Skip to content

Commit 1065a09

Browse files
committed
1D Convolution & Pooling Quartus PyTests
1 parent e5bff4a commit 1065a09

File tree

3 files changed

+86
-66
lines changed

3 files changed

+86
-66
lines changed

test/pytest/test_conv1d.py

Lines changed: 44 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,10 @@
1+
from distutils.command.config import config
12
from hls4ml.converters.keras_to_hls import keras_to_hls
23
import pytest
34
import hls4ml
45
import numpy as np
56
from sklearn.metrics import accuracy_score
6-
import tensorflow as tf
77
from tensorflow.keras.models import model_from_json
8-
import yaml
98
from pathlib import Path
109

1110
test_root_path = Path(__file__).parent
@@ -25,46 +24,58 @@ def keras_model():
2524
model.load_weights(example_model_path / 'keras/KERAS_conv1d_weights.h5')
2625
return model
2726

28-
@pytest.fixture
29-
@pytest.mark.parametrize('settings', [('io_parallel', 'latency'),
30-
('io_parallel', 'resource'),
31-
('io_stream', 'latency'),
32-
('io_stream', 'resource')])
33-
def hls_model(settings):
34-
io_type = settings[0]
35-
strategy = settings[1]
36-
config = hls4ml.converters.create_config(output_dir = 'hls4mlprj_conv1d_{}_{}'.format(io_type, strategy))
37-
config['KerasJson'] = str(example_model_path / 'keras/KERAS_conv1d.json')
38-
config['KerasH5'] = str(example_model_path / 'keras/KERAS_conv1d_weights.h5')
39-
config['OutputDir'] = str(test_root_path / 'hls4mlprj_conv1d_{}_{}'.format(io_type, strategy))
40-
config['IOType'] = io_type
27+
@pytest.fixture
28+
@pytest.mark.parametrize('backend, io_type, strategy', [
29+
('Quartus', 'io_parallel', 'resource'),
30+
('Vivado', 'io_parallel', 'resource'),
31+
32+
('Vivado', 'io_parallel', 'latency'),
33+
34+
('Vivado', 'io_stream', 'latency'),
35+
('Vivado', 'io_stream', 'resource')
36+
])
37+
def hls_model(keras_model, backend, io_type, strategy):
38+
default_precision = 'ap_fixed<16,3,AP_RND_CONV,AP_SAT>' if backend=='Vivado' else 'ac_fixed<16,3,true,AC_RND_CONV,AC_SAT>'
39+
fc1_weight_precision = 'ap_fixed<16,3>' if backend=='Vivado' else 'ac_fixed<16,3,true>'
40+
fc1_result_precision = 'ap_fixed<16,6,AP_RND_CONV,AP_SAT>' if backend=='Vivado' else 'ac_fixed<16,6,true,AC_RND_CONV,AC_SAT>'
41+
output_softmax_weight_precision = 'ap_fixed<16,6>' if backend=='Vivado' else 'ac_fixed<16,6,true>'
42+
output_softmax_result_precision = 'ap_fixed<16,6,AP_RND_CONV,AP_SAT>' if backend=='Vivado' else 'ac_fixed<16,6,true,AP_RND_CONV,AP_SAT>'
43+
44+
# Default config
45+
hls_config = hls4ml.utils.config_from_keras_model(keras_model)
46+
hls_config['Model']['Strategy'] = strategy
47+
hls_config['Model']['ReuseFactor'] = 1
48+
hls_config['Model']['Precision'] = default_precision
49+
50+
# Some model-specific precision tuning
51+
hls_config['LayerName'] = {}
52+
hls_config['LayerName']['fc1_relu'] = {'Precision':{'weight' : fc1_weight_precision, 'result' : fc1_result_precision}}
53+
hls_config['LayerName']['output_softmax'] = {'Precision':{'weight' : output_softmax_weight_precision, 'result' : output_softmax_result_precision}}
54+
hls_config['LayerName']['output_softmax_softmax'] = {'Strategy':'Stable'}
4155

42-
hls_config = {'Model' : {'Strategy' : strategy,
43-
'ReuseFactor' : 1,
44-
'Precision' : 'ap_fixed<16,3,AP_RND_CONV,AP_SAT>'}}
45-
# Some model specific precision tuning
46-
config['LayerName'] = {}
47-
config['LayerName']['fc1_relu'] = {'Precision':{'weight' : 'ap_fixed<16,3>', 'result' : 'ap_fixed<16,6,AP_RND_CONV,AP_SAT>'}}
48-
config['LayerName']['output_softmax'] = {'Precision':{'weight' : 'ap_fixed<16,6>', 'result' : 'ap_fixed<16,6,AP_RND_CONV,AP_SAT>'}}
49-
config['LayerName']['output_softmax_softmax'] = {'Strategy':'Stable'}
50-
config['HLSConfig'] = hls_config
51-
hls_model = keras_to_hls(config)
56+
output_dir = str(test_root_path / 'hls4mlprj_conv1d_{}_{}_{}'.format(backend, io_type, strategy))
57+
hls_model = hls4ml.converters.convert_from_keras_model(keras_model, hls_config=hls_config, backend=backend, io_type=io_type, output_dir=output_dir)
5258
hls_model.compile()
5359
return hls_model
5460

55-
@pytest.mark.parametrize('settings', [('io_parallel', 'latency'),
56-
('io_parallel', 'resource'),
57-
('io_stream', 'latency'),
58-
('io_stream', 'resource')])
61+
@pytest.mark.parametrize('backend, io_type, strategy', [
62+
('Quartus', 'io_parallel', 'resource'),
63+
('Vivado', 'io_parallel', 'resource'),
64+
65+
('Vivado', 'io_parallel', 'latency'),
66+
67+
('Vivado', 'io_stream', 'latency'),
68+
('Vivado', 'io_stream', 'resource')
69+
])
5970
def test_accuracy(data, keras_model, hls_model):
6071
X = data
6172
model = keras_model
62-
# model under test predictions and accuracy
73+
74+
# Model under test predictions and accuracy
6375
y_keras = model.predict(X)
6476
y_hls4ml = hls_model.predict(X)
65-
# "accuracy" of hls4ml predictions vs keras
77+
78+
# "Accuracy" of hls4ml predictions vs keras
6679
rel_acc = accuracy_score(np.argmax(y_keras, axis=1), np.argmax(y_hls4ml, axis=1))
67-
6880
print('hls4ml accuracy relative to keras: {}'.format(rel_acc))
69-
7081
assert rel_acc > 0.98

test/pytest/test_keras_api.py

Lines changed: 24 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -90,15 +90,13 @@ def test_activations(activation_function, backend):
9090

9191
assert list(hls_model.get_layers())[2].attributes['class_name'] == activation_function.__class__.__name__
9292

93-
94-
keras_conv1d = [Conv1D]
9593
padds_options = ['same', 'valid']
96-
@pytest.mark.parametrize("conv1d", keras_conv1d)
97-
@pytest.mark.parametrize("padds", padds_options)
98-
def test_conv1d(conv1d, padds):
94+
@pytest.mark.parametrize('padds', padds_options)
95+
@pytest.mark.parametrize('backend', ['Vivado', 'Quartus'])
96+
def test_conv1d(padds, backend):
9997
model = tf.keras.models.Sequential()
10098
input_shape = (10, 128, 4)
101-
model.add(conv1d(filters=32,
99+
model.add(Conv1D(filters=32,
102100
kernel_size=3,
103101
strides=1,
104102
padding=padds,
@@ -108,22 +106,23 @@ def test_conv1d(conv1d, padds):
108106
use_bias=False,
109107
data_format='channels_last'))
110108
model.add(Activation(activation='relu'))
111-
112109
model.compile(optimizer='adam', loss='mse')
110+
113111
X_input = np.random.rand(10,128,4)
114112
keras_prediction = model.predict(X_input)
113+
115114
config = hls4ml.utils.config_from_keras_model(model)
116-
output_dir = str(test_root_path / 'hls4mlprj_keras_api_conv1d_{}'.format(padds))
117-
hls_model = hls4ml.converters.convert_from_keras_model(model, hls_config=config, output_dir=output_dir)
115+
output_dir = str(test_root_path / 'hls4mlprj_keras_api_conv1d_{}_{}'.format(padds, backend))
116+
hls_model = hls4ml.converters.convert_from_keras_model(model, hls_config=config, output_dir=output_dir, backend=backend)
118117
hls_model.compile()
119-
hls_prediction = hls_model.predict(X_input)
118+
hls_prediction = hls_model.predict(X_input).reshape(keras_prediction.shape)
119+
120+
# 5e-2 might be too high
121+
np.testing.assert_allclose(hls_prediction, keras_prediction, rtol=0, atol=5e-2)
120122

121123
assert len(model.layers) + 2 == len(hls_model.get_layers())
122124
assert list(hls_model.get_layers())[1].attributes['name'] == model.layers[0]._name
123-
124-
print(list(hls_model.get_layers())[1].attributes)
125-
if conv1d == 'Conv1D':
126-
assert list(hls_model.get_layers())[1].attributes['class_name'] == 'Conv1D'
125+
assert list(hls_model.get_layers())[1].attributes['class_name'] == 'Conv1D'
127126
assert list(hls_model.get_layers())[1].attributes['activation'] == str(model.layers[0].activation).split()[1]
128127
assert list(hls_model.get_layers())[1].attributes["in_width"] == model.layers[0]._batch_input_shape[1]
129128
assert list(hls_model.get_layers())[1].attributes['filt_width'] == model.layers[0].kernel_size[0]
@@ -139,12 +138,9 @@ def test_conv1d(conv1d, padds):
139138
pad_left = pad_along_width // 2
140139
pad_right = pad_along_width - pad_left
141140

142-
out_valid = math.ceil(float(model.layers[0]._batch_input_shape[1] - model.layers[0].kernel_size[0] + 1) / float(model.layers[0].strides[0]))
143-
144141
if model.layers[0].padding == 'same':
145142
assert list(hls_model.get_layers())[1].attributes['pad_left'] == pad_left
146143
assert list(hls_model.get_layers())[1].attributes['pad_right'] == pad_right
147-
148144
elif model.layers[0].padding == 'valid':
149145
assert list(hls_model.get_layers())[1].attributes['pad_left'] == 0
150146
assert list(hls_model.get_layers())[1].attributes['pad_right'] == 0
@@ -230,11 +226,13 @@ def test_conv2d(chans, padds, backend):
230226
assert list(hls_model.get_layers())[1].attributes['pad_right'] == 0
231227

232228
pooling_layers = [MaxPooling1D, MaxPooling2D, AveragePooling1D, AveragePooling2D]
233-
@pytest.mark.parametrize("pooling", pooling_layers)
234-
@pytest.mark.parametrize("padds", padds_options)
235-
@pytest.mark.parametrize("chans", chans_options)
236-
def test_pooling(pooling, padds, chans):
229+
@pytest.mark.parametrize('pooling', pooling_layers)
230+
@pytest.mark.parametrize('padds', padds_options)
231+
@pytest.mark.parametrize('chans', chans_options)
232+
@pytest.mark.parametrize('backend', ['Vivado', 'Quartus'])
233+
def test_pooling(pooling, padds, chans, backend):
237234
assert '1D' in pooling.__name__ or '2D' in pooling.__name__
235+
238236
input_shape = (8,8,3) if '2D' in pooling.__name__ else (64,3)
239237
model = tf.keras.models.Sequential()
240238
if '2D' in pooling.__name__:
@@ -244,20 +242,22 @@ def test_pooling(pooling, padds, chans):
244242
model.add(Conv1D(filters=32, kernel_size=3, strides=1, padding=padds,
245243
input_shape=input_shape, kernel_initializer='normal', use_bias=False,
246244
data_format=chans))
247-
248245
pool_size = (2, 2) if '2D' in pooling.__name__ else 2
249246
model.add(pooling(pool_size=pool_size, strides=None, padding=padds, data_format=None))
250247
model.compile(optimizer='adam', loss='mse')
251248

252249
X_input = np.random.rand(100, *input_shape)
253250
keras_prediction = model.predict(X_input)
254251
config = hls4ml.utils.config_from_keras_model(model)
255-
output_dir = str(test_root_path / 'hls4mlprj_keras_api_pooling_{}_{}_{}'.format(pooling.__name__, chans, padds))
252+
output_dir = str(test_root_path / 'hls4mlprj_keras_api_pooling_{}_{}_{}_{}'.format(pooling.__name__, chans, padds, backend))
256253

257-
hls_model = hls4ml.converters.convert_from_keras_model(model, hls_config=config, output_dir=output_dir)
254+
hls_model = hls4ml.converters.convert_from_keras_model(model, hls_config=config, output_dir=output_dir, backend=backend)
258255
hls_model.compile()
259256
hls_prediction = hls_model.predict(X_input).reshape(keras_prediction.shape)
260257

258+
# 5e-2 might be too high
259+
np.testing.assert_allclose(hls_prediction, keras_prediction, rtol=0, atol=5e-2)
260+
261261
hls_pool = list(hls_model.get_layers())[-1]
262262
ker_pool = model.layers[-1]
263263
if '2D' in pooling.__name__:

test/pytest/test_pointwiseconv.py

Lines changed: 18 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -18,9 +18,16 @@
1818
@pytest.mark.parametrize('chans', chans_options)
1919
@pytest.mark.parametrize('padds', padds_options)
2020
@pytest.mark.parametrize('strides', strides1d_options)
21-
@pytest.mark.parametrize('io_type', io_type_options)
22-
@pytest.mark.parametrize('strategy', strategy_options)
23-
def test_pointwiseconv1d(chans, padds, strides, io_type, strategy):
21+
@pytest.mark.parametrize('backend, io_type, strategy', [
22+
('Quartus', 'io_parallel', 'resource'),
23+
('Vivado', 'io_parallel', 'resource'),
24+
25+
('Vivado', 'io_parallel', 'latency'),
26+
27+
('Vivado', 'io_stream', 'latency'),
28+
('Vivado', 'io_stream', 'resource')
29+
])
30+
def test_pointwiseconv1d(chans, padds, strides, backend, io_type, strategy):
2431
model = tf.keras.models.Sequential()
2532
input_shape = (28, 3)
2633
model.add(Conv1D(filters=32,
@@ -30,16 +37,18 @@ def test_pointwiseconv1d(chans, padds, strides, io_type, strategy):
3037
input_shape=input_shape,
3138
kernel_initializer='normal',
3239
use_bias=False,
33-
data_format=chans
34-
))
35-
40+
data_format=chans))
3641
model.compile(optimizer='adam', loss='mse')
42+
3743
X_input = np.random.rand(100, *input_shape)
3844
keras_prediction = model.predict(X_input)
39-
config = hls4ml.utils.config_from_keras_model(model, default_precision='ap_fixed<32,16>')
45+
46+
default_precision = 'ac_fixed<32,16,true>' if backend == 'Quartus' else 'ap_fixed<32,16>'
47+
config = hls4ml.utils.config_from_keras_model(model, default_precision=default_precision)
4048
config['Model']['Strategy'] = strategy
41-
output_dir = str(test_root_path / 'hls4mlprj_pointwise1d_{}_strides_{}_{}_padding_{}_{}'.format(chans, strides[0], padds, io_type, strategy))
42-
hls_model = hls4ml.converters.convert_from_keras_model(model, hls_config=config, output_dir=output_dir, io_type=io_type)
49+
50+
output_dir = str(test_root_path / 'hls4mlprj_pointwise1d_{}_strides_{}_{}_padding_{}_{}_{}'.format(chans, strides[0], padds, backend, io_type, strategy))
51+
hls_model = hls4ml.converters.convert_from_keras_model(model, hls_config=config, output_dir=output_dir, io_type=io_type, backend=backend)
4352
hls_model.compile()
4453
hls_prediction = hls_model.predict(X_input).reshape(keras_prediction.shape)
4554

0 commit comments

Comments
 (0)