机器学习---文本特征提取之词袋模型(Machine Learning Text Feature Extraction Bag of Words)

假设有一段文本:"I have a cat, his name is Huzihu. Huzihu is really cute and friendly. We are good friends." 那么怎么提取这段文本的特征呢?

一个简单的方法就是使用词袋模型(bag of words model)。选定文本内一定的词放入词袋,统计词袋内所有词出现的频率(忽略语法和单词出现的顺序),把词频(term frequency)用向量的形式表示出来。

词频统计可以用scikit-learn的CountVectorizer实现:

text1="I have a cat, his name is Huzihu. Huzihu is really cute and friendly. We are good friends." 

from sklearn.feature_extraction.text import CountVectorizer
CV=CountVectorizer()
words=CV.fit_transform([text1]) #这里注意要把文本字符串变为列表进行输入
print(words)

首先CountVectorizer将文本映射成字典,字典的键是文本内的词,值是词的索引,然后对字典进行学习,将其转换成词频矩阵并输出:

  (0, 3)        1
  (0, 4)        1
  (0, 0)        1
  (0, 11)       1
  (0, 2)        1
  (0, 10)       1
  (0, 7)        2
  (0, 8)        2
  (0, 9)        1
  (0, 6)        1
  (0, 1)        1
  (0, 5)        1
(0, 7)        2  代表第7个词"Huzihu"出现了2次。

注:CountVectorizer类会把文本全部转换成小写,然后将文本词块化(tokenize)。文本词块化是把句子分割成词块(token)或有意义的字母序列的过程。词块大多是单词,但它们也可能是一些短语,如标点符号和词缀。CountVectorizer类通过正则表达式用空格分割句子,然后抽取长度大于等于2的字母序列。(摘自:http://lib.csdn.net/article/machinelearning/42813

我们一般提取文本特征是用于文档分类,那么就需要知道各个文档之间的相似程度。可以通过计算文档特征向量之间的欧氏距离(Euclidean distance)来进行比较。

让我们添加另外两段文本,看看这三段文本之间的相似程度如何。

文本二:"My cousin has a cute dog. He likes sleeping and eating. He is friendly to others."

文本三:"We all need to make plans for the future, otherwise we will regret when we‘re old."

text1="I have a cat, his name is Huzihu. Huzihu is really cute and friendly. We are good friends."
text2="My cousin has a cute dog. He likes sleeping and eating. He is friendly to others."
text3= "We all need to make plans for the future, otherwise we will regret when we‘re old."

corpus=[text1,text2,text3] #把三个文档放入语料库

from sklearn.feature_extraction.text import CountVectorizer
CV=CountVectorizer()
words=CV.fit_transform(corpus)
words_frequency=words.todense()  #用todense()转化成矩阵
print(CV.get_feature_names())
print(words_frequency)

此时分别输出的是特征名称和由每个文本的词频向量组成的矩阵:

[‘all‘, ‘and‘, ‘are‘, ‘cat‘, ‘cousin‘, ‘cute‘, ‘dog‘, ‘eating‘, ‘for‘, ‘friendly‘, ‘friends‘, ‘future‘, ‘good‘, ‘has‘, ‘have‘, ‘he‘, ‘his‘, ‘huzihu‘, ‘is‘, ‘likes‘, ‘make‘, ‘my‘, ‘name‘, ‘need‘, ‘old‘, ‘others‘, ‘otherwise‘, ‘plans‘, ‘re‘, ‘really‘, ‘regret‘, ‘sleeping‘, ‘the‘, ‘to‘, ‘we‘, ‘when‘, ‘will‘]
[[0 1 1 ..., 1 0 0]
 [0 1 0 ..., 0 0 0]
 [1 0 0 ..., 3 1 1]]

可以看到,矩阵第一列,其中前两个数都为0,最后一个数为1,代表"all"在前两个文本中都未出现过,而在第三个文本中出现了一次。

接下来,我们就可以用sklearn中的euclidean_distances来计算这三个文本特征向量之间的距离了。

from sklearn.metrics.pairwise import euclidean_distances
for i,j in ([0,1],[0,2],[1,2]):
    dist=euclidean_distances(words_frequency[i],words_frequency[j])
    print("文本{}和文本{}特征向量之间的欧氏距离是:{}".format(i+1,j+1,dist))

输出如下:

文本1和文本2特征向量之间的欧氏距离是:[[ 5.19615242]]
文本1和文本3特征向量之间的欧氏距离是:[[ 6.08276253]]
文本2和文本3特征向量之间的欧氏距离是:[[ 6.164414]]

可以看到,文本一和文本二之间最相似。

现在思考一下,应该选什么样的词放入词袋呢?有一些词并不能提供多少有用的信息,比如:the, be, you, he...这些词被称为停用词(stop words)。由于文本内包含的词的数量非常之多(词袋内的每一个词都是一个维度),因此我们需要尽量减少维度,去除这些噪音,以便更好地计算和拟合。

可以在创建CountVectorizer实例时添加stop_words="english"参数来去除这些停用词。

另外,也可以下载NLTK(Natural Language Toolkit)自然语言工具包,使用其里面的停用词。

下面,我们就用NLTK来试一试(使用之前,请大家先下载安装:pip install NLTK):

text1="I have a cat, his name is Huzihu. Huzihu is really cute and friendly. We are good friends."
text2="My cousin has a cute dog. He likes sleeping and eating. He is friendly to others."
text3= "We all need to make plans for the future, otherwise we will regret when we‘re old."

corpus=[text1,text2,text3]

from nltk.corpus import stopwords
noise=stopwords.words("english")

from sklearn.feature_extraction.text import CountVectorizer
CV=CountVectorizer(stop_words=noise)
words=CV.fit_transform(corpus)
words_frequency=words.todense()
print(CV.get_feature_names())
print(words_frequency)

输出:

[‘cat‘, ‘cousin‘, ‘cute‘, ‘dog‘, ‘eating‘, ‘friendly‘, ‘friends‘, ‘future‘, ‘good‘, ‘huzihu‘, ‘likes‘, ‘make‘, ‘name‘, ‘need‘, ‘old‘, ‘others‘, ‘otherwise‘, ‘plans‘, ‘really‘, ‘regret‘, ‘sleeping‘]
[[1 0 1 ..., 1 0 0]
 [0 1 1 ..., 0 0 1]
 [0 0 0 ..., 0 1 0]]

可以看到,此时词袋里的词减少了。通过查看words_frequncy.shape,我们发现特征向量的维度也由原来的37变为了21。

还有一个需要考虑的情况,比如说文本中出现的friendly和friends意思相近,可以看成是一个词。但是由于之前把这两个词分别算成是两个不同的特征,这就可能导致文本分类出现偏差。解决办法是对单词进行词干提取(stemming),再把词干放入词袋。

下面用NLTK中的SnowballStemmer来提取词干(注意:需要先用正则表达式把文本中的词提取出来,也就是进行词块化,再提取词干,因此在用CountVectorizer时可以把tokenizer参数设为自己写的function):

text1="I have a cat, his name is Huzihu. Huzihu is really cute and friendly. We are good friends."
text2="My cousin has a cute dog. He likes sleeping and eating. He is friendly to others."
text3= "We all need to make plans for the future, otherwise we will regret when we‘re old."

corpus=[text1,text2,text3]

from nltk import RegexpTokenizer
from nltk.stem.snowball import SnowballStemmer

def stemming(token):
    stemming=SnowballStemmer("english")
    stemmed=[stemming.stem(each) for each in token]
    return stemmed

def tokenize(text):
    tokenizer=RegexpTokenizer(r‘\w+‘)  #设置正则表达式规则
    tokens=tokenizer.tokenize(text)
    stems=stemming(tokens)
    return stems

from nltk.corpus import stopwords
noise=stopwords.words("english")

from sklearn.feature_extraction.text import CountVectorizer
CV=CountVectorizer(stop_words=noise,tokenizer=tokenize,lowercase=False)

words=CV.fit_transform(corpus)
words_frequency=words.todense()
print(CV.get_feature_names())
print(words_frequency)

输出:

[‘cat‘, ‘cousin‘, ‘cute‘, ‘dog‘, ‘eat‘, ‘friend‘, ‘futur‘, ‘good‘, ‘huzihu‘, ‘like‘, ‘make‘, ‘name‘, ‘need‘, ‘old‘, ‘otherwis‘, ‘plan‘, ‘realli‘, ‘regret‘, ‘sleep‘]
[[1 0 1 ..., 1 0 0]
 [0 1 1 ..., 0 0 1]
 [0 0 0 ..., 0 1 0]]

可以看到,friendly和friends在提取词干后都变成了friend。而others提取词干后变为other,other属于停用词,被移除了,因此现在词袋特征向量维度变成了19。

此外,还需注意的是词形的变化。比如说单复数:"foot"和"feet",过去式和现在进行时:"understood"和"understanding",主动和被动:"eat"和"eaten",等等。这些词都应该被视为同一个特征。解决的办法是进行词形还原(lemmatization)。这里就不演示了,可以用NLTK中的WordNetLemmatizer来进行词形还原(from nltk.stem.wordnet import WordNetLemmatizer)。

词干提取和词形还原的区别可参见:https://www.neilx.com/blog/?p=1425

最后,再想一下,我们在对文档进行分类时,假如某个词在文档中都有出现,那么这个词就无法给分类带来多少有用的信息。因此,对于出现频率高的词和频率低的词,我们应该区分对待,它们的重要性是不一样的。解决的办法就是用TF-IDF(term frequncy, inverse document frequency)来给词进行加权。TF-IDF会根据单词在文本中出现的频率进行加权,出现频率高的词,加权系数就低,反之,出现频率低的词,加权系数就高。可以用sklearn的TfidfVectorizer来实现。

下面,我们把CountVectorizer换成TfidfVectorizer(包括之前使用过的提取词干和去除停用词),再来计算一下这三个文本之间的相似度:

text1="I have a cat, his name is Huzihu. Huzihu is really cute and friendly. We are good friends."
text2="My cousin has a cute dog. He likes sleeping and eating. He is friendly to others."
text3= "We all need to make plans for the future, otherwise we will regret when we‘re old."

corpus=[text1,text2,text3]

from nltk import RegexpTokenizer
from nltk.stem.snowball import SnowballStemmer

def stemming(token):
    stemming=SnowballStemmer("english")
    stemmed=[stemming.stem(each) for each in token]
    return stemmed

def tokenize(text):
    tokenizer=RegexpTokenizer(r‘\w+‘)  #设置正则表达式规则
    tokens=tokenizer.tokenize(text)
    stems=stemming(tokens)
    return stems

from nltk.corpus import stopwords
noise=stopwords.words("english")

from sklearn.feature_extraction.text import TfidfVectorizer
CV=TfidfVectorizer(stop_words=noise,tokenizer=tokenize,lowercase=False)

words=CV.fit_transform(corpus)
words_frequency=words.todense()
print(CV.get_feature_names())
print(words_frequency)

from sklearn.metrics.pairwise import euclidean_distances
for i,j in ([0,1],[0,2],[1,2]):
    dist=euclidean_distances(words_frequency[i],words_frequency[j])
    print("文本{}和文本{}特征向量之间的欧氏距离是:{}".format(i+1,j+1,dist))

输出:

[‘cat‘, ‘cousin‘, ‘cute‘, ‘dog‘, ‘eat‘, ‘friend‘, ‘futur‘, ‘good‘, ‘huzihu‘, ‘like‘, ‘make‘, ‘name‘, ‘need‘, ‘old‘, ‘otherwis‘, ‘plan‘, ‘realli‘, ‘regret‘, ‘sleep‘]
[[ 0.30300252  0.          0.23044123 ...,  0.30300252  0.          0.        ]
 [ 0.          0.40301621  0.30650422 ...,  0.          0.          0.40301621]
 [ 0.          0.          0.         ...,  0.          0.37796447  0.        ]]
文本1和文本2特征向量之间的欧氏距离是:[[ 1.25547312]]
文本1和文本3特征向量之间的欧氏距离是:[[ 1.41421356]]
文本2和文本3特征向量之间的欧氏距离是:[[ 1.41421356]]

可以看到,现在特征值不再是0和1了,而是加权之后的值。虽然我们只用了很短的文本进行测试,但还是能看出来,经过一系列优化后,计算出的结果更准确了。

词袋模型的缺点: 1. 无法反映词之间的关联关系。例如:"Humans like cats."和"Cats like humans"具有相同的特征向量。

2. 无法捕捉否定关系。例如:"I will not eat noodles today."和"I will eat noodles today."尽管意思相反,但是从特征向量来看它们非常相似。不过这个问题可以通过设置n-gram来解决(比如可以在用sklearn创建CountVectorizer实例时加上ngram_range参数)。

原文地址:https://www.cnblogs.com/HuZihu/p/9576794.html

时间: 2024-08-25 23:17:29

机器学习---文本特征提取之词袋模型(Machine Learning Text Feature Extraction Bag of Words)的相关文章

机器学习【1】(Python Machine Learning读书笔记)

依旧是作为读书笔记发布,不涉及太多代码和工具,作为了解性文章来介绍机器学习. 文章主要分为两个部分,machine learning的概述和 scikit-learn的简单介绍,两部分关系紧密,合并书写,以致整体篇幅较长,分为1.2两篇. 首先,是关于机器学习.要点如下: 1.1 机器学习三种主要方式 1.2 四大过程 1.3 python相关实现(安装包) [1.1] 机器学习方式主要有三大类:supervised learning(监督式学习), unsupervised learning(

机器学习笔记1 - Hello World In Machine Learning

前言 Alpha Go在16年以4:1的战绩打败了李世石,17年又以3:0的战绩战胜了中国围棋天才柯洁,这真是科技界振奋人心的进步.伴随着媒体的大量宣传,此事变成了妇孺皆知的大事件.大家又开始激烈的讨论机器人什么时候会取代人类统治世界的问题. 其实人工智能在上世纪5.60年代就开始进入了理论研究阶段,人们在不断探索人工智能技术的同时,也担忧起机器人会不会替代人类.然而现实比理想残酷的多,由于当时各种条件的限制(理论基础.技术基础.数据基础.硬件性能等),人工智能相关的项目进度缓慢,也缺少实际成效

文本特征提取函数: 词袋法与TF-IDF(代码理解)

文本特征提取函数一:CountVectorizer() CountVectorizer()函数只考虑每个单词出现的频率:然后构成一个特征矩阵,每一行表示一个训练文本的词频统计结果.其思想是,先根据所有训练文本,不考虑其出现顺序,只将训练文本中每个出现过的词汇单独视为一列特征,构成一个词汇表(vocabulary list),该方法又称为词袋法(Bag of Words). 举例解释说明: from sklearn.feature_extraction.text import CountVecto

斯坦福机器学习视频笔记 Week6 关于机器学习的建议 Advice for Applying Machine Learning

我们将学习如何系统地提升机器学习算法,告诉你学习算法何时做得不好,并描述如何'调试'你的学习算法和提高其性能的"最佳实践".要优化机器学习算法,需要先了解可以在哪里做最大的改进. 我们将讨论如何理解具有多个部分的机器学习系统的性能,以及如何处理偏斜数据. Evaluating a Hypothesis 设想当你训练的模型对预测数据有很大偏差的时候,接下来你会选择怎么做? 这个需要花时间去实现,但是对你的帮助也会很大,使你不盲目的做一些决定来提升算法,而是直观地看出哪些是对提升算法是有效

Coursera机器学习-第十周-Large Scale Machine Learning

Gradient Descent with Large Datasets Learning With Large Datasets 我们已经知道,得到一个高效的机器学习系统的最好的方式之一是,用一个低偏差(low bias)的学习算法,然后用很多数据来训练它. 下面是一个区分混淆词组的例子: 但是,大数据存在一个问题,当样本容量m=1,000时还行,但是当m=100,000,000呢?请看一下梯度下降的更新公式: 计算一个θ值需要对1亿个数据进行求和,计算量显然太大,所以时间消耗肯定也就大了.

斯坦福第十课:应用机器学习的建议(Advice for Applying Machine Learning)

10.1  决定下一步做什么 10.2  评估一个假设 10.3  模型选择和交叉验证集 10.4  诊断偏差和方差 10.5  归一化和偏差/方差 10.6  学习曲线 10.7  决定下一步做什么 10.1  决定下一步做什么 到目前为止,我们已经介绍了许多不同的学习算法,如果你一直跟着这些视频的进度学习,你会发现自己已经不知不觉地成为一个了解许多先进机器学习技术的专家了. 然而,在懂机器学习的人当中依然存在着很大的差距,一部分人确实掌握了怎样高效有力地运用这些学习算法.而另一些人他们可能对

machine learning (4)---feature scaling

feature scaling:缩小或扩大feature的值,使所有的feature处于类似的范围,这样进行gradient descnet时更快趋向最小值.因为不同的feature的范围相差很大时,它的cost function的等值线是椭圆的,但当它们相近时,等值线接近于圆,这样趋向最小值是,圆更快,椭圆更曲折,趋向更慢.(一般在+3到-3的范围内即可) mean normalization:

Feature extraction - sklearn文本特征提取

http://blog.csdn.net/pipisorry/article/details/41957763 文本特征提取 词袋(Bag of Words)表征 文本分析是机器学习算法的主要应用领域.但是,文本分析的原始数据无法直接丢给算法,这些原始数据是一组符号,因为大多数算法期望的输入是固定长度的数值特征向量而不是不同长度的文本文件.为了解决这个问题,scikit-learn提供了一些实用工具可以用最常见的方式从文本内容中抽取数值特征,比如说: 标记(tokenizing)文本以及为每一个

Attention Please!TWO HUNDREDS OF machine learning turorial summary

Machine Learning The First Column The Second Column Machine Learning 1 从机器学习入手 Machine Learning 2 机器学习很有趣! Machine Learning 3 机器学习规则:ML工程的最佳实践 Machine Learning 4 机器学习速成课程:第一部分 Machine Learning 5 第二部分;伯克利机器学习 Machine Learning 6 第三部分;伯克利机器学习 Machine Le