提示:转载请注明出处,若本文无意侵犯到您的合法权益,请及时与作者联系。
一、 神经元与神经层的输出计算
二、 实现一个2*3*1的FNN
三、 实现一个自定义结构的FNN
从单个神经元的角度看,我们输入一个x,它经过自身的权重、偏置和激励函数(以Sigmoid为例)返还一个输出值。
从单个神经层(假设有3个神经元)的角度看,我们输入一个向量,它经过自身的权重矩阵、偏置向量和激励函数(以Sigmoid为例)返还一个输出向量。
在上述描述中,我们使用了向量和矩阵的概念,这个和单个神经元的处理原理是相同的,只是向量和矩阵的运算可以同时包含多个神经元。
矩阵本质上就是为了方便解多个线性方程组中而来的,它就是一个数表,没什么特别。例如:
将上述计算使用Python3工程化后如下:
import numpy as np
# Sigmoid激活函数: f(x) = 1 / (1 + e^(-x))
def Sigmoid(x):
# 当x是一个向量时,np自动对向量的每一个元素应用Sigmoid函数
return 1/(1+np.exp(-x))
# 输入向量、权重矩阵、偏置向量
def feedforward(x,weight,biase):
out = np.dot(weight, x) + biase
out = Sigmoid(out)
return out
从上述代码中可知,我们想要计算一个神经层的输出,除了有需要输入向量,还需要指定权重矩阵、偏置向量两个参数(本文的激励函数全部默认为Sigmoid)
众所周知,在一个神经网络中,具有输入层、中间层和输出层,其中中间层可以有多个神经层。
我们以一个简单的二层神经网络为例:
如果我们想要定义这个神经网络,则需要为两个神经层分别指定它的1个权重矩阵和1个偏置向量,我们可以得到它的一个简单代码:
class Network_V1(object):
def __init__(self):
self.n_input = 2 # 输入层的神经元个数
self.n_l1 = 3 # 中间层的神经元个数
self.n_l2 = 1 # 输出层的神经元个数
# 中间层(1个3*2的权重矩阵、1个3*1的偏置向量)
self.weight_l1 = np.random.randn(self.n_l1,self.n_input)
self.biase_l1 = np.random.randn(self.n_l1,1)
# 输出层(1个1*3的权重矩阵、1个1*1的偏置向量)
self.weight_l2 = np.random.randn(self.n_l2, self.n_l1)
self.biase_l2 = np.random.randn(self.n_l2,1)
def feedforward(self,x):
out = x
out = AF.Sigmoid(np.dot(self.weight_l1, out) + self.biase_l1)
out = AF.Sigmoid(np.dot(self.weight_l2, out) + self.biase_l2)
return out
上述的代码是针对该2*3*1的网络结构的,我们考虑将上述代码修改成可以自定义层数,且每层的神经元个数也可以自定义的神经网络:
class Network_V2(object):
def __init__(self,shape_size):
"""shape_size是一个包含有各层神经元数量的列表"""
self.num_layers = len(shape_size) # 神经层的数量(输入层+中间层+输出层)
self.shape_size = shape_size # 包含有各层神经元数量的列表
# 多个权重矩阵的列表,例如weights[0]表示第一层和第二层之间的权重矩阵(行数为第二层神经元数,列数为第一层神经元数)
self.weights = [np.random.randn(y,x) for x,y in zip(shape_size[:-1],shape_size[1:])]
# 多个偏置向量的列表,第一层(输入层)没有偏置,所以biases[0]存储的是第二层的偏置参数列表(长度为第二层神经元数)
self.biases = [np.random.randn(y,1) for y in shape_size[1:]]
def feedforward(self,x):
"""输入一个多维向量,输出网络的输出"""
#out = x
for w,b in zip(self.weights,self.biases):
x = AF.Sigmoid(np.dot(w,x)+b)
return x
在上述代码中我们使用了一个shape_size的列表来表示神经元各层数量,这样做的原因是,神经网络的层数可以自定义,意味着传入的参数数量不确定,使用列表作为这个整体参数比较合适。
假如我们想要构建一个2*3*1的神经网络,我们就为它传入一个[2,3,1]的参数。
程序得到这个[2,3,1]的参数后就可以各个层的权重矩阵和偏置矩阵的参数信息,从而进行初始化。
例如这个[2,3,1]参数,程序可以得到需要生成分别为(3,2)、(1,3)的2个权重矩阵、分为为(3,1)、(1,1)的2个偏置向量。
测试本次迭代优化的正确性,使用如下代码测试:
测试结果如下,表示迭代没有问题: