本文正在参与 人工智能创作者扶持方案

人工智能神经网络( 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)。

动手造轮子自己实现人工智能神经网络(ANN),解决鸢尾花分类问题Golang1.18实现

通俗来讲,便是咱们要练习一个神经网络模型,它能够依据鸢尾花的四个特征,自动地对鸢尾花的品种进行分类。

在这个事例中,咱们运用了一个包含一个躲藏层的神经网络,它的输入层有4个神经元,代表鸢尾花的4个特征;躲藏层有3个神经元;输出层有3个神经元,别离代表3种鸢尾花的品种:

动手造轮子自己实现人工智能神经网络(ANN),解决鸢尾花分类问题Golang1.18实现

由此可见,神经网络一般由三层组成:输入层、躲藏层和输出层。

输入层:输入层接收外部输入信号,是神经网络的起点。它的神经元数量与输入特征的数量相同,每个神经元代表一个输入特征。输入层的首要作用是将外部输入转换为神经网络内部的信号。

躲藏层:躲藏层位于输入层和输出层之间,是神经网络的核心部分。它的神经元数量能够依据问题的杂乱度自由设定,每个神经元接收上一层神经元输出的信号,并进行加权处理和激活函数处理,再将成果传递给下一层神经元。躲藏层的首要作用是对输入信号进行杂乱的非线性转换,提取出输入信号中的特征,然后使得神经网络能够对杂乱的问题进行处理。

输出层:输出层是神经网络的结尾,它的神经元数量一般与问题的输出数量相同。每个神经元代表一个输出成果,输出层的首要作用是将躲藏层处理后的信号进行进一步处理,并将终究的成果输出。

在神经网络中,输入信号从输入层开端,经过躲藏层的处理,终究到达输出层。每一层的神经元都与下一层的神经元相连,它们之间的连接能够看成是一种权重联系,权重值代表了两个神经元之间的相关性强度。当神经网络接收到输入信号后,每个神经元都会对这些信号进行加权处理,并经过激活函数将成果输出给下一层神经元,终究形成输出成果。经过不断调整权重和激活函数,神经网络能够学习到输入和输出之间的杂乱非线性联系,然后对不知道数据进行猜测和分类等任务。

界说神经网络结构体

在开端练习之前,咱们先界说一些需求的结构体和函数:

// 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  
}

预备特征和期望数据

下面咱们需求预备鸢尾花的特征和期望数据,能够在加州大学官网下载:archive.ics.uci.edu/ml/datasets…

动手造轮子自己实现人工智能神经网络(ANN),解决鸢尾花分类问题Golang1.18实现

这儿包含花瓣和花蕊的详细数据,以及这些样本所对应的花的品种,别离对应上文说到的山鸢尾(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股商场存在误解,或许便是个纯骗子,三者必居其一。