天池入门赛-心跳信号分类预测-PyTorch CNN模型

天池赛-心跳信号分类预测

在这里插入图片描述

赛题简介

赛题以预测心电图心跳信号类别为任务,数据集报名后可见并可下载,该数据来自某平台心电图数据记录,总数据量超过20万,主要为1列心跳信号序列数据,其中每个样本的信号序列采样频次一致,长度相等。为了保证比赛的公平性,将会从中抽取10万条作为训练集,2万条作为测试集A,2万条作为测试集B,同时会对心跳信号类别(label)信息进行脱敏。

FieldDescription
id为心跳信号分配的唯一标识
heartbeat_signals心跳信号序列
label心跳信号类别(0、1、2、3)

评测标准

选手需提交4种不同心跳信号预测的概率,选手提交结果与实际心跳类型结果进行对比,求预测的概率与真实值差值的绝对值(越小越好)。

具体计算公式如下:
针对某一个信号,若真实值为[ y 1 y_1 y1, y 2 y_2 y2, y 3 y_3 y3, y 4 y_4 y4], 模型预测概率值为[ a 1 a_1 a1, a 2 a_2 a2, a 3 a_3 a3, a 4 a_4 a4], 那么该模型的平均指标 a b s − s u m abs-sum abssum

a b s − s u m abs-sum abssum = ∑ y = 1 n ∑ i = 1 4 ∣ y i − a i ∣ \displaystyle\sum_{y=1}^{n}\displaystyle\sum_{i=1}^{4} |y_i -a_i| y=1ni=14yiai
例如,心跳信号为1, 会通过编码转成[0, 1, 0, 0], 预测不同心跳信号概率为[0.1, 0.7, 0.1, 0.1], 那么这个预测结果的 a b s − s u m abs-sum abssum
a b s − s u m abs-sum abssum = ∣0.1−0∣+∣0.7−1∣+∣0.1−0∣+∣0.1−0∣=0.6

数据分析

这一部分在天池nootbook上已有作者提供代码示例,在此引用。链接如下:
Task 2 数据分析

CNN模型

这个CNN模型是用PyTorch框架实现的。
大概思路如下:
在这里插入图片描述
希望上图能够帮助新手理解卷积层和采样层是个什么意思,图片大小有限没能画出kernel和kernel怎么移动的。对于每一层输入的shape可以结合笔者代码中的注释进行推算。
也可参考PyTorch的文档和Source code进行理解,链接如下:
nn.Conv1d

class Model(nn.Module):
    def __init__(self):
        """
            CNN模型构造
        """
        super(Model, self).__init__()
        self.conv_layer1 = nn.Sequential(
            # input shape(32, 1, 205) -> [batch_size, channel, features]
            # 参考->https://pytorch.org/docs/stable/generated/torch.nn.Conv1d.html#torch.nn.Conv1d
            nn.Conv1d(in_channels=1, out_channels=16, kernel_size=3, padding=1),   # 卷积后(16, 1, 205)
            nn.BatchNorm1d(16),
            nn.ReLU()
        )
        # 下采样down-sampling
        self.sampling_layer1 = nn.Sequential(
            # input shape(32, 16, 205)
            nn.Conv1d(in_channels=16, out_channels=32, kernel_size=3, padding=1),
            nn.BatchNorm1d(32),
            nn.ReLU(),
            nn.MaxPool1d(kernel_size=2, stride=2),  # size随便选的, 这里output应该是(32, 32, 102)
        )

        self.conv_layer2 = nn.Sequential(
            nn.Conv1d(in_channels=32, out_channels=64, kernel_size=3, padding=1),   # 输出(32, 64, 102)
            nn.BatchNorm1d(64),
            nn.ReLU()
        )

        self.sampling_layer2 = nn.Sequential(
            nn.Conv1d(in_channels=64, out_channels=128, kernel_size=3, padding=1),  # 输出(32, 128, 102)
            nn.BatchNorm1d(128),
            nn.ReLU(),
            nn.MaxPool1d(kernel_size=2, stride=2),  # 输出(32, 64, 51)
        )

        self.conv_layer3 = nn.Sequential(
            nn.Conv1d(in_channels=128, out_channels=256, kernel_size=3, padding=1),  # 输出(32, 256, 51)
            nn.BatchNorm1d(256),
            nn.ReLU()
        )

        self.sampling_layer3 = nn.Sequential(
            nn.Conv1d(in_channels=256, out_channels=512, kernel_size=3, padding=1),  # 输出(32, 512, 51)
            nn.BatchNorm1d(512),
            nn.ReLU(),
            nn.MaxPool1d(kernel_size=2, stride=2),  # 输出(32, 512, 25)
        )
        # 全连接层
        self.full_layer = nn.Sequential(
            nn.Linear(in_features=512*25, out_features=256*25),
            nn.ReLU(),
            nn.Linear(in_features=256*25, out_features=128*25),
            nn.ReLU(),
            nn.Linear(in_features=128*25, out_features=64*25),
            nn.ReLU(),
            nn.Linear(in_features=64*25, out_features=4)
        )
        # 这个是输出label预测概率, 不知道这写法对不对
        self.pred_layer = nn.Softmax(dim=1)

    def forward(self, x):
        """
            前向传播
        :param x: batch
        :return: training == Ture 返回的是全连接层输出, training == False 加上一个Softmax(), 返回各个label概率.
        """
        x = x.unsqueeze(dim=1)  # 升维. input shape(32, 205), output shape(32, 1, 205)
        x = self.conv_layer1(x)
        x = self.sampling_layer1(x)
        x = self.conv_layer2(x)
        x = self.sampling_layer2(x)
        x = self.conv_layer3(x)
        x = self.sampling_layer3(x)
        x = x.view(x.size(0), -1)   # output(32, 12800)
        x = self.full_layer(x)

        if self.training:
            return x	# CrossEntropyLoss自带LogSoftmax, 所以训练的时候不用输出概率(我也不知道这个写法对不对, 我是试错出来的.)
        else:
            return self.pred_layer(x)
   

损失函数

Cross Entropy Loss:
这个损失函数是多分类问题经常使用的, 需要注意的是Cross Entropy Loss结合了LogSoftmax和NLLLoss, 如果你在输出层使用了Softmax可能会导致你的模型无法拟合.
参考链接: Cross Entropy Loss
在这里插入图片描述
L1 Loss:
在评分标准中有题提到这个比赛采用的是 a b s − s u m abs-sum abssum = ∑ y = 1 n ∑ i = 1 4 ∣ y i − a i ∣ \displaystyle\sum_{y=1}^{n}\displaystyle\sum_{i=1}^{4} |y_i -a_i| y=1ni=14yiai
这个其实就是PyTorch中的nn.L1Loss()或者F.l1_loss(). PyTorch默认的是mean absolute error (MAE), 但根据文档你只要将reduction设置为 reduction=‘sum’ 就可以了. 这不就成了sum absolute error(SAE)了.
L1 Loss的参考链接: nn.L1Loss
在这里插入图片描述

主要代码

一下是主要代码供大家参考.

def train_loop(dataloader, model, loss_fn, optimizer):
    """
        模型训练部分
    :param dataloader: 训练数据集
    :param model: 训练用到的模型
    :param loss_fn: 评估用的损失函数
    :param optimizer: 优化器
    :return: None
    """
    for batch, x_y in enumerate(dataloader):
        X, y = x_y[:, :205].type(torch.float64), torch.tensor(x_y[:, 205], dtype=torch.long, device='cuda:0')
        # 开启梯度
        with torch.set_grad_enabled(True):
            # Compute prediction and loss
            pred = model(X.float())
            loss = loss_fn(pred, y)
            optimizer.zero_grad()
            # Backpropagation
            loss.backward()
            optimizer.step()


def test_loop(dataloader, model, loss_fn):
    """
        模型测试部分
    :param dataloader: 测试数据集
    :param model: 测试模型
    :param loss_fn: 损失函数
    :return: None
    """
    size = len(dataloader.dataset)
    test_loss, correct, l1_loss = 0, 0, 0
    # 用来计算abs-sum. 等于PyTorch L1Loss-->
    # https://pytorch.org/docs/stable/generated/torch.nn.L1Loss.html#torch.nn.L1Loss
    l1loss_fn = AbsSumLoss()
    with torch.no_grad():   # 关掉梯度
        model.eval()
        for x_y in dataloader:
            X, y = x_y[:, :205].type(torch.float64), torch.tensor(x_y[:, 205], dtype=torch.long, device='cuda:0')
            # 注意Y和y的区别, Y用来计算L1 loss, y是CrossEntropy loss.
            Y = torch.zeros(size=(len(y), 4), device='cuda:0')
            for i in range(len(Y)):
                Y[i][y[i]] = 1

            pred = model(X.float())
            test_loss += loss_fn(pred, y).item()    # 这个是CrossEntropy loss
            l1_loss += l1loss_fn(pred, Y).item()    # 这个是abs-sum/L1 loss
            correct += (pred.argmax(1) == y).type(torch.float).sum().item()  # 这个是计算准确率的, 取概率最大值的下标.

    test_loss /= size   # 等于CrossEntropy的reduction='mean', 这里有些多此一举可删掉.
    correct /= size
    print(f"Test Results:\nAccuracy: {(100*correct):>0.1f}% abs-sum loss: {l1_loss:>8f} CroEtr loss: {test_loss:>8f}")


def prediction(net, loss):
    """
        对数据进行预测
    :param net: 训练好的模型
    :param loss: 模型的测试误差值, 不是损失函数. 可以去掉, 这里是用来给预测数据命名方便区分.
    :return: None
    """
    with torch.no_grad():
        net.eval()
        pred_loader = torch.utils.data.DataLoader(dataset=pred_data)
        res = []
        for x in pred_loader:
            x = torch.tensor(x, device='cuda:0', dtype=torch.float64)
            output = net(x.float())
            res.append(output.cpu().numpy().tolist())

        res = [i[0] for i in res]
        res_df = pd.DataFrame(res, columns=['label_0', 'label_1', 'label_2', 'label_3'])
        res_df.insert(0, 'id', value=range(100000, 120000))

        res_df.to_csv('res-loss '+str(loss)+'.csv', index=False)


class Model(nn.Module):
    def __init__(self):
        """
            CNN模型构造
        """
        super(Model, self).__init__()
        self.conv_layer1 = nn.Sequential(
            # input shape(32, 1, 205) -> [batch_size, channel, features]
            # 参考->https://pytorch.org/docs/stable/generated/torch.nn.Conv1d.html#torch.nn.Conv1d
            nn.Conv1d(in_channels=1, out_channels=16, kernel_size=3, padding=1),   # 卷积后(32, 16, 205)
            nn.BatchNorm1d(16),
            nn.ReLU()
        )
        # 下采样down-sampling
        self.sampling_layer1 = nn.Sequential(
            # input shape(32, 16, 205)
            nn.Conv1d(in_channels=16, out_channels=32, kernel_size=3, padding=1),
            nn.BatchNorm1d(32),
            nn.ReLU(),
            nn.MaxPool1d(kernel_size=2, stride=2),  # size随便选的, 这里output应该是(32, 32, 102)
        )

        self.conv_layer2 = nn.Sequential(
            nn.Conv1d(in_channels=32, out_channels=64, kernel_size=3, padding=1),   # 输出(32, 64, 102)
            nn.BatchNorm1d(64),
            nn.ReLU()
        )

        self.sampling_layer2 = nn.Sequential(
            nn.Conv1d(in_channels=64, out_channels=128, kernel_size=3, padding=1),  # 输出(32, 128, 102)
            nn.BatchNorm1d(128),
            nn.ReLU(),
            nn.MaxPool1d(kernel_size=2, stride=2),  # 输出(32, 64, 51)
        )

        self.conv_layer3 = nn.Sequential(
            nn.Conv1d(in_channels=128, out_channels=256, kernel_size=3, padding=1),  # 输出(32, 256, 51)
            nn.BatchNorm1d(256),
            nn.ReLU()
        )

        self.sampling_layer3 = nn.Sequential(
            nn.Conv1d(in_channels=256, out_channels=512, kernel_size=3, padding=1),  # 输出(32, 512, 51)
            nn.BatchNorm1d(512),
            nn.ReLU(),
            nn.MaxPool1d(kernel_size=2, stride=2),  # 输出(32, 512, 25)
        )
        # 全连接层
        self.full_layer = nn.Sequential(
            nn.Linear(in_features=512*25, out_features=256*25),
            nn.ReLU(),
            nn.Linear(in_features=256*25, out_features=128*25),
            nn.ReLU(),
            nn.Linear(in_features=128*25, out_features=64*25),
            nn.ReLU(),
            nn.Linear(in_features=64*25, out_features=4)
        )
        # 这个是输出label预测概率, 不知道这写法对不对
        self.pred_layer = nn.Softmax(dim=1)

    def forward(self, x):
        """
            前向传播
        :param x: batch
        :return: training == Ture 返回的是全连接层输出, training == False 加上一个Softmax(), 返回各个label概率.
        """
        x = x.unsqueeze(dim=1)  # 升维. input shape(32, 205), output shape(32, 1, 205)
        x = self.conv_layer1(x)
        x = self.sampling_layer1(x)
        x = self.conv_layer2(x)
        x = self.sampling_layer2(x)
        x = self.conv_layer3(x)
        x = self.sampling_layer3(x)
        x = x.view(x.size(0), -1)   # output(32, 12800)
        x = self.full_layer(x)

        if self.training:
            return x    # CrossEntropyLoss自带LogSoftmax, 训练的时候不用输出概率(我也不知道这个写法对不对, 我是试错出来的.)
        else:
            return self.pred_layer(x)


class AbsSumLoss(nn.Module):
    def __init__(self):
        """
            可以直接用PyTorch的nn.L1Loss, 这个我写的时候不知道。
        """
        super(AbsSumLoss, self).__init__()

    def forward(self, output, target):
        loss = F.l1_loss(target, output, reduction='sum')

        return loss


if __name__ == '__main__':
    set_random_seed(1996)   # 设定随机种子
    # 加载数据集
    data = pd.read_csv('train.csv')
    data = process_data(data)
    pred_data = pd.read_csv('testA.csv')
    pred_data = get_pred_x(pred_data)

    # 初始化模型
    lr_rate = 1e-5
    w_decay = 1e-6
    n_epoch = 100
    b_size = 32
    device = torch.device('cuda:0' if torch.cuda.is_available() else 'cpu')
    net = Model()
    net.to(device)
    optimizer = torch.optim.Adam(params=net.parameters(), lr=lr_rate, weight_decay=w_decay)
    loss_fn = nn.CrossEntropyLoss(reduction='sum')

    # 拆分训练测试集
    train, test = train_test_split(data, test_size=0.2)
    train, test = torch.cuda.FloatTensor(train), torch.cuda.FloatTensor(test)
    train_loader = torch.utils.data.DataLoader(dataset=train, batch_size=b_size)
    test_loader = torch.utils.data.DataLoader(dataset=test, batch_size=b_size)

    for epoch in range(n_epoch):
        start = time.time()
        print(f"\n----------Epoch {epoch + 1}----------")
        train_loop(train_loader, net, loss_fn, optimizer)
        test_loop(test_loader, net, loss_fn)
        end = time.time()
        print('training time: ', end-start)

    # predict

结语

我也是个新手, 没什么经验, 难免存在错误和纰漏还请各位大佬指正. 比赛还在进行中, 如有新的发现和经验会在后续和大家继续分享.

代码

上传了代码,CNN最后B榜的表现是16名。
模型代码

  • 15
    点赞
  • 139
    收藏
    觉得还不错? 一键收藏
  • 21
    评论
### 回答1: 心跳信号分类预测是指使用PyTorch这个强大的深度学习框架,通过训练模型来对心跳信号进行分类预测的任务。心跳信号通常用来判断一个人的心脏健康状况,通过对心跳信号进行分类预测,可以帮助医生对患者的心脏病情有更准确的判断,以及提供合适的治疗方法。 首先,我们需要准备心跳信号的数据集。这个数据集通常会包含一系列心电图信号、心率、心脏病类型等信息。可以使用PyTorch的数据加载工具,如`torchvision`或自定义的数据加载方法来处理数据集。 接下来,我们需要构建一个神经网络模型,用于对心跳信号进行分类预测。可以选择使用PyTorch提供的各种深度学习模型,如卷积神经网络CNN)或循环神经网络(RNN),或者根据实际需求构建自定义的模型。 然后,我们需要将数据划分为训练集和测试集。训练集用于训练模型的参数,而测试集用于评估模型的性能和泛化能力。 模型的训练过程通常包括以下步骤:定义损失函数、选择优化器、迭代数据集、前向传播、计算损失、反向传播和更新模型参数。可以通过调整超参数、增加训练迭代次数等方法来提高模型的准确度和性能。 训练完成后,我们可以使用训练好的模型来对新的心跳信号进行分类预测。通过将新的心跳信号输入到模型中,模型会输出一个预测结果,表示该心跳信号属于什么类型。 当然,在实际应用中,还需要对预测结果进行解释和分析,以便医生做出准确的诊断和治疗决策。同时,还可以使用其他技术和工具来可视化和提取心跳信号的特征,帮助进一步分析和理解心脏病情。总之,PyTorch提供了一种灵活而强大的方式来进行心跳信号分类预测,能够在医疗领域提供更准确和有效的辅助决策。 ### 回答2: PyTorch是一种常用的深度学习框架,用于构建和训练神经网络模型心跳信号分类预测是指基于心电图数据对患者的心跳进行分类预测,例如正常心跳、心律失常等。 首先,我们需要准备心电图数据集。可以从医疗机构、研究论文或公共数据集中获取心电图数据。每个样本包含一段心电图信号和对应的心跳类型标签。 接下来,我们使用PyTorch构建一个神经网络模型。可以选择不同的网络结构,如卷积神经网络 (CNN) 或循环神经网络 (RNN)。模型的输入是一段心电图信号的时间序列数据。 然后,我们使用数据集进行训练。将数据集分为训练集和测试集,通常采用随机划分的方法。通过迭代优化网络参数,使模型能够更好地学习心电图信号心跳类型之间的关系。 在训练过程中,我们可以使用常见的优化算法如随机梯度下降法(SGD)或自适应矩估计算法(Adam)来更新模型参数。此外,还可以使用学习率调度器、正则化技术等进一步提升模型性能。 训练完毕后,我们可以使用测试集对模型进行评估。评估指标可以选择准确率、精确率、召回率等来评估模型的性能。如果模型的性能不理想,可以考虑调整模型结构、调节超参数或增加更多的训练数据。 最后,我们可以使用训练好的模型对新的心电图信号进行预测。将新的心电图信号输入到模型中,模型会输出对应的心跳类型。这样可用于辅助医生诊断心脏疾病、提供个性化的康复方案等。 总之,PyTorch可以帮助我们构建和训练心跳信号分类预测模型。通过合理的数据准备、模型构建和训练优化,可以提高模型预测准确性,为医疗实践提供有用的工具和支持。 ### 回答3: PyTorch是一种开源的深度学习框架,用于构建和训练神经网络模型心跳信号分类预测是指使用PyTorch框架对心电图信号进行分类,以预测心脏病发作风险或诊断心脏病种类。 在进行心跳信号分类预测时,我们首先要获取带有标签的心电图数据集。这些数据集可能来自医疗机构或公开的心电图数据库。然后,我们使用PyTorch进行数据预处理,包括数据清洗、标准化等步骤,同时将数据分为训练集和测试集。 接下来,我们设计心跳信号分类模型。可以选择使用卷积神经网络CNN)或循环神经网络(RNN)等模型架构,根据数据集的特点和任务需求来确定合适的模型。通过在PyTorch中定义网络的结构和参数,我们能够构建自己的模型。 训练过程中,我们使用训练集的数据来优化模型的权重和偏置,以最小化损失函数。可以使用梯度下降等优化算法,通过反向传播计算梯度并更新参数。在PyTorch中,我们可以定义自定义的损失函数和优化器,根据具体需求进行设置。 当模型训练完成后,我们使用测试集的数据对模型进行评估。通过计算准确率、召回率、F1值等指标,评估模型心跳信号分类预测任务上的性能表现。 最后,我们可以使用训练好的模型对新的心电图信号进行预测。将信号输入到模型中,通过前向传播计算模型的输出,即预测心跳信号分类。根据预测结果,可以进行心脏病风险评估或病种诊断。 总之,使用PyTorch框架进行心跳信号分类预测,涉及到数据预处理、模型设计、训练和评估等步骤。通过这些步骤,我们可以构建一个准确预测心跳信号分类模型,为心脏疾病的诊断和风险评估提供支持。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值