❀❀❀❀❀❀❀❀❀❀❀❀❀❀❀❀❀❀❀❀❀❀❀❀❀❀❀❀❀❀❀
老版本、新版本我都看了,把一些知识点进行了融合
❀❀❀❀❀❀❀❀❀❀❀❀❀❀❀❀❀❀❀❀❀❀❀❀❀❀❀❀❀❀❀
import torch
import numpy as np
1. 直接从数据创建(类型是python的自带基本数据类型list
)
data = [[1, 2], [3, 4]]
x_data = torch.tensor(data)
print(type(data)) # list
print(type(x_data)) # torch.Tensor
2. 直接从 Numpy 数组中创建
np_array = np.array(data) # np.ndarray
x_np = torch.from_numpy(np_array) # torch.Tensor
# 或者直接写成 x_np = torch.tensor(np_array)也可以,但是就不能与tensor共享内存了,所以还是建议用torch.from_numpy()
3. 直接从另一个张量创建
新张量只保留参数张量的属性(形状、数据类型),而不是显式覆盖。
[start, end)
中随机选整数填充x_ones = torch.ones_like(x_data) # retains the properties of x_data
x_rand = torch.rand_like(x_data, dtype=torch.float) # overrides the datatype of x_data 验证 x_data 的数据类型
shape = (2, 3,)
rand_tensor = torch.rand(shape)
ones_tensor = torch.ones(shape)
zeros_tensor = torch.zeros(shape)
randint_tensor = torch.randint(low, high, shape)
torch.rand 和 torch.randn有什么区别(一个是均匀分布,一个是标准正态分布)
tensor = torch.rand(3, 4)
print(f"Shape of tensor: {tensor.shape}")
print(f"Datatype of tensor: {tensor.dtype}")
print(f"Device tensor is stored on: {tensor.device}")
# Shape of tensor: torch.Size([3, 4])
# Datatype of tensor: torch.float32
# Device tensor is stored on: cpu
这里全面介绍了 100 多种张量运算,包括转置、索引、切片、数学运算、线性代数、随机采样等。
它们中的每一个都可以在 GPU 上运行(通常以比 CPU 更高的速度运行)
# We move our tensor to the GPU if available
if torch.cuda.is_available():
tensor = tensor.to('cuda')
print(f"Device tensor is stored on: {tensor.device}")
tensor = torch.ones(4, 4)
tensor[:,1] = 0
print(tensor)
t1 = torch.cat([tensor, tensor, tensor], dim=1)
print(t1)
x = torch.tensor([[1,2,3],[0,1,2]])
y = x.mul(x)
print(x)
print(y)
# tensor([[1, 2, 3],
# [0, 1, 2]])
# tensor([[1, 4, 9],
# [0, 1, 4]])
z = x.matmul(x.T) # x.T表示转置,与x.t()用法一致
print(z)
# tensor([[14, 8],
# [ 8, 5]])
具有 _
后缀的操作是就地操作。例如:x.copy_(y),x.t_(),x.add_(),会改变x(就没有返回值了)
就地操作可以节省一些内存,但在计算导数时可能会出现问题,因为会立即丢失历史记录。因此,不鼓励使用它们。
单元素张量,可用 tensor.item()
将元素提出
a = torch.tensor(1)
b = a.item()
print(a, type(a))
print(b, type(b))
# tensor(1)
# 1
在CPU上的张量和NumPy 数组可以共享它们的底层内存位置,改变一个会改变另一个
t = torch.ones(5)
n = t.numpy()
t.add_(1)
print(f"t: {t}")
print(f"n: {n}")
# t: tensor([2., 2., 2., 2., 2.])
# n: [2. 2. 2. 2. 2.]
NumPy 数组的变化反映在张量中
n = np.ones(5)
t = torch.from_numpy(n) # 如果写成torch.tensor(n),则不会同步改变
np.add(n, 1, out=n)
print(f"t: {t}")
print(f"n: {n}")
# t: tensor([2., 2., 2., 2., 2.], dtype=torch.float64)
# n: [2. 2. 2. 2. 2.]
from torch.utils.data import DataLoader,Dataset
PyTorch 提供了两个数据原语,允许您使用预加载的数据集以及您自己的数据:
Dataset 存储样本及其对应的标签,DataLoader 在 Dataset 周围包装了一个可迭代对象,以便轻松访问样本。
from torchvision import datasets, transforms
torchvision.transforms 模块提供了几个开箱即用的常用转换。
神经网络由对数据执行操作的层/模块组成。 torch.nn 命名空间提供了构建自己的神经网络所需的所有构建块。 PyTorch 中的每个模块都是 nn.Module
的子类。神经网络是一个模块本身,它由其他模块(层)组成。这种嵌套结构允许轻松构建和管理复杂的架构。
在接下来的部分中,我们将构建一个神经网络来对 FashionMNIST 数据集中的图像进行分类。
import os
import torch
from torch import nn
from torch.utils.data import DataLoader
from torchvision import datasets, transforms
device = 'cuda' if torch.cuda.is_available() else 'cpu'
print(f'Using {device} device')
我们通过子类化 nn.Module
来定义我们的神经网络,并在 __init__
中初始化神经网络层。每个 nn.Module 子类都在 forward
方法中实现对输入数据的操作。
class NeuralNetwork(nn.Module):
def __init__(self):
super(NeuralNetwork, self).__init__()
self.flatten = nn.Flatten()
self.linear_relu_stack = nn.Sequential(
nn.Linear(28*28, 512),
nn.ReLU(),
nn.Linear(512, 512),
nn.ReLU(),
nn.Linear(512, 10),
)
def forward(self, x):
x = self.flatten(x)
logits = self.linear_relu_stack(x)
return logits
创建一个 NeuralNetwork 实例,并将其移动到设备上,并打印其结构
model = NeuralNetwork().to(device)
print(model)
在输入上调用模型会返回一个 10 维张量,其中包含每个类的原始预测值。我们通过一个 nn.Softmax 模块的实例来获得预测概率
X = torch.rand(1, 28, 28, device=device)
logits = model(X)
pred_probab = nn.Softmax(dim=1)(logits)
y_pred = pred_probab.argmax(1)
print(f'Predicted class: {y_pred}')
让我们分解 FashionMNIST 模型中的层。为了说明这一点,我们将抽取 3 张大小为 28x28 的图像的小批量样本,看看当我们通过网络传递它时会发生什么。(其实有个问题,tensor一般是4维度的,包括batchsize、c、h、w,感觉这里是没有把c给写出来,可能是默认为1通道的灰度图吧,像上面的X应该也是这么个道理)
input_image = torch.rand(3, 28, 28)
print(input_image.size()) # torch.Size([3, 28, 28])
我们初始化 nn.Flatten
层以将每个 2D 28x28 图像转换为 784 个像素值的连续数组(保持小批量维度(dim=0),即dim=0是batchsize应保持不变,从dim=1开始将后面的维度拉直。nn.Flatten()的默认起始dim=1
所以可以直接使用)
flatten = nn.Flatten()
flat_image = flatten(input_image)
print(flat_image.size()) # [3, 28, 28] --> [3, 784]
线性层是一个module,它使用其存储的权重和偏差对输入进行线性变换。
# nn.Linear(in_chnnel, out_chnnel)
layer1 = nn.Linear(in_features=28 * 28, out_features=20)
hidden1 = layer1(flat_image)
print(hidden1.size()) # [3, 784] --> [3, 20]
非线性激活是在模型的输入和输出之间创建复杂映射的原因。它们在线性变换后应用以引入非线性,帮助神经网络学习各种现象。
nn.ReLU()是对元素进行max(0,x)操作,即大于0的则保持原值,小于0的就改为0
print(f"Before ReLU: {hidden1}\n\n")
hidden1 = nn.ReLU()(hidden1)
print(f"After ReLU: {hidden1}")
nn.Sequential
是一个有序的模块容器。数据按照定义的顺序通过所有模块。您可以使用顺序容器来组合一个快速网络,例如 seq_modules。
seq_modules = nn.Sequential(
flatten,
layer1,
nn.ReLU(),
nn.Linear(20, 10)
)
input_image = torch.rand(3,28,28)
logits = seq_modules(input_image)
神经网络的最后一个线性层返回 logits - [-infty, infty] 中的原始值 - 被传递给 nn.Softmax 模块。
logits 被缩放为值 [0, 1]
,表示模型对每个类别的预测概率。
dim 参数指示值必须总和为 1 的维度。
softmax = nn.Softmax(dim=1)
pred_probab = softmax(logits)
神经网络中的许多层都是参数化的,即具有在训练期间优化的相关权重和偏差。(参考:【pytorch】named_parameters()和parameters())
子类化 nn.Module 会自动跟踪模型对象中定义的所有字段,并使用模型的 parameters()
或 named_parameters()
方法使所有参数都可以访问。
使用named_parameters()
查看module中的名称 + 参数:
print(f"Model structure: {model}\n\n")
for name, param in model.named_parameters():
print(f"Layer: {name} | Size: {param.size()} | Values : {param[:2]} \n")
使用parameters()
计算整个模型的参数个数:
total = sum([param.nelement() for param in net.parameters()])
print('Trainable Parameters: %.3fM' % (total / 1e6))
torch.autograd 是 PyTorch 的自动微分引擎,为神经网络训练提供动力。它支持任何计算图的梯度自动计算。
从概念上讲,autograd 在由 Function 对象组成的 有向无环图 (DAG) 中记录数据(张量)和所有执行的操作(以及生成的新张量)。在这个 DAG 中,叶子是输入张量,根是输出张量。通过从根到叶跟踪此图,您可以使用链式法则自动计算梯度。
DAG 在 PyTorch 中是动态的。需要注意的重要一点是图形是从头开始重新创建的;在每次 .backward()
调用之后,autograd 开始填充一个新图。这正是允许您在模型中使用控制流语句的原因;如果需要,您可以在每次迭代时更改形状、大小和操作。
神经网络 (Neural networks,NN) 是在某些输入数据上执行的嵌套函数的集合。这些函数由参数(由权重w和偏差b组成)定义,在 PyTorch 中存储在张量中。
requires_grad
属性,只有requires_grad=True
才能计算梯度并进行传递。torch.no_grad() 块包围我们的计算代码
detach()
训练 NN 分两个步骤进行:
让我们看一下单个训练步骤。
1. 加载数据、标签、模型
对于这个例子,我们从 torchvision 加载一个预训练的 resnet18 模型。
我们创建一个随机数据张量来表示具有 3 个通道、高度和宽度为 64 的单个图像,并将其对应的标签初始化为一些随机值。预训练模型中的标签具有形状 (1,1000)。(目前是在CPU上使用,没有迁移到GPU上加速使用)
import torchvision, torch
model = torchvision.models.resnet18(pretrained=True)
data = torch.rand(1, 3, 64, 64) # 张量是4维的,batchsize、c、h、w
labels = torch.rand(1, 1000)
2. 前向传播
接下来,我们通过模型的每一层运行输入数据以进行预测,这是前传。
prediction = model(data) # forward pass
3. 计算损失并反向传播
我们使用模型的预测和相应的标签来计算误差(损失)。下一步是通过网络反向传播这个loss。
当我们在 loss 张量上调用 .backward()
时,反向传播就开始了。然后 Autograd 计算每个模型参数的梯度并将其存储在参数的 .grad
属性中。
loss = (prediction - labels).sum()
loss.backward() # backward pass
4. 加载优化器
接下来,我们加载一个优化器,在本例中为 SGD,学习率为 0.01,动量为 0.9。我们在优化器中注册模型的所有参数。
optim = torch.optim.SGD(model.parameters(), lr=1e-2, momentum=0.9)
5. 启动梯度下降
最后,我们调用 .step()
来启动梯度下降。优化器通过存储在 .grad
中的梯度来调整每个参数。
optim.step()
总共:
import torchvision
import torch
# 加载数据、模型
model = torchvision.models.resnet18(pretrained=True)
data = torch.rand(1, 3, 64, 64) # 张量是4维的,batchsize、c、h、w
labels = torch.rand(1, 1000)
# 预测(前向传播)
prediction = model(data)
# 计算损失并后向传播
loss = (prediction - labels).sum()
loss.backward()
# 启用优化器,开始梯度下降
optim = torch.optim.SGD(model.parameters(), lr=1e-2, momentum=0.9)
optim.step()
现在我们有了模型和数据,是时候通过优化数据上的参数来训练、验证和测试我们的模型了。
epoch
)中,模型对输出进行猜测,计算猜测中的误差(损失),收集误差相对于其参数的导数,并优化这些参数使用梯度下降。超参数是可调整的参数,可让您控制模型优化过程。不同的超参数值会影响模型训练和收敛速度(阅读有关超参数调整的更多信息)
我们为训练定义了以下超参数:
一旦我们设置了超参数,我们就可以使用优化循环来训练和优化我们的模型。优化循环的每次迭代称为一个epoch。
每个时期包括两个主要部分:
损失函数衡量得到的结果与目标值的差异程度,在训练时要最小化。
为了计算损失,我们使用给定数据样本的输入进行预测,并将其与真实数据标签值进行比较。
常见的损失函数包括:
优化是在每个训练步骤中调整模型参数以减少模型误差的过程。优化算法定义了如何执行这个过程。所有优化逻辑都封装在优化器对象中。
我们通过注册需要训练的模型参数并传入学习率超参数来初始化优化器。
optimizer = torch.optim.SGD(model.parameters(), lr=learning_rate)
在训练循环中,优化分三个步骤进行:
optimizer.zero_grad()
来重置模型参数的梯度。默认情况下渐变加起来;为了防止重复计算,我们在每次迭代时明确地将它们归零。loss.backward()
反向传播预测损失。 PyTorch 存储每个参数的损耗w.r.t梯度。optimizer.step()
来通过反向传播中收集的梯度来调整参数。我们定义了循环优化代码的 train_loop
,以及针对我们的测试数据评估模型性能的 test_loop
。
def train_loop(dataloader, model, loss_fn, optimizer):
size = len(dataloader.dataset)
for batch, (X, y) in enumerate(dataloader):
# Compute prediction and loss
pred = model(X)
loss = loss_fn(pred, y)
# Backpropagation
optimizer.zero_grad()
loss.backward()
optimizer.step()
if batch % 100 == 0:
loss, current = loss.item(), batch * len(X)
print(f"loss: {loss:>7f} [{current:>5d}/{size:>5d}]")
def test_loop(dataloader, model, loss_fn):
size = len(dataloader.dataset)
num_batches = len(dataloader)
test_loss, correct = 0, 0
with torch.no_grad():
for X, y in dataloader:
pred = model(X)
test_loss += loss_fn(pred, y).item()
correct += (pred.argmax(1) == y).type(torch.float).sum().item()
test_loss /= num_batches
correct /= size
print(f"Test Error: \n Accuracy: {(100*correct):>0.1f}%, Avg loss: {test_loss:>8f} \n")
我们初始化损失函数和优化器,并将其传递给 train_loop 和 test_loop。随意增加 epoch 的数量来跟踪模型的改进性能。
loss_fn = nn.CrossEntropyLoss()
optimizer = torch.optim.SGD(model.parameters(), lr=learning_rate)
epochs = 10
for t in range(epochs):
print(f"Epoch {t+1}\n-------------------------------")
train_loop(train_dataloader, model, loss_fn, optimizer)
test_loop(test_dataloader, model, loss_fn)
print("Done!")
1. 保存
PyTorch 模型将学习到的参数存储在称为 state_dict
的内部状态字典中。这些可以通过 torch.save
方法持久化:
import torch
import torchvision.models as models
model = models.vgg16(pretrained=True)
torch.save(model.state_dict(), 'model_weights.pth')
2. 加载
要加载模型权重,您需要先创建相同模型的实例,然后使用 load_state_dict()
方法加载参数。
model = models.vgg16() # 没有指定pretrained=True,表示没有加载默认权重
model.load_state_dict(torch.load('model_weights.pth'))
model.eval()
一定要在推理之前调用
model.eval()
方法,将 dropout 和 batch normalization 层设置为评估模式。不这样做会产生不一致的推理结果。
torch.save(model, 'model.pth')
model = torch.load('model.pth')
可以使用 torch.nn
包构建神经网络。nn 依赖于 autograd
来定义模型并区分它们。An nn.Module
contains layers
, and a method forward(input)
that returns the output
。(一个nn模块,包含了许多层、一个输入并返回结果)
例如,看看这个分类数字图像的网络(LeNet):
这是一个简单的前馈网络。它接受输入,一个接一个地通过几个层,最后给出输出。
神经网络的典型训练过程如下:
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 input image channel, 6 output channels, 5x5 square convolution
# kernel
self.conv1 = nn.Conv2d(1, 6, 5)
self.conv2 = nn.Conv2d(6, 16, 5)
# an affine operation: y = Wx + b
self.fc1 = nn.Linear(16 * 5 * 5, 120) # 5*5 from image dimension
self.fc2 = nn.Linear(120, 84)
self.fc3 = nn.Linear(84, 10)
def forward(self, x):
# Max pooling over a (2, 2) window
x = F.max_pool2d(F.relu(self.conv1(x)), (2, 2))
# If the size is a square, you can specify with a single number
x = F.max_pool2d(F.relu(self.conv2(x)), 2)
x = torch.flatten(x, 1) # flatten all dimensions except the batch dimension
x = F.relu(self.fc1(x))
x = F.relu(self.fc2(x))
x = self.fc3(x)
return x
model= Net()
print(model)
total = sum([param.nelement() for param in net.parameters()])
print('Trainable Parameters: %.3fM' % (total / 1e6))
# 计算net的参数列表中所有需要学习的元素个数(即使p.requires_grad为True)
parameters = filter(lambda p: p.requires_grad, net.parameters())
parameters = sum([np.prod(p.size()) for p in parameters]) / 1_000_000
print('Trainable Parameters: %.3fM' % parameters)
model.zero_grad()
out.backward(torch.randn(1, 10))
损失函数采用(输出,目标)输入对,并计算一个值来估计输出与目标的距离。 nn 包下有几种不同的损失函数。一个简单的损失是:nn.MSELoss,它计算输出和目标之间的均方误差。
data = torch.rand(1, 1, 32, 32)
output = model(data)
target = torch.rand(1, 10)
loss = nn.MSE(output, target)
要反向传播误差,我们所要做的就是 loss.backward()
。需要清除现有的梯度,否则渐变将累积到现有的梯度中。
torch.optim
来实现所有这些优化器。
import torch.optim as optim
# create your optimizer
optimizer = optim.SGD(net.parameters(), lr=0.01)
# in your training loop:
optimizer.zero_grad() # zero the gradient buffers
output = net(input)
loss = criterion(output, target)
loss.backward()
optimizer.step() # Does the update
看官网内容:https://pytorch.org/tutorials/beginner/blitz/cifar10_tutorial.html
numpy 数组
中。然后你可以把这个数组转换成一个torch.*Tensor
。Pillow
、OpenCV
、PILImage
等软件包很有用torchvision
:专门针对计算机视觉的的包。它具有用于常见数据集(如 ImageNet、CIFAR10、MNIST 等)的数据加载器和用于图像的数据转换器,即 torchvision.datasets
和 torch.utils.data.DataLoader
。num_worker 设置为 0
。