import torch
import matplotlib.pyplot as plt
import numpy
import torchvision #需要用到torchversion中的数据集
from IPython import display
#下载数据集
#数据集的格式pytorch不一定支持,所以要用transform把数据集的格式转换成pytorch支持的形式
transform = torchvision.transforms.Compose([torchvision.transforms.ToTensor()])
#下载训练集 root指定下载到哪个路径,这里是当前文件夹下的data文件夹;指定下载train训练集
data_train = torchvision.datasets.MNIST(root='./data/',transform=transform,train=True,download=True)
#下载测试集,指定下载测试集
data_test = torchvision.datasets.MNIST(root='./data/',transform=transform,train=False,download=True)
由于是境外源,所以在pytorch中下载很慢,参考如下博客快速下载数据集:
博客1:解决Pytorch数据集下载时如MNIST数据下载缓慢
博客2:解决Pytorch数据集下载时如MNIST数据下载缓慢
注意,Mnist数据集并不是仅下载压缩包并解压到data文件夹就可以用了,一定要运行代码!
#打印观察数据集
print(data_train)
输出结果如下:可以看到有60000个数据点,根目录位置以及train划分
#进一步看数据类型和数据形状
print(type(data_train.data))
print(data_train.data.shape)
简单介绍一下DataLoader,它是PyTorch中数据读取的一个重要接口,该接口定义在dataloader.py中,只要是用PyTorch来训练模型基本都会用到该接口,该接口的目的:将自定义的Dataset根据batch size大小、是否shuffle等封装成一个Batch Size大小的Tensor,用于后面的训练。
#由于设备有限,不一次性训练全部数据,随机采样500张(也可以不设置,有条件就传入60000张)
sampler = torch.utils.data.SubsetrandomSampler(indices=list(range(500)))
#定义训练集和测试集的数据传入接口,指定传入的数据集,每个batch有多少个样本
data_loder_train = torch.utils.data.DataLoader(dataset = data_train,batch_size = 64)
data_loder_test = torch.utils.data.DataLoader(dataset = data_test,batch_size = 64)
#定义model类,继承自Module父类
class Model(torch.nn.Module):
#初始化
def __init__(self):
#继承父类
super(Model,self).__init__()
#在model类中定义一系列卷积、激活、函数等操作
#这里我定义两层
#操作1:包含卷积,激活、池化
self.block_1 = torch.nn.Sequential(torch.nn.Conv2d(1,64,kernel_size=3,stride=1,padding=1),## 2d卷积,输入通道为1,输出通道为64,输出大小为64*28*28
#卷积层后面一定要跟激活函数:这里采用Relu
torch.nn.ReLU(),
#再设置一层卷积:
torch.nn.Conv2d(64,128,kernel_size=3,stride=1,padding=1),##输入接上一层卷积的输出64,输出设为128,经此层后 输出大小为128*28*28
torch.nn.ReLU(),
#接池化操作:这里采用最大池化
torch.nn.MaxPool2d(stride=2,kernel_size=2))## 输出大小为128*14*14
#操作2,全连接层:
self.block_2 = torch.nn.Sequential(torch.nn.Linear(128*14*14,1024),#承接上层输出的大小作为输入尺寸,规定输出1024的向量
torch.nn.ReLU(),
torch.nn.Dropout(p=0.6),
torch.nn.Linear(1024,10))
#定义前向传播过程:
def forward(self,x):
x = self.block_1(x)
#经过操作1后,得到一个矩阵,需要flatten(拉平)其为一个一维向量,用view
x = x.view(-1,128*14*14)
x = self.block_2(x)
return x
根据自己的需要可以构造多层网络,也可以定义不同维度的输出维度和尺寸大小,但是一定要注意上一层的输出与下一层的输入尺寸要一致!
定义损失函数:
loss_function = torch.nn.CrossEntropyLoss()
GPU设置
#device = torch.device('cuda:0')
注意,训练过程中不仅模型要放到CPU上,数据集也要放到GPU上
model = Model()
#如果使用GPU,那么模型也要放到GPU上 model=Model().to(device)
optimizer =torch.optim.SGD(model.parameters(),lr=1e-3)
n_epochs = 50
train_acc = []
test_acc = []
for epoch in range(n_epochs):
running_loss = 0
running_correct = 0
#训练集
for data in data_loader_train:
X_train, y_train = data
# X_train = X_train.to(device)#需要GPU的话数据也要放到GPU上
#y_train = y_train.to(device)
outputs = easy_model(X_train)
_, pred = torch.max(outputs.data, 1)
optimizer.zero_grad()
loss = cost(outputs, y_train)
loss.backward()
optimizer.step()
running_loss += loss.data.item()
running_correct += torch.sum(pred == y_train.data)
train_acc.append(running_correct.data.item() / 500)
#可视化
display.clear_output(wait=True)
plt.plot(range(len(train_acc)), train_acc, c='r', label='Train Acc')
plt.plot(range(len(test_acc)), test_acc, c='b', label='Test Acc')
plt.title(f'Epoch{epoch + 1}/{n_epochs}')
plt.legend()
plt.pause(0.2)
#测试集
testing_correct = 0
for data in data_loader_test:
X_test, y_test = data
X_test = X_test.to(device)
y_test = y_test.to(device)
outputs = easy_model(X_test)
_, pred = torch.max(outputs.data, 1)
testing_correct += torch.sum(pred == y_test.data)
test_acc.append(testing_correct.data.item() / 500)
1.修改batch,sampler:500——>1000
2.修改optimizer的lr和momentum:
1e-1——>1e-3,
0.9——>0.8
3.修改epoch:
50——>80
6.修改dropout概率
0.7——>0.9
如果不采取DROPOUT……结果就显然意见的过拟合,可见dropout的作用确实可以避免过拟合
增加训练样本和epoch: