hankcs/HanLP · error · ValueError

The last dimension of the inputs to `CRF` should be defined.

Error message

The last dimension of the inputs to `CRF` should be defined. Found `None`.

What it means

Thrown by the TensorFlow CRF layer's build() when the input tensor's last dimension is undefined (None). Keras needs a concrete feature dimension to create the transitions weight matrix of shape (output_dim, output_dim), so an undefined channel count cannot be built. This is the standard check carried over from keras-contrib's CRF implementation.

Source

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

        self.supports_masking = False
        sequence_lengths = None

    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):

View on GitHub (pinned to ddb1299bdd)

Solutions

  1. Define a fixed last dimension in the input, e.g. Input(shape=(None, hidden_size))
  2. Insert a Dense(hidden_size) layer before CRF so the last dim is concrete
  3. If calling build manually, pass an input_shape tuple whose final element is an int

Example fix

# before
inputs = tf.keras.layers.Input(shape=(None, None))
crf = CRFLayer(units)  # build fails
# after
inputs = tf.keras.layers.Input(shape=(None, hidden_size))
crf = CRFLayer(hidden_size)
Defensive patterns

Strategy: validation

Validate before calling

assert inputs.shape[-1] is not None and isinstance(inputs.shape[-1], int)

Type guard

def has_defined_last_dim(shape) -> bool:
    return len(shape) == 3 and isinstance(shape[-1], (int,)) and shape[-1] > 0

Prevention

When it happens

Trigger: Feeding inputs with dynamic/undefined last dimension, e.g. an Input(shape=(None, None)), a layer upstream that produces unknown channel size, or input_shape passed without a defined final axis when calling the layer directly.

Common situations: Using tf.keras Input with variable feature dimension; building a model where the embedding/feature dimension is inferred as None; upgrading Keras versions where shape inference behavior changed.

Related errors


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