NLP(19)--大模型发展(3)

前言

仅记录学习过程,有问题欢迎讨论

大模型训练相关知识:

问题:

  1. 数据集过大,快速训练
  2. 模型过大,gpu跑不完

方案:

  • 数据并行训练:
    复制数据(batch_size)到多个gpu,各自计算loss,再反传更新;至少需要一张卡能训练一个样本
  • 模型并行:
    模型的不同层放到不同的gpu上,解决了单卡不够大的问题,但是需要更多的通讯时间了(一次训练需要传播很多次)【时间换空间】
  • 张量并行:
    将张量划分到不同的gpu上(张量左右分割),进一步减少对单卡的需求,【时间换空间】
    可以混合使用

浮点精度损失:
float32:
使用32位二进制来表示一个浮点数。第一位用于表示符号位(正或负),
接下来的八位用于表示指数,剩下的23位用于表示尾数或分数部分。Float32的数值范围大约在±3.4E+38之间
0.2 = 0.00110(B)~、
DeepSpeed(零冗余优化器)

优化训练的方案

ZeRo:

  • 相当于张量并行,gradients,parameters,分散到不同gpu计算。速度变慢
    ZeRo -offload
    添加内存帮助显卡计算哈哈

大模型的蒸馏: 小模型直接学习文本数据之外,还学习大模型预测的结果
大模型的剪枝: 删除大模型中不重要的部分,减少模型大小

PEFT微调:(当大模型对某个方面表现不好)

  • 预训练模型+微调数据集
    原始权重不动,增加一部分可训练的权重,去结合之前的部分

prompt tuning:

  • 预训练模型+提示词(提前告诉模型需要训练什么)(添加在input data)

P-tuning V2:训练虚拟token(添加在emb)

  • 该方法将 Prompt 转换为可以学习的 Embedding 层,并用MLP+LSTM的方式来对Prompt Embedding进行一层处理。

Adapter: 新增可训练的层(添加在fft后)

LoRa(目前比较流行):

  • 通过低秩分解来模拟参数的改变量,从而以极小的参数量来实现大模型的间接训练(最后再扩大)。

RAG(Retrieve Augmented Generation):

  • 模型幻觉问题,遇见不知道的问题,会乱答
    召回部分和Prompt相似的段落,重新输入模型,获取可靠答案。

优势(主要就这两条):
(1)可扩展性:减少模型大小和训练成本,并能够快速扩展知识。

(2)准确性:模型基于事实进行回答,减少幻觉的发生。

难点
(1)数据标注:需要对数据进行标注,以提供给模型进行检索。

(2)检索算法:需要设计高效的检索算法,以提高检索的效率和准确率。
在这里插入图片描述

BPE(Byte pair encoding):压缩算法

  • RAG针对词表vocab的以下问题
    1.测试过程出现词表没有的词
    2.词表过大
    3.不同语种,切分粒度不同

  • 如aaabdaaaabc ==> XdXac
    在nlp中,针对语料中出现的重复词,添加到vocab中,再切分,再添加

  • BPE通过构建跨语言共享的子词词汇表,提高了模型处理多种语言的能力,有效解决了不同语言间词汇差异大、低频词和OOV问题,增强了模型的泛化能力和翻译性能。

代码:

使用lora对大模型进行微调:
使用的是序列标注的ner代码(主要改动就在main,加了一个配置在config.py)
“tuning_tactics”: “lora_tuning”
微调一下,速度和正确率都上升很多哎!!!
main.py

# -*- coding: utf-8 -*-

import torch
import os
import random
import os
import numpy as np
import logging
from config import Config
from model import TorchModel, choose_optimizer
from evaluate import Evaluator
from loader import load_data
from peft import get_peft_model, LoraConfig, TaskType, PromptEncoderConfig, PromptTuningConfig, PrefixTuningConfig

logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(name)s - %(levelname)s - %(message)s')
logger = logging.getLogger(__name__)

"""
模型训练主程序
"""


def peft_wrapper(model):
    peft_config = LoraConfig(
        r=8,
        lora_alpha=32,
        lora_dropout=0.1,
        target_modules=["query", "value"]
    )
    return get_peft_model(model, peft_config)


def main(config):
    # 创建保存模型的目录
    if not os.path.isdir(config["model_path"]):
        os.mkdir(config["model_path"])
    # 加载训练数据
    train_data = load_data(config["train_data_path"], config)
    # 加载模型
    model = TorchModel(config)
    # model = peft_wrapper(model)
    # 大模型微调策略
    tuning_tactics = config["tuning_tactics"]
    if tuning_tactics == "lora_tuning":
        peft_config = LoraConfig(
            r=8,
            lora_alpha=32,
            lora_dropout=0.1,
            target_modules=["query", "key", "value"]
        )
    elif tuning_tactics == "p_tuning":
        peft_config = PromptEncoderConfig(task_type="SEQ_CLS", num_virtual_tokens=10)
    elif tuning_tactics == "prompt_tuning":
        peft_config = PromptTuningConfig(task_type="SEQ_CLS", num_virtual_tokens=10)
    elif tuning_tactics == "prefix_tuning":
        peft_config = PrefixTuningConfig(task_type="SEQ_CLS", num_virtual_tokens=10)
    model = get_peft_model(model, peft_config)
    if tuning_tactics == "lora_tuning":
        # lora配置会冻结原始模型中的所有层的权重,不允许其反传梯度
        # 但是事实上我们希望最后一个线性层照常训练,只是bert部分被冻结,所以需要手动设置
        for param in model.get_submodule("model").get_submodule("classify").parameters():
            param.requires_grad = True

    # 标识是否使用gpu
    cuda_flag = torch.cuda.is_available()
    if cuda_flag:
        logger.info("gpu可以使用,迁移模型至gpu")
        model = model.cuda()
    # 加载优化器
    optimizer = choose_optimizer(config, model)
    # 加载效果测试类
    evaluator = Evaluator(config, model, logger)
    # 训练
    for epoch in range(config["epoch"]):
        epoch += 1
        model.train()
        logger.info("epoch %d begin" % epoch)
        train_loss = []
        for index, batch_data in enumerate(train_data):
            optimizer.zero_grad()
            if cuda_flag:
                batch_data = [d.cuda() for d in batch_data]
            input_id, labels = batch_data  # 输入变化时这里需要修改,比如多输入,多输出的情况
            loss = model(input_id, labels)
            loss.backward()
            optimizer.step()
            train_loss.append(loss.item())
            if index % int(len(train_data) / 2) == 0:
                logger.info("batch loss %f" % loss)
        logger.info("epoch average loss: %f" % np.mean(train_loss))
        evaluator.eval(epoch)
    model_path = os.path.join(config["model_path"], "epoch_%d.pth" % epoch)
    save_tunable_parameters(model, model_path)  # 保存模型权重

    return model, train_data

# 仅存储当前微调的参数(模型会很小)
def save_tunable_parameters(model, path):
    saved_params = {
        k: v.to("cpu")
        for k, v in model.named_parameters()
        if v.requires_grad
    }
    torch.save(saved_params, path)

if __name__ == "__main__":
    model, train_data = main(Config)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值