BİZ KİMİZ

alternative

Karadeniz Teknik Üniversitesi - Bilgisayar Mühendisliği - Paralel Bilgisayarlar - ThreaData Grup Üyesi

Nurullah DEMİRCİ - Bilgisayar Mühendisi

alternative

Karadeniz Teknik Üniversitesi - Bilgisayar Mühendisliği - Paralel Bilgisayarlar - ThreaData Grup Üyesi

Adnan KURU - Bilgisayar Mühendisi

alternative

Karadeniz Teknik Üniversitesi - Bilgisayar Mühendisliği - Paralel Bilgisayarlar - ThreaData Grup Üyesi

Hasan RAKICI - Bilgisayar Mühendisi

PROJE SUNUM VİDEOMUZ

KOD ANALİZ

Kodumuzu C++ kullanarak yazdık ve paralelleştirme için PThread kullandık. Kodumuzda tipleri template olarak tanımladık. Fakat işlemlerimizi double tip üzerinden gerçekleştirdik ve double tanımlamasını main içerisinde nesnemizi double olarak oluşturarak gerçekleştirdik. Kodumuzu 3 aşamadan oluşmaktadır. 1. Kütüphaneler, Class ve Matrislerin ve dizilerin dinamik tanımlanması 2. Öğrenme(training) Aşaması 3. Test etme Aşaması
alternative

Kütüphaneler, Default Değişkenler, Global Değişkenler, Struct, Class ve Bazı Method Tanımlamaları

    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)

alternative

Ağırlık Matrisleri Methodu

    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.

alternative

Öğrenme(Train) Matrisi Methodu

    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.

alternative

LearnRowSize Methodu

     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.

alternative

ReadTrainDatas Methodu

     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.

alternative

DeepLearning() Methodu

     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.

alternative

Üye Method Concured_DP() Methodu

    Öğ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.

alternative

İleri Yayılım ve Geri Yayılım Aşamasındaki Katman Adım Methodları

    "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.

alternative

CreateTest_FM() Methodu

    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.

alternative

CreateTestMtrx() Methodu

    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.

alternative

Testing() Methodu

     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.

alternative

main() Methodu

    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.

TEST ANALİZ

Sistemimizi hazır bize sunulan test.csv dosyasından elde edilen test matrisimizle ve kendimizin oluşturduğu test matrisimizle test edeceğiz. Hem seri(1 Thread) hem de paralel(8 Thread) çalıştıracağız. Sonuç olarak başarı yüzdemizi ve çalışma sürelerini göreceğiz.

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.

alternative

Mnist_train.csv - TRAIN_COUNT = 1000
Mnist_test.csv - TEST_COUNT = 600
NUM_OF_THREADS = 1

Eğitim Matrisi Oluşma Zamanı : 9.6sn
Sistemin Öğrenme Zamanı : 886sn
Test Matrisi Oluşma Zamanı : 5sn
Başarı Yüzdesi : 2%

alternative

Mnist_train.csv - TRAIN_COUNT = 1000
Mnist_test.csv - TEST_COUNT = 600
NUM_OF_THREADS = 8

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.

alternative

Mnist_train.csv - TRAIN_COUNT = 6000
new_test.csv - TEST_COUNT = 3000
NUM_OF_THREADS = 1

Eğitim Matrisi Oluşma Zamanı : 57sn
Sistemin Öğrenme Zamanı : 3849sn
Test Matrisi Oluşma Zamanı : 68sn
Başarı Yüzdesi : 79%

alternative

Mnist_train.csv - TRAIN_COUNT = 6000
new_test.csv - TEST_COUNT = 3000
NUM_OF_THREADS = 8

Eğitim Matrisi Oluşma Zamanı : 32sn
Sistemin Öğrenme Zamanı : 3254sn
Test Matrisi Oluşma Zamanı : 59sn
Başarı Yüzdesi : 82%

alternative

Mnist_train.csv - TRAIN_COUNT = 2000
new_test.csv - TEST_COUNT = 600
NUM_OF_THREADS = 1

Eğitim Matrisi Oluşma Zamanı : 18sn
Sistemin Öğrenme Zamanı : 1207sn
Test Matrisi Oluşma Zamanı : 15sn
Başarı Yüzdesi : 64%

alternative

Mnist_train.csv - TRAIN_COUNT = 2000
new_test.csv - TEST_COUNT = 600
NUM_OF_THREADS = 8

Eğitim Matrisi Oluşma Zamanı : 10sn
Sistemin Öğrenme Zamanı : 915sn
Test Matrisi Oluşma Zamanı : 11.5sn
Başarı Yüzdesi : 67%

BAŞARI YÜZDESİ

1
K

TRAIN COUNT

1
K

TEST COUNT

1
CORE

NUM OF THREAD

1
%

TRAINING PERCENT