{"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"name":"python","version":"3.10.14","mimetype":"text/x-python","codemirror_mode":{"name":"ipython","version":3},"pygments_lexer":"ipython3","nbconvert_exporter":"python","file_extension":".py"},"kaggle":{"accelerator":"none","dataSources":[{"sourceId":88400,"databundleVersionId":10121331,"sourceType":"competition"}],"dockerImageVersionId":30786,"isInternetEnabled":true,"language":"python","sourceType":"notebook","isGpuEnabled":false}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"code","source":"# This Python 3 environment comes with many helpful analytics libraries installed\n# It is defined by the kaggle/python Docker image: https://github.com/kaggle/docker-python\n# For example, here's several helpful packages to load\n\nimport numpy as np # linear algebra\nimport pandas as pd # data processing, CSV file I/O (e.g. pd.read_csv)\n\n# Input data files are available in the read-only \"../input/\" directory\n# For example, running this (by clicking run or pressing Shift+Enter) will list all files under the input directory\n\nimport os\nfor dirname, _, filenames in os.walk('/kaggle/input'):\n    for filename in filenames:\n        print(os.path.join(dirname, filename))\n\n# You can write up to 20GB to the current directory (/kaggle/working/) that gets preserved as output when you create a version using \"Save & Run All\" \n# You can also write temporary files to /kaggle/temp/, but they won't be saved outside of the current session","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true,"execution":{"iopub.status.busy":"2024-11-12T05:03:24.255261Z","iopub.execute_input":"2024-11-12T05:03:24.256209Z","iopub.status.idle":"2024-11-12T05:03:24.266057Z","shell.execute_reply.started":"2024-11-12T05:03:24.256144Z","shell.execute_reply":"2024-11-12T05:03:24.264824Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# 데이터셋 준비","metadata":{}},{"cell_type":"code","source":"train = pd.read_csv(\"/kaggle/input/aiffel-ds-3-credit-card-fraud-detection/train.csv\") \ntest = pd.read_csv(\"/kaggle/input/aiffel-ds-3-credit-card-fraud-detection/test.csv\")\nsubmission = pd.read_csv(\"/kaggle/input/aiffel-ds-3-credit-card-fraud-detection/sample_submission.csv\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2024-11-12T05:03:24.267945Z","iopub.execute_input":"2024-11-12T05:03:24.268396Z","iopub.status.idle":"2024-11-12T05:03:28.532724Z","shell.execute_reply.started":"2024-11-12T05:03:24.268320Z","shell.execute_reply":"2024-11-12T05:03:28.531455Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# 데이터 이해 및 탐색(EDA)","metadata":{}},{"cell_type":"markdown","source":"- 기본 데이터 형태 확인","metadata":{}},{"cell_type":"markdown","source":"### train\n- 컬럼 기본 정보\n  - id : 고유 숫자 부여 \n  - Time : 각 거래와 데이터셋의 첫 거래 사이의 경과 시간(초)\n  - V1 ~ V28 : PCA로 얻은 주성분\n  - Amount : 거래 금액\n  - Class : 응답 변수(사기 거래인 경우 1, 그렇지 않은 경우 0) -> \"Target\" 데이터!","metadata":{}},{"cell_type":"code","source":"train.columns","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2024-11-12T05:03:28.534729Z","iopub.execute_input":"2024-11-12T05:03:28.535131Z","iopub.status.idle":"2024-11-12T05:03:28.542876Z","shell.execute_reply.started":"2024-11-12T05:03:28.535090Z","shell.execute_reply":"2024-11-12T05:03:28.541707Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# train\ntrain.head(10)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2024-11-12T05:03:28.544636Z","iopub.execute_input":"2024-11-12T05:03:28.545525Z","iopub.status.idle":"2024-11-12T05:03:28.594359Z","shell.execute_reply.started":"2024-11-12T05:03:28.545468Z","shell.execute_reply":"2024-11-12T05:03:28.593202Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"- 170883행, 32열로 이루어져 있음","metadata":{}},{"cell_type":"code","source":"train.shape","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2024-11-12T05:03:28.596984Z","iopub.execute_input":"2024-11-12T05:03:28.597358Z","iopub.status.idle":"2024-11-12T05:03:28.604674Z","shell.execute_reply.started":"2024-11-12T05:03:28.597318Z","shell.execute_reply":"2024-11-12T05:03:28.603450Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"- train의 경우, int형 데이터 및 float형 데이터 -> 즉, 수치형 데이터만 존재\n  - (데이터셋 설명 파트에서 \"이 데이터셋은 PCA 변환을 거친 수치형 변수들만 포함\"이라는 근거가 있음)\n- 결측치는 없는 것으로 확인","metadata":{}},{"cell_type":"code","source":"train.info()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2024-11-12T05:03:28.606763Z","iopub.execute_input":"2024-11-12T05:03:28.607312Z","iopub.status.idle":"2024-11-12T05:03:28.647053Z","shell.execute_reply.started":"2024-11-12T05:03:28.607264Z","shell.execute_reply":"2024-11-12T05:03:28.645917Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"train.isnull().sum()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2024-11-12T05:03:28.648866Z","iopub.execute_input":"2024-11-12T05:03:28.649249Z","iopub.status.idle":"2024-11-12T05:03:28.666622Z","shell.execute_reply.started":"2024-11-12T05:03:28.649209Z","shell.execute_reply":"2024-11-12T05:03:28.665421Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"train.describe()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2024-11-12T05:03:28.667895Z","iopub.execute_input":"2024-11-12T05:03:28.668222Z","iopub.status.idle":"2024-11-12T05:03:29.005380Z","shell.execute_reply.started":"2024-11-12T05:03:28.668187Z","shell.execute_reply":"2024-11-12T05:03:29.004217Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"### test","metadata":{}},{"cell_type":"code","source":"# test\ntest.head()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2024-11-12T05:03:29.006672Z","iopub.execute_input":"2024-11-12T05:03:29.007022Z","iopub.status.idle":"2024-11-12T05:03:29.034277Z","shell.execute_reply.started":"2024-11-12T05:03:29.006985Z","shell.execute_reply":"2024-11-12T05:03:29.033040Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"- 테스트 데이터셋이기 때문에 위와 동일","metadata":{}},{"cell_type":"code","source":"test.info()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2024-11-12T05:03:29.035915Z","iopub.execute_input":"2024-11-12T05:03:29.036267Z","iopub.status.idle":"2024-11-12T05:03:29.059666Z","shell.execute_reply.started":"2024-11-12T05:03:29.036229Z","shell.execute_reply":"2024-11-12T05:03:29.058480Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"### submission","metadata":{}},{"cell_type":"markdown","source":"- 추후 제출할 데이터이기 때문에, id 및 class만 존재","metadata":{}},{"cell_type":"code","source":"# submission\nsubmission.head()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2024-11-12T05:03:29.064950Z","iopub.execute_input":"2024-11-12T05:03:29.065420Z","iopub.status.idle":"2024-11-12T05:03:29.075840Z","shell.execute_reply.started":"2024-11-12T05:03:29.065362Z","shell.execute_reply":"2024-11-12T05:03:29.074722Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# 데이터셋 인포메이션\n- Dataset Description에서 확인해보자.\n  - \"클래스 불균형 비율을 고려하여, 정밀도-재현율 곡선 아래 영역(AUPRC)을 사용하여 정확도를 측정할 것을 권장합니다. 불균형 분류에서는 혼동 행렬 정확도가 큰 의미가 없습니다.\"\n  - 해당 단락에서 유추할 수 있듯 **Class** 속성은 불균형 분포를 가지고 있음!\n  - 정상 거래에 비해 사기 거래가 현저히 적은 것이 대부분이기 때문에 불균형 분포를 가지는 것이 당연\n    - 'Class' : 응답 변수로, 사기 거래인 경우 1, 그렇지 않은 경우 0의 값\n      \n<br>\n\n- 데이터 특징\n  - PCA 변환을 거친 수치형 변수만 포함\n  - Vn형태의 컬럼: PCA로 얻은 주성분\n  - 'Time', 'Amount'는 PCA 작업 X\n    - 'Amount' : 예시 의존적 비용 민감 학습 등에 사용 가능\n\n    ","metadata":{}},{"cell_type":"markdown","source":"# 결측치 및 이상치는?\n- 결측치는 없는 데이터이기 때문에 불필요. 또한, 결측치를 특정 데이터로 대체한 흔적도 현재는 보이지 않는 것으로 판단\n- 이상치의 경우, 이상치 데이터가 있는 경우와 제거 후의 경우를 함께 확인하기 위해 현 단계에서 처리하지 않음.\n  - 이상치는 IQR로 제거 예정","metadata":{}},{"cell_type":"markdown","source":"# 필요한 라이브러리 임포트\n- 사용할 라이브러리\n  - seaborn : 시각화에 필요\n  - pyplot : 시각화에 필요\n  - train_test_split : 테스트 데이터 생성을 위해 필요\n  - StandardScaler : 빠르게 스탠다드 스케일링을 진행할 예정\n  - LogisticRegression : 로지스틱 회귀 모델에 필요\n  - RandomForestClassifier, GradientBoostingClassifier : 모델\n  - roc_auc_score : 결과 수치를 보기 위해 필요\n  ","metadata":{}},{"cell_type":"code","source":"import matplotlib.pyplot as plt\nimport seaborn as sns\nfrom sklearn.model_selection import train_test_split\nfrom sklearn.preprocessing import StandardScaler\nfrom sklearn.linear_model import LogisticRegression\nfrom sklearn.ensemble import RandomForestClassifier, GradientBoostingClassifier\nfrom sklearn.metrics import roc_auc_score, roc_curve, classification_report","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2024-11-12T05:03:29.077213Z","iopub.execute_input":"2024-11-12T05:03:29.077591Z","iopub.status.idle":"2024-11-12T05:03:29.628524Z","shell.execute_reply.started":"2024-11-12T05:03:29.077544Z","shell.execute_reply":"2024-11-12T05:03:29.626433Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# 불균형 분포 데이터 Class를 확인하자\n- 수치로 알 수 있듯, 정상 거래 데이터에 비해 사기 거래 데이터는 현저히 적음\n- 머신러닝 모델에서는 극도로 불균형한 레이블 값 분포는 좋은 성능을 내기 어려움 -> **오버 샘플링**으로 보완\n> 왜?\n> - 현재 중심을 두고 봐야 하는 데이터는 사기 거래 데이터인데, 이 데이터의 양이 매우 적기 때문에 학습을 위해서는 충분한 데이터를 확보해야한다는 의견이 있기 때문\n> - SMOTE를 이용해 K nearest Neighbor를 찾아 -> 이 데이터와 K개 이웃 간 차이를 일정한 값으로 만들어 값을 증식할 예정","metadata":{}},{"cell_type":"code","source":"print(train['Class'].value_counts())","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2024-11-12T05:03:29.630612Z","iopub.execute_input":"2024-11-12T05:03:29.631037Z","iopub.status.idle":"2024-11-12T05:03:29.641913Z","shell.execute_reply.started":"2024-11-12T05:03:29.630986Z","shell.execute_reply":"2024-11-12T05:03:29.640424Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"- 시각화해서 확인해보자\n  - scatterplot 이용\n  - 정상 범주에 속하는 데이터 1의 경우 그 수가 많지만, 사기 거래 데이터인 0인 경우 적게 분포하고 있음을 확인 가능","metadata":{}},{"cell_type":"code","source":"sns.scatterplot(x=train.index, y=train['Class'])","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2024-11-12T05:03:29.643218Z","iopub.execute_input":"2024-11-12T05:03:29.643637Z","iopub.status.idle":"2024-11-12T05:03:30.624845Z","shell.execute_reply.started":"2024-11-12T05:03:29.643595Z","shell.execute_reply":"2024-11-12T05:03:30.623667Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# 데이터 스케일링 및 인코딩\n- StandardScaler로 데이터 평균을 0, 표준 편차를 1로 맞추기\n- 모델에 적용할 때 성능을 더 잘 올리기 위해서 간단히 적용","metadata":{}},{"cell_type":"code","source":"scaler = StandardScaler()\ntrain_scaled = pd.DataFrame(scaler.fit_transform(train.drop('Class', axis=1)), columns=train.columns[:-1])","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2024-11-12T05:03:30.627020Z","iopub.execute_input":"2024-11-12T05:03:30.627436Z","iopub.status.idle":"2024-11-12T05:03:30.770894Z","shell.execute_reply.started":"2024-11-12T05:03:30.627361Z","shell.execute_reply":"2024-11-12T05:03:30.769711Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"- 인코딩의 경우, 주요 컬럼에서 굳이 필요하지 않다고 판단하여 진행하지 않음.","metadata":{}},{"cell_type":"markdown","source":"# 피쳐 엔지니어링\n- 컬럼 자체가 많은 편이 아니기 때문에, 굳이 필요하지 않은 Time 컬럼을 제외하고 고려했을 때 Amount를 생각해볼 수 있음.\n- Amount 값의 스케일이 다른 컬럼에 비해 큰 편이기 때문에 -> 모델이 빠르게 학습하기 어려울 수 있음","metadata":{}},{"cell_type":"code","source":"train['Amount'].max()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2024-11-12T05:03:30.772310Z","iopub.execute_input":"2024-11-12T05:03:30.772737Z","iopub.status.idle":"2024-11-12T05:03:30.781976Z","shell.execute_reply.started":"2024-11-12T05:03:30.772695Z","shell.execute_reply":"2024-11-12T05:03:30.780630Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"train['Amount'].min()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2024-11-12T05:03:30.783656Z","iopub.execute_input":"2024-11-12T05:03:30.784074Z","iopub.status.idle":"2024-11-12T05:03:30.794438Z","shell.execute_reply.started":"2024-11-12T05:03:30.784034Z","shell.execute_reply":"2024-11-12T05:03:30.793258Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"- 즉, Amount를 로그 값으로 변환해서 스케일을 줄이면 모델 학습에 있어 유리할 것으로 판단!","metadata":{}},{"cell_type":"code","source":"train['Amount_log'] = np.log1p(train['Amount'])","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2024-11-12T05:03:30.795719Z","iopub.execute_input":"2024-11-12T05:03:30.796071Z","iopub.status.idle":"2024-11-12T05:03:30.810006Z","shell.execute_reply.started":"2024-11-12T05:03:30.796033Z","shell.execute_reply":"2024-11-12T05:03:30.808693Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"sns.scatterplot(x=train.index, y=train['Amount_log'])","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2024-11-12T05:03:30.811755Z","iopub.execute_input":"2024-11-12T05:03:30.812112Z","iopub.status.idle":"2024-11-12T05:03:31.861732Z","shell.execute_reply.started":"2024-11-12T05:03:30.812073Z","shell.execute_reply":"2024-11-12T05:03:31.860472Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"- 불필요한 컬럼인 Time은 삭제하기(사기 거래 예측에 필요하지 않은 데이터라 판단)\n  - DataFrame을 복사해서 Time 피쳐만 삭제해서 복사한 DataFrame을 반환하기","metadata":{}},{"cell_type":"code","source":"def get_preprocessed_df(df=None):\n    df_copy = df.copy()\n    df_copy.drop('Time', axis=1, inplace=True)\n    return df_copy","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2024-11-12T05:03:31.863106Z","iopub.execute_input":"2024-11-12T05:03:31.863469Z","iopub.status.idle":"2024-11-12T05:03:31.869728Z","shell.execute_reply.started":"2024-11-12T05:03:31.863431Z","shell.execute_reply":"2024-11-12T05:03:31.868537Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# 학습 데이터 및 테스트 데이터 세트 분리\n- 본격적인 모델 학습을 위해 데이터 세트를 분리!\n\n- `get_train_test_dataset()`\n  - 위의 Time 컬럼 삭제 함수 get_preprocessed_df()를 불러오기\n  - split으로 학습 및 테스트 데이터 분할\n    - 테스트 데이터 세트를 전체의 30%(Stratified) 방식으로 추출해 학습 데이터 세트 및 테스트 데이터 세트 레이블 값 분포도를 최대한 동일하게 만들기\n> 참고 : 교재에서는 레이블 값 분포도를 Stratified 방식으로 추출하여 유지하는데, y_target의 경우 현재 고유값도 2개 이상이며 결측치도 없지만 ValueError가 발생했었음.\n> - Amout_log 컬럼을 맨 마지막에 파생 변수로 추가해서, 교재의 코드는 사용할 수 없기 때문에 X_features와 y_target을 Class 컬럼으로 정확히 명시하여 에러 삭제 완료.","metadata":{}},{"cell_type":"code","source":"def get_train_test_dataset(df=None):\n    df_copy = get_preprocessed_df(df)\n    X_features = df_copy.drop(columns=['Class'])\n    y_target = df_copy['Class']\n\n    # print(\"y_target 고유값 개수:\", y_target.nunique())\n    # print(\"y_target 결측값 개수:\", y_target.isnull().sum())\n\n    # 학습 및 데이터 세트 분할\n    X_train, X_test, y_train, y_test = \\\n    train_test_split(X_features, y_target, test_size=0.3, random_state=0, stratify=y_target)\n    return X_train, X_test, y_train, y_test\n\nX_train, X_test, y_train, y_test = get_train_test_dataset(train)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2024-11-12T05:03:31.871259Z","iopub.execute_input":"2024-11-12T05:03:31.871690Z","iopub.status.idle":"2024-11-12T05:03:32.090351Z","shell.execute_reply.started":"2024-11-12T05:03:31.871634Z","shell.execute_reply":"2024-11-12T05:03:32.089079Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"- 학습 데이터 세트 및 테스트 데이터 세트 레이블 값 비율 확인(백분율)\n  - 학습 데이터의 1 비율 : 0.210671\n  - 테스트 데이터 1 비율 : 0.21067","metadata":{}},{"cell_type":"code","source":"print('학습 데이터 레이블 값 비율')\nprint(y_train.value_counts()/y_train.shape[0] * 100)\nprint('테스트 데이터 레이블 값 비율')\nprint(y_test.value_counts()/y_test.shape[0] * 100)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2024-11-12T05:03:32.091824Z","iopub.execute_input":"2024-11-12T05:03:32.092192Z","iopub.status.idle":"2024-11-12T05:03:32.104357Z","shell.execute_reply.started":"2024-11-12T05:03:32.092153Z","shell.execute_reply":"2024-11-12T05:03:32.103097Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# 모델을 고르자\n- 로지스틱 회귀 : 해당 데이터는 분포가 선형적이기 때문에 선형 모델에 적용하면 좋은 성능을 낼 수 있을 것으로 판단\n- 랜덤 포레스트 : 앙상블 학습, 해당 데이터에서의 상관관계가 일정한 특징을 띄지 않는 것으로 파악되어 복잡한 관계에서 유리한 모델도 사용해보는 것으로 판단\n- LightGBM : 현재 데이터의 가장 큰 특징인 \"불균형 데이터\"에 가장 적합한 모델이기 때문","metadata":{}},{"cell_type":"markdown","source":"- 랜덤 포레스트도 사용해보려는 이유\n  - 현재의 상관 행렬 계산만으로, 현 단계에서는 피쳐 간의 명확한 상관과계 수치의 이유를 파악하기 어렵다고 판단했기 때문","metadata":{}},{"cell_type":"code","source":"train.columns","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2024-11-12T05:03:32.106432Z","iopub.execute_input":"2024-11-12T05:03:32.106917Z","iopub.status.idle":"2024-11-12T05:03:32.119626Z","shell.execute_reply.started":"2024-11-12T05:03:32.106863Z","shell.execute_reply":"2024-11-12T05:03:32.118032Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import seaborn as sns\nimport matplotlib.pyplot as plt\n\ndf_filtered = train.drop(columns=['V1', 'V2', 'V3', 'V4', 'V5', 'V6', 'V7', 'V8', 'V9',\n       'V10', 'V11', 'V12', 'V13', 'V14', 'V15', 'V16', 'V17', 'V18', 'V19',\n       'V20', 'V21', 'V22', 'V23', 'V24', 'V25', 'V26', 'V27', 'V28'])\n\ncorrelation_matrix = df_filtered.corr()\n\nplt.figure(figsize=(12, 8))\nsns.heatmap(correlation_matrix, annot=True, cmap='coolwarm', fmt='.2f', cbar=True)\nplt.title('Correlation Matrix')\nplt.show()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2024-11-12T05:03:32.121266Z","iopub.execute_input":"2024-11-12T05:03:32.121693Z","iopub.status.idle":"2024-11-12T05:03:32.573019Z","shell.execute_reply.started":"2024-11-12T05:03:32.121652Z","shell.execute_reply":"2024-11-12T05:03:32.571676Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"### 로지스틱 회귀\n- get_clf_eval 함수를 가져와 사용\n  - 오차 행렬과 정확도, 정밀도, 재현율, F1, ROC-AUC를 보여주기 때문","metadata":{}},{"cell_type":"code","source":"from sklearn.metrics import confusion_matrix, accuracy_score, precision_score, recall_score, f1_score\nfrom sklearn.metrics import roc_auc_score\n\ndef get_clf_eval(y_test, pred=None, pred_proba=None):\n    confusion = confusion_matrix( y_test, pred)\n    accuracy = accuracy_score(y_test , pred)\n    precision = precision_score(y_test , pred)\n    recall = recall_score(y_test , pred)\n    f1 = f1_score(y_test,pred)\n    # ROC-AUC 추가\n    roc_auc = roc_auc_score(y_test, pred_proba)\n    print('오차 행렬')\n    print(confusion)\n    # ROC-AUC print 추가\n    print('정확도: {0:.4f}, 정밀도: {1:.4f}, 재현율: {2:.4f},\\\n    F1: {3:.4f}, ROC-AUC:{4:.4f}'.format(accuracy, precision, recall, f1, roc_auc))","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2024-11-12T05:03:32.574602Z","iopub.execute_input":"2024-11-12T05:03:32.574982Z","iopub.status.idle":"2024-11-12T05:03:32.583231Z","shell.execute_reply.started":"2024-11-12T05:03:32.574942Z","shell.execute_reply":"2024-11-12T05:03:32.582047Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# 로지스틱 회귀\nfrom sklearn.linear_model import LogisticRegression\n\nlr_clf = LogisticRegression()\nlr_clf.fit(X_train, y_train)\nlr_pred = lr_clf.predict(X_test)\nlr_pred_proba = lr_clf.predict_proba(X_test)[:, 1]\n\nget_clf_eval(y_test, lr_pred, lr_pred_proba)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2024-11-12T05:03:32.584733Z","iopub.execute_input":"2024-11-12T05:03:32.585138Z","iopub.status.idle":"2024-11-12T05:03:35.337857Z","shell.execute_reply.started":"2024-11-12T05:03:32.585088Z","shell.execute_reply":"2024-11-12T05:03:35.336758Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"#### 첫 로지스틱 회귀 결과\n  - 정확도: 0.9990\n  - 정밀도: 0.8452\n  - 재현율: 0.6574\n  - F1: 0.7396\n  - ROC-AUC:0.9125","metadata":{}},{"cell_type":"markdown","source":"### LightGBM\n- Estimator 객체 및, 학습 데이터 세트 입력을 통해 학습, 예측, 평가 수행","metadata":{}},{"cell_type":"code","source":"def get_model_train_eval(model, ftr_train=None, ftr_test=None, tgt_train=None, tgt_test=None):\n    model.fit(ftr_train, tgt_train)\n    pred = model.predict(ftr_test)\n    pred_proba = model.predict_proba(ftr_test)[:, 1]\n    get_clf_eval(tgt_test, pred, pred_proba)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2024-11-12T05:03:35.339081Z","iopub.execute_input":"2024-11-12T05:03:35.339420Z","iopub.status.idle":"2024-11-12T05:03:35.346034Z","shell.execute_reply.started":"2024-11-12T05:03:35.339383Z","shell.execute_reply":"2024-11-12T05:03:35.344760Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"from lightgbm import LGBMClassifier\n\nlgbm_clf = LGBMClassifier(n_estimators=1000, num_leaves=64, n_jobs=-1, boost_from_average=False)\nget_model_train_eval(lgbm_clf, ftr_train=X_train, ftr_test=X_test, tgt_train=y_train, tgt_test=y_test)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2024-11-12T05:03:35.347477Z","iopub.execute_input":"2024-11-12T05:03:35.348152Z","iopub.status.idle":"2024-11-12T05:03:55.498254Z","shell.execute_reply.started":"2024-11-12T05:03:35.348113Z","shell.execute_reply":"2024-11-12T05:03:55.497038Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"#### 첫 LightGBM 결과\n- 전체적으로 로지스틱 회귀보다 좋은 성능을 보임!\n  - 정확도: 0.9996\n  - 정밀도: 0.9474\n  - 재현율: 0.8333\n  - F1: 0.8867\n  - ROC-AUC:0.9739","metadata":{}},{"cell_type":"markdown","source":"### 랜덤 포레스트","metadata":{}},{"cell_type":"code","source":"from sklearn.ensemble import RandomForestClassifier\nfrom sklearn.metrics import roc_auc_score\n\nrf_clf = RandomForestClassifier(n_estimators=100, random_state=42)\nrf_clf.fit(X_train, y_train)\n\ny_pred = rf_clf.predict(X_test)\ny_pred_proba = rf_clf.predict_proba(X_test)[:, 1]\n\nget_clf_eval(y_test, y_pred, y_pred_proba)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2024-11-12T05:03:55.504491Z","iopub.execute_input":"2024-11-12T05:03:55.505186Z","iopub.status.idle":"2024-11-12T05:05:36.791451Z","shell.execute_reply.started":"2024-11-12T05:03:55.505144Z","shell.execute_reply":"2024-11-12T05:05:36.790289Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"#### 첫 랜덤 포레스트 결과\n- 정확도: 0.9995\n- 정밀도: 0.9255\n- 재현율: 0.8056\n- F1: 0.8614\n- ROC-AUC: 0.9335","metadata":{}},{"cell_type":"markdown","source":"### 첫 모델 학습 결과 정리\n- 모든 지표가 LightGBM > 랜덤 포레스트 > 로지스틱 회귀 순!\n> LightGBM이 불균형 데이터에서 가장 사용하기 좋은 모델임을 증명함","metadata":{}},{"cell_type":"code","source":"from sklearn.metrics import roc_auc_score, accuracy_score, precision_score, recall_score, f1_score\n\n# 각 모델의 성능 지표 계산\nprint(\"로지스틱 회귀 성능:\")\nprint(f\"ROC-AUC: {roc_auc_score(y_test, lr_pred_proba):.4f}\")\nprint(f\"정확도: {accuracy_score(y_test, lr_pred):.4f}\")\nprint(f\"정밀도: {precision_score(y_test, lr_pred):.4f}\")\nprint(f\"재현율: {recall_score(y_test, lr_pred):.4f}\")\nprint(f\"F1: {f1_score(y_test, lr_pred):.4f}\")\n\nprint(\"\\n랜덤 포레스트 성능:\")\nprint(f\"ROC-AUC: {roc_auc_score(y_test, y_pred_proba):.4f}\")\nprint(f\"정확도: {accuracy_score(y_test, y_pred):.4f}\")\nprint(f\"정밀도: {precision_score(y_test, y_pred):.4f}\")\nprint(f\"재현율: {recall_score(y_test, y_pred):.4f}\")\nprint(f\"F1: {f1_score(y_test, y_pred):.4f}\")\n\n\nlgbm_pred = lgbm_clf.predict(X_test)\nlgbm_pred_proba = lgbm_clf.predict_proba(X_test)[:, 1]\n\nprint(\"\\nLightGBM 성능:\")\nprint(f\"ROC-AUC: {roc_auc_score(y_test, lgbm_pred_proba):.4f}\")\nprint(f\"정확도: {accuracy_score(y_test, lgbm_pred):.4f}\")\nprint(f\"정밀도: {precision_score(y_test, lgbm_pred):.4f}\")\nprint(f\"재현율: {recall_score(y_test, lgbm_pred):.4f}\")\nprint(f\"F1: {f1_score(y_test, lgbm_pred):.4f}\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2024-11-12T05:05:36.792818Z","iopub.execute_input":"2024-11-12T05:05:36.793170Z","iopub.status.idle":"2024-11-12T05:05:42.157298Z","shell.execute_reply.started":"2024-11-12T05:05:36.793132Z","shell.execute_reply":"2024-11-12T05:05:42.156089Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"### 첫 모델 학습 결과를 앙상블로 확인해보자\n- Voting Classifier를 사용해서 앙상블을 구성하고, ROC-AUC를 출력\n  - 앙상블 모델의 ROC-AUC : 0.9334","metadata":{}},{"cell_type":"code","source":"from sklearn.ensemble import VotingClassifier\nfrom sklearn.metrics import roc_auc_score\n\nvoting_clf = VotingClassifier(estimators=[\n    ('lr_clf', lr_clf),\n    ('lgbm_clf', lgbm_clf),\n    ('rf_clf', rf_clf)\n], voting='soft')\n\nvoting_clf.fit(X_train, y_train)\n\ny_pred_proba_voting = voting_clf.predict_proba(X_test)[:, 1]\n\nroc_auc_voting = roc_auc_score(y_test, y_pred_proba_voting)\n\nprint(f'Voting Classifier ROC-AUC: {roc_auc_voting:.4f}')","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2024-11-12T05:05:42.159347Z","iopub.execute_input":"2024-11-12T05:05:42.159741Z","iopub.status.idle":"2024-11-12T05:08:03.817463Z","shell.execute_reply.started":"2024-11-12T05:05:42.159702Z","shell.execute_reply":"2024-11-12T05:08:03.816238Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# 데이터 분포도를 변환해서 다시 모델을 학습/예측/평가\n- Amount 피쳐\n  - 1000불 이하의 데이터가 대부분\n  - 26,000불까지 드물지만 많은 금액을 사용한 지표가 있음 -> 꼬리가 긴 분포 곡선을 가지고 있음\n- 로지스틱 회귀\n  - 선형 모델이기 때문에 데이터가 정규 분포 형태를 띄는 것이 모델 성능에 좋음\n  - Amount의 경우도 로지스틱 회귀에서 조금 더 데이터를 잘 받아들일 수 있도록 정규 분포 형태로 변환하기로 결정","metadata":{}},{"cell_type":"code","source":"import seaborn as sns\n\nplt.figure(figsize=(8, 4))\nplt.xticks(range(0, 30000, 1000), rotation=60)\nsns.histplot(train['Amount'], bins=100, kde=True)\nplt.show()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2024-11-12T05:08:17.117227Z","iopub.execute_input":"2024-11-12T05:08:17.117705Z","iopub.status.idle":"2024-11-12T05:08:18.595725Z","shell.execute_reply.started":"2024-11-12T05:08:17.117661Z","shell.execute_reply":"2024-11-12T05:08:18.594605Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"#### standardScaler로 Amount를 정규분포 형태로 변환\n- 변환한 Amount는 Amount_Scaled로 컬럼명 변경\n- 또한, 맨 앞으로 이동\n- 기존의 컬럼인 Time과 Amount는 필요하지 않으므로 삭제","metadata":{}},{"cell_type":"code","source":"from sklearn.preprocessing import StandardScaler\n\ndef get_preprocessed_df(df=None):\n    df_copy = df.copy()\n    scaler = StandardScaler()\n    amount_n = scaler.fit_transform(df_copy['Amount'].values.reshape(-1, 1))\n\n    df_copy.insert(0, 'Amount_Scaled', amount_n)\n\n    df_copy.drop(['Time','Amount'], axis=1, inplace=True)\n    return df_copy","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2024-11-12T05:15:39.109692Z","iopub.execute_input":"2024-11-12T05:15:39.110147Z","iopub.status.idle":"2024-11-12T05:15:39.117986Z","shell.execute_reply.started":"2024-11-12T05:15:39.110106Z","shell.execute_reply":"2024-11-12T05:15:39.116637Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"train.info()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2024-11-12T05:18:50.593465Z","iopub.execute_input":"2024-11-12T05:18:50.594716Z","iopub.status.idle":"2024-11-12T05:18:50.620345Z","shell.execute_reply.started":"2024-11-12T05:18:50.594660Z","shell.execute_reply":"2024-11-12T05:18:50.619117Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"- 로지스틱 회귀, 랜덤 포레스트, LightGBM 재수행 ","metadata":{}},{"cell_type":"code","source":"X_train, X_test, y_train, y_test = get_train_test_dataset(train)\n\nprint('### 로지스틱 회귀 예측 성능 ###')\nlr_clf = LogisticRegression()\nget_model_train_eval(lr_clf, ftr_train=X_train, ftr_test=X_test, tgt_train=y_train, tgt_test=y_test)\n\nprint('### 랜덤 포레스트 예측 성능 ###')\nrf_clf = RandomForestClassifier(n_estimators=100, random_state=42)\nrf_clf.fit(X_train, y_train)\ny_pred = rf_clf.predict(X_test)\ny_pred_proba = rf_clf.predict_proba(X_test)[:, 1]\nget_clf_eval(y_test, y_pred, y_pred_proba)\n\nprint('### LightGBM 예측 성능 ###')\nlgbm_clf = LGBMClassifier(n_estimators=1000, num_leaves=64, n_jobs=-1, boost_from_average=False)\nget_model_train_eval(lgbm_clf, ftr_train=X_train, ftr_test=X_test, tgt_train=y_train, tgt_test=y_test)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2024-11-12T05:19:00.794397Z","iopub.execute_input":"2024-11-12T05:19:00.795573Z","iopub.status.idle":"2024-11-12T05:21:32.062539Z","shell.execute_reply.started":"2024-11-12T05:19:00.795507Z","shell.execute_reply":"2024-11-12T05:21:32.061383Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"- 로지스틱 회귀 : 정확도 하락, 정밀도 하락, 재현율 하락, F1 하락 -> ROC-AUC는 상승!\n- 랜덤 포레스트 : 정확도 하락, 정밀도 하락, 재현율 하락, F1 하락 -> ROC-AUC는 상승!\n- LightGBM : 정확도 -, 정밀도 하락, 재현율 하락, ROC-AUC 하락 -> F1은 상승!\n> 전체적으로 큰 성능 상의 좋아짐은 느끼지 못할 수준으로 변화됨 ","metadata":{}},{"cell_type":"markdown","source":"#### Amount_Scaled를 로그로 변환하자!\n- 위의 과정에서 Amount_log라는 이름으로 로그 변환을 했음(값의 스케일을 줄이기 위함)\n  - 그러나, Amount_Scaled에 동일한 로그 변환값을 넣을 예정이기 때문에 로그 변환 로직 구동 후 -> 기존의 Amount_log 컬럼은 삭제 조치","metadata":{}},{"cell_type":"code","source":"def get_preprocessed_df(df=None):\n    df_copy = df.copy()\n    amount_n = np.log1p(df_copy['Amount'])\n    df_copy.insert(0, 'Amount_Scaled', amount_n)\n    df_copy.drop(['Time','Amount'], axis=1, inplace=True)\n    return df_copy","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2024-11-12T05:25:09.348099Z","iopub.execute_input":"2024-11-12T05:25:09.348586Z","iopub.status.idle":"2024-11-12T05:25:09.355522Z","shell.execute_reply.started":"2024-11-12T05:25:09.348539Z","shell.execute_reply":"2024-11-12T05:25:09.354440Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"train = train.drop(columns=['Amount_log'])","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2024-11-12T05:27:49.259006Z","iopub.execute_input":"2024-11-12T05:27:49.259470Z","iopub.status.idle":"2024-11-12T05:27:49.281673Z","shell.execute_reply.started":"2024-11-12T05:27:49.259425Z","shell.execute_reply":"2024-11-12T05:27:49.280359Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"train.info()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2024-11-12T05:27:55.243366Z","iopub.execute_input":"2024-11-12T05:27:55.243945Z","iopub.status.idle":"2024-11-12T05:27:55.275041Z","shell.execute_reply.started":"2024-11-12T05:27:55.243891Z","shell.execute_reply":"2024-11-12T05:27:55.273682Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"X_train, X_test, y_train, y_test = get_train_test_dataset(train)\n\nprint('### 로지스틱 회귀 예측 성능 ###')\nlr_clf = LogisticRegression()\nget_model_train_eval(lr_clf, ftr_train=X_train, ftr_test=X_test, tgt_train=y_train, tgt_test=y_test)\n\nprint('### 랜덤 포레스트 예측 성능 ###')\nrf_clf = RandomForestClassifier(n_estimators=100, random_state=42)\nrf_clf.fit(X_train, y_train)\ny_pred = rf_clf.predict(X_test)\ny_pred_proba = rf_clf.predict_proba(X_test)[:, 1]\nget_clf_eval(y_test, y_pred, y_pred_proba)\n\nprint('### LightGBM 예측 성능 ###')\nlgbm_clf = LGBMClassifier(n_estimators=1000, num_leaves=64, n_jobs=-1, boost_from_average=False)\nget_model_train_eval(lgbm_clf, ftr_train=X_train, ftr_test=X_test, tgt_train=y_train, tgt_test=y_test)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2024-11-12T05:31:01.286012Z","iopub.execute_input":"2024-11-12T05:31:01.286477Z","iopub.status.idle":"2024-11-12T05:33:29.193231Z","shell.execute_reply.started":"2024-11-12T05:31:01.286437Z","shell.execute_reply":"2024-11-12T05:33:29.191985Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"- 로지스틱 회귀의 모든 수치는 동일\n  - 이를 통해, 해당 모델은 Amount 컬럼의 값에 지대한 영향을 받지는 않는 것으로 판단\n- 랜덤 포레스트의 모든 수치는 상승\n  - Amount의 로그 변환이 성능 향상에 영향을 미쳤다고 볼 수 있음\n- LightGBM의 모든 수치도 상승\n  - 이 역시 이유 동일(가장 많이 상승)\n","metadata":{}},{"cell_type":"markdown","source":"# 이상치 데이터를 제거하고 다시 모델을 학습/예측/평가","metadata":{}},{"cell_type":"code","source":"import seaborn as sns\n\nplt.figure(figsize=(9, 9))\ncorr = train.corr()\nsns.heatmap(corr, cmap='RdBu')","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2024-11-12T05:39:09.348866Z","iopub.execute_input":"2024-11-12T05:39:09.349322Z","iopub.status.idle":"2024-11-12T05:39:10.673252Z","shell.execute_reply.started":"2024-11-12T05:39:09.349279Z","shell.execute_reply":"2024-11-12T05:39:10.672145Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"- Class와의 음의 상관관계 최고치 : V14, V17\n- 이전에 언급했듯, 이상치 제거는 IQR을 이용!\n  - V14에 대한 이상치 제거만 진행!","metadata":{}},{"cell_type":"code","source":"import numpy as np\n\ndef get_outlier(df=None, column=None, weight=1.5):\n    fraud = df[df['Class']==1][column]\n    quantile_25 = np.percentile(fraud.values, 25)\n    quantile_75 = np.percentile(fraud.values, 75)\n    \n    iqr = quantile_75 - quantile_25\n    iqr_weight = iqr * weight\n    lowest_val = quantile_25 - iqr_weight\n    highest_val = quantile_75 + iqr_weight\n\n    outlier_index = fraud[(fraud < lowest_val) | (fraud > highest_val)].index\n    return outlier_index","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2024-11-12T05:41:35.852453Z","iopub.execute_input":"2024-11-12T05:41:35.853514Z","iopub.status.idle":"2024-11-12T05:41:35.861082Z","shell.execute_reply.started":"2024-11-12T05:41:35.853466Z","shell.execute_reply":"2024-11-12T05:41:35.859845Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"outlier_index = get_outlier(df=train, column='V14', weight=1.5)\nprint('이상치 데이터 인덱스:', outlier_index)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2024-11-12T05:41:37.974326Z","iopub.execute_input":"2024-11-12T05:41:37.975052Z","iopub.status.idle":"2024-11-12T05:41:37.984706Z","shell.execute_reply.started":"2024-11-12T05:41:37.975007Z","shell.execute_reply":"2024-11-12T05:41:37.983454Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"- 이상치 추출 및 제거하는 함수 get_processed_df()\n  - 로그 변환 + V14 피쳐 이상치 데이터 삭제 로직으로 변경","metadata":{}},{"cell_type":"code","source":"def get_preprocessed_df(df=None):\n    df_copy = df.copy()\n    amount_n = np.log1p(df_copy['Amount'])\n    df_copy.insert(0, 'Amount_Scaled', amount_n)\n    df_copy.drop(['Time','Amount'], axis=1, inplace=True)\n    outlier_index = get_outlier(df=df_copy, column='V14', weight=1.5)\n    df_copy.drop(outlier_index, axis=0, inplace=True)\n    return df_copy\n\n# 모델 학습/예측/평가\nX_train, X_test, y_train, y_test = get_train_test_dataset(train)\n\nprint('### 로지스틱 회귀 예측 성능 ###')\nlr_clf = LogisticRegression()\nget_model_train_eval(lr_clf, ftr_train=X_train, ftr_test=X_test, tgt_train=y_train, tgt_test=y_test)\n\nprint('### 랜덤 포레스트 예측 성능 ###')\nrf_clf = RandomForestClassifier(n_estimators=100, random_state=42)\nrf_clf.fit(X_train, y_train)\ny_pred = rf_clf.predict(X_test)\ny_pred_proba = rf_clf.predict_proba(X_test)[:, 1]\nget_clf_eval(y_test, y_pred, y_pred_proba)\n\nprint('### LightGBM 예측 성능 ###')\nlgbm_clf = LGBMClassifier(n_estimators=1000, num_leaves=64, n_jobs=-1, boost_from_average=False)\nget_model_train_eval(lgbm_clf, ftr_train=X_train, ftr_test=X_test, tgt_train=y_train, tgt_test=y_test)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2024-11-12T05:44:36.296187Z","iopub.execute_input":"2024-11-12T05:44:36.296620Z","iopub.status.idle":"2024-11-12T05:47:08.491179Z","shell.execute_reply.started":"2024-11-12T05:44:36.296580Z","shell.execute_reply":"2024-11-12T05:47:08.489744Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"- 이상치 제거 이전과 이후의 성능 변화 살펴보기\n  - 로지스틱 회귀 : 정확도 동일, 정밀도 하락, 재현율 상승, F1 하락, ROC-AUC 상승\n  - 랜덤 포레스트 : 정확도 동일, 정밀도 상승, 재현율 상승, F1 상승, ROC-AUC 상승\n  - LightGBM : 정확도 상승, 정밀도 상승, 재현율 상승, F1 상승, ROC-AUC 상승\n\n#### 얻을 수 있는 인사이트\n> - 불균형 데이터에 가장 적합하다고 알려진 LightGBM의 경우가 피쳐 데이터 가공 시에 눈에 띄는 성능 개선이 보임!!","metadata":{}},{"cell_type":"markdown","source":"# SMOTE 오버 샘플링 적용 후 모델 학습/예측 평가\n- 주의 사항 : 학습 데이터 세트만 오버 샘플링 해야 함!\n  - 검증 데이터 세트 또는 테스트 데이터 세트 오버 샘플링을 하게 되면, 결국 원본 데이터 세트가 아닌 데이터를 통해 검증이나 테스트가 수행되기 때문에 올바르게 검증하고 테스트했다고 보기 어려움..!","metadata":{}},{"cell_type":"code","source":"from imblearn.over_sampling import SMOTE\n\nsmote = SMOTE(random_state=0)\nX_train_over, y_train_over = smote.fit_resample(X_train, y_train)\nprint('SMOTE 적용 전 학습용 피처/레이블 데이터 세트: ', X_train.shape, y_train.shape)\nprint('SMOTE 적용 후 학습용 피처/레이블 데이터 세트: ', X_train_over.shape, y_train_over.shape)\nprint('SMOTE 적용 후 레이블 값 분포: \\n', pd.Series(y_train_over).value_counts())","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2024-11-12T05:55:47.877017Z","iopub.execute_input":"2024-11-12T05:55:47.877518Z","iopub.status.idle":"2024-11-12T05:55:48.294648Z","shell.execute_reply.started":"2024-11-12T05:55:47.877470Z","shell.execute_reply":"2024-11-12T05:55:48.293453Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"- 데이터 증식 완료\n- 또한, Class의 0과 1 값이 동일하게 설정 완료됨\n\n<br>","metadata":{}},{"cell_type":"markdown","source":"#### 로지스틱 회귀부터 해보자","metadata":{}},{"cell_type":"code","source":"lr_clf = LogisticRegression()\nget_model_train_eval(lr_clf, ftr_train=X_train_over, ftr_test=X_test, tgt_train=y_train_over, tgt_test=y_test)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2024-11-12T05:59:24.394050Z","iopub.execute_input":"2024-11-12T05:59:24.394906Z","iopub.status.idle":"2024-11-12T05:59:26.045854Z","shell.execute_reply.started":"2024-11-12T05:59:24.394859Z","shell.execute_reply":"2024-11-12T05:59:26.044445Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"- 직전에 비해 정확도, 정밀도, F1이 하락했고, 재현율과 ROC-AUC는 상승\n  - 재현율을 높을지언정 정밀도가 현저히 낮아졌기 때문에 이 결과는 신뢰도가 떨어질 수밖에 없음.\n  - 이 모델이 실제 테스트 데이터 세트에서 예측을 Class=1에 지나치게 적용하면서 정밀도가 떨어진 것","metadata":{}},{"cell_type":"markdown","source":"#### 분류 결정 임곗값에 따른 정밀도와 재현율 곡선으로 문제 확인\n- precision_recall_curve_plot() 함수 사용!","metadata":{}},{"cell_type":"code","source":"import matplotlib.pyplot as plt\nimport matplotlib.ticker as ticker\nfrom sklearn.metrics import precision_recall_curve\n%matplotlib inline\n\ndef precision_recall_curve_plot(y_test , pred_proba_c1):\n    precisions, recalls, thresholds = precision_recall_curve( y_test, pred_proba_c1)\n\n    plt.figure(figsize=(8,6))\n    threshold_boundary = thresholds.shape[0]\n    plt.plot(thresholds, precisions[0:threshold_boundary], linestyle='--', label='precision')\n    plt.plot(thresholds, recalls[0:threshold_boundary],label='recall')\n\n    start, end = plt.xlim()\n    plt.xticks(np.round(np.arange(start, end, 0.1),2))\n\n    plt.xlabel('Threshold value'); plt.ylabel('Precision and Recall value')\n    plt.legend(); plt.grid()\n    plt.show()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2024-11-12T06:03:48.573992Z","iopub.execute_input":"2024-11-12T06:03:48.574459Z","iopub.status.idle":"2024-11-12T06:03:48.588355Z","shell.execute_reply.started":"2024-11-12T06:03:48.574413Z","shell.execute_reply":"2024-11-12T06:03:48.587068Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"precision_recall_curve_plot( y_test, lr_clf.predict_proba(X_test)[:, 1] )","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2024-11-12T06:03:57.158801Z","iopub.execute_input":"2024-11-12T06:03:57.159249Z","iopub.status.idle":"2024-11-12T06:03:57.556250Z","shell.execute_reply.started":"2024-11-12T06:03:57.159205Z","shell.execute_reply":"2024-11-12T06:03:57.555122Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"> 지표 분석\n- 임계값 0.99 이하\n  - 재현율 양호, 정밀도 낮음\n- 임계값 0.99 이상\n  - 재현율 낮아짐 -> 정밀도 급상승\n- 즉, 로지스틱 회귀의 경우 SMOTE로 오버 샘플링하는 것은 적합하지 않음을 결론으로 내릴 수 있음","metadata":{}},{"cell_type":"markdown","source":"#### 랜덤 포레스트","metadata":{}},{"cell_type":"code","source":"rf_clf = RandomForestClassifier(n_estimators=100, random_state=42)\nrf_clf.fit(X_train_over, y_train_over)\ny_pred = rf_clf.predict(X_test)\ny_pred_proba = rf_clf.predict_proba(X_test)[:, 1]\nget_clf_eval(y_test, y_pred, y_pred_proba)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2024-11-12T06:09:05.255387Z","iopub.execute_input":"2024-11-12T06:09:05.256312Z","iopub.status.idle":"2024-11-12T06:12:23.947262Z","shell.execute_reply.started":"2024-11-12T06:09:05.256263Z","shell.execute_reply":"2024-11-12T06:12:23.946138Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"- 직전과 비교했을 경우 정확도 동일, 정밀도 및 재현율 상승, 또한 ROC-AUC 상승\n- F1 하락\n  - 지표를 분석해보면 오버 샘플링을 통해 정확도가 낮아지는 문제가 생기지 않았고, 다른 모든 지표가 대부분 눈에 띌 정도로 상승했기 때문에 SMOTE를 적용하는 것에 의미가 있는 모델임을 알 수 있음!","metadata":{}},{"cell_type":"markdown","source":"#### LightGBM","metadata":{}},{"cell_type":"code","source":"lgbm_clf = LGBMClassifier(n_estimators=1000, num_leaves=64, n_jobs=-1, boost_from_average=False)\nget_model_train_eval(lgbm_clf, ftr_train=X_train_over, ftr_test=X_test,\n                  tgt_train=y_train_over, tgt_test=y_test)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2024-11-12T06:14:45.331167Z","iopub.execute_input":"2024-11-12T06:14:45.331656Z","iopub.status.idle":"2024-11-12T06:16:21.225239Z","shell.execute_reply.started":"2024-11-12T06:14:45.331609Z","shell.execute_reply":"2024-11-12T06:16:21.224089Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"- 직전과 비교했을 때 정확도, 정밀도, F1은 하락\n- 재현율, ROC-AUC는 상승\n  - 이를 통해 SMOTE를 적용하는 경우는 \"재현율이 높고, 정밀도가 낮아도 되는\" 경우라고 정리해볼 수 있음\n  - 모델의 목표가 정밀도가 아닌 \"재현율 지표\"에 있다면 사용 가능할 듯!","metadata":{}},{"cell_type":"markdown","source":"# 앙상블로 현재 ROC-AUC 확인","metadata":{}},{"cell_type":"code","source":"from sklearn.ensemble import VotingClassifier\nfrom sklearn.metrics import roc_auc_score\n\nvoting_clf = VotingClassifier(estimators=[\n    ('lr_clf', lr_clf),\n    ('lgbm_clf', lgbm_clf),\n    ('rf_clf', rf_clf)\n], voting='soft')\n\nvoting_clf.fit(X_train, y_train)\n\ny_pred_proba_voting = voting_clf.predict_proba(X_test)[:, 1]\n\nroc_auc_voting = roc_auc_score(y_test, y_pred_proba_voting)\n\nprint(f'[M] Voting Classifier ROC-AUC: {roc_auc_voting:.4f}')","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2024-11-12T06:22:56.425201Z","iopub.execute_input":"2024-11-12T06:22:56.426093Z","iopub.status.idle":"2024-11-12T06:25:22.029192Z","shell.execute_reply.started":"2024-11-12T06:22:56.426044Z","shell.execute_reply":"2024-11-12T06:25:22.028009Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"### 이전과 비교\n- 첫 모델 학습 시기와 비교하면 이전의 앙상블 모델 ROC-AUC는 0.9334\n- 현재 모델은 **ROC-AUC가 0.9483**으로 상승했음을 확인할 수 있음!\n  - 큰 폭으로 상승했다고 보기에는 다소 어려울 수 있으나, 데이터의 분포도를 확인해서 Amount를 로그로 변환하고, 이상치 데이터를 처리했기 때문에 모델 성능이 상승했다고 판단해볼 수 있음","metadata":{}},{"cell_type":"markdown","source":"# 하이퍼 파라미터 튜닝을 하자","metadata":{}},{"cell_type":"markdown","source":"#### 로지스틱 회귀\n- C를 이용해서 -> 정규화 강도를 높여가고\n- liblinear와 saga 2가지의 최적화 알고리즘을 써서(데이터셋의 크고 작음에 따름) 튜닝해보자.\n- K-폴드 교차 검증으로 진행(5개)\n  - CV를 5개로 지정해서 이전과 어떻게 달라지는지 확인하기","metadata":{}},{"cell_type":"code","source":"# from sklearn.model_selection import GridSearchCV\n\n# param_grid = {\n#     'C': [0.01, 0.1, 1, 10, 100],\n#     'solver': ['liblinear', 'saga']\n# }\n\n\n# lr_clf = LogisticRegression()\n\n# # 하이퍼 파라미터 튜닝\n# grid_search_lr = GridSearchCV(lr_clf, param_grid, cv=5, scoring='roc_auc', n_jobs=-1)\n# grid_search_lr.fit(X_train, y_train)\n\n# # 최적의 하이퍼파라미터가 무엇인지 출력\n# print(\"Best parameters for Logistic Regression:\", grid_search_lr.best_params_)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2024-11-12T06:32:11.267536Z","iopub.execute_input":"2024-11-12T06:32:11.268129Z","iopub.status.idle":"2024-11-12T06:36:39.555003Z","shell.execute_reply.started":"2024-11-12T06:32:11.268069Z","shell.execute_reply":"2024-11-12T06:36:39.553855Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"#### 랜덤 포레스트\n- 시간이 오래걸리는 문제가 있기 때문에 RandomizedSearchCV를 이용해 탐색!\n- 트리 개수, 트리의 max_depth, min_samples_split를 이용한 튜닝 진행\n\n> 이렇게 했음에도 불구하고 시간이 과도하게 오래 소요되는 문제가 있어 임의의 값으로 진행\n> - Best parameters for Random Forest: {'n_estimators': 100, 'max_depth': 10, 'min_samples_split': 2}로 임의 설정","metadata":{}},{"cell_type":"code","source":"# from sklearn.model_selection import RandomizedSearchCV\n\n# param_dist_rf = {\n#     'n_estimators': [100, 200],\n#     'max_depth': [10, 20],\n#     'min_samples_split': [2, 5]\n# }\n\n# rf_clf = RandomForestClassifier(random_state=42)\n\n# random_search_rf = RandomizedSearchCV(rf_clf, param_distributions=param_dist_rf, \n#                                       n_iter=10, cv=3, scoring='roc_auc', n_jobs=-1, random_state=42)\n# random_search_rf.fit(X_train, y_train)\n\n# print(\"Best parameters for Random Forest:\", random_search_rf.best_params_)","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"#### LightGBM\n- 시간이 오래걸리는 문제가 있기 때문에 RandomizedSearchCV를 이용해 탐색!\n- 리프 노드의 경우, 너무 많으면 과적합이 발생할 수 있으니, 적당하게 설정!\n- 학습률도 비슷.\n- 트리 개수와 max_depth도 지정","metadata":{}},{"cell_type":"code","source":"# from sklearn.model_selection import RandomizedSearchCV\n\n# param_grid_lgbm = {\n#     'num_leaves': [31, 64],\n#     'learning_rate': [0.01, 0.05],\n#     'n_estimators': [100, 200],\n#     'max_depth': [5, 10]\n# }\n\n# lgbm_clf = LGBMClassifier(random_state=42)\n\n# grid_search_lgbm = RandomizedSearchCV(lgbm_clf, param_grid_lgbm, cv=5, scoring='roc_auc', n_jobs=-1)\n# grid_search_lgbm.fit(X_train, y_train)\n\n# print(\"Best parameters for LightGBM:\", grid_search_lgbm.best_params_)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2024-11-12T07:11:04.861224Z","iopub.execute_input":"2024-11-12T07:11:04.861734Z","iopub.status.idle":"2024-11-12T07:16:09.318875Z","shell.execute_reply.started":"2024-11-12T07:11:04.861689Z","shell.execute_reply":"2024-11-12T07:16:09.317668Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"> ## 주의\n> - 최적 파라미터를 찾아내는 과정을 끝내고 명시할 것이기 때문에, 위의 3개 코드는 주석처리하여 재실행 시 다시 실행되지 않도록 할 예정(시간이 오래걸리므로)","metadata":{}},{"cell_type":"markdown","source":"# 최적 파라미터로 재학습","metadata":{}},{"cell_type":"markdown","source":"#### 로지스틱 회귀\n- 하이퍼 파라미터 튜닝 이전 vs 이후\n  - 정확도 : 0.9582 -> 0.9986 ⬆️\n  - 정밀도 : 0.0427 -> 0.7846 ⬆️\n  - 재현율 : 0.8879 -> 0.4766 ⬇️\n  - F1 : 0.0815 -> 0.5930 ⬆️\n  - ROC-AUC : 0.9627 -> 0.9051 ⬇️","metadata":{}},{"cell_type":"code","source":"# 최적의 하이퍼파라미터\nbest_lr_params = {'C': 0.01, 'solver': 'liblinear'}\nlr_clf_tuned = LogisticRegression(C=best_lr_params['C'], solver=best_lr_params['solver'])\n\nlr_clf_tuned.fit(X_train, y_train)\n\nlr_pred = lr_clf_tuned.predict(X_test)\nlr_pred_proba = lr_clf_tuned.predict_proba(X_test)[:, 1]\n\nget_clf_eval(y_test, lr_pred, lr_pred_proba)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2024-11-12T07:17:32.959537Z","iopub.execute_input":"2024-11-12T07:17:32.960301Z","iopub.status.idle":"2024-11-12T07:17:35.213582Z","shell.execute_reply.started":"2024-11-12T07:17:32.960254Z","shell.execute_reply":"2024-11-12T07:17:35.212460Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"#### 랜덤 포레스트\n- 하이퍼 파라미터 튜닝 이전 vs 이후\n  - 정확도 : 0.9995 -> 0.9995 -\n  - 정밀도 : 0.9100 -> 0.9368 ⬆️\n  - 재현율 : 0.8505 -> 0.8318 ⬇️\n  - F1 : 0.8792 -> 0.8812 ⬆️\n  - ROC-AUC : 0.9657 -> 0.9847 ⬆️","metadata":{}},{"cell_type":"code","source":"# 최적의 하이퍼파라미터\nbest_rf_clf = RandomForestClassifier(n_estimators=100, max_depth=10, min_samples_split=2, random_state=42)\n\nbest_rf_clf.fit(X_train, y_train)\n\ny_pred_rf = best_rf_clf.predict(X_test)\ny_pred_proba_rf = best_rf_clf.predict_proba(X_test)[:, 1]\n\nget_clf_eval(y_test, y_pred_rf, y_pred_proba_rf)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2024-11-12T07:17:40.768979Z","iopub.execute_input":"2024-11-12T07:17:40.769460Z","iopub.status.idle":"2024-11-12T07:19:01.742673Z","shell.execute_reply.started":"2024-11-12T07:17:40.769404Z","shell.execute_reply":"2024-11-12T07:19:01.741540Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"#### LightGBM\n- 하이퍼 파라미터 튜닝 이전 vs 이후\n  - 정확도 : 0.9995 -> 0.9995 -\n  - 정밀도 : 0.8911 -> 0.9175 ⬆️\n  - 재현율 : 0.8411 -> 0.8318 ⬇️\n  - F1 : 0.8654 -> 0.8725 ⬆️\n  - ROC-AUC : 0.9793 -> 0.9819 ⬆️","metadata":{}},{"cell_type":"code","source":"# 최적의 하이퍼파라미터\nbest_lgbm_clf = LGBMClassifier(num_leaves=31, learning_rate=0.01, n_estimators=200, max_depth=5, random_state=42)\n\nbest_lgbm_clf.fit(X_train, y_train)\n\ny_pred_lgb = best_lgbm_clf.predict(X_test)\ny_pred_proba_lgb = best_lgbm_clf.predict_proba(X_test)[:, 1]\n\nget_clf_eval(y_test, y_pred_lgb, y_pred_proba_lgb)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2024-11-12T07:19:05.375927Z","iopub.execute_input":"2024-11-12T07:19:05.376343Z","iopub.status.idle":"2024-11-12T07:19:13.891306Z","shell.execute_reply.started":"2024-11-12T07:19:05.376301Z","shell.execute_reply":"2024-11-12T07:19:13.890183Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"### 결과 분석\n- ROC-AUC를 기준으로 봤을 경우, 랜덤 포레스트 > LightGBM > 로지스틱 회귀 순\n  - 해당 데이터는 불균형 데이터이기 때문에 그 형태에 조금 더 적합한 LightGBM을 사용하거나, 수치상으로 Best인 랜덤 포레스트를 사용할 수도 있을 것으로 결론\n  - 다만, 랜덤 포레스트의 경우, 설정값에 따라 아주 오랜 시간이 소요될 수 있어 주의 요망","metadata":{}},{"cell_type":"markdown","source":"# 앙상블로 최종 ROC-AUC 확인","metadata":{}},{"cell_type":"code","source":"from sklearn.ensemble import VotingClassifier\nfrom sklearn.metrics import roc_auc_score\n\nvoting_clf = VotingClassifier(estimators=[\n    ('lr_clf', lr_clf),\n    ('lgbm_clf', lgbm_clf),\n    ('rf_clf', rf_clf)\n], voting='soft')\n\nvoting_clf.fit(X_train, y_train)\n\ny_pred_proba_voting = voting_clf.predict_proba(X_test)[:, 1]\n\nroc_auc_voting = roc_auc_score(y_test, y_pred_proba_voting)\n\nprint(f'[Final] Voting Classifier ROC-AUC: {roc_auc_voting:.4f}')","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2024-11-12T07:24:52.484435Z","iopub.execute_input":"2024-11-12T07:24:52.484931Z","iopub.status.idle":"2024-11-12T07:26:43.847942Z","shell.execute_reply.started":"2024-11-12T07:24:52.484868Z","shell.execute_reply":"2024-11-12T07:26:43.846668Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"### 앙상블 결과 비교(ROC-AUC)\n- First : 0.9334\n- Second : 0.9483\n- Final : 0.9431\n\n> 대체적으로 약 0.93에서 약 0.94까지 성능 지표를 올린 것으로 결론!\n\n<br>\n\n### 최종 결과 분석\n- 하이퍼 파라미터 튜닝 전 : LightGBM > 랜덤 포레스트 > 로지스틱 회귀\n- 하이퍼 파리미터 튜닝 후 : 랜덤 포레스트 > LightGBM > 로직스틱 회귀\n\n> #### 즉, 이번 신용카드 사기 검출 데이터에서 가장 적합한 모델은 랜덤 포레스트 또는 LightGBM이다!!!\n> - 불균형 데이터이기 때문에 LightGBM으로 선정!","metadata":{}},{"cell_type":"markdown","source":"# 결과 제출 파일 생성","metadata":{}},{"cell_type":"code","source":"# 최적의 하이퍼파라미터\nbest_lgbm_clf = LGBMClassifier(num_leaves=31, learning_rate=0.01, n_estimators=200, max_depth=5, random_state=42)\n\nbest_lgbm_clf.fit(X_train, y_train)\n\ny_pred_lgb = best_lgbm_clf.predict(X_test)\ny_pred_proba_lgb = best_lgbm_clf.predict_proba(X_test)[:, 1]\n\nget_clf_eval(y_test, y_pred_lgb, y_pred_proba_lgb)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2024-11-12T07:50:10.819612Z","iopub.execute_input":"2024-11-12T07:50:10.820895Z","iopub.status.idle":"2024-11-12T07:50:19.420664Z","shell.execute_reply.started":"2024-11-12T07:50:10.820841Z","shell.execute_reply":"2024-11-12T07:50:19.419360Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"y_pred_lgb = best_lgbm_clf.predict(X_test)\ny_pred_proba_lgb = best_lgbm_clf.predict_proba(X_test)[:, 1]\n\nget_clf_eval(y_test, y_pred_lgb, y_pred_proba_lgb)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2024-11-12T07:50:30.489768Z","iopub.execute_input":"2024-11-12T07:50:30.490205Z","iopub.status.idle":"2024-11-12T07:50:31.240175Z","shell.execute_reply.started":"2024-11-12T07:50:30.490161Z","shell.execute_reply":"2024-11-12T07:50:31.239090Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# 'Amount_Scaled' 열을 train 데이터에서 계산한 방식대로 test 데이터에 추가\ntest['Amount_Scaled'] = np.log1p(test['Amount'])\n\n# 학습 데이터에서 사용한 열만 선택\ntrain_columns = X_train.columns\n\n# test 데이터에서 train 데이터에 있는 피처만 선택\ntest_selected = test[train_columns]\n\n# 테스트 데이터에 대해 확률 예측\ny_pred_proba_test = best_lgbm_clf.predict_proba(test_selected)[:, 1]  # 두 번째 클래스의 확률값 추출\n\n# 제출 파일 생성 (id 열과 예측값을 포함)\nsubmission = pd.DataFrame({'id': test['id'], 'Class': y_pred_proba_test})\n\n# 제출 파일을 CSV로 저장\nsubmission.to_csv('submission.csv', index=False)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2024-11-12T08:01:30.076886Z","iopub.execute_input":"2024-11-12T08:01:30.077352Z","iopub.status.idle":"2024-11-12T08:01:31.220662Z","shell.execute_reply.started":"2024-11-12T08:01:30.077305Z","shell.execute_reply":"2024-11-12T08:01:31.219700Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"submission","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2024-11-12T08:01:42.347099Z","iopub.execute_input":"2024-11-12T08:01:42.347710Z","iopub.status.idle":"2024-11-12T08:01:42.360788Z","shell.execute_reply.started":"2024-11-12T08:01:42.347595Z","shell.execute_reply":"2024-11-12T08:01:42.359608Z"}},"outputs":[],"execution_count":null}]}