大语言模型LLM的微调代码详解
代码的摘要说明
一、整体功能概述
这段 Python 代码主要实现了基于 Hugging Face Transformers 库对预训练语言模型(具体为 TAIDE-LX-7B-Chat 模型)进行微调(Fine-tuning)的功能,使其能更好地应用于生成唐诗相关内容的任务。整个流程涵盖了数据加载与预处理、模型配置、模型训练以及训练后模型的测试与结果保存等环节。二、代码各部分详细说明
导入模块部分
导入了多个常用的 Python 库,如 os、sys、argparse 等基础库,以及和深度学习、自然语言处理相关的库,像 torch、transformers、peft 等,还导入了用于忽略警告信息、文本颜色控制等功能的相关模块,为后续代码实现做准备。
基础配置与参数设置部分
模型相关路径:定义了模型名称、模型所在目录、工作目录、输出目录以及检查点目录等路径相关的变量,确保后续文件操作(如加载模型、保存训练结果等)能找到正确的位置。
训练参数设置:设定了训练的总轮数(num_epoch)、学习率(LEARNING_RATE),还包括一系列影响模型训练和推理的超参数,例如用于训练的数据集数量(num_train_data)、文本截断长度(CUTOFF_LEN)、LORA 相关参数(LORA_R、LORA_ALPHA、LORA_DROPOUT)、验证集大小(VAL_SET_SIZE)、微批次大小(MICRO_BATCH_SIZE)、梯度累积步数(GRADIENT_ACCUMULATION_STEPS)等,这些参数将用于控制模型训练过程和微调效果。
分布式训练配置:根据环境变量 WORLD_SIZE 判断是否进行分布式训练,若需要则配置相应的设备映射(device_map)。
数据加载与预处理部分
创建目录:首先确保输出目录和检查点目录存在,若不存在则创建它们,用于后续保存训练结果和模型检查点。
加载数据集:从指定的 json 文件路径(dataset_dir)读取原始数据集,先将其加载为 Python 的字典数据结构(通过 json.load),然后从中截取指定数量(由 num_train_data 决定)的数据保存为临时数据集文件(tmp_dataset_path),再通过 load_dataset 函数以 json 格式重新加载这个临时数据集用于后续处理。
数据处理函数定义:定义了 generate_training_data 函数,用于将包含指令、输入和输出文本的数据点转换为适合模型训练的格式,具体是通过 tokenizer 将文本转换为令牌(token)形式,并处理好输入令牌、对应标签以及注意力掩码等信息,构建训练样本。
划分数据集(可选):根据 VAL_SET_SIZE 参数的值决定是否划分训练集和验证集,如果 VAL_SET_SIZE 大于 0,则从原始训练集中划分出验证集,对训练集和验证集的数据都应用 generate_training_data 函数进行预处理,使其格式符合模型训练要求;若 VAL_SET_SIZE 为 0,则整个数据集都作为训练集进行处理。
模型相关配置部分
创建模型和令牌器:从指定的模型路径(model_path)加载预训练的语言模型,并根据相关配置(如量化配置 BitsAndBytesConfig 用于以特定量化方式加载模型以节省内存等)进行初始化;同时加载对应的分词器(tokenizer),并设置其填充令牌(pad_token)与结束令牌(eos_token)相同,方便后续文本处理。
模型准备与微调配置:将加载的模型通过 prepare_model_for_int8_training 函数进行处理,使其适合 INT8 训练方式,接着使用 LoraConfig 配置 LORA(低秩适配,一种参数高效微调方法)相关参数,并通过 get_peft_model 函数应用 LORA 配置到模型上,以便后续进行参数高效的微调训练。
生成配置定义:定义了 GenerationConfig 对象,用于指定模型在生成文本时的一些解码参数,例如是否采样(do_sample)、温度参数(temperature)、束搜索的束数量(num_beams)等,这些参数会影响模型生成唐诗内容时的质量和多样性等特性。
模型训练部分
使用 transformers.Trainer 类来实例化训练器对象(trainer),传入模型、训练数据集、验证数据集(可为 None)以及训练相关的各种参数(如批次大小、学习率、训练轮数、梯度累积步数、日志记录和模型保存相关的配置等),同时指定了数据整理器(data_collator)用于整理训练数据的格式。在训练前还禁用了模型的缓存功能(model.config.use_cache = False),然后通过 try-except 语句块尝试执行模型训练过程,若训练出现异常则打印出错误信息。
模型保存部分
同样使用 try-except 语句块尝试将训练后的模型保存到指定的检查点目录(ckpt_dir)中,若保存过程出现异常则打印相应的错误提示。
模型测试与结果保存部分
从指定的测试数据文件(test_data_path)中加载测试数据集(通过 json.load),然后循环遍历每条测试数据,调用 evaluate 函数(该函数基于给定的指令、生成配置和输入等信息,利用已训练好的模型生成相应回复内容)获取模型针对测试数据的回复,将测试数据的输入以及模型生成的回复拼接后写入到指定的结果文件(output_path)中保存起来,同时也会打印出来方便查看,用于评估模型在测试集上的表现。
原文链接:https://blog.csdn.net/chenchihwen/article/details/144000079
整体代码参照这篇文章
《生成式 AI》课程 作业6 大语言模型(LLM)的训练微调 Fine Tuning -- part2-CSDN博客文章浏览阅读872次,点赞26次,收藏8次。代码围绕一个主工作目录展开,在这个主工作目录下包含了多个子目录和相关文件,用于存放不同阶段的数据、模型以及输出结果等内容,各个部分分工明确,以支持整个预训练语言模型微调及测试的流程。这段 Python 代码主要实现了基于 Hugging Face Transformers 库对预训练语言模型(具体为模型)进行微调(Fine-tuning)的功能,使其能更好地应用于生成唐诗相关内容的任务。整个流程涵盖了数据加载与预处理、模型配置、模型训练以及训练后模型的测试与结果保存等环节。https://blog.csdn.net/chenchihwen/article/details/144000079?spm=1001.2014.3001.5501拆解说明如下
导入必要的库
import os
import sys
import argparse
import json
import warnings
import logging
- 导入了多个 Python 标准库,
os
用于操作系统相关的操作(如文件路径处理、目录创建等),sys
用于处理 Python 运行时环境相关的配置和参数,argparse
常用于命令行参数解析(但此代码中未体现其使用),json
用于处理 JSON 格式的数据读写,warnings
用于控制警告信息的显示,logging
用于记录日志信息。
# 忽略警告信息
warnings.filterwarnings("ignore")
- 配置
warnings
模块,使其忽略所有警告信息,避免在程序运行过程中输出大量警告干扰正常的输出和分析。
import torch
import torch.nn as nn
import bitsandbytes as bnb
from datasets import load_dataset, load_from_disk
import transformers
from peft import PeftModel
from colorama import Fore, Back, Style
- 导入了
torch
(深度学习常用的张量计算库)及其nn
模块(用于构建神经网络),bitsandbytes
(可能用于模型量化等相关功能),load_dataset
和load_from_disk
函数用于加载数据集,transformers
库(用于自然语言处理相关的预训练模型、工具等),PeftModel
(可能是某种特定的模型扩展相关类),colorama
(用于在终端输出添加颜色等样式,不过此处代码后续未看到其使用)。
# 以下是根据Hugging Face Transformers库的相关功能进行的设置
from transformers import AutoTokenizer, AutoConfig, AutoModelForCausalLM, BitsAndBytesConfig
from transformers import GenerationConfig
from peft import (prepare_model_for_int8_training,LoraConfig,get_peft_model,get_peft_model_state_dict,prepare_model_for_kbit_training
)
- 从
transformers
库中导入了多个用于创建模型、配置模型以及处理特定训练相关设置的类和函数,比如AutoTokenizer
用于自动加载合适的文本令牌化器,AutoConfig
用于自动加载模型配置,AutoModelForCausalLM
用于加载适用于因果语言建模的预训练模型,BitsAndBytesConfig
用于配置模型量化相关的参数;从peft
模块导入的函数主要用于对模型进行特定的微调(如int8
训练准备、配置LORA
相关参数、获取基于LORA
配置后的模型等)。
模型和路径相关的参数设置
# 模型名称
model_name = "TAIDE-LX-7B-Chat"
# 模型所在目录
model_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), "TAIDE-LX-7B-Chat")
- 定义了模型的名称以及模型所在的本地文件系统路径,通过
os.path.join
结合当前脚本文件所在目录(os.path.dirname(os.path.abspath(__file__))
获取)来确定完整的模型路径。
# 设置工作目录
work_dir = os.path.dirname(os.path.abspath(__file__))
# 输出目录
output_dir = os.path.join(work_dir, "output")
# 检查点目录
ckpt_dir = os.path.join(work_dir, "checkpoint")
- 确定了工作目录(即当前脚本所在目录),并基于此设置了后续用于存储输出结果的目录和保存模型检查点的目录路径。
# 训练的总Epoch数
num_epoch = 1
# 学习率
LEARNING_RATE = 3e-4
- 定义了模型训练的关键超参数,总训练轮数为
1
轮,学习率设置为3e-4
,学习率决定了模型在训练过程中参数更新的步长大小。
# 配置模型和训练参数
cache_dir = os.path.join(work_dir, "cache")
from_ckpt = False
ckpt_name = None
- 设置了模型缓存目录路径,以及两个与是否从检查点加载模型、具体检查点名称相关的变量,这里表示不从检查点加载(
from_ckpt
为False
)且没有指定具体检查点名称(ckpt_name
为None
)。
# 以下是补充的超参数设置
# 用于训练的数据集数量
num_train_data = 1040 # 可设置的最大值为5000,数据量越多,模型性能可能越好,但训练时间也会增加
# 以下参数影响模型的训练和推理
CUTOFF_LEN = 256 # 文本截断的最大长度
LORA_R = 8 # LORA的R值
LORA_ALPHA = 16 # LORA的Alpha值
LORA_DROPOUT = 0.05 # LORA的Dropout率
VAL_SET_SIZE = 0 # 验证集的大小,0表示不使用验证集
TARGET_MODULES = ["q_proj", "up_proj", "o_proj", "k_proj", "down_proj", "gate_proj", "v_proj"] # 目标模块,用于LORA训练
#
MICRO_BATCH_SIZE = 4 # 微批次大小,这里设置为和原示例类似的值,你可根据实际情况调整
GRADIENT_ACCUMULATION_STEPS = 4 # 计算每个微批次累积的梯度步数,示例中为16//4,这里假设为4,可按需改
logging_steps = 20 # 定義訓練過程中每隔多少步驟輸出一次訓練誌
save_steps = 65 # 定義訓練過程中每隔多少步驟保存一次模型
save_total_limit = 3 # 控制最多保留幾個模型checkpoint
report_to = ["tensorboard"] # 設定上報實驗指標的目標,預設為無 # 可以根据需求调整报告的对象,比如添加其他日志记录工具等
- 这些都是详细的模型训练相关超参数设置:
num_train_data
指定了用于训练模型的数据集样本数量,这里限制为1040
个,更多数据理论上有助于提升模型性能但会增加训练时长。CUTOFF_LEN
用于限制输入文本的最大长度,超过这个长度会截断,有助于控制输入规模。LORA_R
、LORA_ALPHA
、LORA_DROPOUT
是LORA
(低秩适应)方法相关的参数,用于调整模型微调时的结构和特性。VAL_SET_SIZE
决定是否划分出验证集以及验证集的大小,这里设置为0
表示不划分验证集用于评估训练过程中的模型性能。TARGET_MODULES
明确了在LORA
训练时针对模型的哪些模块进行操作。MICRO_BATCH_SIZE
和GRADIENT_ACCUMULATION_STEPS
一起用于控制每次训练时实际处理的数据量和梯度更新的频率。logging_steps
规定了每隔多少训练步骤记录一次训练日志,save_steps
定义了每隔多少步骤保存一次模型检查点,save_total_limit
控制最多保留的检查点数量,report_to
指定了向哪些目标(如tensorboard
)上报实验指标用于可视化等分析。
分布式训练相关配置
world_size = int(os.environ.get("WORLD_SIZE", 1))
ddp = world_size!= 1
if ddp:device_map = {"": int(os.environ.get("LOCAL_RANK") or 0)}
- 首先获取环境变量
WORLD_SIZE
(通常用于分布式训练,表示总的进程数),如果获取不到则默认值为1
。根据world_size
是否等于1
来判断是否是分布式训练(ddp
变量标记),如果是分布式训练(ddp
为True
),则通过环境变量LOCAL_RANK
(通常表示当前进程在本地的序号)来配置设备映射(这里简单地将其映射到一个整数,用于指定设备,比如 GPU 设备编号等)。
创建目录
# 创建输出目录和检查点目录(如果不存在)
os.makedirs(output_dir, exist_ok=True)
os.makedirs(ckpt_dir, exist_ok=True)
- 使用
os.makedirs
函数创建输出目录和检查点目录,如果目录已经存在(exist_ok=True
参数表示存在时不报错)则不会重复创建,确保后续保存输出结果和模型检查点时有对应的目录可用。
数据集加载与处理
# 加载数据集
dataset_dir = os.path.join(work_dir, "data", "training_data", "Tang_training_data.json")
with open(dataset_dir, "r", encoding="utf-8") as f:data_json = json.load(f)
- 构建数据集文件的完整路径(假设数据集是 JSON 格式存储在特定目录下),然后使用
json.load
函数从打开的文件中读取 JSON 数据并解析为 Python 对象(这里是字典或列表等结构)存储在data_json
变量中。
# 保存处理后的数据集
tmp_dataset_path = os.path.join(work_dir, "tmp_dataset.json")
# 获取文件所在目录路径
dir_path = os.path.dirname(tmp_dataset_path)
# 如果目录不存在,则创建目录
if not os.path.exists(dir_path):os.makedirs(dir_path)# 明确使用 'w' 模式打开文件用于写入,同时指定 encoding 为 'utf-8'
with open(tmp_dataset_path, 'w', encoding='utf-8') as f:json.dump(data_json[:num_train_data], f, indent=2, ensure_ascii=False)
- 先确定临时数据集文件的路径以及其所在目录路径,若目录不存在就创建它。然后从原始读取的数据集数据(
data_json
)中选取前面num_train_data
个样本,使用json.dump
将这部分数据以缩进为2
、不强制转换 ASCII 编码(ensure_ascii=False
,便于处理中文等非 ASCII 字符)的方式写入到临时数据集文件中。
data = load_dataset('json', data_files=tmp_dataset_path, download_mode="force_redownload")
- 使用
load_dataset
函数以 JSON 格式加载刚刚处理并保存的临时数据集文件(data_files
参数指定),并设置下载模式为强制重新下载(不过对于本地文件这里可能没实际的下载行为,只是按照对应的加载逻辑处理),将加载好的数据集对象存储在data
变量中。
创建模型和令牌器
model = AutoModelForCausalLM.from_pretrained(model_path,cache_dir=cache_dir,quantization_config=BitsAndBytesConfig(load_in_4bit=True,bnb_4bit_quant_type="nf4",bnb_4bit_use_double_quant=True,bnb_4bit_compute_dtype=torch.bfloat16),low_cpu_mem_usage=True
)
- 通过
AutoModelForCausalLM
类从指定的model_path
路径加载预训练的因果语言模型,使用cache_dir
指定缓存目录,同时配置了模型量化相关参数(通过BitsAndBytesConfig
),这里设置为以4
位量化加载(load_in_4bit=True
),采用nf4
量化类型、启用双重量化以及指定计算的数据类型为torch.bfloat16
,并且设置为低 CPU 内存使用模式,以优化模型加载和运行时的内存占用情况。
tokenizer = AutoTokenizer.from_pretrained(model_name,add_eos_token=True,cache_dir=cache_dir,quantization_config=BitsAndBytesConfig(load_in_4bit=True,bnb_4bit_quant_type="nf4",bnb_4bit_use_double_quant=True,bnb_4bit_compute_dtype=torch.bfloat16)
)
tokenizer.pad_token = tokenizer.eos_token
- 类似地,使用
AutoTokenizer
从指定的model_name
加载对应的文本令牌化器,添加结束符令牌(add_eos_token=True
),配置缓存目录和量化相关参数(与模型配置的量化参数一致)。最后将令牌化器的填充令牌(pad_token
)设置为结束符令牌(eos_token
),方便后续处理文本序列长度不一致等情况。
生成训练数据的函数
def generate_training_data(data_point):"""此函数用于将数据点(包含指令、输入和输出文本)转换为模型可读取的令牌形式参数:data_point (dict): 包含 "instruction"、"input" 和 "output" 字段的字典,所有字段均为字符串返回:dict: 包含模型的输入令牌、注意力掩码和相应输出目标的字典"""# 构建完整的输入提示prompt = f"""\
[INST] <<SYS>>
You are a helpful assistant and good at writing Tang poem. 你是一个乐于助人的助手且擅长写唐诗。
<</SYS>>
{data_point["instruction"]}
{data_point["input"]}
[/INST]"""# 计算输入令牌的数量len_user_prompt_tokens = (len(tokenizer(prompt,truncation=True,max_length=CUTOFF_LEN + 1,padding="max_length",)["input_ids"]) - 1)# 将输入提示转换为令牌full_tokens = tokenizer(prompt + " " + data_point["output"] + "</s>",truncation=True,max_length=CUTOFF_LEN + 1,padding="max_length",)["input_ids"][:-1]return {"input_ids": full_tokens,"labels": [-100] * len_user_prompt_tokens + full_tokens[len_user_prompt_tokens:],"attention_mask": [1] * (len(full_tokens)),}
- 这个函数用于将输入的包含指令、输入文本和输出文本的数据点转换为适合模型训练的格式:
- 首先构建完整的输入提示文本(按照特定的格式拼接指令、输入等内容),形成
prompt
字符串。 - 然后通过令牌化器对
prompt
进行处理(截断到CUTOFF_LEN + 1
长度并进行填充等操作),计算出用户提示部分的令牌数量(减去1
可能是去除某个特定的标记等原因)。 - 接着将包含输出文本的完整提示再进行令牌化处理,并去掉最后一个令牌(具体原因可能与模型训练时的输入输出格式要求有关),得到
full_tokens
。 - 最后构建并返回一个字典,包含模型输入的令牌序列(
input_ids
),对应的标签序列(labels
,用户提示部分设为-100
表示不计算这部分的损失,后面接上实际输出对应的令牌)以及注意力掩码(全1
表示都参与注意力计算,长度与令牌序列长度一致)。
- 首先构建完整的输入提示文本(按照特定的格式拼接指令、输入等内容),形成
评估模型的函数
def evaluate(instruction, generation_config, max_len, input="", verbose=True):"""此函数用于根据输入的指令、生成配置和最大长度,获取模型的输出参数:instruction (str): 描述模型要执行的操作的字符串generation_config (transformers.GenerationConfig): 用于指定与模型推理相关的解码参数的对象max_len (int): 模型输出的最大长度input (str, 可选): 模型需要解决指令的输入字符串,默认值为 ""verbose (bool, 可选): 是否打印模型的输出,默认值为True返回:str: 模型根据指令和输入生成的响应"""# 构建完整的输入提示prompt = f"""\
[INST] <<SYS>>
You are a helpful assistant and good at writing Tang poem. 你是一个乐于助人的助手且擅长写唐诗。
<</SYS>>
{instruction}
{input}
[/INST]"""# 将提示文本转换为模型所需的数字表示形式inputs = tokenizer(prompt, return_tensors="pt")input_ids = inputs["input_ids"].cuda() if torch.cuda.is_available() else inputs["input_ids"]# 使用模型进行生成回复generation_output = model.generate(input_ids=input_ids,generation_config=generation_config,return_dict_in_generate=True,output_scores=True,max_new_tokens=max_len,)# 将生成的回复解码并打印for s in generation_output.sequences:output = tokenizer.decode(s)output = output.split("[/INST]")[1].replace("</s>", "").replace("<s>", "").replace("Assistant:", "").replace("Assistant", "").strip()if verbose:print(output)return output
- 该函数用于对模型进行评估,根据给定的指令、生成配置以及最大输出长度等参数获取模型的输出:
- 先按照特定格式构建完整的输入提示文本
prompt
,包含系统描述、指令和可选的输入内容。 - 接着使用令牌化器将提示文本转换为张量形式(
return_tensors="pt"
表示返回 PyTorch 张量),如果 GPU 可用则将输入的令牌input_ids
移到 GPU 上,方便后续加速计算。
- 先按照特定格式构建完整的输入提示文本
evaluate
函数(续)
generation_config=generation_config,return_dict_in_generate=True,output_scores=True,max_new_tokens=max_len,
)
- 调用模型的
generate
方法来生成回复内容,传递了几个重要参数:generation_config
:这是一个之前定义好的配置对象(类型为transformers.GenerationConfig
),里面包含了如采样策略(do_sample
等参数)、温度参数(temperature
)、束搜索相关参数(num_beams
等)等诸多与生成文本过程相关的设置,用于控制模型生成回复的方式。return_dict_in_generate=True
:表示让generate
方法以字典形式返回生成的结果,方便后续提取和处理相关信息,比如包含生成的序列、对应的分数等信息。output_scores=True
:指示模型在生成过程中同时输出每个生成步骤的分数信息(例如概率得分等,虽然代码后续没有明显体现对这些分数的进一步处理,但在一些更详细的分析场景中可能会用到)。max_new_tokens=max_len
:指定了模型最多生成的新令牌数量,也就是限制了生成回复的最大长度,避免生成过长的无意义文本。
for s in generation_output.sequences:output = tokenizer.decode(s)output = output.split("[/INST]")[1].replace("</s>", "").replace("<s>", "").replace("Assistant:", "").replace("Assistant", "").strip()if verbose:print(output)
- 这段代码对生成的结果进行处理和输出(如果
verbose
为True
):- 首先通过循环遍历
generation_output.sequences
(生成结果中的序列,可能存在多个候选序列等情况,不过这里代码逻辑上似乎只处理了其中一个,没有更复杂的选择逻辑体现)。 - 使用
tokenizer.decode
将生成的令牌序列s
解码为文本形式,得到原始的回复文本。 - 接着对解码后的文本进行一系列字符串处理操作:
split("[/INST]")[1]
:按照[/INST]
这个特定标记进行分割,并取分割后的第二部分内容,可能是为了去除前面的一些指令相关的固定格式内容,提取出真正的回复部分。- 然后通过
replace
方法依次去除一些多余的标记如</s>
(结束符标记)、<s>
(起始符标记)、Assistant:
(可能是回复开头的特定前缀标记)以及Assistant
(类似的前缀情况),并通过strip
方法去除文本两端的空白字符,最终得到比较干净整洁的回复文本内容。
- 如果
verbose
为True
,就将处理后的回复文本打印输出,方便查看模型生成的结果情况;如果verbose
为False
,则只是进行上述处理,不进行打印,用于后续可能的静默式评估或者将结果进一步传递使用等场景。 - 最后,函数返回处理后的回复文本(无论是否打印),使得调用该函数的地方可以获取到模型生成的结果用于后续的操作,比如和真实结果对比评估模型性能等。
- 首先通过循环遍历
划分训练集和验证集(如果需要)
if VAL_SET_SIZE > 0:train_val = data["train"].train_test_split(test_size=VAL_SET_SIZE, shuffle=True, seed=42)train_data = train_val["train"].shuffle().map(generate_training_data)val_data = train_val["test"].shuffle().map(generate_training_data)
else:train_data = data['train'].shuffle().map(generate_training_data)val_data = None
- 这部分代码根据之前定义的验证集大小(
VAL_SET_SIZE
)来决定是否划分训练集和验证集:- 如果
VAL_SET_SIZE
大于0
,表示需要划分出验证集用于在训练过程中评估模型在未见过的数据上的性能。通过调用数据集对象data["train"]
(假设data
数据集有train
这个子集表示训练数据部分)的train_test_split
方法,按照给定的测试集大小比例(test_size=VAL_SET_SIZE
)、进行随机打乱(shuffle=True
)并设置随机种子(seed=42
,保证每次划分结果可复现)来划分出训练集和验证集两部分,分别存储在train_val
这个包含train
和test
两个子集的对象中。 - 然后对划分后的训练集和验证集进一步处理,通过调用
shuffle
方法再次打乱顺序(进一步打乱确保数据随机性,可能是为了后续训练更稳定等原因),并使用map
方法结合generate_training_data
函数将数据集中的每个样本转换为适合模型训练的格式(如前面generate_training_data
函数中所做的将文本数据转换为包含输入令牌、标签、注意力掩码等的字典形式),最终得到处理后的训练数据train_data
和验证数据val_data
。 - 如果
VAL_SET_SIZE
等于0
,则表示不需要划分验证集,直接将整个训练数据集(data['train']
)进行打乱(shuffle
)并通过map
方法应用generate_training_data
函数处理每个样本,得到训练数据train_data
,同时将验证数据val_data
设为None
,表明没有单独的验证集用于后续评估。
- 如果
将模型准备好以使用 INT8 训练
model = prepare_model_for_int8_training(model)
- 调用
prepare_model_for_int8_training
函数对已加载的模型进行预处理,使其能够适应以INT8
量化精度进行训练的要求,这个函数内部可能会对模型的一些层结构、参数初始化等方面进行调整,以便在后续训练过程中能够基于INT8
量化方式正确地更新参数、计算梯度等,确保训练的顺利进行和模型性能的合理优化。
使用 LoraConfig 配置 LORA 模型
config = LoraConfig(r=LORA_R,lora_alpha=LORA_ALPHA,target_modules=TARGET_MODULES,lora_dropout=LORA_DROPOUT,bias="none",task_type="CAUSAL_LM",
)
model = get_peft_model(model, config)
- 首先创建一个
LoraConfig
配置对象,传入之前定义好的相关LORA
超参数:r=LORA_R
:设置LORA
方法中的秩(rank)参数,它决定了低秩矩阵分解的维度等特性,影响模型微调的效果和参数量等。lora_alpha=LORA_ALPHA
:与LORA
的自适应调整等相关的参数,配合r
等参数一起控制模型训练过程中的权重更新等情况。target_modules=TARGET_MODULES
:指定了在模型中哪些模块上应用LORA
技术进行微调,即明确了操作的具体对象范围。lora_dropout=LORA_DROPOUT
:设置LORA
中的丢弃率(Dropout)参数,用于防止过拟合等情况。bias="none"
:表示不处理偏置项(可能是不针对偏置进行LORA
相关的调整等情况)。task_type="CAUSAL_LM"
:表明任务类型是因果语言建模,用于让LORA
配置知晓模型的任务特性以便进行针对性的适配。
- 接着调用
get_peft_model
函数,传入原始模型和LoraConfig
配置对象,对模型进行基于LORA
配置的转换和调整,返回一个应用了LORA
技术后的新模型,替换原来的model
变量,使得后续训练能够基于LORA
微调的方式进行,在尽量少增加参数量的情况下提升模型在特定任务上的性能。
定义 nf4_config
nf4_config = BitsAndBytesConfig(load_in_4bit=True,bnb_4bit_quant_type="nf4",bnb_4bit_use_double_quant=True,bnb_4bit_compute_dtype=torch.bfloat16
)
- 创建一个
BitsAndBytesConfig
配置对象,重新配置了模型量化相关的参数(和之前模型加载时配置的量化参数类似,但这里单独定义了一个配置对象,可能后续有其他使用场景或者方便修改统一的量化设置等):load_in_4bit=True
:明确表示以4
位量化的方式加载模型,减少模型的内存占用等。bnb_4bit_quant_type="nf4"
:指定量化类型为nf4
,这是一种特定的量化技术选择。bnb_4bit_use_double_quant=True
:启用双重量化机制,进一步优化量化效果和模型存储、计算效率等。bnb_4bit_compute_dtype=torch.bfloat16
:设定在量化计算过程中使用的数据类型为torch.bfloat16
,用于在量化情况下兼顾计算精度和速度等方面的平衡。
定义生成配置
generation_config = GenerationConfig(do_sample=True,temperature=0.1,num_beams=1,top_p=0.3,no_repeat_ngram_size=3,pad_token_id=2
)
- 创建一个
GenerationConfig
对象用于配置模型生成文本时的各种参数:do_sample=True
:表示采用采样的方式生成文本,而不是确定性的生成(例如对比于基于束搜索等确定性的生成策略),这样可以增加生成文本的多样性,但也可能带来一定的不确定性和结果的波动性。temperature=0.1
:温度参数,用于控制采样的随机性程度,较小的值(如这里的0.1
)会让采样更偏向于概率高的词汇,生成的文本相对更保守、更符合常规;较大的值则会增加随机性,使生成的文本更加多样化但可能质量参差不齐。num_beams=1
:束搜索相关参数,这里设置为1
表示不使用束搜索(束搜索通常会保留多个候选路径并综合评估选择最优路径,当num_beams
大于1
时启用,这里仅设为1
相当于就是普通的逐个生成词汇的方式)。top_p=0.3
:一种概率截断采样的参数,叫核采样(nucleus sampling),表示只从概率累计和达到0.3
的那部分词汇中进行采样,有助于避免生成一些概率极低但可能出现的不合理词汇,同时保证一定的词汇选择范围和文本多样性。no_repeat_ngram_size=3
:设置生成文本时避免重复出现的n
元语法(连续的n
个词汇组成的单元)的大小为3
,即尽量不让连续的3
个词汇重复出现,以提高生成文本的流畅性和多样性,防止重复啰嗦的表述。pad_token_id=2
:指定填充令牌对应的id
为2
,用于在处理批次数据等场景下对长度不一致的文本序列进行填充对齐操作时的标识。
训练模型
trainer = transformers.Trainer(model=model,train_dataset=train_data,eval_dataset=val_data,args=transformers.TrainingArguments(per_device_train_batch_size=4, # 微批次大小,可根据需要调整gradient_accumulation_steps=16 // 4, # 计算每个微批次累积的梯度步数warmup_steps=50,num_train_epochs=num_epoch,learning_rate=LEARNING_RATE,fp16=True, # 使用混合精度训练logging_steps=20,save_strategy="steps",save_steps=65,output_dir=ckpt_dir,save_total_limit=3,ddp_find_unused_parameters=False if ddp else None,),data_collator=transformers.DataCollatorForLanguageModeling(tokenizer, mlm=False),
)
- 创建一个
transformers.Trainer
对象用于训练模型,传入了多个关键参数:model=model
:指定要训练的模型,即前面经过INT8
训练准备、LORA
配置后的模型。train_dataset=train_data
:传入训练数据集,是经过前面处理(如转换为合适格式、划分等操作)后的用于训练的样本数据集合。eval_dataset=val_data
:传入验证数据集(如果有,即val_data
不为None
时),用于在训练过程中定期评估模型性能,若val_data
为None
则表示训练过程中不进行基于验证集的评估。args=transformers.TrainingArguments(...)
:创建一个TrainingArguments
对象来配置训练相关的各种超参数:per_device_train_batch_size=4
:设置每个设备(如每个 GPU)上每次训练的微批次大小为4
,即每次处理4
个样本数据,这个大小可以根据硬件资源、模型复杂度等情况进行调整,影响训练时的内存占用和梯度更新频率等。gradient_accumulation_steps=16 // 4
:计算每个微批次累积的梯度步数,这里实际设置为4
(通过16 // 4
计算得出),意味着每处理4
个微批次后才进行一次梯度更新,用于在内存有限等情况下模拟更大的批次大小训练效果,同时控制梯度更新的节奏。warmup_steps=50
:在训练开始阶段设置的热身步数,在这个阶段学习率会从初始值逐渐上升到设定的正常学习率,有助于模型在训练初期更稳定地收敛,避免一开始就使用较大学习率导致训练不稳定等情况。num_train_epochs=num_epoch
:指定训练的总轮数,这里使用之前定义的num_epoch
变量,即训练1
轮(根据前面的定义)。learning_rate=LEARNING_RATE
:使用之前定义好的学习率(3e-4
)来控制模型参数更新的步长大小。fp16=True
:启用混合精度训练,即使用半精度(float16
)的数据类型来存储和计算部分张量,减少内存占用同时在支持的硬件上可以加速计算,又能在一定程度上保证计算精度不至于损失太多,适合深度学习模型训练场景。logging_steps=20
:按照每训练20
步记录一次训练日志,方便查看训练过程中的各项指标变化情况,如损失值、学习率等。save_strategy="steps"
:表示按照训练步数来决定何时保存模型检查点,与之对应的还有按训练轮数等保存策略,这里选择基于步数保存。save_steps=65
:结合save_strategy="steps"
,意味着每隔65
个训练步骤就保存一次模型检查点,用于后续可以恢复训练或者选择不同阶段的模型进行评估等。output_dir=ckpt_dir
:指定保存模型检查点的目录为之前定义的ckpt_dir
,确保模型文件保存到正确的位置。save_total_limit=3
:控制最多保留3
个模型检查点,避免过多的检查点占用大量磁盘空间,当保存的检查点数量超过这个限制时,可能会自动删除较早的检查点。ddp_find_unused_parameters=False if ddp else None
:在分布式训练(ddp
为True
)的场景下,设置是否查找未使用的参数,这里设置为False
表示不进行查找(具体是否查找可能根据实际训练需求和模型特点等决定,不查找可以节省一定的计算资源和时间),如果不是分布式训练(ddp
为False
)则设为None
,可能按照默认行为处理。
data_collator=transformers.DataCollatorForLanguageModeling(tokenizer, mlm=False)
:指定数据整理器(Data Collator),用于将训练数据整理成合适的批次格式,这里使用的是适用于语言建模任务的整理器,传入了令牌化器tokenizer
,并且设置mlm=False
表示不是做掩码语言建模(而是因果语言建模任务对应的整理方式),它会对批次内的文本序列进行填充、截断等操作,使其符合模型输入的格式要求。
禁用模型的 cache 功能
model.config.use_cache = False
- 将模型配置中的
use_cache
属性设置为False
,禁用模型的缓存功能。在某些情况下,禁用缓存可以避免一些潜在的内存问题或者不正确的结果,尤其是在训练过程中或者模型结构、输入等有动态变化的场景下,确保每次计算都是基于最新的输入和模型状态进行,而不会依赖之前缓存的中间结果。
训练过程
try:trainer.train()
except Exception as e:print(f"Training failed: {e}")
- 使用
try-except
语句块来执行模型训练过程,调用trainer.train()
方法启动训练,让模型根据前面配置的训练数据集、超参数等进行训练。如果在训练过程中出现任何异常(比如梯度爆炸、内存不足、运行时错误等),就会捕获该异常并打印出错误信息(Training failed: {e}
),方便排查问题,了解训练失败的原因。
保存训练后的模型
try:model.save_pretrained(ckpt_dir)
except Exception as e:print(f"Saving model failed: {e}
整体代码与工作目录,必要的 package
参照这篇文章
《生成式 AI》课程 作业6 大语言模型(LLM)的训练微调 Fine Tuning -- part2-CSDN博客文章浏览阅读872次,点赞26次,收藏8次。代码围绕一个主工作目录展开,在这个主工作目录下包含了多个子目录和相关文件,用于存放不同阶段的数据、模型以及输出结果等内容,各个部分分工明确,以支持整个预训练语言模型微调及测试的流程。这段 Python 代码主要实现了基于 Hugging Face Transformers 库对预训练语言模型(具体为模型)进行微调(Fine-tuning)的功能,使其能更好地应用于生成唐诗相关内容的任务。整个流程涵盖了数据加载与预处理、模型配置、模型训练以及训练后模型的测试与结果保存等环节。https://blog.csdn.net/chenchihwen/article/details/144000079?spm=1001.2014.3001.5501
训练json数据摘要如下:
Training data:sample, 你可以试着补充
Tang Poem datasethttps://github.com/chinese-poetry/chinese-poetry/tree/master/%E5%85%A8%E5%94%90%E8%AF%97?fbclid=IwAR2bM14S42T-VtrvMi3wywCqKfYJraBtMl7QVTo0qyPMjX9jj9Vj3JepFBA
Tang_training_data.json
[
{
"instruction": "以下是一首唐詩的第一句話,請用你的知識判斷並完成整首詩。",
"input": "秦川雄帝宅,函谷壯皇居。",
"output": "綺殿千尋起,離宮百雉餘。連甍遙接漢,飛觀迥凌虛。雲日隱層闕,風煙出綺疎。"
},
{
"instruction": "以下是一首唐詩的第一句話,請用你的知識判斷並完成整首詩。",
"input": "巖廊罷機務,崇文聊駐輦。",
"output": "玉匣啓龍圖,金繩披鳳篆。韋編斷仍續,縹帙舒還卷。對此乃淹留,欹案觀墳典。"
},
{
"instruction": "以下是一首唐詩的第一句話,請用你的知識判斷並完成整首詩。",
"input": "移步出詞林,停輿欣武宴。",
"output": "琱弓寫明月,駿馬疑流電。驚雁落虛弦,啼猿悲急箭。閱賞誠多美,於茲乃忘倦。"
},
{
"instruction": "以下是一首唐詩的第一句話,請用你的知識判斷並完成整首詩。",
"input": "鳴笳臨樂館,眺聽歡芳節。",
"output": "急管韻朱絃,清歌凝白雪。彩鳳肅來儀,玄鶴紛成列。去茲鄭衛聲,雅音方可悅。"
},
{
"instruction": "以下是一首唐詩的第一句話,請用你的知識判斷並完成整首詩。",
"input": "芳辰追逸趣,禁苑信多奇。",
"output": "橋形通漢上,峰勢接雲危。煙霞交隱映,花鳥自參差。何如肆轍跡?萬里賞瑤池。"
},
{
"instruction": "以下是一首唐詩的第一句話,請用你的知識判斷並完成整首詩。",
"input": "白日依山盡,黃河入海流。",
"output": "欲窮千里目,更上一層樓。"
},
{
"instruction": "以下是一首唐詩的第一句話,請用你的知識判斷並完成整首詩。",
"input": "落日雙闕昏,回輿九重暮。",
"output": "長煙散初碧,皎月澄輕素。搴幌翫琴書,開軒引雲霧。斜漢耿層閣,清風搖玉樹。"
},
{
"instruction": "以下是一首唐詩的第一句話,請用你的知識判斷並完成整首詩。",
"input": "歡樂難再逢,芳辰良可惜。",
"output": "玉酒泛雲罍,蘭殽陳綺席。千鍾合堯禹,百獸諧金石。得志重寸陰,忘懷輕尺璧。"
},
{
"instruction": "以下是一首唐詩的第一句話,請用你的知識判斷並完成整首詩。",
"input": "建章歡賞夕,二八盡妖妍。",
"output": "羅綺昭陽殿,芬芳玳瑁筵。珮移星正動,扇掩月初圓。無勞上懸圃,即此對神仙。"
},
{
"instruction": "以下是一首唐詩的第一句話,請用你的知識判斷並完成整首詩。",
"input": "新豐停翠輦,譙邑駐鳴笳。",
"output": "園荒一徑斷,苔古半階斜。前池消舊水,昔樹發今花。一朝辭此地,四海遂爲家。"
},
{
"instruction": "以下是一首唐詩的第一句話,請用你的知識判斷並完成整首詩。",
"input": "春蒐馳駿骨,總轡俯長河。",
"output": "霞處流縈錦,風前瀁卷羅。水花翻照樹,堤蘭倒插波。豈必汾陰曲,秋雲發棹歌。"
},
{
"instruction": "以下是一首唐詩的第一句話,請用你的知識判斷並完成整首詩。",
"input": "重巒俯渭水,碧嶂插遙天。",
"output": "出紅扶嶺日,入翠貯巖煙。疊松朝若夜,複岫闕疑全。對此恬千慮,無勞訪九仙。"
},
testing data
Tang_testing_data.json
[
{
"instruction": "以下是一首唐詩的第一句話,請用你的知識判斷並完成整首詩。",
"input": "雪霽銀妝素,桔高映瓊枝。"
},
{
"instruction": "以下是一首唐詩的第一句話,請用你的知識判斷並完成整首詩。",
"input": "夫子何爲者?栖栖一代中。"
},
{
"instruction": "以下是一首唐詩的第一句話,請用你的知識判斷並完成整首詩。",
"input": "飛蓋去芳園,蘭橈遊翠渚。"
},
{
"instruction": "以下是一首唐詩的第一句話,請用你的知識判斷並完成整首詩。",
"input": "條風開獻節,灰律動初陽。"
},
{
"instruction": "以下是一首唐詩的第一句話,請用你的知識判斷並完成整首詩。",
"input": "昨夜星辰昨夜風,畫樓西畔桂堂東。"
},
{
"instruction": "以下是一首唐詩的第一句話,請用你的知識判斷並完成整首詩。",
"input": "三日入廚下,洗手作羹湯。"
},
{
"instruction": "以下是一首唐詩的第一句話,請用你的知識判斷並完成整首詩。",
"input": "嵩雲秦樹久離居,雙鯉迢迢一紙書。"
},
{
"instruction": "以下是一首唐詩的第一句話,請用你的知識判斷並完成整首詩。",
"input": "慨然撫長劒,濟世豈邀名。"
},
{
"instruction": "以下是一首唐詩的第一句話,請用你的知識判斷並完成整首詩。",
"input": "乘興南遊不戒嚴,九重誰省諫書函。"
},
{
"instruction": "以下是一首唐詩的第一句話,請用你的知識判斷並完成整首詩。",
"input": "猿鳥猶疑畏簡書,風雲常爲護儲胥。"
},
{
"instruction": "以下是一首唐詩的第一句話,請用你的知識判斷並完成整首詩。",
"input": "君問歸期未有期,巴山夜雨漲秋池。"
},
{
"instruction": "以下是一首唐詩的第一句話,請用你的知識判斷並完成整首詩。",
"input": "相見時難別亦難,東風無力百花殘。"
},
{
"instruction": "以下是一首唐詩的第一句話,請用你的知識判斷並完成整首詩。",
"input": "雲母屏風燭影深,長河漸落曉星沈。"
},
{
"instruction": "以下是一首唐詩的第一句話,請用你的知識判斷並完成整首詩。",
"input": "高閣客竟去,小園花亂飛。"
},
{
"instruction": "以下是一首唐詩的第一句話,請用你的知識判斷並完成整首詩。",
"input": "瑤池阿母綺窗開,黃竹歌聲動地哀。"
}
]
相关文章:
大语言模型LLM的微调代码详解
代码的摘要说明 一、整体功能概述 这段 Python 代码主要实现了基于 Hugging Face Transformers 库对预训练语言模型(具体为 TAIDE-LX-7B-Chat 模型)进行微调(Fine-tuning)的功能,使其能更好地应用于生成唐诗相关内容的…...
鸿蒙主流路由详解
鸿蒙主流路由详解 Navigation Navigation更适合于一次开发,多端部署,也是官方主流推荐的一种路由控制方式,但是,使用起来入侵耦合度高,所以,一般会使用HMRouter,这也是官方主流推荐的路由 Navigation官网地址 个人源码地址 路由跳转 第一步-定义路由栈 Provide(PageInfo) pag…...
GelSight和Meta AI合作推出多模态指尖形全向视触觉传感器Digit360
近日,触觉智能技术先锋 GelSight 与 Meta AI相关团队今宣布推出 Digit 360,这是一款呈人工指尖形状的触觉传感器,它能够以高精度将触摸数字化,从而提供较为丰富且详细的触觉数据。Digit 360将会拓展触觉传感研究领域,标…...
XX科技面试笔试题
笔试题 一、选择题 (每题5分,共20分) 1、构成存储器的最小单位是 ( ) A、bit B、Byte C、MB 2、下列不属于WEB服务的是 ( ) A、Apache B、Nginx C、IIS D、LVS 3、web服务器默认端口为 ( ) A、80 B、800 C、22 D、43 4、下列安装系统方法中,适合大量装机需求的是 ( ) A、U盘…...
AVL、B树和B+树
AVL树定义 AVL树(Adelson-Velsky 和 Landis 树)是一种自平衡的二叉搜索树(Binary Search Tree, BST),由苏联数学家Georgy Adelson-Velsky和Evgenii Landis在1962年提出。AVL树通过在每个节点上维护一个平衡因子&#…...
[SWPUCTF 2021 新生赛]include
参考博客: 文件包含 [SWPUCTF 2021 新生赛]include-CSDN博客 NSSCTF | [SWPUCTF 2021 新生赛]include-CSDN博客 考点:php伪协议和文件包含 PHP伪协议详解-CSDN博客 php://filter php://filter可以获取指定文件源码。当它与包含函数结合时,php://filter流会被当…...
ES----安装 elasticsearch入门,elasticsearch安装,centos安装es,centos安装elasticsearch
ES 如需要对应资源,请评论留言,或再最后视频中关注获取 1. 安装 1.1 安装es 创建网络(centos系统,docker环境) docker network create es-netdocker安装es —如果下载失败,请看我的docker配置镜像的文章…...
探索文件系统,Python os库是你的瑞士军刀
文章目录 探索文件系统,Python os库是你的瑞士军刀第一部分:背景介绍第二部分:os库是什么?第三部分:如何安装os库?第四部分:简单库函数使用方法1. 获取当前工作目录2. 改变当前工作目录3. 列出目…...
android studio引用so库
在工程中编译好的so库文件将在原始编译工程对应目录下:build/intermediates/cxx/Debug/xxxxxx/obj/ 其目录结构如上所示,包含生成的四个版本,每个文件夹下均包含c/c源码编译成的Android版本的libnavi.so库和提供应用接口的libnavi-lib.so库。…...
Ubuntu 服务器部署 Tomcat 并配置 SSL/TLS 证书
本文目录 准备登陆云服务器安装 Java下载 tomcat 包配置防火墙浏览器访问 Tomcat 默认页面以服务的形式运行 Tomcat创建 Tomcat 用户和组创建 systemd 服务文件启动 tomcat 服务 Tomcat webapps 文件目录部署一个静态网站tomcat 的配置文件 将域名解析到服务器Tomcat 配置 SSL/…...
不间断电源 (UPS) 对现代技术可靠性的影响
在这个技术型世界里,无论是在个人还是商业环境中,电力供应商提供的稳定供电都变得越来越重要。 不间断电源 (UPS) 系统是一种不可或缺的解决方案,可保证终端设备不受干扰地运行,在出现电源问题或故障时让用户继续工作。 这篇文章…...
Android 基础类(01)- Thread类 - readyToRun和threadLoop
一、前言: 在阅读AOSP代码过程中,我们经常会看到Thread子类重写两个方法:readyToRun和threadLoop,不清楚的同学,可能在这儿连调用逻辑都搞不清楚了,因为找不到谁调用了它。我这儿先不去深究Thread内部逻辑…...
【组件封装】uniapp vue3 封装一个自定义下拉刷新组件pullRefresh,带刷新时间和加载动画教程
文章目录 前言一、实现原理二、组件样式和功能设计三、scroll-view 自定义下拉刷新使用回顾相关属性:最终版完整代码: 前言 手把手教你封装一个移动端 自定义下拉刷新组件带更新时间和加载动画(PullRefresh),以uniapp …...
通过 JNI 实现 Java 与 Rust 的 Channel 消息传递
做纯粹的自己。“你要搞清楚自己人生的剧本——不是父母的续集,不是子女的前传,更不是朋友的外篇。对待生命你不妨再大胆一点,因为你好歹要失去它。如果这世上真有奇迹,那只是努力的另一个名字”。 一、crossbeam_channel 参考 cr…...
一起学习Fortran:如何安装Fortran
Fortran(全称Formula Translation,意为“公式翻译”)是一种通用编译命令式编程语言,适用于数值计算和科学计算。Fortran语言最初是由IBM在20世纪50年代为科学和工程应用程序而开发的,第一个Fortran版本——FORTRAN I在…...
社交新零售模式下“2+1 链动模式 S2B2C 商城小程序”的创新实践与发展策略
摘要:随着实体商业与社交网络深度融合,社交新零售蓬勃兴起,“21 链动模式 S2B2C 商城小程序”作为其中创新典范,融合独特激励机制与数字化运营优势,重塑零售生态。本文剖析该模式架构、运作逻辑,探讨其在私…...
【博主推荐】C# Winform 拼图小游戏源码详解(附源码)
文章目录 前言摘要1.设计来源拼图小游戏讲解1.1 拼图主界面设计1.2 一般难度拼图效果1.3 普通难度拼图效果1.4 困难难度拼图效果1.5 地域难度拼图效果1.6 内置五种拼图效果 2.效果和源码2.1 动态效果2.2 源代码 源码下载结束语 前言 在数字浪潮汹涌澎湃的时代,程序开…...
贝叶斯统计的核心思想与基础知识:中英双语
中文版 贝叶斯统计的核心思想与基础知识 贝叶斯统计是以贝叶斯定理为核心,通过将先验知识和观测数据相结合,更新对参数或模型的认知的一种统计方法。它不仅强调概率的频率解释(频率统计学中概率描述事件的长期发生频率)…...
Verilog使用liberty文件中cell单元的demo
Liberty(.lib)文件是用来描述标准单元库中逻辑单元(如门电路、触发器等)的时序和功耗特性的,不是用来直接定义Verilog中的元件。在Verilog设计中,我们通常通过实例化模块(module)来创…...
openssl生成ca证书
常见CA文件夹 1、生成CA钥匙 openssl genrsa -out ./private/cakey.pem 2、生成CA自签名 openssl req -new -x509 -key ./private/cakey.pem -out ./cacert.crt -days 3650 3、生成http服务器私钥 openssl genrsa -out ./data/frontt.project.com.key 2048 4、CA给http服务器…...
OGRE 3D----2. QGRE + QQuickView
将 OGRE(面向对象图形渲染引擎)集成到使用 QQuickView 的 Qt Quick 应用程序中,可以在现代灵活的 UI 框架中提供强大的 3D 渲染功能。本文将指导您如何在 QQuickView 环境中设置 OGRE。 前提条件 在开始之前,请确保您已安装以下内容: Qt(版本 5.15 )OGRE(版本14.2.5)…...
【Java 学习】面向程序的三大特性:封装、继承、多态
引言 1. 封装1.1 什么是封装呢?1.2 访问限定符1.3 使用封装 2. 继承2.1 为什么要有继承?2.2 继承的概念2.3 继承的语法2.4 访问父类成员2.4.1 子类中访问父类成员的变量2.4.2 访问父类的成员方法 2.5 super关键字2.6 子类的构造方法 3. 多态3.1 多态的概…...
Online Judge——【前端项目初始化】Vue-CLI初始化项目、组件库引入
目录 一、创建项目二、前端工程化配置三、引入组件 一、创建项目 输入命令:vue create oj-frontend来到如下界面: 选择Manually select features 选择如下图的组件:注意空格是选择,之后回车即可。 选择3.x版本 继续选择ÿ…...
ASP.NET Web(.Net Framework)POST无法正常接收数据
ASP.NET Web(.Net Framework)POST无法正常接收数据 介绍站点Post和Get如何打断点测试测试代码如下服务器站点Post方法修改原因总结 总结 介绍 这一篇文章主要是讲一下之前搭建的HTTP站点POST无法正常接收数据,如果还不知道怎么搭建HTTP站点的…...
vue安装cypress及其部分用法
安装Cypress 在vue中安装Cypress 1. 安装 Cypress 首先,确保你已经安装了 Cypress。在你的 Vue 项目根目录下运行以下命令: npm install cypress --save-dev2. 打开 Cypress 安装完 Cypress 后,可以通过以下命令打开 Cypress 测试界面&a…...
Web Worker 入门:让前端应用多线程化
引出: 作为前端切图仔,在之前的工作中一直都是写后台,没机会用到web Worker,传统的性能优化web Worker用到的场景也很少,毕竟大量的数据计算一般直接给后端去做就行,轮不到前端来考虑(没遇到类似…...
Vue+Elementui el-tree树只能选择子节点并且支持检索
效果: 只能选择子节点 添加配置添加检索代码 源码: <template><div><el-button size"small" type"primary" clearable :disabled"disabled" click"showSign">危险点评估</el-button>…...
MySQL各种问题的原因及解决方案梳理
背景:由于最近一直在做生产环境和测试环境的切换,遇到了各种各样的MySQL问题,为了后面的开发顺利,梳理一下MySQL的报错及解决方案 问题1、MySQL的链接数超过了本身MySQL内部设置的链接限制 报错信息: // An highlig…...
LeetCode—74. 搜索二维矩阵(中等)
仅供个人学习使用 题目描述: 给你一个满足下述两条属性的 m x n 整数矩阵: 每行中的整数从左到右按非严格递增顺序排列。 每行的第一个整数大于前一行的最后一个整数。 给你一个整数 target ,如果 target 在矩阵中,返回 true…...
【Redis】Redis介绍
目录 1.Redis是什么? 2. Redis特性 2.1 速度快 2.2 基于键值对的数据结构服务器 2.3 丰富的功能 2.4 简单稳定 2.5 客户端语言多 2.6 持久化 2.7 主从复制 2.8 高可用和分布式 3. Redis使用场景 3.1 缓存(Cache) 3.2 排行榜系统 3.3 计数器应用 3.4 社交网络 …...
Python 3 教程第23篇(模块)
Python3 模块 在前面的几个章节中我们基本上是用 python 解释器来编程,如果你从 Python 解释器退出再进入,那么你定义的所有的方法和变量就都消失了。 为此 Python 提供了一个办法,把这些定义存放在文件中,为一些脚本或者交互式…...
课题组自主发展了哪些CMAQ模式预报相关的改进技术?
空气污染问题日益受到各级政府以及社会公众的高度重视,从实时的数据监测公布到空气质量数值预报及预报产品的发布,我国在空气质量监测和预报方面取得了一定进展。随着计算机技术的高速发展、空气污染监测手段的提高和人们对大气物理化学过程认识的深入&a…...
七牛云AIGC内容安全方案助力企业合规创新
随着人工智能生成内容(AIGC)技术的飞速发展,内容审核的难度也随之急剧上升。在传统审核场景中,涉及色情、政治、恐怖主义等内容的标准相对清晰明确,但在AIGC的应用场景中,这些界限变得模糊且难以界定。用户可能通过交互性引导AI生成违规内容,为审核工作带来了前所未有的不可预测…...
Vue.js 中的事件处理
在 Vue.js 中,事件处理是用户与应用交互的重要方式。Vue.js 允许开发者以一种声明式的方式来绑定事件监听器,这使得代码更加简洁和易于维护。本文将介绍 Vue.js 中的事件处理,包括常用的事件类型和如何使用它们。 Vue.js 事件基础 在 Vue.j…...
Windows用pm2部署node.js项目
Windows上pm2启动命令不生效 按照常规启动命令应该如下,但是发现不生效 pm2 start npm --name "project-name" -- start具体如下,可以看到状态都是stopped $ pm2 start npm --name "chatgpt-next-web" -- start [PM2] Starting …...
算法【Java】—— 动态规划之路径问题
前言 本文章终点解析第一道题目【不同路径】和最后一道题目【地下城游戏】的动态规划思路,中间几道题目会很快过完,大家如果不熟悉动态规划的思路可以重点看一下这两道题目的解析。 不同路径 https://leetcode.cn/problems/unique-paths 解析…...
DreamFace4.9.0 |AI照片动画师,让照片说话和跳舞
DreamFace是一款有趣的照片动画应用程序,通过AI技术让您的照片唱歌、跳舞甚至说话。只需上传照片并选择歌曲,即可生成动态效果。此外,它还支持图片增强、降噪和高清处理,创建人工智能驱动的头像。无论是让宠物说话还是让朋友唱情歌…...
vue-baidu-map基本使用
vue-baidu-map 是一个基于 Vue.js 的百度地图组件库,它封装了百度地图的 JavaScript API,使得在 Vue 项目中使用百度地图功能更加便捷。下面是如何在 Vue 项目中安装和使用 vue-baidu-map 的步骤: 安装 首先确保你的项目已经集成了 Vue 和 …...
新电脑验机-允许上网,同时禁止windows系统联网自动激活
效果: 重要提示:我虽然得到上图效果,但也不确定是否有坑,不保证商家是否认可。 笔记本电脑七天无理由退货的前提条件是“windows和office未激活”。如何 oobe\bypassnor 绕过开机联网并创建本地账号的方法我就不写了,搜…...
Android 是否支持AB分区
Android 是否支持AB分区 C:\Users\Administrator>adb shell bengal:/ $ su bengal:/ # getprop |grep treble [ro.treble.enabled]: [true] bengal:/ #返回不为空而且为true,那就是支持pt(project treble)分区 进入fastboot模式 adb reboot bootloader查看当前…...
Qt6.8安卓Android开发环境配置
时隔多年,重拾QtCreator下Android开发。发现Qt6下安卓开发环境配置变简单不少!只需三步即可在QtCreator下进行Android开发: 一、使用Qt Mantenance Tool进行Android模块的安装: 如果感觉安装网速较慢,可以查看本人另外…...
JavaSE——类与对象(4)
一、静态变量 1.1为什么要有静态变量 现在有一群小朋友在做游戏,不是有新的小朋友加入,请问如何知道现在共有多少人在完?看这段代码: public class first {public static void main(String[] args) {int count 0;child child1 …...
网络编程中的字节序函数htonl()、htons()、ntohl()和ntohs()
目录 1,网络字节序和主机字节序 2. 函数的具体作用 2.1,htonl(Host to Network Long) 2.2,htons(Host to Network Short) 2.3,ntohl(Network to Host Long) 2.4,ntohs(Network to Host Sho…...
如何在HarmonyOS NEXT中处理页面间的数据传递?
大家好,前两天的Mate70的发布,让人热血沸腾啊,不想错过,自学的小伙伴一起啊,今天分享的学习笔记是关于页面间数据伟递的问题,在HarmonyOS NEXT 5.0 中,页面间的数据传递可以有很多种方式&#x…...
【Code First】.NET开源 ORM 框架 SqlSugar 系列
.NET开源 ORM 框架 SqlSugar 系列 【开篇】.NET开源 ORM 框架 SqlSugar 系列【入门必看】.NET开源 ORM 框架 SqlSugar 系列【实体配置】.NET开源 ORM 框架 SqlSugar 系列【Db First】.NET开源 ORM 框架 SqlSugar 系列【Code First】.NET开源 ORM 框架 SqlSugar 系列 …...
Day28 贪心算法 part02
122.买卖股票的最佳时机II 本题解法很巧妙,本题大家可以先自己思考一下然后再看题解,会有惊喜! class Solution {public int maxProfit(int[] prices) {//分析每一天的情况。只要保证今天买,明天卖可以不亏钱,那就是最大的利润。把每一天可以赚钱的机会都不放过,先把能挣…...
JVM_栈详解一
1、栈的存储单位 **栈中存储什么?**, 每个线程都有自己的栈,栈中的数据都是以栈帧(Stack Frame)的格式存在。在这个线程上正在执行的每个方法都各自对应一个栈帧(Stack Frame)。 栈帧是一个内存…...
深入浅出UART驱动开发与调试:从基础调试到虚拟驱动实现
往期内容 本专栏往期内容:Uart子系统 UART串口硬件介绍深入理解TTY体系:设备节点与驱动程序框架详解Linux串口应用编程:从UART到GPS模块及字符设备驱动 解UART 子系统:Linux Kernel 4.9.88 中的核心结构体与设计详解IMX 平台UART驱…...
集成量子光子学(IQP)
IQP 正在成为量子计算的可行替代方案量子源、波导和调制器等领域的研究使这成为可能与 CMOS 技术的兼容意味着工业可扩展性将更加容易 量子光子学的基本组成部分 IQP 系统的基本组成部分包括: 来源(例如腔体中的 QD) 波导定向耦合器&#…...
【代码随想录|贪心算法02】
122.买股票的最佳时机 题目链接https://leetcode.cn/problems/best-time-to-buy-and-sell-stock-ii 好巧妙的一道题啊,做之前完全不会想到这种解法。 局部最优:收集每天正利润 全局最优:求得最大利润 这道题只让你返回最大的利润和&…...