神经网络neural networks:神经网络是由具有适应性的简单单元(神经元)组成的广泛并行互连的网络,它的组织能够模拟生物神经系统对真实世界物体做出的交互反应。
神经元neuron:神经网络中的最基本单元。
M-P神经元模型:神经元接受来自其他n个神经元的输入信号,通过带有权重的连接进行传递,与阈值threshold进行比较后,经过激活函数activation function处理以产生神经元的输出。
阈值又称偏置bias。
理想中的激活函数是阶跃函数,由于其不连续、不光滑,实际中常用sigmiod作为激活函数。
“模拟生物神经网络”是认知科学家对神经网络的类比阐释,从计算机科学的角度看,我们可先不考虑其是否真的模拟了生物神经网络,只需将其看成一个包含了很多参数的数学模型,有效的神经网络学习算法大多以数学证明为支撑。
感知机perceptron:由输入层和输出层组成,输入层是输入数据,输出层是M-P神经元。
感知机可以实现与、或、非逻辑运算
感知机的权重更新公式:
w i ← w i + Δ w i Δ w i = η ( y − y ^ ) x i w_i \leftarrow w_i+\Delta w_i \\ \Delta w_i=\eta(y-\hat{y})x_i wi←wi+ΔwiΔwi=η(y−y^)xi
如果预测正确,感知机不发生变化;否则,根据错误程度来调整参数。
感知机只有一层功能神经元,学习能力有限,只能解决线性可分问题。
经证明:若两类模式是线性可分的(存在一个超平面能将它们分开),则感知机一定会收敛;否则感知机将会发生振荡。
使用多层功能神经元,可解决非线性可分问题。隐含层hidden layer:位于输出层和输入层之间的层。
多层前馈神经网络multi-layer feedforward neural networks:相邻层全连接,同层无连接,不存在跨层连接。
误差逆传播算法,又称反向传播算法(error BackPropagation,BP算法),基于梯度下降gradient descent策略,以目标的负梯度方向对单数进行调整。
学习率 η ∈ ( 0 , 1 ) \eta \in (0,1) η∈(0,1)控制算法每一轮迭代中的更新步长,若太大则容易振荡,太小则收敛速度过慢。
标准BP算法:每次根据一个样本进行更新参数
累积BP算法:最小化累计误差,读取一遍训练集后更新一次参数
经证明:只需一个包含足够多神经元的隐层,多层前馈神经网络就能以任意精度逼近任意复杂度的连续函数
BP神经网络因为具有强大的表达能力,经常产生过拟合现象,缓解过拟合的策略:1.早停early stopping,若训练集误差降低但是验证集误差升高,则停止训练,返回具有最小验证集误差的参数;2.正则化regularization,在误差目标函数中增加一个描述网络复杂度的部分,比如权重的L2范数平方。增加权重的L2范数平方这一项后,训练过程将偏好于较小的参数,使得网络输出更加”光滑“,从而缓解过拟合
两种”最优“:全局最小global minimum和局部极小local minimum,全局最小一定是局部极小,反之不然。
”跳出“局部极小,进一步接近全局最小的策略:1.以多组不同的参数初始化神经网络;2.模拟退火simulated annealing;3.随机梯度下降;4.遗传算法genetic algorithm
上述技术大多是启发式,理论上尚缺乏保障
RBF(Radial Basis Function径向基函数)网络:一种单隐层前馈神经网络,它使用径向基函数作为隐层神经元的激活函数,而输出层是对隐层神经单元输出的线性组合。
RBF网络训练过程:1.确定神经元中心,常用随机采样、聚类等;2.利用BP算法来确定参数。
竞争型学习competitive learning:一种常用的无监督策略,网络的输出神经元互相竞争,每一时刻只有一个竞争获胜的神经元被激活,其他神经元被抑制。又称”胜者通吃“
ART(Adaptive Resonance Theory自适应谐振理论)网络是竞争型学习的重要代表,该网络由比较层、识别层、识别阈值、重置模块构成。其中,比较层负责接收输入样本并将其传递给识别层神经元,识别层每个神经元对应一个模式类,神经元数目可在训练过程中动态增加以增加新的模式类。
ART比较好地缓解了竞争学习中的”可塑性-稳定性窘境“,可塑性是指神经网络学习新知识的能力,稳定性是指神经网络在学习新知识时要保持对旧知识的记忆。ART网络可以进行增量学习incremental learning或在线学习online learning。
增量学习:在学得模型之后,再接收到训练样例时,只需要根据新样例对模型进行更新,不必重新训练整个模型,并且先前学习的有效信息不会被”冲掉“。
在线学习:每获得一个新样例就进行一次模型更新,在线学习是增量学习的一个特例。
SOM(Self-Organizing Map自组织映射)网络,是一种竞争型学习的无监督神经网络,它能将高维输入数据映射到低维空间中(通常为二维),同时保持数据再高维空间中的拓扑结构,即在高维空间中相似的样本点会被映射到网络输出层中邻近的神经元。
结构自适应网络:不仅将参数作为学习目标,并且将网络的结构也作为学习的目标之一。
级联相关(Cascade-Correlation)网络,是结构自适应网络的一种重要代表。级联是指建立层次连接的层次结构,在开始训练时网络只有输入层和输出层,处于最小拓扑结构,随着训练进行,新的隐层神经元逐渐加入,从而创建起层级结构,当新的隐层神经元加入时,其输入端连接权值是冻结固定的。相关是指通关最大化新神经元的输出与网络误差之间的相关性来训练相关的参数。
与一般的神经网络相比,级联相关网络无需设置网络层数、隐层神经元数目,且训练速度快,但是在数据较小时容易过拟合。
递归神经网络recurrent neural networks:允许出现环形结构,从而让一些神经元的输出反馈回来作为输入信号。这样的结构与信息反馈过程,使得网络在t时刻的输出状态不仅与t时刻的输入有关,还与t-1时刻的网络状态有关,从而能处理与时间有关的动态变化。
Boltzmann机是一种基于能量的模型,神经元分为两层:显层和隐层,显层用于表示数据的输入和输出,隐层被理解为数据的内在表达。
标准Boltzmann机是一个全连接图,训练网络的复杂度很高,这使其难以用于解决现实任务,现实常采用受限Boltzmann机。
受限Boltzmann机常采用对比散度(Contrastive Divergence,简称CD)来进行训练。
理论上,参数越多的模型复杂度越高,容量越大,这意味着它可以完成更为复杂的任务,但一般情况下,复杂模型的训练效率低,易陷入过拟合。计算能力的大幅度提升可以缓解训练过程低效性,训练数据的大幅增加可降低过拟合风险。
单隐层的前馈神经网络已具有很强大的学习能力,但从模型的复杂度来看,增加隐层的数目显然比增加隐层的神经元数目更有效。
多层神经网络难以直接用经典算法进行训练,因为误差在多隐层内逆传播时,往往会发散而不能收敛到稳定状态。
无监督逐层训练(unsupervised layer-wise training)是多隐层网络训练的有效手段,基本思想是每次驯良一层隐结点,训练时将上一层隐结点的输出作为输入,而本层隐结点的输出作为下一层隐结点的输入,这称为预训练pre-training;在预训练全部完成后,再对整个网络进行微调fine-tuning。
“预训练+微调”可视为将大量参数分组,对每组先找到局部看起来比较好的设置,然后再基于这些局部较优的结果联合起来进行全局寻优。这在利用了模型大量参数提供的自由度的同时,有效地节省了训练开销,
另外一种节省训练开销的策略是权共享(weight sharing),让一组神经元使用相同的权重。
深度学习,通过多层处理,逐渐将初始的底层特征表示转化为高层特征表示,用简单模型即可完成复杂的任务。深度学习可理解为特征学习(feature learning)或表示学习(representation learning)。
如果激活函数是一个线性函数,那么无论多少层网络,都可以表示为一层线性网络。
不妨设一个2层神经网络一个隐含层和一个输出层,第 l l l层第 j j j个神经元相对于第 l − 1 l-1 l−1层第 i i i个神经元的权重为 w i j l w_{ij}^{l} wijl,第 l l l层第 j j j个神经元的偏置为 b j l b_{j}^{l} bjl,第 l l l层第 j j j个神经元的输出为 o j l o_{j}^{l} ojl,如下图所示。
不妨设输入为 x i x_{i} xi,隐含层和输出层的神经元的激活函数都为线性函数 f ( x ) = x f(x)=x f(x)=x,则
隐含层输出 o j 1 = f ( ∑ i = 1 2 w i j 1 x i + b j 1 ) = ∑ i = 1 2 w i j 1 x i + b j 1 o^{1}_{j}=f(\sum_{i=1}^{2}w_{ij}^{1}x_{i}+b_{j}^{1})=\sum_{i=1}^{2}w_{ij}^{1}x_{i}+b_{j}^{1} oj1=f(∑i=12wij1xi+bj1)=∑i=12wij1xi+bj1,其中 j = 1 , 2 或 3 j=1,2或3 j=1,2或3
输出层输出 o j 2 = f ( ∑ i = 1 3 w i j 2 o i 1 + b j 2 ) = ∑ i = 1 3 w i j 2 o i 1 + b j 2 = ∑ i = 1 3 w i j 2 ( ∑ k = 1 2 w k i 1 x k + b i 1 ) + b j 2 = ( w 11 1 w 1 j 2 + w 12 1 w 2 j 2 + w 13 1 w 3 j 2 ) x 1 + ( w 21 1 w 1 j 2 + w 22 1 w 2 j 2 + w 23 1 w 3 j 2 ) x 2 + w 1 j 2 b 1 1 + w 2 j 2 b 2 1 + w 3 j 2 b 3 1 + b j 2 o^{2}_{j}=f(\sum_{i=1}^{3}w_{ij}^{2}o_{i}^{1}+b_{j}^{2})=\\ \sum_{i=1}^{3}w_{ij}^{2}o_{i}^{1}+b_{j}^{2}=\\ \sum_{i=1}^{3}w_{ij}^{2}(\sum_{k=1}^{2}w_{ki}^{1}x_{k}+b_{i}^{1})+b_{j}^{2}=\\ (w_{11}^{1}w_{1j}^{2}+w_{12}^{1}w_{2j}^{2}+w_{13}^{1}w_{3j}^{2})x_{1}+(w_{21}^{1}w_{1j}^{2}+w_{22}^{1}w_{2j}^{2}+w_{23}^{1}w_{3j}^{2})x_{2}+w_{1j}^{2}b_{1}^{1}+w_{2j}^{2}b_{2}^{1}+w_{3j}^{2}b_{3}^{1}+b_{j}^{2} oj2=f(i=1∑3wij2oi1+bj2)=i=1∑3wij2oi1+bj2=i=1∑3wij2(k=1∑2wki1xk+bi1)+bj2=(w111w1j2+w121w2j2+w131w3j2)x1+(w211w1j2+w221w2j2+w231w3j2)x2+w1j2b11+w2j2b21+w3j2b31+bj2
显然可以使用1层激活函数为 f ( x ) = x f(x)=x f(x)=x的神经元将上面2层神经网络的输出表达出来,证明了使用线性激活函数的缺陷。
单个使用sigmoid为激活函数的神经元与对率回归模型相同,但是对率回归一般用于二分类问题,输出值的概率值需要与一个阈值(通常为0.5)进行比较,判断类别是0还是1.
根据梯度下降策略以及链式求导法则,有
Δ v i h = − η ∂ E k ∂ v i h = − η ∂ E k ∂ b h ∂ b h ∂ α h ∂ α h ∂ v i h = − η ( ∑ j = 1 l ∂ E k ∂ y j ^ ∂ y j ^ ∂ β j ∂ β j ∂ b h ) f ′ ( α h − γ h ) x i = − η ( ∑ j = 1 l ( y j k ^ − y j k ) f ′ ( β j − θ j ) w h j ) ( b h ( 1 − b h ) ) x i = − η b h ( 1 − b h ) ∑ j = 1 l y j k ^ ( 1 − y j k ^ ) ( y j k ^ − y j k ) x i 由 ( 5.10 ) 知 , 设 g j = y j k ^ ( 1 − y j k ^ ) ( y j k ^ − y j k ) 所 以 Δ v i h = − η b h ( 1 − b h ) ∑ j = 1 l g j x i 由 ( 5.15 ) 知 , 设 e h = b h ( 1 − b h ) ∑ j = 1 l g j 故 Δ v i h = − η e h x i ( 5.13 ) 得 证 \Delta v_{ih}=-\eta \frac{\partial E_k}{\partial v_{ih}} \\=-\eta \frac{\partial E_k}{\partial b_h} \frac{\partial b_h}{\partial \alpha_h} \frac{\partial \alpha_h}{\partial v_{ih}} \\=-\eta (\sum_{j=1}^{l}\frac{\partial E_k}{\partial \hat{y_j}} \frac{\partial \hat{y_j}}{\partial \beta_j} \frac{\partial \beta_j}{\partial b_h})f^{'}(\alpha_h-\gamma_h)x_i \\=-\eta(\sum_{j=1}^{l}(\hat{y_j^k}-y_j^k)f^{'}(\beta_j-\theta_j)w_{hj})(b_h(1-b_h))x_i \\=-\eta b_h(1-b_h)\sum_{j=1}^{l}\hat{y_j^k}(1-\hat{y_j^k})(\hat{y_j^k}-y_j^k)x_i \\由(5.10)知,设g_j=\hat{y_j^k}(1-\hat{y_j^k})(\hat{y_j^k}-y_j^k) \\所以\Delta v_{ih}=-\eta b_h(1-b_h)\sum_{j=1}^lg_jx_i \\由(5.15)知,设e_h=b_h(1-b_h)\sum_{j=1}^lg_j \\故\Delta v_{ih}=-\eta e_hx_i \\(5.13)得证 Δvih=−η∂vih∂Ek=−η∂bh∂Ek∂αh∂bh∂vih∂αh=−η(j=1∑l∂yj^∂Ek∂βj∂yj^∂bh∂βj)f′(αh−γh)xi=−η(j=1∑l(yjk^−yjk)f′(βj−θj)whj)(bh(1−bh))xi=−ηbh(1−bh)j=1∑lyjk^(1−yjk^)(yjk^−yjk)xi由(5.10)知,设gj=yjk^(1−yjk^)(yjk^−yjk)所以Δvih=−ηbh(1−bh)j=1∑lgjxi由(5.15)知,设eh=bh(1−bh)j=1∑lgj故Δvih=−ηehxi(5.13)得证
学习率 η ∈ ( 0 , 1 ) \eta \in (0,1) η∈(0,1)控制算法每一轮迭代中的更新步长,若太大则容易振荡,太小则收敛速度过慢。
通过分析,西瓜的色泽、根蒂、敲声、纹理、脐部、触感均是有序的离散属性,可以连续化;密度、含糖率是连续属性。
色泽可以根据颜色深度,将浅白作为0、青绿作为1、乌黑作为2;
根蒂可以根据蜷缩程度,将硬挺作为0、稍蜷作为1、蜷缩作为2;
敲声可以根据声音音调,将沉闷作为0、浊响作为1、清脆作为2;
纹理可以根据清晰程度,将清晰作为0、稍糊作为1、模糊作为2;
脐部可以根据凹陷程度,将平坦作为0、稍凹作为1、凹陷作为2;
触感可以根据硬滑程度,将软粘作为0、硬滑作为1;
密度、含糖量均使用原来的连续值;
此二分类问题,可将好瓜视为1、坏瓜视为0.
import numpy as np
import matplotlib.pyplot as plt
#西瓜数据集 每一列为一条数据
features=np.array([
[1,2,2,1,0,1,2,2,2,1,0,0,1,0,2,0,1],
[2,2,2,2,2,1,1,1,1,0,0,2,1,1,1,2,2],
[1,0,1,0,1,1,1,1,0,2,2,1,1,0,1,1,0],
[0,0,0,0,0,0,1,0,1,0,2,2,1,1,0,2,1],
[2,2,2,2,2,1,1,1,1,0,0,0,2,2,1,0,1],
[1,1,1,1,1,0,0,1,1,0,1,0,1,1,0,1,1],
[0.697,0.774,0.634,0.608,0.556,0.403,0.481,0.437,0.666,0.243,0.245,0.343,0.639,0.657,0.360,0.593,0.719],
[0.460,0.376,0.264,0.318,0.215,0.237,0.149,0.211,0.091,0.267,0.057,0.099,0.161,0.198,0.370,0.042,0.103]
])
labels=np.array([
[1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0]
])
def sigmoid(X):
return 1./(1+np.exp(-X))
class Net():
def __init__(self,num_input=8,num_hidden=10,num_output=1):
#隐含层和输出层的权重和偏置
self.W1=np.random.randn(num_hidden,num_input)
self.b1=np.zeros(num_hidden).reshape(-1,1)
self.W2=np.random.randn(num_output,num_hidden)
self.b2=np.zeros(num_output).reshape(-1,1)
#隐含层和输出层的输出
self.o1=np.zeros(num_hidden).reshape(-1,1)
self.o2=np.zeros(num_output).reshape(-1,1)
#梯度存储变量
self.do2=np.zeros(self.o2.shape)
self.dW2=np.zeros(self.W2.shape)
self.db2=np.zeros(self.b2.shape)
self.do1=np.zeros(self.o1.shape)
self.dW1=np.zeros(self.W1.shape)
self.db1=np.zeros(self.b1.shape)
def forward(self,X):#前向传播
if X.shape[0] != self.W1.shape[1]:
print("输入数据格式错误!")
return
self.input=X
#使用sigmoid函数为激活函数
self.o1=sigmoid(np.matmul(self.W1,self.input)+self.b1)
self.o2=sigmoid(np.matmul(self.W2,self.o1)+self.b2)
return self.o2
def standard_BP(self,label,lr=0.2):#标准BP 使用均方误差为损失函数
#求梯度
self.do2=self.o2-label
self.dW2=np.matmul(self.do2*self.o2*(1-self.o2),self.o1.reshape(1,-1))
self.db2=self.do2*self.o2*(1-self.o2)
self.do1=np.matmul(self.W2.transpose(),self.do2*self.o2*(1-self.o2))
self.dW1=np.matmul(self.do1*self.o1*(1-self.o1),self.input.reshape(1,-1))
self.db1=self.do1*self.o1*(1-self.o1)
#更新参数
self.W2-=self.dW2*lr
self.b2-=self.db2*lr
self.W1-=self.dW1*lr
self.b1-=self.db1*lr
def accumulate_BP(self,labels,lr=0.2):#累积BP 使用均方误差为损失函数
num=labels.shape[1]#样本数量
#求梯度
self.do2=(self.o2-labels)/num
self.dW2=np.matmul(self.do2*self.o2*(1-self.o2),self.o1.transpose())
self.db2=(self.do2*self.o2*(1-self.o2)).sum(axis=1).reshape(-1,1)
self.do1=np.matmul(self.W2.transpose(),self.do2*self.o2*(1-self.o2))
self.dW1=np.matmul(self.do1*self.o1*(1-self.o1),self.input.transpose())
self.db1=(self.do1*self.o1*(1-self.o1)).sum(axis=1).reshape(-1,1)
#更新参数
self.W2-=self.dW2*lr
self.b2-=self.db2*lr
self.W1-=self.dW1*lr
self.b1-=self.db1*lr
def train_standard_BP(features,labels,lr):
net=Net()
epoch=0
loss=1
all_loss=[]
while loss>0.1:#停止条件
for i in range(features.shape[1]):
X=features[:,i]
Y=labels[0,i]
net.forward(X.reshape(-1,1))
net.standard_BP(Y,lr)
output=net.forward(features)
loss=0.5*((output-labels)**2).sum()
epoch+=1
all_loss.append(loss)
print("标准BP","学习率:",lr,"\n终止epoch:",epoch,"loss: ",loss)
plt.xlabel("epoch")
plt.ylabel("loss")
plt.plot(all_loss)
plt.show()
def train_accumulate_BP(features,labels,lr=0.2):
net=Net()
epoch=0
loss=1
all_loss=[]
while loss>0.1:#停止条件
output=net.forward(features)
net.accumulate_BP(labels,lr)
loss=0.5*((output-labels)**2).sum()/labels.shape[1]
epoch+=1
all_loss.append(loss)
print("累积BP","学习率:",lr,"\n终止epoch:",epoch,"loss: ",loss)
plt.xlabel("epoch")
plt.ylabel("loss")
plt.plot(all_loss)
plt.show()
学习率为0.2,停止条件为loss<=0.1,标准BP的迭代次数要比累积BP迭代次数多得多,代码展示如下
train_standard_BP(features,labels,lr=0.2)
train_accumulate_BP(features,labels,lr=0.2)