BrainPad株式会社ブレインパッドのデータ活用に関する取り組みや製品・サービス開発の裏側、社員の日常などをご紹介します。2017-11-07深層学習で麻雀の点数を自動計算してみた!分析エントリーOpenCVを用いた「テンプレートマッチング」と、DeepLearningの物体検出アルゴリズムの一つ「SSD」使って、筆者が大好きな麻雀の牌を検出し点数を自動計算できるか試してみました!こんにちは。アナリティクスサービス部の栗原です。今回のブログは、筆者の大好きな麻雀をテーマに取り上げ、1.物体検出に関わるテクノロジー2.BrainPadの日常(3.麻雀の面白さ)をお伝えしたいと思います。皆さん麻雀経験はありますか?おそらく、1.「全く経験がない」2.「アプリ等でやったことはあるが実際に卓を囲ったことはない」3.「雀荘に行って一通りゲームを楽しめる程度」4.「雀荘に通うほど好き」のいずれかに該当するのではないでしょうか。3.の人向けのコンテンツです。(4.の麻雀玄人の方からは甘いこと言うなと叱られる内容かもしれないのでご容赦ください・・・)筆者は3.に該当します。麻雀は好きなのですが、点計算(※)が正確にできず、覚えるのが面倒くさくていつも人任せです。そのためメンバーに点数計算が出来る人がいないと困ります。筆者のような人間のために「自動で計算するアプリ」はあるのですが、入力が面倒だったり結局時間が3分位かかったりするので、場や流れが乱れます。上がった牌の14個を手動で入力しなければならないもしくは上がった役の全てを入力しなければならないからです。(※)麻雀経験のない方はすみません。簡単に説明すると麻雀は難しいドンジャラみたいなものです。役(ルール)に則って絵柄をそろえるのですが、牌や役の種類や細かいルールが多く、非常にややこしい*1*2。「アプリだとアシスト機能があるからいいけど、実際に卓を囲んでやるのは敷居が高い」という返答をよくされます。声を大にして言いたいのは、麻雀というのはテーブルゲームの一つとしてとても面白いゲームだということ。奥が深い頭脳ゲームで、そこに運の要素が絶妙に絡む・・・。リカバリー力等様々な力が試され鍛えられます。初心者が上級者に勝てるのも魅力の一つです。個人的にはアナログであることが重要だと思っています。戦いが終わった時、卓を囲んだ4人の中には謎の一体感が生まれていることも多々あります。「カメラで卓を取ったら自動で点数計算してくれる麻雀自動役計算くんを作りたい!」(「パシャ!8000オール!)」みたいなイメージ)と思い立ちました。今回は、「OpenCV」を用いたテンプレートマッチングと、DeepLearningの物体検出アルゴリズムの一つである「SSD」を用いて自動役計算くんを構築した結果を比較してみました。■実際に使ってみた■まとめ■はじめに突然ですが、当社のことをすでにご存知だった方への質問です。皆さんがブレインパッドに持っている印象はどのようなものでしょうか?ベンチャーで若い会社?真面目な会社?最近だとAIの会社?と聞かれることもあります。筆者は、この会社は本当に面白い会社だと思っています。「色々なスキルセット/バックグラウンドを持った人が集まっている」かつ「業務外でも自分のスキルを使っていろいろなことを試みようとする人が多い」からです。実は今回ブログを執筆するにあたり、上記のようなたくさんの有志の方の協力をいただきました。今回はテンプレートマッチングとSSDを用いますが、昨今のテクノロジーの進歩は凄まじい勢いです。R-CNN*3でした。一つ学んでいる間に新しい手法が発表され、キャッチアップするだけでも大変です。そんな変化は、我々の身近なところでも広がっており、活用先も広がっています。*5きゅうりの仕分けを自動化するマシーン*6上記はすべて、画像認識のテクノロジーを使っています。画像認識に限らない話ですが、世の中にあるサービスは誰かの身近な課題や想いで始まることが多いと思っています。きゅうりの自動仕分けマシーンを開発した農業を営む小池氏は、「仕分け担当だった母親の負担を軽減してあげたい」という想いのもと開発を始めたそうです。皆さんの身近な課題もテクノロジーで解決できるかもしれません。筆者の場合は、「麻雀を始める敷居を下げたい」「点数計算を楽にしたい」というのが想いです。前置きが長くなりましたが、今回実現しようとしている「麻雀自動役計算くん」は、かなりざっくり以下のような流れになると思います。今回のブログは、上記の牌の識別にあたる部分を中心にご紹介します。■データの準備データの説明をするために、少しだけ麻雀牌の話をします。麻雀の牌というのは商品によって微妙に大きさや書体が異なります*7。どの種類の牌でも確実に検出できるようになるためには、上記のように様々な種類の牌を学習させる必要がありますが、今回はプロトタイプと割り切り、1種類の牌のみで学習します。麻雀牌は下記の通り全部で34種類あります*8。1牌1ファイルの画像をiPhoneで撮影し、これを正解データとします。(麻雀好きの社員に自前の牌をお借りしました!)。テストデータ用に様々な条件下(明るさ、撮影角度、牌の種類等)で撮影したテスト画像を用意しました。※実際に撮影した正解データとテストデータ各牌の正解データテストデータ(例)以降、テンプレートマッチングとSSDで実装してみた結果を記載していきます。■OpenCVのテンプレートマッチングで実装してみる1.テンプレートマッチングとはテンプレートマッチングは、テンプレート画像を入力画像上でピクセル単位に走査させて類似度を計算し、入力画像上のどこに検出対象があるか位置を発見する手法です。物体検出の分野では比較的古典的な手法で、アルゴリズムもわかりやすく、各言語のライブラリも充実しているため画像処理初心者には取り組みやすいかと思います。学習という概念がないため深層学習と比べると処理に時間がかかりません。一方でロバスト性に欠けます。具体的には、一つのテンプレートでは色々な大きさの物体を検索することが難しく、明るさ変動や回転等の変形にも弱いです。OpenCVというオープンソースの画像処理ライブラリを用いPython3で実装しました。結論から言うと、いまいちでした。検出結果どこに何の牌が検出されたか、枠とラベルを描画しています。どの牌がいくつ検出され、検出されたもののうち適合率が最も高いスコアを記載しています結果をみると、w(西)が15個、n^(北の上下反転)が11個、と1種類の牌が異常に多く検出されています。理由は簡単で、前述の通りテンプレートマッチングはピクセル単位で入力画像上を走査しているため、1ピクセルでも異なれば1検出と判定されてしまうからです。試しに、マッチした座標情報に対し、牌の横幅以上のピクセル数が離れていた場合にのみ、検出は有効とすることにしてみましょう。len(np.unique(tmp_match_pixcel))print(cnt)2対処療法でしかないですが、これで正しく2個と判定されます。問題はまだありそうです。今回は撮影した画像を手動でトリミングしたが、運用上は現実的でないためサービス側の仕様でカバーする?※イメージテンプレートマッチングは明るさや変形に弱いという特徴がありますが、この問題に取り掛かる前に、好条件下でも精度が悪い結果となりました。牌検出なんてサクッとうまくいくかと思っていたのですが意外とそうでもなかったので、次はSSDで試してみます。個人的にテンプレートマッチングの結果で面白いなと思ったのは、リサイズせず画像に余白がある状態で検出して誤検出をしてしまう場合、牌の中に内包関係があるかないかが関係していそうだということでした。下記図はどこで何に誤検出をしたかをまとめています。確かに「三」という漢字の中には「一」も「二」も含まれています。この結果は画像上を走査しているので当然の結果といえばそうかもしれません。牌の判別に入るのではなく、牌の輪郭を検出をしてから判別する方が筋が良さそうですね。麻雀の場合、1牌でも検出できなかったり誤検知があると、点数計算にダイレクトに影響し勝敗が変わります。要するに、精度ほぼ100%で牌を正しく検出できないとダメなのです。Liuらが発表した物体検出*9アルゴリズムの一つです。アルゴリズムについては参考になる様々なQiitaや他のブログでの記事がたくさんあるので、本ブログでは細かい説明はしません。筆者も参考にさせていただいたので、興味がある方は是非それらの記事を参考にしてみてください。*10*11*12*13細かい説明はしないとは言いつつも、簡単にまとめると以下のようになります。今回実装にあたり、Gitで公開されているkerasのコードを参考にさせていただきました*17。2.ひと工夫した学習データ作り冒頭でも述べた通り、入力画像には基本的には14個の牌が存在しています。その入力画像上にどこに牌があってそれが何の牌かを知りたいので、牌の場所とラベルの情報を持った学習データを用意する必要があります。これを人力で何千枚も用意するのは骨が折れます。そこで今回は、適当な麻雀マット画像の上に、各牌のテンプレート画像からランダムに1牌選び、横並びで配置するジェネレータを構築しました。することで1枚の画像の中に複数牌が横に並び、かつ牌の場所とラベルの情報を持ったデータが出来上がります。今回は使用している牌のみを学習することになるので牌の種類に対する汎化性がないという懸念点が出てきますが、プロトタイプとしての検証だと割り切り、気にしないこととしました。学習データの画像のサイズは、SSD論文にも記載されていますが、試行錯誤の結果も踏まえ、高解像度の学習データがより精度が出たため、512×512でデータを作成しています。実際に学習させたデータを例として4枚ピックアップしました。3.入力画像の前処理SSDでは512×512のサイズで学習しているため、検出する際はそのサイズに入力画像がリサイズされます。今回撮影したテスト画像は横長の画像なので、モデルにインプットする際は牌が少し縦に伸びた形の画像となります。この現象が起きないよう、画像全体の上下にmarginを追加してアスペクト比が合うようにし、牌が引き伸ばされないようにリサイズした上で、モデルへのインプットとすることにしました。分類スコア*18全牌ほぼ100%で検出できています。前述の通り学習に使った種類の牌をテストデータで使っているため、他の牌だと精度が下がる可能性は高いです。汎化性があるかどうか、様々な条件で試してみましょう。明るさを変更してみます。実際に麻雀中にここまで綺麗な写真を撮らないといけないとなると、冒頭で述べたように場が乱れてしまいます。ユースケースを考えると、以下のように少し斜めから撮ることが多いかと思いますので、それも試してみます。検出結果(斜めからの撮影)分類スコア検出できている牌もありますが、できていない牌もあります。上記の例だと分類スコアの閾値を0.7で切っていますが、牌によって検出のしやすさがありそうです。牌毎に閾値を決めることも考えられますが、そもそも今回は斜めからみた牌は学習させていないので、セオリーとしてはアフィン変換等で斜め画像を生成して学習データに含めるのが良いかと思います。最後に、違う種類の牌でも試してみましょう。検出結果(非学習牌)*19分類スコアやはり牌に対する汎化性能が十分でないようです。今回の場合、プロトタイプのため1種類の牌で完全に検出できれば良かったのですが、今後は学習データに色々な種類の牌を増やしていこうと思います。「試しにやってみようぜ!」との声がかかり、実際に本アルゴリズムを使って点計算をすべく、フィールドワークという名のただの麻雀をしました。物体検出と役判定は確実にできていましたが、どうやらその後の点数計算(ルールベース)コードにバグが残っていたみたいです。※後から知ったのですが、Pythonにはmahjongライブラリがあって、上がり牌と諸条件をインプットすると点数計算してくれるメソッドがあるので、今後はそれを使おうと思います*20。今回はここまでですが、以降も引き続き改良を重ね、まだまだ改善の余地がたくさんあるものの、ゆくゆくはサービス化出来たらいいなぁと密かな野望を抱いております。使ってみた麻雀牌の検出にはテンプレートマッチングよりもSSDの方が精度が良かった学習データの作り方が命ユースケースを考えて何を学習データとするか決める必要がある今回は麻雀をテーマに進めてまいりましたが、当社の分析業務も上記と基本的な流れは変わりません。お客様の課題・要望を聞き、ユースケースを考えて求められる精度や制約を考慮した上で、データの前処理方法やモデリング手法を決定します。深層学習、AIはあくまでもツールに過ぎません。弊社の場合は初期段階で精度が芳しくなかった場合にどうやったら改善するか、このノウハウや経験を持った人間がたくさんいます。本ブログで少しでもブレインパッドって面白い会社だな(ついでに麻雀面白いな、久しぶりにやろうかな)と思っていただけたら幸いです!当社は、深層学習などの技術をビジネスに活用するべく、最先端の取り組みを積極的に実施しています。R-CNN*16:SSDよりYOLOv2の方が速くて精度も高いとYOLOv2論文に記載してあります。余裕があれば試してみたい。*17:https://github.com/rykov8/ssd_keras*18:各牌である確率。採用ページスターをつけました引用をストックしましたストック一覧を見る閉じる引用するにはまずログインしてくださいログイン閉じる引用をストックできませんでした。再度お試しください閉じる限定公開記事のため引用できません。