深入淺出神經網路與深度學習--神經元感知機單層神經網路介紹(-)
1 概述
寫神經網路與深度學習方面的一些知識,是一直想做的事情。但本人比較懶惰,有點時間想玩點遊戲呀什麼的,一直拖到現在。
也由於現在已經快當爸了,心也沉了下來,才能去梳理一些東西。本文會深入檢出的去看神經網路與深度學習的一些知識,包含一些演算法、基礎等,比較適合初學者。
文筆有限,有些不好理解的地方會配圖及例項。
本文主要使用neuroph做例項進行講解去理解機器學習相關,為什麼會選用這個呢,下邊也有介紹。主要如果用tenseflow與spark等,會使用其他語言,如scala、python,會有一定的學習成本。而neuroph完全是為java提供的演算法庫,執行環境當然是JVM。匯入jar包即可執行。
本文參考不止一本教程及資料,所以有些地方顯得比較雜亂,後續會持續更新,並對此做修改
2.1 神經元
說神經網路,那麼必須先看下生物神經網路的組成與行為方式。
人工神經元模擬了生物神經元的仿生工程。是模仿生物神經元組成的神經網路在接收刺激及訊號傳遞等行為。具體可檢視第二章第一節人工神經元構造。
我們可把神經元歸納為:
l 感覺神經元(傳入神經元):其樹突的末端分佈於身體的外周部,接收來自體內外的刺激,將興奮傳至脊髓和腦。
l 運動神經元(傳出神經元):其軸突大於肌肉和腺體。
l 聯絡神經元(中間神經元):結餘上述2種神經元之間,起著神經元之間技能聯絡的作用,多存在於腦和脊髓裡。
2.2 神經網路
2.2.1 組成
這裡我們要說一個概念:
自組織:每個神經元都自己決定和另外哪些神經元連結,甚至不連結,沒有從一個領導的角色去安排大家的工作,這種自決定的特性,構成了神經元的自主學習(對應與神經網路模型的訓練)。
神經元的這種自組織特性來自於神經網路結構的可塑性,即神經元之間相互連線的突觸隨著動作電位脈衝激勵方式與強度的變化,其傳遞電位的作用可增加或減少,簡單的說就是輸入(樹突)輸出(軸突)部分是可以由連結強度來表示。
3 神經網路
3.1 人工神經元構造
看上圖,我們通過生物神經元機制知道,樹突收集刺激源往後傳遞,刺激源是外界感知,也可以是其他神經元細胞產生的結果(output)作為神經元的輸入(input)。其中w代表刺激強度。而傳遞函式,現階段可簡單理解為把s(n)輸出結果格式化為我們想看到的符號或數字。
由生物神經元我們知道,神經元有其本身的特性,我們通過在訊號處理處增加一個處理輸入源b來模擬這種內部強度。
傳遞函式f:現階段我們可以狹義的理解為格式化輸出結果,將結果變成我們可以使用的一種符號或數字。
那麼我們通過上述,可得到一個公式:
3.2 感知機
3.2.1 定義
網路接收若干輸入,並通過輸入函式、傳輸函式給出一個網路的輸出,我們稱之為感知機,即單一的最簡單的神經網路。
3.2.2 解決一個簡單的問題
並且,我們通過感知機的學習來解決一個例項。
品種 | 顏色 | 形狀 |
橘子 | 1橙色 | 1圓形 |
香蕉 | -1黃色 | -1彎形 |
那麼,如何識別香蕉和橘子呢。
通過公式:
s = p1w1+p2w2+p3w3 + b*1
預設值:b=0,w1=w2=1
對橘子鑑別:
s = 1*1+1*1+0 = 2
香蕉
s = -1*1+-1*1+0 = -2
通過step函式我們得到輸出 1,-1分別為橘子,香蕉
但如上述,如果預設值為別的值,結果可能是不對的,那麼,如何保證隨意輸入引數都能得到正確的結果呢。下邊我們來看感知機是如何學習的
3.2.3 感知機的學習
感知機的訓練方法比較簡單,主要是修改神經網路的權值和偏置。
w(new) = w(old) + ep
b(new) = b(old) + e
e表示誤差,e = t-a, t為期望輸出, a為實際輸出
測試1:預設w1=1,w2=1,b=0
期望結果:1
s = p1w1+p2w2+b=0
e = t-a = 1-0 = 1
w1new = w1old + ep = 1 + 1*1 = 2
w2new = w2old +ep = -1 +1*1 = 0
bnew = bold+e = 0+1 = 1
net = p1w1+p2w2+b = 1*2+1*0+1=3
f(step) = 1;
通過有監督學習的方法來進行糾正誤差。
3.2.4 感知機學習一個簡單的邏輯運算
實現一個and運算邏輯,通過這個簡單的知道他是如何運算及學習的。
package com.neuroph.lxl.sample;
import java.util.Arrays;
import org.neuroph.core.NeuralNetwork;
import org.neuroph.core.data.DataSet;
import org.neuroph.core.data.DataSetRow;
import org.neuroph.core.events.LearningEvent;
import org.neuroph.core.events.LearningEventListener;
import org.neuroph.core.learning.LearningRule;
import org.neuroph.core.learning.SupervisedLearning;
import com.neuroph.lxl.util.IterateExport;
import com.neuroph.lxl.util.Perceptron;
publicclass AndSample implements LearningEventListener{
@Override
publicvoid handleLearningEvent(LearningEvent event) {
SupervisedLearningbp = (SupervisedLearning)event.getSource();
if(event.getEventType() != LearningEvent.Type.LEARNING_STOPPED){
System.out.println(bp.getCurrentIteration()+ ". iteration : "+ bp.getTotalNetworkError());
}
}
publicstaticvoid and(DataSet set){
set.addRow(new DataSetRow(newdouble[]{0,0},newdouble[]{0}));
set.addRow(new DataSetRow(newdouble[]{0,1},newdouble[]{0}));
set.addRow(new DataSetRow(newdouble[]{1,0},newdouble[]{0}));
set.addRow(new DataSetRow(newdouble[]{1,1},newdouble[]{1}));
}
publicstaticvoid xor(DataSet set){
set.addRow(new DataSetRow(newdouble[]{0,0},newdouble[]{0}));
set.addRow(new DataSetRow(newdouble[]{0,1},newdouble[]{1}));
set.addRow(new DataSetRow(newdouble[]{1,0},newdouble[]{1}));
set.addRow(new DataSetRow(newdouble[]{1,1},newdouble[]{0}));
}
publicstaticvoid main(String[] args) {
//建立訓練集,有2個輸入一個輸出
DataSet set = new DataSet(2,1);
//and(set);
xor(set);
NeuralNetwork myp = new Perceptron(2,1);
LearningRule lr = myp.getLearningRule();
lr.addListener(new AndSample());
myp.learn(set);
IterateExport.testNeuralNetwork(myp, set);
}
}
輸出
package com.neuroph.lxl.util;
import java.util.Arrays;
import org.neuroph.core.NeuralNetwork;
import org.neuroph.core.data.DataSet;
import org.neuroph.core.data.DataSetRow;
publicclass IterateExport {
publicstaticvoid testNeuralNetwork(NeuralNetwork<?>neuralNet, DataSet testSet) {
for(DataSetRow testSetRow : testSet.getRows()) {
neuralNet.setInput(testSetRow.getInput());
neuralNet.calculate();
double[] networkOutput = neuralNet.getOutput();
System.out.print("Input: " + Arrays.toString( testSetRow.getInput() ) );
System.out.println(" Output: " + Arrays.toString( networkOutput) );
}
}
}
直接執行檢視即可,對此不做過多的論述。
3.3 單層神經網路
又稱為單層前饋神經網路。
一般情況下不會用單層神經網路解決問題。
3.4 多層神經網路
3.4.1 概述
首先了解下層的概念
l 感知器:感知器是一種雙層神經網路模型,一層為輸入層(輸入刺激),另一層具有計算單元,可以通過監督學習建立模式判別的能力。
l 多層神經網路:也稱為前饋神經網路。
特點:前饋網路的各神經元接受前一級輸入,並輸出到下一級,無反饋。
節點:輸入節點,輸出節點
l 計算單元:可有任意一個輸入,但只有一個輸出,輸出可耦合到任意多個其他節點輸入。
l 層:可見層-輸入和輸出節點;隱層-中間層。
我們把中間的單層神經元擴充套件為2個神經元層,第一隱層,第二隱層;把由輸入的訊號源、隱層及輸出組成的層叫做多層神經網路。
我們現在來看,什麼情況下會用到多層神經網路,用多層神經網路能解決什麼問題。
我們通過一個簡單的例子來說明。
3.4.2 XOR問題
XOR運算規則:
0 xor 0 = 0
0 xor 1 = 1
1 xor 0 = 1
1 xor 1 = 0
我們通過單層神經網路,及上述的感知機去學習並運算,10W次沒有得到結果…….,
什麼原因導致的呢?
回到橙子與香蕉的區分問題,我們知道一點,大多數情況下,水果特徵並不像上述說的那麼明顯,在這種情況下,我們並不能很明顯的去區分特徵。
也就是不能通過一條直線,去分隔2個特徵,即引入一個新概念,線性不可分。如圖所示:
由上圖我們看到XOR問題,我們具體問題具體分析,像XOR這種線性不可分問題。
使用step函式:
第一層上測神經元,p1,p2淨輸入為2p1+2p2-1,對應XOR圖藍色三角圈入
p1/p2 | 0 | 1 |
0 | -1 = 0 | 1 = 1 |
1 | 1 = 1 | 3 = 1 |
下側:-2p1-2p2+3,對應XOR圖綠色三角圈入
p1/p2 | 0 | 1 |
0 | 3 = 1 | 1 = 1 |
1 | 1 = 1 | -1 = 0 |
第二層神經元進行and運算。那麼 第一層上測與下側結果為第二層神經元的輸入,對比2個表格的結果,計算如下:
當 p1=0,p2=0時, 0 and1 = 0
當p1=0,p2=1時,1 and 1= 1
當 p1=1,p2=0時,1 and1 = 1
當 p1=1,p2=1時,1 and0 = 0;
解決了XOR問題。
3.4.3 XOR程式碼實現
package com.neuroph.lxl.sample;
import org.neuroph.core.NeuralNetwork;
import org.neuroph.core.data.DataSet;
import org.neuroph.core.data.DataSetRow;
import org.neuroph.core.events.LearningEvent;
import org.neuroph.core.events.LearningEventListener;
import org.neuroph.core.learning.LearningRule;
import org.neuroph.core.learning.SupervisedLearning;
import org.neuroph.nnet.MultiLayerPerceptron;
import org.neuroph.nnet.learning.BackPropagation;
import org.neuroph.nnet.learning.MomentumBackpropagation;
import org.neuroph.nnet.learning.ResilientPropagation;
import org.neuroph.util.TransferFunctionType;
import com.neuroph.lxl.util.IterateExport;
publicclass XorSample implements LearningEventListener{
@Override
publicvoid handleLearningEvent(LearningEvent event) {
BackPropagation bp = (BackPropagation)event.getSource();
//SupervisedLearning bp= (SupervisedLearning) event.getSource();
if(event.getEventType() != LearningEvent.Type.LEARNING_STOPPED){
System.out.println(bp.getCurrentIteration()+ ". iteration : "+ bp.getTotalNetworkError());
}
}
publicstaticvoid xor(DataSet set){
set.addRow(new DataSetRow(newdouble[]{0,0},newdouble[]{0}));
set.addRow(new DataSetRow(newdouble[]{0,1},newdouble[]{1}));
set.addRow(new DataSetRow(newdouble[]{1,0},newdouble[]{1}));
set.addRow(new DataSetRow(newdouble[]{1,1},newdouble[]{0}));
}
publicstaticvoid main(String[] args) {
//建立訓練集,有2個輸入一個輸出
DataSet set = new DataSet(2,1);
xor(set);
//multiStudy(set);
//設定ResilientPropagation學習規則,作廢,現階段一般用反向傳播機制
resilientStudy(set);
}
privatestaticvoid resilientStudy(DataSet set) {
// 轉移函式採用sigmoid,也可以用tanh之類的
MultiLayerPerceptron mlp = new MultiLayerPerceptron(TransferFunctionType.SIGMOID, 2, 3, 1);
// ResilientPropagation學習規則
mlp.setLearningRule(new ResilientPropagation());
LearningRule learningRule = mlp.getLearningRule();
// 學習
learningRule.addListener(new XorSample());
System.out.println("Training neural network...");
mlp.learn(set);
intiterations = ((SupervisedLearning)mlp.getLearningRule()).getCurrentIteration();
System.out.println("Learned in "+iterations+" iterations");
System.out.println("Testing trained neural network");
IterateExport.testNeuralNetwork(mlp, set);
}
privatestaticvoid multiStudy(DataSet set) {
// 建立多層感知機,輸入層2個神經元,隱含層3個神經元,最後輸出層為1個隱含神經元,
// 使用雙曲正切TANH傳輸函式最後格式化輸出
MultiLayerPerceptron mlp = new MultiLayerPerceptron(TransferFunctionType.TANH, 2, 2, 1);
// 啟用batch模式
if (mlp.getLearningRule() instanceof MomentumBackpropagation)
((MomentumBackpropagation) mlp.getLearningRule()).setBatchMode(true);
//反向誤差傳播
mlp.setLearningRule(new BackPropagation());
LearningRule learningRule = mlp.getLearningRule();
// 學習
learningRule.addListener(new XorSample());
mlp.learn(set);
// 測試感知機
IterateExport.testNeuralNetwork(mlp, set);
// 儲存結果
mlp.save("mlp.nnet");
NeuralNetwork loadedMlp = NeuralNetwork.load("mlp.nnet");
// test loaded neural network
System.out.println("Testingloaded neural network");
IterateExport.testNeuralNetwork(loadedMlp, set);
}
}