PyTorch | 学习笔记5

一.数据操作

在PyTorch中,torch.Tensor是存储和变换数据的主要工具

1.创建tensor

#创建一个5*3的未初始化的tensor
x = torch.empty(5,3)
#创建一个5*3的随机初始化的tensor
x = torch.rand(5,3)
#创建一个5*3的long型全0的tensor
x = torch.zeros(5,3,dtype=torch.long)
#直接根据数据创建
x = torch.tensor([5,3])

2.操作

#算术操作
print(torch.add(x,y))
#索引:索引出来的结果与原数据共享内存,即修改一下,另一个也会跟着修改
y = x[0,:]
y += 1
print(y)
print(x[0,:]) #源tensor也被改了
#改变形状
y = x.view(15)
z = x.view(-1,5) # -1所指的维度可以根据其他维度的值推出来
print(x.size(),y.size(),z.size())

3.广播机制

  • 先适当复制元素使这两个tensor形状相同后再按元素运算
x = torch.arange(1,3).view(1,2)
y = torch.arange(1,4).view(3,1)
print(x + y)

4.tensor和numpy相互转换

#tensor转numpy
a = torch.ones(5)
b = a.numpy()
print(a,b)

#numpy转tensor
import numpy as np
a = np.ones(5)
b = torch.from_numpy(a)
print(a,b)

5.tensor on GPU

if torch.cuda.is_available():
  device = torch.device("cuda") #GPU
  y = torch.ones_like(x,device=device) #直接创建一个在GPU上的
  x = x.to(device) #等价于 .to("cuda")
  z = x + y
  print(z)
  print(z.to("cpu",torch.double)) #to()还可以同时更改数据类型

6.自动求梯度

  • 如果将tensor的属性 .requires_grad设置为True,它将开始追踪在其上的所有操作,完成计算后,可以调用 .backward()来完成所有梯度计算,此tensor的梯度将累积到 .grad属性中
  • 如果不想继续追踪,可以调用 .detach() 将其从追踪记录中分离出来,这样就可以防止将来的计算被追踪,这样梯度就传不过去了
  • 还可以用with torch.no_grad() 将不想被追踪的操作代码块包裹起来,这种方法在评估模型的时候很常用,因为在评估模型时,我们并不需要计算可训练参数(requires_grad = True)的梯度
  • function是另外一个很重要的类
  • tensor和function互相结合就可以构建一个记录有整个计算过程的有向无环图
  • 每个tensor都有一个 .grad_fn属性,该属性即创建该tensor的function,就是说tensor是不是通过某些运算得到的,若是,则grad_fn返回一个与这些运算相关的对象,否则None

二.线性回归

线性回归输出是一个连续值,因此适用于回归问题

1.读取数据

  • 由于data常用作变量名,我们将导入的data模块用Data代替
  • 在每一次迭代中,我们将随机读取包含10个数据样本的小批量
import torch.utils.data as Data

batch_size = 10
#将训练数据的特征和标签组合
dataset = Data.TensorDataset(features,labels)
#随机读取小批量
data_iter = Data.DataLoader(dataset,batch_size,shuffle=True)

2.定义模型

  • 导入torch.nn模块,该模块定义了大量神经网络的层
  • nn就是利用autograd来定义模型,nn的核心数据结构是Module,它既可以表示神经网络中的某个层,也可以表示一个包含很多层的神经网络
  • 在实际使用中,最常见的做法是继承nn.Module,撰写自己的网络/层
  • 一个nn.Module实例应该包含一些层以及返回输出的前向传播(forward)方法
class LinearNet(nn.Module):
  def __init__(self,n_feature):
    super(LinearNet,self).__init__()
    self.linear = nn.Linear(n_feature,1)
  #forward 定义前向传播
  def forward(self,x):
    y = self.linear(x)
    return y

net = LinearNet(num_inputs)
print(net) #使用print可以打印出网络的结构

3.初始化模型参数

  • 我们通过init.normal_将权重参数每个元素初始化为随机采样于均值为0,标准差为0.01的正态分布,偏差初始化为0
from torch.nn import init

init.normal_(net[0].weight,mean=0,std=0.01)
init.constant_(net[0].bias,val=0)

4.定义损失函数

  • PyTorch在nn模块中提供了各种损失函数,这些损失函数可以看作是一种特殊的层
  • PyTorch也将这些损失函数实现为nn.Module的子类
loss = nn.MSELoss()

5.定义优化算法

  • torch.optim模块提供了很多常用的优化算法
  • 下面我们创建一个用于优化net所有参数的优化器实例,并指定学习率为0.03的小批量随机梯度下降(SGD)为优化算法
import torch.optim as optim

optimizer = optim.SGD(net.parameters(),lr=0.03)
print(optimizer)

6.训练模型

  • 我们通过调用optim实例的step函数来迭代模型参数
  • 按照小批量随机梯度下降的定义,我们在step函数中指明批量大小,从而对批量中样本梯度求平均
num_epoches = 3
for epoch in range(1,num_epochs + 1):
  for x,y in data_iter:
    output = net(x)
    l = loss(output,y.view(-1,1))
    optimizer.zero_grad()
    l.backward()
    optimizer.step()
  print('spoch %d,loss: %f' % (epoch,l.item())

三.softmax回归

softmax回归的输出单元从一个变成了多个,且引入了softmax运算使输出更适合离散值的预测和训练

1.定义和初始化模型

  • softmax回归的输出层是一个全连接层,所以我们用一个线性模块就可以了
  • 因为前面我们的数据返回的每个batch样本x的形状为(batch_size,1,28,28),所以我们要先用view()将x的形状转换成(batch_size,784)才送入全连接层
num_inputs = 784
num_outputs = 10

class LinearNet(nn.Module):
  def __init__(self,num_inputs,num_outputs):
    super(LinearNet,self).__init__()
    self.linear = nn.Linear(num_inputs,num_outputs)
  def forward(self,x):
    y = self.linear(x.view(x.shape[0],-1))
    return y

net = LinearNet(num_inputs,num_outputs)

#使用均值为0,标准差为0.01的正态分布随机初始化模型的权重参数
init.normal_(net.linear.weight,mean=0,std=0.01)
init.constant_(net.linear.bias,val=0)

2.softmax和交叉熵损失函数

  • 分开定义softmax运算和交叉熵损失函数可能会造成数值不稳定
  • 因此,PyTorch提供了一个包括softmax运算和交叉熵损失计算的函数,它的数值稳定性更好
loss = nn.CrossEntropyLoss()

3.定义优化算法

optimizer = torch.optim.SGD(net.parameters(),lr=0.1)

4.训练模型

  • 在训练模型时,迭代周期num_epochs和学习率lr都是可以调的超参数,改变它们的值可能会得到分类更准确的模型
num_epochs,lr = 5,0.1

def train_ch3(net,train_iter,test_iter,loss,num_epochs,batch_size,params=None,lr=None,
              optimizer=None):
  for epoch in range(num_epochs):
    train_l_sum,train_acc_sum,n = 0.0,0.0,0
    for x,y in train_iter:
      y_hat = net(x)
      l = loss(y_hat,y).sum()

      #梯度清零
      if optimizer is not None:
        optimizer.zero_grad()
      elif params is not None and params[0].grad is not None:
        for param in params:
          param.grad.data.zero_()

      l.backward()
      if optimizer is None:
        d2l.sgd(params,lr,batch_size)
      else:
        optimizer.step()

      train_l_sum += l.item()
      train_acc_sum += (y_hat.argmax(dim=1) == y).sum().item()
      n += y.shape[0]
    test_acc = evaluate_accuracy(test_iter,net)
    print('epoch %d,loss %.4f,train acc %.3f,test acc %.3f' % 
          (epoch + 1,train_l_sum/n,train_acc_sum/n,test_acc))

train_ch3(net,train_iter,test_iter,cross_entropy,num_epochs,batch_size,[w,b],lr)

四.多层感知机

多层感知机在单层神经网络的基础上引入了一到多个隐藏层,隐藏层位于输入层和输出层之间

1.定义模型

  • 我们多加了一个全连接层作为隐藏层,它的隐藏单元个数为256,并使用ReLU函数作为激活函数
num_inputs,num_outputs,num_hiddens=784,10,256

net = nn.Sequential(
      d2l.FlattenLayer(),
      nn.Linear(num_inputs,num_hiddens),
      nn.ReLU(),
      nn.Linear(num_hiddens,num_outputs),
      )

for params in net.parameters():
  init.normal_(params,mean=0,std=0.01)

2.读取数据并训练模型

  • 使用与训练softmax回归几乎相同的步骤来读取数据并训练模型
batch_size = 256
train_iter,test_iter =d2l.load_data_fashion_mnist(batch_size)
loss = torch.nn.CrossEntropyLoss()

optimizer = torch.optim.SGD(net.parameters(),lr=0.5)

num_epochs = 5
d2l.train_ch3(net,train_iter,test_iter,loss,num_epochs,batch_size,None,None,optimizer)

五.模型构造 

1.继承Module类来构造模型

  • Module类时nn模块里提供的一个模型构造类,是所有神经网络模块的基类,我们可以继承它来定义我们想要的模型
  • 下面继承Module类来构造多层感知机
import torch
from torch import nn

#MLP类中无需定义反向传波函数,系统将通过自动求梯度而自动生成反向传播所需的backward函数
class MLP(nn.Module):
  #声明带有模型参数的层,这里声明了两个全连接层
  def __init__(self,**kwargs):
    #调用MLP父类block的构造函数来进行必要的初始化,这样在构造实例时还可以指定其他函数
    super(MLP,self).__init__(**kwargs)
    self.hidden = nn.Linear(784,256) #隐藏层
    self.act = nn.ReLU()
    self.output = nn.Linear(256,10) #输出层

  #定义模型的前向计算,即如何根据输入x计算返回所需要的模型输出
  def forward(self,x):
    a = self.act(self.hidden(x))
    return self.output(a)

2.Module的子类

Sequential类

  • 当模型的前向计算为简单串联各个层的计算时,Sequential类可以通过更加简单的方式定义模型
  • Sequential类的目的:它可以接收一个子模块的有序字典或者一系列子模块作为参数来逐一添加Module的实例,而模型的前向计算就是将这些实例按添加的顺序逐一计算
#下面实现一个与Sequential类有相同功能的MySequential类
#可以帮助我们更加清晰地理解Sequential的工作机制
class MySequential(nn.Module):
  from collections import OrderedDict
  def __init__(self,*args):
    super(MySequential,self).__init__()
    if len(args) == 1 and isinstance(args[0],OrderedDict):
      for key,module in args[0].items():
        self.add_module(key,module)
    else:
      for idx,module in enumerate(args):
        self.add_module(str(idx),module)
  def forward(self,input):
    for module in self._modules.values():
      input = module(input)
    return input

#我们用MySequential类来实现前面描述的MLP类,并使用随机初始化的模型做一次前向计算
net = MySequential(
        nn.Linear(784,256),
        nn.ReLU(),
        nn.Linear(256,10),
        )
print(net)
net(x)

ModuleList类

  • 接收一个子模块的列表作为输入,然后也可以类似list那样进行append和extend操作
net = nn.ModuleList([nn.Linear(784,256),nn.ReLU()])
net.append(nn.Linear(256,10)) #类似list的append操作
print(net[-1]) #类似list的索引访问
print(net)

ModuleDict类

  • 接收一个子模块的字典作为输入,然后也可以类似字典那样进行添加访问操作
net = nn.ModuleDict({
  'linear':nn.Linear(784,256),
  'act':nn.ReLU(),
})
net['output'] = nn.Linear(256,10) #添加
print(net['linear']) #访问
print(net.output)
print(net)

3.构造复杂的模型

class FancyMLP(nn.Module):
  def __init__(self,**kwargs):
    super(FancyMLP,self).__init__(**kwargs)

    self.rand_weight = torch.rand((20,20),requires_grad=False) #不可训练参数(常数参数)
    self.linear = nn.Linear(20,20)

  def forward(self,x):
    x = self.linear(x)
    #使用创建的常数参数,以及nn.functional中的relu函数和mm函数
    x = nn.functional.relu(torch.mm(x,self.rand_weight.data) + 1)
    
    #复用全连接层,等价于两个全连接层共享参数
    x = self.linear(x)
    #控制流,这里我们需要调用item函数来返回标量进行比较
    while x.norm().item() > 1:
      x /= 2
    if x.norm().item() < 0.8:
      x *= 10
    return x.sum()

你可能感兴趣的:(深度学习,深度学习,pytorch,学习)