BP神經網絡樣本數多少有什麼影響 ?

BP神經網絡樣本數有什麼影響

學習神經網絡這段時間,有一個疑問,BP神經網絡中訓練的次數指的網絡的迭代次數,如果有a個樣本,每個樣本訓練次數n,則網絡一共迭代an次,在n>>a 情況下 , 網絡在不停的調整權值,減小誤差,跟樣本數似乎關係不大。而且,a大了的話訓練時間必然會變長。
換一種說法,將你的數據集看成一個固定值, 那麼樣本集與測試集 也可以按照某種規格確定下來如7:3 所以如何看待 樣本集的多少與訓練結果呢? 或者說怎麼使你的網絡更加穩定,更加符合你的所需 。

我嘗試從之前的一個例子中看下區別

如何用70行Java代碼實現深度神經網絡算法

作者其實是實現了一個BP神經網絡 ,不多說,看最後的例子

一個運用神經網絡的例子
最後我們找個簡單例子來看看神經網絡神奇的效果。爲了方便觀察數據分佈,我們選用一個二維座標的數據,下面共有4個數據,方塊代表數據的類型爲1,三角代表數據的類型爲0,可以看到屬於方塊類型的數據有(1,2)和(2,1),屬於三角類型的數據有(1,1),(2,2),現在問題是需要在平面上將4個數據分成1和0兩類,並以此來預測新的數據的類型。

這裏寫圖片描述
圖片描述

我們可以運用邏輯迴歸算法來解決上面的分類問題,但是邏輯迴歸得到一個線性的直線做爲分界線,可以看到上面的紅線無論怎麼擺放,總是有一個樣本被錯誤地劃分到不同類型中,所以對於上面的數據,僅僅一條直線不能很正確地劃分他們的分類,如果我們運用神經網絡算法,可以得到下圖的分類效果,相當於多條直線求並集來劃分空間,這樣準確性更高。
這裏寫圖片描述
圖片描述

簡單粗暴,用作者的代碼運行後 訓練5000次 。根據訓練結果來預測一條新數據的分類(3,1)

這裏寫圖片描述

預測值 (3,1)的結果跟(1,2)(2,1)屬於一類 屬於正方形

這時如果我們去掉 2個樣本,則樣本輸入變成如下

//設置樣本數據,對應上面的4個二維座標數據
  double[][] data = new double[][]{{1,2},{2,2}};
 //設置目標數據,對應4個座標數據的分類
  double[][] target = new double[][]{{1,0},{0,1}};

這裏寫圖片描述

這裏寫圖片描述

則(3,1)結果變成了三角形,

如果你選前兩個點 你會發現直接一條中間線就可以區分 這時候的你的結果跟之前4個點時有區別 so 你得增加樣本 直到這些樣本按照你所想要的方式分類 ,所以樣本的多少 重要性體現在,樣本得能反映所有的特徵值(也就是輸入值) ,樣本多少或者特徵(本例子指點的位置特徵)決定的你的網絡的訓練結果,!!!這是 我們反推出來的結果 。這裏距離深度學習好像近了一步。

另外,這個70行代碼的神經網絡沒有保存你訓練的網絡 ,所以你每次運行都是重新訓練的網絡。其實,在你訓練過後 權值已經確定了下來,我們確定網絡也就是根據權值,so只要把訓練後的權值保存下來,將需要分類的數據按照這種權值帶入網絡,即可得到輸出值,也就是一旦網絡確定, 權值也就確定,一個輸入對應一個固定的輸出,不會再次改變!個人見解。

最後附上作者的源碼,作者的文章見開頭鏈接
下面的實現程序BpDeep.java可以直接拿去使用,

import java.util.Random;
public class BpDeep{
    public double[][] layer;//神經網絡各層節點
    public double[][] layerErr;//神經網絡各節點誤差
    public double[][][] layer_weight;//各層節點權重
    public double[][][] layer_weight_delta;//各層節點權重動量
    public double mobp;//動量係數
    public double rate;//學習係數

    public BpDeep(int[] layernum, double rate, double mobp){
        this.mobp = mobp;
        this.rate = rate;
        layer = new double[layernum.length][];
        layerErr = new double[layernum.length][];
        layer_weight = new double[layernum.length][][];
        layer_weight_delta = new double[layernum.length][][];
        Random random = new Random();
        for(int l=0;l<layernum.length;l++){
            layer[l]=new double[layernum[l]];
            layerErr[l]=new double[layernum[l]];
            if(l+1<layernum.length){
                layer_weight[l]=new double[layernum[l]+1][layernum[l+1]];
                layer_weight_delta[l]=new double[layernum[l]+1][layernum[l+1]];
                for(int j=0;j<layernum[l]+1;j++)
                    for(int i=0;i<layernum[l+1];i++)
                        layer_weight[l][j][i]=random.nextDouble();//隨機初始化權重
            }   
        }
    }
    //逐層向前計算輸出
    public double[] computeOut(double[] in){
        for(int l=1;l<layer.length;l++){
            for(int j=0;j<layer[l].length;j++){
                double z=layer_weight[l-1][layer[l-1].length][j];
                for(int i=0;i<layer[l-1].length;i++){
                    layer[l-1][i]=l==1?in[i]:layer[l-1][i];
                    z+=layer_weight[l-1][i][j]*layer[l-1][i];
                }
                layer[l][j]=1/(1+Math.exp(-z));
            }
        }
        return layer[layer.length-1];
    }
    //逐層反向計算誤差並修改權重
    public void updateWeight(double[] tar){
        int l=layer.length-1;
        for(int j=0;j<layerErr[l].length;j++)
            layerErr[l][j]=layer[l][j]*(1-layer[l][j])*(tar[j]-layer[l][j]);

        while(l-->0){
            for(int j=0;j<layerErr[l].length;j++){
                double z = 0.0;
                for(int i=0;i<layerErr[l+1].length;i++){
                    z=z+l>0?layerErr[l+1][i]*layer_weight[l][j][i]:0;
                    layer_weight_delta[l][j][i]= mobp*layer_weight_delta[l][j][i]+rate*layerErr[l+1][i]*layer[l][j];//隱含層動量調整
                    layer_weight[l][j][i]+=layer_weight_delta[l][j][i];//隱含層權重調整
                    if(j==layerErr[l].length-1){
                        layer_weight_delta[l][j+1][i]= mobp*layer_weight_delta[l][j+1][i]+rate*layerErr[l+1][i];//截距動量調整
                        layer_weight[l][j+1][i]+=layer_weight_delta[l][j+1][i];//截距權重調整
                    }
                }
                layerErr[l][j]=z*layer[l][j]*(1-layer[l][j]);//記錄誤差
            }
        }
    }

    public void train(double[] in, double[] tar){
        double[] out = computeOut(in);
        updateWeight(tar);
    }
}

下面是這個測試程序BpDeepTest.java的源碼:

import java.util.Arrays;
public class BpDeepTest{
    public static void main(String[] args){
        //初始化神經網絡的基本配置
        //第一個參數是一個整型數組,表示神經網絡的層數和每層節點數,比如{3,10,10,10,10,2}表示輸入層是3個節點,輸出層是2個節點,中間有4層隱含層,每層10個節點
        //第二個參數是學習步長,第三個參數是動量係數
        BpDeep bp = new BpDeep(new int[]{2,10,2}, 0.15, 0.8);

        //設置樣本數據,對應上面的4個二維座標數據
        double[][] data = new double[][]{{1,2},{2,2},{1,1},{2,1}};
        //設置目標數據,對應4個座標數據的分類
        double[][] target = new double[][]{{1,0},{0,1},{0,1},{1,0}};

        //迭代訓練5000次
        for(int n=0;n<5000;n++)
            for(int i=0;i<data.length;i++)
                bp.train(data[i], target[i]);

        //根據訓練結果來檢驗樣本數據
        for(int j=0;j<data.length;j++){
            double[] result = bp.computeOut(data[j]);
            System.out.println(Arrays.toString(data[j])+":"+Arrays.toString(result));
        }

        //根據訓練結果來預測一條新數據的分類
        double[] x = new double[]{3,1};
        double[] result = bp.computeOut(x);
        System.out.println(Arrays.toString(x)+":"+Arrays.toString(result));
    }
}

每天都有收穫!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章