ollamaを使ってローカル環境でLLMを動かす

モチベーション

この投稿で、ナレッジグラフを構築できるLLMは、OpenAIとMistral(何もAPI経由)であると述べた。ネットでは、この記事のようにollamaを使ってGraphRAG環境を構築している事例を見かけるようになった。

自分もローカル環境でナレッジグラフを構築することにチャレンジしたい。今回の投稿では、ollamaをインストールするまでをまとめる。

情報源

  1. ollama/ollama - dockerhubの情報。
  2. Ollama is now available as an official Docker image - ollamaのブログ記事でdockerコンテナとしてollamaをインストールする情報。
  3. Ollamaで体験!Gemma2-2b-jpnが切り拓く日本語AI活用の新時代 - ollamaでgemma2日本語版インストールする情報。
  4. Ollamaで Google の日本語版 Gemma 2 2B を動かす - 同じくollamaでgemma2日本語版をインストールする情報。
  5. ローカル環境でLLMを選んでLangChainで実行する。 - ollama CLIコマンドの一覧が載っていて、助かった。また、この記事のコードで4つのLLMを確かめた。
  6. OllamaLLM Connection refused… - JupyterLabでコードを確かめる際に、connection refusedのエラーが出たので、その際に参考にした。
  7. インストールから日本語モデル導入まで - GGUFファイルをollamaに組み込む際に参考した。

ollamaをインストール

docker-compose.ymlを作成

情報源1.や2.のdockerコンテナ起動のコマンドラインを参考に、次のようなyamlを作成した。

services:
  ollama:
    image: ollama/ollama
    container_name: ollama
    ports:
      - "11434:11434"
    volumes:
      - ./ollama:/root/.ollama
    deploy:
      resources:
        reservations:
          devices:
            - driver: nvidia
              count: 1
              capabilities: [gpu]
    restart: always

ホスト側の./ollamaディレクトリは、NFSマウント配下に置いた。そうすることで、別のGPUマシンと共有できると考えて。

$ pwd
/mnt/nfs2/workspace/ollama
$ ls -l
合計 8
-rw-rw-r-- 1 kenji kenji  325 11月 21 18:33 docker-compose.yml
drwxrwxr-x 3 kenji kenji 4096 11月 22 15:28 ollama
$ sudo docker compose up -d

上記の通り、ollamaコンテナを起動する。

LLMモデルを(ダウンロードし)実行

次のように、上記で起動したコンテナに入り、コンテナ内で、ollamaコマンドを使ってモデルを実行する。

$ sudo docker exec -it ollama /bin/bash
# ollama --version
ollama version is 0.4.2
# ollama run schroneko/gemma-2-2b-jpn-it

実行結果

情報源3.のサンプルをそのまま使って、実行した。

>>> 日本の四季について端的にわかりやすく教えて
日本の四季は、大きく分けて**春、夏、秋、冬**の4つの季節があります。

* **春:**  桜の花が咲き乱れる季節です。暖かくなり、植物が芽吹きます。
* **夏:**  暑く、日差しが強くなります。海や湖で泳ぎ、屋外で遊びます。
* **秋:**  紅葉が美しい季節です。気温が下がり、冷たい風が吹きます。
* **冬:**  寒く、雪が降ることがあります。冬スポーツを楽しんだり、暖房で体を温めたりし
ます。

JupyterLabからollamaモデルを使う

この投稿で紹介したDockerfileに、「pip innstall ollama, langchain-ollama」を追加して、JupyterLabのdockerコンテナを作成した。Dockerfileの整理については後述。

実行コード

情報源5.のコードを参考に、JupyterLabコンテナから実行した。

from langchain_core.prompts import ChatPromptTemplate
from langchain_ollama.llms import OllamaLLM

model = OllamaLLM(base_url = "http://192.168.11.4:11434", model="schroneko/gemma-2-2b-jpn-it")

# PROMPT
template = """Question: {question}
Answer: ステップバイステップで考えてみましょう。"""

prompt = ChatPromptTemplate.from_template(template)

# CHAIN
chain = prompt | model
result = chain.invoke({"question": "美味しいパスタの作り方は?"})
print(result)

OllamaLLM()の引数のbase_urlのところがポイント。これは、情報源6.を参考にした。base_urlを指定しないと、connection refusedのエラーが発生する。

ggufファイルをモデルとして組み込む

情報源7.を参考に、huggingfaceにあるこの一覧の「Q8-0」をダウンロードして、以下のようにModelfileを準備した。

$ wget https://huggingface.co/mradermacher/Llama-3-neoAI-8B-Chat-v0.1-GGUF/resolve/main\
/Llama-3-neoAI-8B-Chat-v0.1.Q8_0.gguf
$ cat Modelfile
FROM ./Llama-3-neoAI-8B-Chat-v0.1.Q8_0.gguf

上記の作業は、yamlファイル説明のところのollamaディレクトリ配下で行う。そうするとコンテナからもggufファイルとModelfileにアクセスできる。

ollamaコンテナに入り(既に説明済みのdocker execによって)、ollamaコマンドでモデルを作る。

#ollama create neoai-8b-chat -f Modelfile

気づいたこと

  1. ollamaコンテナでモデルをpullしておけば、JupyterLabのコードのモデル接続(OllamaLLM)時にrunしてくれるようで、アクセスできる。
  2. JupyterLabでモデルを変更すると、ollama側でモデルのstopとrunを行なっているようだ。
  3. コンテナに入って、「ollama stop モデル」するとGPUメモリを解放しているようだ。

JupyterLabコンテナを見直す

ここまででollamaコンテナがバックエンドでLLMの処理を行えるようになった。これまでJupyterLabのコンテナにllama-cpp-pythonを組み込んでいた。今後はJupyterLabコンテナにllama-cpp-pythonが必要でなくなり、JupyterLabコンテナがシンプルになる。

JupyterLabコンテナのDockerfile

シンプルになったDockerfileは次の通り。

# JupyterLabが使えるDockerイメージ
# 自分がこれまで作成したNoteBookに必要なパッケージをインストールしたもの。

FROM nvidia/cuda:12.1.1-cudnn8-devel-ubuntu22.04

# Set bash as the default shell
ENV SHELL=/bin/bash

# Build with some basic utilities
RUN apt update \
        && apt install -y \
        wget \
        bzip2 \
        git \
        git-lfs \
        curl \
        unzip \
        file \
        xz-utils \
        sudo \
        python3 \
        python3-pip && \
        apt-get autoremove -y && \
        apt-get clean && \
        rm -rf /usr/local/src/*

# alias python='python3'
RUN ln -s /usr/bin/python3 /usr/bin/python

RUN pip install --upgrade pip setuptools \
        && pip install torch torchvision torchaudio \
         --index-url https://download.pytorch.org/whl/cu121 \
        && pip install jupyterlab matplotlib pandas scikit-learn ipywidgets \
        && pip install transformers accelerate sentencepiece einops \
        && pip install auto-gptq optimum \
        && pip install langchain bitsandbytes protobuf \
        && pip install langchain-community langchain_openai wikipedia \
        && pip install langchain-huggingface unstructured html2text rank-bm25 janome \
        && pip install langchain-chroma sudachipy sudachidict_full \
        && pip install langchain-experimental neo4j \
        && pip install json-repair langchain-mistralai \
        && pip install mysql-connector-python \
        && pip install ragas datasets neo4j-graphrag \
        && pip install pypdf tiktoken sentence_transformers faiss-gpu trafilatura \
        && pip install ollama langchain-ollama

# Create a working directory
WORKDIR /workdir

# Port number in container side
EXPOSE 8888

ENTRYPOINT ["jupyter-lab", "--ip=0.0.0.0", "--port=8888", "--no-browser", "--allow-root", "--NotebookApp.token=''"]

CMD ["--notebook-dir=/workdir"]

まとめ

JupyterLabコンテナとollamaコンテナに分離し、コーディングを行う部分(フロントエンド)とLLMモデルを扱う部分(バックエンド)に分離でき構造的にすっきりした。これによって、今後LLMモデルを使うアプリを作成する場合にも良い結果をなると思う。