add_mul_settings.py 7.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161
  1. # SPDX-FileCopyrightText: Copyright 2010-2024 Arm Limited and/or its affiliates <open-source-office@arm.com>
  2. #
  3. # SPDX-License-Identifier: Apache-2.0
  4. #
  5. # Licensed under the Apache License, Version 2.0 (the License); you may
  6. # not use this file except in compliance with the License.
  7. # You may obtain a copy of the License at
  8. #
  9. # www.apache.org/licenses/LICENSE-2.0
  10. #
  11. # Unless required by applicable law or agreed to in writing, software
  12. # distributed under the License is distributed on an AS IS BASIS, WITHOUT
  13. # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14. # See the License for the specific language governing permissions and
  15. # limitations under the License.
  16. #
  17. from test_settings import TestSettings
  18. import tensorflow as tf
  19. import numpy as np
  20. import tf_keras as keras
  21. class AddMulSettings(TestSettings):
  22. def __init__(self,
  23. dataset,
  24. testtype,
  25. regenerate_weights,
  26. regenerate_input,
  27. regenerate_biases,
  28. schema_file,
  29. channels=1,
  30. x_in=4,
  31. y_in=4,
  32. decimal_input=6,
  33. randmin=TestSettings.INT8_MIN,
  34. randmax=TestSettings.INT8_MAX,
  35. out_activation_min=TestSettings.INT8_MIN,
  36. out_activation_max=TestSettings.INT8_MAX,
  37. int16xint8=False,
  38. interpreter="tensorflow"):
  39. super().__init__(dataset,
  40. testtype,
  41. regenerate_weights,
  42. regenerate_input,
  43. regenerate_biases,
  44. schema_file,
  45. in_ch=channels,
  46. out_ch=channels,
  47. x_in=x_in,
  48. y_in=y_in,
  49. w_x=1,
  50. w_y=1,
  51. stride_x=1,
  52. stride_y=1,
  53. pad=False,
  54. randmin=randmin,
  55. randmax=randmax,
  56. batches=1,
  57. generate_bias=False,
  58. relu6=False,
  59. out_activation_min=out_activation_min,
  60. out_activation_max=out_activation_max,
  61. int16xint8=int16xint8,
  62. interpreter=interpreter)
  63. self.x_input = self.x_output = x_in
  64. self.y_input = self.y_output = y_in
  65. self.decimal_input = decimal_input
  66. self.left_shift = 15 if self.is_int16xint8 else 20
  67. def generate_data(self, input_data1=None, input_data2=None) -> None:
  68. input_shape = (1, self.y_input, self.x_input, self.input_ch)
  69. input_data1 = self.get_randomized_data(list(input_shape),
  70. self.inputs_table_file,
  71. regenerate=self.regenerate_new_input,
  72. decimals=self.decimal_input)
  73. input_data2 = self.get_randomized_data(list(input_shape),
  74. self.kernel_table_file,
  75. regenerate=self.regenerate_new_weights,
  76. decimals=self.decimal_input)
  77. if self.is_int16xint8:
  78. inttype = "int16_t"
  79. inttype_tf = tf.int16
  80. else:
  81. inttype = "int8_t"
  82. inttype_tf = tf.int8
  83. # Create a one-layer functional Keras model as add/mul cannot use a sequntial Keras model.
  84. input1 = keras.layers.Input(shape=input_shape[1:])
  85. input2 = keras.layers.Input(shape=input_shape[1:])
  86. if self.test_type == 'add':
  87. layer = keras.layers.Add()([input1, input2])
  88. elif self.test_type == 'mul':
  89. layer = keras.layers.Multiply()([input1, input2])
  90. else:
  91. raise RuntimeError("Wrong test type")
  92. out = keras.layers.Lambda(function=lambda x: x)(layer)
  93. model = keras.models.Model(inputs=[input1, input2], outputs=out)
  94. interpreter = self.convert_and_interpret(model, inttype_tf)
  95. input_details = interpreter.get_input_details()
  96. interpreter.set_tensor(input_details[0]["index"], tf.cast(input_data1, inttype_tf))
  97. interpreter.set_tensor(input_details[1]["index"], tf.cast(input_data2, inttype_tf))
  98. # Calculate multipliers, shifts and offsets.
  99. (input1_scale, self.input1_zero_point) = input_details[0]['quantization']
  100. (input2_scale, self.input2_zero_point) = input_details[1]['quantization']
  101. self.input1_zero_point = -self.input1_zero_point
  102. self.input2_zero_point = -self.input2_zero_point
  103. double_max_input_scale = max(input1_scale, input2_scale) * 2
  104. (self.input1_mult, self.input1_shift) = self.quantize_scale(input1_scale / double_max_input_scale)
  105. (self.input2_mult, self.input2_shift) = self.quantize_scale(input2_scale / double_max_input_scale)
  106. if self.test_type == 'add':
  107. actual_output_scale = double_max_input_scale / ((1 << self.left_shift) * self.output_scale)
  108. elif self.test_type == 'mul':
  109. actual_output_scale = input1_scale * input2_scale / self.output_scale
  110. (self.output_mult, self.output_shift) = self.quantize_scale(actual_output_scale)
  111. # Generate reference.
  112. interpreter.invoke()
  113. output_details = interpreter.get_output_details()
  114. output_data = interpreter.get_tensor(output_details[0]["index"])
  115. self.generate_c_array("input1", input_data1, datatype=inttype)
  116. self.generate_c_array("input2", input_data2, datatype=inttype)
  117. self.generate_c_array(self.output_data_file_prefix,
  118. np.clip(output_data, self.out_activation_min, self.out_activation_max),
  119. datatype=inttype)
  120. self.write_c_config_header()
  121. self.write_c_header_wrapper()
  122. def write_c_config_header(self) -> None:
  123. super().write_c_config_header(write_common_parameters=False)
  124. filename = self.config_data
  125. filepath = self.headers_dir + filename
  126. prefix = self.testdataset.upper()
  127. with open(filepath, "a") as f:
  128. f.write("#define {}_DST_SIZE {}\n".format(prefix,
  129. self.batches * self.y_input * self.x_input * self.input_ch))
  130. f.write("#define {}_OUT_ACTIVATION_MIN {}\n".format(prefix, self.out_activation_min))
  131. f.write("#define {}_OUT_ACTIVATION_MAX {}\n".format(prefix, self.out_activation_max))
  132. f.write("#define {}_INPUT1_OFFSET {}\n".format(prefix, self.input1_zero_point))
  133. f.write("#define {}_INPUT2_OFFSET {}\n".format(prefix, self.input2_zero_point))
  134. f.write("#define {}_OUTPUT_MULT {}\n".format(prefix, self.output_mult))
  135. f.write("#define {}_OUTPUT_SHIFT {}\n".format(prefix, self.output_shift))
  136. f.write("#define {}_OUTPUT_OFFSET {}\n".format(prefix, self.output_zero_point))
  137. if self.test_type == 'add':
  138. f.write("#define {}_LEFT_SHIFT {}\n".format(prefix, self.left_shift))
  139. f.write("#define {}_INPUT1_SHIFT {}\n".format(prefix, self.input1_shift))
  140. f.write("#define {}_INPUT2_SHIFT {}\n".format(prefix, self.input2_shift))
  141. f.write("#define {}_INPUT1_MULT {}\n".format(prefix, self.input1_mult))
  142. f.write("#define {}_INPUT2_MULT {}\n".format(prefix, self.input2_mult))