1. 程式人生 > 實用技巧 >Tensorflow實現影象資料增強(Data Augmentation)

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當中的影象資料增強的使用方法,希望大家能夠學到些東西。