滝沢カレンを作る技術

滝沢カレン(以下、敬称略)をご存知でしょうか。ファッション雑誌『JJ』専属モデルであり、モデルやタレントとして活躍されています。*1

そんな彼女の書く文章は非常に豊かな表現で構成されており、一部では純文学とも評されています。 近年、彼女の文章を対象とした言語処理的アプローチが盛んであり*2、例えば構文解析を行った例などがあります。

今回は、滝沢カレン言語モデル (Takizawa Karen Language Model; TKLM) を作成し、その言語モデルを用いて自動で滝沢カレンっぽい文章を生成するタスクに挑戦します。実装はTensorFlow(における高レベルAPIに該当するKeras)で行います。

データセット

Instagramの滝沢カレンの投稿の中で、7月7日(日)22時までに投稿されていた全582件のテキストだけを手動で収集しました。これは利用規約でスクレイピングなど自動で情報を取得する行為が禁止されているためです。

滝沢カレン言語モデル

文字ベース言語モデル

まずは最も単純な、文字ベースの言語モデルの実装です。Kerasのexampleにも存在します。

keras/lstm_text_generation.py at master · keras-team/keras · GitHub

文字ベースの言語モデルでは、系列長 seq_len 分の文字を入力して、その次に出現する文字を予測するモデルを学習します。入力  x とターゲット  t の関係を図示すると以下のようになります。

f:id:pompom168:20190714151923p:plain

モデルのアーキテクチャはKerasのexampleを踏襲し、単一のLSTMレイヤのモデルとします。こちらも図示すると以下のような構成になります。モデルの出力 y は学習データセットにおける語彙数次元のベクトルになり、Softmax関数に通すことで各文字の出現確率となります。

f:id:pompom168:20190714154611p:plain

コードで表すとこんな感じです。

model = tf.keras.models.Sequential()
model.add(tf.keras.layers.LSTM(hidden_size, input_shape=(seq_len, vocab_size)))
model.add(tf.keras.layers.Dense(vocab_size, activation='softmax'))

それでは実験してみましょう。

今回のデータセットにおいては、語彙数(ユニークな文字数)は2457となりました。seq_len は8とし、サンプルサイズ346890となりました。LSTMのユニット数は128としました。損失関数はターゲットとのクロスエントロピーとし、KerasデフォルトのRMSpropで最適化を行いました。またバッチサイズを128として、100epochまで学習しました。今回は検証用データでのearly stoppingなどは行っていません。

学習終了後、シードとなる文字系列を与えてそこから予測された確率が最も高い文字を抽出する作業を300回繰り返し、文章を生成させます。滝沢カレンのインスタグラムで良くあるパターンの「みなさん、こんにちは」から文章を始めたいので、シードは「みなさん、こんに」とします。

出来上がった文章の例を見てみましょう!

みなさん、こんにみなさん、こんにちは😊💕 本日は、な場をシャルにまであのはず自分と言わせてください🙇🏼💋 そして、見ないかもしればせたのうちに、うってがとはついてきただけだから#コメントをまつの服にでしょうか😅クリスマッリタしで顔を見られるようでしょう所✨😍 本日はなんと明日がないけれでした😢 そして、はゃてスタジオは歩くえ。その、まさかんだん、きて居間に来てくれれるかというの笑いなっちゃわざうな明日の そんなことで、なんならからつまりはどうでサイト」に出場します🌙 明日1時間ス#すりと言ってもを会いましょう💕💗💗💗💦💞💞 今夜でみなさま、当たり前j目のひと数がか、きっと一緒に目ではいいでしょうか🙋🏼✨ そんな、知っ

。。。意味が分からない文章なだけで、こんなものは全く滝沢カレンではありません。

早々に文字ベース言語モデルは諦めて、単語ベース言語モデルに進みます。

単語ベース言語モデル

文字ベース言語モデルでは、系列長 seq_len 分の文字を入力してその次に出現する文字を予測する問題でしたが、単語ベース言語モデルでは、系列長 seq_len 分の単語を入力してその次に出現する単語を予測する問題となります。

先ほどと同様に、入力  x とターゲット  t の関係を図示すると以下のようになります。基本的には文字が分かち書き後の単語に変わるだけです。

f:id:pompom168:20190714165344p:plain

モデルのアーキテクチャには少し変更を加えます。LSTMの前にEmbeddingレイヤを置き単語の分散表現をLSTMに入力するようにします。こちらも図示すると以下のような構成になります。

f:id:pompom168:20190714165358p:plain

こちらもコードで表すとこんな感じです。

model = tf.keras.models.Sequential()
model.add(
    tf.keras.layers.Embedding(
        input_dim=vocab_size, output_dim=embedding_size, mask_zero=True))
model.add(tf.keras.layers.LSTM(hidden_size))
model.add(tf.keras.layers.Dense(vocab_size, activation='softmax'))

それでは実験してみましょう。

分かち書きはMeCab + mecab-ipadic-NEologdで行いました。

語彙数は11448になりました。一般的には出現頻度が n 以下の単語は未知語として扱うなどの処理を行いますが今回は行いませんでした。出現頻度が1回だけの単語でも、”う〜んこれは滝沢カレンっぽいな〜”という単語が多かったので、その情報を失わないためです。ただし、数字は全て N に変換しています。seq_lenは4とし、サンプルサイズは191512となりました。embeddingの次元数は50としました。それ以外のハイパーパラメータなどは文字ベースと同じです。

こちらも学習終了後、シードとなる単語系列を与えてそこから予測された確率が最も高い単語を抽出する作業を300回繰り返し、文章を生成させます(ただし終端記号<eos>が出現したらそこで終了します)。シードは「みなさん、こんにちは❣」とします。

出来上がった文章の例を見てみましょう!

みなさん、こんにちは❣️毎日本当に来てますので、グイグイしないで待っていてくれたので、いいときなんかの意識も入っている、嬉しいですまだ何にもこんだろうにしました🌻🎉汗なりお腹にも出場できるので、今日からのご登場です👏👏👏👏はい、みなさまがおっしゃるとおり、石ころもどきの私と人間の気持ちもあり👱🏻‍♀️🌹を緊張な#私は幸せをくれる#でもそんなより素敵な曜日もしているんでしょう😌🌟#カレンのタンス#秋への準備次第

これは割ともう滝沢カレンじゃないでしょうか。特に 毎日本当に来てますので の所なんて滝沢カレンな気がします。また文末においてハッシュタグで文章を続けている所は完全に滝沢カレンです。

しかし、更なる滝沢カレン言語モデルを獲得するために、改良を続けていきます。

多層化

モデルの表現能力向上のため、LSTMレイヤを重ねることにします。また、多層にすることでパラメータ数が増えるので、過学習を防ぐためDropoutも導入します。

コードで表すとこんな感じです。Denseレイヤに接続するLSTMレイヤ以外は、上のLSTMレイヤに途中の時刻の出力も渡さなければならないので、 return_sequences=True とします。

model = tf.keras.models.Sequential()
model.add(
    tf.keras.layers.Embedding(
        input_dim=vocab_size, output_dim=embedding_size, mask_zero=True))
model.add(tf.keras.layers.Dropout(dropout_rate))
model.add(
    tf.keras.layers.LSTM(
        hidden_size, return_sequences=True, dropout=dropout_rate))
model.add(tf.keras.layers.LSTM(hidden_size, dropout=dropout_rate))
model.add(tf.keras.layers.Dense(vocab_size, activation='softmax'))

それでは実験してみましょう。 dropout率を0.5に設定した以外は、先ほどの条件と全く同じです。

出来上がった文章の例を見てみましょう!

みなさん、こんにちは❣️✨毎日jjします😖💋💡みなさん観てくださいね✋🏻❣️令和のさんまさんが突っ込むちゃんの海外ウェディングさに深さしました🤗😅いい桜好きでのお留守番しますが、ここまで汗に遊すぎです🙇🏼‍♀️✋🏻💎頭の上着をたくさん広げていたでしょう🌙🌟それもはいつも過ぎた炊飯器な中に私もやっぱり言い、人間や本意って)が寝るのですか❓そうにもての期待をお楽しみください。☺️.5名全てに、上手いの仲良しになっていますが今夜もこんにちは👋🏻👁人は、未来でではいても感じない人だっていいですが、それはちゃんとが着ませんな余ら並みには入りそうですが、万が一テレビを見るよりもちろん覚えたいから怖い#滝沢カレンよかってと優しく端っこを左右して頂き恥ずかしよ#明日遠足に何日言いようの頑丈でみなさんインスタを見せたい背は、ほとんど火曜日よりなにで映りますが、その岩にの続けるのサポートな隙間を私と取りでしまう天気を心配としてしまっましたから

う〜ん、何とか文章にはなってはいるけど、滝沢カレンっぽさというよりはただただ文法規則に則っていない文章になっている気がします。これ以外の文章を見ても主観ですが、ただただ単語を並べただけの文章になってしまっていました。今回のデータセットのボリューム的には多層化は必要ないのかもしれません。

Weight tying

Embeddingレイヤはvocab_size次元からembedding_size次元へのマッピング、Denseレイヤはhidden_size次元からvocab_size次元へのマッピングであるので、embedding_size = hidden_sizeであればEmbeddingレイヤの重みを転置することでDenseレイヤの重みとして使い回せます。この重みの共有で予測精度が向上することが報告されています。

arxiv.org

これを、単一のLSTMレイヤの場合に適用してみます。コードで表すとこんな感じです。

model = tf.keras.models.Sequential()
model.add(
    tf.keras.layers.Embedding(
        input_dim=vocab_size,
        output_dim=embedding_size,
        mask_zero=True,
        name='embedding'))
model.add(tf.keras.layers.LSTM(hidden_size))
model.add(
    tf.keras.layers.Lambda(lambda x: tf.keras.backend.dot(
        x, tf.keras.backend.transpose(model.get_layer('embedding').embeddings))
                           ))
model.add(tf.keras.layers.Activation('softmax'))

それでは実験してみましょう。embedding_sizeとhidden_sizeの両方を128にした以外は、これまでと条件は同じです。

出来上がった文章の例を見てみましょう!

みなさん、こんにちは❣️本日、「カレンのタンス」ということで観ていたとある国を着てしまったので、改築がんばります😅いにくい方や最近立ち見で座布団すら手にできない方、見事場所は何も見つけないんだ#自分が揺らぎたきゃそれでいい#道筋の支えはしていたかなんて分かりませんがいい感情にはない#しっかり一歩踏み出したくないんだ#人間が動いてるんだから

これはもはや滝沢カレンではないでしょうか。表面的には意味がわかりませんが、何となく応援されている気分になるところが個人的には滝沢カレンです。

ここまでで、一旦定量的な評価もしておきたいと思います。学習データとして収集してから新規に4つの投稿があったので、それをテストデータとしてPerplexityを計算したいと思います。

テストデータの語彙数は11448、サンプルサイズは2117となりました。評価した結果は以下です。

そもそもあり得ないほどPerplexityが大きいですが、そもそもこの評価の方法が妥当かどうかもありますので、一旦絶対的な値には目をつむりモデル間の相対的な値を見てみます。Perplexityが小さい順に、single-LSTM with tying、single-LSTM、Multilayer-LSTMとなっており、weight tyingを用いたものが最良となっていました。これは生成された文章の質を見た定性的な評価とも一致しています。

モデル Perplexity
single-LSTM 1192374
Multilayer-LSTM 1939050
single-LSTM with tying 1092815

バッチ型からシーケンシャル型へ

これまでは、一定の系列長の単語を与えて次に出現する単語を予測する問題を考えていました。しかし、ある単語を与えたときに次の単語を予測する問題も考えることができます。以下のページの説明に従うと、前者はバッチ型で後者はシーケンシャル型と呼ばれます。

medium.com

また上記ページの実験によると、バッチ型の性能はシーケンシャル型に基本的には劣るそうです。よって、シーケンシャル型のモデルを試してみます。

シーケンシャル型では単語が与えられると逐次予測を行うので、データの与え方は以下の図のようになります。

f:id:pompom168:20190714225132p:plain

モデルのアーキテクチャは以下のようになります。全ての時刻においてLSTMは次の時刻へ出力を渡すのと共に、自身の時刻の上位レイヤにも出力を渡し予測を行います。

f:id:pompom168:20190714225143p:plain

コードで表すとこんな感じです。LSTMレイヤが return_sequences=True となっていることが注目点です。これにより、最終時刻以外もLSTMレイヤが上位層に出力を渡します。また、weight tyingを使用せずDenseレイヤを続ける場合は、TimeDistributedレイヤでラップすることで時刻ごとに単語の予測確率を出力します。

model = tf.keras.models.Sequential()
model.add(
    tf.keras.layers.Embedding(
        input_dim=vocab_size,
        output_dim=embedding_size,
        mask_zero=True,
        name='embedding'))
model.add(tf.keras.layers.LSTM(hidden_size, return_sequences=True))
model.add(
    tf.keras.layers.Lambda(lambda x: tf.keras.backend.dot(
        x, tf.keras.backend.transpose(model.get_layer('embedding').embeddings)))
)
# weight tyingを使用しない場合
# model.add(
#     tf.keras.layers.TimeDistributed(
#         tf.keras.layers.Dense(vocab_size, activation="softmax")))
model.add(tf.keras.layers.Activation('softmax'))

それでは実験してみましょう。実験の条件はweight tyingの場合と同様です。

出来上がった文章の例を見てみましょう!

みなさん、こんにちは❣️みなさまに是非お昼休みも近いことですから、あふれんばかりの一回根強さを身にまとう、長澤まさみさんの御殿にいるのではなく、素なわたしが出場してます😋👍🏻❣️これは、ラグビーの選手の方々が来てください😌💞💞#前略大徳さん#カレンのタンス#私服#irene

長澤まさみさんの御殿にいるのではなく、素なわたしが出場しているそうです。これも滝沢カレンっぽいといえばっぽいです。また、傾向としては終端記号<eos>がかなりの頻度で出力されるようになり、文章が割と短くなっています。以下の例などです。(数字を表すNが含まれるN:Nが出現すると9:00に変換しています。)

みなさん、こんにちは❣️本日もお願いします😖💕🙋🏼そんな素晴らしい生き物にサンキュー‼︎

みなさん、こんにちは❣️本日9:00〜より、日本テレビさんにて、「火曜サプライズ」と出場します🌟🙌🏻#カレンの髪技

確かに文章としては、正しい文章が生成されるようになりました。しかし滝沢カレンっぽさが欠如してしまった気がしますし、文章の多様性が失われた気がします。

またテストデータに対するPerplexityも 1210703 と、単一LSTMのweight tyingの場合の方が良い結果となりました。よってこれまでの結論としては、シーケンシャル型よりバッチ型のweight tyingが最良の滝沢カレン言語モデルであるということにします。

まとめ

滝沢カレン言語モデルを作成して、自動で滝沢カレンっぽい文章を生成するタスクに挑戦しました。

ほぼ主観的な評価では、単一のLSTMレイヤかつweight tyingを用いたモデルが最良のモデルとなりました。

例えば上に記載した例以外にも以下のような文章を生成できます。

みなさん、こんにちは❣️お仕事の日は扱いやすいお時間を通してするようで歩くのではなく質問風貌の大きさを取ってくれた、海老つけ麺食べたかったですが、歩く頃にとっちゃ関係ないよね

今後の課題

  • 滝沢カレン言語モデルの評価方法
  • 生成結果を何らかの形で利用できるwebページ作成
  • これまでの方法を踏襲するなら、正解データに対しても単語の確率分布を考え予測された分布と正解の分布との距離を損失に加えた方法を試す

arxiv.org

  • その他言語モデルの改良テクニックを試してみたい(以下の論文とか?これ見とけみたいな論文あったら教えて欲しい)

arxiv.org

  • GANベースの文章生成方法の検証

補足

実験に使用したコードは以下のリポジトリにあります。

しかし、実験段階で未整理かつ非常に雑なので取扱い注意です。

github.com

【論文紹介】Deep Interest Network for Click-Through Rate Prediction

今回は、KDD 2018で発表されたCTR (Click Through Rate) 予測に関する論文 Deep Interest Network for Click-Through Rate Prediction を紹介したいと思います。CTR予測は、広義では注目している行動を起こす確率予測であるので、レコメンドに関する論文と捉えることもできます。なお、1週間前くらいに Machine learning papers reading pitch #3 というイベントで、ATRank というリコメンドの汎用的な方法論の論文について紹介しましたが(資料はこちら)、著者らの所属は同じAlibabaです。(ただ著者は全く違います)

なお自前で再実装したものは、以下にあります。

github.com

概要

  • ユーザーの過去の行動履歴と提示したい広告間の関連性を考慮して、ユーザーの興味・関心を表現するベクトルを適応的に算出する仕組みを提案
  • スパースな入力データから効率的に学習する方法を提案(こちらに関しては今回の紹介からは省いています)
  • Amazon・MovieLensのデータセット、Alibabaの実際のディスプレイ広告のログを用いたオフラインの実験における精度向上、Alibabaのディスプレイ広告システムにおけるオンラインのA/Bテストにおける効果向上を確認

従来手法

ここ数年では、CTR予測でもDeep Learningに基づく方法が提案されています。典型的なCTR予測は、以下のようなテーブルデータを対象としたクリックするかどうかの分類問題であるので、過去にこのブログでも紹介したEntity Embeddingような方法を用いてモデリング可能です。

f:id:pompom168:20190629214018p:plain

それでは特徴量として、ユーザーの行動履歴を使用する場合はどうでしょうか。例えば、行動履歴を広告のクリック履歴とすると以下のようなデータとなります。ユーザーの行動特徴量は可変長のリストです。

f:id:pompom168:20190629215745p:plain

行動履歴を扱っている研究、例えばYoutubeのリコメンドの研究 [Covington et al. 2016] では、以下のようなEntity Embeddingの拡張とも捉えられるネットワークを提案しています。(図は今回紹介しているDeep Interest Networkの論文から引用)

行動の数だけあるEmbeddingベクトルのリストに対して、要素ごとに和を取り1つのベクトルに変換します。そして、行動特徴量以外の特徴量に関するEmbeddingベクトルと結合して後は全結合層を続けます。ここまでが従来手法です。

f:id:pompom168:20190629221148p:plain

提案手法

従来手法の問題点

従来の [Covington et al. 2016] などの方法では、ユーザーの行動は固定長のベクトルに変換されます。表示したい広告に関係なくユーザーに対して固定のベクトルです。これでユーザーの行動からその興味・関心を表現するベクトルが作成されているでしょうか?

例えば、過去1週間で靴と水着を買ったユーザーにゴーグルの広告をリコメンドしてクリックされたとします。これは直感的には、”水着を買った”という行動に影響を受けてゴーグルの広告をクリックしたと考えられます。つまり、ユーザーの過去の行動の一部だけがその後のアクション(広告のクリック)に影響しているという仮説が立てられます。よって、ユーザーの行動を単一の固定長ベクトルに変換するだけでは、不十分であると考えられます。

Deep Interest Network

Attentionに着目し、表示したい候補の広告が与えられるとユーザーの過去の行動との関連性を考慮して、ユーザーの行動から興味・関心を表現するベクトルを適応的に算出します。ネットワークの構造は以下です。

f:id:pompom168:20190629223947p:plain

Source (key/value) がユーザーの行動履歴、Target (query) が候補の広告のAttentionを導入しています。これにより、広告ごとに過去の行動の中で注目する度合いを制御して、ユーザーと広告の組み合わせごとに興味・関心を表現するベクトルを生成できます。

以下に実例を示します。図中の矢印の上下にある画像が過去に購入した商品、矢印の右側の画像が候補の広告です。候補の広告が女性用のコートであり、過去の行動の中では女性用の服の重みが大きくなっていて、それ以外の重みが小さくなっています。

f:id:pompom168:20190629225134p:plain

LSTMなどでは駄目なのか?

ユーザーの行動履歴などの系列データを扱うなら、LSTMなどの方法がまず思い浮かぶでしょう。著者らも試してみたようですが、性能の向上は見られなかったようです。理由としては、自然言語処理のような文法規則の下にあるテキストデータなどとは違い、行動データはそういった制約があるわけでなくノイズだらけのデータと言えるからです。(これは自分の業務での経験とも一致しています。。)

定式化

最後に一応定式化もしておきます。

Feature Representation

特徴量  i のエンコードされたベクトルを  t_i \in R^{K_i} で表現します。ここで  K_i は特徴量  i の取り得る値の数です。

 t_i [ j ]  t_i  j 番目の要素であり、  t_i [ j ] \in \left\{ 0, 1 \right\} です。 \sum_{j=1}^{K_i} t_i [ j ] = k であり、one-hotエンコーディングのときは  k = 1 で、multi-hotエンコーディング(行動履歴特徴量のようなリスト)のときは  k > 1 です。

Embedding

特徴量  t_i に対するEmbeddingの辞書を  W^i = \left[ w_1^i, ..., w_j^i, ..., w_{K_i}^i \right] \in R ^{D×K_i} とします。ここで、  w_j^i \in R^D です。one-hotエンコーディングの場合、  t_i のEmbeddingは  e_i = w_j^i となります。multi-hotエンコーディングの場合、 t_i [ j ] = 1 となる  j \in \left\{ i_1, i_2, ..., i_k \right\} とすると、  t_i のEmbeddingは  \left\{ e _ {i_1}, e _ {i_2},..., e _ {i_k} \right\} = \left\{ w _ {i_1} ^i, w _ {i_2} ^i,..., w _ {i_k} ^i \right\} となります。

Pooling layer and Concat layer

行動の数がユーザーによって異なるので、multi-hotエンコーディングの  t_i における非ゼロの数が異なり、Embeddingのリストの長さはユーザーごとに可変です。全結合層に接続するために、行動履歴特徴量のEmbeddingのリストを固定長のベクトルに変換します。今回は、要素ごとに和をとるpoolingレイヤを介して固定長のベクトルに変換します。

 e _ i = pooling (e _ {i_1}, e _ {i_2}, ..., e _ {i_k} )

最後に、  e _ i とone-hotエンコーディングの  t_i に対するEmbeddingを結合して、1つの密ベクトルを得ます。

MLP

Poolingと結合によって得た密ベクトルを入力として、全結合層を続けます。

Loss

負の対数尤度を目的関数とします。

 \displaystyle L =  - \frac{1}{N} \sum_{(x, y) \in S} (y \log p(x) + (1 - y) \log (1 - p(x)))

 N は学習データセットのサンプルサイズ、  S は学習データセットの集合、  x は入力特徴量、  y \in \left\{  0, 1 \right\} は正解ラベル、  p(x) はソフトマックス関数に通した後のネットワークの出力でありクリックされる確率です。

Local activation unit

Lossまでが従来のネットワークの定式化でした。

Deep Interest Networkでは、表示したい広告に応じてユーザーの行動履歴から興味・関心を表すベクトルを適応的に算出します。候補の広告  A が与えられると、ユーザーの興味・関心ベクトルを  \nu_U は以下のように計算されます。

 \nu_U (A) = f (\nu_A, e_1, e_2, ..., e_H) = \sum_{j=1}^{H} a(e_j, \nu_A) e_j

ここで、  e_1, e_2, ..., e_H はユーザー  U の行動履歴特徴量のEmbeddingのリストで、  \nu_A は広告  A のEmbeddingです。  a はAttention weightを出力する順伝搬のネットワークです(Deep Interest Networkの図における右上のActivation weightを出力しているネットワーク)。そして、  a によって出力されるAttention weightとEmbeddingの積によって得られるベクトルを、行動履歴に渡って和をとることでユーザー興味・関心を表現するベクトルを広告ごとに適応的に獲得します。

実験

データセット

公開されているAmazonとMovieLensのデータセット、非公開のAlibanaのディスプレイ広告システムのログで実験を行っています。例えばAmazonのデータセットはユーザーの商品のレビューデータであり、特徴量としては商品ID、カテゴリID、過去にレビューした商品IDのリスト、カテゴリIDのリストがあります。過去の行動として、 (b_1, b_2,...,b_k,...,b_n) があったとすると、k個の行動からk+1個目の行動を予測する問題です。n-2個までの行動を学習データセットとして、テストではn-1個までの行動が与えられたとき最後の行動を予測します。

f:id:pompom168:20190630000634p:plain

評価値

2つの評価値を用いています。

1つは以下の式で計算されるユーザーごとのAUCの平均です。nはユーザーの数、#impressionは広告の表示回数なのですが、普通にユーザーごとのサンプルサイズです。

f:id:pompom168:20190630001141p:plain

もう1つは、RelaImprというベースラインに対する相対的な改善度合いを表す指標です。今回のベースラインは、従来手法などで紹介した [Covington et al. 2016] などのモデリングです。

f:id:pompom168:20190630001039p:plain

結果

比較手法やハイパーパラメータなどは論文を参照ください。

上の画像がAmazonとMovieLensでの結果、下の画像がAlibabaのログの結果です。両者ともDINというのが今回紹介した方法の結果です。DINにwithがついているものは、今回の記事では紹介しなかった学習方法の工夫も行った結果を表しています。

全てのデータセットにおいて、提案しているDINが最も評価値が良くなっています。AmazonとMovieLensでは、Amazonの方が改善幅が大きくなっていますが、これはAmazonの方が行動データが豊富であり、こういった豊富な行動データがある場合は顕著に提案手法が有効であることが述べられています。

f:id:pompom168:20190630001823p:plain

f:id:pompom168:20190630001833p:plain

またオンラインのA/Bテストにおいても、最大でCTRが10%、収益が3.8%向上したそうです。地味に一番の驚きはこのモデルを実際に運用していることであり、ユーザーのアクセスごとに数百単位の広告に対して10ms以内の予測を行っているそうです。こういったことが出来るということが、Alibabaの強さのような気がします。。。

まとめ

Attentionを用いて、ユーザーの興味・関心を表現するベクトルを表示したい広告に応じて適応的に算出する仕組みを提案した、Deep Interest Networkの論文を紹介しました。また、自前での実装も行い公開しました。

参考文献

[Covington et al. 2016] Paul Covington, Jay Adams, and Emre Sargin. Deep neural networks for youtube recommendations. In Proceedings of the 10th ACM Conference on Recommender Systems. ACM, 191–198, 2016

LDAを用いたカテゴリ変数からの特徴抽出

kaggleのTalkingData AdTracking Fraud Detection Challengeで1位になったチームの解法の1つである、トピックモデルを用いたカテゴリからの特徴抽出を試してみたので紹介します。

Pythonでの実装はこちらです。

github.com

概要

参考にしたのは、kaggleでの解説リクルートコミュニケーションズさんのブログでの解説です。

やろうとしていることは、トピックモデルを考えて2つのカテゴリ変数の共起度から潜在トピックを推定し、それを新規の特徴量として用いるということです。

トピックを抽出する方法として、LDA (Latent Dirichlet Allocation) / NMF (Non-negative Matrix Factorization) / LSA (Latent Semantic Analysis) が使われていましたが、最終的にはLDAが使われていたっぽいので、今回はLDAを使います。

実際にやっていることは、上でも記載したリクルートコミュニケーションズさんのブログを見ていただければ早いです。

文章の例を挙げると、LDAでは文章の生成過程の背後には潜在的なトピックが存在し、ある文章にはそのトピックごとの重み(トピックの確率分布)が存在し、トピックには単語ごとの重み(単語の確率分布)が存在し、それらの確率分布から文章が生成されると考えます。(正確にはトピック自体もディレクリ分布という分布から生成されることを仮定しています。)

つまり、それぞれの文章がトピックの重みを持つので、トピックの重みを特徴量として使用できます。

このLDAをカテゴリ変数に適用することを考えます。

例えばカテゴリ変数として性別と都道府県があったとして、データセット内で性別ごとにどの都道府県と共に出現したかをカウントした共起行列を作成します。

つまり、性別⇔文章、都道府県⇔単語に対応させているわけです。

この共起行列にLDAを適用することで、性別と都道府県間の潜在トピックを推定することができ、性別の各値をトピックの重みの特徴量で表現できます。(トピック数を5とすると5次元の特徴量)

また、文章・単語の対応関係を入れ替えることで、同じ特徴量のペアからそれぞれ特徴量を抽出できます。(上の例だと性別を文章とみなすのか、都道府県を文章とみなすのか)

kaggleの例だと、 (ip, app, device, os, channel) という5つのカテゴリ変数から全ての組み合わせ5×4 = 20 で、トピック数を全て5にして、100次元の特徴量を作成しています。

実験

Criteoが公開しているクリック予測のデータセットを使用します。

10万件だけにサンプリングしている簡易データを以下からダウンロードできます。

labs.criteo.com

各カラムの具体的な意味は伏せられていますが、最初のカラムが正解ラベル(クリックしたかどうか)、それに続く13個のカラムが連続変数、それに続く26個のカラムがカテゴリ変数です。

各行は時系列順に並んでいるので、全データの中で後ろの2割をテストデータとして用いました。

学習データ・テストデータ共に全サンプルにおける正例の割合は約23%となっていました。

比較のため、クリック予測界隈で良く用いられているfeature hashingでも実験を行いました。

LDAによる特徴抽出を行うクラスを以下に実装しています。

https://github.com/kishimoto-banana/handle-categorical-feature/blob/master/lib/topic_model.py

結果

feature hashingによるエンコードする特徴量の次元数は128としました。

LDAによる特徴抽出では、データセットにカテゴリ変数が多いので、各カテゴリ変数に対して自身を除くカテゴリ変数から3つを選択し、それらのペアに対して特徴抽出を行うようにしました。

またトピック数を3としたので、LADによる特徴抽出の次元数は26×3×3 = 234次元となります。

評価値は同じデータセットを用いたkaggleのコンペで使用されていた、LogLossを用いました。LogLossについては前回の記事を参照ください。

結果は以下のようになりました。

方法 LogLoss
feature hashing 0.647
LDA 0.645

若干、feature hashingよりもLDAの方がLogLossが小さくなりましたが、正直コンペで1位となったほどの性能は引き出せてない気がします。

もう少し真面目にハイパーパラメータチューニングをやるべきかもしれませんが、今回はここまでで紹介に留めます。

機械学習の実用的な評価値チートシート

機械学習の評価値として、Accuracy/Precision/Recall/F1などが教科書にも載っており、最も有名な評価値だと思います。

ただ実産業への応用において、これらの評価値では正しくモデルの性能を評価できないことが多く、多くの機械学習エンジニアやデータサイエンティストにとって頭を悩ますポイントだと思います。

これまで自分なりに調査したり試行錯誤したりする中で、大体これでいいだろというものが何となく固まってきたので、備忘録のためにチートシート的なものにしました。

機械学習モデルの評価をする際の一助になれば幸いです。

分類問題では二値分類問題のみを対象としています。

f:id:pompom168:20190410234352p:plain

RMSE (Root Mean Squared Error)

回帰問題の場合は基本的にRMSEを使えば良いと思います。

RMSEは予測値と目的変数の値の二乗誤差の平均値に平方根をとったものです。

これは非常に分かりやすく、予測値が平均的にどれくらい外れているかを評価できるものですね。

AUC (Area Under the Curve)

一般的なROC曲線(正例と予測するしきい値を変化させたときのTPRとFPRをプロットした曲線)の面積ですね。

完全に正確に予測できた場合は1、ランダムに予測したときは0.5になります。

最初に紹介したAccuracy/Precision/Recall/F1などは、しきい値によって変動する値ですが、AUCはしきい値による変動がないので、分類性能自体を見たい場合はこのAUCを見れば良いと思います。

ただし欠点があり、正例に対して負例の数が圧倒的に多い不均衡データの場合は、ROC曲線のAUCでは複数のモデルを比較が上手くできません。

不均衡データの場合は、PR曲線のAUC (=AP) を使うべきです。

この辺の説明は、以下の記事などが詳しいです。

aotamasaki.hatenablog.com

qiita.com

AP (Average Precision)

PR曲線(Recallを横軸に、Precisionを縦軸にとってしきい値を変化させてプロットした曲線)の面積です。

これは、AP (Average Precision) と呼ばれる値と等価であり、不均衡データの分類性能についてはこの評価値を見ればよいです。

計算方法などは以下の記事を参照ください。

レコメンドつれづれ ~第3回 レコメンド精度の評価方法を学ぶ~ - Platinum Data Blog by BrainPad

LogLoss

ここからは、分類問題であっても予測確率が重要な場合です。

例えば、Webのディスプレイ広告などではその広告が表示された場合クリックされる確率(Click Through Rate; CTR)の予測が重要であるなど、割と実産業においてはここらの評価値が重要だと思います。

LogLoss (別名、クロスエントロピー) は、正解ラベルと予測値との距離を考慮した評価値であり、値は小さいほど良いです。

予測確率を p_i 、正解ラベルを y_i \in \{ 0,1 \} とすると式で表されます。

LogLoss = - \frac{1}{N} \sum_{i=1}^{N} y_i log (p_i) + (1 - y_i) log (1 - p_i)

注意が必要なのは絶対的な値には意味がなく、相対的な評価しかできないことです。

つまり同一のテストデータ間でしか比較が出来ません。

Normalized Entropy

一般的な評価値ではないですが、予測確率が重要な場合であって不均衡データの場合はこの評価値が良いと思います。

以下のFacebookのCTR予測の論文において、この評価値が用いられています。

https://quinonero.net/Publications/predicting-clicks-facebook.pdf

この評価値は正規化されたLogLossです。(Normalized LogLossと呼んだ方がいいかもしれませんが、論文に従ってNormalized Entropyと呼んでおきます)

全テストサンプルが、全サンプルの内の正例の割合の値で予測されたときのエントロピーでLogLossを正規化します。

正規化する理由は、正例の割合が0 or 1に近いほどLogLossを小さくすることが簡単であり、この影響を低減するためです。

これも小さいほど良く、絶対的な値には意味がありません。

正例の割合を p とすると、以下の式で表されます。

Normalized Entropy = \frac{LogLoss}{- (p * log (p) + (1-p) * log (1-p) )} = \frac{- \frac{1}{N} \sum_{i=1}^{N}  y_i log (p_i) + (1 - y_i) log (1 - p_i)}{- (p * log (p) + (1-p) * log (1-p) )}

カテゴリ変数の分散表現を学習するEntity Embeddingの実装

機械学習においてカテゴリ変数を扱うとき、何らかの変換を施して任意の数値で表現しなければなりません。

今回はWord2Vecのように任意のカテゴリ変数の分散表現を学習する、Entity Embeddingの紹介とそのPythonの実装をライブラリとして公開したので紹介します。

実装はこちらです。

github.com

実はEmbeddingレイヤというものを知ったときに、Entity Embeddingと同じ方法を思いついてCategory2Vecなどという名前で自分では呼んでいたのですが、普通に既に提案されていて、まあそりゃ誰でも思いつくよなと思った次第です。

Entity Embedding

Entity EmbeddingはkaggleのRossmann Store Salesという店舗の売上を予測するコンペで3位になったチームが提案して使用した方法で、論文にもなっています。

arxiv.org

カテゴリ変数を扱う際色々な方法はあるものの、基本はOne-hot encodingを行い1つのカテゴリ変数を取りうる値の次元数のベクトルで、その値に対応するインデックスのみ1となりその他は0となるように数値に変換します。

ただこの場合、各特徴量はその値であるか・その値でないかの情報しか持たず、次元数も取りうる値に従って大きくなるので計算量の問題も出てきます。

この問題をWord2Vecなどでも使われているEmbeddingレイヤを用いて解決したのが、Entity Embeddingです。(と私は解釈しています)

そもそもEmbeddingレイヤとは何でしょうか?

Embeddingレイヤ

ゼロから作るDeep Learning ❷ ―自然言語処理編』の説明が分かりやすいです。

ニューラルネットワークの入力をOne-hotベクトルだとしてその次元数を10万とすると、10万次元のベクトルと(10万×中間層のユニット数)の行列の積を通常は計算します。

ただ実際やってることは、値が1となっているインデックスに対応する行を抜き出しているだけです。(以下の図の赤枠で囲った部分)

f:id:pompom168:20190324234328p:plain

よって特定のインデックスに対応する行(ベクトルを)抜き出すレイヤをEmbeddingレイヤと呼び、このレイヤを使って任意のネットワークを学習します。

Word2Vecの慣習に従えば、このEmbeddingレイヤの各行が各カテゴリ変数の値の分散表現です。

以下の図が、提案されている論文に載っているEntity Embeddingを表す図です。

上で説明したことをそのまま図にしただけで、各カテゴリ変数のOne-hotベクトルに対してEmbeddingレイヤを接続し、それを全カテゴリ変数分結合したものを入力として普通に全結合ニューラルネットワークを構成します。

そして、任意の目的変数に応じて学習を行えば、Embeddingレイヤの重みが分散表現となるわけです。

f:id:pompom168:20190324235255p:plain

このネットワーク自体は分散表現を入力としたニューラルネットワークなわけなので、これ自体を予測器として用いてもいいですし、学習した分散表現を別の任意の予測器の入力として再学習を行っても良いわけです。

(ただし、Entity Embeddingと別の予測器の学習データを完全に同一のものにすると超絶過学習を起こすので、そこは学習データを分けるなどの工夫が必要です。)

またEntity Embeddingによって得た分散表現を用いて、カテゴリ変数の各値同士の近さを可視化できます。

以下はドイツの州の各値の分散表現同士の近さを可視化したもので、各州の相対的な距離が実際の地図と似たものになったそうです。

f:id:pompom168:20190324235441p:plain

ライブラリ

このカテゴリ変数を扱う画期的な方法であるEntity Embeddingを簡単に扱うために、ライブラリaltenaとして公開したので紹介します。

PyPIにアップロードしてあるので、pipでインストール可能です。

$ pip install altena

まだドキュメントの整備が済んでいないので、使い方は上記リポジトリのexampes/以下を参照してください。

現段階では、任意のネットワーク構成におけるEntity Embeddingの学習と、学習したモデルを使った分散表現への変換に対応しています。

また、その他のカテゴリ変数から特徴抽出する方法についても実装予定です。

実際にEntity Embeddingを使った結果なども追記していと思ってます。

(今まで社内のデータで使っていたため、公開されてるデータセットでやったことがなかったです)

Hugoでポートフォリオサイトみたいなものを作ってGitHub Pagesで公開した

最近アウトプットを増やしたいという思いがあり、とりあえず形からでもと思ってポートフォリオサイトっぽいものを作ろうと思いました。

どうやって作ろうかと思っていた中で、何となく知っていたHugoとかSphinxみたいな単語で調べてみると、そもそも静的サイトジェネレータというものがあることを知りました。

この辺の説明は以下のスライドが分かりやすかったです。

slideship.com

最近はほぼPythonばかり触っていることもあり、普通ならSphinxとかを選択するかもしれませんが、さすがにPythonばかりだとな、、、という思いがあり今回はHugoを選択しました。特に深い理由はありません。

ちなみに静的サイトジェネレータのメリットとしてはHTMLとかを書く必要がないことのようですが、テンプレート的なものを自分の用途に変えようと思うと普通にHTMLを書く必要がありました。この辺はもっと良いやり方があったのかもしれません。

作成したサイトはGitHub Pagesで公開しました。

GitHub PagesはGitHubが提供している静的サイトのホスティングサービスです。

今回作成したサイトは以下のものです。

kishimoto-banana.github.io

Hugoの使い方

基本的にはHugoのquick startを参照しました。

gohugo.io

Hugoのインストール

MacOSであればHomebrewでインストールできます。

brew install hugo

ウェブサイトを作成する

以下のコマンドで my_site という名前で新しいHugoのプロジェクトを作成します。

hugo new site my_site

Gitリポジトリの作成・テーマの追加

https://themes.gohugo.io/ から使いたいテーマを選択し、submoduleとして追加します。今回はGoaというテーマを選択しました。

cd my_site
git init
git submodule add https://github.com/shenoybr/hugo-goa.git themes/hugo-goa

テーマを適用

実際にテーマを適用してサイトを作成します。 ここからのやり方は複数あるような気がしますが、今回はテーマのディレクトリ以下にあるディレクトリをプロジェクト直下のディレクトリにコピーして適用します。

Goaの場合、具体的には themas/hugo-goa/ 以下から layouts を、 themas/hugo-goa/exampleSite 以下から contentstaticdataconfig.toml をコピーします。

ここでHugoサーバを起動します。

hugo server

http://localhost:1313/ にアクセスすれば、テーマのデモサイトと同じ内容が表示されるはずです。

カスタマイズ

基本的な変更は、プロジェクトディレクトリ直下の config.toml を変更します。

ただしサイトの構成を変えるような変更は、layouts 直下のHTMLファイルを修正しなければなりません。

例えば今回採用したテーマのGoaは、トップページから自己紹介ページやブログページに遷移する構成でしたが、自分はトップページに経歴などの内容を全て記載したかったので、 layouts 以下にHTMLファイルを追加しました。

GitHub Pagesでホスティング

こちらも基本的には以下のHugoのドキュメントを参照しました。 gohugo.io

場合によって2つの方法があるようです。

https://<username>.github.io/ というURL、もしくはhttps://<username>.github.io/<projectname> といったように任意のプロジェクトのURLの場合です。

今回は前者の場合です。

2つのリポジトリを作成

  • ソース管理のための任意のリポジトリ。今回は my-site とする。
  • 公開するサイトのファイルを置くためのリポジトリ。リポジトリ名は <username>.github.io とする。

ソース管理リポジトリをcloneする

git clone <my-siteリポジトリのURL>

作成したHugoプロジェクトのコピー

cloneした my-site に Hugoプロジェクトの my_site 以下をコピーする。

cp -p -f -R my_site/* my-site/

ローカルで動作確認

my-site ディレクトリに移動して、再度ローカルにてHugoサーバを起動して表示内容を確認します。

cd my-site
hugo server

http://localhost:1313/ にアクセスして内容を確認します。

問題なければ Ctrl+C で停止します。

publicディレクトリを公開先リポジトリとしてサブモジュール化

publicディレクトリを削除して、公開先リポジトリとしてサブモジュール化します。

rm -rf public
git submodule add -b master https://github.com/<username>/<username>.github.io.git public

リモートリポジトリにpush

ドキュメントに載っていたシェルを叩くだけです。

#!/bin/bash

echo -e "\033[0;32mDeploying updates to GitHub...\033[0m"

# Build the project.
hugo # if using a theme, replace with `hugo -t <YOURTHEME>`

# Go To Public folder
cd public
# Add changes to git.
git add .

# Commit changes.
msg="rebuilding site `date`"
if [ $# -eq 1 ]
  then msg="$1"
fi
git commit -m "$msg"

# Push source and build repos.
git push origin master

# Come Back up to the Project Root
cd ..

後は https://<username>.github.io/ にアクセスすれば自分のサイトが公開されています。

PyData.tokyo One-day Conference 2018に参加した

PyData.tokyo One-day Conference 2018に参加したので、メモ書きを記載する。

pydatatokyo.connpass.com

なお自分の記憶のためのメモなので、内容の正確さは保証できません。

PyData.Tokyo

データ分析のための Python パフォーマンスチューニングテクニック @atsuoishimoto

PythonとNumPyの歴史

  • Pythonは科学技術計算に昔から強い(IEEEでも紹介)
  • 1995年くらいにはNumPyの前進numericがあった
  • NumPy
    • NumPyが科学計算ライブラリエコシステムの核
    • C拡張ライブラリの作成簡単さ
    • 元々NumPyはPythonの標準ライブラリとして開発
    • NumPyのellipsis
    • NumPyのndarray(Cでの構造体)はPythonのデータ交換用として登録

分析処理の平行処理

  • マルチプロセス、マルチスレッドにして高速化したいんですけど、、、
    • 実際はそんなに早くならない
    • 計算量・データ量の削減が基本
  • concrurrent.futures
    • threadingやmultiprocessよりこっち使うべき
  • マルチスレッド vs マルチプロセス
    • データ処理であれば可能ならマルチスレッドで
    • GIL (Global Interpreter Lock)
      • Pythonインタプリタは複数のスレッドを同時に実行できない
      • GILを持つスレッドだけが処理を実行可
      • GILは定期的な開放(5ms)以外に、自発的に開放できる
      • Pythonの処理以外(printとかのosの機能など)はGIL必要ない
    • つまり、Python以外の機能の部分をGILを持つスレッド以外で実行すれば良いよ
  • NumPyの場合のマルチスレッド
    • numpy.add()
      • マルチスレッドでもマルチプロセスでも対して高速化できない
      • 最近のCPUであれば行列の足し算は高速
      • マルチスレッド、マルチプロセス化のオーバヘッドの方が基本的に大きい
      • 4スレッド/プロセス程度くらいまではパフォーマンス向上
      • NumPyはGILを解放するので、マルチスレッド有効
    • numpy.dot()
      • マルチスレッド/プロセスで遅くなること多々
      • 内部のライブラリ(BLAS)などで並列化してくれているので、余計な処理は邪魔
  • GPUの場合
    • CPUとGPUの通信のストリーム
      • GPUの演算が終了する前に次の演算を開始
      • GPUに演算は非同期に実行
      • データ転送は同期的に実行
      • ストリームを活用できれば、演算中に次の演算のデータ転送
    • Nvidia Visual Profiler
  • マルチGPUの場合
    • それぞれのGPUは別々のストリームで通信

PythonによるWikipediaを活用した自然言語処理 @山田育矢 (Studio Ousia)

参考

前置き

  • Wikipediaを自然言語処理に使うと良い
    • テキストコーパスとして
    • 記事の見出し語(エンティティ)を辞書として活用
    • エンティティのリダイレクト構造
    • リンク

Wikipediaをテキストコーパスとして使う

  • DBpediaプロジェクトがテキストやリンクなどをRDF形式で配布
    • wikiマークアップの除去を行わなくていい
    • dumpしたやつよりこっちの方が良い
  • word2vecで簡単に単語表現学習できるよ

WIkipediaから表記ゆれしやすい文字ペアを抽出

  • wikipediaリダイレクト
    • エンティティにアクセスする際のある程度の表記揺れを解消する機能
    • 日本語で67万件
    • 同義語辞書として使用できる
  • wikipediaのリダイレクトを全て集計
  • レーベンシュタイン距離が1のエンティティペアを抽出
  • 異なる文字のペアをカウント
    • カタカナ、漢字、記号などの表記ゆれ
    • 旧字体と新字体とかも
  • wikipedia2vec

wikipediaエンティティ辞書を活用する

  • アップルが”アップル(企業)”なのか”リンゴ”なのか
  • 記事に出現するリンクについて、リンクテキスト(エンティティ名)とリンク先(エンティティ)を記録して集計
    • 専門用語や固有名詞を含む辞書
  • エンティティ辞書の2つの指標
    • リンク確率
    • コモンネス
  • 利用例1(単語・フレーズ辞書)
      - エンティティ名で辞書作成
      - リンク確率でエンティティ名フィルタリング
    
  • 利用例2(エンティティリンキング)
      - ある文章から辞書に含まれるエンティティ名を抜き出して、該当するwikipediaのエンティティを抽出
      - wikipediaの情報を用いたタグ付け
      - テキスト分類にエンティティを用いる
    

wikipediaから単語とエンティティのベクトル表現を学習する

  • word2vecを拡張、単語+wikipediaエンティティを同一空間にマップ
    • エンティティは曖昧性や表記ゆれがない
    • wikipediaに含まれる世界の物事の知識を自然にモデル化
    • 手法の詳しいことはGithubとかみて
  • 高速化
    • 実装の大半をCythonを用いてCに変換
    • skip-gramでは、学習対象の単語やエンティティのペアからベクトルを更新する処理を高速に行う必要
  • 可視化
    • TensorBoardのProjector

wikipedia2vevとエンティティリンキングで早押しクイズAI

  • Quiz bowl
    • 質問が与えられて、文が説明しているwikiのエンティティを予測
  • クイズをテキスト分類で解く
    • 文章の単語と出現したエンティティ
    • エンティティは、質問文からエンティティリンギングで抽出

Pythonでマテリアルズ・インフォマティクス ~機械学習と物性・材料の融合領域~ @福島 真太朗 (TOYOTA)

Materials Informaticsの存在

  • 物性・材料とデータ科学の融合領域
  • 従来
    • 材料を作って物性値を調べる
  • Materials Informatics
    • 欲しい物性を持つ材料を見つける
  • 歴史
    • 2011年にアメリカでオバマ政権が始めた
    • 日本でも2015年にNIMSがプロジェクト開始
  • 成果例
    • 富士通・理研
    • TDK・京都大学
    • NEC
  • NIPSでもワークショップ

Materials Informaticsの基礎

  • 分子の文字列表現
    • SMILES
  • 記述子
    • つまり特徴量
    • 材料から記述子Xを定義
    • 材料Xと物性Yの関係を Y=f(X)
  • Fingerprint
    • Extended Connectivity FingerPrint (ECFP)

PythonでMaterials Informatics

  • RDkitライブラリ
    • 大部分はC++で実装
    • https://www.rdkit.org/
    • Chem.MolFromSmiles(smiles)
    • 記述子の計算
      • 多くの方法が用意
  • 具体例
    • 溶解度予測(物質の解けやすさ)
    • 記述子を入力として溶解度を予測する問題

最近の動向

  • 分子生成
  • そもそもGAN
    • Generatorが人工データ生成
      • 生成モデル
    • Discriminatorがデータの真偽を識別
  • SeqGAN
    • 分子のsmilesで次に来る記号に利得が最大になるように分子を生成
  • ORGAN
    • SeqGANでsmiles生成、強化学習で評価
  • MolGAN
    • 分子をグラフ構造の生成
  • smilesの文法に従うように生成する方法

ソニーが提供するディープラーニングの開発環境の最新情報 @成平 拓也 (Sony)

開発者向けソフトウェア

  • Neural Network Libraries
    • 研究開発者向けフレームワーク
    • オープンソース
  • Neural Network Console
    • GUIツール

ソニーグループ内事例

  • 画像認識
    • aibo
  • ジェスチャー認識
    • Xperia Ear
  • 価格推定

Neural Network Libraries

  • Mixed Precision学習
    • NVIDIAのPascal世代以降のGPUを用いた場合に利用できる
    • 可能な限りfloatの32bitの代わりに16bitの表現を用いた学習
    • メモリ使用量削減
    • NVIDIAのVOLTA世代以降では、TenserCoreによる高速化の恩恵

ソニー不動産におけるビジネス現場での活用と運用

...

データサイエンスのワークフローを加速する為のNVIDIA GPU

NVIDIAについて

  • ...

データサイエンティストのためのNVIDIAのソリューション

  • RAPID
    • GPUでデータサイエンスのフローを加速
      • データの読み込み、加工、学習などのフローをGPUで高速に
      • データサイエンスの問題点
        • 前処理が重い
        • 学習が重い
        • ここらをGPUで加速
      • RAPIDSライブラリ
        • cuDF
          • データをGPUに配置
        • cuML
          • 機械学習
        • cuGRAPH
          • グラフ解析
      • GPU DataFrame
        • Apach Arrowの仕様に基づいている
          • Apach arrow
            • 背景としてライブラリによってデータフォーマットが異なる
            • 変換が重い
            • 共通化したい
          • libgdf
            • Cから使える
          • Pygdf
            • Pythonから使える
          • dask_gdf
      • 配布
        • dockerコンテナ
        • ソースコード
      • RAPIDSの使用例
        • dockerコンテナpullすればすぐに使える
        • pandasで読み込んでgdfに変換できる
        • メソッド等はpandasライクに書ける
  • GPUのメモリサイズ小さくてデータ入らないのでは?
    • GPUも進化してるもんね?

競馬予測の苦楽 @heartz2001

自己紹介

  • Fringe81
  • 普段はレコメンド
  • +競馬予測

競馬予測の楽しみ

  • 毎週解くべき問題となるデータが追加
  • 結果がリアルタイムで映像で確認できる
  • データ豊富
  • 日本競馬の難易度
    • JRAに売上の20~25%が入るようにオッズが動的に変化
      • パチンコは15%程度
    • ランダムに購入すると回収率の期待値は75~80%程度
    • 回収率100%の壁を超えることは難しいが、不可能ではない

競馬予測の苦しみ

  • 問題設定
    • 出走馬1頭1頭に関数fを学習して、スコア化
    • 何を解くのか?
      • 分類?回帰?ランキング?
      • 分類
        • 0/1に丸めると情報が欠落
        • ラベル不均衡
      • 回帰
        • 自由度高く選択が難しい
      • ランキング
        • 回帰よりも学習コストが重いかつ精度もそんなに高くない
      • 回帰の目的関数
        • 着順
        • タイム
        • 本賞金
        • etc...
      • 目的関数のエンジニアリング
        • 特徴量作成やハイパーパラメータ調整より重要
        • 試行錯誤するしかない
          • 30種類くらい試している
        • ドメイン特価のエンジニアリング
          • レース内標準化
            • 絶対的なタイムに意味はないので
          • 4位以下のスコア同じに
            • 馬券に絡まないので
        • 解く問題を分ける
          • 強さ
          • 回収しやすさ
          • 人気
  • データ整形
    • 馬柱をモデル化してデータ処理を簡潔に
  • 特徴量
    • 出走履歴
      • 可変長
      • 過去N走の成績を横に並べる
        • 出走数が馬ごとに異なるので欠損値が多く発生
        • 次元数の割に情報量が少ない
      • 集計値に丸める
        • 次元数を丸めて情報密度が高い
      • RNNのような可変長な入力をとれるモデルを選択するアプローチ
        • 時系列データ
      • 集計特徴量
        • レース条件の切り分け方は競馬ドメイン知識が必要
  • オッズ
    • 馬券発売締切まで変動
  • レース区分
    • 競馬場
    • 芝・ダート
    • 距離
    • etc...
  • レース区分ごとに予測モデルを作成
    • どの粒度が適切かは特徴量と目的変数に大きく依存
  • 学習
    • LightGBM
    • カテゴリ変数をカテゴリ変数として扱うことが可能
    • 欠損値を欠損値として扱うことができる
    • GCEのプリエンプティブインスタンス
      • 利用料金約70%OFF
      • 1プロジェクト72CPU
        • それ以上はプロジェクト分ける必要
        • プロジェクト間でイメージのコピーはできない
      • 予告なしに落ちる、探索状態を逐次保存、再開できるように
      • 学習に進捗をslackに通知
  • 評価
    • nDCG
      • ランキング問題でよく使われる
      • 非負かつ高ければ良いもの
      • 競馬だと賞金
    • 評価データの期間
      • 年間3000レース
      • 季節性なども考慮して最低1年間は評価に必要
      • コースの改修があるので古いデータは役にたたない
  • 勝てる馬券≠売れる馬券
    • ....
  • ...

Daskによる並列分散処理 @堀越 真映 (ARISE)

Daskとは

  • 問題
    • 計算が単一スレッドで行われるため処理速度が遅い
    • データがメモリに乗らない
  • 解決策
    • 並列処理
      • 複数のタスクを並列に
    • Out-of-core処理
      • メモリに乗らないデータを逐次処理する
  • Dask
    • データ処理、数値計算が主目的
      • Numpy、Pandasと同じように使える
    • データ構造
      • Dask Array
        • NumPyのndarray
      • Dask Dataframe
        • pandasDataFrame
    • Dask Array
      • Numpyのndarrayを分割
      • np.ones*1 -> dx = da.ones*2, chunk=(5,5))
        • 内部で5×5の4つの行列に分割
        • 計算グラフを作成するだけ、メモリの確保はしていない
      • dy = dx.sum(axis = 0)
        • axis=0に沿って値を計算
      • dy.compute()で初めて計算が行われる
        • 結果はndarrayで得られる
      • chunk間の処理が発生するときは、chunkサイズを一致させたほうがいい
    • Dask DataFrame
      • 複数のPands DataFrameで構成
      • indexに沿ってPartitionに分割
        • 2つの表ができるイメージ
      • ddf = dd.from_pandas(df, 2)
      • ddf.mean()
    • 計算グラフの実行イメージ
      • 計算グラフの依存関係の解析
        • 不要なタスクの削除
      • 計算順序の静的・動的な解析
      • 計算グラフの最適化
        • 特定処理のインライン化
    • Linear Algebra
      • コレスキー分解とか特異値分解とか

機械学習におけるDaskの活用

  • scikit-learnでの並列処理
    • n_jobsで指定
    • 内部的にはjoblibを利用
      • ノード内並列 (threading, multiprocessing)
      • Out-of-core (OOC) 処理はできない
  • Daskでの並列処理
    • Daskその前に
      • 本当に全てのデータが必要?
        • サンプリングでいいのでは?
    • Dask ML
      • Daskのデータ構造に対して機械学習
      • 機械学習アルゴリズムを並列化、OOC処理
      • scikit-learnにDaskのarrayを投げると内部でNumPy Arrayに変換
        • 逐次メモリを確保してOOMの可能性
      • Dask MLではDaskのデータ構造を維持して処理を実行
      • Incremental
        • Daskのchunkごとにscikit-learnのpartitial_fitを行う
      • ParallelPostFIt
        • 学習済Estimatorのtrasnformやpredictを並列で行う
          • テストデータが大きいとき有効
    • Dask Distributed
      • ノード間分散処理を行うスケジューラを提供
      • スケジューラでの計算を複数ノードで分散
        • 低レイテンシ
        • worker間でのデータ共有
      • プラガブルAPI
        • withブロックでjoblib.Parallelの既定バックエンドを変更可

*1:10, 10

*2:10, 10