同步操作将从 Ascend/vision 强制同步,此操作会覆盖自 Fork 仓库以来所做的任何修改,且无法恢复!!!
确定后同步将在后台操作,完成时将刷新页面,请耐心等待。
原生Torchvision在区别图像处理后端时,使用了image_backend
后端参数来控制图像处理所用后端。
# torchvision_npu/__init__.py
_image_backend = "npu"
def set_image_backend(backend):
global _image_backend
if backend not in ["PIL", "accimage", "npu", "cv2"]:
raise ValueError(f"Invalid backend '{backend}'. Options are 'npu', 'PIL' , 'cv2' and 'accimage'")
_image_backend = backend
print('transform backend: ', torchvision.get_image_backend())
if torchvision.get_image_backend() == 'cv2':
print('If you use the cv2 backend, must install opencv-python already and the input must be np.ndarray, '
'otherwise an exception will be thrown.')
def get_image_backend():
return _image_backend
default_loader
适配。
在 folder.py
中重新定义 default_loader
:
# torchvision_npu/datasets/folder.py
def default_loader(path: str) -> Any:
from torchvision import get_image_backend
if get_image_backend() == 'npu':
return npu_loader(path)
elif get_image_backend() == 'cv2':
return cv2_loader(path)
elif get_image_backend() == "accimage":
return fold.accimage_loader(path)
else:
return fold.pil_loader(path)
monky-patch原有实现:
# torchvision_npu/datasets/folder.py
def add_dataset_imagefolder():
torchvision.datasets.DatasetFolder = DatasetFolder
torchvision.datasets.ImageFolder = ImageFolder
通过monky-patch数据集的__get_item__
方法实现。
Torchvision在torchvision/transforms
路径下提供了图像处理的一些基本方法,其目录结构如下:
transforms/
├── functional_pil.py
├── functional.py # 屏蔽后端实现,将调用转发到PIL或者tensor
├── functional_tensor.py
├── transforms.py # 对外提供接口,调用functional.py
└── ...
通过简单分析能够发现,transforms部分设计逻辑便为:
transforms.py
是对外呈现的表现层,调用torchvision.transforms.functional
中定义的函数,
torchvision.transforms.functional
是具体实现的分发层,
将调用分发到具体实现的torchvision.transforms.functional_xx
模块中。
由于原生分层较为清晰,故在适配transforms部分时也较为简单。
首先在torchvision_npu/transforms
文件夹下增加functional_npu.py
,定义需要适配的图像处理的npu实现;
然后重写并monky-patch部分需要适配的functional.py
内容,非必须情况下,对外呈现层transforms.py
不做monkey-patch。
例如:
# torchvision/transforms/transforms.py
class Normalize(torch.nn.Module):
def __init__(self, mean, std, inplace=False):
super().__init__()
self.mean = mean
self.std = std
self.inplace = inplace
def forward(self, tensor: Tensor) -> Tensor:
return F.normalize(tensor, self.mean, self.std, self.inplace)
def __repr__(self):
return self.__class__.__name__ + '(mean={0}, std={1})'.format(self.mean, self.std)
# torchvision_npu/transforms/functional.py
from torchvision.transforms import functional_tensor as F_t
from torchvision.transforms import functional_pil as F_pil
from . import functional_npu as F_npu
def normalize(tensor: Tensor, mean: List[float], std: List[float], inplace: bool = False) -> Tensor:
if not torch.jit.is_scripting() and not torch.jit.is_tracing():
_log_api_usage_once(normalize)
if not isinstance(tensor, torch.Tensor):
raise TypeError(f"img should be Tensor Image. Got {type(tensor)}")
if tensor.device.type == 'npu':
return F_npu.normalize(tensor, mean, std, inplace)
return F_t.normalize(tensor, mean=mean, std=std, inplace=inplace)
# torchvision_npu/transforms/functional_npu.py
def normalize(tensor: Tensor, mean: List[float], std: List[float], inplace: bool = False) -> Tensor:
if not inplace:
return torch.ops.torchvision.npu_normalize(tensor, mean=mean, variance=std, dtype=tensor.dtype)
return torch.ops.torchvision.npu_normalize_(tensor, mean=mean, variance=std, dtype=tensor.dtype)
表 1 NPU支持算子列表
算子 | 是否支持 |
---|---|
nms | √ |
deform_conv2d | √ |
ps_roi_align | - |
ps_roi_pool | - |
roi_align | √ |
roi_pool | √ |
原生Torchvision的算子适配是将cuda/cpu算子以C++语言的方式进行编写,编译为so库;在加载Torchvision库的时候用torch.ops.load_library(lib_path)
载入该so库;在做Torchvision Adapter插件的适配时,考虑沿用原生实现,增加npu实现方案。
原生实现分析
Torchvision关于算子完整框架代码存在于torchvision/csrc/ops
目录下,其目录结构如下:
torchvision/csrc/ops
│ nms.cpp
│ nms.h
│ ...
│
├─autocast
│ nms_kernel.cpp
│ ...
│
├─autograd
│ ...
│
├─cpu
│ nms_kernel.cpp
│ ...
│
└─cuda
cuda_helpers.h
nms_kernel.cu
...
下面以nms算子为例分析完整的实现逻辑。
如下代码所示,torchvision/csrc/ops/nms.cpp
中定义了算子分发逻辑,算子作为插件附加到torch.ops.torchvision.nms中:
// torchvision/csrc/ops/nms.cpp
namespace vision {
namespace ops {
at::Tensor nms(
const at::Tensor& dets,
const at::Tensor& scores,
double iou_threshold) {
static auto op = c10::Dispatcher::singleton()
.findSchemaOrThrow("torchvision::nms", "")
.typed<decltype(nms)>();
return op.call(dets, scores, iou_threshold);
}
TORCH_LIBRARY_FRAGMENT(torchvision, m) {
m.def(TORCH_SELECTIVE_SCHEMA(
"torchvision::nms(Tensor dets, Tensor scores, float iou_threshold) -> Tensor"));
} // 此处定义 torch.ops.torchvision.nms ,并且该函数的入参和返回值
} // namespace ops
} // namespace vision
再以CPU实现为例分析算子侧实现,CUDA算子实现同理。
文件torchvision/csrc/ops/cpu/nms_kernel.cpp
的nms_kernel
函数中定义了nms算子的CPU实现,
后通过TORCH_LIBRARY_IMPL(torchvision, CPU, m)
宏确认nms算子的CPU实现。
// torchvision/csrc/ops/cpu/nms_kernel.cpp
at::Tensor nms_kernel(
const at::Tensor& dets,
const at::Tensor& scores,
double iou_threshold) {
...
}
TORCH_LIBRARY_IMPL(torchvision, CPU, m) { //绑定torch.ops.torchvision.nms的CPU实现为nms_kernel
m.impl(TORCH_SELECTIVE_NAME("torchvision::nms"), TORCH_FN(nms_kernel));
}
Torchvision接口调用对外呈现为torchvision.ops.nms
,实际上时在内层又是调用了torch.ops.torchvision.nms
,其调用关系如下所示:
适配分析
在Torchvision Adapter插件中对算子进行适配时其实现较为简单,具体而言分为以下几点:
在torchvision_npu/csrc/ops/npu
文件夹下新增npu算子实现,如nms_kernel.cpp
,示例代码如下:
namespace vision {
namespace ops {
at::Tensor nms_kernel(
const at::Tensor& dets,
const at::Tensor& scores,
double iou_threshold) {
// 具体实现
}
TORCH_LIBRARY_IMPL(torchvision, XLA, m) { //绑定torch.ops.torchvision.nms的NPU实现为nms_kernel
m.impl(TORCH_SELECTIVE_NAME("torchvision::nms"), TORCH_FN(nms_kernel));
}
} // namespace ops
} // namespace vision
在打包文件setup.py中配置ext_modules参数,将torchvision_npu/csrc/ops/npu/*.cpp
编译至torchvision_npu._C
模块中;
在torchvision_npu的init函数中,使用torch.ops.load_library(lib_path)
将torchvision_npu._C.so
加载至Torch框架;
在NPU设备执行完成import torchvision
和import torchvision_npu
后,调用 torchvision.ops.nms
或 torch.ops.torchvision.nms
便可自动调用到NPU实现的nms算子。
适配注意事项
Torchvision需要使用在npu设备编译的whl包,否则在NPU上,torch将无法正常加载torchvision._C.so
和torchvision_npu._C.so
这两个二进制库。
此处可能存在不合适展示的内容,页面不予展示。您可通过相关编辑功能自查并修改。
如您确认内容无涉及 不当用语 / 纯广告导流 / 暴力 / 低俗色情 / 侵权 / 盗版 / 虚假 / 无价值内容或违法国家有关法律法规的内容,可点击提交进行申诉,我们将尽快为您处理。