這篇文章給大家分享的是有關(guān)spl中如何實現(xiàn)手寫數(shù)字識別的內(nèi)容。小編覺得挺實用的,因此分享給大家做個參考,一起跟隨小編過來看看吧。
創(chuàng)新互聯(lián)專注于五原企業(yè)網(wǎng)站建設(shè),響應(yīng)式網(wǎng)站,購物商城網(wǎng)站建設(shè)。五原網(wǎng)站建設(shè)公司,為五原等地區(qū)提供建站服務(wù)。全流程按需網(wǎng)站設(shè)計,專業(yè)設(shè)計,全程項目跟蹤,創(chuàng)新互聯(lián)專業(yè)和態(tài)度為您提供的服務(wù)
識別手寫的阿拉伯數(shù)字,對于人類來說十分簡單,但是對于程序來說還是有些復(fù)雜的。
不過隨著機器學(xué)習(xí)技術(shù)的普及,使用10幾行代碼,實現(xiàn)一個能夠識別手寫數(shù)字的程序,并不是一件難事。這是因為有太多的機器學(xué)習(xí)模型可以拿來直接用,比如tensorflow、caffe,在python下都有現(xiàn)成的安裝包,寫一個識別數(shù)字的程序,10幾行代碼足夠了。
然而我想做的,是不借助任何第三方的庫,從零開始,完全自己實現(xiàn)一個這樣的程序。之所以這么做,是因為自己動手實現(xiàn),才能深入了解機器學(xué)習(xí)的原理。
熟悉神經(jīng)網(wǎng)絡(luò)回歸算法的,可以略過這一節(jié)了。
學(xué)習(xí)了一些基本概念,決定使用回歸算法。首先下載了著名的MNIST數(shù)據(jù)集,這個數(shù)據(jù)集有60000個訓(xùn)練樣本,和10000個測試樣本。每個數(shù)字圖片都是28*28的灰度圖片,所以輸入可以認為是一個28*28的矩陣,也可以認為是一個28*28=784個像素值。
這里定義一個模型用于判斷一個圖片數(shù)字,每個模型包括每個輸入的權(quán)重,加一個截距,最后再做個歸一。模型的表達式:
Out5= sigmoid(X0*W0+ X1*W1+……X783*W783+bias)
X0到X783是784個輸入,W0到W783是784個權(quán)重,bias是一個常量。sigmoid函數(shù)可以將較大范圍的數(shù)擠壓到(0,1)區(qū)間內(nèi),也就是歸一。
例如我們用這一組權(quán)重和bias來判斷數(shù)字5,期望當圖片是5時輸出是1,當不是5時輸出是0。然后訓(xùn)練的過程就是根據(jù)每個樣本的輸入,計算Out5的值和正確值(0或1)的差距,然后根據(jù)這個差距,調(diào)整權(quán)重和bias。轉(zhuǎn)換一下公式,就是在努力使得(Out5-正確值)接近于0,即所謂損失最小。
同理,10個數(shù)字就要有10套模型,每個判斷不同的數(shù)字。訓(xùn)練好以后,一個圖片來了,用這10套模型進行計算,哪個模型計算的結(jié)果更接近于1,就認為這個圖片是哪個數(shù)字。
按照上面的思路,使用集算器的SPL(結(jié)構(gòu)化處理語言)來編碼實現(xiàn):
A | B | C | |
1 | =file("train-imgs.btx").cursor@bi() | ||
2 | >x=[],wei=[],bia=[],v=0.0625,cnt=0 | ||
3 | for 10 | >wei.insert(0,[to(28*28).(0)]), bia.insert(0,0.01) | |
4 | for 50000 | >label=A1.fetch(1)(1) | |
5 | >y=to(10).(0), y(label+1)=1,x=[] | ||
6 | >x.insert(0,A1.fetch(28*28)) | >x=x.(~/255) | |
7 | =wei.(~**x).(~.sum()) ++ bia | ||
8 | =B7.(1/(1+exp(-~))) | ||
9 | =(B8--y)**(B8.(1-~))**B8 | ||
10 | for 10 | >wei(B10)=wei(B10)--x.(~*v*B9(B10)), bia(B10)=bia(B10) - v*B9(B10) | |
11 | >file("MNIST模型.btx").export@b(wei),file("MNIST模型.btx").export@ba(bia) |
不用再找了,訓(xùn)練模型的所有代碼都在這里了,沒有用到任何第三方庫,下面解析一下:
A1,用游標導(dǎo)入MNIST訓(xùn)練樣本,這個是我轉(zhuǎn)換過的格式,可以被集算器直接訪問;
A2,定義變量:輸入x,權(quán)重wei,訓(xùn)練速度v,等;
A3,B3,初始化10組模型(每組是784個權(quán)重+1個bias);
A4,循環(huán)取5萬個樣本進行訓(xùn)練,10模型同時訓(xùn)練;
B4,取出來label,即這個圖片是幾;
B5,計算正確的10個輸出,保存到變量y;
B6,取出來這個圖片的28*28個像素點作為輸入,C6把每個輸入除以255,這是為了歸一化;
B7,計算X0*W0+ X1*W1+……X783*W783+bias
B8,計算sigmoid(B7)
B9,計算B8的偏導(dǎo),或者叫梯度;
B10,C10,根據(jù)B9的值,循環(huán)調(diào)整10個模型的參數(shù);
A11,訓(xùn)練完畢,把模型保存到文件。
測試一下這個模型的成功率吧,用 SPL 寫了一個測試程序:
A | B | C | |
1 | =file("MNIST模型.btx").cursor@bi() | =[0,1,2,3,4,5,6,7,8,9] | |
2 | >wei=A1.fetch(10),bia=A1.fetch(10) | ||
3 | >cnt=0 | ||
4 | =file("test-imgs.btx").cursor@bi() | ||
5 | for 10000 | >label=A4.fetch(1)(1) | |
6 | >x=[] | ||
7 | >x.insert(0,A4.fetch(28*28)) | >x=x.(~/255) | |
8 | =wei.(~**x).(~.sum()) ++ bia | ||
9 | =B8.(round(1/(1+exp(-~)), 2)) | ||
10 | =B9.pmax() | ||
11 | if label==B1(B10) | >cnt=cnt+1 | |
12 | =A1.close() | ||
13 | =output(cnt/100) |
運行測試,正確率達到了91.1%,我對這個結(jié)果是很滿意的,畢竟這只是一個單層模型,我用TensorFlow的單層模型得到的正確率也是91%多一點。下面解析一下代碼:
A1,導(dǎo)入模型文件;
A2,把模型提取到變量里;
A3,計數(shù)器初始化(用于計算成功率);
A4,導(dǎo)入MNIST測試樣本,這個文件格式是我轉(zhuǎn)換過的;
A5,循環(huán)取1萬個樣本進行測試;
B5,取出來label;
B6,清空輸入;
B7,取出來這個圖片的28*28個像素點作為輸入,每個輸入除以255,這是為了歸一化;
B8,計算X0*W0+ X1*W1+……X783*W783+bias
B9,計算sigmoid(B7)
B10,得到最大值,即最可能的那個數(shù)字;
B11,判斷正確測計數(shù)器加一;
A12,A13,測試結(jié)束,關(guān)閉文件,輸出正確率。
這里要說的優(yōu)化并不是繼續(xù)提高正確率,而是提升訓(xùn)練的速度。想提高正確率的同學(xué)可以嘗試一下這幾個手段:
1. 加一個卷積層;
2. 學(xué)習(xí)速度不要用固定值,而是隨著訓(xùn)練次數(shù)遞減;
3. 權(quán)重的初始值不要使用全零,使用正態(tài)分布;
我認為單純追求正確率的意義不大,因為MNIST數(shù)據(jù)集有些圖片本身就有問題,即使人工也不一定能知道寫的是數(shù)字幾。我用集算器顯示了幾張出錯的圖片,都是書寫十分不規(guī)范的,下面這個圖片很難看出來是2。
下面說重點,要提高訓(xùn)練速度,可以使用并行或集群。使用SPL語言實現(xiàn)并行很簡單,只要使用fork關(guān)鍵字,把上面的代碼稍加處理就可以了。
A | B | C | D | |
1 | =file("train-imgs.btx").cursor@bi() | |||
2 | >x=[],wei=[],bia=[],v=0.0625,cnt=0 | >mode=to(0,9) | ||
3 | >wei=to(28*28).(0) | |||
4 | fork mode | =A1.cursor() | ||
5 | for 50000 | >label=B4.fetch(1)(1) | >y=1,x=[] | |
6 | if label!=A4 | >y=0 | ||
7 | >x.insert(0,B4.fetch(28*28)) | >x=x.(~/255) | ||
8 | =(wei**x).sum() + bia | |||
9 | =1/(1+exp(-C8)) | |||
10 | =(C9-y)*((1-C9))*C9 | |||
11 | >wei=wei--x.(~*v*C10), bia=bia- v*C10 | |||
12 | return wei,bia | |||
13 | =movefile(file("MNIST模型.btx")) | |||
14 | for 10 | >file("MNIST模型.btx").export@ba([A4(A15)(1)]) | ||
15 | for 10 | >file("MNIST模型.btx").export@ba([A4(A16)(2)]) |
使用了并行之后,訓(xùn)練的時間減少差不多一半,而代碼并沒有做太多修改。
使用SPL語言在初期可能會有點不適應(yīng),用得多了會覺得越來越方便:
1. 支持集合運算,比如例子里用到的784個輸入和784個權(quán)重的乘法,直接寫一個**就可以了,如果使用Java或者C,還要自己實現(xiàn)。
2. 數(shù)據(jù)的輸入輸出很方便,可以方便地對文件讀寫。
3. 調(diào)試太方便了,所有變量都直觀可見,這一點比python要好用。
4. 可以單步計算,有了改動不用從頭重來,Java和C做不到這一點,python雖然可以但也不方便,集算器只要點中相應(yīng)格執(zhí)行就可以了。
5. 實現(xiàn)并行和集群很方便,不需要太多的開發(fā)工作量。
6. 支持調(diào)用和被調(diào)用。集算器可以調(diào)用第三方j(luò)ava庫,Java也可以調(diào)用集算器的代碼,例如上面的代碼就可以被Java調(diào)用,實現(xiàn)一個自動填驗證碼的功能。
這樣的編程語言,用在數(shù)學(xué)計算上,實在是最合適不過了。
感謝各位的閱讀!關(guān)于“spl中如何實現(xiàn)手寫數(shù)字識別”這篇文章就分享到這里了,希望以上內(nèi)容可以對大家有一定的幫助,讓大家可以學(xué)到更多知識,如果覺得文章不錯,可以把它分享出去讓更多的人看到吧!