斯坦福大学《Machine Learning》第6周学习笔记

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/junjun150013652/article/details/80753719

Advice for Applying Machine Learning应用机器学习的建议

Evaluating a Learning Algorithm评估学习算法

Deciding What to Try Next 决定接下来要做什么

假如你在开发 一个机器学习系统 或者想试着改进 一个机器学习系统的性能 你应如何决定?   

使用 预测房价的学习例子 假如你已经完成了正则化线性回归 也就是最小化代价函数J的值 假如 在你得到你的学习参数以后 如果你要将你的假设函数 放到一组新的房屋样本上进行测试 假如说你发现在预测房价时 产生了巨大的误差   现在你的问题是 要想改进这个算法 接下来应该怎么办?   

方法一:使用更多的训练样本 

方法二:尝试选用更少的特征集

方法三:……

我们列出以上的优化单子 每一种尝试都可能会花费你大量的时间和精力,大多数人用来选择这些方法的标准 是凭感觉的  但是最后的结果却很糟糕,因此我们希望在花费大量时间完成这些工作之前 我们就能知道其效果如何

  

幸运的是 有一系列简单的方法 能让你事半功倍 排除掉单子上的 至少一半的方法 留下那些确实有前途的方法 同时也有一种很简单的方法 只要你使用 就能很轻松地排除掉很多选择   从而为你节省 大量不必要花费的时间  

它们被称为"机器学习诊断法"   “诊断法”的意思是 这是一种测试法 你通过执行这种测试 能够深入了解 某种算法到底是否有用 这通常也能够告诉你 要想改进一种算法的效果 什么样的尝试才是有意义的 这些诊断法的执行和实现通常也是需要花费很多时间 但是它们是值得的

Evaluating a Hypothesis 评估一个假设

下面介绍怎样用你学过的算法来评估假设函数 并以此为基础来 讨论如何避免 过拟合和欠拟合的问题 

当我们确定学习算法的参数的时候 我们考虑的是选择参量来使训练误差最小化 但是过拟合假设函数并不是好事 那么该如何判断一个假设函数是过拟合的呢?

特征变量只有一个的时候 我们可以 对假设函数 h(x) 进行画图 然后观察图形趋势 但对于特征变量很多的这种一般情况 想要通过画出假设函数来进行观察 就会变得很难甚至是不可能实现 因此 我们需要另一种方法来评估我们的假设函数 如下给出了一种评估假设函数的标准方法 

假设我们有一组数据组 为了确保我们可以评估我们的假设函数 我们要做的是 将这些数据分成两部分 训练集和测试集 其中一种典型的分割方法是 按比例 7:3划分 如果这组数据有某种规律或顺序的话 要随机选择 

你通过以下步骤训练和测试你的学习算法 首先对70%的训练集进行学习得到参数θ 也最小化训练误差J(θ) 然后通过30%的测试集来计算出测试误差来评估你的学习算法,以上左边是线性回归问题,右边是逻辑回归问题,它们有个不同就是,逻辑回归问题在计算出测试误差的时候,不再是使用代价函数,而是使用叫误分类率 也被称为0/1错分率的方式 0/1表示了 你预测到的正确或错误样本的情况

Model Selection and Train/Validation/Test Sets模型选择和训练/验证/测试集

假如你想要确定对于某组数据 最合适的多项式次数是几次 怎样选用正确的特征来构造学习算法 或者假如你需要正确选择 学习算法中的正则化参数λ 你应该怎样做呢? 这些问题我们称之为模型选择问题 在我们对于这一问题的讨论中 我们还将提到 如何将数据分为三组 也就是训练集、验证集和测试集 而不仅仅是前面提到的两组数据 

我们先来考虑模型选择问题 假如说你现在要选择能最好地拟合你数据的多项式次数 换句话说 你应该选择一次函数 二次函数 还是三次函数呢 等等一直到十次函数 这里用 d 来表示你应该选择的多项式次数  所以除了你要确定的参数θ之外 你还要考虑确定一个参数d  第一个选择是 d=1 也就表示线性(一次)方程 我们也可以选择d=2或者3 等等一直到d=10 因此 我们想确定这个多出来的参数d最适当的取值 

具体地说 比如你想要选择一个模型  那就从这10个模型中 选择一个最适当的多项式次数 并且用这个模型进行估计 预测你的假设能否很好地推广到新的样本上 那么你可以这样做 你可以先选择第一个模型 然后求训练误差的最小值 这样你就会得到一个参数向量θ 然后你再选择第n个模型 n次函数模型  进行同样的过程 这样你会得到另一个参数向量 θ 为了区别这些不同的 参数向量θ 我想用上标(1) 上标(2)来表示 这里的上标(n)表示的是 在调整第n个模型  也就是n次函数在和训练数据拟合的过程中得到的参数 

接下来我们要做的是 对所有这些模型 求出测试集误差 因此 我可以算出 Jtest(θ(1))  Jtest(θ(2)) Jtest(θ(3)) 以此类推 也就是对于每一个模型对应的假设 都计算出其作用于测试集的表现如何 接下来为了确定选择哪一个模型最好 我要做的是   看看这些模型中 哪一个对应的测试集误差最小  那么对于这一个例子 我们假设最终选择了五次多项式模型 

这里有一个问题是 这样做不能公平地说明 我的假设推广到一般时的效果 其原因在于 我们刚才是使用的测试集 跟假设拟合 来得到的 多项式次数d 这个参数 这也就是说 我们选择了一个 能够最好地拟合测试集的 参数d的值 因此 我们的参数向量θ(5) 在拟合测试集时的结果  很可能导致一个比实际泛化误差更完美的预测结果 

同样用训练集来拟合得出的参数d 也不能较为公平地预测出 假设函数的在遇到新样本时的表现 为了解决这一问题 在模型选择中 如果我们想要评价某个假设 我们通常采用以下的方法 

给定某个数据集 将其分为三段 第一部分还是叫训练集 所以 我们还是称这部分为训练集 第二部分我把它叫做交叉验证集(cross validation set)用CV来简写“交叉验证” 有时候也直接叫验证集 不叫交叉验证集 最后一部分依然和以前一样是测试集 同时 一种典型的分割比例是 60%的训练集 20%的交叉验证集 20%的测试集 这个比例可以稍微调整 但这种分法是最典型的 

我们还是有这样一些测试集样本  那么我们的模型选择问题是这样的 和之前使用测试集来选择模型不同 我们现在要使用验证集来选择模型 也就是选择使得Jcv最小的那个假设 假设四次函数的模型有最小的交叉验证误差 也就是说拟合出最好的系数d等于4 并且我们是通过交叉验证集来完成的 因此 这样一来这个参数d 这个多项式的次数 就没有跟测试集进行拟合 这样我们就回避了测试集的嫌疑 我们可以光明正大地使用测试集 来估计所选模型的泛化误差了 好的 这就是模型选择了 

Bias vs. Variance偏差与方差

Diagnosing Bias vs. Variance诊断偏差与方差

本节要点:如何区分过拟合(高方差)和欠拟合(高偏差),从而来指导改进学习算法的多项式次数

当你运行一个学习算法时 如果这个算法的表现不理想 那么多半是出现 两种情况 要么是偏差比较大 要么是方差比较大 换句话说 出现的情况要么是欠拟合(高偏差)  要么是过拟合(高方差) 那么这两种情况 哪个和偏差有关 哪个和方差有关 或者是不是和两个都有关 搞清楚这一点非常重要 因为能判断出现的情况 是这两种情况中的哪一种 其实是一个很有效的指示器 指引着可以改进算法的 最有效的方法和途径

上面我们来画出如下这个示意图 横坐标上表示的是 多项式的次数  我们把训练集误差Jtrain 和交叉验证集误差Jcv 画在这个坐标中d等于1时欠拟合Jcv较大,d等于6时过拟合Jcv也较大 d等于2时拟合得较好Jcv最小 如果你画出Jtest(θ) 你也将得到一条类似Jcv(θ)的曲线    

这样一幅图同时也帮助我们 更好地理解偏差和方差的概念 具体来说 假设你得出了一个学习算法 而这个算法并没有表现地 如你期望那么好   所以你的交叉验证误差或者测试集误差都很大 我们应该如何判断 此时的学习算法 正处于高偏差的问题还是高方差的问题 左边的这一端 对应的就是高偏差的问题 也就是你使用了一个 过于小的多项式次数 比如d等于1 但实际上我们需要一个较高的多项式次数来拟合数据 相反地 右边这一端对应的是高方差问题   也就是说 多项式次数d 对于我们的数据来讲太大了 这幅图也提示了我们 怎样区分这两种情况 

结论总结

高偏差 - 欠拟合 - 交叉验证误差和训练误差都会很大 - 多项式次数太低

高方差 - 过拟合 - 训练误差会很小 - 交叉验证误差》训练误差 - 多项式次数太高

Regularization and Bias/Variance正则化和偏差/方差

我们知道我们正则化可以有效地防止过拟合 但正则化跟算法的偏差和方差 又有什么关系呢? 下面更深入地 探讨一下偏差和方差的问题 讨论一下两者之间 是如何相互影响的 以及和正则化之间的相互关系 

假如我们要对这样一个高阶多项式进行拟合 为了防止过拟合现象 我们要使用一个正则化项 因此我们试图通过这样一个正则化项 来让参数的值尽可能小 正则化项的求和范围照例取为 j 等于1到 m 而非 j 等于0到 m 然后我们来分析以下三种情形 

第一种情形是正则化参数 λ 取一个比较大的值 比如 λ 的值取为10000甚至更大 在这种情况下 所有这些参数 θ1 θ2 θ3 等等 将被大大惩罚 其结果是 这些参数的值将近似等于0 并且假设模型 h(x) 的值将等于或者近似等于 θ0 因此我们最终得到的假设函数 应该是这个样子 近似是一条平滑的直线 因此这个假设处于高偏差 对数据集欠拟合(underfit) 因此一条水平直线 对这个数据集来讲不是一个好的假设 

与之对应的另一种情况是 λ值很小 比如说 λ 的值等于0 在这种情况下 如果我们要拟合一个高阶多项式的话 那么我们通常会处于过拟合(overfitting)的情况 

只有当我们取一个中间大小的 既不大也不小的 λ 值时 我们才会得到一组合理的 对数据刚好拟合的 θ 参数值 那么我们应该怎样自动地选择出一个最合适的正则化参数 λ 呢?

这里我们的模型和学习参数 以及最优化目标是这样的 注意Jtrain Jcv Jtest的定义 和没有正则化的Jθ是一样的(后面我们做选择参数 λ的比较计算时,都不考虑正则化项,只有最小化代价函数Jθ时考虑λ)

下面就是我们自动选取正则化参数 λ 的方法 通常我的做法是 选取一系列我想要尝试的 λ 值 因此首先我可能考虑不使用正则化的情形 以及一系列我可能会试的值   比如说我可能从0.01 0.02 0.04开始 一直试下去 通常我会将步长设为2倍速度增长 直到一个比较大的值 在本例中以两倍步长递增的话 我们最终取值10.24 实际上我们取的是10 但已经非常接近了 因为小数点后的24对最终的结果不会有太大影响 因此 这样我就得到了12个不同的正则化参数 λ 对应的12个不同的模型 

得到这12组模型后 接下来我们要做的事情是 选用第一个模型 也就是 λ 等于0 然后最小化我们的代价函数 J(θ) 这样我们就得到了某个参数向量 θ  我使用θ上标(1) 来表示第一个参数向量θ 然后我再取第二个模型 λ 等于0.01的模型  最小化代价函数 得到一个完全不同的参数向量 θ 用 θ(2)来表示 以此类推 一直到最后一个 λ 等于10或10.24的模型 对应 θ(12) 

接下来我就可以用交叉验证集来评价这些假设和参数θ 了 因此我可以从第一个模型开始 然后是第二个模型 对每一个不同的正则化参数 λ 进行拟合 然后用交叉验证集来评价每一个模型 也即测出每一个参数 θ 在交叉验证集上的平均误差平方和 然后我就选取这12个模型中交叉验证集误差最小的 那个模型作为最终选择 

对于本例而言 假如说 最终我选择了 θ(5) 也就是五次多项式 因为此时的交叉验证集误差最小 做完这些 最后 如果我想看看该模型在测试集上的表现 我可以用经过学习得到的模型 θ(5) 来测出它对测试集的预测效果如何 再次重申 这里我们依然是用交叉验证集来拟合模型 这也是为什么我之前预留了一部分数据作为测试集的原因   这样我就可以用这部分测试集比较准确地估计出 我的参数向量 θ 对于新样本的泛化能力 这就是模型选择在选取正则化参数 λ 时的应用 

当我们改变正则化参数 λ 的值时 交叉验证集误差和训练集误差 会随之发生怎样的变化?

这里我们把训练误差 和交叉验证集误差 都定义为不包括正则化项  如果 λ 的值很小 那也就是说我们几乎没有使用正则化 因此我们有很大可能处于过拟合 - 高方差 训练误差小 验证误差大 而如果 λ 的值取的很大的时候 处于高偏差 - 欠拟合 训练误差大 验证误差大

对于真实的数据 你得到的曲线可能比这看起来更凌乱 会有很多的噪声 对某个实际的数据集 你或多或少能看出像这样的一个趋势 通过绘出这条曲线 通过交叉验证集误差的变化趋势 你可以用自己选择出 或者编写程序自动得出 能使交叉验证集误差最小的那个点 然后选出那个与之对应的参数 λ 的值 

Learning Curves学习曲线

学习曲线 是一种很好的工具 用来判断某一个学习算法 是否处于偏差 方差问题 或是二者皆有   

为了绘制一条学习曲线 通常先绘制出Jtrain 或者Jcv 将其绘制成一个关于训练集 样本总数m的函数 但我要自己取一些m的值 也就是说我要自行对m的取值 做一点限制 比如说我取10 20或者 30 40组训练集 然后绘出训练集误差 以及交叉验证集误差 

好的 那么我们来看看 这条曲线绘制出来是什么样子 假设我只有一组训练样本 也即m=1 并且假设使用二次函数来拟合模型 此时其误差一定为0 当m等于1 m=2 或m=3时 对训练集数据进行预测 得到的训练集误差 都将等于0 这里假设我不使用正则化 当然如果使用正则化 那么误差就稍大于0 当训练样本容量m很小的时候 训练误差也会很小 当m等于4 m等于5的情况 这时候再用二次函数来拟合 好像效果有下降但还是差强人意   而当训练集越来越大的时候 你不难发现 要保证使用二次函数 的拟合效果依然很好 就显得越来越困难了 平均训练误差 是逐渐增大的 因此如果你画出这条曲线 你就会发现 训练集误差 随着m的增大而增大 

那么交叉验证集误差的情况如何呢 ?当训练集很小的时候 泛化程度不会很好 意思是不能很好地适应新样本 因此Jcv和Jtest比较大 当我使用 一个更大的训练集时 我才有可能 得到一个能够更好拟合数据的 可能的假设 Jcv和Jtest慢慢变小 因为你使用的数据越多 你越能获得更好地泛化表现 

上图左边是高偏差时的学习曲线,结论:

即使你不断增大m的值 你基本上还是会得到的一条差不多的直线 只要训练集样本容量值达到 或超过了那个特定的数值 交叉验证集误差和测试集误差就趋于不变 这样你会得到最能拟合数据的那条直线 高偏差的情形 反映出的问题是 交叉验证集和训练集 误差最终都很大 选用更多的训练集数据 对于改善算法表现无益 这样可以让你避免 把时间浪费在 想收集更多的训练样本 因为再多的数据也是无意义的 

右边是高方差时的学习曲线

假设我们使用 一个很小的lambda值 可能不等于0 但足够小的lambda 那么很显然 我们会对这组数据 拟合得非常非常好  如果训练集 样本容量很小时 训练集误差Jtrain 将会很小   随着训练集样本容量的增加 Jtrain的值 会随之增大 但总的来说训练集误差还是很小 交叉验证集误差 将会一直都很大 即便我们选择一个 比较合适恰当的 训练集样本数 在训练集误差 和交叉验证集误差之间 有一段很大的差距   而这个曲线图也反映出 如果我们要考虑增大训练集的样本数 这两条学习曲线 蓝色和红色的两条曲线 正在相互靠近 训练集误差很可能会 逐渐增大 而交叉验证集误差 则会持续下降 当然我们最关心的还是交叉验证集误差 或者测试集误差 因此 使用更多的训练集数据 对改进算法的表现 事实上是有效果的 它能告诉你 是否有必要花时间 来增加更多的训练集数据  

结论总结:欠拟合时,增加样本无意义;过拟合时,增加样本可能有帮助

Deciding What to Do Next Revisited决定下一步该做什么

我们使用正则化的线性回归拟合模型 却发现该算法没有达到预期效果 我们提到我们有如下这些选择 那么如何判断 哪些方法更可能是有效的呢 

第一种方法 是使用更多的训练集数据 这种方法对于高方差的情况 是有帮助的 如果处于高偏差 则不会有太明显的帮助 所以你应该先画出 学习曲线 然后看出你的模型 应该至少有那么一点方差问题 也就是说你的交叉验证集误差 应该比训练集误差大一点 

第二种方法是 少选几种特征 这同样是对高方差时有效 如果你的模型处于高偏差问题 那么使用更少的特征不会有任何帮助 

第三种方法是 选用更多的特征 一般可以帮助解决高偏差问题 所以如果你需要增加 更多的特征时 一般是由于你现有的 假设函数太简单 因此我们才决定增加一些 别的特征来让假设函数 更好地拟合训练集 类似的 

第四种方法是 增加更多的多项式特征 这实际上也是属于增加特征 因此也是用于 修正高偏差问题   

第四种方法是 增大和减小λ  减小λ可以修正高偏差 而增大λ的值解决高方差   

如果你要进行神经网络的拟合 其中一个选择是 选用一个相对简单的网络结构 比如说只有一个 隐藏层 或者可能相对来讲 比较少的隐藏单元 因此像这样的一个简单的神经网络 参数就不会很多 很容易出现欠拟合 这种比较小型的神经网络 其最大优势在于计算量较小   与之相对的另一种情况 是相对较大型的神经网络结构 要么隐藏层单元比较多 比如这一层中的隐藏单元数就很多 要么隐藏层比较多   因此这种比较复杂的神经网络 参数一般较多 也更容易出现过拟合   这种结构的一大劣势 计算量较大 最主要的问题 还是它更容易出现过拟合现象 事实上 如果你经常应用神经网络 特别是大型神经网络的话 你就会发现越大型的网络性能越好 但如果发生了过拟合 你可以使用正则化的方法 来修正过拟合 一般来说 使用一个大型的神经网络 并使用正则化来修正过拟合问题 通常比使用一个小型的神经网络 效果更好  

Machine Learning System Design机器学习系统设计

Building a Spam Classifier构建垃圾邮件分类器

Prioritizing What to Work On 确定工作内容的优先级

举一个 垃圾邮件分类的例子 假如你想建立一个垃圾邮件分类器 假设我们已经有一些 加过标签的训练集 包括标注的垃圾邮件 表示为y=1 和非垃圾邮件 表示为y=0 我们如何 以监督学习的方法来构造一个分类器 来区分垃圾邮件和非垃圾邮件呢?   

为了应用监督学习 我们首先 必须确定的是 如何用邮件的特征 构造向量x 给出训练集中的 特征x和标签y 我们就能够训练出某种分类器 比如用逻辑回归的方法 

这里有一种选择 邮件的一些特征变量的方法   比如说我们可能会想出 一系列单词 能够用来区分垃圾邮件或非垃圾邮件 比如说 如果有封邮件 包含单词"deal(交易)" "buy(买)" "discount(折扣)"那么它就很有可能是一封垃圾邮件 如果一封邮件中   包含了我的名字"Andrew" 说明这封邮件 不太可能是垃圾邮件 因为某些原因 我认为 "now(现在)"这个单词表明了 这封邮件可能并不是垃圾邮件 因为我经常收到一些很紧急的邮件 当然还有别的单词 

我们可以选出这样成百上千的单词   给出一封这样的邮件 我们可以将这封邮件 用一个特征向量 来表示 方法如图 

我选择了100个单词 用于表示是否可能为垃圾邮件 所以 这个特征向量x 的维度是100 并且 如果这个特定的单词 即单词 j 出现在   这封邮件中 那么每一个特征变量 xj 的值为1  反之 xj为0 

虽然我所描述的这个过程是我自己 选取的100个单词 但是在实际工作中 最普遍的做法是 遍历整个训练集 然后 在训练集中 选出出现次数最多的n个单词 n一般介于10,000和50,000之间 然后把这些单词 作为你要用的特征 因此不同于手动选取 这些单词会构成特征 这样你就可以用它们 来做垃圾邮件分类   

如果你正在构造一个垃圾邮件分类器 你应该会面对这样一个问题 那就是 你最该去使用哪一种 改进你的方法 从而使得你的垃圾邮件分类器具有较高的准确度 从直觉上讲 是要收集大量的数据 生成了这个叫做 data 的对象 是吧? 事实上确实好多人这么做 很多人认为 收集越多的数据 算法就会表现的越好 

就垃圾邮件分类而言 有一个叫做"Honey Pot"的项目 它可以建立一个 假的邮箱地址 故意将这些地址 泄露给发垃圾邮件的人 这样就能收到大量的垃圾邮件 你看 这样的话 我们就能得到非常多的垃圾邮件来训练学习算法 但是 在前面的课程中我们知道 大量的数据可能会有帮助 也可能没有   

对于大部分的机器学习问题 还有很多办法 用来提升机器学习的效果 比如对于垃圾邮件而言 也许你会想到 用更复杂的特征变量 像是邮件的路径信息   这种信息通常会出现在邮件的标题中   因此 垃圾邮件发送方在发送垃圾邮件时 他们总会试图 让这个邮件的来源变得模糊一些 或者是 用假的邮件标题   或者通过不常见的服务器 来发送邮件 用不常见的路由 他们就能给你发送垃圾邮件 而且这些信息也有可能包含在邮件标题部分 因此可以想到   我们可以通过邮件的标题部分 来构造更加复杂的特征 来获得一系列的邮件路由信息 进而判定这是否是一封垃圾邮件 

你还可能会想到别的方法 比如 从邮件的正文出发 寻找一些复杂点的特征 例如 单词"discount" 是否和单词"discounts"是一样的 又比如 单词"deal(交易)"和"dealer(交易商)"是否也应视为等同 甚至 像这个例子中 有的单词小写有的大写 或者我们是否应该用标点符号来构造复杂的特征变量   因为垃圾邮件可能会更多的使用感叹号 这些都不一定 

同样的 我们也可能构造 更加复杂的算法来检测 或者纠正那些故意的拼写错误 例如 "m0rtgage" "med1cine" "w4tches"   因为垃圾邮件发送方确实这么做了 因为如果你将4放到"w4tches"中   那么 用我们之前提到的 简单的方法 垃圾邮件分类器不会把"w4tches" 和"watches" 看成一样的 这样我们就很难区分这些 故意拼错的垃圾邮件 发垃圾邮件的也很机智 他们这么做就逃避了一些过滤   当我们使用机器学习时 总是可以“头脑风暴”一下 想出一堆方法来试试 就像这样 

顺带一提 我有一段时间 研究过垃圾邮件分类的问题 实际上我花了很多时间来研究这个 尽管我能够理解 垃圾邮件分类的问题 我确实懂一些这方面的东西 但是 我还是很难告诉你 这四种方法中 你最该去使用哪一种 事实上 坦白地说 最常见的情况是 一个研究小组 可能会随机确定其中的一个方法 但是有时候 这种方法 并不是最有成效的 你知道 你只是随机选择了其中的一种方法 实际上 当你需要通过 头脑风暴来想出 不同方法来尝试去提高精度的时候 你可能已经超越了很多人了 令人难过的是 大部分人 他们并不尝试着 列出可能的方法 他们做的 只是 某天早上醒来 因为某些原因 有了一个突发奇想 "让我们来试试 用Honey Pot项目 收集大量的数据吧" 不管出于什么奇怪的原因 早上的灵机一动 还是随机选一个 然后干上大半年   但是我觉得我们有更好的方法 

下一节我们将通过误差分析  告诉你 怎样用一个 更加系统性的方法 从一堆不同的方法中 选取合适的那一个 

Error Analysis误差分析

如果你准备 研究机器学习的东西 或者构造机器学习应用程序 最好的实践方法 不是建立一个 非常复杂的系统 拥有多么复杂的变量 而是 构建一个简单的算法 这样你可以很快地实现它  每当我研究 机器学习的问题时 我最多只会花一天的时间 来试图很快的把结果搞出来 即便效果不好 根本没有用复杂的系统 但是只是很快的得到的结果 即便运行得不完美 但是也把它运行一遍 最后通过交叉验证来检验数据 一旦做完  再通过画出学习曲线 以及检验误差 来找出 你的算法是否有 高偏差和高方差的问题 或者别的问题 在这样分析之后 再来决定用更多的数据训练 或者加入更多的特征变量是否有用 

这么做的原因是 这在你刚接触机器学习问题时 是一个很好的方法 你并不能 提前知道 你是否需要复杂的特征变量 或者你是否需要 更多的数据 还是别的什么 提前知道你应该做什么 是非常难的 因为你缺少证据 缺少学习曲线 因此 你很难知道 你应该把时间花在什么地方来提高算法的表现 但是当你实践一个 非常简单即便不完美的 方法时 你可以通过画出学习曲线来做出进一步的选择 

这种理念是 我们必须 用证据来领导我们的决策 怎样分配自己的时间来优化算法 而不是仅仅凭直觉 凭直觉得出的东西一般总是错误的   除了画出学习曲线之外 一件非常有用的事是 误差分析 当我们在构造 比如构造垃圾邮件分类器时 我会看一看 我的交叉验证数据集 然后亲自看一看 哪些邮件被算法错误地分类 因此 通过这些 被算法错误分类的垃圾邮件 与非垃圾邮件 你可以发现某些系统性的规律 什么类型的邮件总是被错误分类   经常地 这样做之后 这个过程能启发你   构造新的特征变量 或者告诉你 现在 这个系统的短处 然后启发你 如何去提高它 

具体地说 这里有一个例子   假设你正在构造一个 垃圾邮件分类器 你拥有500个实例 在交叉验证集中 假设在这个例子中 该算法有非常高的误差率 它错误分类了 一百个交叉验证实例   所以我要做的是 人工检查这100个错误 然后手工为它们分类 基于例如 这些是什么类型的邮件 哪些变量 能帮助这个算法来正确分类它们   明确地说 通过鉴定这是哪种类型的邮件 通过检查 这一百封错误分类的邮件 我可能会发现 最容易被误分类的邮件 可能是 有关药物的邮件 基本上这些邮件都是卖药的 或者 卖仿品的 比如卖假表   或者一些骗子邮件 又叫做钓鱼邮件 等等   所以 在检查哪些邮件被错误分类的时候 我会看一看每封邮件 数一数 比如 在这100封错误归类的邮件中 我发现有12封 错误归类的邮件是和卖药有关的邮件 4封 是推销仿品的 推销假表或者别的东西   然后我发现 有53封邮件 是钓鱼邮件 诱骗你 告诉他们你的密码 剩下的31封别的类型的邮件   通过算出 每个类别中 不同的邮件数 你可能会发现 比如 该算法在   区分钓鱼邮件的时候 总是表现得很差 这说明 你应该花更多的时间 来研究这种类型的邮件 然后 看一看你是否能通过构造更好的特征变量 来正确区分这种类型的邮件   同时 我要做的是 看一看哪些特征变量 可能会帮助算法正确地分类邮件   

我们假设 能帮助我们提高 邮件分类表现 的方法是 检查有意的拼写错误 不寻常的邮件路由来源 以及垃圾邮件特有的标点符号方式 比如很多感叹号 与之前一样 我会手动地浏览这些邮件 假设有5封这种类型的邮件 16封这种类型的 32封这种类型的 以及一些别的类型的 如果 这就是你从交叉验证中得到的结果 那么 这可能说明 有意地拼写错误出现频率较少 这可能并不值得 你花费时间 去编写算法来检测这种类型的邮件 但是如果你发现 很多的垃圾邮件 都有不一般的标点符号规律 那么这是一个很强的特征 说明你应该 花费你的时间 去构造基于标点符号的 更加复杂的特征变量   因此 这种类型的误差分析 是一种手动检测的过程 检测算法可能会犯的错误 这经常能够帮助你 找到更为有效的手段 这也解释了为什么 我总是推荐先实践一种 快速即便不完美的算法 我们真正想要的是 找出什么类型的邮件 是这种算法最难分类出来的 对于不同的算法 不同的机器学习算法 它们 所遇到的问题一般总是相同的   通过实践一些快速 即便不完美的算法 你能够更快地 找到错误的所在 并且快速找出算法难以处理的例子 这样你就能集中精力在这些真正的问题上   

最后 在构造机器学习算法时   另一个有用的小窍门是 保证你自己能有一种 数值计算的方式来评估你的机器学习算法

先看看这个例子   假设我们试图 决定是否应该 把像"discount""discounts""discounter""discountring" 这样的单词都视为等同 一种方法 是检查这些单词的 开头几个字母 比如 当你在检查这些单词开头几个字母的时候 你发现 这几个单词 大概可能有着相同的意思   在自然语言处理中 这种方法 是通过一种叫做词干提取的软件实现的 如果你想自己来试试 你可以 在网上搜索一下 "Porter Stemmer(波特词干提取法)" 这是在词干提取方面 一个比较不错的软件 这个软件会 将单词"discount""discounts"以及等等 都视为同一个单词   但是这种词干提取软件 只会检查 单词的头几个字母 这有用 但是也可能会造成一些问题 因为 举个例子 因为这个软件会把单词"universe(宇宙)" 和"university(大学)" 也视为同一个单词 因为 这两个单词开头的字母是一样的   因此 当你在决定 是否应该使用词干提取软件用来分类 这总是很难说清楚 特别地 误差分析 也并不能帮助你决定 词干提取是不是一个好的方法   与之相对地 最好的方法 来发现词干提取软件 对你的分类器 到底有没有用 是迅速地着手试一试 来看看它表现到底怎么样   为了这么做 通过数值来评估你的算法 是非常有用的   具体地说 自然而然地 你应该通过交叉验证 来验证不用词干提取与用词干提取的算法的错误率 因此 如果你不在你的算法中使用词干提取 然后你得到 比如 5%的分类错误率 然后你再使用词干提取来运行你的算法 你得到 比如 3%的分类错误 那么这很大的减少了错误发生 于是你决定 词干提取是一个好的办法 就这个特定的问题而言 这里有一个数量的评估数字 即交差验证错误率 我们以后会发现 这个例子中的评估数字 还需要一些处理 但是 我们可以在今后的课程中看到 这么做还是会让你 能更快地做出决定 比如 是否使用词干提取   

如果每一次 你实践新想法的时候 你都手动地检测 这些例子 去看看是表现差还是表现好 那么这很难让你 做出决定 到底是否使用词干提取 是否区分大小写   但是通过一个 量化的数值评估 你可以看看这个数字 误差是变大还是变小了   你可以通过它 更快地实践 你的新想法 它基本上非常直观地告诉你 你的想法是提高了算法表现 还是让它变得更坏 这会大大提高 你实践算法时的速度 所以我强烈推荐 在交叉验证集上来实施误差分析 而不是在测试集上 但是 还是有一些人 会在测试集上来做误差分析 即使这从数学上讲 是不合适的 所以我还是推荐你 在交叉验证向量上 来做误差分析 

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

Handling Skewed Data处理偏斜数据

Error Metrics for Skewed Classes偏斜类的错误度量

有了算法的评估 和误差度量值 有一件重要的事情要注意 就是使用一个合适的误差度量值 这有时会对于你的学习算法 造成非常微妙的影响   这件重要的事情就是 偏斜类(skewed classes)的问题   比如癌症分类问题 我们拥有 内科病人的特征变量 我们希望知道他们是否患有癌症 我们假设 y=1 表示患者患有癌症 假设 y=0 表示他们没有得癌症 我们训练逻辑回归模型 假设我们用测试集 检验了这个分类模型 并且发现它只有1%的错误 因此我们99%会做出正确诊断 看起来是非常不错的结果  但是我们发现 在测试集中 只有0.5%的患者 真正得了癌症 因此在这个例子中 1%的错误率就不再显得那么好了   

举个具体的例子 这里有一行代码 它让y总是等于0 因此它总是预测 没有人得癌症 那么这个算法实际上只有 0.5%的错误率 因此这甚至比 我们之前得到的1%的错误率更好 在这个例子中 正样本的数量 与负样本的数量相比 非常非常少 我们把这种情况叫做 偏斜类   一个类中的样本数 与另一个类的数据相比 多很多 通过总是预测y=0 或者 总是预测y=1 算法可能表现非常好 

因此使用分类误差 或者分类精确度 来作为评估度量可能会产生如下问题   假如说你有一个算法 它的精确度是99.2%   因此它只有0.8%的误差 假设 你对你的算法做出了一点改动 现在你得到了 99.5%的精确度   只有0.5%的误差   这到底是不是算法的一个提升呢 用某个实数来 作为评估度量值 的一个好处就是 它可以帮助我们迅速决定 我们是否需要对算法做出一些改进 将精确度从99.2%提高到99.5%   但是我们的改进到底是有用的 还是说 我们只是把代码替换成了 例如总是预测y=0 这样的东西 因此如果你有一个偏斜类 用分类精确度 并不能很好地衡量算法 因为你可能会获得一个很高的精确度 非常低的错误率 但是我们并不知道 我们是否真的提升了 分类模型的质量 因为总是预测y=0 并不是一个 好的分类模型   但是总是预测y=0 会将你的误差降低至 比如 降低至0.5% 

当我们遇到 这样一个偏斜类时 我们希望有一个 不同的误差度量值 其中一种评估度量值 叫做查准率(precision)和召回率(recall)让我来解释一下 假设我们正在用测试集来评估一个二元分类模型 我们的学习算法 要做的是 做出值的预测  如果 有一个样本它实际所属的类是1 预测的类也是1 那么   我们把这个样本叫做真阳性(true positive)学习算法 预测某个值是阴性 等于0 实际的类也确实属于0 那么我们把这个叫做真阴性(true negative) 学习算法 预测某个值等于1   但是实际上它等于0 这个叫做假阳性(false positive)  算法预测值为0 但是实际值是1 叫做假阴性(false negative) 这样 我们有了一个2x2的表格 基于 实际类与预测类   这样我们有了一个 另一种方式来 评估算法的表现 

我们要计算两个数字 第一个叫做查准率 这个意思是 对于所有我们预测 他们患有癌症的病人 有多大比率的病人是真正患有癌症的 一个分类模型的查准率 = 真阳性/预测为阳性 = 真阳性/(真阳性 +假阳性)  查准率越高就越好  另一个数字我们要计算的 叫做召回率 召回率是 如果所有这些在数据集中的病人 确实得了癌症  有多大比率 我们正确预测他们得了癌症 召回率 = 真阳性/实际阳性  = 真阳性/(真阳性 +  假阴性)   同样地 召回率越高越好   通过计算查准率 和召回率 我们能更好的知道 分类模型到底好不好   

Trading Off Precision and Recall

癌症分类的例子 假如我们希望 在我们非常确信地情况下 才预测一个病人得了癌症 这样做的一种方法 是修改算法 我们不再将临界值 设为0.5 也许 我们只在 h(x)的值大于或等于0.7 的情况下 才预测y=1 因此 你的回归模型   会有较高的查准率较低的召回率 

因为 当我们做预测的时候 我们只给很小一部分的病人预测y=1 现在我们把这个情况夸大一下 我们临界值设为0.9 我们只在至少90%肯定 这个病人患有癌症的情况下 预测y=1 那么这些病人当中 有非常大的比率 真正患有癌症 因此这是一个高查准率的模型   但是召回率会变低 因为我们希望能够正确检测患有癌症的病人 

现在考虑一个不同的例子   假设我们希望 避免遗漏掉患有癌症的人 即我们希望避免假阴性   具体地说 如果一个病人实际患有癌症 但是我们并没有告诉他患有癌症 那这可能造成严重后果 在这个例子中 我们将临界值 设得较低 比如0.3  在这种情况下 我们会有一个 较高召回率和较低的查准率的模型 

因此 总的来说 对于大多数的回归模型 你得权衡查准率和召回率   

当你改变 临界值的值时 我在这儿画了一个 临界值 你可以画出曲线 来权衡查准率 和召回率 这里的一个值 反应出一个较高的临界值 这个临界值可能等于0.99 我们假设 只在有大于99%的确信度的情况下 才预测y=1 至少 有99%的可能性 因此这个点反应高查准率 低召回率 然而这里的一个点 反映一个较低的临界值 比如说0.01 毫无疑问 在这里预测y=1 如果你这么做 你最后会得到 很低的查准率 但是较高的召回率   当你改变临界值 如果你愿意 你可以画出回归模型的所有曲线 来看看你能得到的查准率和召回率的范围 顺带一提 查准率-召回率曲线可以是各种不同的形状 有时它看起来是这样   有时是那样   查准率-召回率曲线的形状 有很多可能性 这取决于回归模型的具体算法   因此这又产生了 另一个有趣的问题 那就是 有没有办法自动选取临界值 或者 更广泛地说 如果我们有不同的算法 或者不同的想法 我们如何比较不同的查准率和召回率呢?

具体来说 假设我们有三个 不同的学习算法 或者这三个不同的学习曲线 是同样的算法 但是临界值不同   我们怎样决定哪一个算法是最好的   我们之前讲到的 其中一件事就是 评估度量值的重要性   这个概念是 通过一个具体的数字 来反映你的回归模型到底如何   但是查准率和召回率的问题 我们却不能这样做 因为在这里我们有两个可以判断的数字   因此 我们经常会 不得不面对这样的情况 如果我们正在试图比较算法1 和算法2 我们最后问自己 到底是0.5的查准率与 0.4的召回率好 还是说 0.7的查准率与 0.1的召回率好 或者每一次 你设计一个新算法 你都要坐下来思考 到底0.5 0.4好 还是说 0.7 0.1好 我不知道 如果你最后这样坐下来思考 这回降低 你的决策速度 思考到底哪些改变是有用的 应该被融入到你的算法   与此相反的是 如果我们有一个评估度量值 一个数字 能够告诉我们到底是算法1好还是算法2好 这能够帮助我们 更快地决定 哪一个算法更好 同时也能够更快地帮助我们 评估不同的改动 哪些应该被融入进算法里面 那么 我们怎样才能 得到这个评估度量值呢?   

你可能会去尝试的 一件事情是 计算一下查准率和召回率的平均值 用 P 和 R 来表示查准率和召回率 你可以做的是 计算它们的平均值 看一看哪个模型有最高的均值   但是这可能 并不是一个很好的解决办法 因为 像我们之前的例子一样   如果我们的回归模型 总是预测   y=1 这么做你可能得到非常高的召回率 得到非常低的查准率 相反地 如果你的模型 总是预测y=0   就是说 如果很少预测y=1 对应的 设置了一个高临界值 最后 你会得到非常高的 查准率和非常低的召回率   这两个极端情况 一个有非常高的临界值 一个有非常低的临界值 它们中的任何一个都不是一个好的模型 

相反地 有一种结合查准率和召回率的不同方式 叫做F值 公式是这样   在这个例子中 F值是这样的 我们可以通过 F值来判断 算法1 有最高的F值 算法2第二 算法3是最低的 因此 通过F值 我们会在这几个算法中选择算法1   F值 也叫做F1值 一般写作F1值 但是人们一般只说F值   它的定义 会考虑一部分 查准率和召回率的平均值 但是它 会给查准率和召回率中较低的值 更高的权重 因此 你可以看到F值的分子 是查准率和召回率的乘积 因此如果查准率等于0 或者召回率等于0 F值也会 等于0 因此它结合了查准率和召回率 对于一个较大的F值 查准率 和召回率都必须较大   我必须说 有较多的公式 可以结合查准率和召回率 F值公式 只是 其中一个 但是出于历史原因 和习惯问题 人们在机器学习中使用F值   这个术语F值 没有什么特别的意义 所以不要担心 它到底为什么叫做F值或者F1值 但是它给了你 你需要的有效方法 因为无论是查准率等于0 还是召回率等于0 它都会得到一个很低的F值 因此 如果要得到一个很高的F值 你的算法的查准率和召回率都要接近于1 具体地说 如果P=0或者 R=0 你的F值也会等于0   对于一个最完美的F值 如果查准率等于1 同时召回率 也等于1 那你得到的F值 等于1乘以1 除以2再乘以2 那么F值 就等于1 如果你能得到最完美的查准率和召回率 在0和1中间的值 这经常是 回归模型最经常出现的分数   

在这次的视频中 我们讲到了如何 权衡查准率和召回率 以及我们如何变动 临界值 来决定我们希望预测y=1 还是y=0 比如我们 需要一个 70%还是90%置信度的临界值 或者别的 来预测y=1 通过变动临界值 你可以控制权衡 查准率和召回率 之后我们讲到了F值 它权衡查准率和召回率 给了你一个 评估度量值 当然 如果你的目标是 自动选择临界值 来决定 你希望预测y=1 还是y=0 那么一个比较理想的办法是 评估这些不同的临界值 在交叉检验集上 得到最高的F值 这是自动选择临界值的较好办法

Using Large Data Sets使用大型数据集

Data For Machine Learning机器学习数据

事实证明 在一定条件下 得到大量的数据并在 某种类型的学习算法中进行训练 可以是一种 有效的方法来获得 一个具有良好性能的学习算法   而这种情况往往出现在 这些条件对于你的问题 都成立 并且 你能够得到大量数据的 情况下 这可以是 一个很好的方式来获得 非常高性能的学习算法   

我先讲一个故事   很多很多年前 我认识的两位研究人员 Michele Banko 和 Eric Brill 进行了一项有趣的研究   他们感兴趣的是研究 使用不同的学习算法的效果 与将这些效果 使用到不同训练数据集上 两者的比较 他们当时考虑这样一个问题 如何在易混淆的词之间进行分类 比如 在这样的句子中: 早餐我吃了__个鸡蛋 (to,two,too) 在这个例子中 早餐我吃了2个鸡蛋   这是一个 易混淆的单词的例子 而这是另外一组情况 于是他们把诸如这样的机器学习问题 当做一类监督学习问题 并尝试将其分类 什么样的词 在一个英文句子特定的位置 才是合适的   

他们选了四种分类算法 这些具体算法并不重要   他们所做的就是 改变了训练数据集的大小 并尝试将这些学习算法 用于不同大小的 训练数据集中 这就是他们得到的结果 这些趋势非常明显 首先大部分算法 都具有相似的性能 其次 随着训练 数据集的增大 这些算法的性能  也都对应地增强了 

事实上 如果 你选择选择了一个"劣等的"算法 如果你给这个 劣等算法更多的数据 那么 它有可能会比"优等算法"更好   像这样的结果 引起了一种 在机器学习中 的普遍共识: "取得成功的人不是拥有最好算法的人 而是拥有最多数据的人"   那么这种说法 在什么时候是真 什么时候是假呢? 

如果 你有大量的数据 而且你训练了一种 带有很多参数的学习算法 那么这将 会是一个很好的方式来提供 一个高性能的学习算法 我觉得关键的测试 我常常问自己 首先 一个人类专家 看到了特征值 x 能很有信心的预测出 y值吗? 因为这可以 证明 y 可以根据特征值 x 被准确地预测出来 其次 我们实际上能得到一组 庞大的训练集并且在这个 训练集中训练一个有 很多参数的学习算法吗? 如果你不能做到这两者 那么更多时候 你会得到一个性能很好的学习算法

参考资料

https://www.coursera.org/learn/machine-learning/

猜你喜欢

转载自blog.csdn.net/junjun150013652/article/details/80753719