ทำ Face recognition ด้วยวิธี N-shot learning บน Pytorch🔥

Witchapong Daroontham
DATAWIZ
Published in
4 min readJan 30, 2020

ก่อนอื่นขออธิบายก่อนว่า ในความเข้าใจของผู้เขียน task การทำ Face recognition ซึ่ง เป็นการหาภาพใบหน้า reference ที่เป็นใบหน้าเดียวกัน กับภาพใบหน้า ที่ input เข้ามา เทียบได้กับการหารูปภาพ reference ที่คล้ายกับ รูปภาพ input นั่นเอง (เช่น ระบบ recommend content บน Pinterest หรือ Google Image Search)

หลายคนอาจจะสงสัยกันว่า Face recoginition ทำอย่างไร ใช่เหมือนกับการทำ image classification ปกติรึเปล่า ที่เอาใบหน้าของแต่ละคน คนละหลายๆ ใบหน้า (100+) มาให้ model ประเภท classifier เรียนรู้เพื่อนทำนายความน่าจะเป็นของรูปภาพที่เข้ามาว่าเป็นใบหน้าไหน

วิธีที่กล่าวมาข้างต้น มีข้อเสียคือ ต้องใช้รูปสำหรับแต่ละใบหน้าค่อนข้างเยอะ ถึงจะทำให้ model เรียนรู้ได้ดี และ ถ้ามีใบหน้าใหม่เข้ามาก็ต้อง train model ใหม่ ซึ่งทั้งสองข้อนี้ค่อนข้างยุ่งยากในเชิงปฏิบัติ

Image classification vs. N-shot learning

idea ในการทำ face recognition คือ แทนที่จะสอนให้ model จำแนกว่าใบหน้าไหนเป็นใคร ก็สอนให้ model เรียนรู้ความต่างระหว่างคู่ใบหน้าแทน โดยใช้ vector distance ระหว่างใบหน้า โดย model ควรจะเรียนรู้ว่า ใบหน้าของบุคคลเดียวกัน ที่ถ่ายในท่าทางต่างๆ มี distance เทียบกับภาพ reference ของบุคคลนั้นไม่มาก ในทางกลับกัน ใบหน้าที่ต่างกัน ไม่ว่าจะถ่ายในท่าทางไหน ก็ควรมี distance ห่างกัน เมื่อเทียบกับ distance ระหว่างใบหน้าเดียวกัน

แทนที่จะ classify มาว่า ใบหน้าที่ input คือ หน้าของใคร ก็จะใช้วิธีเทียบ ใบหน้าที่ input เข้ามา กับทุกใบหน้า reference ใน database ว่ามี distance เทียบกับ reference ไหนน้อยที่สุด หรือคล้ายกัน ที่สุด และ ต่ำกว่าค่า threshold ที่ตั้งไว้ ก็ถือว่าใบหน้า match กัน

การ train model ในลักษณะนี้ จะใช้ข้อมูลใบหน้าจากหลายๆคน (n คน) คนละ N ภาพ (เป็นที่มาของ N shot learning โดย N อาจจะมีขนาดตั้งแต่ 1–20+ ภาพ ซึ่งยังถือว่าน้อยกว่า sample ที่ต้องใช้ในการทำ image classification มาก) โดยที่ n >> N เป็นที่มาของ N-Shot learning

Triplet loss function

อธิบาย triplet loss function (ref: https://gombru.github.io/2019/04/03/ranking_loss/)

loss function สำหรับให้ model เรียนรู้การแยกแยะความแตกต่างระหว่างระหว่างภาพใบหน้าเดียวกัน และคนละใบหน้า สามารถใช้ loss function ได้หลายแบบ ได้แก่ l1 distance + binary cross entropy loss ,contrastive loss, และ triplet loss โดย loss สองแบบแรกจะใช้สำหรับการเรียนรู้แบบ pairwise ระหว่างคู่ ภาพใบหน้า reference (หรือ anchor-a ตามภาพ และ สมการ) และ ภาพใบหน้าเดียวกัน (positive pair: จับคู่ anchor กับ positive) หรือ ภาพใบหน้า reference และ ภาพใบหน้าคนละบุคคล (negative pair: จับคู่ anchor กับ negative)

triplet loss function

triplet loss จะใช้ ทั้งภาพใบหน้า reference , ภาพใบหน้าเดียวกัน, และ ภาพใบหน้าคนละบุคคล พร้อมกัน ในการ train model (หรือเรียกว่า triplet) โดย objective ของ loss function กล่าว ต้องการให้ผลต่างระหว่าง Euclidean distance ระหว่างใบหน้าเดียวกัน — d(r_a,r_p) และ ระหว่างใบหน้าต่างกัน — d(r_a,r_n) ห่างกันเท่ากับ hyperparameter alpha — m ที่กำหนดใน loss function หรือพูดง่ายๆ คือ เราต้องการให้ distance ของ positive pair ต่ำๆ ส่วนของ negative pair ให้สูงๆ แต่ไม่ต้องเกินค่า alpha ที่ตั้งไว้ เพราะมองว่าผลต่างระหว่าง distance เพียงพอแล้ว ให้ model คอยปรับ weight เฉพาะ triplet ที่เป็นเคสยากๆ มี distance ระหว่าง positive pair และ negative pair ใกล้เคียงกันดีกว่า (triplet ไหนที่ผลต่างระหว่าง distance > alpha ค่า loss จะเท่ากับ 0)

Dataset

ใน blog นี้เราจะลอง train model สำหรับทำ face recognition โดยใช้ dataset AT&T database of faces ซึ่งเป็นข้อมลชุดเล็กๆ ประกอบไปด้วยใบหน้าของคน 40 คน คนละ 10 รูป เท่านั้น จุดประสงค์เพื่อความง่ายต่อการเรียนรู้โดยใช้ข้อมูลขนาดเล็ก

Quick notebook walk through

Google Colab notebook link HERE

ภาพหน้าตัวอย่างจาก dataset

ขั้นตอนแรกเราจะ explore ดูก่อนว่าภาพใบหน้าจาก dataset นี้ หน้าตาเป็นอย่างไร แต่ละรูปภาพมีขนาดเท่าไหร่ จะเห็นว่าภาพมีขนาด 112x92 pixel, เป็น grey scale image, และ แต่ละภาพแสดงใบหน้าของบุคคลเดียวกันในสีหน้า ท่าทางต่างๆกัน

load รูปภาพเข้ามาเก็บใน tensor ขนาด 105*105: แบ่งเป็น train set 30 หน้า test set 10 หน้า
ตัวอย่างภาพหน้าหลังจาก store ใน tensor และถูก transform ขนาดแล้ว

ขั้นตอนถัดไปเราจะ load file ภาพเข้ามาเก็บไว้ใน tensor เพื่อความสะดวกเวลา load ข้อมูล train model ต่อไป โดยจะแยกออกเป็น tensor สำหรับข้อมูล train 30 ใบหน้าและ test 10 ใบหน้า ขนาดของ tensor จะมีทั้งหมด 5 dimension คือ (จำนวนใบหน้าที่แตกต่าง (30/10),จำนวนใบหน้าต่อคน(10), image channel (1 สำหรับ grey scale), Height(105), Width(105))

สร้าง triplet loader โดย method get_batch ทำหน้าที่โหลด triplet ออกมาตามขนาด batch_size สำหรับ train model และ method make_oneshot_task สำหรับ สร้าง task ทดสอบ model
ตัวอย่าง output จาก get_batch
output ตัวอย่างจาก make_oneshot_task

ขั้นตอนถัดไป คือ สร้าง data loader ที่สามารถสุ่มเลือก triplet (reference, positive, negative) ออกมาตามขนาด batch size ที่กำหนดสำหรับ train model สำหรับการทดสอบ model ด้วย test set เราสร้าง task ด้วยการสุ่มเลือกใบหน้า reference มา 1 ใบหน้า เรียกว่า test image และ อีก 10 ใบหน้า ซึ่งประกอบไปด้วย 9 ใบหน้าของคนที่ต่างกัน และ อีก 1 ใบหน้าที่เป็นคนเดียวกัน เรียกว่า support set โดยให้ model ทาย distance ระหว่าง test image กับแต่ละภาพใน support set ถ้า model ทาย distance ระหว่างหน้าเดียวกันออกมาได้น้อยที่สุด ถือว่าทายถูก สุ่มแบบนี้ซ้ำๆ ตามจำนวนที่ต้องการและคำนวณออกมาเป็น accuracy

สร้าง feature extractor จาก Resnet34
สร้าง Triplet Net ซึ่งมี output เป็น tuple ของ Euclidean distance ของ positive pair และ negative pair

ขั้นตอนถัดไป คือ การ define architecture ของ neural network โดยเราจะใช้ Resnet34 มาเป็น CNN network สำหรับ extract feature จากรูปภาพ เนื่องจากความสะดวกที่สามารถ import prebuilt architecture จาก Pytorch มาได้เลย แต่เราไม่จำเป็นต้องใช้ pretrained weight เนื่องจาก Resnet ถูก train มาจากข้อมูลชุด Image net (ภาพสัตว์ ต้นไม้ และสิ่งของ มากกว่า 20,000 ชนิด) สำหรับ ทำ Image classification ซึ่งไม่ค่อยตรงกับ distribition ของข้อมูลชุด AT&T database of faces เท่าไหร่

ค่า loss ที่ learning rate ต่างๆ ในการ scan learning rate

หลังจากกำหนด architecture ของ neural network เรียบร้อย และ เราจะ train model โดยกำหนด batch size สำหรับ data loader = 8 เพราะขนาด training data เรามีขนาดเล็ก และค่า alpha = .8 (ทำการ fine-tune มาแล้วคร่าวๆ) และ learning rate ที่ 2*10^-5 หาจากวิธีการ scan learning rate อ้างอิงจาก method lr_finder ใน fastai (อ้างอิงมาจาก paper https://arxiv.org/pdf/1803.09820.pdf ) โดยกำหนดให้ train model ทั้งหมด 10,000 iteration

accuracy ของ model ในการทำ one shot task กับ 10 ใบหน้าอยู่ที่ 70–80% ใน 10,000 iteration
ผลสุ่มจาก one shot task จะเห็นว่า Euclidean distance ระหว่าง ใบหน้าเดียวกัน (base image กับ ภาพแรกใน support set) ต่ำที่สุด

หลังจาก train ครบ ได้ accuracy อยู่ในช่วง 70% — 80% และทดลองสร้าง task ทดสอบแบบสุ่มขึ้นมา จะเห็นว่า distance ระหว่างใบหน้าของคนเดียวกัน น้อยกว่า distance ระหว่างใบหน้าที่ต่างกัน

Reference

  1. Facial similarity with Siamese Network in Pytorch: ทำ face recognition กับ AT&T database of faces โดยใช้ Siamese Network และ Contrastive loss

2. One Shot Learning and Siamese Networks in Keras: ทำ character recognition กับ Omniglot dataset โดยใช้ Siamese Network และ L1 distance + binary cross entropy loss -> triplet data loader modify มาจาก blog นี้

https://sorenbouma.github.io/blog/oneshot

3. Understanding Ranking Loss, Contrastive Loss, Margin Loss, Triplet Loss, Hinge Loss and all those confusing names: คำอธิบายเกี่ยวกับ pairwise และ triplet loss function

https://gombru.github.io/2019/04/03/ranking_loss/

--

--

Witchapong Daroontham
DATAWIZ

Data scientist at Central Technology Organization — CTO, Bangkok & life long learner