Learning & Planning

Paper Reading

/读CCF A类 期刊/会议/

课题方向:1. 视觉问答+多模态大语言模型

​ 2. 视频时刻定位+组合图像检索一

​ 3. 多模态疾病预测:胸片,放疗血液毒性,医疗问答

论文阅读方向:

  • 医疗视觉问答、胸片报告生成、医疗多模态语言模型、多模态语言模型、多模态幻觉;

    【关键词】:medical visual qustion answering、medical report generation、visual language model, multimodal large language model,image captioning,VLM、MLLM;

找论文:

1
2
3
4
5
6
website:
https://paperswithcode.com/
https://www.connectedpapers.com/
https://scholar.google.com/
https://www.semanticscholar.org/
建议精读原文>=谷歌直接翻译>公众号>GPT摘要,GPT会漏掉很多细节

论文阅读+看源码

医疗大模型综述:https://arxiv.org/pdf/2408.11735

Overview of Prevalent Medical Language Models

Summary of Major LLM Applications in Medical Domain

image-20240929102340318

Skills learned

分布式训练

Reference

  • 模型并行:模型过大单GPU显存不足无法加载,将模型切割为几个部分分别加载不同GPU
  • 数据并行:每个GPU复制一份模型,将一批样本分为多份分发到各个GPU进行计算,相当于加大了batch_size

a. Data Parallel (DP):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
数据并行
eg1. nn.parallel
# Replicate module to devices in device_ids
replicas = nn.parallel.replicate(module, device_ids)
# Distribute input to devices in device_ids
inputs = nn.parallel.scatter(input, device_ids)
# Apply the models to corresponding inputs
outputs = nn.parallel.parallel_apply(replicas, inputs)
# Gather result from all devices to output_device
result = nn.parallel.gather(outputs, output_device)

eg2. torch.nnDataParallel(module,device_ids=None,ouput_device=None,dim=0)
# device_ids指定在哪些GPU上进行优化,output_devices指定输出到哪个GPU上,如果不设定要使用的device_ids,程序会自动找可用的所有显卡用于训练, os.environ['CUDA_VISIBLE_DEVICES'] == ’‘限定GPU
new_net= nn.DataParallel(net, device_ids=[0, 1])
output= new_net(input)
1
2
并行数据加载
设置'num_workers>0'使用多个子进程进行数据加载

流程:

DP处理流程

​ 基本上,给定的输入通过在批处理维度中分块在GPU之间进行分配。 在前向传递中,模型在每个设备上复制,每个副本处理批次的一部分。 在向后传递过程中,主GPU(上图中的GPU-1)收集每个GPU的输出output,根据label计算loss,继而计算得到多个梯度grad,然后将梯度分发到各个GPU(官方原理图中第二行第二个),然后每个GPU副本模型上的梯度更新(第二行第三个),然后再将每个更新完梯度的的参数合并到主gpu(第二行最后一个步骤),求和以生成最终的梯度,并将其应用于主gpu(上图中的GPU-1)以更新模型权重。 在下一次迭代中,主GPU上的更新模型将再次复制到每个GPU设备上。

b. Distributed Data Parallel (DDP):

DDP处理流程

​ DDP为每个GPU创建一个进程,每个进程对应一个独立的训练过程(多进程,适用单机和多机训练),梯度计算完成后由rank=0的进程broadcast到所有进程,之后各进程用改梯度来独立的更新参数,各进程的模型参数始终保持一致

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
import torch
import torch.nn as nn
import argparse
from torch.autograd import Variable
from torch.utils.data import Dataset, DataLoader
import os
from torch.utils.data.distributed import DistributedSampler

# 1)添加参数 --local_rank
'''
每个进程分配一个 local_rank 参数, 表示当前进程在当前主机上的编号。这个参数是torch.distributed.launch传递过来的,我们设置位置参数来接受,local_rank代表当前程序进程使用的GPU标号
'''
parser = argparse.ArgumentParser()
parser.add_argument('--local_rank', default=-1, type=int,
help='node rank for distributed training')
args = parser.parse_args()

# 通过args接收 local_rank
local_rank = args.local_rank

# 通过 get_rank() 得到 local_rank,最好放在初始化之后使用
local_rank = torch.distributed.get_rank()

# 2)********************************************************
# 初始化使用nccl后端
torch.distributed.init_process_group(backend="nccl")

# 3)********************************************************
# 获取 local_rank
local_rank = torch.distributed.get_rank()
#配置每个进程的 GPU, 根据local_rank来设定当前使用哪块GPU
torch.cuda.set_device(local_rank)
device = torch.device("cuda", local_rank)

# 4)********************************************************
# 自己的数据获取
dataset = MyDataset(input_size, data_size)

# 使用 DistributedSampler
'''
使用 DistributedSampler 对数据集进行划分。它能帮助我们将每个 batch 划分成几个 partition,在当前进程中只需要获取和 rank 对应的那个 partition 进行训练
#注意 testset不用sampler
'''
train_sampler = torch.utils.data.distributed.DistributedSampler(dataset)

trainloader = DataLoader(dataset=dataset,
pin_memory=true,
shuffle=(train_sampler is None), # 使用分布式训练 shuffle 应该设置为 False
batch_size=args.batch_size,
num_workers=args.workers,
sampler=train_sampler)



# 5)********************************************************
model = Model()
# 把模型移到对应的gpu
# 定义并把模型放置到单独的GPU上,需要在调用`model=DDP(model)`前做
model.to(device)

# 引入SyncBN,这句代码,会将普通BN替换成SyncBN。
model = torch.nn.SyncBatchNorm.convert_sync_batchnorm(model)

# GPU 数目大于 1 才有必要分布式训练
if torch.cuda.device_count() > 1:
model = torch.nn.parallel.DistributedDataParallel(model,
device_ids=[local_rank],
output_device=local_rank)


# 6)********************************************************

for epoch in range(num_epochs):
# 设置sampler的epoch,DistributedSampler需要这个来维持各个进程之间的相同随机数种子
trainloader.sampler.set_epoch(epoch)
# 后面这部分,则与原来完全一致了。
for data, label in trainloader:
prediction = model(data)
loss = loss_fn(prediction, label)
loss.backward()
optimizer = optim.SGD(ddp_model.parameters(), lr=0.001)
optimizer.step()
1
2
3
eg
CUDA_VISIBLE_DEVICES=0,1,2,3 python -m torch.distributed.launch --nproc_per_node=4 main.py --{args}
# --nproc_per_node一般设定为当前主机可用的GPU数目

c. Hugging Face Accelerate:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
Eg
from transformers import BertForSequenceClassification, AdamW, get_scheduler
from datasets import load_dataset
from accelerate import Accelerator

# 初始化 Accelerator
accelerator = Accelerator()

# 加载数据集
dataset = load_dataset("glue", "mrpc")
train_dataset = dataset["train"]

# 定义模型和优化器
model = BertForSequenceClassification.from_pretrained("bert-base-uncased")
optimizer = AdamW(model.parameters(), lr=5e-5)

# 准备数据加载器
train_dataloader = torch.utils.data.DataLoader(train_dataset, batch_size=8)

# 准备模型、数据和优化器
model, optimizer, train_dataloader = accelerator.prepare(model, optimizer, train_dataloader)

# 训练循环
for epoch in range(3):
model.train()
for batch in train_dataloader:
outputs = model(**batch)
loss = outputs.loss
accelerator.backward(loss)
optimizer.step()
optimizer.zero_grad()
print(f'Epoch {epoch+1}, Loss: {loss.item()}')

Accelerate config

mutil GPU config

  • How many different machines will you use (use more than 1 for multi-node training)?
    – 在几台机器(物理意义)上训练

  • Should distributed operations be checked while running for errors? This can avoid timeout issues but will be slower. [yes/NO]:

    – 启用这个选项可以帮助你避免因为错误导致的超时问题,但可能会降低训练速度

  • Do you wish to optimize your script with torch dynamo?[yes/NO]:

    – Torch Dynamo 是 PyTorch 的一个新特性,用于通过动态跟踪和编译 Python 代码来提升模型的运行效率。如果你希望通过这个工具提高模型训练或推理的速度,可以选择 yes

  • Do you want to use DeepSpeed? [yes/NO]:

    – DeepSpeed 是微软开发的一个深度学习优化库,专门用于高效的分布式训练。它可以显著提高大规模模型的训练速度,减少显存占用,并支持混合精度训练。如果你正在训练一个大型模型并且希望提高训练效率,可以选择 yes

  • Do you want to use FullyShardedDataParallel? [yes/NO]:

    – 选择 yes:如果决定启用 FSDP,PyTorch 会自动在训练中使用 FSDP 进行优化。你可能还需要根据实际情况配置 FSDP 相关的参数。

    选择 no:如果选择 no,则不会启用 FSDP,训练将不会使用这种内存优化策略。

  • Do you want to use Megatron-LM ? [yes/NO]:

选择 yes:如果决定启用 Megatron-LM,你可能需要进行额外的配置和调整来确保其正确集成到你的训练过程。

选择 no:如果选择 no,训练将不会使用 Megatron-LM,通常会依赖于默认的训练设置和策略。

1
accelerate configuration saved at /home/dell/.cache/huggingface/accelerate/default_config.yaml

Common Screen Commands

**Introduction:**In project development, when executing programs on the Linux terminal, if the terminal is closed, the program execution will also terminate. This poses significant inconvenience for long-running programs.

Screen facilitates the management of multiple command-line workflows without concern for their interference. Programs are automatically backgrounded and continue execution until completion.

Start a New screen Session

1
2
3
4
# Start a new screen session named "my_session"
screen -S my_session
# Automatically name the new screen session
screen

View Existing screen Sessions

1
2
# List all screen sessions
screen -ls

Attach to an Existing screen Session

1
2
# Attach to the screen session named "my_session"
screen -r my_session

Detach from an Existing screen Session

1
2
3
4
5
6
7
8
# Way 1
# Detach from the screen session named "my_session"
screen -d my_session
# Way 2
# Enter the following keys in turn and the program
# will also continue to execute in the background
Ctrl+a
a

Delete an Existing screen Session

1
2
# Delete the screen session named "my_session"
screen -X -S my_session quit

Eg

1
2
3
4
5
6
7
8
9
安装:
apt-get update
apt-get install screen
screen -S phi3v
screen -r phi3v
screen -Q -S phi3v
删除:screen -S 12345 -X quit
强制附加到已经附加的 screen 会话
screen -dr phi3v

Zip 环境移植

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
cd 对应的环境envs文件夹
zip -r envs_name.zip envs_name
使用winscp等工具传输至服务器conda环境文件夹
unzip envs_name.zip
激活

遇到未初始化无法激活:直接用完整路径激活环境:source /opt/conda/bin/activate phi3v

使用 sed 命令来批量替换所有脚本文件中的解释器路径
find /opt/conda/envs/phi3v/bin/ -type f -exec sed -i 's|/home/dell/.conda/envs/phi3v/bin/python|/opt/conda/envs/phi3v/bin/python|g' {} +
这条命令会遍历 /opt/conda/envs/phi3v/bin/ 目录下的所有文件,并将旧的解释器路径 /home/dell/.conda/envs/phi3v/bin/python 替换为新的路径 /opt/conda/envs/phi3v/bin/python。

# 找不到库
conda deactivate 两次
conda activate envs_names

Docker使用及封装、dockerfile

1
2
3
4
E.g  (docker命令只在宿主机内有效)
docker run --gpus all -it --shm-size 128g -v /mnt/exdisk/maz:/workspace --name my_container maz:latest /bin/bash
eg: docker exec -it 64e /bin/bash
docker exec -it <容器ID或容器名称> /bin/bash
Docker指令解析
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
命令小结
# 安装docker
yum install docker

# 开启docker服务:
service docker start

# 列出所有docker镜像的命令:
docker images

# 删除指定docker镜像的命令:
docker rmi anibali/pytorch

# 下载一个新的镜像的命令:
docker pull anibali/pytorch:1.7.0-cuda11.0

# 运行新镜像,创建一个cpu运行的容器
# -i: 交互式操作。
# -t: 终端。
# /bin/bash:放在镜像名后的是命令,这里我们希望有个交互式 Shell,因此用的是 /bin/bash
docker run -it --name torch_cpu anibali/pytorch:1.7.0-cuda11.0 /bin/bash

# 运行新镜像,创建一个gpu运行的容器
docker run -it --name torch_gpu --gpus all anibali/pytorch:1.7.0-cuda11.0 /bin/bash

# 查看所有的容器命令如下:
docker ps -a

# 查看当前正在运行的容器命令如下:
docker ps -l

# 启动已被停止的容器
docker start CONTAINER_ID/CONTAINER_NAME

# 停止运行中的容器
docker stop CONTAINER_ID/CONTAINER_NAME

# 重启容器
docker restart CONTAINER_ID/CONTAINER_NAME

# 删除容器(-f是指强制执行)
docker rm -f CONTAINER_ID/CONTAINER_NAME
docker rm CONTAINER_ID/CONTAINER_NAME

# 进入容器
docker exec -it CONTAINER_ID/CONTAINER_NAME /bin/bash

# 重命名容器
# docker rename <Old_Name> <New_Name>
docker rename testtorch torch_gpu

# 拷贝本地文件到容器
# docker cp 本地路径 容器ID:容器路径
# docker cp <Local_File> <CONTAINER_ID/CONTAINER_NAME>:/workspace/
docker cp MLP 95909784d85b:/workspace/

# 查看容器端口的映射情况
docker port CONTAINER_ID/CONTAINER_NAME

# 指定宿主机与容器端口的映射(通过参数-p指定端口映射)
docker run -it -d --name CONTAINER_NAME -p 8088:80 IMAGE_NAME

# 保存容器为镜像
docker commit -a <Auther_Name> <CONTAINER_ID/CONTAINER_NAME> <Version_Number>

LLM & 多模态模型

1
CLIP->BLIP->BLIPv2->InstructBLIP->LLAVA->MINIGPT4

理论基础

Fine-tuning: 在预训练模型(通常在一些大规模的通用数据集上训练,可以视作一个基础模型,能够在广泛任务中发挥作用)基础上,通过进一步训练,使其适应特定任务或领域的过程,微调则是在特定数据集上额外训练,以提高模型在特定任务上的性能。

大规模语言模型:从理论到实践 (intro-llm.github.io)

torchkeras—LLM微调

CLIP

GitHub - openai/CLIP: CLIP (Contrastive Language-Image Pretraining), Predict the most relevant text snippet given an image

  • ClIP如何进行预训练:

输入:图片+文字的配对

进入Encoder,生成特征,在特征上作对比学习,特征矩阵里获得正样本和负样本

  • 提出了 prompt engineeringprompt ensemble两种方式来提高模型的准确率

– 识别图片机制:用图片给到图片编码器,再去和ImageNet的1000个分类词作相关性匹配,把相关性最大的词挑出来,即完成分类。

  • 有趣的应用:
  1. 生成图
  2. 物体的分割和检测
  3. 视频检索(OCR)

Clip横向对比

Prompt engineering

为什么要做:

  1. Prompt的多义性
  2. 预训练时基本都是一个句子,很少是一个单词,可如果做推理的时候,输入只是单词,抽取出来的特征可能就不好

CLIP的解决方案:Prompt template:“A photo of a {label}”

使用了这个template后,准确度提升了1.3%

  • CLIP工作最大的贡献,在于打破了固定种类标签的范式

CLIP源码解析

Phi3v

源码

四、Todo-Task

  1. 熟悉phi2、phi3的训练代码,在4090上把MIMIC-CXR的对齐跑起来

  2. Captioning评估指标代码(BLEU、ROUGE、F1等等)

  3. 测phi3-v在三个数据集的zero-shot精度(EXMatch)

  4. 测phi3-v在LLaVA-Med的精度(人类评估、GPT4评估)

建议先试试phi-3v能不能跑(4090不支持flash-attention,可能显存会不够):
1、先把mimic数据整理成PubMed对齐数据一样(参考/data/liangx/Phi-3CookBook/data/)
2、参考/data/liangx/Phi-3CookBook/src/dataset/* 写数据集读取的类
3、改训练代码;


这是Phi3-V的代码:https://github.com/microsoft/Phi-3CookBook/blob/main/code/04.Finetuning/vision_finetuning/finetune_hf_trainer_docvqa.py
文档:https://github.com/microsoft/Phi-3CookBook/blob/main/md/04.Fine-tuning/FineTuning_Vision.md


Phi2的代码:https://github.com/DLYuanGod/TinyGPT-V/tree/main
论文:TinyGPT-V: Efficient Multimodal Large Language Model via Small Backbones

Captioning评估代码可以参考(137行)https://github.com/ecoxial2007/DCG_Enhanced_distilGPT2/blob/main/train_ver4_iu_xray.py

直接用 /data/liangx/Phi-3CookBook/llava-qa_eval_after.json 去试,能输出coco和chexbert几个指标就行

Author

Jiawei Hu

Posted on

2024-09-02

Updated on

2024-09-29

Licensed under


Comments