You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

基于Sklearn TF-IDF的特征提取与相似度计算优化咨询

优化建议

一、TF-IDF特征生成阶段优化

1. 修复分词器的冗余计算

你的自定义分词器每次调用都会重复加载停用词,浪费资源。提前初始化停用词和词干提取器,同时优化文本清理逻辑(处理标点、统一小写):

from nltk.stem import PorterStemmer
from nltk.corpus import stopwords
import re

# 提前初始化,避免重复加载
stop_words = set(stopwords.words('english'))
porter = PorterStemmer()
punct_pattern = re.compile(r'[^\w\s]')  # 清理标点的正则

def tokenizer_stemmer(text: str) -> list:
    clean_text = punct_pattern.sub('', text.lower())
    return [porter.stem(word) for word in clean_text.split() if word not in stop_words]

另外,你在TfidfVectorizer中同时设置了tokenizer和stop_words,会导致重复过滤停用词,直接去掉stop_words参数即可。

2. 削减特征维度

9万维度的特征矩阵过于庞大,是后续计算缓慢的核心原因之一。通过max_features限制特征数量,保留信息量最高的词汇/ngram:

tfidf = TfidfVectorizer(
    ngram_range=(1,2),
    tokenizer=tokenizer_stemmer,
    max_features=30000,  # 可根据效果调整,建议2-4万区间
    norm='l2'  # 开启L2归一化,为后续快速计算相似度铺路
)

3. 提前预处理文本

把文本的清理、分词、词干提取提前完成并存储,避免TF-IDF每次都重复执行这些操作:

# 提前预处理所有oracle_text
df_not_na['processed_text'] = df_not_na['oracle_text'].apply(tokenizer_stemmer)
# 此时TF-IDF直接使用analyzer='word'即可
tfidf = TfidfVectorizer(
    ngram_range=(1,2),
    analyzer='word',
    max_features=30000,
    norm='l2'
)
token_mat = tfidf.fit_transform(df_not_na['processed_text'].apply(' '.join))

二、距离/相似度计算阶段优化

1. 彻底放弃稀疏矩阵转稠密数组

2万×9万的稠密数组会占用14GB以上内存,转数组和后续计算都会极慢。直接用稀疏矩阵运算:

# 假设chosen_card_sparse是目标卡牌的稀疏TF-IDF向量(不要转成数组)
# L2归一化后,余弦相似度等价于向量点积,计算速度远快于欧氏距离
similarities = token_mat.dot(chosen_card_sparse.T).toarray().flatten()

# 取Top10最相似的索引(相似度越高越接近,与欧氏距离逻辑相反)
nearest_10 = np.argpartition(-similarities, 10)[:10]
# 按相似度排序
sorted_indices = nearest_10[np.argsort(-similarities[nearest_10])]
print(df_not_na.iloc[sorted_indices][['name', 'oracle_text']])

2. 使用Sklearn内置近邻工具

sklearn.neighbors.NearestNeighbors对稀疏矩阵做了优化,比手动循环高效得多:

from sklearn.neighbors import NearestNeighbors

# 初始化近邻模型,支持稀疏矩阵
nn = NearestNeighbors(n_neighbors=10, metric='cosine', algorithm='brute')
nn.fit(token_mat)

# 查询目标卡牌的近邻
distances, indices = nn.kneighbors(chosen_card_sparse)
print(df_not_na.iloc[indices[0]][['name', 'oracle_text']])

3. 尝试近似近邻库(可选)

如果后续数据量持续增长,用近似近邻库(如Annoy、FAISS)可以进一步提速,牺牲微小精度换取速度:

# Annoy示例
from annoy import AnnoyIndex

# 此时维度已被max_features限制,转稠密数组内存可控
token_arr = token_mat.toarray()
dim = token_arr.shape[1]

# 构建索引
annoy_idx = AnnoyIndex(dim, 'angular')  # angular对应余弦相似度
for i, vec in enumerate(token_arr):
    annoy_idx.add_item(i, vec)
annoy_idx.build(10)  # 树的数量,越多精度越高

# 查询Top10
nearest_10 = annoy_idx.get_nns_by_vector(chosen_card_array, 10)
print(df_not_na.iloc[nearest_10][['name', 'oracle_text']])

三、其他细节优化

  • 用nltk的word_tokenize替代split():更准确处理英文分词(比如连字符、缩写),避免把draw,这类带标点的内容当成一个词。
  • 并行计算:TF-IDF支持n_jobs=-1参数(利用多核),也可以用joblib加速文本预处理。
  • 缓存模型:训练好的TF-IDF模型用joblib.dump保存,后续直接加载,避免重复执行fit_transform。

内容的提问来源于stack exchange,提问作者FelipeB

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.15 10:13:15