Go语言实现深度学习的正向传播和反向传播

文章目录

  • 开发前言
  • 开发理论
  • 图解理论
  • 数据类型
  • 数学函数
  • 数据节点统一抽象
  • 变量数据节点
  • 常量数据节点
  • 单目运算封装
  • 双目运算封装
  • 算子节点统一抽象
  • 基础算子
  • 加法算子
  • 减法算子
  • 乘法算子
  • 除法算子
  • 指数算子
  • 对数算子
  • 正切算子
  • 正弦算子
  • 余弦算子
  • 数据流图
  • 正向传播
  • 反向传播
  • 正向训练
  • 反向训练
  • 运行示例
  • 开发总结

开发前言

正向传播是指从神经网络的输入层开始,通过逐层计算和传递,将输入数据一直传递到输出层。在每一层中,通过对输入数据进行加权求和并应用激活函数,得到该层的输出。这个过程可以看作是将输入数据在网络中前进(向前传播),直至得到模型的预测结果。

反向传播是指根据模型的预测结果和实际标签之间的差异,从输出层向输入层反向计算梯度,并利用梯度来更新网络参数。

这篇博客我将使用Go语言实现正向传播和反向传播,帮助你理解其底层的运转规律

项目代码使用纯粹的Go语言标准库实现,不借用任何其它第三方库。用轮子是生活,造轮子是信仰。

我是醉墨居士,我们现在开始吧🤗

开发理论

一个数学函数,由一系列数据和一系列运算方式构成,我们将数据对应为数据节点,将运算方式对应为算子节点,这样我们就可以将数学函数转化为由一系列数据节点和一系列算子节点组成的数据流图

正向传递数据流图,不断运算数据,就是正向传播的过程
反向传递数据流图,不断累加梯度,就是反向传播的过程

图解理论

我画了两张图来表示函数3 * pow(x, 2) + 2 * x + 1的正向传播和反向传播的过程

正向传播图解
forward

反向传播图解
backward

数据类型

data/type.go

package datatype Type interface {~int | ~int32 | ~int64 |~uint | ~uint32 | ~uint64 |~float32 | ~float64
}

数学函数

math/math.go

package mathimport ("dl/node"stmath "math"
)func Pow[T node.Type](a, b T) T {return T(stmath.Pow(float64(a), float64(b)))
}func Ln[T node.Type](a T) T {return T(stmath.Log(float64(a)))
}func Tan[T node.Type](a T) T {return T(stmath.Tan(float64(a)))
}func Sin[T node.Type](a T) T {return T(stmath.Sin(float64(a)))
}func Cos[T node.Type](a T) T {return T(stmath.Cos(float64(a)))
}

数据节点统一抽象

fm/datanode.go

type DataNode[T data.Type] interface {Data()TSetData(T)Grad()TsetGrad(T)preNode() CalNode[T]backNodes() *[]CalNode[T]fm()FlowMap[T]Add(DataNode[T]) DataNode[T]Sub(DataNode[T]) DataNode[T]Mul(DataNode[T]) DataNode[T]Div(DataNode[T]) DataNode[T]Pow(DataNode[T]) DataNode[T]Ln() DataNode[T]Tan() DataNode[T]Sin() DataNode[T]Cos() DataNode[T]
}

变量数据节点

package fmimport "dl/data"type varDataNode[T data.Type] struct {data Tgrad Tprenode CalNode[T]backnodes []CalNode[T]flowmap FlowMap[T]
}func (n *varDataNode[T]) Data() T {return n.data
}func (n *varDataNode[T]) SetData(i T) {n.data = i
}func (n *varDataNode[T]) Grad() T {return n.grad
}func (n *varDataNode[T]) setGrad(i T) {n.grad = i
}func (n *varDataNode[T]) preNode() CalNode[T] {return n.prenode
}func (n *varDataNode[T]) backNodes() *[]CalNode[T] {return &n.backnodes
}func (n *varDataNode[T]) fm() FlowMap[T] {return n.flowmap
}func (n *varDataNode[T]) Add(node DataNode[T]) DataNode[T] {return calTwo(newAdd[T](), n, node)
}func (n *varDataNode[T]) Sub(node DataNode[T]) DataNode[T] {return calTwo(newSub[T](), n, node)
}func (n *varDataNode[T]) Mul(node DataNode[T]) DataNode[T] {return calTwo(newMul[T](), n, node)
}func (n *varDataNode[T]) Div(node DataNode[T]) DataNode[T] {return calTwo(newDiv[T](), n, node)
}func (n *varDataNode[T]) Pow(node DataNode[T]) DataNode[T] {return calTwo(newPow[T](), n, node)
}func (n *varDataNode[T]) Ln() DataNode[T] {return calOne(newLn[T](), n)
}func (n *varDataNode[T]) Tan() DataNode[T] {return calOne(newTan[T](), n)
}func (n *varDataNode[T]) Sin() DataNode[T] {return calOne(newSin[T](), n)
}func (n *varDataNode[T]) Cos() DataNode[T] {return calOne(newCos[T](), n)
}

常量数据节点

type constDataNode[T data.Type] struct {data      Tprenode   CalNode[T]backnodes []CalNode[T]flowmap   FlowMap[T]
}func (n *constDataNode[T]) Data() T {return n.data
}func (n *constDataNode[T]) SetData(i T) {n.data = i
}
func (n *constDataNode[T]) Grad() T {return 0
}func (n *constDataNode[T]) setGrad(T) {}func (n *constDataNode[T]) preNode() CalNode[T] {return n.prenode
}func (n *constDataNode[T]) backNodes() *[]CalNode[T] {return &n.backnodes
}func (n *constDataNode[T]) fm() FlowMap[T] {return n.flowmap
}func (n *constDataNode[T]) Add(node DataNode[T]) DataNode[T] {return calTwo(newAdd[T](), n, node)
}func (n *constDataNode[T]) Sub(node DataNode[T]) DataNode[T] {return calTwo(newSub[T](), n, node)
}func (n *constDataNode[T]) Mul(node DataNode[T]) DataNode[T] {return calTwo(newMul[T](), n, node)
}func (n *constDataNode[T]) Div(node DataNode[T]) DataNode[T] {return calTwo(newDiv[T](), n, node)
}func (n *constDataNode[T]) Pow(node DataNode[T]) DataNode[T] {return calTwo(newPow[T](), n, node)
}func (n *constDataNode[T]) Ln() DataNode[T] {return calOne(newLn[T](), n)
}func (n *constDataNode[T]) Tan() DataNode[T] {return calOne(newTan[T](), n)
}func (n *constDataNode[T]) Sin() DataNode[T] {return calOne(newSin[T](), n)
}func (n *constDataNode[T]) Cos() DataNode[T] {return calOne(newCos[T](), n)
}

单目运算封装

func calOne[T data.Type](operation CalNode[T], a DataNode[T]) DataNode[T] {*a.fm().calnodes = append(*a.fm().calnodes, operation)*a.backNodes() = append(*a.backNodes(), operation)res := &varDataNode[T]{prenode: operation,flowmap: a.fm(),}*a.fm().datanodes = append(*a.fm().datanodes, res)operation.CalNode().PreNodes = []DataNode[T]{a}operation.CalNode().BackNode = resreturn res
}

双目运算封装

func calTwo[T data.Type] (operation CalNode[T], a, b DataNode[T]) DataNode[T] {if a.fm() != b.fm() {return nil}*a.fm().calnodes = append(*a.fm().calnodes, operation)*a.backNodes() = append(*a.backNodes(), operation)*b.backNodes() = append(*b.backNodes(), operation)res := &varDataNode[T]{prenode: operation,flowmap: a.fm(),}*a.fm().datanodes = append(*a.fm().datanodes, res)operation.CalNode().PreNodes = []DataNode[T]{a, b}operation.CalNode().BackNode = resreturn res
}

算子节点统一抽象

fm/calnode.go

type CalNode[T data.Type] interface {CalNode() *BaseCalNode[T]Forward()Backward()
}

基础算子

type BaseCalNode[T data.Type] struct {PreNodes []DataNode[T]BackNode DataNode[T]
}

加法算子

type AddNode[T data.Type] BaseCalNode[T]func newAdd[T data.Type]() CalNode[T] {basenode := &BaseCalNode[T]{}return (*AddNode[T])(basenode)
}func (n *AddNode[T]) CalNode() *BaseCalNode[T] {return (*BaseCalNode[T])(n)
}func (n *AddNode[T]) Forward() {n.BackNode.SetData(n.CalNode().PreNodes[0].Data() + n.CalNode().PreNodes[1].Data())
}func (n *AddNode[T]) Backward() {// selfgrad + backgradgrad0 := n.PreNodes[0].Grad() + n.BackNode.Grad()// selfgrad + backgradgrad1 := n.PreNodes[1].Grad() + n.BackNode.Grad()n.PreNodes[0].setGrad(grad0)n.PreNodes[1].setGrad(grad1)
}

减法算子

type SubNode[T data.Type] BaseCalNode[T]func newSub[T data.Type]() CalNode[T] {basenode := &BaseCalNode[T]{}return (*SubNode[T])(basenode)
}func (n *SubNode[T]) CalNode() *BaseCalNode[T] {return (*BaseCalNode[T])(n)
}func (n *SubNode[T]) Forward() {n.BackNode.SetData(n.CalNode().PreNodes[0].Data() - n.CalNode().PreNodes[1].Data())
}func (n *SubNode[T]) Backward() {// selfgrad + backgradgrad0 := n.PreNodes[0].Grad() + n.BackNode.Grad()// selfgrad - backgradgrad1 := n.PreNodes[1].Grad() - n.BackNode.Grad()n.PreNodes[0].setGrad(grad0)n.PreNodes[1].setGrad(grad1)
}

乘法算子

type MulNode[T data.Type] BaseCalNode[T]func newMul[T data.Type]() CalNode[T] {basenode := &BaseCalNode[T]{}return (*MulNode[T])(basenode)
}func (n *MulNode[T]) CalNode() *BaseCalNode[T] {return (*BaseCalNode[T])(n)
}func (n *MulNode[T]) Forward() {n.BackNode.SetData(n.CalNode().PreNodes[0].Data() * n.CalNode().PreNodes[1].Data())
}func (n *MulNode[T]) Backward() {a := n.PreNodes[0].Data()b := n.PreNodes[1].Data()backgrad := n.BackNode.Grad()// selfgrad + (backgrad * b)grad0 := n.PreNodes[0].Grad() + (backgrad * b)// selfgrad + (backgrad * a)grad1 := n.PreNodes[1].Grad() + (backgrad * a)n.PreNodes[0].setGrad(grad0)n.PreNodes[1].setGrad(grad1)
}

除法算子

type DivNode[T data.Type] BaseCalNode[T]func newDiv[T data.Type]() CalNode[T] {basenode := &BaseCalNode[T]{}return (*DivNode[T])(basenode)
}func (n *DivNode[T]) CalNode() *BaseCalNode[T] {return (*BaseCalNode[T])(n)
}func (n *DivNode[T]) Forward() {n.BackNode.SetData(n.CalNode().PreNodes[0].Data() / n.CalNode().PreNodes[1].Data())
}func (n *DivNode[T]) Backward() {a := n.PreNodes[0].Data()b := n.PreNodes[1].Data()backgrad := n.BackNode.Grad()// selfgrad + (backgrad / b)grad0 := n.PreNodes[0].Grad() + (backgrad / b)// selfgrad - (backgrad * a / pow(b, 2))grad1 := n.PreNodes[1].Grad() - (backgrad * a / math.Pow(b, 2))n.PreNodes[0].setGrad(grad0)n.PreNodes[1].setGrad(grad1)
}

指数算子

type PowNode[T data.Type] BaseCalNode[T]func newPow[T data.Type]() CalNode[T] {basenode := &BaseCalNode[T]{}return (*PowNode[T])(basenode)
}func (n *PowNode[T]) CalNode() *BaseCalNode[T] {return (*BaseCalNode[T])(n)
}func (n *PowNode[T]) Forward() {n.BackNode.SetData(math.Pow(n.CalNode().PreNodes[0].Data(), n.CalNode().PreNodes[1].Data()))
}func (n *PowNode[T]) Backward() {a := n.PreNodes[0].Data()b := n.PreNodes[1].Data()backgrad := n.BackNode.Grad()// selfgrad + (backgrad * b * pow(a, b-1))grad0 := n.PreNodes[0].Grad() + (backgrad * b * math.Pow(a, b-1))// selfgrad + (backgrad * pow(a, b) * ln(a))grad1 := n.PreNodes[1].Grad() + (backgrad * math.Pow(a, b) * math.Ln(a))n.PreNodes[0].setGrad(grad0)n.PreNodes[1].setGrad(grad1)
}

对数算子

type LnNode[T data.Type] BaseCalNode[T]func newLn[T data.Type]() CalNode[T] {basenode := &BaseCalNode[T]{}return (*LnNode[T])(basenode)
}func (n *LnNode[T]) CalNode() *BaseCalNode[T] {return (*BaseCalNode[T])(n)
}func (n *LnNode[T]) Forward() {n.BackNode.SetData(math.Ln(n.CalNode().PreNodes[0].Data()))
}func (n *LnNode[T]) Backward() {a := n.PreNodes[0].Data()backgrad := n.BackNode.Grad()// selfgrad + (backgrad / a)grad0 := n.PreNodes[0].Grad() + (backgrad / a)n.PreNodes[0].setGrad(grad0)
}

正切算子

type TanNode[T data.Type] BaseCalNode[T]func newTan[T data.Type]() CalNode[T] {basenode := &BaseCalNode[T]{}return (*TanNode[T])(basenode)
}func (n *TanNode[T]) CalNode() *BaseCalNode[T] {return (*BaseCalNode[T])(n)
}func (n *TanNode[T]) Forward() {n.BackNode.SetData(math.Tan(n.CalNode().PreNodes[0].Data()))
}func (n *TanNode[T]) Backward() {a := n.PreNodes[0].Data()backgrad := n.BackNode.Grad()// selfgrad + (backgrad / pow(cos(a), 2))grad0 := n.PreNodes[0].Grad() + (backgrad / math.Pow(math.Cos(a), 2))n.PreNodes[0].setGrad(grad0)
}

正弦算子

type SinNode[T data.Type] BaseCalNode[T]func newSin[T data.Type]() CalNode[T] {basenode := &BaseCalNode[T]{}return (*SinNode[T])(basenode)
}func (n *SinNode[T]) CalNode() *BaseCalNode[T] {return (*BaseCalNode[T])(n)
}func (n *SinNode[T]) Forward() {n.BackNode.SetData(math.Sin(n.CalNode().PreNodes[0].Data()))
}func (n *SinNode[T]) Backward() {a := n.PreNodes[0].Data()backgrad := n.BackNode.Grad()// selfgrad + (backgrad * cos(a))grad0 := n.PreNodes[0].Grad() + (backgrad * math.Cos(a))n.PreNodes[0].setGrad(grad0)
}

余弦算子

type CosNode[T data.Type] BaseCalNode[T]func newCos[T data.Type]() CalNode[T] {basenode := &BaseCalNode[T]{}return (*CosNode[T])(basenode)
}func (n *CosNode[T]) CalNode() *BaseCalNode[T] {return (*BaseCalNode[T])(n)
}func (n *CosNode[T]) Forward() {n.BackNode.SetData(math.Cos(n.CalNode().PreNodes[0].Data()))
}func (n *CosNode[T]) Backward() {a := n.PreNodes[0].Data()backgrad := n.BackNode.Grad()// selfgrad - (backgrad * sin(a))grad0 := n.PreNodes[0].Grad() - (backgrad * math.Sin(a))n.PreNodes[0].setGrad(grad0)
}

数据流图

fm/flowmap.go

type FlowMap[T data.Type] struct {calnodes  *[]CalNode[T]datanodes *[]DataNode[T]
}func NewFlowMap[T data.Type]() *FlowMap[T] {calnodes := make([]CalNode[T], 0)datanods := make([]DataNode[T], 0)return &FlowMap[T]{calnodes:  &calnodes,datanodes: &datanods,}
}func (m FlowMap[T]) NewData() DataNode[T] {node := &varDataNode[T]{backnodes: make([]CalNode[T], 0),flowmap:   m,}*m.datanodes = append(*m.datanodes, node)return node
}func (m FlowMap[T]) NewConstData(i T) DataNode[T] {return &constDataNode[T]{backnodes: make([]CalNode[T], 0),data:      i,flowmap:   m,}
}

正向传播

func (m FlowMap[T]) Forward() {n := len(*m.calnodes)for i := 0; i < n; i++ {(*m.calnodes)[i].Forward()}
}

反向传播

func (m FlowMap[T]) Backward() {for i := len(*m.datanodes) - 1; i >= 0; i-- {(*m.datanodes)[i].setGrad(0)}n := len(*m.calnodes)-1(*m.calnodes)[n].CalNode().BackNode.setGrad(1)for i := n; i >= 0; i-- {(*m.calnodes)[i].Backward()}
}

正向训练

func (m FlowMap[T]) ForwardWalk(step T) {for i := len(*m.datanodes) - 1; i >= 0; i-- {(*m.datanodes)[i].SetData((*m.datanodes)[i].Data() + (*m.datanodes)[i].Grad() * step)}
}

反向训练

func (m FlowMap[T]) BackwardWalk(step T) {for i := len(*m.datanodes) - 1; i >= 0; i-- {(*m.datanodes)[i].SetData((*m.datanodes)[i].Data() - (*m.datanodes)[i].Grad() * step)}
}

运行示例

我们的运行示例使用理论图解的那个例子: 3 * pow(x, 2) + 2 * x + 1

// 3 * pow(x, 2) + 2 * x + 1
func main() {m := fm.NewFlowMap[float64]()x := m.NewData()three := m.NewConstData(3)two := m.NewConstData(2)one := m.NewConstData(1)res := three.Mul(x.Pow(two)).Add(two.Mul(x)).Add(one)x.SetData(2)m.Forward()m.Backward()// data = 3 * pow(2, 2) + 2 * 2 + 1 = 17// grad = 2 + 6 * x = 14fmt.Println("x=2 -> ", "res.data =", res.Data(), ",", "x.grad =", x.Grad())x.SetData(3)m.Forward()m.Backward()// data = 3 * pow(3, 2) + 2 * 3 + 1 = 34// grad = 2 + 6 * x = 20fmt.Println("x=3 -> ", "res.data =", res.Data(), ",", "x.grad =", x.Grad())x.SetData(4)m.Forward()m.Backward()// data = 3 * pow(4, 2) + 2 * 4 + 1 = 57// grad = 2 + 6 * x = 26fmt.Println("x=4 -> ", "res.data =", res.Data(), ",", "x.grad =", x.Grad())
}

运行结果
result

开发总结

恭喜你,我们一起使用Go语言完成了深度学习的正向传播和反向传播,希望这个项目能让你有所收获😊

我的特色就是用最简单的方式帮助你学会最硬核的知识,一起加油吧❤️

我是醉墨居士,之前这个账号改了好几次名称,从此之后这个账号的名称大概率不会再变动😜

如果有什么错误,请你评论区或者私信我指出,让我们一起进步✌️

请你多多关注我,开发这个项目,并且整理总结,花费了很多的精力,博客热度越高,更新速度越快😎

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

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

相关文章

我的记事本

url uniform resource locator. 统一资源定位符 请求状态码 1XX:信息响应 2XX:成功响应 3XX:重定向消息 4XX:客户端错误响应 5XX:服务器端错误响应 IP地址分类 本机回环IP地址&#xff1a;127.0.0.1 &#xff5e; 127.255.255.254 局域网IP(私网IP) 192.168.0.0 &am…

船舶机电设备振动数据采集监控系统解决方案

船舶运行中&#xff0c;通常需要通过振动数据采集系统对船舶的各个机电设备运行进行监控&#xff0c;有助于在设备故障时快速预警&#xff0c;进行诊断、分析和维护&#xff0c;保证船舶机电设备正常工作&#xff0c;从而确保工作人员及船舶的安全。 船舶各种机电设备会产生大…

vLLM介绍

简介 vLLM 工程github地址 Paged attention论文地址 vLLM开发者介绍 Woosuk Kwon vLLM: A high-throughput and memory-efficient inference and serving engine for LLMs. SkyPilot: A framework for easily and cost effectively running machine learning workloads on …

【模型量化】神经网络量化基础及代码学习总结

1 量化的介绍 量化是减少神经网络计算时间和能耗的最有效的方法之一。在神经网络量化中&#xff0c;权重和激活张量存储在比训练时通常使用的16-bit或32-bit更低的比特精度。当从32-bit降低到8-bit&#xff0c;存储张量的内存开销减少了4倍&#xff0c;矩阵乘法的计算成本则二…

ALNS算法中随机化重要性的评价

文章概述 本研究分析了在海上提货和交付问题中使用的ALNS元启发式算法中的随机化成分。研究者提出了简单的确定性替代方案&#xff0c;并通过实验比较了随机化和确定性成分的性能。结果表明&#xff0c;初始实现的简单确定性替代方案能够与随机化成分的性能相匹配。这项研究为…

IDEA使用git从远程仓库获取项目

将地址填入url中 然后直接clone就行

《Easy3d+Qt+VTK》学习

《Easy3dQtVTK》学习-1、编译与配置 一、编译二、配置注 一、编译 1、 资源下载&#xff1a;easy3d giuhub 2、解压缩 3、用qt打开CMakeLists.txt即可 4、点击项目&#xff0c;选择debug或者release&#xff0c;图中3处可自行选择&#xff0c;因为我的qt版本是6&#xff0c…

Java集合大总结——Collections工具类

简单阐述 参考操作数组的工具类&#xff1a;Arrays&#xff0c;Collections 是一个操作 Set、List 和 Map 等集合的工具类。 常用方法 Collections 中提供了一系列静态的方法对集合元素进行排序、查询和修改等操作&#xff0c;还提供了对集合对象设置不可变、对集合对象实现…

jdbc4.MySQLSyntaxErrorException: Query was empty

出现这种异常的原因&#xff0c;有几个要点 检查sql语句是否正确检查你的条件是否真的被sql使用 背景 delete sql在xml中&#xff0c;赋值list对象&#xff0c;计划进行批量删除的sql&#xff0c;这时出现了异常&#xff0c;检查后&#xff0c;发现这个list竟然是空&#xff…

Linux下的软硬链接

Linux下的软硬链接 Linux下的硬链接和软链接是一种文件系统级别的链接方式&#xff0c;它们允许你在不同的目录中创建指向同一个文件的引用。硬链接和软链接的主要区别在于它们的实现方式和删除方式。 硬链接&#xff08;Hard Link&#xff09;&#xff1a; 硬链接是指向同一…

在linux上如何运用虚拟数据优化器VDO

本章主要介绍虚拟化数据优化器。 什么是虚拟数据优化器VDO 创建VDO设备以节约硬盘空间 16.1 了解什么是VDO VDO全称是Virtual Data Optimize&#xff08;虚拟数据优化)&#xff0c;主要是为了节省硬盘空间。 现在假设有两个文件file1和 file2&#xff0c;大小都是10G。file…

cpu 300% 爆满 内存占用不高 排查

top查询 cpu最高的PID ps -ef | grep PID 查看具体哪一个jar服务 jstack -l PID > ./jstack.log 下载/打印进程的线程栈信息 可以加信息简单分析 或进一步 查看堆内存使用情况 jmap -heap Java进程id jstack.log 信息示例 Full thread dump Java HotSpot(TM) 64-Bit Se…

横向扩展统一存储与备份服务器功能

Infortrend 更新了GS&#xff0c;GSe&#xff0c;GSe Pro统一存储系列的备份服务器功能。该功能降低数据备份成本&#xff0c;并提供灵活的备份策略。通过备份服务器功能&#xff0c;用户可以通过多种途径实现数据备份&#xff0c;包括公有云&#xff08;兼容S3&#xff09;、文…

为内核新增字符驱动模块

1. 放置代码 在./source/driver/char下放置模块文件夹 2. Makefile和Kconfig makefile文件参考 obj-$(AAA_LED) AAA-led.oKconfig文件参考 config AAA_LEDtristate "Phytium macb led control module"depends on XXXdefault mhelpIf you have a n…

C/C++,树算法——二叉树的插入(Insert)算法之源程序

1 文本格式 #include<iostream> using namespace std; // A BTree node class BTreeNode { int* keys; // An array of keys int t; // Minimum degree (defines the range for number of keys) BTreeNode** C; // An array of child pointers int …

.NET中有多少种定时器

.NET中至少有6种定时器&#xff0c;每一种定时器都有它的用途和特点。根据定时器的应用场景&#xff0c;可以分为UI相关的定时器和UI无关的定时器。本文将简单介绍这6种定时器的基本用法和特点。 UI定时器 .NET中的UI定时器主要是WinForm、WPF以及WebForm中的定时器。分别为&am…

dell服务器重启后显示器黑屏

1.硬件层面&#xff1a;观察主机的指示灯 &#xff08;1&#xff09;指示灯偏黄&#xff0c;硬件存在问题&#xff08;内存条有静电&#xff0c;拔出后用橡皮擦擦拭&#xff1b;或GPU松动&#xff09; a.电源指示灯黄&#xff0c;闪烁三下再闪烁一下&#xff0c;扣下主板上的纽…

Python Appium Selenium 查杀进程的实用方法

一、前置说明 在自动化过程中&#xff0c;经常需要在命令行中执行一些操作&#xff0c;比如启动应用、查杀应用等&#xff0c;因此可以封装成一个CommandExecutor来专门处理这些事情。 二、操作步骤 # cmd_util.pyimport logging import os import platform import shutil i…

Java编程中通用的正则表达式(二)

正则表达式&#xff0c;又称正则式、规则表达式、正规表达式、正则模式或简称正则&#xff0c;是一种用来匹配字符串的工具。它是一种字符串模式的表示方法&#xff0c;可以用来检索、替换和验证文本。正则表达式是一个字符串&#xff0c;它描述了一些字符的组合&#xff0c;这…

dockers安装rabbitmq

RabbitMQ: easy to use, flexible messaging and streaming — RabbitMQhttps://www.rabbitmq.com/ Downloading and Installing RabbitMQ — RabbitMQ docker run -it --rm --name rabbitmq -p 5672:5672 -p 15672:15672 rabbitmq:3.12-management 之后参照&#xff1a;dock…