dotnet 从零开始写一个人工智能 网络模型
本文將不使用任何人工智能框架,只用簡單的 dotnet 的類,自己搭建一個人工智能網絡。本文適合小伙伴跟著一步步寫
特別感謝老馬的程序人生的幫助,本文有大量代碼都是從如何利用 C# 抽象神經網絡模型抄的
在人工智能模型有不同的問題可以選用不同的模型,本文主要寫一個 BP 網絡用于做分類,也就是寫出一個簡單的多分類人工智能和一個模擬二進制 與 計算和 或 計算。請不要認為本文會告訴大家如何寫一個會和你聊QQ的人工智能,這里的人工智能其實也就是一個工具,和想象的智能差距有點大。本文的人工智能只能做對數值輸入進行分類或實現模擬二進制計算
在一個人工智能模型,可以將人工智能模型作為網絡模型建立,一個人工智能模型是一個網絡模型,一個網絡里面會有很多層,每一層有很多元。本文將從小到大進行定義,先從元定義,然后再從層定義,最后定義網絡
在人工智能模型里面,最小單元都是神經元。一個神經元可以收到多個輸入,而只有一個輸出。在代碼里面,將輸入和輸出的值都定義為double值。而本文寫的神經元是固定輸入數量,也就是在神經元對象創建的時候需要告訴這個神經元可以收到多少個數量的輸入
在神經元里面將會對每個輸入添加一個權值,在神經網絡的每個元可以收到多個輸入,而對每個輸入需要使用不同的權值計算
最簡單的神經元就是將每個輸入的值乘以一個權值然后加起來然后輸出。當然稍微復雜一點的是加起來之后需要加一個閾值然后調用激活函數計算輸出
先忽略元的計算,定義元的數據結構
上面的模型感謝老馬的程序人生大佬提供,只是定義了基礎數據結構,而計算方法作為抽象方法。在 Compute 方法接受多個輸入,然后有一個 double 輸出
在 Randomize 方法給了權值數組一些隨機值,其實有一句話是人工智能和隨機猜是差不多的。本文下面將寫一個隨機給權值的訓練方法
上面的 DoubleRange 是自己定義的,用來創建范圍內的隨機數,只需要看代碼就知道是如何做的
接下來定義一個 ActivationNeuron 繼承 Neuron 這里定義了閾值和激活函數
計算的方法是將多個輸入的每個輸入乘以權值加起來,然后加上閾值,接下來放入激活函數計算輸出。為什么需要激活函數?原因是計算的 sum 的值需要輸出到下一層需要將 sum 的值處理,如本文需要讓每一層的輸入的值都是?1?和?0而因為每一層的輸出會作為下一層的輸入,所以需要將 sum 值計算為?1?和?0?也就是通過 閾值函數 將一個值按照是否大于等于 0 分為?1?和?0?兩個值
那么 閾值函數 的定義是什么,閾值就是臨界值,函數的目的是大于這個臨界值會怎么樣,小于這個臨界值會怎么樣。本文只是讓大于等于 0 輸出為?1?否則輸出 0 這個值
激活函數的定義是
和 閾值函數 相應的激活函數還有符號函數等,符號函數就是按照一定的范圍,將輸入轉換為?1?或?-1?因為負數叫符號,這也就是符號函數的名字。詳細定義請看?符號函數
也就是在接受多個輸入,對多個輸入乘以權值加起來,再加上閾值,放入激活函數,計算后輸出。這就是最簡單的元的定義
定義完成了元,接下來就是定義層的概念,每一層可以有多個元,每一層可以收到上一層的數據。而第一層叫輸入層,輸入層將會接受用戶的輸入。最后一層叫輸出層,輸出層的值將會作為輸出。簡單的網絡只需要一個元,一個元作為一層,而這個網絡也只有一層。這一層只有一個元,是輸入層也是輸出層。這是最簡單的模型,也就是本文接下來告訴大家的模型。也就是元是核心,只有一個元也能做出人工智能
定義的層需要知道輸入的數量,而層里每個元都會被定義相同數量的輸入。此時的連接和全連接差不多,也就是每個元都接受到相同的輸入。其實這樣的定義對于某個元只需要特定的幾個輸入也是可以實現的,因為每個元會對每個輸入一個權值,如果設置某個輸入的權值為 0 那么相當于放棄這個輸入。這樣就可以做到某個元只接受特定的幾個輸入而不是收到所有的輸入。而為什么一些高級的模型不會讓同一層的所有元收到的輸入相同?剛才不是說可以讓元自己控制放棄哪些輸入,原因是雖然可以讓元自己控制放棄一些輸入,但是這樣做的效率比較低,高級的模型需要提升效率,本文這里無視所以可以使用每一層的所有元收到相同的輸入
本文這里使用的模型是每一層的元數量不可變,在定義層時就知道這一層有多少元。這樣的模型功能會比較差,但是作為入門的博客,這樣的定義差不多可以使用了
每一個元只有一個輸入,所以每一層的輸出數量和每一層的元數量相同
接下來定義 ActivationLayer 作為實際的神經網絡層,其實從代碼可以看到連個類可以合并在一起,只是老馬的程序人生大佬作為兩篇博客,我這里也就跟著他寫了
一個網絡可以包含多個層
請看一下代碼里面的注釋
實現一個網絡
上面代碼的實現有些詭異,原因是我的參數沒有寫好。在 ActivationNetwork 的最后一個參數是一個數組,指定神經網絡每層中的神經元數量。也就是輸入?[1,2]?表示有兩層,其中第一層有 1 個神經元,第二層有兩個。輸入?[1,1,5]?表示有三層
而根據人工智能的教程,第一層是輸入層,也就是?i == 0?設置這一層的輸入為用戶輸入數量。從第二層開始,每一層的輸入數量為上一層的輸出數量
定義完成了人工智能模型,一個模型不會自動運行,還需要定義一個訓練方法。作為可以自己學習的人工智能,學習方法可以分為監督學習和無監督學習,在代碼里面我用老馬的程序人生的說法非監督學習。這兩個方法的不同在于監督學習是我知道輸入內容和結果,我將輸入放入模型,對比模型輸出的值和我知道的結果,按照模型輸出的值和我知道的結果的誤差反饋給模型,讓模型修改參數,如修改權值參數。而無監督學習是我也不知道結果,這個比較難理解,詳細請看監督學習和無監督學習?或?小白都看得懂的監督學習與無監督學習?本文用到的是監督學習
這里的輸入分為多樣本訓練和單樣本訓練也就是我給一堆數據就是多樣本訓練。從方法參數可以看到輸入的都是二維數組,當然這里說二維數組是不對的,應該是數組的數組。從單樣本訓練方法可以看到每個數據都是輸入是一個 double 數組,而輸出也是一個 double 數組,那么多個輸入和多個輸出就是數組的數組
剛才也有說到,人工智能和隨機猜是一樣的,在人工智能的訓練很重要的是反饋,也就是我告訴人工智能說算錯了,他應該如何修改參數?這部分看起來有點難,假設這個人工智能我告訴他算錯了,他就隨機修改他的參數,這就是本文的 Slow 訓練方法,這個方法只是慢,和其他訓練方法差不多
用隨機修改參數方法要求模型很簡單,本文要求的模型只是一層,也就是輸入層和輸出層是相同的一層。在 單個訓練樣本 方法將會使用模型計算出一個值,通過?double[] networkOutput = _network.Compute(input);?然后對比誤差?double e = output[j] - networkOutput[j];?如果存在誤差,那么用?perceptron.Weights[i] = RandRange.GetRan();?更新每個元的每個參數的值
現在大概寫完了代碼,本文的代碼放在?github?下載用 VisualStudio 打開 Bp.sln 文件,然后按下 F5 就可以運行
嘗試用這個模型和訓練方法做出一個模擬二進制 與 的計算,也就是輸入有兩個,輸出是件這兩個輸入進行 與 運算
這個人工智能網絡使用輸入層有兩個,只有一層網絡,一層網絡里面只有一個神經元
創建訓練方法
嘗試運行代碼,可以看到我沒有告訴人工智能如何做 與 運算,但是人工智能模擬了方法
嘗試訓練人工智能模型模擬二進制或計算
我沒有告訴人工智能或計算的方法,但是人工智能可以訓練如何計算
這樣的太簡單了,其實上面的模型可以做出多分類,多分類就是將一些輸入分為幾類。如按照二維幾何距離將數據分為幾類,然后讓人工智能分類
這個數據是如何利用 C# 實現神經網絡的感知器模型用到的數據
上面的數據的輸入是兩個數,而輸出是三個數。在輸出用 0 和 1 表示屬于哪個類型
本文定義一層網絡,在這一層網絡的輸出需要三個數也就是需要三個元
這就是一個簡單的人工智能模型,所有代碼都沒有用到現有人工智能框架,都是使用 dotnet 基礎的代碼。用 C# 實現人工智能模型最成熟的是 ML.NET 但是這個庫沒有基礎很難知道是做什么
本文的代碼放在github?歡迎小伙伴訪問
其實人工智能的一個核心是訓練算法,本文告訴大家的是 Slow 算法,這個算法就是在人工智能模型輸出的值和我知道的值不同時,讓模型隨機更新參數。這個做法雖然能完成,但是效率很低,特別在元的數量多的時候。此時就需要用到比較高級的訓練方法,如如何利用 C# 實現神經網絡的感知器模型
特別感謝老馬的程序人生提供的模型
總結
以上是生活随笔為你收集整理的dotnet 从零开始写一个人工智能 网络模型的全部內容,希望文章能夠幫你解決所遇到的問題。
                            
                        - 上一篇: 远程办公从学习开始,潜伏在家,技术如何逆
 - 下一篇: SkyWalking学习笔记(CentO