【研究生学习】Pytorch基本知识——Pytorch框架基本处理操作
本篇博客记录一下Pytorch的基本知识中的Pytorch框架基本处理操作
Pytorch安装时的基本验证命令
import torch
print(torch.__version__) # 查看Pytorch版本
print(torch.cuda.is_available()) # 判断显卡是否可用
print(torch.cuda.get_device_name(0)) # 输出GPU型号
在我的笔记本电脑上的运行结果:
1.12.0
True
NVIDIA GeForce GTX 1650
Pytorch基本操作简介
- 基本使用方法
- torch.empty(5, 3):创建一个5行3列的没有初始化的tensor
- torch.rand(5, 3):创建一个5行3列的从[0,1)的均匀分布中抽取的随机数tensor
- torch.randn:标准正态分布
- torch.randint:生成在指定范围内均匀分布的整数张量
- torch.normal:生成符合指定参数的高斯分布的随机张量
- torch.zeros(5, 3, dtype=torch.long):创建一个全零矩阵
- torch.tensor([5.5, 3]):直接传入数据创建tensor
- 某一个张量.new_ones(5, 3):返回一个与size大小相同的用1填充的张量,默认返回的Tensor具有与此张量相同的torch.dtype和torch.device
- torch.randn_like(某一个张量, dtype=torch.float):返回一个和输入大小相同的张量,张量服从标准正态分布
- 某一个张量.view(维度):改变矩阵维度(传输-1代表该维度自动计算)
- 张量的属性
- 某一个张量.size():展示矩阵大小
- 某一个张量.dim():维度
- 某一个张量.item():获得张量元素的值(必须是scalar)
- 某一个张量.matmul(某一个张量):张量相乘,输入都是二维时,就是普通的矩阵乘法;当输入有多维时,把多出的一维作为batch提出来,其他部分做矩阵乘法
- 与Numpy协同操作
- 某一个张量.numpy():转换为ndarray类型
- torch.from_numpy(某一个array):转换为张量
从上面的一些使用方法中,可以做如下的总结:
-
torch.*:用于创建特殊形式的 tensor,包括 torch.ones()、torch.zeros()等
-
torch.*_like():用于创建一个与已知tensor形状相同的tensor
-
torch.new_*:用于创建一个与已知tensor类型相同的tensor
-
基本计算方法
- 加法:直接相加/torch.add(x, y)
- 乘法
- torch.mul(x, y)
- 张量直接相乘是对应元素进行点积
- torch.dot(x, y):计算两个张量的点积,只允许一维张量
- 张量与标量进行乘法是每个元素乘以标量
- 张量与行向量进行乘法是每列乘以行向量对应列的值
- 张量与列向量进行乘法是每行乘以列向量对应行的值
自动求导机制
在目前的深度学习框架中,自动求导功能是最核心也是最基础的功能,构建与训练深度学习的基本流程是:根据网络结构逐步搭建计算图,然后求得损失函数,之后根据计算图来计算导数,最后利用梯度下降方法更新参数
在Pytorch中autograd模块实现了深度学习的算法中的反向传播求导数,在张量(Tensor类)上的所有操作,autograd都能为他们自动提供微分,简化了手动计算导数的复杂过程
每一个张量都可以设置requires_grad标志,通过设置requires_grad为True来告诉PyTorch需要对该张量进行自动求导,PyTorch会记录该张量的每一步操作历史并自动计算
需要注意的是,在Pytorch里面,默认只能是标量对标量,或者标量对向量/矩阵求导,同时必须为浮点类型
首先用一个简单的例子来说明,令$ y=x^3 $,当x=2时,导数为12,使用pytorch编写代码如下:
import torch
import torch.autograd
x = torch.tensor([2.0], requires_grad=True)
print(x.grad)
print(x.grad_fn)
print(x.is_leaf)
y = x**3
print(y.grad)
print(y.grad_fn)
print(y.is_leaf)
y.backward() # 反向传播,求解导数
print(y.grad)
print(x.grad)
输出结果为:
None
None
True
None
<PowBackward0 object at 0x0000019CB34ECA60>
False
None
tensor([12.])
其中可以看到对于一个张量而言,和梯度相关的属性如下:
- grad:PyTorch会自动追踪和记录对与张量的所有操作,当计算完成后调用.backward()方法自动计算梯度并且将计算结果保存到grad属性中
- grad_fn:用于保存张量的操作,如果这个张量是用户自己创建的,则grad_fn为None
- is_leaf:是否为叶子节点,在调用.backward()方法时,只有当requires_grad和is_leaf同时为真时,才会计算节点的梯度值
如果我们将程序修改如下:
x = torch.tensor([2.0], requires_grad=True)
print(x.grad)
print(x.grad_fn)
print(x.is_leaf)
y = x**3
print(y.grad_fn)
print(y.is_leaf)
z = y*2
print(z.grad_fn)
print(z.is_leaf)
z.backward() # 反向传播,求解导数
print(x.grad)
print(y.grad)
print(z.grad)
输出结果为:
None
None
True
<PowBackward0 object at 0x000001D238303220>
False
<MulBackward0 object at 0x000001D238303220>
False
tensor([24.])
None
None
可见,.backward()默认计算对计算图叶子节点的导数,中间过程的导数是不计算的
除此之外,还可以.backward(retain_graph=True),即保留计算图中的中间变量,如果为False,则中间变量在计算完后就会被释放,其例子如下:
import torch
x = torch.randn((1,4),dtype=torch.float32,requires_grad=True)
y = x ** 2
z = y * 4
print(x)
print(y)
print(z)
loss1 = z.mean()
loss2 = z.sum()
print(loss1,loss2)
loss1.backward() # 这个代码执行正常,但是执行完中间变量都free了,所以下一个出现了问题
print(loss1,loss2)
loss2.backward() # 这时会引发错误
反向传播时梯度需要清零,否则会累加在一起,可以使用.grad.data.zero_()进行清空
自动求导的其他示例
标量对向量求导
令输入为$ [x_1,x_2,x_3]^T $,权重向量为 $ [w_1,w_2,w_3]^T $,则输出为 $ y=w^Tx+b $
程序如下:
x = torch.tensor([1.0,2.0,3.0], requires_grad=True)
w = torch.tensor([4.0,5.0,6.0], requires_grad=True)
b = 10
y = torch.dot(x,w)+b
print(y)
y.backward()
print(x.grad)
输出结果为:
tensor(42., grad_fn=<AddBackward0>)
tensor([4., 5., 6.])
标量对矩阵求导
令输入为一个2*3的矩阵:
第一次操作:$ Y=X+1 $
第二次操作:Y上每个元素平方
第三次操作:对Z上所有元素取平均值
偏导数可推导:$ \frac{\partial f}{\partial x_{ij}}=\frac{1}{6}(\frac{\partial(x_{ij}+1)^2}{\partial x_{ij}})=\frac{1}{3}(x_{ij}+1) $
程序如下:
x = torch.tensor([[1.0,2.0,3.0],[4.0,5.0,6.0]], requires_grad=True)
y = x+1
z = y**2
f = torch.mean(z)
print(f)
f.backward()
print(x.grad)
输出结果为:
tensor(23.1667, grad_fn=<MeanBackward0>)
tensor([[0.6667, 1.0000, 1.3333],
[1.6667, 2.0000, 2.3333]])
线性回归DEMO
以下是一个线性回归模型的示例,需要从中学习如何使用Pytorch:
import torch
import torch.nn as nn
import numpy as np
# 线性回归模型(不加激活函数的全连接层)
class LinearRegressionModel(nn.Module): # 模型中用到的神经网络层
def __init__(self, input_dim, output_dim):
super(LinearRegressionModel, self).__init__()
self.linear = nn.Linear(input_dim, output_dim) # 全连接层
def forward(self, x): # 前向传播
out = self.linear(x)
return out
# 构造一组输入数据x以及其对应的标签y
x_values = [i for i in range(11)]
x_train = np.array(x_values, dtype=np.float32)
x_train = x_train.reshape(-1, 1)
y_values = [2*i+1 for i in x_values]
y_train = np.array(y_values, dtype=np.float32)
y_train = y_train.reshape(-1, 1)
input_dim = 1
output_dim = 1
model = LinearRegressionModel(input_dim, output_dim)
# 指定参数和损失函数
epotchs = 1000
learning_rate = 0.01
optimizer = torch.optim.SGD(model.parameters(), lr=learning_rate)
criterion = nn.MSELoss()
# 训练模型
for epotch in range(epotchs):
epotch += 1
inputs = torch.from_numpy(x_train)
labels = torch.from_numpy(y_train)
optimizer.zero_grad() # 每一次迭代梯度要清零
outputs = model(inputs) # 前向传播
loss = criterion(outputs, labels) # 计算损失
loss.backward() # 反向传播
optimizer.step() # 更新权重参数
if epotch % 50 == 0:
print('epotch {}, loss {}'.format(epotch, loss.item()))
# 模型预测结果
predicted = model(torch.from_numpy(x_train).requires_grad_()).data.numpy()
print(predicted)
# 模型的保存与读取
# torch.save(model.state_dict(), 'model.pkl')
# model.load_state_dict(torch.load('model.pkl'))
# 使用GPU进行训练:需要将数据和模型传入到cuda里
# device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")
# model.to(device)
# inputs = torch.from_numpy(x_train).to(device)
# labels = torch.from_numpy(y_train).to(device)