赞
踩
吴恩达机器学习课程笔记记录
(已完结 2024.2.1)
课程资料https://github.com/kaieye/2022-Machine-Learning-Specialization
• 定义(非显式编程)
学习内容包括
• 监督学习 course 1,2
• 非监督学习 course 3
• 强化学习(了解)
• 实操学习算法
第一课 监督学习
Week1
监督学习
算法输入,训练对应输出 x to y 的映射
• 回归(regression):从大量可能的输出中预测数字
• 分类(classification):输出的结果(数字或非数字)被分为几种可能的类别,预测的结果是小的预设集合
非监督学习(无监督学习)
在未标记的数据集中找到一些有趣的特性,例如聚类算法(clustering)。
无监督学习并不会给出所谓的“正解”(只有输入x但没有输出标签y),由算法自动找出特性。在谷歌新闻,基因分类中有所应用。
• 聚类(group similar data together)
• 异常检测(发现异常数据点)
• 降维(dimensionality reduction)压缩数据,同时丢失尽可能少的信息
Jupyter notebook
线性回归模型 linear regression
输入特征x,输出目标y,训练集
y_hat是y(真实值)的估计值,f是函数(模型),x -->f to y即特征通过模型进行预测。
只有一个变量的线性回归叫做单变量线性回归。
代价函数(成本函数)cost function
f(X)=wX+b
w,b成为参数,或系数,或权重
成本函数的自然导出:
J(w,b)通过计算方差的方式来构造成本函数,使得可以找到最接近于真实值的f参数。
除以2是为了简化计算,m是(x,y)训练样例总量
差值平方成本函数是常用的线性回归成本函数。
数学上,寻找最佳直线的过程就是minimize J的过程。
如果同时考虑w和b,代价函数会出现汤碗状(二元函数J)
等高线图形式:
梯度下降算法
概念
用于最小化w,b的成本函数,自动处理线性回归问题,是一种更系统的方法
事实上是一种可用于尝试最小化任何函数的算法,甚至是具有两个以上参数的其它成本函数:J(w1…wn,b)
• 不断改变w,b的值(初始设置为0,0),来减小J,直到J被设置为或者接近于一个最小值。
• 最小值可能有多个(也就是极小值,局部最小值)
• 沿着梯度下降的方向(方向导数最大值)减小J。
实现梯度下降算法
• 更新w值,α是学习率,决定了每次下降的步幅。
• derivative→导数
• 同时更新w,b是正确的方法
• 学习率过大或过小的影响
• 随着导数值的减小,即使学习率是固定的,更新的值也会变小。
用于线性回归的梯度下降
• 计算成本函数关于w和b的偏导数。
此时成本函数是凸函数,只要学习率合适,J总是会收敛到全局最小值,(碗形底部)。
运行效果:
使用批量batch梯度下降进行线性回归,指的是每次都使用所有的训练样例。
Week2
多维特征
• 注意符号的含义:下标数字表示的是不同的特征;上标带括号的数字表示的是每个特征的第i个训练样例,且箭头是可选的指示符,用于强调这是一个包含1…4个特征样例的向量。如果上下标同时出现,就表示是Xj特征的第i个样例。
多元线性回归:
写成向量点积形式,不同特征乘不同的w相加
• 不是多元回归,是多元线性回归。前者是其它的概念。
向量化
有助于实施学习算法时缩短代码提高效率。
• 使用了Numpy——一个Python和机器学习中使用最广泛的数值线性代数库
• 使用向量化的方法(dot)能够在计算机中并行使用硬件,无论是普通计算机还是CPU,GPU,通常都能加速机器学习作业,且代码更短。
• 普通方法是一个一个相加,而矢量化方法有专门的硬件并行完成。
• 在梯度下降算法中:
用于多元线性回归的梯度下降法
向量化方法表示:
将J的偏导数具体表示:
另外:正规方程法(处理线性回归问题的另一种方法)
• 不能够泛化解决其他学习算法
• 特征数量大时慢
• 一些线性回归库使用,机器学习从业者一般不会自己实现
推荐的方法是梯度下降法
特征缩放
让梯度下降更快
• 特征数值较小时,其对应参数w往往较大;而特征数值较大时,其对应参数往往较小;这样其预测结果更接近真实值。
• 因此,特征之间的散点分布和成本函数等高线图可能会出现如下的情况:
(此时成本函数等高线图没有考虑b,考虑的是向量w的两个特征w1和w2)
• 为了便于梯度下降法快速找到J的最小情况,应该对特征进行缩放,让各个特征数值之间具有良好的比较能力,效果如下:
• 实现方式:
除以最大值,限定范围0-1
均值归一化(均值标准化)
算出特征均值,然后特征-均值μ,除以范围差值
Z-score归一化(Z分数标准化)
算出x1和x2的标准差(正态分布,高斯曲线)
何时进行特征缩放?
目标是让特征数值在-1到1之间,可接受的范围同样也包括-3到3,-0.3到0.3。对于相差过大的情况,为了让梯度下降运行得更快,鼓励进行特征缩放,这几乎没有坏处。
判断梯度下降是否收敛
• 绘制并查看学习曲线
良好的梯度下降学习曲线的J应该随着迭代次数的增加而下降,最终趋于平稳。
横轴是迭代次数,纵轴是成本函数值。不同的迭代方式可能需要不同的迭代次数。通过观察一定迭代次数之后的J值是否趋于平稳,判断其是否已经收敛。
• 采用自动收敛测试
如果J的变化小于一个小量ε,则宣布J到达全局最小值,梯度下降收敛。实际应用中,定义ε的大小是不容易的,因此更倾向于观察学习曲线。
选择适当的学习率
如果学习曲线并不随着迭代次数下降,甚至随着迭代次数上升,即J不收敛,这往往意味着:
• 学习率α过大
• 代码中存在错误
前者会导致每次的参数调整过于剧烈,以至于无法发现J的全局最小值
后者常见的是将调整的➖写成➕,这与梯度下降思想背道而驰。
在实际的代码调试中,判断J是否收敛的一个小技巧是将学习率α设置为一个很小的值,用来判断究竟是学习率过大导致的不收敛,还是由于代码的错误导致的不收敛。
需要强调的是,这只是一个调试的技巧,过小的学习率虽然可以让J收敛,但是却不是一个合理的学习率,会让梯度下降运行得非常慢,需要更多次数的迭代。
尝试选择学习率α的值
例如,从0.001开始,每次更新乘10倍或者3倍,观察对应的学习曲线。
最终选择的学习率应对应一个良好的学习曲线,尝试选择一个最大可能的学习率,或者比其略小一些。
特征工程
例如,定义一个新特征来获得更好的模型:
特征工程可以帮助拟合直线之外的曲线
多项式回归
选择不同的特征,获得不同的模型,例如(根号x,x²,x三次方)。
• 在这种情况下,对应于x的多项式,不同的特征往往具有相差很大的数据范围,此时特征缩放变得尤其重要。
选择什么特征?将会在第二课介绍。
• Scikit-learn工具的使用,在可选实验室中
Week3
学习分类(y不是任意数字输出了)。事实证明,线性回归是不适合分类的,要学习逻辑回归。
• y只有两种输出的叫二元分类(yes/no 0/1 true/false 正例/反例)
线性回归可能会得到糟糕的预测结果,决策边界发生移动。
逻辑回归虽然叫回归,但这是历史原因导致的,实际上用于解决分类问题。
逻辑回归
Sigmoid函数(逻辑函数)
• 逻辑回归得到的曲线应该呈现如图状态(最终取值还是0/1)
• 逻辑回归的输出始终介于0-1之间,这是Sigmoid函数的值域决定的
• Sigmoid函数用于构造曲线,表达式如图
• 如何使用Sigmoid函数:
• 逻辑回归输出的解释:输出的y=1的可能性,因此也有概率P的写法
决策边界
设置一个预测的阈值,fx或gz大于等于该阈值时y预测=1。小于时=0,例如,设定阈值为0.5
• 决策边界示例1:
令z=0,得出的两个特征的临界值(构成一条直线)就是此时的决策边界。
• 决策边界示例2(逻辑回归的多项式回归):
• 更复杂的决策边界(更复杂的多项式)
逻辑回归的成本函数(代价函数)
成本函数提供衡量一组特定参数与训练数据拟合程度的方法,提供了获得更好的参数的办法。
平方误差成本函数并不是逻辑回归的理想成本函数。
训练集示例:
如果采用平方误差成本函数的话,成本函数曲线区别于线性回归,在逻辑回归的情况下并不是凸函数,因此可能会有很多局部最小值,无法保证梯度下降收敛至全局最小值。
单个训练示例的损失函数 & 逻辑回归的成本函数
将之前的成本函数求和内部部分(含1/2)单独定义为逻辑回归的损失函数,表示一个单独示例中fx和y的差距表现。
在平方误差成本函数的情况下,损失函数是1/2(fx-y)²
当我们为损失函数选择不同的形式,那么由损失函数求和形成的成本函数将可以成为一个具有全局最小值的凸函数。(为什么是凸的是一个数学问题,实际上就是求二阶导,这里就不展开了)
用于逻辑回归的损失函数(由此求和得到了新的成本函数)注意点:
• 根据不同的真实标签y具有不同的表达式形式
• fx(由Sigmoid函数定义的)
fx始终介于0-1之间,因此对数函数的曲线有效部分也相应确定。
真实值为1时的逻辑损失函数
fx在0-1之间,因此图像是对应的对数函数一个(0,+∞)的递减部分。
此时预测值fx和真实值y=1定义的损失函数形式-log(fx)表达了一个事实关系,即:
• fx很接近1时,损失值很小,这表明预测的结果更为合理;
• 当预测值fx更接近0,和真实值y=1并不接近时,损失函数的值会较大,甚至趋于+∞,这表明预测的结果并不合理。
可以看出,损失函数激励了算法作出更准确的预测,当真实值y=1时,损失函数在预测值fx接近1时损失最低。
真实值为0时的逻辑损失函数
相较于真实值为1,仅仅作出了表达式的变化,其思想内涵完全一致。
同样的,预测值fx距离目标y越远,损失值越高。
当模型作出了非常肯定的预测,例如99.99%的概率,但却与目标值不一致,此时将会被惩罚而得到一个非常大的损失。因此,假设当真实标签为1时,算法被强激励为不要预测太接近0的结果。
• 所有单个训练示例的损失函数定义了整个训练集的成本函数,成本函数总结了所有训练样例的损失,衡量了整个训练集的表现。
• 单个训练示例的损失函数表示了预测值与实际值的差距
接下去仍然是找到一组w,b时这个新的逻辑回归成本函数最小化。
简化逻辑回归成本函数
为了更简洁地实现梯度下降,对逻辑回归的成本函数进行简化。
(将函数的两个表达式写成一个等价的表达式)
用简化的损失函数构造成本函数:
另外,为什么我们会在如此多的可能成本函数中选择了这种形式作为逻辑回归的成本函数?这实际上是用最大似然估计的统计原理推导的,它通过寻找最大化样本观测值的概率(似然函数)的参数值来估计参数值。而且这个成本函数具有凸的优良特性。
在逻辑回归中实现梯度下降
根据学习率和对应导数值的乘积重复同步更新w,b
右边的导数形式需要注意。带入就可以得到逻辑回归的梯度下降表达式:
尽管成本函数的导数形式导致w,b的更新方式非常相似,但是逻辑回归与线性回归的函数fx是不一致的。
同样可以判断收敛,向量化实现,特征缩放。
(可选实验室里有逻辑回归梯度下降的可视化,以及scikit-learn的用法)
欠拟合/过拟合问题
我们学习的算法可能会遇到过拟合的问题,与过拟合相反的情况成为欠拟合。我们将学习过拟合的概念以及解决过拟合的正则化方法。
如下的三个房价的例子,分别对应欠拟合,正好,过拟合:
• 欠拟合的数据显然和训练数据非常不符合,因此具有”高偏差“(High Bias)。高偏差和欠拟合是等价的表达。高偏差在这里指的是该算法没有清晰地捕获到训练数据的一个清晰的模式,没有很好地拟合训练数据。或者说存在类似于算法先入为主地认为数据是线性的这样的情况而导致拟合不足。特征太少可能会更容易导致欠拟合问题。
• 正好的拟合说明学习算法能够很好地泛化数据,推广至其它例子。
• 如果采用四次多项式来拟合房价数据,会出现过拟合的情况,此时预测的价格能够完美地符合训练数据,甚至使得成本函数为0。然而这并不代表学习算法是好的算法,因为这样的算法具有”高方差“——由于它实在是太符合训练数据了,因此对于不同的训练数据,训练的结果将会大不相同,并不能很好地泛化数据,这说明了算法出现了过拟合问题。过拟合和高方差也是等价的表达。
机器学习的目标是找到一个既不是过拟合也不是欠拟合的模型,也就是一个既不是高偏差也不是高方差的模型。
过拟合同样会出现于分类问题:
在z中具有非常多的高阶项时,其决策边界(Sigmoid函数的z=0对应的曲线)可能会呈现出最右边图的复杂形状,此时出现了过拟合。
解决过拟合
后续我们会讨论发现过拟合的手段,在此我们先讨论如何解决过拟合问题。
• 首先是可以收集更多的训练数据
这会迫使模型去对抗过拟合
• 其次是选择包括更少的特征
如果包括的特征非常多而对应的训练数据又很少的时候,模型很可能过拟合。
这涉及特征选择的问题,因为在这种情况下不得不丢失一些和结果预测有关的信息。后续会介绍算法来选择一个最小的特征集合。
• 第三是正则化(即将具体介绍)
在实际的过拟合例子中,各个特征的参数往往都比较大,这是过拟合的一个特点,尝试减小这些参数往往会让结果更容易避免过拟合的情况。上述特征选择以减少特征的方法实际上对应将特征前对应的参数设置为0。与之不同,正则化可以保留所有的特征,转而只是防止特征产生过大的影响而产生过拟合。
在实际应用正则化时,通常只对w1…wn(wj)作变小的操作,对于b没有要求。一般而言减小b也不会有什么效果。
可选实验室介绍了过拟合的解决方法示例,建议看看。
正则化
上面已经提到了正则化的基本思想就是减小一些参数值的大小,减弱其对应特征的影响,这比直接删除某个特征更加温和。它的做法可以简单表述为:
我们不再最小化原先的成本函数,而是现在成本函数后加上若干个很大的数乘上对应希望减小的几个参数的平方。之后再进行最小化。因为很大的数和参数的乘积会阻碍成本函数的最小化,因此这些参数都会被最小化方法改变得很小,这达到了我们的目的。
通常而言,我们并不知道应该惩罚哪一个特征使其参数变小,因此我们会惩罚所有的特征。
确切的做法是这样的:
寻找一个正则化参数λ,用如下的式子改变成本函数(即加上一系列参数平方与λ的乘积)
需要注意:
• λ的值也按2m进行缩放,这种正则化方式有助于我们确定λ的值,同时在训练集规模m增长时让之前确定的λ仍然具有可行性。
• 实际中,是否惩罚b对结果没有太大作用。本课程不考虑b。
因此,需要最小化的成本函数变成了均方误差成本加上额外的正则化项,前者能够鼓励模型更好地拟合数据,后者则鼓励模型尽量避免过拟合。
其中,正则化参数λ的作用就是平衡这两部分的关系:
在λ较小时,模型会更加重视拟合数据的部分,即将平方误差最小化,可能导致过拟合,如λ=0时;
在λ非常大时,模型会更重视避免过拟合的问题,可能会导致欠拟合。例如λ趋向于∞时,所有的参数wj都会接近于0,此时整个模型只剩下b的一条直线(b没有参与正则化惩罚),这是失败的拟合结果。
因此选择一个既不偏大也不偏小的λ是非常重要的。在后续的学习中,会介绍具体的确定λ值的方法。
用于线性回归的正则方法
如何让正则线性回归和梯度下降一起工作。
事实上梯度下降的方法并没有本质的变化,只是因为我们修改的成本函数J的内容,因此每次更新wj的方法因J的导数发生了改变。课程中关于介绍导数的计算部分可以忽略,因为我们学过高数(不得不感慨这门课程的人性化程度)。另外,我们不考虑b的正则化,因此b的更新方式不变:
同步更新依然必不可少。
内部的数学机理和方法本身的对应:
更新wj的行为在数学上可以移项成为先将wj乘上一个略小于1的数然后进行通常的梯度下降更新,这也解释了为何正则化在梯度下降法中缩小参数的值。
用于逻辑回归的正则方法
由于二者的梯度更新惊人地相似,因此正则化逻辑回归的更新与正则化线性回归的更新非常相似。
逻辑回归的成本函数我们之前进行了简化,现在我们在原本的成本函数后加上正则化所需的正则化项:
正则化项保证了我们的参数尽可能减小。这可以让我们的分类结果更有效地防止过拟合,来更好地泛化模型。
由于逻辑回归的成本函数关于wj和b的导数和线性回归的成本函数关于wj和b的导数惊人地一致,因此他们的更新公式几乎完全相同,唯一的区别在于逻辑回归的fx是Sigmoid函数gz复合线性和z=wjx+b的形式
实验室里增加了正则化代码。
李飞飞访谈
”我希望我们能用简洁而美丽的公式来描述AI,我们现在正处于‘Pre Newton‘的阶段,处于人工智能激动人心的阶段,就像牛顿不会梦到爱因斯坦。“——李飞飞
Caltech 101数据集
AI changes the world. Who changes AI?
第二课 神经网络(深度学习算法),决策树
课程安排
• week1 week2 神经网络:包括推理预测 训练
• week3 构建机器学习系统的实质性建议
• week4 决策树
Week1
神经元和大脑
神经网络最初用于模拟人脑(生物大脑)
尽管今天的神经网络几乎与大脑的学习方式无关,但是早期的动机是尝试构建软件来模仿大脑。
从生物大脑的神经系统简化出的神经数学模型:
这是一个松散的类比,因为目前人脑的运作机理几乎也没有被揭示,每隔几年就会有新的突破。因此用我们目前对于大脑的理解来构建一个原始的智能很可能是不合理的。
但是这些极其简化的神经元模型可以帮助我们构建非常强大的深度学习算法。
因此生物学动机不是重点,目前也不能成为重点,从事深度学习研究的人并不关注生物学动机,而只是用工程原理构建更有效的算法。
为什么深度学习算法在近几年内起飞了?
传统的机器学习算法(线性回归,逻辑回归)性能有限,构建更大的神经网络有助于提升性能表现,再加上大数据的支持。同时,更快的计算机处理器和GPU的发展也是重要原因。
神经网络举例:需求预测
一个简单的需求预测示例
预测什么样的价格会让一件衬衫成为畅销商品:
用逻辑回归计算出了预测值fx,这是衬衫畅销的概率,最终被分类为0/1(不畅销/畅销)。此时将这个概率记为a,它实际上是叫做激活(activation),这是神经元的概念,指输入价格后神经元输出的衬衫成为畅销商品的概率。我们简化神经元的概念在这里体现为将神经元的功能简化为一个逻辑回归的输入和输出,实际的神经元功能要复杂得多。将这些简化的神经元串在一起就构成了神经网络。
更复杂的需求预测示例
如果影响商品成为畅销品的因素包含四个不同的特征:
• 先由这四个特征用逻辑回归得到商品的”可负担性“,”顾客认知“,和”感知质量“,然后再从这三者中得到最终成为畅销商品的概率
• 左边三个神经元构成一个”层“,层是一个或若干个神经元或特征的组成。神经元的输出叫做激活。所有的特征组成了输入层,中间的神经元组成的层得到输入层的输入输出各个激活值。在图中,右边粉色的神经元得到了上一层的三个对应于商品三种特点的输入用逻辑回归输出一个激活值——畅销的概率,因此这个神经元自己单独构成了输出层。中间层被称为隐藏层。
实际情况下,设置某一层中的每个神经元输入哪些特征是比较繁琐的,因此不妨将所有特征输入,用参数设置使各神经元只关注对自己影响最大的特征集合:
为了简化表示,可以将所有输入特征写成一个向量,同理也可以将每一层的激活值写成向量的形式送往下一层:
由此我们将一个四个数字特征的向量(输入层)喂给隐藏层,得到一个由三个激活值构成的激活向量,再将其喂给输出层,得到最终的激活值——畅销概率。
中间层被称为隐藏层的原因:在数据集的输入输出(vector(x), y)中,数据集往往隐藏中间的激活值,因此你不知道正确的中间激活值,它们是隐藏的。
一个需要注意的特点:我们之前会通过手动特征工程来提高预测的可靠性,例如我们在房价预测中,在地皮地块长宽x1,x2以外构建了草坪面积=x1*x2这一特征,帮助我们更好地进行预测;虽然我们在这里人工指明了隐藏层的内容,但实际上神经网络则不需要我们手动设计特征,它可以学习,即自己计算出隐藏层中应该使用什么样的特征。
多个隐藏层的示例
这种多层的架构很多时候被称为“多层感知器”,指的就是这种情况。
决定有几个隐藏层以及每个隐藏层有几个神经元是神经网络的架构问题,这会对算法的性能产生影响。后续会学习选择神经网络合适架构的技巧。
神经网络举例:图像感知
例如一个1000px*1000px的图像(px=pixel 像素)
每个像素格是一个从0-255的数字,表示像素强度或亮度,整张图片可以按行排列或展开成一个包含1000000(1million)的列表,我们的任务是构建一个神经网络来从这些数字中输出人的身份信息。
神经网络中包含多个隐藏层。如果我们试图观察每一层中的各个神经元的行为,来猜测它实际上在计算什么,我们就会发现:
• 第一层的第一个神经元似乎是在图片中寻找垂直的短线,第二个神经元则是寻找其它朝向的短线,各个神经元都在寻找一些短线或者偶尔是小方块这样的部分。
• 第二层的神经元们则开始抓住一些可能是“眼睛”,“鼻子”这样大块的面容特征的部分。
• 第三层的神经元已经开始获得不同粗糙程度的面容,它们输出的一系列丰富的激活值能够让输出层输出一个图像人员身份信息的概率。
我们能够注意到每一层的激活值在逐渐变得更加高级。
同样地,对于车的图像识别,这个神经网络的工作细节也十分相似:
不同层的神经元开始从边缘细节,到车辆零件,到整体车辆得到越来越高级的激活值。这些特征都是由神经网络自己学习的。
接下去我们将学习神经网络的具体实现细节与方式。
神经网络中的网络层
如何构建一个神经元层以组合一个大型神经网络。
以需求预测为例
我们将输入层记作第0层layer0,之后是第一层和第二层——layer1,layer2
对于layer1:
layer0的向量x输入layer1的每一个神经元。每一个神经元都是一个简单的逻辑回归gz,g是逻辑函数Sigmoid,z是wx+b。每个神经元对应一个激活值,这些激活值一起构成了激活向量vector(a),作为layer2的输入。
其中参数和激活向量带方括号[i]的上标表示第i层layer i,而参数下标表示这是第当前层几个神经元的参数。
对于第二层:
输入是layer1的激活向量vector(a)[1],第二层只包含一个神经元,进行一个简单的逻辑回归,此时的z是wa+b,a是上一层的输入。
对应参数和输出激活向量右上角添加上标[2]用于指明这是与第二层相关联的量。
在这之后,我们还有一些可选步骤,用于设置阈值以分类预测结果,毕竟逻辑回归是一个分类算法。
更复杂的神经网络
用多个隐藏层的神经网络作为示例
• 在这里,我们逻辑回归函数的地位被定义为“激活函数”,逻辑回归函数是我们目前唯一见到过的激活函数形式,我们在将来的学习中会看到更多的激活函数形式。
• 方括号上标[l]依然代表当前层的参数或者激活,在这里,每一个神经元的简单逻辑回归用的是上一层的激活作为输入,因此有了下面方框内的激活计算公式。
• j指的是单元,也就是第几个神经元,他们是等价的表达
• 将输入层定义为第0层,x写为a[0],那么这个公式同样也适用于第一层。
下面我们将把我们学到的东西组合成一个算法,让我们的神经网络进行推理或预测。
神经网络前向传播
手写数字识别例子,将手写的数字二元分类为0/1,输出数字是1的概率。
数字被放在8*8的图中,255是白色,0是黑色。
神经网络包含三层(输入层不算),其中有两个隐藏层,分别包含25和15个单元。
a[1]和a[2]的激活函数都是Sigmoid函数。
之后计算a3激活值(fx),也就是神经网络的输出,激活函数仍是Sigmoid。
这就是前向传播,因为我们在传播神经元的激活。
之后我们会学习反向传播。
前向传播的架构最初几层有更多的隐藏单元,随着离输出层越来越近,隐藏单元的数量会减少。
有了前向传播方法,我们就可以下载别人训练好的模型参数进行推理,下面是tensorflow的实现。
Tensorflow实现推理
Tensorflow是实现深度学习算法的领先框架之一。另一个常用框架是Pytorch。
烤咖啡豆的例子:
根据这个神经网络架构,Tensorflow的基本实现步骤如下:
创建np数组x,x是一个温度和持续时间的向量。
创建layer1,类型是Dense(后续会介绍其它层类型),含三个单元(神经元),激活函数是Sigmoid。
激活值a1=layer1在x输入时的输出。
同样创建只有一个单元的Dense类型的layer2,激活值a2是layer2在a1输入时的输出。
之后根据a2的值判断yhat(分类结果)。
对于手写数字识别的例子:
x是所有像素格展开的亮度数组。
每一层设置单元数和激活函数,分别创建激活值。
最后进行输出分类yhat的二元预测。
Tensorflow中的数据形式
如何在Numpy和Tensorflow中表示数据,这一点很重要。
Numpy在Tensorflow之前被创造,作为线性代数的标准库,而之后的Tensorflow和Numpy的数据表示方式有些许不一致。
可以注意到有两层方括号,内层表示矩阵的行,外层表示行组合成矩阵。
矩阵实际上是一个二维数组。
如果使用了双方括号,那么()内表示一个矩阵,即有行有列,是一个二维的量,即便它只是一个行向量或一个列向量。
在之前的课程中,我们只使用一个方括号,这表示()内部仅仅是一个一维向量,不是一个矩阵。在Tensorflow中,使用双方括号的形式让数据成为一个矩阵而不仅仅是一个简单的一维向量有助于更高效的内部计算。这是一个约定。
因此我们刚刚看到的咖啡豆代码的x用的是双[[…]]
然后,a1=layer_1(x)得到的激活值a1也是一个矩阵,例如[[0.2, 0.7, 0.3]]。如果我们试图打印a1,那么就可以看到a1是tf.Tensor()(张量)类型,这是一个Tensorflow创建的数据类型,用于有效存储和执行矩阵运算,实际上张量比矩阵更加通用,但是本课程将其视为矩阵的一种形式。同时可以查看矩阵内容和矩阵形状,以及表示矩阵元素的数据类型。
因此,Tensorflow内部的矩阵表示形式和Numpy是不同的。如果要将Tensorflow的矩阵转化为Numpy的形式返回,需要调用a1.numpy()方法。
对于第二层,输出a2作为一个激活值,是1*1的矩阵形式,例如[[0.8]],同样是一个二维数组;通过.numpy()方法可以将其转化为Numpy数组(Numpy矩阵)。
由于历史遗留的问题,Numpy和Tensorflow表示矩阵的方式有所不同。通常我们习惯于在Numpy中加载操作数据,但将Numpy数组传递给Tensorflow时,Tensorflow习惯于将其转化为喜欢的Tensor格式,并用其高效操作。读回数据时,我们可以直接读取Tensor,或者将其转化回Numpy数组。虽然我们不得不做一些额外的转换,但是事实证明Tensorflow和Numpy可以很好地协同工作,我们在coding时要注意这些转换的问题。
搭建一个神经网络
之前的显示计算方式:
Tensorflow中构建神经网络的不同方法:
创建层的操作与显示计算相同。然后只需使用顺序函数Sequential,将[layer_1, layer_2](层组成的列表?)传入,就得到一个由第一层和第二层顺序构成的神经网络模型model。
之后,将我们的训练集数据用Numpy数组表示(矩阵和一维向量),进行compile和fit函数操作,就可以用predict函数进行对x_new值的预测。其中compile和fit函数的具体细节在之后介绍。
另一个更复杂的例子(数字分类,有三层):
过程是一致的。同时我们可以不对创建的三个Dense层进行变量赋值,将其直接传入顺序函数Sequential():
方括号包括三个Dense层。
接下去我们介绍一下用Python实现的前向传播。尽管实际中我们更多地是直接使用Tensorflow这样的专业库,但了解这些代码到底在做什么对我们解决和理解问题很有帮助。
单个网络层的前向传播
如果我们直接对前向传播的过程coding,那么就应该是这样的过程。
所有的向量都用一维Numpy数组表示。
我们知道第一层和第二层所有的参数w(向量),b,那么输入x,我们就用最基本的过程计算出向量a[1]的三个元素,然后计算出向量a[2]的一个元素。
这就是用Python和np实现前向传播的过程。
这个过程可以简化。
前向传播的一般实现
读懂理解就足够。
在这里我们将所有的参数也放入了np数组,在需要时提取;并且采用了循环的方式计算出每个单元的激活函数值,从而计算出每个单层的激活值。
对于每个单层,逐层顺序计算激活值,构成顺序函数,就可以得到最终输出。这就是Tensorflow实现的底层原理。
• shape[1]指的是矩阵列数,在这里是提取每层的单元数。
• 将w向量参数按列构成矩阵,使用时也按列提取(按列是因为每一列对应一个不同单元的参数,激活值的每一列也对应一个激活函数值;同样有利于向量化的矩阵乘法实现)
强人工智能(AGI)
狭义的AI(ANI)正迅速发展并成果卓著,这推动了整个AI的发展,但并不代表AGI也随之发展。
再者,逻辑回归的激活方式相较于人脑神经元真正的工作方式,实在是过于简化了。因此试图模拟人脑而发展AGI是困难的。
希望同样存在,有研究发现生物脑的特定部分可以学会其它的功能,这是否意味着存在一个未知的学习算法?是否有望在计算机上实现?未来可期。
神经网络如何高效实现
秘诀在于矢量化,这样可以利用GPU等非常善于做大型矩阵乘法的硬件。
对之前的代码矢量化后,可以看到矩阵乘法matmul方法代替了提取每一列w向量参数相乘的循环,这是很高效的。
矩阵乘法代码
线性代数中我们非常熟悉矩阵乘法。
• 转置函数.T
• @可以代替.matmul
对应过程和代码实现:
实际上Tensorflow的实现不需要对输入进行转置,默认按行排列。老师这里使用的转置是为了解释矩阵乘法的原理。本来一行一列就挺好的。能看懂代码即可。
Week2
TensorFlow中模型训练实现
对于数字0/1识别的例子,我们要导入相应的模块,比如tensorflow,Sequential,Dense;第一步是创建神经网络;第二步骤用compile方法编译模型,并指定损失函数,这里是二元交叉熵损失函数;第三步用fit方法训练模型,epchs是梯度下降的步骤数。
这是Tensorflow训练神经网络的基本代码实现。
模型训练细节
操作原理|逻辑回归过程|神经网络代码
• 创建模型
• 损失函数和成本函数
—— 二元交叉熵损失函数的二元用于强调分类问题的性质,实质上就是逻辑回归的损失函数。交叉熵是统计概念。
—— 大写的W和B表示所有参数w,b。
—— 如果想要使用回归的损失函数,比如平方误差损失函数,可以令loss=mean suqare error。
—— Keras是合并到Tensorflow的,最初是一个完全不同的项目
• 梯度下降
Tensorflow用反向传播算法计算偏导数项来更新参数值。
• Libraries介绍
• Tensorflow需要另外学习了
接下去看看如何交换不同的激活函数。
Sigmoid函数的替代方案
以房屋价格为例:
对于神经网络某一隐藏层每个单元的激活,我们都可以采用不同的激活函数。我们之前只使用了逻辑回归的Sigmoid函数。
我们常会使用ReLU(Rectified Linear Unit)函数,全称是整流线性单元函数。
图像如图所示,可以定义为gz=max(0,z)。z=wx+b
除此以外,还有“线性激活函数”。由于gz=z,因此a=gz=wx+b,这看起来似乎和没有激活函数一样,因此有时也把线性激活函数叫做“没有激活函数”。
后续还会介绍Softmax激活函数,有了这些激活函数,就可以构建强大而复杂的神经网络。
如何选择激活函数
输出层根据目标标签或真实标签y是什么,输出层的激活函数会有一个相当自然的选择,然后我们再看隐藏层的各个单元的激活函数。
输出层
• y=0/1的二元分类问题,一般就会选择Sigmoid函数;
• 对于有正负值的结果(回归问题),可以使用线性激活函数;
• 对于只有0和正值结果的回归问题,可以使用ReLU函数;
隐藏层
对于隐藏层,目前最常用的就是ReLU函数作为激活函数的方案。
• ReLU函数计算比Sigmoid函数更快,因为后者有取幂等操作。
• ReLU函数只在<0处平坦,而Sigmoid函数在两处平坦。这会导致Sigmoid构造出的成本函数在很多地方比较平,这会减慢梯度下降的速度。
总结
这就是常见方案,输出层根据输出决定;隐藏层默认ReLU。
其它激活函数:tanh,swish,LeakyReLU……
激活函数到底是什么,为什么重要,为什么模型需要激活函数
为什么我们不直接将每个神经元进行线性回归(没有激活函数)?
——因为这和直接进行线性回归没有什么区别,破坏了我们构建神经网络的初衷。
只使用多个线性回归单元并不会让神经网络比线性回归更复杂,可以说是完全等价的。如果只有输出层是逻辑回归,那么整个模型和逻辑回归是等价的。
—— 经验法则,不要在隐藏层中使用线性激活函数(也就是一定要用激活函数),使用ReLU就可以。
多分类问题
可能有两个以上的输出标签,不仅仅是0/1。
将了解算法学习出这样的决策边界。
Softmax
对逻辑回归算法作的多分类推广
逻辑回归中我们可以计算出gz,记为a1,这是预测结果y=1的概率;根据概率原理,预测值y=0的概率是0.29。
如果我们将这种方式推广至多分类问题,就得到了Softmax回归的表达式。
• zj=wjx+b,j代表N个分类可能输出的某一个。
• aj=e的zj次,除以所有的e的zk次方之和。代表结果是某一预测值的概率。
• 所有的aj(1…n)之和是1
损失函数和成本函数形式
对于之前的逻辑回归损失函数,我们可以将1-a1的部分写为a2(P|y=0)。在之前对逻辑回归损失函数的讨论中,我们不强调和概率有关的表达方式,但是它实际上可以这样做。逻辑回归的损失函数就可以表示成:
• if y=1:loss= -log a1
• if y=0:loss= -log a2
转化成了真实标签和对应该标签的预测概率的表达形式。
由此推广至Softmax回归。对于每一个可能的预测y(1…n),损失函数是一个n段的分段函数,每一段都是针对当前的真实标签y=j取其对应的预测概率负对数-logaj(注意aj的表达式不再是Sigmoid),这也印证着我们之前”预测值=当前y的概率越大,概率越接近于1,则损失函数越小的思想“,我们希望预测尽可能接近真实标签,那么这个损失函数正好符合我们的需求,我们也希望它越小越好。同样对于其构成的成本函数,应该也要能够是凸的以顺利运行梯度下降。
再次强调,我们之前没有考虑很多和概率有关的问题,因为当时是二元的逻辑回归,我们单纯地构造了y=1和y=0时都满此时足要求的损失函数并用它构造了一个凸的成本函数。而我们现在考虑多分类问题,此时用到了和概率有关的表达方法。事实证明,这种方法也适用于二元的逻辑回归情形,可以视为是二元逻辑回归的推广。
注意对于不同的真实标签y,损失函数的表达式是不同的,因此成本函数是所有标签损失函数的平均值。
以Softmax为输出的神经网络
为了构建一个可以进行多分类的神经网络,我们将采用Softmax回归模型并将其放入神经网络的输出层。
我们将之前逻辑回归只有一个单元的输出层变成了有N个(输出分类数,这里是10)单元的Softmax层。Softmax层的每个输出aj是每个单元的激活值,来源于z1…z10(每一个zj)的计算表达式,代表y=1…10的概率,它们构成一个N维向量a[3],3代表第三层。
与之前的Sigmoid或者ReLU不同,这里Softmax输出层的激活向量中的每一个激活值都是z1…z10的函数,不单单是一个z的函数,单个单元的输出同时用到了其它单元的参数。
tensorflow实现
依然是使用Sequential将三个Dense层串在一起,但是输出层的激活函数使用Softmax。损失函数使用SparseCategoricalCrossentropy函数(稀疏分类交叉熵损失函数),稀疏指的是数字不会同时是多分类中的两个,只会是一个。
之后是compile和fit。
这个版本虽然可以正常工作,但不要使用,有更好的方式,仅供理解。
Softmax的改进实现
虽然我们计算Softmax成本函数的方法是正确的,但有一种不同的公式可以减少类似于浮点数的精度丢失这样的数值误差,从而在tensorflow中实现更准确的计算。
如图:计算误差
不使用a作为计算损失函数的中间值,可以避免计算带来的舍入误差。损失函数参数from_logits=True,同时将输出层的激活函数设置为linear。logit实际上指的是z。
这种方法并不直观,逻辑回归是否使用不影响结果,但是Softmax回归的数值舍入误差会更糟糕,因此需要。
对于Softmax情形:
将输出层的激活函数改为线性,添加from_logits=True参数。
这样和我们之前的做法相比,做的事情没有区别,但是数字更准确,同时也更难以阅读。但还是建议在底层就使用这种形式。
输出层激活函数改成线性实际上也代表”没有”激活函数,稍后肯定需要指定。
• 实际的Softmax代码
• 实际的逻辑回归代码(这个差不多)
预测的logits是z,不是真正的激活值,真正的预测输出通过指明Sigmoid或者Softmax激活函数得到。
这样就能以数值稳定的方式实现分类问题。
多个输出的分类(多标签)
输出可能是一个包含多个数字的向量,不仅仅是一个数字。
道路识别案例的多标签输出
分别使用三个神经网络预测不同的输出是不合理的。我们可以在输出层使用不同的单元预测不同的输出,最够组成一个包含多标签的向量。
需要区分多分类问题和多标签问题。多分类问题是输出标签可能有N个不同的值;而多标签问题则是要处理多个分类问题,例如3个二元分类问题。
高级优化方法(相较于梯度下降)
我们在对成本函数进行梯度下降时,如果下降方向一直都比较一致,那么可以考虑增大学习率;如果梯度下降的偏导数方向非常不一致,那么需要减小学习率。利用Adam算法可以自动调整学习率的大小。
ADAM=Adaptive Moment Estimation 自适应矩估计
Adam算法对每个参数使用不同的学习率,而不是全局一致的α。
实现逻辑和我们描述的一致,根据参数下降的方向是否经常一致来增大或减小该参数对应的学习率:
Adam算法的实现细节此处不考虑,在更高级的深度学习课程中会有介绍。
在代码中,我们添加优化器optimizer参数,指定Adam算法和默认初始学习率。
Adam是很好的选择,大多数从业者都在使用,可以让学习速度更快。
其它的层类型
现在我们所使用的都是密集层类型,每一层获得上一层所有激活的输入。使用密集层足以构建强大的学习算法。
现在我们将介绍其它层类型。
• 复习密集层
• 卷积层
• 在数字识别例子中,每个单元只取一部分像素作为输入。这可以导致更快的计算和更少的数据集需求,并且更不容易过拟合。
• 有多个卷积层的神经网络称为卷积神经网络。
详细说明一个卷积层,隐藏层是卷积层的神经网络例子:
心电图数据预测
通过有效选择卷积层的单元数和每个单元获取的输入窗口这些架构参数,然后在输出层使用Sigmoid二元分类,可以构建更有效的新版本神经网络。
Python的导数操作
使用Sympy库:
计算图
深度学习的关键概念,tensorflow等编程框架计算神经网络导数的方法。
• 计算成本函数J的计算图:
这是一个单元的神经网络(线性回归)。在得知了w,b,x,y之后可以计算J,这是前向传播。
• 计算偏导数的计算图
在前向传播计算J的基础上,将成本函数计算公式拆分为各部分,用反向传播方式逐步计算增量比值,实际上模拟出了链式法则求导的过程,得出每一部分的导数,最终得到J对w的偏导。
通过计算图进行反向传播是计算相关偏导数的高效方法:
例如图中,J对a的导数可以用于计算J对w和b的导数,因此在含有N个节点的计算图中,如果要计算P个参数的导数,由于节点重复利用的性质,计算的次数约是N+P次而不是N*P次。
大型神经网络示例
ReLU隐藏层和输出层(输出非负的选择)
也是根据计算图逐步反向传播计算导数。
如果我们不进行反向传播,那么每次计算参数微小变化导致的J的变化以计算导数的方法是N*P复杂的,是低效的方法。而进行反向传播,我们可以重复利用某个节点的导数值,因此是N+P的高效方法。
曾经的研究人员手动微积分计算,现在都使用框架的backprop以及forwardprop——>autodiff(自动微分)
Week3
下一步做什么?——关于构建机器学习系统的建议
以房价预测为例,如果模型出现不理想的错误预测结果,那么我们可以做的事情有很多(红色箭头→),如何选择?
机器学习诊断:
通过诊断测试,可以知道哪些事情对于学习算法是有作用的。虽然诊断需要时间,但可以帮助我们更好地利用时间。
模型评估——算法的性能
模型可能可以很好地拟合数据,但是不能泛化;另外在特征很多的时候也fx也很复杂,需要评估性能的方法。
我们常将数据集3/7或者2/8分为训练集training set和测试集test set:
用括号上角标表示训练集或测试集的第m个样例,m是训练集和测试集各自的大小。
线性回归的训练和测试程序(用平方误差成本)
最小化成本函数之后,对模型进行评估时,分别计算其训练成本和测试成本,对应于训练集数据预测结果与真实数据的误差以及测试集数据预测结果与真实数据的误差。
• 过拟合的数据可能在训练成本上非常小,但是测试成本很大
• 成本函数最小化时有防止过拟合的正则化项。但是计算训练成本和测试成本时没有,它们只是显示模型是否合理,不需要最小化操作,也就不需要正则化。
分类问题的训练测试程序
• 用逻辑回归误差计算训练成本和测试成本。
如果不使用逻辑回归成本函数类似的形式定义训练成本和测试成本,那么可以直接根据预测值y_hat的个数定义Jtest和Jtrain:
训练成本和测试成本分别为错误分类占总的分数。
模型选择和训练/交叉验证/测试集
直观原始的方法,不建议使用:
对于不同的模型(多项式次数),分别计算Jtest来评估泛化能力,选择测试成本最小的模型。该模型的误差可能乐观地低于实际误差,因为测试集参与了多项式的阶数选择,这就让评估泛化能力的目的遭到了不公平的干扰。
交叉验证集——用于自动选择模型
也叫验证集validation set,开发集dev set。
同时根据验证集数据计算验证误差/开发误差:
于是我们使用训练集来拟合参数,交叉验证集选择模型:
直到需要评估模型的泛化性能时,我们才使用测试集。
在训练和选择模型时,我们不会使用测试集,而只使用训练集和交叉验证集,例如图中Jcv最小的神经网络被选中。只有在模型完成进行评估时,我们才使用未参与过模型训练和选择过程的测试集,以保证避免过分乐观的公平评估。
诊断偏差bias和方差variance
模型通常不可能在第一次训练完成后就表现良好,查看学习算法的偏差和方差可以为下一步的尝试提供很好的指导。
我们之前了解过欠拟合=高偏差以及过拟合=高方差等相关概念,在只有一维输入x时,我们可以很好地通过可视化判断这些现象。但是当输入的特征更多时,我们可能画不出图像,因此我们需要通过模型在训练集和开发集上的表现来判断模型是否出现了高偏差或高方差:
• 训练集成本很高是高偏差的强条件,同时Jcv即开发集成本也会很高。
• 训练集成本Jtrain很低但是Jcv很高,说明模型过拟合,高方差。
• Jtrain和Jcv都低时说明模型刚刚好。
诊断高偏差和高方差的方法:
和我们刚刚用语言描述的一样:
• Jtrain高——高偏差
• Jcv>>Jtrain——高方差
• 同时高偏差和高方差(偶尔会出现这种情况),Jtrain高且Jcv>>Jtrain
正则化对偏差和方差的影响
之前我们介绍过线性回归的正则化方法,可以用于减少过拟合。具体的思想是在成本函数后加上一个带有正则化参数λ的正则化项,这个项由参数的平方乘上lambda构成,以此让各个参数尽可能随着minJ的过程变小,避免特征的过分影响,从而避免过拟合。
现在,我们可以指出,正则化参数λ的取值或许会影响我们的模型性能。
• 过大的λ会导致高偏差,因为要使正则化项很小,我们的参数都会接近于0。
• 过小的λ会导致高方差(过拟合),此时正则化的效果很弱。
假设我们正在拟合上面的四阶多项式,模型如上且使用正则化方法。我们该如何选择一个好的λ值?
注意,我们已经确定了多项式的阶数,现在我们只是在选择正则化参数的值。之前我们用了很类似的方法(改变阶数选最小Jcv)选择了多项式的阶数=4。
针对不同的λ,分别计算Jcv,选取Jcv最小的值,也就是交叉验证误差最小的模型。
同样地,我们可以画出横轴是λ大小的曲线图,左侧是小λ导致的高方差,右侧是大λ导致的高偏差。可以看到Jtrain和Jcv的变化。事实上Jtrain和Jcv的变化本质上是高偏差和高方差的体现,横轴在什么地方高偏差/高方差,它们就会出现很大的值或者很小的值。因此这张图和之前多项式阶数为横轴的图看起来是镜像的。
选择最小Jcv这种方法可以帮助我们选择一个好的多项式阶数或正则化参数。
性能评估的基准——到底什么是“高”,“大得多”
语音识别的例子中,人类的错误率是10.6%,那么相对于它而言的训练误差10.8%看似很高,实际上就是很低的水平。反之,Jcv即交叉验证误差就显得跟高了,因此方差问题更严重。
基准的确定:
• 人类的表现,对于非结构化数据(音频,图像,文本等)是很好的基准。
• 竞争算法的表现
• 根据经验猜测
有了我们根据训练目标设定的基准,我们能更好地判断高偏差和高方差。
学习曲线
学习曲线是一种帮助理解学习算法作为拥有一定量的经验函数的行为的方法。经验指的是训练样例。
训练集的大小和Jcv与Jtrain的关系如图,Jcv减小(模型更好)而Jtrain增大(更难拟合)。且Jcv总是比Jtrain大一些。
高偏差学习曲线
• 高偏差(欠拟合)的情况下,Jtrain很大,且再多的训练数据也不可能帮助模型达到基准。因此此时不适合只是继续投入大量数据来改善模型表现。
高方差学习曲线
• 高方差的情况可以追求更多的训练数据,因为此时Jcv要远大于Jtrain,基准在二者之间。训练数据多,更容易拟合一个更好的模型,而不是过度的拟合。
实际情况中,计算各种学习曲线是比较复杂的任务,但是这样建立的直觉有助于我们判断模型高偏差和高方差的情形。
更好地决定下一步做什么
遇到的问题和手段对应关系如下:
根据高方差还是高偏差的不同情况,选择不同的修复手段以改正模型。
• 高方差:获取更多训练数据,简化模型(减少特征集合),提高正则化参数。
• 高偏差:获取额外特征,添加多项式特征,减小正则化参数。
神经网络的偏差和方差
模型的偏差方差权衡:
• 简单的模型容易高偏差,复杂的模型容易高方差。
对于一个神经网络,它自身有一个解决高偏差和高方差问题的流程图:
高偏差使用更大的网络,高方差则增加更多的训练数据。这虽然不是永远有效的(有时候计算成本很大,有时候数据不能再多),但可以指导下一步做什么。GPU可以很好地加速计算,机器学习的重新兴起也得益于此。在训练一个模型的过程中,可能在不同的时间节点存在或是高偏差或是高方差的问题。
一个大型的神经网络往往是一个低偏差的机器,只要正则化合适,一个更大的神经网络往往也不会导致更高的方差,但是会导致更多的计算,减慢训练过程。
Tensorflow中实现神经网络的正则化
每一层的成本函数都加上正则化项,指定一个正则化参数λ。简单起见,可以将所有层的正则化参数设置为相同的。
机器学习开发的迭代循环
• 选择架构(模型,数据,参数)
• 训练模型
• 诊断(偏差,方差,错误分析等)
以处理垃圾邮件的文本分析为例:
之后做什么?考虑偏差和方差问题
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。