言語モデルによる特定個人の会話の模倣

19EC050 坂本竜秋

目次

1. 研究背景・目的

2023年現在、第三次AIブームの渦中にあるAI分野の発展は目覚ましく、機械学習手法であるディープラーニング技術の応用によって、画像・音声認識分野、自然言語処理分野などAI技術は多方面に進化し発展し続けている。言語生成AI分野では、Rinna社(株)提供の「キャラる」やAI VOLT社(株)提供の「ドリームフレンド」など、キャラクタの模倣に注視したAIチャットボットサービスが登場している。これを踏まえ当研究では「実在する人物の会話を模倣できるか」をテーマに、言語モデルのチューニング手法の観点から研究を行った。

2. 原理・手法

2.1 言語モデル(Language Model)

言語モデルとは、学習したデータを基に言葉の並び方、出現率などの確率を予測することで、テキスト生成、質問への応答など多岐にわたる言語処理を行うことを目的とした技術である。与えられたテキストの次に来る単語を予測、更に次の単語を予測と繰り返すことで一連のテキストを生成する。本研究では、ニューラルネットワークの言語モデルを使用する。

ニューラルネットワークとは、人間の神経回路網を模した数式モデルである。入力データを受け取り、計算処理、出力する「ノード」、ノードが集まって形成される「層」、ノード間の接続の強度である、「重み」から構成されている。層には入力層、隠れ層、出力層の3種があり、入力層からデータを受け取り、隠れ層で計算処理、出力層でデータを出力する。図1はこれらニューラルネットワークの図式である。

ニューラルネットワーク図式
図1 ニューラルネットワーク図式[1]

言語モデルでは、ノードに単語、文字などの言語要素が入り、重みによって単語間、フレーズ間の関係を学習、与えられた単語の次に来る単語の予測確率を調整する。これら重みを言語モデルでは「パラメータ」と呼び、パラメータ数の多い言語モデルはより複雑な処理を行うことが可能である。

本研究のように人物間の会話を模倣させるなど、言語モデルを特定のタスクに特化させる場合、「事前学習」及び「微調整(ファインチューニング)」が特に有効とされている。[2]。事前学習では、ニュース記事や学術論文、書籍など自然言語で書かれたテキストを広範囲に学習することで、言語の基本文法、文脈の関係性などの予測精度を向上させる。その後、言語モデルを特定のタスクに特化させるため、そのタスク専用のデータセットで学習を行うことで、目的に沿った言語モデルを訓練することが可能となる。

本研究は特定個人の会話が模倣可能か研究するため、事前学習済みの日本語言語モデルを用意し、会話に特化した言語モデルを訓練するためのデータセットを用意し、チューニングを行う。

2.2LoRA(Low-Rank Adaptation)

本研究では、「Low-Rank Adaptation(以下LoRA)」と呼ばれる微調整法を用いて言語モデルのチューニングを行う。従来の微調整はデータセットを用いて言語モデル全体のパラメータを更新するものであったが、言語モデルの大規模化に伴い微調整を行うために必要な計算リソースが増加しており、この問題の解決案としてLoRAが考案された[3]。

LoRAは言語モデル内で行われている計算処理を、より少ない計算で近似する手法である。ニューラルネットワークの各層に事前学習済みの重み行列を近似した行列を追加し、この追加した行列に微調整を行うことで計算リソースの削減を行う。以下にその仕組みを解説する。図2は、LoRAの模式図である。

ニューラルネットワーク図式
図2.LoRA 模式図 「LoRA: Low-Rank Adaptation of Large Language Models」[3]

ニューラルネットワークの各層の、入力値をx、出力値をh、事前学習済みの重み行列を"Pretrained weight W"とする。Wがd×kの寸法を持つと定義し、r << min(d,k)となるrを定義する。この時、d×rの寸法を持つ行列A、k×rの寸法を持つ行列Bを定義すると、WはA、Bの積なので

W = AB

で表すことができる。また、Wのパラメータ数はd・k、Aのパラメータ数はd・r、Bのパラメータ数はk・rなので、ABのパラメータ数はd・r + k・rで表すことができる。

r << min(d,k)より、d・k >> d・r + k・r

以上のことから、WではなくABに微調整を行うことで、微調整に必要な訓練パラメータ数を大きく削減することができる。

下記は、LoRAを提唱したOpenAIが調査した、異なる手法でGPT-3(パラメータ数175B)に微調整を行った際の、各微調整に必要な訓練パラメータ数、及び3種の言語モデル用ベンチマークによるテスト結果である。

GPT-3の微調整結果を示す図
GPT-3調整結果 「LoRA: Low-Rank Adaptation of Large Language Models」[3]

LoRAと通常の微調整方法(FT)を比較すると、通常の微調整では訓練に必要なパラメータ数を大幅に削減した上で各ベンチマークのテスト結果が向上していることが分かる。本研究では使用可能な計算リソースが限られているため、リソース効率よく微調整を行うためにLoRAを採用した。

3.実験

3.1概要

本研究では、チューニングを行う大規模言語モデルとして2023年5月よりRinnaより提供されているオープンソースの日本語大規模言語モデル「Rinna 3.6B-ppo」[4]を使用し、LoRAを用いてチューニングを行った。会話を模倣する被験者の日常会話を録音し、テキスト形式に書き起こしたものを訓練データとする。このデータを基に言語モデルのチューニングを行い、機械学習後のAIモデルが効果的に学習をおこなえているかの指標値である、「Validation loss」及び「Training loss」の値を確認し、チューニング結果の評価を行う。

次に、調整済みの言語モデルが被験者を模倣した対話を行えるか検証するために、言葉遣いや状況などで差別化した質疑応答のテンプレートを作成し、調整済み言語モデルに入力して出力結果を観察する。この際、調整前言語モデルとの出力結果の差異や質問に対する回答の精度、傾向などを観察し、被験者の会話の傾向を模倣できているかを評価する。

3.2 構築環境・実験試料等

3.2.1 システム構成

本研究ではチューニングプログラムを動かすための動作環境として、メモリ数32GB、NVIDIA Geforce RTX4070ti(内部メモリ 12GB)のグラフィックボードを内蔵したPCを使用した。またプログラミング環境としては、Python3.10.11を言語として使用し、円滑にコードを記述するためにWindows Jupyter Notebookをプログラミングツールとして使用した。

機械学習には多量の計算リソースが必要となるため、GPUを計算処理に利用するために、CUDA(cuda12.2)を使用した。

3.2.2 言語モデル

・使用言語モデル:Rinna-japanese-gpt-neox-3.6b-instruction-ppo(以下Rinna3.6b-ppo)[3]

Rinna3.6b-ppoは2023年5月にRinna株式会社より発表された日本語オープンソースのGPT言語モデルである。本研究では日本語の日常会話を訓練データとしてチューニングを行うため、日本語データセットで訓練され[4]、上記動作環境内でLoRAを実行可能なモデルサイズの大規模言語モデルとしてRinna3.6b-ppoを選択した。

3.2.3 実験試料

言語モデルをチューニングするための訓練データとして、会話を模倣する被験者の日常会話を約2カ月間録音した。この音声ファイルを訓練用データとして使用するために、以下のjsonフォーマットに整形した。録音データは補遺6.1に記載する。


[
    {  "input" : "対話相手の発話1 " ,
    "completion" : " 被験者の応答1"  },
    {  "input" : "対話相手の発話2 " ,
    "completion" : " 被験者の応答2"  },
]
                

この訓練データを使用してLoRAを行うことで、「input」に会話を受け取り、「completion」で応答を返すチャットボット形式の言語モデルとして訓練した。

3.3 LoRAプログラムとパラメータ

3.3.1概要

LoRAチューニング用のプログラムを作成し、言語モデルに訓練を行う。LoRAの準備プログラム及び実行プログラムは巻末の補遺6.2にそれぞれ図17、図18として記載する。プログラム実行後は訓練中、訓練後の「Validation loss」及び「Training loss」の値を確認することでチューニング評価を行う。

3.3.2 プログラム解説

補遺6.2に記載した図17はLoRA実行前の各種調整を行うプログラムである。データ処理、訓練に必要なライブラリをインポートし(1~6行)、モデル名、訓練データのパス、訓練後モデルの出力ディレクトリなどの基本パラメータを宣言する(7~11行)。その後、言語モデルに入力されたテキストを解析するために単語、フレーズに分解するトークン化処理を行う(20~35)。本プログラムではインポートしたtransformersライブラリ内に用意されたAutoTokenizerクラスをトークン化に使用する。トークン化したテキストはinput_ids、attention_maskとして出力する。

次に、整形した訓練データのjsonファイルをUTF-8形式でエンコードして読み込み(37~41行)、プロンプトテンプレート作成(44~53行)、データセットトークン化(55~65行)を行う。プロンプトテンプレートでは訓練データセット内の改行を<NL>と置換することで1行の文字列として認識させる。データセットのトークン化を行う際には、データポイントの10%をバリデーションセット(val_dataset)、残りをトレーニングセット(train_dataset)として分割し、tokenize関数でトークン化する。次にtransformersライブラリから言語モデルをロードするためのAutoModelForCausalLMクラスをインポートし、LoRAを行うための言語モデル、rinna3.6b-ppoをロードする(68~74行)。ここまでLoRA適用前の言語モデル、及び訓練データセットの準備を行った。

次に、LoRAの設定を定義する(77~98行)。peftからLoraconfigクラスをインポートし、LoRAに必要な各種パラメータの設定を行う。これらパラメータの詳細は4.1.2に記載する。同じくpeftからインポートしたprepare_model_for_kbit_training()関数にrinna3.6b-ppoを入力してモデルの前処理を行いget_peft_model()関数にrinna3.6b-ppo、設定したloraconfigを入力することで、設定の適応を行う。また、model.print_trainable_parametersメソッドを呼び出し、訓練プロセス中のパラメータの表示を行う。

最後に、transformers.Trainerクラスを試用して、トレーナーの設定を行う(105~125行)。この設定についての詳細は4.1.3に記載する。

図18は図17にて調整した各種パラメータを基にLoRAを行うためのプログラムである。CUDAの有効化(1~3行)、訓練実行前後のキャッシュ設定を行い(6、8行)訓練の実行(7行)、及び訓練済みモデルの保存(11行)を行う。訓練には多量の計算リソースを必要とするため実行前にキャッシュ保存を無効化し、トレーニング後の評価時に性能を向上させるために再度有効化する。訓練済みモデルの保存先は図?の10行目で宣言したpeft_nameで指定している。

3.3.3 LoRAパラメータ設定の詳細

図17の77~98行内で設定したLoraConfig内の各種パラメータにおいて、特にチューニングの精度に関わるパラメータについて解説する。

81行目rはLoRA適応における低ランク行列のサイズを指定するパラメータである。この値が大きいほど行列サイズが大きくなるためチューニングするパラメータ量が増加し、より複雑なチューニングを行うことが期待できる。

84行目lora_dropoutはLoRAを行う際に適用されるドロップアウト率を指定するパラ メータである。ドロップアウトは訓練中に加えられる重みを一定確率で無視することで、訓練データに過度に適応する状態を抑制し過学習を防ぐ効果が期待できる。一方で過度な値を設定すると訓練データの多くを無視することで訓練不足に陥る可能性もあるため、適切な値を設定する必要がある。

3.3.4 トレーニングパラメータ設定の詳細

図17の105~125行内で設定したtrainerの各種パラメータにおいて、特にチューニングの精度に関わるパラメータについて解説する。

111行目num_train_epochsは訓練データセットを何往復繰り返してモデルに学習させるかを指定するパラメータである。例えば1エポックなら学習を1回のみ行い、5エポックなら5回学習を行う。値を増加させることで訓練データから多くのパターンを学習できるが、同一の訓練データで学習を行うため、過度な値を取ると過学習を引き起こす可能性がある。そのため、訓練データのサイズに合わせたエポック数を取ることが必要と言える。

112行目learning_rateは学習中のモデルの重みの変化量を制御する値である、学習率を指定するためのパラメータである。学習率はモデルが新しい情報をどの程度まで取り入れるかを決定する値であり、重みの更新量と比例関係にある。そのため学習率を増加させることで一度に変更する重みの大きさが大きくなるため、学習スピードの増加を行える。しかし、学習率を過度に増加させた場合、一度の変化量が大きくなり、訓練データに過度に適応する過学習となってしまう可能性がある。一方で過度に低い値の場合は十分に学習を終える前に訓練が終了してしまう可能性があるため、適切な値を設定する必要がある。

3.3.3及び3.3.4で提示したパラメータは全て訓練精度に影響をおよぼすため、適切な値を設定しなければならない。従って、これら4種のパラメータの値を変化させて複数回訓練を行いそれぞれの訓練後モデルに異なる名前を付け、「Validation loss」及び「Training loss」の値から各訓練後モデルを評価し、パラメータの最適な値を確認した。それぞれの訓練後モデルの名称及びそれらのパラメータ値を、下記表1にまとめる。また、各パラメータ値確認後はそれぞれの最適な値を代入した最終モデルを作成し、評価を行った。

表1.各訓練後モデルの名称及びパラメータ値

訓練後モデル名 (Peft_name) r lora_dropout num_train_epochs learning_rate
lora-Default 8 0.05 3 3e-4
lora-r_1 1 0.05 3 3e-4
lora-r_20 20 0.05 3 3e-4
lora-dropout_0.1 8 0.1 3 3e-4
lora-dropout_0.5 8 0.5 3 3e-4
lora-num_train_epochs_1 8 0.05 1 3e-4
lora-num_train_epochs_10 8 0.05 10 3e-4
lora-learning_rate_1e-2 8 0.05 3 1e-2
lora-learning_rate_1e-5 8 0.05 3 1e-5

3.4 チューニング

それぞれの訓練後モデルの、最終Step時のValidation loss及びTraining loss、学習時間を下記表2にまとめた。

表2. 各訓練モデル最終StepのTraining Loss及びValidation Loss

Peft_name 最終Step Training Loss Validation Loss 学習時間[m]
lora-Default 1300 1.194700 1.419970 16:27
lora-r_1 1300 1.220000 1.378574 16:16
lora-r_20 1300 1.204800 1.366619 16:29
lora-dropout_0.1 1300 1.187400 1.411895 16:43
lora-dropout_0.5 1300 1.207200 1.586636 16:52
lora-num_train_epochs_1 400 1.281900 1.360051 05:21
lora-num_train_epochs_10 4500 0.599600 2.053071 57:27
lora-learning_rate_1e-2 1300 3.241100 3.315143 19:07
lora-learning_rate_1e-5 1300 1.405400 1.436403 16:49

各パラメータの適切な値を確認するために、変化させたパラメータごとにTraining loss、Validation lossの推移を確認、比較を行った。

3.4.1 変化させたパラメータ:r

r=8の「lora_Default」、r=1の「lora-r_1」、r=20の「lora-r_20」の各訓練後モデルのTraining loss、Validation lossの推移をそれぞれ図3、図4に記載する。

rのTr-loss
図3. パラメータ:r 各訓練後モデルのTraining loss 推移
rのVali-loss
図4. パラメータ:r 各訓練後モデルのValidation loss 推移

lora-r_1はlora_Defaultからrの値を減少させた訓練後モデルだが、Trainng loss、Validation lossをlora_Defaultと比較すると、Training lossが増加したが、Validation lossは減少した。これは、低ランク行列の減少に伴って言語モデルのパラメータ数が減少し、複雑なパターンを処理する能力が低下したことで訓練データへの適応力が低下したが、結果として過学習を防いだため、Validation lossは低下したと考えられる。

一方、lora-r_20はrの値を増加させることで、lora-r_defaultよりもTraining lossが僅かに増加したが、Validation lossがlora-r_1よりも更に大きく減少している。これはモデルがパラメータ数の増加に伴ってより複雑な処理に適応できるようになり、未知のデータに対して適切な応答を返せるようになったためと考えられる。

総じて、rの値は増加させることでValidation lossの減少、パラメータ数増加によるモデル性能の向上が期待できるため、r=20の値がより適当であると結論付けた。

3.4.2変化させたパラメータ:lora-dropout

lora-dropout=0.05の「lora_Default」、lora-dropout=0.1の「lora-dropout_0.1」、lora-dropout=0.5の「lora-dropout_0.5」の各訓練後モデルのTraining loss、Validation lossの推移をそれぞれ図5、図6に記載する。

dropoutのTr-loss
図5. パラメータ:lora-dropout 各訓練後モデルのTraining loss 推移
dropoutのVali-loss
図6. パラメータ:lora-dropout 各訓練後モデルのValidation loss 推移

dropout率の増加は学習中に無視するデータ量が増えるため、結果的にはモデルの学習量を減らす効果がある。従って本来はdropout率を増加させるほど過学習を防ぎValidation lossの減少が期待できるが、lora-dropout_0.1は0.05から0.5まで増加させた場合、Validation lossが非常に高い値を取っている。これは過学習状態を起こしているのではなく、学習量を大きく減少させたことによってモデル自体の性能が大きく低下している可能性が考えられる。

一方lora-dropout_0.1とlora-Defaultを比較すると、最終的なValidation lossはlora-dropout_0.1の方が低いが、400~1200Step間ではlora-Defaultの値が低く、dropout率の増加で期待できる過学習の抑制が効果的にはたらいているとは考えにくい。従って、今回はdropout率0.05が最も適切な値であると結論付けるが、最終的なValidation lossの値が逆転していることから、もし更にエポック数を増やして訓練する場合は過学習の抑制を期待してdropout率0.1が適切な値となる可能性が考えられる。

3.4.3 変化させたパラメータ:num_train_epochs

num_train_epochs=3の「lora_Default」、num_train_epochs=1の「lora-num_train_epochs_1」、num_train_epochs=10の「lora-num_train_epochs_10」の各訓練後モデルのTraining loss、Validation lossの推移をそれぞれ図7、図8に記載する。

epochsのTr-loss
図7. パラメータ:num_train_epochs 各訓練後モデルのTraining loss 推移
epochsのVali-loss
図8. パラメータ:num_train_epochs 各訓練後モデルのValidation loss 推移

エポック数に関してはデータの訓練往復数以外のパラメータが完全に同一であるためDefaultとの比較は必要ない。図の通りStep数が増加するほどにTraining lossは大きく減少し、Validation lossは大きく増加している。トレーニングデータに対し高い精度を持つ一方で、未知のデータに対する応答能力が大きく減衰しており、間違いなく過学習の状態を引き起こしているといえるだろう。過学習を起こしている理由としては訓練データ不足が主な原因として考えられるため、3.6.2にて詳しく考察する。

3.4.4 変化させたパラメータ:learning_rate

learning_rate=3e-4の「lora_Default」、learning_rate =1e-2の「lora- learning_rate_1e-2」、learning_rate =1e-5の「lora- learning_rate_1e-5」の各訓練後モデルのTraining loss、Validation lossの推移をそれぞれ図9、図10に記載する。

learning_rateのTr-loss
図9. パラメータ:learning_rate 各訓練後モデルのTraining loss 推移
learning_rateのVali-loss
図10. パラメータ:learning_rate 各訓練後モデルのTraining loss 推移

学習率を3e-5から増加させた結果、1e-5、1e-2ともにTraining loss、Validation lossが増加した。これは、学習を行う際、重みの更新が過剰に行われてしまうことで、最適な値よりも大きく変化しているためであると考えられる。従って、本実験ではデフォルトの3e-4が適切な値と言える。

3.4.5最終モデルの作成

4.2.1~4.2.4の結果より、各パラメータの最適な値を代入した最終モデルである、lora_Expを作成した。下記表3にlora-Default、lora-Expそれぞれの各パラメータ、最終Step時のTraining loss、Validation Lossを記載する。

表3.lora-Exp、lora-Defaultの各パラメータ値及び最終損失値

訓練後モデル名 Peft_name r lora_dropout num_train_epochs learning_rate Training loss Validation loss
lora-Default 8 0.05 3 3e-4 1.1947 1.41997
lora-Exp 20 0.05 5 3e-4 1.0122 1.452561

lora-ExpのTraining loss及びValidation lossを図11、各lossのlora-Defaultと比較したグラフをそれぞれ図12、図13に記載する。

expのloss
図11. lora-ExpのTraining loss及びValidation Loss
expのTr-loss
図12. lora-Exp、lora-DefaultのTraining loss
expのVali-loss
図13. lora-Exp、lora-DefaultのValidation loss

lora-Defaultと比較すると、エポック数を増加させているためlora-num_train_epochs_10のようにTraining lossの減少、及びValidation lossの増加が予想されたが、rを増加させることで複雑な応答への対応力を強化し、訓練データへ適応しながら未知のデータへの対応力を向上させ、結果としてValidation lossの増加を最小限に抑えつつTraining lossを大きく減少させた。未知のデータに対する予測能力を維持しつつ訓練データへの適応力を向上させたため、数値上はlora-Defaultよりも最適なチューニングを行えたと言える。

3.5 検証

3.5.1 検証方法

実際に訓練後モデルが被験者の会話を模倣した対話を行えるか検証するため、質疑応答用プログラム及び質疑応答テンプレートを作成し、実験1で作成したlora-Expを用いて各質問を入力した応答を確認した。訓練後モデルはinputに会話を受け取り、completionで応答を返す形式に訓練しているため、質疑応答プログラムは補遺6.3の図19に記載する。このプログラムでは、

  • ###指示:<NL>質問 <NL><NL>
  • ###回答:<NL>質問に対する言語モデルの回答 <NL><NL>
という形式で質疑応答を行った。下記図14は質疑応答用のテンプレートである。


Question = [
    "Q1:自己紹介をお願いします。まず氏名を教えてください。",
    "Q2:タイムマシーンはできると思う?", 
    "Q3:未来の目標や夢は何ですか?", 
    "Q4:今日すごい良い天気だよね",
    "Q5:受験勉強の調子はどう?"
]
i=0
print("peft_name:"+peft_name)
for question in Question:
    i += 1
    print("Q" + str(i) + " : " + question + "\n")
    for j in range(5):
        print(j + 1, "回目")
        prompt = question
        generate(prompt)
        print("\n")
				

図14質疑応答テンプレート

このテンプレートは、Question配列内の各質問をそれぞれ5回ずつ###指示:に代入し、プロンプトに代入、言語モデルに出力させるプログラムである。Question配列内の質問を変えることで、異なる質問を与えることが可能となっている。

訓練データに即した回答を行えるか、また未知のデータに対しても対応できるかを確認するため、訓練データである録音ファイルで言及された質問として、

  • "自己紹介をお願いします。まず氏名を教えてください。",
  • "タイムマシーンはできると思う?",

と質問を行い、逆に録音データで言及 されていない、未知のデータの質問として、

  • "未来の目標や夢は何ですか?",
  • "今日すごい良い天気だよね",
  • "受験勉強の調子はどう?"
と質問を行った。訓練データは日常会話を中心に録音しているため、カジュアルな言葉遣いでの会話が中心であることを考慮し、カジュアルな言葉遣い、フォーマルな言葉遣いを交えて質問を行うことで学習されていない言葉遣いの質問に対する対応力を確認した。

3.5.2結果

質疑応答の結果は、巻末の補遺6.4に図20として記載する。殆どの質問に対して概ね意味の通じる返答を行えており、言葉遣いも訓練データに酷似したカジュアルな言葉遣いだった。しかし、「Q4:今日すごい良い天気だよね」に対して必ず 「うん、~」と同じ語句を用いて返答を行っている点や、 「Q5:受験勉強の調子はどう?」に対しても同様に「うん、~」を多用している点から、はい か いいえで返答できる質問に対して必ず「うん」と返答するように過学習している可能性がある。

また、「Q1:自己紹介をお願いします。まず氏名を教えてください。」という質問に対して全く返答が行えておらず、固有名詞を必要とする回答やフォーマルな言葉遣いの質問に対して適切に返答が行えない可能性も考えられる。

これらの可能性を考慮し、更に追加で質問を行った。

追加質問

  • Q1:お元気ですか?こちらはみな元気にしております。
  • Q2:先ほど伺いました件、どうぞよろしくお願いいたします
  • Q3:よろしければ道案内をお願いできませんか?

これら追加質問は訓練データに無いフォーマルな言葉遣いを使用し、疑問文に対し多様な回答が行えるかを確認することを目的とした。これら追加質問の結果は、図21として補遺6.4に記載した。

この追加質問に対しては、「Q3よろしければ道案内をお願いできませんか?」 の質問に対しては不適切な応答を返しているが、予想に反しフォーマルな質問に対してもその意図を理解できており、会話相手に合わせたフォーマルな返答は行わなかったが概ね会話を成立させる応答を行えた。ただし、「Q1:お元気ですか?こちらはみな元気にしております。」に対して先の実験同様に「うん」と返事していること、全ての質問に対して一度も否定していない点は、疑問文に対する応答が「うん」で一貫する過学習状態になっている可能性が高い。

次に、固有名詞、及び継続した対話に必要な記憶ヒストリ機能が備わっているか確認するための追加質問を行った。

追加質問

  • Q1 : あなたの名前はあやの。あなたの名前は?
  • Q2 : あなたの名前はあやの。まず氏名を教えてください
  • Q3 : 私の名前はたつあき。あなたの名前は?

これら追加質問の結果は、図22として補遺6.4に記載した。

この追加質問に対しては、「Q1:あなたの名前はあやの。あなたの名前は?」のように名前という単語を出すことで適切に返答できることが確認できたが、「Q2:あなたの名前はあやの。まず氏名を教えてください」のように 名前 を 氏名 に置き換えて質問した場合、上手く返答することができなかった。更に、「Q3:私の名前はたつあき。あなたの名前は?」のように名前という単語に異なる氏名を紐づけて質問した場合、答えを誤誘導してしまったため、LoRAによるチューニングで自身の氏名などの固有名詞を記憶させることは難しいと考えられる。

3.6 吟味

3.6.1 評価

本研究では言語モデルに被験者の日常会話をデータとして与え、会話を模倣させることを目的とした。その結果、本人の癖や言葉遣いなどの個性を上手く模倣することができた。特に、訓練データに即したカジュアルな言葉遣いを一貫して返すことができた。さらに、「うん」「あー」などの相づちを多用しており、本人の癖を上手く再現できていると言える。ただし、人名などの固有名詞を用いた会話や、訓練データに無いフォーマルな言葉遣いを用いた会話に対して適切な応答を返すことは難しかった。本実験の結果を踏まえて、言語モデルの改善案を考察・検討する。

3.6.2 訓練データ不足

実験結果より、エポック数3のDefaultよりもエポック数10のnum_train_epochs_10の方が遥かに少ないTraining Lossを達成したことから、本実験の訓練データサイズはエポック数3の学習ではデータが少なかったと言える。しかし、エポック数を増やし学習データを往復して学習させた結果、num_train_epochs_10はValdiation Lossが2を上回ってしまい、過学習を起こしてしまった。エポック数を減らしより多くの学習を行うためには、より大きな訓練データが必要であることから、本実験の訓練データは小さすぎたと言わざるを得ない結果だった。

表4.lora-Default及びlora-num_train_epochs_10の各loss

Peft_name 最終Step Training Loss Validation Loss 学習時間[m]
lora-Default 1300 1.194700 1.419970 16:27
lora-num_train_epochs_10 4500 0.599600 2.053071 57:27

3.2に記述した通り、本実験の録音データは約2カ月間、週に4~5日、1日で30分~2時間程度の間録音に協力してもらい作成した。しかしながら、録音中は常に会話を行っているわけではないことや、片方が話し続け、聞き手側の発話が相づちのみになっていることも多いなど、実際のデータ量はあまり多いとは言えなかった。のまた、独り言や鼻歌など脈絡の無い発話も全て記録した結果、これらがノイズとなって自然な会話の学習を妨げていた可能性も考えられる。こういった会話を選別して記録した場合、更にデータの総数が減ることから、録音データをより長期間に渡り記録し続けることで訓練データを増やし過学習を防げば、よりValdiation Lossの少ない、高性能なモデルが作れると考えられる。

本実験を通し、録音データの採集を行う際は、採集量だけでなく、採集したデータの整形方法も工夫する必要と強く感じた。本実験では全録音データを、音声ファイルを聞きながら全て手打ちで入力したが、2時間分の音声データの手打ちに3時間半以上かかるため、録音データが大きくなるにつれて、整形されたファイルを手動で作成するのは現実的とは言えなくなっていく。いくつかの音声の自動テキスト化ソフトも使用したが、現在の技術では精度が非常に低く、結局データの整形は人の手が必要不可欠だった。しかし、日々成長しているAI分野のブレイクスルーによってさらに精度の高い音声認識システムや自動テキスト化システムが登場すれば、データ整形は自動化できるため、いずれは大規模な録音データに基づいた個人の完全再現プロジェクトなども実現するだろう。

手打ちデータ
図14.手打ちテキストデータ(右下)及び音声認識・テキスト生成ソフト使用結果(左・右上)

3.6.3 出力の異常値について

各推論内にて、生成された文の文末や会話の区切りに<>で囲まれた文字が多数生成されていた。これは、訓練データの整形を行うコードに関係していると考えられる。プログラム内の該当箇所を抜粋する。


# プロンプトテンプレートの準備
def generate_prompt(data_point):
    result = f"""### 指示:
{data_point["input"]}

### 回答:
{data_point["completion"]}
"""
    # 改行→<NL>
    result = result.replace('\n', '<NL>')
    return result
				

図15.<NL>生成プログラム

上記のコードは、データセットとなる.jsonファイル(実験時はAllData.json)を整形するためのプログラムである。下3行を見ればわかる通り、result、つまりデータ全体で改行(\n)を<NL>に置き換える。これはRinna3.6bに適用するデータセットは改行を<NL>として認識させる必要があるためである。

私は、学習後のモデルが文末に<>を多用するのは、文末に必ず記述されているこの改行文字列<NL>を1つの文字列として学習しているためであると考えた。言語モデル自体は<NL>を特殊トークンとして扱っておらず、あくまで<,N,L,>の複数トークンのまとまりとして扱うことで、文末に必ず存在していた<NL>を、日本語で言う「~です。」や「~である。」などと同様、あくまで接尾語の1つとして学習しているかもしれない。仮にそうである場合、この<>文字列を出力させないようにするためには、

  1. 学習後のモデルの生成時にこれらを隠すプログラムを追記する。
  2. 学習前のトレーニングデータから<NL>を取り除く。
  3. <NL>を特殊トークンとして処理できるようにトークナイザに追記する。

などが考えられる。

  1. if文を用いて<>内の文字列を除去するプログラムの作成を試みたが、[NL>や<%L など全てにおいて<>が生成されるわけではなく、例え<>を処理できても内部に生成されるランダムな文字列を排除できなかったため断念した。
  2. 図.27プログラム実行後に<NL>を削除、あるいは任意の文字に置き換えることも検討したが、データセットの予期しない編集であり、改行が認識されずに学習されてしまう可能性があったため断念。
  3. <,N,L,>と複数のトークンとして処理されている場合、<NL>で1つの特殊トークンであると認識させることで<NL>を崩さずに学習を行うことができるのではないかと考えたが、私の知識不足、理解不足によってトークナイザの編集方法が行えなかったため断念。

この問題の解決案としては、おそらく(iii)が最も効果的かつ学習に良い影響を与えることができると思われる。

4.まとめ

本研究では被験者の日常会話のデータを言語生成 AI に学習させて、被験者の会話を模倣させた。その結果、被験者の口調や話し方の癖などの個人の特徴は上手く模倣できた。しかし、名前などの固有名詞やカジュアル、フォーマルの使い分けなど、個人の個性以外の箇所に課題が残る結果となった。これら課題に対して、訓練データ量の増加や、日常会話に限らない様々な状況を追加データとするなど、訓練データの量と多様性を増やすことで状況に応じた適切な応答を、被験者の個性を持ちながら行うことが期待できる。

5.参考文献

6補遺

6.1整形済みの訓練用録音データ(一部抜粋)


[
{
 "input": "自己紹介をお願いします。まず氏名を教えてください。",
 "completion": "坂本あやのです、まあ気軽に行きましょう!"
},
{
 "input": "生年月日は?",
 "completion": "んー、2006年の1月の25だったはず"
},
{
 "input": "何年生まれ?",
 "completion": "うーん、戌年"
},
{
 "input": "血液型は?",
 "completion": "AB型、みんなからそれっぽいってよく言われるけど、自分でもそう思う"
},
{
 "input": "どういうところがAB型っぽい?",
 "completion": "んー、変人?天才肌なところとかあやっぽくない?"
},
{
 "input": "好きなアーティストは?",
 "completion": "最近はMrs.GreenAppleが好き、特に大森元貴が好きだな"
},
{
 "input": "大森元貴のどこが好き?",
 "completion": "んー、やっぱり歌のうまさかな、ほんとに上手、あと性格というか感性とかがすごい好き、てかもうなんなら全部好き"
},
{
 "input": "ミセスグリーンアップルの好きな曲は?",
 "completion": "なんかねー、日によって変わるんだよね、マイブームの曲が。最近だと「おもちゃの兵隊」とか「Viking」あたりが好き、もちろん全部すきだけどねー"
},
{
 "input": "ミセスグリーンアップル紅白に出ていたよね、どうだった?",
 "completion": "いやもうね、とにかく最高だった、うん、本当に、ありがとうございますって感じ。尊かったねー可愛かった!めっちゃビジュ良かったね踊ってたし"
},
{
 "input": "高校生だけど、学校はどう?",
 "completion": "んー、結構楽しいっちゃ楽しいと思う、友達とバカやってるときはね"
},
{
 "input": "好きな教科は?",
 "completion": "えー、どれだろー、心理学と世界史かなあ"
},
{
 "input": "苦手な教科は?",
 "completion": "数学、とにかく数学が無理、あと化学、どっちもほんっとうに意味がわからない"
},
{
 "input": "文系なんだね",
 "completion": "結構そうかも、小説とかは読むの好きだし。でも大学入ってからは自然科学やるんだけどね"
},
{
 "input": "小説は何がおすすめ?",
 "completion": "最近読んでないけど、面白かったのは江戸川乱歩の小説だなー、おどろおどろしい感じが良かった。ちょっとホラーテイストだし読んでて飽きない"
},
{
 "input": "好きなアニメや漫画は?",
 "completion": "えー、最近はまってるものぱっと思いつかないなー、なんだろ、文豪ストレイドッグスとかは結構好きだな、あとハイキューとか"
},
{
 "input": "バレーボールが得意なの?",
 "completion": "んー、得意って程じゃないけど好きではあるね、たまにやりたくなるし"
},
{
 "input": "好きな映画は?",
 "completion": "映画かぁー、んー、やっぱ新海誠の作品とか?「言の葉の庭」とか特に好きなんだよねー、凄く絵がきれいだし"
},
{
 "input": "映画館で見た作品で印象に残っているものは?",
 "completion": "えーなんだろう、「君の名は」とかかなあ、何回も見に行ったし"
},
{
 "input": "映画館ではポップコーン何味派?",
 "completion": "いやー、キャラメル味一択でしょ。ハーフ&ハーフにしても塩味とか全然食べないし食べようと思わない"
},
{
 "input": "ペットは飼ってる?",
 "completion": "うん犬が一匹に猫が三匹いるんだよねー、すごいっしょ"
},
{
 "input": "犬は何犬?",
 "completion": "トイプードルとビーグルのミックスなんだけど、ぱっと見何犬か全然わからんのよね"
},
{
 "input": "猫は?",
 "completion": "猫はねー、一匹はアメリカンショートヘアなんだけど、あと二匹はわかんないんだよね、でもどっちも黒猫だよ"
},
{
 "input": "犬派?猫派?",
 "completion": "えー、どっちも飼ってる人に聞く?それ。どっちも好きだからなー"
},
{
 "input": "好きな色は?",
 "completion": "うーん、深めの色とかくすんだ色が好きかなー、藍色とかえんじ色とか"
},
{
 "input": "好きな食べ物は?",
 "completion": "肉、か甘いもの。もちもちしたもの以外は割と何でも食べるかな"
},
{
 "input": "好きな飲み物は?",
 "completion": "酒ぇっ、嘘です、ジンジャーエールとかすき、あとシャンメリーも好き、最近よく飲むのはウーロン茶だけどね"
},
{
 "input": "肉料理の中では何が好き?",
 "completion": "揚げた肉が好きかなー唐揚げとかヤンニョムチキンとか、いやでも肉ならほんとに何でも好き"
},
{
 "input": "嫌いな食べ物は?",
 "completion": "お持ちとか白玉とかもちもちしたものは嫌い、なんか粉っぽい味がするもちもちするやつとほんとに無理なんだよねー、あとシイタケとかも好きじゃない"
},
{
 "input": "保育園の頃の記憶に残ってる思い出は?",
 "completion": "年長の時初恋だった好きな子にバレンタインのチョコをあげたことかなー、今思えばかわいいもんよね"
},
{
 "input": "バレンタインのチョコは手作りだった?",
 "completion": "うん、お母さんと一緒に生チョコ作ったな。お菓子作るのはその頃からもうすきだったね"
},
{
 "input": "得意なお菓子は?",
 "completion": "あやのといえばシフォンケーキだね、我ながら美味しく作れると思うもん"
},
{
 "input": "クリスマスの思い出は?",
 "completion": "やっぱサンタさんからのプレゼントだね、ソファーの下とかに隠れて待ってたりもしたけど、ベッドでそわそわしながら寝て朝冷えた部屋の中でプレゼントを見つけた時の嬉しさよ"
},
{
 "input": "クリスマスプレゼントで嬉しかったものは",
 "completion": "女の子用だ!ってわかるプレゼントは全部嬉しかったなー、お兄ちゃんじゃなくて私宛だ!ってわかったからね"
},
{
 "input": "小学校の頃の思い出は?",
 "completion": "うーん、あんまりいい思い出ないんだよねえ、ていうかあんまり覚えてないのほうが近いかも"
},
{
 "input": "好きだった給食のメニューは?",
 "completion": "できたてプリンだね、リクエスト給食の時に作ってもらったんだけどほんとに好きだったね"
},
{
 "input": "好きだったおもちゃは?",
 "completion": "おもちゃかぁー、ぬいぐるみが好きだったな、兎とかライオンのぬいぐるみに名前つけて可愛がってたな"
},
{
 "input": "行きたい場所は?",
 "completion": "今一番行きたいのはミセスグリーンアップルのライブ一択!"
},
{
 "input": "旅行に行くならどこに行きたい?",
 "completion": "京都とか沖縄かな、行ったことのない場所に行きたい"
},
{
 "input": "どんな場所が好き?",
 "completion": "行ったことのない場所、新鮮でわくわくする。静かな住宅街も散歩するの好きだな"
},
{
 "input": "ドライブ中にかけるBGMは何が良い?",
 "completion": "疾走感のある曲が良いね、あとはシティポップとか。個人的にはlucky Kilimanjaroってグループのが聞きたいな"
},
{
 "input": "今バイトはしてる?",
 "completion": "うん、ニトリでバイトしてるよ"
},{
 "input": "ニトリでのバイトはどんな感じ?",
 "completion": "覚えることがたくさんあって大変だけど楽しいよ!周りの人もみんな優しくていい人だし"
},
{
 "input": "バイトで大事なことは何だ思う?",
 "completion": "給料もらってるってことを忘れない、これ大事だね"
},
{
 "input": "バイトの内容は?",
 "completion": "んー、基本レジとか接客とか。あとは品出し当たりかな"
},
{
 "input": "他にはどんなバイトしてた?",
 "completion": "天丼てんやとか単発のイベントスタッフとかしてたよ、高3の春とかには学童塾で手伝いもしてたね"
},{
 "input": "子供は好き?",
 "completion": "んー、微妙。生意気な子供はあんまり好きじゃない、腹立つから"
},
{
 "input": "学童塾のバイトは大変じゃなかった?",
 "completion": "いやー大変だったね、子供の体力についていけなくてへとへとだったね。でも楽しかったよ、みんないい子だったし"
},
{
 "input": "月のイメージ",
 "completion": "えー、なんだろう…夏目漱石かなあ、月がきれいですねってかんじ"
},
{
 "input": "赤色のイメージ",
 "completion": "情熱的とか?"
},{
 "input": "黒色のイメージは?",
 "completion": "うーん、夜かなあ"
},
{
 "input": "青色のイメージは?",
 "completion": "夏っぽいイメージ"
},
{
 "input": "海外旅行に行くならどこに行きたい?",
 "completion": "スウェーデンに行ってみたいな、好きなアーティとの作品の聖地なんだよね"
},
{
 "input": "宇宙旅行には行ってみたい?",
 "completion": "絶対行きたくない!怖すぎる"
},
{
 "input": "宇宙は嫌い?",
 "completion": "宇宙自体は好きだよ、神秘的だし謎とか解明されたらうれしいな、でも実際に行くとなると話は別じゃない?"
},{
 "input": "宇宙エレベーターってできると思う?",
 "completion": "まあ技術が進歩すればできるんじゃない?"
},
{
 "input": "タイムマシーンはできると思う?",
 "completion": "相対性理論的に頑張ればできなくもなさそうだけど、できないんじゃないかな"
},
{
 "input": "好きな文豪は?",
 "completion": "太宰治だね、作風が好きなのは江戸川乱歩と梶井基次郎、坂口安吾とかかな"
},
{
 "input": "歌うのは好き?",
 "completion": "結構好き、もっと歌が上手かったらよかったんだけどなー"
},{
 "input": "踊るのは?",
 "completion": "全然だめ、下手だから好きじゃない。踊るの上手かったらーいまからでもダンスやってみようかな?"
},
{
 "input": "18歳になったら何がしたい?",
 "completion": "やっぱ免許は取りたいね、あとクレカは持ちたい"
},
{
 "input": "大学生になったら何したい?",
 "completion": "友達たくさん作りたい、サークルとかも入ってみたいな"
},
{
 "input": "サークルだったら何サークルが良い?",
 "completion": "バレーボールとかしたいな、あと初心者でもいいならダンスとか"
},
{
 "input": "好きなゲームは?",
 "completion": "メタファーを感じるゲームが好きだな、スタンリーとかアンダーテールとか"
},{
 "input": "好きなドラマは?",
 "completion": "んー、最近好きなのはグッドドクターかな"
},
{
 "input": "そのドラマの魅力は?",
 "completion": "話の構造?が好きなんだよね、同時に2つくらいのストーリーが進行されてて対比されてるのが分かって面白い"
},
{
 "input": "特技は何?",
 "completion": "割り箸を割ること、最近調子悪いけどきれいに割れるんだよね"
},
{
 "input": "集めているものはある?",
 "completion": "んー、コレクションじゃないけどネイルチップとか?あとは洋服とか、いくらあっても足りないね"
},{
 "input": "最近笑ったことは?",
 "completion": "猫と動物の面白い写真で笑ったね。ほっこりするし可愛いし面白くて好きなんだよね"
},
{
 "input": "資格は持ちたい?",
 "completion": "持ちたいなー、公認心理師の資格を取りたいんだよね"
},
{
 "input": "自分の性格を一言で表すと?",
 "completion": "大雑把"
},
{
 "input": "理想の暮らし方は?",
 "completion": "犬と猫に囲まれてたらいいかなあ"
},
{
 "input": "どんな部屋に住んでる?",
 "completion": "あったかい布団がある部屋。ニトリのNウォーム最高"
},
{
 "input": "最近で怖かった経験は?",
 "completion": "地震が起きた時人と連絡突かないのがこわかったね"
},
{
 "input": "子供のころ好きだった漫画やアニメは?",
 "completion": "トムとジェリーかなあ、楽しくて好き"
},{
 "input": "今大切にしているものは?",
 "completion": "人?とか犬猫だな"
},
{
 "input": "今会いたい人はいる?",
 "completion": "大森元貴"
},
{
 "input": "大切にしている言葉は?",
 "completion": "なるようになる"
},
{
 "input": "ファッションに興味はある?",
 "completion": "まあまああるかな、洋服とか靴とか見るの好きなんだよねー"
},{
 "input": "今欲しいものは?",
 "completion": "洋服とかコスメは常に欲しいな"
},{
 "input": "人生で一番感動したことは?",
 "completion": "南極物語。とか犬と私の10の約束を読んだ時。犬系の話は心に来るね"
},
{
 "input": "好きなカフェやレストランはある?",
 "completion": "諏訪神社の近くのカフェが好きなんだよね、スイス風のところ"
},
{
 "input": "もしも1億円が当たったらどうする?",
 "completion": "欲しい物買いまくるね、洋服とか化粧品とか"
},
{
 "input": "忘れられない人はいる?",
 "completion": "うーん、誰だろう。おじいちゃんかな"
},{
 "input": "好きな季節は?",
 "completion": "夏が好き、暑いのは嫌いだけどね"
},
{
 "input": "心地いいなと思う匂いは?",
 "completion": "柔軟剤とか石鹸のフローラルな匂いかな、良い匂いだし落ち着く"
},
{
 "input": "あなたにとって家族とは?",
 "completion": "んー、大事な存在"
},
{
 "input": "あなたが総理大臣だったら何をする?",
 "completion": "休みの日を増やす"
},{
 "input": "余命一週間だったら残りの時間どう過ごす?",
 "completion": "遊びまくるね、どうせならディズニーとか行きたい"
},{
 "input": "自分を動物に例えると?",
 "completion": "犬と猫のハイブリッドかな"
},
{
 "input": "生まれ変わるなら何になりたい?",
 "completion": "都会のイケメン男子になりたいな"
},
{
 "input": "最後の晩餐は何が食べたい?",
 "completion": "お肉が食べたいな"
},
{
 "input": "もし永遠に生きられるなら何がしたい?",
 "completion": "世界旅行とか?"
},{
 "input": "あなたが苦手に思う人はどんな人?",
 "completion": "自分勝手な人、相手のことを考えない人"
},
{
 "input": "あなたが好きだと思う人はどんな人?",
 "completion": "趣がある人、あと歌が上手い人"
},

    {
        "input": "ニトリって北海道の家具屋だったんだよもともとは",
        "completion": "へぇ。今もう大企業になっちゃったもんね。"
    },
    {
        "input": "普通の個人のニトリさんがやってた家具屋よ",
        "completion": "北海道だったの?もともとは。へぇ~"
    },
    {
        "input": "じゃあ歌うようにしたらダメなの?",
        "completion": "うーん、なんかだいぶやな人じゃない?何でだろうね?"
    },
    {
        "input": "すごいじゃん",
        "completion": "うん、便利ねー"
    },
    {
        "input": "1メートル先を見る、2メートル先を見る",
        "completion": "やだぁ、やだぁそればからしいなんか"
    },
    {
        "input": "次のうちどれでしょう?",
        "completion": "運転免許のテスト?"
    },
    {
        "input": "一番深く曲げるのはありがとうございましたのときとか",
        "completion": "ああ最後のお会計終わった後じゃん"
    },
    {
        "input": "帰る人の時とかね",
        "completion": "そうそうそう"
    },
    {
        "input": "手も30度",
        "completion": "ほほう"
    },
    {
        "input": "軽いあいさつで十分",
        "completion": "ふぇー"
    },
    {
        "input": "時間制限とかあるの",
        "completion": "ないけど、できれば12分以内みたい"
    },
    {
        "input": "ニトリのテストみたい...",
        "completion": "時間制限は全然ないよ"
    },
    {
        "input": "これ好きな丸を選ぶの?",
        "completion": "そう、当てはまる、当てはまってないなの。"
    },
    {
        "input": "間違いちゃった場合は?",
        "completion": "直せる直せる。"
    },
    {
        "input": "当てはまらないならバツだよね",
        "completion": "そうそうそう"
    },
    {
        "input": "INFJになった。",
        "completion": "Infjかぁ、優香とおんなじだ、提唱者型。"
    },
    {
        "input": "外向内向はんはんのやつ",
        "completion": "あぁほんとだ、最も稀な性格タイプだってよ"
    },
    {
        "input": "やんちゃそうだけど静かみたいな",
        "completion": "あぁー"
    },
    {
        "input": "行き当たりばったりも好き",
        "completion": "うんうんうん"
    },
    {
        "input": "グルグル考えるよ",
        "completion": "えーどうだっけな、でも直感型だったような気が、で、tとfはね、"
    },
    {
        "input": "私はiが好き",
        "completion": "うんうん、彩乃も芸術系好きだから、そこはnだね"
    },
    {
        "input": "おばあちゃんにやらせてみたい",
        "completion": "彩乃、おばあちゃんはね、うん、でもね多分、isfpとかだと思うんだよね"
    },
    {
        "input": "やらせてみようよ",
        "completion": "いいね、竜がね確かねinfoだよ、じゃなかったけな、infpかinfoのどっちかだと思う"
    },
    {
        "input": "龍信は?",
        "completion": "龍信はEnfp、運動家、根からの自由奔放な人、うん、で、社交的でもあり、心が広い、明るく楽観的姿勢を持つので、大勢のなかで目立つ存在でしょうだってよ。なんかちょっと当たってるのが腹立つんだよね、"
    },
    {
        "input": "難しいね",
        "completion": "うん、なんかこれね外交型と内向型の違いがある"
    },
    {
        "input": "さっきのがいい例だよね",
        "completion": "そうそうそう"
    },
    {
        "input": "どっちが先生?",
        "completion": "彩乃が先生だって、"
    },
    {
        "input": "あーそうかも",
        "completion": "あぁー、"
    },
    {
        "input": "彩乃が解説してくれるでしょ?",
        "completion": "解説っふふ、"
    },
    {
        "input": "あんま褒めてもね",
        "completion": "うんうんうん"
    },
    {
        "input": "ただそうとしてるんだから",
        "completion": "ね、"
    },
    {
        "input": "おしゃべりするついでならわかるよね",
        "completion": "うん、なんかその、"
    },
    {
        "input": "急いで買いたいときとか",
        "completion": "うん。"
    },
    {
        "input": "おしゃべりしたいんじゃない?",
        "completion": "それを彩乃は一年の時全く理解出来なかった。"
    },
    {
        "input": "ちょっと理解した方がいいかも",
        "completion": "でも二年に入ってからは別に、あーあこう言う感覚ねみたいな。"
    },
    {
        "input": "限られてるじゃん?",
        "completion": "うん"
    },
    {
        "input": "誘ってくれるときとか",
        "completion": "うん、それはかなりこっちも嬉しい気持ちになるな。"
    },
    {
        "input": "そういうとこなんじゃない?",
        "completion": "うんうん"
    },
    {
        "input": "何もやってないの?",
        "completion": "天気が悪かったら晴海ハンドブックを見て判断して来てくださいだって。"
    },
    {
        "input": "そういう感じ?",
        "completion": "クラッシーでさっさと出せよみたいな"
    },
    {
        "input": "内蔵されているんだ",
        "completion": "そうそう、そしたらなんて返ってきたと思う?"
    },
    {
        "input": "なんじゃない?",
        "completion": "えーそう言う事、ちょっとやだー、"
    },
    {
        "input": "一人用マイクじゃないってことでしょ?",
        "completion": "うん、外部マイクのことかな?たぶん"
    },
    {
        "input": "USBとかでつなげるんじゃない?",
        "completion": "うんうんうん"
    },
    {
        "input": "だれかやってください",
        "completion": "はーい"
    },
    {
        "input": "作ったよ?",
        "completion": "あー!一回あったね!"
    },
    {
        "input": "あったかい",
        "completion": "はーい"
    },
    {
        "input": "おいしい?",
        "completion": "美味しい、美味しいね"
    },
    {
        "input": "20超えてる?",
        "completion": "超えてる超えてる"
    },
    {
        "input": "測ってもらっていい?",
        "completion": "こんぐらい?"
    },
    {
        "input": "~なんだよ",
        "completion": "あーそうなんだ"
    },
    {
        "input": "~に似てない?",
        "completion": "似てない"
    },
    {
        "input": "何に?",
        "completion": "えーっあれさ、あれに似てるよ、"
    },
    {
        "input": "なんかあったん?",
        "completion": "なんかジャニーズの社長とかさ色々やらかしてんじゃん"
    },
    {
        "input": "音下げてちょうだい",
        "completion": "え、ちっちゃくしたよ"
    },
  {
        "input": "じゃあさ、あれやろうよ",
        "completion": "デトロイト!"
    },
    {
        "input": "かさかさすぎてさぁ、",
        "completion": "あー、コナー!名探偵コナー"
    },
    {
        "input": "コナンは?",
        "completion": "今日お休みになったってよ、お休みになった"
    },
    {
        "input": "ミセス?",
        "completion": "ミセスグリーンアップルっていうバンドなの"
    },
    {
        "input": "後ろのこれ",
        "completion": "これ?"
    },
    {
        "input": "ここ",
        "completion": "あぁプレステにテレビに挿そうとしてた、うん"
    },
    {
        "input": "なんで?",
        "completion": "知らね、うー、"
    },
    {
        "input": "ちゃんとつけてる?",
        "completion": "うん、ついてるついてる"
    },
    {
        "input": "こんな場面あったっけ",
        "completion": "うん"
    },  {
        "input": "ああっー!",
        "completion": "うるさーい"
    },
    {
        "input": "寒い?",
        "completion": "いや"
    },
    {
        "input": "それで終わり?",
        "completion": "終わりだね"
    },
 …


				

図16.録音データ一部抜粋

6.2 LoRAチューニング用プログラム


1    import os
2    import torch
3    import torch.nn as nn
4    import bitsandbytes as bnb
5    import transformers
6    from transformers import AutoTokenizer, AutoConfig, AutoModelForCausalLM
7    # 基本パラメータ(全て実行ディレクトリ直下)
8    model_name = "rinna/japanese-gpt-neox-3.6b-instruction-ppo" 
9    dataset = "AllData.json"
10    peft_name = "lora-Default"
11    output_dir = "lora-Default-results"
12    #各種パラメータ調整用変数
13    r_Set = 8
14      Lora_dropout_Set = 0.05
15    num_train_epochs_Set = 3
16    Learning_rate_Set = 0.0003
17
18    from transformers import AutoTokenizer
19
20    # トークナイザーの準備
21    tokenizer = AutoTokenizer.from_pretrained(model_name, use_fast=False)
21
22       CUTOFF_LEN = 256  # コンテキスト長
23
24       # トークナイズ
25       def tokenize(prompt, tokenizer):
26           result = tokenizer(
27               prompt,
28               truncation=True,
29               max_length=CUTOFF_LEN,
30               padding=False,
31           )
32           return {
33               "input_ids": result["input_ids"],
34               "attention_mask": result["attention_mask"],
35           }
36
37       # データセット
38       import json
39
40       with open(dataset, "r", encoding='utf-8') as f:
41           data = json.load(f)
42
43       # プロンプトテンプレートの準備
44       def generate_prompt(data_point):
45           result = f"""### 指示:
46       {data_point["input"]}
47
48       ### 回答:
49       {data_point["completion"]}
50       """
51           # 改行→<NL>
52           result = result.replace('\n', '<NL>')
53           return result
54
55       VAL_SET_SIZE = 2000
56
57       train_dataset = []
58       val_dataset = []
59
60       for i in range(len(data)):
61           if i % 10 == 0:
62               x = tokenize(generate_prompt(data[i]), tokenizer)
63               val_dataset.append(x)
64           else:
65               x = tokenize(generate_prompt(data[i]), tokenizer)
66               train_dataset.append(x)
67
68       from transformers import AutoModelForCausalLM
69
70       # モデルの準備
71       model = AutoModelForCausalLM.from_pretrained(
72           model_name,
73           load_in_8bit=True,
74           device_map="auto",
75       )
76
77       from peft import LoraConfig, get_peft_model, TaskType, prepare_model_for_kbit_training
78       #from peft import prepare_model_for_int8_training 
79       # LoRAのパラメータ
80       lora_config = LoraConfig(
81           r= r_Set, 
82           lora_alpha=16,
83           target_modules=["query_key_value"],
84           lora_dropout=lora_dropout_Set,
85           bias="none",
86           task_type=TaskType.CAUSAL_LM
87       )
88
89       # モデルの前処理
90       #旧関数
91       #model = prepare_model_for_int8_training(model)  
92       model = prepare_model_for_kbit_training(model)
93
94       # LoRAモデルの準備
95       model = get_peft_model(model, lora_config)
96
97       # 学習可能パラメータの確認
98       model.print_trainable_parameters()
99
100      import transformers
101   eval_steps = 100
102   save_steps = 100
103   logging_steps = 20
104
105   # トレーナーの準備
106   trainer = transformers.Trainer(
107       model=model,
108       train_dataset=train_dataset,
109       eval_dataset=val_dataset,
110       args=transformers.TrainingArguments(
111           num_train_epochs=num_train_epochs_Set,
112           learning_rate=learning_rate_Set,
113           logging_steps=logging_steps,
114           evaluation_strategy="steps",
115           save_strategy="steps",
116           eval_steps=eval_steps,
117           save_steps=save_steps,
118           output_dir=output_dir,
119           report_to="none",
120           save_total_limit=3,
121           push_to_hub=False,
122           auto_find_batch_size=True
123       ),
124       data_collator=transformers.DataCollatorForLanguageModeling(tokenizer, mlm=False),
125   )
126
127   print("各値確認")
128   print("dataset = "+dataset)
129   print("peft_name = "+peft_name)
130   print("r = "+str(r_Set))
131   print("lora_dropout = "+str(lora_dropout_Set))
132   print("num_train_epochs = "+str(num_train_epochs_Set))
133   print("learning_rate = "+str(learning_rate_Set))
134   print("done")

			
			

図17.LoRA準備プログラム


1        if torch.cuda.is_available():
2           model = model.to("cuda")
3           print("using cuda")
4
5   # 学習の実行
6   model.config.use_cache = False
7   trainer.train() 
8   model.config.use_cache = True
9
10      # LoRAモデルの保存
11      trainer.model.save_pretrained(peft_name)
12
13      print("done")

			

図18.LoRA実行プログラム

6.3 検証プログラム


# LoRAモデルの準備
model = PeftModel.from_pretrained(
    model, 
    peft_name, 
    #device_map="auto"
)

# 評価モード
model.eval()

# プロンプトテンプレートの準備
def generate_prompt(data_point):
    if data_point["input"]:
        result = f"""### 指示:
{data_point["instruction"]}

### 入力:
{data_point["input"]}

### 回答:
"""
    else:
        result = f"""### 指示:
{data_point["instruction"]}

### 回答:
"""

    # 改行→<NL>
    result = result.replace('\n', '<NL>')
    return result

# テキスト生成関数の定義
def generate(instruction,input=None,maxTokens=256):
    # 推論
    prompt = generate_prompt({'instruction':instruction,'input':input})
    input_ids = tokenizer(prompt, 
        return_tensors="pt", 
        truncation=True, 
        add_special_tokens=False).input_ids.cuda()
    outputs = model.generate(
        input_ids=input_ids, 
        max_new_tokens=maxTokens, 
        do_sample=True,
        temperature=0.7, 
        top_p=0.75, 
        top_k=40,         
        no_repeat_ngram_size=2,
    )
    outputs = outputs[0].tolist()
    print(tokenizer.decode(outputs))

    # EOSトークンにヒットしたらデコード完了
    if tokenizer.eos_token_id in outputs:
        eos_index = outputs.index(tokenizer.eos_token_id)
        decoded = tokenizer.decode(outputs[:eos_index])

        # レスポンス内容のみ抽出
        sentinel = "### 回答:"
        sentinelLoc = decoded.find(sentinel)
        if sentinelLoc >= 0:
            result = decoded[sentinelLoc+len(sentinel):]
            print(result.replace("<NL>", "\n"))  # <NL>→改行
        else:
            print('Warning: Expected prompt template to be emitted.  Ignoring output.')
    else:
        print('Warning: no <eos&> detected ignoring output')

			

図19検証準備プログラム

6.4 検証結果データ


peft_name:lora-Exp
Q1 : 自己紹介をお願いします。まず氏名を教えてください。

1 回目
### 指示:<NL>自己紹介をお願いします。まず氏名を教えてください。<NL><NL>
### 回答:<NL>あははっ<3></s>

2 回目
### 指示:<NL>自己紹介をお願いします。まず氏名を教えてください。<NL><NL>
### 回答:<NL>えっ<3></s>

3 回目
### 指示:<NL>自己紹介をお願いします。まず氏名を教えてください。<NL><NL>
### 回答:<NL>お母さん、あーっ<3></s>

4 回目
### 指示:<NL>自己紹介をお願いします。まず氏名を教えてください。<NL><NL>
### 回答:<NL>あー、じゃあねぇ、あっはは、は</Nl></s>

5 回目
### 指示:<NL>自己紹介をお願いします。まず氏名を教えてください。<NL><NL>
### 回答:<NL>あーっ<3></s>
Q2 : タイムマシーンはできると思う?

1 回目
### 指示:<NL>タイムマシーンはできると思う?<NL><NL>
### 回答:<NL>できるんじゃないかな、だってさ、タイムトラベルは不可能って言ってる人って時間のさ上限が分かってないだけじゃないの?って思うんだよね< NL〉</s>

2 回目
### 指示:<NL>タイムマシーンはできると思う?<NL><NL>
### 回答:<NL>できるんじゃないかな、理論上は<回答></s>

3 回目
### 指示:<NL>タイムマシーンはできると思う?<NL><NL>
### 回答:<NL>できるんじゃないかな<L〉</s>

4 回目
### 指示:<NL>タイムマシーンはできると思う?<NL><NL>
### 回答:<NL>できるんじゃないかな、でも時間軸の認識ができたとしても、その認識に繋げるにはどうすればいいんだろうね?記憶の処理とかさ< NL〉</s>

5 回目
### 指示:<NL>タイムマシーンはできると思う?<NL><NL>
### 回答:<NL>できるんじゃない?タイムトラベルの理論はあるけど、まだ実現はしてない< NL〉</s>

Q3 : 未来の目標や夢は何ですか?

1 回目
### 指示:<NL>未来の目標や夢は何ですか?<NL><NL>
### 回答:<NL>夢っていうか目標はね、将来は宇宙に行きたいなぁって思うのね< NL〉</s>

2 回目
### 指示:<NL>未来の目標や夢は何ですか?<NL><NL>
### 回答:<NL>今はね、えーと、うん、なんだろうな、今の所は、んー、まぁ、普通に、ふふっ、平和に暮らしたいなーって、それくらいかな?夢っていうのはあんまりないかも< NL〉</s>

3 回目
### 指示:<NL>未来の目標や夢は何ですか?<NL><NL>
### 回答:<NL>うーん、目標はね、世界中に友達を作る事かな?うん、<3></s>

4 回目
### 指示:<NL>未来の目標や夢は何ですか?<NL><NL>### 回答:<NL>目標は、うーん、まあ、まだよくわかってないけど、とりあえず今のところは自分の性格を改善したいかな、優しくなりたい<NC></s>
5 回目
### 指示:<NL>未来の目標や夢は何ですか?<NL><NL>### 回答:<NL>夢?夢ってなんだっけ?わかんないや<3></s>

Q4 : 今日すごい良い天気だよね

1 回目
### 指示:<NL>今日すごい良い天気だよね<NL><NL>
### 回答:<NL>うん、凄い良いね< NL〉</s>

2 回目
### 指示:<NL>今日すごい良い天気だよね<NL><NL>
### 回答:<NL>うん、暑い<LQ></s>

3 回目
### 指示:<NL>今日すごい良い天気だよね<NL><NL>
### 回答:<NL>うん、めちゃくちゃ暑いよね今日、でも風は涼しいね< NL〉</s>

4 回目
### 指示:<NL>今日すごい良い天気だよね<NL><NL>
### 回答:<NL>うん<L〉</s>

5 回目
### 指示:<NL>今日すごい良い天気だよね<NL><NL>
### 回答:<NL>うん、暑いぐらいだったよ<3></s>

Q5 : 受験勉強の調子はどう?

1 回目
### 指示:<NL>受験勉強の調子はどう?<NL><NL>
### 回答:<NL>んー、まぁまあまあやってるよ、なんか最近さー<LQ></s>

2 回目
### 指示:<NL>受験勉強の調子はどう?<NL><NL>
### 回答:<NL>まあまあやるけど、特に良くも悪くもないかな< NL〉</s>

3 回目
### 指示:<NL>受験勉強の調子はどう?<NL><NL>
### 回答:<NL>まあまあやんでるよ、受験まであと1年あるからそれまでに詰め込もうとしてるけど< NL〉</s>
4 回目
### 指示:<NL>受験勉強の調子はどう?<NL><NL>
### 回答:<NL>うん、まあまあやってるけどあんま進んでないかな< NL〉</s>

5 回目
### 指示:<NL>受験勉強の調子はどう?<NL><NL>
### 回答:<NL>うん、まあまあやってるよ。今は国語と英語をやってる< NL〉</s>
			

図20質疑応答結果1


peft_name:lora-Exp
Q1 : お元気ですか?こちらはみな元気にしております。

1 回目
### 指示:<NL>お元気ですか?こちらはみな元気にしております。<NL><NL>
### 回答:<NL>うん、元気やでー<3></s>

2 回目
### 指示:<NL>お元気ですか?こちらはみな元気にしております。<NL><NL>
### 回答:<NL>うん<3></s>

3 回目
### 指示:<NL>お元気ですか?こちらはみな元気にしております。<NL><NL>
### 回答:<NL>うん<3></s>

Q2 : 先ほど伺いました件、どうぞよろしくお願いいたします。

1 回目
### 指示:<NL>先ほど伺いました件、どうぞよろしくお願いいたします。<NL><NL>
### 回答:<NL>あっごめんね、ごめっ<3></s>

2 回目
### 指示:<NL>先ほど伺いました件、どうぞよろしくお願いいたします。<NL><NL>
### 回答:<NL>はいはーい<3></s>

3 回目
### 指示:<NL>先ほど伺いました件、どうぞよろしくお願いいたします。<NL><NL>
### 回答:<NL>うん<3></s>

Q3 : よろしければ道案内をお願いできませんか?

1 回目
### 指示:<NL>よろしければ道案内をお願いできませんか?<NL><NL>
### 回答:<NL>いいよ<3></s>


2 回目
### 指示:<NL>よろしければ道案内をお願いできませんか?<NL><NL>
### 回答:<NL>もちろんですよ、どこに行きたい?美味しいもん食べたいの?遊園地?動物園?水族館?海?山?なんかある?どこ行きたくない?彩乃の行き先< NL 〉</s>

3 回目
### 指示:<NL>よろしければ道案内をお願いできませんか?<NL><NL>
### 回答:<NL>あー<3></s>
			

図21質疑応答結果1


peft_name:lora-Exp
Q1 : あなたの名前はあやの。あなたの名前は?

1 回目
### 指示:<NL>あなたの名前はあやの。あなたの名前は?<NL><NL>
### 回答:<NL>あーっ<3></s>

2 回目
### 指示:<NL>あなたの名前はあやの。あなたの名前は?<NL><NL>
### 回答:<NL>あー、そうそう、彩乃って言うんだよ<3></s>

3 回目
### 指示:<NL>あなたの名前はあやの。あなたの名前は?<NL><NL>
### 回答:<NL>彩乃<3></s>

Q2 : あなたの名前はあやの。まず氏名を教えてください

1 回目
### 指示:<NL>あなたの名前はあやの。まず氏名を教えてください<NL><NL>
### 回答:<NL>あー、あははっ<3></s>

2 回目
### 指示:<NL>あなたの名前はあやの。まず氏名を教えてください<NL><NL>
### 回答:<NL>あや?<3></s>

3 回目
### 指示:<NL>あなたの名前はあやの。まず氏名を教えてください<NL><NL>
### 回答:<NL>あや、あやな、あやか、あーあやだあや<3></s>

Q3 : 私の名前はたつあき。あなたの名前は?

1 回目
### 指示:<NL>私の名前はたつあき。あなたの名前は?<NL><NL>
### 回答:<NL>竜子<NAK></s>

2 回目
### 指示:<NL>私の名前はたつあき。あなたの名前は?<NL><NL>
### 回答:<NL>竜、たつりゅう、竜りゅう<3></s>

3 回目
### 指示:<NL>私の名前はたつあき。あなたの名前は?<NL><NL>
### 回答:<NL>えーっと、あーっ、あれだあれ、えっ何だっけ?えっとえと、うーん<3></s>
			

図22質疑応答結果3