深度学习(11)TensorFlow基础操作七: 向前传播(张量)实战
深度学习(11)TensorFlow基础操作七: 向前传播(张量)实战
- 1. 导包
- 2. 加载数据集
- 3. 转换数据类型
- 4. 查看x.shape, y.shape, x.dtype, y.dtype
- 5. 查看x和y的范围
- 6. 将一些无关信息屏蔽掉
- 7. 将x中的数值设置在[0, 1]之间
- 8. 创建数据集
- 9. 创建迭代器
- 10. 创建权值
- 11. 前向运算
- 12. 添加激活函数
- 13. 计算误差
- 14. 设置自动求导功能
- 15. 求解梯度
- 16. 设置学习率
- 17. 向前传播
- 18. 打印步数
- 19. 每100步打印步数和loss
- 20. 运行程序
- 21. 再次运行
- 附录: 完整代码
What we have learned
- create tensor
- indexing and slices
- reshape and broadcasting
- math operations
Recap - out=relu{relu{relu[X@W1+b1]@W2+b2}@W3+b3}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}
- pred=argmax(out)pred=argmax(out)pred=argmax(out)
- loss=MSE(out,label)loss=MSE(out,label)loss=MSE(out,label)
- minimizelossminimize lossminimizeloss
- [W1′,b1′,W2′,b2′,W3′,b3′][W_1',b_1',W_2',b_2',W_3',b_3'][W1′,b1′,W2′,b2′,W3′,b3′]
1. 导包
import tensorflow as tf
from tensorflow import keras
from tensorflow.keras import datasets
2. 加载数据集
其中x.shape=[60k, 28, 28]; y.shap=[60k];
# x: [60k, 28, 28],
# y: [60k]
(x, y), _ = datasets.mnist.load_data()
3. 转换数据类型
将x的数据类型转换为Tensor,一般常用tf.float32;
将y的数据类型转换为Tensor,y中数据为整型,所以使用tf.int32;
# 转换数据类型
x = tf.convert_to_tensor(x, dtype=tf.float32)
y = tf.convert_to_tensor(y, dtype=tf.int32)
4. 查看x.shape, y.shape, x.dtype, y.dtype
# 查看x.shape, y.shape, x.dtype, y.dtype
print(x.shape, y.shape, x.dtype, y.dtype)
结果如下:
5. 查看x和y的范围
# 查看x和y的范围,即x和y的最大值和最小值
print(tf.reduce_min(x), tf.reduce_max(x))
print(tf.reduce_min(y), tf.reduce_max(y))
6. 将一些无关信息屏蔽掉
例如:
import osos.environ['TF_CPP_MIN_LOG_LEVEL'] = '2'
[‘TF_CPP_MIN_LOG_LEVEL’] = ‘0’
就是打印全部信息,如果[‘TF_CPP_MIN_LOG_LEVEL’] = ‘2’
就是屏蔽这些信息;
7. 将x中的数值设置在[0, 1]之间
将x除以255.即可,这样x的范围就被设置在[0, 1]之间;
# x: [0~255] => [0~1.]
x = tf.convert_to_tensor(x, dtype=tf.float32) / 255.
y = tf.convert_to_tensor(y, dtype=tf.int32)
y在后面的代码中会做one-hot处理;
8. 创建数据集
创建数据集的好处在于可以每次取一个batch,而原数据集每次只能取一个数据;
# 创建数据集
train_db = tf.data.Dataset.from_tensor_slices((x, y)).batch(128)
9. 创建迭代器
# 创建迭代器
train_iter = iter(train_db)
sample = next(train_iter)
一个sample就代表了迭代一次,也就是一个batch的数据;
查看batch及sample[n]
# 查看sample[n].shape
print('batch:', sample[0].shape, sample[1].shape)
结果如下:
sample[0]
代表x的数据,一个batch里有128张28×28的照片,所以sample[0].shape=[128, 28, 28];
sample[1]
代表y的数据,一个batch里有128个整型数据,这些数据代表了每张图片的标签,也就是[0~9],所以sample[1].shape=[128,]
10. 创建权值
创建w1和b1并初始化
w1一般按照裁剪过的正态分布来初始化; b1一般初始化为0;
初始化的过程要注意维度设置,w: [dim_in, dim_out], b: [dim_out];
# 创建权值
# [b, 784] => [b, 256] => [b, 128] => [b, 10]
# w: [dim_in, dim_out], b: [dim_out]
w1 = tf.random.truncated_normal([784, 256])
b1 = tf.zeros([256])
w2, b2, w3, b3同理;
w2 = tf.random.truncated_normal([256, 128])
b2 = tf.zeros([128])
w3 = tf.random.truncated_normal([128, 10])
b3 = tf.zeros([10])
11. 前向运算
for(x, y) in train_db:# x: [128, 28, 28]# y: [128]# 我们需要一个维度变换的操作才能将x.shape由[b, 28, 28]转换为[b, 28*28]x = tf.reshape(x, [-1, 28*28])# x: [b, 28*28]# h1 = x@w1 + b1# [b, 784]@[784, 256] + [256] => [b, 256] + [256] => [b, 256] + [b, 256]# b会自动做broadcasting操作,当然如过想要手动也可以按照如下操作:# h1 = x@w1 + tf.broadcast_to(b1, [x.shape[0], 256])h1 = x@w1 + b1# [b, 256] => [b, 128]h2 = h1@w2 + b2# [b, 128] => [b, 10]out = h2@w3 + b3
注: 这里的x就是刚才设置的一个batch的数据;
12. 添加激活函数
使其非线性化;
h1 = x@w1 + b1
h1 = tf.nn.relu(h1)
# [b, 256] => [b, 128]
h2 = h1@w2 + b2
h2 = tf.nn.relu(h2)
# [b, 128] => [b, 10]
out = h2@w3 + b3
13. 计算误差
(1) 将y进行one-hot编码
# 将y进行one-hot编码
y_onehot = tf.one_hot(y, depth=10)
depth=10
代表一共有10类,即[0~9];
注: 将y进行one-hot编码这一步在数据创建时或者在这里都行,只要在计算误差之前操作就行;
(2) 计算均方差
# mse = mean(sum(y-out)^2)
# [b, 10]
loss = tf.square(y_onehot - out)
(3) 计算误差均值
得到一个Scalar(标量)
# mean: scalar
loss = tf.reduce_mean(loss)
14. 设置自动求导功能
将所有误差计算的部分全部放入自动求导的功能内:
with tf.GradientTape() as tape:# x: [b, 28*28]# h1 = x@w1 + b1# [b, 784]@[784, 256] + [256] => [b, 256] + [256] => [b, 256] + [b, 256]# b会自动做broadcasting操作,当然如过想要手动也可以按照如下操作:# h1 = x@w1 + tf.broadcast_to(b1, [x.shape[0], 256])h1 = x@w1 + b1h1 = tf.nn.relu(h1)# [b, 256] => [b, 128]h2 = h1@w2 + b2h2 = tf.nn.relu(h2)# [b, 128] => [b, 10]out = h2@w3 + b3# compute loss# out: [b, 10]# y: [b]# 将y进行one-hot编码y_onehot = tf.one_hot(y, depth=10)# mse = mean(sum(y-out)^2)# [b, 10]loss = tf.square(y_onehot - out)# mean: scalarloss = tf.reduce_mean(loss)
15. 求解梯度
# compute gradients
grads = tape.gradient(loss, [w1, b1, w2, b2, w3, b3])
16. 设置学习率
即leaning rate,lr
lr = 1e-3
1e-3
表示10^(-3),即0.001;
17. 向前传播
w′=w−lr∗gradw'=w-lr*gradw′=w−lr∗grad
# w1 = w1 - lr * w1_grads
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]
这里grads
是一个列表,所以grads[0]=w1, grads[1]=b1, …;
18. 打印步数
由:
for(x, y) in train_db:
变为:
for step, (x, y) in enumerate(train_db):
这样每一步都会打印当前步数;
19. 每100步打印步数和loss
if step % 100 == 0:print(step, 'loss:', float(loss))
20. 运行程序
发现如下报错:
打印grads:
print(grads)
发现全部为None类型:
原因: tf.GradientTape()
默认只会跟踪类型为tf.Variable的数据,但是我们在创建权重的时候,w1, b1, w2, b2, w3, b3为Tensor类型的数据,所以我们需要使用tf.Variable()
函数将这些权重进行封装:
# 创建权值
# [b, 784] => [b, 256] => [b, 128] => [b, 10]
# w: [dim_in, dim_out], b: [dim_out]
w1 = tf.Variable(tf.random.truncated_normal([784, 256]))
b1 = tf.Variable(tf.zeros([256]))
w2 = tf.Variable(tf.random.truncated_normal([256, 128]))
b2 = tf.Variable(tf.zeros([128]))
w3 = tf.Variable(tf.random.truncated_normal([128, 10]))
b3 = tf.Variable(tf.zeros([10]))
这样,tf.Variable()
会自动跟踪梯度信息;
再次运行,还是出现同样的错误。
原因: w1 = w1 - lr * grads[0]
中后面的w1是刚才已经改好的Variable类型的数据,而前边的w1却还是Tensor类型的数据,前后两个w1是两个不同的对象,这样在下一次计算的时候就会出错。
解决办法: 使用原地更新的函数assign_sub()
:
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])
其中assign_sub就是w - lrgrads[0]; assign_add就是w + lrgrads[0];
这样就会使得数据类型保持不变;
验证: 打印b3的数据类型
print(isinstance(b3, tf.Variable))
print(isinstance(b3, tf.Tensor))
运行结果如下:
这说明原地更新的方法成功了,b3以及其它权重已经是Variable类型了。
21. 再次运行
运行结果如下:
可以看到,100轮迭代过后loss值为nan,这是梯度爆炸现象。
解决: 在初始化权重的时候,需要初始化在一个较好的范围内。原来的truncated_normal()
满足均值为0,方差为1的正态分布,我们需要将其方差变小,在truncated_normal()
里设置参数stddev=0.1:
# 创建权值
# [b, 784] => [b, 256] => [b, 128] => [b, 10]
# w: [dim_in, dim_out], b: [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]))
运行程序,结果如下:
可以看到,梯度爆炸问题得到了非常明显的改善。所以说,在深度学习中,每一个参数对于整个结果影响是非常大的。
从运行结果可以看出,loss在不断下降,完成了对60k张图片的一次完整的迭代,如果想要多迭代几次,可以再添加一个epoch循环:
for epoch in range(10): # iterate db for 10for step, (x, y) in enumerate(train_db):
…
if step % 100 == 0:print(epoch, step, 'loss:', float(loss))
运行结果如下:
从这两次运行结果可以看出,多轮epoch迭代要比单次迭代的效果好很多。
参考文献:
[1] 龙良曲:《深度学习与TensorFlow2入门实战》
附录: 完整代码
import tensorflow as tf
from tensorflow import keras
from tensorflow.keras import datasets
import osos.environ['TF_CPP_MIN_LOG_LEVEL'] = '2'# x: [60k, 28, 28],
# y: [60k]
(x, y), _ = datasets.mnist.load_data()
# 转换数据类型
# x: [0~255] => [0~1.]
x = tf.convert_to_tensor(x, dtype=tf.float32) / 255.
y = tf.convert_to_tensor(y, dtype=tf.int32)
# 查看x.shape, y.shape, x.dtype, y.dtype
print(x.shape, y.shape, x.dtype, y.dtype)
# 查看x和y的范围,即x和y的最大值和最小值
print(tf.reduce_min(x), tf.reduce_max(x))
print(tf.reduce_min(y), tf.reduce_max(y))
# 创建数据集
train_db = tf.data.Dataset.from_tensor_slices((x, y)).batch(128)
# 创建迭代器
train_iter = iter(train_db)
sample = next(train_iter)
# 查看sample[n].shape
print('batch:', sample[0].shape, sample[1].shape)# 创建权值
# [b, 784] => [b, 256] => [b, 128] => [b, 10]
# w: [dim_in, dim_out], b: [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]))lr = 1e-3for epoch in range(10): # iterate db for 10for step, (x, y) in enumerate(train_db): # for every batch# x: [128, 28, 28]# y: [128]# 我们需要一个维度变换的操作才能将x.shape由[b, 28, 28]转换为[b, 28*28]x = tf.reshape(x, [-1, 28*28])with tf.GradientTape() as tape: # tf.Variable# x: [b, 28*28]# h1 = x@w1 + b1# [b, 784]@[784, 256] + [256] => [b, 256] + [256] => [b, 256] + [b, 256]# b会自动做broadcasting操作,当然如过想要手动也可以按照如下操作:# h1 = x@w1 + tf.broadcast_to(b1, [x.shape[0], 256])h1 = x@w1 + b1h1 = tf.nn.relu(h1)# [b, 256] => [b, 128]h2 = h1@w2 + b2h2 = tf.nn.relu(h2)# [b, 128] => [b, 10]out = h2@w3 + b3# compute loss# out: [b, 10]# y: [b]# 将y进行one-hot编码y_onehot = tf.one_hot(y, depth=10)# mse = mean(sum(y-out)^2)# [b, 10]loss = tf.square(y_onehot - out)# mean: scalarloss = tf.reduce_mean(loss)# compute gradientsgrads = tape.gradient(loss, [w1, b1, w2, b2, w3, b3])# print(grads)# w1 = w1 - lr * w1_gradsw1.assign_sub(lr * grads[0])# w1 = w1 - lr * grads[0]b1.assign_sub(lr * grads[1])# b1 = b1 - lr * grads[1]w2.assign_sub(lr * grads[2])# w2 = w2 - lr * grads[2]b2.assign_sub(lr * grads[3])# b2 = b2 - lr * grads[3]w3.assign_sub(lr * grads[4])# w3 = w3 - lr * grads[4]b3.assign_sub(lr * grads[5])# b3 = b3 - lr * grads[5]# print(isinstance(b3, tf.Variable))# print(isinstance(b3, tf.Tensor))if step % 100 == 0:print(epoch, step, 'loss:', float(loss))
深度学习(11)TensorFlow基础操作七: 向前传播(张量)实战相关推荐
- 视频教程-人人都会深度学习之Tensorflow基础入门-深度学习
人人都会深度学习之Tensorflow基础入门 大数据工程师/算法工程师/大数据讲师,毕业于西华大学软件工程专业.在大数据领域有着丰富的实战经验. 擅长领域:Spark/Hadoop.算法设计及系统架 ...
- 【深度学习】TensorFlow基础知识点总结
TensorFlow基础 其是一个面向于深度学习算法的科学计算库,内部数据保存在张量(Tensor)对象上,所有操作也都是基于张量对象进行. 1.数据类型 数值型 --其是TensorFlow的主 ...
- 深度学习框架-Tensorflow基础学习
Tensorflow简介 1. Tensorflow是一个编程系统,使用图来表示计算任务.使用图来表示计算任务. 图中的节点被称之为 op (operation 的缩写). 一个 op 获得 0 个或 ...
- 深度学习(10)TensorFlow基础操作六: 数学运算
深度学习(10)TensorFlow基础操作六: 数学运算 1. Operation type 2. + - * / % // 3. tf.math.log & tf.exp 4. log2, ...
- 深度学习(9)TensorFlow基础操作五: Broadcasting
深度学习(9)TensorFlow基础操作五: Broadcasting 1. 操作思想 2. 具体例子 3. 理解 (1) How to understand? (2) Why Broadcasti ...
- 深度学习(8)TensorFlow基础操作四: 维度变换
深度学习(8)TensorFlow基础操作四: 维度变换 1. View 2. 示例 3. Reshape操作可能会导致潜在的bug 4. tf.transpose 5. Squeeze VS Exp ...
- 深度学习(7)TensorFlow基础操作三: 索引与切片
深度学习(7)TensorFlow基础操作三: 索引与切片 一. 基础索引 1. Basic indexing 2. Numpy-style indexing 3. start : end 4. 切片 ...
- 深度学习(6)TensorFlow基础操作二: 创建Tensor
深度学习(6)TensorFlow基础操作二: 创建Tensor 一. 创建方式 1. From Numpy,List 2. zeros,ones (1) tf.zeros() (2) tf.zero ...
- 深度学习(5)TensorFlow基础操作一: TensorFlow数据类型
深度学习(5)TensorFlow基础操作一: TensorFlow数据类型 Data Container(数据载体) What's Tensor TF is a computing lib(科学计算 ...
最新文章
- mxnet加载resnet,进行预测
- ScheduleThreadPoolExecutor的工作原理与使用示例
- 图论 —— 生成树 —— 最小瓶颈生成树
- 制作win10安装u盘_最简单的Win10系统安装U盘制作方法
- Hibernatel框架关联映射
- 大学里,数据系鄙视物理系,请问二位来自漫画系嘛?
- Android修行手册 -初识Chip
- 哗啦啦收银系统故障收集
- 沟谷网络提取及沟壑密度计算
- 【Mac使用技巧】Mac的VMware虚拟机系统时间如何调成不同步
- 猎人抓兔子 - (广度优先算法)
- Flask 源码解析:session
- 拒不协助执行会有什么后果?
- 复杂网络中节点重要性方面的研究热点问题
- 将本地端口映射子域名
- python opencv设置不同的视频编解码器参数
- Python3飞机大战全代码(亲测OJBK)
- ESP32相关知识点
- 道路测量计算机程序,求卡西欧计算机4850道路测量程序
- 如何去写一封外贸开发信
热门文章
- Android开发之自定义AlertDialog的大小
- Android开发之打开指定APP | 打开APP任意页面的方法
- ImageLoader must be init with configuration before using 错误解决方法
- 尚洋优选健康美电商平台启动仪式在广州召开
- 04Hadoop中的setPartitionerClass/SortComparator/GroupingComparator问题
- 解决jsp两种提交方式乱码 的方法
- COMBO--组合拳打穿回调地狱~
- [高中作文赏析]相约
- 人工大脑项目 —— Nengo
- 怎么注销midas服务器程序,MIDAS的服务器镜像技术