【问题标题】:How to apply a custom stemmer before passing the training corpus to TfidfVectorizer in sklearn?如何在将训练语料库传递给 sklearn 中的 TfidfVectorizer 之前应用自定义词干分析器?
【发布时间】:2018-08-02 04:55:18
【问题描述】:

这是我的代码,我有一个句子,我想在将其传递给 TfidfVectorizer 以最终获得句子的 tf-idf 表示之前对其进行标记和阻止:

from sklearn.feature_extraction.text import TfidfVectorizer
import nltk 
from nltk.stem.snowball import SnowballStemmer

stemmer_ita = SnowballStemmer("italian")

def tokenizer_stemmer_ita(text):
    return [stemmer_ita.stem(word) for word in text.split()]

def sentence_tokenizer_stemmer(text):
    return " ".join([stemmer_ita.stem(word) for word in text.split()])

X_train = ['il libro è sul tavolo']

X_train = [sentence_tokenizer_stemmer(text) for text in X_train]

tfidf = TfidfVectorizer(preprocessor=None, tokenizer=None, use_idf=True, stop_words=None, ngram_range=(1,2))
X_train = tfidf.fit_transform(X_train)

# let's see the features
print (tfidf.get_feature_names())

我得到输出:

['il', 'il libr', 'libr', 'libr sul', 'sul', 'sul tavol', 'tavol']

如果我改变参数

tokenizer=None

到:

tokenizer=tokenizer_stemmer_ita

我评论了这一行:

X_train = [sentence_tokenizer_stemmer(text) for text in X_train]

我希望得到相同的结果,但结果不同:

['il', 'il libr', 'libr', 'libr è', 'sul', 'sul tavol', 'tavol', 'è', 'è sul']

为什么?我是否正确实施了外部词干分析器?至少,停用词(“è”)似乎在第一次运行时被删除,即使 stop_words=None。

[编辑] 正如 Vivek 所建议的那样,问题似乎是默认的令牌模式,当 tokenizer = None 时无论如何都会应用它。因此,如果在 tokenizer_stemmer_ita 的开头添加这两行:

token_pattern = re.compile(u'(?u)\\b\\w\\w+\\b')
text = " ".join( token_pattern.findall(text) )

我应该得到正确的行为,实际上我在上面的简单示例中得到了它,但是对于另一个示例:

X_train = ['0.05%.\n\nVedete?']

我没有,两个输出不同:

['05', '05 ved', 'ved']

['05', '05 vedete', 'vedete']

为什么?在这种情况下,问号似乎是问题所在,没有它,输出是相同的。

[编辑2] 看来我必须先停止然后应用正则表达式,在这种情况下,两个输出是相同的。

【问题讨论】:

  • 您发布的当前代码是错误的。请仔细检查您的实际代码。
  • 你是对的,代码现在是正确的,你可以重现这个问题。

标签: python scikit-learn stemming document-classification tfidfvectorizer


【解决方案1】:

这是因为 TfidfVectorizer 中使用了默认的标记器模式 token_pattern

token_pattern : 字符串

表示什么构成“令牌”的正则表达式,仅在 Analyzer == 'word' 时使用。默认的正则表达式选择 2 个或更多的标记 字母数字字符(标点符号被完全忽略并且总是 视为标记分隔符)。

所以没有选择字符è

import re
token_pattern = re.compile(u'(?u)\\b\\w\\w+\\b')
print token_pattern.findall('il libro è sul tavolo')

# Output
# ['il', 'libro', 'sul', 'tavolo']

当 tokenizer 为 None 时使用此默认 token_pattern,正如您所遇到的那样。

【讨论】:

  • 感谢 Vivek,这解释了故事的大部分内容,但在其他情况下仍然无法正常工作(可能是由于标点符号的处理方式),请参阅我对问题的编辑。
  • 看来我必须先进行词干,然后再应用正则表达式,这对我来说似乎不合逻辑:我会说先找到令牌,然后应用词干分析器,对吗?
  • 忘记我的最后一条评论,正则表达式是在词干分析器之后应用的,因为我在分词器为无时传递了已经词干的文本,所以一切都有意义。再次感谢。模拟定义 token_pattern 的简单 text.split() 的最简单方法是什么?
猜你喜欢
  • 2014-07-15
  • 2017-07-31
  • 2019-07-15
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-08-08
  • 1970-01-01
  • 2021-07-08
相关资源
最近更新 更多