从零复现DeepSeek R1:从V3中对MoE、MLA、MTP的实现,到Open R1对R1中SFT、GRPO的实现
前言
虽然我司从23年起,便逐步从教育为主转型到了科技为主,但不代表教育业务便没有了
随着DeepSeek特别是R1、其次V3模型的大火,我司七月在线的大模型线上营群里一学员朋友DIFY问道:校长好,deepseek 的课程目前有多少内容啦,我想要参与学习,想请问一下关于v3和r1复现的课程有吗,不用那么大参数量,小尺寸就好
实话讲,我一开始确实没咋重点考虑R1和V3复现的问题,一来,想着毕竟人家开源了,二来,即便有诸如Open R1这种复现,但效果和原装的相比还是差太多
但后来有三点改变了我的看法
- 对于V3 都没有开源他们最核心的训练数据、训练代码
比如V3只是开源了模型权重、模型结构(modeling_deepseek)和推理脚本 - 虽然Open-R1 只是复现了R1正式版的前两个阶段(如此文所述,R1正式版 有4个阶段)
虽然效果上 不会太好「所以之前没咋关注 因为对于作商用项目的我司来讲,其落地潜力有限」
但毕竟只是一个从零开始的开源小项目 也没法要求太高,所以放到课程中 还是有一定的科研价值的 - 如此,综上可得,或如DIFY所说
加之,我已经 把deepseek各个模型的原理 写透彻了,接下来,确实准备抠下他们已经对外开源的部分代码,然后再带头组织我司部分同事及相关朋友,填补一下无论是V3、R1还是Open R1缺失的代码与流程
以上种种,使得本文来了
最后,我特别强调一下,如果对deepseek各类模型及各类算法还不熟悉的话,强烈建议先看对应的原理:《火爆全球的DeepSeek系列模型》,可以看到
- 24年1.5日,DeepSeek LLM发布,没太多创新
类似llama那一套「llama1的RoPE/RMSNorm/SwiGLU + llama2 70B或llama3的GQA」- 24年1.11日,DeepSeekMoE,开启创新之路
提出细粒度专家分割和共享专家隔离,以及一系列负载均衡- 24年1.25,发布DeepSeek-Coder
24年2月,发布DeepSeekMath
提出了Group Relative Policy Optimization(简称GRPO),以替代PPO——舍弃critic模型- 24年5.7日,DeepSeek-V2
提出多头潜在注意力MLA且改进MoE
其中的这个MLA是整个deepseek系列最大的几个创新之一,且由此引发了各大厂商百万token的大幅降价- 24年12.26日,DeepSeek-V3发布
在MoE、GRPO、MLA基础上提出Multi-Token预测,且含FP8训练
大家纷纷把它和Llama 3.1 405B对比,V3以极低的训练成本造就超强的效果,再度出圈- 25年1.20日,DeepSeek R1发布
一方面,提出舍弃SFT、纯RL训练大模型的范式,且效果不错
二方面,性能比肩o1甚至略微超越之
三方面,直接公布思维链且免费,不藏着掖着,相比o1,对用户极度友好
至此爆了,火爆全球总之,原理熟悉之后,再看本文的源码实现,事半功倍
第一部分 DeepSeek V3源码解读:MoE、MLA、MTP的实现
首先,我们来看下V3对外开源的内容
类别 | 开源内容 | 未开源内容 |
---|---|---|
模型权重 | FP8/BF16 格式的基础模型和聊天版本 相当于开源了FP8 格式模型权重(包含基础模型和聊天优化版本),支持通过脚本转换为 BF16 格式进行推理——用户可通过 Hugging Face 平台直接下载 | 训练数据 数据清洗、预处理代码或数据集链接,比如 14.8 万亿 Token 的预训练数据集或具体数据来源 |
架构代码 | 包含模型定义的核心代码(如model.py),比如MLA、DeepSeekMoE、MTP 模块实现,但仅限架构定义,不包含完整的训练流程 | 从 DeepSeek-R1 提取知识的详细实现也未提供 |
训练框架 | FP8 混合精度框架、上下文扩展工具 | 完整分布式训练代码、超参数配置 比如分布式训练框架(如 DualPipe 流水线并行)、FP8 混合精度训练的具体实现 |
推理工具 | 主流框架适配方案、Hugging Face 示例 即提供基于 Hugging Face 或 vLLM 的简单推理脚本(如 inference.py),但未公开生产级优化代码(如动态批处理、显存优化等) | 生产级优化内核 比如企业级部署工具(如 DeepSeek 自研的推理引擎),涉及动态批处理、显存管理等等 |
通过此文《一文通透让Meta恐慌的DeepSeek-V3:在MoE、GRPO、MLA基础上提出Multi-Token预测(含FP8训练详解)》可知,在模型的架构层面,V3主要就在MoE、GRPO、MLA的基础上提出了Multi-Token预测
故接下来,咱们便来逐步剖析
1.1 对DeepSeekMoE的实现:涉及RoPE、MoE层、Norm层
根据MoE的结构可知,我们需要实现Norm层、attention层、MoE层,考虑到V3中的attention是潜在多头注意力——即MLA类实现了多头注意力层,支持低秩查询投影和键值投影,并根据配置选项选择不同的注意力实现,故放到下一节中介绍(下图来源于Switch Transformers)
本节中,我们结合V3代码库中的model.py看下这几个部分的实现
- precompute_freqs_cis函数预计算了用于旋转位置嵌入的频率复数指数值
- apply_rotary_emb函数将旋转位置嵌入应用于输入张量
- MLP类实现了一个多层感知机,用于前馈网络层
- Gate类实现了一个门控机制,用于在专家模型中路由输入
- Expert类实现了专家模型中的专家层
- MoE类实现了专家模型模块,包含多个专家和一个共享专家
- RMSNorm类实现了均方根层归一化,用于对输入张量进行归一化处理
- Block类实现了Transformer块,结合了注意力层和前馈网络层
1.1.1 RoPE的实现
model.py中,关于RoPE的实现涉及以下两个函数
- precompute_freqs_cis函数预计算了用于旋转位置嵌入的频率复数指数值
- apply_rotary_emb函数将旋转位置嵌入应用于输入张量
关于RoPE的更多细节,详见此文《一文通透位置编码:从标准位置编码、旋转位置编码RoPE到ALiBi、LLaMA 2 Long(含NTK-aware简介)》
1.1.1.1 precompute_freqs_cis函数
precompute_freqs_cis函数用于预计算旋转位置嵌入的基于频率的复数指数值。该函数接收一个ModelArgs类型的参数args,其中包含了位置嵌入的相关参数。函数返回一个预计算的复数指数值的张量,用于位置嵌入
def precompute_freqs_cis(args: ModelArgs) -> torch.Tensor:"""预计算用于旋转位置嵌入的基于频率的复数指数值。参数:args (ModelArgs): 包含位置嵌入参数的模型参数。返回:torch.Tensor: 预计算的用于位置嵌入的复数指数值。"""
函数首先从args中提取相关参数,包括嵌入维度dim、最大序列长度seqlen、快速和慢速beta修正因子beta_fast和beta_slow、基数base和缩放因子factor
dim = args.qk_rope_head_dim # 获取查询键旋转嵌入的维度seqlen = args.max_seq_len # 获取最大序列长度beta_fast = args.beta_fast # 获取快速beta修正因子beta_slow = args.beta_slow # 获取慢速beta修正因子base = args.rope_theta # 获取旋转位置编码的基数factor = args.rope_factor # 获取扩展序列长度的缩放因子
接着,定义了三个辅助函数:find_correction_dim、find_correction_range和linear_ramp_factor
- find_correction_dim函数计算旋转位置嵌入中给定旋转次数的修正维度
它使用输入参数计算修正维度,并返回该值def find_correction_dim(num_rotations, dim, base, max_seq_len):"""计算旋转位置嵌入中给定旋转次数的修正维度。参数:num_rotations (float): 要计算修正的旋转次数dim (int): 嵌入空间的维度base (float): 指数计算的基数max_seq_len (int): 最大序列长度返回:float: 基于输入参数的修正维度"""return dim * math.log(max_seq_len / (num_rotations * 2 * math.pi)) / (2 * math.log(base)) # 计算修正维度
- find_correction_range函数计算旋转位置嵌入的修正维度范围
它接收旋转次数的上下界、嵌入维度、基数和最大序列长度作为参数,返回修正维度的范围def find_correction_range(low_rot, high_rot, dim, base, max_seq_len):"""计算旋转位置嵌入的修正维度范围参数:low_rot (float): 旋转次数的下界high_rot (float): 旋转次数的上界dim (int): 嵌入空间的维度base (float): 指数计算的基数max_seq_len (int): 最大序列长度返回:Tuple[int, int]: 修正维度的范围(低,高),并限制在有效索引范围内"""low = math.floor(find_correction_dim(low_rot, dim, base, max_seq_len)) # 计算低修正维度high = math.ceil(find_correction_dim(high_rot, dim, base, max_seq_len)) # 计算高修正维度return max(low, 0), min(high, dim-1) # 返回修正维度范围
- linear_ramp_factor函数计算用于在最小值和最大值之间平滑值的线性斜坡函数
它返回一个张量,该张量的值在0和1之间线性插值,并限制在[0, 1]范围内def linear_ramp_factor(min, max, dim):"""计算用于在最小值和最大值之间平滑值的线性斜坡函数参数:min (float): 斜坡函数的最小值max (float): 斜坡函数的最大值dim (int): 斜坡张量的维度返回:torch.Tensor: 形状为(dim,)的张量,值在0和1之间线性插值,并限制在[0, 1]范围内。"""if min == max: # 如果最小值等于最大值max += 0.001 # 增加最大值以避免除零错误linear_func = (torch.arange(dim, dtype=torch.float32) - min) / (max - min) # 计算线性函数ramp_func = torch.clamp(linear_func, 0, 1) # 限制线性函数的值在0到1之间return ramp_func # 返回线性斜坡函数
接下来,函数计算频率值freqs,这些值是基于嵌入维度和基数的指数函数。如果序列长度大于原始序列长度,则应用修正范围和平滑因子来调整频率值
# 计算频率值freqs = 1.0 / (base ** (torch.arange(0, dim, 2, dtype=torch.float32) / dim)) if seqlen > args.original_seq_len: # 如果序列长度大于原始序列长度low, high = find_correction_range(beta_fast, beta_slow, dim, base, args.original_seq_len) # 计算修正范围smooth = 1 - linear_ramp_factor(low, high, dim // 2) # 计算平滑因子freqs = freqs / factor * (1 - smooth) + freqs * smooth # 调整频率值
最后,函数计算时间步长t,并使用外积计算频率值的复数指数表示,返回预计算的复数指数值张量freqs_cis
t = torch.arange(seqlen) # 生成时间步长freqs = torch.outer(t, freqs) # 计算频率值的外积freqs_cis = torch.polar(torch.ones_like(freqs), freqs) # 计算频率值的复数指数表示return freqs_cis # 返回预计算的复数指数值
1.1.1.2 apply_rotary_emb的实现
apply_rotary_emb函数用于将旋转位置嵌入应用到输入张量x上。该函数接收两个参数:x是包含位置嵌入的输入张量,freqs_cis是预计算的复数指数值张量,用于位置嵌入
def apply_rotary_emb(x: torch.Tensor, freqs_cis: torch.Tensor) -> torch.Tensor:"""将旋转位置嵌入应用于输入张量参数:x (torch.Tensor): 包含要应用位置嵌入的输入张量freqs_cis (torch.Tensor): 预计算的用于位置嵌入的复数指数值返回:torch.Tensor: 应用了旋转嵌入的张量"""
- 首先,函数保存输入张量的原始数据类型dtype
dtype = x.dtype # 获取输入张量的数据类型
- 然后,将输入张量x转换为浮点类型,并重新调整其形状,使其最后一个维度的大小变为2,以便视为复数
x = torch.view_as_complex(x.float().view(*x.shape[:-1], -1, 2)) # 将输入张量视为复数
- 接着,函数将x视为复数张量函数将freqs_cis调整形状,使其与输入张量的形状匹配。具体来说,freqs_cis的形状调整为(1, 序列长度, 1, 嵌入维度/2),以便在后续计算中进行广播
freqs_cis = freqs_cis.view(1, x.size(1), 1, x.size(-1)) # 调整频率值的形状
- 然后,函数将输入张量x与freqs_cis相乘,得到应用了旋转位置嵌入的复数张量。接着,将结果转换回实数张量,并将其形状调整为原始形状
y = torch.view_as_real(x * freqs_cis).flatten(3) # 计算应用旋转嵌入后的张量
- 最后,函数将结果张量转换回原始数据类型,并返回该张量。这样,输入张量x就应用了旋转位置嵌入
return y.to(dtype) # 返回转换为原始数据类型的张量
1.1.2 对MoE层的实现:包含MLP类、Gate类、Expert类、MoE类
接下来,我们来看MoE的实现
涉及如下这几个函数的实现
- MLP类实现了一个多层感知机,用于前馈网络层
- Gate类实现了一个门控机制,用于在专家模型中路由输入
- Expert类实现了专家模型中的专家层
- MoE类实现了专家模型模块,包含多个专家和一个共享专家
1.2.1.1 MLP类的实现——多层感知机,用于前馈层
MLP类实现了一个多层感知机(MLP),用于前馈层。该类继承自nn.Module,并包含三个线性层:w1、w2和w3。这些线性层分别用于输入到隐藏层的转换、隐藏层到输出层的转换以及特征转换
class MLP(nn.Module):"""多层感知机(MLP),用于前馈层属性:w1 (nn.Module): 输入到隐藏层的线性层w2 (nn.Module): 隐藏层到输出层的线性层w3 (nn.Module): 额外的特征转换线性层"""
- 在初始化方法__init__中
MLP类接收两个参数:dim表示输入和输出的维度,inter_dim表示隐藏层的维度
w1和w3是列并行线性层(ColumnParallelLinear),用于将输入维度转换为隐藏层维度def __init__(self, dim: int, inter_dim: int):"""初始化MLP层。参数dim (int): 输入和输出的维度inter_dim (int): 隐藏层的维度"""
w2是行并行线性层(RowParallelLinear),用于将隐藏层维度转换回输入维度self.w1 = ColumnParallelLinear(dim, inter_dim) # 定义输入到隐藏层的列并行线性层self.w2 = RowParallelLinear(inter_dim, dim) # 定义隐藏层到输出层的行并行线性层self.w3 = ColumnParallelLinear(dim, inter_dim) # 定义额外的特征转换列并行线性层
1.2.1.2 门控网络Gate类的实现——输入路由的门控机制
Gate类实现了一个用于混合专家(MoE)模型中的输入路由的门控机制
一般就两个计算公式
类似此文《一文速览DeepSeekMoE:从Mixtral 8x7B到DeepSeekMoE(含DeepSeek LLM的简介)》所述,如果每个token选择2个专家,则门控网络的权重矩阵计算对应2个专家的权重,比如w1,w2,然后做softmax,最后与2个专家的输出expert1、expert做加权求和
类似
softmax(X × w1) × expert1 + softmax(X× w2) × expert2
该类继承自nn.Module,并包含多个属性
class Gate(nn.Module):"""混合专家(MoE)模型中用于路由输入的门控机制。属性:dim (int): 输入特征的维度topk (int): 每个输入激活的顶级专家数量n_groups (int): 路由组的数量topk_groups (int): 路由输入的组数score_func (str): 评分函数('softmax'或'sigmoid')route_scale (float): 路由权重的缩放因子weight (torch.nn.Parameter): 门控机制的可学习权重bias (Optional[torch.nn.Parameter]): 门控机制的可选偏置项"""
- 在初始化方法__init__中,Gate类接收一个ModelArgs类型的参数args,其中包含了门控机制的参数
根据这些参数,类初始化了各个属性,并创建了权重和偏置项的量def __init__(self, args: ModelArgs):"""初始化门控模块。参数:args (ModelArgs): 包含门控参数的模型参数。"""super().__init__() # 调用父类的初始化方法self.dim = args.dim # 设置输入特征的维度self.topk = args.n_activated_experts # 设置每个输入激活的顶级专家数量self.n_groups = args.n_expert_groups # 设置路由组的数量self.topk_groups = args.n_limited_groups # 设置路由输入的组数self.score_func = args.score_func # 设置评分函数self.route_scale = args.route_scale # 设置路由权重的缩放因子self.weight = nn.Parameter(torch.empty(args.n_routed_experts, args.dim)) # 初始化可学习权重self.bias = nn.Parameter(torch.empty(args.n_routed_experts)) if self.dim == 7168 else None # 初始化可选偏置项
- 在前向传播方法forward中,Gate类接收一个输入张量x
首先,输入张量通过线性变换函数linear与权重weight相乘,得到评分`score`def forward(self, x: torch.Tensor) -> Tuple[torch.Tensor, torch.Tensor]:"""门控机制的前向传播。参数:x (torch.Tensor): 输入张量。返回:Tuple[torch.Tensor, torch.Tensor]: 路由权重和选择的专家索引。"""
根据评分函数score_func的不同,评分可以通过softmax或sigmoid函数进行归一化scores = linear(x, self.weight) # 计算输入张量与权重的线性变换,得到评分
然后,如果存在偏置项bias,则将其加到评分上if self.score_func == "softmax": # 如果评分函数是softmaxscores = scores.softmax(dim=-1, dtype=torch.float32) # 对评分进行softmax归一化else:scores = scores.sigmoid() # 对评分进行sigmoid归一化
接下来,如果路由组的数量n_groups大于1,评分将被重新调整形状,并计算每组的最大评分或前两个评分的和original_scores = scores # 保存原始评分if self.bias is not None: # 如果存在偏置项scores = scores + self.bias # 将偏置项加到评分上
然后,选择顶级组的索引,并创建一个掩码,将评分与掩码相乘并展平if self.n_groups > 1: # 如果路由组的数量大于1scores = scores.view(x.size(0), self.n_groups, -1) # 调整评分的形状if self.bias is None: # 如果没有偏置项group_scores = scores.amax(dim=-1) # 计算每组的最大评分else: group_scores = scores.topk(2, dim=-1)[0].sum(dim=-1) # 计算每组前两个评分的和
indices = group_scores.topk(self.topk_groups, dim=-1)[1] # 选择顶级组的索引mask = torch.zeros_like(scores[..., 0]).scatter_(1, indices, True) # 创建掩码scores = (scores * mask.unsqueeze(-1)).flatten(1) # 将评分与掩码相乘并展平
1.2.1.3 Expert类的实现:MoE模型中的专家层
Expert类实现了混合专家(MoE)模型中的专家层。该类继承自nn.Module,并包含三个线性层:w1、w2和w3。这些线性层分别用于输入到隐藏层的转换、隐藏层到输出层的转换以及特征转换。
class Expert(nn.Module):"""混合专家(MoE)模型中的专家层属性:w1 (nn.Module): 输入到隐藏层的线性层w2 (nn.Module): 隐藏层到输出层的线性层w3 (nn.Module): 额外的特征转换线性层"""
- 在初始化方法__init__中,Expert类接收两个参数:dim表示输入和输出的维度,inter_dim表示隐藏层的维度
w1是一个线性层,用于将输入维度转换为隐藏层维度def __init__(self, dim: int, inter_dim: int):"""初始化专家层。参数:dim (int): 输入和输出的维度inter_dim (int): 隐藏层的维度"""super().__init__() # 调用父类的初始化方法
w2是另一个线性层,用于将隐藏层维度转换回输入维度self.w1 = Linear(dim, inter_dim) # 定义输入到隐藏层的线性层
w3是一个额外的线性层,用于特征转换self.w2 = Linear(inter_dim, dim) # 定义隐藏层到输出层的线性层
self.w3 = Linear(dim, inter_dim) # 定义额外的特征转换线性层
- 在前向传播方法forward中,Expert类接收一个输入张量x
首先,输入张量通过w1线性层,并应用SiLU激活函数(F.silu)def forward(self, x: torch.Tensor) -> torch.Tensor:"""专家层的前向传播。参数:x (torch.Tensor): 输入张量返回:torch.Tensor: 经过专家层计算后的输出张量"""
然后,结果与通过w3线性层的输入张量相乘
最后,乘积通过w2线性层,得到输出张量# 计算前向传播,应用SiLU激活函数并进行特征转换return self.w2(F.silu(self.w1(x)) * self.w3(x))
1.2.1.4 MoE类:实现了专家模型模块,包含多个专家和一个共享专家
首先,关于什么是共享专家,可以详见此文 《一文速览DeepSeekMoE:从Mixtral 8x7B到DeepSeekMoE(含DeepSeek LLM的简介)》所述
其次,我们来看V3代码库里的model.py中对这一部分的实现
// 待更
1.1.3 Norm层的实现
// 待更
1.2 对多头潜在注意力MLA的实现
// 待更
1.3 对多token预测MTP的实现
// 待更
第二部分 Open R1——对DeepSeek R1正式版完整训练流程前两个阶段的复现
其次,再看R1对外开源的内容,根据R1的GitHub可知
类别 | 开源内容 | 未开源内容 |
---|---|---|
模型权重 | R1、R1-Zero 及蒸馏模型权重(MIT 协议) | 原始训练数据 未公开冷启动数据、RL 训练数据集或合成数据的具体内容,仅提供依赖的公开数据集名称(如 AI-MO、NuminaMath-TIR) |
技术文档 | GRPO 算法、奖励系统设计、冷启动流程等技术报告 | 训练代码,比如分布式训练代码细节 |
训练工具 | 合成数据生成脚本、评估基准代码 | 完整 RL 训练框架 |
推理支持 | API 接口、本地部署方案、框架适配指南 | 生产级优化内核 即动态显存管理、生产级批处理等企业级部署工具未开源 |
不过,有意思的是,有个开源项目——Open R1倒是复现了R1正式版完整训练流程的前两个阶段「以Qwen2.5-1.5B为基础,以deepseek-R1的训练过程打造」,并把代码开源了,其GitHub仓库主要包括:
- src/open_r1:包含训练和评估模型以及生成合成数据的脚本:
grpo.py:在给定的数据集上使用 GRPO 训练模型
sft.py:在数据集上执行模型的简单 SFT
evaluate.py:在 R1 基准上评估模型
generate.py:使用Distilabel从模型生成合成数据 - Makefile:包含利用上述脚本的 R1 管道中每个步骤的易于运行的命令
且如下图所示,分别实现了
- 从 DeepSeek-R1 中提取高质量语料库来复现 R1-Distill 模型
这里有个很重要的问题是,到底如何从R1中提取的高质量语料库
其实如Open R1的GitHub所说,从 DeepSeek-R1 提炼出的具有推理轨迹的数据集(例如Bespoke-Stratos-17k)上运行 SFT - 基于DeepSeek V3 创建 R1-Zero 的纯 RL 管道
- 复现R1正式版完整训练流程的前两个阶段(SFT + 规则奖励下的RL),其中比较有价值的便是对GRPO的实现
毕竟完整的R1正式版训练流程有4个阶段呢阶段一 冷启动SFT 阶段二 规则奖励下的RL R1-Zero模型生成的冷启动数据:微调V3 面向推理的RL:结合三个规则奖励——准确率奖励、格式奖励、语言一致性奖励 阶段三 增强SFT 阶段四 规则+偏好奖励下的RL 来自阶段二模型的60w推理数据
和V3模型的20w非推理数据:微调V3
全场景RL
规则奖励、偏好奖励
我司也会在这个课程《DeepSeek原理与项目实战营》里讲一下这个Open R1的复现思路,及深入解读其源码,以帮助更多人可以更好的用好该Open R1
// 待更
相关文章:
从零复现DeepSeek R1:从V3中对MoE、MLA、MTP的实现,到Open R1对R1中SFT、GRPO的实现
前言 虽然我司从23年起,便逐步从教育为主转型到了科技为主,但不代表教育业务便没有了 随着DeepSeek特别是R1、其次V3模型的大火,我司七月在线的大模型线上营群里一学员朋友DIFY问道:校长好,deepseek 的课程目前有多少…...
[EAI-033] SFT 记忆,RL 泛化,LLM和VLM的消融研究
Paper Card 论文标题:SFT Memorizes, RL Generalizes: A Comparative Study of Foundation Model Post-training 论文作者:Tianzhe Chu, Yuexiang Zhai, Jihan Yang, Shengbang Tong, Saining Xie, Dale Schuurmans, Quoc V. Le, Sergey Levine, Yi Ma 论…...
示例代码:C# MQTTS双向认证(客户端)(服务器EMQX)
初级代码游戏的专栏介绍与文章目录-CSDN博客 我的github:codetoys,所有代码都将会位于ctfc库中。已经放入库中我会指出在库中的位置。 这些代码大部分以Linux为目标但部分代码是纯C的,可以在任何平台上使用。 源码指引:github源…...
自制游戏——斗罗大陆
很简陋,没有图,请见谅 // mine[0] 级数 // mine[1] 战力 //mine[2] 1 白虎 //mine[2] 2 昊天锤 //mine[2] 3 蓝银草 #include <bits/stdc.h> using namespace std; int mine[100],live3, dou 1, luo 1, da 1, bag[1000], huan 0, lia…...
数据治理双证通关经验分享 | CDGA/CDGP备考全指南
历经1个月多的系统准备,本人于2024年顺利通过DAMA China的CDGA(数据治理工程师)和CDGP(数据治理专家)双认证。现将备考经验与资源体系化整理,助力从业者高效通关。 🌟 认证价值与政策背景 根据…...
Vue全流程--Vue3.0与Vue2.0响应式原理对比
Vue2中数据的响应式 需要使用Vue.set这么一个api,修改数据 需要使用Vue.delete这么一个api,删除数据 数据代理这个当面的理解可以看看我前面文章Vue全流程--数据代理的理解以及在Vue中的应用-CSDN博客 Vue3中数据的响应式 Vue3使用proxy这个api实现…...
Spring中都应用了哪些设计模式?
好的!以下是您提到的八种设计模式在 Spring 中的简单示例: 1. 简单工厂模式 简单工厂模式通过传入参数来决定实例化哪个类。Spring 中的 BeanFactory 就是简单工厂模式的应用。 示例代码: // 1. 创建接口和具体实现类 public interface A…...
fastjson2学习大纲
一、基础篇 - JSON与fastjson2核心概念 JSON基础 JSON语法规范(RFC 8259)JSON数据类型与Java类型对应关系序列化/反序列化核心概念 fastjson2入门 与fastjson1的主要区别核心优势: 性能提升(JSONB二进制协议)更完善的…...
k8s部署elasticsearch
前置环境:已部署k8s集群,ip地址为 192.168.10.1~192.168.10.5,总共5台机器。 1. 创建provisioner制备器(如果已存在,则不需要) 制备器的具体部署方式,参考我之前的文章:k8s部署rabbitmq-CSDN博客 2. 编写wms-elk-data-sc.yaml配置文件 apiVersion: storage.k8s.io/…...
BS架构(笔记整理)
楔子.基本概念 1.在网络架构中: 服务器通常是集中式计算资源,负责处理和存储数据;客户机是请求这些服务的终端设备,可能是个人电脑或移动设备;浏览器则是客户机上用来与服务器交互的工具,负责展示网页内容…...
从基础到人脸识别与目标检测
前言 从本文开始,我们将开始学习ROS机器视觉处理,刚开始先学习一部分外围的知识,为后续的人脸识别、目标跟踪和YOLOV5目标检测做准备工作。我采用的笔记本是联想拯救者游戏本,系统采用Ubuntu20.04,ROS采用noetic。 颜…...
ArcGIS Pro SDK (二十七)自定义许可
ArcGIS Pro SDK (二十七)自定义许可 环境:Visual Studio 2022 + .NET6 + ArcGIS Pro SDK 3.0 文章目录 ArcGIS Pro SDK (二十七)自定义许可1 在Config.xaml中添加扩展配置2 在Module1.cs中实现接口IExtensionConfig1 在Config.xaml中添加扩展配置 <modules><inse…...
一、kubernetes k8s
k8s概述: k8s的全称:kubernetes k8s k8s的版本:1.30 1.20------------用的最多的版本,1.18-1.21 1.24------------>k8s的镜像不再使用docker,containerd k8s的作用: 用于自动部署,自动扩展和管理“容器化应…...
C#、.Net 中级高级架构管理面试题杂烩
1、简述值类型和引用类型的区别 存储位置:值类型变量直接存储数据的值,通常存储在栈上;引用类型变量存储的是对象在堆上的引用地址。 内存管理:值类型的内存由系统自动管理,当超出作用域时自动释放;引用类…...
ArrayList和LinkedList有什么区别?在什么情况下使用ArrayList更高效?
ArrayList和LinkedList在Java中是两种常用的数据结构,分别基于数组和链表实现。它们在性能、内存使用和适用场景上各有特点。 ArrayList与LinkedList的主要区别 数据结构: ArrayList:基于动态数组实现,元素存储在连续的内存空间…...
KITE提示词框架:引导大语言模型的高效新工具
大语言模型的应用日益广泛。然而,如何确保这些模型生成的内容在AI原生应用中符合预期,仍是一个需要不断探索的问题。以下内容来自于《AI 原生应用开发:提示工程原理与实战》一书(京东图书:https://item.jd.com/1013604…...
Spring 整合 MyBatis:核心知识点详解
一、Spring 整合 MyBatis 的优势 依赖注入:Spring 的 IOC 容器可以管理 MyBatis 的组件(如 SqlSessionFactory、Mapper 接口等),减少手动创建对象的繁琐。 事务管理:Spring 提供了声明式事务管理,可以轻松…...
centos docker安装
一、前置条件 安装gcc和c: yum -y install gcc yum -y install gcc-c 二、卸载旧版本 如果之前安装过Docker,需要先卸载旧版本: sudo yum remove docker \docker-client \docker-client-latest \docker-common \docker-latest \docker-latest-logr…...
推荐一款 免费的SSL,自动续期
支持自动续期 、泛域名 、可视化所有证书时效性 、可配置CDN 的一款工具。免费5个泛域名和1个自动更新。 链接 支持:nginx、通配符证书、七牛云、腾讯云、阿里云、CDN、OSS、LB(负载均衡) 执行自动部署脚本 提示系统过缺少crontab 安装cro…...
python-leetcode 24.回文链表
题目: 给定单链表的头节点head,判断该链表是否为回文链表,如果是,返回True,否则,返回False 输入:head[1,2,2,1] 输出:true 方法一:将值复制到数组中后用双指针法 有两种常用的列表实现&#…...
利用kali linux 进行自动化渗透测试
本方案旨在自动化创建渗透测试全流程 一、架构 1.智能信息收集体系 class IntelligentOSINT:def __init__(self, target):self.target targetself.intelligence_sources [OSINT_Platforms,DeepWeb_Crawlers, SocialMedia_Trackers,ML_Correlation_Engine]def advanced_col…...
蓝桥杯试题:冒泡排序 选择排序
一、问题描述 在一个神秘的岛屿上,有一支探险队发现了一批宝藏,这批宝藏是以整数数组的形式存在的。每个宝藏上都标有一个数字,代表了其珍贵程度。然而,由于某种神奇的力量,这批宝藏的顺序被打乱了,探险队…...
curl与telnet的区别
协议支持:curl支持多种协议,如HTTP、HTTPS、FTP等,而telnet主要用于基于TCP协议的连接。 功能:curl是一个功能强大的工具,可以用来发送各种HTTP请求、下载文件等,而telnet主要用于在远程服务器上进行简单的…...
防火墙综合练习2
准备阶段 实验拓扑图如下: 试验要求如下: 需求一:完成相关配置 需求二:配置DHCP协议 需求三:防火墙安全区域配置 需求四:防火墙地址组信息 需求五:管理员 需求六:用户认证…...
leetcode_26删除有序数组中的重复项
1. 题意 给定一个重复数组,删除其中的重复项目。 2. 题解 双指针 一个指针指向有序不重复数组的最后一个数,另外一个数遍历整个数组,若两个指针对应用的数不相同,有序数组的指针右移,将数填入。 代码一 class Sol…...
SQLServer的创建,表创建,主键,约束,模糊查询
设置 注意: 设置完成之后 重新启动 创建数据库 注意: 这个目标路径必须要有该文件名的文件夹 -- 指向 master 数据库,告诉它我们要创建一个新的数据库操作了 use master go-- 创建数据库 create database StudentManageDB on primary (-- 以下四个组成部分缺一不可…...
钉钉位置偏移解决,钉钉虚拟定位打卡
虚拟定位打卡工具 一,介绍免费获取工具 一,介绍 提到上班打卡,职场人的内心戏估计能拍成一部连续剧。打卡,这俩字仿佛自带“紧箍咒”,让无数打工人又爱又恨。想象一下,你气喘吁吁地冲进办公室,…...
自有服务与软件包
—— 小 峰 编 程 目录 编辑 一、自有服务概述 二、systemctl管理服务命令 1、显示服务 2、查看启动和停止服务 3、服务持久化 三、常用自有服务(ntp,firewalld,crond) 1、ntp时间同步服务 1)NTP同步服务器原理 2)到哪里去找NPT服务…...
PHP之hyperf学习笔记
Hyperf Model,Dao,Service,Contronller 路由 使用文件来配置路由,就是和laravel一样的 Router::addGroup(["middleware" > ["web", "auth"],"namespace" > "Hyperf\HttpServer\Contr…...
C++STL(六)——list模拟
目录 本次所需实现的三个类一、结点类的模拟实现构造函数 二、迭代器类的模拟实现为什么有迭代器类迭代器类的模板参数说明构造函数运算符的重载- -运算符的重载和!运算符的重载*运算符的重载->运算符的重载引入模板第二个和第三个参数 三、list的模拟实现3.1 默认成员函数构…...
Spring MVC 拦截器(Interceptor)与过滤器(Filter)的区别?
1、两者概述 拦截器(Interceptor): 只会拦截那些被 Controller 或 RestController 标注的类中的方法处理的请求,也就是那些由 Spring MVC 调度的请求。过滤器(Filter): 会拦截所有类型的 HTTP …...
MySQL查询主从同步状态
在MySQL中,监控和检查主从复制(Master-Slave replication)的状态是非常重要的,这有助于确保数据的一致性和完整性。以下是一些常用的方法,可以帮助你查询MySQL的主从数据同步状态: 1. 查看主服务器状态 首…...
docker 安装 --在线方式
第一步: #!/bin/bash sudo curl -o /etc/yum.repos.d/CentOS-Base.repo http://mirrors.aliyun.com/repo/Centos-7.repo sudo sed -i -e /mirrors.cloud.aliyuncs.com/d -e /mirrors.aliyuncs.com/d /etc/yum.repos.d/CentOS-Base.repo sudo curl -o /etc/yum.repo…...
Linux系统-centos防火墙firewalld详解
Linux系统-centos7.6 防火墙firewalld详解 1 firewalld了解 CentOS 7.6默认的防火墙管理工具是firewalld,它取代了之前的iptables防火墙。firewalld属于典型的包过滤防火墙或称之为网络层防火墙,与iptables一样,都是用来管理防火墙的工具&a…...
物联网软件开发与应用方向应该怎样学习,学习哪些内容,就业方向是怎样?(文末领取整套学习视频,课件)物联网硬件开发与嵌入式系统
随着物联网技术的飞速发展,物联网软件开发与应用方向成为了众多开发者关注的焦点。那么,如何在这个领域中脱颖而出呢?本文将为你提供一份详细的学习指南,帮助你从零开始,逐步掌握物联网软件开发与应用的核心技能。 一…...
【大模型】DeepSeek与chatGPT的区别以及自身的优势
目录 一、前言二、核心技术对比2.1 模型架构设计2.1.1 ChatGPT的Transformer架构2.1.2 DeepSeek的混合架构 2.2 训练数据体系2.2.1 ChatGPT的数据特征2.2.2 DeepSeek的数据策略 三、应用场景对比3.1 通用场景表现3.1.1 ChatGPT的强项领域3.2.2 DeepSeek的专项突破 3.3 响应效率…...
常用的python库-安装与使用
常用的python库函数 yield关键字openslide库openslide库的安装-linuxopenslide的使用openslide对象的常用属性 cv2库numpy库ASAP库-multiresolutionimageinterface库ASAP库的安装ASAP库的使用 concurrent.futures.ThreadPoolExecutorxml.etree.ElementTree库skimage库PIL.Image…...
qt widget和qml界面集成到一起
将 Qt Widgets 和 QML 界面集成在一起可以利用 QQuickWidget 或 QQuickView。以下是基本步骤: 使用 QQuickWidget 创建 Qt Widgets 项目: 创建一个基于 Widgets 的应用程序。添加 QQuickWidget: 在你的窗口或布局中添加 QQuickWidget。 例如,可以在 QMainWindow 中使用: …...
mybatis 是否支持延迟加载?延迟加载的原理是什么?
1. MyBatis 是否支持延迟加载? 是的,MyBatis 支持延迟加载。延迟加载的主要功能是推迟数据加载的时机,直到真正需要时再去加载。这种方式能提高性能,尤其是在处理关系型数据时,可以避免不必要的数据库查询。 具体来说…...
MariaDB MaxScale实现mysql8主从同步读写分离
一、MaxScale基本介绍 MaxScale是maridb开发的一个mysql数据中间件,其配置简单,能够实现读写分离,并且可以根据主从状态实现写库的自动切换,对多个从服务器能实现负载均衡。 二、MaxScale实验环境 中间件192.168.121.51MaxScale…...
【图片转换PDF】多个文件夹里图片逐个批量转换成多个pdf软件,子文件夹单独合并转换,子文件夹单独批量转换,基于Py的解决方案
建筑设计公司在项目执行过程中,会产生大量的设计图纸、效果图、实景照片等图片资料。这些资料按照项目名称、阶段、专业等维度存放在多个文件夹和子文件夹中。 操作需求:为了方便内部管理和向客户交付完整的设计方案,公司需要将每个项目文件…...
基于logback+fastjson实现日志脱敏
一、需求背景 日常工作中,必不可免的会将一些敏感信息,如用户名、密码、手机号、身份证号、银行账号等等打印出来,但往往为了安全,这些信息都需要进行脱敏。脱敏实际就是用一些特殊字符来替换部分值。 JSON 和 JSONObject Fastj…...
13.10 统一配置管理中心:TranslationChain 架构的简洁配置管理方案
统一配置管理中心:TranslationChain 架构的简洁配置管理方案 1. 集中式配置文件设计 config/settings.yaml: # 多环境配置开关 env: production # development|test|production# 模型管理中心 models:openai:class: langchain_openai.ChatOpenAIparams...
deepseek大模型集成到idea
1 下载插件 安装CodeGPT打开 IntelliJ IDEA,鼠标点击左上角导航栏,File --> Setting 2 申请API key 3 配置deepseek 在 Settings 界面中的搜索框中,搜索 CodeGPT,路径 Tools --> CodeGPT --> Providers --> 如下一…...
Cocos2d-x 游戏开发-打包apk被默认自带了很多不必要的权限导致apk被报毒,如何在Cocos 2d-x中强制去掉不必要的权限-优雅草卓伊凡
Cocos2d-x 游戏开发-打包apk被默认自带了很多不必要的权限导致apk被报毒,如何在Cocos 2d-x中强制去掉不必要的权限-优雅草卓伊凡 实战操作 去除权限 要在 Cocos2d-x 开发的游戏中去掉 APK 自带权限,可以按照以下步骤操作: 编辑 AndroidMa…...
gitlab多项目流水线
背景是我有多个项目,希望其中一个项目被触发的时候,联动另外一个项目自动打包。然后我就看文档尝试操作了一下,所以有本文。 官方文档参考:https://gitlab.cn/docs/14.5/jh/ci/pipelines/multi_project_pipelines.html 不知道是不…...
GWO优化决策树回归预测matlab
灰狼优化算法(Grey Wolf Optimizer,简称 GWO)是一种群智能优化算法,由澳大利亚格里菲斯大学的 Mirjalii 等人于 2014 年提出。该算法的设计灵感源自灰狼群体的捕食行为,核心思想是模仿灰狼社会的结构与行为模式。 在本…...
2025影视泛目录站群程序设计_源码二次开发新版本无缓存刷新不变实现原理
1. 引言 本设站群程序计书旨在详细阐述苹果CMS泛目录的创新设计与实现,介绍无缓存刷新技术、数据统一化、局部URL控制及性能优化等核心功能,以提升网站访问速度和用户体验。 2. 技术概述 2.1 无缓存刷新技术 功能特点: 内容不变性&#x…...
在Linux上创建虚拟网卡
在 Linux 上创建虚拟网卡可以通过多种方式进行,常见的方式是使用 ip 命令来配置虚拟网卡。以下是一个简单的步骤指南,用于创建虚拟网卡: 步骤 1: 查看现有的网络接口 首先,查看当前网络接口的状态,可以使用以下命令&…...
JVM 类加载子系统在干什么?
JVM 类加载子系统是什么? 类加载子系统(Class Loader Subsystem)是 JVM 负责 加载、链接和初始化 .class 文件的组件。它的主要作用是将字节码文件加载进 JVM 并准备执行。 类加载器(ClassLoader)是 字节码的搬运工&…...