大模型高效微调工具 Hugging Face PEFT

news2024/11/17 1:29:15

概述

HF PEFT是一个为大型预训练模型提供高效微调方法的Python库。它通过采用训练少量提示参数或使用低秩适应等重新参数化方法,减少微调时训练参数的数量。本文主要介绍了PEFT库的内容、与Transformers的集成、核心知识点如AutoPeftModels、PeftConfig、PeftType和TaskType,以及LoRA在文本生成和语音识别任务中的实战应用。

Hugging Face PEFT快速入门

PEFT库是什么?

PEFT 是一个为大型预训练模型提供多种高效微调方法的Python库。
微调传统范式是针对每个下游任务微调模型参数。大模型参数总量庞大,这种方式变得极其昂贵和不切实际。PEFT采用的高效做法是训练少量提示参数(Prompt Tuning)或使用低秩适应(LORA)等重新参数化方法来减少微调时训练参数的数量。

Hugging Face PEFT 库支持的模型和微调方法

选择这个不同的task,对应不同的任务。不同的下游任务,会有不同的模型来支持该下游任务。然后不同的模型内又存在各种各样的PEFT等高效微调的方法。
PEFT methonds
图示 PEFT methonds,来源于HuggingFace官网

PEFT与Transformers结合

既然PEFT是一个单独的Python库,如何与Transformers进行结合呢?
Transformer 预训练模型常见分类
图1 Transformer 预训练模型常见分类
由于现阶段的大模型几乎都是基于预训练的transformers,包括BERT、GPT、T5等预训练的transformer。
那这些神经网络最终怎么样落实到实际可用的代码里呢?
Transformers AutoModel 常见模型
图2 Transformers AutoModel 常见模型
底层依赖TensorFlow、Pytorch等等,高层次抽象则依赖transformers库内一些特定的抽象方法,例如AutoModel。AutoModel常见模型包括AutoModelForMaskedLM、AutoModelForCausalLM和AutoModelForSeq2SeqLM。

PEFT核心类定义与功能说明

Transformers Auto Classes 设计

AutoClasses 旨在通过全局统一的接口 from_pretrained() ,实现基于名称(路径)自动检索预训练权重(模型)、配置文件、词汇表等所有与模型相关的抽象。
Auto Class
图示 Auto Class
在transformers内需要做好模型抽象最重要的三个要素,即auto config、auto tokenizer和auto model。

PEFT AutoPeftModels与Adapters设计

AutoPeftModel通过从配置文件中自动推断任务类型来加载适当的PEFT模型。旨在以一行代码便捷加载一个PEFT模型,而无需担心需要哪个确切的模型、类或手动加载 PeftConfig。简单来说,它既保留了原来transformers里面对大模型的预训练的一套技术路线,比如统一接口、自动检索等。同时,它又不简简单单是一个代表模型,它还能接受这个PEFT,以及各种各样PEFT方法。
PEFT三类方法
图示 市场上主流的PEFT三类方法
目前市面上比较主流的一些PEFT方法主要包括三类,一类是adapter,即适配器;第二类是prefix-tuning为主的Soft Prompt方法;还有一类就是非常通用的LoRA。
根据Auto Class的三件套,即Auto Model、Auto Config和Auto Tokenizer。无论是经典大模型,还是PEFT等演变的模型,其输入端都是直接继承原始设计Auto Tokenizer;将原始的AutoModel变成AutoPeftModel;剩下部分继续沿用Auto Config的最初设计。因此,所有的adapters都可以沿用Auto Config的继承关系。

PEFT类型配置与PeftModel实例化

实例化
怎么样加载适配器?这个PEFT Config也设计有三类不同的抽象,即prompt learning config、PeftConfigMixin和PeftConfig。

  • PromptLearningConfig:基础的类型。PEFT有两大类方法,一类是LoRA ,另一类为Soft Prompt的方法。由于所有Soft Prompt方法都需要有一个基础类型,这个基础类型在PEFT库里就称为PromptLearningConfig。
  • PeftConfig:PeftConfig作为一个基类,具有两个重要参数,即peft_type,task_type。
  • PeftConfigMixin:PeftConfigMixin内置了很多配置,会与HuggingFace HUB做一些交互以及自定义功能插件等等。

PeftType | TaskType

整个流程可以理解为,task_type和peft_type能够自动检索出来下游任务,明确所需要使用的模型,然后这些模型会自动的去找所需要的一些配置config,从而简化问题的复杂性。
PEFT 无缝对接 Transformers Trainer 训练模型
图示 PEFT 无缝对接 Transformers Trainer 训练模型
Transformers trainer来训练模型,需要具备几个要素:第一,输入,即明确数据要在什么模型上训练;第二,训练参数;第三,数据。

实战PEFT库模型微调多种下游任务

LORA 低秩适配 OPT-6.7B 文本生成任务

PEFT 库微调工作原理与最佳实践(以 LORA 为例)
LoraModel
在 peft 中使用 LORA 非常简捷,借助 PeftModel 抽象,我们可以快速使用低秩适配器(LORA)到任意模型。通过使用 peft 中的 get_peft_model 工具函数来实现。

#从peft库导入Loraconfig和get peft model函数
from peft import LoraConfig,get_peft_model
#创建-个LoraConfig对象,用于设置LoRA(Low-Rank Adaptation)的配置参数
config = LoraConfig(
    r=8, # LORA的秩,影响LORA矩阵的大小
    lora_alpha=32,#LORA适应的比例因子
    #指定将LORA应用到的模型模块,通常是attention和全连接层的投影
    target_modules = ["q_proj","k_proj","v_proj", "out _proj","fc_in", "fc _out"],
    lora_dropout=0.05,# 在LORA模块中使用的dropout率
    bias="none",# 设置bias的使用方式,这里没有使用bias
    task_type="CAUSAL_LM" # 任务类型,这里设置为因果(自回归)语言模型
)    
                       
# 使用get_peft_model函数和给定的配置来获取一个PEFT模型
model = get_peft_model(model,config)
                       
# 打印出模型中可训练的参数
model.print_trainable_parameters()       

完整示例如下

如何使用最新的peft库和bitsandbytes来以8-bits加载大语言模型,并对其进行高效微调。
微调方法将依赖于一种名为“低秩适配器”(LoRA)的方法,与其说微调整个模型,不如理解为只需要微调这些适配器(Adapter)并在模型中正确加载它们。

1. 加载模型

加载Facebook opt-6.7b 模型,半精度(float16)模型权重大约需要13GB左右显存,而以以8-bits 加载,只需要大约7GB左右显存。

import os

import torch
import torch.nn as nn
import bitsandbytes as bnb
from transformers import GPT2Tokenizer, AutoConfig, OPTForCausalLM

model_id = "facebook/opt-6.7b"

model = OPTForCausalLM.from_pretrained(model_id, load_in_8bit=True)

tokenizer = GPT2Tokenizer.from_pretrained(model_id)

2. PEFT 微调前的模型处理

在使用 peft 训练 int8 模型之前,需要进行一些预处理:

  • 将所有非 int8 模块转换为全精度(fp32)以保证稳定性;
  • 为输入嵌入层添加一个 forward_hook,以启用输入隐藏状态的梯度计算
  • 启用梯度检查点以实现更高效的内存训练

使用 peft 库预定义的工具函数 prepare_model_for_int8_training,便可自动完成以上模型处理工作。

from peft import prepare_model_for_int8_training

model = prepare_model_for_int8_training(model)
# 获取当前模型占用的 GPU显存(差值为预留给 PyTorch 的显存)
memory_footprint_bytes = model.get_memory_footprint()
memory_footprint_mib = memory_footprint_bytes / (1024 ** 3)  # 转换为 GB

print(f"{memory_footprint_mib:.2f}GB")

输入模型为:
输出就结果

3. LoRA Adapter 配置

在 peft 中使用LoRA非常简捷,借助PeftModel抽象,我们可以快速使用低秩适配器(LoRA)到任意模型。通过使用 peft 中的 get_peft_model 工具函数来实现。

# 从peft库导入LoraConfig和get_peft_model函数
from peft import LoraConfig, get_peft_model

# 创建一个LoraConfig对象,用于设置LoRA(Low-Rank Adaptation)的配置参数
config = LoraConfig(
    r=8,  # LoRA的秩,影响LoRA矩阵的大小
    lora_alpha=32,  # LoRA适应的比例因子
    # 指定将LoRA应用到的模型模块,通常是attention和全连接层的投影
    target_modules = ["q_proj", "k_proj", "v_proj", "out_proj", "fc_in", "fc_out"],
    lora_dropout=0.05,  # 在LoRA模块中使用的dropout率
    bias="none",  # 设置bias的使用方式,这里没有使用bias
    task_type="CAUSAL_LM"  # 任务类型,这里设置为因果(自回归)语言模型
)

# 使用get_peft_model函数和给定的配置来获取一个PEFT模型
model = get_peft_model(model, config)

# 打印出模型中可训练的参数
model.print_trainable_parameters()

打印待训练模型参数的实现逻辑:

def print_trainable_parameters(self,):
    """
    Prints the number of trainable parameters in the model.
    """
    trainable_params = 0
    all_param = 0
    for _, param in model.named_parameters():
        all_param += param.numel()
        if param.requires_grad:
            trainable_params += param.numel()
    print(
        f"trainable params: {trainable_params} || all params: {all_param} || trainable%: {100 * trainable_params / all_param}"
    )

4. 数据处理

from datasets import load_dataset

dataset = load_dataset("Abirate/english_quotes")

输出结果

from datasets import ClassLabel, Sequence
import random
import pandas as pd
from IPython.display import display, HTML

def show_random_elements(dataset, num_examples=10):
    assert num_examples <= len(dataset), "Can't pick more elements than there are in the dataset."
    picks = []
    for _ in range(num_examples):
        pick = random.randint(0, len(dataset)-1)
        while pick in picks:
            pick = random.randint(0, len(dataset)-1)
        picks.append(pick)
    
    df = pd.DataFrame(dataset[picks])
    for column, typ in dataset.features.items():
        if isinstance(typ, ClassLabel):
            df[column] = df[column].transform(lambda i: typ.names[i])
        elif isinstance(typ, Sequence) and isinstance(typ.feature, ClassLabel):
            df[column] = df[column].transform(lambda x: [typ.feature.names[i] for i in x])
    display(HTML(df.to_html()))
    
show_random_elements(dataset["train"])

输出结果

tokenized_dataset = dataset.map(lambda samples: tokenizer(samples["quote"]), batched=True)
from transformers import DataCollatorForLanguageModeling

# 数据收集器,用于处理语言模型的数据,这里设置为不使用掩码语言模型(MLM)
data_collator = DataCollatorForLanguageModeling(tokenizer, mlm=False)

5. 微调模型

from transformers import TrainingArguments, Trainer

model_dir = "models"

training_args = TrainingArguments(
        output_dir=f"{model_dir}/{model_id}-lora",  # 指定模型输出和保存的目录
        per_device_train_batch_size=4,  # 每个设备上的训练批量大小
        learning_rate=2e-4,  # 学习率
        fp16=True,  # 启用混合精度训练,可以提高训练速度,同时减少内存使用
        logging_steps=20,  # 指定日志记录的步长,用于跟踪训练进度
        max_steps=100, # 最大训练步长
        # num_train_epochs=1  # 训练的总轮数
    )

添加 LoRA 模块后的模型如下
添加 LoRA 模块后的模型

trainer = Trainer(
    model=model,  # 指定训练时使用的模型
    train_dataset=tokenized_dataset["train"],  # 指定训练数据集
    args=training_args,
    data_collator=data_collator
)
model.use_cache = False
trainer.train()

输出结果

6. 保存 LoRA 模型

model_path = f"{model_dir}/{model_id}-lora-int8"

#trainer.save_model(model_path)
model.save_pretrained(model_path)

7. 使用 LoRA 模型

lora_model = trainer.model
text = "Two things are infinite: "
inputs = tokenizer(text, return_tensors="pt").to(0)

out = lora_model.generate(**inputs, max_new_tokens=48)
print(tokenizer.decode(out[0], skip_special_tokens=True))

输出结果
通过在 english_quotes 数据集上的少量微调(100 steps,不到1个epoch),LoRA 适配器恢复了阿尔伯特·爱因斯坦的名言警句。

LORA 低秩适配 OpenAI Whisper-Large-V2 语音识别任务

使用 LoRA 在OpenAI Whisper-large-v2模型上实现语音识别(ASR)任务的微调训练。此外,还可以结合int8量化进一步降低训练过程资源开销,同时保证了精度几乎不受影响。
原理图
图示: 实现原理图
完整的训练流程

  • 全局参数设置
  • 数据准备
    • 下载数据集:训练、验证和评估集
    • 预处理数据:降采样、移除不必要字段等
    • 数据抽样
    • 应用数据集处理(Dataset.map)
    • 自定义语音数据处理器
  • 模型准备
    • 加载和处理 int8 精度 Whisper-Large-v2 模型
    • LoRA Adapter 参数配置
    • 实例化 PEFT Model:peft_model = get_peft_model(model, config)
  • 模型训练
    • 训练参数配置Seq2SeqTrainingArguments
    • 实例化训练器Seq2SeqTrainer
    • 训练模型
    • 保存模型
  • 模型推理
    • 使用PeftModel加载LoRA微调后Whisper模型
    • 使用 Pipeline API 部署微调后Whisper实现中文语音识别任务

1. 全局参数设置

model_name_or_path = "openai/whisper-large-v2"
model_dir = "models/whisper-large-v2-asr-int8"

language = "Chinese (China)"
language_abbr = "zh-CN"
task = "transcribe"
dataset_name = "mozilla-foundation/common_voice_11_0"

batch_size=64

2. 数据准备

下载数据集 Common Voice,Common Voice 11.0 数据集包含许多不同语言的录音,总时长达数小时。
首先,初始化一个DatasetDict结构,并将训练集(将训练+验证拆分为训练集)和测试集拆分好,按照中文数据集构建配置加载到内存中:

from datasets import load_dataset, DatasetDict

common_voice = DatasetDict()

common_voice["train"] = load_dataset(dataset_name, language_abbr, split="train", trust_remote_code=True)
common_voice["validation"] = load_dataset(dataset_name, language_abbr, split="validation", trust_remote_code=True)

common_voice["train"][0]

输出结果
预处理训练数据集

from transformers import AutoFeatureExtractor, AutoTokenizer, AutoProcessor

# 从预训练模型加载特征提取器
feature_extractor = AutoFeatureExtractor.from_pretrained(model_name_or_path)

# 从预训练模型加载分词器,可以指定语言和任务以获得最适合特定需求的分词器配置
tokenizer = AutoTokenizer.from_pretrained(model_name_or_path, language=language, task=task)

# 从预训练模型加载处理器,处理器通常结合了特征提取器和分词器,为特定任务提供一站式的数据预处理
processor = AutoProcessor.from_pretrained(model_name_or_path, language=language, task=task)

# 移除数据集中不必要的字段
common_voice = common_voice.remove_columns(
    ["accent", "age", "client_id", "down_votes", "gender", "locale", "path", "segment", "up_votes"]
)

输出结果
降采样音频数据
对48kHz采样率进行采样的输入音频进行降采样以匹配模型预训练时使用的16kHZ采样率,因为Whisper模型是在16kHZ的音频输入上预训练。通过在音频列上使用cast_column方法,并将sampling_rate设置为16kHz来对音频进行降采样。下次调用时,音频输入将实时重新取样:

from datasets import Audio

common_voice = common_voice.cast_column("audio", Audio(sampling_rate=16000))

# sampling_rate 从 48KHZ 降为 16KHZ
common_voice["train"][0]

整合以上数据处理为一个函数
该数据预处理函数应该包括:

  • 通过加载音频列将音频输入重新采样为16kHZ。
  • 使用特征提取器从音频数组计算输入特征。
  • 将句子列标记化为输入标签。
def prepare_dataset(batch):
    audio = batch["audio"]
    batch["input_features"] = feature_extractor(audio["array"], sampling_rate=audio["sampling_rate"]).input_features[0]
    batch["labels"] = tokenizer(batch["sentence"]).input_ids
    return batch

数据抽样: 在 Whisper-Large-v2 上使用小规模数据进行演示训练,保持以下训练参数不变(batch_size=64)。使用 640 个样本训练,320个样本验证和评估,恰好使得1个 epoch 仅需10 steps 即可完成训练。

small_common_voice = DatasetDict()

small_common_voice["train"] = common_voice["train"].shuffle(seed=16).select(range(640))
small_common_voice["validation"] = common_voice["validation"].shuffle(seed=16).select(range(320))

如果全量训练,则使用完整数据代替抽样。

# 抽样数据处理
tokenized_common_voice = small_common_voice.map(prepare_dataset)

# 完整数据训练,尝试开启 `num_proc=8` 参数多进程并行处理(如阻塞无法运行,则不使用此参数)
tokenized_common_voice = common_voice.map(prepare_dataset, num_proc=8)

自定义语音数据整理器:定义了一个针对语音到文本(Seq2Seq)模型的自定义数据整理器类,特别适用于输入为语音特征、输出为文本序列的数据集。这个整理器(DataCollatorSpeechSeq2SeqWithPadding)旨在将数据点批量打包,将每个批次中的attention_mask填充到最大长度,以保持批处理中张量形状的一致性,并用-100替换填充值,以便在损失函数中被忽略。这对于神经网络的高效训练至关重要。

import torch

from dataclasses import dataclass
from typing import Any, Dict, List, Union

# 定义一个针对语音到文本任务的数据整理器类
@dataclass
class DataCollatorSpeechSeq2SeqWithPadding:
    processor: Any  # 处理器结合了特征提取器和分词器

    # 整理器函数,将特征列表处理成一个批次
    def __call__(self, features: List[Dict[str, Union[List[int], torch.Tensor]]]) -> Dict[str, torch.Tensor]:
        # 从特征列表中提取输入特征,并填充以使它们具有相同的形状
        input_features = [{"input_features": feature["input_features"]} for feature in features]
        batch = self.processor.feature_extractor.pad(input_features, return_tensors="pt")

        # 从特征列表中提取标签特征(文本令牌),并进行填充
        label_features = [{"input_ids": feature["labels"]} for feature in features]
        labels_batch = self.processor.tokenizer.pad(label_features, return_tensors="pt")

        # 使用-100替换标签中的填充区域,-100通常用于在损失计算中忽略填充令牌
        labels = labels_batch["input_ids"].masked_fill(labels_batch.attention_mask.ne(1), -100)

        # 如果批次中的所有序列都以句子开始令牌开头,则移除它
        if (labels[:, 0] == self.processor.tokenizer.bos_token_id).all().cpu().item():
            labels = labels[:, 1:]

        # 将处理过的标签添加到批次中
        batch["labels"] = labels

        return batch  # 返回最终的批次,准备好进行训练或评估

# 用给定的处理器实例化数据整理器
data_collator = DataCollatorSpeechSeq2SeqWithPadding(processor=processor)

3. 模型准备

加载预训练模型(int8 精度):使用 int8 精度加载预训练模型,进一步降低显存需求。

from transformers import AutoModelForSpeechSeq2Seq

model = AutoModelForSpeechSeq2Seq.from_pretrained(model_name_or_path, load_in_8bit=True, device_map="auto")

# 设置模型配置中的forced_decoder_ids属性为None
model.config.forced_decoder_ids = None  # 这通常用于指定在解码(生成文本)过程中必须使用的特定token的ID,设置为None表示没有这样的强制要求

# 设置模型配置中的suppress_tokens列表为空
model.config.suppress_tokens = []  # 这用于指定在生成过程中应被抑制(不生成)的token的列表,设置为空列表表示没有要抑制的token

**PEFT 微调前的模型处理:**在使用 peft 训练 int8 模型之前,需要进行一些预处理:

  • 将所有非 int8 精度模块转换为全精度(fp32)以保证稳定性
  • 为输入嵌入层添加一个 forward_hook,以启用输入隐藏状态的梯度计算
  • 启用梯度检查点以实现更高效的内存训练

使用 peft 库预定义的工具函数 prepare_model_for_int8_training,便可自动完成以上模型处理工作。

from peft import prepare_model_for_int8_training

model = prepare_model_for_int8_training(model)

LoRA Adapter 配置:在 peft 中使用LoRA非常简捷,借助 PeftModel抽象,可以快速使用低秩适配器(LoRA)到任意模型。通过使用 peft 中的 get_peft_model 工具函数来实现。

from peft import LoraConfig, PeftModel, LoraModel, LoraConfig, get_peft_model

# 创建一个LoraConfig对象,用于设置LoRA(Low-Rank Adaptation)的配置参数
config = LoraConfig(
    r=4,  # LoRA的秩,影响LoRA矩阵的大小
    lora_alpha=64,  # LoRA适应的比例因子
    # 指定将LoRA应用到的模型模块,通常是attention和全连接层的投影。
    target_modules=["q_proj", "v_proj"],
    lora_dropout=0.05,  # 在LoRA模块中使用的dropout率
    bias="none",  # 设置bias的使用方式,这里没有使用bias
)

# 使用get_peft_model函数和给定的配置来获取一个PEFT模型
peft_model = get_peft_model(model, config)

# 打印 LoRA 微调训练的模型参数
peft_model.print_trainable_parameters()

4. 模型训练

Seq2SeqTrainingArguments训练参数:关于设置训练步数和评估步数

基于 epochs 设置:
num_train_epochs=3, # 训练的总轮数
evaluation_strategy=“epoch”, # 设置评估策略,这里是在每个epoch结束时进行评估
warmup_steps=50, # 在训练初期增加学习率的步数,有助于稳定训练。
基于 steps 设置:
max_steps=100, # 训练总步数
evaluation_strategy=“steps”,
eval_steps=25, # 评估步数

from transformers import Seq2SeqTrainingArguments

# 设置序列到序列模型训练的参数
training_args = Seq2SeqTrainingArguments(
    output_dir=model_dir,  # 指定模型输出和保存的目录
    per_device_train_batch_size=batch_size,  # 每个设备上的训练批量大小
    learning_rate=1e-3,  # 学习率
    num_train_epochs=1,  # 训练的总轮数
    evaluation_strategy="epoch",  # 设置评估策略,这里是在每个epoch结束时进行评估
    # warmup_steps=50,  # 在训练初期增加学习率的步数,有助于稳定训练
    # fp16=True,  # 启用混合精度训练,可以提高训练速度,同时减少内存使用
    per_device_eval_batch_size=batch_size,  # 每个设备上的评估批量大小
    generation_max_length=128,  # 生成任务的最大长度
    logging_steps=10,  # 指定日志记录的步骤,用于跟踪训练进度
    remove_unused_columns=False,  # 是否删除不使用的列,以减少数据处理开销
    label_names=["labels"],  # 指定标签列的名称,用于训练过程中
    # evaluation_strategy="steps",
    # eval_steps=25,
)

实例化 Seq2SeqTrainer 训练器

from transformers import Seq2SeqTrainer

trainer = Seq2SeqTrainer(
    args=training_args,
    model=peft_model,
    train_dataset=tokenized_common_voice["train"],
    eval_dataset=tokenized_common_voice["validation"],
    data_collator=data_collator,
    tokenizer=processor.feature_extractor,
)
peft_model.config.use_cache = False

trainer.train()

5. 保存 LoRA 模型(Adapter)

trainer.save_model(model_dir)

训练后的模型如下:
训练后的模型

6. 模型推理

model_dir = "models/whisper-large-v2-asr-int8"

language = "Chinese (China)"
language_abbr = "zh-CN"
language_decode = "chinese"
task = "transcribe"

使用PeftModel加载 LoRA微调后 Whisper模型:使用PeftConfig加载LoRA Adapter配置参数,使用 PeftModel加载微调后Whisper模型。

from transformers import AutoModelForSpeechSeq2Seq, AutoTokenizer, AutoProcessor
from peft import PeftConfig, PeftModel

peft_config = PeftConfig.from_pretrained(model_dir)

base_model = AutoModelForSpeechSeq2Seq.from_pretrained(
    peft_config.base_model_name_or_path, load_in_8bit=True, device_map="auto"
)

peft_model = PeftModel.from_pretrained(base_model, model_dir)

tokenizer = AutoTokenizer.from_pretrained(peft_config.base_model_name_or_path, language=language, task=task)
processor = AutoProcessor.from_pretrained(peft_config.base_model_name_or_path, language=language, task=task)
feature_extractor = processor.feature_extractor

使用 Pipeline API 部署微调后 Whisper 实现中文语音识别任务

test_audio = "data/audio/test_zh.flac"
from transformers import AutomaticSpeechRecognitionPipeline

pipeline = AutomaticSpeechRecognitionPipeline(model=peft_model, tokenizer=tokenizer, feature_extractor=feature_extractor)

forced_decoder_ids = processor.get_decoder_prompt_ids(language=language_decode, task=task)

import torch

with torch.cuda.amp.autocast():
    text = pipeline(test_audio, max_new_tokens=255)["text"]

输出结果

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

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

相关文章

知识改变命运 数据结构【链表面试题】

1. 删除链表中等于给定值 val 的所有节点。 OJ链接 public ListNode removeElements(ListNode head, int val) {if (headnull) {return null;}ListNode curhead.next;ListNode prehead;while(cur!null) {if(cur.valval) {pre.nextcur.next;curcur.next;}else {precur;curcur.ne…

日拱一卒(5)——leetcode学习记录:股票购买

一、任务 给定一个数组 prices &#xff0c;它的第 i 个元素 prices[i] 表示一支给定股票第 i 天的价格。 你只能选择 某一天 买入这只股票&#xff0c;并选择在 未来的某一个不同的日子 卖出该股票。设计一个算法来计算你所能获取的最大利润。 返回你可以从这笔交易中获取的…

【qt】跳转到另一个界面

如何在一个界面跳转到另一个界面呢&#xff1f; 1.具体步骤 1.先新建一个界面 2.选择qt设计师界面 3.选择W 4.新界面名称 5.界面设计 因为我们要实现通信&#xff0c;需要一个发送信息栏&#xff0c;一个发送按钮&#xff0c;一个清空发送栏按钮 6.实现跳转 我们可以参…

链表反转算法

链表的反转有较多的方法&#xff0c;如原地算法&#xff0c;迭代法、头插法、递归法&#xff0c;本文使用递归法和迭代法两种方式进行演示。 一、定义链表 typedef struct SinglyLinkNode {/**后继节点 */struct SinglyLinkNode* next;/** 节点数据域 */int data; } Lin…

海外代理IP网速快慢取决因素有哪些

在全球化日益加深的今天&#xff0c;海外代理IP成为许多用户进行跨境网络活动的重要工具。然而&#xff0c;用户在使用海外代理IP时&#xff0c;往往会遇到网速快慢不一的问题。本文将从多个角度探讨影响海外代理IP网速快慢的因素&#xff0c;帮助用户更好地理解并选择适合自己…

使用 prefetchComponents 进行组件预取

title: 使用 prefetchComponents 进行组件预取 date: 2024/8/17 updated: 2024/8/17 author: cmdragon excerpt: 摘要&#xff1a;本文介绍Nuxt.js中的prefetchComponents功能&#xff0c;用于预取组件以提高用户体验。通过在客户端后台下载和缓存组件&#xff0c;确保在用户…

普通人如何做文献阅读汇报?

最强小说推文——AI视频生成&#xff1a;小说文案智能分镜智能识别角色和场景批量Ai绘图自动配音添加音乐一键合成视频百万播放量https://aitools.jurilu.com/ 用AI啊&#xff01;以前啃个文献小半天&#xff0c;现在AI 2min搞定&#xff0c;从提取核心论点、参考文献到摘要、翻…

如何将 Windows 11/10/8/7 克隆到另一台计算机

为什么需要将 Windows克隆到新计算机 “我有一台新笔记本电脑来替换我的旧电脑&#xff0c;因为它运行几年后变得越来越慢。我现在面临的问题是如何让 Windows 10、程序和文件与旧 PC 保持相同。我不想重新安装 Windows 和应用程序。有没有快速简便的方法可以做到这一点&#…

检测到目标URL存在http host头攻击漏洞

漏洞描述 修复措施 方法一&#xff1a; nginx 的 default_server 指令可以定义默认的 server 去处理一些没有匹配到 server_name 的请求&#xff0c;如果没有显式定义&#xff0c;则会选取第一个定义的 server 作为 default_server。 server { …

数据结构——关于队列

1.队列的概念及结构 队列&#xff1a;只允许在一端进行插入数据操作&#xff0c;在另一端进行删除数据操作的特殊线性表&#xff0c;队列具有先进先出的特性 入队列&#xff1a;进行插入操作的一端称为队尾 出队列&#xff1a;进行删除操作的一端称为队头 2.队列的…

HiveSQL实战——大厂面试真题

一、字节跳动 最高峰同时直播人数 https://blog.csdn.net/SHWAITME/article/details/135918264 0 问题描述 有如下数据记录直播平台主播上播及下播时间&#xff0c;根据该数据计算出平台最高峰同时直播人数。 ------------------------------------------------------ | us…

NC 用两个栈实现队列

系列文章目录 文章目录 系列文章目录前言 前言 前些天发现了一个巨牛的人工智能学习网站&#xff0c;通俗易懂&#xff0c;风趣幽默&#xff0c;忍不住分享一下给大家。点击跳转到网站&#xff0c;这篇文章男女通用&#xff0c;看懂了就去分享给你的码吧。 描述 用两个栈来实…

计算最简比

原理 c 质数数组长度&#xff0c;除不了就c--&#xff0c;都除不了c0&#xff0c;while(0)退出循环。 mainwindow.h struct WbiH {int width;int height; };WbiH calcWbiH(int w, int h); mainwindow.cpp {......int width player->metaData(SLMD.at(i)).toSize().wi…

JVM上篇:内存与垃圾-回收篇05-本地方法接口和本地方法栈

笔记来源&#xff1a;尚硅谷 JVM 全套教程&#xff0c;百万播放&#xff0c;全网巅峰&#xff08;宋红康详解 java 虚拟机&#xff09; 文章目录 5. 本地方法接口和本地方法栈5.1. 什么是本地方法&#xff1f;5.2. 为什么使用 Native Method&#xff1f;5.2. 本地方法栈 5. 本地…

uniapp去掉页面导航条

在pages.json文件中&#xff0c;globalStyle中添加 ”app-plus“:{"titleNView":false }

C++ 常用STL底层原理

STL 1. std::vector 底层原理&#xff1a;std::vector 是一个动态数组。它在内存中分配一块连续的存储空间来存储元素。随着元素数量的增加&#xff0c;如果存储空间不够用&#xff0c;vector 会分配一块更大的内存&#xff0c;并将现有元素复制到新内存块中。通常&#xff0c…

JavaScript模块化——JS模块化介绍与CommonJS规范

作者&#xff1a;CSDN-PleaSure乐事 欢迎大家阅读我的博客 希望大家喜欢 使用环境&#xff1a;vscode Chrome浏览器 目录 1.模块化概述 1.1什么是模块化 1.2为什么需要模块化 1.2.1全局污染 1.2.2依赖混乱 1.2.3数据安全 2.有哪些模块化规范 3.导入与导出 3.1导入 3.…

YOLOV5单目测距+车辆检测+车道线检测+行人检测(教程-代码)

YOLOv5是一种高效的目标检测算法&#xff0c;结合其在单目测距、车辆检测、车道线检测和行人检测等领域的应用&#xff0c;可以实现多个重要任务的精确识别和定位。 首先&#xff0c;YOLOv5可以用于单目测距。 通过分析图像中的目标位置和尺寸信息&#xff0c;结合相机参数和几…

React原理之Fiber详解

前置文章&#xff1a; React原理之 React 整体架构解读React原理之整体渲染流程 -----读懂这一篇需要对 React 整体架构和渲染流程有大致的概念 &#x1f60a;----- 在React原理之 React 整体架构解读中&#xff0c;简单介绍了 Fiber 架构&#xff0c;也了解了 Fiber 节点的…

一个云端应用市场和配套的移动办公APP,支持iOS和Android端,可私有部署,支持在线体验(附源码)

前言 在当前的企业应用市场中&#xff0c;许多移动办公软件不仅成-本高昂&#xff0c;而且在功能对接和接口完整性方面存在不足&#xff0c;导致开发成本居高不下。这促使企业和开发者寻求更加经济、高效且功能丰富的处理方案。 介绍 O2OA&#xff08;翱途&#xff09;开发平…