CLAM是一种基于深度学习的数据高效、弱监督的全幻灯片(WSI)级的全自动分析工具, 是一种高通量且可解释的方法,使用WSI级标签对整个WSI图像 进行数据高效分类,可自动识别WSI中的组织区域,并提取patch坐标,并自动提取patch级别的特征,但并不真正对patch进行提取和保存,减少计算资源的消耗和内存的占用,可在无需进行ROI标注或者patch级别标签的情况下,基于注意力机制和多示例学习自动识别具有高诊断价值的patch,进而实现整个WSI的实例级别分类。
创建环境
conda create -n clam python==3.10
添加清华镜像
conda config --add channels https://mirrors.tuna.tsinghua.edu.cn/anaconda/pkgs/main
conda config --add channels https://mirrors.tuna.tsinghua.edu.cn/anaconda/pkgs/free
pip config set global.index-url https://pypi.tuna.tsinghua.edu.cn/simple
安装必要的库
conda install openslide
pip install timm==0.9.8 torch torchvision h5py pandas PyYAML opencv-python matplotlib scikit-learn scipy tqdm openslide-python tensorboardX
pip install git+https://github.com/oval-group/smooth-topk.git
安装UNI
git clone https://github.com/mahmoodlab/UNI.git
cd UNI
pip install -e .
安装CONCH
git clone https://github.com/mahmoodlab/CONCH.git
cd CONCH
pip install -e .
下载模型权重
从 Huggingface 模型页面请求访问模型权重:
https://huggingface.co/Mahmoodlab/UNI
https://huggingface.co/MahmoodLab/CONCH
克隆repo并cd进入目录
git clone https://github.com/mahmoodlab/CLAM.git
cd CLAM
CLAM可以处理多种标准格式的WSI影像(.svs、.ndpi、.tiff 等),要求WSI存放在同一个路径(DATA_DIRECTORY
)下。
DATA_DIRECTORY/
├── slide_1.svs
├── slide_2.svs
└── ...
python create_patches_fp.py --source DATA_DIRECTORY --save_dir RESULTS_DIRECTORY --seg --patch --stitch
其中参数含义如下:
source
:包含原始WSI文件的文件夹地址save_dir
:预处理后数据保存的地址seg
:一个标志,如果设置,保存组织分割mask的缩略图patch
:一个标志,如果设置,执行patch切分,保存ROI内patch 的坐标stitch
:一个标志,如果设置,生成图像拼接操作(将patch还原到原始WSI尺寸上,其余部分为黑色背景)除此之外,还可以设置一些可选参数:
step_size
:在WSI上切分patch的步长,默认值256patch_size
:patch切分的尺寸,默认值256no_auto_skip
: 一个标志,用于控制是否自动跳过已经处理过的图像,默认为True,即不自动跳过preset
: 指定一个预定义的分割和过滤参数配置文件的路径(CSV格式),这些参数用于分割、过滤等步骤,默认为空patch_level
: 指定生成图像块时使用的下采样级别,默认为0,即在原始尺寸裁剪process_list
: 指定一个包含要处理的图像列表及其参数的CSV文件的路径,可以为每张WSI指定不同的参数,与preset
不同,是为了进行个性化处理,默认为空上述命令将使用默认参数对 DATA_DIRECTORY
中的每个WSI进行分割,提取分割组织区域内的所有patch,使用提取的patch为每个WSI创建拼接重建(可选),并在指定的 RESULTS_DIRECTORY
中生成以下文件夹结构:
RESULTS_DIRECTORY/
├── masks
├── slide_1.png
├── slide_2.png
└── ...
├── patches
├── slide_1.h5
├── slide_2.h5
└── ...
├── stitches
├── slide_1.png
├── slide_2.png
└── ...
└── process_list_autogen.csv
其中:
masks
文件夹包含组织的分割结果(每张WSI对应一张)patches
文件夹包含从每张WSI中提取的patch数组(每张WSI一个 .h5文件,其中每个条目对应于patch左上角的坐标)stitches
文件夹包含缝合patch的下采样可视化(每张WSI一张图像)(可选,不用于下游任务)process_list_autogen.csv
是自动生成的参数文件,包含所有已处理的WSI的列表,以及它们使用的参数。为了确保高质量分割和提取相关组织patch,用户可以自行设置分割参数,选择执行预分割(通常每张WSI约 1 秒),检查分割结果并根据需要调整选定WSI的参数,如此反复,直至能有效分割组织区域,然后使用自定义的分割参数提取patch。具体步骤如下:
python build_preset.py --preset_name CSV_FILE_NAME
上述命令用来创建一个预设配置文件,该文件包含了图像分割和处理参数,可以设置的各个参数的详细解释和默认值如下:
presets_name
保存分割参数的文件名称,.csv
格式,保存在/presets
文件夹下seg_level
用于分割操作的图像金字塔级别,默认值:-1(表示自动选择最适合的级别,使用 WSI 中最接近 64 倍下采样的下采样)sthresh
分割阈值,用于确定哪些区域被认为是组织(使用较高的阈值会导致前景检测较少,背景检测较多),默认值:8(正整数)mthresh
中值滤波器大小,用于微调组织分割,默认值:7(正数,奇数整数)close
用于在分割后处理中进行形态学闭操作的核大小,默认值:4(正整数或-1)use_otsu
是否使用Otsu算法自动计算分割阈值,默认值:False(表示使用简单二进制阈值)keep_ids / exclude_ids
在生成mask过程中保留或排除特定的WSI,默认值:nonea_t
过滤参数,用于确定保留的最小ROI大小,默认值:100(表示处理检测到面积大于100512512大小的组织)a_h
过滤参数,用于确定识别的孔洞的最大面积,默认值:16(表示处理检测到面积大于16512512大小的孔洞)max_n_holes
过滤参数,每个前景轮廓需要考虑的最大孔洞数量,默认值:8(正整数,最大值越高,修补越准确,但计算成本也越高)vis_level
用于分割结果可视化的图像金字塔级别,默认值:-1(使用最接近 64 倍下采样的 WSI 中的下采样)line_thickness
绘制分割结果的线条粗细,默认值:500white_thresh
参数设置一个饱和度阈值,用于决定是否将某个patch视为空白并排除,这有助于排除几乎没有信息的patch,默认值为5black_thresh
参数设置一个平均RGB阈值,用于决定是否将某个patch视为黑色并排除,这有助于过滤掉可能不包含有用信息的黑色patch默认值为50use_padding
在切patch操作中是否使用padding以保持边缘patch大小符合要求,默认值:Truecontour_fn
判断怎样将patch视为前景,默认值:‘four_pt’(“four_pt”: 检查patch中心周围的小网格中的所有四个点是否都在轮廓内,“center” :检查patch的中心是否在轮廓内,“basic” - 检查patch的左上角是否在轮廓内)patch_leve
用于提取patch的图像金字塔级别,默认值:0在完成分割参数设置之后,可以依据上一步保存的参数文件进行预分割,命令如下:
python create_patches_fp.py --source DATA_DIRECTORY --save_dir RESULTS_DIRECTORY --seg --presets /presets/CSV_FILE_NAME
或者,使用默认参数对 DATA_DIRECTORY
中的每个WSI进行组织识别,命令如下:
python create_patches_fp.py --source DATA_DIRECTORY --save_dir RESULTS_DIRECTORY --seg
上述命令将在mask
文件夹下缩略图,并生成 process_list_autogen.csv
文件,但暂时不会进行修补(patches
和stitches
文件夹将为空)。
在RESULTS_DIRECTORY/masks
文件夹下查看预分割缩略图,可以针对效果不好的特定WSI调整参数配置文件,在调整分割参数之前,用户应复制 csv 文件并为其指定一个新名称(例如 process_list_edited.csv
),否则下次运行命令时会覆盖此具有默认名称的文件。然后,用户可以选择通过更改 csv 文件中的相应字段来调整特定WSI的参数,并通过 --process_list CSV_FILE_NAME
传递。
python create_patches_fp.py --source DATA_DIRECTORY --save_dir RESULTS_DIRECTORY --seg --process_list CSV_FILE_NAME
其中,process
列存储一个二进制变量(0 或 1),用于指示脚本是否应处理特定WSI,以便用户处理并观察少数几个WSI来快速确认调整后的参数是否产生令人满意的结果。
当对所有的预分割结果满意时,用户应该将所有需要处理的WSI的process
列设为 1,并保存 csv格式的CSV_FILE_NAME
文件,然后使用保存的 csv 文件运行create_patches_fp.py
(就像在全自动预处理的运行demo中一样),具体代码如下:
python create_patches_fp.py --source DATA_DIRECTORY --save_dir RESULTS_DIRECTORY --seg --process_list CSV_FILE_NAME --patch --stitch
运行之后会分别在RESULTS_DIRECTORY/patches
和RESULTS_DIRECTORY/patches
路径下生成包含patch左上角坐标的.h5
文件和缝合patch的.jpg
可视化结果。
特征提取需要运行extract_features_fp.py
脚本,可以使用命令行调用
CUDA_VISIBLE_DEVICES=0 python extract_features_fp.py --data_h5_dir DIR_TO_COORDS --data_slide_dir DATA_DIRECTORY --csv_path CSV_FILE_NAME --feat_dir FEATURES_DIRECTORY --batch_size 512 --slide_ext .svs
上述命令会读取存储在DIR_TO_COORDS
下的 .h5
格式的patch坐标文件,批处理大小由 batch_size
参数传入,从原始WSI的每个patch中提取预训练特征,默认使用预训练的resnet50
网络,特征维度为1024,如果使用UNI
和CONCH
作为预训练编码器提取特征,需要提前指定模型目录所在的环境变量,并设置model_name
为uni_v1
或者conch_v1
,UNI
的特征维度为10024,CONCH
的特征维度为512,环境变量设置方法如下:
export CONCH_CKPT_PATH=checkpoints/conch/pytorch_model.bin
export UNI_CKPT_PATH=checkpoints/uni/pytorch_model.bin
命令执行后将生成以下文件夹结构:
FEATURES_DIRECTORY/
├── h5_files
├── slide_1.h5
├── slide_2.h5
└── ...
└── pt_files
├── slide_1.pt
├── slide_2.pt
└── ...
其中每个 .h5 文件包含提取的特征数组及其patch坐标(请注意,为了加快训练速度,还会为每张WSI创建一个 .pt 文件,其中仅包含patch的特征),提取命令的其他超参数如下:
data_h5_dir
数据集的HDF5文件目录,用于指定存储图像patches
的HDF5文件的位置,默认值:None(必须由用户指定)data_slide_dir
WSI图像文件的目录,用于指定WSI文件的存储位置,默认值:None(必须由用户指定)slide_ext
WSI文件的扩展名,默认值:.svs
(常见的WSI格式之一)csv_path
csv 文件应包含要处理的WSI文件名列表(不带文件扩展名)(可以指定上一个自动生成的 csv 文件,然后删除文件扩展名)feat_dir
特征保存目录,用于指定提取的特征保存的位置,默认值:None(必须由用户指定)model_name
用于特征提取的预训练模型,默认值:resnet50_trunc,可选值包括 resnet50_trunc
、 uni_v1
、 conch_v1
batch_size
批处理大小,默认值:256no_auto_skip
是否自动跳过已处理的WSI,默认值:False(如果指定此选项,则不会自动跳过)target_patch_size
目标patch大小,用于指定提取的patch的大小,默认值:224为了评估算法的性能,可以使用多倍(例如 10 倍)训练、验证、测试划分(80:10:10)。可以使用 create_splits_seq.py
脚本自动生成这些分割,只需进行少量修改,就像使用main.py
一样。例如,可以通过调用以下命令创建是否有肿瘤的分类:
python create_splits_seq.py --task task_1_tumor_vs_normal --seed 1 --k 10
其中,可传入参数如下:
label_frac
: 用于指定数据集中使用的标签比例,默认值为1.0,表示使用全部标签seed
: 随机种子,用于确保数据分割的可重复性,默认值为1k
: 分割的数量,即进行k折交叉验证时的k值,默认值为10task
: 任务类型,有两个选项:task_1_tumor_vs_normal
和task_2_tumor_subtyping
,分别对应肿瘤与正常组织的分类任务和肿瘤亚型的分类任务val_frac
: 用于设置验证集的标签比例,默认值为0.1test_frac
: 用于设置测试集的标签比例,默认值为0.1。目前只支持良恶性鉴别(task_1_tumor_vs_normal
,二分类)和肿瘤亚型鉴别(task_2_tumor_subtyping
,三分类)两种任务,如果需要自定义进行其他任务,需要按照Generic_WSI_Classification_Dataset
类要求的数据格式自行编写python脚本配置,有关详细信息,请参阅datasets/dataset_generic.py
中的数据集定义。数据集的划分结果会按照要求保存在splits
文件夹下,具体格式如下:
splits/
├── {task_name}_{label_frac}
├── splits_0.csv
├── splits_0_bool.csv
├── splits_0_descriptor.csv
├── ...
├── splits_9.csv
├── splits_9_bool.csv
└── splits_9_descriptor.csv
├── ...
├── ...
└── {task_name}_{label_frac}
├── splits_0.csv
├── splits_0_bool.csv
├── splits_0_descriptor.csv
├── ...
├── splits_9.csv
├── splits_9_bool.csv
└── splits_9_descriptor.csv
划分好数据集之后,只需要一行代码即可开始训练。
CUDA_VISIBLE_DEVICES=0 python main.py --drop_out 0.25 --early_stopping --lr 2e-4 --k 10 --exp_code task_1_tumor_vs_normal_CLAM_50 --weighted_sample --bag_loss ce --inst_loss svm --task task_1_tumor_vs_normal --model_type clam_sb --log_data --data_root_dir DATA_ROOT_DIR --embed_dim 1024
注意:--embed_dim
为输入特征的维度,默认值为1024,而对于 CONCH
提取的预训练特征, 应设置为 512。其余具体参数如下:
data_root_dir
: 数据根目录,没有默认值,用户必须指定max_epochs
: 训练的最大轮数,默认值为200lr
: 学习率,默认值为0.0001label_frac
: 训练标签的比例,默认值为1.0,表示使用全部标签reg
: 权重衰减,默认值为1e-5seed
: 随机种子,默认值为1,用于实验的可重复性k
: 折数,默认值为10,用于k折交叉验证k_start
: 开始的折数,默认值为-1,表示从第一折开始k_end
: 结束的折数,默认值为-1,表示到最后一折结束results_dir
: 结果保存目录,默认值为./resultssplit_dir
: 数据集划分目录,默认值为None,用户可以手动指定使用的划分数据集log_data
: 是否使用tensorboard记录数据,默认为Falsetesting
: 调试工具,默认为Falseearly_stopping
: 是否启用早停,默认为Falseopt
: 优化器,默认为Adamdrop_out
: Dropout值,默认值为0.25bag_loss
: 包级别的损失函数,svm
(支持向量机损失)和 ce
(交叉熵损失,默认值)中二选一model_type
: 模型类型,可选:clam_sb
(单分支注意力MIL,默认值)、 clam_mb
(多分支注意力MIL)、mil
(普通MIL)exp_code
: 结果文件保存的地址,没有默认值,用户必须指定weighted_sample
: 是否启用加权采样,默认为Falsemodel_size
: 模型大小,可选'small'
(默认值)或者'big'
,默认为smalltask
: 任务类型,没有默认值,用户必须从task_1_tumor_vs_normal
和task_2_tumor_subtyping
中选择no_inst_cluster
: 是否禁用实例级聚类,使用后会提升模型的泛化性能,默认为Falseinst_loss
: 实例级聚类的损失函数,默认值为Nonesubtyping
: 是否为肿瘤亚型分类问题,默认为False,但是如果模型选择了clam_sb
或者 clam_mb
,则必须设置为Truebag_weight
: 包级别损失的权重系数,当no_inst_cluster
为False时启用,默认值为0.7B
: 对于clam_sb
和 clam_mb
模型,每次从包中采样的数量,正负样本采样的数量,较小的值可能导致模型无法充分学习包内所有实例的信息,而较大的值则可能增加计算成本并导致过拟合,默认值为8而对于不属于task_1_tumor_vs_normal
和task_2_tumor_subtyping
的其他任务,则需要按照main.py的形式自定义训练函数。
用户还可以选择使用评估脚本来测试已训练模型的性能。下面提供了与上述训练模型相对应的示例:
CUDA_VISIBLE_DEVICES=0 python eval.py --k 10 --models_exp_code task_1_tumor_vs_normal_CLAM_50_s1 --save_exp_code task_1_tumor_vs_normal_CLAM_50_s1_cv --task task_1_tumor_vs_normal --model_type clam_sb --results_dir results --data_root_dir DATA_ROOT_DIR --embed_dim 1024
其中大多数参数与main.py
中一致,不同的参数如下:
save_exp_code
:用于指定保存评估结果的实验代码models_exp_code
:用于指定加载训练好的模型的实验代码micro_average
:用于设置多类别分类任务中评价指标的平均方法。当设置为True时,使用micro-average
(不平衡数据集)CLAM还可以批量进行热图可视化,填写配置文件并存储在/heatmaps/configs
中,然后使用 --config NAME_OF_CONFIG_FILE
标志运行create_heatmaps.py
,代码如下:
CUDA_VISIBLE_DEVICES=0 python create_heatmaps.py --config config_template.yaml
具体参数如下: