系列博客,原文在笔者所维护的github上:https://aka.ms/beginnerAI,
点击star加星不要吝啬,星越多笔者越努力。git
For things I don't know how to build, I don't understand.程序员
若是我不能亲手搭建起来一个东西,那么我就不能理解它。 -- 美国物理学家理查德·费曼github
在互联网发达的今天,不少知识均可以从网络上找到,可是网络上的博客、文章的质量良莠不齐,或者重点不明确,或者直接把别人的博客抄袭过来。这种情况使得广大的初学者们学习起来很困难,甚至误入歧途,增长了学习曲线的陡峭程度。固然也有不少博主很是很是负责任,文章质量很高,只是连续度不够,正看得过瘾的时候,没有后续章节了,没法造成知识体系。编程
初学者也能够选择看一些教材或者理论书籍,可是,一个鸡生蛋蛋生鸡的问题出现了:若是你不懂,那么看完了理论你仍是不会懂;若是你懂了,那么你就不必看理论。这也是不少教材或者理论书籍的缺憾。数组
笔者也看过吴恩达老师的课,理论知识讲得由浅入深,仍是很是清楚的,虽然代码示例基本没有,但仍然强烈建议你们去看。笔者的心得是:视频能够事先缓存在手机中,利用一些时间片断就能够学习了。缓存
社会上还有一些网课,在线讲解深度学习的知识,笔者也参加了几个团购,老师和助教通常都很负责任,最后能够回看录像,下载PPT课件。这些课程通常偏重于工程项目,讲解深度学习框架和工具的使用,即教你们如何使用工具建模、训练等等,也是颇有帮助的。但对于初学者来讲,理解一个新概念可能须要前面不少个已有知识点的支撑,门槛太高,一会儿就变得很沮丧。或者是知其然而不知其因此然,最后沦为调参工程师,职业发展受到了限制。网络
仍是应了那句古话:授人以鱼不如授人以渔。经历了以上那些学习经历,程序员出身的笔者迫切感受到应该有一种新的学习体验,在“作中学”,用写代码的方式把一些基础的理论复现一遍,能够深入理解其内涵,并能扩充其外延,使读者获得触类旁通的泛化能力。框架
笔者总结了自身的学习经历后,把深度学习的入门知识概括成了9个步骤,简称为9步学习法:机器学习
笔者看到过的不少书籍是直接从第7步起步的,其基本假设是读者已经掌握了前面的知识。可是对于从零开始的初学者们,这种假设并不正确。函数
在后面的讲解中,咱们通常会使用以下方式进行:
原理分析和可视化理解也是本书的一个特色,试图让神经网络是能够解释的,而不是盲目地使用。
还有一个很是重要的地方,咱们还有配套的Python代码,除了一些必要的科学计算库和绘图库,如NumPy和Matplotlib等,咱们没有使用任何已有的深度学习框架,而是带领你们从零开始搭建本身的知识体系,从简单到复杂,一步步理解深度学习中的众多知识点。
对于没有Python经验的朋友来讲,经过阅读示例代码,也能够起到帮助你们学习Python的做用,一箭双雕。随着问题的难度加深,代码也会增多,可是先后都有继承关系的,最后的代码会造成一个小的框架,笔者称之为Mini-Framework,能够用搭积木的方式调用其中的函数来搭建深度学习的组件。
这些代码都是由笔者亲自编写调试的,每章节均可以独立运行,获得相关章节内所描述的结果,包括打印输出和图形输出。
另外,为了便于理解,笔者绘制了大量的示意图,数量是同类书籍的10倍以上。一图顶万字,相信你们会经过这些示意图快速而深入地理解笔者想要分享的知识点,使你们可以从真正的“零”开始,对神经网络、深度学习有基本的了解,并能动手实践。
对于读者的要求:
能够帮助读者达到的水平:
符号 | 含义 |
---|---|
\(x\) | 训练用样本值 |
\(x_1\) | 第一个样本或样本的第一个特征值,在上下文中会有说明 |
\(x_{12},x_{1,2}\) | 第1个样本的第2个特征值 |
\(X\) | 训练用多样本矩阵 |
\(y\) | 训练用样本标签值 |
\(y_1\) | 第一个样本的标签值 |
\(Y\) | 训练用多样本标签矩阵 |
\(z\) | 线性运算的结果值 |
\(Z\) | 线性运算的结果矩阵 |
\(Z1\) | 第一层网络的线性运算结果矩阵 |
\(\sigma\) | 激活函数 |
\(a\) | 激活函数结果值 |
\(A\) | 激活函数结果矩阵 |
\(A1\) | 第一层网络的激活函数结果矩阵 |
\(w\) | 权重参数值 |
\(w_{12},w_{1,2}\) | 权重参数矩阵中的第1行第2列的权重值 |
\(w1_{12},w1_{1,2}\) | 第一层网络的权重参数矩阵中的第1行第2列的权重值 |
\(W\) | 权重参数矩阵 |
\(W1\) | 第一层网络的权重参数矩阵 |
\(b\) | 偏移参数值 |
\(b_1\) | 偏移参数矩阵中的第1个偏移值 |
\(b2_1\) | 第二层网络的偏移参数矩阵中的第1个偏移值 |
\(B\) | 偏移参数矩阵(向量) |
\(B1\) | 第一层网络的偏移参数矩阵(向量) |
\(X^T\) | X的转置矩阵 |
\(X^{-1}\) | X的逆矩阵 |
\(loss,loss(w,b)\) | 单样本偏差函数 |
\(J, J(w,b)\) | 多样本损失函数 |
神经网络由基本的神经元组成,图1-13就是一个神经元的数学/计算模型,便于咱们用程序来实现。
图1-13 神经元计算模型
(x1,x2,x3) 是外界输入信号,通常是一个训练数据样本的多个属性,好比,咱们要预测一套房子的价格,那么在房屋价格数据样本中,x1可能表明了面积,x2可能表明地理位置,x3可能朝向。另一个例子是,假设(x1,x2,x3)分别表明了(红,绿,蓝)三种颜色,而此神经元用于识别输入的信号是暖色仍是冷色。
(w1,w2,w3) 是每一个输入信号的权重值,以上面的 (x1,x2,x3) 的例子来讲,x1的权重多是0.92,x2的权重多是0.2,x3的权重多是0.03。固然权重值相加以后能够不是1。
还有个b是怎么来的?通常的书或者博客上会告诉你那是由于\(y=wx+b\),b是偏移值,使得直线可以沿Y轴上下移动。这是用结果来解释缘由,并不是b存在的真实缘由。从生物学上解释,在脑神经细胞中,必定是输入信号的电平/电流大于某个临界值时,神经元细胞才会处于兴奋状态,这个b实际就是那个临界值。亦即当:
\[w_1 \cdot x_1 + w_2 \cdot x_2 + w_3 \cdot x_3 >= t\]
时,该神经元细胞才会兴奋。咱们把t挪到等式左侧来,变成\((-t)\),而后把它写成b,变成了:
\[w_1 \cdot x_1 + w_2 \cdot x_2 + w_3 \cdot x_3 + b >= 0\]
因而b诞生了!
\[ \begin{aligned} Z &= w_1 \cdot x_1 + w_2 \cdot x_2 + w_3 \cdot x_3 + b \\ &= \sum_{i=1}^m(w_i \cdot x_i) + b \end{aligned} \]
在上面的例子中m=3。咱们把\(w_i \cdot x_i\)变成矩阵运算的话,就变成了:
\[Z = W \cdot X + b\]
求和以后,神经细胞已经处于兴奋状态了,已经决定要向下一个神经元传递信号了,可是要传递多强烈的信号,要由激活函数来肯定:
\[A=\sigma{(Z)}\]
若是激活函数是一个阶跃信号的话,会像继电器开合同样咔咔的开启和闭合,在生物体中是不可能有这种装置的,而是一个渐渐变化的过程。因此通常激活函数都是有一个渐变的过程,也就是说是个曲线,如图1-14所示。
图1-14 激活函数图像
至此,一个神经元的工做过程就在电光火石般的一瞬间结束了。
这是一个单层的神经网络,有m个输入 (这里m=3),有n个输出 (这里n=2)。在神经网络中,\(b\) 到每一个神经元的权值来表示实际的偏移值,亦即\((b_1,b_2)\),这样便于矩阵运算。也有些人把 \(b\) 写成\(x_0\),实际上是同一个效果,即把偏移值看作是神经元的一个输入。
图1-15 单层神经网络模型
从图1-15你们能够看到,同一个特征 \(x_1\),对于\(n一、n2\)来讲,权重是不相同的,由于\(n一、n2\)是两个神经元,它们完成不一样的任务(特征识别)。咱们假设\(x_1,x_2,x_3\)分别表明红绿蓝三种颜色,而 \(n1,n2\) 分别用于识别暖色和冷色,那么 \(x_1\) 到 \(n1\) 的权重,确定要大于 \(x_1\) 到 \(n2\) 的权重,由于\(x_1\)表明红色,是暖色。
而对于\(n1\)来讲,\(x_1,x_2,x_3\)输入的权重也是不相同的,由于它要对不一样特征有选择地接纳。如同上面的例子,\(n1\) 对于表明红色的 \(x_1\),确定是特别重视,权重值较高;而对于表明蓝色的 \(x_3\),尽可能把权重值下降,才能有正确的输出。
从真正的“零”开始学习神经网络时,我没有看到过任何一个流程图来说述训练过程,大神们写书或者博客时都忽略了这一点,图1-16是一个简单的流程图。
图1-16 神经网络训练流程图
假设咱们有表1-1所示的训练数据样本。
表1-1 训练样本示例
Id | x1 | x2 | x3 | Y |
---|---|---|---|---|
1 | 0.5 | 1.4 | 2.7 | 3 |
2 | 0.4 | 1.3 | 2.5 | 5 |
3 | 0.1 | 1.5 | 2.3 | 9 |
4 | 0.5 | 1.7 | 2.9 | 1 |
其中,x1,x2,x3是每个样本数据的三个特征值,Y是样本的真实结果值:
训练完成后,咱们会把这个神经网络中的结构和权重矩阵的值导出来,造成一个计算图(就是矩阵运算加上激活函数)模型,而后嵌入到任何能够识别/调用这个模型的应用程序中,根据输入的值进行运算,输出预测值。
图1-17是一个两层的神经网络,包含隐藏层和输出层,输入层不算作一层。
图1-17 神经网络中的各类符号约定
\[ z1_1 = x_1 \cdot w1_{1,1}+ x_2 \cdot w1_{2,1}+b1_1 \]
\[ z1_2 = x_1 \cdot w1_{1,2}+ x_2 \cdot w1_{2,2}+b1_2 \]
\[ z1_3 = x_1 \cdot w1_{1,3}+ x_2 \cdot w1_{2,3}+b1_3 \]
变成矩阵运算:
\[ z1_1= \begin{pmatrix} x_1 & x_2 \end{pmatrix} \begin{pmatrix} w1_{1,1} \\ w1_{2,1} \end{pmatrix} +b1_1 \]
\[ z1_2= \begin{pmatrix} x_1 & x_2 \end{pmatrix} \begin{pmatrix} w1_{1,2} \\ w1_{2,2} \end{pmatrix} +b1_2 \]
\[ z1_3= \begin{pmatrix} x_1 & x_2 \end{pmatrix} \begin{pmatrix} w1_{1,3} \\ w1_{2,3} \end{pmatrix} +b1_3 \]
再变成大矩阵:
\[ Z1 = \begin{pmatrix} x_1 & x_2 \end{pmatrix} \begin{pmatrix} w1_{1,1}&w1_{1,2}&w1_{1,3} \\ w1_{2,1}&w1_{2,2}&w1_{2,3} \\ \end{pmatrix} +\begin{pmatrix} b1_1 & b1_2 & b1_3 \end{pmatrix} \]
最后变成矩阵符号:
\[Z1 = X \cdot W1 + B1\]
而后是激活函数运算:
\[A1=a(Z1)\]
同理可得:
\[Z2 = A1 \cdot W2 + B2\]
注意:损失函数不是前向计算的一部分。
单层的神经网络可以模拟一条二维平面上的直线,从而能够完成线性分割任务。而理论证实,两层神经网络能够无限逼近任意连续函数。图1-18所示就是一个两层神经网络拟合复杂曲线的实例。
图1-18 回归/拟合示意图
所谓回归或者拟合,其实就是给出x值输出y值的过程,而且让y值与样本数据造成的曲线的距离尽可能小,能够理解为是对样本数据的一种骨架式的抽象。
以图1-18为例,蓝色的点是样本点,从中能够大体地看出一个轮廓或骨架,而红色的点所连成的线就是神经网络的学习结果,它能够“穿过”样本点群造成中心线,尽可能让全部的样本点到中心线的距离的和最近。
如图1-19,二维平面中有两类点,红色的和蓝色的,用一条直线确定不能把二者分开了。
图1-19 分类示意图
咱们使用一个两层的神经网络能够获得一个很是近似的结果,使得分类偏差在满意的范围以内。图1-19中那条淡蓝色的曲线,原本并不存在,是经过神经网络训练出来的分界线,能够比较完美地把两类样本分开,因此分类能够理解为是对两类或多类样本数据的边界的抽象。
图1-18和图1-19的曲线形态其实是一个真实的函数在[0,1]区间内的形状,其原型是:
\[y=0.4x^2 + 0.3xsin(15x) + 0.01cos(50x)-0.3\]
这么复杂的函数,一个两层的神经网络是如何作到的呢?其实从输入层到隐藏层的矩阵计算,就是对输入数据进行了空间变换,使其能够被线性可分,而后在输出层画出一个分界线。而训练的过程,就是肯定那个空间变换矩阵的过程。所以,多层神经网络的本质就是对复杂函数的拟合。咱们能够在后面的试验中来学习如何拟合上述的复杂函数的。
神经网络的训练结果,是一大堆的权重组成的数组(近似解),并不能获得上面那种精确的数学表达式(数学解析解)。
人体骨关节是动物界里最复杂的生理结构,一共有8个重要的大关节:肩关节、
肘关节、腕关节、髋关节、膝关节、踝关节、颈关节、腰关节。
人的臂骨,腿骨等,都是一根直线,人体直立时,也是一根直线。可是人在骨关节和肌肉组织的配合下,能够作不少复杂的动做,缘由就是关节自己不是线性结构,而是一个在有限范围内能够任意活动的结构,有必定的柔韧性。
好比肘关节,能够完成小臂在一个二维平面上的活动。加上肩关节,就能够完成胳膊在三维空间的活动。再加上其它关节,就能够扩展胳膊活动的三维空间的范围。
用表1-2来对比人体运动组织和神经网络组织。
表1-2 人体运动组织和神经网络组织的对比
人体运动组织 | 神经网络组织 |
---|---|
支撑骨骼 | 网络层次 |
关节 | 激活函数 |
肌肉韧带 | 权重参数 |
学习各类运动的动做 | 前向+反向训练过程 |
激活函数就至关于关节。
看如下的例子:
\[Z1=X \cdot W1 + B1\]
\[Z2 = Z1 \cdot W2 + B2\]
\[Z3 = Z2 \cdot W3 + B3\]
展开:
\[ \begin{aligned} Z3&=Z2 \cdot W3 + B3 \\ &=(Z1 \cdot W2 + B2) \cdot W3 + B3 \\ &=((X \cdot W1 + B1) \cdot W2 + B2) \cdot W3 + B3 \\ &=X \cdot (W1\cdot W2 \cdot W3) + (B1 \cdot W2 \cdot W3+B2 \cdot W2+B3) \\ &=X \cdot W+B \end{aligned} \]
\(Z1,Z2,Z3\)分别表明三层神经网络的计算结果。最后能够看到,无论有多少层,总能够归结到\(XW+B\)的形式,这和单层神经网络没有区别。
若是咱们不运用激活函数的话,则输出信号将仅仅是一个简单的线性函数。线性函数一个一级多项式。线性方程是很容易解决的,可是它们的复杂性有限,而且从数据中学习复杂函数映射的能力更小。一个没有激活函数的神经网络将只不过是一个线性回归模型罢了,不能解决现实世界中的大多数非线性问题。
没有激活函数,咱们的神经网络将没法学习和模拟其余复杂类型的数据,例如图像、视频、音频、语音等。这就是为何咱们要使用人工神经网络技术,诸如深度学习,来理解一些复杂的事情,一些相互之间具备不少隐藏层的非线性问题。
图1-20 从简单到复杂的拟合
图1-20展现了几种拟合方式,最左侧的是线性拟合,中间的是分段线性拟合,右侧的是曲线拟合,只有当使用激活函数时,才能作到完美的曲线拟合。