解决pytorch 二分类数据集全部预测到了同一类

问题描述

二分类 0与1(阴性与阳性)

训练集和验证集训练分布

训练集数据验证集数据
阳性(1)2130238
阴性(0)894108
总计3024346

训练方法

损失函数:BCELoss
优化器:Adam

训练结果

在训练的同时进行验证, 发现训练集和验证集的准确率保持不变
训练集准确率=0.704365 验证集准确率=0.687861
训练集和验证集全部被预测为阳性

解决办法

1. 修改学习率

初始采用的优化器是Adam, 学习率是0.001
尝试将学习率修改为0.0001, 发现训练集的准确率仍然保持不变, 而验证集的准确率有所提高, 提高至0.731213

启示
将学习率由大到小调试, 如果准确率有所变化,则在训练时,如果验证集的损失在几个epoch 内没有下降,可以减少学习率

关键代码

# 更新LR 和 早停
for epoch in tqdm(range(0, 64)):
	...
	...
	# best_epoch 和 best_loss 分别指验证集上最好的损失和对应的epoch
	if (epoch - best_epoch) > 3:
	  	if val_loss > best_loss:
	      	print("decay loss from " + str(LR) + " to " + str(LR / 2) + " as not seeing improvement in val loss")
	      	LR = LR / 2
	      	print("created new optimizer with LR " + str(LR))
	
	      	if (epoch - best_epoch) > 10:
	          	print("no improvement in 10 epochs, break")
	          	break
如何寻找最优的初始学习率

理论详解
关键代码

train_data_path = 'train.csv'
cropped_images_floder = 'cropped_images'

train_df = pd.read_csv(train_data_path)
train_size = len(train_df)

train_dataset = ImageData(cropped_images_floder=cropped_images_floder, df=train_df, channel_copy=True,
                          transform=None)
train_loader = DataLoader(dataset=train_dataset, batch_size=4, shuffle=True,
                          num_workers=1)

device = torch.device("cuda:1" if torch.cuda.is_available() else "cpu")
criterion = nn.BCELoss(reduction='mean').to(device)

model = get_model(modelType='resnet50', pretrained=True, num_labels=1, hidden_dropout=0.2)
model = model.to(device)
optimizer = torch.optim.Adam(params=filter(lambda p: p.requires_grad, model.parameters()), lr=1e-5,
                             betas=(0.9, 0.99))


def find_lr(init_value=1e-8, final_value=10., beta=0.98):
    # 755
    num = len(train_loader) - 1
    # (10**9)**(1/755)
    mult = (final_value / init_value) ** (1 / num)
    lr = init_value
    optimizer.param_groups[0]['lr'] = lr
    avg_loss = 0.
    best_loss = 0.
    batch_num = 0
    losses = []
    log_lrs = []
    for i, data in enumerate(train_loader, 0):
        batch_num += 1
        # As before, get the loss for this mini-batch of inputs/outputs
        imgs, labels, _ = data
        imgs, labels = imgs.to(device), labels.to(device)
        optimizer.zero_grad()
        outputs = model(imgs)
        loss = criterion(outputs, labels)
        # Compute the smoothed loss
        avg_loss = beta * avg_loss + (1 - beta) * loss.item()
        smoothed_loss = avg_loss / (1 - beta ** batch_num)
        # Stop if the loss is exploding
        if batch_num > 1 and smoothed_loss > 4 * best_loss:
            return log_lrs, losses
        # Record the best loss
        if smoothed_loss < best_loss or batch_num == 1:
            best_loss = smoothed_loss
        # Store the values
        losses.append(smoothed_loss)
        log_lrs.append(math.log10(lr))
        # Do the SGD step
        loss.backward()
        optimizer.step()
        # Update the lr for the next step
        lr *= mult
        optimizer.param_groups[0]['lr'] = lr
    return log_lrs, losses


logs, losses = find_lr()
plt.plot(logs[10:-5],losses[10:-5])
plt.savefig('search_best_lr.png')

2. 查看各类样本的分布,使用采样的方法WeightedRandomSampler

训练模型时,需要保证训练集和验证集中阳性和阴性数据之比尽可能1:1。
阴性样本很少从而导致模型倾向于总体样本全预测为阳性。
pytorch 中 WeightedRandomSampler 用于平衡数据集的样本类别不平衡
在这里插入图片描述
参数和返回值介绍

  1. weights = [ ] 是指数据集中每个样本的权重, len(weights) = 数据集的样本总数
    计算方式:数据集样本总数 / 单个样本所属类别的数量
  2. num_samples : 需要采样的数量,可以根据自己需要的训练集样本数量设置
  3. replacement: 采样方式 True: 有放回采样;False: 无放回采样
  4. 返回值:从[0, len(weights) -1]中取出num_samples个数, list中的每个数可以作为index供DataLoader取用

关键代码

# dataset类添加get_classes_for_all_images()函数,用于返回训练集中每个样本的标签
class ImageData(Dataset):
    def __init__(self, cropped_images_floder, df, channel_copy=True, transform=None):
        self.cropped_images_floder = cropped_images_floder
        self.transform = transform
        self.df = df
        self.channel_copy = channel_copy
        self.random_number_generator = np.random.RandomState(0)

    def __getitem__(self, idx):
      ...

    def get_classes_for_all_images(self):
        return self.df['label'].tolist()

    def __len__(self):
        return self.df.shape[0]
# 由于训练集样本分布不均匀,给每个样本分配权重进行重采样
# class_counts: 数据集中,每一类的数目,这里是二分类,只有0和1
cropped_images_floder = '/home/user1/data/cropped_images'
train_data_df = pd.read_csv('/home/user1/data/train.csv')
class_counts = [train_data_df['label'].value_counts()[0], train_data_df['label'].value_counts()[1]]
print(class_counts)		# [894, 2130]

# 每个类别的权重
weights = 1. / torch.tensor(class_counts, dtype=torch.float)
print(weights)			# tensor([0.0011, 0.0005])

# train_targets: 训练集中每个样本的标签
train_dataset = ImageData(cropped_images_floder=cropped_images_floder, df=train_data_df, channel_copy=True,
                          transform=None)
train_targets = train_dataset.get_classes_for_all_images()
print(train_targets, len(train_targets))
# [0, 1, 0, ..., 1, 1, 1] 3024

# sample_weights: tensor每个样本的权重=训练集总数/该样本所属类别的数量, 所以长度为训练集的样本数
samples_weights = weights[train_targets] * len(train_data_df)
print(samples_weights, len(samples_weights))
# tensor([3.3826, 1.4197, 3.3826,  ..., 1.4197, 1.4197, 1.4197]) 3024

# num_samples: 表示需要采样的个数,可按照自己的需要设置, replacement:True为有放回地采样,False为无放回地采样
# 返回的sampler 是[0,..,len(weights)-1]之间的任意数。
sampler = WeightedRandomSampler(weights=samples_weights, num_samples=len(samples_weights), replacement=True)
print(list(sampler), len(list(sampler)))
# [137, 127, 1247, 646, 3010, 1543, 1098, 1367, ...] 3024

# shuffle 一定要设置为False, 因为上一步获得sampler时是随机采样,采样的顺序是乱的
train_loader = torch.utils.data.DataLoader(dataset=train_dataset, batch_size=4, shuffle=False,
                                           sampler=sampler, num_workers=1)

测试代码

true_labels = []
for i, data in enumerate(train_loader, 0):
    images, labels, path = data
    for item in labels.cpu().data.numpy():
        for j in item:
            true_labels.append(int(j))
print(pd.value_counts(true_labels)
# 0    1541
# 1    1483
# dtype: int64

问题
使用这种方法, 可能会导致丢失原始数据, 即并不是所有的原始数据都有被采样到和被模型训练

3. 在计算损失时对不同标签的样本赋予不同大小的权重 Focal_Loss()

原理参考博文

class Focal_Loss(torch.nn.Module):
    """
    二分类Focal Loss
    """
    def __init__(self, alpha=0.25, gamma=2):
        super(Focal_Loss, self).__init__()
        self.alpha = alpha
        self.gamma = gamma

    def forward(self, preds, labels):
        """
        preds:sigmoid的输出结果
        labels:标签
        """
        eps = 1e-7
        loss_1 = -1 * self.alpha * torch.pow((1 - preds), self.gamma) * torch.log(preds + eps) * labels
        loss_0 = -1 * (1 - self.alpha) * torch.pow(preds, self.gamma) * torch.log(1 - preds + eps) * (1 - labels)
        loss = loss_0 + loss_1
        return torch.mean(loss)

4. 在训练集进行随机采样

将小类别的全部样本送进训练,并且在每个epoch 都从大类别样本中随机采样,采样与小类别样本相同数量的样本送进训练

  • 7
    点赞
  • 39
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
要使用Pytorch进行信号分类,你需要构建自己的数据集并将其转换成Pytorch可以用于训练的Dataset数据类型。根据引用和引用的内容,你可以按照以下步骤进行操作: 1. 获取数据集:首先,你需要获得你的信号分类数据集。根据引用的描述,你的数据集存放在"./data"文件夹下,其中训练图像存放在"./data/image/train/"文件夹下,对应的图像标签存放在"./data/train.labels.csv"文件中。 2. 数据预处理:在将数据集转换成Pytorch可以用于训练的Dataset数据类型之前,你可能需要进行一些数据预处理的步骤,如图像的归一化、标签的编码等。根据你的具体需求,你可以使用Pytorch提供的数据处理工具库来完成这些任务。 3. 自定义Dataset类:根据引用的描述,你需要重写Pytorch的Dataset类。你可以创建一个新的类,继承自torch.utils.data.Dataset,并实现__len__和__getitem__方法。在__getitem__方法中,你可以根据图像文件路径和标签文件的内容,读取图像和标签数据,并进行必要的预处理。 4. 划分训练集和验证集:根据引用的描述,你可以使用sklearn库提供的函数,按照你的需求将训练图像划分为训练集和验证集。可以按照比例进行随机划分,确保训练集和验证集的样本数量适当。 5. 创建DataLoader对象:最后,你可以使用torch.utils.data.DataLoader类来创建一个数据加载器。你可以指定批量大小、是否打乱数据和多线程加载等参数,以便在训练过程中高效地加载数据。 通过按照上述步骤操作,你可以成功构建并转换你的信号分类数据集,使其可以在Pytorch中用于训练。这样,你就可以使用Pytorch来构建和训练你的神经网络模型了。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *3* [【神经网络】Pytorch构建自己的训练数据集](https://blog.csdn.net/ISASUKEI/article/details/121610626)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] - *2* [基于梅尔频谱的音频信号分类识别(Pytorch)](https://blog.csdn.net/guyuealian/article/details/120601437)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值