Makale Özeti

Bu makalemde sizlerle beraber Data Mining(Veri Madenciliği) algoritmalarından Naive Bayes algoritmasını inceleyeceğiz. Algoritmanın çalışma şeklini çözdükten sonra ise bu algoritmayı çalıştırıp bize sonuçları üretebilecek bir bileşen yazacağız.

Makale

        Makalemin ilk kısmında naive bayes algoritmasına ve bu algoritmanın avantajlarına değinmiştik. Şimdi bu algoritmayı C# ile yazalım.

        Öncelikle NaiveBayes algoritmasını çalıştırabileceğimiz bir class yapalım ve bu class'ın adını NaiveBayes koyalım.

using System;

using System.Collections.Generic;

using System.Text;

using System.Data;

 

namespace NaiveBayes

{

    public class NaiveBayes

    {

        Şimdi ise Training ve Testing datalarını taşıyacağımız iki property olan training ve testing dataset propertylerini yazalım. Bu uygulamamızda veriler DataTable ile taşınacaktır.

        private DataTable _TrainingSet;
        public DataTable TrainingSet
        {
            get { return _TrainingSet; }
            set { _TrainingSet = value; }
        }
 
        private DataTable _TestingSet;
        public DataTable TestingSet
        {
            get { return _TestingSet; }
            set { _TestingSet = value; }
        }

        Şimdi yazacağımız property'ler ise sonuçların bulunduğu kolonun adını taşıyan ResultColumnName, datanın sonuç üretmek için ilk önce bir sefere mahsus işleme tabi tutulmasının gerekliliğini denetleyebilmek amacıyla IsDataProcessed ve test sonucu doğru ve yanlış tahmin sayısının tutulduğu CorrectlyPredictedCount ve IncorrectlyPredictedCount propertyleridir.

        private string _ResultColumnName;
        public string ResultColumnName
        {
            get { return _ResultColumnName; }
            set { _ResultColumnName = value; }
        }
 
        private bool _IsDataProcessed = false;
        public bool IsDataProcessed
        {
            get { return _IsDataProcessed; }
        }
 
        private int _CorrectlyPredictedCount = 0;
        public int CorrectlyPredictedCount
        {
            get { return _CorrectlyPredictedCount; }
        }
 
        private int _IncorrectlyPredictedCount = 0;
        public int IncorrectlyPredictedCount
        {
            get { return _IncorrectlyPredictedCount; }
        }

        Şimdi yazacağımız 3 yerel değişkende sonuç üretiminde kullanılacak verinin biraz önce bahsettiğim ön işlem sonuçlarının tutulması için gereklidir. Bu değişkenlerden bir tanesi Dictionary tipinde dir ve adı deColumnValues dir. Bu değişkende her kolonda bulunan veriler distinct bir şekilde tutulmaktadır. Yukarıdaki örnek için bu değerler Outlook'ta Sunny,Overcast ve Rain olabilmektedir.

        Bir diğer değişken ise DataTable tipinde dtResultPossibilityRatio değişkenidir. Bu değişkende ise daha önceden tanımlanmış ResultColumnName alanında bulunan farklı kayıtlara göre sonuçların gelme olasılığı tutulmaktadır. Yukarıdaki örnekte bu değişkenin değeri Yes için 9 No için 5 değerini barındıracak bir datatable'dır. Ve
bu datatable'ın yapısı aşağıda gösterilmektedir.
       

        Son değişkenimiz ise hangi kolonda bulunan verinin hangi sonucu ne kadar etkileyeceğine dair bilgilerin tutulduğu datatable dır ve adı dtResult'tur. Bu datatable'ın yapısını inceleyelim ve yorumlayalım.

        Bu tabloda bulunan veriden sonuç No olduğunda Outlook'un sunny olduğu 3 kayıt vardır. sonuç yes olduğunda ise 2 kayıt vardır şeklindeki bir bilgiye çok çabuk bir şekilde ulaşabilmekteyiz.

        Şimdi bu değişkenleri dolduracağımız metodumuzu yazalım.İlk önce tanımladığımız datatable lara verileri saklayabilmemiz için gerekli kolonları ekliyoruz.

        public void ProcessData()
        {
            dtResult.Columns.Add("ColumnName", typeof(string));
            dtResult.Columns.Add("ColumnData", typeof(string));
            dtResult.Columns.Add("ResultColumnValue", typeof(string));
            dtResult.Columns.Add("Count", typeof(int));
 
 
            dtResultPossibilityRatio.Columns.Add("ColumnValue", typeof(string));
            dtResultPossibilityRatio.Columns.Add("Ratio", typeof(int));

        Şimdi ise her bir kolon için bulunan farklı değerleri bir list'e toplayacağız ve daha sonrasında bu listi kolon adı key kendisi ise value olacak şekilde bir hashtable'a atacağız.

            foreach (DataColumn dc in TrainingSet.Columns)
            {
                List<string> l = new List<string>();
                foreach (DataRow dr in TrainingSet.Rows)
                {
                    if (!l.Contains(dr[dc.ColumnName].ToString()))
                    {
                        l.Add(dr[dc.ColumnName].ToString());
                    }
                }
                deColumnValues.Add(dc.ColumnName, l);
            }bsp; }

        Şimdi ise her bir sonucun oluşma ihtimalini hesaplayıp datatable'a yazacağız.

            foreach (string res in deColumnValues[ResultColumnName])
            {
                dtResultPossibilityRatio.Rows.Add(new object[] { res, TrainingSet.Select(ResultColumnName + "='" + res + "'").Length });
            }

        Şimdi bulunan her kolondaki her farklı verinin sonuca etki etme olasılığını hesaplayarak bunu dtResult datatable değişkeninde saklayacağız. Bu işlemler bittikten sonra datanın hesap için hazırlanması bitmiş durumdadır. bundan dolayı IsDataProcessed değişkeninin true ya set ediyoruz.

            foreach (DataColumn dc in TrainingSet.Columns)
            {
                if (dc.ColumnName != ResultColumnName)
                {
                    foreach (string s in deColumnValues[dc.ColumnName])
                    {
                        foreach (string s1 in deColumnValues[ResultColumnName])
                        {
                            int count = TrainingSet.Select(dc.ColumnName + "='" + s + "' AND " + ResultColumnName + "='" + s1 + "'").Length;
                            dtResult.Rows.Add(new object[] { dc.ColumnName, s, s1, count });
                        }
                    }
                }
            }
            _IsDataProcessed = true;
        }

        Bu kısımda ise kullanıcı tarafından verilen bir datarow'da bulunan verilerin predict edilmesi işlemini gerçekleştirecek metodu yazacağız. Bu metod her olasılık için sonuçları hesaplayacak ve en yüksek olasılığa sahip olanı bize döndürecektir. Kodu açıklamak gerekirse bu kodda ilk önce datanın process edilip edilmediğine bakılmaktadır. Data process edilmediyse data ilk önce process edilmektedir. Daha sonra olası her sonucun gerçekleşme ihtimali ile verilen datarowda bulunan verilerin bu sonuca etki oranları yukarıda anlattığımız gibi çarpılarak o sonucun çıkma olasılığı hesaplanmaktadır. Kodun son kısmında ise en büyük olasılığa sahip olan sonuç string olarak geri döndürülmektedir.

        public string Predict(DataRow dr)
        {
            if (!IsDataProcessed)
            {
                ProcessData();
            }
            string winner = "";
            decimal winnerValue = 0;
            foreach (string s in deColumnValues[ResultColumnName])
            {
                int resultCount = Convert.ToInt32(dtResultPossibilityRatio.Select("ColumnValue='" + s + "'")[0]["Ratio"]);
                decimal result = Convert.ToDecimal(resultCount) / TrainingSet.Rows.Count;
                foreach (DataColumn dc in TrainingSet.Columns)
                {
 
                    if (dc.ColumnName != ResultColumnName)
                    {
                        result = result * Convert.ToDecimal(dtResult.Select("ColumnName='" + dc.ColumnName + "' AND ColumnData='" + dr[dc.ColumnName] + "' AND ResultColumnValue='" + s + "'")[0]["Count"]) / resultCount;
                    }
                }
                if (result > winnerValue)
                {
                    winnerValue = result;
                    winner = s;
                }
            }
 
            return winner;
        }

        Kullanacağımız bir metod ise verimizi test etmek için kullanacağımız Test metodudur. Bu metodda ise verilen Test datasında bulunan her datarow için predict metodu çalıştırılmakta ve tahmin edilen sonucun doğru sonuç olup olmadığı kontrol edilip duruma göre doğru veya yanlış cevap sayısı arttırılmaktadır.

        public void Test()
        {
            foreach (DataRow dr in TestingSet.Rows)
            {
                if (Predict(dr) == dr[ResultColumnName].ToString())
                {
                    _CorrectlyPredictedCount++;
                }
                else
                {
                    _IncorrectlyPredictedCount++;
                }
            }
        }
 
 
    }
}

        Oluşturduğumuz class'ın genel yapısını inceleyecek olursak şeması aşağıdaki gibidir.

        Şimdi bu yazdığımız algoritmaları test etmek amacıyla ufak bir windows uygulaması yazalım.Bu uygulamada iki tane button bulunsun ve bu buttonlardan biri belirli bir koşulun sonucunu bize verirken diğeri ise algoritmamızın uyumluluğu için test yapıyor olsun.
 



        İlk butonumuz verilen koşullara göre sonucu tahmin etmektedir. Bu kısımda kullanılan Örnek DMHelper.GetSampleData() metodu bize birazönceki örnekte incelediğimiz datayı döndürecek static bir metoddur. Örnek kodun içinde mevcuttur.
            DataTable dt = DMHelper.GetSampleData();
            NaiveBayes nb = new NaiveBayes();
            nb.TrainingSet = dt;
            nb.ResultColumnName = "PlayTennis";
 
            DataRow dr = dt.NewRow();
            dr["Outlook"] = "Sunny";
            dr["Temperature"] = "Hot";
            dr["Humidity"] = "Normal";
            dr["Wind"] = "Strong";
 
            MessageBox.Show(nb.Predict(dr));

        İkinci button ise bize verdiğimiz datatable'lar sonucunda algoritmamızın başarılılık oranını vermektedir.

            DataTable dt = DMHelper.GetSampleData();
            NaiveBayes nb = new NaiveBayes();
            nb.TrainingSet = dt;
            nb.TestingSet = dt;
            nb.ResultColumnName = "PlayTennis";
            nb.Test();
 
            MessageBox.Show("Correctly Classified Instances : " + nb.CorrectlyPredictedCount.ToString());
            MessageBox.Show("Inorrectly Classified Instances : " + nb.IncorrectlyPredictedCount.ToString());

        Bu makalemde naive bayes algoritmasının C# kullanılarak nasıl yazılabileceğini inceledik. Umarım faydalı olmuştur.

         oztamer@hotmail.com
         tamer.oz@yazgelistir.com
         oztamer@hotmail.com
Ornek Uygulama