generate_test_data.py 59 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233
  1. #!/usr/bin/env python3
  2. #
  3. # Copyright (C) 2010-2021 Arm Limited or its affiliates. All rights reserved.
  4. #
  5. # SPDX-License-Identifier: Apache-2.0
  6. #
  7. # Licensed under the Apache License, Version 2.0 (the License); you may
  8. # not use this file except in compliance with the License.
  9. # You may obtain a copy of the License at
  10. #
  11. # www.apache.org/licenses/LICENSE-2.0
  12. #
  13. # Unless required by applicable law or agreed to in writing, software
  14. # distributed under the License is distributed on an AS IS BASIS, WITHOUT
  15. # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  16. # See the License for the specific language governing permissions and
  17. # limitations under the License.
  18. #
  19. import os
  20. import sys
  21. import math
  22. import random
  23. import argparse
  24. import subprocess
  25. import numpy as np
  26. from collections import deque
  27. from packaging import version
  28. from abc import ABC, abstractmethod
  29. try:
  30. import tensorflow as tf
  31. except Exception as e:
  32. print(e)
  33. sys.exit(1)
  34. REQUIRED_MINIMUM_TENSORFLOW_VERSION = version.parse("2.1.0")
  35. DEFAULT_TESTDATA_SET = 'basic'
  36. ALL_TESTDATA_SETS = {}
  37. CLANG_FORMAT = 'clang-format-9 -i'
  38. LICENSE = """/*
  39. * Copyright (C) 2010-2021 Arm Limited or its affiliates. All rights reserved.
  40. *
  41. * SPDX-License-Identifier: Apache-2.0
  42. *
  43. * Licensed under the Apache License, Version 2.0 (the License); you may
  44. * not use this file except in compliance with the License.
  45. * You may obtain a copy of the License at
  46. *
  47. * www.apache.org/licenses/LICENSE-2.0
  48. *
  49. * Unless required by applicable law or agreed to in writing, software
  50. * distributed under the License is distributed on an AS IS BASIS, WITHOUT
  51. * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  52. * See the License for the specific language governing permissions and
  53. * limitations under the License.
  54. */
  55. """
  56. def parse_args():
  57. parser = argparse.ArgumentParser(description="Generate input and refererence output data for unittests."
  58. " It can regenerate all data, load all stored data or a combination of it.")
  59. parser.add_argument('--dataset', type=str, default=DEFAULT_TESTDATA_SET, help="Name of generated test set.")
  60. parser.add_argument('--regenerate-weights', action='store_true', help="Regenerate and store new weights.")
  61. parser.add_argument('--regenerate-input', action='store_true', help="Regenerate and store new input.")
  62. parser.add_argument('--regenerate-biases', action='store_true', help="Regenerate and store new biases.")
  63. parser.add_argument('-a', '--regenerate-all', action='store_true', help="Regenerate and store all data.")
  64. parser.add_argument('-t', '--testtype', type=str, default='conv', choices=['conv', 'depthwise_conv', 'avgpool',
  65. 'maxpool', 'fully_connected', 'softmax',
  66. 'svdf'],
  67. help='Type of test.')
  68. parser.add_argument('--run-all-testsets', action='store_true', help="Run the script for all existing test "
  69. "sets. Regenerate all, partially all or no input data (output may still change, depending on"
  70. " changes in script) depending on regenerate flags.")
  71. args = parser.parse_args()
  72. return args
  73. class TestSettings(ABC):
  74. # This is the generated test data used by the test cases.
  75. OUTDIR = 'TestCases/TestData/'
  76. # This is input to the data generation. If everything or something is regenerated then it is overwritten.
  77. # So it always has the same data as the OUTDIR.
  78. # The purpose of the pregen is primarily for debugging, as it enables to change a single parameter and see how the
  79. # output changes, without regenerating all input data.
  80. # It also convinient when tesing changes in the script, to be able to run all test sets again.
  81. PREGEN = 'PregeneratedData/'
  82. INT32_MAX = 2147483647
  83. INT32_MIN = -2147483648
  84. INT16_MAX = 32767
  85. INT16_MIN = -32767
  86. INT8_MAX = 127
  87. INT8_MIN = -128
  88. UINT8_MAX = 255
  89. UINT8_MIN = 0
  90. def __init__(self, dataset, testtype, args, in_ch, out_ch, x_in, y_in, w_x, w_y, stride_x, stride_y, pad, randmin,
  91. randmax, outminrange=-128, outmaxrange=127, batches=1, generate_bias=True, relu6=False,
  92. out_activation_min=None, out_activation_max=None):
  93. self.tensor_flow_reference_version = ("// Generated by {} using TFL version {} as reference.\n".
  94. format(os.path.basename(__file__), tf.__version__))
  95. # Randomization interval
  96. self.mins = randmin
  97. self.maxs = randmax
  98. self.relu6 = relu6
  99. self.input_ch = in_ch
  100. self.output_ch = out_ch
  101. self.x_input = x_in
  102. self.y_input = y_in
  103. self.filter_x = w_x
  104. self.filter_y = w_y
  105. self.stride_x = stride_x
  106. self.stride_y = stride_y
  107. self.batches = batches
  108. self.test_type = testtype
  109. self.has_padding = pad
  110. if out_activation_min:
  111. self.out_activation_min = out_activation_min
  112. else:
  113. self.out_activation_min = self.INT8_MIN
  114. if out_activation_max:
  115. self.out_activation_max = out_activation_max
  116. else:
  117. self.out_activation_max = self.INT8_MAX
  118. minrange = randmin - 1
  119. maxrange = randmax + 1
  120. (self.input_scale, self.input_zero_point) = self.derive_scale_and_zeropoint_from_min_max(minrange, maxrange)
  121. (self.output_scale, self.output_zero_point) = self.derive_scale_and_zeropoint_from_min_max(outminrange,
  122. outmaxrange)
  123. # Always use output scale of 1, when derived it sometimes gets slighly smaller than 1,
  124. # which may cause output to differ.
  125. self.output_scale = 1.0
  126. # Bias is optional.
  127. self.generate_bias = generate_bias
  128. self.generated_header_files = []
  129. self.pregenerated_data_dir = self.PREGEN
  130. self.testdataset = DEFAULT_TESTDATA_SET
  131. self.config_data = "config_data.h"
  132. self.testdataset = dataset
  133. self.kernel_table_file = self.pregenerated_data_dir + self.testdataset + '/' + 'kernel.txt'
  134. self.inputs_table_file = self.pregenerated_data_dir + self.testdataset + '/' + 'input.txt'
  135. self.bias_table_file = self.pregenerated_data_dir + self.testdataset + '/' + 'bias.txt'
  136. self.parameters_file = self.pregenerated_data_dir + self.testdataset + '/' + 'params.txt'
  137. self.set_output_dims_and_padding()
  138. self.regenerate_new_weights = args.regenerate_weights
  139. self.regenerate_new_input = args.regenerate_input
  140. self.regenerate_new_bias = args.regenerate_biases
  141. if not os.path.exists(self.parameters_file) or args.regenerate_all:
  142. self.regenerate_new_bias = True
  143. self.regenerate_new_weights = True
  144. self.regenerate_new_input = True
  145. self.save_parameters()
  146. else:
  147. self.load_parameters()
  148. self.headers_dir = self.OUTDIR + self.testdataset + '/'
  149. def clamp(self, result, smallest, largest):
  150. return max(smallest, min(result, largest))
  151. def quantize_input(self, value):
  152. result = round(value / self.input_scale) + self.input_zero_point
  153. return self.clamp(result, self.INT8_MIN, self.INT8_MAX)
  154. def derive_scale_from_min_max(self, minrange, maxrange):
  155. scale = (maxrange - minrange) / ((self.INT8_MAX * 1.0) - self.INT8_MIN)
  156. return scale
  157. def derive_scale_and_zeropoint_from_min_max(self, minrange, maxrange):
  158. scale = self.derive_scale_from_min_max(minrange, maxrange)
  159. zeropoint = self.INT8_MIN + int(-minrange / scale + 0.5)
  160. zeropoint = max(self.INT8_MIN, min(zeropoint, -self.INT8_MIN))
  161. return (scale, zeropoint)
  162. def save_multiple_dim_array_in_txt(self, file, data):
  163. header = ','.join(map(str, data.shape))
  164. np.savetxt(file, data.reshape(-1, data.shape[-1]), header=header,
  165. delimiter=',')
  166. def load_multiple_dim_array_from_txt(self, file):
  167. with open(file) as f:
  168. shape = list(map(int, next(f)[1:].split(',')))
  169. data = np.genfromtxt(f, delimiter=',').reshape(shape)
  170. return data.astype(np.float32)
  171. def save_parameters(self):
  172. regendir = os.path.dirname(self.parameters_file)
  173. if not os.path.exists(regendir):
  174. os.makedirs(regendir)
  175. params = np.array([self.input_ch, self.output_ch, self.x_input, self.y_input, self.filter_x, self.filter_y,
  176. self.stride_x, self.stride_y, self.pad_x, self.pad_y, self.batches, self.has_padding])
  177. np.savetxt(self.parameters_file, params, fmt='%i')
  178. def load_parameters(self):
  179. params = np.loadtxt(self.parameters_file).astype(int)
  180. (self.input_ch, self.output_ch, self.x_input, self.y_input, self.filter_x, self.filter_y,
  181. self.stride_x, self.stride_y, self.pad_x, self.pad_y, self.batches, self.has_padding) = \
  182. (map(lambda x: x, params))
  183. def convert_tensor_np(self, tensor_in, converter):
  184. w = tensor_in.numpy()
  185. shape = w.shape
  186. w = w.ravel()
  187. fw = converter(w)
  188. fw.shape = shape
  189. return tf.convert_to_tensor(fw)
  190. def convert_tensor(self, tensor_in, converter, params=None):
  191. w = tensor_in.numpy()
  192. shape = w.shape
  193. w = w.ravel()
  194. normal = np.array(w)
  195. float_normal = []
  196. for i in normal:
  197. if params:
  198. float_normal.append(converter(i, params))
  199. else:
  200. float_normal.append(converter(i))
  201. np_float_array = np.asarray(float_normal)
  202. np_float_array.shape = shape
  203. return tf.convert_to_tensor(np_float_array)
  204. def get_randomized_data(self, dims, npfile, regenerate, decimals=0, minrange=None, maxrange=None):
  205. if not minrange:
  206. minrange = self.mins
  207. if not maxrange:
  208. maxrange = self.maxs
  209. if not os.path.exists(npfile) or regenerate:
  210. regendir = os.path.dirname(npfile)
  211. if not os.path.exists(regendir):
  212. os.makedirs(regendir)
  213. if decimals == 0:
  214. data = tf.Variable(tf.random.uniform(dims, minval=minrange, maxval=maxrange, dtype=tf.dtypes.int32))
  215. data = tf.cast(data, dtype=tf.float32)
  216. else:
  217. data = tf.Variable(tf.random.uniform(dims, minval=minrange, maxval=maxrange, dtype=tf.dtypes.float32))
  218. data = np.around(data.numpy(), decimals)
  219. data = tf.convert_to_tensor(data)
  220. print("Saving data to {}".format(npfile))
  221. self.save_multiple_dim_array_in_txt(npfile, data.numpy())
  222. else:
  223. print("Loading data from {}".format(npfile))
  224. data = tf.convert_to_tensor(self.load_multiple_dim_array_from_txt(npfile))
  225. return data
  226. def get_randomized_input_data(self, input_data):
  227. # Generate or load saved input data unless hardcoded data provided
  228. if input_data is not None:
  229. input_data = tf.reshape(input_data, [self.batches, self.y_input, self.x_input, self.input_ch])
  230. else:
  231. input_data = self.get_randomized_data([self.batches, self.y_input, self.x_input, self.input_ch],
  232. self.inputs_table_file,
  233. regenerate=self.regenerate_new_input)
  234. return input_data
  235. def get_randomized_bias_data(self, biases):
  236. # Generate or load saved bias data unless hardcoded data provided
  237. if not self.generate_bias:
  238. biases = tf.reshape(np.full([self.output_ch], 0), [self.output_ch])
  239. elif biases is not None:
  240. biases = tf.reshape(biases, [self.output_ch])
  241. else:
  242. biases = self.get_randomized_data([self.output_ch],
  243. self.bias_table_file,
  244. regenerate=self.regenerate_new_bias)
  245. return biases
  246. def format_output_file(self, file):
  247. command_list = CLANG_FORMAT.split(' ')
  248. command_list.append(file)
  249. process = subprocess.run(command_list)
  250. if process.returncode != 0:
  251. print("ERROR: {} failed".format(command_list))
  252. sys.exit(1)
  253. def write_c_header_wrapper(self):
  254. filename = "test_data.h"
  255. filepath = self.headers_dir + filename
  256. print("Generating C header wrapper {}...".format(filepath))
  257. with open(filepath, 'w+') as f:
  258. f.write("{}\n\n".format(LICENSE))
  259. f.write(self.tensor_flow_reference_version)
  260. while len(self.generated_header_files) > 0:
  261. f.write('#include "{}"\n'.format(self.generated_header_files.pop()))
  262. self.format_output_file(filepath)
  263. def write_common_config(self, f, prefix):
  264. """
  265. Shared by conv/depthwise_conv and pooling
  266. """
  267. f.write("#define {}_FILTER_X {}\n".format(prefix, self.filter_x))
  268. f.write("#define {}_FILTER_Y {}\n".format(prefix, self.filter_y))
  269. f.write("#define {}_STRIDE_X {}\n".format(prefix, self.stride_x))
  270. f.write("#define {}_STRIDE_Y {}\n".format(prefix, self.stride_y))
  271. f.write("#define {}_PAD_X {}\n".format(prefix, self.pad_x))
  272. f.write("#define {}_PAD_Y {}\n".format(prefix, self.pad_y))
  273. f.write("#define {}_OUTPUT_W {}\n".format(prefix, self.x_output))
  274. f.write("#define {}_OUTPUT_H {}\n".format(prefix, self.y_output))
  275. def write_c_common_header(self, f):
  276. f.write("{}\n\n".format(LICENSE))
  277. f.write(self.tensor_flow_reference_version)
  278. f.write("#pragma once\n")
  279. def write_c_header_offsets(self, f, prefix):
  280. f.write("#define {}_INPUT_OFFSET {}\n".format(prefix, -self.input_zero_point))
  281. f.write("#define {}_OUTPUT_OFFSET {}\n".format(prefix, self.output_zero_point))
  282. def write_c_config_header(self, write_common_parameters=True):
  283. filename = self.config_data
  284. self.generated_header_files.append(filename)
  285. filepath = self.headers_dir + filename
  286. prefix = self.testdataset.upper()
  287. print("Writing C header with config data {}...".format(filepath))
  288. with open(filepath, "w+") as f:
  289. self.write_c_common_header(f)
  290. if (write_common_parameters):
  291. f.write("#define {}_OUT_CH {}\n".format(prefix, self.output_ch))
  292. f.write("#define {}_IN_CH {}\n".format(prefix, self.input_ch))
  293. f.write("#define {}_INPUT_W {}\n".format(prefix, self.x_input))
  294. f.write("#define {}_INPUT_H {}\n".format(prefix, self.y_input))
  295. f.write("#define {}_DST_SIZE {}\n".format(prefix, self.x_output * self.y_output * self.output_ch
  296. * self.batches))
  297. f.write("#define {}_INPUT_SIZE {}\n".format(prefix, self.x_input * self.y_input * self.input_ch))
  298. if self.relu6:
  299. f.write("#define {}_OUT_ACTIVATION_MIN {}\n".format(prefix, 0))
  300. f.write("#define {}_OUT_ACTIVATION_MAX {}\n".format(prefix, 6))
  301. else:
  302. f.write("#define {}_OUT_ACTIVATION_MIN {}\n".format(prefix, self.out_activation_min))
  303. f.write("#define {}_OUT_ACTIVATION_MAX {}\n".format(prefix, self.out_activation_max))
  304. f.write("#define {}_INPUT_BATCHES {}\n".format(prefix, self.batches))
  305. self.format_output_file(filepath)
  306. def generate_c_array(self, name, array, datatype="q7_t", const="const "):
  307. if not os.path.exists(self.headers_dir):
  308. os.makedirs(self.headers_dir)
  309. w = None
  310. if type(array) is list:
  311. w = array
  312. size = len(array)
  313. else:
  314. w = array.numpy()
  315. w = w.ravel()
  316. size = tf.size(array)
  317. filename = name + "_data.h"
  318. filepath = self.headers_dir + filename
  319. self.generated_header_files.append(filename)
  320. print("Generating C header {}...".format(filepath))
  321. with open(filepath, "w+") as f:
  322. self.write_c_common_header(f)
  323. f.write("#include <stdint.h>\n\n")
  324. f.write(const + datatype + " " + self.testdataset + '_' + name + "[%d] =\n{\n" % size)
  325. for i in range(size - 1):
  326. f.write(" %d,\n" % w[i])
  327. f.write(" %d\n" % w[size - 1])
  328. f.write("};\n")
  329. self.format_output_file(filepath)
  330. def quantize_output(self, value):
  331. result = round(value / self.output_scale) + self.output_zero_point
  332. return self.clamp(result, self.out_activation_min, self.out_activation_max)
  333. def set_output_dims_and_padding(self):
  334. if self.has_padding:
  335. self.x_output = math.ceil(float(self.x_input) / float(self.stride_x))
  336. self.y_output = math.ceil(float(self.y_input) / float(self.stride_y))
  337. self.padding = 'SAME'
  338. pad_along_width = max((self.x_output - 1) * self.stride_x + self.filter_x - self.x_input, 0)
  339. pad_along_height = max((self.y_output - 1) * self.stride_y + self.filter_y - self.y_input, 0)
  340. pad_top = pad_along_height // 2
  341. pad_left = pad_along_width // 2
  342. self.pad_x = pad_left
  343. self.pad_y = pad_top
  344. else:
  345. self.x_output = math.ceil(float(self.x_input - self.filter_x + 1) / float(self.stride_x))
  346. self.y_output = math.ceil(float(self.y_input - self.filter_y + 1) / float(self.stride_y))
  347. self.padding = 'VALID'
  348. self.pad_x = 0
  349. self.pad_y = 0
  350. @abstractmethod
  351. def generate_data(self, input_data=None, weights=None, biases=None):
  352. ''' Must be overriden '''
  353. def reshape_conv_kernel(self, kernel):
  354. """
  355. TFL & TFLu conv weight format: kOHWI
  356. Tensorflow conv weight format: kHWIO
  357. """
  358. kernel = tf.reshape(kernel, [self.output_ch, self.filter_y, self.filter_x, self.input_ch])
  359. kernel = tf.transpose(kernel, (1, 2, 0, 3))
  360. kernel = tf.transpose(kernel, (0, 1, 3, 2))
  361. return kernel
  362. def reshape_depthwise_conv_kernel(self, kernel):
  363. """
  364. TFL & TFLu depthwise conv weight format: k1HWO
  365. Tensorflow depthwise conv weight format: kHWIM
  366. """
  367. kernel = tf.reshape(kernel, [1, self.filter_y, self.filter_x, self.output_ch])
  368. kernel = tf.transpose(kernel, (1, 0, 2, 3))
  369. kernel = tf.transpose(kernel, (0, 2, 1, 3))
  370. kernel = tf.reshape(kernel, [self.filter_y, self.filter_x, self.input_ch, self.channel_multiplier])
  371. return kernel
  372. def conv2d(self, indata, weights, bias=None):
  373. """
  374. There is no tf.nn.fully_connected so this is used by fully_connected tests as well.
  375. """
  376. indata = tf.cast(indata, dtype=tf.dtypes.float32)
  377. weights = tf.cast(weights, dtype=tf.dtypes.float32)
  378. bias = tf.cast(bias, dtype=tf.dtypes.float32)
  379. out = tf.nn.conv2d(indata, weights, strides=[1, self.stride_y, self.stride_x, 1], padding=self.padding)
  380. if tf.TensorShape([self.batches, self.y_output, self.x_output, self.output_ch]).as_list() != \
  381. out.shape.as_list():
  382. raise RuntimeError("Shape mismatch, need to regenerate data?")
  383. out = tf.nn.bias_add(out, bias)
  384. out = tf.clip_by_value(out, self.out_activation_min, self.out_activation_max)
  385. return out
  386. def quantize_scale(self, scale):
  387. significand, shift = math.frexp(scale)
  388. significand_q31 = round(significand * (1 << 31))
  389. return significand_q31, shift
  390. class ConvSettings(TestSettings):
  391. def __init__(self, dataset, testtype, args, in_ch=1, out_ch=1, x_in=7, y_in=7, w_x=3, w_y=3, stride_x=2, stride_y=2,
  392. pad=True, randmin=-7, randmax=7, outminrange=-128, outmaxrange=127, batches=1, generate_bias=True,
  393. relu6=False, out_activation_min=None, out_activation_max=None):
  394. super().__init__(dataset, testtype, args, in_ch, out_ch, x_in, y_in, w_x, w_y, stride_x, stride_y, pad,
  395. randmin, randmax, outminrange, outmaxrange, batches, generate_bias=generate_bias, relu6=relu6,
  396. out_activation_min=out_activation_min, out_activation_max=out_activation_max)
  397. self.scaling_factors = []
  398. if self.test_type == 'conv':
  399. self.quantized_dimension = 0
  400. elif self.test_type == 'depthwise_conv':
  401. self.quantized_dimension = 3
  402. self.channel_multiplier = self.output_ch // self.input_ch
  403. if self.output_ch % self.input_ch != 0:
  404. raise RuntimeError("out channel ({}) is not multiple of in channel ({})".format(out_ch, in_ch))
  405. else:
  406. raise RuntimeError("Invalid test type {}".format(self.test_type))
  407. def write_c_config_header(self):
  408. super().write_c_config_header()
  409. filename = self.config_data
  410. filepath = self.headers_dir + filename
  411. prefix = self.testdataset.upper()
  412. with open(filepath, "a") as f:
  413. self.write_c_header_offsets(f, prefix)
  414. self.write_common_config(f, prefix)
  415. if self.test_type == 'depthwise_conv':
  416. f.write("#define {}_CH_MULT {}\n".format(prefix, self.channel_multiplier))
  417. def quantize_bias(self, nparray):
  418. num_channels = self.output_ch
  419. quantized_values = []
  420. values = np.array(nparray)
  421. def quantize_float_to_int(value, scale):
  422. if scale == 0:
  423. print("WARNING: scale is 0")
  424. scale = 0.0000001
  425. quantized = round(value / scale)
  426. if quantized > self.INT16_MAX:
  427. quantized = self.INT16_MAX
  428. elif quantized < self.INT16_MIN:
  429. quantized = self.INT16_MIN
  430. return quantized
  431. for x in range(num_channels):
  432. quantized_values.append(quantize_float_to_int(values[x], self.scaling_factors[x]*self.input_scale))
  433. return np.asarray(quantized_values)
  434. def quantize_filter(self, nparray):
  435. channel_count = self.output_ch
  436. if self.quantized_dimension == 0:
  437. input_size = self.filter_y * self.filter_x * self.input_ch * self.output_ch
  438. elif self.quantized_dimension == 3:
  439. input_size = self.filter_y * self.filter_x * self.input_ch * self.channel_multiplier
  440. per_channel_size = input_size // channel_count
  441. if self.quantized_dimension == 0:
  442. stride = 1
  443. channel_stride = per_channel_size
  444. elif self.quantized_dimension == 3:
  445. stride = channel_count
  446. channel_stride = 1
  447. values = np.array(nparray)
  448. quantized_values = values.copy()
  449. for channel in range(channel_count):
  450. fmin = 0
  451. fmax = 0
  452. for i in range(per_channel_size):
  453. idx = channel * channel_stride + i * stride
  454. fmin = min(fmin, values[idx])
  455. fmax = max(fmax, values[idx])
  456. self.scaling_factors.append(max(abs(fmin), abs(fmax)) / self.INT8_MAX)
  457. for x in range(per_channel_size):
  458. chs = channel * channel_stride + x * stride
  459. quantized_value = round(round(values[chs]) / self.scaling_factors[channel])
  460. # Clamp
  461. quantized_value = min(127, max(-127, quantized_value))
  462. quantized_values[chs] = quantized_value
  463. return np.asarray(quantized_values)
  464. def generate_quantize_per_channel_multiplier(self):
  465. num_channels = self.output_ch
  466. per_channel_multiplier = []
  467. per_channel_shift = []
  468. if len(self.scaling_factors) != num_channels:
  469. raise RuntimeError("Missing scaling factors")
  470. for i in range(num_channels):
  471. effective_output_scale = self.input_scale * self.scaling_factors[i] / self.output_scale
  472. (quantized_multiplier, shift) = self.quantize_scale(effective_output_scale)
  473. per_channel_multiplier.append(quantized_multiplier)
  474. per_channel_shift.append(shift)
  475. self.generate_c_array("output_mult", per_channel_multiplier, datatype='int32_t')
  476. self.generate_c_array("output_shift", per_channel_shift, datatype='int32_t')
  477. def depthwise_conv2d(self, indata, weights, bias=None):
  478. indata = tf.cast(indata, dtype=tf.dtypes.float32)
  479. weights = tf.cast(weights, dtype=tf.dtypes.float32)
  480. bias = tf.cast(bias, dtype=tf.dtypes.float32)
  481. out = tf.nn.depthwise_conv2d(indata,
  482. weights,
  483. strides=[1, self.stride_y, self.stride_x, 1],
  484. padding=self.padding)
  485. if tf.TensorShape([self.batches, self.y_output, self.x_output, self.output_ch]) \
  486. .as_list() != out.shape.as_list():
  487. raise RuntimeError("Shape mismatch, regenerate data?")
  488. out = tf.nn.bias_add(out, bias)
  489. out = tf.clip_by_value(out, self.out_activation_min, self.out_activation_max)
  490. return out
  491. def generate_data(self, input_data=None, weights=None, biases=None):
  492. # Tensorflow Lite has a different kernel format compared to Tensorflow
  493. reshaped_weights = None
  494. input_data = self.get_randomized_input_data(input_data)
  495. if self.test_type == 'conv':
  496. out_channel = self.output_ch
  497. elif self.test_type == 'depthwise_conv':
  498. out_channel = self.channel_multiplier
  499. if weights is not None:
  500. weights = tf.reshape(weights, [self.filter_y, self.filter_x, self.input_ch, out_channel])
  501. else:
  502. weights = self.get_randomized_data([self.filter_y, self.filter_x, self.input_ch, out_channel],
  503. self.kernel_table_file,
  504. regenerate=self.regenerate_new_weights)
  505. if self.test_type == 'conv':
  506. reshaped_weights = self.reshape_conv_kernel(weights)
  507. elif self.test_type == 'depthwise_conv':
  508. reshaped_weights = self.reshape_depthwise_conv_kernel(weights)
  509. biases = self.get_randomized_bias_data(biases)
  510. # Generate reference
  511. if self.test_type == 'conv':
  512. conv = self.conv2d(input_data, reshaped_weights, biases)
  513. elif self.test_type == 'depthwise_conv':
  514. conv = self.depthwise_conv2d(input_data, reshaped_weights, biases)
  515. # Quantize and write to C headers
  516. self.generate_c_array("input", self.convert_tensor(input_data, self.quantize_input))
  517. self.generate_c_array("weights", self.convert_tensor_np(weights, self.quantize_filter))
  518. self.generate_c_array("biases", self.convert_tensor_np(biases, self.quantize_bias), "int32_t")
  519. self.generate_quantize_per_channel_multiplier()
  520. self.generate_c_array("output_ref", self.convert_tensor(conv, self.quantize_output))
  521. self.write_c_config_header()
  522. self.write_c_header_wrapper()
  523. class PoolingSettings(TestSettings):
  524. def __init__(self, dataset, testtype, args, randmin=-7, randmax=7, channels=8, x_in=4, y_in=4, w_x=4, w_y=4,
  525. stride_x=1, stride_y=1, batches=1, pad=False, relu6=False):
  526. super().__init__(dataset, testtype, args, channels, channels, x_in, y_in, w_x, w_y, stride_x, stride_y, pad,
  527. randmin, randmax, relu6=relu6)
  528. def generate_data(self, input_data=None):
  529. input_data = self.get_randomized_input_data(input_data)
  530. input_data = self.convert_tensor(input_data, self.quantize_input)
  531. self.generate_c_array("input", input_data, datatype="int8_t")
  532. input_data = tf.cast(input_data, tf.float32)
  533. if self.test_type == 'avgpool':
  534. pooling = self.average_pooling(input_data)
  535. elif self.test_type == 'maxpool':
  536. pooling = self.max_pooling(input_data)
  537. else:
  538. raise RuntimeError("Wrong test type")
  539. if self.y_output != pooling.shape[1] or self.x_output != pooling.shape[2] or self.output_ch != pooling.shape[3]:
  540. raise RuntimeError("Mismatching output dimensions")
  541. self.generate_c_array("output_ref", self.convert_tensor(pooling, self.quantize_output), datatype="int8_t")
  542. self.write_c_config_header()
  543. self.write_c_header_wrapper()
  544. def write_c_config_header(self):
  545. super().write_c_config_header()
  546. filename = self.config_data
  547. filepath = self.headers_dir + filename
  548. prefix = self.testdataset.upper()
  549. with open(filepath, "a") as f:
  550. self.write_common_config(f, prefix)
  551. def average_pooling(self, x):
  552. result = tf.nn.avg_pool(x,
  553. ksize=[1, self.filter_y, self.filter_x, 1],
  554. strides=[1, self.stride_y, self.stride_x, 1],
  555. padding=self.padding)
  556. if self.relu6:
  557. return tf.nn.relu6(result)
  558. else:
  559. return result
  560. def max_pooling(self, x):
  561. maxpool = tf.nn.max_pool(x,
  562. ksize=[1, self.filter_y, self.filter_x, 1],
  563. strides=[1, self.stride_y, self.stride_x, 1],
  564. padding=self.padding)
  565. if self.relu6:
  566. return tf.nn.relu6(maxpool)
  567. else:
  568. return maxpool
  569. class FullyConnectedSettings(TestSettings):
  570. def __init__(self, dataset, testtype, args, in_ch=1, out_ch=1, x_in=1, y_in=1, w_x=1, w_y=1, stride_x=1, stride_y=1,
  571. pad=False, randmin=-4, randmax=4, outminrange=-128, outmaxrange=127, batches=1, input_scale=1.0,
  572. input_zero_point=0, weights_scale=1.0, bias_scale=1.0, output_scale=1.0,
  573. output_zero_point=0, generate_bias=True, out_activation_min=None, out_activation_max=None):
  574. super().__init__(dataset, testtype, args, in_ch, out_ch, x_in, y_in, w_x, w_y, stride_x, stride_y, pad, randmin,
  575. randmax, outminrange, outmaxrange, batches, generate_bias=generate_bias,
  576. out_activation_min=out_activation_min, out_activation_max=out_activation_max)
  577. if not self.test_type == 'fully_connected':
  578. raise RuntimeError("Invalid test type {}".format(self.test_type))
  579. if x_in != w_x or y_in != w_y:
  580. raise RuntimeError("Mismatching input and filter dimensions")
  581. self.input_scale = input_scale
  582. self.input_zero_point = input_zero_point
  583. self.weights_scale = weights_scale
  584. self.bias_scale = bias_scale
  585. self.output_scale = output_scale
  586. self.output_zero_point = output_zero_point
  587. def write_c_config_header(self):
  588. super().write_c_config_header()
  589. filename = self.config_data
  590. filepath = self.headers_dir + filename
  591. prefix = self.testdataset.upper()
  592. with open(filepath, "a") as f:
  593. self.write_c_header_offsets(f, prefix)
  594. f.write("#define {}_OUTPUT_MULTIPLIER {}\n".format(prefix, self.quantized_multiplier))
  595. f.write("#define {}_OUTPUT_SHIFT {}\n".format(prefix, self.quantized_shift))
  596. f.write("#define {}_ACCUMULATION_DEPTH {}\n".format(prefix, self.input_ch*self.x_input*self.y_input))
  597. def quantize_multiplier(self):
  598. input_product_scale = self.input_scale * self.weights_scale
  599. if input_product_scale < 0:
  600. raise RuntimeError("negative input product scale")
  601. real_multipler = input_product_scale / self.output_scale
  602. (self.quantized_multiplier, self.quantized_shift) = self.quantize_scale(real_multipler)
  603. def derive_filter_scale_and_zeropoint_from_min_max(self, mini, maxi):
  604. scale = self.derive_scale_from_min_max(mini, maxi)
  605. zero = int(self.INT8_MIN + (-mini/scale + 0.5))
  606. return (scale, zero)
  607. def quantize_bias(self, value):
  608. result = int(value / self.bias_scale)
  609. return self.clamp(result, self.INT32_MIN, self.INT32_MAX)
  610. def quantize_weights(self, value):
  611. result = round(value / self.weights_scale)
  612. return self.clamp(result, self.INT8_MIN, self.INT8_MAX)
  613. def generate_data(self, input_data=None, weights=None, biases=None):
  614. input_data = self.get_randomized_input_data(input_data)
  615. if weights is not None:
  616. weights = tf.reshape(weights, [self.filter_y, self.filter_x, self.input_ch, self.output_ch])
  617. else:
  618. weights = self.get_randomized_data([self.filter_y, self.filter_x, self.input_ch, self.output_ch],
  619. self.kernel_table_file,
  620. regenerate=self.regenerate_new_weights)
  621. biases = self.get_randomized_bias_data(biases)
  622. conv = self.conv2d(input_data, self.reshape_conv_kernel(weights), biases)
  623. self.generate_c_array("input", self.convert_tensor(input_data, self.quantize_input))
  624. self.generate_c_array("weights", self.convert_tensor(weights, self.quantize_weights))
  625. self.generate_c_array("biases", self.convert_tensor(biases, self.quantize_bias), "int32_t")
  626. self.generate_c_array("output_ref", self.convert_tensor(conv, self.quantize_output))
  627. self.quantize_multiplier()
  628. self.write_c_config_header()
  629. self.write_c_header_wrapper()
  630. class SoftmaxSettings(TestSettings):
  631. softmax_input_integer_bits = 5
  632. def __init__(self, dataset, testtype, args, x_in=5, y_in=1, randmin=-7, randmax=7):
  633. super().__init__(dataset, testtype, args, 1, 1, x_in, y_in, 1, 1, 1, 1, False, randmin,
  634. randmax)
  635. self.output_scale = 1 / 256
  636. self.output_zero_point = -128
  637. self.x_input = self.x_output = x_in
  638. self.y_input = self.y_output = y_in
  639. input_real_multiplier = min(self.input_scale * (1 << (31 - self.softmax_input_integer_bits)),
  640. (1 << 31) - 1)
  641. (self.input_multiplier, self.input_left_shift) = self.quantize_scale(input_real_multiplier)
  642. self.diff_min = ((1 << self.softmax_input_integer_bits) - 1) * \
  643. (1 << (31 - self.softmax_input_integer_bits)) / \
  644. (1 << self.input_left_shift)
  645. self.diff_min = math.floor(self.diff_min)
  646. def write_c_config_header(self):
  647. super().write_c_config_header(write_common_parameters=False)
  648. filename = self.config_data
  649. filepath = self.headers_dir + filename
  650. prefix = self.testdataset.upper()
  651. with open(filepath, "a") as f:
  652. f.write("#define {}_NUM_ROWS {}\n".format(prefix, self.y_input))
  653. f.write("#define {}_ROW_SIZE {}\n".format(prefix, self.x_input))
  654. f.write("#define {}_INPUT_MULT {}\n".format(prefix, self.input_multiplier))
  655. f.write("#define {}_INPUT_LEFT_SHIFT {}\n".format(prefix, self.input_left_shift))
  656. f.write("#define {}_DIFF_MIN {}\n".format(prefix, -self.diff_min))
  657. f.write("#define {}_DST_SIZE {}\n".format(prefix, self.x_output * self.y_output))
  658. def get_softmax_randomized_input_data(self, input_data):
  659. # Generate or load saved input data unless hardcoded data provided.
  660. if input_data is not None:
  661. input_data = tf.reshape(input_data, [self.y_input, self.x_input])
  662. else:
  663. input_data = self.get_randomized_data([self.y_input, self.x_input],
  664. self.inputs_table_file,
  665. regenerate=self.regenerate_new_input)
  666. return input_data
  667. def softmax(self, indata):
  668. indata = tf.cast(indata, dtype=tf.dtypes.float32)
  669. out = tf.nn.softmax(indata)
  670. return out
  671. def generate_data(self, input_data=None, weights=None, biases=None):
  672. input_data = self.get_softmax_randomized_input_data(input_data)
  673. result = self.softmax(input_data)
  674. self.generate_c_array("input", self.convert_tensor(input_data, self.quantize_input))
  675. self.generate_c_array("output_ref", self.convert_tensor(result, self.quantize_output))
  676. self.write_c_config_header()
  677. self.write_c_header_wrapper()
  678. class SVDFSettings(TestSettings):
  679. def __init__(self, dataset, testtype, args, batches=2, number_inputs=2, rank=8, memory_size=10, randmin=-4,
  680. randmax=4, input_size=3, number_units=4, generate_bias=True):
  681. super().__init__(dataset, testtype, args, 1, 1, 1, 1, 1, 1, 1, 1, False, randmin,
  682. randmax, generate_bias=generate_bias)
  683. self.batches = batches
  684. self.number_units = number_units
  685. self.input_size = input_size
  686. self.memory_size = memory_size
  687. self.rank = rank
  688. self.number_filters = self.number_units * self.rank
  689. self.time_table_file = self.pregenerated_data_dir + self.testdataset + '/' + 'time_data.txt'
  690. self.state_table_file = self.pregenerated_data_dir + self.testdataset + '/' + 'state_data.txt'
  691. self.weights_feature_scale = 1
  692. self.weights_feature_scale = 1
  693. self.weights_time_scale = 1
  694. self.state_scale = 1
  695. self.bias_scale = 1
  696. self.number_inputs = number_inputs
  697. self.input_sequence_length = self.number_inputs * self.input_size * self.batches
  698. effective_scale_1 = self.weights_feature_scale * self.input_scale / self.state_scale
  699. effective_scale_2 = self.state_scale * self.weights_time_scale / self.output_scale
  700. (self.multiplier_in, self.shift_1) = self.quantize_scale(effective_scale_1)
  701. (self.multiplier_out, self.shift_2) = self.quantize_scale(effective_scale_2)
  702. self.in_activation_max = self.INT16_MAX
  703. self.in_activation_min = self.INT16_MIN
  704. def write_c_config_header(self):
  705. super().write_c_config_header(write_common_parameters=False)
  706. filename = self.config_data
  707. filepath = self.headers_dir + filename
  708. prefix = self.testdataset.upper()
  709. with open(filepath, "a") as f:
  710. self.write_c_header_offsets(f, prefix)
  711. f.write("#define {}_MULTIPLIER_IN {}\n".format(prefix, self.multiplier_in))
  712. f.write("#define {}_MULTIPLIER_OUT {}\n".format(prefix, self.multiplier_out))
  713. f.write("#define {}_SHIFT_1 {}\n".format(prefix, self.shift_1))
  714. f.write("#define {}_SHIFT_2 {}\n".format(prefix, self.shift_2))
  715. f.write("#define {}_IN_ACTIVATION_MIN {}\n".format(prefix, self.in_activation_min))
  716. f.write("#define {}_IN_ACTIVATION_MAX {}\n".format(prefix, self.in_activation_max))
  717. f.write("#define {}_RANK {}\n".format(prefix, self.rank))
  718. f.write("#define {}_FEATURE_BATCHES {}\n".format(prefix, self.number_filters))
  719. f.write("#define {}_TIME_BATCHES {}\n".format(prefix, self.memory_size))
  720. f.write("#define {}_INPUT_SIZE {}\n".format(prefix, self.input_size))
  721. f.write("#define {}_DST_SIZE {}\n".format(prefix, self.number_units * self.batches))
  722. f.write("#define {}_OUT_ACTIVATION_MIN {}\n".format(prefix, self.out_activation_min))
  723. f.write("#define {}_OUT_ACTIVATION_MAX {}\n".format(prefix, self.out_activation_max))
  724. f.write("#define {}_INPUT_BATCHES {}\n".format(prefix, self.batches))
  725. def quantize_weights_feature(self, value):
  726. result = round(value / self.weights_feature_scale)
  727. return self.clamp(result, self.INT8_MIN, self.INT8_MAX)
  728. def quantize_weights_time(self, value):
  729. result = round(value / self.weights_time_scale)
  730. return self.clamp(result, self.INT16_MIN, self.INT16_MAX)
  731. def quantize_state(self, value):
  732. result = round(value / self.state_scale)
  733. return self.clamp(result, self.INT16_MIN, self.INT16_MAX)
  734. def quantize_bias(self, value):
  735. result = round(value / self.bias_scale)
  736. return result
  737. def get_randomized_state_data(self, length, npfile, regenerate, minrange=None, maxrange=None):
  738. if not minrange:
  739. minrange = self.mins
  740. if not maxrange:
  741. maxrange = self.maxs
  742. data = []
  743. if not os.path.exists(npfile) or regenerate:
  744. regendir = os.path.dirname(npfile)
  745. if not os.path.exists(regendir):
  746. os.makedirs(regendir)
  747. for i in range(length):
  748. data.append(random.randint(self.mins, self.maxs))
  749. print("Saving data to {}".format(npfile))
  750. with open(npfile, 'w') as f:
  751. for listitem in data:
  752. f.write(str(listitem) + '\n')
  753. else:
  754. print("Loading data from {}".format(npfile))
  755. with open(npfile, 'r') as f:
  756. for line in f:
  757. data.append(int(line.strip()))
  758. return data
  759. def generate_data(self, input_data=None, weights=None, biases=None, time_data=None, state_data=None):
  760. if input_data is not None:
  761. input_data = tf.reshape(input_data, [self.input_sequence_length])
  762. else:
  763. input_data = self.get_randomized_data([self.input_sequence_length],
  764. self.inputs_table_file,
  765. regenerate=self.regenerate_new_input)
  766. self.generate_c_array("input_sequence", self.convert_tensor(input_data, self.quantize_input))
  767. if weights is not None:
  768. weights_feature_data = tf.reshape(weights, [self.number_filters, self.input_size])
  769. else:
  770. weights_feature_data = self.get_randomized_data([self.number_filters, self.input_size],
  771. self.kernel_table_file,
  772. regenerate=self.regenerate_new_weights)
  773. self.generate_c_array("weights_feature",
  774. self.convert_tensor(weights_feature_data, self.quantize_weights_feature))
  775. if time_data is not None:
  776. weights_time_data = tf.reshape(time_data, [self.number_filters, self.memory_size])
  777. else:
  778. weights_time_data = self.get_randomized_data([self.number_filters, self.memory_size],
  779. self.time_table_file,
  780. regenerate=self.regenerate_new_weights)
  781. self.generate_c_array("weights_time",
  782. self.convert_tensor(weights_time_data, self.quantize_weights_time), datatype='q15_t')
  783. if state_data is None:
  784. state_data = self.get_randomized_state_data(self.batches * self.memory_size * self.number_filters,
  785. self.state_table_file,
  786. regenerate=self.regenerate_new_weights)
  787. self.state_data = state_data
  788. state_data = tf.reshape(state_data, [self.batches, self.memory_size*self.number_filters])
  789. self.generate_c_array("state", self.convert_tensor(state_data, self.quantize_state), "q15_t")
  790. if not self.generate_bias:
  791. biases = [0] * self.number_units
  792. if biases is not None:
  793. biases = tf.reshape(biases, [self.number_units])
  794. else:
  795. biases = self.get_randomized_data([self.number_units],
  796. self.bias_table_file,
  797. regenerate=self.regenerate_new_weights)
  798. self.generate_c_array("biases", self.convert_tensor(biases, self.quantize_bias), "int32_t")
  799. # Generate reference output.
  800. svdf = None
  801. for i in range(self.number_inputs):
  802. start = i * self.input_size * self.batches
  803. end = i * self.input_size * self.batches + self.input_size * self.batches
  804. input_sequence = input_data[start:end]
  805. svdf = self.svdf(input_sequence, weights_feature_data, weights_time_data, biases)
  806. self.generate_c_array("output_ref", self.convert_tensor(svdf, self.quantize_output))
  807. self.write_c_config_header()
  808. self.write_c_header_wrapper()
  809. def svdf(self, indata, weights_feature, time_feature, bias=None):
  810. indata = tf.cast(indata, dtype=tf.dtypes.float32)
  811. weights_feature = tf.cast(weights_feature, dtype=tf.dtypes.float32)
  812. time_feature = tf.cast(time_feature, dtype=tf.dtypes.float32)
  813. bias = tf.cast(bias, dtype=tf.dtypes.float32)
  814. state_data = deque(self.state_data)
  815. state_data.rotate(-1)
  816. self.state_data = list(state_data)
  817. indata = tf.reshape(indata, [self.batches, 1, self.input_size])
  818. weights_feature = tf.reshape(weights_feature, [self.number_filters, self.input_size, 1])
  819. weights_feature = tf.transpose(weights_feature)
  820. scratch = tf.nn.conv1d(indata, weights_feature, stride=self.memory_size-1, padding=self.padding)
  821. out_size = self.batches * self.number_filters
  822. scratch = tf.reshape(scratch, [out_size])
  823. state_counter = self.memory_size - 1
  824. for i in range(out_size):
  825. self.state_data[state_counter] = scratch[i].numpy().ravel()[0]
  826. state_counter += self.memory_size
  827. time_feature = tf.reshape(time_feature, [self.number_filters * self.memory_size])
  828. scratch = []
  829. for b in range(self.batches):
  830. counter_t = 0
  831. counter_s = b * self.memory_size * self.number_filters
  832. for i in range(self.number_filters):
  833. dot_prod = 0
  834. for j in range(self.memory_size):
  835. dot_prod += self.state_data[counter_s] * time_feature[counter_t].numpy().ravel()[0]
  836. counter_t += 1
  837. counter_s += 1
  838. scratch.append(dot_prod)
  839. out = tf.constant(scratch, dtype=tf.dtypes.float32)
  840. out = tf.reshape(out, [self.number_units * self.batches, self.rank])
  841. out = tf.reduce_sum(out, axis=1)
  842. if self.generate_bias:
  843. out = tf.reshape(out, [self.batches, self.number_units])
  844. output_list = []
  845. for b in range(self.batches):
  846. output_list.append(tf.add(out[b], bias))
  847. outputs = tf.stack(output_list)
  848. out = outputs
  849. out = tf.clip_by_value(out, self.out_activation_min, self.out_activation_max)
  850. return out
  851. def load_all_testdatasets():
  852. """
  853. Add all new testdata sets here
  854. """
  855. type_of_test = 'conv'
  856. dataset = 'basic'
  857. ALL_TESTDATA_SETS[dataset] = ConvSettings(dataset, type_of_test, args, in_ch=1, out_ch=1, x_in=5,
  858. y_in=8, w_x=2, w_y=4, stride_x=1, stride_y=1, pad=False,
  859. randmin=1, randmax=4)
  860. dataset = 'stride2pad1'
  861. ALL_TESTDATA_SETS[dataset] = ConvSettings(dataset, type_of_test, args, in_ch=1, out_ch=1, x_in=7,
  862. y_in=7, w_x=3, w_y=3, stride_x=2, stride_y=2, pad=True,
  863. randmin=1, randmax=4)
  864. dataset = 'kernel1x1'
  865. ALL_TESTDATA_SETS[dataset] = ConvSettings(dataset, type_of_test, args, in_ch=4, out_ch=17, x_in=15,
  866. y_in=15, w_x=1, w_y=1, stride_x=1, stride_y=1, pad=False,
  867. randmin=1, randmax=4, outminrange=-126, outmaxrange=127)
  868. dataset = 'conv_3'
  869. ALL_TESTDATA_SETS[dataset] = ConvSettings(dataset, type_of_test, args, in_ch=3, out_ch=1, x_in=10, y_in=49, w_x=4,
  870. w_y=10, stride_x=1, stride_y=2, pad=True, randmin=-2, randmax=2,
  871. outminrange=-127, outmaxrange=127)
  872. dataset = 'conv_1_x_n_1'
  873. ALL_TESTDATA_SETS[dataset] = ConvSettings(dataset, type_of_test, args, in_ch=3, out_ch=3, x_in=5, y_in=5, w_x=2,
  874. w_y=1, stride_x=2, stride_y=1, pad=False, randmin=-2, randmax=2,
  875. outminrange=-127, outmaxrange=127, batches=2)
  876. dataset = 'conv_1_x_n_2'
  877. ALL_TESTDATA_SETS[dataset] = ConvSettings(dataset, type_of_test, args, in_ch=3, out_ch=1, x_in=11, y_in=11, w_x=11,
  878. w_y=1, stride_x=1, stride_y=1, pad=True, randmin=-2, randmax=2,
  879. outminrange=-127, outmaxrange=127)
  880. dataset = 'conv_1_x_n_3'
  881. ALL_TESTDATA_SETS[dataset] = ConvSettings(dataset, type_of_test, args, in_ch=1, out_ch=3, x_in=11, y_in=11, w_x=1,
  882. w_y=11, stride_x=1, stride_y=1, pad=True, randmin=-2, randmax=2,
  883. outminrange=-127, outmaxrange=127)
  884. dataset = 'conv_2'
  885. ALL_TESTDATA_SETS[dataset] = ConvSettings(dataset, type_of_test, args, in_ch=2, out_ch=4, x_in=6, y_in=3, w_x=3,
  886. w_y=3, stride_x=1, stride_y=1, pad=True, randmin=1, randmax=4,
  887. outminrange=-126, outmaxrange=127)
  888. dataset = 'conv_4' # batches > 2
  889. ALL_TESTDATA_SETS[dataset] = ConvSettings(dataset, type_of_test, args, in_ch=3, out_ch=3, x_in=5, y_in=5, w_x=2,
  890. w_y=3, stride_x=2, stride_y=2, pad=False, randmin=-2, randmax=2,
  891. outminrange=-127, outmaxrange=127, batches=3)
  892. dataset = 'conv_out_activation'
  893. ALL_TESTDATA_SETS[dataset] = ConvSettings(dataset, type_of_test, args, in_ch=2, out_ch=2, x_in=3, y_in=3, w_x=3,
  894. w_y=3, stride_x=1, stride_y=1, pad=True, randmin=-5, randmax=5,
  895. out_activation_min=-55, out_activation_max=55)
  896. type_of_test = 'depthwise_conv'
  897. dataset = 'depthwise_2'
  898. ALL_TESTDATA_SETS[dataset] = ConvSettings(dataset, type_of_test, args, in_ch=3, out_ch=9, x_in=6, y_in=5, w_x=3,
  899. w_y=4, stride_x=2, stride_y=2, pad=True, randmin=-2, randmax=2,
  900. outminrange=-126, outmaxrange=127)
  901. dataset = 'depthwise_kernel_3x3'
  902. ALL_TESTDATA_SETS[dataset] = ConvSettings(dataset, type_of_test, args, in_ch=5, out_ch=5, x_in=4, y_in=5, w_x=3,
  903. w_y=3, stride_x=2, stride_y=2, pad=True, randmin=-2, randmax=2,
  904. outminrange=-126, outmaxrange=127)
  905. dataset = 'depthwise_eq_in_out_ch'
  906. ALL_TESTDATA_SETS[dataset] = ConvSettings(dataset, type_of_test, args, in_ch=6, out_ch=6, x_in=4, y_in=5, w_x=2,
  907. w_y=3, stride_x=1, stride_y=1, pad=True, randmin=-2, randmax=2,
  908. outminrange=-126, outmaxrange=127)
  909. dataset = 'depthwise_out_activation'
  910. ALL_TESTDATA_SETS[dataset] = ConvSettings(dataset, type_of_test, args, in_ch=3, out_ch=3, x_in=6, y_in=5, w_x=3,
  911. w_y=4, pad=False, randmin=-7, randmax=7, out_activation_min=-31,
  912. out_activation_max=24)
  913. dataset = 'depthwise_mult_batches'
  914. ALL_TESTDATA_SETS[dataset] = ConvSettings(dataset, type_of_test, args, in_ch=3, out_ch=3, x_in=3, y_in=5, w_x=2,
  915. w_y=4, stride_x=2, stride_y=2, pad=True, randmin=-2, randmax=2,
  916. batches=2)
  917. type_of_test = 'fully_connected'
  918. dataset = 'fully_connected'
  919. ALL_TESTDATA_SETS[dataset] = FullyConnectedSettings(dataset, type_of_test, args, in_ch=10, out_ch=6, x_in=2, y_in=1,
  920. w_x=2, w_y=1, batches=3, input_zero_point=-50,
  921. output_zero_point=-2)
  922. dataset = 'fully_connected_mve_0'
  923. ALL_TESTDATA_SETS[dataset] = FullyConnectedSettings(dataset, type_of_test, args, in_ch=16, out_ch=9, x_in=1, y_in=1,
  924. input_zero_point=-3, w_x=1, w_y=1, batches=1,
  925. output_zero_point=-2)
  926. dataset = 'fully_connected_mve_1'
  927. ALL_TESTDATA_SETS[dataset] = FullyConnectedSettings(dataset, type_of_test, args, in_ch=20, out_ch=4, x_in=1, y_in=1,
  928. input_zero_point=-1, w_x=1, w_y=1, batches=1,
  929. output_zero_point=3)
  930. dataset = 'fully_connected_null_bias_0'
  931. ALL_TESTDATA_SETS[dataset] = FullyConnectedSettings(dataset, type_of_test, args, in_ch=33, out_ch=5,
  932. input_zero_point=-1, batches=2, generate_bias=False)
  933. dataset = 'fully_connected_out_activation'
  934. ALL_TESTDATA_SETS[dataset] = FullyConnectedSettings(dataset, type_of_test, args, in_ch=10, out_ch=4, randmin=-15,
  935. randmax=15, input_zero_point=0, output_zero_point=0,
  936. out_activation_min=-105, out_activation_max=120)
  937. type_of_test = 'avgpool'
  938. dataset = 'avgpooling'
  939. ALL_TESTDATA_SETS[dataset] = PoolingSettings(dataset, type_of_test, args, channels=8, x_in=22, y_in=12, stride_x=9,
  940. stride_y=5, w_x=6, w_y=5, pad=True)
  941. dataset = 'avgpooling_1'
  942. ALL_TESTDATA_SETS[dataset] = PoolingSettings(dataset, type_of_test, args, channels=3, x_in=9, y_in=5, stride_x=1,
  943. stride_y=2, w_x=9, w_y=5, pad=False)
  944. dataset = 'avgpooling_2'
  945. ALL_TESTDATA_SETS[dataset] = PoolingSettings(dataset, type_of_test, args, channels=5, x_in=12, y_in=1, stride_x=1,
  946. stride_y=2, w_x=3, w_y=1, pad=True)
  947. dataset = 'avgpooling_3'
  948. ALL_TESTDATA_SETS[dataset] = PoolingSettings(dataset, type_of_test, args, channels=2, x_in=9, y_in=1, stride_x=2,
  949. stride_y=1, w_x=1, w_y=1, pad=False)
  950. dataset = 'avgpooling_4'
  951. ALL_TESTDATA_SETS[dataset] = PoolingSettings(dataset, type_of_test, args, channels=2, x_in=1, y_in=20, stride_x=1,
  952. stride_y=3, w_x=1, w_y=3, pad=True)
  953. dataset = 'avgpooling_5'
  954. ALL_TESTDATA_SETS[dataset] = PoolingSettings(dataset, type_of_test, args, randmin=0, channels=1, x_in=3, y_in=3,
  955. stride_x=1, stride_y=1, w_x=1, w_y=3, pad=True, relu6=True)
  956. type_of_test = 'maxpool'
  957. dataset = 'maxpooling'
  958. ALL_TESTDATA_SETS[dataset] = PoolingSettings(dataset, type_of_test, args, channels=8, x_in=22, y_in=12, stride_x=9,
  959. stride_y=5, w_x=6, w_y=5, pad=True)
  960. dataset = 'maxpooling_1'
  961. ALL_TESTDATA_SETS[dataset] = PoolingSettings(dataset, type_of_test, args, channels=3, x_in=9, y_in=5, stride_x=1,
  962. stride_y=2, w_x=9, w_y=5, pad=False)
  963. dataset = 'maxpooling_2'
  964. ALL_TESTDATA_SETS[dataset] = PoolingSettings(dataset, type_of_test, args, channels=5, x_in=12, y_in=1, stride_x=1,
  965. stride_y=2, w_x=3, w_y=1, pad=True)
  966. dataset = 'maxpooling_3'
  967. ALL_TESTDATA_SETS[dataset] = PoolingSettings(dataset, type_of_test, args, channels=2, x_in=9, y_in=1, stride_x=2,
  968. stride_y=1, w_x=1, w_y=1, pad=False)
  969. dataset = 'maxpooling_4'
  970. ALL_TESTDATA_SETS[dataset] = PoolingSettings(dataset, type_of_test, args, channels=2, x_in=1, y_in=20, stride_x=1,
  971. stride_y=3, w_x=1, w_y=3, pad=True)
  972. dataset = 'maxpooling_5'
  973. ALL_TESTDATA_SETS[dataset] = PoolingSettings(dataset, type_of_test, args, channels=20, x_in=1, y_in=1, stride_x=1,
  974. stride_y=1, w_x=1, w_y=1, pad=True)
  975. dataset = 'maxpooling_6'
  976. ALL_TESTDATA_SETS[dataset] = PoolingSettings(dataset, type_of_test, args, channels=17, x_in=1, y_in=5, stride_x=1,
  977. stride_y=3, w_x=3, w_y=4, pad=True)
  978. dataset = 'maxpooling_7'
  979. ALL_TESTDATA_SETS[dataset] = PoolingSettings(dataset, type_of_test, args, channels=1, x_in=4, y_in=2, stride_x=2,
  980. stride_y=2, w_x=2, w_y=2, pad=False, randmin=-20, randmax=-5,
  981. relu6=True)
  982. type_of_test = 'softmax'
  983. dataset = 'softmax'
  984. ALL_TESTDATA_SETS[dataset] = SoftmaxSettings(dataset, type_of_test, args, x_in=5, y_in=1)
  985. type_of_test = 'svdf'
  986. dataset = 'svdf'
  987. ALL_TESTDATA_SETS[dataset] = SVDFSettings(dataset, type_of_test, args, batches=2, number_inputs=2, rank=8,
  988. memory_size=8, input_size=3, number_units=3)
  989. type_of_test = 'svdf'
  990. dataset = 'svdf_1'
  991. ALL_TESTDATA_SETS[dataset] = SVDFSettings(dataset, type_of_test, args, batches=3, number_inputs=2, rank=1,
  992. memory_size=2, input_size=7, number_units=5)
  993. type_of_test = 'svdf'
  994. dataset = 'svdf_2'
  995. ALL_TESTDATA_SETS[dataset] = SVDFSettings(dataset, type_of_test, args, batches=3, number_inputs=2, rank=2,
  996. memory_size=2, input_size=7, number_units=5, generate_bias=False)
  997. type_of_test = 'svdf'
  998. dataset = 'svdf_3'
  999. ALL_TESTDATA_SETS[dataset] = SVDFSettings(dataset, type_of_test, args, batches=1, number_inputs=2, rank=1,
  1000. memory_size=2, input_size=20, number_units=12, generate_bias=False)
  1001. if __name__ == '__main__':
  1002. if version.parse(tf.__version__) < REQUIRED_MINIMUM_TENSORFLOW_VERSION:
  1003. print("Unsupported tensorflow version, ", version.parse(tf.__version__))
  1004. sys.exit(0)
  1005. args = parse_args()
  1006. testdataset = args.dataset
  1007. test_type = args.testtype
  1008. load_all_testdatasets()
  1009. if (args.run_all_testsets):
  1010. for testset_name, testset_generator in ALL_TESTDATA_SETS.items():
  1011. print("Generating testset {}..".format(testset_name))
  1012. testset_generator.generate_data()
  1013. print()
  1014. # Check that all testsets have been loaded.
  1015. found_test_data_sets = []
  1016. directory = 'TestCases/TestData'
  1017. for dir in next(os.walk(directory))[1]:
  1018. found_test_data_sets.append(dir)
  1019. for testset_name in found_test_data_sets:
  1020. if testset_name not in ALL_TESTDATA_SETS:
  1021. print("WARNING: Testset {} in {} was not loaded".format(testset_name, directory))
  1022. else:
  1023. try:
  1024. generator = ALL_TESTDATA_SETS[testdataset]
  1025. except KeyError:
  1026. print("WARNING: testset {} not in testset list".format(testdataset))
  1027. if args.testtype == 'conv' or args.testtype == 'depthwise_conv':
  1028. generator = ConvSettings(testdataset, test_type, args)
  1029. elif args.testtype == 'fully_connected':
  1030. generator = FullyConnectedSettings(testdataset, test_type, args)
  1031. elif args.testtype == 'avgpool' or args.testtype == 'maxpool':
  1032. generator = PoolingSettings(testdataset, test_type, args)
  1033. generator.generate_data()