DH研究事例集

深層学習に基づく日本の古文書くずし字認識とTEIエンコーディングによる翻刻支援システムの構築

Tags: 深層学習, 古文書, くずし字認識, TEI, デジタルヒューマニティーズ, OCR

導入

デジタルヒューマニティーズ(DH)分野において、歴史的文書のデジタル化と分析は中心的な課題の一つでございます。特に、日本の古文書に頻繁に見られる「くずし字」は、その多様な書体や崩し方、異体字の多さから、専門家による翻刻作業に多大な時間と労力を要し、これが学術研究の進展における障壁となっておりました。この課題に対し、近年発達した深層学習技術を応用することで、くずし字の自動認識と翻刻支援の可能性が模索されております。

本研究事例では、深層学習モデルを用いたくずし字認識の技術的アプローチと、認識結果をText Encoding Initiative (TEI) 形式で構造化し、学術的な再利用性を高めるためのシステム構築について詳細に解説いたします。このアプローチは、古文書研究の効率化に貢献するだけでなく、大規模なデジタルアーカイブ構築における基盤技術としての応用も期待されます。

研究の目的と仮説

本研究の主要な目的は、深層学習モデルを用いて日本のくずし字を高い精度で自動認識し、その認識結果を学術的な標準形式であるTEI/XMLで構造化して出力する翻刻支援システムを構築することです。これにより、以下の仮説を検証いたしました。

  1. 深層学習モデル、特に畳み込みニューラルネットワーク(CNN)とリカレントニューラルネットワーク(RNN)を組み合わせたアプローチは、日本のくずし字の多様な書体や文字パターンを効果的に学習し、実用レベルの認識精度を達成できる。
  2. 認識されたテキスト情報をTEI形式で構造化することで、単なるテキストデータではなく、原典の物理的特性(行、文字、書誌情報など)や翻刻過程における解釈(異体字、誤読可能性)を付与し、DH研究におけるデータの相互運用性と再利用性を飛躍的に向上させることが可能となる。
  3. 本システムは、研究者が手作業で行っていた翻刻作業の負担を大幅に軽減し、より深い文献学的分析や内容分析に注力できる環境を提供することで、古文書研究の効率化と新たな知見の発見を促進する。

研究方法論と技術的アプローチ

本研究では、以下の段階を経てシステムを構築いたしました。

1. データセットの準備と前処理

主要な学習データとして、国立国語研究所が公開する「くずし字データセット」や、東京大学史料編纂所、京都大学人文科学研究所などが公開するくずし字画像と翻刻テキストのペアを含むデータセットを収集いたしました。これらのデータセットは、多様な時代の書物や文書からなる約20万文字以上のくずし字画像と、それに対応する現代のひらがな・カタカナ・漢字のテキストを含んでおります。

データ前処理としては、以下の手順を採用いたしました。 * 画像正規化: 古文書の画像は、紙質の劣化、墨の濃淡、照明条件により多様なノイズを含むため、大津の二値化法(Otsu's method)による二値化処理や、非線形フィルタによるノイズ除去を実施いたしました。 * 行・文字領域のセグメンテーション: 連結成分解析(Connected Component Analysis)や、深層学習に基づくセグメンテーションモデル(例:U-Net)を用いて、画像から文字領域または行領域を正確に抽出いたしました。 * データ拡張: モデルの汎化性能を高めるため、回転、拡大縮小、せん断、明るさ調整、ガウシアンノイズ付加といったデータ拡張手法を適用し、データセットの規模を人工的に拡張いたしました。

2. 深層学習モデルの設計と学習

くずし字認識モデルには、画像中のシーケンスを認識するのに適したCNN-RNN-CTC(Convolutional Neural Network - Recurrent Neural Network - Connectionist Temporal Classification)アーキテクチャを採用いたしました。

モデルの学習にはPythonのPyTorchフレームワークを使用いたしました。GPUを用いた分散学習により、大規模なデータセットに対する学習時間を短縮し、モデルの最適化を進めました。

概念的なモデル構造の定義は以下のようになります。

import torch
import torch.nn as nn

class KuzushijiRecognitionModel(nn.Module):
    def __init__(self, num_classes):
        super(KuzushijiRecognitionModel, self).__init__()
        # CNN Feature Extractor (Example: Simplified ResNet-like block)
        self.cnn = nn.Sequential(
            nn.Conv2d(1, 64, kernel_size=3, stride=1, padding=1),
            nn.ReLU(True),
            nn.MaxPool2d(kernel_size=2, stride=2),
            # Add more CNN layers as needed
            nn.Conv2d(64, 128, kernel_size=3, stride=1, padding=1),
            nn.ReLU(True),
            nn.MaxPool2d(kernel_size=2, stride=2)
        )
        # Assuming input feature maps from CNN will be flattened into a sequence
        # Calculate appropriate input size for RNN based on CNN output
        # For simplicity, let's assume a fixed feature dimension after reshaping

        # Bi-directional LSTM for sequence modeling
        self.rnn = nn.LSTM(input_size=128 * 8 * 8, # Example, needs proper calculation
                           hidden_size=256,
                           num_layers=2,
                           bidirectional=True,
                           batch_first=True)

        # Output layer for CTC
        self.output = nn.Linear(256 * 2, num_classes) # 2 for bidirectional

    def forward(self, x):
        # x: (batch_size, 1, H, W) e.g., (B, 1, 64, 256)
        cnn_features = self.cnn(x) # (B, C_out, H', W')

        # Reshape CNN output for RNN: (batch_size, sequence_length, feature_dimension)
        # Here, W' becomes sequence_length, C_out * H' becomes feature_dimension
        batch_size, c, h, w = cnn_features.size()
        rnn_input = cnn_features.permute(0, 3, 1, 2).contiguous().view(batch_size, w, c * h)

        rnn_output, _ = self.rnn(rnn_input) # (B, SeqLen, HiddenSize*2)

        output = self.output(rnn_output) # (B, SeqLen, NumClasses)
        return output

上記のコードは、概念的なCNN-RNN-CTCモデルの骨格を示すものです。実際のモデル構築においては、入力画像のサイズに応じたCNN層の設計や、RNN層への適切な入力次元の計算が重要となります。

3. TEI/XMLエンコーディング

深層学習モデルによって認識されたくずし字のテキストは、そのままでは単なる文字列であり、原典の構造や翻刻過程における解釈の情報を持ちません。そこで、学術的なデータの交換と再利用を促進するため、Text Encoding Initiative (TEI) P5ガイドラインに準拠したXML形式で出力を行いました。

TEIエンコーディングの主な要素は以下の通りです。 * <facsimile>: 原画像のURIや物理的な領域(@lrx, @lry, @ulx, @uly)を記述し、テキストと画像をリンクさせます。 * <surface>: 原画像のページや面の情報を表します。 * <zone>: 画像内の特定の領域(例:行、欄)を定義します。 * <line>: 個々の行を表現し、その中に認識されたテキストを配置します。 * <lb/>: 原典の改行位置を示します。 * <choice>, <sic>, <corr>: 認識モデルの確信度が低い文字や、異体字、誤読の可能性がある文字に対して、元の文字(sic)と提案される翻刻(corr)を併記することで、研究者による検証や修正を支援します。 * <orig>: 原典に存在するが、現代語にはない文字や記号を表現します。

TEI/XMLの生成には、Pythonのlxmlライブラリを使用いたしました。認識結果のテキストだけでなく、認識モデルが算出した各文字の信頼度スコアや、文字座標情報もTEIの属性として付与することで、よりリッチなデータを提供いたしました。

TEI/XML出力の一部の概念を以下に示します。

from lxml import etree

def generate_tei_xml(recognition_results, image_uri, page_number):
    # Root element
    TEI = etree.Element("{http://www.tei-c.org/ns/1.0}TEI")
    teiHeader = etree.SubElement(TEI, "{http://www.tei-c.org/ns/1.0}teiHeader")
    fileDesc = etree.SubElement(teiHeader, "{http://www.tei-c.org/ns/1.0}fileDesc")
    titleStmt = etree.SubElement(fileDesc, "{http://www.tei-c.org/ns/1.0}titleStmt")
    title = etree.SubElement(titleStmt, "{http://www.tei-c.org/ns/1.0}title")
    title.text = f"Kuzushiji Transcription for Image {page_number}"

    # Text body
    text = etree.SubElement(TEI, "{http://www.tei-c.org/ns/1.0}text")
    body = etree.SubElement(text, "{http://www.tei-c.org/ns/1.0}body")
    div = etree.SubElement(body, "{http://www.tei-c.org/ns/1.0}div")

    # Facsimile information (Linking text to image regions)
    facsimile = etree.SubElement(TEI, "{http://www.tei-c.org/ns/1.0}facsimile")
    surface = etree.SubElement(facsimile, "{http://www.tei-c.org/ns/1.0}surface",
                                n=str(page_number),
                                lrs=image_uri) # lrs: Location of Rectangular Segment

    for line_idx, line_result in enumerate(recognition_results):
        # Assume line_result contains text, bounding box, confidence scores
        # Example: line_result = {"text": "吾輩は猫である", "bbox": [x1, y1, x2, y2]}

        # Create a line element for the text
        line_elem = etree.SubElement(div, "{http://www.tei-c.org/ns/1.0}lb",
                                      n=str(line_idx + 1)) # line break

        # Add the recognized text
        line_elem.tail = line_result["text"]

        # Example for handling uncertainty/choices (simplified)
        # if line_result["confidence"] < 0.8:
        #     choice = etree.SubElement(line_elem, "{http://www.tei-c.org/ns/1.0}choice")
        #     sic = etree.SubElement(choice, "{http://www.tei-c.org/ns/1.0}sic")
        #     sic.text = line_result["original_kuzushiji_character_approximation"]
        #     corr = etree.SubElement(choice, "{http://www.tei-c.org/ns/1.0}corr")
        #     corr.text = line_result["text"]

    return etree.tostring(TEI, pretty_print=True, encoding='UTF-8', xml_declaration=True)

# Example usage (simplified, assuming recognition_results is populated)
# recognition_results = [{"text": "吾輩は猫である。", "bbox": [10, 10, 100, 20]}]
# tei_xml = generate_tei_xml(recognition_results, "http://example.com/image_page_1.jpg", 1)
# print(tei_xml.decode('utf-8'))

このTEI生成のコードは、最小限の構造を示すものであり、実際の運用では、文字単位の座標情報、異体字のマークアップ、改行や句読点の扱いなど、TEIガイドラインのより詳細な要素を組み込むことになります。

研究成果と考察

本研究で構築したシステムは、以下の主要な成果を達成いたしました。

一方で、いくつかの限界も明らかになりました。例えば、非常に稀な崩し字や、墨の滲み、虫食いなどによる判読困難な文字、あるいは複数の文字が結合しているようなケースでは、認識精度が著しく低下することが確認されました。また、モデルが学習していない新たな書体や資料固有の表記揺れに対する適応能力には依然として課題が残ります。これらの課題は、さらなる学習データの拡充や、Few-shot Learning、Meta-learningといった先端的な機械学習技術の導入によって解決される可能性がございます。

結論と今後の展望

本研究は、深層学習が日本の古文書くずし字認識において極めて有効なツールであり、その結果をTEI形式で構造化することで、古文書研究の効率化と学術データの国際的な相互運用性を飛躍的に高めることを実証いたしました。これにより、これまで一部の専門家に限られていた古文書へのアクセスが広がり、新たな研究領域の開拓が期待されます。

今後の展望として、以下の点が挙げられます。

本研究が、デジタルヒューマニティーズ分野における革新的な研究の進展に貢献し、古文書が秘める豊かな知識の宝庫を、未来の研究世代へと繋ぐ一助となることを願っております。

参考文献・関連情報