Tensorflow2.0笔记
本博客为Tensorflow2.0学习笔记,感谢北京大学微电子学院曹建老师
4 程序实现鸢尾花数据集分类
4.1 数据集回顾
先回顾鸢尾花数据集,其提供了 150 组鸢尾花数据,每组包括鸢尾花的花萼长、花萼宽、花瓣长、花瓣宽 4 个输入特征,同时还给出了这一组特征对应的鸢尾花类别。类别包括狗尾鸢尾、杂色鸢尾、弗吉尼亚鸢尾三类, 分别用数字0、1、2 表示。使用此数据集代码如下:
from sklearn.datasets import load_iris
x_data = datasets.load_iris().data # 返回 iris 数据集所有输入特征
y_data = datasets.load_iris().target # 返回 iris 数据集所有标签
即从 sklearn 包中导出数据集,将输入特征赋值给 x_data 变量,将对应标签赋值给 y_data 变量。
4.2 程序实现
我们用神经网络实现鸢尾花分类仅需要三步:
(1) 准备数据,包括数据集读入、数据集乱序,把训练集和测试集中的数据配成输入特征和标签对,生成 train 和 test 即永不相见的训练集和测试集;
(2) 搭建网络,定义神经网络中的所有可训练参数;
(3) 优化这些可训练的参数,利用嵌套循环在 with 结构中求得损失函数 loss 对每个可训练参数的偏导数,更改这些可训练参数,为了查看效果,程序中可以加入每遍历一次数据集显示当前准确率,还可以画出准确率 acc 和损失函数 loss 的变化曲线图。
4.3 程序
以上部分的完整代码与解析如下:
(1) 数据集读入:
from sklearn.datasets import datasets
x_data = datasets.load_iris().data # 返回 iris 数据集所有输入特征
y_data = datasets.load_iris().target # 返回 iris 数据集所有标签
(2) 数据集乱序:
np.random.seed(116) # 使用相同的 seed,使输入特征/标签一一对应
np.random.shuffle(x_data)
np.random.seed(116)
np.random.shuffle(y_data) tf.random.set_seed(116)
(3) 数据集分割成永不相见的训练集和测试集:
x_train = x_data[:-30]
y_train = y_data[:-30]
x_test = x_data[-30:]
y_test = y_data[-30:]
(4) 配成[输入特征,标签]对,每次喂入一小撮(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)
上述四小部分代码实现了数据集读入、数据集乱序、将数据集分割成永不相见的训练集和测试集、将数据配成[输入特征,标签]对。人类在认识这个世界的时候信息是没有规律的,杂乱无章的涌入大脑的,所以喂入神经网络的数据集也需要被打乱顺序。(2)部分实现了让数据集乱序,因为使用了同样的随机种子,所以打乱顺序后输入特征和标签仍然是一一对应的。(3)部分将打乱后的前 120 个数据取出来作为训练集,后 30 个数据作为测试集,为了公正评判神经网络的效果, 训练集和测试集没有交集。(4)部分使用 from_tensor_slices 把训练集的输入特征和标签配对打包,将每 32 组输入特征标签对打包为一个 batch,在喂入神经网络时会以 batch 为单位喂入。
(5) 定义神经网路中所有可训练参数:
w1 = tf.Variable(tf.random.truncated_normal([ 4, 3 ], stddev=0.1, seed=1))
pyb1 = tf.Variable(tf.random.truncated_normal([ 3 ], stddev=0.1, seed=1))
(6) 嵌套循环迭代,with 结构更新参数,显示当前 loss:
for epoch in range(epoch): #数据集级别迭代
for step, (x_train, y_train) in enumerate(train_db): #batch 级别迭代
with tf.GradientTape() as tape: # 记录梯度信息
(前向传播过程计算 y)
(计算总 loss)
grads = tape.gradient(loss, [ w1, b1 ])
w1.assign_sub(lr * grads[0]) #参数自更新
b1.assign_sub(lr * grads[1])
print("Epoch {}, loss: {}".format(epoch, loss_all/4))
上述两部分完成了定义神经网路中所有可训练参数、嵌套循环迭代更新参数。(5) 部分定义了神经网络的所有可训练参数。只用了一层网络,因为输入特征是 4个,输出节点数等于分类数,是 3 分类,故参数w1 为 4 行 3 列的张量,b1 必须与w1 的维度一致,所以是 3。(6)部分用两层 for 循环进行更新参数:第一层 for 循环是针对整个数据集进行循环,故用 epoch 表示;第二层 for 循环是针对 batch 的,用 step 表示。在 with 结构中计算前向传播的预测结果 y ,计算损失函数 loss失函数 loss,分别对参数 w1 和参数b1 计算偏导数,更新参数 w1 和参数b1 的值,打印出这一轮 epoch 后的损失函数值。因为训练集有 120 组数据,batch 是 32, 每个 step 只能喂入 32 组数据,需要 batch 级别循环 4 次,所以 loss 除以 4,求得每次 step 迭代的平均 loss。
(7) 计算当前参数前向传播后的准确率,显示当前准确率 acc:
for x_test, y_test in test_db:
y = tf.matmul(h, w) + b # y 为预测结果
y = tf.nn.softmax(y) # y 符合概率分布
pred = tf.argmax(y, axis=1) # 返回 y 中最大值的索引即预测的分类
pred = tf.cast(pred, dtype=y_test.dtype) # 调整数据类型与标签一致
correct = tf.cast(tf.equal(pred, y_test), dtype=tf.int32)
correct = tf.reduce_sum (correct) # 将每个 batch 的 correct 数加起来
total_correct += int (correct) # 将所有 batch 中的 correct 数加起来
total_number += x_test.shape [0]
acc = total_correct / total_number
print("test_acc:", acc)
(8) acc / loss 可视化:
plt.title('Acc Curve') # 图片标题
plt.xlabel('Epoch') # x 轴名称
plt.ylabel('Acc') # y 轴名称
plt.plot(test_acc, label="$Accuracy$") # 逐点画出
test_acc 值并连线
plt.legend()
plt.show()
上述两部分完成了对准确率的计算并可视化准确率与 loss。(7)部分前向传播计算出 y ,使其符合概率分布并找到最大的概率值对应的索引号,调整数据类型与标 签一致,如果预测值和标签相等则 correct 变量自加一,准确率即预测对了的数量除以测试集中的数据总数。
(9)部分可将计算出的准确率画成曲线图,通过设置图标题、设置 x 轴名称、设置 y 轴名称,标出每个 epoch 时的准确率并画出曲线,可用同样方法画出 loss 曲线。结果图如图 4.1 与 4.2。
图 4.1 训练过程 loss 曲线
图4.2 训练过程准确率曲线