You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

多标签图像分类加权训练/验证/测试集划分(PyTorch实现)

Dataframe Image

问题分析

你面对的是典型的多标签数据集分层拆分问题:单样本可关联多个疾病标签,常规单标签分层抽样无法同时兼顾所有类别的分布均衡,尤其当前数据集存在多个小样本类别(Fibrosis仅25例、Hernia仅1例),随机拆分很容易出现稀有类别仅分布在某一个子集的问题,直接影响模型验证和测试结果的可信度。

拆分方案选择

直接用**迭代分层法(Iterative Stratification)**即可,这是目前多标签数据集拆分的主流方案:算法每一轮优先分配带有「当前分布偏差最大的稀有标签」的样本,将其放到该标签占比最低的子集,迭代直到所有样本分配完成,能最大程度让三个子集的每一类疾病占比和原数据集保持一致。

注:Hernia类别仅1个样本,不存在任何拆分方法能让三个子集同时包含该类样本,若需要评估模型对该类的识别能力,需先补充该类样本。

PyTorch 实现代码

依赖安装

pip install pandas numpy torch scikit-multilearn pillow
scikit-multilearn内置了迭代分层拆分的成熟实现,不需要自己手写算法逻辑。

完整实现步骤

import pandas as pd
import numpy as np
import torch
from torch.utils.data import Dataset, DataLoader
from skmultilearn.model_selection import iterative_train_test_split
from PIL import Image

# ----------------------
# 1. 配置全局参数
# ----------------------
# 所有疾病类别,和你给出的统计结果一一对应
DISEASE_LABELS = [
    'Atelectasis', 'Infiltration', 'No Finding', 'Emphysema', 'Pneumonia',
    'Pleural_Thickening', 'Pneumothorax', 'Mass', 'Nodule', 'Consolidation',
    'Edema', 'Cardiomegaly', 'Effusion', 'Fibrosis', 'Hernia'
]
LABEL2IDX = {lab: i for i, lab in enumerate(DISEASE_LABELS)}
N_CLASSES = len(DISEASE_LABELS)
BATCH_SIZE = 32

# 加载你自己的数据集DataFrame,替换成你的实际路径
# df = pd.read_csv("your_dataset_metadata.csv")

# ----------------------
# 2. 多标签分层拆分
# ----------------------
# 构造多热标签矩阵,形状为(样本总数, 类别数)
y = np.zeros((len(df), N_CLASSES), dtype=np.int8)
for i, label_str in enumerate(df['Finding_Labels'].values):
    # 按你数据集实际的标签分隔符切分,截图中为|分隔
    for lab in label_str.split('|'):
        if lab in LABEL2IDX:
            y[i, LABEL2IDX[lab]] = 1

# 特征用样本索引即可,后续靠索引取对应的数据
X = np.arange(len(df)).reshape(-1, 1)

# 第一次拆分:80%训练集,剩余20%为待拆分的临时集
X_train, y_train, X_temp, y_temp = iterative_train_test_split(X, y, test_size=0.2)
# 第二次拆分:临时集对半分,得到10%验证集、10%测试集
X_val, y_val, X_test, y_test = iterative_train_test_split(X_temp, y_temp, test_size=0.5)

# 提取各子集对应的DataFrame行索引
train_idx = X_train.flatten()
val_idx = X_val.flatten()
test_idx = X_test.flatten()

# 可选:校验各子集标签分布
def check_dist(label_matrix, label_names):
    total = len(label_matrix)
    return {name: round(label_matrix[:,i].sum()/total*100, 2) for i,name in enumerate(label_names)}
print("原集标签占比%:", check_dist(y, DISEASE_LABELS))
print("训练集标签占比%:", check_dist(y_train, DISEASE_LABELS))
print("验证集标签占比%:", check_dist(y_val, DISEASE_LABELS))
print("测试集标签占比%:", check_dist(y_test, DISEASE_LABELS))

# ----------------------
# 3. 对接PyTorch数据加载逻辑
# ----------------------
class ChestXRayDataset(Dataset):
    def __init__(self, sub_df, transform=None):
        self.df = sub_df.reset_index(drop=True)
        self.transform = transform
        # 预加载子集标签,避免训练时重复计算
        self.labels = np.zeros((len(self.df), N_CLASSES), dtype=np.float32)
        for i, label_str in enumerate(self.df['Finding_Labels'].values):
            for lab in label_str.split('|'):
                if lab in LABEL2IDX:
                    self.labels[i, LABEL2IDX[lab]] = 1.0

    def __len__(self):
        return len(self.df)

    def __getitem__(self, idx):
        # 替换成你实际的图像读取逻辑,Image_Index对应当前df里的图像路径列
        img = Image.open(self.df.iloc[idx]['Image_Index']).convert('RGB')
        if self.transform:
            img = self.transform(img)
        label = torch.tensor(self.labels[idx])
        return img, label

# 生成各子集数据集,可自行添加训练/验证对应的图像增强
# train_transform = ... # 训练集增强:随机裁剪、翻转、归一化等
# val_transform = ... # 验证/测试集变换:仅 resize、归一化
train_dataset = ChestXRayDataset(df.iloc[train_idx])
val_dataset = ChestXRayDataset(df.iloc[val_idx])
test_dataset = ChestXRayDataset(df.iloc[test_idx])

# 生成DataLoader
train_loader = DataLoader(train_dataset, batch_size=BATCH_SIZE, shuffle=True, num_workers=4)
val_loader = DataLoader(val_dataset, batch_size=BATCH_SIZE, shuffle=False, num_workers=4)
test_loader = DataLoader(test_dataset, batch_size=BATCH_SIZE, shuffle=False, num_workers=4)

补充说明

  • 正常拆分后,样本量大于100的类别,三个子集的占比和原数据集偏差可以控制在0.5%以内,小样本类别偏差也不会超过2%。
  • 若不想引入scikit-multilearn依赖,可参考sechidis等人2011年提出的迭代分层原理论文自行实现,但直接用库的成熟实现稳定性更高,拆分速度也更快。
  • 拆分完成后必须做标签分布校验,避免拆分逻辑bug导致分布偏差过大。

内容的提问来源于stack exchange,提问作者Shloakr

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.28 00:24:37