官术网_书友最值得收藏!

2.4 用代碼實現一個感知機

了解感知機的運作原理之后,如果你是程序員或者你習慣用代碼去表示,那么現在就可以動手試試了!我們挑一個受眾面比較廣的語言作為基礎語言——Java,構建一個感知機。

2.4.1 Neuroph:一個基于Java的神經網絡框架

在整個第2章中,我們都將用Neuroph作為基礎框架,實現本書提及的神經網絡和相關案例。

下面,我們簡單認識一下Neuroph神經網絡的框架。Neuroph是一個輕量級的開源Java神經網絡框架,結構簡單清晰,非常適合初學者入門,隨著深入學習,可以慢慢了解Neuroph不只是初學者的入門工具,它可以簡化神經網絡的開發和實現,也能實現一些用在生產環境中的算法,圖2-5所示為它的基本類庫。

圖2-5 Neuroph神經網絡的框架

Neuroph由兩部分組成。一部分是由基于Java開發的API組成,你可以方便地利用這部分API創建神經網絡。另一部分是圖形工具,能直接通過簡單的圖形化工具構造一個神經網絡,幫助我們構造多層神經網絡時更加快捷方便。

Java API部分分成三塊,一塊是neuroph.core,這是Neuroph的核心庫,如表2-3所示。

表2-3 Neuroph類庫說明

圖2-6所示最簡化地表示了Neuroph是如何工作的。

圖2-6 Neuroph是如何工作的

(1)神經網絡和學習規則對應,神經網絡按照一定的學習規則訓練相應的數據集。

(2)神經網絡由基礎的層(layer)組成,按照結構分為輸入層、隱藏層和輸出層,我們講神經網絡的時候會具體談到各層的概念。

(3)神經網絡的每層由最基礎的神經元組成。

(4)訓練規則包含一個訓練集,允許有多個訓練集,訓練集由單個訓練元素組成。

接下來,我們看看Neuroph中的類Neuron,它表示單個神經元的構造。如圖2-7所示,這里沒有羅列出Neuron所有的屬性和方法,只展示了與本節相關的部分。

圖2-7 類Neuron

其中inputConnections表示神經元的輸入連接。比如,一個輸入刺激到神經元,就構成一條輸入。由于神經元是可以有多個輸入的,所以神經元和輸入連接是一對多的關系。netInput表示凈輸入,輸入函數的輸出。output表示神經元的輸入。error為神經元的誤差。inputFunction表示輸入函數,通常選擇加權求和。transferFunction表示傳輸函數。

另一個重要的類為Connection。它表示神經元的連接,可以是兩個神經元之間的連接,也可以是輸入信號與神經元之間的連接(實際上,輸入信號可以看作一個輸出永遠等于輸入的簡單神經元), weight表示這個連接的權重。

這個結構已經與之前所述的感知機原理很接近了,下面我們就來構造一個感知機。

2.4.2 代碼實現感知機

回到本節的主題,我們用代碼實現一個感知機,以便更加深入地了解感知機的構造。你也可以選擇跳過所有與代碼相關的章節,不影響閱讀。

首先,我們先想想要構建一個什么樣的感知機:我們將神經元輸入鏈接個數、神經元輸出鏈接個數和傳輸函數類型作為輸入的參數,如圖2-8所示。

圖2-8 感知機

    private void createNetwork(int inputNeuronsCount, int outputNeuronsCount,
    TransferFunctionType transferFunctionType) {
      //設置神經網絡類型,這里我們將類型設置為感知器
      this.setNetworkType(NeuralNetworkType.PERCEPTRON);

      //初始化神經元輸入刺激設置
      NeuronProperties inputNeuronProperties = new NeuronProperties();
      inputNeuronProperties.setProperty("transferFunction",
    TransferFunctionType.LINEAR);

      //創建輸入刺激
      Layer inputLayer = LayerFactory.createLayer(inputNeuronsCount,
    inputNeuronProperties);
      this.addLayer(inputLayer);

      NeuronProperties outputNeuronProperties = new NeuronProperties();
      outputNeuronProperties.setProperty("neuronType", ThresholdNeuron.
    class);
      outputNeuronProperties.setProperty("thresh", new Double(Math.abs
    (Math.random())));
      outputNeuronProperties.setProperty("transferFunction", transferFunctionType);
      //為sigmoid和tanh傳輸函數設置斜率屬性
      outputNeuronProperties.setProperty("transferFunction.slope", new Double(1));
    //create一個神經元的輸出
    Layer outputLayer = LayerFactory.createLayer(outputNeuronsCount,
  outputNeuronProperties);
    this.addLayer(outputLayer);

    //在輸入和輸出層中建立全鏈接
    ConnectionFactory.fullConnect(inputLayer, outputLayer);

    //為神經網絡設置默認輸入輸出
    NeuralNetworkFactory.setDefaultIO(this);
                this.setLearningRule(new BinaryDeltaRule());
   }

至此,一個簡單的感知機已經建立,調用這個感知機可以處理諸如水果分類之類的簡單問題。我們將訓練這個神經網絡,讓它具有記憶和解決簡單問題的能力。

2.4.3 感知機學習一個簡單邏輯運算

我們已經建立了一個簡單的感知機,本節中我們將訓練這個感知機,并讓學習邏輯運算AND。

首先,我們列出邏輯運算AND中的基本規則:

0 and 0=0

0 and 1=0

1 and 0=0

1 and 1=1

也就是說,在感知機接收0、0輸入時,感知機應該響應1;當接收1、1輸入時,應該響應1。當接收0、1或1、0時,應該輸出0。AND主要用在條件判斷中,也就是當兩個子條件都成立時,才往下進行,兩個子條件就用AND連接。

我們先給出完整代碼,再順著代碼的脈絡看看感知機是如何學會AND運算的。

    public static void main(String args[]) {

        //建立訓練集,有兩個輸入一個輸出
        DataSet trainingSet = new DataSet(2, 1);
        trainingSet.addRow(new DataSetRow(new double[]{0, 0}, new double[]{0}));
        trainingSet.addRow(new DataSetRow(new double[]{0, 1}, new double[]{0}));
        trainingSet.addRow(new DataSetRow(new double[]{1, 0}, new double[]{0}));
        trainingSet.addRow(new DataSetRow(new double[]{1, 1}, new double[]{1}));

        //建立一個感知機,定義輸入刺激是2個,感知機輸出是1個,這里我們調用Neuroph提供
    的Perceptron類。
        NeuralNetwork myPerceptron = new Perceptron(2, 1);
        LearningRule lr =myPerceptron.getLearningRule();

        lr.addListener(this);
        //開始學習訓練集
        myPerceptron.learn(trainingSet);
        //測試感知機是否正確輸出,打印
        System.out.println("Testing trained perceptron");
        testNeuralNetwork(myPerceptron, trainingSet);
    }

我們運行程序,結果如下:

    [main] INFO org.neuroph.core.learning.LearningRule - Learning Started
    1. iterate
    2. iterate
    3. iterate
    4. iterate
    5. iterate
    [main] INFO org.neuroph.core.learning.LearningRule - Learning Stoped
    5. iterate
    Testing trained perceptron
    Input: [0.0, 0.0] Output: [0.0]
    Input: [0.0, 1.0] Output: [0.0]
    Input: [1.0, 0.0] Output: [0.0]
    Input: [1.0, 1.0] Output: [1.0]

結果輸出表明,網絡經過5次迭代后,誤差為0,初始的權值和偏置是隨機數,接著網絡根據輸入的訓練數據迭代學習,不斷調整權值和偏置,并最終記憶AND邏輯運算。從測試數據中可以看到,4個輸出已經完全正確,該網絡已經對AND邏輯操作有正確的響應。

同理,只需要改變訓練數據,就可以讓感知機記憶其他內容,比如OR邏輯運算。

0 or 0=0

0 or 1=1

1 or 0=1

1 or 1=1

我們依據OR邏輯運算的規則,更改上例的訓練數據:

    trainingSet.addRow(new DataSetRow(new double[]{0, 0}, new double[]{0}));
    trainingSet.addRow(new DataSetRow(new double[]{0, 1}, new double[]{1}));
    trainingSet.addRow(new DataSetRow(new double[]{1, 0}, new double[]{1}));
    trainingSet.addRow(new DataSetRow(new double[]{1, 1}, new double[]{1}));

然后測試該網絡,給出的輸出可能為:

    [main] INFO org.neuroph.core.learning.LearningRule - Learning Started
    1. iterate
    2. iterate
    3. iterate
    4. iterate
    5. iterate
    6. iterate
    7. iterate
    8. iterate
    9. iterate
    10. iterate
    11. iterate
    12. iterate
    [main] INFO org.neuroph.core.learning.LearningRule - Learning Stoped
    12. iterate
    Testing trained perceptron
    Input: [0.0, 0.0] Output: [0.0]
    Input: [0.0, 1.0] Output: [1.0]
    Input: [1.0, 0.0] Output: [1.0]
    Input: [1.0, 1.0] Output: [1.0]

可以看到,經過12次迭代,網絡已經正確記憶了OR邏輯運算。到這里,細心的讀者可以發現,在學習AND邏輯運算的時候,網絡進行了5次迭代,但在學習OR邏輯運算的時候迭代了12次。這種結果是完全隨機的,實際上在網絡建立的時候,初始權值是隨機產生的(絕對值小于1),因此迭代幾次才能使網絡給出正確輸出是不確定的,但不論初始值如何,經過有限次迭代,網絡一定能完全記住訓練數據。

2.4.4 XOR問題

大家還記得羅森布拉特嗎?他是我們在第0章提到的那位神經網絡中感知機奠基人之一,并且還是利用計算機模擬了感知機模型的天才,他是怎么被自己的中學同學明斯基找到感知機漏洞,郁悶至死的呢?對!就是因為XOR問題,我們來看看XOR能不能在我們的感知機模型中得到解決。

老規矩,先列出XOR的運算規則:

0 xor 0=0

0 xor 1=1

1 xor 0=1

1 xor 1=0

可以看到,當輸入的兩個值相等時,XOR返回0,否則得到1。

依然使用給出的感知機學習XOR會得到什么結果呢?感知機還可以正常工作嗎?

我們將訓練數據替換成如下代碼:

    trainingSet.addRow(new DataSetRow(new double[]{0, 0}, new double[]{0}));
    trainingSet.addRow(new DataSetRow(new double[]{0, 1}, new double[]{1}));
    trainingSet.addRow(new DataSetRow(new double[]{1, 0}, new double[]{1}));
    trainingSet.addRow(new DataSetRow(new double[]{1, 1}, new double[]{0}));

再來運行一下程序:

    …...
    573884. iterate
    573885. iterate
    573886. iterate
    573887. iterate
    573888. iterate
    573889. iterate
    573890. iterate
    573891. iterate
    573892. iterate
    573893. iterate
    573894. iterate
    ……

我暫停了,已經計算了60w次仍然沒有得到結果。

如果不手動終止程序,它將永遠運行下去。讀者如果執行此程序,一定也會得到一樣的結果。這是什么原因呢?為什么感知機可以輕松地記憶AND和OR運算,但是無法學習XOR呢?

先不解答此問題,我們先來構造一個神經網絡,大家且往下看。

主站蜘蛛池模板: 夏津县| 汽车| 察雅县| 谢通门县| 登封市| 偏关县| 兰考县| 吉安市| 贵阳市| 镇坪县| 涿州市| 长兴县| 新沂市| 潍坊市| 绿春县| 红安县| 仙游县| 永修县| 桐乡市| 明星| 错那县| 抚州市| 大连市| 赤城县| 喀什市| 丹寨县| 新龙县| 彭阳县| 英吉沙县| 防城港市| 安阳县| 麻江县| 尚义县| 怀远县| 久治县| 平阳县| 浪卡子县| 木兰县| 邹城市| 库车县| 尚义县|