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

2019-10-30 20:16:28 浏览数 (1)

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。

本文链接:https://blog.csdn.net/blmoistawinde/article/details/80816179

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

代码语言:javascript复制
#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, 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进行分词:

代码语言:javascript复制
    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的格式已经可以直接拿来训练了。让我们跑一下模型试试。

代码语言:javascript复制
    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的单词在英文中一般是无足轻重的,但在中文里,就可能有一些很重要的单字词,所以修改如下:

代码语言:javascript复制
    tfidf_model2 = TfidfVectorizer(token_pattern=r"(?u)bw 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)bww b",其中的两个w决定了其匹配长度至少为2的单词,所以这边减到1个。对这个参数进行更多修改,可以满足其他要求,比如这里依然没有得到标点符号,在此不详解了。

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

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

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

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

代码语言:javascript复制
    # 过滤出现在超过60%的句子中的词语
    tfidf_model3 = TfidfVectorizer(token_pattern=r"(?u)bw 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类型

直接过滤指定的停用词。

代码语言:javascript复制
    tfidf_model4 = TfidfVectorizer(token_pattern=r"(?u)bw 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_相同,也是指定对应关系。

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

代码语言:javascript复制
    tfidf_model5 = TfidfVectorizer(token_pattern=r"(?u)bw 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.        ]

0 人点赞