如何编程来识别一只猫(二)

2020年9月22日21:57:36 发表评论

我们在之前写了关于如何识别一只猫(一),但它的识别率并不是很高,调参员博主调了好半天才把命中率调到74%(这里是指测试集的命中率,训练集命中率是99%)。如何进一步提高命中率是一个值得思考的问题。

一般来说,假如训练集的命中率和测试集的命中率相差甚大,就把这种现象称之为“过拟合”(英文叫over fitting)。显然,在上一篇文章我们就属于这种情况。改善这种情况的解决方案也很简单,那就是设计更加复杂的神经网络,在(一)我们就只用了一个输出节点,也就是说只用了一层模型,那么现在让我们增加一层隐藏层(介于输入和输出层之间的层)。

其实也不是很复杂的东西,在(一)的基础上稍加修改就好了,原理就不多说了,直接看周志华老师的视频吧。我这里直接硬怼代码。

一、定义sigmod函数以及参数初始化

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

#  init parameters
def init_parameters(n_x, n_h, n_y):    #n_x是输入层特征数,n_h是隐藏层节点数,n_y是输出层节点数
    np.random.seed(1)  #设置随机种字为1是为了让结果更加稳定,不然你的准确率会飘
    w1 = np.random.randn(n_h, n_x)*0.01  #w1,b1是第一层的参数
    b1 = np.zeros((n_h, 1))
    w2 = np.random.randn(n_y, n_h)*0.01 #w2,b2是第二层的参数
    b2 = np.zeros((n_y, 1))
    return w1, b1, w2, b2

二、定义loss函数

#  define loss function
def cross_entropy_loss(x, y, w1, b1, w2, b2, m):
    z1, a1, z2, a2 = predict(x, w1, b1, w2, b2)  #这个函数是正向传播函数,也能用作预测
    loss = y*np.log(a2)+(1-y)*np.log(1-a2)  #这里使用的是交叉熵Loss函数
    loss = np.sum(-loss)/m
    return loss, z1, a1, z2, a2
#  predict
def predict(x, w1, b1, w2, b2):
    z1 = w1.dot(x) + b1
    a1 = np.tanh(z1)  #注意,第一层的激活函数用的是tanh函数而不是sigmod
    z2 = w2.dot(a1) + b2  #这是因为sigmod只对二分类比较好,其他一般tanh好过sigmod
    a2 = sigmod(z2)   #输出层用sigmod激活函数
    return z1, a1, z2, a2

三、求梯度

#  get grad
def get_grad(w1, b1, w2, b2, a1, z1, a2, z2, x, y, m):
    dz2 = a2 - y
    dw2 = dz2.dot(a1.T)/m
    db2 = np.sum(dz2, axis=1, keepdims=True)/m
    d_gz = 1-np.power(a1, 2)   
    dz1 = w2.T.dot(dz2)*d_gz
    dw1 = dz1.dot(x.T)
    db1 = np.sum(dz1, axis=1, keepdims=True)/m
    return dw1, db1, dw2, db2

四、迭代梯度下降

# grad descent
def grad_descent(x, y, w1, b1, w2, b2, m, learning_rate, iteration):
    loss = []
    for i in range(iteration):
        loss_tmp, z1, a1, z2, a2 = cross_entropy_loss(x, y, w1, b1, w2, b2, m)
        loss.append(loss_tmp)
        dw1, db1, dw2, db2 = get_grad(w1, b1, w2, b2, a1, z1, a2, z2, x, y, m)
        w1 -= learning_rate*dw1   #更新超参数
        b1 -= learning_rate*db1
        w2 -= learning_rate*dw2
        b2 -= learning_rate*db2
        # if i % 100 == 0:
            # print('loss = '+str(loss_tmp))
    return loss    #loss包含每次迭代的损失

五、求准确率

#  get the accuracy
def get_accuracy(y, y_hat):
    y_hat = np.array(y_hat).reshape(1,y.shape[1])
    for i in range(y_hat.shape[1]):
        y_hat[0, i] = 1 if y_hat[0, i] > 0.5 else 0
    return (1-np.sum(y-y_hat)/y.shape[1])*100

结论

通过多加一层,我们反复微调超参数,发现结果比没有隐藏层的方案好过很多。测试集的准确率由(一)的74%提高到了94%这么一个高度,足足提升了20个点,成就感爆棚,有一种马上就要发论文的冲动。开始调的时候调的都是学习率和迭代次数,以致于测试集的准确率都是82%,直到我设置我认为最优的学习率和迭代次数,再调节隐藏层的节点数,就有了质的飞跃。最后反复测试多次,最优结果保持在94%的成绩。

有一点需要注意的是,w1和w2不是都初始化为0,因为那样的话所有隐藏层节点都是执行一样的运算,最后得出的是一个线性的结果,这样是没有意义的。

附录

代码我托管在码云上了,有需要的同学可以去那下载,有空的话给个start也好啊。(梅开二度)

 

flyingsheep

发表评论