從程序員的角度設計一個Java的神經網絡
歡迎大家前往雲+社區,獲取更多騰訊海量技術實踐幹貨哦~
來自維基百科:
人工神經網絡(ANN)或連接系統是受生物神經網絡啟發構成生物大腦的計算系統。這樣的系統通過考慮例子來學習(逐步提高性能)來完成任務,通常沒有任務特定的編程。
用Java或任何其他編程語言設計神經網絡我們需要理解人工神經網絡的結構和功能。
人工神經網絡執行的任務比如有模式識別、從數據中學習以及像專家一樣預測趨勢,而不像傳統的算法方法那樣需要執行一組步驟來實現所定義的目標。人工神經網絡由於其高度交互的網絡結構,可以學習如何自己解決一些任務。
人造神經元具有與人腦神經元相似的結構。一個天然的神經元是由核,樹突和軸突組成的。軸突延伸到幾個分支形成突觸與其他神經元的樹突。
到目前為止,我們已經區分了神經元的結構和相連神經元的網絡。另一個重要方面是分別與單個神經元相關的神經網絡的處理或計算。自然神經元是信號處理器 - 它們在樹突中接收可以觸發軸突信號的微信號。有一個潛在的閾值,到達的時候,刺激軸突,並傳播信號到其他神經元。因此,我們可以將人造神經元視為一個在輸入中具有信號接收器、在輸出中具有激活單元的東西,其可以發送的信號將被轉發到與圖中所示類似的其他神經元上:
此外,神經元之間的連接具有相應可以修改信號的權重,從而影響神經元的輸出。由於權重是神經網絡的內部因素並影響其輸出,所以可以認為它們是神經網絡的內部學科,調節描述神經元與其他神經元或外部世界的連接的權重將反映神經網絡的能力。
正如Bioinfo Publications所述:
人造神經元接收一個或多個輸入(代表樹突)並將它們相加以產生輸出/ 激活 (代表神經元的軸突)。一般來說每個節點的總和被加權,總和通過激活函數或傳遞函數傳遞。
這個組件為神經網絡處理增加了非線性,這是因為自然神經元具有非線性行為。在一些特殊情況下,它可以是一個線性函數。
維基百科提及到說:
一個標準的計算機芯片電路可以看作是一個激活功能的數字網絡,取決於輸入的是“ON”(1)還是“OFF”(0)。這與神經網絡中的線性感知器的行為類似。然而, 非線性 激活函數允許這樣的網絡僅使用少量的節點來計算特殊問題。使用的流行的激活函數的例子是Sigmoid、雙曲正切、硬極限閾值和純線性。
將這些知識轉化為Java代碼,我們將有一個如下的神經元類:
import java.util.ArrayList; import java.util.List; import edu.neuralnet.core.activation.ActivationFunction; import edu.neuralnet.core.input.InputSummingFunction; /** * Represents a neuron model comprised of(以下內容組成的神經元模型): </br> * <ul> * <li>Summing part(求和部分) - input summing function(輸入求和函數 )</li> * <li>Activation function(激活函數)</li> * <li>Input connections(輸入連接)</li> * <li>Output connections(輸出連接)</li> * </ul> */ public class Neuron { /** * Neuron‘s identifier * 神經元標識符 */ private String id; /** * Collection of neuron‘s input connections (connections to this neuron) * 神經元輸入連接的集合(與此神經元的連接) */ protected List < Connection > inputConnections; /** * Collection of neuron‘s output connections (connections from this to other * neurons) * 神經元輸出連接的集合(從這個到其他神經元的連接) */ protected List < Connection > outputConnections; /** * Input summing function for this neuron * 該神經元的輸入和函數 */ protected InputSummingFunction inputSummingFunction; /** * Activation function for this neuron * 這個神經元的激活函數 */ protected ActivationFunction activationFunction; /** * Default constructor * 默認構造方法 */ public Neuron() { this.inputConnections = new ArrayList < > (); this.outputConnections = new ArrayList < > (); } /** * Calculates the neuron‘s output * 計算神經元輸出 */ public double calculateOutput() { double totalInput = inputSummingFunction.getOutput(inputConnections); return activationFunction.getOutput(totalInput); } ... }
神經元有輸入和輸出連接、輸入求和值和激活函數,那輸入權重在哪裏呢?它們包含在連接本身中,如下所示:
/** * Represents a connection between two neurons an the associated weight. * 表示兩個神經元之間的連接以及相關的權重 */ public class NeuronsConnection { /** * From neuron for this connection (source neuron). This connection is * output connection for from neuron. * 從神經元中獲取這個連接(源神經元)。此連接是來自神經元的輸出連接 */ protected Neuron fromNeuron; /** * To neuron for this connection (target, destination neuron) This * connection is input connection for to neuron. * 對於用於此連接的神經元(目標,目標神經元),此連接是神經元的輸入連接 */ protected Neuron toNeuron; /** * Connection weight * 連接權重 */ protected double weight; /** * Creates a new connection between specified neurons with random weight. * 在具有隨機權重的指定神經元之間創建一個新的連接 * @param fromNeuron * neuron to connect from * @param toNeuron * neuron to connect to */ public NeuronsConnection(Neuron fromNeuron, Neuron toNeuron) { this.fromNeuron = fromNeuron; this.toNeuron = toNeuron; this.weight = Math.random(); } /** * Creates a new connection to specified neuron with specified weight object * 創建與指定權重對象的指定神經元的新連接 * * @param fromNeuron * neuron to connect from * @param toNeuron * neuron to connect to * @param weight * weight for this connection */ public NeuronsConnection(Neuron fromNeuron, Neuron toNeuron, double weight) { this(fromNeuron, toNeuron); this.weight = weight; } /** * Returns weight for this connection * 返回此連接的權重 * @return weight for this connection */ public double getWeight() { return weight; } /** * Set the weight of the connection. * 設置連接的權值 * @param weight * The new weight of the connection to be set */ public void setWeight(double weight) { this.weight = weight; } /** * Returns input of this connection - the activation function result * calculated in the input neuron of this connection. * 返回此連接的輸入 - 在此連接輸入神經元中激活函數計算的結果 * @return input received through this connection */ public double getInput() { return fromNeuron.calculateOutput(); } /** * Returns the weighted input of this connection * 返回此連接的權值輸入 * @return weighted input of the connection */ public double getWeightedInput() { return fromNeuron.calculateOutput() * weight; } /** * Gets from neuron for this connection * 從神經元獲取此連接 * @return from neuron for this connection */ public Neuron getFromNeuron() { return fromNeuron; } /** * Gets to neuron for this connection * 獲取用於此連接的神經元 * @return neuron to set as to neuron */ public Neuron getToNeuron() { return toNeuron; } ... }
連接對象提供權重並負責計算輸入的權值。
求和函數被定義為接口,以便能夠替換神經元的計算策略:
import java.util.List; import edu.neuralnet.core.Connection; /** * Represents the inputs summing part of a neuron also called signal collector. * 神經元的求和部分,也可以稱為信號收集器 */ public interface InputSummingFunction { /** * Performs calculations based on the output values of the input neurons. * 根據輸入神經元的輸出值執行計算 * @param inputConnections * neuron‘s input connections * @return total input for the neuron having the input connections * 總輸入,具有輸入連接的神經元 */ double collectOutput(List<Connection> inputConnections); }
分別實現為:
import java.util.List; import edu.neuralnet.core.Connection; /** * Calculates the weighted sums of the input neurons‘ outputs. * 計算輸入神經元輸出的加權和 */ public final class WeightedSumFunction implements InputSummingFunction { /** * {@inheritDoc} */ @Override public double collectOutput(List<Connection> inputConnections) { double weightedSum = 0d; for (Connection connection : inputConnections) { weightedSum += connection.getWeightedInput(); } return weightedSum; } }
激活函數的接口可以定義如下:
/** * Neural networks activation function interface. * 神經網絡激活函數的接口 */ public interface ActivationFunction { /** * Performs calculation based on the sum of input neurons output. * 基於輸入神經元輸出的和來進行計算 * @param summedInput * neuron‘s sum of outputs respectively inputs for the connected * neuron * * @return Output‘s calculation based on the sum of inputs * 基於輸入和來計算輸出 */ double calculateOutput(double summedInput); }
開始編寫代碼之前需要註意的最後一個問題是神經網絡層。神經網絡由幾個鏈接層組成,形成所謂的多層網絡。
神經層可以分為三類:
- 輸入層
- 隱藏層
- 輸出層
在實踐中,額外的神經層增加了另一個抽象層次的外部刺激,增強了神經網絡認知更復雜知識的能力。
一個圖層類可以被定義為一個有連接的神經元列表:
import java.util.ArrayList; import java.util.List; /** * Neural networks can be composed of several linked layers, forming the * so-called multilayer networks. A layer can be defined as a set of neurons * comprising a single neural net‘s layer. * 神經網絡可以由多個連接層組成,形成所謂的多層網絡, * 一層可以定義為一組包含神經網絡層的神經元 */ public class NeuralNetLayer { /** * Layer‘s identifier * 層次標識符 */ private String id; /** * Collection of neurons in this layer * 該層神經元的集合 */ protected List<Neuron> neurons; /** * Creates an empty layer with an id. * 用ID創建一個空層 * @param id * layer‘s identifier */ public NeuralNetLayer(String id) { this.id = id; neurons = new ArrayList<>(); } /** * Creates a layer with a list of neurons and an id. * 創建一個包含神經元列表和id的層 * @param id * layer‘s identifier 層次標識符 * @param neurons * list of neurons to be added to the layer 添加到該層的神經元列表 */ public NeuralNetLayer(String id, List<Neuron> neurons) { this.id = id; this.neurons = neurons; } ... }
最後,用Java創建一個簡單的神經網絡:
/** * Represents an artificial neural network with layers containing neurons. * 含有神經元層的人工神經網絡 */ public class NeuralNet { /** * Neural network id * 神經網絡ID */ private String id; /** * Neural network input layer * 神經網絡的輸入層 */ private NeuralNetLayer inputLayer; /** * Neural network hidden layers * 神經網絡隱藏的層 */ private List<NeuralNetLayer> hiddenLayers; /** * Neural network output layer * 神經網絡的輸出層 */ private NeuralNetLayer outputLayer; /** * Constructs a neural net with all layers present. * 構造一個具有所有層的神經網絡 * @param id * Neural network id to be set 設置神經網絡標識 * @param inputLayer * Neural network input layer to be set 設置神經網絡的輸入層 * @param hiddenLayers * Neural network hidden layers to be set 設置神經網絡隱藏的層 * @param outputLayer * Neural network output layer to be set 設置神經網絡的輸出層 */ public NeuralNet(String id, NeuralNetLayer inputLayer, List<NeuralNetLayer> hiddenLayers, NeuralNetLayer outputLayer) { this.id = id; this.inputLayer = inputLayer; this.hiddenLayers = hiddenLayers; this.outputLayer = outputLayer; } /** * Constructs a neural net without hidden layers. * 構造一個沒有隱藏層的神經網絡 * @param id * Neural network id to be set 設置神經網絡標識 * @param inputLayer * Neural network input layer to be set 設置神經網絡的輸入層 * @param outputLayer * Neural network output layer to be set 設置神經網絡隱藏的層 */ public NeuralNet(String id, NeuralNetLayer inputLayer, NeuralNetLayer outputLayer) { this.id = id; this.inputLayer = inputLayer; this.outputLayer = outputLayer; } ... }
我們所得到的是一個基於Java的神經網絡層次、神經元和連接的結構定義。我們也談到了一些關於激活函數的內容,並為它們定義了一個接口。為簡單起見,我們省略了各種激活函數的實現以及學習神經網絡的基礎知識。這兩個主題將在本系列的後續文章中介紹。
翻譯人:BAStriver,該成員來自雲+社區翻譯社
原文鏈接:https://dzone.com/articles/designing-a-neural-network-in-java
原文作者:Daniela Kolarova
相關閱讀
通過JS庫Encog實現JavaScript機器學習和神經學網絡
自然語言處理的神經網絡模型初探
如何使用Python超參數的網格搜索ARIMA模型
此文已由作者授權雲加社區發布,轉載請註明文章出處
從程序員的角度設計一個Java的神經網絡