{"metadata":{"kernelspec":{"name":"python3","display_name":"Python 3","language":"python"},"language_info":{"name":"python","version":"3.10.12","mimetype":"text/x-python","codemirror_mode":{"name":"ipython","version":3},"pygments_lexer":"ipython3","nbconvert_exporter":"python","file_extension":".py"},"kaggle":{"accelerator":"gpu","dataSources":[{"sourceId":19991,"databundleVersionId":1117522,"sourceType":"competition"}],"dockerImageVersionId":30918,"isInternetEnabled":true,"language":"python","sourceType":"notebook","isGpuEnabled":true}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"markdown","source":"<center><h1>\n    <b>Ẩn dữ liệu và chia sẽ thông tin - CQ2021/21</b><br>\n    <b>Đồ án môn học</b><br>\n    <b>ALASKA2 Phân tích ẩn hình ảnh</b><br>\n    <font size = \"3px\">\n        Phát hiện dữ liệu bí mật ẩn trong hình ảnh kỹ thuật số<br>\n        Nhóm 4\n    </font>\n</h1></center>\n\n---\n\n## Thành viên\n\n|**MSSV**|**Tên**|**Công việc**|**Đóng góp**|\n|-|:-:|-|:-:|\n|21120348|Nguyễn Trần Trình|- Tìm hiểu về mô hình phân loại<br>- Đánh giá metrics (AUC, F1-Score)<br>- Phân chia train/test<br>- Xử lý mất cân bằng lớp<br>- Triển khai `batch_preprocess`<br>- Xử lý augmentation<br>- Compile mô hình (Adam, loss function)<br>- Cài đặt class weights<br>- Triển khai early stopping<br>- Giám sát quá trình training<br>- Xác định nguyên nhân AUC thấp<br>- Đề xuất cải tiến|50%|\n|21120441|Dương Huỳnh Anh Duy|- Tìm hiểu về steganalysis<br>- Phương pháp trích xuất đặc trưng (GLCM, CNN)<br> - Tạo dataset<br>- Xử lý ảnh (resize, định dạng)<br>- Triển khai `parallel_feature_extraction`<br>- Quản lý cache đặc trưng<br>- Thiết kế kiến trúc MLP<br>- Thêm Dropout<br>- Tinh chỉnh learning rate<br>- Điều chỉnh batch size<br> - Tổng hợp kết quả<br>- Viết báo cáo kỹ thuật|50%|\n\n---\n\n## 1. Giới thiệu\n\nĐể chống lại tội phạm sử dụng tin nhắn ẩn, các cơ quan thực thi pháp luật cần các phương pháp tiên tiến hơn. Cộng đồng khoa học dữ liệu và các nhà nghiên cứu có thể đóng góp bằng cách phát triển các kỹ thuật phát hiện tự động, chính xác hơn. Điều này sẽ giúp truy bắt tội phạm hiệu quả hơn, ngay cả khi chúng cố gắng che giấu thông tin.\n\nMột cuộc thi được tổ chức để thúc đẩy sự phát triển của các phương pháp này. Thí sinh sẽ tạo ra các thuật toán có khả năng phát hiện dữ liệu bí mật ẩn trong hình ảnh kỹ thuật số, vốn thường được xem là vô hại. Các hình ảnh này được thu thập từ nhiều loại máy ảnh khác nhau và được xử lý theo nhiều cách, tạo ra một bộ dữ liệu đa dạng và thách thức. Yêu cầu đặt ra là các thuật toán phải có độ chính xác cao và tỷ lệ sai sót thấp nhất có thể.\n\n---\n\n## 2. Chi tiết bài toán\n\n**Mục tiêu:**\n- Xây dựng một mô hình máy học có thể dự đoán xác suất một bức ảnh chứa dữ liệu ẩn.\n- Phân biệt giữa ảnh gốc và ảnh đã bị nhúng dữ liệu ẩn.\n\n**Dữ liệu:**\n- Tập dữ liệu bao gồm các bức ảnh JPEG.\n- Một số bức ảnh trong tập dữ liệu đã được nhúng dữ liệu ẩn bằng các thuật toán giấu tin (steganography) khác nhau.\n- Tập dữ liệu được chia thành tập huấn luyện (training set) và tập kiểm tra (test set).\n- Tập kiểm tra sẽ có những đặc điểm sau:\n    - Mỗi thuật toán nhúng được sử dụng với xác suất như nhau.\n    - Payload (độ dài tin nhắn) được điều chỉnh sao cho \"độ khó\" xấp xỉ như nhau bất kể nội dung của ảnh. Ảnh có nội dung mượt mà được sử dụng để ẩn tin nhắn ngắn hơn, trong khi ảnh có kết cấu cao sẽ được sử dụng để ẩn nhiều bit bí mật hơn. Payload được điều chỉnh theo cách tương tự cho bộ kiểm tra và huấn luyện.\n    - Độ dài tin nhắn trung bình là 0,4 bit trên hệ số AC DCT khác 0.\n    - Tất cả các hình ảnh được nén với một trong ba hệ số chất lượng JPEG sau: 95, 90 hoặc 75.\n- Dữ liệu ảnh được lấy từ rất nhiều nguồn khác nhau, từ điện thoại thông minh đến máy ảnh chuyên nghiệp.\n\n**Đánh giá:**\n- Kết quả được đánh giá bằng độ đo AUC (Area Under the ROC Curve).\n\n**Thử thách:**\n- Các thuật toán giấu tin ngày càng tinh vi, khiến việc phát hiện dữ liệu ẩn trở nên khó khăn.\n- Sự đa dạng của các thiết bị chụp ảnh và quá trình xử lý ảnh tạo ra nhiều biến thể, gây khó khăn cho việc xây dựng một mô hình tổng quát.\n- Cần tạo ra các thuật toán phát hiện mạnh mẽ với tỷ lệ dương tính giả tối thiểu.\n\n**Tầm quan trọng:**\n- Phân tích dấu vết ẩn đóng vai trò quan trọng trong việc điều tra tội phạm mạng và bảo vệ an ninh thông tin.\n- Việc phát triển các phương pháp phát hiện chính xác hơn có thể giúp lực lượng thực thi pháp luật chống lại tội phạm sử dụng tin nhắn ẩn.","metadata":{}},{"cell_type":"markdown","source":"## 3. Các thư viện cần thiết","metadata":{}},{"cell_type":"code","source":"import os\nimport cv2\nimport numpy as np\nimport pandas as pd\nimport tensorflow as tf\n\nfrom tqdm import tqdm\nfrom joblib import Parallel, delayed\n\nfrom sklearn.metrics import roc_auc_score, f1_score, accuracy_score\nfrom sklearn.model_selection import train_test_split\n\nfrom tensorflow.keras.models import Model\nfrom tensorflow.keras.optimizers import Adam\nfrom tensorflow.keras.callbacks import EarlyStopping\nfrom tensorflow.keras.applications import EfficientNetB3\nfrom tensorflow.keras.layers import GlobalAveragePooling2D, Dense","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-05-22T00:11:48.581122Z","iopub.execute_input":"2025-05-22T00:11:48.581549Z","iopub.status.idle":"2025-05-22T00:12:01.956148Z","shell.execute_reply.started":"2025-05-22T00:11:48.581507Z","shell.execute_reply":"2025-05-22T00:12:01.955332Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"## 4. Cấu hình hệ thống","metadata":{}},{"cell_type":"code","source":"BASE_PATH = '../input/alaska2-image-steganalysis'\nTEST_PATH = os.path.join(BASE_PATH, 'Test')\nFEATURE_CACHE = '/kaggle/working/features.npy'\nIMG_SIZE = (512, 512)\nBATCH_SIZE = 32\nN_JOBS = 4\nCLASS_WEIGHTS = {0: 1, 1: 3}","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-05-22T00:12:01.957151Z","iopub.execute_input":"2025-05-22T00:12:01.957730Z","iopub.status.idle":"2025-05-22T00:12:01.962085Z","shell.execute_reply.started":"2025-05-22T00:12:01.957684Z","shell.execute_reply":"2025-05-22T00:12:01.960994Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"| Biến cấu hình | Mục đích chính | Giá trị |\n|-|-|-|\n| **`BASE_PATH`** | Đường dẫn đến thư mục gốc chứa dataset | `'../input/...'`|\n| **`TEST_PATH`** | Đường dẫn đến thư mục ảnh test | `os.path.join(BASE_PATH, 'Test')` |\n| **`FEATURE_CACHE`** | Lưu trữ đặc trưng đã trích xuất (dạng file .npy) để tái sử dụng | `'/kaggle/working/features.npy'` |\n| **`IMG_SIZE`** | Kích thước ảnh đầu vào cho mô hình (height, width) | `(512, 512)` |\n| **`BATCH_SIZE`** | Số lượng ảnh xử lý cùng lúc trong 1 batch (tối ưu tốc độ và bộ nhớ GPU) | `32` |\n| **`N_JOBS`** | Số CPU cores dùng cho xử lý song song (Parallel Processing) | `4` |\n| **`CLASS_WEIGHTS`** | Trọng số lớp để xử lý mất cân bằng dữ liệu (class imbalance) | `{0: 1, 1: 3}` |","metadata":{}},{"cell_type":"markdown","source":"## 5. Tiền xử lý dữ liệu","metadata":{}},{"cell_type":"code","source":"def batch_preprocess(image_paths, augment=False):\n    batch = []\n    for path in image_paths:\n        img = cv2.imread(path)\n        if img is None:\n            continue\n            \n        img = cv2.resize(img, IMG_SIZE)\n        \n        if augment:\n            if np.random.rand() > 0.5:\n                img = cv2.flip(img, 1)\n            \n            alpha = np.random.uniform(0.9, 1.1)\n            beta = np.random.uniform(-10, 10)\n            img = cv2.convertScaleAbs(img, alpha=alpha, beta=beta)\n\n        img = tf.keras.applications.efficientnet.preprocess_input(img)\n        batch.append(img)\n    \n    return np.array(batch)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-05-22T00:12:01.962827Z","iopub.execute_input":"2025-05-22T00:12:01.963038Z","iopub.status.idle":"2025-05-22T00:12:01.993755Z","shell.execute_reply.started":"2025-05-22T00:12:01.963019Z","shell.execute_reply":"2025-05-22T00:12:01.993145Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"**Mục đích**  \nXử lý hàng loạt ảnh: Đọc, resize, augment (nếu cần), và chuẩn hóa cho mô hình EfficientNet.\n\n---\n\n**Các bước chính**  \n1. **Đọc ảnh**:  \n   - Sử dụng OpenCV (`cv2.imread`).  \n   - Bỏ qua ảnh lỗi.  \n\n2. **Resize**:  \n   - Chuẩn kích thước về `IMG_SIZE` (VD: 512x512).  \n\n3. **Augmentation** (khi `augment=True`):  \n   - **Flip ngang**: 50% cơ hội.  \n   - **Điều chỉnh sáng/tương phản**:  \n     - `alpha` (0.9-1.1): Độ sáng.  \n     - `beta` (-10 đến +10): Độ tương phản.  \n\n4. **Chuẩn hóa**:  \n   - Áp dụng `EfficientNet` preprocessing để scale giá trị pixel.  \n\n5. **Gom batch**:  \n   - Trả về mảng NumPy các ảnh đã xử lý.  \n\n---\n\n**Tham số ảnh hưởng**  \n\n| Tham số     | Ảnh hưởng                                   |\n|-------------|---------------------------------------------|\n| `IMG_SIZE`  | Chất lượng đặc trưng & tốc độ xử lý         |\n| `BATCH_SIZE`| Bộ nhớ GPU và tốc độ                        |\n| `augment`   | Tăng độ đa dạng dữ liệu (dùng khi train)    |","metadata":{}},{"cell_type":"markdown","source":"## 6. Khởi tạo mô hình","metadata":{}},{"cell_type":"code","source":"def init_feature_extractor():    \n    base_model = EfficientNetB3(\n        weights='imagenet',\n        include_top=False,\n        input_shape=(*IMG_SIZE, 3)\n    )\n    \n    for layer in base_model.layers[:-4]:\n        layer.trainable = False\n    \n    x = base_model.output\n    x = GlobalAveragePooling2D()(x)\n    x = Dense(512, activation='relu')(x)\n    return Model(inputs=base_model.input, outputs=x)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-05-22T00:12:01.995671Z","iopub.execute_input":"2025-05-22T00:12:01.995883Z","iopub.status.idle":"2025-05-22T00:12:02.012311Z","shell.execute_reply.started":"2025-05-22T00:12:01.995865Z","shell.execute_reply":"2025-05-22T00:12:02.011651Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"\n### Giải thích hàm `init_feature_extractor`\n\nHàm `init_feature_extractor` dùng để khởi tạo một mô hình trích xuất đặc trưng dựa trên mô hình **EfficientNetB3** đã được huấn luyện trước trên tập dữ liệu **ImageNet**.\n\n#### Chi tiết từng bước:\n\n1. **Khởi tạo mô hình gốc**:\n   ```python\n   base_model = EfficientNetB3(\n       weights='imagenet',\n       include_top=False,\n       input_shape=(*IMG_SIZE, 3)\n   )\n   ```\n   - Sử dụng kiến trúc EfficientNetB3 với trọng số đã được huấn luyện từ trước trên ImageNet.\n   - `include_top=False` nghĩa là không bao gồm phần phân loại cuối cùng của mô hình gốc.\n   - `input_shape=(*IMG_SIZE, 3)` chỉ định kích thước đầu vào ảnh, ví dụ `(300, 300, 3)` nếu `IMG_SIZE = (300, 300)`.\n\n2. **Đóng băng các lớp**:\n   ```python\n   for layer in base_model.layers[:-4]:\n       layer.trainable = False\n   ```\n   - Đóng băng tất cả các lớp ngoại trừ 4 lớp cuối cùng, để tránh cập nhật trọng số trong quá trình huấn luyện.\n   - Điều này giúp tận dụng tri thức đã học từ ImageNet và giảm thời gian huấn luyện.\n\n3. **Thêm các lớp trích xuất đặc trưng**:\n   ```python\n   x = base_model.output\n   x = GlobalAveragePooling2D()(x)\n   x = Dense(512, activation='relu')(x)\n   ```\n   - `GlobalAveragePooling2D`: tổng hợp thông tin không gian thành vector đặc trưng 1 chiều.\n   - `Dense(512, activation='relu')`: thêm lớp fully connected với 512 nút và hàm kích hoạt ReLU để học thêm đặc trưng phi tuyến.\n\n4. **Tạo mô hình đầu ra**:\n   ```python\n   return Model(inputs=base_model.input, outputs=x)\n   ```\n   - Trả về mô hình mới nhận đầu vào ảnh và xuất ra vector đặc trưng 512 chiều.\n\n#### Mục đích:\nMô hình này thường được sử dụng làm **bộ trích xuất đặc trưng** trong các tác vụ như phân loại ảnh, tìm kiếm ảnh tương tự, hoặc làm bước đầu trong pipeline học sâu.","metadata":{}},{"cell_type":"markdown","source":"## 7. Trích xuất đặc trưng","metadata":{}},{"cell_type":"code","source":"def parallel_feature_extraction(paths, model):\n    features = []\n    for i in tqdm(range(0, len(paths), BATCH_SIZE)):\n        batch_paths = paths[i:i+BATCH_SIZE]\n        batch_images = batch_preprocess(batch_paths, augment=True)\n        batch_features = model.predict(batch_images, verbose=0)\n        features.extend(batch_features)\n    return np.array(features)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-05-22T00:12:02.013215Z","iopub.execute_input":"2025-05-22T00:12:02.013459Z","iopub.status.idle":"2025-05-22T00:12:02.028480Z","shell.execute_reply.started":"2025-05-22T00:12:02.013425Z","shell.execute_reply":"2025-05-22T00:12:02.027818Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"**Mục đích**  \nTrích xuất đặc trưng từ ảnh hàng loạt qua mô hình CNN, tối ưu hóa bằng xử lý theo batch.\n\n---\n\n**Luồng xử lý**  \n1. **Chia Batch**:  \n   - Chia danh sách ảnh thành các batch kích thước `BATCH_SIZE`.  \n   - Hiển thị progress bar bằng `tqdm`.  \n\n2. **Tiền Xử Lý**:  \n   - Gọi `batch_preprocess` để resize, augment (nếu `augment=True`), và chuẩn hóa ảnh.  \n\n3. **Trích Xuất Đặc Trưng**:  \n   - Dùng `model.predict()` để lấy đặc trưng từ batch ảnh.  \n\n4. **Gom Kết Quả**:  \n   - Trả về mảng NumPy chứa đặc trưng của tất cả ảnh.\n\n---\n\n**Tham số chính**  \n\n| Tham số | Ảnh hưởng |  \n|---------|------------|  \n| `BATCH_SIZE` | Batch lớn → Tốc độ nhanh hơn, nhưng tốn RAM/GPU. |  \n| `augment=True` | Tăng đa dạng dữ liệu (dùng khi train). |  ","metadata":{}},{"cell_type":"markdown","source":"## 8. Tạo dataset và trích xuất đặc trưng","metadata":{}},{"cell_type":"code","source":"def create_dataset():\n    data = []\n    for folder in ['Cover', 'JMiPOD', 'JUNIWARD', 'UERD']:\n        path = os.path.join(BASE_PATH, folder)\n        files = [f for f in os.listdir(path) if f.endswith('.jpg')]\n        files = files[:5000]\n        labels = [0 if folder == 'Cover' else 1] * len(files)\n        data.extend(zip([os.path.join(path, f) for f in files], labels))\n    return pd.DataFrame(data, columns=['path', 'label']).sample(frac=1)\n\n# Khởi tạo dataset\ndf = create_dataset()\n\nif os.path.exists(FEATURE_CACHE):\n    X = np.load(FEATURE_CACHE)\nelse:\n    model = init_feature_extractor()\n    X = parallel_feature_extraction(df['path'].values, model)\n    np.save(FEATURE_CACHE, X)\n\ny = df['label'].values","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-05-22T00:12:02.029182Z","iopub.execute_input":"2025-05-22T00:12:02.029448Z","iopub.status.idle":"2025-05-22T00:19:21.752608Z","shell.execute_reply.started":"2025-05-22T00:12:02.029421Z","shell.execute_reply":"2025-05-22T00:19:21.751725Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"**1. Tạo Dataset (`create_dataset()`)**\n- **Mục đích**: Tạo DataFrame chứa đường dẫn ảnh và nhãn.\n- **Chi tiết**:\n  - Duyệt qua 4 thư mục: `Cover` (label **0**), `JMiPOD`, `JUNIWARD`, `UERD` (label **1**).\n  - Lọc file `.jpg` và gán nhãn tự động.\n  - Xáo trộn dữ liệu (`.sample(frac=1)`).\n\n**2. Trích xuất đặc trưng**\n- **Logic**:\n  - **Nếu có cache** (`features.npy`): Load đặc trưng đã lưu → `X`.\n  - **Nếu không**: \n    1. Khởi tạo mô hình CNN (`init_feature_extractor()`).\n    2. Trích xuất đặc trưng hàng loạt (`parallel_feature_extraction()`).\n    3. Lưu đặc trưng vào cache để tái sử dụng.\n- **Kết quả**:\n  - `X`: Mảng đặc trưng ảnh (shape: `(số_ảnh, số_đặc_trưng`).\n  - `y`: Mảng nhãn (shape: `(số_ảnh,)`).\n\n**3. Lưu ý chính**\n\n| Thành Phần | Mô tả |\n|---------------------|----------------------------------------------------------------------|\n| **Label**           | Binary: 0 (Cover), 1 (Stego). Mất cân bằng lớp tiềm ẩn.             |\n| **Shuffle**         | Tránh dữ liệu sắp xếp theo thứ tự lớp.                              |\n| **Cache Features**  | Tiết kiệm thời gian khi chạy lại code. Xóa cache nếu đổi mô hình.   |","metadata":{}},{"cell_type":"markdown","source":"## 9. Huấn luyện mô hình","metadata":{}},{"cell_type":"code","source":"X_train, X_test, y_train, y_test = train_test_split(\n    X, y, \n    test_size=0.2, \n    stratify=y,\n    random_state=42\n)\n\nclassifier = tf.keras.Sequential([\n    tf.keras.layers.Input(shape=(X_train.shape[1],)),\n    tf.keras.layers.Dense(256, activation='relu'),\n    tf.keras.layers.Dropout(0.5),\n    tf.keras.layers.Dense(1, activation='sigmoid')\n])\n\nclassifier.compile(\n    optimizer=Adam(1e-4),\n    loss='binary_crossentropy',\n    metrics=[\n        tf.keras.metrics.AUC(name='auc'),\n        'accuracy'\n    ]\n)\n\nearly_stop = EarlyStopping(\n    monitor='val_auc',\n    patience=5,\n    verbose=1,\n    restore_best_weights=True\n)\n\nhistory = classifier.fit(\n    X_train, y_train,\n    validation_data=(X_test, y_test),\n    epochs=50,\n    batch_size=256,\n    class_weight=CLASS_WEIGHTS,\n    callbacks=[early_stop],\n    verbose=1\n)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-05-22T00:19:21.753456Z","iopub.execute_input":"2025-05-22T00:19:21.753712Z","iopub.status.idle":"2025-05-22T00:19:27.442124Z","shell.execute_reply.started":"2025-05-22T00:19:21.753690Z","shell.execute_reply":"2025-05-22T00:19:27.441474Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"**Chia dữ liệu**\n\n| Tham số | Giá trị | Mục đích |\n|-|-|-|\n| `test_size`   | 0.2 (20%) | Tách 20% dữ liệu làm test |\n| `stratify`    | `y`       | Giữ nguyên tỷ lệ lớp      |\n| `random_state`| 42        | Đảm bảo tái lập kết quả   |\n\n---\n\n**Kiến trúc mô hình**  \n```python\nSequential([\n    Input(shape=(features_dim,)),  # Lớp đầu vào\n    Dense(256, activation='relu'), # Lớp ẩn (256 neuron)\n    Dropout(0.5),                  # Giảm overfitting\n    Dense(1, activation='sigmoid') # Lớp đầu ra (phân loại nhị phân)\n])","metadata":{}},{"cell_type":"markdown","source":"## 10. Đánh giá mô hình","metadata":{}},{"cell_type":"code","source":"y_pred = classifier.predict(X_test)\nprint(f\"\\nĐánh giá cuối cùng:\")\nprint(f\"- AUC: {roc_auc_score(y_test, y_pred):.4f}\")\nprint(f\"- Accuracy: {accuracy_score(y_test, (y_pred > 0.5).astype(int)):.4f}\")\nprint(f\"- F1-Score: {f1_score(y_test, (y_pred > 0.5).astype(int)):.4f}\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-05-22T00:19:27.443118Z","iopub.execute_input":"2025-05-22T00:19:27.443410Z","iopub.status.idle":"2025-05-22T00:19:27.872678Z","shell.execute_reply.started":"2025-05-22T00:19:27.443386Z","shell.execute_reply":"2025-05-22T00:19:27.871650Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"## 11. Dự đoán trên tập test","metadata":{}},{"cell_type":"code","source":"test_paths = [os.path.join(TEST_PATH, f) for f in os.listdir(TEST_PATH)]\ntest_features = parallel_feature_extraction(test_paths, model)\n\nsubmission = pd.DataFrame({\n    'Id': [os.path.basename(p) for p in test_paths],\n    'Label': classifier.predict(test_features).flatten()\n})\nsubmission.to_csv('submission.csv', index=False)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-05-22T00:19:27.873698Z","iopub.execute_input":"2025-05-22T00:19:27.874061Z","iopub.status.idle":"2025-05-22T00:21:21.857726Z","shell.execute_reply.started":"2025-05-22T00:19:27.874023Z","shell.execute_reply":"2025-05-22T00:21:21.856997Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"## 12. Kết luận\n\n### **1. Kết quả đạt được**\n- **AUC**: ~0.583 → Mô hình phân biệt kém giữa các lớp, và cần cải thiện thêm.\n- **Accuracy**: 0.75 → Cao nhưng có thể do mất cân bằng lớp (lớp 0 chiếm đa số).\n- **F1-Score**: 0.8571 → Phản ánh Precision/Recall cao cho lớp đa số, nhưng lớp thiểu số bị bỏ qua.\n\n→ **Nguyên nhân chính**: Mô hình học theo lớp đa số (Cover) và dự đoán thiên lệch.\n\n---\n\n### **2. Khó khăn gặp phải**\n\n| Vấn Đề | Mô Tả |  \n|--------|-------|  \n| **Mất cân bằng lớp** | Lớp Cover (0) chiếm đa số, lớp Stego (1) ít → Mô hình học lệch. |  \n| **Chất lượng đặc trưng** | Đặc trưng trích xuất chưa phân biệt tốt giữa ảnh gốc và ảnh giấu tin. |  \n| **Kiến trúc mô hình** | MLP đơn giản không đủ sức học pattern phức tạp. |  \n| **Hyperparameter** | Learning rate, class weight chưa tối ưu. |  \n\n---\n\n### **3. Bài học rút ra**\n1. **Đánh giá đúng metrics**:  \n   - AUC quan trọng hơn Accuracy trong bài toán mất cân bằng.  \n   - Cần tính F1-Score cho từng lớp riêng (bằng `classification_report`).  \n\n2. **Xử lý mất cân bằng lớp**:  \n   - Cân bằng dataset bằng oversampling/undersampling.  \n   - Thử nghiệm **Focal Loss** thay vì class weights.  \n\n3. **Cải thiện đặc trưng**:  \n   - Dùng mô hình pretrained (ResNet, ViT) thay vì EfficientNet.  \n   - Kết hợp đặc trưng thủ công (như DCT coefficients).  \n\n4. **Kiến trúc mô hình**:  \n   - Chuyển sang CNN hoặc Transformer thay vì MLP.  \n   - Thêm các lớp BatchNorm/Regularization.  \n\n---\n\n### **4. Hướng phát triển**\n\n| Hướng Cải Tiến | Mô Tả |  \n|----------------|-------|  \n| **Cân bằng dữ liệu** | Dùng SMOTE, ADASYN hoặc cắt giảm lớp đa số. |  \n| **Mô hình mạnh hơn** | Thử nghiệm ConvNeXt, Vision Transformer. |  \n| **Tuning hyperparameter** | Dùng Optuna/Bayesian Optimization. |  \n| **Ensemble Learning** | Kết hợp dự đoán từ nhiều mô hình. |  \n| **Sử dụng metadata** | Khai thác thông tin EXIF, chất lượng ảnh. |  ","metadata":{}},{"cell_type":"code","source":"","metadata":{"trusted":true},"outputs":[],"execution_count":null}]}