-
Notifications
You must be signed in to change notification settings - Fork 583
Softmax update #1494
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Softmax update #1494
Changes from 37 commits
3d463b3
d678573
77258bc
59bd96f
dbb207b
0c59255
51efff0
6067bea
06fda4e
bf38a6b
e27fd11
9f4a448
16ca197
564b692
974e75a
060c398
f78558c
772b93a
d2b8921
a1ad891
d65544d
2d6a5cc
c3a4584
9b1cf17
31b7ad6
70b19d1
091cd8e
6c9f9a7
7a004b2
9d588f2
d5e3493
a56a8d6
29bdbb3
cab4cbc
42ece34
7e2798a
bd4778e
3946858
be76917
189f64a
e0aba71
584c4f7
711083a
6309fdb
b91a330
f990dbd
5bee49a
134787b
a90b79a
07ad4a4
aa5af6b
49d6ccd
3d7979a
aa678e0
5dadf1a
d8abde4
55e7454
947f195
10532da
232b2a3
84e0710
01d70a4
8d8bc8a
9da60c2
84cdcf4
39dc0d5
7ee6022
865662c
755cc57
f4dbefa
82cbe41
9baf080
6288d0d
640f204
314c1dc
e5bf4af
18a2f6c
e059fa1
1723aae
d1f6d61
7a179d9
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -2,6 +2,7 @@ | |
| from hls4ml.backends.oneapi.oneapi_template import StreamFunctionCallTemplate, TaskSequenceTemplate | ||
| from hls4ml.backends.template import FunctionCallTemplate, LayerConfigTemplate | ||
| from hls4ml.model.layers import Activation, BatchNormalization, Dense, HardActivation, ParametrizedActivation, PReLU, Softmax | ||
| from hls4ml.utils.fixed_point_utils import ceil_log2 | ||
|
|
||
| # Dense templates | ||
|
|
||
|
|
@@ -195,11 +196,32 @@ def format(self, node): | |
| softmax_config_template = """struct {type}_config{index} : nnet::activ_config {{ | ||
| static constexpr unsigned n_in = {n_in}; | ||
| static constexpr unsigned table_size = {table_size}; | ||
| static constexpr unsigned exp_table_size = {exp_table_size}; | ||
| static constexpr unsigned inv_table_size = {inv_table_size}; | ||
| static constexpr unsigned io_type = nnet::{iotype}; | ||
| static constexpr unsigned reuse_factor = {reuse}; | ||
|
|
||
| static constexpr nnet::softmax_implementation implementation = nnet::softmax_implementation::{implementation}; | ||
| typedef {smax_accum_t} accum_t; | ||
| typedef {exp_table_t.name} exp_table_t; | ||
| typedef {inv_table_t.name} inv_table_t; | ||
| typedef {inv_table_t.name} inv_table_t;""" | ||
|
|
||
| softmax_config_table_template = """ | ||
|
|
||
| using {exp_table_name}_arr_t = nnet::array<exp_table_t, exp_table_size>; | ||
| using {inv_table_name}_arr_t = nnet::array<inv_table_t, inv_table_size>; | ||
| static constexpr const {exp_table_name}_arr_t exp_table = {exp_table_name}; | ||
| static constexpr const {inv_table_name}_arr_t invert_table = {inv_table_name}; | ||
|
jmitrevs marked this conversation as resolved.
|
||
| }};\n""" | ||
|
|
||
| softmax_config_table_template_stable = """ | ||
| typedef {inv_inp_t.name} inv_inp_t; | ||
| typedef {inp_norm_t.name} inp_norm_t; | ||
|
|
||
| using {exp_table_name}_arr_t = nnet::array<exp_table_t, exp_table_size>; | ||
| using {inv_table_name}_arr_t = nnet::array<inv_table_t, inv_table_size>; | ||
| static constexpr const {exp_table_name}_arr_t exp_table = {exp_table_name}; | ||
| static constexpr const {inv_table_name}_arr_t invert_table = {inv_table_name}; | ||
| }};\n""" | ||
|
|
||
| activ_function_template = 'nnet::{activation}<{input_t}, {output_t}, {config}>({input}, {output});' | ||
|
|
@@ -221,6 +243,71 @@ def format(self, node): | |
| params = self._default_config_params(node) | ||
| params['type'] = node.get_attr('activation') | ||
|
|
||
| if params['type'] == 'softmax': | ||
| # The lookup input (x - x_max) is always <= 0, so only the negative half | ||
| if 'exp_table_size' in params and params['exp_table_size'] is not None: | ||
| params['exp_table_size'] //= 2 | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I would not divide this in half. The table size as given already takes the unsignedness into account. It doesn't make sense to expect people to give you twice the size of what they want implemented.
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Fixed this problem by assuming positive only types for softmax tables |
||
| else: | ||
| # Use the default precision | ||
| params['exp_table_size'] = 2 ** (params['table_t'].precision.width - 1) | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The default parameters should be defined https://github.com/fastmachinelearning/hls4ml/blob/main/hls4ml/backends/fpga/fpga_backend.py#L130 and similar. Note that the defaults are updated in #1476. I would remove all these updates here. You set the defaults in the attribute, not when reading the attributes.
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Removed this logic, respecting the new defaults |
||
| params['exp_table_t'].precision.width = ceil_log2(params['exp_table_size']) | ||
| params['exp_table_t'].precision.integer = params['table_t'].precision.integer - 1 | ||
| params['exp_table_t'].precision.signed = False | ||
|
|
||
| params.setdefault('table_size', params['exp_table_size']) # Not sure if necessary | ||
|
|
||
| # Determine accumulator type if present, else derive it yourself based on the input size. | ||
| if params['accum_t'].name == 'model_default_t': | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This should be in
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Removed |
||
| extra_bits_req = ceil_log2(params['n_in']) | ||
| s = 'true' if params['exp_table_t'].precision.signed else 'false' | ||
| w = params['exp_table_t'].precision.width + extra_bits_req | ||
| i = params['exp_table_t'].precision.integer + extra_bits_req | ||
| params['smax_accum_t'] = f'ac_fixed<{str(w)},{str(i)},{s}>' | ||
| else: | ||
| params['smax_accum_t'] = params['accum_t'].name | ||
|
|
||
| if 'inp_norm_t' not in params: | ||
| input_t = node.get_input_variable().type.precision | ||
| width, iwidth, signed = input_t.width, input_t.integer, input_t.signed # noqa: F841 | ||
| width, iwidth = width - signed, iwidth - signed | ||
| import copy | ||
|
|
||
| params['inp_norm_t'] = copy.deepcopy(params['exp_table_t']) # assign type,later override | ||
|
|
||
| # This checks if table sizes will be default, if it is just use the table size to derive precision | ||
| if 'inv_table_size' not in params: | ||
| params['inp_norm_t'].precision.width = params['exp_table_t'].precision.width + 1 | ||
| params['inp_norm_t'].precision.integer = params['exp_table_t'].precision.integer + 1 | ||
| params['inp_norm_t'].precision.signed = True | ||
| params['inp_norm_t'].name = f'{node.name}_inp_norm_t' | ||
| else: | ||
| params[ | ||
| 'inp_norm_t' | ||
| ].name = f'ac_fixed<{width},{iwidth},{"true" if signed else "false"},AC_RND,AC_SAT_SYM>' | ||
|
|
||
| node.set_attr('inp_norm_t', params['inp_norm_t']) | ||
|
|
||
| # Again we only look up 1/sum(e^x) which is >=0 so no need the entie address space | ||
| if 'inv_table_size' in params: | ||
| params['inv_table_size'] //= 2 | ||
| else: | ||
| params['inv_table_size'] = 2 ** (params['table_t'].precision.width - 1) | ||
| params['inv_table_t'].precision.width = ceil_log2(params['inv_table_size']) | ||
| params['inv_table_t'].precision.integer = params['table_t'].precision.integer - 1 | ||
| params['inv_table_t'].precision.signed = False | ||
|
|
||
| params['inv_inp_t'].precision.width = params['inv_table_t'].precision.width + 1 | ||
| params['inv_inp_t'].precision.integer = params['inv_table_t'].precision.integer + 1 | ||
| params['inv_inp_t'].precision.signed = True | ||
|
|
||
| if params['implementation'] == 'stable': | ||
| self.template = softmax_config_template + softmax_config_table_template_stable | ||
| else: | ||
| self.template = softmax_config_template + softmax_config_table_template | ||
|
|
||
| params['exp_table_name'] = node.name + '_exp_table' | ||
| params['inv_table_name'] = node.name + '_inv_table' | ||
|
|
||
| return self.template.format(**params) | ||
|
|
||
|
|
||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.