代码
import os
os.environ['TF_CPP_MIN_LOG_LEVEL'] = '2' #清除多余信息 这句话放在import TensorFlow之前才有用
import tensorflow as tf
from tensorflow import keras #tensorflow.keras是TensorFlow一个深度集成的包
from tensorflow.keras import datasets #datasets是keras提供的一个可以直接管理一个数据集的一个工具
# 加载一个mnist的数据集,自动查看你有没有缓存一个mnist数据集,如果没有它就会自动从Google Colab上下载
# 遇到了TimeoutError: [WinError 10060] 由于连接方在一段时间后没有正确答复或连接的主机没有反应,连接尝试失败问题后,将翻墙软件设置为全局模式后就可以下载了
# x:[60000,28,28]
# y:[60000]。
(x,y), _ =datasets.mnist.load_data()
# 拿到数据之后先把它转化为tensor
# 一般来说,我们把input的数据放在-1到1,或者0到1的一个范围,比较适合深度学习进行优化。于是,x的范围从0到255变为0到1.
# y后面会做一个onehot
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)
# 看一下x,y的最小最大值,结果如下
# tf.Tensor(0.0, shape=(), dtype=float32) tf.Tensor(255.0, shape=(), dtype=float32) x最小值为0,最大值为255
# tf.Tensor(0, shape=(), dtype=int32) tf.Tensor(9, shape=(), dtype=int32) y最小值为0,最大值为9
print(tf.reduce_min(x), tf.reduce_max(x))
print(tf.reduce_min(y), tf.reduce_max(y))
# 创建一个数据集,创建数据集的好处在于一次取一个batch,这里bacth取128,就是说一次取128个数值
train_db = tf.data.Dataset.from_tensor_slices((x, y)).batch(128)
# 创建迭代器,迭代器可以对train做iter
train_iter = iter(train_db)
sample = next(train_iter)
print('batch:', sample[0].shape, sample[1].shape)
# batch: (128, 28, 28) (128,)
# 创建权值,权值初始化为一个随机的被修剪过的正态分布,bias一般全部初始化为0
# 降维过程:[b,784] => [b,256] => [b,128] => [b,10] 中间过程的256和128是自己定的,输出是根据你有多少类定的,输入是根据输入的x的大小定的
# w要满足矩阵运算的规则,w的shape为[dim_in,dim_out],b的shape为[dim_out]
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]))
# 特殊表示法,1e-3表示10的-3次方
lr = 1e-3
# 对整个数据集迭代10次
for epoch in range(10):
# 每进行一个batch叫做一个step,对每个batch迭代
for step, (x, y) in enumerate(train_db):
# x:[128, 28, 28]
# y: [128]
# 需要维度变换 [b,28,28] => [b,28*28]
x = tf.reshape(x, [-1, 28*28])
# 需要使用TensorFlow自动求导的一个过程,将需要求梯度(前项计算)的过程包在GradientTape
# 自动跟踪类型为tf.Variable的变量
with tf.GradientTape() as tape:
# x:[b,28*28]
# 前向传播:h1 = x@w1 + b1 [b,784]@[784,256] + [256] => [b,256] + [256],+号可以直接做broadcast
h1 = x@w1 + b1
# 非线性
h1 = tf.nn.relu(h1)
# [b,256] => [b,128]
h2 = h1@w2 +b2
h2 = tf.nn.relu(h2)
# [b,128]
# 最后一次一般不加激活函数层
out = h2@w3 +b3
# 计算误差
# out:[b,10]
# y: [b]
# y: [b] =>[b,10]
y_onehot = tf.one_hot(y,depth=10)
# 均方差计算 mse = mean(sum(y-out)^2)
# [b,10]
loss = tf.square(y_onehot - out)
# mean之后得到scalar
loss = tf.reduce_mean(loss)
#计算梯度
grads = tape.gradient(loss,[w1,b1,w2,b2,w3,b3])
# w1 = w1 -lr*w1_grad,相减之后w1的类型由tf.Variable变为tf.tensor
'''
w1 = w1 - lr * grads[0]
b1 = b1 - lr * grads[1]
w2 = w2 - lr * grads[2]
b2 = b2 - lr * grads[3]
w3 = w3 - lr * grads[4]
b3 = b3 - lr * grads[5]
这么写只能算一遍
'''
# 原地更新
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])
if step %100 == 0:
print(epoch, step, 'loss:', float(loss))
'''
100 loss: nan
200 loss: nan
300 loss: nan
400 loss: nan
nan:not a number
出现这句话说明梯度爆炸
(只迭代一次)把w的方差设置为0.1后:
100 loss: 0.20223207771778107
200 loss: 0.18103241920471191
300 loss: 0.14289739727973938
400 loss: 0.1504320204257965
迭代10次后:
9 100 loss: 0.08612565696239471
9 200 loss: 0.09116230905056
9 300 loss: 0.08154936879873276
9 400 loss: 0.09231320768594742
'''