曾经,基于统计学习的机器学习方法在AI领域占据了半边天,逻辑回归、决策树、支持向量机等算法不久就耳熟能详,「特征工程」一词瞬间火了起来,普遍认为数据弄得好+特征工程做得好≈模型能work。

        后来,学者通过模仿神经系统里神经元的工作机制,研究出一种叫做感知器(Perceptron)的东西,发现竟然可以解决二分类问题,遗憾的是,感知器在线性不可分的时候无法work。后来,学者堆了几层感知器,发现竟然可以解决线性不可分问题了,于是有了一个新的名字——多层感知机(MLP),从此一发不可收拾,逐渐演变为“神经网络”、“深度神经网络”,等等。

        有了多层感知机,理论上具备了“可学习”的能力,那么如何才能学习呢?这个时候大名鼎鼎的Hinton出场了,他把先进的反向传播算法(BP算法)用在了MLP中,使得模型以一种“不断自我纠正”的模式进行学习。尽管后来Hinton多次提到BP算法和自然界生物大脑中存在的机制并不相同,不断否定自己提出的方法(十分佩服敢于质疑自己提出的理论的科学家),但BP算法一直存在于主流模型中。我觉得在具有革命性的新的方法提出之前,BP算法仍然不会被轻易抛弃。

        故事讲完了,下面就聊一聊感知器和BP算法。

感知器

        什么是感知器?见图1。

iris预测多层感知机 多层感知机bp算法_算法

图1

        简单来说,就是给定一些输入Xi,乘上一些权重Wi,再求和,最后激活(激活:比如给定一个阈值,输出±1;或用sigmoid函数压缩到0-1之间)。

        假如研究的是一个二分类问题,分类结果只有+1和-1:

        用符号表示的话就是:

iris预测多层感知机 多层感知机bp算法_iris预测多层感知机_02

        意思是:求和结果大于给定阈值的,判别为类别1,否则判别为类别-1。

        如果用一个等式来总结上面的式子,那就是:

iris预测多层感知机 多层感知机bp算法_深度学习_03

        其中,sign(x),当x>0时取1,当x=0时取0,当x<0时取-1 。

BP算法

        什么是BP算法?见图2。

iris预测多层感知机 多层感知机bp算法_iris预测多层感知机_04

图2

        其中,η指学习率,y(i)指真实标签,output(i)指最后一层输出,(i)指正向传播第i层的激活值。

        嗯?不是说BP算法挺复杂吗?怎么才一个式子?

        没错,BP算法是挺复杂的,但这个式子表达了算法的核心所在,即:正向传播过去之后,根据最后一层和真实标签产生的误差来更新前面每一层的权重,从而不断“纠正”模型的错误,当错误纠正得差不多了(收敛了),就可以说模型学到知识了。

        下面用感知器为例,看看为什么BP可以“自我纠错”(下面的数据都是根据想要的结果凑出来的,一般初始化的权重不会这么大)。

        情况一:

iris预测多层感知机 多层感知机bp算法_深度学习_05

        讲解:假如输入样本为V=[1, 1, 1](见上图蓝点),初始化一个权重w,通过计算,发现求和为1>0(0.1*1+0.3*1+0.6*1),说明h(x)=1,也就是output=1,而真实标签是-1,表明此时需要调小输出值,也就是调小w'·v('代表转置),使之更接近真实标签-1才对由于vi>0,所以只能通过调小w,从而更新后的权重就变小了。此时,再计算w'·v,发现结果为0(-0.1*1+0.1*1+0*1),是不是距离标签-1更近了?

        注:输入V=[1, 1, 1],指V=[偏置, v1, v2],第一个位置默认是偏置项,且初始化值为1,也就是y=w'x+b中的b初始化为1。这样做的好处是,可以把偏置和x放到一个式子中计算,无需单独计算b了。

        情况二:

iris预测多层感知机 多层感知机bp算法_深度学习_06

        讲解:步骤和情况一是一样的,就不再赘述。需要注意的是,情况一和情况二偏置对应的权重都减少了,这是正常的,因为偏置某种意义上可以看做是一种“调节器”,当x难以使模型收敛时,b就起作用了。因此,当发现模型不论怎么调损失都降不下去的时候,可以看看是不是忘了添加偏置了。

下面写一个感知器来看看学习效果(重新凑数据)

# 定义样本、标签,手动初始化权重
X = np.array([[1,2,3]]) # 样本x
X = X.reshape(3,1) # 需要调整维度,使得矩阵乘法在维度上能匹配
w = np.array([[0.1,0.2,0.3], # 权重w
             [0.2,0.1,0.2],
             [0.3,0.2,0.1]])
b = np.array([[1,1,1]]) # 偏置b
b = b.reshape(3,1)
Y = np.array([[1,0.5,1]]) # 标签y
Y = Y.reshape(3,1)

print(X.shape,w.shape,b.shape,Y.shape)

        输出:(3, 1) (3, 3) (3, 1) (3, 1)

iris预测多层感知机 多层感知机bp算法_深度学习_07

# 定义前向传播
def propagate(w, b, X, Y):

    m = X.shape[1] # 样本数为m
    A = sigmoid(np.dot(w.T,X)+b) # 激活一波
    cost = -1/m*np.sum(Y*np.log(A)+(1-Y)*np.log(1-A)) # 交叉熵损失
    
    dw = 1/m*np.dot(X,(A-Y).T) # 损失函数对w求导的表达式
    db = 1/m*np.sum(A-Y)

    grads = {"dw": dw,"db":db} 
    
    return grads,cost
    
# 激活函数    
def sigmoid(z):
    s = 1/(1+np.exp(-z))
    return s

# 权重更新    
def update(w, b, X, Y, times, alpha):
    costs = []
    for i in range(times):
        grads, cost = propagate(w, b, X, Y)
        dw = grads['dw']
        db = grads['db']
        
        w = w - alpha*dw  #这两句是核心 
        b = b - alpha*db
        
        costs.append(cost) # 保存损失
        
        params = {'w':w, 'b':b}
        grads = {'dw':dw, 'db':db}
        
    return params, grads, costs

        损失函数和对w、b的偏导数:

iris预测多层感知机 多层感知机bp算法_深度学习_08

        sigmoid函数:

iris预测多层感知机 多层感知机bp算法_深度学习_09

        激活计算:

iris预测多层感知机 多层感知机bp算法_iris预测多层感知机_10

        更新w、b:

iris预测多层感知机 多层感知机bp算法_算法_11

# 开始学习ing:学习率为0.009, 迭代500次
params, grads, costs = update(w, b, X, Y, 500, 0.009)

print('w = \n',params['w'])
print('b = \n',params['b'])

输出:预览一下权重和偏置在不断更新500次后的结果(看一眼即可)

    w =
     [[ 0.23109952 0.048048 0.45545922]
     [ 0.46219905 -0.20390399 0.51091844]
     [ 0.69329857 -0.25585599 0.56637766]]
    b =
    [[1.13460675]
     [1.13460675]
    [1.13460675]]

w_new = params['w']
b_new = params['b']
A_new = sigmoid(np.dot(w_new.T,X)+b_new)
print('未训练,前向传播最后一层输出:\n',sigmoid(np.dot(w.T,X)+b))
print('收敛时,前向传播最后一层输出:\n',A_new)
print('真实标签:\n',Y)

输出:现在来看一看学习的效果:

    未训练,前向传播最后一层输出:(*1)
     [[0.9168273 ]
     [0.88079708]
     [0.88079708]]

    收敛时,前向传播最后一层输出:(*2)
     [[0.98750681]
     [0.50181969]
     [0.98675806]]

    真实标签:(*3)
     [[1. ]
     [0.5]
     [1. ]]

        从上面可以看出,在没有训练的时候,也就是利用我自己随便初始化的权重进行计算后,输出是(*1),和真实标签(*3)差距较大(这里第1、3个数和标签差距不大,是因为我凑的权重恰好算出来是这样的,通常情况是有较大差距的);进行500次学习后,学到了这么一个权重w_new和偏置b_new,使得输出为(*2),可以发现和真实标签很接近了!

        绘制每次迭代的损失,可以看出模型收敛了:

iris预测多层感知机 多层感知机bp算法_算法_12

        小结:虽然现在流行各种高大上的深度网络模型,但这些模型的基础都是感知器和BP算法。尽管现在有了各种可以搭建模型的框架,无需再从0开始手动实现神经网络(挺费时间),但了解一下背后的机理还是挺有意思的。

        剧透一下:后面会写一篇如何从0开始实现一个神经网络,来识别自己写的数字。


如有新的想法,期待交流探讨

iris预测多层感知机 多层感知机bp算法_算法_13