def dp():
path = './mnist_lite.mat' # 定义路径
matr = io.loadmat(path) # 关键步骤 采用io的loadmat包将数据读入缓存
train_x = matr['train_x'] # 四个标签类型的数据为.mat中存储的形式
train_y = matr['train_y']
test_x = matr['test_x']
test_y = matr['test_y']
'''
原本数据shape为(784,6000), transpose 对于二维函数是将其转置,即6000个数据,784个信息点
'''
train_x = torch.tensor(np.transpose(train_x))
train_x = train_x.to(torch.float32)
train_y = torch.tensor(np.transpose(train_y))
train_y = train_y.to(torch.float32)
test_x = torch.tensor(np.transpose(test_x))
test_x = test_x.to(torch.float32)
test_y = torch.tensor(np.transpose(test_y))
test_y = test_y.to(torch.float32)
print(' train_x.shape:\t',train_x.shape,'\n',
'train_y.shape:\t',train_y.shape,'\n',
'test_x.shape:\t',test_x.shape,'\n',
'test_y.shape:\t',test_y.shape)
train_x = train_x.reshape(6000,1,28,28) # 将图像reshape为28*28的图像
test_x = test_x.reshape(1000,1,28,28)
return train_x, train_y, test_x, test_y
class Net(nn.Module):
def __init__(self):
super(Net, self).__init__()
self.conv1 = nn.Conv2d(in_channels=1, out_channels=8, kernel_size=3, padding=1)
self.conv2 = nn.Conv2d(in_channels=8, out_channels=16, kernel_size=3, padding=1)
self.conv3 = nn.Conv2d(in_channels=16, out_channels=32, kernel_size=3, padding=1)
self.m = nn.MaxPool2d(2, stride=2)
self.fc = nn.Linear(28 * 28 * 2, 10)
# self.initialize_weights()
def forward(self, x):
x = F.relu(self.conv1(x))
# print("1",x.shape)
x = self.m(x)
# print("2",x.shape)
x = F.relu(self.conv2(x))
# print("3",x.shape)
x = self.m(x)
# print("4",x.shape)
x = F.relu(self.conv3(x))
# print("5",x.shape)
x = torch.flatten(x, 1) # flatten all dimensions except the batch dimension
# print("6",x.shape)
x = self.fc(x)
# print("7",x.shape)
# x = F.softmax(x, dim = 0)
return x
net = Net()
对于神经网络的定义一些心得:
关于nn.conv2d()中的参数:
第一层的输入图片样本in_channel的输入取决于图片的类型,RGB即为3,本项目的MNIST手写数据集为1通道数据所以为1,out_channel的大小取决于卷积核的数量,此时out_channel的大小为下一次卷积核的in_channel的大小
对于pytorch,没有 padding = same 的设置,因此需要根据卷积核的大小,如果需要设置padding = same的情况下,如kernelsize设置为3时,padding设置为1,等价于 padding = same 的设置
关于nn.MaxPool2d()中的参数:
kernel_size的设置是移动窗口的大小,这里stride的大小默认值是与窗口大小相同的
每过一个最大池化层,信息点减少一半
关于torch.flatten()函数:
在全连接层前采用flatten将所有信息点进行展开
特别注意:
在后面采用的Loss标准如果采用了交叉熵判定标准的话,是已经有了softmax函数了,因此在网络最后的Sequential中不需要额外添加softmax函数
import torch
import torch.nn as nn
import torch.nn.functional as F
import torch.utils.data as data
import scipy.io as io
import numpy as np
import torch.optim as optim
import matplotlib.pyplot as plt
import data_preprocess
import Network
net = Network.Net() # 网络定义在Network.py文件中
Epoch = 10
BATCH_SIZE = 128 # 批训练的数据个数
# 先转换成 torch 能识别的 Datasettorch_dataset = data.TensorDataset(train_x, train_y)
# 把 dataset 放入 DataLoader
train_x, train_y, test_x, test_y = data_preprocess.dp()
print(train_x.shape,train_y.shape)
torch_dataset = data.TensorDataset(train_x, train_y) # 生成TensorDataset
loader = data.DataLoader(
dataset = torch_dataset, # torch TensorDataset format
batch_size = BATCH_SIZE, # mini batch size
shuffle = True # 要不要打乱数据 (打乱比较好)
)
# loss 画图预备
loss_record = []
bc_record = []
step_ = 0
loss_record_mean = []
epoch_record = []
step__ = 0
# Accuracy 画图预备
accuracy_record = []
_bc_record = []
_step_ = 0
accuracy_record_mean = []
_epoch_record = []
_step__ = 0
optimizer = optim.SGD(net.parameters(), lr=0.05, momentum=0.9)
criterion = nn.CrossEntropyLoss()
optimizer.zero_grad()
for epoch in range(Epoch):
print('............','Epoch:',epoch,'............')
loss_sum = 0
acc_sum = 0
num = 0
for step, (batch_x, batch_y) in enumerate(loader): #每一步loader释放一小批数据用来学习
train_predict_y = net(batch_x)
loss = criterion(train_predict_y.float(), batch_y.float())
optimizer.zero_grad()
loss.backward()
optimizer.step()
# print('parameter updating')
output = torch.argmax(train_predict_y, dim=1, keepdim=False)
# print(output)
label = torch.argmax(batch_y, dim=1, keepdim=False)
'''
这里判断Accuracy的思路为比较预测输出的最大值的索引即第几类与groud_truth是否相等
因为数组形式相同,因此取最大索引值的方式相同
'''
# print(label)
# print('..........output...............',output)
# print('..........label...............',label)
acc = output[output == label].shape[0] / output.shape[0] # 计算正确率
# print(output[output==label])
acc_sum = acc_sum + acc
loss_sum = loss_sum + loss
num = num + 1
# loss for batch
loss_record.append(loss.detach()) # y 坐标
bc_record.append(step_) # 每次加了大约 47 次
step_ = step_ + 1
# acc for batch
accuracy_record.append(acc)
_bc_record.append(_step_)
_step_ = _step_ + 1
print('Loss:', (loss_sum / num).detach())
print("Accuracy:", acc_sum / num)
loss_record_mean.append((loss_sum / num).detach()) # 每一次epoch训练结束之后的 Loss 取的是一个均值 如果取点的话就是一共eoch个点
epoch_record.append(step__)
step__ = step__ + 47
accuracy_record_mean.append((acc_sum / num)) # 每一次epoch训练结束之后的 Accuracy 取的是一个均值
_epoch_record.append(_step__)
_step__ = step__ + 47 # 之前一个batch循环训练完后除以内层batch训练的次数
# 测试集的正确率进行检验
# 画图
fig_list = torch.Tensor(loss_record).numpy()
fig_mean = torch.Tensor(loss_record_mean).numpy()
对于训练网络部分的一些心得:
首先需要将输入数据X和预测输出数据y_hat组合为TensorDataset,才能将数据存储入pytorch数据迭代器的Dataloader中
使用argmax提取出train_y 和 labels的最大值所在的索引进行判别,以此判定出accuracy的大小
将画图的张量采用.detach()函数,返回的张量的requires_grad为false
关于画图:
在每一个批结束后进行点的绘制,Loss和Accuracy在每一小次迭代后都进行值的存储
最终运行情况: