Go Machine Learning

news/2024/5/13 21:16:47/文章来源:https://blog.csdn.net/shelgi/article/details/127436754

Go Machine Learning

前言

最近因为一直在弄部署整天c++写的非常头疼,趁着昨天把分割部署写好后打算换换口味,想着试试Go语言来实现一些机器学习,深度学习会是什么样子.之前推荐过Go+(goplus),不过这次打算用更基础的go语法来尝试.

1.准备工作

对于某个从未涉及的领域一开始肯定是一脸茫然,所以需要先找点资料入门.网上相关资料也没有特别多,搜的话基本就只有那几本书.不过这不重要随便找一本书了解入个门,后面的就都可以举一反三了

这里我看的是这本机器学习Go语言实现,提取码:o69s.这本书的出版时间是2018年,也就代表着书中的代码不一定全部能用,毕竟一些三方库肯定会有更新改动.

对应的仓库地址:https://github.com/PacktPublishing/Machine-Learning-With-Go

1.1 基本知识

这里的基本知识不止包括Go语言语法,编程知识和一些机器学习深度学习的知识,更主要的是了解第三方的库如何使用.拿到书以后迅速看一看前几章(两章左右吧,不用太细致看文字毕竟还有第二次细读),主要关注里面使用的库,这里总结一下我使用到的(有的书里的库我替换成了自己在github上找的)

  • gonum: ”gonum.org/v1/gonum“

  • gota: “github.com/go-gota/gota”

  • sklearn “github.com/pa-m/sklearn”

  • plot “gonum.org/v1/plot”

大致总结一下:gonum提供一些矩阵计算,类似于numpy;gota下有dataframe和series,类似于pandas;sklearn就是go实现部分sklearn的方法;plot顾名思义就是画图

对于Go来说,由于静态类型的限制在机器学习的代码编写上肯定是不如python便捷的,这一点心里要做好准备.如果习惯了python的灵活性,或者是只会python的那部分人最好不要尝试,当然估计这部分人也不会有这种兴趣爱好花时间在其他语言的探索上,有些话最后再说.

扯回这篇Blog,上面的三方库都可以去github上搜到源码和文档说明,配合着书里的代码很容易理解使用.

1.2 项目创建

这个应该是最简单的,go mod init 随便创建一个新的项目,然后根据自己的想法创建相关的文件结构

2. 正式开始

2.1 数据读取部分

既然是机器学习,那么数据当然是必不可少的.所以书上一开始就讲述了数据的收集和组织.这里我不多作赘述,只要学会怎么读取数据就好,给个基本的读取csv文件模板(下面的demo都是基于csv文件)

file, err := os.Open(filename)
if err != nil {
log.Fatal(err)
}defer file.Close()irisDF := dataframe.ReadCSV(file)
fmt.Println(irisDF)

image-20221020200904415

当然如果不想借助dataframe,自己写个读取函数也很简单

func PrintCSV(filename string) {f, err := os.Open(filename)if err != nil {log.Fatal(err)}r := csv.NewReader(f)records, err := r.ReadAll()if err != nil {log.Fatal(err)}for _, record := range records {for _, i := range record {print(i)print(" ")}println()}
}

image-20221020200732574

还有一些其他数据格式,例如json\database等都可以通过各自的方法实现读取,剩下的就是在dataframe里面对数据进行操作

2.2 矩阵计算部分

这部分主要就是利用gonum对数据进行计算,类似的还有numcpp,主要是实现一些矩阵计算.这部分看看书里demo就好,用的时候还是得自己查官网相关的文档

2.3 基本demo

这部分基本就是一些实战,从数据读取到探索性分析,再到建模评估.这里书上的库我没用,而是选择go语言版的sklearn,虽然功能还不太完善,但是基本的几个模型,预处理方法以及评价指标都还比较全.同样的查看文档后去实现书上的几个demo

2.3.1 回归

书上的例子是Advertising.csv,根据自变量回归得到target Sales.根据上述流程,读取数据就直接使用dataframe.然后进行探索性分析,主要是查看自变量和目标是否符合线性关系或者说存在相关性,我们肯定不会拿一个一看就毫不相关的变量去拟合回归.

func PlotImg(filename string) {file, err := os.Open(filename)if err != nil {log.Fatal(err)}defer file.Close()DF := dataframe.ReadCSV(file)target := DF.Col("Sales").Float()for _, colName := range DF.Names() {pts := make(plotter.XYs, DF.Nrow())for i, floatVal := range DF.Col(colName).Float() {pts[i].X = floatValpts[i].Y = target[i]}p := plot.New()p.X.Label.Text = colNamep.Y.Label.Text = "y"p.Add(plotter.NewGrid())s, err := plotter.NewScatter(pts)if err != nil {log.Fatal(err)}s.GlyphStyle.Radius = vg.Points(3)p.Add(s)if err := p.Save(4*vg.Inch, 4*vg.Inch, colName+"_scatter.jpg"); err != nil {log.Fatal(err)}}
}

想比matplotlib等python的画图库来说这个是不是复杂很多,除了要自己新建figure,还得一对对的添加(x,y).当然也可以把遍历部分封装成一个函数,只需要输入想要画的列名,不过如果不是需要多次使用也没必要,毕竟灵活性在这放着.如果改成绘制其他图表,不仅plotter的对象得用if或者switch判断,其他的格式也有可能要改.这里列的选择可以用DF.Select([]string{"xx","xxx"})

image-20221020203009051

image-20221020203035137

应该可以得到书上的效果,从图上也可以看出哪些变量和Sales具有线性关系.这里demo我选择一元线性回归,目标是找到Sales=w*TV+b

线性回归的原理很多书,博客都有介绍,如何利用梯度下降一步步拟合减小损失函数,所以这里直接用sklearn来实现.

sklearn的模型输入是gonum中的mat.Matrix,但是我们是dataframe读取的数据,并不是同一种类型,这里就是静态类型的麻烦之处.不过还好mat.Matrix只是一个interface,所以为了能够转换,我们实现接口相应的方法就好.

image-20221020203850392

type matrix struct {dataframe.DataFrame
}func (m matrix) At(i, j int) float64 {return m.Elem(i, j).Float()
}func (m matrix) T() mat.Matrix {return mat.Transpose{m}
}

image-20221020203947278

我们只需要实现At()和T(),也就是取值和转置,Dims()方法在dataframe中本身就有实现;其实仔细看的话,方法基本都有只是名字不一样.当然这只是针对float,如果是int或者string,At()中也要改为.Int()或者.String().

转为Matrix之后,剩下的就和python类似了,创建模型->fit()->predict()->评估,完整代码

package p3import ("fmt""github.com/go-gota/gota/dataframe""github.com/pa-m/sklearn/linear_model""github.com/pa-m/sklearn/metrics"modelselection "github.com/pa-m/sklearn/model_selection""gonum.org/v1/gonum/mat""gonum.org/v1/plot""gonum.org/v1/plot/plotter""gonum.org/v1/plot/vg""image/color""log""os"
)// for convert dataframe to matrix
// create nessesary implementationstype matrix struct {dataframe.DataFrame
}func (m matrix) At(i, j int) float64 {return m.Elem(i, j).Float()
}func (m matrix) T() mat.Matrix {return mat.Transpose{m}
}// sales=w*TV+b
func LinearDemo(Isplot bool) {//load filefilename := "Advertising.csv"file, err := os.Open(filename)if err != nil {log.Fatal(err)}defer file.Close()DF := dataframe.ReadCSV(file)X := mat.DenseCopyOf(&matrix{DF.Select([]string{"TV"})})Y := mat.DenseCopyOf(&matrix{DF.Select([]string{"Sales"})})XTrain, XTest, YTrain, YTest := modelselection.TrainTestSplit(X, Y, 0.2, 0)lr := linearmodel.NewLinearRegression()lr.Fit(XTrain, YTrain)NPred, _ := XTest.Dims()Pred := mat.NewDense(NPred, 1, nil)lr.Predict(XTest, Pred)fmt.Printf("Coefficients: %.3f\n", mat.Formatted(lr.Coef))fmt.Printf("Coefficients: %.3f\n", mat.Formatted(lr.Intercept))fmt.Printf("the pred result is: Sales=%.3f *TV + %.3f\n", mat.Formatted(lr.Intercept), mat.Formatted(lr.Coef))fmt.Printf("Mean squared error: %.2f\n", metrics.MeanSquaredError(YTest, Pred, nil, "").At(0, 0))fmt.Printf("Mean absolute error: %.2f\n", metrics.MeanAbsoluteError(YTest, Pred, nil, "").At(0, 0))fmt.Printf("Variance score: %.2f\n", metrics.R2Score(YTest, Pred, nil, "").At(0, 0))if Isplot {//predict allNPred, _ = X.Dims()Pred := mat.NewDense(NPred, 1, nil)lr.Predict(X, Pred)p := plot.New()xys := func(X, Y mat.Matrix) plotter.XYs {var data plotter.XYsfor sample := 0; sample < NPred; sample++ {data = append(data, struct{ X, Y float64 }{X.At(sample, 0), Y.At(sample, 0)})}return data}s, _ := plotter.NewScatter(xys(X, Y))l, _ := plotter.NewLine(xys(X, Pred))l.Color = color.RGBA{0, 0, 255, 255}p.Add(s, l)// Save the plot to a PNG file.pngfile := "linearregression.png"os.Remove(pngfile)if err := p.Save(4*vg.Inch, 3*vg.Inch, pngfile); err != nil {panic(err)}}
}

image-20221020204505805

image-20221020204527712

2.3.2 分类

这里以iris为例,首先尝试一下传统的机器学习模型

2.3.2.1 KNN&&SVM

还是一样先查看数据,探索性分析这部分我直接略过,主要是后续对数据的处理.

iris.csv的内容如下

image-20221020205028322

对应的label是string,所以需要先变为数值型.这部分可以使用sklearn中的LabelEncoder ¶或者OneHotEncoder,也可以利用dataframe的Capply()自定义方法实现,这是我的转换函数

func replaceLabel(col series.Series) series.Series {rows := col.Len()NewSeries := series.New([]float64{}, series.Float, "")changeMap := map[string]float64{"Iris-setosa":     0.0,"Iris-versicolor": 1.0,"Iris-virginica":  2.0,}for i := 0; i < rows; i++ {NewSeries.Append(changeMap[col.Elem(i).String()])}return NewSeries}

查看文档可以知道Capply()需要入参和输出都是Series,所以得根据方法要求写转换函数,再次体会一下类型限制.其余的就是使用sklearn没啥好说的,完整代码如下

package p3import ("fmt""github.com/go-gota/gota/dataframe""github.com/go-gota/gota/series""github.com/pa-m/sklearn/metrics"modelselection "github.com/pa-m/sklearn/model_selection""github.com/pa-m/sklearn/neighbors""github.com/pa-m/sklearn/svm""gonum.org/v1/gonum/mat""log""os"
)func replaceLabel(col series.Series) series.Series {rows := col.Len()NewSeries := series.New([]float64{}, series.Float, "")changeMap := map[string]float64{"Iris-setosa":     0.0,"Iris-versicolor": 1.0,"Iris-virginica":  2.0,}for i := 0; i < rows; i++ {NewSeries.Append(changeMap[col.Elem(i).String()])}return NewSeries}func ClassifyDemo() {filename := "iris.csv"file, err := os.Open(filename)if err != nil {log.Fatal(err)}defer file.Close()DF := dataframe.ReadCSV(file)//replace the label from string to floatX := mat.DenseCopyOf(&matrix{DF.Select([]int{0, 1, 3})})Y := mat.DenseCopyOf(&matrix{DF.Select(4).Capply(replaceLabel)})XTrain, XTest, YTrain, YTest := modelselection.TrainTestSplit(X, Y, 0.2, 0)clf := neighbors.NewKNeighborsClassifier(3, "uniform")clf.Fit(XTrain, YTrain)NPred, _ := XTest.Dims()Pred := mat.NewDense(NPred, 1, nil)clf.Predict(XTest, Pred)fmt.Printf("The accuracy of KNN: %.02f%%\n", metrics.AccuracyScore(YTest, Pred, true, nil)*100)clf2 := svm.NewSVC()//clf2.Degree = 3clf2.Kernel = "linear"clf2.Fit(XTrain, YTrain)clf2.Predict(XTest, Pred)//fmt.Printf("Pred:\n%g\n", mat.Formatted(Pred))fmt.Printf("The accuracy of SVM: %.02f%%\n", metrics.AccuracyScore(YTest, Pred, true, nil)*100)
}

image-20221020211125078

KNN本来就是用于多分类问题,所以效果还可以;svm只是二分类,没有用上一对多等策略,所以预测的大多都是某一类导致准确率很差;

2.3.2.2 MLP

尝试了传统的方法,在简单数据集上选择了合适的模型,效果已经非常好了,那再用深度学习方法来试试呢

这里可以选择用sklearn的MLPClassifier,不过书上也给出了纯go实现,这部分还是值得好好品一品的.

回顾一下神经网络训练的基本内容

  1. 构造简单网络,定义输入,隐藏层和输出,中间加入激活函数
  2. 正向传播,计算网络输出与目标的损失
  3. 根据损失函数反向传播计算梯度,对网络中的权重和偏置项进行更新

对于计算推导,可以看看这篇博客,有图有计算步骤https://www.cnblogs.com/charlotte77/p/5629865.html

下面主要来分析一下这本书中的写法,首先要构思如何去写这个功能,根据上面几个库的使用,应该体会到如果想复用或者模块化,那肯定得构建一个对象,然后一步步实现里面的类方法.go语言虽然没有class,但是struct也能实现我们想要的.

type neuralNet struct {config  neuralNetConfigwHidden *mat.DensebHidden *mat.DensewOut    *mat.DensebOut    *mat.Dense
}type neuralNetConfig struct {inputNum     intoutputNum    inthiddenNum    intEpochs       intlearningRate float64
}func NewNetwork(config neuralNetConfig) *neuralNet {return &neuralNet{config: config}
}

这就是最基本的类以及初始化方法,我们构造神经网络的时候必须指定输入输出的大小,隐藏层的层数以及训练时的Epochs和学习率

再来看看两个最基本的类方法,train()predict()

对于train(),根据上面说的流程,我们先初始化一些权重和bias,然后一次次迭代,正向传播计算输出,再反向传播更新网络参数.这里需要注意的是现在的mat不再允许创建行列均为0的Matrix,所以得自己一步步计算原代码中的维度大小进行修改填充

原代码中激活函数用的Sigmoid(),损失就是target-output,好处是对于损失的梯度不用计算,默认就是error(12(y−y^)2)′=y−y^(\frac{1}{2}(y-\hat{y})^2)^{'}=y-\hat{y}(21(yy^)2)=yy^.sigmoid的导数也可以预先计算设置好.修改后的相关代码如下

func (nn *neuralNet) train(x, y *mat.Dense) error {N, _ := x.Dims()randSource := rand.NewSource(time.Now().UnixNano())randGen := rand.New(randSource)wHiddenRaw := make([]float64, nn.config.hiddenNum*nn.config.inputNum)bHiddenRaw := make([]float64, nn.config.hiddenNum)wOutRaw := make([]float64, nn.config.outputNum*nn.config.hiddenNum)bOutRaw := make([]float64, nn.config.outputNum)for _, param := range [][]float64{wHiddenRaw, bHiddenRaw, wOutRaw, bOutRaw} {for i := range param {param[i] = randGen.Float64()}}wHidden := mat.NewDense(nn.config.inputNum, nn.config.hiddenNum, wHiddenRaw)bHidden := mat.NewDense(1, nn.config.hiddenNum, bHiddenRaw)wOut := mat.NewDense(nn.config.hiddenNum, nn.config.outputNum, wOutRaw)bOut := mat.NewDense(1, nn.config.outputNum, bOutRaw)output := mat.NewDense(N, nn.config.outputNum, nil)// train model.for i := 0; i < nn.config.Epochs; i++ {// forwardhiddenLayerInput := mat.NewDense(N, nn.config.hiddenNum, nil)hiddenLayerInput.Mul(x, wHidden)addBHidden := func(_, col int, v float64) float64 { return v + bHidden.At(0, col) }hiddenLayerInput.Apply(addBHidden, hiddenLayerInput)hiddenLayerActivations := mat.NewDense(N, nn.config.hiddenNum, nil)applySigmoid := func(_, _ int, v float64) float64 { return Sigmoid(v) }hiddenLayerActivations.Apply(applySigmoid, hiddenLayerInput)outputLayerInput := mat.NewDense(N, nn.config.outputNum, nil)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)// backpropagation.networkError := mat.NewDense(N, nn.config.outputNum, nil)networkError.Sub(y, output)slopeOutputLayer := mat.NewDense(N, nn.config.outputNum, nil)applySigmoidPrime := func(_, _ int, v float64) float64 { return SigmoidPrime(v) }slopeOutputLayer.Apply(applySigmoidPrime, output)slopeHiddenLayer := mat.NewDense(N, nn.config.hiddenNum, nil)slopeHiddenLayer.Apply(applySigmoidPrime, hiddenLayerActivations)dOutput := mat.NewDense(N, nn.config.outputNum, nil)dOutput.MulElem(networkError, slopeOutputLayer)errorAtHiddenLayer := mat.NewDense(N, nn.config.hiddenNum, nil)errorAtHiddenLayer.Mul(dOutput, wOut.T())dHiddenLayer := mat.NewDense(N, nn.config.hiddenNum, nil)dHiddenLayer.MulElem(errorAtHiddenLayer, slopeHiddenLayer)// Adjust the parameters.wOutAdj := mat.NewDense(nn.config.hiddenNum, nn.config.outputNum, nil)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 := mat.NewDense(nn.config.inputNum, nn.config.hiddenNum, nil)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)}nn.wHidden = wHiddennn.bHidden = bHiddennn.wOut = wOutnn.bOut = bOutreturn nil
}func (nn *neuralNet) predict(x *mat.Dense) (*mat.Dense, error) {N, _ := x.Dims()// Check to make sure that our neuralNet value// represents a trained model.if nn.wHidden == nil || nn.wOut == nil || nn.bHidden == nil || nn.bOut == nil {return nil, errors.New("the supplied neurnal net weights and biases are empty")}// Define the output of the neural network.output := mat.NewDense(N, nn.config.outputNum, nil)// Complete the feed forward process.hiddenLayerInput := mat.NewDense(N, nn.config.hiddenNum, nil)hiddenLayerInput.Mul(x, nn.wHidden)addBHidden := func(_, col int, v float64) float64 { return v + nn.bHidden.At(0, col) }hiddenLayerInput.Apply(addBHidden, hiddenLayerInput)hiddenLayerActivations := mat.NewDense(N, nn.config.hiddenNum, nil)applySigmoid := func(_, _ int, v float64) float64 { return Sigmoid(v) }hiddenLayerActivations.Apply(applySigmoid, hiddenLayerInput)outputLayerInput := mat.NewDense(N, nn.config.outputNum, nil)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
}func Sigmoid(x float64) float64 {return 1.0 / (1.0 + math.Exp(-x))
}func SigmoidPrime(x float64) float64 {return x * (1.0 - x)
}

然后这次将iris的标签进行one-hot,实现如下

func replaceLabel2(col dataframe.DataFrame) dataframe.DataFrame {rows, _ := col.Dims()var NewDF = make([][]string, 0)changeMap := map[string][]string{"Iris-setosa":     []string{"1.", "0.", "0."},"Iris-versicolor": []string{"0.", "1.", "0."},"Iris-virginica":  []string{"0.", "0.", "1."},}for i := 0; i < rows; i++ {NewDF = append(NewDF, changeMap[col.Elem(i, 0).String()])}return dataframe.LoadRecords(NewDF, dataframe.HasHeader(false))
}

为什么这次得这样,因为Capply()返回的是Series,而one-hot之后肯定是二维的dataframe,因此干脆直接转为dataframe.然后就是一些维度求和以及求每一行最大值下标的函数(为了将predict变为one-hot),最后这部分完整代码如下

package p3import ("errors""fmt""github.com/go-gota/gota/dataframe""github.com/pa-m/sklearn/metrics"modelselection "github.com/pa-m/sklearn/model_selection""gonum.org/v1/gonum/floats""gonum.org/v1/gonum/mat""log""math""math/rand""os""time"
)type neuralNet struct {config  neuralNetConfigwHidden *mat.DensebHidden *mat.DensewOut    *mat.DensebOut    *mat.Dense
}type neuralNetConfig struct {inputNum     intoutputNum    inthiddenNum    intEpochs       intlearningRate float64
}func NewNetwork(config neuralNetConfig) *neuralNet {return &neuralNet{config: config}
}func (nn *neuralNet) train(x, y *mat.Dense) error {N, _ := x.Dims()randSource := rand.NewSource(time.Now().UnixNano())randGen := rand.New(randSource)wHiddenRaw := make([]float64, nn.config.hiddenNum*nn.config.inputNum)bHiddenRaw := make([]float64, nn.config.hiddenNum)wOutRaw := make([]float64, nn.config.outputNum*nn.config.hiddenNum)bOutRaw := make([]float64, nn.config.outputNum)for _, param := range [][]float64{wHiddenRaw, bHiddenRaw, wOutRaw, bOutRaw} {for i := range param {param[i] = randGen.Float64()}}wHidden := mat.NewDense(nn.config.inputNum, nn.config.hiddenNum, wHiddenRaw)bHidden := mat.NewDense(1, nn.config.hiddenNum, bHiddenRaw)wOut := mat.NewDense(nn.config.hiddenNum, nn.config.outputNum, wOutRaw)bOut := mat.NewDense(1, nn.config.outputNum, bOutRaw)output := mat.NewDense(N, nn.config.outputNum, nil)// train model.for i := 0; i < nn.config.Epochs; i++ {// forwardhiddenLayerInput := mat.NewDense(N, nn.config.hiddenNum, nil)hiddenLayerInput.Mul(x, wHidden)addBHidden := func(_, col int, v float64) float64 { return v + bHidden.At(0, col) }hiddenLayerInput.Apply(addBHidden, hiddenLayerInput)hiddenLayerActivations := mat.NewDense(N, nn.config.hiddenNum, nil)applySigmoid := func(_, _ int, v float64) float64 { return Sigmoid(v) }hiddenLayerActivations.Apply(applySigmoid, hiddenLayerInput)outputLayerInput := mat.NewDense(N, nn.config.outputNum, nil)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)// backpropagation.networkError := mat.NewDense(N, nn.config.outputNum, nil)networkError.Sub(y, output)slopeOutputLayer := mat.NewDense(N, nn.config.outputNum, nil)applySigmoidPrime := func(_, _ int, v float64) float64 { return SigmoidPrime(v) }slopeOutputLayer.Apply(applySigmoidPrime, output)slopeHiddenLayer := mat.NewDense(N, nn.config.hiddenNum, nil)slopeHiddenLayer.Apply(applySigmoidPrime, hiddenLayerActivations)dOutput := mat.NewDense(N, nn.config.outputNum, nil)dOutput.MulElem(networkError, slopeOutputLayer)errorAtHiddenLayer := mat.NewDense(N, nn.config.hiddenNum, nil)errorAtHiddenLayer.Mul(dOutput, wOut.T())dHiddenLayer := mat.NewDense(N, nn.config.hiddenNum, nil)dHiddenLayer.MulElem(errorAtHiddenLayer, slopeHiddenLayer)// Adjust the parameters.wOutAdj := mat.NewDense(nn.config.hiddenNum, nn.config.outputNum, nil)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 := mat.NewDense(nn.config.inputNum, nn.config.hiddenNum, nil)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)}nn.wHidden = wHiddennn.bHidden = bHiddennn.wOut = wOutnn.bOut = bOutreturn nil
}func (nn *neuralNet) predict(x *mat.Dense) (*mat.Dense, error) {N, _ := x.Dims()// Check to make sure that our neuralNet value// represents a trained model.if nn.wHidden == nil || nn.wOut == nil || nn.bHidden == nil || nn.bOut == nil {return nil, errors.New("the supplied neurnal net weights and biases are empty")}// Define the output of the neural network.output := mat.NewDense(N, nn.config.outputNum, nil)// Complete the feed forward process.hiddenLayerInput := mat.NewDense(N, nn.config.hiddenNum, nil)hiddenLayerInput.Mul(x, nn.wHidden)addBHidden := func(_, col int, v float64) float64 { return v + nn.bHidden.At(0, col) }hiddenLayerInput.Apply(addBHidden, hiddenLayerInput)hiddenLayerActivations := mat.NewDense(N, nn.config.hiddenNum, nil)applySigmoid := func(_, _ int, v float64) float64 { return Sigmoid(v) }hiddenLayerActivations.Apply(applySigmoid, hiddenLayerInput)outputLayerInput := mat.NewDense(N, nn.config.outputNum, nil)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
}func Sigmoid(x float64) float64 {return 1.0 / (1.0 + math.Exp(-x))
}func SigmoidPrime(x float64) float64 {return x * (1.0 - x)
}func sumAlongAxis(axis int, m *mat.Dense) (*mat.Dense, error) {numRows, numCols := m.Dims()var output *mat.Denseswitch 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 MaxAlongAxis(m *mat.Dense) (*mat.Dense, error) {numRows, numCols := m.Dims()var output *mat.Denseres := []float64{}for i := 0; i < numRows; i++ {row := mat.Row(nil, i, m)idx := floats.MaxIdx(row)for j := 0; j < numCols; j++ {if j == idx {res = append(res, 1)} else {res = append(res, 0)}}}output = mat.NewDense(numRows, numCols, res)return output, nil
}func replaceLabel2(col dataframe.DataFrame) dataframe.DataFrame {rows, _ := col.Dims()var NewDF = make([][]string, 0)changeMap := map[string][]string{"Iris-setosa":     []string{"1.", "0.", "0."},"Iris-versicolor": []string{"0.", "1.", "0."},"Iris-virginica":  []string{"0.", "0.", "1."},}for i := 0; i < rows; i++ {NewDF = append(NewDF, changeMap[col.Elem(i, 0).String()])}return dataframe.LoadRecords(NewDF, dataframe.HasHeader(false))
}func NNClassify() {config := neuralNetConfig{inputNum:     4,outputNum:    3,hiddenNum:    10,Epochs:       1000,learningRate: 0.009,}filename := "iris.csv"file, err := os.Open(filename)if err != nil {log.Fatal(err)}defer file.Close()DF := dataframe.ReadCSV(file, dataframe.HasHeader(true))//replace the label from string to floatX := mat.DenseCopyOf(&matrix{DF.Select([]int{0, 1, 2, 3})})Y := mat.DenseCopyOf(&matrix{replaceLabel2(DF.Select(4))})XTrain, XTest, YTrain, YTest := modelselection.TrainTestSplit(X, Y, 0.33, 0)//fmt.Printf("X_train:\n%g\n", mat.Formatted(XTrain))//fmt.Printf("Y_train:\n%g\n", mat.Formatted(YTrain))network := NewNetwork(config)start := time.Now()if err := network.train(XTrain, YTrain); err != nil {log.Fatal(err)}fmt.Println("Train cost time:", time.Since(start))f := mat.Formatted(network.wHidden, mat.Prefix(" "))fmt.Printf("\nwHidden = % v\n\n", f)f = mat.Formatted(network.bHidden, mat.Prefix(" "))fmt.Printf("\nbHidden = % v\n\n", f)f = mat.Formatted(network.wOut, mat.Prefix(" "))fmt.Printf("\nwOut = % v\n\n", f)f = mat.Formatted(network.bOut, mat.Prefix(" "))fmt.Printf("\nbOut = % v\n\n", f)//predictpredictions, err := network.predict(XTest)fmt.Printf("\npredictions = % v\n\n", mat.Formatted(predictions))if err != nil {log.Fatal(err)}//fmt.Println(mat.Formatted(predictions))pred, err := MaxAlongAxis(predictions)if err != nil {log.Fatal(err)}//fmt.Println(mat.Formatted(pred))fmt.Printf("The accuracy of MLP: %.02f%%\n", metrics.AccuracyScore(YTest, pred, true, nil)*100)}

image-20221020215844126

image-20221020215857628

迭代1000次只花了66ms,准确率98%也超过了传统模型.

2.4 More

当然远远不止书上这些,社区里还有很多很好的库能帮助go实现机器学习/深度学习.比如tensorflow(libtorch) binding for Go,可惜已经很久没更新.特别是libtorch的,我去了解的时候一看安装,里面还是cuda10.x,就没有再去探索了.相反的一些点赞数不多的库https://github.com/kuroko1t/gotorch看着真的还很不错,也许目前功能还不完善但拿来作为项目学习下还是可以的.之前很早就提到的Go+也是数据科学方面非常好的go语言拓展.未来如果有时间,会好好探索如何将go转为易用于深度学习的语言之一

最后

花了一天抛开聚类和时间序列两章没具体细看,从完全不会这些库的使用到根据文档基本能实现自己想要的功能;同时还把自己放空了一天.不用再去想为什么onnx转换后trt的精度会有损失,部署的时候各种后处理怎么实现等等,我觉得这种自我调节休息还是很有必要的.长期只使用python,c++,都快忘了其他语言怎么写.

作为喜欢写写代码,研究技术的人,在闲暇时抛开工作自我学习本身就是一种休闲.python在好几年前刚学习的时候我对它爱不释手,因为学习成本很低也不用编译,只要配好解释器随处可以运行,不像c++有的时候为了验证一个demo还得写CMakeLists或者命令行链接一堆头文件.随着不断学习,有的时候运行效率,适用性根本达不到要求.不可能每台电脑都得配python环境再给你pip装一堆库,pyinstaller打包只要引入一些三方库(numpy,pandas等)出来的文件就不会很小,不用三方库那python的优势又在哪呢?

像我们这种科研狗,如果毕业打算继续从事研究,那用python快速验证模型效果等等无可厚非.如果打算从事互联网开发或者算法工程师,仅仅会写点python只能说坑了同事也坑了自己.不是clone一个项目稍微改几句,或者添加点东西点个run能跑就是会写代码.不考虑项目结构,不考虑代码效率,不考虑格式规范真的很头疼.(有感而发,别怼怼就是你对)

现在很多“火箭”三方库已经实现了,我们只需要会用就行.小的“轮子”在这基础上自己造一造,举一反三.

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.luyixian.cn/news_show_404083.aspx

如若内容造成侵权/违法违规/事实不符,请联系dt猫网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

硬盘分哪几种类型及主要参数详解

硬盘分哪几种类型 按接口分为:ide、sata、scsi 。 按大小分1.8英寸、 2.5英寸、 3.5英寸、 5.25英寸。 转速分为&#xff1a; 4500转,5400转&#xff0c;7200转和万转。 缓存分为&#xff1a;2m、8m、16m。 硬盘主要参数 硬盘主要参数详解&#xff1a; 转速&#xff1a;硬盘…

Web前端:所有新前端开发人员应该具备的顶级技能

作为前端开发人员&#xff0c;确保软件程序的用户界面正常运行是你的工作&#xff0c;这是一项艰巨的工作&#xff0c;因为你必须确保每个组件都按照预期的方式工作&#xff0c;这样用户才能有良好的体验。 前端开发现在需求量很大。前端开发者管理软件的用户界面/ UX。这很重要…

我不得不学的反射

什么是反射 反射是指对于任何一个Class类&#xff0c;在运行时都可以直接得到这个类的全部成分 这种运行时动态获取信息以及动态调用类中成分的能力称为java的反射机制 获取字节码文件 获取反射对象 方法一 public static void main(String[] args) throws Exception {Cla…

学生选课系统 前后端分离 vue springboot

学生选课系统 前后端分离 vue springboot系统描述一、系统功能二、系统截图1.网络爬虫 新闻获取代码2.pom源码系统描述 基于spring boot vue的学生选课系统 前端&#xff1a; Vue ElementUI axios 后端 springboot 持久层 mybatis Plus 会话 Spring Session redis 日志 AOP Mo…

程序设计与算法(三)C++面向对象程序设计笔记 第七周 输入输出和模板

笔记按照中国大学MOOC上北京大学郭炜老师主讲的程序设计与算法&#xff08;三&#xff09;C面向对象程序设计所作&#xff0c;B站上也有资源。原课程链接如下&#xff1a; 程序设计与算法&#xff08;三&#xff09;C面向对象程序设计 其他各章节链接如下&#xff1a; 程序设…

《CTF攻防世界web题》之我什么都不会(1)

前言 &#x1f340;作者简介&#xff1a;被吉师散养、喜欢前端、学过后端、练过CTF、玩过DOS、不喜欢java的不知名学生。 &#x1f341;个人主页&#xff1a;被吉师散养的职业混子 &#x1fad2;文章目的&#xff1a;记录唯几我能做上的题 &#x1f342;相应专栏&#xff1a;CT…

[附源码]Java计算机毕业设计SSM高校创新学分申报管理系统

项目运行 环境配置&#xff1a; Jdk1.8 Tomcat7.0 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技术&#xff1a; SSM mybatis Maven Vue 等等组成&#xff0c;B/S模式 M…

从初级进阶为高级程序员,需要经历什么?

“怎样才能成为一名高级程序员&#xff1f;” 对每个新人来说&#xff0c;大概都梦想着尽快实现职场进阶&#xff0c;从萌新成长为技术大佬。但媳妇熬成婆&#xff0c;世界上最难熬的就是工作经验…… 程序员具有明确的职业等级制度&#xff0c;依据工作经验和水平划分&#x…

vite+vue3+ts项目搭建之集成qiankun让其成为子应用模板,并能实现主子应用之间跳转切换(新增在线预览地址)

前言 以下操作&#xff0c;是续接之前 第四步 ——即&#xff1a;vitevue3tspiniaelement-plus项目已完成搭建好&#xff0c;可以直接业务开发了 主应用技术栈&#xff1a;vue2webpackjs 集成qiankun(微前端) 1、安装vite-plugin-qiankun npm install vite-plugin-qiankun2、…

Gradle 入门说难也不难,说简单吧也不简单~

在学习过程中最痛苦的事&#xff0c;并不是认为自己坚持不下去学不会&#xff0c;而是对某块知识点的碎片信息学习了很多&#xff0c;仍然无法窥其门径&#xff0c;处于懵懂状态。 就拿Gradle来说&#xff0c;我之前就是这种状态&#xff0c;不管怎么去查阅文档和视频&#xf…

联邦学习(Federated Learning):技术角度的理解

联邦学习&#xff08;Federated Learning&#xff09;&#xff1a;技术角度的理解 学习笔记 B站学习链接&#xff1a;https://www.bilibili.com/video/BV1YK4y1G7jw/?p7&vd_source7def3d3fc89c6921c7aeadf5e4023d35 1.背景与动机 例子 Example >> 问题&#xff1a…

AES加密解密算法设计(C++)

目 录 1&#xff0e; 背景与意义 4 2. 系统设计 5 2.1系统主要目标 5 2.2主要软件需求&#xff08;运行环境&#xff09; 5 2.3功能模块与系统结构 6 3 系统功能程序设计 8 3.1基本要求部分 8 3.1.1 字节替换 8 3.1.2行移位 9 3.1.3列混合 11 3.1.4密钥加 13 3.1.5密钥扩展 14 …

神经网络训练多少次合适,神经网络训练时间多长

1、tensorflow 训练一个神经网络 需要多长时间 基本使用 使用 TensorFlow, 你必须明白 TensorFlow: 使用图 (graph) 来表示计算任务. 在被称之为 会话 (Session) 的上下文 (context) 中执行图. 使用 tensor 表示数据. 通过 变量 (Variable) 维护状态. 使用 feed 和 fetch 可以…

【附源码】计算机毕业设计SSM实验室设备明细管理系统

项目运行 环境配置&#xff1a; Jdk1.8 Tomcat7.0 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技术&#xff1a; SSM mybatis Maven Vue 等等组成&#xff0c;B/S模式 M…

【自监督论文阅读笔记】Simmim: A simple framework formasked image modeling

本文介绍了 SimMIM&#xff0c;这是一个用于 掩码图像建模 的简单框架。本文简化了最近提出的相关方法&#xff0c;无需特殊设计&#xff0c;例如通过离散 VAE 或聚类 进行 block-wise 分块级的掩码 和 tokenization。为了研究 是什么让掩码图像建模任务学习良好的表示&#xf…

nginx----(1)nginx的单机安装

文章目录Nginx卸载开源版Nginx安装默认简单安装Nginx的源码复杂安装防火墙问题nginx启停安装成系统服务Nginx卸载 步骤一&#xff1a;需要将nginx的进程关闭 ./nginx -s stop步骤二:将安装的nginx进行删除 rm -rf /usr/local/nginx步骤三:将安装包之前编译的环境清除掉 mak…

opc client 客户端软件测试工具

一、概述&#xff1a; 本软件一款OPC客户端测试工具&#xff0c;使用本软件连接到OPC server服务端&#xff0c;获取server数据&#xff0c;简单好上手&#xff0c;软件也很小巧&#xff0c;操作便利&#xff0c;也是绿色软件&#xff0c;免安装。 二、软件基础环境保证&#x…

期货交易结算信息(期货结算系统)

期货中的结算价是怎么计算出来的&#xff1f; 最近常有新手投资者问&#xff1a;为什么期货账户收盘时年持仓是盈利的&#xff0c;怎么当天结算单上却是亏损的&#xff1f; 之所以会有这个疑问&#xff0c;是因为投资者没有明白期货收盘价、结算价、成交价三者之间的关系。今天…

dockerfile编写构建镜像

文章目录一、dockerfile1、什么是dockerfile2、原理二、docker镜像的创建1、创建镜像的三种方法①基于已有镜像创建②基于本地模板创建③基于dockerfile创建三、镜像分层的原理1、docker镜像结构的分层2、bootfs内核空间3、rootfs内核空间4、AUFS与overlay/overlay2overlay结构…

【ViT 微调时关于position embedding如何插值(interpolate)的详解】

目录1. 问题描述2. positional embedding如何interpolate3. 输入的sequence length改变了ViT还能正常前向推断&#xff1f;本文适合对Vision Transformer有一定了解&#xff08;知道内部结构和一些实现细节&#xff0c;最好是精读过ViT这篇论文&#xff09;的读者阅读&#xff…