快速上手pytorch

本文根据小土堆教程撰写(这个up讲的超好,幼儿园毕业表示听起来没有压力,推荐刚入手的小伙伴看一下)。

蚂蚁蜜蜂/练手数据集:链接: https://pan.baidu.com/s/1jZoTmoFzaTLWh4lKBHVbEA 密码: 5suq

官方文档:torch.nn — PyTorch 1.10.0 documentation

这篇博客将以我自己的理解结合例子进行说明,示例中均有详细注释,建议自己尝试一下。

目录

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)

猜你喜欢

转载自blog.csdn.net/hihui1231/article/details/121030699