Azure Cognitive Search の OCR を日本語環境で使えるようにする Tips

# はじめに
組織内に貯まっている大量な構造化・非構造化データから、価値を見出すためのフルマネージド全文検索サービスである [Azure Cognitive Search](https://www.youtube.com/watch?v=9GcjFofXdd0) で日本語 OCR スキルを十分使えるようにするための Tips を紹介します。


# 目次
1. [スキルについて](#スキルについて)
1. [OCR スキルの問題点](#ocr-スキルの問題点)
1. [Tips の紹介](#tips-の紹介)
1. [コード](#コード)
1. [デプロイ](#デプロイ)
1. [注意点](#注意点)
1. [ナレッジマイニングソリューションアクセラレーター日本語版の紹介](#ナレッジマイニングソリューションアクセラレーター日本語版の紹介)

#スキルについて
Azure Cognitive Search では、Microsoft の最先端の AI を使って、ストレージ内のドキュメントから抽出したデータに様々なタグをつけることができます。

その中には、[OCR スキル](https://docs.microsoft.com/ja-jp/azure/search/cognitive-search-skill-ocr)というものがあり、画像やスキャン済み PDF なども検索対象にしたい場合に重宝されるスキルかと思います。[公式ドキュメント](https://docs.microsoft.com/ja-jp/azure/search/cognitive-search-skill-ocr#skill-parameters)上は日本語をサポートしていますので、`defaultLanguageCode` を `ja` に設定することで日本語を利用することができます。

# 2021/07/11 Updates🎉
以下のようなカスタムスキルを実装することで、本記事のような問題を一切気にする必要がなくなりました。

https://qiita.com/nohanaga/items/cc33419244d0e5b86ecc

#OCR スキルの問題点
2021 年 3 月時点で、OCR スキルには以下のような問題点があり、日本語のスキャンされた PDF などのドキュメントを OCR した際にそのインデックスをうまく検索することができません。この問題は報告しているのですが、まだ直ってません。。。

これは、バックグラウンドで使用している OCR が [Vision API v2.1](https://westus.dev.cognitive.microsoft.com/docs/services/5cd27ec07268f6c679a3e641/operations/56f91f2e778daf14a499f20d) (/vision/v2.1/ocr) の出力をスペース処理せずに連携していることに起因していると思われますが、2021 年 4 月に GA した、[Read API v3.2](https://docs.microsoft.com/azure/cognitive-services/computer-vision/whats-new#computer-vision-v32-ga) (/vision/v3.2/read/analyze) の行単位出力を実装するようにアップデートされれば一気に解決されるかと思います。

で、現状ですが以下のように、OCR からの出力がすべて 1 文字ごとに半角スペースが入っており、これがそのままインデックスに登録されているためです。


![02.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/674344/2aa01da2-eb0b-0f30-efa0-148774d4b2d7.png)

Azure Cognitive Search の検索エクスプローラーから青空文庫の「[吾輩は猫である](https://www.aozora.gr.jp/cards/000148/files/789_14547.html)」のスキャン画像を OCR スキルで処理した結果を検索しています。
クエリ文字列には、半角スペースで区切られたテキストを検索するために、一文字ずつ半角スペースを挿入していますが、これは本質的な解決にはつながりません。

以下に Azure Cognitive Search にデフォルトで定義されているスキルパイプラインを表示します。このダイアグラムは[デバッグセッション](https://docs.microsoft.com/ja-jp/azure/search/cognitive-search-debug-session)機能で可視化することができます。

![01.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/674344/5d0105cc-f313-ed4c-67d5-c81228353b7f.png)

Azure Cognitive Search でインデックスを作成するときに、`OCR を有効にし、すべてのテキストを merged_content フィールドにマージする` 設定を有効にすることで、自動的にスキルパイプラインに OCR スキルが組み込まれます。
処理の流れを見てみると、OCR スキルからの出力は、[マージスキル](https://docs.microsoft.com/ja-jp/azure/search/cognitive-search-skill-textmerger)によってドキュメントテキスト本文と結合され、その結果が[言語検出](https://docs.microsoft.com/ja-jp/azure/search/cognitive-search-skill-language-detection)を経て[エンティティ認識スキル](https://docs.microsoft.com/ja-jp/azure/search/cognitive-search-skill-entity-recognition)と[キーフレーズ抽出スキル](https://docs.microsoft.com/ja-jp/azure/search/cognitive-search-skill-keyphrases)にインプットされることがわかります。

上で説明したとおり、OCR スキルからの出力には半角スペースが含まれているため、これより後段に置かれたスキルはうまく日本語として認識できませんので、以下のように変な出力になってしまい、*うわっ Microsoft の OCR 精度低すぎ！* と感じてしまいます。

![吾輩は猫である画像.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/674344/8c4a7397-5b7a-bf38-2b10-1ac212c43e85.png)

インプットデータは、夏目漱石の「吾輩は猫である」を文章を画像化したものとします。この画像をスキルパイプラインに流すと、以下のような出力が得られます。

```json
            "people": [],
            "organizations": [],
            "locations": [],
            "keyphrases": [
                "い な",
                "な る",
                "な か っ",
                "ま る",
                "見 る",
                "や か ん る",
                "つ る つ る",
                "ま っ",
                "あ る 事",
                "つ か",
                "わ か",
                "あ っ つ",
                "か り",
                "か 分",
                "廻 る",
                "こ こ",
                "思 っ",
                "残 っ",
                "坐 っ",
                "違 っ",
                "あ ま り",
                "か 自 分",
                "あ ろ",
                "わ ゆ る 人 間",
                "書 生",
                "ち こ",
                "ど こ",
                "あ る 夏 目 漱 石",
                "ぶ 逢 っ",
                "頃 知 っ",
                "出 そ",
                "か フ ワ フ ワ",
                "見 始",
                "上 今 ま",
                "思 わ",
                "見 当",
                "人 間 中",
                "時 々 ふ",
                "落 ち つ",
                "出 会 わ",
                "持 ち",
                "真 中",
                "記 憶",
                "心 持",
                "ニ ャ ー ニ ャ ー 泣",
                "肝 心",
                "煙 草",
                "日 音",
                "番 獰",
                "種 族",
                "母 親",
                "突 起",
                "装 飾",
                "べ き",
                "片 輪",
                "兄 弟",
                "連 転",
                "薬 缶",
                "到 底 助",
                "名 前"
            ],
```

人物や組織、ロケーションエンティティは抽出されてません。
キーフレーズもよくわからんフレーズが抽出されてしまっています。

#Tips の紹介
現状で、このような問題に対応するために、私の Github にて、[Azure Cognitive Search 日本語OCR用スペース除去カスタムスキル](https://github.com/nohanaga/azure-search-japanese-ocr-tips/)を公開しました。

このカスタムスキルを OCR スキルの直後に置くことで、日本語文字列に含まれる半角スペースを除去し、後段処理に渡すことができます。

[カスタムスキル](https://docs.microsoft.com/ja-jp/azure/search/cognitive-search-custom-skill-web-api)というのは、Azure Cognitive Search の重要な機能の一つで、ドキュメントから抽出した文を外部に置いた Web API エンドポイントにデータを飛ばして自由に処理することができるスキルです。指定のインプット/アウトプット JSON スキーマ定義にしたがってコードを記述するたけで簡単に利用可能です。

この「日本語OCR用スペース除去カスタムスキル」は非常にシンプルな文字列処理スキルであり、新たなカスタムスキルを開発する際のサンプルコードとしても利用できるかと思います。

![03.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/674344/e26a3089-00ee-4109-c024-649223359754.png)

[カスタムスキル](https://docs.microsoft.com/ja-jp/azure/search/cognitive-search-custom-skill-web-api)を追加してスキルパイプラインを可視化すると上図のようになります。
では、このスキルパイプラインを実行してみましょう。


```json
            "people": [
                "夏目漱石"
            ],
            "organizations": [],
            "locations": [],
            "keyphrases": [
                "書生",
                "もの",
                "人間中",
                "見始",
                "始め",
                "むやみ",
                "記憶",
                "薄日音いしめしめし",
                "目次わか",
                "真中",
                "夏目漱石",
                "母親",
                "生れ",
                "はしめ",
                "少し",
                "けむり",
                "速力",
                "連転",
                "いぶ",
                "装飾",
                "のみなら",
                "見当",
                "うち",
                "あく",
                "煙草",
                "あかたわ",
                "たばこ煙",
                "隠し",
                "はず",
                "突起",
                "けんとう",
                "スー",
                "種族",
                "やかんる",
                "兄弟",
                "見え",
                "当時",
                "別段",
                "片輪",
                "心持",
                "はかり",
                "てのひら",
                "薬缶",
                "名前"
            ],
```

やっと人物エンティティに夏目漱石が抽出されました。キーフレーズもうまく抽出されてきているかと思います。若干文章に青空文庫の振り仮名が混ざってしまっていますが。

また、OCR スキルの `defaultLanguageCode` を `ja` (日本語)に設定していても、以下のように長音記号「ー」を半角ハイフン「-」として認識することが多々あり、これを正規表現で修正する処理も入れています。

![04.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/674344/7b1bb253-92f2-875f-dc55-4c3854b9a1d8.png)

#コード

あくまでルールベースなので限界はあります。
C# なら、

```csharp
    // 正規表現で全角文字+半角スペースの場合、半角スペースのみを除去
      var newContent = Regex.Replace(mergedText, @"([^\x01-\x7E])\x20", "$1");
    // 全角文字+ハイフン+半角スペースの場合、ハイフンを全角長音に変換し半角スペースを除去
      newContent = Regex.Replace(newContent, @"([^\x01-\x7E])-\x20?", "$1ー");
```

Python なら、

```python
import re

pattern1 = re.compile(r"([^\x01-\x7E])\x20")
pattern2 = re.compile(r"([^\x01-\x7E])-\x20?")
newContent = pattern1.sub(r"\1", mergedText)
newContent = pattern2.sub(r"\1ー", newContent)
```

C# の具体的なコードは [Github](https://github.com/nohanaga/azure-search-japanese-ocr-tips/blob/b9f17c31f944ed721c8ab2b597206a2b57e9838e/JapaneseOCR.cs#L45) をご覧ください。

#デプロイ

以下のボタンを押せば一発で Azure にデプロイできます。
<a href="https://portal.azure.com/#create/Microsoft.Template/uri/https%3A%2F%2Fraw.githubusercontent.com%2Fnohanaga%2Fazure-search-japanese-ocr-tips%2Fmaster%2Fazuredeploy.json">![deploytoazure.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/674344/dfd2af67-6d1f-2d8b-02de-5067b415a1d3.png)
</a>

#注意点

日本語OCR用スペース除去カスタムスキルは Azure Functions を利用しているため、[利用料](https://azure.microsoft.com/ja-jp/pricing/details/functions/)がかかります。また、公式にサポートしているものではございません、が MIT License ですので、自由にお使いいただけます。

ハンズオン形式でカスタムスキルを作成する記事を用意してありますので、[こちら](https://qiita.com/nohanaga/items/26355ffba58ba4c71c6e)もご参考にどうぞ。

#ナレッジマイニングソリューションアクセラレーター日本語版の紹介
[ナレッジマイニングソリューションアクセラレーター](https://github.com/nohanaga/azure-search-knowledge-mining)

このアクセラレーターは、Azure Cognitive Search を使用して初期ナレッジマイニングプロトタイプをすばやく構築するために必要なすべてのリソースを開発者に提供します。このアクセラレーターを使用して、独自のデータで開発作業を開始したり、学習ツールとして使用して、Cognitive Search を使用してビジネスの固有のニーズを満たす方法をよりよく理解できます。

このリポジトリでは、適切な Azure リソースをデプロイするためのテンプレート、最初の検索インデックスを作成するためのアセット、カスタムスキルを使用するためのテンプレート、基本的な Web UI など、Cognitive Search ソリューションをすばやく作成するために必要なすべての作成物が提供されています アプリ、および PowerBI レポートで検索ソリューションのパフォーマンスを監視します。ドキュメント全体にベストプラクティスを盛り込み、ガイドを提供します。Cognitive Search を使用すると、デジタルデータ（ドキュメントやテキストファイルなど）とアナログデータ（画像やスキャンしたドキュメントなど）の両方に簡単にインデックスを付けることができます。