前言
- 最近在摸索這方面相關(guān)的知識(shí)乃摹,本著整理鞏固云茸,分享促進(jìn)的精神善镰。所以有了這篇博文妹萨。
- 需要注意的是,本文受眾:對(duì)機(jī)器學(xué)習(xí)感興趣炫欺,且愿意花點(diǎn)時(shí)間學(xué)習(xí)的應(yīng)用(業(yè)務(wù))程序員
- 我本意是盡量簡(jiǎn)單乎完,易于理解,快速上手品洛,短時(shí)間能跑出來東西树姨,這樣子才能正向激勵(lì)我們的學(xué)習(xí)欲望摩桶。
- 基于上述條件,需要你已經(jīng)有一定的開發(fā)經(jīng)驗(yàn)帽揪,微不足道的數(shù)學(xué)能力硝清,以及善用搜索引擎的覺悟。
開發(fā)環(huán)境搭建
首先转晰,我希望你是Linux系用戶,如果你是巨硬黨芦拿,裝一個(gè)VirtualBox吧,然后再裝個(gè)ubuntu查邢,由于我們只是入個(gè)門蔗崎,對(duì)性能要求不高的。
機(jī)器學(xué)習(xí)相關(guān)的框架也很多扰藕,我這里選擇了==keras== keras中文文檔缓苛,后端采用的==tensorflow== 官方網(wǎng)站 。那么理所當(dāng)然的邓深,會(huì)用到python來開發(fā)未桥,沒有python經(jīng)驗(yàn)也莫慌,影響并不大庐完。
- ubuntu自帶python 我就不介紹怎么安裝了吧钢属?
先安裝pip(我用的python2.7,后文統(tǒng)一)打開你的終端门躯,輸入這個(gè):(我建議更換下apt-get為國(guó)內(nèi)鏡像,安裝完pip后也更換為國(guó)內(nèi)鏡像吧)
sudo apt-get install python-pip python-dev
- 安裝tensorflow和keras淆党,matplotlib
還是打開終端,輸入輸入
pip install tensorflow
pip install matplotlib
pip install keras
安裝完輸入 python 然后import測(cè)試下
你也可以測(cè)試下tensorflow讶凉,下面是個(gè)標(biāo)準(zhǔn)hello world
import tensorflow as tf
hello = tf.constant('Hello, TensorFlow!')
sess = tf.Session()
print(sess.run(hello))
你看染乌,ubuntu下安裝環(huán)境這么簡(jiǎn)單,我不知道你為什么不嘗試下
卷積神經(jīng)網(wǎng)絡(luò)CNN淺析
我建議你先把CNN當(dāng)作一個(gè)黑盒子懂讯,不要關(guān)心為什么荷憋,只關(guān)心結(jié)果。
這里借用了一個(gè)分辨X
和o
的例子來這里看原文褐望,就是每次給你一張圖勒庄,你需要判斷它是否含有"X"或者"O"。并且假設(shè)必須兩者選其一瘫里,不是"X"就是"O"实蔽。
下面看一下CNN是怎么分辨輸入的圖像是x還是o,如果需要你來編程分辨圖像是x還是o谨读,你會(huì)怎么做局装?可能你第一時(shí)間就想到了逐個(gè)像素點(diǎn)對(duì)比。但是,如果圖片稍微有點(diǎn)變化呢铐尚?像是下面這個(gè)x拨脉,它不是標(biāo)準(zhǔn)的x,我們可以分辨它是x宣增,但是對(duì)于計(jì)算機(jī)而言玫膀,它就只是一個(gè)二維矩陣,逐個(gè)像素點(diǎn)對(duì)比肯定是不行的爹脾。
CNN就是用于解決這類問題的匆骗,它不在意具體每個(gè)點(diǎn)的像素,而是通過一種叫卷積的手段誉简,去提取圖片的特征碉就。
什么是特征? 特征就是我們用于區(qū)分兩種輸入是不是同一類的分辨點(diǎn),像是這個(gè)XXOO的例子闷串,如果要你描述X和O的區(qū)別瓮钥,你會(huì)怎么思考?X是兩條線交叉烹吵,O是封閉的中空的碉熄。。肋拔。
我們來看個(gè)小小的例子锈津,如果下面兩張圖,需要分類出喜歡和不喜歡兩類凉蜂,那么你會(huì)提取什么作為區(qū)分的特征琼梆?(手動(dòng)滑稽)
卷積層
所以對(duì)于CNN而言,第一步就是提取特征窿吩,卷積就是提取猜測(cè)特征的神奇手段茎杂。而我們不需要指定特征,任憑它自己去猜測(cè)纫雁,就像上圖煌往,我們只需要告訴它,我們喜歡左邊的轧邪,不喜歡右邊的刽脖,然后它就去猜測(cè),區(qū)分喜不喜歡的特征是黑絲忌愚,還是奶子呢曲管?
假設(shè),我們上面這個(gè)例子菜循,CNN對(duì)于
X
的猜測(cè)特征如上翘地,現(xiàn)在要通過這些特征來分類。計(jì)算機(jī)對(duì)于圖像的認(rèn)知是在矩陣上的癌幕,每一張圖片有rgb二維矩陣(不考慮透明度)所以衙耕,一張圖片,應(yīng)該是
3x高度x寬度
的矩陣勺远。而我們這個(gè)例子就只有黑白橙喘,所以可以簡(jiǎn)單標(biāo)記1為白,-1為黑胶逢。是個(gè)9x9的二維矩陣厅瞎。我們把上面的三個(gè)特征作為卷積核(我們這里是假設(shè)已經(jīng)訓(xùn)練好了CNN,訓(xùn)練提出的特征就是上面三個(gè)初坠,我們可以通過這三個(gè)特征去分類 X )和簸,去和輸入的圖像做卷積(特征的匹配)。
看完上面的碟刺,估計(jì)你也能看出特征是如何去匹配輸入的锁保,這就是一個(gè)卷積的過程,具體的卷積計(jì)算過程如下(只展示部分):
把計(jì)算出的結(jié)果填入新的矩陣
其他部分也是相同的計(jì)算
最后半沽,我們整張圖用卷積核計(jì)算完成后:
三個(gè)特征都計(jì)算完成后:
不斷地重復(fù)著上述過程爽柒,將卷積核(特征)和圖中每一塊進(jìn)行卷積操作。最后我們會(huì)得到一個(gè)新的二維數(shù)組者填。其中的值浩村,越接近為1表示對(duì)應(yīng)位置的匹配度高,越是接近-1占哟,表示對(duì)應(yīng)位置與特征的反面更匹配心墅,而值接近0的表示對(duì)應(yīng)位置沒有什么關(guān)聯(lián)。
以上就是我們的卷積層榨乎,通過特征卷積嗓化,輸出一個(gè)新的矩陣給下一層。
池化層
在圖像經(jīng)過以上的卷積層后谬哀,得到了一個(gè)新的矩陣刺覆,而矩陣的大小,則取決于卷積核的大小史煎,和邊緣的填充方式谦屑,總之,在這個(gè)XXOO的例子中篇梭,我們得到了7x7的矩陣氢橙。池化就是縮減圖像尺寸和像素關(guān)聯(lián)性的操作,只保留我們感興趣(對(duì)于分類有意義)的信息恬偷。
常用的就是2x2的最大池悍手。
看完上面的圖,你應(yīng)該知道池化是什么操作了。通常情況下坦康,我們使用的都是2x2的最大池竣付,就是在2x2的范圍內(nèi),取最大值滞欠。因?yàn)樽畲蟪鼗╩ax-pooling)保留了每一個(gè)小塊內(nèi)的最大值古胆,所以它相當(dāng)于保留了這一塊最佳的匹配結(jié)果(因?yàn)橹翟浇咏?表示匹配越好)。這也就意味著它不會(huì)具體關(guān)注窗口內(nèi)到底是哪一個(gè)地方匹配了筛璧,而只關(guān)注是不是有某個(gè)地方匹配上了逸绎。這也就能夠看出,CNN能夠發(fā)現(xiàn)圖像中是否具有某種特征夭谤,而不用在意到底在哪里具有這種特征棺牧。這也就能夠幫助解決之前提到的計(jì)算機(jī)逐一像素匹配的死板做法。
同樣的操作以后朗儒,我們就輸出了3個(gè)4x4的矩陣陨帆。
全連接層
全連接層一般是為了展平數(shù)據(jù),輸出最終分類結(jié)果前的歸一化采蚀。 我們把上面得到的4x4矩陣再卷積+池化疲牵,得到2x2的矩陣
全連接就是這樣子,展開數(shù)據(jù)榆鼠,形成1xn的'條'型矩陣纲爸。
然后再把全連接層連接到輸出層。之前我們就說過妆够,這里的數(shù)值识啦,越接近1表示關(guān)聯(lián)度越大,然后我們根據(jù)這些關(guān)聯(lián)度神妹,分辨到底是O還是X.
一個(gè)基本的卷積神經(jīng)網(wǎng)絡(luò)就是這樣子的乍楚。回顧一下届慈,它的結(jié)構(gòu):看上圖(圈圈里面的幾個(gè)關(guān)鍵信息點(diǎn))颓哮,這里有個(gè)新的圖像丟進(jìn)我們的CNN了,根據(jù)卷積>池化>卷積>池化>全連接的步驟鸵荠,我們得到了新的全連接數(shù)據(jù)冕茅,然后去跟我們的標(biāo)準(zhǔn)比對(duì),得出相似度蛹找,可以看到姨伤,相似度是X的為0.92 所以,我們認(rèn)為這個(gè)輸入是X庸疾。
Relu是常用的激活函數(shù)徒溪,所做的工作就是max(0,x)忿偷,就是輸入大于零,原樣輸出臊泌,小于零輸出零鲤桥,這里就不展開了。
CNN實(shí)現(xiàn)手寫數(shù)字識(shí)別
感覺缺虐,這個(gè)mnist的手寫數(shù)字,跟其他語(yǔ)言的helloworld一樣了礁凡。我們這里來簡(jiǎn)單實(shí)現(xiàn)下高氮。首先,我建議你先下載好數(shù)據(jù)集顷牌,keras的下載太慢了下載地址 下載好以后剪芍,按下面的位置放,你可能要先運(yùn)行下程序窟蓝,讓他自己創(chuàng)建文件夾罪裹,不然,你就手動(dòng)創(chuàng)建吧运挫。
新建個(gè)python文件状共,test.py
然后輸入下面的內(nèi)容
#coding: utf-8
from keras.datasets import mnist
import matplotlib.pyplot as plt
# 加載數(shù)據(jù)
(X_train, y_train), (X_test, y_test) = mnist.load_data()
# 展示下第一張圖
plt.imshow(X_train[0], cmap=plt.get_cmap('PuBuGn_r'))
plt.show()
運(yùn)行后出來張圖片,然后關(guān)掉就行谁帕,這里只是看看我們加載數(shù)據(jù)有沒有問題峡继。
x_train
,x_test
是我們的圖像矩陣數(shù)據(jù),是28x28大小匈挖,然后有12500條吧好像碾牌。然后y_train
,y_test
都是標(biāo)簽數(shù)據(jù),標(biāo)明這張圖代表是數(shù)字幾儡循。
#coding: utf-8
#Simple CNN
import numpy
from keras.datasets import mnist
from keras.models import Sequential
from keras.layers import Dense
from keras.layers import Dropout
from keras.layers import Flatten
from keras.layers.convolutional import Conv2D
from keras.layers.convolutional import MaxPooling2D
from keras.utils import np_utils
seed = 7
numpy.random.seed(seed)
#加載數(shù)據(jù)
(X_train, y_train), (X_test, y_test) = mnist.load_data()
# reshape to be [samples][channels][width][height]
X_train = X_train.reshape(X_train.shape[0],28, 28,1).astype('float32')
X_test = X_test.reshape(X_test.shape[0],28, 28,1).astype('float32')
# normalize inputs from 0-255 to 0-1
X_train = X_train / 255
X_test = X_test / 255
# one hot encode outputs
y_train = np_utils.to_categorical(y_train)
y_test = np_utils.to_categorical(y_test)
num_classes = y_test.shape[1]
# 簡(jiǎn)單的CNN模型
def baseline_model():
# create model
model = Sequential()
#卷積層
model.add(Conv2D(32, (3, 3), padding='valid', input_shape=(28, 28,1), activation='relu'))
#池化層
model.add(MaxPooling2D(pool_size=(2, 2)))
#卷積
model.add(Conv2D(15, (3, 3), padding='valid' ,activation='relu'))
#池化
model.add(MaxPooling2D(pool_size=(2, 2)))
#全連接舶吗,然后輸出
model.add(Flatten())
model.add(Dense(128, activation='relu'))
model.add(Dense(num_classes, activation='softmax'))
# Compile model
model.compile(loss='categorical_crossentropy', optimizer='adam', metrics=['accuracy'])
return model
# build the model
model = baseline_model()
# Fit the model
model.fit(X_train, y_train, validation_data=(X_test, y_test), epochs=10, batch_size=128, verbose=2)
代碼也挺簡(jiǎn)單,因?yàn)閗eras也是封裝的挺好的了择膝∈那恚基本你看懂了前面的就沒問題。
Epoch 1/10
3s - loss: 0.2791 - acc: 0.9203 - val_loss: 0.1420 - val_acc: 0.9579
Epoch 2/10
3s - loss: 0.1122 - acc: 0.9679 - val_loss: 0.0992 - val_acc: 0.9699
Epoch 3/10
3s - loss: 0.0724 - acc: 0.9790 - val_loss: 0.0784 - val_acc: 0.9745
Epoch 4/10
3s - loss: 0.0509 - acc: 0.9853 - val_loss: 0.0774 - val_acc: 0.9773
Epoch 5/10
3s - loss: 0.0366 - acc: 0.9898 - val_loss: 0.0626 - val_acc: 0.9794
Epoch 6/10
3s - loss: 0.0265 - acc: 0.9930 - val_loss: 0.0639 - val_acc: 0.9797
Epoch 7/10
3s - loss: 0.0185 - acc: 0.9956 - val_loss: 0.0611 - val_acc: 0.9811
Epoch 8/10
3s - loss: 0.0150 - acc: 0.9967 - val_loss: 0.0616 - val_acc: 0.9816
Epoch 9/10
4s - loss: 0.0107 - acc: 0.9980 - val_loss: 0.0604 - val_acc: 0.9821
Epoch 10/10
4s - loss: 0.0073 - acc: 0.9988 - val_loss: 0.0611 - val_acc: 0.9819
然后你就能看到這些輸出肴捉,acc就是準(zhǔn)確率了踊赠,看后面的val_acc就行。
其他的參數(shù)那些每庆,我建議你看看keras的文檔筐带。然后,入門就結(jié)束了缤灵。如果你感興趣的話伦籍,就自己去摸索吧蓝晒,后續(xù)我也可能會(huì)繼續(xù)分享相關(guān)的內(nèi)容。