之前不太明白神经网络什么层和反向传播什么的,这两天试着写了个纯python+numpy版本的简单神经网络(也就是多层感知机),感觉对原理清楚了不少。
首先生成一些不能线性分割的数据,分三类以上,使用softmax作为输出函数来进行分类:
1 | import numpy as np |
打印出来是这样的
接着定义一些会用到的函数,包括激活函数和它的导数,以及打印模型的函数等等:
1 | def sigmoid(x): |
先看下只用softmax函数(相当于没有隐藏层的神经网络)做分类的效果:
看到只有直线作为决策边界,正确率也只有51%
接下来就是使用神经网络的训练过程了,先来一些原理。简单来说就是每一轮都先计算出一个分类结果(前向传播),然后与标准答案做对比计算出损失,再由损失反过来修改参数的权重(反向传播)。
首先看前向传播的过程,这个很简单,跟逻辑回归类似。一共有三层,输入层,隐藏层和输出层,输入层传进去的数据X和权重W做矩阵乘法得到Z,之后Z通过激活函数f(sigmoid/tanh/relu)得到A,这样就有了隐藏层的输出,再把隐藏层的输出作为输出层的输入,同样的过程得到Z后通过softmax函数(不是激活函数了)得到预测的结果yhat。代码也很简单,这里考虑了不同的激活函数
1 | #forward |
接着是反向传播的过程,首先要计算出损失,这样才可以对损失求梯度来减小损失。softmax都使用交叉熵作为损失函数,因为这样求导后很简单。具体过程可以看这篇文章https://zhuanlan.zhihu.com/p/37740860
得到了输出层的导数是yhat-y,之后由链式法则(这写的参数和上边那个图不一样,A2就是yhat)
1 | dJ/dW2 = dJ/dA2 * dA2/dZ2 * dZ2/dW2 |
前两项就是输出层的导数delta3,第三项就是A1
继续展开
1 | dJ/dW1 = dJ/dA1 * dA1/dZ1 * dZ1/dW1 |
dZ2/dA1就是W2,dA1/dZ1就是激活函数的导数,dZ1/dW1就是X
和这张图对照着看,Z和A的下标需要减1
这样代码也很好写了
1 | #loss |
这样训练函数就写好了,最后预测对softmax的结果取argmax就可以了。完整代码:
1 | def sigmoid(x): |
正确率97.7%,还是很有效果的