资讯详情

PyTorch学习系列教程:构建一个深度学习模型需要哪几步?

导读

继续PyTorch学习系列。前一篇介绍了。PyTorch中最为基础也最为核心的数据结构——Tensor,有了这些基本概念,我们就可以开始深入的学习和实践了。本文围绕这个话题,介绍了构建深度学习模型的几个基本要素。

与经典的机器学习过程不同,深度学习模型的构建和训练更加灵活和简单,因为没有成熟和直接可用的模型,更多的用户需要设计和组装每个网络模块;简单,因为深度学习通常实现端到端训练,即直接从原始数据集到模型输出,数据预处理、特征工程、特征选择等多阶段征工程、特征选择等多阶段工作流。

b4191a56dc7347dba752254caad5d002.png

建立一个深度学习模型也可以分为三个步骤一个深度学习模型也可以分为三个步骤:

  • 数据集准备

  • 模型定义

  • 模型训练

本文首先介绍了这三个环节,然后给出了一个简单的应用案例。

01 数据集准备

理论上,深度学习中的数据集准备与经典机器学习中的数据集准备没有本质区别。一般来说,样本和标签是基于特定数据构建的过程。这里的样本根据不同的应用场景有不同的样式,如CV典型的领域是图片,NLP典型的领域是一段文本。但无论原始样本如何,它最终都应该转化为数值型Tensor。

当然,将数据集转换为Tensor理论上,它可以用于深度学习模型的输入和训练,但为了更好地支持模型训练和大数据集数batch进行训练,PyTorch提供标准数据集类型(Dataset),我们通常继承这种格式来提供这种格式。这里主要介绍三个常用的数据集相关类别:

  • Dataset:所有自定义数据集的基类

  • TensorDataset:Dataset的一个wrapper,用于快速施工Dataset

  • DataLoader:Dataset的一个wrapper,将Dataset自动分为多个batch

Dataset是PyTorch首先查看提供的数据集基类Dataset签名文件如下:

由此可见,所有自定义的数据集都应该继承并重载这些数据集__getitem__和__len__这两种方法都可以。当然,也需要类初始化的方法__init__设置要加载的数据。典型的自定义Dataset实现如下:

class MyDataset(Dataset):     def __init__(self, x, y):         super().__init__()         ……       def __getitem__(self):         return ……       def __len__(self):         return ……

上述通过Dataset标准自定义数据集的构建可以通过这种方式实现,但如果相对简单的数据集仍然需要八股文的重载__getitem__和__len__两种方法,难免有些复杂和俗套。而TensorDataset这是对上述需求的简化,即只需要特定的tensor包裹为一个Dataset当类型用类型作为自定义数据集TensorDataset即可。这里还是先给出签名文件:

具体应用只需要几个tensor输入格式作为参数输入TensorDataset,然后返回结果是标准Dataset类型数据集。标准使用如下:

my_dataset = TensorDataset(tenso_x, tensor_y)

深度学习通常适用于大数据集场景。训练一个成熟的深度学习模型通常需要足够的数据。因此,在深度学习和训练的过程中,所有的训练集数据一般不会一次喂给模型,而是小批量的训练,每批称为一批batch,一个完整的训练集被称为一个训练epoch。实现小批量多批次的方式有很多,比如完全可以通过随机取一个索引分片的方式来实现这一工作,但更为标准和优雅的方式则是使用Dataloader。签名文件如下:

可见,DataLoader一般来说,它可以等同于一个Dataset实现随机采样(sampler),然后为指定的数据集提供可迭代的类型。因此,它的使用相对简单:直接使用一个Dataset作为参数传入的类型数据集DataLoader可以。简单使用样品如下:

dataloader=DataLoader(MyDataset, batch_size=128, shuffle=True)

以上是应用PyTorch构建数据集中常用的三种操作基本上可以满足日常使用的大部分需求,并将结合实际情况进行完整的演示。

02 定义网络架构

深度学习和经典机器学习最大的区别之一是模型结构。经典机器学习模型通常具有固定的范式和结构。例如,随机森林由指定数量的决策树组成,尽管在这里n_estimators可选择,但总体而言,随机森林模型的结构是确定的;深度学习模型的基础在于神经网络,即由多个神经网络层组成,每层使用的神经网络模块类型可以不同(全连接层、卷积层等),神经元数量的差异也会带来很大的差异。因此,深度学习为用户提供了更大的设计创新空间。

当然,网络架构(Architecture)设计不需要从零开始,PyTorch这些深度学习框架的一大功能就是提供了基础的神经网络模块(Module),用户只需要根据自己的设计意图灵活组装——就像积木一样!PyTorch所有网络模块都位于中间torch.nn模块下(nn=nueral network),包括以下模块:

这些模块数量庞大,功能各异,是深度学习模型的核心。但就其功能而言,一般分为以下几类:

  • 例如,模型功能类Linear、Conv2d,RNN全连接层、卷积层、循环神经网络层分别对应,

  • 激活函数:例如Sigmoid,Tanh,ReLU等,

  • 损失函数:CrossEntropyLoss,MSELoss等等,前者是常用的损失函数,后者是常用的损失函数

  • 规范化:LayerNorm等,

  • 防止过拟合:Dropout等

  • 其他

在某种程度上,深度学习的主体是理解和掌握这些基本网络模块的功能和使用方法。在此基础上,网络架构可以根据自己对数据和场景的理解来定义,从而达到预期的模型效果。

这部分太大了,一两条推文解释不清楚。我认为我目前还不够完全理解,所以当徐徐图之和每个人都打破这些模块的学习和介绍时。

在这些单个网络模块的基础上,完整的网络模型需要继承PyTorch中的Module这个过程类似于继承Dataset这里仍然给出类实现自定义数据集)Module签名文件:

由此可见,所有自定义的网络模型都需要继承Module类,一般需要重写forward(用于实现神经网络函数(用于实现神经网络函数)的前向传播过程),而后模型即完成了注册,并拥有了相应的可训练参数等。

03 模型训练

仍然与经典机器学习模型的训练不同,深度学习模型由于其网络架构一般是自定义设计的,所以一般也不能简单的通过调用fit/predict的方式来实现简洁的模型训练/预测过程,而往往交由使用者自己去实现。

大体上,实现模型训练主要包含以下要素:

  • 完成数据集的准备和模型定义

  • 指定一个损失函数,用于评估当前模型在指定数据集上的表现

  • 指定一个优化器,用于"指导"模型朝着预期方向前进

  • 写一个循环调度,实现模型训练的迭代和进化

数据集的准备和模型定义部分就是前两小节所述内容;而损失函数,简单需求可以依据PyTorch提供的常用损失函数,而更为复杂和个性化的损失函数则继承Module类的方式来加以自定义实现;优化器部分则无太多“花样”可言,一般直接调用内置的优化器即可,例如Adam、SGD等等。

这些操作结合后续的实践案例一并介绍。

04 一个简单的深度学习案例

麻雀虽小五脏俱全,解剖一只麻雀,可有助于探悟内涵实质和基本规律。

有了前述小节的理论基础,就可以开始深度学习实践案例了,这里以sklearn中自带的手写数字分类作为目标来加以实践。

1.首先给出方式和效果

from sklearn.datasets import load_digits
from sklearn.model_selection import train_test_split
from sklearn.ensemble import RandomForestClassifier
X, y = load_digits(return_X_y=True)
X_train, X_test, y_train, y_test = train_test_split(X, y)
rf = RandomForestClassifier().fit(X_train, y_train)
rf.score(X_test, y_test)
### 输出:0.9688888888888889

当然,该数据集分类的难度不大,即使在未经过调参的情况下也取得了很好的分类效果。

,这里按照标准的深度学习训练流程,仍然使用上述手写数字分类数据集进行实验:

import torch
from torch.utils.data import TensorDataset, DataLoader


X_train_tensor = torch.Tensor(X_train)
y_train_tensor = torch.Tensor(y_train).long()  # 主要标签需要用整数形式,否则后续用于计算交叉熵损失时报错
dataset = TensorDataset(X_train_tensor, y_train_tensor)  # 直接调用TensorDataset加以包裹使用
dataloader = DataLoader(dataset, batch_size=128, shuffle=True)  # 每128个样本为一个batch,训练时设为随机


X_test_tensor = torch.Tensor(X_test)  # 测试集只需转化为tensor即可
y_test_tensor = torch.Tensor(y_test).long()

from torch import nn, optim
class Model(nn.Module):  # 继承Module基类
    def __init__(self, n_input=64, n_hidden=32, n_ouput=10):
        # 定义一个含有单隐藏层的全连接网络,其中输入64为手写数字数据集的特征数,输出10为类别数,隐藏层神经元数量设置32
        super().__init__()
        # 使用全连接层和ReLU激活函数搭建网络模型
        self.dnn = nn.Sequential(
            nn.Linear(n_input, n_hidden),
            nn.ReLU(),
            nn.Linear(n_hidden, n_output)
        )
    
    def forward(self, x):
        # 重载forward函数,从输入到输出
        return self.dnn(x)

model = Model()  # 初始化模型
creterion = nn.CrossEntropyLoss()  # 选用交叉熵损失函数
optimizer = optim.Adam(model.parameters(), lr=0.001)  # 选用Adam优化器,传入模型参数,设置学习率
for epoch in range(50):  # 50个epoch
    for data, label in dataloader:  # DataLoader是一个可迭代对象
        optimizer.zero_grad()  # 待优化参数梯度清空
        prob = model(data)  # 执行一次前向传播,计算预测结果
        loss = creterion(prob, label)  # 评估模型损失
        loss.backward()  # 损失反向传播,完成对待优化参数的梯度求解
        optimizer.step()  # 参数更新
    if (epoch + 1) % 5 == 0:  # 每隔5个epoch打印当前模型训练效果
        with torch.no_grad():
            train_prob = model(X_train_tensor)
            train_pred = train_prob.argmax(dim=1)
            acc_train = (train_pred==y_train_tensor).float().mean()
            test_prob = model(X_test_tensor)
            test_pred = test_prob.argmax(dim=1)
            acc_test = (test_pred==y_test_tensor).float().mean()
            print(f"epoch: {epoch}, train_accuracy: {acc_train}, test_accuracy: {acc_test} !")
### 输出
epoch: 4, train_accuracy: 0.8507795333862305, test_accuracy: 0.8577777743339539 !
epoch: 9, train_accuracy: 0.948775053024292, test_accuracy: 0.9200000166893005 !
epoch: 14, train_accuracy: 0.9717891812324524, test_accuracy: 0.9444444179534912 !
epoch: 19, train_accuracy: 0.9799554347991943, test_accuracy: 0.9577777981758118 !
epoch: 24, train_accuracy: 0.9866369962692261, test_accuracy: 0.9644444584846497 !
epoch: 29, train_accuracy: 0.9925761222839355, test_accuracy: 0.9644444584846497 !
epoch: 34, train_accuracy: 0.9925761222839355, test_accuracy: 0.9644444584846497 !
epoch: 39, train_accuracy: 0.9962880611419678, test_accuracy: 0.9666666388511658 !
epoch: 44, train_accuracy: 0.9970304369926453, test_accuracy: 0.9711111187934875 !
epoch: 49, train_accuracy: 0.9970304369926453, test_accuracy: 0.9711111187934875 !

至此,就完成了一个深度学习模型训练的基本流程,从数据集准备到模型定义,直至最后的模型训练及输出。当然,由于该数据集分类任务比较简单,加之数据量不大,所以深度学习的优势并不明显。

相关阅读:

  • 写在1024:一名数据分析师的修炼之路

  • 数据科学系列:sklearn库主要模块简介

  • 数据科学系列:seaborn入门详细教程

  • 数据科学系列:pandas入门详细教程

  • 数据科学系列:matplotlib入门详细教程

  • 数据科学系列:numpy入门详细教程

标签: 连接器fit系列

锐单商城拥有海量元器件数据手册IC替代型号,打造 电子元器件IC百科大全!

锐单商城 - 一站式电子元器件采购平台