上一節我們自己寫代碼訓練了只有一個神經元的反相器,它雖然只有一點點代碼,但卻讓我們加深了梯度下降算法和反向傳播算法的理解。只要勇敢的邁出這一步後,我們就可以勇敢的嘗試它:深度學習中的hello wold–識別手寫數字。
只有自己寫過的代碼,才能完全的理解它的用意,不管它多爛,多糟糕,它確是完全屬於你的東西。在訓練處反相器以後,我開始大膽的嘗試自己寫一個全連接的神經網絡,來訓練手寫數字。這並不難,也不需要多少代碼,我大概花了半天的時間就寫完了所有的代碼,你也不妨來試試…
你也可以到這裏下載源碼:
mnist-java
代碼結構如下:
可以看到,我的思路是這樣的:
神經網絡(NerualNetwork)由層(Layer)構成,層(Layer)由神經元構成(Nerve)。這種思路非常直觀,但它似乎並不是很好的設計,因爲代碼顯得比較繁瑣。希望你能設計出更好的結構,或許取消Nerve對象,所有邏輯都放在Layer中更好,這樣會大量用到二維數組…
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
public class DeepLearn3Test {
static List<DigitImage> trains = null ;
static List<DigitImage> tests = null;
public static void main(String[] args) {
//load mnist
ReadFile rf1=new ReadFile("train-labels.idx1-ubyte","train-images.idx3-ubyte");
ReadFile rf2=new ReadFile("t10k-labels.idx1-ubyte","t10k-images.idx3-ubyte");
try {
tests = rf2.loadDigitImages();
trains =rf1.loadDigitImages();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
int size[]={784,40,20,10};
NeuralNetwork network = new NeuralNetwork(size);
network.radomInitWandB();
double image[] = new double[784];
for(int kk=0;kk<10;kk++){
//first: set input
for(int count=0;count<trains.size();count++){
for(int i=0;i<784;i++){
image[i] = (int)(trains.get(count).imageData[i]&0xff);
}
network.setInput(image);
//second: forward cal output
double[] output = network.forwardProc();
//third: backward cal delta
double[] y = new double[10];
for(int i=0;i<y.length;i++){
if(i==trains.get(count).label){
y[i] = 1;
}else{
y[i] = 0;
}
}
network.backwarkProc(y);
//fouth: update w and b
network.updateWAndB(0.5);
}
System.out.println("finished train count: "+kk);
}
boolean isTest = true;
//test
if(isTest){
int countCorrect=0;
for(int count=0;count<tests.size();count++){
for(int i=0;i<784;i++){
image[i] = (int)(tests.get(count).imageData[i]&0xff);
}
network.setInput(image);
//second: forward cal output
int number = network.testDigitalImage();
if(number==tests.get(count).label)countCorrect++;
//System.out.println("count is : "+count+" number is: "+number+" label is: "+tests.get(count).label);
}
System.out.println("countCorrect: "+countCorrect);
}
}
}
代碼的思路非常簡單:
1、load mnist
裝在mnist的代碼我是從網上找到的,我不知道怎麼寫,非常感謝牛人的分享。
2、構建神經網絡
這裏構建了一個四層的神經網絡
int size[]={784,40,20,10};
第一層有784個神經元,對應784個像素,中間有兩個隱藏層,輸出層有10個神經元,對應0~9 共10個數字。
2、訓練
2-1 設置輸入
network.setInput(image);
2-1 計算輸出
double[] output = network.forwardProc();
2-2 反向傳播計算誤差
network.backwarkProc(y);
2-3 跟新權重和偏置
network.updateWAndB(0.5);
反覆重複2中的四步,實現對神經網絡的訓練。訓練完成後,進行測試:
測試只需要計算輸出,然後比對輸出是否正確即可。
經過訓練以後,這個神經網絡的能正確識別6945個數字,總共是10000個,因此準確率接近70%。對深度學習而言,這並不是值得興奮的結果,但是,也有值得興奮的地方。想想,如果不經過訓練,我們的神經網絡識別的準確率應該是隨機的,應該在10%左右,這意味着,我們的神經網絡會學習了,雖然它還不夠聰明,考試的成績還不夠好,但他似乎已經找到了方向,他變得可以教育了,這讓我對他的未來充滿期待…
我最近重寫的卷積神經網絡CupCnn,在mnist數據集上準確率已經能達到99%了,CupCnn也是用java寫的,但是代碼更規範,更加模塊化,以下是CupCnn在github上的地址,歡迎大家下載:
CupCnn
更多詳情請參考我的博文:
java寫卷積神經網絡—CupCnn簡介