//en.wikipedia.org/wiki/Feature_hashing>`_技术,或可称为“散列法”(hashingtrick)的技术。
代替在构建训练中遇到的特征的哈希表,如向量化所做的那样FeatureHasher将哈希函数应用于特征,以便直接在样本矩阵中确定它们的列索引。
结果是以牺牲可检测性为代价,提高速度和减少内存的使用;哈希表不记得输入特性是什么样的,没有inverse_transform办法。
由于散列函数可能导致(不相关)特征之间的冲突,因此使用带符号散列函数,并且散列值的符号确定存储在特征的输出矩阵中的值的符号。
这样,碰撞可能会抵消而不是累积错误,并且任何输出要素的值的预期平均值为零。
默认情况下,此机制将使用alternate_sign=True启用,对于小型哈希表大小(n_features<10000)特别有用。
对于大的哈希表大小,可以禁用它,以便将输出传递给估计器,如sklearn.naive_bayes.MultinomialNB或sklearn.feature_selection.chi2特征选择器,这些特征选项器可以使用非负输入。
类FeatureHasher接受映射(如Python的dict及其在collections模块中的变体),使用键值对(feature,value)或字符串,具体取决于构造函数参数input_type。
映射被视为(feature,value)对的列表,而单个字符串的隐含值为1,因此['feat1','feat2','feat3']被解释为[('feat1',1),('feat2',1),('feat3',1)]。
如果单个特征在样本中多次出现,相关值将被求和(所以('feat',2)和('feat',3.5)变为('feat',5.5))。
FeatureHasher的输出始终是CSR格式的scipy.sparse矩阵。
特征散列可以在文档分类中使用,但与text.CountVectorizer不同,FeatureHasher不执行除Unicode或UTF-8编码之外的任何其他预处理;请参阅下面的哈希技巧向量化大文本语料库,用于组合的tokenizer/hasher。
例如,有一个词级别的自然语言处理任务,需要从(token,part_of_speech)键值对中提取特征。
可以使用Python生成器函数来提取功能:
deftoken_features(token,part_of_speech):
iftoken.isdigit():
yield"numeric"else:
yield"token={}".format(token.lower())yield"token,pos={},{}".format(token,part_of_speech)iftoken[0].isupper():
yield"uppercase_initial"iftoken.isupper():
yield"all_uppercase"yield"pos={}".format(part_of_speech)
然后,raw_X为了可以传入FeatureHasher.transform可以通过如下方式构造:
raw_X=(token_features(tok,pos_tagger(tok))fortokincorpus)
并传入一个hasher:
hasher=FeatureHasher(input_type='string')X=hasher.transform(raw_X)
得到一个scipy.sparse类型的矩阵X。
注意使用发生器的理解,它将懒惰引入到特征提取中:
词令牌(token)只能根据需要从哈希值进行处理。
4.2.2.1.实现细节
类FeatureHasher使用签名的32-bit变体的MurmurHash3。
因此导致(并且由于限制scipy.sparse),当前支持的功能的最大数量.
特征哈希的原始形式源于Weinbergeretal,使用两个单独的哈希函数,和分别确定特征的列索引和符号。
现有的实现是基于假设:
MurmurHash3的符号位与其他位独立。
由于使用简单的模数将哈希函数转换为列索引,建议使用2次幂作为n_features参数;否则特征不会均匀的分布到列中。
参考文献:
KilianWeinberger,AnirbanDasgupta,JohnLangford,AlexSmola和JoshAttenberg(2009)。
用于大规模多任务学习的特征散列。
PROC。
ICML。
MurmurHash3。
参考文献:
●KilianWeinberger,AnirbanDasgupta,JohnLangford,AlexSmolaandJoshAttenberg(2009).用于大规模多任务学习的特征散列.Proc.ICML.
●MurmurHash3.
4.2.3.文本特征提取
4.2.3.1.话语表示
文本分析是机器学习算法的主要应用领域。
然而,原始数据,符号文字序列不能直接传递给算法,因为它们大多数要求具有固定长度的数字矩阵特征向量,而不是具有可变长度的原始文本文档。
为解决这个问题,scikit-learn提供了从文本内容中提取数字特征的最常见方法,即:
●令牌化(tokenizing)对每个可能的词令牌分成字符串并赋予整数形的id,例如通过使用空格和标点符号作为令牌分隔符。
●统计(counting)每个词令牌在文档中的出现次数。
●标准化(normalizing)在大多数的文档/样本中,可以减少重要的次令牌的出现次数的权重。
。
在该方案中,特征和样本定义如下:
●每个**单独的令牌发生频率**(归一化或不归零)被视为一个**特征**。
●给定**文档**中所有的令牌频率向量被看做一个多元sample**样本**。
因此,文本的集合可被表示为矩阵形式,每行对应一条文本,每列对应每个文本中出现的词令牌(如单个词)。
我们称**向量化**是将文本文档集合转换为数字集合特征向量的普通方法。
这种特殊思想(令牌化,计数和归一化)被称为BagofWords或“Bagofn-grams”模型。
文档由单词出现来描述,同时完全忽略文档中单词的相对位置信息。
4.2.3.2.稀疏
由于大多数文本文档通常只使用文本词向量全集中的一个小子集,所以得到的矩阵将具有许多特征值为零(通常大于99%)。
例如,10,000个短文本文档(如电子邮件)的集合将使用总共100,000个独特词的大小的词汇,而每个文档将单独使用100到1000个独特的单词。
为了能够将这样的矩阵存储在存储器中,并且还可以加速代数的矩阵/向量运算,实现通常将使用诸如scipy.sparse包中的稀疏实现。
4.2.3.3.常见Vectorizer使用方法
类CountVectorizer在单个类中实现了tokenization(词语切分)和occurrencecounting(出现频数统计):
>>>
>>>fromsklearn.feature_extraction.textimportCountVectorizer
这个模型有很多参数,但参数的默认初始值是相当合理的(请参阅参考文档了解详细信息):
>>>
>>>vectorizer=CountVectorizer()>>>vectorizerCountVectorizer(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)
我们用它来对简约的文本语料库进行tokenize(分词)和统计单词出现频数:
>>>
>>>corpus=[...'Thisisthefirstdocument.',...'Thisisthesecondseconddocument.',...'Andthethirdone.',...'Isthisthefirstdocument?
',...]>>>X=vectorizer.fit_transform(corpus)>>>X<4x9sparsematrixoftype'<...'numpy.int64'>'with19storedelementsinCompressedSparse...format>
默认配置通过提取至少2个字母的单词来对string进行分词。
做这一步的函数可以显式地被调用:
>>>
>>>analyze=vectorizer.build_analyzer()>>>analyze("Thisisatextdocumenttoanalyze.")==(...['this','is','text','document','to','analyze'])True
analyzer在拟合过程中找到的每个term(项)都会被分配一个唯一的整数索引,对应于resultingmatrix(结果矩阵)中的一列。
此列的一些说明可以被检索如下:
>>>
>>>vectorizer.get_feature_names()==(...['and','document','first','is','one',...'second','the','third','this'])True>>>X.toarray()array([[0,1,1,1,0,0,1,0,1],[0,1,0,1,0,2,1,0,1],[1,0,0,0,1,0,1,1,0],[0,1,1,1,0,0,1,0,1]]...)
从feature名称到columnindex(列索引)的逆映射存储在vocabulary_属性中:
>>>
>>>vectorizer.vocabulary_.get('document')1
因此,在未来对transform方法的调用中,在trainingcorpus(训练语料库)中没有看到的单词将被完全忽略:
>>>
>>>vectorizer.transform(['Somethingcompletelynew.']).toarray()...array([[0,0,0,0,0,0,0,0,0]]...)
请注意,在前面的corpus(语料库)中,第一个和最后一个文档具有完全相同的词,因为被编码成相同的向量。
特别是我们丢失了最后一个文件是一个疑问的形式的信息。
为了防止词组顺序颠倒,除了提取一元模型1-grams(个别词)之外,我们还可以提取2-grams的单词:
>>>
>>>bigram_vectorizer=CountVectorizer(ngram_range=(1,2),...token_pattern=r'\b\w+\b',min_df=1)>>>analyze=bigram_vectorizer.build_analyzer()>>>analyze('Bi-gramsarecool!
')==(...['bi','grams','are','cool','bigrams','gramsare','arecool'])True
由vectorizer(向量化器)提取的vocabulary(词汇)因此会变得更大,同时可以在定位模式时消除歧义:
>>>
>>>X_2=bigram_vectorizer.fit_transform(corpus).toarray()>>>X_2...array([[0,0,1,1,1,1,1,0,0,0,0,0,1,1,0,0,0,0,1,1,0],[0,0,1,0,0,1,1,0,0,2,1,1,1,0,1,0,0,0,1,1,0],[1,1,0,0,0,0,0,0,1,0,0,0,1,0,0,1,1,1,0,0,0],[0,0,1,1,1,1,0,1,0,0,0,0,1,1,0,0,0,0,1,0,1]]...)
特别是“Isthis”的疑问形式只出现在最后一个文档中:
>>>
>>>feature_index=bigram_vectorizer.vocabulary_.get('isthis')>>>X_2[:
feature_index]array([0,0,0,1]...)
4.2.3.4.Tf–idf项加权
在一个大的文本语料库中,一些单词将出现很多次(例如“the”,“a”,“is”是英文),因此对文档的实际内容没有什么有意义的信息。
如果我们将直接计数数据直接提供给分类器,那么这些频繁词组会掩盖住那些我们关注但很少出现的词。
为了为了重新计算特征权重,并将其转化为适合分类器使用的浮点值,因此使用tf-idf变换是非常常见的。
Tf表示**术语频率**,而tf-idf表示术语频率乘以**转制文档频率**:
.
使用TfidfTransformer的默认设置,TfidfTransformer(norm='l2',use_idf=True,smooth_idf=True,sublinear_tf=False)术语频率,一个术语在给定文档中出现的次数乘以idf组件,计算为
其中是文档的总数,是包含术语的文档数。
然后,所得到的tf-idf向量通过欧几里得范数归一化:
.
它源于一个词权重的信息检索方式(作为搜索引擎结果的评级函数),同时也在文档分类和聚类中表现良好。
以下部分包含进一步说明和示例,说明如何精确计算tf-idfs以及如何在scikit-learn中计算tf-idfs,TfidfTransformer并TfidfVectorizer与定义idf的标准教科书符号略有不同
在TfidfTransformer和TfidfVectorizer中smooth_idf=False,将“1”计数添加到idf而不是idf的分母:
该归一化由类TfidfTransformer实现:
>>>
>>>fromsklearn.feature_extraction.textimportTfidfTransformer>>>transformer=TfidfTransformer(smooth_idf=False)>>>transformerTfidfTransformer(norm=...'l2',smooth_idf=False,sublinear_tf=False,use_idf=True)
有关所有参数的详细信息,请参阅参考文档。
让我们以下方的词频为例。
第一个次在任何时间都是100%出现,因此不是很有重要。
另外两个特征只占不到50%的比例,因此可能更具有代表性:
>>>
>>>counts=[[3,0,1],...[2,0,0],...[3,0,0],...[4,0,0],...[3,2,0],...[3,0,2]]...>>>tfidf=transformer.fit_transform(counts)>>>tfidf<6x3sparsematrixoftype'<...'numpy.float64'>'with9storedelementsinCompressedSparse...format>>>>tfidf.toarray()array([[0.81940995,0.,0.57320793],[1.,0.,0.],[1.,0.,0.],[1.,0.,0.],[0.47330339,0.88089948,0.],[0.58149261,0.,0.81355169]])
每行都被正则化,使其适应欧几里得标准:
例如,我们可以计算`计数`数组中第一个文档中第一个项的tf-idf,如下所示:
现在,如果我们对文档中剩下的2个术语重复这个计算,我们得到:
和原始tf-idfs的向量:
然后,应用欧几里德(L2)规范,我们获得文档1的以下tf-idfs:
此外,默认参数smooth_idf=True将“1”添加到分子和分母,就好像一个额外的文档被看到一样包含集合中的每个术语,这样可以避免零分割:
使用此修改,文档1中第三项的tf-idf更改为1.8473:
而L2标准化的tf-idf变为
:
>>>
>>>transformer=TfidfTransformer()>>>transformer.fit_transform(counts).toarray()array([[0.85151335,0.,0.52433293],[1.,0.,0.],[1.,0.,0.],[1.,0.,0.],[0.55422893,0.83236428,0.],[0.63035731,0.,0.77630514]])
通过拟合方法调用计算的每个特征的权重存储在模型属性中:
>>>
>>>transformer.idf_array([1....,2.25...,1.84...])
由于tf-idf经常用于文本特征,所以还有一个类TfidfVectorizer,它将CountVectorizer和TfidfTransformer的所有选项组合在一个单例模型中:
>>>
>>>fromsklearn.feature_extraction