写在前面

好久不见,前几天忙着复习就没有更新啦~经过预训练之后,发现模型表现的不太好,准确率大概也就在45%,经过分析之后发现了之前程序的一个bug,并进行了修复,本篇文章将记录问题以及解决过程。

上次遗留

用ROC曲线等评估模型性能:因为模型初调的时候准确率不高,大概在45%,远不能满足要求,因此我就没有用该方法进行验证,而是通过观察程序、对照公式等方法查找问题。

模型经过初调(1w张训练集,1w张验证集),学习率0.05,双隐层,每层各16个神经元,表现如下所示:

可以看到最后的准确率都是45%多一点点,不咋地,所以今天主要是在提升这个。

在这里我不得不提一下batch的好处了,当我刚开始没用batch的时候硬跑整个数据集,就是下面这个结果:

正确率只有12.6%。

今天的工作

我之前的双隐层用的ReLu函数,输出层用的是sigmoid函数,但是我发现了一个问题,那就是我的神经元的阈值会出现负数,那么神经元输出小于0的可能性就比较大,那么在ReLU函数中,当神经元的输入小于0的时候,这个神经元根本就是没有用的,相当于少了很多的信息。

之前我用的sigmoid函数,但是后来舍弃的主要原因是它会发生梯度消失,但是当时我没分batch进行训练,所以我觉得分了batch之后效果可能会好很多,最起码在我的这个神经网络里面,ReLU函数的表现确实不如人意,因此我将其换回了sigmoid函数。

我将batch size设置成了100,跑1w张,100个epoch,学习率0.1

训练函数更新为如下所示:

@jit
def TrainBody(w, v, gamma, theta_1, theta_2, theta_3, x,\
            dw, dv, dgamma, dtheta_1, dtheta_2, dtheta_3, \
                m, m_out, n, n_out, y, y_out, y_pre):

    for i in range(total_y):
        dtheta_3[0][i] = dtheta_3[0][i] + (y[0][i] - y_out[0][i]) * dsigmoid(y_pre[0][i])
    for i in range(n_num):
        for k in range(total_y):
            dtheta_2[0][i] = dtheta_2[0][i] + (y[0][k] - y_out[0][k]) * dsigmoid(y_pre[0][k]) * \
                        gamma[i][k] * dsigmoid(n[0][i])
    for i in range(m_num):
        for j in range(total_y):
            for k in range(n_num):
                dtheta_1[0][i] = dtheta_1[0][i] + (y[0][j] - y_out[0][j]) * dsigmoid(y_pre[0][j]) * \
                            gamma[k][j] * dsigmoid(n[0][k]) * v[i][k] * dsigmoid(m[0][i])
    for i in range(n_num):
        for j in range(total_y):
            dgamma[i][j] = dgamma[i][j] + (y_out[0][j] - y[0][j]) * n_out[0][i] * dsigmoid(y_pre[0][j])
    for i in range(m_num):
        for j in range(n_num):
            for k in range(total_y):
                dv[i][j] = dv[i][j] + (y_out[0][k] - y[0][k]) * gamma[j][k] * dsigmoid(y_pre[0][k]) * \
                    m_out[0][i] * dsigmoid(n[0][j])
    for i in range(total_x):
        for j in range(m_num):
            for k in range(total_y):
                for l in range(n_num):
                    dw[i][j] = dw[i][j] + (y_out[0][k] - y[0][k]) * dsigmoid(y_pre[0][k]) * gamma[l][k] * \
                            dsigmoid(n[0][l]) * v[j][l] * dsigmoid(m[0][j]) * x[i]

其中,batch的参数更新方式如下所示:

if (k + 1) % division == 0:
    # update w,v,gama,theta_1,theta_2,theta_3
    w = w - (study_step * dw) / division
    v = v - (study_step * dv) / division
    gamma = gamma - (study_step * dgamma) / division
    theta_1 = theta_1 - (study_step * dtheta_1) / division
    theta_2 = theta_2 - (study_step * dtheta_2) / division
    theta_3 = theta_3 - (study_step * dtheta_3) / division

    dw = np.zeros(((total_x, m_num)), dtype=float)  # (()) is used to confirm line and row
    dv = np.zeros((m_num, n_num), dtype=float)
    dgamma = np.zeros((n_num, total_y), dtype=float)
    dtheta_1 = np.zeros(((1, m_num)), dtype=float)
    dtheta_2 = np.zeros(((1, n_num)), dtype=float)
    dtheta_3 = np.zeros(((1, total_y)), dtype=float)
    print("\rtrain_n is %d" % (k), end='')

今天我也学习到了输出控制台不换行,在同一行内更新输出的办法,如下所示:

print("\rtrain_n is %d" % (k), end='')

明日工作

1:根据今天晚上模型训练的结果,如果结果理想,明天就加大训练集的数量继续训练,如果结果不太理想那就说明不是激活函数的问题,可能是公式出问题,回溯之前的公式推导工作。

2:学习batch normalization相关知识。