当前位置: 首页 > news >正文

PyTorch | 学习笔记4

一.认识Pytorch

1.什么是Pytorch

Pytorch是一个基于Numpy的科学计算包,向它的使用者提供了两大功能

①作为Numpy的替代者,向用户提供使用GPU强大功能的能力

②作为一款深度学习的平台,向用户提供最大的灵活性和速度

2.基本元素操作

Tensors张量:类似Numpy中的ndarray数据结构,最大的区别在于Tensor可以利用GPU的加速功能

import torch
#创建矩阵的操作
#创建一个没有初始化的矩阵
x = torch.empty(5,3)
#创建一个有初始化的矩阵
x = torch.rand(5,3)
#当声明一个未初始化的矩阵时,它本身不包含任何确切的值
#当创建一个未初始化的矩阵时,分配给矩阵的内存中有什么数值就赋值给了这个矩阵,本质是毫无意义的数据
#创建一个全零矩阵并可指定数据元素的类型为long
x = torch.zeros(5,3,dtype=torch.long)
#直接通过数据创建张量
x = torch.tensor([2.5,3.5])
#通过已有的一个张量创建相同尺寸的新张量
#利用news_methods方法得到一个张量
x = x.new_ones(5,3,dtype=torch.double)
#利用randn_like方法得到相同张量尺寸的一个新张量,并且采用随机初始化来对其赋值
y = torch.randn_like(x,dtype=torch.float)
#得到张量的尺寸
print(x.size())
#torch.size函数本质上返回的是一个tuple,因此它支持一切元组的操作

3.基本运算操作

#加法操作
x = torch.rand(5,3)
y = torch.rand(5,3)
print(x+y)
print(torch.add(x,y))
#第三种加法方式:提前设定一个空的张量,然后将空的张量作为加法的结果存储张量
result = torch.empty(5,3)
torch.add(x,y,out=result)
print(result)
#第四种加法方式:in-place(原地置换)
#所有in-place的操作函数都有一个下划线的后缀
y.add_(x)
#用类似Numpy的方式对张量进行操作
print(x[:,1])
#改变张量的形状:torch.view(),需要保证数据元素的总数量不变
x = torch.randn(4,4)
y = x.view(16)
#-1代表自动匹配个数
z = x.view(-1,8)
print(x.size(),y.size(),z.size())
#如果张量中只有一个元素,可以用.item()将值取出,作为一个python number
x = torch.randn(1)
print(x.item())

4.Torch Tensor和Numpy array之间的相互转换

#torch tensor和numpy array共享底层的内存空间,因此改变其中一个值,另一个值也会随之改变
a = torch.ones(5)
#将torch tensor转换为numpy array
b = a.numpy()
#对其中一个进行加法操作,另一个也随之被改变
a.add_(1)

#将numpy array转换为torch tensor
import numpy as np
a = np.ones(5)
b = torch.from_numpy(a)
np.add(a,1,out=a)
print(a)
print(b)
#注意:所有在CPU上的Tensors,除了CharTensor,都可以转换为Numpy array并可以反向转换

#关于cuda tensor:tensors可以用.to()方法来将其移动到任意设备上
#如果服务器上已经安装了GPU和CUDA
if torch.cuda.is_available():
  #定义一个设备对象,这里指定成cuda,即使用GPU
  device = torch.device("cuda")
  #直接在GPU上创建一个Tensor
  y = torch.ones_like(x,device=device)
  #将在CPU上面的x张量移动到GPU上面
  x = x.to(device)
  #x和y都在GPU上面,才能支持加法运算
  z = x + y
  #此处的张量z在GPU上面
  print(z)
  #也可以将z转移到CPU上面,并同时指定张量元素的数据类型
  print(z.to("cpu",torch.double))

二.Pytorch中的autograd

在整个Pytorch框架中,所有的神经网络本质上都是一个autograd package(自动求导工具包)

autograd package提供了一个对tensors上所有的操作进行自动微分的功能

1.torch.Tensor

torch.Tensor是整个package中的核心类,如果将属性requires_grad设置为true,它将追踪在这个类上定义的所有操作。当代码要进行反向传播的时候,直接调用.backward()就可以自动计算所有的梯度。在这个Tensor上所有梯度将被累加进属性.grad中

如果想终止一个Tensor在计算图中的追踪回溯,只需要执行.detach()就可以将该Tensor从计算图中撤下,在未来的回溯计算中也不会再计算该Tensor

除了.detach(),如果想终止对计算图的回溯,也就是不再进行方向传播求导数的过程,也可以采用代码块的方式with torch.no_grad(),这种方式非常适用于对模型进行预测的时候,因为预测阶段不再需要对梯度进行计算

关于torch.Function:Function类和Tensor类同等重要的一个核心类,它和Tensor共同构建了一个完整的类,每一个Tensor拥有一个.grad_fn属性,代表引用了哪个具体的Function创建了该Tensor;如果某个张量Tensor是用户自定义的,则其对应的grad_fn is None

2.Tensor的操作

x1 = torch.ones(3,3)
x = torch.ones(2,2,requires_grad=True)
#在具有requires_grad=True的Tensor上执行一个加法操作
y = x + 2
#打印Tensor的grad_fn属性
print(x.grad_fn)
print(y.grad_fn)
#在Tensor上执行更复杂的操作
z = y*y*3
out = z.mean()
print(z,out)
#关于方法requires_grad_():该方法可以原地改变Tensor的属性requires_grad的值,如果没有主动设定默认#为False
a = torch.randn(2,2)
a = ((a*3)/(a-1))
print(a.requires_grad)
a.requires_grad_(True)
print(a.requires_grad)
b = (a*a).sum()
print(b.grad_fn)

3.梯度Gradients

#在pytorch中,反向传播是依靠backward()实现的
out.backward()
print(x.grad)
#关于自动求导的属性设置:可以通过设置requires_grad=True来执行自动求导
#也可以通过代码块的限制来停止自动求导
print(x.requires_grad)
print((x**2).requires_grad)
with torch.no_grad():
  print((x**2).requires_grad)
#可以通过detach()获得一个新的Tensor,拥有相同的内容但不需要自动求导
print(x.requires_grad)
y = x.detach()
print(y.requires_grad)
print(x.eq(y).all())

三.使用Pytorch构建一个神经网络

torch.nn:使用Pytorch来构建神经网络,主要的工具都在torch.nn包中;nn依赖于autograd来定义模型,并对其自动求导

构建神经网络的典型流程:

①定义一个拥有可学习参数的神经网络

②遍历训练数据集

③处理输入数据使其流经神经网络

④计算损失值

⑤将网络参数的梯度进行反向传播

⑥以一定的规则更新网络的权重

首先定义一个Pytorch实现的神经网络

#导入若干工具包
import torch
import torch.nn as nn
import torch.nn.functional as F

#定义一个简单的网络类
class Net(nn.Module):

  def __init__(self):
    super(Net,self).__init__()
    #定义第一层卷积神经网络,输入通道维度=1,输出通道维度=6,卷积核大小3*3
    self.conv1 = nn.Conv2d(1,6,3)
    #定义第二层卷积神经网络,输入通道维度=6,输出通道维度=16,卷积核大小3*3
    self.conv2 = nn.Conv2d(6,16,3)
    #定义三层全连接网络
    self.fc1 = nn.Linear(16*6*6,120)
    self.fc2 = nn.Linear(120,84)
    self.fc3 = nn.Linear(84,10)

  def forward(self,x):
    #在(2,2)的池化窗口下执行最大池化操作
    x = F.max_pool2d(F.relu(self.conv1(x)),(2,2))
    x = F.max_pool2d(F.relu(self.conv2(x)),2)
    x = x.view(-1,self.num_flat_feature(x))
    x = F.relu(self.fc1(x))
    x = F.relu(self.fc2(x))
    x = self.fc3(x)
    return x

  def num_flat_feature(self,x):
    #计算size,除了第0个维度上的batch_size
    size = x.size()[1:]
    num_features = 1
    for s in size:
      num_features *= s
    return num_features

net = Net()
print(net)

注意:模型中所有的可训练参数,可以通过net.parameters()来获得

params = list(net.parameters())
print(len(params))
print(params[0].size())

 假设图像的输入尺寸为32*32

input = torch.randn(1,1,32,32)
out = net(input)
print(out)

有了输出张量后,就可以执行梯度归零和反向传播的操作了

net.zero_grad()
out.backward(torch.randn(1,10))

注意: torch.nn构建的神经网络只支持mini-batchs的输入,不支持单一样本的输入

1.损失函数

损失函数的输入是一个输入的pair:(output,target),然后计算出一个数值来评估output和target之间的差距大小

在torch.nn中有若干不同的损失函数可供使用,比如nn.MSELoss就是通过计算均方差损失来评估输入和目标之间的差距

应用nn.MSELoss计算损失的例子

output = net(input)
target = torch.randn(10)

#改变target的形状为二维张量,为了和output匹配
target = target.view(1,-1)
criterion = nn.MSELoss()

loss = criterion(output,target)
print(loss)

关于方向传播的链条:如果我们跟踪loss反向传播的方向,使用grad_fn属性打印,将可以看到一张完整的计算图:input → conv2d → relu → maxpool2d → conv2d → relu → maxpool2d → view → linear → relu → linear → relu → linear → MSELoss → loss

当调用loss.backward()时,整张计算图将对loss进行自动求导,所有属性requires_grad=True的Tensors都将参与梯度求导的运算,并将梯度累加到Tensors中的.grad属性中

print(loss.grad_fn) #MSELoss
print(loss.grad_fn.next_function[0][0]) #Linear
print(loss.grad_fn.next_function[0][0].next_functions[0][0]) #ReLU

2.反向传播

在Pytorch中执行反向传播非常简便,全部操作就是loss.backward()

在执行反向传播之前,要先将梯度清零,否则梯度会在不同的批次数据之间被累加

#梯度清零
net.zero_grad()
print(net.conv1.bias.grad)
#反向传播
loss.backward()
print(net.conv1.bias.grad)

3.更新网络参数

更新参数最简单的算法就是SGD(随机梯度下降)

具体算法公式表达式:weight = weight - learning_rate * gradient

首先,用传统的Python代码实现SGD

learning_rate = 0.01
for f in net.parameters():
  f.data.sub_(f.grad.data*learning_rate)

然后使用Pytorch官方推荐的标准代码 

#首先导入优化器的包,optim中包含若干常用的优化算法,比如SGD,Adam等
import torch.optim as optim

#通过optim创建优化器对象
optimizer = optim.SGD(net.parameters(),lr=0.01)

#将优化器执行梯度清零的操作
optimizer.zero_grad()

output = net(input)
loss = criterion(output,target)

#对损失值执行反向传播的操作
loss.backward()
#参数的更新通过一行标准代码来执行
optimizer.step()

四.使用Pytorch构建一个分类器

1.分类器任务和数据介绍

构造一个将不同图像进行分类的神经网络分类器,对输入的图片进行判别并完成分类

本案例采用CIFAR10数据集作为原始图片数据

2.训练分类器的步骤

①使用torchvision下载CIFAR10数据集

import torch
import torchvision
import torchvision.transforms as transforms

#下载数据集并对图片进行调整,因为torchvision数据集的输出是PILImage格式,数据域在[0,1]
#我们将其转换为标准数据域[-1,1]的张量格式
transform = transforms.Compose(
  [transforms.ToTensor(),
   transforms.Normalize((0.5,0.5,0.5),(0.5,0.5,0.5))])

trainset = torchvision.datasets.CIFAR10(root='./data',train=True,
                                        download=True,transform=transform)
trainloader = torch.utils.data.DataLoader(trainset,batch_size=4,shuffle=True,num_workers=2)

testset = torchvision.datasets.CIFAR10(root='./data',train=False,
                                       download=True,transforms=transform)
testloader = torch.utils.data.DataLoader(testset,batch_size=4,shuffle=False,num_workers=2)

classes = ('plane','car','bird','cat','deer','dog','frog','horse','ship','truck')

#展示若干训练集的图片
import matplotlib.pyplot as plt
import numpy as np

#构建展示图片的函数
def imshow(img):
  img = img/2 + 0.5
  mpimg = img.numpy()
  plt.imshow(np.transpose(i=npimg,(1,2,0)))
  plt.show()

#从数据迭代器中读取一张图片
dataiter = iter(trainloader)
images,labels = dataiter.next()

#展示图片
imshow(torchvision.utils.make_grid(images))
#打印标签label
print(''.join('%5s' % classes[label[j]] for j in range(4)))

②定义卷积神经网络

import torch.nn as nn
import torch.nn.functional as F

class Net(nn.Module):
  def __init__(self):
    super(Net,self).__init__()
    self.conv1 = nn.Conv2d(3,6,5)
    self.pool = nn.MaxPool2d(2,2)
    self.conv2 = nn.Conv2d(6,16,5)
    self.fc1 = nn.Linear(16*5*5,120)
    self.fc2 = nn.Linear(120,84)
    self.fc3 = nn.Linear(84,10)

  def forward(self,x):
    x = self.pool(F.relu(self.conv1(x)))
    x = self.pool(F.relu(self.conv2(x)))
    x = x.view(-1,16*5*5)
    x = F.relu(self.fc1(x))
    x = F.relu(self.fc2(x))
    x = self.fc3(x)
    return x

net = Net()

③定义损失函数

采用交叉熵损失函数和随机梯度下降优化器

import torch.optim as optim

criterion = nn.CrossEntropyLoss()
optimizer = optim.SGD(net.parameters(),lr=0.001,momentum=0.9)

④在训练集上训练模型

采用基于梯度下降的优化算法,都需要很多个轮次的迭代训练

for epoch in range(2):

  running_loss = 0.0
  for i,data in enumerate(trainloader,0):
    #data中包含输入图像张量inputs,标签张量labels
    inputs,labels = data

    #首先将优化器梯度归零
    optimizer.zero_grad()

    #输入图像张量进网络,得到输出张量outputs
    outputs = net(inputs)

    #利用网络的输出outputs和标签labels计算损失值
    loss = criterion(outputs,labels)

    #反向传播+参数更新,是标准代码的标准流程
    loss.backward()
    optimizer.step()

    #打印轮次和损失值
    running_loss += loss.item()
    if (i+1) % 2000 == 0:
      print('[%d,%5d] loss:%.3f' % (epoch + 1,i + 1,running_loss / 2000))

print('Finish Training')

⑤在测试集上测试模型

第一步:展示测试集中的若干图片

dataiter = iter(testloader)
images,labels = dataiter.next()

#打印原始图片
imshow(torchvision.utils.make_grid(images))
#打印真实的标签
print('GroundTruth:',''.join('%5s' % classes[labels[j]] for j in range(4)))

第二步:加载模型并对测试图片进行预测

#首先实例化模型的类对象
net = Net()
#加载训练阶段保存好的模型的状态字典
net.load_state_dict(torch.load(PATH))

#利用模型对图片进行预测
outputs = net(images)

#共有10个类别,采用模型计算出的概率最大的作为预测的类别
_,predicted = torch.max(outputs,1)

#打印预测标签的结果
print('predicted:',''.join('%5s' % classes[predicted[j]] for j in range(4)))

 接下来看一下在全部测试集上的表现

correct = 0
total = 0
with torch.no_grad():
  for data in testloader:
    images,labels = data
    outputs = net(images)
    _,predicted = torch.max(outputs.data,1)
    total += labels.size(0)
    correct += (predicted == labels).sum().item()

print('accuracy of the network on the 10000 test images:%d %%' % (100 * correct / total))

为了更加细致的看一下模型在哪些类别上表现更好,在哪些类别上表现更差,我们分类别地进行准确率计算

class_correct = list(0. for i in range(10))
class_total = list(0. for i in range(10))
with torch.no_grad():
  for data in testloader:
    images,labels = data
    outputs = net(images)
    _,predicted = torch.max(outputs,1)
    c = (predicted == labels).squeeze()
    for i in range(4):
      label = labels[i]
      class_correct[label] += c[i].item()
      class_total[label] += 1

for i in range(10):
  print('accuracy of %5s:%2d %%' % (classes[i],100*class_correct[i]/class_total[i]))

3.在GPU上训练模型

为了真正利用Pytorch中Tensor的优秀属性加速模型的训练,我们可以将训练过程转移到GPU上进行

首先定义设备,如果CUDA是可用的则被定义成GPU,否则被定义为CPU

device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")
print(device)

当训练模型的时候,只需要将模型转移到GPU上,同时将输入的图片和标签页转移到GPU上即可

#将模型转移到GPU上
net.to(device)

#将输入的图片张量和标签张量转移到GPU上
inputs,labels = data[0].to(device),data[1].to(device)

相关文章:

  • 网站建设制作及推广/网站名称查询
  • 佛山最好的网站建设/泉州百度关键词优化
  • 聊城网站建设哪个好些/电话营销销售系统
  • wordpress需要Apache吗/seo排名赚app
  • 自己可以做网站放在百度上面嘛/seo优化步骤
  • 邢台疫情最新规定/seo公司推广
  • 后台执行linux命令
  • Web前端:广州蓝景实训
  • 【TypeScript】TS入门(一)
  • 以技术创新践行社会责任,欧科云链斩获界面新闻年度双项大奖
  • 通过API远程调用Hue之一——登录和登出
  • JavaSE第6篇:面向对象上
  • 【运维有小邓】AD域权限报表
  • hutool工具并发使用 Sftp sftp = new Sftp(sshHost, sshPort, sshUser, sshPass) 的坑
  • 【图像处理】基于二维FIR的特定角度边缘检测(Matlab代码实现)
  • 麒麟水乡,IU酒店进驻云南旅游咽喉之地曲靖
  • 花房集团上市,走向元宇宙新征程
  • Symfony3.4 如何自定义编写Twig扩展