计算一组Tensor的直方图C算法实现

本文介绍了Tensor量化过程中的KL散度算法,用于衡量信息损失。通过读取和处理150528维度的列向量数据,绘制并分析原始直方图,然后进行数据平移以验证直方图分布的影响。讨论了数据分布对于AI训练和推理的重要性,并展示了不同直方图平移效果。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Tensor量化中,计算信息度损失的的一个重要算法叫做KL散度算法,关于其介绍请参考博客:

模型量化中的KL散度扫盲_papaofdoudou的博客-优快云博客_kl三度

本文介绍其程序实现:

首先构造一组TENSOR向量,维度为150528的列向量。

观察其原始的直方图分布,其分布特点如下图所示:

程序实现:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stddef.h>
#include <stdint.h>
#include <fcntl.h>
#include <unistd.h>
#include <math.h>
#include <sys/ioctl.h>
 
#define DBG(fmt, ...)   do { printf("%s line %d, "fmt"\n", __func__, __LINE__, ##__VA_ARGS__); } while (0)
#define min(x,y) ({ \
    typeof(x) _x = (x);    \
    typeof(y) _y = (y);    \
    (void) (&_x == &_y);    \
    _x < _y ? _x : _y; })
 
int get_tensor_from_txt_file(char *file_path, float **buf)
{
    int len = 0;
    static float *memory = NULL;
    static int max_len = 10 * 1024 * 1024;
    FILE *fp = NULL;
 
    if(memory == NULL)
    {
        memory = (float*) malloc(max_len * sizeof(float));
    }
    
    if((fp = fopen(file_path, "r")) == NULL)
    {
        DBG("open tensor error.");
        exit(-1);
    }
 
    while(!feof(fp))
    {
        fscanf(fp, "%f", &memory[len ++]);
    }
 
    *buf = (float*)malloc(len * sizeof(float));
    if(len == 0 || *buf == NULL)
    {
        DBG("read tensor error, len %d, *buf %p", len, *buf);
        exit(-1);
    }
    memcpy(*buf, memory, len * sizeof(float));
 
    fclose(fp);
 
    return len;
}
 
int main(int argc, char **argv)
{
    FILE *file;
 
    DBG("in");
 
    if(argc != 3)
    {
        DBG("input error, you should use this program like that: program tensor binsnum.");
        exit(-1);
    }
 
    int tensor0_len;
    float *tensor0_dat;
    int bins = atoi(argv[2]);
    
    tensor0_len = 0;
    tensor0_dat = NULL;
 
    tensor0_len = get_tensor_from_txt_file(argv[1], &tensor0_dat);
    DBG("tensor len %d.", tensor0_len);

    float absmax = 0.f;
    int i = 0;

    for(i = 0; i < tensor0_len + 1; i ++)
    {
        if(fabs(tensor0_dat[i]) > absmax)
            absmax = fabs(tensor0_dat[i]);
    }
    
    DBG("abs = %f.", absmax);

    int *histogram = malloc(bins * sizeof(int));
    float *histogram_norm = malloc(bins * sizeof(float));
    if(histogram == NULL || histogram_norm == NULL)
    {
        DBG("fatal error, malloc histogram failure.");
        exit(-1);
    }

    memset(histogram, 0x00, bins * sizeof(int));

    for(i = 0; i < tensor0_len; i ++)
    {
        if (tensor0_dat[i] == 0.f) continue;

        const int index = min((int)(fabs(tensor0_dat[i]) / absmax * bins), (bins - 1));
        histogram[index] += 1;
    }

    for(i = 0; i < bins; i ++)
    {
        DBG("histogram[%d] = %d.", i, histogram[i]);
    }

    //直方图归一化
    int sum = 0;

    for(i = 0; i < bins; i ++)
    {
        sum += histogram[i];
        histogram_norm[i] = 0.f;
    }

    for(i = 0; i < bins; i ++)
        histogram_norm[i] = (float)((float)histogram[i])/(float)sum;

    for(i = 0; i < bins; i ++)
    {
        DBG("histogram[%d] = %f.", i, histogram_norm[i]);
    }

    DBG("out");
    return 0;
}

运行:

3BINS:

 100BINS:

100BINS的数据可视化:

 和上面有所差异,原因可能是程序中绝对值的处理,将负值也作为正值处理了。为了验证,我们找到这批数据中的最小值,它是负的,我们将其加上一个偏移,正好变为0,这样所有的值都变为了正数,平移数据范围不会影响直方图的分布,所以我们就可以验证我们的猜测是否正确。

代码:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stddef.h>
#include <stdint.h>
#include <fcntl.h>
#include <unistd.h>
#include <math.h>
#include <sys/ioctl.h>
 
#define DBG(fmt, ...)   do { printf("%s line %d, "fmt"\n", __func__, __LINE__, ##__VA_ARGS__); } while (0)
#define min(x,y) ({ \
    typeof(x) _x = (x);    \
    typeof(y) _y = (y);    \
    (void) (&_x == &_y);    \
    _x < _y ? _x : _y; })
 
int get_tensor_from_txt_file(char *file_path, float **buf)
{
    int len = 0;
    static float *memory = NULL;
    static int max_len = 10 * 1024 * 1024;
    FILE *fp = NULL;
 
    if(memory == NULL)
    {
        memory = (float*) malloc(max_len * sizeof(float));
    }
    
    if((fp = fopen(file_path, "r")) == NULL)
    {
        DBG("open tensor error.");
        exit(-1);
    }
 
    while(!feof(fp))
    {
        fscanf(fp, "%f", &memory[len ++]);
    }
 
    *buf = (float*)malloc(len * sizeof(float));
    if(len == 0 || *buf == NULL)
    {
        DBG("read tensor error, len %d, *buf %p", len, *buf);
        exit(-1);
    }
    memcpy(*buf, memory, len * sizeof(float));
 
    fclose(fp);
 
    return len;
}
 
int main(int argc, char **argv)
{
    FILE *file;
 
    DBG("in");
 
    if(argc != 3)
    {
        DBG("input error, you should use this program like that: program tensor binsnum.");
        exit(-1);
    }
 
    int tensor0_len;
    float *tensor0_dat;
    int bins = atoi(argv[2]);
    
    tensor0_len = 0;
    tensor0_dat = NULL;
 
    tensor0_len = get_tensor_from_txt_file(argv[1], &tensor0_dat);
    DBG("tensor len %d.", tensor0_len);

    float absmax = 0.f;
    float min = 0.f;
    int i = 0;

    for(i = 0; i < tensor0_len + 1; i ++)
	{
        tensor0_dat[i] += 87.939552;
	}

    for(i = 0; i < tensor0_len + 1; i ++)
    {
        if(fabs(tensor0_dat[i]) > absmax)
            absmax = fabs(tensor0_dat[i]);

        if(tensor0_dat[i] < min)
            min = tensor0_dat[i];
    }
    
    DBG("abs = %f, min %f.", absmax, min);

    int *histogram = malloc(bins * sizeof(int));
    float *histogram_norm = malloc(bins * sizeof(float));
    if(histogram == NULL || histogram_norm == NULL)
    {
        DBG("fatal error, malloc histogram failure.");
        exit(-1);
    }

    memset(histogram, 0x00, bins * sizeof(int));

    for(i = 0; i < tensor0_len; i ++)
    {
        if (tensor0_dat[i] == 0.f) continue;

        const int index = min((int)(fabs(tensor0_dat[i]) / absmax * bins), (bins - 1));
        histogram[index] += 1;
    }

    for(i = 0; i < bins; i ++)
    {
        DBG("histogram[%d] = %d.", i, histogram[i]);
    }

    //直方图归一化
    int sum = 0;

    for(i = 0; i < bins; i ++)
    {
        sum += histogram[i];
        histogram_norm[i] = 0.f;
    }

    for(i = 0; i < bins; i ++)
        histogram_norm[i] = (float)((float)histogram[i])/(float)sum;

    for(i = 0; i < bins; i ++)
    {
        printf("%f\n", histogram_norm[i]);
    }

    DBG("out");
    return 0;
}

这次直方图曲线和本篇开头的直方图符合了:

数据可视化部分的PYTHON代码:

import numpy as np
import linecache
import matplotlib.pyplot as plt

filename = "output.tensor"
cols = 1 # number of column
divided_ch = ' ' # divided_character between numbers

def dat_to_matrix(filename):
    file = open(filename)
    lines = file.readlines()
    rows = len(lines)
    # print(rows)
    # print(lines)
    datamat = np.zeros(rows)
    row = 0

    for line in lines:
        line = line.strip().split(divided_ch) # strip remove block space in line
        datamat[row:] = line[:]
        row += 1

    return datamat


data = dat_to_matrix(filename)
# print(data)
X=np.linspace(0,1,100)      # X轴坐标数据
plt.figure(figsize=(8,6))   # 定义图的大小
plt.plot(X,data)            # 绘制曲线图
plt.show()

总结:

1.直方图平移后会被压缩,但是曲线的变化趋势不变,可以作计算导数的思想实验来验证这一点。

2.对于AI训练和推理来说,数据本身的分布形状比数据本身要重要的多.

当OFFSET过大的时候,以山峰高度为例,相当于我们选取的基础海平面太低,以至于无法体现地表山峰的高度趋势了,这个时候,计算的直方图会被压缩。如下图将OFFSET从87改为870

产生的直方图为,对比上图,可以看到图像形状没有变化,但是被压缩了。

证明很简单,设n>m>0.a > 0

\frac{m}{n} \ ?\ \frac{a+m}{a+n}

?应该是什么呢?先通分。

\\ \frac{m(a+n)}{n(a+n)} ? \frac{n(a+m)}{n(a+n)} \\ \because m(a+n)=ma+mn<na+mn=n(a+m) \\ \therefore \frac{m}{n} \ < \ \frac{a+m}{a+n}

总结:

很多时候,量化重要的一步是找出tensor的值域边界,得到每层tensor值的上下边界,在此基础上确定threhold。

参考博客:

使用NCNN的INT8量化方式进行推理_papaofdoudou的博客-优快云博客_int8量化 ncnn


结束 

### 关键点检测算法及其实现方法 关键点检测是一种用于提取图像中有意义特征的技术,广泛应用于计算机视觉领域中的目标识别、姿态估计以及增强现实等领域。以下是几种常见的关键点检测算法及其具体实现方法。 #### 1. SIFT (Scale-Invariant Feature Transform) 算法 SIFT 是一种经典的尺度不变特征转换算法,能够有效提取图像的关键点并生成对应的描述子。其主要步骤如下: - **尺度空间极值检测**:通过对不同尺度下的高斯差分(DoG)图像进行卷积操作,寻找可能具有尺度和旋转不变性的兴趣点[^3]。 - **关键点定位**:利用拟合模型进一步精确定位候选点的位置与尺度,并剔除稳定性较差的点[^3]。 - **方向确定**:计算每个关键点周围像素梯度的方向直方图,赋予该点一个或多个主导方向,从而提高对旋转变化的鲁棒性[^3]。 - **关键点描述**:围绕每一个关键点构建局部区域的梯度分布模式作为描述符,使其具备较强的抗干扰能力[^3]。 ```python import cv2 def detect_sift(image_path): img = cv2.imread(image_path, cv2.IMREAD_GRAYSCALE) sift = cv2.SIFT_create() keypoints, descriptors = sift.detectAndCompute(img, None) return keypoints, descriptors ``` --- #### 2. OpenPose 算法 OpenPose 是目前最流行的人体关键点检测框架之一,支持多人的姿态估计。它采用自底向上的方式处理输入图片,先独立预测所有人的身体部位再组合成完整的骨架结构[^2]。 - 输入一张RGB彩色照片经过预处理后送入神经网络; - 输出热力图(Heatmaps)、PAFs(Part Affinity Fields),分别代表各个关节概率分布及肢体连接关系; - 基于以上两种结果解析得到最终的人体骨骼坐标集合。 ```bash # 安装依赖库 pip install torch torchvision opencv-python-headless matplotlib numpy scipy cython h5py pyyaml future six imageio scikit-image tensorboardX easydict pycocotools visdom tqdm joblib lmdb faiss-gpu==1.6.5 ffmpeg-python moviepy youtube-dl yt-dlp boto3 google-cloud-storage gdown wandb albumentations timm onnxruntime-gpu openpose-pytorch ``` ```python from openpose import pyopenpose as op params = { 'model_folder': './models/', } opWrapper = op.WrapperPython() opWrapper.configure(params) datum = op.Datum() imageToProcess = cv2.imread('input_image.jpg') datum.cvInputData = imageToProcess opWrapper.emplaceAndPop([datum]) print("Body keypoints:", datum.poseKeypoints) cv2.imshow("Output", datum.cvOutputData) cv2.waitKey(0) ``` --- #### 3. YOLOv5 结合关键点回归的手部检测 YOLOv5 提供了一种快速高效的目标检测解决方案,当扩展到手部关键点任务时,则需额外设计分支专门负责预测手指尖端等特定位置[^1]。 - 修改原版配置文件定义新的损失函数形式; - 添加额外层用来输出每类别的中心偏移量以及其他属性参数; - 训练过程中同时优化边界框回归项与关键点坐标的误差; ```python import torch from models.experimental import attempt_load device = torch.device('cuda' if torch.cuda.is_available() else 'cpu') weights = 'best.pt' imgsz = 640 conf_thres = 0.4 iou_thres = 0.5 model = attempt_load(weights, map_location=device).eval() with torch.no_grad(): results = model(torch.zeros((1, 3, imgsz, imgsz), device=device)) for *xyxy, conf, cls in reversed(results.xywh[0]): print(f'Detected hand at {list(map(int, xyxy))} with confidence {conf:.2f}') ``` --- #### 4. PyTorch 实现人体关键点检测 借助深度学习工具包PyTorch可轻松搭建适用于多种场景的关键点探测器。下面展示了一个简单的例子说明如何加载已训练好的HRNet权重来进行全身姿态评估[^2]。 ```python import torch from hrnet_config import config_update_from_file from lib.models.hrnet_pose import get_pose_net cfg = config_update_from_file('./experiments/coco/hrnet/w32_256x192_adam_lr1e-3.yaml') model = get_pose_net(cfg, is_train=False).to(device) checkpoint = torch.load('/path/to/checkpoint.pth.tar', map_location=lambda storage, loc: storage)['state_dict'] model.load_state_dict(checkpoint) transform = transforms.Compose([ transforms.ToTensor(), transforms.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225]) ]) image = Image.open('test.jpg').convert('RGB') width_height = tuple(reversed(image.size)) center = np.array(width_height)/2. scale = max(center)*2./200. tensor_img = transform(image).unsqueeze_(dim=0).float().to(device) output = model(tensor_img)[-1].detach().cpu().numpy()[0] pred_coords = decode_keypoints(output, center=center.numpy(), scale=scale)[..., :2] draw_points_and_lines(pred_coords.astype(np.intp), width_height[::-1]) plt.show() ``` ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

papaofdoudou

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值