こんにちは、Zero-Cheeseです。
この記事では、Pythonと人工知能モデルGPT-2を使って、独自のAI対話パートナーを作る方法を紹介します。
具体的には、以下のスキルが習得できます。
- GPT-2の基本的な知識
- Pythonを使って、GPT-2をファインチューニングする方法
この記事の対象読者は次の通りです:
- 一人でいることを寂しく感じる方
- 新たな対話パートナーを探している方
特に、テクノロジーに興味があり、新しいことを学ぶ意欲がある方には、ぜひ参考にしてみて下さい。
このスキルを活用すれば、AIがあなたの新たなパートナーとなり、孤独を克服する一助になる可能性があります。
それでは、新たなAI友達を作る旅に、一緒に出発しましょう。
なお、本記事の内容は、
- Pythonに少しでも触れた経験のある方
を対象としています。
AIとの友情
本章では、「AIとの友情やGPT-2の特徴」を、語っています。
「早く、具体的な実装方法を知りたい」方は、次の章までSkipして下さい。
AIの進化とともに描かれる、新たな未来
AI技術が猛烈な速さで進化している現代、私たちの生活の新たなパートナーとして、対話型AIがその存在感を増しています。
この対話型AIは、私たちが作ってきた無数の文章を学習し、人間のように新たな文章を創り出します。
その様子は、まるで生命体のように思考し、感じるAIみたいで、驚きを隠せません。
対話型AIは、
- あなたの問いに対して答えたり、
- ユーモラスなジョークで笑いを誘ったり、
- 詩的な表現で心を動かしたり、
- 独自のストーリーで想像力を刺激したりします。
つまり孤独を感じたとき、頼もしく感じられます。
AIの進化は、私たちの生活に新たな風景をもたらし、私たちの人生をより豊かにしてくれる可能性を秘めています。
なぜ、GPT-2?(なぜ、GPT-3やGPT-4じゃない??)
最新のGPT-3やGPT-4も存在しますが、この記事ではGPT-2を選択しました。
その理由は二つ:アクセシビリティとリソースの効率性です。
アクセシビリティ
GPT-2は、OpenAIのAPIを使用せず、自分のPCから直接アクセスできる、唯一のモデルです。
プライバシー保護と、無制限で無料での使用が、大きな魅力です。
リソースの効率性
GPT-2は、後継モデルに比べてパラメータ数が少なく、個人のPCで、ファインチューニングが可能です。
GPT-2は、個人が購入できるGPUで十分ですが、GPT-3は1750億のパラメータを有しているため、GPUの必要メモリ量が大幅に増え、個人レベルでは、おそらく無理?
(そもそもオープンソース化されていないため、自分のPC上で、ファインチューニング不可ですが・・)
GPT-2のセットアップ
自分だけの対話パートナーを作るために、まずはGPT-2の適切なセットアップが必要です。
ハードウェアと、ソフトウェアに分けて、その手順を詳しく解説します。
必要なハードウェア
GPT-2には複数のモデルが存在し、それぞれが異なる計算リソースが要求します。
下記は、英語版 GPT-2のケースです。
- GPT-2 Small版(117Mパラメータ): 4GB以上のVRAMを搭載したGPUが必要で、NVIDIA製のGPUであれば、GTX 1050Ti以上が望ましいです。
- GPT-2 Large版(1.5Bパラメータ): 大規模なこのモデルは16GB以上のVRAMを搭載した高性能GPU(例えば、RTX 3090)が必要です。
「どんなGPUを選べばいいの?」と思っている方は、下記記事が、参考になります。
また、最低でも16GBのメモリと、数GB以上のストレージスペースが必要です。
必要なソフトウェア
PyTorchのインストール
詳細な手順は、下記記事を参照してください。
GPT-2のインストール
Pythonのパッケージマネージャであるpipを使って、GPT-2を簡単にインストールできます。
pip install transformers
# 日本語を使う場合は、下記パッケージも必要
pip install sentencepiece
「transformers」はHugging Face が開発したパッケージで、GPT-2以外の多数の言語モデルも簡単に利用できます。
これで、セットアップは完了です!
ファインチューニングせずに、GPT-2を動かしてみる
GPT-2は、元々、多様なWebテキストから学習されています。
そのため、ファインチューニングを行わなくても、一般的な文章生成が可能です。
Pythonコードのご紹介
英語版
以下のPythonコードは、ユーザーからの入力に対して、応答文を生成してくれます。
from transformers import GPT2LMHeadModel, GPT2Tokenizer
def gpt2_chat(user_input):
tokenizer = GPT2Tokenizer.from_pretrained('gpt2')
model = GPT2LMHeadModel.from_pretrained('gpt2')
input_ids = tokenizer.encode(user_input, return_tensors='pt')
outputs = model.generate(input_ids, max_length=300,
num_return_sequences=1, temperature=1.0, repetition_penalty=1.2)
response = tokenizer.decode(outputs[0], skip_special_tokens=True)
return response
while True:
user_input = input('入力 >')
print(gpt2_chat(user_input), flush=True)
実行結果:
「Hi How are you」 に対する応答例:
I'm really happy with the results. I think it's a good sign that we're getting better at our game and hopefully, in time, will be able to do something similar for other teams."
ただし、この状態のGPT-2はファインチューニングされていないため、出力される応答は、一般的なものになります。
日本語版
日本語の学習済みモデルを提供してくれている、rinna社のものを使用します。
GPT-2の日本語版モデルは、下記の3種類があります。
- rinna/japanese-gpt2-medium(336Mパラメータ)
- rinna/japanese-gpt2-small(110Mパラメータ)
- rinna/japanese-gpt2-xsmall(37Mパラメータ)
今回は、「rinna/japanese-gpt2-medium」を使用します。
from transformers import AutoTokenizer, AutoModelForCausalLM
tokenizer = AutoTokenizer.from_pretrained("rinna/japanese-gpt2-medium", use_fast=False)
tokenizer.do_lower_case = True # due to some bug of tokenizer config loading
model = AutoModelForCausalLM.from_pretrained("rinna/japanese-gpt2-medium")
def gpt2_chat_ja(input_text):
input_ids = tokenizer.encode(input_text, return_tensors='pt')
outputs = model.generate(input_ids,
max_length=100,
num_return_sequences=1,
temperature=0.7,
no_repeat_ngram_size=2)
response = tokenizer.decode(outputs[0], skip_special_tokens=True)
return response[len(input_text):]
while True:
user_input = input('入力 >')
print(gpt2_chat_ja(user_input), flush=True)
Copyright rinna Co., Ltd.
Released under the MIT license
https://github.com/rinnakk/japanese-pretrained-models/blob/master/LICENSE
実行結果:
「平将門って知ってる?」 に対する応答例:
平 将之(たいら の まさゆき)は、平安時代初期の貴族。 名は将(まさ)とも記される。 官位は従五位上・河内守。 天長8年(831年)従五位下に叙爵し、承和3年/836年に発生した承和元年(934年)6月の遣唐使の大使に任ぜられる。 同年8月に遣隋使が唐に到着すると、将監・平忠
おそらく知らない模様です・・・、
上記コードの場合、attention_maskを使っていないため、警告が表示されます。
無視して頂いても大丈夫ですが、気持ち悪い方は、下記コードで対応可能です。
from transformers import AutoTokenizer, AutoModelForCausalLM
tokenizer = AutoTokenizer.from_pretrained("rinna/japanese-gpt2-medium", use_fast=False)
tokenizer.do_lower_case = True # due to some bug of tokenizer config loading
model = AutoModelForCausalLM.from_pretrained("rinna/japanese-gpt2-medium")
def gpt2_chat_ja(input_text):
inputs = tokenizer.encode_plus(input_text,
return_tensors='pt',
padding="max_length",
truncation=True,
max_length=256)
input_ids, attention_mask = inputs["input_ids"], inputs["attention_mask"]
outputs = model.generate(input_ids,
attention_mask=attention_mask,
max_length=512,
num_return_sequences=1,
temperature=0.7,
no_repeat_ngram_size=2)
response = tokenizer.decode(outputs[0], skip_special_tokens=True)
return response[len(input_text):]
while True:
user_input = input('入力 >')
print(gpt2_chat_ja(user_input), flush=True)
ファインチューニングをせずに使用するGPT-2の問題点
GPT-2は非常に強力な言語モデルであり、多くの用途で優れた性能を発揮します。
しかし、ファインチューニングを行わないで使用すると、いくつかの問題点が発生します。
ここでは、その主な問題点を3つご紹介します。
問題点1:一般的な回答にかたよる
GPT-2は、インターネット上の多様なテキストデータから学習されています。
そのため、学習データは多種多様なジャンルや話題をカバーしていますが、それらは一般的な文化、言語、話題にかたよっています。
つまり、特定の話題や専門知識について、深く学習できていません。
また、特定のユーザーが好む知識、スタイルやトーンからは遠い存在になっています。
問題点2:最新の情報に対応できない
GPT-2は学習時点で利用可能だったデータからのみ学習しています。
そのため、学習データの収集時点以降に登場した、新たな技術や流行語などについては、認識することができません。
具体的な例としては、現代の新しいテクノロジーや最新の流行に、対応できません。
問題点3:誤った情報を提供する可能性
GPT-2は学習したデータからパターンを学習しますが、そのデータが必ずしも正確であるとは限りません。
そのため、誤った情報や誤解を招く情報を生成する可能性があります。
まあ、AI友達との会話は、少しくらい、嘘があった方が楽しいかもですが(笑)
問題点に対する、対応策
以上のような問題を解消するために、「ファインチューニング」という手法があります。
GPT-2の「ファインチューニング」を行うことで、あなた好みのAI友達を。作ることが可能になります。
Pythonで、GPT-2のファインチューニング(日本語版のみ)
本章では、あなたが理想とするAI友達を作成するために、具体的にどのようにファインチューニングを行うか、紹介していきます。
訓練データセットの準備
学習して欲しい内容を、リスト型で準備しましょう。
下記は、あくまでも一例です。
train_texts = ["「平将門」は平安時代初期の武将で、承平天慶の乱の指導者。東国の自立を試み、自称新皇を称した。"]
訓練に必要なパッケージを、インストールする。
pipコマンドを使って、下記をインストールして下さい。
pip install datasets
pip install accelerate
datasets
ライブラリは、Hugging Faceが提供する、機械学習モデルの訓練や評価に使用するデータセットを簡単に利用できるパッケージです。
accelerate
は、Hugging Faceにより提供する、深層学習モデルの訓練を高速化するためのパッケージです。
Pythonで、ファインチューニングの実行、訓練モデルの保存
from transformers import AutoTokenizer
from transformers import AutoModelForCausalLM
from transformers import DataCollatorForLanguageModeling
from transformers import TrainingArguments
from transformers import Trainer
from datasets import Dataset
tokenizer = AutoTokenizer.from_pretrained("rinna/japanese-gpt2-medium", use_fast=False)
tokenizer.do_lower_case = True # due to some bug of tokenizer config loading
model = AutoModelForCausalLM.from_pretrained("rinna/japanese-gpt2-medium")
def load_dataset(tokenizer):
# 訓練データの準備
train_texts = ["「平将門」は平安時代初期の武将で、承平天慶の乱の指導者。東国の自立を試み、自称新皇を称した。"]
train_encodings = tokenizer(train_texts,
truncation=True,
padding=True)
return Dataset.from_dict(train_encodings)
train_dataset = load_dataset(tokenizer)
# Data collator
data_collator = DataCollatorForLanguageModeling(
tokenizer=tokenizer,
mlm=False,
)
# 学習パラメータ(下記設定は、一例です。)
training_args = TrainingArguments(
output_dir="./gpt2-japanese",
overwrite_output_dir=True,
num_train_epochs=100,
per_device_train_batch_size=1,
save_steps=10_000,
save_total_limit=2,
)
# Trainer
trainer = Trainer(
model=model,
args=training_args,
data_collator=data_collator,
train_dataset=train_dataset,
)
# Training
trainer.train()
# 学習済モデルを保存する
model.save_pretrained("./gpt2-japanese")
tokenizer.save_pretrained("./gpt2-japanese")
29行目の、学習パラメータ「 TrainingArguments」の、各引数は、以下の通りです。
output_dir
: モデルのチェックポイントと訓練のログが保存されるディレクトリのパス。ここでは、カレントディレクトリの下に”gpt2-japanese”という名前のディレクトリが、使用されています。overwrite_output_dir
: この引数がTrueの場合、既存の出力ディレクトリが存在するときにそれを上書きします。つまり、訓練を再開する前に、古い訓練の結果を削除します。num_train_epochs
: 訓練データ全体を何回繰り返し学習するか、つまりエポック数を指定します。ここでは、10エポックで訓練を行います。per_device_train_batch_size
: 各デバイス(GPUまたはCPU)ごとに使用するバッチサイズを指定します。ここでは1を指定しています。save_total_limit
: 保存するチェックポイントの最大数を指定します。最新のものが優先され、この数を超えると古いチェックポイントが削除されます。ここでは、2つのチェックポイントを保存するよう指定しています。
Pythonで、訓練モデルを呼び出し、推論する。
さきほど、保存したモデルを呼び出します。
from transformers import AutoTokenizer, AutoModelForCausalLM
tokenizer = AutoTokenizer.from_pretrained("./gpt2-japanese", use_fast=False)
tokenizer.do_lower_case = True # due to some bug of tokenizer config loading
model = AutoModelForCausalLM.from_pretrained("./gpt2-japanese")
def gpt2_chat_ja(input_text):
# Encode the input text and get the attention mask
inputs = tokenizer.encode_plus(input_text,
return_tensors='pt',
padding="max_length",
truncation=True,
max_length=256)
input_ids, attention_mask = inputs["input_ids"], inputs["attention_mask"]
# Generate the output
outputs = model.generate(input_ids,
attention_mask=attention_mask,
max_length=512,
num_return_sequences=1,
temperature=0.7,
no_repeat_ngram_size=2)
response = tokenizer.decode(outputs[0], skip_special_tokens=True)
return response[len(input_text):]
while True:
user_input = input('入力 >')
print(gpt2_chat_ja(user_input), flush=True)
実行結果:
「平将門って知ってる?」 に対する応答例:
将門は平安時代初期の武将で、承平天慶の乱の指導者。東国の自立を試み、自称新皇を称した。
バッチリ、学習されてます。
あとはファインチューニングを進めて、自分の好きな色に染め上げれば、バッチリです!
雑談
以下、ただの雑談になります。(お時間がある方は、お付き合い頂けますと、幸いです。)
正直なところ、まだまだ性能には改善の余地があります。生成される文章が意味不明だったり、文脈がつながらないこともまだ多いのが現状です。
特に、GPT-4を使っている方から見ると、物足りなく感じると思います。
今後、GPT-3やGPT-4がオープンソース化するかどうか分かりませんが、期待したい所です。
また、OpenAI以外からも、LLM(大規模言語モデル)がぞくぞくと出現しているので、そう遠くない将来、もっと精度の高いAI友達が実現できると期待しています。
お話は変わりますが、近いうちに、AIが出力したテキストを音声として出力する(音声合成)方法についても記事をアップ予定です。 後日、公開しました!
ご興味がありましたら、ご覧になって下さい。
本記事も、最後までお付き合い頂き、ありがとうございました。
それではまた、お会いしましょう!