V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
sunhk25
V2EX  ›  C++

C++读取 npy 保存的 float16 精度的二进制文件

  •  
  •   sunhk25 · 2022-12-02 14:04:19 +08:00 · 2791 次点击
    这是一个创建于 722 天前的主题,其中的信息可能已经有所发展或是发生改变。

    numpy 以浮点型( float32 )来保存,然后 C++以浮点型( float )可以正常读取数值。 为了减少保存文件的大小以半精度浮点型来保存的话,无法读取到正确的数值。 请问有知道 C++的读取问题出在哪里吗?

    用 Python 保存 npy 文件

    import numpy as np
    outputDirectory = './t.npy'
    data = [1] * (5)
    
    # npData = np.array(data, dtype=np.float32).reshape(5)
    npData = np.array(data, dtype=np.float16).reshape(5)
    np.save(outputDirectory, npData)
    

    用 C++来读取 npy 文件 ※参照以下库的写法 https://github.com/llohse/libnpy/blob/master/include/npy.hpp#L554

    // ・・・
            // read the data
            //stream.read(reinterpret_cast<char*>(data.data()), sizeof(Scalar) * size);
            stream.read(reinterpret_cast<char*>(data.data()), 2 * 3);
    
    第 1 条附言  ·  2022-12-03 22:34:14 +08:00

    下面是测试代码片段 ・59957800大小的npy数组不论float16还是32Python的numpy来load的话仅需0.047s ・float32型npy通过C++来读取需要0.115s(增加近两倍) ・float16型npy通过C++来读取+转换需要1.7s(增加近35倍) 从结论来看npy以float32来保存还比较实用。

    std::vector<float> data;
    int total = 59957800;
    data.resize(total);
    
    // float32型npy(二维数组214135 * 280)⇒用Python的numpy来load的话仅需0.047s
    // stream.read(reinterpret_cast<char*>(&data[0]), 4 * total);
    // ⇒用时0.115s
    
    // float16型npy(二维数组214135 * 280)⇒用Python的numpy来load的话跟32bit一样仅需0.047s
    uint16_t f16;
    for (int i = 0; i <= total; i = i + 1)
    {
        stream.read(reinterpret_cast<char*>(&f16), 2);
       // https://www.programiz.com/cpp-programming/online-compiler/
        data[i] = float16_to_float32(f16);
    }
    // ⇒用时1.7s
    
    第 2 条附言  ·  2022-12-08 17:29:13 +08:00
    最近找到了下面这个 float16 精度的库,配合 npy 读取文件库可以相对快速读取 float16 精度的 npy 文件。
    https://half.sourceforge.net/index.html#doc
    https://github.com/melowntech/half

    npy 读取文件库:
    https://github.com/PyMesh/PyMesh/blob/main/src/Misc/numpy.hpp

    以上的处理速度是原生的 1.8 倍左右
    https://github.com/numpy/numpy/blob/3544fae2859d8ecac378b9ddc8f909c69455a831/numpy/core/src/npymath/halffloat.c
    16 条回复    2023-01-07 15:31:54 +08:00
    lonewolfakela
        1
    lonewolfakela  
       2022-12-02 14:12:54 +08:00
    你这个 data vector 里的 Scalar 是啥类型呢
    sunhk25
        2
    sunhk25  
    OP
       2022-12-02 14:20:01 +08:00
    @lonewolfakela
    Scalar 是 float 类型,C++不太理解好像没有 numpy 对应的 float16 。
    numpy 可以正常读取保存的 float16 类型。
    lonewolfakela
        3
    lonewolfakela  
       2022-12-02 14:25:55 +08:00
    你把 16 位的数据塞进 32 位 float 的 vector 当然没法得到正确数据啦
    tool2d
        4
    tool2d  
       2022-12-02 14:50:49 +08:00
    需要用 gcc 编译,VC 并不支持 float16 硬件类型。

    https://gcc.gnu.org/onlinedocs/gcc/Half-Precision.html

    在 x86 上,如果没开 mavx512fp16 ,就是纯软件的 float16 ,计算速度会相当慢。不如转换到 float32 。
    sunhk25
        5
    sunhk25  
    OP
       2022-12-02 15:01:00 +08:00
    @lonewolfakela
    16 位的数据时我读取的时候指定的大小是 2 ( float 的时候 sizeof 是 4 )
    lonewolfakela
        6
    lonewolfakela  
       2022-12-02 15:07:23 +08:00
    @sunhk25 #5 你指定大小是 2 它也对不了啊,两个 float16 被你塞到一个 float32 里去了你觉得你能读出来什么呢……
    sunhk25
        7
    sunhk25  
    OP
       2022-12-02 15:12:16 +08:00
    @tool2d
    Python 里 float16 和 float32 的计算速度之前有过比较没有变慢所以才想到保存为半精度也没有问题。
    想着用 C++来再提高下速度,就遇到了这个问题了。
    gcc 编译的话好像挺麻烦的样子 我了解了解。
    sunhk25
        8
    sunhk25  
    OP
       2022-12-02 15:17:04 +08:00
    @lonewolfakela
    对 是这个问题。不知道如何指定 float16 类型。
    刚才那位说了不支持 float16 需要编译一下。
    kirory
        9
    kirory  
       2022-12-03 00:13:20 +08:00
    可以提出指数和小数部分之后再放到 float 里
    kirory
        10
    kirory  
       2022-12-03 00:53:25 +08:00   ❤️ 1
    sunhk25
        11
    sunhk25  
    OP
       2022-12-03 08:38:55 +08:00 via Android
    @kirory
    以 16bit 从二进制文件取出来后放到数组,然后再进行转换吗,效率会不会是个问题。
    稍候测一下。
    kirory
        12
    kirory  
       2022-12-03 19:13:27 +08:00
    @sunhk25 边读边转,转换肯定比 IO 快,影响应该很小
    sunhk25
        13
    sunhk25  
    OP
       2022-12-03 22:37:43 +08:00
    @kirory
    刚才测了一下转换是没有问题,效率有点慢。
    比 numpy 的 load 处理慢了 35 倍。
    c0xt30a
        14
    c0xt30a  
       2022-12-17 09:38:37 +08:00   ❤️ 1
    OP 为啥不直接 dump 内存?

    array.tofile( ... )
    array.fromfile( ... )

    就可以了啊

    C++ 里也是类似处理
    sunhk25
        15
    sunhk25  
    OP
       2022-12-17 21:08:27 +08:00 via Android
    @c0xt30a numpy 按 16bit 的 array 数组存到文件,然后 c++dump 这个文件后,数组再转换到 float ?这个方案可以参考下。
    hunk
        16
    hunk  
       2023-01-07 15:31:54 +08:00
    新手小白插一句,浮点真的比较坑,最近看华泰的 sdk 学到一招,浮点数统统乘 1000 ,瞬间安静。
    仅供参考。
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   2758 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 23ms · UTC 10:06 · PVG 18:06 · LAX 02:06 · JFK 05:06
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.