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)