使用sklearn tfidf优化特征提取

问题描述 投票:0回答:1

我正在开发一个模仿 https://www.mtgassist.com/ 的 Python 项目。对于那些不太熟悉的人来说:万智牌是一款集换式卡牌游戏,其中的收藏卡牌可能非常昂贵。该项目应该采用一张卡的名称,并根据几个功能列出具有类似机制(并且希望更便宜)的其他卡,包括描述该卡功能的“oracle_text”。

  1. 我使用 sklearn 的 TfidfVectorizer 和以下参数:
porter = PorterStemmer()
def tokenizer_stemmer(text: str) -> str:
    stop = stopwords.words('english')
    return [porter.stem(word) for word in text.split() if word not in stop]

tfidf = TfidfVectorizer(
    ngram_range=(1,2)
    , tokenizer=tokenizer_stemmer
    , stop_words=stopwords.words('english')
)
  1. 然后我将 TfidfVectorizer.fit_transform 与之前加载的大约 20k 行的 pandas DataFrame 结合使用。这个过程大约需要25秒:

token_mat = tfidf.fit_transform(df_not_na['oracle_text'])

  1. 接下来,我将
    token_mat
    转换为形状为 ~(20_000, 90_000) 的 numpy 数组 (
    token_arr
    ),并计算所选卡片与数组中所有卡片之间的欧氏距离(这需要额外的 25 秒) 。最后,我打印出前 5 张“最接近”卡片的名称:
token_arr = token_mat.toarray()

distances = []
for _card in tqdm(token_mat):
    distances.append(np.linalg.norm(_card - chosen_card_array))

nearest_5 = np.argpartition(distances, 10)[:10]
print(df_not_na.iloc[nearest_5][['name', 'oracle_text']])

我的目标是优化这个过程并减少创建特征向量和计算距离的时间。

我尝试仅使用二元组而不是 ngram_range=(1,2),但效果很小。

我也想过使用 numba,但读到 sklearn/numpy 具有类似的嵌入式功能,并没有多大好处。

还有其他建议也请告诉我! 谢谢

scikit-learn feature-extraction
1个回答
0
投票

我发现效率低下的两个原因,

  1. TF-IDF 表示会产生非常高维的表示。即使您使用 ngram 长度 = 1,它也会产生等于您的词汇量大小的维度。
  2. 您正在计算 20k 卡中每张卡的距离,这是一种简单但效率低下的 O(n) 时间方法。

改善这一点的方法,

  1. 考虑使用词嵌入。例如,采用预训练的 Word2Vec 模型,找到句子中所有单词的平均嵌入作为该句子的向量表示。或者您可以使用 BERT 直接获取句子嵌入,但它可能无法满足您的需求。如果您想坚持使用 TF-IDF,您可以使用 PCA 或 T-SNE 或 UMAP 进行降维以获得更短的表示。
  2. 要查找最近的 k 个邻居,特别是如果您必须对许多不同的查询卡重复执行此操作,请考虑使用像 hnswlib 这样的向量数据库,您可以提前索引卡向量,并且准备好为您提供前 10 个卡片向量使用 HNSW 算法快速获取最接近的向量。
© www.soinside.com 2019 - 2024. All rights reserved.