神经网络-自动训练
恭喜你,已经将 Numpy 玩得炉火纯青了。我们今天就来玩点大的,用 Numpy 手工做出来一个神经网络。 上节内容,我们已经介绍了,怎么利用 Numpy 的矩阵乘法来做神经网络中最核心的部分。 而且已经把神经网络的雏形给搭建起来了。
不过光有神经网络的皮可不行,我们还要让它能够自学习起来,做一个可以被训练,能够拟合数据的模型出来。 这里就需要了解一点反向传播的概念了。 不过没关系,即使你没有弄懂其中的奥秘,以不妨碍做出一个能用的 AI 模型。
模型前向预测¶
神经网络能够预测物体,是因为他可以通过一个正向的信息传递,得到输入信息(比如猫的图片)加工后的一个结论(这张图片是猫)。
而且这个过程的一个简单版,我们在上节内容中已经做过了。 简单回顾一下:
上面是原始数据的样子,下面是模型预测出来的样子。
如果你对 draw_scatter()
和 draw_line()
函数感兴趣,你可以看看我怎么写的。
下面看看每个神经层的添加。
但是很明显,对比两张图片,你会发现预测是 y 坐标存在明显的量级上的差距。 而我们今天要讲的,是如何让模型能够学习出来数据背后的规律,让它能够精准地预测数据。
简单介绍反向传播¶
我之前做过一个反向传播的概念的动画。里面大概表达的意思是: 神经网络能够做前向的信息加工和预测,但是并不是一开始就能预测得很准的。模型得通过不断学习,不断修正自己的网络参数,才能慢慢变得精准。
那问题来了,我们通过什么样的方法和原则来做网络参数的修正?使之下次做前向预测的时候更准确?
还好,目前有这么一套叫做梯度反向更新
的规则,可以做这件事。如果你大学学了高数,你应该不算陌生。没学也没关系,我不会讲太多细节,毕竟这是 Numpy 的主场, 而不是算法的主场。
简单来说,我们就是要找到每一个神经层的导数,用导数求梯度,用梯度来更新网络参数。哈哈,有些同学可能在这里听不懂了。没关系,你可以做一次拿来主义, 把它当成工具来用就好了。
这就是你可以直接拿来用的功能,它的目标是计算每一层神经层的更新幅度,并按照学习率来更新网络参数,让网络对数据拟合得越来越好。这里面就涉及到了 Numpy 的矩阵转置 .T
,也有累加的操作 .sum()
。 每次调用 backprop()
功能,我们实际上在干的事就是去计算每一层 w
和 b
的梯度 gw
和 gb
。然后将梯度误差在传递到前面的一层去。
OK,了解了这些,我们就用 Numpy 来做神经网络的更新吧。步骤大概是这样的:
- 准备好数据,搭建好模型
- 开始训练循环
- 前向预测,x 通过每一层计算得到 o2
- 计算误差
- 反向传播,更新网络
运行一下上面的代码,我们就能看到在没有训练的时候,预测出来的模型线数值和预测 y 的数量级是有问题的。 下面我们开始训练,然后再画一张对比图,你就能发现模型训练好了是啥样。
现在你可以试着修改一下神经网络层里面的输入输出神经元个数layer(dim_in, dim_out)
,又或者多加两层神经层(对应要修改反向传播的链路)。 如果你在多加两层神经层后能成功运行,那么就说明你掌握了神经网络反向传播的诀窍~
加入激活函数¶
上一节 我们还提到了添加激活函数,这个激活函数也同样是要考虑到反向传播中的。 和上面一样,我下面的功能是用来计算激活函数的梯度的,你可以当成工具直接来用,不一定要搞清楚其中的原理。
我在上面帮你准备了一些激活函数,我们在搭建神经网络的时候就能用得到。上面一个环节我们训练的是一个相对简单的线性数据,这次来一个非线性,比较复杂的。
我们再来用一个没有激活函数的网络训练一次,你会发现,模型学不好。不信你运行下面的代码,看到的应该是一张拟合欠佳的结果。
现在我们加入激活函数。在第一层神经网络的结果 o1
处添加一个非线性函数。并加上对应的非线性反向传播步骤,再来一次。
你看,这不就拟合上了这些奇怪的数据点了吗。说明非线性的激活函数还是很有用的。现在你可以尝试修改激活函数的方法, 并在反向传播的地方也修改一下对应的激活函数的反向传播。
我用 Numpy 写的神经网络库¶
看到上面的教学案例还不过瘾,要不要上一些实战的神经网络?我前几年因为突发奇想,闲着无聊,想自己实现一下神经网络库,顺便看看自己能力如何,能做到什么程度, 所以我就用 Numpy 自己手写了一个类似 Tensorflow 和 Pytorch 的神经网络库。 这个库完全依赖于 Numpy,名字叫 npnet 而且性能也是杠杠的。我的 Github 链接: https://github.com/MorvanZhou/npnet
有兴趣的朋友,也可以直接用 pip 安装,在本地运行:
python3 -m pip install npnet
# 或
pip install npnet
或者直接在这里的交互窗口运行:
首先用 micropip 安装我的 npnet
,然后在用它构建网络,并训练运行。
总结¶
在这一节我们完成了徒手构建简单神经网络的练习,但因为内容量的原因,我只提到了数值型预测的神经网络,而没有提到分类的神经网络。 有兴趣的朋友可以结合一下这两节内容,自己实现一个二分类的梯度更新。
Numpy 的交互式教学内容也告一段落啦。希望你们经历这一路,有学到不错的知识和技能~