fully_connected_settings.py 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287
  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 FullyConnectedSettings(TestSettings):
  22. def __init__(self,
  23. dataset,
  24. testtype,
  25. regenerate_weights,
  26. regenerate_input,
  27. regenerate_biases,
  28. schema_file,
  29. in_ch=1,
  30. out_ch=1,
  31. x_in=1,
  32. y_in=1,
  33. w_x=1,
  34. w_y=1,
  35. stride_x=1,
  36. stride_y=1,
  37. pad=False,
  38. randmin=TestSettings.INT8_MIN,
  39. randmax=TestSettings.INT8_MAX,
  40. batches=1,
  41. generate_bias=True,
  42. out_activation_min=None,
  43. out_activation_max=None,
  44. int16xint8=False,
  45. bias_min=TestSettings.INT32_MIN,
  46. bias_max=TestSettings.INT32_MAX,
  47. interpreter="tensorflow",
  48. input_scale=0.1,
  49. input_zp=0,
  50. w_scale=0.005,
  51. w_zp=0,
  52. bias_scale=0.00002,
  53. bias_zp=0,
  54. output_scale=0.1,
  55. output_zp=0,
  56. int4_weights=False
  57. ):
  58. super().__init__(dataset,
  59. testtype,
  60. regenerate_weights,
  61. regenerate_input,
  62. regenerate_biases,
  63. schema_file,
  64. in_ch,
  65. out_ch,
  66. x_in,
  67. y_in,
  68. x_in,
  69. y_in,
  70. stride_x,
  71. stride_y,
  72. pad,
  73. randmin,
  74. randmax,
  75. batches,
  76. generate_bias=generate_bias,
  77. out_activation_min=out_activation_min,
  78. out_activation_max=out_activation_max,
  79. int16xint8=int16xint8,
  80. bias_min=bias_min,
  81. bias_max=bias_max,
  82. interpreter=interpreter,
  83. int4_weights=int4_weights)
  84. self.filter_zero_point = w_zp
  85. if self.int4_weights or self.filter_zero_point:
  86. if self.generate_bias:
  87. self.json_template = "TestCases/Common/fc_weights_template.json"
  88. else:
  89. self.json_template = "TestCases/Common/fc_weights_template_null_bias.json"
  90. weight_type = "INT4" if self.int4_weights else "INT8"
  91. self.json_replacements = {
  92. "batches": batches,
  93. "input_size": in_ch * x_in * y_in,
  94. "input_scale": input_scale,
  95. "input_zp": input_zp,
  96. "w_type": weight_type,
  97. "w_scale": w_scale,
  98. "w_zp": w_zp,
  99. "bias_size": out_ch,
  100. "bias_scale": bias_scale,
  101. "bias_zp": bias_zp,
  102. "output_size": out_ch,
  103. "output_scale": output_scale,
  104. "output_zp": output_zp
  105. }
  106. def write_c_config_header(self) -> None:
  107. super().write_c_config_header()
  108. filename = self.config_data
  109. filepath = self.headers_dir + filename
  110. prefix = self.testdataset.upper()
  111. with open(filepath, "a") as f:
  112. f.write("#define {}_OUTPUT_MULTIPLIER {}\n".format(prefix, self.quantized_multiplier))
  113. f.write("#define {}_OUTPUT_SHIFT {}\n".format(prefix, self.quantized_shift))
  114. f.write("#define {}_ACCUMULATION_DEPTH {}\n".format(prefix, self.input_ch * self.x_input * self.y_input))
  115. f.write("#define {}_INPUT_OFFSET {}\n".format(prefix, -self.input_zero_point))
  116. f.write("#define {}_FILTER_OFFSET {}\n".format(prefix, -self.filter_zero_point))
  117. f.write("#define {}_OUTPUT_OFFSET {}\n".format(prefix, self.output_zero_point))
  118. def quantize_multiplier(self, weights_scale):
  119. input_product_scale = self.input_scale * weights_scale
  120. if input_product_scale < 0:
  121. raise RuntimeError("negative input product scale")
  122. real_multipler = input_product_scale / self.output_scale
  123. (self.quantized_multiplier, self.quantized_shift) = self.quantize_scale(real_multipler)
  124. def generate_data(self, input_data=None, weights=None, biases=None) -> None:
  125. if self.is_int16xint8:
  126. inttype = tf.int16
  127. datatype = "int16_t"
  128. bias_datatype = "int64_t"
  129. else:
  130. inttype = tf.int8
  131. datatype = "int8_t"
  132. bias_datatype = "int32_t"
  133. # Generate data
  134. fc_input_format = [self.batches, self.input_ch * self.x_input * self.y_input]
  135. if input_data is not None:
  136. input_data = tf.reshape(input_data, fc_input_format)
  137. else:
  138. input_data = self.get_randomized_input_data(input_data, fc_input_format)
  139. # Generate bias
  140. if self.generate_bias:
  141. biases = self.get_randomized_bias_data(biases)
  142. else:
  143. biases = None
  144. if self.filter_zero_point:
  145. temp1 = self.model_path
  146. temp2 = self.json_template
  147. fc_weights_format = [self.input_ch * self.y_input * self.x_input * self.output_ch]
  148. if weights is not None:
  149. weights = tf.reshape(weights, fc_weights_format)
  150. else:
  151. weights = self.get_randomized_data(fc_weights_format,
  152. self.kernel_table_file,
  153. minrange=TestSettings.INT8_MIN,
  154. maxrange=TestSettings.INT8_MAX,
  155. regenerate=self.regenerate_new_weights)
  156. self.model_path = self.model_path
  157. self.json_template = self.json_template
  158. generated_json = self.generate_json_from_template(weights, bias_data=biases, bias_buffer=2)
  159. self.flatc_generate_tflite(generated_json, self.schema_file)
  160. weights_size = weights.numpy().size
  161. filter_index = 1
  162. bias_index = 2
  163. elif self.int4_weights:
  164. # Generate weights, both packed and unpacked model from JSON
  165. temp1 = self.model_path
  166. temp2 = self.json_template
  167. fc_weights_format = [self.input_ch * self.y_input * self.x_input * self.output_ch]
  168. if weights is not None:
  169. weights = tf.reshape(weights, fc_weights_format)
  170. else:
  171. weights = self.get_randomized_data(fc_weights_format,
  172. self.kernel_table_file,
  173. minrange=TestSettings.INT4_MIN,
  174. maxrange=TestSettings.INT4_MAX,
  175. regenerate=self.regenerate_new_weights)
  176. # Unpacked model is used for reference during debugging only and not used by default
  177. self.model_path = self.model_path + "_unpacked"
  178. self.json_template = self.json_template[:-5] + "_unpacked.json"
  179. generated_json = self.generate_json_from_template(weights, bias_data=biases, bias_buffer=2)
  180. self.flatc_generate_tflite(generated_json, self.schema_file)
  181. self.model_path = temp1
  182. self.json_template = temp2
  183. temp = np.reshape(weights, (len(weights) // 2, 2)).astype(np.uint8)
  184. temp = 0xff & ((0xf0 & (temp[:, 1] << 4)) | (temp[:, 0] & 0xf))
  185. weights = tf.convert_to_tensor(temp)
  186. weights_size = weights.numpy().size * 2
  187. generated_json = self.generate_json_from_template(weights, bias_data=biases, bias_buffer=2)
  188. self.flatc_generate_tflite(generated_json, self.schema_file)
  189. filter_index = 1
  190. bias_index = 2
  191. else:
  192. fc_weights_format = [self.input_ch * self.y_input * self.x_input, self.output_ch]
  193. if weights is not None:
  194. weights = tf.reshape(weights, fc_weights_format)
  195. else:
  196. weights = self.get_randomized_data(fc_weights_format,
  197. self.kernel_table_file,
  198. minrange=TestSettings.INT32_MIN,
  199. maxrange=TestSettings.INT32_MAX,
  200. regenerate=self.regenerate_new_weights)
  201. weights_size = weights.numpy().size
  202. # Generate model in tensorflow with one fully_connected layer
  203. model = keras.models.Sequential()
  204. model.add(
  205. keras.layers.InputLayer(input_shape=(self.y_input * self.x_input * self.input_ch, ),
  206. batch_size=self.batches))
  207. fully_connected_layer = keras.layers.Dense(self.output_ch, activation=None, use_bias=self.generate_bias)
  208. model.add(fully_connected_layer)
  209. if self.generate_bias:
  210. fully_connected_layer.set_weights([weights, biases])
  211. else:
  212. fully_connected_layer.set_weights([weights])
  213. self.convert_model(model, inttype)
  214. bias_index = 1
  215. if self.generate_bias:
  216. filter_index = 2
  217. else:
  218. filter_index = 1
  219. interpreter = self.interpret_model(input_data, inttype)
  220. # Get layer information
  221. all_layers_details = interpreter.get_tensor_details()
  222. filter_layer = all_layers_details[filter_index]
  223. bias_layer = all_layers_details[bias_index]
  224. if weights_size != interpreter.get_tensor(filter_layer['index']).size or \
  225. (self.generate_bias and biases.numpy().size != interpreter.get_tensor(bias_layer['index']).size):
  226. raise RuntimeError(f"Dimension mismatch for {self.testdataset}")
  227. weights_zero_point = filter_layer['quantization_parameters']['zero_points'][0]
  228. if weights_zero_point != self.filter_zero_point:
  229. raise RuntimeError(f"Filter zero point point mismatch for {self.filter_zero_point}")
  230. self.x_output = 1
  231. self.y_output = 1
  232. weights_scale = filter_layer['quantization_parameters']['scales'][0]
  233. self.quantize_multiplier(weights_scale)
  234. # Generate reference output
  235. output_details = interpreter.get_output_details()
  236. interpreter.invoke()
  237. output_data = interpreter.get_tensor(output_details[0]["index"])
  238. # Save results
  239. self.generate_c_array(self.input_data_file_prefix, input_data, datatype=datatype)
  240. self.generate_c_array(
  241. self.weight_data_file_prefix, interpreter.get_tensor(filter_layer['index']), pack=self.int4_weights)
  242. if not self.generate_bias:
  243. bias = []
  244. else:
  245. bias = interpreter.get_tensor(bias_layer['index'])
  246. self.generate_c_array(self.bias_data_file_prefix, bias, datatype=bias_datatype)
  247. self.generate_c_array(self.output_data_file_prefix,
  248. np.clip(output_data, self.out_activation_min, self.out_activation_max),
  249. datatype=datatype)
  250. self.write_c_config_header()
  251. self.write_c_header_wrapper()