Python
自然言語処理
機械学習
gensim
fastText

fastTextを用いた競馬血統のベクトル化

概要

fastTextはFacebookが自然言語処理を行うために公開しているツールです。高速に自然言語処理を行うことができます。
GitHub fastText

仕組みについては、解説サイトを参照お願いします。
Facebookが公開した10億語を数分で学習するfastTextで一体何ができるのか

今回、そのfastTextを利用して、自然言語処理ではなく、競馬の血統をベクトル化したいと思います。
fastTextを利用して自然言語処理以外のことを行うというアイデアは、下記の記事に触発されたものです。
fastTextを使って単語じゃないものの分散表現を獲得する

実行結果

githubにベクトルファイルとhowto用のjupyter notebookファイルをアップロードしています。
github keiba_ketto_vec

作成方法

データセット

血統過去3代をfastTextのフォーマットに落とし込みます。

競走馬サトノダイヤモンドの場合の血統表です。

血統 馬名
サトノダイヤモンド
ディープインパクト
マルペンサ
父父 サンデーサイレンス
父母 ウインドインハーヘア
母父 Orpen
母母 Marsella
父父父 Halo
父父母 Wishing Well
父母父 Alzao
父母母 Burghclere
母父父 Lure
母父母 Bonita Francita
母母父 サザンヘイロー
母母母 Riviere

上記の表の馬名を半角スペース区切りで1行に変換します。
他の競走馬についても同様です。

input.csv
サトノダイヤモンド ディープインパクト マルペンサ サンデーサイレンス ウインドインハーヘア Orpen Marsella Halo WishingWell Alzao Burghclere Lure BonitaFrancita サザンヘイロー Riviere
サイモントルナーレ ゴールドアリュール ユーモレスク サンデーサイレンス ニキーヤ アフリート アリーウイン Halo WishingWell Nureyev ReluctantGuest Mr.Prospector PoliteLady Alydar FleetVictress
ウォータールルド ウォーターリーグ ウォーターエナン Dehere Solo BostonHarbor Scrape DeputyMinister SisterDot Halo MineOnly Capote HarborSprings Mr.Prospector File
...

ベクトル化

ベクトル化にはfasttextのskipgramコマンドを利用します。
実行すると、binファイルとvecファイルが生成されているはずです。

$fasttext skipgram -input input.csv -output ketto_model -minn 50
  • inputオプション インプットファイルを指定します
  • outputオプション アウトプットとなるモデル名を指定します
  • minnオプション 文字列を文字に分解する際の最小サイズを指定します。

minnオプションについてですが、fastTextの仕組みとして、スペース区切りした単語に加えて、単語ひとつひとつをさらに文字レベルで分解して分析を行っているようです。
fastTextの実装を見てみた

この機能は、例えば"ゴールドアリュール"と"ゴールドシップ"という馬を"ゴールド"で一括りにしてしまいます。
今回は、名前そのものには意味は無いので、機能を無効化するためにminnオプションを利用して文字レベルへの分解を防ぎます。

結果確認

gensimを利用してベクトル化したファイルを読み込み、ベクトル演算を実施してみます。

サトノダイヤモンドからベクトル演算を利用して、父違いの妹であるリナーテを算出できるかチェックします。

  • サトノダイヤモンド(父:ディープインパクト)
  • リナーテ(父:ステイゴールド)

ですので、

サトノダイヤモンド + ステイゴールド - ディープインパクト = リナーテ 

という式が成り立つはずです。

howto.py
import gensim

# gensimを使ってベクトルデータを読み込み
model = gensim.models.KeyedVectors.load_word2vec_format('ketto_model.vec', binary=False)

# most_similarメソッドを使って演算
# positiveに足し合わせるデータをリストで渡し、negativeに差し引くデータをリストで渡す。

model.most_similar(
    positive=["ステイゴールド", "サトノダイヤモンド"],
    negative=["ディープインパクト"]
)

gensimの利用方法については下記をチェックしてください。
gensim models.word2vec

result.
[('ポーレン', 0.8220623731613159),
 ('マルケッサ', 0.8190209865570068),
 ('マルペンサ', 0.814713716506958),
 ('リナーテ', 0.80884850025177),
 ('シャピーラ', 0.8080180287361145),
 ('ムーンライトナイト', 0.8041872382164001),
 ('Semplice', 0.7995823621749878),
 ('OnAir', 0.7940067648887634),
 ('フュージョンロック', 0.7933699488639832),
 ('Orpen', 0.7927322387695312)]

母父Orpen系のポーレンが一番似ているという結果になってしまいましたが、 母のマルペンサ、半妹のマルペンサ(父オルフェーヴル、父父ステイゴールド)につづき、 リナーテもしっかり演算結果に出現したので、ベクトル化は成功しているといえるのではないでしょうか。

以上。