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

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

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

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

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

IPython notebook,是 Python 的一个工具,它是一个以 HTML 形式呈现的交互环境,可以通过它立刻看到结果。我们也将重温其它广泛用于数据科学领域的实用工具。

想交互运行下面的例子(选读)?

安装免费的 Anaconda Python 发行版,其中已经包含 Python 本身。

安装“自然语言处理”库——TextBlob:安装包在这。

下载本文的源码(网址:http://radimrehurek.com/data_science_python/data_science_python.ipynb 并运行:$ ipython notebook data_science_python.ipynb

观看 IPython notebook 基本用法教程 IPython tutorial video 。

运行下面的第一个代码,如果执行过程没有报错,就可以了。

端到端的例子:自动过滤垃圾信息

In [1]:

%matplotlibinline

importmatplotlib.pyplot asplt

importcsv

fromtextblob importTextBlob

importpandas

importsklearn

importcPickle

importnumpy asnp

fromsklearn.feature_extraction.text importCountVectorizer,TfidfTransformer

fromsklearn.naive_bayes importMultinomialNB

fromsklearn.svm importSVC,LinearSVC

fromsklearn.metrics importclassification_report,f1_score,accuracy_score,confusion_matrix

fromsklearn.pipeline importPipeline

fromsklearn.grid_search importGridSearchCV

fromsklearn.cross_validation importStratifiedKFold,cross_val_score,train_test_split

fromsklearn.tree importDecisionTreeClassifier

fromsklearn.learning_curve importlearning_curve

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

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

$ls -ldata

total 1352

-rw-r--r--@ 1kofola  staff477907Mar152011SMSSpamCollection

-rw-r--r--@ 1kofola  staff5868Apr182011readme

-rw-r-----@ 1kofola  staff203415Dec115:30smsspamcollection.zip

这份文件包含了 5000 多份 SMS 手机信息(查看 readme 文件以获得更多信息):

In [2]:

messages= [line.rstrip()forline inopen('./data/SMSSpamCollection')]

print len(messages)

5574

文本集有时候也称为“语料库”,我们来打印 SMS 语料库中的前 10 条信息:

In [3]:

formessage_no,message inenumerate(messages[:10]):

printmessage_no,message

0ham    Go until jurong point,crazy..Available only inbugisngreat world laebuffet...Cine there got amore wat...

1ham   Ok lar...Joking wifuoni...

2spam  Free entry in2awkly comp to win FA Cup final tkts21stMay2005.Text FA to87121to receive entry question(std txt rate)T&C's apply 08452810075over18's

3hamUdun say so early hor...Ucalready then say...

4ham   NahIdon't think he goes to usf, he lives around here though

5 spam  FreeMsg Hey there darling it'sbeen3week's now and no word back! I'dlike some fun you up forit still?Tb ok!XxX std chgs to send,£1.50to rcv

6ham   Even my brother isnotlike to speak withme.They treat me like aids patent.

7ham   Asper your request'Melle Melle (Oru Minnaminunginte Nurungu Vettam)'has been setasyour callertune forallCallers.Press*9to copyyour friends Callertune

8spam  WINNER!! Asavalued network customer you have been selected to receivea£900prize reward! To claim call09061701461.Claim codeKL341.Valid12hours only.

9spam  Had your mobile11months ormore?URentitled to Update to the latest colour mobiles withcamera forFree!Call The Mobile Update Co FREE on08002986030

我们看到一个 TSV 文件(用制表符 tab 分隔),它的第一列是标记正常信息(ham)或“垃圾文件”(spam)的标签,第二列是信息本身。

这个语料库将作为带标签的训练集。通过使用这些标记了 ham/spam 例子,我们将训练一个自动分辨 ham/spam 的机器学习模型。然后,我们可以用训练好的模型将任意未标记的信息标记为 ham 或 spam。

我们可以使用 Python 的 Pandas 库替我们处理 TSV 文件(或 CSV 文件,或 Excel 文件):

In [4]:

messages= pandas.read_csv('./data/SMSSpamCollection',sep='t',quoting=csv.QUOTE_NONE,

names=["label","message"])

printmessages

我们也可以使用 pandas 轻松查看统计信息:

In [5]:

messages.groupby('label').describe()

out[5]:

这些信息的长度是多少:

In [6]:

messages['length']= messages['message'].map(lambdatext: len(text))

printmessages.head()

In [7]:

messages.length.plot(bins=20, kind='hist')

Out[7]:

In [8]:

messages.length.describe()

Out[8]:

哪些是超长信息?

In [9]:

print list(messages.message[messages.length > 900])

spam 信息与 ham 信息在长度上有区别吗?

In [10]:

messages.hist(column='length', by='label', bins=50)

Out[10]:

array([,

], dtype=object)

太棒了,但是我们怎么能让电脑自己识别文字信息?它可以理解这些胡言乱语吗?

第二步:数据预处理

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

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

与第一步的方法一样,让我们写一个将信息分割成单词的函数:

In [11]:

defsplit_into_tokens(message):

message= unicode(message,'utf8')# convert bytes into proper unicode

returnTextBlob(message).words

这还是原始文本的一部分:

In [12]:

messages.message.head()

Out[12]:

这是原始文本处理后的样子:

In [13]:

messages.message.head().apply(split_into_tokens)

Out[13]:

自然语言处理(NLP)的问题:

大写字母是否携带信息?

单词的不同形式(“goes”和“go”)是否携带信息?

叹词和限定词是否携带信息?

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

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

In [14]:

TextBlob("Hello world, how is it going?").tags  # list of (word, POS) pairs

Out[14]:

并将单词标准化为基本形式 (lemmas):

In [15]:

Out[15]:

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

第三步:数据转换为向量

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

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

1.  对每个词在每条信息中出现的次数进行计数(词频);

2. 对计数进行加权,这样经常出现的单词将会获得较低的权重(逆向文件频率);

3. 将向量由原始文本长度归一化到单位长度(L2 范式)。

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

In [16]:

bow_transformer= CountVectorizer(analyzer=split_into_lemmas).fit(messages['message'])

printlen(bow_transformer.vocabulary_)

8874

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

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

In [17]:

message4= messages['message'][3]

printmessage4

U dun say so early hor... U c already then say...

In [18]:

bow4= bow_transformer.transform([message4])

printbow4

printbow4.shape

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

In [19]:

printbow_transformer.get_feature_names()[6736]

printbow_transformer.get_feature_names()[8013]

say

u

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

In [20]:

messages_bow= bow_transformer.transform(messages['message'])

print'sparse matrix shape:',messages_bow.shape

print'number of non-zeros:',messages_bow.nnz

print'sparsity: %.2f%%'% (100.0* messages_bow.nnz/ (messages_bow.shape[0]* messages_bow.shape[1]))

最终,计数后,使用 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]:

print'accuracy',accuracy_score(messages['label'],all_predictions)

print'confusion matrixn',confusion_matrix(messages['label'],all_predictions)

print'(row=expected, col=predicted)'

In [28]:

plt.matshow(confusion_matrix(messages['label'],all_predictions),cmap=plt.cm.binary,interpolation='nearest')

plt.title('confusion matrix')

plt.colorbar()

plt.ylabel('expected label')

plt.xlabel('predicted label')

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(随机森林)是一种通过训练许多(高方差)模型和求均值来降低方差的方法。

换句话说:

高偏差 = 分类器比较固执。它有自己的想法,数据能够改变的空间有限。另一方面,也没有多少过度拟合的空间(左图)。

低偏差 = 分类器更听话,但也更神经质。大家都知道,让它做什么就做什么可能造成麻烦(右图)。

In [34]:

In [35]:

%time plot_learning_curve(pipeline, "accuracy vs. training set size", msg_train, label_train, cv=5)

CPU times: user382ms,sys: 83.1ms,total: 465ms

Wall time: 28.5s

Out[35]:

(我们对数据的 64% 进行了有效训练:保留 20% 的数据作为测试集,保留剩余的 20% 做 5 折交叉验证 = > 0.8*0.8*5574 = 3567个训练数据。)

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

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

使用更多的训练数据,增加模型的复杂性;

使用更复杂(更低偏差)的模型,从现有数据中获取更多信息。

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

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

第六步:如何调整参数?

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

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

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

让我们来看看:

In [37]:

In [38]:

%timenb_detector= grid.fit(msg_train,label_train)

printnb_detector.grid_scores_

CPU times: user4.09s,sys: 291ms,total: 4.38s

Wall time: 20.2s

[mean: 0.94752,std: 0.00357,params: {'tfidf__use_idf': True,'bow__analyzer': <function split_into_lemmas at0x1131e8668>},mean: 0.92958,std: 0.00390,params: {'tfidf__use_idf': False,'bow__analyzer': <function split_into_lemmas at0x1131e8668>},mean: 0.94528,std: 0.00259,params: {'tfidf__use_idf': True,'bow__analyzer': <function split_into_tokens at0x11270b7d0>},mean: 0.92868,std: 0.00240,params: {'tfidf__use_idf': False,'bow__analyzer': <function split_into_tokens at0x11270b7d0>}]

(首先显示最佳参数组合:在这个案例中是使用 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]:

pipeline_svm= Pipeline([

('bow',CountVectorizer(analyzer=split_into_lemmas)),

('tfidf',TfidfTransformer()),

('classifier',SVC()),# <== change here

])

# pipeline parameters to automatically explore and tune

param_svm= [

{'classifier__C': [1,10,100,1000],'classifier__kernel': ['linear']},

{'classifier__C': [1,10,100,1000],'classifier__gamma': [0.001,0.0001],'classifier__kernel': ['rbf']},

]

grid_svm= GridSearchCV(

pipeline_svm,# pipeline from above

param_grid=param_svm,# parameters to tune via cross validation

refit=True,# fit using all data, on the best detected classifier

n_jobs=-1,# number of cores to use for parallelization; -1 for "all cores"

scoring='accuracy',# what score are we optimizing?

cv=StratifiedKFold(label_train,n_folds=5),# what type of cross validation to use

)

In [43]:

%timesvm_detector= grid_svm.fit(msg_train,label_train)# find the best combination from param_svm

printsvm_detector.grid_scores_

CPU times: user5.24s,sys: 170ms,total: 5.41s

Wall time: 1min8s

[mean: 0.98677,std: 0.00259,params: {'classifier__kernel': 'linear','classifier__C': 1},mean: 0.98654,std: 0.00100,params: {'classifier__kernel': 'linear','classifier__C': 10},mean: 0.98654,std: 0.00100,params: {'classifier__kernel':'linear','classifier__C': 100},mean: 0.98654,std: 0.00100,params: {'classifier__kernel': 'linear','classifier__C': 1000},mean: 0.86432,std: 0.00006,params: {'classifier__gamma': 0.001,'classifier__kernel': 'rbf','classifier__C': 1},mean:0.86432,std: 0.00006,params: {'classifier__gamma': 0.0001,'classifier__kernel': 'rbf','classifier__C': 1},mean: 0.86432,std: 0.00006,params: {'classifier__gamma': 0.001,'classifier__kernel': 'rbf','classifier__C': 10},mean: 0.86432,std:0.00006,params: {'classifier__gamma': 0.0001,'classifier__kernel': 'rbf','classifier__C': 10},mean: 0.97040,std:0.00587,params: {'classifier__gamma': 0.001,'classifier__kernel': 'rbf','classifier__C': 100},mean: 0.86432,std:0.00006,params:

python第30讲数据挖掘_Python 中的实用数据挖掘相关推荐

  1. python 小说数据挖掘_Python 中的实用数据挖掘

    第三步:数据转换为向量 现在,我们将每条消息(词干列表)转换成机器学习模型可以理解的向量. 用词袋模型完成这项工作需要三个步骤: 1. 对每个词在每条信息中出现的次数进行计数(词频): 2. 对计数进 ...

  2. python爬取喜马拉雅收费_Python中使用requests和parsel爬取喜马拉雅电台音频

    场景 喜马拉雅电台: 找到一步小说音频,这里以下面为例 实现 找到下载地址 使用谷歌浏览器打开上面网址,按F12打开调试,点击播放按钮后,然后找到Network下的Media下的Headers下的Re ...

  3. python里的join方法_python中join()方法介绍

    描述 Python join() 方法用于将序列中的元素以指定的字符连接生成一个新的字符串. 语法 join()方法语法: str . join ( sequence ) 参数 sequence -- ...

  4. python常用函数的用法_python中常用函数整理

    1.map map是python内置的高阶函数,它接收一个函数和一个列表,函数依次作用在列表的每个元素上,返回一个可迭代map对象. class map(object):""&qu ...

  5. python怎么用第三方库_python中第三方库的下载方法

    1.最常用:在命令行中输入  pip install "库名称"  例如 pip install gensim 查看pip的命令集: pip uninstall "库名& ...

  6. python新式类c3算法_python中的MRO和C3算法

    一. 经典类和新式类 1.python多继承 在继承关系中,python子类自动用友父类中除了私有属性外的其他所有内容.python支持多继承.一个类可以拥有多个父类 2.python2和python ...

  7. python下载哪一个安装包_python中正确安装对应版本的包

    python在写机器学习的代码过程中,需要引入如numpy.matpoltlib.pandas等等等.有时候直接pip install+对应的包名并不能正确安装,或者安装后依旧会出现安装不完全的情况. ...

  8. python购物车结算不了_python中购物车

    python的基础语法 python数据类型:(按特征划分) 数字类型: 1.整形:布尔形.长整型L.标准整形 2.非整形:双精度型.复数.decimal(不是内建类型) 序列类型: 1.字符串(st ...

  9. 简述python常用的函数模块_Python中常用的Python time模块常用函数

    常用函数 time.time()函数 定义 返回当前时间的时间戳(1970纪元后经过的浮点秒数) 语法 time.time() 代码 print("time.time(): %f " ...

最新文章

  1. 编码(2)从字节理解Unicode(UTF8/UTF16)
  2. mongodb单表最大记录数_SpringBoot+Cloud全家桶微服务实战项目之文档型数据库MongoDB四...
  3. vue better-scroll 使用 下拉刷新、上拉加载
  4. 3-3HDFS中文件的读写操作
  5. 这就是数据分析之numpy
  6. 添加rabbitmq守护进程
  7. bootstrap课程4 bootstrap的css样式有哪些内容需要注意
  8. 零基础学python电子书-资料│最适合大学生零基础学的Python视频+电子书
  9. 在MacOS系统中如何自定义屏幕保护程序?
  10. python numpy 数据类型为python对象-关于Numpy数据类型对象(dtype)使用详解
  11. mysql 表单记录主键重新从1开始排序
  12. k380没有验证码_罗技K380蓝牙键盘
  13. 2014第五届蓝桥杯预赛试题本科c++史丰收速算
  14. 使用itextpdf进行动态盖章,解决PDF页数不确定时签章位置变化问题
  15. SqlServer高级函数使用
  16. 用c语言验证欧拉定理,欧拉定理_欧拉定理的意义
  17. android友盟微信分享到朋友圈,干货,Umeng分享纯图片(避免跳坑)到_微信,朋友圈等...
  18. python图像预处理-(二)腐蚀与膨胀
  19. 荣耀30可以升级鸿蒙系统,惊喜!荣耀手机也能升鸿蒙:这5款机型用户有福了
  20. python的Tkinter库简单应用——开发一个简易计算器

热门文章

  1. 查看win8序列号的方法图文教程【系统天地】
  2. HTML5/CSS3基础——div盒子水平垂直居中的三种方案
  3. 206658-92-6,Tacrine hydrochloride hydrate盐酸他克林水合物,胆碱酯酶抑制剂
  4. 下载 blob视频, 如何下载网站中的blob:https:// 视频
  5. 配置单 核显本排行榜 最新项目
  6. 推荐系统详解(九)常见框架
  7. 关于cad使用cuiload加载菜单文件cui
  8. 微信小程序是如何实现快速编译的?
  9. Offer 62. 约瑟环(规律问题-递归-中等)
  10. HTML图片链接边框半透明,请教,下图登录页如何用css实现?半透明的边框怎么做?...