| |
|
开发:
C++知识库
Java知识库
JavaScript
Python
PHP知识库
人工智能
区块链
大数据
移动开发
嵌入式
开发工具
数据结构与算法
开发测试
游戏开发
网络协议
系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程 数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁 |
-> 人工智能 -> 机器学习笔记-神经网络的原理、数学、代码与手写数字识别 -> 正文阅读 |
|
[人工智能]机器学习笔记-神经网络的原理、数学、代码与手写数字识别 |
机器学习笔记-神经网络 文章目录前言??对于特征数目较少的问题,我们大多可以通过线性回归模型、逻辑回归模型等传统机器学习算法解决,然而面对图片这一类的每一像素点为特征的复杂问题,逻辑回归已经不能够准确的解决,因为我们就算压缩图片为20*20,那么也将有400个特征,对于这400个特征的多项式组合数我们不可能人为去选择。由此有了神经网络,通过网络内部对特征复杂的组合最终得到强大的学习模型,只不过我们对其内部的运算理解不会像线性回归和逻辑回归那样简单透彻。理解了神经网络的基本原理,能够写出模型代码以解决实际问题,那么我们就真的走进了近年火热的深度学习的大门! 一、神经网络的灵感??对于人类而言,包括肌肉控制、情感产生、学习思考都是由大脑完成,随着生物学的发展,科学家发现大脑又是由海量的神经元相互连接相互作用实现对身体的控制。如果将大脑看作一台电脑,那么神经元就是最小单元,它拥有接收信息、简单计算、传递信息等功能,只不过,人脑神经元是通过电信号与化学物质实现。我们想,是不是可以通过现代手段去模拟这样的人脑神经元呢?现在我们去模拟出来一个人工神经元。
二、基本原理1.神经网络最小单元——神经元??神经网络中的单个神经元,由其他神经元的输出,经过权重计算后作为当前神经元的输入,这里我们加入偏置项
x
0
=
1
x_0=1
x0?=1(解释放在第三节理论推导)作为额外输入,当前神经元将输入根据权重加和得到加权输入
z
=
θ
0
x
0
+
θ
1
x
1
+
θ
2
x
2
+
θ
3
x
3
z=\theta_0x_0+\theta_1x_1+\theta_2x_2+\theta_3x_3
z=θ0?x0?+θ1?x1?+θ2?x2?+θ3?x3?,再将加权输入
z
z
z作为激活函数
g
(
z
)
g(z)
g(z)的自变量,计算得到当前神经元的输出
y
=
g
(
z
)
y=g(z)
y=g(z)。 2.神经网络层结构
3.正向传播输入层 -> 隐藏层 -> 隐藏层 -> 输出层 4.反向传播输入层 <- 隐藏层 <- 隐藏层 <- 输出层 5.梯度下降??大多数机器学习问题都可以用梯度下降来使函数收敛(或是局部收敛或是全局收敛),梯度下降的公式都一样, θ j = θ j ? α ? J ( θ ) ? θ j \theta_j=\theta_j-\alpha\frac{\partial J(\theta)}{\partial\theta_j} θj?=θj??α?θj??J(θ)?。不熟悉的朋友可以参考线性回归和逻辑回归,本质上都一样的,只不过在神经网络中,因为具有多层多个参数矩阵,偏导项涉及到链式求导法则。 三、数学理论推导
注:为了方便反向传播的推导计算,我们将特征输入与偏置单元分开,不将而这放入同一矩阵。 1.正向传播公式推导??我们以上图中的Layer2为例,推导向量化数学公式。对于当前层Layer2,有输入
x
=
(
x
1
x
2
)
x=\left(\begin{matrix}x_1\\x_2\\\end{matrix}\right)
x=(x1?x2??),传递给当前层的特征权重矩阵
W
=
(
θ
11
θ
12
θ
21
θ
22
θ
31
θ
32
)
W=\left(\begin{matrix}\theta_{11}&\theta_{12}\\\theta_{21}&\theta_{22}\\\theta_{31}&\theta_{32}\\\end{matrix}\right)
W=???θ11?θ21?θ31??θ12?θ22?θ32?????,偏置项系数
b
=
(
θ
10
θ
20
θ
30
)
b=\left(\begin{matrix}\theta_{10}\\\theta_{20}\\\theta_{30}\\\end{matrix}\right)
b=???θ10?θ20?θ30?????,参数矩阵
θ
=
(
θ
10
θ
11
θ
12
θ
20
θ
21
θ
22
θ
30
θ
31
θ
32
)
\theta=\left(\begin{matrix}\theta_{10}&\theta_{11}&\theta_{12}\\\theta_{20}&\theta_{21}&\theta_{22}\\\theta_{30}&\theta_{31}&\theta_{32}\\\end{matrix}\right)
θ=???θ10?θ20?θ30??θ11?θ21?θ31??θ12?θ22?θ32?????。 对当前层所有神经元,向量化处理后,其加权输入列向量为: 加权输入 z 3 × 1 z_{3\times1} z3×1?经过 s i g m o i d sigmoid sigmoid函数对向量整体计算后得到当前层输出列向量: ??至此,我们推导出经过正向传播获得的各层输入输出数学公式。 2.反向传播公式推导??反向传播计算梯度下降算法
θ
j
i
=
θ
j
i
?
α
?
J
(
θ
)
?
θ
j
i
\theta_{ji}=\theta_{ji}-\alpha\frac{\partial J\left(\theta\right)}{\partial\theta_{ji}}
θji?=θji??α?θji??J(θ)?中的偏导项也就是梯度
g
r
a
d
i
e
n
t
=
?
J
(
θ
)
?
θ
j
i
gradient=\frac{\partial J\left(\theta\right)}{\partial\theta_{ji}}
gradient=?θji??J(θ)?。这里说明一下,
θ
j
i
\theta_{ji}
θji?指的是上一层第
i
i
i个神经元传递给当前层第
j
j
j个神经元的权重,可以对照上述矩阵。损失函数
J
(
θ
)
J(\theta)
J(θ)有很多种,本文主要使用平方损失函数与交叉熵损失函数。这里我们的损失函数是单样本损失函数,而不是线性回归或逻辑回归中的计算整体平均代价,因为对整体计算量太大。损失函数如下: 平方损失函数为: 交叉熵损失函数为: ??因为神经网络是层层排列的,我们希望找到某种递推关系,这样反向传播计算效率会更高,所以我们在求
g
r
a
d
i
e
n
t
=
?
c
o
s
t
(
l
a
b
e
l
,
y
)
?
θ
j
i
gradient=\frac{\partial cost(label,y)}{\partial\theta_{ji}}
gradient=?θji??cost(label,y)?时用到链式求导法则,推导需要一定的技巧。我们想要通过链式求导得到一个简单项,尽量降低复杂度。我们看正向传播推导中的
z
j
=
θ
j
0
x
0
+
θ
j
1
x
1
+
θ
j
2
x
2
z_j=\theta_{j0}x_0+\theta_{j1}x_1+\theta_{j2}x_2
zj?=θj0?x0?+θj1?x1?+θj2?x2?,可以发现
z
j
z_j
zj?关于
θ
j
i
\theta_{ji}
θji?的偏导
?
z
j
?
θ
j
i
\frac{\partial z_j}{\partial\theta_{ji}}
?θji??zj??就是
x
i
x_i
xi?,这一项是不是很简单很容易表示,所以我们对
g
r
a
d
i
e
n
t
=
?
c
o
s
t
(
l
a
b
e
l
,
y
)
?
θ
j
i
gradient=\frac{\partial cost\left(label,y\right)}{\partial\theta_{ji}}
gradient=?θji??cost(label,y)?链式求导有: 向量化表示当前层参数 θ \theta θ梯度有: 如果将参数分为权重参数 W W W和偏置项参数 b b b有: ??为了更方便的表示,我们设 δ j = ? c o s t ( l a b e l , y ) ? z j \delta_j=\frac{\partial cost\left(label,y\right)}{\partial z_j} δj?=?zj??cost(label,y)?,表示当前层第 j j j个神经元的误差项。输出层与隐藏层的误差项不同,下面我们来分别推导。 1) 当前层为输出层??当当前层为输出层,有误差项
δ
j
(
L
)
=
?
c
o
s
t
(
l
a
b
e
l
,
y
)
?
z
j
\delta_j^{(L)}=\frac{\partial cost\left(label,y\right)}{\partial z_j}
δj(L)?=?zj??cost(label,y)?,此时,求偏导使得
c
o
s
t
cost
cost函数中的无关项被消除,仅剩
c
o
s
t
j
(
l
a
b
e
l
,
y
)
{cost}_j\left(label,y\right)
costj?(label,y),
L
L
L代表最后一层第
L
L
L层。 (9)式就是输出层通用的数学表达式,我们再利用向量整体运算将其转换为对当前层的向量表达式,其中, δ \delta δ, c o s t cost cost, y y y, g ( z ) g(z) g(z), z z z均是维度相同的列向量。: 在得到了输出层的通式之后,我们就可以根据自己选择的激活函数和损失函数,计算自己想要的输出层误差项公式。这里我们以 s i g m o i d sigmoid sigmoid激活函数加交叉熵损失函数为例,推导输出层误差项公式。 对于(10)式中的第一项有: 对于(10)式中的第二项有: 最终,将式(11)式(12)代入进式(10)即得到最终输出层误差项公式。 2) 当前层为隐藏层??当当前层为隐藏层时,有误差项
δ
j
(
l
)
=
?
c
o
s
t
(
l
a
b
e
l
,
y
)
?
z
j
\delta_j^{(l)}=\frac{\partial cost\left(label,y\right)}{\partial z_j}
δj(l)?=?zj??cost(label,y)?,此时
c
o
s
t
cost
cost为输出层所有神经元损失之和,
l
l
l代表小于
L
L
L的第
l
l
l层隐藏层。 对于上式第一项有: 对于上式第二项有: 代入有: 转换为当前层向量表达式为: 关于矩阵点乘的操作大家可以自己把参数矩阵和列向量画出来,更加清晰直观,也或者利用点乘条件判断。最后,我们将误差项代入上面的(6)(7)(8)式即可得到梯度表达式。 3.梯度下降公式推导基本公式 将矩阵看作整体进行向量化运算有: 将权重与偏置项分开有: ??这里我再说一下为什么要将权重与偏置项分开,而不是像之前线性回归和逻辑回归一样合并在一起。对于一般的对称结构来说,二者合并更加简洁,但是我们仔细观察神经网络的结构发现,正向传播与反向传播是不完全对称的,偏置项只做输入不做输出,偏置项不会影响反向传播的误差项,在(13)式就能看出,所以放在一起会增加一些步骤,本文的代码将权重 W W W与偏置项 b b b分开,大家也可以合并来试一试,应该会多一步对矩阵的选择(从 θ \theta θ中选择出 W W W)。 4.公式整理
1) 正向传播
z
3
×
1
=
W
?
x
+
b
?????
(
1
)
z_{3\times1}=W\cdot x+b\ \ \ \ \ (1)
z3×1?=W?x+b?????(1) 2) 反向传播c o s t ( l a b e l , y ) = ∑ k = 1 o u t p u t _ s i z e [ ? l a b e l k ln ? y k ? ( 1 ? l a b e l k ) ln ? ( 1 ? y k ) ] ????? ( 4 ) cost(label,y)=\sum_{k=1}^{output\_size}[-{label_k}\ln{y_k}-(1-{label_k})\ln{(1-y_k)}]\ \ \ \ \ (4) cost(label,y)=k=1∑output_size?[?labelk?lnyk??(1?labelk?)ln(1?yk?)]?????(4) ? c o s t ( l a b e l , y ) ? W ( l ) = ? c o s t ( l a b e l , y ) ? z ( l ) ? x ( l ) T = δ ( l ) ? x ( l ) T ????? ( 7 ) \frac{\partial cost\left(label,y\right)}{\partial W^{(l)}}=\frac{\partial cost\left(label,y\right)}{\partial z^{(l)}}{\cdot}x^{{(l)}^T}=\delta^{(l)}{\cdot} x^{{(l)}^T}\ \ \ \ \ (7) ?W(l)?cost(label,y)?=?z(l)?cost(label,y)??x(l)T=δ(l)?x(l)T?????(7) ? c o s t ( l a b e l , y ) ? b ( l ) = ? c o s t ( l a b e l , y ) ? z ( l ) = δ ( l ) ????????????????? ( 8 ) \frac{\partial cost\left(label,y\right)}{\partial b^{(l)}}=\frac{\partial cost\left(label,y\right)}{\partial z^{(l)}}=\delta^{(l)}\ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ (8) ?b(l)?cost(label,y)?=?z(l)?cost(label,y)?=δ(l)?????????????????(8) 输出层误差项: ? c o s t ( l a b e l , y ) ? y = ? l a b e l y + 1 ? l a b e l 1 ? y ?? o r ?? ? c o s t ( l a b e l , y ) ? y = ? ( l a b e l ? y ) ????? ( 11 ) \frac{\partial cost\left(label,y\right)}{\partial y}=-\frac{label}{y}+\frac{1-label}{1-y}\ \ or\ \ \frac{\partial cost\left(label,y\right)}{\partial y}=-(label-y)\ \ \ \ \ (11) ?y?cost(label,y)?=?ylabel?+1?y1?label???or???y?cost(label,y)?=?(label?y)?????(11) ? g ( z ( L ) ) ? z ( L ) = [ 1 ? g ( z ( L ) ) ] × g ( z ( L ) ) = ( 1 ? y ) × y ????? ( 12 ) \frac{\partial g(z^{(L)})}{\partial z^{(L)}}=[1-g(z^{(L)})]×g(z^{(L)})=(1-y)×y \ \ \ \ \ (12) ?z(L)?g(z(L))?=[1?g(z(L))]×g(z(L))=(1?y)×y?????(12) 隐藏层误差项: 3) 梯度下降W ( l ) = W ( l ) ? α δ ( l ) ? x ( l ) T ????? ( 15 ) W^{(l)}=W^{(l)}-\alpha\delta^{\left(l\right)}\cdot x^{\left(l\right)^T}\ \ \ \ \ (15) W(l)=W(l)?αδ(l)?x(l)T?????(15) b ( l ) = b ( l ) ? α δ ( l ) ????? ( 16 ) b^{(l)}=b^{(l)}-\alpha\delta^{\left(l\right)}\ \ \ \ \ (16) b(l)=b(l)?αδ(l)?????(16) ??在以上公式中, ? c o s t ( l a b e l , y ) ? y \frac{\partial cost\left(label,y\right)}{\partial y} ?y?cost(label,y)?和 ? g ( z ( L ) ) ? z ( L ) \frac{\partial g(z^{(L)})}{\partial z^{(L)}} ?z(L)?g(z(L))?根据损失函数和激活函数的选择而定。 四、模型建立1.全连接层类??我们这里最小结构为单个样本的神经网络层,所以我们首先要定义全连接层实现类,选择激活函数与损失函数,在类中实现单样本当前层的正向传播、反向传播、梯度下降等基本操作。代码中有一个地方需要注意,反向传播的误差项分输出层和隐藏层,输出层的误差项我们单独计算,而隐藏层的误差项就根据后一层的误差项递推计算。所以我们的
F
u
l
l
C
o
n
n
e
c
t
e
d
L
a
y
e
r
.
d
e
l
t
a
FullConnectedLayer.delta
FullConnectedLayer.delta计算的是当前层的上一层的误差项。代码如下:
神经网络全连接层类:
2.神经网络类??然后,我们定义神经网络模型类,我们在这个类里面调用全连接层类,实现单样本所有层的正向传播、反向传播、梯度下降操作,并以此训练模型、预测、计算损失值、根据测试集计算准确率。除此之外,为了避免我们写的代码存在不易察觉的bug,我们内置梯度检查函数,梯度检查的基本原理就是根据 J ? θ J-\theta J?θ函数各点的近似斜率与数学推导计算出的斜率(梯度)作比较,如果二者相差极小则认为模型计算梯度正确,否则就应该检查代码是否有bug。代码如下:
??最后,因为训练模型的时间很长,我们不想每次都重新训练,所以我们在神经网络模型中内置save_model与load_model函数,将训练好的模型参数保存在txt文档中,下次可以直接加载模型。 五、应用:手写数字识别??我使用了两个数据集,第一个是吴恩达老师的.mat数据集,大小为5000张20*20的手写数字图片,已进行列向量展开与归一化。第二个是MNIST数据集,大小为60000张28*28的手写数字图片,未进行处理。数据集读取方法我放在了/dataset/readme.txt文档中,最终模型准确率能达到约98.5%。 这个是吴恩达机器学习课后作业手写数字图片: 这个是MNIST手写数字图片: 以下是数据预处理代码:
??关于本项目的全部代码和数据集,我会放在Gitee上,https://gitee.com/xingheguntangxi/neural_network.git,大家自行下载。另外,我自己也写了十张手写数字0-9,都能识别出来,不过9这个数字似乎正确率不高,大家感兴趣可以自己试试呀^_^
|
|
|
上一篇文章 下一篇文章 查看所有文章 |
|
开发:
C++知识库
Java知识库
JavaScript
Python
PHP知识库
人工智能
区块链
大数据
移动开发
嵌入式
开发工具
数据结构与算法
开发测试
游戏开发
网络协议
系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程 数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁 |
360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 | -2024/11/27 16:48:51- |
|
网站联系: qq:121756557 email:121756557@qq.com IT数码 |