🐈
LLM データセット向け 日本語文章の繰り返しを判定するメモ
背景
LLM 向けの高品質なデータセットを web などから構築したい.
繰り返しのある文章を取り除きたい!
ぺろっとやってくれるライブラリは無いっぽ?
を参考に作ります!
しくみ
大まかには
- 入力文をトークナイズ(日本語だと分かち書きでとりま事足りるでしょうか)
- n-gram 作る
- ハッシュ計算
- 判定
のようです.
詳細は
Scaling Language Models: Methods, Analysis & Insights from Training Gopher
を読んでね.
実装
とりま Fugashi で分かち書きして処理するようにしてみました.
# based on https://github.com/shjwudp/c4-dataset-script
import argparse
import nltk
import hashlib
import os
import json
#from pyspark.sql import SparkSession
from fugashi import Tagger
import fugashi
tagger = Tagger('-Owakati')
def parse_args():
parser = argparse.ArgumentParser("Filter out bad docs.")
#parser.add_argument("--output_bad_docs", default="bad_docs.jsonl.zst",
# help="output file for bad lines")
args = parser.parse_args()
return args
def hash_text(text):
return hashlib.md5(text.encode("utf-8")).hexdigest()
def is_repetition_removal(
text, duplicate_line_fraction=0.3, duplicate_line_character_faction=0.2
):
"""Check if there is repeated content in the input text. Excessive
repetition is often linked with uninformative content and can be used to
determine whether it is low-quality text. This function implements
"Repetition Removal" as described in Gopher_.
.. _Gopher: https://arxiv.org/abs/2112.11446
Args:
text (str): input text.
duplicate_line_fraction (float, optional): Duplicate row deduplication
threshold. Defaults to 0.3.
duplicate_line_character_faction (float, optional): Threshold for the
proportion of repeated line characters. Defaults to 0.2.
Returns:
bool: If there is repeated content in the input text.
"""
line_count = 0
dup_line = 0
dup_line_chars = 0
visit_lines = {}
for line in text.split("\n"):
line_hash = hash_text(line)
if line_hash in visit_lines:
dup_line += 1
dup_line_chars += len(line)
visit_lines[line_hash] = True
line_count += 1
if float(dup_line) / line_count > duplicate_line_fraction:
return True
if float(dup_line_chars) / len(text) > duplicate_line_character_faction:
return True
top_ngram_character_fractions = [
(2, 0.2),
(3, 0.18),
(4, 0.16),
]
for ngram, threshold in top_ngram_character_fractions:
#word_list = list(jieba.cut(text))
# wakachi-gaki
word_list = tagger.parse(text).split()
bgs = nltk.ngrams(word_list, ngram)
fdist = nltk.FreqDist(bgs)
for word_list, repeat in fdist.items():
char_count = sum([len(word) for word in word_list])
if char_count * (repeat - 1) / len(text) > threshold:
return True
duplicate_ngram_character_fractions = [
(5, 0.15),
(6, 0.14),
(7, 0.13),
(8, 0.12),
(9, 0.11),
(10, 0.10),
]
for ngram, threshold in duplicate_ngram_character_fractions:
fdist = {}
word_list = tagger.parse(text).split()
mark = [0] * len(word_list)
for i in range(len(word_list) - ngram + 1):
bag = tuple(word_list[i: i + ngram])
if bag in fdist:
for j in range(i, i + ngram):
mark[j] = len(word_list[j])
fdist[bag] += 1
else:
fdist[bag] = 1
if sum(mark) / float(len(text)) > threshold:
return True
return False
def main():
parser = argparse.ArgumentParser()
parser.add_argument("--input", required=True)
#parser.add_argument("--output", default="./rr_output")
args = parser.parse_args()
# assume plain-text
with open(args.input, 'r') as f:
lines = f.readlines()
for line in lines:
print(line)
print('repeat', is_repetition_removal(line))
if __name__ == "__main__":
main()
ダミーテキストです.ダミーテキストです.ダミーテキストです.ダミーテキストです.ダミーテキストです.ダミーテキストです.ダミーテキストです.ダミーテキストです.ダミーテキストです.ダミーテキストです.ダミーテキストです.ダミーテキストです.ダミーテキストです.ダミーテキストです.ダミーテキストです.ダミーテキストです.ダミーテキストです.ダミーテキストです.\nダミーテキストです.ダミーテキストです.ダミーテキストです.ダミーテキストです.ダミーテキストです.ダミーテキストです.ダミーテキストです.ダミーテキストです.ダミーテキストです.ダミーテキストです.ダミーテキストです.ダミーテキストです.ダミーテキストです.ダミーテキストです.ダミーテキストです.ダミーテキストです.ダミーテキストです.ダミーテキストです.\nダミーテキストです.ダミーテキストです.ダミーテキストです.ダミーテキストです.ダミーテキストです.ダミーテキストです.ダミーテキストです.ダミーテキストです.ダミーテキストです.ダミーテキストです.ダミーテキストです.ダミーテキストです.ダミーテキストです.ダミーテキストです.ダミーテキストです.ダミーテキストです.ダミーテキストです.ダミーテキストです.\nダミーテキストです.ダミーテキストです.ダミーテキストです.ダミーテキストです.ダミーテキストです.ダミーテキストです.ダミーテキストです.ダミーテキストです.ダミーテキストです.ダミーテキストです.ダミーテキストです.ダミーテキストです.ダミーテキストです.ダミーテキストです.ダミーテキストです.ダミーテキストです.ダミーテキストです.ダミーテキストです.\nダミーテキストです.ダミーテキストです. ダミーテキストです.ダミーテキストです.ダミーテキストです.ダミーテキストです.ダミーテキストです.ダミーテキストです.ダミーテキストです.ダミーテキストです.ダミーテキストです.ダミーテキストです.ダミーテキストです.ダミーテキストです.ダミーテキストです.ダミーテキストです.ダミーテキストです.ダミーテキストです.
=> repeat True
こんにちは, こんにちの天気はこんにちです.
=> repeat False
こんにちは, こんにちの天気は晴れです.
=> repeat False
無駄無駄無駄無駄ァ
=> repeat True
Voila!
↑の ダミーテキストです
は実際に OSCAR2301 のデータセットにあったものです.
TODO
- 文が重複している場合は, ひとつだけ文を残すようにしてみる.
- Rinna トークナイザなど使ってみる.
- 日本語向けにちょうどよい threshold を求めてみる.
- blog フッターなど, web データセットの中には日付の羅列などもあったりします.
2020 年 1 月 2020 年 2 月 ...
数字はゼロにするなどして判定できるようにするとさらによいでしょうか.
Discussion