该项目是机器学习的课程作业,代码实现了一个1D卷积神经网络(1D CNN)模型,通过三层卷积层(通道数16→32→64)和最大池化层提取一维序列特征,最终通过全局平均池化与全连接层完成二分类任务。模型使用Adam优化器和BCEWithLogitsLoss损失函数,输入数据为320维特征(经CSVDataset转换为[batch, 1, 320]张量),适用于处理如时间序列或基因序列的二分类问题。

1.导入库

代码开始导入了以下的python库:

PyTorch:用于构建和训练神经网络的核心框架。

nn:神经网络模块(层、激活函数、损失函数)。

optim:优化器(如Adam)。

Dataset 和 DataLoader:数据加载工具。

Pandas:用于读取和处理CSV文件。

NumPy:数值计算工具。  

scikit-learn:提供balanced_accuracy_score用于评估分类性能(适用于类别不平衡数据)。

2.CSVDataset类

该自定义数据集类用于加载CSV文件中的二分类数据,适配PyTorch的Dataset接口,支持数据划分、标签编码、张量转换及可选的预处理操作。

首先读取CSV文件,处理标签编码,划分特征和标签。将标签列中的"A"替换为1,"B"替换为0(适用于二分类任务)。根据split列的值筛选数据,选取第4列及之后的列作为特征,选取第3列作为标签列。

class CSVDataset(Dataset):
   def __init__(self, path, type, transform=None):

       data = pd.read_csv(path)
       data = data.replace("A", 1)
       data = data.replace("B", 0)

       self.x = data.loc[data['split'] == type].iloc[:, 3:].values
       self.y = data.loc[data['split'] == type].iloc[:, 2].values.reshape(-1, 1) 

       self.transform = transform

之后返回数据集的样本总数(即特征数据self.x的行数)。

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

之后根据索引idx返回单个样本的特征和标签。将NumPy数组转为PyTorch张量。为特征添加通道维度,适配Conv1d输入格式,通过transform参数支持自定义预处理。

3.CNN1D类

这是一个用于二分类任务的1D卷积神经网络(CNN),通过多层卷积和池化提取时序特征,最终通过全连接层输出分类结果。

首先进行初始化。卷积层中,conv1:输入单通道(如时序信号),输出16通道,卷积核大小3,填充1(保持输入长度不变)。conv2 和 conv3:通道数递增(16,32,64),逐步提取深层特征。池化层中最大池化(kernel_size=2),每次将序列长度减半。

正则化:Dropout层(丢弃率0.3)减少过拟合风险。全局池化:AdaptiveAvgPool1d(1) 将每个通道的时序特征压缩为1个值,生成全局特征向量。全连接层:将64维特征映射到128维,再输出1维。

class CNN1D(nn.Module):
   def __init__(self):
       super(CNN1D, self).__init__()
       self.conv1 = nn.Conv1d(in_channels=1, out_channels=16, kernel_size=3, padding=1)
       self.relu = nn.ReLU()
       self.pool = nn.MaxPool1d(kernel_size=2)
       self.conv2 = nn.Conv1d(in_channels=16, out_channels=32, kernel_size=3, padding=1)
       self.conv3 = nn.Conv1d(in_channels=32, out_channels=64, kernel_size=3, padding=1)
       self.dropout = nn.Dropout(p=0.3)
       self.global_pool = nn.AdaptiveAvgPool1d(1)
       self.fc1 = nn.Linear(64, 128)
       self.fc2 = nn.Linear(128, 1)

输入数据依次通过三个卷积层(conv1、conv2、conv3),每层后接ReLU激活函数和最大池化(pool),逐步提取特征并压缩序列长度。使用自适应平均池化(global_pool)将特征压缩为全局向量,展平后通过Dropout层随机丢弃部分神经元以防止过拟合。经过两个全连接层(fc1、fc2)映射到最终输出,并通过squeeze(1)调整维度,得到形状为[batch]的二分类结果。

4.train函数代码

训练模式与梯度清零,设置模型为训练模式,遍历数据加载器的每个批次,将输入和标签移至指定设备(如GPU),并清空优化器的历史梯度。

前向传播与损失计算,通过模型前向传播得到预测值,计算损失(criterion,如交叉熵),并调用loss.backward()进行反向传播,生成参数梯度。

参数更新与损失累计,使用优化器更新模型参数,累计批次损失,最终返回整个数据集上的平均损失(总损失除以样本总数)。

def train(model, dataloader, criterion, optimizer, device):
   model.train()
   total_loss = 0.0
   for x, y in dataloader:
       x, y = x.to(device), y.to(device)
       optimizer.zero_grad()
       outputs = model(x)
       loss = criterion(outputs, y.squeeze())
       loss.backward()
       optimizer.step()
       total_loss += loss.item() * x.size(0)
   return total_loss / len(dataloader.dataset)

    5.evaluate函数

    此函数用于评估模型性能,首先调用 model.eval() 将模型设置为评估模式,同时使用 torch.no_grad() 上下文管理器来禁止梯度计算,以减少内存消耗并加快推理速度。接着遍历数据加载器 dataloader 中的每个批次数据,将输入 x 和标签 y 移动到指定设备 device 上,通过模型 model 得到输出 outputs,再使用 torch.sigmoid 函数将输出转换为概率值,最后以 0.5 为阈值将概率值转换为预测标签 preds。最后将真实标签 y 和预测标签 preds 移回 CPU,使用 balanced_accuracy_score 函数计算平衡准确率并返回该评估指标。

    def evaluate(model, dataloader, device):
       model.eval()
       with torch.no_grad():
           for x, y in dataloader:
               x, y = x.to(device), y.to(device)
               outputs = model(x)
               preds = (torch.sigmoid(outputs) > 0.5).float()
       return balanced_accuracy_score(y.cpu(), preds.cpu())

    6.程序主入口

    主要功能即初始化训练所需的各项参数和组件,然后进行模型训练与评估,最后保存训练好的模型。

    设备选择、批次大小和训练轮数设置。device:检查系统是否支持CUDA,若支持则使用 GPU 进行计算,否则使用CPU。batch_size:每次训练时输入到模型中的样本数量,这里设置为60。epochs:模型对整个训练数据集进行训练的轮数,设置为 500 轮。

    device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
    batch_size = 60
    epochs = 500

    数据集和数据加载器创建。train_dataset 和 test_dataset:分别从 CSV 文件中加载训练集和测试集数据,使用自定义的 CSVDataset 类进行处理。 train_loader 和 test_loader:使用 DataLoader 类将数据集封装成可迭代的数据加载器,方便按批次加载数据。训练集数据加载器会在每个 epoch 打乱数据顺序(shuffle=True),测试集则不打乱(shuffle=False)。

    train_dataset = CSVDataset('./ACE_features_320_dimension.csv', type='train')
    test_dataset = CSVDataset('./ACE_features_320_dimension.csv', type='test')
    train_loader = DataLoader(train_dataset, batch_size=60, shuffle=True)
    test_loader = DataLoader(test_dataset, batch_size=277, shuffle=False)

    模型、损失函数和优化器初始化。model:实例化自定义的一维卷积神经网络模型 CNN1D,并将其移动到指定设备(GPU 或 CPU)上。criterion:使用二元交叉熵损失函数 nn.BCEWithLogitsLoss,适用于二分类问题。optimizer:使用 Adam 优化器来更新模型的参数,学习率设置为 0.002。

    model = CNN1D().to(device)
    criterion = nn.BCEWithLogitsLoss()
    optimizer = optim.Adam(model.parameters(), lr=0.002)

    模型训练与评估。acc_best:用于记录模型在测试集上的最佳准确率。

    for 循环:进行 500 轮训练和评估。

    train_loss:调用 train 函数对模型进行一轮训练,并返回该轮的平均训练损失。

    acc:调用 evaluate 函数在测试集上评估模型的准确率。

    acc_best:更新最佳准确率。

    print:打印当前轮次的训练损失、测试准确率和最佳准确率。

    best_acc = 0.0
    best_epoch = 0
    best_model_path = 'cnn1d_best.pth'
    for epoch in range(1, epochs + 1):
       train_loss = train(model, train_loader, criterion, optimizer, device)
       test_acc = evaluate(model, test_loader, device)
       if test_acc > best_acc:
           best_acc = test_acc
           best_epoch = epoch
           torch.save(model.state_dict(), best_model_path)
       print(f"Epoch {epoch}/{epochs} - Loss: {train_loss:.4f} - Test Acc: {test_acc:.4f} - Best Acc: {best_acc:.4f} (Epoch {best_epoch})")

    模型保存。torch.save(model, "cnn1d_binary.pth"):将训练好的模型保存到文件 cnn1d_binary.pth 中。print:输出提示信息,表示训练完成且模型已保存。

    torch.save(model, "cnn1d_binary.pth")
    print("train finished, the model has been saved to cnn1d_binary.pth")
    1. 运行结果

    附录:整体代码:

    import torch
    import torch.nn as nn
    import torch.optim as optim
    from torch.utils.data import Dataset, DataLoader
    import numpy as np
    import pandas as pd
    from sklearn.metrics import balanced_accuracy_score
    
    
    class CSVDataset(Dataset):
       def __init__(self, path, type, transform=None):
    
           data = pd.read_csv(path)
           data = data.replace("A", 1)
           data = data.replace("B", 0)
    
           self.x = data.loc[data['split'] == type].iloc[:, 3:].values
           self.y = data.loc[data['split'] == type].iloc[:, 2].values.reshape(-1, 1) 
    
           self.transform = transform
    
       def __len__(self):
           return len(self.x)
    
       def __getitem__(self, idx):
    
           xi = torch.from_numpy(self.x[idx]).type(torch.FloatTensor)    
           xi = xi.unsqueeze(0)
           yi = torch.tensor(self.y[idx]).type(torch.FloatTensor)
    
           if self.transform:
               xi = self.transform(xi)
           return xi, yi
    
    
    
    class CNN1D(nn.Module):
       def __init__(self):
           super(CNN1D, self).__init__()
           self.conv1 = nn.Conv1d(in_channels=1, out_channels=16, kernel_size=3, padding=1)
           self.relu = nn.ReLU()
           self.pool = nn.MaxPool1d(kernel_size=2)
           self.conv2 = nn.Conv1d(in_channels=16, out_channels=32, kernel_size=3, padding=1)
           self.conv3 = nn.Conv1d(in_channels=32, out_channels=64, kernel_size=3, padding=1)
           self.dropout = nn.Dropout(p=0.3)
           self.global_pool = nn.AdaptiveAvgPool1d(1)
           self.fc1 = nn.Linear(64, 128) 
           self.fc2 = nn.Linear(128, 1)
    
       def forward(self, x):
           x = self.conv1(x)
           x = self.relu(x)
           x = self.pool(x)
           x = self.conv2(x)
           x = self.relu(x)
           x = self.pool(x)
           x = self.conv3(x)
           x = self.relu(x)
           x = self.pool(x)
           x = self.global_pool(x)
           x = x.view(x.size(0), -1)
           x = self.dropout(x)
           x = self.fc1(x)
           x = self.relu(x)
           x = self.dropout(x)
           x = self.fc2(x)
           return x.squeeze(1)
    
    
    def train(model, dataloader, criterion, optimizer, device):
       model.train()
       total_loss = 0.0
       for x, y in dataloader:
           x, y = x.to(device), y.to(device)
           optimizer.zero_grad()
           outputs = model(x)
           loss = criterion(outputs, y.squeeze())
           loss.backward()
           optimizer.step()
           total_loss += loss.item() * x.size(0)
       return total_loss / len(dataloader.dataset)
    
    
    def evaluate(model, dataloader, device):
       model.eval()
       with torch.no_grad():
           for x, y in dataloader:
               x, y = x.to(device), y.to(device)
               outputs = model(x)
               preds = (torch.sigmoid(outputs) > 0.5).float()
       return balanced_accuracy_score(y.cpu(), preds.cpu())
    
    if __name__ == "__main__":
    
       device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
       batch_size = 60
       epochs = 500
    
       train_dataset = CSVDataset('./ACE_features_320_dimension.csv', type='train')
       test_dataset = CSVDataset('./ACE_features_320_dimension.csv', type='test')
       train_loader = DataLoader(train_dataset, batch_size=60, shuffle=True)
       test_loader = DataLoader(test_dataset, batch_size=277, shuffle=False)
       model = CNN1D().to(device)
       criterion = nn.BCEWithLogitsLoss()
       #optimizer = optim.SGD(model.parameters(), lr=0.0005, momentum=0.9)
       optimizer = optim.Adam(model.parameters(), lr=0.002)
    
       
       best_acc = 0.0
       best_epoch = 0
       best_model_path = 'cnn1d_best.pth'
       for epoch in range(1, epochs + 1):
           train_loss = train(model, train_loader, criterion, optimizer, device)
           test_acc = evaluate(model, test_loader, device)
           if test_acc > best_acc:
               best_acc = test_acc
               best_epoch = epoch
               torch.save(model.state_dict(), best_model_path)
           print(f"Epoch {epoch}/{epochs} - Loss: {train_loss:.4f} - Test Acc: {test_acc:.4f} - Best Acc: {best_acc:.4f} (Epoch {best_epoch})")
       
    
       print(f"Training complete. Best accuracy {best_acc:.4f} achieved at epoch {best_epoch}.")
       print(f"Best model weights saved to '{best_model_path}'.")

    Logo

    脑启社区是一个专注类脑智能领域的开发者社区。欢迎加入社区,共建类脑智能生态。社区为开发者提供了丰富的开源类脑工具软件、类脑算法模型及数据集、类脑知识库、类脑技术培训课程以及类脑应用案例等资源。

    更多推荐