基於C#的機器學習--顏色混合-自組織映射和彈性神經網絡

自組織映射和彈性神經網絡

       自組織映射(SOM),或者大家可能據說過的Kohonen映射,是自組織神經網絡的基本類型之一。自組織的能力提供了對之前不可見的輸入數據的適應性。它被理論化爲最天然的學習方式之一,就像咱們的大腦所使用的學習方式同樣,在咱們的大腦中,沒有預先定義的模式被認爲是存在的。這些模式是在學習過程當中造成的,而且在以更低的維度(如二維或一維)表示多維數據方面具備難以想象的天賦。此外,該網絡以這樣一種方式存儲信息,即在訓練集中保持任何拓撲關係。html

       更正式地說,SOM是一種集羣技術,它將幫助咱們發現大型數據集中有趣的數據類別。它是一種無監督的神經網絡,神經元被排列在一個單一的二維網格中。網格必須是矩形的,例如,純矩形或六邊形。在整個迭代過程當中,網格中的神經元將逐漸合併到數據點密度更高的區域。當神經元移動時,它們會彎曲和扭曲網格,直到它們更靠近感興趣的點,並反映出數據的形狀。算法

       在這一章中,咱們將討論如下主題:數組

  1. Kohonen SOM(自組織映射)
  2. 使用AForge.NET

SOM引擎

       簡而言之,咱們網格上的神經元,經過迭代,它們逐漸適應數據的形狀(在咱們的示例中,以下面的圖所示,位於點面板左側)。咱們再來討論一下迭代過程自己。網絡

       1.第一步是在網格上隨機放置數據。咱們將隨機將網格的神經元放在數據空間中,以下圖所示:dom

       2.第二步是算法將選擇單個數據點。機器學習

       3.在第三步中,咱們須要找到最接近所選數據點的神經元(數據點)。這就成了咱們最匹配的單元。工具

       4.第四步是將最匹配的單元移向該數據點。咱們移動的距離是由咱們的學習率決定的,每次迭代後學習率都會降低。學習

       5.第五,咱們將把最匹配單元的鄰居移得更近,距離越遠的神經元移動得越少。你在屏幕上看到的初始半徑變量是咱們用來識別鄰居的。這個值,就像初始學習率同樣,會隨着時間的推移而下降。若是您已經啓動並運行了監視器,您能夠看到初始學習率隨着時間的推移而下降,以下面的屏幕截圖所示:優化

       6.咱們的第六步也是最後一步,是更新初始學習速率和初始半徑,就像咱們目前描述的那樣,而後重複它。咱們將繼續這一進程,直到咱們的數據點穩定下來並處於正確的位置。this

       如今咱們已經有了了一些關於SOMs的直觀的認識,咱們再來討論一下咱們這一章要作的事情。咱們選擇了一個很是常見的機制來教咱們的程序,那就是顏色的映射。

       顏色自己是由紅色、綠色和藍色表示的三維對象,可是咱們將把它們組織成二維。顏色的組織有兩個關鍵點。首先,顏色被彙集成不一樣的區域,其次,具備類似屬性的區域一般彼此相鄰。

       第二個例子更高級一些,它將使用ANN(人工神經網絡);這是機器學習的一種高級形式,用於建立與呈現給它的映射相匹配的組織映射。

       讓咱們看第一個例子。下面是咱們示例的屏幕截圖。正如所看到的,咱們有一個隨機的顏色圖案,當完成時,它由一組類似的顏色組成:

       若是咱們成功了,咱們的結果應該是這樣的:

       讓咱們從如下步驟開始:

       1.咱們將首先進行500次迭代來實現咱們的目標。使用較小的數字可能不會產生咱們最終想要的混合。例如,若是咱們進行500次迭代,下面是咱們的結果:

       2.正如所看到的,咱們離咱們須要達到的目標還很遠。可是咱們可以更改迭代,以容許使用徹底正確的設置進行試驗。到了這裏咱們必須認可,500次的迭代遠遠不夠,因此我把它留做練習,讓你本身算出進展到哪裏中止,纔會讓你感到滿意(因此這個迭代次數是因人而異的,但能夠確定的是500次,確定是不符合絕大多數人的要求的)

       3.在設置了迭代次數以後,咱們所要作的就是確保程序擁有咱們想要的隨機顏色模式,這能夠經過單擊隨機按鈕來實現。有了想要的模式後,只需單擊Start按鈕並查看結果。

       4.一旦你點擊開始,中止按鈕將被激活,你能夠隨時中止進程。一旦達到指定的迭代次數,程序將自動中止。

       在咱們進入實際代碼以前,讓咱們看一些組織模式的屏幕截圖。咱們能夠經過簡單地更改不一樣的參數來實現出色的結果,咱們將在後面詳細描述這些參數。

       在下面的截圖中,咱們將迭代次數設置爲3000次,初始半徑爲10

       在下面的截圖中,咱們將迭代次數設置爲4000次,初始半徑爲18:

       在下面的截圖中,咱們將迭代次數設置爲4000次,初始半徑爲5:

       這裏咱們將迭代次數設爲5000次,初始學習率設爲0.3,初始半徑設爲25,以下截圖所示,獲得了指望的結果:

       如今讓咱們深刻研究代碼。

       在本例中,咱們將使用AForge並使用DistanceNetwork對象。距離網絡是隻有一個距離的神經網絡。除了用於SOM以外,它還用於一個彈性網絡操做,這就是咱們將要使用的來展現進程中對象之間的彈性鏈接。

       咱們將使用三個輸入神經元和10000個神經元來建立咱們的距離網絡:

    // 建立網絡
    network = new DistanceNetwork(3, 100 * 100);

       當咱們點擊隨機化顏色按鈕來隨機化顏色時:  

        /// <summary>
        /// 賦予網絡隨機權重
        /// </summary>
        private void RandomizeNetwork()
        {
            if (network != null)
            {
                foreach (var neuron in (network?.Layers.SelectMany(layer => layer?.Neurons)).Where(neuron => neuron != null))
                    neuron.RandGenerator = new UniformContinuousDistribution(new Range(0, 255));

                network?.Randomize();
            }

            UpdateMap();
        }

       這裏須要注意的是,咱們處理的隨機化範圍保持在任何顏色的紅、綠色或藍色特徵的範圍內,即0-255之間(包含0,包含255)。

       接下來,咱們來看看咱們的學習循環,就像這樣。咱們一下子會深刻研究它:

        /// <summary>
        /// 工做內容
        /// </summary>
        private void SearchSolution()
        {
            SOMLearning trainer = new SOMLearning(network);
            double[] input = new double[3];
            double fixedLearningRate = learningRate / 10;
            double driftingLearningRate = fixedLearningRate * 9;
            int i = 0;

            while (!needToStop)
            {
                trainer.LearningRate = driftingLearningRate * (iterations - i) / iterations + fixedLearningRate;
                trainer.LearningRadius = radius * (iterations - i) / iterations;

                if (rand != null)
                {
                    input[0] = rand.Next(256);
                    input[1] = rand.Next(256);
                    input[2] = rand.Next(256);
                }

                trainer.Run(input);

                // 每50次迭代更新一次map
                if ((i % 10) == 9)
                {
                    UpdateMap();
                }

                i++;

                SetText(currentIterationBox, i.ToString());

                if (i >= iterations)
                    break;
            }

            EnableControls(true);
        }

       若是仔細觀察,會發現咱們建立的第一個對象是SOMLearning對象。這個對象是爲正方形空間學習而優化的,這意味着它指望它所處理的網絡具備與其寬度相同的高度。這使得計算網絡神經元數量的平方根變得更加容易:

    SOMLearning trainer = new SOMLearning(network); 

       接下來,咱們須要建立變量來保存紅色、綠色和藍色的輸入顏色,從中咱們將不斷地隨機化輸入顏色,以實現咱們的目標:

       if (rand != null)
       {
           input[0] = rand.Next(256);
           input[1] = rand.Next(256);
           input[2] = rand.Next(256);
       }

       一旦咱們進入while循環,咱們將不斷地更新變量,直到咱們達到所選擇的迭代總數。在這個更新循環中,會發生一些事情。首先,咱們將更新學習速率和學習半徑,並將其存儲在咱們的SOMLearning對象:

    trainer.LearningRate = driftingLearningRate * (iterations - i) / iterations + fixedLearningRate;
 
    trainer.LearningRadius = radius * (iterations - i) / iterations;

 

       學習率決定了咱們的學習速度。學習半徑會對視覺輸出產生很是顯著的影響,它決定了相對於獲勝神經元多少距離,須要更新的神經元數量。指定半徑的圓由神經元組成,神經元在學習過程當中不斷更新。一個神經元離獲勝神經元越近,它接收到的更新信息就越多。請注意,若是在實驗中,將該值設置爲零,那麼只能更新獲勝神經元的權重,而不能更新其餘神經元的權重。

       如今咱們對這個迭代進行一次訓練,並將Input數組傳遞給它:

    trainer.Run(input); 

       讓咱們談一下學習。正如咱們所提到的,每次迭代都將嘗試和學習愈來愈多的信息。這個學習迭代返回一個學習誤差,即神經元的權重和輸入向量Input的差值。如前所述,距離是根據與獲勝神經元之間的距離來測量的。過程以下。

       訓練器運行一次學習迭代,找到獲勝的神經元(權重值與Input中提供的值最接近的神經元),並更新其權重。它還會更新相鄰神經元的權重。隨着每次學習迭代的進行,網絡愈來愈接近最優解。

       接下來是運行應用程序的屏幕截圖。在實時調試和診斷工具中,能夠看到咱們是如何記錄每次迭代的,更新地圖時使用的顏色值,以及學習速率和學習半徑。

       因爲SOM是自組織的,咱們的第二個例子會更加形象。它能幫助咱們更好地理解幕後所發生的事情。

       在本例中,咱們將再次使用AForge。並構建一個二維平面的對象,組織成幾個組。咱們將從一個單獨的位置開始,直觀地獲得這些形狀的位置。這與咱們的顏色示例在概念上是相同的,它使用了三維空間中的點,只是此次,咱們的點是二維的。可視化在map面板中,這是一個自頂向下的視圖,用於查看二維空間中發生的事情,從而得到一個一維圖形視圖。

       起初,SOM網格中的神經元從隨機位置開始,但它們逐漸被揉捏成咱們數據形狀的輪廓。這是一個迭代的過程,我已經在迭代過程的不一樣地方截屏,向你們展現了發生了什麼。固然你也能夠本身運行這個示例來實時查看它。

       咱們將運行500次迭代來展現演進。咱們將從一個空白的白色面板獲得一個相似的點的面板:

       在咱們點擊開始按鈕,咱們將看到這些點開始經過移動到正確的位置來組織本身,但願這將反映出咱們所指定的點:

       通過262次迭代:

       通過343次迭代:

       在完成以後,咱們能夠看到對象已經像咱們最初建立的模式那樣組織了它們本身。藍色的點是活躍的神經元,淺灰色的點是不活躍的神經元,畫的線是神經元之間的彈性鏈接。嗯,這有些像《黑客帝國》中的那個綠色的數據流。若是你看到足夠仔細,是可以看到3D效果的。

       若是你窗體中沒有顯示鏈接和不活躍的神經元,你會看到map中的組織模式與咱們的目標達到了相同的集羣,對咱們來講這意味着成功:

       那麼這一切到底是如何工做的呢。像往常同樣,讓咱們來看看咱們的主執行循環。正如咱們所看到的,咱們將使用與前面討論的相同的DistanceNetworkSOMLearning對象:

        /// <summary>
        /// 工做內容
        /// </summary>
        private void SearchSolution()
        {
            // 建立網絡
            DistanceNetwork network = new DistanceNetwork(2, networkSize * networkSize);

            // 設置隨機生成器範圍
            foreach (var neuron in network.Layers.SelectMany(layer => layer.Neurons))
                neuron.RandGenerator = new UniformContinuousDistribution(
                    new Range(0, Math.Max(pointsPanel.ClientRectangle.Width, pointsPanel.ClientRectangle.Height)));

            // 建立學習算法
            SOMLearning trainer = new SOMLearning(network, networkSize, networkSize);

            // 建立map
            map = new int[networkSize, networkSize, 3];

            double fixedLearningRate = learningRate / 10;
            double driftingLearningRate = fixedLearningRate * 9;

            // 迭代次數
            int i = 0;
            
            while (!needToStop)
            {
                trainer.LearningRate = driftingLearningRate * (iterations - i) / iterations + fixedLearningRate;
                trainer.LearningRadius = (double)learningRadius * (iterations - i) / iterations;

                // 開始紀元的訓練
                trainer.RunEpoch(trainingSet);
                
                UpdateMap(network);
                
                i++;

                // 設置當前迭代的信息
                SetText(currentIterationBox, i.ToString());

                // stop ?
                if (i >= iterations)
                    break;
            }

            // 啓用設置控件
            EnableControls(true);
        } 

 

       正如咱們前面提到的,LearningRateLearningRadius在每次迭代中都在不斷髮展。這一次,讓咱們來談談訓練器的RunEpoch方法。這個方法雖然很是簡單,但其設計目的是獲取一個輸入值向量,而後爲該迭代返回一個學習誤差(正如咱們如今看到的,有時也稱爲epoch)。它經過計算向量中的每一個輸入樣原本實現這一點。學習偏差是神經元的權值和輸入之間的絕對差。這種差別是根據獲勝神經元之間的距離來測量的。如前所述,咱們針對一個學習迭代/曆元運行此計算,找到獲勝者,並更新其權重(以及鄰居權重)。應該指出的是,當說贏家時,指的是權重值與指定輸入向量最接近的神經元,也就是給網絡輸入的最小距離。

       接下來,將重點介紹如何更新映射自己;咱們計算的項目應該與初始輸入向量()匹配:

        private void UpdateMap(DistanceNetwork network)
        {
            // 獲得第一層
            Layer layer = network.Layers[0];

            // 加鎖
            Monitor.Enter(this);

            // 遍歷全部神經元
            for (int i = 0; i < layer.Neurons.Length; i++)
            {
                Neuron neuron = layer.Neurons[i];

                int x = i % networkSize;
                int y = i / networkSize;

                map[y, x, 0] = (int)neuron.Weights[0];
                map[y, x, 1] = (int)neuron.Weights[1];
                map[y, x, 2] = 0;
            }

            // 收集活動的神經元
            for (int i = 0; i < pointsCount; i++)
            {
                network.Compute(trainingSet[i]);
                int w = network.GetWinner();

                map[w / networkSize, w % networkSize, 2] = 1;
            }

            // 解鎖
            Monitor.Exit(this);
            
            mapPanel.Invalidate();
        }

 

       從這段代碼中能夠看到,咱們獲得了第一層,計算了全部神經元的map,收集了活動神經元,這樣咱們就能夠肯定獲勝者,而後更新map

       咱們肯定獲勝者的過程,實際上就是尋找權重與網絡輸入距離最小的神經元,就是這麼簡單,必定不要把簡單的問題複雜化,就好像那個雨滴與碩士博士教授的笑話同樣。

總結

       在這一章中,咱們學習瞭如何利用SOMs和彈性神經網絡的力量。如今咱們已經正式從機器學習跨入了神經網絡的階段。

相關文章
相關標籤/搜索