MinHashLSH
- 텍스트 중복제거는 모든 데이터를 비교하는 과정이 요구된다.
- 데이터 크기가 시스템 메모리를 초과하면 이 작업이 매우 어려워짐.
- MinHashLSH로 메모리 요구량을 줄이고, 유사도 탐색 속도를 개선할 수 있다.
데이터셋
코드
- datasketch==1.5.9
- 텍스트의 길이에 따라 num_perm을 조절
- threshold로 중복의 정도를 선택
- 복잡한 토크나이저를 쓸수록 의미기반의 중복제거가 가능 (가장 단순한 띄어쓰기 기반 토크나이징은 near-deduplication에 적합함)
import sys
import random
from datasketch import MinHash, MinHashLSH
from Korpora import Korpora
def get_hash_dict(dataset:list, num_perm=11) -> dict:
mh_dict = dict()
for text_id, text in enumerate(dataset):
mh_dict[text_id] = MinHash(num_perm=num_perm)
for t in set(text.split(" ")):
mh_dict[text_id].update(t.encode("utf8"))
return mh_dict
def get_size(obj, seen=None):
"""Recursively finds size of objects."""
size = sys.getsizeof(obj)
if seen is None:
seen = set()
obj_id = id(obj)
if obj_id in seen:
return 0
seen.add(obj_id)
if isinstance(obj, dict):
size += sum([get_size(v, seen) for v in obj.values()])
size += sum([get_size(k, seen) for k in obj.keys()])
elif hasattr(obj, "__dict__"):
size += get_size(obj.__dict__, seen)
elif hasattr(obj, "__iter__") and not isinstance(obj, (str, bytes, bytearray)):
size += sum([get_size(i, seen) for i in obj])
return size
def lsh_insert(hash_dict: dict, lsh: MinHashLSH) -> MinHashLSH:
for text_id in hash_dict.keys():
lsh.insert(text_id, hash_dict[text_id])
print(f"Memory Usage:{get_size(lsh) / 1024 / 1024 / 1024:.5f} GB\n")
return lsh
def deduplication(hash_dict: dict, lsh: MinHashLSH) -> list:
removed = set()
results=[]
for j, text_id in enumerate(hash_dict.keys()):
if text_id in removed:
continue
duplicated = lsh.query(hash_dict[text_id])
if len(duplicated) > 1:
temp = []
for dup_id in duplicated:
if dup_id == text_id:
continue
removed.add(dup_id)
temp.append(dup_id)
results.append(
{j: temp}
)
return results
corpus = Korpora.load("kcbert")
dataset = corpus.train[:100000]
num_perm=11
threshold=0.85
hash_dict = get_hash_dict(dataset, num_perm=num_perm)
lsh = MinHashLSH(num_perm=num_perm ,threshold=threshold)
lsh = lsh_insert(hash_dict, lsh)
results = deduplication(hash_dict, lsh)
cnt = 0
for r in results:
cnt += len(list(r.values())[0])
print(f"Removed : {cnt}")
r = random.choice(results)
i = list(r.keys())[0]
j = random.choice(list(r.values())[0])
print(f"Duplicated:{i}, {j}\n")
print(f"{i}:{dataset[i]}")
print(f"{j}:{dataset[j]}")
결과
Memory Usage:0.05044 GB
Removed : 106
Duplicated:55064, 97272
55064:트럼프가 한국 대통령 같다
97272:문재인보다 트럼프가 한국 대통령 같다