大语言模型之十五-预训练和监督微调中文LLama-2

news2025/1/11 21:05:34

这篇博客是继《大语言模型之十二 SentencePiece扩充LLama2中文词汇》、《大语言模型之十三 LLama2中文推理》和《大语言模型之十四-PEFT的LoRA》
前面博客演示了中文词汇的扩充以及给予LoRA方法的预训练模型参数合并,并没有给出LoRA模型参数是如何训练得出的。
本篇博客将分析LoRA模型是和训练得到的。还是以7B参数量的模型为例。本篇博客依然基于Chinese-LLaMA-Alpaca-2开源项目。

pre-train

deepspeed

LLM的训练成本较大,需要昂贵的多卡多节点GPU集群,即使拥有集群GPU训练效率往往也达不到50%,各大小公司想要更轻松、快速、经济的训练和部署私有的LLM,微软开发的deepspeed框架就是一个高效、且易于使用的开源深度学习优化库。它可以通过多种技术方法来加速训练,包括模型并行化、梯度积累、动态精度缩放、本地模式混合精度等,此外,DeepSpeed还提供了一些辅助工具,如分布式训练管理、内存优化和模型压缩等,DeepSpeed基于Pytorch构建,迁移成本低,已经在很多项目和公司中得到应用,包括大语言模型、图像生成等。

2.1 ZeRO(零冗余优化器)
零冗余优化器(Zero Redundancy Optimizer,缩写为Zero)是一种用于大规模分布式深度学习的新型内存优化技术。ZeRO可以在当前一代GPU集群上以当前最佳系统吞吐量的三到五倍的速度训练具有1000亿个参数的深度学习模型。它还为训练具有数万亿参数的模型提供了一条清晰的道路,展示了深度学习系统技术的前所未有的飞跃。ZeRO作为DeepSpeed的一部分,用于提高显存效率和计算效率。
ZeRO可以克服数据并行和模型并行的局限性,同时实现两者的优点。通过在数据并行进程之间划分模型状态参数、梯度和优化器状态来消除数据并行进程中的内存冗余,而不是复制它们。在训练期间使用动态通信调度来在分布式设备之间共享必要的状态,以保持数据并行的计算粒度和通信量。
ZeRO的三个阶段以及对应功能。
ZeRO有三个主要的优化阶段(如下图所示),它们对应于优化器状态、梯度和参数的划分。
1.Optimizer State Partitioning(Pos):减少4倍内存,通信量与数据并行性相同
2.添加梯度分区(Pos+g):减少8倍内存,通信量与数据并行性相同
3.添加参数分区(Pos+g+p):内存减少与数据并行度Nd呈线性关系。例如,在64个GPU(Nd=64)之间进行拆分将产生64倍的内存缩减。通信量有50%的适度增长。
ZeRO消除了内存冗余,并使集群的全部聚合内存容量可用。在启用所有三个阶段的情况下,ZeRO可以在1024个NVIDIA GPU上训练万亿参数模型。像Adam这样具有16位精度的优化器的万亿参数模型需要大约16 TB的内存来保存优化器的状态、梯度和参数。16TB除以1024是16GB,这对于GPU来说是在合理的范围内的。

优化1: ZeRO2
它扩展了ZeRO-1,包括减少梯度内存占用,同时还添加了针对激活内存和碎片内存的优化。与ZeRO-1相比,ZeRO-2将DeepSpeed可以训练的模型大小增加了一倍,同时显著提高了训练效率。使用ZeRO-2,1000亿参数模型的训练速度可以比仅基于模型并行性的现有技术快10倍。
优化2:Zero-3 offload
ZeRO-3 offload是ZeRO Stage 3和ZeRO offload相结合的一种高效且易于使用的实施方式,旨在通过向每个人提供高效的大规模深度学习训练来实现人工智能民主化的持续目标。ZeRO-3 offload的主要好处是:
极高的内存效率,可以在有限的GPU资源上运行非常大的模型-例如,在单个GPU上具有超过40B的参数,在512个GPU上具有2万亿的参数的微调模型。
极易使用
扩展到超过一万亿个参数,而不需要以复杂的方式组合多种并行技术。
对于现有的DeepSpeed用户,只需在DeepSpeedConfig文件中使用几个标志即可打开ZeRO-3卸载。
每个GPU的高性能吞吐量和跨GPU的超线性可扩展性,用于分布式训练。
使用1万亿参数,ZeRO-3 Offload在512个NVIDIA V100 GPU上的计算性能可维持25 PetaFlops,实现49 TFlop/GPU。

Chinese-LLaMA-Alpaca-2

预训练的所有脚本都位于Chinese-LLaMA-Alpaca-2/scripts/training/目录下,这里仅仅解释相关代码的作用,由于需要的GPU资源较多,欢迎投喂高Memory资源的GPU资源。

#学习率
lr=2e-4
#这对应于大语言模型之十四-PEFT的LoRA中B和A矩阵的秩r
lora_rank=64
#这是超参数,用于控制学习的程度
lora_alpha=128
#这里显示了学习的参数量,可以和《大语言模型之七- Llama-2单GPU微调SFT》这里的设置对比一下
lora_trainable="q_proj,v_proj,k_proj,o_proj,gate_proj,down_proj,up_proj"
modules_to_save="embed_tokens,lm_head"
lora_dropout=0.05

pretrained_model=../merged_chinese_llama_7b/
chinese_tokenizer_path=../tokenizer/
dataset_dir=../dataset
data_cache=temp_data_cache_dir
per_device_train_batch_size=1
gradient_accumulation_steps=8
block_size=512
output_dir=output_dir

#deepspeed这里目的是单机多卡加速
deepspeed_config_file=ds_zero2_no_offload.json

torchrun --nnodes 1 --nproc_per_node 1 run_clm_pt_with_peft.py \
    --deepspeed ${deepspeed_config_file} \
    --model_name_or_path ${pretrained_model} \
    --tokenizer_name_or_path ${chinese_tokenizer_path} \
    --dataset_dir ${dataset_dir} \
    --data_cache_dir ${data_cache} \
    --validation_split_percentage 0.001 \
    --per_device_train_batch_size ${per_device_train_batch_size} \
    --do_train \
    --seed 4 \
    --fp16 \
    --num_train_epochs 1 \
    --lr_scheduler_type cosine \
    --learning_rate ${lr} \
    --warmup_ratio 0.05 \
    --weight_decay 0.01 \
    --logging_strategy steps \
    --logging_steps 10 \
    --save_strategy steps \
    --save_total_limit 3 \
    --save_steps 200 \
    --gradient_accumulation_steps ${gradient_accumulation_steps} \
    --preprocessing_num_workers 8 \
    --block_size ${block_size} \
    --output_dir ${output_dir} \
    --overwrite_output_dir \
    --ddp_timeout 30000 \
    --logging_first_step True \
    --lora_rank ${lora_rank} \
    --lora_alpha ${lora_alpha} \
    --trainable ${lora_trainable} \
    --lora_dropout ${lora_dropout} \
    --modules_to_save ${modules_to_save} \
    --torch_dtype float16 \
    --load_in_kbits 16 \
    --gradient_checkpointing \
    --ddp_find_unused_parameters False

其运行之后生成tokenize化的数据集,其中的一段输出如下:
请添加图片描述
其上shell调用的python脚本是run_clm_pt_with_peft.py

    with training_args.main_process_first(desc="dataset map tokenization and grouping"):
        lm_datasets = []
        path = Path(data_args.dataset_dir)
        files = [file.name for file in path.glob("*.txt")]
        if training_args.debug_mode is True:
            files = [files[0]]
        for idx, file in enumerate(files):
            data_file = os.path.join(path, file)
            filename = ''.join(file.split(".")[:-1])
            cache_path = os.path.join(data_args.data_cache_dir, filename+f"_{block_size}")
            os.makedirs(cache_path, exist_ok=True)
            try:
                processed_dataset = datasets.load_from_disk(cache_path, keep_in_memory=False)
                logger.info(f'training datasets-{filename} has been loaded from disk')
            except Exception:
                cache_dir = os.path.join(data_args.data_cache_dir, filename+f"_text_{block_size}")
                os.makedirs(cache_dir, exist_ok=True)
                raw_dataset = load_dataset("text", data_files=data_file, cache_dir=cache_dir, keep_in_memory=False)
                logger.info(f"{file} has been loaded")
                tokenized_dataset = raw_dataset.map(
                    tokenize_function,
                    batched=True,
                    num_proc=data_args.preprocessing_num_workers,
                    remove_columns="text",
                    load_from_cache_file=True,
                    keep_in_memory=False,
                    cache_file_names = {k: os.path.join(cache_dir, 'tokenized.arrow') for k in raw_dataset},
                    desc="Running tokenizer on dataset",
                )
                grouped_datasets = tokenized_dataset.map(
                    group_texts,
                    batched=True,
                    num_proc=data_args.preprocessing_num_workers,
                    load_from_cache_file=True,
                    keep_in_memory=False,
                    cache_file_names = {k: os.path.join(cache_dir, 'grouped.arrow') for k in tokenized_dataset},
                    desc=f"Grouping texts in chunks of {block_size}",
                )
                processed_dataset = grouped_datasets
                processed_dataset.save_to_disk(cache_path)
            if idx == 0:
                lm_datasets = processed_dataset['train']
            else:
                assert lm_datasets.features.type == processed_dataset["train"].features.type
                lm_datasets = concatenate_datasets([lm_datasets, processed_dataset["train"]])
        lm_datasets = lm_datasets.train_test_split(test_size = data_args.validation_split_percentage)

在生成好中文训练集之后,接下来是加载转换好的Huggingface模型,

        device_map = {"":int(os.environ.get("LOCAL_RANK") or 0)}
        model = LlamaForCausalLM.from_pretrained(
            model_args.model_name_or_path,
            from_tf=bool(".ckpt" in model_args.model_name_or_path),
            config=config,
            cache_dir=model_args.cache_dir,
            revision=model_args.model_revision,
            use_auth_token=True if model_args.use_auth_token else None,
            torch_dtype=torch_dtype,
            low_cpu_mem_usage=True,
            device_map=device_map,
            load_in_4bit=load_in_4bit,
            load_in_8bit=load_in_8bit,
            quantization_config=quantization_config,
        )

模型加载好之后,设置PEFT训练参数,这些参数值来源于最开始的shell脚本。

        logger.info("Init new peft model")
        target_modules = training_args.trainable.split(',')
        modules_to_save = training_args.modules_to_save
        if modules_to_save is not None:
            modules_to_save = modules_to_save.split(',')
        lora_rank = training_args.lora_rank
        lora_dropout = training_args.lora_dropout
        lora_alpha = training_args.lora_alpha
        logger.info(f"target_modules: {target_modules}")
        logger.info(f"lora_rank: {lora_rank}")
        peft_config = LoraConfig(
            task_type=TaskType.CAUSAL_LM,
            target_modules=target_modules,
            inference_mode=False,
            r=lora_rank, lora_alpha=lora_alpha,
            lora_dropout=lora_dropout,
            modules_to_save=modules_to_save)
        model = get_peft_model(model, peft_config)

紧接着就可以训练了

    # Initialize our Trainer
    trainer = Trainer(
        model=model,
        args=training_args,
        train_dataset=train_dataset if training_args.do_train else None,
        eval_dataset=eval_dataset if training_args.do_eval else None,
        tokenizer=tokenizer,
        data_collator=fault_tolerance_data_collator,
        compute_metrics=compute_metrics if training_args.do_eval and not is_torch_tpu_available() else None,
        preprocess_logits_for_metrics=preprocess_logits_for_metrics
        if training_args.do_eval and not is_torch_tpu_available()
        else None,
    )
    trainer.add_callback(SavePeftModelCallback)
    # Training
    if training_args.do_train:
        checkpoint = None
        if training_args.resume_from_checkpoint is not None:
            checkpoint = training_args.resume_from_checkpoint
        elif last_checkpoint is not None:
            checkpoint = last_checkpoint
        train_result = trainer.train(resume_from_checkpoint=checkpoint)

        metrics = train_result.metrics

        max_train_samples = (
            data_args.max_train_samples if data_args.max_train_samples is not None else len(train_dataset)
        )
        metrics["train_samples"] = min(max_train_samples, len(train_dataset))

        trainer.log_metrics("train", metrics)
        trainer.save_metrics("train", metrics)
        trainer.save_state()

SFT和这里的预训练代码结构上非常相似,只是训练集变成有监督的了,因而差异主要在于有监督训练集的构建。这通过字典的方式获得的,具体代码见build_dataset.py

@dataclass
class DataCollatorForSupervisedDataset(object):
    """Collate examples for supervised fine-tuning."""

    tokenizer: transformers.PreTrainedTokenizer

    def __call__(self, instances: Sequence[Dict]) -> Dict[str, torch.Tensor]:
        input_ids, labels = tuple([instance[key] for instance in instances] for key in ("input_ids", "labels"))
        input_ids = torch.nn.utils.rnn.pad_sequence(
            input_ids, batch_first=True, padding_value=self.tokenizer.pad_token_id
        )
        labels = torch.nn.utils.rnn.pad_sequence(labels, batch_first=True, padding_value=-100)
        return dict(
            input_ids=input_ids,
            labels=labels,
            attention_mask=input_ids.ne(self.tokenizer.pad_token_id),
        )

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

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

相关文章

网络安全考研院校推荐

考研预报名开始了 大部分院校也出来了考纲 本篇文章盘点一下网络空间安全(080911TK) 考研院校推荐 以下仅供参考,详情可去学校官网查看招生简章 2023一志愿录取分数线 院校 录取最低分 录取平均分 南昌大学 279 304 河北师…

23.2 Bootstrap 卡片

1.卡片 1.1卡片样式 在Bootstrap 5中, .card, card-header, .card-body, .card-footer类是用于创建卡片样式.下面是这些类的简单介绍: * 1. .card: 用于创建一个基本的卡片容器它作为一个包裹元素,通常与其他卡片类一起使用.* 2. .card-header: 用于创建卡片的头部部分.通常在…

IDEA Rogstry中找不到compiler.automake.allow.when.app.running问题解决

网上大部分人教我们 先 File > Settings 然后 勾选 Build 下的 Compiler中的 Build project automatically 这些步骤都不会有问题 然后就会让我们 ctrl shift alt / 点 Rogstry 打开后 我人就麻了 根本没有什么 compiler.automake.allow.when.app.running 也不用慌 我们…

lv7 嵌入式开发-网络编程开发 03 TCP/IP与五层体系结构

目录 1 TCP/IP协议族体系结构 1.1 OSI与TCP/IP 1.2 TCP/IP 的体系结构 1.3 TCP/IP 体系结构的另一种表示方法 1.4 沙漏计时器形状的 TCP/IP 协议族 2 五层协议的体系结构 2.1 各层的主要功能 2.2 互联网中客户-服务器工作方式 2.3 同时为多个客户进程提供服务 3 练…

WebSocket实战之六心跳重连机制

一、前言 WebSocket应用部署到生产环境,我们除了会碰到因为经过代理服务器无法连接的问题(注:该问题可以通过搭建WSS来解决,具体配置请看 WebSocket实战之四WSS配置 ),另外一个问题就是外网环境不稳定经常…

力扣刷题-哈希表-三数之和

15 三数之和 给你一个包含 n 个整数的数组 nums,判断 nums 中是否存在三个元素 a,b,c ,使得 a b c 0 ?请你找出所有满足条件且不重复的三元组。 注意: 答案中不可以包含重复的三元组。 示例&#xff1a…

Collagen

\ collagen XV/XVIII, Endostatin- angiogenesis inhibitor; c-type lectin 结构; TSP ( 含有 Laminin-G)

asp.net core 远程调试

大概说下过程: 1、站点发布使用Debug模式 2、拷贝到远程服务器,以及iis创建站点。 3、本地的VS2022的安装目录:C:\Program Files\Microsoft Visual Studio\2022\Professional\Common7\IDE下找Remote Debugger 你的服务器是64位就拷贝x64的目…

WOL唤醒配置(以太网、PHY、MAC)

目录 wol 以太网 MAC PHY RMII 通信配置 总结 wol Wake-on-LAN简称WOL,WOL(网络唤醒) 是一种标准网络协议,它的功效在于让已经进入休眠状态或关机状态的计算机,透过局域网(多半为以太网&#xff…

Vue项目搭建图文详解教程

版权声明 本文原创作者:谷哥的小弟作者博客地址:http://blog.csdn.net/lfdfhl 预备工作 请在本地创建文件夹用于存放Vue项目,例如:创建HelloWorld文件夹存放即将创建的Vue新项目。 创建Vue项目 首先,请在DOS中将目录…

多线程(如何理解pthread库)

上一节,我们主要介绍了pthread库中一些常见函数的用法,这节我们主要分析一下pthread库到底是什么? 什么是库 我们之前提过,在每一个linux平台下,必定会存在对应的pthread库 它存在于/lib64这个路径底下 换句话说&am…

Android逆向学习(五)app进行动态调试

Android逆向学习(五)app进行动态调试 一、写在前面 非常抱歉鸽了那么久,前一段时间一直在忙,现在终于结束了,可以继续更新android逆向系列的,这个系列我会尽力做下去,然后如果可以的话我看看能…

jvm 参数配置

查看当前jvm配置参数的值 jsp查看所有的jvm端口 jinfo -flag 参数(XX:后面的) JIT配置 -XX:CompileThreshold在方法调用的默认阈值在客户端1500次,在服务器端10000次。 -XX:-UseCounterDecay用来关闭热度衰减。 -XX:CounterHalfLifeTime设置半衰减的时间&#x…

Ubuntu中启动HDFS后没有NameNode解决办法

关闭进程: stop-dfs.sh 格式化: hadoop namenode -format 出现报错信息: 23/10/03 22:27:04 WARN fs.FileUtil: Failed to delete file or dir [/usr/data/hadoop/tmp/dfs/name/current/fsimage_0000000000000000000.md5]: it still exi…

我的企业证书是正常的但是下载应用app到手机提示无法安装“app名字”无法安装此app,因为无法验证其完整性解决方案

我的企业证书是正常的但是下载应用app到手机提示无法安装“app名字”无法安装此app,因为无法验证其完整性解决方案 首先,确保您从可信任的来源下载并安装企业开发者签名过的应用程序。如果您不确定应用程序的来源,建议您联系应用程序提供者…

大数据-玩转数据-Flink 海量数据实时去重

一、海量数据实时去重说明 借助redis的Set,需要频繁连接Redis,如果数据量过大, 对redis的内存也是一种压力;使用Flink的MapState,如果数据量过大, 状态后端最好选择 RocksDBStateBackend; 使用布隆过滤器,…

快速选择排序

"你经过我每个灿烂时刻,我才真正学会如你般自由" 前些天有些无聊,想试试自己写的快排能否过leetcode上的排序算法题。结果是,不用截图可想而知,肯定是没过的,否则也不会有这篇文章的产出。 这份快排算法代码…

请求转发与请求作用域

创建input.jsp页面,通过表单输入学号、姓名后,单击登录按钮,控制转发到FirstServlet对其进行处理,然后通过请求对象的getRequestDispartcher()获得RequestDispartcher对象,将请求转发至SecondServlet,在Sec…

辅助驾驶功能开发-测试篇(2)-真值系统介绍

1 真值系统概述 1.1 真值评测系统核心应用 快速构建有效感知真值,快速完成感知性能评估,快速分析感知性能缺陷。 主要应用场景包括: 1. 感知算法开发验证: 在算法开发周期中,评测结果可以作为测试报告的一部分,体现算法性能的提升。 2. 遴选供应…

九、2023.10.3.Linux(end).9

文章目录 33、简述mmap的原理和使用场景?34、互斥量能不能在进程中使用?35、协程是轻量级线程,轻量级表现在哪里?36、说说常见信号有哪些,表示什么含义?37、说说线程间通信的方式有哪些?38、说说…