大模型微调
大模型微调(Fine-tuning)是一种技术,通过在预训练的大型语言模型上使用特定数据集进行进一步训练,使模型能够更好地适应特定任务或领域。
其核心原理在于,机器学习模型只能代表其训练数据的逻辑和理解。对于未见过的数据样本,模型可能无法准确识别或理解。对于大型模型而言,它们虽然能够处理广泛的语言信息并进行流畅的对话,但在特定场景下可能无法提供准确的答案。
1.LoRA微调
LoRA(Low-Rank Adaptation)微调是一种高效的模型微调技术,特别适用于大型预训练语言模型的适应性调整。LoRA的核心思想是通过引入低秩矩阵来调整模型的权重,从而在不显著增加模型参数数量的情况下,实现对模型的微调。
代码介绍
lora.ipynb
cell1
!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
这段代码是一系列使用 pip
命令安装 Python 包的命令。这些包主要用于机器学习和自然语言处理(NLP)任务。下面是每个包的简要说明:
-
modelscope==1.9.5:ModelScope 是一个开源的模型库,提供预训练模型和模型训练工具,方便用户进行模型开发和部署。
-
transformers>=4.39.0:Transformers 是一个用于自然语言处理(NLP)任务的库,提供了大量预训练模型,如 BERT、GPT 等。
-
streamlit==1.24.0:Streamlit 是一个用于创建数据应用的框架,可以快速将数据科学代码转换为 Web 应用。
-
sentencepiece==0.1.99:SentencePiece 是一个无监督文本分词工具,适用于各种语言,可以用于文本预处理。
-
transformers_stream_generator==0.0.4:这个包可能是一个用于生成文本流的工具,与 Transformers 库配合使用。
-
datasets==2.18.0:Datasets 是一个用于加载和处理数据集的库,支持多种数据格式和来源。
-
peft==0.10.0:PEFT(Parameter-Efficient Fine-Tuning)是一个用于参数高效微调的库,可以减少微调过程中的计算资源消耗。
-
openai==1.17.1:OpenAI 的 Python 客户端库,用于访问 OpenAI 的 API,如 GPT-3 等。
-
tqdm==4.64.1:TQDM 是一个快速、可扩展的进度条库,用于在命令行中显示进度条。
-
transformers==4.39.3:这是另一个 Transformers 库的版本,与前面的版本号不同,但功能相同。
-
setuptools==69.5.1:Setuptools 是一个用于打包 Python 项目的工具,可以简化安装和分发过程。
-
vllm==0.4.0.post1:VLLM(Vectorized Language Model)是一个用于大规模语言模型训练和推理的库。
-
nest-asyncio:这个包允许在异步环境中使用
asyncio
,通常用于解决 Python 的异步 I/O 问题。 -
accelerate:Accelerate 是一个用于加速深度学习训练的库,支持多种硬件加速器。
-
tf-keras:TensorFlow 的 Keras API,用于构建和训练深度学习模型。
用途:这些包主要用于构建和部署机器学习模型,特别是自然语言处理任务。它们提供了预训练模型、数据处理工具、Web 应用框架、参数高效微调方法等,可以加速开发过程并提高模型性能。
注意事项:
- 确保你的 Python 环境支持这些包的版本要求。
- 在安装这些包之前,最好创建一个新的虚拟环境,以避免与其他项目的依赖冲突。
- 在安装过程中,如果遇到权限问题,可以尝试使用
sudo
(在 Linux 或 macOS 上)或以管理员身份运行命令提示符(在 Windows 上)。 - 安装完成后,建议运行一些简单的测试代码,确保所有包都能正常工作。
cell2
import torch
from modelscope import snapshot_download, AutoModel, AutoTokenizer
import os
model_dir = snapshot_download('qwen/Qwen2-7B-Instruct', cache_dir='./', revision='master')
这段代码的主要功能是从模型库中下载一个预训练的模型,并设置其缓存目录。具体来说,它使用了torch
和modelscope
这两个库来实现这一功能。下面是对代码的详细解释:
-
导入必要的库:
import torch from modelscope import snapshot_download, AutoModel, AutoTokenizer
torch
:PyTorch库,用于深度学习任务。modelscope
:一个用于模型管理和下载的库,可以方便地下载和加载预训练模型。snapshot_download
:modelscope
中的一个函数,用于从模型库中下载模型。AutoModel
和AutoTokenizer
:modelscope
中的类,用于自动加载模型和对应的分词器。
-
设置模型下载目录:
model_dir = snapshot_download('qwen/Qwen2-7B-Instruct', cache_dir='./', revision='master')
'qwen/Qwen2-7B-Instruct'
:这是模型的名称或标识符,表示要下载的模型是Qwen2-7B-Instruct
。cache_dir='./'
:指定模型下载的缓存目录为当前目录(./
)。revision='master'
:指定下载模型的版本,这里使用的是master
分支。
实现原理:
snapshot_download
函数会根据提供的模型名称和版本,从模型库中下载对应的模型文件,并将其保存到指定的缓存目录中。- 下载完成后,
model_dir
变量会保存模型文件在本地文件系统中的路径。
用途:
- 这段代码的用途是准备一个预训练的模型,以便后续的深度学习任务中使用。例如,可以用于文本生成、问答系统、文本分类等任务。
注意事项:
- 确保网络连接正常,以便能够从模型库中下载模型。
- 如果模型较大,下载时间可能会比较长。
modelscope
库需要正确安装,可以通过pip install modelscope
命令进行安装。- 下载的模型文件会保存在指定的缓存目录中,如果需要下载其他模型,可以修改
cache_dir
参数来指定不同的目录。
这里运行完请重启notebook
cell3
from datasets import Dataset
import pandas as pd
from transformers import AutoTokenizer, AutoModelForCausalLM, DataCollatorForSeq2Seq, TrainingArguments, Trainer, GenerationConfig
这段代码是使用Python进行自然语言处理(NLP)任务的一个示例,具体来说,它涉及使用Hugging Face的Transformers库来处理序列到序列(Seq2Seq)的任务,如文本生成。下面是对这段代码的详细解释:
-
导入库:
from datasets import Dataset
:从datasets
库中导入Dataset
类。Dataset
类用于创建和管理数据集,可以方便地进行数据预处理和加载。import pandas as pd
:导入pandas
库,用于数据处理和分析。pandas
提供了数据结构如DataFrame,可以方便地处理表格数据。from transformers import AutoTokenizer, AutoModelForCausalLM, DataCollatorForSeq2Seq, TrainingArguments, Trainer, GenerationConfig
:从transformers
库中导入多个类和函数,这些类和函数用于加载预训练模型、处理数据、配置训练参数、训练模型和生成文本。
-
用途:
- 这段代码的目的是设置一个环境,以便进行序列到序列的任务,如文本生成。它包括数据集的创建、模型的加载、训练参数的配置等步骤。
-
实现原理:
AutoTokenizer
:用于加载预训练的tokenizer,将文本转换为模型可以理解的格式(如token IDs)。AutoModelForCausalLM
:用于加载预训练的因果语言模型(Causal Language Model),这种模型可以用于文本生成任务。DataCollatorForSeq2Seq
:用于将多个数据样本组合成一个批次,以便进行训练。TrainingArguments
:用于配置训练参数,如学习率、批次大小、训练轮数等。Trainer
:用于训练模型,它封装了训练过程中的所有步骤,如数据加载、模型训练、评估等。GenerationConfig
:用于配置生成文本时的参数,如最大生成长度、温度等。
-
注意事项:
- 确保已经安装了
datasets
和transformers
库,可以通过pip install datasets transformers
命令进行安装。 - 在实际使用中,需要根据具体任务和数据集进行相应的配置和调整,如加载不同的预训练模型、调整训练参数等。
- 在处理大规模数据集时,可能需要考虑数据加载和处理的效率问题,可以使用
pandas
进行数据预处理,或者使用datasets
库提供的工具进行数据加载和预处理。
- 确保已经安装了
这段代码为使用Hugging Face的Transformers库进行序列到序列任务提供了一个基础框架,可以根据具体需求进行扩展和定制。
cell4
# 将JSON文件转换为CSV文件
df = pd.read_json('ana.json')
ds = Dataset.from_pandas(df)
ds[:3] # 查看切片
这段代码的作用是将一个JSON文件转换为CSV文件。具体来说,它使用了Pandas库和Dataset库来实现这个转换。下面是对代码的详细解释:
-
df = pd.read_json('ana.json')
:这行代码使用Pandas库的read_json
函数读取名为ana.json
的JSON文件,并将其内容存储在一个DataFrame对象df
中。Pandas是一个强大的数据处理和分析库,DataFrame是Pandas中最常用的数据结构之一,它类似于Excel表格或SQL表格,可以存储和操作二维数据。 -
ds = Dataset.from_pandas(df)
:这行代码使用Dataset库的from_pandas
方法将DataFrame对象df
转换为Dataset对象ds
。Dataset是一个用于处理大规模数据集的库,它提供了许多高级的数据处理和分析功能。通过将DataFrame转换为Dataset,可以更方便地进行大规模数据的处理和分析。
cell5
tokenizer = AutoTokenizer.from_pretrained('./qwen/Qwen2-7B-Instruct', use_fast=False, trust_remote_code=True)
tokenizer
这段代码使用Hugging Face的transformers
库来加载一个预训练的tokenizer。具体来说,它从本地路径./qwen/Qwen2-7B-Instruct
加载了一个名为Qwen2-7B-Instruct
的预训练模型。下面是对这段代码的详细解释:
-
AutoTokenizer
:AutoTokenizer
是transformers
库中的一个类,用于自动选择并加载适合特定模型的tokenizer。它可以根据模型名称或路径自动选择合适的tokenizer类。
-
from_pretrained
方法:from_pretrained
是AutoTokenizer
类的一个方法,用于从预训练模型库或本地路径加载预训练的tokenizer。它接受一个字符串参数,该参数可以是预训练模型的名称(如bert-base-uncased
),也可以是本地路径(如./qwen/Qwen2-7B-Instruct
)。
-
参数解释:
use_fast=False
: 这个参数指定是否使用快速版本的tokenizer。如果设置为True
,将使用tokenizers
库的快速版本,这通常更快,但可能不支持所有模型。在这里,设置为False
表示不使用快速版本。trust_remote_code=True
: 这个参数指定是否信任远程代码。如果设置为True
,将允许加载远程代码,这在加载自定义模型或tokenizer时可能需要。在这里,设置为True
表示信任远程代码。
-
tokenizer
:tokenizer
是一个变量,用于存储加载的tokenizer对象。这个对象可以用于将文本转换为模型可以理解的token ID序列,以及将token ID序列转换回文本。
-
用途:
- 这段代码的用途是加载一个预训练的tokenizer,以便后续使用该tokenizer对文本进行编码和解码。这在自然语言处理任务中非常常见,例如文本分类、情感分析、问答系统等。
-
注意事项:
- 确保本地路径
./qwen/Qwen2-7B-Instruct
存在,并且包含有效的tokenizer配置和词汇表文件。 - 如果使用
trust_remote_code=True
,请确保加载的远程代码是可信的,以避免潜在的安全风险。 - 使用
use_fast=False
可能会降低处理速度,但在某些情况下,使用非快速版本可能是必要的,例如需要支持特殊字符或词汇。
- 确保本地路径
总结来说,这段代码加载了一个预训练的tokenizer,以便后续对文本进行编码和解码操作。
cell6
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 不在开头加 special_tokens
response = tokenizer(f"{example['output']}", add_special_tokens=False)
input_ids = instruction["input_ids"] + response["input_ids"] + [tokenizer.pad_token_id]
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]
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
}
这段Python代码定义了一个名为process_func
的函数,用于处理输入的示例数据,并将其转换为适合Llama模型输入的格式。下面是对代码的详细解释:
函数定义
def process_func(example):
example
:输入的示例数据,包含指令(instruction
)、输入(input
)和输出(output
)。
常量定义
MAX_LENGTH = 1800
MAX_LENGTH
:定义了Llama模型的最大输入长度为1800个token。
初始化变量
input_ids, attention_mask, labels = [], [], []
input_ids
:存储输入的token ID。attention_mask
:存储注意力掩码,用于指示哪些token应该被模型关注。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)
- 使用
tokenizer
将指令和输入转换为token ID,add_special_tokens=False
表示不在开头添加特殊token。
处理输出
response = tokenizer(f"{example['output']}", add_special_tokens=False)
- 使用
tokenizer
将输出转换为token ID。
构建输入ID、注意力掩码和标签
input_ids = instruction["input_ids"] + response["input_ids"] + [tokenizer.pad_token_id]
attention_mask = instruction["attention_mask"] + response["attention_mask"] + [1]
labels = [-100] * len(instruction["input_ids"]) + response["input_ids"] + [tokenizer.pad_token_id]
input_ids
:将指令和输出的token ID拼接起来,并在末尾添加一个填充token ID。attention_mask
:将指令和输出的注意力掩码拼接起来,并在末尾添加一个1,表示填充token需要被关注。labels
:将指令部分的token ID用-100
填充,表示这些token不参与损失计算;然后将输出的token ID拼接起来,并在末尾添加一个填充token ID。
截断处理
if len(input_ids) > MAX_LENGTH:
input_ids = input_ids[:MAX_LENGTH]
attention_mask = attention_mask[:MAX_LENGTH]
labels = labels[:MAX_LENGTH]
- 如果生成的token ID长度超过最大长度,则进行截断处理。
返回结果
return {
"input_ids": input_ids,
"attention_mask": attention_mask,
"labels": labels
}
- 返回一个包含
input_ids
、attention_mask
和labels
的字典,这些数据可以直接用于Llama模型的输入。
注意事项
tokenizer
需要提前定义,并且需要支持add_special_tokens
参数。MAX_LENGTH
需要根据实际情况进行调整,以确保模型能够处理足够长度的输入。- 在处理长文本时,可能需要进一步优化,例如使用滑动窗口等技术来处理超出长度的文本。
cell7
tokenized_id = ds.map(process_func, remove_columns=ds.column_names)
tokenized_id
这段代码使用了Python的Pandas库来处理数据集(ds
),具体来说,它对数据集中的每一行应用了一个名为process_func
的函数,并移除了原始数据集中的所有列。下面是对这段代码的详细解释:
-
ds.map(process_func, remove_columns=ds.column_names)
:ds
是一个Pandas DataFrame,代表一个数据集。map
是Pandas DataFrame的一个方法,用于对DataFrame中的每一行或每一列应用一个函数。process_func
是一个函数,它定义了如何处理数据集中的每一行。这个函数应该接受一个DataFrame行作为输入,并返回处理后的结果。remove_columns=ds.column_names
参数指定了在应用process_func
之后,从原始DataFrame中移除哪些列。ds.column_names
返回DataFrame的所有列名,因此这行代码意味着移除所有列。
-
tokenized_id
:tokenized_id
是map
方法返回的结果,它是一个新的DataFrame,包含了应用process_func
处理后的数据。- 由于
remove_columns
参数移除了所有列,所以tokenized_id
将是一个空的DataFrame,除非process_func
返回了新的列。
-
用途:
- 这段代码的用途是使用
process_func
对数据集中的每一行进行处理,并生成一个新的DataFrame,其中包含处理后的数据。由于移除了所有原始列,新的DataFrame可能只包含process_func
返回的列。
- 这段代码的用途是使用
-
注意事项:
process_func
函数需要被定义,并且它应该能够接受一个DataFrame行作为输入,并返回一个包含处理后的数据的DataFrame。- 如果
process_func
返回的DataFrame包含与原始DataFrame不同的列,那么tokenized_id
将包含这些新列。 - 如果
process_func
返回的DataFrame包含与原始DataFrame相同的列,那么tokenized_id
将包含这些列,但它们将包含处理后的数据。 - 由于
remove_columns
参数移除了所有列,所以tokenized_id
将是一个空的DataFrame,除非process_func
返回了新的列。
总结来说,这段代码通过应用一个处理函数process_func
来转换数据集中的每一行,并移除原始数据集中的所有列,最终生成一个新的DataFrame。
cell8
tokenizer.decode(tokenized_id[0]['input_ids'])
输出
'<|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|>'
cell9
tokenizer.decode(list(filter(lambda x: x != -100, tokenized_id[1]["labels"])))
输出
'根据题目描述的函数逻辑,我们来逐步分析输入列表 [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|>'
cell9
import torch
model = AutoModelForCausalLM.from_pretrained('./qwen/Qwen2-7B-Instruct', device_map="auto",torch_dtype=torch.bfloat16)
model
这段代码使用了PyTorch库来加载一个预训练的因果语言模型(Causal Language Model),具体来说是一个名为Qwen2-7B-Instruct的模型。下面是对这段代码的详细解释:
-
导入库:
import torch
这行代码导入了PyTorch库,PyTorch是一个用于深度学习的开源机器学习库。
-
加载预训练模型:
model = AutoModelForCausalLM.from_pretrained('./qwen/Qwen2-7B-Instruct', device_map="auto", torch_dtype=torch.bfloat16)
这行代码使用
AutoModelForCausalLM
类从本地路径./qwen/Qwen2-7B-Instruct
加载了一个预训练的因果语言模型。具体参数解释如下:from_pretrained
:这是一个类方法,用于从预训练模型文件中加载模型。'./qwen/Qwen2-7B-Instruct'
:这是预训练模型的本地路径。device_map="auto"
:这个参数指定了模型加载到哪个设备上。"auto"
表示自动选择设备,可以是CPU或GPU。torch_dtype=torch.bfloat16
:这个参数指定了模型参数的数据类型。torch.bfloat16
是一种16位浮点数格式,通常用于加速计算并减少内存使用。
-
输出模型:
model
这行代码只是简单地输出了加载的模型对象,通常用于调试或确认模型是否成功加载。
用途:
这段代码的用途是加载一个预训练的因果语言模型,以便进行进一步的推理、微调或其他任务。因果语言模型是一种生成模型,能够根据前面的文本生成后续的文本,常用于文本生成、对话系统、文本补全等任务。
注意事项:
- 确保本地路径
./qwen/Qwen2-7B-Instruct
是正确的,并且包含预训练模型的权重文件。 - 确保安装了必要的依赖库,如
torch
和transformers
(transformers
库提供了AutoModelForCausalLM
类)。 - 使用
torch.bfloat16
数据类型可能会影响模型的精度,但可以加速计算并减少内存使用。根据具体任务的需求,可能需要调整数据类型。
cell10
model.enable_input_require_grads() # 开启梯度检查点时,要执行该方法
model.dtype #torch.bfloat16
这段代码是用来开启一个深度学习模型中输入张量的梯度检查点功能。具体来说,model.enable_input_require_grads()
是一个方法调用,用于在模型训练过程中启用输入张量的梯度计算。这在某些特定的深度学习框架中,如 PyTorch,是非常有用的功能。
实现原理
在深度学习中,梯度是用于优化模型参数的关键信息。通常,我们只对模型的输出和损失函数有关联的参数计算梯度。然而,在某些情况下,我们可能需要计算输入张量的梯度,例如在对抗训练(adversarial training)或某些特定的损失函数中。
enable_input_require_grads()
方法的作用就是告诉模型在计算梯度时,不仅计算模型参数的梯度,还要计算输入张量的梯度。这通常通过在输入张量上设置 requires_grad=True
来实现。
用途
-
对抗训练:对抗训练是一种防御对抗性攻击的方法,通过在训练过程中故意添加对抗性噪声来增强模型的鲁棒性。计算输入的梯度可以帮助模型更好地理解输入数据,从而提高对抗性攻击的防御能力。
-
梯度惩罚:在某些损失函数中,可能需要计算输入的梯度来惩罚模型对输入的敏感度,从而提高模型的泛化能力。
-
输入依赖的损失函数:某些损失函数可能依赖于输入数据,计算输入的梯度可以帮助模型更好地理解输入数据,从而提高模型的性能。
注意事项
-
计算开销:计算输入的梯度会增加计算开销,因为需要计算更多的梯度信息。因此,在启用输入梯度计算时,需要权衡计算效率和模型性能。
-
内存消耗:计算输入的梯度会增加内存消耗,因为需要存储更多的梯度信息。因此,在启用输入梯度计算时,需要确保有足够的内存可用。
-
模型稳定性:在某些情况下,计算输入的梯度可能会导致模型训练不稳定。因此,在使用时需要小心,并可能需要调整学习率或其他超参数。
总的来说,enable_input_require_grads()
是一个非常有用的功能,可以帮助我们在训练深度学习模型时更好地理解和优化模型。然而,使用时需要谨慎,并注意计算开销和内存消耗等问题。
cell11
from peft import LoraConfig, TaskType, get_peft_model
config = LoraConfig(
task_type=TaskType.CAUSAL_LM,
target_modules=["q_proj", "k_proj", "v_proj", "o_proj", "gate_proj", "up_proj", "down_proj"],
inference_mode=False, # 训练模式
r=8, # Lora 秩
lora_alpha=32, # Lora alaph,具体作用参见 Lora 原理
lora_dropout=0.1# Dropout 比例
)
config
这段代码使用了peft
库来配置和初始化一个LoRA(Low-Rank Adaptation)模型。LoRA是一种用于微调大型预训练模型的技术,通过低秩矩阵分解来减少计算和存储需求,同时保持模型性能。
代码解释
-
导入库:
from peft import LoraConfig, TaskType, get_peft_model
这行代码从
peft
库中导入了LoraConfig
类、TaskType
枚举和get_peft_model
函数。LoraConfig
用于配置LoRA模型,TaskType
定义了任务类型,get_peft_model
用于获取配置好的LoRA模型。 -
配置LoRA模型:
config = LoraConfig( task_type=TaskType.CAUSAL_LM, target_modules=["q_proj", "k_proj", "v_proj", "o_proj", "gate_proj", "up_proj", "down_proj"], inference_mode=False, # 训练模式 r=8, # Lora 秩 lora_alpha=32, # Lora alaph,具体作用参见 Lora 原理 lora_dropout=0.1# Dropout 比例 )
这里创建了一个
LoraConfig
对象,并设置了以下参数:task_type=TaskType.CAUSAL_LM
:指定任务类型为因果语言建模(Causal Language Modeling)。target_modules
:指定需要应用LoRA的模型模块,这里包括查询(query)、键(key)、值(value)、输出(output)、门控(gate)、上采样(up)和下采样(down)投影。inference_mode=False
:设置为False
表示当前处于训练模式。r=8
:LoRA的秩,即低秩矩阵分解的秩,决定了低秩矩阵的大小。lora_alpha=32
:LoRA的alpha参数,用于调整LoRA的影响,具体作用参见LoRA原理。lora_dropout=0.1
:Dropout比例,用于在训练过程中随机丢弃部分神经元,以防止过拟合。
-
输出配置:
config
这行代码输出配置对象,以便查看配置的详细信息。
用途
这段代码的用途是配置一个LoRA模型,用于微调大型预训练模型,特别是那些在因果语言建模任务上的模型。LoRA通过低秩矩阵分解来减少计算和存储需求,同时保持模型性能,适用于需要高效微调大型预训练模型的场景。
注意事项
- 目标模块:需要根据具体的模型架构选择合适的模块进行LoRA应用。
- 训练模式:确保在训练过程中将
inference_mode
设置为False
。 - 参数调整:
r
、lora_alpha
和lora_dropout
等参数需要根据具体任务和数据集进行调整,以达到最佳性能。 - 依赖库:确保安装了
peft
库及其依赖项,可以通过pip install peft
进行安装。
cell12
model = get_peft_model(model, config)
config #LoraConfig(peft_type=<PeftType.LORA: 'LORA'>, auto_mapping=None, base_model_name_or_path='./qwen/Qwen2-7B-Instruct', revision=None, task_type=<TaskType.CAUSAL_LM: 'CAUSAL_LM'>, inference_mode=False, r=8, target_modules={'down_proj', 'v_proj', 'gate_proj', 'up_proj', 'k_proj', 'q_proj', 'o_proj'}, lora_alpha=32, lora_dropout=0.1, fan_in_fan_out=False, bias='none', use_rslora=False, modules_to_save=None, init_lora_weights=True, layers_to_transform=None, layers_pattern=None, rank_pattern={}, alpha_pattern={}, megatron_config=None, megatron_core='megatron.core', loftq_config={}, use_dora=False, layer_replication=None)
model.print_trainable_parameters() # trainable params: 20,185,088 || all params: 7,635,801,600 || trainable%: 0.26434798934534914
cell13
args = TrainingArguments(
output_dir="./output/Qwen2_instruct_lora",
per_device_train_batch_size=1,
gradient_accumulation_steps=4,
logging_steps=10,
num_train_epochs=1,
save_steps=100, # 为了快速演示,这里设置10,建议你设置成100
learning_rate=1e-4,
save_on_each_node=True,
gradient_checkpointing=True
)
这段代码定义了一个名为 TrainingArguments
的对象,用于配置训练模型的参数。这个对象通常用于配置深度学习框架(如 Hugging Face 的 Transformers 库)中的训练过程。下面是对每个参数的详细解释:
-
output_dir="./output/Qwen2_instruct_lora"
:- 指定模型训练完成后保存的输出目录。在这个例子中,模型将会被保存到
./output/Qwen2_instruct_lora
目录下。
- 指定模型训练完成后保存的输出目录。在这个例子中,模型将会被保存到
-
per_device_train_batch_size=1
:- 每个设备(通常是 GPU 或 CPU)上的训练批次大小。这里设置为 1,意味着每个批次只包含一个样本。
-
gradient_accumulation_steps=4
:- 梯度累积步数。由于内存限制,不能一次性处理大批次的数据,因此可以将多个小批次的数据累积起来,再进行一次梯度更新。这里设置为 4,意味着每累积 4 个小批次的数据后,进行一次梯度更新。
-
logging_steps=10
:- 每隔多少步进行一次日志记录。这里设置为 10,意味着每训练 10 个批次的数据后,记录一次日志。
-
num_train_epochs=1
:- 训练的轮数。这里设置为 1,意味着模型将进行 1 轮完整的训练。
-
save_steps=100
:- 每隔多少步保存一次模型。这里设置为 100,意味着每训练 100 个批次的数据后,保存一次模型。为了快速演示,这里设置为 10,实际应用中建议设置为 100 或更大。
-
learning_rate=1e-4
:- 学习率,控制模型参数更新的步长。这里设置为 0.0001。
-
save_on_each_node=True
:- 是否在每个节点上保存模型。这里设置为 True,意味着在每个节点上都会保存模型。
-
gradient_checkpointing=True
:- 是否启用梯度检查点。梯度检查点是一种节省内存的技术,通过在反向传播过程中不保存所有中间激活值,从而减少内存使用。这里设置为 True,意味着启用梯度检查点。
用途:
这段代码的用途是配置一个深度学习模型的训练参数,以便在指定的输出目录中进行训练。通过调整这些参数,可以控制训练过程中的各种行为,如批次大小、梯度累积步数、日志记录频率、模型保存频率等。
注意事项:
per_device_train_batch_size
和gradient_accumulation_steps
的设置需要根据硬件资源(如 GPU 内存)进行调整,以避免内存溢出。save_steps
的设置应根据训练的轮数和批次大小进行调整,以平衡模型保存的频率和训练的效率。gradient_checkpointing
可以显著减少内存使用,但可能会增加计算时间,因为它需要在反向传播过程中重新计算某些中间激活值。
cell14
trainer = Trainer(
model=model,
args=args,
train_dataset=tokenized_id,
data_collator=DataCollatorForSeq2Seq(tokenizer=tokenizer, padding=True),
)
这段代码是使用Hugging Face的Transformers库中的Trainer
类来训练一个模型。下面是对这段代码的详细解释:
实现原理
Trainer
类是Hugging Face Transformers库中的一个高级API,用于简化模型训练的过程。它封装了训练过程中的许多细节,如数据加载、模型评估、优化器选择等,使得用户可以更专注于模型的设计和调优。
用途
这段代码的用途是初始化一个Trainer
对象,以便开始训练一个序列到序列(Seq2Seq)模型。Seq2Seq模型通常用于机器翻译、文本摘要等任务,其中输入序列被映射到输出序列。
参数解释
model=model
: 这是你要训练的模型实例。model
变量应该是一个已经定义好的模型对象,通常是通过加载预训练模型或自定义模型类创建的。args=args
: 这是训练的配置参数,通常是一个TrainingArguments
对象,包含了训练过程中的各种设置,如学习率、批次大小、训练轮数等。train_dataset=tokenized_id
: 这是训练数据集,tokenized_id
应该是一个已经分词并转换为模型可以处理的ID格式的数据集。分词是将文本转换为模型可以理解的数字ID序列。data_collator=DataCollatorForSeq2Seq(tokenizer=tokenizer, padding=True)
: 这是数据整理器,用于将输入数据整理成模型可以接受的格式。DataCollatorForSeq2Seq
是专门为Seq2Seq模型设计的,它将输入序列和目标序列整理成模型可以处理的格式,并且可以选择是否进行填充(padding)。
注意事项
- 模型初始化: 确保你已经正确初始化了模型,并且模型已经加载了适当的预训练权重或进行了适当的初始化。
- 数据准备: 确保你的训练数据已经被正确分词并转换为ID格式,并且数据集的大小和格式与模型的要求相匹配。
- 配置参数: 确保你的训练配置参数(
args
)已经正确设置,包括学习率、批次大小、训练轮数等,这些参数将直接影响训练的效果和效率。 - 数据整理器: 使用
DataCollatorForSeq2Seq
时,确保你已经正确初始化了tokenizer
,并且padding
参数根据你的需求进行设置。
通过这段代码,你可以快速开始训练一个序列到序列模型,而无需手动处理数据加载、模型评估等细节。
cell15
torch.backends.cuda.enable_mem_efficient_sdp(False)
这行代码是用来禁用PyTorch中的内存高效稀疏张量乘法(Memory-Efficient Sparse Tensor Multiplication, MeSparM)的。具体来说,它调用了torch.backends.cuda.enable_mem_efficient_sdp(False)
函数,将内存高效稀疏张量乘法的启用状态设置为False
。
实现原理
在PyTorch中,稀疏张量乘法是一种常见的操作,特别是在处理大规模稀疏数据时。传统的稀疏张量乘法方法虽然有效,但在处理非常大的稀疏矩阵时,内存消耗会非常大。为了解决这个问题,PyTorch引入了内存高效稀疏张量乘法,通过优化算法和内存管理,减少内存消耗,提高计算效率。
用途
内存高效稀疏张量乘法主要用于以下场景:
- 大规模稀疏矩阵计算:在处理大规模稀疏矩阵乘法时,可以显著减少内存消耗,提高计算效率。
- 深度学习模型训练:在训练深度学习模型时,尤其是处理大规模稀疏数据时,可以减少内存占用,提高训练速度。
注意事项
- 兼容性:内存高效稀疏张量乘法可能需要特定的硬件支持,如支持CUDA的GPU。
- 性能优化:虽然内存高效稀疏张量乘法可以减少内存消耗,提高计算效率,但在某些情况下,可能需要根据具体应用场景进行性能调优。
- 启用状态:通过
torch.backends.cuda.enable_mem_efficient_sdp(False)
可以禁用内存高效稀疏张量乘法,如果不需要减少内存消耗,可以保持默认设置。
总的来说,这行代码的作用是禁用PyTorch中的内存高效稀疏张量乘法,以减少内存消耗,提高计算效率。但在实际应用中,需要根据具体场景和需求来决定是否启用这一特性。
cell16
trainer.train()
这里运行完请重启notebook
cell17
from transformers import AutoModelForCausalLM, AutoTokenizer
import torch
from peft import PeftModel
mode_path = './qwen/Qwen2-7B-Instruct/'
lora_path = './output/Qwen2_instruct_lora_an/checkpoint-100' # 这里改称你的 lora 输出对应 checkpoint 地址
# 加载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 = '''你是一个逻辑推理专家,擅长解决逻辑推理问题。以下是一个逻辑推理的题目,形式为单项选择题。所有的问题都是(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}
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))
这段代码的主要目的是使用一个预训练的模型(Qwen2-7B-Instruct
)和一个LoRA(Layer-wise Relevance Analysis)权重文件(Qwen2_instruct_lora_an/checkpoint-100
)来生成一个逻辑推理问题的答案。以下是代码的详细解释:
1. 导入必要的库和模块
from transformers import AutoModelForCausalLM, AutoTokenizer
import torch
from peft import PeftModel
AutoModelForCausalLM
和AutoTokenizer
是来自transformers
库的类,用于加载和操作预训练的语言模型。torch
是 PyTorch 库,用于张量和计算。PeftModel
是用于加载和操作LoRA权重文件的类。
2. 定义模型和LoRA权重的路径
mode_path = './qwen/Qwen2-7B-Instruct/'
lora_path = './output/Qwen2_instruct_lora_an/checkpoint-100'
mode_path
是预训练模型的路径。lora_path
是LoRA权重的路径。
3. 加载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()
- 使用
AutoTokenizer
加载tokenizer。 - 使用
AutoModelForCausalLM
加载模型,并将其设置为评估模式(eval()
)。
4. 加载LoRA权重
model = PeftModel.from_pretrained(model, model_id=lora_path)
- 使用
PeftModel
加载LoRA权重到模型中。
5. 定义输入提示
prompt = '''...'''
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')
prompt
是一个逻辑推理问题的描述。- 使用
tokenizer.apply_chat_template
方法将提示转换为模型可以理解的输入格式,并将其移动到GPU上。
6. 设置生成参数并生成答案
from transformers import AutoModelForCausalLM, AutoTokenizer
import torch
from peft import PeftModel
mode_path = './qwen/Qwen2-7B-Instruct/'
lora_path = './output/Qwen2_instruct_lora_an/checkpoint-100' # 这里改称你的 lora 输出对应 checkpoint 地址
# 加载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 = '''你是一个逻辑推理专家,擅长解决逻辑推理问题。以下是一个逻辑推理的题目,形式为单项选择题。所有的问题都是(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}
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))
这段代码的主要目的是使用一个预训练的模型(Qwen2-7B-Instruct
)和一个LoRA(Layer-wise Relevance Analysis)权重文件(Qwen2_instruct_lora_an/checkpoint-100
)来生成一个逻辑推理问题的答案。以下是代码的详细解释:
1. 导入必要的库和模块
from transformers import AutoModelForCausalLM, AutoTokenizer
import torch
from peft import PeftModel
AutoModelForCausalLM
和AutoTokenizer
是来自transformers
库的类,用于加载和操作预训练的语言模型。torch
是 PyTorch 库,用于张量和计算。PeftModel
是用于加载和操作LoRA权重文件的类。
2. 定义模型和LoRA权重的路径
mode_path = './qwen/Qwen2-7B-Instruct/'
lora_path = './output/Qwen2_instruct_lora_an/checkpoint-100'
mode_path
是预训练模型的路径。lora_path
是LoRA权重的路径。
3. 加载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()
- 使用
AutoTokenizer
加载tokenizer。 - 使用
AutoModelForCausalLM
加载模型,并将其设置为评估模式(eval()
)。
4. 加载LoRA权重
model = PeftModel.from_pretrained(model, model_id=lora_path)
- 使用
PeftModel
加载LoRA权重到模型中。
5. 定义输入提示
prompt = '''...'''
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')
prompt
是一个逻辑推理问题的描述。- 使用
tokenizer.apply_chat_template
方法将提示转换为模型可以理解的输入格式,并将其移动到GPU上。
6. 设置生成参数并生成答案
gen_kwargs = {"max_length": 2500, "do_sample": True, "top_k": 1}
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))
gen_kwargs
是生成参数,包括最大长度、是否采样和top-k采样。- 使用
model.generate
方法生成答案。 - 使用
tokenizer.decode
方法将生成的输出解码为文本。 - 打印生成的答案。
注意事项
- 确保安装了所有必要的库,如
transformers
、torch
和peft
。 - 确保预训练模型和LoRA权重文件路径正确。
- 代码中的
prompt
需要根据实际逻辑推理问题进行修改。 - 生成参数可以根据需要调整,以获得更好的生成效果。
cell18
# 模型合并存储
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)
这段代码的主要目的是将一个模型合并并存储,具体步骤如下:
-
模型合并:首先,代码通过调用
model.merge_and_unload()
方法将模型进行合并。这个方法的具体实现可能涉及到将多个模型或模型的部分合并成一个完整的模型,以便后续的存储和加载。 -
指定存储目录:代码定义了一个新的目录路径
new_model_directory
,该路径为"./merged_model_an"
,用于存储合并后的模型。 -
保存模型:接着,代码使用
merged_model.save_pretrained(new_model_directory, max_shard_size="2048MB", safe_serialization=True)
方法将合并后的模型保存到指定的目录中。这里有几个关键参数:new_model_directory
:指定保存模型的目录。max_shard_size="2048MB"
:指定每个权重文件的最大大小为2048MB,这意味着模型会被分割成多个文件存储,每个文件不超过2GB。safe_serialization=True
:启用安全序列化,这通常意味着使用一种更安全的格式来保存模型,以防止数据损坏。
实现原理:
- 模型合并:合并模型可能涉及到将多个模型的权重合并到一个模型中,或者将模型的各个部分合并成一个整体。这通常是为了优化模型的大小和加载速度。
- 保存模型:保存模型时,使用
max_shard_size
参数可以将模型分割成多个文件,每个文件不超过指定的大小。这样可以避免单个文件过大,影响存储和加载效率。safe_serialization
参数则确保了保存过程的安全性。
用途:
- 这个过程通常用于将多个模型或模型的部分合并成一个完整的模型,以便于后续的模型训练、评估或部署。
- 通过将模型分割成多个文件,可以更好地管理模型的大小,避免单个文件过大导致的存储和加载问题。
注意事项:
- 确保在调用
merge_and_unload
方法时,模型已经准备好并可以进行合并。 - 在保存模型时,需要确保指定的目录有足够的权限和空间来存储分割后的多个文件。
- 使用
safe_serialization=True
可以增加数据的安全性,但可能会稍微影响保存和加载的速度。
cell19
!cp ./qwen/Qwen2-7B-Instruct/tokenizer.json ./merged_model_an/
这段代码是一条在Python环境中执行的命令,使用的是cp
命令,用于复制文件或目录。具体来说,它将当前目录下./qwen/Qwen2-7B-Instruct/tokenizer.json
文件复制到./merged_model_an/
目录下。
实现原理
cp
命令是Linux和Unix系统中的一个基本命令,用于复制文件或目录。./qwen/Qwen2-7B-Instruct/tokenizer.json
是源文件路径,表示当前目录下的qwen
文件夹中的Qwen2-7B-Instruct
文件夹中的tokenizer.json
文件。./merged_model_an/
是目标目录路径,表示当前目录下的merged_model_an
文件夹。
用途
- 这段代码的用途是将一个特定的JSON文件(
tokenizer.json
)从源目录复制到目标目录,通常用于模型训练、部署或数据预处理等场景。 - 在自然语言处理(NLP)任务中,
tokenizer.json
文件通常包含分词器的配置信息,用于将文本转换为模型可以处理的格式。
注意事项
- 确保源文件路径和目标目录路径正确无误,避免路径错误导致的文件复制失败。
- 如果目标目录不存在,
cp
命令会报错。如果需要创建目标目录,可以使用mkdir -p ./merged_model_an/
命令先创建目录。 - 在执行此命令之前,确保当前工作目录是正确的,即包含
qwen
和merged_model_an
这两个目录的父目录。
2. vllm加速
vLLM(Virtual Large Language Model)是一个由伯克利大学LMSYS组织开源的大规模语言模型高速推理框架。它的设计目标是在实时应用场景中大幅提升语言模型服务的吞吐量和内存使用效率。vLLM的特点包括易于使用、与Hugging Face等流行工具无缝集成以及高效的性能。
接下来请打开start_vllm.ipynb,执行后我们通过vllm的类openai接口成功将微调后的模型部署到8000端口。
!python -m vllm.entrypoints.openai.api_server --model ./merged_model_an --served-model-name Qwen2-7B-Instruct-lora --max-model-len=4096
3.vllm api调用
model_name用自己的名称
MODEL_NAME = 'Qwen2-7B-Instruct-lora'
我们改写了baseline中的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
vllm服务启动后,再运行baseline