热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

Pytorch学习笔记1

文本为学习pytorch的笔记,教程为pytorch教程1.Tensor张量1.1构建5*3矩阵,未初始化from__future__importprint_functionimp

文本为学习pytorch的笔记,教程为pytorch教程

1. Tensor张量

1. 1 构建5*3矩阵,未初始化

from __future__ import print_function
import torch
x = torch.Tensor(5, 3)
print(x)
tensor([[0.0000e+00, 0.0000e+00, 0.0000e+00],
[0.0000e+00, 0.0000e+00, 0.0000e+00],
[0.0000e+00, 0.0000e+00, 0.0000e+00],
[0.0000e+00, 6.9504e-43, 0.0000e+00],
[0.0000e+00, 3.7772e-39, 0.0000e+00]])

1.2 构建一个随机初始化的矩阵

x = torch.rand(5, 3)
print(x)
tensor([[0.4936, 0.9939, 0.3484],
[0.7368, 0.4310, 0.0223],
[0.6139, 0.4587, 0.0168],
[0.1365, 0.8824, 0.7639],
[0.3669, 0.0629, 0.0108]])

1.3 获得张量的size,torch.Size实际上是一个 tuple(元组), 所以它支持所有 tuple(元组)的操作

print(x.size())

2. Tensor张量的操作

2.1 加法

y = torch.rand(5, 3)
print(x + y)
print(torch.add(x, y))

提供输出tensor作为参数,输出为result

result = torch.Tensor(5, 3)
torch.add(x, y, out = result)
print(result)

加法,就地操作(in-place)

# adds x to y
y.add_(x)
print(y)

注解:任何改变张量的操作方法都是以后缀 _ 结尾的. 例如: x.copy_(y), x.t_(), 将改变张量 x.

你可以用类似Numpy的索引来处理所有的张量!

print(x[:, 1])

2.2 改变张量大小,torch.view

x = torch.randn(4, 4)
y = x.view(16)
z = x.view(-1, 8) # the size -1 is inferred from other dimensions
print(x.size(), y.size(), z.size())
torch.Size([4, 4])
torch.Size([16])
torch.Size([2, 8])

3. Numpy Bridge

torch tensor和numpy array互相转变,torch tensor和numpy array共享它们实际的内存位置,改变一个另一个也会改变

3.1 torch tensor 转换为 numpy array

a = torch.ones(5)
print(a)
b = a.numpy()
print(b)

查看numpy数组有没有给变

a.add_(1)
print(a)
print(b)

3.2 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)

注意:除了 CharTensor 之外, CPU 上的所有 Tensor 都支持与Numpy进行互相转换

3.3 CUDA Tensors

可以使用.cuda方法将 Tensors 在GPU上运行

# 只要在 CUDA 是可用的情况下, 我们可以运行这段代码
if torch.cuda.is_available():
x = x.cuda()
y = y.cuda()
x + y

4. 自动求导

PyTorch 中所有神经网络的核心是 autograd 自动求导包. 我们先来简单介绍一下, 然后我们会去训练我们的第一个神经网络.

autograd 自动求导包针对张量上的所有操作都提供了自动微分操作. 这是一个逐个运行的框架, 这意味着您的反向传播是由您的代码如何运行来定义的, 每个单一的迭代都可以不一样

4.1 Variable变量

autograd.Variable 是包的核心类. 它包装了张量, 并且支持几乎所有的操作. 一旦你完成了你的计算, 你就可以调用 .backward() 方法, 然后所有的梯度计算会自动进行.

你还可以通过 .data 属性来访问原始的张量, 而关于该 variable(变量)的梯度会被累计到 .grad 上去

《Pytorch学习笔记1》
Variable

还有一个针对自动求导实现来说非常重要的类 – Function.

VariableFunction 是相互联系的, 并且它们构建了一个非循环的图, 编码了一个完整的计算历史信息. 每一个 variable(变量)都有一个 .grad_fn 属性, 它引用了一个已经创建了 VariableFunction (除了用户创建的 Variable 之外 – 它们的 grad_fnNone ).

如果你想计算导数, 你可以在 Variable 上调用 .backward() 方法. 如果 Variable 是标量的形式(例如, 它包含一个元素数据), 你不必指定任何参数给 backward(), 但是, 如果它有更多的元素. 你需要去指定一个 grad_output 参数, 该参数是一个匹配 shape(形状)的张量.

import torch
from torch.autograd import Variable

创建 variable(变量):

x = Variable(torch.ones(2, 2), requires_grad = True)
print(x)

variable(变量)的操作:

y = x + 2
print(y)

y由操作创建,所以它有grad_fn属性.

print(y.grad_fn)

y 的更多操作

z = y * y * 3
out = z.mean()
print(z, out)

4.2 梯度

我们现在开始了解反向传播, out.backward() 与 out.backward(torch.Tensor([1.0])) 这样的方式一样

out.backward()

但因 d(out)/dx 的梯度

print(x.grad)

你可以使用自动求导来做很多有趣的事情

x = torch.randn(3)
x = Variable(x, requires_grad = True)
y = x * 2
while y.data.norm() <1000:
y = y * 2
print(y)

5. 神经网络

神经网络torch.nn包构建

autograd实现了反向传播功能, 但是直接用来写深度学习的代码在很多情况下还是稍显复杂,torch.nn是专门为神经网络设计的模块化接口.nn构建于Autograd之上, 可用来定义和运行神经网络.nn.Modulenn中最重要的类, 可把它看成是一个网络的封装, 包含网络各层定义以及forward方法, 调用forward(input)方法, 可返回前向传播的结果

可以用分类数字图像网络:

《Pytorch学习笔记1》
《Pytorch学习笔记1》

convnet

这是一个基础的前向传播(feed-forward)网络: 接收输入, 经过层层传递运算, 得到输出.

一个典型的神经网络训练过程如下:

  • 定义具有一些可学习参数(或权重)的神经网络
  • 迭代输入数据集
  • 通过网络处理输入
  • 计算损失(输出的预测值与实际值之间的距离)
  • 将梯度传播回网络
  • 更新网络的权重, 通常使用一个简单的更新规则: weight = weight - learning_rate * gradient

5.1 定义网络

import torch
from torch.autograd import Variable
import torch.nn as nn
import torch.nn.functional as F
class Net(nn.Module):
def __init__(self):
super(Net, self).__init__()
# 卷积层 '1'表示输入图片为单通道, '6'表示输出通道数, '5'表示卷积核为5*5
# 核心
self.conv1 = nn.Conv2d(1, 6, 5)
self.conv2 = nn.Conv2d(6, 16, 5)
# 仿射层/全连接层: y = Wx + b
self.fc1 = nn.Linear(16 * 5 * 5, 120)
self.fc2 = nn.Linear(120, 84)
self.fc3 = nn.Linear(84, 10)
def forward(self, x):
#在由多个输入平面组成的输入信号上应用2D最大池化.
# (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_features(x))
x = F.relu(self.fc1(x))
x = F.relu(self.fc2(x))
x = self.fc3(x)
return x
def num_flat_features(self, x):
size = x.size()[1:] # 除批量维度外的所有维度
num_features = 1
for s in size:
num_features *= s
return num_features
net = Net()
print(net)

你只要在nn.Module的子类中定义了forward函数,backward函数就会自动被实现(利用autograd). 在forward函数中可使用任何 Tensor 支持的操作.

网络的可学习参数通过
net.parameters()返回,
net.named_parameters可同时返回学习的参数以及名称.

params = list(net.parameters())
print(len(params))
print(params[0].size()) # conv1的weight

向前的输入是一个autograd.Variable, 输出也是如此. 注意: 这个网络(LeNet)的预期输入大小是 32&#215;32, 使用这个网上 MNIST 数据集, 请将数据集中的图像调整为 32&#215;32.

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

5.2 反向传播

将网络中所有参数的梯度清零.

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

注解:

torch.nn 只支持小批量(mini-batches), 不支持一次输入一个样本, 即一次必须是一个 batch.

例如, nn.Conv2d 的输入必须是 4 维的, 形如 nSamples x nChannels x Height x Width.

如果你只想输入一个样本, 需要使用 input.unsqueeze(0) 将 batch_size 设置为 1.

在继续之前, 让我们回顾一下迄今为止所有见过的类.
概括:

torch.Tensor &#8211; 一个
多维数组.

autograd.Variable &#8211;
包装张量并记录应用于其上的历史操作. 具有和
Tensor 相同的 API ,还有一些补充, 如
backward(). 另外
拥有张量的梯度.

nn.Module &#8211; 神经网络模块.
方便的方式封装参数, 帮助将其移动到GPU, 导出, 加载等.

nn.Parameter &#8211; 一种变量, 当被指定为
Model 的属性时, 它会自动注册为一个参数.

autograd.Function &#8211; 实现
autograd 操作的向前和向后定义 . 每个
Variable 操作, 至少创建一个
Function 节点, 连接到创建
Variable 的函数, 并
编码它的历史.

5.3 损失函数

损失函数采用 (output,target) 输入对, 并计算预测输出结果与实际目标的距离.

nn 包下有几种不同的 损失函数 . 一个简单的损失函数是: nn.MSELoss 计算输出和目标之间的均方误差,例如:

output = net(input)
target = Variable(torch.arange(1, 11)) # 一个虚拟的目标
criterion = nn.MSELoss()
loss = criterion(output, target)
print(loss)

5.4 反向传播

为了反向传播误差, 我们所要做的就是 loss.backward(). 你需要清除现有的梯度, 否则梯度会累加之前的梯度.

现在我们使用 loss.backward(), 看看反向传播之前和之后 conv1 的梯度

net.zero_grad() # 把之前的梯度清零
print('conv1.bias.grad before backward')
print(net.conv1.bias.grad)
loss.backward()
print('conv1.bias.grad after backward')
print(net.conv1.bias.grad)

5.5 更新网络的权重

实践中使用的最简单的更新规则是随机梯度下降( SGD ):

weight = weight &#8211; learning_rate * gradient

我们可以使用简单的 python 代码来实现这个:

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

然而, 当你使用神经网络时, 你需要使用各种不同的更新规则, 比如 SGD, Nesterov-SGD, Adam, RMSProp等. 为了实现这个功能, 我们建立了一个包:torch.optim实现所有这些方法. 使用它非常的简单:

import torch.optim as optim
# 新建一个优化器, 指定要调整的参数和学习率
optimizer = optim.SGD(net.parameters(), lr = 0.01)
# 在训练过程中:
optimizer.zero_grad() # 首先梯度清零(与 net.zero_grad() 效果一样)
output = net(input)
loss = criterion(output, target)
loss.backward()
optimizer.step() # 更新参数

注解:

观察如何使用手动设置梯度清零 optimizer.zero_grad() . 需要手动清零的原因在 Backprop_ 中已经说明了(梯度会累加之前的梯度)

6. 训练一个分类器

6.1 数据集合

一般来说, 当你不得不处理图像, 文本, 音频或者视频数据时, 你可以使用标准的 Python 包将数据加载到一个 numpy 数组中. 然后你可以将这个数组转换成一个torch.*Tensor

  • 对于图像, 会用到的包有 Pillow, OpenCV .
  • 对于音频, 会用的包有 scipy 和 librosa.
  • 对于文本, 原始 Python 或基于 Cython 的加载, 或者 NLTK 和 Spacy 都是有用的.

特别是对于vision, 我们已经创建了一个叫做torchvision, 其中有对普通数据集如 Imagenet, CIFAR10, MNIST 等和用于图像数据的转换器, 即torchvision.datasetstorch.utils.data.DataLoader

这提供了巨大的便利, 避免了编写重复代码

在本教程中, 我们将使用 CIFAR10 数据集. 它有: ‘airplane’, ‘automobile’, ‘bird’, ‘cat’, ‘deer’,‘dog’, ‘frog’, ‘horse’, ‘ship’, ‘truck’ 这些类别. CIFAR10 中的图像大小为 3x32x32 , 即 32&#215;32 像素的 3 通道彩色图像.

《Pytorch学习笔记1》
《Pytorch学习笔记1》

6.2 训练一个图像分类器

我们将按顺序执行以下步骤:

  1. 加载 CIFAR10 测试和训练数据集并规范化 torchvision
  2. 定义一个卷积神经网络
  3. 定义一个损失函数
  4. 在训练数据上训练网络
  5. 在测试数据上测试网络

1)加载并规范化 CIFAR10

使用torchvision, 加载 CIFAR10 非常简单

import torch
import torchvision
import torchvision.transforms as transforms

torchvision 数据集的输出是范围 [0, 1] 的 PILImage 图像. 我们将它们转换为归一化范围是[-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, transform=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')

2) 定义一个卷积神经网络

从神经网络部分复制神经网络, 并修改它以获取 3 通道图像(而不是定义的 1 通道图像).

from torch.autograd import Variable
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()

3) 定义损失函数和优化器

我们使用交叉熵损失函数( CrossEntropyLoss )和随机梯度下降( SGD )优化器.

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

4)训练网络

for epoch in range(2): # 循环遍历数据集多次
running_loss = 0.0
for i, data in enumerate(trainloader, 0):
# 得到输入数据
inputs, labels = data
# 包装数据
inputs, labels = Variable(inputs), Variable(labels)
# 梯度清零
optimizer.zero_grad()
# forward + backward + optimize
outputs = net(inputs)
loss = criterion(outputs, labels)
loss.backward()
optimizer.step()
# 打印信息
running_loss += loss.data[0]
if i % 2000 == 1999: # 每2000个小批量打印一次
print('[%d, %5d] loss: %.3f' %
(epoch + 1, i + 1, running_loss / 2000))
running_loss = 0.0
print('Finished Training')

5)在测试集合上面测试

在整个数据集合上面进行测试

correct = 0
total = 0
for data in testloader:
images, labels = data
outputs = net(Variable(images))
_, predicted = torch.max(outputs.data, 1)
total += labels.size(0)
correct += (predicted == labels).sum()
print('Accuracy of the network on the 10000 test images: %d %%' % (
100 * correct / total))

训练的准确率远比随机猜测(准确率10%)好, 证明网络确实学到了东西.

嗯, 我们来看看哪些类别表现良好, 哪些类别表现不佳:

class_correct = list(0. for i in range(10))
class_total = list(0. for i in range(10))
for data in testloader:
images, labels = data
outputs = net(Variable(images))
_, predicted = torch.max(outputs.data, 1)
c = (predicted == labels).squeeze()
for i in range(4):
label = labels[i]
class_correct[label] += c[i]
class_total[label] += 1
for i in range(10):
print('Accuracy of %5s : %2d %%' % (
classes[i], 100 * class_correct[i] / class_total[i]))

6.3 在GPU上面训练数据

就像你如何将一个张量传递给GPU一样, 你将神经网络转移到GPU上. 这将递归遍历所有模块, 并将其参数和缓冲区转换为CUDA张量:

net.cuda()

请记住, 您必须将输入和目标每一步都发送到GPU

inputs, labels = Variable(inputs.cuda()), Variable(labels.cuda())

如果发现在 GPU 上并没有比 CPU 提速很多, 实际上是因为网络比较小, GPU 没有完全发挥自己的真正实力

pytorch新手入门教程


推荐阅读
author-avatar
彼岸花芬芳
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有