前言
深度學習近兩年來發展極為迅速,在計算能力大大提高的情況下,很多深度學習方向的思想都得以實現。但是,深度學習有一個令人頭疼的缺點,那就是需要大量樣本來進行訓練才能達到較好的泛化。
雖然我們有遷移學習可以適當減少我們需要的資料量,亦或者,我們可以通過資料增強的方式來翻倍我們的資料:
影像增強方法最直接了當,但是為什麼需要那麼多的資料呢,從直觀上理解很簡單,和人相似,學習到足夠多的東西才能舉一反三。當然這裡指的足夠多的東西是一個資料集中包含多種多樣的資料,包含不同情況不同形態的儘可能豐富的資料。
資料集的分佈曲線應該平滑
如果你只學習到一個場景,這個場景的資料很多,很容易造成神經網路“過度學習”,也就是所謂的學的太過了,舉一反三的本領太強。舉個例子,在目標檢測的一個簡單的任務中,我們要識別一個木板(board)和參照物(reference),下圖就是一個正確的識別結果:
但假如我們的資料集是這樣的(冰山一角):
發現什麼了?在資料集中我們的參照物往往都放置在木板的上面,只是換了擺放的位置和拍攝的角度,也就是所有的參照物下面都有一個木板。
那麼,如果我們的資料集都是這樣的,然後我們進行了訓練,隨後,取一張沒有木板的光有參照物的影像(這種影像在資料集中沒有或者很少)做測試:
看看發生了什麼?我們訓練的這個神經網路太會舉一反三了,認為只要有這個參照物,參照物下面的總是木板,於是就出現了這樣的情況。想要消除這種情況,資料集中木板和參照物的資料必須多元化,足夠多,不能光滿足一種情況下的資料量,需要多種情況多種條件下的資料。
深度學習聖經有句話:常用的隱示”先驗”是平滑先驗或區域性不變性,這個先驗表明我們學習的函式不應該在小區域內發生很大的變化。也就是說學習的函式應該是平滑的,不應該“偏科”。另外,只要學習的真實函式的峰值和谷值處有足夠多的樣本,那麼平滑性假設和相關的無引數學習演算法的效果都非常好,這也就是資料量豐富的優勢。
但對於人工智慧領域這些比較複雜的演算法任務來說,我們想要獲得更好的結果的直接途徑,除了改進演算法,那就是擴充你的資料集了。
關於神經網路的旋轉不變形
這裡有一個簡單的手寫資料識別網路的例子,LaNet5數字識別網路,一個擁有七層的簡單的神經網路,但卻包含了一整套神經網路的雛形。
LeNet手寫數字識別網路中有一個對影像變化的簡單測試,利用普通的數字影像訓練好資料後,拿經過變換後的資料嘗試進行識別,
以下分別是數字圖經過平移、縮放、旋轉後的識別情況,我們可以看到,在經過簡單的平移(數字簡單移動15px左右)、縮放和旋轉(左右各40度)神經網路仍然可以進行識別,但是如果變形的太過分,識別就失效了。
為什麼會出現這種情況,我們看下LaNet5的基本結構:
我們可以看到整個神經網路一共有7層(不包括輸入層),其中有兩個是池化層(pooling layer),LaNet-5使用的是平均池化方法,在網路中起到了降維的作用。
其實,為什麼神經網路也可以識別資料集中微小的變形呢?說白了是因為pooling
操作在降維的同時起到了一定的(很微小)旋轉不變性的作用。
比如在我們檢測一個物體的時候,神經網路的activation層
在這個物體的邊緣處發生了比較明顯的變化,當這個物體稍微旋轉一下,物體的邊緣稍稍移動時,由於max-pooling
的緣故,這個物體的邊緣依然會刺激到我們的activation層
,當然,max-pooling
的核大小是很有限的,對於一般的影像旋轉,平移,max-pooling
就無法發揮作用了。
quora
上也有說法,大致意思是過濾層(卷積層)有不變性,但是全連線層只會對特定的啟用層產生反應,也就是如果啟用層地點發生了變化,那麼全連線層就檢測不到了。
底下的評論中也有人進行了測試(訓練第一象限位置的數字影像,然後用第二象限的影像進行測試):
設計的網路層也很簡單(Keras):
class Model(object):
def __init__(self, num_classes, *input_shape):
self.model = None
self.input_shape = input_shape[0]
self.num_classes = num_classes
def create_model(self, use_fully_connected=True):
self.model = Sequential()
self.model.add(Conv2D(64, (3, 3), activation='relu', input_shape=self.input_shape))
self.model.add(Conv2D(32, (3, 3), activation='relu'))
self.model.add(MaxPooling2D((2, 2)))
if use_fully_connected:
self.model.add(Flatten())
self.model.add(Dense(128, activation='relu'))
self.model.add(Dense(self.num_classes, activation='softmax'))
else:
self.model.add(Conv2D(16, (3, 3), activation='relu'))
self.model.add(Flatten())
self.model.add(Dense(self.num_classes, activation='softmax'))
def compile(self):
self.model.compile(loss=keras.losses.categorical_crossentropy,
optimizer=keras.optimizers.Adadelta(),
metrics=['accuracy'])
def train(self, train_x, train_y, num_epochs, batch_size):
self.model.fit(train_x, train_y, verbose=1, epochs=num_epochs, batch_size=batch_size)
def test(self, test_x, test_y):
return self.model.evaluate(test_x, test_y)
複製程式碼
設計的模型中有max-pooling
層,最後的連線層是設定為全連線層或者直接去掉。就這樣訓練某個位置的數字影像然後用另一個位置的數字影像去測試,不論構建的神經網路有沒有全連線層,測試出來的結果都很“爛”:
準確率不足10%。
也就是說,max-pooling
的作用其實是有限的,我們不應該光靠這個“特性”來偷懶,然後減少資料的收集。
其他相關的研究與看法
類似的研究還是有很多的,在Multi-scale Orderless Pooling of Deep Convolutional Activation Features作者對影像分別進行旋轉、縮放、平移等各種變換操作,記錄多種變換程度下測試的準確性。
可以看到,大部分的曲線都是成”拱形”的或者下降趨勢,顯然還是original
的情況下效果最好。
還有很多另外的研究結果,大家可以自行搜尋閱讀。
關於Uber最新出現的論文CoordConv
Uber最新出了一片論文,在神經網路層的卷積操作上加了一層座標層,為的就是改進神經網路對目標位置不敏感的問題(平移不變性)。
呃,評價呢,褒貶不一,有興趣可以看看一下的連線:
卷積神經網路「失陷」,CoordConv 來填坑
要拯救CNN的CoordConv受嘲諷,翻譯個座標還用訓練?
結合大家觀點總結下這個論文:實現起來很簡單,幾十行程式碼就可以。但是實際上可能並不適合用於大型的任務,因為在論文中作者只是在小型任務上進行了嘗試,其他的任務還沒有人嘗試過,我也沒有,所以不好評價。
結論
說了這麼多,最簡單的提升神經網路不變性的方法就是擴充我們的資料集,也就是所謂的data augmentation,在kaggle上很多的trick都是通過在資料集上下功夫就可以提高演算法的分數了,這個方面還是很有學問的。
當然也有很多的在神經網路上直接實現對資料的增強,達到提高測試精度的效果,有興趣的可以閱讀下:
spatial transform network
AutoAugment: Learning Augmentation Policies from Data
參考
medium.com/nanonets/ho…
blog.keras.io/building-po…
www.zhihu.com/question/30…
Exploiting Cyclic Symmetry in Convolutional Neural Networks
yann.lecun.com/exdb/lenet/…
文章來源於OLDPAN部落格,歡迎來訪:Oldpan部落格
歡迎關注Oldpan部落格公眾號,持續醞釀深度學習質量文: