Open18

WindowsでOllamaを使って、ローカルで動くLLMのLlama3やPhi3を使いこなす!

shohei6117shohei6117

主要なサイトへのリンク

一般公開されているLLMリンク集

このスクラップへのリンク

shohei6117shohei6117

Llama3をOllamaで動かす#1

ゴール

  • WindowsにOllamaをインストールする
  • Llama3をOllmaで動かす
  • PowerShellでLlama3とチャットする

参考リンク

  1. Ollamaの公式ブログ 2024-4-18

手順

  1. Ollama公式サイトからWindows版をダウンロード
  2. インストーラを起動してインストールする
  3. PowerShellでollamaollama --versionを打ってインストールできていることを確認
  4. ollama run llma3コマンドでLlama3をダウンロード&起動
    • モデルの自動でダウンロードされる
    • 起動するとプロンプトの入力待ち状態になる
  5. 動いた!

    日本語も出力できる。
shohei6117shohei6117

Llama3をOllamaで動かす #2

ゴール

  • WindowsでOpen-WebUIのDockerコンテナを導入して動かす
    • 前提:Docker Desktopはインストール済み
  • ChatGPTライクのOpen-WebUIアプリを使って、Ollamaで動かしているLlama3とチャットする

参考リンク

  1. Open WebUI 公式doc
  2. Open WebUI + Llama3(8B)をMacで動かしてみた
  3. Llama3もGPT-4も使える!オープンソースのChatGPT風アプリ「Open WebUI」完全ガイド

手順

  1. PowerShellでLlama3を起動する。
PowerShell
ollama start llama3
  • ollama serveコマンドでも良い
  1. Doker Dewktopが動く環境を作る(割愛!)
  2. PowerShellから以下のコマンドを流して、Open WebUIのコンテナを起動する
PowerShell
docker run -d -p 3000:8080 --add-host=host.docker.internal:host-gateway -v open-webui:/app/backend/data --name open-webui --restart always ghcr.io/open-webui/open-webui:main

Nvidia GPUを使うときはこっち。

PowerShell
docker run -d -p 3000:8080 --gpus all --add-host=host.docker.internal:host-gateway -v open-webui:/app/backend/data --name open-webui --restart always ghcr.io/open-webui/open-webui:cuda
  • 3000番ポートを使っている場合は、別の番号にする
    netstat -naoコメンドでアクティブな接続を確認できる
  1. コンテナが起動したら、ブラウザでlocalhost:3000を開くとOpen WebUIが開く
  2. 最初はサインアップする(割愛)
  3. 画面左下のアカウントをクリックして設定を開き、以下のように記入する。

  • システムプロンプトはYou are an excellent AI assistant. You must provide every answer in Japanese. よろしくね!
  1. 動いた!日本語で答えてくれた!
  2. OpenAI APIを設定しておくと、OpenAIモデルも使える

  3. いちどコンテナをつくれば、Docker Desktopからコンテナの起動、終了ができる
  • コンテナを再起動すると登録していたOpenAI APIキー情報が消えている。。(チャット履歴などは保存されている)
shohei6117shohei6117

Llama3をOllamaで動かす #3

ゴール

  • 以下の2通りで、Ollamaで動いているLlama3とAPI経由でチャットする
    • PowerShellでInvoke-WebRequestコマンドを使う
    • コマンドプロンプトでcurlコマンドを使う
  • proxyエラーを回避できるようになる
    • システム環境変数に NO_PROXY設定を追加する
    • さらにコマンドを修正する必要あり

まとめ

  • PowerShell
    • Invoke-WebRequestはサンプルコードのまま通る
  • コマンドプロンプト
    • システム環境変数にNO_PROXYを設定する
    • サンプルコマンドは修正が必要だった

参考リンク

手順: PowerShellからInvoke-WevRequestを送る

  1. Ollamaでllama3を動かす
PowerShell
ollama start llama3
  1. 別のPowerShellを起動して、公式ブログに掲載されているコマンドを打つ(llama2をllama3に変える!)
PowerShell
(Invoke-WebRequest -method POST -Body '{"model":"llama3", "prompt":"Why is the sky blue?", "stream": false}' -uri http://localhost:11434/api/generate ).Content | ConvertFrom-json

⇒返ってくる!

以下、ChatGPTの解説:

指定されたPowerShellコマンドは、ローカルAPIエンドポイントに対してPOSTリクエストを行い、JSON形式のレスポンスを受信して、その内容を解析するものです。ここで使われている主要な要素とその比較を説明します:
1. Invoke-WebRequest: PowerShellでWebリクエストを送信するためのコマンドです。curlコマンドと同様の役割を果たしますが、PowerShellに組み込まれた機能を利用します。
2. -Method POST: HTTP POSTメソッドを使用して、サーバーにデータを送信することを指示します。
3. -Body '{"model":"llama3", "prompt":"Why is the sky blue?", "stream": false}': JSON形式の文字列をリクエストボディとして送信します。この部分でAPIに必要なデータを渡しています。
4. -Uri http://localhost:11434/api/generate: リクエストを送るURLを指定します。
5. ( ... ).Content | ConvertFrom-json: Invoke-WebRequestの結果からContentプロパティを取り出し、そのJSON形式のテキストをPowerShellオブジェクトに変換します。これにより、レスポンスデータをより簡単に扱える形にします。

このコマンドは、APIからの応答を直接的に扱いやすくするために設計されています。特にConvertFrom-jsonは、受け取ったJSONデータをPowerShellのオブジェクトに変換し、その後の処理やデータアクセスを容易にします。

手順2: コマンドプロンプトからcurlを送る

  1. 次に、コマンドプロンプトを起動して、公式リポジトリに掲載されているコマンドを打つ
コマンドプロンプト
curl http://localhost:11434/api/generate -d '{  "model": "llama3",  "prompt": "Why is the sky blue?" }'

⇒エラーが出る

⇒エラーをChatGPTに解説してもらい、「proxyサーバを経由してAPIエンドポイントに接続しようとしているができない」ためのエラーと分かる
同じPCで稼働しているollamaにアクセスしようとしているので、proxyは無効にする

  1. ChatGPTに教えてもらった、proxyを使わないコマンドを流す
コマンドプロンプト
curl --noproxy "localhost" http://localhost:11434/api/generate -d '{"model": "llama3", "prompt": "Why is the sky blue?"}'

⇒エラーが出る

⇒ChatGPTに解決策を教えてもらう

Windowsのコマンドプロンプトでは、コマンドラインの解釈がUnix系システムのシェルとは異なります。特に、シングルクォート(')は文字列を囲むための引用符として認識されないため、JSONデータなどをコマンドラインに直接入力する際にはダブルクォート(")を使用する必要があります。さらに、ダブルクォート内でダブルクォートを使用する場合には、エスケープシーケンス(")を用いてエスケープする必要があります。

このため、WindowsのコマンドプロンプトでJSON形式のデータを扱う場合には、正しく解釈されるようダブルクォートで囲み、必要に応じてダブルクォートをエスケープする方法が必要になります。そのため、修正されたコマンドの形式が必要となり、そうしないとエラーが発生するのです。

  1. ChatGPTに改善してもらったコマンドを流す
コマンドプロンプト
curl --noproxy "localhost" http://localhost:11434/api/generate -d "{\"model\": \"llama3\", \"prompt\": \"Why is the sky blue?\"}"

⇒通った!

手順3: システム環境変数にNO_PROXYを追加

  1. noproxyを毎回宣言するのは面倒なので、システム環境変数にNO_PROXYを追加する
  • 「システム環境変数の編集」を開いて
  • 詳細設定タブ>環境変数をクリック
  • システム環境変数>新規をクリック
  • 変数名 NO_PROXY、変数値 localhost,127.0.0.1,(あればPCの固定IP)空白を入れずに入力
  1. コマンドプロンプトを再起動して、--noproxy "localhost"を削除した同じコマンドを叩く
コマンドプロンプト
curl --noproxy "localhost" http://localhost:11434/api/generate -d "{\"model\": \"llama3\", \"prompt\": \"Why is the sky blue?\"}"

⇒通った!

手順4: PowerShellでInvoke-Requestを送る#2

  1. コマンドプロンプトで通ったコマンドを、ChatGPTにPowerShellコマンドに変換してもらう
PowerShell
Invoke-WebRequest -Uri "http://localhost:11434/api/generate" -Method Post -ContentType "application/json" -Body '{"model": "llama3", "prompt": "Why is the sky blue?"}' -UseBasicParsing

⇒返ってくるけど期待と違う。。

ChatGPTによる解説:

このPowerShellのInvoke-WebRequestコマンドは、指定されたURIに対してHTTP POSTリクエストを送信するために使用されます。各パラメータの役割は以下の通りです:

  • -Uri "http://localhost:11434/api/generate": リクエストを送信するURLを指定します。この例では、ローカルマシン上の11434ポートで待ち受けているAPIに対してリクエストを送っています。
  • -Method Post: HTTPリクエストのタイプとしてPOSTを使用します。
  • -ContentType "application/json": リクエストの内容がJSON形式であることを示します。
  • -Body '{"model": "llama3", "prompt": "Why is the sky blue?"}': 送信するデータ本体です。ここではJSON形式のデータで、modelにはllama3promptには質問「Why is the sky blue?」が含まれています。
  • -UseBasicParsing: Internet Explorerを使わずに、より単純なHTMLパーサーを使ってレスポンスを解析します。
shohei6117shohei6117

Llama3をOllamaで動かす #4

ゴール

  • 以下の3通りで、Ollamaで動いているLlama3とチャットする
    • ollama-pythonライブラリを使う
    • Pythonのrequestライブラリを使う
    • openaiライブラリを使う
  • proxyエラーを回避できるようになる

まとめ

  • ollama-pythonライブラリ
    • proxy設定を無効化するコードが必要
  • requestライブラリ、openaiライブラリ
    • システム環境変数にNO_PROXYを設定しておけばサンプルのまま動く

参考リンク


手順: ollama-pythonライブラリを使う

  1. ollama-pythonをpipインストールする
python -m pip install ollama
  1. リポジトリのサンプルコードをそのまま実行する
ollama-python-sample1.py
import ollama
response = ollama.chat(model='llama3', messages=[
  {
    'role': 'user',
    'content': 'Why is the sky blue?',
  },
])
print(response['message']['content'])

エラーになる

  1. proxy設定を無効にする
ollama-python-sample2.py
import os
os.environ['HTTP_PROXY'] = ''
os.environ['HTTPS_PROXY'] = ''

import ollama
response = ollama.chat(model='llama3', messages=[
  {
    'role': 'user',
    'content': 'Why is the sky blue?',
  },
])
print(response['message']['content'])

⇒これは通る(VSCodeのインタラクティブモードでの実行結果)


手順2: requestライブラリを使う

  1. 公式ブログで紹介されていて、PowerShellで実行できたコードを、ChatGPTにPythonコードに書き換えてもらう。
PowerShell
(Invoke-WebRequest -method POST -Body '{"model":"llama3", "prompt":"Why is the sky blue?", "stream": false}' -uri http://localhost:11434/api/generate ).Content | ConvertFrom-json
request-sample1.py
import requests
import json

url = 'http://localhost:11434/api/generate'
data = {'model': 'llama3', 'prompt': 'Why is the sky blue?', 'stream': False}
headers = {'Content-Type': 'application/json'}

response = requests.post(url, headers=headers, data=json.dumps(data))
result = response.json()

print(result)

⇒通る。※システム環境変数にNO_PROXYが設定してあるから

  1. proxyを無効にするコードを追加する
request-python2.py
import requests
import json

url = 'http://localhost:11434/api/generate'
data = {'model': 'llama3', 'prompt': 'Why is the sky blue?', 'stream': False}
headers = {'Content-Type': 'application/json'}
# プロキシを無効にする設定
proxies = {
    "http": None,
    "https": None
}

response = requests.post(url, headers=headers, data=json.dumps(data), proxies=proxies)
result = response.json()
# response.json()で取得した結果からresponseキーの値を取得する
answer = result.get('response')
print(answer)

⇒もちろんこれも通る。


手順3: openaiライブラリを使う

  1. 公式ブログ公式リポジトリで紹介されているように、openaiライブラリを使ってみる。
openai-sample1.py
from openai import OpenAI
client = OpenAI(
    base_url='http://localhost:11434/v1/',
    api_key='ollama', #ダミーでOK
)

chat_completion = client.chat.completions.create(
    messages=[
        {
            'role': 'user',
            'content': 'Why is the sky blue?',
        }
    ],
    model='llama3',
)

print(chat_completion.choices[0].message.content)

⇒通った。
注意: openaiのバージョンが古いとエラーになる

手順4: requestライブラリを使った簡単なチャットボット

  1. Ollama公式リポジトリのpython-simplechatのclient.pyコードを修正して実行する。
client.py
import json
import requests

# 注意: Ollamaは`ollama serve`でサーバーを起動しておく必要があります。
model = "llama3"  

def chat(messages):
    r = requests.post(
        # "http://0.0.0.0:11434/api/chat",
        "http://localhost:11434/api/chat",
        json={"model": model, "messages": messages, "stream": True},
        # proxies={"http": None, "https": None} #プロキシを無効にするときは必要
    )
    r.raise_for_status()
    output = ""

    for line in r.iter_lines():
        body = json.loads(line)
        if "error" in body:
            raise Exception(body["error"])
        if body.get("done") is False:
            message = body.get("message", "")
            content = message.get("content", "")
            output += content
            # the response streams one token at a time, print that as we receive it
            print(content, end="", flush=True)

        if body.get("done", False):
            message["content"] = output
            return message

def main():
    messages = []

    while True:
        user_input = input("Enter a prompt: ")
        if not user_input:
            exit()
        print()
        messages.append({"role": "user", "content": user_input})
        message = chat(messages)
        messages.append(message)
        print("\n\n")

if __name__ == "__main__":
    main()

ターミナルを通じてではあるが、チャットができる。

shohei6117shohei6117

Llama3をOllamaで動かす #5

ゴール

  • 同一ネットワークにある別のPCからOllamaに接続してチャットできる

まとめ

  • システム環境変数 OLLAMA_HOST0.0.0.0に設定すれば良い

参考にした記事

手順

  1. 稼働中のOllamaを落とす
  2. システム環境変数にOLLAMA_HOSTを新規追加する
  3. Ollamaを再起動する
  4. 別のPCから接続できるか確認する
  • ***.***.***.***はOllamaが起動しているPCの固定IPアドレス
コマンドプロンプト
curl http://***.***.***.***:11434/api/generate -d "{\"model\": \"llama3\", \"prompt\": \"Why is the sky blue?\"}"

⇒通った。

shohei6117shohei6117

Llama3をOllamaで動かす #6

ゴール

  • Chrome拡張機能のOllama-UIをつかって、Ollamaで動いているLlama3とチャットする

まとめ

  • 同一PCではすぐ使えた
  • 同一ネットワークにある別のPCからもアクセスできたが、返信が取得できず(現状未解決)

参考リンク

手順: Ollamaが動いているPCで使う

  1. PowerShellでOllamaを起動する ollama serve

  2. Chromeを起動して、ollama-ui拡張機能を追加する

  3. インストールした拡張機能を、ブラウザにピン止めして、クリックするとアプリが起動する

  4. あとはチャットするだけ

手順2: 同一ネットワークにある別のPCから使う

前提:コマンドラインやPowerShellからは接続できるようになっていること

  1. 別のPC(クライアントPC)にも、Chromeにollama-ui拡張機能を追加する
  2. アプリを起動すると以下のエラーが出る

⇒表示されたメッセージをもとに、システム環境変数にOLLAMA_ORIGINを設定する

⇒Ollamaを再起動して、再度接続してみる
まだエラーが出る。


  1. あらためてollama serve --helpを叩いてみる

⇒メッセージどおりのコマンドを打つ

PowerShell
OLLAMA_ORIGINS=chrome-extension://cmgdpmlhgjhoadnonobjeekmfcehffco ollama serve

⇒エラーになる

⇒コマンドを修正する

PowerShell
$env:OLLAMA_ORIGINS="chrome-extension://cmgdpmlhgjhoadnonobjeekmfcehffco"; $env:OLLAMA_HOST="0.0.0.0:11434"; ollama serve

⇒通った。

⇒別のPCのOllama-UIからもアクセスできた! でも返信が取得できない。。

shohei6117shohei6117

Llama3をOllamaで動かす #7

ゴール

  • ollama-pythonライブラリを使って、チャット回答をストリーミング表示する

まとめ

  • 簡単にできた。
  • 初回だけ少し待つが、2回目からはストレスなく回答が返ってくる。
  • 次は簡単なチャットアプリを作ってみる

参考リンク

手順

  1. 以下のPythonコードを実行する、だけ
    注意: hostのIPアドレスにはOllamaが稼働しているPCの固定IPを入れる
ollama-python-sample-streaming.py

import os
os.environ['HTTP_PROXY'] = ''
os.environ['HTTPS_PROXY'] = ''

from ollama import Client
client = Client(host='http://161.93.***.***:11434') #固定IPを入れる
response = client.chat(
  model='llama3', 
  messages=[{
    'role': 'user',
    'content': 'Why is the sky blue?',
  },],
  stream=True)

for chunk in response:
  print(chunk['message']['content'], end='', flush=True)

毎回 回答が変わるのが面白い。

shohei6117shohei6117

Llama3をOllamaで動かす #8

ゴール

  • streamlitを使ったチャットボットを作る

まとめ

  • 初回の回答だけ遅いが、それを超えるとサクサク動く
  • たまに回答がおかしくなる
  • 本当はstliteを使いたいが、openaiをインポートできない

参考リンク

手順

  1. PowerShellでOllamaを起動する
PowerShell
ollama serve
  1. 必要なライブラリ(streamlitとopenai)をインストールする
PowerShell
python -m pip install streamlit openai
  1. 以下のコードをファイル保存する
streamlit-chatbot.py
from openai import OpenAI
import streamlit as st

# ページ設定
st.set_page_config(
    page_title="Ollama-Chatbot",
    page_icon="🤖",
    layout="wide",
)

# タイトルの表示
st.title("Chat with Ollama🤖")
st.caption("会話をリセットしたいときはページを再読み込みしてね。")

# OpenAIクライアントの設定
client = OpenAI(
    # base_url='http://localhost:11434/v1/', # ローカル環境の場合
    base_url='http://***.***.***.***:11434/v1/', # 別のPCから接続する場合(実際のIPアドレスを入力する)
    api_key='dummy' # ダミーでOK
    )

# LLMモデルの指定
chat_model = "llama3"

# 初期メッセージの設定
if "messages" not in st.session_state:
    st.session_state["messages"] = [
        # systemに役割を付与
        {"role": "system", "content": "You are an excellent AI Assistant. You must provide every answer in Japanese. よろしくね!"},in Japanese. よろしくね!"},
        # assistantのメッセージ
        {"role": "assistant", "content": f"こんにちは。私は{chat_model}です。 **何でも聞いてください。** "}
        ]

# 以前のメッセージを表示
for message in st.session_state.messages:
    # roleによってアバターを切り替える
    if message["role"] == "system":
        st.chat_message(message["role"], avatar='💻').write(message['content'])
    elif message["role"] == "assistant":
        st.chat_message(message["role"], avatar='🤖').write(message["content"])
    elif message["role"] == "user":
        st.chat_message(message["role"], avatar='👦').write(message["content"])

# ユーザーの入力後の処理
if prompt := st.chat_input("What is up?"):
    # ユーザーのメッセージを追加
    st.session_state.messages.append({"role": "user", "content": prompt})

    # ユーザーのメッセージを表示
    with st.chat_message("user", avatar='👦'):
        st.markdown(prompt)

    # チャットボットの返答を取得&表示
    with st.chat_message("assistant", avatar='🤖'):
        # チャットボットの返答を取得
        stream = client.chat.completions.create(
            model=chat_model,
            messages=st.session_state.messages,
            stream=True,
        )
        # 返信をstreaming出力
        response = st.write_stream(stream)

    # メッセージに返答を追加
    st.session_state.messages.append({"role": "assistant", "content": response})
  1. ファイル保存したディレクトリでstreamlitアプリを起動する
PowerShell
streamlit run streamlit-chatbot.py

⇒できた。

shohei6117shohei6117

phi3をOllamaで動かす #1

ゴール

  • Ollamaにphi3をpullして動かす
  • 自作アプリでphi3とチャットする

まとめ

  • ollmaコマンドが通らないときは「Docker Desktopとstreamlitアプリをシャットダウン」する。 それでもダメなときは「システム環境変数のOLLAMA_HOSTの一時削除」をする。
  • phi3とllama3を比べると、phi3は回答が速いが、すこし馬鹿かも。。

参考リンク

手順 #1: phi3をOllamaでpull & runする

  1. PowerShellを閉じて、稼働しているOllamaを終了する
  • タスクマネージャーでollama.exeやollama_llama_server.exeが実行中の場合は、マウス右クリックで「タスクの終了」をする。
  1. あらたにPowerShellを起動して、phi3をpull&runする
PowerShell
ollama run phi3

⇒エラーが出た

  1. トララブルシューティングを参考に、ログファイルを確認する
  • ollama.exeのあるフォルダをコマンドプロンプトから開く
コマンドプロンプト
explorer %LOCALAPPDATA%\Programs\Ollama

  • ollama app.exeのあるフォルダでPowerShellを開いて、以下コマンドを打つ
PowerShell
$env:OLLAMA_DEBUG="1"
& "ollama app.exe"
  • ollama app.exeが起動するので、画面右下のアイコンからlogファイルのあるフォルダを開く

※フォルダは C:\Users\(ユーザー名)\AppData\Local\Ollama

server.logを読んでも解決の糸口は見つからず。。

  1. DockerDesktopとstreamlitアプリをシャットダウンしてから再トライ。
  2. さらにシステム環境変数のOLLAMA_HOSTをいったん削除
    ⇒あっさり通った。。

手順 #2: streamlitアプリでチャットする

  1. 以下のコードをファイル保存する
streamlitコード
streamlit-chatbot2.py
from openai import OpenAI
import streamlit as st

# ページ設定
st.set_page_config(
    page_title="Ollama-Chatbot",
    page_icon="🤖",
    layout="wide",
)

# タイトルの表示
st.title("Chat with Ollama🤖")
st.caption("会話をリセットしたいときはページを再読み込みしてね。")

# OpenAIクライアントの設定
client = OpenAI(
    # base_url='http://localhost:11434/v1/', # ローカル環境の場合
    base_url='http://161.93.100.100:11434/v1/', # 別のPCから接続する場合
    api_key='dummy' # ダミーでOK
    )

# サイドバーでLLMモデルを選択
with st.sidebar.form(key='my_form'):
    chat_model = st.selectbox(
        "Select LLM model",
        ["llama3", "phi3"],
        index=0
        )
    submit_button = st.form_submit_button(label='Submit')
    st.warning("モデルを切り替えると、会話がリセットされます。")
    if submit_button:
        # st.session_state["messages"]を削除する
        del st.session_state["messages"]

# 初期メッセージの設定
if "messages" not in st.session_state:
    st.session_state["messages"] = [
        # systemに役割を付与
        # {"role": "system", "content": "You are an excellent AI Assistant. You must provide every answer in Japanese. よろしくね!"},
        {"role": "system", "content": "You are an excellent Japanese AI Assistant. 回答は必ず日本語でしてください。"},
        # assistantのメッセージ
        {"role": "assistant", "content": f"こんにちは。私は{chat_model}です。 **何でも聞いてください。** "}
        ]

    # 初回の応答時間短縮のために、初期メッセージを送信
    client.chat.completions.create(
        model=chat_model,
        messages=st.session_state.messages,
        )

# 以前のメッセージを表示
for message in st.session_state.messages:
    # roleによってアバターを切り替える
    if message["role"] == "system":
        st.chat_message(message["role"], avatar='💻').write(message['content'])
    elif message["role"] == "assistant":
        st.chat_message(message["role"], avatar='🤖').write(message["content"])
    elif message["role"] == "user":
        st.chat_message(message["role"], avatar='👦').write(message["content"])

# ユーザーの入力後の処理
if prompt := st.chat_input("What is up?"):
    # ユーザーのメッセージを追加
    st.session_state.messages.append({"role": "user", "content": prompt})

    # ユーザーのメッセージを表示
    with st.chat_message("user", avatar='👦'):
        st.markdown(prompt)

    # チャットボットの返答を取得&表示
    with st.chat_message("assistant", avatar='🤖'):
        # チャットボットの返答を取得
        stream = client.chat.completions.create(
            model=chat_model,
            messages=st.session_state.messages,
            stream=True,
        )
        # 返信をstreaming出力
        response = st.write_stream(stream)

    # メッセージに返答を追加
    st.session_state.messages.append({"role": "assistant", "content": response})
  1. streamlitアプリを起動する
PowerShell
streamlit run streamlit-chatbot2.py

⇒いい感じ。

shohei6117shohei6117

Command-R+とCommand-RをOllamaで動かす #1

ゴール

  • OllamaにCommand-R+とCommand-Rをpullして動かす
  • Open WebUIと自作アプリでphi3とチャットする

まとめ

  • Command-R+は重すぎて使えない。タイムアウトでエラーになるレベル。
     ⇒AzureかAWS経由で使った方がよさそう。
  • Command-Rもかなり重い。体感でLlama3の2倍から3倍遅い。でも日本語は自然。

参考リンク

手順 #1: PowerShellでモデルをpull&起動

  1. Phi3を導入したときの手順と同じ
PowerShell
ollama run command-r-plus
PowerShell
ollama run command-r-plus

⇒動いた

手順 #2: Streamlitアプリでチャットする

コードを修正する。

streamlitアプリのコード(修正版)
コード
from openai import OpenAI
import streamlit as st

# ページ設定
st.set_page_config(
    page_title="Ollama-Chatbot",
    page_icon="🤖",
    layout="wide",
)

# タイトルの表示
st.title("Chat with Ollama🤖")
st.caption("会話をリセットしたいときはページを再読み込みしてね。")

# OpenAIクライアントの設定
client = OpenAI(
    # base_url='http://localhost:11434/v1/', # ローカル環境の場合
    base_url='http://161.93.999.999:11434/v1/', # 別のPCから接続する場合
    api_key='dummy' # ダミーでOK
    )

# サイドバーでLLMモデルを選択
with st.sidebar.form(key='my_form'):
    chat_model = st.selectbox(
        "Select LLM model",
        ["llama3", "phi3", "command-r"],
        index=0
        )
    submit_button = st.form_submit_button(label='Submit')
    st.warning("モデルを切り替えると、会話がリセットされます。")
    if submit_button:
        # st.session_state["messages"]を削除する
        del st.session_state["messages"]

# 初期メッセージの設定
if "messages" not in st.session_state:
    st.session_state["messages"] = [
        # systemに役割を付与
        # {"role": "system", "content": "You are an excellent AI Assistant. You must provide every answer in Japanese. よろしくね!"},
        {"role": "system", "content": "You are an excellent Japanese AI Assistant. 回答は必ず日本語でしてください。"},
        # assistantのメッセージ
        {"role": "assistant", "content": f"こんにちは。私は{chat_model}です。 **何でも聞いてください。** "}
        ]

    # 初回の応答時間短縮のために、初期メッセージを送信
    client.chat.completions.create(
        model=chat_model,
        messages=st.session_state.messages,
        )

# 以前のメッセージを表示
for message in st.session_state.messages:
    # roleによってアバターを切り替える
    if message["role"] == "system":
        st.chat_message(message["role"], avatar='💻').write(message['content'])
    elif message["role"] == "assistant":
        st.chat_message(message["role"], avatar='🤖').write(message["content"])
    elif message["role"] == "user":
        st.chat_message(message["role"], avatar='👦').write(message["content"])

# ユーザーの入力後の処理
if prompt := st.chat_input("What is up?"):
    # ユーザーのメッセージを追加
    st.session_state.messages.append({"role": "user", "content": prompt})

    # ユーザーのメッセージを表示
    with st.chat_message("user", avatar='👦'):
        st.markdown(prompt)

    # チャットボットの返答を取得&表示
    with st.chat_message("assistant", avatar='🤖'):
        # チャットボットの返答を取得
        stream = client.chat.completions.create(
            model=chat_model,
            messages=st.session_state.messages,
            stream=True,
        )
        # 返信をstreaming出力
        response = st.write_stream(stream)

    # メッセージに返答を追加
    st.session_state.messages.append({"role": "assistant", "content": response})

⇒いい感じ。

shohei6117shohei6117

Open WebUIをアップデートする

ゴール

  • 公式サイトの手順どおりでできる。

まとめ

  • アップデート自体は簡単だが、コンテナ名を変えると会話や設定の継承に失敗する。

参考リンク

手順

  1. PowerShellで最新イメージを取得する
PowerShell
docker pull ghcr.io/open-webui/open-webui:main
  1. 既存のコンテナの停止&削除
    ※Docker Desktopでも出来る
PowerShell
docker stop open-webui
docker rm open-webui
  1. 新たにコンテナを作る
PowerShell
docker run -d -p 3000:8080 --add-host=host.docker.internal:host-gateway -v open-webui:/app/backend/data --name open-webui --restart always ghcr.io/open-webui/open-webui:main

⇒会話履歴が消えている。。
このプロセスでは、Docker ボリュームに保存されているデータを維持しながら、Open WebUI コンテナを最新バージョンに更新します。

⇒既存コンテナの名前がopen-webui-cpuだったことを思い出して、再度コンテナを作る

PowerShell
docker run -d -p 3000:8080 --add-host=host.docker.internal:host-gateway -v open-webui:/app/backend/data --name open-webui-cpu --restart always ghcr.io/open-webui/open-webui:main

⇒会話履歴は復活せず。。
⇒あらためてopen-webuiの名前でコンテナを作る。。

shohei6117shohei6117

gemma, mistral, llava-llama3をOllamaで動かす

ゴール

  • 3つのモデルとチャットできるようになる。
  • 特にllava-llama3には画像を説明させる。

まとめ

  • gemma、mistralともに応答速度は良好。精度検証はこれから。
  • llava-llama3はかたくなに英語で答える

参考リンク

手順

  1. DockerDesktopを停止
  2. 稼働中アプリ停止
  3. システム環境変数から OLLAMA_HOSTを一時削除
  4. モデルをpull
    ※1、2、3を実施しないと以下コマンドが通らなかった。
PowerShell
ollama pull gemma
ollama pull mistral
ollama pull llava-llama3
PowerShell画面

  1. システム変数にOLLAMA_HOST=0.0.0.0を再設定
  2. Chrome拡張機能の ollama-uiを使ってチャットする
gemmaの回答:ちょっと日本語がおかしい


mistralの回答:かなり日本語がおかしい


比較)Llama3の回答:かたくなに英語で答える





  1. llava-llama3に画像を説明してもらう。
    ※渡したのは私がロードバイクでレースを走っている画像(非掲載)
これは通らず。`OLLAMA_HOST`を設定しているから?
import os
os.environ['HTTP_PROXY'] = ''
os.environ['HTTPS_PROXY'] = ''

import ollama
res = ollama.chat(
	model="llava-llama3",
	messages=[
		{
			'role': 'user',
			'content': 'Describe this image:',
			'images': ['./bike.jpg']
		}
	]
)

print(res['message']['content'])

⇒コードを修正

こっちは通った。
import os
os.environ['HTTP_PROXY'] = ''
os.environ['HTTPS_PROXY'] = ''

from ollama import Client
client = Client(host='http://localhost:11434')
res = client.chat(
	model="llava-llama3",
	messages=[
		{
			'role': 'user',
			'content': 'この画像を説明して。日本語で!:',
			'images': ['./bike.jpg']
		}
	]
)

print(res['message']['content'])

⇒でも回答はかたくなに英語。。

shohei6117shohei6117

Llava-llama3とstreamlitを通じてチャットする

ゴール

  • マルチモーダルモデルのLlava-llama3とstreamlitチャットボットを通じてチャットする

まとめ

  • マルチモーダルモデルはOpenAIライブラリは使えないので、Ollamaライブラリを使う。
  • Ollamaライブラリでのストリーミング出力方法が分かった。
  • st.file_uploadで403エラーが出たときの対処方法が分かった。(後述)
  • かたくなに英語で回答する。

参考リンク

手順

  1. 以下のpythonコードを保存する
コード

client2 = ollama.Client(host='http://161.93.***.***:11434')の部分は書き換える。

main.py
import streamlit as st
import base64
import ollama

# ページ設定
st.set_page_config(page_title="Ollama-Chatbot", page_icon="🤖", layout="wide")
st.title("Chat with Ollama🤖")

# クライアント設定
# アドレスは書き換える!
client2 = ollama.Client(host='http://161.93.***.***:11434')

# モデル選択
model_dict = {
    "Meta Llama3": "llama3", 
    "Microsoft Phi3": "phi3",
    "Cohere Command-R": "command-r", 
    "Google Gemma": "gemma",
    "Mistral": "mistral", 
    "Llava-Llama3(画像認識可能)": "llava-llama3"
}
with st.sidebar.form(key='my_form'):
    selected_key = st.selectbox("モデルを選択してください:", list(model_dict.keys()), index=0)
    chat_model = model_dict[selected_key]
    multimodal = (chat_model == "llava-llama3")
    if st.form_submit_button(label='選択 or 切替!'):
        if 'messages' in st.session_state:
            del st.session_state.messages
    st.warning("モデルを切り替えると、会話がリセットされます。")

# 会話リセットボタン
with st.sidebar.form(key='reset_form'):
    if st.form_submit_button(label='会話のリセット!'):
        if 'messages' in st.session_state:
            del st.session_state.messages

# 初期メッセージ登録
if "messages" not in st.session_state:
    initial_messages = [
        {"role": "system", "content": "You are an AI agent who is imaginative and fluent in Japanese. よろしくね!"}, 
        {"role": "assistant", "content": f"こんにちは。私は{chat_model}です。 **何でも聞いてください。**"}
        ]
    st.session_state.messages = initial_messages
    # 初回の応答時間短縮のために、初期メッセージを送信
    client2.chat(model=chat_model, messages=initial_messages)

# メッセージ表示
for message in st.session_state.messages:
    if message["role"] == "system":
        avatar = '💻'
    elif message["role"] == "assistant":
        avatar = '🤖'
    elif message["role"] == "user":
        avatar = '👦'
    st.chat_message(message["role"], avatar=avatar).write(message['content'])

# チャットボットの返答生成。ストリーミングで返答を生成
def generate_response():
    response = client2.chat(model=chat_model, stream=True, messages=st.session_state.messages)
    for partial_resp in response:
        token = partial_resp["message"]["content"]
        st.session_state.full_message += token
        yield token

if prompt := st.chat_input("ここに入力してください。"):
    prompt += '。日本語で回答して。'
    st.session_state.messages.append({"role": "user", "content": prompt})
    st.chat_message("user", avatar='👦').write(prompt)
    st.session_state.full_message = ""
    st.chat_message("assistant", avatar='🤖').write_stream(generate_response)
    st.session_state.messages.append({"role": "assistant", "content": st.session_state.full_message})

# マルチモーダルモデルの画像アップロードと処理
if multimodal:
    uploaded_file = st.sidebar.file_uploader("👇ここに画像ファイルをアップロードしてください。", type=['jpg', 'png', 'jpeg'])
    if uploaded_file:
        st.sidebar.image(uploaded_file, caption="Uploaded Image.", use_column_width=True)
        if st.sidebar.button("説明して!", key='button'):
            # 画像をbase64エンコード
            image_data = base64.b64encode(uploaded_file.getvalue()).decode()
            # 画像を含むメッセージを送信する
            prompt = 'この画像を説明して'
            st.session_state.messages.append({'role': 'user', 'content': "この画像を説明して:", 'images': [image_data]})
            st.chat_message("user", avatar='👦').write(prompt)
            st.session_state.full_message = ""
            st.chat_message("assistant", avatar='🤖').write_stream(generate_response)
            st.session_state.messages.append({"role": "assistant", "content": st.session_state.full_message})
            
            # どうしても回答が英語になるので、次の処理も追加
            prompt = "日本語でも説明して。"
            st.session_state.messages.append({"role": "user", "content": prompt})
            st.chat_message("user", avatar='👦').write(prompt)
            st.session_state.full_message = ""
            st.chat_message("assistant", avatar='🤖').write_stream(generate_response)
            st.session_state.messages.append({"role": "assistant", "content": st.session_state.full_message})

  1. Pythonコードを保存したフォルダに、.pythonフォルダを作る
  2. .pythonフォルダにconfig.tomlファイルを作る
config.toml
[server]
enableXsrfProtection = false
enableCORS = false
  1. streamlitを起動する
PowerShell
streamlit run main.py

⇒できた!

shohei6117shohei6117

OllamaのModelfileを理解する

ゴール

  • Modelfileを理解して、カスタムモデルを作れるようになる

まとめ

  • ollama create [定義するモデル名] -f .\Modelfileで簡単にカスタムモデルを作れる
    • まだTEMPLATEの書き方はまだ理解不十分
  • Modelfileの実例はopenwebui.comにある
  • Open WebUIだけでカスタムモデルを定義する方法もある

参考リンク

手順 #1

  1. 公式doc Ollama Model Fileを読む
- A model file is the blueprint to create and share models with Ollama.
- モデル ファイルは、モデルを作成して Ollama と共有するための青写真です。
切り抜き

  • 設定する項目:
    • FROM (必須)
      • モデル作成に使う基本モデルを指定する
    • PARAMETER
      • TEMPERATUREとかTOP_KとかTOP_Pとか色々
    • TEMPLATE
      • 構文はモデルによって異なります。
    • SYSTEM
      • TENPLATE内で使うSYSTEMメッセージを指定
    • ADAPTER
      • ベースモデルに適用する LoRA アダプタを指定するオプション命令。
    • LICENSE
    • MESSAGE
      • メッセージ履歴を指定する。
  • Note:
    • 大文字と小文字は区別されない
    • 指示の順番は任意

なんとなくわかってきた。

  1. まずはインストール済みモデルのmodelfileを覗いてみる
PowerShell
ollama show llama3 --modelfile
出力画面

⇒コメントなどを削除してみる

さまざまなモデルのmodelfile
Llama3のmodelfile
FROM C:\Users\********\.ollama\models\blobs\sha256-00e1317cbf74d901080d7100f57580ba8dd8de57203072dc6f668324ba545f29

TEMPLATE "{{ if .System }}<|start_header_id|>system<|end_header_id|>

{{ .System }}<|eot_id|>{{ end }}{{ if .Prompt }}<|start_header_id|>user<|end_header_id|>

{{ .Prompt }}<|eot_id|>{{ end }}<|start_header_id|>assistant<|end_header_id|>

{{ .Response }}<|eot_id|>"

PARAMETER num_keep 24
PARAMETER stop <|start_header_id|>
PARAMETER stop <|end_header_id|>
PARAMETER stop <|eot_id|>
Phi3のmodelfile
FROM C:\Users\********\.ollama\models\blobs\sha256-4fed7364ee3e0c7cb4fe0880148bfdfcd1b630981efa0802a6b62ee52e7da97e

TEMPLATE "{{ if .System }}<|system|>
{{ .System }}<|end|>
{{ end }}{{ if .Prompt }}<|user|>
{{ .Prompt }}<|end|>
{{ end }}<|assistant|>
{{ .Response }}<|end|>
"

PARAMETER num_keep 4
PARAMETER stop <|user|>
PARAMETER stop <|assistant|>
PARAMETER stop <|system|>
PARAMETER stop <|end|>
PARAMETER stop <|endoftext|>
mistralのmodelfile
FROM C:\Users\********\.ollama\models\blobs\sha256-e8a35b5937a5e6d5c35d1f2a15f161e07eefe5e5bb0a3cdd42998ee79b057730

TEMPLATE [INST] {{ .System }} {{ .Prompt }} [/INST]

PARAMETER stop [INST]
PARAMETER stop [/INST]
command-Rのmodelfile
FROM C:\Users\********\.ollama\models\blobs\sha256-8a9611e7bca168be635d39d21927d2b8e7e8ea0b5d0998b7d5980daf1f8d4205

TEMPLATE {{ if .System }}<|START_OF_TURN_TOKEN|><|SYSTEM_TOKEN|>{{ .System }}<|END_OF_TURN_TOKEN|>{{ end }}{{ if .Prompt }}<|START_OF_TURN_TOKEN|><|USER_TOKEN|>{{ .Prompt }}<|END_OF_TURN_TOKEN|>{{ end }}<|START_OF_TURN_TOKEN|><|CHATBOT_TOKEN|>{{ .Response }}<|END_OF_TURN_TOKEN|>

PARAMETER stop <|START_OF_TURN_TOKEN|>
PARAMETER stop <|END_OF_TURN_TOKEN|>

⇒TEMPLATEの書き方が難しい。。

手順 #2

手始めに、既存モデルからプロンプトを利用してカスタムモデルを作ってみる

  1. Modelfileを作る
Modelfile
# ベースモデルを選ぶ
FROM llama3

# パラメータをセットする
PARAMETER temperature 1

# システムメッセージをセットする
SYSTEM """
You are Son Goku from Doragon Ball, acting as an assistant.
"""

このファイルをModelfileという名前で保存する

  1. カスタムモデルを作る
  • Modelfileと同じディレクトリでPowerShellを起動して、以下のコマンドを流す。
PowerShell
ollama create Goku-llama3 -f ./Modefile

⇒成功

  1. カスタムモデルを起動する
  • すでに ollama serveしている場合は自動でモデルが起動する
  • まだの場合は ollama serveあるいはollama run Goku-llama3で起動する。
  1. カスタムモデルとチャットしてみる
  • PowerShellで

    ⇒いい感じ

  • Ollama-UIで
    ⇒あれ、⇒問題なし

  • streamlitチャットで

    ⇒いい感じ

※最初に投げるプロンプトに次の1行が入っていたのだが、これがModelfileで定義したSYSTEMの内容を上書きしてしまうので削除した。
{"role": "system", "content": "You are an AI agent who is imaginative and fluent in Japanese. よろしくね!"},

修正後のstreamlitコード
import streamlit as st
import base64
import ollama

# ページ設定
st.set_page_config(page_title="Ollama-Chatbot", page_icon="🤖", layout="wide")
st.title("Chat with Ollama🤖")

# クライアント設定
client2 = ollama.Client(host='http://161.93.***.***:11434')

# モデル選択
model_dict = {
    "Meta Llama3": "llama3", 
    "Microsoft Phi3": "phi3",
    "Cohere Command-R": "command-r", 
    "Google Gemma": "gemma",
    "Mistral": "mistral", 
    "孫悟空-llama3": "Goku-llama3", # 2024-05-10追加    
    "Llava-Llama3(画像認識可能)": "llava-llama3"
}
with st.sidebar.form(key='my_form'):
    selected_key = st.selectbox("モデルを選択してください:", list(model_dict.keys()), index=0)
    chat_model = model_dict[selected_key]
    multimodal = (chat_model == "llava-llama3")
    if st.form_submit_button(label='選択 or 切替!'):
        if 'messages' in st.session_state:
            del st.session_state.messages
    st.warning("モデルを切り替えると、会話がリセットされます。")

# 会話リセットボタン
with st.sidebar.form(key='reset_form'):
    if st.form_submit_button(label='会話のリセット!'):
        if 'messages' in st.session_state:
            del st.session_state.messages

# 初期メッセージ登録
if "messages" not in st.session_state:
    initial_messages = [
        # {"role": "system", "content": "You are an AI agent who is imaginative and fluent in Japanese. よろしくね!"}, 
        # {"role": "assistant", "content": f"こんにちは。私は{chat_model}です。 **何でも聞いてください。**"}
        {"role": "assistant", "content": f"**何でも聞いてください。**"}
        ]
    st.session_state.messages = initial_messages
    # 初回の応答時間短縮のために、初期メッセージを送信
    client2.chat(model=chat_model, messages=initial_messages)

# メッセージ表示
for message in st.session_state.messages:
    if message["role"] == "system":
        avatar = '💻'
    elif message["role"] == "assistant":
        avatar = '🤖'
    elif message["role"] == "user":
        avatar = '👦'
    st.chat_message(message["role"], avatar=avatar).write(message['content'])

# チャットボットの返答生成。ストリーミングで返答を生成
def generate_response():
    response = client2.chat(model=chat_model, stream=True, messages=st.session_state.messages)
    for partial_resp in response:
        token = partial_resp["message"]["content"]
        st.session_state.full_message += token
        yield token

if prompt := st.chat_input("ここに入力してください。"):
    prompt += '。日本語で回答して。'
    st.session_state.messages.append({"role": "user", "content": prompt})
    st.chat_message("user", avatar='👦').write(prompt)
    st.session_state.full_message = ""
    st.chat_message("assistant", avatar='🤖').write_stream(generate_response)
    st.session_state.messages.append({"role": "assistant", "content": st.session_state.full_message})

# マルチモーダルモデルの画像アップロードと処理
if multimodal:
    uploaded_file = st.sidebar.file_uploader("👇ここに画像ファイルをアップロードしてください。", type=['jpg', 'png', 'jpeg'])
    if uploaded_file:
        st.sidebar.image(uploaded_file, caption="Uploaded Image.", use_column_width=True)
        if st.sidebar.button("説明して!", key='button'):
            # 画像をbase64エンコード
            image_data = base64.b64encode(uploaded_file.getvalue()).decode()
            # 画像を含むメッセージを送信する
            prompt = 'この画像を説明して'
            st.session_state.messages.append({'role': 'user', 'content': "この画像を説明して:", 'images': [image_data]})
            st.chat_message("user", avatar='👦').write(prompt)
            st.session_state.full_message = ""
            st.chat_message("assistant", avatar='🤖').write_stream(generate_response)
            st.session_state.messages.append({"role": "assistant", "content": st.session_state.full_message})
            
            # どうしても回答が英語になるので、次の処理も追加
            prompt = "日本語でも説明して。"
            st.session_state.messages.append({"role": "user", "content": prompt})
            st.chat_message("user", avatar='👦').write(prompt)
            st.session_state.full_message = ""
            st.chat_message("assistant", avatar='🤖').write_stream(generate_response)
            st.session_state.messages.append({"role": "assistant", "content": st.session_state.full_message})

補足

  1. Modelfileの実例は以下のサイトに沢山ある
  • OpenWebUI ※OllamaHubという名前だったようだ

    たとえばこんな感じ
  1. Open WebUIでカスタムモデルを定義して使う方法もある
  • このページの「Way 2. Create and Use Custom Models with Open WebUI」を参照
shohei6117shohei6117

(未完) ollama pullできないFugaku-LLMをollmaで動かす

ゴール

  • Fugaku-LLMをollamaで動かせるようにする

まとめ

  • ***

参考サイト

事前知識

  • ollama公式リポジトリより

    ⇒Modelfileとは?GGUFモデルとは?

  • まずGGUFモデルを理解する

    • huggingfaceサイトより GGUF

      ⇒まだわからない。
    • GGML/GGUF/GPTQの違い

      ⇒GGUFはCPUだけでAIを動かすためのライブラリで、GGMLの後継フォーマットと分かった。
  • つぎにモデルファイルについて

手順 #1: GGUFファイルを入手

  1. huggingfaceの Fugaku-LLM にアクセス
  2. Fugaku-LLM/Fugaku-LLM-13B-instruct-ggufに進む
  3. LICENSE_jaを読んでみる
ChatGPTによる抜粋

「Fugaku-LLM利用規約」は、スーパーコンピュータ「富岳」を用いた大規模言語モデルの利用条件を定めたものです。利用者は、商用・非商用問わずFugaku-LLMを利用でき、改変や再配布が可能ですが、再配布時には本規約の遵守と改変の明記が必要です。Fugaku-LLMは現状有姿で提供され、開発者はいかなる保証もせず、利用に関連する損害の責任を負いません。また、禁止行為や輸出取引に関する規制が設けられており、利用者は自己責任で規約を遵守する必要があります。紛争が発生した場合の管轄裁判所は東京地方裁判所とされています。

  1. Fugaku-LLM-13B-instruct-0325b-q5_k_m.ggufをダウンロード(11.2GB)

手順 #2: Modelfileを作る

※ここから先は後回し

shohei6117shohei6117

OllamaでFugaku-llmとElayza-japaneseを動かす

ゴール

  • Ollama modelsにないFugaku-llmとElayza-japaneseをOllamaで動かす

まとめ

  • Ollamaトップページから検索するとFugakuもElayzaもヒットしてpullできる(誰が登録してくれているのかは不明)

関連リンク

手順

  1. Ollamaトップページの検索ボックスにElayzaFugakuと入力する
  2. 登録済モデルがヒットするので、それぞれpullする
ollama pull federer/elyza-japanese
ollama pull nebel/fugaku-llm:13b-instruct
  1. ollamaを起動する
  2. Chrome拡張機能のollama-uiを開く
  3. モデルが選択できるのでチャットしてみる →成功!
    どちらも高速。回答には個性がある。
テニスのルールを聞いてみた

Elayzaの回答

Fugaku-llmの回答

shohei6117shohei6117

OllamaをDifyなどから利用できるようにする方法

ゴール

  • 現状の運用方法をまとめる

まとめ

  • 自動起動するOllamaアプリをシャットダウンしたあとに、後述のbatファイルでOLLAMA_HOST環境変数を設定して、11434ポートを開放する。
    • ※システム環境変数に書き込んでおく方法だとダメなケースがあったため。

関連リンク

手順

  • 起動しているOllamaアプリをシャットダウンする
  • 以下の内容のbatファイルを作って、起動する。以上!
ollama起動.bat
@echo off
set OLLAMA_HOST=0.0.0.0:11434
ollama serve