VLM(视觉语言模型)与DeepSeek R1(奖励机制)如何结合
VLM(视觉语言模型)与DeepSeek R1(奖励机制)如何结合
flyfish
VLM的传统训练依赖于监督学习(直接拟合问答对),而规则奖励函数通常用于强化学习(通过试错和奖励反馈优化策略)。这两种方式如何结合?
源码来自
VLM-R1/src/open-r1-multimodal/src/open_r1/grpo_rec.py
# 导入 debugpy 库,用于调试,当前代码中被注释掉,若需要调试可取消注释
# import debugpy
# try:
# # 5678 是 VS Code 调试配置中的默认附加端口。除非指定主机和端口,否则主机默认为 127.0.0.1
# debugpy.listen(("localhost", 9501))
# print("Waiting for debugger attach")
# debugpy.wait_for_client()
# except Exception as e:
# pass# 导入操作系统相关功能的库
import os
# 导入正则表达式库,用于字符串匹配和处理
import re
# 导入日期时间处理库
from datetime import datetime
# 导入数据类装饰器和字段定义类,用于定义数据类
from dataclasses import dataclass, field
# 导入可选类型注解,用于表示某个参数可以为 None
from typing import Optional# 导入 Pillow 库中的 Image 类,用于处理图像
from PIL import Image
# 导入 PyTorch 中的数据集基类
from torch.utils.data import Dataset
# 导入 Qwen2VL 条件生成模型
from transformers import Qwen2VLForConditionalGeneration# 导入自定义的数学验证模块中的解析和验证函数
from math_verify import parse, verify
# 导入自定义的 Qwen2VLGRPOTrainer 类
from open_r1.trainer import Qwen2VLGRPOTrainer
# 导入 TRL 库中的 GRPO 配置、训练器、模型配置、脚本参数、解析器和 PEFT 配置获取函数
from trl import GRPOConfig, GRPOTrainer, ModelConfig, ScriptArguments, TrlParser, get_peft_config
# 导入 Transformers 库中的训练参数类
from transformers import TrainingArguments
# 导入 YAML 文件处理库
import yaml
# 导入 JSON 文件处理库
import json
# 导入随机数生成库
import random
# 导入数学计算库
import math# ----------------------- 修复当前版本 transformers 中的 flash attention 错误 -----------------------
# 导入 Qwen2_5_VL 模型中的相关类和函数
from transformers.models.qwen2_5_vl.modeling_qwen2_5_vl import Qwen2_5_VLVisionFlashAttention2, apply_rotary_pos_emb_flashatt, flash_attn_varlen_func
# 导入 PyTorch 库
import torch
# 导入元组类型注解
from typing import Tuple# 自定义 Qwen2_5_VLVisionFlashAttention2 类的前向传播函数
def custom_forward(self,hidden_states: torch.Tensor,cu_seqlens: torch.Tensor,rotary_pos_emb: Optional[torch.Tensor] = None,position_embeddings: Optional[Tuple[torch.Tensor, torch.Tensor]] = None,) -> torch.Tensor:# 获取隐藏状态的序列长度seq_length = hidden_states.shape[0]# 通过 qkv 层得到查询、键、值张量,并进行形状调整和维度置换q, k, v = self.qkv(hidden_states).reshape(seq_length, 3, self.num_heads, -1).permute(1, 0, 2, 3).unbind(0)# 如果没有提供位置嵌入,则根据旋转位置嵌入计算余弦和正弦值if position_embeddings is None:# 打印一次警告信息,提示 RoPE 嵌入计算方式的变化logger.warning_once("The attention layers in this model are transitioning from computing the RoPE embeddings internally ""through `rotary_pos_emb` (2D tensor of RoPE theta values), to using externally computed ""`position_embeddings` (Tuple of tensors, containing cos and sin). In v4.54 `rotary_pos_emb` will be ""removed and `position_embeddings` will be mandatory.")# 拼接旋转位置嵌入emb = torch.cat((rotary_pos_emb, rotary_pos_emb), dim=-1)# 计算余弦值cos = emb.cos().float()# 计算正弦值sin = emb.sin().float()else:# 从位置嵌入中获取余弦和正弦值cos, sin = position_embeddings# 将余弦值转换为浮点类型cos = cos.to(torch.float)# 将正弦值转换为浮点类型sin = sin.to(torch.float)# 应用旋转位置嵌入到查询和键张量q, k = apply_rotary_pos_emb_flashatt(q.unsqueeze(0), k.unsqueeze(0), cos, sin)# 去除查询张量的额外维度q = q.squeeze(0)# 去除键张量的额外维度k = k.squeeze(0)# 计算最大序列长度max_seqlen = (cu_seqlens[1:] - cu_seqlens[:-1]).max().item()# 调用 flash 注意力函数计算注意力输出attn_output = flash_attn_varlen_func(q, k, v, cu_seqlens, cu_seqlens, max_seqlen, max_seqlen).reshape(seq_length, -1)# 通过投影层得到最终的注意力输出attn_output = self.proj(attn_output)return attn_output# 将自定义的前向传播函数赋值给 Qwen2_5_VLVisionFlashAttention2 类的 forward 方法
Qwen2_5_VLVisionFlashAttention2.forward = custom_forward# ----------------------- 主脚本 -----------------------
# 定义 GRPOScriptArguments 数据类,继承自 ScriptArguments
@dataclass
class GRPOScriptArguments(ScriptArguments):"""用于 GRPO 训练脚本的脚本参数。参数:reward_funcs (`list[str]`):奖励函数列表。可能的值: 'accuracy', 'format'。"""# 奖励函数列表,默认包含 'accuracy' 和 'format'reward_funcs: list[str] = field(default_factory=lambda: ["accuracy", "format"],metadata={"help": "List of reward functions. Possible values: 'accuracy', 'format'"})# 图像的最大像素数,默认为 12845056max_pixels: Optional[int] = field(default=12845056,metadata={"help": "Maximum number of pixels for the image"})# 图像的最小像素数,默认为 3136min_pixels: Optional[int] = field(default=3136,metadata={"help": "Minimum number of pixels for the image"})# 图像的根目录,默认为 Noneimage_root: Optional[str] = field(default=None,metadata={"help": "Root directory of the image"})# 定义系统提示信息,用于指导模型的对话生成
SYSTEM_PROMPT = ("A conversation between User and Assistant. The user asks a question, and the Assistant solves it. The assistant ""first thinks about the reasoning process in the mind and then provides the user with the answer. The reasoning ""process and answer are enclosed within <think> </think> and <answer> </answer> tags, respectively, i.e., ""<think> reasoning process here </think><answer> answer here </answer>"
)# 定义 LazySupervisedDataset 类,继承自 Dataset
class LazySupervisedDataset(Dataset):def __init__(self, data_path: str, script_args: GRPOScriptArguments):# 调用父类的构造函数super(LazySupervisedDataset, self).__init__()# 保存脚本参数self.script_args = script_args# 初始化数据字典列表self.list_data_dict = []# 如果数据文件是 YAML 格式if data_path.endswith(".yaml"):# 打开 YAML 文件with open(data_path, "r") as file:# 加载 YAML 数据yaml_data = yaml.safe_load(file)# 获取数据集列表datasets = yaml_data.get("datasets")# 文件格式应为:# datasets:# - json_path: xxxx1.json# sampling_strategy: first:1000# - json_path: xxxx2.json# sampling_strategy: end:3000# - json_path: xxxx3.json# sampling_strategy: random:999# 遍历每个数据集for data in datasets:# 获取 JSON 文件路径json_path = data.get("json_path")# 获取采样策略,默认为 'all'sampling_strategy = data.get("sampling_strategy", "all")# 初始化采样数量为 Nonesampling_number = None# 如果 JSON 文件是 JSONL 格式if json_path.endswith(".jsonl"):# 初始化当前数据字典列表cur_data_dict = []# 打开 JSONL 文件with open(json_path, "r") as json_file:# 逐行读取文件for line in json_file:# 解析每行 JSON 数据并添加到当前数据字典列表cur_data_dict.append(json.loads(line.strip()))# 如果 JSON 文件是 JSON 格式elif json_path.endswith(".json"):# 打开 JSON 文件with open(json_path, "r") as json_file:# 加载 JSON 数据到当前数据字典列表cur_data_dict = json.load(json_file)else:# 如果文件类型不支持,抛出异常raise ValueError(f"Unsupported file type: {json_path}")# 如果采样策略包含冒号if ":" in sampling_strategy:# 分割采样策略和采样数量sampling_strategy, sampling_number = sampling_strategy.split(":")# 如果采样数量包含百分比符号if "%" in sampling_number:# 计算采样数量sampling_number = math.ceil(int(sampling_number.split("%")[0]) * len(cur_data_dict) / 100)else:# 将采样数量转换为整数sampling_number = int(sampling_number)# 应用采样策略if sampling_strategy == "first" and sampling_number is not None:# 取前 sampling_number 个样本cur_data_dict = cur_data_dict[:sampling_number]elif sampling_strategy == "end" and sampling_number is not None:# 取后 sampling_number 个样本cur_data_dict = cur_data_dict[-sampling_number:]elif sampling_strategy == "random" and sampling_number is not None:# 随机打乱样本random.shuffle(cur_data_dict)# 取前 sampling_number 个样本cur_data_dict = cur_data_dict[:sampling_number]# 打印从当前 JSON 文件加载的样本数量print(f"Loaded {len(cur_data_dict)} samples from {json_path}")# 将当前数据字典列表添加到总数据字典列表self.list_data_dict.extend(cur_data_dict)else:# 如果文件类型不支持,抛出异常raise ValueError(f"Unsupported file type: {data_path}")def __len__(self):# 返回数据字典列表的长度return len(self.list_data_dict)def __getitem__(self, i):# 定义将示例转换为对话格式的函数def make_conversation(example):return {"prompt": [{"role": "system", "content": SYSTEM_PROMPT},{"role": "user", "content": example["problem"]}]}# 问题模板,用于包含图像的对话QUESTION_TEMPLATE = "{Question} First output the thinking process in <think> </think> tags and then output the final answer in <answer> </answer> tags. Output the final answer in JSON format."# 定义将包含图像的示例转换为对话格式的函数def make_conversation_image(example):return {"prompt": [# {"role": "system", "content": [{"type": "text", "text": SYSTEM_PROMPT}]},{"role": "user","content": [{"type": "image"},{"type": "text", "text": QUESTION_TEMPLATE.format(Question=example["problem"])}]}]}# 获取指定索引的示例example = self.list_data_dict[i]# 获取图像根目录image_root = self.script_args.image_root# 如果示例中包含图像信息if 'image' in example:# 构建图像路径image_path = os.path.join(image_root, example['image'])# 如果图像文件不存在while not os.path.exists(image_path):# 打印警告信息print(f"Warning: Image {image_path} not found, randomly selecting another image")# 随机选择一个新的索引new_index = random.randint(0, len(self.list_data_dict)-1)# 获取新的示例example = self.list_data_dict[new_index]# 构建新的图像路径image_path = os.path.join(image_root, example['image'])# 打开图像并转换为 RGB 格式image = Image.open(image_path).convert("RGB")else:# 如果示例中不包含图像信息,图像为 Noneimage = Nonereturn {'image': image,'problem': example['problem'],'solution': example['solution'],'prompt': make_conversation_image(example)['prompt'] if 'image' in example else make_conversation(example)['prompt']}'''如果模型预测的边界框与真实边界框的交并比(IoU)大于 0.5,则奖励为 1.0,否则为 0.0。这是一种硬奖励,未来可能使用软奖励会更好。
'''
def iou_reward(completions, solution, **kwargs):# 定义计算交并比的函数def iou(box1, box2):# 计算交集的左上角坐标inter_x1 = max(box1[0], box2[0])inter_y1 = max(box1[1], box2[1])# 计算交集的右下角坐标inter_x2 = min(box1[2]-1, box2[2]-1)inter_y2 = min(box1[3]-1, box2[3]-1)# 如果交集存在if inter_x1 < inter_x2 and inter_y1 < inter_y2:# 计算交集面积inter = (inter_x2-inter_x1+1)*(inter_y2-inter_y1+1)else:# 交集面积为 0inter = 0# 计算并集面积union = (box1[2]-box1[0])*(box1[3]-box1[1]) + (box2[2]-box2[0])*(box2[3]-box2[1]) - inter# 返回交并比return float(inter)/union# 获取完成内容列表contents = [completion[0]["content"] for completion in completions]# 初始化奖励列表rewards = []# 获取当前时间并格式化current_time = datetime.now().strftime("%d-%H-%M-%S-%f")# 定义答案标签的正则表达式模式answer_tag_pattern = r'<answer>(.*?)</answer>'# 定义边界框的正则表达式模式bbox_pattern = r'\[(\s*-?\d*\.?\d+\s*),\s*(\s*-?\d*\.?\d+\s*),\s*(\s*-?\d*\.?\d+\s*),\s*(\s*-?\d*\.?\d+\s*)\]'# 遍历完成内容和真实解决方案for content, sol in zip(contents, solution):# 初始化奖励为 0.0reward = 0.0# 尝试进行符号验证try:# 在完成内容中查找答案标签content_answer_match = re.search(answer_tag_pattern, content)if content_answer_match:# 获取答案内容content_answer = content_answer_match.group(1).strip()# 在答案内容中查找边界框bbox_match = re.search(bbox_pattern, content_answer)if bbox_match:# 获取边界框坐标bbox = [int(bbox_match.group(1)), int(bbox_match.group(2)), int(bbox_match.group(3)), int(bbox_match.group(4))]# 如果交并比大于 0.5if iou(bbox, sol) > 0.5:# 奖励为 1.0reward = 1.0except Exception:# 如果验证失败,继续下一个验证方法pass# 将奖励添加到奖励列表rewards.append(reward)# 如果处于调试模式if os.getenv("DEBUG_MODE") == "true":# 获取日志路径log_path = os.getenv("LOG_PATH")# 打开日志文件并追加记录with open(log_path, "a") as f:# 记录当前时间和奖励信息f.write(f"------------- {current_time} Accuracy reward: {reward} -------------\n")# 记录完成内容f.write(f"Content: {content}\n")# 记录真实解决方案f.write(f"Solution: {sol}\n")return rewardsdef format_reward(completions, **kwargs):"""奖励函数,用于检查完成内容是否符合特定格式。"""# 定义格式的正则表达式模式# pattern = r"<think>.*?</think>\s*<answer>.*?</answer>"pattern = r"<think>.*?</think>\s*<answer>.*?\{.*\[\d+,\s*\d+,\s*\d+,\s*\d+\].*\}.*?</answer>"# 获取完成内容列表completion_contents = [completion[0]["content"] for completion in completions]# 检查每个完成内容是否符合格式matches = [re.fullmatch(pattern, content, re.DOTALL) for content in completion_contents]# 根据匹配结果生成奖励列表return [1.0 if match else 0.0 for match in matches]# 奖励函数注册表,将奖励函数名称映射到对应的函数
reward_funcs_registry = {"accuracy": iou_reward,"format": format_reward,
}def main(script_args, training_args, model_args):# 根据脚本参数中的奖励函数名称,从注册表中获取对应的奖励函数reward_funcs = [reward_funcs_registry[func] for func in script_args.reward_funcs]# 打印奖励函数列表print("reward_funcs:", reward_funcs)# 加载数据集dataset = LazySupervisedDataset(script_args.dataset_name, script_args)# 选择训练器类,这里使用自定义的 Qwen2VLGRPOTrainertrainer_cls = Qwen2VLGRPOTrainer# 初始化 GRPO 训练器trainer = trainer_cls(model=model_args.model_name_or_path, # 模型名称或路径reward_funcs=reward_funcs, # 奖励函数列表args=training_args, # 训练参数train_dataset=dataset, # 训练数据集eval_dataset=None, # 评估数据集,这里设为 Nonepeft_config=get_peft_config(model_args), # PEFT 配置attn_implementation=model_args.attn_implementation, # 注意力实现方式max_pixels=script_args.max_pixels, # 图像最大像素数min_pixels=script_args.min_pixels, # 图像最小像素数torch_dtype=model_args.torch_dtype, # PyTorch 数据类型)# 开始训练模型trainer.train()# 保存模型到指定的输出目录trainer.save_model(training_args.output_dir)# 如果设置了将模型推送到 Hubif training_args.push_to_hub:# 将模型推送到 Hub,并指定数据集名称trainer.push_to_hub(dataset_name=script_args.dataset_name)if __name__ == "__main__":# 创建 TrlParser 对象,用于解析脚本参数、训练配置和模型配置parser = TrlParser((GRPOScriptArguments, GRPOConfig, ModelConfig))# 解析命令行参数和配置script_args, training_args, model_args = parser.parse_args_and_config()# 调用主函数开始训练main(script_args, training_args, model_args)
代码中的两个关键奖励函数 format_reward 和 iou_reward。
1. 格式奖励函数 format_reward
函数定义和功能
def format_reward(completions, **kwargs):"""Reward function that checks if the completion has a specific format."""pattern = r"<think>.*?</think>\s*<answer>.*?\{.*\[\d+,\s*\d+,\s*\d+,\s*\d+\].*\}.*?</answer>"completion_contents = [completion[0]["content"] for completion in completions]matches = [re.fullmatch(pattern, content, re.DOTALL) for content in completion_contents]return [1.0 if match else 0.0 for match in matches]
此函数的主要功能是检查模型生成的完成内容是否符合特定的格式要求。具体来说,它期望模型的输出满足以下格式:
- 包含
<think>和</think>标签,用于包裹思考过程。 - 包含
<answer>和</answer>标签,用于包裹答案。 - 答案部分需要是一个 JSON 格式,并且其中包含一个由四个整数组成的列表,通常可以理解为表示边界框的坐标。
实现步骤
- 定义正则表达式模式:
pattern是一个正则表达式,用于描述期望的输出格式。 - 提取完成内容:
completion_contents从completions中提取出每个完成内容的文本部分。 - 检查格式匹配:
matches使用re.fullmatch函数检查每个完成内容是否完全匹配正则表达式模式。 - 生成奖励列表:根据匹配结果,为每个完成内容生成一个奖励值,如果匹配则为 1.0,否则为 0.0。
作用
通过这个奖励函数,模型在训练过程中会被激励去生成符合特定格式的输出,有助于规范模型的回答结构,使得输出更易于解析和使用。
2. 交并比(IoU)奖励函数 iou_reward
函数定义和功能
def iou_reward(completions, solution, **kwargs):def iou(box1, box2):inter_x1 = max(box1[0], box2[0])inter_y1 = max(box1[1], box2[1])inter_x2 = min(box1[2]-1, box2[2]-1)inter_y2 = min(box1[3]-1, box2[3]-1)if inter_x1 < inter_x2 and inter_y1 < inter_y2:inter = (inter_x2-inter_x1+1)*(inter_y2-inter_y1+1)else:inter = 0union = (box1[2]-box1[0])*(box1[3]-box1[1]) + (box2[2]-box2[0])*(box2[3]-box2[1]) - interreturn float(inter)/unioncontents = [completion[0]["content"] for completion in completions]rewards = []current_time = datetime.now().strftime("%d-%H-%M-%S-%f")answer_tag_pattern = r'<answer>(.*?)</answer>'bbox_pattern = r'\[(\s*-?\d*\.?\d+\s*),\s*(\s*-?\d*\.?\d+\s*),\s*(\s*-?\d*\.?\d+\s*),\s*(\s*-?\d*\.?\d+\s*)\]'for content, sol in zip(contents, solution):reward = 0.0try:content_answer_match = re.search(answer_tag_pattern, content)if content_answer_match:content_answer = content_answer_match.group(1).strip()bbox_match = re.search(bbox_pattern, content_answer)if bbox_match:bbox = [int(bbox_match.group(1)), int(bbox_match.group(2)), int(bbox_match.group(3)), int(bbox_match.group(4))]if iou(bbox, sol) > 0.5:reward = 1.0except Exception:passrewards.append(reward)if os.getenv("DEBUG_MODE") == "true":log_path = os.getenv("LOG_PATH")with open(log_path, "a") as f:f.write(f"------------- {current_time} Accuracy reward: {reward} -------------\n")f.write(f"Content: {content}\n")f.write(f"Solution: {sol}\n")return rewards
此函数的主要功能是评估模型预测的边界框与真实边界框之间的重叠程度,并根据交并比(IoU)值给予奖励。
实现步骤
- 定义 IoU 计算函数:
iou函数用于计算两个边界框的交并比。它首先计算两个边界框的交集面积和并集面积,然后将交集面积除以并集面积得到 IoU 值。 - 提取完成内容:
contents从completions中提取出每个完成内容的文本部分。 - 查找答案和边界框:使用正则表达式
answer_tag_pattern查找完成内容中的答案部分,再使用bbox_pattern查找答案中的边界框坐标。 - 计算 IoU 并给予奖励:对于每个完成内容,提取预测的边界框坐标,与真实边界框计算 IoU 值。如果 IoU 值大于 0.5,则给予 1.0 的奖励,否则给予 0.0 的奖励。
- 日志记录(可选):如果设置了调试模式(
DEBUG_MODE为true),则将每个完成内容的奖励信息记录到日志文件中。
作用
通过这个奖励函数,模型在训练过程中会被激励去预测更准确的边界框,提高目标检测的精度。同时,结合格式奖励函数,可以让模型不仅准确预测边界框,还能以规定的格式输出结果。
监督学习与规则奖励函数强化学习的结合方式
1. 数据层面的结合
- 利用监督数据初始化模型:在开始强化学习训练之前,使用监督学习的方式对视觉语言模型(VLM)进行预训练。通过直接拟合问答对数据,让模型学习到基本的语言和视觉特征表示以及问题回答的模式。例如,在代码中使用
LazySupervisedDataset类加载数据集,这些数据可以作为监督学习阶段的训练数据,让模型初步学习到如何根据问题和图像生成答案。 - 监督数据作为强化学习的参考:在强化学习的过程中,监督学习的数据可以作为参考来评估模型的输出。例如,在
iou_reward函数中,通过比较模型预测的边界框与真实边界框的交并比(IoU)来给予奖励,这里的真实边界框就是监督学习中的标签信息。
2. 训练过程的结合
- 分阶段训练:先进行监督学习训练,让模型收敛到一个较好的初始状态。然后再切换到强化学习阶段,使用规则奖励函数来进一步优化模型的策略。在代码中,虽然没有明确体现分阶段训练的逻辑,但可以在实际应用中先使用监督学习的方法对
Qwen2VLForConditionalGeneration模型进行训练,然后再使用Qwen2VLGRPOTrainer进行强化学习训练。 - 混合训练:在每个训练步骤中,既使用监督学习的损失函数,又使用强化学习的奖励函数。例如,可以将监督学习的交叉熵损失和强化学习的奖励损失加权求和,作为总的损失函数来更新模型参数。这样可以让模型在学习过程中既考虑到直接拟合标签的准确性,又考虑到长期的奖励优化。
3. 奖励函数设计结合监督信息
- 准确性奖励:如
iou_reward函数,将模型输出与监督学习中的标签进行比较,根据比较结果给予奖励。这种奖励函数可以促使模型在强化学习过程中输出更接近真实标签的结果,从而结合了监督学习的信息。 - 格式奖励:
format_reward函数可以确保模型输出的格式符合特定要求,这可以看作是一种规则约束。同时,这种格式要求也可以是在监督学习阶段就定义好的,从而将监督学习中的格式规范融入到强化学习的奖励机制中。
相关文章:
VLM(视觉语言模型)与DeepSeek R1(奖励机制)如何结合
VLM(视觉语言模型)与DeepSeek R1(奖励机制)如何结合 flyfish VLM的传统训练依赖于监督学习(直接拟合问答对),而规则奖励函数通常用于强化学习(通过试错和奖励反馈优化策略…...
FFMPEG编码容错处理解决办法之途径----升级库文件
在qt开发环境下接收网络数据,调用ffmpeg解码播放视频,出现闪屏现象,具体现象可以使用操作系统自带的ffplay播放器播放原始视频流可复现;而使用操作系统自带的mpv播放器播放视频则不会出现闪屏;闪屏时会报Could not fin…...
uniapp h5端和app端 使用 turn.js
前提:添加页后,添加页与当前页会重叠在一起,不知道为什么,没有找到解决办法 1.h5端 <template><view class"container"><view id"flipbook"><view class"page page1">Page 1</view><view class"page pag…...
【idea问题排查技巧】
以下是针对 IDEA 中 日志打标(动态标记) 和 全链路追踪 功能的分步详解,结合具体场景和操作截图说明,帮助快速掌握实战技巧。 一、动态日志打标:不修改代码输出关键信息 1. 断点日志打印(非侵入式打标) 场景:在调试时,需要临时查看某个变量的值,但不想修改代码添加…...
【入门音视频】音视频基础知识
🌈前言🌈 这个系列在我学习过程中,对音视频知识归纳总结的笔记。因为音视频相关讲解非常稀少,所以我希望通过这个音视频系列,跟大家一起学习音视频,希望减少初学者在学习上的压力。同时希望也欢迎指出文章的…...
JMeter性能问题
性能测试中TPS上不去的几种原因 性能测试中TPS上不去的几种原因_tps一直上不去-CSDN博客 网络带宽 连接池 垃圾回收机制 压测脚本 通信连接机制 数据库配置 硬件资源 压测机 业务逻辑 系统架构 CPU过高什么原因 性能问题分析-CPU偏高 - 西瓜汁拌面 - 博客园 US C…...
软考高级信息系统项目管理师笔记-第2章信息技术发展
第2章 信息技术发展 2.1 信息技术及其发展 1、按表现形态的不同,信息技术可分为硬技术(物化技术)与软技术(非物化技术)。前者指各种信息设备及其功 能,如传感器、服务器、智能手机、通信卫星、笔记本电脑。后者指有关信息获取与处理的各种知识、方法 与技能,如语言文字…...
大语言模型(LLM)提示词(Prompt)高阶撰写指南
——结构化思维与工程化实践 一、LLM提示词设计的核心逻辑 1. 本质认知 LLM是「超强模式识别器概率生成器」,提示词的本质是构建数据分布约束,通过语义信号引导模型激活特定知识路径。优秀提示词需实现: 精准性:消除歧义&#…...
捷 C++ 课程学习笔记:STL 应用与复杂度分析
一、STL 六大组件 STL(Standard Template Library)是 C 标准库的重要组成部分,提供了通用的模板类和函数,用于实现常用的数据结构和算法。STL 主要包括以下六大组件: 容器(Containers)…...
【python】提取word\pdf格式内容到txt文件
一、使用pdfminer提取 import os import re from pdfminer.high_level import extract_text import docx2txt import jiebadef read_pdf(file_path):"""读取 PDF 文件内容:param file_path: PDF 文件路径:return: 文件内容文本"""try:text ext…...
数据结构☞泛型
一.基础定义与应用方向 1.定义: 一般的类和方法,只能使用具体的类型 : 要么是基本类型,要么是自定义的类。如果要编写可以 应用于多种类型 的代码,这种刻板的限制对代码的束缚就会很大。----- 来源《 Java 编程思想》对泛型的介…...
MFC学习笔记-1
一、编辑框和按钮 //.h文件private:CString str;//给窗口类加了一个变量(定义一个成员变量),关联到IDC_EDIT1中(要在实现中关联,源文件文件夹中)CString str2;//接收button2,和IDC_EDIT2绑定 p…...
html中rel、href、src、url的区别
1.url url(统一资源定位符):是对可以从互联网上得到的资源的位置和访问方法的一种简洁的表示,是互联网上标准资源的地址。 2.href href:Hypertext Reference的缩写。 意思是超文本引用。 3.rel rel:relatio…...
hot100-二叉树
二叉树 二叉树递归 相当于这个的顺序来回调换 class Solution {private List<Integer> res new ArrayList<>();public List<Integer> inorderTraversal(TreeNode root) {if(root null)return res;inorderTraversal(root.left);res.add(root.val);inorde…...
嵌入式项目:STM32刷卡指纹智能门禁系统
本文详细介绍基于STM32的刷卡指纹智能门禁系统。 获取资料/指导答疑/技术交流/选题/帮助,请点链接: https://gitee.com/zengzhaorong/share_contact/blob/master/stm32.txt 1 系统功能 1.1 功能概述 本系统由STM32硬件端(下位机)…...
短剧小程序系统源码
短剧小程序系统源码 今天我要向大家介绍的是最新作品——短剧小程序系统源码。这不仅仅是一款简单的播放工具,它背后蕴含的强大功能能够帮助你的短剧业务实现质的飞跃! 为什么说这款源码很厉害? 首先,在当今竞争激烈的市场环境…...
鸿蒙5.0实战案例:基于measure实现的文本测量
往期推文全新看点(文中附带全新鸿蒙5.0全栈学习笔录) ✏️ 鸿蒙(HarmonyOS)北向开发知识点记录~ ✏️ 鸿蒙(OpenHarmony)南向开发保姆级知识点汇总~ ✏️ 鸿蒙应用开发与鸿蒙系统开发哪个更有前景&#…...
C#中级教程(2)——走进 C# 面向对象编程:从基础到进阶的深度探索
一、为什么选择面向对象编程 在软件开发的演进过程中,随着程序规模和复杂度的不断增加,传统的编程方式逐渐暴露出局限性。面向对象编程应运而生,它就像是一位智慧的组织者,将程序中的功能进行模块化划分。每个模块各司其职&#x…...
基于SpringBoot的“流浪动物救助系统”的设计与实现(源码+数据库+文档+PPT)
基于SpringBoot的“流浪动物救助系统”的设计与实现(源码数据库文档PPT) 开发语言:Java 数据库:MySQL 技术:SpringBoot 工具:IDEA/Ecilpse、Navicat、Maven 系统展示 系统功能结构图 局部E-R图 系统首页界面 系统…...
基于WebRTC与AI大模型接入EasyRTC:打造轻量级、高实时、强互动的嵌入式音视频解决方案
随着物联网和嵌入式技术的快速发展,嵌入式设备对实时音视频通信的需求日益增长。然而,传统的音视频解决方案往往存在体积庞大、实时性差、互动体验不佳等问题,难以满足嵌入式设备的资源限制和应用场景需求。 针对以上痛点,本文将介…...
Windows - 通过ssh打开带有图形界面的程序 - 一种通过计划任务的曲折实现方式
Windows(奇思妙想) - 通过ssh打开带有图形界面的程序 - 一种通过计划任务的曲折实现方式 前言 Windows启用OpenSSH客户端后就可以通过SSH的方式访问Windows了。但是通过SSH启动的程序: 无法显示图形界面会随着SSH进程的结束而结束 于是想到了一种通过执行“计划…...
应用层的协议-http/https的状态码
1xx:表示临时响应,需要操作者继续操作 2xx:成功,操作被成功接受并处理 3xx:一般是重定向问题 4xx:客户端的问题 5xx:服务端的问题 1xx: 100: 表示服务器收到客户端的第一部分请…...
前端Sass面试题及参考答案
目录 什么是 Sass? Sass 和 CSS 的主要区别是什么? Sass 中如何处理列表? Sass 中如何处理映射(map)? Sass 中如何使用函数? Sass 中如何使用内置函数? Sass 中如何设置默认值? Sass 中的 @function 和 @mixin 有什么区别? Sass 中如何实现模块化? Sass 中…...
python采集京东商品详情API接口系列,json数据示例返回
在Python中采集京东商品详情API接口的数据,你需要与京东开放平台(现已更名为京东联盟开放平台)进行交互。京东开放平台提供了多种API接口,用于访问京东的商品数据、用户数据等。然而,需要注意的是,京东对于…...
RT-Thread+STM32L475VET6——USB鼠标模拟
文章目录 前言一、板载资源二、具体步骤1.配置icm20608传感器2.打开CubeMX进行USB配置3. 配置USB3.1 打开USB驱动3.2 声明USB3.3 剪切stm32xxxx_hal_msp.c中的void HAL_PCD_MspInit(PCD_HandleTypeDef* hpcd)和void HAL_PCD_MspDeInit(PCD_HandleTypeDef* hpcd)函数至board.c3.…...
计算机毕业设计SpringBoot+Vue.js母婴商城(源码+LW文档+PPT+讲解+开题报告)
温馨提示:文末有 CSDN 平台官方提供的学长联系方式的名片! 温馨提示:文末有 CSDN 平台官方提供的学长联系方式的名片! 温馨提示:文末有 CSDN 平台官方提供的学长联系方式的名片! 作者简介:Java领…...
Teigha(ODA<Open Design Alliance>_开放设计联盟)——cad c# 二次开发
需将dll库文件与exe文件放同一路径下,运行exe即可执行。 using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.IO; using System.Linq; using System.Text; using System.Thread…...
Java 中 HTTP 协议版本使用情况剖析
Java 中 HTTP 协议版本使用情况剖析 一、HTTP/1.1 与 HTTP/2 概述 (一)HTTP/1.1 HTTP/1.1 是广泛应用且成熟的 HTTP 协议版本,它在互联网发展历程中扮演了重要角色。其特点主要包括: 连接方式:默认采用短连接,即每次请求都要建立新的 TCP 连接,请求完成后断开。不过也…...
Zama fhEVM应用:摩根大通旗下 Kinexys 发布概念验证
1. 引言 Zama 全同态加密 (FHE) 技术在摩根大通的 Kinexys(以前称为 Onyx)中成功进行了概念验证。该概念验证是“EPIC 项目:通过链上企业隐私、身份和可组合性推动代币化金融”的一部分,在 Kinexys 数字资产沙盒(以前…...
idea 部署 AJ-Report 启动的注意事项
AJ-Report 入门参考: AJ-Report 初学(入门教程) gitee 下载:https://gitee.com/anji-plus/report/releases 根据上面提供的 gitee 下载链接,点击直接下载 最上面的就是最新版本的,旧版本往下拉就可以找到,有三个下载…...
