1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

【WindowsローカルVLM】OllamaでQwen3-VL-8B(4bit)をホストし、日本語OCRをOpenAI互換API化する

1
Posted at

はじめに

本記事では、Windows上で qwen3-vl:8b をOllamaによりローカルホストし、UIスクリーンショットから日本語テキストを抽出するOCR APIを構築します。

最終的には、次の処理をWindows PC内で完結させます。

  • Qwen3-VL-8Bの4bit量子化版をローカル実行する
  • 日本語UI画像から文字列とフィールド構造を抽出する
  • サムネイル内の重複文字を除外する
  • OCR結果をJSON Schemaで固定する
  • Ollama Native APIから呼び出す
  • OpenAI互換の /v1/chat/completions APIから呼び出す
  • GPUが正しく使われているか確認する

本記事で構築するデータフローは次のとおりです。

OCR対象画像
    ↓
PowerShell / Pythonクライアント
    ↓
Ollama for Windows
    ↓
Qwen3-VL-8B Q4_K_M
    ↓
JSON Schemaで制約したOCR結果

今回利用するOllama版モデルは次の構成です。

項目 内容
モデル名 qwen3-vl:8b
モデル種別 Vision-Language Model
パラメータ数 8.77B
量子化方式 Q4_K_M
ダウンロードサイズ 約6.1 GB
ライセンス Apache-2.0
API Ollama Native API / OpenAI互換API
対応入力 Text / Image

本記事は2026年8月時点の情報を基にしています。OllamaとQwen3-VLの対応状況は更新されるため、再現環境では実際に使用したバージョンを記録してください。


背景と課題

Windowsでどの推論基盤を使うか

WindowsでVLMをローカルホストする場合、主な候補は次のとおりです。

ツール 特徴 今回の適性
Ollama Windowsネイティブ、モデル取得とAPI化が簡単 最有力
LM Studio GUIでモデルとVRAM設定を調整しやすい PoC向け
llama.cpp GGUFとGPUオフロードを細かく制御可能 上級者向け
vLLM 高スループット WindowsではWSL2が基本
SGLang 高並列・本番推論向け Linuxサーバ向け

今回は、Windowsネイティブで動作し、画像入力・Structured Outputs・OpenAI互換APIに対応するOllamaを使用します。

一般的なOCRより難しい点

対象がUIスクリーンショットの場合、単純な文書OCRとは異なる難しさがあります。

  • 小さい日本語文字
  • 装飾背景上のテキスト
  • 複数カラム
  • キャラクター名とセリフの対応付け
  • サムネイル画像内の重複文字
  • SU180Fのような構造化フィールド
  • 読めない文字をモデルが文脈から補完する危険性

このため、文字認識だけではなく、画像内レイアウトの理解とKey Information Extractionを同時に実行できるVLMを使用します。

システム要件

Ollama for Windowsの公式要件は次のとおりです。

  • Windows 10 22H2以降
  • Windows HomeまたはPro
  • NVIDIA GPUの場合は対応ドライバ
  • AMD GPUの場合はROCmまたはVulkan対応ドライバ
  • モデルとランタイムを保存できるディスク容量

NVIDIA GPUを使用する場合、本記事では最新のStudio DriverまたはGame Ready Driverへの更新を推奨します。

GPUメモリの目安

qwen3-vl:8b は約6.1 GBのQ4_K_Mモデルです。

ただし、モデルファイル以外にも次のメモリが必要です。

  • Vision Encoderの中間テンソル
  • KV Cache
  • CUDAコンテキスト
  • 画像トークン
  • 推論ワークスペース
  • 並列リクエスト用バッファ

実務上の目安は次のとおりです。

VRAM 目安
6 GB 8Bは厳しい。qwen3-vl:4bを推奨
8 GB 単一画像・短いコンテキストなら動作候補
12 GB 8Bの実用ライン
16 GB以上 高解像度画像や余裕のある運用
24 GB以上 複数並列や上位モデルの検討が可能

8 GB VRAMで「起動すること」と「モデル全体がGPUへ載ること」は同義ではありません。Ollamaは一部をシステムRAMへオフロードする場合があります。後述する ollama ps で実際のロード状態を確認してください。


エラーログ

ここでは、Windows環境で発生しやすいエラーと対処方法を整理します。

ollamaコマンドが見つからない

ollama : 用語 'ollama' は、コマンドレット、関数、
スクリプト ファイル、または操作可能なプログラムの名前として認識されません。

Ollamaのインストール後に開いたPowerShellではない場合、PATHが反映されていない可能性があります。

まずPowerShellを閉じて、再度開きます。

それでも解決しない場合は、次のディレクトリを確認します。

Get-ChildItem "$env:LOCALAPPDATA\Programs\Ollama"

実行ファイルが存在する場合は、絶対パスで確認します。

& "$env:LOCALAPPDATA\Programs\Ollama\ollama.exe" --version

APIへ接続できない

Invoke-RestMethod:
接続先のコンピューターによって拒否されたため、接続できませんでした。

Ollamaのバックグラウンドプロセスが起動していない可能性があります。

スタートメニューからOllamaを起動し、次を実行します。

Invoke-RestMethod http://localhost:11434/api/version

手動でサーバを起動する場合は次です。

ollama serve

ただし、Ollamaアプリがすでに起動している状態で ollama serve を実行すると、ポート競合が発生する場合があります。


ポートが使用中になっている

Error: listen tcp 127.0.0.1:11434:
bind: Only one usage of each socket address is normally permitted.

すでにOllamaアプリがバックグラウンドでAPIサーバを起動しています。

この場合、追加で ollama serve を起動する必要はありません。

プロセスとポートを確認します。

Get-NetTCPConnection -LocalPort 11434 -ErrorAction SilentlyContinue
Get-Process ollama -ErrorAction SilentlyContinue

モデルが見つからない

Error: model 'qwen3-vl:8b' not found

モデルを事前に取得します。

ollama pull qwen3-vl:8b

取得済みモデルを確認します。

ollama ls

Qwen3-VLを利用するには、対応バージョンのOllamaが必要です。

ollama --version

古い場合はOllamaを更新してください。


GPUではなくCPUで動いている

OCRは動作するものの、極端に遅い場合があります。

ロード状態を確認します。

ollama ps

出力例です。

NAME            ID            SIZE     PROCESSOR          UNTIL
qwen3-vl:8b     xxxxxxxxxxxx  8.2 GB   100% GPU           4 minutes from now

PROCESSOR列の意味は次のとおりです。

表示 意味
100% GPU モデル全体がGPU上
100% CPU モデル全体がシステムRAM上
CPU/GPU混在 一部がGPU、一部がシステムRAM

NVIDIA GPUの状態も確認します。

nvidia-smi

Ollamaプロセスが表示されず、VRAM使用量も増えていない場合は、ドライバまたはGPU検出に問題がある可能性があります。


8 GB GPUでメモリ不足になる

Error: unable to allocate CUDA buffer

または次のようなエラーが発生することがあります。

500 Internal Server Error

対策は次の順で実施します。

  1. 同時リクエスト数を1にする
  2. コンテキスト長を4096以下にする
  3. 画像をカード単位にクロップする
  4. 入力画像の解像度を下げる
  5. ほかのGPUアプリを終了する
  6. qwen3-vl:4bへ切り替える

Windowsのユーザー環境変数へ次を設定すると、並列処理を抑制できます。

OLLAMA_NUM_PARALLEL=1
OLLAMA_MAX_LOADED_MODELS=1
OLLAMA_CONTEXT_LENGTH=4096

環境変数を変更した後は、タスクトレイからOllamaを終了し、スタートメニューから再起動してください。


PowerShellでJSONが途中までしか生成されない

ConvertTo-Jsonは、ネストが深いオブジェクトを扱う場合に深さの指定が必要です。

悪い例です。

$body | ConvertTo-Json

修正版です。

$body | ConvertTo-Json -Depth 30

Vision入力とJSON Schemaはネストが深いため、-Depth 20以上を指定してください。


画像のBase64化に失敗する

Exception calling "ReadAllBytes" with "1" argument(s):
Could not find file ...

画像パスを絶対パスへ変換します。

$ImagePath = (Resolve-Path "C:\path\to\{input-image}.png").Path

パスの存在を確認します。

Test-Path $ImagePath

解決方法

1. WindowsとGPUを確認する

PowerShellを開き、Windowsのバージョンを確認します。

Get-CimInstance Win32_OperatingSystem |
    Select-Object Caption, Version, BuildNumber

GPUを確認します。

Get-CimInstance Win32_VideoController |
    Select-Object Name, AdapterRAM, DriverVersion

NVIDIA GPUの場合は次も実行します。

nvidia-smi

期待する状態は次のとおりです。

Windows 10 22H2以降、またはWindows 11
NVIDIAまたは対応AMD GPU
GPUドライバが認識されている

2. Ollama for Windowsをインストールする

公式サイトから OllamaSetup.exe を取得し、実行します。

Ollamaは通常、管理者権限なしでユーザーディレクトリへインストールされます。

インストール後、新しいPowerShellを開きます。

ollama --version

APIの稼働も確認します。

Invoke-RestMethod http://localhost:11434/api/version

レスポンス例です。

{
  "version": "0.x.x"
}

ポイント解説

Windows版Ollamaは、インストール後にバックグラウンドで起動します。

通常は次のURLでAPIが待ち受けます。

http://localhost:11434

ローカル利用だけなら、追加のサーバ設定は不要です。


3. モデル保存先を変更する

モデルはデフォルトで次の場所へ保存されます。

C:\Users\{user-name}\.ollama\models

Qwen3-VL-8Bだけでも約6.1 GBを使用します。複数モデルを扱う場合は、空き容量の大きいSSDへ保存先を変更できます。

Windowsの「環境変数を編集」から、ユーザー環境変数として次を追加します。

変数名: OLLAMA_MODELS
変数値: D:\{ollama-models-dir}

PowerShellから設定する場合は次です。

[Environment]::SetEnvironmentVariable(
    "OLLAMA_MODELS",
    "D:\{ollama-models-dir}",
    "User"
)

設定後、タスクトレイからOllamaを終了し、再起動します。

既存モデルを移動する場合は、Ollamaを終了してからモデルディレクトリをコピーしてください。


4. Qwen3-VL-8Bを取得する

ollama pull qwen3-vl:8b

取得済みモデルを確認します。

ollama ls

出力例です。

NAME            ID            SIZE     MODIFIED
qwen3-vl:8b     xxxxxxxxxxxx  6.1 GB   ...

モデルの詳細を確認します。

ollama show qwen3-vl:8b

5. CLIで画像OCRを試す

OCR対象画像を用意します。

C:\path\to\{project-dir}\input.png

PowerShellで実行します。

ollama run qwen3-vl:8b @"
画像内に表示されている日本語テキストを読み取ってください。
画像から確認できない文字は推測しないでください。
判読できない箇所は [判読不能] としてください。

C:\path\to\{project-dir}\input.png
"@

より簡単な確認では次の形式でも実行できます。

ollama run qwen3-vl:8b `
    "画像内の日本語テキストを列挙してください。C:\path\to\{project-dir}\input.png"

ポイント解説

CLIは動作確認には便利ですが、次の課題があります。

  • 出力形式を厳密に固定しにくい
  • バッチ処理しにくい
  • エラー処理を組み込みにくい
  • 後段のDB保存やCSV化に向かない

実運用ではAPI経由で呼び出します。


6. Ollama Native APIでOCRする

作業ディレクトリを作成します。

New-Item `
    -ItemType Directory `
    -Force `
    -Path "C:\path\to\{project-dir}" |
    Out-Null

Set-Location "C:\path\to\{project-dir}"

次の内容を ocr-native.ps1 として保存します。

$ErrorActionPreference = "Stop"

# OCR対象画像
$ImagePath = (Resolve-Path ".\input.png").Path

# REST APIでは画像をBase64文字列として送信する
$ImageBase64 = [Convert]::ToBase64String(
    [IO.File]::ReadAllBytes($ImagePath)
)

# OCR結果のJSON Schema
$Schema = @{
    type = "object"
    properties = @{
        items = @{
            type = "array"
            items = @{
                type = "object"
                properties = @{
                    su = @{
                        type = @("string", "null")
                    }
                    floor = @{
                        type = @("integer", "null")
                    }
                    character = @{
                        type = @("string", "null")
                    }
                    dialogue = @{
                        type = @("string", "null")
                    }
                    status = @{
                        type = @("string", "null")
                    }
                }
                required = @(
                    "su",
                    "floor",
                    "character",
                    "dialogue",
                    "status"
                )
                additionalProperties = $false
            }
        }
    }
    required = @("items")
    additionalProperties = $false
}

$Prompt = @"
画像内の各カードについて、上部に表示された情報だけを抽出してください。
下部サムネイル内の文字は無視してください。

抽出対象:
- su: SU1などの識別子
- floor: 80Fなどの数値部分
- character: キャラクター名
- dialogue: セリフの原文
- status: OKなどの状態

制約:
- 画像から確認できない文字を推測しない
- 判読不能なフィールドはnullにする
- 句読点と三点リーダーを可能な限り保持する
- 同じ文字列を重複して出力しない
"@

$Body = @{
    model = "qwen3-vl:8b"
    messages = @(
        @{
            role = "user"
            content = $Prompt
            images = @($ImageBase64)
        }
    )
    format = $Schema
    stream = $false
    keep_alive = "10m"
    options = @{
        temperature = 0
        num_ctx = 4096
    }
}

$JsonBody = $Body | ConvertTo-Json -Depth 30

$Response = Invoke-RestMethod `
    -Method Post `
    -Uri "http://localhost:11434/api/chat" `
    -ContentType "application/json; charset=utf-8" `
    -Body $JsonBody `
    -TimeoutSec 300

# message.contentはJSON文字列なので再度パースする
$Result = $Response.message.content | ConvertFrom-Json

$Result | ConvertTo-Json -Depth 20

実行します。

powershell -ExecutionPolicy Bypass -File .\ocr-native.ps1

PowerShell 7を利用している場合は次です。

pwsh -File .\ocr-native.ps1

レスポンス例です。

{
  "items": [
    {
      "su": "SU1",
      "floor": 80,
      "character": "八幡",
      "dialogue": "他校と合同イベントねぇ...",
      "status": "OK"
    },
    {
      "su": "SU2",
      "floor": 180,
      "character": "雪乃",
      "dialogue": "一色さんの初仕事ね",
      "status": "OK"
    }
  ]
}

ポイント解説

Ollama Native APIでは、JSON Schemaを format に直接指定します。

format = $Schema

これにより、自由文ではなく、指定したオブジェクト構造に沿って生成されます。

ただし、JSON Schemaが保証するのは主に形式です。

次の正しさは別途評価する必要があります。

  • OCR文字列が正しいか
  • キャラクター名とセリフの対応が正しいか
  • サムネイル文字が混入していないか
  • 判読不能箇所を勝手に補完していないか

7. OpenAI互換APIでOCRする

既存アプリケーションがOpenAI SDKを利用している場合は、ベースURLを差し替えてOllamaへ接続できます。

次の内容を ocr-openai.ps1 として保存します。

$ErrorActionPreference = "Stop"

$ImagePath = (Resolve-Path ".\input.png").Path
$ImageBase64 = [Convert]::ToBase64String(
    [IO.File]::ReadAllBytes($ImagePath)
)
$ImageDataUrl = "data:image/png;base64,$ImageBase64"

$Schema = @{
    type = "object"
    properties = @{
        items = @{
            type = "array"
            items = @{
                type = "object"
                properties = @{
                    su = @{
                        type = @("string", "null")
                    }
                    floor = @{
                        type = @("integer", "null")
                    }
                    character = @{
                        type = @("string", "null")
                    }
                    dialogue = @{
                        type = @("string", "null")
                    }
                    status = @{
                        type = @("string", "null")
                    }
                }
                required = @(
                    "su",
                    "floor",
                    "character",
                    "dialogue",
                    "status"
                )
                additionalProperties = $false
            }
        }
    }
    required = @("items")
    additionalProperties = $false
}

$Body = @{
    model = "qwen3-vl:8b"
    messages = @(
        @{
            role = "system"
            content = @"
あなたは日本語UI画像を正確に解析するOCRエージェントです。
画像から確認できない文字は推測しないでください。
"@
        }
        @{
            role = "user"
            content = @(
                @{
                    type = "text"
                    text = @"
画像内の各カードについて、上部の情報だけを抽出してください。
下部サムネイル内の文字は無視してください。
SU、floor、character、dialogue、statusを返してください。
判読不能な値はnullにしてください。
"@
                }
                @{
                    type = "image_url"
                    image_url = $ImageDataUrl
                }
            )
        }
    )
    response_format = @{
        type = "json_schema"
        json_schema = @{
            name = "ocr_cards"
            strict = $true
            schema = $Schema
        }
    }
    temperature = 0
    max_tokens = 512
    stream = $false
}

$JsonBody = $Body | ConvertTo-Json -Depth 30

$Response = Invoke-RestMethod `
    -Method Post `
    -Uri "http://localhost:11434/v1/chat/completions" `
    -ContentType "application/json; charset=utf-8" `
    -Body $JsonBody `
    -TimeoutSec 300

$Content = $Response.choices[0].message.content
$Result = $Content | ConvertFrom-Json

$Result | ConvertTo-Json -Depth 20

実行します。

pwsh -File .\ocr-openai.ps1

OpenAI互換APIを使う利点

  • 既存のOpenAI SDKベース実装を流用しやすい
  • クラウドモデルとローカルモデルを切り替えやすい
  • LangChainなどの周辺ツールへ接続しやすい
  • APIクライアント側のインターフェースを統一できる

注意点

OllamaはOpenAI APIの完全な互換実装ではありません。

利用する機能について、Ollamaが対応しているリクエストフィールドを確認してください。


8. PythonのOpenAI SDKから呼び出す

Python 3.11以降を利用する例です。

仮想環境を作成します。

py -3.12 -m venv .venv
.\.venv\Scripts\Activate.ps1
python -m pip install -U pip
pip install -U openai pydantic

PowerShellの実行ポリシーで仮想環境を有効化できない場合は、現在のプロセスだけ許可します。

Set-ExecutionPolicy `
    -Scope Process `
    -ExecutionPolicy Bypass

次の内容を ocr_client.py として保存します。

from __future__ import annotations

import base64
import json
from pathlib import Path

from openai import OpenAI
from pydantic import BaseModel, ConfigDict


MODEL_ID = "qwen3-vl:8b"
IMAGE_PATH = Path("input.png").resolve()


class OcrItem(BaseModel):
    """1カード分のOCR結果です。"""

    model_config = ConfigDict(extra="forbid")

    su: str | None
    floor: int | None
    character: str | None
    dialogue: str | None
    status: str | None


class OcrResult(BaseModel):
    """画像全体のOCR結果です。"""

    model_config = ConfigDict(extra="forbid")

    items: list[OcrItem]


def encode_image_as_data_url(image_path: Path) -> str:
    """画像をOpenAI互換API用のData URLへ変換します。"""

    if not image_path.is_file():
        raise FileNotFoundError(f"画像が見つかりません: {image_path}")

    suffix = image_path.suffix.lower()
    mime_types = {
        ".png": "image/png",
        ".jpg": "image/jpeg",
        ".jpeg": "image/jpeg",
        ".webp": "image/webp",
    }

    try:
        mime_type = mime_types[suffix]
    except KeyError as exc:
        raise ValueError(f"未対応の画像形式です: {suffix}") from exc

    encoded = base64.b64encode(image_path.read_bytes()).decode("ascii")
    return f"data:{mime_type};base64,{encoded}"


def main() -> None:
    client = OpenAI(
        base_url="http://localhost:11434/v1/",
        api_key="ollama",  # ローカルOllamaでは値は無視される
    )

    image_data_url = encode_image_as_data_url(IMAGE_PATH)
    schema = OcrResult.model_json_schema()

    response = client.chat.completions.create(
        model=MODEL_ID,
        messages=[
            {
                "role": "system",
                "content": (
                    "あなたは日本語UI画像を正確に解析するOCRエージェントです。"
                    "画像から確認できない文字を推測しないでください。"
                ),
            },
            {
                "role": "user",
                "content": [
                    {
                        "type": "text",
                        "text": (
                            "各カード上部からsu、floor、character、"
                            "dialogue、statusを抽出してください。"
                            "下部サムネイル内の文字は無視し、"
                            "判読不能なフィールドはnullにしてください。"
                        ),
                    },
                    {
                        "type": "image_url",
                        "image_url": image_data_url,
                    },
                ],
            },
        ],
        response_format={
            "type": "json_schema",
            "json_schema": {
                "name": "ocr_cards",
                "strict": True,
                "schema": schema,
            },
        },
        temperature=0,
        max_tokens=512,
    )

    content = response.choices[0].message.content
    if content is None:
        raise RuntimeError("モデルからcontentが返されませんでした。")

    result = OcrResult.model_validate_json(content)
    print(json.dumps(result.model_dump(), ensure_ascii=False, indent=2))


if __name__ == "__main__":
    main()

実行します。

python .\ocr_client.py

ポイント解説

Pydanticを使用することで、次の2段階で形式を検証できます。

生成時:
JSON Schemaで出力制約
    ↓
受信後:
Pydanticで再検証

VLMの出力をそのままDBへ保存するのではなく、必ずアプリケーション側でも検証してください。


9. GPUロード状態を確認する

OCR APIを1回実行した後に確認します。

ollama ps

NVIDIA GPUの場合は次も確認します。

nvidia-smi

継続監視する場合です。

while ($true) {
    Clear-Host
    nvidia-smi
    Start-Sleep -Seconds 2
}

確認ポイントは次のとおりです。

  • OllamaプロセスがGPUを使用しているか
  • VRAM使用量が増えているか
  • モデルがCPUへ大きくオフロードされていないか
  • 画像処理時にVRAMが上限へ張り付いていないか

補足Tips

8 GB VRAMではコンテキストと並列数を抑える

8 GB GPUで試す場合は、ユーザー環境変数を次のように設定します。

[Environment]::SetEnvironmentVariable(
    "OLLAMA_NUM_PARALLEL",
    "1",
    "User"
)

[Environment]::SetEnvironmentVariable(
    "OLLAMA_MAX_LOADED_MODELS",
    "1",
    "User"
)

[Environment]::SetEnvironmentVariable(
    "OLLAMA_CONTEXT_LENGTH",
    "4096",
    "User"
)

設定後、Ollamaを再起動します。

モデルを複数同時にロードしないことも重要です。

ollama ps
ollama stop qwen3-vl:8b

8Bが厳しい場合は4Bへ切り替える

ollama pull qwen3-vl:4b

APIのモデル名だけを変更します。

qwen3-vl:8b
    ↓
qwen3-vl:4b

Ollamaライブラリ上のモデルサイズは、8Bが約6.1 GB、4Bが約3.3 GBです。

8 GB VRAMでは、4Bの方が次の点で運用しやすくなります。

  • GPUへ全体を載せやすい
  • 高解像度画像を扱いやすい
  • OOMの発生率を下げやすい
  • レスポンスを高速化しやすい

一方で、小さい日本語文字や類似漢字の精度は8Bと比較評価してください。


固定レイアウトなら画像を分割する

固定UIでは、画像全体をVLMへ渡すより、カード単位にクロップした方が安定します。

全体スクリーンショット
    ↓
カード領域を固定座標で分割
    ↓
各カードを個別にOCR
    ↓
結果を結合

主な利点です。

  • 小さい文字が相対的に大きくなる
  • サムネイル内文字の混入を減らせる
  • カード間の対応誤りを減らせる
  • Vision Encoderの負荷を抑えられる
  • 8 GB VRAMでも処理しやすくなる

モデルを大きくする前に、ROI設計を改善する価値があります。


Pillowで2倍拡大する

pip install -U pillow
from pathlib import Path

from PIL import Image, ImageEnhance


INPUT_PATH = Path("input.png")
OUTPUT_PATH = Path("input_scaled.png")
SCALE = 2

with Image.open(INPUT_PATH) as image:
    enlarged = image.resize(
        (image.width * SCALE, image.height * SCALE),
        Image.Resampling.LANCZOS,
    )

    # 軽いコントラスト補正に留める
    enhanced = ImageEnhance.Contrast(enlarged).enhance(1.15)
    enhanced.save(OUTPUT_PATH)

print(OUTPUT_PATH.resolve())

過度な二値化やシャープ化は、次の文字を壊す可能性があります。

  • 濁点
  • 半濁点
  • 句読点
  • 三点リーダー
  • 細い漢字の画線

前処理なし、2倍拡大、軽いコントラスト補正の3条件を比較してください。


OCRではtemperatureを0にする

Qwen3-VLのOllamaモデルには通常の生成用パラメータが設定されています。

OCR用途では、表現の多様性より再現性を優先します。

{
  "temperature": 0
}

ただし、temperature=0でも文字列の誤認識や補完が完全になくなるわけではありません。

プロンプトにも次を明記します。

画像から確認できない文字を推測しないでください。
判読不能なフィールドはnullにしてください。

モデルを事前ロードする

最初のAPIリクエストは、モデルロードにより時間がかかります。

事前ロードする場合は、空のリクエストを送ります。

$Body = @{
    model = "qwen3-vl:8b"
} | ConvertTo-Json

Invoke-RestMethod `
    -Method Post `
    -Uri "http://localhost:11434/api/generate" `
    -ContentType "application/json" `
    -Body $Body

APIリクエストでは keep_alive を指定できます。

keep_alive = "10m"

常時ロードするとVRAMを占有し続けるため、After Effects、Blender、ゲームなどと同じGPUを共有する場合は注意してください。


ログを確認する

Windows版Ollamaのログは、主に次のディレクトリにあります。

%LOCALAPPDATA%\Ollama

エクスプローラーで開きます。

explorer "$env:LOCALAPPDATA\Ollama"

主なログです。

app.log
server.log
upgrade.log

直近のサーバログを確認します。

Get-Content `
    "$env:LOCALAPPDATA\Ollama\server.log" `
    -Tail 100

ログを追従表示します。

Get-Content `
    "$env:LOCALAPPDATA\Ollama\server.log" `
    -Wait `
    -Tail 50

GPU検出、メモリ不足、モデルロード失敗の調査に利用できます。


LANへ直接公開しない

Ollamaはデフォルトで 127.0.0.1:11434 にバインドされます。

LAN内の別PCから利用する場合は、ユーザー環境変数へ次を設定できます。

OLLAMA_HOST=0.0.0.0:11434

PowerShellで設定する例です。

[Environment]::SetEnvironmentVariable(
    "OLLAMA_HOST",
    "0.0.0.0:11434",
    "User"
)

ただし、OllamaのローカルAPIには、一般的なWeb APIのような強制認証が標準で入るわけではありません。

そのため、単純なポート開放は避け、少なくとも次を組み合わせます。

  • Windows Defender Firewallで接続元IPを制限
  • VPN経由でのみ接続
  • 認証付きリバースプロキシを前段に置く
  • TLSを有効化
  • インターネットへ直接公開しない

ローカルPC内だけで利用する場合は、デフォルトの 127.0.0.1 のままにしてください。


モデル名を固定する

アプリケーションでは qwen3-vl ではなく、明示的にタグを指定します。

推奨:
qwen3-vl:8b

非推奨:
qwen3-vl

latest相当のモデルが将来変更された場合でも、意図しないモデル切り替えを防ぎやすくなります。

さらに厳密な再現性が必要な場合は、次も記録します。

ollama --version
ollama ls
ollama show qwen3-vl:8b
nvidia-smi

OCR精度を評価する

目視確認だけでは、モデルや前処理の比較が困難です。

100〜300枚程度の評価セットを作成し、次を計測します。

指標 内容
CER 文字単位の誤り率
完全一致率 セリフ全体が一致した割合
フィールド一致率 SU、floor、characterなどの一致率
Schema準拠率 JSON Schemaを満たした割合
重複混入率 サムネイル内の文字が混入した割合
補完率 画像にない文字を生成した割合
レイテンシ 1画像あたりの処理時間
GPU使用量 VRAMとGPU使用率

比較条件の例です。

モデル:
- qwen3-vl:4b
- qwen3-vl:8b

入力:
- 元画像
- 2倍拡大
- カード単位クロップ

VRAM:
- 8 GB
- 12 GB以上

専用OCRとのハイブリッド構成

大量処理やBounding Boxが必要な場合は、VLM単体より専用OCRとの二段構成が有効です。

画像
  ↓
PP-OCRなどの専用OCR
  ├── テキスト
  ├── Bounding Box
  └── confidence
  ↓
Qwen3-VL
  ├── フィールド割当
  ├── 読み順の整理
  ├── 重複除去
  └── JSON正規化

VLMは意味理解に強い一方、文字ごとの座標やconfidenceを厳密に扱う用途では専用OCRに利点があります。


まとめ

本記事では、Windows上でOllamaを利用し、qwen3-vl:8b を日本語OCR用のローカルAPIとしてホストしました。

重要なポイントは次のとおりです。

  • WindowsではOllamaが最も簡単にQwen3-VLをローカルホストしやすい
  • qwen3-vl:8b は8.77B、Q4_K_M、約6.1 GB
  • OllamaはWindowsネイティブでNVIDIAおよび対応AMD GPUを利用できる
  • Native APIではJSON Schemaを format に指定する
  • OpenAI互換APIでは /v1/chat/completions を利用できる
  • Vision入力は画像をBase64またはData URLとして送信する
  • 8 GB VRAMでは単一画像・短いコンテキスト・並列数1から試す
  • GPUロード状態は ollama psnvidia-smi で確認する
  • 固定UIではカード単位のクロップが精度とメモリ効率に効く
  • JSON Schema準拠とOCR精度は分けて評価する
  • LANへ公開する場合は認証とネットワーク制限を前段に追加する

まずは qwen3-vl:8b を単一画像で動かし、8 GB GPUで厳しい場合は qwen3-vl:4b とROIクロップを比較してください。

その後、実データを用いてCER、フィールド完全一致率、サムネイル文字の混入率、レイテンシを計測すると、実運用に必要な構成を判断しやすくなります。


参考リンク

1
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?