Skip to content

Latest commit

 

History

History
360 lines (270 loc) · 25.9 KB

advice-for-appying-and-system-design.md

File metadata and controls

360 lines (270 loc) · 25.9 KB

打造实用的机器学习系统

应用机器学习算法的建议

这部分介绍如果改进机器学习系统性能的一些建议。

对一个线性回归模型,在得到学习参数后,如果要将假设函数放到一组新的房屋样本上测试。如果发现在预测房价时产生了巨大的误差,现在你的问题是要想改进这个算法,接下来应该怎么办?可能有如下办法:

  1. 一种办法是使用更多的训练样本
  2. 尝试选用更少的特征集
    • 如果你有一系列特征比如 x1,x2,x3 等等。也许可以从这些特征中仔细挑选一小部分来防止过拟合
  3. 尝试选用更多的特征
    • 也许目前的特征集,对你来讲并不是很有帮助
  4. 尝试增加多项式特征的方法
    • 比如 x1 的平方, x2 的平方, x1,x2 的乘积
  5. 减小正则化参数 λ 的值
  6. 增大正则化参数 λ 的值

不应该随机选择上面的某种方法来改进算法,而是运用一些机器学习诊断法来决定上面哪些方法对算法是有效的。

评估一个假设函数 Evaluating a Hypothesis

对于一个训练集,在计算算法的参数的时候,考虑的是选择参数以使训练误差(Cost function)最小化,有人认为得到一个非常小的训练误差一定是一件好事,但仅仅是因为这个假设具有很小的训练误差,并不能说明它是一个好的假设函数。尤其是考虑到过拟合的假设函数,所以这这个方法推广到新的训练集上是不适用的。

对于这个简单的例子,可以对假设函数 h(x) 进行画图,然后观察图形趋势,但对于特征变量不止一个的这种一般情况,还有像有很多特征变量的问题,想要通过画出假设函数来进行观察,就会变得很难甚至是不可能实现。

因此,需要另一种方法来评估假设函数过拟合检验。

为了检验算法是否过拟合,将数据分成训练集(Training set)和测试集(Test set),通常用70%的数据作为训练集,用剩下30%的数据作为测试集。

注意训练集和测试集均要含有各种类型的数据,通常要对数据进行“洗牌”(随机排序),然后再分成训练集和测试集。

测试集评估在通过训练集让模型学习得出其参数后,对测试集运用该模型,有两种方式计算误差:

  1. 对于线性回归模型,利用测试集数据计算代价函数 J:

  1. 对于逻辑回归模型:

    • 利用测试集数据计算代价函数 J

    • 计算对于每一个测试集样本的误分类的比率,然后求平均:

模型选择和交叉验证集 Model Selection

假设要在10个不同次数的二项式模型间选择:

显然越高次数的多项式模型越能够适应训练数据集,但也可能意味着过拟合(Overfitting),应该选择一个更能适应一般情况的模型。

这时需要使用交叉验证集(Cross-validation set)来帮助选择模型。​

  • 交叉验证集通常是独立于训练集和测试集的
  • 通常用6/2/2的划分,即60%的数据作为训练集,20%的数据作为交叉验证集,20%的数据作为测试集

模型选择方法:

  1. 使用训练集训练出10个模型
  2. 用10个模型分别对交叉验证集计算得出交叉验证误差(代价函数的值)
  3. 选取代价函数值最小的模型
  4. 用步骤3中选出的模型对测试集计算得出测试误差(代价函数的值)

训练误差(Training error):

交叉验证误差(Cross Validation error):

测试误差(Test error):

虽然现实中有人用测试集来计算交叉验证误差,并用同样的测试集来计算测试误差,但这并不是一个好的做法,除非有大量的训练数据.. Anyway,不推荐这种做法,还是将数据集分成训练数据、交叉验证数据和测试数据吧。

偏差(Bias)和方差(Variance)

如果一个机器学习算法表现不理想,多半出现两种情况:

  • 要么是偏差比较大,要么是方差比较大。
  • 换句话说,出现的情况要么是欠拟合,要么是过拟合问题

例如下图,依次对应的是欠拟合、正常、过拟合:

为了分析模型性能,通常会将训练集和交叉验证集的代价函数误差与多项式的次数绘制在同一张图表上来帮助分析:

上图可看出:

  • 对于训练集,当 d 较小时,模型拟合程度更低,误差较大;随着 d 的增长,拟合程度提高,误差减小。
  • 对于交叉验证集,当 d 较小时,模型拟合程度低,误差较大;但是随着 d 的增长,误差呈现先减小后增大的趋势,转折点是模型开始过拟合训练数据集的时候。

根据上面的图表,还可以总结判断高方差、高偏差的方法:

  • 训练集误差和交叉验证集误差近似时:
    • 高偏差 / 欠拟合
  • 交叉验证集误差远大于训练集误差时:
    • 高方差 / 过拟合

正则化和偏差/方差

在训练模型时,一般会用正则化方法来防止过拟合。但是可能会正则化程度太高或太小,即在选择λ的值时也需要思考与此前选择多项式模型次数类似的问题。如下图是不同的 λ 对应不同拟合程度:

通常会尝试一系列的 λ 值,以测试最佳选择:

  1. 使用训练集训练出12个不同程度正则化的模型
  2. 用12个模型分别对交叉验证集计算的出交叉验证误差
  3. 选择得出交叉验证误差最小的模型
  4. 运用步骤3中选出模型对测试集计算得出推广误差

也可以同时将训练集和交叉验证集模型的代价函数误差与λ的值绘制在一张图表上(如下图),可以看出训练集误差和 λ 的关系如下:

总结:

  • λ 较小时,训练集误差较小(过拟合)而交叉验证集误差较大
  • λ 的增加,训练集误差不断增加(欠拟合),而交叉验证集误差则是先减小后增加

学习曲线

学习曲线是用来判断某一个学习算法是否处于偏差、方差问题,它是学习算法的一个很好的合理检验sanity check)。

学习曲线是将训练集误差和交叉验证集误差作为训练集样本数量( m )的函数绘制的图表。

例如,如果有100行数据,从1行数据开始,逐渐学习更多行。 思想是:当训练较少数据时,训练模型将能非常完美地适应较少的训练数据,但是训练的模型不能很好地适应交叉验证集或测试集。

如何利用学习曲线识别高偏差/欠拟合

  • 作为例子,尝试一条直线来拟合下面的数据,可以看出,无论训练集增长到多大误差都不会有太大改观(始终保持很高的error)
  • 即:在高偏差/欠拟合的情况下,增加训练集不一定有帮助。

如何利用学习曲线识别高方差/过拟合

  • 假设使用一个非常高次的多项式模型,并且正则化非常小,可以看出,当交叉验证集误差远大于训练集误差时,往训练集增加更多数据可以提高模型的效果。
  • 即:在高方差/过拟合的情况下,增加训练集可能提高算法效果。

总结:决定下一步做什么

  1. 增加训练样本数 m ——解决高方差
  2. 减少特征的数量 n ——解决高方差
  3. 获得更多的特征 n ——解决高偏差
  4. 增加多项式特征 n ——解决高偏差
  5. 减少正则化程度 λ ——解决高偏差
  6. 增加正则化程度 λ ——解决高方差

神经网络的方差和偏差:

神经网络神经元个数选择:

  • 使用较小的神经网络类似于参数较少的情况,容易导致高偏差和欠拟合,但计算代价较小
  • 使用较大的神经网络,类似于参数较多的情况,容易导致高方差和过拟合,虽然计算代价比较大,但是可以通过正则化手段来调整而更加适应数据
  • 通常选择较大的神经网络并采用正则化处理会比采用较小的神经网络效果要好

神经网络隐藏层层数的选择:

  • 通常从一层开始逐渐增加层数
  • 为了更好地作选择,可以把数据分为训练集、交叉验证集和测试集,针对不同隐藏层层数的神经网络训练神经网络,然后选择交叉验证集代价最小的神经网络。

机器学习系统设计

误差分析 Error Analysis

Andrew说他开始研究一个机器学习问题时,最多只会花一天的时间试图很快得出结果,即便效果不好。 也就是刚开始根本不用复杂的系统,只是很快的得到结果。即便运行的不完美,但也它运行一遍,最后通过交叉验证来检验数据。

之后通过画学习曲线,以及检验误差,找出算法是否有高偏差和高方差的问题,或者其他问题。 之后,再来决定用更多的数据训练,或者加入更多的特征变量是否有用。

这么做的原因: 在刚接触机器学习问题时,并不能提前知道是否需要复杂的特征变量,或者需要更多的数据,还是别的。 因为你缺少证据,缺少学习曲线。 因此,很难知道该如何提高算法性能。 但当你实践一个非常简单即便不完美的方法时,可以通过画出学习曲线来做出进一步的选择。 可以用这种方式避免类似电脑编程里的过早优化问题。

理念是:必须用证据来指引决策。 包括怎样分配自己的时间来优化算法,而不是仅仅凭直觉,而凭直觉一般都是错误的。 除了画出学习曲线之外,另外是做误差分析。

比如在构造垃圾邮件分类器时,会看一看交叉验证数据集,然后看看哪些邮件被算法错误地分类。 通过被算法错误分类的垃圾邮件与非垃圾邮件,可以发现某些系统性的规律:什么类型的邮件总是被错误分类。 经常地这样做之后,这个过程能启发你构造新的特征变量,或者告诉你:现在这个系统的短处,然后启发你如何去提高它。

构建一个学习算法的推荐方法为:

  1. 从一个简单的能快速实现的算法开始,实现该算法并用交叉验证集数据测试这个算法
  2. 绘制学习曲线,决定是增加更多数据,或者添加更多特征,还是其他选择
  3. 进行误差分析:人工检查交叉验证集中算法中产生预测误差的样本,看看这些样本是否有某种系统化的趋势

以垃圾邮件过滤器为例:

  • 误差分析要做的既是检验交叉验证集中算法产生错误预测的所有邮件,看是否能将这些邮件按照类分组。例如医药品垃圾邮件,仿冒品垃圾邮件或者密码窃取邮件等。然后看分类器对哪一组邮件的预测误差最大,并着手优化。
  • 思考怎样能改进分类器。例如,发现是否缺少某些特征,记下这些特征出现的次数。
  • 例如记录下错误拼写出现了多少次,异常的邮件路由情况出现了多少次等等,然后从出现次数最多的情况开始着手优化。

误差分析并不总能帮助判断应该如何改进。有时需要尝试不同的模型,然后比较,在模型比较时,用数值来判断哪一个模型更好更有效,通常是看交叉验证集的误差。

  • 在垃圾邮件分类器例子中,是否应该将discount / discounts / discounted / discounting处理成同一个词?如果这样做可以改善算法,那应该采用一些截词软件。误差分析不能帮助做出这类判断,只能尝试采用和不采用截词软件这两种不同方案,然后根据数值检验的结果来判断哪一种更好。

因此,当在构造学习算法的时候,你会尝试很多新想法,实现很多版本的学习算法。 如果每一次实践新想法时,你都要手动地检测这些例子,去看是表现差还是表现好,那么这很难让你做出决定。到底是否使用词干提取,是否区分大小写。

但是通过一个量化的数值评估,你可以看这个数字,误差是变大还是变小了。你可以通过它更快地实践你的新想法,它基本上非常直观地告诉你:你的想法是提高了算法表现,还是让它变得更坏,这会提高实践算法的速度。 所以强烈推荐在交叉验证集上实施误差分析,而不是在测试集上。但是,还是有一些人会在测试集上来做误差分析。

总结一下:

  • 当在研究一个新的机器学习问题时,总是推荐实现一个较为简单快速、即便不完美的算法。然而几乎从未见过人们这样做。大家经常干的事情是:花费大量的时间在构造算法上,构造他们以为的简单的方法。因此,不要担心你的算法太简单,或者太不完美,而是尽可能快地实现你的算法。
  • 当你有了初始的实现之后,它会变成一个非常有力的工具,来帮助你决定下一步的做法。因为可以看看算法造成的错误,通过误差分析,看看犯了什么错,然后来决定优化的方式。
  • 假设你有了一个快速而不完美的算法实现,又有一个数值的评估,这会帮助你尝试新的想法,快速地发现你尝试的这些想法是否能够提高算法的表现,从而你会更快地做出决定,在算法中放弃什么,吸收什么误差分析可以帮助系统化地选择该做什么。

类偏斜的误差度量

前面介绍了误差分析,以及设定误差度量值的重要性。有了算法的评估和误差度量值。还有一件重要的事情是使用一个合适的误差度量值,这有时会对于学习算法造成非常微妙的影响,这件重要的事情就是偏斜类(Skewed classes)的问题。类偏斜情况表现为训练集中有非常多的同一种类的样本,只有很少或没有其他类的样本。

例如希望用算法预测癌症是否是恶性的,在训练集中,只有0.5%的实例是恶性肿瘤。假设编写一个非学习算法,在所有情况下都预测肿瘤是良性的,那么误差只有0.5%。然而通过训练得到的神经网络算法却有1%的误差。这时,误差的大小是不能视为评判算法效果的依据的。

查准率Precision)和查全率Recall

将算法预测的结果分成四种情况:

  1. 正确肯定True Positive, TP):预测为真,实际为真
  2. 正确否定True Negative, TN):预测为假,实际为假
  3. 错误肯定False Positive, FP):预测为真,实际为假
  4. 错误否定False Negative, FN):预测为假,实际为真

查准率=TP/(TP+FP)

  • 例,在所有预测有恶性肿瘤的病人中,实际上有恶性肿瘤的病人的百分比,越高越好。

查全率=TP/(TP+FN)

  • 例,在所有实际上有恶性肿瘤的病人中,成功预测有恶性肿瘤的病人的百分比,越高越好。

这样,对于刚才那个总是预测病人肿瘤为良性的算法,其查全率是0。

可以对结果构建误差分析表格,也称作混淆矩阵(Confusion Matrix),如下:

预测值
Positive Negtive
实际值 Positive TP FN
Negtive FP TN

查准率和查全率之间的权衡

在分类问题中,会设定一个阈值( threshold ),当 hθ(x) ≥ gethreshold 时,预测输出1,否则输出0。

如何选择阀值:

  • 一种方法是计算F1F1 Score,也叫做F Score),其计算公式为:

原则是:选择得到更高 F1 值的阈值

机器学习的数据

有一项研究,尝试通过机器学习算法区分常见的易混淆的单词。研究者尝试了许多种不同的算法,并发现数据量非常大时,这些不同类型的算法效果都很好。

例子:

For breakfast I ate (to, two, too) eggs.

在这个例子中,(to, two, too) 是一个易混淆的单词子。于是研究员把诸如这样的机器学习问题,当做一类监督学习问题,并尝试将其分类,什么样的词,在一个英文句子特定的位置,才是合适的。

他们用了几种不同的学习算法,这些算法在(2001年进行的研究)当时都已经被公认是比较领先的:

  • perceptron 算法
  • Winnow算法。很类似于回归问题,但在一些方面又有所不同,过去用得比较多,但现在用得不太多
  • 一种基于内存的学习算法,现在也用得比较少了
  • 一个朴素贝叶斯算法

什么时候会希望获得更多数据,而非修改算法? 他们改变训练数据集的大小,并尝试将这些算法用于不同大小训练集,结果如图:

图中趋势非常明显。

  • 首先对于大部分算法,都具有相似的性能
  • 其次,随着训练数据集的增大,在横轴上代表以百万为单位的训练集大小,从0.1个百万到1000百万,也就是到了10亿规模的训练集的样本,这些算法的性能也都对应地增强了。

由于这项原始的研究非常具有影响力,因此已经系列不同的研究显示了类似的结果。 结果表明,许多不同的学习算法有时倾向于表现出非常相似的表现,但是真正能提高性能的,是能够给一个算法大量的训练数据。

像这样的结果,引起了一种在机器学习中的普遍共识:"取得成功的人不是拥有最好算法的人,而是拥有最多数据的人"。

那这种说法在什么时候是真,什么时候是假呢? 因为如果有一个学习算法,并且如果这种说法是真的,那么得到大量的数据通常是保证具有一个高性能算法的最佳方式,而不是去争辩应该用什么样的算法。

假如有这样一些假设,在这些假设下有大量认为有用的训练集,假设在机器学习问题中,特征值 x 包含了足够的信息,这些信息可以帮助准确地预测 y

  • 例如,如果采用了一些容易混淆的词,如:twototoo,假如说它能够描述 x ,捕捉到需要填写的空白处周围的词语,那么特征捕捉到之后,就希望有对于“早饭我吃了__个鸡蛋”,那么这就有大量的信息来告诉我中间我需要填的词是“两个”(two),而不是单词totoo,因此特征捕捉,哪怕是周围词语中的一个词,就能够给我足够的信息来确定出标签 y 是什么。换句话说,从这三组易混淆的词中,我应该选什么词来填空。

来看一看,大量的数据有帮助的情况。

  1. 假设特征值有足够的信息来预测 y 值,假设使用一种需要大量参数的学习算法。

    • 比如有很多特征的逻辑回归或线性回归,或者用带有许多隐藏单元的神经网络。这些都是非常强大的学习算法,它们有很多参数,这些参数可以拟合非常复杂的函数。
    • 这些算法想象成低偏差算法,因为能够拟合非常复杂的函数,而且因为有非常强大的学习算法,这些学习算法能够拟合非常复杂的函数。很有可能,如果用这些数据运行这些算法,这种算法能很好地拟合训练集,因此,训练误差就会很低了。
  2. 现在假设使用了非常非常大的训练集,在这种情况下,尽管希望有很多参数,但是如果训练集比参数的数量还大,甚至是更多,那么这些算法就不太可能会过度拟合。也就是说训练误差有希望接近测试误差。

另一种考虑这个问题的角度是为了有一个高性能的学习算法,希望它不要有高的偏差和方差。

  • 偏差问题,将通过一个具有很多参数的学习算法来解决
  • 方差问题,通过用非常大的训练集来保证

从根本上来说,这是一个关键的假设:

  • 特征值有足够的信息量,且有一类很好的函数,这是为什么能保证低误差的关键所在。
  • 它有大量的训练数据集,这能保证得到更多的方差值

总结关键的检验标准:

  • 首先,一个人类专家看到了特征值 x ,能很有信心的预测出 y 值吗?这可以证明 y 可以根据特征值 x 被准确地预测出来?
  • 其次,能得到一组庞大的训练集并且在训练出一个有很多参数的学习算法吗?

如果都能得到肯定答案,那么你很可能可以得到一个性能很好的学习算法。

Jupyter Notebook编程练习

回到顶部