AI大模型探索之路-训练篇15:大语言模型预训练之全量参数微调

news2024/12/23 20:13:53

系列篇章💥

AI大模型探索之路-训练篇1:大语言模型微调基础认知
AI大模型探索之路-训练篇2:大语言模型预训练基础认知
AI大模型探索之路-训练篇3:大语言模型全景解读
AI大模型探索之路-训练篇4:大语言模型训练数据集概览
AI大模型探索之路-训练篇5:大语言模型预训练数据准备-词元化
AI大模型探索之路-训练篇6:大语言模型预训练数据准备-预处理
AI大模型探索之路-训练篇7:大语言模型Transformer库之HuggingFace介绍
AI大模型探索之路-训练篇8:大语言模型Transformer库-预训练流程编码体验
AI大模型探索之路-训练篇9:大语言模型Transformer库-Pipeline组件实践
AI大模型探索之路-训练篇10:大语言模型Transformer库-Tokenizer组件实践
AI大模型探索之路-训练篇11:大语言模型Transformer库-Model组件实践
AI大模型探索之路-训练篇12:语言模型Transformer库-Datasets组件实践
AI大模型探索之路-训练篇13:大语言模型Transformer库-Evaluate组件实践
AI大模型探索之路-训练篇14:大语言模型Transformer库-Trainer组件实践


目录

  • 系列篇章💥
  • 前言
  • 一、预训练任务类型
  • 二、模型和数据集选择
  • 三、指令微调数据处理
  • 四、全量参数微调实践
    • 学术资源加速
    • 步骤1:导入相关包
    • 步骤2:加载数据集
    • 步骤3:数据预处理
    • 步骤4:创建模型
    • 步骤5:配置训练参数
    • 步骤6:创建训练器
    • 步骤7:模型训练
    • 步骤8:模型推理
  • 总结


前言

在自然语言处理(NLP)领域,预训练模型的应用已经越来越广泛。预训练模型通过大规模的无监督学习,能够捕捉到丰富的语言知识和上下文信息。然而,由于预训练模型通常需要大量的计算资源和时间进行训练,因此在实际使用时,我们往往需要对预训练模型进行微调,以便更好地适应特定的任务需求。本文将介绍全量参数微调的方法,以及如何在实践中进行操作。

一、预训练任务类型

1)掩码语言模型,自编码模型
将一些位置的token替换成特殊[MASK]字符,预测被替换的字符;(代表:BERT)
在这里插入图片描述

2)因果模型,自回归模型
将完整序列输入,基于上文的token预测下文的token;(代表:GPT)
在这里插入图片描述

3)序列到序列模型
采用编码器解码器的方式,预测放在解码器部分 (代表:GLM)
在这里插入图片描述

二、模型和数据集选择

目标:训练一个对话模型
模型:https://huggingface.co/Langboat/bloom-800m-zh
数据集:https://huggingface.co/datasets/c-s-ale/alpaca-gpt4-data-zh
在这里插入图片描述

三、指令微调数据处理

自回归编码指令微调数据处理过程
1)input输入构建:首先,我们将数据集中的指令(instruction),用户输入(input),以及预期输出(output)拼接成单一的字符串。这创建了一个格式为[instruction] [input] [output]的序列。
2)label标签创建:接着,为了构建训练标签,我们将用户输入部分保持不变,而对于输出部分,我们将其转化为目标标签。在掩码语言模型中,除了输出部分外,其他部分(包括指令和输入)的标签被替换为一个特殊的分隔符(例如:[SEP])加上-100,表示这部分不需要模型去预测。(前面instruction 和 input,对应部分不需要推理,采用-100填充;后面补上output)
在这里插入图片描述

四、全量参数微调实践

在自然语言处理(NLP)领域,全量参数微调(Fine-tuning)是释放预训练语言模型潜力的关键步骤。该过程涉及对大规模模型进行细微调整,以适应特定的下游任务。全量参数微调的标准流程包括:导包、加载数据集、数据预处理、创建模型、配置训练参数、创建训练器、模型训练、模型推理。

学术资源加速

方便从huggingface下载模型,这云平台autodl提供的,仅适用于autodl。

import subprocess
import os

result = subprocess.run('bash -c "source /etc/network_turbo && env | grep proxy"', shell=True, capture_output=True, text=True)
output = result.stdout
for line in output.splitlines():
    if '=' in line:
        var, value = line.split('=', 1)
        os.environ[var] = value

步骤1:导入相关包

开始之前,我们需要导入适用于模型训练和推理的必要库,如transformers。

from datasets import Dataset
from transformers import AutoTokenizer, AutoModelForCausalLM, DataCollatorForSeq2Seq, TrainingArguments, Trainer

步骤2:加载数据集

使用适当的数据加载器,例如datasets库,来加载预处理过的指令遵循性任务数据集。

ds = Dataset.load_from_disk("data/alpaca_data_zh/")
ds

输出:

Dataset({
    features: ['output', 'input', 'instruction'],
    num_rows: 26858
})

数据查看:

ds[:3]

输出

{'output': ['以下是保持健康的三个提示:\n\n1. 保持身体活动。每天做适当的身体运动,如散步、跑步或游泳,能促进心血管健康,增强肌肉力量,并有助于减少体重。\n\n2. 均衡饮食。每天食用新鲜的蔬菜、水果、全谷物和脂肪含量低的蛋白质食物,避免高糖、高脂肪和加工食品,以保持健康的饮食习惯。\n\n3. 睡眠充足。睡眠对人体健康至关重要,成年人每天应保证 7-8 小时的睡眠。良好的睡眠有助于减轻压力,促进身体恢复,并提高注意力和记忆力。',
  '4/16等于1/4是因为我们可以约分分子分母都除以他们的最大公约数4,得到(4÷4)/ (16÷4)=1/4。分数的约分是用分子和分母除以相同的非零整数,来表示分数的一个相同的值,这因为分数实际上表示了分子除以分母,所以即使两个数同时除以同一个非零整数,分数的值也不会改变。所以4/16 和1/4是两种不同的书写形式,但它们的值相等。',
  '朱利叶斯·凯撒,又称尤利乌斯·恺撒(Julius Caesar)是古罗马的政治家、军事家和作家。他于公元前44年3月15日被刺杀。 \n\n根据历史记载,当时罗马元老院里一些参议员联合起来策划了对恺撒的刺杀行动,因为他们担心恺撒的统治将给罗马共和制带来威胁。在公元前44年3月15日(又称“3月的艾达之日”),恺撒去参加元老院会议时,被一群参议员包围并被攻击致死。据记载,他身中23刀,其中一刀最终致命。'],
 'input': ['', '输入:4/16', ''],
 'instruction': ['保持健康的三个提示。', '解释为什么以下分数等同于1/4', '朱利叶斯·凯撒是如何死亡的?']}

步骤3:数据预处理

利用预训练模型的分词器(Tokenizer)对原始文本进行编码,并生成相应的输入ID、注意力掩码和标签。
1)获取分词器

tokenizer = AutoTokenizer.from_pretrained("Langboat/bloom-800m-zh")
tokenizer

在这里插入图片描述

输出:

BloomTokenizerFast(name_or_path='Langboat/bloom-800m-zh', vocab_size=46145, model_max_length=1000000000000000019884624838656, is_fast=True, padding_side='left', truncation_side='right', special_tokens={'bos_token': '<s>', 'eos_token': '</s>', 'unk_token': '<unk>', 'pad_token': '<pad>'}, clean_up_tokenization_spaces=False),  added_tokens_decoder={
	0: AddedToken("<unk>", rstrip=False, lstrip=False, single_word=False, normalized=False, special=True),
	1: AddedToken("<s>", rstrip=False, lstrip=False, single_word=False, normalized=False, special=True),
	2: AddedToken("</s>", rstrip=False, lstrip=False, single_word=False, normalized=False, special=True),
	3: AddedToken("<pad>", rstrip=False, lstrip=False, single_word=False, normalized=False, special=True),
}

2)定义数据处理函数

def process_func(example):
    MAX_LENGTH = 256  # 定义最大长度为256
    input_ids, attention_mask, labels = [], [], []  # 初始化输入ID、注意力掩码和标签列表
    # 对指令和输入进行编码,返回输入ID和注意力掩码
    instruction = tokenizer("\n".join(["Human: " + example["instruction"], example["input"]]).strip() + "\n\nAssistant: ")
    # 对输出进行编码,返回输出ID和注意力掩码
    response = tokenizer(example["output"] + tokenizer.eos_token)
    # 将指令和回应的输入ID合并
    input_ids = instruction["input_ids"] + response["input_ids"]
    # 将指令和回应的注意力掩码合并
    attention_mask = instruction["attention_mask"] + response["attention_mask"]
    # 标签列表的前半部分是指令的长度个-100(表示这些位置的标签是被忽略的),后半部分是回应的输入ID
    labels = [-100] * len(instruction["input_ids"]) + response["input_ids"]
    # 如果输入ID的长度大于最大长度,将其截断为最大长度
    if len(input_ids) > MAX_LENGTH:
        input_ids = input_ids[:MAX_LENGTH]
        attention_mask = attention_mask[:MAX_LENGTH]
        labels = labels[:MAX_LENGTH]
    # 返回一个包含输入ID列表、注意力掩码列表和标签列表的字典
    return {
        "input_ids": input_ids,
        "attention_mask": attention_mask,
        "labels": labels
    }

3)对数据进行预处理

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

输出:

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

4)数据格式检查
检查input部分

#检查一下数据格式,知识部分是否符合我们的需求
tokenizer.decode(tokenized_ds[1]["input_ids"])

输出:

'Human: 解释为什么以下分数等同于1/4\n输入:4/16\n\nAssistant: 4/16等于1/4是因为我们可以约分分子分母都除以他们的最大公约数4,得到(4÷4)/ (16÷4)=1/4。分数的约分是用分子和分母除以相同的非零整数,来表示分数的一个相同的值,这因为分数实际上表示了分子除以分母,所以即使两个数同时除以同一个非零整数,分数的值也不会改变。所以4/16 和1/4是两种不同的书写形式,但它们的值相等。</s>'

检查label部分

## 检查一下数据格式,目标值是否符合我们的需求
tokenizer.decode(list(filter(lambda x: x != -100, tokenized_ds[1]["labels"])))

输出

'4/16等于1/4是因为我们可以约分分子分母都除以他们的最大公约数4,得到(4÷4)/ (16÷4)=1/4。分数的约分是用分子和分母除以相同的非零整数,来表示分数的一个相同的值,这因为分数实际上表示了分子除以分母,所以即使两个数同时除以同一个非零整数,分数的值也不会改变。所以4/16 和1/4是两种不同的书写形式,但它们的值相等。</s>'

步骤4:创建模型

实例化一个预训练模型,它将作为我们微调的基础。

model = AutoModelForCausalLM.from_pretrained("Langboat/bloom-800m-zh")

在这里插入图片描述

步骤5:配置训练参数

定义训练参数,包括输出目录、学习率、批次大小、梯度累积步数、优化器选择等。

args = TrainingArguments(
    output_dir="/root/autodl-tmp/tuningdata/boomtuning",# 指定模型训练结果的输出目录。
    per_device_train_batch_size=4,  # 指定每个设备(如GPU)上的批次大小
    gradient_accumulation_steps=8,  # 指定梯度累积步数。在本例子中,每8个步骤进行一次梯度更新。
    logging_steps=10, #指定日志记录的频率。在本例子中,每10个步骤记录一次日志
    num_train_epochs=1 #指定训练的总轮数。在本例子中,训练将进行1轮, 实际使用是会是多轮
)

查看服务器当前GPU使用情况,大概48G

nvidia-smi

在这里插入图片描述

步骤6:创建训练器

初始化Trainer类,它封装了训练循环,并提供了一种简单的方式来运行训练和评估。

trainer = Trainer(
    model=model,#指定训练模型
    args=args, #指定训练参数
    train_dataset=tokenized_ds, #指定数据集
    data_collator=DataCollatorForSeq2Seq(tokenizer=tokenizer, padding=True) #指定数据收集器。其中tokenizer是分词器,padding=True表示对输入进行填充以保持批次大小一致。
)

data_collator在训练机器学习模型时,有以下作用:
1)数据转换:data_collator负责将输入的特征数据转换成统一形状和格式的张量(tensor),这是为了便于模型进行统一的处理。
2)批量处理:它能够将多个数据样本整合成一个小批次(batch)的数据,这有助于提高模型训练的效率。
3)填充(padding):在文本处理中,不同大小的输入需要被填充或截断到相同的长度以形成统一的形状,这对于很多自然语言处理模型来说是必要的,而DataCollatorWithPadding就是执行这一操作的常用collator。

综上所述,data_collator是连接数据处理与模型训练之间的重要桥梁,确保了数据的有效整理和组织,以便模型可以高效地从中学习。

步骤7:模型训练

模型训练资源估算:
1)模型大小:4G(模型参数数量是8亿,按10亿估算,如果参数使用32位浮点数,每个参数需要4字节的空间。因此,模型大小差不多是4G)
2)梯度大小:4G(梯度的大小通常与模型大小相同,因为它们是针对每个参数计算的。所以,如果模型大小是4GB,梯度大小也应该是4GB)
3)优化器状态:4G * 2 = 8G (优化器状态的大小取决于使用的优化器类型和其内部参数的数量。Adam优化器通常会为每个模型参数维护两个额外的向量。因此,如果模型大小是4GB,优化器状态可能需要额外的8GB,每个参数两个额外的值。)
4)其他开销:还需要考虑数据加载、中间变量、系统进程等其他内存开销。
大约需要16G,实际资源则要比16G多一点

调用train()方法启动训练过程。

trainer.train()

在这里插入图片描述

训练后查看GPU在这里插入图片描述

步骤8:模型推理

使用pipeline进行推理,展示模型的能力。

from transformers import pipeline

pipe = pipeline("text-generation", model=model, tokenizer=tokenizer, device=0)
ipt = "Human: {}\n{}".format("如何写好一个简历?", "").strip() + "\n\nAssistant: "
pipe(ipt, max_length=256, do_sample=True, )

输出

[{'generated_text': 'Human: 如何写好一个简历?\n\nAssistant: 在撰写一份简历时,需要考虑很多细节。以下是一些关键因素:\n\n1. 结构清晰:将信息组织在一个清晰的的结构里。例如,使用一个目录或列表来将信息组织。避免使用令人反感的混乱结构,例如使用树、流程图或层次结构。\n\n2. 内容丰富:列出最重要的信息。包括关键字和关键问题,以吸引招聘师的注意。此外,可以使用大量的数据和图表来增强整张简历的信息性。\n\n3. 恰当的背景信息:用适当的背景信息进行补充和修饰,例如加入家庭信息、教育经历或工作经验等。可以提供更加个性化和关键性的背景信息来帮助招聘师更好地了解你的工作技能和能力。'}]

总结

在当今的自然语言处理领域,全量参数微调(Fine-tuning)已成为释放大型预训练语言模型潜力的关键技术手段。然而,随着模型规模的扩大,这一过程对计算资源的要求也急剧上升。

在本次实践中,我们选择了一个拥有约8亿参数的模型进行全量参数微调。这一规模级别的模型,通常需要大约18GB的显存资源。尽管这在现代硬件上是可行的,但当模型规模扩大到80亿参数时,所需的显存将飙升至180GB,这种级别的资源消耗通常只有资金雄厚的企业才能承担。

进一步地,如果模型的规模达到惊人的800亿参数,那么所需的显存将达到庞大的1800GB。如此巨大的资源需求,即便是对于许多公司而言,也是一项极具挑战的任务,往往超出了他们的能力范围。因此,尽管全量参数微调在技术上可行,但在实际的应用和研究中,由于资源的限制,这样的实践相对较少。

鉴于此,大多数实际应用场景倾向于采用部分微调(Partial Fine-tuning)或迁移学习,这样既可以利用预训练模型中的知识,又可以针对性地调整模型以适应特定任务,而无需动用如全量微调那般庞大的计算资源。这种方法更加经济高效,同时也能产生令人满意的结果。

在这里插入图片描述

🎯🔖更多专栏系列文章:AIGC-AI大模型探索之路

如果文章内容对您有所触动,别忘了点赞、⭐关注,收藏!加入我,让我们携手同行AI的探索之旅,一起开启智能时代的大门!

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

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

相关文章

.Net 8.0 下的新RPC,IceRPC之如何创建连接connection

作者引言 很高兴啊&#xff0c;我们来到了IceRPC之如何创建连接connection,基础引导&#xff0c;让自已不在迷茫&#xff0c;快乐的畅游世界。 如何创建连接connection 学习如何使用IceRPC&#xff0c;创建和接受连接。 连接有什么用途&#xff1f; 连接在 IceRPC 中发挥着核心…

深度学习中的注意力机制一(Pytorch 15)

一 简介 灵长类动物的视觉系统接受了大量的感官输入&#xff0c;这些感官输入远远超过了大脑能够完全处理的程度。然而&#xff0c; 并非所有刺激的影响都是相等的。意识的聚集和专注使灵长类动物能够在复杂的视觉环境中将注意力引向感 兴趣的物体&#xff0c;例如猎物和天敌。…

Baidu Comate智能代码助手-高效代码编程体验

关于Baidu Comate智能代码助手 智能代码助手简介 代码助手可以快速的帮我们补充代码&#xff0c;修改代码&#xff0c;添加注释&#xff0c;翻译中英文&#xff0c;起变量函数名字等操作&#xff0c;十分的友好&#xff0c;这类代码助手现阶段有较多的产品&#xff0c;比如&a…

深度学习-线性回归+基础优化算法

目录 线性模型衡量预估质量训练数据参数学习训练损失最小化损失来学习参数显式解 总结基础优化梯度下降选择学习率 小批量随机梯度下降选择批量大小 总结线性回归的从零开始实现实现一个函数读取小批量效果展示这里可视化看一下 线性回归从零开始实现线性回归的简洁实现效果展示…

【C++】学习笔记——vector_3

文章目录 七、vector3. vector的模拟实现4. vector实现代码整合 未完待续 七、vector 3. vector的模拟实现 上篇文章我们讲解了非常 玄幻 的拷贝构造函数&#xff0c;同样的方法&#xff0c;我们也能用这种方法来实现 赋值重载函数 。 void swap(vector<T>& v) {s…

哈尔滨等保测评模拟题

《中华人民共和国网络安全法》中规定计算机信息系统安全保护等级共分为五级。&#xff08; t &#xff09;2019年&#xff0c;随着标准GB/T22239的更新&#xff0c;等级保护进入2.0时代。&#xff08; t&#xff09;某单位信息系统的等级定义为S2A3G2&#xff0c;根据取高原则…

自学Python爬虫js逆向(二)chrome浏览器开发者工具的使用

js逆向中很多工作需要使用浏览器中的开发者工具&#xff0c;所以这里以chrome为例&#xff0c;先把开发者工具的使用总结一下&#xff0c;后面用到的时候可以回来查询。 Google Chrome浏览器的开发者工具是前端开发者的利器&#xff0c;它不仅提供了丰富的功能用于开发、调试和…

实验九 Java 语言网络通信程序设计练习(课内实验)

一、实验目的 本次实验的主要目的是练习网络通信程序的设计方法&#xff0c;并掌握计算机网络基 础知识、Java语言网络通信程序类库的结构和使用方法。 二、实验要求 1. 认真阅读实验内容&#xff0c;完成实验内容所设的题目。 2. 能够应用多种编辑环境编写Java语言源程序…

如何使用DEEPL免费翻译PDF

如何使用DEEPL免费翻译PDF 安装DEEPL取消PDF限制 安装DEEPL 安装教程比较多&#xff0c;这里不重复。 把英文pdf拖进去&#xff0c;点翻译&#xff0c;在下面的框中有已经翻译完毕的文档。 但是存在两个问题 问题1&#xff1a;这些文档是加密的。 问题2&#xff1a;带有DeepL标…

去掉antd design vue组件库中表格的分割线

前言&#xff1a; 最近在公司完成UI设计稿的时候&#xff0c;需要使用antd design vue组件库中表格&#xff0c;但是UI设计稿中并不需要表格分割线&#xff0c;尝试了多种方式最后实现如下&#xff1a; <style lang"scss" scoped>::v-deep .ant-table-cell::…

Django中如何让页面之间建立关系

今天给大家讲解两种让页面建立联系的方式 一、重定向 二、表单提交 先看第一种方式&#xff0c;重定向 首先需要了解客户端发起请求的过程 1、客户端向服务端发起请求,比如请求地址是&#xff1a;http://127.0.0.1:8000/lili/submit/ 2、程序根据路由找到视图函数 3、执行视…

汽车 - 什么是车轮抱死

车轮抱死分为两种情况&#xff0c;一种是车辆故障层面&#xff0c;另一种是驾驶过程中的物理现象。我们先来说最通俗的刹车车轮抱死吧。 刹车制动车轮抱死 车轮停止轴向转动就是抱死&#xff0c;有速度的情况下抱死车轮&#xff0c;如果车辆的惯性动能大于轮胎抓地力&#xff0…

How Linux Works I - How Linux Start Up

目录 Linux如何启动&#xff1f; 启动信息 内核启动初始化与启动选项 写在前面&#xff1a;上一个专栏中我写完了内核源码层面看Linux&#xff0c;我们把抽象层拉高一点&#xff0c;看看Linux是如何工作的&#xff01; Linux如何启动&#xff1f; BIOS&#xff08;Basic Inpu…

CSS探索之旅:定位

前言 欢迎来到我的博客 个人主页:北岭敲键盘的荒漠猫-CSDN博客 本文我们详细介绍 css中定位的相关知识点 定位的用处 先简单认识一下定位是做什么的。 其实&#xff0c;定位的功能就像他的名字一样&#xff0c;可以规定显示在网页的一个位置。 其他布局的效果 我们之前默认…

C语言:环形链表

1.例子1&#xff1a;环形链表 142. 环形链表 II - 力扣&#xff08;LeetCode&#xff09; 思路&#xff1a;我们先定义两个变量slow和fast&#xff0c;slow每次走一步&#xff0c;fast每次走两步&#xff0c;如果链表是环形链表&#xff0c;那么必定存在fast不会走到链表的最后…

利用github pages建立Serverless个人博客

利用github pages建立Serverless个人博客 概述 使用github pages&#xff0c;可以在github上部署静态网站。利用这个功能&#xff0c;可以很方便地实现个人博客的发布托管。 比如我的个人博客&#xff1a;Buttering’s Blog 对应代码仓库&#xff1a;buttering/EasyBlog: 自…

从一到无穷大 #26 Velox:Meta用cpp实现的大一统模块化执行引擎

本作品采用知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议进行许可。 本作品 (李兆龙 博文, 由 李兆龙 创作)&#xff0c;由 李兆龙 确认&#xff0c;转载请注明版权。 文章目录 引言业务案例PrestoSparkXStreamDistributed messaging systemData IngestionData Pr…

【软件测试理论001】认识软件测试、测试分类、测试原则

目录 1 认识软件测试 1.1 什么是软件 1.2 什么是软件测试 1.3 软件测试的发展历程 1.4 为什么要进行软件测试 1.5 软件测试职业规划 2 软件测试的分类 2.1 按开发阶段划分 单元测试&#xff08;Unit Testing&#xff09; 集成测试&#xff08;Integration Testing&am…

【ChatGPT with Date】使用 ChatGPT 时显示消息时间的插件

文章目录 1. 介绍2. 使用方法2.1 安装 Tampermonkey2.2 安装脚本2.3 使用 3. 配置3.1 时间格式3.2 时间位置3.3 高级配置(1) 生命周期钩子函数(2) 示例 4. 反馈5. 未来计划6. 开源协议7. 供给开发者自定义修改脚本的文档7.1 项目组织架构7.2 定义新的 Component(1) 定义一个新的…

Vue2——前端笔记

Vue 一、Vue核心1.1、vue简介1.2、初始vue1.3、模板语法1.4、数据绑定1.5、el与data的两种写法1.6、MVVM模型1.7、Vue中的数据代理1.7.1、Object.defineProperty() 理解1.7.2、Vue中的数据代理 1.8、事件处理1.8.1、事件的基本用法1.8.2、事件修饰符1.8.3、键盘事件 1.9、计算属…