在之前的BLOG中,我们学习了一些在搭建机器学习工程的相关技巧。在这篇BLOG中,就让我们从垃圾邮件分类这个小项目入手,看看应该如何着手一个工程吧!

基本模型的建立

接下来我们将一同学习机器学习系统的设计,看看我们可能会遇到的主要问题并探讨高效的解决方法。首先我们举一个垃圾邮件分类的例子,假如我们想建立一个垃圾邮件分类器,首先我们就要找到这些垃圾邮件与非垃圾邮件的相关例子:

HT1.png

比如上图中,左边这封邮件想向你推销东西。注意这封垃圾邮件有意的拼错一些单词就像 "Med1cine" 中有一个1, "m0rtgage"里有个0;而右边的邮件显然不是一个垃圾邮件,大概率是亲友写来的。假设我们已经有一些加过标签的训练集,包括标注的垃圾邮件表示为y = 1和非垃圾邮件表示为y = 0,我们如何使用监督学习的方法来构造一个分类器来区分垃圾邮件和非垃圾邮件呢?

为了应用监督学习我们首先必须确定的是如何用邮件的特征来构造向量 x 。如果能给出训练集中的特征 x 和标签 y ,我们就能够训练出某种分类器再用逻辑回归的方法训练我们的分类算法。这里有一种选择邮件的一些特征变量的方法,比如说我们可能会想出成百上千的单词认为这些单词能够用来区分垃圾邮件或非垃圾邮件。比如说如果有封邮件包含单词"deal(交易)" 那么它就很有可能是一封垃圾邮件,同时 包含单词"buy(买)"、"discount(折扣)"的邮件也很有可能是垃圾邮件。但是如果一封邮件中包含了我的名字"Jvruo" ,那这有就很可能是一个知道我的人写的,这说明这封邮件不太可能是垃圾邮件;还因为某些原因我们认为 "now(现在)"这个单词表明了这封邮件可能并不是垃圾邮件……当然还有别的单词,我们可以选出这样成百上千的单词。给出一封这样的邮件我们可以将这封邮件用一个特征向量来表示,方法如下:我们先将选好的单词列出来,然后检查一下这些词汇是否出现在一封邮件中。接着我们用一个 特征向量 x 来表示这封邮件的反应情况。

具体来说,下图这封邮件中我的名字没有出现,因此这里是0;单词"buy(购买)"出现了,所以这里是1,注意在向量里面只有 1 或 0 表示有没有出现,所以尽管"buy"出现了两次这里仍然只是 1;单词"deal"也出现了,所以这里也是 1;单词"discount"并没有出现,所以这里是 0,单词"now"出现了,所以为 1 :

HT2.png

所以我们依据对应的单词是否出现填上 0 和 1 就构造出了我们的特征向量 x 。虽然在这个例子中,我们只选取了 5 个单词,但是在实际工作中最普遍的做法是遍历整个训练集,然后在训练集中选出出现次数最多的 n 个单词并且 n 一般介于 10,000 和 50,000 之间,然后把这些单词作为我们要用的特征。

在得到特征向量 x 和分类标签 y 后我们就可以通过逻辑分类得出我们的学习算法。但如果我们构造完了一个初步的垃圾邮件分类器,我们就会面对这样一个问题——现在我们最该去使用哪一种改进你的方法去提高我们分类的准确度?其实我们也有很多种方法。

第一种方法也是最直接的,我们可以收集更多的数据。事实上确实好多人这么做,很多人认为收集越多的数据算法就会表现的越好。事实上就垃圾邮件分类而言,有一个叫做"Honey Pot"的项目,它建立了许多假的邮箱地址,并且故意将这些地址泄露给发垃圾邮件的人,这样就能收到大量的垃圾邮件。但是在前面的学习中我们知道大量的数据可能会有帮助,也可能没有。

第二种方法我们也经常用到,就是用更复杂的特征变量。比如我们可以增加像是邮件的路径信息,邮件标题,发送的服务器信息,路由信息来构造更加复杂的特征进而判定这是否是一封垃圾邮件。

第三种方法中我们可以去寻找其他特征点,比如从邮件的正文出发寻找一些复杂点的特征,例如单词"discount" 是否和单词"discounts"是一样的,又比如单词"deal(交易)"和"dealer(交易商)"是否也应视为等同,甚至有的单词小写有的大写或者标点符号都可以用来构造复杂的特征变量。我们甚至可能构造更加复杂的算法来检测或者纠正那些故意的拼写错误,例如 "m0rtgage" "med1cine" "w4tches" ,因为垃圾邮件发送方确实经常这么做以逃避一些过滤。

当我们使用机器学习时,总是可以“头脑风暴”一下想出一堆方法来试试。其实这三种方法不一定都适用于你的学习算法优化,但最常见的情况是一个研究小组可能会随机确定其中的一个方法,但是往往这种拍脑门决定方法并不是最有成效的。所以在下一部分,我们讲讲到一种叫做误差分析的方法来帮助我们选择那个真正适合我们的高效方法。

误差分析

这一部分,就让我们来一同看看误差分析(error analysis)的概念,这会帮助我们更系统地做出决定。如果我们准备研究机器学习的东西或者构造机器学习应用程序,最好的实践方法并不是直接建立一个非常复杂的系统,而是尽可能快地构建一个简单的算法即便效果不好。坦白的说,就是先构建一个根本没有用复杂的系统但是可以很快的得到结果的算法,即便运行得不完美,但是也把它运行一遍,然后通过交叉验证来检验数据。一旦做完以上步骤,我们就可以画出学习曲线并且检验误差,来找出我们的算法是否有高偏差或高方差的问题;在这样分析之后再来决定用更多的数据训练或者加入更多的特征变量之类的。

这么做的原因是,在我们刚接触机器学习问题时并不能提前知道是否需要复杂的特征变量或者是否需要更多的数据还是别的什么,因为我们缺少证据缺少学习曲线,因此我们也很难知道应该把时间花在什么地方来提高算法的表现。但是当我们实践一个非常简单即便不完美的方法时,我们可以通过画出学习曲线来做出进一步的选择,在实践项目中我们必须用证据来领导我们的决策怎样分配自己的时间来优化算法,而不是仅仅凭直觉,凭直觉得出的东西一般总是错误的。

具体来说当我们在构造比如构造垃圾邮件分类器时,我们应该看一看交叉验证数据集的数据表现,然后亲自看一看哪些邮件被算法错误地分类;然后通过这些被算法错误分类的垃圾邮件与非垃圾邮件,我们可以发现什么类型的邮件总是被错误分类。这个过程能启发我们构造新的特征变量或者告诉我们现在这个系统的短处然后启发我们如何去提高它。

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

同时我们要做的是看一看哪些特征变量可能会帮助算法正确地分类邮件。我们假设能帮助我们提高邮件分类表现的方法有检查有意的拼写错误,不寻常的邮件路由来源以及鉴别垃圾邮件特有的标点符号方式,比如很多感叹号。与之前一样我们可以手动地浏览这些邮件,假设有5有意的拼写错误的邮件,16封不寻常的邮件路由来源的,有32封特有的标点符号方式的以及一些别的类型的。那么这可能说明,有意地拼写错误出现频率较少,这可能并不值得我们花费时间去编写算法来检测这种类型的邮件,但是我们发现很多的垃圾邮件都有不一般的标点符号规律,那么这是一个很强的特征说明我们应该花费时间去构造基于标点符号的更加复杂的特征变量。

因此以上这种类型的误差分析是一种手动检测的过程,用于检测算法可能会犯的错误,这经常能够帮助我们找到更为有效的手段,这也解释了为什么我们总是推荐先实践一种快速即便不完美的算法,我们真正想要的是找出什么类型的邮件是这种算法最难分类出来的。对于不同的机器学习算法,它们所遇到的问题一般总是相似的,通过实践一些快速即便不完美的算法,我们能够更快地找到错误的所在并且快速找出算法难以处理的例子,这样我们就能集中精力在这些真正的问题上。

最后在构造机器学习算法时的另一个有用的小窍门是保证我们自己有一种数值计算的方式来评估你的机器学习算法。在构造一个学习算法的过程中,如果我们能有一种评估我们算法的方法,将会让我们事倍功半。一种用数字说话的评估方法可以准确的告诉我们我们的算法到底表现有多好。具体来说假设我们试图决定是否应该把像"discount""discounts""discounter""discountring" 这样的单词都视为等同,一种方法是检查这些单词的开头几个字母,这种方法是通过一种叫做词干提取的软件实现的。如果你想自己来试试你可以在网上搜索一下 "Porter Stemmer(波特词干提取法)" ,这是在词干提取方面一个比较不错的软件,这个软件会将单词"discount""discounts"以及其他的都视为同一个单词。但是这种词干提取软件只会检查单词的头几个字母,这常常很有用但是也可能会造成一些问题,举个例子 我们可能会把单词"universe(宇宙)" 和"university(大学)" 视为同一个单词,因为这两个单词开头的字母是一样的,但他们的意思千差万别。因此当我们在决定是否应该使用词干提取软件用来分类,这总是很难说清楚。特别地人工的误差分析也并不能帮助我们决定词干提取是不是一个好的方法。

与之相对地最好的方法来发现词干提取软件对我们的分类器到底有没有用,即运用自动的误差分析来通过数值来评估我们的算法。具体地说我们可以通过交叉验证来验证不用词干提取与用词干提取的算法的错误率,因此如果我们不在自己的算法中使用词干提取,然后你得到比如 5%的分类错误率;接着我们再使用词干提取来运行自己的算法得到比如 3%的分类错误,那么就代表词干提取软件很大的减少了错误发生,于是我们就可以决定词干提取是一个好的办法。

就这个特定的问题而言,我们是通过计算得到的评估数字即交差验证错误率来判断我们是否应该使用某种方法。这就是自动误差分析的一个用处体现。

再比如对于我们是否使用应该区分单词的大小写,并且我们有一种能够评估我们算法的方法的自动误差分析算法,然后我们得到如果不区分大小写,最后得到3.2%的错误率,然后如果区别大小写,我们的错误率只有2%,所以我们发现增加不区分大小写的操作表现还会较差些,所以我们就会果断抛弃这个想法。

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

总结一下当我们在研发一个新的机器学习问题时,我们总可以先实现一个较为简单快速但不是那么完美的算法,然后当我们有了初始的实现之后,它会变成一个非常有力的工具来帮助我们决定下一步的做法。因为我们可以先看看算法造成的错误通过误差分析来看看他犯了什么错,然后来决定优化的方式。另一件事是假设我们有了一个快速而不完美的算法实现,并且有一个数值的评估数据,我们就能够在后期快速地尝试我们的优化想法是否确属能够提高算法的表现,从而更快地做出决定在算法中放弃什么吸收什么。

结语

通过这篇BLOG,相信你已经对误差分析有了一定的了解,并且对小工程的开展也有了一点点感觉了。最后希望你喜欢这篇BLOG!

Last modification:March 7th, 2020 at 06:00 am
If you think my article is useful to you, please feel free to appreciate