神经网络(全连接神经网络)
对人类中枢神经系统的观察启发了人工神经网络这个概念。在人工神经网络中,简单的人工节点,称作神经元(neurons),连接在一起形成一个类似生物神经网络的网状结构。人工神经网络与生物神经网络的相似之处在于,它可以集体地、并行地计算函数的各个部分,而不需要描述每一个单元的特定任务。神经网络这个词一般指统计学、认知心理学和人工智能领域使用的模型,而控制中央神经系统的神经网络属于理论神经科学和计算神经科学。
在神经网络的现代软件实现中,被生物学启发的那种方法已经很大程度上被抛弃了,取而代之的是基于统计学和信号处理的更加实用的方法。在一些软件系统中,神经网络或者神经网络的一部分(例如人工神经元)是大型系统中的一个部分。这些系统结合了适应性的和非适应性的元素。虽然这种系统使用的这种更加普遍的方法更适宜解决现实中的问题,但是这和传统的连接主义人工智能已经没有什么关联了。不过它们还有一些共同点:非线性、分布式、并行化,局部性计算以及适应性。从历史的角度讲,神经网络模型的应用标志着二十世纪八十年代后期从高度符号化的人工智能(以用条件规则表达知识的专家系统为代表)向低符号化的机器学习(以用动力系统的参数表达知识为代表)的转变。
感知器(perceptron)
要先理解神经网络,让我们先从最简单的感知器开始。感知机算法是一种很好的二分类在线算法,对于线性可分的模型,感知机能在输入空间将实例划分为正负样本,而分离他们的是超平面。感知机是神经网络以及支持向量机的基础。
让我们先来看一下下面一张图:
一个感知机包含多个输入
(x1,x2,⋅,xn)
(
x
1
,
x
2
,
⋅
,
x
n
)
,每个输入
xi
x
i
对应一个权重
wi
w
i
,此外还有一个偏置项
b
b
,求和后通过一个激活函数(perceptron中是阶跃函数,其他还有sigmoid,tanh,Relu等)得到我们的输出。即
激活函数可表示为
感知机是一个简单的二类分类的线性分类模型,要求我们的样本是线性可分的我们要做的就是找到一个最合适的超平面,将正负样本分割开来。
假定数据集
对每一个样本有若
wxi>0
w
x
i
>
0
,
yi=1
y
i
=
1
反之
wxi<0
w
x
i
<
0
,
yi=−1
y
i
=
−
1
由几何意义易得,每一个样本到这个超平面的距离为:
由上面论述可知,对于一个分类错误的误分类点,有
所以误分类点导超平面的距离为
则误分类点到超平面的总和为
根据如上所述,我们定义损失函数为
其中M是误分类的样本的集合。对于损失函数,当所有样本都正确分类时损失函数为0,感知机学习的策略就是在假设空间中选取使损失函数最小的模型参数,即感知机模型。
为正确获得损失函数的最小值,我们可以使用随机梯度下降的方法
分别对 w和b w 和 b 求偏导得梯度
对权值进行更新后得到
η η 称为学习率(步长),随着迭代的不断增加,损失函数逐渐收敛于一个局部最小值。最终我们可以获得我们想要的感知机模型。
神经元
首先让我们来看看人脑中的神经元:
一个神经元包含多个树突,接受来自其他多个神经元的电信号,一个轴突,轴突末梢又与其他多个神经元相连,传递信息。
人类模拟生物神经元的结构,构建出了抽象的神经元模型,见下图:
由此可以看出,神经元与感知机的本质上是一样的,输入多个,经过求和转化最终得到输出。有所区别的是,感知机是用的阶跃函数,而神经元大多是sigmoid函数或者是tanh函数。
以下是几个激活函数(active function)的差别:
(1)阶跃函数
阶跃函数的定义是
(2)sigmoid函数
sigmoid函数的定义是
除此之外,sigmoid函数还有一个很重要的性质,即
(3)tanh函数
tanh函数的定义是
(4)Relu函数
Relu函数的定义是
神经网络的结构
神经网络之所以被称为网络(network),是因为他通常用许多不同的函数复合在一起来表示。该模型有一个有向无环图相关联,而图描述了函数是如何复合在一起的。例如,我们有三个函数
f(x)=f(3)(f(2)(f(1)(x)))
f
(
x
)
=
f
(
3
)
(
f
(
2
)
(
f
(
1
)
(
x
)
)
)
。这些链式结构视神经网络中最常见的结构。
如上图所示,我们有四层网络,第一层是输入层(input layer),最右边的一层是输出层(output layer)。在上面的例子中输出层只有一个输出神经元。而中间的两层既不是输入层也不是输出层,我们称之为隐层(hidden layer)。链的全长我们称之为深度(depth),正因如此,才出现了深度学习(deep learning)这个名词。上面这个模型也可称为前馈神经网络(feedforward neural network)或者多层感知机(multilayer perceptron,MLP),即把上一层的输出作为下层输入的神经网络。
上面这些规则定义了全连接神经网络的结构。事实上还存在很多其它结构的神经网络,比如卷积神经网络(CNN)、循环神经网络(RNN),他们都具有不同的连接规则。(以后会比较详细的介绍)
反向传播算法(back propagation)
接下来介绍一个比较基础也比较重要的算法,反向传播算法。
由上图可知,我们定义输入层
x0
x
0
,输出层
y
y
,权重,
wlij
w
i
l
j
是第l-1层第i个元素到l层第j个元素的权重。
我们另输出
f(x) f ( x ) 是我们选择的激活函数
我们设损失函数
我们要做的就是通过学习得到最佳的 {wlij} { w i j l } 使得我们的损失函数 E E 达到最小,为达到这个目的,我们使用梯度下降算法。
在输出层
我们让
我们令 ∂E∂Sli=δli=(yi−targeti)yi(1−yi) ∂ E ∂ S i l = δ i l = ( y i − t a r g e t i ) y i ( 1 − y i )
所以得到,其中 δ δ 是输出误差
接下来我们对前一层求偏导
由此,我们可以类推得到
δ(l)j δ j ( l ) 就是由 δ(l+1)k δ k ( l + 1 ) 反向传播得到的。
而后对w进行求偏导,得到
由此便是反向传播的主要思路:
import numpy as np
import matplotlib.pyplot as plt
import random
def sigmoid(x):
return 1.0 / (1.0 + np.exp(-x))
def sigmoid_prime(x):
return sigmoid(x)*(1.0 - sigmoid(x))
class BP_Network(object):
def __init__(self,sizes):
self.num_layer = len(sizes)
self.sizes = sizes
#初始化bias和权重
self.bias = [np.random.randn(y,1) for y in sizes[1:]]
self.weight = [np.random.randn(y,x) for y,x in zip(sizes[1:],sizes[:-1])]
def forwardpro(self,a):\
#输入第一层,前向传播求出每一层
activity = np.array([a]).transpose()
for b,w in zip(self.bias,self.weight):
activity = sigmoid(np.dot(w,activity) + b)
return activity
def Mini_Batch_GD(self,train_data,num_literation,mini_batch_size,eta):
"""
num_literation:梯度下降迭代的次数
mini_batch_size:mini_batch的长度
eta:
"""
n = len(train_data)
for j in range(num_literation):
random.shuffle(train_data)
mini_batches = [train_data[k:k + mini_batch_size] for k in range(0,n,mini_batch_size)]
for mini_batch in mini_batches:
self.update_weight(mini_batch,eta)
def update_weight(self,mini_batch,eta):
# 初始化b,w
partial_b = [np.zeros(b.shape) for b in self.bias]
partial_w = [np.zeros(w.shape) for w in self.weight]
#更新b,w
for (x,y) in mini_batch:
change_b,change_w = self.backpro(x,y)
partial_b = [cb + pb for cb,pb in zip(change_b,partial_b)]
partial_w = [cw + pw for cw,pw in zip(change_w,partial_w)]
self.weight = [w - (eta/len(mini_batch)*nw) for w,nw in zip(self.weight,partial_w)]
self.bias = [b - (eta/len(mini_batch)*nb) for b,nb in zip(self.bias,partial_b)]
def backpro(self,x,y):
partial_b = [np.zeros(b.shape) for b in self.bias]
partial_w = [np.zeros(w.shape) for w in self.weight]
#前向传播计算出所有隐藏层和输出层的值
activity = np.array([x]).transpose()
y = np.array([y]).transpose()
# print(y.shape)
forward = [activity]
ss = []
for b,w in zip(self.bias,self.weight):
s = np.dot(w,activity)+b
ss.append(s)
activity = sigmoid(s)
forward.append(activity)
#计算最后一层的误差和权重
delta = sigmoid_prime(ss[-1])*(forward[-1]-y)
partial_b[-1] = delta
partial_w[-1] = np.dot(delta,forward[-2].transpose())
# 反向传播计算误差
for l in range(2,self.num_layer):
sp = sigmoid_prime(ss[-l])
delta = np.dot(self.weight[-l+1].transpose(),delta)*sp
partial_b[-l] = delta
partial_w[-l] = np.dot(delta,forward[-l-1].transpose())
return (partial_b,partial_w)
def predict_digits(self,train_data):
random.shuffle(train_data)
test_data = train_data[0:10]
i=0
# 打印出预测数据和准确率
plt.figure()
for (x,y) in test_data:
i = i+1
plt.subplot(2, 5, i)
plt.axis('off')
plt.imshow(x.reshape(8,8), cmap=plt.cm.gray_r, interpolation='nearest')
plt.title('Training: {}'.format(np.argmax(self.forwardpro(x))))
plt.show()
count = 0
for (x,y) in train_data:
if(np.argmax(self.forwardpro(x))==np.argmax(y)):
count = count + 1
print(count/1797)
from sklearn import datasets
# 导入数据
digits = datasets.load_digits()
digits['data'].shape
digits['images'].shape
(1797, 8, 8)
# 处理数据进行预测
r = digits['target']
x = digits['data']
(a,) = r.shape
y = np.zeros((10,a))
# y.shape
for i in range(a):
y[r[i],i] = 1
x=x.transpose()
print(x.shape)
X = [(x[:,i],y[:,i]) for i in range(a)]
k=499
bp = BP_Network([64,30,10])
bp.Mini_Batch_GD(X,1000,30,0.1)
#输出预测准确率
bp.predict_digits(X)
(64, 1797)
0.8770172509738453
自此,全连接神经网络介绍完毕,接下来,我会花一些时间总结一下cnn与rnn的实现原理并给出相应的代码,由于是新手,所写出的介绍必定会有许多方面的不足,对于其中写的不好的地方,欢迎指出。
参考文献
零基础入门深度学习(3) - 神经网络和反向传播算法
神经网络浅讲:从神经元到深度学习
一文弄懂神经网络中的反向传播法——BackPropagation