u++の備忘録

【Python&遊戯王】文章類似度の計算手法”Doc2vec”は「コンマイ語」にも通用するのか

はじめに

要するにやること

本記事では

  • 遊戯王カードの効果テキストを基に
  • Doc2vecという文章単位の類似度を計算するアルゴリズムを使って
  • 類似したカードを探すコードを実装してみます

遊戯王カードの効果テキストは、あまりの複雑さや特殊な解釈のために「コンマイ語」と揶揄されることもあるのですが、今回の記事はそんなコンマイ語に対してもDoc2vecが通用するのかの検証の意味合いも持っています。

動機

下記の記事を発見し、次のような記載があったので、やってみることにしました。
qiita.com

余談ですが遊戯王のカードでもやってみたのですが、自分が遊戯王に詳しくないので似ているかどうかピンと来なかったのでやめました。
Githubにソースとモデル一式をUPしておくので、興味がある方がいましたらやってみてください。
https://github.com/GuiltyMorishita/card2vec

ソースコードの修正

↑のGithubからデータを取得し、Windowsならではの"UnicodeEncodeError"を回避するためにコードを修正しました。修正の過程などはGithubでご確認ください。

github.com

gensimのDoc2vecは以前に下記の記事で触れていたこともあり、自分のやりやすい書き方にザックリと変更している箇所もあります。

upura.hatenablog.com

実行

card2vec.py(先駆者のこの命名センス、素晴らしいと思います)を実行すると、下記のような形式のデータを保持します。"names"はカード名、"texts"は効果テキストをそれぞれテキスト型で持っています。

f:id:upura:20180121181249p:plain

類似カードの出力

学習が終了したら、いよいよ類似カードを出力してみます。

    # 類似カードを求めたいカード名
    TARGET_CARD_NAME = names[random.randint(0, len(names))]
    # 直接指定も可能
    # TARGET_CARD_NAME = "xxxxxxxxxxxxxx"
    card_index = names.index(TARGET_CARD_NAME)

    # 類似カードと類似度のタプル(類似度上位10件)のリストを受け取る
    similar_docs = model.docvecs.most_similar(card_index)
    print(names[card_index])
    print(texts[card_index])
    print("--------------------is similar to--------------------\n")
    for similar_doc in similar_docs:
        print(names[similar_doc[0]] + " " + str(similar_doc[1]))
        print(texts[similar_doc[0]], "\n")

例えば「ソーラー・エクスチェンジ」に関する類似カードは、以下のような結果になりました。きちんと、ライトロード関連のカードが出力されています。

ソーラー・エクスチェンジ
手札から「ライトロード」と名のついたモンスターカード1枚を捨てて発動する。自分のデッキからカードを2枚ドローし、その後デッキの上からカードを2枚墓地に送る。
--------------------is similar to--------------------

閃光のイリュージョン 0.7759238481521606
自分の墓地から「ライトロード」と名のついたモンスター1体を選択し、攻撃表示で特殊召喚する。自分のエンドフェイズ毎に、デッキの上からカードを2枚墓地に送る。このカードがフィールド上から離れた時、そのモンスターを破壊する。そのモンスターがフィールド上から離れた時このカードを破壊する。 

ライトロード・ドルイド オルクス 0.7378130555152893
このカードがフィールド上に表側表示で存在する限り、「ライトロード」と名のついたモンスターを魔法・罠・効果モンスターの効果の対象にする事はできない。このカードが自分フィールド上に表側表示で存在する限り、自分のエンドフェイズ毎に、自分のデッキの上からカードを2枚墓地に送る。 

ライトロード・スピリット シャイア 0.7361984252929688
このカードの攻撃力は、自分の墓地に存在する「ライトロード」と名のついたモンスターの種類×300ポイントアップする。このカードが自分フィールド上に表側表示で存在する限り、自分のエンドフェイズ毎に、自分のデッキの上からカードを2枚墓地へ送る。 

ライトロード・ドラゴン グラゴニス 0.7135570049285889
このカードの攻撃力と守備力は、自分の墓地に存在する「ライトロード」と名のついたモンスターカードの種類×300ポイントアップする。このカードが守備表示モンスターを攻撃した時、その守備力を攻撃力が超えていれば、その数値だけ相手ライフに戦闘ダメージを与える。このカードが自分フィールド上に表側表示で存在する場合、自分のエンドフェイズ毎に、デッキの上からカードを3枚墓地に送る。 

ライト・バニッシュ 0.7092658877372742
自分フィールド上に存在する「ライトロード」と名のついたモンスター1体をリリースして発動する。モンスターの召喚・反転召喚・特殊召喚を無効にし破壊する。 

ライトロード・エンジェル ケルビム 0.703083872795105
このカードが「ライトロード」と名のついたモンスターを生け贄にして生け贄召喚に成功した時、デッキの上からカードを4枚墓地に送る事で相手フィールド上のカードを2枚まで破壊する。 

光の援軍 0.6913601756095886
自分のデッキの上からカードを3枚墓地へ送って発動する。自分のデッキからレベル4以下の「ライトロード」と名のついたモンスター1体を手札に加える。 

マイン・ゴーレム 0.6905478835105896
このカードが戦闘によって墓地に送られた時、相手ライフに500ポイントダメージを与える。 

ライトロード・レイピア 0.6747479438781738
「ライトロード」と名のついたモンスターにのみ装備可能。装備モンスターの攻撃力は700ポイントアップする。このカードがデッキから墓地に送られた時、このカードを自分フィールド上に存在する「ライトロード」と名のついたモンスター1体に装備する事ができる。 

魔法再生 0.6690211296081543
手札の魔法カードを2枚墓地に送る。自分の墓地から魔法カードを1枚選択し、手札に加える。 

おわりに

無事にコンマイ語でもDoc2vecが機能していそうだと分かりました。定量的に精査するならば、遊戯王Wikiなどのラベルを用いてP@kなどを算出するのが良い気がします。うまい検証方法を思いついたら、発表資料の形式にまとめられるレベルまで深めてみたいと思っています。自然言語処理では「英語でうまくいった手法を日本語で使ってみたら~だった」みたいな論文を見かける気もするので、僕もJSAI辺りへの投稿を目指したいと思います(←舐めんな)。