File size: 6,070 Bytes
bef9738 12ea146 a0f93ae 12ea146 a0f93ae 12ea146 bef9738 84005d4 bef9738 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 | ---
language: ja
license: cc-by-sa-4.0
pipeline_tag: image-classification
tags:
- kuzushiji
- japanese-historical
- image-classification
- convnext
- onnx
---
<p align="center">
<img src="https://yuta1984.github.io/soramaru_kuzushiji_ai/soramaru/12_smile.png" alt="そらまる" style="height: 200px">
</p>
# そらまる くずし字認識モデル (convnext_v4)
ブラウザ完結のくずし字(一文字)認識を目的とした ConvNeXt-tiny ベースの ONNX モデル。
[みんなで翻刻](https://app.honkoku.org) のマスコット「そらまる」がアシスタント役を務めるデモサイトで使用されています。
- 🎮 **デモ / フロント実装**: https://github.com/yuta1984/soramaru_kuzushiji_ai
- 🔗 **モデル本体**: [`convnext_v4.onnx`](./resolve/main/convnext_v4.onnx) (約 117 MB)
## モデル概要
| 項目 | 値 |
|---|---|
| アーキテクチャ | ConvNeXt-tiny (`timm/convnext_tiny.fb_in22k_ft_in1k` を fine-tune) |
| 入力 | 384 × 384 RGB |
| 出力 | 3,673 クラスへの logits |
| 形式 | ONNX (opset 17) |
| ランタイム | ONNX Runtime Web / Python onnxruntime |
## 学習データ
- [Kaggle Kuzushiji Recognition](https://www.kaggle.com/competitions/kuzushiji-recognition) コンペティションデータセット
- [東京大学史料編纂所くずし字データセット](https://lab.hi.u-tokyo.ac.jp/datasets/kuzushiji)
ページ画像から各 bounding box を切り出し、一文字単位の分類タスクとして再構成しています。
| 区分 | 件数 |
|---|---|
| 学習サンプル | 887,133 |
| 検証サンプル | 68,481 |
| 出力クラス数 | 3,673 |
## 学習設定
- **事前学習**: ImageNet-22k → ImageNet-1k (timm 提供のチェックポイント)
- **Fine-tune**: 2 段階(低解像度で大まかに学習 → 384px で仕上げ)
- **損失**: Cross-Entropy
- **サンプラー**: `WeightedRandomSampler` で各クラスの 1 epoch あたりの期待出現回数を平準化
- **クラス除外**: 出現件数が 3 件未満のクラスは学習対象から除外(cutoff=3)
## 性能評価
検証セット **val_kr**(Kaggle Kuzushiji Recognition の hold-out 68,481 件、3,673 クラス出力空間で argmax):
| 指標 | top-1 | top-5 | top-20 |
|---|---|---|---|
| micro 平均 | **96.5%** | 99.5% | 99.6% |
| macro 平均(val に出現する 1,268 クラス) | 96.9% | 99.4% | 99.5% |
`val_kr + val_extra` を併せた **3,673 全クラスでの評価**(極稀少クラスを含む厳しめの条件):
| 指標 | top-1 | top-5 | top-20 |
|---|---|---|---|
| micro | 95.3% | 99.0% | 99.4% |
| macro(出現 3,673 クラス) | 71.9% | 91.3% | 95.7% |
### 訓練サンプル数別の per-class recall(macro 平均)
| 訓練サンプル数 | クラス数 | 検証件数 | top-1 | top-5 | top-20 |
|---|---|---|---|---|---|
| 3 〜 10 | 1,690 | 1,690 | 52.5% | 85.2% | 93.5% |
| 10 〜 30 | 431 | 431 | 66.6% | 90.0% | 93.7% |
| 30 〜 100 | 622 | 2,168 | 92.5% | 97.9% | 98.4% |
| 100 〜 500 | 632 | 7,890 | 96.1% | 98.4% | 98.6% |
| 500 〜 2,000 | 213 | 11,096 | 95.7% | 98.9% | 99.1% |
| 2,000 以上 | 85 | 47,611 | 95.2% | 99.3% | 99.5% |
頻度の高い文字では top-1 が 95% 以上に達します。出現 10 件未満の極稀少クラスでは top-1 を外しがちですが、top-20 候補に含まれる確率は 93% あり、候補列挙ベースの翻刻支援用途では有効です。
## 前処理
```
中央正方形クロップ → 384×384 リサイズ → ImageNet 標準で正規化
mean = [0.485, 0.456, 0.406]
std = [0.229, 0.224, 0.225]
```
入力テンソル: `float32 [1, 3, 384, 384]` (NCHW)
## 出力
shape `[1, 3673]` の logits。softmax + argmax で top-1 クラスを取得し、`convnext_v4.meta.json` の `classes` 配列で対応する Unicode コード(例: `U+3042`)に変換します。
## 使い方
### ブラウザ (ONNX Runtime Web)
```javascript
import * as ort from "onnxruntime-web";
const url = "https://huggingface.co/yuta1984/soramaru_kuzushiji_ai/resolve/main/convnext_v4.onnx";
const session = await ort.InferenceSession.create(url, { executionProviders: ["wasm"] });
// preprocess: 中央正方形クロップ → 384×384 → Float32Array [1,3,384,384]
const tensor = new ort.Tensor("float32", buf, [1, 3, 384, 384]);
const out = await session.run({ [session.inputNames[0]]: tensor });
const logits = out[session.outputNames[0]].data;
```
完全な実装例は [`app.js`](https://github.com/yuta1984/soramaru_kuzushiji_ai/blob/main/app.js) を参照。
### Python (onnxruntime)
```python
import numpy as np, onnxruntime as ort
from PIL import Image
sess = ort.InferenceSession("convnext_v4.onnx", providers=["CPUExecutionProvider"])
img = Image.open("kuzushiji.png").convert("RGB")
w, h = img.size
s = min(w, h)
img = img.crop(((w-s)//2, (h-s)//2, (w+s)//2, (h+s)//2)).resize((384, 384), Image.BICUBIC)
x = (np.array(img, dtype=np.float32) / 255.0 - [0.485, 0.456, 0.406]) / [0.229, 0.224, 0.225]
x = x.transpose(2, 0, 1)[None].astype(np.float32)
logits = sess.run(None, {sess.get_inputs()[0].name: x})[0][0]
top5 = logits.argsort()[::-1][:5]
# → meta.json["classes"][k] で Unicode コードに変換
```
## 制限
- **1 文字単位の分類のみ**。ページ全体の検出・OCR ではありません(事前に文字単位で切り出す必要があります)
- 学習データに含まれない字体・崩し方には弱く、信頼度が下がります
- top-1 確率が低い場合は誤認識の可能性が高いため、上位複数候補の参照を推奨
## ライセンス
学習元データの再配布条件に倣い、本モデルは **CC BY-SA 4.0** で公開しています。派生物を公開する際は本モデルおよび以下のデータセットを引用してください:
- Kaggle Kuzushiji Recognition (2019)
- 東京大学史料編纂所くずし字データセット
## クレジット
- マスコット「そらまる」: [みんなで翻刻](https://app.honkoku.org)
|