使用deepspeed,transformers,safetensor中常见的训练精度,共享权重问题

时间:2024-04-08 17:20:28

使用deepspeed可能需要注意精度问题

在这里插入图片描述

混合精度,LayerNorm

虽然deepspeed有混合精度训练的功能,但是对于网络上各种奇奇怪怪的代码的DIY转化中,他还是很弱小的。它的精度问题,使用deepspeed如果模型中有部分模型使用的是half精度,那么整个模型都会使用half精度,即使是nn.LayerNorm这样新创立的层。因为我们通常可能在计算权重的时候使用half,在LayerNorm的时候使用float32这样更好的归一化,防止 梯度 因为 精度 的问题消失或者爆炸。所以通常建议使用float32精度进行计算。但是这样的强制数据类型转化,在deepspeed中就会因为将模型的全部精度都降低而难以实现。
这个难题可以解决,但是需要特别设置,尤其是原来代码没有这样设置的时候。

class LayerNorm(nn.LayerNorm):
    def __init__(self, normalized_shape, eps=1e-5, elementwise_affine=True):
        super(LayerNorm, self).__init__(normalized_shape, eps=eps, elementwise_affine=elementwise_affine)
        # 确保权重和偏置初始化为float32,即使之后模型转换为fp16
        if self.elementwise_affine:
            self.weight.data = self.weight.data.float()
            self.bias.data = self.bias.data.float()
    
    def forward(self, x: torch.Tensor):
        # print(f"这是是layernorm")
        # embed()
        orig_type = x.dtype
        # ret = super().forward(x.type(torch.float32))
        ret = super().forward(x)
        return ret.type(orig_type)

一个很好的检查模型计算精度的方法是将模型的权重精度打印出来,单纯的显示出来模型并不能显示出来计算精度。

def print_model_parameters(model):
    for name, param in model.named_parameters():
        print(name, param.size(), id(param))
        ## 我更喜欢使用 .dypte 来直接查看精度
print_model_parameters(your_model_instance)

共享张量(Shared Tensors)

safetensor可并没有那么好,它保存不了某些特别的自定义的共享张量。共享张量是PyTorch中一种用于减少内存使用并提高计算效率的特性。通过共享相同的数据缓冲区,多个张量可以引用相同的内存空间,而不需要复制数据。在transformers模型中,嵌入层(embeddings)和语言模型头(lm_head)经常共享权重,这样做既节省了参数数量,又使得梯度更有效地传播到模型的不同部分。共享向量是很多多模态领域训练的精髓。

共享张量说白了就是模型的参数传递,这种传递是直接赋值,而不是用另一个模型参数去计算。这种现象很常见就是一个张量数据是传递一致的,但是在模型的运算传递多次使用到。

layer1 = nn.Linear(10, 10)
layer2 = nn.Linear(10, 10)

# 明确共享layer1的权重和偏置到layer2
layer2.weight = layer1.weight
layer2.bias = layer1.bias

这里报错误实际上和我实例化两个参数有关,实际上可以避免,但是模型原来的设计者可没有想到这一点。
报错的显示

RuntimeError: 
            Some tensors share memory, this will lead to duplicate memory on disk and potential differences when loading them again: 
            [{'encoder_decoder.cmn.linears.0.weight', 'encoder_decoder.model.cmn.linears.0.weight'},
             {'encoder_decoder.model.cmn.linears.0.bias', 'encoder_decoder.cmn.linears.0.bias'}, 
             {'encoder_decoder.cmn.linears.1.weight', 'encoder_decoder.model.cmn.linears.1.weight'},
              {'encoder_decoder.cmn.linears.1.bias', 'encoder_decoder.model.cmn.linears.1.bias'}, 
              {'encoder_decoder.model.cmn.linears.2.weight', 'encoder_decoder.cmn.linears.2.weight'}, 
              {'encoder_decoder.cmn.linears.2.bias', 'encoder_decoder.model.cmn.linears.2.bias'}, 
              {'encoder_decoder.cmn.linears.3.weight', 'encoder_decoder.model.cmn.linears.3.weight'},
               {'encoder_decoder.model.cmn.linears.3.bias', 'encoder_decoder.cmn.linears.3.bias'}].
            A potential way to correctly save your model is to use `save_model`.
            More information at https://huggingface.co/docs/safetensors/torch_shared_tensors
       0%|          | 1/410000 [00:34<3883:03:26, 34.10s/it]        

使用原始的pytorch存储格式就好使了,而transformers版本大一点的,如4.35,4.36它们在使用transformers.train函数调用自动保存时,保存的结果都是safetensor,但是safetensor应该是为了节约空间,做了一些现在还不完善的处理。导致出现了上面需要显示保存或者修改继承的方式。

安装库错误的问题

需要在集群上经常安装库,在报错的时候,尤其是下面这种,缺乏CUDA编译器的问题,直接使用module ava, load需要的CUDA库往往就能解决问题。

Looking in indexes: https://pypi.tuna.tsinghua.edu.cn/simple
Collecting deepspeed==0.9.5
  Downloading https://pypi.tuna.tsinghua.edu.cn/packages/99/0f/a4ebd3b3f6a8fd9bca77ca5f570724f3902ca90b491f8146e45c9733e64f/deepspeed-0.9.5.tar.gz (809 kB)
     ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 809.9/809.9 kB 2.9 MB/s eta 0:00:00
  Preparing metadata (setup.py) ... error
  error: subprocess-exited-with-error

  × python setup.py egg_info did not run successfully.
  │ exit code: 1
  ╰─> [8 lines of output]
      Traceback (most recent call last):
        File "<string>", line 2, in <module>
        File "<pip-setuptools-caller>", line 34, in <module>
        File "/tmp/pip-install-uc05hpfj/deepspeed_b69d07d91ac4496684f65ba796150c78/setup.py", line 82, in <module>
          cuda_major_ver, cuda_minor_ver = installed_cuda_version()
        File "/tmp/pip-install-uc05hpfj/deepspeed_b69d07d91ac4496684f65ba796150c78/op_builder/builder.py", line 41, in installed_cuda_version
          assert cuda_home is not None, "CUDA_HOME does not exist, unable to compile CUDA op(s)"
      AssertionError: CUDA_HOME does not exist, unable to compile CUDA op(s)
      [end of output]

  note: This error originates from a subprocess, and is likely not a problem with pip.
error: metadata-generation-failed

× Encountered error while generating package metadata.
╰─> See above for output.

note: This is an issue with the package mentioned above, not pip.
hint: See above for details.
module ava
module load cuda/7/11.8