深度学习--tensor(数据转换、运算操作、自动微分)

一、数据转换

1.1常见数据

tensor和numpy都是很常见的数据格式。两者相互转换后能够进行更丰富的操作。

1.2张量转numpy

(1)浅拷贝--数据共享

        API:data.numpy()

代码示例:

# 张量转换为numpy数组--浅拷贝
import torch
data_tensor = torch.tensor([1,2,3])
data_numpy = data_tensor.numpy()
print(type(data_tensor))
print(type(data_numpy))

#查看是否数据共享
data_tensor[0] = 100
print(data_tensor)
print(data_numpy)

结果:

<class 'torch.Tensor'> <class 'numpy.ndarray'>

tensor([100, 2, 3])

[100 2 3]

(2)深拷贝--单独备份

        API:data.numpy().copy()

代码示例:

# 张量转换为numpy数组--浅拷贝
import torch
data_tensor = torch.tensor([1,2,3])
data_numpy = data_tensor.numpy().copy()
print(type(data_tensor))
print(type(data_numpy))

#查看是否数据共享
data_tensor[0] = 100
print(data_tensor)
print(data_numpy)

结果:

<class 'torch.Tensor'> <class 'numpy.ndarray'>

tensor([100, 2, 3])

[1 2 3]

1.3numpy转张量

(1)浅拷贝

        API:torch.from_numpy(data)

代码示例:

import torch
import numpy as np
data_numpy = np.array([1,2,3])
data_tensor = torch.from_numpy(data_numpy)
print(type(data_numpy))
print(type(data_tensor))

data_numpy[2] = 20
print(data_numpy)
print(data_tensor)

结果:

<class 'numpy.ndarray'>

<class 'torch.Tensor'>

[ 1 2 20]

tensor([1, 2, 20])

(2)深拷贝

        API:torch.tensor(data)

代码示例:

import torch
import numpy as np
data_numpy = np.array([1,2,3])
data_tensor = torch.tensor(data_numpy)
print(type(data_numpy))
print(type(data_tensor))

data_numpy[2] = 20
print(data_numpy)
print(data_tensor)

结果:

<class 'numpy.ndarray'>

<class 'torch.Tensor'>

[ 1 2 20]

tensor([1, 2, 3])

二、常见操作

2.1获取单个元素值

        API:data.item()

注意:data的原数据个数必须为1,否则报错

代码示例:

import torch
data = torch.tensor([[[89]]])
print(data.item())

结果:

89

2.2元素值运算(广播)

常见的加减乘除次方取反开方等各种操作,带有_的方法则会替换原始值。

API:data.add()  data.add_()

带下划线则数据共享

代码示例:

# 运算
data = torch.tensor([[[89],[90],[23]]])
print(data.add(2))
print(data.add_(2))

结果:

tensor([[[91], [92], [25]]])

tensor([[[91], [92], [25]]])

其他类似。

2.3阿达玛积(点积)

        两个形状相同的矩阵或张量对应位置的元素相乘。它不用乘起来相加,只需要对应位置相乘放在相应位置即可。

API:  (1)data1 * data2

           (2)data1.mul(data2)

代码示例:

# 阿达玛积
data1 = torch.tensor([[45],[0]])
data2 = torch.tensor([[1],[90]])
print(data1 * data2)
print(data1.mul(data2))

结果:

tensor([[45], [ 0]])

tensor([[45], [ 0]])

2.4Tensor相乘

        矩阵乘法运算要求如果第一个矩阵的shape是 (N, M),那么第二个矩阵 shape必须是 (M, P),最后两个矩阵点积运算的shape为 (N, P)。

        API:(1)data1 @ data2

                 (2)data1.matmul( data2 )

代码示例:

# 矩阵乘法
data1 = torch.tensor([[[45,1]]])
data2 = torch.tensor([[[1],
                       [89]
                      ]])
print(data1 @ data2)
print(data1.matmul(data2))

结果:

tensor([[[134]]])

tensor([[[134]]])

2.5形状改变

        2.5.1reshape--可用于内存不连续状况

                reshape可以将张量转换为不同的形状,但要保证转换后的元素个数与原张量元素数量相同。

代码示例:

import torch
data1= torch.tensor([[1,2,3],[4,5,6]])
print(data)
data_new = data1.reshape(3,-1)
print(data_new)

注:填写-1,表示自动计算。

结果:

tensor([[1, 2, 3], [4, 5, 6]])

tensor([[1, 2], [3, 4], [5, 6]])

        2.5.2view--用于内存连续的状况

                在pytorch中修改形状用的较多,但是要保证张量在内存中是连续的,否则不能执行。

判断是否是连续:API----->:data.is_contiguous()\

代码示例:

import numpy as np
data1 = np.random.randint(0,10,(1,4))
data1_tensor = torch.from_numpy(data1)
print(data1_tensor.is_contiguous())
print(data1_tensor)
if data1_tensor.is_contiguous():
    print(data1_tensor.view(2,2))

结果:

True

tensor([[2, 0, 8, 9]], dtype=torch.int32)

tensor([[2, 0], [8, 9]], dtype=torch.int32)

        2.5.3transpose

        transpose 用于交换张量的两个维度,注意,是2个维度,它返回的是原张量的视图。

        api :torch.transpose(data,dim0,dim1)

代码示例:

# transpose维度交换
x = torch.randint(0,100,(1,3,4))
y = torch.transpose(x,1,2)
# 这里表示下标为1的维度与下标为2的维度进行交换
print(x)
print(y)

结果:

tensor([[[86, 26, 44, 93],

[ 5, 28, 39, 57],

[53, 47, 69, 3]]])

tensor([[[86, 5, 53],

[26, 28, 47],

[44, 39, 69],

[93, 57, 3]]])

        2.5.4permute

        permute更常用,它也是用于维度交换的。因为permute交换可以不局限于只能交换两个维度,它可以是多个维度同时交换。

        API:data.permute(dims)  

        dims:表示的是一个元组,里面放整数

代码示例:

x = torch.randint(0,100,(2,3,4))
y = x.permute(2,1,0)
print(x,x.shape)
print(y,y.shape)

结果:

tensor([[[39, 71, 42, 48],

[49, 43, 36, 9],

[91, 70, 70, 13]],

[[23, 54, 83, 36],

[67, 34, 26, 61],

[52, 48, 65, 25]]]) torch.Size([2, 3, 4])

tensor([[[39, 23], [49, 67], [91, 52]],

[[71, 54], [43, 34], [70, 48]],

[[42, 83], [36, 26], [70, 65]],

[[48, 36], [ 9, 61], [13, 25]]]) torch.Size([4, 3, 2])

        2.5.5升维和降维

在后续的网络学习中,升维和降维是常用操作,需要掌握。

  • unsqueeze:用于在指定位置插入一个大小为 1 的新维度。

  • squeeze:用于移除所有大小为 1 的维度,或者移除指定维度的大小为 1 的维度。

代码示例:

# 升维
data = torch.randint(0,100,(2,3,1))
data_new = data.unsqueeze(0)
print(data.shape)
print(data_new.shape)

# 降维
data = torch.randint(0,100,(1,2,3,1))
data_new = data.squeeze(0).squeeze(-1)
print(data.shape)
print(data_new.shape)

结果:

torch.Size([ 2, 3]) torch.Size([1,2, 3,1])

torch.Size([1, 2, 3, 1]) torch.Size([2, 3])

2.6广播机制

        这里只阐述一些基本的简单概念。

        广播机制通过自动扩展较小维度的张量,使其与较大维度的张量兼容,从而实现按元素计算。

        只有对维度为1的张量才有可能进行广播,实现对张量的复制 。

2.6.1 广播机制规则

广播机制需要遵循以下规则:

  • 每个张量的维度至少为1

  • 满足右对齐

代码示例:

# 广播
data1 = torch.tensor([1,2,3])
data2 = torch.tensor([[1],[2],[3]])
print(data1 + data2)

结果:

 tensor([[2, 3, 4],

[3, 4, 5],

[4, 5, 6]])

2.7并行化

        主要是查看cpu的线程数,然后根据自身设备的线程数来确定最好的参数以此获得最优计算结果。

2.7.1查看线程数

       api: torch.get_num_threads()

2.7.2设置线程数

        API:torch.set_num_threads()

        注意:设置线程数时,确保考虑到你的 CPU 核心数和其他进程的资源需求,以获得最佳性能。

三、自动微分(重点)

        自动微分模块torch.autograd的主要功能是计算张量操作的梯度,具有自动求导功能。

         自动微分模块是构成神经网络训练的必要模块,可以实现网络权重参数的更新,使得反向传播算法的实现变得简单而高效。

3.1概念

一些重要的专业名词,了解清楚之后能加速理解之后的神经网络相关操作。

(1)张量:张量是一个多维数组,一般由用户直接创建,主要用于存储和操作数据内容。在这个版块里,有一个关键的属性,requires_grad:需要梯度?

        属性requires_grad决定是否对其进行梯度计算。默认是 False,如需计算梯度则设置为True。

(2)计算图:就是由张量、中间值、最后标量创建出来的一个大致图。在这个例子中,x,x是张量叶子结点,z是中间值,loss复制将z标量化。

(3)前向计算:就类似于已知x,y,z之间的数学关系,然后从计算图的由上到下运行的方式。

(4)反向传播:就类似于已知x,y,z之间的数学关系,然后从计算图的由下到上运行的方式。然后一般使用了反向传播data.backward()都会自动计算叶子节点的梯度。

(5)叶子节点:用户直接创建的张量,并且它的 requires_grad=True;这些张量是计算图的起始点,通常作为模型参数或输入变量。

        默认情况下,叶子节点的梯度不会自动清零,需要显式调用 optimizer.zero_grad() 或 x.grad.zero_() 清除。

(6)梯度:通过data.grad()访问得到,这些梯度用于优化模型参数,以最小化损失函数。

3.2计算梯度

主要的API是tensor.backward()

        3.2.1标量梯度计算

                这里的标量要设置为浮点数

代码示例:

import torch
x = torch.tensor(2.0,requires_grad=True)
y = pow(x,2)
y.backward()
print(x.grad)

结果:

tensor( 4.)

        3.2.2向量梯度计算

这里需要转换,把向量转换为标量,一般是 中间变量.sum()  / .mean()等等

import torch
x = torch.tensor([2.0,3.0,4.0],requires_grad=True)
y = pow(x,2)
z = y.sum()
z.backward()
print(x.grad)

结果:

 tensor([4., 6., 8.])

        3.2.3多标量梯度计算

等同于标量梯度,只不过标量数变多了。

import torch
x = torch.tensor(2.0,requires_grad=True)
n= torch.tensor(240.0,requires_grad=True)

y = pow(x,2)+3*n
y.backward()
print(x.grad,n.grad)

结果:

tensor(4.) tensor(3.) 

        3.2.4多向量梯度计算

等同于向量梯度,只不过向量数变多了。

代码示例: 

import torch
x = torch.tensor([2.0,3.0,4.0],requires_grad=True)
m = torch.tensor([12.0,13.0,14.0],requires_grad=True)
y = pow(x,2) + pow(m,3)
# 对x求导等于 y =  2*x
# 对m求导等于 y =  3*m²
z = y.sum()
z.backward()
print(x.grad,m.grad)

结果:

tensor([4., 6., 8.])

tensor([432., 507., 588.]) 

3.3梯度上下文控制

        3.3.1控制梯度计算

                出现的原因是因为有时候我们并不需要每一个叶子结点的梯度都出现,为节省性能开销,控制梯度应运而生。

                API:with torch.no_grad()

运用这个API之后,查看当前值有没有进行梯度计算的检验方法是data.requires_grad

结果为True则说明它进行了梯度计算,为False则说明它没有进行梯度计算。

代码示例:

# 控制梯度计算
import torch
x = torch.tensor([2.0,3.0,4.0],requires_grad=True)
y = pow(x,2)
z = y.sum()
z.backward()
print(x.requires_grad)
print(y.requires_grad)

with torch.no_grad():
    y = pow(x,2)
print(y.requires_grad)

结果:

True

True

False

        3.3.2累计梯度

        重复进行梯度计算是为了解决硬件限制和优化训练过程。当我们重复对一个自变量进行梯度计算时,梯度是累加的。

代码示例:

# 累计梯度
import torch
x = torch.tensor([2.0,3.0,4.0],requires_grad=True)
for i in range(3):
    y = pow(x,2)
    z = y.sum()
    z.backward()
    print(x.grad)

结果:

tensor([4., 6., 8.])

tensor([ 8., 12., 16.])

tensor([12., 18., 24.])

        3.3.3梯度清零

但是通常情况下,我们并不需要梯度进行累加,因此梯度清零又出现了。

API:data.grad.zero_()

(1)创建张量;

(2)累计梯度;

(3)在梯度计算之前进行梯度清零;

(4)打印结果

代码示例:

# 梯度清零
import torch
x = torch.tensor([2.0,3.0,4.0],requires_grad=True)
for i in range(3):
    y = pow(x,2)
    z = y.sum()
    if x.grad is not None:
        x.grad.zero_()
    z.backward()
    print(x.grad)

结果:

tensor([4., 6., 8.])

tensor([4., 6., 8.])

tensor([4., 6., 8.])

四、案例

4.1函数求最小值

已知函数关系式,求函数的最小值,一般默认函数为开口向上的函数,即a>0。求它的最小值就是沿着梯度下降最快的切线方向调试,这样可以更快地找到函数的最小值。

大致步骤如下:

(0)定义函数关系式,结合matplotlib观察图像;(这个可以不要,看个人需求)

(1)初始化张量x;

(2)设定学习轮次,学习率;

(3)循环:前向传播、梯度清理、反向传播(计算梯度)、梯度控制、梯度下降公式;

(4)打印观察结果。

代码示例:

import torch
def fn():
    x = torch.tensor([2.0],requires_grad = True)
    epochs = 300
    lr = 0.1
    for epoch in range(epochs):
        y = x ** 2
     
        if x.grad is not None:
            x.grad.zero_()
        y.backward()
        with torch.no_grad():
            x -= lr*x.grad
        print('x:',x.grad,x.item())

if __name__ == '__main__':
    fn()

结果:

x: tensor([4.2264e-29]) 1.690542485657674e-29

解释:

最终梯度: tensor([0.0004])
最终参数值: 1.9680459429146233e-14  (结果都差不多,不影响,只是解释x.grad和x.item)

4.2函数参数求解

已知样本数据信息x,y,现在要探索x与y之间的数学关系,建立数学线性方程y'=wx+b,现在要求函数的未知参数w,b。利用本阶段和上一个阶段的梯度下降公式可以进行求解。

步骤如下:

(1)定义已知张量;

(2)初始化两个未知参数;

(3)定义学习率(固定不固定都可以,但还是偏向动态的学习率),定义迭代次数;

(4)循环累加梯度;

(5)在循环内,写公式,定义损失函数,梯度清零,反向传播,控制上下文梯度,梯度下降;

(6)打印计算结果。

代码示例:

import torch
import numpy as np
from matplotlib import pyplot as plt
# 特征值x,标签(真实值y)
x = torch.tensor([1, 2, 3, 4, 5], dtype=torch.float)
y = torch.tensor([3, 5, 7, 9, 11], dtype=torch.float)
# 根据x,y的数据求函数y = wx + b中的参数w,b
# 初始化w,b 
w = torch.tensor(np.random.randint(0,5),dtype=torch.float,requires_grad=True)
b = torch.tensor(np.random.randint(0,5),dtype=torch.float,requires_grad=True)
# lr = 0.1 固定学习率
t0,t1 = 1,100  #动态学习率
epochs = 300
for epoch in range(epochs):
    # 前向传播,得到预测值
    y_pred = w * x + b
    # 定义loss函数,MAE平均绝对误差,用均方差MSE也行
    Loss = abs(y_pred - y).mean()   
    # 对w,b梯度清零
    if w.grad is not None:
        w.grad.zero_()
    if b.grad is not None:
        b.grad.zero_()
    # 梯度计算,反向传播
    Loss.backward()
    # 梯度下降
    with torch.no_grad():
        lr = t0/(t1+epoch)
        w -= lr*w.grad
        b -= lr*b.grad
    # print(f'训练次数:{epoch+1}/{epochs},w:',w.item(),'b:',b.item(),'loss:',Loss.item())

print('w:',w.item(),'b:',b.item())





结果:

w: 2.0090982913970947 b: 0.9916829466819763

五、小结

        主要学习了tensor的形状变化和其他操作以及自动微分的重要概念,最后以例子结尾加深印象。一定要记住相关步骤和操作,勤加练习。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值