渋谷駅前で働くデータサイエンティストのブログ

元祖「六本木で働くデータサイエンティスト」です / 道玄坂→銀座→東京→六本木→渋谷駅前

機械学習のビジネス上の価値を「効果測定」して「数値評価」する方法

f:id:TJO:20190221111234p:plain
(Image by Pixabay)

気が付けば、日本における第一次データサイエンティストブームから6年、人工知能ブーム開始から3年が経ったようです。意外と言っては何ですが、これまでのところ人工知能ブームも、そしてそれにブーストされた形で起こった第二次データサイエンティストブームも、まだまだ続くどころかどんどん加速していきそうな状況です。


なのですが、これだけ統計学機械学習のような高度なデータ分析技術がビジネスの現場に浸透するようになった現在でも、なぜかあまり多く見かけないものがあります。それは機械学習(もしくは自動化された統計分析)によるビジネス上の成果を数値として示したもの」。意外かもしれませんが、個人的な観測範囲では例えば「Deep Learningを導入したら〇〇がXX%向上した」みたいなリリースや記事を見かけることは、正直なところ思った以上に少ないように思われます。それでも第一次データサイエンティストブームだった2013-14年頃はそういう事例記事を時々見かけましたが、最近はあまり見ない気がします。


それは単に、ビジネス的な競争力の根幹に関わる部分なので各社ともひた隠しにしているだけなのかもしれません。もしくは、もしかしたら実は世の中機械学習を導入しても特にビジネス上のプラスのインパクトがない事例ばっかりなのかもしれません。さもなくば、そもそもビジネス上のインパクトを「効果測定」して「数値評価」する枠組みを整えていないだけなのかもしれません。もしそうであるならば、これほど勿体無い話もないと思うのです。


第一次データサイエンティストブームから6年、そして人工知能ブーム開始から3年が経った今、データ分析業界がやるべきは「統計学機械学習やデータサイエンスでビジネス上の価値を数値評価できる形で出せた」という「成功事例」をもっと世に出していくことだと個人的には考えています。言い換えれば、そろそろ「論より証拠」の時期であろうと。そこで、そういう「効果測定」「数値評価」の方法としてどんなものがあり得るかを大雑把に論じてみようと思います。


A/Bテストをする


何を当たり前の話を言ってるんだと怒られそうですが、今でもA/Bテストは極めて重要です。ただ、A/Bテストは容易に交絡要因によってその結果が変わってしまうことがあるので(対処法は後述)、できればランダム化比較対照試験(RCT)*1に近い形で実施することが望ましいです。インターネット広告の類だと割と容易にRCTと同等の形でテストすることが可能ですが、それ以外だとかなり難しいのではないかと思われます。



勿論やり方は言うまでもなく、「機械学習システム導入を行った場合のKPIなどのデータ」即ちAセットと「行わなかった場合の同じ種類のKPIなどのデータ」即ちBセットとを用意し、AとBとで比較して(統計的に有意な)差があったかどうかを判定するというものです。6年前の記事でも書いていますが、判定の方法は色々あり得ると思います。




ちなみに言わずもがなですが、webマーケティングの世界ではA/Bテストは極めてありふれた効果測定&数値評価方法であり、ザッと調べただけでもかなりの資料が出てきます。


DiD(差分の差分法)テストをする


一方、A/Bテストには致命的な欠陥があります。それは交絡要因などによってそもそも介入効果が分からなくなってしまうことがあるということです。特に時系列に依存するデータを使う場合はより深刻で、seasonalityがある場合はどうにもならないと言って良いでしょう。例えば「11月に機械学習システムを使わない場合の売上高を記録して、12月に機械学習システムを使った場合の売上高を記録して、比較する」ということを考えてみましょう。普通に考えればこれで良いように見えますが、一般には12月は年末商戦の真っ只中なので「何もしなくても売上高が勝手に伸びる」時期です*2。すると「11月の売上高<12月の売上高」という結果になっても、それが機械学習システムのおかげかどうかは分かりません。これでは困ってしまいます。


f:id:TJO:20160729141427j:plain

そこで、こういう場合にはDiD(Difference in Differences: 差分の差分法)テストを使うのが一般的です。一番簡単なのは、上記の例で言えばAという地域グループとBという地域グループを用意して、11月は何もせずそのまま2地域の売上高を記録し、12月はA地域では機械学習システムを使う一方B地域では機械学習システムを使わないようにして売上高をそれぞれ記録する、というものです。これなら、年末商戦による11月<12月というスロープに加えて、機械学習システムによる恩恵が+αで乗っかるかどうか(つまりスロープがより急になるかどうか)を見ることで、機械学習システムの効果の有無を判定することが出来ます。



このタイプの検証を行う場合、時系列でデータを取れるのであればズバリ{CausalImpact}を使って検証するのが最も手っ取り早いと思います。{CausalImpact}を使えば反実仮想(counterfactual)としてのベースラインを設定できるので、直接ベースラインからの増分のパーセンテージに対してベイズの枠組みで検定した上で「XX%の改善・向上」という形で効果測定することができます。

> library(CausalImpact)
> set.seed(101)
> x1 <- 100 + arima.sim(model = list(ar = 0.999), n = 100)
> # x1はcounterfactual
> y <- x1 + rnorm(100)
> y[71:100] <- y[71:100] + 10
> # yが目的変数
> # 71番目以降に10%に当たる10だけ機械学習システムの効果が上乗せされている
> data <- cbind(y, x1)
> pre.period <- c(1, 70) # 機械学習システム稼働前
> post.period <- c(71, 100) # 機械学習システム稼働後
> impact <- CausalImpact(data, pre.period, post.period)
> # モデル計算を行い、オブジェクトに結果を収納する
> 
> summary(impact)
Posterior inference {CausalImpact}

                         Average        Cumulative  
Actual                   118            3527        
Prediction (s.d.)        107 (0.56)     3217 (16.73)
95% CI                   [106, 108]     [3185, 3253]
                                                    
Absolute effect (s.d.)   10 (0.56)      309 (16.73) 
95% CI                   [9.1, 11]      [274.3, 342]
                                                    
Relative effect (s.d.)   9.6% (0.52%)   9.6% (0.52%)
95% CI                   [8.5%, 11%]    [8.5%, 11%] 

Posterior tail-area probability p:   0.00101
Posterior prob. of a causal effect:  99.8994%

For more details, type: summary(impact, "report")

> plot(impact)

f:id:TJO:20190411145609p:plain

機械学習システムの真の効果が「10%増」の時に、95%信頼区間[8.5%, 11%]と出ていてきちんと効果の「量」も推定されていることが分かります。ちなみに原系列にトレンドがあるとこの推定は乱れることがあるので、その場合はモデリング時にトレンド項の有無を指定するか、{bsts}で予めcounterfactualのモデルを作っておいてそれを引数として与えるかする必要があります。


「実験」し「効果測定」して「数値評価」するのはデジタル領域の専売特許ではない


ここまで読んだところで「え?これってただのマーケティングテストじゃん?」と思った人は多いのではないかと思いますが、実際その通りです。この記事で僕が言いたかったことは「統計分析なり機械学習なりとにかくデータ分析で何かしらのアクションを行ったらきちんと実験しよう」という話であり、その実験の方法としてマーケティングテスト(エクスペリメント)を行うべきだという、ただそれだけです。


ところが冒頭にも書いたように、正直に言えば特に機械学習人工知能)のビジネス応用においては、そんな簡単なA/Bテストの結果すらもあまり明示的には見聞きしない印象があります。やっていて出していないのなら単なるPR不足だと思いますが、やっていなくて出していないのなら問題かなと。本当にビジネスへの効果が期待できる機械学習人工知能)システムならばきちんと「実験」し「効果測定」して「数値評価」すべきでしょう。やってみて、実際に数値的にもプラスの効果が見られればそれに越したことはないですし、仮に効果がなかったならば改善すれば良いだけのこと。それをやらなければ「流行りに乗っただけ」と言われても仕方ないようにも思われます。



最近読んで面白かったのがこちらの記事。作業服チェーンで有名なワークマンが、デジタル分野の各社も顔負けの「リアル店舗でのA/Bテスト」を行い、しかも新規発注システムを採用するに当たって導入済み店舗とその他全店舗平均とで比較して「売上の伸び率で3ポイント上回った」ことからシステム導入の全店舗への導入を断行したという、徹底したデータによる数値評価を行っているという事例の紹介です。


このワークマンの事例にも見えるように、データに基づいてビジネスを改善するという営みには、高度な統計学機械学習は勿論のこと、完全にデジタル化されたビジネスフレームワークですらも必ずしも要らないのです。言い方を変えると、そこまで科学的な取り組みでなくても、そしてデジタルでオンラインではないオフラインのビジネスであっても、「実験」して「効果測定」して「数値評価」するという一連のプロセスを常にきちんと行うことが出来れば、非常に合理的にビジネスを改善し続けていくことが出来るのです。同じことが、データ分析特に機械学習人工知能)のビジネスへの導入についても言えるのではないかと思っています。


おまけ:テストするというよりも多腕バンディットでベストのアウトプットを選ぶ



これまではテストをすることで機械学習の効果があったか否かを調べるやり方の話をしてきましたが、そもそも「機械学習に汎用的な効果があるのであれば一番効果のある施策や実装がどれかをデータに基づいて推定して、一番良かったものだけを投入し続ける」という考え方も(アプリ運営やwebマーケティングであれば)アリだと思います。そういう際によく使われるのが多腕バンディットです。


名前の通りで、これはカジノで複数ある当たり確率不明のバンディット(スロット)マシンを何度も引きながら勝ち金額を増やしていくのと同じ構図です。データ分析に基づく施策をn個用意して、例えばコンバージョンを獲得すればするほどその施策だけを集中して実施していくというやり方です。ある意味動的なA/Bテストとも言えますが、明示的なパフォーマンス比較をしない点がA/Bテストとは異なります。


僕自身は全くもって多腕バンディットには詳しくないどころかほぼ完全にど素人なので解説なんぞ何も出来ないのですが、以前遊びで多腕バンディットの真似事をシミュレーションした時のRコードがありますので、下に適当に貼っておきます。GitHubにも上げてありますので、そちらから取ってきていただいても構いません。

# Create a sample data matrix
seed_prob <- c(0.05, 0.05, 0.8, 0.05, 0.025, 0.025)
set.seed(101)
d <- t(rmultinom(100000, 1, seed_prob))

# Set a hyper parameter
inc <- 1e-4

# Initiate an arm probability
prob <- rep(1/6, 6)

# Set a result vector
res <- rep(0, nrow(d))

# Iterate multi-arm bandit computation
for (i in 1:nrow(d)){
  pred <- rmultinom(1, 1, prob) # Predict which arm returns 1
  res[i] <- t(pred) %*% d[i, ] # Compute an internal prod for answer
  # if 1, update the arm probability to reinforce
  if (res[i] == 1 && min(prob) > 0){
    prob[which(pred > 0)] <- prob[which(pred > 0)] + inc
    prob[which(pred == 0)] <- prob[which(pred == 0)] - inc/5
  }
  # if 0, update it to less reinforce
  if (res[i] == 0 && min(prob) > 0){
    prob[which(pred > 0)] <- prob[which(pred > 0)] - inc
    prob[which(pred == 0)] <- prob[which(pred == 0)] + inc/5
  }
  
  # Adjust a scale of the arm probability
  if (min(prob) < 0){
    prob <- (prob - min(prob)) / sum(prob)
  }
  if (max(prob) > 1){
    prob <- (prob - (max(prob) - 1)) / sum(prob)
  }
}

# Check and plot a trajectory of reinforcement learning
res_bin <- rep(0, 1000)
for (i in 1:1000){
  res_bin[i] <- sum(res[((i-1) * 100 + 1) : (i * 100)]) / 100
}
plot(res_bin, type = 'l',
     xlab = 'Iteration', ylab = 'Correct answer probability for 100 epochs')
segments(0, max(seed_prob), 1000, max(seed_prob), col = 'red', lwd = 5, lty = 3)

f:id:TJO:20190320161305p:plain

ここでは「6つの異なる機械学習モデルに基づいて最適化されたデザインの商品ページがあってどれがコンバージョンを得たかがストリーミングで記録されている際に、6つのうちどのデザインを出すべきかを多腕バンディット(もどき)的に判定してその都度出し分ける」というシチュエーションを想定しています。ちなみにこれは正確には前職のデータ分析チームで自主的に社内コンペをやった際のお題の一つで、上記のdに当たるデータはストリーミングで次々とやってくるという代物でした。


僕のやり方は極めて単純で、多項分布乱数で6つのうちどれかを確率的に選択して出すのですが、それがコンバージョンを得たら選択したデザインを出す確率を上げ(残りを下げ)、コンバージョンを得られなかったらその逆をやる、というものです。このやり方だとEpsilon-greedyの最も単純なバージョン、になるんですかね? 詳しい方どなたか教えてくださいorz*3

*1:完全にランダムにA群B群にサンプルを割り振って独立に介入を行った上でそれぞれの群の反応を比べるというもの

*2:典型的な交絡要因ですね

*3:なお某所でしましま先生からは「問題設定としては多腕バンディットにのっとっているので良いのでは、解法が勾配法っぽくて他では見かけない気がするが、トンプソンサンプリングとも違うような」という趣旨のコメントをいただきました