机器学习入门之BP算法

前言

在上一篇感知器算法中只有一层功能神经元,其只能解决对于与或非等简单线性可分的问题。对于简单的非线性可分的问题,在解决非线性可分的问题时,感知器的学习过程无法找到一个合适的分割平面对数据进行分类。为了解决感知器无法解决异或问题,可以使用多层神经元的感知器模型(一般来说三层就够了,神经网络的层数不是越多越好)。其结构模型如图所示,图片来自于西瓜书。
BP算法

BP算法数学模型

在BP算法的模型中,与感知器算法相似,

  1. 在每个输入与隐藏层的每个神经元都相连并且存在权重 v i h v_{ih} vih
  2. 每个隐藏层神经元中都存在阈值 γ h \gamma_{h} γh和激活函数f(x)
  3. 在每个隐藏层与输出层直接都相连并存在权重 ω h j \omega_{hj} ωhj
  4. 在每个输出层神经元中都存在阈值 θ j \theta_{j} θj和激活函数f(x)

数据的正向传递

  1. 我们首先将从输入层到隐藏层的权值设为 V = [ v 11 v 1 h . . . v 1 q . . . . . . . . . . . . v i 1 v i h . . . v i q . . . . . . . . . . . . v d 1 v d h . . . v d q ] d × q V= {\left[ \begin{matrix} v_{11} & v_{1h} & ...&v_{1q} \\ ...&...&...&...\\ v_{i1} & v_{ih} & ...&v_{iq} \\ ...&...&...&...\\ v_{d1}& v_{dh} &...& v_{dq} \end{matrix} \right] }_{d\times q} V=v11...vi1...vd1v1h...vih...vdh...............v1q...viq...vdqd×q
    其中 v i h v_{ih} vih的值为从输入 x i x_{i} xi到隐藏层神经元 b h b_{h} bh的权重
  2. 当一个d维数据 X = [ x 1 , . . . , x d ] X=[x_{1},...,x_{d}] X=[x1,...,xd]输入到BP算法模型中时,首先能获得隐藏层神经元的输入 α = X V = [ α 1 , . . . , α q ] \alpha=XV=[\alpha_{1},...,\alpha_{q}] α=XV=[α1,...,αq]
  3. 隐藏层的输出 b = [ f ( α 1 − γ 1 ) , . . . , f ( α h − γ h ) , . . . , f ( α q − γ q ) ] = [ b 1 , . . . , b h , . . . , b q ] b=[f(\alpha_{1}-\gamma_{1}),...,f(\alpha_{h}-\gamma_{h}),...,f(\alpha_{q}-\gamma_{q})]=[b_{1},...,b_{h},...,b_{q}] b=[f(α1γ1),...,f(αhγh),...,f(αqγq)]=[b1,...,bh,...,bq]
  4. 同理从隐藏层到输出层的权值设为 V = [ ω 11 ω 1 j . . . ω 1 l . . . . . . . . . . . . ω h 1 ω h j . . . ω h l . . . . . . . . . . . . ω q 1 ω q j . . . ω q l ] q × l V= {\left[ \begin{matrix} \omega_{11} & \omega_{1j} & ...&\omega_{1l} \\ ...&...&...&...\\ \omega_{h1} & \omega_{hj} & ...&\omega_{hl} \\ ...&...&...&...\\ \omega_{q1}& \omega_{qj} &...& \omega_{ql} \end{matrix} \right] }_{q\times l} V=ω11...ωh1...ωq1ω1j...ωhj...ωqj...............ω1l...ωhl...ωqlq×l
    其中 ω h j \omega_{hj} ωhj的值为从隐藏层输出到输出层神经元的权重
  5. 输出层的输入 β j = b V = [ β 1 , . . . , β j , . . . , β l ] \beta_{j}=bV=[\beta_{1},...,\beta_{j},...,\beta_{l}] βj=bV=[β1,...,βj,...,βl]
  6. 输出层的输出 o u t p u t = [ f ( θ 1 − β 1 ) , . . . , f ( θ h − β h ) , . . . , f ( θ q − γ q ) ] = [ o 1 , . . . , o h , . . . , o q ] output=[f(\theta_{1}-\beta_{1}),...,f(\theta_{h}-\beta_{h}),...,f(\theta_{q}-\gamma_{q})]=[o_{1},...,o_{h},...,o_{q}] output=[f(θ1β1),...,f(θhβh),...,f(θqγq)]=[o1,...,oh,...,oq]

误差反向传播

在上面我们获得了这一组数据的输出 o u t p u t output output,我们需要根据均方误差公式调整 B P BP BP算法中的参数:
输出层阈值 θ \theta θ 隐藏层到输出层的权重 ω \omega ω 隐藏层阈值 γ \gamma γ 输入层到隐藏层 v v v
均方误差计算公式: E k = 1 2 Σ j = 1 l ( o j − t a r g e t j ) 2 E_{k}=\frac{1}{2}{\Sigma}^{l}_{j=1}(o_{j}-target_{j})^2 Ek=21Σj=1l(ojtargetj)2
因此对于给定的学习率 η \eta η,更新 B P BP BP算法的参数。 η ∈ ( 0 , 1 ) \eta \in(0,1) η(0,1), η \eta η值 过大则容易引起震荡,过小则容易收敛速度过慢。通常设置 η = 0.1 \eta=0.1 η=0.1,在更新参数 θ \theta θ ω \omega ω与参数 γ \gamma γ v v v中的 η \eta η值并不相同
算法中所有的参数 p a r a m e t e r s parameters parameters都是同样的更新方式 p a r a m e t e r s = p a r a m e t e r s + Δ p a r a m e t e r s parameters = parameters+\Delta parameters parameters=parameters+Δparameters

更新输出层阈值 θ \theta θ

θ j = θ j + Δ θ j \theta_{j}=\theta_{j}+\Delta\theta_{j} θj=θj+Δθj
其中 Δ θ j = − η 1 g j \Delta\theta_{j}=-\eta_{1} g_{j} Δθj=η1gj
其中 g j = o j ( 1 − o j ) ( t a r g e t j − o j ) (1) g_{j}=o_{j}(1-o_{j})(target_{j}-o_{j}) \tag1 gj=oj(1oj)(targetjoj)(1)

更新隐藏层到输出层的权重值 ω \omega ω

ω h j = ω h j + Δ ω h j \omega_{hj}=\omega_{hj}+\Delta\omega_{hj} ωhj=ωhj+Δωhj
其中 Δ ω h j = η 1 g j b h \Delta\omega_{hj}=\eta_{1} g_{j} b_{h} Δωhj=η1gjbh
其中 g j g_{j} gj与公式(1)中的 g j g_{j} gj为同一个, b h b_{h} bh为隐藏层中第 h h h个神经元的输出

更新隐藏层神经元的阈值 γ \gamma γ

γ h = γ h + Δ γ h \gamma_{h}=\gamma_{h}+\Delta\gamma_{h} γh=γh+Δγh
其中 Δ γ h = − η 2 e h \Delta\gamma_{h}=-\eta_{2} e_{h} Δγh=η2eh
其中 e h = b h ( 1 − b h ) Σ j = 1 l ω h j g j (2) e_{h}=b_{h}(1-b_{h})\Sigma^{l}_{j=1}\omega_{hj}g_{j}\tag2 eh=bh(1bh)Σj=1lωhjgj(2)

最后更新输入层到隐藏层的权重值 v v v

v i h = v i h + Δ v i h v_{ih}=v_{ih}+\Delta v_{ih} vih=vih+Δvih
其中 Δ v i h = η 2 e h x i \Delta v_{ih}=\eta_{2} e_{h}x_{i} Δvih=η2ehxi
其中 e h e_{h} eh为公式(2)中的 e h e_{h} eh为同一个, x i x_{i} xi为输入层中的第 i i i个输入值。

最后经过训练反复迭代,使得 B P BP BP算法中的各项参数能够有效的作用于数据集上。以上的内容总结都是基于西瓜书,更有详细信息,请参阅西瓜书。本文如有错误,希望不吝指教。

代码实现

code by young_monkeysun 2019
import numpy
import matplotlib.pyplot as plt
import csv
import pandas
import random
from sklearn.datasets import load_iris
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import MinMaxScaler
plt.rcParams['font.sans-serif']=['SimHei']
plt.rcParams['axes.unicode_minus'] = False
#定义画图函数
def draw_Eks(array):
    plt.plot(range(1,len(array)+1),array,label = '累计均方误差')
    plt.xlabel("训练次数")
    plt.ylabel("数据集单次训练累计均方误差")
    plt.legend()
    plt.show()

#BP神经网络学习算法
class BP_net(object):
    def __init__(self , eta , n_inter , q  ):
        self.eta  = eta
        self.inter = n_inter
        self.q = q

    def Sigmoid(self,x): 
       return 1.0/(1+numpy.exp(-x))  

     #隐藏层输入
    def input_hide(self,x):
        return numpy.dot(x , self.v.T)
    #隐藏层输出
    def output_hide(self,x):
        Alpha = self.input_hide(x)
        Al_Gam=Alpha - self.Gamma
        self.bh =  self.Sigmoid(Al_Gam)
        return self.bh
    #输出层输入
    def input_out(self,x):
        bh = self.output_hide(x)
        return numpy.dot(bh,self.W.T)
    #输出层输出
    def predict(self, x):
        beta = self.input_out(x)
        bet_Thet = beta - self.Theta 
        return self.Sigmoid(bet_Thet)
    def fit(self , x , y):
        '''初始化输入层到隐藏层的权值
            假设单个训练样本xi有3个属性,隐藏层神经元有4个
                Vih = [
                        [v1 , v2 , v3 ]
                        [v4 , v5 , v6 ]
                        [v7 , v8 , v9 ]
                        [v10 , v11 , v12]
                       ]
                X = [
                        [x1 , x2 , x3]
                        ...........
                        [Xn-2 , Xn-1 , Xn ]
                    ]
            隐藏层输入
            Alpha = [X[0]*Vih[0] , X[0]*Vih[1] , X[0]*Vih[2] , X[0]*Vih[3]]
            隐藏层输出
            bh = Sigmoid(Alpha - Gamma)
            隐藏层到输出层权值,设隐藏层神经元为4个
            W = [
                 [w1 , w2 , w3 , w4]
                 [w5 , w6 , w7 , w8]
                 [w9 , w10 , w11 , w12]
                ]
            输出层输入
            beta = [W[0]*bh , w[1]*bh , w[2]*bh ]
            输出层输出
            y = Sigmoid[beta - Theta]
        '''
        self.v = numpy.random.rand(self.q , x.shape[1])                                                                                                                                                                                                                         
        #初始化隐藏层神经元阈值
        self.Gamma = numpy.random.rand(self.q) 
        #初始化隐藏层到输出层权值
        self.W = numpy.random.rand(1,self.q)
        #初始化输出层神经元阈值
        self.Theta = numpy.random.rand(1)
        #创建总均方误差统计数组
        self.Eks = []
        #执行self.inter次训练
        for N in range(self.inter):
            #单次训练错误次数初始化为0
            Ek= []
            #从训练集中抽取单个训练样本
            for xi , target in zip(x,y):
                #获得输出层输出
                self.y=self.predict(xi)
                #计算单次输出均方误差
                Ek_ = self.y-target
                Ek.append(Ek_)
                #调整隐藏层到输出层权值和阈值
                gj = self.y*(1-self.y)*(target-self.y)
                #计算Δθ
                Delta_Theta = (-1)*self.eta*gj
                #更新θ
                self.Theta +=Delta_Theta
                #计算Δw
                Delta_w = self.eta*gj*self.bh
                #更新W
                self.W += Delta_w

                #调整输入层到隐藏层的权值和阈值
                #计算各个ΣWhj_gj的值
                Sigma_W_g = self.W*gj
                Sigma_W_g = Sigma_W_g[0]
                #计算隐藏层各个神经元的eh
                eh=[]
                for n in range(len(Sigma_W_g)):
                    eh_ = self.bh[n]*(1-self.bh[n])*Sigma_W_g[n]
                    eh.append(eh_)
                #计算Δγ
                Delta_Gamma = [self.eta*eh[i] for i in range(len(eh))]
                Delta_Gamma = numpy.asarray(Delta_Gamma)
                Delta_Gamma = Delta_Gamma[0]
                #更新阈值γ
                self.Gamma -=Delta_Gamma
                #计算Δv
                for n in range(len(self.v)):
                    Delta_v = self.eta*eh[n]*self.v[n]
                    self.v[n] +=Delta_v
            self.Eks.append(numpy.average(0.5*sum(numpy.square(Ek))))

iris = load_iris()
x= iris['data']
y = iris['target']
y = y.reshape(-1,1)
MinMaxScaler = MinMaxScaler(feature_range=(0,1))
x = MinMaxScaler.fit_transform(x)
y = MinMaxScaler.fit_transform(y)
x_train ,x_test,y_train ,y_test = train_test_split(x,y,test_size = 0.3)


#设置学习率,训练次数,隐藏层神经元个数
bp_net =BP_net(eta = 0.01 , n_inter = 2000 , q =8)
#训练样本
bp_net.fit(x_train,y_train )
draw_Eks(bp_net.Eks)


#仿真输出和实际输出对比图
netout_target = []
for i in range(len(y_test)):  
   netout_target.append(bp_net.predict(x_test[i]))
y_test = MinMaxScaler.inverse_transform(y_test)
netout_target = MinMaxScaler.inverse_transform(netout_target)
for i in range(len(y_test)):
    if netout_target[i]<=0.66:
        netout_target[i]=0
    elif netout_target[i]>0.66 and netout_target[i]<=1.32:
        netout_target[i]=1
    else:
        netout_target[i]=2
#画出实际值和预测值折线图
plt.plot(netout_target, marker = 'o', label = '预测值')
plt.plot(y_test, marker = 'x', label = '实际值')
plt.xlabel('样本')
plt.ylabel('类别')
plt.title('预测类别与实际类别对比图')
plt.legend()
plt.show()
#画出误差率饼图
correctN = 0
wrongN =0
for i in range(len(y_test)):
    if netout_target[i]==y_test[i]:
        correctN+=1
    else:
        wrongN+=1

cor_and_wrong=[]
cor_and_wrong.append(correctN)
cor_and_wrong.append(wrongN)
label=["正确","错误"]
plt.pie(cor_and_wrong,labels=label,autopct="%.2f%%")
plt.title('错误率饼图')
plt.legend(loc="best")
plt.show()

总结

算法都是基于数学,能把其中的数学推导理解,码代码只是花时间的问题。话虽如此但这毕竟是简单的机器学习算法,以后可能就不会这么想了

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值