深度学习 pytorch的使用(张量2)

深度学习 pytorch的使用(张量1)


五、张量索引操作

简单行、列索引、列表索引、范围索引、布尔索引、多维索引

import torch
# 数据
data = torch.randint(0,10,[4,5])
print(data)

tensor([[7, 6, 9, 4, 6],
        [1, 9, 0, 9, 2],
        [5, 7, 1, 7, 4],
        [1, 2, 7, 2, 1]])

"""1.简单行、列索引"""
print(data[0])  # 第一行
print(data[:,0])  # 第一列

tensor([7, 6, 9, 4, 6])
tensor([7, 1, 5, 1])

"""2.列表索引"""
# 返回(0,1)、(1,2)两个位置的元素
print(data[[0,1],[1,2]])

tensor([6, 0])

# 返回0、1行的1、2列共4个元素
# print(data[[0],[1],[1,2]])
"""3.范围索引"""
# 前3 行的前2列数据
print(data[:3,:2])
# 前2行到最后的前2列数据
print(data[2:,:2])

tensor([[7, 6],
        [1, 9],
        [5, 7]])
tensor([[5, 7],
        [1, 2]])

"""4.布尔索引"""
# 第三列大于5的行数据
print(data[data[:,2]>5])
# 第二行大于5的列数据
print(data[:,data[1]>5])

tensor([[7, 6, 9, 4, 6],
        [1, 2, 7, 2, 1]])
tensor([[6, 4],
        [9, 9],
        [7, 7],
        [2, 2]])

"""5.多维索引"""
data = torch.randint(0,10,[3,4,5])
print(data)
print('-'*50,'\n')

print(data[0,:,:])
print(data[:,0,:])
print(data[:,:,0])

tensor([[[5, 5, 5, 9, 1],
         [1, 5, 3, 8, 0],
         [8, 3, 8, 2, 8],
         [7, 2, 5, 0, 4]],

        [[4, 7, 2, 1, 3],
         [9, 2, 7, 5, 2],
         [0, 0, 4, 8, 7],
         [3, 6, 6, 5, 3]],

        [[1, 7, 5, 5, 4],
         [7, 8, 9, 5, 0],
         [0, 0, 3, 5, 5],
         [2, 0, 8, 3, 6]]])
-------------------------------------------------- 

tensor([[5, 5, 5, 9, 1],
        [1, 5, 3, 8, 0],
        [8, 3, 8, 2, 8],
        [7, 2, 5, 0, 4]])
tensor([[5, 5, 5, 9, 1],
        [4, 7, 2, 1, 3],
        [1, 7, 5, 5, 4]])
tensor([[5, 1, 8, 7],
        [4, 9, 0, 3],
        [1, 7, 0, 2]])


六、张量形状操作

1.reshape 函数可以在保证张量数据不变的前提下改变数据的维度
2.transpose 函数可以实现交换张量形状的指定维度,permute可以一次交换更多的维度
3.view 函数也可以用于修改张量的形状,但是它要求被转换的张量内存必须连续,所以易班配合contiguous函数使用
4.squeeze 和 unsqueeze 函数可以用来增加或者减少维度

1、reshape 函数的用法

reshape 函数可以在保证张量数据不变的前提下改变数据的维度,将其转换成指定的形状,在神经网络学习时,会经常使用该函数来调节数据的形状,以适配不同网络层之间的数据传递

import torch

data = torch.tensor([[10,20,30],[40,50,60]])
print(data)
# tensor([[10, 20, 30],
#        [40, 50, 60]])


# 1.使用shape属性或者size方法都可以获得张量的形状
print(data.shape,data.shape[0],data.shape[1])
# torch.Size([2, 3]) 2 3
print(data.size(),data.size(0),data.size(1))
# torch.Size([2, 3]) 2 3



# 2.使用reshape函数修改张量形状
new_data = data.reshape(1,6)
print(new_data)
print(new_data.shape)
# tensor([[10, 20, 30, 40, 50, 60]])
# torch.Size([1, 6])

2、transpose 和 permute 函数的使用

transpose 函数可以实现交换张量形状的指定维度, 例如: 一个张量的形状为 (2, 3, 4) 可以通过 transpose 函数把 3 和 4 进行交换, 将张量的形状变为 (2, 4, 3) permute 函数可以一次交换更多的维度

import torch
import numpy as np


data = torch.tensor(np.random.randint(0, 10, [3, 4, 5]))
print('data shape:', data.size())
# data shape: torch.Size([3, 4, 5])


# 1. 交换1和2维度
new_data = torch.transpose(data, 1, 2)
print('data shape:', new_data.size())
# data shape: torch.Size([3, 5, 4])


# 2. 将 data 的形状修改为 (4, 5, 3)
new_data = torch.transpose(data, 0, 1)
new_data = torch.transpose(new_data, 1, 2)
print('new_data shape:', new_data.size())
# new_data shape: torch.Size([4, 5, 3])


# 3. 使用 permute 函数将形状修改为 (4, 5, 3)
new_data = torch.permute(data, [1, 2, 0])
print('new_data shape:', new_data.size())
# new_data shape: torch.Size([4, 5, 3])

3、view 和 contigous 函数的用法

view 函数也可以用于修改张量的形状,但是其用法比较局限,只能用于存储在整块内存中的张量。 在 PyTorch 中,有些张量是由不同的数据块组成的,它们并没有存储在整块的内存中, view 函数无法对这样的张量进行变形处理,例如: 一个张量经过了 transpose 或者 permute 函数的处理之后,就无法使用 view 函数进行形状操作。

import torch
data = torch.tensor([[10, 20, 30], [40, 50, 60]])
print('data shape:', data.size())
# data shape: torch.Size([2, 3])

# 1. 使用 view 函数修改形状
new_data = data.view(3, 2)
print('new_data shape:', new_data.shape)
# new_data shape: torch.Size([3, 2])


# 2. 判断张量是否使用整块内存
print('data:', data.is_contiguous())  # True
# data: True

# 3. 使用 transpose 函数修改形状
new_data = torch.transpose(data, 0, 1)
print('new_data:', new_data.is_contiguous())  # False
# new_data = new_data.view(2, 3)  # RuntimeError

# 需要先使用 contiguous 函数转换为整块内存的张量,再使用 view 函数
print(new_data.contiguous().is_contiguous())  # True
new_data = new_data.contiguous().view(2, 3)
print('new_data shape:', new_data.shape)  
# new_data shape: torch.Size([2, 3])

4、 squeeze 和 unsqueeze 函数的用法

import torch
import numpy as np

data = torch.tensor(np.random.randint(0, 10, [1, 3, 1, 5]))
print('data shape:', data.size())

# 1. 去掉值为1的维度
new_data = data.squeeze()
print('new_data shape:', new_data.size())  # torch.Size([3, 5])

# 2. 去掉指定位置为1的维度,注意: 如果指定位置不是1则不删除
new_data = data.squeeze(2)
print('new_data shape:', new_data.size())  # torch.Size([3, 5])

# 3. 在2维度增加一个维度
new_data = data.unsqueeze(-1)
print('new_data shape:', new_data.size())  # torch.Size([3, 1, 5, 1])


七、张量运算函数

1、常见运算函数

PyTorch 为每个张量封装很多实用的计算函数,例如计算均值、平方根、求和等等

import torch

def test():

    data = torch.randint(0, 10, [2, 3], dtype=torch.float64)
    print(data)
    print('-' * 50)

    # 1. 计算均值
    # 注意: tensor 必须为 Float 或者 Double 类型
    print(data.mean())
    print(data.mean(dim=0))  # 按列计算均值
    print(data.mean(dim=1))  # 按行计算均值
    print('-' * 50)

    # 2. 计算总和
    print(data.sum())
    print(data.sum(dim=0))
    print(data.sum(dim=1))
    print('-' * 50)

    # 3. 计算平方
    print(data.pow(2))
    print('-' * 50)

    # 4. 计算平方根
    print(data.sqrt())
    print('-' * 50)

    # 5. 指数计算, e^n 次方
    print(data.exp())
    print('-' * 50)

    # 6. 对数计算
    print(data.log())  # 以 e 为底
    print(data.log2())
    print(data.log10())


if __name__ == '__main__':
    test()

tensor([[7., 5., 9.],
        [6., 1., 6.]], dtype=torch.float64)
--------------------------------------------------
tensor(5.6667, dtype=torch.float64)
tensor([6.5000, 3.0000, 7.5000], dtype=torch.float64)
tensor([7.0000, 4.3333], dtype=torch.float64)
--------------------------------------------------
tensor(34., dtype=torch.float64)
tensor([13.,  6., 15.], dtype=torch.float64)
tensor([21., 13.], dtype=torch.float64)
--------------------------------------------------
tensor([[49., 25., 81.],
        [36.,  1., 36.]], dtype=torch.float64)
--------------------------------------------------
tensor([[2.6458, 2.2361, 3.0000],
        [2.4495, 1.0000, 2.4495]], dtype=torch.float64)
--------------------------------------------------
tensor([[1.0966e+03, 1.4841e+02, 8.1031e+03],
        [4.0343e+02, 2.7183e+00, 4.0343e+02]], dtype=torch.float64)
--------------------------------------------------
tensor([[1.9459, 1.6094, 2.1972],
        [1.7918, 0.0000, 1.7918]], dtype=torch.float64)
tensor([[2.8074, 2.3219, 3.1699],
        [2.5850, 0.0000, 2.5850]], dtype=torch.float64)
tensor([[0.8451, 0.6990, 0.9542],
        [0.7782, 0.0000, 0.7782]], dtype=torch.float64)
 


八、自动微分模块

自动微分(Autograd)模块对张量做了进一步的封装,具有自动求导功能。
自动微分模块是构成神经网络训练的必要模块,在神经网络的反向传播过程中,
Autograd 模块基于正向计算的结果对当前的参数进行微分计算,从而实现网络权重参数的更新

pytorch 中非常重要的自动微分模型的使用和理解
对需要计算梯度的张量需要设置requires_grad=True属性,
并且需要注意的是梯度是累计的,在每次计算梯度前需要进行梯度清零。

1、梯度基本运算

使用 backward 方法、grad 属性来实现梯度的计算和访问

import torch


# 1. 单标量梯度的计算
# y = x**2 + 20
def test01():

    # 定义需要求导的张量
    # 张量的值类型必须是浮点类型
    x = torch.tensor(10, requires_grad=True, dtype=torch.float64)
    # 变量经过中间运算
    f = x ** 2 + 20
    # 自动微分
    f.backward()
    # 打印 x 变量的梯度
    # backward 函数计算的梯度值会存储在张量的 grad 变量中
    print(x.grad)


# 2. 单向量梯度的计算
# y = x**2 + 20
def test02():

    # 定义需要求导张量
    x = torch.tensor([10, 20, 30, 40], requires_grad=True, dtype=torch.float64)
    # 变量经过中间计算
    f1 = x ** 2 + 20

    # 注意:
    # 由于求导的结果必须是标量
    # 而 f 的结果是: tensor([120., 420.])
    # 所以, 不能直接自动微分
    # 需要将结果计算为标量才能进行计算
    f2 = f1.mean()  # f2 = 1/2 * x

    # 自动微分
    f2.backward()

    # 打印 x 变量的梯度
    print(x.grad)


# 3. 多标量梯度计算
# y = x1 ** 2 + x2 ** 2 + x1*x2
def test03():

    # 定义需要计算梯度的张量
    x1 = torch.tensor(10, requires_grad=True, dtype=torch.float64)
    x2 = torch.tensor(20, requires_grad=True, dtype=torch.float64)

    # 经过中间的计算
    y = x1**2 + x2**2 + x1*x2

    # 将输出结果变为标量
    y = y.sum()

    # 自动微分
    y.backward()

    # 打印两个变量的梯度
    print(x1.grad, x2.grad)


# 4. 多向量梯度计算
def test04():

    # 定义需要计算梯度的张量
    x1 = torch.tensor([10, 20], requires_grad=True, dtype=torch.float64)
    x2 = torch.tensor([30, 40], requires_grad=True, dtype=torch.float64)

    # 经过中间的计算
    y = x1 ** 2 + x2 ** 2 + x1 * x2
    print(y)

    # 将输出结果变为标量
    y = y.sum()

    # 自动微分
    y.backward()

    # 打印两个变量的梯度
    print(x1.grad, x2.grad)


if __name__ == '__main__':
    test04()

tensor(20., dtype=torch.float64) tensor([ 5., 10., 15., 20.], dtype=torch.float64) tensor(40., dtype=torch.float64) tensor(50., dtype=torch.float64) tensor([1300., 2800.], dtype=torch.float64, grad_fn=<AddBackward0>) tensor([50., 80.], dtype=torch.float64) tensor([ 70., 100.], dtype=torch.float64)

2、控制梯度计算

通过一些方法使得在 requires_grad=True 的张量在某些时候计算不进行梯度计算。
import torch


# 1. 控制不计算梯度
def test01():

    x = torch.tensor(10, requires_grad=True, dtype=torch.float64)
    print(x.requires_grad)

    # 第一种方式: 对代码进行装饰
    with torch.no_grad():
        y = x ** 2
    print(y.requires_grad)

    # 第二种方式: 对函数进行装饰
    @torch.no_grad()
    def my_func(x):
        return x ** 2
    print(my_func(x).requires_grad)


    # 第三种方式
    torch.set_grad_enabled(False)
    y = x ** 2
    print(y.requires_grad)


# 2. 注意: 累计梯度
def test02():

    # 定义需要求导张量
    x = torch.tensor([10, 20, 30, 40], requires_grad=True, dtype=torch.float64)

    for _ in range(3):

        f1 = x ** 2 + 20
        f2 = f1.mean()

        # 默认张量的 grad 属性会累计历史梯度值
        # 所以, 需要我们每次手动清理上次的梯度
        # 注意: 一开始梯度不存在, 需要做判断
        if x.grad is not None:
            x.grad.data.zero_()

        f2.backward()
        print(x.grad)


# 3. 梯度下降优化最优解
def test03():

    # y = x**2
    x = torch.tensor(10, requires_grad=True, dtype=torch.float64)

    for _ in range(5000):

        # 正向计算
        f = x ** 2

        # 梯度清零
        if x.grad is not None:
            x.grad.data.zero_()

        # 反向传播计算梯度
        f.backward()

        # 更新参数
        x.data = x.data - 0.001 * x.grad

        print('%.10f' % x.data)


if __name__ == '__main__':
    test01()
    test02()
    test03()

True

False

False

False

tensor([ 5., 10., 15., 20.], dtype=torch.float64)

tensor([ 5., 10., 15., 20.], dtype=torch.float64)

tensor([ 5., 10., 15., 20.], dtype=torch.float64)

3、梯度计算注意

当对设置 requires_grad=True 的张量使用 numpy 函数进行转换时, 会出现如下报错:
Can't call numpy() on Tensor that requires grad. Use tensor.detach().numpy() instead.
此时, 需要先使用 detach 函数将张量进行分离, 再使用 numpy 函数.
注意: detach 之后会产生一个新的张量, 新的张量作为叶子结点,并且该张量和原来的张量共享数据, 但是分离后的张量不需要计算梯度。
import torch


# 1. detach 函数用法
def test01():

    x = torch.tensor([10, 20], requires_grad=True, dtype=torch.float64)

    # Can't call numpy() on Tensor that requires grad. Use tensor.detach().numpy() instead.
    # print(x.numpy())  # 错误
    print(x.detach().numpy())  # 正确


# 2. detach 前后张量共享内存
def test02():

    x1 = torch.tensor([10, 20], requires_grad=True, dtype=torch.float64)

    # x2 作为叶子结点
    x2 = x1.detach()

    # 两个张量的值一样: 140421811165776 140421811165776
    print(id(x1.data), id(x2.data))
    x2.data = torch.tensor([100, 200])
    print(x1)
    print(x2)

    # x2 不会自动计算梯度: False
    print(x2.requires_grad)


if __name__ == '__main__':
    test01()
    test02()

[10. 20.]
1865743596496 1865743596496
tensor([10., 20.], dtype=torch.float64, requires_grad=True)
tensor([100, 200])
False

猜你喜欢

转载自blog.csdn.net/m0_74261473/article/details/135742513