Pytorch 基本概念
条评论参考:官网, 深度学习入门之Pytorch,PyTorch中文文档
Tensor & Variable
Pytorch 是一个拥有强力GPU加速的张量和动态构建网络的库,其主要构建是张量。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18import torch
import numpy as np
# 创建 numpy ndarray
numpy_tensor = np.random.randn(10,20)
# 创建 pytorch tensor
pytorch_tensor = torch.randn(3,2)
# 将 ndarray 转换到 tensor 上
pytorch_tensor1 = torch.Tensor(numpy_tensor)
pytorch_tensor2 = torch.from_numpy(numpy_tensor)
# 将 tensor 转换为 ndarray
# cpu
numpy_array = pytorch_tensor1.numpy()
# gpu
numpy_array = pytorch_tensor1.cpu().numpy()将 tensor 放到 GPU 上
1
2
3
4
5
6
7# 第一种方式:定义 cuda 数据类型
dtype = torch.cuda.FloatTensor # 定义默认 GPU 的 数据类型
gpu_tensor = torch.randn(10, 20).type(dtype)
# 第二种方式:
gpu_tensor = torch.randn(10, 20).cuda(0) # 将 tensor 放到第一个 GPU 上
gpu_tensor = torch.randn(10, 20).cuda(1) # 将 tensor 放到第二个 GPU 上
使用第一种方式将 tensor 放到 GPU 上的时候会将数据类型转换成定义的类型,而是用第二种方式能够直接将 tensor 放到 GPU 上,类型跟之前保持一致
将 tensor 放回 CPU
1
cpu_tensor = gpu_tensor.cpu()
访问 tensor 的属性
1
2
3
4
5
6
7
8
9
10
11
12
13# 大小
pytorch_tensor1.shape
pytorch_tensor1.size()
# 类型
pytorch_tensor1.type()
pytorch_tensor1.type(torch.DoubleTensor) #转换为float64
# 维度
pytorch_tensor1.dim()
# 所有元素个数
pytorch_tensor1.numel()Tensor 操作
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
40x = torch.ones(2,2) # float tensor
x = x.long() # 转换为整型
x = x.float() # 转换为 float
x = torch.randn(4,3) # 随机矩阵
# 沿行取最大值,返回每一行最大值及下标
max_value, max_index = torch.max(x, dim=1)
# 沿行求和
sum_x = torch.sum(x, dim=1)
# 增加维度
x = x.unsqueeze(0) # 在第一维加
# 减少维度
x = x.squeeze(0) # 减少第一维
x = x.squeeze() # 将 tensor 中所有一维去掉
# 维度交换
x = x.permute(1,0,2) # 重新排列 tensor 的维度
x = x.transpose(0,2) # 交换 tensor 中的两个维度
# 使用 view 对 tensor 进行 reshape
x = torch.randn(3,4,5)
x = x.view(-1,5) # -1 表示任意大小,5表示第二维变成5
# 两个 tensor 求和
x = torch.randn(3,4)
y = torch.randn(3,4)
z = x+y
z = torch.add(x,y)
# inplace 操作,在操作的符号后加_
x.unsqueeze_(0)
x.transpose_(1,0)
x.add_(y)Variable
Variable 是对 tensor 的封装,包含三个属性:.data
tensor 本身,.grad
tensor 的梯度,.grad_fn
variable 的获得方式。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19from torch.autograd import Variable
x_tensor = torch.randn(10, 5)
y_tensor = torch.randn(10, 5)
# 将 tensor 变成 Variable
x = Variable(x_tensor, requires_grad=True) # 默认 Variable 是不需要求梯度
y = Variable(y_tensor, requires_grad=True)
z = torch.sum(x + y)
print(z.data)
print(z.grad_fn)
# 求 x 和 y 的梯度
z.backward()
print(x.grad)
print(y.grad)
自动求导
简单情况
1
2
3
4
5
6
7
8
9import torch
from torch.autograd import Variable
x = Variable(torch.Tensor([2]), requires_grad=True)
y = x + 2
z = y ** 2 + 3
z.backward()
print(x.grad)复杂情况
1
2
3
4
5
6
7
8m = Variable(torch.FloatTensor([[2, 3]]), requires_grad=True) # 构建一个 1 x 2 的矩阵
n = Variable(torch.zeros(1, 2)) # 构建一个相同大小的 0 矩阵
n[0, 0] = m[0, 0] ** 2
n[0, 1] = m[0, 1] ** 3
n.backward(torch.ones_like(n)) # 将 (w0, w1) 取成 (1, 1)
print(m.grad)多次自动求导
1
2
3
4
5
6
7
8x = Variable(torch.FloatTensor([3]), requires_grad=True)
y = x * 2 + x ** 2 + 3
y.backward(retain_graph=True) # 设置 retain_graph 为 True 来保留计算图
print(x.grad) # 8
y.backward() # 再做一次自动求导,这次不保留计算图
print(x.grad) # 16
这里做了两次自动求导,16 为第一次的梯度 8 和第二次的梯度 8 加和结果。
- 练习
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15x = Variable(torch.FloatTensor([2, 3]), requires_grad=True)
k = Variable(torch.zeros(2))
k[0] = x[0] ** 2 + 3 * x[1]
k[1] = x[1] ** 2 + 2 * x[0]
j = torch.zeros(2, 2)
k.backward(torch.FloatTensor([1, 0]), retain_graph=True)
j[0] = x.grad.data
x.grad.data.zero_() # 归零之前求得的梯度
k.backward(torch.FloatTensor([0, 1]))
j[1] = x.grad.data
线性模型和梯度下降
1 | import torch |
获得
[x,x^2,x^3]
:1
2x_sample = np.arange(-3, 3.1, 0.1)
x_train = np.stack([x_sample ** i for i in range(1, 4)], axis=1)线性模型
1
2def multi_linear(x):
return torch.mm(x, w) + b
Logistic 回归
1 | import torch |
- pytorch 中包含一些常见 loss,如线性回归分类
nn.MSE()
和二分类nn.BCEWithLogitsLoss()
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# 使用自带的loss
criterion = nn.BCEWithLogitsLoss() # 将 sigmoid 和 loss 写在一层,有更快的速度、更好的稳定性
w = nn.Parameter(torch.randn(2, 1))
b = nn.Parameter(torch.zeros(1))
def logistic_reg(x):
return torch.mm(x, w) + b
optimizer = torch.optim.SGD([w, b], 1.)
y_pred = logistic_reg(x_data)
loss = criterion(y_pred, y_data)
print(loss.data)
# 同样进行 1000 次更新
start = time.time()
for e in range(1000):
# 前向传播
y_pred = logistic_reg(x_data)
loss = criterion(y_pred, y_data)
# 反向传播
optimizer.zero_grad()
loss.backward()
optimizer.step()
# 计算正确率
mask = y_pred.ge(0.5).float()
acc = (mask == y_data).sum().data / y_data.shape[0]
if (e + 1) % 200 == 0:
print('epoch: {}, Loss: {:.5f}, Acc: {:.5f}'.format(e+1, loss.data, acc))
during = time.time() - start
print()
print('During Time: {:.3f} s'.format(during))