在上节课中,我们学习了逻辑回归——一种经典的学习算法。我兴致勃勃地用它训练了一个猫狗分类模型,结果只得到了57%这么个惨淡的准确率。正好,这周开始学习如何实现更复杂的模型了。这次,我一定要一雪前耻!
开始学这周的课之前,先回忆一下上周我们学习了什么。
对于一个神经网络,我们要定义它的网络结构(一个数学公式),定义损失函数。根据损失函数和网络结构,我们可以对网络的参数求导,并用梯度下降法最小化损失函数。
也就是说,不管是什么神经网络,都由以下几部分组成:
- 网络结构
- 损失函数
- 优化策略
而在编程实现神经网络时,我们不仅要用计算机语言定义上面这几项内容,还需要收集数据、预处理数据。
在这堂课中,我们要学一个更复杂的模型,其知识点逃不出上面这些范围。在之后的学习中我们还会看到,浅层神经网络的损失函数和优化策略和上节课的逻辑回归几乎是一模一样的。我们要关心的,主要是网络结构上的变化。
在学习之前,我们可以先有一个心理准备,知道大概要学到哪些东西。
前排提示:本文篇幅较长。如果想看本文的精简版,欢迎移步我在其他地方发的文章.
课堂笔记
神经网络概述与符号标记
上节课我们使用的逻辑回归过于简单,它只能被视为只有一个神经元(计算单元)的神经网络。如上图第一行所示。
一般情况下,神经网络都是由许多神经元组成的。我们把一次性计算的神经元都算作“一层”。比如上图第二行的网络有两层,第一层有3个神经元,第二层有1个神经元。
上节课中,对于一个样本 x x x,一层的神经网络是用下面的公式计算的:
y ^ = a = σ ( w T x + b ) \hat{y}=a=\sigma(w^Tx+b) y^=a=σ(wTx+b)
而这节课将使用的两层神经网络,也使用类似的公式计算:
a [ 1 ] = σ ( W [ 1 ] x + b [ 1 ] ) y ^ = a [ 2 ] = σ ( W [ 2 ] a [ 1 ] + b [ 2 ] ) \begin{aligned} a^{[1]} & = \sigma(W^{[1]}x+b^{[1]}) \\ \hat{y}=a^{[2]} & = \sigma(W^{[2]}a^{[1]}+b^{[2]}) \end{aligned} a[1]y^=a[2]=σ(W[1]x+b[1])=σ(W[2]a[1]+b[2])
上节课中,参数 w w w是一个列向量。这节课的参数 W W W是一个矩阵。我们稍后会见到 W W W的全貌。
这里的方括号上标 [ l ] [l] [l]表示第 l l l层相关的变量。总结一下, a i [ j ] ( k ) a_i^{[j](k)} ai[j](k)表示第 k k k个样本在网络第 j j j层中向量的第 i i i个分量。
事实上,输入 x x x可以看成 a [ 0 ] a^{[0]} a[0]。
这里的a是activation(激活)意思,每个 a a a都是激活函数的输出。
为了方便称呼,我们给神经网络的层取了些名字:
其中,输入叫做“输入层”,最后一个计算层叫做“输出层”,中间其余的层都叫做“隐藏层”。事实上,由于第一个输入层不参与计算,它不会计入网络的总层数,只是为了方便称呼才这么叫。因此,上面这个网络看上去有3层,但叫做“双层神经网络”,或“单隐藏层神经网络”。
单样本多神经元的计算
让我们先看一下,对于一个输入样本 x ( 1 ) x^{(1)} x(1),神经网络是怎么计算输出的。
如图,输入 x x x 是一个形状为 3 × 1 3 \times 1 3×1的列向量。第一层有三个神经元,第一个神经元的参数是 w 1 [ 1 ] , b 1 [ 1 ] w_1^{[1]}, b_1^{[1]} w1[1],b1[1],第二个是 w 2 [ 1 ] , b 2 [ 1 ] w_2^{[1]}, b_2^{[1]} w2[1],b2[1],第三个是 w 3 [ 1 ] , b 3 [ 1 ] w_3^{[1]}, b_3^{[1]} w3[1],b3[1]。
w i [ 1 ] w_i^{[1]} wi[1]的形状是 1 × 3 1 \times 3 1×3, b i [ 1 ] b_i^{[1]} bi[1]是常数。
每个神经元的计算公式和上节课的逻辑回归相同,都是 z i [ 1 ] = w i [ 1 ] x + b i [ 1 ] z_i^{[1]}=w_i^{[1]}x+b_i^{[1]} zi[1]=wi[1]x+bi[1], a i [ 1 ] = σ ( z i [ 1 ] ) a_i^{[1]}=\sigma(z_i^{[1]}) ai[1]=σ(zi[1])( i ∈ [ 1 , 2 , 3 ] i \in [1, 2, 3] i∈[1,2,3])。
回忆一下,上一节课里 w w w的形状是 n x × 1 n_x \times1 nx×1,即一个长度为 n x n_x nx的列向量,其中 n x n_x nx是输入向量的长度(此处为3)。 b b b是一个常数。计算结果时,我们要把 w w w转置,计算 w T x + b w^Tx+b wTx+b。这里的 w i [ 1 ] w_i^{[1]} wi[1]是一个行向量,其形状是 1 × n x 1 \times n_x 1×nx,计算时不用转置。计算时直接 w i [ 1 ] x + b i [ 1 ] w_i^{[1]}x+b_i^{[1]} wi[1]x+bi[1]就行。
因为有三个神经元,我们得到三个计算结果 a 1 [ 1 ] , a 2 [ 1 ] , a 3 [ 1 ] a_1^{[1]}, a_2^{[1]}, a_3^{[1]} a1[1],a2[1],a3[1]。我们可以把它们合起来当成一个 3 × 1 3 \times 1 3×1的列向量 a [ 1 ] a^{[1]} a[1],就像输入 x x x一样。
之后,这三个输出作为输入传入第二层的神经元,计算 z [ 2 ] = W 1 [ 2 ] a [ 1 ] + b [ 2 ] z^{[2]}=W_1^{[2]}a^{[1]}+b^{[2]} z[2]=W1[2]a[1]+b[2], y ^ = a [ 2 ] = σ ( z [ 2 ] ) \hat{y}=a^{[2]}=\sigma(z^{[2]}) y^=a[2]=σ(z[2])。这个算式和上周的逻辑回归一模一样。
总结一下,如果某一层有 n n n个神经元,那么这一层的输出就是一个长度为 n n n的列向量。这个输出会被当作下一层的输入。神经网络的每一层都按同样的方式计算着。
对于单隐层神经网络,隐藏层的参数 W [ 1 ] W^{[1]} W[1]的形状是 n 1 × n x n_1 \times n_x n1×nx,其中 n 1 n_1 n1是隐藏层神经元个数, n x n_x nx是每个输入样本的向量长度。参数 b [ 1 ] b^{[1]} b[1]的形状是 n 1 × 1 n_1 \times 1 n1×1。输出层参数 W [ 2 ] W^{[2]} W[2]的形状是 1 × n 1 1 \times n_1 1×n1, b [ 2 ] b^{[2]} b[2]的形状是 1 × 1 1 \times 1 1×1。
多样本多神经元的计算
和上一节课一样,让我们把一个输入样本拓展到多个样本,看看整个计算公式该怎么写。
对于第 i i i个输入样本 x ( i ) x^{(i)} x(i),我们要计算:
a [ 1 ] ( i ) = σ ( W [ 1 ] x ( i ) + b [ 1 ] ) a [ 2 ] ( i ) = σ ( W [ 2 ] a [ 1 ] ( i ) + b [ 2 ] ) \begin{aligned} a^{[1](i)} & =\sigma(W^{[1]}x^{(i)}+b^{[1]}) \\ a^{[2](i)} & =\sigma(W^{[2]}a^{[1](i)}+b^{[2]}) \end{aligned} a[1](i)a[2](i)=σ(W[1]x(i)+b[1])=σ(W[2]a[1](i)+b[2])
直接写的话,我们要写个for循环,把 i i i从 0 0 0遍历到 m − 1 m-1 m−1。
回忆一下, m m m是样本总数。
但是,如果把输入打包在一起,形成一个 n x × m n_x \times m nx×m的矩阵 X X X,那么整个计算过程可以用十分相似的向量化计算公式表示:
A [ 1 ] = σ ( W [ 1 ] X + b [ 1 ] ) A [ 2 ] = σ ( W [ 2 ] A [ 1 ] + b [ 2 ] ) \begin{aligned} A^{[1]} & =\sigma(W^{[1]}X+b^{[1]}) \\ A^{[2]} & =\sigma(W^{[2]}A^{[1]}+b^{[2]}) \end{aligned} A[1]A[2]=σ(W[1]X+b[1])=σ(W[2]A[1]+b[2])
这里的 X X X, A A A相当于横向“拉长了”:
X = [ ∣ ∣ ∣ x ( 1 ) x ( 2 ) . . . x ( m ) ∣ ∣ ∣ ] A [ l ] = [ ∣ ∣ ∣ a [ l ] ( 1 ) a [ l ] ( 2 ) . . . a [ l ] ( m ) ∣ ∣ ∣ ] X=\left[ \begin{matrix} | & | & & | \\ x^{(1)} & x^{(2)} & ... & x^{(m)} \\ | & | & & | \end{matrix} \right] \\ \ \\ A^{[l]}=\left[ \begin{matrix} | & | & & | \\ a^{[l](1)} & a^{[l](2)} & ... & a^{[l](m)} \\ | & | & & | \end{matrix} \right] X=⎣⎡∣x(1)∣∣x(2)∣...∣x(m)∣⎦⎤ A[l]=⎣⎡