一次失败的CNN实现
前言
- 这是一次较为“失败”的分类器尝试,在本次报告中尝试实现了CNN模型对EEG图像进行学习,进行了大量调参的尝试,并在二分类问题上与其他简单模型进行比较
- 使用python tensorflow(vision=1.13.0)
- 最好的试验中测试集的准确率超过80%,但是由于测试集的选取原因这个数字没有很好的说服力,并不能说明在那个参数下模型有很好的泛化能力(见正则化)
- 虽然结果并不好,但在大量的学习中对机器学习的基本框架和如何调整参数有个更好的理解,本次报告中的CNN及其他网络也能很好地移植到其他问题当中
简介
数据
- 由于EEG的五个电极的数据存在一定的相关性,因此考虑用CNN模型来保持一定的结构。由于数据集中的EEG图较小,我们考虑将每个160为数据转化为5x32的数据进行卷积操作。
CNN网络
- 原因
- 相关的学术研究中也有用CNN对EEG数据进行学习的研究,但他的数据集,每个数据的维度均比我们大,也有尝试用3维CNN网络训练EEG数据的成功例子
- 先对DEEP库愉悦度进行学习(二分类)
- 网络结构
选择模型参数较小的原因是数据量小,实验结果表明可能过拟合。 - 使用的库
//使用的库
import tensorflow as tf #机器学习库
import numpy as np #python基本库
from matplotlib.pyplot import * #画图
import random #用于添加噪声
- 数据处理
对数据进行中心化和标准化,减少极端数据的影响,提高模型的泛化能力
// data 中心化和标准化
def MaxMinNormalization(x):
x=np.transpose(x)
return np.transpose(np.array([(x[i] - np.average(x[i]))/np.std(x[i]) for i in range(x.shape[0])]))
train_data=MaxMinNormalization(train_data)
模型调整
我们将前1000个作为训练集,后216个作为测试集进行训练来粗略地确定一些参数
- 损失函数选择
我们在均方误差和交叉熵中选择,在梯度下降的情况下训练1000次。(优化器Adam)
![]() |
![]() |
可以看出交叉熵的收敛速度要快于均方误差,虽然交叉熵更容易遇到过拟合问题,在测试集上准确率下降。但考虑到计算能力及大量尝试发现两者并无在调好参数后并无太大差别,最后测试中我们选择用交叉熵来加快学习。
- 优化器和学习率选择:
Tensorflow中提供了很多优化器的算法,我们选择了一些优化器进行测试,针对学习率1.0 0.1 0.01……中取最好的结果,由于时间和的计算能力的限制,我们每个只训练500次,选取一开始收敛最快的算法(学习率过小会训练缓慢,过大会导致最后震荡不收敛,我们选择较大的学习率但又控制最后的震荡幅度)
![]() |
![]() |
![]() |
- 增加池化层:
h_pooli=max_pool_2x2(h_convi)
可以看出如果数据库的数据是良好的,那么我们认为我们的模型存在严重的过拟合问题(高方差),我们尝试用各种方法来减小方差
减少过拟合的尝试
- dropout
对于全连接层采取dropout方法,训练1000次
// 采用新tf中的keep_prob
keep_prob=tf.placeholder(tf.float32)
drop=1-keep_prob
![]() |
![]() |
![]() |
![]() |
可以看出dropout在0.75左右的时候可以减少过拟合并保持较快的收敛效果
- 正则化
采取L2正则,取不同数量级的lambda,采用cross validation来选取最好的(dropout=0.75)将1216个数据分为8个组,每组152个,输出平均的训练集和测试集上的正确度。交叉验证的代码见后
// L2 regulation
tf.contrib.layers.l2_regularizer(lambda)(W)
![]() |
![]() |
![]() |
![]() |
![]() |
![]() |
![]() |
- 最后我们加上千分之2的正态噪声,进行训练
//add noise
rand=np.random.randint(0,high=1000, size=500)
noise=np.random.normal(0,1/500,[1216,160])
train_data_noised=np.add(train_data,noise)
![]() |
![]() |
![]() |
可见不同的验证集所需要的参数相差较大,这可能是因为我们没有对样本做最佳的预处理(按人平均没有更好的效果),或许在某些分类情况下能很较好地学习,但在平均意义下这个模型不能很好地预测该二分类问题
与全连接网络比较
-
全连接网络
我们使用两个隐层的四层全连接网络与我们的模型进行比较,隐层的节点个数通过 n i = n i − 1 ∗ n i + 1 n_{i}=\sqrt{n_{i-1}*n_{i+1}} ni=ni−1∗ni+1来确定(一个简单的确定较好的节点个数的方法) -
故第一个隐层36个节点,第二个隐层9个节点
总体来说CNN的效果比全连接稍好,
全连接的正则化较难调整
与SVM网络比较
- 由于数据相近,及时在某一数据分类下用SVM获得较好的拟合,一旦改变数据分割,在测试集上的准确度也会快速下降。
- 代码见下节
模型运用到其他分类标准
对MAHNOB-HCI库中的EEG_emotion_category进行9分类
运用CNN代码简单地更改参数即可
//生成label集合
def switch(var):
return{
0.0:np.eye(9)[0],
1.0:np.eye(9)[1],
2.0:np.eye(9)[2],
3.0:np.eye(9)[3],
4.0:np.eye(9)[4],
5.0:np.eye(9)[5],
6.0:np.eye(9)[6],
11.0:np.eye(9)[7],
12.0:np.eye(9)[8],
}.get(var,'error')
train_label=np.array([])
for i in range(533):
train_label=np.append(train_label,switch(label[i]),axis=0)
train_label=train_label.reshape([-1,9])
结果分析
- 整体的训练效果不佳,原因可能有三
- CNN计算开销过大,确定交叉验证非常粗糙,并且其余多个变量之间基本是单一改变确定的,因此若调参时间较长可一定程度上提高准确率
- 缺乏EEG相关知识和数据库的了解,可能缺少对数据的处理或是随机的裁剪
- 从训练结果可知若是大量增加训练集合可能会有更好的效果,尤其是在数据较小的时候,并不是非常适合采用CNN,相反采用GANS等模型可能结果更好
代码
CNN网络代码
// CNN network
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Created on Tue Apr 2 17:14:27 2019
@author: rain
"""
import tensorflow as tf