将Tensorflow Keras中的权重导出到C语言生成的输出集不同,模型的准确性降低,

jogvjijk  于 5个月前  发布在  其他
关注(0)|答案(7)|浏览(47)

问题类型

支持

来源

源代码

Tensorflow版本

任意版本

自定义代码

是的

OS平台和发行版

Linux

当前行为?

出于某种原因,当我从Tensorflow Keras模型导出权重(无论是简单的Sequential FNN),然后将它们加载到C中进行前向传播时,我得到的结果不同。有时相似,但通常根本不相同。
这是我导出权重的方式:

for layer in model.layers:
        if layer.get_weights() != []:
            numpy.savetxt(layer.name + ".csv", layer.get_weights()[0].flatten(), delimiter=",")
            numpy.savetxt(layer.name + "_bias.csv", layer.get_weights()[1].flatten(), delimiter=",")

f.write(", /* weight */ " + str(layer.get_weights()[0].flatten()[i]))
f.write(", /* bias */ " + str(layer.get_weights()[1].flatten()[i]))

第一种方法将一个较长的E值写入最高有效位,而第二种方法将较小的float值写入最高有效位。尽管这不应该导致第一种方法在精度上损失吗?对吧?
我不确定为什么保存的权重会产生不同的输出,即使是一个只有一层FNN的简单网络,所有的权重都以float32的形式保存在C中,然后使用float数学函数(如tanhf()等)处理,具体取决于层的输出。
我一定是对Tensorflow或Keras背后的一些实现理解有误?它的某个默认配置对我的网络/权重产生了影响,需要禁用或重现吗?权重数组是以相反的顺序还是其他方式排列的?我需要从左到右而不是从右到左迭代输入吗?我漏掉了什么?
这是一个演示此问题的项目:
https://github.com/jcwml/neural_unitvector
这是一个很好的示例项目。使用相同的输入数据在Tensorflow Keras中训练的权重在C中处理时会产生不同的输出。
在这个项目中,从main.c导出一个数据集,以确保C程序和Tensorflow Keras程序都使用由相同的随机函数生成的数据,然后Keras为一个简单的FNN网络训练一组权重到一些隐藏单元和层,然后这些权重被导出并加载到C程序中,其中在C代码中重建前向传播。在这个示例中有三种类型的网络已经重建了它们的前向传播,每种都比上一种更复杂,然而,没有一个产生与Keras predict()相同的输出,甚至连轻微的精度损失都没有,差异非常大,你可以说权重仍然表示它们曾经训练过的内容,但预测准确性要差得多。
另一个重要的事实是,当我训练一个具有多层的网络时,似乎在许多情况下,当我将权重导出到C进行前向传播时,输入数据最终在到达更深层次之前变为0,就像梯度消失一样,这意味着Tensorflow可能使用了不同的浮点精度格式,但文档说它默认使用float32,而且控制台打印也声称在我的应用中使用的是float32。我唯一的想法是也许numpy没有以完整的float32精度保存它们,但当我查看时,显然它确实导出了完整的精度,这种导出权重的方式应该是没问题的。
我非常困惑。

rxztt3cl

rxztt3cl1#

请确保在C中正确加载和处理权重。您可以通过从.csv文件中加载权重后打印它们,并将其与Python中的模型输出进行比较来实现这一点。如果它们不相同,则权重没有正确读取。检查Python和C中使用的数据类型,并确保它们匹配。此外,确保在C中正确处理权重,并使用与Python中的模型相同的操作。
顺便说一下,我使用了Clerkie(AI代码调试工具)- https://clerkie.co/ 来帮助我在这里思考方法(在分享之前仍然会审查/调查反馈)。希望这有帮助!

jei2mxaa

jei2mxaa2#

感谢您的回复,但这不是问题所在。我已经用许多简单的FNN网络进行了测试,包括一个直接输出的单层隐藏单元,这些网络非常简单易实现且不容易出错。实际上,我已经遇到这个问题一年多了,而且一直让我感到困惑。这似乎与Tensorflow在幕后的工作方式有关。文档声称它默认使用float32,当我检查我的Python应用程序的控制台时也是如此,而且我在C中也使用float32,所以我不确定差异来自哪里。这就是为什么我希望比我自己在Tensorflow方面更有经验的人能解释一下这个问题。

ymzxtsji

ymzxtsji3#

你好,BeverlyCode!
抱歉回复晚了。感谢你分享关于从用Python训练的Keras模型加载权重时C++中差异的观察。
是否有可能为你创建一个带有Tensorflow C++文档的玩具模型,并在这里发布你的结果以复制相同的不一致性。
你可以在这个论坛上发布这个问题。
谢谢!

5vf7fwbs

5vf7fwbs4#

问题不是关于Tensorflow C++的,而是关于在Python的Tensorflow Keras中,当我使用原始问题中定义的Python代码导出权重时,这些相同的权重在不使用Tensorflow的情况下无法产生相同的预测(当我在自定义的C程序中使用它们时)。

在Tensorflow中训练的权重应该可以在Tensorflow之外使用相同的网络拓扑结构工作,但它们不能,我不知道为什么。我想知道为什么。

如果我做错了什么,那么在Python中从Tensorflow Keras模型导出权重到文本文件的推荐方法是什么?

wgmfuz8q

wgmfuz8q5#

好的,@BeverlyCode !
感谢您的更新。
@sachinprasadhs !
您能看一下这个问题吗?
谢谢!

k2fxgqgv

k2fxgqgv6#

首先,你需要在第236行的main.c文件中生成数据集,取消注解到第276行,然后添加一个return 0;并使用gcc main.c -lm -Ofast -mfma -march=native -o main编译,最后使用./main执行。这将生成所需的数据集dataset.dattestset.dat。(记住在生成数据集后将这些行注解回来,因为你以后需要重新运行此程序进行预测,而不想用新数据集覆盖现有的数据集)*然后只需执行python3 fit.py(无参数)。

权重的C头文件将输出到models/目录。这些是在main.c中用C执行前向传播的权重。

这是一个简单的FNN神经网络,有16个单元,2层:用C构造输入和输出层如下:

你的输出头文件在models/目录中将包含两个const float数组; neural_unitvector_layer0neural_unitvector_layer1

neural_unitvector_layer0重命名为nv0

用新的const float数组nv0替换第68行。

neural_unitvector_layer1重命名为nv1

用新的const float数组nv1替换第69行。

(记得在main.c开头注解掉生成新的dataset.dattestset.dat文件的那些行)*

使用gcc main.c -lm -Ofast -mfma -march=native -o main编译,然后再次使用./main执行。

你应该得到与Python程序相同的预测结果,因为它们是相同的dataset.dattestset.dat,使用相同的权重。但出于某种原因,你不会得到相同的结果。它产生的结果与在相同权重下使用model.predict()不同。main.c只测试整体准确性,但你可以轻松地将其更改为直接比较整个testset.dat或仅从中提取一个输入进行比较 - 显然的是,相同的权重在C和Python的Tensorflow Keras中产生不同的结果,我不知道为什么。

xpcnnkqh

xpcnnkqh7#

仅是一个小更新,根据我的研究,在最新版本的Python和包中,使用numpy.savetxt(layer.name + ".csv", layer.get_weights()[0].flatten(), delimiter=",")f.write("," + str(layer.get_weights()[0].flatten()[bc]))导出权重时,它们会精确地导出权重。但是为了确保万无一失,我还将所有浮点数权重导出为字节,然后在C中将它们转换回浮点数。我使用了以下函数进行导出:

def float_to_hex(f): return hex(struct.unpack('<I', struct.pack('<f', f))[0])(它给出了相同的权重)

似乎在Tensorflow中,即使是最基本神经网络的前向传播中也有一些事情是Tensorflow在前向传播中做的,这对我来说并不明显(如果我不得不猜测的话),而且我不确定如何找出它在做什么。我想假设在Tensorflow中,默认情况下所有东西的状态都被禁用,例如层正则化等,除非明确打开?有可能Tensorflow实际上默认打开了一些东西来提高默认训练的网络吗?我只是没有意识到这一点?

我在这里抓救命稻草,但Tensorflow是一个庞大而复杂的最先进框架,我真的不知道从哪里开始或如何进行调试。

但是能够将非常简单的FNN网络在Tensorflow Keras中训练得到的权重提取出来,然后在C程序中重新使用这些权重,对我来说是非常有价值的。当我从一个类似的或等效的FNN网络中导出模型时,我也尝试以上述描述的完全相同方式导出权重,我得到了以下输出:

{
 "class_name": "Sequential",
 "config": {
  "name": "sequential",
  "layers": [
   {
    "class_name": "InputLayer",
    "config": {
     "batch_input_shape": [
      null,
      784
     ],
     "dtype": "float32",
     "sparse": false,
     "ragged": false,
     "name": "dense_input"
    }
   },
   {
    "class_name": "Dense",
    "config": {
     "name": "dense",
     "trainable": true,
     "dtype": "float32",
     "batch_input_shape": [
      null,
      784
     ],
     "units": 8,
     "activation": "tanh",
     "use_bias": true,
     "kernel_initializer": {
      "class_name": "GlorotUniform",
      "config": {
       "seed": null
      }
     },
     "bias_initializer": {
      "class_name": "Zeros",
      "config": {}
     },
     "kernel_regularizer": null,
     "bias_regularizer": null,
     "activity_regularizer": null,
     "kernel_constraint": null,
     "bias_constraint": null
    }
   },
   {
    "class_name": "Dense",
    "config": {
     "name": "dense_1",
     "trainable": true,
     "dtype": "float32",
     "units": 1,
     "activation": "sigmoid",
     "use_bias": true,
     "kernel_initializer": {
      "class_name": "GlorotUniform",
      "config": {
       "seed": null
      }
     },
     "bias_initializer": {
      "class_name": "Zeros",
      "config": {}
     },
     "kernel_regularizer": null,
     "bias_regularizer": null,
     "activity_regularizer": null,
     "kernel_constraint": null,
     "bias_constraint": null
    }
   }
  ]
 },
 "keras_version": "2.11.0",
 "backend": "tensorflow"
}

这很奇怪,因为这个输出似乎声称它只是一个普通的基础FNN网络,而我的C代码应该可以很好地重现上面提到的前面的回答中的前向传播。这是奇怪的。

相关问题