29/07/2019, 10:09

Phát hiện giả mạo khuôn mặt bằng Deep Learning

Với sự phát triển của AI, các ứng dụng nhận diện xác minh khuân mặt ngày càng trở lên phổ biến. Các ứng dụng yêu cầu xác minh khuôn mặt cần phải chống lại các yếu tố tấn công giả mạo từ các nguồn như ảnh chụp/ ảnh khuôn mặt từ màn hình điện thoại (anti-spoofing). Giả sử điện thoại bạn có sử dụng ...

Với sự phát triển của AI, các ứng dụng nhận diện xác minh khuân mặt ngày càng trở lên phổ biến. Các ứng dụng yêu cầu xác minh khuôn mặt cần phải chống lại các yếu tố tấn công giả mạo từ các nguồn như ảnh chụp/ ảnh khuôn mặt từ màn hình điện thoại (anti-spoofing).

Giả sử điện thoại bạn có sử dụng chức năng mở khóa bằng khuôn mặt, một hôm lúc bạn đi vắng nhưng để quên điện thoại ở nhà thế là con vợ bạn muốn mở điện thoại bạn ra để kiểm tra xem bạn có abcxyz với con nào không. Muốn mở khóa được bắt buộc phải có ảnh mặt bạn ở đó, thế là nó giơ camera trước lên ảnh cưới của bạn treo trên tường, và tất nhiên các nhà sản xuất điện thoại đã tính trước cả rồi, ảnh cưới mặc dù có mặt bạn nhưng không thể mở được khóa. Vậy phần mềm trên điện thoại đã làm thế nào?

Có nhiều cách để thực hiện điều này, ví dụ như chức năng mở khóa bằng khuân mặt của iphone sử dụng cảm biến 3D để tái hiện lại cấu trúc 3D của khuôn mặt, một số dòng điện thoại khác sử dụng thuật toán để phát hiện đâu là khuôn mặt được chụp trực tiếp từ camera, đâu là khuôn mặt không phải trực tiếp. Hôm nay mình sẽ hướng dẫn các bạn cách để huấn luyện 1 mô hình đơn giản nhưng hiệu quả để phát hiện ảnh thật/giả giúp con vợ không thể mở khóa được điện thoại nếu không phải là mặt bạn. Để bắt đầu, chúng ta sẽ đi theo đúng quy trình giải quyết bài toán deeplearning:

Yêu cầu chung của bài toán là một input gồm 1 ảnh và output sẽ đánh nhãn là ảnh chụp mặt thật (real) hay ảnh giả mạo (fake). Như vậy đây là bài toán phân loại nhị phân (Binary classification) thông thường. Ngoài ra đây chỉ là một module con trong ứng dụng nhận diện/xác minh khuôn mặt nên bên cạnh đó còn yêu cầu quan trọng về tốc độ và tính khả thi về triển khai, không thể dùng 1 model phức tạp nhiều tham số sẽ ảnh hưởng đến tốc độ tính toán và khó triển khai trên các thiết bị di động hoặc thống nhúng có khả năng tính toán thấp.

Vì các yêu cầu trên nên mình chọn mạng MobileNets - Efficient Convolutional Neural Networks for Mobile Vision Applications

Bạn clone mã nguồn tại https://github.com/dinhquy94/face-antispoofing-using-mobileNet

Dưới đây benchmark các mô hình trên cùng tập dữ liệu ImageNet, ta có thể thấy MobileNetV2 có độ chính xác không hề thua kém các mô hình khác như VGG16, VGG19 trong khi lượng parameters chỉ vỏn vẹn 3.5M (khoảng 1/40 số tham số của VGG16).

Ở đây mình dùng dataset CASIA-FASD. Đây là bộ dataset gồm các đoạn video, mỗi đoạn gồm 100-200 khung hình. Trong mỗi video mình lấy ra 30 khung hình (với thời gian lấy mẫu giống nhau). Sau đó mình dùng Haar_classifier để cắt khuôn mặt khỏi các video và phân vào 2 thư mục: ClientFace (gồm các ảnh thật) và ImposterFace (gồm các ảnh Fake) Thư mục data training mình đã đặt cả trong git, bạn có thể xem tại đây

CASIA-FASD là bộ dataset miễn phí phục vụ nghiên cứu, nhưng việc sử dụng vì lý do bản quyền nên bạn muốn dùng thì có thể google để download.

Chúng ta tiến hành load ảnh từ 2 thư mục và gán nhãn tương ứng cho các ảnh. Các ảnh được resize về kích thước 224 x 224px và trả về mảng 4 chiều. Bước này được xử lý trong file data_loader.py. Bạn phải chỉnh lại đường dẫn đến thư mục data ở biến real_dir và fake_dir theo đường dẫn trên máy của bạn

def get_files():
    all_image_list = []
    all_label_list = []
    real_dir = '/Users/nguyendinhquy/PycharmProjects/MobileNet/data/ClientFace'
    fake_dir = '/Users/nguyendinhquy/PycharmProjects/MobileNet/data/ImposterFace'
    # load the real image
    count_real = 0
    count_fake = 0
    for sub_dir in os.listdir(real_dir):
        if os.path.isdir(real_dir + '/' + sub_dir):
            for file_name in os.listdir(real_dir + '/' + sub_dir):
                if not file_name.endswith('.jpg') or file_name.startswith('.'):
                    continue  # Skip!
                # all_image_list.append(plt.imread(real_dir + '/' + sub_dir + '/' + file_name))
                image = cv2.imread(real_dir + '/' + sub_dir + '/' + file_name, cv2.IMREAD_COLOR)
                all_image_list.append(cv2.resize(image, (224, 224)))
                all_label_list.append(1)
                count_real += 1

    for sub_dir_fake in os.listdir(fake_dir):
        if os.path.isdir(fake_dir + '/' + sub_dir_fake):
            for fake_file_name in os.listdir(fake_dir + '/' + sub_dir_fake):
                if not fake_file_name.endswith('.jpg') or fake_file_name.startswith('.'):
                    continue  # Skip!

                image = cv2.imread(fake_dir + '/' + sub_dir_fake + '/' + fake_file_name, cv2.IMREAD_COLOR)
                all_image_list.append(cv2.resize(image, (224, 224)))
                all_label_list.append(0)
                count_fake += 1

    print('There are %d real images
There are %d fake images' % (count_real, count_fake))
    all_image_list = np.array(all_image_list).reshape((len(all_image_list), 224, 224, 3))

    return all_image_list, np.array([label for label in all_label_list])

Chúng ta sử dụng MobileNets nên không cần phải implement lại, chính hiệu từ tensorflow cũng đã có code implement sẵn, chúng ta chỉ cần sử dụng thôi. Trong bài này chúng ta sẽ sử dụng weight của mô hình đã được train từ trước, được đặt trong thư mục pretrained_weights. Đây là 1 trong số các kỹ thuật transfer learning được sử dụng để tăng độ chính xác của mô hình.

Code mô hình được định nghĩa trong file model.py

Các siêu tham số của mô hình được đặt tại config/test.json:

{
  "experiment_dir": "test_experiment",
  "num_epochs": 30,
  "num_classes": 2,
  "batch_size": 10,
  "awidth_multiplier": 1.0,
  "shuffle": true,
  "l2_strength": 4e-5,
  "bias": 0.0,
  "learning_rate": 1e-3,
  "batchnorm_enabled": true,
  "dropout_keep_prob": 0.999,
  "pretrained_path": "pretrained_weights/mobilenet_v1.pkl",
  "max_to_keep": 4,
  "save_model_every": 5,
  "test_every": 5,
  "to_train": false,
  "to_test": true
}

Vì tập dữ liệu để training nhỏ so với tập dữ liệu pretrain của MobileNet nên mình chọn learning_rate tương đối nhỏ (1e-3) để tránh hiện tượng overfit. Số Epochs được đặt là 30-100 tùy các bạn.

Chúng ta sẽ tiến hành chia tập dữ liệu thành 2 phần riêng biệt để training và testing (validating) với tỉ lệ 80-20

self.X_train, self.X_val, self.y_train, self.y_val = train_test_split(data, labels, test_size=0.20,
                                                                              random_state=42)

random_state=42 để xáo trộn lại dữ liệu, điều này rất quan trọng trong việc đưa dữ liệu vào.

Tạo mô hình

Trong file main.py:

    model = MobileNet(config_args)
    summarizer = Summarizer(sess, config_args.summary_dir)
    trainer = Train(sess, model, data, summarizer)

Đặt checkpoint

Việc đặt checkpoint rất quan trọng trong quá trình training mô hình. Giúp chúng ta lưu lại được mô hình trong trường hợp đang training bị gián đoạn do lỗi mất điện, service bị crash... hoặc muốn train tiếp sau này.

    _, config_args.summary_dir, config_args.checkpoint_dir = create_experiment_dirs(config_args.experiment_dir)

Nói chung là không thể không đặt checkpoint khi training nếu không muốn mất thời gian sau này

Training

trainer.train()

Quá trình training sẽ tiến hành train từng epoch, mỗi epoch sẽ lần lượt đưa dữ liệu vào theo từng batch nên thời gian training sẽ khá lâu. Đánh đổi lại việc training này sẽ giúp mô hình chính xác hơn. Để training, sửa tham số trong file config thành:

"to_train": false,
 "to_test": true

(Nếu bạn muốn test kết quả thì đổi ngược lại giá trị 2 biến này)

Sau đó chạy lệnh:

python3 main.py --config config/test.json

Kết quả huấn luyện sau Epoch đầu tiên:

Kết quả testing sau khi training mô hình thành công:

Mô hình cho kết quả khá tốt, loss chỉ còn 0.03 và độ chính xác trên tập test lên tới hơn 99.9%. Vậy là quá tuyệt vời.

Vì bài viết khá dài nên mình sẽ hướng dẫn deploy mô hình và stream trực tiếp từ openCV trong bài viết sau nhé.

Các bạn có thể tham khảo source code của bài viết tại đây

Cảm ơn các bạn đã theo dõi bài viết hẹn gặp lại trong những bài viết tiếp theo.

0