GPUも使ってFugaku-LLMを動かしてみた ～古めのGPUサポートで動かしてみた～

サーバ用GPUではサクサク動くことはわかってるFugaku-LLM。
[前回はCPUのみで動かしてみました](https://qiita.com/kit/items/02c8af51ac181f02fd18)が、
今回は自宅用GPUを追加して早くなるのかを試してみました。

# 結果：とりあえず20倍くらい高速化
自宅用GPU（Geforce 2070）を使うとFugaku-LLMは少し早くなる。

・GPUでのサポートあり実行結果：約4分
・CPUのみでの実行時間：約70分

# 実行環境
以前の実行環境を強化しました。
・GPU：Geforce 2070を追加
・メモリ：64GB追加（合計96GB）

## PCスペック：GPU追加、メモリ強化
・CPU：Intel Core i5-13500
・<font color="HotPink">メモリ：96GB（DDR4-3200×4枚）</font>← 以前は32GB
・<font color="HotPink">GPU：Geforce 2070（8GB）</font>
・SSD：NVMe 1TB
・その他：
　・メモリを3倍、古めのGPU搭載しました。

## OSなど
・OS：Ubuntu 22.04 LTS Desktop
・今回は仮想化やコンテナは利用していません。

## 実行コード
以前と同じです。

```
import datetime
import torch
from transformers import AutoModelForCausalLM, AutoTokenizer

dt = datetime.datetime.now()
start = 'start: '+str(dt)
print(start)

model_path = "Fugaku-LLM/Fugaku-LLM-13B-instruct"
dt = datetime.datetime.now()
str1 = 'after_model_path: '+str(dt)
print(str1)


tokenizer = AutoTokenizer.from_pretrained(model_path)
dt = datetime.datetime.now()
str1 = 'after_tokenizer: '+str(dt)
print(str1)


model = AutoModelForCausalLM.from_pretrained(model_path, torch_dtype=torch.bfloat16, device_map="auto")
dt = datetime.datetime.now()
str1 = 'after_model: '+str(dt)
print(str1)


model.eval()
dt = datetime.datetime.now()
str1 = 'after_model.eval(): '+str(dt)
print(str1)


system_example = "以下は、タスクを説明する指示です。要求を適切に満たす応答を書きなさい。"
instruction_example = "スーパーコンピュータ「富岳」の名前の由来を教えてください。"

prompt = f"{system_example}\n\n### 指示:\n{instruction_example}\n\n### 応答:\n"

input_ids = tokenizer.encode(prompt,
                             add_special_tokens=False,
                             return_tensors="pt")
dt = datetime.datetime.now()
str1 = 'after_input_ids: '+str(dt)
print(str1)

tokens = model.generate(
    input_ids.to(device=model.device),
    max_new_tokens=128,
    do_sample=True,
    temperature=0.1,
    top_p=1.0,
    repetition_penalty=1.0,
    top_k=0
)
dt = datetime.datetime.now()
str1 = 'after_tokens: '+str(dt)
print(str1)


out = tokenizer.decode(tokens[0], skip_special_tokens=True)
dt = datetime.datetime.now()
str1 = 'after_out: '+str(dt)
print(str1)
print(out)
```

## 実行ログ
```
tsubasa@ubuntu2204:~/fugaku$ python3 fugaku.py
start: 2024-06-04 23:25:21.430459
after_model_path: 2024-06-04 23:25:21.430477
after_tokenizer: 2024-06-04 23:25:22.056591
Loading checkpoint shards:  17%|████████▊                                            | 1/6 [00:00<00:02,  Loading checkpoint shards:  33%|█████████████████▋                                   | 2/6 [00:00Loading checkpoint shards:  83%|█████████████████████████████████████████Loading checkpoint shards: 100%|█████████████████████████████████████████████████████| 6/6 [00:00<00:00,  7.37it/s]
WARNING:root:Some parameters are on the meta device device because they were offloaded to the cpu.
after_model: 2024-06-04 23:25:23.883337
after_model.eval(): 2024-06-04 23:25:23.883999
after_input_ids: 2024-06-04 23:25:23.885092
after_tokens: 2024-06-04 23:29:28.747243
after_out: 2024-06-04 23:29:28.747494
以下は、タスクを説明する指示です。要求を適切に満たす応答を書きなさい。

### 指示:
スーパーコンピュータ「富岳」の名前の由来を教えてください。

### 応答:
「富岳」は日本の理化学研究所と富士通が共同で開発したスーパーコンピュータで、富士山の異名である「富嶽」に由来している。この名前は、スーパーコンピュータが日本の高い技術力と革新性を象徴し、世界の科学と技術の進歩に貢献することを願って付けられた。また、富士山が四季を通じて美しく、荘厳であり続けるように、「富岳」もスーパーコンピュータの安定性、スピード、性能の象徴であり続けることを願って名付けられた。

```

# 考察とこれから

今回は家庭用GPUを使って動かしてみましたが、
結果、実行時間が約1/20になりました。
しかし、まだ4分もかかっており遅い。
この原因を考えます。

## 原因1：GPUメモリが足りない。
実行前からわかっていましたが、
<strong>「Geforce 2070のGPUメモリ8GBには、
　Fugaku-LLM全てはGPUメモリには乗り切らない」</strong>です。
（完全GPU動作させるにはGPUメモリが28GBくらい必要な模様）

このため、Pytouchでは、
GPUをアクセラレーターとして使い、
足りないメモリ領域をCPUで動かしているようです。
これは、実行時に以下のようなWARNINGが出てることからもほぼ確定。

`WARNING:root:Some parameters are on the meta device device because they were offloaded to the cpu.
`

以下は実行時スクショですが、
やっぱりGPUメモリだけでは足りずCPU側のメインメモリも食っています。
まぁ、足りないものは仕方ないですから…
…サーバ用GPUとか高くて買えないし、
ストレージにスワップするよりはマシでしょう。

＜GPUメモリ利用率＞
![スクリーンショット 2024-06-04 232202.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154954/2de8e6ce-a713-ebc8-9826-9deb69772fe0.png)

＜CPU側メモリ（メインメモリ）利用率＞
![スクリーンショット 2024-06-03 233102.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154954/165dd22c-b194-a220-6ca8-ba2dab1291c4.png)


## 原因2：CPUが全然回っていない
こちらのスクショからもわかるんですが、
CPUコアの合計利用率が
トータル1コア分100%以上にならないんですよ。
なんで？？？

で、
感覚的なもの（理由はきちんと追求してない）では、
・CPU利用率がトータル100%にキャップされている
という可能性もありなんですが、
なんとなく、
・メモリ転送速度の方で詰まってね？
って気もします。
ここらへん、
CPUとメモリの利用のあたり勉強しないとわかんないっすね。
いや、ソースコード読めっていうツッコミもありますが。。。

＜CPU側メモリ（メインメモリ）利用率＞
![スクリーンショット 2024-06-03 233102.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154954/165dd22c-b194-a220-6ca8-ba2dab1291c4.png)

## 原因3：実行時に毎回メモリロードしている
これはサンプルコードをただ流用して動かしてるせいなんですが、
このコード、
<strong>「実行するたびに毎回LLMをメモリ上に展開してる」</strong>
んですよね。
これは非効率なんで、
この辺は後日改善しようと思います。

# 最後に広告
本業の方の広告になってしまうのですが、
会社関係で「メタバース・デジタルツイン講座」やってます。
メタバース・デジタルツインに興味のある人はぜひよろしくお願いします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154954/f9382d5b-6e7e-e6be-160e-cc9afb097442.png)
https://www.kcc.knowledgewing.com/icm/srv/course-application/init-detail?cd=FLM&pcd=FLMC&cscd=UMT01L