【2024年7月版】ノートPCのGPUで動くローカルLLMまとめ

プライベートで購入したASUS VivobookにNVIDIA GeForce RTX4060(8GB)が積まれているのですが、こんな感じの小さいGPUで動作が確認できたローカルLLMを紹介します。

なお、ローカルLLMの候補は[こちら](https://note.com/npaka/n/n1d99253ae2cf)を参考にしました。

## 前提
- CUDA 12.1.0
- WSL2
- Ubuntu 22.04
- Python 3.12.4

## 前準備

必要なライブラリをインストール
```bash
pip install torch torchvision torchaudio transformers accelerate
```

ライブラリのインポートなど
```python
# Huggingfaceのトークン認証が必要なとき(Gemmaなど)
import os
import getpass
os.environ['HF_TOKEN'] = getpass.getpass('HF_TOKEN:')

from transformers import AutoTokenizer, AutoModelForCausalLM
```

# 結果

- 動作したモデル
google/gemma-1.1-2b-it
microsoft/Phi-3-mini-4k-instruct
line-corporation/japanese-large-lm-3.6b
rinna/japanese-gpt-1b
cyberagent/open-calm-3b

- 動作しなかったモデル
google/gemma-2-9b-it (メモリ不足)
elyza/Llama-3-ELYZA-JP-8B (メモリ不足)
microsoft/Phi-3-small-8k-instruct (ライブラリの不整合)
microsoft/Phi-3-small-128k-instruct (ライブラリの不整合)
karakuri-ai/karakuri-lm-7b-apm-v0.1 (メモリ不足)
rinna/japanese-gpt-neox-3.6b (うまくGPUに乗らなかった)


# google/gemma-1.1-2b-it

```python
model_name = 'google/gemma-1.1-2b-it'
tokenizer = AutoTokenizer.from_pretrained(model_name)
model = AutoModelForCausalLM.from_pretrained(
    model_name,
    device_map='cuda',
)
chat = [
    {'role': 'user', 'content': '徳川幕府の歴代将軍を表形式で教えてください。'},
]
input_ids = tokenizer.apply_chat_template(chat, tokenize=True, return_tensors='pt').to(model.device)
outputs = model.generate(input_ids, max_new_tokens=1000)
print(tokenizer.decode(outputs[0]))
```

```bash:outout
<bos><start_of_turn>user
徳川幕府の歴代将軍を表形式で教えてください。<end_of_turn>
* 1333年 - 1335年：足利義昭
* 1335年 - 1346年：足利義元
* 1346年 - 1351年：足利義昭
* 1351年 - 1368年：足利義元
* 1368年 - 1374年：足利義昭
* 1374年 - 1380年：足利義元
* 1380年 - 1382年：足利義昭
* 1382年 - 1385年：足利義元
* 1385年 - 1394年：足利義昭<eos>
```

# microsoft/Phi-3-mini-4k-instruct

```python
torch.random.manual_seed(0) 
model = AutoModelForCausalLM.from_pretrained( 
    "microsoft/Phi-3-mini-4k-instruct",  
    device_map="cuda",  
    torch_dtype="auto",  
    trust_remote_code=True,  
) 

tokenizer = AutoTokenizer.from_pretrained("microsoft/Phi-3-mini-4k-instruct") 

messages = [ 
    {'role': 'user', 'content': '徳川幕府の歴代将軍を表形式で教えてください。'},
] 

pipe = pipeline( 
    "text-generation", 
    model=model, 
    tokenizer=tokenizer, 
) 

generation_args = { 
    "max_new_tokens": 500, 
    "return_full_text": False, 
    "temperature": 0.0, 
    "do_sample": False, 
} 

output = pipe(messages, **generation_args) 
print(output[0]['generated_text'])
```

```bash:outout
 徳川幕府の歴代将軍は以下の通りです。


- 初代将軍: 徳川家康

- 中間期の将軍: 徳川秀忠

- 後期の将軍: 徳川家慶

- 最後の将軍: 徳川家摂


これらの将軍は徳川幕府の統治を築いた重要な人物であり、それぞれの時代において異なる政策や改革を行いました。
```

# line-corporation/japanese-large-lm-3.6b

```python
model = AutoModelForCausalLM.from_pretrained("line-corporation/japanese-large-lm-3.6b", torch_dtype=torch.float16)
tokenizer = AutoTokenizer.from_pretrained("line-corporation/japanese-large-lm-3.6b", use_fast=False)
generator = pipeline("text-generation", model=model, tokenizer=tokenizer, device='cuda')
set_seed(101)

text = generator(
    "徳川幕府の歴代将軍を表形式で教えてください。",
    max_length=30,
    do_sample=True,
    pad_token_id=tokenizer.pad_token_id,
    num_return_sequences=5,
)

for t in text:
    print(t)
```

```bash:outout
{'generated_text': '徳川幕府の歴代将軍を表形式で教えてください。 徳川幕府の歴代将軍を教えてください。 表形式でお願いします。 一代目と二代目の違い'}
{'generated_text': '徳川幕府の歴代将軍を表形式で教えてください。できるだけ多く。'}
{'generated_text': '徳川幕府の歴代将軍を表形式で教えてください。'}
{'generated_text': '徳川幕府の歴代将軍を表形式で教えてください。'}
{'generated_text': '徳川幕府の歴代将軍を表形式で教えてください。 徳川家が将軍の系統を示す図で、歴代将軍の家系図のようなイメージのものを'}
```

# rinna/japanese-gpt-1b

```python
tokenizer = AutoTokenizer.from_pretrained("rinna/japanese-gpt-1b", use_fast=False)
model = AutoModelForCausalLM.from_pretrained("rinna/japanese-gpt-1b")

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_length=100,
        min_length=100,
        do_sample=True,
        top_k=500,
        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,
        bad_words_ids=[[tokenizer.unk_token_id]]
    )

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

```bash:outout
徳川幕府の歴代将軍を表形式で示すと以下のようになります。ちなみに「将軍」は、武家政権樹立以来、徳川家康を始祖として150年間の「歴代将軍」のことであるために、(実際には他の大名も出ていますから)より古いこと(そして、実際より上位である)という意味で「〜家」の「歴代」と呼ばれています。そして、各代将軍の在位期間や功績、特に功績では特に認められがたい事について年表にして表記したものが「年表」です。ただし、歴代の「将軍
```

# cyberagent/open-calm-3b

```python
model = AutoModelForCausalLM.from_pretrained("cyberagent/open-calm-3b", device_map="cuda", torch_dtype=torch.float16)
tokenizer = AutoTokenizer.from_pretrained("cyberagent/open-calm-3b")
inputs = tokenizer("徳川幕府の歴代将軍を表形式で示すと以下のようになります。", return_tensors="pt").to(model.device)
with torch.no_grad():
    tokens = model.generate(
        **inputs,
        max_new_tokens=64,
        do_sample=True,
        temperature=0.7,
        top_p=0.9,
        repetition_penalty=1.05,
        pad_token_id=tokenizer.pad_token_id,
    )
output = tokenizer.decode(tokens[0], skip_special_tokens=True)
print(output)
```

```bash:outout
徳川幕府の歴代将軍を表形式で示すと以下のようになります。
1.徳川家康(1543~1616)2、徳川秀忠・徳川家光3.、徳川吉宗4、.徳川定勝5、・徳川慶喜6、(第13代:1862-1868)(明治天皇による改元により改名した名前にあたる)、7.(新任の国務大臣として初代
```

# まとめ
今回使った8GBクラスのGPUでは、およそ3~4Bサイズのモデルまでは動くような結果となりました。
また、4B以下のモデルでも環境設定とかが理由でうまく動かなかったものがあるので、後でちゃんとやり直してみたいと思っています。