Closed sei-jgwohlbier closed 2 weeks ago
Hi! Thanks for testing this and working on a fix! I think your development goes into a promising direction. From a quick check of your fork, it seems to be like hsl4ml is still treating the new Constant layer like an input layer and is trying to find it's tensor at runtime. I'm not quite sure how best to fix it, but maybe @vloncar can advise.
A similar situation came up a while ago for RNG feature. I think the best way would be to introduce a new type of node that has no input, only output. Then we can use it as a constant input in situations like these. It would require some changes in how this is handled transparently to the rest of the nodes in the graph.
Thanks for the comment. Let me know if I can help out. For the time being I am adding additional input tensors and passing in constant values to them.
Note, we do this lots in the ONNX parsing, mostly to handle Quant nodes between the weights and where they are used. See if maybe the Constant node we have for ONNX parsing is useful here.
That new Constant node does look like it would be very useful for this use case. I'll play with that.
Yep, that seems to work. I think the Constant
class that @sei-jgwohlbier implemented would work just as well. I realized that the issue in that implementation was that the new constant layer was still added to the list of input layers: https://github.com/sei-jgwohlbier/hls4ml/blob/pytorch/tensorconstant/hls4ml/converters/pytorch_to_hls.py#L292, creating the issues I saw.
I have now an implementation that seems to work and that makes use of the Constant
class that was introduced with the QONNX PR at https://github.com/JanFSchulte/hls4ml/tree/constant @sei-jgwohlbier can you have a look and see if that works for you?
Oh sweet. I'll give it a shot next week. Thanks!
@JanFSchulte I'm testing this today. This update https://github.com/fastmachinelearning/hls4ml/pull/1121 wasn't in your fork so I needed to manually add it for my case. Just FYI. I'll report back soon on whether it worked for me.
@JanFSchulte your fork worked for my test! Thanks so much!
Great, thanks for checking! PR with the fix is here: https://github.com/fastmachinelearning/hls4ml/pull/1123
@JanFSchulte I think there might be an issue when multiple constants are present. For example, for the following class I get the same failure as before.
class test(nn.Module):
def __init__(self):
super().__init__()
self.downsample = nn.AvgPool1d(kernel_size=1, stride=2)
def forward(self, x):
d = self.downsample(x)
p = torch.mul(d,1.0)
q = torch.mul(d,2.0)
return torch.cat((p, q), dim=-1)
Ah damn, didn't think to test that. I'll have a look and update the PR accordingly.
The issue was that pytorch starts adding _n
to the name of the layer for multiple instances of the same operation if n >0
, so that the matching of the operations failed. Should be fixed in the branch now.
Ok, I confirmed it works on that small test case. I'll next try my more complicated network.
Seems to be working for my resnet. Thanks!
Prerequisites
Please make sure to check off these prerequisites before submitting a bug report.
Quick summary
Merge layers that have a constant input do not work with PyTorch.
Details
Steps to Reproduce
import numpy as np import os import shutil import torch import torch.nn as nn from torchinfo import summary
from hls4ml.converters import convert_from_pytorch_model from hls4ml.utils.config import config_from_pytorch_model
test_root_path = Path(file).parent
if name == "main":
========================================================================================== Layer (type:depth-idx) Output Shape Param #
test [2, 2, 8] 12 ├─AvgPool1d: 1-1 [2, 2, 4] --
Total params: 12 Trainable params: 12 Non-trainable params: 0 Total mult-adds (M): 0
Input size (MB): 0.00 Forward/backward pass size (MB): 0.00 Params size (MB): 0.00 Estimated Total Size (MB): 0.00
{'Model': {'Precision': 'ap_fixed<16,6>', 'ReuseFactor': 1, 'ChannelsLastConversion': 'internal', 'TransposeOutputs': False, 'Strategy': 'Resource'}, 'PytorchModel': test( (conv1): Conv1d(2, 2, kernel_size=(3,), stride=(1,), padding=(1,), bias=False) (downsample): AvgPool1d(kernel_size=(1,), stride=(2,), padding=(0,)) ), 'InputShape': (None, 2, 8)} /home/hls4ml-user/work/ewstapp_research/isolate/NETWORK/hls4mlprj_mul_Vitis_io_stream Interpreting Model ... Topology: Layer name: downsample, layer type: AveragePooling1D, input shape: [[None, 2, 8]] Layer name: mul, layer type: Merge, input shape: [[None, 2, 4]] Layer name: cat, layer type: Concatenate, input shape: [[None, 2, 4], [None, 2, 4]] Creating HLS model WARNING: Changing pipeline style to "dataflow". Traceback (most recent call last): File "/home/hls4ml-user/work/ewstapp_research/isolate/NETWORK/test_mul.py", line 81, in
hls_model = convert_from_pytorch_model(
File "/home/hls4ml-user/miniconda3/envs/hls4ml/lib/python3.10/site-packages/hls4ml/converters/init.py", line 308, in convert_from_pytorch_model
return pytorch_to_hls(config)
File "/home/hls4ml-user/miniconda3/envs/hls4ml/lib/python3.10/site-packages/hls4ml/converters/pytorch_to_hls.py", line 374, in pytorch_to_hls
hls_model = ModelGraph(config, layer_list, inputs=input_layers)
File "/home/hls4ml-user/miniconda3/envs/hls4ml/lib/python3.10/site-packages/hls4ml/model/graph.py", line 387, in init
self._make_graph(layer_list)
File "/home/hls4ml-user/miniconda3/envs/hls4ml/lib/python3.10/site-packages/hls4ml/model/graph.py", line 416, in _make_graph
self.graph[name] = self.make_node(kind, name, layer, inputs, outputs)
File "/home/hls4ml-user/miniconda3/envs/hls4ml/lib/python3.10/site-packages/hls4ml/model/graph.py", line 503, in make_node
node = layer_cls(self, name, attributes, inputs, outputs)
File "/home/hls4ml-user/miniconda3/envs/hls4ml/lib/python3.10/site-packages/hls4ml/model/layers.py", line 117, in init
self.initialize()
File "/home/hls4ml-user/miniconda3/envs/hls4ml/lib/python3.10/site-packages/hls4ml/model/layers.py", line 950, in initialize
assert len(self.inputs) == 2