创建新项目的时候选择的编译器要选择Conda环境,如果手选Conda环境,要在Anaconda3的envs这个文件里选pytorch里面的python.exe就可以了。

两个重要函数

pytorch是一个package工具箱

  • dir():打开,看见里面有什么东西。当使用dir()得到的参数是双下划线的时候,说明这些标识符是函数。
  • help():说明书,还有一种形式例如Dataset??
    • 例如查看is_available()函数,我们就运行help(torch.cuda.is_available),括号里只需要输入标识符即可

加载数据

如何读取数据涉及到两个类

  • Dataset:提供一种方式去获取数据及其label。
    • Dataset是一个抽象类,所有数据集都要继承它,所有子类都要实现getitem方法,选择性重写len方法。
    • 如何获取每一个数据及其label?实现getitem方法。
    • 告诉我们总共有多少的数据?重写len方法
  • Dataloader:对我们要送进网络的数据进行打包,为网络提供不同的数据形式

import os:

  • os.path.join(str1,str2...)函数的参数可以放字符串,得到的字符串结果是把多个字符串用\\连接,因为单独的\会被当成转义字符,/并不是转义字符,例如:

    root_dir="dataset/train"	label_dir="ants"
    path=os.path.join(root_dir,label_dir)
    >>>path={str}'dataset/train\\ants'
  • os.listdir(path)函数可以将路径path中的文件转化成列表形式,例如:

    img_path_list=os.listdir(path)
    >>>img_path_list={list:124}[...]

from PIL import Image

  • Image.open(img_path)此函数用于创建图片,返回{JpegImageFile}变量
  • img.show()函数用于显示图片

对目前所学的一个例子:

from torch.utils.data import Dataset
# 从常用工具区的关于数据的 data 区 import Dataset
from PIL import Image #读取图片
import os #operating system 关于系统的库
class MyData(Dataset):#继承Dataset类
def __init__(self,root_dir,label_dir):
self.root_dir=root_dir #self相当于在类中创建了全局变量
self.label_dir=label_dir
self.path = os.path.join(self.root_dir, self.label_dir)
self.img_path = os.listdir(self.path)

def __getitem__(self, idx):
img_name = self.img_path[idx]#注意要加self
img_item_path = os.path.join(self.path,img_name)
img=Image.open(img_item_path)
label = self.label_dir #相对于本个例子而言的label
return img, label
#这里重写了getitem,返回img,label,就达到了我们利用下标获取每一个数据及其label的目的

def __len__(self):
return len(self.img_path)#返回列表长度


root_dir = "dataset/train"

ants_label_dir = "ants"
ants_dataset = MyData(root_dir, ants_label_dir)

bees_label_dir = "bees"
bees_dataset = MyData(root_dir, bees_label_dir)

print(ants_dataset[0])#会输出img以及对应的label
img,label = ants_dataset[0]#实现了geititem函数,有两个返回值
img.show()//显示图片
img,label = bees_dataset[1]
img.show()

len(ants_dataset)#得到124
len(bees_dataset)#得到121
train_dataset = ants_dataset+bees_dataset#这个数据集会变成ants_dataset和bees_dataset的拼接,达到仿造数据集,解决数据集不足问题
len(train_dataset)#得到245

img,label=train_dataset[123]#得到蚂蚁,蚂蚁是[0-123]
img,label=train_dataset[124]#得到蜜蜂,蜜蜂是[124-244]

作出数据集的另一种存储方式:

#把图片的label生成以图片名为文件名的txt文档
import os

root_dir = '练手数据集/train'
target_dir = 'ants_image'
img_path = os.listdir(os.path.join(root_dir, target_dir))
label = target_dir.split('_')[0]
out_dir = 'ants_label'
for i in img_path:
file_name = i.split('.jpg')[0]
with open(os.path.join(root_dir, out_dir,"{}.txt".format(file_name)),'w') as f:#open会生成不存在的文件
f.write(label)

TensorBoard的使用

TensorBoard:演示transform的结果,在运行完方法后展示图像。例如loss图、训练结果output。

首先要from torch.utils.tensorboard import SummaryWriter

SummaryWriter类使用

CTRL+鼠标移至类可以查看类的用法。

  • SummerWriter将条目直接写入 TensorBoard 要使用的log_dir中的事件文件,事件文件可以直接被TensorBoard解析。

  • 初始化的时候需要输入log_dir文件夹的名称,不输入也可以,有默认位置。

  • 初始化方式:

    writer = SummaryWriter()#什么都不加,默认的事件文件存储的位置在runs/下
    writer = SummaryWriter("my_experiment")#给出初始化文件夹
    writer = SummaryWriter(comment="LR_0.1_BATCH_16")#设置相应参数

主要用到两个方法:

  • writer.add_scalar(tag(string),scalar_value(float or string/blobname),global_step(int))
    • 参数tag(string)设置图表的title
    • 参数scalar_value(float or string/blobname)设置图表y轴
    • 参数global_step(int)对应x轴

打开事件文件,在pycharm的命令行,输入tensorboard --logdir=事件文件所在文件夹名。如果端口被占用了,可以自行添加端口,输入tensorboard --logdir=事件文件所在文件夹名 --port=端口值

如果打开的tensorboard出现了重复在一张图上的情况,需要进行删除原来的事件文件,或者重新SummaryWriter一个log_dir文件

  • writer.add_image(tag(string),img_tensor(torch.Tensor, numpy.array, or string/blobname),global_step(int))
    • 参数tag(string),设置图像的title
    • 参数img_tensor(torch.Tensor, numpy.array, or string/blobname),设置图像
    • 参数global_step(int),设置训练步骤

可以利用Opencv读取图片,获得numpy型图片数据。

如果是用PIL数据转化成numpy数据,需要在add_image()中指定shape中每一个数字/维表示的含义。主要是通道数。

step可以理解为多张图片可以放在同一个title里面,通过滑块可以看到每一步的图片,如果想让多张图片单独显示,需要把title重新命名

上面的代码综合运用:

from torch.utils.tensorboard import SummaryWriter
from PIL import Image
import numpy as np

writer = SummaryWriter("logs")#存储文件夹
# y=3x
for i in range(100): #i从0-99
writer.add_scalar("y=3x",3*i,i)# 添加标量

img_path = "data/train/ants_image/0013035.jpg"
img = Image.open(img_path)
img_array = np.array(img) # 类型转换,这样得到的numpy.array的通道是在最后面的
print(type(img_array))
print(img_array.shape) # 结果是(512,768,3)
writer.add_image("train",img_array,1,dataformats='HWC')#需要指定是HWC型,因为数据就是通道在最后面,不然会报错

writer.close()

Transforms的使用

Transforms主要是用于对图片进行一些变换。将特定格式的图片经过工具之后输出我们想要的图片结果。

Transforms的结构及用法

Transforms.py工具箱具有的工具:

  • Totensor类,Totensor就是把PIL Image或者numpy.ndarray类型转化为tensor类型的
  • resize类,裁剪

tensor数据类型

通过transforms.Totensor去解决两个问题:

  • transforms该如何使用(python)?

    tensor_trans = transforms.ToTensor() 	#返回totensor的对象,默认构造
    tensor_img = tensor_trans(img) #将img转化为tensor类型的img
  • Tensor数据类型相较于普通的数据类型或者图片数据类型有何区别?为什么需要tensor的数据类型?

    Tensor数据类型包装了神经网络所需要的一些参数

上述示例代码:

from torch.utils.tensorboard import SummaryWriter
from torchvision import transforms
from PIL import Image
import cv2

img_path = "data/train/ants_image/0013035.jpg"
img = Image.open(img_path)
print(img) # 得到<PIL.JpegImagePlugin.JpegImageFile image mode=RGB size=768x512 at 0x1B1C3BB77F0>

tensor_trans = transforms.ToTensor() #新建totensor的对象
tensor_img = tensor_trans(img)#调用call函数,调用类的名称时会自动运行该方法,将img转化为tensor类型的img
print(tensor_img)

cv_img = cv2.imread(img_path) #cv_img的数据类型就是numpy.ndarray
tensor_cv_img = tensor_trans(cv_img)#将cv_img转化为tensor类型的img
print(tensor_cv_img)

writer = SummaryWriter("logs")
writer.add_image("Tensor_img",tensor_img)

writer.close()

python中call函数的作用:call函数可以直接使用对象名(参数)这种方式去调用,这种方式就像Java重写的toString一样

常见的Transform

Compose类:把不同的transform结合在一起

ToTensor类:把PIL Image或者numpy.ndarray类型转化为tensor类型

ToPILImage类:把tensor类型或者ndarray类型转化为PIL Image类型

Normalize类:用平均值和标准差对tensor image(图像张量)进行归一化,归一到[-1,1]

Resize类:把图像缩放成指定的大小,如果给了元组序列,图像就会按元组序列的大小来重新调整,如果只是给了一个int型,图像就会以这个int型进行等比缩放,最短边的长度等于int值。输入为PIL Image,返回值还是PIL Image

RandomCrop类:随即裁剪,和Resize用法差不多,输入都是 PIL Image

使用方法总结:1. 关注输入和输出类型,输出可以直接print()看,也可以断点看 2.多看官方文档 3.关注方法需要的参数,没有设置默认值就是要输入的参数,找到它然后查看它的类型

上述代码实例:

from PIL import Image
from torch.utils.tensorboard import SummaryWriter
from torchvision import transforms

img = Image.open("data/train/ants_image/0013035.jpg")
print(img)
writer = SummaryWriter("logs")

# Totensor
trans_totensor = transforms.ToTensor()
img_tenser = trans_totensor(img)
writer.add_image("ToTenser", img_tenser)

# Normalize
# 归一化计算公式:output[channel] = (input[channel] - mean[channel]) / std[channel]
print(img_tenser[0][0][0])
trans_norm = transforms.Normalize([0.5,0.5,0.5],[0.5,0.5,0.5])
img_norm = trans_norm(img_tenser)
print(img_norm[0][0][0])
writer.add_image("Normalize",img_norm)

# Resize
print(img.size) #(768, 512)
trans_resize = transforms.Resize((512,512))
# img是PIL类型,经过resize返回还是一个PIL类型
img_resize = trans_resize(img)
print(img_resize) #<PIL.Image.Image image mode=RGB size=512x512 at 0x13442E71430>
#img_resize.show()
# PIL类型使用totensor转化为tensor对象
img_resize = trans_totensor(img_resize)
writer.add_image("Resize",img_resize,0)

# Compose — resize的第二种用法
trans_resize_2 = transforms.Resize(512)
# PIL Image->PIL Image->tensor 要注意列表中前一个输出和后一个输入是不是相互匹配,因为是联系的
trans_compose = transforms.Compose([trans_resize_2,trans_totensor])
img_resize_2 = trans_compose(img)#第一个transform需要的输入
writer.add_image("Resize",img_resize_2,1)

# RandomCrop
trans_random = transforms.RandomCrop(512)
trans_compose_2 = transforms.Compose([trans_random,trans_totensor])
for i in range(10):
img_crop = trans_compose_2(img)
writer.add_image("RandomCrop",img_crop,i)

writer.close()

将dataset和transform结合到一起

dataset告诉我们数据集在什么样的位置和多少数据,给索引+数据

具体实例:

import torchvision
from PIL import Image
from torch.utils.tensorboard import SummaryWriter

dataset_transform = torchvision.transforms.Compose([
torchvision.transforms.ToTensor()
])
# 保存数据集的路径,训练集还是测试集,transform,下载到本地否,'.'表示当前目录,'..'表示上一级目录
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)#测试集
# (<PIL.Image.Image image mode=RGB size=32x32 at 0x2784D869610>, 3),3是target
# 原始图片是PIL Image
print(test_set[0])
print(test_set.classes)

img, target = test_set[0]
print(img)
print(target)
print(test_set.classes[target])
img.show()

print(test_set[0])

writer = SummaryWriter("logs")
for i in range(10):
img,target = test_set[i]
writer.add_image("test_set",img,i)

writer.close()

dataloader

dataloader就是从dataset中取数据,参数控制取多少怎么取

  • batch_size这个参数是指每次取多少数据
  • suffle这个参数是打乱顺序
  • num_workers多线程,加载数据的时候采用单个线程还是多个线程,默认情况为0,采用主线程
  • drop_last是否舍去最后的余数

代码实例:

import torchvision
from torch.utils.data import DataLoader
from torch.utils.tensorboard import SummaryWriter

# 准备的测试数据集
test_data = torchvision.datasets.CIFAR10("./dataset",train=False,transform=torchvision.transforms.ToTensor())

#取出四个img,四个target的形式分别打包返回
test_loader = DataLoader(dataset=test_data,batch_size=64,shuffle=False,num_workers=0,drop_last=False)

# 测试数据集中第一张图片及target
img,target = test_data[0]
print(img.shape)
print(target)

writer = SummaryWriter("logs")
for epoch in range(2):#相当于一轮结束的洗牌
step = 0
for data in test_loader:
imgs, targets = data
# print(imgs.shape)
# print(targets)
writer.add_images("Epoch:{}".format(epoch),imgs,step)
step = step+1

writer.close()

网络基本骨架的搭建

关于神经网络的工具一般都在torch.nn里面

Containers—>神经网络的骨架、结构,常用Module模块,为所有神经网络提供一个基本的骨架,所搭建的神经网络必须从torch.nn.Module类继承而来。其中有个forward函数,它是call方法的实现,在所有继承了torch.nn.Module类的子类中都要重写,是前向传播,比较重要,类似于call方法直接调用类作为方法,它的作用是输入通过forward变为输出

Module的使用代码:

import torch
from torch import nn


class Model(nn.Module):
def __init__(self) -> None:
super().__init__()

#这种方法也可以初始化,但是我不知道区别在哪
#def __init__(self):
#super(Model, self).__init__()

def forward(self,input):
output = input +1
return output


model = Model()
x = torch.tensor(1.0)
# 把x放到神经网络中
output = model(x)
print(output)

Sequential的目的可以让神经网络代码压缩在一段代码中,过程连续。

sequential实例代码:

import torch
from torch import nn
from torch.nn import Conv2d, MaxPool2d, Flatten, Linear, Sequential
from torch.utils.tensorboard import SummaryWriter


class CNN(nn.Module):
def __init__(self):
super(CNN, 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)

self.module1 = 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)
)
# 检验网络参数是否正确的话,可以一步步写forward 然后运行查看output.shape
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)

x = self.module1(x)
return x

cnn = CNN()
print(cnn)

input = torch.ones((64,3,32,32))
output = cnn(input)
print(output.shape)

writer = SummaryWriter("logs")
writer.add_graph(cnn,input)
writer.close()

Covolution Layers—>卷积层

卷积核随机初始化,网络训练的就是卷积核

卷积操作内部具体实现<实践用不到>:

import torch
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]])
#调整尺寸,由于cov2d的input需要四位张量
input = torch.reshape(input,(1,1,5,5))
kernel = torch.reshape(kernel,(1,1,3,3))


print(input.shape)
print(kernel.shape)

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)

代码示例:

import torchvision
import torch
import torch.nn
from torch import nn
from torch.nn import Conv2d
from torch.utils.data import DataLoader
from torch.utils.tensorboard import SummaryWriter

dataset = torchvision.datasets.CIFAR10("./dataset",train=False,transform=torchvision.transforms.ToTensor(),download=True)

dataloader = DataLoader(dataset,batch_size=64)

class NN(nn.Module):
def __init__(self):
super(NN, 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


nn = NN()
print(nn)
writer = SummaryWriter("logs")
step = 0
for data in dataloader:
imgs,targets = data
output = nn(imgs)
print(imgs.shape)
print(output.shape)
#torch.Size([64, 3, 32, 32])
writer.add_images("input",imgs,step)
#torch.Size([64, 6, 30, 30]) 会报错,因为彩色图像为3个channel,6个channel不知道怎么显示

output = torch.reshape(output,(-1,3,30,30))#设置-1,会根据数据自动调整
writer.add_images("output",output,step)
step = step + 1

Pooling Layers—>池化层

最大池化目的:保留输入的特征,同时把参数量减少

代码示例:

import torch
import torchvision.datasets
from torch import nn
from torch.nn import MaxPool2d
from torch.utils.data import DataLoader
from torch.utils.tensorboard import SummaryWriter

dataset = torchvision.datasets.CIFAR10("./dataset",train=False,download=True,transform=torchvision.transforms.ToTensor())

dataloader = DataLoader(dataset, batch_size=64)

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)#其中数字转化为浮点数,编程浮点数的tensor类型

input = torch.reshape(input,(-1,1,5,5))
print(input.shape)

class maxpool(nn.Module):
def __init__(self):
super(maxpool, self).__init__()
self.maxpool1 = MaxPool2d(kernel_size=3,ceil_mode=True)

def forward(self,input):
output = self.maxpool1(input)
return output


mp = maxpool()
# output = mp(input)
# print(output)
writer = SummaryWriter("logs")
step = 0
for data in dataloader:
imgs,targets = data
writer.add_images("input",imgs,step)
output = mp(imgs)#得到的图像依然是三维的
writer.add_images("output",output,step)
step=step+1

writer.close()

非线性激活

RELU,当input>0时,赋值为input原来值,当input<0时,赋值为0,输入只有N—>batch_size;Sigmoid,也只是输入N。

ReLU的实例代码:

import torch
from torch import nn
from torch.nn import ReLU

input = torch.tensor([[1,-0.5],
[-1,3]])
input = torch.reshape(input,(-1,1,2,2))
print(input.shape)

class RELU(nn.Module):
def __init__(self):
super(RELU, self).__init__()
self.relu = ReLU()#inplace这个参数如果是True就在原值上进行改变,如果为False原值不变,返回改变后的值

def forward(self,input):
output = self.relu(input)
return output


relu = RELU()
output = relu(input)
print(output)

Sigmoid的实例代码:

import torch
import torchvision.datasets
from torch import nn
from torch.nn import ReLU, Sigmoid
from torch.utils.data import DataLoader
from torch.utils.tensorboard import SummaryWriter
class SigMoid(nn.Module):
def __init__(self):
super(SigMoid, self).__init__()
self.sigmoid = Sigmoid()

def forward(self,input):
output = self.sigmoid(input)
return output

sigmoid = SigMoid()

dataset = torchvision.datasets.CIFAR10("./dataset",train=False,download=True,transform=torchvision.transforms.ToTensor())
dataloader = DataLoader(dataset,batch_size = 64)
writer = SummaryWriter("logs")
step = 0
for data in dataloader:
imgs,targets = data
writer.add_images("input",imgs,step)
output=sigmoid(imgs)
writer.add_images("output",output,step)
step=step+1

writer.close()

Padding Layers—>填充层,用的不多,在卷积里能达到同样的效果

Normalization Layers—>标准化层,用的不多

Linear Layers—>全连接层

Linear代码示例:

import torch
import torchvision
from torch import nn
from torch.nn import Linear
from torch.utils.data import DataLoader

dataset =torchvision.datasets.CIFAR10("./dataset",train=False,transform=torchvision.transforms.ToTensor())
dataloader= DataLoader(dataset,batch_size=64)

class LINEAR(nn.Module):
def __init__(self):
super(LINEAR, self).__init__()
self.linear = Linear(196608,10)

def forward(self,input):
output = self.linear(input)
return output


linear = LINEAR()
for data in dataloader:
imgs,target = data
print(imgs.shape)
# output = torch.reshape(imgs,(1,1,1,-1))
output = torch.flatten(imgs)#扁平化处理
print(output.shape)
output = linear(output)
print(output.shape)

Loss Function:loss就是输出值与实际的误差值,越小越好,是神经网络训练的依据。可以计算实际输出和目标之间的差距,为我们更新输出提供一定的依据(反向传播)。

  • CrossEntropyLoss,交叉熵,分类问题中有C个类别。

各种loss函数的使用

import torch
from torch import nn
from torch.nn import L1Loss, MSELoss

# 实际默认的数据就是float,这是因为我们创建tensor的时候没有加小数
inputs = torch.tensor([1,2,3],dtype=torch.float32)
targets = torch.tensor([1,2,5],dtype=torch.float32)

inputs = torch.reshape(inputs,(1,1,1,3))
targets = torch.reshape(targets,(1,1,1,3))

loss = L1Loss()#默认是取平均差值,可以选择取加值
result = loss(inputs,targets)
print(result)#tensor(0.6667)

loss_mse = MSELoss()#取平均平方差
result_mse = loss_mse(inputs,targets)
print(result_mse)#tensor(1.3333)

x = torch.tensor([0.1,0.2,0.3])
print(x.shape)
y = torch.tensor([1])
x = torch.reshape(x,(1,3))#一张图,三个分类
print(x.shape)
loss_cross = nn.CrossEntropyLoss()
result_cross = loss_cross(x,y)
print(result_cross)
import torch
import torchvision
from torch import nn
from torch.nn import Conv2d, MaxPool2d, Flatten, Linear, Sequential
from torch.utils.data import DataLoader

dataset = torchvision.datasets.CIFAR10("./dataset",train = False,transform=torchvision.transforms.ToTensor(),download=True)
dataloader = DataLoader(dataset,batch_size=1)

class CNN(nn.Module):
def __init__(self):
super(CNN, self).__init__()
self.module1 = 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.module1(x)
return x

loss = nn.CrossEntropyLoss()
cnn = CNN()
for data in dataloader:
#看outputs和targets长什么样,看选择什么样的损失函数
imgs,targets = data
output = cnn(imgs)
# print(output)
# print(targets)
# 反向传播,求导,需要选择合适的优化器
result_loss = loss(output,targets)
# print(result_loss)
#得到反向传播要更新参数对应的梯度
result_loss.backward()

加上参数调优之后的整个网络:

import torch
import torchvision
from torch import nn
from torch.nn import Conv2d, MaxPool2d, Flatten, Linear, Sequential
from torch.utils.data import DataLoader

dataset = torchvision.datasets.CIFAR10("./dataset",train = False,transform=torchvision.transforms.ToTensor(),download=True)
dataloader = DataLoader(dataset,batch_size=1)

class CNN(nn.Module):
def __init__(self):
super(CNN, self).__init__()
self.module1 = 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.module1(x)
return x

# 交叉熵损失函数
loss = nn.CrossEntropyLoss()
cnn = CNN()
# 优化器,第一个参数是模型的参数
optim = torch.optim.SGD(cnn.parameters(),lr= 0.01)
for epoch in range(20):
running_loss = 0.0
for data in dataloader:
# 看outputs和targets长什么样,看选择什么样的损失函数
imgs,targets = data
output = cnn(imgs)
# 计算loss
result_loss = loss(output,targets)
# 把上一个循环中每个参数对应的梯度清零
optim.zero_grad()
# 反向传播,计算更新参数对应的梯度
result_loss.backward()
# 参数调优
optim.step()
running_loss = running_loss + result_loss

print(running_loss)

使用网络模型调参

import torchvision
from torch import nn

# train_data = torchvision.datasets.ImageNet("./dataset_ImageNet",split='train',transform=torchvision.transforms.ToTensor())

# 当为false的时候,加载网络模型——>默认参数,可以将pretrained=False改为weight=none;为True的时候需要从网络中下载每一个参数pretrained=True改成weight=default
vgg16_false = torchvision.models.vgg16(pretrained=False)
vgg16_true = torchvision.models.vgg16(pretrained=True)

# print(vgg16_true)

train_data = torchvision.datasets.CIFAR10("./dataset",train=True,transform=torchvision.transforms.ToTensor(),download=True)


# 对VGG-16 True进行调参修改,加到最后
vgg16_true.add_module('add_linear',nn.Linear(1000,10))
# print(vgg16_true)
#加到相应标签下
vgg16_true.classifier.add_module('add_linear',nn.Linear(1000,10))
# print(vgg16_true)

# print(vgg16_false)
#对某一个步骤进行修改
vgg16_false.classifier[6] =nn.Linear(4096,10)
print(vgg16_false)

网络模型的保存与读取

import torch
import torchvision
from torch import nn

vgg16 = torchvision.models.vgg16(pretrained=False)
# 保存方式1,保存网络模型的结构和参数
# 在别的文件使用方法 model = torch.load("vgg16_method1.pth")
torch.save(vgg16,"vgg16_method1.pth")
# 方式一陷阱
class cnn(nn.Module):
def __init__(self):
super(cnn, self).__init__()
self.conv1 = nn.Conv2d(3,64,kernel_size=3)

def forward(self,x):
x = self.conv1(x)
return x

Cnn = cnn()
torch.save(cnn,"Cnn_method1.pth")

# 加载会报错,需要将模型的定义一并放到引用文件中,或者from model_save import *也可以
model = torch.load("Cnn_method1.pth")

# 保存方式2(推荐)
# 将vgg16的参数保存成字典型
# 在别的文件中使用方法 model = torch.load("vgg16_method2.pth")
torch.save(vgg16.state_dict(),"vgg16_method2.pth")

# 使用方法
vgg16 = torchvision.models.vgg16(pretrained=False)
vgg16.load_state_dict(torch.load("vgg16_method2.pth"))

完整的模型训练套路

import torchvision
from torch.utils.data import DataLoader
from torch.utils.tensorboard import SummaryWriter

from model import *

# 准备数据集
train_data = torchvision.datasets.CIFAR10("./dataset",train=True,transform=torchvision.transforms.ToTensor(),download=True)

test_data = torchvision.datasets.CIFAR10("./dataset",train=False,transform=torchvision.transforms.ToTensor(),download=True)

# length 长度
train_data_size = len(train_data)
test_data_size = len(test_data)
# 字符串格式化写法
print("训练数据集的长度为:{}".format(train_data_size))
print("测试数据集的长度为:{}".format(test_data_size))

# 利用DataLoader加载数据集
train_dataloader = DataLoader(train_data,batch_size=64)
test_dataloader = DataLoader(test_data,batch_size=64)

# 创建网络模型
cnn = CNN()

# 创建损失函数
loss_fn = nn.CrossEntropyLoss()

# 定义优化器
learning_rate = 1e-2
optimizer = torch.optim.SGD(cnn.parameters(),lr=learning_rate)

# 设置训练网络的一些参数
# 记录训练的次数
total_train_step = 0
# 记录测试的次数
total_test_step = 0
# 训练的轮数
epoch = 10

# 添加tensorboard
writer = SummaryWriter("logs")

for i in range(epoch):
print("---------第{}轮训练开始---------".format(i+1))
# 训练步骤开始
# 设置成训练模式,与测试模式一样,只有当网络中有Dropout层、BatchNorm层等的时候才发挥作用
cnn.train()
for data in train_dataloader:
imgs,targets = data
outputs = cnn(imgs)
loss = loss_fn(outputs,targets)
# 优化器优化模型
# 梯度清零
optimizer.zero_grad()
# 反向传播
loss.backward()
# 参数优化
optimizer.step()
total_train_step=total_train_step+1
# 加上item()会将tensor值转化为真实的数字,都可以
if total_train_step % 100 == 0:
print("训练次数:{},Loss:{}".format(total_train_step,loss.item()))
writer.add_scalar("train_loss",loss.item(),total_train_step)

# 测试步骤开始
# 设置成测试模式
cnn.eval()
total_test_loss = 0
# 整体正确的个数
total_accuracy = 0
# 没有梯度,方便调优
with torch.no_grad():
for data in test_dataloader:
imgs,targets = data
outputs = cnn(imgs)
loss = loss_fn(outputs,targets)
total_test_loss = total_test_loss+loss
accuracy = (outputs.argmax(1) == targets).sum()
total_accuracy = total_accuracy + accuracy
print("整体测试集上的Loss:{}".format(total_test_loss))
print("整体测试集上的正确率:{}".format(total_accuracy/test_data_size))
writer.add_scalar("test_accuracy",total_accuracy/test_data_size,total_test_step)
writer.add_scalar("test_loss",total_test_loss,total_test_step)
total_test_step = total_test_step+1

# torch.save(cnn,"cnn_{}.pth".format(i))
# print("模型已保存")


writer.close()

上述的准确率计算的说明:

import torch

outputs = torch.tensor([[0.1,0.2],
[0.3,0.4]])
# 参数为1的话从左往右看,0的话从上往下看
print(outputs.argmax(1))
# 看到预测的分类是哪个
preds = outputs.argmax(1)
# 实际的分类是哪个
targets = torch.tensor([0,1])
# 计算出对应位置相等的个数
print((preds == targets).sum())

代码的CNN模型<未调参>:

import torch
from torch import nn

# 搭建神经网络
class CNN(nn.Module):
def __init__(self):
super(CNN, self).__init__()
self.model=nn.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(1024,64),
nn.Linear(64,10)
)

def forward(self,x):
x = self.model(x)
return x

# 主函数用来debug,查看参数等
# if __name__ == '__main__':
# cnn =CNN()
# input = torch.ones(64,3,32,32)
# output = cnn(input)
# print(output.shape)

如何使用GPU来训练

第一种方式:需要网络模型、数据(输入,输出)、损失函数,调用.cuda()返回即可

import torch
import torchvision
from torch import nn
from torch.utils.data import DataLoader
from torch.utils.tensorboard import SummaryWriter
# from model import *
import time
# 准备数据集,数据集没有cuda方法
train_data = torchvision.datasets.CIFAR10("./dataset",train=True,transform=torchvision.transforms.ToTensor(),download=True)

test_data = torchvision.datasets.CIFAR10("./dataset",train=False,transform=torchvision.transforms.ToTensor(),download=True)

# length 长度
train_data_size = len(train_data)
test_data_size = len(test_data)
# 字符串格式化写法
print("训练数据集的长度为:{}".format(train_data_size))
print("训练数据集的长度为:{}".format(test_data_size))

# 利用DataLoader加载数据集
train_dataloader = DataLoader(train_data,batch_size=64)
test_dataloader = DataLoader(test_data,batch_size=64)

# 创建网络模型
class CNN(nn.Module):
def __init__(self):
super(CNN, self).__init__()
self.model=nn.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(1024,64),
nn.Linear(64,10)
)

def forward(self,x):
x = self.model(x)
return x

cnn = CNN()
# 网络模型转移到cuda上面,判断cuda是否可用
if torch.cuda.is_available():
cnn = cnn.cuda()

# 创建损失函数
loss_fn = nn.CrossEntropyLoss()
# 损失函数的cuda
if torch.cuda.is_available():
loss_fn = loss_fn.cuda()

# 定义优化器
learning_rate = 1e-2
optimizer = torch.optim.SGD(cnn.parameters(),lr=learning_rate)

# 设置训练网络的一些参数
# 记录训练的次数
total_train_step = 0
# 记录测试的次数
total_test_step = 0
# 训练的轮数
epoch = 10

# 添加tensorboard
writer = SummaryWriter("logs")

start_time = time.time()
for i in range(epoch):
print("---------第{}轮训练开始---------".format(i+1))
# 训练步骤开始
# 设置成训练模式,与测试模式一样,只有当网络中有Dropout层、BatchNorm层等的时候才发挥作用
cnn.train()
for data in train_dataloader:
imgs,targets = data
# 对输入数据进行cuda
if torch.cuda.is_available():
imgs = imgs.cuda()
targets = targets.cuda()
outputs = cnn(imgs)
loss = loss_fn(outputs,targets)
# 优化器优化模型
# 梯度清零
optimizer.zero_grad()
# 反向传播
loss.backward()
# 参数优化
optimizer.step()
total_train_step = total_train_step+1
# 加上item()会将tensor值转化为真实的数字,都可以
if total_train_step % 100 == 0:
end_time = time.time()
print("耗时:{}".format(end_time-start_time))
print("训练次数:{},Loss:{}".format(total_train_step,loss.item()))
writer.add_scalar("train_loss",loss.item(),total_train_step)

# 测试步骤开始
# 设置成测试模式
cnn.eval()
total_test_loss = 0
# 整体正确的个数
total_accuracy = 0
# 没有梯度,方便调优
with torch.no_grad():
for data in test_dataloader:
imgs,targets = data
if torch.cuda.is_available():
imgs = imgs.cuda()
targets = targets.cuda()
outputs = cnn(imgs)
loss = loss_fn(outputs,targets)
total_test_loss = total_test_loss+loss
accuracy = (outputs.argmax(1) == targets).sum()
total_accuracy = total_accuracy + accuracy
print("整体测试集上的Loss:{}".format(total_test_loss))
print("整体测试集上的正确率:{}".format(total_accuracy/test_data_size))
writer.add_scalar("test_accuracy",total_accuracy/test_data_size,total_test_step)
writer.add_scalar("test_loss",total_test_loss,total_test_step)
total_test_step = total_test_step+1

# torch.save(cnn,"cnn_{}.pth".format(i))
# print("模型已保存")
writer.close()

第二种GPU训练方式:需要网络模型、数据(输入,输出)、损失函数,调用.to(device),这个divcie = torch.device("cpu")也可以是device = torch.device("cuda"),电脑有多个显卡的话,可以指定device = torch.device("cuda:n")

import torch
import torchvision
from torch import nn
from torch.utils.data import DataLoader
from torch.utils.tensorboard import SummaryWriter
# from model import *
import time
# 定义训练设备,如果cuda可用,采用GPU,不可用的话采用CPU
device = torch.device("cuda" if torch.cuda.is_avaliable() else "cpu")
train_data = torchvision.datasets.CIFAR10("./dataset",train=True,transform=torchvision.transforms.ToTensor(),download=True)

test_data = torchvision.datasets.CIFAR10("./dataset",train=False,transform=torchvision.transforms.ToTensor(),download=True)

# length 长度
train_data_size = len(train_data)
test_data_size = len(test_data)
# 字符串格式化写法
print("训练数据集的长度为:{}".format(train_data_size))
print("训练数据集的长度为:{}".format(test_data_size))

# 利用DataLoader加载数据集
train_dataloader = DataLoader(train_data,batch_size=64)
test_dataloader = DataLoader(test_data,batch_size=64)

# 创建网络模型
class CNN(nn.Module):
def __init__(self):
super(CNN, self).__init__()
self.model=nn.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(1024,64),
nn.Linear(64,10)
)

def forward(self,x):
x = self.model(x)
return x

cnn = CNN()
# 网络模型转移到cuda上面,判断cuda是否可用

cnn = cnn.cuda()
cnn = cnn.to(device) # 也可以不用赋值,直接cnn.to(device)

# 创建损失函数
loss_fn = nn.CrossEntropyLoss()
# 损失函数的cuda

loss_fn = loss_fn.cuda()
loss_fn = loss_fn.to(device)
# 定义优化器
learning_rate = 1e-2
optimizer = torch.optim.SGD(cnn.parameters(),lr=learning_rate)

# 设置训练网络的一些参数
# 记录训练的次数
total_train_step = 0
# 记录测试的次数
total_test_step = 0
# 训练的轮数
epoch = 10

# 添加tensorboard
writer = SummaryWriter("logs")

start_time = time.time()
for i in range(epoch):
print("---------第{}轮训练开始---------".format(i+1))
# 训练步骤开始
# 设置成训练模式,与测试模式一样,只有当网络中有Dropout层、BatchNorm层等的时候才发挥作用
cnn.train()
for data in train_dataloader:
imgs,targets = data
# 对输入数据调用硬件
imgs = imgs.to(device)
targets = targets.to(device)
outputs = cnn(imgs)
loss = loss_fn(outputs,targets)
# 优化器优化模型
# 梯度清零
optimizer.zero_grad()
# 反向传播
loss.backward()
# 参数优化
optimizer.step()
total_train_step = total_train_step+1
# 加上item()会将tensor值转化为真实的数字,都可以
if total_train_step % 100 == 0:
end_time = time.time()
print("耗时:{}".format(end_time-start_time))
print("训练次数:{},Loss:{}".format(total_train_step,loss.item()))
writer.add_scalar("train_loss",loss.item(),total_train_step)

# 测试步骤开始
# 设置成测试模式
cnn.eval()
total_test_loss = 0
# 整体正确的个数
total_accuracy = 0
# 没有梯度,方便调优
with torch.no_grad():
for data in test_dataloader:
imgs,targets = data
imgs = imgs.to(device)
targets = targets.to(device)
outputs = cnn(imgs)
loss = loss_fn(outputs,targets)
total_test_loss = total_test_loss+loss
accuracy = (outputs.argmax(1) == targets).sum()
total_accuracy = total_accuracy + accuracy
print("整体测试集上的Loss:{}".format(total_test_loss))
print("整体测试集上的正确率:{}".format(total_accuracy/test_data_size))
writer.add_scalar("test_accuracy",total_accuracy/test_data_size,total_test_step)
writer.add_scalar("test_loss",total_test_loss,total_test_step)
total_test_step = total_test_step+1

# torch.save(cnn,"cnn_{}.pth".format(i))
# print("模型已保存")
writer.close()

完整的模型验证(测试,demo)套路

核心是利用已经训练好的模型,然后给它提供输入。

例如:

import torch
import torchvision
from PIL import Image
from torch import nn

image_path = "./img/dog.jpg"
image = Image.open(image_path)
# 因为png格式是4个通道,除了RGB三通道外,还有一个透明度通道
# 调用image = image.convert('RGB'),保留颜色通道,加上这一步之后,不管是png还是jpg图片都可以运行
image = image.convert('RGB')
transform = torchvision.transforms.Compose([torchvision.transforms.Resize((32,32)),
torchvision.transforms.ToTensor()])
image = transform(image)
print(image.shape)

class CNN(nn.Module):
def __init__(self):
super(CNN, self).__init__()
self.model=nn.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(1024,64),
nn.Linear(64,10)
)

def forward(self,x):
x = self.model(x)
return x

# 因为train文件里面我没有保存模型,所以这里我是没法运行的,要先运行train文件
# 采用GPU训练的模型,在CPU加载的过程中,要映射到CPU上
model = torch.load("cnn_9.pth",map_location=torch.device('cpu'))
# model的输入要是4维的
torch.reshape(image,(1,3,32,32))
# 记得写上
model.eval()
with torch.no_grad():
output = model(image)

print(output.argmax(1))