【Pytorch】(一)张量(tensor)

网友投稿 932 2022-05-30

三阶多项式拟合正弦函数(numpy, ndarray)

张量

直接由数据得到

由NumPy array得到

【Pytorch】(一)张量(tensor)

由另一个张量得到

初始化随机或常量值张量

三阶多项式拟合正弦函数(numpy, ndarray)

张量

直接由数据得到

由NumPy array得到

由另一个张量得到

初始化随机或常量值张量

张量的属性

张量运算

标准numpy式的索引和切片

连接张量[2]

算术运算

单元素张量

就地(In-place)操作[3]

张量与Numpy 数组

张量 到 NumPy 数组

NumPy 数组 到 张量

三阶多项式拟合正弦函数(pytorch,tensor)

三阶多项式拟合正弦函数(numpy, ndarray)

Numpy是科学计算的框架,不是专门用于计算图、深度学习或梯度的。但我们可以使用numpy实现网络的正向和反向传播。例如,用三阶多项式拟合正弦函数:

# -*- coding: utf-8 -*- import numpy as np import math import time time_start = time.time() # 随机初始化输入和输出数据 x = np.linspace(-math.pi, math.pi, 2000) y = np.sin(x) # 随机初始化权重 a = np.random.randn() b = np.random.randn() c = np.random.randn() d = np.random.randn() learning_rate = 1e-6 for t in range(2000): # 前向传播:计算预测值 y # y = a + b x + c x^2 + d x^3 y_pred = a + b * x + c * x ** 2 + d * x ** 3 # 计算损失 loss = np.square(y_pred - y).sum() if t % 100 == 99: print(f'迭代次数:{t+1},损失值:{loss}') # 反向传播:计算损失关于a, b, c, d 的梯度 grad_y_pred = 2.0 * (y_pred - y) grad_a = grad_y_pred.sum() grad_b = (grad_y_pred * x).sum() grad_c = (grad_y_pred * x ** 2).sum() grad_d = (grad_y_pred * x ** 3).sum() # 梯度下降更新权重 a -= learning_rate * grad_a b -= learning_rate * grad_b c -= learning_rate * grad_c d -= learning_rate * grad_d time_end = time.time() print('消耗时间:', time_end - time_start, 's') print(f'结果: y = {a} + {b} x + {c} x^2 + {d} x^3')

输出:

... ... 迭代次数:2000,损失值:9.68922650914574 消耗时间: 0.6189007759094238 s 结果: y = -0.03123761638932383 + 0.8577752449653959 x + 0.0053890086233387485 x^2 + -0.09347752370202965 x^3

既然numpy也可以实现网络的正向和反向传播,那为什么还要pytorch呢?

Numpy是一个很棒的框架,但它不能利用GPU来加速计算,这使得它不适用于大计算量的深度学习。而pytorch的一种数据结构——张量(tensor)则可以在GPU或其他硬件加速器上运行。另外,pytorch的自动求导机制,能自动的帮我们把反向传播全部计算好。

下面,我们将进入本文的主题:张量(tensor)。

张量

张量(tensor)是一种特殊的数据结构,与数组和矩阵非常相似。在pytorch中,我们使用张量对模型的输入和输出以及模型的参数进行编码。张量与NumPy的数组很相似,只是它可以在GPU或其他硬件加速器上运行。

In[2]: import torch In[3]: import numpy as np

直接由数据得到

使用torch.tensor()

In[4]: data = [[1, 2],[3, 4]] In[5]: x_data = torch.tensor(data) In[6]: x_data Out[6]: tensor([[1, 2], [3, 4]])

由NumPy array得到

使用torch.from_numpy()

np_array = np.array(data) x_np = torch.from_numpy(np_array) x_np Out[9]: tensor([[1, 2], [3, 4]], dtype=torch.int32)

由另一个张量得到

除非显式重写,否则新的张量将保留参数张量的属性(形状、数据类型)。

In[10]: x_ones = torch.ones_like(x_data) In[11]: x_ones Out[11]: tensor([[1, 1], [1, 1]]) In[12]: x_rand = torch.rand_like(x_data, dtype=torch.float) # 类型重写 In[13]: x_rand Out[13]: tensor([[0.2792, 0.9185], [0.5906, 0.8662]]) In[14]: x_rand.dtype Out[14]: torch.float32

初始化随机或常量值张量

shape是表示张量维度的元组。在下面的函数中,它决定了输出张量的维数。

In[15]: shape = (2,3,) ...: rand_tensor = torch.rand(shape) ...: ones_tensor = torch.ones(shape) ...: zeros_tensor = torch.zeros(shape) In[16]: rand_tensor Out[16]: tensor([[0.3380, 0.0584, 0.5423], [0.6003, 0.6216, 0.9982]]) In[17]: ones_tensor Out[17]: tensor([[1., 1., 1.], [1., 1., 1.]]) In[18]: zeros_tensor Out[18]: tensor([[0., 0., 0.], [0., 0., 0.]])

张量的属性

张量属性描述它们的形状(shape)、数据类型(dtype)和存储它们的设备(device)。

In[19]: tensor = torch.rand(3,4) In[20]: tensor.shape Out[20]: torch.Size([3, 4]) In[21]: tensor.dtype Out[21]: torch.float32 In[22]: tensor.device Out[22]: device(type='cpu')

张量运算

pytorch对张量有超过100种运算操作(传送门)。每个操作都可以在GPU上运算(速度通常高于CPU)。

默认情况下,创建张量都是在CPU上运算。我们需要使用.to显式地将张量移动到GPU中(在GPU可用的前提下)。但请记住,跨设备复制大量张量需要耗费许多时间和内存!

In[23]: tensor Out[23]: tensor([[0.0061, 0.1010, 0.5185, 0.8282], [0.7172, 0.8436, 0.0652, 0.0033], [0.2006, 0.7263, 0.8957, 0.8063]]) In[25]: # 如果GPU可用,将张量移动到GPU ...: if torch.cuda.is_available(): ...: tensor = tensor.to('cuda') In[26]: tensor Out[26]: tensor([[0.0061, 0.1010, 0.5185, 0.8282], [0.7172, 0.8436, 0.0652, 0.0033], [0.2006, 0.7263, 0.8957, 0.8063]], device='cuda:0')

下面列举一些简单的操作。如果你熟悉NumPy API,您会发现Tensor API很容易使用。

标准numpy式的索引和切片

In[33]: tensor = torch.rand(4, 4) In[34]: tensor Out[34]: tensor([[0.1227, 0.2580, 0.4331, 0.9736], [0.3351, 0.5426, 0.5793, 0.1816], [0.7569, 0.6747, 0.0966, 0.9883], [0.1359, 0.1780, 0.0796, 0.9142]]) In[35]: tensor[0] # 第一行 Out[35]: tensor([0.1227, 0.2580, 0.4331, 0.9736]) In[36]: tensor[:, 0] #第一列 Out[36]: tensor([0.1227, 0.3351, 0.7569, 0.1359]) In[37]: tensor[..., -1] # 最后一列 Out[37]: tensor([0.9736, 0.1816, 0.9883, 0.9142])

连接张量[2]

连接张量有两种方法:torch.cat()或torch.stack()

In[39]: tensor = torch.ones(2, 3) In[40]: tensor Out[40]: tensor([[1., 1., 1.], [1., 1., 1.]])

首先看一下torch.cat():

dim=1时,形状变为[2, 6]

In[41]: t1 = torch.cat([tensor, tensor, tensor], dim=1) In[42]: t1 Out[42]: tensor([[1., 1., 1., 1., 1., 1., 1., 1., 1.], [1., 1., 1., 1., 1., 1., 1., 1., 1.]])

dim=0时,形状变为[6, 2]

In[43]: t0 = torch.cat([tensor, tensor, tensor], dim=0) In[44]: t0 Out[44]: tensor([[1., 1., 1.], [1., 1., 1.], [1., 1., 1.], [1., 1., 1.], [1., 1., 1.], [1., 1., 1.]])

与torch.cat()不同,torch.stack()是沿着一个新维度对输入张量序列进行连接。 序列中所有的张量都应该为相同形状。

浅显说法:就是把多个2维的张量凑成一个3维的张量;多个3维的凑成一个4维的张量……以此类推,也就是在增加新的维度进行堆叠。

例子:

准备2个[3,3]的张量数据。

In[45]: T1 = torch.tensor([[1, 2, 3], ...: [4, 5, 6], ...: [7, 8, 9]]) ...: T2 = torch.tensor([[10, 20, 30], ...: [40, 50, 60], ...: [70, 80, 90]])

dim=0时,形状变为[2, 3, 3]

In[48]: stack_0=torch.stack((T1,T2),dim=0) In[49]: stack_0 Out[49]: tensor([[[ 1, 2, 3], [ 4, 5, 6], [ 7, 8, 9]], [[10, 20, 30], [40, 50, 60], [70, 80, 90]]]) In[50]: stack_0.shape Out[50]: torch.Size([2, 3, 3])

dim=1时,形状变为[3, 2, 3]

In[51]: stack_1=torch.stack((T1,T2),dim=1) In[52]: stack_1 Out[52]: tensor([[[ 1, 2, 3], [10, 20, 30]], [[ 4, 5, 6], [40, 50, 60]], [[ 7, 8, 9], [70, 80, 90]]]) In[53]: stack_1.shape Out[53]: torch.Size([3, 2, 3])

dim=2时,形状变为[3, 3, 2]

In[54]: stack_2=torch.stack((T1,T2),dim=2) In[55]: stack_2 Out[55]: tensor([[[ 1, 10], [ 2, 20], [ 3, 30]], [[ 4, 40], [ 5, 50], [ 6, 60]], [[ 7, 70], [ 8, 80], [ 9, 90]]]) In[56]: stack_2.shape Out[56]: torch.Size([3, 3, 2])

算术运算

下面是计算两个张量之间的矩阵乘法的方法。y1、y2、y3将具有相同的值

In[62]: tensor=torch.ones(3,3) In[63]: y1 = tensor @ tensor.t() #.t():矩阵转置 In[64]: y2 = tensor.matmul(tensor.t()) In[65]: y3 = torch.rand_like(tensor) In[66]: torch.matmul(tensor, tensor.t(), out=y3) Out[66]: tensor([[3., 3., 3.], [3., 3., 3.], [3., 3., 3.]]) In[67]: y1 Out[67]: tensor([[3., 3., 3.], [3., 3., 3.], [3., 3., 3.]]) In[68]: y2 Out[68]: tensor([[3., 3., 3.], [3., 3., 3.], [3., 3., 3.]]) In[69]: y3 Out[69]: tensor([[3., 3., 3.], [3., 3., 3.], [3., 3., 3.]])

下面将计算逐元素的乘积。z1、z2、z3将具有相同的值

In[70]: z1 = tensor * tensor In[71]: z2 = tensor.mul(tensor) In[72]: z3 = torch.rand_like(tensor) In[73]: torch.mul(tensor, tensor, out=z3); In[74]: z1 Out[74]: tensor([[1., 1., 1.], [1., 1., 1.], [1., 1., 1.]]) In[75]: z2 Out[75]: tensor([[1., 1., 1.], [1., 1., 1.], [1., 1., 1.]]) In[77]: z3 Out[77]: tensor([[1., 1., 1.], [1., 1., 1.], [1., 1., 1.]])

单元素张量

如果有一个单元素张量,例如将张量的所有值加起来得到一个值,那么可以使用item()将其转换为Python数值:

In[82]: agg = tensor.sum() In[83]: agg Out[83]: tensor(9.) In[84]: agg_item = agg.item() In[85]: agg_item Out[85]: 9.0 In[86]: type(agg_item) Out[86]: float In[87]: type(agg) Out[87]: torch.Tensor

就地(In-place)操作[3]

就地操作是直接更改给定张量的内容而不会为变量分配新的内存进行复制。

将结果存储到操作数中的操作称为就地调用。它们由一个后缀表示。例如:x.copy_(y), x.t_()。这种操作将更改x。

In[88]: tensor Out[88]: tensor([[1., 1., 1.], [1., 1., 1.], [1., 1., 1.]]) In[89]: tensor.add_(5) Out[89]: tensor([[6., 6., 6.], [6., 6., 6.], [6., 6., 6.]]) In[90]: tensor Out[90]: tensor([[6., 6., 6.], [6., 6., 6.], [6., 6., 6.]])

注意:

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

张量与Numpy 数组

在CPU的tensor和NumPy的array会共享其底层内存,即更改其中一个的同时另一个也会被更改。

张量 到 NumPy 数组

In[91]: t = torch.ones(5) In[92]: n = t.numpy() In[93]: t Out[93]: tensor([1., 1., 1., 1., 1.]) In[94]: n Out[94]: array([1., 1., 1., 1., 1.], dtype=float32)

张量的改变将导致对应NumPy数组的改变。

In[95]: t.add_(1) Out[95]: tensor([2., 2., 2., 2., 2.]) In[96]: t Out[96]: tensor([2., 2., 2., 2., 2.]) In[97]: n Out[97]: array([2., 2., 2., 2., 2.], dtype=float32)

NumPy 数组 到 张量

In[99]: n = np.ones(5) In[99]: t = torch.from_numpy(n) In[100]: np.add(n, 1, out=n) Out[100]: array([2., 2., 2., 2., 2.]) In[101]: t Out[101]: tensor([2., 2., 2., 2., 2.], dtype=torch.float64) In[102]: n Out[102]: array([2., 2., 2., 2., 2.])

三阶多项式拟合正弦函数(pytorch,tensor)

下面我们基于PyTorch的张量,将三阶多项式拟合正弦函数。

# -*- coding: utf-8 -*- import torch import math import time time_start = time.time() dtype = torch.float device = torch.device("cpu") #device = torch.device("cuda:0") # 取消这一行注释以在GPU上运行 # 随机初始化输入和输出数据 x = torch.linspace(-math.pi, math.pi, 2000, device=device, dtype=dtype) y = torch.sin(x) # 随机初始化权重 a = torch.randn((), device=device, dtype=dtype) b = torch.randn((), device=device, dtype=dtype) c = torch.randn((), device=device, dtype=dtype) d = torch.randn((), device=device, dtype=dtype) learning_rate = 1e-6 for t in range(2000): # 前向传播:计算预测值 y y_pred = a + b * x + c * x ** 2 + d * x ** 3 # 计算损失 loss = (y_pred - y).pow(2).sum().item() if t % 100 == 99: print(f'迭代次数:{t + 1},损失值:{loss}') # 反向传播:计算损失关于a, b, c, d 的梯度 grad_y_pred = 2.0 * (y_pred - y) grad_a = grad_y_pred.sum() grad_b = (grad_y_pred * x).sum() grad_c = (grad_y_pred * x ** 2).sum() grad_d = (grad_y_pred * x ** 3).sum() # 梯度下降更新权重 a -= learning_rate * grad_a b -= learning_rate * grad_b c -= learning_rate * grad_c d -= learning_rate * grad_d time_end = time.time() print('消耗时间:', time_end - time_start, 's') print(f'Result: y = {a.item()} + {b.item()} x + {c.item()} x^2 + {d.item()} x^3')

使用CPU,输出:

... ... 迭代次数:2000,损失值:14.256892204284668 消耗时间: 0.8661777973175049 s Result: y = 0.05625741183757782 + 0.8070318698883057 x + -0.00970533862709999 x^2 + -0.08625971525907516 x^3

使用GPU,输出:

... ... 迭代次数:2000,损失值:9.254937171936035 消耗时间: 8.750219345092773 s Result: y = -0.000127201754366979 + 0.8364022374153137 x + 2.19428966374835e-05 x^2 + -0.0904374048113823 x^3

???说好的GPU加速呢?

用电锯切菜能快么,电锯是用来砍树的。同样道理,GPU在大规模网络才有明显的加速。

GPU比CPU慢的原因大致为[4]:

数据传输会有很大的开销,而GPU处理数据传输要比CPU慢,而GPU的专长矩阵计算在小规模神经网络中无法明显体现出来。

下面以单纯的矩阵乘法做对比:

import torch import time a = torch.ones(10000, 1000) b = torch.ones(1000, 10000) t0 = time.time() c = torch.matmul(a, b) t1 = time.time() print('CPU:', t1 - t0, 's') device = torch.device('cuda') a = a.to(device) b = b.to(device) t0 = time.time() c = torch.matmul(a, b) t1 = time.time() print('GPU:', t1 - t0, 's')

输出:

CPU: 2.434323310852051 s GPU: 0.5386180877685547 s

这不,快了。

参考:

[1] https://pytorch.org/tutorials/beginner/basics/tensorqs_tutorial.html

[2] https://blog.csdn.net/xinjieyuan/article/details/105205326

[3]https://zhuanlan.zhihu.com/p/344455805

[4]https://blog.csdn.net/qq_43673118/article/details/90305340

Numpy pytorch

版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。

上一篇:Opengl es2.0 学习笔记(三)shader的使用
下一篇:【云驻共创】低代码平台关键能力之数据编排
相关文章