自組織映射(SOM),或者大家可能據說過的Kohonen映射,是自組織神經網絡的基本類型之一。自組織的能力提供了對之前不可見的輸入數據的適應性。它被理論化爲最天然的學習方式之一,就像咱們的大腦所使用的學習方式同樣,在咱們的大腦中,沒有預先定義的模式被認爲是存在的。這些模式是在學習過程當中造成的,而且在以更低的維度(如二維或一維)表示多維數據方面具備難以想象的天賦。此外,該網絡以這樣一種方式存儲信息,即在訓練集中保持任何拓撲關係。html
更正式地說,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中的組織模式與咱們的目標達到了相同的集羣,對咱們來講這意味着成功:
那麼這一切到底是如何工做的呢。像往常同樣,讓咱們來看看咱們的主執行循環。正如咱們所看到的,咱們將使用與前面討論的相同的DistanceNetwork和SOMLearning對象:
/// <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); }
正如咱們前面提到的,LearningRate和LearningRadius在每次迭代中都在不斷髮展。這一次,讓咱們來談談訓練器的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和彈性神經網絡的力量。如今咱們已經正式從機器學習跨入了神經網絡的階段。