| /* Copyright 2019 The TensorFlow Authors. All Rights Reserved. |
| |
| Licensed under the Apache License, Version 2.0 (the "License"); |
| you may not use this file except in compliance with the License. |
| You may obtain a copy of the License at |
| |
| http://www.apache.org/licenses/LICENSE-2.0 |
| |
| Unless required by applicable law or agreed to in writing, software |
| distributed under the License is distributed on an "AS IS" BASIS, |
| WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| See the License for the specific language governing permissions and |
| limitations under the License. |
| ==============================================================================*/ |
| |
| #include "tensorflow/compiler/mlir/lite/quantization/quantization_utils.h" |
| |
| #include "mlir/Dialect/QuantOps/FakeQuantSupport.h" // TF:local_config_mlir |
| #include "mlir/Dialect/QuantOps/QuantTypes.h" // TF:local_config_mlir |
| #include "mlir/Dialect/QuantOps/QuantizeUtils.h" // TF:local_config_mlir |
| #include "mlir/Dialect/QuantOps/UniformSupport.h" // TF:local_config_mlir |
| #include "mlir/IR/Attributes.h" // TF:local_config_mlir |
| #include "mlir/IR/StandardTypes.h" // TF:local_config_mlir |
| #include "tensorflow/compiler/mlir/lite/utils/attribute_utils.h" |
| |
| namespace mlir { |
| namespace TFL { |
| |
| // Returns the quantized type for the |
| // input_type/min/max/storag_type_width/narrow_range. |
| static Type GetQuantizedType(Builder builder, Type input_type, double min, |
| double max, int storage_type_width, |
| bool narrow_range, bool is_signed) { |
| auto converter = |
| quant::ExpressedToUniformQuantizedConverter::forInputType(input_type); |
| |
| quant::UniformQuantizedType quantizedEleType = quant::fakeQuantAttrsToType( |
| builder.getUnknownLoc(), storage_type_width, min, max, narrow_range, |
| converter.expressedType, is_signed); |
| return converter.convert(quantizedEleType); |
| } |
| |
| TypeAttr GetQuantizedTypeAttr(Builder builder, Type input_type, FloatAttr min, |
| FloatAttr max, Type storage_type, |
| bool narrow_range, bool is_signed) { |
| int storage_type_width = storage_type.cast<IntegerType>().getWidth(); |
| Type final_type = GetQuantizedType( |
| builder, input_type, min.getValueAsDouble(), max.getValueAsDouble(), |
| storage_type_width, narrow_range, is_signed); |
| return builder.getTypeAttr(final_type); |
| } |
| |
| TypeAttr GetQuantizedTypeAttr(Builder builder, Type input_type, Attribute min, |
| Attribute max, IntegerAttr num_bits, |
| BoolAttr narrow_range) { |
| FloatAttr min_value = GetSingleElementAsFloatOrSelf(min); |
| FloatAttr max_value = GetSingleElementAsFloatOrSelf(max); |
| if (!min_value || !max_value) return {}; |
| return GetQuantizedTypeAttr(builder, input_type, min_value, max_value, |
| builder.getIntegerType(num_bits.getInt()), |
| narrow_range.getValue(), /*is_signed=*/false); |
| } |
| |
| TypeAttr CastQuantizedTypeAttrFromExpressedType(Builder builder, |
| TypeAttr source, Type target) { |
| if (!source || !source.getValue().isa<TensorType>()) return {}; |
| auto ele_type = source.getValue().cast<TensorType>().getElementType(); |
| if (auto quantized_type = ele_type.dyn_cast<quant::QuantizedType>()) { |
| Type final_type = quantized_type.castFromExpressedType(target); |
| if (final_type) return builder.getTypeAttr(final_type); |
| } |
| return {}; |
| } |
| |
| Type GetUniformQuantizedTypeForElementsAttr(ElementsAttr attr, |
| unsigned storage_type_width, |
| bool is_signed, bool narrow_range) { |
| Builder builder(attr.getContext()); |
| double min = std::numeric_limits<double>::max(); |
| double max = std::numeric_limits<double>::min(); |
| if (auto fp = attr.dyn_cast<DenseFPElementsAttr>()) { |
| for (auto it = fp.begin(), e = fp.end(); it != e; ++it) { |
| double ele_value = FloatAttr::getValueAsDouble(*it); |
| min = std::min(min, ele_value); |
| max = std::max(max, ele_value); |
| } |
| // The range must straddle zero. |
| if (min > 0.0 || max < 0.0) return {}; |
| auto type = GetQuantizedType(builder, attr.getType(), min, max, |
| storage_type_width, narrow_range, is_signed); |
| if (auto ele_type = type.dyn_cast_or_null<TensorType>()) |
| return ele_type.getElementType(); |
| } |
| |
| // The range from SplatElementAttr and other element attribute types couldn't |
| // straddle zero, so the quantization parameters couldn't be derived from its |
| // range. |
| return {}; |
| } |
| |
| quant::QuantizedType GetUniformQuantizedTypeForBias( |
| const std::vector<quant::QuantizedType>& op_types) { |
| if (op_types.empty()) return {}; |
| |
| double scale = 1.0; |
| for (unsigned i = 0, e = op_types.size(); i != e; ++i) { |
| auto qtype = op_types[i].dyn_cast_or_null<quant::UniformQuantizedType>(); |
| if (!qtype) return {}; |
| scale *= qtype.getScale(); |
| } |
| auto type = op_types.back().cast<quant::UniformQuantizedType>(); |
| Builder builder(type.getContext()); |
| // TODO(fengliuai): make the bit width configurable. |
| IntegerType storageType = builder.getIntegerType(32); |
| return quant::UniformQuantizedType::getChecked( |
| /*flags=*/true, storageType, type.getExpressedType(), scale, |
| /*zeroPoint=*/0, |
| quant::QuantizedType::getDefaultMininumForInteger(/*isSigned=*/true, 32), |
| quant::QuantizedType::getDefaultMaxinumForInteger(/*isSigned=*/true, 32), |
| builder.getUnknownLoc()); |
| } |
| |
| ElementsAttr Quantize(Attribute real_value, Type tensor_type) { |
| if (auto q_type = |
| quant::QuantizedType::getQuantizedElementType(tensor_type)) { |
| Type converted_type; |
| return quant::quantizeAttr(real_value, q_type, converted_type) |
| .cast<ElementsAttr>(); |
| } |
| return {}; |
| } |
| |
| } // namespace TFL |
| } // namespace mlir |