Numpy 和 Tensor
Numpy 和 Tensor文章目录Numpy 和 Tensor1、Numpy 与 Tensor1.1、Tensor 概述1.2、创建 Tensor1.3、修改 Tensor 形状1.4、索引操作1.5、广播机制1.6、逐元素操作1.7、归并操作1.8、比较操作1.9、矩阵操作1.10、Pytorch 与 Numpy 比较2、Tensor 与 Autograd2.1、自动求导要点2.2、计算图3、
Numpy 和 Tensor
文章目录
1、Numpy 与 Tensor
Tensor,它可以是零维(又称为标量或一个数)、一维、二维及多维的数组。
其自称为神经网络界的 Numpy, 它与 Numpy 相似,它们共享内存,它们之间的转换非常方便和高效。
不过它们也有不同之处,最大的区别就是 Numpy 会把 ndarray 放在 CPU 中加速运算,而由 torch 产生的 tensor 会放在 GPU 中加速运算(假设当前环境有GPU)。
1.1、Tensor 概述
- 对 tensor 的操作很多,从接口的角度来划分,可以分为两类:
- torch.function,如 torch.sum、torch.add 等,
- tensor.function,如 tensor.view、tensor.add 等。
- 如果从修改方式的角度,可以分为以下两类:
- 不修改自身数据,如 x.add(y),x 的数据不变,返回一个新的 tensor;
- 修改自身数据,如 x.add_(y)(运行符带下划线后缀),运算结果存在 x 中,x 被修改。
1.2、创建 Tensor
- 注意 torch.Tensor 与 torch.tensor 的几点区别:
- torch.Tensor 是 torch.empty 和 torch.tensor 之间的一种混合,但是,当传入数据时,torch.Tensor 使用全局默认 dtype(FloatTensor),torch.tensor 从数据中推断数据类型;
- torch.tensor(1) 返回一个固定值 1,而 torch.Tensor(1) 返回一个大小为 1 的张量,它是随机初始化的值。
1.3、修改 Tensor 形状
-
torch.view 与 torch.reshape 的异同
- reshape() 可以由 torch.reshape(),也可由 torch.Tensor.reshape() 调用。view() 只可由 torch.Tensor.view() 来调用;
- 对于一个将要被 view 的 Tensor,新的 size 必须与原来的 size 与 stride 兼容。否则,在 view 之前必须调用contiguous() 方法;
- 同样也是返回与 input 数据量相同,但形状不同的 tensor。若满足 view 的条件,则不会 copy,若不满足,则会copy;
- 如果您只想重塑张量,请使用 torch.reshape。 如果您还关注内存使用情况并希望确保两个张量共享相同的数据,请使用 torch.view。
1.4、索引操作
1.5、广播机制
同 Numpy。
1.6、逐元素操作
这些操作均创建新的 tensor,如果需要就地操作,可以使用这些方法的下划线版本,例如 abs_。
1.7、归并操作
归并操作顾名思义,就是对输入进行归并或合计等操作,这类操作的输入输出形状一般不相同,而且往往是输入大于输出形状。归并操作可以对整个 tensor,也可以沿着某个维度进行归并。
- 归并操作一般涉及一个 dim 参数,指定沿哪个维进行归并。另一个参数是 keepdim,说明输出结果中是否保留维度1,缺省情况是 False,即不保留。
1.8、比较操作
1.9、矩阵操作
- torch 的 dot 与 Numpy 的 dot 有点不同,torch 中 dot 对两个为 1D 张量进行点积运算,Numpy 中的 dot 无此限制;
- mm 是对 2D 的矩阵进行点积,bmm 对含 batch 的 3D 进行点积运算;
- 转置运算会导致存储空间不连续,需要调用 contiguous 方法转为连续。
1.10、Pytorch 与 Numpy 比较
2、Tensor 与 Autograd
-
在神经网络中,一个重要内容就是进行参数学习,而参数学习离不开求导,Pytorch 是如何进行求导的呢?
torch.autograd 包就是用来自动求导的:
autograd 包为张量上所有的操作提供了自动求导功能,而 torch.Tensor 和 torch.Function 为 autograd 上的两个核心类,他们相互连接并生成一个有向非循环图。
2.1、自动求导要点
- 创建叶子节点(leaf node)的 tensor,使用 requires_grad 参数指定是否记录对其的操作,以便之后利用backward() 方法进行梯度求解。requires_grad 参数缺省值为 False,如果要对其求导需设置为 True,与之有依赖关系的节点自动变为 True。
- 可利用 requires_grad_() 方法修改 tensor 的 requires_grad 属性。可以调用 .detach() 或 with torch.no_grad():将不再计算张量的梯度,跟踪张量的历史记录。这点在评估模型、测试模型阶段常常使用。
- 通过运算创建的 tensor(即非叶子节点),会自动被赋于 grad_fn 属性。该属性表示梯度函数。叶子节点的 grad_fn为 None。
- 最后得到的 tensor 执行 backward() 函数,此时自动计算各变量的梯度,并将累加结果保存到各自的 grad 属性中。计算完成后,非叶子节点的梯度自动释放。
- backward() 函数接受参数,该参数应和调用 backward() 函数的 Tensor 的维度相同,或者是可以 broadcast 的维度。如果求导的 tensor 为标量(即一个数字),backward 中参数可省略。
- 反向传播的中间缓存会被清空,如果需要进行多次反向传播,需要指定 backward 中的参数 retain_graph=True。多次反向传播时,梯度是累加的。
- 非叶子节点的梯度 backward 调用后即被清空。
- 可以通过用 torch.no_grad() 包裹代码块来阻止 autograd 去跟踪那些标记为 .requesgrad=True 的张量的历史记录。这步在测试阶段经常使用。
整个过程中,Pytorch 采用计算图的形式进行组织,该计算图为动态图,它的计算图在每次前向传播时,将重新构建。
2.2、计算图
-
计算图是一种有向无环图像,用图形方式表示算子与变量之间的关系,直观高效。如下图所示,圆形表示变量,矩阵表示算子。
如表达式:z = wx + b,可写成两个表示式:y = wx,则 z = y + b,
-
其中 x、w、b 为变量,是用户创建的变量,不依赖于其他变量,故又称为叶子节点。
为计算各叶子节点的梯度,需要把对应的张量参数 requires_grad 属性设置为True,这样就可自动跟踪其历史记录。
-
y、z 是计算得到的变量,非叶子节点,z 为根节点。
-
mul 和 add 是算子(或操作或函数)。
由这些变量及算子,就构成一个完整的计算过程(或前向传播过程)。
-
我们的目标是更新各叶子节点的梯度,根据复合函数导数的链式法则,不难算出各叶子节点的梯度。
∂ z ∂ x = ∂ z ∂ y ∂ y ∂ x = w \frac{\partial \mathrm{z}}{\partial \mathrm{x}}=\frac{\partial \mathrm{z}}{\partial \mathrm{y}} \frac{\partial \mathrm{y}}{\partial \mathrm{x}}=\mathrm{w} ∂x∂z=∂y∂z∂x∂y=w
∂ z ∂ w = ∂ z ∂ y ∂ y ∂ w = x \frac{\partial \mathrm{z}}{\partial \mathrm{w}}=\frac{\partial \mathrm{z}}{\partial \mathrm{y}} \frac{\partial \mathrm{y}}{\partial \mathrm{w}}=\mathrm{x} ∂w∂z=∂y∂z∂w∂y=x
∂ z ∂ b = 1 \frac{\partial \mathrm{z}}{\partial \mathrm{b}}=1 ∂b∂z=1
Pytorch 调用 backward(),将自动计算各节点的梯度,这是一个反向传播过程,这个过程可用下图表示。
在反向传播过程中,autograd 沿着上图,从当前根节点 z 反向溯源,利用导数链式法则,计算所有叶子节点的梯度,其梯度值将累加到 grad 属性中。对非叶子节点的计算操作(或 function)记录在 grad_fn 属性中,叶子节点的 grad_fn 值为 None。
3、使用 Numpy 实现机器学习
-
首先,我们用最原始的 Numpy 实现有关回归的一个机器学习任务,不用 Pytorch 中的包或类。这种方法代码可能多一点,但每一步都是透明的,有利于理解每步的工作原理。
主要步骤包括:
- 首先,是给出一个数组 x,然后基于表达式:y=3x^2+2,加上一些噪音数据到达另一组数据 y;
- 然后,构建一个机器学习模型,学习表达式 y=wx^2+b 的两个参数 w,b。利用数组 x,y 的数据为训练数据;
- 最后,采用梯度梯度下降法,通过多次迭代,学习到 w、b 的值。
-
导入需要的库
import numpy as np from matplotlib import pyplot as plt
-
生成输入数据 x 及目标数据 y
设置随机数种子,生成同一个份数据,以便用多种方法进行比较:
np.random.seed(100) x = np.linspace(-1, 1, 100).reshape(100,1) y = 3*np.power(x, 2) +2+ 0.2*np.random.rand(x.size).reshape(100,1)
-
查看 x,y 数据分布情况
# 画图 plt.scatter(x, y) plt.show()
-
初始化权重参数
# 随机初始化参数 w1 = np.random.rand(1,1) b1 = np.random.rand(1,1)
-
训练模型
定义损失函数,假设批量大小为 100:
L o s s = 1 2 ∑ i = 1 100 ( w x i 2 + b − y i ) 2 Loss=\frac{1}{2}\sum^{100}_{i=1}{(wx_i^2+b-y_i)^2} Loss=21i=1∑100(wxi2+b−yi)2
对损失函数求导:
∂ L o s s ∂ w = ∑ i = 1 100 ( w x i 2 + b − y i ) x i 2 \frac{\partial{Loss}}{\partial{w}}=\sum^{100}_{i=1}{(wx_i^2+b-y_i)x_i^2} ∂w∂Loss=i=1∑100(wxi2+b−yi)xi2∂ L o s s ∂ b = ∑ i = 1 100 ( w x i 2 + b − y i ) \frac{\partial{Loss}}{\partial{b}}=\sum^{100}_{i=1}{(wx_i^2+b-y_i)} ∂b∂Loss=i=1∑100(wxi2+b−yi)
利用梯度下降法学习参数,学习率为 l r lr lr:
w 1 − = l r ∗ ∂ L o s s ∂ w w_1-=lr*\frac{\partial{Loss}}{\partial{w}} w1−=lr∗∂w∂Lossb 1 − = l r ∗ ∂ L o s s ∂ b b_1-=lr*\frac{\partial{Loss}}{\partial{b}} b1−=lr∗∂b∂Loss
lr =0.001 # 学习率 for i in range(800): # 前向传播 y_pred = np.power(x,2)*w1 + b1 # 定义损失函数 loss = 0.5 * (y_pred - y) ** 2 loss = loss.sum() #计算梯度 grad_w=np.sum((y_pred - y)*np.power(x,2)) grad_b=np.sum((y_pred - y)) #使用梯度下降法,是loss最小 w1 -= lr * grad_w b1 -= lr * grad_b
-
可视化结果
plt.plot(x, y_pred,'r-',label='predict') plt.scatter(x, y,color='blue',marker='o',label='true') # true data plt.xlim(-1,1) plt.ylim(2,6) plt.legend() plt.show() print(w1,b1)
4、使用 Tensor 及 antograd 实现机器学习
使用 Pytorch 的自动求导的一个包 antograd,利用这个包及对应的 Tensor,便可利用自动反向传播来求梯度,无需手工计算梯度。
-
导入需要的库
import torch as t from matplotlib import pyplot as plt
-
生成训练数据,并可视化数据分布情况
t.manual_seed(100) dtype = t.float #生成x坐标数据,x为tenor,需要把x的形状转换为100x1 x = t.unsqueeze(t.linspace(-1, 1, 100), dim=1) #生成y坐标数据,y为tenor,形状为100x1,另加上一些噪音 y = 3*x.pow(2) +2+ 0.2*t.rand(x.size()) # 画图,把tensor数据转换为numpy数据 plt.scatter(x.numpy(), y.numpy()) plt.show()
-
初始化权重参数
# 随机初始化参数,参数w,b为需要学习的,故需requires_grad=True w = t.randn(1,1, dtype=dtype,requires_grad=True) b = t.zeros(1,1, dtype=dtype, requires_grad=True)
-
训练模型
lr =0.001 # 学习率 for ii in range(800): # 前向传播,并定义损失函数loss y_pred = x.pow(2).mm(w) + b loss = 0.5 * (y_pred - y) ** 2 loss = loss.sum() # 自动计算梯度,梯度存放在grad属性中 loss.backward() # 手动更新参数,需要用torch.no_grad(),使上下文环境中切断自动求导的计算 with t.no_grad(): w -= lr * w.grad b -= lr * b.grad # 梯度清零 w.grad.zero_() b.grad.zero_()
-
可视化训练结果
plt.plot(x.numpy(), y_pred.detach().numpy(),'r-',label='predict')#predict plt.scatter(x.numpy(), y.numpy(),color='blue',marker='o',label='true') # true data plt.xlim(-1,1) plt.ylim(2,6) plt.legend() plt.show() print(w, b)
更多推荐





所有评论(0)