opencl与数据格式

1、多维数据

推理引擎中会接触到多种类型的数据格式,比如NHWC、NCHW等,那这些数据格式该如何理解,它们的物理排布又是怎么样的呢?

首先,NHWC四个维度分别是怎么样的呢?

  • H,高度
  • W,宽度
  • C,channel,假设一张图片,宽高很容易理解,图片中的一个像素会有raba等不同分量,C即是指不同的分量,针对一般图片而言,C等于4
  • N,batch,可以简单理解为图片的数量

如果N = 2, C = 16, H = 5, W = 4,那么NCHW、NHWC两种格式的数据排布分别如下:

首先,不管是什么格式的数据,它们的物理排布,其实都是一维数组,就像是二维数组,其实也是一维数组实现的,排布也是一维数组。

其次,不论是何种格式,都是先排最后一个维度的数据,如上图,NHWC是一种比较费解的格式,但它其实也是先取最后一个维度C的16个数据,取完所有的16个C的数据,再把倒数第二个数组W移动一位,继续取所有C,即NHWC取数据,是按 C W H N的逆序取的。

举个例子,假设针对格式为NCHW,范围内的任意nchw,那它在一维数组的哪个位置呢?

ini 复制代码
int stride_n = C * H * W * D, 
stride_c = H * W * D, 
stride_h = W * D, 
stride_w = D;

int index = n * stride_n + c * stride_c + h * stride_h + w

提个小问题,如果要把NCHW的数据转换成NHWC格式的数据,该怎么转呢?其实非常简单,对于索引固定的数据,nchw,它在一维数组中的位置是可知的,那同样的索引,nhwc,在一维数组中的位置也是可知的,赋值即可

2、image

在推理引擎中,要处理多维数据,那么核函数中是使用image更好,还是使用其它类型的数据更好呢?

一般而言,image数据有如下好处:

  • 硬件加速缓存,GPU会为image数据提供纹理缓存
  • 较为高效的内存方式模式,通道打包,一次可以存取4个通道数据

这种特性,比较好处理NCHW这种多维数据。所以一般推理引擎的核函数中,也是用image来处理数据。但它也有缺点,它的缺点就是数据格式较复杂,理解难度高

现在问题来了,要使用image处理张量数据,假设张量数据格式为NHWC,但image只有宽高两个维度,那要怎么对应的呢?

假设张量形状为 [N=2, H=3, W=3, C=8],其逻辑布局如下(以 N=0 为例):

H/W W=0 W=1 W=2
H=0 [C0,C1,C2,C3,C4,C5,C6,C7] ... (共 3x8=24 个通道值)
H=1 ...
H=2 ...
  • 每个 (H,W) 位置有 8 个通道值(C=0~7),内存中是连续的。
  • N 维度 :不同批次的数据在物理内存中是连续存储的(N=0 后紧跟 N=1)。

因为N和H是具有内存连续性的,同时W和C也是有内存连续性的,将N和H合并,W和C合并。

ini 复制代码
image.h = N * H
image.w = W * C / 4

为什么C要除以4呢,因为image天然有ragb四个通道

另外为什么一定要NH合并,WC合并呢,不能是其它数据合并吗?其实主要是从性能上考虑,WC、NH它们在内存上有连续性

注意,因为C/4可能出现0,必须确保C是4的倍数,如果不是,则需要补齐,C/4可以理解为C对4向上取整

N=2, H=3, W=3, C=8 为例,合并之后,image2d_t 的布局如下:

如果已经知道image中的x和y,那么怎么推算张量中的位置呢?

注意,最终的c值无法反推了,因为合并只是合并C4,而不是C,只能反推得到C4。

注意,如果使用image处理张量数据,NHWC的格式更适合image处理。而NCHW不适合image处理,因为image一次性就会取4个通道数据,契合NHWC。

3、最大池化算子实践

最大池化的计算方式比较简单,计算方式如下图所示:

现在要使用opencl的方式实现,核函数使用image处理数据,输入的数据的格式为NHWC

java 复制代码
__kernel void max_pooling(
    __read_only image2d_t input,
    __write_only image2d_t output,
    int out_img_width, int out_img_height,
    int width, int height,
    int kW, int kH,
    int strideW, int strideH,
    int outW, int outH, int channel_4
) {
    int x = get_global_id(0);
    int y = get_global_id(1);
    
    // 边界检查
    if (x >= out_img_width || y >= out_img_height) {
        return;
    }
    
    int w_out = x / channel_4;
    int c4 = x % channel_4;
    int h_out = y % outH;
    int n = y / outH;

    int h0 = h_out * strideH;
    int w0 = w_out * strideW;

    printf("x %d, y = %d, w_out = %d, c4 = %d, h_out = %d, n = %d\n", x, y, w_out, c4, h_out, n);

    float4 maxv = (float4)(-FLT_MAX);

    const sampler_t sampler = CLK_NORMALIZED_COORDS_FALSE | 
                             CLK_ADDRESS_CLAMP | 
                             CLK_FILTER_NEAREST;
    for(int kh = 0; kh < kH; ++kh) {
        for(int kw = 0; kw < kW; ++kw) {
            int h = h0 + kh;
            int w = w0 + kw;
            int scrX = c4 * width + w;
            int scry = n * height + h;
            float4 v = read_imagef(input, sampler, (int2)(scrX, scry));
            maxv = max(maxv, v);
        }
    }

    int dst_x = c4 * outW + w_out;
    int dst_y = n * outH + h_out;
    write_imagef(output, (int2)(dst_x, dst_y), maxv);
}
  • 首先,注意上面根据x、y反推张量位置的计算方式,和此前说法一致
  • h0和w0,是输出窗口的起始位置,关于最大池化位置计算公式,大家可以自行百度,通过上述公式可以得到当前池化窗口的起始w和h值
  • 然后就是遍历池化窗口宽高了,遍历找到当前窗口的最大值,找到之后再写入对应的位置
  • 写入位置,也是image的位置,这里又涉及到从张量位置转换为x/y值
相关推荐
渲吧云渲染12 小时前
SaaS模式重构工业软件竞争规则,助力中小企业快速实现数字化转型
大数据·人工智能·sass
算家云12 小时前
DeepSeek-OCR本地部署教程:DeepSeek突破性开创上下文光学压缩,10倍效率重构文本处理范式
人工智能·计算机视觉·算家云·模型部署教程·镜像社区·deepseek-ocr
AgeClub12 小时前
1.2亿老人需助听器:本土品牌如何以AI破局,重构巨头垄断市场?
人工智能
PPIO派欧云13 小时前
PPIO上线Qwen-VL-8B/30B、GLM-4.5-Air等多款中小尺寸模型
人工智能
chenchihwen14 小时前
AI代码开发宝库系列:FAISS向量数据库
数据库·人工智能·python·faiss·1024程序员节
张登杰踩15 小时前
工业产品表面缺陷检测方法综述:从传统视觉到深度学习
人工智能·深度学习
sponge'15 小时前
opencv学习笔记6:SVM分类器
人工智能·机器学习·支持向量机·1024程序员节
zandy101115 小时前
2025年AI IDE的深度评测与推荐:从单一功能效率转向生态壁垒
ide·人工智能
andyguo15 小时前
ChatGPT Atlas vs Chrome:AI 浏览器的新纪元
人工智能·chrome·chatgpt