虽然说深度学习的教程已经烂大街了,基础理论也比较容易掌握,但是真正让自己去实现的时候还是有一些坑。一方面教程不会涉及太多具体的工程问题,另一方面啃PyTorch的英文文档还是有点麻烦。记录一下,就当是作业报告了。
获取数据集
首先导入所需要的包:
import torch
import torch.nn as nn
import torch.utils.data as Data
import numpy as np
import matplotlib.pyplot as plt
所用的数据集共有500条,输入特征feature_n = 8
,输出类别lables_n = 4
。取前两行数据看看数据的格式:
0.4812 | 0.7790 | 0.8904 | 0.7361 | 0.9552 | 0.2119 | 0.7992 | 0.2409 | 4 |
0.4472 | 0.5985 | 0.7859 | 0.5035 | 0.6912 | 0.4038 | 0.0787 | 0.2301 | 1 |
我们将前400条数据作为训练集,后100条数据作为测试集。
定义一个函数用于从文件中获取此数据集:
def get_data(filename):
dataset = np.loadtxt(filename)
x_train = dataset[0:400,0:8]
raw_y_train = dataset[0:400,8:]
x_test = dataset[400:,0:8]
raw_y_test = dataset[400:,8:]
y_train = np.zeros((400,4),dtype = np.int)
y_test = np.zeros((100,4),dtype = np.int)
y_train = raw_y_train - 1
y_test = raw_y_test - 1
#for i in range(400):
# y_train[i,int(raw_y_train[i])-1]=1
#for j in range(100):
# y_test[j,int(raw_y_test[j])-1]=1
return x_train,y_train,x_test,y_test
从原理上讲Softmax分类器的输出个数应与class label的数量相同,理想情况下正确的那一项为1,其他为0。那么class label为4时,理想预测值应为[0, 0, 0, 1] ,而数据集中给出的值为[4] 。如果是用NumPy手动搭建网络和写Softmax函数,那么就需要把y_train
处理成二维矩阵。
但是对于PyTorch中提供的损失函数torch.nn.CrossEntropyLoss()
来说,数据格式稍有不同。torch.nn.CrossEntropyLoss()
第一个参数y_pred
为batch_size * labels_n大小的矩阵,第二个参数y
是一维的batch_size大小的向量,且数据范围为[0, labels_n -1],所以要在y_train
的基础上减1,把标签1/2/3/4变成0/1/2/3 。
设置批训练
读取进来的数据集还是ndarray格式的,我们先将它们转化为tensor:
x_train,y_train,x_test,y_test = get_data('dataset.txt')
x_train = torch.from_numpy(x_train).type(torch.FloatTensor)
y_train = torch.from_numpy(y_train).type(torch.LongTensor)
注意对转换后的数据格式有要求。这也是损失函数torch.nn.CrossEntropyLoss()
的要求,预测概率必须为float,正确标签必须为long。
之后建立训练集并加载,测试集同理 :
train_set = Data.TensorDataset(x_train,y_train)
train_loader = Data.DataLoader(
dataset=train_set,
batch_size=BATCH_SIZE,
shuffle=True
)
在《动手学习深度学习》书中采用了自己手写的迭代器进行批训练的数据迭代。我个人觉得PyTorch提供的DataLoader工具更加简单好用。
定义模型
采用PyTorch提供的快速搭建法进行搭建神经网络,包含隐藏层和输出层共2层。需要注意一点,损失函数torch.nn.CrossEntropyLoss()
中已经包含了Softmax函数,所以我们的神经网络直接线性输出即可。
net = nn.Sequential(
nn.Linear(8,50),
nn.ReLU(),
nn.Linear(50,4)
)
之后定义一些训练模型的函数和参数:
EPOCH = 5000
BATCH_SIZE = 100
LR = 0.01
LOSS_FUNC = nn.CrossEntropyLoss()
OPTIMIZER = torch.optim.SGD(net.parameters(), lr=LR)
神经网络建立后本身会对参数w、b进行初始化。因为我也不知道应该初始化为多少比较合适,所以在此不进行显式的初始化而采用其默认的。
训练模型
for epoch in range(1,EPOCH+1):
loss_sum = 0.0
for step,(x,y) in enumerate(train_loader):
y_pred = net(x)
y = y.squeeze() #修正标签格式
loss = LOSS_FUNC(y_pred,y)
loss_sum += loss
OPTIMIZER.zero_grad()
loss.backward()
OPTIMIZER.step()
print("epoch: %d, loss: %f" %(epoch,loss_sum/BATCH_SIZE))
之前说过损失函数对数据格式有要求。我们的标签y
实际上还是二维矩阵,大小为batch_size * 1,所以在计算损失函数前需要进行降维。
测试模型
acc_sum = 0.0
acc_sum += (net(x_test).argmax(dim=1) == y_test.squeeze()).sum()
print("test accuracy: %f" %(acc_sum/100))
用测试集进行测试可知准确率能达到93%。