我是tensorflow的新手,我试图用tensorflow. dataset提供一些数据。我使用城市景观数据集与8个不同的类。下面是我的代码:
import os
import cv2
import numpy as np
import tensorflow as tf
H = 256
W = 256
id2cat = np.array([0,0,0,0,0,0,0, 1,1,1,1, 2,2,2,2,2,2, 3,3,3,3, 4,4, 5, 6,6, 7,7,7,7,7,7,7,7,7])
def readImage(x):
x = cv2.imread(x, cv2.IMREAD_COLOR)
x = cv2.resize(x, (W, H))
x = x / 255.0
x = x.astype(np.float32)
return x
def readMask(path):
mask = cv2.imread(path, 0)
mask = cv2.resize(mask, (W, H))
mask = id2cat[mask]
return mask.astype(np.int32)
def preprocess(x, y):
def f(x, y):
image = readImage(x)
mask = readMask(y)
return image, mask
image, mask = tf.numpy_function(f, [x, y], [tf.float32, tf.int32])
mask = tf.one_hot(mask, 3, dtype=tf.int32)
image.set_shape([H, W, 3])
mask.set_shape([H, W, 3])
return image, mask
def tf_dataset(x, y, batch=8):
dataset = tf.data.Dataset.from_tensor_slices((x, y))
dataset = dataset.shuffle(buffer_size=5000)
dataset = dataset.map(preprocess)
dataset = dataset.batch(batch)
dataset = dataset.repeat()
dataset = dataset.prefetch(2)
return dataset
def loadCityscape():
trainPath = os.path.join(os.path.dirname(os.path.realpath(__file__)), 'datasets\Cityscape\train')
imagesPath = os.path.join(trainPath, 'images')
maskPath = os.path.join(trainPath, 'masks')
images = []
masks = []
print('Loading images and masks for Cityscape dataset...')
for image in os.listdir(imagesPath):
images.append(readImage(os.path.join(imagesPath, image)))
for mask in os.listdir(maskPath):
if 'label' in mask:
masks.append(readMask(os.path.join(maskPath, mask)))
print('Loaded {} imagesn'.format(len(images)))
return images, masks
images, masks = loadCityscape()
dataset = tf_dataset(images, masks, batch=8)
print(dataset)
最后一个print(dataset)显示:
<PrefetchDataset shapes: ((None, 256, 256, 3), (None, 256, 256, 3)), types: (tf.float32, tf.int32)>
为什么我得到(None, 256,256,3)而不是(8,256,256,3)?对于如何遍历这个数据集,我也有一些疑问。
谢谢你。
Tensorflow
是一个基于图形的数学框架,它为你抽象出你所面临的所有复杂的向量或材料操作,特别是在机器学习中。
开发人员认为,每次指定需要在模型中传递多少输入向量进行训练是不舒服的,所以他们决定为您抽象它。
只要输出与输入维度匹配(但任何内部操作都应该在维度上匹配!),如果您的模型使用单个或数千个样本,您将不会感兴趣。
因此,None
大小是一个占位符,用于可能改变的形状,这通常是输入的批处理大小。
我们需要一个占位符,因为(None, 2)
是(2,)
的不同形状,因为在第一种情况下,我们知道我们将面对二维。
即使在"编译"时不知道None
维度你的模型,只有在严格需要的时候才会被评估,换句话说,当你运行它的时候。通过这种方式,你的模型将很高兴地在64个批次大小上运行,就像128个样本一样。
对于其余的(非标量)Tensor
的行为就像一个普通的numpy数组:
tensor1 = tf.constant([ 0, 1, 2, 3]) # shape (4, )
tensor2 = tf.constant([ [0], [1], [2], [3]]) # shape (4, 1)
for x in tensor1:
print(x) # 0, 1, 2, 3
for x in tensor2:
print(x) # Tensor([0]), Tensor([1]), Tensor([2]), Tensor([3])
唯一的区别是它可以分配到任何支持的设备内存(CPU/Cuda GPU)。
遍历数据集就像以(通常)常量大小对其进行切片,该常量是您的批处理大小,它将填充空的None
维度。
这行代码将负责将数据集切片为"子张量";("子数组")组成的样本:
dataset = dataset.batch(N)
# iterating over it:
for batch in dataset: # I'm taking N samples here
...
你"runtime"shape将是(N, 256, 256, 3)
,但如果你将尝试从数据集中取一个元素,它仍然可以有None
的形状…这是因为我们不能保证,例如,数据集的维度完全可以被批大小整除,所以一些可变形状的跟踪样本仍然是可能的。您将很难摆脱None
维度,但在您的模型的一些自定义方法中,您可以实现这一点。
如果你仍然对张量感到不舒服,有tensor.numpy()
方法可以给你返回一个numpy数组,但代价是复制它(通常到你的CPU)。这不是在过程的每个步骤中都可用。
在tensorflow中有很多定义数据集的方法,我建议阅读他们是如何认为你应该建立一个输入管道的,因为如果你了解tensorflow在更高的抽象层次上对你的代码有多大的帮助,它会让你的生活更轻松。