TensorFlow加载Vgg预训练模型时的数据类型处理与保存注意事项
本文详解在TensorFlow中加载Vgg预训练模型时遇到的uint8与float数据类型不匹配报错。通过对比tf.image.decode_jpeg默认行为,说明使用tf.to_float进行类型转换的必要性与安全性,并指出保存float格式图片时需转回uint8,否则会导致显示异常或API报错。
引言与问题概述
本文旨在解决一个关键Bug:Value passed to parameter 'input' has DataType uint8 not in list of allowed values: float16, bfloat16, float32, float64。我们将围绕图片加载与本地保存两个环节,详细解析该Bug及其衍生问题的解决方法。
图片加载时的数据类型处理
图片加载环节
首先,导致上述Bug的代码示例如下所示。
image_path = "data/test.jpg" # 本地的测试图片
image_raw = tf.gfile.GFile(image_path, 'rb').read()
# 一定要tf.float(),否则会报错
image_decoded = tf.image.decode_jpeg(image_raw)
# 扩展图片的维度,从三维变成四维,符合Vgg19的输入接口
image_expand_dim = tf.expand_dims(image_decoded, 0)
# 定义Vgg19模型
vgg19 = VGG19(data_path)
net = vgg19.feed_forward(image_expand_dim, 'vgg19')
print(net)
上述代码用于加载Vgg19预训练模型,并传入图片以获取所有层的特征图。具体的代码实现与原理讲解可参考我的另一篇博客:TensorFlow加载Vgg预训练模型。那么,为什么代码会出现Value passed to parameter 'input' has DataType uint8 not in list of allowed values: float16, bfloat16, float32, float64这一错误呢?
这句英文报错的含义是:传递的参数类型是uint8,但模型接受的参数类型必须是float系列(如float16、bfloat16、float32、float64)。因此,根本原因在于传入值的数据类型不匹配。解决这个Bug的方法非常简单。
image_path = "data/test.jpg" # 本地的测试图片
image_raw = tf.gfile.GFile(image_path, 'rb').read()
# 一定要tf.float(),否则会报错
image_decoded = tf.to_float(tf.image.decode_jpeg(image_raw))
# 扩展图片的维度,从三维变成四维,符合Vgg19的输入接口
image_expand_dim = tf.expand_dims(image_decoded, 0)
# 定义Vgg19模型
vgg19 = VGG19(data_path)
net = vgg19.feed_forward(image_expand_dim, 'vgg19')
print(net)
对比这两个代码块,唯一的区别在于:在输出image_decoded结果前,增加了一个tf.to_float()调用,将其转换为float类型。
在TensorFlow API中,tf.image.decode_jpeg()默认读取的图片数据格式为uint8,而非float。uint8数据的范围在0到255之间,这正好符合图片像素值的范围(0-255)。然而,保存在本地的Vgg19预训练模型所期望的数据接口为float类型,这就导致了文章开头提到的Bug。
这里还需要补充一点:如果使用PIL库的方法来加载图片,则不会出现上述Bug。因为通过PIL读取的图片格式默认就是float类型,而非uint8,因此无需进行额外的类型转换。
许多同学可能会疑惑:如果强行改变原图片的数据格式,从uint8类型转换为float类型,是否会导致数据数值改变或出错?为此,我进行了如下实验:
image_path = "data/3.jpg"
image_raw = tf.gfile.GFile(image_path, 'rb').read()
image_unit8 = tf.image.decode_jpeg(image_raw)
image_float = tf.to_float(image_unit8)
with tf.Session() as sess:
image_unit8_, image_float_ = sess.run([image_unit8, image_float])
print("image_unit8_", image_unit8_)
print("image_float_ ", image_float_ )
代码运行结果如下:
image_unit8_
[180, 192, 204],
[183, 195, 207],
[186, 198, 210],
...,
[191, 205, 218],
[191, 205, 218],
[190, 204, 217]],
image_float_
[180., 192., 204.],
[183., 195., 207.],
[186., 198., 210.],
...,
[191., 205., 218.],
[191., 205., 218.],
[190., 204., 217.]],
从结果可以看出,数据数值本身没有任何变化,只是在数值后面多了一个小数点,仅改变了数据类型而未强制改变数值。因此,同学们无需对此过度担心。
图片保存至本地的格式转换
图片保存至本地环节
在加载图片时,为了适配保存在本地的预训练Vgg19模型,我们需要将读取的图片由uint8格式转换为float格式。那么,如果我们将已经转换为float格式的图片再保存回本地,该怎么做呢?
首先,我们根据上述逻辑读取图片,并将其转换为float格式。在将读取的图片再次保存到本地之前,我们先对转换格式后的图片进行可视化,代码如下:
import tensorflow as tf
from matplotlib import pyplot as plt
image_path = "data/boat.jpg"
image_raw = tf.gfile.GFile(image_path, 'rb').read()
image_decoded = tf.image.decode_jpeg(image_raw)
image_decoded = tf.to_float(image_decoded)
with tf.Session() as sess:
image_decoded_ = sess.run(image_decoded)
plt.imshow(image_decoded_)
plt.show()
生成的图片效果如下图所示:

左侧为原图,右侧为转换为float格式后的图片。可见,将图片转换为float格式虽然对数值影响不大,但若直接保存至本地,则会引发问题。
以上讨论旨在说明一点:在保存图片至本地之前,需要将其格式从float转回uint8,否则会造成一系列错误,如图片显示异常、API报错等。正确的保存代码如下:
save_path = "data/boat_copy.jpg"
image_uint = tf.cast(image_decoded, tf.uint8)
with tf.Session() as sess:
with open(save_path, 'wb') as img:
image_saved = sess.run(tf.image.encode_jpeg(image_uint))
img.write(image_saved)
其中最关键的一句话是tf.cast(image_decoded, tf.uint8)。
以上这篇关于TensorFlow加载Vgg预训练模型的注意事项就是小编分享给大家的全部内容了,希望能给大家提供参考,也希望大家多多支持本站。
本站声明
本文内容仅供参考,不代表本站观点。转载或引用请注明出处。如发现内容问题,请通过联系我们页面反馈。













