Professional Documents
Culture Documents
ĐỒ ÁN CHUYÊN NGÀNH
ĐỀ TÀI: PHÂN LOẠI TÌNH TRẠNG HOA QUẢ SỬ
DỤNG CNN KẾT HỢP KMEANS
Hà Nội – 2023
MỤC LỤC
MỤC LỤC...........................................................................................................................................2
LỜI MỞ ĐẦU.....................................................................................................................................3
LỜI CẢM ƠN......................................................................................................................................4
CHƯƠNG 1: TỔNG QUAN...............................................................................................................5
1.1 Tổng quan về đề tài....................................................................................................................5
1.1.1 Lý do chọn đề tài.................................................................................................................5
1.1.2 Bài toán phân loại và phân cụm hoa quả.............................................................................5
1.1.3 Một số kỹ thuật hiện có để giải quyết bài toán....................................................................7
1.2 Cơ sở lý thuyết.........................................................................................................................10
1.2.1 Mô hình mạng CNN (Convolutional neural networks).....................................................10
1.2.2 K Means............................................................................................................................20
1.3 Các thư viện sử dụng trong bài toán.........................................................................................26
CHƯƠNG 2: THỰC NGHIỆM.........................................................................................................28
2.1 Dataset......................................................................................................................................28
2.2 Các mô hình sẽ sử dụng trong bài toán....................................................................................28
2.2.1 Mô hình tự tạo...................................................................................................................28
2.2.2 Mô hình có sẵn..................................................................................................................28
2.3 Thực nghiệm............................................................................................................................30
2.3.1 Huấn luyện mô hình (Sử dụng CNN)................................................................................30
2.3.2 Phân lớp và phân cụm hoa quả (Kmeans và best_model.h5)............................................54
CHƯƠNG 3: KIỂM THỬ CHƯƠNG TRÌNH.................................................................................57
3.1 Tổng quan về kiểm thử.............................................................................................................57
3.2 Phương pháp và kĩ thuật kiểm thử sử dụng..............................................................................57
3.2.1 Kiểm thử đơn vị................................................................................................................58
3.2.2 Ưu điểm, nhược điểm........................................................................................................58
3.2.3 Quy trình kiểm thử............................................................................................................59
3.3 Thực hiện kiểm thử..................................................................................................................60
3.3.1 Tổ chức module.................................................................................................................60
3.3.2 Xây dựng test case.............................................................................................................62
3.3.3 Test script..........................................................................................................................63
3.4 Đánh giá kiểm thử....................................................................................................................68
KẾT LUẬN.......................................................................................................................................70
TÀI LIỆU THAM KHẢO.................................................................................................................71
2
LỜI MỞ ĐẦU
Công nghệ thông tin đã trở thành một phần quan trọng không thể thiếu
trong cuộc sống hiện đại của con người. Từ những công trình nghiên cứu tiên
tiến trong công nghiệp hàng không vũ trụ, đến những ứng dụng và trang web
chúng ta sử dụng hang ngày. Sự phát triển vượt bậc của công nghệ thông tin
đã mang lại nhiều ứng dụng đáng kể, và trong số đó, trí tuệ nhân tạo
(Artificial Intelligence - AI) đóng một vai trò quan trọng.
Trí tuệ nhân tạo đã thúc đẩy sự phát triển và cải thiện nhiều khả năng
của máy tính, từ nhận dạng hình ảnh (nhận dạng con người, vân tay, …) , xử
lý ngôn ngữ tự nhiên đến dự đoán và phân tích dữ liệu phức tạp liên quan đến
các khối ngành kinh tế, thời tiết, đời sống... Một trong những lĩnh vực tiềm
năng của trí tuệ nhân tạo là trong việc phân cụm và phân loại dữ liệu, và đồ án
chuyên ngành này, nhóm em sẽ tập trung vào ứng dụng mô hình mạng CNN
(Convolutional Neural Network) và thuật toán K-means để thực hiện phân
cụm tình trạng hoa quả.
Đồ án chuyên ngành của chúng em gồm 4 phần chính:
- Chương 1: Tổng quan
- Chương 2: Thực nghiệm
- Chương 3: Kiểm thử
Trong quá trình hoàn thành đồ án, với trình độ kiến thức chuyên môn
chưa nhiều, kinh nghiệm thực tế còn ít và thời gian có hạn nên đồ án của
nhóm em không thể tránh được những thiếu sót. Do đó, nhóm em kính mong
được sự chỉ bảo thêm của các thầy, cô và đóng góp của các bạn để em được
hoàn thiện hơn.
Nhóm em xin chân thành cảm
ơn!
3
LỜI CẢM ƠN
Để có được một đồ án chuyên ngành chỉnh chu và đạt được kết quả tốt
đẹp, em đã nhận được sự giúp đỡ, hỗ trợ của các thầy cô bạn bè. Với tình cảm
sâu sắc, chân thành của mình, cho phép em được bày tỏ lòng biết ơn sâu sắc
đến tất cả các thầy cô và bạn bè đã nhiệt tình giúp đỡ, góp ý
Đặc biệt tôi xin gửi lời cảm ơn chân thành nhất tới cô giáo - Th.S Ngô
Thị Thanh Hoà đã quan tâm giúp đỡ, trực tiếp hướng dẫn tôi hoàn thành tốt
đồ án này trong thời gian qua.
4
CHƯƠNG 1: TỔNG QUAN
1.1 Tổng quan về đề tài
1.1.1 Lý do chọn đề tài
Lý do lựa chọn đề tài này bao gồm:
Áp dụng công nghệ trí tuệ nhân tạo hiện đại vào lĩnh vực nông nghiệp
có ý nghĩa thiết thực.
CNN và K-means là những kỹ thuật phù hợp với bài toán phân loại
hình ảnh và phân cụm dữ liệu.
Bài toán phân loại chất lượng hoa quả là thực tế và có ý nghĩa kinh tế
lớn.
Kết quả nghiên cứu có thể ứng dụng vào thực tế để nâng cao hiệu quả
sản xuất.
Đề tài phù hợp với chuyên ngành công nghệ thông tin và trình độ kiến
thức của nhóm.
Như vậy, đề tài này có ý nghĩa khoa học và thực tiễn cao, phù hợp với xu
thế ứng dụng trí tuệ nhân tạo trong nông nghiệp hiện nay. Kết quả nghiên cứu
có thể góp phần nâng cao năng suất và chất lượng sản xuất hoa quả tại Việt
Nam.
1.1.2 Bài toán phân loại và phân cụm hoa quả
a, Bài toán
Bài toán mà nhóm đặt ra cần giải quyết là:
Đầu vào: Tập hợp các ảnh hoa quả có kích thước và góc chụp xác định
(hướng từ trên xuống).
Đầu ra:
Xác định chính xác loại hoa quả trong ảnh.
Phân cụm các ảnh hoa quả thành các nhóm dựa trên tình trạng chất
lượng.
Mục tiêu:
5
Phân loại 15 loại hoa quả đã được xác định với độ chính xác cao
Phân cụm hoa quả thành các cụm có mức độ giống nhau nhất
Giới hạn:
Số lượng hoa quả: 15 loại
Số lượng ảnh: khoảng 44000 ảnh
Kích thước ảnh đầu vào: ngẫu nhiên pixel.
b, Ý nghĩa và ứng dụng thực tế
Việc áp dụng công nghệ trí tuệ nhân tạo để phân loại và phân cụm chất lượng
hoa quả có ý nghĩa rất lớn:
Giúp đánh giá chất lượng nông sản một cách khách quan, khoa học
thay vì dựa trên cảm quan của con người.
Tiết kiệm nhân công và thời gian cần thiết để phân loại sản phẩm thủ
công.
Nâng cao năng suất và hiệu quả trong quá trình sơ chế, đóng gói và
xuất khẩu hoa quả.
Giảm tổn thất do các sản phẩm bị loại bỏ không đáp ứng tiêu chuẩn
xuất khẩu.
Cung cấp thông tin phản hồi để cải tiến quy trình canh tác, bảo quản
sau thu hoạch.
Áp dụng vào hệ thống kiểm soát chất lượng tự động trong các nhà máy
chế biến.
Một số ứng dụng tiềm năng của bài toán bao gồm:
Hệ thống phân loại chất lượng sản phẩm tự động trên dây chuyền đóng
gói.
Phần mềm hỗ trợ ra quyết định phân loại cho các nhân viên kiểm định.
Camera phân tích hoa quả tự động tại các trạm kiểm soát biên giới.
Ứng dụng di động hỗ trợ đánh giá chất lượng hoa quả tại vườn cho
nông dân.
6
Phần mềm phân tích dữ liệu về chất lượng đầu vào để cải tiến quy trình
sản xuất.
Như vậy, việc ứng dụng công nghệ phân loại và phân cụm AI vào lĩnh vực
nông nghiệp có thể mang lại hiệu quả lớn về mặt kinh tế và xã hội.
1.1.3 Một số kỹ thuật hiện có để giải quyết bài toán
a, Kỹ thuật phân loại truyền thống
Trước khi có sự bùng nổ của học sâu (deep learning), các bài toán phân
loại hình ảnh phổ biến được giải quyết bằng các kỹ thuật truyền thống như:
Máy vectơ hỗ trợ (SVM): sử dụng một số đặc trưng của ảnh như
histogram, SIFT, HOG...để huấn luyện các siêu phẳng phân chia các lớp.
SVM hiệu quả với bài toán phân loại ít lớp, nhưng không mạnh với các dữ
liệu phức tạp như ảnh.
Cây quyết định: các thuật toán như Random Forest, Gradient Boosted
Trees... dựa trên việc kết hợp nhiều cây quyết định để tăng độ chính xác.
Chúng linh hoạt với dữ liệu ảnh nhưng độ chính xác thấp hơn so với deep
learning.
K láng giềng gần nhất (KNN): so sánh khoảng cách tính năng của ảnh
input với các mẫu trong training set để quyết định nhãn cho ảnh input. Đơn
giản nhưng không hiệu quả với tập dữ liệu lớn.
Nhìn chung, các kỹ thuật truyền thống có những hạn chế về độ chính
xác và khả năng tổng quát hóa khi áp dụng cho các bài toán phân loại hình
ảnh phức tạp như hoa quả. Chúng cần sự thiết kế tính năng thủ công và hiệu
năng kém hơn so với học sâu trên các tập dữ liệu lớn và phức tạp.
b, Kỹ thuật phân loại hiện đại
Các kỹ thuật phân loại hình ảnh hiện đại chủ yếu dựa trên học sâu
(deep learning) với 2 hướng tiếp cận chính:
Mạng nơ-ron tích chập (CNN): sử dụng các lớp tích chập để tự động
trích xuất đặc trưng từ hình ảnh, sau đó kết hợp với các lớp kết nối đầy đủ
7
(FC) để dự đoán xác suất cho mỗi lớp. CNN rất hiệu quả trong việc học các
đặc trưng cấp thấp từ ảnh và phù hợp với bài toán phân loại hình ảnh.
Học chuyển giao (Transfer learning): sử dụng các mô hình CNN đã
được huấn luyện sẵn trên tập dữ liệu lớn (ImageNet, COCO...), sau đó tinh
chỉnh lại trên tập dữ liệu cụ thể. Giúp tận dụng tri thức đã học và rút ngắn quá
trình huấn luyện.
So với các phương pháp truyền thống, học sâu cho phép mô hình tự học
các đặc trưng phức tạp từ dữ liệu mà không cần thiết kế thủ công. CNN và
Transfer Learning đạt độ chính xác rất cao trong nhiều bài toán nhận dạng và
phân loại hình ảnh thực tế.
Một số mô hình CNN phổ biến:
VGGNet: sử dụng nhiều lớp CNN 3x3 liên tiếp để tăng độ sâu.
ResNet: sử dụng kết nối tắt (shortcut connection) để khắc phục hiện
tượng biến mất gradient.
Inception: sử dụng các Inception block với nhiều kích thước lọc khác
nhau.
Xception: dùng depthwise separable convolution thay cho standard
convolution.
MobileNet: thiết kế nhẹ, hiệu quả cho thiết bị di động.
Một số lợi ích của CNN:
Khả năng học tính năng tự động từ dữ liệu, không cần kỹ thuật thiết kế
thủ công.
Xử lý tốt các dữ liệu hình ảnh, video nhờ các tầng tích chập.
Tốc độ huấn luyện nhanh trên GPU. Có thể huấn luyện trên lượng dữ
liệu rất lớn.
Khả năng tổng quát hóa tốt khi kết hợp regularization và data
augmentation.
c, Thuật toán phân cụm K-means
8
Trong bài toán phân cụm chất lượng hoa quả, một kỹ thuật phù hợp là
thuật toán K-means. Đây là một thuật toán phân cụm không giám sát, không
yêu cầu nhãn của dữ liệu.
Thuật toán K-means hoạt động như sau:
Khởi tạo ngẫu nhiên K điểm làm centroid ban đầu của các cụm
Lặp lại cho đến khi hội tụ:
Gán mỗi điểm dữ liệu vào cụm có khoảng cách nhỏ nhất với
centroid
Cập nhật lại vị trí centroid bằng cách lấy trung bình cộng của các
điểm trong cụm
Trả về các nhãn cụm cho mỗi điểm dữ liệu
Ưu điểm của K-means:
Thuật toán đơn giản, dễ hiểu và dễ cài đặt
Tốc độ huấn luyện nhanh cho tập dữ liệu lớn
Có thể áp dụng trên nhiều loại dữ liệu khác nhau
Không yêu cầu dữ liệu được gán nhãn
Tuy nhiên thuật toán cũng có một số nhược điểm cần lưu ý:
Kết quả phụ thuộc vào lựa chọn ban đầu của centroid
Dễ bị mắc kẹt ở điểm cực tiểu cục bộ
Không hiệu quả với các cụm có hình dạng phức tạp
Cần chỉ định số lượng cụm K trước
Tóm lại, K-means là một lựa chọn phù hợp cho bài toán phân cụm chất lượng
hoa quả do tính đơn giản, hiệu quả và khả năng mở rộng tốt.
d, Thách thức và hướng phát triển
Mặc dù vậy, việc xây dựng hệ thống phân loại và phân cụm hoa quả thực tế
vẫn còn một số thách thức:
Xây dựng được tập dữ liệu ảnh hoa quả đủ lớn, đa dạng các loại và chất
lượng.
9
Mô hình CNN cần kiến trúc và siêu tham số phù hợp để đạt độ chính
xác cao.
Cải thiện khả năng tổng quát hóa của mô hình trên các điều kiện mới.
Điều chỉnh thuật toán K-means để xử lý tốt các cụm dữ liệu phức tạp.
Xử lý các yếu tố gây nhiễu trong thực tế như ánh sáng, góc chụp, nền
phức tạp...
Tối ưu hệ thống cho thiết bị nhúng và xử lý real-time.
Một số hướng phát triển tiềm năng:
Kết hợp nhiều mô hình CNN khác nhau thông qua bỏ phiếu mềm hoặc
tập hợp.
Sử dụng các kỹ thuật tăng cường dữ liệu ảnh để nâng cao độ chính xác.
Nghiên cứu các thuật toán phân cụm mới như DBSCAN, Mean-shift để
xử lý tốt hơn các cụm dữ liệu không đều.
Triển khai giải pháp trên nền tảng di động và web để dễ dàng sử dụng.
Tích hợp với các cảm biến và thiết bị IoT để thu thập dữ liệu đầu vào.
Như vậy, với sự phát triển mạnh mẽ của học máy và khả năng thu thập dữ
liệu lớn, bài toán phân loại và phân cụm chất lượng hoa quả có thể đạt độ
chính xác ngày càng cao hơn trong thực tế. Kết quả nghiên cứu sẽ góp phần
đưa công nghệ trí tuệ nhân tạo vào ứng dụng nông nghiệp, nâng cao năng suất
và giá trị hoa quả.
1.2 Cơ sở lý thuyết
1.2.1 Mô hình mạng CNN (Convolutional neural networks)
a, Tổng quan và khái niệm cơ bản
Kiến trúc truyền thống của một mạng CNN ― Mạng neural tích chập
(Convolutional neural networks), còn được biết đến với tên CNNs, là một
dạng mạng neural được cấu thành bởi các tầng sau:
10
Tầng tích chập và tầng pooling có thể được hiệu chỉnh theo các siêu
tham số (hyperparameters) được mô tả.
b, Các kiểu tầng
Tầng tích chập (CONV) ― Tầng tích chập (CONV) sử dụng các bộ
lọc để thực hiện phép tích chập khi đưa chúng đi qua đầu vào I theo các chiều
của nó. Các siêu tham số của các bộ lọc này bao gồm kích thước bộ lọc F và
độ trượt (stride) S. Kết quả đầu ra O được gọi là feature map hay activation
map.
Lưu ý: Bước tích chập cũng có thể được khái quát hóa cả với trường
hợp một chiều (1D) và ba chiều (3D).
Pooling (POOL) ― Tầng pooling (POOL) là một phép downsampling,
thường được sử dụng sau tầng tích chập, giúp tăng tính bất biến không gian.
Cụ thể, max pooling và average pooling là những dạng pooling đặc biệt, mà
tương ứng là trong đó giá trị lớn nhất và giá trị trung bình được lấy ra.
11
Từng phép pooling chọn giá Từng phép pooling tính trung
Chức năng trị lớn nhất trong khu vực mà bình các giá trị trong khu vực mà
nó đang được áp dụng nó đang được áp dụng
Minh họa
Nhận xét Bảo toàn các đặc trưng đã Giảm kích thước feature map
phát hiện và được sử dụng Được sử dụng trong mạng
thường xuyên LeNet
Fully Connected (FC) ― Tầng kết nối đầy đủ (FC) nhận đầu vào là
các dữ liệu đã được làm phẳng, mà mỗi đầu vào đó được kết nối đến tất cả
neuron. Trong mô hình mạng CNNs, các tầng kết nối đầy đủ thường được tìm
thấy ở cuối mạng và được dùng để tối ưu hóa mục tiêu của mạng ví dụ như độ
chính xác của lớp.
12
c, Các siêu tham số của bộ lọc
Tầng tích chập chứa các bộ lọc mà rất quan trọng cho ta khi biết ý
nghĩa đằng sau các siêu tham số của chúng.
Các chiều của một bộ lọc ― Một bộ lọc kích thước F × F áp dụng lên
đầu vào chứa C kênh (channels) thì có kích thước tổng kể là F × F × C thực
hiện phép tích chập trên đầu vào kích thước I × I × C và cho ra một feature
map (hay còn gọi là activation map) có kích thước O × O × 1.
Stride ― Đối với phép tích chập hoặc phép pooling, độ trượt S ký hiệu
số pixel mà cửa sổ sẽ di chuyển sau mỗi lần thực hiện phép tính.
13
Zero-padding ― Zero-padding là tên gọi của quá trình thêm P số
không vào các biên của đầu vào. Giá trị này có thể được lựa chọn thủ công
hoặc một cách tự động bằng một trong ba những phương pháp mô tả bên
dưới:
Minh họa
14
Còn được gọi là
'half' padding
I −F + P start + Pend
O= S
+1
Lưu ý: Trong một số trường hợp, Pstart = Pend ≜ P, ta có thể thay thế
Pstart + Pend bằng $2P trong công thức trên.
Hiểu về độ phức tạp của mô hình ― Để đánh giá độ phức tạp của
một mô hình, cách hữu hiệu là xác định số tham số mà mô hình đó sẽ có.
Trong một tầng của mạng neural tích chập, nó sẽ được tính toán như sau:
CONV POOL FC
15
Minh họa
Trường thụ cảm ― Trường thụ cảm (receptive field) tại tầng k là vùng
được ký hiệu Rk × Rk của đầu vào mà những pixel của activation map thứ k có
thể "nhìn thấy". Bằng cách gọi Fj là kích thước bộ lọc của tầng j và Si là giá trị
16
độ trượt của tầng i và để thuận tiện, ta mặc định S0 = 1, trường thụ cảm của
tầng k được tính toán bằng công thức:
k j−1
R k =1+ ∑ ❑ ( Fj−1 ) ∏ ❑ Si
j=0 i=0
17
Softmax ― Bước softmax có thể được coi là một hàm logistic tổng
quát lấy đầu vào là một vector chứa các giá trị x ∈ Rn và cho ra là một vector
gồm các xác suất p ∈ Rn thông qua một hàm softmax ở cuối kiến trúc. Nó
được định nghĩa như sau:
xi
e
pi= n
P= p 1 ⋮ pn với
∑ ❑ e xi
j=1
18
kỳ động vật khác. Việc phân loại này được thực hiện bằng cách xuất ra xác
suất của hình ảnh thuộc từng lớp:
Localization: Mục đích chính của localization là tạo ra một hộp giới
hạn mô tả vị trí của đối tượng trong hình ảnh (được gọi là biên). Đầu ra bao
gồm một nhãn lớp và một hộp giới hạn. Tác vụ này có thể được sử dụng trong
cảm biến để xác định xem một đối tượng ở bên trái hay bên phải của màn
hình:
Detection: Nhiệm vụ này bao gồm thực hiện localization trên tất cả các
đối tượng trong ảnh. Các đầu ra bao gồm nhiều hộp giới hạn, cũng như nhiều
nhãn lớp (một cho mỗi hộp). Nhiệm vụ này được ứng dụng trong việc chế tạo
ô tô tự lái, với mục tiêu là có thể xác định vị trí các biển báo giao thông,
19
đường, ô tô khác, người đi bộ và bất kỳ đối tượng nào khác có thể phù hợp để
đảm bảo trải nghiệm lái xe an toàn…
1.2.2 K Means
a, Tổng quan
Trong thuật toán K-means clustering, chúng ta không biết nhãn (label)
của từng điểm dữ liệu. Mục đích là làm thể nào để phân dữ liệu thành các
cụm (cluster) khác nhau sao cho dữ liệu trong cùng một cụm có tính chất
giống nhau.
20
Ý tưởng đơn giản nhất về cluster (cụm) là tập hợp các điểm ở gần
nhau trong một không gian nào đó (không gian này có thể có rất nhiều chiều
trong trường hợp thông tin về một điểm dữ liệu là rất lớn). Hình bên dưới là
một ví dụ về 3 cụm dữ liệu (cluster).
Giả sử mỗi cluster có một điểm đại diện (center) màu vàng. Và
những điểm xung quanh mỗi center thuộc vào cùng nhóm với center đó.
Một cách đơn giản nhất, xét một điểm bất kỳ, ta xét xem điểm đó gần với
center nào nhất thì nó thuộc về cùng nhóm với center đó.
Để hiểu hơn về việc phân nhóm ta lấy ví dụ 1 bài toán: Trên một
vùng biển hình vuông lớn có ba đảo hình vuông, tam giác, và tròn màu vàng
như hình trên. Một điểm trên biển được gọi là thuộc lãnh hải của một đảo
nếu nó nằm gần đảo này hơn so với hai đảo kia. Hãy xác định ranh giới lãnh
hải của các đảo.
Hình dưới đây là một hình minh họa cho việc phân chia lãnh hải nếu
có 5 đảo khác nhau được biểu diễn bằng các hình tròn màu đen:
21
Phân vùng lãnh hải của mỗi đảo. Các vùng khác nhau có màu sắc khác nhau.
Chúng ta thấy rằng đường phân định giữa các lãnh hải là các đường
thẳng (chính xác hơn thì chúng là các đường trung trực của các cặp điểm gần
nhau). Vì vậy, lãnh hải của một đảo sẽ là một hình đa giác.
Cách phân chia này trong toán học được gọi là Voronoi Diagram.
Trong không gian ba chiều, lấy ví dụ là các hành tinh, ta có thể gọi lãnh
không của mỗi hành tinh sẽ là một đa diện. Trong không gian nhiều chiều hơn
chúng ta sẽ có những siêu đa diện (hyperpolygon).
b, Ý nghĩa toán học thuật toán Kmeans
Trước khi tóm tắt thuật toán ta hãy cùng phân tích toán học cho thuật
toán. Mục đích cuối cùng của thuật toán phân nhóm này là: từ dữ liệu đầu vào
và số lượng nhóm chúng ta muốn tìm, hãy chỉ ra center của mỗi nhóm và
phân các điểm dữ liệu vào các nhóm tương ứng. Giả sử thêm rằng mỗi điểm
dữ liệu chỉ thuộc vào đúng một nhóm.
Một số ký hiệu toán học
22
Giả sử có N điểm dữ liệu là X = [x1, x2, …, xN ] ∈ Rd×N và K < N là
số cluster chúng ta muốn phân chia. Chúng ta cần tìm các center m1, m2, …,
1
mK ∈ Rd× và label của mỗi điểm dữ liệu.
Một số lưu ý: các số vô hướng được biểu diễn dưới dạng không in
đậm và có thể được viết hoa, ví dụ x 1, x2 Các vector được biểu diễn bằng các
chữ cái viết thường in đậm ví dụ a, b. Các ma trận được biểu diễn bởi các
chữ viết hoa in đậm ví dụ X, M, Y.
Với mỗi điểm dữ liệu xi đặt yi = [yi1, yi2, … , yiK] và label vector của
Điều này có nghĩa là có đúng một phần tử của vector y i là bằng 1 tương ứng
với cluster của xi, các phần tử còn lại bằng 0. Ví dụ nếu một điểm dữ liệu có
label vector là [1, 0, 0, ..., 0] nó thuộc cluster 1, là [0, 1, 0, ..., 0] thì nó thuộc
vào cluster 2, ... cách mã hóa label như vậy được gọi là biểu diễn one-hot.
Ràng buộc của yi có thể viết dưới dạng toán học như sau:
K
N K
2
L(Y,M) = ∑ ❑ ∑ ❑ yij ‖ xi−mk ‖
i=1 j=1 2
Trong đó Y = [y1; y2;...; yN], M = [m1, m2,...mK] lần lượt là các ma trận
được tạo bởi label vector của mỗi điểm dữ liệu và center của mỗi cluster.
Hàm số mất mát trong bài toán K-means clustering của chúng ta là hàm
L(Y,M) với ràng buộc như được nêu trong phương trình (1).
Tóm lại, chúng ta cần tối ưu bài toán sau:
N K
2
Y,M = arg ∑ ❑ ∑ ❑ yij ‖ xi−mk ‖ (2)
i=1 j=1 2
K
Trong đó arg min là giá trị của biến số để hàm số đó đạt giá trị nhỏ
nhất. Ví dụ nếu f(x) = x2 – 2x + 1 = (x - 1)2 thì giá trị nhỏ nhất của hàm số này
bằng 0, đạt được khi x = 1. Trong ví dụ này minx f(x) = 0 và arg minx f(x) =
1. Mặt khác nếu cho x 1 = 0, x2 = 4, x3 = 7 thì ta nói arg minx x i = 1 vì 1 là chỉ
số để xi đạt giá trị nhỏ nhất ( = 0 ). Biến số viết bên dưới min là biến số
chúng ta cần tối ứu. Trong các bài toán tối ưu, ta thường quan tâm tới arg min
hơn là min.
Thuật toán tối ưu hàm mất mát
Bài toán (2) là một bài toán khó tìm điểm tối ưu vì nó có thêm các
điều kiện ràng buộc. Bài toán này thuộc loại mix-integer programming (điều
kiện biến là số nguyên) - là loại rất khó tìm nghiệm tối ưu toàn cục (global
optimal point, tức nghiệm làm cho hàm mất mát đạt giá trị nhỏ nhất có thể).
24
Tuy nhiên, trong một số trường hợp chúng ta vẫn có thể tìm được phương
pháp để tìm được nghiệm gần đúng hoặc điểm cực tiểu. (Nếu chúng ta vẫn
nhớ chương trình toán ôn thi đại học thì điểm cực tiểu chưa chắc đã phải là
điểm làm cho hàm số đạt giá trị nhỏ nhất).
Một cách đơn giản để giải bài toán (2) là giải xen kẽ Y và M khi biến
còn lại được cố định. Đây là một thuật toán lặp, cũng là kỹ thuật phổ biến khi
giải bài toán tối ưu. Chúng ta sẽ lần lượt giải quyết hai bài toán sau đây:
Cố định M, tìm Y
Giả sử đã tìm được các centers, hãy tìm các label vector để hàm mất
mát đạt giá trị nhỏ nhất. Điều này tương đương với việc tìm cluster cho mỗi
điểm dữ liệu.
Khi các centers là cố định, bài toán tìm label vector cho toàn bộ dữ
liệu có thể được chia nhỏ thành bài toán tìm label vector cho từng điểm dữ
liệu xi như sau:
K
2
yi = arg ∑ ❑ yij ‖ xi−mj ‖ (3)
j=1 2
K
Vì chỉ có một phần tử của label vector yi = 1 nên bài toán (3) có thể
tiếp tục được viết dưới dạng đơn giản hơn:
2
j = arg ‖ xi−mj ‖ 2
2
Vì ‖ xi−mj ‖ 2 chính là bình phương khoảng cách tính từ điểm xi tới mj,
a có thể kết luận rằng mỗi điểm xi thuộc vào cluster có center gần nó nhất!
Từ đó ta có thể dễ dàng suy ra label vector của từng điểm dữ liệu.
Cố định Y, tìm M
Giả sử đã tìm được cluster cho từng điểm, hãy tìm center mới cho mỗi
cluster để hàm mất mát đạt giá trị nhỏ nhất.
25
Một khi chúng ta đã xác định được label vector cho từng điểm dữ liệu,
bài toán tìm center cho mỗi cluster được rút gọn thành:
N
2
mj = arg ∑ ❑ yij ‖ xi−mj ‖
i=1 2
Tới đây, ta có thể tìm nghiệm bằng phương pháp giải đạo hàm bằng 0,
vì hàm cần tối ưu là một hàm liên tục và có đạo hàm xác định tại mọi điểm.
Và quan trọng hơn, hàm này là hàm convex (lồi) theo mj nên chúng ta sẽ tìm
được giá trị nhỏ nhất và điểm tối ưu tương ứng.
Đặt l(mj) là hàm bên trong dấu arg min, ta có đạo hàm:
N
∂ l(mj)
=2 ∑ ❑ yij(mj−xi)
∂ mj i=1
N N
mj ∑ ❑ yij=∑ ❑ yijxi
i=1 i=1
∑ ❑ yijxi
→ mj= i=1N
∑ ❑ yij
i=1
Nếu để ý một chút, chúng ta sẽ thấy rằng mẫu số chính là phép đếm số
lượng các điểm dữ liệu trong cluster j. Còn tử số chính là tổng các điểm dữ
liệu trong cluster j.
Hay nói một cách đơn giản hơn nhiều: mj là trung bình cộng của các
điểm trong cluster j.
c, Tóm tắt thuật toán
Đầu vào: Dữ liệu X và số lượng cluster cần tìm K.
Đầu ra: Các center M và label vector cho từng điểm dữ liệu Y.
1. Chọn K điểm bất kỳ làm các center ban đầu.
2. Phân mỗi điểm dữ liệu vào cluster có center gần nó nhất.
3. Nếu việc gán dữ liệu vào từng cluster ở bước 2 không thay đổi
so với vòng lặp trước nó thì ta dừng thuật toán.
26
4. Cập nhật center cho từng cluster bằng cách lấy trung bình cộng
của tất các các điểm dữ liệu đã được gán vào cluster đó sau
bước 2.
5. Quay lại bước 2.
Chúng ta có thể đảm bảo rằng thuật toán sẽ dừng lại sau một số hữu
hạn vòng lặp. Thật vậy, vì hàm mất mát là một số dương và sau mỗi bước 2
hoặc 3, giá trị của hàm mất mát bị giảm đi. Theo kiến thức về dãy số trong
chương trình cấp 3: nếu một dãy số giảm và bị chặn dưới thì nó hội tụ! Hơn
nữa, số lượng cách phân nhóm cho toàn bộ dữ liệu là hữu hạn nên đến một lúc
nào đó, hàm mất mát sẽ không thể thay đổi, và chúng ta có thể dừng thuật
toán tại đây.
1.3 Các thư viện sử dụng trong bài toán
Trong quá trình giải quyết bài toán, chúng em sử dụng một số thư viện
Python phổ biến sau:
1. Numpy
Numpy là thư viện cung cấp cấu trúc dữ liệu mảng đa chiều
(ndarray) và các hàm đi kèm để thao tác với chúng.
Hỗ trợ tính toán ma trận nhanh chóng dựa trên C và Fortran.
Trong bài toán này, Numpy được dùng để lưu trữ và xử lý các mảng
pixel của ảnh, cũng như vector đặc trưng.
2. Pandas
Pandas cung cấp cấu trúc dữ liệu DataFrame để lưu trữ và thao tác
dữ liệu dạng bảng.
Hỗ trợ các phương thức xử lý như groupby, pivot, join, merge.
Trong bài toán, Pandas được dùng để lưu các siêu dữ liệu về ảnh
cũng như kết quả dự đoán.
3. Matplotlib
Thư viện vẽ các loại đồ thị như đường, cột, mặt phẳng, hình 3D.
27
Cung cấp giao diện lập trình ở cấp cao cho các chức năng của thư
viện vẽ matplotlib của Python.
Sử dụng để trực quan hóa kết quả phân tích, đồ thị so sánh các mô
hình.
4. Seaborn
Dựa trên matplotlib, cung cấp các lớp vẽ đồ thị thống kê cao cấp
hơn.
Hỗ trợ vẽ các biểu đồ phân phối, tương quan, phân tích hồi quy.
Giúp phân tích và trực quan hóa phân phối của dữ liệu.
5. Scikit-learn
Thư viện học máy phổ biến nhất trong Python.
Bao gồm các thuật toán phân loại, hồi quy, chụm cụm.
Trong bài toán dùng KMeans, SVM cho phân cụm và phân loại.
6. Keras
Keras là thư viện deep learning để xây dựng và huấn luyện mô hình
học sâu.
Cung cấp các lớp mạng nơ-ron, tích chập, tối ưu hóa, huấn luyện mô
hình.
Sử dụng để xây dựng và huấn luyện mô hình CNN.
7. OpenCV
Thư viện xử lý ảnh và thị giác máy tính mã nguồn mở.
Hỗ trợ xử lý ảnh cơ bản: cắt, xoay, thay đổi kích thước.
Dùng để tiền xử lý ảnh trước khi đưa vào mô hình.
28
CHƯƠNG 2: THỰC NGHIỆM
2.1 Dataset
- Tên dataset: Fruit Recognition
- Kích thước: 9 GB
- Số lượng ảnh: 44406 ảnh được gán nhãn
- Kích cỡ từng ảnh: 320×258 pixel
- Số loại hoa quả: 15 loại
- Link dataset: Fruit Recognition (kaggle.com)
2.2 Các mô hình sẽ sử dụng trong bài toán
2.2.1 Mô hình tự tạo
Trong phần thực nghiệm, chúng em sẽ xây dựng một mô hình CNN tự
tạo để phân loại các loại hoa quả. Kiến trúc mô hình bao gồm:
- Các lớp Convolutional 2D với filter size 3x3 và số lượng filter tăng
dần từ 32 đến 64
- Các lớp MaxPool2D để giảm chiều dữ liệu
- Lớp Flatten để chuyển các feature maps thành vector đặc trưng
- Lớp Dense với 128 neuron và hàm aktivasi ReLU
- Lớp Dense đầu ra với 15 neuron tương ứng 15 nhãn hoa quả và hàm
aktivasi Softmax
Mô hình sẽ được huấn luyện trên tập dataset gồm 44406 ảnh hoa quả
được gán nhãn với 15 loại hoa quả khác nhau.
2.2.2 Mô hình có sẵn
Ngoài việc xây dựng mô hình CNN tự tạo, chúng em cũng sẽ thử nghiệm
huấn luyện một số kiến trúc mô hình CNN có sẵn như sau:
DenseNet: Sử dụng các khối dense block với kết nối đặc trưng giữa các
lớp để truyền tải thông tin hiệu quả. Trong mỗi dense block, mỗi lớp
được kết nối trực tiếp với tất cả các lớp phía trước thông qua việc ghép
nối các bản đồ đặc trưng (feature maps). Phiên bản DenseNet121,
29
DenseNet169 và DenseNet201 có độ sâu tăng dần từ 121 lớp đến 201
lớp.
EfficientNet: Dòng mô hình được thiết kế để cân bằng tối ưu giữa
chiều rộng (width), chiều sâu (depth) và độ phân giải (resolution) của
ảnh đầu vào. Các phiên bản EfficientNetB0 đến EfficientNetB7 có số
lượng tham số và độ sâu mô hình tăng dần, cho phép cân bằng giữa độ
chính xác và chi phí tính toán. Các phiên bản càng cao thì càng đạt độ
chính xác cao hơn nhưng cũng tốn nhiều tài nguyên tính toán hơn.
InceptionResNetV2: Kết hợp kiến trúc Inception module với ResNet
architecture để tăng hiệu suất, sử dụng các kết nối tắt (shortcut
connection) giúp lưu truyền gradient hiệu quả hơn, giảm tình trạng biến
mất gradient trong quá trình lan truyền ngược.
InceptionV3: Sử dụng các Inception module song song với nhau để
học được nhiều chiều biểu diễn khác nhau từ cùng một đầu vào. Mỗi
Inception module bao gồm nhiều lớp tích chập và pooling cỡ khác nhau
được áp dụng song song.
MobileNet: Thiết kế nhẹ, hiệu quả cho mobile và embedded vision. Sử
dụng depthwise separable convolutions thay cho standard convolutions
giúp giảm đáng kể số lượng tham số cần huấn luyện.
NASNetMobile: Kiến trúc được tối ưu bằng thuật toán Neural
Architecture Search (NAS), tự động tìm ra kiến trúc tốt nhất thông qua
quá trình thử nghiệm nhiều kiến trúc khác nhau.
ResNet: Sử dụng kết nối tắt (residual connection) để khắc phục hiện
tượng biến mất gradient trong quá trình lan truyền ngược. Phiên bản
ResNet 101 và ResNet 152 có độ sâu lên tới hơn 100 lớp nhờ vào các
kết nối tắt này.
VGG16, VGG19: Mô hình sử dụng nhiều lớp tích chập 3x3 liên tiếp để
tăng độ sâu mô hình. VGG19 có thêm một số lớp so với VGG16 nên độ
sâu và độ chính xác cao hơn.
30
Xception: Dựa trên kiến trúc Inception, sử dụng depthwise separable
convolution thay vì standard convolution để giảm số lượng tham số cần
huấn luyện.
Việc sử dụng các mô hình đã được pre-trained trên tập dữ liệu lớn sẽ giúp
nâng cao khả năng phân loại và giảm thời gian huấn luyện. Chúng em sẽ huấn
luyện lại các lớp cuối cùng của mô hình để phân loại các lớp hoa quả. Kết quả
của các thử nghiệm sẽ được trình bày chi tiết ở phần thực nghiệm chương 3.
Như vậy, chúng em hy vọng việc kết hợp huấn luyện cả mô hình tự tạo và
mô hình có sẵn sẽ giúp tìm ra phương án tốt nhất cho bài toán phân loại hoa
quả.
2.3 Thực nghiệm
2.3.1 Huấn luyện mô hình (Sử dụng CNN)
Do cấu hình của máy tính bị hạn chế, nên việc thực hiện huấn luyện mô
hình sẽ được thực hiện trên Kaggle.
In [1]:
# Import các thư viện cần thiết
import pandas as pd
import numpy as np
import seaborn as sns
import os
import cv2
import matplotlib.pyplot as plt
import random
import time
from sklearn.metrics import classification_report, confusion_matrix, accuracy_score
from sklearn.model_selection import train_test_split
import keras
from keras import Sequential
from keras.layers import Activation, Dropout, Flatten, Dense, Conv2D, MaxPooling2D
from keras.utils import to_categorical
from keras.callbacks import EarlyStopping, ModelCheckpoint
import gc
from IPython.display import Markdown, display
31
if only_path == False:
images = []
# Duyệt qua tất cả các file trong thư mục
for filename in os.listdir(folder):
# Đọc hình ảnh bằng thư viện matplotlib
img = plt.imread(os.path.join(folder, filename))
# Nếu hình ảnh không rỗng, thêm vào danh sách hình ảnh
if img is not None:
images.append(img)
return images
else:
path = []
# Duyệt qua tất cả các file trong thư mục
for filename in os.listdir(folder):
img_path = os.path.join(folder, filename)
# Nếu đường dẫn không rỗng, thêm vào danh sách đường dẫn với nhãn
if img_path is not None:
path.append([label, img_path])
return path
In [2]:
# Khởi tạo danh sách rỗng để lưu trữ thông tin hình ảnh
images = []
# Đường dẫn đến thư mục chứa dữ liệu về trái cây
dirp = "/kaggle/input/fruit-recognition-khmt02/"
# Duyệt qua tất cả các tệp và thư mục trong thư mục chứa dữ liệu
for f in os.listdir(dirp):
# Kiểm tra xem tệp có chứa hình ảnh PNG không
if "png" in os.listdir(dirp+f)[0]:
# Nếu có, thêm đường dẫn của hình ảnh với nhãn tương ứng vào danh sách
images += load_images_from_folder(dirp+f, True, label=f)
else:
# Nếu không phải là tệp PNG, duyệt qua tất cả các thư mục con
for d in os.listdir(dirp+f):
# Thêm đường dẫn của hình ảnh trong thư mục con với nhãn tương ứng vào danh sách
images += load_images_from_folder(dirp+f+"/"+d, True, label=f)
# Tạo DataFrame từ danh sách hình ảnh và đặt tên cột là "fruit" và "path"
df = pd.DataFrame(images, columns=["fruit", "path"])
# Trộn dữ liệu để đảm bảo tính ngẫu nhiên, sử dụng random_state để có kết quả nhất quán
from sklearn.utils import shuffle
df = shuffle(df, random_state=0)
# Tạo một ánh xạ từ tên trái cây sang số nguyên để làm nhãn cho mô hình
mapper_fruit_names = dict(zip(fruit_names, [t for t in range(len(fruit_names))]))
df["label"] = df["fruit"].map(mapper_fruit_names)
32
df.head()
Out [2]:
In [3]:
# Tính số lượng hình ảnh cho mỗi loại trái cây
vc = df["fruit"].value_counts()
Out [3]:
33
In [4]:
# Tạo một lưới 4x5 subplot với kích thước tổng cộng là 15x15
fig, axes = plt.subplots(nrows=4, ncols=5, figsize=(15, 15),
subplot_kw={'xticks': [], 'yticks': []})
# Duyệt qua mỗi subplot và hiển thị hình ảnh tương ứng
for i, ax in enumerate(axes.flat):
# Hiển thị hình ảnh từ đường dẫn được lấy từ DataFrame
ax.imshow(plt.imread(df.path[i]))
In [5]:
34
# Đọc hình ảnh từ đường dẫn trong DataFrame
img = plt.imread(df.path[0])
# Hiển thị hình ảnh sau khi đã thay đổi kích thước
plt.imshow(resized_img)
plt.title("After resizing")
plt.show()
Out [5]:
35
In [6]:
def cut_df(df, number_of_parts, part):
"""
Cắt DataFrame thành các phần và trả về phần được chỉ định.
Parameters:
- df: DataFrame
DataFrame cần cắt.
- number_of_parts: int
Số lượng phần cần cắt DataFrame.
- part: int
Phần cần lấy từ DataFrame.
Returns:
- DataFrame
DataFrame là một phần của DataFrame được cắt.
"""
if part < 1:
print("Error, the part should be at least 1")
elif part > number_of_parts:
print("Error, the part cannot be higher than the number_of_parts")
number_imgs_each_part = int(df.shape[0] / number_of_parts)
idx1 = (part - 1) * number_imgs_each_part
idx2 = part * number_imgs_each_part
36
return df.iloc[idx1:idx2]
def load_img(df):
"""
Tải hình ảnh từ DataFrame.
Parameters:
- df: DataFrame
DataFrame chứa thông tin về hình ảnh.
Returns:
- tuple
Tuple chứa hai mảng NumPy: X (hình ảnh) và y (nhãn).
"""
img_paths = df["path"].values
img_labels = df["label"].values
X = []
y = []
for i, path in enumerate(img_paths):
img = plt.imread(path)
img = cv2.resize(img, (150, 150))
label = img_labels[i]
X.append(img)
y.append(label)
return np.array(X), np.array(y)
In [7]:
def create_model():
# Kích thước của hình ảnh đầu vào
shape_img = (150, 150, 3)
# Thêm lớp Conv2D với 32 bộ lọc, kích thước kernel (3,3), hàm kích hoạt là 'relu', và padding 'same'
model.add(Conv2D(filters=32, kernel_size=(3,3), input_shape=shape_img, activation='relu',
padding='same'))
model.add(MaxPooling2D(pool_size=(2, 2)))
# Thêm lớp Conv2D với 64 bộ lọc, kích thước kernel (3,3), hàm kích hoạt là 'relu', và padding 'same'
model.add(Conv2D(filters=64, kernel_size=(3,3), input_shape=shape_img, activation='relu',
padding='same'))
model.add(MaxPooling2D(pool_size=(2, 2)))
# Lặp lại quá trình thêm lớp Conv2D và MaxPooling2D nhiều lần
# để giảm kích thước của đầu ra và tăng độ sâu của mô hình
for _ in range(4):
model.add(Conv2D(filters=64, kernel_size=(3,3), input_shape=shape_img, activation='relu',
padding='same'))
model.add(MaxPooling2D(pool_size=(2, 2)))
# Thêm lớp Dense với 256 đơn vị và hàm kích hoạt 'relu'
model.add(Dense(256))
model.add(Activation('relu'))
37
# Áp dụng Dropout để giảm overfitting
model.add(Dropout(0.5))
# Thêm lớp Dense với số lượng đơn vị bằng số lượng loại trái cây và hàm kích hoạt 'softmax'
model.add(Dense(len(mapper_fruit_names)))
model.add(Activation('softmax'))
# Compile mô hình với hàm mất mát là categorical_crossentropy, tối ưu hóa bằng 'adam', và đánh
giá theo độ chính xác
model.compile(loss='categorical_crossentropy', optimizer='adam', metrics=['accuracy'])
return model
In [8]:
def from_categorical(lst):
"""
Chuyển đổi từ dạng one-hot encoding sang nhãn dạng số nguyên.
Parameters:
- lst: list
Danh sách chứa các vector one-hot encoding.
Returns:
- list
Danh sách chứa các nhãn dạng số nguyên.
"""
lst = lst.tolist()
lst2 = []
for x in lst:
lst2.append(x.index(max(x)))
return lst2
Parameters:
- y_test: array-like
Nhãn thực tế của dữ liệu kiểm tra.
- pred: array-like
Nhãn dự đoán từ mô hình.
Returns:
- None
"""
print(f"### Result of the predictions using {len(y_test)} test data ###\n")
y_test_class = from_categorical(y_test)
print("Classification Report:\n")
print(classification_report(y_test_class, pred))
print("\nConfusion Matrix:\n\n")
print(confusion_matrix(y_test_class, pred))
print("\n")
printmd(f"# Accuracy: {round(accuracy_score(y_test_class, pred), 5)}")
def plot_training(model):
38
"""
Vẽ đồ thị hiển thị kết quả của quá trình đào tạo mô hình.
Parameters:
- model: keras.models.Sequential
Mô hình đã được đào tạo.
Returns:
- None
"""
history = pd.DataFrame(model.history.history)
history[["accuracy", "val_accuracy"]].plot()
plt.title("Training results")
plt.xlabel("# epoch")
plt.show()
In [9]:
# Tạo mô hình
model = create_model()
# Danh sách để lưu trữ các lịch sử đào tạo từ các fold khác nhau
hists = []
In [10]:
# Thu gom rác sau khi train model để giải phóng bộ nhớ
39
gc.collect()
Out [10]:
In [11]:
# Tính thời gian đào tạo mô hình bằng cách lấy thời điểm hiện tại và trừ đi thời điểm bắt đầu
time_model = time.time() - start_time
In [12]:
# Tạo danh sách trống để lưu trữ accuracy và val_accuracy từ mỗi lịch sử đào tạo
acc = []
val_acc = []
Out [12]:
40
In [13]:
# Tắt các cảnh báo để giảm nhiễu đầu ra
import warnings
warnings.filterwarnings("ignore")
# Dự đoán xác suất của từng lớp cho dữ liệu kiểm tra
pred_probabilities = model.predict(X)
# Hiển thị các thông số đánh giá (báo cáo phân loại, ma trận nhầm, và độ chính xác)
display_stats(y_test, pred)
Out [13]:
41
In [14]:
42
# Tạo subplot 4x4 để hiển thị 16 hình ảnh
fig, axes = plt.subplots(nrows=4, ncols=4, figsize=(10, 10),
subplot_kw={'xticks': [], 'yticks': []})
# Duyệt qua từng ô subplot và hiển thị hình ảnh kèm theo nhãn thực tế và dự đoán
for i, ax in enumerate(axes.flat):
# Hiển thị hình ảnh từ dữ liệu kiểm tra
ax.imshow(X[-i])
In [15]:
# Chia dữ liệu thành tập huấn luyện và tập kiểm thử
# - df[['path','fruit']]: Chọn chỉ cột 'path' và 'fruit' từ DataFrame df.
# - sample(frac=0.05, random_state=0): Lấy mẫu 5% ngẫu nhiên từ DataFrame với seed là 0.
43
# - test_size=0.2: Chia dữ liệu thành 80% tập huấn luyện và 20% tập kiểm thử.
# - random_state=0: Seed để đảm bảo việc chia dữ liệu là nhất quán mỗi lần chạy mã.
train_df, test_df = train_test_split(df[['path', 'fruit']].sample(frac=0.05, random_state=0), test_size=0.2,
random_state=0)
In [16]:
import tensorflow as tf
from time import perf_counter
# Tạo hàm để tạo generator cho dữ liệu huấn luyện và kiểm thử
def create_gen():
train_generator = tf.keras.preprocessing.image.ImageDataGenerator(
preprocessing_function=tf.keras.applications.mobilenet_v2.preprocess_input,
validation_split=0.1
)
test_generator = tf.keras.preprocessing.image.ImageDataGenerator(
preprocessing_function=tf.keras.applications.mobilenet_v2.preprocess_input
)
44
return train_generator, test_generator, train_images, val_images, test_images
45
for name, model in models.items():
m = get_model(model['model'])
models[name]['model'] = m
start = perf_counter()
# Đào tạo mô hình với một epoch và đánh giá thời gian đào tạo
history = m.fit(train_images, validation_data=val_images, epochs=1, verbose=0)
duration = perf_counter() - start
duration = round(duration, 2)
# Lưu độ chính xác trên tập kiểm thử sau mỗi epoch vào biến 'val_acc'
val_acc = history.history['val_accuracy']
models[name]['val_acc'] = [round(v, 4) for v in val_acc]
Out [16]:
In [17]:
# Duyệt qua từng mô hình trong danh sách và thực hiện dự đoán trên tập kiểm thử
for name, model in models.items():
Out [17]:
In [18]:
46
# Tạo một danh sách để lưu kết quả của mỗi mô hình
models_result = []
# Duyệt qua từng mô hình và lấy thông tin về độ chính xác, độ chính xác trên tập kiểm thử, và thời gian
đào tạo
for name, v in models.items():
models_result.append([name, models[name]['val_acc'][-1], models[name]['acc'], models[name]
['perf']])
47
In [19]:
# Vẽ đồ thị cột hiển thị độ chính xác của mỗi mô hình trên tập kiểm thử
plt.figure(figsize=(15, 5))
sns.barplot(x='model', y='accuracy', data=df_results)
In [20]:
48
# Sử dụng train_test_split để chia dữ liệu thành tập huấn luyện và tập kiểm thử
train_df, test_df = train_test_split(df, test_size=0.1, random_state=0)
# Sử dụng hàm create_gen để tạo generator cho dữ liệu huấn luyện và kiểm thử
train_generator, test_generator, train_images, val_images, test_images = create_gen()
Out [20]:
In [21]:
# Sử dụng hàm get_model để tạo mô hình DenseNet201
model = get_model(tf.keras.applications.DenseNet201)
In [22]:
# Tạo DataFrame từ lịch sử huấn luyện
history_df = pd.DataFrame(history.history)
49
In [23]:
# Tạo DataFrame từ lịch sử huấn luyện
history_df = pd.DataFrame(history.history)
Out [23]:
50
In [24]:
# Thực hiện dự đoán trên tập kiểm thử
pred = model.predict(test_images)
# Chuyển đổi chỉ số của lớp thành tên lớp sử dụng class_indices
labels = (train_images.class_indices)
labels = dict((v, k) for k, v in labels.items())
pred = [labels[k] for k in pred]
Out [24]:
51
In [25]:
# Tạo lưới hình ảnh với 4 hàng và 6 cột, tổng cộng 24 ô
fig, axes = plt.subplots(nrows=4, ncols=6, figsize=(20, 12),
subplot_kw={'xticks': [], 'yticks': []})
Sau 2 lần training, ta thu được model (best_model.h5) với độ chính xác
99.8%
2.3.2 Phân lớp và phân cụm hoa quả (Kmeans và best_model.h5)
from keras.models import load_model
from keras.preprocessing import image
import numpy as np
52
import os
import shutil
from keras.models import Model
from PIL import Image
from sklearn.cluster import KMeans
# Load model
model = load_model('best_model.h5')
# Thư mục chứa các ảnh hoa quả cần phân loại
input_images_directory = 'Data_test/test_1'
# Đường dẫn đến thư mục chứa các hình ảnh cần phân loại
folder_clustered = 'clustered'
# Kiểm tra và tạo mới hoặc làm sạch thư mục 'data_predicted'
if os.path.exists(folder_predicted):
shutil.rmtree(folder_predicted)
os.makedirs(folder_predicted, exist_ok=True)
# Kiểm tra và tạo mới hoặc làm sạch thư mục 'clustered'
if os.path.exists(folder_clustered):
shutil.rmtree(folder_clustered)
os.makedirs(folder_clustered, exist_ok=True)
53
value == predicted_label[0]][0]
# Tạo thư mục cho nhãn dự đoán nếu chưa tồn tại
output_class_directory = os.path.join(folder_predicted,
predicted_fruit)
os.makedirs(output_class_directory, exist_ok=True)
print("Phân loại hoàn thành. Các ảnh đã được di chuyển vào các thư mục
tương ứng với nhãn dự đoán.")
# Duyệt qua các thư mục của các loại hoa quả
for fruit_folder in os.listdir(folder_predicted):
fruit_folder_path = os.path.join(folder_predicted, fruit_folder)
# Lấy danh sách các tệp tin trong thư mục và loại bỏ các thư mục
không chứa hình ảnh
image_files = [os.path.join(fruit_folder_path, f) for f in
os.listdir(fruit_folder_path) if
f.endswith(('.png', '.jpg', '.jpeg'))]
# Khởi tạo một danh sách để lưu trữ các đặc trưng
features_list = []
# Chuyển đổi danh sách đặc trưng thành một mảng numpy
features_array = np.vstack(features_list)
54
Kết quả sau khi chạy chương trình, ta được thư mục chứa ảnh cấu trúc như
sau:
Mỗi loại quả có 3 cụm (K = 3)
Cây thư mục gồm các loại quả được phân cụm:
58
- Thực hiện kiểm thử đơn vị: Chạy các kịch bản kiểm thử đã được xác
định trong giai đoạn 1 trên đơn vị mã nguồn cụ thể. Đảm bảo rằng tất cả các
trạng thái và thao tác kiểm thử đã được thực hiện theo đúng yêu cầu.
- So sánh kết quả kiểm thử: So sánh kết quả kiểm thử thực tế với kết
quả mong đợi đã xác định trong giai đoạn 1. Nếu kết quả không khớp, báo
cáo lỗi và các vấn đề tương tự.
3.3 Thực hiện kiểm thử
3.3.1 Tổ chức module
Module CNN : thực hiện phân tách thành cách hàm nhỏ để thực hiện test
- Hàm đọc ảnh và xử lí ảnh với đầu là đường dẫn ảnh
def load_and_preprocess_image(image_path, image_width=150,
image_height=150):
img = image.load_img(image_path,target_size=(image_width,
image_height))
img_array = image.img_to_array(img)
img_array = np.expand_dims(img_array, axis=0)
img_array = img_array / 255.0
return img_array
output_class_directory = os.path.join(output_directory,
predicted_fruit)
os.makedirs(output_class_directory, exist_ok=True)
shutil.copy(image_path, output_class_directory)
- Hàm phân loại ảnh (thực hiện phân loại ảnh quả là quả nào)
def classify_images(input_images_directory,
output_directory, model, fruit_labels):
os.makedirs(output_directory, exist_ok=True)
59
image_file)
predict_and_move_image(model, image_path,
output_directory, fruit_labels)
- Hàm main:
def main():
model_path = "..\\Modal\\best_model.h5"
model = load_model(model_path)
fruit_labels = {
'Apple': 0, 'Banana': 1, 'Carambola': 2, 'Guava': 3,
'Kiwi': 4,
'Mango': 5, 'Orange': 6, 'Peach': 7, 'Pear': 8,
'Persimmon': 9,
'Pitaya': 10, 'Plum': 11, 'Pomegranate': 12,
'Tomatoes': 13, 'muskmelon': 14
}
classify_images(input_images_directory, output_directory,
model, fruit_labels)
print("Phân loại hoàn thành. Các ảnh đã được di chuyển
vào các thư mục tương ứng với nhãn dự đoán.")
Module Kmeans:
- Hàm load model
def load_pretrained_model(model_path):
return load_model(model_path)
60
def cluster_images(features_array, num_clusters):
kmeans = KMeans(n_clusters=num_clusters)
return kmeans.fit_predict(features_array)
- Hàm main
def main(input_folder_path, output_folder_path, model,
num_clusters):
for fruit_folder in os.listdir(input_folder_path):
fruit_folder_path = os.path.join(input_folder_path,
fruit_folder)
image_files = [os.path.join(fruit_folder_path, f) for
f in os.listdir(fruit_folder_path) if f.endswith('.png')]
features_list = []
features_array = np.vstack(features_list)
cluster_labels = cluster_images(features_array,
num_clusters)
61
test_classify_images_failed 6 Đường dẫn Không thực hiện được dự
thư mục input đoán ảnh
không tồn tại
Module Kmeans
Tên hàm Mục đích Test Đầu vào Expected
cas
e
test_load_pretrained_model_success Thực 1 Đường dẫn model Đường dẫn tới file
hiện load hợp lệ model .h5
model
test_load_pretrained_model_failure 2 Đường dẫn model Đường dẫn không tồn tại,
train không hợp lệ và không load đc model
test_extract_features Thực 3 Đầu vào hợp lệ Đối tượng Model đã
hiện kiểm được load, mảng ảnh có
tra hàm kích thước (150, 150, 3)
test_extract_features_invalid_input trích chọn 4 Đầu vào không hợp Không thể trích chọn đặc
đặc trưng lệ trưng
test_resize_image_success Thực 5 Đầu vào hợp lệ Đối tượng Image, kích
hiện kiểm thước mục tiêu (150,
tra thay 150)
đổi kích
thước
ảnh
test_cluster_images_success Thực 6 Kích thước mục tiêu Ảnh được phân cụm
hiện kiểm không hợp lệ
tra phân
test_cluster_images_invalid_clusters 7 Số cụm không hợp Không thể phân cụm
cụm ảnh lệ
class TestFruitImageClassification(unittest.TestCase):
def setUp(self):
self.model_path = "../Modal/best_model.h5"
self.model = load_model(self.model_path)
self.fruit_labels = {
'Apple': 0, 'Banana': 1, 'Carambola': 2, 'Guava':
3, 'Kiwi': 4,
'Mango': 5, 'Orange': 6, 'Peach': 7, 'Pear': 8,
'Persimmon': 9,
'Pitaya': 10, 'Plum': 11, 'Pomegranate': 12,
62
'Tomatoes': 13, 'muskmelon': 14
}
self.input_images_directory = "D:\\Dataset\\Data
test\\test_2"
self.output_directory = "D:\\Python - craw data\\
DACN\\Test\\FruitsTestFolder"
# Test case 1: thực hiện load ảnh thành công với đường
dẫn đứng
def test_load_and_preprocess_image(self):
image_path = "D:\\Dataset\\Data test\\test_2\\Apple
343.png"
img_array = load_and_preprocess_image(image_path)
# Test case 2: thực hiện load ảnh với đường dẫn sai
def test_load_and_preprocess_image_failed(self):
image_path = "wrong_path"
with self.assertRaises(FileNotFoundError):
img_array = load_and_preprocess_image(image_path)
print("Test failed!")
# Test case 5: thực hiện dự đoán ảnh với đường dẫn đúng
def test_classify_images(self):
classify_images(self.input_images_directory,
self.output_directory, self.model, self.fruit_labels)
63
for fruit_label in self.fruit_labels:
fruit_directory =
os.path.join(self.output_directory, fruit_label)
self.assertTrue(os.path.exists(fruit_directory))
print("Test passed!")
with self.assertRaises(Exception):
classify_images(wrong_input_dir,
self.output_directory, self.model, self.fruit_labels)
print("Test failed!")
if __name__ == "__main__":
pytest.main()
Module Kmeans:
import unittest
import os
import numpy as np
from keras.models import Model
from PIL import Image
from Main.Kmean_module import load_pretrained_model,
extract_features, resize_image, cluster_images
class TestClusteringFunctions(unittest.TestCase):
def setUp(self):
# Thiết lập biến sử dụng chung cho các bài kiểm tra
self.model_path = '../Modal/best_model.h5'
self.model = load_pretrained_model(self.model_path)
self.image_path = '../Main/Fruits/Apple/Apple
E05100.png'
self.sample_image = np.random.rand(150, 150,
3).astype(np.float32)
# Test case 1: thực hiện load model với đường dẫn đúng
def test_load_pretrained_model_success(self):
model_path = self.model_path
loaded_model = load_pretrained_model(model_path)
self.assertIsNotNone(loaded_model)
print("Model loaded successfully!")
64
# Test case 2: thực hiện load model với đường dẫn sai
def test_load_pretrained_model_failure(self):
model_path = "invalid_path"
with self.assertRaises(OSError):
loaded_model = load_pretrained_model(model_path)
print("Failed to load model!")
# Test case 3: thực hiện trích chọn đặc trưng với ảnh
hợp lệ
def test_extract_features(self):
features = extract_features(self.model,
self.sample_image)
self.assertIsNotNone(features)
self.assertEqual(features.ndim, 2)
print("Test passed!")
# Test case 4: thực hiện trích chọn đặc trưng với ảnh
không hợp lệ
def test_extract_features_invalid_input(self):
invalid_image = None
with self.assertRaises(ValueError):
features = extract_features(self.model,
invalid_image)
# Test case 5: thực hiện thay đổi kích thước ảnh với
ảnh hợp lệ
def test_resize_image_success(self):
img = Image.fromarray((self.sample_image *
255).astype(np.uint8))
resized_img = resize_image(img, (50, 50))
# Test case 6: thực hiện phân cụm ảnh với số cụm hợp lệ
def test_cluster_images_success(self):
features_array = np.random.rand(10, 1280)
num_clusters = 5
cluster_labels = cluster_images(features_array,
num_clusters)
65
self.assertEqual(cluster_labels.shape, (10,))
# Test case 7: thực hiện phân cụm ảnh với số cụm không
hợp lệ
def test_cluster_images_invalid_clusters(self):
features_array = np.random.rand(10, 1280)
num_clusters = 0
with self.assertRaises(ValueError):
cluster_labels = cluster_images(features_array,
num_clusters)
if __name__ == '__main__':
unittest.main()
Module Kmeans:
66
7 test đều pass trong vòng 4.89s.
3.4 Đánh giá kiểm thử
Với bộ test case xây dựng và sau khi thực hiện xong kiểm thử, chương
trình của chúng em đã thực hiện chạy ổn định với các dữ liệu đầu vào được
kiểm thử.
Tên hàm Test Đầu vào Expected Kết quả
case test
test_load_and_preprocess_image 1 Đường Mảng ảnh có kích Passed
dẫn file thước (1, 150,
ảnh hợp 150, 3)
lệ
test_load_and_preprocess_image_faile 2 Đường Không load được Passed
d dẫn file ảnh
không tồn
tại
test_predict_and_move_image 3 Đầu vào File ảnh được di Passed
hợp lệ chuyển vào thư
mục output tương
ứng
test_predict_and_move_image_failed 4 Đường Không thực hiện Passed
dẫn file di chuyển ảnh
ảnh
không tồn
tại
test_classify_images 5 Đầu vào Các file ảnh trong Passed
hợp lệ thư mục input
được di chuyển
vào các thư mục
con tương ứng
trong thư mục
output
test_classify_images_failed 6 Đường Không thực hiện Passed
67
dẫn thư được dự đoán
mục input ảnh
không tồn
tại
68
KẾT LUẬN
Trong đồ án này, chúng em đã thực hiện và nghiên cứu mô hình mạng
CNN kết hợp với thuật toán phân cụm Kmeans để thực hiện giải quyết bài
toán: Phân loại tình trạng hoa quả.
Sau đây là một số tổng kết chúng em rút ra được:
- Mô hình CNN được train với độ chính xác cao (>90%) thực
hiện thành công nhận dạng hoa quả (15 loại hoa quả) với càng nhiều
lớp thì càng nhiều đặc trưng được nhặt ra và kết hợp lại thành kết quả
đầu ra.
- Thuật toán Kmeans thực hiện phân cụm dựa trên trích chọn đặc
trưng từ các ảnh đầu vào (các đặc trưng được thực hiện trích chọn
thông qua mô hình CNN) cho độ chính xác cao (bộ dữ liệu test gồm 5
bộ dữ liệu - mỗi bộ dữ liệu trung bình tầm 2000 ảnh)
- Khả năng tổ chức công việc và lên kế hoạch cho hoạt động làm
việc nhóm giúp chúng em làm quen với việc kết nối, trao đổi và giúp
đỡ không chỉ trong môi trường học đường mà có thể hữu ích với cả môi
trường doanh nghiệp.
Và mong muốn của chúng em là đạt được những tầm cao mới như:
- Đạt được độ chính xác gần sát với tuyệt đối (97-99)%.
- Xây dựng thêm giao diện người dùng giúp dễ dàng thao tác với
người dùng cuối.
- Mở rộng số lượng hoa quả giúp đưa vào giúp ích cho việc sản
xuất nông nghiệp.
Nhóm em hy vọng những nghiên cứu và kết quả trong đồ án này sẽ góp phần
giải quyết hiệu quả bài toán phân loại và phân cụm hoa quả, từ đó ứng dụng
vào thực tiễn sản xuất và chế biến. Đây cũng là hướng nghiên cứu tiềm năng
để áp dụng trí tuệ nhân tạo trong lĩnh vực nông nghiệp.
69
TÀI LIỆU THAM KHẢO
1. Tác phẩm sách:
[1] Hồ Viết Hoàng. (2018). Xử lý ảnh và nhận dạng đối tượng.
[2] Trần Đình Cường. (2017). Python cơ bản và nâng cao
[3] Lê Đình Duy. (2017). Deep Learning - Nhập môn máy học sâu
[4] Nguyễn Văn Lợi. (2017). Xử lý ảnh số và thị giác máy tính với
Python và OpenCV
[5] M. Emre Celebi và Bogdan Smolka. (2016). Convolutional Neural
Networks in Visual Computing: A Concise Guide.
[6] Adrian Rosebrock. (2016). Practical Python and OpenCV.
[7] Kevin P. Murphy (2012). Machine Learning: A Probabilistic
Perspective.
2. Website:
[1] https://stanford.edu/~shervine/l/vi/teaching/cs-230/cheatsheet-
convolutional-neural-networks
[2] Stanford University CS231n: Deep Learning for Computer Vision
[3] sklearn.cluster.KMeans — scikit-learn 1.3.2 documentation
3. Tài liệu học thuật trực tuyến (ebook, học liệu trực tuyến):
[1] Nguyễn Thanh Tuấn. (2020). Deep Learning Cơ Bản.
https://nttuan8.com/sach-deep-learning-co-ban
[2] Vũ Hữu Tiệp. (2020). Machine Learning cơ bản.
https://machinelearningcoban.com/ebook/
70