Các kiến trúc CNN hiện đại: LeNet, AlexNet, VGG, GoogLeNet, ResNet, Xception, SENet, YOLO
This content is not available in your language yet.
Mục tiêu
Phần tiêu đề “Mục tiêu”Sau bài này, bạn sẽ:
- Kể được dòng thời gian từ LeNet-5 (1998) tới SENet (2018), và nói được mỗi kiến trúc thêm một ý tưởng nào để giải quyết vấn đề gì.
- Tự tính được vì sao hai lớp rẻ hơn một lớp , vì sao tích chập 1×1 (1×1 convolution) và tích chập tách theo chiều sâu (depthwise separable convolution) cắt được nhiều tham số.
- Giải thích được kết nối tắt (residual connection) của ResNet, và vì sao nó giúp huấn luyện mạng hàng trăm lớp.
- Hiểu ý tưởng phát hiện vật thể một lượt của YOLO: lưới, hộp, độ tin cậy, IoU và NMS, và cài đặt được IoU, NMS bằng NumPy.
Cần biết trước
Phần tiêu đề “Cần biết trước”- Convolutional Neural Networks (CNN), in Deep Learning(Not translated yet) (Learned)
Trực giác
Phần tiêu đề “Trực giác”Một cuộc thi, mỗi năm một ý tưởng
Phần tiêu đề “Một cuộc thi, mỗi năm một ý tưởng”Từ năm 2010, cuộc thi ImageNet (ILSVRC) cho các đội khoảng 1.2 triệu ảnh, 1000 lớp, và chấm lỗi top-5: tỉ lệ ảnh mà nhãn đúng không nằm trong 5 dự đoán đầu của mô hình. Hãy nhìn lỗi của đội thắng (hoặc đứng đầu) qua vài năm:
| Năm | Mô hình | Lỗi top-5 |
|---|---|---|
| 2012 | AlexNet | 15.3% (đội thứ hai: 26.2%) |
| 2014 | VGG (hạng hai) | 7.3% |
| 2014 | GoogLeNet (hạng nhất) | 6.67% |
| 2015 | ResNet | 3.57% |
| 2017 | SENet | 2.251% |
Lỗi giảm khoảng 7 lần trong 5 năm. Điều đáng chú ý: phép tích chập không đổi. Mọi mô hình trong bảng vẫn trượt bộ lọc trên ảnh như bài Mạng nơ-ron tích chập. Cái đổi là cách xếp các lớp tích chập. Mỗi kiến trúc gặp một bức tường, rồi tìm ra một ý tưởng để vượt qua:
- Mạng lớn huấn luyện quá chậm, lại học thuộc lòng dữ liệu. (AlexNet)
- Nên dùng bộ lọc cỡ nào: , , ? (VGG)
- Muốn dùng mọi cỡ cùng lúc mà không đắt. (GoogLeNet)
- Thêm lớp thì lỗi huấn luyện lại tăng. (ResNet)
- Một bộ lọc vừa nhìn không gian vừa trộn kênh, có lãng phí không? (Xception)
- Kênh nào quan trọng với ảnh này? (SENet)
Từ “ảnh này là gì” tới “vật nào ở đâu”
Phần tiêu đề “Từ “ảnh này là gì” tới “vật nào ở đâu””Phân loại trả lời một câu: ảnh có con mèo. Một chiếc xe tự lái cần nhiều hơn: có ba người đi bộ và một xe máy, ở những vị trí này, và phải trả lời trong vài chục mili giây. Đó là bài toán phát hiện vật thể (object detection). YOLO dùng chính một CNN để làm việc đó chỉ trong một lần chạy qua mạng.
Bài toán của bài này là: mỗi kiến trúc nổi tiếng đã thêm ý tưởng nào vào khung “tích chập, pooling, lớp cuối” của CNN, ý tưởng đó giải quyết vấn đề gì, và tốn (hoặc tiết kiệm) bao nhiêu tham số.
Lý thuyết
Phần tiêu đề “Lý thuyết”Khung chung của một CNN phân loại ảnh
Phần tiêu đề “Khung chung của một CNN phân loại ảnh”Mọi mạng trong bài (trừ YOLO ở phần đầu ra) có cùng một dáng:
- Thân (backbone): nhiều lớp tích chập. Càng vào sâu, ảnh càng nhỏ lại (nhờ pooling hoặc stride 2) còn số kênh càng nhiều. Lớp đầu bắt cạnh, lớp sau ghép cạnh thành hoa văn, rồi thành bộ phận của vật.
- Đầu (head): biến bản đồ đặc trưng cuối thành 1000 logit, rồi softmax.
Phần còn lại của mục này đi theo dòng thời gian. Mỗi mục nói vấn đề trước, ý tưởng sau.
LeNet-5 (1998): khuôn mẫu đầu tiên
Phần tiêu đề “LeNet-5 (1998): khuôn mẫu đầu tiên”LeCun và cộng sự (1998) dùng LeNet-5 để đọc chữ số viết tay, và hệ thống của họ đã được dùng để đọc séc ngân hàng. Ảnh vào , đi qua:
- tích chập ra 6 kênh, rồi lấy mẫu xuống (subsampling, giống pooling trung bình);
- tích chập ra 16 kênh, rồi lấy mẫu xuống;
- một lớp tích chập ra 120 kênh (ảnh lúc này còn ), một lớp kết nối đầy đủ 84 nơ-ron, rồi 10 đầu ra.
Cộng số tham số từng lớp mà bài báo ghi, phần huấn luyện được có khoảng 60 nghìn. Ý tưởng của LeNet là chính cái khung tích chập → pooling, lặp lại, rồi lớp kết nối đầy đủ, huấn luyện đầu cuối bằng lan truyền ngược. Mọi mạng sau đều giữ khung này.
AlexNet (2012): lớn hơn, cùng dữ liệu lớn và GPU
Phần tiêu đề “AlexNet (2012): lớn hơn, cùng dữ liệu lớn và GPU”Gần 15 năm sau, ý tưởng vẫn vậy, nhưng ba thứ đã khác: có ImageNet với 1.2 triệu ảnh gán nhãn, có GPU, và có vài mẹo huấn luyện. Krizhevsky, Sutskever và Hinton (2012) xây một mạng 5 lớp tích chập và 3 lớp kết nối đầy đủ, khoảng 60 triệu tham số, gấp khoảng 1000 lần LeNet. Bốn điểm làm nó chạy được:
- ReLU thay cho tanh. ReLU không bão hòa ở phía dương, nên gradient không bị bóp nhỏ. Trong bài báo, một mạng 4 lớp dùng ReLU đạt lỗi huấn luyện 25% trên CIFAR-10 nhanh hơn khoảng 6 lần bản dùng tanh.
- Dropout (tỉ lệ 0.5) ở hai lớp kết nối đầy đủ đầu, nơi tập trung phần lớn tham số và dễ overfitting nhất.
- Tăng cường dữ liệu (data augmentation): cắt ngẫu nhiên các mảnh ảnh, lật ngang, đổi cường độ màu, để 1.2 triệu ảnh “nhiều” hơn.
- Hai GPU GTX 580 (mỗi cái 3 GB) chạy song song, huấn luyện mất 5 tới 6 ngày.
Kết quả: lỗi top-5 15.3%, trong khi đội thứ hai (không dùng mạng nơ-ron sâu) là 26.2%. Đây là thời điểm học sâu chiếm lấy thị giác máy tính.
VGG (2014): chỉ dùng bộ lọc 3×3, xếp chồng
Phần tiêu đề “VGG (2014): chỉ dùng bộ lọc 3×3, xếp chồng”AlexNet dùng bộ lọc ở lớp đầu, ở lớp sau. Simonyan và Zisserman (2014) hỏi: nếu chỉ dùng và xếp nhiều lớp thì sao?
Bước 1: hai lớp 3×3 nhìn được bao xa. Một ô ở lớp thứ nhất nhìn một vùng của ảnh. Một ô ở lớp thứ hai nhìn ô của lớp thứ nhất. Hai ô lớp một nằm ở hai mép của cửa sổ đó cách nhau 2 bước, và mỗi ô lại nhìn thêm 1 điểm ảnh về mỗi phía, nên vùng ảnh tổng cộng rộng . Tổng quát, với stride 1, mỗi lớp nới trường tiếp nhận thêm :
Với : hai lớp cho , ba lớp cho . Vậy hai lớp “nhìn” được đúng vùng của một lớp .
Bước 2: so số tham số. Giả sử số kênh vào và ra đều là , bỏ qua hệ số tự do. Một lớp có trọng số. Do đó:
Hai lớp nhỏ tốn 72% số tham số của một lớp lớn. Với ba lớp so với một lớp , khoảng cách còn lớn hơn: so với .
Bước 3: thêm phi tuyến. Hai lớp có hai ReLU, một lớp chỉ có một. Cùng vùng nhìn, mạng có thêm một chỗ “uốn”, nên biểu diễn được hàm phức tạp hơn.
VGG-16 và VGG-19 (16 và 19 lớp có trọng số) chỉ là các khối “vài lớp rồi max pooling” lặp lại, cuối cùng là ba lớp kết nối đầy đủ. Thiết kế đơn giản đến mức người ta vẫn dùng VGG làm ví dụ đầu tiên. Nhưng nó nặng: VGG-16 có khoảng 138 triệu tham số, và khoảng 124 triệu trong số đó nằm ở ba lớp kết nối đầy đủ cuối (đếm bằng torchvision).
GoogLeNet và khối Inception (2014): nhánh song song, tích chập 1×1
Phần tiêu đề “GoogLeNet và khối Inception (2014): nhánh song song, tích chập 1×1”Cùng năm, nhóm của Szegedy (2014) đặt một câu hỏi khác: tại sao phải chọn cỡ bộ lọc? Một vật nhỏ cần , một vật to cần . Khối Inception chạy cùng lúc bốn nhánh trên cùng một đầu vào: tích chập , , , và max pooling . Đầu ra của bốn nhánh có cùng chiều cao và chiều rộng (nhờ padding), nên được ghép theo chiều kênh.
Cách làm ngây thơ này rất đắt. Nhánh trên đầu vào 192 kênh, ra 32 kênh, tốn tham số, và phép nhân thì nhân thêm với số vị trí trên ảnh.
Tích chập 1×1 là gì. Kernel không nhìn hàng xóm nào. Tại mỗi vị trí , nó lấy vectơ giá trị kênh ở đúng vị trí đó và nhân với một ma trận . Nói cách khác, nó là một lớp kết nối đầy đủ áp lên từng điểm ảnh, dùng chung trọng số cho mọi điểm. Khi , nó nén thông tin của nhiều kênh vào ít kênh hơn.
GoogLeNet đặt một lớp giảm kênh trước các lớp và . Ở nhánh trên: giảm 192 kênh xuống 16 trước, rồi mới ra 32 kênh. Số tham số:
ít hơn khoảng 10 lần. Ô code thứ nhất tính lại các số này.
GoogLeNet có 22 lớp có tham số, và bỏ các lớp kết nối đầy đủ to ở cuối, thay bằng pooling trung bình toàn cục (global average pooling): lấy trung bình mỗi kênh trên cả ảnh, được một vectơ, rồi một lớp tuyến tính ra 1000 lớp. Kết quả: lỗi top-5 6.67%, với số tham số ít hơn AlexNet khoảng 12 lần (theo bài báo). Tên GoogLeNet là lời chào tới LeNet.
ResNet (2015): kết nối tắt
Phần tiêu đề “ResNet (2015): kết nối tắt”VGG cho thấy sâu hơn thì tốt hơn. Vậy cứ thêm lớp? He và cộng sự (2015) thử, và thấy một chuyện lạ.
Vấn đề: thêm lớp, lỗi huấn luyện lại tăng. Trên CIFAR-10, một mạng “thường” (plain, không có kết nối tắt) 56 lớp có lỗi trên tập huấn luyện cao hơn mạng 20 lớp. Đây không phải overfitting: overfitting làm lỗi huấn luyện thấp còn lỗi kiểm tra cao. Ở đây mạng sâu học tệ hơn ngay trên dữ liệu nó được thấy. Các tác giả gọi đó là hiện tượng thoái hóa (degradation).
Chuyện này lạ vì có một lời giải dễ: lấy mạng 20 lớp đã học, thêm 36 lớp chỉ làm phép đồng nhất (identity), tức trả lại đúng đầu vào. Mạng 56 lớp đó tốt bằng mạng 20 lớp. Vậy lời giải tồn tại, chỉ là gradient descent không tìm ra. Một chồng tích chập và ReLU khó học được phép “không làm gì”.
Ý tưởng: học phần dư. Gọi là đầu vào của một khối vài lớp, và là hàm ta muốn khối đó biểu diễn. Thay vì bắt các lớp học thẳng , ResNet cho chúng học phần dư (residual):
rồi cộng trở lại qua một đường tắt (shortcut). Đầu ra của khối là:
ở đây là hai lớp tích chập , mỗi lớp có BatchNorm, ReLU ở giữa, và là ReLU sau phép cộng. Hình ở mục Minh họa vẽ đúng khối này. Nếu phép đồng nhất là tốt nhất, các lớp chỉ cần đẩy về 0, tức đẩy trọng số về gần 0. Việc đó dễ hơn nhiều so với học ra phép đồng nhất từ đầu.
Vì sao gradient đi xa được. Xét trường hợp một chiều, bỏ ReLU cuối cho gọn: . Đạo hàm là:
Số hạng 1 đến từ đường tắt. Mạng thường có nên đạo hàm chỉ là . Qua 50 khối, quy tắc dây chuyền nhân 50 đạo hàm với nhau. Nếu mỗi cỡ 0.5, mạng thường còn : gradient tiêu biến. Với kết nối tắt, mỗi thừa số là , quanh 1 khi nhỏ, nên tích không sụp về 0. Gradient có một “đường cao tốc” đi thẳng từ lớp cuối về lớp đầu. (Lập luận này đúng nhất khi đường tắt là phép đồng nhất thuần túy; He và cộng sự (2016) phân tích kỹ hơn trong bài về ánh xạ đồng nhất.)
Khi kích thước không khớp. Phép cộng cần hai tensor cùng kích thước. Khi một khối giảm ảnh đi một nửa (stride 2) và tăng gấp đôi số kênh, đường tắt dùng một tích chập stride 2 để đổi cho khớp. Đường tắt đồng nhất thì không thêm tham số nào.
Khối nút cổ chai. ResNet-50, 101, 152 dùng khối : lớp đầu giảm kênh, lớp làm việc trên ít kênh, lớp sau tăng kênh lại. Đây là đúng ý tưởng giảm kênh của Inception.
Kết quả trên ImageNet: mạng thường 34 lớp có lỗi top-1 28.54%, tệ hơn mạng thường 18 lớp (27.94%). Thêm kết nối tắt, ResNet-34 còn 25.03%, tốt hơn ResNet-18. ResNet-152 có 152 lớp mà vẫn tốn ít phép tính hơn VGG-16 (11.3 so với 15.3 tỉ FLOPs), và tổ hợp các ResNet đạt lỗi top-5 3.57%. Kết nối tắt từ đó có mặt khắp nơi, kể cả trong mọi khối Transformer (bài Transformer).
Xception (2017): tích chập tách theo chiều sâu
Phần tiêu đề “Xception (2017): tích chập tách theo chiều sâu”Một lớp tích chập thường làm hai việc cùng lúc: nhìn hàng xóm trong không gian (vùng ), và trộn thông tin giữa các kênh (mỗi bộ lọc nhìn mọi kênh vào). Chollet (2017) đặt giả thuyết rằng hai việc này tách riêng được, và tách thì hiệu quả hơn.
Tích chập tách theo chiều sâu làm hai bước:
- Depthwise: mỗi kênh vào có một bộ lọc riêng, chỉ nhìn kênh đó. Không trộn kênh.
- Pointwise: một tích chập trộn các kênh, như ở Inception.
Đếm tham số (bỏ qua hệ số tự do). Tích chập thường có . Bản tách có (depthwise) cộng (pointwise). Chia hai số cho nhau:
Dòng này có được bằng cách chia từng số hạng ở tử cho mẫu: số hạng đầu còn , số hạng sau còn . Với và , tỉ số là : bản tách tốn khoảng 8.7 lần ít hơn. Khi số kênh lớn, gần 0, nên mức tiết kiệm tiến gần lần.
Chollet nhìn Inception theo cách này: một khối Inception chia kênh thành vài nhóm, mỗi nhóm có tích chập không gian riêng. Đẩy tới cực điểm, mỗi kênh một nhóm, ta được tích chập tách theo chiều sâu. Tên Xception nghĩa là “Extreme Inception”. Mạng có 36 lớp tích chập, chia thành 14 module, có kết nối tắt kiểu ResNet. Với khoảng 22.9 triệu tham số (Inception V3 có 23.6 triệu), Xception hơn Inception V3 một chút trên ImageNet (top-1 79.0% so với 78.2%) và hơn rõ trên một bộ dữ liệu nội bộ 350 triệu ảnh. Vì số tham số gần bằng nhau, cái lợi đến từ cách dùng tham số, không phải từ việc có nhiều hơn. Cùng ý tưởng này là nền của MobileNet, mạng cho điện thoại (bài Phân loại ảnh với CNN).
SENet (2018): mỗi kênh một trọng số
Phần tiêu đề “SENet (2018): mỗi kênh một trọng số”Đến đây, mọi kênh ra của một lớp được đối xử như nhau. Nhưng với ảnh một con mèo, kênh bắt “lông” quan trọng hơn kênh bắt “bánh xe”. Hu và cộng sự (2018) thêm một khối nhỏ học cách tăng hoặc giảm từng kênh tùy theo ảnh: khối squeeze-and-excitation (SE). Gọi là đầu ra của một lớp, có kênh, mỗi kênh là một ma trận .
Bước 1, squeeze (ép): tóm mỗi kênh thành một số bằng trung bình toàn cục:
Ta được vectơ có thành phần, một “bản tóm tắt” ảnh này theo từng kênh.
Bước 2, excitation (kích): cho qua một MLP nhỏ có nút cổ chai, rồi sigmoid:
có kích thước (nén số xuống ), có kích thước (mở lại số). Bài báo chọn . Sigmoid đưa mỗi về khoảng : đó là “trọng số” của kênh .
Bước 3, scale (co giãn): nhân cả kênh với trọng số của nó, .
Trọng số thay đổi theo từng ảnh, vì tính từ chính ảnh đó. Đây là một dạng chú ý theo kênh (channel attention), họ hàng xa của attention trong Transformer. Khối SE rất rẻ: chỉ có và , khoảng tham số. Gắn vào mỗi khối của ResNet-50, số tham số tăng từ 25.6 lên 28.1 triệu (khoảng 10%), số phép tính gần như không đổi (3.87 so với 3.86 tỉ FLOPs), còn lỗi top-1 giảm từ 24.80% xuống 23.29%. Đội dùng SENet thắng ILSVRC 2017 với lỗi top-5 2.251%.
YOLO (2016): phát hiện vật thể trong một lượt
Phần tiêu đề “YOLO (2016): phát hiện vật thể trong một lượt”Các mạng trên trả lời “ảnh này là gì”. Phát hiện vật thể cần thêm “ở đâu”: đầu ra là một danh sách hộp bao (bounding box), mỗi hộp có vị trí, kích thước, và nhãn lớp. Cách làm phổ biến trước đó (họ R-CNN) đề xuất khoảng hai nghìn vùng có thể chứa vật, rồi chạy bộ phân loại trên từng vùng. Chính xác, nhưng chậm.
Redmon và cộng sự (2016) đặt câu hỏi ngược lại: một CNN có thể nhìn ảnh một lần (You Only Look Once) rồi trả ra mọi hộp cùng lúc không? Ý tưởng:
Lưới. Chia ảnh thành lưới ô (bài báo dùng ). Ô nào chứa tâm của một vật thì ô đó chịu trách nhiệm phát hiện vật ấy.
Mỗi ô dự đoán gì. Mỗi ô dự đoán hộp (). Mỗi hộp gồm 5 số: tâm tính theo ô, chiều rộng và chiều cao tính theo cả ảnh, và một độ tin cậy (confidence). Ngoài ra mỗi ô dự đoán xác suất lớp (với bộ dữ liệu PASCAL VOC, ), dùng chung cho các hộp của ô. Vậy đầu ra của mạng là một tensor:
Mạng có 24 lớp tích chập (nhiều lớp giảm kênh, học từ GoogLeNet) rồi 2 lớp kết nối đầy đủ, và lớp cuối ra thẳng tensor này. Phát hiện vật thể thành một bài hồi quy: một lần lan truyền xuôi là xong.
IoU: hai hộp trùng nhau bao nhiêu. Để biết một hộp dự đoán tốt cỡ nào, ta so nó với hộp thật bằng IoU (Intersection over Union, tỉ lệ giao trên hợp):
IoU bằng 1 khi hai hộp trùng khít, bằng 0 khi không chạm nhau. Ví dụ: hộp từ tới và hộp từ tới , mỗi hộp . Phần giao chạy từ tới (rộng 38) và tới (cao 36), diện tích . Phần hợp là tổng hai diện tích trừ phần giao (để không đếm phần giao hai lần): . Vậy .
Độ tin cậy của một hộp được huấn luyện để bằng giữa hộp dự đoán và hộp thật: gần 0 nếu ô không chứa vật, gần IoU nếu có. Lúc dùng, điểm của hộp cho một lớp là xác suất lớp đó nhân với độ tin cậy.
NMS: bỏ hộp trùng. Một con mèo to có thể được vài ô và vài hộp cùng “nhận”. Ta được nhiều hộp chồng lên nhau quanh cùng một vật. NMS (Non-Maximum Suppression, khử phi cực đại) dọn chúng, làm riêng cho từng lớp:
- Sắp các hộp theo điểm giảm dần.
- Giữ hộp điểm cao nhất.
- Bỏ mọi hộp còn lại có IoU với hộp vừa giữ từ một ngưỡng trở lên (thường 0.5): chúng nhiều khả năng là cùng một vật.
- Lặp lại với các hộp còn sót, tới khi hết.
Với YOLO, NMS thêm 2 tới 3 điểm mAP (mean average precision, thước đo chuẩn của phát hiện vật thể, cao hơn là tốt hơn). Kết quả: YOLO chạy 45 khung hình mỗi giây với 63.4 mAP trên PASCAL VOC 2007, đủ nhanh cho video thời gian thực. Điểm yếu, theo chính bài báo: mỗi ô chỉ có 2 hộp và một lớp, nên YOLO khó với nhiều vật nhỏ đứng sát nhau, như một đàn chim, và định vị kém chính xác hơn các phương pháp hai bước. Các phiên bản sau (YOLOv2, YOLOv3 của chính Redmon, rồi nhiều bản của các nhóm khác) thêm hộp neo (anchor box) và dự đoán ở nhiều tỉ lệ ảnh để bù những điểm yếu này.
Bảng so sánh
Phần tiêu đề “Bảng so sánh”| Năm | Kiến trúc | Số lớp | Số tham số (xấp xỉ) | Ý tưởng chính |
|---|---|---|---|---|
| 1998 | LeNet-5 | 7 (không tính lớp vào) | 60 nghìn ¹ | Khung tích chập → pooling → kết nối đầy đủ |
| 2012 | AlexNet | 8 (5 tích chập + 3 kết nối đầy đủ) | 61 triệu ² | ReLU, dropout, tăng cường dữ liệu, GPU |
| 2014 | VGG-16 / VGG-19 | 16 / 19 | 138 / 144 triệu ² | Chỉ dùng , xếp chồng |
| 2014 | GoogLeNet | 22 | 6.6 triệu ² | Nhánh song song, giảm kênh, pooling trung bình toàn cục |
| 2015 | ResNet-18 / 50 / 152 | 18 / 50 / 152 | 11.7 / 25.6 / 60.2 triệu ² | Kết nối tắt, học phần dư |
| 2017 | Xception | 36 lớp tích chập | 22.9 triệu ³ | Tích chập tách theo chiều sâu |
| 2018 | SE-ResNet-50 | 50 | 28.1 triệu ³ | Khối SE: trọng số cho từng kênh |
| 2016 | YOLO | 24 tích chập + 2 kết nối đầy đủ | không nêu ⁴ | Phát hiện vật thể một lượt: lưới, hộp, độ tin cậy |
Nguồn của cột số tham số: ¹ cộng số tham số huấn luyện được của từng lớp trong bài báo LeNet-5; ² tự đếm bằng torchvision 0.29 (weights=None, GoogLeNet không tính hai bộ phân loại phụ dùng khi huấn luyện), khớp con số trong bài báo AlexNet (60 triệu) và VGG (138, 144 triệu); ³ bảng trong bài báo Xception và SENet; ⁴ bài báo YOLO không ghi tổng số tham số.
Hai điều nhìn ra từ bảng: số tham số không tăng theo độ sâu (ResNet-152 sâu gấp gần 10 lần VGG-16 mà ít tham số hơn), và hầu hết các ý tưởng sau VGG là để tiêu tham số khôn hơn, không phải để có nhiều hơn.
Giới hạn
Phần tiêu đề “Giới hạn”- Bài chỉ kể ý tưởng. Huấn luyện một mạng phân loại ảnh thật, dùng lại trọng số đã học sẵn (transfer learning), và các mạng nhẹ như MobileNet, EfficientNet nằm ở bài Phân loại ảnh với CNN.
- Số tham số chưa phải tất cả. Tốc độ thật còn phụ thuộc số phép tính (FLOPs), bộ nhớ cho kích hoạt và phần cứng. Tích chập tách theo chiều sâu ít phép nhân hơn nhiều, nhưng trên GPU không phải lúc nào cũng nhanh hơn tương ứng.
- YOLO ở đây chỉ ở mức ý tưởng. Hàm mất mát nhiều thành phần, hộp neo và cách đo mAP chi tiết không có trong bài này.
Minh họa
Phần tiêu đề “Minh họa”Hình dưới vẽ một khối của ResNet-18. Hãy nhìn hai đường đi từ : đường chính đi qua hai lớp tích chập, đường tắt đi vòng qua và gặp lại đường chính ở dấu cộng, trước ReLU cuối. Đường tắt không có lớp nào, nên không có tham số nào.
Bảng dưới là khối Inception đầu tiên của GoogLeNet (khối “3a” trong bài báo), đặt trên đầu vào . Hãy nhìn cột thứ hai: mọi nhánh lớn đều có một lớp giảm kênh đứng trước.
| Nhánh | Lớp giảm kênh | Lớp chính | Kênh ra |
|---|---|---|---|
| không có | 64 | ||
| 192 → 96 | 128 | ||
| 192 → 16 | 32 | ||
| Pooling | không có | max pooling , rồi | 32 |
| Ghép theo kênh | 256 |
Đầu ra là : cùng chiều cao, chiều rộng với đầu vào, chỉ khác số kênh. Cả khối có khoảng 159 nghìn tham số (theo bảng 1 của bài báo).
Ví dụ code
Phần tiêu đề “Ví dụ code”Ba ô dưới đây chạy độc lập. Hai ô đầu chỉ dùng NumPy và SciPy, chạy ngay trên trang; ô thứ nhất lần đầu chạy lâu hơn một chút vì trình duyệt phải tải SciPy. Ô thứ ba cần PyTorch nên chạy trên máy bạn. Code chạy thử lần cuối tháng 11/2026, với Python 3.14, NumPy 2.4 (bản có trong Pyodide), PyTorch 2.15 và torchvision 0.29 trên CPU.
Đếm tham số của ba ý tưởng
Phần tiêu đề “Đếm tham số của ba ý tưởng”Ô dưới đếm tham số cho bốn ý tưởng của mục Lý thuyết, có tính cả hệ số tự do: hai lớp so với một lớp (256 kênh), nhánh của Inception có và không có , tích chập tách theo chiều sâu so với tích chập thường, và chi phí của một khối SE. Ô cũng kiểm tra trường tiếp nhận bằng cách đặt một điểm sáng giữa ảnh, chập hai lần với kernel , rồi đo vùng khác 0.
Đếm tham số: VGG, Inception, Xception, SENet
import numpy as npfrom scipy.signal import convolve2d
# 1. Hai lớp 3x3 so với một lớp 5x5 (256 kênh)c = 256params_2x_3x3 = 2 * (3 * 3 * c * c + c)params_1x_5x5 = 5 * 5 * c * c + cprint(f"2x 3x3: {params_2x_3x3:,} params | 1x 5x5: {params_1x_5x5:,} params")
# Kiểm tra trường tiếp nhận: 1 điểm sáng giữa ảnh 9x9img = np.zeros((9, 9))img[4, 4] = 1.0k = np.ones((3, 3))conv1 = convolve2d(img, k, mode="same")conv2 = convolve2d(conv1, k, mode="same")rf_h = np.count_nonzero(conv2.any(axis=1))rf_w = np.count_nonzero(conv2.any(axis=0))print(f"Receptive field (2x 3x3): {rf_h}x{rf_w}")
# 2. Nhánh 5x5 của Inception: 192 vào, 32 ra (có và không có 1x1 16 kênh)cin, cout, cred = 192, 32, 16direct_5x5 = 5 * 5 * cin * cout + coutreduced_5x5 = (1 * 1 * cin * cred + cred) + (5 * 5 * cred * cout + cout)print(f"Inception 5x5 direct: {direct_5x5:,} | reduced: {reduced_5x5:,}")
# 3. Tích chập thường so với tách theo chiều sâu (256 kênh vào/ra)std_conv = 3 * 3 * c * c + cdepthwise = (3 * 3 * c + c) + (1 * 1 * c * c + c)print(f"Standard 3x3: {std_conv:,} | Depthwise separable: {depthwise:,}")
# 4. Khối SE: r = 16, C = 256r = 16se_params = (c * (c // r) + (c // r)) + ((c // r) * c + c)print(f"SE block (r={r}): {se_params:,} params")The first run downloads Python, about 10 MB.
Đọc kết quả:
- Hai lớp 3×3: 1,180,160 tham số, một lớp 5×5: 1,638,656. Tỉ số khoảng 0.72, đúng (hệ số tự do gần như không đáng kể). Vùng khác 0 sau hai lần chập là 5×5: một điểm sáng ảnh hưởng tới vùng của đầu ra, tức mỗi ô đầu ra nhìn vùng của ảnh vào.
- Nhánh 5×5 của Inception: 153,632 tham số khi làm thẳng, 15,920 khi có 1×1 giảm xuống 16 kênh, ít hơn khoảng 10 lần, khớp phép tính ở mục Lý thuyết.
- Tích chập 3×3 thường: 590,080; bản tách theo chiều sâu: 68,352, ít hơn 8.6 lần. Công thức không tính hệ số tự do cho ; hệ số tự do làm tỉ số lệch đi một chút.
- Khối SE với chỉ tốn 8,464 tham số, bằng 1.4% của một lớp cùng số kênh. Đó là lý do SENet thêm được khối này vào mọi khối của ResNet mà mạng chỉ nặng thêm khoảng 10%.
IoU và NMS bằng NumPy
Phần tiêu đề “IoU và NMS bằng NumPy”Ô dưới cài đặt IoU và NMS đúng như các bước ở mục YOLO, trên 5 hộp tự đặt cho một lớp: ba hộp quanh một con mèo, hai hộp quanh một con chó. Hộp có dạng : góc trên trái và góc dưới phải. Hàm iou tính IoU của một hộp với nhiều hộp cùng lúc; hàm nms trả về chỉ số các hộp được giữ, theo thứ tự giữ.
IoU và NMS trên 5 hộp
import numpy as np
def iou(box, boxes): # box: (4,), boxes: (N, 4) với dạng (x1, y1, x2, y2) x1 = np.maximum(box[0], boxes[:, 0]) y1 = np.maximum(box[1], boxes[:, 1]) x2 = np.minimum(box[2], boxes[:, 2]) y2 = np.minimum(box[3], boxes[:, 3]) inter = np.maximum(0, x2 - x1) * np.maximum(0, y2 - y1) area1 = (box[2] - box[0]) * (box[3] - box[1]) area2 = (boxes[:, 2] - boxes[:, 0]) * (boxes[:, 3] - boxes[:, 1]) union = area1 + area2 - inter return inter / union
def nms(boxes, scores, threshold): # Sắp xếp theo điểm giảm dần order = scores.argsort()[::-1] keep = [] while order.size > 0: i = order[0] keep.append(int(i)) if order.size == 1: break # Tính IoU của hộp điểm cao nhất với các hộp còn lại ious = iou(boxes[i], boxes[order[1:]]) # Giữ lại các hộp có IoU nhỏ hơn ngưỡng order = order[1:][ious < threshold] return keep
# 5 hộp: 3 hộp mèo (0..2), 2 hộp chó (3..4)boxes = np.array([ [10, 10, 50, 50], # hộp 0 (mèo) [12, 14, 52, 54], # hộp 1 (mèo) [14, 17, 54, 57], # hộp 2 (mèo) [100, 100, 140, 140], # hộp 3 (chó) [104, 105, 144, 145], # hộp 4 (chó)])scores = np.array([0.90, 0.80, 0.60, 0.85, 0.70])
print("IoU hop 0 voi cac hop 1..4:", np.round(iou(boxes[0], boxes[1:]), 2))print("IoU hop 3 voi hop 4: ", np.round(iou(boxes[3], boxes[4:])[0], 2))print("NMS voi nguong 0.5: ", nms(boxes, scores, 0.5))print("NMS voi nguong 0.7: ", nms(boxes, scores, 0.7))The first run downloads Python, about 10 MB.
Đọc kết quả:
- IoU của hộp 0 với các hộp khác là 0.75, 0.59, 0, 0. Số 0.75 khớp phép tính tay ở mục Lý thuyết. Hộp 2 lệch nhiều hơn nên IoU thấp hơn. Hai hộp của con chó không chạm hộp 0, nên IoU bằng 0. IoU của hai hộp chó với nhau là 0.65.
- Với ngưỡng 0.5, NMS giữ
[0, 3]: hộp mèo điểm cao nhất (0.90), rồi hộp chó điểm cao nhất (0.85). Ba hộp còn lại đều trùng từ 0.5 trở lên với một hộp đã giữ. Mỗi vật đúng một hộp. - Với ngưỡng 0.7, NMS giữ
[0, 3, 4, 2]: chỉ hộp 1 (IoU 0.75) bị bỏ. Hộp 4 (IoU 0.65 với hộp 3) và hộp 2 (IoU 0.59 với hộp 0) sống sót, nên mỗi con vật bị đếm hai lần. Ngưỡng là một siêu tham số, và quá cao thì không dọn hết hộp trùng.
Khối residual bằng PyTorch
Phần tiêu đề “Khối residual bằng PyTorch”File dưới định nghĩa khối BasicBlock của ResNet (hai lớp có BatchNorm, đường tắt đồng nhất, hoặc stride 2 khi đổi kích thước), so số tham số của nó với khối tương ứng trong resnet18 của torchvision, rồi làm một thí nghiệm nhỏ về hiện tượng thoái hóa: huấn luyện một mạng sâu có và không có đường tắt trên bộ ảnh chữ số load_digits của scikit-learn (có sẵn khi cài, không tải mạng).
Lỗi hay gặp
Phần tiêu đề “Lỗi hay gặp”Kiểm tra nhanh
Phần tiêu đề “Kiểm tra nhanh”Tóm tắt và đọc thêm
Phần tiêu đề “Tóm tắt và đọc thêm”Ý chính:
- Mọi kiến trúc trong bài giữ khung của LeNet-5 (tích chập, pooling, rồi lớp phân loại) và thêm một ý tưởng. AlexNet (2012): lớn hơn, với ReLU, dropout, GPU và ImageNet. VGG (2014): chỉ ; lớp nhìn vùng với ít tham số hơn một lớp lớn ( so với ).
- Tích chập là lớp kết nối đầy đủ trên từng điểm ảnh, dùng để giảm kênh trước lớp đắt (GoogLeNet, nút cổ chai của ResNet). Tích chập tách theo chiều sâu (Xception) tách việc nhìn không gian khỏi việc trộn kênh, tiết kiệm gần lần.
- ResNet học phần dư: . Phép đồng nhất trở nên dễ học, gradient có đường đi thẳng qua đường tắt, nên mạng 152 lớp vẫn huấn luyện được. SENet cho mỗi kênh một trọng số tính từ chính ảnh, với rất ít tham số.
- YOLO biến phát hiện vật thể thành một lần hồi quy: lưới , mỗi ô hộp có độ tin cậy và xác suất lớp. IoU đo hai hộp trùng nhau bao nhiêu; NMS giữ hộp điểm cao nhất và bỏ các hộp trùng với nó quá ngưỡng.
Đọc thêm:
- Zhang, A., Lipton, Z. C., Li, M., Smola, A. J. Đắm mình vào Học Sâu, bản dịch tiếng Việt của nhóm Đắm mình vào Học Sâu. Chương 7: Mạng Nơ-ron Tích chập Hiện đại. AlexNet, VGG, GoogLeNet, ResNet có code từng mạng; bản tiếng Anh mới hơn là Dive into Deep Learning, chương 8 (2023).
- He, K., Zhang, X., Ren, S., Sun, J. (2015). Deep Residual Learning for Image Recognition, arXiv:1512.03385 (CVPR 2016), mục 1, 3 và hình 1. Hiện tượng thoái hóa, phần dư, khối nút cổ chai; bài báo cũng viết , như bài này.
- Simonyan, K., Zisserman, A. (2014). Very Deep Convolutional Networks for Large-Scale Image Recognition, arXiv:1409.1556 (ICLR 2015), mục 2.3. Lập luận “ba lớp thay một lớp ” và bảng số tham số.
- Szegedy, C. và cộng sự (2014). Going Deeper with Convolutions, arXiv:1409.4842 (CVPR 2015), mục 4 và bảng 1. Khối Inception và vai trò giảm kênh của tích chập .
- Redmon, J., Divvala, S., Girshick, R., Farhadi, A. (2016). You Only Look Once: Unified, Real-Time Object Detection, CVPR 2016, mục 2. Lưới, hộp, độ tin cậy, và các giới hạn của YOLO.
- Các bài gốc còn lại: LeCun, Y. và cộng sự (1998), Gradient-Based Learning Applied to Document Recognition (PDF), mục II; Krizhevsky, A., Sutskever, I., Hinton, G. E. (2012), ImageNet Classification with Deep Convolutional Neural Networks, NIPS 2012; Chollet, F. (2017), Xception: Deep Learning with Depthwise Separable Convolutions, CVPR 2017; Hu, J. và cộng sự (2018), Squeeze-and-Excitation Networks, CVPR 2018.
Your browser is blocking storage. Your progress will be lost when you reload the page.
Content licensed under CC BY-NC-SA 4.0. Code under the MIT license.About