1. 程式人生 > >[CS231n-assignment2] Python從零實現的CNN在CIFAR-10上的實驗報告

[CS231n-assignment2] Python從零實現的CNN在CIFAR-10上的實驗報告

1. CS231n課程

CS231n是斯坦福大學李飛飛團隊的一門關於卷積神經網路CNN的課程,這個課程從KNN和線性分類器講到普通的神經網路,再將到卷積神經網路的實現,以及一些實用的技術如Dropout、Batch Normalization等,整個課程下來後會對CNN有個比較全面的瞭解。這個課程通俗易懂,是入門深度學習的良心課程,不僅圖文結合闡述了網路正向和反向傳播的過程,還會介紹一些在實用中的應用的trick如訓練過程的梯度檢查、如何判斷學習率大小、如何判斷過擬合等等,以及相應的解決方法

1.2 Assignment 2 使用

配套CS231n課程的有兩次作業,作業2主要有四部分內容:全連線層的實現、Batch Normalization、Dropout和卷積層的實現,CS231n有作業的初始模板,可以

在此下載,模板裡已經填好了實現這些網路的部分程式碼,學習者只需要在特定的方法中填寫自己的程式碼即可,例如下面全連線層的forward方法,只需理解了課程筆記後按照提示在TODO中填寫程式碼,填寫完後,模板中還提供了方法來檢查你程式碼的正確性:執行IPython notebook,這個notebook會引導你如何填寫程式碼並測試程式碼,最後會在CIFAR-10資料集上測試你的CNN的準確性。

def affine_forward(x, w, b):
  """
  Computes the forward pass for an affine (fully-connected) layer.

  The input x has shape (N, d_1, ..., d_k) and contains a minibatch of N
  examples, where each example x[i] has shape (d_1, ..., d_k). We will
  reshape each input into a vector of dimension D = d_1 * ... * d_k, and
  then transform it to an output vector of dimension M.

  Inputs:
  - x: A numpy array containing input data, of shape (N, d_1, ..., d_k)
  - w: A numpy array of weights, of shape (D, M)
  - b: A numpy array of biases, of shape (M,)

  Returns a tuple of:
  - out: output, of shape (N, M)
  - cache: (x, w, b)
  """
out = None ############################################################################# # TODO: Implement the affine forward pass. Store the result in out. You # # will need to reshape the input into rows. # #############################################
################################ pass ############################################################################# # END OF YOUR CODE # ############################################################################# cache = (x, w, b) return out, cache

本文對作業中填寫的程式碼參照了CS231n (winter 2016) : Assignment2 - 簡書,完整的程式碼位於Github,執行前請參考CS231n的Readme,Readme中詳細介紹了環境的搭建、資料集的下載,如何使用IPython,以及相應的編譯過程(如果要用Cpython加速訓練過程的話)

請先下載初始模板,搭建Python環境,下載資料集,執行IPython,然後參考notebook中的提示,填寫相應程式碼。由於各個部分的程式碼在簡書和Github中已有,不再累述。下面主要說一些在CIFAR-10資料庫上通過改變CNN網路結構引數提高準確率的心得

2. 在CIFAR-10上的表現

2.1 簡單兩層神經網路

當完成Part 1:全連線神經網路後,notebook會提示在CIFAR-10上跑一下,此時若程式碼實現正常,在測試集上基本上可以達到50%-55%的準確率,網路結構為簡單兩層,具體請見Github中的classifiers/fc_net.py,下面總結實戰中的一些小的trick:

Trick 1: 如何測試我們實現的模型有效性:小資料集過擬合: 如何判斷我們的方法到能不能work,根據notebook裡的提示,可以在一個很小的資料集上跑幾個epoch,觀察我們的模型是否能夠對訓練集很好的過擬合,而測試集準確率很低,具體是:在訓練集這個100張圖片裡,是否能夠實現99%-100%準確率的判斷,而在測試集中另外的100張圖片有比較低的準確率(10%左右),這裡的模型指我們手動實現的演算法

Trick 2: 如何判斷對層的實現是否正確:梯度檢查: 在將我們的演算法應用到正式的資料集上之前,需要對實現的層進行解析梯度和數值梯度的比較,具體方法在notebook裡,數值梯度是微調1e-6引數獲得的差值除以改變數1e-6得到的,而解析梯度是我們實現程式碼反向傳播的輸出(因為我們對層的更新都是根據求導法則來的,所以是梯度的解析值),將這兩個梯度值比較,觀察相對誤差,能判斷對這個層的實現有沒有問題,相對誤差在1e-7或者更小是很好的結果,若相對誤差達到了1e-2,通常你的實現就有問題。但是,網路越深,相對誤差會累計,在10層的網路裡若有1e-2的相對誤差,那也是可以的。除了梯度檢查,notebook中提供了參考值用來檢查你實現的權值更新準則如SGD+Momentum、RMSProp、Adam

Trick 3: 如何判斷權值和偏移量引數的初始化是否正確: 初始化對訓練過程也是非常重要的,所以我們需要對訓練過程進行檢查,當正則強度為0時,對於CIFAR-10的softmax輸出的分類器(初始權值w為0.01量級的隨機數,初始偏移b為0),一般初始的loss function的值為2.302,這是因為初始時分類器對每一類的概率期望為0.1(共有10類),因此softmax損失函式值為對於正確分類概率的負對數:-ln(0.1)=2.302

Trick 4: 如何判斷學習率是否合適: 在訓練過程中,需要對loss值進行實時地列印,可以判斷當前訓練的狀態:高的學習率高會使損失值下降很快,然後停止在一個比較高的位置(相對最優),這是因為引數每次更新過大,導致在最優點附近震盪,但始終無法達到最優點,而過高的學習率會直接使損失值遞增。過於低的學習率會導致損失值下降很慢,訓練過程太長,引用筆記中的一張圖來理解:

這裡寫圖片描述

Trick 5: 如何判斷模型的過擬合程度: 在訓練過程中,我們還需要對每個epoch中的訓練集和測試集的準確率進行列印,能夠確定模型是否過擬合或者欠擬合,若訓練集準確率一直大幅度高於驗證集,說明此時模型過擬合,對訓練集有過好的分類能力導致無法在驗證集上進行比較好的分類,解決的方法可以增大正則化強度,如增大L2正則懲罰,增加dropout的隨機失活率等。如果訓練集一直小幅度低於驗證集,說明此時稍微過擬合,而如果訓練集和驗證集的準確率不相上下,說明此時模型有點欠擬合,沒有很好地學習到特徵,此時可以調整模型引數如層的深度等,引用筆記中的一張圖說明:

這裡寫圖片描述

Trick 6: 如何判斷訓練中出現的梯度消散問題: 我們知道,當網路的層數過於深以後,會出現梯度消散的情況,也就是回傳到前幾層的梯度值很小,導致前面幾層的引數無法更新。對此,我們可以列印前幾層網路權值引數w的更新比例,經驗結論是這個更新比例在1e-3比較比較好,若這個值太大,說明學習率太高;若這個值很小到1e-7,說明引數w基本上不會變,發生了梯度消散,解決方法為:1)使用Batch Normalization歸一化每層之間的輸出,2)啟用函式改用線性ReLU,3)還有可能是學習率太低,4)減少網路層數

Trick 7: 如何判斷訓練過程是否穩定和有效: 若資料為影象資料,那麼可以把第一層的權重進行視覺化,觀察模型是否學習到了比較的好的特徵,notebook裡內建了相關視覺化的方法,若特徵圖中顏色雜亂無規律且充滿噪音,說明訓練過程未收斂(學習率太高)或者正則化懲罰不夠,引用筆記中的圖來解釋,下圖中的右圖為比較好的特徵,平滑而且種類繁多,說明訓練過程有效且穩定

這裡寫圖片描述

Trick 8: 如何進行有效的資料預處理: 在實際應用中CNN比較多的是減均值法和歸一化,其他的處理方法為PCA和白化(Whitening):PCA能消除資料的相關性,使資料的分佈在基準值上;白化則可看成是把資料在各個特徵方向上進行拉伸壓縮變化,使之服從均值為零的高斯分佈,具體參考[知乎](https://zhuanlan.zhihu.com/p/21560667?refer=intelligentunit

2.2 卷積神經網路

2.2.1 三層簡單CNN

在寫Part 2:卷積神經網路之前,會先完成Dropout和Batch Normalization這兩部分。在完成了Part 2後,notebook會用classifiers/cnn.py 中一個三層的簡單的卷積神經網路來跑CIFAR-10,最終的表現在測試集上達到55-59%這樣一個結果,比普通的神經網路高了幾個百分點,這個網路結構如下:

這裡寫圖片描述

2.2.2 稍微複雜點的CNN

基於這個naive的CNN,我再加入一個卷積層和一個全連線層,去掉了Pool層,因為size為2的Pool層會使影象壓縮至四分之一,而FICAR的影象大小為32*32,經過一個Pool後變成了16*16,資訊損失太大,所以去掉Pool,考慮使用卷積層的stride=2或者3來壓縮影象:

 INPUT --> [CONV --> RELU]*2 --> [FC --> RELU]*2 --> FC/OUT

此時在測試集上的精度大概能達到60-65%的程度,然後各種修改卷積層的padding,stride,filter_num引數,大概能提高到67%左右,而訓練集精度基本上可以達到90%,說明模型有點過擬合,下一步考慮使用Dropout。

2.2.3 多層小卷積層CNN+Dropout

2.上述網路卷積層的過濾器尺寸始終未6或者7,相對於32*32的影象來說確實是一個比較大的尺寸,然而多層的小size的卷積層效果要比大的size的卷積層好:

現在,我們以3個3x3的卷積層和1個7x7的卷積層為例,加以對比說明。從下圖可以看出,這兩種方法最終得到的activation map大小是一致的,但3個3x3的卷積層明顯更好:
1)、3層的非線性組合要比1層線性組合提取出的特徵具備更高的表達能力;
2)、3層小size的卷積層的引數數量要少,3x3x3<7x7;
3)、同樣的,為了便於反向傳播時的梯度計算,我們需要保留很多中間梯度,3層小size的卷積層需要保留的中間梯度更少。
這裡寫圖片描述
來自簡書

因此,我使用小的卷積層,兩個卷積層的過濾器都為filter_size=(3,3),使用stride=2來壓縮影象;同時在輸出層前一層加入了Dropout防止過擬合,隨機失活率p=0.8,網路結構如下:

 INPUT --> [CONV --> RELU]*2 --> FC --> RELU --> FC --> RELU --> DropOut --> FC/OUT

此時在測試集上的精度大概能達到65-70%左右的程度,訓練集精度在很多次epoch後還是維持在80%以上,這時的調整包括再次增加一個全連線層,但是精度還是不能很好提高,遇到了瓶頸,這時可以考慮加入Batch Normalization了。

2.2.4 分析大殺器ResNets

然後我去CIFAR資料集查好解決方案,看到了2015年最好的網路結構ResNets,能達到93%+的精度,這個網路最深能達到110層,而且在20層的時候就能達到91%了。我們考慮20層簡單的情況:

INPUT --> [CONV --> BatchNorm --> RELU]*19 --> POOL --> FC/OUT

這個網路的特點為:每一卷積層都使用小的過濾器filter_size=(3,3),分階段調整stride步長值:分別在第8層和第13層調整stride=2來壓縮影象,其他卷積層的步長stride都1,而且每層卷積層後都會跟一個BatchNormal防止梯度彌散。通過分析這個網路,可以看出:

  1. 其實在stride>1的時候,stride跟pool一樣,只保留了上個網路部分的資訊,能起到壓縮影象內容。

  2. zero-padding的作用不只是起到一個折中的方案:填補空白區域使卷積過程能夠順利進行,方便從過濾器能從初始位置以步長為單位可以剛好滑倒末尾位置,它的另外一個作用是為了保持影象的尺寸不變,根據公式output_size=(input_size+2*padding-filter_size)/stride+1,我們以input_size=32, padding=1, filter_size=3, stride=1 來計算卷積後的影象大小output_size=(32-2*1-3)/1+1=32,可以看出輸入影象和卷積後的影象大小並沒有改變,這也就是ResNets為什麼能在32*32這麼小的影象上卷積100多次的原因了,而且只靠stride=2來壓縮影象兩次

對於ImageNet這樣256*256影象的資料來說,怎麼設定stride、padding、filter_size可能沒有這麼講究,但是對於CIFAR小影象來說,如何巧妙地設計這些引數就有很大用處了,是深層網路必須的考慮的事

2.2.5 最後的掙扎

根據上面的分析,我最後掙扎了一下,因為機器不可能跑這麼多層網路,還是採用了經典的CNN網路模型:

INPUT --> [CONV --> RELU --> CONV --> RELU --> POOL]*2 --> [FC --> RELU] --> DROPOUT --> [FC --> RELU] --> FC/OUT

四個卷積層三個全連線,每個卷積層fliter_size=(3,3), stride=1,padding=1,重新使用pool壓縮影象,以下是我的最終引數,可以在classfiers/cnn_custom 裡檢視:

weight_scale=0.01, L2 regularization=0.0005, dropout=0.8, batch_size=100, optimizer=adam, learning_rate=0.001

INPUT: input_dim=(3,32,32)
CONV1: filters=64, filter_size=(3,3),stride=1, pad=1
CONV2: filters=64, filter_size=(3,3), stride=1, pad=1
POOL2: pool_height= 2, pool_width= 2, stride= 2
CONV3: filters=64, filter_size=(3,3),stride=1, pad=1
CONV4: filters=64, filter_size=(3,3), stride=1, pad=1
POOL4: pool_height= 2, pool_width= 2, stride= 2
FC5: 512 neurons
FC6: 64 neurons
FC7: 10 outputs

最終結果能拿到77%左右的測試集精度,90%+的訓練集精度,未來的提高點在於每層加入Batch Normalization,因為BN需要額外的計算量還挺大的(自己實現的話),所以沒有加上。最好的方案是跟ResNets一樣,用小卷積層並擴充套件深度至20層以上。

這裡寫圖片描述

下面給出我第一層卷積層權值w的視覺化,因為是3*3,好像並看不出來什麼,但是相比剛開始訓練的時候要好的很多,不信你可以在訓練完1個epoch時就視覺化看看

這裡寫圖片描述

相關推薦

[CS231n-assignment2] Python實現的CNN在CIFAR-10實驗報告

1. CS231n課程 CS231n是斯坦福大學李飛飛團隊的一門關於卷積神經網路CNN的課程,這個課程從KNN和線性分類器講到普通的神經網路,再將到卷積神經網路的實現,以及一些實用的技術如Dropout、Batch Normalization等,整個課程下來後

python開始--36 python內建類屬性 __len__ __getitem__ 實現 (補充26節)

在網上看到一個關於實現 __len__   __getitem__的程式碼,稍微修改了一下,剛好作為26節內建類屬性的補充。 程式碼說明: 1. 定義一稿Card具名元組,用來存放撲克點數和花色的組合,FrenchDeck初始化後,剛好是52組資料 2. __len_

python開始--36 python內建類屬性 __len__ __getitem__ 實現 (補充26節)

在網上看到一個關於實現 __len__   __getitem__的程式碼,剛好作為26節內建類屬性的補充。 程式碼說明: 1. 定義一稿Card具名元組,用來存放撲克點數和花色的組合,FrenchDeck初始化後,剛好是52組資料 2. __len__實現了len(o

實現jQuery的extend

log 如何 asc 基本類型 是否 query 解決 復制 上進 前言 jQuery 的 extend 是 jQuery 中應用非常多的一個函數,今天我們一邊看 jQuery 的 extend 的特性,一邊實現一個 extend! extend 基本用法 先來看看 ext

Python開始創建區塊鏈

.com python -h send htm route 區塊鏈 文章 特定字符 用 Python 從 0 開始創建一個區塊鏈 對數字貨幣的崛起感到新奇的我們,並且想知道其背後的技術——區塊鏈是怎樣實現的。本文通過 Python 構建一個區塊鏈可以加深對區塊鏈的理解。

實現一個http服務器

retrieve vba ilove ext TP 應用場景 註釋 end HA 我始終覺得,天生的出身很重要,但後天的努力更加重要,所以如今的很多“科班”往往不如後天努力的“非科班”。所以,我們需要重新給“專業”和“專家”下一個定義:所謂專業,就是別人搞你不搞,這就是你的

實現Lumen-JWT擴展包(序):前因

height targe ctu 就是 internal 結果 lazy 黑名單 ace 轉自:https://zhuanlan.zhihu.com/p/22531819?refer=lsxiao 最近這段時間我尋思著把幾個月前爬下來的6萬多首詩詞曲文做成一個API,免費

Python 基礎開始概述

www. bsp 編程 參考 ext lov char NPU lin Python概述 計算機語言概述 語言:交流的工具,溝通媒介 計算機語言:人跟計算機交流的工具,翻譯官 Python是計算機語言裏的一種 Python編程語言 代碼:人類語言,同過代碼命令機

python開始 -- 第1篇之環境搭建

接收 window .py 文檔 路徑 很多 教程 編碼 官方   事實上,網絡上有很多相應的教程,本文無意做成文章的粘貼展示板,附上我認為的簡易的安裝詳解: 安裝 Python 環境(編程小白的第一本 Python 入門書),包含了python以及相關的IDE,圖文並茂,

Python開始寫爬蟲(二)BeautifulSoup庫使用

Beautiful Soup 是一個可以從HTML或XML檔案中提取資料的Python庫, BeautifulSoup在解析的時候是依賴於解析器的,它除了支援Python標準庫中的HTML解析器,還支援一些第三方的解析器比如lxml等。可以從其官網得到更詳細的資訊:http://beau

Python開始寫爬蟲(一)requests庫使用

requests是一個強大的網路請求庫,簡單易用-讓 HTTP 服務人類。可以參考這個網站的介紹:http://cn.python-requests.org/zh_CN/latest/index.html 直接使用pip install requests安裝此模組之後,開始吧。

實現 Spring Boot 2.0 整合 weixin-java-mp(weixin-java-tools) 獲取 openId,用於微信授權

步驟: 一、內網穿透申請二級域名(有伺服器和域名者可略過) 二、申請微信公眾平臺測試號(有已認證的微信服務號者可略過) 三、搭建 Spring Boot 2.0 專案實現獲取openId 一、內網穿透: 因為要直接用內網本機開發除錯,微信網頁授權在回撥時要訪問本機,所以直接

python進階之路——day2

列表list:增,刪,查,改,切片,拷貝 1.切片 # -*- coding-utf8 -*- # author:zingerman name=['hu','zhao','wang','zhou'] print(name[:2])   #預設從0開始 print(name[-3:

開始Tableau | 10.表計算-基礎

  表計算是tableau中的一個重要知識點,也是應用的難點之一,但用好表計算,能較好解決日常分析中的許多計算問題。本節記錄要點: 基礎概念 快速表計算 建立表計算 基礎概念 1.表計算是針對多行資料進行計算的方式,建立

python開始--35 wxPython 加 wxFromBuilder處理python桌面UI

python的桌面UI設計和處理貌似沒有VB, C#等語言方便。今天查詢了一些資料,發現用wxPython + wxFromBuilder是一個相對簡單的方案。 1.  用pip下載最新的wxPython (我在下載的時候,發現pip預設的源下載wxPython連線不成功,切換到國內的源

技術 | Python開始系列連載(二十九)

寫爬蟲防止被封的關鍵有以下幾點:  ●  偽裝請求報頭(request header)  ●  減輕訪問頻率,速度  ●  使用代理IP 一般第一點都能做到,第二點減輕訪問頻率就會大大增加任務時間,而使用代理就能

Python入門教程 | 在不同的作業系統中安裝Python程式設計環境

Python是一種跨平臺的程式語言,這意味著它能夠執行在所有主要的作業系統中,那麼我們所熟知的作業系統包括:Windows、MacOs、 Linux。那麼今天要講的就是如何在每個作業系統中成功的安裝python. 一、在Windows系統中搭建Python程式設計環境 01.下

python進階之路——day4

  高階函式 函式在記憶體中儲存方式跟變數一樣,計算機會為函式開闢記憶體空間存放函式體,同時函式名指向這個記憶體空間,函式名相當於控制代碼(記憶體地址),函式可以賦值給變數,通過變數呼叫,函式與變數一樣也可以作為引數傳給另一個函式。 把函式作為引數傳入,這樣的函式稱為高階函式,函數語言程式設計

python學——scrapy初體驗

python從零學——scrapy初體驗 近日因為一些事情,需要從網上爬取一些東西,故而想通過使用爬蟲來順便學習下強大的python。現將一些學習中遇到的問題記錄下來,以便日後查詢 1. 開發環境的準備(本人windows10 x64) python的爬蟲框架應該說是有挺多的了,使用sc

教程 | 僅需六步,實現機器學習演算法!

從頭開始寫機器學習演算法能夠獲得很多經驗。當你最終完成時,你會驚喜萬分,而且你明白這背後究竟發生了什麼。 有些演算法比較複雜,我們不從簡單的演算法開始,而是要從非常簡單的演算法開始,比如單層感知器。 本文以感知器為例,通過以下 6 個步驟引導你從頭開始寫演算法:  ●