Ara 182018
 
4.844 kez okundu

OpenCV ile Yüz Tanıma Bölüm 3-2

Önceki yazımda veri setimizi nasıl oluşturacağımızı anlatmıştım.

Bu kez veri setimizi Keras kullanarak eğiteceğiz. Sonra da yeni resimleri bu eğitilmiş veri setini kullanarak sınıflandıracağız.

3. ADIM – EĞİTİM

Projemize örnek olarak aldığımız çalışmada Adrian, Keras modellemesi için SmallerVGGNet sınıfını kullanıyor. Ben onun yerine daha basit bir model kullandım (vgg_like). Eğer isterseniz yazının sonundaki Referanslar bölümünde yer alan linkleri kullanarak SmallerVGGNet kodlarına ulaşabilirsiniz.

Lafı uzatmadan eğitim betiğimize geçelim. Betiğimizin adı: train.py

İşin içine eğitim girince bir sürü kütüphane ve modüle ihtiyaç duyuyoruz. Kullanacağımız kütüphaneler hakkında kabaca da olsa, bilgi sahibi olduğunuzu umuyorum. Yoksa bu yazıyı 1000-1500 kelimelik bir hacme sığdırmak olanaksız hale gelirdi.

İlk 27 satırımızda matplotlib, keras, sklearn, imutils, numpy, random, pickle, cv2 ve os kütüphanelerimizi içe aktarıyoruz. smallervggnet kütüphanesini de belki kullanırsınız diye listeden çıkarmadım.

Modeli daha basit tutmak amacıyla vgg_like() fonksiyonunu tanımladım. (VGGNet, 2014 yılındaki Imagenet Büyük Ölçekli Görsel Tanıma Yarışmasında çok iyi performans gösteren bir yapay sinir ağıdır. VGG (Visual Geometry Group) Oxford Üniversitesi Mühendislik Bilimleri Bölümünün bir alt birimidir.)

Eğitim için Doğrusal(Sequential) bir model kullanıyoruz – satır 30
Görsellerimiz 96×96 boyutlu ve 3 renk kanalına sahip.
Modelimize ilk önce bir Conv2D katmanı uyguluyoruz – satır 31
İkinci katmanımız yine Conv2D – satır 32
Üçüncü katmanda MaxPooling2D var – satır 33
34. satırdaki Dropout ile %25’lik giriş bilgisini devre dışı bırakıyoruz (overfitting engelleme).

Benzer işlemleri bir kez daha tekrarlıyoruz. Ama boyutlamalar biraz farklı. satır 36-39

Son aşamada modelimize önce Flatten(), sonra Dense() uyguluyoruz. satır 41-42
Dropout oranımız %50 satır 43
Son işlemimiz her zamanki gibi ‘softmax’ satır 44
45.satırda modelimizi geri döndürüyoruz.

47. satırda işlem başlangıç zamanını t0 olarak not ediyoruz. Bu değişkeni belirli aşamalarda geçen süreyi belirtmek için kullanacağız.

Satır 48-51’de sabitlerimizi tanımlıyoruz.

data ve labels boş birer listedir. satır 53-54

58. satırda görsel dosya yollarımızı bir listede topluyoruz (imagePaths).

imagePaths listesinin satırlarını karıştırırken, sonraki denemelerde de aynı rasgele değerleri elde edebilmek için seed değerini 42 olarak belirliyoruz. satır 60 (Bu değer herhangi bir başka sayı da olabilir. Önemli olan hep aynı başlatma değerini kullanmak.)

63. satırda görsel dosyalarını işlemeye başlıyoruz.
Dosyayı okuyoruz – satır 64
İmajı 96×96 olarak yeniden boyutlandırıyoruz – satır 65
İmajı bir dizi (array) haline dönüştürüyoruz – satır 66
Ve imaj dizisini data listesine ekliyoruz – satır 67

Dosya yolundan etiket bilgisini (sanatçı_kodu) ayrıştırıyoruz – satır 70
Etiket bilgisini labels listesine ekliyoruz – satır 71

data listesini elemanları 0 veya 1 olacak şekilde bir numpy dizisine çeviriyoruz – satır 75
Aynı şekilde labels listesini de bir numpy dizisi haline getiriyoruz – satır 76

Satır 81-82’de etiketleri sayısallaştırıyoruz.

Satır 85-86: data ve labels dizi elemanlarının %20’sini test, geri kalanını eğitim için ayırıyoruz.

Veri sayımız kısıtlı olduğu için data çoğullama (augmentation) işlemi yaptırıyoruz – satır 89-91 (Çoğullama sırasında döndürme, kaydırma, yükseklik değiştirme, kesme, büyütme ve yatay aynalama (flip) işlemleri yapılacak.)

Modelimizi vgg_like() fonksiyonu ile oluşturuyoruz – satır 99

model.summary(), model katmanlarımızın değişimini ayrıntılı bir şekilde açıklıyor. Bu özeti hem ekrana, hem de dosyaya kaydediyoruz. – satır 102-105

Optimizasyon işlemi için Adam da yaygın bir şekilde kullanılıyor. Benim yaptığım denemelerde SGD daha iyi sonuçlar almamı sağladı. Her ikisini de deneyebilirsiniz. Satır 108

110. satırda modelimizi derliyoruz.

Eğitim işlemleri (satır 116-120) için bir kaç dakika bekliyoruz.

Eğitim tamamlandığında, önce modelimizi (satır 124), sonra da etiketlerimizi (satır 128-130) kaydediyoruz.

Son aşama olarak, eğitim işlemlerimiz sırasında hesaplanan kayıp ve doğruluk değerlerini grafiğe döküyor ve plot.png adıyla kaydediyoruz. – satır 135-146

OpenCV ile Yüz Tanıma Bölüm 3-2

Artık sınıflandırma işlemlerimize geçebiliriz. Ama daha önce model özetimize kısaca bir göz atsak iyi olacak:

Conv2D katman çıkış boyutlarında 2 piksellik küçülme yaratıyor.
MaxPooling2D ise (2,2) oranları ile her iki yönde boyutları yarıya indiriyor.
Flatten işlemi katmanı tek boyutlu bir diziye dönüştürür.
Dropout input değerlerinden bir bölümünü işlem dışı bırakır. Böylece overfitting olasılığı azalır.

4. ADIM – SINIFLANDIRMA

Sınıflandırma işlemlerinde eğitim aşamasında kullanmadığımız görsellerden yararlanacağız. Her sanatçı için birer görsel seçtik ve bunları kontrol klasörünün altına kopyaladık.

İlk 7 satırda gerekli kütüphaneleri içeri aktarıyoruz.

imajlar listesi sınıflandıracağımız fotoğrafları tutuyor. satır 10

Sınıflandırma işlemlerini check_image() fonksiyonu ile yapacağız (satır 12). Her görsel ayrı ayrı işleme sokulacak.

Satır 13-16’da imajımızı 96×96 piksel boyutlarına küçültüyor ve modelimize uygun bir numpy dizisine dönüştürüyoruz.

Satır 19 ve 20’de eğitim sonunda kaydettiğimiz model ve etiket bilgilerini belleğe alıyoruz.

Modele göre bulunan tahmin ve olası doğruluk derecesini saptıyor; etiket değerini alıyoruz. satır 23-25

Sınıflandırma sonucunu genişliğini 400 pikselle sınırlandırdığımız imajımızın üzerine işliyoruz. satır 28-33

İşlenmiş imajı ekranda gösterip diske kaydettikten sonra bir tuşa basılmasını bekliyoruz. satır 35-38

Tuşa basıldığında belleği temizliyoruz: satır 39

check_image() fonksiyonumuzu her resim için ayrı ayrı çağıran döngümüz satır 42-45 arasında.

Elde ettiğim görüntüler aşağıda:

OpenCV ile Yüz Tanıma Bölüm 3-2

OpenCV ile Yüz Tanıma Bölüm 3-2

OpenCV ile Yüz Tanıma Bölüm 3-2

SmallerVGGNet kullanarak yaptığım denemelerde daha yüksek sayısal doğruluk oranlarına ulaştım ama, yapılan tahminlerin pek çoğu aslında hatalıydı. Bu, veri kümemizin çok dar olmasından kaynaklanmış olabilir.

Aynı eğitim setini yüz saptama fonksiyonlarıyla birlikte kullanarak grup fotoğraflarından çoklu yüz tanıma işlemleri gerçekleştirmek mümkün olabilir.

Açıklamalara böyle bir blog yazısında daha kapsamlı bir şekilde girmek mümkün olmuyor. Sorularınız varsa, yorum alanından iletebilirsiniz.

Bu tür çalışmalar çok geniş bir kapsama alanına sahip. Yapılan işlemler ve elde edilen sonuçlar deneysel nitelikte. Bu yüzden, bu tür konulara ilgi duyuyorsanız, siz de kendi modellerinizi geliştirin. Mümkünse veri kümeleri oluşturun. Deneyin. Yorumlayın.

Elbette hala öğrenecek çok şey var. Ama yapabileceğimiz şeyler de pek çok…

Önemli olan, hangi düzeyde olursak olalım, deneysel bakış açımızı yitirmemek ve hevesimizi kaybetmemek… Hata yapmak bizi korkutmasın. Hatalar, kullanmasını bilenler için en etkin yol göstericilerdir.

Yeni yazılarda buluşmak üzere…

Beni izlemeye devam edin.

Ahmet Aksoy

Referanslar:
https://github.com/kjaisingh/pokemon-classifier/blob/master/pyimagesearch/smallervggnet.py
https://www.pyimagesearch.com/2018/04/16/keras-and-convolutional-neural-networks-cnns/
https://hackernoon.com/learning-keras-by-implementing-vgg16-from-scratch-d036733f2d5
http://www.robots.ox.ac.uk/~vgg/research/very_deep/

Ara 172018
 
5.520 kez okundu

OpenCV ile Yüz Tanıma Bölüm 3-1

Bu yazımda 3 Yeşilçam emektarının fotoğraflarını içeren küçük bir veri setini oluşturmak için gerekli işlemleri ele alacağım. Eğitim ve sınıflandırma işlemleri ise bir sonraki yazımın konusu olacak.

OpenCV ile Yüz Tanıma Bölüm 3-1

Orijinal kodları yine PyimageSearch web sitesinden edinmek mümkün. Adrian, anime karakterleri kullanmış. Ben ise Yeşilçam karakterlerini tercih ettim. Ayrıca kodları biraz daha basitleştirdim ve gerekli yerlerini Türkçeleştirdim.

Veri seti için Google üzerinden ulaşabildiğim fotoğrafların sayısı hiç ummadığım kadar düşük çıktı. Oysa çok fazla fotoğrafa ulaşabileceğimi umuyordum. Bu proje için 3 sanatçımızın toplam 147 farklı fotoğrafına ulaşabildim. Sayının azlığına rağmen sınıflandırma değerleri fena değil.

Projemizde 4 ayrı betiğimiz var:
1- Dosyadaki linklerin okunup, ilgili imaj dosyalarının internetten indirilip kaydedilmesi
2- Kaydedilen dosyaların denetlenerek, içeriği aynı olan dosyaların teke düşürülmesi
3- Veri setinin eğitilmesi
4- Eğitilmiş veri seti bilgilerini kullanarak örnek dosyaların sınıflandırılması

VERİ SETİMİZİ OLUŞTURMAK

Sanatçılarımız: Türkan Şoray, Sadri Alışık ve Cüneyt Arkın. Amacım sadece çalışma yöntemini örneklemek olduğu için sayıyı daha fazla arttırmadım. Belki ileride, bu veri setini genişletip çok daha zengin ve işlevsel bir projeye dönüştürmek mümkün olabilir.

1. ADIM – VERİ SETİNİN OLUŞTURULMASI

İnternet üzerinden ulaştığım görsel linklerini, “linkler.txt” isimli bir metin dosyasına kaydettim. Bu dosyayı linkler.txt adresinden indirebilir veya tamamen kendiniz oluşturabilirsiniz.
Dosya yapısı şöyle:
sanatçı_kodu,link1
sanatçı_kodu,link2

Sanatçı kodları aynı zamanda data klasörünün içindeki klasörlerin de adlarıdır. O nedenle kodlarda Türkçe karakter kullanmadım. Kodlar ve klasör isimleri şu şekildedir:
c_arkin
s_alisik
t_soray

Betik kodlarını kısa tutmak için data klasörünü ve onun altındaki klasörleri kendim oluşturdum.

Çalışma klasörümüzün adı “yesilcam”. Alt dizin yapısı da şu şekilde:
yesilcam
|–data
| |–c_arkin
| |–s_alisik
| |–t_soray
|–kontrol
|–__pycache__

Önce ilk betiğimizi (resim_indir.py) inceleyelim:

skimage kütüphanesi, imaj dosyalarımızı doğrudan internet üzerinden okuyabilmek için gerekli. Ancak bu kütüphane RGB renk sistemini kullanıyor. Bu yüzden 22 nolu satır ile renk sistemini OpenCV’nin kullandığı BGR sistemine çeviriyoruz.

Linkleri tutan dosyamızın adı “linkler.txt”.

Sorunsuz okunan dosya sayısını tutmak için ok, sorunluları saymak içinse hata değişkenlerini kullanıyoruz.

kac sözlük değişkeni, her sanatçı için dosya isimlerini ayrı ayrı sayılaştırmamızı sağlıyor.

Satırları tek tek okuyoruz. # ile başlayan veya boyu 3 karakterden kısa olan satırları atlıyoruz.

İşlenecek satırları “,” ile parçalıyoruz. İlk parçada sanatçı kodu bulunuyor. Normalde ikinci parçanın link adresini tutması lazım. Ancak, sayıları az olsa da, bazı link adreslerinde “,” işaretinin kullanıldığına rastlayabiliriz. Bu nedenle -eğer varsa- 3. parçayı 2. parçaya ekleyerek doğru adresi elde ediyoruz.

17. satırda başlattığımız try bloğu, hatalı okumalardan kaçınmamızı sağlıyor.

kim değişkeni sanatçı kodunu, url ise imaj adresini saklıyor.

kac anahtarları içinde kim stringi yoksa, ilk kez tanımlanacak demektir. Başlangıç değeri olarak 1 atıyoruz.

21. satırda dosyamızı internet üzerinden okuyor; 2. satırda renk sistemini BGR haline çeviriyor ve 23. satırda okuduğumuz imajı görselleştiriyoruz.

24. satırdaki tuş kontrolü, gerektiğinde, Esc tuşuna basarak döngüyü kırabilmemizi sağlıyor. Bu satır aynı zamanda görselleştirme işlemleri için de gerekli.

27-36 satırlarını kullanarak dosyayı data klasörü altındaki sanatçı klasörüne (kim) kaydediyoruz. Dosya isimlendirmesinde bir çakışma olması ihtimalini dikkate alıyor, ama bu sayının 100’den fazla olmasına izin vermiyoruz. (Dikkat! Eğer kendi veri setinizi oluşturacaksanız, kullanacağınız link sayısını dikkate almak için bu sınırı değiştirmeniz gerekebilir.)

41. satırda son işlem olarak bilgisayar belleğini temizliyoruz.

İmajlar farklı boyutlarda olduğu için işlenmeleri farklı süreler gerektirebiliyor.

(Not: Çalışmalarımı Pycharm üzerinde yapıyor ve çalıştırıyorum. Ancak bu betiği terminal üzerinden çalıştırmak zorunda kaldım. Aynı durum sizin başınıza da gelebilir. Betiği çalıştırmak için Pycharm’ın terminal modunu kullanabilirsiniz.)

2. ADIM – İMAJ TEKRARLARININ ÖNLENMESİ

Bu betiğimizde doğrudan md5 hesaplamak yerine imagehash kütüphanesindeki dhash() metodunu uyguluyoruz. Bu metod hash hesabını imajları belli bir boyuta indirip gri renge çevirdikten sonra yapıyor. Yeniden boyutlandırma biraz zaman alıyor olsa da sonuçlar daha güvenilir.

İkinci betiğimizde os, PIL ve imagehash kütüphanelerini kullanıyoruz.

hash() fonksiyonumuzda okuma işlemini PIL.Image modülü ile gerçekleştiriyor ve elde edilen imaja imagehash.dhash() işlemini uyguluyoruz. (Doğrudan md5 hesabı yaptığımızda birbirine çok benzer bazı imajlar gözden kaçabiliyor.)

Tekrarlanan imaj dosyaları tekrar_sil() fonksiyonunun içinde temizleniyor.

19. satırda path değişkenimizle aktarılan klasör yolu bir döngü yapısı içinde kök, klasör ve dosya bileşenlerine ayrıştırılıyor ve her bir dosya için hash() değeri hesaplanıp, hash_dict sözlük değişkeninde saklanıyor. Eğer eşdeğer hash değeri (key) bulunursa, aynı anahtarın karşılığına mükerrer dosyanın adı ekleniyor. Böylece len(hash_dict[k]) değeri birden büyük olan kayıtlar, mükerrer dosyaları tutuyor.

25-30 satırları mükerrer dosyaların silinmesini sağlıyor.

34. satırda data klasörünü işleme sokuyoruz. Böylece onun altındaki tüm klasör ve dosyaları taramış oluyoruz.

Sizi daha fazla yormamak ve veri setini hazırlamanız zaman tanımak için bu yazıyı burada kesiyorum. Bir sonraki yazımda, oluşturduğumuz veri setini önce eğiteceğiz, sonra da eğitim setinde yer almayan kontrol görsellerinin kime ait olduğunu sorgulayacağız.

Beni izlemeye devam edin.

Ahmet Aksoy

Referanslar:

https://www.pyimagesearch.com/