人工智能神經網路( Artificial Neural Network,又稱為ANN)是一種由人工神經元組成的網路結構,神經網路結構是所有機器學習的基本結構,換句話說,無論是深度學習還是強化學習都是基于神經網路結構進行構建,關于人工神經元,請參見:人工智能機器學習底層原理剖析,人造神經元,您一定能看懂,通俗解釋把AI“黑話”轉化為“白話文”,
機器學習可以解決什么問題
機器學習可以幫助我們解決兩大類問題:回歸問題和分類問題,它們的主要區別在于輸出變數的型別和預測目標的不同,
在回歸問題中,輸出變數是連續值,預測目標是預測一個數值,例如,預測房價、預測銷售額等都是回歸問題,通常使用回歸模型,如線性回歸、決策樹回歸、神經網路回歸等來解決這類問題,回歸問題的評估指標通常是均方誤差(Mean Squared Error,MSE)、平均絕對誤差(Mean Absolute Error,MAE)等,
在分類問題中,輸出變數是離散值,預測目標是將樣本劃分到不同的類別中,例如,預測郵件是否是垃圾郵件、預測影像中的物體類別等都是分類問題,通常使用分類模型,如邏輯回歸、決策樹分類、支持向量機、神經網路分類等來解決這類問題,分類問題的評估指標通常是準確率、精度(Precision)、召回率(Recall)等,
事實上,機器學習只能解決“可以”被解決的問題,也就是說,機器學習能幫我們做的是提高解決問題的效率,而不是解決我們本來解決不了的問題,說白了,機器學習只能解決人目前能解決的問題,比如說人現在不能做什么?人不能永生,不能白日飛升,也不能治愈絕癥,所以你指望機器學習解決此類問題,就是癡心妄想,
同時,機器學習輸入的特征引數和輸出的預期結果必須有邏輯相關性,什么意思?比如說我們想預測房價,結果特征引數輸入了很多沒有任何邏輯相關性的資料,比如歷年水稻的出產率,這就是沒有邏輯相關性的資料,這樣的問題再怎么調參也是無法通過機器學習來解決的,
此外,回歸問題中有一個領域非常引人關注,那就是預測股票價格,國內經常有人說自己訓練的模型可以預測某支A股的價格走勢,甚至可以精準到具體價格單位,說實話,挺滑稽的,關鍵是還真有人相信靠機器學習能在A股市場大殺特殺,
因為,稍微有點投資經驗的人都知道,股票的歷史資料和未來某個時間點或者某個時間段的實際價格,并不存在因果關系,尤其像A股市場這種可被操控的黑盒環境,連具體特征都是隱藏的,或者說特征是什么都是未知的,你以為的特征只是你以為的,并不是市場或者政策以為的,所以你輸入之前十年或者二十年的歷史股票資料,你讓它預測,就是在搞笑,機器學習沒法幫你解決此類問題,
為什么現在GPT模型現在這么火?是因為它在NLP(自然語言分析)領域有了質的突破,可以通過大資料模型聯系背景關系關系生成可信度高的回答,而這個背景關系關系,就是我們所謂的引數和預期結果的因果關系,
鳶尾花分類問題
鳶尾花分類問題是一個經典的機器學習問題,也是神經網路入門的常用案例之一,它的目標是通過鳶尾花的花萼長度、花萼寬度、花瓣長度和花瓣寬度這四個特征來預測鳶尾花的品種,分為三種:山鳶尾(Iris Setosa)、變色鳶尾(Iris Versicolour)和維吉尼亞鳶尾(Iris Virginica),

通俗來講,就是我們要訓練一個神經網路模型,它能夠根據鳶尾花的四個特征,自動地對鳶尾花的品種進行分類,
在這個案例中,我們使用了一個包含一個隱藏層的神經網路,它的輸入層有4個神經元,代表鳶尾花的4個特征;隱藏層有3個神經元;輸出層有3個神經元,分別代表3種鳶尾花的品種:

由此可見,神經網路通常由三層組成:輸入層、隱藏層和輸出層,
輸入層:輸入層接收外部輸入信號,是神經網路的起點,它的神經元數量與輸入特征的數量相同,每個神經元代表一個輸入特征,輸入層的主要作用是將外部輸入轉換為神經網路內部的信號,
隱藏層:隱藏層位于輸入層和輸出層之間,是神經網路的核心部分,它的神經元數量可以根據問題的復雜度自由設定,每個神經元接收上一層神經元輸出的信號,并進行加權處理和激活函式處理,再將結果傳遞給下一層神經元,隱藏層的主要作用是對輸入信號進行復雜的非線性轉換,提取出輸入信號中的特征,從而使得神經網路能夠對復雜的問題進行處理,
輸出層:輸出層是神經網路的終點,它的神經元數量通常與問題的輸出數量相同,每個神經元代表一個輸出結果,輸出層的主要作用是將隱藏層處理后的信號進行進一步處理,并將最終的結果輸出,
在神經網路中,輸入信號從輸入層開始,通過隱藏層的處理,最終到達輸出層,每一層的神經元都與下一層的神經元相連,它們之間的連接可以看成是一種權重關系,權重值代表了兩個神經元之間的相關性強度,當神經網路接收到輸入信號后,每個神經元都會對這些信號進行加權處理,并通過激活函式將結果輸出給下一層神經元,最終形成輸出結果,通過不斷調整權重和激活函式,神經網路可以學習到輸入和輸出之間的復雜非線性關系,從而對未知資料進行預測和分類等任務,
定義神經網路結構體
在開始訓練之前,我們先定義一些需要的結構體和函式:
// neuralNet contains all of the information
// that defines a trained neural network.
type neuralNet struct {
config neuralNetConfig
wHidden *mat.Dense
bHidden *mat.Dense
wOut *mat.Dense
bOut *mat.Dense
}
// neuralNetConfig defines our neural network
// architecture and learning parameters.
type neuralNetConfig struct {
inputNeurons int
outputNeurons int
hiddenNeurons int
numEpochs int
learningRate float64
}
這里neuralNet是神經網路結構體,同時定義輸入、隱藏和輸出層神經元的配置,
隨后宣告函式初始化神經網路:
func newNetwork(config neuralNetConfig) *neuralNet {
return &neuralNet{config: config}
}
這里回傳神經網路的指標,
除此之外,我們還需要定義激活函式及其導數,這是在反向傳播程序中需要使用的,激活函式有很多選擇,但在這里我們將使用sigmoid函式,這個函式有很多優點,包括概率解釋和方便的導數運算式:
// sigmoid implements the sigmoid function
// for use in activation functions.
func sigmoid(x float64) float64 {
return 1.0 / (1.0 + math.Exp(-x))
}
// sigmoidPrime implements the derivative
// of the sigmoid function for backpropagation.
func sigmoidPrime(x float64) float64 {
return sigmoid(x) * (1.0 - sigmoid(x))
}
實作反向傳播
反向傳播是指在前向傳播之后,計算神經網路誤差并將誤差反向傳播到各層神經元中進行引數(包括權重和偏置)的更新,在反向傳播程序中,首先需要計算網路的誤差,然后通過鏈式法則將誤差反向傳播到各層神經元,以更新每個神經元的權重和偏置,這個程序也被稱為“反向梯度下降”,因為它是通過梯度下降演算法來更新神經網路引數的,
說白了,反向傳播就是逆運算,用結果反推程序,這里我們可以撰寫一個實作反向傳播方法的方法,用于訓練或優化我們網路的權重和偏置,反向傳播方法包括以下步驟:
1 初始化權重和偏置(例如,隨機初始化),
2 將訓練資料輸入神經網路中進行前饋,以生成輸出,
3 將輸出與正確輸出進行比較,以獲取誤差,
4 基于誤差計算權重和偏置的變化,
5 將變化通過神經網路進行反向傳播,
對于給定的迭代次數或滿足停止條件時,重復步驟2-5,
在步驟3-5中,我們將利用隨機梯度下降(SGD)來確定權重和偏置的更新:
// train trains a neural network using backpropagation.
func (nn *neuralNet) train(x, y *mat.Dense) error {
// Initialize biases/weights.
randSource := rand.NewSource(time.Now().UnixNano())
randGen := rand.New(randSource)
wHidden := mat.NewDense(nn.config.inputNeurons, nn.config.hiddenNeurons, nil)
bHidden := mat.NewDense(1, nn.config.hiddenNeurons, nil)
wOut := mat.NewDense(nn.config.hiddenNeurons, nn.config.outputNeurons, nil)
bOut := mat.NewDense(1, nn.config.outputNeurons, nil)
wHiddenRaw := wHidden.RawMatrix().Data
bHiddenRaw := bHidden.RawMatrix().Data
wOutRaw := wOut.RawMatrix().Data
bOutRaw := bOut.RawMatrix().Data
for _, param := range [][]float64{
wHiddenRaw,
bHiddenRaw,
wOutRaw,
bOutRaw,
} {
for i := range param {
param[i] = randGen.Float64()
}
}
// Define the output of the neural network.
output := new(mat.Dense)
// Use backpropagation to adjust the weights and biases.
if err := nn.backpropagate(x, y, wHidden, bHidden, wOut, bOut, output); err != nil {
return err
}
// Define our trained neural network.
nn.wHidden = wHidden
nn.bHidden = bHidden
nn.wOut = wOut
nn.bOut = bOut
return nil
}
接著實作具體的反向傳播邏輯:
// backpropagate completes the backpropagation method.
func (nn *neuralNet) backpropagate(x, y, wHidden, bHidden, wOut, bOut, output *mat.Dense) error {
// Loop over the number of epochs utilizing
// backpropagation to train our model.
for i := 0; i < nn.config.numEpochs; i++ {
// Complete the feed forward process.
hiddenLayerInput := new(mat.Dense)
hiddenLayerInput.Mul(x, wHidden)
addBHidden := func(_, col int, v float64) float64 { return v + bHidden.At(0, col) }
hiddenLayerInput.Apply(addBHidden, hiddenLayerInput)
hiddenLayerActivations := new(mat.Dense)
applySigmoid := func(_, _ int, v float64) float64 { return sigmoid(v) }
hiddenLayerActivations.Apply(applySigmoid, hiddenLayerInput)
outputLayerInput := new(mat.Dense)
outputLayerInput.Mul(hiddenLayerActivations, wOut)
addBOut := func(_, col int, v float64) float64 { return v + bOut.At(0, col) }
outputLayerInput.Apply(addBOut, outputLayerInput)
output.Apply(applySigmoid, outputLayerInput)
// Complete the backpropagation.
networkError := new(mat.Dense)
networkError.Sub(y, output)
slopeOutputLayer := new(mat.Dense)
applySigmoidPrime := func(_, _ int, v float64) float64 { return sigmoidPrime(v) }
slopeOutputLayer.Apply(applySigmoidPrime, output)
slopeHiddenLayer := new(mat.Dense)
slopeHiddenLayer.Apply(applySigmoidPrime, hiddenLayerActivations)
dOutput := new(mat.Dense)
dOutput.MulElem(networkError, slopeOutputLayer)
errorAtHiddenLayer := new(mat.Dense)
errorAtHiddenLayer.Mul(dOutput, wOut.T())
dHiddenLayer := new(mat.Dense)
dHiddenLayer.MulElem(errorAtHiddenLayer, slopeHiddenLayer)
// Adjust the parameters.
wOutAdj := new(mat.Dense)
wOutAdj.Mul(hiddenLayerActivations.T(), dOutput)
wOutAdj.Scale(nn.config.learningRate, wOutAdj)
wOut.Add(wOut, wOutAdj)
bOutAdj, err := sumAlongAxis(0, dOutput)
if err != nil {
return err
}
bOutAdj.Scale(nn.config.learningRate, bOutAdj)
bOut.Add(bOut, bOutAdj)
wHiddenAdj := new(mat.Dense)
wHiddenAdj.Mul(x.T(), dHiddenLayer)
wHiddenAdj.Scale(nn.config.learningRate, wHiddenAdj)
wHidden.Add(wHidden, wHiddenAdj)
bHiddenAdj, err := sumAlongAxis(0, dHiddenLayer)
if err != nil {
return err
}
bHiddenAdj.Scale(nn.config.learningRate, bHiddenAdj)
bHidden.Add(bHidden, bHiddenAdj)
}
return nil
}
接著宣告一個工具函式,它幫助我們沿一個矩陣維度求和,同時保持另一個維度不變:
// sumAlongAxis sums a matrix along a particular dimension,
// preserving the other dimension.
func sumAlongAxis(axis int, m *mat.Dense) (*mat.Dense, error) {
numRows, numCols := m.Dims()
var output *mat.Dense
switch axis {
case 0:
data := make([]float64, numCols)
for i := 0; i < numCols; i++ {
col := mat.Col(nil, i, m)
data[i] = floats.Sum(col)
}
output = mat.NewDense(1, numCols, data)
case 1:
data := make([]float64, numRows)
for i := 0; i < numRows; i++ {
row := mat.Row(nil, i, m)
data[i] = floats.Sum(row)
}
output = mat.NewDense(numRows, 1, data)
default:
return nil, errors.New("invalid axis, must be 0 or 1")
}
return output, nil
}
實作前向傳播進行預測
在訓練完我們的神經網路之后,我們希望使用它進行預測,為此,我們只需要將一些給定的鳶尾花特征值輸入到網路中進行前向傳播,用來生成輸出,
有點像反向傳播邏輯,不同之處在于,這里我們將回傳生成的輸出:
// predict makes a prediction based on a trained
// neural network.
func (nn *neuralNet) predict(x *mat.Dense) (*mat.Dense, error) {
// Check to make sure that our neuralNet value
// represents a trained model.
if nn.wHidden == nil || nn.wOut == nil {
return nil, errors.New("the supplied weights are empty")
}
if nn.bHidden == nil || nn.bOut == nil {
return nil, errors.New("the supplied biases are empty")
}
// Define the output of the neural network.
output := new(mat.Dense)
// Complete the feed forward process.
hiddenLayerInput := new(mat.Dense)
hiddenLayerInput.Mul(x, nn.wHidden)
addBHidden := func(_, col int, v float64) float64 { return v + nn.bHidden.At(0, col) }
hiddenLayerInput.Apply(addBHidden, hiddenLayerInput)
hiddenLayerActivations := new(mat.Dense)
applySigmoid := func(_, _ int, v float64) float64 { return sigmoid(v) }
hiddenLayerActivations.Apply(applySigmoid, hiddenLayerInput)
outputLayerInput := new(mat.Dense)
outputLayerInput.Mul(hiddenLayerActivations, nn.wOut)
addBOut := func(_, col int, v float64) float64 { return v + nn.bOut.At(0, col) }
outputLayerInput.Apply(addBOut, outputLayerInput)
output.Apply(applySigmoid, outputLayerInput)
return output, nil
}
準備特征和期望資料
下面我們需要準備鳶尾花的特征和期望資料,可以在加州大學官網下載:https://archive.ics.uci.edu/ml/datasets/iris

這里包含花瓣和花蕊的具體資料,以及這些樣本所對應的花的種類,分別對應上文提到的山鳶尾(Iris Setosa)、維吉尼亞鳶尾(Iris Virginica)和 變色鳶尾(Iris Versicolour),注意鳶尾花種類順序分先后,分別對應上表中的資料,
開始訓練
訓練之前,需要安裝基于Golang的浮點庫:
go get gonum.org/v1/gonum/floats
安裝后之后,撰寫腳本:
package main
import (
"encoding/csv"
"errors"
"fmt"
"log"
"math"
"math/rand"
"os"
"strconv"
"time"
"gonum.org/v1/gonum/floats"
"gonum.org/v1/gonum/mat"
)
// neuralNet contains all of the information
// that defines a trained neural network.
type neuralNet struct {
config neuralNetConfig
wHidden *mat.Dense
bHidden *mat.Dense
wOut *mat.Dense
bOut *mat.Dense
}
// neuralNetConfig defines our neural network
// architecture and learning parameters.
type neuralNetConfig struct {
inputNeurons int
outputNeurons int
hiddenNeurons int
numEpochs int
learningRate float64
}
func main() {
// Form the training matrices.
inputs, labels := makeInputsAndLabels("data/train.csv")
// Define our network architecture and learning parameters.
config := neuralNetConfig{
inputNeurons: 4,
outputNeurons: 3,
hiddenNeurons: 3,
numEpochs: 5000,
learningRate: 0.3,
}
// Train the neural network.
network := newNetwork(config)
if err := network.train(inputs, labels); err != nil {
log.Fatal(err)
}
// Form the testing matrices.
testInputs, testLabels := makeInputsAndLabels("data/test.csv")
// Make the predictions using the trained model.
predictions, err := network.predict(testInputs)
if err != nil {
log.Fatal(err)
}
// Calculate the accuracy of our model.
var truePosNeg int
numPreds, _ := predictions.Dims()
for i := 0; i < numPreds; i++ {
// Get the label.
labelRow := mat.Row(nil, i, testLabels)
var prediction int
for idx, label := range labelRow {
if label == 1.0 {
prediction = idx
break
}
}
// Accumulate the true positive/negative count.
if predictions.At(i, prediction) == floats.Max(mat.Row(nil, i, predictions)) {
truePosNeg++
}
}
// Calculate the accuracy (subset accuracy).
accuracy := float64(truePosNeg) / float64(numPreds)
// Output the Accuracy value to standard out.
fmt.Printf("\nAccuracy = %0.2f\n\n", accuracy)
}
// NewNetwork initializes a new neural network.
func newNetwork(config neuralNetConfig) *neuralNet {
return &neuralNet{config: config}
}
// train trains a neural network using backpropagation.
func (nn *neuralNet) train(x, y *mat.Dense) error {
// Initialize biases/weights.
randSource := rand.NewSource(time.Now().UnixNano())
randGen := rand.New(randSource)
wHidden := mat.NewDense(nn.config.inputNeurons, nn.config.hiddenNeurons, nil)
bHidden := mat.NewDense(1, nn.config.hiddenNeurons, nil)
wOut := mat.NewDense(nn.config.hiddenNeurons, nn.config.outputNeurons, nil)
bOut := mat.NewDense(1, nn.config.outputNeurons, nil)
wHiddenRaw := wHidden.RawMatrix().Data
bHiddenRaw := bHidden.RawMatrix().Data
wOutRaw := wOut.RawMatrix().Data
bOutRaw := bOut.RawMatrix().Data
for _, param := range [][]float64{
wHiddenRaw,
bHiddenRaw,
wOutRaw,
bOutRaw,
} {
for i := range param {
param[i] = randGen.Float64()
}
}
// Define the output of the neural network.
output := new(mat.Dense)
// Use backpropagation to adjust the weights and biases.
if err := nn.backpropagate(x, y, wHidden, bHidden, wOut, bOut, output); err != nil {
return err
}
// Define our trained neural network.
nn.wHidden = wHidden
nn.bHidden = bHidden
nn.wOut = wOut
nn.bOut = bOut
return nil
}
// backpropagate completes the backpropagation method.
func (nn *neuralNet) backpropagate(x, y, wHidden, bHidden, wOut, bOut, output *mat.Dense) error {
// Loop over the number of epochs utilizing
// backpropagation to train our model.
for i := 0; i < nn.config.numEpochs; i++ {
// Complete the feed forward process.
hiddenLayerInput := new(mat.Dense)
hiddenLayerInput.Mul(x, wHidden)
addBHidden := func(_, col int, v float64) float64 { return v + bHidden.At(0, col) }
hiddenLayerInput.Apply(addBHidden, hiddenLayerInput)
hiddenLayerActivations := new(mat.Dense)
applySigmoid := func(_, _ int, v float64) float64 { return sigmoid(v) }
hiddenLayerActivations.Apply(applySigmoid, hiddenLayerInput)
outputLayerInput := new(mat.Dense)
outputLayerInput.Mul(hiddenLayerActivations, wOut)
addBOut := func(_, col int, v float64) float64 { return v + bOut.At(0, col) }
outputLayerInput.Apply(addBOut, outputLayerInput)
output.Apply(applySigmoid, outputLayerInput)
// Complete the backpropagation.
networkError := new(mat.Dense)
networkError.Sub(y, output)
slopeOutputLayer := new(mat.Dense)
applySigmoidPrime := func(_, _ int, v float64) float64 { return sigmoidPrime(v) }
slopeOutputLayer.Apply(applySigmoidPrime, output)
slopeHiddenLayer := new(mat.Dense)
slopeHiddenLayer.Apply(applySigmoidPrime, hiddenLayerActivations)
dOutput := new(mat.Dense)
dOutput.MulElem(networkError, slopeOutputLayer)
errorAtHiddenLayer := new(mat.Dense)
errorAtHiddenLayer.Mul(dOutput, wOut.T())
dHiddenLayer := new(mat.Dense)
dHiddenLayer.MulElem(errorAtHiddenLayer, slopeHiddenLayer)
// Adjust the parameters.
wOutAdj := new(mat.Dense)
wOutAdj.Mul(hiddenLayerActivations.T(), dOutput)
wOutAdj.Scale(nn.config.learningRate, wOutAdj)
wOut.Add(wOut, wOutAdj)
bOutAdj, err := sumAlongAxis(0, dOutput)
if err != nil {
return err
}
bOutAdj.Scale(nn.config.learningRate, bOutAdj)
bOut.Add(bOut, bOutAdj)
wHiddenAdj := new(mat.Dense)
wHiddenAdj.Mul(x.T(), dHiddenLayer)
wHiddenAdj.Scale(nn.config.learningRate, wHiddenAdj)
wHidden.Add(wHidden, wHiddenAdj)
bHiddenAdj, err := sumAlongAxis(0, dHiddenLayer)
if err != nil {
return err
}
bHiddenAdj.Scale(nn.config.learningRate, bHiddenAdj)
bHidden.Add(bHidden, bHiddenAdj)
}
return nil
}
// predict makes a prediction based on a trained
// neural network.
func (nn *neuralNet) predict(x *mat.Dense) (*mat.Dense, error) {
// Check to make sure that our neuralNet value
// represents a trained model.
if nn.wHidden == nil || nn.wOut == nil {
return nil, errors.New("the supplied weights are empty")
}
if nn.bHidden == nil || nn.bOut == nil {
return nil, errors.New("the supplied biases are empty")
}
// Define the output of the neural network.
output := new(mat.Dense)
// Complete the feed forward process.
hiddenLayerInput := new(mat.Dense)
hiddenLayerInput.Mul(x, nn.wHidden)
addBHidden := func(_, col int, v float64) float64 { return v + nn.bHidden.At(0, col) }
hiddenLayerInput.Apply(addBHidden, hiddenLayerInput)
hiddenLayerActivations := new(mat.Dense)
applySigmoid := func(_, _ int, v float64) float64 { return sigmoid(v) }
hiddenLayerActivations.Apply(applySigmoid, hiddenLayerInput)
outputLayerInput := new(mat.Dense)
outputLayerInput.Mul(hiddenLayerActivations, nn.wOut)
addBOut := func(_, col int, v float64) float64 { return v + nn.bOut.At(0, col) }
outputLayerInput.Apply(addBOut, outputLayerInput)
output.Apply(applySigmoid, outputLayerInput)
return output, nil
}
// sigmoid implements the sigmoid function
// for use in activation functions.
func sigmoid(x float64) float64 {
return 1.0 / (1.0 + math.Exp(-x))
}
// sigmoidPrime implements the derivative
// of the sigmoid function for backpropagation.
func sigmoidPrime(x float64) float64 {
return sigmoid(x) * (1.0 - sigmoid(x))
}
// sumAlongAxis sums a matrix along a
// particular dimension, preserving the
// other dimension.
func sumAlongAxis(axis int, m *mat.Dense) (*mat.Dense, error) {
numRows, numCols := m.Dims()
var output *mat.Dense
switch axis {
case 0:
data := make([]float64, numCols)
for i := 0; i < numCols; i++ {
col := mat.Col(nil, i, m)
data[i] = floats.Sum(col)
}
output = mat.NewDense(1, numCols, data)
case 1:
data := make([]float64, numRows)
for i := 0; i < numRows; i++ {
row := mat.Row(nil, i, m)
data[i] = floats.Sum(row)
}
output = mat.NewDense(numRows, 1, data)
default:
return nil, errors.New("invalid axis, must be 0 or 1")
}
return output, nil
}
func makeInputsAndLabels(fileName string) (*mat.Dense, *mat.Dense) {
// Open the dataset file.
f, err := os.Open(fileName)
if err != nil {
log.Fatal(err)
}
defer f.Close()
// Create a new CSV reader reading from the opened file.
reader := csv.NewReader(f)
reader.FieldsPerRecord = 7
// Read in all of the CSV records
rawCSVData, err := reader.ReadAll()
if err != nil {
log.Fatal(err)
}
// inputsData and labelsData will hold all the
// float values that will eventually be
// used to form matrices.
inputsData := make([]float64, 4*len(rawCSVData))
labelsData := make([]float64, 3*len(rawCSVData))
// Will track the current index of matrix values.
var inputsIndex int
var labelsIndex int
// Sequentially move the rows into a slice of floats.
for idx, record := range rawCSVData {
// Skip the header row.
if idx == 0 {
continue
}
// Loop over the float columns.
for i, val := range record {
// Convert the value to a float.
parsedVal, err := strconv.ParseFloat(val, 64)
if err != nil {
log.Fatal(err)
}
// Add to the labelsData if relevant.
if i == 4 || i == 5 || i == 6 {
labelsData[labelsIndex] = parsedVal
labelsIndex++
continue
}
// Add the float value to the slice of floats.
inputsData[inputsIndex] = parsedVal
inputsIndex++
}
}
inputs := mat.NewDense(len(rawCSVData), 4, inputsData)
labels := mat.NewDense(len(rawCSVData), 3, labelsData)
return inputs, labels
}
代碼最后將測驗集資料匯入,并且開始進行預測:
// Form the testing matrices.
testInputs, testLabels := makeInputsAndLabels("data/test.csv")
fmt.Println(testLabels)
// Make the predictions using the trained model.
predictions, err := network.predict(testInputs)
if err != nil {
log.Fatal(err)
}
// Calculate the accuracy of our model.
var truePosNeg int
numPreds, _ := predictions.Dims()
for i := 0; i < numPreds; i++ {
// Get the label.
labelRow := mat.Row(nil, i, testLabels)
var prediction int
for idx, label := range labelRow {
if label == 1.0 {
prediction = idx
break
}
}
// Accumulate the true positive/negative count.
if predictions.At(i, prediction) == floats.Max(mat.Row(nil, i, predictions)) {
truePosNeg++
}
}
// Calculate the accuracy (subset accuracy).
accuracy := float64(truePosNeg) / float64(numPreds)
// Output the Accuracy value to standard out.
fmt.Printf("\nAccuracy = %0.2f\n\n", accuracy)
程式輸出:
&{{31 3 [0 1 0 1 0 0 1 0 0 0 1 0 0 1 0 0 0 1 1 0 0 1 0 0 1 0 0 0 1 0 0 0 1 0 0 1 1 0 0 0 0 1 0 0 1 0 0 1 0 0 1 0 1 0 0 0 1 1 0 0 1 0 0 0 1 0 1 0 0 0 0 1 0 0 1 1 0 0 1 0 0 0 1 0 0 0 1 0 0 1 0 0 0] 3} 31 3}
Accuracy = 0.97
可以看到,一共31個測驗樣本,只錯了3次,成功率達到了97%,
當然,就算是自己實作的小型神經網路,預測結果正確率也不可能達到100%,因為機器學習也是基于概率學范疇的學科,
為什么使用Golang?
事實上,大部分人都存在這樣一個刻板影響:機器學習必須要用Python來實作,就像前文所提到的,機器學習和Python語言并不存在因果關系,我們使用Golang同樣可以實作神經網路,同樣可以完成機器學習的流程,編程語言,僅僅是實作的工具而已,
但不能否認的是,Python當前在人工智能領域的很多細分方向都有比較廣泛的應用,比如自然語言處理、計算機視覺和機器學習等領域,但是并不意味著人工智能研發一定離不開Python語言,實際上很多其他編程語言也完全可以替代Python,比如Java、C++、Golang等等,
機器學習相關業務之所以大量使用Python,是因為Python有著極其豐富的三方庫進行支持,能夠讓研發人員把更多的精力放在演算法設計和演算法訓練等方面,說白了,就是不用重復造輪子,提高研發團隊整體產出的效率,比如面對基于Python的Pytorch和Tensorflow這兩個顛撲不破的深度學習巨石重鎮,Golang就得敗下陣來,沒有任何優勢可言,
所以,單以人工智能生態圈的繁榮程度而論,Golang還及不上Python,
結語
至此,我們就使用Golang完成了一個小型神經網路的實作,并且解決了一個真實存在的分類問題,那么,走完了整個流程,我們應該對基于神經網路架構的機器學習程序有了一個大概的了解,那就是機器學習只能解決可以被解決的問題,有經驗或者相關知識儲備的人類通過肉眼也能識別鳶尾花的種類,機器學習只是幫我們提高了識別效率而已,所以,如果還有人在你面前吹噓他能夠用機器學習來預測A股價格賺大錢,那么,他可能對機器學習存在誤解,或者可能對A股市場存在誤解,或者就是個純騙子,三者必居其一,
轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/548366.html
標籤:其他
