X.3 Go语言使用Gonum包辅助科学计算 -- 在图表中增加sigmoid函数的曲线

一张图表中也可以同时绘制多条折线,下面我们就在前面图表的基础上多增加一条表达数学函数 y = sigmoid(x) 的折线。sigmoid这个函数在人工智能的深度学习领域具有重要的地位,在神经网络的优化中发挥过非常巨大的作用,它的数学表达是:



其中,e是数学中的自然底数。sigmoid函数的特性是会把任何数字收敛到[0, 1]的范围之内,就是这点导致它在科学计算中有着广泛的应用范畴。下面我们就用代码来将sigmoid函数的曲线加入到图表中。注意通过增加折线中点的个数,可以模拟出接近平滑曲线的效果。


package main


import (

  "io/ioutil"


  "github.com/golang/freetype/truetype"

  "gonum.org/v1/plot"

  "gonum.org/v1/plot/plotter"

  "gonum.org/v1/plot/plotutil"

  "gonum.org/v1/plot/vg"


  "math"


  t "tools"

)


// sigmoid 计算sigmoid的函数

func sigmoid(x float64) float64 {

  return 1 / (1 + math.Pow(math.E, -x))

}


func main() {

  //为即将使用的中文字体起名

  fontName := "simhei"


  //读入改TrueType字体文件

  ttfBytes, err :=ioutil.ReadFile("/xiaoxian/web/txhelp/simhei.ttf")


  //读取文件发生异常时的处理

  if err != nil {

       t.Printfln("读取字体文件失败:%v", err.Error())

       return

  }


  //将字体加入字体表(仅本次运行有效)

  font, _ := truetype.Parse(ttfBytes)

  vg.AddFont(fontName,font)


  // 设置gonum/plot绘图时默认字体为自定义的中文字体

  plot.DefaultFont = fontName


  //设置所绘制的每个数据点的大小

  plotter.DefaultGlyphStyle.Radius =vg.Points(1.0)


  //新建一张图表

  p, _ := plot.New()


  //设置图表标题与X、Y轴说明文字

  p.Title.Text = "Gonum plot示例"

  p.X.Label.Text = "X轴"

  p.Y.Label.Text = "Y轴"


  //手动建立第一条折线的数据,共包含3个点

  points1 := plotter.XYs{

       {0.0, 0.0},

       {1.0, 1.0},

       {2.0, 4.0},

  }


  //为第二条折线(sigmoid函数对应的曲线)分配内存空间

  points2 := make(plotter.XYs, 0, 4)


  //让x的值从-10开始至10结束,每次步进0.1,循环计算每个x值对应的sigmoid函数计算结果值y;然后将该值加入到切片变量points2中

  for x := -10.0; x <= 10.0; x = x + 0.1 {

       y := sigmoid(x)


       points2 = append(points2, plotter.XY{X:x, Y: y})

  }


  //同时在图表中加入两条折线并起名

  plotutil.AddLinePoints(p, "函数 y = x * x", points1, "函数 y = sigmoid(x)", points2)


  //保存图表到图片文件

  p.Save(6*vg.Inch, 6*vg.Inch,"points.png")

}

代码 5‑48 增加sigmoid函数曲线

* 注:本代码参考网址(https://github.com/topxeq/goexamples/blob/master/plot2/plot2.go)

代码5‑48中,已经书写了很多代码注释来说明各处细节问题。我们在这里仅再做一些重点说明。


* 代码中首先定义了计算sigmoid的函数,里面使用了math包中的Pow函数来计算乘方运算;

* 为更充分地演示汉字的显示效果,除图表标题外,X轴、Y轴的说明和折线说明等也都加上了汉字;

* 为演示Linux和MacOS中加载字体文件时路径与Windows中的不同,本次的路径是Linux和MacOS格式的,注意与Windows的不同之处主要在于没有盘符,以及使用斜杠字符“/”而非反斜杠字符“\”来分隔路径中的各个部分;斜杠字符无需进行转义;

* 表示第一条折线的变量points1使用了简化的声明加直接用数值进行赋值的方式,这种方法对于数据不多的情况非常简单快捷。但注意,更规范的写法应该是:


  points1 := plotter.XYs {

       {X: 0.0, Y:0.0},

       {X: 1.0, Y:1.0},

       {X: 2.0, Y:4.0},

  }


因为plotter.XYs中的每个数据项应该是plotter.XY类型的,而plotter.XY类型定义如下:


type XY struct{ X, Y float64 }


其中两个成员名字是X和Y,严谨的话应该在赋值时加上这两个字段的名字。


* 表示第二条折线的变量points2是使用make函数来声明并分配内存空间的。当初始化切片变量并且make函数有3个参数时,第一个参数还是表示数据类型,第二个则表示新建几个数据项,第三个参数是表示一开始为该切片变量保留多少个数据项的空间(一般称作“容量”,即英语中的capacity),容量并非实际上有数据项,但是在数据项超出容量之前可以一直不重新分配内存空间,这样代码执行的效率更高。因此,一开始保留多少空间并非非常重要,但如果太小而实际数据量又很大的话,会导致系统频繁为points2重新分配空间,运行效率当然会下降。本例中初始化points2变量时,没有为其新建数据项,但保留了4个数据项的空间容量;

* 如前所述,为了让折线看起来更平滑、更像一条曲线,我们通过增加第二条线上的数据点个数来实现。因此为该条曲线准备数据的循环每次只让循环变量x增加0.1(注意此时当然不能用int类型的变量),然后就可以求得采样频度更高的sigmoid值曲线;

* 循环中为切片变量points2新增每一个数据项用的是Go语言的内置函数append,这是Go语言中专门用于给复合结构类型增加数据项的函数。一般用法是:


a := make([]int, 0, 10)

a = append(a, 8)


注意变量a是int类型的切片变量,并且一开始没有数据项但保留了10个数据项的空间(容量为10),append函数调用之后其中将有一个数据项8,其容量仍然为10,如果用len函数来查看变量a的大小(指数据项的个数)则应该是1。


* plotutil.AddLinePoints函数一次可以增加多条折线,每增加一条折线需要多输入两个参数,第一个是折线的名称,第二个才是线上数据点的切片集合;

* 由于数据较多,本次在保存图片时使用了6英寸的图片大小;


代码5‑48运行后产生的图片文件内容如下,



图5.9 增加了sigmoid函数曲线的图表


可以看出,每隔0.1一个数据点导致sigmoid函数的曲线还是比较平滑的。另外,多条曲线时,gonum/plot包会自动为其改变颜色以示区别。所有汉字的显示也正常。

你可能感兴趣的:(X.3 Go语言使用Gonum包辅助科学计算 -- 在图表中增加sigmoid函数的曲线)