题目3:请根据附件 2 所提供的部分食物寒热属性(分为三类:性平、性温热、性凉寒),对附 件 1 中的食物进行分类,判断这些食物是属于性平、性温热或性凉寒中哪一类,并说明你分类的合理性;
BP神经网络:外部输入信号经输入层、隐含层的神经元的逐层处理,并向前传播至输出层从而获得结果。如果在输出层无法得到期望输出,则转入误差的反向传播过程,将网络输出实际值之间的误差沿原连接通路原路返回,通过修改各层神经元的连接权重,减小误差,然后转入正向传播过程,经过多次迭代,直至到达最大迭代次数或误差小于给定值为止。

过程:
1、数据预处理。附件 1中 1284种食物,有 549种食物是已知其寒热属性,另外 735种食物的寒热属性未知。提取数据并归一化处理。
2、构建模型。此时将食物各成分看成 BP神经网络模型的输入层,将 Y(寒热属性)当作模型的输出层。这里损失函数用的是均方误差。
3、正交实验设计。要考虑学习率、迭代次数、和神经元个数三个因素。各设置3个水平,采用正交实验的好处是,考虑了 3个因素彼此之间的交互作用,且减少实验的次数。
4、BP神经网络训练。得出的模型效果良好,但受参数影响较大。且训练时间随着问题规模以及神经元个数的增加而增加。
5、未知类别数据分类。利用训练出来的模型进行分类,并将分类结果标注在表中,并输出一个新的excel表。
python源代码:
注意:因变量Y需要映射为数值变量。因为后面的one-hot编码不能对字符串型状态编码,这点不同于哑变量(dummy)的处理。另外数据类型的转换也需要注意,因为这里面调用了一些已有的库,它们输入数据有些是array数组,dataframe表格,tensor张量等。还有就是神经网络训练时需要对已知数据集分成训练集和测试集,选择时尽量随机选择数据,避免训练集和测试集数据差异过大。
import pandas as pd
import numpy as np
from sklearn import preprocessing
import re
import random
import tensorflow as tf
df1 = pd.read_excel(r'食物成分表.xlsx', index_col=0).reset_index(drop=True)
df3 = df1.drop(['可食部分(%)'], axis=1)
scaler = preprocessing.MinMaxScaler() # 标准化
df33 = scaler.fit_transform(df3[df3.columns[1:17]]) # 训练和导出结果
df33 = pd.DataFrame(df33, columns=df3.columns[1:17]) # 数据格式转换
df3 = pd.concat([df3['名 称'], df33], axis=1)
df3['catef'] = '' # 添加分类列
print(df3.head()) # 查看前5条数据
# 根据附件2分类标注数据
def TagCategory(datastr, f, tap):
for line in f:
a = re.split('。|、|;|(|)|\\n| ', line) # 分割字符串保存到a
a = [x.strip() for x in a if x.strip() != ''] # 去除空字符串
for ss in a:
index0 = datastr.str.find(ss) # 找到则返回所要找字符串在指定字符串位置,没有则返回-1
ind = index0[index0.values != -1].index # 不为-1的即表示有
df3['catef'][ind] = tap # 标注
mingcheng = df3['名 称'].astype(str) # 提取名称所在列
f0 = open(r'性平.txt', encoding='utf-8')
f1 = open(r'性凉寒.txt', encoding='utf-8')
f2 = open(r'性温热.txt', encoding='utf-8')
TagCategory(mingcheng, f0, '性平') # 性平标
TagCategory(mingcheng, f1, '性凉寒') # 性凉寒标
TagCategory(mingcheng, f2, '性温热') # 性温热标
print(df3.head()) # 查看前5行
NonNulldf = df3[(df3['catef'].notnull()) & (df3['catef'] != "")] # 找到所有已知类数据
IsNulldf = df3[(df3['catef'] == "")] # 找到未知类数据
kongindex = df3[(df3['catef'] == "")].index # 找到未知类数据的索引
print(NonNulldf.head())
# 取数据方法:
data_train = NonNulldf
# 在初始数据划分训练集与测试集时直接将数据打乱:
data_train['catef']=data_train.catef.map({'性平':0,'性凉寒':1,'性温热':2})#分类列映射
DataIndex = [i for i in range(len(NonNulldf))]#产生索引
random.shuffle(DataIndex)#打算排序
data_train.index = [DataIndex]#新的排序
x_train = data_train.iloc[0:round(0.95 * len(NonNulldf)), 1:17] # 取16个特征变量
y_train = data_train.iloc[0:round(0.95 * len(NonNulldf)), 17] # 后三列是因变量标签
x_test = data_train.iloc[round(0.95 * len(NonNulldf)):, 1:17] # 取16个特征变量
y_test = data_train.iloc[round(0.95 * len(NonNulldf)):, 17] #
# 转换x的数据类型,否则后面矩阵相乘时会因数据类型不一致报错
x_train = tf.cast(x_train, tf.float32)
x_test = tf.cast(x_test, tf.float32)
# from_tensor_slices函数使输入特征和标签值一一对应。(把数据集分批次,每个批次batch组数据)
train_db = tf.data.Dataset.from_tensor_slices((x_train, y_train)).batch(32)
test_db = tf.data.Dataset.from_tensor_slices((x_test, y_test)).batch(32)
# 生成神经网络的参数,16个输入特征,输入层为16个输入节点;因为3分类,故输出层为3个神经元
# 用tf.Variable()标记参数可训练
w1 = tf.Variable(tf.random.truncated_normal([16, 3], stddev=0.1)) # 16行三列,方差为0.1
b1 = tf.Variable(tf.random.truncated_normal([3], stddev=0.1)) # 一行三列,方差为0.1
a = 0.4 # 学习率为0.4
epoch = 1000 # 循环1000轮
# 训练部分
for epoch in range(epoch): # 数据集级别的循环,每个epoch循环一次数据集
for step, (x_train, y_train) in enumerate(train_db): # batch级别的循环 ,每个step循环一个batch
with tf.GradientTape() as tape: # with结构记录梯度信息
y = tf.matmul(x_train, w1) + b1 # 神经网络乘加运算
y = tf.nn.softmax(y) # 使输出y符合概率分布
y_ = tf.one_hot(y_train, depth=3) # 将标签值转换为独热码格式,方便计算loss
loss = tf.reduce_mean(tf.square(y_ - y)) # 采用均方误差损失函数mse = mean(sum(y-y*)^2)
# 计算loss对w, b的梯度
grads = tape.gradient(loss, [w1, b1])
# 实现梯度更新 w1 = w1 - lr * w1_grad b = b - lr * b_grad
w1.assign_sub(a * grads[0]) # 参数w1自更新
b1.assign_sub(a * grads[1]) # 参数b自更新
# 每1000次迭代,输出一次损失函数
if epoch % 10 == 0:
print('迭代第%i次,损失函数为:%f' % (epoch, loss))
# 测试部分
total_correct, total_number = 0, 0
for x_test, y_test in test_db:
# 前向传播求概率
y = tf.matmul(x_test, w1) + b1
y = tf.nn.softmax(y)
predict = tf.argmax(y, axis=1) # 返回y中最大值的索引,即预测的分类
# 将predict转换为y_test的数据类型
predict = tf.cast(predict, dtype=y_test.dtype)
# 若分类正确,则correct=1,否则为0,将bool型的结果转换为int型
correct = tf.cast(tf.equal(predict, y_test), dtype=tf.int32)
# 将每个batch的correct数加起来
correct = tf.reduce_sum(correct)
# 将所有batch中的correct数加起来
total_correct += int(correct)
# total_number为测试的总样本数,也就是x_test的行数,shape[0]返回变量的行数
total_number += x_test.shape[0]
# 总的准确率等于total_correct/total_number
acc = total_correct / total_number
print("测试准确率 = %.2f %%" % (acc * 100.0))
data_pre = IsNulldf
x_pre = data_pre.iloc[0:, 1:17] # 取16个特征变量
x_pre = tf.convert_to_tensor(x_pre)#转换为张量
x_pre = tf.cast(x_pre, tf.float32)#转换数据类型
y = tf.matmul(x_pre, w1) + b1#y值
y = tf.nn.softmax(y)
category = {0: "性平", 1: "性凉寒", 2: "性温热"}#映射
predict = np.array(tf.argmax(y, axis=1)) # 返回y中最大值的索引,即预测的分类[0]
for i in range(len(predict)):#未知数据预测
print("食物属性为:", category.get(predict[i]))
#数据标注并写入excel表中
_1index=[]
_2index=[]
_3index = []
for i in range(len(predict)):
if predict[i]== 1:
_1index.append(i)#找到预测为性凉寒
elif predict[i]== 2:
_2index.append(i)#找到温热
else:
_3index.append(i)#找到性平
#在原始数据中标注好分类
df3['catef'][kongindex[_1index]] = '性凉寒'
df3['catef'][kongindex[_2index]] = '性温热'
df3['catef'][kongindex[_3index]] = '性平'
print(len(_1index),len(_2index),len(_3index))#看预测各类各有多少个
#将预测分类写入表格
df3_w=pd.concat([df1[df1.columns[0]],df1[df1.columns[2:17]], df3['catef']], axis=1)
writer1 = pd.ExcelWriter('df3.xlsx') # 创建excel表格
df3_w.to_excel(writer1, 'page_1')
writer1.save()#保存