def _quantize_wp_field(wp, nbits, qm, shape, axis=0, **kwargs):
    """
    Quantize WeightParam field in Neural Network Protobuf

    wp: MLModel.NeuralNetwork.WeightParam
        WeightParam field
    nbits: int
        Number of bits to be quantized
    qm: str
        Quantization mode
    shape: tuple
        Tensor shape held by wp
    axis: int
        Axis over which quantization is performed on, can be either 0 or 1
    lut_function: (``callable function``)
        Python callable representing a LUT table function
    """

    # De-quantization
    if qm == _ct.models._QUANTIZATION_MODE_DEQUANTIZE:
        return _dequantize_wp(wp, shape, axis)

    # If the float32 field is empty do nothing and return
    if len(wp.floatValue) == 0:
        return

    # Half precision (16-bit) quantization
    if nbits == 16:
        return _wp_to_fp16wp(wp)

    if nbits > 8:
        raise Exception("Only 8-bit and lower quantization is supported")

    if qm not in _ct.models._SUPPORTED_QUANTIZATION_MODES:
        raise Exception("Quantization mode {} not supported".format(qm))

    # axis parameter check
    if axis == 1 and len(shape) != 4:
        raise Exception(
            "Quantization on second axis is only supported " "for rank-4 weight blob."
        )
    if axis != 0 and axis != 1:
        raise Exception(
            "Invalid quantization axis {} passed in. Allowed"
            "values are 0 (first axis) and 1 (second axis)".format(axis)
        )

    # WeightParam size check - non-linear quantizations are applied on layer level
    num_channels = (
        shape[axis]
        if qm
        in [
            _ct.models._QUANTIZATION_MODE_LINEAR_QUANTIZATION,
            _ct.models._QUANTIZATION_MODE_LINEAR_SYMMETRIC,
        ]
        else 1
    )
    if len(wp.floatValue) % num_channels:
        raise Exception(
            "Number of quantization channels does not divide evenly into weights"
        )

    qparams = wp.quantization
    qparams.numberOfBits = nbits

    weights = _np.array(wp.floatValue).reshape(shape)
    scale, bias, lut, uint8_weights = _quantize_wp(weights, nbits, qm, axis, **kwargs)
    uint8_weights = uint8_weights.flatten()
    if qm in [
        _ct.models._QUANTIZATION_MODE_LINEAR_QUANTIZATION,
        _ct.models._QUANTIZATION_MODE_LINEAR_SYMMETRIC,
    ]:
        qparams.linearQuantization.scale.extend(scale)
        qparams.linearQuantization.bias.extend(bias)
    else:
        qparams.lookupTableQuantization.floatValue.extend(lut)

    wp.rawValue = bytes()
    if nbits == 8:
        wp.rawValue += uint8_weights.tobytes()
    else:
        wp.rawValue += _convert_array_to_nbit_quantized_bytes(
            uint8_weights, nbits
        ).tobytes()
    del wp.floatValue[:]

    Quantize WeightParam field in Neural Network Protobuf

    wp: MLModel.NeuralNetwork.WeightParam
        WeightParam field
    nbits: int
        Number of bits to be quantized
    qm: str
        Quantization mode
    shape: tuple
        Tensor shape held by wp
    axis: int
        Axis over which quantization is performed on, can be either 0 or 1
    lut_function: (``callable function``)
        Python callable representing a LUT table function
    
coremltools/models/neural_network/quantization_utils.py