TRAIN COUNT
Yapay sinir ağı modelimizde tam-bağlantılı (fully connected) bir YSA bulunmaktadır. Giriş katmanında 784 giriş, Orta katmanda 128, Çıkış katmanında 10 nöron bulunmaktadır.
Projemize başlangıç adımı olan, kütüphane tanımlamaları ile başlayalım. Projemizde kullanacağımız olan "pthread" , "fstream" , "iostream" , "ctime" , "string" , "math" , "iomanip" ve kendimizin oluşturduğu olan "color.h" kütüphanelerini projemize ekliyoruz.
Sonra yine projemizde kullanacağımız olan default değerlerimizi tanımlıyoruz. #define BIAS 1 // Yanlılık Değeri #define INPUT_SIZE 784 // 28*28 Image'in Pixel Sayısı #define TRAIN_COUNT 60000 // Sisteme Öğretilecek Değer Sayısı #define TEST_COUNT 10000 // Sistemde Test Edilecek Değer Sayısı #define LAMDA1 0.000001 // Öğrenme Değeri 1 #define LAMDA2 0.9 // Öğrenme Değeri 2 #define NUM_OF_THREADS 8
Pthread tanımlamarınıda yapıp struct yapısı tanımlıyoruz ve içeriğinde Matrix ve Array değişkenlerini tanımlıyoruz. Class tanımını yapıyoruz ve private tanımlı struct nesnelerini oluşturuyoruz. Bu nesneler ile struct yapısında tanımlı olan Matrix ve Array kullanacağız. Class'ın public kısmında da method tanımlamalarımız yapıyoruz. pthread_t threads[NUM_OF_THREADS]; // Thread Tanımı for Pthread pthread_mutex_t mutex; // Mutex Tanımı for Pthread pthread_cond_t cond; // Condition Tanımı for Pthread T Nesnesi : .csv formatlı resim pixel verileri (T.Matrix) W Nesnesi : 1. ağırlık matrisi (W.Matrix) Y Nesnesi : 2. ağırlık matrisi (Y.Matrix) A Nesnesi : Ara katman dizisi (A.Array) R Nesnesi : Ara katmana relu normalizyon uygulanmış dizi (R.Array) O Nesnesi : Çıkış katman dizisi (O.Array) S Nesnesi : Çıkış katmana softmax normalizyon uygulanmış dizi (S.Array) C Nesnesi : Beklenen çıkış dizisi. (C.Array)
Bu kısımda 1. ve 2. Ağırlık matrislerimizi oluşturacağız. Bunun için verilen .txt dosyası okuma işlemi yapıldı. CreateWeightMtrx( std::string img_file_path, std::string name ){...} isimli methodumuzda "img_file_path" ile okunacak .txt dosyamızın yolunu belirtiyoruz. "name" ile de 1. ağırlık matrisimizi mi yoksa 2. ağırlık matrisimizi mi oluşturacağımızı belirtiyoruz. (W ve Y kullanarak.)
.txt dosyamızlarımızda ilk satır bize matrisimizin yüksekliğini yani satır sayısını veriyor. sonraki her satır matrisimizin satır bilgilerini içeriyor. 1. Ağırlık matrisimiz için her satırda 784 veri + 1 tane BIAS değerimiz mevcuttur. 2. Ağırlık matrisimiz için her satırda 128 veri + 1 tane BIAS değerimiz mevcuttur. Bu verileri dosyadan okuma işlemi yaparak ağırlık matrislerimizi oluşturuyoruz.
Bu method ile .csv formatlı dosyamızdaki verileri okuyup sisteme öğretmek istediğimiz resimlerin pixel değerlerini matris haline dönüştürüyoruz. Oluşturulacak olan matrisin her satırı bir resimin pixel değerlerini içermektedir. CreatTrainMtrx( std::string img_file_path ) methodu ile tanımlamızı yapıyoruz. "img_file_path" ile okunacak olan .csv dosyasının yolunu belirtiyoruz. LearnRowSize(img_file_path, "Train", 0); methodu ile .csv dosyasının her satırının boyutunu bir "RowSize" dizimize yazıyoruz. Burdaki amacımız .csv dosyasındaki istediğimiz satıra gidebilmek için cursoru boyutu kadar ileri konumlandırmak ve ordan sonraki satırı okumaktır.
Sonrasında "pthread_create()" ile threadlerimizi tanımlıyoruz ve ilgili üye methodumuza gönderiyoruz. Bu kısmı paralelleştimemizin amacı her satırı tek tek okuyarak matrisimize yazmaktansa satır sayımızı thread sayısına göre pay ederek zamandan tasarruf etmektir. Burda yük dengelemesi olarak blok veri paylaşımı kullanılmıştır. Üye methodumuz olan "CTrainM()" ile threadlerimiz çalışmaya başlıyor. Bu method içinde olan "ReadTrainDatas()" methoduyla .csv dosyasındaki istediğimiz satırı yani resim bilgilerini okuyabiliyoruz. Son olarak "pthread_join()" ile threadler için bir toplanma alanı oluşturuyoruz.
LearnRowSize( std::string img_file_path, std::string TrainOrTest, int DataCount ) methoduyla .csv dosyasının her satırının boyutunu bir "RowSize" dizimize yazıyoruz. Burdaki amacımız .csv dosyasındaki istediğimiz satıra gidebilmek için cursoru boyutu kadar ileri konumlandırmak ve ordan sonraki satırı okumaktır.
"img_file_path" ile okuyacağımız olan .csv dosyasının yolunu belirtiyoruz. "TrainOrTest" değişkeni ile Train işlemi mi yoksa Test işlemimi yapacağız onu belirtiyoruz. "DataCount" ile eğer Train yada Test işlemi yapmayacaksak belirtilen .csv dosyasından ilk "DataCount" kadar satırın boyutunu dizimize yazacaktır. Bunuda kendimize yeni bir test dosyası oluştururken kullanıyoruz.
ReadTrainDatas( std::string img_file_path, int row ) methodu ile "img_file_path" ile yolu belitilen .csv dosyamızdaki "row" ile belirtilen satır verilerini okuyoruz. Ve geçici olarak bir diziye yazıyoruz. Sonrasında bu dizideki verileri Train matrisimiz olan T.Matrix matrisimize yazıyoruz.
Bu methodumuzla sisteme öğretme yani öğrenme adımına başlıyoruz. Burdaki amaç input değerlerimizi 1. ve 2. ağırlık matrislerimizi kullanarak sonuç olarak 10 elemanlı bir dizi elde edeceğiz ve bu dizimizi istenen dizimizle kıyaslayıp bir hata değeri bulacağız. Eğer bulduğumuz hata değeri 10^-5 ten küçükse öğretme işlemi(sistemin öğrenme işlemi) o resim için tamam demektir. Fakat hata değerimiz 10^-5 ten büyükse, geri yayılım uygulamamız gerekiyor. 1. ve 2. ağırlık matrislerimizi güncelleyerek tekrar ileri yayılım yapmalıyız. İleri yayılım sonucunda yeni bulacağımız 10 elemanlı dizimizi istenen dizimizle tekrar kıyaslayıp yeni hata değeri hesaplayacağız. Bu döngü hata değerini istediğimiz seviyeye çekene kadar devam edecektir.
DeepLearning() methodumuzda ilk olarak global tanımlanan matrislerimiz ve dizilerimizi dinamik oluşturuyoruz. Üye fonksiyonlar ile paralelleştirme yapabildiğimiz için üye fonksiyonu içinden Class'ın elemanlarına ulaşamıyoruz. Bu yüzden global değişkenler, diziler, matrisler ile işlemlerimizi yapıp, işi biteni class elemanlarına atayacağız ve global dizi veya matrisleri silme(delete) işlemi uygulayacağız.
Sonrasında "pthread_create()" ile threadlerimizi oluşturuyoruz ve ilgili üye fonksiyona yönlendiriyoruz. Threadler işlerini bitirince "pthread_join()" ile bir toplanma alanı belirtiyoruz.
"pthread_create" işleminden önce tanımlanan "pthread_mutex_init" ile üye fonksiyonumuzda kullanacağımız olan mutex'i başlatıyoruz. "pthread_cond_init" ile de yine üye fonksiyonumuzda kullanacağımız olan condition'u başlatıyoruz. Mutex bize birbirini dışlama (Mutually Exclusive) sağlar. Yani threadlerimizi senkronize edeceğiz fakat birbirine karışmamaları gerekiyor. Mutex bu karışmayı engelliyor diyebiliriz. Cond bize yarış (condition) durumundan kurtulmamızda işe yarıyor. Yarış dediğimiz olay, threadlerin aynı veriye ulaşmak istemesi durumunda birbirleri ile yarış haline düşebilirler ve program bu yarış durumundan gecikmelere yada kilitlenmelere düşebilir. Bunu cond ile önlüyoruz.
Öğrenme aşamasındaki üye methodumuza ulaşmış bulunmaktayız. Bu methodta ileri yayılım ve geri yayılım adımlarını gereçekleştireceğiz.
İleri yayılımın ilk adımı olan 1 resim için elde ettiğimiz input verileri ile 1. ağırlık matrisimizi çarpıyoruz. ([128][785]*[785][1] = [128][1]) Bu işlemi "FirstLayerForNext(start, end)" methodu ile gerçekleştiriyoruz. Satır sayısına göre threadlerimizi eşit pay ediyoruz. Yani blok veri paylaşımı yapmış oluyoruz. Bu işlemin sonucunda elimizde 128 elemanlı bir ara katman A[] dizisi elde ediyoruz. Bu A[] dizimize relu normalizasyon "relu(_A< Type >, col)" methoduyla uyguluyoruz ve default tanımlı BIAS değerini dizimize ekliyoruz. Elimizde yeni ara katman R[] dizisi oluşuyor. İleri yayılımdaki 1. katmanı tamamlamış olduk. Sırada 2. katman adımları var. R[] dizimiz ile 2. Ağırlık matrisimizi çarpıyoruz([10][129]*[129][1] = [10][1]). Bu çarpma işlemini "SecondLayerForNext(start, end)" methodu ile gerçekleştiriyoruz. Bu işlemin sonucunda elimizde 10 elemanlı çıktı O[] dizisi elde ediyoruz. O[] dizisine softmax normalizasyon "softmax(_O< Type >, row)" methodu kullanarak uyguluyoruz. Elimizde yeni çıktı S[] dizimiz oluşuyor. Bu S[] dizimiz 0 ile 1 arasında değerler almaktadır ve içlerinden sadece bir tanesi 1'e çok yakındır, diğerleri 0'a yakındır.
Bu aşamadan sonra bir hata değeri hesaplamamız gerekiyor. Hata değerini çıkış dizimizden istenen dizi değerlerini farkının karelerinin toplamını alarak ortalamasını alıyoruz. Yani pow(S[i] - C[i], 2) tüm indislerinin toplamının ortalaması. Burdaki hata değerimiz 10^-5 ten küçükse öğretme o resim için tamamlanmıştır. büyükse geri yayılım işlemine başlıyoruz.
Geri yayılım adımlarına terstten başlıyoruz. Yani 2 katmandan başlayarak ilk 2. Ağırlık matrisimizi sonrasında 1. katmandan devam ederek 1.Ağırlık matrisimizi güncelleyeceğiz.
Geri yayılımda zincirleme kuralı uygulanır. (türevC[] / türevY[]) = (türevO[] / türevY[]) * (türevS[] / türevO[]) * (türevC[] / türevS[]) Burdaki tüm türevleri kodumuzda "SecondLayerForBack(start, end)" ve "softmaxDerivative()" methodlarıyla gerçekleştirdik. Sonuç olarak 2. Ağırlık matrisimizi(Y[]) güncelledik. Şimdi 1. katman için geri yayılım zincirleme kuralı uygulayacağız. Bu aşamada da "FirstLayerForBack(start, end)", relu türevi, "softmaxDerivative()" methodları threadler kullanılarak uygulanmıştır.
Sonuç olarak burda bir döngü oluşur. sürekli güncellenen ağırlık matrislerimizle elde edilen hata değerimize göre döngü ya sonlanır yada istediğimizi alana kadar dönmeye devam eder.
"FirstLayerForNext(int _start, int _end)" İleri yayılım 1. katman matris çarpım işlemi methodudur. Threadler kullanılarak blok veri paylaşımına göre yük dağılımı yapılmış ve method sorunsuz çalışmıştır.
"SecondLayerForNext(int _start, int _end)" İleri yayılım 2. katman matris çarpım işlemi methodudur.
"SecondLayerForBack(int _start, int _end)" Geri yayılım 2. katman zincirleme kuralı türev alma işlemi methodudur. Burada LAMDA2(L2) Öğrenme katsayısı kullanılmıştır. Bu katsayı deneme yanılmayla bulduğumuz sistem için en iyi sabit değerdir.
"FirstLayerForBack(int _start, int _end)" Geri yayılım 1. katman zincirleme kuralı türev alma işlemi methodudur. Threadler kullanılarak blok veri paylaşımına göre yük dağılımı yapılmış ve method sorunsuz çalışmıştır. Burada LAMDA1(L1) Öğrenme katsayısı kullanılmıştır. Bu katsayı deneme yanılmayla bulduğumuz sistem için en iyi geri yayılım sayısına göre değişen değerdir.
"relu(Type r[], int row)" İleri yayılım 1. katman relu normalizasyonu uygulama işlemi methodudur.
"softmax(Type o[], int row)" İleri yayılım 2. katman softmax normalizasyonu uygulama işlemi methodudur.
"softmaxDerivative(Type o[], int row, int h)" Geri yayılım 1. ve 2. katmanda kullanılan softmax türevi işlemi methodudur.
Bu method da sisteme öğrettiğimiz train.csv dosyasındaki 60000 veriden TEST_COUNT sayısı kadar random satır okuyarak yeni test matrisimizi oluşturuyoruz.
Bu method da sisteme öğrettiğimiz train.csv dosyasındaki 60000 veriden TEST_COUNT sayısı kadar random satır okuyarak yeni test matrisimizi oluşturuyor. Tabi bu adımları yapmamız için yine "SubRowSize()" methodumuzu çalıştırıyoruz. Sonrasında "pthread_create()" ile threadlerimizi oluşturup ilgili üye fonksiyonumuza "CTest_FM()" yönlendiriyoruz. "CTest_FM()" fonksiyonumuzda threadler çalışmaya başlıyor, ve bu fonksiyon içinde de "ReadTrainData()" methodu çalışıyor. Tek farkı Test dosyası olarak çalışıyor olmasıdır. Sistem aynı tanımlama adımındaki belittiğimiz gibi çalışmaktadır.
Bu methodla test.csv dosyamızdan TEST_COUNT sayısı kadar satır okuyoruz. Ve test matrisimizi oluşturuyoruz.
Tabi bu adımları yapmamız için yine "SubRowSize()" methodumuzu çalıştırıyoruz. Sonrasında "pthread_create()" ile threadlerimizi oluşturup ilgili üye fonksiyonumuza "CTestM()" yönlendiriyoruz. "CTestM()" fonksiyonumuzda threadler çalışmaya başlıyor, ve bu fonksiyon içinde de "ReadTrainData()" methodu çalışıyor. Tek farkı Test dosyası olarak çalışıyor olmasıdır. Sistem aynı tanımlama adımındaki belittiğimiz gibi çalışmaktadır.
Bu Method içerisinde sadece ileri yayılım yapıyoruz. Elde ettiğimiz çıktı dizimizi(S[]) istenen çıktı(C[]) dizimizle kıyaslıyoruz. eğer dizilerin içeriği benzerse TRUE benzer değilse FALSE değerini geri dönürüyor. Sistemin döndürdüğü TRUE ve FALSE değerlerine göre başarı yüzdemizi hesaplıyoruz.
Main kodumuzda ilk olarak kullanacağımız dosyaların yollarını değişkenlere atadık. Sonrasında Model Class'ımızdan double NeuralNet nesnemizi oluşturduk.
NeuralNet.CreateWeightMtrx(weight1, "W"); 1. ağırlık matrisi olan W[128][785] oluşturuldu. NeuralNet.CreateWeightMtrx(weight1, "Y"); 2. ağırlık matrisi olan Y[10][129] oluşturuldu.
CreatTrainMtrx(train); Train Matrisimiz oluşturuldu.
DeepLearning(); Sistemimize Train matrisindeki verileri(örnek resimlerimizi) öğrettik.
CreateTest_FM(train, testMy, TEST_COUNT); 60000 lik Train.csv dosyamızdan random olarak TEST_COUNT kadar satırlar çekerek yeni test matrisimizi oluşturduk. CreatTestMtrx(testMy); bu komut uygulanmış olsaydı, hazır elimizde olan test dosyasını kullarak TEST_COUNT kadar test matrisimizi oluşturacaktık.
Testing(); Öğrettiğimiz sistemde test matrisimizi test ettik.
Aşağıdaki iki test çıktılarında Mnist_test.csv dosyasından ilk TEST_COUNT sayısı kadar satır okuyarak matrisimizi oluşturduk ve testimizi bu matris üzerinden gerçekleştirdik.
Eğitim Matrisi Oluşma Zamanı : 9.6sn Sistemin Öğrenme Zamanı : 886sn Test Matrisi Oluşma Zamanı : 5sn Başarı Yüzdesi : 2%
Eğitim Matrisi Oluşma Zamanı : 5sn Sistemin Öğrenme Zamanı : 704sn Test Matrisi Oluşma Zamanı : 3sn Başarı Yüzdesi : 2%
Aşağıdaki dört test çıktılarında biz train.csv dosyasından random yeni satırlar okuyarak yeni matrisimizi oluşturduk ve testimizi o yeni matris üzerinden gerçekleştirdik. Her test aşamasında test matrisim farklı oluştuğu için başarı yüzdemde ufak artışlar yada azalmalar olmuştur.
Eğitim Matrisi Oluşma Zamanı : 57sn Sistemin Öğrenme Zamanı : 3849sn Test Matrisi Oluşma Zamanı : 68sn Başarı Yüzdesi : 79%
Eğitim Matrisi Oluşma Zamanı : 32sn Sistemin Öğrenme Zamanı : 3254sn Test Matrisi Oluşma Zamanı : 59sn Başarı Yüzdesi : 82%
Eğitim Matrisi Oluşma Zamanı : 18sn Sistemin Öğrenme Zamanı : 1207sn Test Matrisi Oluşma Zamanı : 15sn Başarı Yüzdesi : 64%
Eğitim Matrisi Oluşma Zamanı : 10sn Sistemin Öğrenme Zamanı : 915sn Test Matrisi Oluşma Zamanı : 11.5sn Başarı Yüzdesi : 67%
TRAIN COUNT
TEST COUNT
NUM OF THREAD
TRAINING PERCENT
Copyright © Yapay Sinir Ağları - Protected by ThreaData