Face clustering với DBSCAN và Chinese Whispers
Thế gian ngày nay, công nghệ phát triển, kĩ thuật tiến bộ. Học được kinh Kaizen của GMO, ghi lại triết lí cải tiến sản xuất, có thể giúp GMO đạt mục tiêu 55 năm, hùng bá thiên hạ. Mình đã về nước tận lực cống hiến nhưng cho đến nay mãi vẫn chỉ biết làm Web. Mình quyết định tu luyện OpenCV, nhưng vì không có data nên mãi vẫn không biết con yêu tinh này yêu nữa này tên gì nguồn gốc ở đâu.
Quay tay lại ngay với chủ để chính thôi thì không biết nó là ai ít nhất phân biệt các diễn viên hay dở còn xem lại.
Bài toán cụ thể như sau: Có rất nhiều bộ phim cúa các diễn viên khác nhau nhưng thường các bộ phim không được nhóm hay sắp sếp hoặc chia nhóm theo tên diễn viên mà tên các file phim thì thường được đăt tên rất phức tạp nên cần phải chia nhóm theo các diên dựa vào khuôn mặt diễn viên xuất hiện trong phim.
Nhận dạng khuôn mặt và phân cụm khuôn mặt là các khái niệm khác nhau, nhưng có liên quan cao. Khi thực hiện nhận dạng khuôn mặt, chúng ta đang áp dụng phương pháp học có giám sát trong đó chúng tôi có cả (1) hình ảnh ví dụ về khuôn mặt mà chúng ta muốn nhận ra cùng với (2) tên tương ứng với mỗi khuôn mặt (gán nhãn).
Nhưng trong phân nhóm mặt chúng ta cần phải thực hiện việc học không có giám sát - chúng ta chỉ có những khuôn mặt mình với không tên / nhãn. Từ đó chúng ta cần xác định và đếm số lượng người duy nhất trong một tập dữ liệu.
Để làm được điều này chúng ta cần 2 bước:
- Một để trích xuất và định lượng các khuôn mặt trong bộ dữ liệu (encoding)
- Từ bộ dữ liệu đó chia thành các cụm với mỗi cụm ứng với 1 người duy nhất
Cài đặt môi trường
Ngôn ngữ sử dụng là python, nếu bạn có GPU hãy cài thêm CUDA.
Các thư viện cần thiết:
Dataset dùng để chạy thuật toán
Năm cầu thủ mình đã tìm 129 bức ảnh làm dataset
Cấu trúc source code
Gồm 1 thư mục và 3 file
$ tree --dirsfirst
.
├── dataset [129 entries]
│ ├── 00000000.jpg
│ ├── 00000001.jpg
│ ├── 00000002.jpg
│ ├── ...
│ ├── 00000126.jpg
│ ├── 00000127.jpg
│ └── 00000128.jpg
├── encode_faces.py
├── encodings.pickle
└── cluster_faces.py
1 directory, 132 files
dataset
: Chứa 129 hình ảnh của năm cầu thủ bóng đá ở trên. Lưu ý ở đầu ra ở trên rằng không có thông tin nhận dạng trong tên tệp hoặc tệp khác xác định ai là người trong mỗi hình ảnh. Không thể biết cầu thủ bóng đá nào trong hình ảnh chỉ dựa trên tên tập tin. Mình sẽ sử dụng một thuật toán phân cụm khuôn mặt để xác định các khuôn mặt tương tự và duy nhất trong bộ dữ liệu.encode_faces.py
: Đây là tập lệnh trong bước đầu tiên - nó encode khuôn mặt và lư ra fileencodings.pickle
encodings.pickle
: File lưu trữ encodings của ảnhcluster_faces.py
: nơi chúng ta sẽ phân cụm các khuôn mặt tương tự
Encoding faces với deep learning
Trước khi chúng ta có thể phân cụm một tập hợp các khuôn mặt, trước tiên chúng ta cần encode chúng. Quá trình encode khuôn mặt này sẽ được thực hiện bằng cách sử deep learning và xuất ra một vectơ 128 chiều
Viết code chó quá trình encoding
# loop over the image paths
for (i, imagePath) in enumerate(imagePaths):
# Đọc bức ảnh đầu vào và chuyển nó thành rgb
# vì dlib chỉ sử dụng (RGB)
print("[INFO] processing image {}/{}".format(i + 1,
len(imagePaths)))
print(imagePath)
image = cv2.imread(imagePath)
rgb = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)
# Lấy vị trí khuôn mặt trong bức ảnh to
boxes = face_recognition.face_locations(rgb, model="cnn")
Lưu ý: Mình đang sử dụng trình phát hiện khuôn mặt CNN để có độ chính xác cao hơn, nhưng sẽ mất nhiều thời gian hơn để chạy nếu bạn đang sử dụng CPU thay vì GPU. Nếu bạn muốn encoding chạy nhanh hơn hoặc hệ thống của bạn và hệ thống của bạn không có đủ RAM hoặc sức mạnh CPU cho quá trình phát hiện khuôn mặt CNN, thay vào đó, hãy sử dụng phương pháp HOG + Linear SVM.
# Chậy encoding từ vị trí khuông mặt đã thu đc
encodings = face_recognition.face_encodings(rgb, boxes)
# format dư liêu
d = [{"imagePath": imagePath, "loc": box, "encoding": enc}
for (box, enc) in zip(boxes, encodings)]
data.extend(d)
# ghi ra file
f.write(pickle.dumps(data))
Kết quả chạy encoding
$ python encode_faces.py --dataset dataset --encodings encodings.pickle
[INFO] quantifying faces...
[INFO] processing image 1/129
dataset/00000038.jpg
[INFO] processing image 2/129
dataset/00000010.jpg
[INFO] processing image 3/129
dataset/00000004.jpg
...
[INFO] processing image 127/129
dataset/00000009.jpg
[INFO] processing image 128/129
dataset/00000021.jpg
[INFO] processing image 129/129
dataset/00000035.jpg
[INFO] serializing encodings...
Nếu chạy bằng GPU có thể mất 1-2 phút nhưng nếu chạy bằng CPU se mất 20-30 phút
Clustering faces
Bây giờ chúng ta đã có dữ liệu encoding của cac bức ảnh được biểu diễn dưới dạng vector 128 chiều. Tiếp theo từ các vector này sẽ phân cụm cho các bức ảnh.
Trong ví dụ này, chúng ta biết chỉ có năm cầu thủ bóng đá - nhưng trong các ứng dụng trong thế giới thực, bạn có thể không biết có bao nhiêu cá nhân duy nhất có trong một tập dữ liệu.
Do đó, chúng ta cần sử dụng thuật toán phân cụm dựa trên mật độ hoặc dựa trên biểu đồ để không chỉ phân cụm các điểm dữ liệu mà còn có thể xác định số lượng cụm cũng dựa trên mật độ của dữ liệu. Chính về thế nhiều thuật toán phân cụm như K-mean không thể dử dụng được trong trường hợp này vì cần phải biết trước số cụm.
Đối với phân cụm khuôn mặt, tôi muốn giới thiệu hai thuật toán:
- Phân cụm dựa trên mật độ của các ứng dụng có nhiễu DBSCAN(Density-based spatial clustering of applications with noise)
- Chinese Whispers
Chúng tôi sẽ sử dụng DBSCAN cho hướng dẫn này vì tập dữ liệu của chúng tôi tương đối nhỏ. Đối với các bộ dữ liệu thực sự lớn, bạn nên xem xét sử dụng thuật toán thì Chinese Whispers theo thời gian tuyến tính.
Thuật toán DBSCAN hoạt động bằng cách nhóm các điểm lại với nhau mà được đóng gói chặt chẽ trong một không gian N chiều. Các điểm nằm gần nhau sẽ được nhóm lại trong một cụm.
DBSCAN cũng tự nhiên xử lý các noise, đánh dấu chúng là nhiễu nếu chúng rơi vào các khu vực mật độ thấp.
Ví dụ như hinh trên được chia thành 3 cụm và mật độ các điểm trong cum rất cao. Những điểm mầu đen được đánh dấu là nhiễu
DBSCAN có thể phân cụm với các cụm lông nhau
DBSCAN phụ thuộc vào 2 tham số Eps(bán kính) và minPts (số điểm lân cận tối thiểu). Cơ chế của DBSCAN có thể hiểu đơn giản là trong phạm vi Eps có số điểm xung quanh lớn hơn minPts thì điểm đó làm trong cụm, những điểm lân cận nhỏ hơn minPts thì được coi là nhiễu. Chi tiết thuật toán có thể xem trong tài liệu tham khảo.
Hãy tiếp tục và triển khai phân cụm khuôn mặt bằng DBSCAN.
# import the necessary packages
from sklearn.cluster import DBSCAN
# Lấy dữ lệu đã được encoding
data = pickle.loads(open(args["encodings"], "rb").read())
data = np.array(data)
encodings = [d["encoding"] for d in data]
# cluster the embeddings
print("[INFO] clustering...")
clt = DBSCAN(metric="euclidean", n_jobs=args["jobs"])
clt.fit(encodings)
# ouput label đã được phân cụm
labelIDs = np.unique(clt.labels_)
numUniqueFaces = len(np.where(labelIDs > -1)[0])
# Tách face theo từng cụm
for labelID in labelIDs:
# find all indexes into the `data` array that belong to the
# current label ID, then randomly sample a maximum of 25 indexes
# from the set
print("[INFO] faces for face ID: {}".format(labelID))
idxs = np.where(clt.labels_ == labelID)[0]
idxs = np.random.choice(idxs, size=min(25, len(idxs)),
replace=False)
# initialize the list of faces to include in the montage
faces = []
# loop over the sampled indexes
for i in idxs:
# load the input image and extract the face ROI
image = cv2.imread(data[i]["imagePath"])
(top, right, bottom, left) = data[i]["loc"]
face = image[top:bottom, left:right]
# force resize the face ROI to 96x96 and then add it to the
# faces montage list
face = cv2.resize(face, (96, 96))
faces.append(face)
Kết quả chạy phân cụm
$ python cluster_faces.py --encodings encodings.pickle
[INFO] loading encodings...
[INFO] clustering...
[INFO] # unique faces: 5
[INFO] faces for face ID: -1
[INFO] faces for face ID: 0
[INFO] faces for face ID: 1
[INFO] faces for face ID: 2
[INFO] faces for face ID: 3
[INFO] faces for face ID: 4
faceID = - 1
tức là nhiễu
Tổng hợp các khuôn mặt đã được phần cụm
Trường hợp nhiễu
Thử với Chinese Whispers
Chinese Whispers là một trò chơi nói thầm lan truyền thông tin từ người này sang người khác. Thuật toán Chinese Whispers Clustering cũng tương tư như vậy.
Các node sẻ được đánh các mầu ngâu nhiên khác nhau và tư một node sẽ chia se trạng thái cho các node bên cạch
https://en.wikipedia.org/wiki/Chinese_Whispers_(clustering_method)
Sức mạnh chính của Chinese Whispers là tốc độ, mình chạy thử với bộ dataset trên thì mất khoản gần 1 phút và không phải encoding. Chính vì thế Chinese Whispers mà một công cụ tốt để phân tích các cấu trúc cộng đồng trong biểu đồ với số lượng nút rất cao.
Để chạy thuật toán này có thể dung trực tiếp hàm chinese_whispers_clustering
của dlib
ngay trên trang chủ của dlib cũng có code dành cho face cluster
http://dlib.net/face_clustering.py.html
Về kết quả của Chinese Whispers chính xác không kém gì DBSCAN với dataset của mình tuy nhiên nhưng điểm nhiễu Chinese Whispers sẽ chia thành 1 cụm riêng.
Tổng hợp
Trong bài đăng trên blog này, bạn đã học cách thực hiện phân cụm khuôn mặt bằng Python và học sâu.
Không giống như nhận diện khuôn mặt, là một nhiệm vụ học tập có giám sát , phân cụm khuôn mặt là một nhiệm vụ học tập không giám sát .
Với nhận dạng khuôn mặt, chúng ta có cả hai:
- Các khuôn mặt người
- Và tên của họ (nghĩa là nhãn lớp)
Nhưng trong cụm mặt, chúng ta chỉ có các mặt - chúng ta cũng không có tên tương ứng của chúng. Thiếu tên / nhãn lớp, chúng tôi chỉ có thể tận dụng các thuật toán học tập không giám sát, trong trường hợp này là các kỹ thuật phân cụm.
Để phân cụm các khuôn mặt thực tế thành các nhóm cá nhân, mình đã dùng thuật toán DBSCAN . Các thuật toán phân cụm khác cũng có thể được sử dụng - Davis King (người tạo ra dlib) đề nghị sử dụng Chinese Whispers .
Tôi hy vọng bạn thích bài viết này!
Tài liệu tham khảo:
- https://www.youtube.com/watch?v=XKtsja3Kl34
- https://scikit-learn.org/stable/modules/generated/sklearn.cluster.DBSCAN.html
- https://en.wikipedia.org/wiki/DBSCAN
- http://www.cit.ctu.edu.vn/~dtnghi/rech/dir_0/Duy-et-al-New.pdf
- https://github.com/nlpub/chinese-whispers-python
- https://en.wikipedia.org/wiki/Chinese_Whispers_(clustering_method)
- https://www.pyimagesearch.com/2018/07/09/face-clustering-with-python/