Week2:神经网络的编程基础(Basics of Neural Network programming)
Pre:本章基本字母含义
2.1 Binary Classification(二分法)
逻辑回归模型一般用来解决二分类(Binary Classification)问题。
- 二分类就是输出y只有{0,1}两个离散值(也有{-1,1}的情况)。
- 以一个图像识别问题为例,判断图片中是否有猫存在,0代表no cat,1代表cat。
- 彩色图片包含RGB三个通道。例如该cat图片的尺寸为(64,64,3)。在神经网络模型中,我们首先要将图片输入 x x x(维度是(64,64,3))转化为一维的特征向量(feature vector)。方法是每个通道一行一行取,再连接起来。由于64x64x3=12288,则转化后的输入特征向量维度为(12288,1)。此特征向量 x x x是列向量,维度一般记为 n x n_x nx。
- 如果训练样本共有 m m m张图片,那么整个训练样本X组成了矩阵,维度是 ( n x , m ) (n_x,m) (nx,m)。注意,这里矩阵 X X X的行 n x n_x nx代表了每个样本 x ( i ) x^{(i)} x(i)特征个数,列 m m m代表了样本个数。
- Python实现的时候,你会看到
X.shape
,这是一条Python命令,用于显示矩阵的规模,即X.shape
等于 ( n x , m ) (n_x,m) (nx,m)
2.2 Logistic Regression(逻辑回归)
2.3 Logistic Regression Cost Function(逻辑回归的损失函数)
参照吴恩达《机器学习》Week3的逻辑回归和代价/损失函数。
2.4 Gradient Descent(逻辑回归的梯度下降法)
- 问题提出
对单个数据 ( x , y ) (x,y) (x,y)逻辑回归的损失函数是: L ( y ^ , y ) = − y log ( y ^ ) − ( 1 − y ) log ( 1 − y ^ ) L\left( \hat{y},y \right)=-y\log(\hat{y})-(1-y)\log (1-\hat{y}) L(y^,y)=−ylog(y^)−(1−y)log(1−y^), m m m个样本的损失函数是:
J ( w , b ) = 1 m ∑ i = 1 m L ( y ^ ( i ) , y ( i ) ) = 1 m ∑ i = 1 m ( − y ( i ) log y ^ ( i ) − ( 1 − y ( i ) ) log ( 1 − y ^ ( i ) ) ) J\left( w,b \right)=\frac{1}{m}\sum\limits_{i=1}^{m}{L\left( {{{\hat{y}}}^{(i)}},{{y}^{(i)}} \right)}=\frac{1}{m}\sum\limits_{i=1}^{m}{\left( -{{y}^{(i)}}\log {{{\hat{y}}}^{(i)}}-(1-{{y}^{(i)}})\log (1-{{{\hat{y}}}^{(i)}}) \right)} J(w,b)=m1i=1∑mL(y^(i),y(i))=m1i=1∑m(−y(i)logy^(i)−(1−y(i))log(1−y^(i)))
所以在训练逻辑回归模型时候,我们需要找到合适的 w w w和 b b b,来让代价函数 J J J 的总代价降到最低。其中: y ^ = σ ( w T x + b ) , σ ( z ) = 1 1 + e − z \hat{y}=\sigma\left(w^{T} x+b\right), \sigma(z)=\frac{1}{1+e^{-z}} y^=σ(wTx+b),σ(z)=1+e−z1 - 梯度下降法
- 由于J(w,b)是convex function,梯度下降算法是先随机选择一组参数w和b值,然后每次迭代的过程中分别沿着w和b的梯度(偏导数)的反方向前进一小步,不断修正w和b。每次迭代更新w和b后,都能让J(w,b)更接近全局最小值。梯度下降的过程如下图所示:
- 梯度下降算法每次迭代更新,w和b的修正表达式为:
w : = w − α ∂ J ( w , b ) ∂ w w:=w-\alpha\frac{\partial J(w,b)}{\partial w} w:=w−α∂w∂J(w,b)
b : = b − α ∂ J ( w , b ) ∂ b b:=b-\alpha\frac{\partial J(w,b)}{\partial b} b:=b−α∂b∂J(w,b) - 上式中,
α
\alpha
α是学习因子(learning rate),表示梯度下降的步进长度。
- α \alpha α越大, w w w和 b b b每次更新的“步伐”更大一些;
- α \alpha α越小, w w w和 b b b每次更新的“步伐”更小一些。
- 在程序代码中,我们通常使用
d
w
dw
dw来表示
∂
J
(
w
,
b
)
∂
w
\frac{\partial J(w,b)}{\partial w}
∂w∂J(w,b),用
d
b
db
db来表示
∂
J
(
w
,
b
)
∂
b
\frac{\partial J(w,b)}{\partial b}
∂b∂J(w,b)。
微积分里, d f d x \frac{df}{dx} dxdf表示对单一变量求导数, ∂ f ∂ x \frac{\partial f}{\partial x} ∂x∂f表示对多个变量中某个变量求偏导数。
- 由于J(w,b)是convex function,梯度下降算法是先随机选择一组参数w和b值,然后每次迭代的过程中分别沿着w和b的梯度(偏导数)的反方向前进一小步,不断修正w和b。每次迭代更新w和b后,都能让J(w,b)更接近全局最小值。梯度下降的过程如下图所示:
2.5 Derivatives(求导)
2.6 More Derivative Examples
这两节是求导和求导的例子。
2.7 Computation graph(计算图)
整个神经网络的训练过程实际上包含了两个过程:正向传播(Forward Propagation) 和 反向传播(Back Propagation)。
- 正向传播是从输入到输出,由神经网络计算得到预测输出的过程;
- 反向传播是从输出到输入,对参数w和b计算梯度的过程。
- 用计算图(Computation graph)的形式来理解这两个过程。举个简单的例子,假如Cost function为
J
(
a
,
b
,
c
)
=
3
(
a
+
b
c
)
J(a,b,c)=3(a+bc)
J(a,b,c)=3(a+bc),包含
a
,
b
,
c
a,b,c
a,b,c三个变量。我们用
u
u
u表示
b
c
bc
bc,
v
v
v表示
a
+
u
a+u
a+u,则
J
=
3
v
J=3v
J=3v。它的计算图可以写成如下图所示:
- 令 a = 5 , b = 3 , c = 2 a=5,b=3,c=2 a=5,b=3,c=2,则 u = b c = 6 , v = a + u = 11 , J = 3 v = 33 u=bc=6,v=a+u=11,J=3v=33 u=bc=6,v=a+u=11,J=3v=33。计算图中,这种从左到右,从输入到输出的过程就对应着神经网络或者逻辑回归中输入与权重经过运算计算得到Cost function的 正 向 过 程 \color{red}正向过程 正向过程。
2.8 Derivatives with a Computation Graph(计算图的求导)
上一节介绍了计算图的正向传播(Forward Propagation),下面来介绍计算图的反向传播(Back Propagation),即计算输出对输入的偏导数。
还是上个计算图的例子,输入参数有3个,分别是 a , b , c a,b,c a,b,c。
- 首先计算J对参数a的偏导数。从计算图上来看,从右到左,
J
J
J是
v
v
v的函数,
v
v
v是
a
a
a的函数。则利用求导技巧,可以得到:
∂ J ∂ a = ∂ J ∂ v ⋅ ∂ v ∂ a = 3 ⋅ 1 = 3 \frac{\partial J}{\partial a}=\frac{\partial J}{\partial v}\cdot \frac{\partial v}{\partial a}=3\cdot 1=3 ∂a∂J=∂v∂J⋅∂a∂v=3⋅1=3 - 根据这种思想,然后计算
J
J
J对参数
b
b
b的偏导数。从计算图上来看,从右到左,
J
J
J是
v
v
v的函数,
v
v
v是
u
u
u的函数,
u
u
u是
b
b
b的函数。可以推导:
∂ J ∂ b = ∂ J ∂ v ⋅ ∂ v ∂ u ⋅ ∂ u ∂ b = 3 ⋅ 1 ⋅ c = 3 ⋅ 1 ⋅ 2 = 6 \frac{\partial J}{\partial b}=\frac{\partial J}{\partial v}\cdot \frac{\partial v}{\partial u}\cdot \frac{\partial u}{\partial b}=3\cdot 1\cdot c=3\cdot 1\cdot 2=6 ∂b∂J=∂v∂J⋅∂u∂v⋅∂b∂u=3⋅1⋅c=3⋅1⋅2=6 - 最后计算
J
J
J对参数
c
c
c的偏导数。仍从计算图上来看,从右到左,
J
J
J是
v
v
v的函数,
v
v
v是
u
u
u的函数,
u
u
u是
c
c
c的函数。可以推导:
∂ J ∂ c = ∂ J ∂ v ⋅ ∂ v ∂ u ⋅ ∂ u ∂ c = 3 ⋅ 1 ⋅ b = 3 ⋅ 1 ⋅ 3 = 9 \frac{\partial J}{\partial c}=\frac{\partial J}{\partial v}\cdot \frac{\partial v}{\partial u}\cdot \frac{\partial u}{\partial c}=3\cdot 1\cdot b=3\cdot 1\cdot 3=9 ∂c∂J=∂v∂J⋅∂u∂v⋅∂c∂u=3⋅1⋅b=3⋅1⋅3=9
2.9 Logistic Regression Gradient Descent(逻辑回归的梯度下降法)
将对逻辑回归进行梯度计算。对单个样本而言,逻辑回归Loss function表达式如下:
z
=
w
T
x
+
b
y
^
=
a
=
σ
(
z
)
L
(
a
,
y
)
=
−
(
y
l
o
g
(
a
)
+
(
1
−
y
)
l
o
g
(
1
−
a
)
)
\begin{array}{l}z=w^Tx+b\\ \hat y=a=\sigma(z)\\ L(a,y)=-(ylog(a)+(1-y)log(1-a))\end{array}
z=wTx+by^=a=σ(z)L(a,y)=−(ylog(a)+(1−y)log(1−a))
-
首先是正向传播过程。根据上述公式,例如输入样本 x x x有两个特征 ( x 1 , x 2 ) (x_1,x_2) (x1,x2),相应的权重 w w w维度也是 2 2 2,即 ( w 1 , w 2 ) (w_1,w_2) (w1,w2)。则 z = w 1 x 1 + w 2 x 2 + b z=w_1x_1+w_2x_2+b z=w1x1+w2x2+b,Loss function的计算图如上图。
-
然后,计算该逻辑回归的反向传播过程,即由Loss function计算参数w和b的偏导数。推导过程如下:
d a = ∂ L ∂ a = − y a + 1 − y 1 − a d z = ∂ L ∂ z = ∂ L ∂ a ⋅ ∂ a ∂ z = ( − y a + 1 − y 1 − a ) ⋅ a ( 1 − a ) = a − y \begin{array}{l}da=\frac{\partial L}{\partial a}=-\frac ya+\frac{1-y}{1-a}\\ dz=\frac{\partial L}{\partial z}=\frac{\partial L}{\partial a}\cdot \frac{\partial a}{\partial z}=(-\frac ya+\frac{1-y}{1-a})\cdot a(1-a)=a-y\end{array} da=∂a∂L=−ay+1−a1−ydz=∂z∂L=∂a∂L⋅∂z∂a=(−ay+1−a1−y)⋅a(1−a)=a−y
知道了dz之后,就可以直接对w_1,w_2和b进行求导了。
d w 1 = ∂ L ∂ w 1 = ∂ L ∂ z ⋅ ∂ z ∂ w 1 = x 1 ⋅ d z = x 1 ( a − y ) d w 2 = ∂ L ∂ w 2 = ∂ L ∂ z ⋅ ∂ z ∂ w 2 = x 2 ⋅ d z = x 2 ( a − y ) d b = ∂ L ∂ b = ∂ L ∂ z ⋅ ∂ z ∂ b = 1 ⋅ d z = a − y \begin{array}{l}dw_1=\frac{\partial L}{\partial w_1}=\frac{\partial L}{\partial z}\cdot \frac{\partial z}{\partial w_1}=x_1\cdot dz=x_1(a-y)\\ dw_2=\frac{\partial L}{\partial w_2}=\frac{\partial L}{\partial z}\cdot \frac{\partial z}{\partial w_2}=x_2\cdot dz=x_2(a-y)\\ db=\frac{\partial L}{\partial b}=\frac{\partial L}{\partial z}\cdot \frac{\partial z}{\partial b}=1\cdot dz=a-y\end{array} dw1=∂w1∂L=∂z∂L⋅∂w1∂z=x1⋅dz=x1(a−y)dw2=∂w2∂L=∂z∂L⋅∂w2∂z=x2⋅dz=x2(a−y)db=∂b∂L=∂z∂L⋅∂b∂z=1⋅dz=a−y
则梯度下降算法可表示为:
w 1 : = w 1 − α d w 1 w 2 : = w 2 − α d w 2 b : = b − α d b \begin{array}{l}w_1:=w_1-\alpha\ dw_1\\ w_2:=w_2-\alpha\ dw_2\\ b:=b-\alpha\ db\end{array} w1:=w1−α dw1w2:=w2−α dw2b:=b−α db
2.10 Gradient descent on m examples(m个样本的梯度下降法)
在之前的视频中,你已经看到如何计算导数,以及应用梯度下降在逻辑回归的一个训练样本上。现在我们想要把它应用在
m
m
m个训练样本上。
m
m
m个样本,其Cost function表达式如下:
z
(
i
)
=
w
T
x
(
i
)
+
b
y
^
(
i
)
=
a
(
i
)
=
σ
(
z
(
i
)
)
J
(
w
,
b
)
=
1
m
∑
i
=
1
m
L
(
y
^
(
i
)
,
y
(
i
)
)
=
−
1
m
∑
i
=
1
m
[
y
(
i
)
l
o
g
y
^
(
i
)
+
(
1
−
y
(
i
)
)
l
o
g
(
1
−
y
^
(
i
)
)
]
\begin{array}{l} z^{(i)}=w^Tx^{(i)}+b\\ \hat y^{(i)}=a^{(i)}=\sigma(z^{(i)})\\ J(w,b)=\frac1m\sum_{i=1}^mL(\hat y^{(i)},y^{(i)})=-\frac1m\sum_{i=1}^m[y^{(i)}log\ \hat y^{(i)}+(1-y^{(i)})log\ (1-\hat y^{(i)})]\end{array}
z(i)=wTx(i)+by^(i)=a(i)=σ(z(i))J(w,b)=m1∑i=1mL(y^(i),y(i))=−m1∑i=1m[y(i)log y^(i)+(1−y(i))log (1−y^(i))]
-
w
w
w和
b
b
b的偏导数可以写成和平均的形式:
d w 1 = 1 m ∑ i = 1 m x 1 ( i ) ( a ( i ) − y ( i ) ) d w 2 = 1 m ∑ i = 1 m x 2 ( i ) ( a ( i ) − y ( i ) ) d b = 1 m ∑ i = 1 m ( a ( i ) − y ( i ) ) \begin{array}{l}dw_1=\frac1m\sum_{i=1}^mx_1^{(i)}(a^{(i)}-y^{(i)})\\ dw_2=\frac1m\sum_{i=1}^mx_2^{(i)}(a^{(i)}-y^{(i)})\\ db=\frac1m\sum_{i=1}^m(a^{(i)}-y^{(i)}) \end{array} dw1=m1∑i=1mx1(i)(a(i)−y(i))dw2=m1∑i=1mx2(i)(a(i)−y(i))db=m1∑i=1m(a(i)−y(i))
这样,每次迭代中 w w w和 b b b的梯度有 m m m个训练样本计算平均值得到。其算法流程图如下所示:J=0; dw1=0; dw2=0; db=0; for i = 1 to m z(i) = wx(i)+b; a(i) = sigmoid(z(i)); J += -[y(i)log(a(i))+(1-y(i))log(1-a(i)); dz(i) = a(i)-y(i); dw1 += x1(i)dz(i); dw2 += x2(i)dz(i); db += dz(i); J /= m; dw1 /= m; dw2 /= m; db /= m;
- 经过每次迭代后,根据梯度下降算法,w和b都进行更新:
w 1 : = w 1 − α d w 1 w 2 : = w 2 − α d w 2 b : = b − α d b \begin{array}{l}w_1:=w_1-\alpha\ dw_1\\ w_2:=w_2-\alpha\ dw_2\\ b:=b-\alpha\ db\end{array} w1:=w1−α dw1w2:=w2−α dw2b:=b−α db
这样经过 n n n次迭代后,整个梯度下降算法就完成了。 - 这种计算中有两个缺点,也就是说应用此方法在逻辑回归上你需要编写两个for循环。
- 第一个for循环是一个小循环遍历 m m m个训练样本,第二个for循环是一个遍历所有特征的for循环。
- 如果你有更多特征,你开始编写你的因此 d w 1 d{{w}_{1}} dw1, d w 2 d{{w}_{2}} dw2,你有相似的计算从 d w 3 d{{w}_{3}} dw3一直下去到 d w n d{{w}_{n}} dwn。所以看来你需要一个for循环遍历所有 n n n个特征。
- 深度学习算法,在代码中显式地使用for循环使你的算法很低效,同时在深度学习领域会有越来越大的数据集。需要使用矩阵,进行向量化处理。
2.11 向量化(Vectorization)
向量化是非常基础的去除代码中for循环的艺术,在深度学习安全领域、深度学习实践中,你会经常发现自己训练大数据集,因为深度学习算法处理大数据集效果很棒,所以你的代码运行速度非常重要。
- 以python为例子,向量化来加速运算(np.function),遍历
m
m
m遍的
f
o
r
for
for循环可以写成:
z=np.dot(w, x) + b
输出结果类似于:import numpy as np #导入numpy库 a = np.array([1,2,3,4]) #创建一个数据a print(a) # [1 2 3 4] import time #导入时间库 a = np.random.rand(1000000) b = np.random.rand(1000000) #通过round随机得到两个一百万维度的数组 tic = time.time() #现在测量一下当前时间 #向量化的版本 c = np.dot(a,b) toc = time.time() print("Vectorized version:" + str(1000*(toc-tic)) +"ms") #打印一下向量化的版本的时间 #继续增加非向量化的版本 c = 0 tic = time.time() for i in range(1000000): c += a[i]*b[i] toc = time.time() print(c) print("For loop:" + str(1000*(toc-tic)) + "ms")#打印for循环的版本的时间
250286.989866 Vectorized version:1.5027523040771484ms 250286.989866 For loop:474.29513931274414ms
- 为了加快深度学习神经网络运算速度,可以使用比CPU运算能力更强大的GPU。事实上,GPU和CPU都有并行指令(parallelization instructions),称为Single Instruction Multiple Data(SIMD)。SIMD是单指令多数据流,能够复制多个操作数,并把它们打包在大型寄存器的一组指令集。SIMD能够大大提高程序运行速度。
- python的numpy库中的内建函数(built-in function)就是使用了SIMD指令。
- 相比而言,GPU的SIMD要比CPU更强大一些。
2.12 向量化的更多例子(More Examples of Vectorization)
- 在python的numpy库中,我们通常使用np.dot()函数来进行矩阵运算。当你想写循环时候,检查numpy是否存在类似的内置函数,从而避免使用循环(loop)方式。
- numpy库有很多向量函数。比如
u=np.log
是计算对数函数( l o g log log)、np.abs()
计算数据的绝对值、np.maximum(v, 0)
按元素计算 v v v中每个元素和和0相比的最大值,v**2
代表获得元素 v v v 每个值的平方、1/v
获取 v v v 中每个元素的倒数等等。
2.13 向量化逻辑回归(Vectorizing Logistic Regression)
- 常规循环
首先我们回顾一下逻辑回归的前向传播步骤。如果有 m m m 个训练样本,然后对第一个样本进行预测,你需要这样计算。- 计算 z z z,我正在使用这个熟悉的公式 z ( 1 ) = w T x ( 1 ) + b z^{(1)}=w^{T}x^{(1)}+b z(1)=wTx(1)+b 。然后计算激活函数 a ( 1 ) = σ ( z ( 1 ) ) a^{(1)}=\sigma (z^{(1)}) a(1)=σ(z(1)) ,计算第一个样本的预测值 y y y 。
- 然后对第二个样本进行预测,你需要计算 z ( 2 ) = w T x ( 2 ) + b z^{(2)}=w^{T}x^{(2)}+b z(2)=wTx(2)+b , a ( 2 ) = σ ( z ( 2 ) ) a^{(2)}=\sigma (z^{(2)}) a(2)=σ(z(2)) 。
- 然后对第三个样本进行预测,你需要计算 z ( 3 ) = w T x ( 3 ) + b z^{(3)}=w^{T}x^{(3)}+b z(3)=wTx(3)+b , a ( 3 ) = σ ( z ( 3 ) ) a^{(3)}=\sigma (z^{(3)}) a(3)=σ(z(3)) ,依次类推。
- 如果你有 m m m 个训练样本,你可能需要这样做 m m m 次。
- 向量表示
- 定义了一个矩阵 X X X 作为训练输入,每个 x x x样本是 n x n_x nx,将不同的列中堆积在一起。这是一个 n x n_x nx 行 m m m 列的矩阵。我现在将它写为Python numpy的形式 ( n x , m ) (n_{x},m) (nx,m) ,这只是表示 X X X 是一个 n x n_x nx 乘以 m m m 的矩阵 R n x × m R^{n_x \times m} Rnx×m。
-
z
(
1
)
z^{(1)}
z(1),
z
(
2
)
z^{(2)}
z(2) ……一直到
z
(
m
)
z^{(m)}
z(m) ,所有值都是在同一时间内完成。
[ z ( 1 ) z ( 2 ) . . . z ( m ) ] = w T X + [ b , b . . . b ] [z^{(1)} z^{(2)}...z^{(m)}]=w^{T}X+[b,b...b] [z(1)z(2)...z(m)]=wTX+[b,b...b]
[ b , b . . . b ] [b,b...b] [b,b...b] 是一个 1 × m 1\times m 1×m 的向量或者 1 × m 1\times m 1×m 的矩阵或者是一个 m m m 维的行向量。所以希望你熟悉矩阵乘法,你会发现的 w w w 转置乘以 x ( 1 ) x^{(1)} x(1) , x ( 2 ) x^{(2)} x(2) 一直到 x ( m ) x^{(m)} x(m) 。继续简化可以写成:
Z = [ z ( 1 ) z ( 2 ) . . . z ( m ) ] = w T X + [ b , b . . . b ] = [ w T x ( 1 ) + b , w T x ( 2 ) + b . . . w T x ( m ) + b ] Z= [z^{(1)} z^{(2)}...z^{(m)}]=w^{T}X+[b,b...b]=[w^{T}x^{(1)}+b,w^{T}x^{(2)}+b...w^{T}x^{(m)}+b] Z=[z(1)z(2)...z(m)]=wTX+[b,b...b]=[wTx(1)+b,wTx(2)+b...wTx(m)+b]
其中大写的 Z Z Z表示为 [ z ( 1 ) z ( 2 ) . . . z ( m ) ] [z^{(1)} z^{(2)} ... z^{(m)}] [z(1)z(2)...z(m)] , w T x ( 1 ) + b w^{T}x^{(1)}+b wTx(1)+b 这是第一个元素, w T x ( 2 ) + b w^{T}x^{(2)}+b wTx(2)+b 这是第二个元素, w T x ( m ) + b w^{T}x^{(m)}+b wTx(m)+b 这是第 m m m 个元素。
- Python表示
- numpy命令是
Z=np.dot(w.T,X)+b
;最后再进行 s i g m o i d sigmoid sigmoid函数,一次性表示出 A = [ a ( 1 ) , a ( 2 ) . . . a ( m ) ] A = [a^{(1)},a^{(2)}...a^{(m)}] A=[a(1),a(2)...a(m)]。 - 这里在Python中有一个巧妙的地方,这里 b b b 是一个实数,或者你可以说是一个 1 × 1 1\times 1 1×1 矩阵。但是当你将这个向量加上这个实数时,Python自动把这个实数 b b b 扩展成一个 1 × m 1\times m 1×m 的行向量。在Python中被称作广播(brosdcasting),后边会具体介绍。
- numpy命令是
2.14 向量化 logistic 回归的梯度输出(Vectorizing Logistic Regression’s Gradient)
注:本节中大写字母代表向量,小写字母代表元素
- 在本节
2.10
2.10
2.10中,已经给出了逻辑回归中梯度下降法的一般表示形式:
d z j = a j − y j d w j = 1 m ∑ i = 1 m x j ( i ) ( a j ( i ) − y j ( i ) ) d b = 1 m ∑ i = 1 m ( a j ( i ) − y j ( i ) ) \begin{array}{l}dz_j = a_j - y_j\\ dw_j=\frac1m\sum_{i=1}^mx_j^{(i)}(a^{(i)}_j-y^{(i)}_j)\\ db=\frac1m\sum_{i=1}^m(a^{(i)}_j-y^{(i)}_j) \end{array} dzj=aj−yjdwj=m1∑i=1mxj(i)(aj(i)−yj(i))db=m1∑i=1m(aj(i)−yj(i)) - 接下来看看逻辑回归中的梯度下降算法如何转化为向量化的矩阵形式,对于所有
m
m
m个样本。
-
d
Z
dZ
dZ的维度是
(
1
,
m
)
(1,m)
(1,m),可表示为:
d Z = A − Y dZ=A-Y dZ=A−Y -
d
b
db
db可表示为:
d b = 1 m d Z = 1 m ∑ i = 1 m d z ( i ) db=\frac1m dZ = \frac1m \sum_{i=1}^mdz^{(i)} db=m1dZ=m1i=1∑mdz(i)
对应的程序为:db = 1/m*np.sum(dZ)
-
d
w
dw
dw可表示为:
d w = 1 m X ⋅ d Z T dw=\frac1m X\cdot dZ^T dw=m1X⋅dZT
对应的程序为:dw = 1/m*np.dot(X,dZ.T)
- 我们把整个逻辑回归中的
f
o
r
for
for循环尽可能用矩阵运算代替,对于单次迭代,梯度下降算法流程如下所示:
其中 α \alpha α是学习因子,决定 w w w和 b b b的更新速度。上述代码只是对单次训练更新而言的,外层还需要一个 f o r for for循环,表示迭代次数。Z = np.dot(w.T,X) + b A = sigmoid(Z) dZ = A-Y dw = 1/m*np.dot(X,dZ.T) db = 1/m*np.sum(dZ) w = w - alpha*dw b = b - alpha*db
-
d
Z
dZ
dZ的维度是
(
1
,
m
)
(1,m)
(1,m),可表示为:
2.15 Python 中的广播(Broadcasting in Python)
- numpy广播机制
- 定义:对于四则运算及其混合形式,如果两个数组的后缘维度的轴长度相符或其中一方的轴长度为1,则认为它们是广播兼容的。广播会在缺失维度和轴长度为1的维度上进行。 python中的广播机制可由下面四条表示:
- 对于Matlab/Octave 有类似功能的函数
bsxfun
。
- 定义:对于四则运算及其混合形式,如果两个数组的后缘维度的轴长度相符或其中一方的轴长度为1,则认为它们是广播兼容的。广播会在缺失维度和轴长度为1的维度上进行。 python中的广播机制可由下面四条表示:
- numpy中的axis
import numpy as np #输入数据 A = np.array([[56.0, 0.0, 4.4, 68.0], [1.2, 104.0, 52.0, 8.0], [1.8, 135.0, 99.0, 0.9]]) print(A) # 求每列的和 cal = A.sum(axis= 0) print(cal)
A.sum(axis = 0)
中的参数axis
。axis用来指明将要进行的运算是沿着哪个轴执行,在numpy中,0轴是垂直的,也就是列,而1轴是水平的,也就是行。
2.16 关于 python _ numpy 向量的说明(A note on python or numpy vectors)
本讲Python中的numpy一维数组的特性,以及与行向量或列向量的区别。并介绍在实际应用中的一些小技巧,去避免在coding中由于这些特性而导致的bug。
- 秩1:
这条语句生成的a的维度是(5,)。它既不是行向量也不是列向量,我们把a叫做rank 1 array(秩1)。这种定义会带来一些问题。例如我们对a进行转置,还是会得到a本身。a = np.random.randn(5)
- 定义向量:
- 如果我们要定义(5,1)的列向量或者(1,5)的行向量,最好使用下来标准语句,避免使用rank 1 array。
a = np.random.randn(5,1) b = np.random.randn(1,5)
- 可以使用
assert
语句对向量或数组的维度进行判断,例如:
assert会对内嵌语句进行判断,即判断a的维度是不是(5,1)的。如果不是,则程序在此处停止。使用assert语句也是一种很好的习惯,能够帮助我们及时检查、发现语句是否正确。assert(a.shape == (5,1))
- 还可以使用
reshape
函数对数组设定所需的维度:a.reshape((5,1))
- 如果我们要定义(5,1)的列向量或者(1,5)的行向量,最好使用下来标准语句,避免使用rank 1 array。
2.17 Jupyter/iPython Notebooks快速入门(Quick tour of Jupyter/iPython Notebooks)
Python安装Jupyter Notebook配置使用教程
2.18 (选修)logistic 损失函数的解释(Explanation of logistic regression cost function)
我们介绍过逻辑回归的Cost function。接下来我们将简要解释这个Cost function是怎么来的。
-
预测输出 y ^ \hat y y^的表达式可以写成:
y ^ = σ ( w T x + b ) \hat y=\sigma(w^Tx+b) y^=σ(wTx+b)
其中, σ ( z ) = 1 1 + e x p ( − z ) \sigma(z)=\frac{1}{1+exp(-z)} σ(z)=1+exp(−z)1。 y ^ \hat y y^可以看成是预测输出为正类 ( + 1 ) (+1) (+1)的概率: y ^ = P ( y = 1 ∣ x ) \hat y=P(y=1|x) y^=P(y=1∣x)- 当 y = 1 y=1 y=1时: p ( y ∣ x ) = y ^ p(y|x)=\hat y p(y∣x)=y^
- 当 y = 0 y=0 y=0时: p ( y ∣ x ) = 1 − y ^ p(y|x)=1-\hat y p(y∣x)=1−y^
-
把上面两个式子整合到一个式子中,得到:
P ( y ∣ x ) = y ^ y ( 1 − y ^ ) ( 1 − y ) P(y|x)=\hat y^y(1-\hat y)^{(1-y)} P(y∣x)=y^y(1−y^)(1−y)
由于 l o g log log函数的单调性,可以对上式 P ( y ∣ x ) P(y|x) P(y∣x)进行log处理:
l o g P ( y ∣ x ) = l o g y ^ y ( 1 − y ^ ) ( 1 − y ) = y l o g y ^ + ( 1 − y ) l o g ( 1 − y ^ ) log\ P(y|x)=log\ \hat y^y(1-\hat y)^{(1-y)}=y\ log\ \hat y+(1-y)log(1-\hat y) log P(y∣x)=log y^y(1−y^)(1−y)=y log y^+(1−y)log(1−y^)
上述概率 P ( y ∣ x ) P(y|x) P(y∣x)越大越好,对上式加上负号,则转化成了单个样本的Loss function,越小越好,也就得到了我们之前介绍的逻辑回归的Loss function形式。
L = − ( y l o g y ^ + ( 1 − y ) l o g ( 1 − y ^ ) ) L=-(y\ log\ \hat y+(1-y)log(1-\hat y)) L=−(y log y^+(1−y)log(1−y^)) -
如果对于所有m个训练样本,假设样本之间是独立同分布的(iid),我们希望总的概率越大越好:
m a x ∏ i = 1 m P ( y ( i ) ∣ x ( i ) ) max\ \prod_{i=1}^m\ P(y^{(i)}|x^{(i)}) max i=1∏m P(y(i)∣x(i))
引入 l o g log log函数,加上负号,将上式转化为Cost function:
J ( w , b ) = − 1 m ∑ i = 1 m L ( y ^ ( i ) , y ( i ) ) = − 1 m ∑ i = 1 m y ( i ) l o g y ^ ( i ) + ( 1 − y ( i ) ) l o g ( 1 − y ^ ( i ) ) J(w,b)=-\frac1m\sum_{i=1}^mL(\hat y^{(i)},y^{(i)})=-\frac 1m\sum_{i=1}^my^{(i)}\ log\ \hat y^{(i)}+(1-y^{(i)})log(1-\hat y^{(i)}) J(w,b)=−m1i=1∑mL(y^(i),y(i))=−m1i=1∑my(i) log y^(i)+(1−y(i))log(1−y^(i))
上式中, 1 m \frac1m m1表示对所有 m m m个样本的Cost function求平均,是缩放因子。
本章总结
本节主要介绍了神经网络基础——python和向量化。使用向量化和矩阵运算的方法能够大大提高运行速度,节省时间。以逻辑回归为例,我们将其算法流程包括梯度下降转换为向量化的形式。
-
左上:
logistic 回归主要用于二分类问题,如图中所示,logistic 回归可以求解一张图像是不是猫的问题,其中图像是输入(x),猫(1)或非猫(0)是输出。我们可以将 logistic 回归看成将两组数据点分离的问题,如果仅有线性回归(激活函数为线性),则对于非线性边界的数据点(例如,一组数据点被另一组包围)是无法有效分离的,因此在这里需要用非线性激活函数替换线性激活函数。在这个案例中,我们使用的是 sigmoid 激活函数,它是值域为(0, 1)的平滑函数,可以使神经网络的输出得到连续、归一(概率值)的结果。 -
左下: 神经网络的训练目标是确定最合适的权重 w w w 和偏置项 b b b,那这个过程是怎么样的呢?
这个分类其实就是一个优化问题,优化过程的目的是使预测值 y ^ \hat y y^ 和真实值 y y y 之间的差距最小,形式上可以通过寻找目标函数的最小值来实现。所以我们首先确定目标函数(损失函数、代价函数)的形式,然后用梯度下降逐步更新 w w w、 b b b,当损失函数达到最小值或者足够小时,我们就能获得很好的预测结果。 -
右上:
损失函数值在参数曲面上变化的简图,使用梯度可以找到最快的下降路径,学习率的大小可以决定收敛的速度和最终结果。学习率较大时,初期收敛很快,不易停留在局部极小值,但后期难以收敛到稳定的值;学习率较小时,情况刚好相反。一般而言,我们希望训练初期学习率较大,后期学习率较小,之后会介绍变化学习率的训练方法。 -
右下: 总结整个训练过程,从输入节点 x x x开始,通过前向传播得到预测输出 y ^ \hat y y^,用 y ^ \hat y y^ 和 y y y得到损失函数值,开始执行反向传播,更新 w w w和 b b b,重复迭代该过程,直到收敛。
编程作业总结
-
numpy中的“ ∗ * ∗”运算符表示元素乘法。它不同于“ n p . d o t ( ) np.dot() np.dot()”。
a = np.random.randn(4, 3) # a.shape = (4, 3) b = np.random.randn(3, 2) # b.shape = (3, 2) c = a * b
c . s h a p e c.shape c.shape不能得出结论;如果你尝试“ c = n p . d o t ( a , b ) c=np.dot(a,b) c=np.dot(a,b)”,你会得到 c . s h a p e = ( 4 , 2 ) c.shape=(4,2) c.shape=(4,2)。
-
Python:Shape()函数
- 功能是读取矩阵的长度,比如shape[0]就是读取矩阵第一维度的长度。
- 直接用.shape可以快速读取矩阵的形状,使用shape[0]读取矩阵第一维度的长度
- 功能是读取矩阵的长度,比如shape[0]就是读取矩阵第一维度的长度。
-
Python:reshape的用法
- mat (or array).reshape(c, -1) 必须是矩阵格式或者数组格式,才能使用 .reshape(c, -1) 函数, 表示将此矩阵或者数组重组。
- reshape(m,-1) #改变维度为m行、1列 - reshape(-1,m) #改变维度为1行、m列
− 1 -1 −1的作用就在此$: 自 动 计 算 d \color{red}自动计算d 自动计算d: d = 数 组 或 者 矩 阵 里 面 所 有 的 元 素 个 数 / c d=数组或者矩阵里面所有的元素个数/c d=数组或者矩阵里面所有的元素个数/c, d必须是整数,不然报错)
(reshape(-1, m)即列数固定,行数需要计算)- 把一个矩阵变成另外的形式
- numpy.arange(a,b,c) 从 数字a起, 步长为c, 到b结束,生成array - numpy.arange(a,b,c).reshape(m,n) :将array的维度变为m 行 n列。
- reshape(1,-1)将矩阵转化成1行:
- mat (or array).reshape(c, -1) 必须是矩阵格式或者数组格式,才能使用 .reshape(c, -1) 函数, 表示将此矩阵或者数组重组。
-
python: numpy.zeros()
np.zeros(5)
array([ 0., 0., 0., 0., 0.])np.zeros((5,), dtype=np.int)
array([0, 0, 0, 0, 0])np.zeros((2, 1))
array([[ 0.],
[ 0.]])s = (2,2); np.zeros(s)
array([[ 0., 0.],
[ 0., 0.]])
-
整体思路
The main steps for building a Neural Network are:- Define the model structure (such as number of input features)
- Initialize the model’s parameters
- Loop:
- Calculate current loss (forward propagation)
- Calculate current gradient (backward propagation)
- Update parameters (gradient descent)
You often build 1-3 separately and integrate them into one function we call model().