Merge branch 'master' of github.com:Jittor/jittor

This commit is contained in:
cxjyxx_me 2021-08-26 22:28:06 -04:00
commit 7349284663
6 changed files with 205 additions and 13 deletions

View File

@ -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)

View File

@ -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',

View File

@ -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

View File

@ -1,7 +1,37 @@
jittor.mpi
=====================
这里是Jittor的MPI模块的API文档您可以通过`from jittor import mpi`来获取该模块。
计图分布式基于MPIMessage 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使用算子opreduce到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
```

View File

@ -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

View File

@ -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):