按照固有思維方式,人們總以為人工智能是一個莫測高深的行業,這個行業的人都是高智商人群,無論是寫文章還是和人講話,總是諱莫如深,接著就是蹦出一些“高級”詞匯,什么“神經網路”,什么“卷積神經”之類,教人半懂不懂的,尤其ChatGPT的風靡一時,更加“神話”了這個行業,用魯迅先生形容諸葛武侯的話來講:“多智而近妖”,
事實上,根據二八定理,和別的行業一樣,人工智能行業內真正頂尖的天才也就是20%,他們具備真正的行業顛覆能力,可以搞出像ChatGPT這種“工業革命”級別的產品,而剩下的80%也不過就是普通人,每天的作業和我們這些人一樣,枯燥且乏味,而之所以會出現類似“行業壁壘”的現象,是因為這個行業的“黑話”太多了,導致一般人聽不懂,所謂“黑話”可以理解為行業術語,搞清楚了這些所謂的行業術語,你會發現,所謂的“人工智能”,不過也就是個“套路活兒”,
本次我們試圖使用“白話文”來描摹人工智能機器學習的底層邏輯,并且通過Golang1.18來實作人造神經元的原理,
人造神經元 Neural
現在業內比較流行的,比如Transformer模型、GPT模型、深度學習、強化學習、卷積神經網路等等,無論聽起來多么高端大氣上檔次,說出大天去,它也是神經網路架構,換句話說它們的底層都一樣,類比的話,就像汽車行業,無論是汽油驅動還是電驅動、三缸發動機還是六缸發動機、單電機還是雙電機,混合動力還是插混動力,無論汽車主機廠怎么吹牛逼,無論車評人怎么瘋狂恰飯,廠商造出來的車最侄訓是一個最基本的汽車架構,說白了就是一個底盤四個輪兒,你再牛逼,你也逃不出這個基本架構,
所以說,機器學習的基本架構是神經網路架構,神經網路由大量的人工神經元組成,它們聯結之后就可以進行所謂的“機器學習”,所以,必須搞清楚什么是神經元,才能弄懂神經網路,
神經元是神經網路架構中最微小的單位,也是最小的可訓練單位,一個基本的神經元結構是下面這樣的:

毋庸諱言,大多數人看見這個基本上都會瞬間放棄,數學公式簡直就是開發界的洪水猛獸,其勸退能力堪比艾爾登法環中的大樹守衛,我們的機器學習之旅還沒開始,就已經結束了,

那么我們把這個玩意兒翻譯成大多數人能看懂的樣子:f( sum( x*w) + b )
x代表輸入的資料,w代表權重,sum代表求和,b代表偏差,f代表激活函式,最后這個公式運行的結果,就是機器學習的結果,
簡單往里頭套點資料,比如我希望機器學習的結果是10,那么,x、w、和b分別應該是什么才能讓結果變為10呢?如果 x=2 w=4 b = 2 就是我們想要的結果,這也就是最基本的線性回歸,我們只處理一個維度的資料,因為結果已經顯而易見了,我們已經不需要機器學習了,因為靠猜也能猜出來結果是什么,
但是生產環境中,x并非是單維度,而是多維度的,比如x1、x2、x3.....組成的矩陣,但無論是多維度還是單維度,計算公式用的還是一樣的,每一個x對應一個權重w,所以是xn*wn,
說白了,x就是一個多維特征,類比的話,假如我們想讓電腦智能識圖,比如識別一只貓,那么x就是貓的特征,比如形態、顏色、眼睛、叫聲等等,作為多維度的輸入特征x,喂給電腦,讓電腦給出識別結果,這就是簡單的機器學習處理分類問題,
這里需要注意的是,x作為特征引數,并不是越多越好,而是特征越明顯越好,舉個例子,你想讓AI去識別魯迅的文章,那提供的特征最好應該具備魯迅文章的特點,而不是全量輸入,因為魯迅就算再“魯迅”,他寫的文字也會和別人重復,也就是說并不是每句話都是他獨有的,如果把他所有的文章都喂給電腦,可能就會產生“噪聲”,影響機器學習的結果,
另外應該知道的是,x引數特征并不是我們認為的單詞或者漢字,而是一串單精度區間在0-1之間的浮點數字,也就是所謂的“向量”,因為只有數字才能套著神經元公式進行計算,
所以所有的文本特征在進行神經元計算之前,必須通過一些方法進行“向量化”操作,說白了就是把漢字轉化為數字,就這么簡單,
另外這也就證明了,電腦真的沒有思想,它不理解什么是貓,或者誰是魯迅,它就是在進行計算,而已,
隨后是w,w指的是權重,權重是指神經元接收到的輸入值的重要性,這些輸入值通過乘以對應的權重,被加權求和作為神經元的輸入,權重值越大,表示該輸入在神經元的輸出中所占的比重越大,說白了,貓的所有特征的權重并不是統一的,比如黑夜里突然一個東西跳了出來,你怎么判斷它是什么物種?很明顯,一聲“喵嗚”我們就可以立刻斷定這是一只貓,所以叫聲特征的權重一定大于其他特征的權重,
最后是b,也就是偏差(bias),偏差通常是一個實數,與神經元的權重一樣,也是通過訓練神經網路而調整的引數,偏差的作用是在神經元的輸入上增加一個常量,以調整神經元的激活閾值,如果沒有偏差,那么神經元的激活函式將僅僅取決于加權和的值,而無法產生任何偏移,
說白了,b就是讓x * w的值更活一點,讓它不是“死”的數,
最后說說f 也就是激活函式,激活函式通常具有非線性的特性,這使得神經網路能夠擬合非線性的復雜函式,從而提高其性能和準確度,
說白了,如果沒有激活函式,我們的權重計算就是“線性”的,什么叫線性?如果把x特征從1開始輸入,一直到100,然后將計算結果繪制成圖:

我們會發現計算結果是一根直線,這顯然不符合客觀規律,更符合生物特征的形態應該是“曲線”,所以說白了,激活函式f的作用就是把“直線”變成“曲線”,
最后,我們把神經元公式改造成方便我們理解的形式:期望結果 = 激活函式( 求和(特征 * 權重) + 偏差 ),
如果用代碼實作這個公式:
func neuron(inputs []float64, weights []float64, bias float64) float64 {
if len(inputs) != len(weights) {
panic("inputs and weights must have the same length")
}
sum := bias
for i := 0; i < len(inputs); i++ {
sum += inputs[i] * weights[i]
}
return sum
}
這個函式接收兩個長度相同的浮點數陣列 inputs 和 weights,以及一個偏置值 bias,它通過將每個輸入值乘以其對應的權重,加上偏置值,得到神經元的加權和,最后,函式回傳這個加權和作為神經元的輸出值,
使用這個函式時,可以將輸入資料和權重作為引數傳遞給它,例如,假設我們有一個二分類問題,輸入資料有兩個特征:x1和x2,并且我們有一個包含兩個權重和一個偏置的神經元,那么可以這樣呼叫神經元公式:
inputs := []float64{x1, x2}
weights := []float64{w1, w2}
bias := b
output := neuron(inputs, weights, bias)
這里回傳神經元的輸出值,所以,誰說學習人工智能必須得用Python?我們就驕傲地使用Golang,
機器學習,到底怎么學習
機器學習的程序就是上文中神經元公式的使用程序,第一步收集所有的特征資料,然后進行權重分配,最后向量化操作,把文本資料轉換為計算機能計算的浮點數,隨后加權求和,之后加一個偏差(bias),最后過一下激活函式,最終得到一個期望結果,就完事了,
難嗎?不難,普通人連猜帶蒙也能做,
但事實上,這只是原理,也就是最基本的東西,一般人都能掌握,比如打籃球,規則非常簡單,核心就是運球和投籃,無對抗下小學生也能瞬間掌握,但小學生沒法去打NBA級別的比賽,因為很多高端的籃球技術是構筑于運球和投籃的,需要經年累月的練習和自身天賦的加成,所以全世界能打NBA的就那么幾百人,而已,
同理,機器學習的程序也并非如此簡單,通過特征輸入,經過神經元公式,得到的結果真的一定是我們所期望的結果嗎?
其實未必,機器學習還包括兩個極其重要的概念:前向傳播和反向傳播,
前向傳播是指將輸入資料從神經網路的輸入層傳遞到輸出層的程序,在前向傳播程序中,輸入資料通過神經網路的每一層,每個神經元都會對其進行一定的加權和激活函式計算,最終得到輸出層的輸出值,這個程序也被稱為“正向傳播”,因為資料是從輸入層依次向前傳播到輸出層,
反向傳播是指在前向傳播之后,計算神經網路誤差并將誤差反向傳播到各層神經元中進行引數(包括權重和偏置)的更新,在反向傳播程序中,首先需要計算網路的誤差,然后通過鏈式法則將誤差反向傳播到各層神經元,以更新每個神經元的權重和偏置,這個程序也被稱為“反向梯度下降”,因為它是通過梯度下降演算法來更新神經網路引數的,
說白了,前向傳播就是由特征到結果的程序,反向傳播則是逆運算,用結果反推程序,
回到分類問題,我們輸入了貓的特征和特征權重,經過計算,結果未必是貓,可能是狗,或者是耗子,也可能是別的什么東西,但這不重要,重要的是我們需要拿到一個結果的誤差,這個誤差越小越好,而反向傳播就是幫我們推算誤差到底有多大的方法,
而誤差的大小就取決于特征的輸入,導致機器學習結果錯誤的根源是引數,此時,我們需要調整引數的輸入,從而減小誤差值,這也就是人工智能行業從業人員經常說的“調參”,
比如,我們期望結果是貓,結果計算機回傳狗,那么調整引數,結果回傳熊貓,那么就說明調大發了,繼續調整,直到計算機回傳結果:貓,
在 Golang1.18 中,可以通過以下代碼實作反向傳播:
func backpropagation(inputs []float64, targets []float64, network *Network, learningRate float64) {
// 1. 前向傳播,計算每個神經元的輸出值
outputs := feedforward(inputs, network)
// 2. 計算輸出層的誤差
outputErrors := make([]float64, len(outputs))
for i := range outputs {
outputErrors[i] = outputs[i] - targets[i]
}
// 3. 反向傳播誤差,計算每個神經元的誤差值
for i := len(network.layers) - 1; i >= 0; i-- {
layer := network.layers[i]
errors := make([]float64, len(layer.neurons))
// 3.1. 計算神經元的誤差值
if i == len(network.layers)-1 {
// 輸出層的誤差
for j := range layer.neurons {
errors[j] = outputErrors[j] * sigmoidPrime(layer.neurons[j].output)
}
} else {
// 隱藏層的誤差
for j := range layer.neurons {
errorSum := 0.0
nextLayer := network.layers[i+1]
for k := range nextLayer.neurons {
errorSum += nextLayer.neurons[k].weights[j] * nextLayer.neurons[k].error
}
errors[j] = errorSum * sigmoidPrime(layer.neurons[j].output)
}
}
// 3.2. 將誤差值保存到神經元中
for j := range layer.neurons {
layer.neurons[j].error = errors[j]
}
}
// 4. 更新神經網路的權重和偏置
for i := range network.layers {
layer := network.layers[i]
// 4.1. 更新權重
for j := range layer.neurons {
for k := range layer.neurons[j].weights {
if i == 0 {
// 輸入層的權重
layer.neurons[j].weights[k] -= learningRate * layer.neurons[j].error * inputs[k]
} else {
// 隱藏層和輸出層的權重
prevLayer := network.layers[i-1]
layer.neurons[j].weights[k] -= learningRate * layer.neurons[j].error * prevLayer.neurons[k].output
}
}
// 4.2. 更新偏置
layer.neurons[j].bias -= learningRate * layer.neurons[j].error
}
}
}
這個函式接收輸入資料 inputs、目標資料 targets、神經網路 network 以及學習率 learningRate 作為引數,它首先呼叫 feedforward 函式進行前向傳播,計算每個神經元的輸出值,然后,它計算輸出層的誤差,通過誤差反向傳播,計算每個神經元的誤差值,并將其保存到神經元中,
接下來,函式根據誤差值和學習率更新神經網路的權重和偏置,在更新權重時,需要根據神經元所在的層來選擇更新的權重型別(輸入層、隱藏層或輸出層),然后根據誤差值和輸入資料或上一層神經元的輸出值來更新權重,在更新偏置時,只需要根據誤差值和學習率來更新即可,
總的來說,這個函式實作了反向傳播的所有步驟,可以用于訓練神經網路并提高其準確度和性能,
結語
大道不過三倆句,說破不值半文錢,所謂人工智能機器學習就這么回事,沒必要神話,也無須貶低,類比的話,就像餐飲行業的廚師崗,所謂做菜,底層原理是什么?就是食材和火候,掌握了做菜的底層原理,就能做出好菜,其他的,比如刀工、顏色等等,不過就是錦上添花的東西,而已,
所以機器學習就是做菜,做出來的東西可能不盡如人意,就得不停地調整食材的搭配和火候的大小,所謂機器學習的最重要技巧,其實是特征的提取以及引數的調整,所謂大道至簡,殊途同歸,
轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/548243.html
標籤:其他
上一篇:做個清醒的程式員之成為少數派
