常用的机器学习算法
㈠ 干货 | 基础机器学习算法
本篇内容主要是面向机器学习初学者,介绍常见的机器学习算法,当然,欢迎同行交流。
哲学要回答的基本问题是从哪里来、我是谁、到哪里去,寻找答案的过程或许可以借鉴机器学习的套路:组织数据->挖掘知识->预测未来。组织数据即为设计特征,生成满足特定格式要求的样本,挖掘知识即建模,而预测未来就是对模型的应用。
特征设计依赖于对业务场景的理解,可分为连续特征、离散特征和组合高阶特征。本篇重点是机器学习算法的介绍,可以分为监督学习和无监督学习两大类。
无监督学习算法很多,最近几年业界比较关注主题模型,LSA->PLSA->LDA 为主题模型三个发展阶段的典型算法,它们主要是建模假设条件上存在差异。LSA假设文档只有一个主题,PLSA 假设各个主题的概率分布不变(theta 都是固定的),LDA 假设每个文档和词的主题概率是可变的。
LDA 算法本质可以借助上帝掷骰子帮助理解,详细内容可参加 Rickjin 写的《 LDA 数据八卦》文章,浅显易懂,顺便也科普了很多数学知识,非常推荐。
监督学习可分为分类和回归,感知器是最简单的线性分类器,现在实际应用比较少,但它是神经网络、深度学习的基本单元。
线性函数拟合数据并基于阈值分类时,很容易受噪声样本的干扰,影响分类的准确性。逻辑回归(Logistic Regression)利用 sigmoid 函数将模型输出约束在 0 到 1 之间,能够有效弱化噪声数据的负面影响,被广泛应用于互联网广告点击率预估。
逻辑回归模型参数可以通过最大似然求解,首先定义目标函数 L ( theta ),然后 log 处理将目标函数的乘法逻辑转化为求和逻辑(最大化似然概率 -> 最小化损失函数),最后采用梯度下降求解。
相比于线性分类去,决策树等非线性分类器具有更强的分类能力,ID3 和 C4.5 是典型的决策树算法,建模流程基本相似,两者主要在增益函数(目标函数)的定义不同。
线性回归和线性分类在表达形式上是类似的,本质区别是分类的目标函数是离散值,而回归的目标函数是连续值。目标函数的不同导致回归通常基于最小二乘定义目标函数,当然,在观测误差满足高斯分布的假设情况下,最小二乘和最大似然可以等价。
当梯度下降求解模型参数时,可以采用 Batch 模式或者 Stochastic 模式,通常而言,Batch 模式准确性更高,Stochastic 模式复杂度更低。
上文已经提到,感知器虽然是最简单的线性分类器,但是可以视为深度学习的基本单元,模型参数可以由自动编码( Auto Encoder )等方法求解。
深度学习的优势之一可以理解为特征抽象,从底层特征学习获得高阶特征,描述更为复杂的信息结构。例如,从像素层特征学习抽象出描述纹理结构的边缘轮廓特征,更进一步学习获得表征物体局部的更高阶特征。
俗话说三个臭皮匠赛过诸葛亮,无论是线性分类还是深度学习,都是单个模型算法单打独斗,有没有一种集百家之长的方法,将模型处理数据的精度更进一步提升呢?当然,Model Ensembe l就是解决这个问题。Bagging 为方法之一,对于给定数据处理任务,采用不同模型/参数/特征训练多组模型参数,最后采用投票或者加权平均的方式输出最终结果。
Boosting为Model Ensemble 的另外一种方法,其思想为模型每次迭代时通过调整错误样本的损失权重提升对数据样本整体的处理精度,典型算法包括 AdaBoost 、GBDT 等。
不同的数据任务场景,可以选择不同的 Model Ensemble 方法,对于深度学习,可以对隐层节点采用 DropOut 的方法实现类似的效果。
介绍了这么多机器学习基础算法,说一说评价模型优劣的基本准则。欠拟合和过拟合是经常出现的两种情况,简单的判定方法是比较训练误差和测试误差的关系,当欠拟合时,可以设计更多特征来提升模型训练精度,当过拟合时,可以优化特征量降低模型复杂度来提升模型测试精度。
特征量是模型复杂度的直观反映,模型训练之前设定输入的特征量是一种方法,另外一种比较常用的方法是在模型训练过程中,将特征参数的正则约束项引入目标函数/损失函数,基于训练过程筛选优质特征。
模型调优是一个细致活,最终还是需要能够对实际场景给出可靠的预测结果,解决实际问题。期待学以致用! 作者 晓惑 本文转自阿里技术,转载需授权
㈡ 机器学习新手必看十大算法
机器学习新手必看十大算法
本文介绍了机器学习新手需要了解的 10 大算法,包括线性回归、Logistic 回归、朴素贝叶斯、K 近邻算法等。
在机器学习中,有一种叫做“没有免费的午餐”的定理。简而言之,它指出没有任何一种算法对所有问题都有效,在监督学习(即预测建模)中尤其如此。
例如,你不能说神经网络总是比决策树好,反之亦然。有很多因素在起作用,例如数据集的大小和结构。
因此,你应该针对具体问题尝试多种不同算法,并留出一个数据“测试集”来评估性能、选出优胜者。
当然,你尝试的算法必须适合你的问题,也就是选择正确的机器学习任务。打个比方,如果你需要打扫房子,你可能会用吸尘器、扫帚或拖把,但是你不会拿出铲子开始挖土。
大原则
不过也有一个普遍原则,即所有监督机器学习算法预测建模的基础。
机器学习算法被描述为学习一个目标函数 f,该函数将输入变量 X 最好地映射到输出变量 Y:Y = f(X)
这是一个普遍的学习任务,我们可以根据输入变量 X 的新样本对 Y 进行预测。我们不知道函数 f 的样子或形式。如果我们知道的话,我们将会直接使用它,不需要用机器学习算法从数据中学习。
最常见的机器学习算法是学习映射 Y = f(X) 来预测新 X 的 Y。这叫做预测建模或预测分析,我们的目标是尽可能作出最准确的预测。
对于想了解机器学习基础知识的新手,本文将概述数据科学家使用的 top 10 机器学习算法。
1. 线性回归
线性回归可能是统计学和机器学习中最知名和最易理解的算法之一。
预测建模主要关注最小化模型误差或者尽可能作出最准确的预测,以可解释性为代价。我们将借用、重用包括统计学在内的很多不同领域的算法,并将其用于这些目的。
线性回归的表示是一个方程,它通过找到输入变量的特定权重(称为系数 B),来描述一条最适合表示输入变量 x 与输出变量 y 关系的直线。
线性回归
例如:y = B0 + B1 * x
我们将根据输入 x 预测 y,线性回归学习算法的目标是找到系数 B0 和 B1 的值。
可以使用不同的技术从数据中学习线性回归模型,例如用于普通最小二乘法和梯度下降优化的线性代数解。
线性回归已经存在了 200 多年,并得到了广泛研究。使用这种技术的一些经验是尽可能去除非常相似(相关)的变量,并去除噪音。这是一种快速、简单的技术,可以首先尝试一下。
2. Logistic 回归
Logistic 回归是机器学习从统计学中借鉴的另一种技术。它是解决二分类问题的首选方法。
Logistic 回归与线性回归相似,目标都是找到每个输入变量的权重,即系数值。与线性回归不同的是,Logistic 回归对输出的预测使用被称为 logistic 函数的非线性函数进行变换。
logistic 函数看起来像一个大的 S,并且可以将任何值转换到 0 到 1 的区间内。这非常实用,因为我们可以规定 logistic 函数的输出值是 0 和 1(例如,输入小于 0.5 则输出为 1)并预测类别值。
Logistic 回归
由于模型的学习方式,Logistic 回归的预测也可以作为给定数据实例(属于类别 0 或 1)的概率。这对于需要为预测提供更多依据的问题很有用。
像线性回归一样,Logistic 回归在删除与输出变量无关的属性以及非常相似(相关)的属性时效果更好。它是一个快速的学习模型,并且对于二分类问题非常有效。
3. 线性判别分析(LDA)
Logistic 回归是一种分类算法,传统上,它仅限于只有两类的分类问题。如果你有两个以上的类别,那么线性判别分析是首选的线性分类技术。
LDA 的表示非常简单直接。它由数据的统计属性构成,对每个类别进行计算。单个输入变量的 LDA 包括:
每个类别的平均值;
所有类别的方差。
线性判别分析
进行预测的方法是计算每个类别的判别值并对具备最大值的类别进行预测。该技术假设数据呈高斯分布(钟形曲线),因此最好预先从数据中删除异常值。这是处理分类预测建模问题的一种简单而强大的方法。
4. 分类与回归树
决策树是预测建模机器学习的一种重要算法。
决策树模型的表示是一个二叉树。这是算法和数据结构中的二叉树,没什么特别的。每个节点代表一个单独的输入变量 x 和该变量上的一个分割点(假设变量是数字)。
决策树
决策树的叶节点包含一个用于预测的输出变量 y。通过遍历该树的分割点,直到到达一个叶节点并输出该节点的类别值就可以作出预测。
决策树学习速度和预测速度都很快。它们还可以解决大量问题,并且不需要对数据做特别准备。
5. 朴素贝叶斯
朴素贝叶斯是一个简单但是很强大的预测建模算法。
该模型由两种概率组成,这两种概率都可以直接从训练数据中计算出来:1)每个类别的概率;2)给定每个 x 的值,每个类别的条件概率。一旦计算出来,概率模型可用于使用贝叶斯定理对新数据进行预测。当你的数据是实值时,通常假设一个高斯分布(钟形曲线),这样你可以简单的估计这些概率。
贝叶斯定理
朴素贝叶斯之所以是朴素的,是因为它假设每个输入变量是独立的。这是一个强大的假设,真实的数据并非如此,但是,该技术在大量复杂问题上非常有用。
6. K 近邻算法
KNN 算法非常简单且有效。KNN 的模型表示是整个训练数据集。是不是很简单?
KNN 算法在整个训练集中搜索 K 个最相似实例(近邻)并汇总这 K 个实例的输出变量,以预测新数据点。对于回归问题,这可能是平均输出变量,对于分类问题,这可能是众数(或最常见的)类别值。
诀窍在于如何确定数据实例间的相似性。如果属性的度量单位相同(例如都是用英寸表示),那么最简单的技术是使用欧几里得距离,你可以根据每个输入变量之间的差值直接计算出来其数值。
K 近邻算法
KNN 需要大量内存或空间来存储所有数据,但是只有在需要预测时才执行计算(或学习)。你还可以随时更新和管理训练实例,以保持预测的准确性。
距离或紧密性的概念可能在非常高的维度(很多输入变量)中会瓦解,这对算法在你的问题上的性能产生负面影响。这被称为维数灾难。因此你最好只使用那些与预测输出变量最相关的输入变量。
7. 学习向量量化
K 近邻算法的一个缺点是你需要遍历整个训练数据集。学习向量量化算法(简称 LVQ)是一种人工神经网络算法,它允许你选择训练实例的数量,并精确地学习这些实例应该是什么样的。
学习向量量化
LVQ 的表示是码本向量的集合。这些是在开始时随机选择的,并逐渐调整以在学习算法的多次迭代中最好地总结训练数据集。在学习之后,码本向量可用于预测(类似 K 近邻算法)。最相似的近邻(最佳匹配的码本向量)通过计算每个码本向量和新数据实例之间的距离找到。然后返回最佳匹配单元的类别值或(回归中的实际值)作为预测。如果你重新调整数据,使其具有相同的范围(比如 0 到 1 之间),就可以获得最佳结果。
如果你发现 KNN 在你的数据集上达到很好的结果,请尝试用 LVQ 减少存储整个训练数据集的内存要求。
8. 支持向量机(SVM)
支持向量机可能是最受欢迎和最广泛讨论的机器学习算法之一。
超平面是分割输入变量空间的一条线。在 SVM 中,选择一条可以最好地根据输入变量类别(类别 0 或类别 1)对输入变量空间进行分割的超平面。在二维中,你可以将其视为一条线,我们假设所有的输入点都可以被这条线完全的分开。SVM 学习算法找到了可以让超平面对类别进行最佳分割的系数。
支持向量机
超平面和最近的数据点之间的距离被称为间隔。分开两个类别的最好的或最理想的超平面具备最大间隔。只有这些点与定义超平面和构建分类器有关。这些点被称为支持向量,它们支持或定义了超平面。实际上,优化算法用于寻找最大化间隔的系数的值。
SVM 可能是最强大的立即可用的分类器之一,值得一试。
9. Bagging 和随机森林
随机森林是最流行和最强大的机器学习算法之一。它是 Bootstrap Aggregation(又称 bagging)集成机器学习算法的一种。
bootstrap 是从数据样本中估算数量的一种强大的统计方法。例如平均数。你从数据中抽取大量样本,计算平均值,然后平均所有的平均值以便更好的估计真实的平均值。
bagging 使用相同的方法,但是它估计整个统计模型,最常见的是决策树。在训练数据中抽取多个样本,然后对每个数据样本建模。当你需要对新数据进行预测时,每个模型都进行预测,并将所有的预测值平均以便更好的估计真实的输出值。
随机森林
随机森林是对这种方法的一种调整,在随机森林的方法中决策树被创建以便于通过引入随机性来进行次优分割,而不是选择最佳分割点。
因此,针对每个数据样本创建的模型将会与其他方式得到的有所不同,不过虽然方法独特且不同,它们仍然是准确的。结合它们的预测可以更好的估计真实的输出值。
如果你用方差较高的算法(如决策树)得到了很好的结果,那么通常可以通过 bagging 该算法来获得更好的结果。
10. Boosting 和 AdaBoost
Boosting 是一种集成技术,它试图集成一些弱分类器来创建一个强分类器。这通过从训练数据中构建一个模型,然后创建第二个模型来尝试纠正第一个模型的错误来完成。一直添加模型直到能够完美预测训练集,或添加的模型数量已经达到最大数量。
AdaBoost 是第一个为二分类开发的真正成功的 boosting 算法。这是理解 boosting 的最佳起点。现代 boosting 方法建立在 AdaBoost 之上,最显着的是随机梯度提升。
AdaBoost
AdaBoost与短决策树一起使用。在第一个决策树创建之后,利用每个训练实例上树的性能来衡量下一个决策树应该对每个训练实例付出多少注意力。难以预测的训练数据被分配更多权重,而容易预测的数据分配的权重较少。依次创建模型,每个模型在训练实例上更新权重,影响序列中下一个决策树的学习。在所有决策树建立之后,对新数据进行预测,并且通过每个决策树在训练数据上的精确度评估其性能。
因为在纠正算法错误上投入了太多注意力,所以具备已删除异常值的干净数据非常重要。
总结
初学者在面对各种机器学习算法时经常问:“我应该用哪个算法?”这个问题的答案取决于很多因素,包括:(1)数据的大小、质量和特性;(2)可用的计算时间;(3)任务的紧迫性;(4)你想用这些数据做什么。
即使是经验丰富的数据科学家在尝试不同的算法之前,也无法分辨哪种算法会表现最好。虽然还有很多其他的机器学习算法,但本篇文章中讨论的是最受欢迎的算法。如果你是机器学习的新手,这将是一个很好的学习起点。
㈢ 机器学习一般常用的算法有哪些
机器学习是人工智能的核心技术,是学习人工智能必不可少的环节。机器学习中有很多算法,能够解决很多以前难以企的问题,机器学习中涉及到的算法有不少,下面小编就给大家普及一下这些算法。
一、线性回归
一般来说,线性回归是统计学和机器学习中最知名和最易理解的算法之一。这一算法中我们可以用来预测建模,而预测建模主要关注最小化模型误差或者尽可能作出最准确的预测,以可解释性为代价。我们将借用、重用包括统计学在内的很多不同领域的算法,并将其用于这些目的。当然我们可以使用不同的技术从数据中学习线性回归模型,例如用于普通最小二乘法和梯度下降优化的线性代数解。就目前而言,线性回归已经存在了200多年,并得到了广泛研究。使用这种技术的一些经验是尽可能去除非常相似(相关)的变量,并去除噪音。这是一种快速、简单的技术。
二、Logistic 回归
它是解决二分类问题的首选方法。Logistic 回归与线性回归相似,目标都是找到每个输入变量的权重,即系数值。与线性回归不同的是,Logistic 回归对输出的预测使用被称为 logistic 函数的非线性函数进行变换。logistic 函数看起来像一个大的S,并且可以将任何值转换到0到1的区间内。这非常实用,因为我们可以规定logistic函数的输出值是0和1并预测类别值。像线性回归一样,Logistic 回归在删除与输出变量无关的属性以及非常相似的属性时效果更好。它是一个快速的学习模型,并且对于二分类问题非常有效。
三、线性判别分析(LDA)
在前面我们介绍的Logistic 回归是一种分类算法,传统上,它仅限于只有两类的分类问题。而LDA的表示非常简单直接。它由数据的统计属性构成,对每个类别进行计算。单个输入变量的 LDA包括两个,第一就是每个类别的平均值,第二就是所有类别的方差。而在线性判别分析,进行预测的方法是计算每个类别的判别值并对具备最大值的类别进行预测。该技术假设数据呈高斯分布,因此最好预先从数据中删除异常值。这是处理分类预测建模问题的一种简单而强大的方法。
四、决策树
决策树是预测建模机器学习的一种重要算法。决策树模型的表示是一个二叉树。这是算法和数据结构中的二叉树,没什么特别的。每个节点代表一个单独的输入变量x和该变量上的一个分割点。而决策树的叶节点包含一个用于预测的输出变量y。通过遍历该树的分割点,直到到达一个叶节点并输出该节点的类别值就可以作出预测。当然决策树的有点就是决策树学习速度和预测速度都很快。它们还可以解决大量问题,并且不需要对数据做特别准备。
五、朴素贝叶斯
其实朴素贝叶斯是一个简单但是很强大的预测建模算法。而这个模型由两种概率组成,这两种概率都可以直接从训练数据中计算出来。第一种就是每个类别的概率,第二种就是给定每个 x 的值,每个类别的条件概率。一旦计算出来,概率模型可用于使用贝叶斯定理对新数据进行预测。当我们的数据是实值时,通常假设一个高斯分布,这样我们可以简单的估计这些概率。而朴素贝叶斯之所以是朴素的,是因为它假设每个输入变量是独立的。这是一个强大的假设,真实的数据并非如此,但是,该技术在大量复杂问题上非常有用。所以说,朴素贝叶斯是一个十分实用的功能。
六、K近邻算法
K近邻算法简称KNN算法,KNN 算法非常简单且有效。KNN的模型表示是整个训练数据集。KNN算法在整个训练集中搜索K个最相似实例(近邻)并汇总这K个实例的输出变量,以预测新数据点。对于回归问题,这可能是平均输出变量,对于分类问题,这可能是众数类别值。而其中的诀窍在于如何确定数据实例间的相似性。如果属性的度量单位相同,那么最简单的技术是使用欧几里得距离,我们可以根据每个输入变量之间的差值直接计算出来其数值。当然,KNN需要大量内存或空间来存储所有数据,但是只有在需要预测时才执行计算。我们还可以随时更新和管理训练实例,以保持预测的准确性。
七、Boosting 和 AdaBoost
首先,Boosting 是一种集成技术,它试图集成一些弱分类器来创建一个强分类器。这通过从训练数据中构建一个模型,然后创建第二个模型来尝试纠正第一个模型的错误来完成。一直添加模型直到能够完美预测训练集,或添加的模型数量已经达到最大数量。而AdaBoost 是第一个为二分类开发的真正成功的 boosting 算法。这是理解 boosting 的最佳起点。现代 boosting 方法建立在 AdaBoost 之上,最显着的是随机梯度提升。当然,AdaBoost 与短决策树一起使用。在第一个决策树创建之后,利用每个训练实例上树的性能来衡量下一个决策树应该对每个训练实例付出多少注意力。难以预测的训练数据被分配更多权重,而容易预测的数据分配的权重较少。依次创建模型,每一个模型在训练实例上更新权重,影响序列中下一个决策树的学习。在所有决策树建立之后,对新数据进行预测,并且通过每个决策树在训练数据上的精确度评估其性能。所以说,由于在纠正算法错误上投入了太多注意力,所以具备已删除异常值的干净数据十分重要。
八、学习向量量化算法(简称 LVQ)
学习向量量化也是机器学习其中的一个算法。可能大家不知道的是,K近邻算法的一个缺点是我们需要遍历整个训练数据集。学习向量量化算法(简称 LVQ)是一种人工神经网络算法,它允许你选择训练实例的数量,并精确地学习这些实例应该是什么样的。而学习向量量化的表示是码本向量的集合。这些是在开始时随机选择的,并逐渐调整以在学习算法的多次迭代中最好地总结训练数据集。在学习之后,码本向量可用于预测。最相似的近邻通过计算每个码本向量和新数据实例之间的距离找到。然后返回最佳匹配单元的类别值或作为预测。如果大家重新调整数据,使其具有相同的范围,就可以获得最佳结果。当然,如果大家发现KNN在大家数据集上达到很好的结果,请尝试用LVQ减少存储整个训练数据集的内存要求
㈣ 机器学习中有哪些重要的优化算法
梯度下降是非常常用的优化算法。作为机器学习的基础知识,这是一个必须要掌握的算法。借助本文,让我们来一起详细了解一下这个算法。
前言
本文的代码可以到我的Github上获取:
https://github.com/paulQuei/gradient_descent
本文的算法示例通过Python语言实现,在实现中使用到了numpy和matplotlib。如果你不熟悉这两个工具,请自行在网上搜索教程。
关于优化
大多数学习算法都涉及某种形式的优化。优化指的是改变x以最小化或者最大化某个函数的任务。
我们通常以最小化指代大多数最优化问题。最大化可经由最小化来实现。
我们把要最小化或最大化的函数成为目标函数(objective function)或准则(criterion)。
我们通常使用一个上标*表示最小化或最大化函数的x值,记做这样:
[x^* = arg; min; f(x)]
优化本身是一个非常大的话题。如果有兴趣,可以通过《数值优化》和《运筹学》的书籍进行学习。
模型与假设函数
所有的模型都是错误的,但其中有些是有用的。– George Edward Pelham Box
模型是我们对要分析的数据的一种假设,它是为解决某个具体问题从老洞数据中学习到的,因此它是机器学习最核心的概念。
针对一个问题,通常有大量的模型可以选择。
本文不会深入讨论这方面的内容,关于各种模型请参阅机器学习的相关书籍。本文仅以最简单的线性模型为基础来讨论梯度下降算法。
这里我们先介绍一下在监督学习(supervised learning)中常见的三个符号:
m,描述训练样本的数量
x,描述输入变量或特征
y,描述输出变量或者叫目标值
- 请注意,一个样本笑或可能有很多的特征,因此x和y通常是一个向量。不过在刚开始学习的时候,为了便于理解,你可以暂时理解为这就是一个具体的数值。
- 代价函数也叫损失函数。
- 不同的模型可能会用不同的损失函数。例如,logistic回归的假设函数是这样的:。其代价函数是这样的:
对于一个函数,怎么确定下行的方向?
每一步该往前走多远?
有没有可能停留在半山腰的平台上?
- 这里的下标i表示第i个参数。 上标k指的是第k步的计算结果,而非k次方。在能够理解的基础上,下文的公式中将省略上标k。
收敛是指函数的变化率很小。具体选择多少合适需要根据具体的项目来确定。在演示项目中我们可以选择0.01或者0.001这样的值。不同的值将影响算法的迭代次数,因为在梯度下降的最后,我们会越来越接近平坦的地方,这个时候函数的变化率也越来越小。如果选择一个很小的值,将可能导致算法迭代次数暴增。
公式中的 称作步长,也称作学习率(learning rate)。它决定了每一步往前走多远,关于这个值我们会在下文中详细讲解。你可以暂时人为它是一个类似0.01或0.001的固定值。
在具体的项目,我们不会让算法无休止的运行下去,所以通常会设置一个迭代次数的最大上限。
我们随机选择了 都为10作为起点
设置最多迭代1000次
收敛的范围设为0.001
学习步长设为0.01
如果样本数量较小(例如小于等于2000),选择BGD即可。
如果样本数量很大,选择 来进行MBGD,例如:64,128,256,512。
- 《深度学习》一书中是这样描述的:“与其说是科学,这更像是一门艺术,我们应该谨慎地参考关于这个问题的大部分指导。”。
对于凸函数或者凹函数来说,不存在局部极值的问题。其局部极值一定是全局极值。
最近的一些研究表明,某些局部极值并没有想象中的那么糟糕,它们已经非常的接近全局极值所带来的结果了。
Wikipeida: Gradient descent
Sebastian Ruder: An overview of gradient descent optimization algorithms
吴恩达:机器学习
吴恩达:深度学习
Peter Flach:机器学习
李宏毅 - ML Lecture 3-1: Gradient Descent
PDF: 李宏毅 - Gradient Descent
Intro to optimization in deep learning: Gradient Descent
Intro to optimization in deep learning: Momentum, RMSProp and Adam
Stochastic Gradient Descent – Mini-batch and more
刘建平Pinard - 梯度下降(Gradient Descent)小结
多元函数的偏导数、方向导数、梯度以及微分之间的关系思考
[Machine Learning] 梯度下降法的三种形式BGD、SGD以及MBGD
- 作者:阿Paul https://paul.pub/gradient-descent/
训练集会包含很多的样本,我们用 表示其中第i个样本。
x是数据样本的特征,y是其目标值。例如,在预测房价的模型中,x是房子的各种信息,例如:面积,楼层,位置等等,y是房子的价格。在图像识别的任务中,x是图形的所有像素点数据,y是图像中包含的目标对象。
我们是希望寻找一个函数,将x映射到y,这个函数要足够的好,以至于能够预测对应的y。由于历史原因,这个函数叫做假设函数(hypothesis function)。
学习的过程如下图所示。即:首先根据已有的数据(称之为训练集)训练我们的算法模型,然后根据模型的假设函数来进行新数据的预测。
线性模型(linear model)正如其名称那样:是希望通过一个直线的形式来描述模式。线性模型的假设函数如下所示:
[h_{ heta}(x) = heta_{0} + heta_{1} * x]
这个公式对于大家来说应该都是非常简单的。如果把它绘制出来,其实就是一条直线。
下图是一个具体的例子,即: 的图形:
在实际的机器学习工程中碰含伍,你会拥有大量的数据。这些数据会来自于某个数据源。它们存储在csv文件中,或者以其他的形式打包。
但是本文作为演示使用,我们通过一些简单的代码自动生成了需要的数据。为了便于计算,演示的数据量也很小。
import numpy as np
max_x = 10
data_size = 10
theta_0 = 5
theta_1 = 2
def get_data:
x = np.linspace(1, max_x, data_size)
noise = np.random.normal(0, 0.2, len(x))
y = theta_0 + theta_1 * x + noise
return x, y
这段代码很简单,我们生成了x范围是 [1, 10] 整数的10条数据。对应的y是以线性模型的形式计算得到,其函数是:。现实中的数据常常受到各种因素的干扰,所以对于y我们故意加上了一些高斯噪声。因此最终的y值为比原先会有轻微的偏离。
最后我们的数据如下所示:
x = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
y = [6.66, 9.11, 11.08, 12.67, 15.12, 16.76, 18.75, 21.35, 22.77, 24.56]
我们可以把这10条数据绘制出来这样就有一个直观的了解了,如下图所示:
虽然演示用的数据是我们通过公式计算得到的。但在实际的工程中,模型的参数是需要我们通过数据学习到的。所以下文我们假设我们不知道这里线性模式的两个参数是什么,而是通过算法的形式求得。
最后再跟已知的参数进行对比以验证我们的算法是否正确。
有了上面的数据,我们可以尝试画一条直线来描述我们的模型。
例如,像下面这样画一条水平的直线:
很显然,这条水平线离数据太远了,非常的不匹配。
那我们可以再画一条斜线。
我们初次画的斜线可能也不贴切,它可能像下面这样:
最后我们通过不断尝试,找到了最终最合适的那条,如下所示:
梯度下降算法的计算过程,就和这种本能式的试探是类似的,它就是不停的迭代,一步步的接近最终的结果。
代价函数
上面我们尝试了几次通过一条直线来拟合(fitting)已有的数据。
二维平面上的一条直线可以通过两个参数唯一的确定,两个参数的确定也即模型的确定。那如何描述模型与数据的拟合程度呢?答案就是代价函数。
代价函数(cost function)描述了学习到的模型与实际结果的偏差程度。以上面的三幅图为例,最后一幅图中的红线相比第一条水平的绿线,其偏离程度(代价)应该是更小的。
很显然,我们希望我们的假设函数与数据尽可能的贴近,也就是说:希望代价函数的结果尽可能的小。这就涉及到结果的优化,而梯度下降就是寻找最小值的方法之一。
对于每一个样本,假设函数会依据计算出一个估算值,我们常常用来表示。即 。
很自然的,我们会想到,通过下面这个公式来描述我们的模型与实际值的偏差程度:
[(h_ heta(x^i) - y^i)^2 = (widehat{y}^{i} - y^i)^2 = ( heta_{0} + heta_{1} * x^{i} - y^{i})^2]
请注意, 是实际数据的值, 是我们的模型的估算值。前者对应了上图中的离散点的y坐标,后者对应了离散点在直线上投影点的y坐标。
每一条数据都会存在一个偏差值,而代价函数就是对所有样本的偏差求平均值,其计算公式如下所示:
[L( heta) = frac {1}{m} sum_{i=1}^{m}(h_ heta(x^i) - y^i)^2 = frac {1}{m} sum_{i=1}^{m}( heta_{0} + heta_{1} * x^{i} - y^{i})^2]
当损失函数的结果越小,则意味着通过我们的假设函数估算出的结果与真实值越接近。这也就是为什么我们要最小化损失函数的原因。
借助上面这个公式,我们可以写一个函数来实现代价函数:
def cost_function(x, y, t0, t1):
cost_sum = 0
for i in range(len(x)):
cost_item = np.power(t0 + t1 * x[i] - y[i], 2)
cost_sum += cost_item
return cost_sum / len(x)
这个函数的代码应该不用多做解释,它就是根据上面的完成计算。
我们可以尝试选取不同的 和 组合来计算代价函数的值,然后将结果绘制出来:
import numpy as np
import matplotlib.pyplot as plt
from matplotlib import cm
from mpl_toolkits.mplot3d import Axes3D
theta_0 = 5
theta_1 = 2
def draw_cost(x, y):
fig = plt.figure(figsize=(10, 8))
ax = fig.gca(projection='3d')
scatter_count = 100
radius = 1
t0_range = np.linspace(theta_0 - radius, theta_0 + radius, scatter_count)
t1_range = np.linspace(theta_1 - radius, theta_1 + radius, scatter_count)
cost = np.zeros((len(t0_range), len(t1_range)))
for a in range(len(t0_range)):
for b in range(len(t1_range)):
cost[a][b] = cost_function(x, y, t0_range[a], t1_range[b])
t0, t1 = np.meshgrid(t0_range, t1_range)
ax.set_xlabel('theta_0')
ax.set_ylabel('theta_1')
ax.plot_surface(t0, t1, cost, cmap=cm.hsv)
在这段代码中,我们对 和 各自指定了一个范围进行100次的采样,然后以不同的 组合对来计算代价函数的值。
如果我们将所有点的代价函数值绘制出来,其结果如下图所示:
从这个图形中我们可以看出,当 越接近 [5, 2]时其结果(偏差)越小。相反,离得越远,结果越大。
直观解释
从上面这幅图中我们可以看出,代价函数在不同的位置结果大小不同。
从三维的角度来看,这就和地面的高低起伏一样。最高的地方就好像是山顶。
而我们的目标就是:从任意一点作为起点,能够快速寻找到一条路径并以此到达图形最低点(代价值最小)的位置。
而梯度下降的算法过程就和我们从山顶想要快速下山的做法是一样的。
在生活中,我们很自然会想到沿着最陡峭的路往下行是下山速度最快的。如下面这幅图所示:
针对这幅图,细心的读者可能很快就会有很多的疑问,例如:
这些问题也就是本文接下来要讨论的内容。
算法描述
梯度下降算法最开始的一点就是需要确定下降的方向,即:梯度。
我们常常用 来表示梯度。
对于一个二维空间的曲线来说,梯度就是其切线的方向。如下图所示:
而对于更高维空间的函数来说,梯度由所有变量的偏导数决定。
其表达式如下所示:
[ abla f({ heta}) = ( frac{partial f({ heta})}{partial heta_1} , frac{partial f({ heta})}{partial heta_2} , ... , frac{partial f({ heta})}{partial heta_n} )]
在机器学习中,我们主要是用梯度下降算法来最小化代价函数,记做:
[ heta ^* = arg min L( heta)]
其中,L是代价函数,是参数。
梯度下降算法的主体逻辑很简单,就是沿着梯度的方向一直下降,直到参数收敛为止。
记做:
[ heta ^{k + 1}_i = heta^{k}_i - lambda abla f( heta^{k})]
这里有几点需要说明:
线性回归的梯度下降
有了上面的知识,我们可以回到线性模型代价函数的梯度下降算法实现了。
首先,根据代价函数我们可以得到梯度向量如下:
[ abla f({ heta}) = (frac{partial L( heta)}{ partial heta_{0}}, frac{ partial L( heta)}{ partial heta_{1}}) = (frac {2}{m} sum_{i=1}^{m}( heta_{0} + heta_{1} * x^{i} - y^{i}) , frac {2}{m} sum_{i=1}^{m}( heta_{0} + heta_{1} * x^{i} - y^{i}) x^{i})]
接着,将每个偏导数带入迭代的公式中,得到:
[ heta_{0} := heta_{0} - lambda frac{partial L( heta_{0})}{ partial heta_{0}} = heta_{0} - frac {2 lambda }{m} sum_{i=1}^{m}( heta_{0} + heta_{1} * x^{i} - y^{i}) heta_{1} := heta_{1} - lambda frac{partial L( heta_{1})}{ partial heta_{1}} = heta_{1} - frac {2 lambda }{m} sum_{i=1}^{m}( heta_{0} + heta_{1} * x^{i} - y^{i}) x^{i}]
由此就可以通过代码实现我们的梯度下降算法了,算法逻辑并不复杂:
learning_rate = 0.01
def gradient_descent(x, y):
t0 = 10
t1 = 10
delta = 0.001
for times in range(1000):
sum1 = 0
sum2 = 0
for i in range(len(x)):
sum1 += (t0 + t1 * x[i] - y[i])
sum2 += (t0 + t1 * x[i] - y[i]) * x[i]
t0_ = t0 - 2 * learning_rate * sum1 / len(x)
t1_ = t1 - 2 * learning_rate * sum2 / len(x)
print('Times: {}, gradient: [{}, {}]'.format(times, t0_, t1_))
if (abs(t0 - t0_) < delta and abs(t1 - t1_) < delta):
print('Gradient descent finish')
return t0_, t1_
t0 = t0_
t1 = t1_
print('Gradient descent too many times')
return t0, t1
这段代码说明如下:
如果我们将算法迭代过程中求得的线性模式绘制出来,可以得到下面这幅动态图:
最后算法得到的结果如下:
Times: 657, gradient: [5.196562662718697, 1.952931052920264]
Times: 658, gradient: [5.195558390180733, 1.9530753071808193]
Times: 659, gradient: [5.194558335124868, 1.9532189556399233]
Times: 660, gradient: [5.193562479839619, 1.9533620008416623]
Gradient descent finish
从输出中可以看出,算法迭代了660次就收敛了。这时的结果[5.193562479839619, 1.9533620008416623],这已经比较接近目标值 [5, 2]了。如果需要更高的精度,可以将delta的值调的更小,当然,此时会需要更多的迭代次数。
高维扩展
虽然我们举的例子是二维的,但是对于更高维的情况也是类似的。同样是根据迭代的公式进行运算即可:
[ heta_{i} = heta_{i} - lambda frac {partial L( heta)}{partial heta_i} = heta_{i} - frac{2lambda}{m} sum_{i=1}^{m}(h_ heta(x^{k})-y^k)x_i^k]
这里的下标i表示第i个参数,上标k表示第k个数据。
梯度下降家族BGD
在上面的内容中我们看到,算法的每一次迭代都需要把所有样本进行遍历处理。这种做法称为之Batch Gradient Descent,简称BGD。作为演示示例只有10条数据,这是没有问题的。
但在实际的项目中,数据集的数量可能是几百万几千万条,这时候每一步迭代的计算量就会非常的大了。
于是就有了下面两个变种。
SGD
Stochastic Gradient Descent,简称SGD,这种算法是每次从样本集中仅仅选择一个样本来进行计算。很显然,这样做算法在每一步的计算量一下就少了很多。
其算法公式如下:
[ heta_{i} = heta_{i} - lambda frac {partial L( heta)}{partial heta_i} = heta_{i} - lambda(h_ heta(x^k)-y^k)x_i^k]
当然,减少算法计算量也是有代价的,那就是:算法结果会强依赖于随机取到的数据情况,这可能会导致算法的最终结果不太令人满意。
MBGD
以上两种做法其实是两个极端,一个是每次用到了所有数据,另一个是每次只用一个数据。
我们自然就会想到两者取其中的方法:每次选择一小部分数据进行迭代。这样既避免了数据集过大导致每次迭代计算量过大的问题,也避免了单个数据对算法的影响。
这种算法称之为Mini-batch Gradient Descent,简称MBGD。
其算法公式如下:
[ heta_{i} = heta_{i} - lambda frac {partial L( heta)}{partial heta_i} = heta_{i} - frac{2lambda}{m} sum_{i=a}^{a + b}(h_ heta(x^k)-y^k)x_i^k]
当然,我们可以认为SGD是Mini-batch为1的特例。
针对上面提到的算法变种,该如何选择呢?
下面是Andrew Ng给出的建议:
下表是 Optimization for Deep Learning 中对三种算法的对比
方法准确性更新速度内存占用在线学习BGD好慢高否SGD好(with annealing)快低是MBGD好中等中等是
算法优化
式7是算法的基本形式,在这个基础上有很多人进行了更多的研究。接下来我们介绍几种梯度下降算法的优化方法。
Momentum
Momentum是动量的意思。这个算法的思想就是借助了动力学的模型:每次算法的迭代会使用到上一次的速度作为依据。
算法的公式如下:
[v^t = gamma v^{t - 1} + lambda abla f( heta) heta = heta - v_t]
对比式7可以看出,这个算法的主要区别就是引入了,并且,每个时刻的受前一个时刻的影响。
从形式上看,动量算法引入了变量 v 充当速度角色——它代表参数在参数空间移动的方向和速率。速度被设为负梯度的指数衰减平均。名称动量来自物理类比,根据牛顿运动定律,负梯度是移动参数空间中粒子的力。动量在物理学上定义为质量乘以速度。在动量学习算法中,我们假设是单位质量,因此速度向量 v 也可以看作是粒子的动量。
对于可以取值0,而是一个常量,设为0.9是一个比较好的选择。
下图是momentum算法的效果对比:
对原来的算法稍加修改就可以增加动量效果:
def gradient_descent_with_momentum(x, y):
t0 = 10
t1 = 10
delta = 0.001
v0 = 0
v1 = 0
gamma = 0.9
for times in range(1000):
sum1 = 0
sum2 = 0
for i in range(len(x)):
sum1 += (t0 + t1 * x[i] - y[i])
sum2 += (t0 + t1 * x[i] - y[i]) * x[i]
v0 = gamma * v0 + 2 * learning_rate * sum1 / len(x)
v1 = gamma * v1 + 2 * learning_rate * sum2 / len(x)
t0_ = t0 - v0
t1_ = t1 - v1
print('Times: {}, gradient: [{}, {}]'.format(times, t0_, t1_))
if (abs(t0 - t0_) < delta and abs(t1 - t1_) < delta):
print('Gradient descent finish')
return t0_, t1_
t0 = t0_
t1 = t1_
print('Gradient descent too many times')
return t0, t1
以下是该算法的输出:
Times: 125, gradient: [4.955453758569991, 2.000005017897775]
Times: 126, gradient: [4.955309381126545, 1.9956928964532015]
Times: 127, gradient: [4.9542964317327005, 1.9855674828684156]
Times: 128, gradient: [4.9536358220657, 1.9781180992510465]
Times: 129, gradient: [4.95412496254411, 1.9788858350530971]
Gradient descent finish
从结果可以看出,改进的算法只用了129次迭代就收敛了。速度比原来660次快了很多。
同样的,我们可以把算法计算的过程做成动态图:
对比原始的算法过程可以看出,改进算法最大的区别是:在寻找目标值时会在最终结果上下跳动,但是越往后跳动的幅度越小,这也就是动量所产生的效果。
Learning Rate 优化
至此,你可能还是好奇该如何设定学习率的值。
事实上,这个值的选取需要一定的经验或者反复尝试才能确定。
关键在于,这个值的选取不能过大也不能过小。
如果这个值过小,会导致每一次迭代的步长很小,其结果就是算法需要迭代非常多的次数。
那么,如果这个值过大会怎么样呢?其结果就是:算法可能在结果的周围来回震荡,却落不到目标的点上。下面这幅图描述了这个现象:
事实上,学习率的取值未必一定要是一个常数,关于这个值的设定有很多的研究。
下面是比较常见的一些改进算法。
AdaGrad
AdaGrad是Adaptive Gradient的简写,该算法会为每个参数设定不同的学习率。它使用历史梯度的平方和作为基础来进行计算。
其算法公式如下:
[ heta_i = heta_i - frac{lambda}{sqrt{G_t + epsilon}} abla f( heta)]
对比式7,这里的改动就在于分号下面的根号。
根号中有两个符号,第二个符号比较好理解,它就是为了避免除0而人为引入的一个很小的常数,例如可以设为:0.001。
第一个符号的表达式展开如下:
[G_t = sum_{i = 1}^{t} abla f( heta){i} abla f( heta){i}^{T}]
这个值其实是历史中每次梯度的平方的累加和。
AdaGrad算法能够在训练中自动的对learning rate进行调整,对于出现频率较低参数采用较大的学习率;相反,对于出现频率较高的参数采用较小的学习率。因此,Adagrad非常适合处理稀疏数据。
但该算法的缺点是它可能导致学习率非常小以至于算法收敛非常的慢。
关于这个算法的直观解释可以看李宏毅教授的视频课程:ML Lecture 3-1: Gradient Descent。
RMSProp
RMS是Root Mean Square的简写。RMSProp是AI教父Geoff Hinton提出的一种自适应学习率方法。AdaGrad会累加之前所有的梯度平方,而RMSProp仅仅是计算对应的平均值,因此可缓解Adagrad算法学习率下降较快的问题。
该算法的公式如下:
[E[ abla f( heta_{i})^2]^{t} = gamma E[ abla f( heta_{i})^2]^{t - 1} + (1-gamma)( abla f( heta_{i})^{t})^{2} heta_i = heta_i - frac{lambda}{sqrt{E[g^2]^{t+1} + epsilon}} abla f( heta_{i})]
类似的,是为了避免除0而引入。 是衰退参数,通常设为0.9。
这里的 是t时刻梯度平方的平均值。
Adam
Adam是Adaptive Moment Estimation的简写。它利用梯度的一阶矩估计和二阶矩估计动态调整每个参数的学习率。
Adam的优点主要在于经过偏置校正后,每一次迭代学习率都有个确定范围,使得参数比较平稳。
该算法公式如下:
[m^{t} = eta_{1} m^{t-1} + (1-eta_{1}) abla f( heta) v^{t} = eta_{2} v^{t-1} + (1-eta_{2}) abla f( heta)^2 widehat{m}^{t} = frac{m^{t}}{1 - eta^{t}_1} widehat{v}^{t} = frac{v^{t}}{1 - eta^{t}_2} heta = heta - frac{lambda}{sqrt{widehat{v}^{t}} + epsilon}widehat{m}^{t}]
,分别是对梯度的一阶矩估计和二阶矩估计。, 是对,的校正,这样可以近似为对期望的无偏估计。
Adam算法的提出者建议 默认值为0.9,默认值为0.999,默认值为 。
在实际应用中 ,Adam较为常用,它可以比较快地得到一个预估结果。
优化小结
这里我们列举了几种优化算法。它们很难说哪种最好,不同的算法适合于不同的场景。在实际的工程中,可能需要逐个尝试一下才能确定选择哪一个,这个过程也是目前现阶段AI项目要经历的工序之一。
实际上,该方面的研究远不止于此,如果有兴趣,可以继续阅读 《Sebastian Ruder: An overview of gradient descent optimization algorithms》 这篇论文或者 Optimization for Deep Learning 这个Slides进行更多的研究。
由于篇幅所限,这里不再继续展开了。
算法限制
梯度下降算法存在一定的限制。首先,它要求函数必须是可微分的,对于不可微的函数,无法使用这种方法。
除此之外,在某些情况下,使用梯度下降算法在接近极值点的时候可能收敛速度很慢,或者产生Z字形的震荡。这一点需要通过调整学习率来回避。
另外,梯度下降还会遇到下面两类问题。
局部最小值
局部最小值(Local Minima)指的是,我们找到的最小值仅仅是一个区域内的最小值,而并非全局的。由于算法的起点是随意取的,以下面这个图形为例,我们很容易落到局部最小值的点里面。
这就是好像你从上顶往下走,你第一次走到的平台未必是山脚,它有可能只是半山腰的一个平台的而已。
算法的起点决定了算法收敛的速度以及是否会落到局部最小值上。
坏消息是,目前似乎没有特别好的方法来确定选取那个点作为起点是比较好的,这就有一点看运气的成分了。多次尝试不同的随机点或许是一个比较好的方法,这也就是为什么做算法的优化这项工作是特别消耗时间的了。
但好消息是:
鞍点
除了Local Minima,在梯度下降的过程中,还有可能遇到另外一种情况,即:鞍点(Saddle Point)。鞍点指的是我们找到点某个点确实是梯度为0,但它却不是函数的极值,它的周围既有比它小的值,也有比它大的值。这就好像马鞍一样。
如下图所示:
多类随机函数表现出以下性质:在低维空间中,局部极值很普遍。但在高维空间中,局部极值比较少见,而鞍点则很常见。
不过对于鞍点,可以通过数学方法Hessian矩阵来确定。关于这点,这里就不再展开了,有兴趣的读者可以以这里提供的几个链接继续探索。
参考资料与推荐读物
㈤ 机器学习算法有哪些,最常用是哪些几种,有什么优点
楼主肯定对机器学习了解不多才会提这种问题。这问题专业程度看起来和“机器学习工程师”这词汇一样。
机器学习,基础的PCA模型理论,贝叶斯,boost,Adaboost,
模式识别中的各种特征,诸如Hog,Haar,SIFT等
深度学习里的DBN,CNN,BP,RBM等等。
非专业出身,只是略懂一点。
没有常用的,只是针对需求有具体的设计,或者需要自己全新设计一个合适的算法,现在最热门的算是CNN(convolutional neural networks)卷积神经网络了。
优点:不需要训练获取特征,在学习过程中自动提取图像中的特征,免去了常规方法中,大量训练样本的时间。在样本足够大的情况下,能够得到非常精确的识别结果。一般都能95%+的正确率。
缺点:硬件要求高,CUDA的并行框架算是用的很火的了。但是一般的台式机跑一个Demo花费的时间长资源占用高。不过这也是这块算法的通病。