【深度学习】(7) 交叉验证、正则化,自定义网络案例:图片分类,附python完整代码
各位同學(xué)好,今天和大家分享一下TensorFlow2.0深度學(xué)習(xí)中的交叉驗證法和正則化方法,最后展示一下自定義網(wǎng)絡(luò)的小案例。
1. 交叉驗證
交叉驗證主要防止模型過于復(fù)雜而引起的過擬合,找到使模型泛化能力最優(yōu)的參數(shù)。我們將數(shù)據(jù)劃分為訓(xùn)練集、驗證集、測試集。訓(xùn)練集用于輸入網(wǎng)絡(luò)模型作為樣本進行學(xué)習(xí)。驗證集是在迭代過程中對模型進行評估,尋找最優(yōu)解。測試集是在整個網(wǎng)絡(luò)訓(xùn)練完成后進行評估。
K折交叉驗證,就是將訓(xùn)練集數(shù)據(jù)等比例劃分成K份,以其中的1份作為驗證數(shù)據(jù),其他的K-1份數(shù)據(jù)作為訓(xùn)練數(shù)據(jù)。每次迭代從都是從K個部分選取一份不同的數(shù)據(jù)部分作為測試數(shù)據(jù),剩下的K-1個當(dāng)作訓(xùn)練數(shù)據(jù),最后把得到的K個實驗結(jié)果進行平分。
?
?
劃分方法?
(1)構(gòu)造數(shù)據(jù)集時劃分
首先導(dǎo)入訓(xùn)練集(x,y)和測試集(x_test, y_test),K折交叉驗證是對測試集的劃分,指定迭代500次,每次迭代都從訓(xùn)練集中選出一部分作為驗證數(shù)據(jù)ds_val,剩下的作為訓(xùn)練數(shù)據(jù)ds_train。使用tf.random.shuffle() 隨機打亂索引順序,不影響x和y之間的對應(yīng)關(guān)系。tf.gather()根據(jù)索引來選取值。
# 以手寫數(shù)字為例,獲取訓(xùn)練集和測試集
(x,y),(x_test,y_test) = datasets.mnist.load_data()# 預(yù)處理函數(shù)
def processing(x,y): # 從[0,255]=>[-1,1]x = 2 * tf.cast(x, dtype=tf.float32) / 255.0 - 1y = tf.cast(y, dtype=tf.int32)return(x,y)# 交叉驗證K=500
for epoch in range(500):idx = tf.range(60000) # 假設(shè)training數(shù)據(jù)一共有60k張圖象,生成索引idx = tf.random.shuffle(idx) # 隨機打亂索引# 利用隨機打散的索引來收集數(shù)據(jù),不改變xy之間的關(guān)聯(lián)x_train, y_train = tf.gather(x, idx[:50000]), tf.gather(y, idx[:50000])x_val, y_val = tf.ga,ther(x, idx[-10000:]), tf.gather(y, idx[-10000:])# 構(gòu)建訓(xùn)練集ds_train = tf.data.Dataset.from_tensor_slices((x_train, y_train))  # 自動將輸入的xy轉(zhuǎn)變成tenosr類型ds_train = ds_train.map(processing).shuffle(10000).batch(128) # 對數(shù)據(jù)集中的所有數(shù)據(jù)使用預(yù)處理函數(shù)# 構(gòu)建驗證集ds_val = tf.data.Dataset.from_tensor_slices((x_val, y_val))  ds_val = ds_test.map(processing).batch(128) # 每次迭代取128組數(shù)據(jù),驗證不需要打亂數(shù)據(jù)
 
 
(2)使用訓(xùn)練函數(shù)fit()中的參數(shù)劃分
如果嫌使用上面的方法構(gòu)造數(shù)據(jù)集太麻煩的話,可以在模型訓(xùn)練函數(shù)fit()中指定劃分方式validation_split=0.1,每次迭代取0.1倍的訓(xùn)練數(shù)據(jù)作為驗證集,剩下的作為訓(xùn)練集。ds_train_val?要求是沒有被劃分過的訓(xùn)練集數(shù)據(jù)。這樣的話就不需要再指定validation_data驗證集數(shù)據(jù)了,在劃分時自動生成。
# ds_train_val指沒有劃分過的train和val數(shù)據(jù)集,validation_split=0.1動態(tài)切割,0.1比例的數(shù)據(jù)分給val
network.fit(ds_train_val, epochs=6, validation_split=0.1, validation_freq=2)
# 不需要再指定validation_data,已經(jīng)在被包含在validation_split中了
 
 
在模型迭代過程中使用驗證集來查看什么時候模型效果最優(yōu),找到最優(yōu)的就跳出循環(huán)。驗證集在挑選模型參數(shù)的時候,先保存誤差極小值對應(yīng)的權(quán)重,如果后面檢測到的誤差都大于它,就使用當(dāng)前這個權(quán)重。
?
2. 正則化
當(dāng)采用比較復(fù)雜的模型,去擬合數(shù)據(jù)時,很容易出現(xiàn)過擬合現(xiàn)象,這會導(dǎo)致模型的泛化能力下降,對模型添加正則化項可以限制模型的復(fù)雜度,使得模型在復(fù)雜度和性能達到平衡。
原理:?【通俗易懂】機器學(xué)習(xí)中 L1 和 L2 正則化的直觀解釋
?
L1正則化是在原來的損失函數(shù)基礎(chǔ)上加上權(quán)重參數(shù)的絕對值。L1可以產(chǎn)生0解,L1獲得稀疏解。
L2正則化是在原來的損失函數(shù)基礎(chǔ)上加上權(quán)重參數(shù)的平方和。L2可以產(chǎn)生趨近0的解,L2獲得非零稠密解。
在構(gòu)建網(wǎng)絡(luò)層時指定正則化參數(shù)kernel_regularizer,使用二范數(shù)的方法keras.regularizers.l2,懲罰系數(shù)0.01。
# 使用二范數(shù)正則化,loss = loss + 0.001*regularizer,指定正則化的權(quán)重
model = keras.Sequential([keras.layers.Dense(16, kernel_regularizer=keras.regularizers.l2(0.001), activation=tf.nn.relu),keras.layers.Dense(16, kernel_regularizer=keras.regularizers.l2(0.001), activation=tf.nn.relu),keras.layers.Dense(1, activation=tf.nn.sigmoid)])
 
 
3. 自定義網(wǎng)絡(luò)
3.1 數(shù)據(jù)獲取
首先導(dǎo)入我們需要的庫文件,從系統(tǒng)中導(dǎo)入圖片數(shù)據(jù),劃分測試集和訓(xùn)練集。
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' # 輸出框只輸出有意義的信息#(1)數(shù)據(jù)獲取
(x,y),(x_test,y_test) = datasets.cifar10.load_data() #獲取圖像分類數(shù)據(jù)
# 查看數(shù)據(jù)信息
print(f'x.shape: {x.shape}, y.shape: {y.shape}')  #查看訓(xùn)練集的維度信息
print(f'x_test.shape: {x_test.shape}, y_test.shape: {y_test.shape}')  #測試集未讀信息
print(f'y[:5]: {y[:5]}')  #查看訓(xùn)練集目標(biāo)的前5項
# 繪圖展示
import matplotlib.pyplot as plt
for i in range(10): # 展示前10張圖片plt.subplot(2,5,i+1)  # 2行5列第i+1個位置plt.imshow(x[i])plt.xticks([]) # 不顯示x和y軸坐標(biāo)刻度plt.yticks([])# 輸入的圖像形狀
# x.shape: (50000, 32, 32, 3), y.shape: (50000, 1)
# x_test.shape: (10000, 32, 32, 3), y_test.shape: (10000, 1)
 
 
需要訓(xùn)練的圖片如下,圖片本身不清晰,這里只說一下基本的自定義網(wǎng)絡(luò)的構(gòu)造,最多只有80%準(zhǔn)確率,模型優(yōu)化到卷積神經(jīng)網(wǎng)絡(luò)章節(jié)再談。
?
3.2 數(shù)據(jù)預(yù)處理
由于導(dǎo)入的目標(biāo)值y的shape時二維[50k,1],需要將axis=1的軸壓縮掉,變成一個一維的向量[50k],使用tf.squeeze()壓縮指定軸,對目標(biāo)值one-hot編碼對應(yīng)索引的值變?yōu)?,其他索引對應(yīng)的值變?yōu)?,shape變?yōu)閇b,10]。把特征值x的范圍映射到[-1,1]之間。
#(2)數(shù)據(jù)預(yù)處理
# 定義預(yù)處理函數(shù)
def processing(x,y): # 由于目標(biāo)數(shù)據(jù)是而二維的,把shape=1的軸刪除,從向量變成標(biāo)量y = tf.squeeze(y)  # 默認壓縮所有維度為1的軸,shape為[50k]y = tf.one_hot(y, depth=10) # one-hot編碼,分成10個類別,shape為[50k,10],對應(yīng)下標(biāo)所在的值為1# 每個像素值的范圍在[-1,1]之間,從[0,255]=>[-1,1]x = 2 * tf.cast(x, dtype=tf.float32) / 255.0 - 1y = tf.cast(y, dtype=tf.int32)return(x,y)# 構(gòu)建訓(xùn)練集數(shù)據(jù)集
ds_train = tf.data.Dataset.from_tensor_slices((x, y))  # 自動將輸入的xy轉(zhuǎn)變成tenosr類型
ds_train = ds_train.map(processing).batch(128).shuffle(10000)  # 對數(shù)據(jù)集中的所有數(shù)據(jù)使用預(yù)處理函數(shù)# 構(gòu)建測試集數(shù)據(jù)集
ds_test = tf.data.Dataset.from_tensor_slices((x_test, y_test))  
ds_test = ds_test.map(processing).batch(128) # 每次迭代取128組數(shù)據(jù),測試不需要打亂數(shù)據(jù)# 構(gòu)造迭代器,查看數(shù)據(jù)集是否正確
sample = next(iter(ds_train))  # 每次運行從訓(xùn)練數(shù)據(jù)集中取出一組xy
print('x_batch.shape', sample[0].shape, 'y_batch.shape', sample[1].shape)
# x_batch.shape (128, 32, 32, 3)   y_batch.shape (128, 10)
 
 
3.3 自定義網(wǎng)絡(luò)
#(3)構(gòu)造網(wǎng)絡(luò)
class MyDense(layers.Layer): #必須繼承l(wèi)ayers.Layer層,放到sequential容器中# 代替layers.Dense層def __init__(self, input_dim, output_dim):super(MyDense, self).__init__()   # 調(diào)用母類初始化,必須# 自己發(fā)揮'w''b'指定名字沒什么用,創(chuàng)建shape為[input_dim, output_dim的權(quán)重# 使用add_variable創(chuàng)建變量        self.kernel = self.add_variable('w',[input_dim, output_dim])self.bias = self.add_variable('b', [output_dim])# call方法,training來指示現(xiàn)在是訓(xùn)練還是測試         def call(self, inputs, training=None):x = inputs @ self.kernel + self.biasreturn x# 自定義網(wǎng)絡(luò)層
class MyNetwork(keras.Model):  # 必須繼承keras.Model大類,才能使用complie、fit等功能def __init__(self):super(MyNetwork, self).__init__()  # 調(diào)用父類Mymodel# 新建五個層次self.fc1 = MyDense(32*32*3, 256)  #input_dim=784,output_dim=256self.fc2 = MyDense(256, 128)self.fc3 = MyDense(128, 64)self.fc4 = MyDense(64, 32)        self.fc5 = MyDense(32, 10)def call(self, inputs, training=None):# 前向傳播,可以接收四維的tensorx = tf.reshape(inputs, [-1,32*32*3]) # 改變輸入特征的形狀x = self.fc1(x) #第一層[b,32*32*3]==>[b,256]x = tf.nn.relu(x) #激活函數(shù)x = self.fc2(x)x = tf.nn.relu(x)x = self.fc3(x)x = tf.nn.relu(x)x = self.fc4(x)x = tf.nn.relu(x)x = self.fc5(x)  #logits層return x
 
3.4 網(wǎng)絡(luò)配置
#(4)網(wǎng)絡(luò)配置
network = MyNetwork()       
network.compile(optimizer = optimizers.Adam(lr=0.001),  # 指定優(yōu)化器loss = tf.losses.CategoricalCrossentropy(from_logits=True), #交叉熵損失metrics = ['accuracy'])  # 測試指標(biāo)     #(5)網(wǎng)絡(luò)訓(xùn)練,輸入訓(xùn)練數(shù)據(jù),循環(huán)5次,驗證集為ds_test,每一次大循環(huán)做一次測試
network.fit(ds_train, epochs=5, validation_data=ds_test, validation_freq=1)# 循環(huán)5次后的結(jié)果為
Epoch 5/5
391/391 [==============================] - 3s 8ms/step - loss: 1.2197 - accuracy: 0.5707 - val_loss: 1.3929 - val_accuracy: 0.5182
 
優(yōu)化方法到卷積神經(jīng)網(wǎng)絡(luò)再展示
總結(jié)
以上是生活随笔為你收集整理的【深度学习】(7) 交叉验证、正则化,自定义网络案例:图片分类,附python完整代码的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
                            
                        - 上一篇: 【深度学习】(6) tensorflow
 - 下一篇: 【神经网络】(2) 网络优化,案例:服装