[TensorFlow 2] [MNIST] 前向传播(张量)-学习笔记

介绍

以龙良曲老师的《深度学习与TensorFlow 2入门实战》为教材,结合自己的理解,记录一下我的学习笔记。

代码

运行结果

定义

输出函数:
o u t = r e l u { r e l u { r e l u [ X @ W 1 + b 1 ] @ W 2 + b 2 } @ W 3 + b 3 } out = relu\{relu\{relu[X@W_{1}+b_{1}]@W_{2}+b_{2}\}@W_{3}+b_{3}\} out=relu{relu{relu[X@W1+b1]@W2+b2}@W3+b3}
获取输出函数最大值:
p r e d = a r g m a x ( o u t ) pred = argmax(out) pred=argmax(out)
构建误差函数:
l o s s = M S E ( o u t , l a b e l ) loss = MSE(out,label) loss=MSE(out,label)
最小化minimize误差函数loss,获得更新:
[ W 1 ′ , b 1 ′ , W 2 ′ , b 2 ′ , W 3 ′ , b 3 ′ ] [W^{'}_{1},b^{'}_{1},W^{'}_{2},b^{'}_{2},W^{'}_{3},b^{'}_{3}] [W1,b1,W2,b2,W3,b3]

代码分析

导入TF

import  tensorflow as tf
from    tensorflow import keras
from    tensorflow.keras import datasets

去掉无关信息

import  os
os.environ['TF_CPP_MIN_LOG_LEVEL'] = '2'

加载数据

(x, y), _ = datasets.mnist.load_data()

自动检测有没有mnist数据集,否则直接从Google网站上下载

输入为:
x:60k张28x28像素的图片
y:60k个图片中的数字(从0-9)

切片

train_db = tf.data.Dataset.from_tensor_slices((x,y)).batch(128)

每次获取128张图片,作为一个批次batch

生成迭代器

train_iter = iter(train_db)
sample = next(train_iter)
print('batch:', sample[0].shape, sample[1].shape)

获取第一批次数据存在sample中,sample[0]为中输入的x,sample[1]为输入的y。显示结果:

batch: (128,28,28)(128,)

创建权值

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]))

里面的参数为 [dim_in, dim_out], [dim_out]
b的大小就为dim_out
MNIST像素输入
如图所示,输入层为784个像素点(28*28)

神经网络
如图所示,神经网络中
隐藏层1,256个节点
隐藏层2,128个节点
输出层,10个节点
[batch, 784] => [batch, 256] => [batch, 128] => [batch, 10]

这里用了tf.Variable()
因为之后要用tf.GradientTape()求导数。tf.GradientTape()默认只监控由tf.Variable创建的traiable=True属性(默认)的变量。tf.random()求导得到的是None类型数据。

把方差设置为0.1,,为了避免梯度爆炸。

创建学习率

lr = 1e-3

设置梯度下降中的学习率为0.001

创建循环

for step, (x, y) in enumerate(train_db):

对于每一批次batch
x [batch,28,28] => [batch,784]

    x=tf.reshape(x,[-1,28*28])

维度变换
第0个维度为-1,即不变。

插入梯度求解器

    with tf.GradientTape() as tape: 

h1 = x@w1 + b1

    	h1 = x@w1 + tf.broadcast_to(b1, [x.shape[0], 256])

x 为 [batch, 784]
w1 为 [784, 256]
x@w1 得到 [batch, 256]
b1 为 [256]
所以要把b1 => [batch, 256]
(会自动进行维度转换,可以不用tf.broadcast_to())

添加非线性函数

    	h1 = tf.nn.relu(h1)

h2 = h1@w2 + b2

    	h2 = h1@w2 + b2
    	h2 = tf.nn.relu(h2)

获得输出函数out

    	out = h2@w3 + b3

[batch, 128] => [batch, 10]

y[batch] => y[batch,10]

		y_onehot = tf.one_hot(y, depth=10)

out为 [batch, 10]
y为 [batch]
所以要利用独热编码(one-hot encoding)可以这么理解:
y原本的数据为每张图片中的数字,即[5,2,1,9,4…],第一张图写的是数字5,第二张图写的是数字2…
现在要变成
[ 0. 0. 0. 0. 0. 1. 0. 0. 0. 0. 0 0. 1. 0. 0. 0. 0. 0. 0. 0. 0 1. 0. 0. 0. 0. 0. 0. 0. 0. ⋯ ] \begin{bmatrix} 0. & 0. & 0. & 0. & 0. & 1. & 0. & 0. & 0. & 0. \\ 0 & 0. & 1. & 0. & 0. & 0. & 0. & 0. & 0. & 0. \\ 0 & 1. & 0. & 0. & 0. & 0. & 0. & 0. & 0. & 0. \\ & & & & \cdots & & & & & \end{bmatrix} 0.000.0.1.0.1.0.0.0.0.0.0.0.1.0.0.0.0.0.0.0.0.0.0.0.0.0.0.

第一行第5个索引位置为1
第二行第2个索引位置为1

有数据为1,没有数据为0。

tf.one_hot()函数用法:
tf.one_hot(indices, depth…)

  • indices: 需要编码的索引,这里就是y
  • depth: 编码深度,这里是10,因为数字是0-9

计算平均方差

		loss = tf.square(y_onehot - out)
		loss = tf.reduce_mean(loss)

M S E = m e a n ( ∑ ( y − o u t ) 2 ) MSE = mean (\sum(y-out)^2) MSE=mean((yout)2)
假设out为
[ 0. 0. 0. 0.04 0. 0.94 0.02 0. 0. 0. 0. 0.02 0.82 0.09 0. 0. 0. 0.06 0. 0.01 ⋯ ] \begin{bmatrix} 0. & 0. & 0. & 0.04 & 0. & 0.94 & 0.02 & 0. & 0. & 0. \\ 0. & 0.02 & 0.82 & 0.09 & 0. & 0. & 0. & 0.06 & 0. & 0.01 \\ & & & & \cdots & & & & & \end{bmatrix} 0.0.0.0.020.0.820.040.090.0.0.940.0.020.0.0.060.0.0.0.01

第一张图片,神经网络认为数字为3的可能性为0.04,数字为5的可能性为0.94……
第一张图片,神经网络认为数字为1的可能性为0.02,数字为2的可能性为0.82……
(正确值y - out) ^2 得到 [batch,10]
加到一起,然后计算整个矩阵的平均数(标量)。
也可以计算每一行的平均数得到[batch],对结果没有影响,因为正向放缩不影响梯度方向。

完成梯度计算

	grads = tape.gradient(loss, [w1, b1, w2, b2, w3, b3])

得到梯度 [ W 1 ′ , b 1 ′ , W 2 ′ , b 2 ′ , W 3 ′ , b 3 ′ ] [W^{'}_{1},b^{'}_{1},W^{'}_{2},b^{'}_{2},W^{'}_{3},b^{'}_{3}] [W1,b1,W2,b2,W3,b3]
grads[0]存放的是 W 1 ′ W^{'}_{1} W1
grads[1]存放的是 b 1 ′ b^{'}_{1} b1
……

更新w1权重

	w1.assign_sub(lr * grads[0])

W 1 n e w = W 1 − l r ∗ W 1 ′ W^{new}_{1} = W_{1} - lr * W'_1 W1new=W1lrW1
如果直接用w1 = w1 - lr * grads[0],会报错。
因为w1要始终为tf.Veriable(),才能用tf.GradientTape()求导。
w1 - lr * grads[0]会得到一个tf.Tensor()
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(step, 'loss', float(loss))

loss是个Tensor变量,转换成numpy float
每100个loss,打印一个,运行一下得到结果:

0 loss: 0.33614686293729638
100 loss: 0.20110829137202349
200 loss: 0.18116827382093792
……

loss下降

多次循环
将for循环外再嵌套一个循环:

for epoch in range(10): 

然后下面的都缩进
循环十次

	if step % 100 == 0:
		print(epoch, step, 'loss:', float(loss))

得到结果:

0 0 loss: 0.33114686293729638
0 100 loss: 0.20110829137202349
……
9 400 loss: 0.08436782179239382

扩展阅读

[1] 神经网络的评估和内部状态解析, 论智, 2017.11, (http://m.elecfans.com/article/586515.html)

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值