小さな大規模言語モデル(0.3B)をトレースで自家構築する際のメモ

はじめに

これまで、大規模言語モデル(LLM)に対するファインチューニングや追加学習の検討を行ってきたのですが、やっぱりゼロから作ってみた方が面白そうなので、構築していきます。
自家製サーバーやGoogle Colabで動かす想定で、まずは小さめのLLMを作っていきます。


参考サイト

参考になりそうなサイトを集めました。

1. 300MモデルをDeepZeroで構築

基本的にはshスクリプトを叩くだけでOKという、神仕様です。
Colabのサンプルコードも上がっていましたが、簡易バージョン(?)なので、回答は壊滅的でした。

2. GPT-NEOXでの構築

A100(40GB) ✕ 8くらいの環境が必要なようです。

3. GPT-NEOX+Colabでの構築

4. Rinnna2のフルスクラッチ構築

英語です。

実装

1.を動かすことにしました。
以下、試してみた的な追試メモとなります。

動作環境

A100x2
SSD 2 TB
HDD 10TB
Intel(R) Xeon(R) w5-3435X
RAM 512 GB (DDR5)

レポジトリ取得

https://github.com/ce-lery/japanese-mistral-300m-recipe.git
cd japanese-mistral-300m-recipe

Dockerの構築

pip周りがかなり面倒そうなので、webサイトの説明通り、dockerを使ったほうが良さそうです。


#install
docker build -t cuda12.1-cudnn8-python3.11.6 ./

#起動: カレントディレクトリをマウントする (※元のコマンドから変えました)
docker run -v $(pwd):/home/japanese-gpt2/ -it --gpus all cuda12.1-cudnn8-python3.11.6

ローカルマシンのsshが2 TB弱しかなく、容量が不安だったので、hddにマウントさせ、動かすことにしました。
(SDDだと10%くらい早くなるようです)

実行

諸々の処理を自動実行してくれるscriptファイルが、run_all.shでした。

cd japanese-gpt2/japanese-mistral-300m-recipe #ディレクトリ移動
bash run_all.sh

あまりにも全自動なので、自分なりに処理を追っていきます。

※注意
A100(x2)環境で回していたところ、tokenize中にエラーが出ました(後述)。
打開策として、GPUを1枚だけ認識させる設定にしたら、エラー回避できました。

export CUDA_VISIBLE_DEVICES=0


環境セットアップ

環境構築スクリプトがsetup.shです。pyenvで仮想環境を作るようです。

データセットのダウンロードと前処理

wikipediaとcc100データをダウンロード・前処理・マージしてくれるのがdataset.shです。前処理に10hrほどはかかるようです。とりあえず1日以上、放置したら終わってました。
前処理はわりと適当にやっているようで、色々と改善の余地があるとのこと。

最終的な出力は、以下の通り。ファイルがでかいですね。

  • merge_dataset_for_tokenizer.txt (72GB)

    • データセットをマージしたテキストファイル

  • train.txt (69 GB)

    • 訓練データ

  • test.txt (3 GB)

    • テストデータ

2TBのSSD、10TBのHDDのマシンを最近購入したんですが、すぐに容量不足になりそうです。

トークナイズ

tokenizer.shというスクリプトファイルが動きます。
Sentencepieceというライブラリでトークン化 & 語彙を登録し、Transformersライブラリで使えるように変換するようです。
30分以内に終わったような気がします。

訓練

pretrain.shが動きます。
config関連でパス間違いがありましたが、issueで修正してもらいました。

データセットを読み込むと、tokenizeが始まります。

2hrほどかかるようです。

補足
tokenize中、自身の環境では30分くらいのタイミングでエラーが出てしまいました(2回中、2回)

Running tokenizer on dataset:  23%|███████████████████▍                                                               | 143009000/609617183 [30:00<1:32:25, 84143.01 examples/s]Traceback (most recent call last):
  File "/home/japanese-gpt2/japanese-mistral-300m-recipe/pretrain/train/run_clm.py", line 686, in <module>
    main()
  File "/home/japanese-gpt2/japanese-mistral-300m-recipe/pretrain/train/run_clm.py", line 507, in main
    with training_args.main_process_first(desc="dataset map tokenization"):
  File "/home/.pyenv/versions/3.11.6/lib/python3.11/contextlib.py", line 137, in __enter__
    return next(self.gen)
           ^^^^^^^^^^^^^^
  File "/home/japanese-gpt2/japanese-mistral-300m-recipe/.env/lib/python3.11/site-packages/transformers/training_args.py", line 2070, in main_process_first
    dist.barrier()
  File "/home/japanese-gpt2/japanese-mistral-300m-recipe/.env/lib/python3.11/site-packages/torch/distributed/c10d_logger.py", line 47, in wrapper
    return func(*args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^
  File "/home/japanese-gpt2/japanese-mistral-300m-recipe/.env/lib/python3.11/site-packages/torch/distributed/distributed_c10d.py", line 3696, in barrier
    work = default_pg.barrier(opts=opts)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
RuntimeError: [1] is setting up NCCL communicator and retrieving ncclUniqueId from [0] via c10d key-value store by key '0', but store->get('0') got error: Socket Timeout
Exception raised from doWait at ../torch/csrc/distributed/c10d/TCPStore.cpp:445 (most recent call first):
frame #0: c10::Error::Error(c10::SourceLocation, std::string) + 0x57 (0x7f1b1ccc9617 in /home/japanese-gpt2/japanese-mistral-300m-recipe/.env/lib/python3.11/site-packages/torch/lib/libc10.so)
frame #1: c10::detail::torchCheckFail(char const*, char const*, unsigned int, char const*) + 0x68 (0x7f1b1cc84a56 in /home/japanese-gpt2/japanese-mistral-300m-recipe/.env/lib/python3.11/site-packages/torch/lib/libc10.so)
frame #2: c10d::TCPStore::doWait(c10::ArrayRef<std::string>, std::chrono::duration<long, std::ratio<1l, 1000l> >) + 0x32c (0x7f1ab54ce00c in /home/japanese-gpt2/japanese-mistral-300m-recipe/.env/lib/python3.11/site-packages/torch/lib/libtorch_cpu.so)
frame #3: c10d::TCPStore::doGet(std::string const&) + 0x32 (0x7f1ab54cf192 in /home/japanese-gpt2/japanese-mistral-300m-recipe/.env/lib/python3.11/site-packages/torch/lib/libtorch_cpu.so)
frame #4: c10d::TCPStore::get(std::string const&) + 0x55 (0x7f1ab54cf5b5 in /home/japanese-gpt2/japanese-mistral-300m-recipe/.env/lib/python3.11/site-packages/torch/lib/libtorch_cpu.so)
frame #5: c10d::PrefixStore::get(std::string const&) + 0x31 (0x7f1ab5486e01 in /home/japanese-gpt2/japanese-mistral-300m-recipe/.env/lib/python3.11/site-packages/torch/lib/libtorch_cpu.so)
frame #6: c10d::PrefixStore::get(std::string const&) + 0x31 (0x7f1ab5486e01 in /home/japanese-gpt2/japanese-mistral-300m-recipe/.env/lib/python3.11/site-packages/torch/lib/libtorch_cpu.so)
frame #7: c10d::PrefixStore::get(std::string const&) + 0x31 (0x7f1ab5486e01 in /home/japanese-gpt2/japanese-mistral-300m-recipe/.env/lib/python3.11/site-packages/torch/lib/libtorch_cpu.so)
frame #8: c10d::ProcessGroupNCCL::broadcastUniqueNCCLID(ncclUniqueId*, bool, std::string const&, int) + 0xb2 (0x7f1a6987a312 in /home/japanese-gpt2/japanese-mistral-300m-recipe/.env/lib/python3.11/site-packages/torch/lib/libtorch_cuda.so)
frame #9: c10d::ProcessGroupNCCL::getNCCLComm(std::string const&, std::vector<c10::Device, std::allocator<c10::Device> > const&, c10d::OpType, int, bool) + 0x203 (0x7f1a6987fce3 in /home/japanese-gpt2/japanese-mistral-300m-recipe/.env/lib/python3.11/site-packages/torch/lib/libtorch_cuda.so)
frame #10: <unknown function> + 0x128e4f7 (0x7f1a6988e4f7 in /home/japanese-gpt2/japanese-mistral-300m-recipe/.env/lib/python3.11/site-packages/torch/lib/libtorch_cuda.so)
frame #11: c10d::ProcessGroupNCCL::allreduce_impl(std::vector<at::Tensor, std::allocator<at::Tensor> >&, c10d::AllreduceOptions const&) + 0x21 (0x7f1a698901b1 in /home/japanese-gpt2/japanese-mistral-300m-recipe/.env/lib/python3.11/site-packages/torch/lib/libtorch_cuda.so)
frame #12: c10d::ProcessGroupNCCL::allreduce(std::vector<at::Tensor, std::allocator<at::Tensor> >&, c10d::AllreduceOptions const&) + 0x3a7 (0x7f1a69891dd7 in /home/japanese-gpt2/japanese-mistral-300m-recipe/.env/lib/python3.11/site-packages/torch/lib/libtorch_cuda.so)
frame #13: c10d::ProcessGroupNCCL::barrier(c10d::BarrierOptions const&) + 0xb25 (0x7f1a698a3b15 in /home/japanese-gpt2/japanese-mistral-300m-recipe/.env/lib/python3.11/site-packages/torch/lib/libtorch_cuda.so)
frame #14: <unknown function> + 0x567a342 (0x7f1ab547a342 in /home/japanese-gpt2/japanese-mistral-300m-recipe/.env/lib/python3.11/site-packages/torch/lib/libtorch_cpu.so)
frame #15: <unknown function> + 0x5684960 (0x7f1ab5484960 in /home/japanese-gpt2/japanese-mistral-300m-recipe/.env/lib/python3.11/site-packages/torch/lib/libtorch_cpu.so)
frame #16: <unknown function> + 0x5684a65 (0x7f1ab5484a65 in /home/japanese-gpt2/japanese-mistral-300m-recipe/.env/lib/python3.11/site-packages/torch/lib/libtorch_cpu.so)
frame #17: <unknown function> + 0x4cb046b (0x7f1ab4ab046b in /home/japanese-gpt2/japanese-mistral-300m-recipe/.env/lib/python3.11/site-packages/torch/lib/libtorch_cpu.so)
frame #18: <unknown function> + 0x4cae44c (0x7f1ab4aae44c in /home/japanese-gpt2/japanese-mistral-300m-recipe/.env/lib/python3.11/site-packages/torch/lib/libtorch_cpu.so)
frame #19: <unknown function> + 0x1a039d8 (0x7f1ab18039d8 in /home/japanese-gpt2/japanese-mistral-300m-recipe/.env/lib/python3.11/site-packages/torch/lib/libtorch_cpu.so)
frame #20: <unknown function> + 0x568df24 (0x7f1ab548df24 in /home/japanese-gpt2/japanese-mistral-300m-recipe/.env/lib/python3.11/site-packages/torch/lib/libtorch_cpu.so)
frame #21: <unknown function> + 0x568ee8d (0x7f1ab548ee8d in /home/japanese-gpt2/japanese-mistral-300m-recipe/.env/lib/python3.11/site-packages/torch/lib/libtorch_cpu.so)
frame #22: <unknown function> + 0xc542c8 (0x7f1af5a542c8 in /home/japanese-gpt2/japanese-mistral-300m-recipe/.env/lib/python3.11/site-packages/torch/lib/libtorch_python.so)
frame #23: <unknown function> + 0x3f8724 (0x7f1af51f8724 in /home/japanese-gpt2/japanese-mistral-300m-recipe/.env/lib/python3.11/site-packages/torch/lib/libtorch_python.so)
<omitting python frames>
frame #41: <unknown function> + 0x29d90 (0x7f1b9b443d90 in /usr/lib/x86_64-linux-gnu/libc.so.6)
frame #42: __libc_start_main + 0x80 (0x7f1b9b443e40 in /usr/lib/x86_64-linux-gnu/libc.so.6)
frame #43: _start + 0x25 (0x560678b70095 in /home/japanese-gpt2/japanese-mistral-300m-recipe/.env/bin/python)
. This may indicate a possible application crash on rank 0 or a network set up issue.
Running tokenizer on dataset:  23%|███████████████████▍                                                               | 143147000/609617183 [30:01<1:35:10, 81691.93 examples/s][2023-12-30 01:07:00,796] [INFO] [launch.py:315:sigkill_handler] Killing subprocess 33348
[2023-12-30 01:07:01,692] [INFO] [launch.py:315:sigkill_handler] Killing subprocess 33349
[2023-12-30 01:07:01,692] [ERROR] [launch.py:321:sigkill_handler] ['/home/japanese-gpt2/japanese-mistral-300m-recipe/.env/bin/python', '-u', 'run_clm.py', 'hf_config.json', '--deepspeed', '--deepspeed_config', 'ds_config_zero.json'] exits with return code = 1
cuda
Traceback (most recent call last):
  File "/home/japanese-gpt2/japanese-mistral-300m-recipe/pretrain/inference/inference.py", line 18, in <module>
    tokenizer = AutoTokenizer.from_pretrained(MODEL_NAME,use_fast=False)
                ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/japanese-gpt2/japanese-mistral-300m-recipe/.env/lib/python3.11/site-packages/transformers/models/auto/tokenization_auto.py", line 718, in from_pretrained
    tokenizer_config = get_tokenizer_config(pretrained_model_name_or_path, **kwargs)
                       ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/japanese-gpt2/japanese-mistral-300m-recipe/.env/lib/python3.11/site-packages/transformers/models/auto/tokenization_auto.py", line 550, in get_tokenizer_config
    resolved_config_file = cached_file(
                           ^^^^^^^^^^^^
  File "/home/japanese-gpt2/japanese-mistral-300m-recipe/.env/lib/python3.11/site-packages/transformers/utils/hub.py", line 430, in cached_file
    resolved_file = hf_hub_download(
                    ^^^^^^^^^^^^^^^^
  File "/home/japanese-gpt2/japanese-mistral-300m-recipe/.env/lib/python3.11/site-packages/huggingface_hub/utils/_validators.py", line 110, in _inner_fn
    validate_repo_id(arg_value)
  File "/home/japanese-gpt2/japanese-mistral-300m-recipe/.env/lib/python3.11/site-packages/huggingface_hub/utils/_validators.py", line 158, in validate_repo_id
    raise HFValidationError(
huggingface_hub.utils._validators.HFValidationError: Repo id must be in the form 'repo_name' or 'namespace/repo_name': '../train/checkpoints-mistral-300M-FA2'. Use `repo_type` argument if needed.
dataset/dataset.sh: line 3: [!: command not found
fatal: destination path 'databricks-dolly-15k-ja' already exists and is not an empty directory.


色々試したところ、マルチGPU環境でnode間の通信がうまくいっていない感じでした。
A100(80GB)x2の環境だったんですが、一枚だけ使う条件で回すことにしました。

export CUDA_VISIBLE_DEVICES=0

事前学習がうまく回りだすと、進捗バーが表示されます。

A100x1の環境では、80 hrくらいで終わるようです。
元記事のRTX4090環境では120hrとのこと。

水冷GPUなので、GPUが温度を低めに抑えられています

使用VRAMは15GB程度でした。バッチサイズをもっと大きくしても良いかもしれません。

ファインチューニング

無事に事前学習が終わると、ファインチューニングが始まります。
自身の環境では、このタイミングで何故かgpuドライバの認識エラーが出てしまいました(nvidia-smiもエラー)。
→ dockerをrestartして解決。

ファインチューニング中、Dynamoフレームワーク中の警告がたくさん出てきましたが、学習自体は進んでいるようなので無視しました。

[rank0]:[2024-01-03 18:07:15,153] [9/44] torch._dynamo.variables.higher_order_ops: [ERROR] HigherOrderOperator with body that accepts non-Tensors as input. Got: <class 'tuple'>
[rank0]:[2024-01-03 18:07:16,131] [8/45] torch._dynamo.variables.higher_order_ops: [WARNING] speculate_subgraph: while introspecting the user-defined autograd.Function, we were unable to trace function `trampoline_autograd_fwd` into a single graph. This means that Dynamo was unable to prove safety for this API and will fall back to eager-mode PyTorch, which could lead to a slowdown.
[rank0]:[2024-01-03 18:07:16,131] [8/45] torch._dynamo.variables.higher_order_ops: [ERROR] HigherOrderOperator with body that accepts non-Tensors as input. Got: <class 'tuple'>
[rank0]:[2024-01-03 18:07:16,815] [9/45] torch._dynamo.variables.higher_order_ops: [WARNING] speculate_subgraph: while introspecting the user-defined autograd.Function, we were unable to trace function `trampoline_autograd_fwd` into a single graph. This means that Dynamo was unable to prove safety for this API and will fall back to eager-mode PyTorch, which could lead to a slowdown.
[rank0]:[2024-01-03 18:07:16,815] [9/45] torch._dynamo.variables.higher_order_ops: [ERROR] HigherOrderOperator with body that accepts non-Tensors as input. Got: <class 'tuple'>
[rank0]:[2024-01-03 18:07:17,783] [8/46] torch._dynamo.variables.higher_order_ops: [WARNING] speculate_subgraph: while introspecting the user-defined autograd.Function, we were unable to trace function `trampoline_autograd_fwd` into a single graph. This means that Dynamo was unable to prove safety for this API and will fall back to eager-mode PyTorch, which could lead to a slowdown.
[rank0]:[2024-01-03 18:07:17,783] [8/46] torch._dynamo.variables.higher_order_ops: [ERROR] HigherOrderOperator with body that accepts non-Tensors as input. Got: <class 'tuple'>
[rank0]:[2024-01-03 18:07:18,152] [9/46] torch._dynamo.variables.higher_order_ops: [WARNING] speculate_subgraph: while introspecting the user-defined autograd.Function, we were unable to trace function `trampoline_autograd_fwd` into a single graph. This means that Dynamo was unable to prove safety for this API and will fall back to eager-mode PyTorch, which could lead to a slowdown.
[rank0]:[2024-01-03 18:07:18,152] [9/46] torch._dynamo.variables.higher_order_ops: [ERROR] HigherOrderOperator with body that accepts non-Tensors as input. Got: <class 'tuple'>
[rank0]:[2024-01-03 18:07:19,115] [8/47] torch._dynamo.variables.higher_order_ops: [WARNING] speculate_subgraph: while introspecting the user-defined autograd.Function, we were unable to trace function `trampoline_autograd_fwd` into a single graph. This means that Dynamo was unable to prove safety for this API and will fall back to eager-mode PyTorch, which could lead to a slowdown.
[rank0]:[2024-01-03 18:07:19,116] [8/47] torch._dynamo.variables.higher_order_ops: [ERROR] HigherOrderOperator with body that accepts non-Tensors as input. Got: <class 'tuple'>
[rank0]:[2024-01-03 18:07:19,488] [9/47] torch._dynamo.variables.higher_order_ops: [WARNING] speculate_subgraph: while introspecting the user-defined autograd.Function, we were unable to trace function `trampoline_autograd_fwd` into a single graph. This means that Dynamo was unable to prove safety for this API and will fall back to eager-mode PyTorch, which could lead to a slowdown.
[rank0]:[2024-01-03 18:07:19,488] [9/47] torch._dynamo.variables.higher_order_ops: [ERROR] HigherOrderOperator with body that accepts non-Tensors as input. Got: <class 'tuple'>
huggingface/tokenizers: The current process just got forked, after parallelism has already been used. Disabling parallelism to avoid deadlocks...
To disable this warning, you can either:
        - Avoid using `tokenizers` before the fork if possible
        - Explicitly set the environment variable TOKENIZERS_PARALLELISM=(true | false)
huggingface/tokenizers: The current process just got forked, after parallelism has already been used. Disabling parallelism to avoid deadlocks...
To disable this warning, you can either:
        - Avoid using `tokenizers` before the fork if possible
        - Explicitly set the environment variable TOKENIZERS_PARALLELISM=(true | false)
huggingface/tokenizers: The current process just got forked, after parallelism has already been used. Disabling parallelism to avoid deadlocks...


ファインチューニングはdolly-15k-jaを使うようです。
4時間ほどかかるとのこと。

評価結果

元記事と同じデータセットを使って学習・評価していますが、random seedなどは異なるので、参考までに質疑の例を示します。

質問: 日本で一番高い山は?

Assistant:日本の山の中で、最も標高の高い山は何でしょう?
日本にある山で、標高は1,125mです(世界第2位)。 標高が最も高いのは、長野県の白馬山(標高2,230m)で2番目に高い山である。

質問: 日本で一番広い湖は?

Assistant:日本の湖の面積は約1,100km2で、湖の数は約2,400km3で全国第4位である。湖の数では世界第3位の湖であり、世界の湖の中で2番目に大きい湖である[citation needed]。

質問: 世界で一番高い山は?

Assistant:地球上で最も低い山の1つである。地球上のすべての天体の中で最も低い山であり、地球から最も遠い山でもある。その大きさは、地球の約3分の1である[citation needed]。 しかし、その高さは地球に最も近い天体の1

質問: 世界で一番広い湖は?

Assistant:世界第3位の湖であるインドは、世界で最も大きな湖の1つである。インドには、世界最大かつ最も人口密度の高い湖が2つあり、そのうち1つは湖で、2番目に大きい湖であり、3番目に人口の多い湖でもあります。また、インド最大の湖

質問: 冗談を言ってください。

Assistant:AIは、人間の知能をはるかに超えた能力を持っています。 AIが人間の能力を超えるのは、その能力が人間より優れているからです。しかし、AIの能力は人間よりも優れており、人間とAIを区別する能力も持っています。また、機械やロボットよりも優れた能力

基本的にはハルシネーションの連発でしたが、最後の質問だけは、意味深なジョークが返ってきました。


所感と今後

無事に大規模言語モデルのフルスクラッチ学習、ファインチューニング、推論を回すことができました。学習データの量とモデルサイズが小さいため、流石に低クオリティなモデルとなってしまいましたが、雰囲気はだいぶ掴めてきました。

次は筆者のメールボックス10年分くらいを追加して学習させてみようかと考えています。

この記事が気に入ったらサポートをしてみませんか?