sparsepixels 0.2.2__tar.gz → 0.2.3__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: sparsepixels
3
- Version: 0.2.2
3
+ Version: 0.2.3
4
4
  Summary: Efficient convolution for sparse data on FPGAs
5
5
  Home-page: https://github.com/hftsoi/sparse-pixels
6
6
  Author: Ho Fung Tsoi
@@ -13,7 +13,7 @@ Description-Content-Type: text/markdown
13
13
  License-File: LICENSE
14
14
  Requires-Dist: tensorflow
15
15
  Requires-Dist: keras>=3.0
16
- Requires-Dist: HGQ2
16
+ Requires-Dist: HGQ2>=0.1.8
17
17
  Dynamic: license-file
18
18
 
19
19
  <p align="center">
@@ -53,19 +53,21 @@ import keras
53
53
  from keras.layers import Flatten, Activation
54
54
  from hgq.layers import QConv2D, QDense
55
55
  from hgq.config import QuantizerConfigScope, LayerConfigScope
56
+ from hgq.quantizer.config import QuantizerConfig
56
57
  from sparsepixels.layers import InputReduce, QConv2DSparse, AveragePooling2DSparse
57
58
  ```
58
59
 
59
- Build an example sparse CNN within HGQ2 quantization scopes. For sparse models, input
60
- quantization and eBOPs are disabled for now (`enable_iq=False, enable_ebops=False`) because the input
61
- quantizer's internal regularizer degrades precision too fast for sparse data's weak gradient
62
- signal to counteract (we will fix that in future release soon).
60
+ Build an example sparse CNN within HGQ2 quantization scopes. A custom input quantizer
61
+ config with higher initial fractional bits (`f0=8`) is used to prevent the default (`f0=2`)
62
+ from zeroing out sparse signals in early training epochs:
63
63
 
64
64
  ```python
65
+ iq_conf = QuantizerConfig(place='datalane', q_type='kif', i0=4, f0=8, overflow_mode='WRAP')
66
+
65
67
  with (
66
68
  QuantizerConfigScope(place='all', default_q_type='kbi', overflow_mode='SAT_SYM'),
67
69
  QuantizerConfigScope(place='datalane', default_q_type='kif', overflow_mode='WRAP'),
68
- LayerConfigScope(enable_ebops=False, enable_iq=False), # for now and can be enabled after fix soon
70
+ LayerConfigScope(enable_ebops=True, enable_iq=True, beta0=1e-5),
69
71
  ):
70
72
  x_in = keras.Input(shape=(28, 28, 1), name='x_in')
71
73
 
@@ -74,19 +76,47 @@ with (
74
76
 
75
77
  # Sparse convolution
76
78
  x = QConv2DSparse(filters=3, kernel_size=3, name='conv1', padding='same', strides=1,
77
- activation='relu')([x, keep_mask])
79
+ activation='relu', iq_conf=iq_conf)([x, keep_mask])
78
80
 
79
81
  # Sparse pooling
80
82
  x, keep_mask = AveragePooling2DSparse(2, name='pool1')([x, keep_mask])
81
83
 
82
84
  x = Flatten(name='flatten')(x)
83
- x = QDense(10, name='dense1', activation='relu')(x)
85
+ x = QDense(10, name='dense1', activation='relu', iq_conf=iq_conf)(x)
84
86
  x = Activation('softmax', name='softmax')(x)
85
87
 
86
88
  model = keras.Model(x_in, x)
87
89
  ```
88
90
 
89
- We are working on hls4ml integration that auto parses the sparse layers into HLS.
91
+ ## Converting a trained model to HLS with hls4ml
92
+
93
+ > **Note:** A [PR](https://github.com/fastmachinelearning/hls4ml/pull/1468) adding `sparsepixels` support to the official [hls4ml](https://github.com/fastmachinelearning/hls4ml) repo has been submitted but is not yet merged. In the meantime you can install hls4ml from the PR branch on this fork to try the converter:
94
+ >
95
+ > ```bash
96
+ > pip install "git+https://github.com/hftsoi/hls4ml.git@sparsepixels"
97
+ > ```
98
+
99
+ Once installed, converting a trained sparsepixels model to HLS is as usual:
100
+
101
+ ```python
102
+ import hls4ml
103
+
104
+ hls_config = hls4ml.utils.config_from_keras_model(model, granularity='name')
105
+ hls_config.setdefault('Model', {})['PipelineStyle'] = 'dataflow' # use "#pragma HLS DATAFLOW" (instead of the default "#pragma HLS PIPELINE" for io_parallel)
106
+
107
+ hls_model = hls4ml.converters.convert_from_keras_model(
108
+ model,
109
+ hls_config=hls_config,
110
+ output_dir='hls_proj/my_sparse_cnn',
111
+ backend='Vitis',
112
+ io_type='io_parallel', # io_stream is not supported yet
113
+ )
114
+ hls_model.write()
115
+ hls_model.compile()
116
+ y_hls = hls_model.predict(x_test)
117
+ ```
118
+
119
+ > **Note:** The converter currently supports only fully parallelized `io_parallel` HLS. We are working on expanding to partial parallelization and `io_stream` for larger flexibility.
90
120
 
91
121
  ## Documentation
92
122
 
@@ -35,19 +35,21 @@ import keras
35
35
  from keras.layers import Flatten, Activation
36
36
  from hgq.layers import QConv2D, QDense
37
37
  from hgq.config import QuantizerConfigScope, LayerConfigScope
38
+ from hgq.quantizer.config import QuantizerConfig
38
39
  from sparsepixels.layers import InputReduce, QConv2DSparse, AveragePooling2DSparse
39
40
  ```
40
41
 
41
- Build an example sparse CNN within HGQ2 quantization scopes. For sparse models, input
42
- quantization and eBOPs are disabled for now (`enable_iq=False, enable_ebops=False`) because the input
43
- quantizer's internal regularizer degrades precision too fast for sparse data's weak gradient
44
- signal to counteract (we will fix that in future release soon).
42
+ Build an example sparse CNN within HGQ2 quantization scopes. A custom input quantizer
43
+ config with higher initial fractional bits (`f0=8`) is used to prevent the default (`f0=2`)
44
+ from zeroing out sparse signals in early training epochs:
45
45
 
46
46
  ```python
47
+ iq_conf = QuantizerConfig(place='datalane', q_type='kif', i0=4, f0=8, overflow_mode='WRAP')
48
+
47
49
  with (
48
50
  QuantizerConfigScope(place='all', default_q_type='kbi', overflow_mode='SAT_SYM'),
49
51
  QuantizerConfigScope(place='datalane', default_q_type='kif', overflow_mode='WRAP'),
50
- LayerConfigScope(enable_ebops=False, enable_iq=False), # for now and can be enabled after fix soon
52
+ LayerConfigScope(enable_ebops=True, enable_iq=True, beta0=1e-5),
51
53
  ):
52
54
  x_in = keras.Input(shape=(28, 28, 1), name='x_in')
53
55
 
@@ -56,19 +58,47 @@ with (
56
58
 
57
59
  # Sparse convolution
58
60
  x = QConv2DSparse(filters=3, kernel_size=3, name='conv1', padding='same', strides=1,
59
- activation='relu')([x, keep_mask])
61
+ activation='relu', iq_conf=iq_conf)([x, keep_mask])
60
62
 
61
63
  # Sparse pooling
62
64
  x, keep_mask = AveragePooling2DSparse(2, name='pool1')([x, keep_mask])
63
65
 
64
66
  x = Flatten(name='flatten')(x)
65
- x = QDense(10, name='dense1', activation='relu')(x)
67
+ x = QDense(10, name='dense1', activation='relu', iq_conf=iq_conf)(x)
66
68
  x = Activation('softmax', name='softmax')(x)
67
69
 
68
70
  model = keras.Model(x_in, x)
69
71
  ```
70
72
 
71
- We are working on hls4ml integration that auto parses the sparse layers into HLS.
73
+ ## Converting a trained model to HLS with hls4ml
74
+
75
+ > **Note:** A [PR](https://github.com/fastmachinelearning/hls4ml/pull/1468) adding `sparsepixels` support to the official [hls4ml](https://github.com/fastmachinelearning/hls4ml) repo has been submitted but is not yet merged. In the meantime you can install hls4ml from the PR branch on this fork to try the converter:
76
+ >
77
+ > ```bash
78
+ > pip install "git+https://github.com/hftsoi/hls4ml.git@sparsepixels"
79
+ > ```
80
+
81
+ Once installed, converting a trained sparsepixels model to HLS is as usual:
82
+
83
+ ```python
84
+ import hls4ml
85
+
86
+ hls_config = hls4ml.utils.config_from_keras_model(model, granularity='name')
87
+ hls_config.setdefault('Model', {})['PipelineStyle'] = 'dataflow' # use "#pragma HLS DATAFLOW" (instead of the default "#pragma HLS PIPELINE" for io_parallel)
88
+
89
+ hls_model = hls4ml.converters.convert_from_keras_model(
90
+ model,
91
+ hls_config=hls_config,
92
+ output_dir='hls_proj/my_sparse_cnn',
93
+ backend='Vitis',
94
+ io_type='io_parallel', # io_stream is not supported yet
95
+ )
96
+ hls_model.write()
97
+ hls_model.compile()
98
+ y_hls = hls_model.predict(x_test)
99
+ ```
100
+
101
+ > **Note:** The converter currently supports only fully parallelized `io_parallel` HLS. We are working on expanding to partial parallelization and `io_stream` for larger flexibility.
72
102
 
73
103
  ## Documentation
74
104
 
@@ -1,6 +1,6 @@
1
1
  [metadata]
2
2
  name = sparsepixels
3
- version = 0.2.2
3
+ version = 0.2.3
4
4
  description = Efficient convolution for sparse data on FPGAs
5
5
  author = Ho Fung Tsoi
6
6
  author_email = ho.fung.tsoi@cern.ch
@@ -19,7 +19,7 @@ python_requires = >=3.10
19
19
  install_requires =
20
20
  tensorflow
21
21
  keras>=3.0
22
- HGQ2
22
+ HGQ2>=0.1.8
23
23
  include_package_data = True
24
24
 
25
25
  [options.package_data]
@@ -72,6 +72,13 @@ class QConv2DSparse(keras.layers.Layer):
72
72
  self.masker = RemoveDilatedPixels()
73
73
 
74
74
  def build(self, input_shape):
75
+ # Build the wrapped conv here (eagerly, during layer build) instead of lazily inside call().
76
+ # Otherwise, when Keras traces call() symbolically to infer the output shape, the conv would
77
+ # build in graph mode and HGQ2 (>=0.1.9) runs a weight check there that evaluates a tensor as
78
+ # a Python bool -- which is not allowed in graph mode. See also compute_output_shape below.
79
+ x_shape = input_shape[0]
80
+ if not self.conv.built:
81
+ self.conv.build(x_shape)
75
82
  if self._use_bias:
76
83
  self.sparse_bias = self.add_weight(
77
84
  name="sparse_bias",
@@ -83,6 +90,11 @@ class QConv2DSparse(keras.layers.Layer):
83
90
  self._bq.build((self.conv.filters,))
84
91
  super().build(input_shape)
85
92
 
93
+ def compute_output_shape(self, input_shape):
94
+ # Provide the output shape directly so Keras does not trace call() symbolically (masking
95
+ # preserves shape, so the output shape is the wrapped conv's output shape).
96
+ return self.conv.compute_output_shape(input_shape[0])
97
+
86
98
  def call(self, inputs, **kwargs):
87
99
  x, keep_mask = inputs
88
100
  x = self.masker((x, keep_mask))
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: sparsepixels
3
- Version: 0.2.2
3
+ Version: 0.2.3
4
4
  Summary: Efficient convolution for sparse data on FPGAs
5
5
  Home-page: https://github.com/hftsoi/sparse-pixels
6
6
  Author: Ho Fung Tsoi
@@ -13,7 +13,7 @@ Description-Content-Type: text/markdown
13
13
  License-File: LICENSE
14
14
  Requires-Dist: tensorflow
15
15
  Requires-Dist: keras>=3.0
16
- Requires-Dist: HGQ2
16
+ Requires-Dist: HGQ2>=0.1.8
17
17
  Dynamic: license-file
18
18
 
19
19
  <p align="center">
@@ -53,19 +53,21 @@ import keras
53
53
  from keras.layers import Flatten, Activation
54
54
  from hgq.layers import QConv2D, QDense
55
55
  from hgq.config import QuantizerConfigScope, LayerConfigScope
56
+ from hgq.quantizer.config import QuantizerConfig
56
57
  from sparsepixels.layers import InputReduce, QConv2DSparse, AveragePooling2DSparse
57
58
  ```
58
59
 
59
- Build an example sparse CNN within HGQ2 quantization scopes. For sparse models, input
60
- quantization and eBOPs are disabled for now (`enable_iq=False, enable_ebops=False`) because the input
61
- quantizer's internal regularizer degrades precision too fast for sparse data's weak gradient
62
- signal to counteract (we will fix that in future release soon).
60
+ Build an example sparse CNN within HGQ2 quantization scopes. A custom input quantizer
61
+ config with higher initial fractional bits (`f0=8`) is used to prevent the default (`f0=2`)
62
+ from zeroing out sparse signals in early training epochs:
63
63
 
64
64
  ```python
65
+ iq_conf = QuantizerConfig(place='datalane', q_type='kif', i0=4, f0=8, overflow_mode='WRAP')
66
+
65
67
  with (
66
68
  QuantizerConfigScope(place='all', default_q_type='kbi', overflow_mode='SAT_SYM'),
67
69
  QuantizerConfigScope(place='datalane', default_q_type='kif', overflow_mode='WRAP'),
68
- LayerConfigScope(enable_ebops=False, enable_iq=False), # for now and can be enabled after fix soon
70
+ LayerConfigScope(enable_ebops=True, enable_iq=True, beta0=1e-5),
69
71
  ):
70
72
  x_in = keras.Input(shape=(28, 28, 1), name='x_in')
71
73
 
@@ -74,19 +76,47 @@ with (
74
76
 
75
77
  # Sparse convolution
76
78
  x = QConv2DSparse(filters=3, kernel_size=3, name='conv1', padding='same', strides=1,
77
- activation='relu')([x, keep_mask])
79
+ activation='relu', iq_conf=iq_conf)([x, keep_mask])
78
80
 
79
81
  # Sparse pooling
80
82
  x, keep_mask = AveragePooling2DSparse(2, name='pool1')([x, keep_mask])
81
83
 
82
84
  x = Flatten(name='flatten')(x)
83
- x = QDense(10, name='dense1', activation='relu')(x)
85
+ x = QDense(10, name='dense1', activation='relu', iq_conf=iq_conf)(x)
84
86
  x = Activation('softmax', name='softmax')(x)
85
87
 
86
88
  model = keras.Model(x_in, x)
87
89
  ```
88
90
 
89
- We are working on hls4ml integration that auto parses the sparse layers into HLS.
91
+ ## Converting a trained model to HLS with hls4ml
92
+
93
+ > **Note:** A [PR](https://github.com/fastmachinelearning/hls4ml/pull/1468) adding `sparsepixels` support to the official [hls4ml](https://github.com/fastmachinelearning/hls4ml) repo has been submitted but is not yet merged. In the meantime you can install hls4ml from the PR branch on this fork to try the converter:
94
+ >
95
+ > ```bash
96
+ > pip install "git+https://github.com/hftsoi/hls4ml.git@sparsepixels"
97
+ > ```
98
+
99
+ Once installed, converting a trained sparsepixels model to HLS is as usual:
100
+
101
+ ```python
102
+ import hls4ml
103
+
104
+ hls_config = hls4ml.utils.config_from_keras_model(model, granularity='name')
105
+ hls_config.setdefault('Model', {})['PipelineStyle'] = 'dataflow' # use "#pragma HLS DATAFLOW" (instead of the default "#pragma HLS PIPELINE" for io_parallel)
106
+
107
+ hls_model = hls4ml.converters.convert_from_keras_model(
108
+ model,
109
+ hls_config=hls_config,
110
+ output_dir='hls_proj/my_sparse_cnn',
111
+ backend='Vitis',
112
+ io_type='io_parallel', # io_stream is not supported yet
113
+ )
114
+ hls_model.write()
115
+ hls_model.compile()
116
+ y_hls = hls_model.predict(x_test)
117
+ ```
118
+
119
+ > **Note:** The converter currently supports only fully parallelized `io_parallel` HLS. We are working on expanding to partial parallelization and `io_stream` for larger flexibility.
90
120
 
91
121
  ## Documentation
92
122
 
@@ -1,3 +1,3 @@
1
1
  tensorflow
2
2
  keras>=3.0
3
- HGQ2
3
+ HGQ2>=0.1.8
@@ -1,8 +1,6 @@
1
1
  dist
2
2
  docs
3
- hls_proj
4
3
  hls_templates
5
4
  notebook
6
5
  sparsepixels
7
6
  tests
8
- weights
@@ -2,14 +2,16 @@ import keras
2
2
  from keras.layers import Flatten, Activation, AveragePooling2D
3
3
  from hgq.layers import QConv2D, QDense
4
4
  from hgq.config import QuantizerConfigScope, LayerConfigScope
5
+ from hgq.quantizer.config import QuantizerConfig
5
6
  from sparsepixels.layers import InputReduce, QConv2DSparse, AveragePooling2DSparse
6
7
 
7
8
 
8
9
  def build_cnn(is_sparse, n_max_pixels=None):
10
+ iq_conf = QuantizerConfig(place='datalane', q_type='kif', i0=4, f0=8, overflow_mode='WRAP')
9
11
  with (
10
12
  QuantizerConfigScope(place='all', default_q_type='kbi', overflow_mode='SAT_SYM'),
11
13
  QuantizerConfigScope(place='datalane', default_q_type='kif', overflow_mode='WRAP'),
12
- LayerConfigScope(enable_ebops=not is_sparse, enable_iq=not is_sparse, beta0=1e-5),
14
+ LayerConfigScope(enable_ebops=True, enable_iq=True, beta0=1e-5),
13
15
  ):
14
16
  x_in = keras.Input(shape=(32, 32, 1), name='x_in')
15
17
  if is_sparse:
@@ -19,26 +21,26 @@ def build_cnn(is_sparse, n_max_pixels=None):
19
21
 
20
22
  if is_sparse:
21
23
  x = QConv2DSparse(filters=1, kernel_size=7, name='conv1', padding='same', strides=1,
22
- activation='relu')([x, keep_mask])
24
+ activation='relu', iq_conf=iq_conf)([x, keep_mask])
23
25
  x, keep_mask = AveragePooling2DSparse(4, name='pool1')([x, keep_mask])
24
26
 
25
27
  x = QConv2DSparse(filters=3, kernel_size=5, name='conv2', padding='same', strides=1,
26
- activation='relu')([x, keep_mask])
28
+ activation='relu', iq_conf=iq_conf)([x, keep_mask])
27
29
  x, keep_mask = AveragePooling2DSparse(2, name='pool2')([x, keep_mask])
28
30
  else:
29
31
  x = QConv2D(filters=1, kernel_size=7, name='conv1', padding='same', strides=1,
30
- activation='relu')(x)
32
+ activation='relu', iq_conf=iq_conf)(x)
31
33
  x = AveragePooling2D(4, name='pool1')(x)
32
34
 
33
35
  x = QConv2D(filters=3, kernel_size=5, name='conv2', padding='same', strides=1,
34
- activation='relu')(x)
36
+ activation='relu', iq_conf=iq_conf)(x)
35
37
  x = AveragePooling2D(2, name='pool2')(x)
36
38
 
37
39
  x = Flatten(name='flatten')(x)
38
40
 
39
- x = QDense(36, name='dense1', activation='relu')(x)
41
+ x = QDense(36, name='dense1', activation='relu', iq_conf=iq_conf)(x)
40
42
 
41
- x = QDense(10, name='dense2')(x)
43
+ x = QDense(10, name='dense2', iq_conf=iq_conf)(x)
42
44
  x = Activation('softmax', name='softmax')(x)
43
45
 
44
46
  return keras.Model(x_in, x)
File without changes
File without changes