这篇博客将以我自己的理解结合例子进行说明,示例中均有详细注释,建议自己尝试一下。
目录
0、环境说明
1、加载数据
2、tensorboard可视化工具
3、transform对图像操作
4、DataSet加载数据集
5、dataloader加载数据
6、卷积操作
7、卷积层
8、池化层
9、非线性激活层
10、线性层(全连接层)
11、简单的神经网络
12、损失函数
13、反向传播
14、优化器
15、导入现有网络
16、模型的储存
17、模型的读取
18、标签选择
19、经典样例(有基础的推荐直接看这里)
20、利用GPU
21、测试
22、numpy与tensor
0、环境说明
torch1.1+torchvision0.11.1+cudatoolkit10.0.130,其他的提示缺啥就下载啥
1、加载数据
首先是加载数据,这里是使用了一个类Dataset(数据集),在使用中会和下面的代码有一些出入,要视情况而定。
比如下面这个代码读取的数据集,其类名直接作为文件夹的名字,然后文件夹的内部就是一系列图片,所以root_dir指向数据集,label_dir说明类别,由root_dir+label_dir链接地址就能找到此类的图片,这就是MyData类所做的事情。
注意:输出完整的tensor:
torch.set_printoptions(profile="full")
main函数做的是将两个类图片分别读取然后合并得到总的数据集。
#加载数据#
import torch
from torch.utils.data import Dataset
from PIL import Image
import os
class MyData(Dataset):
#初始化#
def __init__(self,root_dir,label_dir):
#加self表示类变量#
self.root_dir=root_dir#基础路径(根目录)#
self.label_dir=label_dir#指向基础路径下的目标文件地址#label_dir是图片文件夹名称即图片标签
self.path=os.path.join(root_dir,label_dir)#将两个文件路径进行连接#
self.img_path=os.listdir(self.path)#读取path指向的文件目录下每一个图片名称#
#返回第idx张图片及其标签#
def __getitem__(self, idx):
img_name=self.img_path[idx]#获得第idx张图片名称#
img_item_path=os.path.join(self.path,img_name)#此路径指向该图片#
img=Image.open(img_item_path)#加载图片#
#↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓使用时注意该句子↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓#
#label_dir是图片文件夹名称即图片标签
label=self.label_dir
return label,img#label是img的类别#
#返回图片列表长度即训练集/测试集大小#
def __len__(self):
return len(self.img_path)#图片数量#
'''
#单张图片的读取
img_path = "pct/2959730355_416a18c63c.jpg" # 图片路径并非图片文件夹“#
img = Image.open(img_path)
img.show()#显示图片#
'''
#利用Dataset读取
root_dir=""#基础路径(根目录)#
ants_dir="ant"
ants_dataset=MyData(root_dir,ants_dir)
label_0,img_0=ants_dataset[0]#第0号图片及其标签
#img_0.show()
print(label_0)
bees_dir="bee"
bees_dataset=MyData(root_dir,bees_dir)
label_0,img_0=bees_dataset[0]#第0号图片及其标签
#img_0.show()
print(label_0)
#将两个数据集进行合并#
all_dataset=ants_dataset+bees_dataset
print(len(all_dataset))
2、tensorboard可视化工具
tensorboard是一个可视化的工具,重点要了解他怎么写和怎么读,这些都写在下面的代码里了,在使用的时候要记得在最后执行close。
#tensorboard#
from torch.utils.tensorboard import SummaryWriter
from PIL import Image
import numpy as np
writer=SummaryWriter("logs")#把数据写入logs文件夹内
image_path="./ant/308196310_1db5ffa01b.jpg"
img=Image.open(image_path)#此时img类型为JpegImageFile
img_array=np.array(img)#将img转化为numpy型
writer.add_image("test",img_array,1,dataformats="HWC")
'''
add_image(self, tag, img_tensor, global_step=None, walltime=None, dataformats='CHW'):
tag:标题
img_tensor:图像(类型:torch.Tensor, numpy.array, or string/blobname)
global_step:步数
dataformats:图像类型HWC表示高度、宽度、通道(可以利用img_array.shape进行查看)
生成文件后放在前面在类中定义的logs文件夹中
阅读生成的文件:
控制台输入
tensorboard --logdir="logs" --port=6007
#logdir指定目录
#port指定接入端口
#点击TensorBoard 2.4.0 at http://localhost:6006/ (Press CTRL+C to quit)中的“http://localhost:6006/”
如果是在服务器下Tensorboard然后在本地查看:
可见https://zhuanlan.zhihu.com/p/231790992亲测有用
'''
for i in range(100):
writer.add_scalar("y=x",i,i)
'''
add_scalar(self, tag, scalar_value, global_step=None, walltime=None):
tag:图表标题
scalar_value:数值,与global_step对应,y轴
global_step:步数,训练多少步时对应数值是多少,x轴
生成文件后放在前面在类中定义的logs文件夹中
阅读生成的文件:
终端输入
tensorboard --logdir="logs" --port=6006
#logdir指定目录
#port指定接入端口
#点击TensorBoard 2.4.0 at http://localhost:6006/ (Press CTRL+C to quit)中的“http://localhost:6006/”
'''
writer.close()
3、transform对图像操作
transform有一系列对图像的操作,比如类型转化、大小变化、裁剪等。
#transform对图像进行操作
from PIL import Image
from torch.utils.tensorboard import SummaryWriter
from torchvision import transforms
#↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓使用时注意该句子↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓#
img_path="pct/2959730355_416a18c63c.jpg"
img=Image.open(img_path)#打开图片为PIL数据类型
print(img)
"""
利用transform.ToTensor将PIL或numpy数据类型的img转化为Tensor数据类型的图片
tensor:包装了神经网络所需要的参数如梯度、反向传播等
"""
tensor_trans=transforms.ToTensor()
tensor_img=tensor_trans(img)#执行__call__将img转化为tensor类型
print(tensor_img)
#"""test2中所用到的SummaryWriter类"""
#writer=SummaryWriter("log")
#writer.add_image("Tensor_img",tensor_img)
#writer.close()
"""
Totensor的使用
Normalize归一化
resize调整大小
RandomCrop随机裁剪
"""
"""
Normalize(均值,方差)
output[channel] = (input[channel] - mean[channel]) / std[channel]
"""
trans_norm=transforms.Normalize([0.5,0.5,0.5],[0.5,0.5,0.5])#(output-0.5)/0.5
img_norm=trans_norm(tensor_img)
"""
resize调整大小
参数为大小(序列或整数):所需的输出大小。
如果大小是这样的序列(h,w),输出大小将与此匹配。如果大小是整数,图像的较小边缘将与此数字匹配。
"""
trans_resize=transforms.Resize((512,512))#调整到512X512的PIL类型img
img_resize=trans_resize(img)#这里输入为PIL,输出为PIL
print(img_resize)
trans_resize_2=transforms.Resize(1000)
#串联两个操作即先执行trans_resize_2再执行tensor_trans
trans_compose=transforms.Compose([trans_resize_2,tensor_trans])
img_resize_2=trans_compose(img)
"""
RandomCrop输入一个PIL类型的img
"""
trans_random=transforms.RandomCrop(20)#裁剪为512X512的,若指定(n,m)则裁剪大小为nXm
trans_compose_2=transforms.Compose([trans_random,tensor_trans])
for i in range(10):
img_crop=trans_compose_2(img)
4、DataSet加载数据集
注意这一节与第一节是不同的,这里包含有MNIST、FakeData、COCO、LSUN、ImageFolder、DatasetFolder、ImageNet、CIFAR等一些常用的数据集,可以直接下载使用。
"""
使用官方提供的数据集
CIFAR10:6万32X32的图片,5万训练,1万测试
"""
import torchvision
from torch.utils.tensorboard import SummaryWriter
dataset_transform=torchvision.transformsz.Compose([
torchvision.transforms.ToTensor()
])
#下载CIFAR10到root路径下,train=True表示下载的是训练集,False表示下载的是测试集,download表示要下载
#transform=dataset_transform转化成Tensor数据类型
#↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓使用时注意修改该句子↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓#
train_set=torchvision.datasets.CIFAR10(root="./DataSet",train=True,transform=dataset_transform,download=True)
test_set=torchvision.datasets.CIFAR10(root="./DataSet",train=False,transform=dataset_transform,download=True)
# print(test_set.classes)
#
# print(test_set[0])#输出图片+标签信息
# img,target=test_set[0]#img存放图片信息,target存放标签信息
# print(img)
# print(target)
# print(test_set.classes[target])#输出标签类别
# img.show()#显示图片
writer=SummaryWriter("logs")
for i in range(10):
img,target=test_set[i]
writer.add_image("test_set",img,i)
writer.close()
5、dataloader加载数据
这里是读取第四步中下载的数据,dataloader本质是一个可迭代对象,将自定义的Dataset根据batch size大小、是否shuffle等封装成一个Batch Size大小的Tensor,用于后面的训练。
#DataLoader加载数据
import torchvision
from torch.utils.data import DataLoader
from torch.utils.tensorboard import SummaryWriter
dataset_transform=torchvision.transforms.Compose([
torchvision.transforms.ToTensor()
])
test_data=torchvision.datasets.CIFAR10(root="./DataSet",train=False,transform=dataset_transform,download=True)
"""
dataset:数据集
batch_size:一次取多少数据
shuffle:是否按顺序读取,默认为false
num_workers:多进程,默认为0,用主进程加载,>0时windows下可能出错
drop_last:对余数的处理(除不尽)
"""
test_loader=DataLoader(dataset=test_data,batch_size=4,shuffle=True,num_workers=0,drop_last=False)
writer=SummaryWriter("dataloader")
for epoch in range(2):
step=0
for data in test_loader:
img,targets=data
writer.add_images("epoch_{}".format(epoch),img,step)
step=step+1
writer.close()
6、卷积操作
这里是利用函数的卷积。
#conv2d卷积操作
import torch
from torch import nn
import torch.nn.functional as F
input=torch.tensor([[1,2,0,3,1],
[0,1,2,3,1],
[1,2,1,0,0],
[5,2,3,1,1],
[2,1,0,1,1]])
kernel=torch.tensor([[1,2,1],
[0,1,0],
[2,1,0]])
input=torch.reshape(input,(1,1,5,5))#尺寸变化,Conv2d有要求
kernel=torch.reshape(kernel,(1,1,3,3))
"""
二维卷积
conv2d(input,weight,bias=None,stride=1,padding=0,dilation=1,groups=1):
input:输入要求是(batch,通道,高,宽)
weight:权重(卷积核),输入要求是(输出通道,输入通道,高,宽)
bias:偏置
stride:步长,单个数(横纵皆是),俩数(横步长,纵步长)
padding:填充边框,单个数四周按此值填充,元组(横填充,纵填充)
"""
output=F.conv2d(input,kernel,stride=1)
print(output)
output2=F.conv2d(input,kernel,stride=2)
print(output2)
output3=F.conv2d(input,kernel,stride=1,padding=1)
print(output3)
7、卷积层
与第6节不同,这里引入了model的概念,自己定义卷积核的大小、步长等通过正向传播的得到结果。卷积层也是未来常用的一个层
#nn.Conv2d卷积层
import torch
import torchvision
from torch.nn import Conv2d
from torch.utils.data import DataLoader
from torch import nn
from torch.utils.tensorboard import SummaryWriter
#加载数据集
dataset_transform=torchvision.transforms.Compose([
torchvision.transforms.ToTensor()
])
dataset=torchvision.datasets.CIFAR10(root="./DataSet",train=False,transform=dataset_transform,download=True)
dataloader=DataLoader(dataset,batch_size=64)
"""
nn.Conv2d(in_channels,out_channels,kernel_size,stride=1,
padding=0,dilation=1,groups=1,bias=True,padding_mode='zeros'):
in_channels:输入通道数,RGB图像一般为3通道
out_channels:输出通道数,通道数1->2,两个卷积核
kernel_size:卷积核大小,数或者元组(行,列),卷积核中参数训练得到
stride:步长,单个数(横纵皆是),俩数(横步长,纵步长)
padding:填充边框,单个数四周按此值填充,元组(横填充,纵填充)
下面的不太常用一般设为默认值
dilation:卷积核对应位的距离
groups:分组卷积
bias:结果是否加减一个常数
padding_mode:按怎样模式填充
"""
class MyModule(nn.Module):
def __init__(self):
super(MyModule, self).__init__()
self.conv1=Conv2d(in_channels=3,out_channels=6,kernel_size=3,stride=1,padding=0)
def forward(self,x):
x=self.conv1(x)
return x
myModule=MyModule()
writer=SummaryWriter("nn.conv")
step=0
for data in dataloader:
imgs,targets=data
output=myModule(imgs)
writer.add_images("input",imgs,step)
#变化成六通道后不能显示(报错),需要reshape转化为三通道
output=torch.reshape(output,(-1,3,30,30))#设-1会自动计算结果
writer.add_images("output",output,step)
step=step+1
writer.close()
8、池化层
池化操作,同7,也是module中的一个层次。
#pooling池化层
import torch
from torch import nn
from torch.nn import MaxPool2d
input=torch.tensor([[1,2,0,3,1],
[0,1,2,3,1],
[1,2,1,0,0],
[5,2,3,1,1],
[2,1,0,1,1]],dtype=torch.float32)#将数字格式设置为浮点型
input=torch.reshape(input,(-1,1,5,5))#尺寸变化,MaxPool2d有要求
"""
nn.MaxPool2d(kernel_size, stride,padding, dilation, ceil_mode,return_indices)
kernel_size:窗口大小,数字或元组
stride:横向或纵向路径大小,默认值是窗口大小
padding:填充边框,单个数四周按此值填充,元组(横填充,纵填充)
dilation:窗口对应位的距离
ceil_mode:True则ceil模式(向下取整),False则floor模式(向上取整),设计到对边缘数的处理同理,默认为False
"""
class MyModule(nn.Module):
def __init__(self):
super(MyModule, self).__init__()
self.maxpool1=MaxPool2d(kernel_size=3,ceil_mode=True)
def forward(self, input):
output=self.maxpool1(input)
return output
MyModule=MyModule()
output=MyModule(input)
print(output)
9、非线性激活层
#非线性激活层
import torch
from torch import nn
from torch.nn import ReLU
input=torch.tensor([[1,-0.5],
[-1,3]])
output=torch.reshape(input,(-1,1,2,2))
"""
ReLU(inplace)
负数归0
inplace:是否覆盖input,true覆盖,False不覆盖,默认为False
Sigmoid()
归一化归至(-1,1)
"""
class MyModule(nn.Module):
def __init__(self):
super(MyModule, self).__init__()
self.relu1=ReLU()
def forward(self,input):
output=self.relu1(input)
return output
MyModule=MyModule()
output=MyModule(input)
print(output)
10、线性层(全连接层)
#线性层
import torch
import torchvision
from torch import nn
from torch.nn import Linear
from torch.utils.data import DataLoader
#加载数据集
dataset_transform=torchvision.transforms.Compose([
torchvision.transforms.ToTensor()
])
dataset=torchvision.datasets.CIFAR10(root="./DataSet",train=False,transform=dataset_transform,download=True)
dataloader=DataLoader(dataset,batch_size=64,drop_last=True)
"""
torch.nn.Linear(in_features, out_features, bias=True, device=None, dtype=None):
in_features:输入大小
out_features:输出大小
bias:True则设偏置学习,False不设
"""
class MyModule(nn.Module):
def __init__(self):
super(MyModule, self).__init__()
self.linear1=Linear(196608,10)
def forward(self,input):
output=self.linear1(input)
return output
MyModule=MyModule()
for data in dataloader:
imgs,targets=data
print(imgs.shape)
"""
flatten将输入展成一行
[[1,2],[3,4]]->[1,2,3,4]
"""
input=torch.flatten(imgs)
print(input.shape)
output=MyModule(input)
11、简单的神经网络
这一节结合了7~10节,构成了一个简单的神经网络,当然,只有正向传播,这个代码的最后有一个tensorboard的黑科技,记得试一下。
#构建一个简单的神经网络
import torch
from torch import nn
from torch.nn import Conv2d, MaxPool2d, Flatten, Linear, Sequential
from torch.utils.tensorboard import SummaryWriter
class MyModule(nn.Module):
def __init__(self):
super(MyModule, self).__init__()
self.conv1=Conv2d(3,32,5,padding=2)
self.maxpool1=MaxPool2d(2)
self.conv2=Conv2d(32,32,5,padding=2)
self.maxpool2=MaxPool2d(2)
self.conv3=Conv2d(32,64,5,padding=2)
self.maxpool3=MaxPool2d(2)
self.flatten=Flatten()
self.linear1=Linear(1024,64)
self.linear2=Linear(64,10)
"""
Sequential():
依次执行()中的操作
"""
self.model1=Sequential(#这个与前面分开写的是一样的
Conv2d(3, 32, 5, padding=2),
MaxPool2d(2),
Conv2d(32, 32, 5, padding=2),
MaxPool2d(2),
Conv2d(32, 64, 5, padding=2),
MaxPool2d(2),
Flatten(),
Linear(1024, 64),
Linear(64, 10)
)
def forward(self, x):
x = self.conv1(x)
x = self.maxpool1(x)
x = self.conv2(x)
x = self.maxpool2(x)
x = self.conv3(x)
x = self.maxpool3(x)
x = self.flatten(x)
x = self.linear1(x)
x = self.linear2(x)
return x
MyModule=MyModule()
input=torch.ones((64,3,32,32))#定义大小,其中元素全部复制为1
output=MyModule(input)
writer=SummaryWriter("../compute")
writer.add_graph(MyModule,input)#生成计算图,很神奇,记得点几下
writer.close()
12、损失函数
要进行训练,那么损失函数就必不可少了,这里展示了几种常见的loss函数例如MSELoss、CrossEntropyLoss。
#loss实际与预测值的损失,用于更新(反向传播) 注意输入输出
import torch
from torch.nn import L1Loss
from torch import nn
input=torch.tensor([1,2,3],dtype=torch.float32)
target=torch.tensor([1,2,5],dtype=torch.float32)
input=torch.reshape(input,(1,1,1,3))
target=torch.reshape(target,(1,1,1,3))
"""
torch.nn.L1Loss(size_average=None, reduce=None, reduction='mean'):
reduction:mean:求均值;sum:求和
"""
loss=L1Loss()
result=loss(input,target)
print(result)
"""
nn.MSELoss()
先求平方差,再求均值
"""
loss_mse=nn.MSELoss()
result_mse=loss_mse(input,target)
print(result_mse)
"""
nn.CrossEntropyLoss():
交叉熵损失,常用于分类问题
input:(N,C) N batch size,C类
output:实际标签
"""
x=torch.tensor([0.1,0.2,0.3])
y=torch.tensor([1])
x=torch.reshape(x,(1,3))
loss_CE=nn.CrossEntropyLoss()
print(loss_CE(x,y))
13、反向传播
加上反向传播,可用pycharm的调试功能看到梯度的变化。
#Back propagation反向传播,在test14网络的基础上进行
import torch
import torchvision.datasets
from torch import nn
from torch.nn import Conv2d, MaxPool2d, Flatten, Linear, Sequential
from torch.utils.data import DataLoader
from torch.utils.tensorboard import SummaryWriter
dataset_transform=torchvision.transforms.Compose([
torchvision.transforms.ToTensor()
])
test_data=torchvision.datasets.CIFAR10(root="./DataSet",train=False,transform=dataset_transform,download=True)
dataloader=DataLoader(dataset=test_data,batch_size=4,shuffle=True,num_workers=0,drop_last=False)
class MyModule(nn.Module):
def __init__(self):
super(MyModule, self).__init__()
self.conv1=Conv2d(3,32,5,padding=2)
self.maxpool1=MaxPool2d(2)
self.conv2=Conv2d(32,32,5,padding=2)
self.maxpool2=MaxPool2d(2)
self.conv3=Conv2d(32,64,5,padding=2)
self.maxpool3=MaxPool2d(2)
self.flatten=Flatten()
self.linear1=Linear(1024,64)
self.linear2=Linear(64,10)
"""
Sequential():
依次执行()中的操作
"""
self.model1=Sequential(#这个与前面分开写的是一样的
Conv2d(3, 32, 5, padding=2),
MaxPool2d(2),
Conv2d(32, 32, 5, padding=2),
MaxPool2d(2),
Conv2d(32, 64, 5, padding=2),
MaxPool2d(2),
Flatten(),
Linear(1024, 64),
Linear(64, 10)
)
def forward(self, x):
x = self.conv1(x)
x = self.maxpool1(x)
x = self.conv2(x)
x = self.maxpool2(x)
x = self.conv3(x)
x = self.maxpool3(x)
x = self.flatten(x)
x = self.linear1(x)
x = self.linear2(x)
return x
MyModule=MyModule()
loss=nn.CrossEntropyLoss()
for data in dataloader:
imgs,targets=data
# 向前传播 该网络输出为一个10维向量,每一维表示是此类的概率
outputs=MyModule(imgs)
#利用交叉熵计算损失
result_loss=loss(outputs,targets)
#反向传播,计算梯度
result_loss.backward()
14、优化器
优化器会根据第13节生成的梯度对模型权值进行修改以求loss的降低,优化器有多种,可以根据自己的需要选择合适优化器。
#根据梯度选择合适的优化器进行调参
"""
torch.optim.Adadelta(params, lr=1.0, rho=0.9, eps=1e-06, weight_decay=0):
params:要调节的模型参数
lr:学习速率
"""
#Back propagation反向传播,在test14网络的基础上进行
import torch
import torchvision.datasets
from torch import nn
from torch.nn import Conv2d, MaxPool2d, Flatten, Linear, Sequential
from torch.utils.data import DataLoader
from torch.utils.tensorboard import SummaryWriter
dataset_transform=torchvision.transforms.Compose([
torchvision.transforms.ToTensor()
])
test_data=torchvision.datasets.CIFAR10(root="./DataSet",train=False,transform=dataset_transform,download=True)
dataloader=DataLoader(dataset=test_data,batch_size=4,shuffle=True,num_workers=0,drop_last=False)
class MyModule(nn.Module):
def __init__(self):
super(MyModule, self).__init__()
self.conv1=Conv2d(3,32,5,padding=2)
self.maxpool1=MaxPool2d(2)
self.conv2=Conv2d(32,32,5,padding=2)
self.maxpool2=MaxPool2d(2)
self.conv3=Conv2d(32,64,5,padding=2)
self.maxpool3=MaxPool2d(2)
self.flatten=Flatten()
self.linear1=Linear(1024,64)
self.linear2=Linear(64,10)
"""
Sequential():
依次执行()中的操作
"""
self.model1=Sequential(#这个与前面分开写的是一样的
Conv2d(3, 32, 5, padding=2),
MaxPool2d(2),
Conv2d(32, 32, 5, padding=2),
MaxPool2d(2),
Conv2d(32, 64, 5, padding=2),
MaxPool2d(2),
Flatten(),
Linear(1024, 64),
Linear(64, 10)
)
def forward(self, x):
x = self.conv1(x)
x = self.maxpool1(x)
x = self.conv2(x)
x = self.maxpool2(x)
x = self.conv3(x)
x = self.maxpool3(x)
x = self.flatten(x)
x = self.linear1(x)
x = self.linear2(x)
return x
MyModule=MyModule()
loss=nn.CrossEntropyLoss()
#定义优化器
optim=torch.optim.SGD(MyModule.parameters(),lr=0.01)
for epoch in range(20):#进行20次迭代调参
running_loss=0.0#测试每一次迭代所有的误差,对比可以发现逐渐减小
for data in dataloader:#对所有数据
imgs,targets=data
outputs=MyModule(imgs)
result_loss=loss(outputs,targets)
# 将上一步的梯度置零
optim.zero_grad()
result_loss.backward()
# 优化器调整weight
optim.step()
running_loss=running_loss+result_loss
print(running_loss)
15、导入现有网络
官方定义了一系列优秀的网络结构供直接使用,其中还有预训练的权值,下面以VGG16举例:
#VGG网络
import torchvision
from torch import nn
"""
直接将VGG16的网络结构拿过来使用
"""
vgg16_F=torchvision.models.vgg16(pretrained=False)#不要预训练的权重
vgg16_T=torchvision.models.vgg16(pretrained=True)#需要预训练的权重
print(vgg16_T)#查看网络结构
"""
可以发现vgg16是对1000个类别进行识别,而CIFAR10仅有10个标签,如何利用vgg16在CIFAR10上进行使用呢
"""
train_data=torchvision.datasets.CIFAR10(root="./DataSet",train=True,
transform=torchvision.transforms.ToTensor(),
download=True)
#1、在vgg16的最后增加一个线性层输入为1000维输出为10维
vgg16_T.add_module('add_linear',nn.Linear(1000,10))
print(vgg16_T)#查看网络结构
"""
修改网络结构的方式:
1、在某类中加:
vgg16_T.classifier.add_module('add_linear',nn.Linear(1000,10))
2、在网络的最后加:
vgg16_T.add_module('add_linear',nn.Linear(1000,10))
3、对某一层进行修改:
找到那一层直接赋值:
vgg16_T.classifier[6]=nn.Linear(1000,10)
"""
16、模型的储存
将训练好的模型储存供下一次使用,这里有两种方式:
#模型的存放与读取
import torchvision
import torch
vgg16=torchvision.models.vgg16(pretrained=False)#需要预训练的权重
"""
保存方式1:torch.save(vgg16,"vgg16_method1.pth")
保存了网络模型结构+参数
注意:对于自己定义的模型,在读取是需要将那个模型类重新定义一下,但无需创建对象,class需要定义,
mymodule=MyModule()不用
"""
torch.save(vgg16,"vgg16_method1.pth")
"""
保存方式2:
将参数保存为字典的形式,仅保存参数(官方推荐),占有空间小
"""
torch.save(vgg16.state_dict(),"vgg16_merhod2.pth")
17、模型的读取
结合16一起看,不同的存储方式对应不同的读取方式:
#模型的存放与读取
import torchvision
import torch
"""
读取方式1:对应保存方式1
"""
model=torch.load("vgg16_method1.pth")
print(model)
"""
读取方式2:对应保存方式2
需要定义模型,然后将参数加载到模型中
"""
vgg16=torchvision.models.vgg16(pretrained=False)
model=torch.load("vgg16_method2.pth")
vgg16.load_state_dict(model)
print(model)
18、标签选择
对于分类任务,网络会对图片与标签的对应打分,我们需要选择打分最高的一个,这里以一个例子进行说明
#选择分数最大的一项作为此图的类别
import torch
#模拟得分,共0,1两个类别,一个[]表示一张图片的得分
input=torch.tensor([[0.1,0.2],
[0.3,0.4]])
preds=input.argmax(1)#横向进行选择,0的时候为纵向
print(preds)
19、经典样例(有基础的推荐直接看这里)
这个样例非常经典,用到了前面讲的所有内容,是一个在CIFAR10数据集上进行的分类任务。主要是这么几步:准备数据,加载数据,准备模型,设置损失函数,设置优化器,开始训练,最后验证,结果聚合展示。
#样例 准备数据,加载数据,准备模型,设置损失函数,设置优化器,开始训练,最后验证,结果聚合展示
import torch
import torchvision
from torch.nn import Sequential
from torch.utils.data import DataLoader
from torch import nn
from torch.utils.tensorboard import SummaryWriter
"""准备数据集"""
#训练
train_data=torchvision.datasets.CIFAR10(root="./DataSet",train=True,
transform=torchvision.transforms.ToTensor(),
download=True)
#测试
test_data=torchvision.datasets.CIFAR10(root="./DataSet",train=False,
transform=torchvision.transforms.ToTensor(),
download=True)
train_data_size=len(train_data)
test_data_size=len(test_data)
print("训练数据集的长度为{}".format(train_data_size))#50000
print("测试数据集的长度为{}".format(test_data_size))#10000
#利用DataLoader加载数据集
train_dataloader=DataLoader(train_data,batch_size=64)
test_dataloader=DataLoader(test_data,batch_size=64)
#搭建神经网络
class MyModule(nn.Module):
def __init__(self):#注意是init!!!
super(MyModule, self).__init__()
self.model1=Sequential(
nn.Conv2d(3,32,5,1,2),
nn.MaxPool2d(2),
nn.Conv2d(32, 32, 5, 1, 2),
nn.MaxPool2d(2),
nn.Conv2d(32, 64, 5, 1, 2),
nn.MaxPool2d(2),
nn.Flatten(),
nn.Linear(64*4*4,64),
nn.Linear(64,10)
)
def forward(self, x):
x=self.model1(x)
return x
#初始化
#创建模型
myModule=MyModule()
#损失函数
loss_fn=nn.CrossEntropyLoss()
#定义优化器
learning_rate=0.01
optimize=torch.optim.SGD(myModule.parameters() ,lr=learning_rate )
#负责记录
#tensorboard
writer=SummaryWriter("logs")
#训练次数
train_step=0
#测试次数
test_step=0
#训练轮数
epoch=10
#开始训练
#训练迭代几次
for i in range(epoch):
print("------第 {} 轮训练开始------".format(i+1))
#每一次的训练
myModule.train()
for data in train_dataloader:
imgs,targets=data
outputs=myModule(imgs)
#计算损失
loss=loss_fn(outputs,targets)
# 利用优化器对参数优化,调优
optimize.zero_grad()
loss.backward()
optimize.step()
train_step=train_step+1#一张图片加一次
# y轴:loss,x轴:训练次数
writer.add_scalar("train_loss", loss.item(), train_step)
if train_step%100==0:
print("训练次数:{},Loss:{}".format(train_step,loss.item()))#加item将tensor转化为数字
#对每一次训练进行测试
myModule.eval()
total_test_loss=0#损失
total_accuracy=0#正确率
test_step=test_step+1
with torch.no_grad():#不要梯度,不训练
for data in test_dataloader:
imgs,targets=data
output=myModule(imgs)
loss=loss_fn(output,targets)
total_test_loss=total_test_loss+loss.item()
#测试值与真实值相同的个数表示准确率
total_accuracy=total_accuracy+(output.argmax(1)==targets).sum()
print("第{}次测试误差为{}".format(test_step,total_test_loss))
print("测试数据集上的准确率{}".format(total_accuracy/test_data_size))
writer.add_scalar("test_loss",total_test_loss,test_step)
writer.add_scalar("test_accutacy",total_accuracy/test_data_size,test_step)
#保存每一轮训练后的模型
torch.save(myModule.state_dict(),"myModule_{}.pth".format(i))
print("模型已保存")
writer.close()
"""
注:
训练时有些代码利用myModule.train():只对Dropout、BatchNorm有作用
测试时有些代码利用myModule.eval():只对Dropout、BatchNorm有作用
"""
20、利用GPU
方式一:利用.cuda()
这种方式可以将模型、数据、损失函数加速
#在19的基础上
#初始化
#创建模型
myModule=MyModule()
"""模型GPU加速"""
if torch.cuda.is_available():
myModule=myModule.cuda()
loss_fn=nn.CrossEntropyLoss()
"""损失函数GPU加速"""
if torch.cuda.is_available():
loss_fn=loss_fn.cuda()
imgs,targets=data
"""数据GPU加速"""
if torch.cuda.is_available():
imgs=imgs.cuda()
targets=targets.cuda()
方式二:利用.to(device)
首先定义device,然后在代码中对相关部分进行移植。
#在19的基础上
"""
torch.device()里面可以写cpu/cuda/cuda:0
"""
#定义设备
device=torch.device("cuda" if torch.cuda.is_available() else "cpu")
#GPU可用的情况下用cuda,否则cpu
#初始化
#创建模型
myModule=MyModule()
"""模型GPU加速"""
myModule=myModule.to(device)
#注意模型和损失函数其实不用写=,可以直接写myModule.to(device),但数据必须得写,
#为了方便记忆所以这里直接都写=了
loss_fn=nn.CrossEntropyLoss()
"""损失函数GPU加速"""
loss_fn=loss_fn.to(device)
imgs,targets=data
"""数据GPU加速"""
imgs=imgs.to(device)
targets=targets.to(device)
21、测试
读取图片,应用训练好模型进行测试。
#读取现有的模型myModule_0.pth进行测试
import torch
import torchvision
from PIL import Image
from torch import nn
from torch.nn import Sequential
image_path="imgs/8d6ffeef9c4ccd32304fd2df50d40d47.jpeg"
image=Image.open(image_path)
image=image.convert('RGB')#改变通道使其为RGB,此步是为了适应png、jpg等图片类型的图片
#将图片大小变为32*32,再变成tensor类型,此步是预处理为了对应于网络的输入
transform=torchvision.transforms.Compose([torchvision.transforms.Resize((32,32)),
torchvision.transforms.ToTensor()])
image=transform(image)
image=torch.reshape(image,(1,3,32,32))
print(image.shape)#[1,3,32,32]
#搭建神经网络
class MyModule(nn.Module):
def __init__(self):#注意是init!!!
super(MyModule, self).__init__()
self.model1=Sequential(
nn.Conv2d(3,32,5,1,2),
nn.MaxPool2d(2),
nn.Conv2d(32, 32, 5, 1, 2),
nn.MaxPool2d(2),
nn.Conv2d(32, 64, 5, 1, 2),
nn.MaxPool2d(2),
nn.Flatten(),
nn.Linear(64*4*4,64),
nn.Linear(64,10)
)
def forward(self, x):
x=self.model1(x)
return x
#加载保存的模型,注意与保存方式对应
myModule = MyModule()
#如果是在GPU生成模型加载到cpu运行需要进行指明
#model=torch.load("myModule_0.pth",map_location=torch.device('cpu'))
model=torch.load("myModule_0.pth")
myModule.load_state_dict(model)
#不要忘记写,不训练了
myModule.eval()
with torch.no_grad():
output=myModule(image)
print(output)
print(output.argmax(1))#概率最大的类别标签
22、numpy与tensor
import torch
import numpy as np
#初始化
Ta=torch.rand((3,4))
Na=np.array([[1,2,3],[4,5,6],[7,8,9]])
print(Ta)
print(Na)
#相互转换
Tb=torch.from_numpy(Na)
Nb=Ta.numpy()
print(Tb)
print(Nb)
#维度转化
Ta=Ta.view(2,-1)
Na=Na.reshape(9,-1)
print(Ta)
print(Na)