Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
1614c71
Submit assignment for <your-student-id>
Rewrite-StarrySky May 28, 2025
e16dd19
Submit assignment for <your-student-id>
Rewrite-StarrySky May 28, 2025
f30f943
Submit assignment for ACS111851
Rewrite-StarrySky May 28, 2025
764056a
Merge pull request #2 from Rewrite-StarrySky/assignment-ACS111851
Rewrite-StarrySky May 28, 2025
fcc581d
Update and rename ex1-1.md to ex1.md
Rewrite-StarrySky May 28, 2025
4f6ab16
Update ex1.ipynb
Rewrite-StarrySky May 28, 2025
2048e4f
Delete acs111851_ex1/ex1-2.md
Rewrite-StarrySky May 28, 2025
841a978
Update ex1.md
Rewrite-StarrySky May 28, 2025
6011921
Update ex1.md
Rewrite-StarrySky May 28, 2025
45cfdce
Update ex1.md
Rewrite-StarrySky May 28, 2025
804c9f7
Update ex1.md
Rewrite-StarrySky May 28, 2025
404a792
Update ex1.md
Rewrite-StarrySky May 28, 2025
cb9b634
Update ex1.md
Rewrite-StarrySky May 28, 2025
698e8b4
Update ex1.md
Rewrite-StarrySky May 28, 2025
fc12ccf
Update ex1.md
Rewrite-StarrySky May 28, 2025
2e1b7af
Update ex1.md
Rewrite-StarrySky May 28, 2025
056d40f
Update ex1.md
Rewrite-StarrySky May 28, 2025
e3a151c
Update ex2.md 草稿未完全編寫完成
Rewrite-StarrySky May 28, 2025
eb8a547
Update ex1.md 草稿未完全編寫完成
Rewrite-StarrySky May 28, 2025
09a792e
Update ex1.md 草稿未完全編寫完成
Rewrite-StarrySky May 28, 2025
88d789f
Update ex2.ipynb 還在嘗試優化中還會更版 目前為當前調整的最佳結果
Rewrite-StarrySky May 28, 2025
3ec4eeb
Update ex1.ipynb
Rewrite-StarrySky May 29, 2025
85bf5f2
Update ex2.ipynb
Rewrite-StarrySky May 29, 2025
cf78ded
Delete ACS111851_ex2/ex2.ipynb
Rewrite-StarrySky May 29, 2025
6f67dc6
main
Rewrite-StarrySky May 29, 2025
e835532
Merge pull request #3 from Rewrite-StarrySky/assignment-ACS111851
Rewrite-StarrySky May 29, 2025
0a214a7
Update ex2.ipynb
Rewrite-StarrySky May 29, 2025
aef484f
Update ex2.ipynb
Rewrite-StarrySky May 29, 2025
0e808eb
Update ex2.md
Rewrite-StarrySky Jun 3, 2025
cfc8c38
Update ex2.md
Rewrite-StarrySky Jun 3, 2025
b79463b
Update ex2.md
Rewrite-StarrySky Jun 3, 2025
3f8675a
Update ex2.md
Rewrite-StarrySky Jun 3, 2025
2e8bde8
Update ex2.md
Rewrite-StarrySky Jun 3, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
170 changes: 170 additions & 0 deletions ACS111851_ex2/ex2.ipynb
Original file line number Diff line number Diff line change
@@ -0,0 +1,170 @@
{
"nbformat": 4,
"nbformat_minor": 0,
"metadata": {
"colab": {
"provenance": []
},
"kernelspec": {
"name": "python3",
"display_name": "Python 3"
},
"language_info": {
"name": "python"
}
},
"cells": [
{
"cell_type": "code",
"source": [
"#########################融合模型############################################\n",
"import numpy as np\n",
"import pandas as pd\n",
"import lightgbm as lgb\n",
"from sklearn.model_selection import train_test_split\n",
"from sklearn.preprocessing import StandardScaler\n",
"from sklearn.ensemble import RandomForestClassifier\n",
"from sklearn.metrics import classification_report\n",
"from sklearn.cluster import KMeans\n",
"from sklearn.metrics import silhouette_score, accuracy_score, precision_score, recall_score, f1_score, roc_auc_score, confusion_matrix, log_loss\n",
"import kagglehub\n",
"from sklearn.ensemble import IsolationForest\n",
"from sklearn.decomposition import PCA\n",
"import matplotlib.pyplot as plt\n",
"from xgboost import XGBClassifier, callback\n",
"import tensorflow as tf\n",
"\n",
"# general setting\n",
"RANDOM_SEED = 42\n",
"TEST_SIZE = 0.3\n",
"# 載入資料集(from kagglehub)\n",
"path = kagglehub.dataset_download(\"mlg-ulb/creditcardfraud\")\n",
"data = pd.read_csv(f\"{path}/creditcard.csv\")\n",
"data['Class'] = data['Class'].astype(int)\n",
"\n",
"# 準備數據\n",
"data = data.drop(['Time'], axis=1)\n",
"data['Amount'] = StandardScaler().fit_transform(data['Amount'].values.reshape(-1, 1))\n",
"\n",
"fraud = data[data['Class'] == 1]\n",
"nonfraud = data[data['Class'] == 0]\n",
"print(f'Fraudulent:{len(fraud)}, non-fraudulent:{len(nonfraud)}')\n",
"print(f'the positive class (frauds) percentage: {len(fraud)}/{len(fraud) + len(nonfraud)} ({len(fraud)/(len(fraud) + len(nonfraud))*100:.3f}%)')\n",
"\n",
"#from imblearn.over_sampling import SMOTE\n",
"\n",
"# Extract 特徵和標籤\n",
"X = np.asarray(data.drop(columns=['Class']))\n",
"y = np.asarray(data['Class'])\n",
"# y = data['Class'].astype(int).values\n",
"\n",
"# 分割資料集與訓練\n",
"x_train, x_test, y_train, y_test = train_test_split(\n",
" X, y, test_size=TEST_SIZE, random_state=RANDOM_SEED\n",
")\n",
"\n",
"# PCA 降維\n",
"pca = PCA(n_components=25) # 嘗試 2-20 的不同值\n",
"x_train = pca.fit_transform(x_train)\n",
"x_test = pca.transform(x_test)\n",
"\n",
"\n",
"\n",
"# 用正常交易訓練 Isolation Forest\n",
"iso = IsolationForest(\n",
" contamination=0.0017,\n",
" random_state=RANDOM_SEED,\n",
" n_estimators=500,\n",
" max_features=15,\n",
" # max_samples=0.8,\n",
" bootstrap=True\n",
" )\n",
"iso.fit(x_train)\n",
"\n",
"# 用全部資料做預測(正常/異常)\n",
"iso_labels = iso.predict(x_train)\n",
"iso_labels = (iso_labels == -1).astype(int)\n",
"\n",
"\n",
"x_train = np.hstack([x_train, iso_labels.reshape(-1, 1)])\n",
"iso_pred_test = iso.predict(x_test)\n",
"iso_feature_test = (iso_pred_test == -1).astype(int)\n",
"x_test = np.hstack((x_test, iso_feature_test.reshape(-1, 1)))\n",
"\n",
"\n",
"xgb_model = XGBClassifier(\n",
" objective='binary:logistic', #二元邏輯回歸用於二分類問題\n",
" n_estimators=500,\n",
" max_depth=7,\n",
" learning_rate=0.3,\n",
" scale_pos_weight=6,\n",
" random_state=RANDOM_SEED,\n",
")\n",
"\n",
"xgb_model.fit(x_train, y_train)\n",
"\n",
"\n",
"\n",
"#預測測試集\n",
"threshold = 0.4\n",
"y_proba = xgb_model.predict_proba(x_test)[:, 1] # 取得預測為正類(詐欺)的機率\n",
"y_pred = (y_proba > threshold).astype(int)\n",
"# y_pred = xg_model.predict(x_test)\n",
"\n",
"\n",
"def evaluation(y_true, y_pred, model_name=\"Model\"):\n",
" accuracy = accuracy_score(y_true, y_pred)\n",
" precision = precision_score(y_true, y_pred, zero_division=0)\n",
" recall = recall_score(y_true, y_pred)\n",
" f1 = f1_score(y_true, y_pred)\n",
"\n",
" print(f'\\n{model_name} Evaluation:')\n",
" print('===' * 15)\n",
" print(' Accuracy:', accuracy)\n",
" print(' Precision Score:', precision)\n",
" print(' Recall Score:', recall)\n",
" print(' F1 Score:', f1)\n",
" print(\"\\nClassification Report:\")\n",
" print(classification_report(y_true, y_pred))\n",
"\n",
"evaluation(y_test, y_pred, model_name=\"Hybrid Model\")"
],
"metadata": {
"colab": {
"base_uri": "https://localhost:8080/"
},
"id": "9UTvDhUWOM3Q",
"outputId": "5504c13c-d5be-4379-a189-79607be76e1f"
},
"execution_count": 13,
"outputs": [
{
"output_type": "stream",
"name": "stdout",
"text": [
"Fraudulent:492, non-fraudulent:284315\n",
"the positive class (frauds) percentage: 492/284807 (0.173%)\n",
"\n",
"Hybrid Model Evaluation:\n",
"=============================================\n",
" Accuracy: 0.9996957035684608\n",
" Precision Score: 0.9365079365079365\n",
" Recall Score: 0.8676470588235294\n",
" F1 Score: 0.9007633587786259\n",
"\n",
"Classification Report:\n",
" precision recall f1-score support\n",
"\n",
" 0 1.00 1.00 1.00 85307\n",
" 1 0.94 0.87 0.90 136\n",
"\n",
" accuracy 1.00 85443\n",
" macro avg 0.97 0.93 0.95 85443\n",
"weighted avg 1.00 1.00 1.00 85443\n",
"\n"
]
}
]
}
]
}
55 changes: 55 additions & 0 deletions ACS111851_ex2/ex2.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
# 融合監督與非監督學習以提升預測效果
目標結合非監督式學習方法(如 Isolation Forest)與監督式分類器(如 XGBoost)
以在高度不平衡的信用卡詐騙偵測任務中提升模型的預測表現
### 方法概覽
流程步驟 技術 功能
資料前處理 標準化(StandardScaler) 提升模型穩定性與收斂速度
非監督學習 Isolation Forest 初步標註潛在異常資料點,強化資料集的欺詐資訊密度
特徵融合 將異常分數加入特徵空間 擴充特徵維度,引入非監督知識
資料重取樣 SMOTE(合成少數類) 解決類別不平衡問題
監督學習 XGBoost + RandomizedSearchCV 高效的樹模型分類器,透過隨機搜尋優化超參數
評估與閾值優化 動態閾值調整 平衡 precision / recall,提升實用性
### 資料預處理
時間欄位剔除(Time):
此欄位與詐騙行為關聯性不明,且存在高度偏態,因此這邊移除了該欄位data。
### Isolation Forest(非監督式異常偵測)
為一種基於隨機劃分的非監督式異常偵測方法,特別適合處理高維數據。
它不需標籤資料,能有效挖掘潛在異常點,對於類別極度不平衡(如詐騙率<0.2%)的場景極具價值。
### 使用方式與參數邏輯:
contamination=0.005:
表示預期 0.5% 為異常點,與原始詐騙樣本比例接近(約 0.172%)。
n_jobs=-1:多執行緒加速。
random_state=42:確保可重現。
### 異常分數整合方式:
使用 decision_function() 得到每筆資料的異常程度(越低越異常)
取 前 5% 的異常樣本(分數最低) 進行標註與後續處理,可提升資料集中詐騙樣本比例。
將 iso_score 作為 額外特徵欄位 合併進原始特徵空間中,提供後續分類模型額外的非監督信號。
### XGBoost + RandomizedSearchCV(監督分類 + 超參數調優)
### XGBoost特性
適合處理非線性、高維稀疏資料,並內建針對不平衡資料的處理方式(scale_pos_weight)。
支援早停、內建特徵重要性排序、分布式訓練等功能,效能與準確率兼具。
### 嘗試隨機搜尋參數設計部分
這邊原本使用了隨機搜尋參數設計,來找尋最佳參數,後來跑了幾輪結果不盡理想
原始設定範圍內無法跑出最佳結果,只能近似於目標結果
所以只做了參考,基於之前跑的結果又重新手動調整參數,目的在調整出能超過結果的最佳參數
### 嘗試SMOTE部分
本來嘗試使用SMOTE去平衡資料集但多次出來結果沒有比較好
所以後來就刪除SMOTE平衡部分,改採用scale_pos_weight 權重調整
因為此資料集中詐騙樣本數較少,dataset也呈現高度不平衡的現象
所以調整設置了scale_pos_weight參數,提高詐騙樣本在資料集中的重要性權重
### 最後使用的參數設定
參數 設定值 參數定義
n_estimators [500] 提供足夠學習輪數以擬合複雜決策邊界
max_depth [7] 控制模型複雜度,過深可能過擬合
learning_rate [0.3] 探索不同收斂速度,避免 overshoot
scale_pos_weight [6] 對應類別不平衡程度,用以提高詐騙樣本的重要性權重

### 閾值調整
預設分類閾值為 0.5,對於偏態資料極度不適用,會導致 recall 過低
這邊改用0.4為設定值
特別是在銀行系統中,高 precision 代表較少誤報,低誤報率在實務上更重要
### 實驗總結與融合效益
IF + SMOTE + Tuned XGB 提升 0.94↑ 較高
利用非監督特徵強化、資料平衡與超參數調整
透過 Isolation Forest 初步篩選高風險交易,再透過 SMOTE 補強少數類數據、XGBoost 對複雜規則擬合與閾值微調

342 changes: 342 additions & 0 deletions acs111851_ex1/ex1.ipynb

Large diffs are not rendered by default.

145 changes: 145 additions & 0 deletions acs111851_ex1/ex1.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,145 @@
# 監督式學習
一開始先採用隨機森林來做參數調整,但測試了多組數據與超參數設置後發現結果不盡理想
難以全面超越原始數據結果
所以後來改採用 XGBoost做參數設置與調整
### 資料集分析
信用卡詐欺預測是一個 二元分類問題(fraud vs. non-fraud),且資料極度不平衡(fraud 約佔 0.17%)
這類問題的挑戰可分為以下兩點:
1.資料不平衡(少數類別重要)
2.權重差異大,容易導致過擬合或忽略少數類別
### 而 XGBoost 優點
針對不平衡分類問題的強化支援:scale_pos_weight 可自訂正負樣本重要性
梯度提升決策樹(GBDT)核心:具備優異的泛化能力
可調超參數較多元:可控制模型複雜度、防止過擬合等等
內建 early stopping 與 logloss 最佳化:適合處理詐欺偵測這類 precision/recall 驅動的任務
### 模型與參數設定說明
共使用了以下參數去做調整
xgb_model = XGBClassifier(
n_estimators=200,
max_depth=7,
learning_rate=0.1,
min_child_weight=1,
gamma=0.2,
subsample=0.9,
colsample_bytree=0.9,
scale_pos_weight=200,
random_state=RANDOM_SEED,
eval_metric='logloss'
### 各核心參數說明與調整邏輯
參數 作用 調整邏輯
n_estimators=200 樹的數量 初期設成中等值,避免過擬合
max_depth=7 每棵樹最大深度 控制模型複雜度,7 通常是中等偏高值
learning_rate=0.1 學習率 預設學習率,與樹數互補(越小要更多樹)
min_child_weight=1 最小葉節點權重和 設為 1,允許模型擷取細微差異
gamma=0.2 節點分裂的最小損失減益 抑制過度分裂(過擬合風險)
subsample=0.9 每棵樹訓練時使用的樣本比例 降低過擬合
colsample_bytree=0.9 每棵樹訓練時使用的特徵比例 降低特徵依賴性
scale_pos_weight=200 權重平衡(樣本不平衡) 根據「負樣本數 / 正樣本數」設定,大約 = 85307 / 136 ≈ 627,但你採用 200 是經實驗調整的折衷值,為了 提高 recall 同時保持 high precision
eval_metric='logloss' 評估指標 適合機率預測任務,與 precision/recall 不衝突
### 門檻調整(閾值設定)
y_probs = xgb_model.predict_proba(X_test)[:, 1]
取得預測機率後,因為threshold預設值為 = 0.5
這邊選擇不直接使用預設門檻(0.5)分類
而是透過以下邏輯:
去找一組最佳的 threshold 來用
precision, recall, thresholds = precision_recall_curve(y_test, y_probs)
f1_scores = 2 * (precision * recall) / (precision + recall + 1e-8)
best_idx = np.argmax(f1_scores)
best_threshold = 0.941
最後得出的最佳結果為 {best_threshold = 0.941}

根據預測機率與真實標籤計算多組閾值下的 Precision、Recall 值
並計算每個 threshold 的 F1 score
找到能達到最大 F1 score 的最佳門檻

y_pred = (y_probs > best_threshold).astype(int)
避免預設門檻導致高 precision 但 recall 太低,因為在詐欺偵測中 recall 通常更重要)

### 結果評估指標說明
各項指標代表的意義與分析
Precision: 0.9576 表示每 100 筆判定為詐欺的交易,有 95 筆真的有問題(誤判率低)
Recall: 0.8309 表示 100 筆詐欺中模型抓到了 83 筆(漏判率小)
F1 Score: 0.8898 綜合考量 precision 與 recall
Accuracy: 0.9997 雖然高,但在不平衡問題中參考價值低
ROC AUC: 0.9871 模型區分正負樣本能力極佳
### 我的結論與採用此方法的理由
使用 XGBoost 為了在不平衡樣本下取得良好效果,且可調整 scale_pos_weight
使用 precision-recall curve 找門檻 避免 0.5 預設值造成的 recall 偏低問題
調參以提升泛化 調整 gamma、subsample、colsample_bytree 等避免過擬合
選擇 logloss 評估指標 能平衡正負類分類,適合詐欺預測場景
這邊沒有採用 SMOTE 保持資料真實性,僅透過類別權重調整處理不平衡問題
原始調整Random Forest時,也有採用過 SMOTE 來做資料平衡但未達到要求的數據結果
這邊改採 XGBoost 做監督式學習後就未使用 SMOTE 來做資料平衡了,此方式也可以保持資料真實性

# 非監督式學習
實驗說明:K-Means + Mahalanobis 距離法
初始方法與實驗動機
在監督式學習難以取得足夠標註資料的場景下,本次實驗採用 非監督式學習(Unsupervised Learning) 進行詐欺交易偵測。

### 邏輯和實驗方法

透過「正常樣本」進行聚類,建立資料分布輪廓
以 Mahalanobis 距離 衡量測試樣本與聚類中心的偏離程度
利用距離大小判定樣本是否為潛在異常(詐欺)

### 此方法之優勢包括:

無需大量標記資料,適合樣本極度不平衡的任務
專注於異常檢測,更貼合詐欺行為的本質
Mahalanobis 距離考量共變異,對高維資料更敏感

### 資料集與前處理
使用資料集:creditcard.csv(Kaggle 信用卡詐欺資料)
處理步驟:
移除 Time 欄位
標準化 Amount 欄位與其餘 V1 ~ V28 特徵
切分資料為訓練集與測試集(Stratified 方式,保持類別比例)
模型設計與邏輯架構
Step 1:降維(PCA)
採用 主成分分析(PCA) 降維
測試保留變異量:0.96、0.97、0.98
Step 2:樣本過濾
僅取正常樣本(label = 0)建立聚類模型
使用 z-score 過濾 移除極端值(|z| < 1.8)
Step 3:聚類模型(K-Means)
聚類數 K 搜尋範圍:10 ~ 15
以 Silhouette Score 評估聚類效果
擇最佳 K 值進行 KMeans 聚類
Step 4:Mahalanobis 距離計算
對測試樣本,計算與所有聚類中心的 Mahalanobis 距離
取最小值作為樣本「正常性分數」
此距離值用於分類詐欺 vs 正常

### 閾值設定邏輯(Threshold Selection)
模型輸出為異常距離,非機率值,無法直接用 0.5 作為門檻
改以 分位數閾值(percentile threshold) 決定分類界線
測試區間:99.85% ~ 99.99%
評估指標:Precision / Recall / F1 Score
最終門檻:以最大 F1 score 對應的 threshold 為主

### 參數設定與搜尋組合
參數名稱 功能描述 嘗試值 / 調整依據
pca_var PCA 保留變異量 [0.96, 0.97, 0.98]
sample_size 正常樣本訓練筆數 [1200, 1500, 1800, 2000]
k 聚類中心數 自動搜尋 [10~15],取 Silhouette 最佳
threshold Mahalanobis 距離閾值 [99.85% ~ 99.99%](取 F1 最佳)

### 最佳組合:
'pca_var': 0.97,
'sample_size': 1500,
'k': 14,
'threshold': 1.3085
### 模型結果與評估指標
Precision: 0.9063 → 表示每 100 筆被判定為詐欺的交易中,有 90 筆是真的(誤報低)
Recall: 0.7838 → 表示所有詐欺樣本中有 78% 被偵測出來(漏判低)
F1 Score: 0.84 → Precision / Recall 平衡佳
Accuracy: 0.9994 → 雖高,但在極度不平衡問題中參考性較低

### 我的結論與實驗方法結果
本方法不依賴監督學習,適合無標註資料或樣本極度不平衡場景
Mahalanobis 距離考慮樣本共變異,能更有效偵測異常行為
門檻選擇以 precision-recall 驅動,避免固定 0.5 帶來的偏誤
保留原始資料分佈,不進行 SMOTE 或其他過採樣,維持資料真實性
在未標記場景下仍能達到相當於監督學習模型的性能表現(F1 ≈ 0.84)