本文概述
在深度学习中, 通常会围绕张量作为基石数据结构进行大量讨论。 Tensor甚至以Google旗舰机器学习库的名称出现:” TensorFlow”。张量是线性代数中使用的一种数据结构, 像矢量和矩阵一样, 你可以使用张量来计算算术运算。
另一方面, PyTorch是由Facebook构建的python软件包, 具有两个高级功能:1)具有强大GPU加速功能的Tensor计算(如Numpy), 以及2)基于磁带自动分化系统构建的Deep Neural Networks。
在本教程中, 你将发现什么是张量以及如何使用PyTorch在Python中操纵它们。
在本教程中, 你将介绍以下内容:
- 张量介绍
- PyTorch简介
- PyTorch的安装步骤
- PyTorch很少进行张量操作
- 使用PyTorch的简单神经网络
因此, 事不宜迟, 让我们开始介绍Tensors。
张量介绍
张量是向量和矩阵的一般化, 很容易理解为多维数组。根据流行的深度学习书” Deep Learning”(Goodfellow等),
“一般情况下, 排列在规则网格上的数字数组具有可变数量的轴称为张量。”
标量是零阶张量或秩零张量。向量是一维或一阶张量, 矩阵是二维或二阶张量。
以下信息图形以非常方便的方式描述了张量:
现在让我们以更清晰的方式建立张量背后的直觉。
张量是现代机器学习的基本组成部分。它的核心是数据容器。通常, 它包含数字。有时它甚至包括字符串, 但这很少见。因此, 可以将其视为一堆数字。
但通常, 人们将张量与多维数组混淆。根据StackExchange:
张量和多维数组是不同类型的对象。第一个是函数的类型, 第二个是适合于表示坐标系中张量的数据结构。
数学上, 张量定义为多线性函数。多线性函数由各种矢量变量组成。张量字段是张量值的函数。有关严格的数学解释, 你可以在此处阅读。
因此, 张量是你需要定义的函数或容器。实际计算是在有数据输入时发生的。你所看到的数组或多维(一维, 二维, …, ND)可视为通用张量。
现在, 让我们谈谈张量表示法。
张量符号非常类似于矩阵符号, 其中大写字母表示张量, 小写字母的下标整数表示张量内的标量值。
可以将可以用标量, 向量和矩阵执行的许多操作重新构造为使用张量执行。
张量和张量代数作为一种工具广泛用于物理和工程领域。这是一个术语, 关于张量, 可以描述深度学习模型的训练和操作中机器学习中已知的一组技术。
介绍PyTorch
PyTorch是基于Python的科学计算软件包, 其目标是:
- 替代NumPy以使用GPU的功能。
- 提供最大灵活性和速度的深度学习研究平台。
让我们快速总结一下PyTorch的独特功能-
- PyTorch提供了多种张量例程来加速和满足你的科学计算需求, 例如切片, 索引, 数学运算, 线性代数, 归约。他们很快。
- PyTorch具有构建神经网络的独特方法:使用和重放录音机。
- TensorFlow, Theano, Caffe和CNTK等大多数框架对世界有一个静态的看法。必须建立一个神经网络并一次又一次地重复使用相同的结构。改变网络行为方式意味着必须从头开始。
- PyTorch使用一种称为”反向模式自动区分”的技术, 该技术可让你以零延迟或零开销更改网络任意行为的方式。我们的灵感来自与此主题相关的几篇研究论文, 以及当前和过去的工作, 例如autograd, autograd, Chainer等。
(尽管该技术并非PyTorch独有, 但它是迄今为止最快的实现方式。对于疯狂的研究, 你将获得最快的速度和灵活性。)
- PyTorch具有最小的框架开销。我们集成了加速库, 例如Intel MKL和NVIDIA(CuDNN, NCCL)以最大化速度。它的核心是CPU和GPU张量, 而神经网络后端(TH, THC, THNN, THCUNN)则使用C99 API编写为独立的库。它们已经成熟, 并且经过了多年的测试。
(因此, 无论你运行的是小型神经网络还是大型神经网络, PyTorch的速度都非常快。)
- 与Torch或某些替代方案相比, PyTorch中的内存使用效率极高。 PyTorch的创建者已为GPU编写了自定义内存分配器, 以确保你的深度学习模型最大程度地提高内存效率。这使你可以训练比以前更大的深度学习模型。
该博客使PyTorch和Tensorflow之间的比较非常好。
附注:此srcmini博客是Tensorflow入门的绝佳入门。
安装PyTorch
PyTorch的安装非常简单。由于PyTorch支持高效的GPU计算, 因此可以与你的Cuda驱动程序进行有效的通信并更快地执行操作。
你需要使用PyTorch的Torch和Torchvision。让我们为Windows环境安装它们。稍后, 你还将看到针对Linux和Mac的步骤。
我有带有Anaconda设置的Python 3.5。另外, 我没有CUDA。运行以下命令来安装torch和torchvision:
- pip3安装http://download.pytorch.org/whl/cpu/torch-0.4.1-cp35-cp35m-win_amd64.whl
- pip3安装torchvision
(请记住, PyTorch不支持Python 2.7。因此它必须具有Python版本=> 3.5。)
如果你的计算机上启用了CUDA, 请随意运行以下命令(这适用于CUDA 9.0):
- pip3安装http://download.pytorch.org/whl/cu90/torch-0.4.1-cp35-cp35m-win_amd64.whl
- pip3安装torchvision
现在, 让我们看一下具有Python 3.5并且不支持CUDA的Linux环境的安装步骤:
- pip3安装http://download.pytorch.org/whl/cpu/torch-0.4.1-cp35-cp35m-linux_x86_64.whl
- pip3安装torchvision
是的, 你猜对了!与Windows相同。
现在, 如果你具有CUDA支持(9.0), 那么步骤将是:
- pip3安装割炬torchvision
对于具有Python 3.5并且不支持CUDA的Mac环境, 步骤如下:
- pip3安装割炬torchvision
并且, 借助CUDA支持(9.0):
- pip3安装割炬torchvision
(MacOS Binaries不支持CUDA, 如果需要CUDA, 请从源代码安装)
希望你现在已完成PyTorch的安装!
现在, 让我们直接了解PyTorch的一些Tensor算法。
使用PyTorch进行张量算术
首先, 让我们导入所有必需的库。
from __future__ import print_function
import torch
如果PyTorch安装成功, 则运行上述代码行不会给你任何错误。
现在, 让我们构造一个未初始化的5×3矩阵:
x = torch.rand(5, 3)
print(x)
tensor([[ 0.5991, 0.9365, 0.6120], [ 0.3622, 0.1408, 0.8811], [ 0.6248, 0.4808, 0.0322], [ 0.2267, 0.3715, 0.8430], [ 0.0145, 0.0900, 0.3418]])
构造一个填充零且数据类型为long的矩阵:
x = torch.zeros(5, 3, dtype=torch.long)
print(x)
tensor([[ 0, 0, 0], [ 0, 0, 0], [ 0, 0, 0], [ 0, 0, 0], [ 0, 0, 0]])
直接从数据构造张量:
x = torch.tensor([5.5, 3])
print(x)
tensor([ 5.5000, 3.0000])
如果你正确理解了Tensor, 请在评论部分告诉我Tensor x是哪种类型!
你可以基于现有张量创建张量。这些方法将重用输入张量的属性, 例如dtype(数据类型), 除非用户提供新值:
x = x.new_ones(5, 3, dtype=torch.double)
print(x)
x = torch.randn_like(x, dtype=torch.float)
print(x)
tensor([[ 1., 1., 1.], [ 1., 1., 1.], [ 1., 1., 1.], [ 1., 1., 1.], [ 1., 1., 1.]], dtype=torch.float64)
tensor([[-1.2174, 1.1807, 1.4249], [-1.1114, -0.8098, 0.4003], [ 0.0780, -0.5011, -1.0985], [ 1.8160, -0.3778, -0.8610], [-0.7109, -2.0509, -1.2079]])
得到它的大小:
print(x.size())
torch.Size([5, 3])
请注意, torch.Size实际上是一个元组, 因此它支持所有元组操作。
现在, 让我们对张量进行加法运算。
张量加法:
两个具有相同尺寸的张量的按元素相加会导致一个具有相同尺寸的新张量, 其中每个标量值是父张量中标量的按元素相加。
# Syntax 1 for Tensor addition in PyTorch
y = torch.rand(5, 3)
print(x)
print(y)
print(x + y)
tensor([[-1.2174, 1.1807, 1.4249], [-1.1114, -0.8098, 0.4003], [ 0.0780, -0.5011, -1.0985], [ 1.8160, -0.3778, -0.8610], [-0.7109, -2.0509, -1.2079]])
tensor([[ 0.8285, 0.7619, 0.1147], [ 0.1624, 0.8994, 0.6119], [ 0.2802, 0.2950, 0.7098], [ 0.8132, 0.3382, 0.4383], [ 0.6738, 0.2022, 0.3264]])
tensor([[-0.3889, 1.9426, 1.5396], [-0.9490, 0.0897, 1.0122], [ 0.3583, -0.2061, -0.3887], [ 2.6292, -0.0396, -0.4227], [-0.0371, -1.8487, -0.8815]])
# Syntax 2 for Tensor addition in PyTorch
print(torch.add(x, y))
tensor([[-0.3889, 1.9426, 1.5396], [-0.9490, 0.0897, 1.0122], [ 0.3583, -0.2061, -0.3887], [ 2.6292, -0.0396, -0.4227], [-0.0371, -1.8487, -0.8815]])
让我们现在进行张量减法。
张量减法:
将一个张量与另一个具有相同尺寸的张量进行逐元素相减会生成一个具有相同尺寸的新张量, 其中每个标量值都是父张量中标量的按元素相减4。
现在让我们看一下Tensor产品:
张量产品:
对矩阵mat1和mat2执行矩阵乘法。
如果mat1是(n×m)张量, mat2是(m×p)张量, out将是(n×p)张量。
你可以在PyTorch中执行Tensor产品, 如下所示:
mat1 = torch.randn(2, 3)
mat2 = torch.randn(3, 3)
print(mat1)
print(mat2)
print(torch.mm(mat1, mat2))
tensor([[ 1.9490, -0.6503, -1.9448], [-0.7126, 1.0519, -0.4250]])
tensor([[ 0.0846, 0.4410, -0.0625], [-1.3264, -0.5265, 0.2575], [-1.3324, 0.6644, 0.3528]])
tensor([[ 3.6185, -0.0901, -0.9753], [-0.8892, -1.1504, 0.1654]])
请注意, torch.mm()不会广播。让我们讨论一下广播。
广播:
术语广播描述了在算术运算期间如何以不同的形状处理数组。受某些约束的影响, 较小的阵列将”广播”到较宽的阵列, 以便它们具有兼容的形状。广播提供了一种对数组操作进行矢量化的方法, 从而使循环在C而不是Python中发生。它无需复制不必要的数据即可完成此操作, 通常可以实现高效的算法实现。但是, 在某些情况下, 广播不是一个好主意, 因为广播会导致内存使用效率低下, 从而减慢计算速度。
如果满足以下规则, 则两个张量是”可广播的”:
- 每个张量具有至少一个维度。
- 从尾随尺寸开始迭代尺寸尺寸时, 尺寸尺寸必须相等, 其中之一为1, 或者不存在其中之一。
让我们使用以下代码片段通过PyTorch理解这一点:
x=torch.empty(5, 7, 3)
y=torch.empty(5, 7, 3)
# same shapes are always broadcastable (i.e. the above rules always hold)
x=torch.empty((0, ))
y=torch.empty(2, 2)
# x and y are not broadcastable, because x does not have at least 1 dimension
# can line up trailing dimensions
x=torch.empty(5, 3, 4, 1)
y=torch.empty( 3, 1, 1)
# x and y are broadcastable.
# 1st trailing dimension: both have size 1
# 2nd trailing dimension: y has size 1
# 3rd trailing dimension: x size == y size
# 4th trailing dimension: y dimension doesn't exist
# but:
>>> x=torch.empty(5, 2, 4, 1)
>>> y=torch.empty( 3, 1, 1)
# x and y are not broadcastable, because in the 3rd trailing dimension 2 != 3
现在你已经有了一个广播的好主意, 让我们看看两个张量是否”可广播”, 那么它们产生的张量将是什么。
如果两个张量x, y是”可广播的”, 则所得张量大小的计算如下:
如果x和y的维数不相等, 则在张量的维数前面加1, 以使其长度相等。然后, 对于每个尺寸大小, 最终的尺寸大小就是该尺寸上x和y的最大值。例如:
# can line up trailing dimensions to make reading easier
x=torch.empty(5, 1, 4, 1)
y=torch.empty( 3, 1, 1)
(x+y).size()
torch.Size([5, 3, 4, 1])
torch.Size([5, 3, 4, 1])
# but not necessary:
x=torch.empty(1)
y=torch.empty(3, 1, 7)
(x+y).size()
torch.Size([3, 1, 7])
torch.Size([3, 1, 7])
x=torch.empty(5, 2, 4, 1)
y=torch.empty(3, 1, 1)
(x+y).size()
---------------------------------------------------------------------------
RuntimeError Traceback (most recent call last)
<ipython-input-17-72fb34250db7> in <module>()
1 x=torch.empty(5, 2, 4, 1)
2 y=torch.empty(3, 1, 1)
----> 3 (x+y).size()
RuntimeError: The size of tensor a (2) must match the size of tensor b (3) at non-singleton dimension 1
你现在就明白了!
张量积是你可能会遇到的最常见的张量乘法形式, 但是还存在许多其他类型的张量乘法, 例如张量点积和张量收缩。
将Torch张量转换为NumPy数组, 反之亦然, 这很容易。这个概念称为Numpy Bridge。让我们来看一下。
numpy桥:
Torch Tensor和NumPy数组将共享其基础内存位置, 并且更改一个将更改另一个。
将火炬张量转换为NumPy数组。
# A 1D tensor of 5 ones
a = torch.ones(5)
print(a)
tensor([ 1., 1., 1., 1., 1.])
# Convert the Torch tensor to a NumPy array
b = a.numpy()
print(b)
[1. 1. 1. 1. 1.]
查看NumPy数组的值如何变化。
因此, 在以上部分中, 你进行了一些基本的Tensor算术运算, 例如加法, 减法和张量积。现在, 在以下部分中, 你将在PyTorch中实现一个基本的神经网络。
使用PyTorch实施简单的神经网络
如果你需要神经网络的基础知识, 那么这篇srcmini文章是最佳的选择。你可能还需要检查以下链接:
- https://matrices.io/deep-neural-network-from-scratch/
- http://neuralnetworksanddeeplearning.com
- https://www.youtube.com/watch?v=bxe2T-V8XRs
在实施之前, 让我们先讨论一下称为自动差分的概念, 该概念对于PyTorch中的所有神经网络都是至关重要的。这对于在反向传播过程中计算梯度特别有用。
autograd软件包为Tensor上的所有操作提供自动区分。这是一个按运行定义的框架, 这意味着你的反向传播是由代码的运行方式定义的, 并且每次迭代都可以不同。
让我们看一个简单的代码示例, 了解自动区分的实际应用。
# Create a Rank-2 tensor of all ones
x = torch.ones(2, 2, requires_grad=True)
print(x)
tensor([[ 1., 1.], [ 1., 1.]])
进行加法运算。
y = x + 2
print(y)
tensor([[ 3., 3.], [ 3., 3.]])
对y进行更多操作。
z = y * y * 3
out = z.mean()
print(z, out)
tensor([[ 27., 27.], [ 27., 27.]]) tensor(27.)
现在让我们反向传播, 因为out包含单个标量, out.backward()等同于out.backward(torch.tensor(1))。
out.backward()
# print gradients d(out)/dx
print(x.grad)
tensor([[ 4.5000, 4.5000], [ 4.5000, 4.5000]])
现在, 你了解了自动差分以及PyTorch如何处理它。现在, 你将使用PyTorch编写一个简单的神经网络。
你将创建一个具有一个隐藏层和一个输出单元的简单神经网络。你将在隐藏层中使用ReLU激活, 并在输出层中使用S形激活。
首先, 你需要导入PyTorch库。可以使用torch.nn包构建神经网络。
import torch
import torch.nn as nn
然后, 你定义所有图层的大小和批处理大小:
n_in, n_h, n_out, batch_size = 10, 5, 1, 10
现在, 你将创建一些虚拟输入数据x和一些虚拟目标数据y。你将使用PyTorch张量存储此数据。 PyTorch张量可以像NumPy数组一样使用和操纵, 但其附加好处是PyTorch张量可以在GPU上运行。但是你只需要在本教程的CPU上运行它们即可。虽然, 将它们传输到GPU非常简单。
x = torch.randn(batch_size, n_in)
y = torch.tensor([[1.0], [0.0], [0.0], [1.0], [1.0], [1.0], [0.0], [0.0], [1.0], [1.0]])
现在, 你将在一行代码中定义我们的模型。
model = nn.Sequential(nn.Linear(n_in, n_h), nn.ReLU(), nn.Linear(n_h, n_out), nn.Sigmoid())
这将创建一个看起来像输入->线性-> relu->线性-> Sigmoid的模型。还有另一种定义模型的方法, 该方法用于定义更复杂和自定义的模型。这是通过在类中定义我们的模型来完成的。你可以在这里读到它。
现在, 该构造损失函数了。你将使用均方误差损失。
criterion = torch.nn.MSELoss()
另外, 不要忘记定义优化器。你将在其中使用强大的随机梯度下降法, 学习速率为0.01。 model.parameters()返回模型参数(权重和偏差)的迭代器。
optimizer = torch.optim.SGD(model.parameters(), lr=0.01)
现在, 你将运行”梯度下降” 50个纪元。这将按该顺序进行正向传播, 损耗计算, 反向传播和参数更新。
for epoch in range(50):
# Forward Propagation
y_pred = model(x)
# Compute and print loss
loss = criterion(y_pred, y)
print('epoch: ', epoch, ' loss: ', loss.item())
# Zero the gradients
optimizer.zero_grad()
# perform a backward pass (backpropagation)
loss.backward()
# Update the parameters
optimizer.step()
epoch: 0 loss: 0.2399429827928543
epoch: 1 loss: 0.23988191783428192
epoch: 2 loss: 0.23982088267803192
epoch: 3 loss: 0.2397598922252655
epoch: 4 loss: 0.23969893157482147
epoch: 5 loss: 0.23963800072669983
epoch: 6 loss: 0.23957709968090057
epoch: 7 loss: 0.23951618373394012
epoch: 8 loss: 0.23945537209510803
epoch: 9 loss: 0.23939454555511475
epoch: 10 loss: 0.23933371901512146
epoch: 11 loss: 0.23927298188209534
epoch: 12 loss: 0.23921218514442444
epoch: 13 loss: 0.23915143311023712
epoch: 14 loss: 0.2390907108783722
epoch: 15 loss: 0.23903003334999084
epoch: 16 loss: 0.23896940052509308
epoch: 17 loss: 0.23890872299671173
epoch: 18 loss: 0.23884813487529755
epoch: 19 loss: 0.23878750205039978
epoch: 20 loss: 0.23872694373130798
epoch: 21 loss: 0.2386663407087326
epoch: 22 loss: 0.2386058121919632
epoch: 23 loss: 0.23854532837867737
epoch: 24 loss: 0.23848481476306915
epoch: 25 loss: 0.23842433094978333
epoch: 26 loss: 0.2383638620376587
epoch: 27 loss: 0.23830339312553406
epoch: 28 loss: 0.2382429838180542
epoch: 29 loss: 0.23818258941173553
epoch: 30 loss: 0.2381247729063034
epoch: 31 loss: 0.2380656749010086
epoch: 32 loss: 0.23800739645957947
epoch: 33 loss: 0.2379491776227951
epoch: 34 loss: 0.2378900945186615
epoch: 35 loss: 0.23783239722251892
epoch: 36 loss: 0.23777374625205994
epoch: 37 loss: 0.23771481215953827
epoch: 38 loss: 0.23765745759010315
epoch: 39 loss: 0.23759838938713074
epoch: 40 loss: 0.23753997683525085
epoch: 41 loss: 0.2374821901321411
epoch: 42 loss: 0.23742322623729706
epoch: 43 loss: 0.23736533522605896
epoch: 44 loss: 0.23730707168579102
epoch: 45 loss: 0.23724813759326935
epoch: 46 loss: 0.23719079792499542
epoch: 47 loss: 0.23713204264640808
epoch: 48 loss: 0.23707345128059387
epoch: 49 loss: 0.2370160073041916
- y_pred从模型的正向获取预测值。你将其与目标值y一起传递给计算损失的标准。
- 然后, optimizer.zero_grad()将所有渐变归零。你需要执行此操作, 以免之前的梯度不断累积。
- 然后, loss.backward()是使用PyTorch的Autograd功能的主要PyTorch魔术。 Autograd计算所有梯度w.r.t.所有参数根据动态创建的计算图自动生成。基本上, 这是进行梯度下降的反向通过(反向传播)。
- 最后, 调用optimizer.step(), 它使用新的梯度对所有参数进行一次更新。
因此, 你已经做到了最后。在本文中, 你涵盖了从张量到自动微分的所有内容, 而没有!你还使用PyTorch及其张量系统实现了一个简单的神经网络。
如果你想了解有关PyTorch的更多信息并想深入了解它, 请查看PyTorch的官方文档和教程。他们真的写得很好。你可以在这里找到它们。
以下是一些有助于我编写本教程的参考资料:
- 《矢量和张量学生指南》, 2011年。链接
- PyTorch的官方文档(我已经提到了链接)。
- 张量介绍-机器学习精通博客。
让我知道你在评论部分有什么要问或讨论的地方!
如果你有兴趣了解有关Python的更多信息, 请阅读srcmini的Python统计思想(第1部分)。
评论前必须登录!
注册