svdf_settings.py 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256
  1. # SPDX-FileCopyrightText: Copyright 2010-2023 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. class SVDFSettings(TestSettings):
  20. def __init__(self,
  21. dataset,
  22. testtype,
  23. regenerate_weights,
  24. regenerate_input,
  25. regenerate_biases,
  26. schema_file,
  27. batches=2,
  28. number_inputs=2,
  29. rank=8,
  30. memory_size=10,
  31. randmin=TestSettings.INT8_MIN,
  32. randmax=TestSettings.INT8_MAX,
  33. input_size=3,
  34. number_units=4,
  35. generate_bias=True,
  36. int8_time_weights=False,
  37. input_scale=0.1,
  38. input_zp=0,
  39. w_1_scale=0.005,
  40. w_1_zp=0,
  41. w_2_scale=0.005,
  42. w_2_zp=0,
  43. bias_scale=0.00002,
  44. bias_zp=0,
  45. state_scale=0.005,
  46. state_zp=0,
  47. output_scale=0.1,
  48. output_zp=0,
  49. interpreter="tensorflow"):
  50. super().__init__(dataset,
  51. testtype,
  52. regenerate_weights,
  53. regenerate_input,
  54. regenerate_biases,
  55. schema_file,
  56. 1,
  57. 1,
  58. 1,
  59. 1,
  60. 1,
  61. 1,
  62. 1,
  63. 1,
  64. False,
  65. randmin,
  66. randmax,
  67. generate_bias=generate_bias,
  68. interpreter=interpreter)
  69. self.batches = batches
  70. self.number_units = number_units
  71. self.input_size = input_size
  72. self.memory_size = memory_size
  73. self.rank = rank
  74. self.number_filters = self.number_units * self.rank
  75. self.time_table_file = self.pregenerated_data_dir + self.testdataset + '/' + 'time_data.txt'
  76. self.number_inputs = number_inputs
  77. self.input_sequence_length = self.number_inputs * self.input_size * self.batches
  78. self.int8_time_weights = int8_time_weights
  79. if self.int8_time_weights:
  80. self.json_template = "TestCases/Common/svdf_s8_weights_template.json"
  81. self.in_activation_max = TestSettings.INT8_MAX
  82. self.in_activation_min = TestSettings.INT8_MIN
  83. else:
  84. self.json_template = "TestCases/Common/svdf_template.json"
  85. self.in_activation_max = TestSettings.INT16_MAX
  86. self.in_activation_min = TestSettings.INT16_MIN
  87. self.json_replacements = {
  88. "memory_sizeXnumber_filters": self.memory_size * self.number_filters,
  89. "batches": self.batches,
  90. "input_size": self.input_size,
  91. "number_filters": self.number_filters,
  92. "memory_size": self.memory_size,
  93. "number_units": self.number_units,
  94. "rank_value": self.rank,
  95. "input_scale": input_scale,
  96. "input_zp": input_zp,
  97. "w_1_scale": w_1_scale,
  98. "w_1_zp": w_1_zp,
  99. "w_2_scale": w_2_scale,
  100. "w_2_zp": w_2_zp,
  101. "bias_scale": bias_scale,
  102. "bias_zp": bias_zp,
  103. "state_scale": state_scale,
  104. "state_zp": state_zp,
  105. "output_scale": output_scale,
  106. "output_zp": output_zp
  107. }
  108. def calc_multipliers_and_shifts(self, input_scale, weights_1_scale, weights_2_scale, state_scale, output_scale):
  109. effective_scale_1 = weights_1_scale * input_scale / state_scale
  110. effective_scale_2 = state_scale * weights_2_scale / output_scale
  111. (self.multiplier_in, self.shift_1) = self.quantize_scale(effective_scale_1)
  112. (self.multiplier_out, self.shift_2) = self.quantize_scale(effective_scale_2)
  113. def write_c_config_header(self) -> None:
  114. super().write_c_config_header(write_common_parameters=False)
  115. filename = self.config_data
  116. filepath = self.headers_dir + filename
  117. prefix = self.testdataset.upper()
  118. with open(filepath, "a") as f:
  119. f.write("#define {}_MULTIPLIER_IN {}\n".format(prefix, self.multiplier_in))
  120. f.write("#define {}_MULTIPLIER_OUT {}\n".format(prefix, self.multiplier_out))
  121. f.write("#define {}_SHIFT_1 {}\n".format(prefix, self.shift_1))
  122. f.write("#define {}_SHIFT_2 {}\n".format(prefix, self.shift_2))
  123. f.write("#define {}_IN_ACTIVATION_MIN {}\n".format(prefix, self.in_activation_min))
  124. f.write("#define {}_IN_ACTIVATION_MAX {}\n".format(prefix, self.in_activation_max))
  125. f.write("#define {}_RANK {}\n".format(prefix, self.rank))
  126. f.write("#define {}_FEATURE_BATCHES {}\n".format(prefix, self.number_filters))
  127. f.write("#define {}_TIME_BATCHES {}\n".format(prefix, self.memory_size))
  128. f.write("#define {}_INPUT_SIZE {}\n".format(prefix, self.input_size))
  129. f.write("#define {}_DST_SIZE {}\n".format(prefix, self.number_units * self.batches))
  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 {}_INPUT_BATCHES {}\n".format(prefix, self.batches))
  133. f.write("#define {}_INPUT_OFFSET {}\n".format(prefix, self.input_zero_point))
  134. f.write("#define {}_OUTPUT_OFFSET {}\n".format(prefix, self.output_zero_point))
  135. def generate_data(self, input_data=None, weights=None, biases=None, time_data=None, state_data=None) -> None:
  136. if self.int8_time_weights:
  137. if not self.use_tflite_micro_interpreter:
  138. print("Warning: interpreter tflite_micro must be used for SVDF int8. Skipping generating headers.")
  139. return
  140. # TODO: Make this compatible with newer versions than 2.10.
  141. if float(('.'.join(tf.__version__.split('.')[:2]))) > 2.10:
  142. print("Warning: tensorflow version > 2.10 not supported for SVDF unit tests. Skipping generating headers")
  143. return
  144. if input_data is not None:
  145. input_data = tf.reshape(input_data, [self.input_sequence_length])
  146. else:
  147. input_data = self.get_randomized_data([self.input_sequence_length],
  148. self.inputs_table_file,
  149. regenerate=self.regenerate_new_input)
  150. self.generate_c_array("input_sequence", input_data)
  151. if weights is not None:
  152. weights_feature_data = tf.reshape(weights, [self.number_filters, self.input_size])
  153. else:
  154. weights_feature_data = self.get_randomized_data([self.number_filters, self.input_size],
  155. self.kernel_table_file,
  156. regenerate=self.regenerate_new_weights)
  157. if time_data is not None:
  158. weights_time_data = tf.reshape(time_data, [self.number_filters, self.memory_size])
  159. else:
  160. weights_time_data = self.get_randomized_data([self.number_filters, self.memory_size],
  161. self.time_table_file,
  162. regenerate=self.regenerate_new_weights)
  163. if not self.generate_bias:
  164. biases = [0] * self.number_units
  165. if biases is not None:
  166. biases = tf.reshape(biases, [self.number_units])
  167. else:
  168. biases = self.get_randomized_data([self.number_units],
  169. self.bias_table_file,
  170. regenerate=self.regenerate_new_weights)
  171. # Generate tflite model
  172. generated_json = self.generate_json_from_template(weights_feature_data,
  173. weights_time_data,
  174. biases,
  175. self.int8_time_weights)
  176. self.flatc_generate_tflite(generated_json, self.schema_file)
  177. # Run TFL interpreter
  178. interpreter = self.Interpreter(model_path=str(self.model_path_tflite),
  179. experimental_op_resolver_type=self.OpResolverType.BUILTIN_REF)
  180. interpreter.allocate_tensors()
  181. # Read back scales and zero points from tflite model
  182. all_layers_details = interpreter.get_tensor_details()
  183. input_layer = all_layers_details[0]
  184. weights_1_layer = all_layers_details[1]
  185. weights_2_layer = all_layers_details[2]
  186. bias_layer = all_layers_details[3]
  187. state_layer = all_layers_details[4]
  188. output_layer = all_layers_details[5]
  189. (input_scale, self.input_zero_point) = self.get_scale_and_zp(input_layer)
  190. (weights_1_scale, zero_point) = self.get_scale_and_zp(weights_1_layer)
  191. (weights_2_scale, zero_point) = self.get_scale_and_zp(weights_2_layer)
  192. (bias_scale, zero_point) = self.get_scale_and_zp(bias_layer)
  193. (state_scale, zero_point) = self.get_scale_and_zp(state_layer)
  194. (output_scale, self.output_zero_point) = self.get_scale_and_zp(output_layer)
  195. self.calc_multipliers_and_shifts(input_scale, weights_1_scale, weights_2_scale, state_scale, output_scale)
  196. # Generate unit test C headers
  197. self.generate_c_array("weights_feature", interpreter.get_tensor(weights_1_layer['index']))
  198. self.generate_c_array(self.bias_data_file_prefix, interpreter.get_tensor(bias_layer['index']), "int32_t")
  199. if self.int8_time_weights:
  200. self.generate_c_array("weights_time", interpreter.get_tensor(weights_2_layer['index']), datatype='int8_t')
  201. self.generate_c_array("state", interpreter.get_tensor(state_layer['index']), "int8_t")
  202. else:
  203. self.generate_c_array("weights_time", interpreter.get_tensor(weights_2_layer['index']), datatype='int16_t')
  204. self.generate_c_array("state", interpreter.get_tensor(state_layer['index']), "int16_t")
  205. if self.use_tflite_micro_interpreter:
  206. interpreter = self.tflite_micro.runtime.Interpreter.from_file(model_path=str(self.model_path_tflite))
  207. # Generate reference output
  208. svdf_ref = None
  209. for i in range(self.number_inputs):
  210. start = i * self.input_size * self.batches
  211. end = i * self.input_size * self.batches + self.input_size * self.batches
  212. input_sequence = input_data[start:end]
  213. input_sequence = tf.reshape(input_sequence, [self.batches, self.input_size])
  214. if self.use_tflite_micro_interpreter:
  215. interpreter.set_input(tf.cast(input_sequence, tf.int8), input_layer["index"])
  216. else:
  217. interpreter.set_tensor(input_layer["index"], tf.cast(input_sequence, tf.int8))
  218. interpreter.invoke()
  219. if self.use_tflite_micro_interpreter:
  220. svdf_ref = interpreter.get_output(0)
  221. else:
  222. svdf_ref = interpreter.get_tensor(output_layer["index"])
  223. self.generate_c_array(self.output_data_file_prefix, svdf_ref)
  224. self.write_c_config_header()
  225. self.write_c_header_wrapper()
  226. def get_scale_and_zp(self, layer):
  227. return (layer['quantization_parameters']['scales'][0], layer['quantization_parameters']['zero_points'][0])