大语言模型PEFT

目录

一、PEFT的关键概念和方法

部分参数微调

概念

方法

优势

适配器(Adapters)

方法

优势

低秩分解(Low-rank Factorization)

方法

优势

差分微调(Delta Tuning)

方法

优势

多任务学习(Multi-task Learning)

方法

优势

二、低秩矩阵分解技术

低秩分解的原理

常见的低秩分解方法

奇异值分解(Singular Value Decomposition, SVD)

主成分分析(Principal Component Analysis, PCA)

非负矩阵分解(Non-negative Matrix Factorization, NMF)

低秩分解的应用

例子

三、部分参数微调

部分参数微调的原理

常见的部分参数微调方法

顶层微调(Top-layer Tuning)

中间层微调(Intermediate-layer Tuning)

瓶颈层微调(Bottleneck-layer Tuning)

层归一化参数微调(Layer Normalization Parameter Tuning)

特定参数组微调(Specific Parameter Group Tuning)

代码实例

四、适配器

适配器的原理

适配器的结构

降维层(Down-projection layer)

非线性激活函数

升维层(Up-projection layer)

残差连接

适配器的插入位置

代码实例

五、差分微调(Delta Tuning)

差分微调的原理

代码实例

六、多任务学习(Multi-task Learning)

多任务学习的原理

多任务学习的方法

硬共享(Hard Parameter Sharing)

软共享(Soft Parameter Sharing)

代码实例 

PEFT(Parameter-Efficient Fine-Tuning)是一种在大规模预训练模型(如Transformer模型)上进行高效微调的方法。这种方法的主要目标是通过优化较少的参数来实现模型的高效微调,从而降低计算成本和存储需求,同时保持或接近原始模型的性能。PEFT 在实际应用中非常重要,特别是在资源受限的环境中。

一、PEFT的关键概念和方法

部分参数微调

概念

部分参数微调是一种只调整模型中特定参数或层的方法,仅微调模型的一部分参数而不是整个模型,例如某些特定的层、子网络或参数组。这种方法选择性地微调一些关键部分,这可以大大减少需要优化的参数数量,从而减少计算负担和内存使用。

方法

冻结大部分层:只微调最后几层或特定的中间层。

选择性解冻:在训练过程中,逐步解冻更多的层。

层归一化:只微调归一化层的参数,如BatchNorm层或LayerNorm层。

优势

高效性:减少需要优化的参数数量,节省计算资源。

稳定性:通过限制参数更新的范围,减少过拟合风险。

适配器(Adapters)

在模型的特定层中插入小的适配器模块,这些模块通常比原始层小得多,只需微调适配器的参数,适配器的参数是独立优化的。这种方法可以在保持原始模型架构的前提下实现高效微调。

方法

在Transformer层中插入适配器:通常插入在每个Transformer层的前馈网络部分。

参数共享:在多任务学习中,不同任务的适配器可以共享部分参数。

优势

灵活性:适配器模块可以插入到不同层中,适应不同任务的需求。

节省资源:大大减少需要微调的参数量。

低秩分解(Low-rank Factorization)

将模型参数矩阵分解成两个较小的矩阵进行优化。这种方法可以减少参数量,同时保持模型的表示能力。

方法

矩阵分解:将一个大矩阵 W 分解为两个小矩阵 A 和 B,即 W≈A×B。

训练过程:只微调小矩阵 A 和 B,而不更新整个大矩阵。

优势

减少参数量:有效降低模型的参数规模。

保持性能:在很多情况下,可以保持模型性能不变或略微下降。

差分微调(Delta Tuning)

只微调与原始模型参数的差值部分,而不是整个参数集。这种方法可以在节省计算资源的同时,实现有效的参数更新。这种方法通过优化参数的增量来实现模型调整。

方法

参数初始化:从预训练模型加载参数。

增量更新:仅优化参数的增量部分 Δθ,即 Δθ′=θ+Δθ。

优势

节省内存:只存储和更新参数的增量部分。

稳定性:原始模型参数作为基础,有助于保持模型性能。

多任务学习(Multi-task Learning)

多任务学习是一种通过共享参数在多个任务之间进行微调的方法。这种方法利用多个任务的共同信息,提高模型的泛化能力。

方法

共享层:在多个任务之间共享部分模型层,减少总参数量。

任务特定层:每个任务拥有一些特定的参数层,用于处理任务特有的信息。

交替训练:在不同任务的数据上交替进行训练。

优势

参数共享:通过共享参数,显著减少总参数量。

提高泛化能力:利用多个任务的共同信息,提高模型的泛化性能。

二、低秩矩阵分解技术

低秩分解(Low-rank Factorization)是一种在机器学习和信号处理领域中广泛应用的技术,主要用于降维、压缩和优化模型参数。通过将一个高维矩阵分解成两个或多个低维矩阵,低秩分解可以有效减少参数数量,同时保持原始矩阵的大部分信息和结构特征。 

低秩分解的原理

低秩分解基于矩阵的秩(rank),即矩阵中线性独立行或列的最大数目。低秩分解通过将一个高秩矩阵近似为两个或多个低秩矩阵的乘积,从而降低参数维度。常见的低秩分解方法包括SVD(奇异值分解)、PCA(主成分分析)、NMF(非负矩阵分解)等。

给定一个矩阵W\in\mathbb{R}^{m\times n},其低秩分解可以表示为:W\approx A\times B

其中,A\in\mathbb{R}^{m\times k}B\in\mathbb{R}^{k\times n},k 是低秩近似的秩,通常 k 远小于 m 和 n。

常见的低秩分解方法

奇异值分解(Singular Value Decomposition, SVD)

原理:SVD将矩阵分解为三个矩阵的乘积,即W=U\Sigma V^{T} ,其中 U 和 V 是正交矩阵, ΣΣ 是对角矩阵,其对角线上的元素为奇异值。

低秩近似:选择前 k 个最大的奇异值及其对应的奇异向量,得到矩阵的低秩近似

主成分分析(Principal Component Analysis, PCA)

原理:PCA通过对数据进行协方差矩阵的特征值分解,找到数据的主成分,即方差最大的方向。

低秩近似:选择前 k 个主成分构建新的低维空间,从而实现降维。

非负矩阵分解(Non-negative Matrix Factorization, NMF)

原理:NMF将一个非负矩阵分解为两个非负矩阵的乘积,即 W\approx A\times B,其中 A 和 B 均为非负矩阵。

低秩近似:通过优化目标函数(如平方误差)找到最优的非负矩阵 A 和 B。

低秩分解的应用

降维:通过低秩分解将高维数据映射到低维空间,从而减少计算复杂度,提高模型效率。

压缩:在深度学习中,通过低秩分解压缩权重矩阵,减少模型参数量和存储需求。

去噪:低秩分解能够有效去除数据中的噪声,提高数据的质量和模型的鲁棒性。

推荐系统:在协同过滤中,低秩分解用于分解用户-物品评分矩阵,预测用户对未评分物品的偏好。

例子

假设有一个权重矩阵W\in\mathbb{R}^{1000\times1000},我们希望将其分解为两个低秩矩阵 A 和 B,其中A\in\mathbb{R}^{1000\times50}B\in\mathbb{R}^{50\times1000}。通过低秩分解,我们将原始矩阵的参数数量从1000\times1000=10^{6}减少到1000\times50+50\times1000=10^{5},显著降低了参数量和计算成本。

三、部分参数微调

部分参数微调(Partial Parameter Tuning)是一种高效优化方法,通过选择性地微调模型中的一部分参数来实现模型的适应性调整。这个方法适用于大规模预训练模型(如BERT、GPT等),能够在节省计算资源的同时保持模型性能。可以大幅减少需要优化的参数数量,降低计算成本和训练时间。通过限制参数更新的范围,降低模型过拟合的风险。在计算资源有限的情况下,快速适应新任务或新数据。

部分参数微调的原理

部分参数微调基于以下原则:

冻结大部分参数:保持模型的大部分参数不变,仅微调特定层或参数组。

选择性解冻:逐步解冻和微调更多层或参数,以逐步适应新的任务或数据。

常见的部分参数微调方法

顶层微调(Top-layer Tuning)

方法:只微调模型的最后几层或输出层。这种方法通常用于下游分类或回归任务,因为顶层参数直接影响模型的最终输出。

优势:大幅减少需要优化的参数数量,同时保留预训练模型提取的底层特征。

中间层微调(Intermediate-layer Tuning)

方法:选择模型的中间层进行微调。这种方法适用于需要调整模型内部特征表示的任务。

优势:灵活性较高,可以根据具体任务选择最相关的层进行微调。

瓶颈层微调(Bottleneck-layer Tuning)

方法:微调网络中的瓶颈层,即那些对信息流具有瓶颈效应的层(如Transformer中的注意力层)。

优势:通过微调瓶颈层,可以有效调整模型的表示能力,适应不同的任务需求。

层归一化参数微调(Layer Normalization Parameter Tuning)

方法:只微调层归一化(LayerNorm)或批归一化(BatchNorm)层的参数。

优势:归一化层参数较少,但对模型的稳定性和收敛速度有重要影响,因此微调这些参数可以带来显著的性能提升。

特定参数组微调(Specific Parameter Group Tuning)

方法:根据任务需求,选择性地微调特定参数组,如词嵌入层参数或特定注意力头的参数。

优势:精细控制微调过程,优化最相关的参数,节省资源。

代码实例

冻结大部分层:只微调BERT模型的最后一层Transformer层和输出层。

from transformers import BertModel, BertForSequenceClassification
import torchmodel = BertForSequenceClassification.from_pretrained('bert-base-uncased')# 冻结所有层
for param in model.bert.parameters():param.requires_grad = False# 只微调最后一层和分类头
for param in model.bert.encoder.layer[-1].parameters():param.requires_grad = True
for param in model.classifier.parameters():param.requires_grad = Trueoptimizer = torch.optim.Adam(filter(lambda p: p.requires_grad, model.parameters()), lr=1e-4)

选择性解冻:逐步解冻更多的Transformer层进行微调。

# 逐步解冻更多层
for i in range(-1, -4, -1):  # 解冻最后三层for param in model.bert.encoder.layer[i].parameters():param.requires_grad = True

四、适配器

适配器(Adapters)是部分参数微调的一种具体实现方法,通过在预训练模型的特定层中插入小型的适配器模块来实现模型微调。适配器的设计初衷是为了在保持预训练模型的大部分参数不变的情况下,实现对新任务的适应性调整。

适配器的原理

适配器的基本思想是将适配器模块插入到预训练模型的各个层中,这些模块通常包含少量参数,并且在微调过程中只更新这些参数。这样做的好处是减少了需要优化的参数量,同时利用了预训练模型中已学习到的丰富特征。

适配器的结构

降维层(Down-projection layer)

将输入特征降维到一个较小的空间。通常是一个线性变换,例如全连接层。z=W_\text{down}x+b_\text{down},其中, x 是输入特征,W_{\mathrm{down}}b_{\mathrm{down}}是降维层的权重和偏置。

非线性激活函数

在降维层之后,应用非线性激活函数(如ReLU,z'=\mathrm{ReLU}(z))来引入非线性特性。

升维层(Up-projection layer)

将降维后的特征升维回原始空间。也是一个线性变换。

x'=W_\mathrm{up}z'+b_\mathrm{up}

其中,W_{\mathrm{up}}b_{\mathrm{up}}是升维层的权重和偏置。

残差连接

将升维后的特征与原始输入特征相加,形成残差连接。这可以帮助模型保持原有的特征表示,同时引入适配器模块的调整。

y=x+x'

适配器模块的整体结构如下:

y=x+W_\text{up}(\text{ReLU}(W_\text{down}x+b_\text{down}))+b_\text{up}

适配器的插入位置

适配器模块可以插入到预训练模型的不同位置,常见的插入位置包括:

Transformer层内部:在Transformer层的多头注意力子层和前馈神经网络子层之间插入适配器模块。

每个Transformer层之后:在每个Transformer层之后插入适配器模块。

特定层中:根据任务需求,在特定的层中插入适配器模块。

代码实例

import torch
import torch.nn as nn
from transformers import BertModel, BertConfigclass Adapter(nn.Module):def __init__(self, input_dim, bottleneck_dim):super(Adapter, self).__init__()self.down_proj = nn.Linear(input_dim, bottleneck_dim)self.up_proj = nn.Linear(bottleneck_dim, input_dim)self.activation = nn.ReLU()def forward(self, x):z = self.down_proj(x)z = self.activation(z)z = self.up_proj(z)return x + zclass BertWithAdapters(nn.Module):def __init__(self, model_name, adapter_dim):super(BertWithAdapters, self).__init__()self.bert = BertModel.from_pretrained(model_name)self.adapters = nn.ModuleList([Adapter(self.bert.config.hidden_size, adapter_dim) for _ in range(self.bert.config.num_hidden_layers)])def forward(self, input_ids, attention_mask=None, token_type_ids=None):outputs = self.bert(input_ids, attention_mask=attention_mask, token_type_ids=token_type_ids)sequence_output = outputs[0]for i, adapter in enumerate(self.adapters):sequence_output = adapter(sequence_output)return sequence_output# 使用适配器微调BERT模型
model = BertWithAdapters('bert-base-uncased', adapter_dim=64)
optimizer = torch.optim.Adam(model.adapters.parameters(), lr=1e-4)

五、差分微调(Delta Tuning)

差分微调(Delta Tuning)是一种高效的模型微调方法,通过仅优化与预训练模型参数的差值部分(增量),而不是整个参数集,从而降低计算和存储成本。这种方法特别适用于大规模预训练模型,如BERT、GPT等,在实际应用中能有效地减少微调时的资源消耗。

差分微调的原理

差分微调的基本思想是将模型的参数表示为预训练参数和微调增量的组合。具体来说,对于预训练模型的参数 θ,我们在微调过程中引入一个增量参数 Δθ,使得微调后的参数 θ′ 可以表示为:

\theta^{\prime}=\theta+\Delta\theta

在训练过程中,我们只优化增量参数 Δθ,保持预训练参数 θ 不变。

代码实例

加载预训练模型:

from transformers import BertModel, BertConfig# 加载预训练的BERT模型
model = BertModel.from_pretrained('bert-base-uncased')
pretrained_params = {name: param.clone() for name, param in model.named_parameters()}

定义增量参数

创建增量参数 Δθ,并将其初始化为零。

import torch# 定义增量参数
delta_params = {name: torch.zeros_like(param) for name, param in pretrained_params.items()}

微调模型

在训练过程中,只更新增量参数 Δθ。

optimizer = torch.optim.Adam(delta_params.values(), lr=1e-4)for epoch in range(num_epochs):for batch in data_loader:# 前向传播outputs = model(input_ids=batch['input_ids'], attention_mask=batch['attention_mask'])# 计算损失loss = loss_function(outputs, batch['labels'])# 反向传播optimizer.zero_grad()loss.backward()# 更新增量参数optimizer.step()# 更新模型参数for name, param in model.named_parameters():param.data = pretrained_params[name] + delta_params[name]

完整的demo:

import torch
from transformers import BertModel, BertTokenizerclass DeltaBertModel(nn.Module):def __init__(self, model_name):super(DeltaBertModel, self).__init__()self.bert = BertModel.from_pretrained(model_name)self.delta_params = nn.ParameterDict({name: nn.Parameter(torch.zeros_like(param))for name, param in self.bert.named_parameters()})def forward(self, input_ids, attention_mask=None, token_type_ids=None):outputs = self.bert(input_ids, attention_mask=attention_mask, token_type_ids=token_type_ids)return outputsdef update_parameters(self):for name, param in self.bert.named_parameters():param.data += self.delta_params[name].data# 初始化模型和优化器
model_name = 'bert-base-uncased'
model = DeltaBertModel(model_name)
optimizer = torch.optim.Adam(model.delta_params.parameters(), lr=1e-4)# 模拟训练循环
for epoch in range(num_epochs):for batch in data_loader:input_ids = batch['input_ids']attention_mask = batch['attention_mask']labels = batch['labels']# 前向传播outputs = model(input_ids, attention_mask=attention_mask)logits = outputs[0]# 计算损失loss = loss_function(logits, labels)# 反向传播optimizer.zero_grad()loss.backward()optimizer.step()# 更新模型参数model.update_parameters()

六、多任务学习(Multi-task Learning)

多任务学习(Multi-task Learning, MTL)是一种机器学习方法,通过同时学习多个相关任务来提高模型的泛化能力和效率。与单任务学习不同,多任务学习旨在通过共享不同任务之间的信息,利用它们的共同特性,提升整体模型的性能。多任务学习在自然语言处理、计算机视觉、推荐系统等领域有广泛的应用。

多任务学习的原理

多任务学习的基本思想是将多个相关任务放在同一个模型中进行训练,这些任务共享部分模型参数,从而实现知识的共享和互补。多任务学习的主要目标是通过共享不同任务之间的信息来提高模型的泛化能力,减少过拟合风险。

多任务学习的方法

多任务学习的实现可以通过多种方法,主要包括硬共享(hard parameter sharing)和软共享(soft parameter sharing)两种。

硬共享(Hard Parameter Sharing)

硬共享是多任务学习中最常见的方法,多个任务共享模型的部分层或参数。在这种方法中,底层网络的参数在所有任务之间共享,而任务特定的参数只在各自的任务上进行微调。

import torch
import torch.nn as nnclass SharedModel(nn.Module):def __init__(self):super(SharedModel, self).__init__()self.shared_layers = nn.Sequential(nn.Linear(768, 256),nn.ReLU(),nn.Linear(256, 128),nn.ReLU())self.task1_head = nn.Linear(128, 10)  # 任务1的输出层self.task2_head = nn.Linear(128, 5)   # 任务2的输出层def forward(self, x, task):x = self.shared_layers(x)if task == 'task1':return self.task1_head(x)elif task == 'task2':return self.task2_head(x)# 创建模型和优化器
model = SharedModel()
optimizer = torch.optim.Adam(model.parameters(), lr=1e-4)# 训练循环
for epoch in range(num_epochs):for batch in data_loader:inputs, labels, task = batch['inputs'], batch['labels'], batch['task']optimizer.zero_grad()outputs = model(inputs, task)loss = loss_function(outputs, labels)loss.backward()optimizer.step()

软共享(Soft Parameter Sharing)

软共享通过为每个任务定义独立的模型参数,同时在训练过程中通过正则化项使得不同任务的参数尽可能相似。这种方法保留了任务特定的特征,同时实现了信息共享。

class SoftSharedModel(nn.Module):def __init__(self):super(SoftSharedModel, self).__init__()self.task1_layers = nn.Sequential(nn.Linear(768, 256),nn.ReLU(),nn.Linear(256, 128),nn.ReLU())self.task2_layers = nn.Sequential(nn.Linear(768, 256),nn.ReLU(),nn.Linear(256, 128),nn.ReLU())self.shared_layers = nn.Sequential(nn.Linear(128, 64),nn.ReLU(),nn.Linear(64, 32),nn.ReLU())self.task1_head = nn.Linear(32, 10)self.task2_head = nn.Linear(32, 5)def forward(self, x, task):if task == 'task1':x = self.task1_layers(x)elif task == 'task2':x = self.task2_layers(x)x = self.shared_layers(x)if task == 'task1':return self.task1_head(x)elif task == 'task2':return self.task2_head(x)# 创建模型和优化器
model = SoftSharedModel()
optimizer = torch.optim.Adam(model.parameters(), lr=1e-4)# 训练循环
for epoch in range(num_epochs):for batch in data_loader:inputs, labels, task = batch['inputs'], batch['labels'], batch['task']optimizer.zero_grad()outputs = model(inputs, task)loss = loss_function(outputs, labels)loss.backward()optimizer.step()

代码实例 

from transformers import BertModel, BertTokenizerclass MultiTaskBertModel(nn.Module):def __init__(self, model_name):super(MultiTaskBertModel, self).__init__()self.bert = BertModel.from_pretrained(model_name)self.shared_layers = nn.Linear(768, 128)self.task1_head = nn.Linear(128, 10)  # 任务1:文本分类self.task2_head = nn.Linear(128, 5)   # 任务2:情感分析def forward(self, input_ids, attention_mask=None, token_type_ids=None, task=None):outputs = self.bert(input_ids, attention_mask=attention_mask, token_type_ids=token_type_ids)pooled_output = outputs[1]  # 使用BERT的[CLS]标记的输出shared_output = self.shared_layers(pooled_output)if task == 'task1':return self.task1_head(shared_output)elif task == 'task2':return self.task2_head(shared_output)# 创建模型和优化器
model_name = 'bert-base-uncased'
model = MultiTaskBertModel(model_name)
optimizer = torch.optim.Adam(model.parameters(), lr=1e-4)# 训练循环
for epoch in range(num_epochs):for batch in data_loader:input_ids = batch['input_ids']attention_mask = batch['attention_mask']labels = batch['labels']task = batch['task']optimizer.zero_grad()outputs = model(input_ids, attention_mask=attention_mask, task=task)loss = loss_function(outputs, labels)loss.backward()optimizer.step()

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

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

相关文章

政策及需求多因素驱动下 中国适老化改造市场空间大

政策及需求多因素驱动下 中国适老化改造市场空间大 适老化改造是为了提高老年人居住环境的舒适度和安全性,满足老年人居住需求进行的建筑改造,根据住房和城乡建设部城市建设司发布的《城市居家适老化改造指导手册》可以将适老化改造分为基础性改造和提升…

【linux】新增磁盘的使用

查看硬盘和分区信息 【Linux】lsblk 命令使用-CSDN博客 lsblk 根据您提供的lsblk命令输出,我们可以看到系统中的块设备及其分区信息。以下是对每个设备的解释: vda:这是一个大小为40G的磁盘设备。 vda1:这是vda磁盘的第一个分区…

Python学习---基于TCP协议的网络通信程序案例

TCP简介: ●TCP 面向连接、可靠的、基于字节流的传输控制协议 ●TCP的特点 ○面向连接 ○可靠传输 ■应答机制 ■超时重传 ■错误校验 ■流量管控 ●TCP通信模型 TCP严格区分客户…

智慧展厅设计的难点有哪些

1、运用先进的展示技术 将全息影像、三维投影、虚拟现实、人机互动等技术做做完美衔接,把展厅的内容展示做到丰富多彩,从而让展厅富有科技感和艺术性。 2、内容要生动有趣 从而更好地吸引参观者。展厅设计师要与客户有良好深入的沟通,搜集与整…

TP6 模型批量插入获取插入的自增ID

在TP框架中,数据插入 添加一条数据,返回添加成功的条数 $data [foo > bar, bar > foo]; Db::name(user)->save($data); // 或者 Db::name(user)->insert($data); 批量添加 $data [[foo > bar, bar > foo],[foo > bar1, bar > foo1],[…

无人机的相关基础知识(看不懂了 待定以后继续补充)

视频: 【浙江大学】浙大博导带你从0制作无人机_哔哩哔哩_bilibili 什么是无人飞行器 无人机自主导航构架 IMU(加速度计和陀螺仪),可以测出当前的 加速度和角速度 这俩信息再去融合外部传感器 (例如视觉传感器或者雷…

YoloV9实战与改进——专栏目录

摘要 🔥🚀本专栏教你如何嗨翻Yolov9!🚀🔥 🚀炸裂升级:嗨,小伙伴们!这里有一波Yolov9的升级大招,带你领略最新论文的精华!💥 什么注意…

lenovo联想小新Pro 16 APH8 2023款(83AR)笔记本电脑原装出厂Windows11系统镜像安装包下载

恢复出厂开箱状态OEM预装win11系统,自带恢复重置初始化还原功能 下载链接:https://pan.baidu.com/s/1n_mPM4ZrLPrmXpCTukuyCQ?pwdmnwj 提取码:mnwj 联想原装系统自带所有驱动、出厂主题壁纸、系统属性联机支持标志、Office办公软件、联想…

王安石,变革年代的文学巨匠

王安石,字介甫,号半山,生于北宋真宗天禧五年(公元1021年),卒于哲宗元佑元年(公元1086年),享年66岁。他是中国历史上杰出的政治家、思想家、文学家和改革家,其…

2024年电工杯A题论文首发+摘要分享+问题一代码分享

问题一论文代码链接:https://pan.baidu.com/s/1kDV0DgSK3E4dv8Y6x7LExA 提取码:sxjm --来自百度网盘超级会员V5的分享 园区微电网风光储协调优化配置 摘要:园区微电网由风光发电和主电网联合为负荷供电,为了尽量提高风光电量的…

python ofd转pdf及图片

本文部分内容参考,如有侵权请联系删除:使用 easyofd 解析ofd 文件_python模块easyofd如何使用-CSDN博客 背景需求:需要将邮箱中得ofd格式发票提取出来转换成pdf或者图片。 在网上搜了发现使用pyofd包,安装之后使用各种问题&…

.NET 一款兼容Exchange获取密钥的工具

01阅读须知 此文所提供的信息只为网络安全人员对自己所负责的网站、服务器等(包括但不限于)进行检测或维护参考,未经授权请勿利用文章中的技术资料对任何计算机系统进行入侵操作。利用此文所提供的信息而造成的直接或间接后果和损失&#xf…

电商平台的消费增值模式革新

在当今的电商市场,用户留存和粘性是各大平台竞相追求的目标。而消费增值模式,以其独特的激励机制,正逐渐成为电商平台吸引和留住用户的新策略。 一、消费即投资:创新的返利机制 在传统的电商消费中,消费者完成交易后&…

小阿轩yx-FTP文件传输服务

小阿轩yx-FTP文件传输服务 协议 用来上传和下载实现远程共享文件统一管理文件 原理 控制文件的双向传输,是一个应用程序工作在TCP/IP协议簇的提高文件传输的共享性和可靠性使用C/S模式的工作方式连接同时处理服务器和客户端的连接命令和数据传输将命令和数据分…

【NumPy】NumPy实战入门:线性代数(dot、linalg)与随机数(numpy.random)详解

🧑 博主简介:阿里巴巴嵌入式技术专家,深耕嵌入式人工智能领域,具备多年的嵌入式硬件产品研发管理经验。 📒 博客介绍:分享嵌入式开发领域的相关知识、经验、思考和感悟,欢迎关注。提供嵌入式方向…

Modular RPG Hero PBR

-掩码着色着色器提供了无限的颜色变化。(适用于标准/HDRP/URP 11.0.0) -为剑与盾/双剑/双剑姿态提供了简单的角色控制器。(不包括弓和魔杖控制器)(它是用旧的输入系统建造的) -HDRP/URP(11.0.0)SRP 100%支持常规着色器和遮罩着色着色器(基于着色器图形) -具有许多模块…

rtk技术的使用, test ok

1. 什么是gnss 2 rtk定位

五分钟部署开源运维平台Spug结合内网穿透实现远程登录管理

文章目录 前言1. Docker安装Spug2 . 本地访问测试3. Linux 安装cpolar4. 配置Spug公网访问地址5. 公网远程访问Spug管理界面6. 固定Spug公网地址 前言 Spug 面向中小型企业设计的轻量级无 Agent 的自动化运维平台,整合了主机管理、主机批量执行、主机在线终端、文件…

C语言中的 ?: :三元运算符详解

C语言中的 ?: :三元运算符详解 在C语言的浩瀚代码海洋中,三元运算符(?:)如同一位优雅的舞者,以简洁的姿态完成条件判断与赋值的双重任务。它以问号(?)和冒号(:)这两个…

Spring boot 注入成员变量HttpServletRequest的原理

前言 最近做项目,springboot项目,本来我们在controller的requestmapping取参数值或者返回写时,使用方法参数,但是发现老项目直接注入了成员变量,Spring本身是单例的,如果是成员变量注入,那么也…