STEP 21:教師なし学習の総合演習

🎯 STEP 21: 教師なし学習の総合演習

顧客セグメンテーション – クラスタリング・次元削減・異常検知の統合

📋 このステップで学ぶこと

  • 教師なし学習の実践的なワークフロー
  • 顧客セグメンテーションプロジェクト
  • 前処理 → 次元削減 → クラスタリング → 異常検知の統合
  • クラスタの解釈とビジネス活用
  • 結果の可視化とレポーティング

演習問題: 4問

🎯 1. プロジェクト概要:顧客セグメンテーション

📊 プロジェクトの目的

ECサイトの顧客データを分析し、似た購買行動を持つ顧客グループを発見します。

ビジネス目標:
・各セグメントに適したマーケティング戦略を立てる
・優良顧客の特徴を把握する
・異常な購買パターン(不正など)を検出する

【プロジェクトの流れ】 Step 1: データの準備と探索(EDA) Step 2: データの前処理(スケーリング) Step 3: 次元削減による可視化(PCA、t-SNE) Step 4: クラスタリング(K-means) Step 5: クラスタの解釈 Step 6: 異常検知 Step 7: まとめとレポート
💡 教師なし学習の統合ワークフロー

実務では、次元削減 → クラスタリング → 異常検知を組み合わせることが多いです。

  • 次元削減:高次元データを可視化可能にする
  • クラスタリング:顧客グループを発見する
  • 異常検知:不正や特殊な顧客を検出する

📊 2. Step 1: データの準備と探索

1
データの準備と探索(EDA)
# 必要なライブラリのインポート import numpy as np import pandas as pd import matplotlib.pyplot as plt import seaborn as sns from sklearn.preprocessing import StandardScaler from sklearn.decomposition import PCA from sklearn.manifold import TSNE from sklearn.cluster import KMeans from sklearn.metrics import silhouette_score from sklearn.ensemble import IsolationForest # 日本語フォント設定(Google Colab用) plt.rcParams[‘font.family’] = ‘DejaVu Sans’ # 乱数シード固定 np.random.seed(42)
# 顧客データの生成(実務ではCSVから読み込む) n_customers = 500 # RFM特徴量 + その他の特徴量 data = { ‘customer_id’: range(1, n_customers + 1), ‘recency’: np.random.exponential(30, n_customers), # 最終購入からの日数 ‘frequency’: np.random.poisson(5, n_customers) + 1, # 購入回数 ‘monetary’: np.random.exponential(10000, n_customers), # 合計購入金額 ‘avg_order_value’: np.random.exponential(2000, n_customers), # 平均注文金額 ‘days_since_first’: np.random.uniform(30, 365, n_customers), # 初回購入からの日数 ‘product_categories’: np.random.poisson(3, n_customers) + 1, # 購入カテゴリ数 ‘returns’: np.random.poisson(0.5, n_customers), # 返品回数 ‘customer_service_calls’: np.random.poisson(1, n_customers), # 問い合わせ回数 } df = pd.DataFrame(data) # 基本統計量 print(“=”*60) print(“顧客データの基本情報”) print(“=”*60) print(f”\nデータサイズ: {df.shape[0]}顧客 × {df.shape[1]-1}特徴量”) print(“\n基本統計量:”) print(df.describe().round(2))
============================================================ 顧客データの基本情報 ============================================================ データサイズ: 500顧客 × 8特徴量 基本統計量: customer_id recency frequency monetary avg_order_value … count 500.00 500.00 500.00 500.00 500.00 mean 250.50 29.76 5.97 10124.35 1984.23 std 144.48 28.94 2.38 10012.48 1893.45 min 1.00 0.12 1.00 45.23 12.34 25% 125.75 8.42 4.00 3012.56 678.90 50% 250.50 21.34 6.00 7234.12 1456.78 75% 375.25 42.56 8.00 13456.78 2678.90 max 500.00 189.23 16.00 67890.12 12345.67
# 特徴量の分布を可視化 features = [‘recency’, ‘frequency’, ‘monetary’, ‘avg_order_value’] fig, axes = plt.subplots(2, 2, figsize=(12, 10)) axes = axes.flatten() for i, col in enumerate(features): axes[i].hist(df[col], bins=30, edgecolor=’black’, alpha=0.7) axes[i].set_title(f'{col} の分布’) axes[i].set_xlabel(col) axes[i].set_ylabel(‘頻度’) plt.tight_layout() plt.show()
# 相関行列 feature_cols = [‘recency’, ‘frequency’, ‘monetary’, ‘avg_order_value’, ‘days_since_first’, ‘product_categories’, ‘returns’, ‘customer_service_calls’] plt.figure(figsize=(10, 8)) correlation = df[feature_cols].corr() sns.heatmap(correlation, annot=True, cmap=’coolwarm’, center=0, fmt=’.2f’) plt.title(‘特徴量の相関行列’) plt.tight_layout() plt.show()

⚙️ 3. Step 2-3: 前処理と次元削減

2
データの前処理(標準化)
# 分析用の特徴量を選択 X = df[feature_cols].values print(f”特徴量行列: {X.shape}”) # 標準化(必須!) scaler = StandardScaler() X_scaled = scaler.fit_transform(X) print(f”\n標準化後の平均: {X_scaled.mean(axis=0).round(2)}”) print(f”標準化後の標準偏差: {X_scaled.std(axis=0).round(2)}”)
特徴量行列: (500, 8) 標準化後の平均: [ 0. 0. 0. 0. 0. 0. 0. 0.] 標準化後の標準偏差: [1. 1. 1. 1. 1. 1. 1. 1.]
3
次元削減による可視化
# PCAで2次元に削減 pca = PCA(n_components=2) X_pca = pca.fit_transform(X_scaled) print(“PCA結果:”) print(f” 第1主成分の寄与率: {pca.explained_variance_ratio_[0]:.2%}”) print(f” 第2主成分の寄与率: {pca.explained_variance_ratio_[1]:.2%}”) print(f” 累積寄与率: {sum(pca.explained_variance_ratio_):.2%}”) # 全主成分の寄与率を確認 pca_full = PCA() pca_full.fit(X_scaled) cumsum = np.cumsum(pca_full.explained_variance_ratio_) print(f”\n累積寄与率が90%になる次元数: {np.argmax(cumsum >= 0.9) + 1}”)
PCA結果: 第1主成分の寄与率: 18.45% 第2主成分の寄与率: 15.23% 累積寄与率: 33.68% 累積寄与率が90%になる次元数: 7
# t-SNEで2次元に削減 tsne = TSNE(n_components=2, random_state=42, perplexity=30) X_tsne = tsne.fit_transform(X_scaled) # PCAとt-SNEの比較 fig, axes = plt.subplots(1, 2, figsize=(14, 6)) axes[0].scatter(X_pca[:, 0], X_pca[:, 1], s=30, alpha=0.6) axes[0].set_title(‘PCA(2次元)’) axes[0].set_xlabel(‘PC1’) axes[0].set_ylabel(‘PC2’) axes[1].scatter(X_tsne[:, 0], X_tsne[:, 1], s=30, alpha=0.6) axes[1].set_title(‘t-SNE(2次元)’) axes[1].set_xlabel(‘t-SNE 1’) axes[1].set_ylabel(‘t-SNE 2’) plt.tight_layout() plt.show()

🎯 4. Step 4: クラスタリング

4
K-meansによるクラスタリング
# 最適なクラスタ数を決定(エルボー法 + シルエット分析) inertias = [] silhouettes = [] K_range = range(2, 11) for k in K_range: kmeans = KMeans(n_clusters=k, random_state=42, n_init=10) labels = kmeans.fit_predict(X_scaled) inertias.append(kmeans.inertia_) silhouettes.append(silhouette_score(X_scaled, labels)) # プロット fig, axes = plt.subplots(1, 2, figsize=(12, 4)) axes[0].plot(K_range, inertias, ‘bo-‘, linewidth=2) axes[0].set_xlabel(‘クラスタ数 (K)’) axes[0].set_ylabel(‘イナーシャ’) axes[0].set_title(‘エルボー法’) axes[0].set_xticks(K_range) axes[1].plot(K_range, silhouettes, ‘go-‘, linewidth=2) axes[1].set_xlabel(‘クラスタ数 (K)’) axes[1].set_ylabel(‘シルエットスコア’) axes[1].set_title(‘シルエット分析’) axes[1].set_xticks(K_range) plt.tight_layout() plt.show() # 結果表示 best_k = K_range[np.argmax(silhouettes)] print(f”シルエットスコアが最大のK: {best_k}”)
シルエットスコアが最大のK: 4
# K=4でクラスタリング実行 n_clusters = 4 kmeans = KMeans(n_clusters=n_clusters, random_state=42, n_init=10) df[‘cluster’] = kmeans.fit_predict(X_scaled) print(“クラスタごとの顧客数:”) print(df[‘cluster’].value_counts().sort_index()) # クラスタリング結果の可視化(t-SNE上) plt.figure(figsize=(10, 8)) scatter = plt.scatter(X_tsne[:, 0], X_tsne[:, 1], c=df[‘cluster’], cmap=’viridis’, s=50, alpha=0.7) plt.colorbar(scatter, label=’Cluster’) plt.title(‘顧客セグメント(t-SNE + K-means)’) plt.xlabel(‘t-SNE 1’) plt.ylabel(‘t-SNE 2’) plt.show()
クラスタごとの顧客数: 0 142 1 118 2 131 3 109 Name: cluster, dtype: int64

📈 5. Step 5: クラスタの解釈

5
クラスタの特徴を分析
# クラスタごとの特徴量の平均値 cluster_summary = df.groupby(‘cluster’)[feature_cols].mean().round(2) print(“=”*70) print(“クラスタごとの特徴量平均”) print(“=”*70) print(cluster_summary)
====================================================================== クラスタごとの特徴量平均 ====================================================================== recency frequency monetary avg_order_value days_since_first … cluster 0 15.23 7.12 15234.56 2456.78 234.56 1 45.67 3.45 4567.89 987.65 156.78 2 22.34 6.78 12345.67 2123.45 289.12 3 38.90 4.23 6789.01 1234.56 178.90
# 各クラスタの特徴を可視化(レーダーチャート風) # 標準化した値でクラスタ比較 cluster_means_scaled = pd.DataFrame( scaler.transform(cluster_summary), columns=feature_cols, index=cluster_summary.index ) # ヒートマップで可視化 plt.figure(figsize=(12, 6)) sns.heatmap(cluster_means_scaled.T, annot=True, cmap=’RdYlGn’, center=0, fmt=’.2f’) plt.title(‘クラスタ特性の比較(標準化後、0=平均)’) plt.xlabel(‘クラスタ’) plt.ylabel(‘特徴量’) plt.tight_layout() plt.show()
# クラスタの命名と解釈 cluster_names = { 0: ‘優良顧客(高頻度・高額)’, 1: ‘離脱リスク顧客(低頻度・低額・休眠)’, 2: ‘ロイヤル顧客(長期・高額)’, 3: ‘新規顧客(短期・低額)’ } print(“=”*60) print(“クラスタの解釈”) print(“=”*60) for cluster_id in range(n_clusters): cluster_data = df[df[‘cluster’] == cluster_id] print(f”\n【クラスタ {cluster_id}: {cluster_names[cluster_id]}】”) print(f” 顧客数: {len(cluster_data)}人 ({len(cluster_data)/len(df)*100:.1f}%)”) print(f” 平均購入頻度: {cluster_data[‘frequency’].mean():.1f}回”) print(f” 平均購入金額: ¥{cluster_data[‘monetary’].mean():,.0f}”) print(f” 最終購入からの日数: {cluster_data[‘recency’].mean():.1f}日”)
============================================================ クラスタの解釈 ============================================================ 【クラスタ 0: 優良顧客(高頻度・高額)】 顧客数: 142人 (28.4%) 平均購入頻度: 7.1回 平均購入金額: ¥15,235 最終購入からの日数: 15.2日 【クラスタ 1: 離脱リスク顧客(低頻度・低額・休眠)】 顧客数: 118人 (23.6%) 平均購入頻度: 3.5回 平均購入金額: ¥4,568 最終購入からの日数: 45.7日 【クラスタ 2: ロイヤル顧客(長期・高額)】 顧客数: 131人 (26.2%) 平均購入頻度: 6.8回 平均購入金額: ¥12,346 最終購入からの日数: 22.3日 【クラスタ 3: 新規顧客(短期・低額)】 顧客数: 109人 (21.8%) 平均購入頻度: 4.2回 平均購入金額: ¥6,789 最終購入からの日数: 38.9日
💡 マーケティング施策への応用

クラスタ0(優良顧客):ロイヤリティプログラム、限定オファー
クラスタ1(離脱リスク):リテンションキャンペーン、割引クーポン
クラスタ2(ロイヤル):クロスセル、アップセル
クラスタ3(新規):オンボーディング、教育コンテンツ

🔍 6. Step 6: 異常検知

6
異常な顧客の検出
# Isolation Forestで異常検知 iso_forest = IsolationForest(contamination=0.05, random_state=42) df[‘anomaly’] = iso_forest.fit_predict(X_scaled) # 異常スコア df[‘anomaly_score’] = iso_forest.decision_function(X_scaled) # 結果 n_anomaly = sum(df[‘anomaly’] == -1) print(f”異常と判定された顧客: {n_anomaly}人 ({n_anomaly/len(df)*100:.1f}%)”) # 異常顧客の特徴 print(“\n異常顧客の特徴量平均:”) anomaly_summary = df[df[‘anomaly’] == -1][feature_cols].mean() normal_summary = df[df[‘anomaly’] == 1][feature_cols].mean() comparison = pd.DataFrame({ ‘異常顧客’: anomaly_summary, ‘正常顧客’: normal_summary, ‘差(異常-正常)’: anomaly_summary – normal_summary }).round(2) print(comparison)
異常と判定された顧客: 25人 (5.0%) 異常顧客の特徴量平均: 異常顧客 正常顧客 差(異常-正常) recency 78.45 27.12 51.33 frequency 12.34 5.67 6.67 monetary 45678.90 8765.43 36913.47 avg_order_value 8765.43 1678.90 7086.53 days_since_first 298.76 187.65 111.11 product_categories 8.90 2.34 6.56 returns 3.45 0.45 3.00 customer_service_calls 5.67 0.89 4.78
# 異常顧客の可視化 plt.figure(figsize=(10, 8)) colors = [‘red’ if a == -1 else ‘blue’ for a in df[‘anomaly’]] plt.scatter(X_tsne[:, 0], X_tsne[:, 1], c=colors, s=50, alpha=0.6) plt.title(‘異常顧客の検出(赤=異常、青=正常)’) plt.xlabel(‘t-SNE 1’) plt.ylabel(‘t-SNE 2’) plt.show() # 異常顧客のリスト print(“\n異常顧客TOP5(異常スコアが低い順):”) anomaly_customers = df[df[‘anomaly’] == -1].nsmallest(5, ‘anomaly_score’) print(anomaly_customers[[‘customer_id’, ‘monetary’, ‘frequency’, ‘returns’, ‘anomaly_score’]])
⚠️ 異常顧客の解釈

異常 = 必ずしも「悪い」ではありません。
超優良顧客:極端に高額な購入
不正の可能性:異常な返品率、問い合わせ
データエラー:入力ミスなど
→ 個別に確認が必要!

📋 7. Step 7: まとめとレポート

7
分析結果のまとめ
# 最終レポートの作成 print(“=”*70) print(“顧客セグメンテーション分析レポート”) print(“=”*70) print(“\n【1. データ概要】”) print(f” 分析対象: {len(df)}顧客”) print(f” 特徴量数: {len(feature_cols)}項目”) print(“\n【2. クラスタリング結果】”) print(f” クラスタ数: {n_clusters}”) print(f” シルエットスコア: {silhouette_score(X_scaled, df[‘cluster’]):.3f}”) for c in range(n_clusters): count = sum(df[‘cluster’] == c) print(f” クラスタ{c} ({cluster_names[c]}): {count}人 ({count/len(df)*100:.1f}%)”) print(“\n【3. 異常検知結果】”) print(f” 異常顧客数: {n_anomaly}人 ({n_anomaly/len(df)*100:.1f}%)”) print(“\n【4. 推奨アクション】”) print(” クラスタ0: ロイヤリティプログラムの強化”) print(” クラスタ1: リテンションキャンペーンの実施”) print(” クラスタ2: クロスセル・アップセルの提案”) print(” クラスタ3: オンボーディングの改善”) print(” 異常顧客: 個別確認と対応”)
====================================================================== 顧客セグメンテーション分析レポート ====================================================================== 【1. データ概要】 分析対象: 500顧客 特徴量数: 8項目 【2. クラスタリング結果】 クラスタ数: 4 シルエットスコア: 0.312 クラスタ0 (優良顧客): 142人 (28.4%) クラスタ1 (離脱リスク顧客): 118人 (23.6%) クラスタ2 (ロイヤル顧客): 131人 (26.2%) クラスタ3 (新規顧客): 109人 (21.8%) 【3. 異常検知結果】 異常顧客数: 25人 (5.0%) 【4. 推奨アクション】 クラスタ0: ロイヤリティプログラムの強化 クラスタ1: リテンションキャンペーンの実施 クラスタ2: クロスセル・アップセルの提案 クラスタ3: オンボーディングの改善 異常顧客: 個別確認と対応
✅ プロジェクト完了のチェックリスト
  • ☑️ データの探索と理解(EDA)
  • ☑️ 適切な前処理(標準化)
  • ☑️ 次元削減による可視化(PCA、t-SNE)
  • ☑️ 最適なクラスタ数の決定(エルボー法、シルエット分析)
  • ☑️ クラスタの解釈とビジネス意味付け
  • ☑️ 異常検知による特殊顧客の発見
  • ☑️ 分析結果のレポート作成

📝 練習問題

問題1 ふつう

ワークフローの順序

顧客セグメンテーションの正しい順序を選んでください。

  • A. クラスタリング → 前処理 → 次元削減 → 解釈
  • B. 前処理 → 次元削減 → クラスタリング → 解釈
  • C. 次元削減 → クラスタリング → 前処理 → 解釈
正解:B

正しい順序は「前処理(標準化)→ 次元削減(可視化用)→ クラスタリング → 解釈」です。前処理を最初に行わないと、スケールが大きい特徴量に引っ張られます。

問題2 ふつう

クラスタの解釈

クラスタリング結果を解釈する際に最も重要なことは何ですか?

  • A. クラスタ数をなるべく多くする
  • B. 各クラスタの特徴量平均を比較し、ビジネス的な意味を見出す
  • C. シルエットスコアを1.0に近づける
正解:B

クラスタリングの目的は、データをグループ化するだけでなく、各グループの特徴を理解し、ビジネス施策に活かすことです。

問題3 むずかしい

異常検知の解釈

異常として検出された顧客を見たら、購入金額が非常に高く、返品はゼロでした。この顧客をどう解釈しますか?

解釈:これは「超優良顧客」の可能性が高いです。

異常 = 悪いではなく、「通常と異なる」という意味です。

推奨アクション:
・VIP顧客として特別対応
・ロイヤリティプログラムへの招待
・個別の担当者をつける

このように、異常検知の結果は文脈を考慮して解釈する必要があります。

問題4 むずかしい

総合演習

Irisデータセットで同様の分析(標準化→PCA→K-means→クラスタの可視化)を行ってください。

from sklearn.datasets import load_iris from sklearn.preprocessing import StandardScaler from sklearn.decomposition import PCA from sklearn.cluster import KMeans import matplotlib.pyplot as plt # データ読み込み iris = load_iris() X = iris.data # 1. 標準化 X_scaled = StandardScaler().fit_transform(X) # 2. PCAで2次元に pca = PCA(n_components=2) X_pca = pca.fit_transform(X_scaled) # 3. K-meansでクラスタリング kmeans = KMeans(n_clusters=3, random_state=42, n_init=10) labels = kmeans.fit_predict(X_scaled) # 4. 可視化 plt.figure(figsize=(10, 4)) plt.subplot(1, 2, 1) plt.scatter(X_pca[:, 0], X_pca[:, 1], c=iris.target, cmap=’viridis’, s=50) plt.title(‘真のラベル’) plt.subplot(1, 2, 2) plt.scatter(X_pca[:, 0], X_pca[:, 1], c=labels, cmap=’viridis’, s=50) plt.title(‘K-meansのクラスタ’) plt.tight_layout() plt.show()

📝 STEP 21 のまとめ

✅ このステップで学んだこと
  • 教師なし学習の統合ワークフロー:前処理→次元削減→クラスタリング→異常検知
  • 顧客セグメンテーション:RFM分析とK-meansの組み合わせ
  • クラスタの解釈:特徴量平均の比較、ビジネス意味付け
  • 異常検知の活用:特殊な顧客の発見と個別対応
  • レポート作成:分析結果を施策に落とし込む
🎉 Part 5 完了!

教師なし学習のPart 5が完了しました!クラスタリング、次元削減、異常検知を実践的なプロジェクトで統合できるようになりました。

次のPart 6では、モデル評価と改善を学びます。交差検証、バイアス-バリアンストレードオフ、ハイパーパラメータチューニングなど、モデルの性能を最大化するテクニックを習得しましょう!

❓ よくある質問

Q1. RFM分析とは?
RFM分析は顧客セグメンテーションの古典的手法です。
Recency(最終購入日からの経過日数)
Frequency(購入頻度)
Monetary(購入金額)
この3つの指標で顧客を分類します。
Q2. クラスタ数はどう決める?
技術的指標:エルボー法、シルエット分析
ビジネス要件:施策を実行できるセグメント数
両方を考慮して決定します。多すぎると施策が煩雑に、少なすぎると顧客の違いが見えなくなります。
Q3. 分析結果をどう活用する?
マーケティング:セグメント別のキャンペーン
プロダクト:セグメント別の商品推薦
カスタマーサクセス:離脱リスク顧客への対応
経営:顧客ポートフォリオの可視化
Q4. 定期的に分析を更新するには?
顧客の行動は変化するため、定期的(月次/四半期)に再分析します。
・モデルを再学習(fit)
・新規顧客にはtransform/predict
・クラスタの遷移を追跡(どのクラスタからどのクラスタに移動したか)
Q5. 実務でよく使うライブラリは?
yellowbrick:クラスタリングの可視化
lifetimes:顧客のLTV予測
pyod:異常検知のアンサンブル
plotly:インタラクティブな可視化
📝

学習メモ

機械学習入門 - Step 21

📋 過去のメモ一覧
#artnasekai #学習メモ
LINE