【深度学习】(1) 前向传播,附python完整代码
各位同學大家好,今天和大家分享一下TensorFlow2.0深度學習中前向傳播的推導過程,使用系統自帶的mnist數據集。
1. 數據獲取
首先,我們導入需要用到的庫文件和數據集。導入的x和y數據是數組類型,需要轉換成tensor類型tf.convert_to_tensor(),再查看一下我們讀入的數據有沒有問題。
import numpy as np
import tensorflow as tf
from tensorflow import keras
from tensorflow.keras import datasets # 數據集工具
import os # 設置一下輸出框打印的內容
os.environ['TF_CPP_MIN_LOG_LEVEL'] = '2' # '2'輸出欄只打印error信息,其他亂七八糟的信息不打印#(1)獲取mnist數據集
(x,y),_ = datasets.mnist.load_data()
#(2)轉換成tensor類型,x數據類型一般為float32,y存放的是圖片屬于哪種具體類型,屬于整型
x = tf.convert_to_tensor(x,dtype=tf.float32)
y = tf.convert_to_tensor(y,dtype=tf.int32)
#(3)查看數據內容
print('shape:',x.shape,y.shape,'\ndtype:',x.dtype,y.dtype) #查看shape和數據類型
print('x的最小值:',tf.reduce_min(x),'\nx的最大值:',tf.reduce_max(x)) #查看x的數據范圍
print('y的最小值:',tf.reduce_min(y),'\ny的最大值:',tf.reduce_max(y)) #查看y的數據范圍
# 打印的結果如下
shape: (60000, 28, 28) (60000,)
dtype: <dtype: 'float32'> <dtype: 'int32'>
x的最小值: tf.Tensor(0.0, shape=(), dtype=float32)
x的最大值: tf.Tensor(255.0, shape=(), dtype=float32)
y的最小值: tf.Tensor(0, shape=(), dtype=int32)
y的最大值: tf.Tensor(9, shape=(), dtype=int32)
2. 數據預處理
首先對x數據進行歸一化處理,原來x的每個像素值在[0,255]之間,現在轉變成[0,1]之間。剛導入的y數據的shape是[6000],一維,存放分類數,為了和最后的預測結果比較,對它one-hot編碼,shape變為[6000,10]。存放每張圖屬于每一個分類的概率。y.numpy()[0]表示第0張圖像屬于第5個分類的概率是1,屬于其他分類的概率是0。再設置一個學習率lr,用于每次迭代完成后更新神經網絡權重參數,初始學習率以 0.01 ~ 0.001 為宜。
#(4)預處理
x = x/255. # 歸一化處理,將x數據的范圍從[0,255]變成[0,1]
y = tf.one_hot(y,depth=10) # y是分類數值,對它進行one-hot編碼,shape變為[b,10]
y.numpy()[0] # 查看編碼后的y的數據
# array([0., 0., 0., 0., 0., 1., 0., 0., 0., 0.], dtype=float32)lr = 1e-3 # 學習率,設置過大,會導致loss震蕩,學習難以收斂;設置過小,那么訓練的過程將大大增加
加載數據集tf.data.Dataset.from_tensor_slices(),生成迭代器的?iter(),返回迭代器的下一個項目next()?
#(5)指定一個選取數據的batch,一次取128個數據
train_db = tf.data.Dataset.from_tensor_slices((x,y)).batch(128)
train_iter = iter(train_db) # 指定迭代器
sample = next(train_iter) # 存放的每一個batch
# sample[0]存放x數據,sample[1]存放y數據,每個sample有128組圖片
print('batch:',sample[0].shape,sample[1].shape)
# 打印結果: batch: (128, 28, 28) (128, 10)
3. 構建網絡
輸入特征x的shape為[128,28,28],即輸入層有28*28個神經元,自定義隱含層1有256個神經元,隱含層2有128個神經元,最終輸出結果是固定的10個分類。
根據每一層神經元的個數來確定每個連接層的shape,使用隨機的截斷高斯分布來初始化各個權重參數,將定義的變量從tensor類型,轉變為神經網絡類型variable類型。
#(6)構建網絡
# 輸入層由輸入多少個特征點決定,輸出層根據有多少個分類決定
# 輸入層shape[b,784],輸出層shape[b,10]
# 構建網絡,自定義中間層的神經元個數
# [b,784] => [b,256] => [b,128] => [b,10]# 第一個連接層的權重和偏置,都變成tf.Variable類型,這樣tf.GradientTape才能記錄梯度信息
w1 = tf.Variable(tf.random.truncated_normal([784,256], stddev=0.1)) # 截斷正態分布,標準差調小一點防止梯度爆炸
b1 = tf.Variable(tf.zeros([256])) #維度為[dim_out]
# 第二個連接層的權重和偏置
w2 = tf.Variable(tf.random.truncated_normal([256,128], stddev=0.1)) # 截斷正態分布,維度為[dim_in, dim_out]
b2 = tf.Variable(tf.zeros([128])) #維度為[dim_out]
# 第三個連接層的權重和偏置
w3 = tf.Variable(tf.random.truncated_normal([128,10], stddev=0.1)) # 截斷正態分布,維度為[dim_in, dim_out]
b3 = tf.Variable(tf.zeros([10])) #維度為[dim_out]
4. 前向傳播運算
每一次迭代從train_db中取出128個樣本數據,由于取出的x數據的shape是[128,28,28],需要將它的形狀轉變成[128,28*28]才能傳入輸入層tf.reshape()。h = x @ w + b,本層的特征向量和權重做內積,再加上偏置,將計算結果放入激活函數tf.nn.relu(),得到下一層的輸入特征向量。最終得到的輸出結果out中存放的是每張圖片屬于每個分類的概率。
#(7)前向傳播運算
for i in range(10): #對整個數據集迭代10次# 對數據集的所有batch迭代一次# x為輸入的特征項,shape為[128,28,28],y為分類結果,shape為[128,10]for step,(x,y) in enumerate(train_db): # 返回下標和對應的值# 這里的x的shape為[b,28*28],從[b,w,h]變成[b,w*h]x = tf.reshape(x,[-1,28*28]) #對輸入特征項的維度變換,-1會自動計算bwith tf.GradientTape() as tape: # 自動求導計算梯度,只會跟蹤tf.Variable類型數據# ==1== 從輸入層到隱含層1的計算方法為:h1 = w1 @ x1 + b1 # [b,784] @ [784,256] + [b,256] = [b,256]h1 = x @ w1 + b1 # 相加時會自動廣播,改變b的shape,自動進行tf.broadcast_to(b1,[x.shape[0],256])# 激活函數,relu函數h1 = tf.nn.relu(h1)# ==2== 從隱含層1到隱含層2,[b,256] @ [256,128] + [b,128] = [b,128]h2 = h1 @ w2 + b2h2 = tf.nn.relu(h2)# ==3== 從隱含層2到輸出層,[b,128] @ [128,10] + [b,10] = [b,10]out = h2 @ w3 + b3 # shape為[b,10]#(8)計算誤差,輸出值out的shape為[b,10],onehot編碼后真實值y的shape為[b,10]# 計算均方差 mse = mean(sum((y-out)^2)loss_square = tf.square(y-out) # shape為[b,10]loss = tf.reduce_mean(loss_square) # 得到一個標量# 梯度計算grads = tape.gradient(loss,[w1,b1,w2,b2,w3,b3])# 注意:下面的方法,運算返回值是tf.tensor類型,在下一次運算會出現錯誤# w1 = w1 - lr * grads[0] # grads[0]值梯度計算返回的w1,是grad的第0個元素# 權重更新,lr為學習率,梯度每次下降多少# 因此需要原地更新函數,保證更新后的數據類型不變tf.Variablew1.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: #每100次顯示一次數據print(f"第{step+1}次迭代,loss為{np.float(loss)}") #loss是tensor變量
計算輸出結果和真實結果之間的均方差作為模型損失。使用tf.GradientTape()中的梯度計算方法tape.gradient(),用于更新下一次迭代的權重參數。公式為w1 = w1 - lr * grads[n],但由于該公式返回的是tensor類型的變量,而tf.GradientTape()梯度計算方法,只能跟蹤計算tf.variable類型的數據。因此需要使用assign_sub()函數原地更新權重參數,不造成變量類型改變。
# 最后一次循環循環,loss的輸出結果為:
第1次迭代,loss為0.08258605003356934
第101次迭代,loss為0.09005936980247498
第201次迭代,loss為0.0828738585114479
第301次迭代,loss為0.0822446346282959
第401次迭代,loss為0.08802710473537445
總結
以上是生活随笔為你收集整理的【深度学习】(1) 前向传播,附python完整代码的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 【opencv】(8) 傅里叶变换,高通
- 下一篇: 【深度学习】(2) 数据加载,前向传播2