ARISE analytics kaggle部の越智です。
昨年度SIGNATEにて開催されたテクノプロ・デザイン社 商品パッケージ画像解析(上級部門)に参加し銅メダル(14位/41チーム・209人)を獲得することができました。そこで今回は、このコンペの取り組み内容、解法の解説を行いたいと思います。
コンペ概要
コンペの目的
まずは今回のコンペの概要について説明します。
本コンペは、食料や飲料などのパッケージ画像に対して、その栄養成分表示からエネルギーの項目を自動的に抽出して読み取るアルゴリズムの作成をするというとてもシンプルなタスクです。(図はコンペの概要ページより抜粋)
データセット
次に与えられたデータセットについて説明します。
訓練データとして246枚の商品パッケージのjpegの画像データと、それに対応する栄養成分表の矩形情報とエネルギーの数値(kcal)のjsonデータが与えられました。
下に画像データの例とjsonデータの例を示します。※コンペの写真データを外部公開することはできないため、イメージとしてこちらで用意した写真データです。
{
"nutrition_facts": [
290.5032679738562,
234.94117647058823,
644.0980392156863,
394.41830065359477
],
"calory": "21"
}
jsonデータの例
評価方法
精度評価にはF1スコアが指定されました。TP/FP/FNは編集距離(置換なし)で計算し、これらを用いて以下のように計算します。
・TP(True Positive):削除されずに残った文字数(この場合3個)
・FN(False Negative):削除された文字(この場合”3″と”5″の2個)
・FP(False Positive):正しく認識できた文字を除いた文字数(この場合2個)
例えば、正解:”12345″、予測:”31254”とした際には、まず正解の文字列に対して挿入と削除を繰り返して予測された文字列になるように編集を加えます。
・先頭に”3″を挿入 “12345”→”312345″
・”2″の次の”3″を削除 “312345”→”31245”
・”4″の前に”5″を挿入 “31245“→”312545”
・最後の”5″を削除 “312545“→”31254”
よって、TP=3(削除されずに残った文字数)、 FN=2(削除された文字)、FP=2となります。
評価値は0~1の値をとり、精度が高いほど大きな値となります。正しい数字を正しい順序で読み取ることが重要となります。
投稿方法
提出ファイルは以下のファイルを作成して投稿します。
submitファイル
├── model 学習済モデルを置く
├── src 必須: Pythonのプログラムを置くディレクトリ
│ ├── predictor.py 必須: 最初のプログラムが呼び出すファイル
│ └── … その他のファイル (ディレクトリ作成可能)
└── requirements.txt 追加で必要なライブラリ一覧
また、その他の制限としては以下がありました。
・投稿ファイルのサイズは1GB以内。
・推論時間は10[秒/画像]、全体で3時間以内
1点目について、モデル数を増やすとすぐに超えてしまいます。2点目についても、沢山のモデルを噛ませて推論をすると超えてしまうので注意が必要でした。
評価システムと同様のDockerイメージが配布されているので提出前に実行確認をして制限を超えていないか確認することができます。
解法
ベースライン
最初にベースラインの解法を説明します。
学習データが少ないこともあり、コンペのフォーラムで紹介されていたpaddleOCRという学習済のモデルをそのまま利用するアプローチをとりました。
paddleOCRのモデルを利用して画像から文字を読み取り、読み取った文字からエネルギー数字を抽出しました。
・OCRフェーズ
・paddleOCRを利用して英字の読み取り
・数値抽出フェーズ
・kcalのような文字の直前の数字を抽出
・kcalパターン ‘(?i)([\w.]+\skcal).‘
・kcalのうち1つかけたパターン'(?i)([\w.]+\s*(kcal?|kca?l|kc?al|k?cal)).‘
・kcalのうち2つかけたパターン'(?i)([\w.]+\s(k?c?al|k?ca?l|k?cal?|kc?a?l|kc?al?|kca?l?)).*’
・文字化けしやすい数字の修正
・I→1、b→6、g→9、O→0、
ベースラインの時点では暫定スコア0.776、最終スコア0.796でした。
ベースラインからの改善
改善のために試したこととその結果を以下に表で記載します。ベースラインにおける、「OCRフェーズ」、「数値抽出フェーズ」に加えそれらの前段における「前処理フェーズ」それぞれで精度向上に向けて色々と試してみました。
問題/課題 |
例 |
試したこと |
結果 |
||
---|---|---|---|---|---|
フェーズ |
内容 |
○× |
詳細 |
||
・商品の角度によりエネルギー部が歪んでいると読み取れない |
|
前処理フェーズ |
|
× |
|
・画像の解像度が悪いと読み取れない |
|
前処理フェーズ |
|
〇 |
|
・画像の光の加減で読み取れない |
|
前処理フェーズ |
|
× |
|
・「kcal」だけではなく「エネルギー」や「熱量」の文字位置からエネルギー数値を特定 |
|
OCRフェーズ |
|
〇 |
|
・エネルギー部を読み取れない場合がある ・全く異なる文字として読みとる |
– |
OCRフェーズ |
|
〇 〇 |
|
・一部間違えた文字として読みとる |
「135kcal」を「|3bk,čal」 「熱量」を「熟堂」 「エネルギー」を「正ネルギー」etc. |
数値抽出フェーズ |
|
〇 |
|
・xxkcalの数値が2種類書かれているとどちらが商品のエネルギー数値か判別できない |
|
数値抽出フェーズ |
|
〇 〇 |
|
・人が見てもわからない |
– |
対応策なし |
– |
– |
– |
最終モデル
改善結果と計算時間、ファイルの大きさの観点から、下図のように条件分岐をさせて各モデルにインプットしていくことにしました。
詳細は以下です。
・パラメータを変えた2つずつの英語/日本語対応のpaddleOCRの計4モデルで文字を抽出
・エネルギー数値の抽出ができた場合は、多数決で最終的なエネルギー数値を決定
・エネルギー数値の抽出ができなかった場合は、別モデルのeasyOCRで文字を抽出
・ただし画像サイズが300kbyte未満であればSwimirを用いて高解像度化し、再度paddleOCRでエネルギー数値を抽出
・上記で抽出できない場合に、easyOCRで文字を抽出
・最終的にエネルギーの数値を抽出できなかった場合は”0”と設定
平均推論時間は10[秒/画像]という制限があるため、時間を要するSwimirとeasyOCRは一部の画像のみの利用としました。
また、最終的にエネルギーの数値抽出できなかった場合を”0”としたのは、エネルギー数値は四捨五入の関係上か0を含むことが多く、また一桁の方が編集距離が短くなる傾向にあるためです。
このモデルで、暫定スコア0.958、最終スコア0.930となり14位の銅メダルを獲得することができました。
終わりに
回は、銅メダルを獲得した商品パッケージコンペの解法について紹介しました。
本解法はモデルの前後の処理を工夫したものでしたが、上位者の解法の中には、矩形情報抽出のためのyolov8や数値抽出のためのTrOCRの学習を行っている方もいました。今後は学習方面にも手を出してみたいと思っています。
ARISE analyticsでは、kaggle等の分析コンペティションで上位成績を残すと、インセンティブとして報奨がもらえるARISE Tech Master制度があるので引き続き金メダルを目指し、努力していきたいです。
分析はチームワークが大事です。役割を決めお互いの強みを持ち合うことで、一人で行うより何倍も効率的に分析課題に取り組めます。我々と一緒にKaggle部を盛り上げてくださる方、ARISE analyticsに興味を持っていただいた方はこちらの採用ページをご覧ください。
※過去のKaggle部活動記はこちら
※ARISE analytics 公式noteはこちら