Skip to content

Commit 2a4d456

Browse files
authored
Merge branch 'main' into pre-commit-ci-update-config
2 parents 1dd0f9e + fc287ba commit 2a4d456

File tree

2 files changed

+92
-2
lines changed

2 files changed

+92
-2
lines changed

hls4ml/model/layers.py

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -258,7 +258,13 @@ def add_bias(self, quantizer=None):
258258
precision = None
259259
type_name = None
260260
if data is None:
261-
data = np.zeros(self.get_output_variable().shape[-1])
261+
if 'data_format' in self.attributes:
262+
if self.attributes['data_format'] == 'channels_first':
263+
data = np.zeros(self.get_output_variable().shape[0])
264+
elif self.attributes['data_format'] == 'channels_last':
265+
data = np.zeros(self.get_output_variable().shape[-1])
266+
else:
267+
data = np.zeros(self.get_output_variable().shape[-1])
262268
precision = IntegerPrecisionType(width=1, signed=False)
263269
type_name = 'bias{index}_t'
264270
quantizer = None # Don't quantize non-existant bias

test/pytest/test_batchnorm_pytorch.py

Lines changed: 85 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,13 +13,22 @@
1313
atol = 5e-3
1414

1515

16-
@pytest.fixture(scope='module')
16+
@pytest.fixture
1717
def data():
1818
np.random.seed(0)
1919
X = np.random.rand(100, in_shape)
2020
return X
2121

2222

23+
@pytest.fixture(scope='module')
24+
def fusion_data():
25+
n_batch = 2
26+
n_in = 2
27+
size_in_height = 32
28+
X = np.random.rand(n_batch, n_in, size_in_height)
29+
return X
30+
31+
2332
@pytest.mark.parametrize('io_type', ['io_parallel', 'io_stream'])
2433
@pytest.mark.parametrize('backend', ['Vivado', 'Vitis', 'Quartus', 'Catapult'])
2534
def test_batchnorm(data, backend, io_type):
@@ -41,3 +50,78 @@ def test_batchnorm(data, backend, io_type):
4150
pytorch_prediction = model(torch.Tensor(data)).detach().numpy()
4251
hls_prediction = hls_model.predict(data)
4352
np.testing.assert_allclose(pytorch_prediction, hls_prediction, rtol=0, atol=atol, verbose=True)
53+
54+
55+
atol = 5e-2
56+
57+
58+
class BatchNorm_w_Fusion(nn.Module):
59+
def __init__(self, filters, momentum):
60+
super().__init__()
61+
self.conv1 = nn.Conv1d(
62+
int(filters),
63+
filters,
64+
kernel_size=3,
65+
stride=1,
66+
padding=1,
67+
bias=False,
68+
)
69+
self.bn1 = nn.BatchNorm1d(filters)
70+
self.relu1 = nn.ReLU()
71+
72+
def forward(self, x):
73+
x = self.conv1(x)
74+
x = self.bn1(x)
75+
x = self.relu1(x)
76+
return x
77+
78+
79+
@pytest.mark.parametrize('io_type', ['io_parallel', 'io_stream'])
80+
@pytest.mark.parametrize('backend', ['Vivado', 'Vitis', 'Quartus', 'Catapult'])
81+
def test_batchnorm_fusion(fusion_data, backend, io_type):
82+
n_in = 2
83+
momentum = 0.99
84+
size_in_height = 32
85+
filters = n_in
86+
87+
# see above for model definition
88+
model = BatchNorm_w_Fusion(filters, momentum)
89+
# Important to set model to eval to fix batchnorm behavior
90+
model.eval()
91+
# generating config
92+
pytorch_prediction = model(torch.Tensor(fusion_data)).detach().numpy()
93+
94+
# We do not have an implementation of a transpose for io_stream, need to transpose inputs and outputs outside of hls4ml
95+
if io_type == 'io_stream':
96+
fusion_data = np.ascontiguousarray(fusion_data.transpose(0, 2, 1))
97+
config = hls4ml.utils.config_from_pytorch_model(model, channels_last_conversion='internal', transpose_outputs=False)
98+
else:
99+
config = hls4ml.utils.config_from_pytorch_model(model, channels_last_conversion='full', transpose_outputs=True)
100+
101+
config['Model']['Strategy'] = 'Resource'
102+
103+
# conversion
104+
output_dir = str(test_root_path / f'hls4mlprj_block_{backend}_{io_type}')
105+
hls_model = hls4ml.converters.convert_from_pytorch_model(
106+
model,
107+
(None, n_in, size_in_height),
108+
hls_config=config,
109+
output_dir=output_dir,
110+
backend=backend,
111+
io_type=io_type,
112+
)
113+
114+
# compiling model
115+
hls_model.compile()
116+
117+
if io_type == 'io_stream':
118+
hls_prediction = np.transpose(
119+
np.reshape(
120+
hls_model.predict(fusion_data),
121+
(pytorch_prediction.shape[0], pytorch_prediction.shape[2], pytorch_prediction.shape[1]),
122+
),
123+
(0, 2, 1),
124+
)
125+
else:
126+
hls_prediction = np.reshape(hls_model.predict(fusion_data), pytorch_prediction.shape)
127+
np.testing.assert_allclose(pytorch_prediction, hls_prediction, rtol=0, atol=atol, verbose=True)

0 commit comments

Comments
 (0)