Pytorch学习记录

学习网站:《PyTorch深度学习实践》完结合集_哔哩哔哩_bilibili

 NLPnote/1.2.7 使用pytorch实现手写数字识别.md at master · SpringMagnolia/NLPnote (github.com)

import torch
import numpy as np
#张量tensor

#创建张量
t=torch.tensor([[1.,-1.],[1.,-1.]])#使用python中的列表或者序列创建tensor
print(t)
t=torch.tensor(np.array([[1,2,3],[4,5,6]]))#使用numpy中的数组创建tensor
print(t)
#torch.empty(3,4)创建3行4列的空的tensor,会用无用数据进行填充
#torch.ones([3,4]) 创建3行4列的全为1的tensor
#torch.zeros([3,4])创建3行4列的全为0的tensor
#torch.rand([3,4]) 创建3行4列的随机值的tensor,随机值的区间是[0, 1)
#torch.randn([3,4]) 创建3行4列的随机数的tensor,随机值的分布式均值为0,方差为1
t=torch.rand([3,4])#随机值
print(t)
t=torch.randint(low=0,high=10,size=[3,4])#随机整数,区间【low,high】
print(t)

#常用方法
#1.获取tensor中只有1个元素数据:tensor.item()
a=torch.randn(1)
t=a.item()
print(t)
#2.转化为numpy数组:z.numpy()
#3.获取形状:tensor.size()
#4.形状改变:tensor.view((3,4))
#5.获取阶数:tensor.dim()
#6.获取最大值:tensor.max()
#7.转置:tensor.t()
#8.tensor[1,3] 获取tensor中第一行第三列的值
#9.tensor[1,3]=100 对tensor中第一行第三列的位置进行赋值100
#10.tensor的切片
a = torch.rand(4, 3, 28, 28)
print(a[:2].shape)  # 在第一个维度上取后0和1
print(a[:2, :1, :, :].shape)  # 在第一个维度上取0和1,在第二个维度上取0
print(a[:2, 1:, :, :].shape)  # 在第一个维度上取0和1,在第二个维度上取1,2
print(a[:2, -2:, :, :].shape)  # 在第一个维度上取0和1,在第二个维度上取1,2
print(a[:, :, 0:28:2, 0:28:2].shape)  # step=2隔行采样
print(a[:, :, ::2, ::2].shape)  # 等同于这个

#tensor数据类型
#获取tensor的数据类型:tensor.dtype 创建数据的时候指定类型:torch.ones([2,3],dtype=torch.float32) 类型的修改

#tensor相加: torch.add(x,y) 或者 x.add(y) 以及 x.add_(y)带下划线对x修改,以及和数字相加
#torch.cuda这个模块增加了对CUDA tensor的支持,能够在cpu和gpu上使用相同的方法操作tensor
#通过.to方法能够把一个tensor转移到另外一个设备(比如从CPU转到GPU)

 梯度下降和反向传播

 

梯度是一个向量,导数+变化最快的方向(学习的前进方向),需要取梯度下降的方向即梯度的反方向作为变化方向。

梯度下降算法:

 

梯度就是多元函数参数的变化趋势(参数学习的方向),只有一个自变量时称为导数

计算图:通过图的方式来描述函数的图形

用目标函数对权重求导,求到上升方向;

 下降方向求倒数的负方向:

学习率,表示往前走多远,一般取值会比较小,如果取得特别大可能无法收敛。

“贪心算法”:每次迭代都朝着下降最快的方向去往前走一步

随机梯度下降:

选择的是单个样本点的损失为标准

 梯度更新公式

梯度下降算法很难达到全局最优,使用随机梯度下降算法,性能好,但时间复杂度高。

综合梯度下降和随机梯度下降算法,折中:patch(mini-patch),批量的随机梯度下降,将若干样本分成一组,记录一组的梯度用来代替随机梯度下降中的单个样本

总结:前馈算损失,反馈算梯度,用梯度下降算法更新权重

 l.backward()会把计算图中所有需要梯度(grad)的地方都会求出来,然后把梯度都存在对应的待求的参数中,最终计算图被释放。

前向传播求损失函数

y=w*x+b,假设 x=1,w=1,y=2(实际值),b=0,则

反向传播更新梯度

假设学习率η=0.03,则由上文梯度更新公式可得,权重w=1-0.03*(-2)=1.006,一次迭代结束。可以推测,在后续的梯度下降过程中,w会不断接近准确的权重值2,适当增加学习率可以加快这一过程。

在pytorch中,反向传播的过程用loss.backward()方法完成。该函数可以自动计算反向传播阶段的各个梯度。w的梯度用w.grad.data获取。则梯度更新的代码为:

 自动微分

MindSpore计算一阶导数方法mindspore.ops.GradOperation (get_all=False, get_by_list=False, sens_param=False)其中get_allFalse时,只会对第一个输入求导,为True时,会对所有输入求导;get_by_listFalse时,不会对权重求导,为True时,会对权重求导;sens_param对网络的输出值做缩放以改变最终梯度。

 

对输入求一阶导

定义网络结构:

MatMul算子构成的网络f(x,y)=zxyf(x,y)=zxy

class Net(nn.Cell):

    def __init__(self):

        super(Net, self).__init__()

        self.matmul = ops.MatMul()

        self.z = Parameter(Tensor(np.array([1.0], np.float32)), name='z')

    def construct(self, x, y):

        x = x * self.z

        out = self.matmul(x, y)

        return out

定义求导网络:

__init__函数中定义需要求导的网络self.netops.GradOperation操作,construct函数中对self.net进行求导

可以通过sens_param参数对网络的输出值做缩放以改变最终梯度

 

通用数据处理

数据处理算子

算子说明

shuffle

对数据集进行混洗,随机打乱数据顺序。

map

提供自定义函数或算子,作用于数据集的指定列数据。

batch

对数据集进行分批,可以减少训练轮次,加速训练过程。

repeat

对数据集进行重复,达到扩充数据量的目的。

zip

将两个数据集进行列拼接,合并为一个数据集。

concat

将两个数据集进行行拼接,合并为一个数据集。

 

shuffle

对数据集进行混洗,随机打乱数据顺序。

buffer_size越大,混洗程度越大,但时间、计算资源消耗也会更大。

map

将指定的函数或算子作用于数据集的指定列数据,实现数据映射操作。

batch

将数据集分批,分别输入到训练系统中进行训练,可以减少训练轮次,达到加速训练过程的目的。

repeat

repeatbatch操作的顺序会影响训练batch的数量,建议将repeat置于batch之后。

zip

将两个数据集进行列拼接,合并为一个数据集。

如果两个数据集的列名相同,则不会合并,请注意列的命名。

如果两个数据集的行数不同,合并后的行数将和较小行数保持一致。

concat

将两个数据集进行行拼接,合并为一个数据集。

输入数据集中的列名,列数据类型和列数据的排列应相同。

 

Pytorch完成基础的模型

nn.Modul是pytorch自定义网络的一个基类

  1. __init__需要调用super方法,继承父类的属性和方法
  2. forward方法必须实现,用来定义我们的网络的向前计算的过程

nn.Linear为torch预定义好的线性模型,也被称为全连接层,传入的参数为输入的数量,输出的数量(in_features, out_features),是不算(batch_size的列数)

优化器(optimizer):

  1. torch.optim.SGD(参数,学习率)
  2. torch.optim.Adam(参数,学习率)

   参数:model.parameters()获取

优化器的使用方法:

  1. 实例化
  2. 所有参数的梯度,将其值置为0
  3. 反向传播计算梯度
  4. 更新参数值

示例:

optimizer = optim.SGD(model.parameters(), lr=1e-3) #1. 实例化

optimizer.zero_grad() #2. 梯度置为0

loss.backward() #3. 计算梯度

optimizer.step()  #4. 更新参数的值

损失函数:

  1. 均方误差 : nn.MSELoss(),常用于回归问题
  2. 交叉熵损失 :nn.CrossEntropyLoss(),常用于分类问题

model.eval()表示设置模型为评估模式,即预测模式

model.train(mode=True) 表示设置模型为训练模式

优化算法:

  1. 梯度下降算法
  2. 随机梯度下降法
  3. 小批量梯度下降
  4. 动量法
  5. ADAGRAD
  6. RMSPROP
  7. ADAM

数据加载

数据集的原始地址:http://yann.lecun.com/exdb/mnist/

 

使用Pytorch实现手写数字识别

  1. 准备数据2.构建模型3.模型训练4.模型保存5.模型评估
  • 准备训练集和测试集

1.1torchvision.transforms的图形数据处理方法

1.1.1torchvision.transforms.ToTensor

把一个取值范围是[0,255]PIL.Image或者shape(H,W,C)numpy.ndarray,转换成形状为[C,H,W],其中[C,H,W]为(高,宽,通道数),黑白图片的通道数为1,每个像素点的取值为【0255】,彩色图片通道为(rgb),每个通道的像素点取值为【0,255】。

1.1.2torchvision.transforms.Normalize(mean, std)

给定均值:mean,shape和图片的通道数相同(指的是每个通道的均值),方差:std,和图片的通道数相同

1.1.3torchvision.transforms.Compose(transforms)

将多个transform组合起来使用。

1.2准备MNIST数据集的DatasetDataLoader

二.构建模型

使用全连接层:当前一层的神经元和前一层的神经元相互链接,其核心操作就是y = wx,即矩阵的乘法,实现对前一层的数据的变换

使用三层的神经网络,包括两个全连接层和一个输出层,第一个全连接经过激活函数处理

激活函数使用:常用RELU激活函数

2.2模型中数据的形状:

  1. 原始输入数据为的形状:[batch_size,1,28,28]
  2. 进行形状的修改:[batch_size,28*28] ,(全连接层是在进行矩阵的乘法操作)
  3. 第一个全连接层的输出形状:[batch_size,28],这里的28是个人设定的,你也可以设置为别的
  4. 激活函数不会修改数据的形状
  5. 第二个全连接层的输出形状:[batch_size,10],因为手写数字有10个类别

2.3损失函数:

使用softmax函数

Softmax概率传入对数似然损失得到的损失函数称为交叉熵损失

三.模型的训练

  1. 实例化模型,设置模型为训练模式
  2. 实例化优化器类,实例化损失函数
  3. 获取,遍历dataloader
  4. 梯度置为0
  5. 进行向前计算
  6. 计算损失
  7. 反向传播
  8. 更新参数

 手写数字识别

import torch
import torch.nn as nn
import torch.nn.functional as F
import torch.optim as optim
from torchvision import datasets, transforms
import torchvision
from torch.autograd import Variable
from torch.utils.data import DataLoader
import cv2
# 下载训练集
train_dataset = datasets.MNIST(root='./num/',
                               train=True,
                               transform=transforms.ToTensor(),
                               download=True)
# 下载测试集
test_dataset = datasets.MNIST(root='./num/',
                              train=False,
                              transform=transforms.ToTensor(),
                              download=True)
# dataset 参数用于指定我们载入的数据集名称
# batch_size参数设置了每个包中的图片数据个数
# 在装载的过程会将数据随机打乱顺序并进打包
batch_size = 64
#建立一个数据迭代器
# 装载训练集
train_loader = torch.utils.data.DataLoader(dataset=train_dataset,
                                           batch_size=batch_size,
                                           shuffle=True)
# 装载测试集
test_loader = torch.utils.data.DataLoader(dataset=test_dataset,
                                          batch_size=batch_size,
                                          shuffle=True)
# 实现单张图片可视化
images, labels = next(iter(train_loader))
img = torchvision.utils.make_grid(images)

img = img.numpy().transpose(1, 2, 0)
std = [0.5, 0.5, 0.5]
mean = [0.5, 0.5, 0.5]
img = img * std + mean
print(labels)
# 卷积层使用 torch.nn.Conv2d
# 激活层使用 torch.nn.ReLU
# 池化层使用 torch.nn.MaxPool2d
# 全连接层使用 torch.nn.Linear
class LeNet(nn.Module):
    def __init__(self):
        super(LeNet, self).__init__()
        self.conv1 = nn.Sequential(nn.Conv2d(1, 6, 3, 1, 2), nn.ReLU(),
                                   nn.MaxPool2d(2, 2))

        self.conv2 = nn.Sequential(nn.Conv2d(6, 16, 5), nn.ReLU(),
                                   nn.MaxPool2d(2, 2))

        self.fc1 = nn.Sequential(nn.Linear(16 * 5 * 5, 120),
                                 nn.BatchNorm1d(120), nn.ReLU())

        self.fc2 = nn.Sequential(
            nn.Linear(120, 84),
            nn.BatchNorm1d(84),
            nn.ReLU(),
            nn.Linear(84, 10))
# 最后的结果一定要变为 10,因为数字的选项是 0 ~ 9
    def forward(self, x):
        x = self.conv1(x)
        x = self.conv2(x)
        x = x.view(x.size()[0], -1)
        x = self.fc1(x)
        x = self.fc2(x)
        return x
device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
LR = 0.001
net = LeNet().to(device)
# 损失函数使用交叉熵
criterion = nn.CrossEntropyLoss()
# 优化函数使用 Adam 自适应优化算法
optimizer = optim.Adam(
    net.parameters(),
    lr=LR,
)
epoch = 1
if __name__ == '__main__':
    for epoch in range(epoch):
        sum_loss = 0.0
        for i, data in enumerate(train_loader):
            inputs, labels = data
            inputs, labels = Variable(inputs).cpu(), Variable(labels).cpu()
            optimizer.zero_grad()  #将梯度归零
            outputs = net(inputs)  #将数据传入网络进行前向运算
            loss = criterion(outputs, labels)  #得到损失函数
            loss.backward()  #反向传播
            optimizer.step()  #通过梯度做一步参数更新
            # print(loss)
            sum_loss += loss.item()
            if i % 100 == 99:
                print('[%d,%d] loss:%.03f' %
                      (epoch + 1, i + 1, sum_loss / 100))
                sum_loss = 0.0
net.eval()  #将模型变换为测试模式
correct = 0
total = 0
for data_test in test_loader:
        images, labels = data_test
        images, labels = Variable(images).cpu(), Variable(labels).cpu()
        output_test = net(images)
        _, predicted = torch.max(output_test, 1)
        total += labels.size(0)
        correct += (predicted == labels).sum()
print("correct1: ", correct)
print("Test acc: {0}".format(correct.item() /
                                 len(test_dataset)))

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值