摘要: PyTorch是一个基于Python语言的深度学习框架,专门针对 GPU 加速的深度神经网络(DNN)的程序开发。基本上,它全部的程序都是用python写的,这就使得它的源码看上去比较简洁,在机器学习领域中有普遍的应用。
PyTorch是一个灵活的深度学习框架,它容许经过动态神经网络(即if条件语句和while循环语句那样利用动态控制流的网络)自动分化。它支持GPU加速、分布式训练、多种优化以及更多的、更简洁的特性。python
神经网络是计算图形的一个子类。计算图形接收输入数据,而且数据被路由到那些可能由对数据进行处理的节点进行转换。在深度学习中,神经网络中的神经元一般用参数和可微函数进行数据变换,从而能够经过梯度降低来优化参数以最大程度的减小损失。更普遍来讲,函数能够是随机的,而且图形的结构能够是动态的。所以,虽然神经网络能够很好地适合数据流开发,可是PyTorch的API却围绕着命令行式的编程,这是一种更常见的考虑程序的方式。这使得读取复杂程序的代码和理由变得更容易,而没必要浪费大量的性能;PyTorch实际上运行的速度至关快,并带有不少优化,你能够放心地忘记你是个最终用户。算法
该文件的其他部分是基于官方的MNIST示例,而且应该仅仅是在学习了官方初级教程以后再看。为了提升可读性,代码放在了带有注释的区块中,所以不会被分割成不一样的函数或者是文件,由于一般要用于清晰的、模块化的代码。编程
这些是很是标准的程序或者是包导入代码,特别是用于解决计算机视觉问题的视觉模块:数组
argparse是一种处理在Python中命令行参数的标准方法。网络
它是一种编写与设备无关的代码的好方法(在可用时受益于GPU加速,但当不可用时则返回到CPU)是选择并保存适当的torch.device,它能够用来决定应该存储张量的位置。更多资料请参阅官方文档。PyTorch方法是将设备放置在用户的控制之下,这对于简单的例子来讲可能看起来是件讨厌的事情,可是它使得更容易计算出张量的位置是对调试有用仍是使得手动使用设备变得高效。多线程
对于可重复的实验,有必要为任何使用随机数生成的进行随机种子设置。注意,cuDNN使用非肯定性算法,而且可使用torch.backends.cudnn.enabled = False来进行禁用。框架
因为torchvision模型在~/.torch/models/下面进行保存的,我在~/.torch/datasets保存torchvision数据集。一般来讲,若是结束重用几个数据集,那么将数据集与代码分离开来存放是很是值得的。torchvision.transforms包含不少给单个图片的方便转换的功能,如修剪和正常化。机器学习
DataLoader含有许多可选方案,可是在batch_size和shuffle参数以外,num_workers和pin_memory对于效率也是值得了解一下的。num_workers > 0使用了子进程来进行异步加载数据,而不是在这个过程当中使用主进程块。pin_memory使用pinned RAM来加速RAM到GPU的传输。异步
网络初始化一般包括一些成员变量和可训练参数的层,以及可能分开的可训练参数和不可训练的缓冲器。前向传递以后,使用那些来自纯函数F的函数(不包含参数)的结合。有些人倾向具备彻底功能的网络(例如,保持参数分离和使用F.conv2d,而不是nn.Conv2d)或者是那些彻底分层的网络(例如,nn.ReLU,而不是F.relu)。分布式
.to(device)是将设备参数(和缓冲器)发送到GPU的简便方法,若是设备被设置为GPU,则不作任何操做(当设备被设置为CPU)时。在将网络参数传递给优化器以前,将它们传递给合适的设备是很是重要的,不然优化器将不会正确跟踪参数。
神经网络(nn.Module)和优化器(optim.Optimizer)都具备保存和加载其内部状态的能力,而且.load_state_dict(state_dict)是推荐这么作的方法,你将须要从新加载这两个状态以恢复以前保存的状态字典的训练。保存整个对象可能会容易出错。
这里没有指出的一些要点是,正向传递可使用控制流,例如,成员变量,或者甚至数据自己能够决定if语句的执行。在中间打印出张量也是很是有效的,这会使调试变得更加容易。最后,前向传递可使用多个参数。用一个简短的代码片断来讲明这一点:
默认状况下,网络模块设置为训练模式—这影响了一些模块的运行效果,最明显的是流失和批量标准化。不管如何,最好经过.train()来进行手动设置参数,它将训练标志继承到全部的子模块。
在用loss.backward()收集一组新的梯度并用optimiser.step()进行反向传播以前,有必要手动地集中那些用优化器.zero_grad()优化过了参数的梯度。默认状况下,PyTorch逐渐增长梯度,这是很是方便的,尤为是当你没有足够的资源来计算全部你一次性须要的梯度的时候。
PyTorch使用基于磁带的自动梯度系统—它按必定的顺序收集对张量进行的操做,而后对它们进行重放以进行逆向模式求导。这就是为何它是超级灵活的缘由,而且容许任意的计算图形。若是张量中没有一个须要梯度(当构造张量时,你必须设置requires_grad=True),则不存储任何图形!然而,网络每每趋向那些具备须要梯度的参数,因此从一个网络的输出所作的任何计算都将存储在图形中。所以,若是要想存储由此产生的数据,那么你须要手动禁用梯度,或者更常见地,将其存储为Python数字(经过使用PyTorch标量上的.item())或numpy数组。请在autograd上参阅官方文档以了解更多信息。
切割计算图形的一种方法是使用.detach(),当经过截断反向传播时间来训练RNNs时,可使用这个方法来隐藏状态。当一个成分是另外一个网络的输出时,它也很方便的区分一个损耗,可是这个网络不该该在损失方面被优化 — 例如在GAN训练中从生成器的输出中训练一个鉴别器,或者使用值函数做为基线(例如A2C)的算法训练一个演员评论算法的策略,另外一种防止梯度计算的技术在GAN训练中是有效的(训练来自鉴别器的生成器),以及一般在微调中是经过网络参数并设置param.requires_grad = False进行循环。
除了在控制台或者在日志文件中的日志记录结果外,检查点模型参数(和优化器状态)是很是重要的,用于以防万一。你还可使用torch.save()来保存普通的Python对象,但其它标准选择包括在内置的配置中。
其余:
CUDA调试错误,一般是逻辑问题,会在CPU上产生更明白易懂的错误消息。若是你正在计划使用GPU,最好的方式是能在CPU和GPU之间轻松地切换。一个更广泛的开发技巧是可以设置你的代码,以便在启动一个合适的工做任务以前快速运行全部的逻辑来检查代码—示例是准备一个小的、合成的数据集,运行一个训练、测试周期等等。若是是一个CUDA错误,或者你真的不能切换到CPU模式,那么设置CUDA_LAUNCH_BLOCKING=1将使CUDA内核同步启动,从而会提供更清楚明确的错误消息。
对于torch.multiprocessing的记录,甚至只是一次性运行多个PyTorch脚本。由于PyTorch使用多线程的BLAS库来加速CPU上的线性代数运算,所以它一般会使用多个内核。若是想同时使用多个处理进程或者多个脚原本运行多个程序,那么你能够手动地经过将环境变量OMP_NUM_THREADS设置为1或另外一个小的数字参数来实现—这减小了CPU大幅震动的机会。官方文档中有特别用于多处理技术的注释。
本文做者:【方向】
本文为云栖社区原创内容,未经容许不得转载。