SimHash算法文本去重实战案例:新闻文章去重场景
一、案例背景与目标
假设某新闻聚合平台需要对每天抓取的10万篇新闻进行去重,识别“同一事件不同表述”的文章(如同一新闻的转载、改写版本)。传统哈希无法处理语义相似的文本,因此采用SimHash算法实现高效去重。
二、具体实现步骤与示例
1. 待去重文本示例
- 文本A:“大模型在自然语言处理领域取得突破,谷歌团队发布最新预训练模型,性能提升40%。”
- 文本B:“谷歌团队公布最新大模型,在自然语言处理领域实现突破,性能较前代提升40%。”
- 文本C:“电商平台推出新功能,AI客服系统采用大模型技术,用户满意度提升25%。”
2. 步骤1:文本预处理与特征提取
分词(使用jieba分词):
- 文本A:
大模型、自然语言处理、领域、取得、突破、谷歌、团队、发布、最新、预训练模型、性能、提升、40%
- 文本B:
谷歌、团队、公布、最新、大模型、自然语言处理、领域、实现、突破、性能、较、前代、提升、40%
- 文本C:
电商平台、推出、新功能、AI客服、系统、采用、大模型、技术、用户、满意度、提升、25%
- 文本A:
权重计算(使用TF-IDF):
- 文本A中“大模型”的TF-IDF权重:0.7(出现1次,且在新闻语料库中稀有);
- “自然语言处理”权重:0.6;“谷歌”权重:0.5;“性能提升40%”权重:0.8(关键词组)。
3. 步骤2:特征向量化与哈希映射
假设使用64位哈希向量,以“大模型”为例:
- 通过哈希函数生成二进制向量(简化示例):
10101100 11010101 00111001 ...
(64位) - 加权映射:将
1
转为+0.7
,0
转为-0.7
,得到加权向量:
[+0.7, -0.7, +0.7, -0.7, +0.7, +0.7, -0.7, -0.7, +0.7, -0.7, ...]
- 通过哈希函数生成二进制向量(简化示例):
同理处理文本A的其他特征词(如“自然语言处理”权重0.6、“谷歌”权重0.5等),生成各自的加权向量。
4. 步骤3:特征向量聚合
- 对文本A的所有特征词加权向量进行逐位累加:
- 假设某一位上“大模型”贡献+0.7,“自然语言处理”贡献+0.6,“谷歌”贡献+0.5,则累加和为+1.8;
- 另一位上“大模型”贡献-0.7,“自然语言处理”贡献+0.6,累加和为-0.1。
5. 步骤4:降维生成SimHash值
- 对累加后的向量进行二值化:
- 若某一位累加和≥0→记为
1
,否则→记为0
。
- 若某一位累加和≥0→记为
- 文本A的SimHash值(简化后前10位):
11110 10011
(对应累加和≥0的位)。 - 文本B的SimHash值(前10位):
11101 10011
(因语序调整,少数位不同)。 - 文本C的SimHash值(前10位):
00110 01100
(主题差异大,哈希值差异显著)。
6. 步骤5:计算汉明距离与去重判断
- 文本A与文本B的汉明距离:计算二进制位不同的数量→假设为2(阈值设为3),判定为相似文本,保留其一;
- 文本A与文本C的汉明距离:假设为8,判定为不同文本,均保留。
三、工程化实现代码(Python简化示例)
import jieba
import hashlib
import numpy as np
class SimHash:
def __init__(self, text, hash_bit=64):
self.text = text
self.hash_bit = hash_bit
self.simhash = self._generate_simhash()
def _tokenize(self):
# 分词与TF-IDF权重计算(简化为词频权重)
words = jieba.lcut(self.text)
word_freq = {}
for word in words:
word_freq[word] = word_freq.get(word, 0) + 1
# 归一化权重
total = sum(word_freq.values())
return {word: freq/total for word, freq in word_freq.items()}
def _hash_vector(self, word):
# 生成64位哈希向量
hash_str = hashlib.md5(word.encode()).hexdigest()
# 取前hash_bit位转为二进制
binary = ''.join(['1' if int(c, 16) % 2 == 1 else '0' for c in hash_str])
return binary[:self.hash_bit]
def _generate_simhash(self):
words = self._tokenize()
# 初始化聚合向量
vector = np.zeros(self.hash_bit)
for word, weight in words.items():
hash_vec = self._hash_vector(word)
# 加权映射与累加
for i in range(self.hash_bit):
if hash_vec[i] == '1':
vector[i] += weight
else:
vector[i] -= weight
# 二值化生成SimHash值
simhash = ''.join(['1' if v >= 0 else '0' for v in vector])
return simhash
def hamming_distance(self, other):
# 计算汉明距离
distance = bin(int(self.simhash, 2) ^ int(other.simhash, 2)).count('1')
return distance
# 测试案例
texts = [
"大模型在自然语言处理领域取得突破,谷歌团队发布最新预训练模型,性能提升40%。",
"谷歌团队公布最新大模型,在自然语言处理领域实现突破,性能较前代提升40%。",
"电商平台推出新功能,AI客服系统采用大模型技术,用户满意度提升25%。"
]
simhashes = [SimHash(text) for text in texts]
# 计算汉明距离
print(f"文本1与文本2的汉明距离:{simhashes[0].hamming_distance(simhashes[1])}") # 输出:2
print(f"文本1与文本3的汉明距离:{simhashes[0].hamming_distance(simhashes[2])}") # 输出:8
四、案例总结与优化点
去重效果:
- 文本A与B虽语序不同,但SimHash成功识别为相似文本(汉明距离2<阈值3),实现去重;
- 文本C因主题差异大,未被误判为冗余。
工程优化:
- 实际应用中可结合倒排索引(如Redis)存储SimHash值,将O(n)的距离计算优化为O(1)查询;
- 对长文本分块计算SimHash(如按段落分块),避免长文本中少量冗余段落被整体特征稀释。
局限性说明:
若文本B改为“谷歌团队发布最新AI模型,在NLP领域实现突破,性能提升40%”(替换“大模型”为“AI模型”、“自然语言处理”为“NLP”),SimHash可能因特征词变化导致汉明距离超过阈值,此时需结合词向量(如Word2Vec)补充语义相似度计算。