« 円形計算尺を購入してみた | トップページ | 先日の「ローカルLLM+RAG」をGPU上+より大規模モデル(13b)でも動かせるようにしてみた »

2024年5月23日 (木)

大規模言語モデル「ELYZA-7b」と自作小説「計算士と空中戦艦」で「RAG」をしてみた

今さら感はありますが、大規模言語モデル(LLM)を独自の文章データでカスタマイズする手法の一つである「RAG」を使って、実際に使えるようにするところまでやってみました。

参考にしたのは、以下のサイト。

LlamaIndexを使ってローカル環境でRAGを実行する方法 - 電通総研 テックブログ

このサイト通りやれば楽勝・・・と思っていた自分を呪いたい。

Windows版Pythonで、しかもWSL2などを使わずにRAGをやろうというのが、本来無茶なのかもしれない。
が、チピチピチャパチャパしながら、どうにかRAGの動作環境を構築しました。

ということで、「Windows上でそこそこのLLMを使ってRAGしてみたい」と言う人のために、手順をここに残します。

なお、我が家のメインPCと会社PCの両方で動かしました。
最低ラインのハードは、「4コアCPUとメモリー16GB」だと思ってください。
(正直、とても実用レベルではありませんが)

RAGとは?

その前に「RAG」ってなんやねん?という方のための解説。

RAGとは「Retrieval-Augmented Generation」の頭文字で、簡単に言うと「LLMに外部の知識データベースを検索させて回答を生成させる」手法で、学習なしで独自データを用いた文書生成を可能にする技術です。

学生に例えると、通常の機械学習が「試験勉強」ならば、RAGは「辞書や教科書を見ながら回答する」ようなものです。

前準備

うちでは、Windows版Python 3.10.9を使って構築します。まだ持っていないという方は、以下から入手が便利です。

非公式Pythonダウンロードリンク - Python downloads

以下、C:\linuxというフォルダを作って作業しているものとします(フォルダ名は英数字なら何でもOK)。
まずは、コマンドプロンプトかWindows PowerShellにて、

> cd c:\linux

と移動しておきましょう。

はじめに「仮想環境」を作ります。
すでにPythonにライブラリを入れているという方は、必ずやっておいてください。その環境が壊れる恐れがあります。
私の場合は「rag」という名前の仮想環境を作りました。コマンドプロンプトかWindows PowerShellで、以下のコマンドを実行。

> python -m venv rag

この後に、仮想環境を有効化するには、

> .\rag\Scripts\activate

と入力します。

で、まだこれだけでは足りなくて、C++のビルド環境を作ります。

以下のサイト、

Microsoft C++ Build Tools - Visual Studio

からVisual Studio Build Tools 2022のインストーラーをダウンロードします。
これを立ち上げると、「Visual Studio Build Tools 2022」ってやつの横に「変更」というボタンが出てきます。

20240520-230318

出てきたオプションの中から「C++によるデスクトップ開発」のみをチェックしてインストールを開始します。
ちょっと時間かかります(数GBのダウンロード)。

さらに以下を参考に、Windows版Gitをインストールしておきましょう。

Gitのインストール方法(Windows版) #Git - Qiita

Python環境構築

これでいよいよ、Pythonのライブラリを入れていきます。

まずは「torch」です。

今回使うのはCPU版なので、

> pip install torch==2.1.1 torchvision==0.16.1 torchaudio==2.1.1 --index-url https://download.pytorch.org/whl/cpu

でインストールします。

それ以外のライブラリですが、メモアプリか何かで、

llama-index==0.9.13
transformers==4.35.2
llama_cpp_python==0.2.20

と描き込み、「requirements.txt」という名前で保存します。
これを使って、必要なライブラリをインストールします。

> pip install -r requirements.txt

ここでllama-cpp-pythonもインストールされます。Visual Studio Build Tools入れてないと、ここで止まりますので注意。

コードを入れておく作業フォルダを作っておきます。
とりあえずここでは、「elyza-rag」とします。

コマンドプロンプト上でこのフォルダに移動し、

> cd c:\linux\elyza-rag

モデルをダウンロードしておきます。

> git lfs clone https://huggingface.co/mmnga/ELYZA-japanese-Llama-2-7b-instruct-gguf/ --include "ELYZA-japanese-Llama-2-7b-instruct-q8_0.gguf"

ダウンロードが終わると「ELYZA-japanese-Llama-2-7b-instruct-gguf」という名前のフォルダができるので、「models」というフォルダを作って、その中にこれを入れておきます。

※ 【追記】その後、上のモデルを「ELYZA-japanese-Llama-2-7b-fast-instruct-q8_0.gguf 」に変えてみましたが、ほぼ同じ性能で2倍近く高速化します。これを落とすには、
> git lfs clone https://huggingface.co/mmnga/ELYZA-japanese-Llama-2-7b-fast-instruct-gguf/ --include "ELYZA-japanese-Llama-2-7b-fast-instruct-q8_0.gguf"
と実行。以下、プログラムコード内のモデル名も修正してください。

文章ファイル準備

「RAG」というのは、独自の文章データを参照して、その中身について回答する仕組みです。
なので、独自文章データを用意しておきます。

data」というフォルダを作って、その中にUTF-8形式のテキストデータを入れておきます。

私の場合は、自作小説

計算士と空中戦艦 : 小説家になろう

からテキストダウンロードして、このdataフォルダに放り込んでおきました。全部で11万文字。

これで、ようやく準備完了です。

実行

では、いよいよ実行です。
と、その前に、実行させるプログラムコードがないですね。


import logging
import os
import sys

from llama_index import (
    LLMPredictor,
    PromptTemplate,
    ServiceContext,
    SimpleDirectoryReader,
    VectorStoreIndex,
)
from llama_index.callbacks import CallbackManager, LlamaDebugHandler
from llama_index.embeddings import HuggingFaceEmbedding
from llama_index.llms import LlamaCPP

# ログレベルの設定
logging.basicConfig(stream=sys.stdout, level=logging.DEBUG, force=True)

# ドキュメントの読み込み
documents = SimpleDirectoryReader("data").load_data()

# LLMのセットアップ
model_path = f"models/ELYZA-japanese-Llama-2-7b-instruct-gguf/ELYZA-japanese-Llama-2-7b-instruct-q8_0.gguf"
llm = LlamaCPP(
    model_path=model_path,
    temperature=0.1,
    model_kwargs={"n_ctx": 4096, "n_gpu_layers": 32},
)
llm_predictor = LLMPredictor(llm=llm)

# 埋め込みモデルの初期化
EMBEDDING_DEVICE = "cpu"
# 実行するモデルの指定とキャッシュフォルダの指定
embed_model_name = ("intfloat/multilingual-e5-large",)
cache_folder = "./sentence_transformers"
# 埋め込みモデルの作成
embed_model = HuggingFaceEmbedding(
    model_name="intfloat/multilingual-e5-large",
    cache_folder=cache_folder,
    device=EMBEDDING_DEVICE,
)

# ServiceContextのセットアップ
## debug用 Callback Managerのセットアップ
llama_debug = LlamaDebugHandler(print_trace_on_end=True)
callback_manager = CallbackManager([llama_debug])

service_context
= ServiceContext.from_defaults(
    llm_predictor=llm_predictor,
    embed_model=embed_model,
    chunk_size=500,
    chunk_overlap=20,
    callback_manager=callback_manager,
)

# インデックスの生成
index = VectorStoreIndex.from_documents(
    documents,
    service_context=service_context,
)

# 質問
temp = """
[INST]
<<SYS>>
以下の「コンテキスト情報」を元に「質問」に回答してください。
なお、コンテキスト情報に無い情報は回答に含めないでください。
また、コンテキスト情報から回答が導けない場合は「分かりません」と回答してください。
<</SYS>>
# コンテキスト情報
---------------------
{context_str}
---------------------

# 質問
{query_str}

[/INST]
"""

query_engine = index.as_query_engine(
    similarity_top_k=5, text_qa_template=PromptTemplate(temp)
)

while True:
    req_msg = input("\n## Question: ")
    if req_msg == "":
        continue
    res_msg = query_engine.query(req_msg)
    res_msg.source_nodes[0].text
    event_pairs = llama_debug.get_llm_inputs_outputs()
    print("\n## Answer: \n", str(res_msg).strip())

これを「elyza_rag.py」という名前で保存。

あとはこれを、

> python elyza_rag.py

と実行します。

一番最初の実行時にのみ、埋め込み用モデルのダウンロードが行われます。続いて、テキストファイルのベクトル化が行われ、だいたい10分ほどで「## Question:」とでて待機状態になります。

この後ろに、質問を入れます。

では、せっかくなので、この文章ファイルにしかない情報を質問してみました。

##Question: カルヒネン曹長の恩師の名前は?

20240520-230145

質問を入力してエンターキーを押した直後、なにやらずらずらと文章っぽいものが並んできます。デバッグデータが並んでいるようですが、これを見ても何のことやら、というところです。

で、この状態で待つこと数分。回答が返ってきました。

## Answer:
ラハナスト

いやまあ、正解なんですけど、なんだかそっけない答えです。

もう一つ、行きます。

## Question: キヴィネンマー要塞の司令官の名前は?

で、また数分後。

## Answer:
エクロース大佐

ちょっと惜しいなぁと思ったのが、正解は「エクロース准将」なんですよね。佐官では司令官にはなれませんから。
作中の昇進前の階級が出てきました。

とはいえ、確かに「data」フォルダ内の文章を参照し、回答していることが分かります。

ただし、回答されない場合や、目茶苦茶な答えが返ってくることがあります。そこは軽量な7b(70億パラメータ)モデルですからねぇ。

にしても質問されて答えが返ってくるまでに数分(5~8分)かかるのは、ちょっと実用的ではありません。
今回はCPUで動かしたため、こうなりましたが、CUDA対応torchとGPU版llama-cpp-pythonを導入し、そこそこのGPUがあれば、より実用的な速度で運用できるかと思います。さらに上の13bモデル辺りを使えば、より高精度な回答が作れそうです。

といっても、やっぱり小説を食わせたのがよくなかったのかな。
本来ならばビジネス文書、あるいはQ&A集のようなものを入れておき、チャットボット的に活用するのがいいかもしれません。

セキュアな環境で動かせるローカルLLMによるRAGを検討されている方なら、ぜひご参考まで。


AIビジネスチャンス 技術動向と事例に学ぶ新たな価値を生成する攻めの戦略(できるビジネス)

« 円形計算尺を購入してみた | トップページ | 先日の「ローカルLLM+RAG」をGPU上+より大規模モデル(13b)でも動かせるようにしてみた »

数値解析系」カテゴリの記事

コメント

コメントを書く

(ウェブ上には掲載しません)

« 円形計算尺を購入してみた | トップページ | 先日の「ローカルLLM+RAG」をGPU上+より大規模モデル(13b)でも動かせるようにしてみた »

当ブログ内検索

スポンサード リンク

ブログ村

無料ブログはココログ