我们在之前写了关于如何识别一只猫(一),但它的识别率并不是很高,调参员博主调了好半天才把命中率调到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也好啊。(梅开二度)