小白學(xué)CNN以及Keras的速成
一、為何要用Keras
如今在深度學(xué)習(xí)大火的時候,第三方工具也層出不窮,比較出名的有Tensorflow,Caffe,Theano,MXNet,在如此多的第三方框架中頻繁的更換無疑是很低效的,只要你能夠好好掌握其中一個框架,熟悉其原理,那么之后因為各種要求你想要更換框架也是很容易的。
那么sherlock用的是哪個框架呢?sherlock使用的是Google的開源框架Tensorflow,因為Google開源了tensorflow之后其社區(qū)非?;钴S,而且版本更新也非常穩(wěn)定,所以我就選擇了這個框架。對于框架之爭,在知乎上已經(jīng)有很多人在撕逼了,這個就好比哪種編程語言好這個問題一樣。對于我們來講,選擇一個穩(wěn)定的框架,好好的學(xué)習(xí)deep learning才是重中之重,對于哪種框架更好的問題,我們學(xué)好之后自然有自己的見解,所以前期切忌在刷知乎聽完大神撕逼之后頻繁更換框架。
對于Tensorflow的安裝,以及CPU和GPU版本,各種系統(tǒng)的安裝網(wǎng)上已經(jīng)有很多人詳細(xì)的寫過攻略了,可以自己去網(wǎng)上搜一搜,很容易就可以安裝成功。
選擇了Tensorflow之后,我們就可以愉快的開始我們的深度學(xué)習(xí)之旅了。去Tensorflow的中文社區(qū),可以看到有一些新手教程,網(wǎng)上也有很多學(xué)習(xí)材料,推薦看看stanford大學(xué)cs224d的課件,http://cs224d.stanford.edu/lectures/CS224d-Lecture7.pdf, 很詳細(xì)的介紹了tensorflow。然后你就可以寫tensorflow的程序了。雖然說tensorflow已經(jīng)是一個封裝好的框架,但是你發(fā)現(xiàn)你寫一個簡單的神經(jīng)網(wǎng)絡(luò)也需要很多行才能夠?qū)懲?,這個時候,就有很多的第三方插架來幫助你寫網(wǎng)絡(luò),也就是說你用tensorflow要寫10行,第三方插架幫你封裝了一個函數(shù),就是把這10行集合在這個函數(shù)里面,那么你用1行,傳入相同的參數(shù),就能夠達到10行相同的效果,如此簡便并且節(jié)約時間,可以幫助很快的實現(xiàn)我們的想法。
Keras Documentation就是Keras的官方文檔,里面可以查閱所有的函數(shù),并且可以在github上看他的開源代碼,非常方便。安裝也很簡單,打開終端,輸入pip install keras 就可以等待安裝了。
下面就給一個簡單的例子,來看一看Keras到底有多簡單。
- from keras.models import Sequential
- model = Sequential()
引入sequential,這個就是一個空的網(wǎng)絡(luò)結(jié)構(gòu),并且這個結(jié)構(gòu)是一個順序的序列,所以叫Sequential,Keras里面還有一些其他的網(wǎng)絡(luò)結(jié)構(gòu)。
- from keras.layers import Dense, Activation
- model.add(Dense(units=64, input_dim=100))
- model.add(Activation('relu'))
- model.add(Dense(units=10))
- model.add(Activation('softmax'))
可以看到加入層很簡單,只需要寫.add,后面是要加的層的類型。
- model.compile(loss='categorical_crossentropy',
- optimizer='sgd',
- metrics=['accuracy'])
一旦你寫好了網(wǎng)絡(luò)之后,就可以用compile編譯整個網(wǎng)絡(luò),看參數(shù)設(shè)置有沒有問題
- model.compile(loss=keras.losses.categorical_crossentropy,
- optimizer=keras.optimizers.SGD(lr=0.01,momentum=0.9,nesterov=True))
你也可以自定義其中的優(yōu)化函數(shù),就像上面這樣,’sgd’是Keras已經(jīng)寫好了一些默認(rèn)參數(shù)的優(yōu)化函數(shù),你可以自己重新定義參數(shù),得到一個優(yōu)化函數(shù)。
- model.fit(x_train,y_train,epochs=5,batch_size=32)
這個就像scikit-learn一樣訓(xùn)練模型。
- loss_and_metrics=model.evaluate(x_test,y_test,batch_size=128)
這個就是評估訓(xùn)練結(jié)果。
- classes=model.predict(x_test,batch_size=128)
或者是通過predict進行預(yù)測。
看了上面的代碼,相信很多熟悉scikit-learn的同學(xué)都很親切,因為確實很簡便,跟scikit-learn也有著類似的語法。
二、開始學(xué)習(xí)CNN
在理解CNN之前,我們有必要先理解一下什么是神經(jīng)網(wǎng)絡(luò),這樣才能開始了解更高級的卷積神經(jīng)網(wǎng)絡(luò)。
要學(xué)習(xí)神經(jīng)網(wǎng)絡(luò)當(dāng)然有很多途徑,網(wǎng)上不少的大牛寫了很多攻略,有的推崇從理論到工程完成深度學(xué)習(xí),有的希望從工程出發(fā)發(fā)現(xiàn)問題,解決問題。各種各樣的方式都有不同的人去嘗試,攻略也是一大推,這使得不少的小白直接倒在了選擇材料的路上,一直在補先修知識,待到熱情結(jié)束就放棄了學(xué)習(xí),連卷積網(wǎng)絡(luò)都不知道是什么,大大地打擊了大家的學(xué)習(xí)熱情。今天,sherlock在這里給大家推薦一個學(xué)習(xí)材料,保證你能夠快速入門cnn,出去裝逼也能夠和別人聊幾句。
這個材料是什么呢,就是大名鼎鼎的standford的cs231n這門課程。CS231n Convolutional Neural Networks for Visual Recognitionstanford大學(xué)確實算是深度學(xué)習(xí)和人工智能領(lǐng)域非常牛逼的學(xué)校。
神經(jīng)網(wǎng)絡(luò)
廢話不多說,開始學(xué)習(xí)我們的神經(jīng)網(wǎng)絡(luò)。
這是一張腦神經(jīng)的圖片,神經(jīng)網(wǎng)絡(luò)的發(fā)明也是由此開始的,這就是所謂的一個神經(jīng)元,上面有各種接受突觸,然后通過一個腦神經(jīng)來接受,最后得到輸出的結(jié)果。
那么由這張腦神經(jīng)圖能夠抽象出來的神經(jīng)網(wǎng)絡(luò)是什么呢?就是下面這個神經(jīng)網(wǎng)絡(luò)模型。
這個怎么理解呢?就是輸入一個向量,然后給向量的每一個元素分配一個權(quán)重,然后通過權(quán)重求和得到一個結(jié)果,然后將這個結(jié)果輸入一個激活函數(shù),得到最后的輸出結(jié)果。
激活函數(shù)又是什么鬼?激活函數(shù)的出現(xiàn)是因為人腦的構(gòu)造,人腦里面接受信息得到結(jié)果這個過程是非線性的,比如你看到一樣?xùn)|西,你不可能保留這個東西的全部特征,你會重點觀察你感興趣的地方,這就是非線性的,也就是說需要一個非線性變化將輸入的結(jié)果變換為非線性的結(jié)果?,F(xiàn)在常用的非線性函數(shù)就是Relu(x) = max(x, 0),就是將小于0的部分去掉,只保留大于0的部分。
這就是單元的輸入和輸出,將這些單元合在一起就是一個神經(jīng)網(wǎng)絡(luò)。
這就是簡單的一層網(wǎng)絡(luò),也可以由多層網(wǎng)絡(luò)
這里面的input layer就是所謂的單個訓(xùn)練集的維數(shù),將所有的訓(xùn)練集輸入就可以開始訓(xùn)練一個神經(jīng)網(wǎng)絡(luò)。
Keras實現(xiàn)簡單的神經(jīng)網(wǎng)絡(luò)
知道了神經(jīng)網(wǎng)絡(luò)的基本結(jié)構(gòu)和原理,我們就可以開始使用keras去實現(xiàn)一個簡單的神經(jīng)網(wǎng)絡(luò)。
- import keras
- from keras.models import Sequential
- from keras.layers import Dense
- import numpy as np
導(dǎo)入必要的package
- x=np.array([[0,1,0],[0,0,1],[1,3,2],[3,2,1]])
- y=np.array([0,0,1,1]).T
設(shè)定輸入的x和y
- simple_model=Sequential()
- simple_model.add(Dense(5,input_shape=(x.shape[1],),activation='relu',name='layer1'))
- simple_model.add(Dense(4,activation='relu',name='layer2'))
- simple_model.add(Dense(1,activation='sigmoid',name='layer3'))
輸入一個三層的神經(jīng)網(wǎng)絡(luò),中間的hidden layer的元素個數(shù)是5和4,最后一層輸出一個結(jié)果
- simple_model.compile(optimizer='sgd',loss='mean_squared_error')
complie這個簡單的模型
- simple_model.fit(x,y,epochs=20000)
訓(xùn)練20000次模型
- simple_model.predict(x[0:1])
可以預(yù)測一下第一個輸入的x的結(jié)果與實際的是否相符。
上面就是一個簡單三層網(wǎng)絡(luò)的keras實現(xiàn),接下來我們將正式進入Convolutional Neural Network
三、Convolutional Neural Network
前面給大家推薦了一門好課cs231n,本篇文章也是按照這個思路來的。
基本結(jié)構(gòu)
首先解釋一下什么是卷積,這個卷積當(dāng)然不是數(shù)學(xué)上的卷積,這里的卷積其實表示的是一個三維的權(quán)重,這么解釋起來可能不太理解,我們先看看卷積網(wǎng)絡(luò)的基本結(jié)構(gòu)。
通過上面的圖我們清楚地了解到卷積網(wǎng)絡(luò)和一般網(wǎng)絡(luò)結(jié)構(gòu)上面的差別,也可以理解為卷積網(wǎng)絡(luò)是立體的,而一般的網(wǎng)絡(luò)結(jié)構(gòu)是平面的。
卷積層
了解完了基本的結(jié)構(gòu)之后,我們就要了解cnn最重要的一個部分,也是最為創(chuàng)新的一個部分,卷積層。首先用一張圖片來比較一下卷積網(wǎng)絡(luò)到底創(chuàng)新在什么地方。
我們通過這個結(jié)構(gòu)就可以清晰地看到卷積網(wǎng)絡(luò)到底是怎么實現(xiàn)的。首先右邊是傳統(tǒng)的網(wǎng)絡(luò)結(jié)構(gòu),在前面我們已經(jīng)詳細(xì)的解釋過了。而左邊的圖片,我們首先看看圖中最左邊的結(jié)構(gòu),你肯定會好奇為什么是32x32x3的一塊立體方塊。這個32×32代表的是像素點,說白了也就是圖片的大小,這個大小是你可以設(shè)置的,你可以設(shè)置為50×50,也可以是256×256,這都取決與圖片的大小,那么3表示什么呢?3其實表示的是RGB的三個通道,RGB也是什么?RGB表示red,green,blue,這三種顏色的各種組合疊加可以形成各種各樣的顏色,所以任何一張照片都可以用左邊這種圖形來表示。
那么中間這個小方塊又表示什么呢?這個就是我們要重點講的卷積。所謂的卷積,就是這種小方塊,我們設(shè)置一個小方塊的大小,但是這個小方塊的厚度必須和左邊的這個大方塊的厚度是一樣的,大方塊每一個像素點由一個0到255的數(shù)字表示,這樣我們就可以賦予小方塊權(quán)重,比如我們?nèi)⌒》綁K的大小是3×3,我們要求厚度必須要和左邊的大方塊厚度一樣,那么小方塊的的大小就為3x3x3,我們就可以賦予其3x3x3個權(quán)重,然后我們就可以開始計算卷積的結(jié)果,將小方塊從大方塊的左上角開始,一個卷積小方塊所覆蓋的范圍是3x3x3,然后我們將大方塊中3x3x3的數(shù)字和小方塊中的權(quán)重分別相乘相加,再加上一個偏差,就可以得到一個卷積的結(jié)果,可以抽象的寫成Wx+b這種形式,這就是圖上所顯示的結(jié)果,然后我們可以設(shè)置小方塊的滑動距離,每次滑動就可以形成一個卷積的計算結(jié)果,然后將整張大圖片滑動覆蓋之后就可以形成一層卷積的結(jié)果,我們看到圖中的卷積結(jié)果是很厚的,也就是設(shè)置了很多層卷積??偨Y(jié)來說,每層卷積就是一個卷積核在圖片上滑動求值,然后設(shè)置多個卷積核就可以形成多層的卷積層。
池化層
講完卷積層,接下來就要講一下池化層。為什么會有池化層的出現(xiàn)呢?是因為不斷的做卷積,得到的中間結(jié)果會越來越厚,卷積就相當(dāng)于提取圖片中的特征,所以卷積層一般會設(shè)置得越來越厚,不然你就無法從前面的結(jié)果來提取更多的特征。這樣就會導(dǎo)致中間的結(jié)果會越來越大,計算會越來越慢,所以提出了池化層。
所謂的池化層,就是將圖片的大小縮小的一種處理方式。我們可以先看看下面的圖片。
通過這個圖片,我們可以清楚地看到池化層是怎么處理的。池化層也是需要先設(shè)置一個窗口,但是這個小窗口的厚度是1,而不再是前一層輸出的結(jié)果的厚度。然后有兩種處理方式,一種是取這個小窗口里面所有元素的最大值來代表這個小窗口,一種是取平均值,然后將小窗口滑動,在第二的位置再做同樣的處理,上層網(wǎng)絡(luò)輸出方塊的每一層做完之后就進入這個大方塊的下一層做同樣的操作,這個處理辦法就可以讓整個大方塊的大小變小,可以看看上面的圖片的左邊。右邊是一個簡單的一層厚度,取最大值的例子。
實現(xiàn)Lenet
講完了卷積網(wǎng)絡(luò)的基本結(jié)構(gòu)之后,你是不是已經(jīng)迫不及待希望能夠?qū)崿F(xiàn)一個簡單的神經(jīng)網(wǎng)絡(luò)了呢?卷積網(wǎng)絡(luò)發(fā)展的特別迅速,最早是由Lecun提出來的,Lenet成為cnn的鼻祖,接下來他的學(xué)生Alex提出了層數(shù)更深的Alexnet,然后2013年又提出了VGGnet,有16層和19層兩種,這些都只是在層數(shù)上面的加深,并沒有什么其他的創(chuàng)新,而之后google提出了inception net在網(wǎng)絡(luò)結(jié)構(gòu)上實現(xiàn)了創(chuàng)新,提出了一種inception的機構(gòu),facebook ai 實驗室又提出了resnet,殘差網(wǎng)絡(luò),實現(xiàn)了150層的網(wǎng)絡(luò)結(jié)構(gòu)可訓(xùn)練化,這些我們之后會慢慢講到。
接下來我們就來實現(xiàn)一下最簡單的Lenet,使用mnist手寫子體作為訓(xùn)練集。
- import keras
- from keras.datasets import mnist
- (x_train, y_train), (x_test,y_test) =mnist.load_data()
導(dǎo)入必要的庫和數(shù)據(jù)集
- x_train=x_train.reshape(-1,28,28,1)
- x_test=x_test.reshape(-1,28,28,1)
- x_train=x_train/255.
- x_test=x_test/255.
- y_train=keras.utils.to_categorical(y_train)
- y_test=keras.utils.to_categorical(y_test)
處理數(shù)據(jù),讓數(shù)據(jù)的shape是(28, 28, 1),然后label做一個one-hot encoding處理,比如類別是3,那么變成[0, 0, 1 ,0, 0, 0, 0, 0, 0, 0]。
- from keras.layers import Conv2D,MaxPool2D,Dense,Flatten
- from keras.models import Sequential
- lenet=Sequential()
- lenet.add(Conv2D(6,kernel_size=3,strides=1,padding='same',input_shape=(28, 28, 1)))
- lenet.add(MaxPool2D(pool_size=2,strides=2))
- lenet.add(Conv2D(16,kernel_size=5,strides=1,padding='valid'))
- lenet.add(MaxPool2D(pool_size=2,strides=2))
- lenet.add(Flatten())
- lenet.add(Dense(120))
- lenet.add(Dense(84))
- lenet.add(Dense(10,activation='softmax'))
構(gòu)建lenet
- lenet.compile('sgd',loss='categorical_crossentropy',metrics=['accuracy'])
編譯
- lenet.fit(x_train,y_train,batch_size=64,epochs=50,validation_data=[x_test,y_test])
訓(xùn)練50次,得到結(jié)果如下
- lenet.save('myletnet.h5')
可以保存訓(xùn)練好的模型
總結(jié)
OK, 這就是我們寫的一個超級簡單的Lenet,訓(xùn)練50次得到的訓(xùn)練準(zhǔn)確率已經(jīng)達到0.9939,測試準(zhǔn)確率達到0.9852。