FancyKeras-數據的輸入(花式)

上一期說了官網建議的一些數據輸入方式,除了自己能逐個讀取圖片數據進行輸入之外,還能用官方的generator來進行數據的自動生成。這期的花式數據輸入,就教大家手動寫一個自定義的生成器或迭代器出來,實現多線程多進程訓練哦!這樣遇到一些需要特殊操作的模型,比如faster-rcnn或者RPN,這類輸入不能直接使用官方的API,就要自己寫一個出來了。

一、生成器-Generator

如果熟悉python的generator都知道,生成器是能夠被無限循環、不斷yield數據出來,不熟悉的同學可以稍微看看這裡:python生成器-廖雪峰。我們先來構建一個生成器:

import globnimport mathnfrom PIL import Imagenimport numpy as npnndef read_img(path, target_size):n try:n img = Image.open(path).convert("RGB")n img_rs = img.resize(target_size)n except Exception as e:n print(e)n else:n x = np.expand_dims(np.array(img_rs), axis=0)n return xnndef my_gen(path, batch_size, target_size):n img_list = glob.glob(path + *.jpg) # 獲取path裡面所有圖片的路徑n steps = math.ceil(len(img_list) / batch_size) # 確定每輪有多少個batchn print("Found %s images."%len(img_list))n while True:n for i in range(steps):n batch_list = img_list[i * batch_size : i * batch_size + batch_size]n x = [read_img(file, target_size) for file in batch_list]n batch_x = np.concatenate([array for array in x])n y = np.zeros((batch_size, 1000)) # 你可以讀取你寫好的標籤,這裡為了演示簡潔就全設成0n yield batch_x, y # 把製作好的x, y生成出來n

然後我們導入keras自帶的ResNet模型,使用fit_generator進行訓練。

# 代碼緊接上面nfrom keras.applications import ResNet50nfrom keras import optimizersnnpath = /home/ubuntu/dataset/yoins_all/img/nmodel = ResNet50()nmodel.compile(optimizer=optimizers.Adam(1e-4), loss=categorical_crossentropy)nnbatch_size = 64nsteps = math.ceil(len(glob.glob(path + *.jpg)) / batch_size)ntarget_size = (224, 224)ndata_gen = my_gen(path, batch_size, target_size) # 使用上面寫好的generatornnnmodel.fit_generator(data_gen, steps_per_epoch=steps, epochs=10, verbose=1, n use_multiprocessing=False, workers=2)n

二、Sequence-序列數據

細心的讀者可能會發現,上面使用fit_generator的其中一個參數「use_multiprocessing」設置為False,也就是不使用多進程來輸入數據進行訓練。為什麼不能用多進程?keras給出的說明如下:

using a generator with `use_multiprocessing=True` and multiple workers may duplicate your data. Please consider using the`keras.utils.Sequence class.

意思是如果你使用generator的時候,如果設置多進程輸入,代碼就會把你的數據複製幾份,分給不同的workers進行輸入,這顯然不是我們希望的,我們希望一份數據直接平均分給多個workers幫忙輸入,這樣才是最快的。而Sequence數據類能完美解決這個問題。

大家可以看看Sequence的官方說明:Utils - Keras Documentation。我們需要把數據生成的方式寫成一個迭代器的形式,定義一個Sequence類,類內要包含「__init__」、「__len__」和「__getitem__」3個方法。下面直接給出例子:

import cv2nimport globnfrom keras.utils import Sequencenimport mathnimport numpy as npnnclass SequenceData(Sequence):n def __init__(self, path, batch_size, target_size):n # 初始化所需的參數n self.path = pathn self.batch_size = batch_sizen self.target_size = target_sizen self.x_filenames = glob.glob(self.path + *.png)n self.x_filenames.sort(key=lambda x: int(x.split(/)[-1][:-4]))nn def __len__(self):n # 讓代碼知道這個序列的長度n num_imgs = len(glob.glob(self.path + *.png))n return math.ceil(num_imgs / self.batch_size)nn def __getitem__(self, idx):n # 迭代器部分n batch_x = self.x_filenames[idx * self.batch_size: (idx + 1) * self.batch_size]n x_arrays = np.array([self.read_img(filename) for filename in batch_x]) # 讀取一批圖片n batch_y = np.zeros((self.batch_size, 1000)) # 為演示簡潔全部假設y為0nn return x_arrays, batch_ynn def read_img(self, x):n try:n img = cv2.imread(x) # 這裡用cv2是因為讀取圖片比pillow快n img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB) #opencv讀取通道順序為BGR,所以要轉換n img = cv2.resize(img, self.target_size)n except Exception as e:n print(e)n else:n return imgn

接下來的訓練部分跟generator部分的基本上相同了,不過現在是可以使用多進程了,當你數據量大的時候,多進程能夠讓你的GPU保持98%以上的使用率,這樣你的訓練就非常高效了!(GPU如果在等待CPU喂數據,訓練時間就變長了)

# 代碼緊接上面nfrom keras.applications import ResNet50nfrom keras import optimizersnnpath = /home/ubuntu/huzhihao/qinquan/yoins/dataset/1121_4/a/anchor/nmodel = ResNet50()nmodel.compile(optimizer=optimizers.Adam(1e-4), loss=categorical_crossentropy)nnbatch_size = 64nsteps = math.ceil(len(glob.glob(path + *.png)) / batch_size)ntarget_size = (224, 224)nsequence_data = SequenceData(path, batch_size, target_size)nnnmodel.fit_generator(sequence_data, steps_per_epoch=steps, epochs=10, verbose=1, n use_multiprocessing=True, workers=2)n

三、彩蛋

寫這篇文章之前,我測試了一下,同一個Sequence數據(1000張圖片),同一個ResNet模型,如果用多進程和單進程多線程分別來比較速度,結果會是怎樣?

可以看到,使用多進程和單進程多線程在這個數據量的時候並沒有很明顯的速度差別,估計是數據量太少的原因,大家可以拿imagenet級別的數據量來試試。另外,測試到結果發現workers數量等於2或3的時候速度是最快的,因為太多的workers增加了通訊時間,反而減慢了速度。本節的代碼可以在我github找到,地址是:JustinhoCHN/fancy_keras

如果你覺得有用,請點贊或分享給您的朋友!

推薦閱讀:

時間序列與回歸預測(一)
自己實現黑白圖片自動上色AI(一)
keras中embedding層是如何將一個正整數(下標)轉化為具有固定大小的向量的?
Keras使用VGG16訓練圖片分類?

TAG:深度学习DeepLearning | Keras | 计算机视觉 |