《机器学习系统设计》之应用scikit-learn做文本分类(上)
前言:
本系列是在作者学习《机器学习系统设计》([美] WilliRichert)过程中的思考与实践,全书通过Python从数据处理。到特征project,再到模型选择,把机器学习解决这个问题的过程一一呈现。
书中设计的源码和数据集已上传到我的资源:http://download.csdn.net/detail/solomon1558/8971649
第3章通过词袋模型+K均值聚类实现相关文本的匹配。本文主要解说文本预处理部分内容。涉及切分文本、数据清洗、计算TF-IDF值等内容。
相关链接:《机器学习系统设计》之应用scikit-learn做文本分类(下)
1. 统计词语
使用一个简单的数据集进行实验,它包含5个文档:
01. txt This is a toy post about machine learning.Actually, it contains not much interesting stuff.
02. txt Imaging databases provide storagecapabilities.
03. txt Most imaging databases safe imagespermanently.
04. txt Imaging databases store data.
05. txt Imaging databases store data. Imagingdatabases store data. Imaging databases store data.
在这个文档数据集中。我们想要找到和文档”imaging database”最相近的文档。为了将原始文本转换成聚类算法能够使用的特征数据,首先须要使用词袋(bag-of-word)方法来衡量文本间相似性。终于生成每一个文本的特征向量。
词袋方法基于简单的词频统计。统计每个帖子中的词频,表示成一个向量,即向量化。Scikit-learn的CountVectorizer能够高效地完毕统计词语的工作,Scikit的函数和类能够通过sklearn包引入进来:
posts = [open(os.path.join(DIR, f)).read() for f in os.listdir(DIR)]
vectorizer = CountVectorizer(min_df=1, stop_words="english")
X_train = vectorizer.fit_transform(posts)
如果待训练的文本存放在文件夹DIR下,我们将数据集传给CountVectorizer。
參数min_df决定了CounterVectorizer怎样处理那些不常常使用的词语(最小文档词频)。当min_df为一个整数时,全部出现次数小于这个值的词语都将被扔掉;当它是一个比例时,将整个数据集中出现比例小于这个值的词语都将被丢弃。
我们须要告诉这个想量化处理器整个数据集的信息,使它能够预先知道都有哪些词语:
X_train = vectorizer.fit_transform(posts)
num_samples, num_features = X_train.shape
print ("#sample: %d, #feature: %d" % (num_samples, num_features))
print(vectorizer.get_feature_names())
程序的输出例如以下。5个文档中包括了25个词语
#sample: 5, #feature: 25
[u'about', u'actually', u'capabilities', u'contains',u'data', u'databases', u'images', u'imaging', u'interesting',
u'is', u'it',u'learning', u'machine', u'most', u'much', u'not', u'permanently', u'post',u'provide', u'safe', u'storage', u'store', u'stuff', u'this', u'toy']
对新文档进行向量化:
#a new post
new_post = "imaging databases"
new_post_vec = vectorizer.transform([new_post])
把每一个样本的词频数组当做向量进行相似度计算,须要使用数组的所有元素[使用成员函数toarray()]。
通过norm()函数计算新文档与所有训练文档向量的欧几里得范数(最小距离),从而衡量它们之间的相似度。
#------- calculate raw distances betwee new and old posts and record the shortest one-------------------------
def dist_raw(v1, v2):
delta = v1 - v2
return sp.linalg.norm(delta.toarray())
best_doc = None
best_dist = sys.maxint
best_i = None
for i in range(0, num_samples):
post = posts[i]
if post == new_post:
continue
post_vec = X_train.getrow(i)
d = dist_raw(post_vec, new_post_vec)
print "=== Post %i with dist = %.2f: %s" % (i, d, post)
if d<best_dist:
best_dist = d
best_i = i
print("Best post is %i with dist=%.2f" % (best_i, best_dist))
=== Post 0 with dist = 4.00:This is a toy post about machine learning. Actually, it contains not muchinteresting
stuff.
=== Post 1 with dist =1.73:Imaging databases provide storage capabilities.
=== Post 2 with dist =2.00:Most imaging databases
safe images permanently.
=== Post 3 with dist =1.41:Imaging databases
store data.
=== Post 4 with dist =5.10:Imaging databases
store data. Imaging databases store data. Imaging databasesstore data.
Best post is 3 with dist=1.41
结果显示文档3与新文档最为相似。然而文档4和文档3的内容一样,但反复了3遍。所以,它和新文档的相似度应该与文档3是一样的。
#-------case study: why post 4 and post 5 different ?-----------
print(X_train.getrow(3).toarray())
print(X_train.getrow(4).toarray())
[[0 0 0 0 1 1 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0]]
[[0 0 0 0 3 3 0 3 0 0 0 0 0 0 0 0 0 0 0 0 0 3 0 0 0]]
2. 文本处理
2.1 词频向量归一化
对第2节中的dist_raw函数进行扩展,在归一化的向量上(向量各分量除以其模长)计算向量间的距离。
def dist_norm(v1, v2):
v1_normalized = v1 / sp.linalg.norm(v1.toarray())
v2_normalized = v2 / sp.linalg.norm(v2.toarray())
delta = v1_normalized - v2_normalized
return sp.linalg.norm(delta.toarray())
=== Post 0 with dist = 1.41: This is a toy post aboutmachine learning. Actually, it contains not much interesting stuff.
=== Post 1 with dist = 0.86: Imaging databases providestorage capabilities.
=== Post 2 with dist = 0.92: Most imaging databasessafe images permanently.
=== Post 3 with dist = 0.77:Imagingdatabases store data.
=== Post 4 with dist = 0.77:Imaging databases store data. Imaging databases
store data. Imaging databasesstore data.
Best post is 3 with dist=0.77
词频向量归一化之后,文档3和文档4与新文档具有了同样的相似度。从词频统计的角度来说,这样处理更为正确。
2.2 排除停用词
文本中类似”the”、”of”这些单词常常出如今各种不同的文本中,被称为停用词。因为停用词对于区分文本没有多大帮助,因此删除停用词是文本处理中的一个常见步骤。
CountVectorizer中有一个简单的參数stop_words()能够完毕该任务:
vectorizer = CountVectorizer(min_df=1, stop_words='english')
2.3词干处理
为了将语义类似但形式不同的词语放在一起统计,我们须要一个函数将词语归约到特定的词干形式。自然语言处理工具包(NLTK)提供了一个很easy嵌入到CountVectorizer的词干处理器。
把文档传入CountVectorizer之前,我们须要对它们进行词干处理。
该类提供了几种钩子。能够用它们定制预处理和词语切分阶段的操作。
预处理器和词语切分器能够当作參数传入构造函数。
我们并不想把词干处理器放入它们不论什么一个其中,由于那样的话,之后还须要亲自对词语进行切分和归一化。相反。我们能够通过改写build_analyzer方法来实现:
import nltk.stem
english_stemmer = nltk.stem.SnowballStemmer('english')
class StemmedCountVectorizer(CountVectorizer):
def build_analyzer(self):
analyzer = super(StemmedCountVectorizer, self).build_analyzer()
return lambda doc: (english_stemmer.stem(w) for w in analyzer(doc))
vectorizer = StemmedCountVectorizer(min_df=1, stop_words='english')
依照例如以下步骤对每一个帖子进行处理:
(1) 在预处理阶段将原始文档变成小写字母形式(这在父类中完毕);
(2) 在词语切分阶段提取全部单词;
(3) 将每一个词语转换成词干形式。
3. 计算TF-IDF
至此,我们採用统计词语的方式,从充满噪声的文本中提取了紧凑的特征向量。这些特征的值就是对应词语在全部训练文本中出现的次数,我们默认较大的特征值意味着合格词语对文本更为重要。
可是在训练文本中,不同的词语对文本的可区分性贡献更大。
这须要通过统计每一个文本的词频,而且对出如今多个文本中的词语在权重上打折来解决。即当某个词语常常出如今一些特定的文本中,而在其它地方非常少出现时。应该赋予该词语更大的权值。
这正是词频-反转文档频率(TF-IDF)所要做的:TF代表统计部分,而IDF把权重折扣考虑了进去。一个简单的实现例如以下:
import scipy as sp
def tfidf(t, d, D):
tf = float(d.count(t)) / sum(d.count(w) for w in set(d))
idf = sp.log(float(len(D)) / (len([doc for doc in D if t in doc])))
return tf * idf
在实际应用过程中,scikit-learn已经将该算法封装进了TfidfVectorizer(继承自CountVectorizer)中。进行这些操作后,我们得到的文档向量不会再包括词语拥挤值,而是每一个词语的TF-IDF值。
代码清单:
import os
import sys
import scipy as sp
from sklearn.feature_extraction.text import CountVectorizer DIR = r"../data/toy"
posts = [open(os.path.join(DIR, f)).read() for f in os.listdir(DIR)]
new_post = "imaging databases" import nltk.stem
english_stemmer = nltk.stem.SnowballStemmer('english') class StemmedCountVectorizer(CountVectorizer):
def build_analyzer(self):
analyzer = super(StemmedCountVectorizer, self).build_analyzer()
return lambda doc: (english_stemmer.stem(w) for w in analyzer(doc))
#vectorizer = StemmedCountVectorizer(min_df=1, stop_words='english') from sklearn.feature_extraction.text import TfidfVectorizer class StemmedTfidfVectorizer(TfidfVectorizer):
def build_analyzer(self):
analyzer = super(StemmedTfidfVectorizer, self).build_analyzer()
return lambda doc: (english_stemmer.stem(w) for w in analyzer(doc)) vectorizer = StemmedTfidfVectorizer(min_df=1, stop_words='english')
print(vectorizer)
X_train = vectorizer.fit_transform(posts) num_samples, num_features = X_train.shape
print("#samples: %d, #features: %d" % (num_samples, num_features)) new_post_vec = vectorizer.transform([new_post])
print(new_post_vec, type(new_post_vec))
print(new_post_vec.toarray())
print(vectorizer.get_feature_names()) def dist_raw(v1, v2):
delta = v1 - v2
return sp.linalg.norm(delta.toarray())
def dist_norm(v1, v2):
v1_normalized = v1 / sp.linalg.norm(v1.toarray())
v2_normalized = v2 / sp.linalg.norm(v2.toarray())
delta = v1_normalized - v2_normalized
return sp.linalg.norm(delta.toarray()) dist = dist_norm
best_dist = sys.maxsize
best_i = None for i in range(0, num_samples):
post = posts[i]
if post == new_post:
continue
post_vec = X_train.getrow(i)
d = dist(post_vec, new_post_vec)
print("=== Post %i with dist=%.2f: %s" % (i, d, post))
if d < best_dist:
best_dist = d
best_i = i
print("Best post is %i with dist=%.2f" % (best_i, best_dist))
4. 总结
文本预处理过程包括的步骤总结例如以下:
(1) 切分文本;
(2) 扔掉出现过于频繁。而又对匹配相关文档没有帮助的词语;
(3) 扔掉出现频率非常低。仅仅有非常小可能出如今未来帖子中的词语;
(4) 统计剩余的词语。
(5) 考虑整个预料集合,从词频统计中计算TF-IDF值。
通过这一过程。我们将一堆充满噪声的文本转换成了一个简明的特征表示。
然而,尽管词袋模型及其扩展简单有效,但仍然有一些缺点须要注意:
(1) 它并不涵盖词语之间的关联关系。採用之前的向量化方法,文本”Car hits wall”和”Wall hits car”会有同样的特征向量。
(2) 它没法捕捉否定关系。
比如”I will eat ice cream”和”I will not eat ice cream”。虽然它们意思截然相反。但从特征向量来看它们非常相似。这个问题事实上非常easy解决,仅仅须要既统计单个词语(又叫unigrams)。又考虑成队的词语(bigrams)或者trigrams(一行中的三个词语)就可以。
(3) 对于拼写错误的词语会处理失败。
《机器学习系统设计》之应用scikit-learn做文本分类(上)的更多相关文章
- 使用CNN做文本分类——将图像2维卷积换成1维
使用CNN做文本分类 from __future__ import division, print_function, absolute_import import tensorflow as tf ...
- Spark ML下实现的多分类adaboost+naivebayes算法在文本分类上的应用
1. Naive Bayes算法 朴素贝叶斯算法算是生成模型中一个最经典的分类算法之一了,常用的有Bernoulli和Multinomial两种.在文本分类上经常会用到这两种方法.在词袋模型中,对于一 ...
- NLP系列(2)_用朴素贝叶斯进行文本分类(上)
作者:龙心尘 && 寒小阳 时间:2016年1月. 出处: http://blog.csdn.net/longxinchen_ml/article/details/50597149 h ...
- 如何使用scikit—learn处理文本数据
答案在这里:http://www.tuicool.com/articles/U3uiiu http://scikit-learn.org/stable/modules/feature_extracti ...
- 应用scikit-learn做文本分类
文本挖掘的paper没找到统一的benchmark,只好自己跑程序,走过路过的前辈如果知道20newsgroups或者其它好用的公共数据集的分类(最好要所有类分类结果,全部或取部分特征无所谓)麻烦留言 ...
- 应用scikit-learn做文本分类(转)
文本挖掘的paper没找到统一的benchmark,只好自己跑程序,走过路过的前辈如果知道20newsgroups或者其它好用的公共数据集的分类(最好要所有类分类结果,全部或取部分特征无所谓)麻烦留言 ...
- 基于SVMLight的文本分类
支持向量机(Support Vector Machine)是Cortes和Vapnik于1995年首先提出的,它在解决小样本 .非线性及高维模式识别 中表现出许多特有的优势,并能够推广应用到函数拟合等 ...
- 基于Naive Bayes算法的文本分类
理论 什么是朴素贝叶斯算法? 朴素贝叶斯分类器是一种基于贝叶斯定理的弱分类器,所有朴素贝叶斯分类器都假定样本每个特征与其他特征都不相关.举个例子,如果一种水果其具有红,圆,直径大概3英寸等特征,该水果 ...
- 文本分类实战(十)—— BERT 预训练模型
1 大纲概述 文本分类这个系列将会有十篇左右,包括基于word2vec预训练的文本分类,与及基于最新的预训练模型(ELMo,BERT等)的文本分类.总共有以下系列: word2vec预训练词向量 te ...
随机推荐
- 【BZOJ4002】[JLOI2015]有意义的字符串 - 矩阵乘法
题意: 给出b,d,n,求$\lfloor(\frac{b+\sqrt{d}}{2})^n\rfloor \mod 999999999999999989$(原题是7528443412579576937 ...
- mysql 定时每秒插入一条数据
1.创建表 2.创建存储过程 CREATE PROCEDURE user()INSERT INTO user(name,sex) VALUES ('1111','1'); 3.创建定时器 CREATE ...
- Zookeeper入门-Linux环境下异常ConnectionLossException解决
实际项目开发中,用的是Linux环境. 中午突然断电,死活连不上Zookeeper,最终发现是需要关闭防火墙. 看日志,报错如下: Exception in thread "mai ...
- Material Design学习之 Button(具体分析,传说中的水滴动画)
转载请注明出处:王亟亟的大牛之路 上一篇大致介绍了Material Design的一些基本概念传送门:http://blog.csdn.net/ddwhan0123/article/details/5 ...
- OC第二课
主要内容:实例变量可见度.方法 一.实例变量可见度 public(共同拥有的):实例变量能够在类的内部和外部使用 protected(受保护的.默认的):实例变量仅仅能在该类及其子类中使用 priva ...
- 更改linux文件的拥有者及用户组(chown和chgrp)
.使用chown命令更改文件拥有者 在 shell 中,能够使用chown命令来改变文件全部者.chown命令是change owner(改变拥有者)的缩写.须要要注意的是,用户必须是已经存在系统中的 ...
- 前端到后台ThinkPHP开发整站--php开发案例
前端到后台ThinkPHP开发整站--php开发案例 总结 还是需要做几个案例,一天一个为佳,那样才能做得快. 从需求分析着手,任务体系要构建好,这样才能非常高效. 转自: 前端到后台ThinkPHP ...
- [Codeforces Round #194 (Div. 2)] Secret 解题报告 (数学)
题目链接:http://codeforces.com/problemset/problem/334/C 题目: 题目大意: 给定数字n,要求构建一个数列使得数列的每一个元素的值都是3的次方,数列之和S ...
- 2.LINUX常用命令
Linux 必备指令摘要一般用户指令/bin 指令 功能说明 范例 bash GNU Bouren-Again Shell bash shell_script cat 观看一般文本文件 cat fil ...
- [ xml ] [ log4j2 ] No grammar constraints (DTD or XML Schema) referenced in the document.
<!DOCTYPE xml> http://rx1226.pixnet.net/blog/post/321584550