THUDM / VisualGLM-6B

Chinese and English multimodal conversational language model | 多模态中英双语对话语言模型
Apache License 2.0
4.1k stars 418 forks source link

请问visualglm-6b可以多卡部署吗 #102

Open ywm12345 opened 1 year ago

freelancerllm commented 1 year ago

+1 目前单卡推理也太慢了 30w的数据批量产出 24h还没解决。

szdengdi commented 1 year ago

说明里面没有写支持多卡。尝试将ChatGLM-6B里面的utils.py复制过来,按照ChatGLM-6B的方式,将Web_demo_hf.py改多卡,会报错。

ywm12345 commented 1 year ago

说明里面没有写支持多卡。尝试将ChatGLM-6B里面的utils.py复制过来,按照ChatGLM-6B的方式,将Web_demo_hf.py改多卡,会报错。

是的,我也这么操作了,报错各种参数不存在什么的,应该需要重新写生成device_map这个方法,

ywm12345 commented 1 year ago

说明里面没有写支持多卡。尝试将ChatGLM-6B里面的utils.py复制过来,按照ChatGLM-6B的方式,将Web_demo_hf.py改多卡,会报错。

能不能指定多卡中的某个卡部署呀

Sleepychord commented 1 year ago

多卡inference可以通过改变设备的device,如果不想理解并改动代码,可以通过在外面设定CUDA_VISIBLE_DEVICES 环境变量启动使用不同卡的多个进程实现。

ywm12345 commented 1 year ago

多卡inference可以通过改变设备的device,如果不想理解并改动代码,可以通过在外面设定CUDA_VISIBLE_DEVICES 环境变量启动使用不同卡的多个进程实现。

我看了chatglm-6b的那个device_map也就是把层数分到机器的各个gpu块上,但visualglm中的层数我看了config.json,也没有准确看出有多少层,且还有类似device_map = {'transformer.word_embeddings': 0, 'transformer.final_layernorm': 0, 'lm_head': 0}这种参数也不知道有哪些参数,所以我也不知道该如何去定义device_map,有没有例子或教程什么的

TreemanCHou commented 1 year ago

稍微分享一下:

根据代码,可以知道 VisualGLM 大概是根据 BLIP-2 的方式训练出来的。(参阅BLIP-2论文)

目前的尝试是,把 ChatGLM-6B 里的 utils 里的 load_model_on_gpus 和 auto_configure_device_map 两个函数复制到这边。然后直接使用 load_model_on_gpus 函数加载模型,然后运行。

在这种情况下,模型会报出几百行错误,具体内容为:我们的 device_map 并没有给模型的各种层各种权重(比如Qformer和ViT的各种Layer还有从Visual Prompt到LLM的投影层之类的)指定所在的device. 根据报错信息列出的所有内容,可以得知BLIP-2中的Q-Former包括12层(0-11)和一个Final_layernorm,以及WordEmbedding,同理还有图像编码器ViT的39层和几个Embedding。所以把这些内容全部添加到device_map里就行了,需要改写一些逻辑。


def auto_configure_device_map(num_gpus: int) -> Dict[str, int]:
    # transformer.word_embeddings 占用1层
    # transformer.final_layernorm 和 lm_head 占用1层
    # transformer.layers 占用 28 层
    # 总共30层分配到num_gpus张卡上
    num_trans_layers = 28

    # bugfix: 在linux中调用torch.embedding传入的weight,input不在同一device上,导致RuntimeError
    # windows下 model.device 会被设置成 transformer.word_embeddings.device
    # linux下 model.device 会被设置成 lm_head.device
    # 在调用chat或者stream_chat时,input_ids会被放到model.device上
    # 如果transformer.word_embeddings.device和model.device不同,则会导致RuntimeError
    # 因此这里将transformer.word_embeddings,transformer.final_layernorm,lm_head都放到第一张卡上

    # bugfix[TreemanCHou]
    # 对于采用BLIP的 VisualGLM,其包括39层的ViT和12层的Qformer.
    # Qformer包括一个final_layernorm层。
    # 除此之外,还包括VitTransformer 的Wordembedding和positionembedding
    # 以及 VItMixin的patchEmbedding和cls.ln_vision(虽然暂时不知道是干啥的)
    # 如果把独立的层全部放在第一个卡里,会报OOM. 所以把一部分层放到最后一卡,
    # 并且把used以及总layers - 1 。
    # 在这样做之后,DEVICE 0 会突然OOM。怀疑是因为各种Word的Embedding太大了。
    # 所以把他挪到最后一卡去。
    device_map = {
            'transformer.word_embeddings': num_gpus - 1, 
            'transformer.final_layernorm': num_gpus - 1, 
            'lm_head': num_gpus - 1,
            'image_encoder.qformer.transformer.final_layernorm': 0,
            'image_encoder.vit.mixins.patch_embedding.proj': num_gpus - 1,
            'image_encoder.vit.mixins.cls.ln_vision': num_gpus - 1,
            'image_encoder.vit.transformer.word_embeddings': num_gpus - 1,
            'image_encoder.vit.transformer.position_embeddings': num_gpus - 1,
            'image_encoder.qformer.transformer.word_embeddings': num_gpus - 1,
            'image_encoder.glm_proj': 0,
            }
    num_vit_layers = 39
    num_qf_layers = 12
    per_gpu_layers = (num_qf_layers + num_trans_layers + num_vit_layers + 10) / num_gpus

    used = 10 - 8 # 因为挪了8个到最后一卡
    gpu_target = 0

    for i in range(num_trans_layers + num_vit_layers + num_qf_layers):
        if used >= per_gpu_layers:
            gpu_target += 1
            used = 0
        assert gpu_target < num_gpus
        if i < num_trans_layers:
            device_map[f'transformer.layers.{i}'] = gpu_target
        elif i < num_trans_layers + num_vit_layers:
            device_map[f'image_encoder.vit.transformer.layers.{i - num_trans_layers}'] = gpu_target
        elif i < num_trans_layers + num_vit_layers + num_qf_layers:
            device_map[f'image_encoder.qformer.transformer.layers.{i - num_trans_layers - num_vit_layers }'] = gpu_target
        used += 1
    return device_map

这样做了之后是可以运行的,观察各个卡的性能,在开始阶段,模型也的确均匀地分布在多卡上加载了。但唯独GPU-0会别的多一些,然后再Out Of Memory 。尽管做了如代码中的修改和量化,仍然会发生如我在注释里描述的那样的错误,即:在最后突然GPU-0的显存占用急速增加然后OOM. 不过我的服务器性能有限,只有8卡12g,具体能否在更大的单卡上成功运行我也没机会测试了,大家有进展的话记得踢我一脚(

bennyyu79 commented 1 year ago

多卡部署,参考chatGLM-6B ,报错Expected all tensors to be on the same device, but found at least two devices, cuda:1 and cuda:0! (when checking argument for argument index in method wrapper__index_select)

bennyyu79 commented 1 year ago

通过手动自定gpu 搞定: pre_txt_emb = pre_txt_emb.to('cuda:0') image_embeds = image_embeds.to('cuda:0') post_txt_emb = post_txt_emb.to('cuda:0') inputs_embeds = torch.cat([pre_txt_emb, image_embeds, post_txt_emb], dim=1)

bennyyu79 commented 1 year ago

稍微分享一下:

根据代码,可以知道 VisualGLM 大概是根据 BLIP-2 的方式训练出来的。(参阅BLIP-2论文)

目前的尝试是,把 ChatGLM-6B 里的 utils 里的 load_model_on_gpus 和 auto_configure_device_map 两个函数复制到这边。然后直接使用 load_model_on_gpus 函数加载模型,然后运行。

在这种情况下,模型会报出几百行错误,具体内容为:我们的 device_map 并没有给模型的各种层各种权重(比如Qformer和ViT的各种Layer还有从Visual Prompt到LLM的投影层之类的)指定所在的device. 根据报错信息列出的所有内容,可以得知BLIP-2中的Q-Former包括12层(0-11)和一个Final_layernorm,以及WordEmbedding,同理还有图像编码器ViT的39层和几个Embedding。所以把这些内容全部添加到device_map里就行了,需要改写一些逻辑。

def auto_configure_device_map(num_gpus: int) -> Dict[str, int]:
    # transformer.word_embeddings 占用1层
    # transformer.final_layernorm 和 lm_head 占用1层
    # transformer.layers 占用 28 层
    # 总共30层分配到num_gpus张卡上
    num_trans_layers = 28

    # bugfix: 在linux中调用torch.embedding传入的weight,input不在同一device上,导致RuntimeError
    # windows下 model.device 会被设置成 transformer.word_embeddings.device
    # linux下 model.device 会被设置成 lm_head.device
    # 在调用chat或者stream_chat时,input_ids会被放到model.device上
    # 如果transformer.word_embeddings.device和model.device不同,则会导致RuntimeError
    # 因此这里将transformer.word_embeddings,transformer.final_layernorm,lm_head都放到第一张卡上

    # bugfix[TreemanCHou]
    # 对于采用BLIP的 VisualGLM,其包括39层的ViT和12层的Qformer.
    # Qformer包括一个final_layernorm层。
    # 除此之外,还包括VitTransformer 的Wordembedding和positionembedding
    # 以及 VItMixin的patchEmbedding和cls.ln_vision(虽然暂时不知道是干啥的)
    # 如果把独立的层全部放在第一个卡里,会报OOM. 所以把一部分层放到最后一卡,
    # 并且把used以及总layers - 1 。
    # 在这样做之后,DEVICE 0 会突然OOM。怀疑是因为各种Word的Embedding太大了。
    # 所以把他挪到最后一卡去。
    device_map = {
            'transformer.word_embeddings': num_gpus - 1, 
            'transformer.final_layernorm': num_gpus - 1, 
            'lm_head': num_gpus - 1,
            'image_encoder.qformer.transformer.final_layernorm': 0,
            'image_encoder.vit.mixins.patch_embedding.proj': num_gpus - 1,
            'image_encoder.vit.mixins.cls.ln_vision': num_gpus - 1,
            'image_encoder.vit.transformer.word_embeddings': num_gpus - 1,
            'image_encoder.vit.transformer.position_embeddings': num_gpus - 1,
            'image_encoder.qformer.transformer.word_embeddings': num_gpus - 1,
            'image_encoder.glm_proj': 0,
            }
    num_vit_layers = 39
    num_qf_layers = 12
    per_gpu_layers = (num_qf_layers + num_trans_layers + num_vit_layers + 10) / num_gpus

    used = 10 - 8 # 因为挪了8个到最后一卡
    gpu_target = 0

    for i in range(num_trans_layers + num_vit_layers + num_qf_layers):
        if used >= per_gpu_layers:
            gpu_target += 1
            used = 0
        assert gpu_target < num_gpus
        if i < num_trans_layers:
            device_map[f'transformer.layers.{i}'] = gpu_target
        elif i < num_trans_layers + num_vit_layers:
            device_map[f'image_encoder.vit.transformer.layers.{i - num_trans_layers}'] = gpu_target
        elif i < num_trans_layers + num_vit_layers + num_qf_layers:
            device_map[f'image_encoder.qformer.transformer.layers.{i - num_trans_layers - num_vit_layers }'] = gpu_target
        used += 1
    return device_map

这样做了之后是可以运行的,观察各个卡的性能,在开始阶段,模型也的确均匀地分布在多卡上加载了。但唯独GPU-0会别的多一些,然后再Out Of Memory 。尽管做了如代码中的修改和量化,仍然会发生如我在注释里描述的那样的错误,即:在最后突然GPU-0的显存占用急速增加然后OOM. 不过我的服务器性能有限,只有8卡12g,具体能否在更大的单卡上成功运行我也没机会测试了,大家有进展的话记得踢我一脚(

没必要搞得这么麻烦,我开始也是这样的,后来直接 device_map = {'transformer.word_embeddings': 0, 'transformer.final_layernorm': 0, 'lm_head': 0, 'image_encoder': 1
} image的层就都别的GPU了

daihuangyu commented 1 year ago

@bennyyu79, 老哥,想问问怎么手动修改pre_txt_emb = pre_txt_emb.to('cuda:0') image_embeds = image_embeds.to('cuda:0') post_txt_emb = post_txt_emb.to('cuda:0') inputs_embeds = torch.cat([pre_txt_emb, image_embeds, post_txt_emb], dim=1),我下载了模型权重文件,指定了对应路径,但是加载权重文件的模型还是用缓存里的模型文件,导致每次修改的都不生效

qq281541534 commented 1 year ago

请官方团队出个方案给解决下呗

1049451037 commented 1 year ago

使用最新版github的sat

git clone https://github.com/THUDM/SwissArmyTransformer
cd SwissArmyTransformer
pip install .

然后

torchrun --nnode 1 --nproc-per-node 2 cli_demo_mp.py

如果之前下载过checkpoint,需要手动删掉重新下载。(因为权重文件为了适配model parallel更新了)

TC2022lxf commented 1 year ago

使用最新版github的sat

git clone https://github.com/THUDM/SwissArmyTransformer
cd SwissArmyTransformer
pip install .

然后

torchrun --nnode 1 --nproc-per-node 2 cli_demo_mp.py

如果之前下载过checkpoint,需要手动删掉重新下载。(因为权重文件为了适配model parallel更新了)

AttributeError: 'Namespace' object has no attribute 'pad_token_id'. Did you mean: 'bos_token_id'?为什么会显示这错误,我已经最新版了

1049451037 commented 1 year ago

需要保证本地的sat和VisualGLM-6B的代码都是最新的,重新pull一下github代码吧,别用旧版本

pip install SwissArmyTransformer --upgrade
git clone https://github.com/THUDM/VisualGLM-6B
1049451037 commented 1 year ago

你这个错误应该是checkpoint是旧版的,需要重新下载一下新的checkpoint

但是下载新的checkpoint需要两个前提:

  1. 你本地的cache删掉:rm -r ~/.sat_models/visualglm-6b
  2. 你的VisualGLM-6B代码是github新版本的
1049451037 commented 1 year ago

总之一句话:sat、visualglm-6b代码、visualglm-6b的checkpoint,三者都需要是新版本的。