銀行は、歴史的に大量の構造化データが収集されてきた幸運な分野の一つであり、データサイエンス技術が最初に適用された分野の一つでもあります。
では、銀行ではデータサイエンスがどのように使われているのでしょうか。今日では、この領域においてデータは最も価値のある資産となっています。銀行が競合に歩調を合わせ、より多くの顧客を惹きつけ、既存顧客のロイヤルティを高め、より効率的なデータドリブンの意思決定を行い、事業を強化し、業務効率を高め、既存のサービス/製品を改善して新たな提供を実現し、セキュリティを強化し、その結果として収益を増やすためには、データサイエンスが不可欠です。データサイエンスの求人需要の大半が銀行から生じているのも不思議ではありません。
データサイエンスにより、銀行業界は以下のような数多くのタスクをうまく遂行できます。
- 投資リスク分析
- 顧客生涯価値(CLV)の予測
- 顧客セグメンテーション
- 顧客離反率の予測
- パーソナライズド・マーケティング
- 顧客感情分析
- バーチャルアシスタントとチャットボット
以下では、銀行における最も一般的なデータサイエンスのユースケースの一つを詳しく見ていきます。
銀行におけるデータサイエンスのユースケース:不正検知
不正行為は、銀行だけでなく、政府、保険、公共部門、販売、ヘルスケアなど多くの分野で難しい課題となっています。オンライン取引が多いビジネスは、重大な不正リスクを抱えます。金融犯罪には、クレジットカードの不正利用、小切手の偽造、脱税、マネーロンダリング、サイバー攻撃、顧客アカウントの窃取、合成アイデンティティ、虚偽申請、詐欺など、さまざまな形態があります。
不正検知とは、不正行為と金銭的損失を特定・防止するための能動的な取り組みの総称です。主な分析手法は次の2つに大別されます。
- 統計手法:統計パラメータの算出、回帰、確率分布、データマッチング
- 人工知能(AI):データマイニング、機械学習、ディープラーニング
機械学習は不正検知の重要な柱です。そのツールキットには2つのアプローチがあります。
- 教師あり手法:k 近傍法、ロジスティック回帰、サポートベクターマシン、決定木、ランダムフォレスト、時系列分析、ニューラルネットワークなど
- 教師なし手法:クラスタ分析、リンク分析、自己組織化マップ、主成分分析、異常検知など
不正検知に万能で信頼できる機械学習アルゴリズムは存在しません。実務のユースケースでは、複数の手法やその組み合わせを試し、モデルの予測精度を算出して、最適なアプローチを選択するのが一般的です。
不正検知システムの主な課題は、絶えず変化する不正パターンや手口に素早く適応し、ますます巧妙化する新たなスキームを迅速に見抜くことです。不正の事例は常に少数派であり、実際の取引の中に巧妙に紛れています。
データセットの準備
ここでは、Python を使ってクレジットカード不正検知の機械学習実装を試してみます。使用する creditcard_data データセットは、Kaggle のCredit Card Fraud Detectionのサンプルを加工したものです。元データは、2013年9月の2日間にヨーロッパのカード保有者が行ったクレジットカードの取引を表しています。
データをインポートして、ざっと確認してみましょう。
import pandas as pd
creditcard_data = pd.read_csv('creditcard_data.csv', index_col=0)
print(creditcard_data.info())
print('\n')
pd.options.display.max_columns = len(creditcard_data)
print(creditcard_data.head(3))
<class 'pandas.core.frame.DataFrame'>
Int64Index: 5050 entries, 0 to 5049
Data columns (total 30 columns):
# Column Non-Null Count Dtype
--- ------ -------------- -----
0 V1 5050 non-null float64
1 V2 5050 non-null float64
2 V3 5050 non-null float64
3 V4 5050 non-null float64
4 V5 5050 non-null float64
5 V6 5050 non-null float64
6 V7 5050 non-null float64
7 V8 5050 non-null float64
8 V9 5050 non-null float64
9 V10 5050 non-null float64
10 V11 5050 non-null float64
11 V12 5050 non-null float64
12 V13 5050 non-null float64
13 V14 5050 non-null float64
14 V15 5050 non-null float64
15 V16 5050 non-null float64
16 V17 5050 non-null float64
17 V18 5050 non-null float64
18 V19 5050 non-null float64
19 V20 5050 non-null float64
20 V21 5050 non-null float64
21 V22 5050 non-null float64
22 V23 5050 non-null float64
23 V24 5050 non-null float64
24 V25 5050 non-null float64
25 V26 5050 non-null float64
26 V27 5050 non-null float64
27 V28 5050 non-null float64
28 Amount 5050 non-null float64
29 Class 5050 non-null int64
dtypes: float64(29), int64(1)
memory usage: 1.2 MB
V1 V2 V3 V4 V5 V6 V7 \
0 1.725265 -1.337256 -1.012687 -0.361656 -1.431611 -1.098681 -0.842274
1 0.683254 -1.681875 0.533349 -0.326064 -1.455603 0.101832 -0.520590
2 1.067973 -0.656667 1.029738 0.253899 -1.172715 0.073232 -0.745771
V8 V9 V10 V11 V12 V13 V14 \
0 -0.026594 -0.032409 0.215113 1.618952 -0.654046 -1.442665 -1.546538
1 0.114036 -0.601760 0.444011 1.521570 0.499202 -0.127849 -0.237253
2 0.249803 1.383057 -0.483771 -0.782780 0.005242 -1.273288 -0.269260
V15 V16 V17 V18 V19 V20 V21 \
0 -0.230008 1.785539 1.419793 0.071666 0.233031 0.275911 0.414524
1 -0.752351 0.667190 0.724785 -1.736615 0.702088 0.638186 0.116898
2 0.091287 -0.347973 0.495328 -0.925949 0.099138 -0.083859 -0.189315
V22 V23 V24 V25 V26 V27 V28 \
0 0.793434 0.028887 0.419421 -0.367529 -0.155634 -0.015768 0.010790
1 -0.304605 -0.125547 0.244848 0.069163 -0.460712 -0.017068 0.063542
2 -0.426743 0.079539 0.129692 0.002778 0.970498 -0.035056 0.017313
Amount Class
0 189.00 0
1 315.17 0
2 59.98 0
このデータセットには次の変数が含まれています。
- 数値化された V1〜V28 は、PCA 変換で得られた主成分です。機密保持のため、元の特徴量に関する背景情報は提供されていません。
- Amount 変数は取引金額を表します。
- Class 変数は、取引が不正(1)か否か(0)を示します。
不正発生は本質的に、どの取引リストでも幸い少数です。しかし、機械学習アルゴリズムは、データセット内の各クラスがある程度均等に含まれているときに最も良く機能するのが一般的です。そうでないと、学習できるデータがほとんどありません。この問題はクラス不均衡と呼ばれます。
データセット内の不正の計算
データセット内の総取引数に対する不正取引の割合を計算してみましょう。
round(creditcard_data['Class'].value_counts()*100/len(creditcard_data)).convert_dtypes()
0 99
1 1
Name: Class, dtype: Int64
そして、不正と正常のデータ点を可視化するプロットを作成します。
import matplotlib.pyplot as plt
import numpy as np
def prep_data(df):
X = df.iloc[:, 1:28]
X = np.array(X).astype(float)
y = df.iloc[:, 29]
y = np.array(y).astype(float)
return X, y
def plot_data(X, y):
plt.scatter(X[y==0, 0], X[y==0, 1], label='Class #0', alpha=0.5, linewidth=0.15)
plt.scatter(X[y==1, 0], X[y==1, 1], label='Class #1', alpha=0.5, linewidth=0.15, c='r')
plt.legend()
return plt.show()
X, y = prep_data(creditcard_data)
plot_data(X, y)

SMOTE を用いたデータの再バランス
ここまでで、不正取引の比率が非常に低く、クラス不均衡の問題があることを確認できました。これを是正するには、SMOTE(Synthetic Minority Over-sampling Technique、合成少数クラス過サンプリング手法)を用いてデータを再バランスできます。ランダムなオーバーサンプリングと異なり、SMOTE は単に観測値の完全なコピーを作るのではありません。不正事例の最近傍の特徴を用いて、新しい合成サンプルを生成し、少数クラスの既存観測値にかなり近いサンプルを作ります。では、クレジットカードデータに SMOTE を適用してみましょう。
from imblearn.over_sampling import SMOTE
method = SMOTE()
X_resampled, y_resampled = method.fit_resample(X, y)
plot_data(X_resampled, y_resampled)

ご覧のとおり、SMOTE によって少数クラスの観測値が一気に増えました。さらに結果をわかりやすくするため、元データと比較してみます。
def compare_plot(X, y, X_resampled, y_resampled, method):
f, (ax1, ax2) = plt.subplots(1, 2)
c0 = ax1.scatter(X[y==0, 0], X[y==0, 1], label='Class #0',alpha=0.5)
c1 = ax1.scatter(X[y==1, 0], X[y==1, 1], label='Class #1',alpha=0.5, c='r')
ax1.set_title('Original set')
ax2.scatter(X_resampled[y_resampled==0, 0], X_resampled[y_resampled==0, 1], label='Class #0', alpha=.5)
ax2.scatter(X_resampled[y_resampled==1, 0], X_resampled[y_resampled==1, 1], label='Class #1', alpha=.5,c='r')
ax2.set_title(method)
plt.figlegend((c0, c1), ('Class #0', 'Class #1'), loc='lower center', ncol=2, labelspacing=0.)
plt.tight_layout(pad=3)
return plt.show()
print(f'Original set:\n'
f'{pd.value_counts(pd.Series(y))}\n\n'
f'SMOTE:\n'
f'{pd.value_counts(pd.Series(y_resampled))}\n')
compare_plot(X, y, X_resampled, y_resampled, method='SMOTE')
Original set:
0.0 5000
1.0 50
dtype: int64
SMOTE:
0.0 5000
1.0 5000
dtype: int64

このように、SMOTE によってデータは完全にバランスされ、少数クラスは多数クラスと同じサイズになりました。
SMOTE の実践的な適用には後ほど戻るとして、まずは元のデータに立ち返り、不正事例の検知を試みます。「オールドスクール」なやり方では、不正を捕捉するルールを作成する必要があります。例えば、取引場所の異常や取引頻度の不自然な多さなどに関するルールです。一般的な統計、しばしば観測値の平均に基づいてしきい値を設定し、そのしきい値を特徴量に適用して不正を検知する、という考え方です。
print(creditcard_data.groupby('Class').mean().round(3)[['V1', 'V3']])
V1 V3
Class
0 0.035 0.037
1 -4.985 -7.294
このケースでは、V1 < -3 かつ V3 < -5 という条件を適用してみます。次に、このアプローチの性能を評価するため、フラグ付けされた不正事例と実際の不正を比較します。
creditcard_data['flag_as_fraud'] = np.where(np.logical_and(creditcard_data['V1']<-3, creditcard_data['V3']<-5), 1, 0)
print(pd.crosstab(creditcard_data['Class'], creditcard_data['flag_as_fraud'], rownames=['Actual Fraud'], colnames=['Flagged Fraud']))
Flagged Fraud 0 1
Actual Fraud
0 4984 16
1 28 22
ロジスティック回帰の適用
50件の不正のうち22件を検知できましたが、残り28件は見逃し、誤検知も16件発生しました。機械学習手法を使えば、これらの結果を上回れるか見てみましょう。
ここでは、クレジットカードデータに単純なロジスティック回帰の分類アルゴリズムを実装して不正発生を識別し、その結果を混同行列で可視化します。
from sklearn.model_selection import train_test_split
from sklearn.linear_model import LogisticRegression
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.3, random_state=0)
lr = LogisticRegression()
lr.fit(X_train, y_train)
predictions = lr.predict(X_test)
print(pd.crosstab(y_test, predictions, rownames=['Actual Fraud'], colnames=['Flagged Fraud']))
Flagged Fraud 0.0 1.0
Actual Fraud
0.0 1504 1
1.0 1 9
ここで混同行列の観測数が少ないのは、モデル結果の計算にテストセット、すなわちデータ全体の30%のみを使用しているためである点に注意してください。
捕捉できた不正の割合は 90%(10件中9件)で、先ほどの 44%(50件中22件)よりも高くなりました。誤検知も大幅に減っており、改善が見られます。
続いて、先に述べたクラス不均衡の問題に立ち戻り、ロジスティック回帰モデルと SMOTE によるリサンプリングを組み合わせることで、予測結果をさらに向上できるか検討します。効率的に一度で実行するため、パイプラインを定義してデータに適用します。
from imblearn.pipeline import Pipeline
# Defining which resampling method and which ML model to use in the pipeline
resampling = SMOTE()
lr = LogisticRegression()
pipeline = Pipeline([('SMOTE', resampling), ('Logistic Regression', lr)])
pipeline.fit(X_train, y_train)
predictions = pipeline.predict(X_test)
print(pd.crosstab(y_test, predictions, rownames=['Actual Fraud'], colnames=['Flagged Fraud']))
Flagged Fraud 0.0 1.0
Actual Fraud
0.0 1496 9
1.0 1 9
ご覧のとおり、今回のケースでは SMOTE による改善は見られませんでした。依然として不正の 90% を捕捉できていますが、誤検知がやや増えています。これは、リサンプリングが常に良い結果をもたらすとは限らないためです。不正事例がデータ全体に広く分散している場合、その最近傍が必ずしも不正とは限らず、SMOTE の使用によりバイアスが入り得ます。
まとめ
次の一手として、ロジスティック回帰モデルの精度を高めるために、アルゴリズムの各種パラメータをチューニングする方法が考えられます。データセットを単純に二分割する代わりに、K 分割交差検証を検討してもよいでしょう。最後に、他の機械学習アルゴリズム(例:決定木やランダムフォレスト)も試し、より良い結果が得られるか確認することができます。
不正検知モデルの理論面・技術面についてさらに学ぶには、Fraud Detection in Python コースの教材を参照してください。
