Python ile Yüz Tanıma Sistemleri Nasıl Geliştirilir
Derin öğrenme ile duygu, yaş ve cinsiyet, etnisite gibi konuların analizi hali hazırda yapabiliyoruz. Bu gibi çalışmaları genellikle konvolüsyonel nöral ağlar ile gerçekleştiriyoruz. Temel ihtiyacımız tespit etmek istediğimiz sınıfa ait örneklerin beslenmesine dayanıyor. Örneğin yüz resimlerinden duygu analizi yacaksak yüz resimleri girdimiz iken mutlu, mutsuz ya da sinirli olmak çıktımızı belirtiyor ve nöral ağ bu girdi ve çıktı arasındaki ilişkiyi öğreniyor. Yüz tanıma teknolojilerinde ise işleyiş biraz daha farklı. Bu yazıda ana hatlarıyla Python, TensorFlow ve Keras kullanarak nasıl bir yüz tanıma sistemi geliştireceğimize odaklanacağız.
Yüz tanıma ile gelen zorluk
100 kişiden oluşan bir topluluk için bir yüz tanıma sistemi geliştireceğinizi varsayalım. Her bir personaya ait de 10 resimden öğrenmesini bekleyelim. Nöral ağın girdisi yüz resimleri çıktısı ise 100 kişiden hangisinin olduğu olabilirdi. Ancak topluluğa eklenecek 101. kişinin olması durumunda sinir ağının çıktısının da 101 değerden oluşması gerekecek. Dolayısıyla ağ yapımızı topluluğa eklenecek her bir kişi için değiştirmemiz ve yeniden eğitmemiz gerekecektir. Benzer şekilde zaman geçtikçe eğitimi yaptığımız veri setindeki resimler eskiyecek, ağ belki de genç halinizin fotoğrafından yaşlı halinizi tanıyamayacaktır. Ya da saçını boyatan, kısaltan ya da uzatan kişiler olması durumunda neyle karşılaşacağımızı bilemiyoruz.
Tek seferde öğrenme
Yüz tanıma teknolojilerinde genel kabul tek seferde öğrenmeye (one shot learning) dayanmaktadır. Bazı kaynaklar bu yaklaşıma yüz tanıma (face recognition) yerine yüz doğrulama (face verification) da demektedir.
Size ait bir fotoğrafın bir konvolüsyonel nöral ağ ile özeti (embedding) çıkarılacak, güncel halinizin özeti ile karşılaştırılacak, benzerlikleri bir eşik değerden daha düşükse sizin kimliğiniz ile eşleştirilecektir.
Bu şekilde 100 kişilik bir topluluk için yüz tanıması yapmak istiyorsanız bu 100 kişinin yüz resimlerinin özeti nöral ağımız ile çıkarılacak ve bir veri tabanına kaydedilecek, topluluğa eklenecek 101. kişinin olması halinde sadece bu yeni kişinin yüzünün özeti veri tabanına eklenecek ama nöral ağımız aynı kalacaktır.
Benzerlik kontrolü
Sinir ağımızın çıktısını çıktı boyutunda bir vektör olarak ifade edebiliriz. Örneğin ağ modelimize 224x224x3 boyutunda resimler beslerken 2622 boyutunda çıktılar alıyorsak özet aslında 2622 boyutunda bir vektördür. Dolayısıyla iki yüz fotoğrafını bizler aslında iki vektör olarak ifade ettik.
Vektörler hatırlayacağınız gibi yönü ve uzunluğu olan niceliklerdir. İki vektörün ne kadar benzer olduğunu aralarında açı yani kosinüs benzerliği (cosine similarity) veya aralarındaki uzaklık yani Öklid uzaklığı (Euclidean distance) ile ifade edebiliriz.
İki boyutlu uzayda 2 noktanın arasındaki uzaklığı aşağıdaki şekilde hesaplayabiliyoruz.
Bunu n boyutlu uzaya yansıtırsak da bu formüle sadık kalmış oluruz. İşlemi numpy kütüphanesi ile gerçekleştirirsek radikal şekilde hızlanacaktır. Burada source_representation ve test_representation girdilerinin n boyutlu birer vektör olduklarını göz önünde bulundurun.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
"""def findEuclideanDistance(source_representation, test_representation): sum = 0 for i in range(0, source_representation.shape[0]): sum += pow(source_representation[i] - test_representation[i], 2) sum = math.sqrt(sum) return sum""" def findEuclideanDistance(source_representation, test_representation): euclidean_distance = source_representation - test_representation euclidean_distance = np.sum(np.multiply(euclidean_distance, euclidean_distance)) euclidean_distance = np.sqrt(euclidean_distance) return euclidean_distance |
Kosinüs benzerliğinin formülü ise aşağıdaki şekilde ifade edilmektedir.
Bunu da numpy ile aşağıdaki şekilde kodlayabiliyoruz.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
""" def findCosineDistance(source_representation, test_representation): sum = 0; a = 0; b = 0 for i in range(0, source_representation.shape[0]) sum += source_representation[i] * test_representation[i] a += source_representation[i] * source_representation[i] b += test_representation[i] * test_representation[i] return sum / (math.sqrt(a) * math.sqrt(b)) """ def findCosineDistance(source_representation, test_representation): a = np.matmul(np.transpose(source_representation), test_representation) b = np.sum(np.multiply(source_representation, source_representation)) c = np.sum(np.multiply(test_representation, test_representation)) return 1 - (a / (np.sqrt(b) * np.sqrt(c))) |
Yüzün algılanması
Zorunlu olmamakla birlikte yüz tanıma üretim hattının öncelikli adımları yüzün algılanması ve hizalanmasıdır. Yüzün algılaması için çeşitli çözümler bulunmaktadır. OpenCV içerisinde Haar ve SSD yöntemlerini, Dlib içerisinde HoG ve MMOD ve son olarak MTCNN yöntemleri en yaygın methodlardır. Performanslarını aşağıdaki videodan gözlemleyebilirsiniz.
Yüz tanıma modelleri
Yüzün özetini çıkarmak için kendi modelinizi eğitebileceğiniz gibi Oxford Üniversitesi Visual Geometry Group (VGG) tarafından VGG-Face, Google tarafından Facenet ve Carnegie Mellon Üniversitesi tarafından OpenFace modelleri en doğru yüz özetlerini çıkaracak şekilde optimize edilmiştir. Aralarında en başarılı model VGG-Face iken çok kompleks yapısı sebebiyle gerçek zamanlı çalışmalarda doğruluktan feragat edilip OpenFace tercih edilebilir.
VGG-Face mimarisiVGG-Face 224x224x3 boyutunda girdi beklerken (burada 3 renkli resim olması sebebiyle RGB kodlarını ayrı ayrı ifade etmektedir), 2622 boyutlu bir çıktı vektör üretmektedir. Modeli Keras kullanarak aşağıdaki şekilde dizayn edebiliriz.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 |
model = Sequential() model.add(ZeroPadding2D((1,1),input_shape=(224,224, 3))) model.add(Convolution2D(64, (3, 3), activation='relu')) model.add(ZeroPadding2D((1,1))) model.add(Convolution2D(64, (3, 3), activation='relu')) model.add(MaxPooling2D((2,2), strides=(2,2))) model.add(ZeroPadding2D((1,1))) model.add(Convolution2D(128, (3, 3), activation='relu')) model.add(ZeroPadding2D((1,1))) model.add(Convolution2D(128, (3, 3), activation='relu')) model.add(MaxPooling2D((2,2), strides=(2,2))) model.add(ZeroPadding2D((1,1))) model.add(Convolution2D(256, (3, 3), activation='relu')) model.add(ZeroPadding2D((1,1))) model.add(Convolution2D(256, (3, 3), activation='relu')) model.add(ZeroPadding2D((1,1))) model.add(Convolution2D(256, (3, 3), activation='relu')) model.add(MaxPooling2D((2,2), strides=(2,2))) model.add(ZeroPadding2D((1,1))) model.add(Convolution2D(512, (3, 3), activation='relu')) model.add(ZeroPadding2D((1,1))) model.add(Convolution2D(512, (3, 3), activation='relu')) model.add(ZeroPadding2D((1,1))) model.add(Convolution2D(512, (3, 3), activation='relu')) model.add(MaxPooling2D((2,2), strides=(2,2))) model.add(ZeroPadding2D((1,1))) model.add(Convolution2D(512, (3, 3), activation='relu')) model.add(ZeroPadding2D((1,1))) model.add(Convolution2D(512, (3, 3), activation='relu')) model.add(ZeroPadding2D((1,1))) model.add(Convolution2D(512, (3, 3), activation='relu')) model.add(MaxPooling2D((2,2), strides=(2,2))) model.add(Convolution2D(4096, (7, 7), activation='relu')) model.add(Dropout(0.5)) model.add(Convolution2D(4096, (1, 1), activation='relu')) model.add(Dropout(0.5)) model.add(Convolution2D(2622, (1, 1))) model.add(Flatten()) model.add(Activation('softmax')) |
Önceden eğitilmiş ağırlıklar Oxford VGG tarafından matlab formatında paylaşılmıştır. Ağırlıkları Keras formatında (.h5) dönüştürerek kendi Drive hesabımda paylaştım. Aşağıdaki şekilde model ağırlıklarını yükleyebilirsiniz. Son ağırlıklar siamese network kullanılarak eğitilmiştir.
1 2 3 4 5 6 |
from keras.models import model_from_json model.load_weights('vgg_face_weights.h5') |
Sonrasında iki ayrı resmin özetini çıkarmamız gerekecek.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
def preprocess_image(image_path): img = load_img(image_path, target_size=(224, 224)) img = img_to_array(img) img = np.expand_dims(img, axis=0) img = preprocess_input(img) return img vgg_face_descriptor = Model(inputs=model.layers[0].input, outputs=model.layers[-2].output) img1_representation = vgg_face_descriptor.predict(preprocess_image('1.jpg'))[0,:] img2_representation = vgg_face_descriptor.predict(preprocess_image('2.jpg'))[0,:] |
Yüz doğrulama
Özetlediğimiz iki resmin uzaklığı bir eşik değerinden düşükse bunlar aynı kişilerdir diyebileceğiz. Testlerimde cosinüs benzerliğinin daha doğru sonuçlar ürettiğini görmem sebebiyle burada bunu kullandım. Sizler öklid uzaklığını tercih edebilirsiniz.
1 2 3 4 5 6 7 8 9 10 |
cosine_distance = findCosineSimilarity(img1_representation, img2_representation) if(cosine_distance < 0.40): print("verified... they are same person") else: print("unverified! they are not same person!") |
Testler
Test sonuçları oldukça tatminkar görünüyor.
Gerçek zamanlı uygulamalar
Şu ana kadar anlattığımız kısmı gerçek zamanlı olarak da çalıştırabiliriz. Bundan önce kesilmiş yüz resimlerini modele beslerken gerçek zamanlı uygulamalarda OpenCV’nin yüz algılama (face detection) için geliştirdiği haar cascade modülünü kullanıyoruz.
Öte yandan VGG-Face yerine Facenet kullanmamız durumunda programın daha hızlı çalıştığını görebilirsiniz.
OpenFace de Facenet gibi gerçek zamanlı uygulamalar için daha uygundur.
Facebook ise insan sınırlarını aşan bir yüz tanıma sistemi olan DeepFace‘i duyurmuştu. Hız ve başarı konusunda oldukça başarılı olduğunu görebilirsiniz.
Öte yandan VGG-Face kullanılmaması halinde başarıdan feragat edileceğini unutmamakta fayda var.
Workshop
Modern bir yüz tanıma üretim hattındaki istasyonları incelediğimiz aşağıdaki workshop ilginizi çekebilir.
Hazır Yazılım İskeleti
Deepface python iskeleti ile bir kaç satır kod yazarak yüz tanıma işlemlerini de gerçekleştirebilirsiniz. İskelet, VGG-Face, Google FaceNet, OpenFace ve Facebook DeepFace modellerini, mukayese için de cosine ve euclidean uzaklıklarını kullanabilmekte. Detayları için reponun readme dosyasına göz atabilirsiniz.
Büyük Ölçekli Yüz Tanıma
Yüz tanıma işlemi çoklu sayıda yüz doğrulama işleminin uygulanmasına dayanmaktadır ve her bir yüz doğrulama işlemi bir CNN’in çağırılması anlamına gelmektedir ki bu da maliyetli bir işlemdir. Yine de bazı hacking yetenekleri uygulayarak bir resmi büyük bir veri tabanı içerisinde hızlıca bulabiliriz.
Son bir kaç söz
Bu yazıda yüz tanıma teknolojisinin arkasındaki yapıyı koda dökmeye çalıştık. Yüz tanıma teknolojisi geleneksel derin öğrenme uygulamalarındaki yaklaşımlardan farklı bir yoğurt yiyişi gerektirmekte.
Bu yazı boyunca yaptığımız uygulamanın koduna buradan erişebilirsiniz. Bunun dışında uygulamayı VGG-Face için yapmış olsak da Facenet ve OpenFace alternatiflerini de incelemenizi şiddetle tavsiye ederim.
Webinar: Yazılım Mühendisliği ile Makine Öğrenmesi Arasındaki Boşluğu Doldurmak
Her şey güzel hoş. 1 milyar fotoğraf içerisinden kaç dk da getireceksiniz bunu? Kaç dk da eşleştirme yapacaksınız? Hangi veritabanı sistemi ile getireceksiniz?
DeepFace’e ait represent fonksiyonunu kullanarak elde ettiğiniz embeddingleri a-nn algoritması ile ararsanız milyar seviyesinde veriniz olsa bile milisaniyeler mertebesinde. Algoritmaya ait detayları aşağıdaki linklerden bulabilirsiniz. Ayrıca milvus, pinecone gibi vektör veritabanları ve faiss, annoy gibi indeks çözümleri de bu algoritma ile arama yaptıkları için milyarlar seviyesinde olsa bile yine milisaniyeler mertebesinde sonuca ulaşabiliyorlar.
– https://sefiks.com/2023/12/31/a-step-by-step-approximate-nearest-neighbor-example-in-python-from-scratch/
– https://bilisim.io/2023/07/03/vektor-veritabanlari-vector-databases/