在使用kears訓練model的時候,一般會將所有的訓練數據加載到內存中,然后喂給網絡,但當內存有限,且數據量過大時,此方法則不再可用。此博客,將介紹如何在多核(多線程)上實時的生成數據,並立即的送入到模型當中訓練。 本篇文章由圓柱模板博主發布。
先看一下還未改進的版本:
import numpy as np from keras.models import Sequential #載入全部的數據!! X, y = np.load('some_training_set_with_labels.npy') #設計模型 model = Sequential() [...] #網絡結構 model.compile() # 在數據集上進行模型訓練 model.fit(x=X, y=y)
下面的結構將改變一次性載入全部數據的情況。接下來將介紹如何一步一步的構造數據生成器,此數據生成器也可應用在你自己的項目當中;復制下來,並根據自己的需求填充空白處。
在構建之前先定義統一幾個變量,並介紹幾個小tips,對我們處理大的數據量很重要。
ID type為string,代表數據集中的某個樣本。
調整以下結構,編譯處理樣本和他們的label:
1.新建一個詞典名叫 partition :
partition[‘train’] 為訓練集的ID,type為list partition[‘validation’] 為驗證集的ID,type為list
2.新建一個詞典名叫 * labels * ,根據ID可找到數據集中的樣本,同樣可通過labels[ID]找到樣本標簽。
舉個例子:
假設訓練集包含三個樣本,ID分別為id-1,id-2和id-3,相應的label分別為0,1,2。驗證集包含樣本ID id-4,標簽為 1。此時兩個詞典partition和 labels分別如下:
partition {'train': ['id-1', 'id-2', 'id-3'], 'validation': ['id-4']}
labels {'id-1': 0, 'id-2': 1, 'id-3': 2, 'id-4': 1}
data/ 中為數據集文件。
數據生成器(data generator)
接下來將介紹如何構建數據生成器 DataGenerator ,DataGenerator將實時的對訓練模型feed數據。
接下來,將先初始化類。我們使此類繼承自keras.utils.Sequence,這樣我們可以使用多線程。
def __init__(self, list_IDs, labels, batch_size=32, dim=(32,32,32), n_channels=1, n_classes=10, shuffle=True): 'Initialization' self.dim = dim self.batch_size = batch_size self.labels = labels self.list_IDs = list_IDs self.n_channels = n_channels self.n_classes = n_classes self.shuffle = shuffle self.on_epoch_end()
我們給了一些與數據相關的參數 dim,channels,classes,batch size ;方法 on_epoch_end 在一個epoch開始時或者結束時觸發,shuffle決定是否在數據生成時要對數據進行打亂。
def on_epoch_end(self): 'Updates indexes after each epoch' self.indexes = np.arange(len(self.list_IDs)) if self.shuffle == True: np.random.shuffle(self.indexes)
另一個數據生成核心的方法__data_generation 是生成批數據。
def __data_generation(self, list_IDs_temp): 'Generates data containing batch_size samples' # X : (n_samples, *dim, n_channels) # Initialization X = np.empty((self.batch_size, *self.dim, self.n_channels)) y = np.empty((self.batch_size), dtype=int) # Generate data for i, ID in enumerate(list_IDs_temp): # Store sample X[i,] = np.load('data/' + ID + '.npy') # Store class y[i] = self.labels[ID] return X, keras.utils.to_categorical(y, num_classes=self.n_classes)
在數據生成期間,代碼讀取包含各個樣本ID的代碼ID.py.因為我們的代碼是可以應用多線程的,所以可以采用更為復雜的操作,不用擔心數據生成成為總體效率的瓶頸。
另外,我們使用Keras的方法keras.utils.to_categorical對label進行2值化
(比如,對6分類而言,第三個label則相應的變成 to [0 0 1 0 0 0]) 。
def __len__(self): 'Denotes the number of batches per epoch' return int(np.floor(len(self.list_IDs) / self.batch_size))
現在,當相應的index的batch被選到,則生成器執行_getitem_方法來生成它。
def __getitem__(self, index): 'Generate one batch of data' # Generate indexes of the batch indexes = self.indexes[index*self.batch_size:(index+1)*self.batch_size] # Find list of IDs list_IDs_temp = [self.list_IDs[k] for k in indexes] # Generate data X, y = self.__data_generation(list_IDs_temp) return X, y