一、什么是MinHash
MinHash是一种用于近似相等度量的技术,它被广泛应用于文本比较、网页去重、音乐相似度分析等领域。 在使用MinHash之前,先来了解一下Jaccard相似度。 Jaccard相似度是用来衡量两个集合相似度的指标,计算公式为:
J(A,B) = |A∩B| / |A∪B|
其中,A和B为两个集合,|A∩B|为两个集合的交集的元素个数,|A∪B|为两个集合的并集的元素个数。
二、MinHash的原理
MinHash是基于Locality Sensitive Hashing(局部敏感哈希LSH)的一种技术,它可以近似计算出两个集合的Jaccard相似度,且时间和空间复杂度都较小。 MinHash需要将集合中元素的哈希值分成多个小组,每组取一个最小的哈希值,将这些哈希值组成一个签名(签名的长度一般是一个固定值k),两个集合的相似度就可以通过它们的签名的相似度来近似计算。 通俗来讲,就是将两个集合的哈希值进行比较,如果两个哈希值相同,那么就把这个位置上的1替换成0,并记录这个位置,那么最后的结果就是两个集合在所有位置上相同的数量,除以签名的长度k即为它们的相似度近似值。
三、如何实现MinHash
Step1:生成哈希函数
MinHash需要用到多个哈希函数,每个哈希函数都可以将一个元素映射到0到1之间的一个随机值,可以采用MurMurHash、SHA-1等哈希函数。在这里,我们使用Python的hashlib库来生成哈希函数。
import hashlib
class MinHash(object):
def __init__(self, k):
self.k = k
self.hashes = self._create_hashes()
def _create_hashes(self):
hashes = []
for i in range(self.k):
hashes.append(hashlib.sha1(str(i).encode('utf-8')))
return hashes
Step2:生成签名
生成签名需要先将集合里的元素用哈希函数生成一个哈希值,然后选取一个最小的哈希值作为这个元素的签名,将所有元素的签名组成一个签名矩阵,每行代表一个元素的签名,每列代表一个哈希函数的结果。
def _sig(self, element):
# 将元素用哈希函数映射为k个随机值,选取一个最小的值作为这个元素的签名
sig = []
for h in self.hashes:
h.update(str(element).encode('utf-8'))
sig.append(h.hexdigest())
return min(sig)
def generate_signature_matrix(self, elements):
# 生成元素的签名矩阵
signature_matrix = []
for element in elements:
signature = [int(bit, 16) for bit in self._sig(element)]
signature_matrix.append(signature)
return signature_matrix
Step3:计算相似度
计算相似度需要先比较两个集合每个元素的签名,然后计算它们相同的元素数量,最后除以签名的长度即可得到相似度的近似值。
def jaccard_similarity(self, signature1, signature2):
# 计算签名的Jaccard相似度
count_same = sum([1 for i in range(len(signature1)) if signature1[i] == signature2[i]])
count_all = len(signature1)
return float(count_same) / float(count_all)
def minhash_similarity(self, set1, set2):
# 计算集合的MinHash相似度
signature_matrix1 = self.generate_signature_matrix(set1)
signature_matrix2 = self.generate_signature_matrix(set2)
similarities = []
for i in range(len(signature_matrix1)):
for j in range(len(signature_matrix2)):
similarity = self.jaccard_similarity(signature_matrix1[i], signature_matrix2[j])
similarities.append(similarity)
return sum(similarities) / len(similarities)
四、应用案例
MinHash最初是用于文本去重的,现在被广泛应用于音乐相似度分析、推荐系统、网页去重等领域。
Case1:文本去重
文本去重是指在一定的数据范围内,将相似的文本取出来,一般是用于搜索引擎等领域。 假设有10000篇文章需要去重,要求相同文章不超过100篇,不同的文章要被归为不同的组别。那么可以使用MinHash对文章进行去重。首先,将每篇文章转换成set格式,然后利用MinHash计算集合的相似度,找出相似度大于等于0.8的文章,它们就归为同一组别。
import time
class Article(object):
def __init__(self, id, title, content):
self.id = id
self.title = title
self.content = content
self.words = set(self.title.split() + self.content.split())
def read_data():
# 读取数据,返回文章列表
articles = []
with open('articles.txt', 'r') as f:
for line in f:
id, title, content = line.strip().split('\t')
article = Article(id, title, content)
articles.append(article)
return articles
def deduplication(articles):
# 对文章进行去重
minhash = MinHash(10)
doc_signatures = []
for article in articles:
doc_signature = minhash.generate_signature_matrix(article.words)
doc_signatures.append((article.id, doc_signature))
groups = {}
for i in range(len(doc_signatures)):
id1, sig1 = doc_signatures[i]
for j in range(i+1, len(doc_signatures)):
id2, sig2 = doc_signatures[j]
similarity = minhash.minhash_similarity(sig1, sig2)
if similarity >= 0.8:
group_i = groups.get(id1, set([id1]))
group_i.add(id2)
groups[id1] = group_i
group_j = groups.get(id2, set([id2]))
group_j.add(id1)
groups[id2] = group_j
deduplicated_articles = []
for group in groups.values():
if len(group) <= 100:
articles_in_group = [article for article in articles if article.id in group]
deduplicated_articles.extend(articles_in_group)
else:
# 如果一个组别超过100篇文章,将它们分成多个子组别
sub_groups = []
sub_group_i = set([list(group)[0]])
group.remove(list(group)[0])
for id in group:
is_in_sub_group = False
for sub_group in sub_groups:
if minhash.minhash_similarity(doc_signatures[sub_group[0]][1],
doc_signatures[doc_signatures.index((id, doc_signatures[id][1]))][1]) >= 0.8:
sub_group.add(id)
is_in_sub_group = True
break
if not is_in_sub_group:
sub_groups.append(set([id]))
for sub_group in sub_groups:
articles_in_group = [article for article in articles if article.id in sub_group]
deduplicated_articles.extend(articles_in_group)
return deduplicated_articles
if __name__ == '__main__':
start_time = time.time()
articles = read_data()
deduplicated_articles = deduplication(articles)
print('文章数量:{}'.format(len(deduplicated_articles)))
print('用时:{}s'.format(time.time() - start_time))
Case2:音乐相似度分析
音乐相似度分析是指在海量音乐数据中,根据用户的听歌记录或者上传音乐的音频特征提取,找出相似的音乐,用于音乐推荐、音乐搜索等领域。 首先,使用spectral clustering或者kmeans等聚类算法将相似的音乐聚到一起,然后针对每个聚类,使用MinHash计算相似度,将相似度大于等于0.8的音乐归为同一组别。
五、总结
MinHash是一种高效的近似相等度量技术,它可以广泛用于文本去重、音乐相似度分析、推荐系统等领域。在使用MinHash时,需要先生成哈希函数、计算元素的签名、比较签名、计算相似度。通过应用案例,可以看到MinHash在实际场景中有着广泛的应用。