点击上方“潜心的Python小屋”关注我们,第一时间推送优质文章。
前言
大家好,我是潜心。最近看到2018年易观竞赛题---对用户性别年龄进行预测,Top1队伍的特征工程使用了TF-IDF,深受启发,想要对广告id做一个TF-IDF提取特征,发现sklearn包中直接有实现的工具,因此写一篇文章来记录下如何将文本序列(物品序列)转化为TF-IDF稀疏矩阵。
本文约2.5k字,预计阅读15分钟。
文本特征提取
文本序列以字符串构成,无法直接传递给相应算法,因此需要将其转化为固定长度的数字特征。sklearn 提供了从文本内容中提取数字特征的最常见方法:
令牌化(tokenizing) 对每个可能的词令牌分成字符串并赋予整数形的id,例如通过使用空格和标点符号作为令牌分隔符。
统计(counting) 每个词令牌在文档中的出现次数。
标准化(normalizing) 在大多数的文档 / 样本中,可以减少重要的词令牌的出现次数的权重。【TF-IDF】
每个单独的词令牌发生频率(标准化或不标准化)被视为一个特征,每个词令牌频率向量被看做为一个多元样本, 这种思想(令牌化,统计和标准化)被称为词袋( Bag of n-grams) 模型。文档由单词出现来描述,同时完全忽略文档中单词的相对位置信息。虽然相比Word2vec,该模型有很多的缺陷(忽略相对位置信息,词表量大的过于稀疏),但它的优点是能够表现每个词在语句中的重要程度。
关于文本特征提取的内容,我们先拿「Python之禅」作为例子:
corpus = [
'The Zen of Python, by Tim Peters',
'Beautiful is better than ugly.',
'Explicit is better than implicit.',
'Simple is better than complex.',
'Complex is better than complicated.',
'Flat is better than nested.',
'Sparse is better than dense.',
'Readability counts.',
'Special cases aren\'t special enough to break the rules.',
'Although practicality beats purity.',
'Errors should never pass silently.',
'Unless explicitly silenced.',
'In the face of ambiguity, refuse the temptation to guess.',
'There should be one-- and preferably only one --obvious way to do it.',
'Although that way may not be obvious at first unless you\'re Dutch.\'Now is better than never.',
'Although never is often better than *right* now.',
'If the implementation is hard to explain, it\'s a bad idea.',
'If the implementation is easy to explain, it may be a good idea.',
'Namespaces are one honking great idea -- let\'s do more of those!']
词频统计---CountVectorizer
sklearn中的CountVectorizer
类实现了词语切分和词频统计的功能。
引入包:
from sklearn.feature_extraction.text import CountVectorizer
类参数(参数默认值都是合理的,一般不需要改动):
CountVectorizer(analyzer=...'word', binary=False, decode_error=...'strict',
dtype=<... 'numpy.int64'>, encoding=...'utf-8', input=...'content',
lowercase=True, max_df=1.0, max_features=None, min_df=1,
ngram_range=(1, 1), preprocessor=None, stop_words=None,
strip_accents=None, token_pattern=...'(?u)\\b\\w\\w+\\b',
tokenizer=None, vocabulary=None)
类中的属性包括:
vocabulary_:dict类型,是词语(特征)到索引的映射;
fixed_vocabulary_:boolean类型,如果用户提供了从词汇表到索引的固定映射,则为真;
stop_words_:set类型,设置停用词(去掉一些无意义的单词);
类的最主要方法是fit_transform()
,来实现词语切分和词频统计。它接受一个可迭代的字符串\文本对象,返回一个文档-词频矩阵(这里我们将每句话看作一个文档)。
>>>vectorizer = CountVectorizer()
>>>matrix = vectorizer.fit_transform(corpus)
>>>matrix
<19x84 sparse matrix of type '<class 'numpy.int64'>'
with 139 stored elements in Compressed Sparse Row format>
>>>type(matrix)
<class 'scipy.sparse.csr.csr_matrix'>
可以发现,我们得到的是一个19*84的词频稀疏矩阵,返回的类型是一个scipy.sparse.csr.csr_matrix
,因为大多数文本文档通常只使用文本词向量全集中的一个小子集,所以得到的矩阵将具有许多特征值为零。为了能够将这样的矩阵存储在存储器中,sklearn使用 scipy.sparse
包中的稀疏实现。
查看具体的词频矩阵,使用将稀疏转化为密集矩阵方法todense()
,(注意:如果矩阵过大,千万不要尝试,比赛中我试图将一个190w*283w稀疏矩阵给密集化,结果电脑直接死机,因为没有那么大的内存来存储。)
>>> matrix.todense()
matrix([[0, 0, 0, ..., 0, 0, 1],
[0, 0, 0, ..., 0, 0, 0],
[0, 0, 0, ..., 0, 0, 0],
...,
[0, 0, 0, ..., 0, 0, 0],
[0, 0, 0, ..., 0, 0, 0],
[0, 0, 0, ..., 0, 0, 0]], dtype=int64)
查看特征名get_feature_names()
,返回一个索义对应的特征名列表:
>>> vectorizer.get_feature_names()
['although', 'ambiguity', 'and', 'are', 'aren', 'at', 'bad', 'be', 'beats', 'beautiful', 'better', 'break', 'by', 'cases', 'complex', 'complicated', 'counts', 'dense', 'do', 'dutch', 'easy', 'enough', 'errors', 'explain', 'explicit', 'explicitly', 'face', 'first', 'flat', 'good', 'great', 'guess', 'hard', 'honking', 'idea', 'if', 'implementation', 'implicit', 'in', 'is', 'it', 'let', 'may', 'more', 'namespaces', 'nested', 'never', 'not', 'now', 'obvious', 'of', 'often', 'one', 'only', 'pass', 'peters', 'practicality', 'preferably', 'purity', 'python', 're', 'readability', 'refuse', 'right', 'rules', 'should', 'silenced', 'silently', 'simple', 'sparse', 'special', 'temptation', 'than', 'that', 'the', 'there', 'those', 'tim', 'to', 'ugly', 'unless', 'way', 'you', 'zen']
可以发现特征是经过一定的字母顺序进行排列,并且去掉了标点符号,以及单词旁边的特殊字符,如*right*
,但也有一些问题,如re
处理为了一个单独字符(处理方法是在最开始讲起替换为are,或者在类实例化时设置停用词)。
我们也可以通过类的属性vocabulary_
来获得特征对应的索引:
>>> vectorizer.vocabulary_.get('re')
60
词权重---TfidfTransformer
在一个大的文档语料库中,一些单词将出现很多次(例如 “the”, “a”, “is” ),但对文档的实际内容没有什么有意义的信息。我们主要是找到一些对文档重要的词汇,给它分配更高的权重,来体现整篇文档的特征。如何找到重要的词汇呢?TF-IDF是最常用的方法。
TF-IDF(term frequency–inverse document frequency)用以评估一字词对于一个文档集或一个语料库中的其中一份文档的重要程度。字词的重要性随着它在文件中出现的次数成正比增加,但同时会随着它在语料库中出现的频率成反比下降。
词频(TF)指的是某一个给定的词语在该文件中出现的频率,即对词数的归一化,以防止它偏向长的文件,对于词语 ,它的重要性可表示为: