0%

PyTorch基础使用实例

PyTorch官方资料

官网 PyTorch

文档PyTorch documentation — PyTorch 2.1 documentation

pip安装等入门指南: Start Locally | PyTorch

Tensor操作介绍

引入包

1
import torch

定义tensor

1
2
3
4
5
6
7
8
9
10
a = torch.tensor([1, 2, 3])  # 定义a为[1,2,3]
a = torch.from_numpy(np.array([[1, -1], [-1, 1]])) # 将np转为tensor
a = torch.zeros([3, 4]) # 定义2维全0张量, 输入的是形状, a.shape = (3, 4)
a = torch.zeros([3, 4, 2]) # 定义3维全0张量, 输入的是形状, a.shape = (3, 4, 2)
a = torch.ones([3, 4]) # 定义2维全1张量, 输入的是形状
a = torch.rand((2, 4)) # 定义2维随机张量, 输入的是形状, 数值将会是[0-1)的均匀分布
a = torch.randn((2, 4)) # 定义2维随机张量, 输入的是形状, 数值将会是标准正态分布

# 其中可以用dtype定义tensor数值的类型
a = torch.tensor([1, 2, 3], dtype=torch.float32)

操作

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
a = torch.zeros([1, 2, 3])  # 定义全0张量
a = a.squeeze(0) # 消除维度,只能消除数值为1的维度
# print(a.shape)
# torch.Size([2, 3])
a = torch.zeros([1, 2, 3]) # 定义全0张量
a = a.unsqueeze(2) # 在某个维度上加一维
# print(a.shape)
# torch.Size([1, 2, 1, 3])

'''transpose - 维度交换'''
a = a.transpose(0, 1) # 将维度交换
# 或者 a = torch.transpose(a, 0, 1)

'''concatenate - 连接'''
x = torch.zeros([2, 1, 3])
y = torch.zeros([2, 3, 3])
z = torch.zeros([2, 5, 3])
# 将多个tensor合并,除了合并维度,其他维度必须一致
a = torch.cat([x, y, z], dim=1)
# print(a.shape)
# torch.Size([2, 9, 3])

a = torch.rand([1, 2, 3])
'''运算'''
y = a.sum() # 求和
y = a.mean() # 平均
y = a.var() # 方差
y = a.std() # 标准差
y = a.max() # 求最大
y = a.argmax() # 最大值下标(大于1则是数组)
y = a.min() # 最小
y = a.argmin() # 最小值下标(大于1则是数组)

运行设备

1
2
3
4
5
6
7
8
9
# CPU
x = x.to("cpu")

# GPU
# x = x.to("cuda")

# 判断gpu
# torch.cuda.is_available()
# 装的是cpu版torch的话没有cuda

常用:

1
2
3
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
# 单cuda和cpu表示放到当前cpu或gpu, 可以用cuda:0, cuda:1这样来指定设备
# https://pytorch.org/docs/stable/tensor_attributes.html#torch.device

计算梯度

  1. 定义tensor并设置requires_grad为true
1
x = torch.tensor([[1, 0], [-1, 0]], dtype=torch.float, requires_grad=True)
  1. 定义某个函数或运算
1
2
3
4
5
z = x.pow(2).sum() # 注意只有scalar值可以计算梯度, 不能计算矩阵梯度
# 所以需要用sum把每个维度加起来计算梯度
# 例如[a1, a2, a3] * 2则梯度应当为[2, 2, 2]
# 但是不能直接算[2 * a1, 2 * a2, 2 * a3] 的梯度, 只能算2 * a1 + 2 * a2 + 2 * a3这样一个scalar的梯度
# 由于是加法, 其实它的梯度就相当于矩阵每个参数的梯度
  1. 反向传播(计算微分)
1
z.backward()
  1. 计算出的微分会存在x.grad上
1
print(x.grad)
  1. 清除梯度
1
x.grad.data.zero_() # 下次计算时梯度会累积而不会重新计算

所有步骤

1
2
3
4
5
6
7
8
9
10
11
12
# 1. 定义
x = torch.tensor([[1, 0], [-1, 0]], dtype=torch.float, requires_grad=True)
# 2. 清除梯度(清除梯度只要不在backward和使用grad之间即可)
x.grad.data.zero_()
# 3. 定义运算
z = x.pow(2).sum()
# 4. 反向传播
z.backward()
# 5. 获取梯度
print(x.grad)

# 如果需要则可循环2-5操作

dataset与dataloader

引入包

1
2
3
import torch
from torch.utils.data.dataloader import DataLoader
from torch.utils.data.dataset import Dataset

dataset

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
class MyDataset(Dataset):
def __init__(self) -> None:
super().__init__()
# init中可以根据需要自己定义入参和相关操作
self.data = [1, 2, 3]
self.label = [
[0, 0, 1],
[0, 1, 0],
[1, 0, 0]
]

def __getitem__(self, index):
# 必须要实现
# 根据index返回对应数据
# 例如我这里返回一个tuple
# 分别是原始数据和对应标签
# 具体返回的内容根据需要决定
return self.data[index], self.label[index]

def __len__(self):
# 必须要实现
# 返回你的dataset的大小
return len(self.data)

dataloader

1
2
3
dataset = MyDataset() # 声明dataset
batch_size = 10 # 定义batchsize即每一次iteration取出的数据数量
dataloader = DataLoader(dataset, batch_size, shuffle=True)

从dataloader中取出数据

1
2
for data, label in dataloader:
pass

搭建神经网络

引入包

1
2
import torch
import torch.nn as nn

神经网络层

1
2
3
4
5
6
7
8
9
10
11
12
# Linear 全连接层
# nn.Linear(in_features, out_features, bias)
# 维度 , 维度
# in:(*, 32) -> Linear(32, 64) -> out:(*, 64) + bias
layer = nn.Linear(32, 64, bias=True)
# print(layer.weight.shape)
# torch.Size([64, 32])

# 一种用法
input = torch.ones((2, 32))
output = layer(input)
# print(output)

神经网络Module定义

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
class MyModel_A(nn.Module):
def __init__(self):
super(MyModel, self).__init__()
# 初始化与定义layer
self.fc1 = nn.Linear(10, 32),
self.ac = nn.Sigmoid(),
self.fc2 = nn.Linear(32, 1)


# forward: 计算x
def forward(self, x):
# 必须实现
# x输入shape一定是(*, 10)才能被init中定义的神经元接受
out = self.fc1(x)
out = self.ac(out)
out = self.fc2(out)
return out

# 也可以用sequential把层叠在一次
class MyModel_B(nn.Module):
def __init__(self):
super(MyModel, self).__init__()
# 初始化与定义layer
self.net = nn.Sequential(
nn.Linear(10, 32),
nn.Sigmoid(),
nn.Linear(32, 1)
)

# forward: 计算x
def forward(self, x):
# 必须实现
# x输入shape一定是(*, 10)才能被init中定义的神经元接受
return self.net(x)

# 使用model
# 1. 定义对象
model = MyModel_B() # 如果有参数在这里输入

# 2. 传入参数
out = model(x) # 会调用forward

训练

引入包

1
2
3
4
5
import torch
import torch.nn as nn
from torch.utils.data.dataloader import DataLoader
from torch.utils.data.dataset import Dataset
from torch.optim import Adam # 引入adam优化器

优化器

基本用Adam就行了

1
2
3
optimizer = Adam(params, lr)
# params为第5.2中定义的module的参数, 用model.parameters()即可
# lr是学习率, 默认为0.001

训练前准备

1
2
3
4
# 定义超参数
batch_size = 8 # 每一批参与学习的data数量
lr = 0.0001 # 学习率
n_epochs = 100 # 总轮次
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
'''1. 定义dataset和dataloader'''
class MyDataset(Dataset):
def __init__(self) -> None:
super().__init__()
self.data = torch.randn((100,100,10))
self.label = torch.ones((100))

def __getitem__(self, index):
# 必须要实现
# 根据index返回对应数据
return self.data[index], self.label[index]

def __len__(self):
# 必须要实现
# 返回你的dataset的大小
return len(self.data)

dataset = MyDataset() # 声明dataset
dataloader = DataLoader(dataset, batch_size, shuffle=True)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
'''1. 定义model'''
class MyModel_B(nn.Module):
def __init__(self):
super(MyModel, self).__init__()
# 初始化与定义layer
self.net = nn.Sequential(
nn.Linear(10, 32),
nn.Sigmoid(),
nn.Linear(32, 1)
)

# forward: 计算x
def forward(self, x):
# 必须实现
# x输入shape一定是(*, 10)才能被init中定义的神经元接受
return self.net(x)

模型训练与保存

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
from tqdm import tqdm 
# tqdm是有用的进度条工具
# pip install tqdm安装
import torch
import torch.nn as nn

device = torch.device("cuda" if torch.cuda.is_available() else "cpu") # 见2.4

# 1. 超参数
batch_size = 8
lr = 0.0001
n_epochs = 100 # 训练轮次

# 2. 数据集
dataset = MyDataset() # 见4.2-4.3
dataloader = DataLoader(dataset, batch_size, shuffle=True)

# 3. 模型及优化器准备
model = MyModel_B() # 见5.3
optimizer = Adam(model.parameters(), lr=0.0001) # 见6.2

# 4. 定义损失函数
def criterion(pred, y):
# 假设pred.shape = (batch_size, out_dim)
# y,shape = (batch_size, label_dim)
# out_dim == label_dim
# 自己定义并返回值
# 注意要返回scalar value
# 这里定义的是均方误差MSE
loss = ((y - pred) * (y - pred)/ y.shape[1]).sum()

return loss
# 也可以用torch.nn里的损失函数
criterion = nn.MSELoss()

# 设定model为train, 会使某些功能生效, 例如dropout
model.train()
# 用.to(device)把参数放入对应设备运算
# 如果有gpu则device为cuda, 会调用gpu运算
model.to(device)
t_bar = tqdm(range(n_epochs))

for epoch in t_bar:
loss_temp = 0 # 记录loss值以打印
c = 0
for x, y in dataloader:
optimizer.zero_grad() # 梯度清零
x, y = x.to(device), y.to(device)
loss = criterion(pred, y) # 计算损失
loss.backward() # 反向传播计算gradient
optimizer.step() # optimizer更新
loss_temp += loss
c += 1
tbar.set_description(f"loss={loss_temp/c}") # 让tbar显示loss

torch.save(model.state_dict(), "mymodel.model") # torch保存模型

模型加载与测试

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
from tqdm import tqdm 
# tqdm是有用的进度条工具
# pip install tqdm安装
import torch
import torch.nn as nn

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

# 1. 准备数据集
# 这里加载一个新的test数据集
dataset_test = MyDataset() # 见4.2-4.3
dataloader = DataLoader(dataset_test, batch_size, shuffle=True)

# 2. 定义损失
criterion = nn.MSELoss()

# 3. 加载模型
model = MyModel_B()
model.load_state_dict(torch.load("mymodel.model"))
model.to(device)

# 4. 开始验证
model.eval() # 验证模式

model.to(device)

loss_temp = 0 # 记录loss值以打印
c = 0
for x, y in tqdm(dataloader):
with torch.no_grad(): # 不需要跟踪梯度, 速度更快
x, y = x.to(device), y.to(device)
loss = criterion(pred, y) # 计算损失
loss_temp += loss
c += 1
print(f"loss:{loss_temp/c}")