論文一覧に戻る 📚 用語集トップ 🗺 概念マップ
📚 用語解説
📚 用語解説
文字認識
OCR / Character Recognition
認識技術
別称: OCR

🔖 キーワード索引

本ページで扱う主要概念へ素早く飛べるチップ群。 文字認識(OCR)は「画像 → 文字列」の橋渡しであり、 統計・データ解析の前処理段階で頻出する。

🎨 直感 📐 数式 🔬 数式を言葉で読み解く 🧮 実値で計算 🐍 Python 実装 ⚠️ 落とし穴 🌐 関連手法 📍 文脈 📚 関連教材 画像認識 CNN 深層学習

💡 30秒で分かる結論

🍰 まずはやさしく

画像から文字を読み取る技術です。

画像をテキストデータに変えるために使います。

看板の文字をスマホで読み取る時に役立ちます。

この章では文字認識の種類や道具を学びます。

画像から文字を読み取る技術

📍 あなたが今見ているもの

🍰 まずはやさしく

画像認識という技術の一種です。

紙の情報をデジタルにするために使います。

紙のアンケートをデータ化する時に便利です。

この章では文字認識がAIのどこに位置するかを学びます。

PDF や紙の帳票から数値を抽出するとき、 必ず通るステップ。 RPA(業務自動化)の中核技術でもあります。

📍 文脈ボックス

あなたが今見ているのは 「文字認識(Character Recognition / OCR)」 のページです。 これは 画像認識 の特殊ケースであり、 入力は 画素値の 2 次元配列、 出力は テキスト文字列。 上位概念は AI機械学習深層学習CNN、 並列概念は 音声認識物体検出。 統計・データ解析コンペで扱う場面としては 「紙のアンケート用紙をデジタル化する」「自治体公開 PDF から表データを抽出する」 など、 入力データ取得段階の前処理として現れる。

🎨 直感で掴む

🍰 まずはやさしく

写真から文字を書き出す魔法のような技術です。

手入力の手間をなくすために使います。

レシート読み取りアプリなどが身近な例です。

この章では文字を読み取る仕組みを学びます。

OCR の処理は3段階:

  1. 前処理:傾き補正、 二値化、 ノイズ除去
  2. 文字検出:画像のどこに文字があるか
  3. 文字認識:その文字は何か(深層学習で分類)

印刷文字(フォントが整っている)は99%以上の精度。 手書き・スキャン崩れ・古文書になると一気に難しくなります。

文字認識 (OCR) の身近な例として、 レシート読み取りアプリを考えると分かりやすい。 スマホでレシートを撮影すると「店舗名: ローソン / 合計: 1,280 円」と自動でテキスト化される。 内部では レシート画像 (2,000×800 px) → 二値化 → 文字領域検出 (CTPN/EAST) → 文字列認識 (CRNN+CTC) → 後処理 (辞書補正) の 4 段が走る。

もう 1 つの例は SSDSE-B-2026 の紙台帳デジタル化。 仮に総務省「社会・人口統計体系」の昭和 50 年代の冊子 (印刷数値表) が画像 PDF で提供された場合、 OCR で 47 都道府県 × 100 列の数値表をテキスト化する。 ここでは「I (アイ) と 1 (イチ)」「O (オー) と 0 (ゼロ)」「カタカナ ロ と 漢字 口」の混同が頻発し、 認識精度 99% でも 4,700 セル中 47 セル誤読の計算になる。 だから OCR 出力は必ず原本と数値比較する習慣が必要だ。

本ページでは、 次節以降で CTC 損失の数式 (📐) → 5 文字列で値代入 (🧮) → CRNN を Python で動かす (🐍) の流れで進める。 「印刷文字なら 99% 精度」を「では SSDSE 冊子の何セルを人手検証すべきか」という実務判断に落とし込めるようにする。

🎮 触って理解する — 生画素ではなく「特徴」で文字を見分ける

最近傍法は画像を「画素の並び」とみなして距離を測りますが(画像分類のウィジェット参照)、 少しずれるだけで崩れます。 OCR で使う古典的な発想はその逆で、 まず画像から「形の特徴」を抜き出し、 その特徴どうしで比べます。 下の 7×7 マスに数字を描くと、 生画素ではなく 各行・各列の黒画素数(射影ヒストグラム)/穴の数/縦横比/黒画素率 といった特徴ベクトルに変換し、 4 つのテンプレート特徴との距離で認識します。 同時に生画素距離も並べ、 「特徴のほうが頑健」なことを体感しましょう。

※ この 7×7 画像とテンプレート(1・0・7・8)は説明用の架空データです(実在の文字画像データセットではありません)。 特徴量・距離はすべてブラウザ内でリアルタイムに厳密計算しています(Node で検証済み)。

✏️ 7×7 に数字を描く(タップ / ドラッグ)
📐 お手本テンプレート(クリックでコピー)
🔧 前処理後(二値化→傾き補正→正規化)
位置を中央にそろえてから特徴を測る
📊 各テンプレートとの距離(短いほど近い)
緑=特徴ベクトル距離(正規化後)で認識/ 灰=生画素距離(正規化なし・参考)
🔎 試してほしい 3 つの実験
  1. 穴の数で見分ける:「0」を描くと穴 1 個、 「8」なら穴 2 個、 「1」「7」は穴 0 個。 生画素では似た画数の 0 と 8 も、 「穴の数」という特徴なら一発で分かれます。
  2. ずらしても平気(特徴の勝ち):数字を描いて「➡️ 1マスずらす」。 特徴距離はほぼ 0 のまま正解を維持しますが、 生画素距離(灰)は跳ね上がり順位が入れ替わります。 特徴抽出+正規化が平行移動に強い決定的な理由。
  3. 傾けると穴がつぶれる → 前処理で復活:「0」「8」を描いて「↘ 傾ける」。 粗い 7×7 では輪が切れて穴が消え誤認識します。 そこで「🔧 傾き補正」にチェックを入れると、 前処理で傾きを直してから特徴を測るので正しく認識に戻ります

💡 直感:文字は「形の特徴」で見分けられる

人が「0」と「8」を区別するとき、 全画素を照合しているわけではありません。 閉じた輪がいくつあるか(穴の数)縦長か横長か(縦横比)墨がどの行・列に集中しているか(射影ヒストグラム)——そういう少数の特徴だけで足ります。 特徴空間では「0」と「8」は穴の数の軸で大きく離れ、 k近傍法のような単純な距離分類でも安定します。 生画素の 49 次元より、 意味のある十数次元の特徴で比べるほうが、 少ないお手本で頑健に効くのが古典 OCR の知恵です。

⚠️ 落とし穴:特徴も万能ではない

🚀 発展:手作り特徴から「学習する特徴」へ

「どんな特徴を使うか」を人間が設計するのが古典 OCR。 CNN(畳み込みニューラルネット)は、 射影や穴の数に相当する特徴をデータから自動で学習し、 多様な字形にも適応します。 さらに 1 文字ずつ切り出せない連続文字列は、 RNNCTC 損失(本ページ 📐 節)で「系列」として一気に認識。 看板や写真中の文字を扱うシーンテキスト認識では、 まず物体検出で文字領域を見つけてから認識します。 手作り特徴 → 学習特徴 → 系列 → シーン、 と発展の筋道を押さえておきましょう。

📐 定義/数式

🍰 まずはやさしく

画像から文字を読み取る技術のことです。

正しく文字を判定するために使います。

似た形の文字を見分ける時に重要になります。

この章では文字認識の定義や数式を学びます。

文字認識OCR / Character Recognition):画像から文字を読み取る技術

同義・関連語:OCR

【CTC損失(OCRの定番学習目的関数)】
$$ \mathcal{L}_{\text{CTC}} = -\log \sum_{\pi \in \mathcal{B}^{-1}(\mathbf{y})} P(\pi | \mathbf{x}) $$
可能な全ての文字列パス $\pi$ を周辺化して、 正解列 $\mathbf{y}$ の確率を最大化。

🔬 記号・用語の読み解き

記号意味
$\mathbf{x}$入力画像(または特徴系列)
$\mathbf{y}$正解文字列
CTCConnectionist Temporal Classification
BBox文字の位置を示す矩形

🔬 数式を言葉で読み解く

文字認識の心臓部は CNN による特徴抽出 + ソフトマックスによるクラス確率化である。 数式記号は冷たく見えるが、 言葉に置き換えると「画像のパターンを発見 → 各文字らしさを点数化 → 最大点数の文字を選ぶ」という素朴な流れに過ぎない。

畳み込み演算 (Convolution)

$$ (I * K)(i,j) = \sum_{m}\sum_{n} I(i+m, j+n) \cdot K(m,n) $$

ソフトマックスによる確率化

$$ p(y=c \mid x) = \frac{\exp(z_c)}{\sum_{k=1}^{K} \exp(z_k)} $$

この 2 式が組み合わさることで「画素 → 特徴 → スコア → 確率 → 最大確率の文字」という認識パイプラインが完成する。

🧮 実値で計算してみる

SSDSE-B-2026 には 47 都道府県のデータが含まれる。 ここでは 「都道府県名を OCR で読み取った場合の文字数分布」 を実値で集計してみる。 OCR の認識難易度はおおむね 文字数に比例 するため、 この分布を知ることは前処理時間の見積もりに直結する。

47 都道府県名の文字数分布

文字数件数代表例
3 文字43北海道、 青森県、 岩手県、 大阪府、 京都府、 …
4 文字3神奈川県、 和歌山県、 鹿児島県
3 文字 (都)1東京都
合計47

結果: 3 文字 = 44 件 (94%)、 4 文字 = 3 件 (6%)。 OCR の処理時間を 1 文字あたり 5ms と仮定すると、 全 47 件の平均処理時間は約 15ms × 47 ≈ 720 ms 程度と見積もれる。

混同しやすい文字ペア (実例)

🧮 数式に値を入れて手で計算する: 文字認識精度 (CER)

合成データで Character Error Rate (CER) を編集距離から計算する。

Step 1: 正解と認識結果

正解認識編集距離CER
HELLO (5)HFLLO10.20
WORLD (5)WROLD20.40
PYTHON (6)PYTH0N10.167

Step 2: 平均 CER

合計編集距離 = 1+2+1 = 4 合計文字数 = 5+5+6 = 16 全体 CER = 4/16 = 0.250 = 25%

🐍 Python で再現

1
2
3
4
5
6
edits = [1, 2, 1]
lengths = [5, 5, 6]
cer = sum(edits) / sum(lengths)
print(f"合計編集距離: {sum(edits)}")
print(f"合計文字数: {sum(lengths)}")
print(f"CER = {cer:.3f}")

📤 実行結果

合計編集距離: 4 合計文字数: 16 CER = 0.250

💬 手計算 (Step 2) 0.250 と Python 出力が完全一致。

🐍 Python での実装例

SSDSE-B-2026 などの実データを使った最小コード(9行):

1
2
3
4
5
6
7
8
9
# Tesseract を使う例(要 pip install pytesseract、 tesseract本体)
import pytesseract
from PIL import Image
text = pytesseract.image_to_string('document.png', lang='jpn')
print(text[:200])
# データ抽出例:表形式の値を取り出して DataFrame に
import re
numbers = re.findall(r'\d{1,3}(?:,\d{3})*', text)
print('検出数値:', numbers[:10])

data/raw/SSDSE-B-2026.csve-Stat SSDSE から取得した実データを想定。

🐍 Python 実装

文字認識(OCR)は実務では Tesseract(pytesseract ラッパー)または深層学習モデル(EasyOCR, PaddleOCR)を使う。 ここでは SSDSE-B-2026 の都道府県名データを題材に、 OCR 前後の処理を段階的に示す。

コード 1: 都道府県名の文字数分布を集計

🎯 このコードでやること: SSDSE-B-2026 から都道府県名を読み込み、 文字数の分布を集計する。 OCR の処理時間見積もりや認識難易度のラフな指標になる。

📥 入力例 (SSDSE-B-2026 抜粋):

SSDSE-2026 都道府県 ... R01000 北海道 ... R02000 青森県 ... R13000 東京都 ... R14000 神奈川県 ... R47000 沖縄県 ...
1
2
3
4
5
6
7
import pandas as pd

df = pd.read_csv('data/raw/SSDSE-B-2026.csv', encoding='cp932', skiprows=1)
prefs = df['都道府県'].unique()
lens = pd.Series([len(p) for p in prefs])
print('総数:', len(prefs))
print(lens.value_counts().sort_index())

📤 実行例:

総数: 47 3 44 4 3 dtype: int64

💬 結果の読み方: 3 文字が 44 件で大多数、 4 文字 (神奈川県・和歌山県・鹿児島県) が 3 件。 OCR の負荷は 3-4 文字 × 47 行 ≈ 145 文字程度で済む。

コード 2: pytesseract で画像から文字を読む(疑似コード)

🎯 このコードでやること: pytesseract に PIL 画像を渡し、 日本語モード(jpn)で文字列を抽出する。 都道府県名を含む画像であれば「北海道」「東京都」などの文字列が返る。

📥 入力例 (画像):

prefecture_label.png (200×60 px、 黒地白文字で「北海道」と書かれた画像)
1
2
3
4
5
6
import pytesseract
from PIL import Image

img = Image.open('data/raw/prefecture_label.png')
text = pytesseract.image_to_string(img, lang='jpn')
print('認識結果:', text.strip())

📤 実行例:

認識結果: 北海道

💬 結果の読み方: 印刷文字なら 1 行で「北海道」と正しく取れる。 ただし手書きやノイズが多い画像では「北海進」のように 1 文字単位で誤る場合がある(後段の辞書照合が必要)。

コード 3: 認識結果と SSDSE 都道府県マスタを突合

🎯 このコードでやること: OCR 結果が SSDSE-B-2026 の正規都道府県名リストにあるか確認し、 ない場合は最も類似する名前を提示する(誤認識補正)。

📥 入力例 (OCR 結果と都道府県リスト):

ocr_text = '北海進' # OCR が間違えた結果 prefs = ['北海道', '青森県', ..., '沖縄県'] # 47 件のマスタ
1
2
3
4
5
6
7
8
9
import pandas as pd
import difflib

df = pd.read_csv('data/raw/SSDSE-B-2026.csv', encoding='cp932', skiprows=1)
prefs = df['都道府県'].unique().tolist()

ocr_text = '北海進'
match = difflib.get_close_matches(ocr_text, prefs, n=1, cutoff=0.5)
print('正規化結果:', match[0] if match else '(該当なし)')

📤 実行例:

正規化結果: 北海道

💬 結果の読み方: 「北海進」(OCR 誤認識) → 「北海道」(マスタ照合で訂正)。 OCR と辞書照合を組み合わせると、 単純 OCR の精度 80% が 95% 以上に跳ね上がる。

コード 4: 簡易 CNN モデル定義(学習なしの骨格)

🎯 このコードでやること: scikit-learn の多クラスロジスティック回帰で「文字画像 → ラベル」のミニ OCR を作る骨格を示す。 実データの代わりに sklearn 同梱の load_digits (0-9 の 8×8 数字画像) を用いる。

📥 入力例 (sklearn load_digits):

画像数: 1797 画像サイズ: 8×8 = 64 画素 クラス数: 10 (0〜9)
1
2
3
4
5
6
7
8
from sklearn.datasets import load_digits
from sklearn.linear_model import LogisticRegression
from sklearn.model_selection import train_test_split

X, y = load_digits(return_X_y=True)
X_tr, X_te, y_tr, y_te = train_test_split(X, y, test_size=0.3, random_state=0)
clf = LogisticRegression(max_iter=2000).fit(X_tr, y_tr)
print('テスト精度:', round(clf.score(X_te, y_te), 4))

📤 実行例:

テスト精度: 0.9685

💬 結果の読み方: 数字 10 クラス分類で 96.85% の精度。 CNN を使えば 99% 以上、 ひらがな・漢字に拡張すると数千クラスとなり CNN/Transformer が必須となる。

🐍 追加 Python サンプル

CER (Character Error Rate) を実装する

🎯 このコードでやること: OCR の出力と正解文字列から、 編集距離 (Levenshtein) と CER を計算する。 評価指標として広く使われる。

📥 入力例:

予測: '北海進' 正解: '北海道'
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
def levenshtein(a, b):
    m, n = len(a), len(b)
    dp = [[0]*(n+1) for _ in range(m+1)]
    for i in range(m+1): dp[i][0] = i
    for j in range(n+1): dp[0][j] = j
    for i in range(1, m+1):
        for j in range(1, n+1):
            cost = 0 if a[i-1] == b[j-1] else 1
            dp[i][j] = min(dp[i-1][j]+1, dp[i][j-1]+1, dp[i-1][j-1]+cost)
    return dp[m][n]

pred, gold = '北海進', '北海道'
d = levenshtein(pred, gold)
cer = d / max(len(gold), 1)
print(f'編集距離={d}, CER={cer:.3f}')

📤 実行例:

編集距離=1, CER=0.333

💬 結果の読み方: 3 文字中 1 文字を間違えたので CER = 0.333。 商用 OCR の目標は CER < 0.01 (99% 以上正答)。

SSDSE-B-2026 都道府県名から疑似 OCR テスト

🎯 このコードでやること: 47 都道府県名にランダム誤りを 1 文字注入し、 編集距離ベースのマスタ照合で復元できるか確かめる。 OCR 後処理の有効性検証ミニ実験。

📥 入力例:

SSDSE-B-2026.csv の都道府県列 (47 件)
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
import pandas as pd, difflib

df = pd.read_csv('data/raw/SSDSE-B-2026.csv', encoding='cp932', skiprows=1)
prefs = df['都道府県'].unique().tolist()

# 各都道府県名の 2 文字目を「々」に置換した疑似 OCR 誤り
noisy = [p[0] + '々' + p[2:] for p in prefs]
hits = sum(1 for n, p in zip(noisy, prefs)
           if (m := difflib.get_close_matches(n, prefs, n=1, cutoff=0.3)) and m[0] == p)
print(f'復元成功率: {hits}/{len(prefs)} = {hits/len(prefs):.2%}')

📤 実行例:

復元成功率: 37/47 = 78.72%

💬 結果の読み方: 1 文字誤りでもマスタ辞書照合で 79% を自動復元できる。 失敗ケースは「東々都」のように共通文字が少ないものが多い。

混同行列で誤読パターンを可視化

🎯 このコードでやること: 文字単位の混同行列を作り、 どの文字がどの文字に間違われやすいかをヒートマップにする。

📥 入力例:

y_true = ['0','1','2','3','0','1','2','3'] y_pred = ['0','1','2','3','6','1','7','3']
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
from sklearn.metrics import confusion_matrix
import numpy as np

y_true = ['0','1','2','3','0','1','2','3']
y_pred = ['0','1','2','3','6','1','7','3']
labels = sorted(set(y_true) | set(y_pred))
cm = confusion_matrix(y_true, y_pred, labels=labels)
print('ラベル:', labels)
print('混同行列:')
print(cm)

📤 実行例:

ラベル: ['0', '1', '2', '3', '6', '7'] 混同行列: [[1 0 0 0 1 0] [0 2 0 0 0 0] [0 0 1 0 0 1] [0 0 0 2 0 0] [0 0 0 0 0 0] [0 0 0 0 0 0]]

💬 結果の読み方: 「0 → 6」「2 → 7」のような形状類似誤読が見える。 こうした誤読パターンを発見できれば、 訓練データの追加収集や前処理改善の指針になる。

❓ よくある質問 (FAQ)

⚠️ よくある落とし穴

❌ 低解像度/傾き
200dpi 以下は精度低下。 前処理で補正。
❌ レイアウト破壊
段組や表で順序が崩れる。 LayoutLM 系で対処。
❌ 似た文字
「O と 0」「l と 1」「カ と 力」 — 文脈で判別必要。
❌ 言語混在
日本語と英語混在は lang 指定で 'jpn+eng'

⚠️ 落とし穴

🗺 学習ロードマップ

  1. (1 週目) Tesseract を動かす — pytesseract で印刷文字を読み、 出力を眺める。 SSDSE-B-2026 の都道府県マスタと突合してエラーパターンを観察する。
  2. (2 週目) MNIST で CNN を学ぶ — PyTorch / Keras で LeNet 相当を学習し、 精度 99% を体感する。
  3. (3 週目) 手書き日本語へ拡張 — ETL データベースを使い、 ひらがな 46 クラスを CNN で分類。
  4. (4 週目) 文字列認識 (CRNN) — SynthText で事前学習、 自前の日本語データで fine-tune。
  5. (5 週目) 帳票 OCR の総合演習 — レイアウト解析 + OCR + 名寄せ + 構造化を一通り組む。 SSDSE-B-2026 のような統計データへの統合まで体験する。

🛤 エンドツーエンド・パイプライン例

統計・データ解析の文脈で OCR を実運用に乗せる典型的な 6 段パイプラインを示す。 各段で何が起きるか、 失敗時にどこに戻るかを把握しておくと事故が減る。

段階処理主なライブラリ失敗時の対処
1. 入力PDF / 画像 / スキャンpdf2image, pillow解像度不足 → 300dpi 以上で再取得
2. 前処理2 値化・傾き補正・ノイズ除去opencv-pythonパラメータ調整 / 適応的 2 値化に切替
3. レイアウト解析表・段・領域の分割layoutparser, PaddleOCR人手アノテーションで段を指定
4. OCR文字認識本体pytesseract, easyocr複数モデル投票で精度向上
5. 後処理誤認識補正・名寄せdifflib, rapidfuzzマスタ辞書を整備、 cutoff を緩める
6. 構造化DataFrame 化 → DB / CSVpandas, sqlalchemyスキーマ違反は元データに戻し確認

SSDSE-B-2026 級の最終データを得るためには、 1-6 すべてを通したうえで「サンプリングして人手チェック → 修正フィードバック」のループを 2-3 回回すのが現実的。

💰 コスト・性能トレードオフ

OCR には無料 OSS から商用クラウドまで選択肢が多い。 統計コンペや研究での選定基準を示す。

ソリューションコスト精度 (日本語印刷)向き不向き
Tesseract 5無料 (OSS)CER 3-5%学習・小規模試作
EasyOCR無料 (OSS)CER 2-4%多言語混在、 シーン文字
PaddleOCR無料 (OSS)CER 1-3%中国語・日本語が強い
Google Cloud Vision1.5 ドル / 1000 画像CER 0.5-1%本番運用、 多言語
Amazon Textract1.5 ドル / 1000 ページCER 0.5-1%帳票・表抽出に強み
Azure Form Recognizer1.5 ドル / 1000 ページCER 0.5-1%日本語帳票テンプレ多数

実務での目安: 「ページ数 < 1000」なら OSS で十分、 「精度 99% が必要」なら商用クラウド、 「機密データ社外送信不可」なら OSS をオンプレで運用。

⚖️ 倫理・バイアスの観点

文字認識は一見中立な技術に見えるが、 学習データの偏りが社会的影響を持つことがある。 統計データ活用の前提として認識しておくべき点を列挙する。

character recognition 画像前処理 画像分類 CNN 自然言語処理 CAR (Character CER (Character WER (Word Erro

🔗 隣接手法への橋渡し

「文字認識 (OCR)」は画像 → 文字列の変換を担う中継工程で、 上流の画像前処理と下流の自然言語処理を橋渡しする位置にある。 単独で精度を語ると下流の解析タスクで破綻する。

上流の二値化・傾き補正で精度を底上げし、 並列の画像分類モデルと比較し、 下流の自然言語処理に渡すパイプラインを組めば、 帳票・申請書・手書きアンケートを SSDSE 型データに変換するワークフローを完結できる。

🌳 手法選択フロー

「文字認識(OCR)」を実際に使うとき、 何をどう選ぶかを順に判断する。 上から順に答えていくと、 使うべき手法と評価の仕方が決まる。

  1. 読み取る文字は活字か、 手書きか
    活字なら既製の OCR エンジンで実用水準に届く。 手書き、 特に日本語の崩し字は誤りが増えるので、 認識結果を人が確認する工程を最初から設計に入れる。
  2. レイアウトは決まっているか
    帳票のように位置が固定なら、 領域を切り出してから読むほうが精度が上がる。 自由なレイアウトなら、 まず版面解析(どこが表でどこが本文か)が要る。
  3. 前処理でどこまで直せるか
    傾き補正・二値化・ノイズ除去だけで誤り率が大きく下がることが多い。 モデルを替える前に、 入力画像の質を上げる余地を先に潰す。
  4. 誤りをどう扱うか
    数字 1 文字の誤りが金額を 10 倍にする用途では、 確信度の低い箇所を人に回す仕組みが必須。 チェックディジットや合計値との突き合わせで自動検出できる場合もある。

OCR は「読めた文字列」を返すだけで、 正しさは保証しない。 後段で必ず検証できる形(合計との照合、 マスタとの突き合わせ)にしておく。

🔎 解説を一段深く — OCR 出力は「統計的に検算」できる

本ページの各節では「誤読は人手検証で拾う」と述べてきた。 しかし全セルを人手で見るのは非現実的だ。 この深化セクションでは視点を変え、 OCR が読み取った「数値データそのものが持つ統計的性質」を使って、 誤読を機械的にあぶり出す方法を、 SSDSE-B-2026 の 2023 年・47 都道府県データの実測値で確かめる。 画像側の工夫(前処理・モデル選択)とは独立に効く、 いわば出口側の品質保証である。

💡 直感 — 正しい統計表は「検算式」を内蔵している

統計表は無関係な数値の羅列ではなく、 列どうしが恒等式で結ばれている。 SSDSE-B-2026 なら「総人口(男)+総人口(女)=総人口」「15歳未満+15〜64歳+65歳以上=総人口」が成り立つはずだ。 OCR が 1 桁でも誤読すればこの等式が崩れるため、 足し算するだけで誤読セルの行を特定できる。 さらに規模感も効く。 実測では 2023 年の総人口(A1101 列)は 東京都 14,086,000 人が最大、 鳥取県 537,000 人が最小、 47 都道府県の合計は 124,353,000 人。 かりに OCR が東京都の先頭桁「1」を「7」と誤読すると 74,086,000 人となり、 1 都道府県だけで全国合計の半分を超える異常値として即座に検出できる。 桁数だけ見ても、 総人口が 8 桁になるのは東京都ただ 1 県(7 桁 36 県・6 桁 10 県)なので、 「8 桁が 2 行ある」時点で誤読確定である。

⚠️ 落とし穴(重要) — 検算式は「丸め誤差」の分だけ緩めないと偽陽性の山になる

ここが最大の落とし穴。 「男+女=総人口」を完全一致で判定すると、 SSDSE-B-2026 の 2023 年実データでは一致するのは 47 県中 31 県だけで、 残り 16 県が「誤読」と誤判定される(差 +1,000 が 8 県、 −1,000 が 8 県)。 たとえば北海道は 男 2,405,000 + 女 2,688,000 = 5,093,000 に対し総人口は 5,092,000 で、 1,000 人ずれる。 これは誤読ではなく、 各列が千人単位で独立に四捨五入されているためだ。 年齢三区分の和も同様で、 完全一致は 34 県、 残り 13 県は ±1,000 の差が出る。 許容誤差を ±1,000(丸め単位 1 個分)に設定すれば 47 県すべてが検算を通過し、 それを超える差だけが真の誤読候補になる。 教訓:検算ルールは「データの丸め仕様」を読んでから設計すること。 完全一致主義は偽陽性を量産し、 逆に人手検証への信頼を壊す。 同じ理由で、 構成比(%)列の「合計が 100 にならない」も多くは丸めが原因である。

🚀 発展 — 先頭桁の分布(ベンフォードの法則)で「表全体」の健全性を診る

検算式が使えない列でも、 先頭桁の出現分布という集団レベルの性質が使える。 桁数が幅広く散らばる実世界の数値は、 先頭桁 1 が約 30% を占める「ベンフォードの法則」($P(d)=\log_{10}(1+1/d)$)に近づくことが多い。 SSDSE-B-2026 の 2023 年・47 都道府県の全正値セル 5,092 個で先頭桁を実測すると次の通り(カッコ内はベンフォード理論値)。

先頭桁123456789
実測 (%)31.317.212.29.27.56.25.55.45.5
理論 (%)30.117.612.59.77.96.75.85.14.6

実データが理論値にきわめて近いことが分かる(最大乖離は桁 1 の +1.2 ポイント)。 ここで OCR が「1 と 7」「0 と 6」を系統的に混同すると、 先頭桁 1 の山が削れて 7 が不自然に膨らむ——つまり個々のセルではなく分布の形の歪みとして、 系統誤読を一括検出できる。 本ページ 🧮 節の CER が「個票単位の誤り率」だとすれば、 これは「表単位の健康診断」であり、 両者は補完関係にある。 なお 1 セルだけの単発誤読は分布には現れないので、 上の検算式・桁数チェックと併用するのが正しい運用だ。 この発想は OCR に限らず、 会計データの不正検知や調査票の改ざん検出にも同じ形で使われている。

※ 本節の数値はすべて data/raw/SSDSE-B-2026.csv(2023 年・47 都道府県、 総人口系列は千人単位丸め)から Python (pandas) で実際に集計した実測値。 「東京都の 1→7 誤読」は検出例を示すための架空のシナリオである。