FastAPI service wrapping the Surya-OCR-2 model (datalab-to) served through vLLM: legacy /v1/api/ai/* endpoints, an OpenAI-compatible /v1/chat/completions endpoint, a coalescing request batcher, a local OCR CLI, Docker packaging, multilingual example outputs, and quantization/concurrency benchmarks. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
79 lines
2.6 KiB
Python
79 lines
2.6 KiB
Python
"""Speed-vs-accuracy Pareto scatter and per-method bar charts."""
|
|
from __future__ import annotations
|
|
|
|
from pathlib import Path
|
|
from typing import Any, Dict, List
|
|
|
|
import matplotlib
|
|
|
|
matplotlib.use("Agg") # headless
|
|
import matplotlib.pyplot as plt # noqa: E402
|
|
|
|
|
|
def pareto_points(rows: List[Dict[str, Any]], x_key: str, y_key: str) -> List[Dict[str, Any]]:
|
|
points = []
|
|
for row in rows:
|
|
if row.get("status") != "ok":
|
|
continue
|
|
if row.get(x_key) is None or row.get(y_key) is None:
|
|
continue
|
|
points.append({
|
|
"method": row["method"],
|
|
"x": row[x_key],
|
|
"y": row[y_key],
|
|
"t4_deployable": row.get("t4_deployable"),
|
|
})
|
|
return points
|
|
|
|
|
|
def _scatter(points, x_label, y_label, title, out_path: Path) -> None:
|
|
fig, ax = plt.subplots(figsize=(7, 5))
|
|
for p in points:
|
|
marker = "o" if p["t4_deployable"] else "x"
|
|
ax.scatter(p["x"], p["y"], marker=marker, s=80)
|
|
ax.annotate(p["method"], (p["x"], p["y"]), textcoords="offset points", xytext=(5, 5))
|
|
ax.set_xlabel(x_label)
|
|
ax.set_ylabel(y_label)
|
|
ax.set_title(title + " (o = T4-deployable, x = A100-only)")
|
|
fig.tight_layout()
|
|
fig.savefig(out_path, dpi=120)
|
|
plt.close(fig)
|
|
|
|
|
|
def _bar(rows, metric_key, y_label, out_path: Path) -> None:
|
|
ok = [r for r in rows if r.get("status") == "ok" and r.get(metric_key) is not None]
|
|
fig, ax = plt.subplots(figsize=(7, 5))
|
|
ax.bar([r["method"] for r in ok], [r[metric_key] for r in ok])
|
|
ax.set_ylabel(y_label)
|
|
ax.set_title(metric_key)
|
|
fig.tight_layout()
|
|
fig.savefig(out_path, dpi=120)
|
|
plt.close(fig)
|
|
|
|
|
|
def render_all(rows: List[Dict[str, Any]], out_dir: Path) -> List[Path]:
|
|
out_dir = Path(out_dir)
|
|
out_dir.mkdir(parents=True, exist_ok=True)
|
|
written: List[Path] = []
|
|
|
|
p1 = out_dir / "pareto_latency_cer.png"
|
|
_scatter(pareto_points(rows, "mean_latency_s", "mean_cer"),
|
|
"mean latency (s) [lower=faster]", "CER vs BF16 [lower=better]",
|
|
"Speed vs recognition accuracy", p1)
|
|
written.append(p1)
|
|
|
|
p2 = out_dir / "pareto_latency_iou.png"
|
|
_scatter(pareto_points(rows, "mean_latency_s", "mean_bbox_iou"),
|
|
"mean latency (s) [lower=faster]", "bbox IoU vs BF16 [higher=better]",
|
|
"Speed vs detection accuracy", p2)
|
|
written.append(p2)
|
|
|
|
for metric, label in [("mean_latency_s", "mean latency (s)"),
|
|
("mean_cer", "CER vs BF16"),
|
|
("mean_bbox_iou", "bbox IoU vs BF16")]:
|
|
bp = out_dir / f"bar_{metric}.png"
|
|
_bar(rows, metric, label, bp)
|
|
written.append(bp)
|
|
|
|
return written
|