时至今日,人们常用的计算机程序几乎都是软件开发人员从零编写的。比如,现在开发人员要编写一个程序 来管理网上商城。经过思考,开发人员可能提出如下一个解决方案:首先,用户通过Web浏览器(或移动应 用程序)与应用程序进行交互;紧接着,应用程序与数据库引擎进行交互,以保存交易历史记录并跟踪每个 用户的动态;其中,这个应用程序的核心——“业务逻辑”,详细说明了应用程序在各种情况下进行的操作。 为了完善业务逻辑,开发人员必须细致地考虑应用程序所有可能遇到的边界情况,并为这些边界情况设计合 适的规则。当买家单击将商品添加到购物车时,应用程序会向购物车数据库表中添加一个条目,将该用户ID与 商品ID关联起来。虽然一次编写出完美应用程序的可能性微乎其微,但在大多数情况下,开发人员可以从上 述的业务逻辑出发,编写出符合业务逻辑的应用程序,并不断测试直到满足用户的需求。根据业务逻辑设计 自动化系统,驱动正常运行的产品和系统,是一个人类认知上的非凡壮举。 幸运的是,对日益壮大的机器学习科学家群体来说,实现很多任务的自动化并不再屈从于人类所能考虑到的 逻辑。想象一下,假如开发人员要试图解决以下问题之一: • 编写一个应用程序,接受地理信息、卫星图像和一些历史天气信息,并预测明天的天气; • 编写一个应用程序,接受自然文本表示的问题,并正确回答该问题; • 编写一个应用程序,接受一张图像,识别出该图像所包含的人,并在每个人周围绘制轮廓; • 编写一个应用程序,向用户推荐他们可能喜欢,但在自然浏览过程中不太可能遇到的产品。 在这些情况下,即使是顶级程序员也无法提出完美的解决方案,原因可能各不相同。有时任务可能遵循一种 随着时间推移而变化的模式,我们需要程序来自动调整。有时任务内的关系可能太复杂(比如像素和抽象类 别之间的关系),需要数千或数百万次的计算。即使人类的眼睛能毫不费力地完成这些难以提出完美解决方 案的任务,这其中的计算也超出了人类意识理解范畴。机器学习(machinelearning,ML)是一类强大的可 以从经验中学习的技术。通常采用观测数据或与环境交互的形式,机器学习算法会积累更多的经验,其性能 17 也会逐步提高。相反,对于刚刚所说的电子商务平台,如果它一直执行相同的业务逻辑,无论积累多少经验, 都不会自动提高,除非开发人员认识到问题并更新软件。本书将带读者开启机器学习之旅,并特别关注深度 学习(deeplearning,DL)的基础知识。深度学习是一套强大的技术,它可以推动计算机视觉、自然语言处 理、医疗保健和基因组学等不同领域的创新。
通常,我们需要做两件重要的事:(1) 获取数据;(2)将数据读入计算机后对其进行处理。如果没有某种方法来存储数据,那么获取数据是没有意 义的。 首先,我们介绍n维数组,也称为张量(tensor)。使用过Python中NumPy计算包的读者会对本部分很熟悉。 无论使用哪个深度学习框架,它的张量类(在MXNet中为ndarray,在PyTorch和TensorFlow中为Tensor)都 与Numpy的ndarray类似。但深度学习框架又比Numpy的ndarray多一些重要功能:首先,GPU很好地支持加 速计算,而NumPy仅支持CPU计算;其次,张量类支持自动微分。这些功能使得张量类更适合深度学习。
首先,我们从MXNet导入np(numpy)模块和npx(numpy_extension)模块。np模块包含NumPy支持的函数; 而npx模块包含一组扩展函数,用来在类似NumPy的环境中实现深度学习开发。当使用张量时,几乎总是会 调用set_np函数,这是为了兼容MXNet的其他张量处理组件。
import torch
张量表示一个由数值组成的数组,这个数组可能有多个维度。具有一个轴的张量对应数学上的向量(vector); 具有两个轴的张量对应数学上的矩阵(matrix);具有两个轴以上的张量没有特殊的数学名称。 首先,我们可以使用arange创建一个行向量x。这个行向量包含以0开始的前12个整数,它们默认创建为浮 点数。张量中的每个值都称为张量的元素(element)。例如,张量x中有12个元素。除非额外指定,新的 张量将存储在内存中,并采用基于CPU的计算。
x = torch.arange(12)
print(x)
可以通过张量的shape属性来访问张量(沿每个轴的长度)的形状。
x.shape
如果只想知道张量中元素的总数,即形状的所有元素乘积,可以检查它的大小(size)。因为这里在处理的是 一个向量,所以它的shape与它的size相同。
要想改变一个张量的形状而不改变元素数量和元素值,可以调用reshape函数。例如,可以把张量x从形状为 (12,)的行向量转换为形状为(3,4)的矩阵。这个新的张量包含与转换前相同的值,但是它被看成一个3行4列 的矩阵。要重点说明一下,虽然张量的形状发生了改变,但其元素值并没有变。注意,通过改变张量的形状, 张量的大小不会改变。
X = x.reshape(3, 4)
print(X)
输出:
我们不需要通过手动指定每个维度来改变形状。也就是说,如果我们的目标形状是(高度,宽度),那么在 知道宽度后,高度会被自动计算得出,不必我们自己做除法。在上面的例子中,为了获得一个3行的矩阵, 我们手动指定了它有3行和4列。幸运的是,我们可以通过-1来调用此自动计算出维度的功能。即我们可以 用x.reshape(-1,4)或x.reshape(3,-1)来取代x.reshape(3,4)。 有时,我们希望使用全0、全1、其他常量,或者从特定分布中随机采样的数字来初始化矩阵。我们可以创建 一个形状为(2,3,4)的张量,其中所有元素都设置为0。代码如下
torch.zeros((2, 3, 4))
同样,我们可以创建一个形状为(2,3,4)的张量,其中所有元素都设置为1。代码如下:
torch.ones((2, 3, 4))
有时我们想通过从某个特定的概率分布中随机采样来得到张量中每个元素的值。例如,当我们构造数组来作 为神经网络中的参数时,我们通常会随机初始化参数的值。以下代码创建一个形状为(3,4)的张量。其中的 每个元素都从均值为0、标准差为1的标准高斯分布(正态分布)中随机采样。
torch.randn(3, 4)
我们还可以通过提供包含数值的Python列表(或嵌套列表),来为所需张量中的每个元素赋予确定值。在这 里,最外层的列表对应于轴0,内层的列表对应于轴1。
torch.tensor([[2, 1, 4, 3], [1, 2, 3, 4], [4, 3, 2, 1]])
x = torch.tensor([1.0, 2, 4, 8])
y = torch.tensor([2, 2, 2, 2])
x + y, x- y, x * y, x / y, x ** y # **运算符是求幂运算
“按元素”方式可以应用更多的计算,包括像求幂这样的一元运算符。
torch.exp(x)
除了按元素计算外,我们还可以执行线性代数运算,包括向量点积和矩阵乘法。我们将在2.3节中解释线性 代数的重点内容。 我们也可以把多个张量连结(concatenate)在一起,把它们端对端地叠起来形成一个更大的张量。我们只需 要提供张量列表,并给出沿哪个轴连结。下面的例子分别演示了当我们沿行(轴‐0,形状的第一个元素)和按 列(轴‐1,形状的第二个元素)连结两个矩阵时,会发生什么情况。我们可以看到,第一个输出张量的轴‐0长 度(6)是两个输入张量轴‐0长度的总和(3+3);第二个输出张量的轴‐1长度(8)是两个输入张量轴‐1长度 的总和(4+4)
X = torch.arange(12, dtype=torch.float32).reshape((3,4))
Y = torch.tensor([[2.0, 1, 4, 3], [1, 2, 3, 4], [4, 3, 2, 1]])
torch.cat((X, Y), dim=0), torch.cat((X, Y), dim=1)
有时,我们想通过逻辑运算符构建二元张量。以X == Y为例:对于每个位置,如果X和Y在该位置相等,则新 张量中相应项的值为1。这意味着逻辑语句X == Y在该位置处为真,否则该位置为0。
X == Y
对张量中的所有元素进行求和,会产生一个单元素张量。
X.sum()
在上面的部分中,我们看到了如何在相同形状的两个张量上执行按元素操作。在某些情况下,即使形状不同, 我们仍然可以通过调用广播机制(broadcastingmechanism)来执行按元素操作。这种机制的工作方式如 下:
1. 通过适当复制元素来扩展一个或两个数组,以便在转换之后,两个张量具有相同的形状;
2. 对生成的数组执行按元素操作。 在大多数情况下,我们将沿着数组中长度为1的轴进行广播,如下例子:
a = torch.arange(3).reshape((3, 1))
b = torch.arange(2).reshape((1, 2))
a, b
由于a和b分别是3×1和1×2矩阵,如果让它们相加,它们的形状不匹配。我们将两个矩阵广播为一个更大 的3×2矩阵,如下所示:矩阵a将复制列,矩阵b将复制行,然后再按元素相加。
a + b
就像在任何其他Python数组中一样,张量中的元素可以通过索引访问。与任何Python数组一样:第一个元素 的索引是0,最后一个元素索引是‐1;可以指定范围以包含第一个元素和最后一个之前的元素。 如下所示,我们可以用[-1]选择最后一个元素,可以用[1:3]选择第二个和第三个元素:
X[-1], X[1:3]
除读取外,我们还可以通过指定索引来将元素写入矩阵。
X[1, 2] = 9
X
before = id(Y)
Y = Y + X
id(Y) == before
这可能是不可取的,原因有两个: 1. 首先,我们不想总是不必要地分配内存。在机器学习中,我们可能有数百兆的参数,并且在一秒内多次 更新所有参数。通常情况下,我们希望原地执行这些更新; 2. 如果我们不原地更新,其他引用仍然会指向旧的内存位置,这样我们的某些代码可能会无意中引用旧 的参数。 幸运的是,执行原地操作非常简单。我们可以使用切片表示法将操作的结果分配给先前分配的数组,例如Y[:] = 。为了说明这一点,我们首先创建一个新的矩阵Z,其形状与另一个Y相同,使用zeros_like来 分配一个全0的块。
Z = torch.zeros_like(Y)
print('id(Z):', id(Z))
Z[:] = X + Y
print('id(Z):', id(Z))
id(Z): 140327634811696
id(Z): 140327634811696
如果在后续计算中没有重复使用X,我们也可以使用X[:] = X + Y或X += Y来减少操作的内存开销
before = id(X)
X += Y
id(X) == before
将深度学习框架定义的张量转换为NumPy张量(ndarray)很容易,反之也同样容易。torch张量和numpy数 组将共享它们的底层内存,就地操作更改一个张量也会同时更改另一个张量。
A = X.numpy()
B = torch.tensor(A)
type(A), type(B)
(numpy.ndarray, torch.Tensor)
要将大小为1的张量转换为Python标量,我们可以调用item函数或Python的内置函数。
a = torch.tensor([3.5])
a, a.item(), float(a), int(a)
(tensor([3.5000]), 3.5, 3.5, 3)
• 深度学习存储和操作数据的主要接口是张量(n维数组)。它提供了各种功能,包括基本数算、广 播、索引、切片、内存节省和转换其他Python对象。
因篇幅问题不能全部显示,请点此查看更多更全内容
Copyright © 2019- yule263.com 版权所有 湘ICP备2023023988号-1
违法及侵权请联系:TEL:199 1889 7713 E-MAIL:2724546146@qq.com
本站由北京市万商天勤律师事务所王兴未律师提供法律服务