5
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

あなたの回帰モデルを破壊する呪われし処置後変数を紹介します

Posted at

はじめに

こんにちは、事業会社で働いているデータサイエンティストです。

皆さんは、キャンペーンの購買効果を測定する回帰モデルにおいて、「クリック数」を独立変数として入れたことはありますか?

私はデータサイエンティストとして4年間実務に携わってきましたが、このようなモデル定式化を何度か見かけたことがあります。クリック数を説明変数に入れる方は、主に次のような考えに基づいているのではないでしょうか。

クリック数がないと購入ボタンを押せない。だから、ユーザーフロー的に購入には必ず影響するはずなので、モデルに入れました。

本記事では、このような考え方が「処置後変数」という呪われし変数をモデルに含めてしまい、深刻なバイアスを生むこと、そしてその結果として意思決定を誤らせるリスクがあることをシミュレーションの実例を用いて説明します。

最後に、処置後変数を見極めるためのシンプルな判断基準も紹介します。

なお、本記事は実務家向けですので、「なぜ処置後変数が数理的にバイアスを引き起こすのか」といった理論的な解説は省きます。より詳しく理解したい方は、政治学方法論の優れた論文である Montgomery, Nyhan, and Torres (2018) をぜひご参照ください。

処置後変数の直感的な例:交通事故にあったらがんを予防できる!?

シミュレーションの実例に入る前に、ここはまず、処置後変数をモデルに入れてしまうことで、どれほど深刻なバイアスが生じうるかを、直感的に理解できる Acharya, Blackwell, and Sen(2016)の例を紹介します。詳細に興味ありましたらぜひ元の論文を確認してみてください。

想像してみてください。ある研究者が、「交通事故に遭ったこと」と「がんを患っていること」の因果関係を分析しているとします。どう考えても、交通事故に遭ったかどうかががんの発症に直接影響を与えるとはありえないです。つまり、この2つには因果関係はないとするのが妥当です。

要するに、下記の回帰モデルを推定すると:

$$
がん発症 = \alpha + \beta \cdot 交通事故に遭った + \epsilon
$$

$\beta$ は統計的に有意になりません。

ところが、ここで「現在入院しているかどうか」という変数を条件としてモデルに含めると:

$$
がん発症 = \alpha + \beta_1 \cdot 交通事故に遭った + \beta_2 \cdot 入院 + \epsilon
$$

話は変わってきます。交通事故でもがんでも、どちらでも人は病院に入院しますよね。そうすると、「今病院にいる」という情報を条件にしたとたん、「がんなどの病気がない人」は「交通事故で入院している可能性が高い」と推論されてしまいます。

回帰モデルに新しい変数を追加するということは、その変数で条件付けすることと実質的に同じ意味を持ちます。

ここでは話をシンプルにするために、研究者が「入院している患者」のデータだけを見ている状況を考えてみましょう。

おそらく、こんなデータになるのではないでしょうか:

> in_hospital_df <- tibble::tibble(
     accident = c(1,0,1,0,1,0),
     cancer = c(0,1,0,1,0,1)
 ) 
> in_hospital_df
# A tibble: 6 × 2
  accident cancer
     <dbl>  <dbl>
1        1      0
2        0      1
3        1      0
4        0      1
5        1      0
6        0      1

ここで相関係数を計算すると:

> cor(in_hospital_df$accident, in_hospital_df$cancer)
[1] -1

当たり前ですが、完璧な負の相関が出てしまいます。これはあくまでもシミュレーション(シミュレーションでもないかw)ですが、実際のデータでも似たような結果になるでしょう。

この結果、がん発症交通事故に遭った の間に負の相関が観測され、「交通事故に遭った人はがんのリスクが低い」という非常に奇妙な結果が統計的には導かれてしまいます。

では、仮にあなたがこの分析結果を見たとして、「がんを予防するために交通事故にあおう」と考えるでしょうか?そんなことは絶対にしないはずですね。この分析結果が明らかにおかしいと直感的にわかるからです。

これは、「入院しているかどうか」という処置後変数を条件にしてしまったことで、本来は無関係だった2つの変数の間に危険な偽の相関が生じてしまった典型例です。

つまり、処置後変数をモデルに入れるだけで、「存在しない因果関係」を統計的にあるように見せてしまうことがあるのです。これが、処置後変数がもたらすバイアスの恐ろしさです。

このように、処置後変数をモデルに含めることで、独立変数と目的変数の間に存在しないはずの因果関係を見せかけとして作り出してしまうことがあります。

さらに厄介なのは、この交通事故とがんの例を示したAcharya, Blackwell, and Sen(2016)が説明するように、実際のデータ分析では、こうしたバイアスの方向や大きさは予測できません。処置後変数の導入は、時として効果があるように見せかけてしまう強烈なバイアスを生み出し、誤ったビジネス判断につながるリスクがあるという点に注意が必要です。

購買数とクリック数の例

ここから本格的なシミュレーションに入ります!

まず、データはこのように生成されたとしましょう:

$$
クリック数 = \alpha + \beta_1 \cdot UI変更 + \beta_2 \cdot 価格 + \epsilon
$$

$$
購買数 = \beta_{CVR} \cdot クリック数
$$

係数の値は下記の表にまとめました:

係数 真の値
$\alpha$ 10
$\beta_1$ 10
$\beta_2$ -5
$\beta_{CVR}$ 0.8

ここでは、まずデータを生成しましょう:

set.seed(12345)
my_df <- 10000000 |>
  rnorm() |>
  tibble::tibble(
    e = _
  ) |>
  dplyr::mutate(
    treatment = rbinom(dplyr::n(), 1, 0.5),
    price = abs(rnorm(dplyr::n())),
    click_count = 10 + 10 * treatment - 5 * price + e,
    buy_count = 0.8 * click_count
  )

データは1000万件ありますので、「これはサンプル数が少ないから起きた現象だ!」といった批判は通用しません。

どうしてもデータ量で反論したい場合は、スーパーコンピューターでも借りて、N = 9000兆ぐらいで試してみてください。結果をコメントのところで教えていただければ幸いです笑

さて、ここでは以下の2つの回帰モデルを推定してみます。

まずはクリック数を無視した、シンプルなモデルです:

$$
購買数 = \hat{\alpha} + \hat{\beta_1} \cdot UI変更 + \hat{\beta_2} \cdot 価格
$$

このモデルは、購買数がUIの変更(処置、Rのコードでは treatment)と価格によって直接決まるという定式化です。記事冒頭でも紹介したように、

クリック数がないと購入ボタンを押せない。だから、ユーザーフロー的に購入には必ず影響するはず

という考え方に立つと、このモデルは重要な情報(クリック数)を無視しており、不適切だと思われるかもしれません。

そこで、次に「クリック数を考慮したモデル」も推定してみます:

$$
購買数 = \hat{\alpha} + \hat{\beta_{CVR}} \cdot クリック数 + \hat{\beta_1} \cdot UI変更 + \hat{\beta_2} \cdot 価格
$$

一見、こちらのほうがドメイン知識を反映しており、もっともらしく見えるかもしれません。しかし、ここで思い出していただきたいのが先ほどの「交通事故とがん」の例です。

実際の運用を考えると、UIの変更と価格の設定がまず先に行われ、それを見たユーザーがクリックするという流れです。たとえば、管理画面で価格設定を確定させたり、AB テストツールで価格表示パターンを決めたりする場面ですね。

つまり、クリック数はUIと価格のに生じる変数であり、これは統計的には処置後変数に該当します。

このような変数をモデルに含めてしまうと、UI変更や価格の効果を正しく測れなくなります

実際にまずシミュレーションデータでモデル推定して、stagazer パッケージで推定結果を表にまとめて確認しましょう:

m_correct <- my_df |>
  lm(buy_count ~ treatment + price, data = _)

m_wrong <- my_df |>
  lm(buy_count ~ click_count + treatment + price, data = _) 
> stargazer::stargazer(m_correct, m_wrong, omit.stat = c("f"), type = "text")

=============================================================
                               Dependent variable:           
                    -----------------------------------------
                                    buy_count                
                            (1)                  (2)         
-------------------------------------------------------------
click_count                                    0.800***      
                                               (0.000)       
                                                             
treatment                 8.000***              -0.000       
                          (0.001)              (0.000)       
                                                             
price                    -4.000***              0.000        
                          (0.0004)             (0.000)       
                                                             
Constant                  8.000***             0.000***      
                          (0.0005)             (0.000)       
                                                             
-------------------------------------------------------------
Observations             10,000,000           10,000,000     
R2                         0.971                1.000        
Adjusted R2                0.971                1.000        
Residual Std. Error 0.800 (df = 9999997) 0.000 (df = 9999996)
=============================================================
Note:                             *p<0.1; **p<0.05; ***p<0.01

まず、(1) のモデル $購買数 = \hat{\alpha} + \hat{\beta_1} \cdot UI変更 + \hat{\beta_2} \cdot 価格$ を確認しましょう。

UI変更の係数 $\beta_1$ の真の値は 10 に設定してありますが、推定結果では 8 となっています。価格の係数 $\beta_2$ の真の値は -5 に設定されていますが、こちらも推定結果は -4 にズレています。

ここで注目すべきは、(1) のモデルで得られた係数 8 や -4 という値が、それぞれ真の係数に $\beta_{\text{CVR}}$ を掛けた値になっている点です:

  • $10 \times 0.8 = 8$
  • $-5 \times 0.8 = -4$

つまり、クリック数をモデルに入れないことによって、本来測りたかった UI変更や価格の効果が、クリック数を通じて希釈されてしまっています。

では、私たちはクリック数をモデルに入れるべきでしょうか?

(2) のモデルを見てみると、クリック数を入れたことで UI変更や価格の係数はほぼ 0 にまで押しつぶされてしまい、まるで UI や価格が購買に影響していないかのような結果になっています。これは明らかにおかしいですよね。

このように、処置後変数(クリック数)をモデルに入れてしまうと、真の因果効果が歪められ、誤った意思決定につながるリスクがあります。

さらに注目すべきは、モデルの良し悪しを予測精度だけで評価することの危うさです。

たとえば、(1) の適切なモデルの決定係数(R2)は 0.971 と非常に高いですが、処置後変数を誤って含めた (2) のモデルでは、R2 が 1.000 となっています。

一見すると (2) のほうが「より良いモデル」に見えるかもしれませんが、これはクリック数が購買数をほぼ完全に説明してしまっているだけであり、UI変更や価格の効果が吸収されてしまっていることに他なりません。

つまり、「予測精度が高い」=「因果効果を正しく捉えている」ではないということを、この結果ははっきりと示しています。

ビジネスの現場で (2) のような結果をそのまま受け取ってしまえば、「UIを変えても効果はない」「価格変更も意味がない」といった誤った結論を導いてしまい、改善のチャンスを見逃すことにつながります。

したがって、モデルに含める変数が処置後変数に該当するかどうかを慎重に判断することは、統計的な正確さだけでなく、ビジネス上の成果に直結する非常に重要なポイントなのです。

処置後変数の判断方法

ここでは、ビジネスで実務に携わるデータサイエンティストや PdM、エンジニアの皆さんに向けて、その変数が処置後変数かどうかを判断するシンプルな方法を紹介します。

本当に簡単です。

その変数は、「処置の値が決まった瞬間」に、すでに存在していますか?

この問いにいいえと答える場合、その変数は処置後変数です。

たとえば、「UI変更を適用する」や「価格を設定する」といった処置を考えている場合に、その処置が決まったタイミングで「クリック数」や「スクロール率」といった行動ログは当然まだ存在しません。これらはユーザーの反応として後から生まれるものです。

このように、「その変数が、因果の時間軸においてどのタイミングで観測されるか」を意識するだけで、処置後変数かどうかは簡単に見分けられます。

また、細かい話ですが、次のような回帰モデルを考えてみましょう:

$$
購買数 = \alpha + \beta_1 \cdot UI変更 + \beta_2 \cdot 価格 + \beta_3 \cdot 商品説明文の質 + \epsilon
$$

このモデルを推定したいとき、「商品説明文の質」が数値としてデータベースに存在しない場合、たとえば商品情報をリリースした 1ヶ月後 にデータサイエンティストが構築した自然言語処理モデルで推定された値:

$$
\hat{商品説明文の質} = BERT(商品説明文)
$$

を代用することがあるかもしれません。

このとき、データベース上のレコードとしては、$\hat{商品説明文の質}$ が登場するのは UIや価格の設定より後になるため、ぱっと見では処置後変数に見えるかもしれません。

しかし、ここで重要なのは「データベースに格納されたタイミング」ではなく、その変数の意味内容が、処置(UI変更や価格設定)より前に決まっていたかどうかです。

このケースでは、BERT によって得られた $\hat{商品説明文の質}$ は、あくまでリリース前にすでに存在していた商品説明文に基づいています。つまり、因果関係上の時間順序としては UI 変更や価格設定より前に存在していた、もしくは UI 変更や価格設定と同時に確定した情報です。

したがって、たとえデータベース上では後から付与された変数に見えても、実質的には処置前変数として扱うことが可能です。

このように、処置前か処置後かの判断では、「データベース上での記録の順序」ではなく「因果関係上の時間順序」を意識することが重要です。

処置後変数を避けることは、特に AB テストや回帰分析などの効果測定において非常に重要です。見落としてしまうと、因果効果の推定に大きなバイアスが生じ、誤った結論や意思決定につながりかねません。したがって、変数の選定は慎重に行いましょう。

結論

いかがでしたか?

クリック数がないと購入ボタンを押せない。だから、ユーザーフロー的に購入には必ず影響するはずなので、モデルに入れました。

「データサイエンスにおいてドメイン知識が大事」とよく言われますので、駆け出しのデータサイエンティストやデータサイエンスの専門外の方が、このように直感的に考えてしまうのはよく理解できます。実際、私も同じような判断をしてしまった方々を何人も見てきました。

ただ、それはドメイン知識を正しく活かす方法ではありません。因果推論において重要なのは、「変数が因果構造の中でどの位置にあるか」を理解し、それに基づいてモデルに含める変数を慎重に選ぶことです。処置後変数を誤って含めてしまうと、どれだけ高度なモデリングと高度なドメイン知識を駆使しても、得られる結論は誤ったものになります。

真に価値ある分析とは、直感だけに頼らず、因果関係の構造に立脚してドメイン知識を活かすことです。これを意識するだけで、分析の質は格段に向上します。

最後に、私たちと一緒に働きたい方はぜひ下記のリンクもご確認ください:

参考文献

Acharya, Avidit, Matthew Blackwell, and Maya Sen. "Explaining causal findings without bias: Detecting and assessing direct effects." American Political Science Review 110.3 (2016): 512-529.

Montgomery, Jacob M., Brendan Nyhan, and Michelle Torres. "How conditioning on posttreatment variables can ruin your experiment and what to do about it." American Journal of Political Science 62.3 (2018): 760-775.

5
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
5
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?