Singleton Tasarım Kalıbı ve Yapay Öğrenme
Tasarım kalıpları (design patterns) yazılım mühendisliği için en iyi pratiklerken yapay öğrenme çalışmalarında çokça atlanılabilmektedir. Bu yazımızda python ile bir yapay öğrenme uygulamasında singleton tasarım kalıbına neden ihtiyaç duyulduğunu ve nasıl kullanılabileceğini tartışacağız.
Yegane!
Türkçe’ye tek nesne veya yegane olarak çevrilebileceğimiz bu tasarım kalıbını bir sınıfı yalnızca bir kez oluşturmanız temeline dayanır. Her ne kadar soyut bir açıklama olsa da makine öğrenimi uygulamalarına bire bir oturmaktadır.
Bir yapay öğrenme hayat döngüsü temelde 3 adımdan oluşmaktadır: model dizaynı, eğitim ve tahmin. Eğitim adımını çevrimdışı olarak nadiren uygularız. Ancak üretim ortamında eğitim adımının yerini daha önceden eğitilmiş modelin kullanılması almaktadır. Eğitim görece uzun bir adımken (saatler mertebesinde), daha önceden eğitilmiş bir modelin yüklenmesi ise kompleks bir işlemdir (dakikalar mertebesinde). Örneğin VGG-Face modeli 145M, FaceNet 22M, ArcFace 34M parametre barındırır.
Senaryo
Çok sayıda yüz çiftinin aynı kişi olup olmadığının doğrulanması senaryosunu göz önüne getirelim. Yüz çiftlerini python’da liste olarak saklayabiliriz. Bunun için bir for döngüsü kuracağız ve her iterasyonda doğrulama fonksiyonunu çağıracağız.
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 |
#main.py import function face_pairs = [ ["img1.jpg", "img2.jpg"], ["img1.jpg", "img3.jpg"], ["img1.jpg", "img4.jpg"], ["img1.jpg", "img5.jpg"], ["img1.jpg", "img6.jpg"], ["img1.jpg", "img7.jpg"], ["img1.jpg", "img8.jpg"], ["img1.jpg", "img9.jpg"], ["img1.jpg", "img10.jpg"], ["img1.jpg", "img11.jpg"] ] for face_pair in face_pairs: img1 = face_pair[0] img2 = face_pair[1] function.verify(img1, img2) |
Yüz çiftlerinin uzunluğu 10 olduğu için doğrulama fonksiyonu 10 kere çağırılacak.
Şimdi de doğrulama fonksiyonunun içeriğine göz atalım. Her birinin vektör kalıbının çıkarılması için her yüz resmini yüz tanıma modeline aktaracağız. Bu sebeple ilk önce yüz tanıma modelini inşa etmeliyiz. Bu arada, DeepFace’in modelleme fonksiyonu kullanıyorum.
1 2 3 4 5 6 7 8 9 10 11 |
#function.py from deepface import DeepFace #pip install deepface def verify(img1, img2): model = DeepFace.build_model("Facenet") img1_embedding = model.predict(img1) img2_embedding = model.predict(img1) |
FaceNet, modelin dizaynı ve önceden eğitilmiş ağırlıkların yüklenmesi için 3.10 sn’ye ihtiyaç duymakta. Model bir kere ayağa kaldırıldıktan sonra ise tahminleme işlemi 1.05 sn sürüyor. Dolayısıyla for döngümüzün her adımında model dizaynı, ağırlıkların yüklenmesi ve tahmin için 4.15 sn’ye ihtiyaç duyacağız. Fakat burada tasarım olarak bir yanlış söz konusu. Çünkü her adımda modelin yeniden kurulmasına gerçekten ihtiyacımız var mı?
Nesneye Dayalı Programlama
Bildiğiniz gibi nesneye dayalı programlama güçlü kalıtım yapısı ile birlikte gelir. Bir sınıf daha ilk ayağa kalkarken Facenet modelini inşa ederse bu tekrar tekrar inşa probleminin önüne geçmiş oluruz.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
#wrapper.py from deepface import DeepFace class Wrapper: def __init__(self): self.model = DeepFace.build_model("Facenet") def verify(self, img1, img2): img1_embedding = self.model.predict(img1) img2_embedding = self.model.predict(img1) |
Böylelikle wrapper sınıfımızı bir kere yükleyeceğimiz için Facenet modeli de bir kere yüklenecek. Dolayısıyla doğrulama fonksiyonuna hali hazırda inşa edilmiş model aktarılıyor olacak. Önceki kurgumuza göre döngünün her adımı için 3 sn kazanmış olacağız.
1 2 3 4 5 6 7 8 9 10 11 12 |
from wrapper import Wrapper obj = Wrapper() for face_pair in face_pairs: img1 = face_pair[0] img2 = face_pair[1] obj.verify(img1, img2) |
Sıradan Python ile Singleton
Eğer yazılım mühendisliği disiplininden geliyorsanız nesneye dayalı programlamaya aşina olacağınız için yukarıda kurguladığımız yapı kafanıza oturacaktır. Öte yandan yazılım gelişitiricisi değilseniz sınıf yapıları kafanızı karıştırabilir. Sınıf yapılarına gerek duymadan sıradan python kodu yazarak da singleton tasarım kalıbını uygulayabiliriz. Samimi olmak gerekirse python’un basitliğini seven biri olarak bu yöntemi daha çok seviyorum.
İlk olarak globak bir model değişkeni tanımlayacağız ve sonrasında da bunun var olup olmadığını kontrol edeceğiz. Daha önce yaratılmamışsa modeli inşa edip değişkene bunun içeriğini besleyeceğiz.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
#function.py from deepface import DeepFace def build_model(): global model if not "model_obj" in globals(): model = DeepFace.build_model("Facenet") return model def verify(img1, img2): model = build_model() img1_embedding = model.predict(img1) img2_embedding = model.predict(img1) |
Böylelikle for döngümüzün ilk adımında Facenet modeli yaratılacak sonraki iterasyonlarda da yaratılmış bu model kullanılacaktır. Bu yaklaşım zaman olarak nesneye dayalı yaklaşım ile aynı olacaktır.
1 2 3 4 5 6 7 8 9 10 11 12 |
#main.py import function for face_pair in face_pairs: img1 = face_pair[0] img2 = face_pair[1] function.verify(img1, img2) |
Sonuç
Bu yazımızda singleton yazılım tasarım kalıbını neden kullanmaya ihtiyaç duyduğumuzu gerçek bir senaryo üzerinden işledik. Tasarım kalıpları, yazılım mühendisliği için en iyi pratiklerden derlenmiştir. Her ne kadar makine öğrenimi uygulamalarında gözden kaçabilse de üretim ortamı odaklı uygulamalar için anlaşılacağı üzere olmazsa olmazdır. Öte yandan makine öğrenmesi çalışmalarında modellerin inşası yazılım projelerindeki sınıflardan zaman ve hesaplama gücü perspektifinden çok daha karmaşık olabilmektedir. Bu sebeple yazılım mühendisliğinde olduğu kadar önemli bir tasarım kalıbını incelemiş olduk.
Bu yazı Singleton Design Pattern in Machine Learning yazısından Türkçe’ye çevrilmiştir.