C++ 从 HDF5 文件读取 Keras 神经网络模型和参数

一. 背景与应用

我就是想这样做, 具体应用可参考 C++ 和 OpenCV 实现卷积神经网络并加载 Keras 训练好的参数进行预测C++ 和 OpenCV 实现 Keras Sequential 网络

二. Keras 保存的 HDF5 参数(Weight) 文件分析

所有的 HDF5 文件中都有一个 Root Group, 所有的 Object 都在 Root Group 下, 所以从 Root Group 为起点出发, 就可以遍历所有 Object (Group\Dataset…)
hdf5 structure

注意, 在下面可能不再提示, 所有的 HDF5 Object 在用完之后都要调用对应的 close 函数关闭

神经网络参数文件用 HDF5 View 打开如下图. 每一层的参数就是一个 Group, 里面包含了 另一个 Group, 这个同名的 Group 中包含两个 Dataset, 分别是 kernel:0 和 bias:0. 全连接层、卷积模板 等需要训练的参数 都在 kernel:0 里面

注意图中有的 Group 上面有一个 A 字母, 这个 A 字母表示这个 Group 的 Property 中的 Attributes 不为空
hdf5 view

二. 遍历起点

怎样找到这个 Root Group? Root Group 的名字就是一个斜杠 “/”, 用这个名称就可以找到它

1. 工程需要包含的头文件和库文件

工程的配置可参考 C\C++ 写 HDF5 文件示例

#include <stdint.h>
#include <hdf5.h>
#include <H5Cpp.h>
#include <iostream>

using namespace H5;
using namespace std;

#ifdef _DEBUG
#pragma comment(lib, "hdf5_D.lib")
#pragma comment(lib, "hdf5_cpp_D.lib")
#else
#pragma comment(lib, "hdf5.lib")
#pragma comment(lib, "hdf5_cpp.lib")
#endif

2. 打开文件

// 用只读方式打开文件, 用完后记得要调用 file.close() 关闭释放资源
H5File file("文件路径, 你需要自己修改, 文件名包括.扩展名", H5F_ACC_RDONLY);

3. 打开 Root Group 并输出其中 Object 名称

这个步骤不是必须的, 只是为了看到有哪些 Object 而已

// 打开 Root Grout, 用完后记得要调用 rg.close() 关闭释放资源
Group rg(file.getObjId("/"));

// 取得 Group 中 Object 的数量
const hsize_t objs = rg.getNumObjs();

for (hsize_t i = 0; i < objs; i++)
{
	// 用 Index 为参数获取 Object 名字
	const H5std_string name = rg.getObjnameByIdx(i);
	
	cout << "Obj_name_" << i + 1 << ": " << name.c_str() << endl;
}

rg.close();
file.close();

cout << endl << endl;
system("pause");

输出如下, 和上面 HDF5 View 中显示的一样, 这个顺序和神经网络定义的时候的顺序是不是样的, 有用的是 name, 不是顺序
如果要看某个 Group 中内容, 再对这个 Group 进行相同的操作就可以了
layer name

三. 读取 Dataset 中的数据

以读取 conv_1 中的 kernel:0 为例

1. 打开 kernel:0

在上面我们已经定位到了 Root Group, 现在要打开 kernel:0, 只需要其路径就可以办到

// 用完之后也要调用 dset.close() 关闭
DataSet dset(rg.getObjId("/conv_1/conv_1/kernel:0")); // 绝对路径

// 如果使用相对路径的话, 变成
// DataSet dset(rg.getObjId("conv_1/conv_1/kernel:0"));
// 还可以这样
// DataSet dset = file.openDataSet("/conv_1/conv_1/kernel:0");

2. 读取 DataSpace 信息

DataSpace 描述了数据的 维度, 属性 等信息, 要读取数据, 就需要知道这些信息, 从而分配存在空间

DataSpace dsp = dset.getSpace();

// rank 描述的是有几个维, 就是有 rank 个描述维度大小的变量
const int rank = dsp.getSimpleExtentNdims();

// 创建一个数组存储每一个维度的大小
hsize_t *dims = new hsize_t[rank];

// 这里的 ndims 和 rank 是一样的
const int ndims = dsp.getSimpleExtentDims(dims);

// 输出各维度的大小
for (int i = 0; i < rank; i++)
{
	cout << "Dimension_" << i + 1 << " = " << dims[i] << endl;
}

delete []dims;
dims = nullptr;

// Dataset 中数据的数据类型
DataType dt = dset.getDataType();
const H5T_class_t t = dt.getClass();
cout << "kernel:0 type is " << t << endl;

dt.close();
dsp.close();
dset.close();

我打开的文件输出的是 (3, 5, 3, 8), 表示卷积模板有 8 个, 每个大小是 3 * 5 * 3 , 最后一个 3 表示 这个模板是 3 通道的, 这些数据和定义神经网络的结构是相关的, 不同的结构输出就不一样
dims
数据类型输出, 可以看出, 类型 = 1, 表示 Kernel:0 中的数据是 H5T_FLOAT, 就是浮点型数据
data type

3. 读取 Dataset 数据

上面已经知道了数据维度和数据类型, 现在要读数据就很简单了

// 数据在内存中的字节数除以数据类型得到 buf 的大小
const hsize_t data_size = dset.getInMemDataSize() / sizeof(float);

float *buf = new float[data_size];

// 读出数据到 buf 中
dset.read(buf, dt);

for (int i = 0; i < data_size; i++)
{
	cout << buf[i] << endl;
}

delete []buf;
buf = nullptr;

部分数据如下
kernel data
至此神经网络的其他参数就可以用相同的方法读取了

四. 读取神经网络保存的模型结构

除了参数, 还可以读取保存的模型结构, 只是模型结构保存在模型文件(.h5) 中的 Attributes\model_config 中, 读出来之后解析字符串就可以了
model
model_weightsAttributes\layer_names 中还有按模型定义顺序的每一层的名称
layer names
那这个要怎么读取呢?

1. 打开文件

H5File file("文件路径, 你需要自己修改, 文件名包括.扩展名", H5F_ACC_RDONLY);

2. 读取 Attributes

Attribute at = rg.openAttribute("model_config");

// 数据类型
DataType dt = at.getDataType();

// 读出的字符串
H5std_string ati;
at.read(dt, ati);

cout << ati.c_str() << endl;

dt.close();
at.close();

rg.close();
file.close();

输出如下
model_config
要读取其他的数据也大同小异. 有了模型定义之后, 就可以用 C++ 加载模型和参数实现神经网络预测了

有一点需要注意, 保存的模型里面也有 model_weights, 它和单独保存的 weights 可能不一样, 因为你在训练的时候有指定 save_best_only = “True”

五. 代码下载

完整的代码可下载 VS2015 x64 代码示例

  • 9
    点赞
  • 44
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 5
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

Mr-MegRob

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

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

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

打赏作者

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

抵扣说明:

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

余额充值