神经网络多层次实现的时候涉及权重Weight、及层与层之间的连接,神经元网络的权重较复杂,权重涉及两个层次,权重有来源节点及目标节点。接下来我们实现神经网络层次Layers之间节点Nodes的连接。在创建不同层次连接的时候需实现权重weight,在entity目录下创建Weight.py的实体,在Weight.py中创建一个类class Weight。
第一步:设置和访问权重Weight的的ID。类class Weight的set_weight_index方法第一个参数是self,因为创建Weight实例的时候可以有很多实体,因此传入self创建实例。
第二步:设置和访问权重Weight的来源节点。权重涉及前面一个层次的节点,也涉及到后面一个层次的节点。例如,TensorFlow的可视化图中,输入层与隐藏层的之间的权重,权重可能来源于x1节点。神经元节点Node的Index很重要,这里权重的from_node_index就设置为来源节点的Index。
第三步:设置和访问Weight的目标节点。
第四步:设置和访问Weight的值。权重的核心内容是值Value。
Create_AI_Framework_In5Classes(Class1)版本的Weight.py源代码:
# -*- coding: utf-8 -*-
class Weight:
#第一步:设置和访问Weight的的ID
def set_weight_index(self, weight_index):
self._weight_index = weight_index
def get_weight_index(self):
return self._weight_index
#第二步:设置和访问Weight的来源节点
def set_from_index(self, from_node_index):
self._from_index = from_node_index
def get_from_index(self):
return self._from_index
#第三步:设置和访问Weight的目标节点
def set_to_index(self, to_node_index):
self._to_node_index = to_node_index
def get_to_index(self):
return self._to_index
#第四步:设置和访问Weight的值
def set_value(self, value):
self._value = value
def get_value(self):
return self._value
接下来实现权重连接不同的神经元网络层次。我们在service目录中创建一个业务类NetworkConnection.py。前面创建了神经元的网络结构,但没有创建层次之间的连接,现在把不同的网络层次连接起来,如果不把不同的网络层次连接起来,激活函数就无法把前一个层次的内容传递给下一个层次,将通过激活函数把上一个层次的数据传递给下一个层次,所以必须建立网络之间的连接。
例如,TensorFlow的可视化图中,建立网络之间的连接要做第一件事情是创建Weight,不同层次之间都通过Weight连接的,网络图的中一条条连线就是Weight。
Weight的代码如何写?神经元网络有很多不同的层,而且有很多不同的Weight。基本的思路:遍历不同的层次,例如,TensorFlow的可视化图中如果有4层,第一层和第二层之间有Weight,第二层和第三层之间有Weight,第三层和第四层之间有Weight,至少要有2层。NetworkConnection.py的create_Weights方法中需不需知道Layer的信息?不需要,因为神经元节点中已经包含层次的信息,神经元节点通过set_level设置了层次信息。在create_Weights方法中传入nodes, num_of_features, hidden_layers的参数。神经元网络有多个层次的时候,第一个层次的输出是第二个层次的输入,第二个层次的输出是第三个层次的输入......。
先创建一个数组weights,存放所有的权重Weight信息。两两层次之间中间会有权重Weight,那么整个神经网络是多少层?网络层数的计算公式是:整个Neural Network的层数= Input layers + Hiddenlayers + Output layers。这里的输入层Input layers是1层,输出层Output layers是1层。因为有很多个Weight,通过weight_index记录Weight的ID。
接下来进行外层循环,循环不同层次,循环不同层次构成Weight,Weight代表了神经网络中前后层次之间的联系。循环总的层次,例如,TensorFlow的可视化图中有4层,输入层、第一个隐藏层、第二个隐藏层、输出层,构建的Weight会有3列,第一层和第二层之间会有1列Weight,第二层和第三层之间会有1列Weight,第三层和第四层之间会有1列Weight,Weight发生于前后两个层次Layers之间,所以循环的次数要从总的层次Layer数量中减去1,代表有多少个不同类型的Weight。
然后继续进行内层循环。例如,TensorFlow的可视化图中第一层和第二层之间会有很多的Weight。这个时候循环遍历所有的节点,每个节点都有自己的索引,判断当前节点nodes[j]所在的具体的Layer,如果当前节点nodes[j]所在的层次等于i,此时已经获得了一个当前节点nodes[j],再次循环遍历所有的节点,核心目的在判断不同节点之间的Layer的先后关系,考虑刚才的节点nodes[j]和现在的节点nodes[k]之间的Level关系,如果是前一个层次的Level,那两个节点nodes[j]、nodes[k]之间就有Weight。这个时候可能会有性能的问题,但我们主要关注神经网络的实现,暂不考虑性能的问题。判断一下当前获得节点nodes[k]的Level是否等于i+ 1,目的是找到节点nodes[k]前面的Level的某个节点,只要是前一个Level的节点,那他们之间就有Weight关系,构成Weight的前提条件是有前后相邻的Layer关系。
创建Weight的时候必须设置Weight的源节点、目标节点,及设置Weight的index。weight的set_from_index、set_to_index需填写什么节点的index,这个比较关键,因为从nodes[j]触发到nodes[k]之间创建Weight,节点所在的Layer的ID越小,就越在前面,这里nodes[j]在第i层,而nodes[k]在第i+1层,因此,weight.set_from_index设置为出发点nodes[j].get_index(),weight.set_to_index设置为目的点nodes[k].get_index()。
震撼人心的时刻到了,下面是本章节中从零起步编码实现多层次神经网络的最大难点:创建Weight的值Value。此时再次运行http://playground.tensorflow.org/,会发现AI运行所有的故事都是在更新Weight的值。TensorFlow的可视化图中,可看见Weight的值都在变化,不断更新Weight。Weight怎么创建?这也是Tensorflow、Pytorch之间的比较点,Tensorflow、Pytorch之间Weight的具体实现不相同。如何设置Weight的初始值可参考论文《Improving the learning speedof 2-layer neural networks by choosing initial values of the adaptive weights》,论文里面告诉怎么设置Weight,框架开发者的任务是实现论文中的公式,由于Python及生态系统天然处理数学的基因,很容易实现这些公式。
现在根据论文实现其中的公式,使用rand设置随机数,听上去是随机,其实并不随机,因为这将影响训练多少次,及每次训练的难度。TensorFlow的可视化图的权重是-1到1,这里我们假设最小值是0,最大值是1,权重值从0到1之间,对其进行处理使其更符合实际的情况,尽量可能使其随机。这也是Tensorflow、Pytorch内部实现的核心。具体算法实现请参考本书QQ群内部的文件。