深度学习(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基础操作七: 向前传播(张量)实战相关推荐

  1. 视频教程-人人都会深度学习之Tensorflow基础入门-深度学习

    人人都会深度学习之Tensorflow基础入门 大数据工程师/算法工程师/大数据讲师,毕业于西华大学软件工程专业.在大数据领域有着丰富的实战经验. 擅长领域:Spark/Hadoop.算法设计及系统架 ...

  2. 【深度学习】TensorFlow基础知识点总结

    TensorFlow基础 ​ 其是一个面向于深度学习算法的科学计算库,内部数据保存在张量(Tensor)对象上,所有操作也都是基于张量对象进行. 1.数据类型 数值型 --其是TensorFlow的主 ...

  3. 深度学习框架-Tensorflow基础学习

    Tensorflow简介 1. Tensorflow是一个编程系统,使用图来表示计算任务.使用图来表示计算任务. 图中的节点被称之为 op (operation 的缩写). 一个 op 获得 0 个或 ...

  4. 深度学习(10)TensorFlow基础操作六: 数学运算

    深度学习(10)TensorFlow基础操作六: 数学运算 1. Operation type 2. + - * / % // 3. tf.math.log & tf.exp 4. log2, ...

  5. 深度学习(9)TensorFlow基础操作五: Broadcasting

    深度学习(9)TensorFlow基础操作五: Broadcasting 1. 操作思想 2. 具体例子 3. 理解 (1) How to understand? (2) Why Broadcasti ...

  6. 深度学习(8)TensorFlow基础操作四: 维度变换

    深度学习(8)TensorFlow基础操作四: 维度变换 1. View 2. 示例 3. Reshape操作可能会导致潜在的bug 4. tf.transpose 5. Squeeze VS Exp ...

  7. 深度学习(7)TensorFlow基础操作三: 索引与切片

    深度学习(7)TensorFlow基础操作三: 索引与切片 一. 基础索引 1. Basic indexing 2. Numpy-style indexing 3. start : end 4. 切片 ...

  8. 深度学习(6)TensorFlow基础操作二: 创建Tensor

    深度学习(6)TensorFlow基础操作二: 创建Tensor 一. 创建方式 1. From Numpy,List 2. zeros,ones (1) tf.zeros() (2) tf.zero ...

  9. 深度学习(5)TensorFlow基础操作一: TensorFlow数据类型

    深度学习(5)TensorFlow基础操作一: TensorFlow数据类型 Data Container(数据载体) What's Tensor TF is a computing lib(科学计算 ...

最新文章

  1. mxnet加载resnet,进行预测
  2. ScheduleThreadPoolExecutor的工作原理与使用示例
  3. 图论 —— 生成树 —— 最小瓶颈生成树
  4. 制作win10安装u盘_最简单的Win10系统安装U盘制作方法
  5. Hibernatel框架关联映射
  6. 大学里,数据系鄙视物理系,请问二位来自漫画系嘛?
  7. Android修行手册 -初识Chip
  8. 哗啦啦收银系统故障收集
  9. 沟谷网络提取及沟壑密度计算
  10. 【Mac使用技巧】Mac的VMware虚拟机系统时间如何调成不同步
  11. 猎人抓兔子 - (广度优先算法)
  12. Flask 源码解析:session
  13. 拒不协助执行会有什么后果?
  14. 复杂网络中节点重要性方面的研究热点问题
  15. 将本地端口映射子域名
  16. python opencv设置不同的视频编解码器参数
  17. Python3飞机大战全代码(亲测OJBK)
  18. ESP32相关知识点
  19. 道路测量计算机程序,求卡西欧计算机4850道路测量程序
  20. 如何去写一封外贸开发信

热门文章

  1. Android开发之自定义AlertDialog的大小
  2. Android开发之打开指定APP | 打开APP任意页面的方法
  3. ImageLoader must be init with configuration before using 错误解决方法
  4. 尚洋优选健康美电商平台启动仪式在广州召开
  5. 04Hadoop中的setPartitionerClass/SortComparator/GroupingComparator问题
  6. 解决jsp两种提交方式乱码 的方法
  7. COMBO--组合拳打穿回调地狱~
  8. [高中作文赏析]相约
  9. 人工大脑项目 —— Nengo
  10. 怎么注销midas服务器程序,MIDAS的服务器镜像技术