当前位置:   article > 正文

EEG深度学习实战:脑电信号通过CNN神经网络进行二分类_cnn 脑电数据

cnn 脑电数据

2023/3/11 -3/13 脑机接口学习内容一览:

        

        这一篇文章主要建立在前文处理EEG脑电信号生成时频图dataset的基础上对数据集进行操作,通过CNN神经网络做到二分类,尝试使用深度学习的模式来预测分类脑电信号。


一、需求分析

        我们需要在前文生成的数据集的基础上建立自己的数据集,并稍微修改参考资料【3】中提供的神经网络,用我们的训练集来修正网络参数,最后通过测试集来检验准确率。

1.数据集

        数据集结构如图:

 

         1代表rt事件,2代表square事件,具体参考前文。

2.输出数据

        测试集的预测准确率。


二、处理流程

        1.导入数据集;

        2.根据数据集结构构建自己的dataset class;

        3.构建神经网络,导入训练集进行训练,并使用tensorboard查看模型结构;

        4.导入测试集,输出准确率。


三、完整代码

  1. import torch.optim
  2. from torch.utils.data import Dataset
  3. from torchvision import transforms
  4. from torch.utils.data import DataLoader
  5. from PIL import Image
  6. import os
  7. import torch.nn as nn
  8. from torch.utils.tensorboard import SummaryWriter
  9. class tf_dataset(Dataset):
  10. def __init__(self, data_dir, transform=None):
  11. """
  12. 分类任务的Dataset
  13. :param data_dir: str, 数据集所在路径
  14. :param transform: torch.transform,数据预处理
  15. """
  16. self.label_name = {"1": 0, "2": 1}
  17. self.data_info = self.get_img_info(data_dir) # data_info存储所有图片路径和标签,在DataLoader中通过index读取样本
  18. self.transform = transform
  19. def __getitem__(self, index):
  20. path_img, label = self.data_info[index]
  21. img = Image.open(path_img).convert('RGB') # 0~255
  22. if self.transform is not None:
  23. img = self.transform(img) # 在这里做transform,转为tensor等等
  24. return img, label
  25. def __len__(self):
  26. # 返回数据长度(图片数量)
  27. return len(self.data_info)
  28. @staticmethod
  29. def get_img_info(data_dir):
  30. data_info = list()
  31. # 子文件夹(类别)分别对应的类别标签
  32. label_name = {"1": 0, "2": 1}
  33. for root, dirs, _ in os.walk(data_dir):
  34. # 深度遍历类别,本质上是遍历root文件夹下所有子文件
  35. for sub_dir in dirs:
  36. # 将子文件夹下的所有图片文件名称str列入list中
  37. img_names = os.listdir(os.path.join(root, sub_dir))
  38. # 筛选所有png类型图片
  39. img_names = list(filter(lambda x: x.endswith('.png'), img_names))
  40. # 遍历图片list
  41. for i in range(len(img_names)):
  42. img_name = img_names[i]
  43. # 获取当前图片路径
  44. path_img = os.path.join(root, sub_dir, img_name)
  45. # 根据图片的父文件夹的名称获取图片所属类别
  46. label = label_name[sub_dir]
  47. # 增加(图片路径, 类别)元组
  48. data_info.append((path_img, label))
  49. return data_info
  50. # 构造神经网络
  51. class my_cnn(nn.Module):
  52. def __init__(self):
  53. super(my_cnn, self).__init__()
  54. self.module = nn.Sequential(
  55. nn.Conv2d(3, 3, 5, 1, 2),
  56. nn.MaxPool2d(5),
  57. nn.Conv2d(3, 32, 5, 1, 2),
  58. nn.MaxPool2d(2),
  59. nn.Conv2d(32, 32, 5, 1, 2),
  60. nn.MaxPool2d(2),
  61. nn.Conv2d(32, 64, 5, 1, 2),
  62. nn.ReLU(),
  63. nn.MaxPool2d(2),
  64. nn.Flatten(),
  65. nn.Linear(64*4*4, 64),
  66. nn.Linear(64, 2)
  67. )
  68. def forward(self, x):
  69. x = self.module(x)
  70. return x
  71. # device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")
  72. device = "cpu"
  73. cnn_model = my_cnn().to(device)
  74. print(cnn_model)
  75. """
  76. 1.导入数据集
  77. """
  78. split_dir = os.path.join(os.getcwd(), 'data/root')
  79. train_dir = os.path.join(split_dir, 'train')
  80. test_dir = os.path.join(split_dir, 'test')
  81. # 构建transforms预处理方式
  82. trans_resize = transforms.CenterCrop((160, 160))
  83. trans_totensor = transforms.ToTensor()
  84. trans_normal = transforms.Normalize([0.5, 0.5, 0.5], [0.5, 0.5, 0.5])
  85. pre_transform = transforms.Compose([trans_resize, trans_totensor, trans_normal])
  86. # 创建可用数据集
  87. train_data = tf_dataset(data_dir=train_dir, transform=pre_transform)
  88. test_data = tf_dataset(data_dir=test_dir, transform=pre_transform)
  89. print("训练集长度为:{}".format(len(train_data)))
  90. print("测试集长度为:{}".format(len(test_data)))
  91. # 构建DataLoader
  92. batch_size = 32 # 确定batch_size
  93. train_loader = DataLoader(dataset=train_data, batch_size=batch_size, shuffle=True)
  94. print(train_data[0][0].size())
  95. test_loader = DataLoader(dataset=test_data, batch_size=batch_size)
  96. # 确定损失函数
  97. loss_fn = nn.CrossEntropyLoss()
  98. # 创建网络模型
  99. EEGnet = my_cnn()
  100. # 优化器
  101. learning_rate = 0.008
  102. optimizer = torch.optim.SGD(EEGnet.parameters(), lr=learning_rate)
  103. # 训练轮数
  104. epoch = 50
  105. # 已训练轮数
  106. total_train_step = 0
  107. # 已测试轮数
  108. total_test_step = 0
  109. # 添加tensorboard
  110. writer = SummaryWriter("./logs_train")
  111. for i in range(epoch):
  112. print("----------第{}轮训练开始----------".format(i+1))
  113. EEGnet.train()
  114. for data in train_loader:
  115. imgs, targets = data
  116. # print(imgs.size())
  117. # print(targets)
  118. # 数据导入模型生成结果
  119. outputs = EEGnet(imgs)
  120. # print(outputs.shape)
  121. loss = loss_fn(outputs, targets)
  122. # 优化器优化模型
  123. optimizer.zero_grad() # 清除优化梯度
  124. loss.backward() # 反向传播计算梯度值
  125. optimizer.step() # 参数更新
  126. total_train_step += 1
  127. # 训练50次打印1次
  128. if total_train_step % 10 == 0:
  129. print("训练次数:{}, loss = {}".format(total_train_step, loss.item()))
  130. writer.add_scalar("train_loss", loss.item(), total_train_step)
  131. # 测试(验证)步骤开始(评估模型是否训练好)
  132. EEGnet.eval()
  133. total_test_loss = 0
  134. total_accuracy = 0
  135. # 若梯度近似为0
  136. with torch.no_grad():
  137. for data in test_loader:
  138. imgs, targets = data
  139. outputs = EEGnet(imgs)
  140. loss = loss_fn(outputs, targets)
  141. total_test_loss += loss.item()
  142. # 求取准确率
  143. accuracy = (outputs.argmax(1) == targets).sum()
  144. total_accuracy += accuracy
  145. print("整体测试集上的Loss = {}".format(total_test_loss))
  146. print("整体测试集上的正确率 = {}".format(total_accuracy*1.0/len(test_data)))
  147. writer.add_scalar("test_loss", total_test_loss, total_test_step)
  148. writer.add_scalar("test_accuracy", total_accuracy*1.0/len(test_data), total_test_step)
  149. total_test_step += 1
  150. # 保存模型参数,以训练轮数区分
  151. torch.save(EEGnet, "model_save/EEGnet_{}.path".format(i))
  152. print("模型已保存")
  153. writer.close()


四、结果展示与分析 

        根据参考资料【4】中对于命令行中tensorboard的操作方法,我们可以在开启的tensorboard界面中获取这样的训练过程图像:

 

 

        从这里可以看出训练的误差在不断减小。 

        

        50轮训练稍微有点多了,容易过拟合,最后出来的准确率其实不太好看。在思考之后总结出了以下几点可能的错误:
        将通道分离开绘制同一epoch下的各个通道的时频图,然后进行训练预测有一些不妥当,在同一类别(rt, square)下的各通道的时频图像很显然属于不同的分布,在之后会尝试一下使用平均诱发电位evoke进行绘制和预测。


五、参考文献

【1】Pytorch数据读取机制(DataLoader)与图像预处理模块(transforms)

【2】CNN数据集——自己建立数据集要点

【3】脑电EEG代码开源分享 【6. 分类模型-深度学习篇】

【4】PyTorch深度学习快速入门教程(绝对通俗易懂!)【小土堆】

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/从前慢现在也慢/article/detail/248665
推荐阅读
相关标签
  

闽ICP备14008679号