File size: 12,706 Bytes
04dde81
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
47542cf
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
e878aee
47542cf
 
e878aee
 
 
 
 
 
 
 
 
47542cf
 
e878aee
 
 
 
 
 
 
 
 
 
 
47542cf
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
8af33ff
47542cf
8af33ff
47542cf
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
---
license: apache-2.0
library_name: pytorch
pipeline_tag: image-classification
tags:
  - efficientnet
  - cbam
  - diabetic-retinopathy
  - medical-imaging
  - retinal-fundus
  - classification
  - attention-mechanism
datasets:
  - eyepacs
  - aptos2019-blindness-detection
  - messidor-2
metrics:
  - accuracy
  - f1
  - roc_auc
language:
  - vi
  - en
model-index:
  - name: EfficientNet-B4 + CBAM (Diabetic Retinopathy)
    results:
      - task:
          type: image-classification
          name: Diabetic Retinopathy Classification
        metrics:
          - type: accuracy
            name: Quadratic Weighted Kappa
            value: 0.8542
          - type: f1
            name: Macro F1-Score
            value: 0.7215
---

# Cấu trúc Mô hình AI — EfficientNet-B4 + CBAM (Diabetic Retinopathy Classification)

Tài liệu chi tiết về kiến trúc mô hình AI, cấu hình huấn luyện, dòng chảy dữ liệu (data flow) và hướng dẫn đóng gói suy luận (inference packaging) cho bài toán **Phân loại Mức độ Bệnh Võng mạc Tiểu đường (DR - 5 lớp ICDR)**.

---

## 1. Tổng quan Mô hình

| Thông số | Giá trị |
|---|---|
| **Tên mô hình** | `EfficientNetB4_CBAM` |
| **Bài toán** | Phân loại 5 mức độ bệnh Võng mạc Tiểu đường (ICDR Standard) |
| **Backbone** | `EfficientNet-B4` (Pretrained ImageNet) |
| **Cơ chế Chú ý (Attention)** | `CBAM` (Convolutional Block Attention Module) |
| **Số tham số (Parameters)** | ~17.5M (Backbone: 17.5M, CBAM: ~0.04M) |
| **Kích thước đầu vào** | `3 × 224 × 224` (hoặc `3 × 380 × 380`) |
| **Đầu ra** | 5 logits / xác suất (Probabilities via Softmax) |
| **Hàm mất mát (Loss Function)** | `Focal Loss` ($\gamma = 2.0$, Class-weighted) |
| **Đánh giá chính** | Quadratic Weighted Kappa (`QWK`), Macro `F1-Score`, `ROC-AUC` |

---

## 2. Các Lớp Đầu Ra (Target Classes)

Mô hình dự đoán 5 mức độ tổn thương võng mạc tiểu đường theo tiêu chuẩn quốc tế ICDR:

| Class ID | Tên nhãn (Label Name) | Mô tả lâm sàng | Tỷ lệ dữ liệu train |
|:---:|---|---|---:|
| **0** | `No DR` | Không phát hiện tổn thương DR | 71.6% |
| **1** | `Mild` | Xuất hiện vi phình mạch (Microaneurysms) | 7.0% |
| **2** | `Moderate` | Xuất hiện xuất huyết / xuất tiết nhẹ-vừa | 15.8% |
| **3** | `Severe` | Xuất huyết nặng 4 góc phần tư / chuỗi tĩnh mạch | 2.8% |
| **4** | `Proliferative DR` | Tăng sinh tân mạch / xuất huyết dịch kính | 2.9% |

---

## 3. Kiến trúc Chi tiết (Model Architecture)

Mô hình kết hợp giữa khả năng trích xuất đặc trưng đa tỷ lệ của **EfficientNet-B4** và cơ chế lọc đặc trưng không gian & kênh của **CBAM Attention**.

### 3.1. Sơ đồ Dòng Chảy Dữ Liệu (Data Flow Diagram)

```mermaid
flowchart TD
    A["Input Image (3 x 224 x 224)"] --> B["EfficientNet-B4 Backbone"]
    B --> C["Feature Maps (1792 x 7 x 7)"]
    
    subgraph CBAM ["CBAM Attention Module"]
        C --> D["Channel Attention Module (CA)"]
        D -->|Feature * CA Map| E["Spatial Attention Module (SA)"]
        E -->|Feature * SA Map| F["Refined Features (1792 x 7 x 7)"]
    end
    
    F --> G["AdaptiveAvgPool2d (1 x 1)"]
    G --> H["Flatten (1792)"]
    H --> I["Dropout (p=0.3)"]
    I --> J["Linear Classification Head (1792 -> 5)"]
    J --> K["Logits Output (5)"]
    K --> L["Softmax (Probabilities)"]
```

---

### 3.2. Mô tả Chi tiết từng Thành phần

#### A. Backbone (EfficientNet-B4)
- **Chức năng**: Trích xuất đặc trưng cấp cao từ ảnh đáy mắt (fundus).
- **Đầu ra trích xuất**: Tensor có kích thước `(Batch, 1792, H/32, W/32)` — tương ứng với `(Batch, 1792, 7, 7)` khi đầu vào là `224x224`.

#### B. CBAM Attention Module
Bao gồm 2 sub-modules nối tiếp nhau:

1. **Channel Attention (Chú ý theo Kênh)**:
   - Gom thông tin không gian bằng `AdaptiveAvgPool2d(1)``AdaptiveMaxPool2d(1)`.
   - Đưa qua shared MLP (2 lớp Conv2d giảm chiều theo tỷ lệ `ratio=16`, tức $1792 \rightarrow 112 \rightarrow 1792$).
   - Kết hợp kết quả bằng phép cộng, qua hàm `Sigmoid` tạo Trọng số Kênh $\mathbf{M}_c$.
   - Nhân phần tử với feature map ban đầu.

2. **Spatial Attention (Chú ý theo Không gian)**:
   - Gom thông tin kênh bằng phép `mean(dim=1)` và `max(dim=1)` tạo tensor 2 kênh.
   - Đưa qua lớp `Conv2d(2 -> 1, kernel_size=7, padding=3)` và hàm `Sigmoid` tạo Trọng số Không gian $\mathbf{M}_s$.
   - Nhân phần tử để tập trung vào các vùng tổn thương quan trọng (xuất huyết, vi phình mạch, xuất tiết).

#### C. Classifier Head
- `AdaptiveAvgPool2d(1)`: Thu gọn đặc trưng không gian về `1792 x 1 x 1`.
- `Flatten`: Biến đổi thành vector 1D chiều dài `1792`.
- `Dropout(p=0.3)`: Giảm thiểu hiện tượng Overfitting.
- `Linear(1792, 5)`: Ánh xạ đặc trưng về 5 điểm số đại diện cho 5 mức độ bệnh.

---

## 4. Cấu trúc File Checkpoint (`.pth`)

File checkpoint `modelAI/efficientnet_b4_cbam_fold1.pth` lưu trữ dưới dạng PyTorch dictionary:

```python
checkpoint = {
    "epoch": 24,                                 # Epoch đạt kết quả tốt nhất
    "model_state_dict": model.state_dict(),     # Trọng số tất cả các layer
    "optimizer_state_dict": optimizer.state_dict(),
    "val_qwk": 0.8542,                           # QWK trên tập Validation
    "val_f1": 0.7215,                            # Macro F1 trên tập Validation
    "args": CONFIG                               # Dictionary lưu tham số cấu hình
}
```

---

## 5. Hướng dẫn Đóng gói Mô hình Suy luận (Inference Packaging Guide)

Để đưa mô hình này vào ứng dụng sản xuất (Production / Web / Mobile / REST API), bạn cần đóng gói bộ file theo cấu trúc chuẩn bên dưới.

### 5.1. Cấu trúc thư mục đóng gói đề xuất

```text
modelAI_EfficientNetB4/
├── README.md                              # Tài liệu cấu hình & kiến trúc (File này)
├── efficientnet_b4_cbam_fold1.pth         # File trọng số PyTorch checkpoint
├── config.json                            # Cấu hình nhãn & tiền xử lý
├── model.py                               # Đã trích xuất PyTorch class (EfficientNetB4_CBAM)
├── preprocessing.py                       # Pipeline tiền xử lý ảnh (Ben Graham + Letterbox)
├── predictor.py                           # Class DRPredictor chính để gọi suy luận
├── gradcam_visualizer.py                  # Script & module sinh bản đồ nhiệt Grad-CAM
├── main_api.py                            # REST API Server với FastAPI (hỗ trợ Grad-CAM)
├── requirements.txt                       # Danh sách thư viện phụ thuộc
├── docs/                                  # Tài liệu HTML/MD chi tiết
└── hf_space/                              # Gradio UI App cho Hugging Face Space
```

---

### 5.2. Các REST API Endpoints hỗ trợ cho Web / Mobile

| Endpoint | Method | Trả về | Mục đích sử dụng |
|---|:---:|---|---|
| `/api/predict` | `POST` | `JSON` | Chẩn đoán 5 mức độ bệnh + bảng xác suất `probabilities`. |
| `/api/predict_gradcam` | `POST` | `JSON` | Chẩn đoán + bảng xác suất + chuỗi Base64 ảnh Grad-CAM sạch (`gradcam_image_base64`) + lời khuyên lâm sàng. |
| `/api/predict_gradcam_image` | `POST` | `PNG Image` | Trực tiếp file ảnh PNG chứa Grad-CAM heatmap (xem nhanh trên Swagger UI). |


---

### 5.2. Mã nguồn mã hóa Python Lớp Model (`package/model.py`)

```python
import torch
import torch.nn as nn
from torchvision import models

class ChannelAttention(nn.Module):
    def __init__(self, in_planes, ratio=16):
        super().__init__()
        self.avg_pool = nn.AdaptiveAvgPool2d(1)
        self.max_pool = nn.AdaptiveMaxPool2d(1)
        self.fc = nn.Sequential(
            nn.Conv2d(in_planes, in_planes // ratio, 1, bias=False),
            nn.ReLU(inplace=True),
            nn.Conv2d(in_planes // ratio, in_planes, 1, bias=False),
        )
        self.sigmoid = nn.Sigmoid()

    def forward(self, x):
        avg_out = self.fc(self.avg_pool(x))
        max_out = self.fc(self.max_pool(x))
        return self.sigmoid(avg_out + max_out)

class SpatialAttention(nn.Module):
    def __init__(self, kernel_size=7):
        super().__init__()
        self.conv = nn.Conv2d(2, 1, kernel_size, padding=kernel_size // 2, bias=False)
        self.sigmoid = nn.Sigmoid()

    def forward(self, x):
        avg_out = torch.mean(x, dim=1, keepdim=True)
        max_out, _ = torch.max(x, dim=1, keepdim=True)
        return self.sigmoid(self.conv(torch.cat([avg_out, max_out], dim=1)))

class CBAM(nn.Module):
    def __init__(self, in_planes, ratio=16, kernel_size=7):
        super().__init__()
        self.ca = ChannelAttention(in_planes, ratio)
        self.sa = SpatialAttention(kernel_size)

    def forward(self, x):
        x = x * self.ca(x)
        x = x * self.sa(x)
        return x

class EfficientNetB4_CBAM(nn.Module):
    def __init__(self, num_classes=5, drop_rate=0.3, cbam_ratio=16):
        super().__init__()
        backbone = models.efficientnet_b4(weights=None)
        self.features = backbone.features
        in_planes = 1792
        self.cbam = CBAM(in_planes, ratio=cbam_ratio)
        self.avgpool = nn.AdaptiveAvgPool2d(1)
        self.classifier = nn.Sequential(
            nn.Dropout(p=drop_rate),
            nn.Linear(in_planes, num_classes),
        )

    def forward(self, x):
        x = self.features(x)
        x = self.cbam(x)
        x = self.avgpool(x)
        x = torch.flatten(x, 1)
        return self.classifier(x)
```

---

### 5.3. File Cấu hình Json (`package/config.json`)

```json
{
  "model_name": "EfficientNetB4_CBAM",
  "num_classes": 5,
  "input_size": [224, 224],
  "mean": [0.485, 0.456, 0.406],
  "std": [0.229, 0.224, 0.225],
  "labels": {
    "0": "No DR",
    "1": "Mild",
    "2": "Moderate",
    "3": "Severe",
    "4": "Proliferative DR"
  }
}
```

---

### 5.4. Class Gọi Dự đoán (`package/predictor.py`)

```python
import os
import json
import torch
from PIL import Image
from torchvision import transforms
from model import EfficientNetB4_CBAM

class DRPredictor:
    def __init__(self, weights_path="../efficientnet_b4_cbam_fold1.pth", config_path="config.json"):
        with open(config_path, "r", encoding="utf-8") as f:
            self.config = json.load(f)

        self.device = torch.device("cuda" if torch.cuda.is_available() else "cpu")

        # 1. Khởi tạo kiến trúc
        self.model = EfficientNetB4_CBAM(
            num_classes=self.config["num_classes"],
            drop_rate=0.3
        )

        # 2. Load trọng số (hỗ trợ cả dạng checkpoint dict hoặc raw state_dict)
        checkpoint = torch.load(weights_path, map_location=self.device)
        if isinstance(checkpoint, dict) and "model_state_dict" in checkpoint:
            self.model.load_state_dict(checkpoint["model_state_dict"])
        else:
            self.model.load_state_dict(checkpoint)

        self.model.to(self.device)
        self.model.eval()

        # 3. Pipeline Transform chuẩn化
        self.transform = transforms.Compose([
            transforms.Resize(tuple(self.config["input_size"])),
            transforms.ToTensor(),
            transforms.Normalize(mean=self.config["mean"], std=self.config["std"])
        ])

    def predict(self, image_path):
        """
        Nhận vào đường dẫn ảnh đáy mắt và trả về kết quả phân loại DR.
        """
        image = Image.open(image_path).convert("RGB")
        tensor_img = self.transform(image).unsqueeze(0).to(self.device)

        with torch.no_grad():
            outputs = self.model(tensor_img)
            probs = torch.softmax(outputs, dim=1)[0]
            pred_class = torch.argmax(probs).item()

        return {
            "class_id": pred_class,
            "class_name": self.config["labels"][str(pred_class)],
            "confidence": float(probs[pred_class]),
            "probabilities": {
                self.config["labels"][str(i)]: float(probs[i])
                for i in range(len(probs))
            }
        }
```

---

## 6. Yêu cầu Môi trường (Dependencies)

File `package/requirements.txt`:

```text
torch>=2.0.0
torchvision>=0.15.0
Pillow>=9.5.0
numpy>=1.24.0
opencv-python>=4.7.0
```