乐趣区

关于图像识别:深度学习与CV教程4-神经网络与反向传播

  • 作者:韩信子 @ShowMeAI
  • 教程地址:http://www.showmeai.tech/tutorials/37
  • 本文地址:http://www.showmeai.tech/article-detail/263
  • 申明:版权所有,转载请分割平台与作者并注明出处
  • 珍藏 ShowMeAI 查看更多精彩内容

本系列为 斯坦福 CS231n《深度学习与计算机视觉 (Deep Learning for Computer Vision)》的全套学习笔记,对应的课程视频能够在 这里 查看。更多材料获取形式见文末。


引言

在上一篇 深度学习与 CV 教程(3) | 损失函数与最优化 内容中,咱们给大家介绍了线性模型的损失函数构建与梯度降落等优化算法,【本篇内容】ShowMeAI 给大家切入到神经网络,解说神经网络计算图与反向流传以及神经网络构造等相干常识。

本篇重点

  • 神经网络计算图
  • 反向流传
  • 神经网络构造

1. 反向流传算法

神经网络的训练,利用到的梯度降落等办法,须要计算损失函数的梯度,而其中最外围的常识之一是 反向流传 ,它是利用数学中 链式法则 递归求解简单函数梯度的办法。而像 tensorflow、pytorch 等支流 AI 工具库最外围的智能之处也是可能主动微分,在本节内容中 ShowMeAI 就联合 cs231n 的第 4 讲内容开展解说一下神经网络的计算图和反向流传。

对于神经网络反向流传的解释也能够参考 ShowMeAI 的 深度学习教程 | 吴恩达专项课程 · 全套笔记解读 中的文章 神经网络根底 浅层神经网络 深层神经网络 里对于不同深度的网络前向计算和反向流传的解说

1.1 标量模式反向流传

1) 引例

咱们来看一个简略的例子,函数为 \(f(x,y,z) = (x + y) z\)。初值 \(x = -2\),\(y = 5\),\(z = -4\)。这是一个能够间接微分的表达式,然而咱们应用一种有助于直观了解反向流传的办法来辅助了解。

下图是整个计算的线路图,绿字局部是函数值,红字是梯度。(梯度是一个向量,但通常将对 \(x\) 的偏导数称为 \(x\) 上的梯度。)

上述公式能够分为 2 局部,\(q = x + y\) 和 \(f = q z\)。它们都很简略能够间接写出梯度表达式:

  • \(f\) 是 \(q\) 和 \(z\) 的乘积,所以 \(\frac{\partial f}{\partial q} = z=-4\),\(\frac{\partial f}{\partial z} = q=3\)
  • \(q\) 是 \(x\) 和 \(y\) 相加,所以 \(\frac{\partial q}{\partial x} = 1\),\(\frac{\partial q}{\partial y} = 1\)

咱们对 \(q\) 上的梯度不关怀(\(\frac{\partial f}{\partial q}\) 没有用途)。咱们关怀 \(f\) 对于 \(x,y,z\) 的梯度。链式法则 通知咱们能够用「乘法」将这些梯度表达式链接起来,比方

$$
\frac{\partial f}{\partial x} = \frac{\partial f}{\partial q} \frac{\partial q}{\partial x} =-4
$$

  • 同理,\(\frac{\partial f}{\partial y} =-4\),还有一点是 \(\frac{\partial f}{\partial f}=1\)

前向流传 从输出计算到输入(绿色),反向流传 从尾部开始,依据 链式法则 递归地向前计算梯度(显示为红色),始终到网络的输出端。能够认为,梯度是从计算链路中回流

上述计算的参考 python 实现代码如下:

# 设置输出值
x = -2; y = 5; z = -4

# 进行前向流传
q = x + y # q 是 3
f = q * z # f 是 -12

# 进行反向流传:
# 首先回传到 f = q * z
dfdz = q # df/dz = q, 所以对于 z 的梯度是 3
dfdq = z # df/dq = z, 所以对于 q 的梯度是 -4
# 当初回传到 q = x + y
dfdx = 1.0 * dfdq # dq/dx = 1. 这里的乘法是因为链式法则。所以 df/dx 是 -4
dfdy = 1.0 * dfdq # dq/dy = 1. 所以 df/dy 是 -4

'''个别能够省略 df'''

2) 直观了解反向流传

反向流传是一个柔美的部分过程。

以下图为例,在整个计算线路图中,会给每个门单元(也就是 \(f\) 结点)一些输出值 \(x\) , (y\) 并立刻计算这个门单元的输入值 \(z\),和以后节点输入值对于输出值的 部分梯度(local gradient)\(\frac{\partial z}{\partial x}\) 和 \(\frac{\partial z}{\partial y}\)。

门单元的这两个计算在前向流传中是齐全独立的,它无需晓得计算线路中的其余单元的计算细节。但在反向流传的过程中,门单元将取得整个网络的最终输入值在本人的输入值上的梯度 \(\frac{\partial L}{\partial z}\)。

依据链式法则,整个网络的输入对该门单元的每个输出值的梯度,要用 回传梯度乘以它的输入对输出的部分梯度,失去 \(\frac{\partial L}{\partial x}\) 和 \(\frac{\partial L}{\partial y}\)。这两个值又能够作为后面门单元的回传梯度。

因而,反向流传能够看做是门单元之间在通过梯度信号互相通信,只有让它们的输出沿着梯度方向变动,无论它们本人的输入值在何种水平回升或升高,都是为了让整个网络的输入值更高。

比方引例中 \(x,y\) 梯度都是 \(-4\),所以让 \(x,y\) 减小后,\(q\) 的值尽管也会减小,但最终的输入值 \(f\) 会增大(当然损失函数要的是最小)。

3) 加法门、乘法门和 max 门

引例中用到了两种门单元:加法和乘法。

  • 加法求偏导:\(f(x,y) = x + y \rightarrow \frac{\partial f}{\partial x} = 1 \frac{\partial f}{\partial y} = 1\)
  • 乘法求偏导:\(f(x,y) = x y \rightarrow \frac{\partial f}{\partial x} = y \frac{\partial f}{\partial y} = x\)

除此之外,罕用的操作还包含取最大值:

$$
\begin{aligned}
f(x,y) &= \max(x, y) \\
\rightarrow \frac{\partial f}{\partial x} &= \mathbb{1}(x \ge y)\\
\frac{\partial f}{\partial y} &\mathbb{1}(y \ge x)
\end{aligned}
$$

上式含意为:若该变量比另一个变量大,那么梯度是 \(1\),反之为 \(0\)。

  • 加法门单元是梯度分配器,输出的梯度都等于输入的梯度,这一行为与输出值在前向流传时的值无关;
  • 乘法门单元是梯度转换器,输出的梯度等于输入梯度乘以另一个输出的值,或者乘以倍数 \(a\)(\(ax\) 的模式乘法门单元);max 门单元是梯度路由器,输出值大的梯度等于输入梯度,小的为 \(0\)。

乘法门单元的部分梯度就是输出值,然而是相互交换之后的,而后依据链式法则乘以输入值的梯度。基于此,如果乘法门单元的其中一个输出十分小,而另一个输出十分大,那么乘法门会把大的梯度调配给小的输出,把小的梯度调配给大的输出。

以咱们之前讲到的线性分类器为例,权重和输出进行点积 \(w^Tx_i\),这阐明输出数据的大小对于权重梯度的大小有影响。具体的,如在计算过程中对所有输出数据样本 \(x_i\) 乘以 100,那么权重的梯度将会增大 100 倍,这样就必须升高学习率来补救。

也阐明了数据预处理有很重要的作用,它即便只是有渺小变动,也会产生微小影响

对于梯度在计算线路中是如何流动的有一个直观的了解,能够帮忙调试神经网络。

4) 简单示例

咱们来看一个简单一点的例子:

$$
f(w,x) = \frac{1}{1+e^{-(w_0x_0 + w_1x_1 + w_2)}}
$$

这个表达式须要应用新的门单元:

$$
\begin{aligned}
f(x) &= \frac{1}{x} \\
\rightarrow \frac{df}{dx} &=- \frac{1}{x^2}\ f_c(x) = c + x \\
\rightarrow \frac{df}{dx} &= 1 \ f(x) = e^x \\
\rightarrow \frac{df}{dx} &= e^x \ f_a(x) = ax \\
\rightarrow \frac{df}{dx} &= a
\end{aligned}
$$

计算过程如下:

  • 对于 \(1/x\) 门单元,回传梯度是 \(1\),部分梯度是 \(-1/x^2=-1/1.37^2=-0.53\),所以输出梯度为 \(1 \times -0.53 = -0.53\);\(+1\) 门单元不扭转梯度还是 \(-0.53\)
  • exp 门单元部分梯度是 \(e^x=e^{-1}\),而后乘回传梯度 \(-0.53\) 后果约为 \(-0.2\)
  • 乘 \(-1\) 门单元会将梯度加负号变为 \(0.2\)
  • 加法门单元会调配梯度,所以从上到下三个加法分支都是 \(0.2\)
  • 最初两个乘法单元会转换梯度,把回传梯度乘另一个输出值作为本人的梯度,失去 \(-0.2\)、\(0.4\)、\(-0.4\)、\(-0.6\)

5) Sigmoid 门单元

咱们能够将任何可微分的函数视作「门」。能够将多个门组合成一个门,也能够依据须要将一个函数拆成多个门。咱们察看能够发现,最右侧四个门单元能够合成一个门单元,\(\sigma(x) = \frac{1}{1+e^{-x}}\),这个函数称为 sigmoid 函数

sigmoid 函数能够微分:

$$
\frac{d\sigma(x)}{dx} = \frac{e^{-x}}{(1+e^{-x})^2} = \left(\frac{1 + e^{-x} – 1}{1 + e^{-x}} \right) \left(\frac{1}{1+e^{-x}} \right) = \left(1 – \sigma(x) \right) \sigma(x)
$$

所以下面的例子中曾经计算出 \(\sigma(x)=0.73\),能够间接计算出乘 \(-1\) 门单元输出值的梯度为:\(1 \ast (1-0.73) \ast0.73~=0.2\),计算简化很多。

下面这个例子的反向流传的参考 python 实现代码如下:

# 假如一些随机数据和权重
w = [2,-3,-3] 
x = [-1, -2]

# 前向流传,计算输入值
dot = w[0]*x[0] + w[1]*x[1] + w[2]
f = 1.0 / (1 + math.exp(-dot)) # sigmoid 函数

# 反向流传,计算梯度
ddot = (1 - f) * f # 点积变量的梯度, 应用 sigmoid 函数求导
dx = [w[0] * ddot, w[1] * ddot] # 回传到 x
dw = [x[0] * ddot, x[1] * ddot, 1.0 * ddot] # 回传到 w
# 最终失去输出的梯度

在实际操作中,有时候咱们会把前向流传分成不同的阶段,这样能够让反向流传过程更加简洁。比方创立一个两头变量 \(dot\),寄存 \(w\) 和 \(x\) 的点乘后果。在反向流传时,能够很快计算出装着 \(w\) 和 \(x\) 等的梯度的对应的变量(比方 \(ddot\),\(dx\) 和 \(dw\))。

本篇内容列了很多例子,咱们心愿通过这些例子解说「前向流传」与「反向流传」过程,哪些函数能够被组合成门,如何简化,这样他们能够“链”在一起,让代码量更少,效率更高。

6) 分段计算示例

$$
f(x,y) = \frac{x + \sigma(y)}{\sigma(x) + (x+y)^2}
$$

这个表达式只是为了实际反向流传,如果间接对 \(x,y\) 求导,运算量将会很大。上面先代码实现前向流传:

x = 3  # 例子数值
y = -4

# 前向流传
sigy = 1.0 / (1 + math.exp(-y)) # 分子中的 sigmoid         #(1)
num = x + sigy # 分子                                    #(2)
sigx = 1.0 / (1 + math.exp(-x)) # 分母中的 sigmoid         #(3)
xpy = x + y                                              #(4)
xpysqr = xpy**2                                          #(5)
den = sigx + xpysqr # 分母                                #(6)
invden = 1.0 / den                                       #(7)
f = num * invden     

代码创立了多个两头变量,每个都是比较简单的表达式,它们计算部分梯度的办法是已知的。能够给咱们计算反向流传带来很多便当:

  • 咱们对前向流传时产生的每个变量 \((sigy, num, sigx, xpy, xpysqr, den, invden)\) 进行回传。
  • 咱们用同样数量的变量(以 d 结尾),存储对应变量的梯度。
  • 留神:反向流传的每一小块中都将蕴含了表达式的部分梯度,而后依据应用链式法则乘以上游梯度。对于每行代码,咱们将指明其对应的是前向流传的哪局部,序号对应。
# 回传 f = num * invden
dnum = invden # 分子的梯度                                         #(8)
dinvden = num # 分母的梯度                                         #(8)
# 回传 invden = 1.0 / den 
dden = (-1.0 / (den**2)) * dinvden                                #(7)
# 回传 den = sigx + xpysqr
dsigx = (1) * dden                                                #(6)
dxpysqr = (1) * dden                                              #(6)
# 回传 xpysqr = xpy**2
dxpy = (2 * xpy) * dxpysqr                                        #(5)
# 回传 xpy = x + y
dx = (1) * dxpy                                                   #(4)
dy = (1) * dxpy                                                   #(4)
# 回传 sigx = 1.0 / (1 + math.exp(-x))
dx += ((1 - sigx) * sigx) * dsigx # 留神这里用的是 +=,上面有解释    #(3)
# 回传 num = x + sigy
dx += (1) * dnum                                                  #(2)
dsigy = (1) * dnum                                                #(2)
# 回传 sigy = 1.0 / (1 + math.exp(-y))
dy += ((1 - sigy) * sigy) * dsigy   

补充解释

①对前向流传变量进行缓存

  • 在计算反向流传时,前向流传过程中失去的一些两头变量十分有用。
  • 实现过程中,在代码里对这些两头变量进行缓存,这样在反向流传的时候也能用上它们。

②在不同分支的梯度要相加

  • 如果变量 \(x,y\) 在前向流传的表达式中呈现屡次,那么进行反向流传的时候就要十分小心,要应用 \(+=\) 而不是 \(=\) 来累计这些变量的梯度。
  • 依据微积分中的多元链式法则,如果变量在线路中走向不同的分支,那么梯度在回传的时候,应该累加。即:

$$
\frac{\partial f}{\partial x} =\sum_{q_i}\frac{\partial f}{\partial q_i}\frac{\partial q_i}{\partial x}
$$

7) 理论利用

如果有一个计算图,曾经拆分成门单元的模式,那么主类代码构造如下:

class ComputationalGraph(object):
    # ...
    def forward(self, inputs):
        # 把 inputs 传递给输出门单元
        # 前向流传计算图
        # 遍历所有从后向前按顺序排列的门单元
        for gate in self.graph.nodes_topologically_sorted(): 
            gate.forward()  # 每个门单元都有一个前向流传函数
        return loss  # 最终输入损失

    def backward(self):
        # 反向遍历门单元
        for gate in reversed(self.graph.nodes_topologically_sorted()): 
            gate.backward()  # 反向流传函数利用链式法则
        return inputs_gradients  # 输入梯度
        return inputs_gradients  # 输入梯度

门单元类能够这么定义,比方一个乘法单元:

class MultiplyGate(object):
    def forward(self, x, y):
        z = x*y
        self.x = x
        self.y = y
        return z

    def backward(self, dz):
        dx = self.y * dz
        dy = self.x * dz
        return [dx, dy]

1.2 向量模式反向流传

先思考一个简略的例子,比方:

这个 \(max\) 函数对输出向量 \(x\) 的每个元素都和 \(0\) 比拟输入最大值,因而输入向量的维度也是 \(4096\)维。此时的梯度是 雅可比矩阵 ,即 输入的每个元素对输出的每个元素求偏导组成的矩阵

如果输出 \(x\) 是 \(N\) 维的向量,输入 \(y\) 是 \(m\) 维的向量,则 \(y_1,y_2, \cdots,y_m\) 都是 \((x_1-x_n)\) 的函数,失去的雅克比矩阵如下所示:

$$
\left[\begin{array}{ccc}
\frac{\partial y_{1}}{\partial x_{1}} & \cdots & \frac{\partial y_{1}}{\partial x_{n}} \\
\vdots & \ddots & \vdots \\
\frac{\partial y_{m}}{\partial x_{1}} & \cdots & \frac{\partial y_{m}}{\partial x_{n}}
\end{array}\right]
$$

那么这个例子的雅克比矩阵是 \([4096 \times 4096]\) 维的,输入有 \(4096\) 个元素,每一个都要求 \(4096\) 次偏导。其实仔细观察发现,这个例子输入的每个元素都只和输出相应地位的元素无关,因而失去的是一个对角矩阵。

理论利用的时候,往往 100 个 \(x\) 同时输出,此时雅克比矩阵是一个 \([409600 \times 409600]\) 的对角矩阵,当然只是针对这里的 \(f\) 函数。

实际上,齐全写出并存储雅可比矩阵不太可能,因为维度极其大。

1) 一个例子

指标公式为:\(f(x,W)=\vert \vert W\cdot x \vert \vert ^2=\sum_{i=1}^n (W\cdot x)_{i}^2\)

其中 \(x\) 是 \(N\) 维的向量,\(W\) 是 \(n \times n\) 的矩阵。

设 \(q=W\cdot x\) , 于是失去上面的式子:

$$
\left[\begin{array}{ccc}
\frac{\partial y_{1}}{\partial x_{1}} & \cdots & \frac{\partial y_{1}}{\partial x_{n}} \\
\vdots & \ddots & \vdots \\
\frac{\partial y_{m}}{\partial x_{1}} & \cdots & \frac{\partial y_{m}}{\partial x_{n}}
\end{array}\right]
$$

$$
\begin{array}{l}
q=W \cdot x=\left(\begin{array}{c}
W_{1,1} x_{1}+\cdots+W_{1, n} x_{n} \\
\vdots \\
W_{n, 1} x_{1}+\cdots+W_{n, n} x_{n}
\end{array}\right) \\
\end{array}
$$

$$
f(q)=\|q\|^{2}=q_{1}^{2}+\cdots+q_{n}^{2}
$$

能够看出:

  • \(\frac{\partial f}{\partial q_i}=2q_i\) 从而失去 \(f\) 对 \(q\) 的梯度为 \(2q\);
  • \(\frac{\partial q_k}{\partial W_{i, j}}=1{i=k}x_j\),\(\frac{\partial f}{\partial W_{i, j}}=\sum_{k=1}^n\frac{\partial f}{\partial q_k}\frac{\partial q_k}{\partial W_{i, j}}=\sum_{k=1}^n(2q_k)1{i=k}x_j=2q_ix_j\),从而失去 \(f\) 对 \(W\) 的梯度为 \(2q\cdot x^T\);
  • \(\frac{\partial q_k}{\partial x_i}=W_{k,i}\),\(\frac{\partial f}{\partial x_i}=\sum_{k=1}^n\frac{\partial f}{\partial q_k}\frac{\partial q_k}{\partial x_i}=\sum_{k=1}^n(2q_k)W_{k,i}\),从而失去 \(f\) 对 \(x\) 的梯度为 \(2W^T\cdot q\)

上面为计算图:

2) 代码实现

import numpy as np

# 初值
W = np.array([[0.1, 0.5], [-0.3, 0.8]])
x = np.array([0.2, 0.4]).reshape((2, 1))  # 为了保障 dq.dot(x.T)是一个矩阵而不是实数

# 前向流传
q = W.dot(x)
f = np.sum(np.square(q), axis=0)

# 反向流传
# 回传 f = np.sum(np.square(q), axis=0)
dq = 2*q
# 回传 q = W.dot(x)
dW = dq.dot(x.T)  # x.T 就是对矩阵 x 进行转置
dx = W.T.dot(dq)

留神:要剖析维度!不要去记忆 \(dW\) 和 \(dx\) 的表达式,因为它们很容易通过维度推导进去。

权重的梯度 \(dW\) 的尺寸必定和权重矩阵 \(W\) 的尺寸是一样的

  • 这里的 \(f\) 输入是一个实数,所以 \(dW\)和 \(W\) 的形态统一。
  • 如果思考 \(dq/dW\) 的话,如果依照雅克比矩阵的定义,\(dq/dw\) 应该是 \(2 \times 2 \times 2\) 维,为了减小计算量,就令其等于 \(x\)。
  • 其实齐全不必思考那么简单,因为 最终的损失函数肯定是一个实数,所以每个门单元的输出梯度肯定和原输出形态雷同。 对于这点的阐明,能够 点击这里,官网进行了具体的推导。
  • 而这又是由 \(x\) 和 \(dq\) 的矩阵乘法决定的,总有一个形式是可能让维度之间可能对的上的。

例如,\(x\) 的尺寸是 \([2 \times 1]\),\(dq\) 的尺寸是 \([2 \times 1]\),如果你想要 \(dW\) 和 \(W\) 的尺寸是 \([2 \times 2]\),那就要 dq.dot(x.T),如果是 x.T.dot(dq) 后果就不对了。(\(dq\) 是回传梯度不能转置!)

2. 神经网络简介

2.1 神经网络算法介绍

在不诉诸大脑的类比的状况下,仍然是能够对神经网络算法进行介绍的。

在线性分类一节中,在给出图像的状况下,是应用 \(Wx\) 来计算不同视觉类别的评分,其中 \(W\) 是一个矩阵,\(x\) 是一个输出列向量,它蕴含了图像的全副像素数据。在应用数据库 CIFAR-10 的案例中,\(x\) 是一个 \([3072 \times 1]\) 的列向量,\(W\) 是一个 \([10 \times 3072]\) 的矩阵,所以输入的评分是一个蕴含 10 个分类评分的向量。

一个两层的神经网络算法则不同,它的计算公式是 \(s = W_2 \max(0, W_1 x)\)。

\(W_1\) 的含意:举例来说,它能够是一个 \([100 \times 3072]\) 的矩阵,其作用是将图像转化为一个 100 维的过渡向量,比方马的图片有头朝左和朝右,会别离失去一个分数。

函数 \(max(0,-)\) 是非线性的,它会作用到每个元素。这个非线性函数有多种抉择,大家在后续激活函数里会再看到。当初看到的这个函数是最罕用的 ReLU 激活函数,它将所有小于 \(0\) 的值变成 \(0\)。

矩阵 \(W_2\) 的尺寸是 \([10 \times 100]\),会对中间层的得分进行加权求和,因而将失去 10 个数字,这 10 个数字能够解释为是分类的评分。

留神:非线性函数在计算上是至关重要的,如果略去这一步,那么两个矩阵将会合二为一,对于分类的评分计算将从新变成对于输出的线性函数。这个非线性函数就是扭转的关键点。

参数 \(W_1\) ,\(W_2\) 将通过随机梯度降落来学习到,他们的梯度在反向流传过程中,通过链式法则来求导计算得出。

一个三层的神经网络能够类比地看做 \(s = W_3 \max(0, W_2 \max(0, W_1 x))\),其中 \(W_1\), \(W_2\) ,\(W_3\) 是须要进行学习的参数。两头隐层的尺寸是网络的超参数,后续将学习如何设置它们。当初让咱们先从神经元或者网络的角度了解上述计算。

两层神经网络参考代码实现如下,中间层应用 sigmoid 函数:

import numpy as np
from numpy.random import randn

N, D_in, H, D_out = 64, 1000, 100, 10
# x 是 64x1000 的矩阵,y 是 64x10 的矩阵
x, y = randn(N, D_in), randn(N, D_out)
# w1 是 1000x100 的矩阵,w2 是 100x10 的矩阵
w1, w2 = randn(D_in, H), randn(H, D_out)

# 迭代 10000 次,损失达到 0.0001 级
for t in range(10000):
    h = 1 / (1 + np.exp(-x.dot(w1)))  # 激活函数应用 sigmoid 函数,中间层
    y_pred = h.dot(w2)
    loss = np.square(y_pred - y).sum()  # 损失应用 L2 范数
    print(str(t)+':'+str(loss))

    # 反向流传
    grad_y_pred = 2.0 * (y_pred - y)
    grad_w2 = h.T.dot(grad_y_pred)
    grad_h = grad_y_pred.dot(w2.T)
    # grad_xw1 = grad_h*h*(1-h)
    grad_w1 = x.T.dot(grad_h*h*(1-h))

    # 学习率是 0.0001
    w1 -= 1e-4 * grad_w1
    w2 -= 1e-4 * grad_w2

2.2 神经网络与实在的神经比照

神经网络算法很多时候是受生物神经系统启发而简化模仿失去的。

大脑的根本计算单位是 神经元(neuron)。人类的神经系统中大概有 860 亿个神经元,它们被大概 1014 – 1015 个 突触(synapses) 连接起来。下图的上方是一个生物学的神经元,下方是一个简化的罕用数学模型。每个神经元都从它的 树突(dendrites) 取得输出信号,而后沿着它惟一的 轴突(axon) 产生输入信号。轴突在末端会逐步分枝,通过突触和其余神经元的树突相连。

在神经元的计算模型中,沿着轴突流传的信号(比方 \(x_0\))将基于突触的突触强度(比方 \(w_0\)),与其余神经元的树突进行乘法交互(比方 \(w_0 x_0\))。

对应的想法是,突触的强度(也就是权重 \(W\)),是可学习的且能够管制一个神经元对于另一个神经元的影响强度(还能够管制影响方向:使其兴奋(正权重)或使其克制(负权重))。

树突将信号传递到细胞体,信号在细胞体中相加。如果最终之和高于某个阈值,那么神经元将会「激活」,向其轴突输入一个峰值信号。

在计算模型中,咱们假如峰值信号的精确工夫点不重要,是激活信号的频率在交流信息。基于这个速率编码的观点,将神经元的激活率建模为 激活函数(activation function) \(f\),它表白了轴突上激活信号的频率。

因为历史起因,激活函数经常抉择应用sigmoid 函数 \(\sigma\),该函数输出实数值(求和后的信号强度),而后将输出值压缩到 \(0\sim 1\) 之间。在本节前面局部会看到这些激活函数的各种细节。

这里的激活函数 \(f\) 采纳的是 sigmoid 函数,代码如下:

class Neuron:
    # ...
    def neuron_tick(self, inputs):
        # 假如输出和权重都是 1xD 的向量,偏差是一个数字
        cell_body_sum = np.sum(inputs*self.weights) + self.bias
        # 当和远大于 0 时,输入为 1,被激活
        firing_rate = 1.0 / (1.0 + np.exp(-cell_body_sum))
        return firing_rate

2.3 罕用的激活函数

3. 神经网络构造

对于神经网络构造的常识也能够参考 ShowMeAI 的 深度学习教程 | 吴恩达专项课程 · 全套笔记解读 中的文章 神经网络根底 浅层神经网络 深层神经网络 里对于不同深度的网络结构的解说

对于一般神经网络,最一般的层级构造是 全连贯层(fully-connected layer)。全连贯层中的神经元与其前后两层的神经元是齐全成对连贯的,然而在同层外部的神经元之间没有连贯。网络结构中没有循环(因为这样会导致前向流传的有限循环)。

上面是两个神经网络的图例,都应用的全连贯层:

  • 右边:一个 2 层神经网络,隐层由 4 个神经元(也可称为单元(unit))组成,输入层由 2 个神经元组成,输出层是 3 个神经元(指的是输出图片的维度而不是图片的数量)。
  • 左边:一个 3 层神经网络,两个含 4 个神经元的隐层。

留神:当咱们说 \(N\) 层神经网络的时候,咱们并不计入输出层。单层的神经网络就是没有隐层的(输出间接映射到输入)。也会应用人工神经网络(Artificial Neural Networks 缩写 ANN)或者多层感知器(Multi-Layer Perceptrons 缩写 MLP)来指代全连贯层构建的这种神经网络。此外,输入层的神经元个别不含激活函数。

用来度量神经网络的尺寸的规范次要有两个:一个是 神经元的个数 ,另一个是 参数的个数。用下面图示的两个网络举例:

  • 第一个网络有 \(4+2=6\) 个神经元(输出层不算),\([3 \times 4]+[4 \times 2]=20\) 个权重,还有 \(4+2=6\) 个偏置,共 \(26\) 个可学习的参数。
  • 第二个网络有 \(4+4+1=9\) 个神经元,\([3 \times 4]+[4 \times 4]+[4 \times 1]=32\) 个权重,\(4+4+1=9\) 个偏置,共 \(41\) 个可学习的参数。

古代卷积神经网络能蕴含上亿个参数,可由几十上百层形成(这就是深度学习)。

3.1 三层神经网络代码示例

一直用类似的构造重叠造成网络,这让神经网络算法应用矩阵向量操作变得简略和高效。咱们回到下面那个 3 层神经网络,输出是 \([3 \times 1]\) 的向量。一个层所有连贯的权重能够存在一个独自的矩阵中。

比方第一个隐层的权重 \(W_1\) 是 \([4 \times 3]\),所有单元的偏置贮存在 \(b_1\) 中,尺寸 \([4 \times 1]\)。这样,每个神经元的权重都在 \(W_1\) 的一个行中,于是矩阵乘法 np.dot(W1, x)+b1 就能作为该层中所有神经元激活函数的输出数据。相似的,\(W_2\) 将会是 \([4 \times 4]\) 矩阵,存储着第二个隐层的连贯,\(W_3\) 是 \([1 \times 4]\) 的矩阵,用于输入层。

残缺的 3 层神经网络的前向流传就是简略的 3 次矩阵乘法,其中交织着激活函数的利用。

import numpy as np

# 三层神经网络的前向流传
# 激活函数
f = lambda x: 1.0/(1.0 + np.exp(-x))

# 随机输出向量 3x1
x = np.random.randn(3, 1)
# 设置权重和偏差
W1, W2, W3 = np.random.randn(4, 3), np.random.randn(4, 4), np.random.randn(1, 4),
b1, b2= np.random.randn(4, 1), np.random.randn(4, 1)
b3 = 1

# 计算第一个暗藏层激活 4x1
h1 = f(np.dot(W1, x) + b1)
# 计算第二个暗藏层激活 4x1
h2 = f(np.dot(W2, h1) + b2)
# 输入是一个数
out = np.dot(W3, h2) + b3

在下面的代码中,\(W_1\),\(W_2\),\(W_3\),\(b_1\),\(b_2\),\(b_3\) 都是网络中能够学习的参数。留神 \(x\) 并不是一个独自的列向量,而能够是一个批量的训练数据(其中每个输出样本将会是 \(x\) 中的一列),所有的样本将会被并行化的高效计算出来。

留神神经网络最初一层通常是没有激活函数的(例如,在分类工作中它给出一个实数值的分类评分)。

全连贯层的前向流传个别就是先进行一个矩阵乘法,而后加上偏置并使用激活函数。

3.2 了解神经网络

对于深度神经网络的解释也能够参考 ShowMeAI 的 深度学习教程 | 吴恩达专项课程 · 全套笔记解读 中的文章 深层神经网络 里「 深度网络其余劣势」局部的解说

全连贯层的神经网络的一种了解是:

  • 它们定义了一个由一系列函数组成的函数族,网络的权重就是每个函数的参数。

领有至多一个隐层的神经网络是一个通用的近似器,神经网络能够近似任何连续函数。

尽管一个 2 层网络在数学实践上能完满地近似所有连续函数,但在实际操作中成果绝对较差。尽管在实践上深层网络(应用了多个隐层)和单层网络的表达能力是一样的,然而就实践经验而言,深度网络成果比单层网络好。

对于全连贯神经网络而言,在实践中 3 层的神经网络会比 2 层的体现好,然而持续加深(做到 4,5,6 层)很少有太大帮忙。卷积神经网络的状况却不同,在卷积神经网络中,对于一个良好的识别系统来说,深度是一个十分重要的因素(比方当今成果好的 CNN 都有几十上百层)。对于该景象的一种解释观点是:因为图像领有层次化构造(比方脸是由眼睛等组成,眼睛又是由边缘组成),所以多层解决对于这种数据就有直观意义。

4. 拓展学习

能够点击 B 站 查看视频的【双语字幕】版本

  • 【课程学习指南】斯坦福 CS231n | 深度学习与计算机视觉
  • 【字幕 + 材料下载】斯坦福 CS231n | 深度学习与计算机视觉 (2017·全 16 讲)
  • 【CS231n 进阶课】密歇根 EECS498 | 深度学习与计算机视觉
  • 【深度学习教程】吴恩达专项课程 · 全套笔记解读
  • 【Stanford 官网】CS231n: Deep Learning for Computer Vision

5. 要点总结

  • 前向流传与反向流传
  • 标量与向量化模式计算
  • 求导链式法则利用
  • 神经网络构造
  • 激活函数
  • 了解神经网络

斯坦福 CS231n 全套解读

  • 深度学习与 CV 教程(1) | CV 引言与根底
  • 深度学习与 CV 教程(2) | 图像分类与机器学习根底
  • 深度学习与 CV 教程(3) | 损失函数与最优化
  • 深度学习与 CV 教程(4) | 神经网络与反向流传
  • 深度学习与 CV 教程(5) | 卷积神经网络
  • 深度学习与 CV 教程(6) | 神经网络训练技巧 (上)
  • 深度学习与 CV 教程(7) | 神经网络训练技巧 (下)
  • 深度学习与 CV 教程(8) | 常见深度学习框架介绍
  • 深度学习与 CV 教程(9) | 典型 CNN 架构 (Alexnet, VGG, Googlenet, Restnet 等)
  • 深度学习与 CV 教程(10) | 轻量化 CNN 架构 (SqueezeNet, ShuffleNet, MobileNet 等)
  • 深度学习与 CV 教程(11) | 循环神经网络及视觉利用
  • 深度学习与 CV 教程(12) | 指标检测 (两阶段, R-CNN 系列)
  • 深度学习与 CV 教程(13) | 指标检测 (SSD, YOLO 系列)
  • 深度学习与 CV 教程(14) | 图像宰割 (FCN, SegNet, U-Net, PSPNet, DeepLab, RefineNet)
  • 深度学习与 CV 教程(15) | 视觉模型可视化与可解释性
  • 深度学习与 CV 教程(16) | 生成模型 (PixelRNN, PixelCNN, VAE, GAN)
  • 深度学习与 CV 教程(17) | 深度强化学习 (马尔可夫决策过程, Q-Learning, DQN)
  • 深度学习与 CV 教程(18) | 深度强化学习 (梯度策略, Actor-Critic, DDPG, A3C)

ShowMeAI 系列教程举荐

  • 大厂技术实现:举荐与广告计算解决方案
  • 大厂技术实现:计算机视觉解决方案
  • 大厂技术实现:自然语言解决行业解决方案
  • 图解 Python 编程:从入门到精通系列教程
  • 图解数据分析:从入门到精通系列教程
  • 图解 AI 数学根底:从入门到精通系列教程
  • 图解大数据技术:从入门到精通系列教程
  • 图解机器学习算法:从入门到精通系列教程
  • 机器学习实战:手把手教你玩转机器学习系列
  • 深度学习教程:吴恩达专项课程 · 全套笔记解读
  • 自然语言解决教程:斯坦福 CS224n 课程 · 课程带学与全套笔记解读
  • 深度学习与计算机视觉教程:斯坦福 CS231n · 全套笔记解读

退出移动版