【PyTorch】刘二大人的Pytorch深度学习实践学习笔记

【PyTorch】刘二大人的Pytorch深度学习实践学习笔记

写在前面

博主在去年暑假已经看过一次刘二大人的《Pytorch深度学习实践》,但是没有做任何笔记。现在突发奇想打算再过一次视频来夯实一下代码基础,顺便做下笔记来供自己以后复习使用。

01. Overview

之前基于rule的系统是通过人工设定规则实现,到了传统的机器学习方法则是通过手动设计特征(如把一段语音变成向量),最后通过一个mapping函数对应到输出。

表示学习则希望feature的提取也是通过学习的方法来得到(而不是手工获取),但是其中feature的提取和mapping映射是分开完成的。

深度学习是一种端到端的(end2end)的算法,之前的表示学习可能需要一个设计特征提取器来获取特征,而深度学习直接设计一个很大的模型完成数据从输入到输出的整体过程。

image-20240704234208966

SVM的劣势。

image-20240704235252942

02. 线性模型

四步:数据集,设计模型,训练模型,推理(预测)

image-20240705144804133

训练集train set:训练模型的数据集;

测试集test set:预测步骤,测试模型的准确性。

训练集可能会存在过拟合问题,比如学习到图形的噪声,我们要求模型有好的泛化能力。比如训练集的猫狗图像可能是艺术照片(美颜加猫狗在中间),而实际上可能用户上传后照片是随手一拍。由此我们将训练集分为两块,将另外一块用于评估,称之为验证集valid set。

image-20240705145501822

评估模型,称之为损失(loss)

image-20240705150257904

loss function:对于一个样本的损失

cost function:对于所有样本(整个训练集)的平均损失。如图所示是MSE损失。

image-20240705150546025

03. 梯度下降算法

观察法:维度过高时候过于困难,程序复杂度和数据量过高;

分治法:不是凸函数的情况下容易不容易得到最优值。而且维度过高也难以划分。

image-20240705162930689

由此提出梯度下降算法。贪心算法,容易陷入局部最优点。但是在深度网络里面,人们发现局部最优点比较少,所以可以使用。

image-20240705163254405

image-20240705163430477

但是深度学习里面鞍点比较多,(比如马鞍面),也是需要解决的最大的问题。

image-20240705163607203

随机梯度下降:用一个数据(或者单个样本)代替原来所有的数据计算损失。这样有助于跳过鞍点。

image-20240705164512015

但是随机梯度下降无法实现并行,因为权值w有依赖关系。

image-20240705164840883

在深度学习中选择折中,因为梯度下降效率高但性能差,随机梯度下降效率低但性能好。所以采用批次的随机梯度下降法。

image-20240705165016259

04. 反向传播

理论

在之前讲解过程中我们很容易求得梯度的解析式,如下所示。

image-20240705165333496

但是在深度学习中再求取解析式的话,过于复杂,而且嵌套了很多复合函数,例子如下。由此设计了反向传播算法,利用计算图通过链式法则来得到梯度。

image-20240705165509311

关于激活函数的作用,主要是得到非线性的变化。

image-20240705165939805

链式法则的步骤:

  1. 前馈获取loss。

image-20240705170218891

  1. 局部梯度。这儿的f是输入x和权重w的函数。

image-20240705170333420

  1. 获取loss对于z的偏导。 (从前面的传播过程获得)

image-20240705170526340

  1. 反向传播,从而获得L对x、w的导数,其中z对x、对w的导数,是在计算f的函数的时候算出的。

image-20240705170757293

举例说明:

正向得到loss为1后,通过正向计算过程中算得的梯度,反向传播获取最终的偏导。

这儿的loss一般会保存,用来评估模型。

image-20240705172356524

代码

image-20240705173044512

  1. forward函数里面的乘法被重载了。
  2. w需要计算梯度,后面的也需要计算梯度(图中蓝色方框)。
  3. 调用一次loss,动态产生计算图。

image-20240705173154375

#训练代码
for epoch in range(100):
    for x,y in zip(x_data,y_data):
        l = loss(x,y) #前向传播,得到loss,构造计算图。同时将梯度存到w中
        l.backward()#反向传播,释放计算图(每次反向传播释放计算图可以方便drop out)
        
        print("grad:",x,y,w.grad.item())#w.grad.item()原理同w.grad.data
        w.data = w.data - 0.01 * w.grad.data#w.grad是一个tensor,如果直接用它是在构造计算图,因此需要用.data,这样可以不需要构造计算图
        w.grad.data.zero_()#梯度清零,因为梯度在反向传播中是累加的,需要手动清零

05. pytorch实现线性回归

深度学习四步。

image-20240705204742313

广播机制,如下所示,这里的乘是矩阵元素对应位置相乘,不是矩阵乘。

image-20240705205346087

在nn.Linear中,包括了权重w和偏置b。它也是继承自Module,所以可以自动反向传播求导。image-20240709152654338

对应完整代码如下所示:

import numpy as np
import torch

x_data = torch.Tensor([[1.0],[2.0],[3.0]])
y_data = torch.Tensor([[2.0],[4.0],[6.0]])

#nn.Module是所有神经网络的父类
class LinearModel(torch.nn.Module):
#至少需要两个函数:构造函数和forward函数。前者用来初始化,后者用来计算前缀forward过程
#如果觉得pytorch中封装的反向传播算法效率不高,可以自己写function类
    def __init__(self):
        super(LinearModel,self).__init__()#调用父类的构造函数
        self.linear = torch.nn.Linear(1,1)#linear是一个nn.Linear的对象

    def forward(self,x):
        y_pred = self.linear(x)#可调用的对象(__call__)
        return y_pred

def train(model):
    criterion = torch.nn.MSELoss(size_average=False)  # MSEloss继承自nn.Module
    optimizer = torch.optim.SGD(model.parameters(), lr=0.05)  # 不会构建计算图,model.parameters()记录了需要更新的参数
    for epoch in range(100):
        #step 1:得到y_hat
        y_pred = model(x_data)
        #step 2: 计算loss
        loss = criterion(y_pred, y_data)
        print(epoch, loss.item())
        #step 3:求取梯度
        optimizer.zero_grad()#梯度清零
        loss.backward()#反向传播
        #step 4: 权重更新
        optimizer.step()#更新

    print('w= ',model.linear.weight.item())
    print('b= ',model.linear.bias.item())

    x_test = torch.Tensor([[4.0]])
    y_test = model(x_test)
    print('y_pred = ',y_test.data)

if __name__ == '__main__':
    model = LinearModel()
    train(model)

06-08. Logistic Regression

sigmoid函数

下面都是sigmoid函数,但是由于logistic function: 1 1 + e − x \frac{1}{1+e^{-x}} 1+ex1比较出名,因此后面sigmoid函数便就是logistic function 1 1 + e − x \frac{1}{1+e^{-x}} 1+ex1

image-20240709225435774

交叉熵损失:输出的是分布,而不是实数距离。

image-20240709235644778

8个feature为例,如下。

image-20240710001351203

mini-batch实例

epoch:所有样本经过一次前馈和反向传播,便为一个epoch。

batch_size:进行一次前馈和反向传播的样本数量。

iteration:每一次epoch执行了多少个batch_size。

Dataset需要满足:1.下标访问; 2.数据集长度。

由此dataloader可以对dataset进行shuffle,然后批次输入并可以利用for循环。image-20240710140252932

详细代码如下。

import numpy as np
import torch
import torch.nn as nn
import torch.nn.functional as F
from torch.utils.data import Dataset#Dataset是一个抽象类
from torch.utils.data import DataLoader#帮助进行shuffle等操作


class MyDataset(Dataset):
    def __init__(self,file_path):
        xy = np.loadtxt(file_path,delimiter=',',dtype=np.float32)
        self.len = xy.shape[0]#(N,9)
        self.x_data = torch.from_numpy(xy[:,:-1])#取前八列
        self.y_data = torch.from_numpy(xy[:,[-1]])#取最后一列,并取矩阵形式

    def __getitem__(self, index):
        return self.x_data[index],self.y_data[index]

    def __len__(self):#len()
        return self.len


class LogisticRegressionModel(torch.nn.Module):
    def __init__(self):
        super(LogisticRegressionModel, self).__init__()
        self.FFN = nn.Sequential(
            nn.Linear(8,4),
            nn.Sigmoid(),
            nn.Linear(4,2),
            nn.Sigmoid(),
            nn.Linear(2,1),
            nn.Sigmoid()
        )

    def forward(self, x):
        return self.FFN(x)


def train(model):
    criterion = torch.nn.BCELoss(reduction='mean')
    optimizer = torch.optim.SGD(model.parameters(), lr=0.05)
    for epoch in range(100):
        for i,data in enumerate(train_loader,0):
            #prepare data
            inputs,labels = data
            #forward
            y_pred=model(inputs)
            loss = criterion(y_pred,labels)
            print(epoch,i,loss.item())
            #backward
            optimizer.zero_grad()
            loss.backward()
            #update
            optimizer.step()


if __name__ == '__main__':
    #load data
    dataset = MyDataset('data/diabetes/diabetes.csv.gz')
    train_loader = DataLoader(dataset=dataset,batch_size=32,shuffle=True,num_workers=2)
    #model
    model = LogisticRegressionModel()
    train(model)

作业

后续再补。。

09. 多分类问题

softmax函数

softmax实现多分类:保证概率和为1,每类的概率大于等于0。

image-20240710151700613

举例如下。

image-20240710151801493

交叉熵损失函数,如下所示。NLLLoss需要要求先对Y_hat求对数。

image-20240710152049586

为方便,利用CrossEntrpyLoss,将Softmax、求对数过程、NLLLoss全部封装在其中。

这时候要求y(label)是LongTensor类型。

image-20240710152302049

代码对应举例如下所示:

image-20240710152557831

MNIST数据集

详细代码如下。

import torch
import torch.nn as nn
from torchvision import datasets, transforms
from torch.utils.data import DataLoader

def load_data():
    transform = transforms.Compose([
        transforms.ToTensor(),  # 把0-255转为0-1的张量,(28,28)-》(1,28,28),即 channel * w * h
        transforms.Normalize((0.1307,), (0.3081,))  # 均值 mean 和标准差 std
    ])
    train_set = datasets.MNIST(root='./data/mnist', train=True, download=True, transform=transform)
    test_set = datasets.MNIST(root='./data/mnist', train=False, download=True, transform=transform)
    return train_set, test_set

class Mymodel(nn.Module):
    def __init__(self):
        super(Mymodel, self).__init__()
        self.FFN = nn.Sequential(
            nn.Linear(28*28, 512), nn.LeakyReLU(),
            nn.Linear(512, 256), nn.LeakyReLU(),
            nn.Linear(256, 128), nn.LeakyReLU(),
            nn.Linear(128, 64), nn.LeakyReLU(),
            nn.Linear(64, 10)
        )

    def forward(self, x):
        x = x.view(-1, 784)  # -1 表示自动计算第一个维度,784 是一张图片大小为 28 * 28
        return self.FFN(x)

def train_valid(model, train_loader, test_loader):
    criterion = nn.CrossEntropyLoss()
    optimizer = torch.optim.SGD(model.parameters(), lr=0.01, momentum=0.5)

    for epoch in range(10):
        model.train()  # 确保模型处于训练模式
        running_loss = 0.0
        for batch_idx, data in enumerate(train_loader):
            inputs, targets = data
            optimizer.zero_grad()

            outputs = model(inputs)
            loss = criterion(outputs, targets)
            loss.backward()
            optimizer.step()

            running_loss += loss.item()
            if batch_idx % 300 == 299:
                print("[{:d}, {:5d}] loss: {:.3f}".format(epoch + 1, batch_idx + 1, running_loss / 300))
                running_loss = 0.0
        vaild(model, test_loader)

def vaild(model, test_loader):
    correct = 0
    total = 0
    model.eval()  # 确保模型处于评估模式
    with torch.no_grad():
        for data in test_loader:
            images, labels = data
            outputs = model(images)  # (batch_size, 10)
            _, predicted = torch.max(outputs.data, dim=1)  # 返回最大值和最大值的下标
            total += labels.size(0)  # (batch_size, 1)
            correct += (predicted == labels).sum().item()
    print('Accuracy on test set: %d %%' % (100 * correct / total))

if __name__ == '__main__':
    batch_size = 64
    train_set, test_set = load_data()

    train_loader = DataLoader(train_set, shuffle=True, batch_size=batch_size)
    test_loader = DataLoader(test_set, shuffle=False, batch_size=batch_size)
    model = Mymodel()

    train_valid(model, train_loader, test_loader)

PS:这儿出现了一个问题,原函数名叫test,让pycharm误以为是测试用例,修改为valid后正常。

image-20240710161521274

输出结果如下。

[1,   300] loss: 2.234
[1,   600] loss: 1.042
[1,   900] loss: 0.445
Accuracy on test set: 89 %
[2,   300] loss: 0.327
[2,   600] loss: 0.284
[2,   900] loss: 0.234
Accuracy on test set: 93 %
[3,   300] loss: 0.197
[3,   600] loss: 0.180
[3,   900] loss: 0.164
Accuracy on test set: 95 %
[4,   300] loss: 0.134
[4,   600] loss: 0.133
[4,   900] loss: 0.126
Accuracy on test set: 96 %
[5,   300] loss: 0.107
[5,   600] loss: 0.098
[5,   900] loss: 0.099
Accuracy on test set: 96 %
[6,   300] loss: 0.079
[6,   600] loss: 0.080
[6,   900] loss: 0.081
Accuracy on test set: 97 %
[7,   300] loss: 0.065
[7,   600] loss: 0.066
[7,   900] loss: 0.065
Accuracy on test set: 97 %
[8,   300] loss: 0.050
[8,   600
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值