sklearn: TfidfVectorizer 中文处理及一些使用参数

    TfidfVectorizer可以把原始文本转化为tf-idf的特征矩阵,从而为后续的文本相似度计算,主题模型(如LSI),文本搜索排序等一系列应用奠定基础。基本应用如:

#coding=utf-8
from sklearn.feature_extraction.text import TfidfVectorizer
document = ["I have a pen.",
            "I have an apple."]
tfidf_model = TfidfVectorizer().fit(document)
sparse_result = tfidf_model.transform(document)     # 得到tf-idf矩阵,稀疏矩阵表示法
print(sparse_result)
# (0, 3)	0.814802474667   # 第0个字符串,对应词典序号为3的词的TFIDF为0.8148
# (0, 2)	0.579738671538
# (1, 2)	0.449436416524
# (1, 1)	0.631667201738
# (1, 0)	0.631667201738
print(sparse_result.todense())                     # 转化为更直观的一般矩阵
# [[ 0.          0.          0.57973867  0.81480247]
#  [ 0.6316672   0.6316672   0.44943642  0.        ]]
print(tfidf_model.vocabulary_)                      # 词语与列的对应关系
# {'have': 2, 'pen': 3, 'an': 0, 'apple': 1}

但是要把它运用到中文上还需要一些特别的处理,故写此文分享我的经验。

第一步:分词

    中文不比英文,词语之间有着空格的自然分割,所以我们首先要进行分词处理,再把它转化为与上面的document类似的格式。这里采用著名的中文分词库jieba进行分词:

import jieba
text = """我是一条天狗呀!
我把月来吞了,
我把日来吞了,
我把一切的星球来吞了,
我把全宇宙来吞了。
我便是我了!"""
sentences = text.split()
sent_words = [list(jieba.cut(sent0)) for sent0 in sentences]
document = [" ".join(sent0) for sent0 in sent_words]
print(document)
# ['我 是 一条 天狗 呀 !', '我 把 月 来 吞 了 ,', '我 把 日来 吞 了 ,', '我 把 一切 的 星球 来 吞 了 ,', '我 把 全宇宙 来 吞 了 。', '我 便是 我 了 !']

PS:语料来自郭沫若《天狗》。另外,由于分词工具的不完善,也会有一些错误,比如这边错误地把"日来"分到了一起。

第二步:建模

    理论上,现在得到的document的格式已经可以直接拿来训练了。让我们跑一下模型试试。

tfidf_model = TfidfVectorizer().fit(document)
print(tfidf_model.vocabulary_)
# {'一条': 1, '天狗': 4, '日来': 5, '一切': 0, '星球': 6, '全宇宙': 3, '便是': 2}
sparse_result = tfidf_model.transform(document)
print(sparse_result)
# (0, 4)	0.707106781187
# (0, 1)	0.707106781187
# (2, 5)	1.0
# (3, 6)	0.707106781187
# (3, 0)	0.707106781187
# (4, 3)	1.0
# (5, 2)	1.0

    没有错误,但有一个小问题,就是单字的词语,如“我”、“吞”、“呀”等词语在我们的词汇表中怎么都不见了呢?为了处理一些特殊的问题,让我们深入其中的一些参数。

第三步:参数

    查了一些资料以后,发现单字的问题是token_pattern这个参数搞的鬼。它的默认值只匹配长度≥2的单词,就像其实开头的例子中的'I'也被忽略了一样,一般来说,长度为1的单词在英文中一般是无足轻重的,但在中文里,就可能有一些很重要的单字词,所以修改如下:

tfidf_model2 = TfidfVectorizer(token_pattern=r"(?u)\b\w+\b").fit(document)
print(tfidf_model2.vocabulary_)
# {'我': 8, '是': 12, '一条': 1, '天狗': 7, '呀': 6, '把': 9, '月': 13, '来': 14, '吞': 5, '了': 2, '日来': 10, '一切': 0, '的': 15, '星球': 11, '全宇宙': 4, '便是': 3}

 

    token_pattern这个参数使用正则表达式来分词,其默认参数为r"(?u)\b\w\w+\b",其中的两个\w决定了其匹配长度至少为2的单词,所以这边减到1个。对这个参数进行更多修改,可以满足其他要求,比如这里依然没有得到标点符号,在此不详解了。

 

    当然有些时候我们还是要过滤掉一些无意义的词,下面有些别的参数也可以帮助我们实现这一目的:

    1.max_df/min_df: [0.0, 1.0]内浮点数或正整数, 默认值=1.0

    当设置为浮点数时,过滤出现在超过max_df/低于min_df比例的句子中的词语;正整数时,则是超过max_df句句子。

    这样就可以帮助我们过滤掉出现太多的无意义词语,如下面的"我"就被过滤(虽然这里“我”的排比在文学上是很重要的)。

# 过滤出现在超过60%的句子中的词语
tfidf_model3 = TfidfVectorizer(token_pattern=r"(?u)\b\w+\b", max_df=0.6).fit(document)  
print(tfidf_model3.vocabulary_)
# {'是': 8, '一条': 1, '天狗': 5, '呀': 4, '月': 9, '来': 10, '日来': 6, '一切': 0, '的': 11, '星球': 7, '全宇宙': 3, '便是': 2}

    2.stop_words: list类型

       直接过滤指定的停用词。

tfidf_model4 = TfidfVectorizer(token_pattern=r"(?u)\b\w+\b", max_df=0.6, stop_words=["是", "的"]).fit(document)
print(tfidf_model4.vocabulary_)
# {'一条': 1, '天狗': 5, '呀': 4, '月': 8, '来': 9, '日来': 6, '一切': 0, '星球': 7, '全宇宙': 3, '便是': 2}

    3.vocabulary: dict类型

       只使用特定的词汇,其形式与上面看到的tfidf_model4.vocabulary_相同,也是指定对应关系。

       这一参数的使用有时能帮助我们专注于一些词语,比如我对本诗中表达感情的一些特定词语(甚至标点符号)感兴趣,就可以设定这一参数,只考虑他们:

tfidf_model5 = TfidfVectorizer(token_pattern=r"(?u)\b\w+\b",vocabulary={"我":0, "呀":1,"!":2}).fit(document)
print(tfidf_model5.vocabulary_)
# {'我': 0, '呀': 1, '!': 2}
print(tfidf_model5.transform(document).todense())
# [[ 0.40572238  0.91399636  0.        ]
#  [ 1.          0.          0.        ]
#  [ 1.          0.          0.        ]
#  [ 1.          0.          0.        ]
#  [ 1.          0.          0.        ]

2019.12.21 再更新几个常用的参数    

      4.ngram_range: tuple

       有时候我们觉得单个的词语作为特征还不足够,能够加入一些词组更好,就可以设置这个参数,如下面允许词表使用1个词语,或者2个词语的组合:

       这里顺便使用了一个方便的方法 get_feature_names() ,可以以列表的形式得到所有的词语

tfidf_model5 = TfidfVectorizer(token_pattern=r"(?u)\b\w+\b", ngram_range=(1,2), stop_words=["是", "的"]).fit(document)
print(tfidf_model5.get_feature_names())
"""
['一切', '一切 星球', '一条', '一条 天狗', '了', '便是', '便是 我', '全宇宙', '全宇宙 来', '吞', '吞 了', '呀', '天狗', '天狗 呀', '我', '我 一条', '我 了', '我 便是', '我 把', '把', '把 一切', '把 全宇宙', '把 日来', '把 月', '日来', '日来 吞', '星球', '星球 来', '月', '月 来', '来', '来 吞']
"""

    5.max_feature: int

       在大规模语料上训练TFIDF会得到非常多的词语,如果再使用了上一个设置加入了词组,那么我们词表的大小就会爆炸。出于时间和空间效率的考虑,可以限制最多使用多少个词语,模型会优先选取词频高的词语留下。下面限制最多使用10个词语:

tfidf_model6 = TfidfVectorizer(token_pattern=r"(?u)\b\w+\b", max_features=10, ngram_range=(1,2), stop_words=["是", "的"]).fit(document)
print(tfidf_model6.vocabulary_)
"""
{'我': 3, '把': 5, '来': 8, '吞': 1, '了': 0, '我 把': 4, '来 吞': 9, '吞 了': 2, '日来 吞': 6, '星球': 7}
"""

   比如这里大部分的词组都被过滤了,但是“我 把”因为多次出现而保留了。

 

    

 

已标记关键词 清除标记
©️2020 CSDN 皮肤主题: 创作都市 设计师:CSDN官方博客 返回首页