Spaces:
Running
Running
File size: 14,497 Bytes
0211afc | 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 348 349 350 351 352 353 354 355 356 357 358 359 360 | # Tài liệu Kỹ thuật Mô hình AI — ResNet-50 Baseline
## Chẩn đoán Bệnh Võng mạc Tiểu đường (Diabetic Retinopathy Classification)
Tài liệu này mô tả chi tiết kiến trúc mô hình AI, quy trình xử lý dữ liệu đầu vào (Image Preprocessing Pipeline), cơ chế suy luận (Inference), cấu trúc Chi tiết của **File JSON Đầu ra** thu được từ mô hình và REST API, cùng hướng dẫn tích hợp cho nhà phát triển Backend / Frontend cho mô hình **ResNet-50 Baseline**.
---
## 1. Tổng quan Hệ thống AI
Mô hình AI trong thư mục `modelAI_ResNet50` được thiết kế để phân loại tự động 5 mức độ bệnh Võng mạc Tiểu đường (Diabetic Retinopathy - DR) theo tiêu chuẩn y tế quốc tế **ICDR (International Clinical Diabetic Retinopathy Disease Severity Scale)**.
### Thông số kỹ thuật chính:
* **Tên mô hình**: `ResNet50_DR`
* **Backbone**: ResNet-50 (Deep Residual Network với 50 layers và các kết nối tắt residual shortcuts)
* **Kích thước ảnh đầu vào**: `224 × 224` pixel (3 kênh RGB)
* **Số lượng tham số**: ~25.6 triệu tham số (Bottleneck Block Feature Extractor: 2048 channels)
* **Lớp đầu ra**: 5 lớp tương ứng 5 mức độ bệnh (0: No DR, 1: Mild, 2: Moderate, 3: Severe, 4: Proliferative DR)
* **Môi trường thực thi**: PyTorch 2.0+, CUDA / CPU
---
## 2. Kiến trúc Chi tiết Mô hình (Model Architecture)
Mô hình dựa trên kiến trúc **ResNet-50** chuẩn được tinh chỉnh phần classifier head để phân loại 5 lớp DR.
```
[Input Image: 3 x 224 x 224]
│
▼
[ResNet-50 Conv1 + MaxPool]
│
▼ (64 channels, 56 x 56)
[Layer 1: 3 x Bottleneck Blocks] (256 channels)
│
▼
[Layer 2: 4 x Bottleneck Blocks] (512 channels)
│
▼
[Layer 3: 6 x Bottleneck Blocks] (1024 channels)
│
▼
[Layer 4: 3 x Bottleneck Blocks] (2048 channels, 7 x 7)
│
▼
[Adaptive Avg Pooling (1 x 1)]
│
▼ (Flatten: Batch x 2048)
[Dropout (Rate = 0.3)]
│
▼
[Linear Classifier Head (2048 -> 5)]
│
▼
[Raw Logits Output (5 values)]
│
▼
[Softmax Function] -> Xác suất cho 5 lớp [P0, P1, P2, P3, P4]
```
### 2.1. Residual Blocks (Khối kết nối tắt)
* **Khái niệm cốt lõi**: Khối Bottleneck ($1 \times 1 \rightarrow 3 \times 3 \rightarrow 1 \times 1$ Conv) sử dụng đường truyền tắt (Identity Shortcut Connection) $\mathbf{y} = \mathcal{F}(\mathbf{x}) + \mathbf{x}$.
* **Ưu điểm**: Giải quyết triệt để hiện tượng tiêu biến đạo hàm (Vanishing Gradient) trong các mạng nơ-ron rất sâu, giúp mô hình hội tụ tốt và học được các biểu diễn đặc trưng phong phú.
* **Classifier Head**: Thay thế lớp kết nối đầy đủ mặc định bằng `Dropout(p=0.3)` và `Linear(2048, 5)` để tương thích với 5 nhãn bệnh ICDR.
---
## 3. Quy trình Xử lý Ảnh Đầu vào (Preprocessing Pipeline)
Trước khi đưa ảnh đáy mắt (Fundus Photo) vào mô hình AI, dữ liệu được truyền qua pipeline xử lý ảnh 5 bước trong `preprocessing.py`:
```
[Ảnh gốc (File / Bytes / PIL / Array)]
│
▼ 1. load_image()
[Mảng BGR OpenCV (Numpy Array)]
│
▼ 2. auto_detect_border() & crop_fundus_circle()
[Ảnh đã cắt bỏ viền đen]
│
▼ 3. letterbox_resize(target_size=(224, 224))
[Ảnh 224x224 bảo toàn tỷ lệ aspect ratio (Đệm đen)]
│
▼ 4. ben_graham_transform()
[Ảnh tăng cường tương phản Ben Graham]
│
▼ 5. prepare_image_tensor()
[PyTorch Tensor (1, 3, 224, 224) - Normalized ImageNet]
```
### Các bước xử lý chi tiết:
1. **Chuẩn hóa định dạng đầu vào (`load_image`)**:
- Hỗ trợ đường dẫn file (`str`), mảng byte (`bytes`), đối tượng PIL Image, hoặc mảng NumPy.
- Chuyển đổi tất cả về định dạng chuẩn OpenCV BGR (`uint8`).
2. **Cắt bỏ viền đen tự động (`crop_fundus_circle`)**:
- Xác định ngưỡng tối `CROP_TOLERANCE = 12`.
- Tìm Bounding Box của vùng cầu mắt có chứa thông tin y tế (`cv2.findNonZero`) và crop loại bỏ viền đen vô ích xung quanh.
3. **Letterbox Resize (`letterbox_resize`)**:
- Thay vì co giãn làm biến dạng hình dạng tổn thương mắt, ảnh được scale giữ nguyên tỷ lệ chiều rộng/chiều cao.
- Đệm thêm viền đen cân đối để đạt kích thước cố định `(224, 224)`.
4. **Biến đổi Ben Graham (`ben_graham_transform`)**:
- Áp dụng công thức lọc nhiễu & nổi bật mạch máu/tổn thương:
$$\text{Output} = \text{clip}(4 \times I - 4 \times \text{GaussianBlur}(I, \sigma=10) + 128, 0, 255)$$
- Cân bằng ánh sáng giữa các ảnh chụp từ nhiều thiết bị soi đáy mắt khác nhau.
5. **Chuyển đổi Tensor & Normalization**:
- Chuyển mảng BGR sang RGB.
- Đưa về dải giá trị $[0.0, 1.0]$.
- Chuẩn hóa theo chỉ số ImageNet: Mean `[0.485, 0.456, 0.406]`, Std `[0.229, 0.224, 0.225]`.
- Mở rộng chiều batch: Tensor shape `(1, 3, 224, 224)`.
---
## 4. Cách Mô hình Xử lý & Tạo ra File JSON Kết quả
Khi nhận yêu cầu phân loại, mô hình AI thực thi theo trình tự sau:
1. **Nhận dữ liệu & Tiền xử lý**: Mảng ảnh được chuẩn hóa thành PyTorch Tensor.
2. **Forward Pass**: Tensor được đẩy qua mô hình `ResNet50_DR`.
3. **Softmax Scoring**: Tính xác suất cho từng nhãn:
$$P(y = i | X) = \frac{e^{z_i}}{\sum_{j=0}^{4} e^{z_j}}$$
4. **Xác định Nhãn Dự đoán & Độ tin cậy**:
- `class_id` = $\arg\max_i P(y=i|X)$
- `confidence` = $\max_i P(y=i|X) \times 100\%$
5. **Đóng gói Kết quả JSON**:
- Trích xuất bảng xác suất cho tất cả 5 nhãn (làm tròn 4 chữ số thập phân).
- Ghép nối thông tin lời khuyên y tế lâm sàng (`CLINICAL_ADVICE`) dựa trên `class_id`.
- Mã hóa ảnh sau tiền xử lý Ben Graham thành chuỗi `Base64` JPEG data URI.
---
## 5. Cấu trúc File JSON Đầu ra (JSON Output Specification)
Hệ thống cung cấp 2 định dạng JSON đầu ra tùy thuộc vào cách gọi (gọi qua Python Class SDK hoặc gọi qua REST API Web Server).
### 5.1. Dạng JSON từ `DRPredictor.predict()` (Python Core / SDK Level)
Khi sử dụng trực tiếp class `DRPredictor` trong Python:
```json
{
"class_id": 2,
"class_name": "Moderate",
"confidence": 86.30,
"probabilities": {
"No DR": 0.0210,
"Mild": 0.0960,
"Moderate": 0.8630,
"Severe": 0.0150,
"Proliferative DR": 0.0050
}
}
```
#### Chi tiết các trường:
| Trường (Field) | Kiểu dữ liệu | Mô tả |
|---|---|---|
| `class_id` | `integer` | Mã lớp dự đoán (0 đến 4). |
| `class_name` | `string` | Tên lớp dự đoán (`No DR`, `Mild`, `Moderate`, `Severe`, `Proliferative DR`). |
| `confidence` | `float` | Độ tin cậy của dự đoán tính theo phần trăm (từ `0.0` đến `100.0`). |
| `probabilities` | `object` | Dictionary chứa xác suất dự đoán của từng lớp trong 5 lớp (tổng bằng 1.0). |
---
### 5.2. Dạng JSON từ REST API `POST /api/predict` (Web Server FastAPI Level)
Đây là định dạng JSON đầy đủ nhất được trả về cho Web Frontend, Mobile App hoặc hệ thống tích hợp bên thứ ba.
#### Ví dụ JSON Response hoàn chỉnh (HTTP 200 OK):
```json
{
"success": true,
"filename": "patient_fundus_right_eye.jpg",
"prediction": {
"class_id": 2,
"class_name": "Moderate",
"confidence": 86.30,
"probabilities": {
"No DR": 0.0210,
"Mild": 0.0960,
"Moderate": 0.8630,
"Severe": 0.0150,
"Proliferative DR": 0.0050
}
},
"clinical_guidance": {
"title": "Bệnh Trung Bình (Moderate DR)",
"badge_color": "#f59e0b",
"advice": "Tổn thương xuất huyết/xuất tiết mức độ vừa. Cần thăm khám bác sĩ nhãn khoa trong 3 - 6 tháng để đánh giá hoàng điểm và can thiệp kịp thời.",
"urgency": "Khám chuyên khoa"
},
"preprocessed_image_base64": "data:image/jpeg;base64,/9j/4AAQSkZJRgABAQAAAQABAAD/2wBDAAgGBgcGBQgHBwcJCQgK..."
}
```
#### Mô tả chi tiết từng trường dữ liệu trong JSON REST API:
| Nhóm trường | Trường (Field) | Kiểu dữ liệu | Mô tả |
|---|---|---|---|
| **Chung** | `success` | `boolean` | `true` nếu xử lý thành công, `false` nếu phát sinh lỗi. |
| | `filename` | `string` | Tên file ảnh gốc do người dùng hoặc ứng dụng tải lên. |
| **`prediction`** | `class_id` | `integer` | Chỉ số mức độ bệnh từ `0` đến `4`. |
| | `class_name` | `string` | Tên tiếng Anh chuẩn của nhãn ICDR tương ứng. |
| | `confidence` | `float` | Phần trăm độ tin cậy của mô hình cho nhãn dự đoán. |
| | `probabilities` | `object` | Bảng xác suất chi tiết của cả 5 nhãn ICDR. |
| **`clinical_guidance`**| `title` | `string` | Tiêu đề tiếng Việt hiển thị cho bác sĩ / bệnh nhân. |
| | `badge_color` | `string` | Mã màu HEX khuyến nghị để hiển thị Badge/Tag trên giao diện. |
| | `advice` | `string` | Chi tiết lời khuyên y tế lâm sàng dành cho bệnh nhân/bác sĩ. |
| | `urgency` | `string` | Mức độ khẩn cấp xử lý y tế (`Bình thường`, `Theo dõi định kỳ`, `Khám chuyên khoa`, `Cần can thiệp sớm`, `KHẨN CẤP`). |
| **Hiển thị** | `preprocessed_image_base64` | `string` | Chuỗi Data URI base64 của ảnh sau khi tiền xử lý Ben Graham, dùng để render trực tiếp lên thẻ `<img src="...">` của Frontend. |
---
## 6. Danh mục Lời khuyên Y tế Lâm sàng (Clinical Guidance Table)
Bảng ánh xạ từ `class_id` ra chỉ dẫn y tế và màu sắc giao diện:
| Class ID | Nhãn ICDR | Tiêu đề Tiếng Việt (`title`) | Mức khẩn cấp (`urgency`) | Mã màu (`badge_color`) |
|:---:|---|---|---|:---:|
| **0** | `No DR` | Mắt Bình Thường (No DR) | Bình thường | `#10b981` (Xanh lá) |
| **1** | `Mild` | Bệnh Nhẹ (Mild DR) | Theo dõi định kỳ | `#3b82f6` (Xanh dương) |
| **2** | `Moderate` | Bệnh Trung Bình (Moderate DR) | Khám chuyên khoa | `#f59e0b` (Cam vàng) |
| **3** | `Severe` | Bệnh Nặng (Severe DR) | Cần can thiệp sớm | `#ef4444` (Đỏ) |
| **4** | `Proliferative DR` | Tăng Sinh Nguy Hiểm (Proliferative DR) | KHẨN CẤP | `#8b5cf6` (Tím) |
---
## 7. Hướng dẫn Tích hợp API (API Integration Guide)
### 7.1. Khởi chạy REST API Server
```bash
# Di chuyển vào thư mục modelAI_ResNet50
cd modelAI_ResNet50
# Chạy server uvicorn
python main_api.py
```
Server sẽ chạy tại địa chỉ: `http://localhost:8000`. Bạn có thể truy cập `/docs` để mở giao diện Swagger UI tương tác.
---
### 7.2. Các Endpoints
#### 1. Kiểm tra trạng thái máy chủ
* **URL**: `GET /`
* **Response**:
```json
{
"message": "AI Diabetic Retinopathy API Service is running.",
"docs_url": "/docs",
"health_check": "/api/info",
"predict_endpoint": "POST /api/predict"
}
```
#### 2. Thông tin mô hình AI
* **URL**: `GET /api/info`
* **Response**:
```json
{
"status": "online",
"model_name": "ResNet50",
"num_classes": 5,
"device": "cuda:0",
"classes": {
"0": "No DR",
"1": "Mild",
"2": "Moderate",
"3": "Severe",
"4": "Proliferative DR"
}
}
```
#### 3. Chẩn đoán ảnh đáy mắt
* **URL**: `POST /api/predict`
* **Content-Type**: `multipart/form-data`
* **Body Form-Data**: `file`: [File ảnh PNG / JPG / JPEG]
---
### 7.3. Ví dụ Code Gọi API
#### A. cURL Command:
```bash
curl -X POST "http://localhost:8000/api/predict" \
-H "accept: application/json" \
-H "Content-Type: multipart/form-data" \
-F "file=@/path/to/fundus_image.jpg"
```
#### B. Python Code (requests):
```python
import requests
url = "http://localhost:8000/api/predict"
file_path = "test_eye.jpg"
with open(file_path, "rb") as f:
files = {"file": (file_path, f, "image/jpeg")}
response = requests.post(url, files=files)
data = response.json()
print("Kết quả chẩn đoán:", data["prediction"]["class_name"])
print("Độ tin cậy:", data["prediction"]["confidence"], "%")
print("Lời khuyên y tế:", data["clinical_guidance"]["advice"])
```
#### C. JavaScript (Fetch API - Frontend React/Vue/Vanilla):
```javascript
async function predictFundusImage(fileInput) {
const formData = new FormData();
formData.append("file", fileInput.files[0]);
const response = await fetch("http://localhost:8000/api/predict", {
method: "POST",
body: formData
});
const result = await response.json();
console.log("JSON Trả về từ AI ResNet-50:", result);
// Hiển thị lên UI
document.getElementById("status-badge").innerText = result.clinical_guidance.title;
document.getElementById("status-badge").style.backgroundColor = result.clinical_guidance.badge_color;
document.getElementById("processed-img").src = result.preprocessed_image_base64;
}
```
---
## 8. Xử lý Lỗi (Error Handling & HTTP Status Codes)
| Status Code | Nguyên nhân phát sinh | Cấu trúc JSON Lỗi |
|:---:|---|---|
| `400 Bad Request` | File tải lên không phải định dạng ảnh hợp lệ (VD: PDF, TXT). | `{"detail": "File tải lên không phải là định dạng ảnh hợp lệ."}` |
| `500 Internal Error` | Không nạp được file weights `.pth` hoặc lỗi bộ nhớ GPU/CPU trong quá trình xử lý ảnh. | `{"detail": "Lỗi trong quá trình xử lý ảnh: [chi tiết lỗi]"}` |
---
## 9. Đóng gói Docker Container
Để triển khai hệ thống lên môi trường Server / Cloud:
```bash
# Build image Docker
docker build -t dr-resnet50-api .
# Chạy container
docker run -d -p 8000:8000 --name dr_resnet50_service dr-resnet50-api
```
Hoặc khởi chạy thông qua `docker-compose`:
```bash
docker-compose up -d
```
|