pytorch:对小鼠的脑电数据进行睡眠状态三分类
本文采用PyTorch框架,用实验室采集到老鼠大脑数据来学习一波多分类。
1.特征提取
我的数据是26只老鼠的mat形式的脑电数据,每个老鼠有15个通道,每个老鼠在不同的睡眠状态截取30段数据,每一段数据长为10s。 这里采集的睡眠状态有三种,分别是awake,rem,和sws
这里为了更好的获取时域和频域的特征,分别用相关矩阵和转移熵矩阵作为我们分类的特征,然后用pca方法对相关矩阵和转移熵矩阵各提取前30个特征,总共得到60个特征,最终得到的数据为(2340,60)的矩阵,接下来就可以开始处理了。
2.用pytorch分类
首先分出训练集和测试集
data_size = np.shape(all_sample)[0]
feature_size = np.shape(all_sample)[1]
n = int(data_size / 3) # 每个睡眠状态的样本数
X = all_sample
Y = np.zeros(data_size) # 为每个样本打label
Y[n:2 * n] = 1
Y[2 * n:] = 2
# 用sklearn里面的MinMaxScaler进行归一化
scaler_minmax = MinMaxScaler(feature_range=(-1, 1)) # 设置变换范围
X_minmax = scaler_minmax.fit_transform(X)
# 随机选取30%的数据作为测试集
X_train, X_test, Y_train, Y_test = train_test_split(X_minmax, Y, test_size=0.3)
# 样本数据转tensor float32
# label 转tensor int64
X_train = torch.from_numpy(X_train).to(torch.float).to(device)
X_test = torch.from_numpy(X_test).to(torch.float).to(device)
Y_train = torch.from_numpy(Y_train).to(torch.int64).to(device)
Y_test = torch.from_numpy(Y_test).to(torch.int64).to(device)
构建数据迭代器
pytorch 的迭代器可以很方便分批导入batch块,我们这里设置batch_size为30.
batch_size = 30
train_dataset = torch.utils.data.TensorDataset(X_train, Y_train)
test_dataset = torch.utils.data.TensorDataset(X_test, Y_test)
train_iter = torch.utils.data.DataLoader(train_dataset, batch_size=batch_size, shuffle=True, num_workers=0)
test_iter = torch.utils.data.DataLoader(test_dataset, batch_size=batch_size, shuffle=False, num_workers=0)
构建网络
用nn.Swquential()函数来构建网络。因为我们用到的神经网络中每一层之前都是全连接,所以可以用线性层加激活函数层来进行正向传播。隐藏层神经元个数设置为20,激活函数直接调用了nn里面的sigmoid函数(ReLU也可以,并且训练速度会快很多)。pytorch中的初始化可以用torch.nn中的init.normal_()函数直接对我们构建的net进行初始化。
# 模型构建
num_inputs, num_outputs, num_hiddens = feature_size, 3, 20
net = nn.Sequential(
nn.Linear(num_inputs, num_hiddens),
nn.Sigmoid(),
# nn.Linear(num_hiddens, num_hiddens),
# nn.ReLU(),
nn.Linear(num_hiddens, num_outputs)
# torch交叉熵的损失函数自带softmax运算,这里就没有再加一层激活函数
)
# 参数初始化
for param in net.parameters():
init.normal_(param, mean=0, std=0.01)
准确率评估
训练完成后需要对测试集进行评估,我们先构建一个评估函数.这里模型的输出net(x)的形状是(batch_size, 3),所以判断输出结果时,用.argmax(dim=1)返回第二维的最大值的index。然后用.item()提取结算结果。
def acc(test_iter, net, device):
acc_sum, n = 0.0, 0
for x, y in test_iter:
acc_sum += (net(x.to(device)).argmax(dim=1) == y.to(device)).sum().item()
n += y.shape[0]
return acc_sum / n
用该评估器来评估还未训练的模型对测试集的识别率,输出结果应该分布在1/3左右:
acc(test_iter, net, device)
Out[3]: 0.31196581196581197
模型训练
损失函数使用torch中的交叉熵损失函数
# 交叉熵损失函数
loss = torch.nn.CrossEntropyLoss()
优化器采用torch.optim中的SGD方法(Adam使用方法也类似),这里设置学习率为0.01,正则化参数为0.0001
# 优化器
optimizer = torch.optim.SGD(params=net.parameters(), lr=0.01, weight_decay=0.0001)
#optimizer = torch.optim.Adam(params=net.parameters(), lr=0.01, weight_decay=0.0001)
接下来开始训练,在pytorch中梯度的计算十分方便,我们可以直接在创建tensor时用requires_grad=True来设置是否跟踪梯度,在每个batch中只需用.backward()函数就可获得梯度,然后用optimizer.step()自动更新net中的参数。具体代码如下
num_epochs = 500
for epoch in range(num_epochs):
train_l_sum, train_acc_sum, n = 0.0, 0.0, 0
for x, y in train_iter:
y_hat = net(x)
l = loss(y_hat, y).sum()
optimizer.zero_grad() # 优化器的梯度清零
l.backward() # 反向传播梯度计算
optimizer.step() # 优化器迭代
train_l_sum += l.item()
train_acc_sum += (y_hat.argmax(dim=1) == y).sum().item()
n += y.shape[0]
test_acc = acc(test_iter, net, device)
print('epoch %d, loss %.4f, train acc %.3f, test acc %.3f'
% (epoch + 1, batch_size * train_l_sum / n, train_acc_sum / n, test_acc))
3. 分类结果
最终可以得到97%左右的正确率