【2024】Datawhale AI夏令营 Task3笔记——Baseline2部分代码解读及初步上分思路

Task3 逻辑推理方向 #datawhale#LoRA 本次博客主要是2024年第三期的第三次任务,涉及了大模型微调等相关内容。经过先前的两次任务,我们已经学会了如何去调用大模型,但是对于如何提升大模型,我们可能只能靠更加精准的提问,这种靠更加精准的提问来获得更好的效果的方式我们称为提示词工程很全面的提示工程指南(包含大量示例!这次任务的大模型微调采用LoRA微调。LoRA(Low-Rank Adaptation)微调是一种高效的模型微调技术,特别适用于大型预训练语言模型的适应性调整。 阅读详情

【2024】Datawhale AI夏令营 Task3笔记——Baseline2部分代码解读及初步上分思路

本文对可完成赛事“逻辑推理赛道:复杂推理能力评估”初赛的Baseline2部分关键代码进行详细解读,介绍Baseline2涉及的关键技术和初步上分思路。

Baseline2代码由Datawhale AI夏令营提供,核心内容是将大语言模型部署至本地,并在此基础上使用Lora技术微调大语言模型。另外,为了加快本地推理的速度,本次任务代码使用vllm技术加速大语言模型推理(Task1和Task2方法的核心是直接调用外部大语言模型的API进行推理)。

🔴注意:

1、在进行代码解读时,为了简化代码、不影响代码理解,本文对baseline2代码顺序进行了一定调整。

2、运行Baseline2代码有部分注意点,分别是:尽量使用cuda12.1的设备(一)、根据选择的微调数据集修改答案提取函数(五)。

3、使用ana.json作为微调数据集,效果由于使用an.json。关于这两个文件的区别详见后文。

4、本文另外尝试在不使用vLLM的情况下进行推理,加速前单个问题的推理速度约为5分钟/题,加速后单个问题的推理速度约为0.35分钟/题,加速效果显著。

5、本文另外发现了vLLM服务启动后GPU占用较高的问题,vLLM服务启动后整体推理速度也仍有待提高(目前推理所有问题在V100-32G上需要7小时。改进方式可参考使用vLLM加速大语言模型推理)。

✨Baseline2涉及的关键技术

1、Lora微调(上分)

LoRA(Low-Rank Adaptation)微调是一种高效的模型微调技术,特别适用于大型预训练语言模型的适应性调整。LoRA的核心思想是通过引入低秩矩阵来调整模型的权重,从而在不显著增加模型参数数量的情况下,实现对模型的微调。

LoRA优势:

  • 可以针对不同的下游任务构建小型 LoRA 模块,从而在共享预训练模型参数基础上有效地切换下游任务。

  • LoRA 使用自适应优化器(Adaptive Optimizer),不需要计算梯度或维护大多数参数的优化器状态,训练更有效、硬件门槛更低。

  • LoRA 使用简单的线性设计,在部署时将可训练矩阵与冻结权重合并,不存在推理延迟。

  • LoRA 与其他方法正交,可以组合。

实际使用方法可参考本文“Baseline2部分代码解读”中的第二、三章节。

2、vLLM(加速推理)

vLLM(Virtual Large Language Model)是一个由伯克利大学LMSYS组织开源的大规模语言模型高速推理框架,设计目标是在实时应用场景中大幅提升语言模型服务的吞吐量和内存使用效率。vLLM的特点包括易于使用、与Hugging Face等流行工具无缝集成以及高效的性能。

实际使用方法可参考本文“Baseline2部分代码解读”中的第四章节。

3、多路投票(上分)

Datawhale教程中对于多路投票/多路召回的解释:

所谓的“多路召回策略”就是指采用不同的策略、特征或者简单模型,分别召回一部分候选集,然后再把这些候选集混合在一起后供后续排序模型使用的策略。

在实践中,“不同的策略、特征或者简单模型”就是指不同次调用微调后的大语言模型;“召回一部分候选集”就是指分别得到不同的回答/答案选项(可能不同,也可能相同),“把这些候选集混合在一起后供后续排序模型使用”就是指统计这些回答中每个出现过的回答的次数,按照出现次数进行排序,最终决定出现次数最多的回答即为答案。

实际使用方法可参考本文“Baseline2部分代码解读”中的第五章节。

👩‍💻Baseline2部分代码解读

Baseline2的代码有一部分与Baseline1的代码相似,在此文章只记录Baseline2中新出现的技术与代码内容。

一、环境配置与数据文件准备

1.1 环境配置

Datawhale教程建议使用的环境配置如下:

unbuntu22.04
cuda12.1.0
py3.10
torch2.1.2

我认为cuda的版本比较关键,在自己配置环境的过程中,一开始先选择了一台pytorch版本也是2.1.2但cuda版本是11.8的GPU进行配置,最后到了启动vllm的环节时会无法启动(报错信息可见本章的补充部分)。

其余依赖的python第三方包:

pip install modelscope==1.9.5
pip install "transformers>=4.39.0"
pip install streamlit==1.24.0
pip install sentencepiece==0.1.99
pip install transformers_stream_generator==0.0.4
pip install datasets==2.18.0
pip install peft==0.10.0
pip install openai==1.17.1
pip install tqdm==4.64.1
pip install transformers==4.39.3
python -m pip install setuptools==69.5.1
pip install vllm==0.4.0.post1
pip install nest-asyncio
pip install accelerate
pip install tf-keras
1.2 数据文件准备

数据文件用于微调大语言模型,使之推理能力增强。本次任务使用赛事官方提供的训练集文件round1_train_data.jsonl作为微调数据。Datawhale教程对round1_train_data.jsonl文件中的数据进行了两种处理,分别得到an.json和ana.json文件。本次任务将使用an.json或ana.json文件作为微调数据集。

  • 处理一:将每个问题中的子问题和答案构建成问答对,得到的结果示例:

     {
            "instruction": "你是一个逻辑推理专家,擅长解决逻辑推理问题。以下是一个逻辑推理的题目,形式为单项选择题。所有的问题都是(close-world assumption)闭世界假设,即未观测事实都为假。请逐步分析问题并在最后一行输出答案,最后一行的格式为\"答案是:A\"。题目如下:\n\n### 题目:\n假设您需要构建一个二叉搜索树,其中每个节点或者是一个空的节点(称为\"空节点\"),或者是一个包含一个整数值和两个子树的节点(称为\"数值节点\")。以下是构建这棵树的规则:\n\n1. 树中不存在重复的元素。\n2. 对于每个数值节点,其左子树的所有值都小于该节点的值,其右子树的所有值都大于该节点的值。\n3. 插入一个新值到一个\"空节点\"时,该\"空节点\"会被一个包含新值的新的数值节点取代。\n4. 插入一个已存在的数值将不会改变树。\n\n请基于以上规则,回答以下选择题:\n\n### 问题:\n选择题 1:\n给定一个空的二叉搜索树,插入下列数字: [5, 9, 2, 10, 11, 3],下面哪个选项正确描述了结果树的结构?\nA. tree(5, tree(2, tree(3, nil, nil), nil), tree(9, tree(10, nil, nil), tree(11, nil, nil)))\nB. tree(5, tree(2, nil, tree(3, nil, nil)), tree(9, nil, tree(10, nil, tree(11, nil, nil))))\nC. tree(5, tree(3, tree(2, nil, nil), nil), tree(9, nil, tree(10, tree(11, nil, nil), nil)))\nD. tree(5, nil, tree(2, nil, tree(3, nil, nil)), tree(9, tree(11, nil, nil), tree(10, nil, nil)))\n",
            "input": "",
            "output": "B"
    },
    

    整个文件处理完成后,数据存储在an.json文件中。

  • 处理二:通过qwen2-72b模型在train数据集上跑得正确结果并分析,将每个问题中的子问题、分析及答案构建成问答对,得到的结果示例:

    {
            "instruction": "你是一个逻辑推理专家,擅长解决逻辑推理问题。以下是一个逻辑推理的题目,形式为单项选择题。所有的问题都是(close-world assumption)闭世界假设,即未观测事实都为假。请逐步分析问题并在最后一行输出答案,最后一行的格式为\"答案是:A\"。题目如下:\n\n### 题目:\n假设您需要构建一个二叉搜索树,其中每个节点或者是一个空的节点(称为\"空节点\"),或者是一个包含一个整数值和两个子树的节点(称为\"数值节点\")。以下是构建这棵树的规则:\n\n1. 树中不存在重复的元素。\n2. 对于每个数值节点,其左子树的所有值都小于该节点的值,其右子树的所有值都大于该节点的值。\n3. 插入一个新值到一个\"空节点\"时,该\"空节点\"会被一个包含新值的新的数值节点取代。\n4. 插入一个已存在的数值将不会改变树。\n\n请基于以上规则,回答以下选择题:\n\n### 问题:\n选择题 1:\n给定一个空的二叉搜索树,插入下列数字: [5, 9, 2, 10, 11, 3],下面哪个选项正确描述了结果树的结构?\nA. tree(5, tree(2, tree(3, nil, nil), nil), tree(9, tree(10, nil, nil), tree(11, nil, nil)))\nB. tree(5, tree(2, nil, tree(3, nil, nil)), tree(9, nil, tree(10, nil, tree(11, nil, nil))))\nC. tree(5, tree(3, tree(2, nil, nil), nil), tree(9, nil, tree(10, tree(11, nil, nil), nil)))\nD. tree(5, nil, tree(2, nil, tree(3, nil, nil)), tree(9, tree(11, nil, nil), tree(10, nil, nil)))\n",
            "input": "",
            "output": "根据题目中的规则,我们依次插入数字 [5, 9, 2, 10, 11, 3] 来构建二叉搜索树。\n\n首先插入数字 5,树为:\n```\ntree(5, nil, nil)\n```\n\n然后插入数字 9,由于 9 > 5,因此插入到右子树:\n```\ntree(5, nil, tree(9, nil, nil))\n```\n\n接下来插入数字 2,由于 2 < 5,因此插入到左子树:\n```\ntree(5, tree(2, nil, nil), tree(9, nil, nil))\n```\n\n再插入数字 10,由于 10 > 5 并且 10 > 9,所以插入到 9 的右子树:\n```\ntree(5, tree(2, nil, nil), tree(9, nil, tree(10, nil, nil)))\n```\n\n继续插入数字 11,由于 11 > 5 并且 11 > 9 和 11 > 10,所以插入到 10 的右子树:\n```\ntree(5, tree(2, nil, nil), tree(9, nil, tree(10, nil, tree(11, nil, nil))))\n```\n\n最后插入数字 3,由于 3 < 5 但是 3 > 2,所以插入到 2 的右子树:\n```\ntree(5, tree(2, nil, tree(3, nil, nil)), tree(9, nil, tree(10, nil, tree(11, nil, nil))))\n```\n\n比较这个最终结果与给定的选项,可以看到选项 B 正确地描述了结果树的结构。\n\n答案是:B"
        },
    

    整个文件处理完成后,数据存储在ana.json文件中。

数据文件由Datawhale处理后提供,可在链接Datawhale2024年第二届世界科学智能大赛逻辑推理赛道:复杂推理能力评估baseline2中查看(链接中除了数据文件还有相关的代码,本文分析的代码源自此链接中代码)。
在这里插入图片描述

可以使用如下命令将数据文件下载到服务器中:

git clone https://www.modelscope.cn/datasets/Datawhale/DW2024_Complex_reasoning_ability_assessment_qwen2-7b-lora.git

这里多说一句,微调数据集的质量对微调后模型推理的性能有较大的影响。根据我自己的实验结果,使用an.json中数据进行微调,在比赛中的分数低于使用ana.json中数据进行微调。

1.3 大语言模型文件下载

下载魔搭社区的Qwen2-7B-Instruct模型文件。将模型下载到服务器后,我们可以直接使用模型文件推理(不考虑推理速度问题的话),不必再调用外部的大语言模型API。

使用如下命令进行下载:

import torch
from modelscope import snapshot_download, AutoModel, AutoTokenizer
import os
model_dir = snapshot_download('qwen/Qwen2-7B-Instruct', cache_dir='./', revision='master')
(😢补充)使用cuda11.8的GPU配置服务,无法启动vllm

在安装完需要使用的python第三方包、微调完成后,在启动vllm时出现如下多则报错信息,最终未能解决,此处仅将尝试解决报错的过程进行记录。

报错信息1

ImportError: libcudart.so.12: cannot open shared object file: No such file or directory

vllm的github仓库有人就这一问题进行提问:ImportError: libcudart.so.12,得到的回复是:
在这里插入图片描述

这个回复的意思是,要么使用cuda 12.1的环境,要么安装适合cuda 11.8的vllm。

在xformers的github仓库中,安装cuda 11.8对应的vllm使用的指令如下,前提是需要已有pytorch 2.3.1,这与教程推荐的pytorch版本又有出入(但是我忘记当时自己是不是没看到这个前提、仍然在pytorch=2.1.2的情况下执行了这条命令,安装成功了,但是后续仍然有新的报错)。

# cuda 11.8 version
pip3 install -U xformers --index-url https://download.pytorch.org/whl/cu118

报错信息2

xFormers can't load C++/CUDA extensions.

针对这则报错信息,Stackflow上的一位网友建议检查xFormers与pytorch的版本是否对应。可在此网站上查看xFormers和pytorch的版本对应信息:https://anaconda.org/xformers/xformers/files。在这个网页上,看到与pytorch2.1.2、cuda11.8对应的xformers包是linux-64/xformers-0.0.23.post1-py310_cu11.8.0_pyt2.1.2.tar.bz2,使用wget命令将这个包下载到服务器后安装:

wget https://anaconda.org/xformers/xformers/0.0.23.post1/download/linux-64/xformers-0.0.23.post1-py310_cu11.8.0_pyt2.1.2.tar.bz2
pip install xformers-0.0.23.post1-py310_cu11.8.0_pyt2.1.2.tar.bz2

参考:BUG:C++/CUDA extensions. xFormers was built for: PyTorch 2.1.2+cu121 with CUDA 1201 (you have 2.1.0+

报错信息3

RuntimeError: CUDA error: no kernel image is available for execution on the device

文章《解决CUDA error: no kernel image is available for execution on the device》的作者认为此报错由GPU算力与pytorch依赖的CUDA的算力不匹配造成,建议重新安装pytorch,故使用如下命令重新安装pytorch:

pip install --force-reinstall torch==2.1.2 torchvision==0.16.2 torchaudio==2.1.2 --index-url https://download.pytorch.org/whl/cu118

然而这并没有帮助我解决这一报错。由于时间已晚,不再深入debug,使用新的cuda版本为2.1.2的GPU服务器配置环境。

二、Lora微调

2.1 加载微调所需环境及数据文件
from datasets import Dataset
import pandas as pd
from transformers import AutoTokenizer, AutoModelForCausalLM, DataCollatorForSeq2Seq, TrainingArguments, Trainer, GenerationConfig
import torch
from peft import LoraConfig, TaskType, get_peft_model

df = pd.read_json('an.json') # 这里先使用an.json文件
ds = Dataset.from_pandas(df) # 将JSON文件转换为CSV文件
2.2 加载Tokenizer及预训练模型
pretrained_model_path = './qwen/Qwen2-7B-Instruct' # 具体路径需按实际情况修改

# 加载Tokenizer
tokenizer = AutoTokenizer.from_pretrained(pretrained_model_path, use_fast=False, trust_remote_code=True)

# 加载预训练模型
model = AutoModelForCausalLM.from_pretrained(pretrained_model_path, device_map="auto",torch_dtype=torch.bfloat16)
2.3 定义数据格式化函数并检查

使用Lora微调大语言模型时,训练数据需要经过格式化、编码(即tokenize操作),然后再输入模型进行微调训练。这个过程一般包括:

  • 将输入文本编码为 input_ids;
  • 将输出文本编码为 labels。

编码之后的结果都是多维的向量。

定义预处理函数process_func,对每一个样本的输入、输出文本进行编码,返回编码后的字典。

def process_func(example):
    MAX_LENGTH = 1800 # Llama分词器会将一个中文字切分为多个token,因此需要放开一些最大长度,保证数据的完整性
    input_ids, attention_mask, labels = [], [], []
    # 对输入部分进行分词
    instruction = tokenizer(f"<|im_start|>system\n你是一个逻辑推理专家,擅长解决逻辑推理问题。<|im_end|>\n<|im_start|>user\n{example['instruction'] + example['input']}<|im_end|>\n<|im_start|>assistant\n", add_special_tokens=False) # add_special_tokens=False表示不在开头加special_tokens
    # 对输出部分进行分词
    response = tokenizer(f"{example['output']}", add_special_tokens=False)
    
    input_ids = instruction["input_ids"] + response["input_ids"] + [tokenizer.pad_token_id] # input_ids是指token在词典中对应的序号
    attention_mask = instruction["attention_mask"] + response["attention_mask"] + [1] # 因为eos token也要关注,所以 补充为1
    labels = [-100] * len(instruction["input_ids"]) + response["input_ids"] + [tokenizer.pad_token_id] # 对于instruction部分的 token,用-100填充(通常用于忽略计算损失)response部分的token则直接用其token IDs,并在末尾添加一个pad_token_id。
    
    if len(input_ids) > MAX_LENGTH: # 做截断,只保留最靠后的部分
        input_ids = input_ids[:MAX_LENGTH]
        attention_mask = attention_mask[:MAX_LENGTH]
        labels = labels[:MAX_LENGTH]
    return {
        "input_ids": input_ids,
        "attention_mask": attention_mask,
        "labels": labels
    }

调用process_func函数处理数据。

tokenized_id = ds.map(process_func, remove_columns=ds.column_names)

调用process_func函数处理数据后,得到的数据集结构如下:

Dataset({
    features: ['input_ids', 'attention_mask', 'labels'],
    num_rows: 1204
})

进行decode数据检查,选择数据集中第一个问答对,检查其输入文本的解码情况;选择数据集中第二个问答对,检查其输出文本的解码情况。

tokenizer.decode(tokenized_id[0]['input_ids']) # 检查输入文本的解码结果
tokenizer.decode(list(filter(lambda x: x != -100, tokenized_id[1]["labels"]))) # 检查输出文本的解码结果

输入文本的解码结果如下:

'<|im_start|>system\n你是一个逻辑推理专家,擅长解决逻辑推理问题。<|im_end|>\n<|im_start|>user\n你是一个逻辑推理专家,擅长解决逻辑推理问题。以下是一个逻辑推理的题目,形式为单项选择题。所有的问题都是(close-world assumption)闭世界假设,即未观测事实都为假。请逐步分析问题并在最后一行输出答案,最后一行的格式为"答案是:A"。题目如下:\n\n### 题目:\n假设您需要构建一个二叉搜索树,其中每个节点或者是一个空的节点(称为"空节点"),或者是一个包含一个整数值和两个子树的节点(称为"数值节点")。以下是构建这棵树的规则:\n\n1. 树中不存在重复的元素。\n2. 对于每个数值节点,其左子树的所有值都小于该节点的值,其右子树的所有值都大于该节点的值。\n3. 插入一个新值到一个"空节点"时,该"空节点"会被一个包含新值的新的数值节点取代。\n4. 插入一个已存在的数值将不会改变树。\n\n请基于以上规则,回答以下选择题:\n\n### 问题:\n选择题 1:\n给定一个空的二叉搜索树,插入下列数字: [5, 9, 2, 10, 11, 3],下面哪个选项正确描述了结果树的结构?\nA. tree(5, tree(2, tree(3, nil, nil), nil), tree(9, tree(10, nil, nil), tree(11, nil, nil)))\nB. tree(5, tree(2, nil, tree(3, nil, nil)), tree(9, nil, tree(10, nil, tree(11, nil, nil))))\nC. tree(5, tree(3, tree(2, nil, nil), nil), tree(9, nil, tree(10, tree(11, nil, nil), nil)))\nD. tree(5, nil, tree(2, nil, tree(3, nil, nil)), tree(9, tree(11, nil, nil), tree(10, nil, nil)))\n<|im_end|>\n<|im_start|>assistant\n根据题目中的规则,我们依次插入数字 [5, 9, 2, 10, 11, 3] 来构建二叉搜索树。\n\n首先插入数字 5,树为:\n```\ntree(5, nil, nil)\n```\n\n然后插入数字 9,由于 9 > 5,因此插入到右子树:\n```\ntree(5, nil, tree(9, nil, nil))\n```\n\n接下来插入数字 2,由于 2 < 5,因此插入到左子树:\n```\ntree(5, tree(2, nil, nil), tree(9, nil, nil))\n```\n\n再插入数字 10,由于 10 > 5 并且 10 > 9,所以插入到 9 的右子树:\n```\ntree(5, tree(2, nil, nil), tree(9, nil, tree(10, nil, nil)))\n```\n\n继续插入数字 11,由于 11 > 5 并且 11 > 9 和 11 > 10,所以插入到 10 的右子树:\n```\ntree(5, tree(2, nil, nil), tree(9, nil, tree(10, nil, tree(11, nil, nil))))\n```\n\n最后插入数字 3,由于 3 < 5 但是 3 > 2,所以插入到 2 的右子树:\n```\ntree(5, tree(2, nil, tree(3, nil, nil)), tree(9, nil, tree(10, nil, tree(11, nil, nil))))\n```\n\n比较这个最终结果与给定的选项,可以看到选项 B 正确地描述了结果树的结构。\n\n答案是:B<|endoftext|>'

输出文本的解码结果如下:

'根据题目描述的函数逻辑,我们来逐步分析输入列表 [3,7,5,9] 的处理过程:\n\n1. 列表非空,开始处理第一个元素3。\n2. 3是奇数,根据规则应被删除。\n3. 处理下一个元素7,同样为奇数,删除。\n4. 继续处理5,同样是奇数,删除。\n5. 最后处理9,依然是奇数,删除。\n\n整个过程中,由于输入列表中的所有元素都是奇数,根据规则它们都将被删除,不会有任何元素保留在结果列表中。\n\n因此,对于输入列表 [3,7,5,9],函数返回的结果是空列表。\n\n答案是:D. []<|endoftext|>'
2.4 设置Lora微调相关参数(LoraConfig)
config = LoraConfig(
    task_type=TaskType.CAUSAL_LM, # 模型类型
    target_modules=["q_proj", "k_proj", "v_proj", "o_proj", "gate_proj", "up_proj", "down_proj"], # 需要训练的模型层的名字,主要是attention部分的层,不同的模型对应的层的名字不同,可以传入数组,也可以字符串,也可以正则表达式
    inference_mode=False, # 训练模式
    r=8, # Lora 秩,具体可以看Lora原理
    lora_alpha=32, # Lora alaph,具体作用参见 Lora 原理
    # Lora的缩放是lora_alpha/r, 在这个LoraConfig中缩放就是4倍
    lora_dropout=0.1# Dropout 比例
)
2.5 创建微调模型(PeftModel)

PEFT(Parameter-Efficient Fine-Tuning,参数高效微调),是一个用于在不微调所有模型参数的情况下,高效地将预训练语言模型(PLM)适应到各种下游应用的库。

每个PEFT方法由一个PeftConfig类来定义,该类存储了用于构建PeftModel的所有重要参数。由于本文使用LoRA微调,因此实际创建的是LoraConfig类。随后,使用get_peft_model()函数创建PeftModel,需要基本模型(可从Transformers库加载)和 LoraConfig(其中包含如何配置模型以使用 LoRA 进行训练的参数)。

model = get_peft_model(model, config)

打印可训练参数的规模:

model.print_trainable_parameters()
# trainable params: 20,185,088 || all params: 7,635,801,600 || trainable%: 0.26434798934534914
2.6 设置训练器相关参数(TrainingArguments)
args = TrainingArguments(
    output_dir="./output/Qwen2_instruct_lora", # 微调完成后,微调模型的输出路径(仅包括微调层的部分,并不是整个模型。如果需要得到微调后的整个模型,需要进行模型合并,下文会继续提到)
    per_device_train_batch_size=1, # batch_size
    gradient_accumulation_steps=4, # 梯度累加,如果显存比较小,可以把batch_size设置小一点,梯度累加增大一些
    logging_steps=10, # 多少步,输出一次log
    num_train_epochs=1, # epoch
    save_steps=100, # 为了快速演示,这里设置10,建议你设置成100
    learning_rate=1e-4,
    save_on_each_node=True,
    gradient_checkpointing=True # 梯度检查,这个一旦开启,模型就必须执行
)

开启梯度检查点时,要执行此方法:

model.enable_input_require_grads()

关于梯度检查点(gradient_checkpoint),这是一种用于节省训练时显存的同时不拖慢训练的技术。更多介绍可参考文章:大模型高效训练基础知识:梯度检查点(Gradient Checkpointing)。

2.7 定义训练器
trainer = Trainer(
    model=model,
    args=args,
    train_dataset=tokenized_id,
    data_collator=DataCollatorForSeq2Seq(tokenizer=tokenizer, padding=True),
)
2.8 开启训练
torch.backends.cuda.enable_mem_efficient_sdp(False)
trainer.train()

多说一句:在魔搭平台的notebook(A10,8核cpu)运行训练过程耗时约10min,在我自己配置的V100-32GB(6核cpu)服务器上运行训练过程则耗时约60min,差异较大,暂时未弄清差异来源。

三、微调模型测试及模型合并存储

3.1 调用微调后模型进行测试
from transformers import AutoModelForCausalLM, AutoTokenizer
import torch
from peft import PeftModel

mode_path = './qwen/Qwen2-7B-Instruct' # 预训练大语言模型的路径
lora_path = './output/Qwen2_instruct_lora/checkpoint-100' # 微调完成后,微调模型的输出路径

# 加载tokenizer
tokenizer = AutoTokenizer.from_pretrained(mode_path, trust_remote_code=True)
# 加载模型
model = AutoModelForCausalLM.from_pretrained(mode_path, device_map="auto",torch_dtype=torch.float16, trust_remote_code=True).eval()
# 加载lora权重
model = PeftModel.from_pretrained(model, model_id=lora_path)

# 测试问题,这里写好prompt
prompt = '''你是一个逻辑推理专家,擅长解决逻辑推理问题。以下是一个逻辑推理的题目,形式为单项选择题。所有的问题都是(close-world assumption)闭世界假设,即未观测事实都为假。请逐步分析问题并在最后一行输出答案,最后一行的格式为"答案是:A"。题目如下:\n\n### 题目:\n假设您需要构建一个二叉搜索树,其中每个节点或者是一个空的节点(称为"空节点"),或者是一个包含一个整数值和两个子树的节点(称为"数值节点")。以下是构建这棵树的规则:\n\n1. 树中不存在重复的元素。\n2. 对于每个数值节点,其左子树的所有值都小于该节点的值,其右子树的所有值都大于该节点的值。\n3. 插入一个新值到一个"空节点"时,该"空节点"会被一个包含新值的新的数值节点取代。\n4. 插入一个已存在的数值将不会改变树。\n\n请基于以上规则,回答以下选择题:\n\n### 问题:\n选择题 1:\n给定一个空的二叉搜索树,插入下列数字: [5, 9, 2, 10, 11, 3],下面哪个选项正确描述了结果树的结构?\nA. tree(5, tree(2, tree(3, nil, nil), nil), tree(9, tree(10, nil, nil), tree(11, nil, nil)))\nB. tree(5, tree(2, nil, tree(3, nil, nil)), tree(9, nil, tree(10, nil, tree(11, nil, nil))))\nC. tree(5, tree(3, tree(2, nil, nil), nil), tree(9, nil, tree(10, tree(11, nil, nil), nil)))\nD. tree(5, nil, tree(2, nil, tree(3, nil, nil)), tree(9, tree(11, nil, nil), tree(10, nil, nil)))'''

inputs = tokenizer.apply_chat_template(
    [{"role": "user", "content": "你是一个逻辑推理专家,擅长解决逻辑推理问题。"},{"role": "user", "content": prompt}],
    add_generation_prompt=True,
    tokenize=True,
    return_tensors="pt",
    return_dict=True
).to('cuda')

gen_kwargs = {"max_length": 2500, "do_sample": True, "top_k": 1}

# 使用generate函数生成推理结果
with torch.no_grad():
    outputs = model.generate(**inputs, **gen_kwargs)
    outputs = outputs[:, inputs['input_ids'].shape[1]:]
    print(tokenizer.decode(outputs[0], skip_special_tokens=True))

输出:

……(一些日志信息)
2024-07-28 00:15:34.902958: W tensorflow/compiler/tf2tensorrt/utils/py_utils.cc:38] TF-TRT Warning: Could not find TensorRT
Special tokens have been added in the vocabulary, make sure the associated word embeddings are fine-tuned or trained.
Loading checkpoint shards: 100%|██████████| 4/4 [00:40<00:00, 10.25s/it]
B
3.2 模型合并存储

将lora微调后的模型融入到原模型中。

new_model_directory = "./merged_model_an" # 合并后模型的存储路径
merged_model = model.merge_and_unload()
# 将权重保存为safetensors格式的权重, 且每个权重文件最大不超过2GB(2048MB)
merged_model.save_pretrained(new_model_directory, max_shard_size="2048MB", safe_serialization=True)

预训练大语言模型的tokenizer.json文件也需要一并放入合并后模型的路径(下方命令在终端执行,路径按实际情况修改):

cp ./qwen/Qwen2-7B-Instruct/tokenizer.json ./merged_model_an/

四、启动vllm服务加速推理

由于在环境配置时已经安装vllm第三方包,直接在终端输入如下命令即可启动vllm服务。执行后通过vllm的类openai接口便可成功将微调后的模型部署到本地8000端口。

VLLM_USE_MODELSCOPE=False python -m vllm.entrypoints.openai.api_server --model ./merged_model --served-model-name Qwen2-7B-Instruct-lora --max-model-len=4096 --gpu_memory_utilization=0.6
  • VLLM_USE_MODELSCOPE=False表示不使用modelscope上面的模型,使用本地的模型。添加这个参数的设置可以避免vllm错误识别模型路径或名称、远程下载模型。

  • --model ./merged_model表示需要调用./merged_model中的模型,路径需根据实际情况修改

  • --served-model-name Qwen2-7B-Instruct-lora表示启动的模型名称为Qwen2-7B-Instruct-lora

  • --gpu_memory_utilization=0.6(可选,设置这个参数是为了避免占用过多GPU内存,默认情况下是0.9)

vllm服务启动成功的输出:
在这里插入图片描述

(😢补充)vLLM服务启动后GPU占用较高

发现启动vLLM服务后GPU占用较高,以V100-32GB显卡为例,GPU内存使用达到17799MiB / 32768MiB(GPU利用率则为0%)。开始推理后,GPU内存使用达到17971MiB / 32768MiB(这是在设置了gpu_memory_utilization=0.6的情况下,如果不设置这个参数,会几乎拉满),GPU利用率则为92%左右。

讨论这个问题的文章有:【实践】VLLM显存暴增 | 多卡推理 | 批量推理

推理速度的瓶颈应该还是在于显卡显存的大小,而非GPU内存使用。

五、开始推理(多路投票)

正式进行推理这部分的代码与Baseline1中的代码相似,这里只记录几个不同的地方。

5.1 MODEL_NAME设置

由于在启动vllm服务时,定义了served-model-name是Qwen2-7B-Instruct-lora,所以在代码中,MODEL_NAME的值也需要是Qwen2-7B-Instruct-lora,即:

MODEL_NAME = 'Qwen2-7B-Instruct-lora'
5.2 类openAI接口

改写Baseline1中的call_qwen_api代码,改为调用本地类openai的qwen微调模型接口。

def call_qwen_api(MODEL_NAME, query):
    # 这里采用dashscope的api调用模型推理,通过http传输的json封装返回结果 
    client = OpenAI(
        base_url="http://localhost:8000/v1",
        api_key="sk-xxx", # 随便填写,只是为了通过接口参数校验
    )
    completion = client.chat.completions.create(
      model=MODEL_NAME,
      messages=[
                # {'role':'system','content':'你是一个解决推理任务的专家,你需要分析出问题中的每个实体以及响应关系。然后根据问题一步步推理出结果。并且给出正确的结论。'},

        {"role": "user", "content": query}
      ]
    )
    return completion.choices[0].message.content
5.3 多路投票

1、设计投票函数

定义函数most_frequent_chat,通过三次结果推理,将选择答案最多的结果作为最终结果返回。

def most_frequent_char(char1, char2, char3): # 这里设置三个char作为参数是因为后面会三次调用LLM,每个char对应一个LLM调用的回答
    # 创建一个字典来存储每个字符的出现次数
    frequency = {char1: 0, char2: 0, char3: 0}
    
    # 增加每个字符的出现次数
    frequency[char1] += 1
    frequency[char2] += 1
    frequency[char3] += 1
    
    # 找到出现次数最多的字符
    most_frequent = max(frequency, key=frequency.get)
    
    return most_frequent

假设输入此函数的参数分别为A、A、B,初始化字符频率字典 frequency 为 {A: 0, A: 0, B: 0}。但是由于字典的键必须是唯一的,所以最终字典将是 {A: 0, B: 0}。

更新频率字典,增加每个字符的出现次数:

  • frequency[A] += 1,频率字典变为 {A: 1, B: 0}。
  • frequency[A] += 1,频率字典变为 {A: 2, B: 0}。
  • frequency[B] += 1,频率字典变为 {A: 2, B: 1}。

使用 max(frequency, key=frequency.get) 找到出现次数最多的字符。此时 A 的频率是 2,B 的频率是 1,所以 A 是出现次数最多的字符。

2、多次调用大语言模型,生成多个结果以供投票

改写process_datas函数,三次调用LLM,统计出现的答案的出现次数,最终返回投票数最多的结果。

def process_datas(datas,MODEL_NAME):
    results = []

    # 送入多线程任务
    for data in tqdm(datas, desc="Submitting tasks", total=len(datas)):
        problem = data['problem']
        for id,question in enumerate(data['questions']):
            prompt = get_prompt(
                problem, 
                question['question'], 
                question['options'],
            )
            # 三次调用LLM
            res, res1, res2 = api_retry(MODEL_NAME, prompt), api_retry(MODEL_NAME, prompt), api_retry(MODEL_NAME, prompt)
            # 获取不同的答案
            extract_response, extract_response1, extract_response2 = extract(res), extract(res1), extract(res2)
            ans = most_frequent_char(extract_response, extract_response1, extract_response2)
            data['questions'][id]['answer'] = ans
            results.append(data) 
    return results
(🔴补充)答案提取

值得一提的是,答案提取函数extract的写法根据微调训练使用的数据集不同也有较小的改动,具体改动内容见注释:

def extract(input_text):
    ans_pattern = re.compile(r"(.)", re.S) # 使用an.json文件作为微调训练集时使用
    # ans_pattern = re.compile(r"答案是:(.)", re.S) # 使用ana.json文件作为微调训练集时使用
    problems = ans_pattern.findall(input_text)
    if(problems == []):
        return 'A'
    return problems[0]

这个改动是相当重要的,否则推理完成后,可能会发现提取的答案不是合格的字符,浪费推理资源。

(💦补充)在不使用vllm加速推理的情况下进行推理

在不使用/无法使用vllm进行加速推理时,由于微调后的大语言模型就存储在本地服务器中,因此直接使用模型进行推理是可行的,这里展示一段直接使用模型进行推理的代码(与3.1节中的代码其实很相似):

from transformers import AutoModelForCausalLM, AutoTokenizer
from datetime import datetime

device = "cuda" # the device to load the model onto

model_path = './merged_model' # 按实际情况修改
model = AutoModelForCausalLM.from_pretrained(model_path, device_map="auto")
tokenizer = AutoTokenizer.from_pretrained(model_path)

prompt = '''你是一个逻辑推理专家,擅长解决逻辑推理问题。以下是一个逻辑推理的题目,形式为单项选择题。所有的问题都是(close-world assumption)闭世界假设,即未观测事实都为假。请逐步分析问题并在最后一行输出答案,最后一行的格式为"答案是:A"。题目如下:\n\n### 题目:\n假设您需要构建一个二叉搜索树,……\n\n请基于以上规则,回答以下选择题:\n\n### 问题:\n选择题 1:\n给定一个空的二叉搜索树,插入下列数字: [5, 9, 2, 10, 11, 3],下面哪个选项正确描述了结果树的结构?\nA. ……\nB. ……\nC. ……\nD. ……'''
messages = [{"role": "user", "content": prompt}]
for i in range(5): # 这个循环只是为了模拟回答多个问题的情形
    currentDateAndTime = datetime.now()
    print("The current date and time is", currentDateAndTime) # 方便观察回答一个问题耗时
    text = tokenizer.apply_chat_template(messages, tokenize=False, add_generation_prompt=True)
    model_inputs = tokenizer([text], return_tensors="pt").to(device)
    generated_ids = model.generate(model_inputs.input_ids, max_new_tokens=512, do_sample=True) # max_new_tokens需要设置得大一点
    generated_ids = [output_ids[len(input_ids):] for input_ids, output_ids in zip(model_inputs.input_ids, generated_ids)]
    response = tokenizer.batch_decode(generated_ids, skip_special_tokens=True)[0]
    print(response)
    
    '''
    具体地组织不同问题的prompt、提取答案选项、保存答案至文件等环节的代码省略
    '''

多说一句,在我的环境和配置下,不使用vllm加速推理,处理测试集中500个问题(由于每个问题背景下有多个子问题,因此实际是需要处理1204个问题),每个问题需要处理5 minutes;使用vllm加速推理的情况下,处理全部问题需要约7 hours,即每个问题需要处理约0.35 minute,加速效果还是挺明显的(但是整体推理速度还是比较慢,需要7小时)。

💦初步上分思路

1、prompt优化

在Task4文档里有此部分建议,提出了若干种类型的prompt。

  • 训练时和推理时用的prompt建议用一样的
  • 题目有数学计算、逻辑推理、判断等主要类型。可以考虑将数据集进行分类,对不同类型的题目设计不同的prompt(有没有相关的案例?)

2、agent设计

3、数据集扩充

  • 使用相同数据集或开源数据集进行数据集扩充

  • 用GPT 4或者其他能力更强的大语言模型生成数据

4、其他微调方法

目前微调是使用参数高效微调peft框架进行微调。peft框架文档:https://huggingface.co/docs/peft/conceptual_guides/adapter

  • 可以尝试微调和RAG一起弄

  • FFT全量微调

  • LoRA-Pro对齐全量微调

  • DoRA

  • LoRA +
    具体地组织不同问题的prompt、提取答案选项、保存答案至文件等环节的代码省略
    ‘’’


多说一句,在我的环境和配置下,不使用vllm加速推理,处理测试集中500个问题(由于每个问题背景下有多个子问题,因此实际是需要处理1204个问题),每个问题需要处理5 minutes;使用vllm加速推理的情况下,处理全部问题需要约7 hours,即每个问题需要处理约0.35 minute,加速效果还是挺明显的(但是整体推理速度还是比较慢,需要7小时)。



## 💦初步上分思路

### 1、prompt优化

在Task4文档里有此部分建议,提出了若干种类型的prompt。

- 训练时和推理时用的prompt建议用一样的
- 题目有数学计算、逻辑推理、判断等主要类型。可以考虑将数据集进行分类,对不同类型的题目设计不同的prompt(有没有相关的案例?)

### 2、agent设计

### 3、数据集扩充

- 使用相同数据集或开源数据集进行数据集扩充

- 用GPT 4或者其他能力更强的大语言模型生成数据

### 4、其他微调方法

目前微调是使用参数高效微调peft框架进行微调。peft框架文档:https://huggingface.co/docs/peft/conceptual_guides/adapter

- 可以尝试微调和RAG一起弄

- FFT全量微调

- LoRA-Pro对齐全量微调
- DoRA
- LoRA +
解决Python中的ImportError: libcudart 安装CUDA时,请确保将CUDA的库路径添加到系统的动态链接库搜索路径中。当这些库尝试加载与CUDA相关的共享库文件时,如果找不到或无法访问这些文件,就会引发该错误。如果上述方法都无效,您可以尝试重新编译与CUDA相关的Python库。如果您已经正确安装了CUDA,但仍然遇到该错误,请检查CUDA的路径配置是否正确。您需要确保环境变量中包含正确的CUDA路径。如果您的CUDA库文件位于非标准位置,您可以尝试创建一个软链接将其链接到正确的位置。有时,该错误可能是由于CUDA相关的库文件损坏或不兼容引起的。 阅读详情

相关推荐

ImportError: cannot import name ‘AutoTokenizer‘ from ‘modelscope‘解决方案

本文主要介绍了ImportError: cannot import name ‘AutoTokenizer’ from 'modelscope’解决方案,希望能对使用Python的同学们有所帮助。 文章目录 1. 问题描述 2. 解决方案

weixin_43178406的博客 15万+

使用HuggingFace Transformers进行NLP模型微调

evaluate库已经提供了大量评估函数的实现。这里使用了最简单的准确率acc和F1分值。• TP (True Positive):真*正例,即实际为正类,”正确“预测为正类的样本数。• TN (True Negative):真*负例,即实际为负类,”正确“预测为负类的样本数。• FP (False Positive):假*正例,即实际为负类,”错误“预测为正类的样本数。• FN (False Negative):假*负例,即实际为正类,”错误“预测为负类的样本数。, acc被称为准确率。

xxue345678的博客 1294

Unity通过Vosk实现离线语音识别方法

以下是一个使用 Unity 和 Vosk(一个开源的离线语音识别库)的简单示例。将Vosk的DLL文件(如libvosk.dll、vosk.dll等)放入Assets/Plugins文件夹中。通过以上步骤,你可以在Unity中实现一个简单的离线语音识别功能。音频格式:确保录音的音频格式与Vosk模型要求的格式一致(通常是16kHz、16bit单声道)。首先,你需要下载Vosk的语音识别模型。在Assets文件夹中创建一个Plugins文件夹,用于存放Vosk的DLL文件。打开Unity并创建一个新的项目。

qq_39515892的博客 2070

error... : lib*.so.*: cannot open shared object file: No such file or directory

报错:error while loading shared libraries: libo*.so.12: cannot open shared object file: No such file or directory 出现这个报错,一般是在安装软件,./configure 的时候,系统找不到这个库报的错 1、找到安装软件的 lib 库 一般是在软件的解压目录里,会有一个 lib 目录,pwd 记下当前路径,比如我的是 /usr/local/kingbase/KingbaseSE/bin 2、找到系

很简单_的博客 2638

形位公差标注的常见误区与优化策略

本文深入解析机械设计中形位公差标注的常见误区与优化策略,特别针对NX2406用户提供实用技巧。从标注冗余、基准选择到公差值设定等五大误区入手,结合工程图实例展示如何提升标注精度与效率,帮助工程师避免常见错误,优化设计流程,确保零件功能实现与制造可行性。

info6的博客 245

手把手教你用Kokoro-82M-v1.1-zh搭建本地TTS服务(含WebSocket接口)

本文详细介绍了如何使用Kokoro-82M-v1.1-zh模型搭建本地TTS服务,包括环境配置、模型部署、WebSocket接口实现及性能优化。通过实战指南,开发者可以快速掌握本地部署TTS系统的关键技术,提升语音交互应用的开发效率。

weixin_30776273的博客 183

debug 笔记:llama 3.2 部署bug 之cutlassF: no kernel found to launch!

按照官方的写法。

qq_40206371的博客 673

别再死记硬背了!用LabVIEW串口通信的3个真实项目案例,手把手教你从调试到部署

本文通过3个真实项目案例,详细讲解LabVIEW串口通信从调试到部署的全过程。涵盖Arduino温湿度监控、西门子PLC Modbus通信以及高可靠性通信框架设计,提供实用技巧和常见问题解决方案,帮助工程师快速掌握LabVIEW串口通信的实战应用。

weixin_30485379的博客 523

2024 Datawhale AI夏令营 第五期 Task2 && Task3:baseline改进方法与具体思路

站在一个参赛者的视角尽我所能对城市违法行为检测中baseline原有代码提出一些改进思路

qq_49874310的博客 1278

大厂硬件机试选择题与判断题解析-卷1

本文精选30道硬件工程师机试题目,涵盖模拟电路、数字电路、PCB设计三大方向。模拟电路部分重点考察运算放大器、滤波器、整流电路等核心知识;数字电路部分聚焦计数器、电平转换等关键技术;PCB设计部分强调高速信号处理、阻抗控制等实践要点。题目均来自华为、小米等大厂真题,剔除基础题,每道题附详细解析,包括正确答案推导过程及相关公式说明。特别针对易错概念(如负反馈类型判断、Q值与带宽关系等)进行重点解析,帮助考生系统掌握硬件设计的核心考点和解题思路。

启芯硬件的博客 679

YOLOv11改进策略【模型轻量化】| 替换骨干网络 CVPR-2024 StarNet,超级精简高效的轻量化模块

本文记录的是基于StarNet的YOLOv11轻量化改进方法研究。StarNet设计简洁,没有复杂的设计和精细调整的超参数,仅是一个 4 阶段的分层架构。并且其中能够在低维空间计算的同时考虑极高维的特征这一特性,进而提高模型精度。本文在替换骨干网络中配置了原论文中的starnet_s1starnet_s2starnet_s3和starnet_s4七种模型,以满足不同的需求。

Limiiiing的博客 5987

llm推理

sub_df = test_df[['id', 'rewrite_prompt']] # # 只保留id和rewrite_prompt两列。quantization_config=bnb_config, # 应用4bit量化配置。input_token_len = 1024 # 输入token最大长度。output_token_len = 100 # 输出token最大长度。trust_remote_code=True, # 信任远程代码。# 将生成的prompt拼接上平均prompt。

feifeikon的博客 587

uniapp [全端兼容] - 详细实现拍照或相册选取图片后插入水印功能,手机拍照或相册上传图像并添加自定义水印及指定位置显示水印,uniApp给图片加水印后上传到后端服务器或保存到本机相册详细教程!

uni-app,h5移动端网页,微信小程序,支付宝小程序,Android安卓,苹果ios,app,nvue,图片水印,上传照片带水印,从相册选择图片加水印功能,拍完照片给图片加水印,添加照片水印,uniapp图片加水印,为图片添加自定义水印,uni-app上传图片并添加水印,上传图片加水印,uni-app如何对拍摄的照片加水印,uni-app实现图片上传添加水印操作,给上传的图片添加上水印,图片加完水印后如何上传到服务器,水印相机,拍照图片加水印,相册选取后给图片加上水印,渲染失败,无法保存,文字水印,日期

🏆 前端领域优质创作者 1万+

编译自定义算子出现的问题(GCFSR)-- libcudart.so.12: No such file

本文中是在torch调用中报错,在torch官网中发现,在本博客创建时间时,pytorch支持的最高版本的cuda是11.8,对cuda-12是不支持的。2、在进行与cuda相关的编译时,调用了cuda的版本,生成了对应的.so 文件。1、调用的package 与 cuda 版本不匹配。2、删除原来的编译结果,重新进行编译。1、重新安装cuda。

在人工智能的道路上爬行 3309

迈瑞BS系列全自动生化分析仪LIS协议接口手册(内含HL7和ASTM)

迈瑞BS系列全自动生化分析仪LIS协议接口手册,内含HL7和ASTM)

计算机控制技术及应用

《计算机控制技术及应用》通过总结作者的科研成果和吸收国内外的先进理论、方法和技术,以计算机控制系统的网络化、开放化、智能化和集成化发展趋势为主线索,主要介绍计算机控制系统的基本概念和计算机控制系统的典型形式,计算机控制系统的数学描述与基本分析方法,以及数字PID控制器、最少拍控制系统、达林算法、状态变量设计法等设计方法及其各种改进算法。重点突出计算机控制系统输入输出通道接口的设计方法和典型控制系统的设计案例与实现方法等。

kaggle运行报错RuntimeError: cutlassF: no kernel found to launch!

处理报错问题:RuntimeError: cutlassF: no kernel found to launch!

zc1226的博客 1911

老版QQ原生表情gif动态

这是老版的QQ表情,不过全是动态的的,Windows照片查看器是不会动的,可以右击用浏览器打开查看,再说一遍是老版的,只是分享给宁愿用老版也要动态的人。

vllm ImportError: libcudart.so.12: cannot open shared object file: No such file or directory

r!r。

自闭啦~~ 1507

蓝牙模块HM-10手册

蓝牙模块HM-10手册,该手册对蓝牙模块HM-10进行了详细的介绍和使用方法以及一些AT指令。

教你如何跑 LLama3 模型

本文将介绍LLama 3,以及其框架,并一步步在 GPU 云服务上运行它。

了解关于云主机、GPU、AI、数据库托管、Kubernetes等相关技术知识,以及DigitalOcean云平台使用教程 1678

报错libcudart.so.12: cannot open shared object file: No such file or directory

【代码】报错libcudart.so.12: cannot open shared object file: No such file or directory。

qq_15821487的博客 9568

wsl配置vllm问题:ImportError: libcudart.so.12: cannot open shared object file: No such file or director

wsl配置vllm遇到的问题:ImportError: libcudart.so.12: cannot open shared object file: No such file or director

zdx3456789的博客 1256

datasets.Dataset.map方法学习笔记

可以将datasets中的Dataset实例看做是一张数据表。map方法会将输入的function按照指定的方式应用在每一行(每一行称为一个example)上。是map方法的核心,其介绍单独放在下列章节。

weixin_44815943的博客 9689

大模型LLM微调的方式:Instruction/Promt/Prefix/PET、

先从预训练模型开始,初始化多个不同的模型(图中1步骤),在有标注的少量样本上进行Prompt-Tuning,然后通过多个不同的PVP训练得到多个不同的模型(图中a步骤),每个模型在无标注数据上进行推理打标,并根据置信度筛选(图中b步骤),根据新标注的数据与原始标注数据融合后,再重新进行Prompt-Tuning(图中c步骤),重复abc三个步骤多次后,获得每个模型后,在测试时进行集成投票(图中2、3步骤)。同连续的模板一样,为了让label word也可以自适应的变化,提出连续的label word。

具身机器人和通用AI 2437

2025版最新Qwen2大模型微调入门实战(附完整代码)零基础入门到精通,收藏这篇就够了

大模型指令微调(Instruction Tuning)是一种针对大型预训练语言模型的微调技术,其核心目的是增强模型理解和执行特定指令的能力,使模型能够根据用户提供的自然语言指令准确、恰当地生成相应的输出或执行相关任务。指令微调特别关注于提升模型在遵循指令方面的一致性和准确性,从而拓宽模型在各种应用场景中的泛化能力和实用性。在实际应用中,我的理解是,指令微调更多把LLM看作一个更智能、更强大的传统NLP模型(比如Bert),来实现更高精度的文本预测任务。

Libra1313的博客 1207

ollama导入自己微调后的模型胡言乱语

原因:Modelfile的模板格式不对,对的如下所示。1.ollama导入自己微调后的模型胡言乱语。下载llama.cpp。

m0_57057282的博客 4385

DataWhale AI夏令营task03

此外,还使用了更安全的RSA加密算法对服务器和客户端之间的选票信息及用户信息进行数据加密,有效保证了数据传输过程中的安全。:提出了一种基于安全多方求和的多候选人电子选举方案,该方案在一个多精度数中隐藏“m选k”形式的选票,并将多精度计算及安全多方求和协议应用于投票和计票过程中。这种方案具有更强的安全性,包括选票的完全保密性和无收据性、计票的公平性和无争议性、系统的健壮性等。实现多路投票的方法多种多样,包括基于强化学习的投票机制、顺序投票、多平台移动投票系统设计、电子投票的安全性增强以及多代理模拟等。

m0_73841861的博客 438

视频隐私脱敏工程化:CSRT/KCF追踪算法在本地推理中的实现与边界

在短视频内容合规审查中,动态遮挡(Dynamic Masking)是高频且高耗时的环节。传统手动关键帧打码不仅效率低下,且在目标快速移动或遮挡时极易出现遮罩漂移。本文从计算机视觉工程角度,解析基于CSRT与KCF算法的目标追踪方案在视频隐私处理中的技术路径、性能权衡及落地实践。

Mysssone的博客 252
上一篇: 【2024】Datawhale AI夏令营 Task2笔记——Baseline代码详细解读
下一篇: 【2024】Datawhale AI夏令营 Task4笔记——vllm加速方式修改及llm推理参数调整上分
Mocode
博客等级 码龄7年 335粉丝 53原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值