diff --git a/categories/index.html b/categories/index.html index 7e2b9b5..7e592c5 100644 --- a/categories/index.html +++ b/categories/index.html @@ -8,5 +8,5 @@ Cancel文章标签分类 -
+
\ No newline at end of file diff --git a/categories/index.xml b/categories/index.xml index 4689a78..68fe12b 100644 --- a/categories/index.xml +++ b/categories/index.xml @@ -1 +1 @@ -Categories - Category - yizhigopher的博客http://plutolove233.github.io/categories/Categories - Category - yizhigopher的博客Hugo -- gohugo.iozh-CNyizhigopher@163.com (yizhigopher)yizhigopher@163.com (yizhigopher)Fri, 08 Nov 2024 22:39:44 +0800golang之路http://plutolove233.github.io/categories/golang%E4%B9%8B%E8%B7%AF/Fri, 08 Nov 2024 22:39:44 +0800yizhigopherhttp://plutolove233.github.io/categories/golang%E4%B9%8B%E8%B7%AF/Dockerhttp://plutolove233.github.io/categories/docker/Tue, 29 Oct 2024 19:44:43 +0800yizhigopherhttp://plutolove233.github.io/categories/docker/vscode插件配置设置http://plutolove233.github.io/categories/vscode%E6%8F%92%E4%BB%B6%E9%85%8D%E7%BD%AE%E8%AE%BE%E7%BD%AE/Mon, 22 Jan 2024 14:48:37 +0800yizhigopherhttp://plutolove233.github.io/categories/vscode%E6%8F%92%E4%BB%B6%E9%85%8D%E7%BD%AE%E8%AE%BE%E7%BD%AE/cudahttp://plutolove233.github.io/categories/cuda/Thu, 14 Dec 2023 15:11:01 +0800yizhigopherhttp://plutolove233.github.io/categories/cuda/golang小技巧http://plutolove233.github.io/categories/golang%E5%B0%8F%E6%8A%80%E5%B7%A7/Mon, 04 Dec 2023 19:13:11 +0800yizhigopherhttp://plutolove233.github.io/categories/golang%E5%B0%8F%E6%8A%80%E5%B7%A7/Hugohttp://plutolove233.github.io/categories/hugo/Sun, 12 Nov 2023 15:39:14 +0800yizhigopherhttp://plutolove233.github.io/categories/hugo/机器学习之路http://plutolove233.github.io/categories/%E6%9C%BA%E5%99%A8%E5%AD%A6%E4%B9%A0%E4%B9%8B%E8%B7%AF/Fri, 10 Nov 2023 20:48:46 +0800yizhigopherhttp://plutolove233.github.io/categories/%E6%9C%BA%E5%99%A8%E5%AD%A6%E4%B9%A0%E4%B9%8B%E8%B7%AF/ \ No newline at end of file +Categories - Category - yizhigopher的博客http://plutolove233.github.io/categories/Categories - Category - yizhigopher的博客Hugo -- gohugo.iozh-CNyizhigopher@163.com (yizhigopher)yizhigopher@163.com (yizhigopher)Sun, 10 Nov 2024 20:11:24 +0800vscode插件配置设置http://plutolove233.github.io/categories/vscode%E6%8F%92%E4%BB%B6%E9%85%8D%E7%BD%AE%E8%AE%BE%E7%BD%AE/Sun, 10 Nov 2024 20:11:24 +0800yizhigopherhttp://plutolove233.github.io/categories/vscode%E6%8F%92%E4%BB%B6%E9%85%8D%E7%BD%AE%E8%AE%BE%E7%BD%AE/golang之路http://plutolove233.github.io/categories/golang%E4%B9%8B%E8%B7%AF/Fri, 08 Nov 2024 22:39:44 +0800yizhigopherhttp://plutolove233.github.io/categories/golang%E4%B9%8B%E8%B7%AF/Dockerhttp://plutolove233.github.io/categories/docker/Tue, 29 Oct 2024 19:44:43 +0800yizhigopherhttp://plutolove233.github.io/categories/docker/cudahttp://plutolove233.github.io/categories/cuda/Thu, 14 Dec 2023 15:11:01 +0800yizhigopherhttp://plutolove233.github.io/categories/cuda/golang小技巧http://plutolove233.github.io/categories/golang%E5%B0%8F%E6%8A%80%E5%B7%A7/Mon, 04 Dec 2023 19:13:11 +0800yizhigopherhttp://plutolove233.github.io/categories/golang%E5%B0%8F%E6%8A%80%E5%B7%A7/Hugohttp://plutolove233.github.io/categories/hugo/Sun, 12 Nov 2023 15:39:14 +0800yizhigopherhttp://plutolove233.github.io/categories/hugo/机器学习之路http://plutolove233.github.io/categories/%E6%9C%BA%E5%99%A8%E5%AD%A6%E4%B9%A0%E4%B9%8B%E8%B7%AF/Fri, 10 Nov 2023 20:48:46 +0800yizhigopherhttp://plutolove233.github.io/categories/%E6%9C%BA%E5%99%A8%E5%AD%A6%E4%B9%A0%E4%B9%8B%E8%B7%AF/ \ No newline at end of file diff --git "a/categories/vscode\346\217\222\344\273\266\351\205\215\347\275\256\350\256\276\347\275\256/index.html" "b/categories/vscode\346\217\222\344\273\266\351\205\215\347\275\256\350\256\276\347\275\256/index.html" index 63bfc10..7aeab0b 100644 --- "a/categories/vscode\346\217\222\344\273\266\351\205\215\347\275\256\350\256\276\347\275\256/index.html" +++ "b/categories/vscode\346\217\222\344\273\266\351\205\215\347\275\256\350\256\276\347\275\256/index.html" @@ -8,6 +8,7 @@ Cancel文章标签分类 -

 vscode插件配置设置

2024

 vscode插件配置设置

2024

\ No newline at end of file diff --git "a/categories/vscode\346\217\222\344\273\266\351\205\215\347\275\256\350\256\276\347\275\256/index.xml" "b/categories/vscode\346\217\222\344\273\266\351\205\215\347\275\256\350\256\276\347\275\256/index.xml" index b7d12bf..cc684dd 100644 --- "a/categories/vscode\346\217\222\344\273\266\351\205\215\347\275\256\350\256\276\347\275\256/index.xml" +++ "b/categories/vscode\346\217\222\344\273\266\351\205\215\347\275\256\350\256\276\347\275\256/index.xml" @@ -1 +1 @@ -vscode插件配置设置 - Category - yizhigopher的博客http://plutolove233.github.io/categories/vscode%E6%8F%92%E4%BB%B6%E9%85%8D%E7%BD%AE%E8%AE%BE%E7%BD%AE/vscode插件配置设置 - Category - yizhigopher的博客Hugo -- gohugo.iozh-CNyizhigopher@163.com (yizhigopher)yizhigopher@163.com (yizhigopher)Mon, 22 Jan 2024 14:48:37 +0800Todotree插件设置http://plutolove233.github.io/todotree/Mon, 22 Jan 2024 14:48:37 +0800yizhigopherhttp://plutolove233.github.io/todotree/1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 \ No newline at end of file +vscode插件配置设置 - Category - yizhigopher的博客http://plutolove233.github.io/categories/vscode%E6%8F%92%E4%BB%B6%E9%85%8D%E7%BD%AE%E8%AE%BE%E7%BD%AE/vscode插件配置设置 - Category - yizhigopher的博客Hugo -- gohugo.iozh-CNyizhigopher@163.com (yizhigopher)yizhigopher@163.com (yizhigopher)Sun, 10 Nov 2024 20:11:24 +0800Todotree插件设置http://plutolove233.github.io/todotree/Mon, 22 Jan 2024 14:48:37 +0800yizhigopherhttp://plutolove233.github.io/todotree/在插件中找到Todo Tree,点击设置按钮 找到在settings.json中编辑更多 将上述代码复制到settings.json中即可。 1 2 3vscode Go环境设置http://plutolove233.github.io/go/Sun, 10 Nov 2024 20:11:24 +0800yizhigopherhttp://plutolove233.github.io/go/首先你需要按照这个教程 安装好Go环境。 之后在vscode插件商城中,搜寻Go,选择第一个进行安装。 安装好插件后,按下Ctrl+Shift+P \ No newline at end of file diff --git a/go-basic2/index.html b/go-basic2/index.html index b0a6bfb..9554375 100644 --- a/go-basic2/index.html +++ b/go-basic2/index.html @@ -1,5 +1,5 @@ Go指针、函数、结构体、接口以及方法 - yizhigopher的博客 -
+
\ No newline at end of file diff --git a/go/index.html b/go/index.html new file mode 100644 index 0000000..b4d35df --- /dev/null +++ b/go/index.html @@ -0,0 +1,18 @@ +vscode Go环境设置 - yizhigopher的博客 +

Contents

vscode Go环境设置

Contents +

首先你需要按照这个教程 +安装好Go环境。

之后在vscode插件商城中,搜寻Go,选择第一个进行安装。

./install.png

安装好插件后,按下Ctrl+Shift+P快捷键,输入go tool,并安装所有工具。

./tool.png +./tools2.png

观察在${GOPATH}/bin目录下是否存在这些工具的可执行文件。

./ok.png

重启vscode,即可。

+
\ No newline at end of file diff --git a/go/index.md b/go/index.md new file mode 100644 index 0000000..97db2d9 --- /dev/null +++ b/go/index.md @@ -0,0 +1,19 @@ +# vscode Go环境设置 + + +首先你需要按照[这个教程](https://plutolove233.github.io/go-install)安装好Go环境。 + +之后在vscode插件商城中,搜寻`Go`,选择第一个进行安装。 + +![](./install.png) + +安装好插件后,按下`Ctrl+Shift+P`快捷键,输入go tool,并安装所有工具。 + +![](./tool.png) +![](./tools2.png) + +观察在`${GOPATH}/bin`目录下是否存在这些工具的可执行文件。 + +![](./ok.png) + +重启vscode,即可。 diff --git a/go/install.png b/go/install.png new file mode 100644 index 0000000..07b1d13 Binary files /dev/null and b/go/install.png differ diff --git a/go/ok.png b/go/ok.png new file mode 100644 index 0000000..7223362 Binary files /dev/null and b/go/ok.png differ diff --git a/go/tool.png b/go/tool.png new file mode 100644 index 0000000..02ddd7f Binary files /dev/null and b/go/tool.png differ diff --git a/go/tools2.png b/go/tools2.png new file mode 100644 index 0000000..51655e3 Binary files /dev/null and b/go/tools2.png differ diff --git a/index.html b/index.html index cbaaba8..7191e30 100644 --- a/index.html +++ b/index.html @@ -15,10 +15,10 @@

Todotree插件设置

 published on  
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70
Read More

Go指针、函数、结构体、接口以及方法

 published on  
在插件中找到Todo Tree,点击设置按钮 找到在settings.json中编辑更多 将上述代码复制到settings.json中即可。 1 2 3
Read More

vscode Go环境设置

 published on  
首先你需要按照这个教程 安装好Go环境。 之后在vscode插件商城中,搜寻Go,选择第一个进行安装。 安装好插件后,按下Ctrl+Shift+P
Read More

Go指针、函数、结构体、接口以及方法

 published on  
指针 和C语言一样,Go中也存在指针,一般采用如下方式来获取变量的地址: 1 2 a := 12 var ptr *int = &a 如果我们想要获取该指针变量指向地址对应的内容时,我
Read More

Go基本运算符、流程控制、数组与切片以及字典

 published on  
上一章节我们已经简单介绍了一个最简单的go程序以及关于变量以及常量的定义。本章节将会继续极少go的基础知识。 基本运算符 go的基础运算符包含 数
Read More

Hello Go!

 published on  
在完成了上个章节的Go编译器安装后,本章节将介绍Go的基础语法,并通过几个例子进行展示。 Hello World 学习每个语言的第一步永远是学会如何在控制台打印😊
Read More

如何实现多个个容器之间的通讯

 published on  
为了进行多个容器之间通讯的实验,首先我们需要创建两个服务,分别打包成不同的镜像,并通过docker-compose进行容器编排。本次实验的项
Read More

Go语言环境安装

 published on  
在本节中,我们将首先简单介绍下如何安装Go语言环境,以开启Go语言学习之路!我们将简单介绍在Windows环境,Linux环境以及Mac环境
Read More
published on  Docker
为了进行多个容器之间通讯的实验,首先我们需要创建两个服务,分别打包成不同的镜像,并通过docker-compose进行容器编排。本次实验的项
Read More
 dockergo
\ No newline at end of file diff --git a/index.json b/index.json index 2bca863..cb545e3 100644 --- a/index.json +++ b/index.json @@ -1 +1 @@ -[{"categories":["vscode插件配置设置"],"content":" \"todo-tree.regex.regex\": \"((%|#|//|\u003c!--|^\\\\s*\\\\*)\\\\s*($TAGS)|^\\\\s*- \\\\[ \\\\])\", \"todo-tree.regex.regexCaseSensitive\": false, \"todo-tree.general.tags\": [ \"BUG\", \"HACK\", \"FIXME\", \"TODO\", \"todo\", \"bug\", \"tag\", \"done\", \"mark\", \"test\", \"update\", \"[]\", \"[x]\" ], \"todo-tree.highlights.defaultHighlight\": { \"background\": \"#ff9100\", \"foreground\": \"#ffffff\", \"type\": \"text\", \"icon\": \"bug\" }, \"todo-tree.highlights.customHighlight\": { //todo 需要做的功能 \"todo\": { \"icon\": \"alert\", //标签样式 \"background\": \"#c9c552\", //背景色 \"rulerColour\": \"#c9c552\", //外框颜色 \"iconColour\": \"#c9c552\", //标签颜色 }, //bug 必须要修复的BUG \"bug\": { \"background\": \"#ff3737\", \"icon\": \"bug\", \"rulerColour\": \"#eb5c5c\", \"iconColour\": \"#eb5c5c\", }, //tag 标签 \"tag\": { \"background\": \"#38b2f4\", \"icon\": \"tag\", \"rulerColour\": \"#38b2f4\", \"iconColour\": \"#38b2f4\", \"rulerLane\": \"full\" }, //done 已完成 \"done\": { \"background\": \"#328050\", \"icon\": \"check\", \"rulerColour\": \"#5eec95\", \"iconColour\": \"#5eec95\", }, //mark 标记一下 \"mark\": { \"background\": \"#f90\", \"icon\": \"note\", \"rulerColour\": \"#f90\", \"iconColour\": \"#f90\", }, //test 测试代码 \"test\": { \"background\": \"#df7be6\", \"icon\": \"flame\", \"rulerColour\": \"#df7be6\", \"iconColour\": \"#df7be6\", }, //update 优化升级点 \"update\": { \"background\": \"#b14e75\", \"icon\": \"versions\", \"rulerColour\": \"#d65d8e\", \"iconColour\": \"#d65d8e\", }, }, 将上述代码复制到settings.json中即可。 ","date":"2024-01-22","objectID":"/todotree/:0:0","tags":["vscode","extension"],"title":"Todotree插件设置","uri":"/todotree/"},{"categories":["golang之路"],"content":"指针 和C语言一样,Go中也存在指针,一般采用如下方式来获取变量的地址: a := 12 var ptr *int = \u0026a 如果我们想要获取该指针变量指向地址对应的内容时,我们可采用如下方式: fmt.Println(*ptr) *操作符和\u0026操作符分别代表了取值操作和取地址操作。 对于引用类型的变量,在Go中需要注意不仅需要声明,而且需要为其分配内存空间,不然就是对一个空指针进行操作了,具体的例子如下: func main(){ var a *int *a = 100 } 为了修改上述代码,我们可以添加如下操作: func main() { var a *int a = new(int) *a = 100 } new(T)函数能够返回传入类型T的内存地址,并且内存中对应的值为类型的零值。 ","date":"2024-11-08","objectID":"/go-basic2/:1:0","tags":["golang","study"],"title":"Go指针、函数、结构体、接口以及方法","uri":"/go-basic2/"},{"categories":["golang之路"],"content":"函数 函数的定义方式如下: func 函数名(参数) (返回值) { 函数体 } 其中需要说明的是: 参数由参数变量和参数变量的类型组成,多个参数之间使用,分隔。 如果相邻的参数类型相同,例如a int, b int可以简写为a, b int 如果传入参数数量不确定,可用...T来表示可变参数。例如func (a string, x ...int) 固定参数和可变参数搭配时,可变参数得放在固定参数后面。 可变参数可以是为一个数组 返回值由返回值变量和其变量类型组成,也可以只写返回值的类型,多个返回值必须用()包裹,并用,分隔。 如果返回值部分是由返回值变量和变量类型组成的话,函数体内部可以直接使用这个返回值,并在最后仅使用return来表示返回函数结果 我们定义一个a+b的函数作为示例: func intSum(a int, b int) res int { res = a + b return } int main() { b := intSum(1, 2) // 调用函数 } ","date":"2024-11-08","objectID":"/go-basic2/:2:0","tags":["golang","study"],"title":"Go指针、函数、结构体、接口以及方法","uri":"/go-basic2/"},{"categories":["golang之路"],"content":"函数类型 在go中,函数作为一等公民,因此可以作为变量进行传输。例如: func add(x, y int) int { return x + y } func minus(x, y int) int { return x - y } func calc(x, y int, op func(int, int)int) int { return op(x, y) } func main() { res := calc(1, 2, add) println(res) } 但是在上述实现中,op的函数签名过长,实际应用中并不现实,而且不能突出这个变量的意义,因此我们可以为这个类型定义一个类型: type IntOperation func(int,int)int 可以理解成为func(int,int)int这个函数签名定义新的名字,当然,我们也可以为基本类型定义新名字: type MyInt int 通过上述方法,我们就可以简化代码为如下形式: type IntOperation func(int,int)int func add(x, y int) int { return x + y } func minus(x, y int) int { return x - y } func calc(x, y int, op IntOperation) int { return op(x, y) } func main() { res := calc(1, 2, add) println(res) } ","date":"2024-11-08","objectID":"/go-basic2/:2:1","tags":["golang","study"],"title":"Go指针、函数、结构体、接口以及方法","uri":"/go-basic2/"},{"categories":["golang之路"],"content":"匿名函数和闭包 函数同样可以作为返回值,在go中不能在函数内部定义函数,因此采用匿名函数来实现,例如: func main() { add := func(x, y int) int { fmt.Println(x+y) } add(1, 2) func (x, y int) int { fmt.Println(x+y) }(1, 2) } 当函数的返回值是一个函数,并且该函数还包含相关引用环境时,我们就称其为闭包,例如: func addr() func(int) int { x := 10 return func(y int) int { x += y return x } } func main() { f := addr() print(f(10)) // 20 print(f(20)) // 40 } ","date":"2024-11-08","objectID":"/go-basic2/:2:2","tags":["golang","study"],"title":"Go指针、函数、结构体、接口以及方法","uri":"/go-basic2/"},{"categories":["golang之路"],"content":"defer语句 defer语句会将其后面跟随的语句进行延迟处理,在defer归属的函数即将返回时,将延迟处理的语句按defer定义的逆序进行执行,也就是说,先被defer的语句最后被执行,最后被defer的语句,最先被执行,例如: func deferDemo() { println(\"Hello\") defer println(\"defer 1\") defer println(\"defer 2\") println(\"World\") } /* Hello World defer 2 defer 1 */ 其执行原理如下图所示: 由于defer语句的这一特性,我们可以在defer后面执行例如资源清理、错误处理、解锁等操作。 ","date":"2024-11-08","objectID":"/go-basic2/:2:3","tags":["golang","study"],"title":"Go指针、函数、结构体、接口以及方法","uri":"/go-basic2/"},{"categories":["golang之路"],"content":"结构体 go中结构体的定义方式如下: type StructName struct { a1 int b1 string ... } 其中: StructName结构体名在同一个包内仅能有一个,不能重复 a1,b1称为字段名,在每个结构体内唯一,不能重复 例如: type person struct { name string age int sex int } 当我们声明了一个结构体后,如何实例化: var p person 或 p := new(person) 或 p := \u0026person{} 后面两种得到的是结构体的指针,但是对于结构体而言,并不需要像C++中需要(*p).name=\"John\"来修改属性,只需要p.name=John就行。 既然变量有匿名变量,结构体也会有匿名结构体: var user = struct {name string, age int}{ \"yizhigopher\", 12, } 上面这段代码不仅展示了匿名结构体的用法,还展示了结构体初始化的一种方式,这种方式通过直接按着结构体属性的顺序赋值即可(需要注意的是,这种方法必须对所有属性都初始化)。 接下来展示其他初始化的方法: var p person p.name = \"yizhigopher\" // 或 p := person{ name: \"yizhigopher\", age: 22, } // 或 p := \u0026person{ name: \"yizhigopher\" } 在go中没有构造函数的说法,需要自己实现构造函数: func newPerson() *person { return \u0026peroson{ name: \"yizhigopher\", age: 12, } } Go中的结构体继承通过匿名结构体来实现,具体如下: //Animal 动物 type Animal struct { name string } //Dog 狗 type Dog struct { Feet int8 *Animal //通过嵌套匿名结构体实现继承 } 其中关于匿名结构体需要注意的几点有: 当访问结构体成员时,会先在结构体中查找该字段,找不到再去嵌套的匿名字段中查找。 嵌套结构体内部可能存在相同的字段名。在这种情况下为了避免歧义需要通过指定具体的内嵌结构体字段名。 除此之外,结构体还有一个究极大杀器,称为结构体标签(Tag)。Tag是结构体的元信息,可以在运行的时候通过反射的机制读取出来。 Tag在结构体字段的后方定义,由一对反引号包裹起来,具体的格式如下: `key1:\"value1\" key2:\"value2\"` 需要注意的是: 结构体tag由一个或多个键值对组成。键与值使用冒号分隔,值用双引号括起来。同一个结构体字段可以设置多个键值对tag,不同的键值对之间使用空格分隔。 为结构体编写Tag时,必须严格遵守键值对的规则=,一旦格式写错,编译和运行时都不会提示任何错误,通过反射也无法正确取值。例如不要在key和value之间添加空格。 我们举个例子来展示tag的能力: //Student 学生 type Student struct { ID int `json:\"id\"` //通过指定tag实现json序列化该字段时的key Gender string //json序列化是默认使用字段名作为key name string //私有不能被json包访问 } func main() { s1 := Student{ ID: 1, Gender: \"男\", name: \"yizhigopher\", } data, err := json.Marshal(s1) if err != nil { fmt.Println(\"json marshal failed!\") return } fmt.Printf(\"json str:%s\\n\", data) //json str:{\"id\":1,\"Gender\":\"男\"} } ","date":"2024-11-08","objectID":"/go-basic2/:3:0","tags":["golang","study"],"title":"Go指针、函数、结构体、接口以及方法","uri":"/go-basic2/"},{"categories":["golang之路"],"content":"方法 go语言中,方法与OOP中的成员方法类似,是一种作用于特定类型变量的函数。这种特定类型变量叫做接收者(Receiver)。接收者的概念就类似于其他语言中的this或者self,具体定义的格式如下: func (接受者变量 接受者类型) 方法名(参数列表) (返回参数) { 函数体 } 可以发现,方法和函数之间在表达形式上的区别,就是在函数名前面增加了一段用于申明接受者类型的语句。需要注意的是: 接收者中的参数变量名在命名时,建议使用接收者类型名称首字母的小写,而不是self、this之类的命名。例如,Person类型的接收者变量应该命名为 p,Connector类型的接收者变量应该命名为c等。 接收者类型和参数类似,可以是指针类型和非指针类型。 非本地类型不能定义方法,也就是说我们不能给别的包的类型定义方法。 在Go语言中,接收者的类型可以是任何类型,不仅仅是结构体,任何类型都可以拥有方法。举个例子,我们基于内置的int类型使用type关键字可以定义新的自定义类型,然后为我们的自定义类型添加方法。 type MyInt int func (m MyInt) SayHello() { fmt.Println(\"Hello, 我是一个int。\") } 当接受者类型是指针等引用类型时,方法内对于接受者的修改是直接作用于接受者的。但是如果接受者类型是指类型,那么是作用于接受者copy后的对象。可以通过下属例子体现其中的区别: func (p *Person) SetAge(newAge int8) { p.age = newAge } func (p Person) SetAge2(newAge int8) { p.age = newAge } 我们应当在以下这些情况,使用指针类型接受者: 需要修改接收者中的值 接收者是拷贝代价比较大的大对象 保证一致性,如果有某个方法使用了指针接收者,那么其他的方法也应该使用指针接收者。 ","date":"2024-11-08","objectID":"/go-basic2/:4:0","tags":["golang","study"],"title":"Go指针、函数、结构体、接口以及方法","uri":"/go-basic2/"},{"categories":["golang之路"],"content":"推荐阅读: Go结构体的内存布局 通过八个demo搞明白Go语言defer的五大特性 Go Tag标签详解 ","date":"2024-11-08","objectID":"/go-basic2/:5:0","tags":["golang","study"],"title":"Go指针、函数、结构体、接口以及方法","uri":"/go-basic2/"},{"categories":["golang之路"],"content":"上一章节我们已经简单介绍了一个最简单的go程序以及关于变量以及常量的定义。本章节将会继续极少go的基础知识。 ","date":"2024-11-08","objectID":"/go-basic/:0:0","tags":["golang","study"],"title":"Go基本运算符、流程控制、数组与切片以及字典","uri":"/go-basic/"},{"categories":["golang之路"],"content":"基本运算符 go的基础运算符包含 数值计算:加+,减-,乘*,除/,求余% 逻辑计算:与\u0026\u0026,或||,非! 位运算符号:\u0026,|,^,\u003e\u003e,\u003c\u003c 关系运算符:相等==,不等于!=,大于\u003e,大于等于\u003e=,小于\u003c,小于等于\u003c= ","date":"2024-11-08","objectID":"/go-basic/:1:0","tags":["golang","study"],"title":"Go基本运算符、流程控制、数组与切片以及字典","uri":"/go-basic/"},{"categories":["golang之路"],"content":"流程控制 ","date":"2024-11-08","objectID":"/go-basic/:2:0","tags":["golang","study"],"title":"Go基本运算符、流程控制、数组与切片以及字典","uri":"/go-basic/"},{"categories":["golang之路"],"content":"if-else分支结构 if-else分支判断结构如下所示: if 判断1 { 语句1 } else if 判断2{ 语句2 } else { 语句3 } 需要注意的是: 花括号不能换行,必须和if在同一行内 花括号是不可以省略的,即使语句1中只有1个语句 在go中if-else还有一种特殊且常用的写法: func ifDemo() { if score := 65; score \u003e= 90 { fmt.Println(\"A\") } else if score \u003e 75 { fmt.Println(\"B\") } else { fmt.Println(\"C\") } } 我们可以在if表达式之前添加一个执行语句,再根据变量值进行判断,并且这个变量的作用域仅在这段if-else语句中。 ","date":"2024-11-08","objectID":"/go-basic/:2:1","tags":["golang","study"],"title":"Go基本运算符、流程控制、数组与切片以及字典","uri":"/go-basic/"},{"categories":["golang之路"],"content":"循环结构 go中所有的循环均适用for来实现,不会有while以及do-until结构。 for循环的基本结构如下 for 初始语句;终止语句;每步更新内容 { ... } 其中初始语句、终止语句以及每步更新内容都不是必填项。 例如: for i := 0; i\u003c 10; i++ { print(i) } // ----------------------- i := 0 for i \u003c 10 { print(i) i++ } // ----------------------- i := 0 for { if i \u003e= 10 { break } print(i) i += 1 } 以上三种写法均是等价的。 此外还有for-range遍历结构: for k,v := range variable { } 其中variable可以是map,string,slice,array等类型,k表示索引,v表示索引对应的值。 在go1.22版本中对循环结构增加了几个特性。 首先是增加了对整数range的新特性,上述代码也可以这么写: for i := range 10 { print(i) } 此外去除了for-range不在共享循环变量,在go1.16和go1.22两个版本中分别运行一下代码,执行结果会有所不同。 list := []int{1, 2, 3, 4, 5} for _, val := range list { go func() { print(val) }() } 旧版本中for循环迭代器的变量是一个单一变量,在每个循环迭代中仅是取值不同。这样做在性能上非常高效,但如果使用不当,会导致意想不到的行为,可能会造成共享循环变量的问题。而go1.22中,for循环的每次迭代都会创建新变量,每次循环自己迭代自己的变量,以避免意外共享错误。关于这方面的知识可以阅读这篇博客 。 ","date":"2024-11-08","objectID":"/go-basic/:2:2","tags":["golang","study"],"title":"Go基本运算符、流程控制、数组与切片以及字典","uri":"/go-basic/"},{"categories":["golang之路"],"content":"数组与切片 ","date":"2024-11-08","objectID":"/go-basic/:3:0","tags":["golang","study"],"title":"Go基本运算符、流程控制、数组与切片以及字典","uri":"/go-basic/"},{"categories":["golang之路"],"content":"数组(Array) 数组是同一种数据类型元素的集合。在Go语言中,数组从声明时就确定,使用时可以修改数组成员,但是数组大小不可变化。示例如下: var a [3]int // {0, 0, 0} var cities [3]string{\"Shanghai\", \"Beijing\"} // {\"Shanghai\", \"Beijing\", \"\"} var deduce [...]int{1, 2, 3} // 数组大小就是3了 var matrix [2][3]int{ {1, 2, 3}, {4, 5, 6}, } 可以通过如下方式实现数组的遍历: for i := 0; i \u003c len(cities); i++ { print(cities[i]) } for _, val := range cities { print(val) } 需要注意的是,如果在函数中传入一个数组类型的变量,那么仅会copy值到另一个变量中,而不是传递一个引用。 func modifyArray(x [3]int) { x[0] = 100 } func modifyArray2(x [3][2]int) { x[2][0] = 100 } func main() { a := [3]int{10, 20, 30} modifyArray(a) //在modify中修改的是a的副本x fmt.Println(a) //[10 20 30] b := [3][2]int{ {1, 1}, {1, 1}, {1, 1}, } modifyArray2(b) //在modify中修改的是b的副本x fmt.Println(b) //[[1 1] [1 1] [1 1]] } [n]T表示指针数组,[n]T表示数组指针 。 ","date":"2024-11-08","objectID":"/go-basic/:3:1","tags":["golang","study"],"title":"Go基本运算符、流程控制、数组与切片以及字典","uri":"/go-basic/"},{"categories":["golang之路"],"content":"切片(Array) 由于数组长度不可变,我们也希望go中能有个像vector的类型,能够支持可变长度。 切片(Slice)是一个拥有相同类型元素的可变长度的序列。它是基于数组类型做的一层封装。它非常灵活,支持自动扩容。 切片是一个引用类型,它的内部结构包含地址、长度和容量。切片一般用于快速地操作一块数据集合。申明切片变量如下: var x []T var a []int{1, 2, 3} var slice = make([]int, 3, 10) // 定义了len==3,cap==10的切片 make函数仅用来初始化slice, map以及chan三种类型的变量 切片拥有自己的长度和容量,我们可以通过使用内置的len()函数求长度,使用内置的cap()函数求切片的容量。 我们也可以在数组上,创建一个切片 var x [5]int{1, 2, 3, 4, 5} b := x[3:5] // 左闭右开。 切片的本质就是对底层数组的封装,它包含了三个信息:底层数组的指针、切片的长度(len)和切片的容量(cap)。因此判断切片是否为空,应当如下: if len(s) == 0 { } 而不是s==nil,如果这么比较的话,实际上是在判断指针是否为空。例如: var s1 []int //len(s1)=0;cap(s1)=0;s1==nil s2 := []int{} //len(s2)=0;cap(s2)=0;s2!=nil s3 := make([]int, 0) //len(s3)=0;cap(s3)=0;s3!=nil 由由于切片是指向底层数据的指针,因此如果copy,那么对copy后的变量进行修改,原先的变量也会发生改变,举个例子: package main import \"fmt\" func sliceFunc(s []int, val int) { s[0] = val } func main() { var list = [5]int{1, 2, 3, 4, 5} s1 := list[0:3] s2 := list[:] s3 := s2 sliceFunc(s1, 6) fmt.Println(list) //[6 2 3 4 5] for _, val := range s1 { print(val, \" \") } println() //6 2 3 for _, val := range s2 { print(val, \" \") } println() // 6 2 3 4 5 for _, val := range s3 { print(val, \" \") } println() // 6 2 3 4 5 } 因此,当我们使用切片时,最好遵循所有权的唯一性原则(这在Rust中被纳入语法范围中),即对于内存中的某个数据,同一时刻一个值只能有一个所有者。 既然切片的长度是可变的,那么该如何增加切片的元素呢: func main() { //append()添加元素和切片扩容 var numSlice []int for i := 0; i \u003c 10; i++ { numSlice = append(numSlice, i) fmt.Printf(\"%v len:%d cap:%d ptr:%p\\n\", numSlice, len(numSlice), cap(numSlice), numSlice) } } 使用内置的append函数即可。 需要注意的是,每个切片会指向一个底层数组,这个数组的容量够用就添加新增元素。当底层数组不能容纳新增的元素时,切片就会自动按照一定的策略进行“扩容”,此时该切片指向的底层数组就会更换。“扩容”操作往往发生在append()函数调用时,所以我们通常都需要用原变量接收append函数的返回值。 关于每次扩容对cap修改的策略,可以参考${GOROOT}/src/runtime/slice.go的源码,这里就不过多展开。 ","date":"2024-11-08","objectID":"/go-basic/:3:2","tags":["golang","study"],"title":"Go基本运算符、流程控制、数组与切片以及字典","uri":"/go-basic/"},{"categories":["golang之路"],"content":"字典(map) map是一种无序的基于key-value的数据结构,Go语言中的map是引用类型,必须初始化才能使用。 var stuMap map[string]int stuMap = make(map[string]int) stuMap[\"Tom\"] = 24 exampleMap := map[string]string{ \"code\": \"23\", \"message\": \"Query success!\", } 思考下[]map[string]int和map[string][]int之间的区别 如何判断某个键是否存在: _, ok := m[key] if !ok{ println(\"key is not existed!\") } 可以通过如下方法删去map中的某个键: delete(m, key) ","date":"2024-11-08","objectID":"/go-basic/:4:0","tags":["golang","study"],"title":"Go基本运算符、流程控制、数组与切片以及字典","uri":"/go-basic/"},{"categories":["golang之路"],"content":"思考 package main import \"fmt\" func main() { type Map map[string][]int m := make(Map) s := []int{1, 2} s = append(s, 3) fmt.Printf(\"%+v\\n\", s) m[\"q1mi\"] = s s = append(s[:1], s[2:]...) fmt.Printf(\"%+v\\n\", s) fmt.Printf(\"%+v\\n\", m[\"q1mi\"]) } 尝试写出上面这段代码的结果,从而深刻理解本章节所阐述的内容。 ","date":"2024-11-08","objectID":"/go-basic/:5:0","tags":["golang","study"],"title":"Go基本运算符、流程控制、数组与切片以及字典","uri":"/go-basic/"},{"categories":["golang之路"],"content":"在完成了上个章节的Go编译器安装后,本章节将介绍Go的基础语法,并通过几个例子进行展示。 Hello World 学习每个语言的第一步永远是学会如何在控制台打印😊,现在让我们看一下go如何实现Hello World: // main.go package main import \"fmt\" func main(){ fmt.Println(\"Hello World!\") } 在本文件的目录下,在控制台中执行go build main.go \u0026\u0026 main或go run main.go观察控制台输出的结果。 我们逐行分析说明: 第1行使用package main申明这是一个主包,我们需要在主包下面定义func main()函数,用于声明程序入口。 第3行使用import来导入外部包\"fmt\",该包通常用于实现格式化的I/O操作,这个与C的\u003ccstdio\u003e相似。 第5行使用func main()来申明编译入口以及程序执行的开始位置。 第6行使用fmt.Println(\"Hello World!\")向控制台输出Hello World! 通过上述例子,我们介绍下go语言一个程序所需要的三个部分: 包名。每个go程序都需要申明该程序所属的包,在本章节中,我们所有代码的包名均为main,而对于go的包管理方式,我们将在后续的章节中进行介绍。 导入外部包。想要实现一段代码,不可避免需要调用已经封装好的程序,因此通过import来导入一些必要的外部包,例如本例子中的fmt,以及一些常用的bytes,net等,这些均是已经封装好的标准库。这些标准库的位置在${GOROOT}/src中 函数。go语言函数申明格式为func functionName(args)并用花括号包裹函数体。需要注意的是,不像C++可以将花括号换行,go语言中花括号的换行是不允许换行的,并使用tab进行代码缩进。 接下来我们将介绍go的基本语法 GO基本语法 ","date":"2024-10-30","objectID":"/go-hello/:0:0","tags":["golang","study"],"title":"Hello Go!","uri":"/go-hello/"},{"categories":["golang之路"],"content":"变量和常量 ","date":"2024-10-30","objectID":"/go-hello/:1:0","tags":["golang","study"],"title":"Hello Go!","uri":"/go-hello/"},{"categories":["golang之路"],"content":"变量 变量定义如下方式如下: var a int var a int = 10 var a = 10 a := 10 上述为4种不同表达形式的变量申明方式,其中a为变量名,int为变量类型,后两种方式中,go能够自动推理出各个变量的类型。 go的变量名可以用26个英文字母,_以及数字组合而成 数字不能作为变量的起始字符 此外go中存在一些关键字,其具备特殊含义,包含一下25个,这些单词不能作为变量名: break default func interface select case defer go map struct chan else goto package switch const fallthrough if range type continue for import return var 此外还存在一些保留字,这些同样不建议作为变量名: Constants: true false iota nil Types: int int8 int16 int32 int64 uint uint8 uint16 uint32 uint64 uintptr float32 float64 complex128 complex64 bool byte rune string error Functions: make len cap new append copy close delete complex real imag panic recover 如果需要定义多个变量可以如此声明: var ( a int = 19 b bool = true ) 或 var ( a = 10 b = false ) ","date":"2024-10-30","objectID":"/go-hello/:1:1","tags":["golang","study"],"title":"Hello Go!","uri":"/go-hello/"},{"categories":["golang之路"],"content":"常量定义 与变量定义类似,可以如此定义 const a int = 10 const a = 10 // notice the type of a is 'untyped int' const ( a = 10 b string = \"12a\" ) 需要注意的是,常量的批量定义中,如果没有确定常量的类型,go并不会直接推到出常量的类型,而是untyped ...,关于这个问题可以查看官方文档 ,对于untyped常量会导致的问题,可以查看疑惑: Go const 导致程序结果错乱? ","date":"2024-10-30","objectID":"/go-hello/:1:2","tags":["golang","study"],"title":"Hello Go!","uri":"/go-hello/"},{"categories":["golang之路"],"content":"多重赋值 这个特点有点像js中的多重赋值,我们可以这么做 x, y, z := 1, 2, 3 这时,x的值为1,y的值就为2,…… 你可以随处使用该方法来实现多重赋值,例如可以用在常量定义中 ","date":"2024-10-30","objectID":"/go-hello/:1:3","tags":["golang","study"],"title":"Hello Go!","uri":"/go-hello/"},{"categories":["golang之路"],"content":"匿名变量 go中会使用_来表示一个仅用于接收,但不能使用的变量,常用于当我们想要忽略多重赋值中的某个值时。例如: x, _, z := 1, 2, 3 ","date":"2024-10-30","objectID":"/go-hello/:1:4","tags":["golang","study"],"title":"Hello Go!","uri":"/go-hello/"},{"categories":["golang之路"],"content":"iota iota是go语言的常量计数器,只能在常量的表达式中使用。 iota在const关键字出现时将被重置为0。const中每新增一行常量声明将使iota计数一次。使用iota能简化定义,在定义枚举时很有用。例如: const ( a = iota // a = 0 b // b = 1 c // c = 2 ) 或 const ( _ = iota KB = 1\u003c\u003c(10*iota) MB GB ) 可以发现,后续的常量推导,均是依据离这条语句最近的iota表达来推导的。 代码风格 在go的代码中,变量命名的风格通常具有一个约定俗成的规则,这里我将总结一下我遇到的一些规则 对于常量名,一般全部大写,并用_隔开,例如:MAX_CONNECTION。 对于变量名以及函数名,一般采用驼峰风格,例如isHashed。如果需要将这个函数或变量设为public,则将首字母也大写。 当然变量的定义最好能够直观表现其意义,最好别用拼音、无意义的一个字符串来定义一个变量。 ","date":"2024-10-30","objectID":"/go-hello/:1:5","tags":["golang","study"],"title":"Hello Go!","uri":"/go-hello/"},{"categories":["Docker"],"content":"为了进行多个容器之间通讯的实验,首先我们需要创建两个服务,分别打包成不同的镜像,并通过docker-compose进行容器编排。本次实验的项目目录如下: ├── client │ ├── dockerfile │ ├── index.html │ └── nginx.conf ├── docker-compose.yml ├── microservice1 │ ├── dockerfile │ ├── main │ └── main.go └── microservice2 ├── dockerfile ├── main └── main.go microservice后端服务定义 我们定义了两个后端服务,其代码如下: // microservice1/main.go package main import ( \"encoding/json\" \"net/http\" ) type Resp struct { Code int Message string } func main() { http.HandleFunc(\"/say_hello\", func(w http.ResponseWriter, r *http.Request) { w.Header().Set(\"Access-Control-Allow-Origin\", \"*\") //允许访问所有域 w.Header().Add(\"Access-Control-Allow-Headers\", \"Content-Type\") //header的类型 w.Header().Set(\"content-type\", \"application/json\") var data Resp if r.Method != \"GET\" { data = Resp{ Code: 4000, Message: \"Only Allow GET Method!\", } } else { data = Resp{ Code: 2000, Message: \"Hello Microservice1\", } } resp, _ := json.Marshal(data) w.Write(resp) }) http.ListenAndServe(\":8080\", nil) } 编译上述代码: GOOS=linux GOARCH=amd64 go build main.go 另一个后端服务代码如下: // microservice2/main.go package main import ( \"encoding/json\" \"net/http\" ) func main() { http.HandleFunc(\"/who_am_i\", func(w http.ResponseWriter, r *http.Request) { w.Header().Set(\"Access-Control-Allow-Origin\", \"*\") //允许访问所有域 w.Header().Add(\"Access-Control-Allow-Headers\", \"Content-Type\") //header的类型 w.Header().Set(\"content-type\", \"application/json\") data := struct{ Code int Message string }{ Code: 2000, Message: \"I am yizhigopher!\", } resp, _ := json.Marshal(data) w.Write(resp) }) http.ListenAndServe(\":8080\", nil) } 使用上述命令编译代码。 并编写容器build脚本,每个后端服务的dockerfile一样: FROM golang:1.23-alpine WORKDIR /app COPY ./main /app/microservice EXPOSE 8080 ENTRYPOINT [\"/app/microservice\"] 前端界面定义 为了直观体现出不同容器之间通讯作用,我们编写了如下前端代码: \u003c!-- client/index.html --\u003e \u003c!DOCTYPE html\u003e \u003chtml lang=\"CH-ZN\"\u003e \u003chead\u003e \u003cmeta charset=\"UTF-8\"\u003e \u003cmeta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\"\u003e \u003cmeta http-equiv=\"X-UA-Compatible\" content=\"ie=edge\"\u003e \u003cscript src=\"https://code.jquery.com/jquery-3.7.1.min.js\"\u003e\u003c/script\u003e \u003c/head\u003e \u003cbody\u003e \u003cbutton onclick=\"say_hello()\" style=\"width:100px; height:20px\"\u003ecall say hello\u003c/button\u003e \u003cbr/\u003e \u003cbutton onclick=\"whoami()\" style=\"width:100px; height:20px\"\u003ecall who am i\u003c/button\u003e \u003c/body\u003e \u003c/html\u003e \u003cscript\u003e function say_hello() { $.ajax({ url: \"http://localhost/api/v1/say_hello\", type: \"GET\", dataType: \"json\", async: true, success: function(res) { console.log(res) }, error: function(param) { console.log(\"fail, error=\", param) } }) } function whoami() { $.ajax({ url: \"http://localhost/api/v2/who_am_i\", type: \"GET\", dataType: \"json\", async: true, success: function(res) { console.log(res) }, error: function(param) { console.log(\"fail, error=\", param) } }) } \u003c/script\u003e 需要注意的是,其中每个接口请求的url为:http://localhost/api/v1/say_hello和http://localhost/api/v2/who_am_i。并编写nginx.conf配置代理: user nginx; worker_processes auto; error_log /var/log/nginx/error.log warn; pid /var/run/nginx.pid; events { worker_connections 1024; } http { include /etc/nginx/mime.types; default_type application/octet-stream; log_format main '$remote_addr - $remote_user [$time_local] \"$request\" ' '$status $body_bytes_sent \"$http_referer\" ' '\"$http_user_agent\" \"$http_x_forwarded_for\"'; access_log /var/log/nginx/access.log main; sendfile on; keepalive_timeout 65; # Server block for handling requests server { listen 80; server_name localhost; # 处理静态文件的请求(如果有) location / { root /fontend; index index.html; } location ^~/api/v1/ { proxy_pass http://router_one:8080/; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; } location ^~/api/v2/ { proxy_pass http://router_two:8080/; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; } } } 我们注意到,代理的转发路径分别是http://router_one:8080/和http://router_two:8080/,其中不是IP地址,而是容器名。 容器编排以及通信测试 编写docker-compose.yml编排内容,配置如下: version: '3' services: font_end: image: fontend:v1 build: ./client contai","date":"2024-10-29","objectID":"/docker-connection/:0:0","tags":["docker","go"],"title":"如何实现多个个容器之间的通讯","uri":"/docker-connection/"},{"categories":["golang之路"],"content":"在本节中,我们将首先简单介绍下如何安装Go语言环境,以开启Go语言学习之路!我们将简单介绍在Windows环境,Linux环境以及Mac环境下如何安装,尽可能全面的覆盖不同读者所使用的操作系统。 Go语言下载地址 Windows安装 下载windows环境下的zip文件,解压到系统的某个目录中,在我的系统中,我将其解压到本地D盘目录D:\\Program Files\\Go\\go下。 之后在系统的环境变量中将该目录下的bin目录添加到环境变量中,如图所示: 保存后通过打开终端,输入go version即可判断是否添加到环境变量中 之后需申明GOROOT、GOPATH,并将GOPATH目录下的bin目录添加到环境变量中,便于后续调用go install指令安装的命令。 Linux安装 下载图1中的源码包,删除 /usr/local/go 文件夹(如果存在),删除之前安装的 Go,然后将刚刚下载的压缩包解压缩到/usr/local,在 /usr/local/go 中创建一个全新的 GOROOT rm -rf /usr/local/go \u0026\u0026 tar -C /usr/local -xzf go1.23.2.linux-amd64.tar.gz 在PATH环境变量中添加/usr/local/go/bin,在$HOME/.profile或/etc/profile(用于全系统安装)中添加以下一行即可: export GOROOT=/usr/local/go export GOPATH=/data/go export PATH=$GOPATH/bin:$GOROOT/bin:$PATH 之后使用命令source $HOME/.profile激活环境变量配置即可。 Mac安装 安装图1中的.pkg安装文件即可 执行以下命令: cd ~ vim .bash_profile 编辑用户环境变量: export GOPATH=/Users/XXX/go #本地创建的gopath绝对路径 export PATH=$PATH:$GOPATH/bin 激活配置文件source ~/.bash_profile即可。 额外的注意点: 需要注意的是,GOPATH目录下需要创建三个文件夹,分别是src、pkg以及bin,其中bin目录保存go install安装的指令,pkg保存第三方包的源码,src用于在GO111MODULE=off时,在该路径下编写包含第三方包的程序(现在基本不在src下写代码了) 之后我们需要覆写go的几个配置: go env -w GO111MODULE=on go env -w GOPROXY=https://goproxy.cn,direct GO111MODULE是Go语言用于管理依赖关系的一种机制,它可以帮助开发者更好地管理项目的依赖包,并且可以确保项目在不同环境下的一致性 GOPROXY表示Go安装第三方包的代理(因为国内众所周知的原因),便于后续直接go get安装第三方包 至此,go的安装已经完成了 ","date":"2024-10-08","objectID":"/go-install/:0:0","tags":["golang","study"],"title":"Go语言环境安装","uri":"/go-install/"},{"categories":null,"content":"归一化流基本内容 什么是标准化流:基于一系列可逆变换,生成样本对应的分布 标准化流的基本原理: 变量替换:$p_x(x)=p_z(f_\\theta(x))|\\det J_{f_\\theta}(x)|$,我们可以理解为通过$z=f_\\theta(x)$,将原先复杂分布,变换成简单易懂的分布形式,其中我们要求$f_\\theta$必须是可逆变换。标准化流的目的就是求得该可逆变换。 在标准化流中,往往会使用多个这样的可逆变换,即$f_\\theta(x)=f_1(x)\\circ f_2(x)…\\circ f_K(x)$ 雅各比行列式求解: $$ \\det J_{f_\\theta}(x)=\\det \\prod_{i=1}^K J_{f_i}(x)=\\prod_{i=1}^K\\det J_{f_i}(x) $$ 利用对数函数,我们可以得到: $$ \\log p_x(x)=\\log p_z(f_{\\theta}(x))+\\sum_{i=1}^K\\log |det J_{f_i}(x)| $$ 我们需要最大化这个概率密度 ","date":"2024-01-06","objectID":"/nf-introduce/:0:1","tags":null,"title":"Normalizing Flow介绍","uri":"/nf-introduce/"},{"categories":null,"content":"论文《A deep generative model for probabilistic energy forecasting in power systems: normalizing flows》的应用: 在该论文中,作者采用标准化流的方式实现了负荷预测功能。作者利用$\\bm x$表示d-1天的负荷数据,用$\\bm c$表示天气信息,希望能够得到符合符合数据分布的分布$p_\\theta(\\bm x|\\bm c)$,从而可以利用该分布得到预测数据$^{\\bm x}$。 在利用归一化流方式对泰迪比赛数据进行负荷预测的尝试中,会进行如下操作: 提取时间数据和天气数据,重新整合原本数据格式,得到归一化流的模型输入格式 其中每一行为某个时刻的负荷数据y和影响负荷的因素(时间因素、天气因素)x。根据日期重新排列数据x,将每个时刻影响负荷的因素横向排列并组合。此时x重新排列为一个$day\\times 960$的矩阵,表现如下: ","date":"2024-01-06","objectID":"/nf-introduce/:0:2","tags":null,"title":"Normalizing Flow介绍","uri":"/nf-introduce/"},{"categories":["cuda"],"content":"创建实例 在阿里云平台上创建实例: 其中我们镜像选择为公共的Ubuntu20.04镜像,当然你也可以选择其他配置好的镜像从而简化之后的步骤。 我们之后会演示如何在一个空白的系统中,安装python、cuda以及pytorch的环境 配置好以上信息之后,我们就能获得一个实例,点击实例操作里的打开即可连接远程主机,之后我们会在这个实力上安装一些新的东西,配置我们需要的环境。 ","date":"2023-12-14","objectID":"/install/:0:1","tags":["cuda","安装教程"],"title":"阿里云PAI平台搭建pytorch-cuda11.4环境","uri":"/install/"},{"categories":["cuda"],"content":"安装基本环境 更新apt源 apt-get update 安装python 十分简单: apt-get install python3 apt-get install python3-venv apt-get install python3-pip 安装完成之后,可以执行 python3 --version 安装cuda所需环境 控制台敲入nvidia-smi获得GPU信息,可以我们所需cuda的版本是11.4 安装libxml2 apt-get install libxml2 配置好时区信息即可 安装gcc环境: apt-get install gcc-7 g++-7 配置gcc版本优先级,不然cuda无法识别到安装好的gcc update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-7 70 --slave /usr/bin/g++ g++ /usr/bin/g++-7 --slave /usr/bin/gcov gcov /usr/bin/gcov-7 update-alternatives --config gcc 敲入gcc --version出现如下信息即可: 安装wget和vim apt-get install wget apt-get install vim 安装cuda 到nvidia官网上找到对应cuda驱动下载信息:如图选择即可 在当前目录下创建一个cuda-installer目录,进入该目录,执行installer中的命令: wget https://developer.download.nvidia.com/compute/cuda/11.4.0/local_installers/cuda_11.4.0_470.42.01_linux.run sh cuda_11.4.0_470.42.01_linux.run 敲入accpet同意协议,在之后的配置中选择如下配置: 安装即可,同时按照给出的提示: 修改/etc/profile文件,设置环境变量。使用vim /etc/profile在文末添加如下信息: export CUDA_HOME=/usr/local/cuda-11.4 export LD_LIBRARY_PATH=/usr/local/cuda-11.4/lib64:$LD_LIBRARY_PATH export PATH=/usr/local/cuda-11.4/bin:$PATH 重起实例,执行nvcc -V出现: 即可。 至此cuda安装完毕 安装Torch 去官网上,找到对应版本执行一下命令即可 pip install torch==1.12.0+cu113 torchvision==0.13.0+cu113 torchaudio==0.12.0 --extra-index-url https://download.pytorch.org/whl/cu113 ","date":"2023-12-14","objectID":"/install/:0:2","tags":["cuda","安装教程"],"title":"阿里云PAI平台搭建pytorch-cuda11.4环境","uri":"/install/"},{"categories":["golang小技巧"],"content":"简介 我们能在go代码中添加一段注释,用来表示一种编译约束 //go:build [tags] 通过这个并且配合go build -tags=\"...\"的方式,就能实现带有约束的编译。 举个例子: 我们编写一下代码:主程序申明如下 //main.go package main var configArr []string func main(){ for _, v := range configArr { print(v, \" \") } } 同时,我们会编写另外两个用于初始化的代码,分别为dev和test //go:build dev package main func init() { configArr = append(configArr, \"dev\") } //go:build test package main func init() { configArr = append(configArr, \"test\") } 我们可以看到,在两个同一包下,我们定义了两个不同的初始化功能,分别为configArr添加相应的内容。 当我们采用不同方式编译代码时,会看到如下情况: 能够看到编译时携带的tag不同,结果也就不同。 我们可以很直观的想到可以通过这种方法,实现不同模式下的编译 ","date":"2023-12-04","objectID":"/tips-1/:0:1","tags":["golang","tips"],"title":"go语言小技巧--编译约束","uri":"/tips-1/"},{"categories":["golang小技巧"],"content":"环境设置 当然我们也可以通过添加//go:build linux的方式对本段代码进行编译环境的限制。 例如我们对上述代码的go:build注释进行修改,可以看到如下效果: //go:build linux package main func init() { configArr = append(configArr, \"dev\") } //go:build windows package main func init() { configArr = append(configArr, \"test\") } 这时,我们直接进行代码编译,能够看到如下效果: 可以看到,我们只将test部分的代码编译,而dev的代码并没有编译 ","date":"2023-12-04","objectID":"/tips-1/:0:2","tags":["golang","tips"],"title":"go语言小技巧--编译约束","uri":"/tips-1/"},{"categories":["golang小技巧"],"content":"格式 举个例子:// go:build linux \u0026\u0026 amd64 \u0026\u0026 !cgo || darwin,通过这个我们就能发现,可以通过逻辑运算符来表明编译约束。 ","date":"2023-12-04","objectID":"/tips-1/:0:3","tags":["golang","tips"],"title":"go语言小技巧--编译约束","uri":"/tips-1/"},{"categories":["Docker"],"content":"如何运行容器 在之前的几篇文章中,我们学习了如何使用docker的指令以及如何进行容器打包,接下来我们就需要了解,该如何运行容器。 最简单的方式: 使用docker run或docker start指令来运行镜像或容器 但是这种方式在面对需要进行容器的端口映射、镜像卷以及容器间的网络通信时,会比较麻烦,不易操作。 同时,如果某个容器额外需要依赖另一个容器的存在,即容器之间存在关联的关系时,容器的启动顺序就比较重要了。举个简单的例子,我们手头有个web服务容器,这个容器需要依赖另一个MySQL容器的存在,这时,我们就需要先在web服务容器运行前,先运行MySQL容器。也许你会认为这种情况下,容器的先后顺序并不会对你造成困扰,但是如果容器数量多了怎么办,容器运行的相关参数特别多怎么办。亦或者经过了较长时间,你忘记了这个依赖关系怎么办? 这时候我们就需要有一个工具来管理容器运行的相关参数以及依赖关系。 ","date":"2023-11-30","objectID":"/docker-run/:0:1","tags":null,"title":"容器运行方式","uri":"/docker-run/"},{"categories":["Docker"],"content":"docker-compose工具 如何安装 Windows环境下,因为docker-desktop自带docker-compose,所以不需要关注如何安装 Linux环境下,访问docker-compose的下载地址Releases · docker/compose (github.com) ,选择适合系统的以及相应架构的可运行程序。运行sudo chmod +x docker-compose赋予其可运行能力,让后将该可执行文件放入环境变量的目录下即可 编写docker-compose.yml文件 当我们已经将容器打包完成之后,我们可以编写docker-compose.yml文件,从而能够运行容器。 我们举个简单的例子:在这个例子中,我们只需要申明其镜像卷的挂载即可 verison: '3' services: cliffwalking: image: demo:v1 container_name: reinforce_learning volumes: - /home/yizhigopher/cliffwalk/ouput:/cliffwalking/output - /home/yizhigopher/cliffwalk/model:/cliffwalking/model 我们只需要在当前docker-compose.yml的目录下,运行docker-compose up就能够运行一个名为reinforce_learning的容器,并且将容器内部的output和model文件夹的内容和容器外部的对应的文件夹相关联. 当然,docker-compose能够识别的关键字不止以上这些内容,我们可以通过访问Docker Compose overview | Docker Docs 以及菜鸟教程Docker Compose | 菜鸟教程 (runoob.com) 获得相关的更多信息.以上只是一个简单的示例,希望能够通过这些能够让读者一窥docker-compose容器编排的高效以及优越性 ","date":"2023-11-30","objectID":"/docker-run/:0:2","tags":null,"title":"容器运行方式","uri":"/docker-run/"},{"categories":["Docker"],"content":"结语 至此,docker的相关知识基本分享完毕,事实上,仅仅通过短短的三个博文是不能说明白docker的,也不可能就仅仅读这些内容就能掌握docker.这三篇博文,仅仅能够让大家对docker有个基本的了解,以及基本操作的熟悉.想要熟练掌握docker,还需要经过自己大量的实践练习和操作才能真正熟悉. ","date":"2023-11-30","objectID":"/docker-run/:0:3","tags":null,"title":"容器运行方式","uri":"/docker-run/"},{"categories":["Docker"],"content":"基本操作 在上节,我们已经安装好docker,但是就如同写代码一样,我们安装好了相关环境,但是并没有代码能够让我们运行,我们又不会写代码(自己构建镜像),那该怎么办。我们可以到github上面寻找一个相关的代码,拉取到本地,然后运行即可。 所以我们现在首先要做的事应该是学会如何拉取镜像到本地。 docker pull 完整的docker pull命令应该是:docker pull ${IMAGE_NAME}:${IMAGE_TAG} 它会从docker的官方镜像仓库中拉取对应的镜像。其中${IMAGE_TAG}部分如果不写,会默认拉取最新的镜像信息。 例如,我们现在运行docker pull hello-world命令,我们看看会发生什么: 我们能够看到,使用了最新的版本(tag),镜像的sha256验证信息,以及从哪里拉取的镜像。所以之后,如果我们需要某种镜像,我们就可以直接pull以下就可以了。 可以通过访问docker hub 上,搜索你想要的基础镜像的信息即可(这一过程需要科学上网),或者使用docker search ${IMAGE_NAME},实在不行利用bing搜索相关信息也是不错的选择 那么我们拉取完了,我们怎么能够确定这个镜像就是在我的电脑上的呢? docker images 我们在控制台输入docker images看看会发生什么: 我们看到,在我们本地存储中,确确实实有一个来自hello-world,TAG是latest的仓库的镜像。 当然你也可以通过docker inspect ${IMAGE_NAME}查看镜像在本地的哪个位置,但是这么做完全没有必要。 我们已经能够确定确确实实有个镜像在本地,那么我们该如何运行这个镜像呢 docker run 完整的docker run指令如下:docker run [OPTIONS] ${IMAGE_NAME} [COMMAND] [ARG...],这样之后会生成一个容器container 其中options常用的是: -v:绑定容器卷,后面紧跟映射关系,具体而言就是将本地文件和容器内的文件相关联。需要注意的是,两处的文件路径用:隔开,前面是本地文件的绝对路径,后面是容器内的绝对路径。例如:docker run -v /home/yizhigopher/result:/project/result xxx:v1 -d:后台运行容器,使用这个option后,我们就不会看到容器运行的结果,而只能看到一个容器ID -p:端口映射。有些时候,我们需要访问容器内的一些服务,例如MySQL容器,我们需要在本地就能连接容器中的MySQL服务,我们可以这么写:docker run -d -p 8080:3306 mysql,这样我们就是将容器内的3306端口映射到本地的8080端口,我们在本地就可以通过访问8080端口获得MySQL服务。 -i:以交互模式运行容器,通常与 -t 同时使用 -t:为容器重新分配一个伪输入终端,通常与 -i 同时使用 上述就是我们一般常用的OPTIONS信息,我们掌握了这些,基本可以应付大部分情况。如果需要其他的操作可以查阅官方文档,或者baidu。 举个例子:在终端执行docker run -it golang:alpine会发生什么: 需要注意的是我们这个命令相当于启动一个容器并进入容器内部。可以观察到,我们终端的位置发生了变化,从root@yizhigopher进入到了容器内部的/go目录下,由此也能看出docker的容器也可看成是一个小型的操作系统,在带有bash工具的容器内部,也可以执行一些简单的终端命令,例如ls、cd等等 既然我们已经学会了如何拉取镜像,如何启动一个容器,那么我们可以学学如何生成一个镜像。 ","date":"2023-11-14","objectID":"/docker-cmd/:0:1","tags":null,"title":"docker常用命令以及镜像打包","uri":"/docker-cmd/"},{"categories":["Docker"],"content":"dockerfile 要实现自己编制一个镜像,我们就得学会dockerfile文件的编写规则。 当然,由于篇幅,这里仅仅提供一些简单的dockerfile文件编写的介绍,学会了这些也就够了,更多详细的说明都可以查看官方文档。 FROM ${IMAGE_NAME} 每个dockerfile都得以FROM作为开始,我们自己构建镜像,需要在一个镜像的基础上加以创作。就如同我们平时编写代码一样,我们不可能从0101二进制串来编写代码,我们会借助编译器等基础环境来高效编写程序。 FROM ${IMAGE_NAME}的意义十分简单,你可以简答地理解为docker pull ${IMAGE_NAME},我们之后的一切操作都是在这个镜像内部执行的 WORKDIR ${FILE_NAME} 这个是自选的,WORKDIR相当于申明一个工作目录,当你的容器运行时,会默认在这个目录下运行。如果这个目录不存在,会自动帮你创建这个目录 COPY ${FILE_PATH} ${CONTAINER_FILE_PATH} 这个步骤会将本地主机下的${FILE_PATH}的内容拷贝到容器内部${CONTAINER_FILE_PATH}目录下。我们需要注意的是,如果你想拷贝一个文件,那么你需要指明这个文件在容器内部的名字,即:COPY ./requirement.txt /project/req.txt。如果你拷贝的是文件夹,那么你同样需要指明这个文件在容器内部的名称。 联系一下Linux系统下的mv指令,会有许多相同之处 需要注意的是,有一个ADD指令和COPY指令十分相似,执行的功能也大致相同,但是ADD会自动将压缩文件进行解压,而MOVE不行 RUN ${COMMAND} 这个命令会在搭建docker镜像时执行,它会执行${COMMAND}命令,通常我们用这条指令来为镜像内容进行修改。例如,我们为python代码进行打包时,会将python源代码COPY到镜像内部,然后会执行RUN pip install -r requirements.txt来安装相关包从而能够运行python代码 CMD ${COMMAND} 这个部分的命令会在容器启动时执行,需要注意的有几点 你可以写多个CMD,但是只有最后一个CMD会执行 CMD的指令会被docker run附带的运行指令覆盖 COMMND的书写格式应写成字符数组的形式,例如ls -a需写成[\"ls\", \"-a\"] ENTRYPOINT ${COMMAND} 这个指令和CMD指令十分相似,但是有一点不同,ENTRYPOINT不会被docker run后的运行指令覆盖,而是会将docker run后面的参数传递给ENTRYPOINT。具体的示例可看这个博文Docker从入门到精通——CMD与ENTRYPOINT区别 。这里就不再赘述。 ok我们大概介绍完了dockerfile需要了解的地方,现在我们来自己编写个dockerfile试试看吧。随便挑一个文件夹,将所需要的文件内容拷贝到这里,然后在这个文件夹下新建一个dockerfile文件(名字就是dockerfile),文件内容如下: 然后在这个文件夹下打开控制台,输入:docker buuild -t cliffwalk:v1 . docker build表示执行构建镜像 -t表示构建的镜像名和镜像tag为cliffwalk:v1 最后的 . 表示使用当前目录下的dockerfile 会出现: 当指令结束后,我们使用docker images就可以看到构建好的镜像信息 由于我们最后使用的是CMD来表示容器运行的指令,所以,我们可以通过docker run的方式来覆盖这条指令来进入容器内部,我们能够看到在/cliffwalking目录下,有我们COPY进去的文件内容: 至此,我们完成了docker的基本命令和镜像打包的学习,最后一章我们会介绍如何高效的启动运行容器镜像 ","date":"2023-11-14","objectID":"/docker-cmd/:0:2","tags":null,"title":"docker常用命令以及镜像打包","uri":"/docker-cmd/"},{"categories":["Docker"],"content":"什么是docker docker可以说是一门技术、一种软件,它能够帮助我们将程序进行打包,从而方便后续的项目部署以及维护。 ","date":"2023-11-14","objectID":"/docker-introduce/:0:1","tags":["docker"],"title":"docker介绍以及安装步骤说明","uri":"/docker-introduce/"},{"categories":["Docker"],"content":"为什么要用docker 场景:做项目开发的同学经常会遇到一个问题,就是这个项目能够在本地上运行,但是换一台电脑却会报各种各样的错误,这个时候不得不面对环境配置这一极其麻烦的问题 所以我们说,docker将过去的程序和系统环境的绑定关系给打破了,只要我们的电脑安装了docker,我就可以通过拉取云端的镜像,无需关注环境的搭建就可以实现跨平台的应用部署。我们不再需要面对繁杂的应用上云出现的环境搭建问题,就可以轻松地使用服务。 ","date":"2023-11-14","objectID":"/docker-introduce/:0:2","tags":["docker"],"title":"docker介绍以及安装步骤说明","uri":"/docker-introduce/"},{"categories":["Docker"],"content":"怎样安装docker 安装docker: Windows系统: 在Windows系统,我们需要确保自己的电脑安装了WSL 以管理员身份打开CMD,并敲入: wsl --install 官方文档:安装 WSL | Microsoft Learn ,可查阅官方文档获得更多的信息。 之后我们前往docker-desktop官方网站Docker Desktop: The #1 Containerization Tool for Developers | Docker 下载最新的docker-desktop即可。 需要注意的是,在使用安装好的docker-desktop之前,我们需要修改几个配置 调整docker虚拟机的位置,不然根据初始设置,会把C盘占满 修改docker的镜像源信息,不然在docker pull拉取镜像时,会特别慢: 将其中的内容替换成下述信息: { \"builder\": { \"gc\": { \"defaultKeepStorage\": \"20GB\", \"enabled\": true } }, \"experimental\": false, \"features\": { \"buildkit\": true }, \"registry-mirrors\": [ \"http://hub-mirror.c.163.com\", \"https://mirror.ccs.tencentyun.com\" ] } 或者直接修改C:\\Users\\{{ USERNAME }}\\.docker\\daemon.json内容为上述信息,重启docker。 判断是否安装成功,在CMD中,敲入: docker version 出现如下信息即可: docker之所以需要在Linux环境下使用,是源于在使用go进行编写docker时,需要借助Linux系统的Namespace和Cgroup隔离机制,所以我们在Windows上运行docker实质上是在Windows上安装了一个虚拟机WSL,在虚拟机的内部执行docker Ubuntu系统: 更新索引:sudo apt-get update 安装:sudo apt install docker.io 修改镜像源:在/etc/docker目录下创建daemon.json,在里面添加: { \"registry-mirrors\": [ \"http://hub-mirror.c.163.com\", \"https://mirror.ccs.tencentyun.com\" ] } 即可 判断是否安装成功,在终端输入:docker --version,出现 即可 CentOS/aliyun服务器: 卸载旧版(如果你之前没有安装过,可跳过这一步) yum remove docker \\ docker-client \\ docker-client-latest \\ docker-common \\ docker-latest \\ docker-latest-logrotate \\ docker-logrotate \\ docker-engine 安装依赖包:yum install -y yum-utils 为了防止是从国外的源下载,所以配置阿里云地址:yum-config-manager --add-repo http://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo 更新依赖:yum update 安装docker:yum install docker-ce docker-ce-cli containerd.io 配置docker镜像源:同样的在/etc/docker目录下创建daemon.json文件,填入镜像源地址信息即可。 启动docker服务:systemctl start docker 判断是否安装完成:控制台输入docker info,看到镜像源信息更改即为安装成功 当然你可以也可以在其他平台上使用docker info来判断镜像源的地址信息是否成功修改 ","date":"2023-11-14","objectID":"/docker-introduce/:0:3","tags":["docker"],"title":"docker介绍以及安装步骤说明","uri":"/docker-introduce/"},{"categories":["Docker"],"content":"一些最基本的概念 仓库Registry:你可以理解为类似代码仓库的东西,只是这里面存放的不是一段段代码,而是一堆镜像,我们可以通过一些指令来获得或上传镜像信息。 镜像Image:它是一种模板,具有创建docker容器的指令。并且一种镜像是得基于另一种镜像才能构建。我们可以把这个理解为虚拟机安装所需的iso文件,只要我有这个iso文件,我就可以打造多个一样的虚拟机环境。当然我们还可以在这个iso的基础上附带一些东西,构成一个新的iso。例如centos分为minimal(最基础的)以及desktop(带有桌面UI的),desktop就是基于minimal,同时又添加了新的东西形成的新的iso文件。 容器Container:它是一种运行实例,每个容器之间是相互隔离互不影响的。同样的,我们举个例子,当你拥有一个iso文件时,你不把它放在VMware中产生一个虚拟机实例,那么这个iso文件也就毫无用处。而你使用同一个iso文件创建的不同虚拟机之间也是不会有任何影响,你不会发现,在A虚拟机中创建了一个文件,B虚拟机中也可以找到。 如果上述的例子还是不能让你理解这三者之间的关系和实际的作用,我们可以拿日常开发做一个例子。仓库Registry就是github.com;镜像就是github.com中的pytorch项目代码,我们可以利用pytorch本身的东西创建一个新的例如predict的项目代码,当然我们也可以上传到github;容器就相当于我们执行这段代码的进程,每个进程之间不会相互影响(不考虑使用共享内存,管道等方式实现进程间的控制)。 ","date":"2023-11-14","objectID":"/docker-introduce/:0:4","tags":["docker"],"title":"docker介绍以及安装步骤说明","uri":"/docker-introduce/"},{"categories":["Docker"],"content":"总结 在这一章中,我们大概介绍了docker的一些基本信息和安装方式,在下一章我们会在ubuntu系统上介绍docker的基本操作。 ","date":"2023-11-14","objectID":"/docker-introduce/:0:5","tags":["docker"],"title":"docker介绍以及安装步骤说明","uri":"/docker-introduce/"},{"categories":["Hugo"],"content":"为什么要用Action来实现自动化部署hugo项目 不采用自动化部署,每次更新项目时,都需要在本地编译,然后上传整个public目录,比较麻烦 不采用自动化部署,没办法管理博客内容以及维护 ","date":"2023-11-12","objectID":"/workflow/:0:1","tags":["github","自动化部署"],"title":"如何利用GitHub的Action实现hugo项目自动化部署","uri":"/workflow/"},{"categories":["Hugo"],"content":"怎样实现 在github上创建 {{ username }}.github.io仓库,在main分支的基础上,新建source分支用于存放hugo的markdown文件,而main分支用于存放编译好后的文件 前期准备: 在本地,使用ssh工具创建公私钥 ssh-keygen -t rsa -N '' -f ./deploy-key -q 在本地我们会得到deploy-key和deploy-key.pub两个文件 现在需要将ssh密钥信息设置到仓库中: 添加deploy-key信息 deploy key的title随便填写即可,我们将生成的deploy-key.pub(即公钥)的内容填到key中 设置action的密钥: 新建一个actions secret 其中Name的内容填为ACTIONS_DEPLOY_KEY,Secret的内容填为我们生成的deploy-key(即私钥)的内容。 在source分支中添加.github/workflows/main.yml文件,用于定义Github Actions,其中main.yaml文件内容为 name: auto-update-page # 定义action名称 on: push: branches: - source # 当source分支出现push行为时,执行这个action jobs: deploy: runs-on: ubuntu-22.04 # 定义环境以及之后的行为 steps: - uses: actions/checkout@v4 with: submodules: true # Fetch Hugo themes (true OR recursive) fetch-depth: 0 # Fetch all history for .GitInfo and .Lastmod - name: Setup Hugo uses: peaceiris/actions-hugo@v2 with: hugo-version: \"0.120.3\" extended: true - name: Build run: hugo --minify - name: Deploy uses: peaceiris/actions-gh-pages@v3 with: deploy_key: ${{ secrets.ACTIONS_DEPLOY_KEY }} publish_dir: ./public external_repository: plutolove233/plutolove233.github.io publish_branch: main # 将编译好的public里面的内容部署到对应仓库的分支上 设置完之后,我们查看仓库的Actions,我们能够看到: 看到如上内容,就意味着我们自动化部署的工作流完成了,我们可以尝试修改部分markdown文件,上传之后我们可以看到我们的博客内容也会发生改变 ","date":"2023-11-12","objectID":"/workflow/:0:2","tags":["github","自动化部署"],"title":"如何利用GitHub的Action实现hugo项目自动化部署","uri":"/workflow/"},{"categories":["机器学习之路"],"content":"问题描述 给定一个4×12的网格环境,如下图所示,其中黄色区域表示悬崖,我们不能经过,蓝色是我们的目标区域,我们希望能求出每个状态如何利用最少的步骤到达目的点。 ","date":"2023-11-10","objectID":"/cliffwalking/:0:1","tags":["pytorch","DQN","RL"],"title":"DQN算法示例:CliffWalking问题","uri":"/cliffwalking/"},{"categories":["机器学习之路"],"content":"编写工具类代码 # rl_utils.py def one_hot(index, num_size=10): return [1 if i == index else 0 for i in range(num_size)] class ReplayBuffer: def __init__(self, capacity): self.buffer = collections.deque(maxlen=capacity) def add(self, state, action, reward, next_state, done): self.buffer.append((state, action, reward, next_state, done)) def sample(self, batch_size): transitions = random.sample(self.buffer, batch_size) state, action, reward, next_state, done = zip(*transitions) return np.array(state), action, reward, np.array(next_state), done def size(self): return len(self.buffer) def train_off_policy_agent(env, agent, num_episodes, replay_buffer, minimal_size, batch_size): return_list = [] for i in range(10): with tqdm(total=int(num_episodes / 10), desc='Iteration %d' % i) as pbar: for i_episode in range(int(num_episodes / 10)): episode_return = 0 state = env.reset()[0] done = False while not done: action = agent.take_action(state) next_state, reward, done, _, _ = env.step(action) replay_buffer.add(state, action, reward, next_state, done) state = next_state episode_return += reward if replay_buffer.size() \u003e minimal_size: b_s, b_a, b_r, b_ns, b_d = replay_buffer.sample(batch_size) transition_dict = {'states': b_s, 'actions': b_a, 'next_states': b_ns, 'rewards': b_r, 'dones': b_d} agent.update(transition_dict) return_list.append(episode_return) if (i_episode + 1) % 10 == 0: pbar.set_postfix({'episode': '%d' % (num_episodes / 10 * i + i_episode + 1), 'return': '%.3f' % np.mean(return_list[-10:])}) pbar.update(1) return return_list ","date":"2023-11-10","objectID":"/cliffwalking/:0:2","tags":["pytorch","DQN","RL"],"title":"DQN算法示例:CliffWalking问题","uri":"/cliffwalking/"},{"categories":["机器学习之路"],"content":"制定规则 我们规定,每走一步所得的reward是-1,走进悬崖的reward是-100,到达目标地点的reward是100。 我们编写环境代码如下: # grid_world.py class GridWorld: \"\"\" 悬崖问题的环境建立 \"\"\" def __init__(self, row=4, col=12): self.action_num = 4 self.col = col self.row = row self.x = row - 1 self.y = 0 self.statue_dim = self.row*self.col self.action_dim = 4 def step(self, action) -\u003e (list, float, bool, bool, dict): action = int(action) # 假设一个3*3的情景,目的是中间方块,求最短距离 self.x = min(self.row - 1, max(0, self.x + change[action][0])) self.y = min(self.col - 1, max(0, self.y + change[action][1])) next_state = self.x * self.col + self.y reward = -1 done = False if self.x == self.row - 1 and self.y \u003e 0: done = True if self.y != self.col - 1: # enter into hole reward = -100 else: # reach the final reward = 100 nextState = np.array(rl.one_hot(next_state, num_size=self.row * self.col)) return nextState, reward, done, False, {} def reset(self) -\u003e (list, dict): self.x = self.row - 1 self.y = 0 label = self.x * self.col + self.y state = np.array(rl.one_hot(label, num_size=self.row * self.col)) return state, {} 在上述代码中,我们实现了环境的定义,同时,我们注意到,我们返回的状态采用了独热编码,这也就意味着,之后我们构建网络时,输入的维度应该是48。 ","date":"2023-11-10","objectID":"/cliffwalking/:0:3","tags":["pytorch","DQN","RL"],"title":"DQN算法示例:CliffWalking问题","uri":"/cliffwalking/"},{"categories":["机器学习之路"],"content":"编写神经网络用来拟合QSA target 我们需要知道,这个网络的输入是48,输出应该是action个数。 # grid_world.py class CliffWalkNet(nn.Module): \"\"\" 悬崖问题神经网络构造 \"\"\" def __init__(self, in_dim, hidden_dim, out_dim): super(CliffWalkNet, self).__init__() self.layer = nn.Sequential( nn.Linear(in_dim, hidden_dim), nn.ReLU(), nn.Linear(hidden_dim, hidden_dim), nn.ReLU(), nn.Linear(hidden_dim, out_dim) ) def forward(self, x): return self.layer(x) 在里面使用三个全连接层,以及两个隐藏层。 ","date":"2023-11-10","objectID":"/cliffwalking/:0:4","tags":["pytorch","DQN","RL"],"title":"DQN算法示例:CliffWalking问题","uri":"/cliffwalking/"},{"categories":["机器学习之路"],"content":"编写DQN核心代码 我们每个动作采用ε贪婪策略,以及规定每次操作执行的更新方法 class CliffWalkDQLearning: def __init__(self, in_dim, hidden_dim, action_dim): self.ActionNumber = action_dim self.Q_net = CliffWalkNet(in_dim, hidden_dim, action_dim).to(device) self.Q_target = CliffWalkNet(in_dim, hidden_dim, action_dim).to(device) self.optimizer = torch.optim.Adam(self.Q_net.parameters(), lr=learning_rate) self.criterion = nn.MSELoss() self.count = 0 self.QSA = [[0, 0, 0, 0] for _ in range(in_dim)] def take_action(self, state): if np.random.random() \u003c epsilon: return np.random.randint(self.ActionNumber) state = torch.tensor(state, dtype=torch.float).to(device) return self.Q_net(state).argmax().item() def update(self, transition: dict): # load data from dictionary states_ = torch.tensor(transition.get(\"states\"), dtype=torch.float).to(device) actions_ = torch.tensor(transition.get(\"actions\")).view(-1, 1).to(device) rewards_ = torch.tensor(transition.get(\"rewards\"), dtype=torch.float).view(-1, 1).to(device) next_states_ = torch.tensor(transition.get(\"next_states\"), dtype=torch.float).to(device) dones_ = torch.tensor(transition.get(\"dones\"), dtype=torch.float).view(-1, 1).to(device) q_values = self.Q_net(states_).gather(1, actions_) # 每列收集对应actions_拟合的value值 max_qvalue = self.Q_target(next_states_).max(1)[0].view(-1, 1) q_target = rewards_ + gamma * max_qvalue * (1-dones_) loss = self.criterion(q_values, q_target) self.optimizer.zero_grad() loss.backward() self.optimizer.step() if self.count % update_times == 0: net_state_dict = self.Q_net.state_dict() self.Q_target.load_state_dict(net_state_dict) self.count += 1 ","date":"2023-11-10","objectID":"/cliffwalking/:0:5","tags":["pytorch","DQN","RL"],"title":"DQN算法示例:CliffWalking问题","uri":"/cliffwalking/"},{"categories":["机器学习之路"],"content":"执行代码,进行模型训练 我们可以直接利用rl_utils里面的(off_policy)工具,进行DQN的训练 env = GridWorld() dim = env.row*env.col agent = CliffWalkDQLearning(in_dim=dim, hidden_dim=int(dim/2), action_dim=4) buffer = rl.ReplayBuffer(1000) random.seed(0) np.random.seed(0) torch.manual_seed(0) return_list = rl.train_off_policy_agent(env, agent, num_episodes, buffer, minimal_size, batch_size) episodes_list = list(range(len(return_list))) plt.plot(episodes_list, return_list) plt.xlabel('Episodes') plt.ylabel('Returns') plt.title('DQN on {}'.format(\"Cliff Walking\")) plt.show() ","date":"2023-11-10","objectID":"/cliffwalking/:0:6","tags":["pytorch","DQN","RL"],"title":"DQN算法示例:CliffWalking问题","uri":"/cliffwalking/"},{"categories":["机器学习之路"],"content":"模型保存 torch.save({\"CliffWalking\": agent.Q_target.state_dict()}, \"./cliff_walking.pth\") # load model model = CliffWalkNet(in_dim=dim, hidden_dim=int(dim/2), out_dim=4).to(device) state_dict = torch.load(\"./cliff_walking.pth\")[\"CliffWalking\"] model.load_state_dict(state_dict) res = [[0, 0, 0, 0] for _ in range(dim)] for i in range(4): for j in range(12): state = rl.one_hot(i*12+j, dim) input_ = torch.tensor(state, dtype=torch.float).to(device) target = model(input_).cpu().detach().numpy().tolist() res[i*12+j] = target # print(\"state(%d,%d):\" % (i, j)) # print(target) # print() print_agent(res, env, action_graph, list(range(37, 47)), [47]) 我们将训练好的模型保存,之后导入model,这样我们就可以避免重复的模型训练,直接计算每个结点的QSA,寻找每个状态的action最大值,从而得出训练后的策略结果。 由上述结果,我们发现每个状态都已经找到了最优的策略,这个模型的结果还算可以。 ","date":"2023-11-10","objectID":"/cliffwalking/:0:7","tags":["pytorch","DQN","RL"],"title":"DQN算法示例:CliffWalking问题","uri":"/cliffwalking/"},{"categories":["机器学习之路"],"content":"基本概念: 对于强化学习,我们一般会分成智能体(agent),环境(通过智能体的状态和动作反馈信息)两大部分,我们现在介绍一些名词,从而有利于之后学习的理解。在这一部分,我们会结合一个3×3的网格寻路来形象化介绍下述概念。 我们最初位置是在(1,1),我们希望能够在最短的时间内到达蓝色网格位置。 State:agent相对于environment的状态。例如以s1表示网格(1,1),s9表示网格(3,3)。所有的状态会构成S(State Set)。 Action:每个状态可能采取的行动。例如向上(a1)、下(a3)、左(a4)、右(a2)走,这所有的动作会构成A(Action Set)。 state transition:agent从s1→s4的过程。 state transition probability:用条件概率的形式,表示转移的情况。例如p(s4|s1, a3)=0.5表示从状态s1执行动作a3(向下)到达状态s4的可能性是0.5(因为还可以向右到达s2的可能)。 Reward:一个数字,用于表示一个state transition行为所产生的激励。 forbidden area:进入但是会产生损失的区域(Reward\u003c0)。例如图示中的黄色区域。 Policy:让agent知道在某个状态该如何行动,也是用条件概率的形式来表示,例如π(a1|s1)=0,π(a2|s1)=0.5。同时我们应该还满足下述条件: $$ \\sum_{a\\in A}\\pi(a|s_j) = 1 $$ Trajectory:是整个状态行为链,例如s1(a2)→s2(a3)→s5(a3)→s8(a2)→s9(a4)→s8… Return:表示整个Trajectory上Reward之和 Discounted Rate:由上可知,一个Trajectory可能是无穷无尽的,所以我们需要定义γ来种折扣Reward,从而使整个过程的Return不会太大。 Discounted Return:表示整个Trajectory的折扣回报总和,具体表示为: $$ Discounted\\ Return=\\sum_{i=1}^\\infin \\gamma^{i-1}r_i $$ 我们可以知道,这个结果最终会收敛到一个值。并且γ越小,那么Discounted Return会与最早的reward相关(更近视),γ越大也就越远视 Episode:一个有限步的Trajectory,并且最终状态时terminal state(目标状态,即图中蓝色方块)。 ","date":"2023-11-10","objectID":"/concept/:0:1","tags":["RL","math"],"title":"强化学习数学知识总结","uri":"/concept/"},{"categories":["机器学习之路"],"content":"马尔可夫过程 随机过程 随机过程研究的对象是,随时间改变的随机现象。例如本例子中,时刻1在状态s1,时刻2可能就会在s2获s4。这一过程是随机的,我们通常用 $$ P(s_{t+1}|a_{t+1},s_{t}, a_{t}, s_{t-1}…a_1, s_0) $$ 来表示下一个时刻,状态的可能性 马尔可夫性质 当前状态只与上一个状态有关时,我们称这个过程具有马尔可夫性质,即 $$ P(s_{t+1}|a_{t+1},s_{t}, a_{t}, s_{t-1}…a_1, s_0)=P(s_{t+1}|a_{t+1}, s_t) $$ 在本例子中,每个时刻到达某个网格的状态可能完全取决于上一时刻所在状态和动作,所以我们这个网格寻路的例子是满足马尔可夫性质的。 几个新的概率 $$ P(s’|s, a)表示s\\stackrel{a}{\\longrightarrow}s’即从s执行动作a到达s’的概率\\\\ P(r |s, a)表示s\\stackrel{a}{\\longrightarrow}s’这一过程获得reward=r的概率\\\\ $$ 新的概念 State Value $$ v_{\\pi}(s)=\\mathbb{E}(G_t|S=s)\\\\ $$ 回报矩阵G:表示当前时刻所有状态所获得的discounted return $$ G_t=R_{t+1}+\\gamma G_{t+1} $$ 贝尔曼公式 我们将上述式子进行展开以及归纳,可得贝尔曼公式: $$ \\forall s\\in S,v_{\\pi}(s)=\\mathbb{E}(G_t|S_t=s)=\\mathbb{E}(R_{t+1}|S_t=s)+\\gamma\\mathbb{E}(G_{t+1}|S_t=s)\\\\ =\\sum_{a\\in A}\\pi(a|s)\\sum_rp(r|s,a)r + \\gamma\\sum_{s’}\\mathbb{E}(G_{t+1}|S_{t+1}=s’,S_t=s)P(s’|s)\\\\ =\\sum_{a\\in A}\\pi(a|s)\\sum_rp(r|s,a)r +\\gamma\\sum_{s’}v_{\\pi}(s’)\\sum_{a\\in A}P(s’|s, a)\\pi(a|s) $$ 我们将这个公式化成矩阵的形式 $$ \\mathbf{v_{\\pi}}=\\mathbf{r_{\\pi}}+\\gamma\\mathbf{P_{\\pi}}\\mathbf{v_{\\pi}} $$ 其中: $$ r_{\\pi}(s)=\\sum_{a\\in A}\\pi(a|s)\\sum_rp(r|s,a)r\\\\ P_{\\pi}(s’|s)=\\sum_{a \\in A}p(s’|s,a)\\pi(a|s),即s\\rarr s’的可能性\\\\ \\mathbf{P_{\\pi(i,j)}} = P_{\\pi}(s_j|s_i) $$ 我们该如何求解——求state value 求矩阵的逆 迭代的方式: 先随机设置v0初始值 利用 $$ \\mathbf{v_{k+1}}=\\mathbf{r_{\\pi}}+\\gamma\\mathbf{P_{\\pi}}\\mathbf{v_{k}} $$ 不断进行迭代,当k→∞时,vk→vπ ","date":"2023-11-10","objectID":"/concept/:0:2","tags":["RL","math"],"title":"强化学习数学知识总结","uri":"/concept/"},{"categories":["机器学习之路"],"content":"安装pytorch 我们以英伟达显卡为例,我们需要知道自己电脑对应cuda版本信息: 在控制台输入nvidia-smi我们可以看到对应cuda版本的信息内容: 从上图中,我们可知:当前我们CUDA的版本是11.2,之后我们去pytorch官方网页 查询对应pytorch的版本信息,按照给出的安装命令信息安装即可。 由于pytorch官网并没有cuda11.2的安装方法,所以我选择安装cuda11.1的环境 pip install torch==1.9.1+cu111 torchvision==0.10.1+cu111 torchaudio==0.9.1 -f https://download.pytorch.org/whl/torch_stable.html 我们可以通过一下代码来判断pytorch是否已经正确安装以及识别到显卡驱动 import torch print(torch.cuda.is_available()) print(torch.cuda.current_device()) print(torch.cuda.device_count()) print(torch.cuda.get_device_name(0)) 执行结果如下: ","date":"2023-11-09","objectID":"/linear/:0:1","tags":["pytorch","mechine learning"],"title":"pytorch实现线性拟合","uri":"/linear/"},{"categories":["机器学习之路"],"content":"编写代码 现在我们可以进行代码编写过程了 构造数据集,我们假设拟合的结果是 $$ y=w_1\\times x_1 + w_2\\times x_2 + b\\\\ 即y=\\begin{bmatrix} w_1 \u0026 w_2 \\\\ \\end{bmatrix} \\begin{bmatrix} x_1\\\\ x_2\\\\ \\end{bmatrix}+b $$ 为了能够体现机器学习拟合的有效性,我们在生成数据点时会增加一些噪声,从而体现出数据的混乱 代码实现: def synthetic_data(w, b, num_examples): \"\"\"生成 y = Xw + b + 噪声。\"\"\" X = torch.normal(0, 1, (num_examples, len(w))) # X是均值为0,方差为1的随机数。有num_examples个样本,列就是len(w) y = torch.matmul(X, w) + b # Y是X与w的乘积(matmul == mm,矩阵相乘)加上偏差b y += torch.normal(0, 0.01, y.shape) # 加入一个噪音,均值为0,方差为0.01,形状与y相同 return X, y.reshape((-1, 1)) # 其中,y返回一个列向量。-1表示自动计算,1表示固定,即列向量为1 true_w = torch.tensor([2, -3.4]) true_b = 4.2 features, labels = synthetic_data(true_w, true_b, 1000) 我们可以绘制图像,用于显示我们构造的数据: 然后我们将数据进行封装成Dataset class LinearDataSet(Dataset): def __init__(self, x, y): self.X = torch.FloatTensor(x) self.Y = torch.FloatTensor(y) def __getitem__(self, index): return self.X[index], self.Y[index] def __len__(self): return len(self.X) trainSet = LinearDataSet(features, labels) trainLoader = DataLoader(trainSet, batch_size=10, shuffle=True, pin_memory=True) 我们现在编写网络,以及编写一个训练过程: class LinearNetWork(nn.Module): def __init__(self, n_feature): super(LinearNetWork, self).__init__() self.layers = nn.Sequential( nn.Linear(n_feature, 1) # 定义一个最简单线性全连接层 ) def forward(self, x): y = self.layers(x) return y def Trainer(train_loader: DataLoader, model: LinearNetWork): criterion = nn.MSELoss() # 定义一个均方误差 optimizer = torch.optim.SGD(model.parameters(), lr=0.03) # 定义一个优化器 for i in range(epoch): model.train() for x, y in train_loader: optimizer.zero_grad() # 采用梯度下降,每次训练都需要将梯度信息清零 x, y = x.to(device), y.to(device) pred = model(x) loss = criterion(pred, y) print(\"loss=\", loss.detach().item()) loss.backward() # 梯度回退 optimizer.step() model = LinearNetWork(2).to(device) # 因为我们总共就两个变量,所以我们传入的特征信息为2 Trainer(trainLoader, model) print(model.state_dict()) 这样我们一个最简单的神经网络已经构成,我们执行上述代码查看网络每层的信息: ","date":"2023-11-09","objectID":"/linear/:0:2","tags":["pytorch","mechine learning"],"title":"pytorch实现线性拟合","uri":"/linear/"},{"categories":["机器学习之路"],"content":"总结 总的来说,这次这个任务还是比较简单的,我们构造一个简单的神经网络,完成了最简单的线性拟合的问题,我们可以在这里面一窥机器学习的基本过程。我们需要获取数据、数据处理、构造网络、进行训练、调整参数,然后不断循环往复,从而得到一个加好的结果。 ","date":"2023-11-09","objectID":"/linear/:0:3","tags":["pytorch","mechine learning"],"title":"pytorch实现线性拟合","uri":"/linear/"},{"categories":null,"content":"个人介绍 出生于2002年6月,喜欢go语言,LOL的蒟蒻。 目前在华北电力大学就读软件工程本科学业。 目前正在学习强化学习,希望能够通过编写个人博客来增加我学习的动力,以及记录我学习的过程。 博客会更新什么 本科以及之后研究生学习的过程和结果 go语言相关的内容,会包括最新特性,以及我觉得有用的东西 docker应用示例,我希望通过几个简短的章节能够让你了解docker如何部署一个项目 或许还会有一些每周心得体会什么的? 我也希望能够有人在阅读我的博客之后,发送email(yizhigopher@163.com)和我交流,希望能够在各位大佬变得更强,无限进步。 ","date":"2023-11-08","objectID":"/about/:0:0","tags":null,"title":"About","uri":"/about/"},{"categories":null,"content":"这是我的第一份博客,用于测试 package main import \"fmt\" func main(){ fmt.Println(\"Hello World\") } $$ \\sum_{i=0}^{100}i = 5050 $$ ","date":"2023-11-08","objectID":"/first_post/:0:0","tags":["hugo"],"title":"First_post","uri":"/first_post/"}] \ No newline at end of file +[{"categories":["vscode插件配置设置"],"content":"在插件中找到Todo Tree,点击设置按钮 找到在settings.json中编辑更多 将上述代码复制到settings.json中即可。 \"todo-tree.regex.regex\": \"((%|#|//|\u003c!--|^\\\\s*\\\\*)\\\\s*($TAGS)|^\\\\s*- \\\\[ \\\\])\", \"todo-tree.regex.regexCaseSensitive\": false, \"todo-tree.general.tags\": [ \"BUG\", \"HACK\", \"FIXME\", \"TODO\", \"todo\", \"bug\", \"tag\", \"done\", \"mark\", \"test\", \"update\", \"[]\", \"[x]\" ], \"todo-tree.highlights.defaultHighlight\": { \"background\": \"#ff9100\", \"foreground\": \"#ffffff\", \"type\": \"text\", \"icon\": \"bug\" }, \"todo-tree.highlights.customHighlight\": { //todo 需要做的功能 \"todo\": { \"icon\": \"alert\", //标签样式 \"background\": \"#c9c552\", //背景色 \"rulerColour\": \"#c9c552\", //外框颜色 \"iconColour\": \"#c9c552\", //标签颜色 }, //bug 必须要修复的BUG \"bug\": { \"background\": \"#ff3737\", \"icon\": \"bug\", \"rulerColour\": \"#eb5c5c\", \"iconColour\": \"#eb5c5c\", }, //tag 标签 \"tag\": { \"background\": \"#38b2f4\", \"icon\": \"tag\", \"rulerColour\": \"#38b2f4\", \"iconColour\": \"#38b2f4\", \"rulerLane\": \"full\" }, //done 已完成 \"done\": { \"background\": \"#328050\", \"icon\": \"check\", \"rulerColour\": \"#5eec95\", \"iconColour\": \"#5eec95\", }, //mark 标记一下 \"mark\": { \"background\": \"#f90\", \"icon\": \"note\", \"rulerColour\": \"#f90\", \"iconColour\": \"#f90\", }, //test 测试代码 \"test\": { \"background\": \"#df7be6\", \"icon\": \"flame\", \"rulerColour\": \"#df7be6\", \"iconColour\": \"#df7be6\", }, //update 优化升级点 \"update\": { \"background\": \"#b14e75\", \"icon\": \"versions\", \"rulerColour\": \"#d65d8e\", \"iconColour\": \"#d65d8e\", }, }, ","date":"2024-01-22","objectID":"/todotree/:0:0","tags":["vscode","extension"],"title":"Todotree插件设置","uri":"/todotree/"},{"categories":["vscode插件配置设置"],"content":"首先你需要按照这个教程 安装好Go环境。 之后在vscode插件商城中,搜寻Go,选择第一个进行安装。 安装好插件后,按下Ctrl+Shift+P快捷键,输入go tool,并安装所有工具。 观察在${GOPATH}/bin目录下是否存在这些工具的可执行文件。 重启vscode,即可。 ","date":"2024-11-10","objectID":"/go/:0:0","tags":["golang"],"title":"vscode Go环境设置","uri":"/go/"},{"categories":["golang之路"],"content":"指针 和C语言一样,Go中也存在指针,一般采用如下方式来获取变量的地址: a := 12 var ptr *int = \u0026a 如果我们想要获取该指针变量指向地址对应的内容时,我们可采用如下方式: fmt.Println(*ptr) *操作符和\u0026操作符分别代表了取值操作和取地址操作。 对于引用类型的变量,在Go中需要注意不仅需要声明,而且需要为其分配内存空间,不然就是对一个空指针进行操作了,具体的例子如下: func main(){ var a *int *a = 100 } 为了修改上述代码,我们可以添加如下操作: func main() { var a *int a = new(int) *a = 100 } new(T)函数能够返回传入类型T的内存地址,并且内存中对应的值为类型的零值。 ","date":"2024-11-08","objectID":"/go-basic2/:1:0","tags":["golang","study"],"title":"Go指针、函数、结构体、接口以及方法","uri":"/go-basic2/"},{"categories":["golang之路"],"content":"函数 函数的定义方式如下: func 函数名(参数) (返回值) { 函数体 } 其中需要说明的是: 参数由参数变量和参数变量的类型组成,多个参数之间使用,分隔。 如果相邻的参数类型相同,例如a int, b int可以简写为a, b int 如果传入参数数量不确定,可用...T来表示可变参数。例如func (a string, x ...int) 固定参数和可变参数搭配时,可变参数得放在固定参数后面。 可变参数可以是为一个数组 返回值由返回值变量和其变量类型组成,也可以只写返回值的类型,多个返回值必须用()包裹,并用,分隔。 如果返回值部分是由返回值变量和变量类型组成的话,函数体内部可以直接使用这个返回值,并在最后仅使用return来表示返回函数结果 我们定义一个a+b的函数作为示例: func intSum(a int, b int) res int { res = a + b return } int main() { b := intSum(1, 2) // 调用函数 } ","date":"2024-11-08","objectID":"/go-basic2/:2:0","tags":["golang","study"],"title":"Go指针、函数、结构体、接口以及方法","uri":"/go-basic2/"},{"categories":["golang之路"],"content":"函数类型 在go中,函数作为一等公民,因此可以作为变量进行传输。例如: func add(x, y int) int { return x + y } func minus(x, y int) int { return x - y } func calc(x, y int, op func(int, int)int) int { return op(x, y) } func main() { res := calc(1, 2, add) println(res) } 但是在上述实现中,op的函数签名过长,实际应用中并不现实,而且不能突出这个变量的意义,因此我们可以为这个类型定义一个类型: type IntOperation func(int,int)int 可以理解成为func(int,int)int这个函数签名定义新的名字,当然,我们也可以为基本类型定义新名字: type MyInt int 通过上述方法,我们就可以简化代码为如下形式: type IntOperation func(int,int)int func add(x, y int) int { return x + y } func minus(x, y int) int { return x - y } func calc(x, y int, op IntOperation) int { return op(x, y) } func main() { res := calc(1, 2, add) println(res) } ","date":"2024-11-08","objectID":"/go-basic2/:2:1","tags":["golang","study"],"title":"Go指针、函数、结构体、接口以及方法","uri":"/go-basic2/"},{"categories":["golang之路"],"content":"匿名函数和闭包 函数同样可以作为返回值,在go中不能在函数内部定义函数,因此采用匿名函数来实现,例如: func main() { add := func(x, y int) int { fmt.Println(x+y) } add(1, 2) func (x, y int) int { fmt.Println(x+y) }(1, 2) } 当函数的返回值是一个函数,并且该函数还包含相关引用环境时,我们就称其为闭包,例如: func addr() func(int) int { x := 10 return func(y int) int { x += y return x } } func main() { f := addr() print(f(10)) // 20 print(f(20)) // 40 } ","date":"2024-11-08","objectID":"/go-basic2/:2:2","tags":["golang","study"],"title":"Go指针、函数、结构体、接口以及方法","uri":"/go-basic2/"},{"categories":["golang之路"],"content":"defer语句 defer语句会将其后面跟随的语句进行延迟处理,在defer归属的函数即将返回时,将延迟处理的语句按defer定义的逆序进行执行,也就是说,先被defer的语句最后被执行,最后被defer的语句,最先被执行,例如: func deferDemo() { println(\"Hello\") defer println(\"defer 1\") defer println(\"defer 2\") println(\"World\") } /* Hello World defer 2 defer 1 */ 其执行原理如下图所示: 由于defer语句的这一特性,我们可以在defer后面执行例如资源清理、错误处理、解锁等操作。 ","date":"2024-11-08","objectID":"/go-basic2/:2:3","tags":["golang","study"],"title":"Go指针、函数、结构体、接口以及方法","uri":"/go-basic2/"},{"categories":["golang之路"],"content":"结构体 go中结构体的定义方式如下: type StructName struct { a1 int b1 string ... } 其中: StructName结构体名在同一个包内仅能有一个,不能重复 a1,b1称为字段名,在每个结构体内唯一,不能重复 例如: type person struct { name string age int sex int } 当我们声明了一个结构体后,如何实例化: var p person 或 p := new(person) 或 p := \u0026person{} 后面两种得到的是结构体的指针,但是对于结构体而言,并不需要像C++中需要(*p).name=\"John\"来修改属性,只需要p.name=John就行。 既然变量有匿名变量,结构体也会有匿名结构体: var user = struct {name string, age int}{ \"yizhigopher\", 12, } 上面这段代码不仅展示了匿名结构体的用法,还展示了结构体初始化的一种方式,这种方式通过直接按着结构体属性的顺序赋值即可(需要注意的是,这种方法必须对所有属性都初始化)。 接下来展示其他初始化的方法: var p person p.name = \"yizhigopher\" // 或 p := person{ name: \"yizhigopher\", age: 22, } // 或 p := \u0026person{ name: \"yizhigopher\" } 在go中没有构造函数的说法,需要自己实现构造函数: func newPerson() *person { return \u0026peroson{ name: \"yizhigopher\", age: 12, } } Go中的结构体继承通过匿名结构体来实现,具体如下: //Animal 动物 type Animal struct { name string } //Dog 狗 type Dog struct { Feet int8 *Animal //通过嵌套匿名结构体实现继承 } 其中关于匿名结构体需要注意的几点有: 当访问结构体成员时,会先在结构体中查找该字段,找不到再去嵌套的匿名字段中查找。 嵌套结构体内部可能存在相同的字段名。在这种情况下为了避免歧义需要通过指定具体的内嵌结构体字段名。 除此之外,结构体还有一个究极大杀器,称为结构体标签(Tag)。Tag是结构体的元信息,可以在运行的时候通过反射的机制读取出来。 Tag在结构体字段的后方定义,由一对反引号包裹起来,具体的格式如下: `key1:\"value1\" key2:\"value2\"` 需要注意的是: 结构体tag由一个或多个键值对组成。键与值使用冒号分隔,值用双引号括起来。同一个结构体字段可以设置多个键值对tag,不同的键值对之间使用空格分隔。 为结构体编写Tag时,必须严格遵守键值对的规则=,一旦格式写错,编译和运行时都不会提示任何错误,通过反射也无法正确取值。例如不要在key和value之间添加空格。 我们举个例子来展示tag的能力: //Student 学生 type Student struct { ID int `json:\"id\"` //通过指定tag实现json序列化该字段时的key Gender string //json序列化是默认使用字段名作为key name string //私有不能被json包访问 } func main() { s1 := Student{ ID: 1, Gender: \"男\", name: \"yizhigopher\", } data, err := json.Marshal(s1) if err != nil { fmt.Println(\"json marshal failed!\") return } fmt.Printf(\"json str:%s\\n\", data) //json str:{\"id\":1,\"Gender\":\"男\"} } ","date":"2024-11-08","objectID":"/go-basic2/:3:0","tags":["golang","study"],"title":"Go指针、函数、结构体、接口以及方法","uri":"/go-basic2/"},{"categories":["golang之路"],"content":"方法 go语言中,方法与OOP中的成员方法类似,是一种作用于特定类型变量的函数。这种特定类型变量叫做接收者(Receiver)。接收者的概念就类似于其他语言中的this或者self,具体定义的格式如下: func (接受者变量 接受者类型) 方法名(参数列表) (返回参数) { 函数体 } 可以发现,方法和函数之间在表达形式上的区别,就是在函数名前面增加了一段用于申明接受者类型的语句。需要注意的是: 接收者中的参数变量名在命名时,建议使用接收者类型名称首字母的小写,而不是self、this之类的命名。例如,Person类型的接收者变量应该命名为 p,Connector类型的接收者变量应该命名为c等。 接收者类型和参数类似,可以是指针类型和非指针类型。 非本地类型不能定义方法,也就是说我们不能给别的包的类型定义方法。 在Go语言中,接收者的类型可以是任何类型,不仅仅是结构体,任何类型都可以拥有方法。举个例子,我们基于内置的int类型使用type关键字可以定义新的自定义类型,然后为我们的自定义类型添加方法。 type MyInt int func (m MyInt) SayHello() { fmt.Println(\"Hello, 我是一个int。\") } 当接受者类型是指针等引用类型时,方法内对于接受者的修改是直接作用于接受者的。但是如果接受者类型是指类型,那么是作用于接受者copy后的对象。可以通过下属例子体现其中的区别: func (p *Person) SetAge(newAge int8) { p.age = newAge } func (p Person) SetAge2(newAge int8) { p.age = newAge } 我们应当在以下这些情况,使用指针类型接受者: 需要修改接收者中的值 接收者是拷贝代价比较大的大对象 保证一致性,如果有某个方法使用了指针接收者,那么其他的方法也应该使用指针接收者。 ","date":"2024-11-08","objectID":"/go-basic2/:4:0","tags":["golang","study"],"title":"Go指针、函数、结构体、接口以及方法","uri":"/go-basic2/"},{"categories":["golang之路"],"content":"推荐阅读: Go结构体的内存布局 通过八个demo搞明白Go语言defer的五大特性 Go Tag标签详解 ","date":"2024-11-08","objectID":"/go-basic2/:5:0","tags":["golang","study"],"title":"Go指针、函数、结构体、接口以及方法","uri":"/go-basic2/"},{"categories":["golang之路"],"content":"上一章节我们已经简单介绍了一个最简单的go程序以及关于变量以及常量的定义。本章节将会继续极少go的基础知识。 ","date":"2024-11-08","objectID":"/go-basic/:0:0","tags":["golang","study"],"title":"Go基本运算符、流程控制、数组与切片以及字典","uri":"/go-basic/"},{"categories":["golang之路"],"content":"基本运算符 go的基础运算符包含 数值计算:加+,减-,乘*,除/,求余% 逻辑计算:与\u0026\u0026,或||,非! 位运算符号:\u0026,|,^,\u003e\u003e,\u003c\u003c 关系运算符:相等==,不等于!=,大于\u003e,大于等于\u003e=,小于\u003c,小于等于\u003c= ","date":"2024-11-08","objectID":"/go-basic/:1:0","tags":["golang","study"],"title":"Go基本运算符、流程控制、数组与切片以及字典","uri":"/go-basic/"},{"categories":["golang之路"],"content":"流程控制 ","date":"2024-11-08","objectID":"/go-basic/:2:0","tags":["golang","study"],"title":"Go基本运算符、流程控制、数组与切片以及字典","uri":"/go-basic/"},{"categories":["golang之路"],"content":"if-else分支结构 if-else分支判断结构如下所示: if 判断1 { 语句1 } else if 判断2{ 语句2 } else { 语句3 } 需要注意的是: 花括号不能换行,必须和if在同一行内 花括号是不可以省略的,即使语句1中只有1个语句 在go中if-else还有一种特殊且常用的写法: func ifDemo() { if score := 65; score \u003e= 90 { fmt.Println(\"A\") } else if score \u003e 75 { fmt.Println(\"B\") } else { fmt.Println(\"C\") } } 我们可以在if表达式之前添加一个执行语句,再根据变量值进行判断,并且这个变量的作用域仅在这段if-else语句中。 ","date":"2024-11-08","objectID":"/go-basic/:2:1","tags":["golang","study"],"title":"Go基本运算符、流程控制、数组与切片以及字典","uri":"/go-basic/"},{"categories":["golang之路"],"content":"循环结构 go中所有的循环均适用for来实现,不会有while以及do-until结构。 for循环的基本结构如下 for 初始语句;终止语句;每步更新内容 { ... } 其中初始语句、终止语句以及每步更新内容都不是必填项。 例如: for i := 0; i\u003c 10; i++ { print(i) } // ----------------------- i := 0 for i \u003c 10 { print(i) i++ } // ----------------------- i := 0 for { if i \u003e= 10 { break } print(i) i += 1 } 以上三种写法均是等价的。 此外还有for-range遍历结构: for k,v := range variable { } 其中variable可以是map,string,slice,array等类型,k表示索引,v表示索引对应的值。 在go1.22版本中对循环结构增加了几个特性。 首先是增加了对整数range的新特性,上述代码也可以这么写: for i := range 10 { print(i) } 此外去除了for-range不在共享循环变量,在go1.16和go1.22两个版本中分别运行一下代码,执行结果会有所不同。 list := []int{1, 2, 3, 4, 5} for _, val := range list { go func() { print(val) }() } 旧版本中for循环迭代器的变量是一个单一变量,在每个循环迭代中仅是取值不同。这样做在性能上非常高效,但如果使用不当,会导致意想不到的行为,可能会造成共享循环变量的问题。而go1.22中,for循环的每次迭代都会创建新变量,每次循环自己迭代自己的变量,以避免意外共享错误。关于这方面的知识可以阅读这篇博客 。 ","date":"2024-11-08","objectID":"/go-basic/:2:2","tags":["golang","study"],"title":"Go基本运算符、流程控制、数组与切片以及字典","uri":"/go-basic/"},{"categories":["golang之路"],"content":"数组与切片 ","date":"2024-11-08","objectID":"/go-basic/:3:0","tags":["golang","study"],"title":"Go基本运算符、流程控制、数组与切片以及字典","uri":"/go-basic/"},{"categories":["golang之路"],"content":"数组(Array) 数组是同一种数据类型元素的集合。在Go语言中,数组从声明时就确定,使用时可以修改数组成员,但是数组大小不可变化。示例如下: var a [3]int // {0, 0, 0} var cities [3]string{\"Shanghai\", \"Beijing\"} // {\"Shanghai\", \"Beijing\", \"\"} var deduce [...]int{1, 2, 3} // 数组大小就是3了 var matrix [2][3]int{ {1, 2, 3}, {4, 5, 6}, } 可以通过如下方式实现数组的遍历: for i := 0; i \u003c len(cities); i++ { print(cities[i]) } for _, val := range cities { print(val) } 需要注意的是,如果在函数中传入一个数组类型的变量,那么仅会copy值到另一个变量中,而不是传递一个引用。 func modifyArray(x [3]int) { x[0] = 100 } func modifyArray2(x [3][2]int) { x[2][0] = 100 } func main() { a := [3]int{10, 20, 30} modifyArray(a) //在modify中修改的是a的副本x fmt.Println(a) //[10 20 30] b := [3][2]int{ {1, 1}, {1, 1}, {1, 1}, } modifyArray2(b) //在modify中修改的是b的副本x fmt.Println(b) //[[1 1] [1 1] [1 1]] } [n]T表示指针数组,[n]T表示数组指针 。 ","date":"2024-11-08","objectID":"/go-basic/:3:1","tags":["golang","study"],"title":"Go基本运算符、流程控制、数组与切片以及字典","uri":"/go-basic/"},{"categories":["golang之路"],"content":"切片(Array) 由于数组长度不可变,我们也希望go中能有个像vector的类型,能够支持可变长度。 切片(Slice)是一个拥有相同类型元素的可变长度的序列。它是基于数组类型做的一层封装。它非常灵活,支持自动扩容。 切片是一个引用类型,它的内部结构包含地址、长度和容量。切片一般用于快速地操作一块数据集合。申明切片变量如下: var x []T var a []int{1, 2, 3} var slice = make([]int, 3, 10) // 定义了len==3,cap==10的切片 make函数仅用来初始化slice, map以及chan三种类型的变量 切片拥有自己的长度和容量,我们可以通过使用内置的len()函数求长度,使用内置的cap()函数求切片的容量。 我们也可以在数组上,创建一个切片 var x [5]int{1, 2, 3, 4, 5} b := x[3:5] // 左闭右开。 切片的本质就是对底层数组的封装,它包含了三个信息:底层数组的指针、切片的长度(len)和切片的容量(cap)。因此判断切片是否为空,应当如下: if len(s) == 0 { } 而不是s==nil,如果这么比较的话,实际上是在判断指针是否为空。例如: var s1 []int //len(s1)=0;cap(s1)=0;s1==nil s2 := []int{} //len(s2)=0;cap(s2)=0;s2!=nil s3 := make([]int, 0) //len(s3)=0;cap(s3)=0;s3!=nil 由由于切片是指向底层数据的指针,因此如果copy,那么对copy后的变量进行修改,原先的变量也会发生改变,举个例子: package main import \"fmt\" func sliceFunc(s []int, val int) { s[0] = val } func main() { var list = [5]int{1, 2, 3, 4, 5} s1 := list[0:3] s2 := list[:] s3 := s2 sliceFunc(s1, 6) fmt.Println(list) //[6 2 3 4 5] for _, val := range s1 { print(val, \" \") } println() //6 2 3 for _, val := range s2 { print(val, \" \") } println() // 6 2 3 4 5 for _, val := range s3 { print(val, \" \") } println() // 6 2 3 4 5 } 因此,当我们使用切片时,最好遵循所有权的唯一性原则(这在Rust中被纳入语法范围中),即对于内存中的某个数据,同一时刻一个值只能有一个所有者。 既然切片的长度是可变的,那么该如何增加切片的元素呢: func main() { //append()添加元素和切片扩容 var numSlice []int for i := 0; i \u003c 10; i++ { numSlice = append(numSlice, i) fmt.Printf(\"%v len:%d cap:%d ptr:%p\\n\", numSlice, len(numSlice), cap(numSlice), numSlice) } } 使用内置的append函数即可。 需要注意的是,每个切片会指向一个底层数组,这个数组的容量够用就添加新增元素。当底层数组不能容纳新增的元素时,切片就会自动按照一定的策略进行“扩容”,此时该切片指向的底层数组就会更换。“扩容”操作往往发生在append()函数调用时,所以我们通常都需要用原变量接收append函数的返回值。 关于每次扩容对cap修改的策略,可以参考${GOROOT}/src/runtime/slice.go的源码,这里就不过多展开。 ","date":"2024-11-08","objectID":"/go-basic/:3:2","tags":["golang","study"],"title":"Go基本运算符、流程控制、数组与切片以及字典","uri":"/go-basic/"},{"categories":["golang之路"],"content":"字典(map) map是一种无序的基于key-value的数据结构,Go语言中的map是引用类型,必须初始化才能使用。 var stuMap map[string]int stuMap = make(map[string]int) stuMap[\"Tom\"] = 24 exampleMap := map[string]string{ \"code\": \"23\", \"message\": \"Query success!\", } 思考下[]map[string]int和map[string][]int之间的区别 如何判断某个键是否存在: _, ok := m[key] if !ok{ println(\"key is not existed!\") } 可以通过如下方法删去map中的某个键: delete(m, key) ","date":"2024-11-08","objectID":"/go-basic/:4:0","tags":["golang","study"],"title":"Go基本运算符、流程控制、数组与切片以及字典","uri":"/go-basic/"},{"categories":["golang之路"],"content":"思考 package main import \"fmt\" func main() { type Map map[string][]int m := make(Map) s := []int{1, 2} s = append(s, 3) fmt.Printf(\"%+v\\n\", s) m[\"q1mi\"] = s s = append(s[:1], s[2:]...) fmt.Printf(\"%+v\\n\", s) fmt.Printf(\"%+v\\n\", m[\"q1mi\"]) } 尝试写出上面这段代码的结果,从而深刻理解本章节所阐述的内容。 ","date":"2024-11-08","objectID":"/go-basic/:5:0","tags":["golang","study"],"title":"Go基本运算符、流程控制、数组与切片以及字典","uri":"/go-basic/"},{"categories":["golang之路"],"content":"在完成了上个章节的Go编译器安装后,本章节将介绍Go的基础语法,并通过几个例子进行展示。 Hello World 学习每个语言的第一步永远是学会如何在控制台打印😊,现在让我们看一下go如何实现Hello World: // main.go package main import \"fmt\" func main(){ fmt.Println(\"Hello World!\") } 在本文件的目录下,在控制台中执行go build main.go \u0026\u0026 main或go run main.go观察控制台输出的结果。 我们逐行分析说明: 第1行使用package main申明这是一个主包,我们需要在主包下面定义func main()函数,用于声明程序入口。 第3行使用import来导入外部包\"fmt\",该包通常用于实现格式化的I/O操作,这个与C的\u003ccstdio\u003e相似。 第5行使用func main()来申明编译入口以及程序执行的开始位置。 第6行使用fmt.Println(\"Hello World!\")向控制台输出Hello World! 通过上述例子,我们介绍下go语言一个程序所需要的三个部分: 包名。每个go程序都需要申明该程序所属的包,在本章节中,我们所有代码的包名均为main,而对于go的包管理方式,我们将在后续的章节中进行介绍。 导入外部包。想要实现一段代码,不可避免需要调用已经封装好的程序,因此通过import来导入一些必要的外部包,例如本例子中的fmt,以及一些常用的bytes,net等,这些均是已经封装好的标准库。这些标准库的位置在${GOROOT}/src中 函数。go语言函数申明格式为func functionName(args)并用花括号包裹函数体。需要注意的是,不像C++可以将花括号换行,go语言中花括号的换行是不允许换行的,并使用tab进行代码缩进。 接下来我们将介绍go的基本语法 GO基本语法 ","date":"2024-10-30","objectID":"/go-hello/:0:0","tags":["golang","study"],"title":"Hello Go!","uri":"/go-hello/"},{"categories":["golang之路"],"content":"变量和常量 ","date":"2024-10-30","objectID":"/go-hello/:1:0","tags":["golang","study"],"title":"Hello Go!","uri":"/go-hello/"},{"categories":["golang之路"],"content":"变量 变量定义如下方式如下: var a int var a int = 10 var a = 10 a := 10 上述为4种不同表达形式的变量申明方式,其中a为变量名,int为变量类型,后两种方式中,go能够自动推理出各个变量的类型。 go的变量名可以用26个英文字母,_以及数字组合而成 数字不能作为变量的起始字符 此外go中存在一些关键字,其具备特殊含义,包含一下25个,这些单词不能作为变量名: break default func interface select case defer go map struct chan else goto package switch const fallthrough if range type continue for import return var 此外还存在一些保留字,这些同样不建议作为变量名: Constants: true false iota nil Types: int int8 int16 int32 int64 uint uint8 uint16 uint32 uint64 uintptr float32 float64 complex128 complex64 bool byte rune string error Functions: make len cap new append copy close delete complex real imag panic recover 如果需要定义多个变量可以如此声明: var ( a int = 19 b bool = true ) 或 var ( a = 10 b = false ) ","date":"2024-10-30","objectID":"/go-hello/:1:1","tags":["golang","study"],"title":"Hello Go!","uri":"/go-hello/"},{"categories":["golang之路"],"content":"常量定义 与变量定义类似,可以如此定义 const a int = 10 const a = 10 // notice the type of a is 'untyped int' const ( a = 10 b string = \"12a\" ) 需要注意的是,常量的批量定义中,如果没有确定常量的类型,go并不会直接推到出常量的类型,而是untyped ...,关于这个问题可以查看官方文档 ,对于untyped常量会导致的问题,可以查看疑惑: Go const 导致程序结果错乱? ","date":"2024-10-30","objectID":"/go-hello/:1:2","tags":["golang","study"],"title":"Hello Go!","uri":"/go-hello/"},{"categories":["golang之路"],"content":"多重赋值 这个特点有点像js中的多重赋值,我们可以这么做 x, y, z := 1, 2, 3 这时,x的值为1,y的值就为2,…… 你可以随处使用该方法来实现多重赋值,例如可以用在常量定义中 ","date":"2024-10-30","objectID":"/go-hello/:1:3","tags":["golang","study"],"title":"Hello Go!","uri":"/go-hello/"},{"categories":["golang之路"],"content":"匿名变量 go中会使用_来表示一个仅用于接收,但不能使用的变量,常用于当我们想要忽略多重赋值中的某个值时。例如: x, _, z := 1, 2, 3 ","date":"2024-10-30","objectID":"/go-hello/:1:4","tags":["golang","study"],"title":"Hello Go!","uri":"/go-hello/"},{"categories":["golang之路"],"content":"iota iota是go语言的常量计数器,只能在常量的表达式中使用。 iota在const关键字出现时将被重置为0。const中每新增一行常量声明将使iota计数一次。使用iota能简化定义,在定义枚举时很有用。例如: const ( a = iota // a = 0 b // b = 1 c // c = 2 ) 或 const ( _ = iota KB = 1\u003c\u003c(10*iota) MB GB ) 可以发现,后续的常量推导,均是依据离这条语句最近的iota表达来推导的。 代码风格 在go的代码中,变量命名的风格通常具有一个约定俗成的规则,这里我将总结一下我遇到的一些规则 对于常量名,一般全部大写,并用_隔开,例如:MAX_CONNECTION。 对于变量名以及函数名,一般采用驼峰风格,例如isHashed。如果需要将这个函数或变量设为public,则将首字母也大写。 当然变量的定义最好能够直观表现其意义,最好别用拼音、无意义的一个字符串来定义一个变量。 ","date":"2024-10-30","objectID":"/go-hello/:1:5","tags":["golang","study"],"title":"Hello Go!","uri":"/go-hello/"},{"categories":["Docker"],"content":"为了进行多个容器之间通讯的实验,首先我们需要创建两个服务,分别打包成不同的镜像,并通过docker-compose进行容器编排。本次实验的项目目录如下: ├── client │ ├── dockerfile │ ├── index.html │ └── nginx.conf ├── docker-compose.yml ├── microservice1 │ ├── dockerfile │ ├── main │ └── main.go └── microservice2 ├── dockerfile ├── main └── main.go microservice后端服务定义 我们定义了两个后端服务,其代码如下: // microservice1/main.go package main import ( \"encoding/json\" \"net/http\" ) type Resp struct { Code int Message string } func main() { http.HandleFunc(\"/say_hello\", func(w http.ResponseWriter, r *http.Request) { w.Header().Set(\"Access-Control-Allow-Origin\", \"*\") //允许访问所有域 w.Header().Add(\"Access-Control-Allow-Headers\", \"Content-Type\") //header的类型 w.Header().Set(\"content-type\", \"application/json\") var data Resp if r.Method != \"GET\" { data = Resp{ Code: 4000, Message: \"Only Allow GET Method!\", } } else { data = Resp{ Code: 2000, Message: \"Hello Microservice1\", } } resp, _ := json.Marshal(data) w.Write(resp) }) http.ListenAndServe(\":8080\", nil) } 编译上述代码: GOOS=linux GOARCH=amd64 go build main.go 另一个后端服务代码如下: // microservice2/main.go package main import ( \"encoding/json\" \"net/http\" ) func main() { http.HandleFunc(\"/who_am_i\", func(w http.ResponseWriter, r *http.Request) { w.Header().Set(\"Access-Control-Allow-Origin\", \"*\") //允许访问所有域 w.Header().Add(\"Access-Control-Allow-Headers\", \"Content-Type\") //header的类型 w.Header().Set(\"content-type\", \"application/json\") data := struct{ Code int Message string }{ Code: 2000, Message: \"I am yizhigopher!\", } resp, _ := json.Marshal(data) w.Write(resp) }) http.ListenAndServe(\":8080\", nil) } 使用上述命令编译代码。 并编写容器build脚本,每个后端服务的dockerfile一样: FROM golang:1.23-alpine WORKDIR /app COPY ./main /app/microservice EXPOSE 8080 ENTRYPOINT [\"/app/microservice\"] 前端界面定义 为了直观体现出不同容器之间通讯作用,我们编写了如下前端代码: \u003c!-- client/index.html --\u003e \u003c!DOCTYPE html\u003e \u003chtml lang=\"CH-ZN\"\u003e \u003chead\u003e \u003cmeta charset=\"UTF-8\"\u003e \u003cmeta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\"\u003e \u003cmeta http-equiv=\"X-UA-Compatible\" content=\"ie=edge\"\u003e \u003cscript src=\"https://code.jquery.com/jquery-3.7.1.min.js\"\u003e\u003c/script\u003e \u003c/head\u003e \u003cbody\u003e \u003cbutton onclick=\"say_hello()\" style=\"width:100px; height:20px\"\u003ecall say hello\u003c/button\u003e \u003cbr/\u003e \u003cbutton onclick=\"whoami()\" style=\"width:100px; height:20px\"\u003ecall who am i\u003c/button\u003e \u003c/body\u003e \u003c/html\u003e \u003cscript\u003e function say_hello() { $.ajax({ url: \"http://localhost/api/v1/say_hello\", type: \"GET\", dataType: \"json\", async: true, success: function(res) { console.log(res) }, error: function(param) { console.log(\"fail, error=\", param) } }) } function whoami() { $.ajax({ url: \"http://localhost/api/v2/who_am_i\", type: \"GET\", dataType: \"json\", async: true, success: function(res) { console.log(res) }, error: function(param) { console.log(\"fail, error=\", param) } }) } \u003c/script\u003e 需要注意的是,其中每个接口请求的url为:http://localhost/api/v1/say_hello和http://localhost/api/v2/who_am_i。并编写nginx.conf配置代理: user nginx; worker_processes auto; error_log /var/log/nginx/error.log warn; pid /var/run/nginx.pid; events { worker_connections 1024; } http { include /etc/nginx/mime.types; default_type application/octet-stream; log_format main '$remote_addr - $remote_user [$time_local] \"$request\" ' '$status $body_bytes_sent \"$http_referer\" ' '\"$http_user_agent\" \"$http_x_forwarded_for\"'; access_log /var/log/nginx/access.log main; sendfile on; keepalive_timeout 65; # Server block for handling requests server { listen 80; server_name localhost; # 处理静态文件的请求(如果有) location / { root /fontend; index index.html; } location ^~/api/v1/ { proxy_pass http://router_one:8080/; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; } location ^~/api/v2/ { proxy_pass http://router_two:8080/; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; } } } 我们注意到,代理的转发路径分别是http://router_one:8080/和http://router_two:8080/,其中不是IP地址,而是容器名。 容器编排以及通信测试 编写docker-compose.yml编排内容,配置如下: version: '3' services: font_end: image: fontend:v1 build: ./client contai","date":"2024-10-29","objectID":"/docker-connection/:0:0","tags":["docker","go"],"title":"如何实现多个个容器之间的通讯","uri":"/docker-connection/"},{"categories":["golang之路"],"content":"在本节中,我们将首先简单介绍下如何安装Go语言环境,以开启Go语言学习之路!我们将简单介绍在Windows环境,Linux环境以及Mac环境下如何安装,尽可能全面的覆盖不同读者所使用的操作系统。 Go语言下载地址 Windows安装 下载windows环境下的zip文件,解压到系统的某个目录中,在我的系统中,我将其解压到本地D盘目录D:\\Program Files\\Go\\go下。 之后在系统的环境变量中将该目录下的bin目录添加到环境变量中,如图所示: 保存后通过打开终端,输入go version即可判断是否添加到环境变量中 之后需申明GOROOT、GOPATH,并将GOPATH目录下的bin目录添加到环境变量中,便于后续调用go install指令安装的命令。 Linux安装 下载图1中的源码包,删除 /usr/local/go 文件夹(如果存在),删除之前安装的 Go,然后将刚刚下载的压缩包解压缩到/usr/local,在 /usr/local/go 中创建一个全新的 GOROOT rm -rf /usr/local/go \u0026\u0026 tar -C /usr/local -xzf go1.23.2.linux-amd64.tar.gz 在PATH环境变量中添加/usr/local/go/bin,在$HOME/.profile或/etc/profile(用于全系统安装)中添加以下一行即可: export GOROOT=/usr/local/go export GOPATH=/data/go export PATH=$GOPATH/bin:$GOROOT/bin:$PATH 之后使用命令source $HOME/.profile激活环境变量配置即可。 Mac安装 安装图1中的.pkg安装文件即可 执行以下命令: cd ~ vim .bash_profile 编辑用户环境变量: export GOPATH=/Users/XXX/go #本地创建的gopath绝对路径 export PATH=$PATH:$GOPATH/bin 激活配置文件source ~/.bash_profile即可。 额外的注意点: 需要注意的是,GOPATH目录下需要创建三个文件夹,分别是src、pkg以及bin,其中bin目录保存go install安装的指令,pkg保存第三方包的源码,src用于在GO111MODULE=off时,在该路径下编写包含第三方包的程序(现在基本不在src下写代码了) 之后我们需要覆写go的几个配置: go env -w GO111MODULE=on go env -w GOPROXY=https://goproxy.cn,direct GO111MODULE是Go语言用于管理依赖关系的一种机制,它可以帮助开发者更好地管理项目的依赖包,并且可以确保项目在不同环境下的一致性 GOPROXY表示Go安装第三方包的代理(因为国内众所周知的原因),便于后续直接go get安装第三方包 至此,go的安装已经完成了 ","date":"2024-10-08","objectID":"/go-install/:0:0","tags":["golang","study"],"title":"Go语言环境安装","uri":"/go-install/"},{"categories":null,"content":"归一化流基本内容 什么是标准化流:基于一系列可逆变换,生成样本对应的分布 标准化流的基本原理: 变量替换:$p_x(x)=p_z(f_\\theta(x))|\\det J_{f_\\theta}(x)|$,我们可以理解为通过$z=f_\\theta(x)$,将原先复杂分布,变换成简单易懂的分布形式,其中我们要求$f_\\theta$必须是可逆变换。标准化流的目的就是求得该可逆变换。 在标准化流中,往往会使用多个这样的可逆变换,即$f_\\theta(x)=f_1(x)\\circ f_2(x)…\\circ f_K(x)$ 雅各比行列式求解: $$ \\det J_{f_\\theta}(x)=\\det \\prod_{i=1}^K J_{f_i}(x)=\\prod_{i=1}^K\\det J_{f_i}(x) $$ 利用对数函数,我们可以得到: $$ \\log p_x(x)=\\log p_z(f_{\\theta}(x))+\\sum_{i=1}^K\\log |det J_{f_i}(x)| $$ 我们需要最大化这个概率密度 ","date":"2024-01-06","objectID":"/nf-introduce/:0:1","tags":null,"title":"Normalizing Flow介绍","uri":"/nf-introduce/"},{"categories":null,"content":"论文《A deep generative model for probabilistic energy forecasting in power systems: normalizing flows》的应用: 在该论文中,作者采用标准化流的方式实现了负荷预测功能。作者利用$\\bm x$表示d-1天的负荷数据,用$\\bm c$表示天气信息,希望能够得到符合符合数据分布的分布$p_\\theta(\\bm x|\\bm c)$,从而可以利用该分布得到预测数据$^{\\bm x}$。 在利用归一化流方式对泰迪比赛数据进行负荷预测的尝试中,会进行如下操作: 提取时间数据和天气数据,重新整合原本数据格式,得到归一化流的模型输入格式 其中每一行为某个时刻的负荷数据y和影响负荷的因素(时间因素、天气因素)x。根据日期重新排列数据x,将每个时刻影响负荷的因素横向排列并组合。此时x重新排列为一个$day\\times 960$的矩阵,表现如下: ","date":"2024-01-06","objectID":"/nf-introduce/:0:2","tags":null,"title":"Normalizing Flow介绍","uri":"/nf-introduce/"},{"categories":["cuda"],"content":"创建实例 在阿里云平台上创建实例: 其中我们镜像选择为公共的Ubuntu20.04镜像,当然你也可以选择其他配置好的镜像从而简化之后的步骤。 我们之后会演示如何在一个空白的系统中,安装python、cuda以及pytorch的环境 配置好以上信息之后,我们就能获得一个实例,点击实例操作里的打开即可连接远程主机,之后我们会在这个实力上安装一些新的东西,配置我们需要的环境。 ","date":"2023-12-14","objectID":"/install/:0:1","tags":["cuda","安装教程"],"title":"阿里云PAI平台搭建pytorch-cuda11.4环境","uri":"/install/"},{"categories":["cuda"],"content":"安装基本环境 更新apt源 apt-get update 安装python 十分简单: apt-get install python3 apt-get install python3-venv apt-get install python3-pip 安装完成之后,可以执行 python3 --version 安装cuda所需环境 控制台敲入nvidia-smi获得GPU信息,可以我们所需cuda的版本是11.4 安装libxml2 apt-get install libxml2 配置好时区信息即可 安装gcc环境: apt-get install gcc-7 g++-7 配置gcc版本优先级,不然cuda无法识别到安装好的gcc update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-7 70 --slave /usr/bin/g++ g++ /usr/bin/g++-7 --slave /usr/bin/gcov gcov /usr/bin/gcov-7 update-alternatives --config gcc 敲入gcc --version出现如下信息即可: 安装wget和vim apt-get install wget apt-get install vim 安装cuda 到nvidia官网上找到对应cuda驱动下载信息:如图选择即可 在当前目录下创建一个cuda-installer目录,进入该目录,执行installer中的命令: wget https://developer.download.nvidia.com/compute/cuda/11.4.0/local_installers/cuda_11.4.0_470.42.01_linux.run sh cuda_11.4.0_470.42.01_linux.run 敲入accpet同意协议,在之后的配置中选择如下配置: 安装即可,同时按照给出的提示: 修改/etc/profile文件,设置环境变量。使用vim /etc/profile在文末添加如下信息: export CUDA_HOME=/usr/local/cuda-11.4 export LD_LIBRARY_PATH=/usr/local/cuda-11.4/lib64:$LD_LIBRARY_PATH export PATH=/usr/local/cuda-11.4/bin:$PATH 重起实例,执行nvcc -V出现: 即可。 至此cuda安装完毕 安装Torch 去官网上,找到对应版本执行一下命令即可 pip install torch==1.12.0+cu113 torchvision==0.13.0+cu113 torchaudio==0.12.0 --extra-index-url https://download.pytorch.org/whl/cu113 ","date":"2023-12-14","objectID":"/install/:0:2","tags":["cuda","安装教程"],"title":"阿里云PAI平台搭建pytorch-cuda11.4环境","uri":"/install/"},{"categories":["golang小技巧"],"content":"简介 我们能在go代码中添加一段注释,用来表示一种编译约束 //go:build [tags] 通过这个并且配合go build -tags=\"...\"的方式,就能实现带有约束的编译。 举个例子: 我们编写一下代码:主程序申明如下 //main.go package main var configArr []string func main(){ for _, v := range configArr { print(v, \" \") } } 同时,我们会编写另外两个用于初始化的代码,分别为dev和test //go:build dev package main func init() { configArr = append(configArr, \"dev\") } //go:build test package main func init() { configArr = append(configArr, \"test\") } 我们可以看到,在两个同一包下,我们定义了两个不同的初始化功能,分别为configArr添加相应的内容。 当我们采用不同方式编译代码时,会看到如下情况: 能够看到编译时携带的tag不同,结果也就不同。 我们可以很直观的想到可以通过这种方法,实现不同模式下的编译 ","date":"2023-12-04","objectID":"/tips-1/:0:1","tags":["golang","tips"],"title":"go语言小技巧--编译约束","uri":"/tips-1/"},{"categories":["golang小技巧"],"content":"环境设置 当然我们也可以通过添加//go:build linux的方式对本段代码进行编译环境的限制。 例如我们对上述代码的go:build注释进行修改,可以看到如下效果: //go:build linux package main func init() { configArr = append(configArr, \"dev\") } //go:build windows package main func init() { configArr = append(configArr, \"test\") } 这时,我们直接进行代码编译,能够看到如下效果: 可以看到,我们只将test部分的代码编译,而dev的代码并没有编译 ","date":"2023-12-04","objectID":"/tips-1/:0:2","tags":["golang","tips"],"title":"go语言小技巧--编译约束","uri":"/tips-1/"},{"categories":["golang小技巧"],"content":"格式 举个例子:// go:build linux \u0026\u0026 amd64 \u0026\u0026 !cgo || darwin,通过这个我们就能发现,可以通过逻辑运算符来表明编译约束。 ","date":"2023-12-04","objectID":"/tips-1/:0:3","tags":["golang","tips"],"title":"go语言小技巧--编译约束","uri":"/tips-1/"},{"categories":["Docker"],"content":"如何运行容器 在之前的几篇文章中,我们学习了如何使用docker的指令以及如何进行容器打包,接下来我们就需要了解,该如何运行容器。 最简单的方式: 使用docker run或docker start指令来运行镜像或容器 但是这种方式在面对需要进行容器的端口映射、镜像卷以及容器间的网络通信时,会比较麻烦,不易操作。 同时,如果某个容器额外需要依赖另一个容器的存在,即容器之间存在关联的关系时,容器的启动顺序就比较重要了。举个简单的例子,我们手头有个web服务容器,这个容器需要依赖另一个MySQL容器的存在,这时,我们就需要先在web服务容器运行前,先运行MySQL容器。也许你会认为这种情况下,容器的先后顺序并不会对你造成困扰,但是如果容器数量多了怎么办,容器运行的相关参数特别多怎么办。亦或者经过了较长时间,你忘记了这个依赖关系怎么办? 这时候我们就需要有一个工具来管理容器运行的相关参数以及依赖关系。 ","date":"2023-11-30","objectID":"/docker-run/:0:1","tags":null,"title":"容器运行方式","uri":"/docker-run/"},{"categories":["Docker"],"content":"docker-compose工具 如何安装 Windows环境下,因为docker-desktop自带docker-compose,所以不需要关注如何安装 Linux环境下,访问docker-compose的下载地址Releases · docker/compose (github.com) ,选择适合系统的以及相应架构的可运行程序。运行sudo chmod +x docker-compose赋予其可运行能力,让后将该可执行文件放入环境变量的目录下即可 编写docker-compose.yml文件 当我们已经将容器打包完成之后,我们可以编写docker-compose.yml文件,从而能够运行容器。 我们举个简单的例子:在这个例子中,我们只需要申明其镜像卷的挂载即可 verison: '3' services: cliffwalking: image: demo:v1 container_name: reinforce_learning volumes: - /home/yizhigopher/cliffwalk/ouput:/cliffwalking/output - /home/yizhigopher/cliffwalk/model:/cliffwalking/model 我们只需要在当前docker-compose.yml的目录下,运行docker-compose up就能够运行一个名为reinforce_learning的容器,并且将容器内部的output和model文件夹的内容和容器外部的对应的文件夹相关联. 当然,docker-compose能够识别的关键字不止以上这些内容,我们可以通过访问Docker Compose overview | Docker Docs 以及菜鸟教程Docker Compose | 菜鸟教程 (runoob.com) 获得相关的更多信息.以上只是一个简单的示例,希望能够通过这些能够让读者一窥docker-compose容器编排的高效以及优越性 ","date":"2023-11-30","objectID":"/docker-run/:0:2","tags":null,"title":"容器运行方式","uri":"/docker-run/"},{"categories":["Docker"],"content":"结语 至此,docker的相关知识基本分享完毕,事实上,仅仅通过短短的三个博文是不能说明白docker的,也不可能就仅仅读这些内容就能掌握docker.这三篇博文,仅仅能够让大家对docker有个基本的了解,以及基本操作的熟悉.想要熟练掌握docker,还需要经过自己大量的实践练习和操作才能真正熟悉. ","date":"2023-11-30","objectID":"/docker-run/:0:3","tags":null,"title":"容器运行方式","uri":"/docker-run/"},{"categories":["Docker"],"content":"基本操作 在上节,我们已经安装好docker,但是就如同写代码一样,我们安装好了相关环境,但是并没有代码能够让我们运行,我们又不会写代码(自己构建镜像),那该怎么办。我们可以到github上面寻找一个相关的代码,拉取到本地,然后运行即可。 所以我们现在首先要做的事应该是学会如何拉取镜像到本地。 docker pull 完整的docker pull命令应该是:docker pull ${IMAGE_NAME}:${IMAGE_TAG} 它会从docker的官方镜像仓库中拉取对应的镜像。其中${IMAGE_TAG}部分如果不写,会默认拉取最新的镜像信息。 例如,我们现在运行docker pull hello-world命令,我们看看会发生什么: 我们能够看到,使用了最新的版本(tag),镜像的sha256验证信息,以及从哪里拉取的镜像。所以之后,如果我们需要某种镜像,我们就可以直接pull以下就可以了。 可以通过访问docker hub 上,搜索你想要的基础镜像的信息即可(这一过程需要科学上网),或者使用docker search ${IMAGE_NAME},实在不行利用bing搜索相关信息也是不错的选择 那么我们拉取完了,我们怎么能够确定这个镜像就是在我的电脑上的呢? docker images 我们在控制台输入docker images看看会发生什么: 我们看到,在我们本地存储中,确确实实有一个来自hello-world,TAG是latest的仓库的镜像。 当然你也可以通过docker inspect ${IMAGE_NAME}查看镜像在本地的哪个位置,但是这么做完全没有必要。 我们已经能够确定确确实实有个镜像在本地,那么我们该如何运行这个镜像呢 docker run 完整的docker run指令如下:docker run [OPTIONS] ${IMAGE_NAME} [COMMAND] [ARG...],这样之后会生成一个容器container 其中options常用的是: -v:绑定容器卷,后面紧跟映射关系,具体而言就是将本地文件和容器内的文件相关联。需要注意的是,两处的文件路径用:隔开,前面是本地文件的绝对路径,后面是容器内的绝对路径。例如:docker run -v /home/yizhigopher/result:/project/result xxx:v1 -d:后台运行容器,使用这个option后,我们就不会看到容器运行的结果,而只能看到一个容器ID -p:端口映射。有些时候,我们需要访问容器内的一些服务,例如MySQL容器,我们需要在本地就能连接容器中的MySQL服务,我们可以这么写:docker run -d -p 8080:3306 mysql,这样我们就是将容器内的3306端口映射到本地的8080端口,我们在本地就可以通过访问8080端口获得MySQL服务。 -i:以交互模式运行容器,通常与 -t 同时使用 -t:为容器重新分配一个伪输入终端,通常与 -i 同时使用 上述就是我们一般常用的OPTIONS信息,我们掌握了这些,基本可以应付大部分情况。如果需要其他的操作可以查阅官方文档,或者baidu。 举个例子:在终端执行docker run -it golang:alpine会发生什么: 需要注意的是我们这个命令相当于启动一个容器并进入容器内部。可以观察到,我们终端的位置发生了变化,从root@yizhigopher进入到了容器内部的/go目录下,由此也能看出docker的容器也可看成是一个小型的操作系统,在带有bash工具的容器内部,也可以执行一些简单的终端命令,例如ls、cd等等 既然我们已经学会了如何拉取镜像,如何启动一个容器,那么我们可以学学如何生成一个镜像。 ","date":"2023-11-14","objectID":"/docker-cmd/:0:1","tags":null,"title":"docker常用命令以及镜像打包","uri":"/docker-cmd/"},{"categories":["Docker"],"content":"dockerfile 要实现自己编制一个镜像,我们就得学会dockerfile文件的编写规则。 当然,由于篇幅,这里仅仅提供一些简单的dockerfile文件编写的介绍,学会了这些也就够了,更多详细的说明都可以查看官方文档。 FROM ${IMAGE_NAME} 每个dockerfile都得以FROM作为开始,我们自己构建镜像,需要在一个镜像的基础上加以创作。就如同我们平时编写代码一样,我们不可能从0101二进制串来编写代码,我们会借助编译器等基础环境来高效编写程序。 FROM ${IMAGE_NAME}的意义十分简单,你可以简答地理解为docker pull ${IMAGE_NAME},我们之后的一切操作都是在这个镜像内部执行的 WORKDIR ${FILE_NAME} 这个是自选的,WORKDIR相当于申明一个工作目录,当你的容器运行时,会默认在这个目录下运行。如果这个目录不存在,会自动帮你创建这个目录 COPY ${FILE_PATH} ${CONTAINER_FILE_PATH} 这个步骤会将本地主机下的${FILE_PATH}的内容拷贝到容器内部${CONTAINER_FILE_PATH}目录下。我们需要注意的是,如果你想拷贝一个文件,那么你需要指明这个文件在容器内部的名字,即:COPY ./requirement.txt /project/req.txt。如果你拷贝的是文件夹,那么你同样需要指明这个文件在容器内部的名称。 联系一下Linux系统下的mv指令,会有许多相同之处 需要注意的是,有一个ADD指令和COPY指令十分相似,执行的功能也大致相同,但是ADD会自动将压缩文件进行解压,而MOVE不行 RUN ${COMMAND} 这个命令会在搭建docker镜像时执行,它会执行${COMMAND}命令,通常我们用这条指令来为镜像内容进行修改。例如,我们为python代码进行打包时,会将python源代码COPY到镜像内部,然后会执行RUN pip install -r requirements.txt来安装相关包从而能够运行python代码 CMD ${COMMAND} 这个部分的命令会在容器启动时执行,需要注意的有几点 你可以写多个CMD,但是只有最后一个CMD会执行 CMD的指令会被docker run附带的运行指令覆盖 COMMND的书写格式应写成字符数组的形式,例如ls -a需写成[\"ls\", \"-a\"] ENTRYPOINT ${COMMAND} 这个指令和CMD指令十分相似,但是有一点不同,ENTRYPOINT不会被docker run后的运行指令覆盖,而是会将docker run后面的参数传递给ENTRYPOINT。具体的示例可看这个博文Docker从入门到精通——CMD与ENTRYPOINT区别 。这里就不再赘述。 ok我们大概介绍完了dockerfile需要了解的地方,现在我们来自己编写个dockerfile试试看吧。随便挑一个文件夹,将所需要的文件内容拷贝到这里,然后在这个文件夹下新建一个dockerfile文件(名字就是dockerfile),文件内容如下: 然后在这个文件夹下打开控制台,输入:docker buuild -t cliffwalk:v1 . docker build表示执行构建镜像 -t表示构建的镜像名和镜像tag为cliffwalk:v1 最后的 . 表示使用当前目录下的dockerfile 会出现: 当指令结束后,我们使用docker images就可以看到构建好的镜像信息 由于我们最后使用的是CMD来表示容器运行的指令,所以,我们可以通过docker run的方式来覆盖这条指令来进入容器内部,我们能够看到在/cliffwalking目录下,有我们COPY进去的文件内容: 至此,我们完成了docker的基本命令和镜像打包的学习,最后一章我们会介绍如何高效的启动运行容器镜像 ","date":"2023-11-14","objectID":"/docker-cmd/:0:2","tags":null,"title":"docker常用命令以及镜像打包","uri":"/docker-cmd/"},{"categories":["Docker"],"content":"什么是docker docker可以说是一门技术、一种软件,它能够帮助我们将程序进行打包,从而方便后续的项目部署以及维护。 ","date":"2023-11-14","objectID":"/docker-introduce/:0:1","tags":["docker"],"title":"docker介绍以及安装步骤说明","uri":"/docker-introduce/"},{"categories":["Docker"],"content":"为什么要用docker 场景:做项目开发的同学经常会遇到一个问题,就是这个项目能够在本地上运行,但是换一台电脑却会报各种各样的错误,这个时候不得不面对环境配置这一极其麻烦的问题 所以我们说,docker将过去的程序和系统环境的绑定关系给打破了,只要我们的电脑安装了docker,我就可以通过拉取云端的镜像,无需关注环境的搭建就可以实现跨平台的应用部署。我们不再需要面对繁杂的应用上云出现的环境搭建问题,就可以轻松地使用服务。 ","date":"2023-11-14","objectID":"/docker-introduce/:0:2","tags":["docker"],"title":"docker介绍以及安装步骤说明","uri":"/docker-introduce/"},{"categories":["Docker"],"content":"怎样安装docker 安装docker: Windows系统: 在Windows系统,我们需要确保自己的电脑安装了WSL 以管理员身份打开CMD,并敲入: wsl --install 官方文档:安装 WSL | Microsoft Learn ,可查阅官方文档获得更多的信息。 之后我们前往docker-desktop官方网站Docker Desktop: The #1 Containerization Tool for Developers | Docker 下载最新的docker-desktop即可。 需要注意的是,在使用安装好的docker-desktop之前,我们需要修改几个配置 调整docker虚拟机的位置,不然根据初始设置,会把C盘占满 修改docker的镜像源信息,不然在docker pull拉取镜像时,会特别慢: 将其中的内容替换成下述信息: { \"builder\": { \"gc\": { \"defaultKeepStorage\": \"20GB\", \"enabled\": true } }, \"experimental\": false, \"features\": { \"buildkit\": true }, \"registry-mirrors\": [ \"http://hub-mirror.c.163.com\", \"https://mirror.ccs.tencentyun.com\" ] } 或者直接修改C:\\Users\\{{ USERNAME }}\\.docker\\daemon.json内容为上述信息,重启docker。 判断是否安装成功,在CMD中,敲入: docker version 出现如下信息即可: docker之所以需要在Linux环境下使用,是源于在使用go进行编写docker时,需要借助Linux系统的Namespace和Cgroup隔离机制,所以我们在Windows上运行docker实质上是在Windows上安装了一个虚拟机WSL,在虚拟机的内部执行docker Ubuntu系统: 更新索引:sudo apt-get update 安装:sudo apt install docker.io 修改镜像源:在/etc/docker目录下创建daemon.json,在里面添加: { \"registry-mirrors\": [ \"http://hub-mirror.c.163.com\", \"https://mirror.ccs.tencentyun.com\" ] } 即可 判断是否安装成功,在终端输入:docker --version,出现 即可 CentOS/aliyun服务器: 卸载旧版(如果你之前没有安装过,可跳过这一步) yum remove docker \\ docker-client \\ docker-client-latest \\ docker-common \\ docker-latest \\ docker-latest-logrotate \\ docker-logrotate \\ docker-engine 安装依赖包:yum install -y yum-utils 为了防止是从国外的源下载,所以配置阿里云地址:yum-config-manager --add-repo http://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo 更新依赖:yum update 安装docker:yum install docker-ce docker-ce-cli containerd.io 配置docker镜像源:同样的在/etc/docker目录下创建daemon.json文件,填入镜像源地址信息即可。 启动docker服务:systemctl start docker 判断是否安装完成:控制台输入docker info,看到镜像源信息更改即为安装成功 当然你可以也可以在其他平台上使用docker info来判断镜像源的地址信息是否成功修改 ","date":"2023-11-14","objectID":"/docker-introduce/:0:3","tags":["docker"],"title":"docker介绍以及安装步骤说明","uri":"/docker-introduce/"},{"categories":["Docker"],"content":"一些最基本的概念 仓库Registry:你可以理解为类似代码仓库的东西,只是这里面存放的不是一段段代码,而是一堆镜像,我们可以通过一些指令来获得或上传镜像信息。 镜像Image:它是一种模板,具有创建docker容器的指令。并且一种镜像是得基于另一种镜像才能构建。我们可以把这个理解为虚拟机安装所需的iso文件,只要我有这个iso文件,我就可以打造多个一样的虚拟机环境。当然我们还可以在这个iso的基础上附带一些东西,构成一个新的iso。例如centos分为minimal(最基础的)以及desktop(带有桌面UI的),desktop就是基于minimal,同时又添加了新的东西形成的新的iso文件。 容器Container:它是一种运行实例,每个容器之间是相互隔离互不影响的。同样的,我们举个例子,当你拥有一个iso文件时,你不把它放在VMware中产生一个虚拟机实例,那么这个iso文件也就毫无用处。而你使用同一个iso文件创建的不同虚拟机之间也是不会有任何影响,你不会发现,在A虚拟机中创建了一个文件,B虚拟机中也可以找到。 如果上述的例子还是不能让你理解这三者之间的关系和实际的作用,我们可以拿日常开发做一个例子。仓库Registry就是github.com;镜像就是github.com中的pytorch项目代码,我们可以利用pytorch本身的东西创建一个新的例如predict的项目代码,当然我们也可以上传到github;容器就相当于我们执行这段代码的进程,每个进程之间不会相互影响(不考虑使用共享内存,管道等方式实现进程间的控制)。 ","date":"2023-11-14","objectID":"/docker-introduce/:0:4","tags":["docker"],"title":"docker介绍以及安装步骤说明","uri":"/docker-introduce/"},{"categories":["Docker"],"content":"总结 在这一章中,我们大概介绍了docker的一些基本信息和安装方式,在下一章我们会在ubuntu系统上介绍docker的基本操作。 ","date":"2023-11-14","objectID":"/docker-introduce/:0:5","tags":["docker"],"title":"docker介绍以及安装步骤说明","uri":"/docker-introduce/"},{"categories":["Hugo"],"content":"为什么要用Action来实现自动化部署hugo项目 不采用自动化部署,每次更新项目时,都需要在本地编译,然后上传整个public目录,比较麻烦 不采用自动化部署,没办法管理博客内容以及维护 ","date":"2023-11-12","objectID":"/workflow/:0:1","tags":["github","自动化部署"],"title":"如何利用GitHub的Action实现hugo项目自动化部署","uri":"/workflow/"},{"categories":["Hugo"],"content":"怎样实现 在github上创建 {{ username }}.github.io仓库,在main分支的基础上,新建source分支用于存放hugo的markdown文件,而main分支用于存放编译好后的文件 前期准备: 在本地,使用ssh工具创建公私钥 ssh-keygen -t rsa -N '' -f ./deploy-key -q 在本地我们会得到deploy-key和deploy-key.pub两个文件 现在需要将ssh密钥信息设置到仓库中: 添加deploy-key信息 deploy key的title随便填写即可,我们将生成的deploy-key.pub(即公钥)的内容填到key中 设置action的密钥: 新建一个actions secret 其中Name的内容填为ACTIONS_DEPLOY_KEY,Secret的内容填为我们生成的deploy-key(即私钥)的内容。 在source分支中添加.github/workflows/main.yml文件,用于定义Github Actions,其中main.yaml文件内容为 name: auto-update-page # 定义action名称 on: push: branches: - source # 当source分支出现push行为时,执行这个action jobs: deploy: runs-on: ubuntu-22.04 # 定义环境以及之后的行为 steps: - uses: actions/checkout@v4 with: submodules: true # Fetch Hugo themes (true OR recursive) fetch-depth: 0 # Fetch all history for .GitInfo and .Lastmod - name: Setup Hugo uses: peaceiris/actions-hugo@v2 with: hugo-version: \"0.120.3\" extended: true - name: Build run: hugo --minify - name: Deploy uses: peaceiris/actions-gh-pages@v3 with: deploy_key: ${{ secrets.ACTIONS_DEPLOY_KEY }} publish_dir: ./public external_repository: plutolove233/plutolove233.github.io publish_branch: main # 将编译好的public里面的内容部署到对应仓库的分支上 设置完之后,我们查看仓库的Actions,我们能够看到: 看到如上内容,就意味着我们自动化部署的工作流完成了,我们可以尝试修改部分markdown文件,上传之后我们可以看到我们的博客内容也会发生改变 ","date":"2023-11-12","objectID":"/workflow/:0:2","tags":["github","自动化部署"],"title":"如何利用GitHub的Action实现hugo项目自动化部署","uri":"/workflow/"},{"categories":["机器学习之路"],"content":"问题描述 给定一个4×12的网格环境,如下图所示,其中黄色区域表示悬崖,我们不能经过,蓝色是我们的目标区域,我们希望能求出每个状态如何利用最少的步骤到达目的点。 ","date":"2023-11-10","objectID":"/cliffwalking/:0:1","tags":["pytorch","DQN","RL"],"title":"DQN算法示例:CliffWalking问题","uri":"/cliffwalking/"},{"categories":["机器学习之路"],"content":"编写工具类代码 # rl_utils.py def one_hot(index, num_size=10): return [1 if i == index else 0 for i in range(num_size)] class ReplayBuffer: def __init__(self, capacity): self.buffer = collections.deque(maxlen=capacity) def add(self, state, action, reward, next_state, done): self.buffer.append((state, action, reward, next_state, done)) def sample(self, batch_size): transitions = random.sample(self.buffer, batch_size) state, action, reward, next_state, done = zip(*transitions) return np.array(state), action, reward, np.array(next_state), done def size(self): return len(self.buffer) def train_off_policy_agent(env, agent, num_episodes, replay_buffer, minimal_size, batch_size): return_list = [] for i in range(10): with tqdm(total=int(num_episodes / 10), desc='Iteration %d' % i) as pbar: for i_episode in range(int(num_episodes / 10)): episode_return = 0 state = env.reset()[0] done = False while not done: action = agent.take_action(state) next_state, reward, done, _, _ = env.step(action) replay_buffer.add(state, action, reward, next_state, done) state = next_state episode_return += reward if replay_buffer.size() \u003e minimal_size: b_s, b_a, b_r, b_ns, b_d = replay_buffer.sample(batch_size) transition_dict = {'states': b_s, 'actions': b_a, 'next_states': b_ns, 'rewards': b_r, 'dones': b_d} agent.update(transition_dict) return_list.append(episode_return) if (i_episode + 1) % 10 == 0: pbar.set_postfix({'episode': '%d' % (num_episodes / 10 * i + i_episode + 1), 'return': '%.3f' % np.mean(return_list[-10:])}) pbar.update(1) return return_list ","date":"2023-11-10","objectID":"/cliffwalking/:0:2","tags":["pytorch","DQN","RL"],"title":"DQN算法示例:CliffWalking问题","uri":"/cliffwalking/"},{"categories":["机器学习之路"],"content":"制定规则 我们规定,每走一步所得的reward是-1,走进悬崖的reward是-100,到达目标地点的reward是100。 我们编写环境代码如下: # grid_world.py class GridWorld: \"\"\" 悬崖问题的环境建立 \"\"\" def __init__(self, row=4, col=12): self.action_num = 4 self.col = col self.row = row self.x = row - 1 self.y = 0 self.statue_dim = self.row*self.col self.action_dim = 4 def step(self, action) -\u003e (list, float, bool, bool, dict): action = int(action) # 假设一个3*3的情景,目的是中间方块,求最短距离 self.x = min(self.row - 1, max(0, self.x + change[action][0])) self.y = min(self.col - 1, max(0, self.y + change[action][1])) next_state = self.x * self.col + self.y reward = -1 done = False if self.x == self.row - 1 and self.y \u003e 0: done = True if self.y != self.col - 1: # enter into hole reward = -100 else: # reach the final reward = 100 nextState = np.array(rl.one_hot(next_state, num_size=self.row * self.col)) return nextState, reward, done, False, {} def reset(self) -\u003e (list, dict): self.x = self.row - 1 self.y = 0 label = self.x * self.col + self.y state = np.array(rl.one_hot(label, num_size=self.row * self.col)) return state, {} 在上述代码中,我们实现了环境的定义,同时,我们注意到,我们返回的状态采用了独热编码,这也就意味着,之后我们构建网络时,输入的维度应该是48。 ","date":"2023-11-10","objectID":"/cliffwalking/:0:3","tags":["pytorch","DQN","RL"],"title":"DQN算法示例:CliffWalking问题","uri":"/cliffwalking/"},{"categories":["机器学习之路"],"content":"编写神经网络用来拟合QSA target 我们需要知道,这个网络的输入是48,输出应该是action个数。 # grid_world.py class CliffWalkNet(nn.Module): \"\"\" 悬崖问题神经网络构造 \"\"\" def __init__(self, in_dim, hidden_dim, out_dim): super(CliffWalkNet, self).__init__() self.layer = nn.Sequential( nn.Linear(in_dim, hidden_dim), nn.ReLU(), nn.Linear(hidden_dim, hidden_dim), nn.ReLU(), nn.Linear(hidden_dim, out_dim) ) def forward(self, x): return self.layer(x) 在里面使用三个全连接层,以及两个隐藏层。 ","date":"2023-11-10","objectID":"/cliffwalking/:0:4","tags":["pytorch","DQN","RL"],"title":"DQN算法示例:CliffWalking问题","uri":"/cliffwalking/"},{"categories":["机器学习之路"],"content":"编写DQN核心代码 我们每个动作采用ε贪婪策略,以及规定每次操作执行的更新方法 class CliffWalkDQLearning: def __init__(self, in_dim, hidden_dim, action_dim): self.ActionNumber = action_dim self.Q_net = CliffWalkNet(in_dim, hidden_dim, action_dim).to(device) self.Q_target = CliffWalkNet(in_dim, hidden_dim, action_dim).to(device) self.optimizer = torch.optim.Adam(self.Q_net.parameters(), lr=learning_rate) self.criterion = nn.MSELoss() self.count = 0 self.QSA = [[0, 0, 0, 0] for _ in range(in_dim)] def take_action(self, state): if np.random.random() \u003c epsilon: return np.random.randint(self.ActionNumber) state = torch.tensor(state, dtype=torch.float).to(device) return self.Q_net(state).argmax().item() def update(self, transition: dict): # load data from dictionary states_ = torch.tensor(transition.get(\"states\"), dtype=torch.float).to(device) actions_ = torch.tensor(transition.get(\"actions\")).view(-1, 1).to(device) rewards_ = torch.tensor(transition.get(\"rewards\"), dtype=torch.float).view(-1, 1).to(device) next_states_ = torch.tensor(transition.get(\"next_states\"), dtype=torch.float).to(device) dones_ = torch.tensor(transition.get(\"dones\"), dtype=torch.float).view(-1, 1).to(device) q_values = self.Q_net(states_).gather(1, actions_) # 每列收集对应actions_拟合的value值 max_qvalue = self.Q_target(next_states_).max(1)[0].view(-1, 1) q_target = rewards_ + gamma * max_qvalue * (1-dones_) loss = self.criterion(q_values, q_target) self.optimizer.zero_grad() loss.backward() self.optimizer.step() if self.count % update_times == 0: net_state_dict = self.Q_net.state_dict() self.Q_target.load_state_dict(net_state_dict) self.count += 1 ","date":"2023-11-10","objectID":"/cliffwalking/:0:5","tags":["pytorch","DQN","RL"],"title":"DQN算法示例:CliffWalking问题","uri":"/cliffwalking/"},{"categories":["机器学习之路"],"content":"执行代码,进行模型训练 我们可以直接利用rl_utils里面的(off_policy)工具,进行DQN的训练 env = GridWorld() dim = env.row*env.col agent = CliffWalkDQLearning(in_dim=dim, hidden_dim=int(dim/2), action_dim=4) buffer = rl.ReplayBuffer(1000) random.seed(0) np.random.seed(0) torch.manual_seed(0) return_list = rl.train_off_policy_agent(env, agent, num_episodes, buffer, minimal_size, batch_size) episodes_list = list(range(len(return_list))) plt.plot(episodes_list, return_list) plt.xlabel('Episodes') plt.ylabel('Returns') plt.title('DQN on {}'.format(\"Cliff Walking\")) plt.show() ","date":"2023-11-10","objectID":"/cliffwalking/:0:6","tags":["pytorch","DQN","RL"],"title":"DQN算法示例:CliffWalking问题","uri":"/cliffwalking/"},{"categories":["机器学习之路"],"content":"模型保存 torch.save({\"CliffWalking\": agent.Q_target.state_dict()}, \"./cliff_walking.pth\") # load model model = CliffWalkNet(in_dim=dim, hidden_dim=int(dim/2), out_dim=4).to(device) state_dict = torch.load(\"./cliff_walking.pth\")[\"CliffWalking\"] model.load_state_dict(state_dict) res = [[0, 0, 0, 0] for _ in range(dim)] for i in range(4): for j in range(12): state = rl.one_hot(i*12+j, dim) input_ = torch.tensor(state, dtype=torch.float).to(device) target = model(input_).cpu().detach().numpy().tolist() res[i*12+j] = target # print(\"state(%d,%d):\" % (i, j)) # print(target) # print() print_agent(res, env, action_graph, list(range(37, 47)), [47]) 我们将训练好的模型保存,之后导入model,这样我们就可以避免重复的模型训练,直接计算每个结点的QSA,寻找每个状态的action最大值,从而得出训练后的策略结果。 由上述结果,我们发现每个状态都已经找到了最优的策略,这个模型的结果还算可以。 ","date":"2023-11-10","objectID":"/cliffwalking/:0:7","tags":["pytorch","DQN","RL"],"title":"DQN算法示例:CliffWalking问题","uri":"/cliffwalking/"},{"categories":["机器学习之路"],"content":"基本概念: 对于强化学习,我们一般会分成智能体(agent),环境(通过智能体的状态和动作反馈信息)两大部分,我们现在介绍一些名词,从而有利于之后学习的理解。在这一部分,我们会结合一个3×3的网格寻路来形象化介绍下述概念。 我们最初位置是在(1,1),我们希望能够在最短的时间内到达蓝色网格位置。 State:agent相对于environment的状态。例如以s1表示网格(1,1),s9表示网格(3,3)。所有的状态会构成S(State Set)。 Action:每个状态可能采取的行动。例如向上(a1)、下(a3)、左(a4)、右(a2)走,这所有的动作会构成A(Action Set)。 state transition:agent从s1→s4的过程。 state transition probability:用条件概率的形式,表示转移的情况。例如p(s4|s1, a3)=0.5表示从状态s1执行动作a3(向下)到达状态s4的可能性是0.5(因为还可以向右到达s2的可能)。 Reward:一个数字,用于表示一个state transition行为所产生的激励。 forbidden area:进入但是会产生损失的区域(Reward\u003c0)。例如图示中的黄色区域。 Policy:让agent知道在某个状态该如何行动,也是用条件概率的形式来表示,例如π(a1|s1)=0,π(a2|s1)=0.5。同时我们应该还满足下述条件: $$ \\sum_{a\\in A}\\pi(a|s_j) = 1 $$ Trajectory:是整个状态行为链,例如s1(a2)→s2(a3)→s5(a3)→s8(a2)→s9(a4)→s8… Return:表示整个Trajectory上Reward之和 Discounted Rate:由上可知,一个Trajectory可能是无穷无尽的,所以我们需要定义γ来种折扣Reward,从而使整个过程的Return不会太大。 Discounted Return:表示整个Trajectory的折扣回报总和,具体表示为: $$ Discounted\\ Return=\\sum_{i=1}^\\infin \\gamma^{i-1}r_i $$ 我们可以知道,这个结果最终会收敛到一个值。并且γ越小,那么Discounted Return会与最早的reward相关(更近视),γ越大也就越远视 Episode:一个有限步的Trajectory,并且最终状态时terminal state(目标状态,即图中蓝色方块)。 ","date":"2023-11-10","objectID":"/concept/:0:1","tags":["RL","math"],"title":"强化学习数学知识总结","uri":"/concept/"},{"categories":["机器学习之路"],"content":"马尔可夫过程 随机过程 随机过程研究的对象是,随时间改变的随机现象。例如本例子中,时刻1在状态s1,时刻2可能就会在s2获s4。这一过程是随机的,我们通常用 $$ P(s_{t+1}|a_{t+1},s_{t}, a_{t}, s_{t-1}…a_1, s_0) $$ 来表示下一个时刻,状态的可能性 马尔可夫性质 当前状态只与上一个状态有关时,我们称这个过程具有马尔可夫性质,即 $$ P(s_{t+1}|a_{t+1},s_{t}, a_{t}, s_{t-1}…a_1, s_0)=P(s_{t+1}|a_{t+1}, s_t) $$ 在本例子中,每个时刻到达某个网格的状态可能完全取决于上一时刻所在状态和动作,所以我们这个网格寻路的例子是满足马尔可夫性质的。 几个新的概率 $$ P(s’|s, a)表示s\\stackrel{a}{\\longrightarrow}s’即从s执行动作a到达s’的概率\\\\ P(r |s, a)表示s\\stackrel{a}{\\longrightarrow}s’这一过程获得reward=r的概率\\\\ $$ 新的概念 State Value $$ v_{\\pi}(s)=\\mathbb{E}(G_t|S=s)\\\\ $$ 回报矩阵G:表示当前时刻所有状态所获得的discounted return $$ G_t=R_{t+1}+\\gamma G_{t+1} $$ 贝尔曼公式 我们将上述式子进行展开以及归纳,可得贝尔曼公式: $$ \\forall s\\in S,v_{\\pi}(s)=\\mathbb{E}(G_t|S_t=s)=\\mathbb{E}(R_{t+1}|S_t=s)+\\gamma\\mathbb{E}(G_{t+1}|S_t=s)\\\\ =\\sum_{a\\in A}\\pi(a|s)\\sum_rp(r|s,a)r + \\gamma\\sum_{s’}\\mathbb{E}(G_{t+1}|S_{t+1}=s’,S_t=s)P(s’|s)\\\\ =\\sum_{a\\in A}\\pi(a|s)\\sum_rp(r|s,a)r +\\gamma\\sum_{s’}v_{\\pi}(s’)\\sum_{a\\in A}P(s’|s, a)\\pi(a|s) $$ 我们将这个公式化成矩阵的形式 $$ \\mathbf{v_{\\pi}}=\\mathbf{r_{\\pi}}+\\gamma\\mathbf{P_{\\pi}}\\mathbf{v_{\\pi}} $$ 其中: $$ r_{\\pi}(s)=\\sum_{a\\in A}\\pi(a|s)\\sum_rp(r|s,a)r\\\\ P_{\\pi}(s’|s)=\\sum_{a \\in A}p(s’|s,a)\\pi(a|s),即s\\rarr s’的可能性\\\\ \\mathbf{P_{\\pi(i,j)}} = P_{\\pi}(s_j|s_i) $$ 我们该如何求解——求state value 求矩阵的逆 迭代的方式: 先随机设置v0初始值 利用 $$ \\mathbf{v_{k+1}}=\\mathbf{r_{\\pi}}+\\gamma\\mathbf{P_{\\pi}}\\mathbf{v_{k}} $$ 不断进行迭代,当k→∞时,vk→vπ ","date":"2023-11-10","objectID":"/concept/:0:2","tags":["RL","math"],"title":"强化学习数学知识总结","uri":"/concept/"},{"categories":["机器学习之路"],"content":"安装pytorch 我们以英伟达显卡为例,我们需要知道自己电脑对应cuda版本信息: 在控制台输入nvidia-smi我们可以看到对应cuda版本的信息内容: 从上图中,我们可知:当前我们CUDA的版本是11.2,之后我们去pytorch官方网页 查询对应pytorch的版本信息,按照给出的安装命令信息安装即可。 由于pytorch官网并没有cuda11.2的安装方法,所以我选择安装cuda11.1的环境 pip install torch==1.9.1+cu111 torchvision==0.10.1+cu111 torchaudio==0.9.1 -f https://download.pytorch.org/whl/torch_stable.html 我们可以通过一下代码来判断pytorch是否已经正确安装以及识别到显卡驱动 import torch print(torch.cuda.is_available()) print(torch.cuda.current_device()) print(torch.cuda.device_count()) print(torch.cuda.get_device_name(0)) 执行结果如下: ","date":"2023-11-09","objectID":"/linear/:0:1","tags":["pytorch","mechine learning"],"title":"pytorch实现线性拟合","uri":"/linear/"},{"categories":["机器学习之路"],"content":"编写代码 现在我们可以进行代码编写过程了 构造数据集,我们假设拟合的结果是 $$ y=w_1\\times x_1 + w_2\\times x_2 + b\\\\ 即y=\\begin{bmatrix} w_1 \u0026 w_2 \\\\ \\end{bmatrix} \\begin{bmatrix} x_1\\\\ x_2\\\\ \\end{bmatrix}+b $$ 为了能够体现机器学习拟合的有效性,我们在生成数据点时会增加一些噪声,从而体现出数据的混乱 代码实现: def synthetic_data(w, b, num_examples): \"\"\"生成 y = Xw + b + 噪声。\"\"\" X = torch.normal(0, 1, (num_examples, len(w))) # X是均值为0,方差为1的随机数。有num_examples个样本,列就是len(w) y = torch.matmul(X, w) + b # Y是X与w的乘积(matmul == mm,矩阵相乘)加上偏差b y += torch.normal(0, 0.01, y.shape) # 加入一个噪音,均值为0,方差为0.01,形状与y相同 return X, y.reshape((-1, 1)) # 其中,y返回一个列向量。-1表示自动计算,1表示固定,即列向量为1 true_w = torch.tensor([2, -3.4]) true_b = 4.2 features, labels = synthetic_data(true_w, true_b, 1000) 我们可以绘制图像,用于显示我们构造的数据: 然后我们将数据进行封装成Dataset class LinearDataSet(Dataset): def __init__(self, x, y): self.X = torch.FloatTensor(x) self.Y = torch.FloatTensor(y) def __getitem__(self, index): return self.X[index], self.Y[index] def __len__(self): return len(self.X) trainSet = LinearDataSet(features, labels) trainLoader = DataLoader(trainSet, batch_size=10, shuffle=True, pin_memory=True) 我们现在编写网络,以及编写一个训练过程: class LinearNetWork(nn.Module): def __init__(self, n_feature): super(LinearNetWork, self).__init__() self.layers = nn.Sequential( nn.Linear(n_feature, 1) # 定义一个最简单线性全连接层 ) def forward(self, x): y = self.layers(x) return y def Trainer(train_loader: DataLoader, model: LinearNetWork): criterion = nn.MSELoss() # 定义一个均方误差 optimizer = torch.optim.SGD(model.parameters(), lr=0.03) # 定义一个优化器 for i in range(epoch): model.train() for x, y in train_loader: optimizer.zero_grad() # 采用梯度下降,每次训练都需要将梯度信息清零 x, y = x.to(device), y.to(device) pred = model(x) loss = criterion(pred, y) print(\"loss=\", loss.detach().item()) loss.backward() # 梯度回退 optimizer.step() model = LinearNetWork(2).to(device) # 因为我们总共就两个变量,所以我们传入的特征信息为2 Trainer(trainLoader, model) print(model.state_dict()) 这样我们一个最简单的神经网络已经构成,我们执行上述代码查看网络每层的信息: ","date":"2023-11-09","objectID":"/linear/:0:2","tags":["pytorch","mechine learning"],"title":"pytorch实现线性拟合","uri":"/linear/"},{"categories":["机器学习之路"],"content":"总结 总的来说,这次这个任务还是比较简单的,我们构造一个简单的神经网络,完成了最简单的线性拟合的问题,我们可以在这里面一窥机器学习的基本过程。我们需要获取数据、数据处理、构造网络、进行训练、调整参数,然后不断循环往复,从而得到一个加好的结果。 ","date":"2023-11-09","objectID":"/linear/:0:3","tags":["pytorch","mechine learning"],"title":"pytorch实现线性拟合","uri":"/linear/"},{"categories":null,"content":"个人介绍 出生于2002年6月,喜欢go语言,LOL的蒟蒻。 目前在华北电力大学就读软件工程本科学业。 目前正在学习强化学习,希望能够通过编写个人博客来增加我学习的动力,以及记录我学习的过程。 博客会更新什么 本科以及之后研究生学习的过程和结果 go语言相关的内容,会包括最新特性,以及我觉得有用的东西 docker应用示例,我希望通过几个简短的章节能够让你了解docker如何部署一个项目 或许还会有一些每周心得体会什么的? 我也希望能够有人在阅读我的博客之后,发送email(yizhigopher@163.com)和我交流,希望能够在各位大佬变得更强,无限进步。 ","date":"2023-11-08","objectID":"/about/:0:0","tags":null,"title":"About","uri":"/about/"},{"categories":null,"content":"这是我的第一份博客,用于测试 package main import \"fmt\" func main(){ fmt.Println(\"Hello World\") } $$ \\sum_{i=0}^{100}i = 5050 $$ ","date":"2023-11-08","objectID":"/first_post/:0:0","tags":["hugo"],"title":"First_post","uri":"/first_post/"}] \ No newline at end of file diff --git a/index.xml b/index.xml index 211b70d..a93667c 100644 --- a/index.xml +++ b/index.xml @@ -1 +1 @@ -yizhigopher的博客http://plutolove233.github.io/变得更强,无线进步Hugo -- gohugo.iozh-CNyizhigopher@163.com (yizhigopher)yizhigopher@163.com (yizhigopher)Fri, 08 Nov 2024 22:39:44 +0800Todotree插件设置http://plutolove233.github.io/todotree/Mon, 22 Jan 2024 14:48:37 +0800yizhigopherhttp://plutolove233.github.io/todotree/1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70Go指针、函数、结构体、接口以及方法http://plutolove233.github.io/go-basic2/Fri, 08 Nov 2024 22:39:44 +0800yizhigopherhttp://plutolove233.github.io/go-basic2/指针 和C语言一样,Go中也存在指针,一般采用如下方式来获取变量的地址: 1 2 a := 12 var ptr *int = &a 如果我们想要获取该指针变量指向地址对应的内容时,我Go基本运算符、流程控制、数组与切片以及字典http://plutolove233.github.io/go-basic/Fri, 08 Nov 2024 15:24:06 +0800yizhigopherhttp://plutolove233.github.io/go-basic/上一章节我们已经简单介绍了一个最简单的go程序以及关于变量以及常量的定义。本章节将会继续极少go的基础知识。 基本运算符 go的基础运算符包含 数Hello Go!http://plutolove233.github.io/go-hello/Wed, 30 Oct 2024 10:16:11 +0800yizhigopherhttp://plutolove233.github.io/go-hello/在完成了上个章节的Go编译器安装后,本章节将介绍Go的基础语法,并通过几个例子进行展示。 Hello World 学习每个语言的第一步永远是学会如何在控制台打印😊如何实现多个个容器之间的通讯http://plutolove233.github.io/docker-connection/Tue, 29 Oct 2024 19:44:43 +0800yizhigopherhttp://plutolove233.github.io/docker-connection/为了进行多个容器之间通讯的实验,首先我们需要创建两个服务,分别打包成不同的镜像,并通过docker-compose进行容器编排。本次实验的项Go语言环境安装http://plutolove233.github.io/go-install/Tue, 08 Oct 2024 15:47:15 +0800yizhigopherhttp://plutolove233.github.io/go-install/在本节中,我们将首先简单介绍下如何安装Go语言环境,以开启Go语言学习之路!我们将简单介绍在Windows环境,Linux环境以及Mac环境Normalizing Flow介绍http://plutolove233.github.io/nf-introduce/Sat, 06 Jan 2024 14:08:15 +0800yizhigopherhttp://plutolove233.github.io/nf-introduce/归一化流基本内容 什么是标准化流:基于一系列可逆变换,生成样本对应的分布 标准化流的基本原理: 变量替换:$p_x(x)=p_z(f_\theta阿里云PAI平台搭建pytorch-cuda11.4环境http://plutolove233.github.io/install/Thu, 14 Dec 2023 15:11:01 +0800yizhigopherhttp://plutolove233.github.io/install/创建实例 在阿里云平台上创建实例: 其中我们镜像选择为公共的Ubuntu20.04镜像,当然你也可以选择其他配置好的镜像从而简化之后的步骤。 我们go语言小技巧--编译约束http://plutolove233.github.io/tips-1/Mon, 04 Dec 2023 19:13:11 +0800yizhigopherhttp://plutolove233.github.io/tips-1/简介 我们能在go代码中添加一段注释,用来表示一种编译约束 1 //go:build [tags] 通过这个并且配合go build -tags="..."的方式,就能实容器运行方式http://plutolove233.github.io/docker-run/Thu, 30 Nov 2023 14:37:10 +0800yizhigopherhttp://plutolove233.github.io/docker-run/如何运行容器 在之前的几篇文章中,我们学习了如何使用docker的指令以及如何进行容器打包,接下来我们就需要了解,该如何运行容器。 最简单的方式 \ No newline at end of file +yizhigopher的博客http://plutolove233.github.io/变得更强,无线进步Hugo -- gohugo.iozh-CNyizhigopher@163.com (yizhigopher)yizhigopher@163.com (yizhigopher)Sun, 10 Nov 2024 20:11:24 +0800Todotree插件设置http://plutolove233.github.io/todotree/Mon, 22 Jan 2024 14:48:37 +0800yizhigopherhttp://plutolove233.github.io/todotree/在插件中找到Todo Tree,点击设置按钮 找到在settings.json中编辑更多 将上述代码复制到settings.json中即可。 1 2 3vscode Go环境设置http://plutolove233.github.io/go/Sun, 10 Nov 2024 20:11:24 +0800yizhigopherhttp://plutolove233.github.io/go/首先你需要按照这个教程 安装好Go环境。 之后在vscode插件商城中,搜寻Go,选择第一个进行安装。 安装好插件后,按下Ctrl+Shift+PGo指针、函数、结构体、接口以及方法http://plutolove233.github.io/go-basic2/Fri, 08 Nov 2024 22:39:44 +0800yizhigopherhttp://plutolove233.github.io/go-basic2/指针 和C语言一样,Go中也存在指针,一般采用如下方式来获取变量的地址: 1 2 a := 12 var ptr *int = &a 如果我们想要获取该指针变量指向地址对应的内容时,我Go基本运算符、流程控制、数组与切片以及字典http://plutolove233.github.io/go-basic/Fri, 08 Nov 2024 15:24:06 +0800yizhigopherhttp://plutolove233.github.io/go-basic/上一章节我们已经简单介绍了一个最简单的go程序以及关于变量以及常量的定义。本章节将会继续极少go的基础知识。 基本运算符 go的基础运算符包含 数Hello Go!http://plutolove233.github.io/go-hello/Wed, 30 Oct 2024 10:16:11 +0800yizhigopherhttp://plutolove233.github.io/go-hello/在完成了上个章节的Go编译器安装后,本章节将介绍Go的基础语法,并通过几个例子进行展示。 Hello World 学习每个语言的第一步永远是学会如何在控制台打印😊如何实现多个个容器之间的通讯http://plutolove233.github.io/docker-connection/Tue, 29 Oct 2024 19:44:43 +0800yizhigopherhttp://plutolove233.github.io/docker-connection/为了进行多个容器之间通讯的实验,首先我们需要创建两个服务,分别打包成不同的镜像,并通过docker-compose进行容器编排。本次实验的项Go语言环境安装http://plutolove233.github.io/go-install/Tue, 08 Oct 2024 15:47:15 +0800yizhigopherhttp://plutolove233.github.io/go-install/在本节中,我们将首先简单介绍下如何安装Go语言环境,以开启Go语言学习之路!我们将简单介绍在Windows环境,Linux环境以及Mac环境Normalizing Flow介绍http://plutolove233.github.io/nf-introduce/Sat, 06 Jan 2024 14:08:15 +0800yizhigopherhttp://plutolove233.github.io/nf-introduce/归一化流基本内容 什么是标准化流:基于一系列可逆变换,生成样本对应的分布 标准化流的基本原理: 变量替换:$p_x(x)=p_z(f_\theta阿里云PAI平台搭建pytorch-cuda11.4环境http://plutolove233.github.io/install/Thu, 14 Dec 2023 15:11:01 +0800yizhigopherhttp://plutolove233.github.io/install/创建实例 在阿里云平台上创建实例: 其中我们镜像选择为公共的Ubuntu20.04镜像,当然你也可以选择其他配置好的镜像从而简化之后的步骤。 我们go语言小技巧--编译约束http://plutolove233.github.io/tips-1/Mon, 04 Dec 2023 19:13:11 +0800yizhigopherhttp://plutolove233.github.io/tips-1/简介 我们能在go代码中添加一段注释,用来表示一种编译约束 1 //go:build [tags] 通过这个并且配合go build -tags="..."的方式,就能实 \ No newline at end of file diff --git a/page/2/index.html b/page/2/index.html index 91dc6cc..c2b02d8 100644 --- a/page/2/index.html +++ b/page/2/index.html @@ -14,11 +14,11 @@ -

Normalizing Flow介绍

Go语言环境安装

在本节中,我们将首先简单介绍下如何安装Go语言环境,以开启Go语言学习之路!我们将简单介绍在Windows环境,Linux环境以及Mac环境

Normalizing Flow介绍

归一化流基本内容 什么是标准化流:基于一系列可逆变换,生成样本对应的分布 标准化流的基本原理: 变量替换:$p_x(x)=p_z(f_\theta

阿里云PAI平台搭建pytorch-cuda11.4环境

创建实例 在阿里云平台上创建实例: 其中我们镜像选择为公共的Ubuntu20.04镜像,当然你也可以选择其他配置好的镜像从而简化之后的步骤。 我们

go语言小技巧--编译约束

简介 我们能在go代码中添加一段注释,用来表示一种编译约束 1 //go:build [tags] 通过这个并且配合go build -tags="..."的方式,就能实

容器运行方式

如何运行容器 在之前的几篇文章中,我们学习了如何使用docker的指令以及如何进行容器打包,接下来我们就需要了解,该如何运行容器。 最简单的方式

docker常用命令以及镜像打包

基本操作 在上节,我们已经安装好docker,但是就如同写代码一样,我们安装好了相关环境,但是并没有代码能够让我们运行,我们又不会写代码(自己

docker介绍以及安装步骤说明

什么是docker docker可以说是一门技术、一种软件,它能够帮助我们将程序进行打包,从而方便后续的项目部署以及维护。 为什么要用docke
published on  Docker
基本操作 在上节,我们已经安装好docker,但是就如同写代码一样,我们安装好了相关环境,但是并没有代码能够让我们运行,我们又不会写代码(自己
Read More
\ No newline at end of file diff --git a/page/3/index.html b/page/3/index.html index 621cfac..84ed73d 100644 --- a/page/3/index.html +++ b/page/3/index.html @@ -14,7 +14,8 @@ -

如何利用GitHub的Action实现hugo项目自动化部署

docker介绍以及安装步骤说明

什么是docker docker可以说是一门技术、一种软件,它能够帮助我们将程序进行打包,从而方便后续的项目部署以及维护。 为什么要用docke

如何利用GitHub的Action实现hugo项目自动化部署

为什么要用Action来实现自动化部署hugo项目 不采用自动化部署,每次更新项目时,都需要在本地编译,然后上传整个public目录,比较麻烦

DQN算法示例:CliffWalking问题

问题描述 给定一个4×12的网格环境,如下图所示,其中黄色区域表示悬崖,我们不能经过,蓝色是我们的目标区域,我们希望能求出每个状态如何利用最少

强化学习数学知识总结

基本概念: 对于强化学习,我们一般会分成智能体(agent),环境(通过智能体的状态和动作反馈信息)两大部分,我们现在介绍一些名词,从而有利于

pytorch实现线性拟合

Cancel文章标签分类 -

All Posts

2024

如何实现多个个容器之间的通讯 diff --git a/posts/index.xml b/posts/index.xml index b07ac93..60e856d 100644 --- a/posts/index.xml +++ b/posts/index.xml @@ -1 +1 @@ -All Posts - yizhigopher的博客http://plutolove233.github.io/posts/All Posts | yizhigopher的博客Hugo -- gohugo.iozh-CNyizhigopher@163.com (yizhigopher)yizhigopher@163.com (yizhigopher)Fri, 08 Nov 2024 22:39:44 +0800Todotree插件设置http://plutolove233.github.io/todotree/Mon, 22 Jan 2024 14:48:37 +0800yizhigopherhttp://plutolove233.github.io/todotree/1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70Go指针、函数、结构体、接口以及方法http://plutolove233.github.io/go-basic2/Fri, 08 Nov 2024 22:39:44 +0800yizhigopherhttp://plutolove233.github.io/go-basic2/指针 和C语言一样,Go中也存在指针,一般采用如下方式来获取变量的地址: 1 2 a := 12 var ptr *int = &a 如果我们想要获取该指针变量指向地址对应的内容时,我Go基本运算符、流程控制、数组与切片以及字典http://plutolove233.github.io/go-basic/Fri, 08 Nov 2024 15:24:06 +0800yizhigopherhttp://plutolove233.github.io/go-basic/上一章节我们已经简单介绍了一个最简单的go程序以及关于变量以及常量的定义。本章节将会继续极少go的基础知识。 基本运算符 go的基础运算符包含 数Hello Go!http://plutolove233.github.io/go-hello/Wed, 30 Oct 2024 10:16:11 +0800yizhigopherhttp://plutolove233.github.io/go-hello/在完成了上个章节的Go编译器安装后,本章节将介绍Go的基础语法,并通过几个例子进行展示。 Hello World 学习每个语言的第一步永远是学会如何在控制台打印😊如何实现多个个容器之间的通讯http://plutolove233.github.io/docker-connection/Tue, 29 Oct 2024 19:44:43 +0800yizhigopherhttp://plutolove233.github.io/docker-connection/为了进行多个容器之间通讯的实验,首先我们需要创建两个服务,分别打包成不同的镜像,并通过docker-compose进行容器编排。本次实验的项Go语言环境安装http://plutolove233.github.io/go-install/Tue, 08 Oct 2024 15:47:15 +0800yizhigopherhttp://plutolove233.github.io/go-install/在本节中,我们将首先简单介绍下如何安装Go语言环境,以开启Go语言学习之路!我们将简单介绍在Windows环境,Linux环境以及Mac环境Normalizing Flow介绍http://plutolove233.github.io/nf-introduce/Sat, 06 Jan 2024 14:08:15 +0800yizhigopherhttp://plutolove233.github.io/nf-introduce/归一化流基本内容 什么是标准化流:基于一系列可逆变换,生成样本对应的分布 标准化流的基本原理: 变量替换:$p_x(x)=p_z(f_\theta阿里云PAI平台搭建pytorch-cuda11.4环境http://plutolove233.github.io/install/Thu, 14 Dec 2023 15:11:01 +0800yizhigopherhttp://plutolove233.github.io/install/创建实例 在阿里云平台上创建实例: 其中我们镜像选择为公共的Ubuntu20.04镜像,当然你也可以选择其他配置好的镜像从而简化之后的步骤。 我们go语言小技巧--编译约束http://plutolove233.github.io/tips-1/Mon, 04 Dec 2023 19:13:11 +0800yizhigopherhttp://plutolove233.github.io/tips-1/简介 我们能在go代码中添加一段注释,用来表示一种编译约束 1 //go:build [tags] 通过这个并且配合go build -tags="..."的方式,就能实容器运行方式http://plutolove233.github.io/docker-run/Thu, 30 Nov 2023 14:37:10 +0800yizhigopherhttp://plutolove233.github.io/docker-run/如何运行容器 在之前的几篇文章中,我们学习了如何使用docker的指令以及如何进行容器打包,接下来我们就需要了解,该如何运行容器。 最简单的方式 \ No newline at end of file +All Posts - yizhigopher的博客http://plutolove233.github.io/posts/All Posts | yizhigopher的博客Hugo -- gohugo.iozh-CNyizhigopher@163.com (yizhigopher)yizhigopher@163.com (yizhigopher)Sun, 10 Nov 2024 20:11:24 +0800Todotree插件设置http://plutolove233.github.io/todotree/Mon, 22 Jan 2024 14:48:37 +0800yizhigopherhttp://plutolove233.github.io/todotree/在插件中找到Todo Tree,点击设置按钮 找到在settings.json中编辑更多 将上述代码复制到settings.json中即可。 1 2 3vscode Go环境设置http://plutolove233.github.io/go/Sun, 10 Nov 2024 20:11:24 +0800yizhigopherhttp://plutolove233.github.io/go/首先你需要按照这个教程 安装好Go环境。 之后在vscode插件商城中,搜寻Go,选择第一个进行安装。 安装好插件后,按下Ctrl+Shift+PGo指针、函数、结构体、接口以及方法http://plutolove233.github.io/go-basic2/Fri, 08 Nov 2024 22:39:44 +0800yizhigopherhttp://plutolove233.github.io/go-basic2/指针 和C语言一样,Go中也存在指针,一般采用如下方式来获取变量的地址: 1 2 a := 12 var ptr *int = &a 如果我们想要获取该指针变量指向地址对应的内容时,我Go基本运算符、流程控制、数组与切片以及字典http://plutolove233.github.io/go-basic/Fri, 08 Nov 2024 15:24:06 +0800yizhigopherhttp://plutolove233.github.io/go-basic/上一章节我们已经简单介绍了一个最简单的go程序以及关于变量以及常量的定义。本章节将会继续极少go的基础知识。 基本运算符 go的基础运算符包含 数Hello Go!http://plutolove233.github.io/go-hello/Wed, 30 Oct 2024 10:16:11 +0800yizhigopherhttp://plutolove233.github.io/go-hello/在完成了上个章节的Go编译器安装后,本章节将介绍Go的基础语法,并通过几个例子进行展示。 Hello World 学习每个语言的第一步永远是学会如何在控制台打印😊如何实现多个个容器之间的通讯http://plutolove233.github.io/docker-connection/Tue, 29 Oct 2024 19:44:43 +0800yizhigopherhttp://plutolove233.github.io/docker-connection/为了进行多个容器之间通讯的实验,首先我们需要创建两个服务,分别打包成不同的镜像,并通过docker-compose进行容器编排。本次实验的项Go语言环境安装http://plutolove233.github.io/go-install/Tue, 08 Oct 2024 15:47:15 +0800yizhigopherhttp://plutolove233.github.io/go-install/在本节中,我们将首先简单介绍下如何安装Go语言环境,以开启Go语言学习之路!我们将简单介绍在Windows环境,Linux环境以及Mac环境Normalizing Flow介绍http://plutolove233.github.io/nf-introduce/Sat, 06 Jan 2024 14:08:15 +0800yizhigopherhttp://plutolove233.github.io/nf-introduce/归一化流基本内容 什么是标准化流:基于一系列可逆变换,生成样本对应的分布 标准化流的基本原理: 变量替换:$p_x(x)=p_z(f_\theta阿里云PAI平台搭建pytorch-cuda11.4环境http://plutolove233.github.io/install/Thu, 14 Dec 2023 15:11:01 +0800yizhigopherhttp://plutolove233.github.io/install/创建实例 在阿里云平台上创建实例: 其中我们镜像选择为公共的Ubuntu20.04镜像,当然你也可以选择其他配置好的镜像从而简化之后的步骤。 我们go语言小技巧--编译约束http://plutolove233.github.io/tips-1/Mon, 04 Dec 2023 19:13:11 +0800yizhigopherhttp://plutolove233.github.io/tips-1/简介 我们能在go代码中添加一段注释,用来表示一种编译约束 1 //go:build [tags] 通过这个并且配合go build -tags="..."的方式,就能实 \ No newline at end of file diff --git a/sitemap.xml b/sitemap.xml index b01cc19..1871111 100644 --- a/sitemap.xml +++ b/sitemap.xml @@ -1 +1 @@ -http://plutolove233.github.io/todotree/2024-01-22T14:48:37+08:00weekly0.5http://plutolove233.github.io/categories/2024-11-08T22:39:44+08:00weekly1http://plutolove233.github.io/tags/golang/2024-11-08T22:39:44+08:00weekly1http://plutolove233.github.io/categories/golang%E4%B9%8B%E8%B7%AF/2024-11-08T22:39:44+08:00weekly1http://plutolove233.github.io/go-basic2/2024-11-08T22:39:44+08:00weekly1http://plutolove233.github.io/posts/2024-11-08T22:39:44+08:00weekly1http://plutolove233.github.io/tags/study/2024-11-08T22:39:44+08:00weekly1http://plutolove233.github.io/tags/2024-11-08T22:39:44+08:00weekly1http://plutolove233.github.io/2024-11-08T22:39:44+08:00weekly1http://plutolove233.github.io/go-basic/2024-11-08T15:24:06+08:00weekly1http://plutolove233.github.io/go-hello/2024-10-30T10:16:11+08:00weekly0.9http://plutolove233.github.io/tags/docker/2024-10-29T19:44:43+08:00weekly0.9http://plutolove233.github.io/categories/docker/2024-10-29T19:44:43+08:00weekly0.9http://plutolove233.github.io/tags/go/2024-10-29T19:44:43+08:00weekly0.9http://plutolove233.github.io/docker-connection/2024-10-29T19:44:43+08:00weekly0.9http://plutolove233.github.io/go-install/2024-10-08T15:47:15+08:00weekly0.6http://plutolove233.github.io/tags/extension/2024-01-22T14:48:37+08:00weekly0.5http://plutolove233.github.io/tags/vscode/2024-01-22T14:48:37+08:00weekly0.5http://plutolove233.github.io/categories/vscode%E6%8F%92%E4%BB%B6%E9%85%8D%E7%BD%AE%E8%AE%BE%E7%BD%AE/2024-01-22T14:48:37+08:00weekly0.5http://plutolove233.github.io/nf-introduce/2024-01-06T14:08:15+08:00weekly0.5http://plutolove233.github.io/tags/cuda/2023-12-14T15:11:01+08:00weekly0.5http://plutolove233.github.io/categories/cuda/2023-12-14T15:11:01+08:00weekly0.5http://plutolove233.github.io/tags/%E5%AE%89%E8%A3%85%E6%95%99%E7%A8%8B/2023-12-14T15:11:01+08:00weekly0.5http://plutolove233.github.io/install/2023-12-14T15:11:01+08:00weekly0.5http://plutolove233.github.io/categories/golang%E5%B0%8F%E6%8A%80%E5%B7%A7/2023-12-04T19:13:11+08:00weekly0.5http://plutolove233.github.io/tips-1/2023-12-04T19:13:11+08:00weekly0.5http://plutolove233.github.io/tags/tips/2023-12-04T19:13:11+08:00weekly0.5http://plutolove233.github.io/docker-run/2023-11-30T14:37:10+08:00weekly0.5http://plutolove233.github.io/docker-cmd/2023-11-14T21:11:15+08:00weekly0.5http://plutolove233.github.io/docker-introduce/2023-11-14T19:45:12+08:00weekly0.5http://plutolove233.github.io/tags/github/2023-11-12T15:39:14+08:00weekly0.5http://plutolove233.github.io/categories/hugo/2023-11-12T15:39:14+08:00weekly0.5http://plutolove233.github.io/workflow/2023-11-12T15:39:14+08:00weekly0.5http://plutolove233.github.io/tags/%E8%87%AA%E5%8A%A8%E5%8C%96%E9%83%A8%E7%BD%B2/2023-11-12T15:39:14+08:00weekly0.5http://plutolove233.github.io/tags/dqn/2023-11-10T20:48:46+08:00weekly0.5http://plutolove233.github.io/cliffwalking/2023-11-10T20:48:46+08:00weekly0.5http://plutolove233.github.io/tags/pytorch/2023-11-10T20:48:46+08:00weekly0.5http://plutolove233.github.io/tags/rl/2023-11-10T20:48:46+08:00weekly0.5http://plutolove233.github.io/categories/%E6%9C%BA%E5%99%A8%E5%AD%A6%E4%B9%A0%E4%B9%8B%E8%B7%AF/2023-11-10T20:48:46+08:00weekly0.5http://plutolove233.github.io/tags/math/2023-11-10T15:24:04+08:00weekly0.5http://plutolove233.github.io/concept/2023-11-10T15:24:04+08:00weekly0.5http://plutolove233.github.io/tags/mechine-learning/2023-11-09T21:59:54+08:00weekly0.5http://plutolove233.github.io/linear/2023-11-09T21:59:54+08:00weekly0.5http://plutolove233.github.io/about/2023-11-08T18:58:39+08:00weekly0.5http://plutolove233.github.io/first_post/2023-11-08T18:18:13+08:00weekly0.5http://plutolove233.github.io/tags/hugo/2023-11-08T18:18:13+08:00weekly0.5 \ No newline at end of file +http://plutolove233.github.io/todotree/2024-01-22T14:48:37+08:00weekly0.5http://plutolove233.github.io/categories/2024-11-10T20:11:24+08:00weekly1http://plutolove233.github.io/tags/golang/2024-11-10T20:11:24+08:00weekly1http://plutolove233.github.io/posts/2024-11-10T20:11:24+08:00weekly1http://plutolove233.github.io/tags/2024-11-10T20:11:24+08:00weekly1http://plutolove233.github.io/go/2024-11-10T20:11:24+08:00weekly1http://plutolove233.github.io/categories/vscode%E6%8F%92%E4%BB%B6%E9%85%8D%E7%BD%AE%E8%AE%BE%E7%BD%AE/2024-11-10T20:11:24+08:00weekly1http://plutolove233.github.io/2024-11-10T20:11:24+08:00weekly1http://plutolove233.github.io/categories/golang%E4%B9%8B%E8%B7%AF/2024-11-08T22:39:44+08:00weekly1http://plutolove233.github.io/go-basic2/2024-11-08T22:39:44+08:00weekly1http://plutolove233.github.io/tags/study/2024-11-08T22:39:44+08:00weekly1http://plutolove233.github.io/go-basic/2024-11-08T15:24:06+08:00weekly1http://plutolove233.github.io/go-hello/2024-10-30T10:16:11+08:00weekly0.9http://plutolove233.github.io/tags/docker/2024-10-29T19:44:43+08:00weekly0.9http://plutolove233.github.io/categories/docker/2024-10-29T19:44:43+08:00weekly0.9http://plutolove233.github.io/tags/go/2024-10-29T19:44:43+08:00weekly0.9http://plutolove233.github.io/docker-connection/2024-10-29T19:44:43+08:00weekly0.9http://plutolove233.github.io/go-install/2024-10-08T15:47:15+08:00weekly0.6http://plutolove233.github.io/tags/extension/2024-01-22T14:48:37+08:00weekly0.5http://plutolove233.github.io/tags/vscode/2024-01-22T14:48:37+08:00weekly0.5http://plutolove233.github.io/nf-introduce/2024-01-06T14:08:15+08:00weekly0.5http://plutolove233.github.io/tags/cuda/2023-12-14T15:11:01+08:00weekly0.5http://plutolove233.github.io/categories/cuda/2023-12-14T15:11:01+08:00weekly0.5http://plutolove233.github.io/tags/%E5%AE%89%E8%A3%85%E6%95%99%E7%A8%8B/2023-12-14T15:11:01+08:00weekly0.5http://plutolove233.github.io/install/2023-12-14T15:11:01+08:00weekly0.5http://plutolove233.github.io/categories/golang%E5%B0%8F%E6%8A%80%E5%B7%A7/2023-12-04T19:13:11+08:00weekly0.5http://plutolove233.github.io/tips-1/2023-12-04T19:13:11+08:00weekly0.5http://plutolove233.github.io/tags/tips/2023-12-04T19:13:11+08:00weekly0.5http://plutolove233.github.io/docker-run/2023-11-30T14:37:10+08:00weekly0.5http://plutolove233.github.io/docker-cmd/2023-11-14T21:11:15+08:00weekly0.5http://plutolove233.github.io/docker-introduce/2023-11-14T19:45:12+08:00weekly0.5http://plutolove233.github.io/tags/github/2023-11-12T15:39:14+08:00weekly0.5http://plutolove233.github.io/categories/hugo/2023-11-12T15:39:14+08:00weekly0.5http://plutolove233.github.io/workflow/2023-11-12T15:39:14+08:00weekly0.5http://plutolove233.github.io/tags/%E8%87%AA%E5%8A%A8%E5%8C%96%E9%83%A8%E7%BD%B2/2023-11-12T15:39:14+08:00weekly0.5http://plutolove233.github.io/tags/dqn/2023-11-10T20:48:46+08:00weekly0.5http://plutolove233.github.io/cliffwalking/2023-11-10T20:48:46+08:00weekly0.5http://plutolove233.github.io/tags/pytorch/2023-11-10T20:48:46+08:00weekly0.5http://plutolove233.github.io/tags/rl/2023-11-10T20:48:46+08:00weekly0.5http://plutolove233.github.io/categories/%E6%9C%BA%E5%99%A8%E5%AD%A6%E4%B9%A0%E4%B9%8B%E8%B7%AF/2023-11-10T20:48:46+08:00weekly0.5http://plutolove233.github.io/tags/math/2023-11-10T15:24:04+08:00weekly0.5http://plutolove233.github.io/concept/2023-11-10T15:24:04+08:00weekly0.5http://plutolove233.github.io/tags/mechine-learning/2023-11-09T21:59:54+08:00weekly0.5http://plutolove233.github.io/linear/2023-11-09T21:59:54+08:00weekly0.5http://plutolove233.github.io/about/2023-11-08T18:58:39+08:00weekly0.5http://plutolove233.github.io/first_post/2023-11-08T18:18:13+08:00weekly0.5http://plutolove233.github.io/tags/hugo/2023-11-08T18:18:13+08:00weekly0.5 \ No newline at end of file diff --git a/tags/extension/index.xml b/tags/extension/index.xml index 8215f0f..b9b8170 100644 --- a/tags/extension/index.xml +++ b/tags/extension/index.xml @@ -1 +1 @@ -extension - Tag - yizhigopher的博客http://plutolove233.github.io/tags/extension/extension - Tag - yizhigopher的博客Hugo -- gohugo.iozh-CNyizhigopher@163.com (yizhigopher)yizhigopher@163.com (yizhigopher)Mon, 22 Jan 2024 14:48:37 +0800Todotree插件设置http://plutolove233.github.io/todotree/Mon, 22 Jan 2024 14:48:37 +0800yizhigopherhttp://plutolove233.github.io/todotree/1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 \ No newline at end of file +extension - Tag - yizhigopher的博客http://plutolove233.github.io/tags/extension/extension - Tag - yizhigopher的博客Hugo -- gohugo.iozh-CNyizhigopher@163.com (yizhigopher)yizhigopher@163.com (yizhigopher)Mon, 22 Jan 2024 14:48:37 +0800Todotree插件设置http://plutolove233.github.io/todotree/Mon, 22 Jan 2024 14:48:37 +0800yizhigopherhttp://plutolove233.github.io/todotree/在插件中找到Todo Tree,点击设置按钮 找到在settings.json中编辑更多 将上述代码复制到settings.json中即可。 1 2 3 \ No newline at end of file diff --git a/tags/golang/index.html b/tags/golang/index.html index a4a2bbc..180d7c2 100644 --- a/tags/golang/index.html +++ b/tags/golang/index.html @@ -8,7 +8,8 @@
Cancel
文章标签分类 -

 golang

2024

Go语言环境安装 diff --git a/tags/golang/index.xml b/tags/golang/index.xml index 809e504..7c000b0 100644 --- a/tags/golang/index.xml +++ b/tags/golang/index.xml @@ -1 +1 @@ -golang - Tag - yizhigopher的博客http://plutolove233.github.io/tags/golang/golang - Tag - yizhigopher的博客Hugo -- gohugo.iozh-CNyizhigopher@163.com (yizhigopher)yizhigopher@163.com (yizhigopher)Fri, 08 Nov 2024 22:39:44 +0800Go指针、函数、结构体、接口以及方法http://plutolove233.github.io/go-basic2/Fri, 08 Nov 2024 22:39:44 +0800yizhigopherhttp://plutolove233.github.io/go-basic2/指针 和C语言一样,Go中也存在指针,一般采用如下方式来获取变量的地址: 1 2 a := 12 var ptr *int = &a 如果我们想要获取该指针变量指向地址对应的内容时,我Go基本运算符、流程控制、数组与切片以及字典http://plutolove233.github.io/go-basic/Fri, 08 Nov 2024 15:24:06 +0800yizhigopherhttp://plutolove233.github.io/go-basic/上一章节我们已经简单介绍了一个最简单的go程序以及关于变量以及常量的定义。本章节将会继续极少go的基础知识。 基本运算符 go的基础运算符包含 数Hello Go!http://plutolove233.github.io/go-hello/Wed, 30 Oct 2024 10:16:11 +0800yizhigopherhttp://plutolove233.github.io/go-hello/在完成了上个章节的Go编译器安装后,本章节将介绍Go的基础语法,并通过几个例子进行展示。 Hello World 学习每个语言的第一步永远是学会如何在控制台打印😊Go语言环境安装http://plutolove233.github.io/go-install/Tue, 08 Oct 2024 15:47:15 +0800yizhigopherhttp://plutolove233.github.io/go-install/在本节中,我们将首先简单介绍下如何安装Go语言环境,以开启Go语言学习之路!我们将简单介绍在Windows环境,Linux环境以及Mac环境go语言小技巧--编译约束http://plutolove233.github.io/tips-1/Mon, 04 Dec 2023 19:13:11 +0800yizhigopherhttp://plutolove233.github.io/tips-1/简介 我们能在go代码中添加一段注释,用来表示一种编译约束 1 //go:build [tags] 通过这个并且配合go build -tags="..."的方式,就能实 \ No newline at end of file +golang - Tag - yizhigopher的博客http://plutolove233.github.io/tags/golang/golang - Tag - yizhigopher的博客Hugo -- gohugo.iozh-CNyizhigopher@163.com (yizhigopher)yizhigopher@163.com (yizhigopher)Sun, 10 Nov 2024 20:11:24 +0800vscode Go环境设置http://plutolove233.github.io/go/Sun, 10 Nov 2024 20:11:24 +0800yizhigopherhttp://plutolove233.github.io/go/首先你需要按照这个教程 安装好Go环境。 之后在vscode插件商城中,搜寻Go,选择第一个进行安装。 安装好插件后,按下Ctrl+Shift+PGo指针、函数、结构体、接口以及方法http://plutolove233.github.io/go-basic2/Fri, 08 Nov 2024 22:39:44 +0800yizhigopherhttp://plutolove233.github.io/go-basic2/指针 和C语言一样,Go中也存在指针,一般采用如下方式来获取变量的地址: 1 2 a := 12 var ptr *int = &a 如果我们想要获取该指针变量指向地址对应的内容时,我Go基本运算符、流程控制、数组与切片以及字典http://plutolove233.github.io/go-basic/Fri, 08 Nov 2024 15:24:06 +0800yizhigopherhttp://plutolove233.github.io/go-basic/上一章节我们已经简单介绍了一个最简单的go程序以及关于变量以及常量的定义。本章节将会继续极少go的基础知识。 基本运算符 go的基础运算符包含 数Hello Go!http://plutolove233.github.io/go-hello/Wed, 30 Oct 2024 10:16:11 +0800yizhigopherhttp://plutolove233.github.io/go-hello/在完成了上个章节的Go编译器安装后,本章节将介绍Go的基础语法,并通过几个例子进行展示。 Hello World 学习每个语言的第一步永远是学会如何在控制台打印😊Go语言环境安装http://plutolove233.github.io/go-install/Tue, 08 Oct 2024 15:47:15 +0800yizhigopherhttp://plutolove233.github.io/go-install/在本节中,我们将首先简单介绍下如何安装Go语言环境,以开启Go语言学习之路!我们将简单介绍在Windows环境,Linux环境以及Mac环境go语言小技巧--编译约束http://plutolove233.github.io/tips-1/Mon, 04 Dec 2023 19:13:11 +0800yizhigopherhttp://plutolove233.github.io/tips-1/简介 我们能在go代码中添加一段注释,用来表示一种编译约束 1 //go:build [tags] 通过这个并且配合go build -tags="..."的方式,就能实 \ No newline at end of file diff --git a/tags/index.html b/tags/index.html index e1af9e9..22dfdd8 100644 --- a/tags/index.html +++ b/tags/index.html @@ -8,5 +8,5 @@
Cancel
文章标签分类 -
\ No newline at end of file diff --git a/tags/index.xml b/tags/index.xml index 8da17bb..92f1189 100644 --- a/tags/index.xml +++ b/tags/index.xml @@ -1 +1 @@ -Tags - Tag - yizhigopher的博客http://plutolove233.github.io/tags/Tags - Tag - yizhigopher的博客Hugo -- gohugo.iozh-CNyizhigopher@163.com (yizhigopher)yizhigopher@163.com (yizhigopher)Fri, 08 Nov 2024 22:39:44 +0800golanghttp://plutolove233.github.io/tags/golang/Fri, 08 Nov 2024 22:39:44 +0800yizhigopherhttp://plutolove233.github.io/tags/golang/studyhttp://plutolove233.github.io/tags/study/Fri, 08 Nov 2024 22:39:44 +0800yizhigopherhttp://plutolove233.github.io/tags/study/dockerhttp://plutolove233.github.io/tags/docker/Tue, 29 Oct 2024 19:44:43 +0800yizhigopherhttp://plutolove233.github.io/tags/docker/gohttp://plutolove233.github.io/tags/go/Tue, 29 Oct 2024 19:44:43 +0800yizhigopherhttp://plutolove233.github.io/tags/go/extensionhttp://plutolove233.github.io/tags/extension/Mon, 22 Jan 2024 14:48:37 +0800yizhigopherhttp://plutolove233.github.io/tags/extension/vscodehttp://plutolove233.github.io/tags/vscode/Mon, 22 Jan 2024 14:48:37 +0800yizhigopherhttp://plutolove233.github.io/tags/vscode/cudahttp://plutolove233.github.io/tags/cuda/Thu, 14 Dec 2023 15:11:01 +0800yizhigopherhttp://plutolove233.github.io/tags/cuda/安装教程http://plutolove233.github.io/tags/%E5%AE%89%E8%A3%85%E6%95%99%E7%A8%8B/Thu, 14 Dec 2023 15:11:01 +0800yizhigopherhttp://plutolove233.github.io/tags/%E5%AE%89%E8%A3%85%E6%95%99%E7%A8%8B/tipshttp://plutolove233.github.io/tags/tips/Mon, 04 Dec 2023 19:13:11 +0800yizhigopherhttp://plutolove233.github.io/tags/tips/githubhttp://plutolove233.github.io/tags/github/Sun, 12 Nov 2023 15:39:14 +0800yizhigopherhttp://plutolove233.github.io/tags/github/ \ No newline at end of file +Tags - Tag - yizhigopher的博客http://plutolove233.github.io/tags/Tags - Tag - yizhigopher的博客Hugo -- gohugo.iozh-CNyizhigopher@163.com (yizhigopher)yizhigopher@163.com (yizhigopher)Sun, 10 Nov 2024 20:11:24 +0800golanghttp://plutolove233.github.io/tags/golang/Sun, 10 Nov 2024 20:11:24 +0800yizhigopherhttp://plutolove233.github.io/tags/golang/studyhttp://plutolove233.github.io/tags/study/Fri, 08 Nov 2024 22:39:44 +0800yizhigopherhttp://plutolove233.github.io/tags/study/dockerhttp://plutolove233.github.io/tags/docker/Tue, 29 Oct 2024 19:44:43 +0800yizhigopherhttp://plutolove233.github.io/tags/docker/gohttp://plutolove233.github.io/tags/go/Tue, 29 Oct 2024 19:44:43 +0800yizhigopherhttp://plutolove233.github.io/tags/go/extensionhttp://plutolove233.github.io/tags/extension/Mon, 22 Jan 2024 14:48:37 +0800yizhigopherhttp://plutolove233.github.io/tags/extension/vscodehttp://plutolove233.github.io/tags/vscode/Mon, 22 Jan 2024 14:48:37 +0800yizhigopherhttp://plutolove233.github.io/tags/vscode/cudahttp://plutolove233.github.io/tags/cuda/Thu, 14 Dec 2023 15:11:01 +0800yizhigopherhttp://plutolove233.github.io/tags/cuda/安装教程http://plutolove233.github.io/tags/%E5%AE%89%E8%A3%85%E6%95%99%E7%A8%8B/Thu, 14 Dec 2023 15:11:01 +0800yizhigopherhttp://plutolove233.github.io/tags/%E5%AE%89%E8%A3%85%E6%95%99%E7%A8%8B/tipshttp://plutolove233.github.io/tags/tips/Mon, 04 Dec 2023 19:13:11 +0800yizhigopherhttp://plutolove233.github.io/tags/tips/githubhttp://plutolove233.github.io/tags/github/Sun, 12 Nov 2023 15:39:14 +0800yizhigopherhttp://plutolove233.github.io/tags/github/ \ No newline at end of file diff --git a/tags/vscode/index.xml b/tags/vscode/index.xml index 84ad58d..f0367f5 100644 --- a/tags/vscode/index.xml +++ b/tags/vscode/index.xml @@ -1 +1 @@ -vscode - Tag - yizhigopher的博客http://plutolove233.github.io/tags/vscode/vscode - Tag - yizhigopher的博客Hugo -- gohugo.iozh-CNyizhigopher@163.com (yizhigopher)yizhigopher@163.com (yizhigopher)Mon, 22 Jan 2024 14:48:37 +0800Todotree插件设置http://plutolove233.github.io/todotree/Mon, 22 Jan 2024 14:48:37 +0800yizhigopherhttp://plutolove233.github.io/todotree/1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 \ No newline at end of file +vscode - Tag - yizhigopher的博客http://plutolove233.github.io/tags/vscode/vscode - Tag - yizhigopher的博客Hugo -- gohugo.iozh-CNyizhigopher@163.com (yizhigopher)yizhigopher@163.com (yizhigopher)Mon, 22 Jan 2024 14:48:37 +0800Todotree插件设置http://plutolove233.github.io/todotree/Mon, 22 Jan 2024 14:48:37 +0800yizhigopherhttp://plutolove233.github.io/todotree/在插件中找到Todo Tree,点击设置按钮 找到在settings.json中编辑更多 将上述代码复制到settings.json中即可。 1 2 3 \ No newline at end of file diff --git a/todotree/index.html b/todotree/index.html index fb10134..08d6c37 100644 --- a/todotree/index.html +++ b/todotree/index.html @@ -1,5 +1,5 @@ Todotree插件设置 - yizhigopher的博客 -

Contents

Todotree插件设置

Contents -
 1
+

在插件中找到Todo Tree,点击设置按钮

./todo-plugin.png

找到在settings.json中编辑更多

./todo-setting.png

将上述代码复制到settings.json中即可。

 1
  2
  3
  4
@@ -161,5 +161,5 @@
             "iconColour": "#d65d8e",
         },
     },
-

将上述代码复制到settings.json中即可。

\ No newline at end of file diff --git a/todotree/index.md b/todotree/index.md index e3cb0b0..8a7cfd6 100644 --- a/todotree/index.md +++ b/todotree/index.md @@ -1,6 +1,15 @@ # Todotree插件设置 +在插件中找到Todo Tree,点击设置按钮 + +![](./todo-plugin.png) + +找到在`settings.json`中编辑更多 + +![](./todo-setting.png) + +将上述代码复制到`settings.json`中即可。 ```json     "todo-tree.regex.regex": "((%|#|//|