学习地址
???????????????????????????????
老版本、新版本我都看了,把一些知识点进行了融合
???????????????????????????????
1. 张量
1.1 张量初始化
1.1.1 直接创建
import torch
import numpy as np
1. 直接从数据创建(类型是python的自带基本数据类型list )
data = [[1, 2], [3, 4]]
x_data = torch.tensor(data)
print(type(data))
print(type(x_data))
2. 直接从 Numpy 数组中创建
np_array = np.array(data)
x_np = torch.from_numpy(np_array)
3. 直接从另一个张量创建
新张量只保留参数张量的属性(形状、数据类型),而不是显式覆盖。
- torch.ones_like(x_data) #全为1,且张量形状与x_data相同
- torch.zeros_like(x_data)
- torch.empty_like(x_data)
- torch.full_like(x_data, 5) #自定义一个数字例如5,然后全部填充为5
- torch.rand_like(x_data, dtype=torch.float) #全为随机数,必须指定dtype元素类型
- torch.randint_like(x_data, start, end) # 从范围
[start, end) 中随机选整数填充
x_ones = torch.ones_like(x_data)
x_rand = torch.rand_like(x_data, dtype=torch.float)
1.1.2 用随机数或常量创建
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有什么区别(一个是均匀分布,一个是标准正态分布)
1.2 张量属性
- shape
- datatype
- the device on which they are stored
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}")
1.3 张量操作
这里全面介绍了 100 多种张量运算,包括转置、索引、切片、数学运算、线性代数、随机采样等。
1.3.1 转到GPU上操作
它们中的每一个都可以在 GPU 上运行(通常以比 CPU 更高的速度运行)
if torch.cuda.is_available():
tensor = tensor.to('cuda')
print(f"Device tensor is stored on: {tensor.device}")
1.3.2 标准的类似 numpy 的索引和切片
tensor = torch.ones(4, 4)
tensor[:,1] = 0
print(tensor)
1.3.3 张量拼接
- dim=0,y轴方向(默认方向)
- dim=1,x轴方向
t1 = torch.cat([tensor, tensor, tensor], dim=1)
print(t1)
1.3.4 张量乘法(对应位置元素相乘)
x = torch.tensor([[1,2,3],[0,1,2]])
y = x.mul(x)
print(x)
print(y)
1.3.5 矩阵乘法(matrix multiplication)
z = x.matmul(x.T)
print(z)
1.3.6 就地操作
具有 _ 后缀的操作是就地操作。例如:x.copy_(y),x.t_(),x.add_(),会改变x(就没有返回值了)
就地操作可以节省一些内存,但在计算导数时可能会出现问题,因为会立即丢失历史记录。因此,不鼓励使用它们。
1.3.7 单元素张量
单元素张量,可用 tensor.item() 将元素提出
a = torch.tensor(1)
b = a.item()
print(a, type(a))
print(b, type(b))
1.4 与Numpy的桥梁
在CPU上的张量和NumPy 数组可以共享它们的底层内存位置,改变一个会改变另一个
1.4.1 张量到 Numpy 数组
t = torch.ones(5)
n = t.numpy()
t.add_(1)
print(f"t: {t}")
print(f"n: {n}")
1.4.2 Numpy 数组到张量
NumPy 数组的变化反映在张量中
n = np.ones(5)
t = torch.from_numpy(n)
np.add(n, 1, out=n)
print(f"t: {t}")
print(f"n: {n}")
2. DATASETS & DATALOADERS
from torch.utils.data import DataLoader,Dataset
PyTorch 提供了两个数据原语,允许您使用预加载的数据集以及您自己的数据:
- torch.utils.data.Dataset
- torch.utils.data.DataLoader
Dataset 存储样本及其对应的标签,DataLoader 在 Dataset 周围包装了一个可迭代对象,以便轻松访问样本。
3. TRANSFORMS
from torchvision import datasets, transforms
torchvision.transforms 模块提供了几个开箱即用的常用转换。
4. 构建神经网络
神经网络由对数据执行操作的层/模块组成。 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
4.1 Get Device for Training
device = 'cuda' if torch.cuda.is_available() else 'cpu'
print(f'Using {device} device')
4.2 Define the Class
我们通过子类化 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}')
4.3 Model Layers
让我们分解 FashionMNIST 模型中的层。为了说明这一点,我们将抽取 3 张大小为 28x28 的图像的小批量样本,看看当我们通过网络传递它时会发生什么。(其实有个问题,tensor一般是4维度的,包括batchsize、c、h、w,感觉这里是没有把c给写出来,可能是默认为1通道的灰度图吧,像上面的X应该也是这么个道理)
input_image = torch.rand(3, 28, 28)
print(input_image.size())
4.3.1 nn.Flatten
我们初始化 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())
4.3.2 nn.Linear
线性层是一个module,它使用其存储的权重和偏差对输入进行线性变换。
layer1 = nn.Linear(in_features=28 * 28, out_features=20)
hidden1 = layer1(flat_image)
print(hidden1.size())
4.3.3 nn.ReLU
非线性激活是在模型的输入和输出之间创建复杂映射的原因。它们在线性变换后应用以引入非线性,帮助神经网络学习各种现象。
nn.ReLU()是对元素进行max(0,x)操作,即大于0的则保持原值,小于0的就改为0
print(f"Before ReLU: {hidden1}\n\n")
hidden1 = nn.ReLU()(hidden1)
print(f"After ReLU: {hidden1}")
4.3.4 nn.Sequential
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)
4.3.5 nn.Softmax
神经网络的最后一个线性层返回 logits - [-infty, infty] 中的原始值 - 被传递给 nn.Softmax 模块。
logits 被缩放为值 [0, 1] ,表示模型对每个类别的预测概率。
dim 参数指示值必须总和为 1 的维度。
softmax = nn.Softmax(dim=1)
pred_probab = softmax(logits)
4.4 Model Parameters
神经网络中的许多层都是参数化的,即具有在训练期间优化的相关权重和偏差。(参考:【pytorch】named_parameters()和parameters())
子类化 nn.Module 会自动跟踪模型对象中定义的所有字段,并使用模型的 parameters() 或 named_pa??rameters() 方法使所有参数都可以访问。
- named_pa??rameters():给出网络层的名字、参数的迭代器
- pa??rameters():只给出参数的迭代器
使用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))
5. Autograd: 自动求导
torch.autograd 是 PyTorch 的自动微分引擎,为神经网络训练提供动力。它支持任何计算图的梯度自动计算。
从概念上讲,autograd 在由 Function 对象组成的 有向无环图 (DAG) 中记录数据(张量)和所有执行的操作(以及生成的新张量)。在这个 DAG 中,叶子是输入张量,根是输出张量。通过从根到叶跟踪此图,您可以使用链式法则自动计算梯度。
DAG 在 PyTorch 中是动态的。需要注意的重要一点是图形是从头开始重新创建的;在每次 .backward() 调用之后,autograd 开始填充一个新图。这正是允许您在模型中使用控制流语句的原因;如果需要,您可以在每次迭代时更改形状、大小和操作。
5.1 介绍
神经网络 (Neural networks,NN) 是在某些输入数据上执行的嵌套函数的集合。这些函数由参数(由权重w和偏差b组成)定义,在 PyTorch 中存储在张量中。
- 为了能够计算损失函数相对于这些变量的梯度,设置了这些张量的
requires_grad 属性,只有requires_grad=True 才能计算梯度并进行传递。 - 停止跟踪梯度计算:通过用
torch.no_grad() 块包围我们的计算代码
- 或者,直接在张量上使用
detach() - 禁用原因:
- 将神经网络中的某些参数标记为冻结参数。这是微调预训练网络的一个非常常见的场景
- 只进行前向传递时加快计算速度(即不进行训练,而是进行验证 or 测试)
训练 NN 分两个步骤进行:
- 前向传播:在前向传播中,NN 对正确的输出做出最佳猜测。它通过其每个函数运行输入数据以进行猜测。
- 反向传播:在反向传播中,NN 根据猜测中的误差调整其参数。它通过从输出向后遍历,收集关于函数参数(梯度)的误差导数,并使用梯度下降优化参数来做到这一点。
5.2 在PyTorch中的使用
让我们看一下单个训练步骤。
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)
labels = torch.rand(1, 1000)
2. 前向传播
接下来,我们通过模型的每一层运行输入数据以进行预测,这是前传。
prediction = model(data)
3. 计算损失并反向传播
我们使用模型的预测和相应的标签来计算误差(损失)。下一步是通过网络反向传播这个loss。
当我们在 loss 张量上调用 .backward() 时,反向传播就开始了。然后 Autograd 计算每个模型参数的梯度并将其存储在参数的 .grad 属性中。
loss = (prediction - labels).sum()
loss.backward()
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)
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()
6. 优化模型参数
现在我们有了模型和数据,是时候通过优化数据上的参数来训练、验证和测试我们的模型了。
- 训练模型是一个迭代过程;
- 在每次迭代(称为
epoch )中,模型对输出进行猜测,计算猜测中的误差(损失),收集误差相对于其参数的导数,并优化这些参数使用梯度下降。
6.1 Hyperparameters 超参数
超参数是可调整的参数,可让您控制模型优化过程。不同的超参数值会影响模型训练和收敛速度(阅读有关超参数调整的更多信息)
我们为训练定义了以下超参数:
- Number of Epochs:迭代数据集的次数
- Batch Size:参数更新前通过网络传播的数据样本数
- Learning Rate:在每个批次/时期更新模型参数的程度。较小的值会产生较慢的学习速度,而较大的值可能会导致训练期间出现不可预测的行为。
6.2 Optimization Loop 优化循环
一旦我们设置了超参数,我们就可以使用优化循环来训练和优化我们的模型。优化循环的每次迭代称为一个epoch。
每个时期包括两个主要部分:
- The Train Loop:迭代训练数据集并尝试收敛到最佳参数
- The Validation/Test Loop:迭代测试数据集以检查模型性能是否正在改善
6.3 Loss Function
损失函数衡量得到的结果与目标值的差异程度,在训练时要最小化。
为了计算损失,我们使用给定数据样本的输入进行预测,并将其与真实数据标签值进行比较。
常见的损失函数包括:
- 用于回归的 nn.MSELoss(均方误差)
- 用于分类的 nn.NLLLoss(负对数似然)
- nn.CrossEntropyLoss 结合了 nn.LogSoftmax 和 nn.NLLLoss
6.4 Optimizer
优化是在每个训练步骤中调整模型参数以减少模型误差的过程。优化算法定义了如何执行这个过程。所有优化逻辑都封装在优化器对象中。
我们通过注册需要训练的模型参数并传入学习率超参数来初始化优化器。
optimizer = torch.optim.SGD(model.parameters(), lr=learning_rate)
在训练循环中,优化分三个步骤进行:
- 调用
optimizer.zero_grad() 来重置模型参数的梯度。默认情况下渐变加起来;为了防止重复计算,我们在每次迭代时明确地将它们归零。 - 通过调用
loss.backward() 反向传播预测损失。 PyTorch 存储每个参数的损耗w.r.t梯度。 - 一旦我们有了梯度,我们调用
optimizer.step() 来通过反向传播中收集的梯度来调整参数。
6.5 Full Implementation 全面实施
我们定义了循环优化代码的 train_loop ,以及针对我们的测试数据评估模型性能的 test_loop 。
def train_loop(dataloader, model, loss_fn, optimizer):
size = len(dataloader.dataset)
for batch, (X, y) in enumerate(dataloader):
pred = model(X)
loss = loss_fn(pred, y)
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!")
7. SAVE AND LOAD THE MODEL 保存和加载模型
7.1 保存和加载模型权重(常用、推荐)
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()
model.load_state_dict(torch.load('model_weights.pth'))
model.eval()
一定要在推理之前调用 model.eval() 方法,将 dropout 和 batch normalization 层设置为评估模式。不这样做会产生不一致的推理结果。
7.2 使用形状保存和加载模型
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):
这是一个简单的前馈网络。它接受输入,一个接一个地通过几个层,最后给出输出。
神经网络的典型训练过程如下:
- 定义具有一些可学习参数(或权重)的神经网络
- 迭代输入数据集
- 通过网络处理输入
- 计算损失(结果与正确值的差距)
- 将梯度传播回网络的参数
- 更新网络的权重,通常使用的简单的更新规则:权重=权重-学习率*梯度
3.1 定义网络
import torch
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(1, 6, 5)
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 = F.max_pool2d(F.relu(self.conv1(x)), (2, 2))
x = F.max_pool2d(F.relu(self.conv2(x)), 2)
x = torch.flatten(x, 1)
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))
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))
3.2 损失函数
损失函数采用(输出,目标)输入对,并计算一个值来估计输出与目标的距离。 nn 包下有几种不同的损失函数。一个简单的损失是:nn.MSELoss,它计算输出和目标之间的均方误差。
data = torch.rand(1, 1, 32, 32)
output = model(data)
target = torch.rand(1, 10)
loss = nn.MSE(output, target)
3.3 反向传播
要反向传播误差,我们所要做的就是 loss.backward() 。需要清除现有的梯度,否则渐变将累积到现有的梯度中。
3.4 更新权重
torch.optim 来实现所有这些优化器。
import torch.optim as optim
optimizer = optim.SGD(net.parameters(), lr=0.01)
optimizer.zero_grad()
output = net(input)
loss = criterion(output, target)
loss.backward()
optimizer.step()
训练一个分类器(旧版)
看官网内容:https://pytorch.org/tutorials/beginner/blitz/cifar10_tutorial.html
- 通常,当您必须处理图像、文本、音频或视频数据时,您可以使用标准 python 包将数据加载到
numpy 数组 中。然后你可以把这个数组转换成一个torch.*Tensor 。 - 对于图像,
Pillow 、OpenCV 、PILImage 等软件包很有用 torchvision :专门针对计算机视觉的的包。它具有用于常见数据集(如 ImageNet、CIFAR10、MNIST 等)的数据加载器和用于图像的数据转换器,即 torchvision.datasets 和 torch.utils.data.DataLoader 。- 如果在 Windows 上运行并且遇到 BrokenPipeError,请尝试将 torch.utils.data.DataLoader() 的
num_worker 设置为 0 。
|