日本語の事前学習データセット(OSCAR,mc4)を機械学習でクリーニングしてみる練習
はじめに
日本語の事前学習データセットを最近は触っています。
Common Crawlから直接構築することも検討中ですが、まずは既存のデータセットをクリーニングしてみるところから始めてみます。
(ルールベースで真面目に清掃するスクリプトも存在します)
2/21追記 いくらか関連するコードをgithubにuploadしました。
データセットのダウンロードと内訳チェック
huggingfaceのdatasetsクラスから、streamingで落とせます。非常に便利です。
from datasets import load_dataset
# ignore_verifications=Trueをつけないとエラーとなる
oscar_dataset = load_dataset('oscar', 'unshuffled_original_ja',
split='train',
ignore_verifications=True,
streaming=True)
#mc4の場合はこちら
mc4_dataset = load_dataset('mc4', 'ja',split='train', streaming=True)
dataset=oscar_dataset
例えばはじめの1万件を読み込む場合、適当にiterationを回します。
loc_records = []
cnt=0
for record in dataset:
loc_records.append(record)
cnt+=1
if cnt>10**4:
break
はじめの10件の内訳は以下の通り。
販促サイト
販促サイト
ニュース
販促サイト
販促サイト
ゲーム関連の一般人の記事?
何かを語る記事
ポルノ
販促サイト
音楽サイト(?)
10件中、大規模言語モデルに学習させたいと思うテキストは4件だけでした。個人的に、学習データは質が大切だと思っていますので、不要なテキストを除去するスクリプトを書いていきます。
テキストのアノテーション
各テキストのはじめの100文字の情報をもとに、手作業でアノテーション(good/bad)していきます。
以下のサイトを用い、適当に100件ほど、アノテーションしました。
削除基準
明らかに販売目的のサイト
短すぎるサイト
単語の羅列が中心のサイト
公序良俗に反するサイト
機械学習による分類
logistic regressionで分けていきます。
補助関数として、以下のファイルを用います。
rom sklearn.model_selection import train_test_split
from sklearn.metrics import classification_report
from src.LogisticClassifier import prepare_classifier
pipeline = prepare_classifier()
# データセットの分割
X_train, X_test, y_train, y_test = train_test_split(annotated_texts, annotated_labels, test_size=0.2, random_state=42)
# モデルの訓練
pipeline.fit(X_train, y_train)
# モデルの評価
predictions = pipeline.predict(X_test)
print(classification_report(y_test, predictions))
追加のアノテーションデータの生成
good/badの予測値が0.5付近のものを優先的にアノテーションしていきます。
import pandas as pd
import random
from tqdm import tqdm
#新たな問題の生成
n_problems=500
start_num=random.randint(1000, 10**4)
cut_threshold=0.3 #0-0.5 値が大きいほど、自信のない問題を選ぶ
problem_list=[]
cnt=-1
dataset=mc4_dataset
dataset_type="mc4"
pipeline.fit(annotated_texts, annotated_labels)
for record in tqdm(dataset):
cnt+=1
if cnt<start_num:
continue
text = record["text"]
if dataset_type=="mc4":
record["id"]=f"mc4_{cnt}"
if text not in annotated_texts:
p_text = prepare_problem_text(record, with_id=True)
problem_list.append(p_text)
if len(problem_list)>n_problems:
break
unlabeled_problem_list=problem_list
annotations = pipeline.predict(unlabeled_problem_list)
predicted_probabilities = pipeline.predict_proba(unlabeled_problem_list)
df = pd.DataFrame({ "label": annotations, "probability": predicted_probabilities[:, 1],
"text": unlabeled_problem_list,
})
df=df.sort_values(by="probability", ascending=False)
df=df[df["probability"]>cut_threshold]
df=df[df["probability"]<1-cut_threshold]
question_texts=df["text"].tolist()
with open(f"annot/pages/questions.text", "w") as f:
f.write("\n".join(question_texts))
annot/pages/questions.textに、追加の質問が生成されるので、こちらもアノテーションします。
数百件ほどアノテーションすると、安定してきます。
データのフィルタリング
学習したモデルでフィルタリングします。
せっかくなので、0-1でスコア(確信度)を出し、0.5, 0.6, 0.7, …に分けて保存することにします。ひょっとすると、品質でテキストを分けられるかもしれないので。
import pandas as pd
import random
from tqdm import tqdm
import json
#フィルタリングしたデータセットの生成
n_problems=2000
problem_list=[]
mini_problem_list=[]
cnt=0
minibatch=1000
file_split_num=10**6
save_path=f"annot/pages/extracted_mc4.txt"
base_path=f"output/{dataset_type}/"
dataset=mc4_dataset
#dataset=oscar_dataset
for record in tqdm(dataset):
text = record["text"]
p_text = prepare_problem_text(record, with_id=False,)
mini_problem_list.append((cnt,p_text,text))
cnt+=1
if cnt>n_problems:
break
if cnt%minibatch==5:
rids,p_texts,texts=zip(*mini_problem_list)
#annotations = pipeline.predict(p_texts)
prob=pipeline.predict_proba(p_texts)[:,0]
for i in range(len(prob)):
int_prob=int(prob[i]*10)
prefix=int(cnt/file_split_num)
#ちょっとカットオフを厳し目にする
if prob[i]>0.55:
#せっかくなので、確信度で分けて保存する
with open(f"{base_path}/{prefix}_class_{int_prob}.txt", "a") as f:
j_line=json.dumps({"id":rids[i], "text":texts[i]},ensure_ascii=False)
f.write(j_line+"\n")
mini_problem_list=[]
結果
oscarは2000→500件
mc4は2000→330件
までフィルタリングされました。
完璧なフィルタリング精度では有りませんが、わりといい感じのテキストを抽出できたと思います。
フィルタ前
フィルタ後
mc4の処理速度が500it/sec程度でした。87,337,884件のデータがあるとのことなので、ざっくり2日ほど放置すれば、フィルタリングが終わる計算になります。ローカルマシンでも行える処理なので、十分に許容できる速度だと思います。
まとめ
このフィルタ効率でいくと
mc4: 830GB → 400GB
Oscar:110GB→30GB
くらいまで減らせそうです。