텍스트 중복 제거 (MinHash LSH)

-·2024년 9월 8일

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:문재인보다 트럼프가 한국 대통령 같다

0개의 댓글