记录一些关于mindspore代码的东西。

算子调用流程

例如GPU算子Argmax,他的调用顺序是: Argmax<-Primitive.Argmax<-ccsrc/backend/kernel_compiler/gpu/arrays/argmax_gpu_kernel.cc<-ccsrc/backend/kernel_compiler/gpu/cuda_impl/argmax_impl.cu

对于batchnorm这类操作,因为cudnn中有预先写好的,因此可以不用手动写GPU impl。

batchnorm

batchnorm属实比较复杂,我看了一下ccsrc里面有4种batchnorm[batchnorm,fused_batch_norm_ex,fused_batch_norm,batchnorm_fold,batchnorm_fold_2]。其中batchnorm_fold,batchnorm_fold_2是量化时使用的。batchnorm,fused_batch_norm_ex,fused_batch_norm是根据当前的运行模式决定的,如果是graph_mode执行P.FusedBatchNormEx进行训练,而测试时均用P.BatchNorm

目前的问题是P.FusedBatchNormExP.BatchNormGPU后端都不支持2D输入。。但我看代码其实也不复杂,因为对于GPU后端其实都是调用的cudnn库,因此需要看看为何cudnn不支持2D输入,先看看pytorch的底层是怎么搞的。

pytorchbatchnorm实现

  1. 因为pytorch时间久,因此aten/src/ATen/native/cudnn/BatchNorm.cpp中包含了很多版本适配。看起来比较复杂。
  2. 对于2D的输入他默认的mode=CUDNN_BATCHNORM_PER_ACTIVATION,介绍里写的是bnScale, bnBias tensor dims are 1xCxHxWx.. (one value per CHW...-slice, normalized over N slice),然后:
TensorDescriptor idesc{ *input, 4 };  // input descriptor
TensorDescriptor wdesc{ expandScale(*weight, input->dim()), 4 }; // descriptor for weight, bias, running_mean, etc.

我认为他的想法应该是将2D输入扩展为4D然后调用cudnnbatchnorm。然后做了下测试,发现他们的梯度的确是相同的:

import torch
import torch.nn.modules as m
import numpy as np

n1 = m.BatchNorm1d(100).to('cuda')
n1.train(True)
x = torch.randn(32, 100, device='cuda', requires_grad=True)
y = torch.randn(32, 100, device='cuda', requires_grad=True)
criterion = m.MSELoss()
y_pred = n1(x)
loss = criterion(y_pred, y)
loss.backward()
xg_1 = x.grad.cpu().numpy().copy()
x.grad.zero_()
print(xg_1)

n2 = m.BatchNorm2d(100).to('cuda')
n2.train(True)
nx = torch.unsqueeze(torch.unsqueeze(x, -1), -1)
ny = torch.unsqueeze(torch.unsqueeze(y, -1), -1)
y_pred = n2(nx)
loss = criterion(y_pred, ny)
loss.backward()
xg_2 = x.grad.cpu().numpy().copy()
x.grad.zero_()
print(xg_2)
print(np.allclose(xg_1, xg_2))

编译代码

编译代码首先需要按官方配置设置docker,属实麻烦,因为需要下载很多依赖,必须得有vpn不然不行。

  1. 配置docker使用主机的代理:我用的是vscode,因此在devcontainer中添加:
"runArgs": [
"--runtime=nvidia",
"--network=host"
],

然后dockerfile添加代理地址,或者手动在终端设置:

RUN echo -e "\nexport https_proxy=http://127.0.0.1:8888\nexport http_proxy=http://127.0.0.1:8889" >> ~/.bashrc && source ~/.bashrc

  1. 开始编译,icu4j校验不匹配

我切换到0.7.0版本,然后./build.sh -e gpu -j 8,一开始构建很正常,到icu4j就奇怪了,这个包我下载了不下5次,然后看了一下mindspore的源码记录:https://gitee.com/mindspore/mindspore/commit/094bdbe253a328baee394922aeb54389ca07d563,发现他的md5写错了。。。按他的更新即可。

  1. 出现No rule to make target '/usr/local/cuda/lib64/libcudnn.so', needed by 'mindspore/ccsrc/CMakeFiles/_c_expression.dir/cmake_device_link.o'. Stop.

这个原因是官方给的develrelease版的镜像配置不太一样,导致libcudnn.so没有在正确位置,执行:

ln -s /usr/lib/x86_64-linux-gnu/libcudnn.so.7.6.5 /usr/local/cuda/lib64/libcudnn.so
ln -s /usr/include/x86_64-linux-gnu/cudnn_v7.h /usr/local/cuda/include/cudnn.h