小C的第一宇宙
wangc
Oct 16, 2017
阅读本文需要 16 分钟(按字数)

笔记学习自网易云课堂-微专业-深度学习工程师

这是个人的学习笔记,限于能力,难免疏忽。如有错误,欢迎留言批评和交流。

上一周简单的介绍了监督学习神经网络深度学习这几个概念,而神经网络的学习是深度学习的基础。这一周将从二分类问题出发,利用logistic回归模型作为模型,梯度下降法作为学习方法,进行监督学习。同时用Python着手进行编程练习。

从一个问题出发

学习一个新理论从实际应用出发是最直观的。这里从一个简单的分类问题来引出logistic回归模型。

二分分类

二分分类是一种只要求得到“是”或“不是”(1 或 0)答案的分类问题。 (关于分类问题…)

比如输入一张照片,判断照片里的物体是否为猫。

图像的输入:上图中照片将输入为3个像素矩阵(RGB),最终整合为一列特征向 (关于图像的存储…

符号约定和基本概念

  • 向量x表示输入的特征向量,如上面的猫图。
  • nnx或:向量x的维度(列向量x的行数),加入猫图的每个像素矩阵为64×64,那么猫图的n将为64×64×64×3。
  • 向量y表示分类标号,在二分类问题里y为0或1。上面的猫图因为就是猫,所以对应y为1了。
  • (x,y)组成一个单独的样本
  • 样本集:神经网络需要输入大量的样本进行学习,如上面的猫图问题,会输入大量的图片和对于这些图片各自的标记(是或不是)。
  • 训练样本:用于训练的样本叫做训练样本。(还有几类样本并不用于训练过程,比如用于测试的样本)。
  • (x(i),y(i))表示第i个样本。
  • m样本的个数,不特别说明的话就是指训练样本的个数。
  • X:训练集中所有的x(i)组成的矩阵。

分类的模型

logistic回归模型是一种基本的分类模型,它的学习会让我们更容易的理解之后的神经网络模型。

Logistic回归

Logistic(逻辑)回归是一个用于二分分类的监督学习算法,之所以用于二分类,因为他的输出只有0或1。其表达式如下:

Logistic回归里面是一个线性函数wx+b。外面的σ()称为Sigmoid函数,它的作用是把函数内的数值转化在了0到1之间。

与其他概念的关系

  • 监督学习:Logistic回归是一种监督学习算法,但一般来说逻辑回归划出的分类线是线性的,对分类线非线性的分类问题表现力不足。(关于监督学习…
  • 回归算法:Logistic回归作为一种回归算法,实际上可以看做是线性回归外面加了一个Sigmoid函数。 (关于回归算法…
  • 神经网络:Logistic回归可以看做一个小型的神经网络。(Sigmoid函数看做为激活函数。)
  • 支持向量机:他也是一种二分类算法,支持向量机算法从某种意义上来说是逻辑回归算法的强化:通过给予逻辑回归算法更严格的优化条件,支持向量机算法可以获得比逻辑回归更好的分类界线。(关于支持向量机…)

关于Sigmoid函数

下面是Sigmoid的函数图像。从图像容易看出,它的值域是在[0,1]区间内的。Logistic回归中,在Sigmoid函数的作用就是把里面的线性函数的输出值转化为了0到1之间的概率。(比如猫图问题中输入一张图片,输出为0.7,可以当做有70%的概论是猫。)

拓展:为什么说逻辑回归的值可以代表概率?

衡量学习效果

有了Logistic回归的计算式,在分类之前,我们还需要确定计算式中的参数(w和b)。对于系数的设定,当然是分类效果越准越好了,那么我们怎么样来衡量不同系数分类相关的好坏呢?这就引入了下面的两个函数。

损失函数

损失函数(Loss\error function)衡量的是分类函数(这里是Logistic回归),在单个训练样本上的表现。

下图为损失函数表达式,其中y hat(y上面一个ʌ)是分类函数得到的预测值,y是实际的分类标号。我们希望让损失函数的值尽可能小。

拓展:一般误差分析不是用平方差吗(a-b)^2,为什么用这个表达式作为误差函数?

成本函数

成本函数(Cost Function)衡量的是分类函数(这里是Logistic回归),在 全体训练样本上的表现。

下图为成本函数表达式,其实就是每一样本损失函数的求和平均。同样的,我们希望寻早一组参数能够让它的值最小。

另外,这里的成本函数也是一个凸函数,所以是存在全局最优点的。如下图是参数是二维情况下的成本函数图像,存在一组w和b可以使函数值最小。 (关于凸函数…)

学习的方法

这时候,我们有了用于分类的模型(Logistic回归),有了衡量模型学习效果的两个函数,我们拿什么方法对分类模型进行“学习”呢?也就是说我们怎么样找到一组最好的w和b值使得我们的成本函数最小呢?这里用到了梯度下降法。(我把机器学习中搜索模型参数的过程理解为“学习”,不知道合适否。)

梯度下降法

梯度下降法(gradient descent)用于帮我们搜素出一组最优化的参数,让成本函数的值最小。梯度下降法所做的就是从初始点开始,朝最“抖”的下坡方向走一步。

下图直观的说明了这个过程。

这里说的寻找最“抖”的方向,用到的就是这一点的导数了。每一次都向斜率最大的方向往下走,能快的找到图中的最低点。

关于初始化

开始搜索最小值之前,我们需要一组初始的w和b。对于逻辑回归而言,通常使用0来进行初始化,虽然随机赋于初始值点也同样有效(因为函数是凸的,所以无论你在那一点初始,都应该到达同一点,或大致相同的点),但我们通常不这么做。

单参数的梯度下降法

这里是一个当参数只有一个w时,梯度下降法迭代的过程。w变化的表达式是这样的:w = w - α*(dJ(w)/dw)

  • 其中dJ(w)/dw是这一点的导数,减去它,即是沿着最抖的地方下降一步(对于二维函数图像,一个点的斜率只有一个),
  • α称为学习率,在梯度下降中它控制着每一次搜索(下降)的步长。
  • w的值会随着迭代不断变化,最终使J(w)的值到最小(全局最优点)。

m个参数的梯度下降法

当m=2时,伪代码如下:

易推广到更多参数的情况,可以多加几个循环,不过不建议再使用for循环,将会用下面的向量化技术来代替。

与其他概念的联系

  • 优化算法:梯度下降法是一种经典的优化算法,用于寻找问题的最优解。比如寻找成本函数的最小值就是一个最优化问题。
  • 数值计算:梯度下降法同样是数字计算学科的经典算法,非常适合来处理求解函数极值的问题。

计算图

到这里其实使用Logistic回归进行监督学习的过程就大致说完了,接下来介绍一种保住我们更好理解数学计算式的工具—计算图。 在各学科研究中,往往有很多可视化图形的方式来帮助我们理解各学科的抽象知识,计算图就是帮助我们理解表达式的计算过程的,后面神经网络的反向传播过程就可以很好的拿计算图来表示和理解。

简单的例子

比如我们把函数J(a,b,c)=3(a+a*b)用计算图表示出来。

当我们输入三个自变量后,第一步就是b*c,然后是a+b*c,最后乘3,顺着箭头计算图把这些步骤呈现了出来。

用计算图求导

我们还可以用计算图对表达式进行求导,比如求dJ/du,dJ/db和dJ/dc。如下图,采取的是从J=3v逆向向前求导,我们把这个过程称作反向传播,其实下图中实际上就是复合函数求导,将链式法则的过程展现了出来。

另一个例子

接下来我们也可以借助计算图来表示和计算逻辑回归中用梯度下降法寻找最优参数w和b的过程,以此作为对计算图和梯度下降法的再次熟悉。

如下图,我们有之前得到的三个表达式,分别是逻辑回归的两个表达式和损失函数的表达式。现在来计算一次梯度下降的过程,这里输入样本只有两个。

用Python编程

关于Python

Python是一门高级语言,因为其易学性和有大量的科学技术和机器学习的包和库,所以广泛应用机器学习领域。 详细

搭建Python的机器学习环境

网上可以搜到很多不同的方式来搭建。这里我是按照课程上来安装的,需要以下几个软件。

Python的安装

首先需要安装Python,廖雪峰 的Python教程

Anaconda的安装

Anaconda是一个用于科学计算的Python发行版,支持 Linux, Mac, Windows系统,提供了包管理与环境管理的功能 anaconda里面集成了很多关于python科学计算的第三方库,主要是安装方便。 安装比较简单,官网可以下载

Jupyter Notebook的安装

Jupyter Notebook(此前被称为 IPython notebook)是一个交互式笔记本,在上面课以编写运行Python代码。下载安装在官网

关于Numpy

Numpy是一个Python的科学计算包。种工具可用来存储和处理大型矩阵,比Python自身的嵌套列表(nested list structure)结构要高效的多(该结构也可以用来表示矩阵(matrix))。据说NumPy将Python相当于变成一种免费的更强大的MatLab系统。

如果上面安装好了Anaconda,那么Numpy也就被一起安装了。

下面是对numpy简单的使用

import numpy as np
a = np.array([1,2,3,4])
print(a)

将会输出:

[1 2 3 4]

详细学习Numpy,见Numpy参考文档

向量化技术

在实际编程中,当你要训练一个监督学习模型时,需要训练大量的样本,这时候可能会用到for循环语句。向量化技术是消除你的代码中显示for循环语句的艺术,向量化是深度学习领域很重要的一个技巧,可以大幅度提高运算速度。 经验法则是,当你编写新网络时,或逻辑回归时,有其他可能(内置函数等),就不要使用显式for循环。

如下面这个例子,分别用向量化方法和for循环方法实现两组向量的点乘(内积),并且计时比较。

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(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("循环方法:" + str(1000*(toc-tic))+"ms")

输出为:

249616.322851
向量化方法:2.0029544830322266ms
249616.322851
循环方法:1541.5911674499512ms

可以看见,在我的笔记本上,向量化方法比for循环方法快了700多倍。其实用过matlab向量化就很熟悉了,就是matlab的矩阵运算吗。

向量化Logistic回归

如下图:

向量化梯度输出

如下图:

Python中的广播

广播是一种让你的Python代码执行更快的手段,可以通过下面的三个例子来直观了解。第一分例子里一列向量和一个实数(一维向量)相加,实数会扩展为列向量。下线两个例子也是如此,当矩阵运算时维数不一致,矩阵(向量)会通过复制的方式进行扩展。

Py/numpy 陷阱

Python/numpy很好用,但是在提供灵活性的同时,其中的你如果不完全熟悉其中的一切规则,就会导致难以察觉的bug

如下例: 用随机正态分布创建两个变量,一个是五个一行的数组,一个是一行五列的矩阵,似乎差不多。

a = np.random.randn(5) 
b = np.random.randn(1,5)
print(a)
print(b)

输出:

[ 0.49956966 -1.30384568  1.01848851  0.64490386 -0.37697139]
[[-2.58280363  0.38830941 -1.38110236 -0.12325656 -0.67463813]]

但是这是两种不同的数据结构,这里的混淆会导致后面的向量运算产生难以察觉的错误。

print(a.shape)
print(b.shape)

输出:

(5,)
(5, 1)

所以一般不要使用a = np.random.randn(5) 形式,并且为了保证矩阵的维度的一至,我们可以经常使用a.reshape(1,5)assert(a.shape = (1,5))函数来确保我们矩阵的维数是正确的。(这两个函数都O(1),开销很小)

作业

Python Basics with Numpy (optional assignment)(刷新后显示)

Logistic Regression with a Neural Network mindset.html(刷新后显示)