AI/ML実装チュートリアル集

Hugging Face TransformersとPyTorchで始める軽量LLM推論入門:Chatモデルを動かす実践チュートリアル

Tags: Hugging Face, Transformers, LLM, PyTorch, 推論, 自然言語処理, 実践チュートリアル

導入

近年、大規模言語モデル(LLM: Large Language Model)は目覚ましい発展を遂げ、様々な分野での応用が期待されています。特に、オープンソースの軽量LLMが登場したことで、リソースに制約のある環境や特定のタスクに特化したアプリケーションへの組み込みが現実的になってきました。

本記事では、AI/ML分野で広く利用されているオープンソースライブラリ「Hugging Face Transformers」と「PyTorch」を用いて、軽量なChat LLMをローカル環境で動かすための具体的な実装手順を解説します。Transformersライブラリは、多様な事前学習済みモデルとその利用に必要なツールを提供し、PyTorchはモデルの実行環境として広く採用されています。

この記事を通じて、読者の皆様は以下の知識とスキルを習得することを目指します。

Web開発エンジニアの皆様が、自身のアプリケーションにLLMの機能を組み込むための一歩として、本チュートリアルが役立つことを願っております。

環境構築

本チュートリアルを進めるために必要な環境を構築します。Pythonの仮想環境を利用し、必要なライブラリをインストールします。

1. Python仮想環境の作成と有効化

まず、プロジェクト用の仮想環境を作成し、有効化します。これにより、システムのPython環境を汚染することなく、必要なライブラリを管理できます。

# 仮想環境の作成
python -m venv llm-inference-env

# 仮想環境の有効化 (macOS/Linux)
source llm-inference-env/bin/activate

# 仮想環境の有効化 (Windows PowerShell)
# .\llm-inference-env\Scripts\Activate.ps1

# 仮想環境の有効化 (Windows Command Prompt)
# .\llm-inference-env\Scripts\activate.bat

仮想環境が有効化されると、ターミナルやコマンドプロンプトの先頭に (llm-inference-env) のような表示が追加されます。

2. 必要なライブラリのインストール

次に、Hugging Face Transformers、PyTorch、およびその他の関連ライブラリをインストールします。PyTorchのインストールには、利用可能なGPUの有無に応じて最適なコマンドを選択することが重要です。

CPU環境の場合

GPUがない、またはGPUを使用しない場合は、以下のコマンドでPyTorchをインストールします。

pip install torch torchvision torchaudio --index-url https://download.pytorch.org/whl/cpu
NVIDIA GPU(CUDA)環境の場合

NVIDIA GPU(CUDA)を利用できる場合は、高速な推論のためにGPU対応版のPyTorchをインストールします。ご自身のCUDAバージョン(例: 11.8, 12.1)に合わせてcu118cu121の部分を調整してください。

pip install torch torchvision torchaudio --index-url https://download.pytorch.org/whl/cu118

注釈: CUDAのバージョン確認はnvidia-smiコマンドで行えます。詳細な情報はPyTorch公式ウェブサイトをご確認ください。

共通のライブラリインストール

PyTorchのインストール後、transformersacceleratesentencepiecebitsandbytes をインストールします。

pip install transformers accelerate sentencepiece bitsandbytes

インストールが完了したら、pip listコマンドでインストールされたパッケージを確認できます。

pip list

実装手順(チュートリアル本体)

ここでは、stabilityai/japanese-stablelm-3b-4e1t-chatモデルを例に、LLMの推論手順をステップバイステップで解説します。このモデルは日本語に特化しており、比較的軽量でチャットタスクに適しています。

1. モデルとトークナイザーのロード

Hugging Face Transformersライブラリでは、AutoModelForCausalLMを使用してモデルを、AutoTokenizerを使用してトークナイザーを簡単にロードできます。モデルはHugging Face Hubから自動的にダウンロードされます。

import torch
from transformers import AutoTokenizer, AutoModelForCausalLM

# 使用するモデルのIDを指定します
model_name = "stabilityai/japanese-stablelm-3b-4e1t-chat"

# モデルとトークナイザーをロードします
# torch.bfloat16はメモリ効率の良いデータ型ですが、GPUが対応している必要があります。
# CPUのみの場合はtorch.float32を使用するか、指定しないことでデフォルトの型を使用します。
# load_in_8bit=True は bitsandbytes を利用してモデルを8ビット量子化でロードし、メモリ使用量を削減します。
# ただし、CPU環境ではload_in_8bitは機能しない点に注意してください。
try:
    tokenizer = AutoTokenizer.from_pretrained(model_name)
    model = AutoModelForCausalLM.from_pretrained(
        model_name,
        torch_dtype=torch.bfloat16 if torch.cuda.is_available() else torch.float32,
        load_in_8bit=torch.cuda.is_available(), # GPUがある場合のみ8bitロードを試行
        device_map="auto" # 利用可能なデバイス(GPUまたはCPU)に自動的にモデルを配置
    )
    print(f"モデル '{model_name}' をロードしました。")
    if torch.cuda.is_available():
        print(f"モデルはGPU ({model.device}) にロードされました。")
    else:
        print(f"モデルはCPU ({model.device}) にロードされました。")

except Exception as e:
    print(f"モデルのロード中にエラーが発生しました: {e}")
    print("GPU環境でない場合は、load_in_8bit=False に設定するか、torch_dtypeをtorch.float32にしてみてください。")

コード解説: * AutoTokenizer.from_pretrained(model_name): モデル名に基づいて適切なトークナイザーを自動的に選択し、ロードします。トークナイザーは、人間が理解できるテキストをモデルが処理できる数値データ(トークンID)に変換する役割を担います。 * AutoModelForCausalLM.from_pretrained(model_name, ...): モデル名に基づいて適切な因果言語モデル(Causal Language Model)を自動的に選択し、ロードします。 * torch_dtype=torch.bfloat16: モデルのパラメータをbfloat16という半精度浮動小数点数型でロードします。これにより、GPUメモリの使用量を削減し、推論速度を向上させることが可能です。ただし、GPUがbfloat16に対応している必要があります。CPU環境ではtorch.float32を使用します。 * load_in_8bit=torch.cuda.is_available(): bitsandbytesライブラリを利用し、モデルを8ビット量子化でロードします。これにより、さらにメモリ使用量を大幅に削減できます。このオプションはGPU環境でのみ有効です。 * device_map="auto": モデルを自動的に最適なデバイス(GPUまたはCPU)に割り当てます。これはaccelerateライブラリの機能です。

2. プロンプトの準備と推論の実行

チャットモデルに与えるプロンプトは、特定の形式に従う必要があります。stabilityai/japanese-stablelm-3b-4e1t-chatモデルの場合、以下のような形式が推奨されます。

ユーザー: (ユーザーの質問)
システム: (システムの応答)
ユーザー: (次のユーザーの質問)
...

以下に、単一の質問に対する推論コードの例を示します。

# 推論の実行(単一ターン)
def generate_response(user_input, model, tokenizer, max_new_tokens=128):
    if model is None or tokenizer is None:
        print("モデルまたはトークナイザーがロードされていません。")
        return "エラー: モデルが利用できません。"

    # プロンプトの形式を整えます
    prompt = f"ユーザー: {user_input}\nシステム: "

    # トークナイザーを使用してテキストをトークンIDに変換します
    # return_tensors="pt" でPyTorchテンソルとして返します
    # .to(model.device) でモデルと同じデバイスにテンソルを移動します
    input_ids = tokenizer.encode(prompt, return_tensors="pt").to(model.device)

    # モデルのgenerateメソッドを呼び出し、テキストを生成します
    # do_sample=True はサンプリングに基づいてテキストを生成することを意味し、より多様な出力が得られます
    # temperature=0.7 はサンプリングのランダム性を制御します。値を大きくするとよりランダムに、小さくするとより決定論的になります
    # top_p=0.9 は確率が高い上位p%のトークンからサンプリングを行います
    # max_new_tokens は新しく生成するトークンの最大数を指定します
    # pad_token_id はパディングトークンのIDを設定します。多くのモデルでEOSトークンと同じIDが使われます
    output = model.generate(
        input_ids,
        do_sample=True,
        temperature=0.7,
        top_p=0.9,
        max_new_tokens=max_new_tokens,
        pad_token_id=tokenizer.eos_token_id
    )

    # 生成されたトークンIDをテキストにデコードします
    # skip_special_tokens=True で特殊トークン(例: EOS, PAD)を除外します
    response = tokenizer.decode(output[0], skip_special_tokens=True)

    # 生成された応答からシステムのメッセージ部分のみを抽出します
    # プロンプトが "ユーザー: ...\nシステム: " で終わるため、それ以降がモデルの応答です。
    response_text = response.replace(prompt, "").strip()
    return response_text

# 質問例
user_question = "日本の首都はどこですか?"
generated_text = generate_response(user_question, model, tokenizer)
print(f"ユーザー: {user_question}")
print(f"システム: {generated_text}")

print("-" * 30)

user_question_2 = "AIと機械学習の違いについて簡潔に教えてください。"
generated_text_2 = generate_response(user_question_2, model, tokenizer)
print(f"ユーザー: {user_question_2}")
print(f"システム: {generated_text_2}")

コード解説: * tokenizer.encode(prompt, return_tensors="pt"): プロンプト文字列をモデルが理解できるトークンIDのリスト(PyTorchテンソル形式)に変換します。.to(model.device)で、入力テンソルをモデルと同じデバイス(CPUまたはGPU)に移動させます。 * model.generate(...): モデルのテキスト生成メソッドを呼び出します。 * do_sample=True: 確率的なサンプリングに基づいてトークンを選択します。これにより、毎回異なる、より人間らしい応答が生成されやすくなります。 * temperature: サンプリングの多様性を制御するパラメータです。値が高いほど生成されるテキストがランダムになり、低いほど予測可能になります。 * top_p: トークンの累積確率がこの値を超えるまで、最も確率の高いトークンを選択するサンプリング戦略です。 * max_new_tokens: 新しく生成されるトークンの最大数を指定します。 * pad_token_id=tokenizer.eos_token_id: パディングトークンIDを指定します。多くのモデルでは、文の終わり(EOS)トークンと同じIDがパディングに使用されます。 * tokenizer.decode(output[0], skip_special_tokens=True): 生成されたトークンIDのリストを、元のテキスト文字列にデコードします。skip_special_tokens=Trueとすることで、<s>, </s>などの特殊トークンが最終的な出力に含まれないようにします。 * response.replace(prompt, "").strip(): 生成されたテキストには入力プロンプトも含まれるため、プロンプト部分を削除し、純粋なモデルの応答だけを抽出します。

3. Hugging Face pipeline を利用した簡易推論

Hugging Face Transformersには、特定のタスクに対してモデルとトークナイザーをまとめて扱うことができるpipeline機能があります。これにより、より少ないコードで手軽に推論を実行できます。

from transformers import pipeline

# 'text-generation'タスク用のパイプラインを作成します
# model引数にロード済みのモデルインスタンスを渡すことで、再ロードを避けることができます
# device_map="auto" でデバイスの自動割り当てを行います
text_generator = pipeline(
    "text-generation",
    model=model,
    tokenizer=tokenizer,
    device_map="auto"
)

# 複数ターンの対話プロンプトの例
messages = [
    {"role": "user", "content": "AIとは何ですか?"},
    {"role": "system", "content": "AIは「人工知能」の略で、人間の知的な能力をコンピュータで模倣しようとする技術や分野を指します。学習、推論、問題解決、知覚、言語理解など、さまざまな能力を含みます。"},
    {"role": "user", "content": "機械学習との違いは何ですか?"}
]

# プロンプトをチャット形式にフォーマットします
# ChatCompletion APIのような形式で、プロンプトを構築します
# StabilityAIのモデルは、この形式に直接対応していないため、手動で結合する例を示します
formatted_prompt = ""
for message in messages:
    formatted_prompt += f"{message['role'].capitalize()}: {message['content']}\n"
formatted_prompt += "システム: " # 最後の応答をシステムに期待

print(f"--- Pipelineによる対話生成 ---")
print(f"入力プロンプト:\n{formatted_prompt}")

# パイプラインでテキストを生成します
# max_new_tokens を指定することで、生成するテキストの長さを制御できます
# do_sample=True, temperature, top_p は generate メソッドと同様の制御が可能です
outputs = text_generator(
    formatted_prompt,
    max_new_tokens=128,
    do_sample=True,
    temperature=0.7,
    top_p=0.9,
    pad_token_id=tokenizer.eos_token_id
)

# 生成結果からモデルの応答部分を抽出します
generated_text_pipeline = outputs[0]['generated_text']
response_text_pipeline = generated_text_pipeline.replace(formatted_prompt, "").strip()

print(f"\nシステム: {response_text_pipeline}")

コード解説: * pipeline("text-generation", ...): text-generationタスク用のパイプラインを初期化します。ロード済みのmodeltokenizerインスタンスを渡すことで、再ロードの手間を省きます。 * text_generator(formatted_prompt, ...): パイプラインにフォーマットされたプロンプトを渡し、テキスト生成を実行します。max_new_tokenstemperatureなどのパラメータはmodel.generate()と同様に設定できます。 * パイプラインの出力は辞書のリスト形式で返されます。outputs[0]['generated_text']で生成されたテキスト全体を取得し、その後プロンプト部分を削除してモデルの応答のみを抽出します。

動作確認と結果

上記のコードをファイル(例: llm_inference.py)に保存し、仮想環境を有効にしたターミナルで実行します。

python llm_inference.py

実行すると、以下のような出力が表示されます(具体的な応答内容はモデルやサンプリング設定によって異なります)。

モデル 'stabilityai/japanese-stablelm-3b-4e1t-chat' をロードしました。
モデルはGPU (cuda:0) にロードされました。 (または CPU にロードされました。)
ユーザー: 日本の首都はどこですか?
システム: 東京です。

------------------------------
ユーザー: AIと機械学習の違いについて簡潔に教えてください。
システム: AI(人工知能)は、人間のような知能をコンピュータで実現しようとする広範な分野です。機械学習はAIの一分野であり、データからパターンを学習し、予測や意思決定を行うアルゴリズムや技術を指します。機械学習はAIを実現するための主要な手段の一つです。

--- Pipelineによる対話生成 ---
入力プロンプト:
User: AIとは何ですか?
System: AIは「人工知能」の略で、人間の知的な能力をコンピュータで模倣しようとする技術や分野を指します。学習、推論、問題解決、知覚、言語理解など、さまざまな能力を含みます。
User: 機械学習との違いは何ですか?
システム:

システム: 機械学習はAIの中核をなす技術の一つであり、特にデータからパターンやルールを自動的に学習し、それに基づいて予測や判断を行うことを目的としています。AIという広い概念の中に機械学習が含まれる関係性です。

このように、モデルが与えられたプロンプトに対して自然な日本語で応答を生成していることが確認できます。GPU環境であれば推論は比較的速やかに行われますが、CPU環境の場合は応答生成に時間がかかることがあります。

応用例・発展的な内容

本チュートリアルで紹介した基本的なLLM推論の知識は、様々な応用へと発展させることが可能です。

  1. プロンプトエンジニアリング: LLMの性能は、与えるプロンプトの質に大きく左右されます。より複雑なタスク(要約、翻訳、コード生成など)に対応するためには、効果的なプロンプトの設計(プロンプトエンジニアリング)が不可欠です。Few-shot学習やChain-of-Thoughtプロンプティングなどの手法を学ぶことで、モデルの能力を最大限に引き出すことができます。
  2. 異なるモデルの試用: Hugging Face Hubには、Stability AIのモデル以外にも、Meta社のLlama 2やMistral AIのMistral、GoogleのGemmaなど、多様なオープンソースLLMが公開されています。それぞれのモデルは特性や性能が異なるため、目的に合わせて最適なモデルを選択し、試用することで理解を深めることができます。
  3. 量子化による最適化: load_in_8bit以外にも、bitsandbytesAutoGPTQなどのライブラリを利用して、さらに低いビット数(例: 4ビット)にモデルを量子化することで、メモリ使用量を劇的に削減し、推論速度を向上させることが可能です。これにより、より多くのモデルを限られたリソースで実行できるようになります。
  4. Webアプリケーションへの組み込み: 本チュートリアルで確立した推論ロジックは、FlaskやFastAPIといったPythonのWebフレームワークと組み合わせることで、WebアプリケーションのバックエンドとしてLLM機能を提供できます。ユーザーからの入力を受け取り、LLMで処理し、結果を返すAPIを構築することで、チャットボットやコンテンツ生成ツールなどを開発できます。
  5. RAG (Retrieval Augmented Generation) システム: LLMが知らない情報や最新の情報を扱う必要がある場合、外部のデータベースやドキュメントから関連情報を取得し、それをLLMのプロンプトに組み込むRAGという手法が有効です。LangChainやLlamaIndexといったフレームワークが、RAGシステムの構築を支援します。

これらの発展的なトピックを学ぶことで、より実践的で高性能なLLMアプリケーションを開発するスキルを身につけることができるでしょう。

トラブルシューティング/FAQ

Q1. モデルのロード時にOutOfMemoryErrorが発生しました。

A1. これは主にGPUメモリが不足している場合に発生します。以下の対策を試してみてください。 * モデルの量子化: AutoModelForCausalLM.from_pretrainedの引数にload_in_8bit=Trueを追加し、bitsandbytesライブラリを使って8ビット量子化でモデルをロードします。ただし、これはGPU環境でのみ有効です。 * より小さなモデルの利用: よりパラメータ数の少ない軽量なモデルを選択します。 * torch_dtypeの変更: torch_dtype=torch.float16(またはtorch.bfloat16)を使用して、モデルの精度を下げてメモリ使用量を削減します。ただし、GPUがこのデータ型に対応している必要があります。 * バッチサイズの削減: (複数のプロンプトを一度に処理する場合)バッチサイズを小さくします。

Q2. GPUを使用しているはずなのに、推論が遅いです(またはCPUで実行されています)。

A2. 以下の点を確認してください。 * PyTorchがGPU対応版でインストールされているか: 環境構築のセクションを確認し、pip install torch torchvision torchaudio --index-url https://download.pytorch.org/whl/cuXXX のようにCUDA対応版がインストールされているか確認してください。 * CUDAが正しくインストールされているか: nvidia-smiコマンドでGPUが認識されているか、CUDAドライバが最新か確認してください。 * device_map="auto"が指定されているか: AutoModelForCausalLM.from_pretrainedまたはpipelinedevice_map="auto"が指定されていることを確認してください。 * 入力テンソルがGPU上にあるか: input_ids = tokenizer.encode(...).to(model.device) のように、入力テンソルがモデルと同じデバイスに移動されているか確認してください。

Q3. sentencepieceに関連するエラーが発生します。

A3. sentencepieceライブラリが正しくインストールされているか確認してください。 * pip install sentencepiece を実行して、インストールされていることを確認します。一部の日本語モデルのトークナイザーは、このライブラリに依存しています。

まとめ

本記事では、Hugging Face TransformersとPyTorchを利用して、軽量なChat LLMをローカル環境で動かすための実践的なチュートリアルを提供しました。環境構築から、モデルとトークナイザーのロード、そしてmodel.generate()メソッドやpipeline機能を用いたテキスト生成まで、ステップバイステップで解説を行いました。

読者の皆様は、このチュートリアルを通じて、最新のAI/MLオープンソースライブラリを使ったLLM推論の基本的な流れを理解し、実際にコードを動かすことで具体的な実装経験を積むことができたのではないでしょうか。

LLMの技術は日々進化しており、本記事で紹介した内容はあくまでその入門に過ぎません。しかし、この基礎を足がかりに、プロンプトエンジニアリングの深化、多様なモデルの探索、さらにはRAGシステムやWebアプリケーションへの統合といった、より高度な応用に挑戦することで、AI/ML技術を自身のプロジェクトやビジネスに活用する可能性が大きく広がります。

今後も最新のAI/ML技術に触れ、実践的なスキルを磨き続けることで、皆様のWeb開発エンジニアとしてのキャリアパスをより豊かなものにしていくことを応援しております。