传统机器学习——特征工程之文本数据(二)
前言
该篇接前面《传统机器学习——特征工程之文本数据(一)》,继续总结文本数据的处理方法。
声明:关于编程语法相关问题不会展开论述,本文只针对方法路线。
意义的单位:从单位、n元词到短语
主要目的是将字符串转换成一个单词序列。
解析与分词
什么时候需要解析:当字符串不只包含纯文本时。
举例:
- 网页:需要处理URL
- 电子邮件:发件人、收件人和标题都需要处理
- 日志:不感兴趣的部分
经过简单解析后,就可以对文档的纯文本部分进行分词了。
一般步骤:将字符串转成一个字符序列,再转成一个标记序列。
分词程序需要知道
哪些字符表示一个标记已经结束而且另一个标记已经开始了。空格通常是一个非常好的分
隔符,标点符号也是一样。如果文本中包含推文内容,那么井号(#)就不应该被用作分
隔符(又称定界符)了。
有时候,分析需要在句子上而不是整个文档上进行。例如,n 元词(n-gram)是单词概念
的一个推广,它就不能超过句子的边界。像word2vec 这样比较复杂的文本特征化方法也
是工作在句子或段落上的。在这种情况下,我们需要先将文档解析为句子,然后再对每个
句子进行分词,得到单词。
通过搭配提取进行短语检测
从语义上说,我们更习惯于理解短语,而不是n元词。
在计算机自然语言处理(NLP)中,有用短语的概念被称为搭配(collocation)。
搭配能表达的意义比组成它的各个单词的总和还要多。
例如,“strong tea”的意义绝对不止“great physical strength”和“tea”,因此可以认为它是个搭配。
另一方面,短语“cute puppy”的意义则就是两个单词“cute”和“puppy”之和,因此我们认为它不是个搭配。
搭配不一定是个连贯的序列。例如,可以认为句子“Emma knocked on the door”包含搭配“knock door”,因此,不是所有的搭配都是n 元词。反之,也不是所有n 元词都一定是有意义的搭配。
1、 基于频率的方法
一种简单方法是查看那些出现频率最高的n 元词。这种方法的问题是最常出现的词不一定
是最有用的。
**举例:**下图是该数据集下最常见的二元词,但是这些词对描述性没有多大用处。
2、用于搭配提取的假设检验
这块的内容比较复杂,我这块也没搞懂。
我们可以将原假设Hnull(不相关)表述为P(w2 | w1) = P(w2| not w1),将备择假设Halternate(相关)表述为P(w2 | w1) ≠ (w2 | not w1)。
最终统计量为以下两个函数的比值的对数:
似然函数L(Data; H) 表示在使用相关模型或不相关模型时,得到一对单词在数据集中出现频率的概率。为了计算出这个概率,还必须做出另外一个假设,即数据是如何生成的。最简单的数据生成模型是二项式模型,对于数据集中的每个单词,我们都掷一次硬币,如果硬币正面向上,我们就插入一个特定单词,否则就插入某个其他单词。在这种策略下,这个特定单词的出现次数就服从二项式分布。二项式分布完全由单词的总数、感兴趣单词的
出现次数和硬币正面向上的概率来决定。通过似然比检验这种分析方法来检测常见短语的算法如下。
(1) 计算出所有单词的出现概率:P(w)。
(2) 对所有的唯一二元词,计算出成对单词出现的条件概率:P(w2 | w1)。
(3) 对所有的唯一二元词,计算出似然比log λ。
(4) 按照似然比为二元词排序。
(5) 将似然比最小的二元词作为特征。
3、文本分块和词性标注
要生成更长的短语,需要另外的方法,比如文本分块,或结合词性标注来使用。
文本分块要比找出n 元词复杂一些,复杂之处在于,它要使用基于规则的模型并基于词性生成标记序列。
使用spaCy 和TextBlob 通过判断词性来找出名词短语。
>>> import pandas as pd
>>> import json
# 加载前10条点评
>>> f = open('data/yelp/v6/yelp_academic_dataset_review.json')
>>> js = []
>>> for i in range(10):
... js.append(json.loads(f.readline()))
>>> f.close()
>>> review_df = pd.DataFrame(js)
# 首先使用spaCy中的函数
>>> import spacy
# 预先加载语言模型
>>> nlp = spacy.load('en')
# 我们可以创建一个spaCy nlp变量的Pandas序列
>>> doc_df = review_df['text'].apply(nlp)
# spaCy可以使用(.pos_)提供细粒度的词性,
# 使用(.tag_)提供粗粒度的词性
>>> for doc in doc_df[4]:
... print([doc.text, doc.pos_, doc.tag_])
Got VERB VBP
a DET DT
letter NOUN NN
in ADP IN
the DET DT
mail NOUN NN
last ADJ JJ
week NOUN NN
that ADJ WDT
said VERB VBD
Dr. PROPN NNP
Goldberg PROPN NNP
is VERB VBZ
moving VERB VBG
to ADP IN
Arizona PROPN NNP
to PART TO
take VERB VB
a DET DT
new ADJ JJ
position NOUN NN
there ADV RB
in ADP IN
June PROPN NNP
. PUNCT .
SPACE SP
He PRON PRP
will VERB MD
be VERB VB
missed VERB VBN
very ADV RB
much ADV RB
. PUNCT .
SPACE SP
I PRON PRP
think VERB VBP
finding VERB VBG
a DET DT
new ADJ JJ
doctor NOUN NN
in ADP IN
NYC PROPN NNP
that ADP IN
you PRON PRP
actually ADV RB
like INTJ UH
might VERB MD
almost ADV RB
be VERB VB
as ADV RB
awful ADJ JJ
as ADP IN
trying VERB VBG
to PART TO
find VERB VB
a DET DT
date NOUN NN
! PUNCT .
# spaCy还可以进行基本的名词分块
>>> print([chunk for chunk in doc_df[4].noun_chunks])
[a letter, the mail, Dr. Goldberg, Arizona, a new position, June, He, I,
a new doctor, NYC, you, a date]
#####
# 我们还可以使用TextBlob实现同样的特征转换
from textblob import TextBlob
# TextBlob中的默认标记器使用PatternTagger,在这个例子中是没有问题的。
# 你还可以指定使用NLTK标记器,它对于不完整的句子效果更好。
>>> blob_df = review_df['text'].apply(TextBlob)
>>> blob_df[4].tags
[('Got', 'NNP'),
('a', 'DT'),
('letter', 'NN'),
('in', 'IN'),
('the', 'DT'),
('mail', 'NN'),
('last', 'JJ'),
('week', 'NN'),
('that', 'WDT'),
('said', 'VBD'),
('Dr.', 'NNP'),
('Goldberg', 'NNP'),
('is', 'VBZ'),
('moving', 'VBG'),
('to', 'TO'),
('Arizona', 'NNP'),
('to', 'TO'),
('take', 'VB'),
('a', 'DT'),
('new', 'JJ'),
('position', 'NN'),
('there', 'RB'),
('in', 'IN'),
('June', 'NNP'),
('He', 'PRP'),
('will', 'MD'),
('be', 'VB'),
('missed', 'VBN'),
('very', 'RB'),
('much', 'JJ'),
('I', 'PRP'),
('think', 'VBP'),
('finding', 'VBG'),
('a', 'DT'),
('new', 'JJ'),
('doctor', 'NN'),
('in', 'IN'),
('NYC', 'NNP'),
('that', 'IN'),
('you', 'PRP'),
('actually', 'RB'),
('like', 'IN'),
('might', 'MD'),
('almost', 'RB'),
('be', 'VB'),
('as', 'RB'),
('awful', 'JJ'),
('as', 'IN'),
('trying', 'VBG'),
('to', 'TO'),
('find', 'VB'),
('a', 'DT'),
('date', 'NN')]
>>> print([np for np in blob_df[4].noun_phrases])
['got', 'goldberg', 'arizona', 'new position', 'june', 'new doctor', 'nyc']
可见,通过不同程序库找出的名词短语并不完全一样。spaCy 找出的短语中包括英文中的一些普通词,如“a”和“the”,而TextBlob 则去掉了这些词。