Python 中的实用数据挖掘

本文是 2014 年 12 月我在布拉格经济大学做的名为‘ Python 数据科学’讲座的笔记。欢迎通过 @RadimRehurek 进行提问和评论。

本次讲座的目的是展示一些关于机器学习的高级概念。该笔记中用具体的代码来做演示,大家可以在自己的电脑上运行(需要安装 IPython,如下所示)。

本次讲座的听众需要了解一些基础的编程(不一定是 Python),并拥有一点基本的数据挖掘背景。本次讲座不是机器学习专家的“高级演讲”。

这些代码实例创建了一个有效的、可执行的原型系统:一个使用“spam”(垃圾信息)或“ham”(非垃圾信息)对英文手机短信(”短信类型“的英文)进行分类的 app。

Python 中的实用数据挖掘_第1张图片

整套代码使用 Python 语言。 python 是一种在管线(pipeline)的所有环节(I/O、数据清洗重整和预处理、模型训练和评估)都好用的通用语言。尽管 python 不是唯一选择,但它灵活、易于开发,性能优越,这得益于它成熟的科学计算生态系统。Python 庞大的、开源生态系统同时避免了任何单一框架或库的限制(以及相关的信息丢失)。

IPython notebook,是 Python 的一个工具,它是一个以 HTML 形式呈现的交互环境,可以通过它立刻看到结果。我们也将重温其它广泛用于数据科学领域的实用工具。
 
想交互运行下面的例子(选读)?
1. 安装免费的 Anaconda Python 发行版,其中已经包含 Python 本身。
2. 安装“自然语言处理”库——TextBlob: 安装包在这。
3. 下载本文的源码(网址: http://radimrehurek.com/data_science_python/data_science_python.ipynb 并运行: $ ipython notebook data_science_python.ipynb
4. 观看 IPython notebook 基本用法教程  IPython tutorial video 。
5. 运行下面的第一个代码,如果执行过程没有报错,就可以了。
 
端到端的例子:自动过滤垃圾信息
In [1]:
 

第一步:加载数据,浏览一下

 
让我们跳过真正的第一步(完善资料,了解我们要做的是什么,这在实践过程中是非常重要的),直接到  https://archive.ics.uci.edu/ml/datasets/SMS+Spam+Collection 下载 demo 里需要用的 zip 文件,解压到 data 子目录下。你能看到一个大概 0.5MB 大小,名为 SMSSpamCollection 的文件:

 

这份文件包含了 5000 多份 SMS 手机信息(查看 readme 文件以获得更多信息):
 
In [2]:
5574
 
文本集有时候也称为“语料库”,我们来打印 SMS 语料库中的前 10 条信息:
In [3]:
 
我们看到一个 TSV 文件(用制表符 tab 分隔),它的第一列是标记正常信息(ham)或“垃圾文件”(spam)的标签,第二列是信息本身。
 
这个语料库将作为带标签的训练集。通过使用这些标记了 ham/spam 例子,我们将训练一个自动分辨 ham/spam 的机器学习模型。然后,我们可以用训练好的模型将任意未标记的信息标记为 ham 或 spam。

Python 中的实用数据挖掘_第2张图片

 
我们可以使用 Python 的 Pandas 库替我们处理 TSV 文件(或 CSV 文件,或 Excel 文件):
 
In [4]:
 
 
我们也可以使用 pandas 轻松查看统计信息:
 
In [5]:
 out[5]:
   
message
label    
ham count 4827
unique 4518
top Sorry, I’ll call later
freq 30
spam count 747
unique 653
top Please call our customer service representativ…
freq 4
 
 
这些信息的长度是多少:
 
In [6]:
In [7]:
Out[7]:
In [8]:
Out[8]:
哪些是超长信息?
In [9]:
spam 信息与 ham 信息在长度上有区别吗?
 
In [10]:
Out[10]:
 
太棒了,但是我们怎么能让电脑自己识别文字信息?它可以理解这些胡言乱语吗?

 

第二步:数据预处理

这一节我们将原始信息(字符序列)转换为向量(数字序列);

这里的映射并非一对一的,我们要用词袋模型(bag-of-words)把每个不重复的词用一个数字来表示。

与第一步的方法一样,让我们写一个将信息分割成单词的函数:
In [11]:
这还是原始文本的一部分:
 
In [12]:
Out[12]:
 
这是原始文本处理后的样子:
 
In [13]:
Out[13]:
 
自然语言处理(NLP)的问题:
 
  1. 大写字母是否携带信息?
  2. 单词的不同形式(“goes”和“go”)是否携带信息?
  3. 叹词和限定词是否携带信息?

换句话说,我们想对文本进行更好的标准化。

我们使用 textblob 获取 part-of-speech (POS) 标签:

In [14]:
Out[14]:
 
并将单词标准化为基本形式 ( lemmas):
In [15]:
Out[15]:

 

这样就好多了。你也许还会想到更多的方法来改进预处理:解码 HTML 实体(我们上面看到的 & 和 <);过滤掉停用词 (代词等);添加更多特征,比如所有字母大写标识等等。

 

第三步:数据转换为向量

现在,我们将每条消息(词干列表)转换成机器学习模型可以理解的向量。

用词袋模型完成这项工作需要三个步骤:

1.  对每个词在每条信息中出现的次数进行计数(词频);
2. 对计数进行加权,这样经常出现的单词将会获得较低的权重(逆向文件频率);
3. 将向量由原始文本长度归一化到单位长度(L2 范式)。

每个向量的维度等于 SMS 语料库中包含的独立词的数量。

In [16]:
 

这里我们使用强大的 python 机器学习训练库 scikit-learn (sklearn),它包含大量的方法和选项。

我们取一个信息并使用新的 bow_tramsformer 获取向量形式的词袋模型计数:

In [17]:
 
In [18]:
 
 

message 4 中有 9 个独立词,它们中的两个出现了两次,其余的只出现了一次。可用性检测,哪些词出现了两次?

In [19]:
 
 

整个 SMS 语料库的词袋计数是一个庞大的稀疏矩阵:

In [20]:
 
 

最终,计数后,使用 scikit-learn 的 TFidfTransformer 实现的 TF-IDF 完成词语加权和归一化。

In [21]:
 
 

单词 “u” 的 IDF(逆向文件频率)是什么?单词“university”的 IDF 又是什么?

In [22]:
 
 

将整个 bag-of-words 语料库转化为 TF-IDF 语料库。

In [23]:
 
 
有许多方法可以对数据进行预处理和向量化。这两个步骤也可以称为“特征工程”,它们通常是预测过程中最耗时间和最无趣的部分,但是它们非常重要并且需要经验。诀窍在于反复评估:分析模型误差,改进数据清洗和预处理方法,进行头脑风暴讨论新功能,评估等等。

第四步:训练模型,检测垃圾信息

我们使用向量形式的信息来训练 spam/ham 分类器。这部分很简单,有很多实现训练算法的库文件。

这里我们使用 scikit-learn,首先选择 Naive Bayes 分类器:

In [24]:
 
 

我们来试着分类一个随机信息:

In [25]:
 
 

太棒了!你也可以用自己的文本试试。

有一个很自然的问题是:我们可以正确分辨多少信息?

In [26]:
 
In [27]:
 
In [28]:
Out[28]:
 
我们可以通过这个混淆矩阵计算精度(precision)和召回率(recall),或者它们的组合(调和平均值)F1:
In [29]:
 
 

有相当多的指标都可以用来评估模型性能,至于哪个最合适是由任务决定的。比如,将“spam”错误预测为“ham”的成本远低于将“ham”错误预测为“spam”的成本。

 

第五步:如何进行实验?

在上述“评价”中,我们犯了个大忌。为了简单的演示,我们使用训练数据进行了准确性评估。永远不要评估你的训练数据。这是错误的。

这样的评估方法不能告诉我们模型的实际预测能力,如果我们记住训练期间的每个例子,训练的准确率将非常接近 100%,但是我们不能用它来分类任何新信息。

一个正确的做法是将数据分为训练集和测试集,在模型拟合和调参时只能使用训练数据,不能以任何方式使用测试数据,通过这个方法确保模型没有“作弊”,最终使用测试数据评价模型可以代表模型真正的预测性能。

In [30]:
 
 
按照要求,测试数据占整个数据集的 20%(总共 5574 条记录中的 1115 条),其余的是训练数据(5574 条中的 4459 条)。

让我们回顾整个流程,将所有步骤放入 scikit-learn 的 Pipeline 中:

In [31]:
 
实际当中一个常见的做法是将训练集再次分割成更小的集合,例如,5 个大小相等的子集。然后我们用 4 个子集训练数据,用最后 1 个子集计算精度(称之为“验证集”)。重复5次(每次使用不同的子集进行验证),这样可以得到模型的“稳定性“。如果模型使用不同子集的得分差异非常大,那么很可能哪里出错了(坏数据或者不良的模型方差)。返回,分析错误,重新检查输入数据有效性,重新检查数据清洗。

在这个例子里,一切进展顺利:

In [32]:
 
 

得分确实比训练全部数据时差一点点( 5574 个训练例子中,准确性 0.97),但是它们相当稳定:

In [33]:
 
 

我们自然会问,如何改进这个模型?这个得分已经很高了,但是我们通常如何改进模型呢?

Naive Bayes 是一个高偏差-低方差的分类器(简单且稳定,不易过度拟合)。与其相反的例子是低偏差-高方差(容易过度拟合)的 k 最临近(kNN)分类器和决策树。Bagging(随机森林)是一种通过训练许多(高方差)模型和求均值来降低方差的方法。

  Python 中的实用数据挖掘_第3张图片

换句话说:

  • 高偏差 = 分类器比较固执。它有自己的想法,数据能够改变的空间有限。另一方面,也没有多少过度拟合的空间(左图)。
  • 低偏差 = 分类器更听话,但也更神经质。大家都知道,让它做什么就做什么可能造成麻烦(右图)。
In [34]:
In [35]:
 
Out[35]:
 
 
 
 
 (我们对数据的 64% 进行了有效训练:保留 20% 的数据作为测试集,保留剩余的 20% 做 5 折交叉验证 = > 0.8*0.8*5574 = 3567个训练数据。)

随着性能的提升,训练和交叉验证都表现良好,我们发现由于数据量较少,这个模型难以足够复杂/灵活地捕获所有的细微差别。在这种特殊案例中,不管怎样做精度都很高,这个问题看起来不是很明显。

关于这一点,我们有两个选择:

  1. 使用更多的训练数据,增加模型的复杂性;
  2. 使用更复杂(更低偏差)的模型,从现有数据中获取更多信息。

在过去的几年里,随着收集大规模训练数据越来越容易,机器越来越快。方法 1 变得越来越流行(更简单的算法,更多的数据)。简单的算法(如 Naive Bayes)也有更容易解释的额外优势(相对一些更复杂的黑箱模型,如神经网络)。

了解了如何正确地评估模型,我们现在可以开始研究参数对性能有哪些影响。

第六步:如何调整参数?

到目前为止,我们看到的只是冰山一角,还有许多其它参数需要调整。比如使用什么算法进行训练。

上面我们已经使用了 Navie Bayes,但是 scikit-learn 支持许多分类器:支持向量机、最邻近算法、决策树、Ensamble 方法等…

Python 中的实用数据挖掘_第4张图片

 

我们会问:IDF 加权对准确性有什么影响?消耗额外成本进行词形还原(与只用纯文字相比)真的会有效果吗?

让我们来看看:

In [37]:
In [38]:
 
 

(首先显示最佳参数组合:在这个案例中是使用 idf=True 和 analyzer=split_into_lemmas 的参数组合)

快速合理性检查

In [39]:
 
 

predict_proba 返回每类(ham,spam)的预测概率。在第一个例子中,消息被预测为 ham 的概率 >99%,被预测为 spam 的概率 <1%。如果进行选择模型会认为信息是 ”ham“:

In [40]:
 
 

在训练期间没有用到的测试集的整体得分:

In [41]:
 
 
这是我们使用词形还原、TF-IDF 和 Navie Bayes 分类器的 ham 检测 pipeline 获得的实际预测性能。

让我们尝试另一个分类器:支持向量机(SVM)。SVM 可以非常迅速的得到结果,它所需要的参数调整也很少(虽然比 Navie Bayes 稍多一点),在处理文本数据方面它是个好的起点。

In [42]:
In [43]:
 

转载于:https://www.cnblogs.com/xyou/p/7676936.html

你可能感兴趣的:(人工智能,python,数据结构与算法)