マシンラーニングで生成した予測モデルを使用して傾向スコアを決定する
Adobe Experience Platform Query Serviceを利用すれば、マシンラーニング(機械学習)基盤に構築された傾向スコアなどの予測モデルを活用して、Experience Platformのデータを分析できます。
このガイドでは、クエリサービスを使用して機械学習プラットフォームにデータを送信し、計算ノートブックでモデルをトレーニングする方法を説明します。 トレーニングされたモデルをSQLを使用してデータに適用し、各訪問に対する顧客の購買傾向を予測できます。
はじめに
このプロセスの一環として、マシンラーニングモデルをトレーニングする必要があります。このドキュメントでは、1つ以上のマシンラーニング環境に関する実用的な知識を前提としています。
この例では、Jupyter Notebookを開発環境として使用しています。 利用可能なオプションは多数ありますが、Jupyter Notebookは計算要件が低いオープンソースのweb アプリケーションであるため、お勧めします。 公式サイトから ダウンロードできます。
まだ実行していない場合は、このガイドを続行する前に、Adobe Experience Platform クエリサービス と Jupyter Notebook 接続する手順に従ってください。
この例で使用されるライブラリには、次のものが含まれます。
python=3.6.7
psycopg2
sklearn
pandas
matplotlib
numpy
tqdm
Experience PlatformからJupyter Notebookに分析テーブルをインポートします import-analytics-tables
傾向スコアモデルを生成するには、Experience Platformに保存されている分析データの投影をJupyter Notebookに読み込む必要があります。 次のコマンドは、Query Serviceに接続されたPython 3 Jupyter Notebookから、架空の衣料品店であるLumaから顧客行動データセットを読み込みます。 Experience Platform データはExperience Data Model (XDM)フォーマットを使用して保存されるため、スキーマの構造に準拠したサンプル JSON オブジェクトを生成する必要があります。 サンプル JSON オブジェクトを生成する方法については、ドキュメントを参照してください。
出力には、Jupyter Notebook ダッシュボード内のLumaの行動データセットのすべての列の表形式のビューが表示されます。
マシンラーニング用のデータ準備 prepare-data-for-machine-learning
マシンラーニングモデルをトレーニングするには、ターゲット列を特定する必要があります。 購入傾向がこのユースケースの目標であるため、Lumaの結果からターゲット列としてanalytic_action列が選択されます。 値productPurchaseは、顧客購入の指標です。 purchase_value列とpurchase_num列は、製品購入アクションに直接関連しているため、削除されます。
これらのアクションを実行するためのコマンドは次のとおりです。
#define the target label for prediction
df['target'] = (df['analytic_action'] == 'productPurchase').astype(int)
#remove columns that are dependent on the label
df.drop(['analytic_action','purchase_value'],axis=1,inplace=True)
次に、Luma データセットのデータを適切な表現に変換する必要があります。 次の2つの手順が必要です。
- 数値を表す列を数値列に変換します。 これを行うには、
dataframeのデータ型を明示的に変換します。 - また、カテゴリ列を数値列に変換することもできます。
#convert columns that represent numbers
num_cols = ['purchase_num', 'value_cart', 'value_lifetime']
df[num_cols] = df[num_cols].apply(pd.to_numeric, errors='coerce')
1回のホットエンコーディングと呼ばれる手法を使用して、カテゴリデータ変数を変換し、マシンラーニングとディープラーニングのアルゴリズムで使用します。 その結果、予測が向上し、モデルの分類精度も向上します。 Sklearn ライブラリを使用して、各カテゴリ値を別の列で表します。
from sklearn.preprocessing import OneHotEncoder
#get the categorical columns
cat_columns = list(set(df.columns) - set(num_cols + ['target']))
#get the dataframe with categorical columns only
df_cat = df.loc[:,cat_columns]
#initialize sklearn's OneHotEncoder
enc = OneHotEncoder(handle_unknown='ignore')
#fit the data into the encoder
enc.fit(df_cat)
#define OneHotEncoder's columns names
ohc_columns = [[c+'='+c_ for c_ in cat] for c,cat in zip(cat_columns,enc.categories_)]
ohc_columns = [item for sublist in ohc_columns for item in sublist]
#finalize the data input to the ML models
X = pd.DataFrame( np.concatenate((enc.transform(df_cat).toarray(),df[num_cols]),axis=1),
columns = ohc_columns + num_cols)
#define target column
y = df['target']
Xとして定義されたデータは表形式で、次のように表示されます。
機械学習に必要なデータが利用可能になったので、Pythonのsklearn ライブラリにある事前設定済みの機械学習モデルに適合できます。 Logistics Regressionは傾向モデルのトレーニングに使用され、テスト データの精度を確認できます。 この場合、約85%です。
マシンラーニングアルゴリズムのパフォーマンスを推定するために使用されるLogistic Regression アルゴリズムとtrain-testの分割方法は、次のコードブロックに読み込まれます。
from sklearn.linear_model import LogisticRegression
from sklearn.model_selection import train_test_split
X_train, X_test, y_train, y_test = train_test_split(
X, y, test_size=0.33, random_state=42)
clf = LogisticRegression(max_iter=2000, random_state=0).fit(X_train, y_train)
print("Test data accuracy: {}".format(clf.score(X_test, y_test)))
テストデータの精度は0.8518518518518519です。
ロジスティクス回帰を使用すると、購入理由を可視化し、降順でのランク付けされた重要度によって傾向を決定する機能を並べ替えることができます。 最初の列は、購買行動につながる高い因果関係を示しています。 後者の列は、購買行動につながらない要因を示しています。
結果を2つの棒グラフとして視覚化するコードは次のとおりです。
from matplotlib import pyplot as plt
#get feature importance as a sorted list of columns
feature_importance = np.argsort(-clf.coef_[0])
top_10_features_purchase_names = X.columns[feature_importance[:10]]
top_10_features_purchase_values = clf.coef_[0][feature_importance[:10]]
top_10_features_not_purchase_names = X.columns[feature_importance[-10:]]
top_10_features_not_purchase_values = clf.coef_[0][feature_importance[-10:]]
#plot the figures
fig, (ax1, ax2) = plt.subplots(1, 2,figsize=(10,5))
ax1.bar(np.arange(10),top_10_features_purchase_values)
ax1.set_xticks(np.arange(10))
ax1.set_xticklabels(top_10_features_purchase_names,rotation = 90)
ax1.set_ylim([np.min(clf.coef_[0])-0.1,np.max(clf.coef_[0])+0.1])
ax1.set_title("Top 10 features to define \n a propensity to purchase")
ax2.bar(np.arange(10),top_10_features_not_purchase_values, color='#E15750')
ax2.set_xticks(np.arange(10))
ax2.set_xticklabels(top_10_features_not_purchase_names,rotation = 90)
ax2.set_ylim([np.min(clf.coef_[0])-0.1,np.max(clf.coef_[0])+0.1])
ax2.set_title("Top 10 features to define \n a propensity to NOT purchase")
plt.show()
結果の縦棒グラフのビジュアライゼーションは次のとおりです。
棒グラフからいくつかのパターンを見分けることができます。 チャネルのPOS (販売時点情報管理)と償還としての通話トピックは、購買行動を決定する最も重要な要因です。 一方、「苦情や請求書としてのコール」トピックは、購入以外の行動を定義するための重要な役割です。 これらは、マーケターが顧客の購入傾向に対処するためのマーケティング施策の実施に活用できる、定量化可能で実用的なインサイトです。
学習したモデルを適用するためにクエリサービスを使用します use-query-service-to-apply-trained-model
トレーニングされたモデルを作成したら、Experience Platformに保持されているデータに適用する必要があります。 これを行うには、マシンラーニングパイプラインのロジックをSQLに変換する必要があります。 この移行の2つの重要な要素は次のとおりです。
-
まず、予測ラベルの可能性を取得するには、SQLがLogistics Regression モジュールの代わりに使用する必要があります。 ロジスティクス回帰によって作成されたモデルは、重み
wと切片cがモデルの出力となる回帰モデルy = wX + cを生成しました。 SQLの特徴を使用して、重みを乗算して確率を得ることができます。 -
次に、1つのホットエンコーディングでPythonで達成されたエンジニアリングプロセスもSQLに組み込む必要があります。 例えば、元のデータベースでは、郡を格納する
geo_county列がありますが、列はgeo_county=Bexar、geo_county=Dallas、geo_county=DeKalbに変換されます。 次のSQL ステートメントは同じ変換を実行します。ここでは、w1、w2およびw3は、Pythonでモデルから学習した重みで置き換えることができます。
SELECT CASE WHEN geo_state = 'Bexar' THEN FLOAT(w1) ELSE 0 END AS f1,
CASE WHEN geo_state = 'Dallas' THEN FLOAT(w2) ELSE 0 END AS f2,
CASE WHEN geo_state = 'Bexar' THEN FLOAT(w3) ELSE 0 END AS f3,
数値の特徴については、以下のSQL ステートメントに示すように、列に重みを直接掛けることができます。
SELECT FLOAT(purchase_num) * FLOAT(w4) AS f4,
数値を取得した後、それらをシグモイド関数に取り込み、ロジスティクス回帰アルゴリズムが最終的な予測を生成します。以下のステートメントでは、interceptは回帰におけるインターセプトの数です。
SELECT CASE WHEN 1 / (1 + EXP(- (f1 + f2 + f3 + f4 + FLOAT(intercept)))) > 0.5 THEN 1 ELSE 0 END AS Prediction;
エンドツーエンドの例
2つの列(c1とc2)がある場合、c1に2つのカテゴリがある場合、Logistic Regression アルゴリズムは次の関数でトレーニングされます。
y = 0.1 * "c1=category 1"+ 0.2 * "c1=category 2" +0.3 * c2+0.4
SQLの同等の値は次のとおりです。
SELECT
CASE WHEN 1 / (1 + EXP(- (f1 + f2 + f3 + FLOAT(0.4)))) > 0.5 THEN 1 ELSE 0 END AS Prediction
FROM
(
SELECT
CASE WHEN c1 = 'Cateogry 1' THEN FLOAT(0.1) ELSE 0 END AS f1,
CASE WHEN c1 = 'Cateogry 2' THEN FLOAT(0.2) ELSE 0 END AS f2,
FLOAT(c2) * FLOAT(0.3) AS f3
FROM TABLE
)
翻訳プロセスを自動化するPython コードは次のとおりです。
def generate_lr_inference_sql(ohc_columns, num_cols, clf, db):
features_sql = []
category_sql_text = "case when {col} = '{val}' then float({coef}) else 0 end as f{name}"
numerical_sql_text = "float({col}) * float({coef}) as f{name}"
for i, (column, coef) in enumerate(zip(ohc_columns+num_cols, clf.coef_[0])):
if i < len(ohc_columns):
col,val = column.split('=')
val = val.replace("'","%''%")
sql = category_sql_text.format(col=col,val=val,coef=coef,name=i+1)
else:
sql = numerical_sql_text.format(col=column,coef=coef,name=i+1)
features_sql.append(sql)
features_sum = '+'.join(['f{}'.format(i) for i in range(1,len(features_sql)+1)])
final_sql = '''
select case when 1/(1 + EXP(-({features} + float({intercept})))) > 0.5 then 1 else 0 end as Prediction
from
(select {cols}
from {db})
'''.format(features=features_sum,cols=",".join(features_sql),intercept=clf.intercept_[0],db=db)
return final_sql
SQLを使用してデータベースを推測する場合、出力は次のようになります。
sql = generate_lr_inference_sql(ohc_columns, num_cols, clf, "fdu_luma_raw")
cur.execute(sql)
samples = [r for r in cur]
colnames = [desc[0] for desc in cur.description]
pd.DataFrame(samples,columns=colnames)
表形式の結果には、各顧客セッションの購入傾向が表示され、0は購入傾向がないことを意味し、1は購入傾向が確認されたことを意味します。
サンプリングされたデータの操作:ブートストラップ working-on-sampled-data
ローカルマシンがモデルのトレーニング用のデータを保存するにはデータサイズが大きすぎる場合は、Query Serviceの完全なデータの代わりにサンプルを取得できます。 Query Serviceからサンプルする必要があるデータの量を把握するには、ブートストラップと呼ばれる手法を適用します。 この点において、ブートストラップとは、モデルが様々なサンプルで複数回トレーニングされ、異なるサンプル間でのモデルの精度の変動が検査されることを意味します。 上記の傾向モデルの例を調整するには、まず、マシンラーニングのワークフロー全体を関数にカプセル化します。 コードは次のとおりです。
def end_to_end_pipeline(df):
#define the target label for prediction
df['target'] = (df['analytic_action'] == 'productPurchase').astype(int)
#remove columns that are dependent on the label
df.drop(['analytic_action','purchase_value'],axis=1,inplace=True)
num_cols = ['purchase_num','value_cart','value_lifetime']
df[num_cols] = df[num_cols].apply(pd.to_numeric, errors='coerce')
#get the categorical columns
cat_columns = list(set(df.columns) - set(num_cols + ['target']))
#get the dataframe with categorical columns only
df_cat = df.loc[:,cat_columns]
#initialize sklearn's One Hot Encoder
enc = OneHotEncoder(handle_unknown='ignore')
#fit the data into the encoder
enc.fit(df_cat)
#define one hot encoder's columns names
ohc_columns = [[c+'='+c_ for c_ in cat] for c,cat in zip(cat_columns,enc.categories_)]
ohc_columns = [item for sublist in ohc_columns for item in sublist]
#finalize the data input to the ML models
X = pd.DataFrame( np.concatenate((enc.transform(df_cat).toarray(),df[num_cols]),axis=1),
columns = ohc_columns + num_cols)
#define target column
y = df['target']
X_train, X_test, y_train, y_test = train_test_split(
X, y, test_size=0.33, random_state=42)
clf = LogisticRegression(max_iter=2000,random_state=0).fit(X_train, y_train)
return clf.score(X_test, y_test)
この関数は、ループ内で複数回実行できます(例:10回)。 前のコードとの違いは、サンプルがテーブル全体から取得されるのではなく、行のサンプルのみが取得されることです。 例えば、以下のサンプルコードは1000行のみを取ります。 各反復の正確さは保存できます。
from tqdm import tqdm
bootstrap_accuracy = []
for i in tqdm(range(100)):
#sample data from QS
cur.execute('''SELECT *
FROM fdu_luma_raw
ORDER BY random()
LIMIT 1000
''')
samples = [r for r in cur]
colnames = [desc[0] for desc in cur.description]
df_samples = pd.DataFrame(samples,columns=colnames)
df_samples.fillna(0,inplace=True)
#train the propensity model with sampled data and output its accuracy
bootstrap_accuracy.append(end_to_end_pipeline(df_samples))
bootstrap_accuracy = np.sort(bootstrap_accuracy)
次に、ブートストラップモデルの精度をソートします。 その後、モデルの精度の10番目と90番目の分位数は、指定されたサンプルサイズでのモデルの精度に対する95%の信頼区間になります。
上記の図では、モデルのトレーニングに1,000行のみを使用する場合、精度が約84%~88%に低下することが予想されます。 必要に応じてクエリサービスクエリのLIMIT句を調整し、モデルのパフォーマンスを確保できます。