mirror of https://github.com/Jittor/Jittor
Merge branch 'master' of github.com:Jittor/jittor
This commit is contained in:
commit
7349284663
|
@ -0,0 +1,76 @@
|
|||
Jittor调试技巧
|
||||
=====================
|
||||
|
||||
该文档包含了几种异常情况的调试方法和技巧。
|
||||
|
||||
## 爆Nan、Inf
|
||||
|
||||
在模型训练的过程中,可能因为数值不稳定而出现Nan或者Inf,为了帮助您定位出现nan的代码,您可以设置如下环境变量:
|
||||
|
||||
```bash
|
||||
export JT_CHECK_NAN=1
|
||||
export trace_py_var=3
|
||||
```
|
||||
|
||||
其中,环境变量`JT_CHECK_NAN=1`的用途是:当算子的输出出现异常浮点数时,自动报错并停止程序,环境变量`trace_py_var=3`的用途是:输出算子对应的Python代码行数,3代表输出的详细等级,为最高等级。
|
||||
|
||||
需要注意的是,开启这两个特性之后,jittor速度会大幅下降,并且触发重编译,请不要在训练环境或者生产环境开启该模式,也不要长时间开启该模式。
|
||||
|
||||
## 错误信息定位不准确
|
||||
|
||||
Jittor框架默认采用延迟执行(Lazy execution)的方式进行加速,算子的执行和创建是不同步的,这可能导致报错信息定位不准确,您可以手动关闭延迟执行,采取立刻执行(eager execution)的模式,使用如下环境变量即可:
|
||||
|
||||
```bash
|
||||
export lazy_execution=0
|
||||
```
|
||||
|
||||
或者在python代码中通过flag关闭
|
||||
```python
|
||||
jt.flags.lazy_execution=0
|
||||
```
|
||||
|
||||
## 内存不足
|
||||
|
||||
当您发现Jittor由于内存相关问题,无法运行时,Jittor会向您报告内存使用情况,内存不足可能有两种情况:
|
||||
|
||||
1. 训练模型过大,一个迭代就崩溃报错。
|
||||
2. 多次迭代的过程中,内存占用不断增长,直到最后内存耗尽报错。
|
||||
|
||||
**对于第一种情况** ,您可能需要调整模型或者数据大小,或者使用[多卡训练](jittor.mpi),此外,您还可以在每个迭代内部,让Jittor强制回收内存:
|
||||
|
||||
```python
|
||||
for ...:
|
||||
...
|
||||
jt.sync_all()
|
||||
jt.gc()
|
||||
```
|
||||
|
||||
如果您使用到了CUDA和卷积,还有可能是卷积消耗的临时空间过大,在这种情况下,可以关闭cudnn的临时内存申请,请将如下代码插入到最开始:
|
||||
|
||||
```python
|
||||
jt.cudnn.set_max_workspace_ratio(0.0)
|
||||
```
|
||||
|
||||
**对于第二种情况**,可能是存在内存内存泄漏,请检查您是否存在全局变量没有释放,或者全局变量没有停止梯度,导致计算图不断增加,检查方法如下,您可以在每个迭代内部,插入如下调试代码:
|
||||
|
||||
```python
|
||||
for ...:
|
||||
...
|
||||
jt.sync_all()
|
||||
jt.display_memory_info()
|
||||
```
|
||||
|
||||
Jittor会输出内存消耗,以及计算图的大小`lived_var,lived_op`,以及用户持有的变量数`hold_var`, 如果计算图规模不断增大,请检查代码,或者提交github issue联系我们,并且附上错误日志和代码复现脚本。
|
||||
|
||||
|
||||
## 段错误
|
||||
|
||||
如果Jittor出现了段错误,建议您将错误提交github issue联系我们,并且附上错误日志和代码复现脚本。您也可以使用如下环境变量对程序以及框架进行诊断:
|
||||
|
||||
```bash
|
||||
export debug=1
|
||||
export gdb_attach=1
|
||||
```
|
||||
|
||||
其中,环境变量`debug=1`代表开启jittor的debug模式,性能会大幅下降,但会保留调试信息,`gdb_attach=1`将会自动将gdb贴在jittor的主进程上,方便您进行单步调试。关于gdb的使用,您可以参考[GDB Cheat Sheet](https://darkdust.net/files/GDB%20Cheat%20Sheet.pdf)
|
||||
|
|
@ -45,7 +45,8 @@ language = 'zh_CN'
|
|||
# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
|
||||
# ones.
|
||||
extensions = [
|
||||
'recommonmark',
|
||||
# 'recommonmark',
|
||||
'myst_parser',
|
||||
'sphinx.ext.autodoc',
|
||||
# Auto-generate section labels.
|
||||
'sphinx.ext.autosectionlabel',
|
||||
|
|
|
@ -32,10 +32,22 @@
|
|||
jittor.loss3d
|
||||
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 2
|
||||
:caption: 计图模型库:
|
||||
|
||||
JDet
|
||||
segmentation-jittor
|
||||
InstanceSegmentation-jittor
|
||||
gan-jittor
|
||||
PointCloudLib
|
||||
jrender
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 1
|
||||
:caption: 其他:
|
||||
|
||||
Jittor调试技巧
|
||||
教程 <https://cg.cs.tsinghua.edu.cn/jittor/tutorial/>
|
||||
|
||||
Indices and tables
|
||||
|
|
|
@ -1,7 +1,37 @@
|
|||
jittor.mpi
|
||||
=====================
|
||||
|
||||
这里是Jittor的MPI模块的API文档,您可以通过`from jittor import mpi`来获取该模块。
|
||||
计图分布式基于MPI(Message Passing Interface),本文档主要阐述使用计图MPI,进行多卡和分布式训练的教程。
|
||||
|
||||
|
||||
## 计图MPI安装
|
||||
|
||||
计图依赖`OpenMPI`,用户可以使用如下命令安装`OpenMPI`:
|
||||
|
||||
```bash
|
||||
sudo apt install openmpi-bin openmpi-common libopenmpi-dev
|
||||
```
|
||||
|
||||
计图会自动检测环境变量中是否包含`mpicc`,如果计图成功的检测到了`mpicc`,那么会输出如下信息:
|
||||
|
||||
```
|
||||
[i 0502 14:09:55.758481 24 __init__.py:203] Found mpicc(1.10.2) at /usr/bin/mpicc
|
||||
```
|
||||
|
||||
如果计图没有在环境变量中找到mpi,用户也可以手动指定mpicc的路径告诉计图,添加环境变量即可:`export mpicc_path=/you/mpicc/path`
|
||||
|
||||
`OpenMPI`安装完成以后,用户无需修改代码,需要做的仅仅是修改启动命令行,计图就会用数据并行的方式自动完成并行操作。
|
||||
|
||||
```bash
|
||||
# 单卡训练代码
|
||||
python3.7 -m jittor.test.test_resnet
|
||||
# 分布式多卡训练代码
|
||||
mpirun -np 4 python3.7 -m jittor.test.test_resnet
|
||||
# 指定特定显卡的多卡训练代码
|
||||
CUDA_VISIBLE_DEVICES="2,3" mpirun -np 2 python3.7 -m jittor.test.test_resnet
|
||||
```
|
||||
|
||||
这种便捷性的背后是计图的分布式算子的支撑,计图支持的mpi算子后端会使用nccl进行进一步的加速。计图所有分布式算法的开发均在Python前端完成,这让分布式算法的灵活度增强,开发分布式算法的难度也大大降低。
|
||||
|
||||
## 如何从单卡代码适配多卡代码
|
||||
|
||||
|
@ -11,6 +41,8 @@ jittor.mpi
|
|||
* jittor.nn.BatchNorm*: 同步batch norm
|
||||
* jittor.dataset: 自动数据并行
|
||||
|
||||
用户在使用MPI进行分布式训练时,计图内部的Dataset类会自动并行分发数据,需要注意的是Dataset类中设置的Batch size是**所有节点的batch size之和**,也就是总batch size, 不是单个节点接收到的batch size。
|
||||
|
||||
大部分情况下,单卡训练的代码可以直接使用`mpirun`实现分布式多卡运行。 但仍然如下几种情况下,需要对代码进行调整:
|
||||
|
||||
1. 对硬盘进行写操作(保存模型,保存曲线)
|
||||
|
@ -93,10 +125,30 @@ def val(epoch):
|
|||
......
|
||||
```
|
||||
|
||||
下面是 jittor 的 mpi api reference.
|
||||
|
||||
## MPI接口
|
||||
|
||||
下面是 jittor 的 mpi api reference.
|
||||
目前MPI开放接口如下:
|
||||
|
||||
* `jt.in_mpi`: 当计图不在MPI环境下时,`jt.mpi == False`, 用户可以用这个判断是否在mpi环境下。
|
||||
* `jt.world_rank`: 获取当前进程总数量,如果没有用mpi,则为1。
|
||||
* `jt.rank`: 获取当前进程的编号,区间为`0 ~ jt.world_rank-1`, 如果没有用mpi,则为0。
|
||||
* `jt.mpi`: 计图的MPI模块。
|
||||
* `jt.Module.mpi_param_broadcast(root=0)`: 将模块的参数从root节点广播给其他节点。
|
||||
* `jt.mpi.mpi_reduce(x, op='add', root=0)`: 将所有节点的变量x使用算子op,reduce到root节点。如果op是'add'或者'sum',该接口会把所有变量求和,如果op是'mean',该接口会取均值。
|
||||
|
||||
<img src="https://cg.cs.tsinghua.edu.cn/jittor/images/tutorial/2020-5-2-16-44-distributed/mpi_reduce.png">
|
||||
|
||||
* `jt.mpi.mpi_broadcast(x, root=0)`: 将变量x从root节点广播到所有节点。
|
||||
|
||||
<img src="https://cg.cs.tsinghua.edu.cn/jittor/images/tutorial/2020-5-2-16-44-distributed/mpi_broadcast.png">
|
||||
|
||||
* `jt.mpi.mpi_all_reduce(x, op='add')`: 将所有节点的变量x使用一起reduce,并且吧reduce的结果再次广播到所有节点。如果op是'add'或者'sum',该接口会把所有变量求和,如果op是'mean',该接口会取均值。
|
||||
|
||||
<img src="https://cg.cs.tsinghua.edu.cn/jittor/images/tutorial/2020-5-2-16-44-distributed/mpi_all_reduce.png">
|
||||
|
||||
|
||||
|
||||
```eval_rst
|
||||
.. automodule:: jittor_mpi_core
|
||||
|
@ -106,3 +158,56 @@ def val(epoch):
|
|||
:members:
|
||||
:undoc-members:
|
||||
```
|
||||
|
||||
## 实例:MPI实现分布式同步批归一化层
|
||||
|
||||
|
||||
下面的代码是使用计图实现分布式同步批归一化层的实例代码,在原来批归一化层的基础上,只需增加三行代码,就可以实现分布式的batch norm,添加的代码如下:
|
||||
|
||||
```python
|
||||
# 将均值和方差,通过all reduce同步到所有节点
|
||||
if self.sync and jt.mpi:
|
||||
xmean = xmean.mpi_all_reduce("mean")
|
||||
x2mean = x2mean.mpi_all_reduce("mean")
|
||||
```
|
||||
|
||||
> 注:计图内部已经实现了同步的批归一化层,用户不需要自己实现
|
||||
|
||||
分布式同步批归一化层的完整代码:
|
||||
|
||||
```python
|
||||
class BatchNorm(Module):
|
||||
def __init__(self, num_features, eps=1e-5, momentum=0.1, affine=None, is_train=True, sync=True):
|
||||
assert affine == None
|
||||
|
||||
self.sync = sync
|
||||
self.num_features = num_features
|
||||
self.is_train = is_train
|
||||
self.eps = eps
|
||||
self.momentum = momentum
|
||||
self.weight = init.constant((num_features,), "float32", 1.0)
|
||||
self.bias = init.constant((num_features,), "float32", 0.0)
|
||||
self.running_mean = init.constant((num_features,), "float32", 0.0).stop_grad()
|
||||
self.running_var = init.constant((num_features,), "float32", 1.0).stop_grad()
|
||||
|
||||
def execute(self, x):
|
||||
if self.is_train:
|
||||
xmean = jt.mean(x, dims=[0,2,3], keepdims=1)
|
||||
x2mean = jt.mean(x*x, dims=[0,2,3], keepdims=1)
|
||||
# 将均值和方差,通过all reduce同步到所有节点
|
||||
if self.sync and jt.mpi:
|
||||
xmean = xmean.mpi_all_reduce("mean")
|
||||
x2mean = x2mean.mpi_all_reduce("mean")
|
||||
|
||||
xvar = x2mean-xmean*xmean
|
||||
norm_x = (x-xmean)/jt.sqrt(xvar+self.eps)
|
||||
self.running_mean += (xmean.sum([0,2,3])-self.running_mean)*self.momentum
|
||||
self.running_var += (xvar.sum([0,2,3])-self.running_var)*self.momentum
|
||||
else:
|
||||
running_mean = self.running_mean.broadcast(x, [0,2,3])
|
||||
running_var = self.running_var.broadcast(x, [0,2,3])
|
||||
norm_x = (x-running_mean)/jt.sqrt(running_var+self.eps)
|
||||
w = self.weight.broadcast(x, [0,2,3])
|
||||
b = self.bias.broadcast(x, [0,2,3])
|
||||
return norm_x * w + b
|
||||
```
|
||||
|
|
|
@ -9,7 +9,7 @@
|
|||
# file 'LICENSE.txt', which is part of this source code package.
|
||||
# ***************************************************************
|
||||
|
||||
__version__ = '1.2.3.93'
|
||||
__version__ = '1.2.3.94'
|
||||
from jittor_utils import lock
|
||||
with lock.lock_scope():
|
||||
ori_int = int
|
||||
|
@ -332,12 +332,12 @@ def std(x):
|
|||
return out
|
||||
Var.std = std
|
||||
|
||||
def norm(x, k=2, dim=-1, keepdim=False):
|
||||
assert k==1 or k==2
|
||||
if k==1:
|
||||
def norm(x, p=2, dim=-1, keepdim=False, eps=1e-30):
|
||||
assert p==1 or p==2
|
||||
if p==1:
|
||||
return x.abs().sum(dim, keepdim)
|
||||
if k==2:
|
||||
return (x.sqr()).sum(dim, keepdim).maximum(1e-6).sqrt()
|
||||
if p==2:
|
||||
return (x.sqr()).sum(dim, keepdim).maximum(eps).sqrt()
|
||||
Var.norm = norm
|
||||
|
||||
origin_reshape = reshape
|
||||
|
|
|
@ -344,7 +344,7 @@ def cross(input, other, dim=-1):
|
|||
return jt.contrib.concat([a1.unsqueeze(dim),a2.unsqueeze(dim),a3.unsqueeze(dim)], dim=dim)
|
||||
jt.Var.cross = cross
|
||||
|
||||
def normalize(input, p=2, dim=1, eps=1e-12):
|
||||
def normalize(input, p=2, dim=1, eps=1e-30):
|
||||
r'''
|
||||
Performs L_p normalization of inputs over specified dimension.
|
||||
|
||||
|
@ -376,9 +376,7 @@ def normalize(input, p=2, dim=1, eps=1e-12):
|
|||
[0.02647221 0.59484214 0.80340654]
|
||||
[0.6910677 0.58067477 0.4303977 ]]
|
||||
'''
|
||||
assert p == 2
|
||||
if p == 2:
|
||||
return input / jt.maximum(input.sqr().sum(dim,True).sqrt(), eps)
|
||||
return input / input.norm(p, dim, True, eps)
|
||||
jt.Var.normalize = normalize
|
||||
|
||||
def unbind(x, dim=0):
|
||||
|
|
Loading…
Reference in New Issue