【深度学习】(5) 简单网络,案例:服装图片分类,附python完整代码
1. 數據獲取
使用系統內部的服裝數據集構建神經網絡。首先導入需要的庫文件,x和y中保存訓練集的圖像和目標。x_test和y_test中保存測試集需要的圖像和目標。(x, y)及(x_test, y_test)都是數組類型。
# keras層方式做前向傳播,服裝圖片分類每一張圖片28*28
import tensorflow as tf
from tensorflow import keras
from tensorflow.keras import datasets,layers,optimizers,Sequential,metrics
# 導入數據集管理庫,層級,優化器,全連接層容器,測試度量器
import os # 設置一下輸出框打印的內容
os.environ['TF_CPP_MIN_LOG_LEVEL'] = '2' # '2'輸出欄只打印error信息,其他亂七八糟的信息不打印#(1)導入數據集,數組類型
(x,y),(x_test,y_test) = datasets.fashion_mnist.load_data()
# 查看數據集信息
print(f'x.shape={x.shape},y.shape={y.shape}') # 查看訓練集xy的大小
print(f'x_test.shape={x_test.shape},y_test.shape={y_test.shape}') #查看測試集的大小
print(f'y[:5]={y[:5]}') # 查看y的前5項數據
數據集信息如下,變量x中有60k張圖片,每張圖片的大小是28*28,變量y保存的是每張圖片屬于哪個分類。如,y[:5]=[9 0 0 3 0],第一張圖片屬于第10個類別,第二張圖片屬于第0個類別。
x.shape=(60000, 28, 28),y.shape=(60000,)
x_test.shape=(10000, 28, 28),y_test.shape=(10000,)
y[:5]=[9 0 0 3 0]
為了我們能對這個數據集有個直觀的認識,將圖片信息繪制出來,展示前10張圖像。
# 數據集展示
import matplotlib.pyplot as plt
import numpy as np
# 每個類別的名稱
class_names = ['Tshirt','Trouser','Pullover','Dress','Coat','Sandal','Shirt','Sneaker','Bag','Ankle boot']
# 繪制圖像
for i in range(0,10):plt.subplot(2,5,i+1) # 當前的圖繪制在2行5列的第i+1個位置plt.imshow(x[i]) plt.xlabel(class_names[y[i]]) #y[i]代表所屬分類的標簽值plt.xticks([]) # 不顯示x和y軸坐標plt.yticks([])
2. 數據加載
首先對訓練集的目標值y進行one-hot編碼,便于后續與預測結果計算損失。從標量變成一個向量,索引對應的數值變成1。如編碼前y[0]=9,第一張圖片對應的分類是第9類,編碼后y[0]=[0., 0., 0., 0., 0., 0., 0., 0., 0., 1.],第9個索引對應的值變成1,其他值都是0。
使用tf.data.Dataset.from_tensor_slices()創建數據集,自動將輸入的數組類型轉變成tensor類型。使用.map()函數對數據集中的所有元素執行函數內容;使用.batch()函數指定每次迭代從數據集中取多少個數據,.shuffle()打亂數據集,但不改變xy的對應關系,避免結果出現偶然性。
在對訓練集預處理時,不需要對y_test數據進行one-hot編碼,因為測試得到的預測結果是一個數值,和y_test比較,看是不是相同就行。
# 數據預處理函數,轉變數據類型
def processing(x,y):x = tf.cast(x,tf.float32)/255.0 # x數據改變數據類型,并歸一化y = tf.cast(y,tf.int32) # 對目標y改變數據類型return(x,y)#(2)數據加載
# 對訓練集預處理
y = tf.one_hot(y,depth=10) # one-hot編碼,轉換成長度為10的向量,對應索引的值變為1
ds_train = tf.data.Dataset.from_tensor_slices((x,y)) # 自動將x和y轉換為tensor類型
ds_train = ds_train.map(processing).batch(128).shuffle(10000) # 設置每次采樣大小,并打亂
# 對測試集預處理
ds_test = tf.data.Dataset.from_tensor_slices((x_test,y_test))
ds_test = ds_test.map(processing).batch(128) #一次測試完所有測試集樣本呢,不需要打亂# 生成迭代器,檢測數據加載是否正確
sample = next(iter(ds_train)) # 運行一次取出一個batch,即128個數據
print('x_batch:',sample[0].shape,'y_batch:',sample[1].shape) # 查看一次取了多少個
構造一個迭代器來查看我們預處理后的數據是否正確,起檢驗作用。iter()函數生成一個迭代器,每執行一次next()函數,從ds_train中獲取一組大小為batch的樣本(x, y),sample[0]保存x的數據,sample[1]保存y的數據。
# 輸出結果
x.shape=(60000, 28, 28),y.shape=(60000,)
x_test.shape=(10000, 28, 28),y_test.shape=(10000,)
y[:5]=[9 0 0 3 0]
x_batch: (128, 28, 28) y_batch: (128, 10)
3. 構建網絡
使用堆層的方法構建全連接層?tf.keras.Sequential(),使用?tf.keras.layers.Dense()添加每一層,構建5層的全連接層,指定激活函數為relu函數,使網絡的維度從[b,28*28]變換到最終的[b,10],即輸出10個分類的結果。使用model.build()函數指定輸入層的輸入特征大小,model.summary()查看整個網絡的結構,指定優化器跟新權重和偏置optimizers.Adam(),學習率為0.001,即梯度下降速度。
#(3)構建網絡
# ==1== 設置全連接層
# [b,784]=>[b,256]=>[b,128]=>[b,64]=>[b,32]=>[b,10],中間層一般從大到小降維
model = Sequential([layers.Dense(256, activation=tf.nn.relu), #第一個連接層,輸出256個特征layers.Dense(128, activation=tf.nn.relu), #第二個連接層layers.Dense(64, activation=tf.nn.relu), #第三個連接層layers.Dense(32, activation=tf.nn.relu), #第四個連接層layers.Dense(10), #最后一層不需要激活函數,輸出10個分類])# ==2== 設置輸入層維度
model.build(input_shape=[None, 28*28])
# ==3== 查看網絡結構
model.summary()
# ==4== 優化器
# 完成權重更新 w = w - lr * grad
optimizer = optimizers.Adam(lr=1e-3)
網絡結構如下,param代表每一層的參數個數,以最后一層為例,權重w的shape為[32,10],偏置b的shape為[10],參數個數為32*10+10=330
Model: "sequential"
_________________________________________________________________Layer (type) Output Shape Param #
=================================================================dense (Dense) (None, 256) 200960 dense_1 (Dense) (None, 128) 32896 dense_2 (Dense) (None, 64) 8256 dense_3 (Dense) (None, 32) 2080 dense_4 (Dense) (None, 10) 330 =================================================================
Total params: 244,522
Trainable params: 244,522
Non-trainable params: 0
_________________________________________________________________
4. 網絡訓練
設置整個網絡循環20次。for step,(x,y) in enumerate(ds_train)代表每次從訓練集中取batch個樣本用于訓練,等把樣本都取完了就完成一次循環。每次取出的x的shape為[128, 28, 28],y的shape為[128, 10]。
使用model(x)會自動對輸入的x完成權重和偏置計算,得到隱含層最后一層的輸出結果logits。接下來計算損失函數,使用交叉熵更新權重偏置。梯度計算tape.gradient(),以loss2為因變量,權重和偏置為自變量計算梯度,model.trainable_variables()獲取網絡所有的權重和偏置。
zip(grads, model.trainable_variables) 將梯度和權重及偏置結合在一起。optimizer.apply_gradients(),使用優化器更新梯度,相當于計算w1 = w1 - lr * grads[0],并原地更新權重偏置不改變數據類型,簡化了w1.assign_sub(lr * grads[0]),不需要一個一個手敲計算更新梯度。
#(5)前向傳播
for epoch in range(20): # 運行20次# 運行每一個batchfor step,(x,y) in enumerate(ds_train):# ds_train中x的shape是[b,28,28],由于輸入層是[b,28*28],需要類型轉換x = tf.reshape(x, [-1, 28*28]) #-1會自動結算第0維# 梯度計算with tf.GradientTape() as tape:# 網絡自動運行:[b,784]=>[b,10]logits = model(x) #得到最后一層的輸出# 計算均方誤差,真實值y(onehot編碼后的)和輸出結果之間loss1 = tf.reduce_mean(tf.losses.MSE(y, logits)) # 計算交叉熵損失,真實值y(onehot編碼后的)和輸出概率(logits會自動進行softmax變成概率值)loss2 = tf.reduce_mean(tf.losses.categorical_crossentropy(y, logits, from_logits=True))# 梯度計算,第1個因變量,第2個自變量,model.trainable_variables獲得所有的權重和偏置參數grads = tape.gradient(loss2, model.trainable_variables) # 更新權重,zip將grads的元素和model.trainable_variables中的元素結合在一起optimizer.apply_gradients(zip(grads, model.trainable_variables)) # 完成任務:w1.assign_sub(lr * grads[0])# 每次運行完一個batch后打印結果if step % 100 == 0:print(f'epochs:{epoch}, step:{step}, loss_MSE:{loss1}, loss_CE:{loss2}')
5. 網絡測試
網絡測試是在20次的大循環內部的,使用總預測對了的個數除以總測試樣本數來計算模型準確率。同樣,每次迭代從測試集中取batch個樣本,放入網絡計算,得到輸出層的結果logits,需要求預測結果概率最大值所在下標,知道了下標索引也就知道了每個測試樣本屬于第幾個分類。
使用tf.nn.softmax()函數將輸出結果映射到0-1之間,且概率和為1,再配合tf.argmax()就知道概率最大值所在的下標值。將預測值predict和真實值y比較,看是否相同,若相同就是預測對了。tf.equal()返回布爾類型,如果兩個變量對應位置的值相同就返回True,再將布爾類型轉換位數值類型即可求和tf.reduce_sum()。
#(6)網絡測試--前向傳播total_correct = 0 # 總預測對了的個數total_sum = 0 # 總統計的個數for (x,y) in ds_test: #返回測試集的x和y# 將x的shape從[b,28,28]=>[b,28*28]x = tf.reshape(x, [-1,28*28]) # 計算輸出層[b,10]logits = model(x)# 計算概率最大的值所在的索引 # 將logits轉換為probilityprob = tf.nn.softmax(logits, axis=1) # 在最后一個維度上轉換概率,且概率和為1predict = tf.argmax(prob, axis=1) # 找到最大值所在位置,得到一個標量# y 是int32類型,shape為[128]# predict 是int64類型,shape為[128]predict = tf.cast(predict, dtype=tf.int32)# y是一個向量,每個元素代表屬于第幾類;predict也是一個向量,下標值指示屬于第幾類# 只要看兩個變量的值是否相同correct = tf.equal(y, predict) #返回True和False# True和False變成1和0,統計1的個數,一共有多少個預測對了correct = tf.reduce_sum(tf.cast(correct, dtype=tf.int32))# 預測對了的個數,correct是tensor類型,變量numpy類型total_correct += int(correct) total_sum += x.shape[0] #第0維度,每次測試有多少張圖片 # 計算一次大循環之后的模型準確率acc = total_correct/total_sumprint(f'acc: {acc}')
6. 結果展示
模型第1次循環,以及經過20次循環后的結果如下:
epochs: 0
loss_MSE: 16.709985733032227, loss_CE: 0.4360983967781067
acc: 0.839epochs: 19
loss_MSE: 130.57691955566406, loss_CE: 0.27311569452285767
acc: 0.8878
完整代碼如下:
# keras層方式做前向傳播,服裝圖片分類每一張圖片28*28
import tensorflow as tf
from tensorflow import keras
from tensorflow.keras import datasets,layers,optimizers,Sequential,metrics
# 導入數據集管理庫,層級,優化器,全連接層容器,測試度量器
import os # 設置一下輸出框打印的內容
os.environ['TF_CPP_MIN_LOG_LEVEL'] = '2' # '2'輸出欄只打印error信息,其他亂七八糟的信息不打印#(1)導入數據集,數組類型
(x,y),(x_test,y_test) = datasets.fashion_mnist.load_data()
# 查看數據集信息
print(f'x.shape={x.shape},y.shape={y.shape}') # 查看訓練集xy的大小
print(f'x_test.shape={x_test.shape},y_test.shape={y_test.shape}') #查看測試集的大小
print(f'y[:5]={y[:5]}') # 查看y的前5項數據#(2)數據預處理,轉變為tenor類型
def processing(x,y):x = tf.cast(x,tf.float32)/255.0 # x數據改變數據類型,并歸一化y = tf.cast(y,tf.int32) # 對目標y改變數據類型return(x,y)#(3)數據加載
# 對訓練集預處理
y = tf.one_hot(y,depth=10) # one-hot編碼,轉換成長度為10的向量,對應索引的值變為1
ds_train = tf.data.Dataset.from_tensor_slices((x,y)) # 自動將x和y轉換為tensor類型
ds_train = ds_train.map(processing).batch(128).shuffle(10000) # 設置每次采樣大小,并打亂
# 對測試集預處理
ds_test = tf.data.Dataset.from_tensor_slices((x_test,y_test))
ds_test = ds_test.map(processing).batch(128) #一次測試完所有測試集樣本呢,不需要打亂
# 生成迭代器,檢測數據加載是否正確
sample = next(iter(ds_train)) # 運行一次取出一個batch,即128個數據
print('x_batch:',sample[0].shape,'y_batch:',sample[1].shape) # 查看一次取了多少個#(4)構建網絡
# ==1== 設置全連接層
# [b,784]=>[b,256]=>[b,128]=>[b,64]=>[b,32]=>[b,10],中間層一般從大到小降維
# 輸入的維度會自動推算
model = Sequential([layers.Dense(256, activation=tf.nn.relu), #第一個連接層,輸出256個特征layers.Dense(128, activation=tf.nn.relu), #第二個連接層layers.Dense(64, activation=tf.nn.relu), #第三個連接層layers.Dense(32, activation=tf.nn.relu), #第四個連接層layers.Dense(10), #最后一層不需要激活函數,輸出10個分類])# ==2== 設置輸入層維度
model.build(input_shape=[None, 28*28])# ==3== 查看網絡結構
model.summary()
# 330 = 權重參數32*10 + 偏置參數10# ==4== 優化器
# 完成權重更新 w = w - lr * grad
optimizer = optimizers.Adam(lr=1e-3) #(5)模型訓練--前向傳播
for epoch in range(20): # 運行20次# 運行每一個batchfor step,(x,y) in enumerate(ds_train):# ds_train中x的shape是[b,28,28],由于輸入層是[b,28*28],需要類型轉換x = tf.reshape(x, [-1, 28*28]) #-1會自動結算第0維# 梯度計算with tf.GradientTape() as tape:# 網絡自動運行:[b,784]=>[b,10]logits = model(x) #得到最后一層的輸出# 計算均方誤差,真實值y(onehot編碼后的)和輸出結果之間loss1 = tf.reduce_mean(tf.losses.MSE(y, logits)) # 計算交叉熵損失,真實值y(onehot編碼后的)和輸出概率(logits會自動進行softmax變成概率值)loss2 = tf.reduce_mean(tf.losses.categorical_crossentropy(y, logits, from_logits=True))# 梯度計算,第1個因變量,第2個自變量,model.trainable_variables獲得所有的權重和偏置參數grads = tape.gradient(loss2, model.trainable_variables) # 更新權重,zip將grads的元素和model.trainable_variables中的元素結合在一起optimizer.apply_gradients(zip(grads, model.trainable_variables)) # 完成任務:w1.assign_sub(lr * grads[0])# 每次運行完一個batch后打印結果# if step % 100 == 0:# print(f'epochs:{epoch}, step:{step}, loss_MSE:{loss1}, loss_CE:{loss2}')# 一次循環完成的結果print(f'epochs: {epoch}, loss_MSE: {loss1}, loss_CE: {loss2}')#(6)網絡測試--前向傳播total_correct = 0 # 總預測對了的個數total_sum = 0 # 總統計的個數for (x,y) in ds_test: #返回測試集的x和y# 將x的shape從[b,28,28]=>[b,28*28]x = tf.reshape(x, [-1,28*28]) # 計算輸出層[b,10]logits = model(x)# 計算概率最大的值所在的索引 # 將logits轉換為probilityprob = tf.nn.softmax(logits, axis=1) # 在最后一個維度上轉換概率,且概率和為1predict = tf.argmax(prob, axis=1) # 找到最大值所在位置,得到一個標量# y 是int32類型,shape為[128]# predict 是int64類型,shape為[128]predict = tf.cast(predict, dtype=tf.int32)# y是一個向量,每個元素代表屬于第幾類;predict也是一個向量,下標值指示屬于第幾類# 只要看兩個變量的值是否相同correct = tf.equal(y, predict) #返回True和False# True和False變成1和0,統計1的個數,一共有多少個預測對了correct = tf.reduce_sum(tf.cast(correct, dtype=tf.int32))# 預測對了的個數,correct是tensor類型,變量numpy類型total_correct += int(correct) total_sum += x.shape[0] #第0維度,每次測試有多少張圖片 # 計算一次大循環之后的模型準確率acc = total_correct/total_sumprint(f'acc: {acc}')
總結
以上是生活随笔為你收集整理的【深度学习】(5) 简单网络,案例:服装图片分类,附python完整代码的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 【深度学习】(4) 梯度下降、损失函数
- 下一篇: 【深度学习】(6) tensorflow