C# İle Yapay Sinir Ağı Eğitimi – 4

Merhabalar, C# ile yapay sinir ağı eğitimi serimize devam ediyoruz. Bu yazımda veriseti oluşturma işlemlerini anlatacağım. Veriseti, yapay sinir ağı için en önemli şeylerden biri ki zaten yapay sinir ağı danışmanlı öğrenme algoritmalarından biri. Danışmanlı öğrenmenin amacı zaten var olan verilerle (veriseti) eğitim yapıp sonrasında verisetinde olmayan verileri tahmin etmek yada sınıflandırmak. Bu yüzden veriseti olmazsa yapay sinir ağı da olmaz. Bu yazımda da işte bu vazgeçilmez olan verisetini oluşturacağız. Veriseti, çözülecek olan problemin bağımlı değişkenler ve bağımlı değişkenlere göre elde edilen çıkış değerlerinin bulunduğu veriler kümesi. Bunu C# tarafında yapmanın yolu ise, her bir bağımlı değişkeni tutan bir sınıf oluşturacak ve başka bir sınıfta da bu sınıfı liste olarak tutacağız.
Ben UCI Maching Repository’de bulunan enerji verimliliği verisetini kullanacağım. Bu verisetinde 8 tane bağımlı değişken, 2 tane de çıkış değerinden oluşan toplam 768 veri bulunmakta ve bence başlangıç için ideal bir veriseti 🙂 Verisetini buradan indirebilirsiniz. 2 tane çıkış değeri var fakat şu an için bir tanesi yeterli, o yüzden 2. çıkış değerini siliyorum ben. Verisetinide indirdikten sonra artık kod tarafına geçebiliriz.
Verisetine, veriler kümesi demiştik ya hani, öncelikle verileri tutan sınıfımızı yapalım. Veri sınıfımızda da verisetinde bulunan 8 tane öz nitelik için toplam 8 tane, 1 tane çıkış içinde 1 tane olmak üzere 9 tane property ekleyelim. Bağımlı değişkenlerin property isimleri X1, X2, X3, X4, X5, X6, X7 ve X8, çıkış değeri içinde Y1 olsun. Sınıfımızıda herhangi bir interface yada abstract classtan türetmeyeceğiz. Yani şöyle olacak;
public class Veri
{
public double X1 { get; set; }
public double X2 { get; set; }
public double X3 { get; set; }
public double X4 { get; set; }
public double X5 { get; set; }
public double X6 { get; set; }
public double X7 { get; set; }
public double X8 { get; set; }
public double Y1 { get; set; }
}
Verisetinin tamamını tutmak içinde bir tane Veriseti sınıfı oluşturalım ve bu sınıfı List türünden türetelim. Yapay sinir ağlarında kullanılacak verisetini doğrudan verilerin ham hali ile kullanmıyoruz, verileri 0-1 aralığına çekmemiz gerekiyor. Çünkü örneğin bi verinin 935345732423424 olduğunu düşünün, bu veri üzerinde toplama ve aktivasyon fonksiyonlarını gerçekleştirmek biraz zahmetli olabilir. Bu yüzden verileri büyüklükleriyle doğru orantılı olacak şekilde 0-1 aralığına çekiyoruz ki işlem yoğunluğunu azaltalım ve büyük sayılarla uğraşmak zorunda kalmayalım. Yaptığımız bu işleme ise normalizasyon deniyor. Ve normalizasyonun da matematiksel formülü;

Buradaki z verinin normalize edilmiş halini, x verinin orijinal halini, min(x) verinin bulunduğu sütundaki minimum değeri, max(x) ise verinin bulunduğu sütundaki maksimum değeri temsil etmekte. Verisetinde normalizasyon yapmamız gerekiyor ya, bu işlemi Veriseti sınıfında yapabiliriz. Yani bir tane NormalizeEt() adında fonksiyon ekleyip, yukarıdaki resimde olan formülü işletebiliriz. Verisetini, Veri sınıfından türettiğimiz içinde o sınıfa ait propertyleride ekleyerek, indirdiğimiz verisetini normalize ederek C# tarafına geçirebiliriz. Ayrıca eğitim esnasında verisetinden verileri sıralı yada rastgele olarak alıp eğitimi yaptırabiliriz, her ikiside yapılabilir fakat ben rastgele almayı tercih ediyorum. Çünkü her eğitimde ve hatta her epochta farklı veriler geldiği için kodlama aşamasında farklı verilerle test etme imkanım oluyor. Yani biz oluşturulan verisetini rastgele olarak tekrardan sıralayacağız. Bu işlemi de ayrıca bir fonksiyonda yapalım, çünkü birbirinden bağımsız her işlem ayrı bir fonksiyonda yapılmalı ki kimin ne yaptığını anlayabilelim. Yani Veriseti sınıfının kodu şöyle olacaktır;
public class Veriseti : List<Veri>
{
internal Veriseti NormalizeEt()
{
double x1Min = this.Min(x => x.X1);
double x2Min = this.Min(x => x.X2);
double x3Min = this.Min(x => x.X3);
double x4Min = this.Min(x => x.X4);
double x5Min = this.Min(x => x.X5);
double x6Min = this.Min(x => x.X6);
double x7Min = this.Min(x => x.X7);
double x8Min = this.Min(x => x.X8);
double y1Min = this.Min(x => x.Y1);
Veriseti set = new Veriseti();
set.AddRange(this.Select(x => new Veri { X1 = (x.X1 - x1Min) / (this.Max(y => y.X1) - x1Min), X2 = (x.X2 - x2Min) / (this.Max(a => a.X2) - x2Min), X3 = (x.X3 - x3Min) / (this.Max(p => p.X3) - x3Min), X4 = (x.X4 - x4Min) / (this.Max(a => a.X4) - x4Min), X5 = (x.X5 - x5Min) / (this.Max(y => y.X5) - x5Min), X6 = (x.X6 - x6Min) / (this.Max(z => z.X6) - x6Min), X7 = (x.X7 - x7Min) / (this.Max(e => e.X7) - x7Min), X8 = (x.X8 - x8Min) / (this.Max(k => k.X8) - x8Min), Y1 = (x.Y1 - y1Min) / (this.Max(a => a.Y1) - y1Min) }));
return set;
}
internal Veriseti VerisetiniKaristir()
{
Veriseti veriseti = new Veriseti();
veriseti.AddRange(this.AsEnumerable().OrderBy(x => Guid.NewGuid()));
return veriseti;
}
}
Yazdığımız kodları biraz irdeleyecek olursak; NormalizeEt() fonksiyonunda verisetinde X1–X8 arası bağımlı değişkenlerin ve ayrıca çıkış değeri sütunlarının en küçük değerini alıp ayrıca değişkenlere atadık. Çünkü normalizasyon formülüne bakacak olursanız min(x) yani verinin bulunduğu sütunun minimum değeri 2 kere kullanılıyor. Yani bu değere 2 kere ihtiyacımız olacak. Minimumu bulma gibi ilgili sütunun tüm verilerini gezerek sınama gibi zahmetli bir işi 2 kere tekrarlamak zahmeti ikiye katlayacağından hepsini bir değişkene atadık ve normalizasyon formülü içinde bu değişkenleri kullanacağız. C#’ın en güzel nimetlerinden biri olan Linq’nun Select() fonksiyonu ile tüm veriseti içerisinden verileri normalizasyon formülünden geçirerek kolaylıkla aldık ve oluşturduğumuz veriseti içerisine ekledik. Son olarakta oluşturduğumuz verisetini geriye döndürerek al sana normalize edilmiş verilerden oluşan veriseti diyebildik.
VerisetiniKaristir() fonksiyonunda ise yine Linq’nun fonksiyonlarından olan OrderBy() ile sıralama yaptık ve sıralama kuralını da Guid.NewGuid() olarak belirledik. Guid.NewGuid(), System kütüphanesi altında bulunan Guid sınıfından yeni bir nesne veriyor bize ve liste içerisinden rastgele değerler çekmeyi sağlıyor. Rastgele çekilen verileri de yeni bir Veriseti sınıfı içerisine doldurup onuda geriye döndürüyoruz.
Hem NormalizeEt() hemde VerisetiniKaristir() fonksiyonlarında yeni bir Veriseti sınıfı oluşturuyoruz çünkü normalize etme işlemini doğrudan aynı veriseti üzerinde yaparsak veriler değişeceği için fonksiyon içerisinde bulunan veriler değişeceğinden sütundaki maksimum değerlerde değişecektir. Buda normalizasyonun yanlış yapılmasına neden olacaktır. VerisetiniKaristir() fonksiyonunda ise verilerin üst üste binmesine, dolayısıyla veri kaybına neden olacağından her iki fonksiyonda da yeni bir Veriseti nesnesi oluşturup elde edilen verileri onun içine doldurarak oluşabilecek olumsuzlukların önüne geçtik.
Evet bu yazımda bu kadar olsun, çünkü verisetinide hallettik. Bi sonraki yazımda verisetini Excel üzerinden okuyup bu yazımda oluşturmuş olduğumuz Veri ve Veriseti sınıflarına doldurma işlemlerini yapalım. Bu yazı zaten yeterince uzun oldu, daha fazla uzatıp sıkıcı olmayalım dimi 🙂
Tüm seriye buraya tıklayarak ulaşabilirsiniz. Herkese hayırlı günler.