首先看一下運行效果:
下面是項目整體目錄:
0.實現(xiàn)神經(jīng)網(wǎng)絡(luò)總覽
神經(jīng)網(wǎng)絡(luò)由層、神經(jīng)元、權(quán)重、激活函數(shù)和偏置組成。每層都有一個或者多個神經(jīng)元,每一個神經(jīng)元都和神經(jīng)輸入/輸出連接,這些連接就是權(quán)重。
需要重點強調(diào)一下,一個神經(jīng)網(wǎng)絡(luò)可能有很多隱含層,也可能一個沒有,因為每層的神經(jīng)元數(shù)目也可能不同。然而,輸入輸出層的神經(jīng)元個數(shù)分別等于神經(jīng)輸入/輸出的個數(shù)。
我們?yōu)榱藢崿F(xiàn),需要定義以下的類:
* Neuron: 定義人工神經(jīng)元
* NeuralLayer: 抽象類,定義一個神經(jīng)元層。
* InputLayer: 定義神經(jīng)輸入層
* HiddenLayer:定義輸入層和輸出層之間的層
* OutputLayer: 定義神經(jīng)輸出層。
* InputNeuron: 定義神經(jīng)網(wǎng)絡(luò)輸入中出現(xiàn)的神經(jīng)元。
* NeuralNet:將前面定義的所有類組成一個ANN結(jié)構(gòu)。
除了以上的類,我們還需要為激活函數(shù)定義一個IActivationFunction接口。這是必要的,因為激活函數(shù)與方法類似,需要作為神經(jīng)元的一個屬性進行分配。所以要為激活函數(shù)定義類,這些類需要實現(xiàn)IActivationFunction接口:
* Linear
* Sigmoid
* Step
* HyperTan
第一章的編碼基本完成。除此之外,還需要定義倆個類。一個用于異常處理(NeuralException),另一個用于產(chǎn)生隨機數(shù)(RandomNumberGenerator)。最后,將這些類分別放到倆個包。
1.神經(jīng)元Neuron類
神經(jīng)元類是本章代碼的基礎(chǔ)類。根據(jù)理論,人工神經(jīng)元有如下屬性
* 輸入
* 權(quán)重
* 偏置
* 激活函數(shù)
* 輸出
首先定義神經(jīng)元的各種屬性:
public class Neuron { //神經(jīng)元相關(guān)的權(quán)重 protected ArrayList<Double> weight; //神經(jīng)元的輸入
private ArrayList<Double> input; //這個神經(jīng)元的輸出,由激活函數(shù)產(chǎn)生 private Double output;
//傳遞給激活函數(shù)的值 private Double outputBeforeActivation; //輸入的數(shù)量。如果為0,則表示神經(jīng)元尚未初始化。
private int numberOfInputs = 0; //神經(jīng)元的偏差。除了第一層,其他都應(yīng)該是1.0。 protected Double bias
= 1.0; //神經(jīng)元的激活函數(shù) private IActivationFunction activationFunction; }
當(dāng)實例化神經(jīng)元時,需要指定輸入數(shù)據(jù)的個數(shù)以及激活函數(shù)。構(gòu)造函數(shù)如下:
public Neuron(int numberofinputs,IActivationFunction iaf){
numberOfInputs=numberofinputs; weight=new ArrayList<>(numberofinputs+1);
input=new ArrayList<>(numberofinputs); activationFunction=iaf; }
注意,為偏置定義另一個權(quán)重。一個重要的步驟是初始化神經(jīng)元,也就是為權(quán)重賦初始值。這主要在init()方法中完成,通過隨機數(shù)生成器靜態(tài)類RandomNumberGenerator生成隨機數(shù),賦值權(quán)重。注意:設(shè)置權(quán)重值時需要防止權(quán)重數(shù)組越界。
public void init(){ if(numberOfInputs>0){ for(int i=0;i<=numberOfInputs;i++){
double newWeight = RandomNumberGenerator.GenerateNext(); try{
this.weight.set(i, newWeight); } catch(IndexOutOfBoundsException iobe){
this.weight.add(newWeight); } } } }
最后,在calc()方法中計算輸出值:
public void calc(){ outputBeforeActivation=0.0; if(numberOfInputs>0){
if(input!=null && weight!=null){ for(int i=0;i<=numberOfInputs;i++){
outputBeforeActivation+=(i==numberOfInputs?bias:input.get(i))*weight.get(i); }
} } output=activationFunction.calc(outputBeforeActivation); }
首先需要對所有輸入和權(quán)重的成績進行求和(偏置乘最后一個權(quán)重,i==Number-OfInputs),然后將得出的結(jié)果保存在屬性outputBeforeActivation中。激活函數(shù)用這個值計算神經(jīng)元的輸出。
總代碼如下:
package neuralnet; import java.util.ArrayList; public class Neuron {
//神經(jīng)元相關(guān)的權(quán)重 protected ArrayList<Double> weight; //神經(jīng)元的輸入 private
ArrayList<Double> input; //這個神經(jīng)元的輸出,由激活函數(shù)產(chǎn)生 private Double output; //傳遞給激活函數(shù)的值
private Double outputBeforeActivation; //輸入的數(shù)量。如果為0,則表示神經(jīng)元尚未初始化。 private int
numberOfInputs = 0; //神經(jīng)元的偏差。除了第一層,其他都應(yīng)該是1.0。 protected Double bias = 1.0;
//神經(jīng)元的激活函數(shù) private IActivationFunction activationFunction; public Neuron(){ }
public Neuron(int numberofinputs){ numberOfInputs=numberofinputs; weight=new
ArrayList<>(numberofinputs+1); input=new ArrayList<>(numberofinputs); } public
Neuron(int numberofinputs,IActivationFunction iaf){
numberOfInputs=numberofinputs; weight=new ArrayList<>(numberofinputs+1);
input=new ArrayList<>(numberofinputs); activationFunction=iaf; } public void
init(){ if(numberOfInputs>0){ for(int i=0;i<=numberOfInputs;i++){ double
newWeight = RandomNumberGenerator.GenerateNext(); try{ this.weight.set(i,
newWeight); } catch(IndexOutOfBoundsException iobe){
this.weight.add(newWeight); } } } } public void setInputs(double [] values){
if(values.length==numberOfInputs){ for(int i=0;i<numberOfInputs;i++){ try{
input.set(i, values[i]); } catch(IndexOutOfBoundsException iobe){
input.add(values[i]); } } } } public void setInputs(ArrayList<Double> values){
if(values.size()==numberOfInputs){ input=values; } } public ArrayList<Double>
getArrayInputs(){ return input; } public double[] getInputs(){ double[] inputs
= new double[numberOfInputs]; for (int i=0;i<numberOfInputs;i++){
inputs[i]=this.input.get(i); } return inputs; } public void setInput(int
i,double value){ if(i>=0 && i<numberOfInputs){ try{ input.set(i, value); }
catch(IndexOutOfBoundsException iobe){ input.add(value); } } } public double
getInput(int i){ return input.get(i); } public double[] getWeights(){ double[]
weights = new double[numberOfInputs+1]; for(int i=0;i<=numberOfInputs;i++){
weights[i]=weight.get(i); } return weights; } public ArrayList<Double>
getArrayWeights(){ return weight; } public void updateWeight(int i, double
value){ if(i>=0 && i<=numberOfInputs){ weight.set(i, value); } } public int
getNumberOfInputs(){ return this.numberOfInputs; } public void setWeight(int
i,double value) throws NeuralException{ if(i>=0 && i<numberOfInputs){
this.weight.set(i, value); } else{ throw new NeuralException("Invalid weight
index"); } } public double getOutput(){ return output; } public void calc(){
outputBeforeActivation=0.0; if(numberOfInputs>0){ if(input!=null &&
weight!=null){ for(int i=0;i<=numberOfInputs;i++){
outputBeforeActivation+=(i==numberOfInputs?bias:input.get(i))*weight.get(i); }
} } output=activationFunction.calc(outputBeforeActivation); } public void
setActivationFunction(IActivationFunction iaf){ this.activationFunction=iaf; }
public double getOutputBeforeActivation(){ return outputBeforeActivation; } }
2.NeuralLayer類
在這個類中,將把在同一層中對齊的神經(jīng)元分成一組。因為一層需要將值傳遞給另一層,也需要定義層與層之間的連接。類的屬性定義如下:
//這一層的神經(jīng)元數(shù)量 protected int numberOfNeuronsInLayer; //這一層的神經(jīng)元 private
ArrayList<Neuron> neuron; //激勵函數(shù) protected IActivationFunction activationFnc;
//將值提供給此層的前一層 protected NeuralLayer previousLayer; protected NeuralLayer
nextLayer; protected ArrayList<Double> input; protected ArrayList<Double>
output; protected int numberOfInputs;
這個類是抽象的,整整可實例化的層類是InputLayer、HiddenLayer和Outp-utLayer。創(chuàng)建一個類是,必須使用另一個類的構(gòu)造函數(shù),這幾個類具有相似的構(gòu)造函數(shù)。
而層的初始化和計算都和神經(jīng)元一樣,他們也實現(xiàn)了init()方法和calc() 方法。生命欸protected類型,確保了只有子類可以調(diào)用或覆蓋這些方法。
全部的代碼如下:
package neuralnet; import java.util.ArrayList; public abstract class
NeuralLayer { //這一層的神經(jīng)元數(shù)量 protected int numberOfNeuronsInLayer; //這一層的神經(jīng)元
private ArrayList<Neuron> neuron; //激勵函數(shù) protected IActivationFunction
activationFnc; //將值提供給此層的前一層 protected NeuralLayer previousLayer; protected
NeuralLayer nextLayer; protected ArrayList<Double> input; protected
ArrayList<Double> output; protected int numberOfInputs; public NeuralLayer(int
numberofneurons){ this.numberOfNeuronsInLayer=numberofneurons; neuron = new
ArrayList<>(numberofneurons); output = new ArrayList<>(numberofneurons); }
public NeuralLayer(int numberofneurons,IActivationFunction iaf){
this.numberOfNeuronsInLayer=numberofneurons; this.activationFnc=iaf; neuron =
new ArrayList<>(numberofneurons); output = new ArrayList<>(numberofneurons); }
public int getNumberOfNeuronsInLayer(){ return numberOfNeuronsInLayer; } public
ArrayList<Neuron> getListOfNeurons(){ return neuron; } protected NeuralLayer
getPreviousLayer(){ return previousLayer; } protected NeuralLayer
getNextLayer(){ return nextLayer; } protected void setPreviousLayer(NeuralLayer
layer){ previousLayer=layer; } protected void setNextLayer(NeuralLayer layer){
nextLayer=layer; } protected void init(){ if(numberOfNeuronsInLayer>=0){
for(int i=0;i<numberOfNeuronsInLayer;i++){ try{
neuron.get(i).setActivationFunction(activationFnc); neuron.get(i).init(); }
catch(IndexOutOfBoundsException iobe){ neuron.add(new
Neuron(numberOfInputs,activationFnc)); neuron.get(i).init(); } } } } protected
void setInputs(ArrayList<Double> inputs){ this.numberOfInputs=inputs.size();
this.input=inputs; } protected void calc(){ if(input!=null && neuron!=null){
for(int i=0;i<numberOfNeuronsInLayer;i++){ neuron.get(i).setInputs(this.input);
neuron.get(i).calc(); try{ output.set(i,neuron.get(i).getOutput()); }
catch(IndexOutOfBoundsException iobe){ output.add(neuron.get(i).getOutput()); }
} } } protected ArrayList<Double> getOutputs(){ return output; } protected
Neuron getNeuron(int i){ return neuron.get(i); } protected void setNeuron(int
i, Neuron _neuron){ try{ this.neuron.set(i, _neuron); }
catch(IndexOutOfBoundsException iobe){ this.neuron.add(_neuron); } } }
3.ActivationFunction接口
在定義NeerualNetwork類之前,先看接口的Java代碼示例:
public interface IActivationFunction { double calc(double x); public enum
ActivationFunctionENUM { STEP, LINEAR, SIGMOID, HYPERTAN } }
其中calc()方法屬于實現(xiàn)IActivationFunction接口的特定的激活函數(shù)類,例如Sigmoid函數(shù)。
public class Sigmoid implements IActivationFunction { private double a=1.0;
public Sigmoid(){ } public Sigmoid(double value){ this.setA(value); } public
void setA(double value){ this.a=value; } @Override public double calc(double
x){ return 1.0/(1.0+Math.exp(-a*x)); } }
這也是多態(tài)性的一個示例,即在相同的函數(shù)名下,類和方法呈現(xiàn)不同的行為,產(chǎn)生靈活的應(yīng)用。
4.神經(jīng)網(wǎng)絡(luò)(NeuralNet)類
最后,定義神經(jīng)網(wǎng)絡(luò)類。到目前為止,我們已經(jīng)知道,神經(jīng)網(wǎng)絡(luò)在神經(jīng)層中組織神經(jīng)元,且每個神經(jīng)網(wǎng)絡(luò)至少有倆層,一個用來接收收入,一個用來處理輸出,還有一個數(shù)量可變的隱含層。因此,除了具有和神經(jīng)元以及NeuralLary類相似的屬性之外,Neural還將擁有這幾個屬性,如numberOfInputs,numberOfOutputs等。
private InputLayer inputLayer; private ArrayList<HiddenLayer> hiddenLayer;
private OutputLayer outputLayer; private int numberOfHiddenLayers; private int
numberOfInputs; private int numberOfOutputs; private ArrayList<Double> input;
private ArrayList<Double> output;
這個類的構(gòu)造函數(shù)比前面類的參數(shù)更多:
public NeuralNet(int numberofinputs,int numberofoutputs, int []
numberofhiddenneurons,IActivationFunction[] hiddenAcFnc, IActivationFunction
outputAcFnc)
如果隱含層的數(shù)量是可變的,我們還應(yīng)該考慮到可能有多個隱含層或0個隱含層,且對每個隱含層來說,隱藏神經(jīng)元的數(shù)量也是可變的。處理這種可變性的最好方法就是把每個隱含層中的神經(jīng)元數(shù)量表示為一個整數(shù)向量(參數(shù)
numberofhiddenlayers)。此外,需要為每個隱含層定義激活函數(shù),包括輸出層,完成這個目標(biāo)所需要的參數(shù)分別為hiddenActivationFnc和outputAcFnc。
完整實現(xiàn)如下:
public NeuralNet(int numberofinputs,int numberofoutputs, int []
numberofhiddenneurons,IActivationFunction[] hiddenAcFnc, IActivationFunction
outputAcFnc){ numberOfHiddenLayers=numberofhiddenneurons.length;
numberOfInputs=numberofinputs; numberOfOutputs=numberofoutputs;
if(numberOfHiddenLayers==hiddenAcFnc.length){ input=new
ArrayList<>(numberofinputs); inputLayer=new InputLayer(numberofinputs);
if(numberOfHiddenLayers>0){ hiddenLayer=new ArrayList<>(numberOfHiddenLayers);
} for(int i=0;i<numberOfHiddenLayers;i++){ if(i==0){ try{ hiddenLayer.set(i,new
HiddenLayer(numberofhiddenneurons[i], hiddenAcFnc[i],
inputLayer.getNumberOfNeuronsInLayer())); } catch(IndexOutOfBoundsException
iobe){ hiddenLayer.add(new HiddenLayer(numberofhiddenneurons[i],
hiddenAcFnc[i], inputLayer.getNumberOfNeuronsInLayer())); }
inputLayer.setNextLayer(hiddenLayer.get(i)); } else{ try{ hiddenLayer.set(i,
new HiddenLayer(numberofhiddenneurons[i], hiddenAcFnc[i],hiddenLayer.get(i-1)
.getNumberOfNeuronsInLayer() )); } catch(IndexOutOfBoundsException iobe){
hiddenLayer.add(new HiddenLayer(numberofhiddenneurons[i],
hiddenAcFnc[i],hiddenLayer.get(i-1) .getNumberOfNeuronsInLayer() )); }
hiddenLayer.get(i-1).setNextLayer(hiddenLayer.get(i)); } }
if(numberOfHiddenLayers>0){ outputLayer=new
OutputLayer(numberofoutputs,outputAcFnc,
hiddenLayer.get(numberOfHiddenLayers-1) .getNumberOfNeuronsInLayer() );
hiddenLayer.get(numberOfHiddenLayers-1).setNextLayer(outputLayer); } else{
outputLayer=new OutputLayer(numberofinputs, outputAcFnc, numberofoutputs);
inputLayer.setNextLayer(outputLayer); } } }
5.運行程序
代碼如下:
package neuralnet; import neuralnet.math.IActivationFunction; import
neuralnet.math.Linear; import neuralnet.math.RandomNumberGenerator; import
neuralnet.math.Sigmoid; public class NeuralNetConsoleTest { public static void
main(String[] args){ RandomNumberGenerator.seed=0; int numberOfInputs=2; int
numberOfOutputs=1; int[] numberOfHiddenNeurons= { 3 }; IActivationFunction[]
hiddenAcFnc = { new Sigmoid(1.0) } ; Linear outputAcFnc = new Linear(1.0);
System.out.println("Creating Neural Netword..."); NeuralNet nn = new
NeuralNet(numberOfInputs,numberOfOutputs,
numberOfHiddenNeurons,hiddenAcFnc,outputAcFnc); System.out.println("Neural
Network Network..."); double [] neuralInput = { 1.5 , 0.5 };
System.out.println("Feeding the values {1.5;0.5} to the neural network");
double [] neuralOutput; nn.setInputs(neuralInput); nn.calc();
neuralOutput=nn.getOutputs(); System.out.println("OutPut 1:" +
neuralOutput[0]); neuralInput[0] = 1.0; neuralInput[1] = 2.1;
System.out.println("Feeding the values {1.0;2.1} to the neural network");
nn.setInputs(neuralInput); nn.calc(); neuralOutput=nn.getOutputs();
System.out.println("OutPut 2:" + neuralOutput[0]); } }
到此就完成了我們神經(jīng)網(wǎng)絡(luò)的全部代碼:下面是源代碼壓縮包。有需要的同學(xué)可以下載運行。
下載鏈接 <https://files-cdn.cnblogs.com/files/godoforange/neuralnet.zip>
熱門工具 換一換