神经网络
3.1从感知机到神经网络
y = h ( b + w 1 x 1 + w 2 x 2 ) y=h(b+w_1x_1+w_2x_2) y=h(b+w1x1+w2x2)
将感知机写成更简洁的形式,在这里我们使用一个函数h(x)来表示这种分情况的动作(超过0则输出1,否则输出0)。
h(x)即是激活函数,激活函数使用了阶跃函数的模型.
a = b + w 1 x 1 + w 2 x 2 a=b+w_1x_1+w_2x_2 a=b+w1x1+w2x2 y = h ( a ) y=h(a) y=h(a)
3.2激活函数
3.2.1 sigmoid函数与阶跃函数
sigmoid函数为:
h ( x ) = 1 ( 1 + e x p ( − x ) ) h(x)={1\over (1+exp(-x))} h(x)=(1+exp(−x))1
阶跃函数为:h ( x ) = { 1 0 < x 0 0 ≤ x h(x)=\left\{ \begin{array}{rcl} 1 & & {0 < x}\\ 0 & & {0 \leq x}\\ \end{array} \right. h(x)={100<x0≤x
阶跃函数的实现
#法一:简单实现
import numpy as np
def step_function1(x):
if x>0:
return 1
else:
return 0
#法二:运用Numpy库
def step_function2(x):
y=x>0#逻辑结果
return y.astype(np.int)#将布尔变量转为1和0
阶跃函数的图形:
#用matplotlib库画阶跃函数图像
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)#start=-5.0,stop=5.0,step=0.1(-5.0->4.9)
y=step_function(x)
plt.plot(x,y)
plt.ylim(-0.1,1.1)#指定y轴的范围
plt.show()
sigmoid函数的实现与画图
#用matplotlib库画sigmoid函数图像
import numpy as np
import matplotlib.pyplot as plt
def sigmoid(x):
return 1/(1+np.exp(-x))
x=np.arange(-5.0,5.0,0.1)
y=sigmoid(x)
plt.plot(x,y)
plt.ylim(-0.1,1.1)#y轴范围
plt.show()
sigmoid函数图像为:
3.2.2 sigmoid函数与阶跃函数的关系
不同点:阶跃函数只能返回0或1,而sigmoid函数可以返回0.731…、0.880…等实数且具有平滑性。即感知机钟神经元之间流动的是0或1的二元信号,而神经网络中流动的是连续的实数值信号。
共同点:两者结构都为“输入小时,输出接近0(为0);随着输入增大,输出向1靠近(变成1)”。也就是说,当输入信号为重要信息时,阶跃函数和sigmoid函数都会输出较大的值;当输入信号为不重要的信息时,两者都输出较小的值。但不管输入信号有多小,或者说有多大,输出信号始终在0到1间。
3.2.3非线性函数
阶跃函数与sigmoid函数都不是线性函数。神经网络的激活函数必须使用非线性函数。
原因:线性函数的问题在于不管如何加深层数,总是存在与之等效的“无隐藏层的神经网络”(如下例),无法发挥多层网络带来的优势。
y ( x ) = h ( h ( h ( x ) ) ) → y ( x ) = c × c × c × x → y = a × x ( a = c 3 ) y(x)=h(h(h(x))) \rightarrow y(x)=c\times c \times c \times x \rightarrow y=a \times x (a=c^3) y(x)=h(h(h(x)))→y(x)=c×c×c×x→y=a×x(a=c3)
3.2.4 ReLU函数
ReLU函数可以表示为下面式子:
h ( x ) = { x x > 0 0 x ≤ 0 h(x)=\left\{ \begin{array}{rcl} x & & {x > 0}\\ 0 & & {x \leq 0}\\ \end{array} \right. h(x)={x0x>0x≤0
#运用numpy实现ReLU函数,用matlplotlib画图
import numpy as np
import matplotlib.pyplot as plt
def relu(x):
return np.maximum(0,x)
x=np.arange(-5,5,0.1)
y=relu(x)
plt.plot(x,y)
plt.ylim(-1,5)#y轴范围
plt.show()
ReLU函数的图像:
3.3多维数组的运算
3.3.1多维数组
#多维数组
import numpy as np
A=np.array([1,2,3,4])
#数组的维数用np.ndim()获得
print(A,np.ndim(A))
#数组的形状可通过实例变量shape获得,返回结果是一个元组
print(A.shape,A.shape[0])
#二维数组(矩阵)
B=np.array([[1,2],[3,4],[5,6]])
print(B,np.ndim(B),B.shape)
3.3.2矩阵乘法
矩阵乘法的实现:
#运用np.dot()函数计算矩阵乘法
import numpy as np
A=np.array([[1,2],[3,4]])
print(A.shape)
B=np.array([[5,6],[7,8]])
print(B.shape)
print(np.dot(A,B))
注意:在多维数组的乘积运算中,两个矩阵中对应维度的元素个数一致。
3.3.3神经网络的内积
以上图简单神经网络为例(该神经网络省略了偏置和激活函数,只有权重)。
#注意X、W、Y的形状
import numpy as np
X=np.array([1,2])
print(X.shape)
W=np.array([[1,3,5],[2,4,6]])
print(W,W.shape)
Y=np.dot(X,W)
print(Y)
3.4 3层神经网络的实现
3层神经网络:输入层(第0层)有2个神经元,第1个隐藏层(第1层)有3个神经元,第2个隐藏层(第2层)有2个神经元,输出层(第3层)有2个神经元
先定义符号(即使不记忆也不影响后面的理解)
w
12
(
1
)
w_{12}^{(1)}
w12(1)
3.4.1输入层
现在看看从输入层到第1层的第1个神经元的信号传递过程。
a
1
(
1
)
=
w
11
(
1
)
x
1
+
w
12
(
1
)
x
2
+
b
1
(
1
)
a_1^{(1)}=w_{11}^{(1)}x_1+w_{12}^{(1)}x_2+b_1^{(1)}
a1(1)=w11(1)x1+w12(1)x2+b1(1)
若用矩阵乘法计算,则可以表示成:
A
(
1
)
=
X
W
(
1
)
+
B
(
1
)
A^{(1)}=XW^{(1)}+B^{(1)}
A(1)=XW(1)+B(1)
其中:
A ( 1 ) = ( a 1 ( 1 ) a 2 ( 1 ) a 3 ( 1 ) ) A^{(1)}= \left( \begin{matrix} a^{(1)}_1 & a_2^{(1)} & a_3^{(1)} \end{matrix} \right) A(1)=(a1(1)a2(1)a3(1))
X = ( x 1 x 2 ) X= \left( \begin{matrix} x_1 & x_2 \end{matrix} \right) X=(x1x2)
B ( 1 ) = ( b 1 ( 1 ) b 2 ( 1 ) b 3 ( 1 ) ) B^{(1)}= \left( \begin{matrix} b_1^{(1)} & b_2^{(1)} & b_3^{(1)} \end{matrix} \right) B(1)=(b1(1)b2(1)b3(1))
W ( 1 ) = ( w 11 ( 1 ) w 21 ( 1 ) w 31 ( 1 ) w 12 ( 1 ) w 22 ( 1 ) w 32 ( 1 ) ) W^{(1)}= \left( \begin{matrix} w_{11}^{(1)} & w_{21}^{(1)} & w_{31}^{(1)} \\ w_{12}^{(1)} & w_{22}^{(1)} & w_{32}^{(1)} \end{matrix} \right) W(1)=(w11(1)w12(1)w21(1)w22(1)w31(1)w32(1))
import numpy as np
def sigmoid(x):
return 1/(1+np.exp(-x))
X1=np.array([1.0,0.5])
W1=np.array([[0.1,0.3,0.5],[0.2,0.4,0.6]])
B1=np.array([0.1,0.2,0.3])
print(W1.shape)#(2,3)
print(X1.shape)#(2,)
print(B1.shape)#(3,)
A1=np.dot(X1,W1)+B1
3.4.2中间层
现在我们观察第1层与第2层中激活函数的计算过程,如下图。
#用NumPy实现第0层与第1层与第1层到第2层的信息传递
A1=np.dot(X1,W1)+B1
Z1=sigmoid(A1)
W2=np.array([[0.1,0.4],[0.2,0.5],[0.3,0.6]])
B2=np.array([0.1,0.2])
print(Z1.shape)#(3,)
print(W2.shape)#(3,2)
print(B2.shape)#(2,)
A2=np.dot(Z1,W2)+B2
Z2=sigmoid(A2)
3.4.3输出层
之后实现第1层到第2层的信号传递,与之前的实现基本相同。但是最后的激活函数和之前的隐藏层有所不同。
def identity_function(x):
return x
W3=np.array([[0.1,0.3],[0.2,0.4]])
B3=np.array([0.1,0.2])
A3=np.dot(Z2,W3)+B3
Y=identity_function(A3)#或者Y=A3
注意:这里定义的identity_function()函数(恒等函数),并将其作为输出层的激活函数,只是为之前的流程保持一致。
3.4.4代码实现小结
按照神经网络的实现惯例,对代码进行整理。
import numpy as np
def sigmoid(x):
return 1/(1+np.exp(-x))
def identity_function(x):
return x
def init_network():
network={}
network['W1']=np.array([[0.1,0.3,0.4],[0.2,0.4,0.6]])
network['b1']=np.array([0.1,0.2,0.3])
network['W2']=np.array([[0.1,0.4],[0.2,0.5],[0.3,0.6]])
network['b2']=np.array([0.1,0.2])
network['W3']=np.array([[0.1,0.3],[0.2,0.4]])
network['b3']=np.array([0.1,0.2])
return network
def forward(network,x):
W1,W2,W3=network['W1'],network['W2'],network['W3']
b1,b2,b3=network['b1'],network['b2'],network['b3']
a1=np.dot(x,W1)+b1
z1=sigmoid(a1)
a2=np.dot(z1,W2)+b2
z2=sigmoid(a2)
a3=np.dot(z2,W3)+b3
y=identity_function(a3)
return y
network=init_network()
x=np.array([1.0,0.5])
y=forward(network,x)
print(y)#[0.31628407 0.69505815]
3.5输出层的设计
一般来说,回归问题用恒等函数,分类问题用softmax函数。
softmax函数:
y
k
=
e
x
p
(
a
k
)
∑
i
=
1
n
e
x
p
(
a
i
)
y_k=\frac{exp(a_k)}{\sum_{i=1}^nexp(a_i)}
yk=∑i=1nexp(ai)exp(ak)
可以由公式看出,输出层的各个神经元都受到所有输入信号的影响。
#softmax函数的实现
import numpy as np
def softmax(x):
exp_a=np.exp(x)
sum_exp_a=np.num(exp_a)
y=exp_a/sum_exp_a
return y
但因为softmax实现存在溢出问题,所以将softmax函数进行如下改进:
y
k
=
e
x
p
(
a
k
)
∑
i
=
1
n
e
x
p
(
a
i
)
=
C
e
x
p
(
a
k
)
C
∑
i
=
1
n
e
x
p
(
a
i
)
=
e
x
p
(
a
k
+
l
o
g
C
)
∑
i
=
1
n
e
x
p
(
a
i
+
l
o
g
C
)
=
e
x
p
(
a
k
+
C
′
)
∑
i
=
1
n
e
x
p
(
a
i
+
C
′
)
\begin{aligned} y_k={\frac{exp(a_k)}{\sum_{i=1}^nexp(a_i)}} &=\frac{Cexp(a_k)}{C\sum_{i=1}^nexp(a_i)} \\ &=\frac{exp(a_k+logC)}{\sum_{i=1}^nexp(a_i+logC)}\\ &=\frac{exp(a_k+C')}{\sum_{i=1}^nexp(a_i+C')} \end{aligned}
yk=∑i=1nexp(ai)exp(ak)=C∑i=1nexp(ai)Cexp(ak)=∑i=1nexp(ai+logC)exp(ak+logC)=∑i=1nexp(ai+C′)exp(ak+C′)
这里的
C
′
C'
C′可以用任何值,但是为了防止溢出,一般会使用输入信号中的最大值。
#改进版softmax函数的实现
import numpy as np
def softmax(a):
c=np.max(a)
exp_a=np.exp(a-c)
sum_exp_a=np.sum(exp_a)
y=exp_a/sum_exp_a
return y
一般来说,神经网络只把输出值最大的神经元对应的类别作为识别结果。并且,即使使用softmax函数,输出值最大的神经元位置也不会改变,故一般将softmax函数省略。
3.6手写数字识别
这里使用MNIST手写数字图像集(由0-9数字图像构成的,训练图像6万张,测试图像1万张)。此文档只根据本章内容说明MNIST数据集的处理部分与推理部分。
3.6.1MNIST数据集读取
从http://yann.lecun.com/exdb/mnist/下载MNSIT数据集文件,并进行解压。
import numpy as np
import os
import struct
import matplotlib.pyplot as plt
from PIL import Image
#下载并处理数据集格式
def load_mnist(path, kind='train'):
labels_path = os.path.join(path, '%s-labels.idx1-ubyte' % kind)
#os.path.join()函数用于路径拼接文件路径
images_path = os.path.join(path, '%s-images.idx3-ubyte' % kind)
with open(labels_path, 'rb') as lbpath:
magic, n = struct.unpack('>II', lbpath.read(8))
labels = np.fromfile(lbpath, dtype=np.uint8)
with open(images_path, 'rb') as imgpath:
magic, num, rows, cols = struct.unpack(">IIII", imgpath.read(16))
images = np.fromfile(imgpath, dtype=np.uint8).reshape(len(labels), 784)
return images, labels
#数据集的载入-测试数据
def get_data_for_test():
X_train, y_train = load_mnist('MNIST_data', kind='train')
print('Rows: %d, columns: %d' % (X_train.shape[0], X_train.shape[1]))
X_test, y_test = load_mnist('MNIST_data', kind='t10k')
print('Rows: %d, columns: %d' % (X_test.shape[0], X_test.shape[1]))
return X_test,y_test
#数据集的载入-训练数据
def get_data_for_train():
X_train, y_train = load_mnist('MNIST_data', kind='train')
print('Rows: %d, columns: %d' % (X_train.shape[0], X_train.shape[1]))
X_test, y_test = load_mnist('MNIST_data', kind='t10k')
print('Rows: %d, columns: %d' % (X_test.shape[0], X_test.shape[1]))
return X_train,y_train
3.6.2神经网络的推理处理
def init_network():
sample_weight_path='sample_weight.pkl'
with open(sample_weight_path,"rb") as f:
network=pickle.load(f)
return network
def sigmoid(x):
return 1/(1+np.exp(-x))
def softmax(x):
return x
def predict(network,x):
W1,W2,W3=network['W1'],network['W2'],network['W3']
b1,b2,b3=network['b1'],network['b2'],network['b3']
a1=np.dot(x,W1)+b1
z1=sigmoid(a1)
a2=np.dot(z1,W2)+b2
z2=sigmoid(a2)
a3=np.dot(z2,W3)+b3
y=softmax(a3)
return y