CPU を使用して Llama 2 モデルをローカルで実行し、ドキュメント Q&A を実装します。

OpenAI の GPT4 などのサードパーティの商用大規模言語モデル (LLM) プロバイダーを使用すると、単純な API 呼び出しを通じて LLM を簡単に使用できるようになります。ただし、データ プライバシーやコンプライアンスなどのさまざまな理由により、モデル推論をオンプレミスまたはプライベートで展開する必要がある場合があります。

オープンソース LLM の人気により、大規模な言語モデルを非公開で展開できるようになり、これらのサードパーティ プロバイダーへの依存が軽減されます。

オープンソース モデルをオンプレミスまたはクラウドでホストする場合、専用のコンピューティング能力が重要な考慮事項になります。GPU インスタンスが最適な選択かもしれませんが、コストが高騰しやすい上に、現在ではカードを見つけるのが難しくなり、モデルを実行するのが困難になってきています。

このガイドでは、CPU を使用して、ネイティブ Python で検索拡張生成 (ドキュメント Q&A とも呼ばれる) 用のオープンソースの軽量 LLM モデルを実行する方法を説明します。このプロジェクトでは、最新の高性能 Llama 2 チャット モデルを活用します。

量子化の概要

LLM は優れた機能を発揮しますが、その操作には大量のコンピューティング リソースとメモリ リソースが必要です。この問題に対処するには、量子化を使用してこれらのモデルを圧縮し、モデルのパフォーマンスと効果を維持しながらメモリ使用量を削減し、推論計算プロセスを高速化できます。

量子化は、数値または値を表すために使用されるビット数を減らすための技術です。LLM のコンテキストでは、重みを精度の低いデータ型で格納することによってモデル パラメーターの精度を下げることが含まれます。

量子化はモデルのサイズを削減するため、CPU のみで GPU を持たないデバイスや組み込みシステムなど、リソースに制約のあるデバイスにモデルをデプロイするのに役立ちます。

一般的なアプローチは、モデルの重みを元の 16 ビット浮動小数点値から低精度の 8 ビット整数値に量子化することです。

16 ビット浮動小数点から 8 ビット整数への重み量子化

ツールとデータ

以下の図は、このプロジェクトで構築する Document Q&A アプリケーションのアーキテクチャを示しています。

ドキュメント Q&A スキーマ

このプロジェクトでドキュメント Q&A を実行するドキュメントは、マンチェスター ユナイテッド フットボール クラブの 2022 年公開年次報告書で、合計 177 ページです。

データ出典:Manchester United Plc (2022). 2022 年年次報告書 2022 年フォーム 20-F。 https://ir.manutd.com/~/media/Files/M/Manutd-IR/documents/manu-20f-2022-09-24.pdf  (CC0: パブリック ドメイン、SEC コンテンツはパブリック ドメインであり、自由にアクセスできるため)使用)

この例の実行環境には、AMD Ryzen 5 5600X 6 コア CPU16GB メモリ (DDR4 3600)が搭載されています。RTX 3060TI 8G グラフィックス カードも内蔵されていますが、CPU のみを使用してモデルを実行する方法について説明するため、この例では使用されません。

このバックエンド アプリケーションを構築するときに使用するツールは次のとおりです。

ラングチェーン:

LangChainは、LLM ドライバーを開発するための現在人気のあるアプリケーション フレームワークであり、さまざまな統合インターフェイスとデータ接続インターフェイスを提供し、さまざまなモジュールをチェーン オーケストレーションして、チャットボット、データ分析、ドキュメント Q&A などの高度なユース ケースを作成できます。

C 変圧器:

C Transformers は、 GGMLライブラリ C/C++ バインディングを使用する Transformer モデルを提供する Python ライブラリですそういえば、まず GGML とは何かを理解しましょう。

ggml.aiチームによって開発された GGML ライブラリは、機械学習用に設計された Tensor ライブラリであり、消費者グレードのハードウェアで高いパフォーマンスで大規模なモデルを実行できます。これは、整数量子化のサポートと組み込みの最適化アルゴリズムによって実現されます。

したがって、GGML バージョンの LLM (バイナリ形式の量子化モデル) は CPU 上で効率的に実行できます。このプロジェクトでは Python を使用しているため、GGML モデルの Python バインディングを提供する C Transformers ライブラリを使用します。

C Transformers は、Llama、GPT4All-J、MPT、Falcon などの現在人気のあるモデルを含む、いくつかの一般的なオープン ソース モデルをサポートしています。

C Transformer によってサポートされる大規模な言語モデル

Sentence-Transformers 埋め込みモデル

Sentence-transformers は、文、テキスト、画像の埋め込み (高密度ベクトル表現) を生成する簡単な方法を提供する Python ライブラリです。

これにより、ユーザーは 100 を超える言語で関連する文を生成し、それらを比較して同様の意味を持つ文を見つけることができます。

このプロジェクトでは、最高の速度と優れた一般的な生成品質を備えたオープンソースの全 MiniLM-L6-v2モデルを使用します。

フェイス

Facebook AI 類似性検索 (FAISS) は、 効率的な密ベクトル (Dense Vector) 類似性検索とクラスタリングのために設計されたライブラリです。

一連の入力が与えられると、FAISS を使用してそれらにインデックスを付け、その強力なセマンティック検索アルゴリズムを利用してインデックス内で最も類似したベクトルを検索できます。

従来の意味での完全なベクトル ストア (データベース管理システムなど) ではありませんが、効率的な隣接検索を可能にする最適化された方法でベクトルのストレージを処理します。

このプロジェクトでは、使いやすさと一貫性のため、Poetryを使用して virtualenv をセットアップし、Python パッケージ管理を処理します。

以前に venv を使用したことがある場合は、依存関係の管理がより効率的かつシームレスになるため、Poetry に切り替えることを強くお勧めします。

オープンソース LLM オプション

オープンソース LLM は現在大きく進歩しており、その多くは Hugging Face のOpen LLM ランキングで見つけることができます。

次の考慮事項に基づいて、このプロジェクトには最新のオープンソース Llama-2–7B-Chat モデル (GGML 8 ビット) を選択しました。

モデルタイプ(ラマ2)

  • C Transformers によってサポートされるオープン ソース モデル。
  • Open LLM リーダーボードのランキング (2023 年 7 月現在) によると、現在、複数の指標にわたって最もパフォーマンスの高いモデルです。
  • 以前のベンチマークでは、以前の Llama モデルに比べて大幅な改善が見られました。
  • コミュニティで広く言及され、ダウンロードされています。

モデルサイズ(7B)

  • ドキュメント Q&A タスクを実行していることを考慮すると、LLM は主にドキュメント ブロックの比較的単純な要約に使用されます。したがって、このタスクには技術的に大きすぎるモデル (65B 以上など) は必要ないため、7B のモデル サイズがニーズを満たします。

Find-tuned Version(Llama-2–7B-Chat)

  • Llama-2–7B 基本モデルはテキスト補完を目的としているため、ドキュメント Q&A シナリオで最適なパフォーマンスを達成するために必要な微調整が不足しています。
  • Llama-2–7B-Chat モデルは、対話と質問応答用に設計されているため、ターゲット シナリオに最適です。
  • このモデルは部分的に商用利用が許可されています。これは、微調整された Llama-2-Chat モデルが、公開されている指示データセットと 100 万人を超える個人からの注釈を使用しているためです。

量子化フォーマット(8ビット)

  • メモリの制約を考慮すると、9.6 GB のメモリ サイズしか必要としない 8 ビット GGML バージョンが適しています。
  • 8 ビット形式の応答品質は 16 ビットと同等です。
  • 元の量子化されていない 16 ビット モデルには約 15GB のメモリが必要ですが、これは 16GB RAM の制限に近すぎます。
  • 4 ビットや 5 ビットなど、他の小さな量子化フォーマットも利用できますが、精度と応答品質が犠牲になります。

実装手順

さまざまなコンポーネントを理解したところで、Document Q&A アプリケーションを構築する方法を見てみましょう。この記事の関連コードは、このGitHub リポジトリにあり、すべての依存関係は、requirements.txt ファイルにあります。

注: すでに多くのチュートリアルが提供されているため、ドキュメント Q&A コンポーネント (テキストのチャンク化、ベクトル ストレージ設定など) の複雑さや詳細については説明しません。この記事では、オープンソース LLM と CPU 推論に焦点を当てます。

ステップ 1 - データを処理してベクター ストアを構築する

このステップでは、次の 3 つのタスクが実行されます。

  1. データのインポートとテキストのテキスト チャンク (チャンク) への分割
  2. 埋め込みモデル (文変換) をロードする
  3. ブロックはインデックス付けされ、FAISS ベクトル ストレージに保存されます。
# File: db_build.py

from langchain.vectorstores import FAISS
from langchain.text_splitter import RecursiveCharacterTextSplitter
from langchain.document_loaders import PyPDFLoader, DirectoryLoader
from langchain.embeddings import HuggingFaceEmbeddings

# Load PDF file from data path
loader = DirectoryLoader('data/',
                         glob="*.pdf",
                         loader_cls=PyPDFLoader)
documents = loader.load()

# Split text from PDF into chunks
text_splitter = RecursiveCharacterTextSplitter(chunk_size=500,
                                               chunk_overlap=50)
texts = text_splitter.split_documents(documents)

# Load embeddings model
embeddings = HuggingFaceEmbeddings(model_name='sentence-transformers/all-MiniLM-L6-v2',
                                   model_kwargs={'device': 'cpu'})

# Build and persist FAISS vector store
vectorstore = FAISS.from_documents(texts, embeddings)
vectorstore.save_local('vectorstore/db_faiss')

上記の Python スクリプトを実行すると、ベクター ストアが生成され、ローカル ディレクトリに「vectorstore/db_faiss」として保存され、セマンティック検索と取得の準備が整います。

ステップ 2 - プロンプト テンプレートの設定

Llama-2–7B-Chat モデルを使用していることを考慮すると、ここで使用されているプロンプト テンプレートに注意する必要があります。

たとえば、OpenAI の GPT モデルは、会話の入力とメッセージの出力用に設計されています。これは、プロンプト テンプレートが会話レコード(サブシステム メッセージやユーザー メッセージなど)と同様の形式である必要があることを意味します。

ただし、Llama 2 モデルはこのタイプの会話インターフェイス用に特に最適化されていないため、このようなテンプレートはここでは適用できません。代わりに、以下に示すように、従来のプロンプト テンプレートを使用する方が適切です。

# File: prompts.py

qa_template = """Use the following pieces of information to answer the user's question.
If you don't know the answer, just say that you don't know, don't try to make up an answer.
Context: {context}
Question: {question}
Only return the helpful answer below and nothing else.
Helpful answer:
"""

注: 7B モデルなどの小さい LLM は、フォーマット要件の影響をより受けやすくなります。たとえば、プロンプト テンプレートの間隔とインデントを変更すると、出力が若干異なります。

ステップ 3 — Llama-2–7B-Chat GGML バイナリをダウンロードする

LLM をローカルで実行するため、量子化された Llama-2–7B-Chat モデルのバイナリをダウンロードする必要があります。

Hugging Face でホストされているTheBloke の Llama-2–7B-Chat GGML ページにアクセスし、llama-2–7b-chat.ggmlv3.q8_0.bin という名前の GGML 8 ビット量子化ファイルをダウンロードできます。

HuggingFace の Llama-2–7B-Chat-GGML ページのファイルとバージョン情報

ダウンロードした 8 ビット量子化モデルの .bin ファイルは、/models などのプロジェクトのサブフォルダーに保存できます。

モデル カード ページには、各量子化形式の詳細情報と詳細な説明も表示されます。

詳細を含むさまざまな量子化フォーマット

注: C Transformers でサポートされている他の GGML 量子化モデルをダウンロードするには、HuggingFace にある TheBloke のホームページにアクセスし、必要なモデルを検索して、名前が「-GGML」で終わるリンクを探してください。

ステップ 4 - LLM をセットアップする

ダウンロードした GGML モデルを使用するために、C Transformers と LangChain の間の統合を使用します。具体的には、GGML モデルへの統一インターフェイスを提供する LangChain の C Transformers LLM Wrapper を使用します。

# File: llm.py
from langchain.llms import CTransformers

# Local CTransformers wrapper for Llama-2-7B-Chat
llm = CTransformers(model='models/llama-2-7b-chat.ggmlv3.q8_0.bin', # Location of downloaded GGML model
                    model_type='llama', # Model type Llama
                    config={'max_new_tokens': 256,
                            'temperature': 0.01})

トークンの最大数、上位 k、温度、繰り返しペナルティなど、LLM の多くの構成オプションを定義できます。

注: 温度を 0 ではなく 0.01 に設定しました。これは、温度がちょうど 0 の場合、奇妙な戻り値 (文字 E が繰り返される長い文字列など) が返されるためです。

ステップ 5 - RetrievalQA を構築して初期化する

プロンプト テンプレートと準備された C Transformers LLM を使用して、ドキュメント Q&A タスクを実行できるように、LangChain の RetrievalQA オブジェクトを構築する 3 つの関数を作成します。

# File: utils.py
from langchain import PromptTemplate
from langchain.chains import RetrievalQA
from langchain.embeddings import HuggingFaceEmbeddings
from langchain.vectorstores import FAISS

# Wrap prompt template in a PromptTemplate object
def set_qa_prompt():
    prompt = PromptTemplate(template=qa_template,
                            input_variables=['context', 'question'])
    return prompt


# Build RetrievalQA object
def build_retrieval_qa(llm, prompt, vectordb):
    dbqa = RetrievalQA.from_chain_type(llm=llm,
                                       chain_type='stuff',
                                       retriever=vectordb.as_retriever(search_kwargs={'k':2}),
                                       return_source_documents=True,
                                       chain_type_kwargs={'prompt': prompt})
    return dbqa


# Instantiate QA object
def setup_dbqa():
    embeddings = HuggingFaceEmbeddings(model_name="sentence-transformers/all-MiniLM-L6-v2",
                                       model_kwargs={'device': 'cpu'})
    vectordb = FAISS.load_local('vectorstore/db_faiss', embeddings)
    qa_prompt = set_qa_prompt()
    dbqa = build_retrieval_qa(llm, qa_prompt, vectordb)

    return dbqa

ステップ 6 - コンポーネントを起動スクリプトに組み込む

次のステップでは、前のコンポーネントを main.py スクリプトにマージします。ユーザークエリをコマンドラインからアプリケーションに渡すため、argparse モジュールを使用します。

ソースドキュメントを返すことを考慮して、添付されたコードを使用してドキュメントブロックを処理し、視覚的に表示しやすくします。

CPU 推論の速度を評価するには、timeit モジュールも使用します。

# File: main.py
import argparse
import timeit

if __name__ == "__main__":
    parser = argparse.ArgumentParser()
    parser.add_argument('input', type=str)
    args = parser.parse_args()
    start = timeit.default_timer() # Start timer

    # Setup QA object
    dbqa = setup_dbqa()
    
    # Parse input from argparse into QA object
    response = dbqa({'query': args.input})
    end = timeit.default_timer() # End timer

    # Print document QA response
    print(f'\nAnswer: {response["result"]}')
    print('='*50) # Formatting separator

    # Process source documents for better display
    source_docs = response['source_documents']
    for i, doc in enumerate(source_docs):
        print(f'\nSource Document {i+1}\n')
        print(f'Source Text: {doc.page_content}')
        print(f'Document Name: {doc.metadata["source"]}')
        print(f'Page Number: {doc.metadata["page"]}\n')
        print('='* 50) # Formatting separator
        
    # Display time taken for CPU inference
    print(f"Time to retrieve response: {end - start}")

ステップ 7 - サンプル クエリを実行する

今度はアプリケーションをテストします。プロジェクト ディレクトリから virtualenv をロードした後、コマンド ライン インターフェイス (CLI) でユーザー クエリを含むコマンドを実行できます。

たとえば、次のコマンドを使用して、Adidas (マンチェスター ユナイテッドのグローバル テクニカル スポンサー) に支払う最低入金額を問い合わせることができます。

poetry run python main.py "How much is the minimum guarantee payable by adidas?"

注: Poetry を使用しない場合は、接頭辞を省略できますpoetry run

結果

ドキュメント Q&A アプリケーションに渡されるユーザー クエリからの出力 | 著者による画像

出力は、ユーザーのクエリに対する正しい応答 (つまり、7 億 5,000 万ポンド) を取得できたことを示し、同時にクエリと意味的に類似した関連ドキュメントのスニペットも表示します。

アプリケーションの起動とビルドの応答時間の合計 31 秒は、AMD Ryzen 5600X (優れた CPU ではありますが、現在の市場で最高ではありません) でネイティブに実行していたことを考えると、かなり良好です。

GPU 上で (たとえば、HuggingFace 上で直接) LLM 推論を実行する場合にも数十秒かかる可能性があることを考慮すると、この結果はさらに印象的です。

次のステップ

CPU 推論で LLM を実行する Document Q&A アプリケーションを構築したので、このプロジェクトを進めるためにいくつかの興味深い手順を実行できます。

  1. Streamlit を使用してフロントエンド チャット インターフェイスを構築します。特に、Streamlit と LangChain の統合と、強力なチャットボット インターフェイスを簡単に構築できる Streamlit ChatUI の起動という 2 つの重要な最近の発表があるためです。
  2. アプリケーションを Docker 化してクラウド ホストにデプロイします。ローカル推論について説明しましたが、アプリケーションは簡単にクラウドに移行できます。また、クラウド上のより強力な CPU インスタンスを利用して推論を高速化することもできます (たとえば、c5.4xlarge などのコンピューティングに最適化された AWS EC2 インスタンス)。
  3. Llama 13B Chat モデルなど、少し大きい LLM を試してください。すでに 7B モデルを使用しているため、理論的には適度な量のメモリ使用量を維持しながら精度が高くなるはずなので、少し大きなモデルのパフォーマンスを評価することをお勧めします。
  4. 推論速度と応答品質の違いを客観的に評価するには、4 ビットや 5 ビットなどのより小さい量子化形式 (新しい k-quant メソッドの使用を含む) を試してください。
  5. ローカル GPU を活用して推論を高速化します。C Transformers モデルで GPU を使用してテストしたい場合は、GPU でいくつかのモデルを実行することでテストできます。現在、GPU をサポートしているのは Llama モデル タイプのみであるため、これは役立つはずです。
  6. 評価では、高スループットでメモリ効率の高い LLM 推論および提供エンジンである vLLM を使用します。ただし、vLLM を使用するには GPU を使用する必要があります。

今後数週間で上記のアイデアに関する記事やプロジェクトの執筆に取り組む予定ですので、より洞察力に富んだ生成 AI コンテンツにご期待ください。

おすすめ

転載: blog.csdn.net/jeansboy/article/details/131867599