OpenCLAMのハンズオンでいきなり躓きまくった

# はじめに
本記事は新納浩幸氏の著書「[LLMのファインチューニングとRAG](https://amzn.asia/d/fFZGozY)」（2024年5月オーム社より出版）をもとに、**ズブの素人**がPythonとOpenCLAMを使用して、LLMのファインチューニングとRAGをハンズオンで自己学習する過程での、詰まったポイントと回避策の記録です。
出版から1年経っていない書籍をもとにしていますが、バージョンやOSの違いによるものか、最初のステップで躓いて苦労しました。

# 著作権に関わる制限事項
**本記事では「LLMのファインチューニングとRAG」内で記述されている文書およびコードに関し、一切複製・転記は致しません。
これは著作権法上及び、同書籍の下記リンク先「ダウンロード」の項に書かれている転載、複製の禁止を遵守するためですのでご容赦ください。**

https://www.ohmsha.co.jp/book/9784274231957/

# 学習環境
|項目|仕様|
|:--|:--|
|OS|macOS Sequoia 15.2|
|CPU|Apple M3|
|Python|3.12.0|

# 使用ライブラリ
|ライブラリ名|バージョン|
|:--|:--|
|torch|2.5.1|
|torchvision|0.20.1|
|torchaudio|2.5.1|
|transformers|4.47.1|
|accelerate|1.2.1|

:::note warn
最初の躓きポイントでした。主軸として使用するライブラリ「torch」はPythonの最新バージョンで使用できない可能性があります。
[PyTorchの公式リリース情報](https://github.com/pytorch/pytorch/releases)から、利用可能なPythonのバージョンを事前確認しておくことを推奨します。
筆者はバージョンを3.18.1→3.12.0にダウングレードしました。
:::

# ライブラリのインストール
```shell:zsh
pip3 install torch torchvision torchaudio
pip3 install transformers accelerate
```

# 試行錯誤の記録
OpenCLAM-smallへの問い合わせを書籍の通り実装して実行するとエラーが発生しました。
## 躓き1
ファイル階層
```plaintext:zsh
tree
.
└── Chapt1
    └── llm-based-use.py
```
実行結果
```plaintext:zsh
Python3 ./Chapt1/llm-based-use.py
The `GPTNeoXSdpaAttention` class is deprecated in favor of simply modifying the `config._attn_implementation`attribute of the `GPTNeoXAttention` class! It will be removed in v4.48
Setting `pad_token_id` to `eos_token_id`:0 for open-end generation.
首都
Traceback (most recent call last):
  File "/Library/Frameworks/Python.framework/Versions/3.12/lib/python3.12/site-packages/transformers/modeling_utils.py", line 3897, in from_pretrained
    ).start()
      ^^^^^^^
  File "/Library/Frameworks/Python.framework/Versions/3.12/lib/python3.12/multiprocessing/process.py", line 121, in start
    self._popen = self._Popen(self)
                  ^^^^^^^^^^^^^^^^^
  File "/Library/Frameworks/Python.framework/Versions/3.12/lib/python3.12/multiprocessing/context.py", line 224, in _Popen
    return _default_context.get_context().Process._Popen(process_obj)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/Library/Frameworks/Python.framework/Versions/3.12/lib/python3.12/multiprocessing/context.py", line 289, in _Popen
    return Popen(process_obj)
           ^^^^^^^^^^^^^^^^^^
  File "/Library/Frameworks/Python.framework/Versions/3.12/lib/python3.12/multiprocessing/popen_spawn_posix.py", line 32, in __init__
    super().__init__(process_obj)
  File "/Library/Frameworks/Python.framework/Versions/3.12/lib/python3.12/multiprocessing/popen_fork.py", line 19, in __init__
    self._launch(process_obj)
  File "/Library/Frameworks/Python.framework/Versions/3.12/lib/python3.12/multiprocessing/popen_spawn_posix.py", line 42, in _launch
    prep_data = spawn.get_preparation_data(process_obj._name)
                ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/Library/Frameworks/Python.framework/Versions/3.12/lib/python3.12/multiprocessing/spawn.py", line 164, in get_preparation_data
    _check_not_importing_main()
  File "/Library/Frameworks/Python.framework/Versions/3.12/lib/python3.12/multiprocessing/spawn.py", line 140, in _check_not_importing_main
    raise RuntimeError('''
RuntimeError: 
        An attempt has been made to start a new process before the
        current process has finished its bootstrapping phase.

        This probably means that you are not using fork to start your
        child processes and you have forgotten to use the proper idiom
        in the main module:

            if __name__ == '__main__':
                freeze_support()
                ...

        The "freeze_support()" line can be omitted if the program
        is not going to be frozen to produce an executable.

        To fix this issue, refer to the "Safe importing of main module"
        section in https://docs.python.org/3/library/multiprocessing.html
        

The above exception was the direct cause of the following exception:

Traceback (most recent call last):
  File "<string>", line 1, in <module>
  File "/Library/Frameworks/Python.framework/Versions/3.12/lib/python3.12/multiprocessing/spawn.py", line 122, in spawn_main
    exitcode = _main(fd, parent_sentinel)
               ^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/Library/Frameworks/Python.framework/Versions/3.12/lib/python3.12/multiprocessing/spawn.py", line 131, in _main
    prepare(preparation_data)
  File "/Library/Frameworks/Python.framework/Versions/3.12/lib/python3.12/multiprocessing/spawn.py", line 246, in prepare
    _fixup_main_from_path(data['init_main_from_path'])
  File "/Library/Frameworks/Python.framework/Versions/3.12/lib/python3.12/multiprocessing/spawn.py", line 297, in _fixup_main_from_path
    main_content = runpy.run_path(main_path,
                   ^^^^^^^^^^^^^^^^^^^^^^^^^
  File "<frozen runpy>", line 286, in run_path
  File "<frozen runpy>", line 98, in _run_module_code
  File "<frozen runpy>", line 88, in _run_code
  File "/Users/yoda/LernLLMAndRAG/Chapt1/llm-based-use.py", line 4, in <module>
    model = AutoModelForCausalLM.from_pretrained("cyberagent/open-calm-small")
            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/Library/Frameworks/Python.framework/Versions/3.12/lib/python3.12/site-packages/transformers/models/auto/auto_factory.py", line 564, in from_pretrained
    return model_class.from_pretrained(
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/Library/Frameworks/Python.framework/Versions/3.12/lib/python3.12/site-packages/transformers/modeling_utils.py", line 3941, in from_pretrained
    raise EnvironmentError(
OSError: Can't load the model for 'cyberagent/open-calm-small'. If you were trying to load it from 'https://huggingface.co/models', make sure you don't have a local directory with the same name. Otherwise, make sure 'cyberagent/open-calm-small' is the correct path to a directory containing a file named pytorch_model.bin, tf_model.h5, model.ckpt or flax_model.msgpack.

```
結果こそ標準出力されるものの、大量にトレースバックが出力されました。トレースバックの内容があまりにも多すぎて、無視して進めるのも厳しいので、これが出力されないようにしたいところ。

## 躓き2
トレースバックの内容を消すために、`mkdir -p cyberagent/open-calm-small`コマンドでディレクトリを用意。
ファイル階層は以下の通り。
```plaintext:zsh
tree                           
.
├── Chapt1
│   └── llm-based-use.py
└── cyberagent
    └── open-calm-small
```
実行結果
```plaintext:zsh
Python3 ./Chapt1/llm-based-use.py  
Traceback (most recent call last):
  File "./Chapt1/llm-based-use.py", line 4, in <module>
    model = AutoModelForCausalLM.from_pretrained("cyberagent/open-calm-small")
            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/Library/Frameworks/Python.framework/Versions/3.12/lib/python3.12/site-packages/transformers/models/auto/auto_factory.py", line 526, in from_pretrained
    config, kwargs = AutoConfig.from_pretrained(
                     ^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/Library/Frameworks/Python.framework/Versions/3.12/lib/python3.12/site-packages/transformers/models/auto/configuration_auto.py", line 1051, in from_pretrained
    return CONFIG_MAPPING[pattern].from_dict(config_dict, **unused_kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/Library/Frameworks/Python.framework/Versions/3.12/lib/python3.12/site-packages/transformers/configuration_utils.py", line 734, in from_dict
    config = cls(**config_dict)
             ^^^^^^^^^^^^^^^^^^
  File "/Library/Frameworks/Python.framework/Versions/3.12/lib/python3.12/site-packages/transformers/models/rag/configuration_rag.py", line 128, in __init__
    raise ValueError(
ValueError: A configuraton of type rag cannot be instantiated because both `question_encoder` and `generator` sub-configurations were not passed, only {'attn_implementation': None}

```
事態が悪化。結果も返さなくなりました。

## 躓き3
一旦前項で作ったディレクトリは削除します。
代わりに、LLMのモデルもローカルにダウンロードして格納するためのディレクトリ「LLM」を作成します。
```shell:zsh
rm -rf cyberagent/open-calm-small
mkdir LLM
```
ファイル階層
```shell:zsh
tree
.
├── Chapt1
│   └── llm-based-use.py
└── LLM
    ├── config.json
    ├── generation_config.json
    ├── pytorch_model.bin
    ├── special_tokens_map.json
    ├── tokenizer.json
    └── tokenizer_config.json
```
LLMフォルダ内のファイルは以下から必要な6ファイルをダウンロードして格納しています。

https://huggingface.co/cyberagent/open-calm-small

モデルの指定箇所と、一部の内容をHugging Faceのサンプルに則り修正。
```python:llm-based-use.py（オリジナルの箇所に限り抜粋）
model = AutoModelForCausalLM.from_pretrained("./LLM", device_map="auto", torch_dtype=torch.float32)
tokenizer = AutoTokenizer.from_pretrained("./LLM")

input = tokenizer("東京は日本の", return_tensors="pt").to(model.device)
```
実行結果
```plaintext:zsh
Python3 ./Chapt1/llm-based-use.py
The `GPTNeoXSdpaAttention` class is deprecated in favor of simply modifying the `config._attn_implementation`attribute of the `GPTNeoXAttention` class! It will be removed in v4.48
Setting `pad_token_id` to `eos_token_id`:0 for open-end generation.
首都
```
トレースバックは出力されなくなりました。
出力結果も標準出力されるよう戻りましたが、警告メッセージは出たままです。
最初の警告は非推奨のメッセージで、ざっと見た感じではOpenCLAMが参照しているGPT-NeoXから出力されているように思えたので、手の出しようがなさげに思えます。

## 最終的にうまくいった構成
`pad_token_id`に関する警告を出力させないように、generateメソッドにパラメータ`pad_token_id=tokenizer.eos_token_id`を追加しました。
実行結果
```plaintext:zsh
Python3 ./Chapt1/llm-based-use.py
The `GPTNeoXSdpaAttention` class is deprecated in favor of simply modifying the `config._attn_implementation`attribute of the `GPTNeoXAttention` class! It will be removed in v4.48
首都
```

# 終わりに
これでようやく冒頭に紹介した書籍の4ページ目以降に進められます。
前途多難な感じは否めませんが、MacにおいてOpenCLAM-Smallは[Llama 3.2より高速で結果を返してくれている](https://qiita.com/take-yoda/items/ca3773bbfee36592c185)ので、その点に関しては使用感が良いので、ストレスフリーでプログラミングできることを期待します。

::: note warn
そもそもですが、「LLMのファインチューニングとRAG」は対象読者を、**PyTorchを使用してプログラムを作成した経験がある、初級者から中級者**としています。
初心者がハンズオンとして参考にする書籍ではないので、まずは別の入門書などを入り口にすべきでした。
:::

# 参考

https://nikkie-ftnext.hatenablog.com/entry/setup-opencalm-3b-apple-silicon-cpu