西瓜书课后题——第五章(神经网络)

课后题5.5:编程实现一个标准bp算法和一个累积bp算法,用这两个算法训练一个带有单隐层的网络,并给出在西瓜数据集3.0上的效果

首先,单隐层网络,就是输入和输出层之间只有一层神经元的网络,见西瓜书P102图 5.7所示

标准bp算法,如书上P102-104所述,每次更新参数只使用一个样本,所以到达收敛所需要训练的次数会比较多。

累积bp算法,每次更新参数时,使用的是所有的训练样本,也就是一个epoch之后再进行参数更新。所以训练次数可能比较少。

下面给出实现这两个算法的代码:

import numpy as np

def dataSet():
    # 西瓜数据集离散化
    X = np.mat('2,3,3,2,1,2,3,3,3,2,1,1,2,1,3,1,2;\
            1,1,1,1,1,2,2,2,2,3,3,1,2,2,2,1,1;\
            2,3,2,3,2,2,2,2,3,1,1,2,2,3,2,2,3;\
            3,3,3,3,3,3,2,3,2,3,1,1,2,2,3,1,2;\
            1,1,1,1,1,2,2,2,2,3,3,3,1,1,2,3,2;\
            1,1,1,1,1,2,2,1,1,2,1,2,1,1,2,1,1;\
            0.697,0.774,0.634,0.668,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\
            ').T
    X = np.array(X)         # 样本属性集合,行表示一个样本,列表示一个属性
    Y = np.mat('1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0')
    Y = np.array(Y).T          # 每个样本对应的标签
    return X, Y


def sigmod(x):
    return 1.0/(1.0+np.exp(-x))


def bpstand(hideNum):                         # 标准的反向传播算法
    X,Y = dataSet()
    V = np.random.rand(X.shape[1],hideNum)      # 权值及偏置初始化
    V_b = np.random.rand(1,hideNum)
    W = np.random.rand(hideNum,Y.shape[1])
    W_b = np.random.rand(1,Y.shape[1])

    rate = 0.1
    error = 0.001
    maxTrainNum = 1000000
    trainNum = 0
    loss = 10

    while (loss>error) and (trainNum < maxTrainNum):
        for k in range(X.shape[0]):               # 标准bp方法一次只处理一个样本
            H = sigmod(X[k,:].dot(V)-V_b)          # 因为书上一直给出的是减去阈值,所以这里用减号。
            Y_ = sigmod(H.dot(W)-W_b)              # 其实大部分情况下人们都用的是加上偏置b这种表达方式
            loss = sum((Y[k]-Y_)**2)*0.5           # 改成加号后只需要在下面更新参数时也用加号即可

            g = Y_*(1-Y_)*(Y[k]-Y_)        #  计算相应的梯度,及更新参数。 此处特别注意维度的正确对应关系
            e = H*(1-H)*g.dot(W.T)
            W += rate*H.T.dot(g)
            W_b -= rate*g
            V += rate*X[k].reshape(1,X[k].size).T.dot(e)
            V_b -= rate*e
            trainNum += 1

    print("总训练次数:",trainNum)
    print("最终损失:",loss)
    print("V:",V)
    print("V_b:",V_b)
    print("W:",W)
    print("W_b:",W_b)


def bpAccum(hideNum):                   # 累积bp算法
    X,Y = dataSet()
    V = np.random.rand(X.shape[1],hideNum)
    V_b = np.random.rand(1,hideNum)
    W = np.random.rand(hideNum,Y.shape[1])
    W_b = np.random.rand(1,Y.shape[1])

    rate = 0.1
    error = 0.001
    maxTrainNum = 1000000
    trainNum = 0
    loss = 10

    while (loss>error) and (trainNum<maxTrainNum):
        H = sigmod(X.dot(V)-V_b)
        Y_ = sigmod(H.dot(W)-W_b)
        loss = 0.5*sum((Y-Y_)**2)/X.shape[0]

        g = Y_*(1-Y_)*(Y-Y_)                 # 对应元素相乘,类似于matlab中的点乘
        e = H*(1-H)*g.dot(W.T)
        W += rate*H.T.dot(g)
        W_b -= rate*g.sum(axis=0)
        V += rate*X.T.dot(e)
        V_b -= rate*e.sum(axis=0)
        trainNum += 1

    print("总训练次数:",trainNum)
    print("最终损失:",loss)
    print("V:",V)
    print("V_b:",V_b)
    print("W:",W)
    print("W_b:",W_b)

if __name__ == '__main__':
    bpstand(5)
    bpAccum(5)

最终两种方法得到的损失以及训练次数分别是:

标准bp算法:
总训练次数: 65008
最终损失: [ 0.00099985]

累积bp算法:
总训练次数: 6578
最终损失: [ 0.00099987]

可以看出,在得到同样精度的情况下,累积bp算法的训练次数要比标准bp算法的训练次数少的多。 

参考这篇博客:https://blog.csdn.net/u013527937/article/details/58637367

课后题5.6:动态调整学习率提高收敛速度。

关于这个问题,参见这篇博客对于BP算法优化的一些讨论。 

猜你喜欢

转载自blog.csdn.net/qq_37691909/article/details/85338450