从程序员的角度设计一个Java的神经网络
欢迎大家前往云+社区,获取更多腾讯海量技术实践干货哦~
来自维基百科:
人工神经网络(ANN)或连接系统是受生物神经网络启发构成生物大脑的计算系统。这样的系统通过考虑例子来学习(逐步提高性能)来完成任务,通常没有任务特定的编程。
用Java或任何其他编程语言设计神经网络我们需要理解人工神经网络的结构和功能。
人工神经网络执行的任务比如有模式识别、从数据中学习以及像专家一样预测趋势,而不像传统的算法方法那样需要执行一组步骤来实现所定义的目标。人工神经网络由于其高度交互的网络结构,可以学习如何自己解决一些任务。
人造神经元具有与人脑神经元相似的结构。一个天然的神经元是由核,树突和轴突组成的。轴突延伸到几个分支形成突触与其他神经元的树突。
到目前为止,我们已经区分了神经元的结构和相连神经元的网络。另一个重要方面是分别与单个神经元相关的神经网络的处理或计算。自然神经元是信号处理器 – 它们在树突中接收可以触发轴突信号的微信号。有一个潜在的阈值,到达的时候,刺激轴突,并传播信号到其他神经元。因此,我们可以将人造神经元视为一个在输入中具有信号接收器、在输出中具有激活单元的东西,其可以发送的信号将被转发到与图中所示类似的其他神经元上:
此外,神经元之间的连接具有相应可以修改信号的权重,从而影响神经元的输出。由于权重是神经网络的内部因素并影响其输出,所以可以认为它们是神经网络的内部学科,调节描述神经元与其他神经元或外部世界的连接的权重将反映神经网络的能力。
人造神经元接收一个或多个输入(代表树突)并将它们相加以产生输出/ 激活 (代表神经元的轴突)。一般来说每个节点的总和被加权,总和通过激活函数或传递函数传递。
这个组件为神经网络处理增加了非线性,这是因为自然神经元具有非线性行为。在一些特殊情况下,它可以是一个线性函数。
一个标准的计算机芯片电路可以看作是一个激活功能的数字网络,取决于输入的是“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
相关阅读
此文已由作者授权云加社区发布,转载请注明文章出处