“”“前向传播实战:分类问题手写训练参数,损失函数为MSE”""
"""前向传播实战:分类问题手写训练参数,损失函数为MSE"""
import matplotlib
from keras import metrics
from matplotlib import pyplot as plt
import tensorflow as tf
# from tensorflow import keras
from tensorflow.keras import datasets
import os
# 图的默认参数
matplotlib.rcParams['font.size'] = 20 # 设置字体大小
matplotlib.rcParams['figure.titlesize'] = 20 # s设置图片标题大小
matplotlib.rcParams['figure.figsize'] = [9, 7] # 设置图片大小
matplotlib.rcParams['font.family'] = ['STKaiTi'] # 设置字体样式
matplotlib.rcParams['axes.unicode_minus'] = False # 字符显示
# 调整输出信息,只输出error
os.environ['TF_CFF_MIN_LOG_LEVEL'] = '2'
# 设置log输出信息的,也就是程序运行时系统打印的信息。
# x:[60k, 28,28]
# y:[60k]
(x, y), _ = datasets.mnist.load_data()
# x[0-255]-1
x = tf.convert_to_tensor(x, dtype=tf.float32)/255
y = tf.convert_to_tensor(y, dtype=tf.int32)
print(x.shape, y.shape, x.dtype, y.dtype)
print(tf.reduce_min(x), tf.reduce_max(x))
print(tf.reduce_min(y), tf.reduce_max(y))
# 设置一批数据为128
train_db = tf.data.Dataset.from_tensor_slices((x, y)).batch(128)
# 把给定的元组、列表和张量等数据进行特征切片。切片的范围是从最外层维度开始的。
# <BatchDataset shapes: ((None, 28, 28), (None,)), types: (tf.float32, tf.int32)>
train_iter = iter(train_db)
print(train_iter)
sample = next(train_iter)
# 我们首先要知道什么是可迭代的对象(可以用for循环的对象)Iterable:
# 一类:list,tuple,dict,set,str
# # 二类:generator,包含生成器和带yield的generatoe function
# 而生成器不但可以作用于for,还可以被next()函数不断调用并返回下一个值,可以被next()函数不断返回下一个值的对象称为迭代器:Iterator
# 生成器都是Iterator对象,但list,dict,str是Iterable,但不是Iterator,要把list,dict,str等Iterable转换为Iterator可以使用iter()函数
print('batch:', sample[0].shape, sample[1].shape)
# 定义参数和学习率
# 第一层的参数
w1 = tf.Variable(tf.random.truncated_normal([784, 256], stddev=0.1))
b1 = tf.Variable(tf.zeros(256))
# 第二层参数
w2 = tf.Variable(tf.random.truncated_normal([256, 128], stddev=0.1))
b2 = tf.Variable(tf.zeros(128))
# 第三层参数
w3 = tf.Variable(tf.random.truncated_normal([128, 10], stddev=0.1))
b3 = tf.Variable(tf.zeros(10))
lr = (1e-1)*8.5
losses = []
acces = []
acc_meter = metrics.SparseCategoricalAccuracy()
for epoch in range(10):
# 分批循环数据集
for step, (x, y) in enumerate(train_db):
# 把X转换为[batch,784]
x = tf.reshape(x, [-1, 28*28])
# tensor提供的自动求导
with tf.GradientTape() as tape:
# x[128,784]->[128,256]
h1 = x@w1+tf.broadcast_to(b1, [x.shape[0], 256])
# 非线性激励
h1 = tf.nn.relu(h1)
# x[128,256]->[128,128]
h2 = h1@w2+b2
h2 = tf.nn.relu(h2)
# 输出[128,10]
out = h2@w3+b3
# 对y进行one_hot转变为[128,10]
y_onehot = tf.one_hot(y, depth=10)
# 损失函数:(y-y')**2/128
loss = tf.square(y_onehot-out)
loss = tf.reduce_mean(loss)
# 传入损失函数,参数
grads = tape.gradient(loss, [w1, b1, w2, b2, w3, b3])
# 更新参数
w1.assign_sub(lr*grads[0])
b1.assign_sub(lr*grads[1])
w2.assign_sub(lr*grads[2])
b2.assign_sub(lr*grads[3])
w3.assign_sub(lr*grads[4])
b3.assign_sub(lr*grads[5])
acc_meter.update_state(y, out)
# 输出损失值
if step % 100 == 0:
print(epoch, step, 'loss', float(loss), 'acc', acc_meter.result().numpy())
losses.append(float(loss))
acces.append(acc_meter.result().numpy())
print(losses)
print(acces)
plt.figure()
plt.plot(losses, color='C0', marker='s', label='训练')
plt.plot(acces, color='green', marker='s', label='训练')
plt.xlabel('epoch')
plt.ylabel('MSE,ACC')
plt.show()