本文内容整理自中国大学MOOC “北京大学-人工智能实践:Tensorflow笔记” 课程,部分内容可能在原笔记内容上进行了修改,转载请注明出处。
授课老师:曹健
中国大学MOOC 人工智能实践:Tensorflow笔记课程链接
本讲目标:理解神经网络计算过程,使用基于TF2原生代码搭建第一个神经网络训练模型
本节内容:本小节将借助Tensorflow2.0库中的底层代码搭建一个最基础的神经网络并对代码进行初步优化
本小节将搭建一个神经网络训练模型用于实现鸢尾花分类,该过程基本思路如下:
准备数据:包括数据集读入、数据集乱序、把训练集和测试集中的数据配成输入特征和标签对、生成 train 和 test 即永不相见的训练集和测试集;
鸢尾花数据集共提供了 150 组鸢尾花数据,每组包括鸢尾花的花萼长、花萼宽、花瓣长、花瓣宽 4 个输入特征,同时还给出了这一组特征对应的鸢尾花类别。
类别包括狗尾鸢尾(Setosa Iris)、杂色鸢尾(Versicolour Iris)、弗吉尼亚鸢尾(Virginica Iris)三类, 分别用数字0、1、2 表示。
其中部分数据示例如下:
从sklearn包 datasets 读入数据集,代码如下:
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
变量。
人类在认识这个世界的时候信息是没有规律的、杂乱无章的涌入大脑的,所以喂入神经网络的数据集也需要被打乱顺序。
鸢尾花数据集乱序代码如下:
np.random.seed(116) # 使用相同的 seed,使输入特征/标签一一对应
np.random.shuffle(x_data)
np.random.seed(116)
np.random.shuffle(y_data)
tf.random.set_seed(116)
程序说明:以上代码实现了让数据集乱序,因为使用了同样的随机种子,所以打乱顺序后输入特征和标签仍然是一一对应的。
为了公正评判神经网络的效果,训练集和测试集要求没有交集。
将数据集分割成永不相见的训练集和测试集,代码如下:
x_train = x_data[:-30]
y_train = y_data[:-30]
x_test = x_data[-30:]
y_test = y_data[-30:]
程序说明:以上代码将打乱后的前 120 个数据取出来作为训练集,后 30 个数据作为测试集,从而保证了训练集和测试集没有交集。
使用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)
代码说明:以上代码使用
from_tensor_slices
把训练集的输入特征和标签配对打包,将每 32 组输入特征标签对打包为一个 batch,在喂入神经网络时会以 batch 为单位喂入。
搭建网络:定义神经网络中的所有可训练参数。
代码如下:
w1 = tf.Variable(tf.random.truncated_normal([ 4, 3 ], stddev=0.1))
b1 = tf.Variable(tf.random.truncated_normal([ 3 ], stddev=0.1))
代码说明:以上代码定义了神经网络的所有可训练参数。
由于只用了一层网络,因为输入特征是4个,输出节点数等于分类数,是3分类,故参数 w i w_i wi 为4行3列的张量, b i b_i bi 必须与 w i w_i wi 的维度一致,所以是3。
参数优化:优化这些可训练的参数,利用嵌套循环在 with 结构中求得损失函数 loss对每个可训练参数的偏导数,更改这些可训练参数。
为了查看效果,程序中可以加入每遍历一次数据集显示当前准确率,还可以画出准确率 acc 和损失函数 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))
代码说明:以上代码功能为:在 with 结构中计算前向传播的预测结果 y 、计算损失函数 loss 损失、分别对参数 w 1 w_1 w1 和参数 b 1 b_1 b1 计算偏导数、更新参数 w 1 w_1 w1 和参数 b 1 b_1 b1 的值、打印出这一轮 epoch 后的损失函数值。
因为训练集有 120 组数据,batch 是 32,每个 step 只能喂入 32 组数据,需要 batch 级别循环 4 次,所以 loss 除以 4,求得每次 step 迭代的平均 loss。
测试效果:计算当前参数前向传播后的准确率,显示当前准确率 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)
代码说明:以上代码通过前向传播计算出 y ,使其符合概率分布并找到最大的概率值对应的索引号,调整数据类型与标签一致,如果预测值和标签相等则 correct 变量自加一,准确率 acc 即预测对了的数量除以测试集中的数据总数。
绘制准确率 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()
代码说明:以上代码可将计算出的准确率画成曲线图,并通过设置图标题、设置 x 轴名称、设置 y 轴名称,标出每个 epoch 时的准确率并画出曲线,可用同样方法画出 loss 曲线。
图12 训练过程 loss 曲线 图13 训练过程准确率 acc 曲线
# -*- coding: UTF-8 -*-
# 利用鸢尾花数据集,实现前向传播、反向传播,可视化loss曲线
# 导入所需模块
import tensorflow as tf
from sklearn import datasets
from matplotlib import pyplot as plt
import numpy as np
# 导入数据,分别为输入特征和标签
x_data = datasets.load_iris().data
y_data = datasets.load_iris().target
# 随机打乱数据(因为原始数据是顺序的,顺序不打乱会影响准确率)
# seed: 随机数种子,是一个整数,当设置之后,每次生成的随机数都一样(为方便教学,以保每位同学结果一致)
np.random.seed(116) # 使用相同的seed,保证输入特征和标签一一对应
np.random.shuffle(x_data)
np.random.seed(116)
np.random.shuffle(y_data)
tf.random.set_seed(116)
# 将打乱后的数据集分割为训练集和测试集,训练集为前120行,测试集为后30行
x_train = x_data[:-30]
y_train = y_data[:-30]
x_test = x_data[-30:]
y_test = y_data[-30:]
# 转换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)
# 生成神经网络的参数,4个输入特征故,输入层为4个输入节点;因为3分类,故输出层为3个神经元
# 用tf.Variable()标记参数可训练
# 使用seed使每次生成的随机数相同(方便教学,使大家结果都一致,在现实使用时不写seed)
w1 = tf.Variable(tf.random.truncated_normal([4, 3], stddev=0.1, seed=1))
b1 = tf.Variable(tf.random.truncated_normal([3], stddev=0.1, seed=1))
lr = 0.1 # 学习率为0.1
train_loss_results = [] # 将每轮的loss记录在此列表中,为后续画loss曲线提供数据
test_acc = [] # 将每轮的acc记录在此列表中,为后续画acc曲线提供数据
epoch = 500 # 循环500轮
loss_all = 0 # 每轮分4个step,loss_all记录四个step生成的4个loss的和
# 训练部分
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符合概率分布(此操作后与独热码同量级,可相减求loss)
y_ = tf.one_hot(y_train, depth=3) # 将标签值转换为独热码格式,方便计算loss和accuracy
loss = tf.reduce_mean(tf.square(y_ - y)) # 采用均方误差损失函数mse = mean(sum(y-out)^2)
loss_all += loss.numpy() # 将每个step计算出的loss累加,为后续求loss平均值提供数据,这样计算的loss更准确
# 计算loss对各个参数的梯度
grads = tape.gradient(loss, [w1, b1])
# 实现梯度更新 w1 = w1 - lr * w1_grad b = b - lr * b_grad
w1.assign_sub(lr * grads[0]) # 参数w1自更新
b1.assign_sub(lr * grads[1]) # 参数b自更新
# 每个epoch,打印loss信息
print("Epoch {}, loss: {}".format(epoch, loss_all/4))
train_loss_results.append(loss_all / 4) # 将4个step的loss求平均记录在此变量中
loss_all = 0 # loss_all归零,为记录下一个epoch的loss做准备
# 测试部分
# total_correct为预测对的样本个数, total_number为测试的总样本数,将这两个变量都初始化为0
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)
pred = tf.argmax(y, axis=1) # 返回y中最大值的索引,即预测的分类
# 将pred转换为y_test的数据类型
pred = tf.cast(pred, dtype=y_test.dtype)
# 若分类正确,则correct=1,否则为0,将bool型的结果转换为int型
correct = tf.cast(tf.equal(pred, 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
test_acc.append(acc)
print("Test_acc:", acc)
print("--------------------------")
# 绘制 loss 曲线
plt.title('Loss Function Curve') # 图片标题
plt.xlabel('Epoch') # x轴变量名称
plt.ylabel('Loss') # y轴变量名称
plt.plot(train_loss_results, label="$Loss$") # 逐点画出trian_loss_results值并连线,连线图标是Loss
plt.legend() # 画出曲线图标
plt.show() # 画出图像
# 绘制 Accuracy 曲线
plt.title('Acc Curve') # 图片标题
plt.xlabel('Epoch') # x轴变量名称
plt.ylabel('Acc') # y轴变量名称
plt.plot(test_acc, label="$Accuracy$") # 逐点画出test_acc值并连线,连线图标是Accuracy
plt.legend()
plt.show()
大部分神经网络模型的数据集需要自行采集或下载,程序运行前往往需要首先加载位于本地的数据集。
本文将前述鸢尾花数据集下载后以文本文件(txt)格式保存在了本地,程序执行时将其输入至神经网络进行训练。鸢尾花数据集的 txt 文件包含部分内容如 图14 所示:
图14 鸢尾花数据集 txt 文件内容(部分)
读取本地数据集有两种常见的方法:
(1) 利用 open 函数打开 txt 文件,并处理成神经网络需要的数据结构,该函数格式为open(文件名 , r)
。
(2) 利用 pandas 中函数读取,并处理成神经网络需要的数据结构,该函数格式为
pd.read_csv( 文件名 ,header=第几行作为表头,sep= 分割符号)
。
其中利用 pandas 中函数读取代码如下:
import pandas as pd
import numpy as np
df = pd.read_csv("iris.txt",header = 0,sep=",") #读取本地文件
data = df.values #去掉索引并取值
x_data = [lines[0:4] for lines in data] #取输入特征
x_data = np.array(x_data,float) #转换为numpy格式
y_data = [lines[4] for lines in data] #取标签
for i in range(len(y_data)):
if y_data[i] == setosa:
y_data[i] = 0
elif y_data[i] == versicolor:
y_data[i] = 1
elif y_data[i] == virginica:
y_data[i] = 2
else
raise ValueError()
y_data = np.array(y_data)
代码说明:以上代码通过读取本地文件、取特征输入、取标签并将其转换为规定格式,实现本地数据集的读取。
利用 open 函数打开 txt 文件并读取数据代码如下:
import numpy as np
f = open("iris.txt","r") # 取本地文件
next(f) # 去掉首行
contents = f.readlines() # 按行读取
x_data=[];y_data=[];
for content in contents:
temp = content.split(",") # 按逗号分隔
x_data.append( np.array(temp[0:4],dtype=float) ) # 取输入特征
if temp[4] == "setosa\n": # 判断标签并赋值
y_data.append(0)
elif temp[4] == "versicolor\n":
y_data.append(1)
elif temp[4] == "virginica\n":
y_data.append(2)
else:
raise ValueError()
x_data = np.array(x_data)
y_data = np.array(y_data)
代码说明:以上代码通过读取本地文件、取特征输入、取标签并将其转换为规定格式,实现本地数据集的读取。
本文中的数据集较为简单,前文中利用了简单网络结构进行拟合,仅考虑输入层与输出层构建单层神经网络。参数定义如下:
w1 = tf.Variable(tf.random.truncated_normal[4,3],stddev = 0.1,seed = 1)) b1 = tf.Variable(tf.random.truncated_normal[3],stddev = 0.1,seed = 1))
若将学习率设置为 0.5,训练后将出现 “梯度爆炸
” 情况,即此时神经网络训练结果不能有效收敛,参数反复在最优解附近跳动,该训练过程 loss 曲类似 图15 所示:
分析产生梯度爆炸的原因,考虑到使用梯度下降思想时,其计算公式为
w n e w = w o l d − l r × ∂ L ∂ W w_{new}=w_{old}-lr \times \frac{\partial{L}}{\partial{W}} wnew=wold−lr×∂W∂L其中参数更新量为学习率与损失函数偏导数相乘,若二者乘积过大,则会导致梯度爆炸。 因此,解决梯度爆炸问题可针对学习率进行调整,也可对数据进行调整。
故解决方法可为:
(1) 逐步减小学习率:例如0.1、0.01 等;
(2) 对数据进行预处理后再输入神经网络,减小偏差值的大小,抑制梯度爆炸,即数据归一化与标准化,其主要方法有线性归一化、非线性归一化、Z-Score 标准化。
线性归一化:将数据映射到[0,1]区间中,计算公式如下:
x ∗ = x − min { x } max { x } − min { x } x^{*}=\frac{x-\min \{x\}}{\max \{x\}-\min \{x\}} x∗=max{x}−min{x}x−min{x}
非线性归一化(log 函数转换):使数据映射到[0,1]区间上,计算公式如下:
x ∗ = log 10 x log 10 max { x } x^{*}=\frac{\log _{10}x}{\log _{10}\max \{x\}} x∗=log10max{x}log10x
Z-Score 标准化:使每个特征中的数值平均值变为
x ∗ = x − m e a n { x } s t d { x } x^{*}=\frac{x-mean\{x\}}{std \{x\}} x∗=std{x}x−mean{x}
以线性归一化为例,其代码实现如下:
def normalize(data):
x_data = data.T # 每一列为同一属性,转置到每一行
for i in range(4):
x_data[i] = (x_data[i] - tf.reduce_min(x_data[i])) / (tf.reduce_max(x_data[i]) - tf.reduce_min(x_data[i]))
return x_data.T # 转置回原格式
我们也可以使用 sklearn库preprocessing包中的StandardScaler
类对数据进行常见的Z-Score 标准化操作,其代码实现如下:
from sklearn.preprocessing import StandardScaler
scaler=StandardScaler() #类的实例化
x_train = scaler.fit_transform(x_train) #使用 fit_transform()对训练集先拟合数据,再标准化
x_test = scaler.transform(x_test) #使用 transform()对测试集进行数据标准化
代码说明:(训练集和测试集采用不同函数的原因)
首先,由于我们一般默认训练集与测试集遵循同一分布,所以两者要进行统一的标准化处理。
但在此处,我们使用了不同的与处理函数。
fit_transform() 的作用就是先拟合数据,然后转化它将其转化为标准形式。
tranform() 的作用是通过找中心和缩放等实现标准化。
由于在训练集中调用 fit_transform函数时,其实找到了均值 μ \mu μ 和方差 δ 2 \delta^2 δ2,即我们已经找到了转换规则,我们把这个规则利用在训练集上,同样,我们可以直接将其运用到测试集上(甚至交叉验证集),所以在测试集上的处理,我们只需要标准化数据而不需要再次拟合数据。用一幅图展示如下:
#:本小段参考自 博客园 fit_transform和transform的区别
以下实验对使用Z-Score 标准化进行数据预处理前后神经网络的迭代情况进行了对比。
对本节前述的鸢尾花识别程序进行修改,进行对比实验 (实验一及实验二)。
实验一
:取 eopch=500,lr=0.3,不对鸢尾花数据做任何预处理;
实验二
:取 eopch=500,lr=0.3,使用 StandardScaler类对鸢尾花数据进行Z-Score 标准化处理。
两程序运行后绘制的 loss-acc 曲线分别如图17 、图18 所示。
图17 实验一:未进行数据预处理后的 loss-acc 曲线 图18 实验二:对数据进行Z-Score 标准化预处理后的 loss-acc 曲线
对比实验结果分析:
由于实验一未对数据进行预处理且学习率设置过高,程序运行时出现了一小段的 “梯度爆炸情况”;
而在实验二中,虽然学习率与实验一相同,但由于数据预处理过程对数据变化幅度进行了压缩,减小了偏差值的大小,进而抑制了“梯度爆炸情况”,故程序运行过程中未出现“梯度爆炸情况”。
做完数据标准化,上述网络已经可以跑通,下面对网络进行部分优化,增加**“指数衰减学习率”**的概念。
我们已经知道,在神经网络训练中,学习率是一个非常重要的超参数。
如果学习率设置过大,训练过程就可能无法收敛到最优解,会在最优解两边进行震荡(即所谓“梯度爆炸”现象);
如果学习率设置过小,学习速度就可能非常慢,会大大降低优化速度,需要很多轮的迭代才能达到一个比较理想的优化效果。
一个重要的想法是引入动态的学习率,使得开始训练时学习率较大,程序可以快速得到一个比较优的解;然后随着迭代的次数来逐步减少学习率,让模型在训练后期更加稳定。
指数衰减学习率可在训练初期赋予网络较大学习率,并在训练过程中逐步减小,该方法能有效增加网络收敛速度。
在 tensorflow 中可使用以下函数产生指数衰减的学习率:
tf.keras.optimizers.schedules.ExponentialDecay(lr_starter,decay_step,decay_rate,staircase,name)
其产生的动态学习率decayed_lr满足: d e c a y e d _ l r = l r _ s t a r t e r × d e c a y _ r a t e g l o b a l _ s t e p d e c a y _ s t e p s decayed\_lr= lr\_starter \times decay\_rate ^ {\frac{global\_step}{decay\_steps}} decayed_lr=lr_starter×decay_ratedecay_stepsglobal_step
函数中当staircase
为 True 时,学习率呈现阶梯状递减。
以下实验对引入 “指数衰减学习率” 前后神经网络的迭代情况进行了对比。
对本节前述的鸢尾花识别程序进行修改,进行对比实验 (实验三、实验四、实验五)。
实验三
:取 eopch=500,对鸢尾花数据进行Z-Score 标准化处理,不使用指数衰减学习率,取 lr 为常量 0.1;
实验四
:取 eopch=500,对鸢尾花数据进行Z-Score 标准化处理,使用 “tf. keras. optimizers. schedules. ExponentialDecay” 函数产生指数衰减学习率:
函数参数lr_starter=0.4, global_step=50, decay_step=0.95, staircase=True。
实验五
:取 eopch=500,对鸢尾花数据进行Z-Score 标准化处理,使用 “tf. keras. optimizers. schedules. ExponentialDecay” 函数产生指数衰减学习率:
函数参数lr_starter=0.4, global_step=50, decay_step=0.95, staircase=False。
三个实验程序运行后绘制的 loss-acc-lr 曲线分别如图19 、图20、图21 所示。
图19 实验三:取学习率为常量0.1时的 loss-acc-lr 曲线 图20 实验四:使用指数衰减学习率且取 “staircase=True” 时的 loss-acc-lr 曲线 图21 实验五:使用指数衰减学习率且取 “staircase=False” 时的 loss-acc-lr 曲线
对比实验结果分析:
(1) 对比实验四、实验五可知:参数staircase
决定学习率是否连续变化,即当 staircase=False 时学习率连续变化,当 staircase=True 时学习率呈阶梯状变化。
(2)由于采用了指数衰减学习率,实验四、实验五即使采用了更大的初始化学习率,依然实现了比实验三更快的迭代速度。说明了引入动态的 “指数衰减学习率” 能够加快神经网络的收敛速度。
以下给出经过初步优化后的完整代码(即上述实验五代码),请注意和之前代码进行对比。
# -*- coding: UTF-8 -*-
# 利用鸢尾花数据集,实现前向传播、反向传播,可视化loss曲线
# 导入所需模块
import tensorflow as tf
from sklearn import datasets
from matplotlib import pyplot as plt
from sklearn.preprocessing import StandardScaler
import numpy as np
# 导入数据,分别为输入特征和标签
x_data = datasets.load_iris().data
y_data = datasets.load_iris().target
# 随机打乱数据
np.random.seed(116)
np.random.shuffle(x_data)
np.random.seed(116)
np.random.shuffle(y_data)
# 将打乱后的数据集分割为训练集和测试集
x_train = x_data[:-30]
y_train = y_data[:-30]
x_test = x_data[-30:]
y_test = y_data[-30:]
# 利用StandardScaler类对数据进行 Z-Score 标准化处理
scaler=StandardScaler() #实例化
x_train = scaler.fit_transform(x_train) #使用fit_transform(data)一步达成结果
x_test = scaler.transform(x_test)
# 转换x的数据类型
x_train = tf.cast(x_train, tf.float32)
x_test = tf.cast(x_test, tf.float32)
# from_tensor_slices函数使输入特征和标签值一一对应。
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)
# 生成神经网络的参数,4个输入特征故,输入层为4个输入节点;因为3分类,故输出层为3个神经元
w1 = tf.Variable(tf.random.truncated_normal([4, 3], stddev=0.1, seed=1))
b1 = tf.Variable(tf.random.truncated_normal([3], stddev=0.1, seed=1))
# 使用 “tf. keras. optimizers. schedules. ExponentialDecay” 函数产生指数衰减学习率:\
# 函数参数lr_starter=0.4, global_step=50, decay_step=0.95, staircase=False。
lr_starter = 0.4 # 初始化学习率为0.4
lr = tf.keras.optimizers.schedules.ExponentialDecay(lr_starter,50,0.95, staircase=False)
train_loss_results = []
test_acc = []
test_lr = [] # 将每轮的lr记录在此列表中,为后续画lr曲线提供数据
epoch = 500 # 循环500轮
loss_all = 0
# 训练部分
for epoch in range(epoch): #数据集级别的循环
# 更新参数lr并记录到列表test_lr[]中
lr_ = lr(epoch).numpy()
test_lr.append(lr_)
for step, (x_train, y_train) in enumerate(train_db): #batch级别的循环
with tf.GradientTape() as tape:
y = tf.matmul(x_train, w1) + b1
y = tf.nn.softmax(y)
y_ = tf.one_hot(y_train, depth=3)
loss = tf.reduce_mean(tf.square(y_ - y))
loss_all += loss.numpy()
grads = tape.gradient(loss, [w1, b1])
w1.assign_sub(lr_ * grads[0]) # 参数w1自更新
b1.assign_sub(lr_ * grads[1]) # 参数b自更新
print("Epoch {}, loss: {}".format(epoch, loss_all/4))
print("Test_lr:", lr_)
train_loss_results.append(loss_all / 4)
loss_all = 0
# 测试部分
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)
pred = tf.argmax(y, axis=1)
pred = tf.cast(pred, dtype=y_test.dtype)
correct = tf.cast(tf.equal(pred, y_test), dtype=tf.int32)
correct = tf.reduce_sum(correct)
total_correct += int(correct)
total_number += x_test.shape[0]
acc = total_correct / total_number
test_acc.append(acc)
print("Test_acc:", acc)
print("--------------------------")
# 绘制 loss 曲线
plt.subplot(2, 2, 1)
plt.title('Loss Function Curve')
plt.xlabel('Epoch')
plt.ylabel('Loss')
plt.plot(train_loss_results, label="$Loss$")
plt.legend()
# 绘制 Accuracy 曲线
plt.subplot(2, 2, 2)
plt.title('Acc Curve')
plt.xlabel('Epoch')
plt.ylabel('Acc')
plt.plot(test_acc, label="$Accuracy$")
plt.legend()
# 绘制 Learning Rate 曲线
plt.subplot(2, 1, 2)
plt.title('Learning Rate Curve')
plt.xlabel('Global steps')
plt.ylabel('Learning Rate')
plt.plot(test_lr, label="$lr$")
plt.legend()
plt.show()
上一节介绍了TensorFlow2.1 中的基本概念与常用函数。
人工智能实践:Tensorflow2.0笔记 北京大学MOOC(1-2)
下一节将进入第二讲,对本讲建立的神经网络进行进一步优化。
人工智能实践:Tensorflow2.0笔记 北京大学MOOC(2-1)<未完工待续。。。>