Upload folder using huggingface_hub
Browse filesThis view is limited to 50 files because it contains too many changes.
See raw diff
- .gitattributes +16 -0
- false/.gitignore +2 -0
- false/.models/a2c_fraud_model.zip +3 -0
- false/.models/a2c_fraud_model2.zip +3 -0
- false/.models/dqn_fraud_model.zip +3 -0
- false/.models/dqn_fraud_model2.zip +3 -0
- false/Copy_of_💵Transaction_Fraud_Detection🕵️_♂️.ipynb +0 -0
- false/RL-1.pdf +3 -0
- false/RL2.0.ipynb +0 -0
- false/RL2.0_ATT.ipynb +0 -0
- false/RL2.0_ATT_Hybrid.ipynb +385 -0
- false/a2c_fraud_checkpoints/A2C_fraud_model_160000_steps.zip +3 -0
- false/a2c_fraud_checkpoints/A2C_fraud_model_240000_steps.zip +3 -0
- false/a2c_fraud_checkpoints/A2C_fraud_model_80000_steps.zip +3 -0
- false/a2c_fraud_tb/evaluation/a2c_eval_20250514-022104/events.out.tfevents.1747185664.archlinux.48104.8 +3 -0
- false/a2c_fraud_tb/evaluation/a2c_eval_20251201-002818/events.out.tfevents.1764545298.archlinux.67971.3 +3 -0
- false/a2c_fraud_tb/evaluation/a2c_eval_20251201-005711/events.out.tfevents.1764547031.archlinux.73972.3 +3 -0
- false/a2c_fraud_tb/evaluation/a2c_eval_20251201-112708/events.out.tfevents.1764584828.archlinux.5620.3 +3 -0
- false/a2c_fraud_tb/evaluation/a2c_eval_20251202-104925/events.out.tfevents.1764668965.archlinux.47939.3 +3 -0
- false/ablation_results.png +0 -0
- false/ablation_study/ablation-study-rl.ipynb +1 -0
- false/ablation_study/ablation.ipynb +514 -0
- false/attention_pooled_embeddings.pkl +3 -0
- false/custom_env.py +99 -0
- false/dqn_fraud_checkpoints/dqn_fraud_model_100000_steps.zip +3 -0
- false/dqn_fraud_checkpoints/dqn_fraud_model_10000_steps.zip +3 -0
- false/dqn_fraud_checkpoints/dqn_fraud_model_110000_steps.zip +3 -0
- false/dqn_fraud_checkpoints/dqn_fraud_model_160000_steps.zip +3 -0
- false/dqn_fraud_checkpoints/dqn_fraud_model_20000_steps.zip +3 -0
- false/dqn_fraud_checkpoints/dqn_fraud_model_240000_steps.zip +3 -0
- false/dqn_fraud_checkpoints/dqn_fraud_model_30000_steps.zip +3 -0
- false/dqn_fraud_checkpoints/dqn_fraud_model_40000_steps.zip +3 -0
- false/dqn_fraud_checkpoints/dqn_fraud_model_50000_steps.zip +3 -0
- false/dqn_fraud_checkpoints/dqn_fraud_model_60000_steps.zip +3 -0
- false/dqn_fraud_checkpoints/dqn_fraud_model_70000_steps.zip +3 -0
- false/dqn_fraud_checkpoints/dqn_fraud_model_80000_steps.zip +3 -0
- false/dqn_fraud_checkpoints/dqn_fraud_model_90000_steps.zip +3 -0
- false/dqn_fraud_tb/DQN_4/events.out.tfevents.1747182467.archlinux.48104.0 +3 -0
- false/dqn_fraud_tb/DQN_5/events.out.tfevents.1764545110.archlinux.67971.0 +3 -0
- false/dqn_fraud_tb/DQN_6/events.out.tfevents.1764546770.archlinux.73972.0 +3 -0
- false/dqn_fraud_tb/DQN_7/events.out.tfevents.1764584658.archlinux.5620.0 +3 -0
- false/dqn_fraud_tb/DQN_8/events.out.tfevents.1764668813.archlinux.47939.0 +3 -0
- false/dqn_fraud_tb/evaluation/eval_20250514-014902/events.out.tfevents.1747183742.archlinux.48104.3 +3 -0
- false/dqn_fraud_tb/evaluation/eval_20251201-002715/events.out.tfevents.1764545235.archlinux.67971.1 +3 -0
- false/dqn_fraud_tb/evaluation/eval_20251201-005435/events.out.tfevents.1764546875.archlinux.73972.1 +3 -0
- false/dqn_fraud_tb/evaluation/eval_20251201-112610/events.out.tfevents.1764584770.archlinux.5620.1 +3 -0
- false/dqn_fraud_tb/evaluation/eval_20251202-104820/events.out.tfevents.1764668900.archlinux.47939.1 +3 -0
- false/embeddings.pkl +3 -0
- false/fraud-detection-with-distilbert.ipynb +0 -0
- false/models/a2c_fraud_model.zip +3 -0
.gitattributes
CHANGED
|
@@ -33,3 +33,19 @@ saved_model/**/* filter=lfs diff=lfs merge=lfs -text
|
|
| 33 |
*.zip filter=lfs diff=lfs merge=lfs -text
|
| 34 |
*.zst filter=lfs diff=lfs merge=lfs -text
|
| 35 |
*tfevents* filter=lfs diff=lfs merge=lfs -text
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 33 |
*.zip filter=lfs diff=lfs merge=lfs -text
|
| 34 |
*.zst filter=lfs diff=lfs merge=lfs -text
|
| 35 |
*tfevents* filter=lfs diff=lfs merge=lfs -text
|
| 36 |
+
false/RL-1.pdf filter=lfs diff=lfs merge=lfs -text
|
| 37 |
+
false/paper/LLM-Assisted[[:space:]]Fraud[[:space:]]Detection[[:space:]]with[[:space:]]Reinforcement[[:space:]]Learning/LLM-Assisted[[:space:]]Fraud[[:space:]]Detection[[:space:]]with[[:space:]]Reinforcement[[:space:]]Learning/Definitions/logo-mdpi.eps filter=lfs diff=lfs merge=lfs -text
|
| 38 |
+
false/paper/LLM-Assisted[[:space:]]Fraud[[:space:]]Detection[[:space:]]with[[:space:]]Reinforcement[[:space:]]Learning/LLM-Assisted[[:space:]]Fraud[[:space:]]Detection[[:space:]]with[[:space:]]Reinforcement[[:space:]]Learning/Fraud_detection.pdf filter=lfs diff=lfs merge=lfs -text
|
| 39 |
+
false/paper/LLM_Assisted_Fraud_Detection_with_Reinforcement_Learning-1.pdf filter=lfs diff=lfs merge=lfs -text
|
| 40 |
+
false/paper/RL.pdf filter=lfs diff=lfs merge=lfs -text
|
| 41 |
+
false/paper/Related[[:space:]]Work_[[:space:]]Dataset-Specific[[:space:]]Performance[[:space:]]Analysis.pdf filter=lfs diff=lfs merge=lfs -text
|
| 42 |
+
false/paper/images/ilovepdf_jpg-to-pdf/a2c_conf.pdf filter=lfs diff=lfs merge=lfs -text
|
| 43 |
+
false/paper/llm_v4_plots/images/cnfA.png filter=lfs diff=lfs merge=lfs -text
|
| 44 |
+
false/paper/llm_v4_plots/images/conf_dqn.png filter=lfs diff=lfs merge=lfs -text
|
| 45 |
+
false/paper/llm_v4_plots/images/eval_ent.png filter=lfs diff=lfs merge=lfs -text
|
| 46 |
+
false/paper/llm_v4_plots/images/image.png filter=lfs diff=lfs merge=lfs -text
|
| 47 |
+
false/paper/llm_v4_plots/images/lossdqn.png filter=lfs diff=lfs merge=lfs -text
|
| 48 |
+
false/paper/llm_v4_plots/images/mer.png filter=lfs diff=lfs merge=lfs -text
|
| 49 |
+
false/paper/llm_v4_plots/images/policy_loss.png filter=lfs diff=lfs merge=lfs -text
|
| 50 |
+
false/paper/llm_v4_plots/images/rwrd_dyn-eval.png filter=lfs diff=lfs merge=lfs -text
|
| 51 |
+
false/paper/llm_v4_plots-1.pdf filter=lfs diff=lfs merge=lfs -text
|
false/.gitignore
ADDED
|
@@ -0,0 +1,2 @@
|
|
|
|
|
|
|
|
|
|
| 1 |
+
ablation_study/creditcard.csv
|
| 2 |
+
ablation_study/paysim.csv
|
false/.models/a2c_fraud_model.zip
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:51aa5e6d4280c5cd785f7ee69eca18ad93f67b01b5ff091772cc8d2e6ec1198d
|
| 3 |
+
size 945552
|
false/.models/a2c_fraud_model2.zip
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:5ab8ce945fb98fb960ece8efd2861e5fae7e0cf0c51f0ffd5f1bf00098410330
|
| 3 |
+
size 179946
|
false/.models/dqn_fraud_model.zip
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:3615ddbbc8cd6bef2488a2899161b600dbe528ab77eb65578ee6dde816584749
|
| 3 |
+
size 978197
|
false/.models/dqn_fraud_model2.zip
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:db510c265c5148c6a8bb3c19edeae6521164ec64aad48480bb311f655f7dc082
|
| 3 |
+
size 182934
|
false/Copy_of_💵Transaction_Fraud_Detection🕵️_♂️.ipynb
ADDED
|
The diff for this file is too large to render.
See raw diff
|
|
|
false/RL-1.pdf
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:aad8cbe3f3f9fa029d950a3b62e0054f933e8b9f768c83be468054bd735201fd
|
| 3 |
+
size 2006409
|
false/RL2.0.ipynb
ADDED
|
The diff for this file is too large to render.
See raw diff
|
|
|
false/RL2.0_ATT.ipynb
ADDED
|
The diff for this file is too large to render.
See raw diff
|
|
|
false/RL2.0_ATT_Hybrid.ipynb
ADDED
|
@@ -0,0 +1,385 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"cells": [
|
| 3 |
+
{
|
| 4 |
+
"cell_type": "markdown",
|
| 5 |
+
"metadata": {},
|
| 6 |
+
"source": [
|
| 7 |
+
"# RL Agents for Fraud Detection (Hybrid: Embeddings + Structured Data)\n",
|
| 8 |
+
"\n",
|
| 9 |
+
"This notebook trains RL agents (PPO, A2C, DQN) on a hybrid dataset consisting of:\n",
|
| 10 |
+
"1. **Embeddings**: Attention-pooled embeddings from a DistilBERT model.\n",
|
| 11 |
+
"2. **Structured Data**: Original features from the CreditCard dataset (Time, V1-V28, Amount).\n",
|
| 12 |
+
"\n",
|
| 13 |
+
"The combined data goes through a preprocessing pipeline:\n",
|
| 14 |
+
"- **Scaling**: StandardScaler applied to the concatenated vector.\n",
|
| 15 |
+
"- **PCA**: Dimensionality reduction on the scaled combined vector.\n",
|
| 16 |
+
"- **CTGAN**: Data augmentation to balance the training set."
|
| 17 |
+
]
|
| 18 |
+
},
|
| 19 |
+
{
|
| 20 |
+
"cell_type": "code",
|
| 21 |
+
"execution_count": null,
|
| 22 |
+
"metadata": {},
|
| 23 |
+
"outputs": [],
|
| 24 |
+
"source": [
|
| 25 |
+
"# Install required packages\n",
|
| 26 |
+
"!pip install gymnasium numpy pandas torch stable-baselines3 scikit-learn matplotlib seaborn ctgan"
|
| 27 |
+
]
|
| 28 |
+
},
|
| 29 |
+
{
|
| 30 |
+
"cell_type": "code",
|
| 31 |
+
"execution_count": null,
|
| 32 |
+
"metadata": {},
|
| 33 |
+
"outputs": [],
|
| 34 |
+
"source": [
|
| 35 |
+
"import gymnasium as gym\n",
|
| 36 |
+
"from gymnasium import spaces\n",
|
| 37 |
+
"import numpy as np\n",
|
| 38 |
+
"import pandas as pd\n",
|
| 39 |
+
"import torch\n",
|
| 40 |
+
"import os\n",
|
| 41 |
+
"\n",
|
| 42 |
+
"from stable_baselines3 import PPO, A2C, DQN\n",
|
| 43 |
+
"from stable_baselines3.common.vec_env import DummyVecEnv\n",
|
| 44 |
+
"\n",
|
| 45 |
+
"from sklearn.metrics import confusion_matrix, accuracy_score, precision_score, recall_score, f1_score\n",
|
| 46 |
+
"from sklearn.model_selection import train_test_split\n",
|
| 47 |
+
"from sklearn.preprocessing import StandardScaler\n",
|
| 48 |
+
"from sklearn.decomposition import PCA\n",
|
| 49 |
+
"import matplotlib.pyplot as plt\n",
|
| 50 |
+
"import seaborn as sns\n",
|
| 51 |
+
"\n",
|
| 52 |
+
"# Set random seed\n",
|
| 53 |
+
"SEED = 42\n",
|
| 54 |
+
"np.random.seed(SEED)\n",
|
| 55 |
+
"torch.manual_seed(SEED)"
|
| 56 |
+
]
|
| 57 |
+
},
|
| 58 |
+
{
|
| 59 |
+
"cell_type": "markdown",
|
| 60 |
+
"metadata": {},
|
| 61 |
+
"source": [
|
| 62 |
+
"## 1. Data Loading and Alignment"
|
| 63 |
+
]
|
| 64 |
+
},
|
| 65 |
+
{
|
| 66 |
+
"cell_type": "code",
|
| 67 |
+
"execution_count": null,
|
| 68 |
+
"id": "7f46057b",
|
| 69 |
+
"metadata": {},
|
| 70 |
+
"outputs": [],
|
| 71 |
+
"source": [
|
| 72 |
+
"# Load Embeddings\n",
|
| 73 |
+
"print(\"Loading embeddings...\")\n",
|
| 74 |
+
"pkl_data = pd.read_pickle(\"attention_pooled_embeddings.pkl\")\n",
|
| 75 |
+
"embeddings = pkl_data['embeddings']\n",
|
| 76 |
+
"labels = np.array(pkl_data['labels'])\n",
|
| 77 |
+
"uids = pkl_data['uids']\n",
|
| 78 |
+
"\n",
|
| 79 |
+
"print(f\"Embeddings shape: {embeddings.shape}\")\n",
|
| 80 |
+
"print(f\"Labels shape: {labels.shape}\")\n",
|
| 81 |
+
"print(f\"UIDs shape: {len(uids)}\")\n",
|
| 82 |
+
"\n",
|
| 83 |
+
"# Load Structured Data\n",
|
| 84 |
+
"print(\"\\nLoading structured data...\")\n",
|
| 85 |
+
"creditcard_df = pd.read_csv(\"ablation_study/creditcard.csv\")\n",
|
| 86 |
+
"\n",
|
| 87 |
+
"# Filter structured data to match the UIDs in the embeddings file\n",
|
| 88 |
+
"# Using uids to select the corresponding rows from the original dataset\n",
|
| 89 |
+
"structured_data = creditcard_df.loc[uids].copy()\n",
|
| 90 |
+
"\n",
|
| 91 |
+
"# Drop Class column to get features\n",
|
| 92 |
+
"structured_features = structured_data.drop('Class', axis=1).values\n",
|
| 93 |
+
"print(f\"Structured features shape: {structured_features.shape}\")\n",
|
| 94 |
+
"\n",
|
| 95 |
+
"# Concatenate Embeddings and Structured Data\n",
|
| 96 |
+
"# We use the labels from the pickle file as the ground truth\n",
|
| 97 |
+
"X = np.hstack([embeddings, structured_features])\n",
|
| 98 |
+
"y = labels\n",
|
| 99 |
+
"\n",
|
| 100 |
+
"print(f\"\\nCombined Data Shape: {X.shape}\")"
|
| 101 |
+
]
|
| 102 |
+
},
|
| 103 |
+
{
|
| 104 |
+
"cell_type": "markdown",
|
| 105 |
+
"metadata": {},
|
| 106 |
+
"source": [
|
| 107 |
+
"## 2. Data Preprocessing Pipeline\n",
|
| 108 |
+
"1. Split Train/Test\n",
|
| 109 |
+
"2. Scale (StandardScaler)\n",
|
| 110 |
+
"3. PCA\n",
|
| 111 |
+
"4. CTGAN Augmentation"
|
| 112 |
+
]
|
| 113 |
+
},
|
| 114 |
+
{
|
| 115 |
+
"cell_type": "code",
|
| 116 |
+
"execution_count": null,
|
| 117 |
+
"metadata": {},
|
| 118 |
+
"outputs": [],
|
| 119 |
+
"source": [
|
| 120 |
+
"# 1. Split\n",
|
| 121 |
+
"X_train, X_test, y_train, y_test = train_test_split(\n",
|
| 122 |
+
" X, y, test_size=0.2, random_state=SEED, stratify=y\n",
|
| 123 |
+
")\n",
|
| 124 |
+
"\n",
|
| 125 |
+
"print(f\"Train shape: {X_train.shape}\")\n",
|
| 126 |
+
"print(f\"Test shape: {X_test.shape}\")\n",
|
| 127 |
+
"\n",
|
| 128 |
+
"# 2. Scale\n",
|
| 129 |
+
"scaler = StandardScaler()\n",
|
| 130 |
+
"X_train_scaled = scaler.fit_transform(X_train)\n",
|
| 131 |
+
"X_test_scaled = scaler.transform(X_test)\n",
|
| 132 |
+
"\n",
|
| 133 |
+
"# 3. PCA\n",
|
| 134 |
+
"# We want to retain 99% variance, similar to the original notebook\n",
|
| 135 |
+
"pca = PCA(n_components=0.99, whiten=True)\n",
|
| 136 |
+
"X_train_pca = pca.fit_transform(X_train_scaled)\n",
|
| 137 |
+
"X_test_pca = pca.transform(X_test_scaled)\n",
|
| 138 |
+
"\n",
|
| 139 |
+
"print(f\"\\nPCA reduced dimensions from {X_train.shape[1]} to {X_train_pca.shape[1]}\")\n",
|
| 140 |
+
"\n",
|
| 141 |
+
"# 4. CTGAN Augmentation\n",
|
| 142 |
+
"from ctgan import CTGAN\n",
|
| 143 |
+
"\n",
|
| 144 |
+
"# Prepare data for CTGAN (only fraud samples from training set)\n",
|
| 145 |
+
"train_df_pca = pd.DataFrame(X_train_pca, columns=[f'pc{i}' for i in range(X_train_pca.shape[1])])\n",
|
| 146 |
+
"train_df_pca['label'] = y_train\n",
|
| 147 |
+
"\n",
|
| 148 |
+
"fraud_df = train_df_pca[train_df_pca['label'] == 1].drop('label', axis=1)\n",
|
| 149 |
+
"print(f\"Fraud samples for CTGAN: {len(fraud_df)}\")\n",
|
| 150 |
+
"\n",
|
| 151 |
+
"# Train CTGAN\n",
|
| 152 |
+
"ctgan = CTGAN(epochs=200, batch_size=64, pac=1, verbose=True)\n",
|
| 153 |
+
"ctgan.fit(fraud_df)\n",
|
| 154 |
+
"\n",
|
| 155 |
+
"# Generate synthetic samples\n",
|
| 156 |
+
"n_synthetic = len(fraud_df) # Double the fraud samples\n",
|
| 157 |
+
"synthetic_fraud = ctgan.sample(n_synthetic)\n",
|
| 158 |
+
"X_synthetic = synthetic_fraud.values.astype(np.float32)\n",
|
| 159 |
+
"y_synthetic = np.ones(n_synthetic, dtype=np.int64)\n",
|
| 160 |
+
"\n",
|
| 161 |
+
"# Augment Training Set\n",
|
| 162 |
+
"X_train_aug = np.vstack([X_train_pca, X_synthetic])\n",
|
| 163 |
+
"y_train_aug = np.concatenate([y_train, y_synthetic])\n",
|
| 164 |
+
"\n",
|
| 165 |
+
"print(f\"Augmented Train Shape: {X_train_aug.shape}\")"
|
| 166 |
+
]
|
| 167 |
+
},
|
| 168 |
+
{
|
| 169 |
+
"cell_type": "markdown",
|
| 170 |
+
"metadata": {},
|
| 171 |
+
"source": [
|
| 172 |
+
"## 3. RL Environment"
|
| 173 |
+
]
|
| 174 |
+
},
|
| 175 |
+
{
|
| 176 |
+
"cell_type": "code",
|
| 177 |
+
"execution_count": null,
|
| 178 |
+
"metadata": {},
|
| 179 |
+
"outputs": [],
|
| 180 |
+
"source": [
|
| 181 |
+
"class FraudDetectionEnv(gym.Env):\n",
|
| 182 |
+
" \"\"\"\n",
|
| 183 |
+
" Custom Environment that follows gym interface.\n",
|
| 184 |
+
" \"\"\"\n",
|
| 185 |
+
" def __init__(self, features, labels, reward_config=None):\n",
|
| 186 |
+
" super(FraudDetectionEnv, self).__init__()\n",
|
| 187 |
+
" \n",
|
| 188 |
+
" self.features = features.astype(np.float32)\n",
|
| 189 |
+
" self.labels = labels.astype(np.int64)\n",
|
| 190 |
+
" self.n_samples = len(features)\n",
|
| 191 |
+
" self.input_dim = features.shape[1]\n",
|
| 192 |
+
" \n",
|
| 193 |
+
" # Define action and observation space\n",
|
| 194 |
+
" # Action: 0 (Not Fraud), 1 (Fraud)\n",
|
| 195 |
+
" self.action_space = spaces.Discrete(2)\n",
|
| 196 |
+
" \n",
|
| 197 |
+
" # Observation: Feature vector\n",
|
| 198 |
+
" self.observation_space = spaces.Box(\n",
|
| 199 |
+
" low=-np.inf, high=np.inf,\n",
|
| 200 |
+
" shape=(self.input_dim,),\n",
|
| 201 |
+
" dtype=np.float32\n",
|
| 202 |
+
" )\n",
|
| 203 |
+
" \n",
|
| 204 |
+
" if reward_config is None:\n",
|
| 205 |
+
" self.reward_config = {\n",
|
| 206 |
+
" 'TP': 10.0,\n",
|
| 207 |
+
" 'FP': -5.0,\n",
|
| 208 |
+
" 'FN': -20.0,\n",
|
| 209 |
+
" 'TN': 1.0\n",
|
| 210 |
+
" }\n",
|
| 211 |
+
" else:\n",
|
| 212 |
+
" self.reward_config = reward_config\n",
|
| 213 |
+
" \n",
|
| 214 |
+
" self.current_step = 0\n",
|
| 215 |
+
" self.indices = np.arange(self.n_samples)\n",
|
| 216 |
+
" \n",
|
| 217 |
+
" def reset(self, seed=None, options=None):\n",
|
| 218 |
+
" super().reset(seed=seed)\n",
|
| 219 |
+
" self.current_step = 0\n",
|
| 220 |
+
" np.random.shuffle(self.indices)\n",
|
| 221 |
+
" return self._get_obs(), {}\n",
|
| 222 |
+
" \n",
|
| 223 |
+
" def _get_obs(self):\n",
|
| 224 |
+
" idx = self.indices[self.current_step]\n",
|
| 225 |
+
" return self.features[idx]\n",
|
| 226 |
+
" \n",
|
| 227 |
+
" def step(self, action):\n",
|
| 228 |
+
" idx = self.indices[self.current_step]\n",
|
| 229 |
+
" true_label = self.labels[idx]\n",
|
| 230 |
+
" \n",
|
| 231 |
+
" reward = 0\n",
|
| 232 |
+
" if action == 1 and true_label == 1:\n",
|
| 233 |
+
" reward = self.reward_config['TP']\n",
|
| 234 |
+
" elif action == 1 and true_label == 0:\n",
|
| 235 |
+
" reward = self.reward_config['FP']\n",
|
| 236 |
+
" elif action == 0 and true_label == 1:\n",
|
| 237 |
+
" reward = self.reward_config['FN']\n",
|
| 238 |
+
" elif action == 0 and true_label == 0:\n",
|
| 239 |
+
" reward = self.reward_config['TN']\n",
|
| 240 |
+
" \n",
|
| 241 |
+
" self.current_step += 1\n",
|
| 242 |
+
" done = self.current_step >= self.n_samples\n",
|
| 243 |
+
" truncated = False\n",
|
| 244 |
+
" \n",
|
| 245 |
+
" info = {\n",
|
| 246 |
+
" 'true_label': true_label,\n",
|
| 247 |
+
" 'pred_label': action\n",
|
| 248 |
+
" }\n",
|
| 249 |
+
" \n",
|
| 250 |
+
" if not done:\n",
|
| 251 |
+
" next_obs = self._get_obs()\n",
|
| 252 |
+
" else:\n",
|
| 253 |
+
" next_obs = np.zeros(self.input_dim, dtype=np.float32)\n",
|
| 254 |
+
" \n",
|
| 255 |
+
" return next_obs, reward, done, truncated, info"
|
| 256 |
+
]
|
| 257 |
+
},
|
| 258 |
+
{
|
| 259 |
+
"cell_type": "markdown",
|
| 260 |
+
"metadata": {},
|
| 261 |
+
"source": [
|
| 262 |
+
"## 4. Training and Evaluation"
|
| 263 |
+
]
|
| 264 |
+
},
|
| 265 |
+
{
|
| 266 |
+
"cell_type": "code",
|
| 267 |
+
"execution_count": null,
|
| 268 |
+
"metadata": {},
|
| 269 |
+
"outputs": [],
|
| 270 |
+
"source": [
|
| 271 |
+
"# Define a learning rate schedule\n",
|
| 272 |
+
"def linear_schedule(initial_value):\n",
|
| 273 |
+
" def schedule(progress_remaining):\n",
|
| 274 |
+
" return progress_remaining * initial_value\n",
|
| 275 |
+
" return schedule\n",
|
| 276 |
+
"\n",
|
| 277 |
+
"def train_evaluate_agent(agent_name, X_train, y_train, X_test, y_test, total_timesteps=10000):\n",
|
| 278 |
+
" print(f\"\\nTraining {agent_name}...\")\n",
|
| 279 |
+
" \n",
|
| 280 |
+
" # Create env\n",
|
| 281 |
+
" env = DummyVecEnv([lambda: FraudDetectionEnv(X_train, y_train)])\n",
|
| 282 |
+
" \n",
|
| 283 |
+
" model = None\n",
|
| 284 |
+
" if agent_name == 'PPO':\n",
|
| 285 |
+
" # PPO was not in the reference notebook, using default parameters\n",
|
| 286 |
+
" model = PPO('MlpPolicy', env, verbose=0)\n",
|
| 287 |
+
" elif agent_name == 'A2C':\n",
|
| 288 |
+
" # Parameters from RL2.0_ATT.ipynb\n",
|
| 289 |
+
" model = A2C(\n",
|
| 290 |
+
" \"MlpPolicy\",\n",
|
| 291 |
+
" env,\n",
|
| 292 |
+
" learning_rate=1e-4,\n",
|
| 293 |
+
" gamma=0.99,\n",
|
| 294 |
+
" n_steps=5,\n",
|
| 295 |
+
" ent_coef=0.01,\n",
|
| 296 |
+
" vf_coef=0.5,\n",
|
| 297 |
+
" max_grad_norm=0.5,\n",
|
| 298 |
+
" verbose=0,\n",
|
| 299 |
+
" device=\"auto\"\n",
|
| 300 |
+
" )\n",
|
| 301 |
+
" elif agent_name == 'DQN':\n",
|
| 302 |
+
" # Parameters from RL2.0_ATT.ipynb\n",
|
| 303 |
+
" model = DQN(\n",
|
| 304 |
+
" \"MlpPolicy\",\n",
|
| 305 |
+
" env,\n",
|
| 306 |
+
" learning_rate=linear_schedule(1e-4),\n",
|
| 307 |
+
" buffer_size=100000,\n",
|
| 308 |
+
" learning_starts=1000,\n",
|
| 309 |
+
" batch_size=512,\n",
|
| 310 |
+
" gamma=0.99,\n",
|
| 311 |
+
" train_freq=1,\n",
|
| 312 |
+
" gradient_steps=1,\n",
|
| 313 |
+
" target_update_interval=500,\n",
|
| 314 |
+
" exploration_fraction=0.1,\n",
|
| 315 |
+
" exploration_initial_eps=1.0,\n",
|
| 316 |
+
" exploration_final_eps=0.05,\n",
|
| 317 |
+
" max_grad_norm=10,\n",
|
| 318 |
+
" verbose=0,\n",
|
| 319 |
+
" device=\"auto\"\n",
|
| 320 |
+
" )\n",
|
| 321 |
+
" \n",
|
| 322 |
+
" if model:\n",
|
| 323 |
+
" model.learn(total_timesteps=total_timesteps)\n",
|
| 324 |
+
" \n",
|
| 325 |
+
" # Evaluate\n",
|
| 326 |
+
" print(f\"Evaluating {agent_name}...\")\n",
|
| 327 |
+
" test_env = FraudDetectionEnv(X_test, y_test)\n",
|
| 328 |
+
" obs, _ = test_env.reset()\n",
|
| 329 |
+
" \n",
|
| 330 |
+
" y_true = []\n",
|
| 331 |
+
" y_pred = []\n",
|
| 332 |
+
" \n",
|
| 333 |
+
" done = False\n",
|
| 334 |
+
" while not done:\n",
|
| 335 |
+
" action, _ = model.predict(obs, deterministic=True)\n",
|
| 336 |
+
" obs, reward, done, truncated, info = test_env.step(action)\n",
|
| 337 |
+
" \n",
|
| 338 |
+
" if 'true_label' in info:\n",
|
| 339 |
+
" y_true.append(info['true_label'])\n",
|
| 340 |
+
" y_pred.append(action)\n",
|
| 341 |
+
" \n",
|
| 342 |
+
" # Metrics\n",
|
| 343 |
+
" print(f\"--- {agent_name} Results ---\")\n",
|
| 344 |
+
" print(f\"Accuracy: {accuracy_score(y_true, y_pred):.4f}\")\n",
|
| 345 |
+
" print(f\"Precision: {precision_score(y_true, y_pred):.4f}\")\n",
|
| 346 |
+
" print(f\"Recall: {recall_score(y_true, y_pred):.4f}\")\n",
|
| 347 |
+
" print(f\"F1 Score: {f1_score(y_true, y_pred):.4f}\")\n",
|
| 348 |
+
" \n",
|
| 349 |
+
" cm = confusion_matrix(y_true, y_pred)\n",
|
| 350 |
+
" sns.heatmap(cm, annot=True, fmt='d', cmap='Blues')\n",
|
| 351 |
+
" plt.title(f'{agent_name} Confusion Matrix')\n",
|
| 352 |
+
" plt.ylabel('True Label')\n",
|
| 353 |
+
" plt.xlabel('Predicted Label')\n",
|
| 354 |
+
" plt.show()\n",
|
| 355 |
+
"\n",
|
| 356 |
+
"# Train Agents\n",
|
| 357 |
+
"# Note: PPO parameters are default as it was not present in the reference notebook.\n",
|
| 358 |
+
"train_evaluate_agent('PPO', X_train_aug, y_train_aug, X_test_pca, y_test)\n",
|
| 359 |
+
"train_evaluate_agent('A2C', X_train_aug, y_train_aug, X_test_pca, y_test)\n",
|
| 360 |
+
"train_evaluate_agent('DQN', X_train_aug, y_train_aug, X_test_pca, y_test)"
|
| 361 |
+
]
|
| 362 |
+
}
|
| 363 |
+
],
|
| 364 |
+
"metadata": {
|
| 365 |
+
"kernelspec": {
|
| 366 |
+
"display_name": "Python 3",
|
| 367 |
+
"language": "python",
|
| 368 |
+
"name": "python3"
|
| 369 |
+
},
|
| 370 |
+
"language_info": {
|
| 371 |
+
"codemirror_mode": {
|
| 372 |
+
"name": "ipython",
|
| 373 |
+
"version": 3
|
| 374 |
+
},
|
| 375 |
+
"file_extension": ".py",
|
| 376 |
+
"mimetype": "text/x-python",
|
| 377 |
+
"name": "python",
|
| 378 |
+
"nbconvert_exporter": "python",
|
| 379 |
+
"pygments_lexer": "ipython3",
|
| 380 |
+
"version": "3.10.12"
|
| 381 |
+
}
|
| 382 |
+
},
|
| 383 |
+
"nbformat": 4,
|
| 384 |
+
"nbformat_minor": 5
|
| 385 |
+
}
|
false/a2c_fraud_checkpoints/A2C_fraud_model_160000_steps.zip
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:968858ebc2df1c6eb362683d84ab9156e90cc62a0e0853ead28ff28fc7cd127d
|
| 3 |
+
size 178290
|
false/a2c_fraud_checkpoints/A2C_fraud_model_240000_steps.zip
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:b5c4befaeb86918c04e3492ae9076198cb68d8c32b7db39c78a344af91082c23
|
| 3 |
+
size 179283
|
false/a2c_fraud_checkpoints/A2C_fraud_model_80000_steps.zip
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:e1a34eac4b517a168f82bf414a9615a0c5078de3fdd2571e851dfd78d47119b9
|
| 3 |
+
size 176965
|
false/a2c_fraud_tb/evaluation/a2c_eval_20250514-022104/events.out.tfevents.1747185664.archlinux.48104.8
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:ad67fdd03eb5488f6b5c6ba4789665dc1cfd654c7f566fad2807f1ef6016ee79
|
| 3 |
+
size 537183
|
false/a2c_fraud_tb/evaluation/a2c_eval_20251201-002818/events.out.tfevents.1764545298.archlinux.67971.3
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:97798a3be0e1e43fac506671c69e924f9d9e1f2e9860e55ad4ef0a52d32e6e11
|
| 3 |
+
size 533948
|
false/a2c_fraud_tb/evaluation/a2c_eval_20251201-005711/events.out.tfevents.1764547031.archlinux.73972.3
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:77e536eda7fe14f187583de227a558fb0eb7e86130e35f9d4252acccc30ef98a
|
| 3 |
+
size 569194
|
false/a2c_fraud_tb/evaluation/a2c_eval_20251201-112708/events.out.tfevents.1764584828.archlinux.5620.3
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:17c1ab22456731651fe2390ecb07abd35ab2adad7b2d8e7b27040d6be2dd9869
|
| 3 |
+
size 538607
|
false/a2c_fraud_tb/evaluation/a2c_eval_20251202-104925/events.out.tfevents.1764668965.archlinux.47939.3
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:1a7d84b2987427b41c800629df236d190d430b224a9920deae61e56496baf74c
|
| 3 |
+
size 556888
|
false/ablation_results.png
ADDED
|
false/ablation_study/ablation-study-rl.ipynb
ADDED
|
@@ -0,0 +1 @@
|
|
|
|
|
|
|
| 1 |
+
{"metadata":{"kernelspec":{"name":"python3","display_name":"Python 3","language":"python"},"language_info":{"name":"python","version":"3.11.13","mimetype":"text/x-python","codemirror_mode":{"name":"ipython","version":3},"pygments_lexer":"ipython3","nbconvert_exporter":"python","file_extension":".py"},"kaggle":{"accelerator":"nvidiaTeslaT4","dataSources":[{"sourceId":1940,"sourceType":"datasetVersion","datasetId":1069},{"sourceId":23498,"sourceType":"datasetVersion","datasetId":310}],"dockerImageVersionId":31193,"isInternetEnabled":true,"language":"python","sourceType":"notebook","isGpuEnabled":true}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"markdown","source":"# Ablation Study: RL Agents for Fraud Detection\n\nThis notebook performs an ablation study to compare the performance of different Reinforcement Learning (RL) agents (PPO, A2C, DQN) on two fraud detection datasets (CreditCard and PaySim). \nWe also evaluate the impact of different preprocessing techniques: Raw Data, PCA, and CTGAN Data Augmentation.","metadata":{}},{"cell_type":"code","source":"!pip install ctgan shimmy","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-02T08:27:12.781130Z","iopub.execute_input":"2025-12-02T08:27:12.781489Z","iopub.status.idle":"2025-12-02T08:28:20.009166Z","shell.execute_reply.started":"2025-12-02T08:27:12.781458Z","shell.execute_reply":"2025-12-02T08:28:20.008483Z"}},"outputs":[{"name":"stdout","text":"Collecting ctgan\n Downloading ctgan-0.11.1-py3-none-any.whl.metadata (10 kB)\nRequirement already satisfied: shimmy in /usr/local/lib/python3.11/dist-packages (1.3.0)\nRequirement already satisfied: numpy>=1.23.3 in /usr/local/lib/python3.11/dist-packages (from ctgan) (1.26.4)\nRequirement already satisfied: pandas>=1.5.0 in /usr/local/lib/python3.11/dist-packages (from ctgan) (2.2.3)\nRequirement already satisfied: torch>=2.0.0 in /usr/local/lib/python3.11/dist-packages (from ctgan) (2.6.0+cu124)\nRequirement already satisfied: tqdm<5,>=4.29 in /usr/local/lib/python3.11/dist-packages (from ctgan) (4.67.1)\nCollecting rdt>=1.14.0 (from ctgan)\n Downloading rdt-1.18.2-py3-none-any.whl.metadata (10 kB)\nRequirement already satisfied: gymnasium>=0.27.0 in /usr/local/lib/python3.11/dist-packages (from shimmy) (0.29.0)\nRequirement already satisfied: cloudpickle>=1.2.0 in /usr/local/lib/python3.11/dist-packages (from gymnasium>=0.27.0->shimmy) (3.1.2)\nRequirement already satisfied: typing-extensions>=4.3.0 in /usr/local/lib/python3.11/dist-packages (from gymnasium>=0.27.0->shimmy) (4.15.0)\nRequirement already satisfied: farama-notifications>=0.0.1 in /usr/local/lib/python3.11/dist-packages (from gymnasium>=0.27.0->shimmy) (0.0.4)\nRequirement already satisfied: mkl_fft in /usr/local/lib/python3.11/dist-packages (from numpy>=1.23.3->ctgan) (1.3.8)\nRequirement already satisfied: mkl_random in /usr/local/lib/python3.11/dist-packages (from numpy>=1.23.3->ctgan) (1.2.4)\nRequirement already satisfied: mkl_umath in /usr/local/lib/python3.11/dist-packages (from numpy>=1.23.3->ctgan) (0.1.1)\nRequirement already satisfied: mkl in /usr/local/lib/python3.11/dist-packages (from numpy>=1.23.3->ctgan) (2025.3.0)\nRequirement already satisfied: tbb4py in /usr/local/lib/python3.11/dist-packages (from numpy>=1.23.3->ctgan) (2022.3.0)\nRequirement already satisfied: mkl-service in /usr/local/lib/python3.11/dist-packages (from numpy>=1.23.3->ctgan) (2.4.1)\nRequirement already satisfied: python-dateutil>=2.8.2 in /usr/local/lib/python3.11/dist-packages (from pandas>=1.5.0->ctgan) (2.9.0.post0)\nRequirement already satisfied: pytz>=2020.1 in /usr/local/lib/python3.11/dist-packages (from pandas>=1.5.0->ctgan) (2025.2)\nRequirement already satisfied: tzdata>=2022.7 in /usr/local/lib/python3.11/dist-packages (from pandas>=1.5.0->ctgan) (2025.2)\nRequirement already satisfied: scipy>=1.9.2 in /usr/local/lib/python3.11/dist-packages (from rdt>=1.14.0->ctgan) (1.15.3)\nRequirement already satisfied: scikit-learn>=1.1.3 in /usr/local/lib/python3.11/dist-packages (from rdt>=1.14.0->ctgan) (1.2.2)\nCollecting Faker!=37.11.0,>=17 (from rdt>=1.14.0->ctgan)\n Downloading faker-38.2.0-py3-none-any.whl.metadata (16 kB)\nRequirement already satisfied: filelock in /usr/local/lib/python3.11/dist-packages (from torch>=2.0.0->ctgan) (3.20.0)\nRequirement already satisfied: networkx in /usr/local/lib/python3.11/dist-packages (from torch>=2.0.0->ctgan) (3.5)\nRequirement already satisfied: jinja2 in /usr/local/lib/python3.11/dist-packages (from torch>=2.0.0->ctgan) (3.1.6)\nRequirement already satisfied: fsspec in /usr/local/lib/python3.11/dist-packages (from torch>=2.0.0->ctgan) (2025.10.0)\nCollecting nvidia-cuda-nvrtc-cu12==12.4.127 (from torch>=2.0.0->ctgan)\n Downloading nvidia_cuda_nvrtc_cu12-12.4.127-py3-none-manylinux2014_x86_64.whl.metadata (1.5 kB)\nCollecting nvidia-cuda-runtime-cu12==12.4.127 (from torch>=2.0.0->ctgan)\n Downloading nvidia_cuda_runtime_cu12-12.4.127-py3-none-manylinux2014_x86_64.whl.metadata (1.5 kB)\nCollecting nvidia-cuda-cupti-cu12==12.4.127 (from torch>=2.0.0->ctgan)\n Downloading nvidia_cuda_cupti_cu12-12.4.127-py3-none-manylinux2014_x86_64.whl.metadata (1.6 kB)\nCollecting nvidia-cudnn-cu12==9.1.0.70 (from torch>=2.0.0->ctgan)\n Downloading nvidia_cudnn_cu12-9.1.0.70-py3-none-manylinux2014_x86_64.whl.metadata (1.6 kB)\nCollecting nvidia-cublas-cu12==12.4.5.8 (from torch>=2.0.0->ctgan)\n Downloading nvidia_cublas_cu12-12.4.5.8-py3-none-manylinux2014_x86_64.whl.metadata (1.5 kB)\nCollecting nvidia-cufft-cu12==11.2.1.3 (from torch>=2.0.0->ctgan)\n Downloading nvidia_cufft_cu12-11.2.1.3-py3-none-manylinux2014_x86_64.whl.metadata (1.5 kB)\nCollecting nvidia-curand-cu12==10.3.5.147 (from torch>=2.0.0->ctgan)\n Downloading nvidia_curand_cu12-10.3.5.147-py3-none-manylinux2014_x86_64.whl.metadata (1.5 kB)\nCollecting nvidia-cusolver-cu12==11.6.1.9 (from torch>=2.0.0->ctgan)\n Downloading nvidia_cusolver_cu12-11.6.1.9-py3-none-manylinux2014_x86_64.whl.metadata (1.6 kB)\nCollecting nvidia-cusparse-cu12==12.3.1.170 (from torch>=2.0.0->ctgan)\n Downloading nvidia_cusparse_cu12-12.3.1.170-py3-none-manylinux2014_x86_64.whl.metadata (1.6 kB)\nRequirement already satisfied: nvidia-cusparselt-cu12==0.6.2 in /usr/local/lib/python3.11/dist-packages (from torch>=2.0.0->ctgan) (0.6.2)\nRequirement already satisfied: nvidia-nccl-cu12==2.21.5 in /usr/local/lib/python3.11/dist-packages (from torch>=2.0.0->ctgan) (2.21.5)\nRequirement already satisfied: nvidia-nvtx-cu12==12.4.127 in /usr/local/lib/python3.11/dist-packages (from torch>=2.0.0->ctgan) (12.4.127)\nCollecting nvidia-nvjitlink-cu12==12.4.127 (from torch>=2.0.0->ctgan)\n Downloading nvidia_nvjitlink_cu12-12.4.127-py3-none-manylinux2014_x86_64.whl.metadata (1.5 kB)\nRequirement already satisfied: triton==3.2.0 in /usr/local/lib/python3.11/dist-packages (from torch>=2.0.0->ctgan) (3.2.0)\nRequirement already satisfied: sympy==1.13.1 in /usr/local/lib/python3.11/dist-packages (from torch>=2.0.0->ctgan) (1.13.1)\nRequirement already satisfied: mpmath<1.4,>=1.1.0 in /usr/local/lib/python3.11/dist-packages (from sympy==1.13.1->torch>=2.0.0->ctgan) (1.3.0)\nRequirement already satisfied: six>=1.5 in /usr/local/lib/python3.11/dist-packages (from python-dateutil>=2.8.2->pandas>=1.5.0->ctgan) (1.17.0)\nRequirement already satisfied: joblib>=1.1.1 in /usr/local/lib/python3.11/dist-packages (from scikit-learn>=1.1.3->rdt>=1.14.0->ctgan) (1.5.2)\nRequirement already satisfied: threadpoolctl>=2.0.0 in /usr/local/lib/python3.11/dist-packages (from scikit-learn>=1.1.3->rdt>=1.14.0->ctgan) (3.6.0)\nRequirement already satisfied: MarkupSafe>=2.0 in /usr/local/lib/python3.11/dist-packages (from jinja2->torch>=2.0.0->ctgan) (3.0.3)\nRequirement already satisfied: onemkl-license==2025.3.0 in /usr/local/lib/python3.11/dist-packages (from mkl->numpy>=1.23.3->ctgan) (2025.3.0)\nRequirement already satisfied: intel-openmp<2026,>=2024 in /usr/local/lib/python3.11/dist-packages (from mkl->numpy>=1.23.3->ctgan) (2024.2.0)\nRequirement already satisfied: tbb==2022.* in /usr/local/lib/python3.11/dist-packages (from mkl->numpy>=1.23.3->ctgan) (2022.3.0)\nRequirement already satisfied: tcmlib==1.* in /usr/local/lib/python3.11/dist-packages (from tbb==2022.*->mkl->numpy>=1.23.3->ctgan) (1.4.0)\nRequirement already satisfied: intel-cmplr-lib-rt in /usr/local/lib/python3.11/dist-packages (from mkl_umath->numpy>=1.23.3->ctgan) (2024.2.0)\nRequirement already satisfied: intel-cmplr-lib-ur==2024.2.0 in /usr/local/lib/python3.11/dist-packages (from intel-openmp<2026,>=2024->mkl->numpy>=1.23.3->ctgan) (2024.2.0)\nDownloading ctgan-0.11.1-py3-none-any.whl (25 kB)\nDownloading rdt-1.18.2-py3-none-any.whl (74 kB)\n\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m74.3/74.3 kB\u001b[0m \u001b[31m3.3 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n\u001b[?25hDownloading nvidia_cublas_cu12-12.4.5.8-py3-none-manylinux2014_x86_64.whl (363.4 MB)\n\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m363.4/363.4 MB\u001b[0m \u001b[31m5.2 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m:00:01\u001b[0m00:01\u001b[0m\n\u001b[?25hDownloading nvidia_cuda_cupti_cu12-12.4.127-py3-none-manylinux2014_x86_64.whl (13.8 MB)\n\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m13.8/13.8 MB\u001b[0m \u001b[31m104.9 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m00:01\u001b[0m0:01\u001b[0m\n\u001b[?25hDownloading nvidia_cuda_nvrtc_cu12-12.4.127-py3-none-manylinux2014_x86_64.whl (24.6 MB)\n\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m24.6/24.6 MB\u001b[0m \u001b[31m80.2 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m:00:01\u001b[0m00:01\u001b[0m\n\u001b[?25hDownloading nvidia_cuda_runtime_cu12-12.4.127-py3-none-manylinux2014_x86_64.whl (883 kB)\n\u001b[2K \u001b[90m━━━━━━━━━━━━���━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m883.7/883.7 kB\u001b[0m \u001b[31m59.1 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n\u001b[?25hDownloading nvidia_cudnn_cu12-9.1.0.70-py3-none-manylinux2014_x86_64.whl (664.8 MB)\n\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m664.8/664.8 MB\u001b[0m \u001b[31m2.8 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m:00:01\u001b[0m00:01\u001b[0m\n\u001b[?25hDownloading nvidia_cufft_cu12-11.2.1.3-py3-none-manylinux2014_x86_64.whl (211.5 MB)\n\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m211.5/211.5 MB\u001b[0m \u001b[31m8.9 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m:00:01\u001b[0m00:01\u001b[0m\n\u001b[?25hDownloading nvidia_curand_cu12-10.3.5.147-py3-none-manylinux2014_x86_64.whl (56.3 MB)\n\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m56.3/56.3 MB\u001b[0m \u001b[31m23.9 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m:00:01\u001b[0m00:01\u001b[0m\n\u001b[?25hDownloading nvidia_cusolver_cu12-11.6.1.9-py3-none-manylinux2014_x86_64.whl (127.9 MB)\n\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m127.9/127.9 MB\u001b[0m \u001b[31m10.6 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m00:01\u001b[0m00:01\u001b[0m\n\u001b[?25hDownloading nvidia_cusparse_cu12-12.3.1.170-py3-none-manylinux2014_x86_64.whl (207.5 MB)\n\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m207.5/207.5 MB\u001b[0m \u001b[31m9.2 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m:00:01\u001b[0m00:01\u001b[0m\n\u001b[?25hDownloading nvidia_nvjitlink_cu12-12.4.127-py3-none-manylinux2014_x86_64.whl (21.1 MB)\n\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m21.1/21.1 MB\u001b[0m \u001b[31m84.2 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m:00:01\u001b[0m00:01\u001b[0m\n\u001b[?25hDownloading faker-38.2.0-py3-none-any.whl (2.0 MB)\n\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m2.0/2.0 MB\u001b[0m \u001b[31m81.6 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n\u001b[?25hInstalling collected packages: nvidia-nvjitlink-cu12, nvidia-curand-cu12, nvidia-cufft-cu12, nvidia-cuda-runtime-cu12, nvidia-cuda-nvrtc-cu12, nvidia-cuda-cupti-cu12, nvidia-cublas-cu12, Faker, nvidia-cusparse-cu12, nvidia-cudnn-cu12, nvidia-cusolver-cu12, rdt, ctgan\n Attempting uninstall: nvidia-nvjitlink-cu12\n Found existing installation: nvidia-nvjitlink-cu12 12.5.82\n Uninstalling nvidia-nvjitlink-cu12-12.5.82:\n Successfully uninstalled nvidia-nvjitlink-cu12-12.5.82\n Attempting uninstall: nvidia-curand-cu12\n Found existing installation: nvidia-curand-cu12 10.3.6.82\n Uninstalling nvidia-curand-cu12-10.3.6.82:\n Successfully uninstalled nvidia-curand-cu12-10.3.6.82\n Attempting uninstall: nvidia-cufft-cu12\n Found existing installation: nvidia-cufft-cu12 11.2.3.61\n Uninstalling nvidia-cufft-cu12-11.2.3.61:\n Successfully uninstalled nvidia-cufft-cu12-11.2.3.61\n Attempting uninstall: nvidia-cuda-runtime-cu12\n Found existing installation: nvidia-cuda-runtime-cu12 12.5.82\n Uninstalling nvidia-cuda-runtime-cu12-12.5.82:\n Successfully uninstalled nvidia-cuda-runtime-cu12-12.5.82\n Attempting uninstall: nvidia-cuda-nvrtc-cu12\n Found existing installation: nvidia-cuda-nvrtc-cu12 12.5.82\n Uninstalling nvidia-cuda-nvrtc-cu12-12.5.82:\n Successfully uninstalled nvidia-cuda-nvrtc-cu12-12.5.82\n Attempting uninstall: nvidia-cuda-cupti-cu12\n Found existing installation: nvidia-cuda-cupti-cu12 12.5.82\n Uninstalling nvidia-cuda-cupti-cu12-12.5.82:\n Successfully uninstalled nvidia-cuda-cupti-cu12-12.5.82\n Attempting uninstall: nvidia-cublas-cu12\n Found existing installation: nvidia-cublas-cu12 12.5.3.2\n Uninstalling nvidia-cublas-cu12-12.5.3.2:\n Successfully uninstalled nvidia-cublas-cu12-12.5.3.2\n Attempting uninstall: nvidia-cusparse-cu12\n Found existing installation: nvidia-cusparse-cu12 12.5.1.3\n Uninstalling nvidia-cusparse-cu12-12.5.1.3:\n Successfully uninstalled nvidia-cusparse-cu12-12.5.1.3\n Attempting uninstall: nvidia-cudnn-cu12\n Found existing installation: nvidia-cudnn-cu12 9.3.0.75\n Uninstalling nvidia-cudnn-cu12-9.3.0.75:\n Successfully uninstalled nvidia-cudnn-cu12-9.3.0.75\n Attempting uninstall: nvidia-cusolver-cu12\n Found existing installation: nvidia-cusolver-cu12 11.6.3.83\n Uninstalling nvidia-cusolver-cu12-11.6.3.83:\n Successfully uninstalled nvidia-cusolver-cu12-11.6.3.83\n\u001b[31mERROR: pip's dependency resolver does not currently take into account all the packages that are installed. This behaviour is the source of the following dependency conflicts.\nlibcugraph-cu12 25.6.0 requires libraft-cu12==25.6.*, but you have libraft-cu12 25.2.0 which is incompatible.\npylibcugraph-cu12 25.6.0 requires pylibraft-cu12==25.6.*, but you have pylibraft-cu12 25.2.0 which is incompatible.\npylibcugraph-cu12 25.6.0 requires rmm-cu12==25.6.*, but you have rmm-cu12 25.2.0 which is incompatible.\u001b[0m\u001b[31m\n\u001b[0mSuccessfully installed Faker-38.2.0 ctgan-0.11.1 nvidia-cublas-cu12-12.4.5.8 nvidia-cuda-cupti-cu12-12.4.127 nvidia-cuda-nvrtc-cu12-12.4.127 nvidia-cuda-runtime-cu12-12.4.127 nvidia-cudnn-cu12-9.1.0.70 nvidia-cufft-cu12-11.2.1.3 nvidia-curand-cu12-10.3.5.147 nvidia-cusolver-cu12-11.6.1.9 nvidia-cusparse-cu12-12.3.1.170 nvidia-nvjitlink-cu12-12.4.127 rdt-1.18.2\n","output_type":"stream"}],"execution_count":4},{"cell_type":"code","source":"import gymnasium as gym\nfrom gymnasium import spaces\nimport numpy as np\nimport pandas as pd\nimport torch\nimport os\nimport matplotlib.pyplot as plt\nimport seaborn as sns\nfrom sklearn.model_selection import train_test_split\nfrom sklearn.preprocessing import StandardScaler, OneHotEncoder\nfrom sklearn.decomposition import PCA\nfrom sklearn.metrics import confusion_matrix, accuracy_score, precision_score, recall_score, f1_score\nfrom sklearn.compose import ColumnTransformer\n\nfrom stable_baselines3 import PPO, A2C, DQN\nfrom stable_baselines3.common.vec_env import DummyVecEnv\nfrom ctgan import CTGAN\n\n# Set random seed for reproducibility\nSEED = 42\nnp.random.seed(SEED)\ntorch.manual_seed(SEED)\n\n# Define a learning rate schedule\ndef linear_schedule(initial_value):\n def schedule(progress_remaining):\n return progress_remaining * initial_value\n return schedule\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-02T08:42:44.030136Z","iopub.execute_input":"2025-12-02T08:42:44.030514Z","iopub.status.idle":"2025-12-02T08:42:44.194310Z","shell.execute_reply.started":"2025-12-02T08:42:44.030482Z","shell.execute_reply":"2025-12-02T08:42:44.193597Z"}},"outputs":[{"name":"stderr","text":"/usr/local/lib/python3.11/dist-packages/rdt/transformers/utils.py:18: DeprecationWarning: module 'sre_parse' is deprecated\n import sre_parse # isort:skip\n","output_type":"stream"}],"execution_count":5},{"cell_type":"markdown","source":"## 1. Custom RL Environment","metadata":{}},{"cell_type":"code","source":"class FraudDetectionEnv(gym.Env):\n \"\"\"\n A custom Gym environment for Fraud Detection.\n State: Feature vector of a transaction.\n Action: 0 (Declare Not Fraud), 1 (Declare Fraud).\n Reward: Based on correctly/incorrectly classifying fraud vs non-fraud.\n \"\"\"\n def __init__(self, features: np.ndarray, labels: np.ndarray, reward_config: dict = None):\n super().__init__()\n \n if reward_config is None:\n # Default reward configuration favoring recall of fraud\n self.reward_config = {\n 'TP': 10.0,\n 'FP': -5.0,\n 'FN': -20.0,\n 'TN': 1.0\n}\n\n else:\n self.reward_config = reward_config\n\n self.features = features.astype(np.float32)\n self.labels = labels.astype(np.int64)\n\n self.num_instances = self.features.shape[0]\n self.feature_dim = self.features.shape[1]\n\n # Action Space: Discrete(2) -> 0 for Not Fraud, 1 for Fraud\n self.action_space = spaces.Discrete(2)\n\n # Observation Space: Box(low, high, shape, dtype)\n self.observation_space = spaces.Box(\n low=-np.inf, high=np.inf, \n shape=(self.feature_dim,), \n dtype=np.float32\n )\n\n self._current_index = 0\n self._order = np.arange(self.num_instances)\n np.random.shuffle(self._order)\n\n def step(self, action: int):\n if self._current_index >= self.num_instances:\n return self.observation_space.sample() * 0, 0, True, False, {}\n\n actual_index = self._order[self._current_index]\n true_label = self.labels[actual_index]\n\n # Calculate Reward\n reward = 0\n if action == 1 and true_label == 1:\n reward = self.reward_config['TP']\n elif action == 1 and true_label == 0:\n reward = self.reward_config['FP']\n elif action == 0 and true_label == 1:\n reward = self.reward_config['FN']\n elif action == 0 and true_label == 0:\n reward = self.reward_config['TN']\n\n self._current_index += 1\n done = self._current_index >= self.num_instances\n truncated = False\n\n next_observation = np.zeros(self.feature_dim, dtype=np.float32)\n if not done:\n next_observation = self.features[self._order[self._current_index]]\n\n info = {\n 'true_label': true_label,\n 'predicted_action': action,\n 'is_done': done\n }\n\n return next_observation, reward, done, truncated, info\n\n def reset(self, seed=None, options=None):\n super().reset(seed=seed)\n self._current_index = 0\n self._order = np.arange(self.num_instances)\n np.random.shuffle(self._order)\n \n initial_observation = self.features[self._order[self._current_index]]\n return initial_observation, {}","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-02T08:42:44.195742Z","iopub.execute_input":"2025-12-02T08:42:44.195998Z","iopub.status.idle":"2025-12-02T08:42:44.205640Z","shell.execute_reply.started":"2025-12-02T08:42:44.195983Z","shell.execute_reply":"2025-12-02T08:42:44.204948Z"}},"outputs":[],"execution_count":6},{"cell_type":"markdown","source":"## 2. Data Loading and Preprocessing","metadata":{}},{"cell_type":"code","source":"def load_creditcard_data(path=\"creditcard.csv\"):\n print(\"Loading CreditCard dataset...\")\n df = pd.read_csv(path)\n \n # 1. Balance Data (1:5 Fraud to Non-Fraud)\n fraud = df[df['Class'] == 1]\n non_fraud = df[df['Class'] == 0]\n \n # Undersample non-fraud\n n_fraud = len(fraud)\n n_non_fraud = n_fraud * 5\n \n if len(non_fraud) > n_non_fraud:\n non_fraud = non_fraud.sample(n=n_non_fraud, random_state=SEED)\n \n balanced_df = pd.concat([fraud, non_fraud]).sample(frac=1, random_state=SEED).reset_index(drop=True)\n print(f\"Balanced CreditCard Data: {len(fraud)} Fraud, {len(non_fraud)} Non-Fraud\")\n\n # 2. Split\n X = balanced_df.drop('Class', axis=1).values\n y = balanced_df['Class'].values\n \n X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=SEED, stratify=y)\n \n # 3. Scale (Fit on Train, Transform Test)\n # Scale Time (0) and Amount (29)\n def scale_columns(X_tr, X_te, indices):\n for i in indices:\n scaler_i = StandardScaler()\n X_tr[:, i] = scaler_i.fit_transform(X_tr[:, i].reshape(-1, 1)).flatten()\n X_te[:, i] = scaler_i.transform(X_te[:, i].reshape(-1, 1)).flatten()\n return X_tr, X_te\n\n if X_train.shape[1] == 30:\n X_train, X_test = scale_columns(X_train, X_test, [0, 29])\n \n return X_train, X_test, y_train, y_test\n\ndef load_paysim_data(path=\"paysim.csv\"):\n print(\"Loading PaySim dataset...\")\n df = pd.read_csv(path)\n \n # Drop unnecessary columns\n df = df.drop(['nameOrig', 'nameDest', 'isFlaggedFraud'], axis=1)\n \n # Rename 'isFraud' to 'Class' for consistency\n df = df.rename(columns={'isFraud': 'Class'})\n \n # One-hot encode 'type'\n df = pd.get_dummies(df, columns=['type'], drop_first=True)\n \n # 1. Balance Data (1:5 Fraud to Non-Fraud)\n fraud = df[df['Class'] == 1]\n non_fraud = df[df['Class'] == 0]\n \n # Undersample non-fraud\n n_fraud = len(fraud)\n n_non_fraud = n_fraud * 5\n \n if len(non_fraud) > n_non_fraud:\n non_fraud = non_fraud.sample(n=n_non_fraud, random_state=SEED)\n \n balanced_df = pd.concat([fraud, non_fraud]).sample(frac=1, random_state=SEED).reset_index(drop=True)\n print(f\"Balanced PaySim Data: {len(fraud)} Fraud, {len(non_fraud)} Non-Fraud\")\n \n # 2. Split\n X = balanced_df.drop('Class', axis=1).values.astype(np.float32)\n y = balanced_df['Class'].values.astype(np.int64)\n \n X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=SEED, stratify=y)\n \n # 3. Scale\n scaler = StandardScaler()\n X_train = scaler.fit_transform(X_train)\n X_test = scaler.transform(X_test)\n \n return X_train, X_test, y_train, y_test\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-02T08:42:44.206516Z","iopub.execute_input":"2025-12-02T08:42:44.206783Z","iopub.status.idle":"2025-12-02T08:42:44.223599Z","shell.execute_reply.started":"2025-12-02T08:42:44.206757Z","shell.execute_reply":"2025-12-02T08:42:44.222946Z"}},"outputs":[],"execution_count":7},{"cell_type":"markdown","source":"## 3. Preprocessing Techniques (PCA & CTGAN)","metadata":{}},{"cell_type":"code","source":"def apply_pca_ctgan(X_train, y_train, X_test, n_components=0.99, epochs=200):\n print(f\"Applying PCA (n_components={n_components}) + CTGAN (epochs={epochs})...\")\n \n # 1. Apply PCA\n pca = PCA(n_components=n_components)\n X_train_pca = pca.fit_transform(X_train)\n X_test_pca = pca.transform(X_test)\n print(f\"PCA reduced dimensions from {X_train.shape[1]} to {X_train_pca.shape[1]}\")\n \n # 2. Apply CTGAN on PCA-transformed data\n # Combine X and y for CTGAN\n df_train = pd.DataFrame(X_train_pca, columns=[f'f{i}' for i in range(X_train_pca.shape[1])])\n df_train['label'] = y_train\n \n # Filter fraud samples\n fraud_df = df_train[df_train['label'] == 1].drop('label', axis=1)\n \n if len(fraud_df) == 0:\n print(\"No fraud samples found for CTGAN!\")\n return X_train_pca, y_train, X_test_pca\n \n # Train CTGAN\n ctgan = CTGAN(epochs=epochs, batch_size=64, pac=1, verbose=True)\n ctgan.fit(fraud_df)\n \n # Generate synthetic samples to balance the dataset (or double the fraud count)\n n_synthetic = len(fraud_df) \n synthetic_fraud = ctgan.sample(n_synthetic)\n \n X_synthetic = synthetic_fraud.values\n y_synthetic = np.ones(n_synthetic)\n \n X_aug = np.vstack([X_train_pca, X_synthetic])\n y_aug = np.concatenate([y_train, y_synthetic])\n \n print(f\"Augmented training set from {len(X_train_pca)} to {len(X_aug)} samples.\")\n return X_aug, y_aug, X_test_pca\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-02T08:42:44.249293Z","iopub.execute_input":"2025-12-02T08:42:44.249852Z","iopub.status.idle":"2025-12-02T08:42:44.255758Z","shell.execute_reply.started":"2025-12-02T08:42:44.249833Z","shell.execute_reply":"2025-12-02T08:42:44.255071Z"}},"outputs":[],"execution_count":8},{"cell_type":"markdown","source":"## 4. Experiment Loop","metadata":{}},{"cell_type":"code","source":"def train_and_evaluate(agent_name, X_train, y_train, X_test, y_test, total_timesteps=10000):\n # Create Environment\n env = DummyVecEnv([lambda: FraudDetectionEnv(X_train, y_train)])\n \n # Initialize Agent\n if agent_name == 'PPO':\n # Default PPO parameters as none were specified in reference\n model = PPO('MlpPolicy', env, verbose=0)\n elif agent_name == 'A2C':\n model = A2C(\n \"MlpPolicy\",\n env,\n learning_rate=1e-4,\n gamma=0.99,\n n_steps=5,\n ent_coef=0.01,\n vf_coef=0.5,\n max_grad_norm=0.5,\n verbose=0,\n device=\"auto\"\n )\n elif agent_name == 'DQN':\n model = DQN(\n \"MlpPolicy\",\n env,\n learning_rate=linear_schedule(1e-4),\n buffer_size=100000,\n learning_starts=1000,\n batch_size=512,\n gamma=0.99,\n train_freq=1,\n gradient_steps=1,\n target_update_interval=500,\n exploration_fraction=0.1,\n exploration_initial_eps=1.0,\n exploration_final_eps=0.05,\n max_grad_norm=10,\n verbose=0,\n device=\"auto\"\n )\n else:\n raise ValueError(f\"Unknown agent: {agent_name}\")\n \n # Train\n model.learn(total_timesteps=total_timesteps)\n \n # Evaluate\n # We'll use the environment logic to step through test set\n test_env = FraudDetectionEnv(X_test, y_test)\n obs, _ = test_env.reset()\n \n y_pred = []\n y_true = []\n \n done = False\n while not done:\n action, _ = model.predict(obs, deterministic=True)\n obs, reward, done, truncated, info = test_env.step(action)\n \n if 'true_label' in info:\n y_true.append(info['true_label'])\n y_pred.append(action)\n \n # Metrics\n precision = precision_score(y_true, y_pred, zero_division=0)\n recall = recall_score(y_true, y_pred, zero_division=0)\n f1 = f1_score(y_true, y_pred, zero_division=0)\n \n return precision, recall, f1\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-02T08:42:44.278425Z","iopub.execute_input":"2025-12-02T08:42:44.278595Z","iopub.status.idle":"2025-12-02T08:42:44.285248Z","shell.execute_reply.started":"2025-12-02T08:42:44.278581Z","shell.execute_reply":"2025-12-02T08:42:44.284588Z"}},"outputs":[],"execution_count":9},{"cell_type":"code","source":"# Configuration\ndatasets = ['CreditCard', 'PaySim']\npreprocessing_methods = ['Raw', 'PCA_CTGAN']\nagents = ['PPO', 'A2C', 'DQN']\n\nresults = []\n\nfor dataset_name in datasets:\n print(f\"\\n=== Processing Dataset: {dataset_name} ===\")\n \n # Load Data\n if dataset_name == 'CreditCard':\n X_train, X_test, y_train, y_test = load_creditcard_data(\"/kaggle/input/creditcardfraud/creditcard.csv\")\n else:\n X_train, X_test, y_train, y_test = load_paysim_data(\"/kaggle/input/paysim1/PS_20174392719_1491204439457_log.csv\")\n \n # Subsample PaySim for speed if it's too large (optional, but recommended for quick ablation)\n # PaySim is huge, so let's take a subset for this study\n if len(X_train) > 500000:\n print(\"Subsampling PaySim to 500k samples for speed...\")\n indices = np.random.choice(len(X_train), 500000, replace=False)\n X_train = X_train[indices]\n y_train = y_train[indices]\n # Also subsample test\n test_indices = np.random.choice(len(X_test), 100000, replace=False)\n X_test = X_test[test_indices]\n y_test = y_test[test_indices]\n\n # Store original copies\n X_train_orig, X_test_orig = X_train.copy(), X_test.copy()\n y_train_orig = y_train.copy()\n\n for prep in preprocessing_methods:\n print(f\"\\n--- Preprocessing: {prep} ---\")\n \n # Reset data\n X_curr_train, X_curr_test = X_train_orig.copy(), X_test_orig.copy()\n y_curr_train = y_train_orig.copy()\n \n # Apply Preprocessing\n if prep == 'PCA_CTGAN':\n X_curr_train, y_curr_train, X_curr_test = apply_pca_ctgan(X_curr_train, y_curr_train, X_curr_test, epochs=200)\n \n for agent in agents:\n print(f\"Training {agent}...\")\n try:\n prec, rec, f1 = train_and_evaluate(agent, X_curr_train, y_curr_train, X_curr_test, y_test, total_timesteps=5000)\n print(f\"Result: F1={f1:.4f} (Prec={prec:.4f}, Rec={rec:.4f})\")\n \n results.append({\n 'Dataset': dataset_name,\n 'Preprocessing': prep,\n 'Agent': agent,\n 'Precision': prec,\n 'Recall': rec,\n 'F1': f1\n })\n except Exception as e:\n print(f\"Failed to train {agent}: {e}\")\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-02T08:42:44.311506Z","iopub.execute_input":"2025-12-02T08:42:44.311705Z","iopub.status.idle":"2025-12-02T08:52:32.225896Z","shell.execute_reply.started":"2025-12-02T08:42:44.311690Z","shell.execute_reply":"2025-12-02T08:52:32.225198Z"}},"outputs":[{"name":"stdout","text":"\n=== Processing Dataset: CreditCard ===\nLoading CreditCard dataset...\nBalanced CreditCard Data: 492 Fraud, 2460 Non-Fraud\n\n--- Preprocessing: Raw ---\nTraining PPO...\nResult: F1=0.7667 (Prec=0.6479, Rec=0.9388)\nTraining A2C...\nResult: F1=0.6460 (Prec=0.4870, Rec=0.9592)\nTraining DQN...\nResult: F1=0.9355 (Prec=0.9886, Rec=0.8878)\n\n--- Preprocessing: PCA_CTGAN ---\nApplying PCA (n_components=0.99) + CTGAN (epochs=200)...\nPCA reduced dimensions from 30 to 24\n","output_type":"stream"},{"name":"stderr","text":"Gen. (-1.22) | Discrim. (-0.19): 100%|██████████| 200/200 [00:38<00:00, 5.26it/s]\n","output_type":"stream"},{"name":"stdout","text":"Augmented training set from 2361 to 2755 samples.\nTraining PPO...\nResult: F1=0.8708 (Prec=0.8198, Rec=0.9286)\nTraining A2C...\nResult: F1=0.9175 (Prec=0.9271, Rec=0.9082)\nTraining DQN...\nResult: F1=0.8911 (Prec=0.8654, Rec=0.9184)\n\n=== Processing Dataset: PaySim ===\nLoading PaySim dataset...\nBalanced PaySim Data: 8213 Fraud, 41065 Non-Fraud\n\n--- Preprocessing: Raw ---\nTraining PPO...\nResult: F1=0.6854 (Prec=0.5757, Rec=0.8466)\nTraining A2C...\nResult: F1=0.6718 (Prec=0.5834, Rec=0.7918)\nTraining DQN...\nResult: F1=0.7020 (Prec=0.6672, Rec=0.7407)\n\n--- Preprocessing: PCA_CTGAN ---\nApplying PCA (n_components=0.99) + CTGAN (epochs=200)...\nPCA reduced dimensions from 10 to 8\n","output_type":"stream"},{"name":"stderr","text":"Gen. (-0.05) | Discrim. (-0.19): 100%|██████████| 200/200 [05:42<00:00, 1.71s/it]\n","output_type":"stream"},{"name":"stdout","text":"Augmented training set from 39422 to 45992 samples.\nTraining PPO...\nResult: F1=0.5086 (Prec=0.3453, Rec=0.9647)\nTraining A2C...\nResult: F1=0.5243 (Prec=0.3649, Rec=0.9306)\nTraining DQN...\nResult: F1=0.7055 (Prec=0.6381, Rec=0.7888)\n","output_type":"stream"}],"execution_count":10},{"cell_type":"markdown","source":"## 5. Results Visualization","metadata":{}},{"cell_type":"code","source":"# Create DataFrame from results sorted by F1 score\nresults_df = pd.DataFrame(results)\nsorted_results_df = results_df.sort_values(by='F1', ascending=False)\n\nprint(\"\\n=== Final Results ===\")\n(results_df)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-02T08:57:53.271031Z","iopub.execute_input":"2025-12-02T08:57:53.271521Z","iopub.status.idle":"2025-12-02T08:57:53.289311Z","shell.execute_reply.started":"2025-12-02T08:57:53.271497Z","shell.execute_reply":"2025-12-02T08:57:53.288745Z"}},"outputs":[{"name":"stdout","text":"\n=== Final Results ===\n","output_type":"stream"},{"execution_count":13,"output_type":"execute_result","data":{"text/plain":" Dataset Preprocessing Agent Precision Recall F1\n0 CreditCard Raw PPO 0.647887 0.938776 0.766667\n1 CreditCard Raw A2C 0.487047 0.959184 0.646048\n2 CreditCard Raw DQN 0.988636 0.887755 0.935484\n3 CreditCard PCA_CTGAN PPO 0.819820 0.928571 0.870813\n4 CreditCard PCA_CTGAN A2C 0.927083 0.908163 0.917526\n5 CreditCard PCA_CTGAN DQN 0.865385 0.918367 0.891089\n6 PaySim Raw PPO 0.575745 0.846622 0.685390\n7 PaySim Raw A2C 0.583408 0.791844 0.671831\n8 PaySim Raw DQN 0.667215 0.740718 0.702048\n9 PaySim PCA_CTGAN PPO 0.345316 0.964699 0.508583\n10 PaySim PCA_CTGAN A2C 0.364916 0.930615 0.524259\n11 PaySim PCA_CTGAN DQN 0.638109 0.788801 0.705498","text/html":"<div>\n<style scoped>\n .dataframe tbody tr th:only-of-type {\n vertical-align: middle;\n }\n\n .dataframe tbody tr th {\n vertical-align: top;\n }\n\n .dataframe thead th {\n text-align: right;\n }\n</style>\n<table border=\"1\" class=\"dataframe\">\n <thead>\n <tr style=\"text-align: right;\">\n <th></th>\n <th>Dataset</th>\n <th>Preprocessing</th>\n <th>Agent</th>\n <th>Precision</th>\n <th>Recall</th>\n <th>F1</th>\n </tr>\n </thead>\n <tbody>\n <tr>\n <th>0</th>\n <td>CreditCard</td>\n <td>Raw</td>\n <td>PPO</td>\n <td>0.647887</td>\n <td>0.938776</td>\n <td>0.766667</td>\n </tr>\n <tr>\n <th>1</th>\n <td>CreditCard</td>\n <td>Raw</td>\n <td>A2C</td>\n <td>0.487047</td>\n <td>0.959184</td>\n <td>0.646048</td>\n </tr>\n <tr>\n <th>2</th>\n <td>CreditCard</td>\n <td>Raw</td>\n <td>DQN</td>\n <td>0.988636</td>\n <td>0.887755</td>\n <td>0.935484</td>\n </tr>\n <tr>\n <th>3</th>\n <td>CreditCard</td>\n <td>PCA_CTGAN</td>\n <td>PPO</td>\n <td>0.819820</td>\n <td>0.928571</td>\n <td>0.870813</td>\n </tr>\n <tr>\n <th>4</th>\n <td>CreditCard</td>\n <td>PCA_CTGAN</td>\n <td>A2C</td>\n <td>0.927083</td>\n <td>0.908163</td>\n <td>0.917526</td>\n </tr>\n <tr>\n <th>5</th>\n <td>CreditCard</td>\n <td>PCA_CTGAN</td>\n <td>DQN</td>\n <td>0.865385</td>\n <td>0.918367</td>\n <td>0.891089</td>\n </tr>\n <tr>\n <th>6</th>\n <td>PaySim</td>\n <td>Raw</td>\n <td>PPO</td>\n <td>0.575745</td>\n <td>0.846622</td>\n <td>0.685390</td>\n </tr>\n <tr>\n <th>7</th>\n <td>PaySim</td>\n <td>Raw</td>\n <td>A2C</td>\n <td>0.583408</td>\n <td>0.791844</td>\n <td>0.671831</td>\n </tr>\n <tr>\n <th>8</th>\n <td>PaySim</td>\n <td>Raw</td>\n <td>DQN</td>\n <td>0.667215</td>\n <td>0.740718</td>\n <td>0.702048</td>\n </tr>\n <tr>\n <th>9</th>\n <td>PaySim</td>\n <td>PCA_CTGAN</td>\n <td>PPO</td>\n <td>0.345316</td>\n <td>0.964699</td>\n <td>0.508583</td>\n </tr>\n <tr>\n <th>10</th>\n <td>PaySim</td>\n <td>PCA_CTGAN</td>\n <td>A2C</td>\n <td>0.364916</td>\n <td>0.930615</td>\n <td>0.524259</td>\n </tr>\n <tr>\n <th>11</th>\n <td>PaySim</td>\n <td>PCA_CTGAN</td>\n <td>DQN</td>\n <td>0.638109</td>\n <td>0.788801</td>\n <td>0.705498</td>\n </tr>\n </tbody>\n</table>\n</div>"},"metadata":{}}],"execution_count":13},{"cell_type":"code","source":"# Plotting\nplt.figure(figsize=(14, 6))\n\nplt.subplot(1, 2, 1)\nsns.barplot(data=results_df[results_df['Dataset'] == 'CreditCard'], x='Agent', y='F1', hue='Preprocessing')\nplt.title('CreditCard Dataset - F1 Score')\nplt.ylim(0, 1)\n\nplt.subplot(1, 2, 2)\nsns.barplot(data=results_df[results_df['Dataset'] == 'PaySim'], x='Agent', y='F1', hue='Preprocessing')\nplt.title('PaySim Dataset - F1 Score')\nplt.ylim(0, 1)\n\nplt.tight_layout()\nplt.show()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-02T08:58:30.402840Z","iopub.execute_input":"2025-12-02T08:58:30.403082Z","iopub.status.idle":"2025-12-02T08:58:30.735267Z","shell.execute_reply.started":"2025-12-02T08:58:30.403066Z","shell.execute_reply":"2025-12-02T08:58:30.734548Z"}},"outputs":[{"name":"stderr","text":"/usr/local/lib/python3.11/dist-packages/seaborn/_oldcore.py:1498: DeprecationWarning: is_categorical_dtype is deprecated and will be removed in a future version. Use isinstance(dtype, pd.CategoricalDtype) instead\n if pd.api.types.is_categorical_dtype(vector):\n/usr/local/lib/python3.11/dist-packages/seaborn/_oldcore.py:1498: DeprecationWarning: is_categorical_dtype is deprecated and will be removed in a future version. Use isinstance(dtype, pd.CategoricalDtype) instead\n if pd.api.types.is_categorical_dtype(vector):\n/usr/local/lib/python3.11/dist-packages/seaborn/_oldcore.py:1498: DeprecationWarning: is_categorical_dtype is deprecated and will be removed in a future version. Use isinstance(dtype, pd.CategoricalDtype) instead\n if pd.api.types.is_categorical_dtype(vector):\n/usr/local/lib/python3.11/dist-packages/seaborn/_oldcore.py:1498: DeprecationWarning: is_categorical_dtype is deprecated and will be removed in a future version. Use isinstance(dtype, pd.CategoricalDtype) instead\n if pd.api.types.is_categorical_dtype(vector):\n/usr/local/lib/python3.11/dist-packages/seaborn/_oldcore.py:1498: DeprecationWarning: is_categorical_dtype is deprecated and will be removed in a future version. Use isinstance(dtype, pd.CategoricalDtype) instead\n if pd.api.types.is_categorical_dtype(vector):\n/usr/local/lib/python3.11/dist-packages/seaborn/_oldcore.py:1498: DeprecationWarning: is_categorical_dtype is deprecated and will be removed in a future version. Use isinstance(dtype, pd.CategoricalDtype) instead\n if pd.api.types.is_categorical_dtype(vector):\n/usr/local/lib/python3.11/dist-packages/seaborn/_oldcore.py:1498: DeprecationWarning: is_categorical_dtype is deprecated and will be removed in a future version. Use isinstance(dtype, pd.CategoricalDtype) instead\n if pd.api.types.is_categorical_dtype(vector):\n/usr/local/lib/python3.11/dist-packages/seaborn/_oldcore.py:1498: DeprecationWarning: is_categorical_dtype is deprecated and will be removed in a future version. Use isinstance(dtype, pd.CategoricalDtype) instead\n if pd.api.types.is_categorical_dtype(vector):\n","output_type":"stream"},{"output_type":"display_data","data":{"text/plain":"<Figure size 1400x600 with 2 Axes>","image/png":"iVBORw0KGgoAAAANSUhEUgAABW0AAAJOCAYAAADMCCWlAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjcuMiwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy8pXeV/AAAACXBIWXMAAA9hAAAPYQGoP6dpAABn40lEQVR4nO3debhd89k//vfJeDIbIqNIQoxFEDXUlxhCqClKTUViVmJKawgqYqyZ56mhNAltqamooqZIzEOFKGoqMUtQJBEyr98fftmPIwkJJzlL8npd174ue63PWvte+5wV93nvtT+rqiiKIgAAAAAAlEK9ui4AAAAAAID/I7QFAAAAACgRoS0AAAAAQIkIbQEAAAAASkRoCwAAAABQIkJbAAAAAIASEdoCAAAAAJSI0BYAAAAAoESEtgAAAAAAJSK0BRa6kSNHpqqqKiNHjqws69evX7p06VJnNX0XV111VaqqqvLGG2/UdSkAAPyAnXLKKamqqqrrMgAoEaEtLIZee+21HHzwwVl++eVTXV2dli1bZqONNsrFF1+cL774oq7LS5J8/vnnOeWUU2oEu183evTo7LXXXunUqVMaN26cpZZaKr169cqwYcMyY8aMhVfst5jVhM96NG3aNMstt1y23377DBs2LFOmTPnO+77zzjtzyimn1F6x39OZZ56ZW2+9dYHs+6vv4Vcf7dq1q4x5//33c/zxx2ezzTZLixYtZvtwYF78/e9/T8+ePdOmTZs0bdo0yy+/fHbdddfcddddtXxEAMDCMuvD9lmP6urqrLTSSunfv3/GjRu3wF73s88+y6BBg7L66qunWbNmWXrppbPWWmvlyCOPzHvvvbfAXndu5vQ+dOjQIb17987//M//ZOLEid95348++mhOOeWUfPrpp7VX8Pdw6aWX5qqrrlog++7Spctce9PJkycn+b+f/dZbb52llloqVVVV813Pww8/nG222SYdO3ZMdXV15W+Ia6+9dgEcFVA2Deq6AGDhuuOOO/Lzn/88jRs3zj777JPVV189U6dOzcMPP5xjjjkmL7zwQq644oqFXteVV16ZmTNnVp5//vnnGTx4cJJk0003nW38H/7whxxyyCFp27Zt9t5776y44oqZOHFihg8fnv333z/vv/9+TjjhhIVV/jy57LLL0rx580yZMiXvvvtu7r777uy333656KKLcvvtt6dTp07zvc8777wzl1xySWmC2zPPPDO77LJL+vTps0D2v+WWW2afffapsaxJkyaV/3755Zdz9tlnZ8UVV8waa6yRxx57bL72f9555+WYY45Jz549M3DgwDRt2jT/+c9/ct999+W6667L1ltvXSvHAQDUjVNPPTVdu3bN5MmT8/DDD+eyyy7LnXfemeeffz5Nmzat1deaNm1aNtlkk7z00kvp27dvDj/88Hz22Wd54YUXcu2112annXZKhw4dkiQnnXRSjj/++Fp9/W8y632YNm1axo4dm5EjR+aoo47KBRdckNtuuy1rrrnmfO/z0UcfzeDBg9OvX78sscQStV/0fLr00kvTunXr9OvXb4Hsf6211sqvfvWr2ZY3atQoSfLRRx/l1FNPzXLLLZfu3bvP94UEN954Y3bbbbdKyL/kkktmzJgxefDBB3PllVdmzz33rI3DAEpMaAuLkTFjxmT33XdP586dc//996d9+/aVdYcddlj+85//5I477pjr9jNnzszUqVNTXV1d67U1bNhwnsc+/vjjOeSQQ7LhhhvmzjvvTIsWLSrrjjrqqDz11FN5/vnnv3dNtX28u+yyS1q3bl15fvLJJ+eaa67JPvvsk5///Od5/PHHa+V1FmUrrbRS9tprr7mu79GjR/773/9mqaWWyk033ZSf//zn87zv6dOn57TTTsuWW26Ze+65Z7b1H3zwwXeq+btYkOcaACzOttlmm6y77rpJkgMOOCBLL710Lrjggvztb3/LHnvsUauvdeutt+aZZ57JNddcM1vANnny5EydOrXyvEGDBmnQYOH9ef7V9yFJBg4cmPvvvz/bbbdddthhh7z44os1Phhndh07dvzGvrR9+/Z5//33065duzz11FP58Y9/PF/7P+WUU7Laaqvl8ccfrwTBsyzMvrQoikyePNnvA9QB0yPAYuScc87JZ599liFDhtQIbGfp1q1bjjzyyMrzqqqq9O/fP9dcc01+9KMfpXHjxpWviL/77rvZb7/90rZt2zRu3Dg/+tGPMnTo0Nn2+c4776RPnz5p1qxZ2rRpk6OPPnqO0wF8dU7bN954I8sss0ySZPDgwZWvGs26mnTWsmuuuaZGYDvLuuuuW+MT9fPOOy8/+clPsvTSS6dJkybp0aNHbrrpptm2+6bjfeGFF7L55punSZMmWXbZZXP66afXuDL4u/rFL36RAw44IE888UTuvffeyvKHHnooP//5z7PccsulcePG6dSpU44++uga01f069cvl1xySaX2WY/5Pe577703/+///b8sscQSad68eVZeeeXZrlKeMmVKBg0alG7dulXqOfbYY2v8LKuqqjJp0qRcffXVlVoW1JUNc9OiRYsstdRS32nbjz76KBMmTMhGG200x/Vt2rSp8Xzy5Mk55ZRTstJKK6W6ujrt27fPz372s7z22muVMZMmTcqvfvWryhQeK6+8cs4777wURVFjX7VxrgEA82/zzTdP8uXFDcm89U89e/ZM9+7d57i/lVdeOb17906SSk8wp95i1hRls8xpTttZ/cGNN96Y1VZbLU2aNMmGG26Y5557Lkny+9//Pt26dUt1dXU23XTT732fhc033zy/+c1v8uabb+bPf/5zZfm//vWv9OvXrzK1Wrt27bLffvvlv//9b436jznmmCRJ165dK73grJqGDRuWzTffPG3atEnjxo2z2mqr5bLLLputhqeeeiq9e/dO69at06RJk3Tt2jX77bdfjTEzZ87MRRddlB/96Eeprq5O27Ztc/DBB+eTTz6pjOnSpUteeOGFPPDAA5Va5vTtvQWpcePGNabxml+vvfZafvzjH88W2Caz96UzZ87MxRdfnDXWWCPV1dVZZpllsvXWW+epp56qjJl1gcIKK6yQxo0bp0uXLjnhhBNm+9usS5cu2W677XL33Xdn3XXXTZMmTfL73/8+SfLpp5/mqKOOqvS23bp1y9lnn10rfxcBs3OlLSxG/v73v2f55ZfPT37yk3ne5v77788NN9yQ/v37p3Xr1unSpUvGjRuXDTbYoNJILrPMMvnHP/6R/fffPxMmTMhRRx2VJPniiy+yxRZb5K233soRRxyRDh065E9/+lPuv//+b3zNZZZZJpdddll++ctfZqeddsrPfvazJMmaa66Zzz//PMOHD88mm2yS5ZZbbp6O4eKLL84OO+yQX/ziF5k6dWquu+66/PznP8/tt9+ebbfd9luPd+zYsdlss80yffr0HH/88WnWrFmuuOKKWvu0ee+9984VV1yRe+65J1tuuWWSL78O9fnnn+eXv/xlll566Tz55JP53//937zzzju58cYbkyQHH3xw3nvvvdx7773505/+9J2O+4UXXsh2222XNddcM6eeemoaN26c//znP3nkkUcq+5k5c2Z22GGHPPzwwznooIOy6qqr5rnnnsuFF16YV155pTKH7Z/+9KcccMABWW+99XLQQQclSVZYYYVaeY9mmTx5cj766KMay1q0aJHGjRt/7323adMmTZo0yd///vccfvjh3xj+zpgxI9ttt12GDx+e3XffPUceeWQmTpyYe++9N88//3xWWGGFFEWRHXbYISNGjMj++++ftdZaK3fffXeOOeaYvPvuu7nwwgtr7PP7nGsAwHczK1hdeumlk8xb/7T33nvnwAMPzPPPP5/VV1+9sq9//vOfeeWVV3LSSSclSTp37pwk+eMf/5iTTjrpO91o7KGHHsptt92Www47LEly1llnZbvttsuxxx6bSy+9NIceemg++eSTnHPOOdlvv/2+tc/+NnvvvXdOOOGE3HPPPTnwwAOTfPkB/+uvv55999037dq1q0yn9sILL+Txxx9PVVVVfvazn+WVV17JX/7yl1x44YWVb5fNuhDjsssuy49+9KPssMMOadCgQf7+97/n0EMPzcyZMyvH9sEHH2SrrbbKMsssk+OPPz5LLLFE3njjjdx88801ajz44INz1VVXZd99980RRxyRMWPG5He/+12eeeaZPPLII2nYsGEuuuiiHH744WnevHlOPPHEJEnbtm2/13vzddOmTZutL23atGmtTbPRuXPnDB8+PO+8806WXXbZbxy7//7756qrrso222yTAw44INOnT89DDz2Uxx9/vMaV5VdffXV22WWX/OpXv8oTTzyRs846Ky+++GJuueWWGvt7+eWXs8cee+Tggw/OgQcemJVXXjmff/55evbsmXfffTcHH3xwlltuuTz66KMZOHBg3n///Vx00UW1ctzAVxTAYmH8+PFFkmLHHXec522SFPXq1SteeOGFGsv333//on379sVHH31UY/nuu+9etGrVqvj888+LoiiKiy66qEhS3HDDDZUxkyZNKrp161YkKUaMGFFZ3rdv36Jz586V5x9++GGRpBg0aFCN13j22WeLJMWRRx45z8cxq55Zpk6dWqy++urF5ptvPk/He9RRRxVJiieeeKKy7IMPPihatWpVJCnGjBnzja8/aNCgIknx4YcfznH9J598UiQpdtppp7nWXBRFcdZZZxVVVVXFm2++WVl22GGHFXP7p3xejvvCCy/8xtqKoij+9Kc/FfXq1SseeuihGssvv/zyIknxyCOPVJY1a9as6Nu371z39X0kmeNj2LBhcxx/4403zvZ79m1OPvnkIknRrFmzYptttinOOOOMYtSoUbONGzp0aJGkuOCCC2ZbN3PmzKIoiuLWW28tkhSnn356jfW77LJLUVVVVfznP/+pcWzf51wDAL7ZsGHDiiTFfffdV3z44YfF22+/XVx33XXF0ksvXTRp0qR45513iqKYt/7p008/Laqrq4vjjjuuxtgjjjiiaNasWfHZZ59V9rXyyisXSYrOnTsX/fr1K4YMGVKMGzdutvpm9YtflaRo3LhxjV7z97//fZGkaNeuXTFhwoTK8oEDB85TXzrrffjnP/851zGtWrUq1l577crzOfUbf/nLX4okxYMPPlhZdu655861hjnto3fv3sXyyy9feX7LLbd8a20PPfRQkaS45ppraiy/6667Zlv+ox/9qOjZs+dc9/V9dO7ceY596df/dpnln//85zf2rXMyZMiQIknRqFGjYrPNNit+85vfFA899FAxY8aMGuPuv//+IklxxBFHzLaPWX3p6NGjiyTFAQccUGP9r3/96yJJcf/99892bHfddVeNsaeddlrRrFmz4pVXXqmx/Pjjjy/q169fvPXWW/N8bMC8MT0CLCYmTJiQJHOcTuCb9OzZM6uttlrleVEU+etf/5rtt98+RVHko48+qjx69+6d8ePH5+mnn07y5U2y2rdvn1122aWyfdOmTStXYS6s4/jqFbGffPJJxo8fn4033rhS51d9/XiTL49jgw02yHrrrVdZtswyy+QXv/jF/JY/R82bN0+SGnfr/WrNkyZNykcffZSf/OQnKYoizzzzzDztd16Oe9ZNIv72t7/N9WtNN954Y1ZdddWsssoqNX7es75OOGLEiHk70Fqw44475t57763xmPUVxNowePDgXHvttVl77bVz991358QTT0yPHj2yzjrr5MUXX6yM++tf/5rWrVvn8MMPn20fs66iufPOO1O/fv0cccQRNdb/6le/SlEU+cc//lFj+fc51wCAedOrV68ss8wy6dSpU3bfffc0b948t9xySzp27Jhk3vqnVq1aZccdd8xf/vKXypRHM2bMyPXXX1+ZFmzWvp544onKtAFXXXVV9t9//7Rv3z6HH374HKcM+7otttiiMoVYkqy//vpJkp133rlGPzxr+euvv/5d3pYamjdvPte+dNa3njbYYIMkmede5Kv7GD9+fD766KP07Nkzr7/+esaPH5/k//rS22+/PdOmTZvjfm688ca0atUqW265ZY3eqEePHmnevPlC7UvXX3/92frSr98w9/vYb7/9ctddd2XTTTfNww8/nNNOOy0bb7xxVlxxxTz66KOVcX/9619TVVWVQYMGzbaPr/alSTJgwIAa62fdSO3r9zXp2rXrbD32jTfemI033jhLLrlkjfe+V69emTFjRh588MHvf9BADaZHgMXErDmzvtqAzYuuXbvWeP7hhx/m008/zRVXXJErrrhijtvMmhj/zTffTLdu3Wb7KtjKK688XzV81Xc5jttvvz2nn356Ro8ePdscrF/39eNNvjyOWY3wV32f4/iqzz77LEnNIPqtt97KySefnNtuu63G/FxJKo3tt5mX495tt93yhz/8IQcccECOP/74bLHFFvnZz36WXXbZJfXqffm53quvvpoXX3yx8vW2r/uuN0IYO3ZsjeetWrX61iknll122fTq1es7vd682mOPPbLHHntkwoQJeeKJJ3LVVVfl2muvzfbbb5/nn38+1dXVee2117Lyyit/4w1D3nzzzXTo0GG2DxhWXXXVyvqv+j7nGgAwby655JKstNJKadCgQdq2bZuVV1650vMk89437rPPPrn++uvz0EMPZZNNNsl9992XcePGZe+9964xrlWrVjnnnHNyzjnn5M0338zw4cNz3nnn5Xe/+11atWqV008//Rvr/fp0YK1atUqSdOrUaY7Lv943fhefffZZjTlTP/744wwePDjXXXfdbL3HvPaljzzySAYNGpTHHnssn3/++Wz7aNWqVXr27Jmdd945gwcPzoUXXphNN900ffr0yZ577lmZCuvVV1/N+PHjZ5vTdZbv2ht9+OGHmTFjRuV58+bNKxdWzE3r1q0XeF/au3fv9O7dO59//nlGjRqV66+/Ppdffnm22267vPTSS2nTpk1ee+21dOjQ4Run9nrzzTdTr169dOvWrcbydu3aZYkllvjWvjT58r3/17/+Vet/EwBzJ7SFxUTLli3ToUOHPP/88/O13ddDtFlXY+61117p27fvHLdZc801v1uR86Bbt25p0KBB5QYM3+ahhx7KDjvskE022SSXXnpp2rdvn4YNG2bYsGG59tprZxtfF3dFnfUzmdVEzZgxI1tuuWU+/vjjHHfccVlllVXSrFmzvPvuu+nXr988TfQ/r8fdpEmTPPjggxkxYkTuuOOO3HXXXbn++uuz+eab55577kn9+vUzc+bMrLHGGrngggvm+Fpf/6NhXn39ZnjDhg1b6Dcu+yYtW7bMlltumS233DINGzbM1VdfnSeeeCI9e/ZcIK9XtnMNABZF6623XmWOz6+bn76xd+/eadu2bf785z9nk002yZ///Oe0a9fuG0O8zp07Z7/99stOO+2U5ZdfPtdcc823hrb169efr+XF1252Or/eeeedjB8/vka4t+uuu+bRRx/NMccck7XWWivNmzfPzJkzs/XWW89TX/raa69liy22yCqrrJILLrggnTp1SqNGjXLnnXfmwgsvrOyjqqoqN910Ux5//PH8/e9/z91335399tsv559/fh5//PHK67Zp0ybXXHPNHF9rboHit/nxj39cI7gcNGhQ5SbIZdC0adNsvPHG2XjjjdO6desMHjw4//jHP+baI87NvM6rPKe/iWbOnJktt9wyxx577By3WWmllearFuDbCW1hMbLddtvliiuuyGOPPZYNN9zwO+1jmWWWSYsWLTJjxoxv/WS5c+fOef7551MURY0G4eWXX/7W15lbQ9G0adNsvvnmuf/++/P2229/a2D417/+NdXV1bn77rtr3Kxq2LBh31rDLJ07d86rr7462/J5OY55MesmYrO+gvTcc8/llVdeydVXX13jK1b33nvvbNvO7X2an+OuV69etthii2yxxRa54IILcuaZZ+bEE0/MiBEj0qtXr6ywwgp59tlns8UWW3xrozc/N9j4+vH86Ec/mudtF7Z11103V199dd5///0kX95g7Yknnsi0adPSsGHDOW7TuXPn3HfffZk4cWKNq21feumlyvpvMj/nGgDw/c1P/1S/fv3sueeeueqqq3L22Wfn1ltvzYEHHjjXMPWrllxyyaywwgrzfTHFwvD1vvSTTz7J8OHDM3jw4Jx88smVcXPqjefWB/7973/PlClTctttt9W4cnhuUxlssMEG2WCDDXLGGWfk2muvzS9+8Ytcd911OeCAA7LCCivkvvvuy0YbbfStF1vMT196zTXX5Isvvqg8X3755ed524Vt1ocOX+1L77777nz88cdzvdq2c+fOmTlzZl599dXKt76SZNy4cfn000+/tS+d9TqfffaZvhQWInPawmLk2GOPTbNmzXLAAQdk3Lhxs61/7bXXcvHFF3/jPurXr5+dd945f/3rX+fYaH744YeV//7pT3+a9957LzfddFNl2eeffz7Xr3p/1ay7rn766aezrRs0aFCKosjee+9dmVrgq0aNGpWrr766Um9VVVWNrzu98cYbufXWW7+1hq8ex+OPP54nn3yysuzDDz+c6yf88+Paa6/NH/7wh2y44YbZYostKjUnNa+UKIpijj+bWXOmff19mtfj/vjjj2fb51prrZUkla8E7rrrrnn33Xdz5ZVXzjb2iy++yKRJk2rUM6ef2Zz06tWrxuPrV94ubJ9//nkee+yxOa6bNf/srCkxdt5553z00Uf53e9+N9vYWT+3n/70p5kxY8ZsYy688MJUVVVlm222+cZ65udcAwC+v/ntG/fee+988sknOfjgg/PZZ59lr732qrH+2WefzUcffTTbdm+++Wb+/e9/19pUW7Xl/vvvz2mnnZauXbtW7t0wp740SS666KLZtv+mvvTr+xg/fvxsYfgnn3wy2+vMqS+dMWNGTjvttNlef/r06TVee3760o022qhGX1qG0Hb48OFzXD5rftqv9qVFUWTw4MGzjf1qX5rM/nOb9U26bbfd9lvr2XXXXfPYY4/l7rvvnm3dp59+munTp3/rPoD540pbWIyssMIKufbaa7Pbbrtl1VVXzT777JPVV189U6dOzaOPPpobb7xxnr6e/tvf/jYjRozI+uuvnwMPPDCrrbZaPv744zz99NO57777KkHggQcemN/97nfZZ599MmrUqLRv3z5/+tOfKoHsN2nSpElWW221XH/99VlppZWy1FJLZfXVV8/qq6+en/zkJ7nkkkty6KGHZpVVVsnee++dFVdcMRMnTszIkSNz2223Vb5qtu222+aCCy7I1ltvnT333DMffPBBLrnkknTr1i3/+te/5ul9O/bYY/OnP/0pW2+9dY488sg0a9YsV1xxRTp37jzP+0iSm266Kc2bN8/UqVPz7rvv5u67784jjzyS7t2758Ybb6yMW2WVVbLCCivk17/+dd599920bNkyf/3rX+c4R1mPHj2SJEcccUR69+6d+vXrZ/fdd5/n4z711FPz4IMPZtttt03nzp3zwQcf5NJLL82yyy6b//f//l+SL/8gueGGG3LIIYdkxIgR2WijjTJjxoy89NJLueGGG3L33XdXPvHv0aNH7rvvvlxwwQXp0KFDunbtOsf5gBekWT/7F154IcmXV4w8/PDDSZKTTjpprtt9/vnn+clPfpINNtggW2+9dTp16pRPP/00t956ax566KH06dMna6+9dpIv57H74x//mAEDBuTJJ5/MxhtvnEmTJuW+++7LoYcemh133DHbb799Nttss5x44ol544030r1799xzzz3529/+lqOOOiorrLDCtx7LvJ5rAMD3N79949prr53VV1+9ctPWddZZp8b6e++9N4MGDcoOO+yQDTbYIM2bN8/rr7+eoUOHZsqUKXX69ft//OMfeemllzJ9+vSMGzcu999/f+6999507tw5t912W6qrq5N8OV3UJptsknPOOSfTpk1Lx44dc88992TMmDGz7XNWX3riiSdm9913T8OGDbP99ttnq622SqNGjbL99ttXAu4rr7wybdq0qVwtmiRXX311Lr300uy0005ZYYUVMnHixFx55ZVp2bJlJXTs2bNnDj744Jx11lkZPXp0ttpqqzRs2DCvvvpqbrzxxlx88cWVmyD36NEjl112WU4//fR069Ytbdq0qdxId2H53e9+l08//TTvvfdeki+vOn7nnXeSJIcffnhlLuI52XHHHdO1a9dsv/32WWGFFSq95t///vf8+Mc/zvbbb58k2WyzzbL33nvnf/7nf/Lqq69Wpq146KGHstlmm6V///7p3r17+vbtmyuuuCKffvppevbsmSeffDJXX311+vTpk8022+xbj+WYY47Jbbfdlu222y79+vVLjx49MmnSpDz33HO56aab8sYbb6R169a18K4BFQWw2HnllVeKAw88sOjSpUvRqFGjokWLFsVGG21U/O///m8xefLkyrgkxWGHHTbHfYwbN6447LDDik6dOhUNGzYs2rVrV2yxxRbFFVdcUWPcm2++Weywww5F06ZNi9atWxdHHnlkcddddxVJihEjRlTG9e3bt+jcuXONbR999NGiR48eRaNGjYokxaBBg2qsHzVqVLHnnnsWHTp0KBo2bFgsueSSxRZbbFFcffXVxYwZMyrjhgwZUqy44opF48aNi1VWWaUYNmxYMWjQoOLr/wR+0/H+61//Knr27FlUV1cXHTt2LE477bRiyJAhRZJizJgxc3mnvzTrtWY9qquri2WXXbbYbrvtiqFDh9Z4z2f597//XfTq1ato3rx50bp16+LAAw8snn322SJJMWzYsMq46dOnF4cffnixzDLLFFVVVTWOaV6Oe/jw4cWOO+5YdOjQoWjUqFHRoUOHYo899iheeeWVGvVMnTq1OPvss4sf/ehHRePGjYsll1yy6NGjRzF48OBi/PjxlXEvvfRSsckmmxRNmjQpkhR9+/b9xvdmfnzTz+fr4+b2+CbTpk0rrrzyyqJPnz5F586di8aNGxdNmzYt1l577eLcc88tpkyZUmP8559/Xpx44olF165dK+fALrvsUrz22muVMRMnTiyOPvroyu/oiiuuWJx77rnFzJkz5/nY5vVcAwDmbtiwYUWS4p///Oc3jpvXvnGWc845p0hSnHnmmbOte/3114uTTz652GCDDYo2bdoUDRo0KJZZZpli2223Le6///4aY+e1Nx0zZkyRpDj33HNrLB8xYkSRpLjxxhu/8fhmvQ+zHo0aNSratWtXbLnllsXFF19cTJgwYbZt3nnnnWKnnXYqllhiiaJVq1bFz3/+8+K9996bY39+2mmnFR07dizq1atXo0++7bbbijXXXLOorq4uunTpUpx99tnF0KFDa4x5+umniz322KNYbrnlisaNGxdt2rQptttuu+Kpp56araYrrrii6NGjR9GkSZOiRYsWxRprrFEce+yxxXvvvVcZM3bs2GLbbbctWrRoUSQpevbs+Y3vzfzo3Llzse22287TuLn1pd/2N8Rf/vKXYvfddy9WWGGFokmTJkV1dXWx2mqrFSeeeOJsP6fp06cX5557brHKKqsUjRo1KpZZZplim222KUaNGlUZM23atGLw4MGV3rVTp07FwIEDZ/tb5JuObeLEicXAgQOLbt26FY0aNSpat25d/OQnPynOO++8YurUqd/6fgDzp6oovudM5QAAALAYuvjii3P00UfnjTfeqDFfKwB8X0JbAAAAmE9FUaR79+5Zeuml53pTLQD4rsxpCwAAAPNo0qRJue222zJixIg899xz+dvf/lbXJQGwCHKlLQAAAMyjN954I127ds0SSyyRQw89NGeccUZdlwTAIqheXb74gw8+mO233z4dOnRIVVVVbr311m/dZuTIkVlnnXXSuHHjdOvWLVddddUCrxMAAOZGTwuLly5duqQoinzyyScCWwAWmDoNbSdNmpTu3bvnkksumafxY8aMybbbbpvNNtsso0ePzlFHHZUDDjggd9999wKuFAAA5kxPCwBAbSvN9AhVVVW55ZZb0qdPn7mOOe6443LHHXfk+eefryzbfffd8+mnn+auu+5aCFUCAMDc6WkBAKgNP6gbkT322GPp1atXjWW9e/fOUUcdNddtpkyZkilTplSez5w5Mx9//HGWXnrpVFVVLahSAQBYAIqiyMSJE9OhQ4fUq1enXxr7zvS0AACLr3ntZ39Qoe3YsWPTtm3bGsvatm2bCRMm5IsvvkiTJk1m2+ass87K4MGDF1aJAAAsBG+//XaWXXbZui7jO9HTAgDwbf3sDyq0/S4GDhyYAQMGVJ6PHz8+yy23XN5+++20bNmyDisDAGB+TZgwIZ06dUqLFi3qupSFSk8LALBomNd+9gcV2rZr1y7jxo2rsWzcuHFp2bLlHK9ISJLGjRuncePGsy1v2bKlBhcA4AfqhzwlgJ4WAIBv62d/UBOBbbjhhhk+fHiNZffee2823HDDOqoIAADmj54WAIBvU6eh7WeffZbRo0dn9OjRSZIxY8Zk9OjReeutt5J8+TWwffbZpzL+kEMOyeuvv55jjz02L730Ui699NLccMMNOfroo+uifAAA0NMCAFDr6jS0feqpp7L22mtn7bXXTpIMGDAga6+9dk4++eQkyfvvv19pdpOka9euueOOO3Lvvfeme/fuOf/88/OHP/whvXv3rpP6AQBATwsAQG2rKoqiqOsiFqYJEyakVatWGT9+/DfO/zVjxoxMmzZtIVZGGTVs2DD169ev6zIAgP/fvPZyizrvAwCUz8yZMzN16tS6LoM69m1Z0rz2cT+oG5EtDEVRZOzYsfn000/ruhRKYokllki7du1+0Dc8AQAAABacqVOnZsyYMZk5c2Zdl0IJ1EaWJLT9mlmBbZs2bdK0aVNB3WKsKIp8/vnn+eCDD5Ik7du3r+OKAAAAgLIpiiLvv/9+6tevn06dOqVevTqdjZQ6VJtZktD2K2bMmFEJbJdeeum6LocSaNKkSZLkgw8+SJs2bUyVAAAAANQwffr0fP755+nQoUOaNm1a1+VQx2orSxL9f8WsOWydYHzVrN8HcxwDAAAAXzdjxowkSaNGjeq4EsqiNrIkoe0cmBKBr/L7AAAAAHwb+QGz1MbvgtAWAAAAAKBEhLaUwlVXXZUllliirssAAAAA4AdgUc+ShLYLWL9+/VJVVZWqqqo0atQo3bp1y6mnnprp06fXdWmlsttuu+WVV16p6zIAAAAA6pQsad4s6llSg7ouYHGw9dZbZ9iwYZkyZUruvPPOHHbYYWnYsGEGDhxYY9zUqVMX2KTVC3LftaFJkyaVu+sBAAAALM5kSd9uUc+SXGm7EDRu3Djt2rVL586d88tf/jK9evXKbbfdln79+qVPnz4544wz0qFDh6y88spJkrfffju77rprllhiiSy11FLZcccd88Ybb1T2N2u7wYMHZ5lllknLli1zyCGHZOrUqZUxm266afr375+jjjoqrVu3Tu/evZMkDzzwQNZbb700btw47du3z/HHH1/jk5qZM2fmnHPOSbdu3dK4ceMst9xyOeOMMyrrv622kSNHZr311kuzZs2yxBJLZKONNsqbb76ZJHn22Wez2WabpUWLFmnZsmV69OiRp556Ksnsl7SfcsopWWuttfKnP/0pXbp0SatWrbL77rtn4sSJlTETJ07ML37xizRr1izt27fPhRdemE033TRHHXXU9/6ZAQAAANQVWZIsSWhbB5o0aVI5KYYPH56XX3459957b26//fZMmzYtvXv3TosWLfLQQw/lkUceSfPmzbP11lvXOJGGDx+eF198MSNHjsxf/vKX3HzzzRk8eHCN17n66qvTqFGjPPLII7n88svz7rvv5qc//Wl+/OMf59lnn81ll12WIUOG5PTTT69sM3DgwPz2t7/Nb37zm/z73//Otddem7Zt2ybJt9Y2ffr09OnTJz179sy//vWvPPbYYznooIMqd8z7xS9+kWWXXTb//Oc/M2rUqBx//PFp2LDhXN+n1157Lbfeemtuv/323H777XnggQfy29/+trJ+wIABeeSRR3Lbbbfl3nvvzUMPPZSnn376+/+AAAAAAEpElrQYZknFYmb8+PFFkmL8+PGzrfviiy+Kf//738UXX3xRa6/Xt2/fYscddyyKoihmzpxZ3HvvvUXjxo2LX//610Xfvn2Ltm3bFlOmTKmM/9Of/lSsvPLKxcyZMyvLpkyZUjRp0qS4++67K/tcaqmlikmTJlXGXHbZZUXz5s2LGTNmFEVRFD179izWXnvtGrWccMIJs+37kksuqWw3YcKEonHjxsWVV145x2P5ttr++9//FkmKkSNHznH7Fi1aFFddddUc1w0bNqxo1apV5fmgQYOKpk2bFhMmTKgsO+aYY4r111+/KIqimDBhQtGwYcPixhtvrKz/9NNPi6ZNmxZHHnnkHF/ju1oQvxcAwHfzTb3c4sT7AADlUdu5gSzp/yyKWdK89nGutF0Ibr/99jRv3jzV1dXZZpttsttuu+WUU05Jkqyxxho15gd59tln85///CctWrRI8+bN07x58yy11FKZPHlyXnvttcq47t27p2nTppXnG264YT777LO8/fbblWU9evSoUceLL76YDTfcsPJpRZJstNFG+eyzz/LOO+/kxRdfzJQpU7LFFlvM8Ti+rballloq/fr1S+/evbP99tvn4osvzvvvv1/ZfsCAATnggAPSq1ev/Pa3v61xPHPSpUuXtGjRovK8ffv2+eCDD5Ikr7/+eqZNm5b11luvsr5Vq1aVrwUAAAAA/FDJkr60OGdJbkS2EGy22Wa57LLL0qhRo3To0CENGvzf296sWbMaYz/77LP06NEj11xzzWz7WWaZZebrdb++72/zbZM3z0ttw4YNyxFHHJG77ror119/fU466aTce++92WCDDXLKKadkzz33zB133JF//OMfGTRoUK677rrstNNOc3y9r1/uXlVVlZkzZ87XMQEAAAD80MiSZEmutF0ImjVrlm7dumW55ZarcZLNyTrrrJNXX301bdq0Sbdu3Wo8WrVqVRn37LPP5osvvqg8f/zxx9O8efN06tRprvteddVV89hjj6UoisqyRx55JC1atMiyyy6bFVdcMU2aNMnw4cO/V21rr712Bg4cmEcffTSrr756rr322sq6lVZaKUcffXTuueee/OxnP8uwYcO+8f2Ym+WXXz4NGzbMP//5z8qy8ePH55VXXvlO+wMAAAAoC1mSLEloWzK/+MUv0rp16+y444556KGHMmbMmIwcOTJHHHFE3nnnncq4qVOnZv/998+///3v3HnnnRk0aFD69++fevXm/iM99NBD8/bbb+fwww/PSy+9lL/97W8ZNGhQBgwYkHr16qW6ujrHHXdcjj322Pzxj3/Ma6+9lscffzxDhgyZp9rGjBmTgQMH5rHHHsubb76Ze+65J6+++mpWXXXVfPHFF+nfv39GjhyZN998M4888kj++c9/ZtVVV/1O71OLFi3St2/fHHPMMRkxYkReeOGF7L///qlXr16NS/YBAAAAFmWypHnzQ8uSTI9QMk2bNs2DDz6Y4447Lj/72c8yceLEdOzYMVtssUVatmxZGbfFFltkxRVXzCabbJIpU6Zkjz32qMxtMjcdO3bMnXfemWOOOSbdu3fPUkstlf333z8nnXRSZcxvfvObNGjQICeffHLee++9tG/fPocccsg81fbFF1/kpZdeytVXX53//ve/ad++fQ477LAcfPDBmT59ev773/9mn332ybhx49K6dev87Gc/m+0uhfPjggsuyCGHHJLtttsuLVu2zLHHHpu333471dXV33mfAAAAAD8ksqR590PKkqqKr17fvBiYMGFCWrVqlfHjx9f4xU2SyZMnZ8yYMenatWspf1iz9OvXL59++mluvfXWui6lVCZNmpSOHTvm/PPPz/77719r+/2h/F4AwOLgm3q5xYn3AQDK44eQG8iS5qwusqR57eNcacsP1jPPPJOXXnop6623XsaPH59TTz01SbLjjjvWcWUAAAAAlM0PKUsS2vKDdt555+Xll19Oo0aN0qNHjzz00ENp3bp1XZcFAAAAQAn9ULIkoe0P0FVXXVXXJZTC2muvnVGjRtV1GQAAAAClJkv60g8pS5r77eEAAAAAAFjohLYAAAAAACUitAUAAAAAKBGhLQAAAABAiQhtAQAAAABKpEFdFwAAfH89jvljXZewyBh17j51XQIAALCYc6UtAAAAAECJuNJ2Hi3sK5jm9yqffv365eqrr06SNGjQIMsuu2x+/vOf59RTT011dfWCKBEAAACAuZAl8X0IbRchW2+9dYYNG5Zp06Zl1KhR6du3b6qqqnL22WfXdWkAAAAAlIwsqbxMj7AIady4cdq1a5dOnTqlT58+6dWrV+69994kyX//+9/sscce6dixY5o2bZo11lgjf/nLXyrb3n777VliiSUyY8aMJMno0aNTVVWV448/vjLmgAMOyF577bVwDwoAAACABUKWVF5C20XU888/n0cffTSNGjVKkkyePDk9evTIHXfckeeffz4HHXRQ9t577zz55JNJko033jgTJ07MM888kyR54IEH0rp164wcObKyzwceeCCbbrrpwj4UAAAAABYwWVK5CG0XIbfffnuaN2+e6urqrLHGGvnggw9yzDHHJEk6duyYX//611lrrbWy/PLL5/DDD8/WW2+dG264IUnSqlWrrLXWWpUTa+TIkTn66KPzzDPP5LPPPsu7776b//znP+nZs2ddHR4AAAAAtUiWVF5C20XIZpttltGjR+eJJ55I3759s++++2bnnXdOksyYMSOnnXZa1lhjjSy11FJp3rx57r777rz11luV7Xv27JmRI0emKIo89NBD+dnPfpZVV101Dz/8cB544IF06NAhK664Yl0dHgAAAAC1SJZUXkLbRUizZs3SrVu3dO/ePUOHDs0TTzyRIUOGJEnOPffcXHzxxTnuuOMyYsSIjB49Or17987UqVMr22+66aZ5+OGH8+yzz6Zhw4ZZZZVVsummm2bkyJF54IEHfDICAAAAsAiRJZVXg7ougAWjXr16OeGEEzJgwIDsueeeeeSRR7LjjjtWJn+eOXNmXnnllay22mqVbWbNRXLhhRdWTqpNN900v/3tb/PJJ5/kV7/6VZ0cCzB/3jp1jbouYZGy3MnP1XUJAAAAC5wsqVxcabsI+/nPf5769evnkksuyYorrph77703jz76aF588cUcfPDBGTduXI3xSy65ZNZcc81cc801lUmiN9lkkzz99NN55ZVXfDoCAAAAsAiTJZWH0HYR1qBBg/Tv3z/nnHNOfvWrX2WdddZJ7969s+mmm6Zdu3bp06fPbNv07NkzM2bMqJxoSy21VFZbbbW0a9cuK6+88sI9AAAAAAAWGllSeVQVRVHUdREL04QJE9KqVauMHz8+LVu2rLFu8uTJGTNmTLp27Zrq6uo6qpCy8XvBD43pEWrXD2V6hB7H/LGuS1hkjDp3n7ougW/wTb3c4sT7AADlITfg677pd2Je+zhX2gIAAAAAlIjQFgAAAACgRIS2AAAAAAAlIrQFAAAAACgRoS0AAAAAQIkIbQEAAAAASkRoCwAAAABQIkJbAAAAAIASEdoCAAAAAJRIg7ouAACgTN46dY26LmGRstzJz9V1CQAA8IMjtJ1HC/sPuPn9A6dfv365+uqrkyQNGzbMcsstl3322ScnnHBCGjRokKIocuWVV2bIkCF54YUX0qBBg3Tr1i177bVXDjrooDRt2rSyr3feeSfLL798VlpppTz//PPzXfuIESNy7rnn5oknnsgXX3yRLl26ZJtttsmAAQNy4oknVuqck86dO+eNN95IkvznP//JmWeemfvuuy/jxo1L69ats8oqq2S//fbLbrvtlgYNav76HnzwwfnDH/6Q6667Lj//+c9rrDvllFMyePDgHHzwwbn88ssry0ePHp211147Y8aMSZcuXeb7WAEAAADmRJY072RJszM9wiJk6623zvvvv59XX301v/rVr3LKKafk3HPPTZLsvffeOeqoo7LjjjtmxIgRGT16dH7zm9/kb3/7W+65554a+7nqqquy6667ZsKECXniiSfmq4bf//736dWrV9q1a5e//vWv+fe//53LL78848ePz/nnn5+LL74477//fuWRJMOGDas8/+c//5kkefLJJ7POOuvkxRdfzCWXXJLnn38+I0eOzAEHHJDLLrssL7zwQo3X/fzzz3Pdddfl2GOPzdChQ+dYW3V1dYYMGZJXX311vo4JAAAAYFEkSypvluRK20VI48aN065duyTJL3/5y9xyyy257bbbssIKK+Saa67Jrbfemh133LEyvkuXLtlhhx0yYcKEyrKiKDJs2LBceumlWXbZZTNkyJCsv/768/T677zzTo444ogcccQRufDCC2u8ziabbJJPP/00rVq1SqtWrWpst8QSS1TqnlVDv379stJKK+WRRx5JvXr/99nCiiuumD322CNFUdTYx4033pjVVlstxx9/fDp06JC33347nTp1qjFm5ZVXTps2bXLiiSfmhhtumKdjAgAAAFhUyZLKmyW50nYR1qRJk0ydOjXXXHNNVl555Ron2SxVVVU1fvFHjBiRzz//PL169cpee+2V6667LpMmTZqn17vxxhszderUHHvssXNcv8QSS8zTfkaPHp0XX3wxv/71r2ucZF+v+6uGDBmSvfbaK61atco222yTq666ao7b/fa3v81f//rXPPXUU/NUCwAAAMDiQpY0u7rKkoS2i6CiKHLffffl7rvvzuabb55XX301K6+88jxtO2TIkOy+++6pX79+Vl999Sy//PK58cYb52nbV199NS1btkz79u2/T/l55ZVXkqRGzR988EGaN29eeVx66aU1Xvfxxx/PbrvtliTZa6+9MmzYsNk+QUmSddZZJ7vuumuOO+6471UjAAAAwKJCllS+LElouwi5/fbb07x581RXV2ebbbbJbrvtllNOOWWOv3Bz8umnn+bmm2/OXnvtVVm21157ZciQIfO0fVEUs31qUVuWXnrpjB49OqNHj84SSyyRqVOnVtYNHTo0vXv3TuvWrZMkP/3pTzN+/Pjcf//9c9zX6aefnoceemi2+VcAAAAAFieypPJmSea0XYRsttlmueyyy9KoUaN06NChcke8lVZaKS+99NK3bn/ttddm8uTJNeYdKYoiM2fOzCuvvJKVVlrpG7dfaaWVMn78+Lz//vvf6xOSFVdcMUny8ssvZ+21106S1K9fP926dUuSGnf6mzFjRq6++uqMHTt2tuVDhw7NFltsMdv+V1hhhRx44IE5/vjj5/kfEQAAAIBFjSypvFmSK20XIc2aNUu3bt2y3HLL1fil23PPPfPKK6/kb3/722zbFEWR8ePHJ/nycvZf/epXlU8hRo8enWeffTYbb7zxXO+i91W77LJLGjVqlHPOOWeO6z/99NN5Oo611147q6yySs4777zMnDnzG8feeeedmThxYp555pkadf/lL3/JzTffPNfXPPnkk/PKK6/kuuuum6eaAAAAABY1sqTyZkmutF0M7Lrrrrnllluyxx575KSTTspWW22VZZZZJs8991wuvPDCHH744enSpUuefvrpXHPNNVlllVVqbL/HHnvk1FNPzemnn17jBP66Tp065cILL0z//v0zYcKE7LPPPunSpUveeeed/PGPf0zz5s1z/vnnf2u9VVVVGTZsWLbccststNFGGThwYFZdddVMmzYtDz74YD788MPUr18/yZf/OGy77bbp3r17jX2sttpqOfroo3PNNdfksMMOm+012rZtmwEDBuTcc8+dl7cQAAAAYLEhS6r7LElouxioqqrKtddemyuuuCJDhw7NGWeckQYNGmTFFVfMPvvsk969e+fYY4/NaqutNttJliQ77bRT+vfvnzvvvDM77LDDN77WoYcempVWWinnnXdedtppp3zxxRfp0qVLtttuuwwYMGCea157uRZ57M7rcvb/XplDDzko4z78b5o1bZI1Vls55ww6Jv123CRvPftA7rjjjlz9u7Mz5b0XZtvHDlv1zB8uvyQH7LRppk/8IMW0yTXGHf6LbXPZJb/L5MmTM2XcK5nSaM53Npw6fWamf/pB3rvkyDSY9P48H0NZLHfyc3VdAgAAAPAD8kPMkjbYYIOMGjUqZ555Zg477LCMHTs2zZo1S/fu3XPhhRdmv/32y7hx43LHHXfk2muvnW37evXqZaeddsqQIUPmGNomya9//etcdtllmTx58jzX9V1VFfM6s/AiYsKECWnVqlXGjx+fli1b1lg3efLkjBkzJl27dk11dXUdVUiSOYawdWXK9Jl5890P0uzRs4W2/CC8deoadV3CIuWHcg71OOaPdV3CIuOWFr6FUZtq+xz6pl5uceJ9AIDykCfxdd/0OzGvfZw5bQEAAAAASkRoyzw788wz07x58zk+ttlmm7ouDwAAAIASkSV9d+a0ZZ4dcsgh2XXXXee4rkmTJgu5GgAAAADKTJb03QltmWdLLbVUllpqqbouAwAAAIAfAFnSd2d6BAAAAACAEhHazsHMmTPrugRKZGaRJEWqihl1XQoAAABQUkVR1HUJlERtZIumR/iKRo0apV69ennvvfeyzDLLpFGjRqmqqqrrshZLU6fXfXBeFMm0mUX+O/7zFF+MT70v/lvXJQEAAAAl07Bhw1RVVeXDDz/MMsssI0tajBVFkalTp+bDDz9MvXr10qhRo++8L6HtV9SrVy9du3bN+++/n/fee6+uy1msTf/0g7ou4Uszp6feh/9Oi5dvdqUtAAAAMJv69etn2WWXzTvvvJM33nijrsuhBJo2bZrlllsu9ep990kOhLZf06hRoyy33HKZPn16ZswQ0tWV9y45sq5LSDIz9aZ9nqqpn6UqvuIAAAAAzFnz5s2z4oorZtq0aXVdCnWsfv36adCgwfe+4lpoOwdVVVVp2LBhGjZsWNelLLYaTHq/rksAAAAAmGf169dP/fr167oMFhFuRAYAAAAAUCJCWwAAAACAEhHaAgAAAACUiNAWAAAAAKBEhLYAAAAAACUitAUAAAAAKBGhLQAAAABAiQhtAQAAAABKRGgLAAAAAFAiQlsAAAAAgBIR2gIAAAAAlIjQFgAAAACgRIS2AAAAAAAlIrQFAAAAACgRoS0AAAAAQIkIbQEAAAAASkRoCwAAAABQIkJbAAAAAIASEdoCAAAAAJSI0BYAAAAAoETqPLS95JJL0qVLl1RXV2f99dfPk08++Y3jL7rooqy88spp0qRJOnXqlKOPPjqTJ09eSNUCAMDs9LQAANSmOg1tr7/++gwYMCCDBg3K008/ne7du6d379754IMP5jj+2muvzfHHH59BgwblxRdfzJAhQ3L99dfnhBNOWMiVAwDAl/S0AADUtjoNbS+44IIceOCB2XfffbPaaqvl8ssvT9OmTTN06NA5jn/00Uez0UYbZc8990yXLl2y1VZbZY899vjWKxkAAGBB0dMCAFDb6iy0nTp1akaNGpVevXr9XzH16qVXr1557LHH5rjNT37yk4waNarS0L7++uu5884789Of/nSurzNlypRMmDChxgMAAGqDnhYAgAWhQV298EcffZQZM2akbdu2NZa3bds2L7300hy32XPP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oESEtgAAAAAAJSK0BQAAAAAoEaEtAAAAAECJCG0BAAAAAEpEaAsAAAAAUCJCWwAAAACAEhHaAgAAAACUiNAWAAAAAKBEhLYAAAAAACUitAUAAAAAKBGhLQAAAABAiQhtAQAAAABKRGgLAAAAAFAiQlsAAAAAgBIR2gIAAAAAlIjQFgAAAACgREoR2l5yySXp0qVLqqurs/766+fJJ5+c69grr7wyG2+8cZZccsksueSS6dWr1zeOBwCABU0/CwBAbarz0Pb666/PgAEDMmjQoDz99NPp3r17evfunQ8++GCO40eOHJk99tgjI0aMyGOPPZZOnTplq622yrvvvruQKwcAAP0sAAC1r85D2wsuuCAHHnhg9t1336y22mq5/PLL07Rp0wwdOnSO46+55poceuihWWuttbLKKqvkD3/4Q2bOnJnhw4cv5MoBAEA/CwBA7avT0Hbq1KkZNWpUevXqVVlWr1699OrVK4899tg87ePzzz/PtGnTstRSSy2oMgEAYI70swAALAgN6vLFP/roo8yYMSNt27atsbxt27Z56aWX5mkfxx13XDp06FCjUf6qKVOmZMqUKZXnEyZM+O4FAwDAVyyMfjbR0wIALG7qfHqE7+O3v/1trrvuutxyyy2prq6e45izzjorrVq1qjw6deq0kKsEAIA5m5d+NtHTAgAsbuo0tG3dunXq16+fcePG1Vg+bty4tGvX7hu3Pe+88/Lb3/4299xzT9Zcc825jhs4cGDGjx9febz99tu1UjsAACyMfjbR0wIALG7qNLRt1KhRevToUeOmC7NuwrDhhhvOdbtzzjknp512Wu66666su+663/gajRs3TsuWLWs8AACgNiyMfjbR0wIALG7qdE7bJBkwYED69u2bddddN+utt14uuuiiTJo0Kfvuu2+SZJ999knHjh1z1llnJUnOPvvsnHzyybn22mvTpUuXjB07NknSvHnzNG/evM6OAwCAxZN+FgCA2lbnoe1uu+2WDz/8MCeffHLGjh2btdZaK3fddVflZg5vvfVW6tX7vwuCL7vsskydOjW77LJLjf0MGjQop5xyysIsHQAA9LMAANS6Og9tk6R///7p37//HNeNHDmyxvM33nhjwRcEAADzQT8LAEBtqtM5bQEAAAAAqEloCwAAAABQIkJbAAAAAIASEdoCAAAAAJSI0BYAAAAAoESEtgAA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Jk2aZNVVV83FF1882/ZTp07NOeeck+7du6dp06Zp3bp1NtpoowwbNizTpk1bWIcBAD9Yelr4/vS0APNOaMtCsfXWW+f999/Pq6++ml/96lc55ZRTcu6551bWv/zyy3nvvfdy44035oUXXsj2229faV5vueWW9OzZM8suu2xGjBiRl156KUceeWROP/307L777nEvPRYHQ4YMyeGHH54HH3ww7733XmX5qFGj0qZNm/z5z3/OCy+8kBNPPDEDBw7M7373u8qYqVOnpnfv3vntb3+bgw46KI8++miefPLJHHbYYfnf//3fvPDCC3VxSLDAfTVgadiwYdq2bZstt9wyQ4cOzcyZM2uMndcgpaqqKtXV1XnzzTdrLO/Tp0/69eu3oA8JqGN6Wvh+9LQwf/Szi7kCFrC+ffsWO+64Y41lW265ZbHBBhsUI0aMKJIUn3zySWXdNddcUyQpXnrppeKzzz4rll566eJnP/vZbPu97bbbiiTFddddt4CPAOrWxIkTi+bNmxcvvfRSsdtuuxVnnHHGN44/9NBDi80226zy/Oyzzy7q1atXPP3007ONnTp1avHZZ5/Ves1QBn379i223nrr4v333y/eeeedYtSoUcUZZ5xRNG/evNhmm22KadOmFUVRFDfffHPRoEGD4sADDyyeeeaZYsyYMcWVV15ZLLnkksUuu+xSzJw5s7LPJEV1dXWxzz771HitHXfcsejbt+/CPDxgIdPTwvejp4X5p59dvLnSljrRpEmTTJ06da7rki8/Sb3nnnvy3//+N7/+9a9nG7f99ttnpZVWyl/+8pcFWivUtRtuuCGrrLJKVl555ey1114ZOnToN16NM378+Cy11FKV59dcc0169eqVtddee7axDRs2TLNmzRZI3VAGjRs3Trt27dKxY8ess846OeGEE/K3v/0t//jHP3LVVVdl0qRJOfDAA7PDDjvkiiuuyFprrZUuXbrkgAMOyNVXX52bbropN9xwQ4199u/fP3/+85/z/PPP19FRAWWhp4V5p6eF70Y/u/gS2rJQFUWR++67L3fffXc233zz2da///77Oe+889KxY8esvPLKeeWVV5Ikq6666hz3t8oqq1TGwKJqyJAh2WuvvZJ8+bXM8ePH54EHHpjj2EcffTTXX399DjrooMqyV199NaussspCqRV+CDbffPN07949N99883cKUjbaaKNst912Of744xdWyUDJ6Glh/ulpofboZxcPQlsWittvvz3NmzdPdXV1ttlmm+y222455ZRTKuuXXXbZNGvWLB06dMikSZPy17/+NY0aNaqs/6ZPYGFR9vLLL+fJJ5/MHnvskSRp0KBBdttttwwZMmS2sc8//3x23HHHDBo0KFtttVVlufMHZrfKKqvkjTfe+M5ByllnnZW77rorDz300AKtEygXPS18N3paqH362UVfg7ougMXDZpttlssuuyyNGjVKhw4d0qBBzV+9hx56KC1btkybNm3SokWLyvKVVlopSfLiiy/mJz/5yWz7ffHFF7Paaqst2OKhDg0ZMiTTp09Phw4dKsuKokjjxo3zu9/9Lq1atUqS/Pvf/84WW2yRgw46KCeddFKNfay00kp56aWXFmrdUHZFUaSqqqrG87n5auAyy2qrrZZ99tknxx9/fB555JEFUiNQPnpa+G70tFD79LOLPlfaslA0a9Ys3bp1y3LLLTdbc5skXbt2zQorrFCjuU2SrbbaKksttVTOP//82ba57bbb8uqrr1Y+rYVFzfTp0/PHP/4x559/fkaPHl15PPvss+nQoUPlKy4vvPBCNttss/Tt2zdnnHHGbPvZc889c9999+WZZ56Zbd20adMyadKkBX4sUDYvvvhiunbtmhVXXLHyfG7jZoUtXzd48OA8/fTTufXWWxdUmUDJ6Glh/ulpYcHQzy76hLaUWrNmzfL73/8+f/vb33LQQQflX//6V954440MGTIk/fr1yy677JJdd921rsuEBeL222/PJ598kv333z+rr756jcfOO++cIUOG5Pnnn89mm22WrbbaKgMGDMjYsWMzduzYfPjhh5X9HHXUUdloo42yxRZb5JJLLsmzzz6b119/PTfccEM22GCDvPrqq3V4lLDw3X///Xnuueey8847p3fv3t8apPTr12+O++nUqVP69++fE044ITNmzFjAVQM/ZHpaFmd6Wqh9+tnFRAELWN++fYsdd9xxjutGjBhRJCk++eSTb9zHgw8+WPTu3bto2bJl0ahRo+JHP/pRcd555xXTp0+v/YKhJLbbbrvipz/96RzXPfHEE0WSYqeddiqSzPbo3LlzjfGTJ08uzjrrrGKNNdYoqquri6WWWqrYaKONiquuuqqYNm3aQjgaWPj69u1bbL311sX7779fvPPOO8WoUaOKM844o2jevHmx3XbbVf4fcuONNxb169cvDjzwwOLZZ58txowZU/zhD38ollxyyeLAAw+ssc8kxS233FJ5/t///rdo1apVUV1dXfTt23chHh2wsOlp4bvR08J3p59dvFUVhdm8AYBFT79+/XL11Vcn+fKGJ0suuWS6d++ePffcM3379k29ev/3haOHHnooZ5xxRh577LFMmDAhSXL22Wfn2GOPrbHPqqqq3HLLLenTp09l2VlnnZUTTjghffv2zVVXXbXAjwsAgMWDfnbxJrQFAPiKyZMnZ8cdd8zbb7+dBx54IMsss0xdlwQAAPNMP7toENoCAHzN5MmTc9FFF2XFFVfMzjvvXNflAADAfNHP/vAJbQEAAAAASqTetw8BAAAAAGBhEdoCAAAAAJSI0BYAAAAAoESEtgAAAAAAJSK0BQAAAAAoEaEtAAAAAECJCG0BSuSxxx5L/fr1s+2229bJ67/xxhupqqrK6NGj6+T1AQD4YdPPAtQOoS1AiQwZMiSHH354Hnzwwbz33nt1XQ4AAMwX/SxA7RDaApTEZ599luuvvz6//OUvs+222+aqq66qsf62227LiiuumOrq6my22Wa5+uqrU1VVlU8//bQy5uGHH87GG2+cJk2apFOnTjniiCMyadKkyvouXbrkzDPPzH777ZcWLVpkueWWyxVXXFFZ37Vr1yTJ2muvnaqqqmy66aYL8pABAFiE6GcBao/QFqAkbrjhhqyyyipZeeWVs9dee2Xo0KEpiiJJMmbMmOyyyy7p06dPnn322Rx88ME58cQTa2z/2muvZeutt87OO++cf/3rX7n++uvz8MMPp3///jXGnX/++Vl33XXzzDPP5NBDD80vf/nLvPzyy0mSJ598Mkly33335f3338/NN9+8EI4cAIBFgX4WoPZUFbP+BQWgTm200UbZddddc+SRR2b69Olp3759brzxxmy66aY5/vjjc8cdd+S5556rjD/ppJNyxhln5JNPPskSSyyRAw44IPXr18/vf//7ypiHH344PXv2zKRJk1JdXZ0uXbpk4403zp/+9KckSVEUadeuXQYPHpxDDjkkb7zxRrp27Zpnnnkma6211sJ+CwAA+AHTzwLUHlfaApTAyy+/nCeffDJ77LFHkqRBgwbZbbfdMmTIkMr6H//4xzW2WW+99Wo8f/bZZ3PVVVelefPmlUfv3r0zc+bMjBkzpjJuzTXXrPx3VVVV2rVrlw8++GBBHRoAAIsB/SxA7WpQ1wUA8OUNG6ZPn54OHTpUlhVFkcaNG+d3v/vdPO3js88+y8EHH5wjjjhitnXLLbdc5b8bNmxYY11VVVVmzpz5HSsHAAD9LEBtE9oC1LHp06fnj3/8Y84///xstdVWNdb16dMnf/nLX7LyyivnzjvvrLHun//8Z43n66yzTv7973+nW7du37mWRo0aJUlmzJjxnfcBAMDiRT8LUPuEtgB17Pbbb88nn3yS/fffP61ataqxbuedd86QIUNyww035IILLshxxx2X/fffP6NHj67cjbeqqipJctxxx2WDDTZI//79c8ABB6RZs2b597//nXvvvXeer25o06ZNmjRpkrvuuivLLrtsqqurZ6sJAAC+Sj8LUPvMaQtQx4YMGZJevXrNsZnceeed89RTT2XixIm56aabcvPNN2fNNdfMZZddVrnbbuPGjZN8ObfXAw88kFdeeSUbb7xx1l577Zx88sk1vqL2bRo0aJD/+Z//ye9///t06NAhO+64Y+0cJAAAiyz9LEDtqyqKoqjrIgCYf2eccUYuv/zyvP3223VdCgAAzDf9LMDcmR4B4Afi0ksvzY9//OMsvfTSeeSRR3Luueemf//+dV0WAADME/0swLwT2gL8QLz66qs5/fTT8/HHH2e55ZbLr371qwwcOLCuywIAgHminwWYd6ZHAAAAAAAoETciAwAAAAAoEaEtAAAAAECJCG0BAAAAAEpEaAsAAAAAUCJCWwAAAACAEhHaAgAAAACUiNAWAAAAAKBEhLYAAAAAACUitAUAAAAAKJH/D8LSjbwJJBTwAAAAAElFTkSuQmCC\n"},"metadata":{}}],"execution_count":14}]}
|
false/ablation_study/ablation.ipynb
ADDED
|
@@ -0,0 +1,514 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"cells": [
|
| 3 |
+
{
|
| 4 |
+
"cell_type": "markdown",
|
| 5 |
+
"metadata": {},
|
| 6 |
+
"source": [
|
| 7 |
+
"# Ablation Study: RL Agents for Fraud Detection\n",
|
| 8 |
+
"\n",
|
| 9 |
+
"This notebook performs an ablation study to compare the performance of different Reinforcement Learning (RL) agents (PPO, A2C, DQN) on two fraud detection datasets (CreditCard and PaySim). \n",
|
| 10 |
+
"We also evaluate the impact of different preprocessing techniques: Raw Data, PCA, and CTGAN Data Augmentation."
|
| 11 |
+
]
|
| 12 |
+
},
|
| 13 |
+
{
|
| 14 |
+
"cell_type": "code",
|
| 15 |
+
"execution_count": 1,
|
| 16 |
+
"metadata": {},
|
| 17 |
+
"outputs": [
|
| 18 |
+
{
|
| 19 |
+
"data": {
|
| 20 |
+
"text/plain": [
|
| 21 |
+
"<torch._C.Generator at 0x7fcfdd72ae10>"
|
| 22 |
+
]
|
| 23 |
+
},
|
| 24 |
+
"execution_count": 1,
|
| 25 |
+
"metadata": {},
|
| 26 |
+
"output_type": "execute_result"
|
| 27 |
+
}
|
| 28 |
+
],
|
| 29 |
+
"source": [
|
| 30 |
+
"import gymnasium as gym\n",
|
| 31 |
+
"from gymnasium import spaces\n",
|
| 32 |
+
"import numpy as np\n",
|
| 33 |
+
"import pandas as pd\n",
|
| 34 |
+
"import torch\n",
|
| 35 |
+
"import os\n",
|
| 36 |
+
"import matplotlib.pyplot as plt\n",
|
| 37 |
+
"import seaborn as sns\n",
|
| 38 |
+
"from sklearn.model_selection import train_test_split\n",
|
| 39 |
+
"from sklearn.preprocessing import StandardScaler, OneHotEncoder\n",
|
| 40 |
+
"from sklearn.decomposition import PCA\n",
|
| 41 |
+
"from sklearn.metrics import confusion_matrix, accuracy_score, precision_score, recall_score, f1_score\n",
|
| 42 |
+
"from sklearn.compose import ColumnTransformer\n",
|
| 43 |
+
"\n",
|
| 44 |
+
"from stable_baselines3 import PPO, A2C, DQN\n",
|
| 45 |
+
"from stable_baselines3.common.vec_env import DummyVecEnv\n",
|
| 46 |
+
"from ctgan import CTGAN\n",
|
| 47 |
+
"\n",
|
| 48 |
+
"# Set random seed for reproducibility\n",
|
| 49 |
+
"SEED = 42\n",
|
| 50 |
+
"np.random.seed(SEED)\n",
|
| 51 |
+
"torch.manual_seed(SEED)\n",
|
| 52 |
+
"\n",
|
| 53 |
+
"# Define a learning rate schedule\n",
|
| 54 |
+
"def linear_schedule(initial_value):\n",
|
| 55 |
+
" def schedule(progress_remaining):\n",
|
| 56 |
+
" return progress_remaining * initial_value\n",
|
| 57 |
+
" return schedule\n"
|
| 58 |
+
]
|
| 59 |
+
},
|
| 60 |
+
{
|
| 61 |
+
"cell_type": "markdown",
|
| 62 |
+
"metadata": {},
|
| 63 |
+
"source": [
|
| 64 |
+
"## 1. Custom RL Environment"
|
| 65 |
+
]
|
| 66 |
+
},
|
| 67 |
+
{
|
| 68 |
+
"cell_type": "code",
|
| 69 |
+
"execution_count": 2,
|
| 70 |
+
"metadata": {},
|
| 71 |
+
"outputs": [],
|
| 72 |
+
"source": [
|
| 73 |
+
"class FraudDetectionEnv(gym.Env):\n",
|
| 74 |
+
" \"\"\"\n",
|
| 75 |
+
" A custom Gym environment for Fraud Detection.\n",
|
| 76 |
+
" State: Feature vector of a transaction.\n",
|
| 77 |
+
" Action: 0 (Declare Not Fraud), 1 (Declare Fraud).\n",
|
| 78 |
+
" Reward: Based on correctly/incorrectly classifying fraud vs non-fraud.\n",
|
| 79 |
+
" \"\"\"\n",
|
| 80 |
+
" def __init__(self, features: np.ndarray, labels: np.ndarray, reward_config: dict = None):\n",
|
| 81 |
+
" super().__init__()\n",
|
| 82 |
+
" \n",
|
| 83 |
+
" if reward_config is None:\n",
|
| 84 |
+
" # Default reward configuration favoring recall of fraud\n",
|
| 85 |
+
" self.reward_config = {\n",
|
| 86 |
+
" 'TP': 10.0,\n",
|
| 87 |
+
" 'FP': -5.0,\n",
|
| 88 |
+
" 'FN': -20.0,\n",
|
| 89 |
+
" 'TN': 1.0\n",
|
| 90 |
+
"}\n",
|
| 91 |
+
"\n",
|
| 92 |
+
" else:\n",
|
| 93 |
+
" self.reward_config = reward_config\n",
|
| 94 |
+
"\n",
|
| 95 |
+
" self.features = features.astype(np.float32)\n",
|
| 96 |
+
" self.labels = labels.astype(np.int64)\n",
|
| 97 |
+
"\n",
|
| 98 |
+
" self.num_instances = self.features.shape[0]\n",
|
| 99 |
+
" self.feature_dim = self.features.shape[1]\n",
|
| 100 |
+
"\n",
|
| 101 |
+
" # Action Space: Discrete(2) -> 0 for Not Fraud, 1 for Fraud\n",
|
| 102 |
+
" self.action_space = spaces.Discrete(2)\n",
|
| 103 |
+
"\n",
|
| 104 |
+
" # Observation Space: Box(low, high, shape, dtype)\n",
|
| 105 |
+
" self.observation_space = spaces.Box(\n",
|
| 106 |
+
" low=-np.inf, high=np.inf, \n",
|
| 107 |
+
" shape=(self.feature_dim,), \n",
|
| 108 |
+
" dtype=np.float32\n",
|
| 109 |
+
" )\n",
|
| 110 |
+
"\n",
|
| 111 |
+
" self._current_index = 0\n",
|
| 112 |
+
" self._order = np.arange(self.num_instances)\n",
|
| 113 |
+
" np.random.shuffle(self._order)\n",
|
| 114 |
+
"\n",
|
| 115 |
+
" def step(self, action: int):\n",
|
| 116 |
+
" if self._current_index >= self.num_instances:\n",
|
| 117 |
+
" return self.observation_space.sample() * 0, 0, True, False, {}\n",
|
| 118 |
+
"\n",
|
| 119 |
+
" actual_index = self._order[self._current_index]\n",
|
| 120 |
+
" true_label = self.labels[actual_index]\n",
|
| 121 |
+
"\n",
|
| 122 |
+
" # Calculate Reward\n",
|
| 123 |
+
" reward = 0\n",
|
| 124 |
+
" if action == 1 and true_label == 1:\n",
|
| 125 |
+
" reward = self.reward_config['TP']\n",
|
| 126 |
+
" elif action == 1 and true_label == 0:\n",
|
| 127 |
+
" reward = self.reward_config['FP']\n",
|
| 128 |
+
" elif action == 0 and true_label == 1:\n",
|
| 129 |
+
" reward = self.reward_config['FN']\n",
|
| 130 |
+
" elif action == 0 and true_label == 0:\n",
|
| 131 |
+
" reward = self.reward_config['TN']\n",
|
| 132 |
+
"\n",
|
| 133 |
+
" self._current_index += 1\n",
|
| 134 |
+
" done = self._current_index >= self.num_instances\n",
|
| 135 |
+
" truncated = False\n",
|
| 136 |
+
"\n",
|
| 137 |
+
" next_observation = np.zeros(self.feature_dim, dtype=np.float32)\n",
|
| 138 |
+
" if not done:\n",
|
| 139 |
+
" next_observation = self.features[self._order[self._current_index]]\n",
|
| 140 |
+
"\n",
|
| 141 |
+
" info = {\n",
|
| 142 |
+
" 'true_label': true_label,\n",
|
| 143 |
+
" 'predicted_action': action,\n",
|
| 144 |
+
" 'is_done': done\n",
|
| 145 |
+
" }\n",
|
| 146 |
+
"\n",
|
| 147 |
+
" return next_observation, reward, done, truncated, info\n",
|
| 148 |
+
"\n",
|
| 149 |
+
" def reset(self, seed=None, options=None):\n",
|
| 150 |
+
" super().reset(seed=seed)\n",
|
| 151 |
+
" self._current_index = 0\n",
|
| 152 |
+
" self._order = np.arange(self.num_instances)\n",
|
| 153 |
+
" np.random.shuffle(self._order)\n",
|
| 154 |
+
" \n",
|
| 155 |
+
" initial_observation = self.features[self._order[self._current_index]]\n",
|
| 156 |
+
" return initial_observation, {}"
|
| 157 |
+
]
|
| 158 |
+
},
|
| 159 |
+
{
|
| 160 |
+
"cell_type": "markdown",
|
| 161 |
+
"metadata": {},
|
| 162 |
+
"source": [
|
| 163 |
+
"## 2. Data Loading and Preprocessing"
|
| 164 |
+
]
|
| 165 |
+
},
|
| 166 |
+
{
|
| 167 |
+
"cell_type": "code",
|
| 168 |
+
"execution_count": 3,
|
| 169 |
+
"metadata": {},
|
| 170 |
+
"outputs": [],
|
| 171 |
+
"source": [
|
| 172 |
+
"def load_creditcard_data(path=\"creditcard.csv\"):\n",
|
| 173 |
+
" print(\"Loading CreditCard dataset...\")\n",
|
| 174 |
+
" df = pd.read_csv(path)\n",
|
| 175 |
+
" \n",
|
| 176 |
+
" # 1. Balance Data (1:5 Fraud to Non-Fraud)\n",
|
| 177 |
+
" fraud = df[df['Class'] == 1]\n",
|
| 178 |
+
" non_fraud = df[df['Class'] == 0]\n",
|
| 179 |
+
" \n",
|
| 180 |
+
" # Undersample non-fraud\n",
|
| 181 |
+
" n_fraud = len(fraud)\n",
|
| 182 |
+
" n_non_fraud = n_fraud * 5\n",
|
| 183 |
+
" \n",
|
| 184 |
+
" if len(non_fraud) > n_non_fraud:\n",
|
| 185 |
+
" non_fraud = non_fraud.sample(n=n_non_fraud, random_state=SEED)\n",
|
| 186 |
+
" \n",
|
| 187 |
+
" balanced_df = pd.concat([fraud, non_fraud]).sample(frac=1, random_state=SEED).reset_index(drop=True)\n",
|
| 188 |
+
" print(f\"Balanced CreditCard Data: {len(fraud)} Fraud, {len(non_fraud)} Non-Fraud\")\n",
|
| 189 |
+
"\n",
|
| 190 |
+
" # 2. Split\n",
|
| 191 |
+
" X = balanced_df.drop('Class', axis=1).values\n",
|
| 192 |
+
" y = balanced_df['Class'].values\n",
|
| 193 |
+
" \n",
|
| 194 |
+
" X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=SEED, stratify=y)\n",
|
| 195 |
+
" \n",
|
| 196 |
+
" # 3. Scale (Fit on Train, Transform Test)\n",
|
| 197 |
+
" # Scale Time (0) and Amount (29)\n",
|
| 198 |
+
" def scale_columns(X_tr, X_te, indices):\n",
|
| 199 |
+
" for i in indices:\n",
|
| 200 |
+
" scaler_i = StandardScaler()\n",
|
| 201 |
+
" X_tr[:, i] = scaler_i.fit_transform(X_tr[:, i].reshape(-1, 1)).flatten()\n",
|
| 202 |
+
" X_te[:, i] = scaler_i.transform(X_te[:, i].reshape(-1, 1)).flatten()\n",
|
| 203 |
+
" return X_tr, X_te\n",
|
| 204 |
+
"\n",
|
| 205 |
+
" if X_train.shape[1] == 30:\n",
|
| 206 |
+
" X_train, X_test = scale_columns(X_train, X_test, [0, 29])\n",
|
| 207 |
+
" \n",
|
| 208 |
+
" return X_train, X_test, y_train, y_test\n",
|
| 209 |
+
"\n",
|
| 210 |
+
"def load_paysim_data(path=\"paysim.csv\"):\n",
|
| 211 |
+
" print(\"Loading PaySim dataset...\")\n",
|
| 212 |
+
" df = pd.read_csv(path)\n",
|
| 213 |
+
" \n",
|
| 214 |
+
" # Drop unnecessary columns\n",
|
| 215 |
+
" df = df.drop(['nameOrig', 'nameDest', 'isFlaggedFraud'], axis=1)\n",
|
| 216 |
+
" \n",
|
| 217 |
+
" # Rename 'isFraud' to 'Class' for consistency\n",
|
| 218 |
+
" df = df.rename(columns={'isFraud': 'Class'})\n",
|
| 219 |
+
" \n",
|
| 220 |
+
" # One-hot encode 'type'\n",
|
| 221 |
+
" df = pd.get_dummies(df, columns=['type'], drop_first=True)\n",
|
| 222 |
+
" \n",
|
| 223 |
+
" # 1. Balance Data (1:5 Fraud to Non-Fraud)\n",
|
| 224 |
+
" fraud = df[df['Class'] == 1]\n",
|
| 225 |
+
" non_fraud = df[df['Class'] == 0]\n",
|
| 226 |
+
" \n",
|
| 227 |
+
" # Undersample non-fraud\n",
|
| 228 |
+
" n_fraud = len(fraud)\n",
|
| 229 |
+
" n_non_fraud = n_fraud * 5\n",
|
| 230 |
+
" \n",
|
| 231 |
+
" if len(non_fraud) > n_non_fraud:\n",
|
| 232 |
+
" non_fraud = non_fraud.sample(n=n_non_fraud, random_state=SEED)\n",
|
| 233 |
+
" \n",
|
| 234 |
+
" balanced_df = pd.concat([fraud, non_fraud]).sample(frac=1, random_state=SEED).reset_index(drop=True)\n",
|
| 235 |
+
" print(f\"Balanced PaySim Data: {len(fraud)} Fraud, {len(non_fraud)} Non-Fraud\")\n",
|
| 236 |
+
" \n",
|
| 237 |
+
" # 2. Split\n",
|
| 238 |
+
" X = balanced_df.drop('Class', axis=1).values.astype(np.float32)\n",
|
| 239 |
+
" y = balanced_df['Class'].values.astype(np.int64)\n",
|
| 240 |
+
" \n",
|
| 241 |
+
" X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=SEED, stratify=y)\n",
|
| 242 |
+
" \n",
|
| 243 |
+
" # 3. Scale\n",
|
| 244 |
+
" scaler = StandardScaler()\n",
|
| 245 |
+
" X_train = scaler.fit_transform(X_train)\n",
|
| 246 |
+
" X_test = scaler.transform(X_test)\n",
|
| 247 |
+
" \n",
|
| 248 |
+
" return X_train, X_test, y_train, y_test\n"
|
| 249 |
+
]
|
| 250 |
+
},
|
| 251 |
+
{
|
| 252 |
+
"cell_type": "markdown",
|
| 253 |
+
"metadata": {},
|
| 254 |
+
"source": [
|
| 255 |
+
"## 3. Preprocessing Techniques (PCA & CTGAN)"
|
| 256 |
+
]
|
| 257 |
+
},
|
| 258 |
+
{
|
| 259 |
+
"cell_type": "code",
|
| 260 |
+
"execution_count": 4,
|
| 261 |
+
"metadata": {},
|
| 262 |
+
"outputs": [],
|
| 263 |
+
"source": [
|
| 264 |
+
"def apply_pca_ctgan(X_train, y_train, X_test, n_components=0.99, epochs=200):\n",
|
| 265 |
+
" print(f\"Applying PCA (n_components={n_components}) + CTGAN (epochs={epochs})...\")\n",
|
| 266 |
+
" \n",
|
| 267 |
+
" # 1. Apply PCA\n",
|
| 268 |
+
" pca = PCA(n_components=n_components)\n",
|
| 269 |
+
" X_train_pca = pca.fit_transform(X_train)\n",
|
| 270 |
+
" X_test_pca = pca.transform(X_test)\n",
|
| 271 |
+
" print(f\"PCA reduced dimensions from {X_train.shape[1]} to {X_train_pca.shape[1]}\")\n",
|
| 272 |
+
" \n",
|
| 273 |
+
" # 2. Apply CTGAN on PCA-transformed data\n",
|
| 274 |
+
" # Combine X and y for CTGAN\n",
|
| 275 |
+
" df_train = pd.DataFrame(X_train_pca, columns=[f'f{i}' for i in range(X_train_pca.shape[1])])\n",
|
| 276 |
+
" df_train['label'] = y_train\n",
|
| 277 |
+
" \n",
|
| 278 |
+
" # Filter fraud samples\n",
|
| 279 |
+
" fraud_df = df_train[df_train['label'] == 1].drop('label', axis=1)\n",
|
| 280 |
+
" \n",
|
| 281 |
+
" if len(fraud_df) == 0:\n",
|
| 282 |
+
" print(\"No fraud samples found for CTGAN!\")\n",
|
| 283 |
+
" return X_train_pca, y_train, X_test_pca\n",
|
| 284 |
+
" \n",
|
| 285 |
+
" # Train CTGAN\n",
|
| 286 |
+
" ctgan = CTGAN(epochs=epochs, batch_size=64, pac=1, verbose=True)\n",
|
| 287 |
+
" ctgan.fit(fraud_df)\n",
|
| 288 |
+
" \n",
|
| 289 |
+
" # Generate synthetic samples to balance the dataset (or double the fraud count)\n",
|
| 290 |
+
" n_synthetic = len(fraud_df) \n",
|
| 291 |
+
" synthetic_fraud = ctgan.sample(n_synthetic)\n",
|
| 292 |
+
" \n",
|
| 293 |
+
" X_synthetic = synthetic_fraud.values\n",
|
| 294 |
+
" y_synthetic = np.ones(n_synthetic)\n",
|
| 295 |
+
" \n",
|
| 296 |
+
" X_aug = np.vstack([X_train_pca, X_synthetic])\n",
|
| 297 |
+
" y_aug = np.concatenate([y_train, y_synthetic])\n",
|
| 298 |
+
" \n",
|
| 299 |
+
" print(f\"Augmented training set from {len(X_train_pca)} to {len(X_aug)} samples.\")\n",
|
| 300 |
+
" return X_aug, y_aug, X_test_pca\n"
|
| 301 |
+
]
|
| 302 |
+
},
|
| 303 |
+
{
|
| 304 |
+
"cell_type": "markdown",
|
| 305 |
+
"metadata": {},
|
| 306 |
+
"source": [
|
| 307 |
+
"## 4. Experiment Loop"
|
| 308 |
+
]
|
| 309 |
+
},
|
| 310 |
+
{
|
| 311 |
+
"cell_type": "code",
|
| 312 |
+
"execution_count": 6,
|
| 313 |
+
"metadata": {},
|
| 314 |
+
"outputs": [],
|
| 315 |
+
"source": [
|
| 316 |
+
"def train_and_evaluate(agent_name, X_train, y_train, X_test, y_test, total_timesteps=10000):\n",
|
| 317 |
+
" # Create Environment\n",
|
| 318 |
+
" env = DummyVecEnv([lambda: FraudDetectionEnv(X_train, y_train)])\n",
|
| 319 |
+
" \n",
|
| 320 |
+
" # Initialize Agent\n",
|
| 321 |
+
" if agent_name == 'PPO':\n",
|
| 322 |
+
" # Default PPO parameters as none were specified in reference\n",
|
| 323 |
+
" model = PPO('MlpPolicy', env, verbose=0)\n",
|
| 324 |
+
" elif agent_name == 'A2C':\n",
|
| 325 |
+
" model = A2C(\n",
|
| 326 |
+
" \"MlpPolicy\",\n",
|
| 327 |
+
" env,\n",
|
| 328 |
+
" learning_rate=1e-4,\n",
|
| 329 |
+
" gamma=0.99,\n",
|
| 330 |
+
" n_steps=5,\n",
|
| 331 |
+
" ent_coef=0.01,\n",
|
| 332 |
+
" vf_coef=0.5,\n",
|
| 333 |
+
" max_grad_norm=0.5,\n",
|
| 334 |
+
" verbose=0,\n",
|
| 335 |
+
" device=\"auto\"\n",
|
| 336 |
+
" )\n",
|
| 337 |
+
" elif agent_name == 'DQN':\n",
|
| 338 |
+
" model = DQN(\n",
|
| 339 |
+
" \"MlpPolicy\",\n",
|
| 340 |
+
" env,\n",
|
| 341 |
+
" learning_rate=linear_schedule(1e-4),\n",
|
| 342 |
+
" buffer_size=100000,\n",
|
| 343 |
+
" learning_starts=1000,\n",
|
| 344 |
+
" batch_size=512,\n",
|
| 345 |
+
" gamma=0.99,\n",
|
| 346 |
+
" train_freq=1,\n",
|
| 347 |
+
" gradient_steps=1,\n",
|
| 348 |
+
" target_update_interval=500,\n",
|
| 349 |
+
" exploration_fraction=0.1,\n",
|
| 350 |
+
" exploration_initial_eps=1.0,\n",
|
| 351 |
+
" exploration_final_eps=0.05,\n",
|
| 352 |
+
" max_grad_norm=10,\n",
|
| 353 |
+
" verbose=0,\n",
|
| 354 |
+
" device=\"auto\"\n",
|
| 355 |
+
" )\n",
|
| 356 |
+
" else:\n",
|
| 357 |
+
" raise ValueError(f\"Unknown agent: {agent_name}\")\n",
|
| 358 |
+
" \n",
|
| 359 |
+
" # Train\n",
|
| 360 |
+
" model.learn(total_timesteps=total_timesteps)\n",
|
| 361 |
+
" \n",
|
| 362 |
+
" # Evaluate\n",
|
| 363 |
+
" # We'll use the environment logic to step through test set\n",
|
| 364 |
+
" test_env = FraudDetectionEnv(X_test, y_test)\n",
|
| 365 |
+
" obs, _ = test_env.reset()\n",
|
| 366 |
+
" \n",
|
| 367 |
+
" y_pred = []\n",
|
| 368 |
+
" y_true = []\n",
|
| 369 |
+
" \n",
|
| 370 |
+
" done = False\n",
|
| 371 |
+
" while not done:\n",
|
| 372 |
+
" action, _ = model.predict(obs, deterministic=True)\n",
|
| 373 |
+
" obs, reward, done, truncated, info = test_env.step(action)\n",
|
| 374 |
+
" \n",
|
| 375 |
+
" if 'true_label' in info:\n",
|
| 376 |
+
" y_true.append(info['true_label'])\n",
|
| 377 |
+
" y_pred.append(action)\n",
|
| 378 |
+
" \n",
|
| 379 |
+
" # Metrics\n",
|
| 380 |
+
" precision = precision_score(y_true, y_pred, zero_division=0)\n",
|
| 381 |
+
" recall = recall_score(y_true, y_pred, zero_division=0)\n",
|
| 382 |
+
" f1 = f1_score(y_true, y_pred, zero_division=0)\n",
|
| 383 |
+
" \n",
|
| 384 |
+
" return precision, recall, f1\n"
|
| 385 |
+
]
|
| 386 |
+
},
|
| 387 |
+
{
|
| 388 |
+
"cell_type": "code",
|
| 389 |
+
"execution_count": null,
|
| 390 |
+
"metadata": {},
|
| 391 |
+
"outputs": [
|
| 392 |
+
{
|
| 393 |
+
"name": "stdout",
|
| 394 |
+
"output_type": "stream",
|
| 395 |
+
"text": [
|
| 396 |
+
"\n",
|
| 397 |
+
"=== Processing Dataset: CreditCard ===\n",
|
| 398 |
+
"Loading CreditCard dataset...\n",
|
| 399 |
+
"\n",
|
| 400 |
+
"--- Preprocessing: Raw ---\n",
|
| 401 |
+
"Training PPO...\n"
|
| 402 |
+
]
|
| 403 |
+
}
|
| 404 |
+
],
|
| 405 |
+
"source": [
|
| 406 |
+
"# Configuration\n",
|
| 407 |
+
"datasets = ['CreditCard', 'PaySim']\n",
|
| 408 |
+
"preprocessing_methods = ['Raw', 'PCA_CTGAN']\n",
|
| 409 |
+
"agents = ['PPO', 'A2C', 'DQN']\n",
|
| 410 |
+
"\n",
|
| 411 |
+
"results = []\n",
|
| 412 |
+
"\n",
|
| 413 |
+
"for dataset_name in datasets:\n",
|
| 414 |
+
" print(f\"\\n=== Processing Dataset: {dataset_name} ===\")\n",
|
| 415 |
+
" \n",
|
| 416 |
+
" # Load Data\n",
|
| 417 |
+
" if dataset_name == 'CreditCard':\n",
|
| 418 |
+
" X_train, X_test, y_train, y_test = load_creditcard_data(\"creditcard.csv\")\n",
|
| 419 |
+
" else:\n",
|
| 420 |
+
" X_train, X_test, y_train, y_test = load_paysim_data(\"paysim.csv\")\n",
|
| 421 |
+
" \n",
|
| 422 |
+
" # Store original copies\n",
|
| 423 |
+
" X_train_orig, X_test_orig = X_train.copy(), X_test.copy()\n",
|
| 424 |
+
" y_train_orig = y_train.copy()\n",
|
| 425 |
+
"\n",
|
| 426 |
+
" for prep in preprocessing_methods:\n",
|
| 427 |
+
" print(f\"\\n--- Preprocessing: {prep} ---\")\n",
|
| 428 |
+
" \n",
|
| 429 |
+
" # Reset data\n",
|
| 430 |
+
" X_curr_train, X_curr_test = X_train_orig.copy(), X_test_orig.copy()\n",
|
| 431 |
+
" y_curr_train = y_train_orig.copy()\n",
|
| 432 |
+
" \n",
|
| 433 |
+
" # Apply Preprocessing\n",
|
| 434 |
+
" if prep == 'PCA_CTGAN':\n",
|
| 435 |
+
" X_curr_train, y_curr_train, X_curr_test = apply_pca_ctgan(X_curr_train, y_curr_train, X_curr_test, epochs=200)\n",
|
| 436 |
+
" \n",
|
| 437 |
+
" for agent in agents:\n",
|
| 438 |
+
" print(f\"Training {agent}...\")\n",
|
| 439 |
+
" try:\n",
|
| 440 |
+
" prec, rec, f1 = train_and_evaluate(agent, X_curr_train, y_curr_train, X_curr_test, y_test, total_timesteps=5000)\n",
|
| 441 |
+
" print(f\"Result: F1={f1:.4f} (Prec={prec:.4f}, Rec={rec:.4f})\")\n",
|
| 442 |
+
" \n",
|
| 443 |
+
" results.append({\n",
|
| 444 |
+
" 'Dataset': dataset_name,\n",
|
| 445 |
+
" 'Preprocessing': prep,\n",
|
| 446 |
+
" 'Agent': agent,\n",
|
| 447 |
+
" 'Precision': prec,\n",
|
| 448 |
+
" 'Recall': rec,\n",
|
| 449 |
+
" 'F1': f1\n",
|
| 450 |
+
" })\n",
|
| 451 |
+
" except Exception as e:\n",
|
| 452 |
+
" print(f\"Failed to train {agent}: {e}\")\n"
|
| 453 |
+
]
|
| 454 |
+
},
|
| 455 |
+
{
|
| 456 |
+
"cell_type": "markdown",
|
| 457 |
+
"metadata": {},
|
| 458 |
+
"source": [
|
| 459 |
+
"## 5. Results Visualization"
|
| 460 |
+
]
|
| 461 |
+
},
|
| 462 |
+
{
|
| 463 |
+
"cell_type": "code",
|
| 464 |
+
"execution_count": null,
|
| 465 |
+
"metadata": {},
|
| 466 |
+
"outputs": [],
|
| 467 |
+
"source": [
|
| 468 |
+
"# Create DataFrame from results sorted by F1 score\n",
|
| 469 |
+
"results_df = pd.DataFrame(results, columns=['Dataset', 'Agent', 'Preprocessing', 'F1'])\n",
|
| 470 |
+
"sorted_results_df = results_df.sort_values(by='F1', ascending=False)\n",
|
| 471 |
+
"\n",
|
| 472 |
+
"print(\"\\n=== Final Results ===\")\n",
|
| 473 |
+
"print(results_df)\n",
|
| 474 |
+
"\n",
|
| 475 |
+
"# Plotting\n",
|
| 476 |
+
"plt.figure(figsize=(14, 6))\n",
|
| 477 |
+
"\n",
|
| 478 |
+
"plt.subplot(1, 2, 1)\n",
|
| 479 |
+
"sns.barplot(data=results_df[results_df['Dataset'] == 'CreditCard'], x='Agent', y='F1', hue='Preprocessing')\n",
|
| 480 |
+
"plt.title('CreditCard Dataset - F1 Score')\n",
|
| 481 |
+
"plt.ylim(0, 1)\n",
|
| 482 |
+
"\n",
|
| 483 |
+
"plt.subplot(1, 2, 2)\n",
|
| 484 |
+
"sns.barplot(data=results_df[results_df['Dataset'] == 'PaySim'], x='Agent', y='F1', hue='Preprocessing')\n",
|
| 485 |
+
"plt.title('PaySim Dataset - F1 Score')\n",
|
| 486 |
+
"plt.ylim(0, 1)\n",
|
| 487 |
+
"\n",
|
| 488 |
+
"plt.tight_layout()\n",
|
| 489 |
+
"plt.show()"
|
| 490 |
+
]
|
| 491 |
+
}
|
| 492 |
+
],
|
| 493 |
+
"metadata": {
|
| 494 |
+
"kernelspec": {
|
| 495 |
+
"display_name": ".pyvenv",
|
| 496 |
+
"language": "python",
|
| 497 |
+
"name": "python3"
|
| 498 |
+
},
|
| 499 |
+
"language_info": {
|
| 500 |
+
"codemirror_mode": {
|
| 501 |
+
"name": "ipython",
|
| 502 |
+
"version": 3
|
| 503 |
+
},
|
| 504 |
+
"file_extension": ".py",
|
| 505 |
+
"mimetype": "text/x-python",
|
| 506 |
+
"name": "python",
|
| 507 |
+
"nbconvert_exporter": "python",
|
| 508 |
+
"pygments_lexer": "ipython3",
|
| 509 |
+
"version": "3.13.7"
|
| 510 |
+
}
|
| 511 |
+
},
|
| 512 |
+
"nbformat": 4,
|
| 513 |
+
"nbformat_minor": 2
|
| 514 |
+
}
|
false/attention_pooled_embeddings.pkl
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:bcf22e6150629cf983dcc72493abfcdeaa9abbd1ec0ff4bd75c09a1003b88151
|
| 3 |
+
size 9083260
|
false/custom_env.py
ADDED
|
@@ -0,0 +1,99 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import numpy as np
|
| 2 |
+
|
| 3 |
+
|
| 4 |
+
class FraudDetectionEnv(gym.Env):
|
| 5 |
+
"""
|
| 6 |
+
A custom Gym environment for Fraud Detection using embeddings.
|
| 7 |
+
|
| 8 |
+
State: Embedding of a transaction.
|
| 9 |
+
Action: 0 (Declare Not Fraud), 1 (Declare Fraud).
|
| 10 |
+
Reward: Based on correctly/incorrectly classifying fraud vs non-fraud.
|
| 11 |
+
"""
|
| 12 |
+
def __init__(self, embeddings: np.ndarray, labels: np.ndarray, reward_config: dict):
|
| 13 |
+
super().__init__()
|
| 14 |
+
|
| 15 |
+
# Ensure data consistency
|
| 16 |
+
assert embeddings.shape[0] == labels.shape[0], "Embeddings and labels must have the same number of instances."
|
| 17 |
+
assert embeddings.shape[1] == 768, f"Embeddings must be 768-dimensional, but got {embeddings.shape[1]}"
|
| 18 |
+
|
| 19 |
+
self.embeddings = embeddings.astype(np.float32)
|
| 20 |
+
self.labels = labels.astype(np.int64)
|
| 21 |
+
|
| 22 |
+
self.num_instances = self.embeddings.shape[0]
|
| 23 |
+
self.reward_config = reward_config
|
| 24 |
+
|
| 25 |
+
# Define action and observation space
|
| 26 |
+
# Action Space: Discrete(2) -> 0 for Not Fraud, 1 for Fraud
|
| 27 |
+
self.action_space = spaces.Discrete(2)
|
| 28 |
+
|
| 29 |
+
# Observation Space: Box(low, high, shape, dtype) -> 768-dim vector
|
| 30 |
+
self.observation_space = spaces.Box(low=-np.inf, high=np.inf, shape=(768,), dtype=np.float32)
|
| 31 |
+
|
| 32 |
+
# Internal state
|
| 33 |
+
self._current_index = 0
|
| 34 |
+
self._order = np.arange(self.num_instances)
|
| 35 |
+
np.random.shuffle(self._order) # Shuffle the order of instances initially
|
| 36 |
+
|
| 37 |
+
|
| 38 |
+
def step(self, action: int):
|
| 39 |
+
# Check if episode is done
|
| 40 |
+
if self._current_index >= self.num_instances:
|
| 41 |
+
print("Warning: step() called when episode is already done.")
|
| 42 |
+
return self.observation_space.sample() * 0, 0, True, False, {} # Return dummy values
|
| 43 |
+
|
| 44 |
+
# Get current instance data based on shuffled order
|
| 45 |
+
actual_index = self._order[self._current_index]
|
| 46 |
+
current_embedding = self.embeddings[actual_index]
|
| 47 |
+
true_label = self.labels[actual_index]
|
| 48 |
+
|
| 49 |
+
# Determine reward
|
| 50 |
+
reward = 0
|
| 51 |
+
if action == 1 and true_label == 1:
|
| 52 |
+
reward = self.reward_config.get('TP', 0)
|
| 53 |
+
elif action == 1 and true_label == 0:
|
| 54 |
+
reward = self.reward_config.get('FP', 0)
|
| 55 |
+
elif action == 0 and true_label == 1:
|
| 56 |
+
reward = self.reward_config.get('FN', 0)
|
| 57 |
+
elif action == 0 and true_label == 0:
|
| 58 |
+
reward = self.reward_config.get('TN', 0)
|
| 59 |
+
|
| 60 |
+
# Move to the next instance
|
| 61 |
+
self._current_index += 1
|
| 62 |
+
|
| 63 |
+
# Check if the episode is finished
|
| 64 |
+
done = self._current_index >= self.num_instances
|
| 65 |
+
truncated = False
|
| 66 |
+
|
| 67 |
+
# Get the next observation
|
| 68 |
+
next_observation = np.zeros_like(current_embedding, dtype=np.float32) # Default for done state
|
| 69 |
+
if not done:
|
| 70 |
+
next_observation = self.embeddings[self._order[self._current_index]]
|
| 71 |
+
|
| 72 |
+
info = {
|
| 73 |
+
'true_label': true_label,
|
| 74 |
+
'predicted_action': action,
|
| 75 |
+
'instance_uid': actual_index,
|
| 76 |
+
'is_done': done
|
| 77 |
+
}
|
| 78 |
+
|
| 79 |
+
return next_observation, reward, done, truncated, info
|
| 80 |
+
|
| 81 |
+
|
| 82 |
+
def reset(self, seed=None, options=None):
|
| 83 |
+
super().reset(seed=seed) # Handles seeding
|
| 84 |
+
|
| 85 |
+
# Reset index and shuffle order for a new episode
|
| 86 |
+
self._current_index = 0
|
| 87 |
+
self._order = np.arange(self.num_instances)
|
| 88 |
+
self.np_random.shuffle(self._order) # Use the environment's random number generator
|
| 89 |
+
|
| 90 |
+
# Get the first observation of the new episode
|
| 91 |
+
initial_observation = self.embeddings[self._order[self._current_index]]
|
| 92 |
+
|
| 93 |
+
info = {'instance_uid': self._order[self._current_index]}
|
| 94 |
+
|
| 95 |
+
return initial_observation, info
|
| 96 |
+
|
| 97 |
+
def close(self):
|
| 98 |
+
# Optional: Implement cleanup
|
| 99 |
+
pass
|
false/dqn_fraud_checkpoints/dqn_fraud_model_100000_steps.zip
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:585c5b2b8027b09c2b64da89fba0d168044312fbc66d1208bf81f4cb757a1677
|
| 3 |
+
size 917517
|
false/dqn_fraud_checkpoints/dqn_fraud_model_10000_steps.zip
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:ac24b960c596527b905ccee51d9e2f5809f85520c1d13a0135f8b3b6f716d2f4
|
| 3 |
+
size 915979
|
false/dqn_fraud_checkpoints/dqn_fraud_model_110000_steps.zip
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:6fc33e3614958ac6455a1ef21551a548301a5820caf2374d4932292fc521fab1
|
| 3 |
+
size 917692
|
false/dqn_fraud_checkpoints/dqn_fraud_model_160000_steps.zip
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:df091f161c1c7e4b802f2aca0fc5ed2285b47250637f748ecd46277e479325f0
|
| 3 |
+
size 181274
|
false/dqn_fraud_checkpoints/dqn_fraud_model_20000_steps.zip
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:bb022da5cc8d9e098a2cd7f18c4c869a5c9a82176df20f38ec42011cd5e6105a
|
| 3 |
+
size 916111
|
false/dqn_fraud_checkpoints/dqn_fraud_model_240000_steps.zip
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:625e4ff26f05cec0be165ceed45e5719cdbee8f3eb758eb60d7ba06d3b63fb63
|
| 3 |
+
size 182271
|
false/dqn_fraud_checkpoints/dqn_fraud_model_30000_steps.zip
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:0e3e1c7b46d356e97b2f6c6aa885ce0edd0cb3035425076dabb44c5d15ab5a7a
|
| 3 |
+
size 916276
|
false/dqn_fraud_checkpoints/dqn_fraud_model_40000_steps.zip
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:fabd12cba4197d7cb21562636653d0bba1ecd9cf3b8ea021806df5a41361cf22
|
| 3 |
+
size 916448
|
false/dqn_fraud_checkpoints/dqn_fraud_model_50000_steps.zip
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:8be26553b9e8b8f96ee2b7d5e88e62b78d7a2dfb4c0019790192e77d20d76409
|
| 3 |
+
size 916648
|
false/dqn_fraud_checkpoints/dqn_fraud_model_60000_steps.zip
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:c87078831ca9a1e05b70305a02916ed6e0deb00a4f60dd5c7506247fea00fcca
|
| 3 |
+
size 916817
|
false/dqn_fraud_checkpoints/dqn_fraud_model_70000_steps.zip
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:5bea3ac686d75f6ee47527fa0e0745623b78e99c3d2e6abbd6587fc0d84987b1
|
| 3 |
+
size 916980
|
false/dqn_fraud_checkpoints/dqn_fraud_model_80000_steps.zip
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:a9e11c031044237d777f31088021a35fa6cf1f103b6b961dccee6ce284155211
|
| 3 |
+
size 179951
|
false/dqn_fraud_checkpoints/dqn_fraud_model_90000_steps.zip
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:cf6d054488ef6e3edf1016c06cb6bdeb53399ee08d57a1c931c9839534b2c989
|
| 3 |
+
size 917357
|
false/dqn_fraud_tb/DQN_4/events.out.tfevents.1747182467.archlinux.48104.0
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:f87439c552fc69f40a847fafda6d5cb09a708cd67dc5e4b6060fdf1a2727787c
|
| 3 |
+
size 6148
|
false/dqn_fraud_tb/DQN_5/events.out.tfevents.1764545110.archlinux.67971.0
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:1223f1afb9acd6b95baea0bc047561d7bb25d8591b14026bcc7cddcba07267e1
|
| 3 |
+
size 6148
|
false/dqn_fraud_tb/DQN_6/events.out.tfevents.1764546770.archlinux.73972.0
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:826cbeb2ca5a3bfd30c75fee2cd683f512e11a7b6340e952868fe789800f38a7
|
| 3 |
+
size 6148
|
false/dqn_fraud_tb/DQN_7/events.out.tfevents.1764584658.archlinux.5620.0
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:64b7581c3b247c8e6add6398488433aef03712ce49f29267d4aa4d32f224bfc4
|
| 3 |
+
size 6148
|
false/dqn_fraud_tb/DQN_8/events.out.tfevents.1764668813.archlinux.47939.0
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:96301031addb0f2f5e872f0266a1b348f8d6800013a9e1baedeae7743d9d48f6
|
| 3 |
+
size 6148
|
false/dqn_fraud_tb/evaluation/eval_20250514-014902/events.out.tfevents.1747183742.archlinux.48104.3
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:7e5d9747632cf39d7324fed3343bb8984ec5d5007a90ede736d21cbb1bda54a6
|
| 3 |
+
size 307820
|
false/dqn_fraud_tb/evaluation/eval_20251201-002715/events.out.tfevents.1764545235.archlinux.67971.1
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:bd4083078fa50c8f52f508982ac76662b624b93295919a71751d71c6c955f534
|
| 3 |
+
size 307902
|
false/dqn_fraud_tb/evaluation/eval_20251201-005435/events.out.tfevents.1764546875.archlinux.73972.1
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:ba41d0fa7f85af90fcbbd3cd79599f00a5fd225caecdc6a574f7833a933052eb
|
| 3 |
+
size 312770
|
false/dqn_fraud_tb/evaluation/eval_20251201-112610/events.out.tfevents.1764584770.archlinux.5620.1
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:32e684588cbd59bcaa29bc43f01e98d297dc0e7b12e42d4416ce12200ea0aa8c
|
| 3 |
+
size 308804
|
false/dqn_fraud_tb/evaluation/eval_20251202-104820/events.out.tfevents.1764668900.archlinux.47939.1
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:6079783a5ceb7bc66fd81adefe0d4ec007172daefa12dcbc85274050421c720c
|
| 3 |
+
size 315329
|
false/embeddings.pkl
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:d3e96b96a7c7f0610a88a48f8a26044e0d209c9054af7153d8d2f369bb1b5a8e
|
| 3 |
+
size 9083260
|
false/fraud-detection-with-distilbert.ipynb
ADDED
|
The diff for this file is too large to render.
See raw diff
|
|
|
false/models/a2c_fraud_model.zip
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:fc864a6872392233e3e6243724fbaee59513c5974b0db2b87d19d59614d93fef
|
| 3 |
+
size 945556
|