紀(jì)念碑旁有一家破舊的電影院秦士,往北走五百米就是南京火車西站
每天都有外地人在直線和曲線之間迷路,氣喘吁吁眼淚模糊奔跑 跌倒 奔跑
秋林龍蝦換了新的地方永高,三十二路還是穿過(guò)挹江門
高架橋修了新的隧道隧土,走來(lái)走去走不出 我的鹽倉(cāng)橋
來(lái)到城市已經(jīng)八百九十六天提针,熱河路一直是相同的容顏
偶爾有干凈的潘西路過(guò),他不會(huì)說(shuō)你好 再見
tensorflow游樂(lè)場(chǎng)
TensorFlow游樂(lè)場(chǎng)是一個(gè)通過(guò)網(wǎng)頁(yè)瀏覽器就可以訓(xùn)練簡(jiǎn)單神經(jīng)網(wǎng)絡(luò)
并實(shí)現(xiàn)了可視化訓(xùn)練過(guò)程的工具次洼。
經(jīng)網(wǎng)絡(luò)解決一個(gè)分類問(wèn)題大致可以分為:
1.提取特征向量作為輸入关贵,比如說(shuō)本例子中零件的長(zhǎng)度和質(zhì)量。
2.定義神經(jīng)網(wǎng)絡(luò)結(jié)構(gòu)卖毁。包括隱藏層數(shù),激活函數(shù)等等落萎。
3.通過(guò)訓(xùn)練利用反響傳播算法不斷優(yōu)化權(quán)重的值亥啦,使之達(dá)到最合理水平。
4.使用訓(xùn)練好的神經(jīng)網(wǎng)絡(luò)來(lái)預(yù)測(cè)未知數(shù)據(jù)练链,這里訓(xùn)練好的網(wǎng)絡(luò)就是指權(quán)重達(dá)到最優(yōu)的情況翔脱。
我玩了一會(huì)發(fā)現(xiàn):這不就是我們做的實(shí)驗(yàn)一嗎!C焦摹届吁!怎么會(huì)有這么好用的工具!绿鸣!可以選擇分類和回歸疚沐,可以選擇輸入數(shù)據(jù)的類型,我的天哪簡(jiǎn)直太好玩了俺蹦!A粱住!
numpy.random.random(20,1)隨機(jī)產(chǎn)生20個(gè)浮點(diǎn)數(shù)擎厢,每個(gè)在(0,1)取值
numpy.zeros(5) (5,dtype = np.int) (2,1) (2,2)
numpy.zeros((2,1)) 2*1矩陣
numpy.dot(A,B)
線性代數(shù)上的矩陣相乘究流,生成矩陣(1,1)為A第一行乘B第一列
A * B or numpy.multiply(A,B)
編程上的矩陣相乘,AB兩矩陣對(duì)應(yīng)位置元素相乘动遭,生成矩陣(1,1)為A(1,1)乘B(1,1)
上周用到的反向傳播算法芬探,一共分為三個(gè)步驟:
前向計(jì)算每個(gè)神經(jīng)元的輸出值aj
反向計(jì)算每個(gè)神經(jīng)元的誤差項(xiàng)deltaj,為損失函數(shù)Loss對(duì)該神經(jīng)元輸入的偏導(dǎo)數(shù)
計(jì)算每個(gè)神經(jīng)元連接權(quán)重wji(from i to j)的梯度厘惦,等于ai * deltaj
梯度下降法更新權(quán)重
對(duì)于卷積神經(jīng)網(wǎng)絡(luò)偷仿,由于涉及到局部連接、下采樣的等操作绵估,影響到了第二步誤差項(xiàng)delta的具體計(jì)算方法炎疆,而權(quán)值共享影響了第三步權(quán)重w的梯度的計(jì)算方法。
numpy實(shí)現(xiàn)lenet5實(shí)現(xiàn)MNIST手寫字符識(shí)別
背景知識(shí)
LeNet-5是一種典型的非常高效的用來(lái)識(shí)別手寫體數(shù)字的卷積神經(jīng)網(wǎng)絡(luò)国裳。LeNet-5出自論文Gradient-Based Learning Applied to Document Recognition形入,是由Yann LeCun提出的,對(duì)MNIST數(shù)據(jù)集的識(shí)別準(zhǔn)確度可達(dá)99.2%缝左。
局部感受野:卷積神經(jīng)網(wǎng)絡(luò)把每一個(gè)隱藏節(jié)點(diǎn)只連接到圖像的某個(gè)局部區(qū)域亿遂,從而減少參數(shù)訓(xùn)練的數(shù)量
共享權(quán)值:在卷積神經(jīng)網(wǎng)絡(luò)的卷積層中浓若,神經(jīng)元對(duì)應(yīng)的權(quán)值是相同的,由于權(quán)值相同蛇数,因此可以減少訓(xùn)練的參數(shù)量挪钓。共享的權(quán)值和偏置也被稱作卷積核或?yàn)V波器
池化:對(duì)圖像進(jìn)行卷積之后,通過(guò)一個(gè)下采樣過(guò)程耳舅,來(lái)調(diào)整圖像的大小
INPUT:輸入圖像的尺寸統(tǒng)一歸一化為32 * 32
C1卷積層:對(duì)輸入圖像進(jìn)行第一次卷積運(yùn)算碌上,使用 6 個(gè)大小為 5 * 5 的卷積核,得到6個(gè)28 * 28的特征圖feature map
S2池化層:使用 2 * 2核進(jìn)行池化浦徊,得到了S2馏予,6個(gè)14 * 14的特征圖(28/2=14)
C3卷積層:第二次卷積,第二次卷積的輸出是C3盔性,16個(gè)10x10的特征圖霞丧,卷積核大小是 5 * 5
S4池化層:窗口大小仍然是2*2,共計(jì)16個(gè)feature map冕香,S4中每個(gè)特征圖的大小是C3中特征圖大小的1/4
C5卷積層:由于S4層的16個(gè)圖的大小為5x5蛹尝,與卷積核的大小相同,所以卷積后形成的圖的大小為1x1
F6全連接:有84個(gè)節(jié)點(diǎn)悉尾,對(duì)應(yīng)于一個(gè)7x12的比特圖突那,-1表示白色,1表示黑色焕襟,這樣每個(gè)符號(hào)的比特圖的黑白色就對(duì)應(yīng)于一個(gè)編碼(ASCII編碼圖)
output全連接:有10個(gè)節(jié)點(diǎn)陨收,分別代表數(shù)字0到9,且如果節(jié)點(diǎn)i的值為0鸵赖,則網(wǎng)絡(luò)識(shí)別的結(jié)果是數(shù)字i
lenet5可以分為三個(gè)部分:
1.網(wǎng)絡(luò)定義 2.訓(xùn)練部分 3.驗(yàn)證部分
將訓(xùn)練部分與驗(yàn)證部分分開的好處在于务漩,訓(xùn)練部分可以持續(xù)輸出訓(xùn)練好的模型,驗(yàn)證部分可以每隔一段時(shí)間驗(yàn)證模型的準(zhǔn)確率它褪;如果模型不好饵骨,則需要及時(shí)調(diào)整網(wǎng)絡(luò)結(jié)構(gòu)的參數(shù)。
LeNet-5 的一些性質(zhì):
- 如果輸入層不算神經(jīng)網(wǎng)絡(luò)的層數(shù)茫打,那么 LeNet-5 是一個(gè) 7 層的網(wǎng)絡(luò)居触。LeNet-5 大約有 60,000 個(gè)參數(shù)
- 隨著網(wǎng)絡(luò)越來(lái)越深,圖像的高度和寬度在縮小老赤,與此同時(shí)轮洋,圖像的 channel 數(shù)量一直在增加
- 現(xiàn)在常用的 LeNet-5 結(jié)構(gòu)和 Yann LeCun論文中提出的結(jié)構(gòu)在某些地方有區(qū)別,比如激活函數(shù)的使用抬旺,以及輸出層一般選擇 softmax
MNIST:
MNIST是一個(gè)非常有名的手寫數(shù)字識(shí)別數(shù)據(jù)集弊予,是NIST數(shù)據(jù)集的一個(gè)子集,包含了60000張圖片作為訓(xùn)練數(shù)據(jù)开财,10000張圖片作為測(cè)試數(shù)據(jù)汉柒。在MNIST數(shù)據(jù)集中的每一張圖片都代表0~9中的一個(gè)數(shù)字
初始思路
最初我的實(shí)現(xiàn)思路是和之前的全連接神經(jīng)網(wǎng)絡(luò)擬合y=sinx一樣误褪,將所有的層c1、s2碾褂、c3 等等等等封裝在一個(gè)類里兽间,每一層作為一個(gè)方法,里面包含前向與反向傳播正塌。
后來(lái)發(fā)現(xiàn)嘀略,層與層之間重復(fù)的部分太多,代碼復(fù)用率太高传货,例如c1和c3基本是一樣的屎鳍,都是實(shí)現(xiàn)了卷積操作。于是果斷棄用问裕,換為將卷積層、池化層孵坚、全連接層粮宛、非線性層每個(gè)都單獨(dú)封裝為一個(gè)類。這樣構(gòu)建每一層的時(shí)候只要初始化類對(duì)象就可以卖宠。
數(shù)據(jù)讀取
通過(guò)glob和struct模塊來(lái)實(shí)現(xiàn)訓(xùn)練集和測(cè)試集的讀取
glob:kind表示前綴巍杈,'t10k' 為測(cè)試集,'train'為訓(xùn)練集扛伍, .表示當(dāng)前目錄筷畦,%s為path和kind代表的字符串,*匹配任意字符刺洒,3-ubyte為圖片數(shù)據(jù)集的后綴鳖宾,返回的為list,[0]表示我們要的是這個(gè)list中的第一個(gè)文件
struct:pack與unpack 實(shí)現(xiàn)python中字節(jié)流與其他格式的轉(zhuǎn)換逆航,>II表示解壓至少需要8個(gè)字節(jié)的緩沖區(qū)鼎文,將bytes文件轉(zhuǎn)換為python數(shù)據(jù)文件,返回為元組
np.fromfile:按照指定的格式讀取數(shù)據(jù)因俐,對(duì)數(shù)組的形狀做簡(jiǎn)單的修改
卷積層
初始化:
def __init__(self, shape, output_channels, kernel_size=3, stride=1, method='VALID'):
shape為四維數(shù)組拇惋,如[64, 28, 28, 1],代表[batchsize, width, height, channels]
output_channels為輸出的層數(shù)
kernel_size為卷積核的大小抹剩,如3 * 3的卷積核的大小就為3
stride為卷積的步長(zhǎng)
method為是否做zero padding 撑帖,VALID代表不做,SAME代表做
前向傳播:利用im2col方法來(lái)做卷積澳眷,同時(shí)得到self.col_image
求梯度:
def gradient(self, last_delta):
輸入為下一層的誤差項(xiàng)last_delta胡嘿,將其reshape
col_delta = np.reshape(last_delta, [self.batchsize, -1, self.output_channels])
將前向傳播得到的self.col_image轉(zhuǎn)置后與誤差項(xiàng)col_delta相乘,得到self.w的梯度境蔼,將col_delta求和灶平,得到self.b的梯度
將col_delta矩陣與翻轉(zhuǎn)180度后的權(quán)重矩陣self.weight矩陣相乘伺通,reshape后得到上一層的誤差項(xiàng)next_delta
反向傳播:
def backward(self, learning_rate = 1e-4, weight_decay = 0.0004):
根據(jù)學(xué)習(xí)率和self.w、self.b的梯度進(jìn)行梯度下降來(lái)更新
全連接層
思路和week18做的全連接層一樣逢享,只不過(guò)這次要把層抽象出來(lái)罐监,方法分為前向傳播forward、求梯度gradient和反向傳播backward
初始化:
def __init__(self, shape, output_amount = 2):
shape為四維數(shù)組同上瞒爬,output_amount代表輸出層神經(jīng)元的數(shù)目
前向傳播:矩陣相乘弓柱,求卷積
求梯度:
def gradient(self, last_delta):
根據(jù)后一層的誤差項(xiàng)來(lái)求出前一層的誤差項(xiàng),并求出self.w和self.b的梯度
反向傳播:
def backward(self, learning_rate = 1e-4, weight_decay = 0.0004):
根據(jù)學(xué)習(xí)率和梯度來(lái)進(jìn)行梯度下降來(lái)更新
非線性層
采用relu激活函數(shù)侧但,前向傳播為
return np.maximum(self.x, 0)
反向傳播為
self.delta[self.x < 0] = 0
最大池化層
前向傳播為(2,2)區(qū)域內(nèi)做池化矢空,取最大值,值得注意的是要記錄每一區(qū)域內(nèi)最大值對(duì)應(yīng)的索引
index = np.argmax(x[b, i:i + self.kernel_size, j:j + self.kernel_size, c])
self.index[b, i+index//self.stride, j + index % self.stride, c] = 1
反向傳播為
def gradient(self, delta):return np.repeat(np.repeat(delta, self.stride, axis=1), self.stride, axis=2) * self.index
Softmax層
初始化
def __init__(self, shape):
self.delta = np.zeros(shape)
self.softmax = np.zeros(shape)
self.batchsize = shape[0]
預(yù)測(cè)
根據(jù)softmax原理進(jìn)行預(yù)測(cè)
def predict(self, prediction):
exp_prediction = np.zeros(prediction.shape)
self.softmax = np.zeros(prediction.shape)
for i in range(self.batchsize):
prediction[i, :] -= np.max(prediction[i, :])
exp_prediction[i] = np.exp(prediction[i])
self.softmax[i] = exp_prediction[i]/np.sum(exp_prediction[i])
return self.softmax
求梯度
def gradient(self):
self.delta = self.softmax.copy()
for i in range(self.batchsize):
self.delta[i, self.label[i]] -= 1
return self.delta
計(jì)算損失
def cal_loss(self, prediction, label):
self.label = label
self.prediction = prediction
self.predict(prediction)
self.loss = 0
for i in range(self.batchsize):
self.loss += np.log(np.sum(np.exp(prediction[i]))) - prediction[i,
label[i]]
return self.loss
運(yùn)行效果
網(wǎng)絡(luò)結(jié)構(gòu)略有不同
早期由于計(jì)算資源有限禀横,S2到C3的連接方式是規(guī)定好的屁药,如圖
而在算力發(fā)達(dá)的今天,我們便不再規(guī)定二者之間的連接方法
如圖所示初始化類對(duì)象柏锄,設(shè)置epoch酿箭、batchsize,開始訓(xùn)練
在訓(xùn)練5個(gè)epoch即5輪后趾娃,訓(xùn)練集上的正確率達(dá)到了98.3%缭嫡,驗(yàn)證集上達(dá)到了97.8%
結(jié)語(yǔ)
算上在家里“天倫之樂(lè)”的時(shí)間,這個(gè)Lenet5我足足寫了三個(gè)星期抬闷,我看到有博主理解和復(fù)寫這個(gè)寫了小半年妇蛀。關(guān)于實(shí)驗(yàn)課,老師的要求是每一句代碼都要手寫笤成,完全不要農(nóng)夫三拳评架。這樣的初衷和目的當(dāng)然是好我能理解,但是在要求的一個(gè)星期的時(shí)間內(nèi)真的有新手能完完整整的手寫出來(lái)嗎疹启?同學(xué)們真的不會(huì)為了完成目標(biāo)而采取特殊手段嗎古程?這都是值得思考的問(wèn)題。在實(shí)驗(yàn)上我已經(jīng)鴿了太久了hhhh喊崖,能過(guò)就趕快過(guò)看后面的吧≌跄ィ現(xiàn)在先吃飯去咯