×

签到

分享到微信

打开微信,使用扫一扫进入页面后,点击右上角菜单,

点击“发送给朋友”或“分享到朋友圈”完成分享

TF的奇幻漂流----BeautyGAN调试笔记 wyy2021-05-25 17:40:02 回复 3 查看 TensorFlow 思元270
TF的奇幻漂流----BeautyGAN调试笔记
分享到:

BeautyGAN是一个基于风格迁移网络的美颜效果应用。作者提供了训练好的模型和数据,我们可以试着将这个网络迁移到MLU上运行。

BeautyGAN: Instance-level Facial Makeup Transfer with Deep Generative Adversarial Network

https://github.com/Honlan/BeautyGAN

http://liusi-group.com/projects/BeautyGAN

首先我们来试着在CPU上运行这个模型。非常幸运,只要经过常规的配置就可以正确运行。上方的是输入风格,左侧的图片是输入图片,右下是美妆后的效果。

这一步的意义在于,在CPU运行这一步我们验证了模型本身的正确性,也验证了数据预处理和后处理的方法。

image.png

量化

那么我们第一步就开始量化。

量化还是在原CPU代码基础上修改python脚本。

添加环境变量:

os.environ['MLU_VISIBLE_DEVICES']=''
os.environ['MLU_QUANT_PARAM']='ganquant.txt'
os.environ['MLU_RUNNING_MODE']='0'

编辑ganquant.txt文件

cat ganquant.txt
  bit_width:8
  quant_mode:naive

然后运行推理脚本,但是不行,我们发现量化参数并没有出现在ganquant.txt文件中。

量化中的问题:

对于定义session有两种方法

一种是 with tf.Session() as session:

另一种是 session = tf.Session()

对于前者可以正常完成量化,对于后者 则需要显式调用 session.close(),才能将量化参数正确写入文件。

推理

在推理脚本中加入一些内容

os.environ['MLU_VISIBLE_DEVICES']='0'
os.environ['MLU_QUANT_PARAM']='ganquant.txt'
os.environ['MLU_RUNNING_MODE']='1'
os.environ['MLU_STATIC_NODE_FUSION']='true'

和一些运行参数:

config = tf.ConfigProto(allow_soft_placement=True)
config.mlu_options.core_num = 1
config.mlu_options.core_version = 'MLU270'
with tf.Session(config=config) as session:

然后运行脚本。很遗憾,可以很直观的发现,结果完全不对。

image.png

在这种情况下,我们可以将结果输出到文件中。比对一下。

with open("result_mlu.txt","w+") as f:
    for j in out[0].reshape(-1):
        f.write("%8.3f\n" % j)

我们可以发现有CPU和MLU的推理结果有很大区别。

image.png

由此我们需要进一步调试精度

注:在以上的MLU代码中我们直接运行了融合模式。这是因为目前TF的底层逐层模式已经换成了CNNL,这使得CNNL的逐层模式对于CNML的融合模式调试已经没有意义。顺便原来TF提供的MLUFusionOP和dump工具也没用了。

下一步的调试方向是将CPU的运行结果和融合打断后的结果比对。

精度解决思路:1、查清网络结构,考虑打断融合的策略。2、导出每一层的CPU运行结果。3、打断融合对比结果。

模型格式转换

对于原网络模型,使用了 和checkpoints的保存方式。

image.png

我们试着用Netron打开 model. 看看。可惜失败了,对于这种格式的模型超出了Netron的处理范围。(这里如果点“是”,会运行一会自动退出)

image.png

这里保存成PB文件的方式非常简单,在读入原网络的结构和checkpoints后,运行代码:

output_node_name=["generator/xs"]

output_graph = tf.graph_util.convert_variables_to_constants(session,
                                                                                                 session.graph_def, output_node_name)
output_graph = graph_util.extract_sub_graph(output_graph, output_node_name)
with tf.gfile.GFile("model.pb", "wb") as f:
        f.write(output_graph.SerializeToString())

就可以将模型保存到PB文件中。接着就可以用Netron打开文件查看模型结构。

image.png

精度调试

搞清楚了网络结构我们就可以顺着思路继续往下调试。在考虑打断融合的时候我们顺着两个思路来考虑这个问题。一是从整体大的模块上把握这个网络的结构,优先dump一些模块间的Tensor。二是查看一下网络中的算子情况,是否出现了一些比较少见的算子,这种算子也可以作为重点排查的对象。

在这个网络中,我们可以发现整体结构上可以分为两个大的部分。首先是通过一系列操作对两个输入图片分别完成特征提取后Concat。然后将concat结果通过一系列的类似残差模块完成进一步的转换,对风格迁移来说一般完成了从低维到高维再到低维的信息融合。这提示我们可以将dump节点加在Concat上。再放在每个模块的add后面。我们先看看到底是哪一块出了问题。

image.png

另外对于特别的算子,我们发现了一个相对来说比较少见的算子,ResizeNearestNeighbor。这个算子的用处根据TF官方文档:Resize images to size using nearest neighbor interpolation.。看起来像是某种线性插值的采样算法。

所以我们也可以将箭头指的 tensor dump出来试试。

image.png

另外要注意的是,dump最好先dump这个算子第一次出现的地方。如果随机选一个中间的算子可能会受前面错误运行结果干扰。

通过Netron的搜索我们可以看到这个算子一共出现了两次。

image.png

另外为了方便我们dump结果,特别是考虑到我们可能会反反复复的折腾,所以我们给程序添加一个参数,方便我们更换dump的结点。

parser.add_argument('--output_nodes', type=str, default="generator/xs:0", help='something like this: generator/xs:0,generator/concat:0')

output_node=args.output_nodes #"generator/xs:0,generator/concat:0"

最后将结果保存的地方修改一下。

#out = deprocess(out)
for k in range(len(output)):
    print(output[k].name.replace('/',''))
        with open(output[k].name.replace('/','')+"cpu.txt","w+") as f:
              for j in out[k].reshape(-1):
                    f.write("%8.3f\n" % j)

于是我们运行一下程序:

python main_cpu.py --output_nodes 'generator/xs:0,generator/concat:0'

这是保存出的结果。

image.png

同样的我们也去修改MLU的运行程序。然后进行比对。(别忘了把文件名后缀改成MLU)

image.png

只要打开文件我们就可以看到,似乎很多点位的误差是有点太大了。

image.png

所以我们进一步往前推我们dump的结点。这次我们试试这两个add。

image.png

我们运行程序(为我上面机智的改了代码加了参数鼓掌!)

python main_cpu.py --output_nodes 'generator/InstanceNorm_3/instancenorm/add_1:0,generator/InstanceNorm/instancenorm/add_1:0'

python main_mlu.py --output_nodes 'generator/InstanceNorm_3/instancenorm/add_1:0,generator/InstanceNorm/instancenorm/add_1:0'

image.png

这里可以看到误差确实变小了。至少很难再用肉眼判断了。这种情况下我们就应该引入比较标准。比如余弦相似度或者MSE来比较。

image.png

配合这些结点在网络中的位置,我们可以看到确实出现了误差放大的现象。特别是Y输入误差较大。 Y是输入的风格图片。(在这里我的第一反应是去查了Y输入的数据预处理,可惜并没发现什么问题。)

顺着这个思路我们继续网下查,可以发现问题出在Mean算子。

image.png

image.png

这提示我们,会不会是因为计算数据类型,精度不足导致了数据溢出?

所以这里我们使用一种可能是最简单的精度改进方法,使用更高的数据类型来解决。FP32+int16一般就可以解决大多数问题。

要注意的是,对于一些网络对精度极其敏感的情况,需要特殊处理,需要更高的精度。对于一些带有循环结构的神经网络,由于循环结构本身会积累误差,所以往往需要更高精度。

要启用更高的精度需要修改两个地方:

首先引入环境变量

image.png

其次使用int16重新量化网络。下面是我已经运行过量化的结果。可以发现量化参数也扩大了。

image.png

再次基础上我们重新运行推理:

fp32+int16 的MLU运行结果:

image.png

到这里我们的调试就算是比较圆满的(虽然绕了个大弯子,确实一开始就应该试一试高精度)结束了。

最后我们再试一试fp32+int8的结果。可以看到精度不能满足要求。

image.png

版权所有 © 2024 寒武纪 Cambricon.com 备案/许可证号:京ICP备17003415号-1
关闭