即插即用模块--KANLinear

KAN网络

KAN网络即Kolmogorov-Arnold 网络,是一类基于 Kolmogorov-Arnold 表示定理的神经网络架构,具有强大的非线性表达能力。在相同迭代次数下超越传统MLP,不仅训练速度更快,收敛性更好,而且在拟合复杂函数时的精度也明显提高。

这是一个即插即用模块–KANLinear,使用时import这个代码文件,然后模型中的nn.Linear换成这个KANLinear即可

import torch
import torch.nn.functional as F
import math
from torchinfo import summaryclass KANLinear(torch.nn.Module):def __init__(self,in_features,out_features,grid_size=5,spline_order=3,scale_noise=0.01,scale_base=0.3,scale_spline=0.1,enable_standalone_scale_spline=True,base_activation=torch.nn.SiLU,grid_eps=0.02,grid_range=[-1, 1],):super(KANLinear, self).__init__()self.in_features = in_features  # 输入特征数self.out_features = out_features  # 输出特征数self.grid_size = grid_size  # 网格大小self.spline_order = spline_order  # 样条阶数# 计算网格步长,并生成网格#         网格的作用# 定义B样条基函数的位置:# B样条基函数是在特定的支持点上进行计算的,这些支持点由网格确定。# 样条基函数在这些网格点上具有特定的值和形状。# 确定样条基函数的间隔:# 网格步长(h)决定了网格点之间的距离,从而影响样条基函数的平滑程度和覆盖范围。# 网格越密集,样条基函数的分辨率越高,可以更精细地拟合数据。# 构建用于插值和拟合的基础:# 样条基函数利用这些网格点进行插值,能够构建出连续的、平滑的函数。# 通过这些基函数,可以实现输入特征的复杂非线性变换。h = (grid_range[1] - grid_range[0]) / grid_sizegrid = ((torch.arange(-spline_order, grid_size + spline_order + 1) * h+ grid_range[0]).expand(in_features, -1).contiguous())self.register_buffer("grid", grid)  # 注册网格作为模型的buffer#         在PyTorch中,buffer是一种特殊类型的张量,它在模型中起到辅助作用,但不会作为模型参数进行更新。buffer通常用于存储一些在前向和后向传播过程中需要用到的常量或中间结果。buffer和模型参数一样,会被包含在模型的状态字典中(state dictionary),可以与模型一起保存和加载。# register_buffer 的作用# self.register_buffer("grid", grid) 的作用是将 grid 注册为模型的一个buffer。这样做有以下几个好处:# 持久化:buffer会被包含在模型的状态字典中,可以通过 state_dict 方法保存模型时一并保存,加载模型时也会一并恢复。这对于训练和推理阶段都很有用,确保所有相关的常量都能正确加载。# 无需梯度更新:buffer不会在反向传播过程中计算梯度和更新。它们是固定的,只在前向传播中使用。这对于像网格点这样的常量非常适合,因为这些点在训练过程中是固定的,不需要更新。# 易于使用:注册为 buffer 的张量可以像模型参数一样方便地访问和使用,而不必担心它们会被优化器错误地更新。# 初始化网络参数和超参数# 初始化基础权重参数,形状为 (out_features, in_features)self.base_weight = torch.nn.Parameter(torch.Tensor(out_features, in_features))# 初始化样条权重参数,形状为 (out_features, in_features, grid_size + spline_order)self.spline_weight = torch.nn.Parameter(torch.Tensor(out_features, in_features, grid_size + spline_order))# 如果启用了独立缩放样条功能,初始化样条缩放参数,形状为 (out_features, in_features)if enable_standalone_scale_spline:self.spline_scaler = torch.nn.Parameter(torch.Tensor(out_features, in_features))# 噪声缩放系数,用于初始化样条权重时添加噪声self.scale_noise = scale_noise# 基础权重的缩放系数,用于初始化基础权重时的缩放因子self.scale_base = scale_base# 样条权重的缩放系数,用于初始化样条权重时的缩放因子self.scale_spline = scale_spline# 是否启用独立的样条缩放功能self.enable_standalone_scale_spline = enable_standalone_scale_spline# 基础激活函数实例,用于对输入进行非线性变换self.base_activation = base_activation()# 网格更新时的小偏移量,用于在更新网格时引入微小变化,避免过拟合self.grid_eps = grid_epsself.reset_parameters()def reset_parameters(self):# 使用kaiming_uniform_方法初始化基础权重参数base_weight# 这个方法基于何凯明初始化,适用于具有ReLU等非线性激活函数的网络torch.nn.init.kaiming_uniform_(self.base_weight, a=math.sqrt(5) * self.scale_base)with torch.no_grad():# 为样条权重参数spline_weight添加噪声进行初始化noise = ((torch.rand(self.grid_size + 1, self.in_features, self.out_features)- 1 / 2)* self.scale_noise/ self.grid_size)# 计算样条权重参数的初始值,结合了scale_spline的缩放因子self.spline_weight.data.copy_((self.scale_spline if not self.enable_standalone_scale_spline else 1.0)* self.curve2coeff(self.grid.T[self.spline_order : -self.spline_order],noise,))if self.enable_standalone_scale_spline:# torch.nn.init.constant_(self.spline_scaler, self.scale_spline)# 作者此前使用了一般的初始化,效果不佳# 使用kaiming_uniform_方法初始化样条缩放参数spline_scalertorch.nn.init.kaiming_uniform_(self.spline_scaler, a=math.sqrt(5) * self.scale_spline)def b_splines(self, x: torch.Tensor):"""计算给定输入张量的B样条基函数。B样条(B-splines)是一种用于函数逼近和插值的基函数。它们具有局部性、平滑性和数值稳定性等优点,广泛应用于计算机图形学、数据拟合和机器学习中。在这段代码中,B样条基函数用于在输入张量上进行非线性变换,以提高模型的表达能力。在KAN(Kolmogorov-Arnold Networks)模型中,B样条基函数用于将输入特征映射到高维空间中,以便在该空间中进行线性变换。具体来说,B样条基函数能够在给定的网格点上对输入数据进行插值和逼近,从而实现复杂的非线性变换。参数:x (torch.Tensor): 输入张量,形状为 (batch_size, in_features)。返回:torch.Tensor: B样条基函数张量,形状为 (batch_size, in_features, grid_size + spline_order)。"""# 确保输入张量的维度是2,并且其列数等于输入特征数assert x.dim() == 2 and x.size(1) == self.in_features# 获取网格点(包含在buffer中的self.grid)grid: torch.Tensor = (self.grid)  # (in_features, grid_size + 2 * spline_order + 1)# 为了进行逐元素操作,将输入张量的最后一维扩展一维x = x.unsqueeze(-1)# 初始化B样条基函数的基矩阵bases = ((x >= grid[:, :-1]) & (x < grid[:, 1:])).to(x.dtype)# 迭代计算样条基函数for k in range(1, self.spline_order + 1):bases = ((x - grid[:, : -(k + 1)])/ (grid[:, k:-1] - grid[:, : -(k + 1)])* bases[:, :, :-1]) + ((grid[:, k + 1 :] - x)/ (grid[:, k + 1 :] - grid[:, 1:(-k)])* bases[:, :, 1:])# 确保B样条基函数的输出形状正确assert bases.size() == (x.size(0),self.in_features,self.grid_size + self.spline_order,)return bases.contiguous()def curve2coeff(self, x: torch.Tensor, y: torch.Tensor):"""计算插值给定点的曲线的系数。curve2coeff 方法用于计算插值给定点的曲线的系数。这些系数用于表示插值曲线在特定点的形状和位置。具体来说,该方法通过求解线性方程组来找到B样条基函数在给定点上的插值系数。此方法的作用是根据输入和输出点计算B样条基函数的系数,使得这些基函数能够精确插值给定的输入输出点对。这样可以用于拟合数据或在模型中应用非线性变换。参数:x (torch.Tensor): 输入张量,形状为 (batch_size, in_features)。y (torch.Tensor): 输出张量,形状为 (batch_size, in_features, out_features)。返回:torch.Tensor: 系数张量,形状为 (out_features, in_features, grid_size + spline_order)。"""# 确保输入张量的维度是2,并且其列数等于输入特征数assert x.dim() == 2 and x.size(1) == self.in_features# 确保输出张量的形状正确assert y.size() == (x.size(0), self.in_features, self.out_features)# 计算B样条基函数A = self.b_splines(x).transpose(0, 1)  # (in_features, batch_size, grid_size + spline_order)# 转置输出张量B = y.transpose(0, 1)  # (in_features, batch_size, out_features)# 使用线性代数方法求解线性方程组,找到系数solution = torch.linalg.lstsq(A, B).solution  # (in_features, grid_size + spline_order, out_features)# 调整结果的形状result = solution.permute(2, 0, 1)  # (out_features, in_features, grid_size + spline_order)# 确保结果张量的形状正确assert result.size() == (self.out_features,self.in_features,self.grid_size + self.spline_order,)# 返回连续存储的结果张量return result.contiguous()@propertydef scaled_spline_weight(self):"""计算带有缩放因子的样条权重。样条缩放:如果启用了 enable_standalone_scale_spline,则将 spline_scaler 张量扩展一维后与 spline_weight 相乘,否则直接返回 spline_weight。具体来说,spline_weight 是一个三维张量,形状为 (out_features, in_features, grid_size + spline_order)。而 spline_scaler 是一个二维张量,形状为 (out_features, in_features)。为了使 spline_scaler 能够与 spline_weight 逐元素相乘,需要将 spline_scaler 的最后一维扩展,以匹配 spline_weight 的第三维。返回:torch.Tensor: 带有缩放因子的样条权重张量。"""return self.spline_weight * (self.spline_scaler.unsqueeze(-1)if self.enable_standalone_scale_splineelse 1.0)def forward(self, x: torch.Tensor):"""实现模型的前向传播。参数:x (torch.Tensor): 输入张量,形状为 (batch_size, in_features)。返回:torch.Tensor: 输出张量,形状为 (batch_size, out_features)。"""# 确保输入张量的最后一维大小等于输入特征数assert x.size(-1) == self.in_features# 保存输入张量的原始形状original_shape = x.shape# 将输入张量展平为二维x = x.view(-1, self.in_features)# 计算基础线性变换的输出base_output = F.linear(self.base_activation(x), self.base_weight)# 计算B样条基函数的输出spline_output = F.linear(self.b_splines(x).view(x.size(0), -1),self.scaled_spline_weight.view(self.out_features, -1),)# 合并基础输出和样条输出output = base_output + spline_output# 恢复输出张量的形状output = output.view(*original_shape[:-1], self.out_features)return output@torch.no_grad()def update_grid(self, x: torch.Tensor, margin=0.01):"""update_grid 方法用于根据输入数据动态更新B样条的网格点,从而适应输入数据的分布。该方法通过重新计算和调整网格点,确保B样条基函数能够更好地拟合数据。这在训练过程中可能会提高模型的精度和稳定性。参数:x (torch.Tensor): 输入张量,形状为 (batch_size, in_features)。margin (float): 网格更新的边缘大小,用于在更新网格时引入微小变化。"""# 确保输入张量的维度正确assert x.dim() == 2 and x.size(1) == self.in_featuresbatch = x.size(0)  # 获取批量大小# 计算输入张量的B样条基函数splines = self.b_splines(x)  # (batch, in, coeff)splines = splines.permute(1, 0, 2)  # 转置为 (in, batch, coeff)# 获取当前的样条权重orig_coeff = self.scaled_spline_weight  # (out, in, coeff)orig_coeff = orig_coeff.permute(1, 2, 0)  # 转置为 (in, coeff, out)# 计算未缩减的样条输出unreduced_spline_output = torch.bmm(splines, orig_coeff)  # (in, batch, out)unreduced_spline_output = unreduced_spline_output.permute(1, 0, 2)  # 转置为 (batch, in, out)# 为了收集数据分布,对每个通道分别进行排序x_sorted = torch.sort(x, dim=0)[0]grid_adaptive = x_sorted[torch.linspace(0, batch - 1, self.grid_size + 1, dtype=torch.int64, device=x.device)]# 计算均匀步长,并生成均匀网格uniform_step = (x_sorted[-1] - x_sorted[0] + 2 * margin) / self.grid_sizegrid_uniform = (torch.arange(self.grid_size + 1, dtype=torch.float32, device=x.device).unsqueeze(1)* uniform_step+ x_sorted[0]- margin)# 混合均匀网格和自适应网格grid = self.grid_eps * grid_uniform + (1 - self.grid_eps) * grid_adaptive# 扩展网格以包括样条边界grid = torch.concatenate([grid[:1]- uniform_step* torch.arange(self.spline_order, 0, -1, device=x.device).unsqueeze(1),grid,grid[-1:]+ uniform_step* torch.arange(1, self.spline_order + 1, device=x.device).unsqueeze(1),],dim=0,)# 更新模型中的网格点self.grid.copy_(grid.T)# 重新计算样条权重self.spline_weight.data.copy_(self.curve2coeff(x, unreduced_spline_output))def regularization_loss(self, regularize_activation=1.0, regularize_entropy=1.0):"""计算正则化损失。这是对论文中提到的原始L1正则化的一种简单模拟,因为原始方法需要从展开的 (batch, in_features, out_features) 中间张量计算绝对值和熵,但如果我们想要一个高效的内存实现,这些张量会被隐藏在F.linear函数后面。现在的L1正则化计算为样条权重的平均绝对值。作者的实现还包括这个项,此外还有基于样本的正则化。"""# 计算样条权重的绝对值的平均值l1_fake = self.spline_weight.abs().mean(-1)# 计算激活正则化损失,即所有样条权重绝对值的和regularization_loss_activation = l1_fake.sum()# 计算每个权重占总和的比例p = l1_fake / regularization_loss_activation# 计算熵正则化损失,即上述比例的负熵regularization_loss_entropy = -torch.sum(p * p.log())# 返回总的正则化损失,包含激活正则化和熵正则化return (regularize_activation * regularization_loss_activation+ regularize_entropy * regularization_loss_entropy)########################################################## 类定义
class KAN(torch.nn.Module):def __init__(self,layers_hidden,grid_size=5,spline_order=3,scale_noise=0.01,scale_base=0.3,scale_spline=0.1,base_activation=torch.nn.SiLU,grid_eps=0.02,grid_range=[-1, 1],):"""初始化KAN模型。参数:layers_hidden (list): 每层的输入和输出特征数列表。grid_size (int): 网格大小。spline_order (int): 样条阶数。scale_noise (float): 样条权重初始化时的噪声缩放系数。scale_base (float): 基础权重初始化时的缩放系数。scale_spline (float): 样条权重初始化时的缩放系数。base_activation (nn.Module): 基础激活函数类。grid_eps (float): 网格更新时的小偏移量。grid_range (list): 网格范围。"""super(KAN, self).__init__()self.grid_size = grid_size  # 网格大小self.spline_order = spline_order  # 样条阶数# 初始化模型层self.layers = torch.nn.ModuleList()for in_features, out_features in zip(layers_hidden, layers_hidden[1:]):self.layers.append(KANLinear(in_features,out_features,grid_size=grid_size,spline_order=spline_order,scale_noise=scale_noise,scale_base=scale_base,scale_spline=scale_spline,base_activation=base_activation,grid_eps=grid_eps,grid_range=grid_range,))def forward(self, x: torch.Tensor, update_grid=False):"""实现模型的前向传播。参数:x (torch.Tensor): 输入张量,形状为 (batch_size, in_features)。update_grid (bool): 是否在前向传播过程中更新网格。返回:torch.Tensor: 输出张量,形状为 (batch_size, out_features)。"""for layer in self.layers:if update_grid:layer.update_grid(x)x = layer(x)return xdef regularization_loss(self, regularize_activation=1.0, regularize_entropy=1.0):"""计算模型的正则化损失。参数:regularize_activation (float): 激活正则化系数。regularize_entropy (float): 熵正则化系数。返回:float: 总的正则化损失。"""return sum(layer.regularization_loss(regularize_activation, regularize_entropy)for layer in self.layers)def demo():# 定义模型的隐藏层结构,每层的输入和输出维度layers_hidden = [64, 128, 256, 128, 64, 32]# 创建5层的FourierKAN模型model = KAN(layers_hidden,grid_size=5,spline_order=3,scale_noise=0.1,scale_base=1.0,scale_spline=1.0,base_activation=torch.nn.SiLU,grid_eps=0.02,grid_range=[-1, 1],)# 打印模型结构device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')model.to(device)# 使用torchsummary输出模型结构summary(model, input_size=(64,))  # 假设输入特征为64维if __name__ == "__main__":demo()# ==========================================================================================
# Layer (type:depth-idx)                   Output Shape              Param #
# ==========================================================================================
# KAN                                      [32]                      --
# ├─ModuleList: 1-1                        --                        --
# │    └─KANLinear: 2-1                    [128]                     81,920
# │    │    └─SiLU: 3-1                    [1, 64]                   --
# │    └─KANLinear: 2-2                    [256]                     327,680
# │    │    └─SiLU: 3-2                    [1, 128]                  --
# │    └─KANLinear: 2-3                    [128]                     327,680
# │    │    └─SiLU: 3-3                    [1, 256]                  --
# │    └─KANLinear: 2-4                    [64]                      81,920
# │    │    └─SiLU: 3-4                    [1, 128]                  --
# │    └─KANLinear: 2-5                    [32]                      20,480
# │    │    └─SiLU: 3-5                    [1, 64]                   --
# ==========================================================================================
# Total params: 839,680

更多详细代码详情请移步GitHub:
lgy112112 Efficient-KAN-in-Chinese

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

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

相关文章

YOLO优化之扫描融合模块(SimVSS Block)

研究背景 在自动驾驶技术快速发展的背景下,目标检测作为其核心组成部分面临着严峻挑战。 驾驶场景中目标尺度和大小的巨大差异 ,以及 视觉特征不显著且易受噪声干扰 的问题,对辅助驾驶系统的安全性构成了潜在威胁。 传统的卷积神经网络(CNN)虽然在目标检测领域取得了显著…

(全)2024下半年真题 系统架构设计师 综合知识 答案解析01

系统架构设计师第二版教程VIP课程https://edu.csdn.net/course/detail/40283 操作系统 下列选项中不能作为预防死锁措施的是 。 A. 破坏“循环等待"条件 B. 破坏“不可抢占”条件 C. 破坏“互斥”条件 D. 破坏“请求和保持”条件 答案&#xff1a;C 解析&…

通义万相 2.1 + 蓝耘算力,AI 视频生成的梦幻组合

在这个科技日新月异的时代&#xff0c;人工智能不断刷新着我们对世界的认知。一次偶然的机会&#xff0c;我借助北京蓝耘科技股份有限公司提供的算力支持&#xff0c;踏上了使用通义万相 2.1 进行 AI 视频生成的奇妙之旅。 目录 1.1初遇蓝耘科技&#xff1a; 1.2通义万相 2.1…

链表·简单归并

cur->next la; //将 p指针所指向的链表节点的 next 指针&#xff08;也就是 p 节点的下一个节点的指针&#xff09;指向 l1 所指向的链表节点。简单来说&#xff0c;就是把 la 节点连接到 p 节点的后面&#xff0c;更新了链表的连接关系。 p la; //将p指针的值更新为 la …

kmp报错→Cannot find skiko-windows-x64.dll.sha256

1、前言 学习kmp&#xff08;Kotlin MultiPlatform简称&#xff09;过程中报了错误&#xff0c;这个报错在直接运行desktop的main方法才会出现&#xff0c;用gradle运行却不会报错&#xff0c;新建的kmp项目也不会出现&#xff0c;我学习的写了一些代码的项目才会出现。   运…

MySQL(事物下)

目录 一 多版本并发控制&#xff08; MVCC &#xff09;是一种用来解决 读-写冲突 的无锁并发控制 1. 前置知识 示例&#xff1a; 二 Read View 1. 当事物进行快照读(读历史数据)会MySQL会创建一个Read Vidw类对象&#xff0c;用来记录和当前一起并发的事物(活跃的事物)&a…

星型组网模块的两种交互方式优缺点解析

星型组网模块简介 星型组网模块工作在433MHz频段&#xff1b;星型组网模块集主机&#xff08;协调器&#xff09;、终端为一体&#xff0c;星型组网模块具有长距离、高速率两种传输模式&#xff0c;一个主机&#xff08;协调器&#xff09;支持多达200个节点与其通讯&#xff0…

IMX6ULL学习整理篇——UBoot的一些基础知识(1.编译流程)

前言 笔者整理了最近刷IMX6ULL的一些学习笔记&#xff0c;这里打算稍微整理一下东西发上来作为作为一个补充 正文 大部分而言&#xff0c;当我们拿到源码的时候&#xff0c;一般都是——先使用make来生成一份针对我们目标开发板的配置。举个例子&#xff0c;正点原子针对他们…

docker桌面版启动redis,解决无法连接

docker run -d --name redis -p 6379:6379 -v E:\2\redis\redis.conf:/usr/local/etc/redis/redis.conf redis redis-server /usr/local/etc/redis/redis.conf 在本地创建一个目录&#xff0c;里面有个redis.conf文件&#xff0c;内容如下&#xff0c;启动时绑定这个配置文件目…

2025-03-15 学习记录--C/C++-PTA 习题3-3 出租车计价

合抱之木&#xff0c;生于毫末&#xff1b;九层之台&#xff0c;起于累土&#xff1b;千里之行&#xff0c;始于足下。&#x1f4aa;&#x1f3fb; 一、题目描述 ⭐️ 习题3-3 出租车计价 本题要求根据某城市普通出租车收费标准编写程序进行车费计算。具体标准如下&#xff1…

《C++ Primer》学习笔记(二)

第二部分&#xff1a;C标准库 1.为了支持不同种类的IO处理操作&#xff0c;标准库定义了以下类型的IO&#xff0c;分别定义在三个独立的文件中&#xff1a;iostream文件中定义了用于读写流的基本类型&#xff1b;fstream文件中定义了读写命名文件的类型&#xff1b;sstream文件…

数据类设计_图片类设计之6_混合图形类设计(前端架构)

前言 学的东西多了,要想办法用出来.C和C是偏向底层的语言,直接与数据打交道.尝试做一些和数据方面相关的内容 引入 接续上一篇,讨论混合图形类设计 方法论-现在能做什么 这段属于聊天内容---有句话是这么说的&#xff1a;不要只埋头拉车&#xff0c;还要抬头看路。写代码也是…

招聘信息|基于SprinBoot+vue的招聘信息管理系统(源码+数据库+文档)

招聘信息管理系统 目录 基于SprinBootvue的招聘信息管理系统 一、前言 二、系统设计 三、系统功能设计 5.1系统功能模块 5.2管理员功能模块 5.3企业后台管理模块 5.4用户后台管理模块 四、数据库设计 五、核心代码 六、论文参考 七、最新计算机毕设选题推荐 八、…

【软件】免费的PDF全文翻译软件,能保留公式图表的样式

转载请注明出处&#xff1a;小锋学长生活大爆炸[xfxuezhagn.cn] 如果本文帮助到了你&#xff0c;欢迎[点赞、收藏、关注]哦~ 很多PDF全文翻译软件都是收费的&#xff0c;而划线翻译看着又很累。这个开源的PDF全文翻译软件非常好用&#xff0c;并且能够保留公式、图表、目录和注…

79.HarmonyOS NEXT 手势操作模型详解:移动、缩放与旋转的实现原理

温馨提示&#xff1a;本篇博客的详细代码已发布到 git : https://gitcode.com/nutpi/HarmonyosNext 可以下载运行哦&#xff01; HarmonyOS NEXT 手势操作模型详解&#xff1a;移动、缩放与旋转的实现原理 文章目录 HarmonyOS NEXT 手势操作模型详解&#xff1a;移动、缩放与旋…

解读Ant Design X API流式响应和流式渲染的原理

前言 AI是未来世界的趋势&#xff0c;deepseek的出现让在国内构建更多的大模型出现了更多的可能。而从前端出发&#xff0c;Ant design团队最近很有意思&#xff0c;基于这个背景&#xff0c;提供了一套面向构建平台化产品的组件。 本篇结合Ant design AI的XSteam、XRequesta…

CentOS 7 64 安装 Docker

前言 在虚拟机中安装 Docker 是一种常见的测试和开发环境搭建方式。通过在虚拟机上安装 Docker&#xff0c;可以方便地创建和管理容器化应用&#xff0c;同时避免对宿主机系统造成影响。以下是在 CentOS 7 虚拟机中安装 Docker 的详细步骤。 1. 更新系统&#xff08;可以不操作…

SPI驱动(八) -- SPI_DAC设备驱动程序

文章目录 参考资料&#xff1a;一、编写设备树二、 编写驱动程序三、编写测试APP四、Makefile五、上机实验 参考资料&#xff1a; 参考资料&#xff1a; 内核头文件&#xff1a;include\linux\spi\spi.h内核文档&#xff1a;Documentation\spi\spidevDAC芯片手册&#xff1a;…

Ansible 自动化运维

Ansible架构: 一.部署主机清单 前期环境准备: 管理端: 192.168.60.128 被管理端: client1:192.168.60.129 client2:192.168.60.131 1.所有被管理端配置ssh密钥 (1.免密登陆 2.允许root远程登陆) 脚本如下: #!/bin/bash# 检查 sshpass 是否已安装 if ! command -v ss…

Qt 实现波浪填充的圆形进度显示

话不多说&#xff0c;先上效果图 代码示例&#xff1a; #include <QApplication> #include <QWidget> #include <QPainter> #include <QPropertyAnimation> #include <QTimer> #include <cmath>class WaveProgressBar : public QWidget {…