TensorFlow:logits和标签必须具有相同的第一维。

42

我是tensorflow的初学者,我想使用自己的数据(40x40大小的图像)调整MNIST教程https://www.tensorflow.org/tutorials/layers。这是我的模型函数:

def cnn_model_fn(features, labels, mode):
        # Input Layer
        input_layer = tf.reshape(features, [-1, 40, 40, 1])

        # Convolutional Layer #1
        conv1 = tf.layers.conv2d(
                inputs=input_layer,
                filters=32,
                kernel_size=[5, 5],
                #  To specify that the output tensor should have the same width and height values as the input tensor
                # value can be "same" ou "valid"
                padding="same",
                activation=tf.nn.relu)

        # Pooling Layer #1
        pool1 = tf.layers.max_pooling2d(inputs=conv1, pool_size=[2, 2], strides=2)

        # Convolutional Layer #2 and Pooling Layer #2
        conv2 = tf.layers.conv2d(
                inputs=pool1,
                filters=64,
                kernel_size=[5, 5],
                padding="same",
                activation=tf.nn.relu)
        pool2 = tf.layers.max_pooling2d(inputs=conv2, pool_size=[2, 2], strides=2)

        # Dense Layer
        pool2_flat = tf.reshape(pool2, [-1, 10 * 10 * 64])
        dense = tf.layers.dense(inputs=pool2_flat, units=1024, activation=tf.nn.relu)
        dropout = tf.layers.dropout(
                inputs=dense, rate=0.4, training=mode == tf.estimator.ModeKeys.TRAIN)

        # Logits Layer
        logits = tf.layers.dense(inputs=dropout, units=2)

        predictions = {
            # Generate predictions (for PREDICT and EVAL mode)
            "classes":       tf.argmax(input=logits, axis=1),
            # Add `softmax_tensor` to the graph. It is used for PREDICT and by the
            # `logging_hook`.
            "probabilities": tf.nn.softmax(logits, name="softmax_tensor")
        }

        if mode == tf.estimator.ModeKeys.PREDICT:
            return tf.estimator.EstimatorSpec(mode=mode, predictions=predictions)

        # Calculate Loss (for both TRAIN and EVAL modes)
        loss = tf.losses.sparse_softmax_cross_entropy(labels=labels, logits=logits)

        # Configure the Training Op (for TRAIN mode)
        if mode == tf.estimator.ModeKeys.TRAIN:
            optimizer = tf.train.GradientDescentOptimizer(learning_rate=0.001)
            train_op = optimizer.minimize(
                    loss=loss,
                    global_step=tf.train.get_global_step())
            return tf.estimator.EstimatorSpec(mode=mode, loss=loss, train_op=train_op)

        # Add evaluation metrics (for EVAL mode)
        eval_metric_ops = {
            "accuracy": tf.metrics.accuracy(
                    labels=labels, predictions=predictions["classes"])}
        return tf.estimator.EstimatorSpec(
                mode=mode, loss=loss, eval_metric_ops=eval_metric_ops)
我在标签和逻辑回归之间遇到了一个形状大小错误:

InvalidArgumentError(有关详细信息,请参见上面的追溯):logits和labels必须具有相同的第一维,得到的logits形状为[3,2],标签形状为[1]

filenames_array是一个包含16个字符串的数组

["file1.png", "file2.png", "file3.png", ...]

并且 labels_array 是一个由16个整数组成的数组

[0,0,1,1,0,1,0,0,0,...]

主要功能是:

# Create the Estimator
mnist_classifier = tf.estimator.Estimator(model_fn=cnn_model_fn, model_dir="/tmp/test_convnet_model")

# Train the model
cust_train_input_fn = lambda: train_input_fn_custom(
        filenames_array=filenames, labels_array=labels, batch_size=1)

mnist_classifier.train(
        input_fn=cust_train_input_fn,
        steps=20000,
        hooks=[logging_hook])

我尝试重新调整logits的形状,但没有成功:

logits = tf.reshape(logits,[1, 2])

我需要你的帮助,谢谢


编辑

经过更多时间的搜索,在我的模型函数的第一行中:

input_layer = tf.reshape(features, [-1, 40, 40, 1])

“-1”代表批量大小的尺寸将动态计算,这里的值为“3”。这个“3”也出现在我的错误信息中:logits and labels must have the same first dimension, got logits shape [3,2] and labels shape [1]

如果我将这个值强制改为“1”,那么就会出现新的错误:

Input to reshape is a tensor with 4800 values, but the requested shape has 1600

可能是我的特征出了问题?


编辑2:

完整代码在此处:https://gist.github.com/geoffreyp/cc8e97aab1bff4d39e10001118c6322e


编辑3:

我已更新Gist。

logits = tf.layers.dense(inputs=dropout, units=1)

https://gist.github.com/geoffreyp/cc8e97aab1bff4d39e10001118c6322e

但是我并不完全理解您关于批次大小的答案,为什么批次大小可以是3,而我选择了批次大小为1?

如果我选择batch_size = 3,我会收到此错误: logits和labels必须具有相同的第一维,得到的logits形状为[9,1],标签形状为[3]

我尝试重新整形标签:

labels = tf.reshape(labels, [3, 1])

我更新了特征和标签的结构:


    filenames_train = [['blackcorner-data/1.png', 'blackcorner-data/2.png', 'blackcorner-data/3.png',
                   'blackcorner-data/4.png', 'blackcorner-data/n1.png'],
                   ['blackcorner-data/n2.png',
                   'blackcorner-data/n3.png', 'blackcorner-data/n4.png',
                   'blackcorner-data/11.png', 'blackcorner-data/21.png'],
                   ['blackcorner-data/31.png',
                   'blackcorner-data/41.png', 'blackcorner-data/n11.png', 'blackcorner-data/n21.png',
                   'blackcorner-data/n31.png']
                   ]

labels = [[0, 0, 0, 0, 1], [1, 1, 1, 0, 0], [0, 0, 1, 1, 1]]

但是没有成功...

7个回答

84

问题出在目标形状上,与选择适当的损失函数有关。你有两种选择:

1. 第一种可能性:如果你有一个一维整数编码目标,你可以使用 sparse_categorical_crossentropy 作为损失函数。


1. 如果您有1D整数编码目标,则可以使用`sparse_categorical_crossentropy`作为损失函数。
n_class = 3
n_features = 100
n_sample = 1000

X = np.random.randint(0,10, (n_sample,n_features))
y = np.random.randint(0,n_class, n_sample)

inp = Input((n_features,))
x = Dense(128, activation='relu')(inp)
out = Dense(n_class, activation='softmax')(x)

model = Model(inp, out)
model.compile(loss='sparse_categorical_crossentropy',optimizer='adam',metrics=['accuracy'])
history = model.fit(X, y, epochs=3)

2. 可能性: 如果您已经对目标进行了独热编码以获得2D形状(n_samples,n_class),则可以使用categorical_crossentropy


说明:该段文字介绍了在机器学习中使用交叉熵损失函数的一种情况,即当目标数据已被独热编码为二维数组时。
n_class = 3
n_features = 100
n_sample = 1000

X = np.random.randint(0,10, (n_sample,n_features))
y = pd.get_dummies(np.random.randint(0,n_class, n_sample)).values

inp = Input((n_features,))
x = Dense(128, activation='relu')(inp)
out = Dense(n_class, activation='softmax')(x)

model = Model(inp, out)
model.compile(loss='categorical_crossentropy',optimizer='adam',metrics=['accuracy'])
history = model.fit(X, y, epochs=3)

52

我将其从sparse_categorical_crossentropy更改为categorical_crossentropy来解决问题,现在正常运行。


3
这帮了我。我也不知道为什么。 - T3rm1
4
这里是问题的解释:https://dev59.com/h1UM5IYBdhLWcg3wOeFY#62286888TensorFlow中的logits和labels必须具有相同的第一个维度,否则会引发"ValueError: logits and labels must have the same first dimension"错误。这通常意味着您的训练数据和标签数目不匹配。 - Marco Cerliani
这也解决了我的问题。现在,我需要理解为什么。 - Raj Salla

12

我在第一次使用tensorflow时就遇到了这个问题,后来我发现我的问题是忘记在上传训练数据和验证数据的函数中添加属性class_mode='sparse' / class_mode='binary'

所以请尝试注意class_mode选项。

image_gen_val = ImageDataGenerator(rescale=1./255)
val_data_gen = image_gen_val.flow_from_directory(batch_size=batch_size,
                                                 directory=val_dir,
                                                 target_size=(IMG_SHAPE, IMG_SHAPE),
                                                 class_mode='sparse')

7

我想提供一个更具有说明性的答案,介绍不同标签表示的推荐方法以及一些内部运作的见解。

首先,我们有3个数据点和5种可能的标签(从0开始计数)。以下是您在机器学习问题中遇到的不同类型的标签。

enter image description here

代码 (tensorflow2)

假设我们有以下虚拟数据和模型:

import tensorflow as tf
import numpy as np

ohe_labels = np.array([[0, 0, 0, 1, 0], [0, 1, 0, 0, 0], [0, 0, 0, 0, 1]])
labels = np.argmax(ohe_labels, axis=-1)

x = np.random.normal(size=(3, 10))

model = tf.keras.models.Sequential(
    [
        tf.keras.layers.Dense(20, 'relu', input_shape=(10,)),
        tf.keras.layers.Dense(5, 'softmax')
    ]
)

# This works!

model.compile(loss='categorical_crossentropy', optimizer='adam')
model.fit(x, ohe_labels)

# This also works!
model.compile(loss='sparse_categorical_crossentropy', optimizer='adam')
model.fit(x, labels)

# This does NOT (Different error - ValueError: Shapes ... are incompatible)!
model.compile(loss='categorical_crossentropy', optimizer='adam')
model.fit(x, labels)

# This does NOT (Gives the above error)!
model.compile(loss='sparse_categorical_crossentropy', optimizer='adam')
model.fit(x, ohe_labels)

什么时候会触发这个错误?

这个错误会在特定情况下被触发。我来举一个每个输入只有一个标签的问题作为例子(对于多标签设置也适用,但需要添加更多细节)。首先(批次)维度需要匹配,但是一旦labels重新调整为1D向量,如果logits的第一个维度和labels的长度不匹配,就会触发此错误。


1
这是一个很好的解释。 - Golden Lion

3

我曾遇到类似问题,并发现在CNN层和Dense层之间缺少了Flatten层。添加Flatten层后,问题得到了解决。


这里有一些非常好的和详细的解释,但是这就是对我有用的解决方案。谢谢! - Gustavo Seabra

1
你的logits形状看起来正确,批次大小为3,输出层大小为2,这是你定义的输出层。你的标签也应该是[3,2]的形状。每个批次有2个[1,0]或[0,1]。此外,请注意,当您具有布尔分类输出时,输出/ logits层上不应有2个神经元。您可以只输出一个取值为0或1的单个值,您可能可以看到[1,0]和[0,1]的2个输出是冗余的,并且可以表示为简单的[0|1]值。这样做通常可以获得更好的结果。因此,你的logits应该最终变成[3,1],而你的标签应该是一个包含3个值的数组,分别对应你的批次中的每个样本。

1
我遇到了类似的问题,结果发现一个池化层没有正确地重新调整形状。在我的情况下,我错误地使用了tf.reshape(pool, shape=[-1, 64 * 7 * 7])而不是tf.reshape(pool, shape=[-1, 64 * 14 * 14]),这导致了一个关于logits和标签的类似错误消息。更改因素,例如tf.reshape(pool, shape=[-1, 64 * 12 * 12])会导致完全不同、不那么误导的错误消息。

也许这也是这里的情况。我建议检查节点的形状,以防万一。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接