高性能PyTorch是如何炼成的?整理的10条脱坑指南

发布时间:2023-11-15 点击:133
如何用最少的精力,完成最高效的 pytorch 训练?一位有着 pytorch 两年使用经历的 medium 博主最近分享了他在这方面的 10 个真诚建议。
在 efficient pytorch 这一部分中,作者提供了一些识别和消除 i/o 和 cpu 瓶颈的技巧。第二部分阐述了一些高效张量运算的技巧,第三部分是在高效模型上的 debug 技巧。
在阅读这篇文章之前,你需要对 pytorch 有一定程度的了解。
好吧,从最明显的一个开始:
建议 0:了解你代码中的瓶颈在哪里
命令行工具比如 nvidia-smi、htop、iotop、nvtop、py-spy、strace 等,应该成为你最好的伙伴。你的训练管道是否受 cpu 约束?io 约束?gpu 约束?这些工具将帮你找到答案。
这些工具你可能从未听过,即使听过也可能没用过。没关系。如果你不立即使用它们也可以。只需记住,其他人可能正在用它们来训练模型,速度可能会比你快 5%%u300110%%u300115%-…… 最终可能会导致面向市场或者工作机会时候的不同结果。
数据预处理
几乎每个训练管道都以 dataset 类开始。它负责提供数据样本。任何必要的数据转换和扩充都可能在此进行。简而言之,dataset 能报告其规模大小以及在给定索引时,给出数据样本。
如果你要处理类图像的数据(2d、3d 扫描),那么磁盘 i/o 可能会成为瓶颈。为了获取原始像素数据,你的代码需要从磁盘中读取数据并解码图像到内存。每个任务都是迅速的,但是当你需要尽快处理成百上千或者成千上万个任务时,可能就成了一个挑战。像 nvidia 这样的库会提供一个 gpu 加速的 jpeg 解码。如果你在数据处理管道中遇到了 io 瓶颈,这种方法绝对值得一试。
还有另外一个选择,ssd 磁盘的访问时间约为 0.08–0.16 毫秒。ram 的访问时间是纳秒级别的。我们可以直接将数据存入内存。
建议 1:如果可能的话,将数据的全部或部分移至 ram。
如果你的内存中有足够多的 ram 来加载和保存你的训练数据,这是从管道中排除最慢的数据检索步骤最简单的方法。
这个建议可能对云实例特别有用,比如亚马逊的 p3.8xlarge。该实例有 ebs 磁盘,它的性能在默认设置下非常受限。但是,该实例配备了惊人的 248gb 的 ram。这足够将整个 imagenet 数据集存入内存了!你可以通过以下方法达到这一目标:
classramdataset(dataset):def__init__(image_fnames,targets):self.targets=targetsself.images=[]forfnameintqdm(image_fnames,desc="loadingfilesinram"):withopen(fname,"rb")asf:self.images.append(f.read())def__len__(self):returnlen(self.targets)def__getitem__(self,index):target=self.targets[index]image,retval=cv2.imdecode(self.images[index],cv2.imread_color)returnimage,target
我个人也面对过这个瓶颈问题。我有一台配有 4x1080ti gpus 的家用 pc。有一次,我采用了有 4 个 nvidia tesla v100 的 p3.8xlarge 实例,然后将我的训练代码移到那里。鉴于 v100 比我的 oldie 1080ti 更新更快的事实,我期待看到训练快 15–30%%u3002出乎意料的是,每个时期的训练时间都增加了。这让我明白要注意基础设施和环境差异,而不仅仅是 cpu 和 gpu 的速度。
根据你的方案,你可以将每个文件的二进制内容保持不变,并在 ram 中进行即时解码,或者对未压缩的图像进行讲解码,并保留原始像素。但是无论你采用什么方法,这里有第二条建议:
建议 2:解析、度量、比较。每次你在管道中提出任何改变,要深入地评估它全面的影响。
假设你对模型、超参数和数据集等没做任何改动,这条建议只关注训练速度。你可以设置一个魔术命令行参数(魔术开关),在指定该参数时,训练会在一些合理的数据样例上运行。利用这个特点,你可以迅速解析管道。
#profilecpubottleneckspython-mcprofiletraining_script.py–profiling#profilegpubottlenecksnvprof–print-gpu-tracepythontrain_mnist.py#profilesystemcallsbottlenecksstrace-fctpythontraining_script.py-etrace=open,close,readadvice3:*preprocesseverythingoffline*
建议 3:离线预处理所有内容
如果你要训练由多张 2048×2048 图像制成的 512×512 尺寸图像,请事先调整。如果你使用灰度图像作为模型的输入,请离线调整颜色。如果你正在进行自然语言处理(nlp),请事先做分词处理(tokenization),并存入磁盘。在训练期间一次次重复相同的操作没有意义。在进行渐进式学习时,你可以以多种分辨率保存训练数据的,这还是比线上调至目标分辨率更快。
对于表格数据,请考虑在创建 dataset 时将 pd.dataframe 目标转换为 pytorch 张量。
建议 4:调整 dataloader 的工作程序
pytorch 使用一个 dataloader 类来简化用于训练模型的批处理过程。为了加快速度,它可以使用 python 中的多进程并行执行。大多数情况下,它可以直接使用。还有几点需要记住:
每个进程生成一批数据,这些批通过互斥锁同步可用于主进程。如果你有 n 个工作程序,那么你的脚本将需要 n 倍的 ram 才能在系统内存中存储这些批次的数据。具体需要多少 ram 呢?
我们来计算一下:
假设我们为 cityscapes 训练图像分割模型,其批处理大小为 32,rgb 图像大小是 512x512x3(高、宽、通道)。我们在 cpu 端进行图像标准化(稍后我将会解释为什么这一点比较重要)。在这种情况下,我们最终的图像 tensor 将会是 512 * 512 * 3 * sizeof(float32) = 3,145,728 字节。与批处理大小相乘,结果是 100,663,296 字节,大约 100mb; 除了图像之外,我们还需要提供 ground-truth 掩膜。它们各自的大小为(默认情况下,掩膜的类型是 long,8 个字节)——512 * 512 * 1 * 8 * 32 = 67,108,864 或者大约 67mb; 因此一批数据所需要的总内存是 167mb。假设有 8 个工作程序,内存的总需求量将是 167 mb * 8 = 1,336 mb。
听起来没有很糟糕,对吗?当你的硬件设置能够容纳提供 8 个以上的工作程序提供的更多批处理时,就会出现问题。或许可以天真地放置 64 个工作程序,但是这将消耗至少近 11gb 的 ram。
当你的数据是 3d 立体扫描时,情况会更糟糕。在这种情况下,512x512x512 单通道 volume 就会占 134mb,批处理大小为 32 时,8 个工作程序将占 4.2gb,仅仅是在内存中保存中间数据,你就需要 32gb 的 ram。
对于这个问题,有个能解决部分问题的方案——你可以尽可能地减少输入数据的通道深度:
将 rgb 图像保持在每个通道深度 8 位。可以轻松地在 gpu 上将图像转换为浮点形式或者标准化。 在数据集中用 uint8 或 uint16 数据类型代替 long。 classmysegmentationdataset(dataset):…def__getitem__(self,index):image=cv2.imread(self.images[index])target=cv2.imread(self.masks[index])#nodatanormalizationandtypecastingherereturntorch.from_numpy(image).permute(2,0,1).contiguous(),torch.from_numpy(target).permute(2,0,1).contiguous()classnormalize(nn.module):#https://github.com/bloodaxe/pytorch-toolbelt/blob/develop/pytorch_toolbelt/modules/normalize.pydef__init__(self,mean,std):super().__init__()self.register_buffer("mean",torch.ten

企业网站到底要怎么营销运营?
云服务器购买要实名吗
注册的域名怎么解析呢?DNS域名解析服务是指什么?
百度云服务器优惠吗
公司建站收费是怎样的 网站建设的注意事项有哪些
win10安全模式怎么进入
电脑打开磁盘/程序/文件时提示Windows无法访问指定设备、路径或文件怎么办
云服务器怎么做404