hankcs/HanLP · error · ValueError

The last dimension of the input shape must be equal to outpu

Error message

The last dimension of the input shape must be equal to output shape. Use a linear layer if needed.

What it means

The TF CRF layer requires input features whose last dimension equals output_dim (the number of tags), because the transitions matrix and emission scores are defined over exactly that axis. Unlike a wrapped CRF loss module, this layer does not project inputs; if your encoder outputs a different hidden size, you must add a projection yourself, hence the message 'Use a linear layer if needed'.

Source

Thrown at hanlp/layers/crf/crf_layer_tf.py:73

    def get_config(self):
        config = {
            'output_dim': self.output_dim,
            'supports_masking': self.supports_masking,
            'transitions': tf.keras.backend.eval(self.transitions)
        }
        base_config = super(CRF, self).get_config()
        return dict(list(base_config.items()) + list(config.items()))

    def build(self, input_shape):
        assert len(input_shape) == 3
        f_shape = tf.TensorShape(input_shape)
        input_spec = tf.keras.layers.InputSpec(min_ndim=3, axes={-1: f_shape[-1]})

        if f_shape[-1] is None:
            raise ValueError('The last dimension of the inputs to `CRF` '
                             'should be defined. Found `None`.')
        if f_shape[-1] != self.output_dim:
            raise ValueError('The last dimension of the input shape must be equal to output'
                             ' shape. Use a linear layer if needed.')
        self.input_spec = input_spec
        self.transitions = self.add_weight(name='transitions',
                                           shape=[self.output_dim, self.output_dim],
                                           initializer='glorot_uniform',
                                           trainable=True)
        self.built = True

    def compute_mask(self, inputs, mask=None):
        # Just pass the received mask from previous layer, to the next layer or
        # manipulate it if this layer changes the shape of the input
        return mask

    # pylint: disable=arguments-differ
    def call(self, inputs, sequence_lengths=None, mask=None, training=None, **kwargs):
        sequences = tf.convert_to_tensor(inputs, dtype=self.dtype)
        if sequence_lengths is not None:
            assert len(sequence_lengths.shape) == 2

View on GitHub (pinned to ddb1299bdd)

Solutions

  1. Add tf.keras.layers.Dense(num_tags) immediately before the CRF layer and set CRF's output_dim to num_tags
  2. Or set the CRF output_dim equal to the encoder output dim only if that genuinely equals the tag count

Example fix

# before
x = encoder(inputs)            # (B, T, 768)
out = CRF(5)(x)                # error: 768 != 5
# after
x = tf.keras.layers.Dense(5)(x)  # (B, T, 5)
out = CRF(5)(x)
Defensive patterns

Strategy: validation

Validate before calling

assert inputs.shape[-1] == num_tags, f'{inputs.shape[-1]} != {num_tags}; add Dense({num_tags})'

Type guard

def dims_match(x: 'tf.Tensor', crf) -> bool:
    return int(x.shape[-1]) == crf.output_dim

Prevention

When it happens

Trigger: Instantiating CRF(output_dim=num_tags) and feeding encoder outputs of hidden_size != num_tags (e.g. 768-dim BERT features straight into a CRF with 5 tags).

Common situations: Forgetting a Dense(num_tags) projection between transformer/LSTM encoder and the CRF layer; copying keras-contrib examples where the previous layer happened to match tag count.

Related errors


AI-assisted analysis of hankcs/HanLP@ddb1299bdd (2026-08-27). Data as JSON: /api/errors/483023b2d24d76a8. Report an issue: GitHub.