一提到深度学习,神经网络等技术,大部分肯定会想到TensorFlow,Keras,Caffe,PyTorch等框架,而这些框架大部分都是使用Python来进行开发的,毫无疑问,在人工智能领域技术上比如神经网络等Python是一种很不错的选择,但是作为一个JAVA程序员,如果想要使用java语言来玩玩神经网络的话,有没有更好的选择呢,其实java在这一方面确实没有Python做的好,不过要使用java来做神经网络的话还是有很多选择的,比如DeepLearning4J, Encog, JOONE等等,这篇文章我们来介绍一款java神经网络学习框架Encog。
Encog是一款Java和.Net上的机器学习框架,好像也有JavaScript版的,但我没找到,它除了支持神经网络之外,也支持大部分通用的机器学习技术。Encog的第一个版本为0.5, 正式发布于2008年7月10号,之后的Encog1.0到2.0极大地扩展了神经网络,到了3.0后增加了更多的机器学习方法。Encog他也不仅仅包括了机器学习,它内部还包括了一款爬虫工具,可以很方便地为神经网络获取数据(不过这款工具我感觉不好用,还没有Jsoup好使呢)。
使用Encog构建一个神经网络也特别简单,在Encog中,创建神经网络主要使用BasicNetwork和BasicLayer这两个类,当然也会用到其他的类比如激活函数。
这里我们使用maven导入encog的相关包,只需要导入encog-core这个包就可以了。
神经网络通常是分层的,至少有一个输入和输出层,也许还有隐藏层,某些神经网络类型不会在输入和输出层之外分解成任何形式层,然而,这个输入层和输出层将永远存在,也许可能被整合在同一层,我们现在先介绍一下输入层,输出层和隐藏层。
输入层: 输入层是神经网络的第一层,和其他层一样,这一层包括了一个指定数字的神经元,同一层的神经元都含有相似的属性,一般情况下,对于分类,回归或者聚类的神经网络的每个属性,输入层都会有一个神经元与之一一对应。
神经元的数量决定了神经网络层的结构,对于每个输入神经元,在Encog中为一个double值,例如,以下的数组能够输入到一个包含五个神经元层的结构中
double[] input = new double[5];
Encog中使用double类型数据,使用MLData接口管理这些double数组,上面的double数据要想被神经网络使用,得先将其转换为MLData,代码如下:
MLData data = new BasicMLData(input);
MLData是可以直接提供给Encog的数据,BasicMLData类是其中一个实现了MLData接口的类。其他实现了MLData接口的类也可以作为Encog神经网络处理的数据。
输出层: 输出层是神经网络的最后一层,这层给出了神经网络最终的输出结果。同样,输出结果也是一个实现了MLData接口的类,大多数神经网络返回的输出类是一个BasicMLData,从MLData中我们也可以得到一个doube数组。
隐藏层:在前面的讨论中,神经网络包含了输入层和输出时,有时候,输出层和输入层是相同的,但是大多数通常是两个单独的层,此外,在输入层和输出层之间也许存在其他的层,这层被称为隐藏层,隐藏层在输入层和输出层之间。隐藏层也可以承担更多复杂的结构。
隐藏层唯一的目标是让神经网络更好的为给定的输入产生预期的输出,神经网络编程首先涉及到的是定义输入层和输出层神经元个数,然后在定义隐藏层。
隐藏层是一个非常大的“黑盒子”,怎样创建一个隐藏层结构,要达到不要太简单,也不要太复杂,太简单的隐藏层结构将学习不精,太复杂的隐藏层结构也要花太多数据和时间去训练,通常是一个单一的隐藏层神经元数目等于输入层的两倍(这个不一定对,但可以先试试),在根据神经网络的性能,在适当增加或者减少隐藏层神经元的数量。
一般我们在编程的时候第一个入门的例子就是打印一句hello world, 相应地,学习XOR运算是我们学习Encog的第一个例子,相当于神经网络编程的hello world。
XOR的运算规则为:0 XOR 0 = 0, 1 XOR 0 = 1, 0 XOR 1 = 1, 1 XOR 1 = 0。
我们希望给神经网络输入0, 0得到0, 输入1,0得到1,输入0,1得到1,输入1,1得到0。
该神经网络是最原始的神经网络,被称为感知器,是一种前馈神经网络,也是我们将要学习的第一个神经网络。XOR运算符有两个输入和一个输出,此外,我们还需要一个隐藏层,该隐藏层具有两个神经元,两个神经元的选择是任意的,你也可以尝试三个或者一个。XOR问题很简单,两个隐藏层神经元足以解决它,该网络的结构如下:
上图中:输入神经元为I1, I2。 输出神经元为O1。隐藏神经元为H1, H2。偏执神经元为B1, B2
构造神经网络:使用Encog构建一个神经网络特别简单,首先需要new 一个BasicNetwork对象,然后往这个对象里面添加层就可以了。BasicLayer构造函数第一个参数指定激活函数,第二个参数指定是由有偏置神经元,第三个参数指定神经元个数,如果只传一个数字,则表示只指定神经元个数,而默认激活函数为ActivationSigmoid, 有偏置神经元。
因为偏执神经元影响的是下一层,以及激活函数影响的是前一层的数据。所以一般第一层(输入层)不需要激活函数,最后一层(输出层)不需要偏置神经元。调用finalizeStructure表示已经构建完成,不需要在往里面添加层了,reset是随机初始化各层之间的连接权重。
处理数据:
使用弹性传播(RPROP)训练:我们在这个使用弹性传播训练(RPROP)来训练我们的网络, RPROP是Encog所支持的最好的训练算法,当然,Encog还提供了其他训练技术,并且在一定的训练技术下能较好地解决某些问题。使用RPROP训练的代码如下:
上面的代码通过多次的迭代,当迭代到使神经网络的错误率低于1%的时候,神经网络训练就结束了。到了这里,我们可以运行我们的代码看看训练的结果:
可以看到,在第一次迭代的时候错误率很大,然后随着迭代次数的增加,当迭代到一百次左右的时候,错误率已经降低到0.01了。注意这里每次的运行结果都不一样,但是只要是错误率在降低说明我们的神经网络构建成功了。
使用该神经网络计算XOR:接下来就是测试一下我们的神经网络能不能得到正确的XOR运算。
输出的结果如下:
可以看到,我们的神经网络最终的结果还是相当不错的。
完整的代码: