NVIDIA の Claude 3.5 Sonnet 超え(?)の Llama-3.1-Nemotron-70B-Instruct を試す
tl;dr
NVIDIA から Llama-3.1-Nemotron-70B-Instruct が公開
3 つのベンチマークで Claude 3.5 Sonnet や GPT-4o を超える性能を誇る
Arena Hard で 85.0、AlpacaEval で 57.6、MT-Bench で 8.98
最大入力トークン数は 128k で最大出力トークン数は 4k
Llama-3.1-70B-Instruct を初期方策として、Llama-3.1-Nemotron-70B-Reward と HelpSteer2-Preference プロンプトを用いて RLHF
NVIDIA NIM、transformers、Ollama、MLX から動作を試した
MLX の変換済みモデルを MLX-Community にアップロードした
モデルページとベンチマーク
論文はこちら。解説は岡野原さんの投稿がわかりやすい。
実際に動かしてみないとわからない。ということで動かしてみましょう。
NVIDIA NIM から動かしてみる
NVIDIA NIM で使えるようです。NVIDIA NIM は、LLM などの AI モデルを本番環境へデプロイすることを最適化、簡素化するためのマイクロサービスソリューション。うーむ、よくわからん。要はモデルをデプロイして、API にして使えるよ、ということのよう。気になる方は下記公式ページをご覧ください。動かしたくてうずうずしているので先に進みます。
開いたらすぐ左にプロンプトを打ち込むチャットがありますので、そちらにプロンプトを入れるだけ。
もう少し聞いてみましょう。
若干翻訳がこなれていない以外はそこそこまとも。ただ、2025 年に ITER のプラズマ実験(ファーストプラズマの点火)はちょっと厳しいかな(情報がかなり古い)。また、プロトンとヘリウムではさすがに核融合反応は起こらない。
総じてハルシネーションの低減さえできれば日本語モデルとしても十分使えるような感触を持ちました。
ではローカル環境で動かしてみます。
transformers で動かしてみる
環境構築。自分は手に馴染むので uv を使っていますがそこはよしなに。
mkdir playground-Llama-3.1-Nemotron-70B-Instruct-HF
cd playground-Llama-3.1-Nemotron-70B-Instruct-HF
uv init --python 3.11
uv add torch transformers accelerate
touch main.py
まずは公式サンプルをそのまま動かしてみたいですね。
# main.py
import torch
from transformers import AutoModelForCausalLM, AutoTokenizer
model_name = "nvidia/Llama-3.1-Nemotron-70B-Instruct-HF"
model = AutoModelForCausalLM.from_pretrained(model_name, torch_dtype=torch.bfloat16, device_map="auto")
tokenizer = AutoTokenizer.from_pretrained(model_name)
prompt = "How many r in strawberry?"
messages = [{"role": "user", "content": prompt}]
tokenized_message = tokenizer.apply_chat_template(messages, tokenize=True, add_generation_prompt=True, return_tensors="pt", return_dict=True)
response_token_ids = model.generate(tokenized_message['input_ids'].cuda(),attention_mask=tokenized_message['attention_mask'].cuda(), max_new_tokens=4096, pad_token_id = tokenizer.eos_token_id)
generated_tokens =response_token_ids[:, len(tokenized_message['input_ids'][0]):]
generated_text = tokenizer.batch_decode(generated_tokens, skip_special_tokens=True)[0]
print(generated_text)
私の環境は macOS ですので、macOS で動かしたい方は下記のように `device_map="mps"` に変更し、.cuda() の部分をto("mps") に変更してください。また、macOS は bfloat16 に対応していないので、float16 に変更することも忘れずに。
import torch
from transformers import AutoModelForCausalLM, AutoTokenizer
model_name = "nvidia/Llama-3.1-Nemotron-70B-Instruct-HF"
# model = AutoModelForCausalLM.from_pretrained(model_name, torch_dtype=torch.bfloat16, device_map="mps")
model = AutoModelForCausalLM.from_pretrained(model_name, torch_dtype=torch.float16, device_map="mps")
tokenizer = AutoTokenizer.from_pretrained(model_name)
prompt = "How many r in strawberry?"
messages = [{"role": "user", "content": prompt}]
tokenized_message = tokenizer.apply_chat_template(messages, tokenize=True, add_generation_prompt=True, return_tensors="pt", return_dict=True)
response_token_ids = model.generate(tokenized_message['input_ids'].to("mps"),attention_mask=tokenized_message['attention_mask'].to("mps"), max_new_tokens=4096, pad_token_id = tokenizer.eos_token_id)
generated_tokens =response_token_ids[:, len(tokenized_message['input_ids'][0]):]
generated_text = tokenizer.batch_decode(generated_tokens, skip_special_tokens=True)[0]
print(generated_text)
では実行しましょう。
uv run main.py
量子化なしの 70B LLM がローカル環境で推論できることを夢見た時代が私にもありました。使用メモリが 150GB を超えており、Unified Memory だとしても 192GB では有効 GPU 利用率が 75% ゆえに 144GB までしか GPU として使えません。(正確には限界突破の方法があったりするのですが、自分の環境でなぜか `Result too large` と出力され、設定できません)
仕方ありません。max tokens を 128 まで減らしてリトライです。
import torch
from transformers import AutoModelForCausalLM, AutoTokenizer
model_name = "nvidia/Llama-3.1-Nemotron-70B-Instruct-HF"
# model = AutoModelForCausalLM.from_pretrained(model_name, torch_dtype=torch.bfloat16, device_map="mps")
model = AutoModelForCausalLM.from_pretrained(model_name, torch_dtype=torch.float16, device_map="mps")
tokenizer = AutoTokenizer.from_pretrained(model_name)
prompt = "How many r in strawberry?"
messages = [{"role": "user", "content": prompt}]
tokenized_message = tokenizer.apply_chat_template(messages, tokenize=True, add_generation_prompt=True, return_tensors="pt", return_dict=True)
response_token_ids = model.generate(tokenized_message['input_ids'].to("mps"),attention_mask=tokenized_message['attention_mask'].to("mps"), max_new_tokens=128, pad_token_id = tokenizer.eos_token_id)
generated_tokens =response_token_ids[:, len(tokenized_message['input_ids'][0]):]
generated_text = tokenizer.batch_decode(generated_tokens, skip_special_tokens=True)[0]
print(generated_text)
たったこれだけの出力ですが 7 分 4 秒かかりました。悲しいですね!
ただ、結果を見てみてください。`strawberry` に `r` は 3 つなのでちゃんと答えられていますね。ちなみに ChatGPT も Claude も答えられません。これは冗談抜きにすごい。
Ollama で動かしてみる
Ollama 公式も対応しましたので試してみます。Ollama が起動している状態でまずはモデルのダウンロードをしましょう。
ollama pull nemotron
デフォルトでは Q4_K_M がダウンロードされます。自分の環境ではダウンロードにおおよそ 34 分程度かかりました。気長に待ちましょう。
ollama run nemotron --verbose "こんにちは。"
簡単にオプションを解説。`--verbose` オプションは推論にかかった時間やトークン数などの数値を見ることができます。ollama run 実行時に単に文字列を与えてあげることでプロンプトを指定することも可能です。
LLM 以前によく存在した分岐型のチャットボット感が漂っていますね!
MLX で動かしてみる
ここからは macOS をお持ちの方のみ。mlx-lm を使って推論させてみましょう。
Llama-3.1-Nemotron-70B-Instruct の MLX 変換版を MLX Community にアップロードしました。
とりあえず一番軽い 4bit 版のものを動かしてみましょう。レスポンスはまあこれまでと同じなので書きませんが、よかったら試してみてください。macOS で推論する場合 4bit だと 64GB あれば動くかと思います。
uv を使用する場合
uv run --with mlx-lm mlx_lm.generate --model mlx-community/Llama-3.1-Nemotron-70B-Instruct-HF-4bit --prompt "こんにちは。" --max-tokens 128 --temp 0.0
pip を使用する場合
pip install mlx-lm
python -m mlx_lm.generate --model mlx-community/Llama-3.1-Nemotron-70B-Instruct-HF-4bit --prompt "こんにちは。" --max-tokens 128 --temp 0.0
もし MLX に興味のある方がいらしたら ChatMLX というオープンソースの macOS 用アプリがあるので試してみてください。
以上となります。Claude 3.5 Sonnet に汎用性能で超えるかというと少し懐疑的には考えていますが、一部のタスクでは確かに超えているものもあるといった印象です。モデルのパラメータサイズはそこそこ大きいですが、NVIDIA NIM であればすぐ動かすこともできるので遊んでみてください。
直近これらのイベント↓に出没します!もしご都合の合う方がいらしたら遊びに来てください!