chapter3 神经网络
3.1 从感知机到神经网络
神经网络的一个重要性质是它可以自动地从数据中学习到合适的权重参数, 是一个自动寻找参数版的感知机.
通过感知机,好消息是即便是计算机进行的复杂处理,感知机(理论上)也可以将其表示出来。坏消息是,设定权重的工作,即确定合适的、能符合预期的输入与输出的权重,现在还是由人工进行的。而神经网络的出现就是为了解决这一问题.
用图来表示神经网络的话,如图所示。我们把最左边的一列称为输入层,最右边的一列称为输出层,中间的一列称为中间层。中间层有时也称为隐藏层。
3.2 激活函数
感知机的公式为 b + w1x1 + w2x2, 设 a = b + w1x1 + w2x2 , y = h(a). 刚才登场的h(x)函数会将输入信号的总和转换为输出信号,这种函数一般称为激活函数(activation function).
下面,我们将仔细介绍激活函数。激活函数是连接感知机和神经网络的
桥梁。
3.2.1 阶跃函数
“阶跃函数”表示的激活函数以阈值为界,一旦输入超过阈值,就切换输出.因此,可以说感知机中使用了阶跃函数作为激活函数。神经网络一般使用其他的激活函数.
阶跃函数的代码实现:
def step_function(x):
if x > 0:
return 1
else:
return 0
由于后面要使用numpy 数组操作,可以将代码优化为
"""
这里,数组x中大于0的元素被转换为True,小于等于0的元素被转换为False,从而生成一个新的数组y。
数组y是一个布尔型数组,但是我们想要的阶跃函数是会输出int型的0 或1的函数。因此,需要把数组y的元素类型从布尔型转换为int型。
"""
import numpy as np
def step_function(x):
return np.array(x > 0, dtype=np.int)
使用matpoltlib 绘制出阶跃函数的图形
import numpy as np
import matplotlib.pylab as plt
def step_function(x):
return np.array(x > 0, dtype=np.int)
x = np.arange(-5.0, 5.0, 0.1) # 生成从 -5.0 到 5.0 的矩阵, 步长为0.1
y = step_function(x)
print(y)
plt.plot(x, y)
plt.ylim(-0.1, 1.1) # 指定y轴的范围
plt.show()
np.arange(-5.0, 5.0, 0.1)在−5.0到5.0的范围内,以0.1为单位,生成NumPy数组([-5.0, -4.9, ... , 4.9])。step_function()以该NumPy数组为参数,对数组的各个元素执行阶跃函数运算,并以数组形式返回运算结果。
对数组x、y进行绘图,结果如图所示。
3.2.2 sigmoid 函数
sigmoid 函数 是神经网络经常使用的一种函数.
其中exp(-x)表示e^(−x)的意思.神经网络中用sigmoid函数作为激活函数进行信号的转换,转换后的信号被传送给下一个神经元。实际上,上一章介绍的感知机和接下来要介绍的神经网络的主要区别就在于这个激活函数。其他方面,比如神经元的多层连接的构造、信号的传递方法等,基本上和感知机是一样的。
代码实现sigmoid 函数:
import numpy as np
def sigmoid(x):
return 1 / (1 + np.exp(-x))
"""
实际上,如果在这个sigmoid函数中输入一个NumPy数组,则结果如下所示。
x = np.array([-1.0, 1.0, 2.0])
sigmoid(x)
array([ 0.26894142, 0.73105858, 0.88079708])
"""
使用matpoltlib 画出sigmoid 函数
import numpy as np
import matplotlib.pylab as plt
def sigmoid(x):
return 1 / (1 + np.exp(-x))
x = np.arange(-5.0, 5.0, 0.1) # 生成从 -5.0 到 5.0 的矩阵, 步长为0.1
y = sigmoid(x)
print(y)
plt.plot(x, y)
plt.ylim(-0.1, 1.1) # 指定y轴的范围
plt.show()
3.2.3 sigmoid 函数与阶跃函数的对比
-
不同点
通过函数图像对比,相对于阶跃函数只能返回0或1,sigmoid函数可以返回0.731 ...、0.880 ...等实数(这一点和刚才的平滑性有关)。也就是说,感知机中神经元之间流动的是0或1的二元信号,而神经网络中流动的是连续的实数值信号。
相同点
阶跃函数和sigmoid函数虽然在平滑性上有差异,但是如果从宏观视角看函数图像对图,可以发现它们具有相似的形状。实际上,两者的结构均是“输入小时,输出接近0(为0);随着输入增大,输出向1靠近(变成1)”。也就是说,当输入信号为重要信息时,阶跃函数和sigmoid函数都会输出较大的值;当输入信号为不重要的信息时,两者都输出较小的值。还有一个共同点是,不管输入信号有多小,或者有多大,输出信号的值都在0到1之间。
且二者均是非线性函数,sigmoid函数是一条曲线,阶跃函数是一条像阶梯一样的折线,两者都属于非线性的函数。
神经网络的激活函数必须使用非线性函数。换句话说,激活函数不能使
用线性函数。为什么不能使用线性函数呢?因为使用线性函数的话,加深神经网络的层数就没有意义了。线性函数只代表倍数的变化, 是一条直线, 比如 h(x)= 2*x ,那么即使是三层也是2*2*2*x,可以优化为一层 8*x, 所以神经网络一定要使用非线性的函数.
3.2.4 ReLU函数
最近使用的比较多的是ReLU函数, ReLU(Rectified Linear Unit)函数在输入大于0时,直接输出该值;在输入小于等于0时,输出0.
使用代码构造:
import numpy as np
def relu(x):
return np.maximum(0, x)
对应的函数图像:
3.3 多维数组的实现
- 如果掌握了NumPy多维数组的运算,就可以高效地实现神经网络。下面介绍一些numpy中相关的API.
import numpy as np
A = np.array([[1, 2], [3, 4]])
print(A)
print(np.ndim(A)) # 输出维度
print(A.shape) # 数组的形状可以通过实例变量shape获得,这里为 二行二列
B = np.array([[5, 6], [7, 8]])
C = np.dot(A,B) # A,B矩阵相乘
print(C)
"""
[[1 2]
[3 4]]
2
(2, 2)
[[19 22]
[43 50]]
"""
-
神经网络内积
下面我们使用NumPy矩阵来实现神经网络。这里我们以下图中的简
单神经网络为对象。这个神经网络省略了偏置和激活函数,只有权重。
其中, X = (1,2).
import numpy as np
X = np.array([1, 2])
print (X.shape) # result: (2,)
W = np.array([[1, 3, 5], [2, 4, 6]])
print(W)
#[[1 3 5]
# [2 4 6]]
pirnt(W.shape) # result: (2, 3)
Y = np.dot(X, W)
print(Y) # result: [ 5 11 17]
如上所示,使用np.dot(多维数组的点积),可以一次性计算出Y 的结果,能够大大加快。这意味着,即便Y 的元素个数为100或1000,也可以通过一次运算就计算出结果!如果不使用np.dot,就必须单独计算Y 的每一个元素(或者说必须使用for语句),非常麻烦。因此,通过矩阵的乘积一次性完成计算的技巧,在实现的层面上可以说是非常重要的.
总结
这部分学习了
- 神经网络与感知机的区别,
- 神经网络相关的函数,
- 以及使用numpy复习了矩阵,简化运算
下一部分学习具体神经网络的实现.