100日チャレンジ(3日目): 日本語Llama 3をローカルPCで動かす!FlaskとStreamlitでチャットボット構築奮闘記

皆さんこんにちは!現在「100日チャレンジ」と題して、日々新しい技術習得に挑戦中の宮川です。

3日目の今日は、Metaの最新LLM「Llama 3」をベースにrinna社が日本語能力を高めたというrinna/llama-3-youko-8bモデルに注目。まずは自分の手元、ローカルマシン(CPU: Intel Core i7 12世代, GPU: Intel Iris Xe 内蔵グラフィクス)でこの大規模言語モデルを動かし、簡単なチャットボットを構築することに挑戦しました!

※1日目、2日目はブログにはまだしていません。


今日の成果

  1. rinna/llama-3-youko-8b をローカル環境で動かす。
  2. 基本的なテキスト生成を確認する。
  3. Webフレームワーク (Flask) でチャットボットを構築する。
  4. ブラウザで対話できるUI (Streamlit) を作る。

環境構築:まずは足元から

開発環境は RockyLinux + VSCode + Python 3.13 です。プロジェクトごとに依存関係をきれいに保つため、まずはPython標準のvenvを使って仮想環境を構築しました。今日の作業はこの仮想環境 (.venv) の中で行います。

# Python
# 作業ディレクトリ作成 & 移動
mkdir 003Llama3-8B-Youko && cd 003Llama3-8B-Youko
# 仮想環境作成 & アクティベート
python3.13 -m venv .venv
source .venv/bin/activate
# 必要なライブラリをインストール
pip install transformers torch accelerate sentencepiece flask streamlit

最初の関門:巨大モデルをローカルに召喚! (test_load.py)

まずはHugging Faceのtransformersライブラリを使って、モデルとトークナイザーをロードするシンプルなスクリプト (test_load.py) を作成。

# Python
# (test_load.py の抜粋)
from transformers import AutoTokenizer, AutoModelForCausalLM

model_name = "rinna/llama-3-youko-8b"
print(f"Loading model: {model_name}")
tokenizer = AutoTokenizer.from_pretrained(model_name)
# 初回実行時、ここで約16.5GBのダウンロードが発生!
model = AutoModelForCausalLM.from_pretrained(model_name)
print("Model loaded successfully.")
print(f"Model is on device: {model.device}") # -> cpu と表示

実行すると、予想通り初回はモデルのダウンロードが始まりました。我が家の100Mbps回線では数十分かかりましたね…。そして、ダウンロード後、モデルがメモリにロードされるのを待ちます。巨大モデルを動かす最初の洗礼を受けた気分です(笑)RAMの使用量もかなり気になるところ。無事にロードが完了し、Model is on device: cpu と表示されたときはホッとしました。


挑戦1:FlaskでWebチャットボット構築 (flask_chatbot.py)

モデルが動くことを確認できたので、次はWebアプリケーションフレームワークの Flask を使って、ブラウザから対話できるチャットボットを作成しました。簡単なHTMLフロントエンド (templates/index.html) も用意し、そこからの入力を受け付けて応答を返す仕組みです。

# Python
# (Flask アプリの抜粋)
from flask import Flask, request, jsonify, render_template
import torch
from transformers import AutoTokenizer, AutoModelForCausalLM
import logging

app = Flask(__name__)
logging.basicConfig(level=logging.INFO)

# モデルとトークナイザーのロード (メモリ効率化とGPU利用を試行)
model_name = "rinna/llama-3-youko-8b"
tokenizer = AutoTokenizer.from_pretrained(model_name)
model = AutoModelForCausalLM.from_pretrained(
    model_name,
    torch_dtype=torch.bfloat16, # メモリ使用量削減のため半精度(bfloat16)を指定
    device_map="auto" # GPUがあれば自動で割り当て (今回はCPUのはず)
)

@app.route("/", methods=["GET"])
def home():
    return render_template("index.html") # HTMLを表示

@app.route("/chat", methods=["POST"])
def chat():
    user_input = request.json.get("message", "")
    logging.info(f"User input: {user_input}")
    # モデルで応答を生成 (パラメータも設定)
    inputs = tokenizer(user_input, return_tensors="pt").to(model.device)
    outputs = model.generate(
        **inputs,
        max_new_tokens=50,  # 最大生成トークン数
        temperature=0.7,    # ランダム性 (低いほど決定的)
        top_p=0.9,          # 上位p%からサンプリング
        do_sample=True      # サンプリングを有効化
    )
    response = tokenizer.decode(outputs[0], skip_special_tokens=True)
    logging.info(f"Bot response: {response}")
    return jsonify({"response": response})

if __name__ == "__main__":
    app.run(debug=True, host='0.0.0.0', port=5000) # 例

このコードでは、モデルロード時に torch_dtype=torch.bfloat16 を指定してメモリ使用量を抑えようとしたり、device_map=”auto” でGPU利用(今回はCPUですが)を考慮しています。また、応答生成時には temperature や top_p などのパラメータを設定し、応答の多様性を調整しようと試みました。flask run でサーバーを起動し、ブラウザからアクセスして動作を確認。ちゃんと応答が返ってきましたが、やはりここでも応答速度が課題となりました。


挑戦2:StreamlitでインタラクティブUI (app.py)

Flaskで基本的なWebアプリは作れましたが、もっと手軽にインタラクティブなUIを作りたいと思い、次は Streamlit を試してみました。PythonコードだけでリッチなUIが作れるのが魅力です。

# Python
# (st_app.py Streamlit アプリの抜粋)
import streamlit as st
# ... (Flaskアプリと同様に model, tokenizer のロード処理) ...

st.title("🦙 Llama 3 Youko ローカルチャット (Streamlit版)")

# チャット履歴の初期化 (st.session_state を活用)
if "messages" not in st.session_state:
    st.session_state.messages = []

# ユーザーからの入力 (st.chat_input を使用)
if prompt := st.chat_input("メッセージを入力してください"):
    # ... (Flask同様にモデルに応答を生成させる処理) ...
    # ... (ユーザー入力とモデル応答を st.session_state.messages に追加) ...

# チャット履歴の表示 (st.chat_message を使用)
for message in st.session_state.messages:
    with st.chat_message(message["role"]):
        st.markdown(message["content"])

st.chat_input や st.chat_message といった便利なコンポーネントのおかげで、Flaskの時よりも少ないコード量で、洗練されたチャットUIを実装できました。状態管理には st.session_state を利用。streamlit run st_app.py で起動し、こちらもブラウザで動作確認。やはり手軽さではStreamlitに軍配が上がりますね!


ローカル実行の現実:忍耐力が試される対話

さて、FlaskとStreamlit、2つの方法でチャットボットを動かしてみての感想です。どちらのアプリでも、ローカルマシン(GPUなし)での応答時間は、プロンプトにもよりますが8秒から、長いと25秒以上かかりました。これは、対話としてはかなり厳しい速度です…。原因はやはりCPUでの推論処理と、もしかしたらRAMのスワップなども影響しているかもしれません。

生成される応答も、時折「おっ」と思わせる自然な日本語が出てくる一方で、少し話が噛み合わなかったり、唐突な内容になったりすることも。Flask側で生成パラメータ(temperature, top_p など)を調整してみましたが、応答速度が遅いため、最適なパラメータを見つけるための試行錯誤に時間がかかりすぎてしまい、ローカル環境でのチューニングは現実的ではないと感じました。


3日目の学びと気づき

  • ローカルLLMの壁: 高性能GPUなしで大規模モデルを快適に動かすのは難しい。理論と実体験で痛感。
  • Flask vs Streamlit: しっかりしたWebアプリやAPIならFlask、手軽なUIプロトタイプやデモならStreamlitと、目的に応じた使い分けが有効。
  • Webフレームワークの選択肢: 今回は試せませんでしたが、より高速なAPIサーバー構築にはFastAPI、UI構築の別候補としてGradioなども比較検討してみたい。
  • モデルロードの工夫: torch_dtype 指定によるメモリ削減効果は確認できた(かもしれない)。device_map はGPUがないと意味がないが、コードの汎用性は上がる。

次なる挑戦:Colabでの高速化とRAGによる”教育”へ

今回のローカルでの挑戦を踏まえ、次はGoogle Colabの有料プラン(Pro/Pro+) を利用して、高性能GPU(例えば A100V100 など、強力なGPUがあります)で、どれだけ応答速度が改善されるか試してみたいと思います!ローカルであれだけ待たされた分、期待が高まります。

さらに将来的には、単に対話するだけでなく、RAG(Retrieval-Augmented Generation) 技術を使って、このチャットボットに特定の知識、例えば私自身のブログ記事の内容や技術メモなどを “教育” して、よりパーソナルで賢いAIアシスタントに育てていくことにも挑戦したいと考えています。


まとめとコード

100日チャレンジ3日目は、ローカル環境でのLLM実行の難しさと、Webアプリ化の手軽さの両方を体験する、非常に学びの多い一日となりました。FlaskとStreamlitという異なるアプローチでチャットボットを形にできたのは大きな収穫です。

今日作成したコードはGitHubにアップロードしています。まだまだ荒削りですが、興味のある方はぜひ覗いてみてください!

皆さんのLLM活用術やローカルでの奮闘記なども、ぜひコメントで教えていただけると嬉しいです!

「100日チャレンジ(3日目): 日本語Llama 3をローカルPCで動かす!FlaskとStreamlitでチャットボット構築奮闘記」への1件のフィードバック

  1. ピンバック: ローカルLLM 100日チャレンジ Day 4: Colab GPUで応答速度劇的改善! でもモデルのクセと環境構築の壁も… | MIYAKAWA AI

コメントする

メールアドレスが公開されることはありません。 が付いている欄は必須項目です

上部へスクロール