问题类型
支持
来源
源代码
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精度保存它们,但当我查看时,显然它确实导出了完整的精度,这种导出权重的方式应该是没问题的。
我非常困惑。
7条答案
按热度按时间rxztt3cl1#
请确保在C中正确加载和处理权重。您可以通过从.csv文件中加载权重后打印它们,并将其与Python中的模型输出进行比较来实现这一点。如果它们不相同,则权重没有正确读取。检查Python和C中使用的数据类型,并确保它们匹配。此外,确保在C中正确处理权重,并使用与Python中的模型相同的操作。
顺便说一下,我使用了Clerkie(AI代码调试工具)- https://clerkie.co/ 来帮助我在这里思考方法(在分享之前仍然会审查/调查反馈)。希望这有帮助!
jei2mxaa2#
感谢您的回复,但这不是问题所在。我已经用许多简单的FNN网络进行了测试,包括一个直接输出的单层隐藏单元,这些网络非常简单易实现且不容易出错。实际上,我已经遇到这个问题一年多了,而且一直让我感到困惑。这似乎与Tensorflow在幕后的工作方式有关。文档声称它默认使用float32,当我检查我的Python应用程序的控制台时也是如此,而且我在C中也使用float32,所以我不确定差异来自哪里。这就是为什么我希望比我自己在Tensorflow方面更有经验的人能解释一下这个问题。
ymzxtsji3#
你好,BeverlyCode!
抱歉回复晚了。感谢你分享关于从用Python训练的Keras模型加载权重时C++中差异的观察。
是否有可能为你创建一个带有Tensorflow C++文档的玩具模型,并在这里发布你的结果以复制相同的不一致性。
你可以在这个论坛上发布这个问题。
谢谢!
5vf7fwbs4#
问题不是关于Tensorflow C++的,而是关于在Python的Tensorflow Keras中,当我使用原始问题中定义的Python代码导出权重时,这些相同的权重在不使用Tensorflow的情况下无法产生相同的预测(当我在自定义的C程序中使用它们时)。
在Tensorflow中训练的权重应该可以在Tensorflow之外使用相同的网络拓扑结构工作,但它们不能,我不知道为什么。我想知道为什么。
如果我做错了什么,那么在Python中从Tensorflow Keras模型导出权重到文本文件的推荐方法是什么?
wgmfuz8q5#
好的,@BeverlyCode !
感谢您的更新。
@sachinprasadhs !
您能看一下这个问题吗?
谢谢!
k2fxgqgv6#
首先,你需要在第236行的main.c文件中生成数据集,取消注解到第276行,然后添加一个
return 0;
并使用gcc main.c -lm -Ofast -mfma -march=native -o main
编译,最后使用./main
执行。这将生成所需的数据集dataset.dat
和testset.dat
。(记住在生成数据集后将这些行注解回来,因为你以后需要重新运行此程序进行预测,而不想用新数据集覆盖现有的数据集)*然后只需执行python3 fit.py
(无参数)。权重的C头文件将输出到
models/
目录。这些是在main.c中用C执行前向传播的权重。这是一个简单的FNN神经网络,有16个单元,2层:用C构造输入和输出层如下:
你的输出头文件在
models/
目录中将包含两个const float数组;neural_unitvector_layer0
和neural_unitvector_layer1
。将
neural_unitvector_layer0
重命名为nv0
。用新的const float数组
nv0
替换第68行。将
neural_unitvector_layer1
重命名为nv1
。用新的const float数组
nv1
替换第69行。(记得在main.c开头注解掉生成新的
dataset.dat
和testset.dat
文件的那些行)*使用
gcc main.c -lm -Ofast -mfma -march=native -o main
编译,然后再次使用./main
执行。你应该得到与Python程序相同的预测结果,因为它们是相同的
dataset.dat
和testset.dat
,使用相同的权重。但出于某种原因,你不会得到相同的结果。它产生的结果与在相同权重下使用model.predict()
不同。main.c只测试整体准确性,但你可以轻松地将其更改为直接比较整个testset.dat
或仅从中提取一个输入进行比较 - 显然的是,相同的权重在C和Python的Tensorflow Keras中产生不同的结果,我不知道为什么。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网络中导出模型时,我也尝试以上述描述的完全相同方式导出权重,我得到了以下输出:
这很奇怪,因为这个输出似乎声称它只是一个普通的基础FNN网络,而我的C代码应该可以很好地重现上面提到的前面的回答中的前向传播。这是奇怪的。