深度学习(11)TensorFlow基础操作七: 向前传播(张量)实战
深度學(xué)習(xí)(11)TensorFlow基礎(chǔ)操作七: 向前傳播(張量)實戰(zhàn)
- 1. 導(dǎo)包
- 2. 加載數(shù)據(jù)集
- 3. 轉(zhuǎn)換數(shù)據(jù)類型
- 4. 查看x.shape, y.shape, x.dtype, y.dtype
- 5. 查看x和y的范圍
- 6. 將一些無關(guān)信息屏蔽掉
- 7. 將x中的數(shù)值設(shè)置在[0, 1]之間
- 8. 創(chuàng)建數(shù)據(jù)集
- 9. 創(chuàng)建迭代器
- 10. 創(chuàng)建權(quán)值
- 11. 前向運算
- 12. 添加激活函數(shù)
- 13. 計算誤差
- 14. 設(shè)置自動求導(dǎo)功能
- 15. 求解梯度
- 16. 設(shè)置學(xué)習(xí)率
- 17. 向前傳播
- 18. 打印步數(shù)
- 19. 每100步打印步數(shù)和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. 導(dǎo)包
import tensorflow as tf from tensorflow import keras from tensorflow.keras import datasets2. 加載數(shù)據(jù)集
其中x.shape=[60k, 28, 28]; y.shap=[60k];
# x: [60k, 28, 28], # y: [60k] (x, y), _ = datasets.mnist.load_data()3. 轉(zhuǎn)換數(shù)據(jù)類型
將x的數(shù)據(jù)類型轉(zhuǎn)換為Tensor,一般常用tf.float32;
將y的數(shù)據(jù)類型轉(zhuǎn)換為Tensor,y中數(shù)據(jù)為整型,所以使用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)結(jié)果如下:
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. 將一些無關(guān)信息屏蔽掉
例如:
[‘TF_CPP_MIN_LOG_LEVEL’] = ‘0’就是打印全部信息,如果[‘TF_CPP_MIN_LOG_LEVEL’] = ‘2’就是屏蔽這些信息;
7. 將x中的數(shù)值設(shè)置在[0, 1]之間
將x除以255.即可,這樣x的范圍就被設(shè)置在[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. 創(chuàng)建數(shù)據(jù)集
創(chuàng)建數(shù)據(jù)集的好處在于可以每次取一個batch,而原數(shù)據(jù)集每次只能取一個數(shù)據(jù);
# 創(chuàng)建數(shù)據(jù)集 train_db = tf.data.Dataset.from_tensor_slices((x, y)).batch(128)9. 創(chuàng)建迭代器
# 創(chuàng)建迭代器 train_iter = iter(train_db) sample = next(train_iter)一個sample就代表了迭代一次,也就是一個batch的數(shù)據(jù);
查看batch及sample[n]
結(jié)果如下:
sample[0]代表x的數(shù)據(jù),一個batch里有128張28×28的照片,所以sample[0].shape=[128, 28, 28];
sample[1]代表y的數(shù)據(jù),一個batch里有128個整型數(shù)據(jù),這些數(shù)據(jù)代表了每張圖片的標(biāo)簽,也就是[0~9],所以sample[1].shape=[128,]
10. 創(chuàng)建權(quán)值
創(chuàng)建w1和b1并初始化
w1一般按照裁剪過的正態(tài)分布來初始化; b1一般初始化為0;
初始化的過程要注意維度設(shè)置,w: [dim_in, dim_out], b: [dim_out];
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]轉(zhuǎn)換為[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操作,當(dāng)然如過想要手動也可以按照如下操作:# 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就是剛才設(shè)置的一個batch的數(shù)據(jù);
12. 添加激活函數(shù)
使其非線性化;
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 + b313. 計算誤差
(1) 將y進(jìn)行one-hot編碼
# 將y進(jìn)行one-hot編碼 y_onehot = tf.one_hot(y, depth=10)depth=10代表一共有10類,即[0~9];
注: 將y進(jìn)行one-hot編碼這一步在數(shù)據(jù)創(chuàng)建時或者在這里都行,只要在計算誤差之前操作就行;
(2) 計算均方差
(3) 計算誤差均值
得到一個Scalar(標(biāo)量)
14. 設(shè)置自動求導(dǎo)功能
將所有誤差計算的部分全部放入自動求導(dǎo)的功能內(nèi):
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操作,當(dāng)然如過想要手動也可以按照如下操作:# 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進(jìn)行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. 設(shè)置學(xué)習(xí)率
即leaning rate,lr
lr = 1e-31e-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. 打印步數(shù)
由:
for(x, y) in train_db:變?yōu)?
for step, (x, y) in enumerate(train_db):這樣每一步都會打印當(dāng)前步數(shù);
19. 每100步打印步數(shù)和loss
if step % 100 == 0:print(step, 'loss:', float(loss))20. 運行程序
發(fā)現(xiàn)如下報錯:
打印grads:
print(grads)發(fā)現(xiàn)全部為None類型:
原因: tf.GradientTape()默認(rèn)只會跟蹤類型為tf.Variable的數(shù)據(jù),但是我們在創(chuàng)建權(quán)重的時候,w1, b1, w2, b2, w3, b3為Tensor類型的數(shù)據(jù),所以我們需要使用tf.Variable()函數(shù)將這些權(quán)重進(jìn)行封裝:
# 創(chuàng)建權(quán)值 # [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()會自動跟蹤梯度信息;
再次運行,還是出現(xiàn)同樣的錯誤。
原因: w1 = w1 - lr * grads[0]
中后面的w1是剛才已經(jīng)改好的Variable類型的數(shù)據(jù),而前邊的w1卻還是Tensor類型的數(shù)據(jù),前后兩個w1是兩個不同的對象,這樣在下一次計算的時候就會出錯。
解決辦法: 使用原地更新的函數(shù)assign_sub():
其中assign_sub就是w - lrgrads[0]; assign_add就是w + lrgrads[0];
這樣就會使得數(shù)據(jù)類型保持不變;
驗證: 打印b3的數(shù)據(jù)類型
運行結(jié)果如下:
這說明原地更新的方法成功了,b3以及其它權(quán)重已經(jīng)是Variable類型了。
21. 再次運行
運行結(jié)果如下:
可以看到,100輪迭代過后loss值為nan,這是梯度爆炸現(xiàn)象。
解決: 在初始化權(quán)重的時候,需要初始化在一個較好的范圍內(nèi)。原來的truncated_normal()滿足均值為0,方差為1的正態(tài)分布,我們需要將其方差變小,在truncated_normal()里設(shè)置參數(shù)stddev=0.1:
運行程序,結(jié)果如下:
可以看到,梯度爆炸問題得到了非常明顯的改善。所以說,在深度學(xué)習(xí)中,每一個參數(shù)對于整個結(jié)果影響是非常大的。
從運行結(jié)果可以看出,loss在不斷下降,完成了對60k張圖片的一次完整的迭代,如果想要多迭代幾次,可以再添加一個epoch循環(huán):
運行結(jié)果如下:
從這兩次運行結(jié)果可以看出,多輪epoch迭代要比單次迭代的效果好很多。
參考文獻(xiàn):
[1] 龍良曲:《深度學(xué)習(xí)與TensorFlow2入門實戰(zhàn)》
附錄: 完整代碼
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() # 轉(zhuǎn)換數(shù)據(jù)類型 # 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)) # 創(chuàng)建數(shù)據(jù)集 train_db = tf.data.Dataset.from_tensor_slices((x, y)).batch(128) # 創(chuàng)建迭代器 train_iter = iter(train_db) sample = next(train_iter) # 查看sample[n].shape print('batch:', sample[0].shape, sample[1].shape)# 創(chuàng)建權(quán)值 # [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]轉(zhuǎn)換為[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操作,當(dāng)然如過想要手動也可以按照如下操作:# 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進(jìn)行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))總結(jié)
以上是生活随笔為你收集整理的深度学习(11)TensorFlow基础操作七: 向前传播(张量)实战的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 抖音外卖平台叫什么名字
- 下一篇: 深度学习(12)TensorFlow高阶