01、Pytorch基础知识点(自用版)

  PyTorch 是一个 Python 深度学习框架,它将数据封装成张量(Tensor)来进行运算。PyTorch 中的张量就是元素为同一种数据类型的多维矩阵。在 PyTorch 中,张量以 "类" 的形式封装起来,对张量的一些运算、处理的方法被封装在类中。

1.1张量的创建

1.1.1基本创建方式

  1. torch.tensor 根据指定数据标量,numpy数组,list列表)创建张量(是一个函数,用来创建一个新的张量。
  2. torch.Tensor 根据形状创建张量, 其也可用来创建指定数据的张量(是一个类,是所有张量的基类
  3. torch.IntTensor、torch.FloatTensor、torch.DoubleTensor 创建指定类型的张量(Tensor具体类来创建)
import torch
import numpy as np

# 1. 根据已有数据创建张量
def test01():

    # 1. 创建张量标量
    data = torch.tensor(10)
    print(data)

    # 2. numpy 数组, 由于 data 为 float64, 下面代码也使用该类型
    data = np.random.randn(2, 3)
    data = torch.tensor(data)#其实就是numpy转换为tensor后面会学到
    print(data)

    # 3. 列表, 下面代码使用默认元素类型 float32
    data = [[10., 20., 30.], [40., 50., 60.]]
    data = torch.tensor(data)
    print(data)


# 2. 创建指定形状的张量
def test02():

    # 1. 创建2行3列的张量, 默认 dtype 为 float32
    data = torch.Tensor(2, 3)
    print(data)

    # 2. 注意: 如果传递列表, 则创建包含指定元素的张量
    data = torch.Tensor([10])
    print(data)

    data = torch.Tensor([10, 20])
    print(data)


# 3. 使用具体类型的张量
def test03():

    # 1. 创建2行3列, dtype 为 int32 的张量
    data = torch.IntTensor(2, 3)
    print(data)

    # 2. 注意: 如果传递的元素类型不正确, 则会进行类型转换
    data = torch.IntTensor([2.5, 3.3])
    print(data)

    # 3. 其他的类型
    data = torch.ShortTensor()  # int16
    data = torch.LongTensor()   # int64
    data = torch.FloatTensor()  # float32
    data = torch.DoubleTensor() # float64



if __name__ == '__main__':

    test01()
    test02()
    test03()



运行结果: 

1.1.2. 创建线性和随机张量

  1. torch.arange (指定步长)和 torch.linspace(自动计算步长) 创建线性张量
  2. torch.random.init_seed (随机数种子)和 torch.random.manual_seed (固定随机数种子,随机数将不变)随机种子设置
  3. torch.randn 创建随机张量  torch.randn()创建的都是小数,torch.randint()都是整数
import torch


# 1. 创建线性空间的张量
def test01():

    # 1. 在指定区间按照步长生成元素 [start, end, step)
    data = torch.arange(0, 10, 2)
    print(data)

    # 2. 在指定区间按照元素个数生成(开始值,结束值,创建元素个数)
    data = torch.linspace(0, 11, 10)
    print(data)


# 2. 创建随机张量
def test02():

    # 1. 创建随机张量
    data = torch.randn(2, 3)  # 创建2行3列张量  randn小数
    print(data)

    data = torch.randint(0, 10, [2, 3])    # randint是整数类型
    print(data)

    # 2. 随机数种子设置(固定随机数)
    print('随机数种子:', torch.random.initial_seed())

    torch.random.manual_seed(0)#固定随机数种子

    print('随机数种子:', torch.random.initial_seed())
    # 1. 创建随机张量
    data = torch.randn(2, 3)  # 创建2行3列张量
    print(data)
if __name__ == '__main__':

    test02()

运行结果:

1.1.3 创建01张量

  1. torch.ones 和 torch.ones_like 创建全1张量
  2. torch.zeros 和 torch.zeros_like 创建全0张量
  3. torch.full 和 torch.full_like 创建全为指定值张量
import torch


# 1. 创建全0张量
def test01():

    # 1. 创建指定形状全0张量
    data1 = torch.zeros(2, 3)
    print(data1)

    # 2. 根据张量形状创建全0张量
    data2 = torch.zeros_like(data1)
    print(data2)


# 2. 创建全1张量
def test02():

    # 1. 创建指定形状全0张量
    data1 = torch.ones(2, 3)
    print(data1)

    # 2. 根据张量形状创建全0张量
    data2 = torch.ones_like(data1)
    print(data2)


# 3. 创建全为指定值的张量
def test03():

    # 1. 创建指定形状指定值的张量
    data = torch.full([2, 3], 10)
    print(data)

    # 2. 根据张量形状创建指定值的张量
    data = torch.full_like(data, 20)
    print(data)


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

运行结果:

1.1.4张量类型的转换

  1. tensor.type(torch.DoubleTensor)//type函数里面写上要转换的张量类型
  2. torch.double()  torch.int()  torch.float()  torch.long()  torch.short()//使用具体类型 函数直接转换

转换完的变量需要一个新的变量来承接,原来变量并不会直接改变

import torch


def test():

    data = torch.full([2, 3], 10)
    print(data.dtype)

    # 将 data 元素类型转换为 float64 类型

    # 1. 第一种方法type函数转换  返回一个新的类型转换过的张量
    data = data.type(torch.DoubleTensor)
    print(data.dtype)

    # 转换为其他类型
    # data = data.type(torch.ShortTensor)
    # data = data.type(torch.IntTensor)
    # data = data.type(torch.LongTensor)
    # data = data.type(torch.FloatTensor)

    # 2. 第二种方法  使用具体类型函数进行转换
    data = data.double()
    print(data.dtype)

    # 转换为其他类型
    data = data.short()
    print(data.dtype)
    data = data.int()
    print(data.dtype)
    data = data.long()
    print(data.dtype)
    data = data.float()
    print(data.dtype)



if __name__ == '__main__':
    test()

 运行结果:

小结

主要学习了以下内容:

1. 创建张量的方式 1. torch.tensor 根据指定数据创建张量 2. torch.Tensor 根据形状创建张量, 其也可用来创建指定数据的张量 3. torch.IntTensor、torch.FloatTensor、torch.DoubleTensor 创建指定类型的张量

2.创建线性和随机张量

torch.arange 和 torch.linspace 创建线性张量

torch.random.init_seed 和 torch.random.manual_seed 随机种子设置

torch.randn 创建随机张量

3.创建01张量

torch.ones 和 torch.ones_like 创建全1张量

torch.zeros 和 torch.zeros_like 创建全0张量

torch.full 和 torch.full_like 创建全为指定值张量

4.张量元素类型转换

tensor.type(torch.DoubleTensor)

torch.double()

1.2张量的数值计算

1.2.1张量的基本计算

基本运算中,包括 add、sub、mul、div、neg 等函数以及这些函数的带下划线的版本 add_、sub_、mul_、div_、neg_,其中带下划线的版本为修改原数据。

print(data.add(100))# +

    print(data.sub(100))# -

    print(data.mul(100))# *

    print(data.div(100)) # /

print(data.neg())   #取相反数

有无下划线的区别在与是否需要新变量来承接该计算结果

import numpy as np
import torch


def test():
    #第一个参数:开始值
    #第二个参数:结束值
    #第三个参数:形状
    data = torch.randint(0, 10, [2, 3])
    print(data)
    print('-' * 50)#打印下分割划线

    # 1. 不修改原数据
    #注意:计算完之后会返回一个新的张量
    new_data = data.add(10)  # 等价 new_data = data + 10
    print(new_data)
    print('-' * 50)#打印下分割划线

    # 2. 直接修改原数据(inplace方式计算)
    # 注意: 带下划线的函数为修改原数据本身
    data.add_(10)  # 等价 data += 10
    print(data)

    # 3. 其他函数
    print(data.add(100))# +
    print(data.sub(100))# -
    print(data.mul(100))# *
    print(data.div(100))# /
    print(data.neg())   #取相反数


if __name__ == '__main__':
    test()

运行结果:

1.2.2阿达玛积(*)

阿达玛积指的是矩阵对应位置的元素相乘.可以使用乘号运算符、也可以使用mul函数来完成计算。

data = torch.mul(data1, data2)

data = data1 * data2

注意:阿达玛积与矩阵乘积(点积)不同。矩阵乘积是将两个矩阵的对应行和列的元素相乘并求和,而阿达玛积是逐元素相乘。 阿达玛积要求两个数组的形状完全相同,否则无法进行操作。

#阿达玛积指的是矩阵对应位置的元素相乘.
import numpy as np
import torch


def test():

    data1 = torch.tensor([[1, 2],
                          [3, 4]])
    data2 = torch.tensor([[5, 6],
                          [7, 8]])

    # 第一种方式
    data = torch.mul(data1, data2)
    print(data)
    print('-' * 50)

    # 第二种方式
    data = data1 * data2
    print(data)
    print('-' * 50)


if __name__ == '__main__':
    test()

运行结果:

1.2.3点击运算(矩阵运算)

点积运算要求第一个矩阵 shape: (n, m),第二个矩阵 shape: (m, p), 两个矩阵点积运算 shape 为: (n, p)。

1、运算符 @ 用于进行两个矩阵的点乘运算

2、torch.mm 用于进行两个矩阵点乘运算, 要求输入的矩阵为2维

3、torch.bmm 用于批量进行矩阵点乘运算, 要求输入的矩阵为3维

4、torch.matmul 对进行点乘运算的两矩阵形状没有限定.

(1)对于输入都是二维的张量相当于 mm 运算.

(2)对于输入都是三维的张量相当于 bmm 运算

(3)对数输入的 shape 不同的张量, 对应的最后几个维度必须符合矩阵运算规则

#点积运算要求第一个矩阵 shape: (n, m),第二个矩阵 shape: (m, p), 两个矩阵点积运算 shape 为: (n, p)。
import torch


# 1. 点积运算
def test01():

    data1 = torch.tensor([[1, 2],
                          [3, 4],
                          [5, 6]])
    data2 = torch.tensor([[5, 6],
                          [7, 8]])

    # 第一种方式
    data = data1 @ data2
    print(data)
    print('-' * 50)

    # 第二种方式
    data = torch.mm(data1, data2)
    print(data)
    print('-' * 50)

    # 第三种方式
    data = torch.matmul(data1, data2)
    print(data)
    print('-' * 50)


# 2. torch.mm 和 torch.matmull 的区别
def test02():

    # matmul 可以两个维度可以不同
    # 第一个张量: (3, 4, 5)
    # 第二个张量: (5, 4)
    # torch.mm 不可以相乘,而 matmul 则可以相乘

    print(torch.matmul(torch.randn(3, 4, 5), torch.randn(5, 4)).shape)
    print(torch.matmul(torch.randn(5, 4), torch.randn(3, 4, 5)).shape)


# 3. torch.bmm 函数的用法
def test03():

    # 批量点积运算
    # 第一个维度为 batch_size
    # 矩阵的二三维要满足矩阵乘法规则

    data1 = torch.randn(3, 4, 5)
    data2 = torch.randn(3, 5, 8)

    data = torch.bmm(data1, data2)
    print(data.shape)


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

运行结果:

1.2.4. 指定运算设备

 注意这里需要安装带GPU版本的pytorch,这一块不影响后续学习。

PyTorch 默认会将张量创建在 CPU 控制的内存中, 即: 默认的运算设备为 CPU。我们也可以将张量创建在 GPU 上, 能够利用对于矩阵计算的优势加快模型训练。将张量移动到 GPU 上有两种方法:

  1. 使用 cuda 方法  

data = data.cuda()

  1. 直接在 GPU 上创建张量

data = torch.tensor([10, 20, 30], device='cuda:0')

  1. 使用 to 方法指定设备

data = data.to('cuda:0')

注意两个数据存储在不同的设备上是无法计算的

import torch


# 1. 使用 cuda 方法  要安装对应的版本
def test01():

    data = torch.tensor([10, 20 ,30])
    print('存储设备:', data.device)

    # 如果安装的不是 gpu 版本的 PyTorch
    # 或电脑本身没有 NVIDIA 卡的计算环境
    # 下面代码可能会报错
    data = data.cuda()
    #################
    print('存储设备:', data.device)

    # 使用 cpu 函数将张量移动到 cpu 上
    data = data.cpu()
    print('存储设备:', data.device)

    # 输出结果:
    # 存储设备: cpu
    # 存储设备: cuda:0
    # 存储设备: cpu


# 2. 直接将张量创建在 指定设备GPU 上
def test02():

    data = torch.tensor([10, 20, 30], device='cuda:0')
    #################
    print('存储设备:', data.device)

    # 使用 cpu 函数将张量移动到 cpu 上
    data = data.cpu()
    print('存储设备:', data.device)

    # 输出结果:
    # 存储设备: cuda:0
    # 存储设备: cpu


# 3. 使用 to 方法
def test03():

    data = torch.tensor([10, 20, 30])
    print('存储设备:', data.device)

    data = data.to('cuda:0')
    #################
    print('存储设备:', data.device)

    # 输出结果:
    # 存储设备: cpu
    # 存储设备: cuda:0


# 4. 存储在不同设备的张量不能运算
def test04():

    data1 = torch.tensor([10, 20, 30], device='cuda:0')
    data2 = torch.tensor([10, 20, 30])
    print(data1.device, data2.device)

    # RuntimeError: Expected all tensors to be on the same device,
    # but found at least two devices, cuda:0 and cpu!
    data = data1 + data2
    print(data)


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

1.3张量的类型转换

1.3.1张量转换为numpy

使用 Tensor.numpy 函数可以将张量转换为 ndarray 数组,但是共享内存。

data_numpy=data_tensor.numpy()

可以使用 copy 函数避免共享。

data_numpy = data_tensor.numpy().copy()

import torch

#1.张量转换为numpy数组
def test01():
    data_tensor = torch.tensor([3,4,5])
    #将张量转换为numpy数组
    print(data_tensor.numpy())

    data_numpy =data_tensor.numpy()
    print(type(data_tensor))
    print(type(data_numpy))

    print(data_tensor)
    print(data_numpy)

#2.张量和numpy数组的共享
def test02():
    data_tensor=torch.tensor([3,4,5])
    data_numpy=data_tensor.numpy()
    #修改张量元素看看numpy数组是否会变化
    data_tensor[0]=100
    print(data_tensor)
    print(data_numpy)

    #修改numpy数组看张量是否会变化
    data_numpy[0]=100
    print(data_tensor)
    print(data_numpy)


#3.使用copy函数实现不共享内存
def test03():
    data_tensor = torch.tensor([3, 4, 5])
    #此处发生了类型转换,可以使用拷贝函数产生新数据,避免共享内存
    data_numpy = data_tensor.numpy().copy()

    #修改后的张量的元素,看看numpy数组是否发生变化?会发生变化

    print(data_tensor)
    print(data_numpy)

   # data_tensor[0]=100
    #print(data_tensor)
    #张量发生了改变,numpy数组并未发生改变
    #print(data_numpy)

    data_numpy[0] = 100
    print(data_tensor)
    print(data_numpy)

if __name__ == '__main__':
    test01()

1.3.2numpy转化为张量

1、使用 from_numpy 可以将 ndarray 数组转换为 Tensor,默认共享内存,使用 copy 函数避免共享。

data_tensor =torch.from_numpy(data_numpy)

2、使用 torch.tensor 可以将 ndarray 数组转换为 Tensor,默认不共享内存。

data_tensor = torch.tensor(data_numpy)

import numpy as np
import torch

#1.from_numpy函数的用法
def test01():
    data_numpy = np.array([2,3,4])
    data_tensor =torch.from_numpy(data_numpy)

    print(type (data_tensor))
    print(type(data_numpy))

    #默认共享内存
    data_tensor[0]=100
    print(data_tensor)
    print(data_numpy)
#2.torch.tensor函数的用法
def test02():
    data_numpy = np.array([2, 3, 4])
    data_tensor = torch.tensor(data_numpy)
    print(type(data_tensor))
    print(type(data_numpy))


    data_tensor[0] = 100
    #data_numpy[0]=100

    print(data_tensor)
    print(data_numpy)
    #默认不共享内存


if __name__ == '__main__':
    test02()

1.3.3标量张量和数字的转换

对于只有一个元素的张量,使用 item 方法将该值从张量中提取出来。

import torch

def test01():
    t1=torch.tensor(30)
    t2=torch.tensor([30])
    t3=torch.tensor([[30]])
    print(t1.shape)
    print(t2.shape)
    print(t3.shape)

    print(t1.item())
    print(t2.item())
    print(t3.item())

    # 注意:张量中只有一个元素,如果有多个元素的话,使用item函数可能就会报错



if __name__ == '__main__':
    test01()

总结:

在本小节中, 我们主要学习了 numpy 和 tensor 互相转换的规则, 以及标量张量与数值之间的转换规则。

1.4张量的拼接操作

1.4.1torch.cat的操作

Cat函数是频繁使用到底函数。torch.cat 函数可以将两个张量根据指定的维度拼接起来.

new_data = torch.cat([data1, data2], dim=0)

new_data = torch.cat([data1, data2], dim=1)

new_data = torch.cat([data1, data2], dim=2)

import torch

def test01():
    data1=torch.randint(0,10,[3,5,4])
    data2=torch.randint(0,10,[3,5,4])

    print('随机数种子:', torch.random.initial_seed())
    torch.random.manual_seed(0)
    print('随机数种子:', torch.random.initial_seed())

    print(data1)
    print(data2)

    print('-' * 50)
    # 1. 按0维度拼接
    new_data=torch.cat([data1,data2],dim=0)
    print(new_data.shape)
    print(new_data)

    print('-' * 50)
    # 1. 按1维度拼接
    new_data = torch.cat([data1, data2], dim=1)
    print(new_data.shape)
    print(new_data)

    print('-' * 50)
    # 1. 按2维度拼接
    new_data = torch.cat([data1, data2], dim=2)
    print(new_data.shape)
    print(new_data)



if __name__ == '__main__':
    test01()

1.4.2torch.stack的使用

torch.stack 函数可以将两个张量根据指定的维度叠加起来或组合成新的元素.

import torch

def test01():
    torch.random.manual_seed(0)
    print(torch.random.initial_seed())
    data1=torch.randint(0,10,[2,3])
    data2=torch.randint(0,10,[2,3])

    print(data1)
    print(data2)

    print('-'*30)
#将两个张量stack起来,像cat一样指定维度
    #1.安照0维度进行叠加  直接堆叠  两个2x3tensor--->[2,2,3]
    new_data=torch.stack([data1,data2],dim=0)
    print(new_data.shape)
    print(new_data)



    print('-' * 30)

    #.按照1维度进行叠   直接堆叠按照行 两个2x3tesnor--->[2,2,3]
    new_data = torch.stack([data1, data2], dim=1)
    print(new_data.shape)
    print(new_data)



    print('-' * 30)

    #安照2维度进行叠加   直接按照列堆叠 两个2x3tensor---->[2,3,2]
    new_data = torch.stack([data1, data2], dim=2)
    print(new_data.shape)
    print(new_data)



if __name__ == '__main__':
    test01()

总结:

张量的拼接操作也是在后面我们经常使用一种操作。cat 函数可以将张量按照指定的维度拼接起来,stack 函数可以将张量按照指定的维度叠加起来(第一个里面取一些元素,第二个里面取一些元素)。

1.5张量的索引操作

1.5.1简单的行和列表索引

我们在操作张量时,经常需要去进行获取或者修改操作,掌握张量的花式索引操作是必须的一项能力。

获得指定行:print(data[i])

获得指定列:print(data[:,i])

逗号前面表示行,逗号后面表示列

冒号表示所有行或者所有列

# 表示先获得前三行,然后再获得第三列的数据

print(data[ : 3 , 2 ])

# 表示获得前三行的前两列

print( data[ : 3 , 2 ])

#表示获得(0,0)、(2,1)、(3,2)三个位置的元素

print(data[ [0,2,3],[0,1,2]])

#表示获得0、2、3行的 0、1、2列

print(data[[[0],[2],[3]],[0,1,2]])

import  torch

#1.简单行列索引
def test01():
    torch.random.manual_seed(0)

    data=torch.randint(0,10,[4,5])
    print(data)
    print("获得指定行索引",'-'*50)

    print(data[0])
    print(data[1])
    print(data[3])

    print("获得指定列索引", '-' * 50)
    print(data[:,0])
    print(data[:,1])
    print(data[:,4])

    # 获得指定的某个列的元素
    # #逗号前面表示行,逗号后面表示列
    # #冒号表示所有行或者所有列

    # 获得指定位置的某个元素
    print(data[1, 2], data[1][2])
    # 表示先获得前三行,然后再获得第三列的数据
    print(data[ : 3,2 ])
    # 表示获得前三行的前两列
    print( data[ : 3,:2 ])

#2。列表索引
def test02( ):
    #固定随机数种子
    torch.manual_seed(0)

    data = torch.randint (0,10,[4,5])
    print(data)
    print( '-' * 30 )
    #如果索引的行列都是一个1维的列表,那么两个列表的长度必须相等
    #表示获得(0,0)、(2,1)、(3,2)三个位置的元素
    print(data[ [0,2,3],[0,1,2]])
    #表示获得0、2、3行的 0、1、2列
    print(data[[[0],[2],[3]],[0,1,2]])
    # 表示获得0、2、3行的 1、3、4列
    print(data[[[0], [2], [3]], [1, 3, 4]])


if __name__ == '__main__':
    test02()

1.5.2布尔索引和多维索引

#希望能够获得该张量中所有大于3的元素

    print(data[ data > 3])

    #希望返回第2列元素大于6的行

    print(data[ data[ :, 1] >6 ])

    #希望返回第2行元素大于3的所有列

print (data[ : , data[ 1] >3])

#按照第0维度选择第0元素,4行5列

    print(data[0,:,:])

    print('-'*30)

    #按照第1维度选择第0元素  按行选

    print(data[:,0,:])

    print(''*30)

    #按照第2维度选择第0元素  按列选

    print(data[:,:,0])

import  torch
#1。布尔索引
def test01():
    torch.manual_seed (0)
    data = torch.randint ( 0,10,[ 4,5])
    print (data)
    #希望能够获得该张量中所有大于3的元素
    print(data[ data > 3])
    #希望返回第2列元素大于6的行
    print(data[ data[ :, 1] >6 ])
    #希望返回第2行元素大于3的所有列
    print (data[ : , data[ 1] >3])

#2. 多维索引
def test02():
    torch.random.manual_seed(0)
    data=torch.randint(0,10,[3,4,5])
    print(data)
    print('-'*30)
    #按照第0维度选择第0元素,4行5列
    print(data[0,:,:])
    print('-'*30)
    #按照第1维度选择第0元素  按行选
    print(data[:,0,:])
    print(''*30)
    #按照第2维度选择第0元素  按列选
    print(data[:,:,0])


if __name__ == '__main__':
    test02()

1.6张量形状的操作

在我们后面搭建神经网络模型时,数据都是基于张量形式的表示,网络层与层之间很多都是以不同的 shape 的方式进行表现和运算,我们需要掌握对张量形状的操作,以便能够更好处理网络各层之间的数据连接。

1.6.1reshape函数的使用

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

    data =torch.randint(0,10,[4,5])

 #修改张量的形状

new_data=data.reshape(2,10)

##使用-1代替省略的形状

new_data=data.reshape(5,-1)  

print(new_data)

new_data=data.reshape(2,-1)

print(new_data)

import  torch


def test01():
    torch.random.manual_seed(0)
    data =torch.randint(0,10,[4,5])

    #查看张量的形状
    print(data.shape,data.shape[0],data.shape[1])
    print(data.size(),data.size(0),data.size(1))
    print(data)
    #修改张量的形状
    new_data=data.reshape(2,10)
    print(new_data)

    # 注意:转换之后的形状元素个数得等于原来张量的元素个数

   ##使用-1代替省略的形状
    new_data=data.reshape(5,-1)
    print(new_data)

    new_data=data.reshape(2,-1)
    print(new_data)



if __name__ == '__main__':
    test01()

1.6.2transpose 和 permute 函数的使用

data=torch.randint(0,10,[3,4,5])

new_data=data.reshape(4,3,5)//reshape函数通过计算转换的

# 直接交换两个维度的值

new_data = data.transpose(1,2)  # 缺点:一次只能交换两个维度

# permute函数可以一次性交换多个维度

new_data = data.permute(1,2,0)

import torch

    #1.transpose函数
def  test01():
    torch.random.manual_seed(0)
    data=torch.randint(0,10,[3,4,5])
    print(data.shape)
    new_data=data.reshape(4,3,5)
    print(new_data.shape)

    # 直接交换两个维度的值
    new_data = torch.transpose(data,1,2)
    print(new_data.shape)
   # new_data=data.transpose(1,2)
   # print(new_data.shape)


    # 缺点:一次只能交换两个维度
    # #把数据的形状变成(4, 5,3 )
    #进行第一次交换:(4, 3,5 )
    #进行第二次交换:(4, 5,3 )
    new_data = torch.transpose(data,0,1)
    new_data = torch.transpose(new_data, 1, 2)
    print(new_data.shape)

# 2. permute函数
def test02( ):
    torch.manual_seed(0)
    data = torch.randint(0, 10, [3, 4, 5])

    # permute函数可以一次性交换多个维度
    new_data = data.permute(1,2,0)
    print(new_data.shape)


if __name__ == '__main__':
    test01()

1.6.3. view 和 contigous 函数的用法

view 函数也可以用于修改张量的形状,但是其用法比较局限,只能用于存储在整块内存中的张量。

在 PyTorch 中,有些张量是由不同的数据块组成的,它们并没有存储在整块的内存中,view 函数无法对这样的张量进行变形处理。

例如: 一个张量经过了 transpose 或者 permute 函数的处理之后,就无法使用 view 函数进行形状操作。

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

data=data.view(3,2)//存储还是连续的

data=data.transpose(0,1)//改变存储不连续

data=data.contiguous().view(2,3)

注意:

data.transpose()这个方法用于转置数据,即将行和列互换常用于需要改变数据维度的场景,比如将行数据转换为列数据。

data.view():常用于需要改变数据表现形式而不改变数据内容的场景,比如改变数据的索引类型。

import torch

#1.view函数的使用(只能使用在连续的内存中)
def test01():
    data=torch.tensor([[10,20,30],[40,50,60]])
    print(data.shape)
    data=data.view(3,2)
    print(data.shape)

    #is_contiguous来判断张量是否是连续的内存空间(整块的内存)
    print(data.is_contiguous())
#2.view函数的使用注意事项
def test02():
    #当张量经过transport或者permute函数之后,内存空间不连续
    #此时,必须先把空间连续起来,才能够使用view函数进行张量形状操作
    data = torch.tensor([[10, 20, 30],
                         [40, 50, 60]])
    print('是否连续:',data.is_contiguous())

    data=data.transpose(0,1)
    print(data)
    print('是否连续:', data.is_contiguous())

    data=data.contiguous().view(2,3)
    print(data)
    print('是否连续:', data.is_contiguous())

if __name__ == '__main__':
    test02()

1.6.4squeeze 和 unsqueeze 函数的用法

squeeze 函数用删除 shape 为 1 的维度

#维度压缩,默认去掉所有的1的维度

#指定去掉某个1的维度new_data = data.squeeze(i)

unsqueeze 在每个维度添加 1, 以增加数据的形状。

#以在指定位置增加维度

#-1代表最后一个维度

new_data = data.unsqueeze(i)

import torch
#1. squeeze函数使用
def test01( ):
    data = torch.randint(0,10,[1,3, 1,5])
    print(data.shape)
#维度压缩,默认去掉所有的1的维度
    new_data = data.squeeze()
    print (new_data.shape)
#指定去掉某个1的维度
    new_data = data.squeeze(0)
    print (new_data.shape)

    new_data = data.squeeze(2)
    print(new_data.shape)

#2. unsqueeze函数使用
def test02( ) :
    data = torch.randint (0,10,[3,5])
    print(data.shape)

    #以在指定位置增加维度
    #-1 代表最后一个维度
    new_data = data.unsqueeze(2)
    print(new_data.shape)
if __name__ == '__main__':
    test02()

总结:

本小节带着同学们学习了经常使用的关于张量形状的操作,我们用到的主要函数有:

1.reshape 函数可以在保证张量数据不变的前提下改变数据的维度.

2.transpose 函数可以实现交换张量形状的指定维度, permute 可以一次交换更多的维度.

3.view 函数也可以用于修改张量的形状, 但是它要求被转换的张量内存必须连续,所以一般配合 contiguous 函数使用.

4.squeeze 和 unsqueeze 函数可以用来增加或者减少维度.

1.7张量运算函数

  1. 均值mean()  mean(dim=i)
  2. 求和sum()   sum(dim=i)
  3. 平方pow()
  4. 平方根sqrt()
  5. 指数计算, e^n 次方exp()
  6. 对数计算log()
import torch


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

    # 1. 计算均值
    # 注意: tensor 必须为 Float 或者 Double 类型
    #mean()默认所有数值求均值
    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()) #以2为底
    print(data.log10())


if __name__ == '__main__':
    test()

1.8 自动微分模块

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

1.8.1 梯度基本计算

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

对于需要求导的张量需要设置 requires_grad=True

#是标量

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

#对x的中间计算

f=x**2+20   #2x

#自动微分

f.backward()

#访问梯度(导数值)x=10的时候的梯度点

print(x.grad)

#是向量

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

    #定义变量的计算过程

    y1=x**2+20

    #注意:自动微分的时候,必须是一个标量转化可以用mean()或sum()

    y2=y1.mean()  #相当于对y1除以4的操作因为有4个向量

    #自动微分

    y2.backward()

    #打印梯度值

    print(x.grad)

import  torch

#1、标量的梯度计算
#  f=x**2+20
def test01():
    #对于需要求导的张量需要设置 requires_grad=True
    x=torch.tensor(10,requires_grad=True,dtype=torch.float64)
    #对x的中间计算
    f=x**2+20   #2x

    #自动微分
    f.backward()

    #访问梯度(导数值)x=10的时候的梯度点
    print(x.grad)

#2、向量
def test02():
    x=torch.tensor([10,20,30,40],requires_grad=True,dtype=torch.float64)

    #定义变量的计算过程
    y1=x**2+20
    #注意:自动微分的时候,必须是一个标量
    y2=y1.mean()  #相当于对y1除以4的操作
    #自动微分
    y2.backward()
    #打印梯度值
    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 = x1 ** 2 + x2 ** 2 + x1 * x2 #对x1求导2*x1+x2   #对x2求导2*x2+x1

    #自动微分
    y.backward()

    print(x1.grad)#对x1求偏导
    print(x2.grad)#对x2求偏导

#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#对x1求导2*x1+x2   #对x2求导2*x2+x1

    #进行sum()操作或mean()操作变为标量
    y=y.sum()

    #自动微分
    y.backward()

    #打印张量的梯度值
    print(x1.grad)
    print(x2.grad)



if __name__ == '__main__':
    test01()

1.8.2 控制梯度计算

#1、控制梯度计算

#1.第一种方法

with torch.no_grad():

#2.第二种方法(装饰器的方式主要用于针对一个函数的)

@torch.no_grad()

#3.设置全局的方式(会影响到其他代码注意使用场合)

torch.set_grad_enabled(False)

#2.累计梯度和清零

   #梯度清零在进行反向传播之前,需要确保梯度已经被清零,否则梯度会累加。可以使用 x.grad.zero_() 或 x.grad = None 来清零梯度

   for _ in range(10):

#对x的计算过程

        f1=x**2+10

        #转化为标量的过程

        f2=f1.mean()

        #梯度清零

        if x.grad is not None:

            x.grad.data.zero_()

#3.案列-梯度下降优化函数

# 更新参数

        x.data = x.data - 0.001 * x.grad

import torch

#1、控制梯度计算
def  test01():
    x=torch.tensor(10,requires_grad=True,dtype=torch.float64)
    print(x.requires_grad)

    #1.第一种方法
    with torch.no_grad():
        y=x**2
        print(y.requires_grad)

    #2.第二种方法(装饰器的方式主要用于针对一个函数的)
    @torch.no_grad()
    def my_func(x):
        return x**2
    y=my_func(x)
    print(y.requires_grad)

    #3.设置全局的方式(会影响到其他代码注意使用场合)
    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)
    # 当我们重复对x进行梯度计算的时候,是会将历史的梯度值累加到x.grad属性中
    # 希望不要去累加历史梯度
    for _ in range(10):
        #对x的计算过程
        f1=x**2+10
        #转化为标量的过程
        f2=f1.mean()
        #梯度清零
        if x.grad is not None:
            x.grad.zero_()

        #自动微分
        f2.backward()

        print(x.grad)


#3.案列-梯度下降优化函数
def test03():
    # y = x**2-2*x+1
    # 当x为什么值的情况下,y最小。
    # 初始化
    x = torch.tensor(10,requires_grad = True,dtype = torch.float64)

    for _ in range(10000):
        # 正向计算
        y = x**2-2*x+1
        # 梯度清零
        if x.grad is not None:
            x.grad.data.zero_()
        # 自动微分
        y.backward()
        # 更新参数   梯度下降
        x.data = x.data - 0.001 * x.grad.data
        # 打印x的值
        print('%.10f' % x.data)





if __name__ == '__main__':
    test02()

1.8.3 梯度计算注意

当对设置 requires_grad=True 的张量使用 numpy 函数进行转换时, 会出现如下报错:Can't call numpy() on Tensor that requires grad. Use tensor.detach().numpy() instead.

此时, 需要先使用 detach 函数将张量进行分离, 再使用 numpy 函数.

x.detach ( ).numpy( )

注意: detach 之后会产生一个新的张量, 新的张量作为叶子结点,并且该张量和原来的张量共享数据, 但是分离后的张量不需要计算梯度。

    # 使用detach函数分离出一个新的张量

    x2 = x1.detach()

print(id(x1.data), id(x2.data))

#x1和x2的地址一样

    # 修改分离后产生的新的张量

    x2[0] = 100

    print(x1)

    print(x2)

    #x1和x2共享存储数据

    print(x1.requires_grad)

print(x2.requires_grad)

# 通过结果我们发现,x2张量不存在 requires_grad=True

    # 表示:对x1 的任何计算都会影响到对x1的梯度计算

    # 但是,对x2 的任何计算不会影响到x1 的梯度计算

import torch
#1。演示下错误
def test01 () :
    x = torch.tensor( [ 10,20 ],requires_grad=True,dtype=torch.float64 )

    #  RuntimeError: can 't call numpy( ) on Tensor that requires grad,Use tensor.detach ( ) .numpy() instead.# print ( x.numpy( ))
    #如果设定了Autograd那么tensor.numpy()会报错,不能将tensor转化为numpy数组(ndarry)
    #下面的是正确的操作detach()分离出来了
    print( x.detach ( ).numpy( ))

# 2。共享数据
def test02():
    # x是叶子结点
    x1 = torch.tensor([10,20], requires_grad = True,dtype = torch.float64)
    # 使用detach函数分离出一个新的张量
    x2 = x1.detach()
    print(id(x1.data), id(x2.data))
    # 修改分离后产生的新的张量
    x2[0] = 100
    print(x1)
    print(x2)
    # 通过结果我们发现,x2张量不存在 requires_grad=True
    # 表示:对x1 的任何计算都会影响到对x1的梯度计算
    # 但是,对x2 的任何计算不会影响到x1 的梯度计算
    print(x1.requires_grad)
    print(x2.requires_grad)


if __name__ == '__main__':
    test02()

小结:

本小节主要讲解了 PyTorch 中非常重要的自动微分f.backward模块的使用和理解。我们对需要计算梯度的张量需要设置 requires_grad=True 属性,并且需要注意的是梯度是累计的,在每次计算梯度前需要先进行梯度清零。 

if x.grad is not None:

            x.grad.data.zero_()

访问张量的梯度值print(x.grad)

向量自动微分时要先转化为标量【sum()或mean()】

  如果需要设计到一个带有梯度true(就是设置了requires_grad=True属性)张量的需要转化为numpy数据不能直接转化要detach()分离

1.9构建线性回归

1.9.1数据的构建

pip3 install numpy==1.21.5 scipy==1.9.1 scikit-learn==1.0.2 matplotlib==3.5.2 pandas==1.3.5 -i https://pypi.tuna.tsinghua.edu.cn/simple/

#数据的构建

from sklearn.datasets import make_regression

#构建数据集函数

def create_dataset():

        x , y, coef  = make_regression(n_samples=100,

                                       n_features=1,

                                       noise=10,

                                       coef=True,

                                       bias=14.5,

                                       random_state=0)

        #将构建数据转化为张量类型

        x=torch.tensor(x)

        y=torch.tensor(y)

        return x,y,coef

        # create_dataset函数使用 make_regression生成一个回归数据集。

        # n_samples = 100表示生成100个样本。

        # n_features = 1表示每个样本有一个特征。

        # noise = 10 表示数据集中的噪声水平。

        # coef = True 返回数据生成的系数。

        # bias = 14.5 设置数据的偏置。

        # random_state = 0 确保每次生成的数据相同。

#数据加载python里面的yield关键字来实现

def  data_loader(x,y,batch_size):

    #计算下样本的数量

    data_len=len(y)

    #构建数据索引

    data_index=list(range(data_len))

    #使用 random.shuffle 打乱数据索引,以实现随机抽样。

    random.shuffle(data_index)

    #计算batch的数量

    batch_number=data_len // batch_size  #//整除

    #然后,通过迭代批次数,每次生成一个批次的数据

    # 使用一个for 循环迭代批次数量 batch_number。

    for idx in range (batch_number):

        # 计算每个批次的起始索引 start和结束索引end。

        start=idx*batch_size

        end=start+batch_size

        # 根据索引从x和y中切片出批次的特征数据 batch_train_x和目标数据batch_train_y。

        batch_train_x=x[start:end]

        batch_train_y=y[start:end]

        # 使用 yield 关键字生成批次数据。这使得data_loader函数成为一个生成器,可以逐批次地提供数据。

        yield batch_train_x,batch_train_y

1.9.2假设函数、损失函数、优化方法

假设函数式:构造线性函数w*x+b  w权重,b偏差

损失函数:平方损失

优化方法:梯度下降

#假设函数

w = torch.tensor(0.1,requires_grad=True,dtype=torch.float64)#w权重

b = torch.tensor(0.0,requires_grad=True,dtype=torch.float64)#b偏置

def linear_regression (x):

    return w*x+ b

    # 这是一个函数定义,接收输入特征 x。

    # 函数体中,计算线性回归的预测值 y^=wx+b

#损失函数

def square_loss(y_pred, y_true) :

    return (y_pred - y_true)**2

#梯度下降的优化方法

def sgd (lr=1e-2) :      #learning rate学习率控制着在每次迭代中参数更新的步长。

    #此处除以16使用的是批次样本的平均梯度值

    #w.data 和 b.data 表示权重和偏置的值。  w.grad.data 和 b.grad.data 表示权重和偏置的梯度。

    #1/16 表示除以批次大小,这里假设批次大小为16。这是因为在每次迭代中,我们希望使用批次样本的平均梯度值来更新参数。

    w.data = w.data - lr * w.grad.data / 16

    b.data = b.data - lr * b.grad.data / 16

1.9.3训练函数

def train():

    #加载数据集

    x,y,coef=create_dataset()

    #定义训练的参数

    epochs=100         #表示训练的总轮数

    learning_rate=0.01 #表示学习率,控制每次迭代中参数的更新步长

    #存储训练过程中的信息

    epoch_loss=[]  #每一个epochs训练完后的平均损失

    total_loss=0.0 #用于累计总损失

    train_samples=0#训练样本的数量需要统计一下

    for _ in range(epochs):#循环重复训练

        for train_x, train_y in data_loader(x, y, batch_size=16):

            # 1.将训练样本送入模型进行预测

            y_pred = linear_regression(train_x)

            # 2.计算预测值和真实值之间的平方损失

            # print(train_y.shape)  # torch.Size([16])

            # print(y_pred.shape)  # torch.Size([16, 1])

            loss = square_loss(y_pred, train_y.reshape(-1, 1)).sum()  # 表示成多行1列    使用-1代替省略的形状

            total_loss += loss.item()

            train_samples += len(train_y)

            # 3.梯度清零

            if w.grad is not None:

                w.grad.data.zero_()

            if b.grad is not None:

                b.grad.data.zero_()

            # 4.自动微分 (反向传播)

            loss.backward()

            # 5.参数更新

            sgd(learning_rate)

            print('loss:%.10f' % (total_loss / train_samples))  # 求出平均损失

        # 记录每一个epochs的平均损失

        epoch_loss.append(total_loss / train_samples)

    #先绘制数据集散点图

    print(coef, w.data.item())

    plt.scatter(x,y)

    #绘制拟合直线

    x=torch.linspace(x.min(),x.max(),1000)

    y1=torch.tensor([v * w + 14.5 for v in x])

    y2=torch.tensor([v * coef + 14.5 for v in x])

    plt.plot( x , y1, label='训练')

    plt.plot( x , y2, label='真实')

    plt.grid()

    plt.legend()

    plt.show()

    #打印损失变化的曲线

    plt.plot(range(epochs),epoch_loss)

    plt.grid()

    plt.title('损失变化曲线')

    plt.show()

手动构建线性完整代码:

import torch
from sklearn.datasets import make_regression
import matplotlib.pyplot as plt
import random


import matplotlib
import warnings
warnings.filterwarnings("ignore")
# 设置字体为支持中文的字体,例如 'SimHei'(黑体)
matplotlib.rcParams['font.family'] = 'SimHei'
matplotlib.rcParams['font.sans-serif'] = ['SimHei']

# make_regression 来自 sklearn.datasets,用于生成回归数据集。
# matplotlib.pyplot 用于绘制图表。

#构建数据集函数
def create_dataset():
        x , y, coef  = make_regression(n_samples=100,
                                       n_features=1,
                                       noise=10,
                                       coef=True,
                                       bias=14.5,
                                       random_state=0)

        #将构建数据转化为张量类型
        x=torch.tensor(x)
        y=torch.tensor(y)
        return x,y,coef

        # create_dataset函数使用 make_regression生成一个回归数据集。
        # n_samples = 100表示生成100个样本。
        # n_features = 1表示每个样本有一个特征。
        # noise = 10 表示数据集中的噪声水平。
        # coef = True 返回数据生成的系数。
        # bias = 14.5 设置数据的偏置。
        # random_state = 0 确保每次生成的数据相同。




#构建数据加载器
def  data_loader(x,y,batch_size):
    #计算下样本的数量
    data_len=len(y)
    #构建数据索引
    data_index=list(range(data_len))
    #使用 random.shuffle 打乱数据索引,以实现随机抽样。
    random.shuffle(data_index)
    #计算batch的数量
    batch_number=data_len // batch_size  #//整除

    #然后,通过迭代批次数,每次生成一个批次的数据
    # 使用一个for 循环迭代批次数量 batch_number。
    for idx in range (batch_number):
        # 计算每个批次的起始索引 start和结束索引end。
        start=idx*batch_size
        end=start+batch_size
        # 根据索引从x和y中切片出批次的特征数据 batch_train_x和目标数据batch_train_y。
        batch_train_x=x[start:end]
        batch_train_y=y[start:end]
        # 使用 yield 关键字生成批次数据。这使得data_loader函数成为一个生成器,可以逐批次地提供数据。
        yield batch_train_x,batch_train_y



def test01():
    x,y,coef=create_dataset()

    #使用 matplotlib.pyplot 绘制散点图展示数据。
    plt.scatter(x,y)#使用 plt.scatter(x, y) 绘制特征数据 x 和目标数据 y 的散点图
    plt.show()      #使用 plt.show() 显示图形。

    for x,y in data_loader(x,y,batch_size=10):
        print(y)
    #调用 data_loader 函数,传入特征数据 x、目标数据 y 和批次大小 batch_size=10。
    #使用 for 循环迭代生成的批次数据。在每次迭代中,打印当前批次的目标数据 y。
    #在每次迭代中,打印当前批次的目标数据 y。

#假设函数
w = torch.tensor(0.1,requires_grad=True,dtype=torch.float64)#w权重  全局变量 一定要设置requires_grad=True因为在进行优化函数更新的时候一定要进行梯度计算
b = torch.tensor(0.0,requires_grad=True,dtype=torch.float64)#b偏置  全局变量
def linear_regression (x):
    return w*x+ b
    # 这是一个函数定义,接收输入特征 x。
    # 函数体中,计算线性回归的预测值 y^=wx+b

#损失函数 :平方损失
def square_loss(y_pred, y_true) :
    return (y_pred - y_true)**2

#数据更新  梯度下降的优化方法
def sgd (lr=1e-2) :      #learning rate学习率控制着在每次迭代中参数更新的步长。
    #此处除以16使用的是批次样本的平均梯度值
    #w.data 和 b.data 表示权重和偏置的值。或者  w.grad.data 和 b.grad.data 表示权重和偏置的梯度。
    #1/16 表示除以批次大小,这里假设批次大小为16。这是因为在每次迭代中,我们希望使用批次样本的平均梯度值来更新参数。
    w.data = w.data - lr * w.grad.data / 16  #w.grad是计算了一个批次的数据累计的梯度值,在进行梯度下降的时候要,把梯度值除以一个batch_size。然后进行梯度下降的计算。实现参数的更新
    b.data = b.data - lr * b.grad.data / 16


#训练函数
def train():
    #加载数据集
    x,y,coef=create_dataset()
    #定义训练的参数
    epochs=100         #表示训练的总轮数
    learning_rate=0.01 #表示学习率,控制每次迭代中参数的更新步长
    #存储训练过程中的信息
    epoch_loss=[]  #每一个epochs训练完后的平均损失
    total_loss=0.0 #用于累计总损失
    train_samples=0#训练样本的数量需要统计一下

    for _ in range(epochs):#循环重复训练
        #内层一个循环一次叫做一个迭代,一次把所有模型都训练一遍,但训练一遍是远远不够的,将所有的样本往模型里面重复训练n次就是需要外层循环
        for train_x, train_y in data_loader(x, y, batch_size=16):  ####训练的时候每次送进去的数据量是一个batch_size
            # 1.将训练样本送入模型进行预测
            y_pred = linear_regression(train_x)

            # 2.计算预测值和真实值之间的平方损失
            # print(train_y.shape)  # torch.Size([16])
            # print(y_pred.shape)  # torch.Size([16, 1])
            #将train_y和y_pred转换成同样的形式
            loss = square_loss(y_pred, train_y.reshape(-1, 1)).sum()  # 表示成多行1列    使用-1代替省略的形状
            total_loss += loss.item()
            train_samples += len(train_y)

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

            # 4.自动微分 (反向传播)为了更新参数  有了梯度就能进行梯度下降的梯度更新
            loss.backward()

            # 5.参数更新
            sgd(learning_rate)

            print('loss:%.10f' % (total_loss / train_samples))  # 求出平均损失
        # 记录每一个epochs的平均损失
        epoch_loss.append(total_loss / train_samples)   #.append() 函数是 list 类型的一个方法,用于将一个元素添加到列表的末尾。这个方法返回 None,但它会修改原始列表。

#训练结束后可以 绘制拟合直线,损失变换
    #先绘制数据集散点图
                                                                                                                                                 # print(coef, w.data.item())
    plt.scatter(x,y)                          #使用Matplotlib的scatter函数绘制x和y数据点的散点图
    #绘制拟合直线
    x=torch.linspace(x.min(),x.max(),1000)    #创建一个包含1000个元素的张量,数值从x的最小值线性变化到x的最大值。
    y1=torch.tensor([v * w + 14.5 for v in x])   #y1训练值  14.5是自己设置的bias偏置
    y2=torch.tensor([v * coef + 14.5 for v in x])#y2真实值

    plt.plot( x , y1, label='训练')
    plt.plot( x , y2, label='真实')
    plt.grid()           #添加网格线到图表
    plt.legend()         #显示图例
    plt.show()           #显示图表。

    #打印损失变化的曲线
    plt.plot(range(epochs),epoch_loss)
    plt.grid()
    plt.title('损失变化曲线')
    plt.show()


if __name__ == '__main__':
    train()

小结:

本小节我们主要手动构建数据集,假设函数,训练函数等来练习下对 PyTorch 相关API的使用。

手动构建线性回归模型不是一个很好的方案我们应该更多的利用pytorch里面提供的基础组建帮助我们实现网络搭建实现模型的构建实现网络训练。

1.10模型定义方法

1.10.1使用PyTorch构建线性回归

使用手动的方式来构建了一个简单的线性回归模型,如果碰到一些较大的网络设计,手动构建过于繁琐。所以,我们需要学会使用 PyTorch 的各个组件来搭建网络。接下来,我们使用 PyTorch 提供的接口来定义线性回归。

  1. 使用 PyTorch 的 nn.MSELoss() 代替自定义的平方损失函数

#初始化平方损失函数对象

    criterion = nn.MSELoss()        #mean square error均方误差

    # 该类内部重写了——call——方法,所以对象可以当做函数来使用

    torch.random.manual_seed(0)

    y_pred=torch.randn(3,5,requires_grad=True)

    y_true=torch.randn(3,5)

    #计算损失

    loss=criterion(y_pred,y_true)

    print(loss)

  1. 使用 PyTorch 的 nn.Linear 代替自定义的假设函数

#假设输入数据的特征要有10个,输出数据的特征要有5个

    model = nn.Linear(in_features=10,out_features=5)#构建模型

#输入数据

    inputs=torch.randn(4,10)#4个样本,每个样本有10个特征

#nn.Linear实现了_call_方法,可以直接把对象当函数使用

    y_pred= model(inputs)

print(y_pred.shape)

  1. 使用 PyTorch 的 optim.SGD 代替自定义的优化器

model = nn.Linear(in_features=10, out_features=5)#构建模型

    #优化方法: 更新模型参数

optimizer=optim.SGD(model.parameters(),lr=1e-3)

#传递模型参数model.parameters,还要设置一下学习率(因为用的梯度下降的优化方法)

    #在backward()调用前要进行梯度清零

    optimizer.zero_grad()

    # 此处省略了backward()函数,假设该函数掉用完毕,有梯度值了

    #更新模型参数

    optimizer.step() 

import  torch
import torch.nn as nn
import torch.optim as optim

#1.损失函数
def test01():

    #初始化平方损失函数对象
    criterion = nn.MSELoss()        #mean square error均方误差
    # 该类内部重写了——call——方法,所以对象可以当做函数来使用
    torch.random.manual_seed(0)
    y_pred=torch.randn(3,5,requires_grad=True)
    y_true=torch.randn(3,5)
    #计算损失
    loss=criterion(y_pred,y_true)
    print(loss)

#2.假设方法
def test02():
    #输入数据的特征要有10个,输出数据的特征要有5个
    model = nn.Linear(in_features=10,out_features=5)#构建模型
    #输入数据
    inputs=torch.randn(4,10)#4个样本,每个样本有10个特征

    #nn.Linear实现了_call_方法,可以直接把对象当函数使用
    y_pred= model(inputs)
    print(y_pred.shape)

#3.优化方法
def test03():
    model = nn.Linear(in_features=10, out_features=5)#构建模型
    #优化方法: 更新模型参数
    optimizer=optim.SGD(model.parameters(),lr=1e-3)#传递模型参数model.parameters,还要设置一下学习率(因为用的梯度下降的优化方法)

    #在backward()调用前要进行梯度清零
    optimizer.zero_grad()

    # 此处省略了backward()函数,假设该函数掉用完毕,有梯度值了

    #更新模型参数
    optimizer.step()

if __name__ == '__main__':
  test02()

torch.nn 模块提供了构建神经网络所需的类和函数。

  1. 定义神经网络结构: 使用 torch.nn.Module 类来定义自己的神经网络结构。
  2. 使用内置的神经网络层: torch.nn 提供了许多内置的神经网络层,如 nn.Linearnn.Conv2d 等。
  3. 损失函数: torch.nn 还提供了多种损失函数,如 nn.CrossEntropyLossnn.MSELoss 等。

torch.optim 模块提供了多种优化算法,用于优化神经网络的参数。

  1. 定义优化器: 使用 torch.optim 中的类来定义优化器,如 optim.SGDoptim.Adam 等。
  2. 优化过程: 在训练循环中,使用优化器的 zero_grad()step() 方法来更新模型参数。

 

 

1.10.2数据加载器

  1. 构建数据类,可以自己编写一个数据类,并让该类继承自Dataset对象,并在类的内部实现:_init_、_len_、_getitem方法,该方法当样本比较复杂的时候,建议使用。
  2. 如果样本比较简单,直接通过是索引就可以返回,此时使用TensorDataset来构建数据集
  3. 如果样本比较简单,直接通过是索引就可以返回,此时使用TensorDataset来构建数据集
import torch
from torch.utils.data import Dataset
from torch.utils.data import DataLoader
from torch.utils.data import TensorDataset


#1。数据类构建
class SampleDataset(Dataset):
    def __init__(self,x,y):
        """初始化"""
        self.x=x
        self.y=y
        self.len=len(y)

    def __len__(self):
        """返回数据总量"""
        return self.len
    def __getitem__(self, idx):
        """根据索引返回一条样本"""
        #将idx限定在合理的范围内
        idx=min(max(idx,0), self.len-1)
        return self.x[idx],self.y[idx]

def test01():
        # 构建包含100个样本的数据集,每个样本有8个特征
        x= torch.randn(100, 8)
        y = torch.randint(0,2,[x.size(0), ])
        # 构建数据加载器的步骤:1。构建数据类型 2.构建数据加载器
        sample_dataset=SampleDataset(x,y)
        print(sample_dataset[0])
        #每次传递一个索引就会返回对应索引位置的样本

#2。数据加载器的使用
def test02():
    #1.先构建数据对象
    x = torch.randn(100, 8)
    y = torch.randint(0, 2, [x.size(0), ])
    sample_dataset = SampleDataset(x, y)
    #2.使用DataLoder构建数据加载器  DataLoder是已有的
    dataloader=DataLoader(sample_dataset,batch_size=4,shuffle=True)#shuffle=True把样本随机打乱,一次返回batch_size=4个

    for x,y in dataloader:
        print(x)
        print(y)
        break
#3。简单的数据类型构建方法 from torch.utils.data import TensorDataset
def test03():
    x = torch.randn(100, 8)
    y = torch.randint(0, 2, [x.size(0), ])
    sample_dataset=TensorDataset(x,y)
    dataloader=DataLoader(sample_dataset,batch_size=4,shuffle=True)#构建数据加载器
    for x,y in dataloader:
        print(x)
        print(y)
        break

if __name__ == '__main__':
    test03()

1.10.3使用组件构建线性回归

import torch
from torch.utils.data import TensorDataset  #构建数据集对象
from torch.utils.data import DataLoader     #构建数据加载器

import torch.nn as nn
import torch.optim as optim
from sklearn.datasets import make_regression
import matplotlib.pyplot as plt


# 构建数据集
def create_dataset():

    x, y, coef = make_regression(n_samples=100,
                                 n_features=1,
                                 noise=10,
                                 coef=True,
                                 bias=14.5,
                                 random_state=0)

    # 将构建数据转换为张量类型
    x = torch.tensor(x)
    y = torch.tensor(y)

    return x, y, coef


def train():
    total_loss = 0.0
    train_samples = 0
    epoch_loss = []
    # 构建数据集
    x, y, coef = create_dataset()
    # 构建数据集对象
    dataset = TensorDataset(x, y)
    # 构建数据加载器
    dataloader = DataLoader(dataset, batch_size=16, shuffle=True)
    # 构建模型
    model = nn.Linear(in_features=1, out_features=1)
    # 构建损失函数
    criterion = nn.MSELoss()
    # 优化方法
    optimizer = optim.SGD(model.parameters(), lr=1e-2)
    # 初始化训练参数
    epochs = 100

    for _ in range(epochs):

        for train_x, train_y in dataloader:

            # 将一个batch的训练数据送入模型
            y_pred = model(train_x.type(torch.float32))
            # 计算损失值
            # print(y_pred.dtype,train_y.dtype)
            loss = criterion(y_pred, train_y.reshape(-1, 1).type(torch.float32))
            total_loss += loss.item()
            train_samples += len(train_y)
            # 梯度清零
            optimizer.zero_grad()
            # 自动微分(反向传播)
            loss.backward()
            # 更新参数
            optimizer.step()
        epoch_loss.append(total_loss / train_samples)


    # 绘制拟合直线
    plt.scatter(x, y)
    x = torch.linspace(x.min(), x.max(), 1000)
    y1 = torch.tensor([v * model.weight + model.bias for v in x])
    y2 = torch.tensor([v * coef + 14.5 for v in x])

    plt.plot(x, y1, label='xunlian')
    plt.plot(x, y2, label='zhenshi')
    plt.grid()
    plt.legend()
    plt.show()

    # 打印损失变化的曲线
    plt.plot(range(epochs), epoch_loss)
    plt.grid()
    plt.title('sunshibianhuaquxian')
    plt.show()


if __name__ == '__main__':
    train()

 

1.11模型的保存加载

学习 目标

  • 掌握PyTorch保存模型的方法

神经网络的训练有时需要几天、几周、甚至几个月,为了在每次使用模型时避免高代价的重复训练,我们就需要将模型序列化到磁盘中,使用的时候反序列化到内存中。

PyTorch 提供了两种保存模型的方法:

  1. 直接序列化模型对象
  2. 存储模型的网络参数

 

 

1.11.1直接序列华对象

class Model(nn.Module):

    def __init__(self, input_size, output_size):

        super(Model,self).__init__()#调用父类方法

        #穿件实列化两个线性层

        self.liner1=nn.Linear(input_size,input_size*2)

        self.liner2=nn.Linear(input_size*2,output_size)

    def forward(self,inputs):

        inputs=self.liner1(inputs)

        output=self.liner2(inputs)

        return output

    #简单的网络搭建完

 model = Model(128, 10)

    # 当我们的模型类继承了nn.model并且实现了forward函数

    # 此时我们就可以把模型函数当做函数使用

    # model()

    # 第一个参数: 存储的模型 # 第二个参数: 存储的路径# 第三个参数: 使用的模块

# 第四个参数: 存储的协议

    torch.save(model , 'model/test_model_save.pth', pickle_module=pickle,  pickle_protocol=2)

def test02():

    # 第一个参数: 加载的路径

    # 第二个参数: 模型加载的设备

    # 第三个参数: 加载的模块

    model=torch.load('model/test_model_save.pth',  map_location='cpu', pickle_module=pickle)

    #当我们训练的模型在 GPU 中时,torch.save 函数将其存储到磁盘中。当再次加载该模型时,会将该模型从磁盘先加载到 CPU 中,再移动到指定的 GPU 中,例如: cuda:0、cuda:1。

    # 但是,当重新加载的机器不存在 GPU 时,模型加载可能会出错,这时,可通过 map_localtion=’cpu’ 将其加载到 CPU 中。

先在自己创建一个文件model.py

import torch.nn as nn
#1-11-1模型的保存加载-直接序列化对象
class Model(nn.Module):
    def __init__(self, input_size, output_size):
        super(Model,self).__init__()#调用父类方法
        #穿件实列化两个线性层
        self.liner1=nn.Linear(input_size,input_size*2)
        self.liner2=nn.Linear(input_size*2,output_size)

    def forward(self,inputs):
        inputs=self.liner1(inputs)
        output=self.liner2(inputs)
        return output
    #简单的网络搭建完
import pickle
import torch
from model import Model

import torch.nn as nn

def test01():

    model = Model(128, 10)
    # 当我们的模型类继承了nn.model并且实现了forward函数
    # 此时我们就可以把模型函数当做函数使用
    # model()
    # 第一个参数: 存储的模型
    # 第二个参数: 存储的路径
    # 第三个参数: 使用的模块
    # 第四个参数: 存储的协议
    torch.save(model, 'model/test_model_save.pth',pickle_module=pickle, pickle_protocol=2)

def test02():
    # 第一个参数: 加载的路径
    # 第二个参数: 模型加载的设备
    # 第三个参数: 加载的模块
    model = torch.load('model/test_model_save.pth', map_location='cpu', pickle_module=pickle)
    #当我们训练的模型在 GPU 中时,torch.save 函数将其存储到磁盘中。当再次加载该模型时,会将该模型从磁盘先加载到 CPU 中,再移动到指定的 GPU 中,例如: cuda:0、cuda:1。
    # 但是,当重新加载的机器不存在 GPU 时,模型加载可能会出错,这时,可通过 map_localtion=’CPU’ 将其加载到 CPU 中。
    print(model)


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

1.11.2存储模型的网络参数

#初始化模型参数

    model = Model(128, 10)

    #存储模型内部的参数

    #存储在训练过程中的一些参数

    #初始优化器

    optimizer = optim.Adam(model.parameters(), lr=1e-3)

# 定义存储的模型参数

    save_params = {

        'init_params': {

            'input_size': 128, #输入维度

            'output_size': 10  #输出维度

        },

        'acc_score': 0.98,#准确率分数

        'avg_loss': 0.86, #平均损失

        'iter_numbers': 100,#当前迭代次数

        'optim_params': optimizer.state_dict(),#优化方法

        'model_params': model.state_dict()  ######存储模型里面的参数

    }

    # 使用save函数存储模型参数

torch.save(save_params, 'model/model_params.pth')

#加载参数恢复模型

    # 从磁盘中将参数加载模型参数

    model_params = torch.load('model/model_params.pth')

    # 使用参数来初始化模型

    model = Model(model_params['init_params']['input_size'], model_params['init_params']['output_size'])

    model.load_state_dict(model_params['model_params'])

    # 使用参数初始化优化器

    optimizer = optim.Adam(model.parameters())

    optimizer.load_state_dict(model_params['optim_params'])

    # 显示其他参数

    print('迭代次数:', model_params['iter_numbers'])

    print('准确率:', model_params['acc_score'])

    print('平均损失:', model_params['avg_loss'])

import torch
import torch.nn as nn
import torch.optim as optim


class Model(nn.Module):

    def __init__(self, input_size, output_size):

        super(Model, self).__init__()
        self.linear1 = nn.Linear(input_size, input_size * 2)
        self.linear2 = nn.Linear(input_size * 2, output_size)

    def forward(self, inputs):

        inputs = self.linear1(inputs)
        output = self.linear2(inputs)
        return output
#简单的网络搭建完

#1.实现模型参数存储
def test01():
    #初始化模型参数
    model = Model(128, 10)
    #存储模型内部的参数
    #存储在训练过程中的一些参数

    #初始优化器
    optimizer = optim.Adam(model.parameters(), lr=1e-3)

    # 定义存储的模型参数
    save_params = {
        'init_params': {
            'input_size': 128, #输入维度
            'output_size': 10  #输出维度
        },
        'acc_score': 0.98,#准确率分数
        'avg_loss': 0.86, #平均损失
        'iter_numbers': 100,#当前迭代次数
        'optim_params': optimizer.state_dict(),#优化方法
        'model_params': model.state_dict()  ######存储模型里面的参数
    }

    # 使用save函数存储模型参数
    torch.save(save_params, 'model/model_params.pth')


def test02():

    #加载参数恢复模型
    # 从磁盘中将参数加载模型参数
    model_params = torch.load('model/model_params.pth')
    # 使用参数来初始化模型
    model = Model(model_params['init_params']['input_size'], model_params['init_params']['output_size'])
    model.load_state_dict(model_params['model_params'])
    # 使用参数初始化优化器
    optimizer = optim.Adam(model.parameters())
    optimizer.load_state_dict(model_params['optim_params'])
    # 显示其他参数
    print('迭代次数:', model_params['iter_numbers'])
    print('准确率:', model_params['acc_score'])
    print('平均损失:', model_params['avg_loss'])


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

 

1.12简单的网络模型搭建

在PyTorch中搭建一个简单的神经网络模型通常涉及以下几个步骤:

1. **导入必要的库**:
   首先,你需要导入PyTorch及相关库。

   ```python
   import torch
   import torch.nn as nn
   import torch.optim as optim
   ```

2. **定义网络结构**:
   创建一个类继承自 `nn.Module`,并定义你的网络层。

   ```python
   class SimpleNet(nn.Module):
       def __init__(self):
           super(SimpleNet, self).__init__()
           self.fc1 = nn.Linear(in_features=784, out_features=128)  # 784个输入特征,例如MNIST图像大小是28x28
           self.relu = nn.ReLU()
           self.fc2 = nn.Linear(in_features=128, out_features=10)  # 10个输出特征,例如MNIST总共有10个类别

       def forward(self, x):
           x = self.fc1(x)
           x = self.relu(x)
           x = self.fc2(x)
           return x
   ```

3. **实例化网络**:
   创建网络的实例。

   ```python
   model = SimpleNet()
   ```

4. **定义损失函数和优化器**:
   选择一个损失函数和优化器。

   ```python
   criterion = nn.CrossEntropyLoss()  # 交叉熵损失,常用于多分类问题
   optimizer = optim.Adam(model.parameters(), lr=0.001)  # Adam优化器
   ```

5. **训练网络**:
   编写训练循环。

   ```python
   for epoch in range(num_epochs):
       for data, target in train_loader:  # 假设train_loader是已经定义好的数据加载器
           optimizer.zero_grad()  # 清空梯度
           output = model(data)  # 前向传播
           loss = criterion(output, target)  # 计算损失
           loss.backward()  # 反向传播
           optimizer.step()  # 更新参数
   ```

6. **评估模型**:
   在测试集上评估模型的性能。

   ```python
   correct = 0
   total = 0
   with torch.no_grad():  # 在评估阶段不计算梯度
       for data, target in test_loader:  # 假设test_loader是已经定义好的数据加载器
           output = model(data)
           _, predicted = torch.max(output.data, 1)
           total += target.size(0)
           correct += (predicted == target).sum().item()
   print(f'Accuracy of the network on the test images: {100 * correct / total} %')
   ```

7. **保存和加载模型**:
   保存训练好的模型或模型的状态字典,并在需要时加载。

   ```python
   # 保存模型的状态字典
   torch.save(model.state_dict(), 'model_state.pth')
   
   # 加载模型的状态字典
   model = SimpleNet()
   model.load_state_dict(torch.load('model_state.pth'))
   ```

以上步骤展示了如何使用PyTorch搭建和训练一个简单的神经网络模型。在实际应用中,你可能需要根据具体问题调整网络结构、损失函数和优化器等。
 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值