panco’s blog

興味が沸いたことを書く

ふらっと大学に入ってみた

社会人が思いつきで大学に入った話です。入学手続き~正式入学までの約3か月間の出来事。

暇すぎる。大学でも行くか。

2025 年 1 月 1 日、適当にググって出てきた University of the People へ入学することにした。自分の中で条件を満たしたのがこの大学だった。UoPeople については、いろいろな記事がすでにあるためここでは割愛する。一応触れておくと、入学の動機は、2025 年 1 月から一時的に仕事がほぼなくなることが確定し、枯れそうだったから。

大学選びの条件

  • 完全オンラインであること
  • 仕事と両立可能なスケジュールが組めること
  • 今すぐ入学できること(単なるわがまま)
  • ソフトウェア開発とかアルゴリズムに触れられるところ(ふんわり)

タイミングよく 1 月末から新しい学期が始まるとのことで、大学の Web サイトの案内に従って入学手続きをした。いろいろあって手続きがスムーズに進まなかったが、なんとか 1 月末から英語のコースを受講できる状態となった。元日に UoPeople の存在を知って、その日のうちに入学金を振り込み、月末には受講開始というスピード感で進められたのは、かなりありがたかった。

入学手続きでのいろいろ

  • Application の提出時点で卒業済みの他大学の成績証明書は必須ではなかった(早めにとっておくことに越したことはないが)。
  • Web サイトがバグって期限内に登録できなかったが、アドバイザーにメールして事なきを得た(2 日程度で返信が来た)。
  • Application の登録サイトが重すぎる。画面遷移に 30 秒ぐらいかかるのでイライラした。

もし計画的に入学を考えていたら

もし自分が計画的に行動できる人間だったら、英語能力の証明のため事前に外部試験を受けていただろう。実際、急いで Duolingo を受けた結果、基準点をギリギリ下回ったため、最初の学期では英語のコース(ENGL008)を受けざるを得なかった。この場合、扱いとしては正式な学生ではなく、Foundation Course の受講者ということになる。そして、このコースを合格して初めて次の学期から CS のコースが受講できる。とはいえ、学生証はゲットできたため、学割はいろいろなところで使えた。

ENGL008 をまじめに受けて、しっかり不合格判定を受ける。

数年経つとはいえ、一応英語学専攻で大学を卒業し、これが学びなおしってやつか~と思いながら毎週課題をこなしていたが、Final Exam でちゃんと点数が足りなくて落ちた。少なくともこのコースについては、毎週の課題が高得点だからと言って、Final Exam でもそうとは限らないことを思い知った。

ENGL008 を受けて

  • クラスには 40 人ぐらいいたが、日本人は自分 1 人だった。いろいろなバックグラウンドの人がいた。
  • その 40 人ぐらいのうち、毎週アクティブに課題を出す学生は半数もいなかった印象。自分を含め、5 人ぐらいが早々に提出して頑張っていた。
  • 個人的に内容はそんなに楽しくなかったかな。。
  • Final Exam の受講方法がやや難解。特に Guardian Browser なるものには、うっすら殺意すら覚えた。自分の Windows11 ではうまく作動せず、結局 Mac を使うなどした。

ENGL008 不合格をリカバリ。そして正式入学。

次の学期でもう一度同コースを受ける選択肢があったが、合格する気がしなかったため、Duolingo を再受験し、その結果を以て英語能力を証明した。費用、リードタイム、難易度の面で Duolingo が一番お手軽だと思う。1 月の入学手続き時以来、約 3 か月ぶりの受験だったが、毎日英語に触れたおかげか基準点をクリアした。5 日もかからず大学に結果が受理され、正式入学となった。

これからどうするの?

直近の学期には手続きが間に合わなかったため、その次の学期から CS の授業を受講していく予定。

bilingual-gpt-neox-4b-minigpt4 を VRAM8GB、RAM16GB の PC で動かす

bilingual-gpt-neox-4b-minigpt4 を VRAM8GB、RAM16GB の PC で動かすために実施したことを書く。

実行環境

  • PC
  • WSL 関連
    • WSL 2.2.4.0
    • Ubuntu 22.04.3 LTS
    • VSCode 1.92.2
    • conda 環境の諸々
      • accelerate 0.34.2
      • bitsandbytes 0.43.3
      • cuda-version 11.8
      • decord 0.6.0
      • huggingface_hub 0.24.6
      • iopath 0.1.10
      • omegaconf 2.3.0
      • opencv 4.10.0
      • python 3.11.3
      • pytorch 2.4.0
      • pytorch-cuda 12.4
      • sentencepiece 0.2.0
      • timm 1.0.9
      • tokenizers 0.19.1
      • transformers 4.41.2
      • webdataset 0.2.100

サンプルコードを実行した場合 → Killed

公式にある 2. Inference のコードを実行した場合、Terminal に Killed と表示される。メモリ不足で正常終了しない。

rinna/bilingual-gpt-neox-4b-minigpt4 · Hugging Face

改良版

ほかにやり方はあると思うが、以下のコードで Out of Memory を回避しつつ結果を出力することができた。

import torch
import requests
from PIL import Image
from minigpt4.processors.blip_processors import Blip2ImageEvalProcessor
from customized_mini_gpt4 import CustomizedMiniGPT4

# デフォルトのテンソル型をGPUに設定
torch.set_default_tensor_type(torch.cuda.FloatTensor)

# チェックポイントのパスを指定
ckpt_path = "./checkpoint.pth"

# CustomizedMiniGPT4, tokenizer 初期化 (8bit量子化)
model = CustomizedMiniGPT4(gpt_neox_model="./bilingual-gpt-neox-4b", low_resource=True)
tokenizer = model.gpt_neox_tokenizer

# if torch.cuda.is_available():
#    model = model.to("cuda")

# チェックポイント読み込み
if ckpt_path is not None:
    print("Load BLIP2-LLM Checkpoint: {}".format(ckpt_path))
    ckpt = torch.load(ckpt_path, map_location="cpu", weights_only=True)
    model.load_state_dict(ckpt['model'], strict=False)

# 画像前処理用のプロセッサを初期化
vis_processor = Blip2ImageEvalProcessor()

# プロンプトを作成
prompt = [
    {"speaker": "ユーザー", "text": "<Img><ImageHere></Img>これは何?"},
]
prompt = [f"{uttr['speaker']}: {uttr['text']}" for uttr in prompt]
prompt = "\n".join(prompt)
prompt = prompt + "\n" + "システム: "

# 画像をダウンロードして前処理
image_url = "https://huggingface.co/rinna/bilingual-gpt-neox-4b-minigpt4/resolve/main/sample.jpg"
raw_image = Image.open(requests.get(image_url, stream=True).raw).convert('RGB')
image = vis_processor(raw_image).unsqueeze(0).to(model.device)
image_emb = model.encode_img(image)

# コンテキストの埋め込みベクトルを生成
embs = model.get_context_emb(prompt, [image_emb])

# テキスト生成
output_ids = model.gpt_neox_model.generate(
    inputs_embeds=embs,
    max_new_tokens=512,
    do_sample=True,
    temperature=1.0,
    top_p=0.85,
    pad_token_id=tokenizer.pad_token_id,
    bos_token_id=tokenizer.bos_token_id,
    eos_token_id=tokenizer.eos_token_id
)

# 生成されたテキストをデコードして表示
output = tokenizer.decode(output_ids.tolist()[0], skip_special_tokens=True)
print(output)

ポイントは以下の通り。

  • CustomizedMiniGPT4 のパラメータに low_resource=True を設定し、8bit 量子化してメモリ消費量を抑えた
  • model = model.to("cuda") を外した
  • デフォルトのテンソル型を GPU に設定した。これを設定しないと RuntimeError が起こる
RuntimeError: Expected all tensors to be on the same device, but found at least two devices, cuda:0 and cpu! (when checking argument for argument tensors in method wrapper_CUDA_cat)

LLM 読み込み時の設定値と実行時間の検証

モデルを読み込む時の設定値をいくつか変えて実行時間を測った結果をまとめる。

検証結果サマリ

4 種類のソースコードを複数回実行し、それぞれの実行時間を計測した。また、メモリ使用状況を把握するためのスナップショットを取得した。

各ソースファイルの違いは以下の通り。

ファイル名 device_map torch_dtype quantization_config 平均実行時間(秒)
rinna_float16_auto.py auto torch.float16 - 28.869
rinna_8bit_auto.py auto - load_in_8bit=True 26.355
rinna_4bit_auto.py auto - load_in_4bit=True, bnb_4bit_compute_dtype=torch.float16 12.277
rinna_auto.py auto - - 254.038

平均実行時間は、各ファイルを 4 回ずつ実行してその平均をとった時間を指す。ただし、rinna_auto.py は 4 回中 2 回が極端に遅く最後まで実行しきれなかった(待てなかった)ため、結果が返ってきた 2 回の平均となっている。そのため、rinna_auto.py はの平均実行時間はあまりきちんとした値ではない。

平均実行時間について

今回測定した中で一番速かったのは、4bit 量子化したバージョン。torch_dtype を設定した場合と比べて約 2.36 倍速くなった。8bit 量子化したバージョンと torch_dtype を設定したバージョンでは、大きな実行時間の差は見られなかった。torch_dtype の設定も量子化もせずに実行した場合は、他と比べて極端に遅い結果となった。

出力結果について

rinna の bilingual-gpt-neox-4b を使って、「絶叫マシンは、」から始まる文章を生成した。量子化すると精度が下がるという特徴は、今回の出力結果からは判断できなかった(全体的におかしな文章だから)。

ファイル名 出力結果(例)
rinna_float16_auto.py 絶叫マシンは、
(これは、ちょっとネタバレしてますが)
『 ジェットコースターで、
ある瞬間に、ジェットコースターが止まって、
落下する。』
そして、そこを、乗り合わせた客たちが、目撃することになる。
その、落下する、ジェットコースターが、
どれも、同じ型の、
『 真っ黒な、黒いモノ 』
(その落下は、落下のスピードが
かなり遅いので、落ちるモノも、
落下が終わると
rinna_8bit_auto.py 絶叫マシンは、どれも、お値段が高いという印象があったので、
今回、体験してみて、とてもお得に感じました。
お食事も美味しくいただけました。 投稿日時:2015 年 6 月 4 日 記事 No.49873 投稿者:おーちゃん 投稿日:2015 年 6 月 4 日 投稿者:なこ 【接客】 団体客が数組とカウンターの席には、中国人
rinna_4bit_auto.py 絶叫マシンは、やはり楽しい物なんでしょうね。
「遊園地」の関連語
遊園地で絶叫マシンに乗って楽しまないと、遊園地に来た意味がありませんね
「遊園地」の関連サイト情報
「遊園地」おすすめ記事一覧
「遊園地」お役立ちリンク
絶叫マシンが待ち時間を潰す!
絶叫マシンが待ち時間を潰す!
絶叫マシンが待ち時間を潰す! 絶叫マシン待ちは遊園地でお土産物選びに時間を潰すのが一番!
絶叫マシン待ちは
rinna_auto.py 絶叫マシンは、まだ、ほとんどないに等しい状況です。
その上、お尻から出る怪光線は、本当に気味が悪いと思います。
私も、乗ってみたいけど、恐いような気がします。
この、大魔神、実は、お酒が大好きな「お酒を飲むおじさん」なんですが
そんなおじさんのことが、好きなんですね。
このおじさんが、悪者を倒すと「正義」なんです。
私は、子供の頃から「正義」と「悪」って、

以降の章では、実行したコードや実行結果などの詳細を記載する。

実行環境

  • PC
  • WSL 関連
    • WSL 2.2.4.0
    • Ubuntu 22.04.3 LTS
    • VSCode 1.92.2
    • conda 環境の諸々
      • conda 24.5.0
      • python 3.11.3
      • pytorch 2.4.0
      • pytorch-cuda 12.4
      • sentencepiece 0.2.0
      • transformers 4.41.2
      • bitsandbytes 0.43.3
      • accelerate 0.34.2
  • 汎用言語モデル rinna/bilingual-gpt-neox-4b

rinna_float16_auto.py

ソースコード

torch_dtype を設定したバージョン。

rinna_float16_auto.py

import torch
from transformers import AutoTokenizer, AutoModelForCausalLM, BitsAndBytesConfig
import time
import datetime

# メモリ使用状態を取得する
torch.cuda.memory._record_memory_history()

# 処理開始
start = time.perf_counter()

# rinnaモデルをローカルパス指定
tokenizer = AutoTokenizer.from_pretrained("./bilingual-gpt-neox-4b", use_fast=False)
model = AutoModelForCausalLM.from_pretrained("./bilingual-gpt-neox-4b", torch_dtype=torch.float16, device_map='auto')

text = "絶叫マシンは、"
token_ids = tokenizer.encode(text, add_special_tokens=False, return_tensors="pt")

with torch.no_grad():
    output_ids = model.generate(
        token_ids.to(model.device),
        max_new_tokens=100,
        min_new_tokens=100,
        do_sample=True,
        temperature=1.0,
        top_p=0.95,
        pad_token_id=tokenizer.pad_token_id,
        bos_token_id=tokenizer.bos_token_id,
        eos_token_id=tokenizer.eos_token_id
    )

output = tokenizer.decode(output_ids.tolist()[0])
print(output)

# 計測終了
end = time.perf_counter()
print(end - start)

# device_map出力
print(model.hf_device_map)

# 現在時刻取得
t_delta = datetime.timedelta(hours=9)
JST = datetime.timezone(t_delta, 'JST')
now = datetime.datetime.now(JST)
d = now.strftime('%Y%m%d%H%M%S')

# メモリのスナップショットを取得
torch.cuda.memory._dump_snapshot("./snapshot/float16_auto_" + d + ".pickle")

実行結果 1 回目

float16_auto_20240915221145

You are using the default legacy behaviour of the <class 'transformers.models.t5.tokenization_t5.T5Tokenizer'>. This is expected, and simply means that the `legacy` (previous) behavior will be used so nothing changes for you. If you want to use the new behaviour, set `legacy=False`. This should only be set if you understand what it means, and thoroughly read the reason why this was added as explained in https://github.com/huggingface/transformers/pull/24565
Some parameters are on the meta device because they were offloaded to the cpu.
絶叫マシンは、
(これは、ちょっとネタバレしてますが)
『 ジェットコースターで、
ある瞬間に、ジェットコースターが止まって、
落下する。』
そして、そこを、乗り合わせた客たちが、目撃することになる。
その、落下する、ジェットコースターが、
どれも、同じ型の、
『 真っ黒な、黒いモノ 』
(その落下は、落下のスピードが
かなり遅いので、落ちるモノも、
落下が終わると
29.240153521001048
{'gpt_neox.embed_in': 0, 'gpt_neox.emb_dropout': 0, 'gpt_neox.layers.0': 0, 'gpt_neox.layers.1': 0, 'gpt_neox.layers.2': 0, 'gpt_neox.layers.3': 0, 'gpt_neox.layers.4': 0, 'gpt_neox.layers.5': 0, 'gpt_neox.layers.6': 0, 'gpt_neox.layers.7': 0, 'gpt_neox.layers.8': 0, 'gpt_neox.layers.9': 0, 'gpt_neox.layers.10': 0, 'gpt_neox.layers.11': 0, 'gpt_neox.layers.12': 0, 'gpt_neox.layers.13': 0, 'gpt_neox.layers.14': 0, 'gpt_neox.layers.15': 0, 'gpt_neox.layers.16': 0, 'gpt_neox.layers.17': 0, 'gpt_neox.layers.18': 0, 'gpt_neox.layers.19': 0, 'gpt_neox.layers.20': 0, 'gpt_neox.layers.21': 0, 'gpt_neox.layers.22': 0, 'gpt_neox.layers.23': 0, 'gpt_neox.layers.24': 0, 'gpt_neox.layers.25': 0, 'gpt_neox.layers.26': 0, 'gpt_neox.layers.27': 0, 'gpt_neox.layers.28': 0, 'gpt_neox.layers.29': 0, 'gpt_neox.layers.30': 0, 'gpt_neox.layers.31': 'cpu', 'gpt_neox.layers.32': 'cpu', 'gpt_neox.layers.33': 'cpu', 'gpt_neox.layers.34': 'cpu', 'gpt_neox.layers.35': 'cpu', 'gpt_neox.final_layer_norm': 'cpu', 'embed_out': 'cpu'}

以下は、device_map の中身を抽出したもの。"gpt_neox.layers.30" までは GPU、"gpt_neox.layers.31" からは CPU に割り当てられている。

{
  "gpt_neox.embed_in": 0,
  "gpt_neox.emb_dropout": 0,
  "gpt_neox.layers.0": 0,
  "gpt_neox.layers.1": 0,
  "gpt_neox.layers.2": 0,
  "gpt_neox.layers.3": 0,
  "gpt_neox.layers.4": 0,
  "gpt_neox.layers.5": 0,
  "gpt_neox.layers.6": 0,
  "gpt_neox.layers.7": 0,
  "gpt_neox.layers.8": 0,
  "gpt_neox.layers.9": 0,
  "gpt_neox.layers.10": 0,
  "gpt_neox.layers.11": 0,
  "gpt_neox.layers.12": 0,
  "gpt_neox.layers.13": 0,
  "gpt_neox.layers.14": 0,
  "gpt_neox.layers.15": 0,
  "gpt_neox.layers.16": 0,
  "gpt_neox.layers.17": 0,
  "gpt_neox.layers.18": 0,
  "gpt_neox.layers.19": 0,
  "gpt_neox.layers.20": 0,
  "gpt_neox.layers.21": 0,
  "gpt_neox.layers.22": 0,
  "gpt_neox.layers.23": 0,
  "gpt_neox.layers.24": 0,
  "gpt_neox.layers.25": 0,
  "gpt_neox.layers.26": 0,
  "gpt_neox.layers.27": 0,
  "gpt_neox.layers.28": 0,
  "gpt_neox.layers.29": 0,
  "gpt_neox.layers.30": 0,
  "gpt_neox.layers.31": "cpu",
  "gpt_neox.layers.32": "cpu",
  "gpt_neox.layers.33": "cpu",
  "gpt_neox.layers.34": "cpu",
  "gpt_neox.layers.35": "cpu",
  "gpt_neox.final_layer_norm": "cpu",
  "embed_out": "cpu"
}

実行結果 2 回目

float16_auto_20240915221445

You are using the default legacy behaviour of the <class 'transformers.models.t5.tokenization_t5.T5Tokenizer'>. This is expected, and simply means that the `legacy` (previous) behavior will be used so nothing changes for you. If you want to use the new behaviour, set `legacy=False`. This should only be set if you understand what it means, and thoroughly read the reason why this was added as explained in https://github.com/huggingface/transformers/pull/24565
Some parameters are on the meta device because they were offloaded to the cpu.
絶叫マシンは、 1 日の旅行でした。 は、少し遅い 1 日で、私達 (3 時間 ) のピックアップ :13:30 、 3 時間観光。 私たちは 1 日してくれました :30:30 、 1 時間観光。 私は、ツアーに 13:30 、 3 時間に申し訳ありませんが、 1 つは、トラック、 4
29.20200488300179
{'gpt_neox.embed_in': 0, 'gpt_neox.emb_dropout': 0, 'gpt_neox.layers.0': 0, 'gpt_neox.layers.1': 0, 'gpt_neox.layers.2': 0, 'gpt_neox.layers.3': 0, 'gpt_neox.layers.4': 0, 'gpt_neox.layers.5': 0, 'gpt_neox.layers.6': 0, 'gpt_neox.layers.7': 0, 'gpt_neox.layers.8': 0, 'gpt_neox.layers.9': 0, 'gpt_neox.layers.10': 0, 'gpt_neox.layers.11': 0, 'gpt_neox.layers.12': 0, 'gpt_neox.layers.13': 0, 'gpt_neox.layers.14': 0, 'gpt_neox.layers.15': 0, 'gpt_neox.layers.16': 0, 'gpt_neox.layers.17': 0, 'gpt_neox.layers.18': 0, 'gpt_neox.layers.19': 0, 'gpt_neox.layers.20': 0, 'gpt_neox.layers.21': 0, 'gpt_neox.layers.22': 0, 'gpt_neox.layers.23': 0, 'gpt_neox.layers.24': 0, 'gpt_neox.layers.25': 0, 'gpt_neox.layers.26': 0, 'gpt_neox.layers.27': 0, 'gpt_neox.layers.28': 0, 'gpt_neox.layers.29': 0, 'gpt_neox.layers.30': 0, 'gpt_neox.layers.31': 'cpu', 'gpt_neox.layers.32': 'cpu', 'gpt_neox.layers.33': 'cpu', 'gpt_neox.layers.34': 'cpu', 'gpt_neox.layers.35': 'cpu', 'gpt_neox.final_layer_norm': 'cpu', 'embed_out': 'cpu'}

実行結果 3 回目

float16_auto_20240915221654

You are using the default legacy behaviour of the <class 'transformers.models.t5.tokenization_t5.T5Tokenizer'>. This is expected, and simply means that the `legacy` (previous) behavior will be used so nothing changes for you. If you want to use the new behaviour, set `legacy=False`. This should only be set if you understand what it means, and thoroughly read the reason why this was added as explained in https://github.com/huggingface/transformers/pull/24565
Some parameters are on the meta device because they were offloaded to the cpu.
絶叫マシンは、40フィート以下のものをさしていました。そして、それらのものは、そのように、より長く維持される必要がありました。
1974年に、私は、ウォーターワークスのプロジェクトで、40フィートのフライング・ボートを運転しているとき、この技術的な問題について、多くの質問を受けました。その答えは、「安全」でした。しかし、その安全は、その技術が必要とされる限り、1974年、1975年、そして、197
28.929758404003223
{'gpt_neox.embed_in': 0, 'gpt_neox.emb_dropout': 0, 'gpt_neox.layers.0': 0, 'gpt_neox.layers.1': 0, 'gpt_neox.layers.2': 0, 'gpt_neox.layers.3': 0, 'gpt_neox.layers.4': 0, 'gpt_neox.layers.5': 0, 'gpt_neox.layers.6': 0, 'gpt_neox.layers.7': 0, 'gpt_neox.layers.8': 0, 'gpt_neox.layers.9': 0, 'gpt_neox.layers.10': 0, 'gpt_neox.layers.11': 0, 'gpt_neox.layers.12': 0, 'gpt_neox.layers.13': 0, 'gpt_neox.layers.14': 0, 'gpt_neox.layers.15': 0, 'gpt_neox.layers.16': 0, 'gpt_neox.layers.17': 0, 'gpt_neox.layers.18': 0, 'gpt_neox.layers.19': 0, 'gpt_neox.layers.20': 0, 'gpt_neox.layers.21': 0, 'gpt_neox.layers.22': 0, 'gpt_neox.layers.23': 0, 'gpt_neox.layers.24': 0, 'gpt_neox.layers.25': 0, 'gpt_neox.layers.26': 0, 'gpt_neox.layers.27': 0, 'gpt_neox.layers.28': 0, 'gpt_neox.layers.29': 0, 'gpt_neox.layers.30': 0, 'gpt_neox.layers.31': 'cpu', 'gpt_neox.layers.32': 'cpu', 'gpt_neox.layers.33': 'cpu', 'gpt_neox.layers.34': 'cpu', 'gpt_neox.layers.35': 'cpu', 'gpt_neox.final_layer_norm': 'cpu', 'embed_out': 'cpu'}

実行結果 4 回目

float16_auto_2024091522183

You are using the default legacy behaviour of the <class 'transformers.models.t5.tokenization_t5.T5Tokenizer'>. This is expected, and simply means that the `legacy` (previous) behavior will be used so nothing changes for you. If you want to use the new behaviour, set `legacy=False`. This should only be set if you understand what it means, and thoroughly read the reason why this was added as explained in https://github.com/huggingface/transformers/pull/24565
Some parameters are on the meta device because they were offloaded to the cpu.
絶叫マシンは、これくらいしかないと断言できる。いや、ないわけではないが、このタイプのものは他にない。これは「ジェットコースター」を「お化け屋敷」に置き換えてもいい。お化け屋敷の場合、「これはお化け屋敷。お化けに怯えてもらわないと、お客さまに安心して入ってもらえない。怖いと気持ちいいは別物なのだ」というメッセージを出さないと、客は怖いもの見たさで入らない。しかし、ジェットコースターの場合は、怖がってもらわなければならないが、
28.104383083998982
{'gpt_neox.embed_in': 0, 'gpt_neox.emb_dropout': 0, 'gpt_neox.layers.0': 0, 'gpt_neox.layers.1': 0, 'gpt_neox.layers.2': 0, 'gpt_neox.layers.3': 0, 'gpt_neox.layers.4': 0, 'gpt_neox.layers.5': 0, 'gpt_neox.layers.6': 0, 'gpt_neox.layers.7': 0, 'gpt_neox.layers.8': 0, 'gpt_neox.layers.9': 0, 'gpt_neox.layers.10': 0, 'gpt_neox.layers.11': 0, 'gpt_neox.layers.12': 0, 'gpt_neox.layers.13': 0, 'gpt_neox.layers.14': 0, 'gpt_neox.layers.15': 0, 'gpt_neox.layers.16': 0, 'gpt_neox.layers.17': 0, 'gpt_neox.layers.18': 0, 'gpt_neox.layers.19': 0, 'gpt_neox.layers.20': 0, 'gpt_neox.layers.21': 0, 'gpt_neox.layers.22': 0, 'gpt_neox.layers.23': 0, 'gpt_neox.layers.24': 0, 'gpt_neox.layers.25': 0, 'gpt_neox.layers.26': 0, 'gpt_neox.layers.27': 0, 'gpt_neox.layers.28': 0, 'gpt_neox.layers.29': 0, 'gpt_neox.layers.30': 0, 'gpt_neox.layers.31': 'cpu', 'gpt_neox.layers.32': 'cpu', 'gpt_neox.layers.33': 'cpu', 'gpt_neox.layers.34': 'cpu', 'gpt_neox.layers.35': 'cpu', 'gpt_neox.final_layer_norm': 'cpu', 'embed_out': 'cpu'}

rinna_8bit_auto.py

ソースコード

8bit 量子化したバージョン。

import torch
from transformers import AutoTokenizer, AutoModelForCausalLM, BitsAndBytesConfig
import time
import datetime

# メモリ使用状態を取得する
torch.cuda.memory._record_memory_history()

# 処理開始
start = time.perf_counter()

# rinnaモデルをローカルパス指定
tokenizer = AutoTokenizer.from_pretrained("./bilingual-gpt-neox-4b", use_fast=False)

# 8bit量子化
quantization_config = BitsAndBytesConfig(load_in_8bit=True)
model = AutoModelForCausalLM.from_pretrained("./bilingual-gpt-neox-4b", quantization_config=quantization_config, device_map='auto')

text = "絶叫マシンは、"
token_ids = tokenizer.encode(text, add_special_tokens=False, return_tensors="pt")

with torch.no_grad():
    output_ids = model.generate(
        token_ids.to(model.device),
        max_new_tokens=100,
        min_new_tokens=100,
        do_sample=True,
        temperature=1.0,
        top_p=0.95,
        pad_token_id=tokenizer.pad_token_id,
        bos_token_id=tokenizer.bos_token_id,
        eos_token_id=tokenizer.eos_token_id
    )

output = tokenizer.decode(output_ids.tolist()[0])
print(output)

# 計測終了
end = time.perf_counter()
print(end - start)

# device_map出力
print(model.hf_device_map)

# 現在時刻取得
t_delta = datetime.timedelta(hours=9)
JST = datetime.timezone(t_delta, 'JST')
now = datetime.datetime.now(JST)
d = now.strftime('%Y%m%d%H%M%S')

# メモリのスナップショットを取得
torch.cuda.memory._dump_snapshot("./snapshot/8bit_auto_" + d + ".pickle")

実行結果 1 回目

You are using the default legacy behaviour of the <class 'transformers.models.t5.tokenization_t5.T5Tokenizer'>. This is expected, and simply means that the `legacy` (previous) behavior will be used so nothing changes for you. If you want to use the new behaviour, set `legacy=False`. This should only be set if you understand what it means, and thoroughly read the reason why this was added as explained in https://github.com/huggingface/transformers/pull/24565
絶叫マシンは、どれも、お値段が高いという印象があったので、
今回、体験してみて、とてもお得に感じました。
お食事も美味しくいただけました。 投稿日時:2015年6月4日 記事No.49873 投稿者:おーちゃん 投稿日:2015年6月4日 投稿者:なこ 【接客】 団体客が数組とカウンターの席には、中国人
26.849325699000474
{'': 0}
Killed

スナップショットを取得するところで Out of Memory が発生した。2 回目以降も同様の結果となった。

実行結果 2 回目

You are using the default legacy behaviour of the <class 'transformers.models.t5.tokenization_t5.T5Tokenizer'>. This is expected, and simply means that the `legacy` (previous) behavior will be used so nothing changes for you. If you want to use the new behaviour, set `legacy=False`. This should only be set if you understand what it means, and thoroughly read the reason why this was added as explained in https://github.com/huggingface/transformers/pull/24565
絶叫マシンは、ちょっとお高めでしたけど、ちょっとした楽しみにはなりそう。
お土産を買うなら、ぜひ「あられのふくちゃん」のあられをおすすめします。
あと、あられ工場に「おめでとうございます、あられ100万粒無料プレゼント!」と書いてあって「あ、あれ?これって、100万個買わされたっていう意味?」と思いましたが、どうやらそれらしい。(笑)
あられ
27.01915191900116
{'': 0}
Killed

実行結果 3 回目

You are using the default legacy behaviour of the <class 'transformers.models.t5.tokenization_t5.T5Tokenizer'>. This is expected, and simply means that the `legacy` (previous) behavior will be used so nothing changes for you. If you want to use the new behaviour, set `legacy=False`. This should only be set if you understand what it means, and thoroughly read the reason why this was added as explained in https://github.com/huggingface/transformers/pull/24565
絶叫マシンは、2018年4月に開業した新観光スポットで、観覧車や観覧車で地上27メートルの高さから琵琶湖を望むことができます。びわ湖大津館では、期間限定で、夜のライトアップされた館内を観覧車を回るナイトショーが開催されています。
びわ湖大津プリンスホテル
【滋賀】大津プリンスホテルでバイキングを楽しみたい 家族旅行にもおすすめ
JR大津駅より徒歩5分の滋賀県大津市にあるホテル。JR大津駅のすぐ近くにあります。
宿泊だけでなく、ランチも充実しており、1,
26.62877797499823
{'': 0}
Killed

実行結果 4 回目

You are using the default legacy behaviour of the <class 'transformers.models.t5.tokenization_t5.T5Tokenizer'>. This is expected, and simply means that the `legacy` (previous) behavior will be used so nothing changes for you. If you want to use the new behaviour, set `legacy=False`. This should only be set if you understand what it means, and thoroughly read the reason why this was added as explained in https://github.com/huggingface/transformers/pull/24565
絶叫マシンは、かなり高額のようですが。 私には関係ありません、はい。 では、みなさま、今宵のアデュー。 おやっさん [...] [続きを読む]
お久しぶりですね。お久しぶりすぎて、もう誰も来てないんじゃないかって心配しておりましたが、 どうにかこうにか、まだお元気でお過ごしのようで、何よりです。 さてさて、今回のお題は どうでもいいが
24.92448170100033
{'': 0}
Killed

rinna_4bit_auto.py

ソースコード

4bit 量子化したバージョン。

import torch
from transformers import AutoTokenizer, AutoModelForCausalLM, BitsAndBytesConfig
import time
import datetime

# メモリ使用状態を取得する
torch.cuda.memory._record_memory_history()

# 処理開始
start = time.perf_counter()

# rinnaモデルをローカルパス指定
tokenizer = AutoTokenizer.from_pretrained("./bilingual-gpt-neox-4b", use_fast=False)

# 4bit量子化
quantization_config = BitsAndBytesConfig(load_in_4bit=True, bnb_4bit_compute_dtype=torch.float16)
model = AutoModelForCausalLM.from_pretrained("./bilingual-gpt-neox-4b", quantization_config=quantization_config, device_map='auto')

text = "絶叫マシンは、"
token_ids = tokenizer.encode(text, add_special_tokens=False, return_tensors="pt")

with torch.no_grad():
    output_ids = model.generate(
        token_ids.to(model.device),
        max_new_tokens=100,
        min_new_tokens=100,
        do_sample=True,
        temperature=1.0,
        top_p=0.95,
        pad_token_id=tokenizer.pad_token_id,
        bos_token_id=tokenizer.bos_token_id,
        eos_token_id=tokenizer.eos_token_id
    )

output = tokenizer.decode(output_ids.tolist()[0])
print(output)

# 計測終了
end = time.perf_counter()
print(end - start)

# device_map出力
print(model.hf_device_map)

# 現在時刻取得
t_delta = datetime.timedelta(hours=9)
JST = datetime.timezone(t_delta, 'JST')
now = datetime.datetime.now(JST)
d = now.strftime('%Y%m%d%H%M%S')

# メモリのスナップショットを取得
torch.cuda.memory._dump_snapshot("./snapshot/4bit_auto_" + d + ".pickle")

実行結果 1 回目

You are using the default legacy behaviour of the <class 'transformers.models.t5.tokenization_t5.T5Tokenizer'>. This is expected, and simply means that the `legacy` (previous) behavior will be used so nothing changes for you. If you want to use the new behaviour, set `legacy=False`. This should only be set if you understand what it means, and thoroughly read the reason why this was added as explained in https://github.com/huggingface/transformers/pull/24565
絶叫マシンは、やはり楽しい物なんでしょうね。
「遊園地」の関連語
遊園地で絶叫マシンに乗って楽しまないと、遊園地に来た意味がありませんね
「遊園地」の関連サイト情報
「遊園地」おすすめ記事一覧
「遊園地」お役立ちリンク
絶叫マシンが待ち時間を潰す!
絶叫マシンが待ち時間を潰す!
絶叫マシンが待ち時間を潰す! 絶叫マシン待ちは遊園地でお土産物選びに時間を潰すのが一番!
絶叫マシン待ちは
11.630876867999177
{'': 0}

実行結果 2 回目

You are using the default legacy behaviour of the <class 'transformers.models.t5.tokenization_t5.T5Tokenizer'>. This is expected, and simply means that the `legacy` (previous) behavior will be used so nothing changes for you. If you want to use the new behaviour, set `legacy=False`. This should only be set if you understand what it means, and thoroughly read the reason why this was added as explained in https://github.com/huggingface/transformers/pull/24565
絶叫マシンは、2018年12月25日まで『ド・ドドンコ』として1日に2公演開催。
(「雪」は「雪月」に改称、同年12月16日 - 25日「超・ド・ド・ド・ド・ド・ド 雪・雪・雪・雪 ド・ド・ド・ド・ド・ド・ド・ド・ド・ド・ド・ド・ド・ド
12.504706722000265
{'': 0}

実行結果 3 回目

You are using the default legacy behaviour of the <class 'transformers.models.t5.tokenization_t5.T5Tokenizer'>. This is expected, and simply means that the `legacy` (previous) behavior will be used so nothing changes for you. If you want to use the new behaviour, set `legacy=False`. This should only be set if you understand what it means, and thoroughly read the reason why this was added as explained in https://github.com/huggingface/transformers/pull/24565
絶叫マシンは、大がかったですが、子供も大喜びで乗れて、いい思い出になりました。
天気にも恵まれ、素晴らしい眺望でした。係の方に大変お世話になりました。
食事もおいしく、バイキングでしたが、種類が豊富でした。
子供の遊ぶところもあり、子供が満足していました。
大満足です。また行きたいです。
とても綺麗でお子様も喜んで頂けたようで大変嬉しく思います。
大浴場にベビー用の
12.663078620000306
{'': 0}

実行結果 4 回目

You are using the default legacy behaviour of the <class 'transformers.models.t5.tokenization_t5.T5Tokenizer'>. This is expected, and simply means that the `legacy` (previous) behavior will be used so nothing changes for you. If you want to use the new behaviour, set `legacy=False`. This should only be set if you understand what it means, and thoroughly read the reason why this was added as explained in https://github.com/huggingface/transformers/pull/24565
絶叫マシンは、超・面白かった!
大興奮で、超・感動でした!!
あと、これまた、超・感動の、
「スピルバーグ・マジック」!
この映画、何と、
「ディズニー映画のリメイク版」だったのです!
この映画の、ミッキー・マウスは、
ディズニーの、初代ミッキーで、
この、ディズニー・キャラクターが、
超・超・超・超超超可愛いい!
映画の中の、ディズニー・キャラクターが、超・超・
12.312457019001158
{'': 0}

rinna_auto.py

torch_dtype も量子化もしていないバージョン。

ソースコード

import torch
from transformers import AutoTokenizer, AutoModelForCausalLM, BitsAndBytesConfig
import time
import datetime

# メモリ使用状態を取得する
torch.cuda.memory._record_memory_history()

# 処理開始
start = time.perf_counter()

# rinnaモデルをローカルパス指定
tokenizer = AutoTokenizer.from_pretrained("./bilingual-gpt-neox-4b", use_fast=False)
model = AutoModelForCausalLM.from_pretrained("./bilingual-gpt-neox-4b", device_map='auto')

text = "絶叫マシンは、"
token_ids = tokenizer.encode(text, add_special_tokens=False, return_tensors="pt")

with torch.no_grad():
    output_ids = model.generate(
        token_ids.to(model.device),
        max_new_tokens=100,
        min_new_tokens=100,
        do_sample=True,
        temperature=1.0,
        top_p=0.95,
        pad_token_id=tokenizer.pad_token_id,
        bos_token_id=tokenizer.bos_token_id,
        eos_token_id=tokenizer.eos_token_id
    )

output = tokenizer.decode(output_ids.tolist()[0])
print(output)

# 計測終了
end = time.perf_counter()
print(end - start)

# device_map出力
print(model.hf_device_map)

# 現在時刻取得
t_delta = datetime.timedelta(hours=9)
JST = datetime.timezone(t_delta, 'JST')
now = datetime.datetime.now(JST)
d = now.strftime('%Y%m%d%H%M%S')

# メモリのスナップショットを取得
torch.cuda.memory._dump_snapshot("./snapshot/auto_" + d + ".pickle")

実行結果 1 回目

You are using the default legacy behaviour of the <class 'transformers.models.t5.tokenization_t5.T5Tokenizer'>. This is expected, and simply means that the `legacy` (previous) behavior will be used so nothing changes for you. If you want to use the new behaviour, set `legacy=False`. This should only be set if you understand what it means, and thoroughly read the reason why this was added as explained in https://github.com/huggingface/transformers/pull/24565
Some parameters are on the meta device because they were offloaded to the disk and cpu.
絶叫マシンは、まだ、ほとんどないに等しい状況です。
その上、お尻から出る怪光線は、本当に気味が悪いと思います。
私も、乗ってみたいけど、恐いような気がします。
この、大魔神、実は、お酒が大好きな「お酒を飲むおじさん」なんですが
そんなおじさんのことが、好きなんですね。
このおじさんが、悪者を倒すと「正義」なんです。
私は、子供の頃から「正義」と「悪」って、
261.3050184269996
{'gpt_neox.embed_in': 0, 'gpt_neox.emb_dropout': 0, 'gpt_neox.layers.0': 0, 'gpt_neox.layers.1': 0, 'gpt_neox.layers.2': 0, 'gpt_neox.layers.3': 0, 'gpt_neox.layers.4': 0, 'gpt_neox.layers.5': 0, 'gpt_neox.layers.6': 0, 'gpt_neox.layers.7': 0, 'gpt_neox.layers.8': 0, 'gpt_neox.layers.9': 0, 'gpt_neox.layers.10': 0, 'gpt_neox.layers.11': 0, 'gpt_neox.layers.12': 0, 'gpt_neox.layers.13': 'cpu', 'gpt_neox.layers.14': 'cpu', 'gpt_neox.layers.15': 'cpu', 'gpt_neox.layers.16': 'cpu', 'gpt_neox.layers.17': 'cpu', 'gpt_neox.layers.18': 'cpu', 'gpt_neox.layers.19': 'cpu', 'gpt_neox.layers.20': 'cpu', 'gpt_neox.layers.21': 'cpu', 'gpt_neox.layers.22': 'cpu', 'gpt_neox.layers.23': 'cpu', 'gpt_neox.layers.24': 'cpu', 'gpt_neox.layers.25': 'cpu', 'gpt_neox.layers.26': 'cpu', 'gpt_neox.layers.27': 'disk', 'gpt_neox.layers.28': 'disk', 'gpt_neox.layers.29': 'disk', 'gpt_neox.layers.30': 'disk', 'gpt_neox.layers.31': 'disk', 'gpt_neox.layers.32': 'disk', 'gpt_neox.layers.33': 'disk', 'gpt_neox.layers.34': 'disk', 'gpt_neox.layers.35': 'disk', 'gpt_neox.final_layer_norm': 'disk', 'embed_out': 'disk'}
Killed

以下は、device_map の中身を抽出したもの。"gpt_neox.layers.12" までは GPU、"gpt_neox.layers.13" から "gpt_neox.layers.26" は CPU、"gpt_neox.layers.27" からは DISK に割り当てられている。

{
  "gpt_neox.embed_in": 0,
  "gpt_neox.emb_dropout": 0,
  "gpt_neox.layers.0": 0,
  "gpt_neox.layers.1": 0,
  "gpt_neox.layers.2": 0,
  "gpt_neox.layers.3": 0,
  "gpt_neox.layers.4": 0,
  "gpt_neox.layers.5": 0,
  "gpt_neox.layers.6": 0,
  "gpt_neox.layers.7": 0,
  "gpt_neox.layers.8": 0,
  "gpt_neox.layers.9": 0,
  "gpt_neox.layers.10": 0,
  "gpt_neox.layers.11": 0,
  "gpt_neox.layers.12": 0,
  "gpt_neox.layers.13": "cpu",
  "gpt_neox.layers.14": "cpu",
  "gpt_neox.layers.15": "cpu",
  "gpt_neox.layers.16": "cpu",
  "gpt_neox.layers.17": "cpu",
  "gpt_neox.layers.18": "cpu",
  "gpt_neox.layers.19": "cpu",
  "gpt_neox.layers.20": "cpu",
  "gpt_neox.layers.21": "cpu",
  "gpt_neox.layers.22": "cpu",
  "gpt_neox.layers.23": "cpu",
  "gpt_neox.layers.24": "cpu",
  "gpt_neox.layers.25": "cpu",
  "gpt_neox.layers.26": "cpu",
  "gpt_neox.layers.27": "disk",
  "gpt_neox.layers.28": "disk",
  "gpt_neox.layers.29": "disk",
  "gpt_neox.layers.30": "disk",
  "gpt_neox.layers.31": "disk",
  "gpt_neox.layers.32": "disk",
  "gpt_neox.layers.33": "disk",
  "gpt_neox.layers.34": "disk",
  "gpt_neox.layers.35": "disk",
  "gpt_neox.final_layer_norm": "disk",
  "embed_out": "disk"
}

実行結果 2 回目

You are using the default legacy behaviour of the <class 'transformers.models.t5.tokenization_t5.T5Tokenizer'>. This is expected, and simply means that the `legacy` (previous) behavior will be used so nothing changes for you. If you want to use the new behaviour, set `legacy=False`. This should only be set if you understand what it means, and thoroughly read the reason why this was added as explained in https://github.com/huggingface/transformers/pull/24565
Some parameters are on the meta device because they were offloaded to the cpu and disk.
絶叫マシンは、何台あるのでしょうか? もう、何台も乗ったなあ。 何台乗りましたか? 1台だけ、2台乗って、1台で帰って、1台だけ、 乗って帰ったって言うのがありましたよねえ。 いや、でも、まあ、乗った台数。 じゃあ、あと、どのくらい乗ったんですか? (笑) 今、あのぉ、111台かな? あれ、
246.77167507500053
{'gpt_neox.embed_in': 0, 'gpt_neox.emb_dropout': 0, 'gpt_neox.layers.0': 0, 'gpt_neox.layers.1': 0, 'gpt_neox.layers.2': 0, 'gpt_neox.layers.3': 0, 'gpt_neox.layers.4': 0, 'gpt_neox.layers.5': 0, 'gpt_neox.layers.6': 0, 'gpt_neox.layers.7': 0, 'gpt_neox.layers.8': 0, 'gpt_neox.layers.9': 0, 'gpt_neox.layers.10': 0, 'gpt_neox.layers.11': 0, 'gpt_neox.layers.12': 0, 'gpt_neox.layers.13': 'cpu', 'gpt_neox.layers.14': 'cpu', 'gpt_neox.layers.15': 'cpu', 'gpt_neox.layers.16': 'cpu', 'gpt_neox.layers.17': 'cpu', 'gpt_neox.layers.18': 'cpu', 'gpt_neox.layers.19': 'cpu', 'gpt_neox.layers.20': 'cpu', 'gpt_neox.layers.21': 'cpu', 'gpt_neox.layers.22': 'cpu', 'gpt_neox.layers.23': 'cpu', 'gpt_neox.layers.24': 'cpu', 'gpt_neox.layers.25': 'cpu', 'gpt_neox.layers.26': 'cpu', 'gpt_neox.layers.27': 'disk', 'gpt_neox.layers.28': 'disk', 'gpt_neox.layers.29': 'disk', 'gpt_neox.layers.30': 'disk', 'gpt_neox.layers.31': 'disk', 'gpt_neox.layers.32': 'disk', 'gpt_neox.layers.33': 'disk', 'gpt_neox.layers.34': 'disk', 'gpt_neox.layers.35': 'disk', 'gpt_neox.final_layer_norm': 'disk', 'embed_out': 'disk'}
Killed

実行結果 3 回目

10 分経っても終わらなかった

実行結果 4 回目

10 分経っても終わらなかった

参考 Active Memory Timeline

Pytorch の公式ドキュメントに記載されていた方法でスナップショットを取得し、内容を表示した結果を貼っておく。 4bit 量子化するとメモリ使用量が抑えられていることがわかる。

Understanding CUDA Memory Usage — PyTorch 2.4 documentation

4bit_auto_20240915225501

float16_auto_20240915221145

bitsandbytes トラブルシューティング (undefined symbol: cget_col_row_stats)

LLM を量子化したいと思い、bitsandbytes (version=0.43.3) を conda コマンドでインストールして実行するとエラーが起きた。エラー内容と対処を残しておく。

実行環境

  • PC
  • WSL 関連
    • WSL 2.2.4.0
    • Ubuntu 22.04.3 LTS
    • VSCode 1.92.2
    • conda 環境の諸々
      • conda 24.5.0
      • python 3.11.3
      • pytorch 2.4.0
      • pytorch-cuda 12.4
      • sentencepiece 0.2.0
      • transformers 4.41.2
  • 汎用言語モデル rinna/bilingual-gpt-neox-4b

実行したソースコードと出力結果

実行したソースコード

import torch
from transformers import AutoTokenizer, AutoModelForCausalLM, BitsAndBytesConfig

# rinnaモデルをローカルパス指定
tokenizer = AutoTokenizer.from_pretrained("./bilingual-gpt-neox-4b", use_fast=False)

# 8bit量子化
quantization_config = BitsAndBytesConfig(load_in_8bit=True)
model = AutoModelForCausalLM.from_pretrained("./bilingual-gpt-neox-4b", quantization_config=quantization_config, device_map='auto')

text = "絶叫マシンは、"
token_ids = tokenizer.encode(text, add_special_tokens=False, return_tensors="pt")

with torch.no_grad():
    output_ids = model.generate(
        token_ids.to(model.device),
        max_new_tokens=100,
        min_new_tokens=100,
        do_sample=True,
        temperature=1.0,
        top_p=0.95,
        pad_token_id=tokenizer.pad_token_id,
        bos_token_id=tokenizer.bos_token_id,
        eos_token_id=tokenizer.eos_token_id
    )

output = tokenizer.decode(output_ids.tolist()[0])
print(output)

出力結果

You are using the default legacy behaviour of the <class 'transformers.models.t5.tokenization_t5.T5Tokenizer'>. This is expected, and simply means that the `legacy` (previous) behavior will be used so nothing changes for you. If you want to use the new behaviour, set `legacy=False`. This should only be set if you understand what it means, and thoroughly read the reason why this was added as explained in https://github.com/huggingface/transformers/pull/24565
Could not find the bitsandbytes CUDA binary at PosixPath('/home/panco/anaconda3/envs/rinna-py311/lib/python3.11/site-packages/bitsandbytes/libbitsandbytes_cuda124.so')
The installed version of bitsandbytes was compiled without GPU support. 8-bit optimizers, 8-bit multiplication, and GPU quantization are unavailable.
Traceback (most recent call last):
  File "/home/panco/workspace/rinna_test.py", line 19, in <module>
    model = AutoModelForCausalLM.from_pretrained("./bilingual-gpt-neox-4b", quantization_config=quantization_config, device_map='auto')
            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/panco/anaconda3/envs/rinna-py311/lib/python3.11/site-packages/transformers/models/auto/auto_factory.py", line 563, in from_pretrained
    return model_class.from_pretrained(
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/panco/anaconda3/envs/rinna-py311/lib/python3.11/site-packages/transformers/modeling_utils.py", line 3754, in from_pretrained
    ) = cls._load_pretrained_model(
        ^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/panco/anaconda3/envs/rinna-py311/lib/python3.11/site-packages/transformers/modeling_utils.py", line 4214, in _load_pretrained_model
    new_error_msgs, offload_index, state_dict_index = _load_state_dict_into_meta_model(
                                                      ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/panco/anaconda3/envs/rinna-py311/lib/python3.11/site-packages/transformers/modeling_utils.py", line 889, in _load_state_dict_into_meta_model
    hf_quantizer.create_quantized_param(model, param, param_name, param_device, state_dict, unexpected_keys)
  File "/home/panco/anaconda3/envs/rinna-py311/lib/python3.11/site-packages/transformers/quantizers/quantizer_bnb_8bit.py", line 206, in create_quantized_param
    new_value = bnb.nn.Int8Params(new_value, requires_grad=False, **kwargs).to(target_device)
                ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/panco/anaconda3/envs/rinna-py311/lib/python3.11/site-packages/bitsandbytes/nn/modules.py", line 626, in to
    return self.cuda(device)
           ^^^^^^^^^^^^^^^^^
  File "/home/panco/anaconda3/envs/rinna-py311/lib/python3.11/site-packages/bitsandbytes/nn/modules.py", line 587, in cuda
    CB, CBt, SCB, SCBt, coo_tensorB = bnb.functional.double_quant(B)
                                      ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/panco/anaconda3/envs/rinna-py311/lib/python3.11/site-packages/bitsandbytes/functional.py", line 2523, in double_quant
    row_stats, col_stats, nnz_row_ptr = get_colrow_absmax(A, threshold=threshold)
                                        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/panco/anaconda3/envs/rinna-py311/lib/python3.11/site-packages/bitsandbytes/functional.py", line 2422, in get_colrow_absmax
    lib.cget_col_row_stats(ptrA, ptrRowStats, ptrColStats, ptrNnzrows, ct.c_float(threshold), rows, cols)
    ^^^^^^^^^^^^^^^^^^^^^^
  File "/home/panco/anaconda3/envs/rinna-py311/lib/python3.11/site-packages/bitsandbytes/cextension.py", line 73, in __getattr__
    return getattr(self._lib, item)
           ^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/panco/anaconda3/envs/rinna-py311/lib/python3.11/ctypes/__init__.py", line 389, in __getattr__
    func = self.__getitem__(name)
           ^^^^^^^^^^^^^^^^^^^^^^
  File "/home/panco/anaconda3/envs/rinna-py311/lib/python3.11/ctypes/__init__.py", line 394, in __getitem__
    func = self._FuncPtr((name_or_ordinal, self))
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
AttributeError: /home/panco/anaconda3/envs/rinna-py311/lib/python3.11/site-packages/bitsandbytes/libbitsandbytes_cpu.so: undefined symbol: cget_col_row_stats

python -m bitsandbytes の結果

Could not find the bitsandbytes CUDA binary at PosixPath('/home/panco/anaconda3/envs/rinna-py311/lib/python3.11/site-packages/bitsandbytes/libbitsandbytes_cuda124.so')
The installed version of bitsandbytes was compiled without GPU support. 8-bit optimizers, 8-bit multiplication, and GPU quantization are unavailable.
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
++++++++++++++++++ BUG REPORT INFORMATION ++++++++++++++++++
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
++++++++++++++++++++++++++ OTHER +++++++++++++++++++++++++++
CUDA specs: CUDASpecs(highest_compute_capability=(8, 9), cuda_version_string='124', cuda_version_tuple=(12, 4))
PyTorch settings found: CUDA_VERSION=124, Highest Compute Capability: (8, 9).
Library not found: /home/panco/anaconda3/envs/rinna-py311/lib/python3.11/site-packages/bitsandbytes/libbitsandbytes_cuda124.so. Maybe you need to compile it from source?
If you compiled from source, try again with `make CUDA_VERSION=DETECTED_CUDA_VERSION`,
for example, `make CUDA_VERSION=113`.

The CUDA version for the compile might depend on your conda install, if using conda.
Inspect CUDA version via `conda list | grep cuda`.
To manually override the PyTorch CUDA version please see: https://github.com/TimDettmers/bitsandbytes/blob/main/docs/source/nonpytorchcuda.mdx
The directory listed in your path is found to be non-existent: VSCODE_WSL_EXT_LOCATION/up
Found duplicate CUDA runtime files (see below).

We select the PyTorch default CUDA runtime, which is 12.4,
but this might mismatch with the CUDA version that is needed for bitsandbytes.
To override this behavior set the `BNB_CUDA_VERSION=<version string, e.g. 122>` environmental variable.

For example, if you want to use the CUDA version 122,
    BNB_CUDA_VERSION=122 python ...

OR set the environmental variable in your .bashrc:
    export BNB_CUDA_VERSION=122

In the case of a manual override, make sure you set LD_LIBRARY_PATH, e.g.
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/usr/local/cuda-11.2,
* Found CUDA runtime at: /usr/local/cuda/lib64/libcudart.so
* Found CUDA runtime at: /usr/local/cuda/lib64/libcudart.so.12
* Found CUDA runtime at: /usr/local/cuda/lib64/libcudart.so.12.6.68
* Found CUDA runtime at: /usr/local/cuda/lib64/libcudart.so
* Found CUDA runtime at: /usr/local/cuda/lib64/libcudart.so.12
* Found CUDA runtime at: /usr/local/cuda/lib64/libcudart.so.12.6.68
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
++++++++++++++++++++++ DEBUG INFO END ++++++++++++++++++++++
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Checking that the library is importable and CUDA is callable...
Traceback (most recent call last):
  File "/home/panco/anaconda3/envs/rinna-py311/lib/python3.11/site-packages/bitsandbytes/diagnostics/main.py", line 66, in main
    sanity_check()
  File "/home/panco/anaconda3/envs/rinna-py311/lib/python3.11/site-packages/bitsandbytes/diagnostics/main.py", line 40, in sanity_check
    adam.step()
  File "/home/panco/anaconda3/envs/rinna-py311/lib/python3.11/site-packages/torch/optim/optimizer.py", line 484, in wrapper
    out = func(*args, **kwargs)
          ^^^^^^^^^^^^^^^^^^^^^
  File "/home/panco/anaconda3/envs/rinna-py311/lib/python3.11/site-packages/torch/utils/_contextlib.py", line 116, in decorate_context
    return func(*args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^
  File "/home/panco/anaconda3/envs/rinna-py311/lib/python3.11/site-packages/bitsandbytes/optim/optimizer.py", line 287, in step
    self.update_step(group, p, gindex, pindex)
  File "/home/panco/anaconda3/envs/rinna-py311/lib/python3.11/site-packages/torch/utils/_contextlib.py", line 116, in decorate_context
    return func(*args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^
  File "/home/panco/anaconda3/envs/rinna-py311/lib/python3.11/site-packages/bitsandbytes/optim/optimizer.py", line 500, in update_step
    F.optimizer_update_32bit(
  File "/home/panco/anaconda3/envs/rinna-py311/lib/python3.11/site-packages/bitsandbytes/functional.py", line 1588, in optimizer_update_32bit
    optim_func = str2optimizer32bit[optimizer_name][0]
                 ^^^^^^^^^^^^^^^^^^
NameError: name 'str2optimizer32bit' is not defined
Above we output some debug information.
Please provide this info when creating an issue via https://github.com/TimDettmers/bitsandbytes/issues/new/choose
WARNING: Please be sure to sanitize sensitive info from the output before posting it.

対処

conda で入れた bitsandbytes をアンインストールし、pip コマンドでインストールした。再実行するとエラーなく正常な出力が得られた。 conda で入れたものも pip で入れたものもバージョンは、0.43.3 で同じだったが、何か違いがあったのだろう。基本的に conda でインストールしているため pip を使うのは少し抵抗があったが、結果論うまくいったように見える。

対処後の実行結果

正常に出力できた。

You are using the default legacy behaviour of the <class 'transformers.models.t5.tokenization_t5.T5Tokenizer'>. This is expected, and simply means that the `legacy` (previous) behavior will be used so nothing changes for you. If you want to use the new behaviour, set `legacy=False`. This should only be set if you understand what it means, and thoroughly read the reason why this was added as explained in https://github.com/huggingface/transformers/pull/24565
絶叫マシンは、
「ジェットコースターは無理」
といった意見もあがるほど。
その分、1日中満喫出来ます。
アトラクションの所要時間は40分前後なので、
休憩しながらのんびり過ごすことも出来ますよ。
このコースター系ではジェットコースターがおすすめ。
最高速度は77kmもある本格的なものです。
ジェットコースターでは珍しい「回転」や「上下反転」もあります。
「回転」は、コース内をグルグルと回るアトラクション。

Accelerate のオフロード関連の処理について(torch_dtype, device_map)

はじめに

LLM で遊んでいると、以下のログが表示された。このログが気になったため、調べたことを整理する。

Some parameters are on the meta device device because they were offloaded to the cpu.

実行環境

  • PC
  • WSL 関連
    • WSL 2.2.4.0
    • Ubuntu 22.04.3 LTS
    • VSCode 1.92.2
    • conda 環境の諸々
      • conda 24.5.0
      • python 3.11.3
      • pytorch 2.4.0
      • pytorch-cuda 12.4
      • sentencepiece 0.2.0
      • transformers 4.41.2
  • 汎用言語モデル rinna/bilingual-gpt-neox-4b

気になったこと

以下 2 種類のソースコードを実行すると、ターミナルに出るログがそれぞれ微妙に異なる。このログがどの処理でどういうロジックで出力されているのかを知りたいと思った。

ソースコード

ソースコード1

import torch
from transformers import AutoTokenizer, AutoModelForCausalLM

# rinnaモデルをローカルパス指定
tokenizer = AutoTokenizer.from_pretrained("./bilingual-gpt-neox-4b", use_fast=False)
model = AutoModelForCausalLM.from_pretrained("./bilingual-gpt-neox-4b", torch_dtype=torch.float16, device_map='auto')

text = "内閣総理大臣は、"
token_ids = tokenizer.encode(text, add_special_tokens=False, return_tensors="pt")

with torch.no_grad():
    output_ids = model.generate(
        token_ids.to(model.device),
        max_new_tokens=100,
        min_new_tokens=100,
        do_sample=True,
        temperature=1.0,
        top_p=0.95,
        pad_token_id=tokenizer.pad_token_id,
        bos_token_id=tokenizer.bos_token_id,
        eos_token_id=tokenizer.eos_token_id
    )

output = tokenizer.decode(output_ids.tolist()[0])
print(output)

ソースコード2

import torch
from transformers import AutoTokenizer, AutoModelForCausalLM

# rinnaモデルをローカルパス指定
tokenizer = AutoTokenizer.from_pretrained("./bilingual-gpt-neox-4b", use_fast=False)
model = AutoModelForCausalLM.from_pretrained("./bilingual-gpt-neox-4b", device_map='auto')

text = "内閣総理大臣は、"
token_ids = tokenizer.encode(text, add_special_tokens=False, return_tensors="pt")

with torch.no_grad():
    output_ids = model.generate(
        token_ids.to(model.device),
        max_new_tokens=100,
        min_new_tokens=100,
        do_sample=True,
        temperature=1.0,
        top_p=0.95,
        pad_token_id=tokenizer.pad_token_id,
        bos_token_id=tokenizer.bos_token_id,
        eos_token_id=tokenizer.eos_token_id
    )

output = tokenizer.decode(output_ids.tolist()[0])
print(output)

2 つのソースコードの違い

コード 1, 2 の違いは以下の部分。torch_dtype=torch.float16 を設定しているか否かである。

ソースコード 1

model = AutoModelForCausalLM.from_pretrained("./bilingual-gpt-neox-4b", torch_dtype=torch.float16, device_map='auto')

ソースコード 2

model = AutoModelForCausalLM.from_pretrained("./bilingual-gpt-neox-4b", device_map='auto')

出力されるログの違い

ソースコード 1 のログ

Some parameters are on the meta device device because they were offloaded to the cpu.

ソースコード 2 のログ

Some parameters are on the meta device device because they were offloaded to the cpu and disk.

ソースコード 1「いくつかのパラメータは、CPU にオフロードされたため、メタデバイスバイス上にある。」
ソースコード 2「いくつかのパラメータは、CPU とディスクにオフロードされたため、メタデバイスバイス上にある。」

"the meta device device" というのがよくわからないが、いろいろ検索してもわからなかったため一旦放置する。(そもそも "the meta device device" というワードが引っ掛からないのだが、誤植じゃないよな・・・)

2024/9/16 追記
typo だった!!

github.com

調査結果

デバッグしていろいろ調べた。結論からいうと、"Some parameters are..." のログは、Accelerate の big_modeling.py に定義されている。

/site-packages/accelerate/big_modeling.py

# warn if there is any params on the meta device
offloaded_devices_str = " and ".join(
    [device for device in set(device_map.values()) if device in ("cpu", "disk")]
)
if len(offloaded_devices_str) > 0:
    logger.warning(
        f"Some parameters are on the meta device device because they were offloaded to the {offloaded_devices_str}."
    )

ここからもう少し探る。2 つのブロックに分けてコードを見ていく。

変数 offloaded_devices_str に何を格納しているのか

offloaded_devices_str = " and ".join(
            [device for device in set(device_map.values()) if device in ("cpu", "disk")]
        )

1つ目は上記のブロック。まずは大枠から。リストに格納された何かしらの文字列を、" and " で結合して、offloaded_devices_str に格納している。

次に、何かしらの文字列について見ていく。重複排除した device_map.values() が、"cpu" か "disk" の場合、その値を device に入れている。

device_map は 関数 dispatch_model で辞書型のオブジェクトとして定義されるパラメータで、実際には以下のような値が格納されている。モデルの各レイヤーと対応するデバイスマッピングされている。

device_map

OrderedDict([('gpt_neox.embed_in', 0), ('gpt_neox.emb_dropout', 0), ('gpt_neox.layers.0', 0), ('gpt_neox.layers.1', 0), ('gpt_neox.layers.2', 0), ('gpt_neox.layers.3', 0), ('gpt_neox.layers.4', 0), ('gpt_neox.layers.5', 0), ('gpt_neox.layers.6', 0), ('gpt_neox.layers.7', 0), ('gpt_neox.layers.8', 0), ('gpt_neox.layers.9', 0), ('gpt_neox.layers.10', 0), ('gpt_neox.layers.11', 0), ('gpt_neox.layers.12', 0), ('gpt_neox.layers.13', 0), ('gpt_neox.layers.14', 0), ('gpt_neox.layers.15', 0), ('gpt_neox.layers.16', 0), ('gpt_neox.layers.17', 0), ('gpt_neox.layers.18', 0), ('gpt_neox.layers.19', 0), ('gpt_neox.layers.20', 0), ('gpt_neox.layers.21', 0), ('gpt_neox.layers.22', 0), ('gpt_neox.layers.23', 0), ('gpt_neox.layers.24', 0), ('gpt_neox.layers.25', 0), ('gpt_neox.layers.26', 0), ('gpt_neox.layers.27', 0), ('gpt_neox.layers.28', 0), ('gpt_neox.layers.29', 0), ('gpt_neox.layers.30', 0), ('gpt_neox.layers.31', 'cpu'), ('gpt_neox.layers.32', 'cpu'), ('gpt_neox.layers.33', 'cpu'), ('gpt_neox.layers.34', 'cpu'), ('gpt_neox.layers.35', 'cpu'), ('gpt_neox.final_layer_norm', 'cpu'), ('embed_out', 'cpu')])

上記の例では、'gpt_neox.embed_in'~'gpt_neox.layers.30'までは 0 (GPU), 'gpt_neox.layers.31'以降は CPU に割り当てられている。そして、変数 offloaded_devices_str には cpu が格納される。
少し脱線するが、自分の環境の場合、GPU だけでは容量が足りず CPU が動員されている。この割り当ては device_map=auto を設定していることで Accelerate がいい感じに割り振っていると理解している。

ログの出力

次に、2つ目のブロックについて見ていく。

if len(offloaded_devices_str) > 0:
    logger.warning(
        f"Some parameters are on the meta device device because they were offloaded to the {offloaded_devices_str}."
    )

前述の通り、変数 offloaded_devices_str には cpu が格納されているため、if 文の条件に合致し、ログが出力される。ログは "Some parameters are on the meta device device because they were offloaded to the cpu." となる。また、ソースコード 2 の場合は offloaded_devices_strcpu and disk が格納され、ログが出力されることがわかった。

【解消】rinna のモデルを使おうとして OutOfMemoryError にはまった話

はじめに

前回の記事の続き。

pancokeiba.hatenablog.com

Windows での環境構築に挫折したため WSL2 で構築することとした。今回はその作業内容と rinna のサンプルコードの実行結果について記載する。

実行環境

  • PC
  • WSL 関連
    • WSL 2.2.4.0
    • Ubuntu 22.04.3 LTS
    • VSCode 1.92.2
    • conda 環境の諸々
      • conda 24.5.0
      • python 3.11.3
      • pytorch 2.4.0
      • pytorch-cuda 12.4
      • sentencepiece 0.2.0
      • transformers 4.41.2
  • 汎用言語モデル rinna/bilingual-gpt-neox-4b

環境構築

実施した作業を時系列に沿って記載する。

WSL2 の導入

過去に導入済みだったが、なぜかネットワークが遅すぎてコマンド実行に数十秒かかる状態だったため、アンインストール・インストールを実施した。
以下のサイトを参考にさせていただいた。

WSL2 のインストールとアンインストール #初心者 - Qiita

CUDA toolkit のインストール

公式サイトのインストールコマンドを実行した。

CUDA Toolkit 12.6 Update 1 Downloads | NVIDIA Developer

cuDNN のインストール

公式サイトのインストールコマンドを実行した。

cuDNN 9.3.0 Downloads | NVIDIA Developer

動作確認

nvidia-smi

上記コマンドを実行すると結果が返ってきた。たぶんこれで OK なはず。

nvcc -V

上記コマンドを実行すると Command not found のエラーが出たため、~/.bashrc環境変数の設定コマンドを追記した。

~/.bashrc

export PATH=/usr/local/cuda:/usr/local/cuda/bin:$PATH
export LD_LIBRARY_PATH=/usr/local/lib:/usr/local/cuda/lib64:$LD_LIBRARY_PATH

~/.bashrcの保存後、WSL を再起動して環境変数の設定は完了。
再度 nvcc コマンドを実行すると結果が返ってきたため OK。

Anaconda のインストール

WSL でも conda 環境で実行したかったため、Anaconda を導入した。
導入手順はこちらを参考にさせていただいた。

【備忘録】WSL2にanaconda3をインストールしてcondaを使えるようにするまで #Python - Qiita

conda 環境の作成

conda の仮想環境を作成した。手順は割愛する。

Pytorch のインストール

公式サイトからインストールコマンドをコピペして実行した。

Start Locally | PyTorch

VSCode の導入

ソースを書く・ターミナルを操作する・GitHub に上げるなどの作業を VSCode に集約したかったため導入した。と言っても、Ubuntu にはインストールされているようで、codeコマンドを実行するだけで VSCode が起動された。

VSCode 起動後は、画面左下のアイコンから Connect to WSL を選択すれば OK。

資材格納

以下のディレクトリ構成で資材を格納した。

  • bilingual-gpt-neox-4b 配下
    • 手動でダウンロードした rinna モデルのファイル一式を格納した
  • rinna_test.py
    • サンプルコードが書かれた Python ファイル
.
├── bilingual-gpt-neox-4b
│   ├── README.md
│   ├── config.json
│   ├── gitattributes
│   ├── model.safetensors
│   ├── pytorch_model.bin
│   ├── rinna.png
│   ├── spiece.model
│   ├── spiece.vocab
│   ├── tokenizer_config.json
└── rinna_test.py

サンプルコードを実行

以下で公開されているサンプルコードを一部変更して実行した。

rinna/bilingual-gpt-neox-4b · Hugging Face

モデルのパスの記載部分を変更し、bilingual-gpt-neox-4b フォルダに格納されたモデルを参照するようにした。

変更後のコード

tokenizer = AutoTokenizer.from_pretrained("./bilingual-gpt-neox-4b", use_fast=False)

サンプルコード実行エラー

Killed

初めてのエラーに遭遇した。Killed と表示されてそれ以外の情報がない。

調べた限り、メモリ不足の際に Killed が表示されるようだが、詳細を確認するために、dmesgコマンドを実行した。

実行したコマンド

dmesg -T

コマンド実行結果

[Sun Sep  1 14:48:46 2024] Out of memory: Killed process 8833 (pt_main_thread) total-vm:52131812kB, anon-rss:6358652kB, file-rss:0kB, shmem-rss:0kB, UID:1000 pgtables:23936kB oom_score_adj:0

たしかにサンプルコードを実行した時間帯に、Out of Memory によってプロセスがキルされていた。今のサンプルコードだと、自分の実行環境ではメモリ不足でキルされてしまうということが分かった。

OOM Killer への対処と結果

キルされないようにサンプルコードを修正した。具体的には、torch_dtypedevice_mapの設定値を追加した。

修正後の rinna_test.py

import torch
from transformers import AutoTokenizer, AutoModelForCausalLM

# メモリ使用状態を取得する
# torch.cuda.memory._record_memory_history()

# rinnaモデルをローカルパス指定
tokenizer = AutoTokenizer.from_pretrained("./bilingual-gpt-neox-4b", use_fast=False)

# 変更前 → Killed
# model = AutoModelForCausalLM.from_pretrained("./bilingual-gpt-neox-4b")
# 変更後 → OK
model = AutoModelForCausalLM.from_pretrained("./bilingual-gpt-neox-4b", torch_dtype=torch.float16, device_map='auto')

# if torch.cuda.is_available():
#    model = model.to("cuda")

text = "西田幾多郎は、"
token_ids = tokenizer.encode(text, add_special_tokens=False, return_tensors="pt")

with torch.no_grad():
    output_ids = model.generate(
        token_ids.to(model.device),
        max_new_tokens=100,
        min_new_tokens=100,
        do_sample=True,
        temperature=1.0,
        top_p=0.95,
        pad_token_id=tokenizer.pad_token_id,
        bos_token_id=tokenizer.bos_token_id,
        eos_token_id=tokenizer.eos_token_id
    )

output = tokenizer.decode(output_ids.tolist()[0])
print(output)

# メモリのスナップショットを取得
# torch.cuda.memory._dump_snapshot("my_snapshot.pickle")

rinna_test.py 実行結果

T5Tokenizer に対するメッセージが出ているが、プログラムは正常終了し、期待する結果が出力された。

You are using the default legacy behaviour of the <class 'transformers.models.t5.tokenization_t5.T5Tokenizer'>. This is expected, and simply means that the `legacy` (previous) behavior will be used so nothing changes for you. If you want to use the new behaviour, set `legacy=False`. This should only be set if you understand what it means, and thoroughly read the reason why this was added as explained in https://github.com/huggingface/transformers/pull/24565
Some parameters are on the meta device device because they were offloaded to the cpu.
西田幾多郎は、明治初期の物理学の第一人者であり、理神論者としても知られています。
彼は、万物は全て霊であり、その本質は、永遠に変わらないものと信じてやまなかったのです。 (以上「万物の霊の本質と、人心の科学」より)
「神がかり」が、なぜ、霊能者に、必要なのか、といえば、それこそ、神秘の世界に神を思うのが、真の宗教だからです。 霊の存在は、宗教そのものです。
宗教には、いろいろな

rinna_test.py の実行時間について

Out of Memory が発生したことから、自分の実行環境はサンプルコードの実行に耐えうるスペックではなさそうということが分かった。参考程度に実行時間について記載しておく。
測定には、time.perf_counter()を使用した。5 回実行したが、どれも 30 秒近くかかっていることが分かる。

1 回目: 29.19523430600384
2 回目: 27.53580800799682
3 回目: 28.29200663699885
4 回目: 28.88365111400344
5 回目: 28.652095464000013

プログラム実行中は、メモリも GPU もかなり使っていた。16GBのGPUが必要なのか・・・

参考資料

WSL2 のインストールとアンインストール #初心者 - Qiita
WSL2 上の PyTorch に GPU を認識させて深層学習環境をつくる
anaconda - CUDA Toolkitインストール時に発生するnvcc missingエラーについて - pytorch
【備忘録】WSL2にanaconda3をインストールしてcondaを使えるようにするまで #Python - Qiita
linux - PyTorch code stops with message "Killed". What killed it? - Stack Overflow

rinna のモデルを使おうとして OutOfMemoryError にはまった話

はじめに

rinna の GPT 言語モデルを使いたくて環境構築をしたが、Windows の自分の実行環境では限界を感じたので作業内容と結果を残しておく。

Windows(あくまで自分の実行環境)ではサンプルコードは正常終了しなかった。メモリ不足で動いていない様子ということはわかったが、それ以上の解析ができなかったため断念した。この記事では、Windows での環境構築~サンプルコードの実行、断念するに至るまでの過程を記録する。

現状、代替として WSL2 (Ubuntu)で環境を再構築し、そちらでサンプルコードが正常終了することを確認した。WSL2 での環境構築の話は別の記事にする予定。結果論からの推測だが、Windows だから動かない、WSL2 だから動くということではなく、model 実行時の設定値が関連していると思われた。

追記:WSL2での環境構築の話を記事にアップした。 pancokeiba.hatenablog.com

実行環境

  • PC
  • conda 環境の諸々
    • conda 24.1.2
    • python 3.11.9
    • pytorch 2.4.0
    • pytorch-cuda 11.8
    • sentencepiece 0.2.0
    • transformers 4.44.2
  • 汎用言語モデル rinna/bilingual-gpt-neox-4b

環境構築

実施した作業を時系列に沿って記載する。

conda 環境の作成

仮想環境上で実行したかったため、conda 環境を作成した。Python のバージョンはこの時に指定した。

Pytorch のインストール

公式サイトのインストールコマンドをコピペした。自分の場合は以下のようにした。

Start Locally | PyTorch

conda install pytorch torchvision torchaudio pytorch-cuda=11.8 -c pytorch -c nvidia

公式サイトからインストールコマンドが生成できる

sentencepiece, transformers のインストール

conda コマンドでそれぞれインストールした。

Sentencepiece | Anaconda.org
Transformers | Anaconda.org

rinna モデルのダウンロード

以下から手動でダウンロードしたが、結果的にはしなくてもいい作業だった。サンプルコードを初回実行した際に取得しに行く作りとなっていたからだ。ダウンロードに 1 時間程度かかった記憶。

rinna/bilingual-gpt-neox-4b at main

資材格納

以下のディレクトリ構成で資材を格納した。

C:.
│  pytorch_version.py
│  rinna_test.py
│
└─bilingual-gpt-neox-4b
        config.json
        gitattributes
        model.safetensors
        pytorch_model.bin
        README.md
        rinna.png
        spiece.model
        spiece.vocab
        tokenizer_config.json
  • bilingual-gpt-neox-4b 配下
    • 手動でダウンロードしたファイル一式を格納した
  • rinna_test.py
    • サンプルコードが書かれた Python ファイル
  • pytorch_version.py
    • サンプルコードの実行には不要なファイル(途中で解析のために作ったもの)

サンプルコードを実行

以下で公開されているサンプルコードを一部変更して実行した。

rinna/bilingual-gpt-neox-4b · Hugging Face

具体的には、モデルのパスの記載部分を変更し、bilingual-gpt-neox-4b フォルダに格納されたモデルを参照するようにした。

変更後のコード

tokenizer = AutoTokenizer.from_pretrained("./bilingual-gpt-neox-4b", use_fast=False)

サンプルコード実行エラー

torch.OutOfMemoryError: CUDA out of memory.

TERMINAL

torch.OutOfMemoryError: CUDA out of memory. Tried to allocate 2.00 MiB. GPU 0 has a total capacity of 8.00 GiB of which 0 bytes is free. Of the allocated memory 14.45 GiB is allocated by PyTorch, and 116.28 MiB is reserved by PyTorch but unallocated. If reserved but unallocated memory is large try setting PYTORCH_CUDA_ALLOC_CONF=expandable_segments:True to avoid fragmentation.  See documentation for Memory Management  (https://pytorch.org/docs/stable/notes/cuda.html#environment-variables)

エラーメッセージをそのまま読み解いて、以下のように解釈した。

  • CUDA のメモリが不足している
  • 2 MiB 割り当てようとしたが、GPU0 は 8GiB 中空き容量が 0byte である
  • 14.45 GiB は PyTorch に割り当てられており、116.28MiB は PyTorch によって予約されているが割り当てられてはいない
  • 予約済みだが割り当てられていないメモリが大きい場合、PYTORCH_CUDA_ALLOC_CONF=expandable_segments:True を設定して回避する

torch.OutOfMemoryError への対処と結果1

エラーメッセージに従い、環境変数PYTORCH_CUDA_ALLOC_CONF=expandable_segments:Trueを設定した。

環境変数の設定方法は、conda のドキュメントから確認した。

Managing environments — conda 24.7.2.dev64 documentation

具体的な手順は以下の記事を参照。

pancokeiba.hatenablog.com

環境変数を設定しても、結果は変わらずtorch.OutOfMemoryErrorが出たままだった。

torch.OutOfMemoryError への対処と結果2

メモリの使用状況を調べるため、PyTorch が提供している方法でスナップショットを取得することを試みた。rinna_test.py にコードを追記した。

Understanding CUDA Memory Usage — PyTorch 2.4 documentation

サンプルコードの実行結果は以下の通り。
torch.cuda.memory._record_memory_history()Linux で実行されることを前提としたものだったため、Windows では使えず。Windows で環境を作るのはやめて WSL2 に移行した。GPUが使えているか、CUDAのバージョンが適切かなどいろいろ調べたが、その内容は割愛する。この記事はここまで。

TERMINAL

RuntimeError: record_context_cpp is not support on non-linux non-x86_64 platforms

参考資料

自宅PCで「rinna」の日本語言語モデルを試用、メモリ32GBあればCPUだけでも動くぞ!【イニシャルB】 - INTERNET Watch
AI初心者が日本語 LLM "rinna" 自力稼働させてみた(環境構築〜テスト稼働)|Rcat999