MUTE.log

マンガ、うまいもん、旅、そしてエンターテイメントについて、だらだら書いていきます。

「#俺マン2013投票検索(仮)」の裏側

押し掛け女房みたいなカタチですが、今回の#俺マン2013の投票ツイートを自動集計するシステム#俺マン2013投票検索(仮)を構築致しました。

この俺マンというイベントですが、「自分の独断と偏見でこの1年読んだマンガから面白かった作品を選ぶ」ということ以外にこれといったレギュレーションがないため、以下のような難点があります。

  • どのタイトルが投票されるのか分からない(いわゆる賞レース系には単行本発売時期などの縛りがある)
  • ひとり何作投票するか決まっていない(投票ツイートが分散して存在する)

そして、最大の問題が自由投票形式のため、タイトルの表記がばらついたり、間違っていたりというケースがあるということです。

たとえば「よつばと!」という作品に対して、「よつばと」のように「!」を端折った表記をしても、人間の目で見れば「よつばと!」と同義であると判断できるのですが、名寄せするためには「よつばと」「よつばと!」を同じく「よつばと!」と判断させなければなりません。

また、いろいろ思い込みなどに起因する書き間違えたりするわけで、この辺りをどうやって解決するのかということが問題になりますね。まあ、このように言っているぼく自身が「神様がうそをつく。」という作品に対して「神様うそをつく。」と"てにをは"を間違えてしまったりいるんですけどね…。

そんな投票検索(仮)ですが、どのように構築したのかをまさに誰得(俺マンのようなフリースタイル投票をするケースがあまり想定できないw)かつ取り留めも無い状態ですが、まとめておきます。

システム概要

今回の検索システムは次のような流れで動いていました。

もともと「自由入力できる」というのが俺マンの最大の特徴であるため、その辺りを損なわないようにどうすればいいかというのを結構悩んでいました。

ブレークスルーとして「出来る限り自動判定、あとはがんばって目検!」というコンセプトに辿り着けたので、システム化の見通しがたち、試作を開始しました。開発前から中の人が頑張らないといけないのは分かっていたのですが、思いのほか大変でした(^^;)

  1. 作品名自動判定辞書の充実
  2. 投票ツイートの判定処理
    • Twitterから「#俺マン2013 -RT」のツイートを採取する
    • 個々のツイートから作品名を自動判定し抽出した結果をデータベースに登録する
  3. 自動判定結果をWebで確認
    • 作品名の判定漏れがあった場合、1の自動判定辞書に追記して、再実行。出来る限り、自動判定で省力化したいため。
    • 判定漏れであっても、特殊な省略表記など、一般化しづらい場合は、手動入力で追記。
    • 誤表記や判定し過ぎなケース(「奈緒子」「太郎」「ありがとう」などが多かった)は手動で削る

システム構成

大した構成ではないので、さくっと飛ばします。

  • バックエンド(ローカルのMacBook Air)
    • Node.js
    • Mecab + ユーザ辞書
    • npm
      • async
      • jaCodeMap
      • mongoose
      • twitter
      • mecab(ユーザ辞書対応パッチをした)
  • データベース(@mongolab)
    • MongoDB
  • フロントエンド(@heroku)
    • Node.js
    • MongooseJS
    • Express
      • ejs
      • async
      • Passport / Passport-twitter
      • apa-client

作品名自動判定辞書+Mecabについて

今回のシステムのキモでありながら、序盤から終盤にかけて一番の難関だったのがMecab周りだったように思います。

システム辞書に組み入れると高速化できるということだったのですが、頻繁にシステム辞書の再構築をするのも大変そうだったので、ユーザ辞書主体としました。

作品名の収集

まず「いかに作品名を自動判定させるか」というところでいうと、作品名辞書を充実させることが重要になってきます。

とはいえ、何もないところからのスタートではしんどすぎますので、Wikipedia、ニコニコ大百科、これまでの俺マンランキングに入った作品群、2013年に刊行されたマンガ作品(2012年12月〜2013年3月までのデータが取れなかった…)を出来る限り収集しました。

表記に付いても英数字はいわゆる半角表記に統一して管理することにしました。エディタにある文字種置換で一気にこの辺は実行しました。何度も繰り返すならsedスクリプトとか準備した方がいいかもしれませんね。

ちなみにツイートはjaCodeMapで半角英数化をおこなったもので判定処理を実施しています。

ユーザ辞書の構築

試作段階で品詞名を適当に入れてもキチンと動作することが分かったので、次のような感じに辞書を組み上げていき、判定結果として品詞が「名詞-マンガタイトル」のものだけを抽出するという基本コンセプトが成り立たせることができました。

重版出来,0,0,10,名詞,マンガタイトル,一般,*,*,*,重版出来!,*,*
重版出来!,0,0,10,名詞,マンガタイトル,一般,*,*,*,重版出来!,*,*

判定には先頭のフィールドを使用し、集計には後ろから3番目のフィールドを使用しています。これにより表現のブレを吸収することが出来ています。

ただまあ、漢字の閉じ開きやカナ表記の扱いまでは吸収できないため、票の集中しそうでやばそうな作品についてはこんな対策もしていました。(末尾の「。」がない、うそ→ウソ・嘘、神様→神さまのバリエーション)

神様がうそをつく,0,0,10,名詞,マンガタイトル,一般,*,*,*,神様がうそをつく。,*,*
神様がウソをつく,0,0,10,名詞,マンガタイトル,一般,*,*,*,神様がうそをつく。,*,*
神様が嘘をつく,0,0,10,名詞,マンガタイトル,一般,*,*,*,神様がうそをつく。,*,*
神さまがうそをつく,0,0,10,名詞,マンガタイトル,一般,*,*,*,神様がうそをつく。,*,*
神さまがウソをつく,0,0,10,名詞,マンガタイトル,一般,*,*,*,神様がうそをつく。,*,*
神さまが嘘をつく,0,0,10,名詞,マンガタイトル,一般,*,*,*,神様がうそをつく。,*,*
神様がうそをつく。,0,0,10,名詞,マンガタイトル,一般,*,*,*,神様がうそをつく。,*,*

また、CSVでは管理上見づらいので、元データとしては集計対象となる正規化された作品名と判定対象の文字列を組み合わせたタブ区切りのデータを起こして、これを基にして上記のMecabユーザ辞書の形式に変換をかけています。

神様がうそをつく。[tab]神様がうそをつく
神様がうそをつく。[tab]神様がウソをつく
神様がうそをつく。[tab]神様が嘘をつく
神様がうそをつく。[tab]神さまがうそをつく
神様がうそをつく。[tab]神さまがウソをつく
神様がうそをつく。[tab]神さまが嘘をつく
神様がうそをつく。[tab]神様がうそをつく。

開始時には8000レコードぐらいだったのですが、最終的にデータとしては10200レコードぐらいになっています。どうしてこうなったw

ハマりどころ1 - カンマを含む作品

どうしても判定がウマいこといかない作品がいくつかあって、共通してるのがタイトル内に「,」表記がありました。エスケープ表記やらなにやらを試してみたけれども、結局はユーザ辞書自体がカンマ区切りであるため(形態素解析するうえで「,」を含む必要性がないでしょうから当たり前なのかも)、ユーザ辞書としてはいわゆる全角の「,」で登録して、集計時に半角置換を行うということで対処できました。

ハマりどころ2 - 作者名に作品名が含まれるケース

特に今回では「重版出来!」の作者である松田奈緒子先生の名前を併記されるケースがまさにこれに該当するのですが、駅伝ストーカーマンガである「奈緒子」が判定結果に上がってきてしまいます。ここであっさり「奈緒子」は旧作だからと言って切り捨てられないのが、俺マンのレギュレーション「今年読んだマンガ」があるためです。投票可能性がないわけではないので、こういう判定をされる先生方の名前を次のようにmecabユーザ辞書に追加しておきました。品詞が「マンガタイトル」ではないので抽出対象からはずすことが自然に実現できました。

松田奈緒子,0,0,10,名詞,マンガ作者,一般,*,*,*,松田奈緒子,*,*

ハマりどころ3 - バッククオート

ツイート上の顔文字などによく使われる"`"(バッククオート)についてはMecab処理時のエラーの原因になるため除去後に判定しています。気付くまでが大変でしたw

ハマりどころ4 - 多重実行できない

深堀はあまりしていないのですが、普通にツイート群に対して、mecab処理を投げると非同期にコールされすぎて、プロセスがお亡くなりになるという現象がありました。ツイート数が膨大になるわけでもないので、今回はasyncをかませて、同期的に1ツイートずつmecabに食わせるようにしました。規模が10倍ぐらいになるぐらいなら、まだまだこれでも耐えられそうな速度ですし、桁がさらに増えるようなことがあれば今回のシステムでは到底まかなえない(人力的に)ので、まあいいかなーと思っています。

MongoDBまわり+ExpressJSまわり

大したことをやっていないので割愛します。

運用まわり

結局自動判定しきれないのは前段から把握できていたので、ツイートのMecab判定データをアップするたびに投票検索のページを見ながら、

  • 投票に関係しないツイートの除外
  • ユーザ辞書に足りないタイトルの追記(といっても正しい表記をしるためにググりまくる。これがしんどい)
  • 文脈的に投票していないと思われるタイトルを投票から除外する

といったことを繰り返していました。辞書の充実による効果もありましたが、1票しか投じられていない作品数はハンパ無く、この辺は最後まで手が抜けなかった部分であります。

集計人のはしくれとして申し上げますと、投票者の方には次の点を心がけて頂きたいなと思います。

  • 作品名の省略はしないでほしい(せめてぐぐってすぐに出る名称で…)
  • 短すぎるタイトル(黒)、一般語彙(日常、会話…)には可能なら出版社名か、作者名も併記してほしい
  • さらにいうと、作品名の思い込み間違いでないかどうか、ぐぐってほしい

ネルヤさんのポータルにも書いてあることなのですが、徹底しづらいですよね…。

書誌情報について

当初は投票作品名からのユーザ串刺し検索だけだったのですが、その作品を知らない人が作品を手に取るまでの導線を確保したい(かつ可能ならアフィ収入がゲットできればというスケベごころ)ということで、書誌情報を掲載するようにしました。

締切後、トップページの表示の変化が無さ過ぎるので、ランダムにピックアップタイトルを載せるようにしたりしています。

自動化のしようがないので、ググる→コピペ・コピペ・コピペの繰り返しです。

今も徐々に追加しています。ネルヤナイト後のランキング公開時には2票以上あつめている作品はほぼ埋め切れていると思います…。

最後に

結構読んでいたり、聞きかじってるつもりでしたが、知らない作品の多さに圧倒されましたね。今回の作業でとりあえず作品(名)に関する知識はかなり増えました。

あらすじやら試し読みを蓄積することで欲しい作品が増えて仕方ありませんw

俺マン2014が開催されるかどうかわかりませんが、来年に向けて作品辞書の充実化を普段から心がけておきたいと思います(^^;)