侧边栏壁纸
  • 累计撰写 5 篇文章
  • 累计创建 0 个标签
  • 累计收到 1 条评论

目 录CONTENT

文章目录

Pytorch学习-介绍

Administrator
2022-07-10 / 0 评论 / 2 点赞 / 93 阅读 / 14076 字 / 正在检测是否收录...

Pytorch

一、Pytorch介绍

PyTorch是一个开源的Python机器学习库,基于Torch,用于自然语言处理等应用程序。
2017年1月,由Facebook人工智能研究院(FAIR)基于Torch推出了PyTorch。它是一个基于Python的可续计算包,提供两个高级功能:1、具有强大的GPU加速的张量计算(如NumPy)。2、包含自动求导系统的深度神经网络。

二、Pytorch基础

2.1、张量Tensor

张量是一种特殊的数据结构,与数组和矩阵非常相似。在PyTorch中,我们使用张量对模型的输入和输出以及模型的参数进行编码。

张量类似于NumPy的ndarray,除了张量可以在 GPU 或其他硬件加速器上运行。事实上,张量和NumPy数组通常可以共享相同的底层内存,从而无需复制数据。``

2.1.1 创建张量

直接从数据中创建张量

data = [[1, 2], [3, 4], [5, 6]]
data_t = torch.tensor(data)
print(f"Tensor from Data:\n {data_t} \n")
 
# Tensor from Data:
#  tensor([[1, 2],
#         [3, 4]]) 

从numpy数据创建张量

np_array = np.array(data)
x_np = torch.from_numpy(np_array)
print(f"Tensor from Numpy:\n {x_np} \n")

# Tensor from Numpy:
#  tensor([[1, 2],
#         [3, 4],
#         [5, 6]], dtype=torch.int32)

2.1.2 张量的属性

张量的属性包括形状、数据类型和存储设备等。

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

2.1.3 张量的操作

PyTorch中有100 多种张量运算,包括算术、线性代数、矩阵操作(转置、索引、切片)、采样等,而且这些操作中都可以在 GPU 上运行(通常以比 CPU 更高的速度)。
默认情况下,张量是在 CPU 上创建的。我们需要使用 .to方法明确地将张量移动到 GPU(在检查 GPU 可用性之后)。

# 将张量移动到GPU上
if torch.cuda.is_available():
    tensor = tensor.to("cuda")
2.1.3.1 切片索引
tensor = torch.ones(4, 4)
print(f"First row: {tensor[0]}")
print(f"First column: {tensor[:, 0]}")
print(f"Last column: {tensor[..., -1]}")
tensor[:, 1] = 0
print(tensor)

# First row: tensor([1., 1., 1., 1.])
# First column: tensor([1., 1., 1., 1.])
# Last column: tensor([1., 1., 1., 1.])
# tensor([[1., 0., 1., 1.],
#         [1., 0., 1., 1.],
#         [1., 0., 1., 1.],
#         [1., 0., 1., 1.]])

2.1.3.2 连接张量
t1 = torch.cat([tensor, tensor, tensor], dim=1) # 在第1个维度拼接,即水平方向
print(t1)
 
# tensor([[1., 0., 1., 1., 1., 0., 1., 1., 1., 0., 1., 1.],
#         [1., 0., 1., 1., 1., 0., 1., 1., 1., 0., 1., 1.],
#         [1., 0., 1., 1., 1., 0., 1., 1., 1., 0., 1., 1.],
#         [1., 0., 1., 1., 1., 0., 1., 1., 1., 0., 1., 1.]])

2.1.3.3 算数运算
# 矩阵相乘,y1、y2和y3的值相同
y1 = tensor @ tensor.T
y2 = tensor.matmul(tensor.T)
 
y3 = torch.rand_like(tensor)
torch.matmul(tensor, tensor.T, out=y3)
print(y1)
 
# tensor([[3., 3., 3., 3.],
#         [3., 3., 3., 3.],
#         [3., 3., 3., 3.],
#         [3., 3., 3., 3.]])

# 矩阵逐元素相乘,z1、z2和z3的值相同
z1 = tensor * tensor
z2 = tensor.mul(tensor)
 
z3 = torch.rand_like(tensor)
torch.mul(tensor, tensor, out=z3)
print(z1)
# tensor([[1., 0., 1., 1.],
#         [1., 0., 1., 1.],
#         [1., 0., 1., 1.],
#         [1., 0., 1., 1.]])
2.1.3.4 单元素张量

只有一个元素的张量,可以通过item()方法转换为数值

agg = tensor.sum()
agg_item = agg.item()
print(agg_item, type(agg_item))
 
# 12.0 <class 'float'>
2.1.3.5 就地操作

将结果存储到操作数中的操作称为就地操作。他们是由_后缀标识的。如:x.copy_(y),x.t_(),会改变x的值。

print(f"{tensor} \n")
tensor.add_(5)
print(tensor)
 
# tensor([[1., 0., 1., 1.],
#         [1., 0., 1., 1.],
#         [1., 0., 1., 1.],
#         [1., 0., 1., 1.]]) 
# 
# tensor([[6., 5., 6., 6.],
#         [6., 5., 6., 6.],
#         [6., 5., 6., 6.],
#         [6., 5., 6., 6.]])

就地操作可以节省一些内存,但在计算导数时可能会出现问题,因为会立即丢失历史记录。因此不建议使用。

2.2 张量与numpy

在CPU上的张量和NumPy数组共享它们的内存位置,改变一个会改变另一个。
张量转换为NumPy数组:

t = torch.ones(5)
print(f"t: {t}")
n = t.numpy()
print(f"n: {n}")
 
# t: tensor([1., 1., 1., 1., 1.])
# n: [1. 1. 1. 1. 1.]

改变张量的值,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)
print(f"n: {n}")
t = torch.from_numpy(n)
print(f"t: {t}")
 
# n: [1. 1. 1. 1. 1.]
# t: tensor([1., 1., 1., 1., 1.], dtype=torch.float64)

同理,改变numpy数组的值,张量的值也随之更改。

三、Autograd:自动求导

PyTorch的核心是autograd包。 我们首先简单的了解一些,然后用PyTorch开始训练第一个神经网络。autograd为所有用于Tensor的operation提供自动求导的功能。我们通过一些简单的例子来学习它基本用法。

3.1 从自动求导看Tensor

torch.Tensor 是这个包的核心类。如果它的属性requires_grad是True,那么PyTorch就会追踪所有与之相关的operation。当完成(正向)计算之后, 我们可以调用backward(),PyTorch会自动的把所有的梯度都计算好。与这个tensor相关的梯度都会累加到它的grad属性里。

如果不想计算这个tensor的梯度,我们可以调用detach(),这样它就不会参与梯度的计算了。为了阻止PyTorch记录用于梯度计算相关的信息(从而节约内存),我们可以使用 with torch.no_grad()。这在模型的预测时非常有用,因为预测的时候我们不需要计算梯度,否则我们就得一个个的修改Tensor的requires_grad属性,这会非常麻烦。

关于autograd的实现还有一个很重要的Function类。Tensor和Function相互连接从而形成一个有向无环图, 这个图记录了计算的完整历史。每个tensor有一个grad_fn属性来引用创建这个tensor的Function(用户直接创建的Tensor,这些Tensor的grad_fn是None)。

如果你想计算梯度,可以对一个Tensor调用它的backward()方法。如果这个Tensor是一个scalar(只有一个数),那么调用时不需要传任何参数。如果Tensor多于一个数,那么需要传入和它的shape一样的参数,表示反向传播过来的梯度。

创建tensor时设置属性requires_grad=True,PyTorch就会记录用于反向梯度计算的信息:

x = torch.ones(2, 2, requires_grad=True)
print(x)

# tensor([[1., 1.],
# [1., 1.]], requires_grad=True)

然后我们通过operation产生新的tensor:

y = x + 2
print(y)
# tensor([[3., 3.],
# [3., 3.]], grad_fn=<AddBackward0>)

y是通过operation产生的tensor,因此它的grad_fn不是None。

print(y.grad_fn)
# <AddBackward0 object at 0x7f35409a68d0>

再通过y得到z和out

z = y * y * 3
out = z.mean()

print(z, out)
# z = tensor([[ 27.,  27.],[ 27.,  27.]])
# out = tensor(27.)

requires_grad_()函数会修改一个Tensor的requires_grad。

a = torch.randn(2, 2)
a = ((a * 3) / (a - 1))
print(a.requires_grad)
a.requires_grad_(True)
print(a.requires_grad)
b = (a * a).sum()
print(b.grad_fn)

# False
# True
# <SumBackward0 object at 0x000001F99DD08130>

3.2 梯度

现在我们里反向计算梯度。因为out是一个scalar,因此out.backward()等价于out.backward(torch.tensor(1))。

out.backward()

我们可以打印梯度d(out)/dx:


print(x.grad)
# tensor([[ 4.5000,  4.5000],
# [ 4.5000,  4.5000]])

手动验算结果:
$$
out=\frac{1}{4}\sum_iz_i \
z_i = 3(x_i+2)^2 \
z_i|_{x_i=1}=27
$$

因此:
$$
\frac{\partial out}{\partial x_i}=\frac{3}{2}(x_i+2)
$$

因此:
$$
\frac{\partial out}{\partial x_i}|_{x_i=1}=\frac{9}{2}=4.5
$$

我们也可以用autograd做一些很奇怪的事情!比如y和x的关系是while循环的关系(似乎很难用一个函数直接表示y和x的关系?对x不断平方直到超过1000,这是什么函数?)

x = torch.randn(3, requires_grad=True)

y = x * 2
while y.data.norm() < 1000:
    y = y * 2

print(y)
# tensor([ -692.4808,  1686.1211,   667.7313])
gradients = torch.tensor([0.1, 1.0, 0.0001], dtype=torch.float)
y.backward(gradients)

print(x.grad)
# tensor([  102.4000,  1024.0000,     0.1024])

我们可以使用”with torch.no_grad()”来停止梯度的计算:

print(x.requires_grad)
print((x ** 2).requires_grad)

with torch.no_grad():
    print((x ** 2).requires_grad)

# True
# True
# False

2

评论区