選自 Medium
機器之心編譯
參與:蔣思源、晏奇、黃小天
眾所周知,
圖像就是圖元值的集合,
而這個觀點可以説明電腦科學家和研究者們構建一個和人類大腦相似並能實現特殊功能的神經網路。
有時候,
這種神經網路甚至能超過人類的准准度。
上圖是一個非常好的案例, 其說明了圖像是由圖元值表徵的這一特徵。 這些小的區塊形成了最基本的卷積神經網路。
卷積神經網路與一般的神經網路有非常高的相似性, 它們都是由可學習的權重和偏置項還有神經元組成。 每個神經元接受一些輸入, 然後執行點積(標量), 隨後可選擇性地執行非線性分類。 整個網路仍然表示單可微分(single differentiable)的評估函數(score function), 整個網路從一端輸入原始圖像圖元, 另一端輸出類別的概率。 該網路仍然具有損失函數, 因為損失函數可以在最後(全連接)層計算相對概率(如支援向量機/Softmax),
卷積是如何進行的。 每一個圖元由周圍圖元的加權和所替代, 神經網路會學習這些權重。
最近, 隨著資料量和計算力的大大提升, ConvNets 在人臉識別、物體識別、交通標誌、機器人和自動駕駛等方向表現得十分出色。
下圖顯展示了在 ConvNet 中四種主要的操作:
1. 卷積(Convolution)
2. 非線性(如 ReLU)
3. 池化或子採樣(Pooling or Sub Sampling)
4. 分類(Classification)
一張汽車的圖片經過 ConNet, 並在全連接層輸出類別為汽車
全卷積網路(All Convolution Network)
大多數用於目標識別的現代卷積神經網路(CNN)都是運用同一原理構建:交替卷積和最大池化層, 並伴隨著少量全連接層。 以前就有一篇論文提出, 最大池化(max-pooling)可被一個帶有增加步長的卷積層輕易替換, 而沒有在圖像識別基準上出現精確度的損失。 論文中提到的另一個趣事也是用一個全域平均池化(Global Average pooling)替換全連接層。
如需詳細瞭解全卷積網路,
去掉全連接層也許不是一件讓人很驚訝的事, 因為長久以來人們本來就不怎麼使用它。 不久前 Yann LeCun 甚至在 Facebook 上說, 我從一開始就沒用過全連接層。
這不無道理, 全連接層與卷積層的唯一區別就是後者的神經元只與輸入中的局部域相連, 並且卷積空間之中的很多神經元共用參數。 然而, 全連接層和卷積層中的神經元依然計算點積, 它們的函數形式是相同的。 因此, 結果證明全連接層和卷積層之間的轉換是可能的, 有時甚至可用卷積層替換全連接層。
正如上文提到的, 下一步是從網路中去除空間池化運算。 現在這也許會引起一些疑惑。 讓我們詳細看一下這個概念。
空間池化(spatial Pooling), 也稱為子採樣(subsampling)或下採樣(downsampling), 其減少了每一個特徵映射的維度,但是保留了最重要的資訊。
讓我們以最大池化為例。在這種情況下,我們定義了一個空間視窗(spatial window),並從其中的特徵映射獲取最大元素,現在記住圖 2(卷積是如何工作的)。直觀來講帶有更大步長的卷積層可作為子採樣和下採樣層,從而使輸入表徵更小更可控。同樣它也可減少網路中的參數數量和計算,進而控制過擬合的發生。
為了減少表徵尺寸,在卷積層中使用更大步長有時成了很多案例中的最佳選擇。在訓練好的生成模型,如變分自動編碼器(VAE)或生成對抗網路(GAN)中,放棄池化層也是十分重要的。此外,未來的神經網路架構可能會具有非常少的或根本沒有池化層。
鑒於所有以上提到的小技巧或微調比較重要,我們在 Github 上發佈了使用 Keras 模型實現全卷積神經網路:https://github.com/MateLabs/All-Conv-Keras
導入庫(library)和依賴項(dependency)
在多 GPU 上訓練
對於模型的多 GPU 實現,我們有一個可將訓練資料分配給可用 GPU 的自訂函數。
計算在 GPU 上完成,輸出數據傳給 CPU 以完成模型。
def make_parallel(model, gpu_count):#Place a copy of the model on each GPU, each getting a slice of the batch for i in range(gpu_count):#Slice each input into a piece for processing on this GPU for x in model.inputs: if not isinstance(outputs, list):#Save all the outputs for merging back together later for l in range(len(outputs)):# merge outputs on CPUwith tf.device('/cpu:0'):配置批量大小(batch size)、類(class)數量以及反覆運算次數
由於我們用的是擁有 10 個類(不同物件的種類)的 CIFAR 10 資料集,所以類的數量是 10,批量大小(batch size)等於 32。反覆運算次數由你自己的可用時間和設備計算能力決定。在這個例子中我們反覆運算 1000 次。
圖像尺寸是 32*32,顏色通道 channels=3(rgb)
batch_size = 32把資料集切分成「訓練集」、「測試集」和「驗證集」三部分
(X_train, y_train), (X_test, y_test) = cifar10.load_data()Y_train = np_utils.to_categorical(y_train, nb_classes)構建模型
model = Sequential()model.add(Convolution2D(96, 3, 3, border_mode = 'same', input_shape=(3, 32, 32)))#The next layer is the substitute of max pooling, we are taking a strided convolution layer to reduce the dimensionality of the image.model.add(Convolution2D(96, 3, 3, border_mode='same', subsample = (2,2)))# The next layer is the substitute of max pooling, we are taking a strided convolution layer to reduce the dimensionality of the image.model.add(Convolution2D(192, 3, 3,border_mode='same', subsample = (2,2)))model.add(GlobalAveragePooling2D())列印模型。這會給你一個模型的一覽,它非常有助於視覺化模型的維度和參數數量
print (model.summary())資料擴充
datagen = ImageDataGenerator(samplewise_center=False, # set each sample mean to 0featurewise_std_normalization=False, # divide inputs by std of the datasetsamplewise_std_normalization=False, # divide each input by its stdzca_whitening=False, # apply ZCA whiteningrotation_range=0, # randomly rotate images in the range (degrees, 0 to 180)width_shift_range=0.1, # randomly shift images horizontally (fraction of total width)height_shift_range=0.1, # randomly shift images vertically (fraction of total height)horizontal_flip=False, # randomly flip imagesvertical_flip=False) # randomly flip imagesdatagen.fit(X_train)在你的模型中保存最佳權重並添加檢查點
filepath="weights.{epoch:02d}-{val_loss:.2f}.hdf5"checkpoint = ModelCheckpoint(filepath, monitor='val_acc', verbose=1, save_best_only=True, save_weights_only=False, mode='max')callbacks_list = [checkpoint]# Fit the model on the batches generated by datagen.flow().history_callback = model.fit_generator(datagen.flow(X_train, Y_train, batch_size=batch_size), samples_per_epoch=X_train.shape[0], nb_epoch=nb_epoch, validation_data=(X_test, Y_test), callbacks=callbacks_list, verbose=0)最後,拿到訓練過程的日誌並保存你的模型
pandas.DataFrame(history_callback.history).to_csv("history.csv")以上的模型在前 350 次反覆運算後很容易就實現超過 90% 的精確度。如果你想要增加精確度,那你可以用計算時間為代價,並嘗試擴充更大的資料。
其減少了每一個特徵映射的維度,但是保留了最重要的資訊。
讓我們以最大池化為例。在這種情況下,我們定義了一個空間視窗(spatial window),並從其中的特徵映射獲取最大元素,現在記住圖 2(卷積是如何工作的)。直觀來講帶有更大步長的卷積層可作為子採樣和下採樣層,從而使輸入表徵更小更可控。同樣它也可減少網路中的參數數量和計算,進而控制過擬合的發生。
為了減少表徵尺寸,在卷積層中使用更大步長有時成了很多案例中的最佳選擇。在訓練好的生成模型,如變分自動編碼器(VAE)或生成對抗網路(GAN)中,放棄池化層也是十分重要的。此外,未來的神經網路架構可能會具有非常少的或根本沒有池化層。
鑒於所有以上提到的小技巧或微調比較重要,我們在 Github 上發佈了使用 Keras 模型實現全卷積神經網路:https://github.com/MateLabs/All-Conv-Keras
導入庫(library)和依賴項(dependency)
在多 GPU 上訓練
對於模型的多 GPU 實現,我們有一個可將訓練資料分配給可用 GPU 的自訂函數。
計算在 GPU 上完成,輸出數據傳給 CPU 以完成模型。
def make_parallel(model, gpu_count):#Place a copy of the model on each GPU, each getting a slice of the batch for i in range(gpu_count):#Slice each input into a piece for processing on this GPU for x in model.inputs: if not isinstance(outputs, list):#Save all the outputs for merging back together later for l in range(len(outputs)):# merge outputs on CPUwith tf.device('/cpu:0'):配置批量大小(batch size)、類(class)數量以及反覆運算次數
由於我們用的是擁有 10 個類(不同物件的種類)的 CIFAR 10 資料集,所以類的數量是 10,批量大小(batch size)等於 32。反覆運算次數由你自己的可用時間和設備計算能力決定。在這個例子中我們反覆運算 1000 次。
圖像尺寸是 32*32,顏色通道 channels=3(rgb)
batch_size = 32把資料集切分成「訓練集」、「測試集」和「驗證集」三部分
(X_train, y_train), (X_test, y_test) = cifar10.load_data()Y_train = np_utils.to_categorical(y_train, nb_classes)構建模型
model = Sequential()model.add(Convolution2D(96, 3, 3, border_mode = 'same', input_shape=(3, 32, 32)))#The next layer is the substitute of max pooling, we are taking a strided convolution layer to reduce the dimensionality of the image.model.add(Convolution2D(96, 3, 3, border_mode='same', subsample = (2,2)))# The next layer is the substitute of max pooling, we are taking a strided convolution layer to reduce the dimensionality of the image.model.add(Convolution2D(192, 3, 3,border_mode='same', subsample = (2,2)))model.add(GlobalAveragePooling2D())列印模型。這會給你一個模型的一覽,它非常有助於視覺化模型的維度和參數數量
print (model.summary())資料擴充
datagen = ImageDataGenerator(samplewise_center=False, # set each sample mean to 0featurewise_std_normalization=False, # divide inputs by std of the datasetsamplewise_std_normalization=False, # divide each input by its stdzca_whitening=False, # apply ZCA whiteningrotation_range=0, # randomly rotate images in the range (degrees, 0 to 180)width_shift_range=0.1, # randomly shift images horizontally (fraction of total width)height_shift_range=0.1, # randomly shift images vertically (fraction of total height)horizontal_flip=False, # randomly flip imagesvertical_flip=False) # randomly flip imagesdatagen.fit(X_train)在你的模型中保存最佳權重並添加檢查點
filepath="weights.{epoch:02d}-{val_loss:.2f}.hdf5"checkpoint = ModelCheckpoint(filepath, monitor='val_acc', verbose=1, save_best_only=True, save_weights_only=False, mode='max')callbacks_list = [checkpoint]# Fit the model on the batches generated by datagen.flow().history_callback = model.fit_generator(datagen.flow(X_train, Y_train, batch_size=batch_size), samples_per_epoch=X_train.shape[0], nb_epoch=nb_epoch, validation_data=(X_test, Y_test), callbacks=callbacks_list, verbose=0)最後,拿到訓練過程的日誌並保存你的模型
pandas.DataFrame(history_callback.history).to_csv("history.csv")以上的模型在前 350 次反覆運算後很容易就實現超過 90% 的精確度。如果你想要增加精確度,那你可以用計算時間為代價,並嘗試擴充更大的資料。