参数高效微调PEFT(三)快速入门LoRA、AdaLoRA

参数高效微调PEFT(三)快速入门LoRA、AdaLoRA

  • 我们已经了解了HuggingFace中peft库的几种高效微调方法。

参数高效微调PEFT(一)快速入门BitFit、Prompt Tuning、Prefix Tuning

参数高效微调PEFT(二)快速入门P-Tuning、P-Tuning V2

  • 今天我们继续了解大火的高效微调方法LoRA以及改进的AdaLoRA。
  • 另外,QLoRA是模型量化 (Quantilization) 与LoRA的结合,我们会在后面低精度微调时(微调Chat GLM3的7B模型)进行介绍。
  • 注意:我们到目前都是以单精度FP32加载模型,模型本身占用的显存大小并没有改变。

1 LoRA

  • 将小型网络模块添加到PLMs中,保持基础模型保持不变的情况下仅针对每个任务微调这些模块,可以用于所有任务。
  • 这样,只需引入和更新少量任务特定的参数,就可以适配下游的任务,大大提高了预训练模型的实用性。如:Adapter tuning、Prefix tuning、Prompt Tuning等。
  • 这类方法虽然大大减少了内存消耗,但是这些方法存在一些问题。
    • 比如:Adapter tuning引入了推理延时;
    • Prefix tuning或Prompt tuning直接优化Prefix和Prompt是非单调的,比较难收敛,并且消耗了输入的token。
  • LoRA训练完成后,可以将两个低秩矩阵与原始模型中的权重进行合并,合并后的模型与原始模型无异,避免了推理期间Prompt系列方法带来的额外计算量

1.1 LoRA简介

  • 论文地址:LORA: LOW-RANK ADAPTATION OF LARGE LANGUAGE MODELS (2106)

  • LORA的核心思想就是通过低秩分解来模拟参数的改变量,从而以极小的参数量来实现大模型的间接训练。

    • 预训练模型中存在一个极小的内在维度,这个内在维度是发挥核心作用的地方。
    • 在微调的过程中,权重的更新依然也有如此特点,即也存在一个内在维度 (内在秩)。
  • 如下图所示,可训练层维度和预训练模型层维度一致为d,先将维度d通过全连接层降维至r,再从r通过全连接层映射回d维度。其中,r<<d,r是矩阵的秩,这样矩阵计算就从 d × d d × d d×d变为 d × r + r × d d × r + r × d d×r+r×d,参数量减少很多。

    • 在下游任务训练时,固定模型的其他参数,只优化新增的两个矩阵的权重参数,将PLM跟新增的通路两部分的结果加起来作为最终的结果(两边通路的输入、输出维度都是一致的),即 h = W x + B A x h=Wx+BAx h=Wx+BAx
    • 第一个矩阵的A的权重参数会通过高斯函数初始化(注意:A矩阵不能初始化为零矩阵);
    • 第二个矩阵的B的权重参数则会初始化为零矩阵,这样能保证训练开始时新增的通路 B A = 0 BA=0 BA=0,从而对模型结果没有影响。

在这里插入图片描述

  • 训练完成后,可以将两个低秩矩阵与原始模型中的权重进行合并,合并后的模型与原始模型无异,避免了推理期间Prompt系列方法带来的额外计算量

  • 我们知道Transformer的权重矩阵包括Attention模块里用于计算query,key,value的Wq、Wk、Wv、多头attention的Wo以及MLP层的权重矩阵。如下图,LoRA只应用于Attention模块中的4种权重矩阵,而且通过消融实验发现同时调整 Wq 和 Wv 会产生最佳结果。

在这里插入图片描述

  • 如下图所示,可以发现保证权重矩阵的种类的数量比起增加隐藏层维度r更为重要,增加r并不一定能覆盖更加有意义的子空间。

在这里插入图片描述

  • 通常情况下,r选择为4,8,16即可,peft库中默认为8。

在这里插入图片描述

  • 在众多数据集上LoRA在只训练极少量参数的前提下,最终在性能上能和全量微调匹配,甚至在某些任务上优于全量微调。

1.2 LoRA源码分析

我们在peft库默认配置下(LoraConfig(task_type=TaskType.CAUSAL_LM)),进行源码分析。

from peft import LoraConfig, TaskType, get_peft_model
config = LoraConfig(task_type=TaskType.CAUSAL_LM)
model = get_peft_model(model, config)

LoRA的代码主要分为三个部分:初始化、推理和参数合并。

1.2.1 LoRA初始化分析

我们先看下LoRA初始化,默认设置下,LoRA为线性层class Linear(nn.Linear, LoRALayer)

  • LoRA仅支持nn.Linear、nn.Embedding和nn.Conv2d,微调线性层是常见的做法。

  • 通过MAPPING找到PeftModelForCausalLM进行初始化

# peft\mapping.pydef get_peft_model(model: PreTrainedModel, peft_config: PeftConfig, adapter_name: str = "default") -> PeftModel:......# 1、判断任务类型 如果不是['SEQ_CLS', 'SEQ_2_SEQ_LM', 'CAUSAL_LM', 'TOKEN_CLS', 'QUESTION_ANS', 'FEATURE_EXTRACTION']里类型 而且 不是prompt_learningif peft_config.task_type not in MODEL_TYPE_TO_PEFT_MODEL_MAPPING.keys() and not peft_config.is_prompt_learning:return PeftModel(model, peft_config, adapter_name=adapter_name)# 2、prompt_learningif peft_config.is_prompt_learning:peft_config = _prepare_prompt_learning_config(peft_config, model_config)# 3、通过mapping找到peft.peft_model.PeftModelForCausalLMreturn MODEL_TYPE_TO_PEFT_MODEL_MAPPING[peft_config.task_type](model, peft_config, adapter_name=adapter_name)
  • 然后我们会进入PeftModelForCausalLM的初始化,此时会调用父类PeftModel的初始化方法
# peft\peft_model.py
class PeftModelForCausalLM(PeftModel):def __init__(self, model, peft_config: PeftConfig, adapter_name="default"):# 调用父类PeftModel的初始化方法super().__init__(model, peft_config, adapter_name)self.base_model_prepare_inputs_for_generation = self.base_model.prepare_inputs_for_generation......
  • 父类PeftModel的初始化中,会进行LoraModel的初始化;在LoraModel中,会进行父类BaseTuner的初始化
# peft\peft_model.py
class PeftModel(PushToHubMixin, torch.nn.Module):def __init__(self, model: PreTrainedModel, peft_config: PeftConfig, adapter_name: str = "default"):super().__init__()self.base_model = modelself.config = getattr(self.base_model, "config", {"model_type": "custom"})self.modules_to_save = Noneself.peft_config = {}self.active_adapter = adapter_nameself.peft_type = peft_config.peft_typeif not peft_config.is_prompt_learning:# 不是prompt_learning,我们进入此分支self.peft_config[adapter_name] = peft_config# 通过PEFT_TYPE_TO_MODEL_MAPPING我们获取peft.tuners.lora.LoraModel# 然后会进入LoraModel的初始化self.base_model = PEFT_TYPE_TO_MODEL_MAPPING[peft_config.peft_type](self.base_model, self.peft_config, adapter_name)self.set_additional_trainable_modules(peft_config, adapter_name)else:self.add_adapter(adapter_name, peft_config)......# peft\tuners\lora.py
class LoraModel(BaseTuner):def __init__(self, model, config, adapter_name) -> None:# 进行BaseTuner的初始化super().__init__(model, config, adapter_name)    .....    
  • 在父类BaseTuner中,会调用inject_adapter方法。
  • inject_adapter中遍历每一个key,调用_create_and_replace,
  • target默认为线性层,会调用_create_new_module方法,返回一个new_module,然后将旧的module替换为new_module ,即加了LoRA后的module。
  • create_new_module会进行new_module = Linear(adapter_name, in_features, out_features, bias=bias, **kwargs),即peft\tuners\lora.py中Linear的初始化。
# peft\tuners\tuners_utils.py
class BaseTuner(nn.Module, ABC):def __init__(self, model, peft_config: Union[PeftConfig, dict[str, PeftConfig]], adapter_name: str) -> None:super().__init__()self.model = model......# 会掉用inject_adapter方法self.inject_adapter(self.model, adapter_name)# Copy the peft_config in the injected model.self.model.peft_config = self.peft_config......     # peft\tuners\lora.pydef inject_adapter(self, model: nn.Module, adapter_name: str):peft_config = self.peft_config[adapter_name]is_target_modules_in_base_model = Falsekey_list = [key for key, _ in model.named_modules()]model_config = getattr(model, "config", {"model_type": "custom"})if hasattr(model_config, "to_dict"):model_config = model_config.to_dict()# 获取高效微调的默认设置项peft_config = self._prepare_adapter_config(peft_config, model_config)# 遍历每一个keyfor key in key_list:is_target_modules_in_base_model = True# 例如,key= 'transformer.h.0.self_attention.query_key_value'# parent = BloomAttention(#  (query_key_value): Linear(in_features=64, out_features=192, bias=True)#  (dense): Linear(in_features=64, out_features=64, bias=True)#  (attention_dropout): Dropout(p=0.0, inplace=False)# )# target = Linear(in_features=64, out_features=192, bias=True)# target_name = 'query_key_value'parent, target, target_name = _get_submodules(model, key)optionnal_kwargs = {"loaded_in_8bit": getattr(model, "is_loaded_in_8bit", False),"loaded_in_4bit": getattr(model, "is_loaded_in_4bit", False),"current_key": key,}# self._create_and_replace(peft_config, adapter_name, target, target_name, parent, **optionnal_kwargs)......# peft\tuners\lora.pydef _create_and_replace(self,lora_config,adapter_name,target,target_name,parent,**optionnal_kwargs,):......# TODO: better deal with thatif isinstance(target, LoraLayer) and isinstance(target, torch.nn.Conv2d):target.update_layer_conv2d(adapter_name,lora_config.r,lora_config.lora_alpha,lora_config.lora_dropout,lora_config.init_lora_weights,)......else:# target默认为线性层,会进入此分支new_module = self._create_new_module(lora_config, adapter_name, target, **kwargs)   # 此方法会将原始module进行替换,替换为加上lora后的self._replace_module(parent, target_name, new_module, target)                    
# 高效微调的默认设置项
LoraConfig(peft_type=<PeftType.LORA: 'LORA'>           # peft的类型, auto_mapping=None, base_model_name_or_path='', revision=None, task_type=<TaskType.CAUSAL_LM: 'CAUSAL_LM'> # 任务类型, inference_mode=False, r=8                                # 秩大小,一般4,8,16,小数据集一般设置更小的r(1,2), target_modules=['query_key_value'] # 指定应用lora的目标模块,Bloom模型默认为query_key_value,可以使用正则, lora_alpha=8         # 尺度缩放参数,ΔW按α/r缩放,即scaling[adapter_name]=lora_alpha/r,默认为1, lora_dropout=0.0     # lora层的dropout比率, fan_in_fan_out=False, bias='none'          # 偏差可以是“无”、“全部”或“lora_only”。如果是“all”或“lora_only”,则相应的偏差将在训练期间更新, modules_to_save=None # 全量微调时,模型的layer名称, init_lora_weights=True, layers_to_transform=None, layers_pattern=None
)
  • Linear初始化中,重要的代码就是update_layer

  • update_layer中,就会初始化秩为r的可训练参数A和B

  • new_module = Linear(in_features=64, out_features=192, bias=True(lora_dropout): ModuleDict((default): Identity())(lora_A): ModuleDict((default): Linear(in_features=64, out_features=8, bias=False))(lora_B): ModuleDict((default): Linear(in_features=8, out_features=192, bias=False))(lora_embedding_A): ParameterDict()(lora_embedding_B): ParameterDict()
    )
    
  • 上面就是Linear初始化后的模块,即new_module。初始化后,就会将之前的module进行替换。

# 替换前
BloomAttention((query_key_value): Linear(in_features=64, out_features=192, bias=True)(dense): Linear(in_features=64, out_features=64, bias=True)(attention_dropout): Dropout(p=0.0, inplace=False)
)# 通过下面代码进行替换
# peft\tuners\lora.py中的_create_and_replace方法
new_module = self._create_new_module(lora_config, adapter_name, target, **kwargs)
self._replace_module(parent, target_name, new_module, target)# 替换后
BloomAttention((query_key_value): Linear(in_features=64, out_features=192, bias=True(lora_dropout): ModuleDict((default): Identity())(lora_A): ModuleDict((default): Linear(in_features=64, out_features=8, bias=False))(lora_B): ModuleDict((default): Linear(in_features=8, out_features=192, bias=False))(lora_embedding_A): ParameterDict()(lora_embedding_B): ParameterDict())(dense): Linear(in_features=64, out_features=64, bias=True)(attention_dropout): Dropout(p=0.0, inplace=False)
)
  • 上面就是替换前后BloomAttention模块的结构。
# peft\tuners\lora.py
class Linear(nn.Linear, LoraLayer):# Lora implemented in a dense layerdef __init__(self,adapter_name: str,in_features: int,out_features: int,r: int = 0,lora_alpha: int = 1,lora_dropout: float = 0.0,fan_in_fan_out: bool = False,  # Set this to True if the layer to replace stores weight like (fan_in, fan_out)is_target_conv_1d_layer: bool = False,**kwargs,):init_lora_weights = kwargs.pop("init_lora_weights", True)nn.Linear.__init__(self, in_features, out_features, **kwargs)LoraLayer.__init__(self, in_features=in_features, out_features=out_features)# Freezing the pre-trained weight matrixself.weight.requires_grad = Falseself.fan_in_fan_out = fan_in_fan_outif fan_in_fan_out:self.weight.data = self.weight.data.Tnn.Linear.reset_parameters(self)# 重要代码self.update_layer(adapter_name, r, lora_alpha, lora_dropout, init_lora_weights)self.active_adapter = adapter_nameself.is_target_conv_1d_layer = is_target_conv_1d_layer......
 def update_layer(self, adapter_name, r, lora_alpha, lora_dropout, init_lora_weights):self.r[adapter_name] = rself.lora_alpha[adapter_name] = lora_alphaif lora_dropout > 0.0:# dropoutlora_dropout_layer = nn.Dropout(p=lora_dropout)else:# 默认设置lora_dropout_layer = nn.Identity()self.lora_dropout.update(nn.ModuleDict({adapter_name: lora_dropout_layer}))# Actual trainable parametersif r > 0:# 如果秩大于0,就初始化秩为R的可训练参数A和Bself.lora_A.update(nn.ModuleDict({adapter_name: nn.Linear(self.in_features, r, bias=False)}))self.lora_B.update(nn.ModuleDict({adapter_name: nn.Linear(r, self.out_features, bias=False)}))# 指定lora_alpha参数,用于平衡LORA 和 基础模型的贡献# 这里scaling默认为1self.scaling[adapter_name] = lora_alpha / rif init_lora_weights:self.reset_lora_parameters(adapter_name)self.to(self.weight.device)        

1.2.2 前向过程

  • 前向过程中,最终会调用BloomForCausalLM的前向传播方法。
LoraModel((model): BloomForCausalLM((transformer): BloomModel((word_embeddings): Embedding(250880, 64)(word_embeddings_layernorm): LayerNorm((64,), eps=1e-05, elementwise_affine=True)(h): ModuleList((0-1): 2 x BloomBlock((input_layernorm): LayerNorm((64,), eps=1e-05, elementwise_affine=True)(self_attention): BloomAttention(# 在BloomAttention的query_key_value中,使用LoRA进行微调(query_key_value): Linear(in_features=64, out_features=192, bias=True(lora_dropout): ModuleDict((default): Identity())(lora_A): ModuleDict((default): Linear(in_features=64, out_features=8, bias=False))(lora_B): ModuleDict((default): Linear(in_features=8, out_features=192, bias=False))(lora_embedding_A): ParameterDict()(lora_embedding_B): ParameterDict())(dense): Linear(in_features=64, out_features=64, bias=True)(attention_dropout): Dropout(p=0.0, inplace=False))(post_attention_layernorm): LayerNorm((64,), eps=1e-05, elementwise_affine=True)(mlp): BloomMLP((dense_h_to_4h): Linear(in_features=64, out_features=256, bias=True)(gelu_impl): BloomGelu()(dense_4h_to_h): Linear(in_features=256, out_features=64, bias=True))))(ln_f): LayerNorm((64,), eps=1e-05, elementwise_affine=True))(lm_head): Linear(in_features=64, out_features=250880, bias=False))
)
  • 在BloomAttention中,会调用peft\tuners\lora.py中Linear的前向传播(如下代码)。
  • 注意:self.lora_A和self.lora_B类型为ModuleDict,这是因为LoRA有高级用法,我们以后再介绍:
    • 一个主模型,可以使用多个适配器(默认self.active_adapter=default)
    • 可以切换适配器
    • 可以禁用适配器,获取原始结果
    # peft\tuners\lora.pydef forward(self, x: torch.Tensor):previous_dtype = x.dtypeif self.active_adapter not in self.lora_A.keys():return F.linear(x, transpose(self.weight, self.fan_in_fan_out), bias=self.bias)if self.disable_adapters:if self.r[self.active_adapter] > 0 and self.merged:self.unmerge()result = F.linear(x, transpose(self.weight, self.fan_in_fan_out), bias=self.bias)# 不禁用adapters,且秩r > 0,且不合并   elif self.r[self.active_adapter] > 0 and not self.merged:# 1、result = torch.Size([1, 48, 192])result = F.linear(x, transpose(self.weight, self.fan_in_fan_out), bias=self.bias)x = x.to(self.lora_A[self.active_adapter].weight.dtype)# 2、核心代码# x = torch.Size([1, 48, 64])# self.lora_A['default'] = Linear(in_features=64, out_features=8, bias=False)# x经过self.lora_A,torch.Size([1, 48, 8])# self.lora_B['default'] = Linear(in_features=8, out_features=192, bias=False)# x经过self.lora_B,torch.Size([1, 48, 192])# 然后result相加合并,即h = Wx + BAxresult += (self.lora_B[self.active_adapter](self.lora_A[self.active_adapter](self.lora_dropout[self.active_adapter](x)))* self.scaling[self.active_adapter])else:result = F.linear(x, transpose(self.weight, self.fan_in_fan_out), bias=self.bias)result = result.to(previous_dtype)return result

1.3 LoRA轻量微调bloom模型

同样,我们只需要在加载原模型后、配置训练器前加peft的代码即可。

from peft import LoraConfig, TaskType, get_peft_modelconfig = LoraConfig(task_type=TaskType.CAUSAL_LM# , target_modules=".*\.1.*query_key_value" 指定要添加LoRA的目标模块(支持正则)# , modules_to_save=["word_embeddings"] 全量微调时,模型的layer名称, modules_to_save=None
)model = get_peft_model(model, config)# 打印可训练参数
model.print_trainable_parameters()trainable params: 786,432 || all params: 346,555,392 || trainable%: 0.22692822508443325
  • 配置训练器、模型训练及推理和参数高效微调PEFT(一)快速入门BitFit、Prompt Tuning、Prefix Tuning中2.1一样。
  • 显存消耗情况:
(base) root@autodl-container-adbc11ae52-f2ebff02:~# nvidia-smi   
+-----------------------------------------------------------------------------+
| NVIDIA-SMI 525.89.02    Driver Version: 525.89.02    CUDA Version: 12.0     |
|-------------------------------+----------------------+----------------------+
| GPU  Name        Persistence-M| Bus-Id        Disp.A | Volatile Uncorr. ECC |
| Fan  Temp  Perf  Pwr:Usage/Cap|         Memory-Usage | GPU-Util  Compute M. |
|                               |                      |               MIG M. |
|===============================+======================+======================|
|   0  NVIDIA GeForce ...  On   | 00000000:41:00.0 Off |                  N/A |
| 35%   59C    P2   143W / 250W |   2810MiB / 11264MiB |     31%      Default |
|                               |                      |                  N/A |
+-------------------------------+----------------------+----------------------++-----------------------------------------------------------------------------+
| Processes:                                                                  |
|  GPU   GI   CI        PID   Type   Process name                  GPU Memory |
|        ID   ID                                                   Usage      |
|=============================================================================|
+-----------------------------------------------------------------------------+

1.4 模型保存

LoRA训练完成后,可以将两个低秩矩阵与原始模型中的权重进行合并,合并后的模型与原始模型无异,避免了推理期间Prompt系列方法带来的额外计算量

from transformers import AutoModelForCausalLM, AutoTokenizerfrom peft import PeftModel# 1、加载基础模型
model_path = r'/root/autodl-fs/models/langboat/bloom-389m-zh'model = AutoModelForCausalLM.from_pretrained(model_path, low_cpu_mem_usage=True)
tokenizer = AutoTokenizer.from_pretrained(model_path)# 2、加载PeftModel
p_model = PeftModel.from_pretrained(model, model_id="./chatbot/checkpoint-500/")# 3、模型合并
merge_model = p_model.merge_and_unload()# 4、完整模型保存,此时大小就很大了,下次加载时候可以用AutoModelForCausalLM直接加载
merge_model.save_pretrained("./chatbot/merge_model")

2 AdaLoRA

2.1 AdaLoRA简介

  • 论文地址:ADAPTIVE BUDGET ALLOCATION FOR PARAMETER EFFICIENT FINE-TUNING(23.03)

  • LoRA可以达到与完全微调几乎相当的性能,但是也存在一些问题。

    • LoRA需要预先指定每个增量矩阵的本征秩 r 相同,忽略了在微调预训练模型时,权重矩阵的重要性在不同模块和层之间存在显著差异
    • 并且只训练了Attention,没有训练FFN,事实上FFN更重要。
  • 如下图a所示,将可微调参数全部放在FFN的效果要好于放在attention矩阵中的效果;如下图b所示,同时微调高层参数的效果会比微调底层参数的效果更好。因此,作者提出了AdaLoRA,它根据权重矩阵的重要性得分,在权重矩阵之间自适应地分配参数预算。

在这里插入图片描述

  • AdaLORA主要包含两个模块:

    • (i) SVD形式参数更新(SVD-based adaptation):直接将增量矩阵Δ参数化为SVD的形式,避免了在训练过程中进行SVD计算带来的资源消耗;

    在这里插入图片描述

    • (ii) 基于重要程度的参数分配(Importance-aware rank allocation): 裁剪一些冗余的奇异值。

      具体原理可以参考:AdaLoRA(Adaptive LoRA)详解

2.2 AdaLoRA轻量微调bloom模型

  • 这里我们没有分析AdaLoRA的源码,直接进行轻量微调,有兴趣的可以分析下源码。

  • 同样,我们只需要在加载原模型后、配置训练器前加peft的代码即可。

from peft import get_peft_model, TaskType, AdaLoraConfigconfig = AdaLoraConfig(task_type=TaskType.CAUSAL_LM, r=8, lora_alpha=8, lora_dropout=0, target_modules=["query_key_value"])model = get_peft_model(model, config)# 打印可训练参数
model.print_trainable_parameters()trainable params: 1,179,936 || all params: 346,948,920 || trainable%: 0.3400892557901607
  • 配置训练器、模型训练及推理和参数高效微调PEFT(一)快速入门BitFit、Prompt Tuning、Prefix Tuning中2.1一样。
  • 显存消耗情况:
(base) root@autodl-container-adbc11ae52-f2ebff02:~/autodl-tmp/transformers-code/03-PEFT# nvidia-smi  
+-----------------------------------------------------------------------------+
| NVIDIA-SMI 525.89.02    Driver Version: 525.89.02    CUDA Version: 12.0     |
|-------------------------------+----------------------+----------------------+
| GPU  Name        Persistence-M| Bus-Id        Disp.A | Volatile Uncorr. ECC |
| Fan  Temp  Perf  Pwr:Usage/Cap|         Memory-Usage | GPU-Util  Compute M. |
|                               |                      |               MIG M. |
|===============================+======================+======================|
|   0  NVIDIA GeForce ...  On   | 00000000:41:00.0 Off |                  N/A |
| 33%   54C    P2   119W / 250W |   2816MiB / 11264MiB |     32%      Default |
|                               |                      |                  N/A |
+-------------------------------+----------------------+----------------------++-----------------------------------------------------------------------------+
| Processes:                                                                  |
|  GPU   GI   CI        PID   Type   Process name                  GPU Memory |
|        ID   ID                                                   Usage      |
|=============================================================================|
+-----------------------------------------------------------------------------+

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

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

相关文章

MyBatis基础理解教程,详细分步基础查询表数据练习(通俗易懂、实时更新)

一、MyBatis是什么 MyBatis 是一个持久层框架&#xff0c;简化JDBC开发&#xff0c;它提供了一个从 Java 应用程序到 SQL 数据库的桥梁&#xff0c;用于数据的存储、检索和映射。MyBatis 支持基本的 SQL 操作、高级映射特性以及与 Maven 等构建工具的集成。 二、持久层是什么…

Spring boot集成通义千问大模型

Spring boot集成通义千问大模型 背景 我在用idea进行java开发时发现了通义灵码这款免费的智能代码补全插件&#xff0c;用了一段时间了&#xff0c;感觉很不错。就想着在自己的项目中也能集成通义千问大模型实现智能回答&#xff0c;毕竟对接openai需要解决网络问题&#xff…

战略合作 | 竹云赋能雁塔区数字经济高质量发展

2024年5月30日&#xff0c;由西安市数据局指导&#xff0c;中共西安市雁塔区委、西安市雁塔区人民政府主办的 “雁塔区企业数字化转型发展大会” 在西安开幕。 本次活动以“数智雁塔&#xff0c;引领未来”为主题&#xff0c;特邀业内150余位政府、数字化服务企业、传统行业企…

先导微型数控桌面式加工中心

随着数控技术、传感器技术、人工智能等技术的不断发展&#xff0c;制造业的快速发展和技术的不断进步&#xff0c;小型五轴加工中心的性能将不断提升&#xff0c;五轴联动技术作为解决异性复杂零件高效优质加工问题的重要手段&#xff0c;使其具有更广泛的应用前景。小型五轴加…

5.30 学习总

刷题记录(Codeforces Round 947 &#xff08;Div. 1 Div. 2&#xff09;B,C题)和Codeforces Round 948 &#xff08;Div. 2&#xff09;B题 一.B. 378QAQ and Mochas Array B. 378QAQ and Mochas Array time limit per test 1 second memory limit per test 256 megabytes in…

长难句5.30

Researchers measured people’s cortisol, which is a stress marker, while they were at work and while they were at home and found it higher at what is supposed to be a place of refuge. 研究人员测量了人们在工作中和在家里的皮质醇(压力的一种标志)&#xff0c;结…

为啥装了erlang,还报错erl: command not found?

转载说明&#xff1a;如果您喜欢这篇文章并打算转载它&#xff0c;请私信作者取得授权。感谢您喜爱本文&#xff0c;请文明转载&#xff0c;谢谢。 问题背景&#xff1a; 在一台不通外网的服务器上装rabbitmq&#xff0c;然后在启动的时候&#xff0c;遇到了报错 “/usr/lib/…

C#中使用Mapster

Mapster是一个开源的.NET对象映射库&#xff0c;它提供了一种简单而强大的方式来处理对象之间的映射。 多个映射框架的性能对比&#xff1a; 第一步安装Mapster 使用方法 public class Test {public string name { get; set; }public string sex { get; set; }public string…

C语言数据结构(超详细讲解)| 二叉树的实现

二叉树 引言 在计算机科学中&#xff0c;数据结构是算法设计的基石&#xff0c;而二叉树&#xff08;Binary Tree&#xff09;作为一种基础且广泛应用的数据结构&#xff0c;具有重要的地位。无论是在数据库索引、内存管理&#xff0c;还是在编译器实现中&#xff0c;二叉树都…

记录Win11安装打印机驱动过程

1. 首先下载打印机对应型号的驱动 可以从这里下载&#xff1a;打印机驱动,打印机驱动下载 - 打印机驱动网 2. 下载 3. 打开控制面板-->设备和打印机 找到目标打印机添加设备即可 新增打印纸张尺寸

B站稿件生产平台高可用建设分享

背景 B站作为国内领先的内容分享平台&#xff0c;其核心功能之一便是支持UP主们创作并分享各类视频内容。UP主稿件系统作为B站内容生产的关键环节&#xff0c;承担着从内容创作到发布的全过程管理。为了满足不同创作者的需求&#xff0c;B站提供了多种投稿渠道&#xff0c;包括…

方差分析的七种类型

方差分析&#xff08;ANOVA&#xff09;是一种用于检验两个以上样本均数差别的显著性统计方法。根据不同的研究设计和数据类型&#xff0c;方差分析可以分为以下7种类型。 一、单因素方差分析 ①单因素方差分析说明 单因素方差分析用于研究一个定类数据&#xff08;自变量&am…

【原创教程】MES服务器与成品打标机控制说明

1 实现的功能及应用的场合 MES即制造执行系统(manufacturing execution system,简称MES),即在加强MRP计划的执行功能,把MRP计划同车间作业现场控制,通过执行系统联系起来。 MES是一个生产管理智能化的一个系统,是用于生产时记录数据、产量等信息的智能管理系统。 该项…

SpockMockStatic方法

SpockMockStatic方法 参考: https://blog.csdn.net/knighttools/article/details/44630975 ‍ static方法 import com.meituan.mafka.client.producer.IProducerProcessor; import com.meituan.mdp.langmodel.api.message.AssistantMessage; import com.sankuai.gaigc.arrang…

文件批量重命名001到100如何操作?这几个文件改名小技巧学起来

文件批量重命名001到100怎么操作&#xff1f;作为打工一族&#xff0c;每天都需要跟很多文件打交道&#xff0c;有时文件太多了&#xff0c;查找起来像是大海捞针&#xff0c;特别是图片文件。这个时候我们就需要对大量文件进行整理和排序&#xff0c;这样有助于提高我们的工作…

微信小程序 自定义 tabBar

自定义 tabBar | 微信开放文档 本文案例使用的Taro 非原生微信小程序 使用流程 1. 配置信息 在 app.json 中的 tabBar 项指定 custom 字段&#xff0c;同时其余 tabBar 相关配置也补充完整。所有 tab 页的 json 里需声明 usingComponents 项&#xff0c;也可以在 app.json 全局…

电脑提示缺少vcruntime140_1.dll的解决方法,总结7种有效方法

vcruntime140_1.dll是Microsoft Visual C 2015运行时库的一部分&#xff0c;它为使用Visual Studio 2015开发的应用程序提供了必要的运行时组件。该文件支持C程序的执行&#xff0c;包括内存管理、输入输出操作以及多线程功能等。缺失或损坏此文件可能导致应用程序无法启动或运…

广告联盟四大家

国内四大广告承接商&#xff1a;①抖音旗下-穿山甲②快手旗下-快手联盟③百度旗下-百青藤④腾讯旗下-优量汇 我们目前在互联网上能看到的所有广告都是由他们发放的&#xff0c;在其中我们打小游戏复活看广告&#xff0c;获得道具看广告&#xff0c;看剧看广告&#xff0c;这…

基于词频统计的聚类算法(kmeans)

基于词频统计的聚类算法&#xff08;kmeans&#xff09; 数据集是三个政府报告文件&#xff0c;这里就不做详细描述了&#xff0c;就是简单的txt文件。 实验过程主要分为如下几步&#xff1a; 1.读取数据并进行停用词过滤 2.统计词频 3.基于三篇文章词频统计的层次聚类 4.基于…

废品回收小程序怎么做?有哪些核心功能?

废品回收行业正逐步走向高质量发展的道路。在国家政策的推动下&#xff0c;再生资源市场需求旺盛&#xff0c;行业内部竞争格局逐渐明朗。 随着互联网技术的发展&#xff0c;"互联网回收"成为废品回收行业的一个新趋势。通过微信小程序这种线上平台&#xff0c;用户…