HGQ QMHA and QLinformer support for oneAPI #1432
Conversation
|
How are the names of tables associated with the layers? Is it now per-instance? |
| copyfile(srcpath, dstpath) | ||
|
|
||
| def __get_table_size(self, model, activation): | ||
| def __get_table_size(self, model, activation, table_name='table_size'): |
There was a problem hiding this comment.
table_name and table_size have very different meanings. Maybe table_name_size or something like that would read better?
|
I assume this also needs changes in |
|
Tests should be written (or hgq tests enabled) to show that this works as expected. |
|
Scratch that about the Libero backend. Apparently it's just cut and paste code that has not been used or tested. The Libero backend doesn't yet support softmax and other table lookup functions. |
There was a problem hiding this comment.
Pull request overview
This PR aims to enable HGQ-based QMHA/QLinformer support on the oneAPI backend by (1) parsing/using HGQ softmax LUTs and (2) adding infrastructure for multi-dimensional softmax in oneAPI firmware templates.
Changes:
- Add a new insertion point in oneAPI
parameters.hand writer logic to include per-layer softmax table headers. - Refactor oneAPI softmax stable implementation to use per-layer exp/invert tables via pointers in
CONFIG_T(instead of#includeing.tbfiles inside the function). - Add a multi-dimensional softmax helper in the oneAPI activation header and update oneAPI template generation to emit per-layer table pointers/types/sizes.
Reviewed changes
Copilot reviewed 6 out of 6 changed files in this pull request and generated 8 comments.
Show a summary per file
| File | Description |
|---|---|
hls4ml/writer/oneapi_writer.py |
Generates and includes per-softmax LUT headers in the oneAPI output project. |
hls4ml/templates/oneapi/firmware/parameters.h |
Adds a marker for writer-inserted softmax table includes. |
hls4ml/templates/oneapi/firmware/nnet_utils/nnet_activation.h |
Switches stable softmax to use CONFIG_T table pointers; adds multidim softmax helper. |
hls4ml/templates/oneapi/firmware/nnet_utils/nnet_activation_stream.h |
Mirrors stable softmax table-pointer usage for streaming implementation. |
hls4ml/backends/oneapi/passes/core_templates.py |
Updates generated softmax config to include exp/inv table sizes and pointers. |
hls4ml/backends/oneapi/oneapi_backend.py |
Removes the previous oneAPI constraint that blocked multidimensional softmax in io_parallel. |
Comments suppressed due to low confidence (1)
hls4ml/backends/oneapi/oneapi_backend.py:263
- Without the
Softmaxinitializer, multidimensional Softmax withIOType=io_parallelis no longer rejected even though oneAPI codegen does not currently switch to the newsoftmax_multidimkernel. This can produce incorrect outputs (softmax applied across the flattened tensor). Either add the multidim function-call selection in codegen, or re-introduce the initializer guard until that wiring is in place.
@layer_optimizer(Activation)
def init_activation(self, layer):
if layer.get_attr('activation') == 'tanh':
layer.set_attr('activation', 'dense_tanh')
if layer.get_attr('recurrent_activation') == 'tanh':
layer.set_attr('recurrent_activation', 'dense_tanh')
@layer_optimizer(Embedding)
def init_embed(self, layer):
if layer.attributes['n_in'] is None:
raise Exception('Input length of Embedding layer must be specified.')
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| h_file.write(f'#ifndef {header_name.upper()}_H_\n') | ||
| h_file.write(f'#define {header_name.upper()}_H_\n\n') | ||
|
|
||
| h_file.write(f'static constexpr {table_name}_t {table_name}[{table_size}] = {{') |
| h_file.write(f'#ifndef {header_name.upper()}_H_\n') | ||
| h_file.write(f'#define {header_name.upper()}_H_\n\n') | ||
|
|
||
| h_file.write(f'static constexpr {table_name}_t {table_name}[{table_size}] = {{') |
| elif '// hls-fpga-machine-learning insert softmax tables' in line: | ||
| newline = line | ||
| for layer in model.get_layers(): | ||
| if 'softmax' in layer.name: | ||
| newline += f'#include "nnet_utils/activation_tables/{layer.name}_exp_table.h"\n' | ||
| newline += f'#include "nnet_utils/activation_tables/{layer.name}_inv_table.h"\n' | ||
|
|
| for layer in model.get_layers(): | ||
| if layer.name == 'softmax': | ||
| ac_type = layer.get_input_variable().type | ||
| if ac_type is not None: | ||
| try: | ||
| fp_bits = ac_type.precision.integer + ac_type.precision.fractional | ||
| fp_integer = ac_type.precision.integer | ||
| fp_signed = ac_type.precision.signed | ||
| except Exception: | ||
| # FixedPrecisionType wasn't correctly stored in layer attributes, use default values | ||
| pass | ||
| if fp_signed is False: | ||
| raise Exception('Softmax types need to be signed') | ||
| if 'softmax' in layer.name: | ||
| table_name = layer.name + '_exp_table' |
| for layer in model.get_layers(): | ||
| if layer.name == 'softmax': | ||
| ac_type = layer.get_attr('exp_table_t') | ||
| if ac_type is not None: | ||
| try: | ||
| fp_bits = ac_type.precision.integer + ac_type.precision.fractional | ||
| fp_integer = ac_type.precision.integer | ||
| fp_signed = ac_type.precision.signed | ||
| except Exception: | ||
| # FixedPrecisionType wasn't correctly stored in layer attributes, use default values | ||
| pass | ||
| if fp_signed is False: | ||
| raise Exception('Softmax types need to be signed') | ||
|
|
||
| sep = '' | ||
| N = ceil_log2(table_size) | ||
| for i in range(table_size): | ||
| f = FixedPointEmulator(fp_bits, fp_integer, signed=fp_signed) | ||
| b = uint_to_binary(i, N) | ||
| b.insert(0, 0) | ||
| f.set_msb_bits(b) | ||
| real_val = f.inv_float() | ||
| h_file.write(sep + str(real_val)) | ||
| sep = ', ' | ||
| if 'softmax' in layer.name: | ||
| table_name = layer.name + '_inv_table' | ||
| table_size = ( |
| 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 {exp_table_t.name} exp_table_t; | ||
| typedef {inv_table_t.name} inv_table_t; | ||
| typedef {inv_table_t.name} inv_table_t;""" |
| params['exp_table_t'].precision.signed = False | ||
|
|
||
| if 'inp_norm_t' not in params: |
| EinsumDense, | ||
| Embedding, | ||
| Layer, | ||
| SimpleRNN, | ||
| Softmax, | ||
| ) |
Description
This PR adds the required changes to enable QMHA/QLinformer in oneAPI.
Requires parsing softmax tables from HGQ and support for multidim softmax.
Type of change
Tests
Test Configuration:
Checklist
pre-commiton the files I edited or added.