u++の備忘録

csv化で文字列になったlistやdictを元に戻す

csv化すると文字列になってしまう仕様があるのでpickle形式などで保存するのが良いですが、諸般の事情でcsvで読み込む想定の場合の一つの解決策として、ast.literal_eval() *1*2 を使う方法を紹介します。

具体例

冒頭でastとpandasをimportします。astは標準ライブラリに含まれているので、pip installなどは不要です。

import ast
import pandas as pd

今回は次のような単純なDataFrameを題材にします。

df = pd.DataFrame({
    'sample': [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
})
df

f:id:upura:20190903190136p:plain

要素を確認すると、list型になっています。

df['sample'][0], type(df['sample'][0])
([1, 2, 3], list)

このDataFrameを一旦csvに吐き出した後に改めて読み込むと、要素は文字列型に変換されています。

df.to_csv('sample.csv', index=False)
df2 = pd.read_csv('sample.csv')
df2['sample'][0], type(df2['sample'][0])
('[1, 2, 3]', str)

文字列を split() して分割する方法もありますが、ここでは ast.literal_eval() を使うことで list 型から文字列型に変換しました。ast.literal_eval() はdict型への変換にも対応しています。

df2['sample_list'] = [ast.literal_eval(d) for d in df2['sample']]
df2

f:id:upura:20190903190804p:plain

df2['sample_list'][0], type(df2['sample_list'][0])
([1, 2, 3], list)

おわりに

本記事では、ast.literal_eval() を用いて csv 化で文字列になった list や dict を元に戻す方法を紹介しました。実装はNotebook形式でGitHubでも公開しています*3

Poincaré Embeddings でJ1リーグのチーム・選手を可視化

ふと「Poincaré Embeddings」*1で遊んでみたいと思い立ち、サッカーJ1リーグのデータで試してみました。

Poincaré Embeddings

Poincaré Embeddingsに関する説明は、ABEJA*2やscouty*3のブログに譲ります。

Poincaré Embeddings は端的に言うと word2vec の埋め込み先をユークリッド空間ではなく双曲空間にするという手法で、階層構造やべき分布をもつデータを埋め込むという問題設定において、低次元でもよい表現を与えられるという特徴があります。

Poincaré Embeddings による職種の類似度計算とその利用 - LAPRAS AI LAB

gensimでの実装とデータセット

Poincaré Embeddingsを手軽に試すに当たって、gensimでの実装*4が存在します。

この実装に渡すデータセットの形式は次の通りです。データセットはタプルを要素に持つリスト形式で、各タプルは('選手名', 'チーム名')のような関係性を持っています。

[('アウグスト ペドロ デ ソウザ', '鹿島アントラーズ'),
 ('アウグスト ペドロ デ ソウザ', '川崎フロンターレ'),
 ('アジエル アモリム', '湘南ベルマーレ'),
 ('アジエル アモリム', '浦和レッズ'),
 ('アデミウソン', '横浜F・マリノス'),
 ('アデミウソン', 'ガンバ大阪'),
 ('アドリアーノ フェヘイラ', 'セレッソ大阪'),
 ('アドリアーノ フェヘイラ', 'ガンバ大阪'),
 ('アマラオ', 'FC東京'),
 ('アマラオ', '湘南ベルマーレ')]

一般にこのような形式で存在しているデータセットはさほど多くなく、今回も全工程の中で特にデータセットを作る部分に苦心しました。

今回は「J1リーグの各チームの歴代所属選手の可視化」という題材に落とし込むことで、Poincaré Embeddingsに適した階層構造を持つデータセットを準備できました。収集には「FootballGEIST」*5を利用しています。具体的には、J1リーグ全18チームそれぞれの歴代所属選手のページからデータを収集し、gensimの実装に渡せる形式にpandasで加工しました。

Poincaré Embeddingsの学習

パッケージのインポートをします。ここでplotlyのバージョンが3以上だと可視化の際にエラーが出ます*6

from gensim.models.poincare import PoincareModel
from gensim.viz.poincare import poincare_2d_visualization
from IPython import display
from plotly.offline import init_notebook_mode, iplot
import pandas as pd
 
init_notebook_mode(connected=True)

環境に入っているバージョンを確認し、必要ならば2.7.0辺りのバージョンを入れておきます。

# import plotly
# plotly.__version__
# '2.7.0'

!pip install plotly==2.7.0

先の節で用意したデータセットを用いて、Poincaré Embeddingsの学習を実行します。今回の場合、epochsは2000回程度で十分なようでした。

model = PoincareModel(train_data, size=2, negative=8)
model.train(epochs=5000)

次のコードでplotlyでの可視化を実行できます。show_node_labelsにはラベルを付ける項目を指定します。

relations_set = set(train_data)
figure_title = ''
iplot(poincare_2d_visualization(
    model,
    relations_set,
    figure_title,
    num_nodes=0,
    show_node_labels=['浦和レッズ'] + urawa
))

可視化の結果は次の通りです。チーム名が中心に来て、円周側に選手が配置されています。 gensimの実装では正則化の影響で周囲にノードが集結しすぎないような工夫がなされているため、ある程度の数のノードが中心付近に残っている模様です*7

f:id:upura:20190901021328p:plain

浦和レッズ所属経験のある選手にラベルを付与したところ、大半は右下に固まりました。興味深いのは、チーム名としての「浦和レッズ」の近くに「サンフレッチェ広島」が配置されている点です。森脇・柏木・槙野・西川・闘莉王など、サンフレッチェ広島から浦和レッズに移籍した選手の影響ではないかと推察しています。

次の図では、インタラクティブにデータを閲覧できます。

ちなみに、次のtwitterの投稿の際は学習が不十分だった模様です。


活用方法

個々のベクトルの値は、次のような形で参照できます。

model.kv['浦和レッズ']
array([ 0.64337554, -0.49962734])

得られたベクトルの活用方法としては、例えば一定の括りで少数派に当たるデータをまとめ上げる、類似するデータを検索するなどの方法が考えられます。Kaggleなど教師あり学習の文脈では、カテゴリ変数の粒度が粗い場合などに、活用の余地があり得るかなと思いました。

おわりに

本記事では、Poincaré Embeddings でJ1リーグのチーム・選手を可視化しました。実装はNotebook形式でGitHubにて公開しています*8

データサイエンティストが活躍する系の映画をオススメしてもらった

私のtwitterでのしょうもない呟きに思いの外たくさんのリプライを頂戴したので、自分用の整理として推挙された映画をまとめます。広範な意味でのデータサイエンスを扱う映画も含まれていると思います。

既に観た映画

次の3作品は、既に私が鑑賞済の映画です。

マネーボール』(2011)


メジャーリーグの貧乏球団アスレチックスを、常勝軍団に作り変えた男ビリー・ビーン
野球の常識を覆し球史に刻まれた「マネーボール理論」が起こした奇跡。完全映画化!

『マネーボール』予告編 - YouTube

イミテーション・ゲーム/エニグマと天才数学者の秘密』(2014)


第2次世界大戦時、ドイツの世界最強の暗号エニグマを解き明かした天才数学者アラン・チューリングの波乱の人生を描いた伝記ドラマ。劣勢だったイギリスの勝利に貢献し、その後コンピューターの概念を創造し「人工知能の父」と呼ばれた英雄にもかかわらず、戦後悲劇の運命をたどったチューリングを、ベネディクト・カンバーバッチが熱演する。

映画『イミテーション・ゲーム/エニグマと天才数学者の秘密』予告編 - YouTube

アルキメデスの大戦』(2019)


1933年(昭和8年)、戦艦大和の建造をめぐる“机の上の大戦”が始まる。
これは、帝国海軍という巨大な権力に立ち向かい、数学で戦争を止めようとした男の物語。

映画『アルキメデスの大戦』予告【7月26日(金)公開】 - YouTube

ネタバレ要素を含みますが、以下に鑑賞録を記しています。

upura.hatenablog.com

オススメしてもらった映画

『NUMBERS 天才数学者の事件ファイル』(2005)

www.happyon.jp

犯罪者の行動を予測する公式を作る天才数学者の弟の協力で、さまざまな難しい犯罪を解明するFBIエージェントを描くドラマシリーズ。

NUMBERS 天才数学者の事件ファイル が見放題! | Hulu(フールー) 【お試し無料】

『ミッション: 8ミニッツ』(2011)


列車爆破事故の犯人を見つけるべく、犠牲者が死亡する8分前の意識に入り込み、爆破直前の列車内を追体験していく男の運命を描く。困難なミッションを課せられた主人公を、『ブロークバック・マウンテン』のジェイク・ギレンホールが熱演。巧妙に練り上げられたプロットと先の読めないストーリー展開に引き込まれる。

映画『ミッション:8ミニッツ』特報 - YouTube

エクス・マキナ』(2014)


人間と人工知能が繰り広げる駆け引きを、限られた登場人物と舞台設定や目を引くビジュアルで活写する。

映画『エクス・マキナ』予告編 - YouTube

『ドリーム』(2016)


2017年 第89回アカデミー賞3部門(作品賞、助演女優賞、脚色賞)ノミネート
宇宙開発史上の偉業を支え、新しい時代を切り開いた知られざる3人の女性がいた――

映画『ドリーム』予告A - YouTube

『メッセージ』(2016)


ある日突然、巨大飛行体が地球に。
その目的は不明―
未知なる飛行体が突如出現、彼らは人類に何を伝えようとしているのか?

映画『メッセージ』本予告編 - YouTube

『スノーデン』(2016)


米国最大の機密を暴いた男。彼は、英雄か。犯罪者か―。
元CIA職員エドワード・スノーデンの知られざる人物像に迫るヒューマンドラマ。

映画『スノーデン』 予告編 - YouTube

「Sports Analyst Meetup #4」を開催&LTしました #spoana

はじめに

「Sports Analyst Meetup #4」を開催しました。前回に引き続き、自分自身もLTで発表しました。

spoana.connpass.com

発表資料

発表資料はこちらにまとまっています(今後、資料が公開され次第追加していきます)。

spoana.connpass.com

togetter

Twitterの #spoana タグの付いた投稿は、下記にまとめています。

togetter.com

ロングトーク①高久侑也さん(株式会社Sportip)

  • Sportip*1のCEOを務める高久さんによるお話でした
  • Sportipは、競技者の動きや姿勢など個人のデータを収集・分析できるような仕組みを提供しています
  • デモも含めて、Sportipの取り組みやビジョンなどを語っていただきました

なお今回の会場は、Sportipが入居しているNTTドコモベンチャーズ*2にご提供いただきました。ありがとうございました。

ロングトーク② TKB84さん

  • 「スポーツ分析業界に飛び込んで半年で見えてきた、現状の概観と今後の展望」という題目での発表でした
  • スポーツ分析業界で実際に働き始めたTKB84さんに、ご自身の経歴や現状の分析、そして「今後どのような人材が求められるか」という展望などの生々しい話も含めて語っていただきました。

LT

今回もどれも面白い12本のLTが実施されました。LTの内容については、発表資料やtogetterをご参照ください。

自分の発表

私の発表は、以前にブログで公開した内容*3*4*5*6をまとめたものです。GitHub*7で実装も公開しています。

おわりに

第4回は数理系な話が多く、個人的な満足度が高い回となりました。また以前の登壇の反響をご共有くださった方や、自分の第3回での発表*8を追加検証してくださった方もいて、一定回数を重ねてきたことを実感した場面も多かったです。参加してくださった皆さま、ご登壇してくださった皆さま、会場をご提供いただいたSportipとNTTドコモベンチャーズの皆さまに、改めて御礼申し上げます。

テーブルデータ向けのGAN(TGAN)で、titanicのデータを増やす

はじめに

ynktk さんのツイート*1を見て、テーブルデータ向けの GAN の存在を知りました。本記事では、TGAN を用いて titanic のデータを拡張してみます。

TGANとは

テーブルデータに対応した GAN (Generative Adversarial Network, 敵対的生成ネットワーク) *2 です。数値などの連続変数だけではなく、カテゴリ変数にも対応しています。

Titanic のデータを増やす

今回は、著名なデータセットである Titanic のデータを対象にTGANを試します。

データの読み込み

まずはデータを読み込みます。データは Kaggle からダウンロードしました*3。下図のようなデータが格納されています。

df = pd.read_csv('input/train.csv')
df.head()

f:id:upura:20190820092059p:plain

行数は900弱です。

df.shape
(891, 12)

欠損値の削除

TGANは、欠損値に対応していません。最初に各カラムの欠損値の数を確認しておきます。

df.isnull().sum()
PassengerId      0
Survived         0
Pclass           0
Name             0
Sex              0
Age            177
SibSp            0
Parch            0
Ticket           0
Fare             0
Cabin          687
Embarked         2
dtype: int64

欠損値が大半を占める 'Cabin' は、この段階で削除します。合わせて、GANでの増幅に不適切な ['PassengerId', 'Name', 'Ticket'] も削除しておきます。

df.drop(['Cabin', 'PassengerId', 'Name', 'Ticket'], axis=1, inplace=True)

その他の欠損値を含む2カラムについて、'Age' は平均値を四捨五入して整数にした値、'Embarked' は最頻値である 'S' で埋めました。

df['Age'].fillna(round(df['Age'].mean(), 0), inplace=True)
df['Embarked'].fillna(df['Embarked'].value_counts().index[0], inplace=True)

カラム名の保持

現在 PyPI でインストールできる TGAN (ver 0.1.0) には、実行後に DataFrame のカラム名がインデックス番号に置換されてしまう不具合があります*4。そのため、実行後のために事前にカラム名を変数に入れて保持しておく必要があります。

df_columns = df.columns

連続変数の指定

TGAN の実行時には、連続変数のカラムのインデックス番号一覧をリスト型で渡します。今回は、次のように float 型のカラムを抽出しました。

continuous_columns = [df.columns.get_loc(c) for c in df.select_dtypes(include=['float']).columns]

TGAN の実行

いよいよ TGAN を実行します。

from tgan.model import TGANModel
tgan = TGANModel(continuous_columns, batch_size=50)
tgan.fit(df)

このときdocsには記載がありませんが、小さめのbatch_sizeを引数に指定しないと、tensorpack の assertion error *5で実行が止まってしまいます。

実行時間は、900弱のデータセットで15分程度でした。学習済のモデルは、次のように保存可能です。

model_path = 'output/models/mymodel.pkl'
tgan.save(model_path)

サンプルの抽出

学習済のモデルから、次のようにデータを生成できます。今回は、元のデータセットと同数を指定しました。

num_samples = len(df)
samples = tgan.sample(num_samples)

f:id:upura:20190819161132p:plain

'Age' が小数点以下になっているので丸める処理などは必要かもしれませんが、'Sex' などカテゴリ変数も含めてデータが生成できていると分かります。目的変数である 'Survived' も、問題なく増幅されていました。

samples['Survived'].value_counts()
0    540
1    310

おわりに

本記事では、TGAN を用いて titanic のデータを拡張してみました。Kaggle などの文脈で言うと、学習用データの水増しに利用できる可能性があります。ただし、ynktk さんとも議論した通り*6、GAN でまともなデータを作るにはそもそも十分量のデータセットが必要というジレンマがありそうです。

TGAN で増やしたデータで性能が向上するかはデータセットと課題設定次第ですが、機会があれば試してみても面白いなと思いました。

今回の実装は GitHub *7で公開しています。

*1:

*2:papers.nips.cc

*3:www.kaggle.com

*4:github.com

*5:github.com

*6:

*7:github.com

【ネタバレ有】『アルキメデスの大戦』とデータ分析の仕事

はじめに

OsciiArtさんの下記のツイートで興味を持ち、観に行きました。本記事では、ネタバレ要素を含みつつ、徒然と感想を書いていこうと思います。


あらすじ

事前の触れ込み通り、まさに「データ分析」を題材にした映画で、主人公の天才数学者・櫂直が旧態依然の組織の中で奮闘します。

時は1933年、海軍では巨大戦艦の建造を巡った対立が発生していました。来たる戦争に向けて華美な巨大戦艦を建造したい「建造推進派」と、新世代の海戦を見据え機動力のある航空母艦を推す「建造反対派」です。

旧態依然な文化が根強く残る海軍の中では「建造推進派」が優勢でした。2週間後の最終決定の会議までに状況を覆したい山本五十六ら「建造反対派」は、巨大戦艦の建造費が過剰に小さく見積もられていると感じた点に目をつけました。

山本五十六は天才数学者の主人公に、巨大戦艦の建造費の正確な見積もりを依頼。しかし、主人公は戦艦のドメイン知識もなく、今回建造予定の巨大戦艦の詳細情報もありません。そのような中で付け焼き刃ながらドメイン知識を身につけ、地道な情報収集も重ねていきます。

「建造推進派」による数々の妨害にも屈せず、巨大戦艦の建造費の予測値を算出した主人公。予測値を突きつけられた「建造推進派」の反応、そして巨大戦艦の建造の顛末は・・・?

(続きは劇場で)

感想

「データ分析の能力を見込まれた主人公」=「データサイエンティスト」と見立てて、感情移入してしまいました。まずは「データ分析で課題が解決できそう」という目論見で呼び出されるも、使えるデータがほとんどない状況。ドメイン知識もない主人公が自ら様々な場所に足を運んでデータを収集し、少しずつ知見を得ていく姿は胸を打つものがあります*1

そして「建造推進派」による数々の妨害。主人公がデータを手に入れられないような政治的な根回しや、最終決定の会議の前倒しなど、フィクションでありながら妙なリアリティのある出来事*2が巻き起こります。「突然やってきた素人の主人公が成功したら困る」という理由で、「建造反対派」であるはずの設計士が主人公に非協力的な部分も、実に人間味にあふれて生々しかったです。

最後に、巨大戦艦の建造費の予測値を突きつける際の描写です。詳細部分は映画の根幹になってしまうので割愛しますが、如何に正しくても数式を押し付けるだけでは聴衆に響かない点や、数式を超越する物事の存在など、多くを考えさせられるクライマックスでした。

おわりに

本記事では、ネタバレ要素を含みつつ『アルキメデスの大戦』の感想を書きました。普段はこの類いの感想ブログはあまり書かないのですが、予想以上に感銘を受けたので筆を執ってしまいました。いつの間にか大人1枚1900円になった映画*3ですが、たまに観るのは悪くないなあと改めて感じました。

*1:自分もKaggleのコンペで自ら猫カフェに行ったことを思い出しました

*2:私はここまで酷い話は噂レベルでしか聞いたことないですが

*3:eiga.com

scikit-learn-contrib の Metric Learning を試す

Metric Learning について

Metric Learning は、データの教師情報を基にデータ間の距離や類似度などの Metric を学習する手法です。日本語で手軽に読める記事だと、*1, *2 などが詳しいです。

このたび、phalanx さんの tweet *3で、 Metric Learning の基礎的なアルゴリズムのいくつかが scikit-learn-contrib *4に搭載されていると知りました。

本記事では、scikit-learn-contrib の metric-learn パッケージを用いて、簡単にMetric Learning を試します。

インストール

README や PyPI *5 に記載のある通り、次の通りにインストールします。

pip install metric-learn

利用するデータセット

今回は、sklearn に含まれている load_digits データセットを利用します*6。64次元の特徴量・0-9の10種類のラベルを持つ手書き数字のデータセットです。

f:id:upura:20190818182045p:plain
画像は*7より引用。

可視化(Metric Learning 前)

特徴量の可視化に当たっては、T-SNE *8 を用いて2次元への削減を行います。

次のコードは、metric-learn の docs に掲載されていた内容*9を、凡例を出すように一部改変しています。

def plot_tsne(X, y):
    plt.figure(figsize=(8, 6))
    
    # clean the figure
    plt.clf()

    tsne = TSNE()
    X_embedded = tsne.fit_transform(X)

    cmap = plt.get_cmap("tab10")
    for idx in range(10):
        plt.scatter(X_embedded[(y==idx), 0], X_embedded[(y==idx), 1], c=cmap(idx), label=idx)

    plt.legend()
    plt.xticks(())
    plt.yticks(())

    plt.show()

load_digits データセットをそのまま可視化したところ、下図のようになりました。大まかに分かれてはいますが、中央付近など少し煩雑になっていると分かります。

f:id:upura:20190818180214p:plain

可視化(Metric Learning 後)

次に、Metric Learning を実施します。

import metric_learn


# setting up LMNN
lmnn = metric_learn.LMNN(k=6, learn_rate=1e-6)

# fit the data!
lmnn.fit(X, y)

# transform our input space
X_lmnn = lmnn.transform(X)

いくつかのアルゴリズムが実装されていますが、ここでは Large Margin Nearest Neighbor (LMNN) を採用します。

Algorithms

  • Large Margin Nearest Neighbor (LMNN)
  • Information Theoretic Metric Learning (ITML)
  • Sparse Determinant Metric Learning (SDML)
  • Least Squares Metric Learning (LSML)
  • Neighborhood Components Analysis (NCA)
  • Local Fisher Discriminant Analysis (LFDA)
  • Relative Components Analysis (RCA)
  • Metric Learning for Kernel Regression (MLKR)
  • Mahalanobis Metric for Clustering (MMC)

READMEから引用。

Metric Learning 実施後の特徴量を可視化したところ、下図のようになりました。Metric Learning 実施前よりも、各クラスがハッキリと分かれているのが確認できます。

f:id:upura:20190818180230p:plain

今回は全データで学習し、全データに適用しています。Metric Learning はデータ間の距離や類似度などの Metric を学習しているので、学習に用いていないデータセットに適用することが可能です。

例えば Kaggle のような教師あり機械学習の文脈で利用する場合には、train データセットで Metric を学習し、test データセットにも適用することになるでしょう。分離に適した新しい特徴量空間を用いることで、より分類性能が高いモデルの構築が期待されます。

おわりに

本記事では、scikit-learn-contrib の metric-learn パッケージに搭載されている Metric Learning を試しました。なかなか使い所が難しい印象もある技術ではありますが、選択肢の一つとして持っておく価値は多分にあると感じています。

実装は notebook 形式で GitHub にて公開しています*10