23 Eylül 2017 Cumartesi

Yapay Zeka Programlama (AI)


Günümüzde yapay zeka artık yavaş yavaş insan hayatına girmeye başlamaktadır. Peki Yapay Zeka Programlama (AI) nasıl yapılır? Bu soruya bu yazıda yapay zekanın programlama dili ile nasıl yapıldığını sizlere paylaşıyorum.

Yapay Zeka Programlama (AI) Sinir Ağları için Temel Nesneye Dayalı Çerçeve (OOP)


Yapay Zeka Programlama (AI)'de bir Sinir Ağı, beynimizdeki nöronların davranışlarını taklit etmeye çalışan metodolojisidir. Sinir ağları, model tanıma konusunda gerçekten parlıyor. Görüntü ve karakter tanıma programlarında, veri filtreleme uygulamalarında ve hatta robotiklerde kullanılmaktadır. Bir yapay zeka, insan sürücüleri gözlemlenmesi sonucunda öğrendiği öğrenim sonrasında ABD genelinde otomatik ve kusursuz bir şekilde araç kullanmak için kullanılmaya başlanmıştır. 


Bu yazıda, Yapay Zeka Programlama (AI) Sinir Ağlarının C # programlama dilinde temel bir çerçeve oluşturacağız ve programımıza temel XOR işlemlerini gerçekleştirmesini öğretiyoruz.

Yapay Zeka Programlama (AI) Bölüm I: Genel Bakış
Temel olarak beyindeki her bir nöron, birçok başka nörondan gelen girdileri kabul eder ve sonra sonuç veren bir sonuç sağlar. Tam da kodla çoğaltacağımız şey budur. Her bir nöron sınıfı, şekillerin bir cismi ve bir çıktı olduğu diyagram1'e benzer bir yapıya sahip olacaktır.

Diyagram1

Her bir nöron birden fazla girdiye sahip olabilir ve nöronlar diyagram2'deki gibi gruplandırılacaktır.

Diyagram2

Nöronlar katmanlar halinde gruplandırılacaktır. Bir sinyali işlerken (buna "darbe" diyeceğiz) sinyal, üst katmandan akarak ve bu katmandaki her bir nöron tarafından değiştirilerek başlatılacaktır.

Diyagram3

Her nöron nabzın kuvvetini değiştirecektir. Değişiklik tamamlandıktan sonra, "darbe" bir sonraki katmana gidecek ve yeniden modifiye edilecektir.

Diyagram4

Ayrıntılara sahip olduğunuza göre, bir adım geri çekilin ve çok sayıda hücrenin "sinir ağı"nı veya bir nöron ağı oluşturduğunu görün. Bir sinir ağı çalışması için, en azından üç grup nörona ihtiyacımız var.

Diyagram5

En üst katman, çevreyi algılamak için sinir ağı tarafından kullanılır ve genellikle "algılama" veya "girdi" katmanı olarak adlandırılır. Buradan, nabız ile net üzerinden iletilecek başlangıç değerlerini ayarlayacağız.

Diyagram6

Daha sonra, sinir ağı nabzımızın nihai çıktısını açığa çıkaracak Diagram7'de görüldüğü gibi. Bu nöronların sinyallerini herhangi bir yere göndermediğine dikkat edin. Darbe bu katmana geçtikten sonra, ağımızın işlemesinin nihai çıktısı olarak çıkış nöronlarındaki değerleri alacağız.

Diyagram7

Son olarak, orta tabaka(lar)daki tüm nöronlar darbeyi net üzerinden işlerken, netin doğrudan girişi veya çıkışı olarak görülmez. Buna genellikle "gizli" katman denir.

Diyagram8

Yapay Zeka Programlama (AI) Bölüm II: Geriye yayılım yoluyla öğrenme.

Şimdi Yapay Zeka Programlama (AI)'nde sinir ağı için şebekeyi öğrenmeye başlıyoruz.

Sinir ağımızın öğrenme yeteneğine sahip olması için sinyalimiz ağımızın tepesinden altına geçtikten sonra, her bir nöronun, ağı yönlendiren sonraki nabzı nasıl etkileyeceğini güncellemeliyiz. Bu, geri yayılım denilen bir işlemle yapılır. Temel olarak, ağımızın ürettiği hata seviyesini gösteren bir rakam buluyoruz. Bu, beklenen net çıktı ile gerçek çıktıyı karşılaştırarak ulaşılır.

Diyelim ki, çıktı katmanındaki hücrelerden birinde hata var.

Diyagram9

Her bir nöron, nabzı gönderen nöronları takip edecek ve bu ana nöronların her birinin çıktısının önemini ayarlayarak, hata hücresinin nihai çıktısına katkıda bulunacaktır.

Diyagram10

Daha sonra, bu nöronların her biri hesaplanan bir hata değerine sahip olacak ve ayarlama "geri sinyal gönderimi" yapacaktır, bu da aynı işlemi sonraki nöron tabakasına (gizli katmana nabız gönderenlere) uygulayacağımız anlamına geliyor.

Diyagram11

Kavramsal olarak, sinir ağı böyle çalışır.  Sinir ağları ile ilgili derin şeylerin bir zamanlar, bu tekrarlayıcı süreci öğrendikten ve tamamen eğitildikten sonra, daha önce hiç karşılaşmadıkları girdi çıktılarını, desen tanıma ve oyun oynamada AI için ideal kılan hesaplamaları hesaplayabilirler. Artık, bir sinir ağı temsil etmek için bazı gerçek C # ara yüzlerine bakmaya başlayacağız.

Yapay Zeka Programlama (AI) Bölüm III. Arayüzler

Yapay Zeka Programlama (AI)'de önce temel ara yüzleri oluşturup bunları uygulayacağız. Ölçeklenebilir kod geliştirirken, ara birimlerimiz projenin en önemli parçası olabilir. Çünkü uygulamanın nasıl gerçekleşeceğini ve nihai olarak projemizin başarısını belirlerler.

Öncelikle, ağımızdaki sinirler boyunca sinyaller tanımlamak için bir ara yüze ihtiyacımız var.


public interface INeuronSignal
{
    double Output { get; set; }

}


Ve birçok nöronun çıktısından oluşan bir nöronun girdisini tanımlamak için bir ara yüze ihtiyacımız var. Bunun için, anahtarın bir sinyal olduğu ve çıktısının o sinyalin "ağırlığını" tanımlayan bir sınıf olduğu genel bir sözlük kullanacağız.

public interface INeuronReceptor
{
    Dictionary<INeuronSignal, NeuralFactor> Input { get; }

}

INeuronReceptor'daki her INeuronSignal'ın ağırlığını temsil etmek için bir double kullanmış olabiliriz. Ancak şebekemizin daha verimli öğrenmesine yardımcı olacak bir dizi geri yayılımdan sonra toplu güncelleme adlı bir teknik uyguluyor olacağız. Sonuç olarak, yalnızca sinyal ağırlığını değil, aynı zamanda güncellenirken uygulayacağımız ayarlamanın miktarını da saklamaya yönelik bir sınıfa ihtiyacımız var. NeuralFactor classımız, bir nöron girişinin ağırlığını ve değişimin izleyecektir. Bu bir ara yüz olmasa da, temel AI Yapay Sinir Ağ çerçevesinin bir parçasıdır. Bu yüzden burayı buraya dahil ediyorum.


public class NeuralFactor
{
    #region Constructors

    public NeuralFactor(double weight)
    {
        m_weight = weight;
        m_delta = 0;
    }

    #endregion

    #region Member Variables

    private double m_weight;
    private double m_delta;

    #endregion

    #region Properties

    public double Weight
    {
        get { return m_weight; }
        set { m_weight = value; }
    }

    public double Delta
    {
        get { return m_delta; }
        set { m_delta = value; }
    }

    #endregion

    #region Methods

    public void ApplyDelta()
    {
        m_weight += m_delta;
        m_delta = 0;
    }

    #endregion

}


Şimdi, gerçek nöron için bir ara yüz tanımlayalım. Her nöron bir reseptör ve bir sinyaldir. Bu nedenle nöron ara yüzümüzü uygulayan herhangi bir nesne hem INeuronSignal hem de INeuronReceptor ara yüzlerini uygulayacaktır. Buna ek olarak, her bir nöron bir ön yargıya sahip olacaktır (başka bir girdi olarak düşünün, fakat kendiliğinden olan ve başka bir nörondan değil). Bu ön yargı, nöronumuzun her girişi ağırlığa sahip olduğu gibi ağırlık kazanacaktır. Ayrıca, bir nabzın işlenmesine ve nöron öğrenmenin uygulanmasına yönelik yöntemler bulacağız.

public interface INeuron : INeuronSignal, INeuronReceptor
{
    void Pulse(INeuralLayer layer);
    void ApplyLearning(INeuralLayer layer);

    NeuralFactor Bias { get; set; }
    double BiasWeight { get; set; }
    double Error { get; set; }
}

Sonra, Yapay Zeka Programlama (AI) sinir ağımızda nöron tabakası için bir ara yüz tanımlayacağız. Temel olarak, bu nabzı geçmek veya katmandaki her nörona öğrenme komutları uygulamak için kullanılacaktır.


public interface INeuralLayer : IList<INeuron>
{
    void Pulse(INeuralNet net);
    void ApplyLearning(INeuralNet net);

}

Ve nihai ara yüzümüz Yapay Zeka Programlama (AI)'nin sinir ağını kendisini tanımlamak için kullanılacaktır. Bu ara yüz için üç katmanımızı takip etmemiz ve aynı zamanda tüm sinir ağı için öğrenme yapabilmemiz ya da daraltabilmemiz gerekir. (komutu her katmana geçirilir ve bu komut katmandaki her nörona iletilir.)


Yapay Zeka Programlama (AI) Bölüm IV. Uygulama Classları
Yapay Zeka Programlama (AI) öğreniminde hala benimle misiniz? Umarım öylesinizdir. Şimdi eğlenmeye başlayabiliriz. Yapay Zeka Programlama (AI) sinir ağını uygulamak.

1) Nöron


Nöron, arabirimlerin uygulanmasında kullanılan üye değişkenlerine sahiptir. Vurgulanacak iki ilginç şey Sigmoid () statik fonksiyonu ve Pulse () metodudur. Sigmoid () fonksiyonu, sinirin çıktısını 0 ile 1 arasındaki değerlere basmak için bir Sigmoid eğrisi kullanır.


private static double Sigmoid(double value)
{
      return 1 / (1 + Math.Exp(-value));
}

Pulse () yöntemi, her girdinin değerini (veya her nöronun çıktısını bu nörona ileten çıktısının) toplamını, sözlükte bulunan ilgili ağırlık ile çarpımını alır. Ardından önyargı, ön çarpma ağırlığı ile çarpılır. Nihai çıktı daha önce tartışılan sigmoid eğrisi tarafından ezilir ve sonuç m_output değişkeninde saklanır.
Not: Bu, tüm ağımızın, 0 <x <1 veya alternatif olarak x: (0,1) (x, 0 ile 1 arasında) değerleriyle çalışır anlamına gelir.

public void Pulse(INeuralLayer layer)
{
    lock (this)
    {
        m_output = 0;

        foreach (KeyValuePair<INeuronSignal, NeuralFactor> item in m_input)
            m_output += item.Key.Output * item.Value.Weight;

        m_output += m_bias.Weight * BiasWeight;

        m_output = Sigmoid(m_output);
    }
}

2) Sinirsel Katman


NeuralLayer sınıfı temelde bir Pulse () veya ApplyLearning () komutunu üye nöronlarına geçirmekten sorumlu bir nöron topluluğudur. Bu, bir List <INeuron> kaydırarak ve IList <INeuron> yöntemlerini ve özelliklerini geçirerek uygulanır. Gerçekten endişe edeceğimiz tek uygulama aşağıdaki iki yöntemdir:
public void Pulse(INeuralNet net)
{
    foreach (INeuron n in m_neurons)
        n.Pulse(this);
}

public void ApplyLearning(INeuralNet net)
{
    foreach (INeuron n in m_neurons)
        n.ApplyLearning(this);
}

3) Sinir Ağı
Son olarak, fakat kesinlikle az değil; Sinir Ağı.


NeuralNet classında dikkat edilmesi gereken en ilginç şeyler m_learningRate üye değişkeni, Train () yöntemleri, BackPropogation () yöntemi ve Initialize () yöntemidir.
Başlamak için, NeuralNet'imizin başlatılmasına göz atalım. Nöronların Nöral Faktörleri'ni oluşturmak için rasgele sayı üreteci için bir çekirdek bilmemiz lazım. Ayrıca girdi nöronları, gizli nöronlar ve çıktı nöronlarının sayısını bilmeliyiz. Initialize (), faktör metodumuzdur ve sinir ağının tüm bileşenlerini oluşturmak ve bunları bağlamakla sorumludur.


public void Initialize(int randomSeed,
    int inputNeuronCount, int hiddenNeuronCount, int outputNeuronCount)
{
    int i, j, k, layerCount;
    Random rand;
    INeuralLayer layer;

    // initializations
    rand = new Random(randomSeed);
    m_inputLayer = new NeuralLayer();
    m_outputLayer = new NeuralLayer();
    m_hiddenLayer = new NeuralLayer();

    for (i = 0; i < inputNeuronCount; i++)
        m_inputLayer.Add(new Neuron());

    for (i = 0; i < outputNeuronCount; i++)
        m_outputLayer.Add(new Neuron());

    for (i = 0; i < hiddenNeuronCount; i++)
        m_hiddenLayer.Add(new Neuron());

    // wire-up input layer to hidden layer
    for (i = 0; i < m_hiddenLayer.Count; i++)
        for (j = 0; j < m_inputLayer.Count; j++)
            m_hiddenLayer[i].Input.Add(m_inputLayer[j],
                 new NeuralFactor( rand.NextDouble()));

    // wire-up output layer to hidden layer
    for (i = 0; i < m_outputLayer.Count; i++)
        for (j = 0; j < m_hiddenLayer.Count; j++)
            m_outputLayer[i].Input.Add(HiddenLayer[j],
                 new NeuralFactor(rand.NextDouble()));

}

Sonra, Pulse () ve ApplyLearning () 'e bakalım, böylece komutları sadece her katmana aktararak onları nöronlara geçirebilirsiniz.


public void Pulse()
{
    lock (this)
    {
        m_hiddenLayer.Pulse(this);
        m_outputLayer.Pulse(this);
    }
}

public void ApplyLearning()
{
    lock (this)
    {
        m_hiddenLayer.ApplyLearning(this);
        m_outputLayer.ApplyLearning(this);
    }
}

Tamam, şimdi ağın etine, BackPropogation () yöntemine giriyoruz. İşlemlerin büyük kısmının gerçekleştiği yer burasıdır. Önce, beklediğimiz şey arasındaki farkı hesaplayarak çıktı nöronlarındaki hataları hesaplarız. (Bir parametre olarak aktarılır) ve nöronun fiili çıktısını hesaplarız. Tüm çıkış nöronları güncellendikten sonra gizli katman nöronlarındaki hataları aynı şekilde hesaplarız. Sonunda, her bir nöron girişinin ayarlanmış ağırlığını ve önyargı m_learningRate parametresiyle çarpımını güncelleriz.

private void BackPropogation(double[] desiredResults)
{
    int i, j;
    double temp, error;

    INeuron outputNode, inputNode, hiddenNode, node, node2;

    // Calcualte output error values
    for (i = 0; i < m_outputLayer.Count; i++)
    {
        temp = m_outputLayer[i].Output;
        m_outputLayer[i].Error = (desiredResults[i] - temp) * temp * (1.0F - temp);
    }

    // calculate hidden layer error values
    for (i = 0; i < m_hiddenLayer.Count; i++)
    {
        node = m_hiddenLayer[i];

        error = 0;

        for (j = 0; j < m_outputLayer.Count; j++)
        {
            outputNode = m_outputLayer[j];
            error += outputNode.Error * outputNode.Input[node].Weight * node.Output * (1.0 - node.Output);
        }

        node.Error = error;
    }

    // adjust output layer weight change
    for (i = 0; i < m_hiddenLayer.Count; i++)
    {
        node = m_hiddenLayer[i];

        for (j = 0; j < m_outputLayer.Count; j++)
        {
            outputNode = m_outputLayer[j];
            outputNode.Input[node].Weight += m_learningRate * m_outputLayer[j].Error * node.Output;
            outputNode.Bias.Delta += m_learningRate * m_outputLayer[j].Error * outputNode.Bias.Weight;
        }
    }

    // adjust hidden layer weight change
    for (i = 0; i < m_inputLayer.Count; i++)
    {
        inputNode = m_inputLayer[i];

        for (j = 0; j < m_hiddenLayer.Count; j++)
        {
            hiddenNode = m_hiddenLayer[j];
            hiddenNode.Input[inputNode].Weight += m_learningRate * hiddenNode.Error * inputNode.Output;
            hiddenNode.Bias.Delta += m_learningRate * hiddenNode.Error * inputNode.Bias.Weight;
        }
    }

}

BackPropogation'dan sonra, sinir ağımızda gerçek ve beklenen çıktıları karşılaştırarak öğrenilen dersleri uygulamaya hazırız. Bunu toplu halde yapmak, ağın farklı girişlerle oluşan hataları telafi etmemesine yardımcı olur. Hazır olduğumuzda, sadece ApplyLearning () 'i çağırırız ve sinir ağımız güncellenecektir.

Train () yöntemleri, sadece bir girdi uygular, net vuruş yapar ve öğrenmek için gerekli geri yayılımı gerçekleştirir.


public void Train(double[] input, double[] desiredResult)
{
    int i;

    if (input.Length != m_inputLayer.Count)
        throw new ArgumentException(string.Format("Expecting {0} inputs for this net", m_inputLayer.Count));

    // initialize data
    for (i = 0; i < m_inputLayer.Count; i++)
    {
        Neuron n = m_inputLayer[i] as Neuron;

        if (null != n) // maybe make interface get;set;
            n.Output = input[i];
    }

    Pulse();
    BackPropogation(desiredResult);
}

public void Train(double[][] inputs, double[][] expected)
{
    for (int i = 0; i < inputs.Length; i++)
        Train(inputs[i], expected[i]);

}

Yapay Zeka Programlama (AI) Sinir ağı öğrenimi için dört basit adımımız şu şekildedir:

1. Adım: Giriş verilerini algılama katmanına ayarlayın
2. Adım: Pulse  ()
3. Adım: BackPropogate ()
4. Adım: ApplyLearning ()

Yapay Zeka Programlama (AI) Bölüm V: Gerçekten Bir Şeyler Yapmak – XOR
Yapay Zeka Programlama (AI)'de İki bit üzerinde XOR işlemi gerçekleştirmek için sinir ağı yetiştirmek istiyoruz. İki girdi nöronu, iki gizli nöron ve bir çıktı nöronuyla sinir ağı kuracağız. Aşağıdaki işlemi gerçekleştirmek için ağı eğitmek istiyoruz.


Sorun şu ki bulanık Boolean sayılarıyla uğraşıyoruz. Bütün sinir ağımız, 0 ile 1 arasında değerlere sahip double veri türü ile çalışır ve netten net değerler almalıyız.

Bir düğme oluşturacağız. Yapay Zeka Programlama (AI) sinir ağımızı eğitmek, onu başlatmak ve her öğrenme uygulaması için 100 eğitim seansının tekrarlaması gerekmektedir. Netimizi hızlandırmak için ne kadar eğitimden geçmesine ihtiyaç duyulduğunu görmek ilginç olacaktır. Bu nedenle gerekli yineleme sayısını sayacağız.


private void button1_Click(object sender, EventArgs e)
{
    net = new NeuralNet();
    double high, mid, low;

    high = .9;
    low = .1;
    mid = .5;

    // initialize with
    //   2 perception neurons
    //   2 hidden layer neurons
    //   1 output neuron
    net.Initialize(1, 2, 2, 1);
  
    double[][] input = new double[4][];
    input[0] = new double[] {high, high};
    input[1] = new double[] {low, high};
    input[2] = new double[] {high, low};
    input[3] = new double[] {low, low};

    double[][] output = new double[4][];
    output[0] = new double[] { low };
    output[1] = new double[] { high };
    output[2] = new double[] { high };
    output[3] = new double[] { low };

    double ll, lh, hl, hh;
    int count;

    count = 0;

    do
    {
        count++;

        for (int i = 0; i < 100; i++)
            net.Train(input, output);

        net.ApplyLearning();

        net.PerceptionLayer[0].Output = low;
        net.PerceptionLayer[1].Output = low;

        net.Pulse();

        ll = net.OutputLayer[0].Output;

        net.PerceptionLayer[0].Output = high;
        net.PerceptionLayer[1].Output = low;

        net.Pulse();

        hl = net.OutputLayer[0].Output;

        net.PerceptionLayer[0].Output = low;
        net.PerceptionLayer[1].Output = high;

        net.Pulse();

        lh = net.OutputLayer[0].Output;

        net.PerceptionLayer[0].Output = high;
        net.PerceptionLayer[1].Output = high;

        net.Pulse();

        hh = net.OutputLayer[0].Output;
    }
    while (hh > mid || lh < mid || hl < mid || ll > mid);

    MessageBox.Show((count*100).ToString() + " iterations required for training");

}


Yapay Zeka Programlama (AI) eğitimi artık tamamlandı, net'i xor işlemlerimizi gerçekleştirmek için bir araç olarak kullanabiliriz.

Yapay Zeka Programlama (AI) Bölüm V: Sonuç
Son bir hatırlatma, başka bir uygulama (Oyun oynamada genellikle AI kullanılır.) Her biri ilişkili bir eylemle birden fazla çıkış nöronuna sahip olmaktan oluşur. Çevrenin gözlemlenmesinden ve şebekenin atılmasından sonra, en yüksek çıktı değerine sahip olan düğüm "kazanan" olarak belirlenir ve ilgili eylem yapılır. Buna "kazanan herkesi al" yaklaşımı denir.

Daha önceki Blockchain, Bitcoin ve Ethereum'un Piyasalarda Hızlanmasının Nedeni... yazımız için tıklayın.

Hiç yorum yok:

Yorum Gönder