pytorch 代码练习

  • pytorch 代码练习

一、pytorch基础练习

PyTorch是一个python库,它主要提供了两个高级功能:

  • GPU加速的张量计算
  • 构建在反向自动求导系统上的深度神经网络

定义数据使用torch.Tensor , tensor的意思是张量,是数字各种形式的总称

  • 可以定义数、向量、二维数组和张量。
import torch
# 可以是一个数
x = torch.tensor(666)
print(x)
# 可以是一维数组(向量)
x = torch.tensor([1,2,3,4,5,6])
print(x)
# 可以是二维数组(矩阵)
x = torch.ones(2,3)
print(x)
# 可以是任意维度的数组(张量)
x = torch.ones(2,3,4)
print(x)
  • Tensor支持各种各样类型的数据,包括:

    torch.float32, torch.float64, torch.float16, torch.uint8, torch.int8, torch.int16, torch.int32, torch.int64 。

    可以使用empty、rand、zeros、new_ones、randn_like等函数初始化张量,并使用dtype改变数据类型。

# 创建一个空张量
x = torch.empty(5,3)
print(x)
# 创建一个随机初始化的张量
x = torch.rand(5,3)
print(x)
# 创建一个全0的张量,里面的数据类型为 long
x = torch.zeros(5,3,dtype=torch.long)
print(x)
# 基于现有的tensor,创建一个新tensor,
# 从而可以利用原有的tensor的dtype,device,size之类的属性信息
y = x.new_ones(5,3)   #tensor new_* 方法,利用原来tensor的dtype,device
print(y)
z = torch.randn_like(x, dtype=torch.float)    # 利用原来的tensor的大小,但是重新定义了dtype
print(z)
  • 用Tensor来进行的计算

    • 基本运算,加减乘除,求幂求余
    • 布尔运算,大于小于,最大最小
    • 线性运算,矩阵乘法,求模,求行列式

    基本运算包括: abs/sqrt/div/exp/fmod/pow ,及一些三角函数 cos/ sin/ asin/ atan2/ cosh,及 ceil/round/floor/trunc 等

    布尔运算包括: gt/lt/ge/le/eq/ne,topk, sort, max/min

    线性计算包括: trace, diag, mm/bmm,t,dot/cross,inverse,svd 等

    其中的方法与Python语法几乎一致。

# 创建一个 2x4 的tensor
m = torch.Tensor([[2, 5, 3, 7],
                  [4, 2, 1, 9]])
#这里使用强制类型转换,使得其转换成int
m.long()
print(m.size(0), m.size(1), m.size(), sep=' -- ')
# 返回 m 中元素的数量
print(m.numel())
# 返回 第0行,第2列的数
print(m[0][2])
tensor(3.)
# 返回 第1列的全部元素
print(m[:, 1])
tensor([5., 2.])
# 返回 第0行的全部元素
print(m[0, :])
# Create tensor of numbers from 1 to 5
# 注意这里结果是1到4,没有5,由于tensor的类型为float,这里把v数据类型改为float
v = torch.arange(1, 5,dtype=torch.float)
print(v)
tensor([1, 2, 3, 4])

张量m的数据类型为float,与v进行内积时,需要将v数据类型改为float,这样才能进行运算进行内积(点乘)

image-20211006115125927

可以抽取一行进行内积。

image-20211006115437941

  • 支持向量的内积、矩阵转置等,算术运算有广播机制。
# Scalar product
m @ v
# Calculated by 1*2 + 2*5 + 3*3 + 4*7
m[[0], :] @ v

# Add a random tensor of size 2x4 to m
m + torch.rand(2, 4)

# 转置,由 2x4 变为 4x2
print(m.t())

# 使用 transpose 也可以达到相同的效果,具体使用方法可以百度
print(m.transpose(0, 1))

# returns a 1D tensor of steps equally spaced points between start=3, end=8 and steps=20
torch.linspace(3, 8, 20)
  • 可结合matplotlib使用,使用matplotlib时,需要将Tensor转换成Numpy的ndarry数据(使用.numpy()方法),再进行显示。
from matplotlib import pyplot as plt

# matlabplotlib 只能显示numpy类型的数据,下面展示了转换数据类型,然后显示
# 注意 randn 是生成均值为 0, 方差为 1 的随机数
# 下面是生成 1000 个随机数,并按照 100 个 bin 统计直方图
plt.hist(torch.randn(1000).numpy(), 100);

# 当数据非常非常多的时候,正态分布会体现的非常明显
plt.hist(torch.randn(10**6).numpy(), 100);

image-20211005231543718image-20211005231607163

  • 此外还支持张量的拼接等。

二、 螺旋数据分类

这里生成了三种类型的二维数据,每类样本都有1000个,使用两种方法对这三种类型的数据进行分类,第一种方法是通过构建线性模型分类,不加入激活函数(即不具有非线性分类能力,只能进行线性分类)。第二种方法是通过构建两层神经网络分类,与第一种相比加入了激活函数。(即加入了非线性分类能力)

  1. 下载plot_lib绘图库到本地,里面有画点的过程中要用到里面的一些函数。

    !wget https://raw.githubusercontent.com/Atcold/pytorch-Deep-Learning/master/res/plot_lib.py
    
  2. 引入基本的库,初始化参数。

    import random
    import torch
    from torch import nn, optim
    import math
    from IPython import display
    from plot_lib import plot_data, plot_model, set_default
    
    # 因为colab是支持GPU的,torch 将在 GPU 上运行
    device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")
    print('device: ', device)
    
    # 初始化随机数种子。神经网络的参数都是随机初始化的,
    # 不同的初始化参数往往会导致不同的结果,当得到比较好的结果时我们通常希望这个结果是可以复现的,
    # 因此,在pytorch中,通过设置随机数种子也可以达到这个目的
    seed = 12345
    random.seed(seed)
    torch.manual_seed(seed)
    
    N = 1000  # 每类样本的数量
    D = 2  # 每个样本的特征维度
    C = 3  # 样本的类别
    H = 100  # 神经网络里隐层单元的数量
    
  3. 对数据进行初始化,三类的点,X中存储的是点的坐标,X的为一个 NxC (即1000X3)行, D(即2) 列的矩阵。C 类样本,每类样本是 N个,所以是 N*C 行。每个样本的特征维度是2,所以是 2列。

    X = torch.zeros(N * C, D).to(device)
    Y = torch.zeros(N * C, dtype=torch.long).to(device)
    for c in range(C):
        index = 0
        t = torch.linspace(0, 1, N) # 在[0,1]间均匀的取10000个数,赋给t
        # 下面的代码不用理解太多,总之是根据公式计算出三类样本(可以构成螺旋形)
        # torch.randn(N) 是得到 N 个均值为0,方差为 1 的一组随机数,注意要和 rand 区分开
        inner_var = torch.linspace( (2*math.pi/C)*c, (2*math.pi/C)*(2+c), N) + torch.randn(N) * 0.2
        
        # 每个样本的(x,y)坐标都保存在 X 里
        # Y 里存储的是样本的类别,分别为 [0, 1, 2]
        for ix in range(N * c, N * (c + 1)):
            X[ix] = t[index] * torch.FloatTensor((math.sin(inner_var[index]), math.cos(inner_var[index])))
            Y[ix] = c
            index += 1
    
    print("Shapes:")
    print("X:", X.size())
    print("Y:", Y.size())
    
  4. 使用plot_lib的plot_data函数可以显示图象。

    # visualise the data
    plot_data(X, Y)
    

至此,数据准备完毕。

此后:使用上述的两种方法,使用nn包建立模型,使用交叉熵验证模型的损失,并计算正确率。

  1. 构建线性模型

    learning_rate = 1e-3
    lambda_l2 = 1e-5
    
    # nn 包用来创建线性模型
    # 每一个线性模型都包含 weight 和 bias
    model = nn.Sequential(
        nn.Linear(D, H),
        nn.Linear(H, C)
    )
    model.to(device) # 把模型放到GPU上
    
    # nn 包含多种不同的损失函数,这里使用的是交叉熵(cross entropy loss)损失函数
    criterion = torch.nn.CrossEntropyLoss()
    
    # 这里使用 optim 包进行随机梯度下降(stochastic gradient descent)优化
    optimizer = torch.optim.SGD(model.parameters(), lr=learning_rate, weight_decay=lambda_l2)
    
    # 开始训练
    for t in range(1000):
        # 把数据输入模型,得到预测结果
        y_pred = model(X)
        # 计算损失和准确率
        loss = criterion(y_pred, Y)
        score, predicted = torch.max(y_pred, 1)
        acc = (Y == predicted).sum().float() / len(Y)
        print('[EPOCH]: %i, [LOSS]: %.6f, [ACCURACY]: %.3f' % (t, loss.item(), acc))
        display.clear_output(wait=True)
    
        # 反向传播前把梯度置 0 
        optimizer.zero_grad()
        # 反向传播优化 
        loss.backward()
        # 更新全部参数
        optimizer.step()
    
    print(y_pred.shape)
    print(y_pred[10, :])
    print(score[10])
    print(predicted[10])
    

    ​ 使用 print(y_pred.shape) 可以看到模型的预测结果,为[3000, 3]的矩阵。每个样本的预测结果为3个,保存在 y_pred 的一行里。值最大的一个,即为预测该样本属于的类别

    ​ score, predicted = torch.max(y_pred, 1) 是沿着第二个方向(即X方向)提取最大值。最大的那个值存在 score 中,所在的位置(即第几列的最大)保存在 predicted 中。

    ​ 此外,每一次反向传播前,都要把梯度清零,这是因为默认情况下会将梯度叠加,梯度清零有利于后续继续进行反向传播。

  2. 构建双层神经网络模型

    learning_rate = 1e-3
    lambda_l2 = 1e-5
    
    # 这里可以看到,和上面模型不同的是,在两层之间加入了一个 ReLU 激活函数
    model = nn.Sequential(
        nn.Linear(D, H),
        nn.ReLU(),   #与1代码块的唯一区别,加入了激活函数
        nn.Linear(H, C)
    )
    model.to(device)
    
    # 下面的代码和之前是完全一样的,这里不过多叙述
    criterion = torch.nn.CrossEntropyLoss()
    optimizer = torch.optim.Adam(model.parameters(), lr=learning_rate, weight_decay=lambda_l2) # built-in L2
    
    # 训练模型,和之前的代码是完全一样的
    for t in range(1000):
        y_pred = model(X)
        loss = criterion(y_pred, Y)
        score, predicted = torch.max(y_pred, 1)
        acc = ((Y == predicted).sum().float() / len(Y))
        print("[EPOCH]: %i, [LOSS]: %.6f, [ACCURACY]: %.3f" % (t, loss.item(), acc))
        display.clear_output(wait=True)
        
        # zero the gradients before running the backward pass.
        optimizer.zero_grad()
        # Backward pass to compute the gradient
        loss.backward()
        # Update params
        optimizer.step()
    
  3. 结果比较

    • 这是使用第一种方法(构建线性模型)得出的最后结果

      image-20211005234135011

      上面使用 print(model) 把模型输出,可以看到有两层:

      • 第一层输入为 2(因为特征维度为主2),输出为 100;
      • 第二层输入为 100 (上一层的输出),输出为 3(类别数)

      从上面图示可以看出,线性模型的准确率最高只能达到 50% 左右,对于这样复杂的一个数据分布,线性模型难以实现准确分类。

    • 这是使用第二种方法(构建双层神经网络模型)得出的最后结果

      image-20211005234429443

  4. 模型可视化后比较

    • 这是这是使用第一种方法(构建线性模型)得出的模型可视化结果

    • 这是这是使用第二种方法(构建双层神经网络模型)得出的模型可视化结果

      image-20211005234601399

      可以看出,加入了激活函数ReLU之后,可以解决非线性问题。在两层神经网络里加入 ReLU 激活函数以后,分类的准确率得到了显著提高。

posted @ 2021-10-06 11:14  ZQFive  阅读(240)  评论(0)    收藏  举报