导读:继续PyTorch学习系列。前篇介绍了PyTorch中最为基础也最为核心的数据结构——Tensor,有了这些基本概念即可开始深度学习实践了。本篇围绕这一话题,本着提纲挈领删繁就简的原则,从宏观上介绍搭建深度学习模型的几个基本要素。
不同于经典的机器学习流程,深度学习模型的搭建和训练更为灵活和简单,称之为灵活是因为一般没有成熟和直接可用的模型,而更多需要使用者自己去设计和组装各个网络模块;称之为简单是因为深度学习往往实现端到端的训练,即直接从原始数据集到模型输出,而无需经典机器学习中的数据预处理、特征工程、特征选择等多阶段式的工作流。
类似于把大象装进冰箱需要3步一样,构建一个深度学习模型也可以将其分为三步:
- 数据集准备
- 模型定义
- 模型训练
本文就首先围绕这三个环节加以介绍,然后给出一个简单的应用案例。
01 数据集准备
理论上,深度学习中的数据集准备与经典机器学习中的数据集准备并无本质性差别,大体都是基于特定的数据构建样本和标签的过程,其中这里的样本依据应用场景的不同而有不同的样式,比如CV领域中典型的就是图片,而NLP领域中典型的就是一段段的文本。但无论原始样本如何,最终都要将其转化为数值型的Tensor。
当然,将数据集转化为Tensor之后理论上即可用于深度学习模型的输入和训练,但为了更好的支持模型训练以及大数据集下的分batch进行训练,PyTorch中提供了标准的数据集类型(Dataset),而我们则一般是要继承此类来提供这一格式。这里主要介绍3个常用的数据集相关的类:
- Dataset:所有自定义数据集的基类
- TensorDataset:Dataset的一个wrapper,用于快速构建Dataset
- DataLoader:Dataset的一个wrapper,将Dataset自动划分为多个batch
1.Dataset
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 ……
2.TensorDataset
上述通过Dataset的方式可以实现一个标准自定义数据集的构建,但如果对于比较简单的数据集仍需八股文似的重载__getitem__和__len__两个方法,则难免有些繁杂和俗套。而TensorDataset就是对上述需求的一个简化,即当仅需将特定的tensor包裹为一个Dataset类型作为自定义数据集时,那么直接使用TensorDataset即可。这里仍然先给出其签名文档:
具体应用时,只需将若干个tensor格式的输入作为参数传入TensorDataset,而后返回结果即是一个标准的Dataset类型数据集。标准使用方式如下:
my_dataset = TensorDataset(tenso_x, tensor_y)
3.DataLoader
深度学习往往适用于大数据集场景,训练一个成熟的深度学习模型一般也需要足够体量的数据。所以,在深度学习训练过程中一般不会每次都将所有训练集数据一次性的喂给模型,而是小批量分批次的训练,其中每个批量叫做一个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.首先给出应用sklearn中随机森林模型的实现方式和效果
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
当然,该数据集分类的难度不大,即使在未经过调参的情况下也取得了很好的分类效果。
2.基于PyTorch的深度学习模型训练实践,这里按照标准的深度学习训练流程,仍然使用上述手写数字分类数据集进行实验:
a.构建Dataset类型数据集
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()
b.自定义一个网络模型,仅使用Linear网络层
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)
c.八股文式的深度学习训练流程
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 !
至此,就完成了一个深度学习模型训练的基本流程,从数据集准备到模型定义,直至最后的模型训练及输出。当然,由于该数据集分类任务比较简单,加之数据量不大,所以深度学习的优势并不明显。
如何学习大模型 AI ?
由于新岗位的生产效率,要优于被取代岗位的生产效率,所以实际上整个社会的生产效率是提升的。
但是具体到个人,只能说是:
“最先掌握AI的人,将会比较晚掌握AI的人有竞争优势”。
这句话,放在计算机、互联网、移动互联网的开局时期,都是一样的道理。
我在一线互联网企业工作十余年里,指导过不少同行后辈。帮助很多人得到了学习和成长。
我意识到有很多经验和知识值得分享给大家,也可以通过我们的能力和经验解答大家在人工智能学习中的很多困惑,所以在工作繁忙的情况下还是坚持各种整理和分享。但苦于知识传播途径有限,很多互联网行业朋友无法获得正确的资料得到学习提升,故此将并将重要的AI大模型资料包括AI大模型入门学习思维导图、精品AI大模型学习书籍手册、视频教程、实战学习等录播视频免费分享出来。
😝有需要的小伙伴,可以点击下方链接免费领取或者V扫描下方二维码免费领取🆓
第一阶段(10天):初阶应用
该阶段让大家对大模型 AI有一个最前沿的认识,对大模型 AI 的理解超过 95% 的人,可以在相关讨论时发表高级、不跟风、又接地气的见解,别人只会和 AI 聊天,而你能调教 AI,并能用代码将大模型和业务衔接。
- 大模型 AI 能干什么?
- 大模型是怎样获得「智能」的?
- 用好 AI 的核心心法
- 大模型应用业务架构
- 大模型应用技术架构
- 代码示例:向 GPT-3.5 灌入新知识
- 提示工程的意义和核心思想
- Prompt 典型构成
- 指令调优方法论
- 思维链和思维树
- Prompt 攻击和防范
- …
第二阶段(30天):高阶应用
该阶段我们正式进入大模型 AI 进阶实战学习,学会构造私有知识库,扩展 AI 的能力。快速开发一个完整的基于 agent 对话机器人。掌握功能最强的大模型开发框架,抓住最新的技术进展,适合 Python 和 JavaScript 程序员。
- 为什么要做 RAG
- 搭建一个简单的 ChatPDF
- 检索的基础概念
- 什么是向量表示(Embeddings)
- 向量数据库与向量检索
- 基于向量检索的 RAG
- 搭建 RAG 系统的扩展知识
- 混合检索与 RAG-Fusion 简介
- 向量模型本地部署
- …
第三阶段(30天):模型训练
恭喜你,如果学到这里,你基本可以找到一份大模型 AI相关的工作,自己也能训练 GPT 了!通过微调,训练自己的垂直大模型,能独立训练开源多模态大模型,掌握更多技术方案。
到此为止,大概2个月的时间。你已经成为了一名“AI小子”。那么你还想往下探索吗?
- 为什么要做 RAG
- 什么是模型
- 什么是模型训练
- 求解器 & 损失函数简介
- 小实验2:手写一个简单的神经网络并训练它
- 什么是训练/预训练/微调/轻量化微调
- Transformer结构简介
- 轻量化微调
- 实验数据集的构建
- …
第四阶段(20天):商业闭环
对全球大模型从性能、吞吐量、成本等方面有一定的认知,可以在云端和本地等多种环境下部署大模型,找到适合自己的项目/创业方向,做一名被 AI 武装的产品经理。
- 硬件选型
- 带你了解全球大模型
- 使用国产大模型服务
- 搭建 OpenAI 代理
- 热身:基于阿里云 PAI 部署 Stable Diffusion
- 在本地计算机运行大模型
- 大模型的私有化部署
- 基于 vLLM 部署大模型
- 案例:如何优雅地在阿里云私有部署开源大模型
- 部署一套开源 LLM 项目
- 内容安全
- 互联网信息服务算法备案
- …
学习是一个过程,只要学习就会有挑战。天道酬勤,你越努力,就会成为越优秀的自己。
如果你能在15天内完成所有的任务,那你堪称天才。然而,如果你能完成 60-70% 的内容,你就已经开始具备成为一名大模型 AI 的正确特征了。
这份完整版的大模型 AI 学习资料已经上传CSDN,朋友们如果需要可以微信扫描下方CSDN官方认证二维码免费领取【保证100%免费
】
😝有需要的小伙伴,可以Vx扫描下方二维码免费领取==🆓