内容简介:原创 · 作者 | Giant学校 | 浙江大学
原创 · 作者 | Giant
学校 | 浙江大学
研究方向 | 对话系统、text2sql
熟悉DL的朋友应该知道Tensorflow、Pytorch、Caffe这些成熟的框架,它们让广大AI爱好者站在巨人的肩膀上,避免了重复造轮子的工作。当你有一个好的想法,这些 工具 可以帮助你快速复现,将idea变现成代码、模型。
此前,我一直在用Tensorflow及其高级API-Keras框架,后者简洁明了的API风格能让一个复杂的模型简化到10行代码。最近,因项目需要接触了基于动态图的pyTorch框架,再一次验证了真香定律。类似 python 的语法,让开发者搭建一个深度学习模型就像写一个python函数那样简单。
类似其他框架,pytorch已经封装好了AlexNet、VGG等模型结构,但一个优秀的算法工程师肯定不满足于调用别人封装好的API;往往,开发者是评估了需求后实现自定义模型。
经过一些实际项目的锻炼,我大致总结了使用深度学习解决一个实际问题的步骤:
(1) 分析问题
这是什么问题,分类还是回归?传统ML方法能否解决?端到端还是多个子任务?等等
(2)准备数据
包含数据分析、清洗、归一、划分等步骤,通常会将数据封装成迭代器形式方便模型调用,节约资源
(3)模型设计
根据任务设计相应模型,包括损失函数、优化器的选择等
(4) 结果分析
观察模型训练结果,通过压力测试、badcase分析等决定模型是否work;可能会多次进行1-3步的迭代优化
对于复杂任务如文本生成、text2sql,需要对模型输出结果先进行解码、还原
(5)封装交付
随着业务迭代,可能会对模型多次训练、微调,或结构变动。
由于这是一篇guide兼踩坑指南,本文主要针对自己碰到过的问题进行总结,欢迎读者朋友们将自己遇到过的问题(附上解决方法就更好啦)留言,共同避坑。
准备篇
1.用好官方文档
对于pytorch还陌生的朋友,入门的好方法之一是直接看官方文档。从类、函数到具体对象,都有详尽清晰的介绍,同时提供了诸多示例。这一点个人认为比tf要略胜一筹。
官网首页就是安装方法,根据python版本、安装包、OS的差异提供了不同路径,可谓考虑非常周到了。
在国内,有时因为网速原因通过官网下载会非常慢,可以找一些镜像资源快速安装。
# 豆瓣镜像快速安装 1.1.0 版本pytorch pip install torch==1.1.0 -i https://pypi.douban.com/simple
对于开发人员最有用的应该是 Docs 页面,提供了pytorch各个模块的解释和示例,还有源码链接。相信你的问题80%都能在这儿解决。
最后,介绍一本pytorch官方推荐的入门书籍: 《Deep-Learning-with-PyTorch》 ,主要面向有python基础的同学,介绍如何从0用pytorch搭建一个深度学习项目(软件/硬件)。
2.学好 numpy
pytorch的基本数据类型 能和numpy对象“无缝切换”。很多关于张量的操作也和numpy的方法基本一致,所以想学好pytorch,可以先复习numpy。掌握了基本的矩阵操作,学习pytorch就不难啦。
(观察当前张量的 shape 变化,可以帮助你更好地了解数据的变换过程和debug)
3.本文测试环境
本文的实验环境为:
pytorch-1.1.0 python-3.6
下面让我们愉快的正式开始。
数据篇
“数据决定了最终结果的上界,好的模型帮助你不断逼近这个上界"。
2014年深度学习重新绽放活力以来,基于神经网络的模型不断刷新着各个领域的任务排行榜,某些任务甚至超越了人类表现。这背后是两大重要能力的支撑:强大的计算能力和庞大的数据。
pytorch工具包中提供了很多和数据准备相关的工具,比如最常用的有这两个:
from torch.utils.data import DataLoader, Dataset
Dataset 是一个数据包装抽象类,我们往往希望加载自定义的数据,只需要继承该类,重写“__ getitem__ ”和“__ len_ _”两个方法即可。
例如,我想在类的初始化函数中对传入的文本分词,可能写成这样:
class MyData(Dataset): def __init__(self, texts, labels, is_train=True): self.texts = [jieba.lcut(t) for t in texts] self.labels = labels # 其他操作 .... def __getitem__(self, item): token_id = convert_tokens_to_ids(self.texts[item]) # 词 -> token_id label = self.labels[item] return torch.LongTensor(token_id), torch.LongTensor([label]) def __len__(self): return len(self.texts)
然后我希望将数据封装成迭代器,每次访问数据时可以返回一个指定batch大小的批数据,不需要一次性把所有数据都load到内存以减少占用:
def get_dataloader(dataset, batch_size, shuffle=False, drop_last=False): data_iter = DataLoader( dataset=dataset, batch_size=batch_size, shuffle=shuffle, drop_last=drop_last ) return data_iter dataset = MyData(texts, labels) dataloader = get_dataloader(dataset, batch_size=16) # 成功封装成迭代器
这样在进行训练或测试时,可以很方便的按batch调用数据。
def train(): model.cuda() model.train() for epoch in range(10): for batch in dataloader: # 传入一个 batch 的数据 model(batch, "train") pass
是不是很简单呢!
接下来是一些碰到过的坑:
1.GPU / CPU 张量转换
模块对tensor在CPU、GPU之前的切换提供了很好的支持。如果你的深度学习模型是在GPU环境下运行的(model.cuda()),则需要将数据转换到GPU上再喂入模型;CPU上的数据和GPU数据直接计算时会抛错。
2.数据填充对齐-pad
一般来说NLP模型的输入是词ID矩阵,形状为 [batch_size, seq_len]。原始文本长度seq_len很可能是参差不齐的,但是神经网络的输入需要一个规整的张量,所以需要通过裁剪(丢失信息较多)或填充的方式使得它们变成定长。
以下代码是针对一个list进行填充(好像用什么框架都需要这一步╮(╯▽╰)╭ ;填充值一般习惯性选“0”)
def pad(s_list, pad_value=0): '''s_list = [[1,2,3,1,0],[1,2,3,]]''' max_len = max(len(i) for i in s_list) s_list = [s + [0] * (max_len - len(s)) if len(s) < max_len else s[:max_len] for s in s_list] return s_list
模型篇
1.模型自定义
pytorch提供了和Keras类似的序列化方式来定义模型,一个简单的CNN网络可以写成:
import torch.nn as nn model = nn.Sequential( nn.Conv2d(1,20,5) nn.ReLU() )
但是实际开发中,这样写基本没什么意义,我们需要的是根据具体任务定义自己的模型。这在pytorch中也是很容易的一件事。分2步: 继承Moulde类,重写init、forward函数
import torch.nn.functional as F import torch.nn as nn optimizer = Adam(lr=2e-5) # 优化器 class MyModel(nn.Module): def __init__(self, ): super(MyModel, self).__init__() self.bert = BertModel.from_pretrained('/chinese_bert_pytorch/', cache_dir=None) self.s_linear = torch.nn.Linear(768, 1) def forward(self, batch, task='train'): batch = [b.cuda() for b in batch] # if needs GPU if task == 'train': input, input_type, label = batch _, pooled = self.bert(input, input_type) out = self.s_linear(pooled) # 1.计算输出 loss = F.binary_cross_entropy_with_logits(out, label).sum() # 2.计算loss optimzer.zero_grad() # 3.清空梯度 loss.backward() # 4.反向传播计算参数梯度 optimzier.step() # 5.根据梯度和优化策略,更新参数 elif task == 'eval': input, input_type = batch pooled = self.bert(input, input_type) out = self.s_linear(pooled) out = torch.sigmoid(out) return out
通常,我们在 __init__ 函数中定义模型需要使用的层以及初始化等。 forward 函数中定义前向传播、反向传播(pytorch后端自动实现)、计算loss等过程。可以简单概括成5点:
1.计算模型输出 out
2.借损失函数计算和真实label之间的误差loss
3.清空梯度
4.反向传播计算梯度
5.更新参数
这样,我们就完成了对一个深度学习模型的训练、参数更新、预测过程。
这里介绍一个小 trick: 在forward中同时传入任务类型 task ,这样1份代码既可以做训练又可以做预测;因为预测时只进行了前向传播,所以通常将模型输出结果直接返回,再做后处理。
另外需要注意, 只有标量才能直接使用backward() ,如果是对一个batch_size计算loss,得到的不是标量,要先使用tensor.sum()转换成scalar。否则会报错:
RuntimeError: grad can be implicitly created only for scalar outputs
2.模型转换
前边提到,CPU上的数据不能和GPU上数据直接计算,模型也是如此。要用GPU时,先简单做一个转换。
model.cuda() # 将模型所有参数和缓存转至GPU if task == 'train': model.train() else: model.eval() # 冻结 dropout、BN 层,具体参考官方文档
3.避免OOM
训练过程中由于loss.backward() 会将计算图的隐藏变量梯度清除,从而释放空间;但是测试的时候没有这一机制,因此有可能随着测试的进行中间变量越来越多,导致out of memory的发生。
pytorch0.4.1以上可以使用 with torch.no_grad() 进行数值计算,不需要创建计算图;也就不会跟踪计算梯度,节省了内存/显存。
with torch.no_grad(): # 不进行梯度计算 for batch in testloader: res = model(batch, task='eval') res = res.cpu() # 转换回CPU,节约显存 pass
如果显存不够大支撑不了实验,一般有几种缓解方法:
1.加大显存
2.减小batch
3.使用一些策略及时释放显存
最后再次强调,学习pytorch的最好途径是阅读官方文档(中文翻译版亦可)。如果能跟着官方Doc学习,结合一些项目实战(NLP、CV等都可以),想必会有事半功倍的效果。
训练篇
1.损失函数
pytorch根据模型输出和真实label计算损失时,一般使用损失函数。对于常用的二元交叉熵损失函数 binary_cross_entropy_with_logits ,有2个注意点:
(1)计算损失时,input和target需要先转换成float类型
(2)reduction参数可以决定返回的loss是tensor还是一个整数
import torch.nn.functional as F print(F.binary_cross_entropy_with_logits(torch.LongTensor([[1.2,3.1,2.2]]).float(), torch.LongTensor([[1,2,3]]).float(), reduction='none')) print(F.binary_cross_entropy_with_logits(torch.LongTensor([[1.2,3.1,2.2]]).float(), torch.LongTensor([[1,2,3]]).float(), reduction='mean')) print(F.binary_cross_entropy_with_logits(torch.LongTensor([[1.2,3.1,2.2]]).float(), torch.LongTensor([[1,2,3]]).float(), reduction='sum')) # output # tensor([[ 0.3133, -2.9514, -3.8731]]) # tensor(-6.5112) # tensor(-6.5112)
2.Device-side assert triggered Error
报错输出的典型信息:
[RuntimeError: cuda runtime error (59) : device-side assert triggered at /opt/conda/condabld/pytorch_1503970438496/work/torch/lib/THC/generic/THCStorage.c:32]....
这个错误一般在model进行forward前向传播中碰到,典型原因是GPU tensor 下索引失败引起的异常,out-of-bounds 即在[0, x]下,索引为负,或者超过 x。
建议:检查targets有没有越界!比如输入数据到 层,对应的索引范围应该是0-9,如果输入1-10就会报错。
3.zip argument #1 must support iteration rror
这个错误是我在使用GPU单机多卡训练时碰到的;多gpu训练时,服务器自动把你的batch_size分成n_gpu份,每个gpu跑一些数据, 最后再合起来。之所以出现这个bug是因为我在模型返回的时候(forward函数中),除了loss还返回了标量(这一批batch_size中正确预测的个数,int类型)。
所以多卡训练时应避免从训练过程中返回标量 ;其他统计指标可以在训练完一个epoch再进行。如果是单卡训练,则返回标量还是张量,都没有问题了。
Loss篇
1.使用Cross_entropy损失函数时出现 RuntimeError: multi-target not supported at …
输入的真实标签必须为0~n-1(sparse编码,非one-hot),而且必须为1维的,如果设置标签为[n x 1]维,也会出现以上错误。
cross_entropy官网函数定义:
# input (Tensor) – size = (N, C) where C = number of classes # target (Tensor) – size = (N,) where each value is 0 <= targets[i] <= C - 1 torch.nn.functional.cross_entropy(input, target, ...)
小结
pytorch框架虽然好用,但只是干活的工具不是目的;同时Keras也有即插即用,部署方便等优点。综上,最理想的状态是各个工具都能灵活使用,且适当了解框架底层的架构、源码,可以按需debug、自定义模型和loss等等。
1.pyTorch官网
2.horace.io/pytorch-vs-te
3.[PyTorch]论文pytorch复现中遇到的BUG
本文由作者授权AINLP原创发布于公众号平台,欢迎投稿,AI、NLP均可。 原文链接,点击"阅读原文"直达:
https://zhuanlan.zhihu.com/p/149771904
推荐阅读
文本自动摘要任务的“不完全”心得总结番外篇——submodular函数优化
斯坦福大学NLP组Python深度学习自然语言处理工具Stanza试用
太赞了!Springer面向公众开放电子书籍,附65本数学、编程、机器学习、深度学习、数据挖掘、数据科学等书籍链接及打包下载
数学之美中盛赞的 Michael Collins 教授,他的NLP课程要不要收藏?
关于AINLP
AINLP 是一个有趣有AI的自然语言处理社区,专注于 AI、NLP、机器学习、深度学习、推荐算法等相关技术的分享,主题包括文本摘要、智能问答、聊天机器人、机器翻译、自动生成、知识图谱、预训练模型、推荐系统、计算广告、招聘信息、求职经验分享等,欢迎关注!加技术交流群请添加AINLPer(id:ainlper),备注工作/研究方向+加群目的。
阅读至此了,点个在看吧 :point_down:
推荐阅读
征稿启示| 200元稿费+5000DBC(价值20个小时GPU算力)
文本自动摘要任务的“不完全”心得总结番外篇——submodular函数优化
斯坦福大学NLP组Python深度学习自然语言处理工具Stanza试用
太赞了!Springer面向公众开放电子书籍,附65本数学、编程、机器学习、深度学习、数据挖掘、数据科学等书籍链接及打包下载
数学之美中盛赞的 Michael Collins 教授,他的NLP课程要不要收藏?
关于AINLP
AINLP 是一个有趣有AI的自然语言处理社区,专注于 AI、NLP、机器学习、深度学习、推荐算法等相关技术的分享,主题包括文本摘要、智能问答、聊天机器人、机器翻译、自动生成、知识图谱、预训练模型、推荐系统、计算广告、招聘信息、求职经验分享等,欢迎关注!加技术交流群请添加AINLPer(id:ainlper),备注工作/研究方向+加群目的。
阅读至此了,点个在看吧 :point_down:
以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网
猜你喜欢:- TensorFlow模型部署到Android,需要注意几点
- 为什么现代系统需要新的编程模型Akka
- 中台之上(五):业务架构和中台的难点,都是需要反复锤炼出标准模型
- 神经网络并不是尚方宝剑,我们需要正视深度 NLP 模型的泛化问题
- 云计算也需要维护 SDN也需要网工 只不过更智能了
- 人生需要点精神吗啡
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
Head Rush Ajax
Brett McLaughlin、Eric Freeman、Elisabeth Freeman / O'Reilly Media, Inc. / 2006-03-01 / USD 34.99
Ajax, or Asynchronous JavaScript and XML, is a term describing the latest rage in web development. Ajax is used to create interactive web applications with XML-based web services, and using JavaScript......一起来看看 《Head Rush Ajax》 这本书的介绍吧!