本文主要分为4个模块,分别是
1 readCifar10
首先进行数据集的读取工作,建立对应标签并将对应图像放入标签文件夹内。
2 loadCifar10
接下来对数据集进行加载,构建DataLoader类(会使用到我们的MyDataset类)MyDataset类会初始化加载器以及转换方式
3 VggNet
自定义VggNet 串联的一个神经网络 进行初始化以及向前传播函数
4 train
函数 向前传播 计算损失 参数置零 反向传播 参数更新
read
import pickle
import glob
import cv2
import numpy as np
import os
# 读取数据集合步骤 利用 glob 的glob 函数得到 存储图片的几个文件夹 train_list
# 对文件夹进行反序列化 pickle.load() 函数 得到文件本身的数据
# print 这个文件(字典类型)里面的key值()
# dict_keys([b'batch_label', b'labels', b'data', b'filenames'])
# 然后 对某一个维度进行遍历 利用enumerate函数 得到 每一个数据项的idx
# 进而得到label以及data
# 并将data的 形状(reshape) 以及 通道排序(transpose)转化为合适后续操作的形式
# save_path = "D:\\PostgraduateStudy\\learn_pytorch\\Dataset\\cifar-10-batches-py\\train"
save_path = "D:\\PostgraduateStudy\\learn_pytorch\\Dataset\\cifar-10-batches-py\\test"
def unpickle(file):
with open(file, "rb") as fo:
dict = pickle.load(fo, encoding="bytes")
return dict
label_name =["airplane",
"automobile",
"bird",
"cat",
"deer",
"dog",
"frog",
"horse",
"ship",
"truck"]
train_list = glob.glob("D:\\PostgraduateStudy\\learn_pytorch"
"\\Dataset\\cifar-10-batches-py\\test_batch")
print(train_list)
for l in train_list:
l_dict = unpickle(l)
print(l_dict.keys())
#dict_keys([b'batch_label', b'labels', b'data', b'filenames'])
for im_idx, im_data in enumerate(l_dict[b"data"]):
# 遍历字典中 data 这个维度
# enumerate()函数表示将列表、字符串等可遍历的数据对象组成一个索引序列
# print(im_idx)
# print(im_data)
im_name = l_dict[b"filenames"][im_idx]
im_label = l_dict[b"labels"][im_idx]
print(im_name, im_label, im_data)
im_lable_name = label_name[im_label]#Cifar10 里面的label 是0 1 2 3 4 故转化为 dog ship
im_data = np.reshape(im_data, [3, 32, 32])
im_data = np.transpose(im_data, [1, 2, 0])# 对空间矩阵的进行转置
# cv2.imshow("data",im_data)
# cv2.waitKey(0)
if not os.path.exists("{}/{}".format(save_path, im_lable_name)):
os.mkdir("{}/{}".format(save_path, im_lable_name))
cv2.imwrite("{}/{}/{}".format(save_path, im_lable_name,
im_name.decode("utf-8")) ,im_data)
load
from torchvision import transforms
from torch.utils.data import DataLoader, Dataset
import os
import glob
from PIL import Image
import numpy as np
# read 读取到数据之后 对自定义数据进行加载
#列表类型
label_name =["airplane",
"automobile",
"bird",
"cat",
"deer",
"dog",
"frog",
"horse",
"ship",
"truck"]
#字典类型
label_dict = {}
for idx, name in enumerate(label_name):
#一般用于for循环。
# enumerate()在遍历中可以获得索引和元素值。
label_dict[name] = idx
def default_loader(path):
return Image.open(path).convert("RGB")
train_transform = transforms.Compose([
# 组合进行图片转换的几个方法
transforms.RandomResizedCrop(28),
transforms.RandomHorizontalFlip(),
transforms.ToTensor()
])
test_transform = transforms.Compose([
# 组合进行图片转换的几个方法
transforms.RandomResizedCrop((28, 28)),
transforms.ToTensor()
])
class MyDataset(Dataset):
def __init__(self, im_list,
transforms=None,
loader = default_loader):
#传入要进行加载的文件, 对图片进行增强的函数, 加载器
super(MyDataset, self).__init__()
imgs = []
for item in im_list:
#"D:\PostgraduateStudy\learn_pytorch\Dataset\cifar-10-batches-py\train
# \airplane\aeroplane_s_000004.png"
im_label_name = item.split("\\")[-2]
#print(im_label_name)
imgs.append([item, label_dict[im_label_name]])
# 给列表中 增加 元素的时候元素有两个信息的时候 利用列表套列表的方式
self.imgs = imgs
self.transforms = transforms
self.loader = loader
def __getitem__(self, index):
#定义对数据的读取和对数据的增强 返回图片的数据以及label
im_path, im_label = self.imgs[index]
im_data = self.loader(im_path)
if self.transforms is not None:
im_data = self.transforms(im_data)
return im_data, im_label
def __len__(self):
return len(self.imgs)
im_train_list = glob.glob("D:\\PostgraduateStudy\\learn_pytorch\\Dataset\\cifar-10-batches-py\\train\\*\\*.png")
im_test_list = glob.glob("D:\\PostgraduateStudy\\learn_pytorch\\Dataset\\cifar-10-batches-py\\test\\*\\*.png")
train_dataset = MyDataset(im_train_list, transforms = train_transform)
test_dataset = MyDataset(im_train_list, transforms = test_transform)
#测试集不需要增强
train_data_loader = DataLoader(dataset=train_dataset,
batch_size=128,
shuffle=True,
num_workers=2)
#batchsize 每一批次输入的个数
test_data_loader = DataLoader(dataset=test_dataset,
batch_size=128,
shuffle=False,
num_workers=2)
print("num of train", len(train_dataset))
print("num of test", len(test_dataset))
VggNet
import torch
import torch.nn as nn
import torch.nn.functional as F
#对数据进行加载之后 进行神经网络的学习
class VGGbase(nn.Module):
def __init__(self):
super(VGGbase, self).__init__()
#调用父类的初始化方法
# 3 * 28 * 28 (crop --》 32, 28)
self.conv1 = nn.Sequential(
nn.Conv2d(3, 64, kernel_size=3, stride=1,padding=1),
# 在这里 卷积核是3*3 卷积完成后输出的特征矩阵加一个
# padding =1 的边框(相当于 给特征矩阵宽和高都加2)
nn.BatchNorm2d(64),
nn.ReLU() # 非线性激活函数
)
self.max_pooling1 = nn.MaxPool2d(kernel_size=2, stride=2)
# 14 * 14
self.conv2_1 = nn.Sequential(
nn.Conv2d(64, 128, kernel_size=3, stride=1, padding=1),
# 在这里 卷积核是3*3 卷积完成后输出的特征矩阵加一个
# padding =1 的边框(相当于 给特征矩阵宽和高都加2)
nn.BatchNorm2d(128),
nn.ReLU()
)
self.conv2_2 = nn.Sequential(
nn.Conv2d(128, 128, kernel_size=3, stride=1, padding=1),
# 在这里 卷积核是3*3 卷积完成后输出的特征矩阵加一个
# padding =1 的边框(相当于 给特征矩阵宽和高都加2)
nn.BatchNorm2d(128),
nn.ReLU()
)
self.max_pooling2 = nn.MaxPool2d(kernel_size=2, stride=2)
# 7 *7
self.conv3_1 = nn.Sequential(
nn.Conv2d(128, 256, kernel_size=3, stride=1, padding=1),
# 在这里 卷积核是3*3 卷积完成后输出的特征矩阵加一个
# padding =1 的边框(相当于 给特征矩阵宽和高都加2)
nn.BatchNorm2d(256),
nn.ReLU()
)
self.conv3_2 = nn.Sequential(
nn.Conv2d(256, 256, kernel_size=3, stride=1, padding=1),
# 在这里 卷积核是3*3 卷积完成后输出的特征矩阵加一个
# padding =1 的边框(相当于 给特征矩阵宽和高都加2)
nn.BatchNorm2d(256),
nn.ReLU()
)
self.max_pooling3 = nn.MaxPool2d(kernel_size=2, stride=2, padding=1)
# 4*4
self.conv4_1 = nn.Sequential(
nn.Conv2d(256, 512, kernel_size=3, stride=1, padding=1),
# 在这里 卷积核是3*3 卷积完成后输出的特征矩阵加一个
# padding =1 的边框(相当于 给特征矩阵宽和高都加2)
nn.BatchNorm2d(512),
nn.ReLU()
)
self.conv4_2 = nn.Sequential(
nn.Conv2d(512, 512, kernel_size=3, stride=1, padding=1),
# 在这里 卷积核是3*3 卷积完成后输出的特征矩阵加一个
# padding =1 的边框(相当于 给特征矩阵宽和高都加2)
nn.BatchNorm2d(512),
nn.ReLU()
)
self.max_pooling4 = nn.MaxPool2d(kernel_size=2, stride=2)
# 2*2
# 想要对其进行全连接故变成2维的tensor
# batchsize * 512 * 2 * 2 -----》 batchsize * (512 * 4)
self.fc = nn.Linear(512*2*2, 10)
def forward(self, x):
#print("你执行到了我")
batchsize = x.size(0)
out = self.conv1(x)
out = self.max_pooling1(out)
out = self.conv2_1(out)
out = self.conv2_2(out)
out = self.max_pooling2(out)
out = self.conv3_1(out)
out = self.conv3_2(out)
out = self.max_pooling3(out)
out = self.conv4_1(out)
out = self.conv4_2(out)
out = self.max_pooling4(out)
out = out.view(batchsize, -1)
# batchsize * c * h * w --> batchsize * c * n
out = self.fc(out)# batchsize * 10
out = F.log_softmax(out, dim=1)
# 对每一行的所有元素进行softMax计算,使得每一行元素和为1
# 对得到的结果进行log运算
return out
def VGGNet():
return VGGbase()
train
import os
import torch
import torch.nn as nn
import torchvision
import tensorboardX
from cpVggNet import VGGbase
from cpload_Cifar10 import test_data_loader, train_data_loader
if __name__ == '__main__':
# 向前传播 计算损失 参数归0 反向传播 参数更新
# 定义环境
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
epoch_num = 100 # 对样本一共遍历200次
lr = 0.01
net = VGGbase().to(device)
batch_size = 128
# loss 交叉熵
loss_func = nn.CrossEntropyLoss()
step_n = 0
# optimizer
optimizer = torch.optim.Adam(net.parameters(), lr=lr)
# optimizer = torch.optim.SGD(net.parameters(), lr = lr,
# momentum=0.9, weight_decay=5e-4)
scheduler = torch.optim.lr_scheduler.StepLR(optimizer,
step_size=1,
gamma=0.9)
if not os.path.exists("log"):
os.mkdir("log")
writer = tensorboardX.SummaryWriter("log")
# 学习率呈现递减的趋势
for epoch in range(epoch_num):
#将数据集遍历epoch_num次
net.train()
for i, data in enumerate(train_data_loader):
#dataloader 里面的batchiszie 为128 num of train 50000
#因此需要5000/128次才能够完成一次 train_data的遍历
inputs, labels = data
inputs, labels = inputs.to(device), labels.to(device)
#print(inputs)
outputs = net(inputs)
# 这个语句的后面是 net = VGGbase().to(device)
# def VGGNet():
# return VGGbase()
# 会执行到构造函数 以及 forward 函数
# forward 函数里面会返回 out
#print(type(outputs))
#print(outputs)
loss = loss_func(outputs, labels)
optimizer.zero_grad()
loss.backward()
optimizer.step()
#打印每一个minibatch 的loss
_,pred = torch.max(outputs.data, dim=1)
#torch.max
#返回输入张量给定维度上每行的最大值,并同时返回每个最大值的位置索引
#print("_:",_,"pred:",pred)
# 概率最大的位置 就是最后预测的值 dim = 1 是行中最大的元素
correct = pred.eq(labels.data).cpu().sum()
#print("train epoch", epoch, "step:", i, "loss is:", loss.item(),
# "mini-batch correct is:", 100.0 * correct / batch_size)
# #print(net.state_dict) 可以查看网络之中的各个卷积层以及池化层的参数信息
#print("train lr is", optimizer.state_dict()["param_groups"][0]["lr"])
writer.add_scalar("train loss",loss.item(), global_step=step_n)
writer.add_scalar("train correct",
100.0 * correct / batch_size, global_step=step_n)
im = torchvision.utils.make_grid(inputs)
writer.add_image("train_im", im, global_step=step_n)
step_n +=1
if not os.path.exists("models"):
os.mkdir("models")
torch.save(net.state_dict(),"models/{}.pth".format(epoch+1))
# state_dict作为python的字典对象将每一层的参数映射成tensor张量
scheduler.step()#对学习率进行更新
#对数据进行测试
sum_loss = 0
sum_correct = 0
for i, data in enumerate(test_data_loader):
net.eval()
#标志着当前是在做测试
inputs, labels = data
inputs, labels = inputs.to(device), labels.to(device)
# print(inputs)
outputs = net(inputs)
loss = loss_func(outputs, labels)
optimizer.zero_grad()
_, pred = torch.max(outputs.data, dim=1)
correct = pred.eq(labels.data).cpu().sum()
sum_correct += correct.item()
sum_loss += loss.item()
writer.add_scalar("test loss", loss.item(), global_step=step_n)
writer.add_scalar("test correct",
100.0 * correct / batch_size, global_step=step_n)
im = torchvision.utils.make_grid(inputs)
writer.add_image("test_im", im, global_step=step_n)
step_n +=1
test_loss = sum_loss / len(test_data_loader)
test_correct = sum_correct * 100.0 / len(test_data_loader) / batch_size
print("test epoch", epoch, "loss is:", test_loss,
"mini-batch correct is:", test_correct)
writer.close()