微忘録

好奇心に記憶力がついていかない人のブログ

BigQueryだけで統計的因果推論を行いたかった

この記事はRettyアドベントカレンダー12日目の記事です。
昨日は櫻井さんの「KubernetesでVolumeトリックを行う方法」でした。

はじめに

Retty株式会社でデータアナリストとしてお仕事をしています。 6日目の「あなたのエリアは何処から? ~地理空間クラスタリングとの差分検証~」の記事と同様に、箸休めの記事になります。 それでは表題通り、BigQueryだけで統計的因果推論を行いたかった話です。

ことの発端

BigQueryは高速処理や豊富な関数など、それ自体でも強力なツールです。最近ではBigQueryMLによる機械学習の実施も可能になり、利用できるモデルの数も増え続けています。

cloud.google.com

Rettyではデータの民主化を進めており、アナリストだけではなくプランナーもBigQueryを活用しています。そのためBigQueryの可能範囲が分かることは重要ですし、時には背伸びをした使い方をアナリストが試す必要があります。そのため今回はBigQueryMLの可能範囲について、統計的因果推論の手法が用いれるか実験したいと思います。


いざ挑戦

今回は岩波DSvol3よりサンプルデータを拝借して、CM視聴とアプリ利用の因果効果を推定します。カラム構成は主に以下です。詳細については書籍をご一読ください。

  • gamedummy…アプリ利用
  • gamesecond…アプリ利用秒数
  • gamecount…アプリ利用回数
  • cm_dummy…CM接触有無
  • etc...

①データの準備

BigQueryでは外部データソースを参照したテーブルを用意することが可能です。
今回はGoogleDriveにデータを設置し、外部データソースとして認識させます。

cloud.google.com

②BigQueryMLによる2項ロジスティック回帰と傾向スコア算出

傾向スコアを算出したい「CM接触有無(cm_dummy)」をlabel(目的変数)に、ゲーム利用関連の列を除き、その他の変数を説明変数にBigQueryMLを実行します。

create model `project_name.sandbox.ml_adv_logreg` 
options(model_type='logistic_reg', auto_class_weights=true, early_stop = true) as
select
 * except(cm_dummy, gamedummy, gamesecond, gamecount, id)
 , cm_dummy as label
from `project_name.sandbox.adventcalendar`
;

次にモデルの結果画面を確認します。モデル評価に必要な情報はおおよそこの画面で確認できます。陽性クラスの閾値も動的に確認でき、その際には下記のプロットが動的に変動します。

f:id:wtnVenga:20191212173900p:plain
モデル結果画面①

f:id:wtnVenga:20191212173957p:plain
モデル結果画面②

モデルが傾向スコアの算出に有用なのか否かについての検討を行います。 まずはAUCが0.78程度あるためモデルのfittingはある程度は良しと考え、次に実際に傾向スコアの推論を行い、分布の対称性があるか確認します。

with pred as ( 
  select *
  from ml.predict(
    model `project_name.sandbox.ml_adv_logreg`,(
    select * except(cm_dummy, gamedummy, gamesecond, gamecount), cm_dummy as label
    from `project_name.sandbox.adventcalendar`),
    struct(0.5192 as threshold)
  )
)

select pred.* except(predicted_label_probs) , _p.prob
from pred, unnest(pred.predicted_label_probs) as _p
where pred.predicted_label = _p.label

ML.PREDICT 関数  |  BigQuery ML  |  Google Cloud

傾向スコアとしてのヒストグラムの対称性を、一旦spreadsheetに出力して確認します。ある程度の分布としての重なりが確認できるため、最後の段階へと移ります。 f:id:wtnVenga:20191212181920p:plain f:id:wtnVenga:20191212181927p:plain

ちなみに単に実行結果を出力するだけだと、以下のような結果が得られます。

f:id:wtnVenga:20191212175118p:plain
ml.predictの実行結果

③統計的因果推論に用いる推定量への変換と検証

ここで問題が発生します。

あれ・・・IDを変数に入れていないからJOINできないぞ・・・?

上記のクエリを拡張し、推定量への変換とATTの算出を行いたかったのですが、説明変数に「ID」を含める御法度をしなければ、predict結果を元テーブルとJOINすることができません。

真の原因変数を用いれないので、層別解析、傾向スコアマッチングさえ用いることは困難になりました。

最後に

どこかにJOINできるINDEXが隠されているのでしょうか。 今回以外にもRMSEやMAEなど様々な誤差指標を算出したい場合はどうすればよいのでしょうか。 本記事はここまでですが、個人的に解決策を探し続けます。情報提供もお待ちしております。