【Python】機械学習で営業活動が成功し易い顧客を予測してみた

こんにちは、のっくんです。

今日は機械学習を使って営業活動(マーケティング)が成功するか予測するためのコードを書いていきたいと思います。

年齢や学歴、結婚の有無、年間の支出、持ち家の有無などの個人情報から、銀行口座を開設したか(yes or no)を与えて機械学習を行うことで、統計的に営業活動が成功しやすい人としにくい人をAIで判別します。

収入が無い人に銀行口座の開設を進めても意味がないことは人間でも分かりますが、機械学習では更に高度な複数の要素を分析するのが得意です。

予めAIで営業活動が成功しやすいクライアントを予想することで、営業チームはクライアントを絞って営業活動をすることができるので無駄な営業を省略しやすくなります。

企業にとっては無駄なリソースの削減ができ、集中して有望なクライアントにフォーカスできるので営業成績が上がることも間違いありません。

それでは実際にどうやってやるのか見ていきましょう。

データセット

使用するデータは以下の通り。

UCIマシンラーニングレポジトリからデータを取得します。

アカウント作成をしなくても自由に誰でもダウンロードできます。

https://archive.ics.uci.edu/ml/datasets/bank+marketing

データ数は45211で、属性は17個です。

データが提供されたのが2012年2月14日です。

以下が属性の1部です。

1 - age (numeric)
2 - job : type of job (categorical: 'admin.','blue-collar','entrepreneur','housemaid','management','retired','self-employed','services','student','technician','unemployed','unknown')
3 - marital : marital status (categorical: 'divorced','married','single','unknown'; note: 'divorced' means divorced or widowed)
4 - education (categorical: 'basic.4y','basic.6y','basic.9y','high.school','illiterate','professional.course','university.degree','unknown')
5 - default: has credit in default? (categorical: 'no','yes','unknown')
6 - housing: has housing loan? (categorical: 'no','yes','unknown')
7 - loan: has personal loan? (categorical: 'no','yes','unknown')
  • 年齢
  • 仕事
  • 結婚
  • 学歴
  • 債務履行の有無
  • 持ち家の有無
  • ローンの有無

ダウンロードしたデータのうち、bank_full.csvファイルを主に使っていきます。

データの読み込み

pandasにデータを読み込むときには`read_csv()`を使います。

import pandas as pd 
df = pd.read_csv('bank-full.csv', delimiter=';', decimal=',')
df.head()

出力はこんな感じです。

属性情報の数が多いですね。

余談ですが、コードを書くときにはGoogle Colaboratoryを使っています。

Pandasの表がダークでカッコいいです。

Google Colabを使うとコーディングがクラウドでできるので、家でも会社でもコードがかけてとても便利です。

データの前処理

欠損したデータがないか確認します。

欠損地の有無は`isnull()`を使うと算出できます。

df.isnull().sum()
age          0
job          0
marital      0
education    0
default      0
balance      0
housing      0
loan         0
contact      0
day          0
month        0
duration     0
campaign     0
pdays        0
previous     0
poutcome     0
y            0
dtype: int64

欠損データは無さそうですね。

良いデータセットです。

次に学習に必要なさそうな属性データ(過去のキャンペーン活動の日付など)を削除します。

df.drop(['contact',"day","month","duration","campaign","pdays","previous","poutcome"], axis=1, inplace=True)
df.head()

うん、シンプルになりました。

ちなみに、アメリカの学歴は、

  • primary education, 小学校
  • secondary education, 中学〜高校
  • tertiary education, 大学、職業専門学校

となっています。

tertiary(ターシャリ)って何だろうと思って調べたら、「第3の」という意味らしいです。

3番目なので高校かと思いましたが、高校はセカンダリーになるみたい。

今回はこの8個の属性から、最後のy(定期預金の開設をしたかどうか)を予測するモデルを作っていきたいと思います。

ここで各カラムの型を見てみましょう。

型情報を得るには、`dtypes`で確認します。

df.dtypes
age           int64
job          object
marital      object
education    object
default      object
balance       int64
housing      object
loan         object
y            object
dtype: object

「age」と「balance」以外は、objectという抽象的なものになっています。

object型はラベルなどの文字列を指す場合が多いです。

機械学習では、基本的に数値(整数型(int)もしくは浮動小数点数型(float))を扱うのでobject型を整数型に変更する必要があります。

pandasの`factorize()`を使って整数型に変換します。

ラベルが例えば”yes”と”no”であれば、それぞれ1,0に変更します。

obuject型のカラム名をリストに含めて、繰り返し数値に変換するようにコードを書きます。

# ラベルを全て数値に変換する
li = ["job","marital","education","default","housing","loan","y"]

for column in li:
    labels, uniques = pd.factorize(df[column])
    df[column] = labels
age          int64
job          int64
marital      int64
education    int64
default      int64
balance      int64
housing      int64
loan         int64
y            int64
dtype: object

これでデータの準備が整いました。

機械学習

SVM

SVMを使って機械学習を行ってみます。

from sklearn.model_selection import train_test_split
from sklearn import datasets,svm,metrics
from sklearn.metrics import accuracy_score,classification_report

y = df['y']
X = df.drop(columns='y')
  
# データを学習用とテスト用に分割する
X_train,X_test,y_train,y_test = train_test_split(X,y,test_size=0.2)
 
# データを学習
clf = svm.LinearSVC()
clf.fit(X_train, y_train)
 
# 予測して精度を確認する
y_pred = clf.predict(X_test)
print(accuracy_score(y_test, y_pred))

print(classification_report(y_test,y_pred))

`accuracy_score`で正解率を求めています。

classification_reportでは、学習機がどのようにクラス分けしたかを表示してくれます。

0.885104500718788
              precision    recall  f1-score   support

           0       0.89      1.00      0.94      8004
           1       0.00      0.00      0.00      1039

    accuracy                           0.89      9043
   macro avg       0.44      0.50      0.47      9043
weighted avg       0.78      0.89      0.83      9043

/usr/local/lib/python3.6/dist-packages/sklearn/svm/base.py:929: ConvergenceWarning: Liblinear failed to converge, increase the number of iterations.
  "the number of iterations.", ConvergenceWarning)
/usr/local/lib/python3.6/dist-packages/sklearn/metrics/classification.py:1437: UndefinedMetricWarning: Precision and F-score are ill-defined and being set to 0.0 in labels with no predicted samples.
  'precision', 'predicted', average, warn_for)

正解率を見てみると、88.5%でした。

高精度、やったね!と喜びたいところですが、`classification_report’の内容を見ると1のprecisionが0.00になっています。

precisionというのは、精度を意味します。

つまり、学習器が0と予想した中で実際に0だった割合が89%、1と予想した中で実際に1だった割合が0%でした。

これでは実際に分類できているとは言えません。

例えば、0が5000個、1が100個あったとします。

全部のデータに対して0と答えれば、5000/5100 = 98%の割合で正解します。

これと同じことが今回の学習で起こっているかもしれません。

確認のために、ラベルの割合を見てみます。

# ラベルの数を表示する
y_no = len(y[y==0])
y_yes = len(y[y==1])
print([y_no],[y_yes])
[39922] [5289]

マーケティング活動を行って、実際に銀行口座を開いてくれた人は僅かです。

ほとんどの人は開設していません。

つまり負例(0)が多く正例(1)が少ない状態です。

全て0と答えれば、39922/(39922+5289) =  88.3% で正解します。

このような学習データは釣り合っていないので不均衡データと呼ばれます。

オーバーサンプリング

今回ような極端に正例が少ない学習データに対してはオーバーサンプリングが有効です。

オーバーサンプリングには、SMOTEと呼ばれる手法を使います。

“SMOTE: synthetic minority over-sampling technique”は2002年の機械学習ジャーナルで発表されている実績のある手法です。

Pythonでは、imblearnパッケージの中にありますのでimportすることで使えるようになります。

from imblearn.over_sampling import SMOTE

#正例が少なすぎるのでオーバーサンプリングして正例の数を増やす
sm=SMOTE(ratio='auto', kind='regular')
X_sampled,y_sampled=sm.fit_sample(X,y)

Sampled_no = len(y_sampled[y_sampled==0])
Sampled_yes = len(y_sampled[y_sampled==1])
print([Sampled_no],[Sampled_yes])
[39922] [39922]

正例と負例の数が同じになりました。

再度学習をかけてみます。

# データを学習用とテスト用に分割する
X_train,X_test,y_train,y_test = train_test_split(X_sampled,y_sampled,test_size=0.2)
 
# データを学習
clf = svm.LinearSVC()
clf.fit(X_train, y_train)
 
# 予測して精度を確認する
y_pred = clf.predict(X_test)
print(accuracy_score(y_test, y_pred))

print(classification_report(y_test,y_pred))
0.5216356691088985
              precision    recall  f1-score   support

           0       0.52      0.43      0.47      7896
           1       0.52      0.61      0.56      8073

    accuracy                           0.52     15969
   macro avg       0.52      0.52      0.52     15969
weighted avg       0.52      0.52      0.52     15969

52%の精度でした。

2値分類なので当てずっぽうでも50%は達成できます。

もう少し精度が欲しいところですね。

ランダムフォレスト

今度はオーバーサンプリングしたデータに対してランダムフォレストを適用してみます。

from sklearn.ensemble import RandomForestClassifier

rfc = RandomForestClassifier(n_estimators=40)
rfc.fit(X_train, y_train)
 
# 予測して精度を確認する
y_pred = rfc.predict(X_test)
print(accuracy_score(y_test, y_pred))
print(classification_report(y_test,y_pred))
0.8288559083223745
              precision    recall  f1-score   support

           0       0.84      0.80      0.82      7896
           1       0.82      0.85      0.83      8073

    accuracy                           0.83     15969
   macro avg       0.83      0.83      0.83     15969
weighted avg       0.83      0.83      0.83     15969

82%まで精度が上がりました。

今回の学習データでは、SVMよりもランダムフォレストの方が精度が高いことが分かりました。

今回学習したモデルを使えば、お客様の個人情報からマーケティングが成功する可能性が高いお客様かどうかを判別することができます。

AIが判定したお客様にフォーカスして営業活動すれば成績が上がること間違いなしですね。

終わり。

参考

Machine Learning Classification with Python for Direct Marketing:

https://towardsdatascience.com/machine-learning-classification-with-python-for-direct-marketing-2da27906ddac