😷

0.5BパラメータのLLMを一から作ろうとして心が折れかけた話

2024/10/14に公開

この記事について

今回Transformerを理解したいという目的でDecoder-OnlyのLLMをスクラッチで作ることにしました。

というのも、この約2年、新しいモデルが発表されなかった月はないほど目まぐるしく変化していくローカルLLMを追っていく中で、楽しさもある反面、よく分からないブラックボックスとして扱い続けるということにもやもやした気持ちを感じていました。

そこで自分もモデルを一から作ってみたらよくわかるんじゃないかな?という気持ちでこのプロジェクトをスタートしました。
既にローカルLLMのコミュニティでは一からモデルを作っている方は複数いるものの、今回は自身のTransformerに対する理解を深めることが目的ですから、なるべく頼らずにいこうと決めました。
(これが一番の過ちだったかもしれません...。)

かなり長くなってしまったので「まとめ」へのリンクを貼っておきます。
まとめだけ見たい方はこちら

一旦振り返りを行う前に、今回作成したモデルについて紹介します。

作成したモデル

https://huggingface.co/YukiTomita-CC/AKU-d_ms-0.5B-chat-v0.1

  • Architecture: Decoder-Only Transformer
  • Type: Mistral
  • Parameters: 0.5B(= 519M)
  • License: Apache-2.0

流れとしては事前学習 -> SFT -> DPOの順番に学習していきました。
それぞれのフェーズで使用したコードや学習データは下記のリポジトリに公開していますので、見たい方や何か聞きたいことがありましたらご連絡ください。

https://github.com/YukiTomita-CC/AKU
https://huggingface.co/datasets/YukiTomita-CC/AKU-d_ms-0.5B-v0.1_dataset

では1か月半、機械学習初心者が取り組んできた内容をまとめていきます。

0. どんなモデルを作るか?

まずはどんなモデルを作りたいかを決めました。

前提として、このプロジェクトの前に以下のceleryさんの記事で300MのMistralモデルを作るチュートリアルをしていました。
https://zenn.dev/selllous/articles/transformers_pretrain_to_ft
あまり大きく変更すると迷子になりそうなので、ここからちょびっと変えるくらいにしておきたいです。
なので、パラメータ数は500Mくらいを目指します。

でも500Mパラメータだと汎用性を求めるのは体感的に無理そうです。
3B、4Bくらいからぎりぎりかな?という感じがします。
そうなると何かに特化したいなー...。

Decoder-Onlyなので生成に関するタスクで、特化したものとなると単純に雑談なんてどうでしょうか。
ふつうに友達と話しているようなモデルがあれば楽しそうです。
モデルのキャラクターも一つに絞ってしまいましょう。
名前は「Aku」でフランクな口調がいいですね。

テーマが決まりました。

★現時点での精神的な体力: 100 / 100

1. 事前学習用コーパス作り

次に事前学習用のコーパスを準備します。

この時点で自分がよく見かけていたのは下記のコーパスです。

  • Wikipedia
  • C4
  • CC-100

Wikipediaはよく知っていますが、C4やCC-100にはあまりなじみがありません。
そこでC4の日本語抽出済みのリポジトリを見つけて中身をのぞいてみることにしました。
しかし、ブログのタイトルだったり、広告、よく分からない記号の羅列だったりと正直あんまりきれいではないなというのが印象でした。

これはそのまま使うと性能の低下につながり、クリーニングという前処理が重要とのこと。
調べてみると、重複した文章や、長すぎる文章を削除したり、正規化?したり特定の文字を置き換えたり...
ただでさえTransformerで精いっぱいなのにこれ以上よく分からない領域を増やしたくないというのが正直なところです。
今回はWikipediaだけにしましょう。Wikipediaは幸いきれいな文章が大半ですし、不要な部分をカットする処理(注釈以下を削除するくらい)は自分でも理解できます。

しかし、会話特化モデルを作りたいのにWikipediaだけ学習しては硬い文章になってしまいそうです。
そもそもWikipediaの中には会話例なんてそうそう入っていないでしょうから学習しようがない気もします。
そこで商用可能なライセンスで公開されている会話コーパスを探してみることにしました。
そうすると下記のリンクにまとまっているではありませんか!
https://individuality.jp/dialogue_corpus.html
ここからいくつか比較的日本語がきれいでライセンス的に問題のないものを選び、事前学習コーパスに追加していきます。

ここで、「事後学習(ファインチューニング)は事前学習で獲得した知識を抽出するものであり、事前学習で獲得していない知識を事後学習で獲得することはできない」という文脈の投稿を見かけたことがあります。
これに従えば、最終的なchatモデルとしてのプロンプトフォーマットに整形した会話コーパスを含めた方がよさそうです。
となると、先にプロンプトフォーマットを考えないといけないですね。

プロンプトフォーマット

なぜプロンプトフォーマットが必要なのでしょうか。

分かったような口のきき方ですが、当方なにも理解しておりません。

今の私が類推するに、モデルが入力に対して何に注目すべきかが分かりやすい方がいいでしょうから、ただの文章じゃなくて構造化してあげましょうということなんだと思います。

では、モデルが注目しやすい構造ってなんでしょうか。
今回の「会話」というケースでは、どういう話の流れなのかというのが分かるといいのかな?
王道ですが、
Aの発言
Bの発言
Aの発言
Bの発言
...
という風になっていた方が流れが時系列なので分かりやすいです。

これだけだとそれぞれの発言を区切るものが改行だけですからもう少し構造化したいです。
A: Aの発言
B: Bの発言
A: Aの発言
B: Bの発言
...

うーん、もっとUserの会話ブロック、Akuの会話ブロックという風にできないだろうか。。

ということでLlama2や既存のチャットテンプレートを参考に最終的に以下の形になりました。
<ROLE></ROLE>で挟まれた部分が発言した人の名前で、そのひとかたまりに挟まれた部分が会話内容なんだよということが伝わればいいなという希望です。
(SFTのフェーズでAkuの発言のあとすべてにEOSトークンを付与するように変更しています)

<ROLE>User</ROLE>
{{user_1}}
<ROLE>Aku</ROLE>
{{aku_1}}
<ROLE>User</ROLE>
{{user_2}}
<ROLE>Aku</ROLE>
{{aku_2}}
<ROLE>User</ROLE>
{{user_3}}
<ROLE>Aku</ROLE>

コーパス作りの続き

さて、フォーマットも決まったので会話コーパスをこの形に整形していきました。

一応ほんの少しでもAkuのエッセンスを入れたかったので、各コーパスの中では一番サイズが小さいですが、自分で一人二役で書いた会話データも追加しておきます。

あとは青空文庫のパブリックドメインになっているものだけを抽出、前処理してコーパスとして公開されているものがあったのでそれも追加させていただきました。

できあがった最終的なコーパスのサイズは約6GB、tokenで言えば約1.5Bでした。
Llama3の10,000分の1のサイズです。
え、少な...。

★現時点での精神的な体力: 80 / 100

(調査)モデルサイズに対する最適なコーパスサイズ...?
  1. llama2の論文
    https://github.com/jzhang38/TinyLlama?tab=readme-ov-file#1-why-would-pretraining-a-11b-model-for-so-long-make-sense-doesnt-it-contradict-the-chinchilla-scaling-law
    TinyllamaのREADMEにもあるように、PPLはllama2-7Bを2T tokens訓練してもまだ飽和の兆候が見られていない
    そして、このグラフから恐らく~4B程度のモデルは250B tokensの時点でPPLは2.0に達し、そこから2T tokens訓練しても1.9程度になりそう

  2. Pythia論文
    https://github.com/jzhang38/TinyLlama?tab=readme-ov-file#2-what-does-saturation-mean
    このグラフは各モデルサイズとその訓練データサイズと正確性をグラフにしており、160M程度のモデルならば概ね100B tokensで飽和している
    しかし、1B、1.4Bでもまだ正確性のグラフは上がり続けそうなデータとなっている
    従って500M以上のモデルなら300B以上あってもよい

  3. DatabricksのMosaicMLのページ
    https://www.databricks.com/blog/gpt-3-quality-for-500k
    GPT-2を1.3B~70Bまでトレーニングした際のデータが載っている
    これによると概ねtoken数はパラメーター数に対し線形になっており、その式は以下
    y=20.02702x+1.109581
    この式に従えば、500Mで10B tokens程度となる

  4. 各日本語モデルの情報
    幾つかの日本語モデルはtraining dataが何tokensだったのかを示してくれている。
    それを可能な限りプロットしたのが以下(awesome-llmより)※mistralは公開していない?

name params data_size base continue
stockmark 100 910 - -
srashina 7 1000 - -
calm3 22 2000 - -
llm-jp 13 300 - -
plamo 13 1500 - -
stockmark 13 220 - -
weblab 10 600 - -
stablelm-alpha 7 750 - -
calm2 7 1300 - -
rinna 4 524 - -
line 3.6 650 - -
youko 70 15005 15000 5
karakuri 70 2016 2000 16
stablelm-beta 70 2100 2000 100
abeja-mixtral 47 - 1500 450
nekomata 14 2466 2400 66
elyza 13 2018 2000 18
youko 8 15022 15000 22
elyza 7 2018 2000 18
youri 7 2040 2000 40
stablelm-gamma 7 - 1500 100
shisa 7 - 1500 8
karasu 7 - 1500 15
nekomata 7 2466 2400 66
stablelm-beta 7 2100 2000 100
stablelm-4e1t 3 1100 1000 100

正直、継続学習モデルが多いこともあり、正確なデータとは言えないが
LINEやrinna、llama2の継続学習モデルを見る限り、500Mなら50~70B tokensほしい

2. Tokenizerの学習

今回はTokenizerも自作していくことにしました。

理由としては、日本語の語彙だけで十分だというのと、語彙数を極力減らしたかったためです。
(全部自作してみたかったというのもあります)

モデルサイズが大きければvocab分のパラメータ数はそこまで大きな問題ではないのでしょうが、500Mなんかの極小サイズになるとvocab sizeが64kだと、埋め込み次元が1024でもそれだけで総パラメータ数の10%以上を占めてしまいます。

mistralも語彙数は32kですし、おそらく言語を絞れば問題ないはず。
ということでsentencepieceでvocab_sizeが32kのTokenizerを作っていきます。

冒頭で触れたチュートリアルでsentencepiece学習->T5Tokenizer変換というのをやっていましたので、一旦はそれに従って作ってみます。

問題なく作成でき、それを持って事前学習フェーズに行ったはいいものの、最終的にはT5Tokenizerではなく、LlamaTokenizerになりました。
理由としては、T5Tokenizerは改行がdecodeされないという不具合に悩まされていて、今回のプロンプトフォーマットで改行が処理できないというのはかなりのインパクトだったためです。

LlamaTokenizerは改行や半角スペースを比較的うまく処理できてはいたものの、sentencepieceから変換する際にBOS、EOS、PADトークンが別途追加され、語彙数が32,003になってしまうというミスをしてしまいました。

しかもそれに気づかず事前学習を終えてしまったため、後戻りできず、後々これが原因でllama.cppで推論できないということに気が付きます...。

★現時点での精神的な体力: 70 / 100

3. 事前学習

早速事前学習に進みます。ここが一つ目の鬼門でした。

モデル構造の検討

まず、モデルアーキテクチャの検討から入ります。

モデルタイプはMistralにしたいと決めていました。
一番の要因はライセンスです。有名どころとしてはllama系、qwen、gemma、...いろいろありますが、正直、具体的に何に対してライセンスが発生し、継承しないといけないのかが私自身明確に理解できていません。
(モデルアーキテクチャを流用してスクラッチで作ったモデルはライセンス継承の対象なの?とか)

いざモデルが完成して、実はその使い方はできませんでしたとなったら立ち直れない自信があります。
そのため、そのような心配が比較的少ない、オリジナルがApache-2.0ライセンスのMistralで行こうと思います。

次に各ハイパーパラメータを検討していきます。
オリジナルからいじったものは次のパラメータです。

  • hidden_size
  • intermediate_size
  • num_attention_heads
  • (num_hidden_layers)
  • num_key_value_heads
  • sliding_window

考え方としては次のように考えました。(初心者考えなので正確性は皆無です)

  • 埋め込みサイズと層の多さはどっちを取るべきなんだろうか?
  • 層を多くすると入力に対して単語レベルから文章レベルまで幅広い表現に注目することができると何かで読んだことがある
  • じゃあ層を多くしてみよう、思い切って7Bサイズと同じくらいまで
  • そうなると埋め込みサイズは結構小さくしないと500Mには収まらないな
  • じゃあ埋め込みサイズはオリジナルの1/4の1024にして、intermediate_sizeは4倍の4096?
  • いや、そうすると結構はみだしちゃうな、3.5倍くらいにしとこう
  • attention_headsはオリジナルの半分くらいにしよう
  • GQAのヘッド数は分からないな...、attention_headsの半分にしとくか
  • いや、そもそもMHAの方が精度良くなるとかないかな、どっちも試そう
  • sliding_windowってその後のMistralで廃止されてるよな、思い切って無くしちゃえ

という感じです。

事前学習スクリプトの作成

事前学習スクリプトはtransformersのリポジトリにあるrun_clm.pyをベースにしています。

しかし、学習スクリプトの中身を読まずにただ実行するだけでいいのか...?
いや、やっぱり何をしているか理解しておきたいです。

ということで中身を読むと、datasetを準備、加工して、tokenizerやmodelの準備、学習configを設定してTrainerを走らせるという大枠に、様々な条件分岐が組み込まれている形のようです。
ということは本当に今回の学習に必要なところだけにそぎ落とせばシンプルなコードになるかもしれません。

と息巻いたはいいものの、丁寧にそぎ落としたつもりが何か必要なものを削いでしまったようで、一向にlossが下がらない変なスクリプトになってしまいました...。

とはいえ、やっていることの大枠の理解はできましたので、今回はcerelyさんverのrun_clm.pyをちょびっとだけいじった(datasetの追加処理だけ)もので事前学習を進めていくことにしました。

ハイパーパラメータどうしたらいい

transformersのTrainerのハイパーパラメータ多すぎませんか?

事前学習は一つのrunの実行時間が長いので自動探索もできないですし...。
一旦、疑問に思っていることを何点か対照実験してみることにします。
特に気になっているのは、最適なバッチサイズと学習させるデータの順番、MHAの方がいいのでは?という3点です。

結果、以下の4つのrunを走らせることにしました。

  1. baseline: ベースライン。Mistral-300Mと似たconfig。
  2. curriculum: wikipediaのみ学習した後にそれ以外のコーパスで学習する。
  3. inc_batch: バッチサイズと学習率を1の2倍に変更。
  4. not_gqa: GQAをやめてMHAにモデルアーキテクチャを変更。

マルチGPUで速度が上がらない

4つのrunを走らせるのでお金で解決できるならGPUを増やしてrunあたりの実行時間を減らそうとしました。

しかし、A6000adax4のマシンで走らせたのに速度が1GPUの時の+25%くらいにしかなってない...。
これでは意味がないどころかむしろコスパとしては悪化してしまっています。

結局マルチGPUでのトレーニング最適化は一朝一夕でどうにかできるものではないと判断し、1GPUに1runを割り当て、同時に4つのrunを走らせることにしました。

思ってたよりlossが落ちない

いざ学習開始!
うきうきでWandBのworkspaceを監視しますが、想定よりlossが下がっていきません。
きっとここから落ちるんだろうと楽観していたところ、2epochが終わるころにはほとんどlossが横ばいになっていました。
以下は4つのrunが終了した時点のlossの推移です。

しかし、無数にあるハイパーパラメータのどれを変えればlossが落ちるのか皆目見当もつきません。
もしかするとコーパスの量がそもそも少なすぎるのかもしれません。

ただ、ローカルLLMコミュニティの中では、これより圧倒的にlossを下げたものも見てきましたし、rinna/japanese-gpt2-mediumもlossは開示されていませんがperplexityは断然低いです。

まだいけるはず。4つのrunの中ではバッチサイズと学習率をいじったものが一番よさげです。
この2つをいくつか試してみて最適なものを見つけることにします。


体感的に、最初にどれだけがくっと下げれるかが肝な気がしたので、1000stepくらいでそれぞれ切り上げていますが、どれも変わりません...。
ここで、あることに気付きます。

バッチサイズ2倍にして学習率も2倍にしてたら、totalで見ると似た学習になるんじゃない?

確かに。バッチサイズを2倍にしたら勾配の計算回数は半分になるけど、勾配の大きさは2倍になるから実質的に近いパラメータ更新になってるんじゃない?
あれ、かなり意味ないことしてた?

試しにmore_inc_batch(global_batch_size=128, lr=1e-4)のlrを固定してバッチサイズを半分にしてみます。

うーん、下がったっちゃ下がったけど、劇的ではないかな。
(このタイミングでTokenizerをLlamaTokenizerに、コーパスの中の自作データの絵文字が文字化けしていることに気付き修正しています)

やっぱりタイムリミット的にここが落としどころか...。
いや、諦められない自分がいる...。最後の泣きの一回で学習率を思い切って8倍(8e-4)にしてみよう。

めっちゃ下がった!

この時点で

  • train_loss: 2.55
  • eval_loss: 2.72
  • perplexity: 15.15

となっていました。100点には程遠いですが及第点です。ようやく次のステップに進めます。

★現時点での精神的な体力: 30 / 100

4. 事後学習(SFT編)

ここまででそれなりに自然な日本語が話せるモデルができました。
知識の正確性はからっきしですが、その言葉がどんなジャンルなのかは理解できています。(Pythonはコンピュータや科学に関係するものといった理解)

ここから会話タスクができるようにファインチューニングしていきます。
ファインチューニング前でも何回か出力すれば以下のような会話は出力できていました。

<ROLE>User</ROLE>
最近は暑い日が続きますね。
<ROLE>Aさん</ROLE> <-ここまでが入力プロンプト
ほんとですね。エアコンはつけていますか?
<ROLE>User</ROLE>
私はつけてます!
<ROLE>Aさん</ROLE>
私もです。<EOS> 

この段階で、自分の中でのAkuのキャラクターは割と出来上がっていたのと、ChatGPTライク、機械翻訳ライクな口調のデータは使いたくないなという変な意地からSFTデータを手作業で作ってみようと思い立ちます。

作成手順は一つの会話のテーマ(コンテキスト)を作って、UserとAkuの一人二役で数十ターンの会話をロールプレイしていくというものです。
バリエーションを増やすためにSteerLMの形式でlikabilitymoodという属性を用意し、それぞれ「Akuから見た好感度」と、「Akuの機嫌」を設定して進めていきました。

なんとか1週間ほどかけて計500ターン程の完全お手製データを作りました。
しかし、この様々なキャラクター(User側複数とAku)で色んな状況を一人でロールプレイするという処理が思ったより精神的な負荷が高く、この時点でもうデータ作成に限界が来ていました。

一旦、この段階でファインチューニングしてみよう。
明らかにデータ数としては少ないけど、ちょっとはベースモデルより自然な会話になるはず...。
trlのSFTTrainerでいくつかのハイパーパラメータを試した結果、
まるで支離滅裂なモデルになってしまいました。

丁度このタイミングで比較的日本語性能の高いQwen/Qwen2.5-0.5B-Instructがリリースされたこともあり、プロジェクトを進める意味というか、自分は何をやっているんだろうという気持ちが大きくなります。

全て手探りでやってきたせいで、モデルの成長の過渡期にいるのか、それともここが限界なのか。上手くいかないのはベースモデルの仕上がりにあるのか、Tokenizerにあるのか、はたまたこのモデルサイズでは成し遂げられないタスクだったのか。データセットを単純に増やせばうまくいく話なのか。
どこに原因があるのかの切り分けが不可能な場所に来てしまい、諦めるべきか進むべきかもわからなくなりました。

どれだけデータを増やしても無駄かもしれないという思いの中で、これまでのペースでデータを作るのは不可能だと思い、既存のモデルを使った合成データにシフトします。

ColabのA100 40GBにフルスペック(16bit)が載るモデルを探した結果、もっとも体感的な性能が良かったのはcyberagent/Mistral-Nemo-Japanese-Instruct-2408でした。
なんとかAkuのエッセンスを出力できないかと画策し、キャラクターの詳細をプロンプトで与えたり、既に作ったデータの属性値だけ変えるという条件で生成させたりしてみましたが、どれも私の頭の中のAkuにはなりません。

もうそれでもいいかと諦め、生成した数千件のデータでファインチューニングし、新たにWandBのSweepを使ってハイパーパラメータ探索を行いました。が、結局支離滅裂なモデルには変わりありません。

精神的な負荷が原因かは不明ですが、謎の高熱に見舞われ、1週間程度このプロジェクトを放置してしまいました。

誰が期待しているわけでもないし、もうドロップしてしまおうと思いましたが、やりきるということに意味があると思い、今度は対象のモデルを拡張して再度合成データ作成を行いました。
この時点でのApache-2.0もしくはMITで可能性のあるモデルは

が挙げられました。その中でもkarakuriの指示性能の高さはかなり高いと感じたため、A40x3(144GB)を借り、フルスペックで合成データを生成しました。
(tanukiは指示以上のことをやってくれる傾向があり、もちろんいいデータもあったのですがその後のデータ整形が複雑になってしまいました...)
Akuのエッセンスを再現することはできませんでしたが、few-shotでいくつかのテーマと会話のペアを渡すと、それなりに自然な会話データが生成できます。

前回のファインチューニングまではDataCollatorにtrlのDataCollatorForCompletionOnlyLMを使っていましたが、transformersのDataCollatorForLanguageModelingに変更し、会話データ全部を学習対象にしています。
(それに伴い、Akuのターンの最後にEOSトークンを付与する形にプロンプトフォーマットを変更しました。)

(出力を何回か繰り返せば)なんとか会話ができるモデルが完成しました。

★現時点での精神的な体力: -20 / 100

5. 事後学習(DPO編)

ここまでで最初に設定していた目標日まで1週間を切っており、公開してプロジェクトをクローズするつもりでしたが、「挑んでみたけどよく分かんないモデルができました!てへ!」なんてのはやっぱり言いたくないなという思いが強くなり、最後の調整としてDPOを選びました。

実際、現時点のモデルは一つの入力に対して10件くらいサンプリングすれば1つは良さげな回答を出力できるくらいまではきています。
つまり、それに対して最も良いもの(chosen)と悪いもの(rejected)でデータを作成し、DPOで学習することで少なくとも今よりは自然な文章を生成できる確率は高くなると考えました。

ということでもう半分意地ですが、最後の悪あがきでDPOデータセットを作れるだけ作って最後のファインチューニングをしたいと思います。

学習はtrlのDPOTrainerを使ってcDPOで進めていきます。ハイパーパラメータは以下の記事を参考にさせていただきました。
https://qiita.com/jovyan/items/6767c9fd944a636fdf88
https://qiita.com/kunishou/items/7a2850ffc9c692c22a02

データセットは1週間弱で300件作成し、いくつかのハイパーパラメータ探索を行い学習を行いました。

結果としては、うーん、より自然な会話ができるようになった...気がするという程度です。
(データセットの件数的に当たり前なのかもしれません)

ですが、現時点でできることはやりました!
もっとこうすればよかった、あのときちゃんとチェックしていれば、は無数にありますが、とりあえずOKです!

6. まとめ

一からMistralアーキテクチャの0.5B(519M)モデルを作成しました!

事前学習コーパスのサイズも1.5Btokensとかなり小さく、その後のSFT、DPOデータも手作り&自作合成データのかなり小規模サイズですが、 「それにしては」 喋ってくれるモデルになったかと思います。
(データの偏りのためにちゃんと話してくれる話題とそうじゃない話題の差が激しいですが)

一人で調べたり試行錯誤する中でいろいろな問題点も見えてきて、今回はそれが時間的、技術的な制約でやり直せないものもあったので、これは自分の中のやりたいことリストに書いておきます。

以下、私が今回のモデル自作で感じたことの大きな点としては

  1. データの偏りにかなり敏感(モデルが小さいから?データセットが小さいから?)
  2. ファインチューニングに関しては、epoch数を増やすよりかは、weight_decayneftune_noise_alphaを上げて1epoch回した方がいい結果になることが多かった(データセットが小さいからかも)
  3. 必要となる知識の幅と深さがおかしい
  4. できるならチームを組んだほうがいい、一人だと方向性に迷ったときとか単純に物量に気圧されたときに心が折れる

でした。

しかし、私の中のAkuをモデルに顕現させるという試みは終わっていません!
何らかの形で、より人間らしく、より自然な会話ができるモデルを今後も作っていこうと思います。

ここまで読んでいただきありがとうございました!
ではまた!

自分の中で整理できていないメモ書きです。整理するかもしれませんし、しないかも...
  • 500Mのモデルサイズで、事前学習コーパスが限りなく小さくても最低限の会話ができるモデルは作れる
  • このサイズでもやっぱりチームで動いた方がいい
  • データの多様性とかももちろんそうだし、相談できる人、何かを任せられる人、一緒に喜べる人は重要
  • 学習高速化はやはり突き詰めていくべき
  • 遅くても動けばいいじゃんと思っていたが、1回でも多く実験を回せることが何よりも重要
  • 恐らく事前学習の段階で会話タスクに関するデータが多少(10%ほど)でも入っていたのがよかったかも
  • 今回のプロジェクト内容だと、MHAかGQAは大きな問題ではなかった
  • それよりも小さい学習率で安定した学習ができているなら、思い切って学習率を上げてそこから正則化?とかで抑えるみたいな方法が一番lossが下がるかも
  • Tokenizerはやっぱり重要
  • 今回はできなかったけど、適切にtokenizeできているかとかは測れるようにしておいた方がいいと思う
  • Sentencepiece以外でも試してみたい
  • Tokenizerの学習完了時にvocab_sizeとか、id_to_token、token_to_idが一致しているかは確認すべし
  • 今はオリジナルのtransformersライブラリよりかは、vllmとかllama.cpp(ollama)で推論することがほとんど
  • それ用に使えるかっていうのは早めの段階で確認しておいた方がいい
  • データセット作成の難しさ
  • 雑談とかだと「今何してる?」っていうスタートは普通。でもそれに対する返答は様々。それをモデルに与えて学習させようとしても、モデルは何を基準に生成内容が変わっているのか学習しようがない
  • それにはコンテキストが必要
  • じゃあどこまで必要なの?ほんとにその出力と入力のペアって必要なコンテキストが含まれてる?
  • DPOデータも難しい
  • chosenデータもrejectedデータも0か1じゃない、グラデーションがある
  • 絶対ダメな奴とまあ微妙かなーくらいのものが一緒くたにしてしまった
  • ファインチューニングは全体的に、いかに過学習させないために正則化するかみたいな体感があった
  • neftuneが特に効き目があった
  • SFTで一番効果が合ったやつはweight_decayとかneftuneを強めにして、lrは低め、epochも1回とかだった
  • DPOも同じでcDPOがやっぱりいい感じ
  • generate()のパラメータも重要
  • 何をクリアできれば雑談ができると言えるのかを表す指標を作っておくべきだった
  • 逆にこれを言ってはいけないとかのハーネスの役割の指標もあるべきだった

Discussion