机器学习作业 3-layer Forward Neural Networks(BP算法)在ex4Data数据集

ML课堂作业,第五个是三层前向网络,老数据集ex4Data,首先把作业要求贴上来,一共可以分为两个部分,分别是手写版和tensorflow版,因为三层前向网络已经不是线性模型,所以也无法绘制出分类线,通过计算准确性来评估模型。

数据集是老数据集了,需要下载的可以在图中或前面的文章找到链接。三层前向网络是一个非常基础的神经网络,可以看成是若干个感知机的复合,可以选择任意激活函数,这里为了求导方便选择sigmoid,激活函数的作用是引入非线性变化,使得网络可以拟合非线性函数,下面给出网络结构

输入层的xi表示样本的每一个特征,输出层的yi表示样本属于第i类的概率,因为三层前向网络已经不是线性模型,所以我们无法向前面的模型一样在样本图上画出分类线,所以我们用正确率来评估模型的性能,为了避免过拟合以及能够准确评估模型,我们使用五重交叉检验,并取测试集上正确率的平均值作为模型最后的正确率。

训练过程的几个关键如上图,数据集以矩阵对的形式,其中样本x共有d个特征,可分为L个类别,我们还可以看出这种全连接的网络参数数量非常之大,在深度学习中这种全连接层一般放在最后或者干脆不用,最优化方法采用梯度下降,则对每个参数都要求对应的梯度,由于前向网络事实上是一个线性函数和激活函数的多重复合,所以梯度求导将运用链式法测,对链式法则不熟悉的同学可以回顾一下高数课本。

简单介绍一下交叉验证,我们学习模型的最终目的是在未知的样本上获得更好的预测性能,而不是追求在训练集上的最好性能,虽然训练集能一定程度的反应未知样本的特性,但可能存在无法反映的或者只有训练集中才有的特性,被我们的学习模型学得,这样会降低模型的泛化能力,所以我们并不使用整个数据集都来训练,我们选取一部分数据来训练一部分不参加训练用作测试。

k折交叉验证法就是将整个数据集分为不相交的k个部分,然后选取其中的一个部分为测试集,其他部分为训练集,获得测试准确率,然后重复上述步骤,直到每个部分都充当过一次测试集,我们以平均准确率作为模型的准确率。

学习过程分为两个步骤,首先是数据的前向传播,也就是让样本通过整个网络得到当前网络的输出,然后是误差的反向传播,根据输出和标签以及对参数求梯度的结果,反向逐步计算出各参数的梯度值,叫做误差的反向传播,通过课件中的图片来简单讲解一下。

首先我们计算隐层和输出层之间的权值的梯度

这是一个简单的链式求导,我们定义了OutputLayer_error,输出层神经元偏置的梯度则为

紧接着求输入层到隐层的连接权值的梯度:

我们可以看到前面我们计算过的OutputLayer_error在这里用上了,并且计算下一层的梯度就是上一层的误差乘上本层的本地梯度,便可以得到本层的误差,在更多层的前向网络中梯度的计算和这里并无太大差别,无非就是传递的层数多一些,这看上去与很像误差从输出层反向传播至输入层,所以叫做BP(反向传播)算法,结合CS231N中计算图来理解将会更好理解BP算法,将每种计算(例如:加减乘除、sigmoid)或者说每个操作看成一个f函数并以一个计算节点表示,计算节点输入层的梯度就等于输出层传回的梯度乘上本地f对输入层的梯度,这样对无法求解析式的复杂网络我们也可以计算对某个样本值的梯度,这使得bp算法成为现在神经网络中广泛运用的算法。

最后我们有隐层偏置的梯度:

 

下面给出代码:

import numpy as np
import matplotlib.pyplot as plt
import nolinear


learn_rate = 0.05
output_size = 2
hidden_size = 7

data_x = np.loadtxt("ex4Data/ex4x.dat")
data_y = np.loadtxt("ex4Data/ex4y.dat")
mean = data_x.mean(axis=0)
variance = data_x.std(axis=0)
data_x = (data_x-mean)/variance
data_y = data_y.reshape(-1, 1)          # 拼接
temp = np.ones(data_y.size)
data_x = np.c_[data_x, temp]

y = np.zeros([data_y.size, output_size])
for i in range(data_y.size):
    if data_y[i] == 0:
        y[i][0] = 1
    if data_y[i] == 1:
        y[i][1] = 1

data_sets = list()
for i in range(data_y.size):
    if i % 8 == 0 and i != 0:
        data_sets.append(data_x[i-8:i, :])
    if i == data_y.size-1:
        data_sets.append(data_x[i-7:i+1, :])

positive_x = data_x[0:40, :]
positive_label = y[0:40, :]
negative_x = data_x[40:80, :]
negative_label = y[40:80, :]
total = 0

for j in range(5):
    x_ = np.r_[np.delete(positive_x, range(j*8, (j+1)*8), 0), np.delete(negative_x, range(j*8, (j+1)*8), 0)]
    y_ = np.r_[np.delete(positive_label, range(j*8, (j+1)*8), 0), np.delete(negative_label, range(j*8, (j+1)*8), 0)]

    test_x = np.r_[data_sets[j], data_sets[j+5]]

    test_label = np.r_[data_y[j*8:(j+1)*8], data_y[(j+5)*8:(j+6)*8]]
    # data_x = np.mat(data_x)
    data_x = np.mat(x_)
    temp = np.ones(data_y.size-16)
    weight_input = np.mat(np.random.normal(size=(data_x.shape[1], hidden_size)))
    weight_hidden = np.mat(np.random.normal(size=(hidden_size+1, output_size)))
    steps = 600
    loss_values = list()

    for i in range(steps):
        # 前向传播过程
        hidden_input = data_x*weight_input                      # ah+yh
        hidden_out = nolinear.sigmods(hidden_input)             # bn
        hidden_out_ = np.c_[hidden_out, temp]
        # print(hidden_out)                                     # weight_hidden 最后一行才是bias
        output_input = hidden_out_*weight_hidden
        output = nolinear.sigmods(output_input)
        # 计算损失
        loss = 0.5*np.sum(np.multiply(output-y_,  output-y_))
        loss_values.append(loss)
        # 反向传播过程

        output_error = np.multiply(np.multiply(output-y_, output), 1-output)
        dew_hidden = hidden_out_.T*output_error
        output_error_ = dew_hidden[7]
        weight_hidden_ = np.delete(weight_hidden, 7, axis=0)
        hidden_error = np.multiply(np.multiply(output_error_*weight_hidden_.T, hidden_out), 1-hidden_out)
        dew_input = data_x.T*hidden_error

        weight_hidden = weight_hidden-learn_rate*dew_hidden
        weight_input = weight_input-learn_rate*dew_input

    plt.plot(loss_values)
    plt.show()

    temp = np.ones(test_label.size)
    hidden_input = test_x*weight_input
    hidden_out = nolinear.sigmods(hidden_input)
    hidden_out_ = np.c_[hidden_out, temp]
    output_input = hidden_out_*weight_hidden
    output = nolinear.sigmods(output_input)

    count = 0
    print(output)
    print(test_label)
    output = np.array(output)
    for i in range(test_label.size):
        outs = output[i].ravel()
        outs = outs.tolist()
        if int(test_label[i]) == outs.index(max(outs)):
            count = count+1
    print("test accuracy", count/test_label.size)
    total = total+count/test_label.size
print("accuracy:", total/5)

 

程序会画出每次的loss值变化以及打印每次验证的准确率和最后的平均准确率,数据划分是有序的,如果改成无序则每次检验其准确率差距会更小,注意保证训练子集和测试子集在概率分布上与整个数据集一致。

第一次检验,用编号为0-7的正例和40-47的反例为测试集其余为训练集。

第二次检验是以编号为8-15的正例和48-55的反例为测试集,其余为训练集:

第三次以及后以此类推:

第四次:

第五次以及最后平均准确率:

最后平均准确率是80%,因为是有序划分,第三第四测试集可能数据都集中在中间混杂在一起的位置,所以测试准确率比较低,对程序稍作修改使其输出每次训练集上的准确率:

可以看到在第四次检测模型表现得很不好,这基本上就是本次的全部工作,后续可能有更深一步的探究,待更新……

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章