LLM预训练

网上大量预训练代码都是封装了trainer-deepspeed后的结果,看了也不了解其中所用技术的优化点在哪。本文从最基础的训练过程开始,层层加码并对比。

在这里插入图片描述

基础版本

1.代码
from transformers import AutoModel,AutoTokenizer
from torch.utils.data import Dataset, DataLoader
# 模型加载
model_path = "xxx/glm2" # 你的模型路径
tokenizer = AutoTokenizer.from_pretrained(model_path, trust_remote_code=True)
model = AutoModel.from_pretrained(model_path, trust_remote_code=True,torch_dtype=torch.float32,device_map="auto")

# 使用一半参数测试
for k,v in model.named_parameters():
    no_grad_layer = list(range(0,27,2))
    no_grad_layer = [str(i) for i in no_grad_layer]
    name_list = k.split(".")
    if len(name_list) >= 4 and name_list[3] in no_grad_layer:
        v.requires_grad=False

# dataset
example = ["text1","text2","text3"]
class GenerateDataset(Dataset):
    def __init__(self, example):
        super(GenerateDataset, self).__init__()
        self.example = example

    def __getitem__(self, item):
        return self.example[item]

    def __len__(self):
        return len(self.example)
ds = GenerateDataset(example)

# dataloader+coll_fn
def pretrain_fn(context):
    input_ids = []
    label_ids = []
    max_len = 2000
    context_ids = tokenizer(context,add_special_tokens=False)["input_ids"]
    target_ids = tokenizer(context,add_special_tokens=False)["input_ids"]
    for c_ids, t_ids in zip(context_ids, target_ids):
        length = len(c_ids)
        if length >= max_len:
            c_ids = c_ids[:max_len - 1] + [2]
            t_ids = t_ids[:max_len - 1] + [2]
        else:
            c_ids = c_ids + [2] * (max_len - length)
            t_ids = t_ids + [-100] * (max_len - length)
        input_ids.append(c_ids)
        label_ids.append(t_ids)
    return {"input_ids":torch.LongTensor(input_ids),"labels":torch.LongTensor(label_ids)}
    
dl = DataLoader(ds, batch_size=1,collate_fn=pretrain_fn)
# 优化器
optimizer = torch.optim.Adam(model.parameters(), lr=1e-5)
# 训练
for batch in tqdm(dl):
    optimizer.zero_grad()
    out=model(input_ids=batch["input_ids"], labels=batch["labels"])
    loss = out.loss
    print(loss)
    loss.backward()
    optimizer.step()
    # print(loss.item)
2.有几个注意事项:

1.torch_dtype=torch.float32,如果用了fp16,模型参数和损失直接变为nan,训练过程出错。用accelerate和trainer会报ValueError: Attempting to unscale FP16 gradients.

  0%|                                                  | 0/1615 [00:00<?, ?it/s]tensor(11.4844, dtype=torch.float16, grad_fn=<ToCopyBackward0>)
  0%|                                        | 1/1615 [00:04<1:54:59,  4.27s/it]tensor(nan, dtype=torch.float16, grad_fn=<ToCopyBackward0>)
  0%|                                          | 2/1615 [00:04<50:53,  1.89s/it]tensor(nan, dtype=torch.float16, grad_fn=<ToCopyBackward0>)
ValueError: Attempting to unscale FP16 gradients.

2.优化器SGD/Adamw,6B模型全参数训练时候,如果用Adamw会爆显存(batch=1,文本长度20),用SGD则不会。实际llm没看到用SGD做优化器,这里只是提一嘴。

Accelerate 版本

# 加载model optimizer dataloader 后,加入以下代码
from accelerate import Accelerator
accelerator = Accelerator(mixed_precision="fp16")
device = accelerator.device
model, optimizer, dl = accelerator.prepare(model, optimizer, dl)
# 原来的loss.backward()替换为accelerator.backward(loss)
# loss.backward()
accelerator.backward(loss)

Trainer 版本

原代码的dataloader optimizer 和训练代码都不用了

train_args = TrainingArguments(save_strategy="epoch",
                               log_level="debug",
                               output_dir="./saved_checkpoint/",
                               per_device_train_batch_size=4,  
                               gradient_accumulation_steps=1,  # 梯度累积步数
                               num_train_epochs=1,
                               learning_rate=1e-5,
                               fp16=True, # 是否使用fp16
                               logging_steps=1,
                               warmup_steps=50,
                               optim="adamw_hf",  # 指定优化器
                               gradient_checkpointing=True, # 梯度检查点
                               )
trainer = Trainer(model=model,train_dataset=ds,args=train_args, data_collator=pretrain_fn)
trainer.train()

需要注意的时:如果gradient_checkpointing=True,则需要加上下面的代码

model.enable_input_require_grads()
# 或者 直接修改模型embedding参数的requires_grad
for k,v in model.named_parameters():
	if k == "transformer.embedding.word_embeddings.weight":
		v.requires_grad = True

Trainer + DeepSpeed

修改train_args, 其他不变

train_args = TrainingArguments(save_strategy="steps",
                               save_steps=40,
                               log_level="debug",
                               output_dir="./test_lora/",
                               per_device_train_batch_size=2,
                               gradient_accumulation_steps=2,
                               num_train_epochs=10,
                               learning_rate=1e-6,
                               fp16=True,
                               logging_steps=1,
                               warmup_steps=10,
                               optim="adamw_hf",
                               gradient_checkpointing=True,
                               deepspeed="./notebooks/deepspeed-stage2.json" # 加入ds路径
                               )

stage-2参数

{
    "fp16": {
        "enabled": "auto",
        "loss_scale": 0,
        "loss_scale_window": 1000,
        "initial_scale_power": 16,
        "hysteresis": 2,
        "min_loss_scale": 1
    },

    "optimizer": {
        "type": "AdamW",
        "params": {
            "lr": "auto",
            "betas": "auto",
            "eps": "auto",
            "weight_decay": "auto"
        }
    },

    "scheduler": {
        "type": "WarmupLR",
        "params": {
            "warmup_min_lr": "auto",
            "warmup_max_lr": "auto",
            "warmup_num_steps": "auto"
        }
    },

    "zero_optimization": {
        "stage": 2,
        "offload_optimizer": {
            "device": "cpu",
            "pin_memory": true
        },
        "allgather_partitions": true,
        "allgather_bucket_size": 2e8,
        "overlap_comm": true,
        "reduce_scatter": true,
        "reduce_bucket_size": 2e8,
        "contiguous_gradients": true
    },

    "gradient_accumulation_steps": "auto",
    "gradient_clipping": "auto",
    "steps_per_print": 20,
    "train_batch_size": "auto",
    "train_micro_batch_size_per_gpu": "auto",
    "wall_clock_breakdown": false
}

stage-3参数

{
    "fp16": {
        "enabled": "auto",
        "loss_scale": 0,
        "loss_scale_window": 1000,
        "initial_scale_power": 16,
        "hysteresis": 2,
        "min_loss_scale": 1
    },

    "optimizer": {
        "type": "AdamW",
        "params": {
            "lr": "auto",
            "betas": "auto",
            "eps": "auto",
            "weight_decay": "auto"
        }
    },

    "scheduler": {
        "type": "WarmupLR",
        "params": {
            "warmup_min_lr": "auto",
            "warmup_max_lr": "auto",
            "warmup_num_steps": "auto"
        }
    },

    "zero_optimization": {
        "stage": 3,
        "offload_optimizer": {
            "device": "cpu",
            "pin_memory": true
        },
        "offload_param": {
            "device": "cpu",
            "pin_memory": true
        },
        "overlap_comm": true,
        "contiguous_gradients": true,
        "sub_group_size": 1e9,
        "reduce_bucket_size": "auto",
        "stage3_prefetch_bucket_size": "auto",
        "stage3_param_persistence_threshold": "auto",
        "stage3_max_live_parameters": 1e9,
        "stage3_max_reuse_distance": 1e9,
        "stage3_gather_16bit_weights_on_model_save": true
    },

    "gradient_accumulation_steps": "auto",
    "gradient_clipping": "auto",
    "steps_per_print": 20,
    "train_batch_size": "auto",
    "train_micro_batch_size_per_gpu": "auto",
    "wall_clock_breakdown": false
}

启动命令

deepspeed pretrain.py --seepspeed ./notebooks/deepspeed-stage2.json

  • 2
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
这个错误通常是由于传递给函数的日期格式不正确造成的。month参数必须在1到12之间,否则就会出现这个错误。 如果您传递的日期格式是字符串,可以尝试按照正确的格式传递日期。例如,如果您想传递2022年5月的日期,可以使用"2022-05"这种格式。 如果您使用的是datetime.date对象,则可以使用对象的strftime方法将日期格式化为字符串,然后再传递给函数。 另外,如果您传递的日期是无效的,例如2月30日,也会导致这个错误的出现。在传递日期之前,最好检查一下日期是否有效。 以下是一个示例程序,用于检查日期是否有效,并将日期格式化为正确的格式: ```python import datetime def check_date(date_str): try: datetime.datetime.strptime(date_str, '%Y-%m-%d') return True except ValueError: return False def format_date(date_str): if check_date(date_str): return date_str else: date_obj = datetime.datetime.strptime(date_str, '%Y%m%d') return date_obj.strftime('%Y-%m') # 示例:将202205格式的日期格式化为2022-05格式 date_str = '202205' formatted_date = format_date(date_str) print(formatted_date) ``` 在这个示例程序中,我们定义了两个函数:check_date和format_date。check_date函数用于检查日期是否有效,如果有效则返回True,否则返回False。format_date函数用于将日期格式化为正确的格式,如果传递的日期格式为"YYYY-MM-DD",则直接返回,否则将日期格式化为"YYYY-MM"的格式。 在进行日期格式化之前,我们先调用check_date函数检查日期是否有效。如果日期无效,则抛出ValueError异常。如果日期有效,则使用datetime库的strptime函数将日期字符串转换为datetime.datetime对象,然后使用对象的strftime方法将日期格式化为正确的格式。 您可以参考这个示例程序,根据您的实际情况进行调整和修改。
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值