はじめに
本記事では、Windows上で qwen3-vl:8b をOllamaによりローカルホストし、UIスクリーンショットから日本語テキストを抽出するOCR APIを構築します。
最終的には、次の処理をWindows PC内で完結させます。
- Qwen3-VL-8Bの4bit量子化版をローカル実行する
- 日本語UI画像から文字列とフィールド構造を抽出する
- サムネイル内の重複文字を除外する
- OCR結果をJSON Schemaで固定する
- Ollama Native APIから呼び出す
- OpenAI互換の
/v1/chat/completionsAPIから呼び出す - 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とは異なる難しさがあります。
- 小さい日本語文字
- 装飾背景上のテキスト
- 複数カラム
- キャラクター名とセリフの対応付け
- サムネイル画像内の重複文字
-
SU1や80Fのような構造化フィールド - 読めない文字をモデルが文脈から補完する危険性
このため、文字認識だけではなく、画像内レイアウトの理解と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にする
- コンテキスト長を4096以下にする
- 画像をカード単位にクロップする
- 入力画像の解像度を下げる
- ほかのGPUアプリを終了する
-
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 psとnvidia-smiで確認する - 固定UIではカード単位のクロップが精度とメモリ効率に効く
- JSON Schema準拠とOCR精度は分けて評価する
- LANへ公開する場合は認証とネットワーク制限を前段に追加する
まずは qwen3-vl:8b を単一画像で動かし、8 GB GPUで厳しい場合は qwen3-vl:4b とROIクロップを比較してください。
その後、実データを用いてCER、フィールド完全一致率、サムネイル文字の混入率、レイテンシを計測すると、実運用に必要な構成を判断しやすくなります。