吴恩达深度学习 | (2) 神经网络与深度学习专项课程第二周学习笔记

课程视频
第二周PPT汇总

吴恩达深度学习专项课程共分为五个部分,本篇博客将介绍第一部分神经网络和深度学习专项的第二周课程:神经网络基础。由于逻辑回归算法可以看作是一个单神经元(单层)的网络结构,为了使大家能更好的理解神经网络算法,本周的课程从逻辑回归算法出发,通过计算图讲解前向传播和反向传播的过程,导数计算的链式法则以及使用Python进行向量化编程(加快速度,告别显式for循环)的技巧。

目录

一、把逻辑回归看作神经网络

1.二分类

2.logistic回归

3.logistic回归损失函数

4.梯度下降法

5.导数

6.更多导数的例子

7.计算图

8.计算图的导数计算

9.logistic回归中的梯度下降

10.m个样本的梯度下降

二、Python和向量化编程

1.向量化

2.向量化更多例子

3.向量化逻辑回归

4.Python中的广播

5.关于Python/NumPy向量的说明

6.logistic损失函数的解释


一、把逻辑回归看作神经网络

1.二分类

  • 对图像进行2分类

首先引入一个对图像进行2分类的实例,判断一幅图像是否是关于猫的图像:

在计算机中用3个独立的矩阵来存储一张彩色图,分别对应红、绿、蓝三个颜色通道。每个矩阵和图像同样大,比如若图像是64*64像素,那么每个矩阵的维度就是64*64的。矩阵中的值代表对应像素点该颜色通道上的亮度值(0-255)。如下图所示:

输入的特征向量x是矩阵中亮度值的堆叠:

就上例而言,特征向量x的维度n_{x}=3*64*64=12288.

输出标签y={0,1} (2分类,1代表是猫,0代表不是)

  • 模型符号标记

(x,y)代表某一个样本,x \in R^{n_{x}},y\in {0,1}

训练集:{{(x^{(1)},y^{(1)}),(x^{(2)},y^{(2)}),...,(x^{(m)},y^{(m)})}},m代表训练集的大小,一般为了和验证集、测试集做区分,训练集的大小一般记做m_{train},测试集的大小记做m_{test}.

训练过程很自然的想法是对每个训练样本通过一个for循环进行遍历,其实不然。鉴于循环的速度问题,使用Python编程时都会使用向量化编程,提高速度。

用特征矩阵X包含每一个样本的特征向量,其中X每一列代表一个样本的特征向量:

当然也可以进行转置,用每一行代表一个样本的特征向量,但是不推荐这样做,因为上图中的表示方法是约定好的。可以使用Python语句,X.shape查看矩阵X的大小,返回(n_{x},m).

用标签矩阵Y包含每一个样本的标签:

可以使用Python语句,Y.shape查看矩阵Y的大小,返回(1,m).

2.logistic回归

在之前机器学习的课程中,曾详细的介绍过逻辑回归算法,不熟悉的朋友可以再去回顾一下:logistic回归

本小节讲述的逻辑回归算法和之前学习的原理是一样的,只是一些符号的使用稍有差别,今后在深度学习专项课中统一使用一下的符号:

  • 图像分类

使用逻辑回归算法对图像进行2分类,可以表述为以下形式:

其中,算法输入x \in R^{n_{x}},是图像的特征向量;算法输出\hat{y}是预测输入属于正类的概率,它是由对输入特征进行线性组合再通过sigmoid函数得到的。

之所以使用sigmoid函数,是因为输入特征的线性组合很有可能比1大或比0小,这都是没有意义的,我们需要的是概率,通过sigmoid把这个组合值映射到0-1之间,sigmoid函数图像及表达式如下:

sigmoid函数的性质:

算法参数包括两部分权重参数w \in R^{n_{x}},偏置参数b \in R,和之前稍有不同,我们之前都是用一个统一的参数向量\Theta代表所有参数,并为输入特征向量增加一个特征x_{0}=1:

今后将不再使用这种形式,统一使用分离的形式。

3.logistic回归损失函数

为了训练得到一组最优的参数w和b,我们需要定义一个代价函数:

算法的输出如下:

其中x^{(i)}代表第i个训练样本的特征向量。

我们希望算法的输出和真实标签越接近越好:

  • 损失(误差)函数

用于描述单个训练样本,算法输出和真实标签的误差:

首先,可能会想到均方误差,但是在logistic回归中我们使用的是第2种损失函数,简单来说是因为,均方误差定义的损失函数是一个非凸函数,存在很多局部最优值,利用梯度下降法很难得到全局最优值;而第二种形式的损失函数是一个凸函数。

  • 代价函数

用于描述整个训练集所有样本损失函数的均值:

定义好代价函数后,就可以通过梯度下降法或高级优化方法最小化代价函数,求得一组最优的参数w和b。

4.梯度下降法

通过梯度下降法最小化代价函数,求得一组最优的参数w和b。

假设w和b都是一维实数,可视化代价函数,直观的理解梯度下降法。之前提到过逻辑回归损失函数之所以定义为第2种形式,是因为他是凸函数,不存在很多局部最优值,只有一个全局最优值,可视化代价函数后会呈现出一个“大碗”的形状:

使用梯度下降法时,初始代价函数值很大,如上图中顶端的红点,通过不断的迭代过程,代价函数的值不断减小,直到接近或达到全局最优值。

进一步进行简化,假设代价函数只有一个参数w,并且w是一个一维实数,可视化代价函数图像如下:

梯度下降的迭代过程不断重复{}中的运算,来最小化代价函数,更新模型参数,最后得到一个最优的参数值。具体步骤是让参数w每次减去代价函数对w的导数值与学习率\alpha的乘积。

反应在图像就是,当代价值位于右侧时,导数值为正,w会在每次迭代后减小,从而逐步接近代价函数的最小值;当代价值位于左侧时,导数值为负,w会在每次迭代后增大,从而逐步接近代价函数的最小值。

代价函数对w的导数值,就是在该点的斜率,它(的反方向)是梯度下降最快的方向。

实际上对于上述逻辑回归问题,梯度下降法的迭代内部会包含两部分,对权重参数w的更新和对偏置参数b的更新:

当代价函数有多个参数时,应该使用偏导数。

5.导数

本小节旨在让你对微积分和导数有一个直观的理解。

考虑下面这个函数,图像如下:

在上图中,当a=2时,f(a)=6;将a增大0.001,即a=2.001时,f(a)=6.003。此时这两点将在上图中构成一个绿色的小三角形,该直线f(a)在a=2处的斜率就等于三角形的高度除以宽度,即0.003/0.001=3,记做:

对于该直线而言,斜率是不变的恒为3。

导数在数学上的定义可以理解为,当a增加一个无限小的量时,f(a)的增量相对a增量的倍数。

6.更多导数的例子

f(a+0.001)\approx f(a)+(0.001)*(\frac{\mathrm{df(a)} }{\mathrm{d} a})

\frac{\mathrm{d} f(a)}{\mathrm{d} a}\approx \frac{f(a+\Delta a)-f(a)}{\Delta a},   \frac{\mathrm{d} f(a)}{\mathrm{d} a} =\lim_{\Delta a->0}\frac{f(a+\Delta a)-f(a)}{\Delta a}

函数的导数就是函数的斜率,函数在不同点的导数一般是不同的。

7.计算图

神经网络的计算包括前向传播得到输出和反向传播计算梯度更新参数两部分。

  • 用计算图来表示简单前向传播

假设代价函数:J(a,b,c)=3(a+bc),记u=bc,v=a+u,J=3v,用计算图来表示代价函数的计算过程:

假设a=5,b=3,c=2,利用计算图得到的结果和利用代价函数得到的是一样的。3*(5+2*3)=33.

8.计算图的导数计算

  • 用计算图来计算简单的导数

计算\frac{\partial finalOutput}{\partial var}, finalOutput指的是代价函数J,var指的是中间变量,如上例中的a,b,c,u,v. 在Python中一般把\frac{\partial finalOutput}{\partial var}记做d var:

9.logistic回归中的梯度下降

接下来我们将用计算图的形式描述logistic回归中的梯度下降,虽然这有些大材小用了,但是便于理解后续神经网络中的梯度下降。

  • 逻辑回归中的公式

考虑单个样本的损失函数,假设输入特征向量x只有两个特征x_1,x_2:

logistic回归的计算图:

梯度下降法需要做的是:最小化单个样本的损失函数,求得一组最优的参数w(w_{1},w_{2}),b:

10.m个样本的梯度下降

考虑m个样本的代价函数:

用for循环求解m个样本的梯度:

上述做法包含两个for循环,速度非常慢,深度学习中应避免显示使用for循环,而是采用向量化编程的方法,来加快运算速度。

 

二、Python和向量化编程

1.向量化

向量化指的是消除代码中显式for循环,加快运行速度的技巧。

例如:z=w^{T}x+b,w \in R^{n_{x}},x \in R^{n_{x}}

  • 非向量化实现
z=0
for i in range(nx):
    z+=w[i]*x[i]
z+=b
  • 向量化实现
z=np.dot(w,x)+b
  • 比较上述两种方式的运行时间:
import numpy as np
import time
a=np.random.rand(1000000)
b=np.random.rand(1000000)

tic=time.time()
c=np.dot(a,b)
toc=time.time()
print(c)
print('向量化:'+str((toc-tic)*1000)+'ms')

c=0
tic=time.time()
for i in range(len(a)):
    c+=a[i]*b[i]
toc=time.time()
print(c)
print('非向量化:'+str((toc-tic)*1000)+'ms')

由此可见,向量化实现版本比非向量化版本快了差不多几百倍。

Numpy中内置的函数,不仅可以去掉显式for循环,还可以充分实现并行化。不论是基于CPU还是GPU都可以实现并行化,GPU更加擅长并行化。

2.向量化更多例子

编写神经网络时,应尽可能的避免使用显示的for循环,采用向量化实现。

  • 示例1

u=Av,u_{i}=\sum _{j}A_{ij}v_{j}

非向量化:

u=np.zeros(m)
for i in range(m):
    for j in range(n):
        u[i]+=A[i][j]*v[j]

向量化:

u=np.dot(A,v)
  • 示例2

非向量化:

u=np.zeros((n,1)) #初始化2维数组
for i in range(n):
    u[i]=math.exp(v[i])

向量化:

u=np.exp(v)

类似地:

np.log(v)
np.maximum(v)
np.abs(v)
v**2

3.向量化逻辑回归

  • 前向传播过程

                        z^{(i)}=w^{T}x^{(i)}+b,a^{(i)}=\sigma (z^{(i)}) ,i=1,...,m

用矩阵X(n_{x}*m)表示m个样本的输入特征向量:

向量化前向传播:

Z=np.dot(w.T,X)+b
A=sigmoid(Z)

其中,Z=[z^{(1)},...,z^{(m)}],A=[a^{(1)},...,a^{(m)}]

  • 反向传播过程

dz^{(i)}=a^{(i)}-y^{(i)},i=1,...,m      Y=[y^{(1)},...,y^{(m)}],A=[a^{(1)},...,a^{(m)}]

dZ=A-Y #向量化

其中,dZ=[dz^{(1)},...,dz^{(m)}]

 

dw_{j}+=x_{j}^{(i)}*dz^{(i)} ;i=1,..,m;j=1,...,n_{x} ;dw_{j}/=m

dW=np.dot(X,dZ.T)/m

db+=dz^{(i)};i=1,...,m; db/=m

db=np.mean(dZ)
  • 逻辑回归完整过程

非向量化:

向量化:

for iter in range(1000):  #1000次迭代
    Z=np.dot(w.T,X)+b
    A=sigmoid(Z)
    dZ=A-Y
    dW=np.dot(X,dZ.T)/m
    db=np.mean(dZ)
    w=w-alpha*dW
    b=b-alpha*db

4.Python中的广播

  • 示例1

不使用for循环的前提下,计算出每种食物的卡路里占比:

import numpy as np

A=np.array([[56,0,4.4,68],
            [1.2,104,52,8],
            [1.8,135,99,0.9]]
cal=A.sum(axis=0)  #对每一列求和 求每一种食物的总卡路里
print(cal)
per=100*A/cal   #每一列的元素除以每一列的和 得到每一种食物的卡路里占比
print(per)

在上述例子中,A:3*4矩阵,cal:1*4的矩阵,二者相除时,cal通过广播扩展成3*4的矩阵,再进行运算,相当于又复制了两行。

  • 更多广播的例子

当一个向量与一个标量运算时,相当于向量中的每个元素都进行这个运算:

当一个m*n的矩阵和一个1*n的向量运算时,1*n的向量先广播为m*n的矩阵再运算:

当一个m*n的矩阵和一个m*1的向量运算时,m*1的向量先广播为m*n的矩阵再运算:

  • 广播的一般规则

5.关于Python/NumPy向量的说明

大家在使用Python中的向量时可能会出现一些莫名其妙的错误,本小节将详细讲解NumPy中的向量。

实际上,大家在使用NumPy向量时,习惯把它声明为一维数组,其实这可能会带来麻烦:

a=np.random.randn(4) #声明一维数组   服从高斯分布的随机数
print(a)
print(a.shape)  #(4,)代表是一个包含4个元素的一维数组  
#其实严格讲 它并不是向量
b=a.T   #转置对其无效
print(b)
print(b.shape)
print(np.dot(a,a.T)) # 会得到一个具体的数值 而不是一个矩阵

推荐的做法是把NumPy向量声明为2维数组,即(m,1)或(1,m)的矩阵:

a=np.random.randn(4,1)  #以2维数组的形式   声明一个列向量 
print(a)
print(a.shape)
b=a.T    #此时转置是有效的  b是行向量
print(b)
print(b.shape)

c=np.random.randn(1,4)  #以2维数组的形式   声明一个行向量 
print(np.dot(a,c))  #得到一个矩阵

6.logistic损失函数的解释

  • 单个样本的损失函数

logistic算法的输出值\hat{y}:

\hat{y}代表的是,在给定输入x的情况下,y=1的概率:

因此,当样本标签y=1时,P(y|x)=\hat{y};当样本标签y=0时,P(y|x)=1-\hat{y}

接下来,把上述这个分段的公式合并为一个公式:

可以把y=0,y=1分别代入,验证二者是等价的。

由于log函数(ln)是严格单调递增的,所以最大化P(y|x)相当于最大化logP(y|x):

我们一般使用优化方法求解的都是最小值,所以取得是-log。单个样本的损失函数L(\hat{y},y)由此定义。

  • m个样本的代价函数

假设训练集中所有的样本独立同分布,因此这些样本的联合概率就是每个样本概率的乘积:

做最大似然估计,求得一组参数,使给定样本的观测值概率最大,即使上式最大。对这个概率最大化,相当于对这个概率取对数后最大化:

由此可以得到m个样本的代价函数J(w,b):

注意,使用优化方法时一般都是最小化代价函数,所以去掉了前面的负号;此外方便起见,可以对代价函数进行适当缩放,前面乘常数因子(1/m)。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值