文書生成AI(テキストファイルからのファインチューニング)

単語を覚え、文法を学習する

AIは2段階で学習します。それは人間の学習に似ていて、
「単語」を学び、その後「文法」を学びます。

単語は「トークン」として覚えます。たとえば、

「これはペンです。これはリンゴです。」という文があると、

rinna/japanese-gpt2-xsmall では、これを

from transformers import AutoTokenizer
tokenizer = AutoTokenizer.from_pretrained("rinna/japanese-gpt2-xsmall")

tokens = tokenizer.tokenize("これはペンです。これはリンゴです。")
print(tokens)

[‘▁これは’, ‘ペン’, ‘です’, ‘。’, ‘これは’, ‘リンゴ’, ‘です’, ‘。’]

のように分割し、それぞれを別単語として認識し、それぞれを数値化します。

tokenizer("これはペンです。これはリンゴです。")

{‘input_ids’: [444, 2243, 2767, 8, 881, 16428, 2767, 8, 2], ‘attention_mask’: [1, 1, 1, 1, 1, 1, 1, 1, 1]}

「▁これは」が「444」で、「ペン」が「2243」…というように、数値を割り当てることで「覚える」という学習をしています。

単語を覚えた後、それぞれの単語の出現頻度を学習する、つまり「文法」を学習することで、文を生成できるようになります。

学習の種類

生成系AIに学習をさせる場合、「単語」から学習するか、「文法」から学習するか、を決める必要があります。単語から学習するのは大変です。そのため文法から学習することが多いです。それがファインチューニングです。

事前に学習されている「単語」と「文法の初期値」を使って、「文法」の修正をします。

そのため、これから学習させたいデータに近い事前学習モデルをダウンロードして使います。少なくとも、言語は同じである必要があります。日本語のデータを学習させたいのに、英語で事前学習されたモデルを使用すると、日本語の「単語」を知らないため、用意した学習データを全く理解できず、学習できません。

さらに「文法」にはいろいろ種類があり、翻訳なら多言語の文法のセットを学習しなければなりませんし、質疑応答では質問と応答の組み合わせを学習しないといけません。

どういう学習データを用意するか、は、何を実現させたいかを明確にし、AIがどのように学習をしているか、更にはその学習を実現するための関数を知らないとできないことなので、ハードルは高いです。

それらの詳細はHugging faceのwebページにサンプルがあります。

テキストデータでファインチューニング

希望通りの応答を返してくれるAIを作成するためには、それ用のデータとそれ用のプログラムが必要ですが、まずは単純にテキストデータを読み込んでファインチューニングするプログラムを作ってみます。

事前モデルは rinna/japanese-gpt2-xsmall
学習データは 以下のテキストファイル(このサイト内の記事)
を使用します。

早速、学習用のコードを。

from transformers import AutoTokenizer, AutoModelForCausalLM
model_name = "rinna/japanese-gpt2-xsmall"
tokenizer = AutoTokenizer.from_pretrained(model_name, padding_side="left") # 事前学習モデル「単語」を読み込み
model = AutoModelForCausalLM.from_pretrained(model_name) # 事前学習モデル「文法」の初期値を読み込み

from transformers import TextDataset
# テキストファイル(str_datasets)をトークン化
train_dataset = TextDataset(
    tokenizer=tokenizer,
    file_path="./testdata.txt", # 学習データ読み込み このテキストを変更すると学習結果が変わる
    block_size=128
)

from transformers import DataCollatorForLanguageModeling # 学習データの調整
data_collator = DataCollatorForLanguageModeling(tokenizer, mlm=False)

from transformers import Trainer, TrainingArguments
training_args = TrainingArguments( # 学習の事前設定
    output_dir=f"{model_name}-finetuned",
    overwrite_output_dir=True,
)
trainer = Trainer( # 学習の設定
    model=model,
    args=training_args,
    train_dataset=train_dataset,
    data_collator=data_collator,
    tokenizer=tokenizer,
)

trainer.train() # 学習の実行
trainer.save_model()

AutoTokenizerとAutoModelForCausalLM で事前学習モデルを読み込み

TextDataset で学習用データを読み込み tokenizer で数値化

DataCollatorForLanguageModeling で学習データを調整

Trainer で学習の設定 ただし、パラメータの一部は TrainingArguments で行う

train() で学習を実施

もし、グラフィックカードで計算できない場合は、use_cpu = True をつけて、CPUで計算します。

training_args = TrainingArguments(
    output_dir=f"{model_name}-finetuned",
    overwrite_output_dir=True,
    use_cpu = True,
)

学習結果の確認

実際に文章を作成させて、学習の成果を確認します。

まずは素の状態のモデルから。

from transformers import AutoTokenizer, AutoModelForCausalLM
model_path = "rinna/japanese-gpt2-xsmall"
tokenizer = AutoTokenizer.from_pretrained(model_path, padding_side='left')
model = AutoModelForCausalLM.from_pretrained(model_path)

def generate_text(model, tokenizer, prompt, length=50):
    input_ids = tokenizer.encode(prompt, return_tensors='pt', padding=True, truncation=True)
    attention_mask = input_ids.ne(tokenizer.pad_token_id).float()
    output = model.generate(
        input_ids=input_ids,
        max_length=length + len(input_ids[0]),
        attention_mask=attention_mask,
        pad_token_id=tokenizer.eos_token_id,
        do_sample=True,
    )

    text = tokenizer.decode(output[0], skip_special_tokens=True)
    return text

prompt = "「科学」とは"
generated_text = generate_text(model, tokenizer, prompt, length=150)
print(generated_text)

出力結果は

「科学」とは人間は、遺伝子レベルでその遺伝子を改変して生まれる生物であることを前提に生命を形成させる生物をつくり出すことである。 その遺伝子の変異は、自然界において、すでに自然界に存在していて、それによって生まれているのが生物である。 人間の遺伝子は、生まれたときと生後3カ月を経過しながら、新しい遺伝子で子孫を作るわけで、遺伝子を改変する以前に、人間は、遺伝子から遺伝子を受け継ぎ、遺伝子を書き換えして変えようとするものだ。 遺伝子は、生きている時から生まれ変わるので、新しい遺伝子が誕生すれば、人間は、すぐに生まれ変わる可能性がある。 遺伝子組み換え作物の作物は、今の遺伝子で生きているので、遺伝子組み換え作物の作物は、人間、

続いて、ファインチューニングしたモデルで。

from transformers import AutoTokenizer, AutoModelForCausalLM
model_path = "rinna/japanese-gpt2-xsmall"
model_name = f"{model_name}-finetuned"
tokenizer = AutoTokenizer.from_pretrained(model_path, padding_side='left')
model = AutoModelForCausalLM.from_pretrained(model_path)

def generate_text(model, tokenizer, prompt, length=50):
    input_ids = tokenizer.encode(prompt, return_tensors='pt', padding=True, truncation=True)
    attention_mask = input_ids.ne(tokenizer.pad_token_id).float()
    output = model.generate(
        input_ids=input_ids,
        max_length=length + len(input_ids[0]),
        attention_mask=attention_mask,
        pad_token_id=tokenizer.eos_token_id,
        do_sample=True,
    )

    text = tokenizer.decode(output[0], skip_special_tokens=True)
    return text

prompt = "「科学」とは"
generated_text = generate_text(model, tokenizer, prompt, length=150)
print(generated_text)

出力結果は

「科学」とはと通信して学習するものです。これは情報社会の到来から情報工学系の大学でも出題されていますので、ご参照ください。 は、により、情報通信技術や応用技術に深く関係した情報化技術について理論的に研究を行うことが可能になり、また、情報通信分野での応用技術・応用技術の研究および実用化に資することを期待しています。 そのため、今回は、本学の本学の専門的な基礎分野である、情報通信分野の専門教育について、これまでのキャリアパスを鑑みながら、学習者の皆さまから、研究や研究を奨励する「研究支援」を通じて、様々な分野の知識・経験を基礎化します。 知的財産とは、インターネットにより、情報が提供されるときに利用する

…変化が感じられません。学習データが少なすぎるように思います。

コメント