1. 项目概述:Python文本相似度计算系统
文本相似度计算是自然语言处理(NLP)中的基础任务,在搜索引擎、推荐系统、抄袭检测等场景中广泛应用。这个Python项目提供了一个完整的解决方案,包含可运行的源码和详细文档,适合需要快速实现文本比对功能的开发者。
我曾在多个内容审核项目中实际应用过类似系统,相比商业API,自建方案在数据隐私保护、定制化需求和成本控制方面有明显优势。本项目采用经典的TF-IDF算法作为核心,配合余弦相似度计算,在保证精度的同时兼顾了实现效率。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 核心算法解析
2.1 TF-IDF向量化
TF-IDF(词频-逆文档频率)是文本处理中的经典算法,通过统计词语在文档中的出现频率和在整个语料库中的分布情况,将文本转换为数值向量。具体实现分为三个步骤:
-
词频(TF)计算:
python复制
tf = (词语在文档中出现的次数) / (文档总词数) -
逆文档频率(IDF)计算:
python复制idf = log((语料库文档总数) / (包含该词语的文档数 + 1)) -
TF-IDF权重计算:
python复制
tfidf = tf * idf
在项目中,我们使用scikit-learn的TfidfVectorizer实现:
python复制from sklearn.feature_extraction.text import TfidfVectorizer
vectorizer = TfidfVectorizer()
tfidf_matrix = vectorizer.fit_transform(text_corpus)
注意:中文文本需要先分词,可以使用jieba库。英文文本则需要注意词干提取(stemming)和停用词过滤。
2.2 余弦相似度计算
得到TF-IDF向量后,我们通过计算向量夹角的余弦值来衡量相似度。余弦值越接近1,表示文本越相似:
python复制from sklearn.metrics.pairwise import cosine_similarity
similarity_matrix = cosine_similarity(tfidf_matrix[0:1], tfidf_matrix)
这个计算过程实际上是在比较两个向量的方向是否一致,而不考虑它们的绝对长度,这正好符合文本相似度的需求——我们关心的是内容的相似性,而不是文本的长短。
3. 系统架构设计
3.1 模块划分
项目采用模块化设计,主要包含以下组件:
code复制text_similarity/
├── core/ # 核心算法实现
│ ├── preprocess.py # 文本预处理
│ ├── vectorizer.py # 向量化模块
│ └── similarity.py # 相似度计算
├── utils/ # 工具函数
│ ├── logger.py # 日志记录
│ └── file_io.py # 文件读写
├── tests/ # 单元测试
└── app.py # 主程序入口
3.2 关键类设计
TextSimilarityCalculator 是核心类,提供完整的处理流水线:
python复制class TextSimilarityCalculator:
def __init__(self, language='zh'):
self.language = language
self.vectorizer = None
def preprocess(self, text):
# 实现文本清洗、分词等预处理
pass
def fit(self, corpus):
# 训练向量化模型
pass
def calculate(self, text1, text2):
# 计算两文本相似度
pass
def batch_calculate(self, query_text, corpus):
# 批量计算查询文本与语料库的相似度
pass
4. 完整实现步骤
4.1 环境准备
首先安装依赖库:
bash复制pip install scikit-learn jieba pandas numpy
对于英文文本处理,还需要安装NLTK:
bash复制pip install nltk
python -m nltk.downloader punkt stopwords
4.2 文本预处理实现
中文预处理示例:
python复制import jieba
import re
def chinese_preprocess(text):
# 去除特殊字符
text = re.sub(r'[^\w\s]', '', text)
# 分词
words = jieba.cut(text)
# 过滤停用词
stopwords = set(['的', '了', '在', '是', '我'])
words = [w for w in words if w not in stopwords]
return ' '.join(words)
英文预处理示例:
python复制from nltk.corpus import stopwords
from nltk.stem import PorterStemmer
import string
def english_preprocess(text):
# 转为小写
text = text.lower()
# 分词
words = text.split()
# 去除标点
words = [w.translate(str.maketrans('', '', string.punctuation)) for w in words]
# 词干提取
stemmer = PorterStemmer()
words = [stemmer.stem(w) for w in words if w not in stopwords.words('english')]
return ' '.join(words)
4.3 核心计算流程
完整的相似度计算流程封装如下:
python复制from sklearn.feature_extraction.text import TfidfVectorizer
from sklearn.metrics.pairwise import cosine_similarity
class TextSimilarity:
def __init__(self, language='zh'):
self.language = language
self.vectorizer = TfidfVectorizer()
self.corpus = []
def add_document(self, text):
"""添加文档到语料库"""
processed = self._preprocess(text)
self.corpus.append(processed)
def fit(self):
"""训练向量化模型"""
self.vectorizer.fit(self.corpus)
def query(self, text, top_n=3):
"""查询最相似的文档"""
processed = self._preprocess(text)
vec = self.vectorizer.transform([processed])
corpus_vec = self.vectorizer.transform(self.corpus)
sims = cosine_similarity(vec, corpus_vec)
sorted_indexes = sims.argsort()[0][-top_n:][::-1]
return [(self.corpus[i], sims[0,i]) for i in sorted_indexes]
def _preprocess(self, text):
# 根据语言调用不同的预处理函数
if self.language == 'zh':
return chinese_preprocess(text)
else:
return english_preprocess(text)
5. 性能优化技巧
5.1 稀疏矩阵优化
当处理大规模文本时,TF-IDF矩阵通常是高度稀疏的。我们可以通过以下方式优化内存使用:
python复制# 使用稀疏矩阵格式
from scipy.sparse import csr_matrix
# 设置TF-IDF参数减少特征维度
vectorizer = TfidfVectorizer(
max_features=5000, # 限制最大特征数
min_df=5, # 忽略低频词
max_df=0.8, # 忽略高频词
ngram_range=(1,2) # 考虑1-2个词的组合
)
5.2 并行计算
对于大规模语料库,可以启用并行处理:
python复制vectorizer = TfidfVectorizer(analyzer='word', n_jobs=-1)
5.3 增量学习
当语料库持续增长时,可以使用HashingVectorizer替代TF-IDF,它不需要存储词汇表:
python复制from sklearn.feature_extraction.text import HashingVectorizer
vectorizer = HashingVectorizer(n_features=2**18, alternate_sign=False)
6. 实际应用案例
6.1 文档查重系统
构建一个简单的文档查重系统:
python复制similarity = TextSimilarity()
# 添加已知文档
documents = ["文档1内容...", "文档2内容...", ...]
for doc in documents:
similarity.add_document(doc)
similarity.fit()
# 查询新文档相似度
new_doc = "待检测文档内容..."
results = similarity.query(new_doc)
for doc, score in results:
print(f"相似度: {score:.2f}\n内容: {doc[:100]}...")
6.2 问答系统匹配
在FAQ问答系统中匹配最相似的问题:
python复制faq = {
"如何重置密码?": "请访问设置页面...",
"账户被锁定怎么办?": "请联系客服..."
}
similarity = TextSimilarity()
for question in faq.keys():
similarity.add_document(question)
similarity.fit()
user_question = "我忘记密码了怎么办?"
best_match, score = similarity.query(user_question, top_n=1)[0]
if score > 0.6: # 设置相似度阈值
print(f"匹配问题: {best_match}")
print(f"答案: {faq[best_match]}")
else:
print("未找到相关问题")
7. 常见问题与解决方案
7.1 相似度分数过低
可能原因及解决方法:
- 文本差异过大:检查预处理是否过度过滤了关键词语
- 特征维度不足:调整TfidfVectorizer的max_features参数
- 缺少领域词汇:考虑使用自定义词典(jieba.load_userdict())
7.2 处理速度慢
优化建议:
- 减少max_features值
- 使用HashingVectorizer替代TfidfVectorizer
- 对文本进行长度限制(如只取前1000个字符)
7.3 中文分词不准确
改进方法:
python复制# 添加自定义词典
jieba.load_userdict('custom_dict.txt')
# 调整分词模式
jieba.cut(text, cut_all=False) # 精确模式
8. 进阶改进方向
8.1 引入词向量模型
对于更精确的语义相似度计算,可以结合词向量:
python复制from gensim.models import Word2Vec
# 训练或加载词向量模型
model = Word2Vec(sentences, vector_size=100, window=5, min_count=1)
# 计算文档向量(词向量的平均)
def doc2vec(text):
words = text.split()
vecs = [model.wv[w] for w in words if w in model.wv]
return np.mean(vecs, axis=0) if vecs else np.zeros(100)
8.2 使用预训练模型
利用BERT等预训练模型获取更高质量的文本表示:
python复制from transformers import BertTokenizer, BertModel
tokenizer = BertTokenizer.from_pretrained('bert-base-chinese')
model = BertModel.from_pretrained('bert-base-chinese')
inputs = tokenizer(text, return_tensors="pt", padding=True, truncation=True)
outputs = model(**inputs)
# 使用[CLS]位置的输出作为文本表示
text_vector = outputs.last_hidden_state[:,0,:].detach().numpy()
8.3 构建REST API
将系统封装为Web服务:
python复制from flask import Flask, request, jsonify
app = Flask(__name__)
similarity = TextSimilarity()
# 初始化加载语料库...
@app.route('/similarity', methods=['POST'])
def calculate_similarity():
text1 = request.json.get('text1')
text2 = request.json.get('text2')
score = similarity.calculate(text1, text2)
return jsonify({'similarity': score})
if __name__ == '__main__':
app.run(host='0.0.0.0', port=5000)
在实际项目中,我发现TF-IDF虽然简单,但在很多场景下已经足够有效。特别是当计算资源有限或需要快速实现原型时,这种传统方法仍然很有价值。对于更复杂的语义理解需求,可以逐步引入深度学习模型作为补充。
