データのプライバシーが最も重要である時代に、独自のローカル言語モデル (LLM)を設定することは、企業と個人の両方にとって重要なソリューションとなります。このチュートリアルは、システムでローカルにホストされているOllama 、 Python 3 、 ChromaDBを使用してカスタム チャットボットを作成するプロセスをガイドするように設計されています。このチュートリアルが必要な主な理由は次のとおりです。
このチュートリアルでは、プライバシーや制御を損なうことなく、ニーズに合わせてカスタマイズされた堅牢で安全なローカル チャットボットを構築できるようになります。
検索拡張生成 (RAG)は、情報検索とテキスト生成の長所を組み合わせて、より正確で文脈に即した応答を作成する高度な技術です。RAG の仕組みとその利点について、以下に詳しく説明します。
RAG は、外部の知識ベースまたはドキュメント ストアを組み込むことで言語モデルの機能を強化するハイブリッド モデルです。このプロセスには、次の 2 つの主要コンポーネントが含まれます。
Ollama、Python、ChromaDB などのツールを使用してローカル RAG アプリケーションを設定すると、データとカスタマイズ オプションを制御しながら、高度な言語モデルの利点を享受できます。
検索拡張生成 (RAG) で使用されるような大規模言語モデル (LLM) を実行するには、かなりの計算能力が必要です。これらのモデルでデータを効率的に処理して埋め込むための重要なコンポーネントの 1 つが、グラフィックス プロセッシング ユニット (GPU) です。このタスクに GPU が不可欠な理由と、GPU がローカル LLM セットアップのパフォーマンスに与える影響について、次に説明します。
GPU は、画像や動画のレンダリングを高速化するために設計された特殊なプロセッサです。順次処理タスクに最適化された中央処理装置 (CPU) とは異なり、GPU は並列処理に優れています。このため、GPU は機械学習やディープラーニング モデルに必要な複雑な数学的計算に特に適しています。
ローカル LLM を設定する場合、GPU の選択はパフォーマンスに大きな影響を与える可能性があります。考慮すべき要素は次のとおりです。
高性能 GPU への投資は、LLM モデルをローカルで実行するために不可欠です。これにより、データ処理の高速化、モデルの効率的なトレーニング、応答の迅速な生成が保証され、ローカル RAG アプリケーションの堅牢性と信頼性が向上します。GPU のパワーを活用することで、特定のニーズとデータ プライバシー要件に合わせてカスタマイズされた独自のカスタム チャットボットをホストするメリットを十分に実現できます。
セットアップに進む前に、次の前提条件が満たされていることを確認してください。
Python 3 環境をインストールして設定するには、次の手順に従ってください: マシンにPython 3 をダウンロードして設定します。次に、Python 3 が正常にインストールされ、実行されていることを確認します。
$ python3 --version # Python 3.11.7
プロジェクト用のフォルダーを作成します (例: local-rag
。
$ mkdir local-rag $ cd local-rag
venv
という名前の仮想環境を作成します。
$ python3 -m venv venv
仮想環境をアクティブ化します。
$ source venv/bin/activate # Windows # venv\Scripts\activate
pip を使用して ChromaDB をインストールします。
$ pip install --q chromadb
モデルをシームレスに操作するには、Langchain ツールをインストールします。
$ pip install --q unstructured langchain langchain-text-splitters $ pip install --q "unstructured[all-docs]"
アプリを HTTP サービスとして提供するには、Flask をインストールします。
$ pip install --q flask
Ollama をインストールするには、次の手順に従います。Ollamaのダウンロード ページにアクセスし、ご使用のオペレーティング システム用のインストーラーをダウンロードします。次のコマンドを実行して、Ollama のインストールを確認します。
$ ollama --version # ollama version is 0.1.47
必要な LLM モデルを取得します。たとえば、Mistral モデルを使用する場合は、次のようにします。
$ ollama pull mistral
テキスト埋め込みモデルを取得します。たとえば、Nomic Embed Text モデルを使用するには、次のようにします。
$ ollama pull nomic-embed-text
次に、Ollama モデルを実行します。
$ ollama serve
Python、Ollama、ChromaDB、その他の依存関係を使用して環境をセットアップしたので、次はカスタム ローカル RAG アプリを構築します。このセクションでは、実践的な Python コードを確認し、アプリケーションを構築する方法の概要を説明します。
app.py
これはメインの Flask アプリケーション ファイルです。ベクター データベースにファイルを埋め込み、モデルから応答を取得するためのルートを定義します。
import os from dotenv import load_dotenv load_dotenv() from flask import Flask, request, jsonify from embed import embed from query import query from get_vector_db import get_vector_db TEMP_FOLDER = os.getenv('TEMP_FOLDER', './_temp') os.makedirs(TEMP_FOLDER, exist_ok=True) app = Flask(__name__) @app.route('/embed', methods=['POST']) def route_embed(): if 'file' not in request.files: return jsonify({"error": "No file part"}), 400 file = request.files['file'] if file.filename == '': return jsonify({"error": "No selected file"}), 400 embedded = embed(file) if embedded: return jsonify({"message": "File embedded successfully"}), 200 return jsonify({"error": "File embedded unsuccessfully"}), 400 @app.route('/query', methods=['POST']) def route_query(): data = request.get_json() response = query(data.get('query')) if response: return jsonify({"message": response}), 200 return jsonify({"error": "Something went wrong"}), 400 if __name__ == '__main__': app.run(host="0.0.0.0", port=8080, debug=True)
embed.py
このモジュールは、アップロードされたファイルの保存、データの読み込みと分割、ベクター データベースへのドキュメントの追加などの埋め込みプロセスを処理します。
import os from datetime import datetime from werkzeug.utils import secure_filename from langchain_community.document_loaders import UnstructuredPDFLoader from langchain_text_splitters import RecursiveCharacterTextSplitter from get_vector_db import get_vector_db TEMP_FOLDER = os.getenv('TEMP_FOLDER', './_temp') # Function to check if the uploaded file is allowed (only PDF files) def allowed_file(filename): return '.' in filename and filename.rsplit('.', 1)[1].lower() in {'pdf'} # Function to save the uploaded file to the temporary folder def save_file(file): # Save the uploaded file with a secure filename and return the file path ct = datetime.now() ts = ct.timestamp() filename = str(ts) + "_" + secure_filename(file.filename) file_path = os.path.join(TEMP_FOLDER, filename) file.save(file_path) return file_path # Function to load and split the data from the PDF file def load_and_split_data(file_path): # Load the PDF file and split the data into chunks loader = UnstructuredPDFLoader(file_path=file_path) data = loader.load() text_splitter = RecursiveCharacterTextSplitter(chunk_size=7500, chunk_overlap=100) chunks = text_splitter.split_documents(data) return chunks # Main function to handle the embedding process def embed(file): # Check if the file is valid, save it, load and split the data, add to the database, and remove the temporary file if file.filename != '' and file and allowed_file(file.filename): file_path = save_file(file) chunks = load_and_split_data(file_path) db = get_vector_db() db.add_documents(chunks) db.persist() os.remove(file_path) return True return False
query.py
このモジュールは、クエリの複数のバージョンを生成し、関連するドキュメントを取得し、コンテキストに基づいて回答を提供することで、ユーザークエリを処理します。
import os from langchain_community.chat_models import ChatOllama from langchain.prompts import ChatPromptTemplate, PromptTemplate from langchain_core.output_parsers import StrOutputParser from langchain_core.runnables import RunnablePassthrough from langchain.retrievers.multi_query import MultiQueryRetriever from get_vector_db import get_vector_db LLM_MODEL = os.getenv('LLM_MODEL', 'mistral') # Function to get the prompt templates for generating alternative questions and answering based on context def get_prompt(): QUERY_PROMPT = PromptTemplate( input_variables=["question"], template="""You are an AI language model assistant. Your task is to generate five different versions of the given user question to retrieve relevant documents from a vector database. By generating multiple perspectives on the user question, your goal is to help the user overcome some of the limitations of the distance-based similarity search. Provide these alternative questions separated by newlines. Original question: {question}""", ) template = """Answer the question based ONLY on the following context: {context} Question: {question} """ prompt = ChatPromptTemplate.from_template(template) return QUERY_PROMPT, prompt # Main function to handle the query process def query(input): if input: # Initialize the language model with the specified model name llm = ChatOllama(model=LLM_MODEL) # Get the vector database instance db = get_vector_db() # Get the prompt templates QUERY_PROMPT, prompt = get_prompt() # Set up the retriever to generate multiple queries using the language model and the query prompt retriever = MultiQueryRetriever.from_llm( db.as_retriever(), llm, prompt=QUERY_PROMPT ) # Define the processing chain to retrieve context, generate the answer, and parse the output chain = ( {"context": retriever, "question": RunnablePassthrough()} | prompt | llm | StrOutputParser() ) response = chain.invoke(input) return response return None
get_vector_db.py
このモジュールは、ドキュメントの埋め込みの保存と取得に使用されるベクター データベース インスタンスを初期化して返します。
import os from langchain_community.embeddings import OllamaEmbeddings from langchain_community.vectorstores.chroma import Chroma CHROMA_PATH = os.getenv('CHROMA_PATH', 'chroma') COLLECTION_NAME = os.getenv('COLLECTION_NAME', 'local-rag') TEXT_EMBEDDING_MODEL = os.getenv('TEXT_EMBEDDING_MODEL', 'nomic-embed-text') def get_vector_db(): embedding = OllamaEmbeddings(model=TEXT_EMBEDDING_MODEL,show_progress=True) db = Chroma( collection_name=COLLECTION_NAME, persist_directory=CHROMA_PATH, embedding_function=embedding ) return db
環境変数を保存するための.env
ファイルを作成します。
TEMP_FOLDER = './_temp' CHROMA_PATH = 'chroma' COLLECTION_NAME = 'local-rag' LLM_MODEL = 'mistral' TEXT_EMBEDDING_MODEL = 'nomic-embed-text'
app.py
ファイルを実行してアプリ サーバーを起動します。
$ python3 app.py
サーバーが稼働したら、次のエンドポイントへのリクエストを開始できます。
$ curl --request POST \ --url http://localhost:8080/embed \ --header 'Content-Type: multipart/form-data' \ --form file=@/Users/nassermaronie/Documents/Nasser-resume.pdf # Response { "message": "File embedded successfully" }
$ curl --request POST \ --url http://localhost:8080/query \ --header 'Content-Type: application/json' \ --data '{ "query": "Who is Nasser?" }' # Response { "message": "Nasser Maronie is a Full Stack Developer with experience in web and mobile app development. He has worked as a Lead Full Stack Engineer at Ulventech, a Senior Full Stack Engineer at Speedoc, a Senior Frontend Engineer at Irvins, and a Software Engineer at Tokopedia. His tech stacks include Typescript, ReactJS, VueJS, React Native, NodeJS, PHP, Golang, Python, MySQL, PostgresQL, MongoDB, Redis, AWS, Firebase, and Supabase. He has a Bachelor's degree in Information System from Universitas Amikom Yogyakarta." }
これらの手順に従うことで、ニーズに合わせてカスタマイズされた Python、Ollama、ChromaDB を使用して、カスタム ローカル RAG アプリを効果的に実行し、操作することができます。必要に応じて機能を調整および拡張し、アプリケーションの機能を強化します。
ローカル展開の機能を活用することで、機密情報を保護できるだけでなく、パフォーマンスと応答性も最適化できます。顧客とのやり取りを強化する場合でも、内部プロセスを合理化する場合でも、ローカルに展開された RAG アプリケーションは、要件に合わせて適応し、拡張できる柔軟性と堅牢性を提供します。
https://github.com/firstpersoncode/local-rag
楽しいコーディングを!