LoginSignup
6
7

More than 3 years have passed since last update.

因果推論でデジタル広告のCPA・ROASを改善する方法

Posted at

記事の概要

現在、デジタル広告の成長率は著しい。インターネットに接続すると、
多くのページに広告が埋め込まれている。広告には必ず配信している
広告主がおり、多額の広告費用が使われている。

しかし、多くの広告主は費用を下げたいと考えているはずだ。
よって、今回はコンバージョン数を下げずに広告費を減らせる
可能性がある方法を記載する。

広告主が取り扱う広告の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値が計算できた。あとはこの値に従って広告を配信する
ユーザと停止するユーザを振り分ける。ただ、いきなり広告配信を取りやめる
のは勇気がいるので、徐々に予算を減らしていき総コンバージョン数が
変わらないことを検証しながら運用していくのだ。

機械学習はマーケティングの分野にも確実に応用されていき、クリエイティブ
出ない分野はどんどん効率化されていくだろう。

6
7
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
6
7