Tensorflow實現影象資料增強(Data Augmentation)
在我們處理有關影象的任務,比如目標檢測,分類,語義分割等等問題當中,我們常常需要對訓練集當中的圖片進行資料增強(data augmentation),這樣會讓訓練集的樣本增多,同時讓神經網路模型的泛化能力更強。在進行圖片的資料增強時,我們一般會對影象進行翻轉,剪裁,灰度變化,對比度變化,顏色變化等等方式生成新的訓練集,這就是計算機視覺當中的資料增強。我們來看看使用影象增強的手段,對一個貓狗影象分類的具體問題是怎麼處理的。
首先我們匯入各種包,像tensorflow之類的就不說了,其中的一個包叫做glob,這個包主要是用於讀取本地計算機上的圖片資料所用的,使用起來十分方便,只需幾行程式碼即可將圖片資料讀入進來,比pathlib包讀取圖片方便多了,程式碼如下所示:
import tensorflow as tf from tensorflow import keras import matplotlib.pyplot as plt import numpy as np import glob import os
然後讀取圖片,我把我的貓狗圖片資料都放在了train資料夾下,這個資料夾下又有兩個資料夾,分別是dog和cat,在呼叫glob庫寫圖片路徑的同時,我們直接用“*”號來表示一個資料夾下的所有檔案都讀取,程式碼如下所示:
#首先獲取圖片,glob這個庫明顯感覺更加好用 train_image_path=glob.glob('F://UNIVERSITY STUDY/AI/dataset/catdog/train/*/*.jpg') #加上*號是為了將當前目錄下的所有檔案,再加上一個星號是為了提取當前目錄下的所有jpg檔案 train_image_label=[int(p.split("\\")[1]=='cat') for p in train_image_path ] #經過一個列表推倒式就可以得到所有label
後面的第二行程式碼是一個列表推導式,我們觀察圖片資料集的名稱可以發現每一張圖片名稱上面都有cat或者dog。這樣我們就可以直接切分名稱上的dog和cat,然後進行索引用split切片出來的第二個字串,這個字串正好表示了圖片是貓還是狗,運用如上所示的列表推導式的話,如果圖片為cat那麼標籤就為1,如果為dog標籤就為0。下面我們來看看資料集當中的名稱是長啥樣的:
從中觀察可得,名稱都是用句號進行分割,圖片的格式為jpg。
我們現在已經得到了所有圖片的路徑,以及標籤了,現在就來到了最激動人心的部分,圖片的資料增強啦!!!
我們編寫一個專門用於圖片的預處理,包括用作圖片資料增強的函式:load_preprosess_image()。在這個函式當中我們對圖片進行預處理,之後再進行呼叫即可。這個函式的程式碼如下:
#現在我們的jpg檔案進行解碼,變成三維矩陣 def load_preprosess_image(path,label): #讀取路徑 image=tf.io.read_file(path) #解碼 image=tf.image.decode_jpeg(image,channels=3)#彩色影象為3個channel #將影象改變為同樣的大小,利用裁剪或者扭曲,這裡應用了扭曲 image=tf.image.resize(image,[360,360]) #隨機裁剪影象 image=tf.image.random_crop(image,[256,256,3]) #隨機上下翻轉影象 image=tf.image.random_flip_left_right(image) #隨機上下翻轉 image=tf.image.random_flip_up_down(image) #隨機改變影象的亮度 image=tf.image.random_brightness(image,0.5) #隨機改變對比度 image=tf.image.random_contrast(image,0,1) #改變資料型別 image=tf.cast(image,tf.float32) #將影象進行歸一化 image=image/255 #現在還需要對label進行處理,我們現在是列表[1,2,3], #需要變成[[1].[2].[3]] label=tf.reshape(label,[1]) return image,label
註釋我相信已經寫得很詳細了,裡面首先對資料集裡字尾為jpg格式的圖片進行解碼,將jpg格式轉化為一個個三維的矩陣,之後將圖片進行resize,resize之後進行剪裁為256*256,因為我之後要搭建的神經網路如果圖片的size是256*256的話,那麼這個網路準確率的表現將會變得更好一些。當然你也可以自己動手設計自己的神經網路,或使用Resnet,VGG16等等卷積神經網路對圖片進行分類,這些網路對圖片的尺寸都會有一定的要求,因此一定要對我們拿到的圖片進行預處理,並不是每一個數據集裡的圖片的大小是已經大小一致方便訓練的。然後我們建立datasets容器用於資料的裝載,同時製作每一個batch的資料,程式碼如下所示:
#現在開始建立dataset train_image_ds=tf.data.Dataset.from_tensor_slices((train_image_path,train_image_label)) AUTOTUNE=tf.data.experimental.AUTOTUNE#根據計算機效能進行運算速度的調整 train_image_ds=train_image_ds.map(load_preprosess_image,num_parallel_calls=AUTOTUNE) #後面的引數表示處理並行運算的CPU執行數量 #現在train_image_ds就讀取進來了,現在進行亂序和batchsize的規定 BATCH_SIZE=32 train_count=len(train_image_path) #現在設定batch和亂序 train_image_ds=train_image_ds.shuffle(train_count).batch(BATCH_SIZE) train_image_ds=train_image_ds.prefetch(AUTOTUNE)#預處理一部分處理,準備讀取 imags,labels=iter(train_image_ds).next()#放到生成器裡,單獨取出資料
現在搭建神經網路:
#現在開始建立模型 model=keras.Sequential([ tf.keras.layers.Conv2D(64,(3,3),input_shape=(256,256,3),activation='relu'), tf.keras.layers.MaxPooling2D(), tf.keras.layers.Conv2D(128,(3,3),activation='relu'), tf.keras.layers.MaxPooling2D(), tf.keras.layers.Conv2D(512,(3,3),activation='relu'), tf.keras.layers.GlobalAveragePooling2D(), tf.keras.layers.Dense(256,activation='relu'), tf.keras.layers.Dense(1) ])
開始自定義訓練,如果使用keras式的程式設計,我們直接model.compile,model.fit模型就訓練完了。但是這裡為了能夠看到訓練當中的模型的變化,我們使用自定義訓練,這也是Tensotflow2.0版本的優越性,既可以自定義訓練也可以使用更加方便的keras方式進行訓練,比Pytorch簡單了不少。自定義訓練的程式碼如下:
loss=tf.keras.losses.BinaryCrossentropy()#用這個來計算交叉熵 #定義優化器 optimizer=tf.keras.optimizers.Adam() epoch_loss_avg=tf.keras.metrics.Mean('train_loss')#定義平均損失 train_accuracy=tf.keras.metrics.Accuracy() def train_step(model,image,labels): with tf.GradientTape() as t: pred=model(image) #計算損失,比較標籤值和預測值的區別 loss_step=tf.keras.losses.BinaryCrossentropy(from_logits=True)(labels,pred) grads=t.gradient(loss_step,model.trainable_variables)#計算梯度 optimizer.apply_gradients(zip(grads,model.trainable_variables))#根據梯度進行優化 epoch_loss_avg(loss_step) train_accuracy(labels,tf.cast(pred>0,tf.int32)) train_loss_results=[] train_acc_resuls=[] num_epochs=30 for epoch in range(num_epochs): for imgs_,labels_ in train_image_ds: train_step(model,imgs_,labels_) print('.',end=' ')#每一個batch就列印一個點 print()#換行 #還可以把train——loss拿進來 train_loss_results.append(epoch_loss_avg.result()) train_acc_resuls.append(train_accuracy) print('Epoch :{}.loss: {:.3f},acc:{:.3f}'.format(epoch+1,epoch_loss_avg.result(),train_accuracy.result())) epoch_loss_avg.reset_states()#重置目前的loss,這樣就可以只用到了目前的平均loss train_accuracy.reset_states()
我還沒訓練完,就給大家看看前面兩個epoch的輸出吧!
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Epoch :1.loss: 0.695,acc:0.491 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Epoch :2.loss: 0.693,acc:0.500
這就是tensorflow當中的影象資料增強的使用方法,希望大家能夠學到些東西。