0. 前言
神经网络是为了解决感知机的缺陷,感知机的权重和偏置需要人为设定,但神经网络可以自动从数据中学习到合适的权重和偏置值,下面给出一个2层的神经网络结构(有些书称为3层),包含输入层、中间层(也称作隐层)和输出层:
1. 激活函数
在感知机介绍中,当输出值大于0时,输出结果为1,当输出值小于等于0时,输出结果为0,在介绍它时并没有明确说明其使用的算法,其实单层感知机使用了阶跃函数作为其激活函数,将输入信号的总和()被激活函数(阶跃函数转换),阶跃函数表达式如下:
Python实现阶跃函数:
import numpy as np
import matplotlib.pyplot as plt
def step_fun(x):
#方法一
#y = x > 0
#return y.astype(int)
#方法二
return np.array(x>0,dtype=int)
if __name__ == '__main__':
x = np.arange(-10,10,0.1)
y = step_fun(x)
print(y)
plt.plot(x,y)
plt.show()
在神经网络中,则使用一种平滑的曲线作为激活函数,换句话说激活函数从单位阶跃函数换成其他函数,就从感知机的世界走进了神经网络的世界。
1.1 Sigmoid激活函数
首先,给出sigmoid函数的表达式及其图像:
Python 实现sigmoid函数:
import numpy as np
import matplotlib.pyplot as plt
def sigmoid_fun(x):
return 1/(1+np.exp(-x))
if __name__ == '__main__':
x = np.arange(-10,10,0.1)
y = sigmoid_fun(x)
plt.plot(x,y)
plt.show()
通过对比sigmoid函数和阶跃函数,明显可以看出两者的区别,阶跃函数只能返回0和1,sigmoid函数可以返回0-1之间的数,具有一定的平滑作用,当输入信号重要时,输出就会向1靠拢,当输入信号不重要时,输出就会向0靠拢。
为什么神经网络要以非线性函数作为激活函数呢?
这是线性函数,比如h(x) = cx,采用此函数作为激活函数,那么无论怎么加深隐层深度,总会有一个与之等效的无隐藏层的神经网络,假设有个3层的神经网络,那么可以得到输出y = h(h(h(x))), 即 y = c *c *c *x; 与y = ax(a = c^3)等效, 所以用线性函数作为激活函数无法发挥多层网络带来的优势。
1.2 Relu函数
Relu函数的表达式很简单,此处只给出公式,不做过多的解释:
2. 神经网络的实现
与感知机原理类似,但实现神经网络之前,需要简单了解以下矩阵乘法相关的知识以及numpy中矩阵的相关操作,具体可以复习下线性代数的知识,以下是简单的矩阵A*矩阵B:
2.1 实现一个3层的神经网络
下图是一个两个输入两个输出的神经网络结构,具有2个隐层,数据在神经网络中流动满足公式,其中i代表第几层神经网络,m、n、k是矩阵的维度
如图中所述,除输入数据外的每个神经元的输出都会通过一个激活函数,根据上图,我们利用Python来实现一个简单3层神经网络,注:此处权重、偏置均为人工设定,假设是神经网络学习得到的参数值。
import numpy as np
def sigmoid_fun(x):
return 1/(1+np.exp(-x))
def Relu_fun(x):
return np.maximum(0,x)
def init_network():
network = {} #字典形式保存权重和偏置值
#根据图中分析,第1层神经网络权重维度为2行3列,偏置1行3列
network['W1'] = np.array([[0.1, 0.2, 0.3], [0.01, 0.7, 0.4]])
network['B1'] = np.array([-0.3, -0.8, -0.4])
#根据图中分析,第2层神经网络权重维度为3行2列,偏置1行2列
network['W2'] = np.array([[0.3, 0.01], [0.5, 0.3], [0.6, 0.4]])
network['B2'] = np.array([-0.2, -0.4])
#根据图中分析,第一层神经网络权重维度为2行2列,偏置1行2列
network['W3'] = np.array([[0.8, 0.4], [0.05, 0.08]])
network['B3'] = np.array([-0.06, -0.8])
return network
def forward_network(x, network):
A1 = np.dot(x, network['W1']) + network['B1']
# A1通过sigmoid激活函数
A1_Hx = sigmoid_fun(A1)
#print(A1_Hx)
A2 = np.dot(A1_Hx, network['W2']) + network['B2']
# A2通过sigmoid激活函数
A2_Hx = sigmoid_fun(A2)
#print(A2_Hx)
A3 = np.dot(A2_Hx, network['W3']) + network['B3']
#print(A3)
# A3通过Relu激活函数
return Relu_fun(A3)
if __name__ == '__main__':
x = np.array([2, 50])
network = init_network()
result = forward_network(x, network)
print(result)
2.2 输出层激活函数和输出层神经元个数
输出层的激活函数根据是回归问题还是分类问题进行区分,一般情况下回归问题(预测)采用恒等变换,即原来输出什么就输出什么,举个例子,根据一个人的身高年龄等等数据预测一个人的体重,输出的是一个预测值,则该值一般即为我们需要的结果。分类问题又分为二元分类和多元分类,二元分类用sigmoid函数,多元分类用softmax函数,关于sigmoid函数以上介绍比较多,下面介绍softmax函数,首先看softmax函数的表达式:
细心的朋友会发现,其实是每个元素与所有的元素的和之比,也就是概率问题,概率最大的那个元素,分类到它的可能性也就最大,但是在Python中直接使用该公式会存在一点问题,就是ai比较大时,会溢出
测试代码如下:
import numpy as np
def softmax_fun(x):
sum_x = np.sum(np.exp(x))
return np.exp(x)/sum_x
if __name__ == '__main__':
x = np.array([2000, 5])
y = softmax_fun(x)
print (y)
为了解决溢出问题,可以利用下面的公式解决,即在计算时,每个数据同时加上或减去一个数,所求softmax函数值不变
所以,若在计算softmax激活函数时,每个数据都同时减去数据中的最大值,所得的数会变小,则不会溢出,修改后的代码如下:
import numpy as np
def softmax_fun(x):
sum_x = np.sum(np.exp(x-np.max(x)))
return np.exp(x-np.max(x))/sum_x
if __name__ == '__main__':
x = np.array([2000, 5, 1967, 205])
y = softmax_fun(x)
print (y)
softmax函数能够得到输出值概率最大的那一项,但是在预测时,它的位置不会发生变化,所以预测时一般不包含softmax函数,但它在神经网络在学习参数有作用。
输出神经元数目一般按照分类目标数目确定,比如要识别0-9 十个数字,那么输出神经元的数目一般定为十个。