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

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

词频统计可以用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

最后,再想一下,长文本和短文本包含的信息是不对等的,一般来说,长文本包含的关键词要比短文本多,因此,我们需要对文本进行归一化处理,将每个单词出现的次数除以该文本中所有单词的个数,这被称之为词频(term frequency)(注:之前说的词频是指绝对频率,这里的词频是指相对频率)。其次,我们在对文档进行分类时,假如某个词在各文本中都有出现,那么这个词就无法给分类带来多少有用的信息。因此,对于出现频率高的词和频率低的词,我们应该区分对待,它们的重要性是不一样的。解决的办法就是用逆文档频率(inverse document frequency)来给词进行加权。IDF会根据单词在文本中出现的频率进行加权,出现频率高的词,加权系数就低,反之,出现频率低的词,加权系数就高。这两者相结合被称之为TF-IDF(term frequncy, inverse document frequency)。可以用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]]

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

词袋模型的缺点: 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参数)。

文本特征提取---词袋模型,TF-IDF模型,N-gram模型(Text Feature Extraction Bag of Words TF-IDF N-gram )的更多相关文章

  1. 机器学习---文本特征提取之词袋模型(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 frie ...

  2. sklearn文本特征提取

    http://cloga.info/2014/01/19/sklearn_text_feature_extraction/ 文本特征提取 词袋(Bag of Words)表征 文本分析是机器学习算法的 ...

  3. Feature extraction - sklearn文本特征提取

    http://blog.csdn.net/pipisorry/article/details/41957763 文本特征提取 词袋(Bag of Words)表征 文本分析是机器学习算法的主要应用领域 ...

  4. scikit-learn:4.2. Feature extraction(特征提取,不是特征选择)

    http://scikit-learn.org/stable/modules/feature_extraction.html 带病在网吧里. ..... 写.求支持. .. 1.首先澄清两个概念:特征 ...

  5. 【sklearn文本特征提取】词袋模型/稀疏表示/停用词/TF-IDF模型

    1. 词袋模型 (Bag of Words, BOW) 文本分析是机器学习算法的一个主要应用领域.然而,原始数据的这些符号序列不能直接提供给算法进行训练,因为大多数算法期望的是固定大小的数字特征向量, ...

  6. 文本离散表示(一):词袋模型(bag of words)

    一.文本表示 文本表示的意思是把字词处理成向量或矩阵,以便计算机能进行处理.文本表示是自然语言处理的开始环节. 文本表示按照细粒度划分,一般可分为字级别.词语级别和句子级别的文本表示.字级别(char ...

  7. 机器学习入门-文本数据-构造Tf-idf词袋模型(词频和逆文档频率) 1.TfidfVectorizer(构造tf-idf词袋模型)

    TF-idf模型:TF表示的是词频:即这个词在一篇文档中出现的频率 idf表示的是逆文档频率, 即log(文档的个数/1+出现该词的文档个数)  可以看出出现该词的文档个数越小,表示这个词越稀有,在这 ...

  8. 词袋模型bow和词向量模型word2vec

    在自然语言处理和文本分析的问题中,词袋(Bag of Words, BOW)和词向量(Word Embedding)是两种最常用的模型.更准确地说,词向量只能表征单个词,如果要表示文本,需要做一些额外 ...

  9. 文本向量化及词袋模型 - NLP学习(3-1)

    分词(Tokenization) - NLP学习(1) N-grams模型.停顿词(stopwords)和标准化处理 - NLP学习(2)   之前我们都了解了如何对文本进行处理:(1)如用NLTK文 ...

随机推荐

  1. Python之Flask框架项目Demo入门

    Python+Flask框架项目Demo入门 本例子用到了 Flask+蓝图+Flask-Login+SQLAlchemy+WTForms+PyMySQL相关架构 Flask Web框架介绍 Flas ...

  2. jboss_log4j.xml配置

    log4j是个优秀的开源的java日志系统,jboss内部也集成他,在jboss下默认的只是对server做了每日日志,并没有对你部署的项目进行每日的日志构建,但我们能通过修改jboss-log4j. ...

  3. 记录下github 与 gitee 同时使用

    参考 Gitee(码云).Github同时配置ssh key 中间有一步,创建config文件,然后测试就过不去了. 报错:Bad owner or permissions on C:\\Users\ ...

  4. Jenkins安装第一个插件和通过离线安装包进行安装

    1.打开左侧Manage Jenkins 选择Manage Plugins菜单 2.搜索Folders插件,该插件用于创建一个目录 3.点击安装进入插件安装状态,Jenkins会自动下载相关联的插件 ...

  5. UVA 291 The House Of Santa Claus DFS

    题目: In your childhood you most likely had to solve the riddle of the house of Santa Claus. Do you re ...

  6. 初探云原生应用管理(一): Helm 与 App Hub

      ​ 系列介绍:初探云原生应用管理系列是介绍如何用云原生技术来构建.测试.部署.和管理应用的内容专辑.做这个系列的初衷是为了推广云原生应用管理的最佳实践,以及传播开源标准和知识.通过这个系列,希望帮 ...

  7. 『Exclusive Access 2 dilworth定理 状压dp』

    Exclusive Access 2 Description 给出 N 个点M 条边的无向图,定向得到有向无环图,使得最长路最短. N ≤ 15, M ≤ 100 Input Format 第一行一个 ...

  8. MySQL慢日志查询分析方法与工具

    MySQL中的日志包括:错误日志.二进制日志.通用查询日志.慢查询日志等等.这里主要介绍下比较常用的两个功能:通用查询日志和慢查询日志. 1)通用查询日志:记录建立的客户端连接和执行的语句. 2)慢查 ...

  9. 【Python】运算符

    python是强类型语言,某些场合下需要进行类型转换.关系运算符的结果是true或false.这里介绍一下基本的运算符,和几个使用实例,了解并掌握python中range()函数和math类库的引入和 ...

  10. 基于log4net的日志组件扩展封装,实现自动记录交互日志 XYH.Log4Net.Extend(微服务监控)

    背景: 随着公司的项目不断的完善,功能越来越复杂,服务也越来越多(微服务),公司迫切需要对整个系统的每一个程序的运行情况进行监控,并且能够实现对自动记录不同服务间的程序调用的交互日志,以及通一个服务或 ...