記事の概要
現在、デジタル広告の成長率は著しい。インターネットに接続すると、
多くのページに広告が埋め込まれている。広告には必ず配信している
広告主がおり、多額の広告費用が使われている。
しかし、多くの広告主は費用を下げたいと考えているはずだ。
よって、今回はコンバージョン数を下げずに広告費を減らせる
可能性がある方法を記載する。
広告主が取り扱う広告のKPI
広告主は主に以下の指標でデジタル広告を評価する。
CVs = 訪問ユーザ数(全部) x コンバージョン率
Cost = 訪問ユーザ数(広告経由) x クリック単価
Revenue = 訪問ユーザ数(全部) x コンバージョン率 x 単価 x 購入回数
CPA = Cost / Cvs
ROAS = Revenue / Cost
訪問ユーザ数(広告経由) x クリック単価が広告費用となる。
これを下げれば、CPA・ROASも改善する仕組みだ。
広告の貢献度の評価
広告の分析にはアトリビューションというものがあり、広告の貢献度を図るために定義された指標だ。
購入の直前に触れた広告のみを評価するラストクリックや、購入までの最初に触れた広告のみを評価する
ファーストクリックが一般的だ。
https://anagrams.jp/blog/basic-of-attribution/
アトリビューション分析によって広告媒体のCPAやROASを計測し、どの時点で貢献しているかが可視化できる。
ただ、これだけでは広告の貢献度を図るのは難しい。なぜなら、広告に触れた
ユーザがコンバージョンしても、広告がきっかけなのかはわからない。
つまり、「広告に触れなくても」コンバージョンしていた可能性がある。
因果推論によるターゲット分類
ここで使用するのが因果推論だ。因果推論は簡潔に言うと「原因」と
「結果」を明らかにする手法だ。例えば医療業界で言うと、「この薬を
飲んだから(原因)、病気が治った(結果)」、ということを検証するために
利用される。以下の事例がわかりやすい。
https://healthpolicyhealthecon.com/2014/09/30/study-design-overview/
広告で言うと「広告に触れて(原因)、商品を購入した(結果)」と
置き換えられる。因果推論を使用する場合、「ある広告に触れたユーザ」と
「ある広告に触れなかったユーザ」が、それぞれ「コンバージョンしたか否か」
のデータを収集する。
このデータを因果推論のモデルに学習させ、テストデータで予測させると、
ターゲットを以下のように分類できる。
① あまのじゃくユーザ:広告を配信すると、コンバージョンしなくなる。
② 無関心ユーザ:広告を配信有無に関わらず、コンバージョンしない。
③ 確実購入ユーザ:広告を配信有無に関わらず、コンバージョンする。
④ 説得可能ユーザ:広告を配信すると、コンバージョンする。
例えば、モデルの予測で④の数が多ければ、広告がユーザを説得して
いることになるので、それは配信するべき広告となる。
しかし、①②③の数が多い場合は広告を配信しなくてもユーザは
コンバージョンするし、コンバージョンを阻害している可能性もある。
CPA・ROASを改善するために
ここまででわかる通り、因果推論でCPA・ROASを改善する方法は以下だ。
・因果推論モデルを用いてターゲットを分類する。
・説得可能ユーザが多い広告の出稿を増やすか、配信不要ユーザが多い広告の
出稿を減らす。
この流れをPythonで実装してみる。
1. モジュールインポート
# Main imports
from econml.metalearners import TLearner, SLearner, XLearner, DomainAdaptationLearner, DoublyRobustLearner
from sklearn.linear_model import LinearRegression
from sklearn.ensemble import RandomForestRegressor, RandomForestClassifier, GradientBoostingRegressor
from sklearn.model_selection import train_test_split
# Helper imports
import os, sys
import numpy as np
import pandas as pd
from numpy.random import binomial, multivariate_normal, normal, uniform
import matplotlib.pyplot as plt
2.データセットの作成
データは以下から取得。BigQueryに存在するGoogleAnalyticsの
公開データセットで、サイトへのアクセス情報だけでなく広告経由の流入も
格納されている。既にダウンロードして、results_bqga.csvにまとめている。
https://bigquery.cloud.google.com/table/bigquery-public-data:google_analytics_sample.ga_sessions_20170801
os.listdir("../input")
df_bqga = pd.read_csv("../input/results_bqga.csv")
# 欠損値の確認
df_bqga.isnull().sum()
total_visits 0
total_transaction 890508
campaign 0
source 0
medium 0
fullVisitorId 0
visitNumber 0
# 欠損値を0に変換
df_bqga["total_transaction"] = df_bqga["total_transaction"].apply(lambda x : 0 if x != x else x)
# ユーザ、参照元、流入経路、キャンペーンでグルーピング
cols = ["fullVisitorId", "source", "medium", "campaign", "visitNumber", "total_visits"]
df_bqga_grp = df_bqga.groupby(by=cols).sum().reset_index()
3. CATE計算のためのデータ作成
CATEは「ある特徴量で条件付けた際の介入の因果効果の期待値」と言える。
つまりCATEが高いユーザは広告を付与した場合の効果が高いので、広告を配信するべきという結論が得られる。主に3つのデータが必要である。
・Outcome(Y) = 目的変数でコンバージョンを達成したか、売上高など
・Treatment(T) = 広告やクーポンなど、アクションに触れたか否か
・Feature(X)= サイト訪問数や属性など、ユーザの特徴
以下のコードで上記のデータを作成している。
# Treatmentの作成(広告に触れたか否か)
df_bqga_grp["Treatment"] = df_bqga_grp.medium.apply(lambda x : 1 if x in ["cpc", "cpm", "affiliate"] else 0)
# Outcomeの作成(購入したか否か)
df_bqga_grp["Outcome"] = df_bqga_grp.total_transaction.apply(lambda x : 0 if x == 0 else 1)
# Featureの作成(自然検索で訪問したか、再訪問したか、何回訪問したか)
df_bqga_grp["is_organic"] = df_bqga_grp.medium.apply(lambda x : 1 if x == "organic" else 0)
df_bqga_grp["is_return"] = df_bqga_grp.visitNumber.apply(lambda x : 0 if x == 1 else 1)
df_bqga_grp = pd.get_dummies(data=df_bqga_grp, columns=["total_visits"])
# 不要な列の作成
df_bqga_grp.drop(columns=["source", "medium", "campaign", "visitNumber" ,"total_transaction"], axis=1, inplace=True)
df_data = df_bqga_grp.groupby(by=["fullVisitorId"]).sum().reset_index()
# 全データを0 or 1に変換
df_data.iloc[:, 1] = df_data.iloc[:, 1].apply(lambda x : 0 if x == 0 else 1)
df_data.iloc[:, 2] = df_data.iloc[:, 2].apply(lambda x : 0 if x == 0 else 1)
df_data.iloc[:, 3] = df_data.iloc[:, 3].apply(lambda x : 0 if x == 0 else 1)
df_data.iloc[:, 4] = df_data.iloc[:, 4].apply(lambda x : 0 if x == 0 else 1)
df_data.iloc[:, 5] = df_data.iloc[:, 5].apply(lambda x : 0 if x == 0 else 1)
df_data.iloc[:, 6] = df_data.iloc[:, 6].apply(lambda x : 0 if x == 0 else 1)
df_data.iloc[:, 7] = df_data.iloc[:, 7].apply(lambda x : 0 if x == 0 else 1)
# 不均衡データの修正
df_case1 = df_data[(df_data["Treatment"] == 1) & (df_data["Outcome"] == 1)]
df_case2 = df_data[(df_data["Treatment"] == 1) & (df_data["Outcome"] == 0)].iloc[0:1000, :]
df_case3 = df_data[(df_data["Treatment"] == 0) & (df_data["Outcome"] == 1)].iloc[0:1000, :]
df_case4 = df_data[(df_data["Treatment"] == 0) & (df_data["Outcome"] == 0)].iloc[0:1000, :]
4. データ分割
トレーニング&テストデータに分割
train_df, test_df = train_test_split(df_data, test_size=0.2, random_state=0, stratify=df_data['Treatment'])
# ユーザIDを抽出しておき、データセットからはユーザIDを削除
user_ids = test_df.fullVisitorId
train_df.drop(["fullVisitorId"], inplace=True, axis=1)
test_df.drop(["fullVisitorId"], inplace=True, axis=1)
5. CATEの計算と予測(ここからが本題)
先ほど作成したデータから広告を配信するべきユーザ=CATE値が高い
ユーザを算出する。今回はeconmlを用いて実行する。
モジュールの詳細を知りたい方は以下を参照してほしい。
https://github.com/microsoft/EconML
EconMLとは?
EconMLはMicrosoftが作成したパッケージで、計量経済学と機械学習を
融合させたもの。このツールを用いて、意思決定を自動化することが
最終目的らしい。今回はEconMLを用いてユーザ毎のCATE値を計算してみる。
データの投入
# 学習に必要なデータを投入
T = train_df.Treatment
Y = train_df.Outcome
X = train_df.drop(["Outcome", "Treatment"], axis=1)
# テスト用データにはユーザの特徴のみを投入しておく
X_test = test_df.drop(["Outcome", "Treatment"], axis=1)
モデル学習
EconMLには様々な計算用アルゴリズムが用意されている。その中の一つで
Meta-Learnersパッケージの中からDR-Learnerのアルゴリズムを使用して
学習する。
# Instantiate Doubly Robust Learner
outcome_model = GradientBoostingRegressor(n_estimators=100, max_depth=6)
pseudo_treatment_model = GradientBoostingRegressor(n_estimators=100, max_depth=6)
propensity_model = RandomForestClassifier(n_estimators=100, max_depth=6,
class_weight='balanced_subsample')
DR_learner = DoublyRobustLearner(outcome_model=outcome_model, pseudo_treatment_model=pseudo_treatment_model,
propensity_model=propensity_model)
# Train DR_learner
DR_learner.fit(Y, T, X)
CATE値の予測
最後にEconMLでCATE値を予測して、ユーザIDと並べてみる。
# Estimate treatment effects on test data
DR_te = DR_learner.effect(X_test)
df_cate = pd.DataFrame({"user-id" : user_ids, "cate" : DR_te})
df_cate.head()
user-id cate
2.121750e+15 -0.188887
2.766490e+17 -0.086540
2.252240e+15 -0.137244
1.563470e+15 -0.188887
1.677000e+15 0.597261
CATEを計算した後
これでユーザ毎のCATE値が計算できた。あとはこの値に従って広告を配信する
ユーザと停止するユーザを振り分ける。ただ、いきなり広告配信を取りやめる
のは勇気がいるので、徐々に予算を減らしていき総コンバージョン数が
変わらないことを検証しながら運用していくのだ。
機械学習はマーケティングの分野にも確実に応用されていき、クリエイティブ
出ない分野はどんどん効率化されていくだろう。