sparsepixels 0.3.0__tar.gz → 0.3.1__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.3.0
3
+ Version: 0.3.1
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
@@ -47,20 +47,16 @@ pip install sparsepixels
47
47
 
48
48
  ## Getting Started
49
49
 
50
- Import the sparse layers, the quantization library (HGQ2), and the training utilities:
51
-
52
50
  ```python
53
51
  import keras
54
52
  from keras.layers import Flatten, Activation
55
- from hgq.layers import QConv2D, QDense
53
+ from hgq.layers import QDense
56
54
  from hgq.config import QuantizerConfigScope, LayerConfigScope
57
55
  from hgq.quantizer.config import QuantizerConfig
58
56
  from sparsepixels.layers import InputReduce, QConv2DSparse, AveragePooling2DSparse, MaxPooling2DSparse
59
57
  from sparsepixels.utils import (
60
- active_pixels_vs_threshold, plot_reduced_examples,
61
- set_sparse_ebops_factor, cosine_lr,
62
- SparseTrainingMonitor, plot_history,
63
- print_quantization, plot_quantization,
58
+ active_pixels_vs_threshold, plot_reduced_examples, cosine_lr,
59
+ SparseTrainingMonitor, plot_history, print_quantization, plot_quantization,
64
60
  )
65
61
  ```
66
62
 
@@ -112,31 +108,34 @@ with (
112
108
  model = keras.Model(x_in, x)
113
109
  ```
114
110
 
115
- Train the model, then read out the learned sparsity to deploy. `set_sparse_ebops_factor` makes the
116
- EBOPS (a proxy for the quantized hardware cost) reflect the sparse compute rather than a dense one; a
117
- cosine-decayed learning rate together with `restore_best_weights` keeps the learned budget from
118
- over-compressing near the end of training. `plot_history` shows the loss breakdown, the learned
119
- budget/threshold and the EBOPS in one figure, and the values to deploy are `layer.n_max_pixels` and
120
- `layer.threshold`.
111
+ Train the model with `SparseTrainingMonitor`. It records the loss breakdown, the learned
112
+ budget/threshold and the EBOPS each epoch, and it corrects the EBOPS (a proxy for the quantized
113
+ hardware cost) to the sparse compute automatically, so no extra setup is needed. The
114
+ only sparse-specific choices are the cosine-decayed learning rate and `restore_best_weights`, which
115
+ keep the learned budget from over-compressing near the end of training.
121
116
 
122
117
  ```python
123
- set_sparse_ebops_factor(model)
124
-
125
- steps_per_epoch = len(x_train) // 128
126
118
  early_stop = keras.callbacks.EarlyStopping(monitor='val_accuracy', mode='max', patience=20, restore_best_weights=True)
127
119
  model.compile(
128
- optimizer=keras.optimizers.Adam(cosine_lr(1e-3, epochs=100, steps_per_epoch=steps_per_epoch)),
120
+ optimizer=keras.optimizers.Adam(cosine_lr(1e-3, epochs=100, steps_per_epoch=len(x_train) // 128)),
129
121
  loss='categorical_crossentropy', metrics=['accuracy'],
130
122
  )
131
123
  history = model.fit(x_train, y_train, validation_data=(x_val, y_val),
132
124
  epochs=100, batch_size=128, callbacks=[early_stop, SparseTrainingMonitor()])
125
+ ```
133
126
 
127
+ After training, plot the diagnostics and read out the learned sparsity to deploy. `plot_history`
128
+ shows the loss breakdown, the learned budget/threshold and the EBOPS in one figure.
129
+ `print_quantization` / `plot_quantization` summarize the per-layer bit-widths. The values to
130
+ deploy are `layer.n_max_pixels` and `layer.threshold` (hls4ml converter will auto-parse these from the model).
131
+
132
+ ```python
134
133
  plot_history(history, early_stopping=early_stop) # loss breakdown, budget, threshold, EBOPS
135
134
  print_quantization(model) # per-layer bit-width distribution and EBOPS
136
135
  plot_quantization(model)
137
136
 
138
137
  ir = model.get_layer('input_reduce')
139
- print(f"deploy with n_max_pixels={ir.n_max_pixels}, threshold={ir.threshold:.3f}")
138
+ print(f"n_max_pixels={ir.n_max_pixels}, threshold={ir.threshold:.3f}")
140
139
  ```
141
140
 
142
141
  ## Converting a trained model to HLS with hls4ml
@@ -147,13 +146,31 @@ print(f"deploy with n_max_pixels={ir.n_max_pixels}, threshold={ir.threshold:.3f}
147
146
  > pip install "git+https://github.com/hftsoi/hls4ml.git@sparsepixels"
148
147
  > ```
149
148
 
150
- Once installed, converting a trained sparsepixels model to HLS is as usual:
149
+ Once installed, pull a config from the trained model, optionally set the per-layer parallelization
150
+ knobs, and convert:
151
151
 
152
152
  ```python
153
153
  import hls4ml
154
154
 
155
155
  hls_config = hls4ml.utils.config_from_keras_model(model, granularity='name')
156
- hls_config.setdefault('Model', {})['PipelineStyle'] = 'dataflow' # use "#pragma HLS DATAFLOW" (instead of the default "#pragma HLS PIPELINE" for io_parallel)
156
+ hls_config.setdefault('Model', {})['PipelineStyle'] = 'dataflow' # "#pragma HLS DATAFLOW"
157
+
158
+ # Optional per-layer parallelization knobs. Omit them for the fully-parallel, lowest-latency default.
159
+ # input_reduce Variant : 'tree' (default, lowest latency) or 'stream' (fewer resources)
160
+ # conv* PixelParallelFactor : active pixels in parallel (<= n_max_pixels)
161
+ # FiltParallelFactor : filters in parallel (<= that conv's filters)
162
+ # pool* PixelParallelFactor : active pixels in parallel
163
+ # ChanParallelFactor : channels in parallel
164
+ # flatten ParallelFactor : scatter positions in parallel (<= out_height * out_width)
165
+ n_sparse = model.get_layer('input_reduce').n_max_pixels
166
+ knobs = {
167
+ 'input_reduce': {'Variant': 'stream'},
168
+ 'conv1': {'PixelParallelFactor': n_sparse, 'FiltParallelFactor': 3},
169
+ 'pool1': {'PixelParallelFactor': n_sparse, 'ChanParallelFactor': 3},
170
+ 'flatten': {'ParallelFactor': 14 * 14}, # this model pools 28*28 -> 14*14
171
+ }
172
+ for name, cfg in knobs.items():
173
+ hls_config['LayerName'].setdefault(name, {}).update(cfg)
157
174
 
158
175
  hls_model = hls4ml.converters.convert_from_keras_model(
159
176
  model,
@@ -28,20 +28,16 @@ pip install sparsepixels
28
28
 
29
29
  ## Getting Started
30
30
 
31
- Import the sparse layers, the quantization library (HGQ2), and the training utilities:
32
-
33
31
  ```python
34
32
  import keras
35
33
  from keras.layers import Flatten, Activation
36
- from hgq.layers import QConv2D, QDense
34
+ from hgq.layers import QDense
37
35
  from hgq.config import QuantizerConfigScope, LayerConfigScope
38
36
  from hgq.quantizer.config import QuantizerConfig
39
37
  from sparsepixels.layers import InputReduce, QConv2DSparse, AveragePooling2DSparse, MaxPooling2DSparse
40
38
  from sparsepixels.utils import (
41
- active_pixels_vs_threshold, plot_reduced_examples,
42
- set_sparse_ebops_factor, cosine_lr,
43
- SparseTrainingMonitor, plot_history,
44
- print_quantization, plot_quantization,
39
+ active_pixels_vs_threshold, plot_reduced_examples, cosine_lr,
40
+ SparseTrainingMonitor, plot_history, print_quantization, plot_quantization,
45
41
  )
46
42
  ```
47
43
 
@@ -93,31 +89,34 @@ with (
93
89
  model = keras.Model(x_in, x)
94
90
  ```
95
91
 
96
- Train the model, then read out the learned sparsity to deploy. `set_sparse_ebops_factor` makes the
97
- EBOPS (a proxy for the quantized hardware cost) reflect the sparse compute rather than a dense one; a
98
- cosine-decayed learning rate together with `restore_best_weights` keeps the learned budget from
99
- over-compressing near the end of training. `plot_history` shows the loss breakdown, the learned
100
- budget/threshold and the EBOPS in one figure, and the values to deploy are `layer.n_max_pixels` and
101
- `layer.threshold`.
92
+ Train the model with `SparseTrainingMonitor`. It records the loss breakdown, the learned
93
+ budget/threshold and the EBOPS each epoch, and it corrects the EBOPS (a proxy for the quantized
94
+ hardware cost) to the sparse compute automatically, so no extra setup is needed. The
95
+ only sparse-specific choices are the cosine-decayed learning rate and `restore_best_weights`, which
96
+ keep the learned budget from over-compressing near the end of training.
102
97
 
103
98
  ```python
104
- set_sparse_ebops_factor(model)
105
-
106
- steps_per_epoch = len(x_train) // 128
107
99
  early_stop = keras.callbacks.EarlyStopping(monitor='val_accuracy', mode='max', patience=20, restore_best_weights=True)
108
100
  model.compile(
109
- optimizer=keras.optimizers.Adam(cosine_lr(1e-3, epochs=100, steps_per_epoch=steps_per_epoch)),
101
+ optimizer=keras.optimizers.Adam(cosine_lr(1e-3, epochs=100, steps_per_epoch=len(x_train) // 128)),
110
102
  loss='categorical_crossentropy', metrics=['accuracy'],
111
103
  )
112
104
  history = model.fit(x_train, y_train, validation_data=(x_val, y_val),
113
105
  epochs=100, batch_size=128, callbacks=[early_stop, SparseTrainingMonitor()])
106
+ ```
114
107
 
108
+ After training, plot the diagnostics and read out the learned sparsity to deploy. `plot_history`
109
+ shows the loss breakdown, the learned budget/threshold and the EBOPS in one figure.
110
+ `print_quantization` / `plot_quantization` summarize the per-layer bit-widths. The values to
111
+ deploy are `layer.n_max_pixels` and `layer.threshold` (hls4ml converter will auto-parse these from the model).
112
+
113
+ ```python
115
114
  plot_history(history, early_stopping=early_stop) # loss breakdown, budget, threshold, EBOPS
116
115
  print_quantization(model) # per-layer bit-width distribution and EBOPS
117
116
  plot_quantization(model)
118
117
 
119
118
  ir = model.get_layer('input_reduce')
120
- print(f"deploy with n_max_pixels={ir.n_max_pixels}, threshold={ir.threshold:.3f}")
119
+ print(f"n_max_pixels={ir.n_max_pixels}, threshold={ir.threshold:.3f}")
121
120
  ```
122
121
 
123
122
  ## Converting a trained model to HLS with hls4ml
@@ -128,13 +127,31 @@ print(f"deploy with n_max_pixels={ir.n_max_pixels}, threshold={ir.threshold:.3f}
128
127
  > pip install "git+https://github.com/hftsoi/hls4ml.git@sparsepixels"
129
128
  > ```
130
129
 
131
- Once installed, converting a trained sparsepixels model to HLS is as usual:
130
+ Once installed, pull a config from the trained model, optionally set the per-layer parallelization
131
+ knobs, and convert:
132
132
 
133
133
  ```python
134
134
  import hls4ml
135
135
 
136
136
  hls_config = hls4ml.utils.config_from_keras_model(model, granularity='name')
137
- hls_config.setdefault('Model', {})['PipelineStyle'] = 'dataflow' # use "#pragma HLS DATAFLOW" (instead of the default "#pragma HLS PIPELINE" for io_parallel)
137
+ hls_config.setdefault('Model', {})['PipelineStyle'] = 'dataflow' # "#pragma HLS DATAFLOW"
138
+
139
+ # Optional per-layer parallelization knobs. Omit them for the fully-parallel, lowest-latency default.
140
+ # input_reduce Variant : 'tree' (default, lowest latency) or 'stream' (fewer resources)
141
+ # conv* PixelParallelFactor : active pixels in parallel (<= n_max_pixels)
142
+ # FiltParallelFactor : filters in parallel (<= that conv's filters)
143
+ # pool* PixelParallelFactor : active pixels in parallel
144
+ # ChanParallelFactor : channels in parallel
145
+ # flatten ParallelFactor : scatter positions in parallel (<= out_height * out_width)
146
+ n_sparse = model.get_layer('input_reduce').n_max_pixels
147
+ knobs = {
148
+ 'input_reduce': {'Variant': 'stream'},
149
+ 'conv1': {'PixelParallelFactor': n_sparse, 'FiltParallelFactor': 3},
150
+ 'pool1': {'PixelParallelFactor': n_sparse, 'ChanParallelFactor': 3},
151
+ 'flatten': {'ParallelFactor': 14 * 14}, # this model pools 28*28 -> 14*14
152
+ }
153
+ for name, cfg in knobs.items():
154
+ hls_config['LayerName'].setdefault(name, {}).update(cfg)
138
155
 
139
156
  hls_model = hls4ml.converters.convert_from_keras_model(
140
157
  model,
@@ -1,6 +1,6 @@
1
1
  [metadata]
2
2
  name = sparsepixels
3
- version = 0.3.0
3
+ version = 0.3.1
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
@@ -49,9 +49,10 @@ def set_sparse_ebops_factor(model):
49
49
  HGQ counts a conv's EBOPS as if it ran over the whole H*W feature map, which overestimates a
50
50
  sparse conv that only touches about n_max_pixels of them. This scales each sparse conv's EBOPS by
51
51
  n_max_pixels / (H*W) through HGQ's own ebops_factor, so both the reported EBOPS and the EBOPS
52
- regularization loss reflect the sparse compute. Call once after building the model, before fit.
53
- For a learnable budget it uses the current n_max_pixels, which is a good approximation since the
54
- budget moves slowly; call it again if the budget changes a lot.
52
+ regularization loss reflect the sparse compute. SparseTrainingMonitor applies this automatically
53
+ at the start of training, so you usually do not need to call it directly -- call it manually only
54
+ to correct EBOPS you inspect before training, or to refresh it if a learnable budget moves a lot
55
+ (it uses the current n_max_pixels, a good approximation since the budget moves slowly).
55
56
 
56
57
  Returns a dict of {layer_name: factor} for the sparse convs that were adjusted.
57
58
  """
@@ -157,12 +158,19 @@ class SparseTrainingMonitor(Callback):
157
158
 
158
159
  - n_max_pixels and threshold: the learned or fixed pixel budget and threshold of the InputReduce
159
160
  layer (suffixed with the layer name when there is more than one).
160
- - ebops: total EBOPS over the quantized layers, a proxy for the quantized hardware cost (sparse
161
- corrected once set_sparse_ebops_factor has been applied).
161
+ - ebops: total EBOPS over the quantized layers, a proxy for the quantized hardware cost. This
162
+ monitor applies set_sparse_ebops_factor at the start of training, so the EBOPS (and its
163
+ regularizer) reflect the sparse (active-pixel) compute without any manual call.
162
164
  - loss_task, loss_ebops, loss_n: the training loss split into the task loss, the EBOPS penalty and
163
165
  the pixel-budget penalty, each already scaled by its weight so the three add up to the loss.
164
166
  """
165
167
 
168
+ def on_train_begin(self, logs=None):
169
+ # Fold in set_sparse_ebops_factor so the user need not call it manually. Uses the current
170
+ # (initial) budget, matching a manual call right before fit; call it again yourself if a
171
+ # learnable budget moves a lot during training.
172
+ set_sparse_ebops_factor(self.model)
173
+
166
174
  def on_epoch_end(self, epoch, logs=None):
167
175
  if logs is None:
168
176
  return
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: sparsepixels
3
- Version: 0.3.0
3
+ Version: 0.3.1
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
@@ -47,20 +47,16 @@ pip install sparsepixels
47
47
 
48
48
  ## Getting Started
49
49
 
50
- Import the sparse layers, the quantization library (HGQ2), and the training utilities:
51
-
52
50
  ```python
53
51
  import keras
54
52
  from keras.layers import Flatten, Activation
55
- from hgq.layers import QConv2D, QDense
53
+ from hgq.layers import QDense
56
54
  from hgq.config import QuantizerConfigScope, LayerConfigScope
57
55
  from hgq.quantizer.config import QuantizerConfig
58
56
  from sparsepixels.layers import InputReduce, QConv2DSparse, AveragePooling2DSparse, MaxPooling2DSparse
59
57
  from sparsepixels.utils import (
60
- active_pixels_vs_threshold, plot_reduced_examples,
61
- set_sparse_ebops_factor, cosine_lr,
62
- SparseTrainingMonitor, plot_history,
63
- print_quantization, plot_quantization,
58
+ active_pixels_vs_threshold, plot_reduced_examples, cosine_lr,
59
+ SparseTrainingMonitor, plot_history, print_quantization, plot_quantization,
64
60
  )
65
61
  ```
66
62
 
@@ -112,31 +108,34 @@ with (
112
108
  model = keras.Model(x_in, x)
113
109
  ```
114
110
 
115
- Train the model, then read out the learned sparsity to deploy. `set_sparse_ebops_factor` makes the
116
- EBOPS (a proxy for the quantized hardware cost) reflect the sparse compute rather than a dense one; a
117
- cosine-decayed learning rate together with `restore_best_weights` keeps the learned budget from
118
- over-compressing near the end of training. `plot_history` shows the loss breakdown, the learned
119
- budget/threshold and the EBOPS in one figure, and the values to deploy are `layer.n_max_pixels` and
120
- `layer.threshold`.
111
+ Train the model with `SparseTrainingMonitor`. It records the loss breakdown, the learned
112
+ budget/threshold and the EBOPS each epoch, and it corrects the EBOPS (a proxy for the quantized
113
+ hardware cost) to the sparse compute automatically, so no extra setup is needed. The
114
+ only sparse-specific choices are the cosine-decayed learning rate and `restore_best_weights`, which
115
+ keep the learned budget from over-compressing near the end of training.
121
116
 
122
117
  ```python
123
- set_sparse_ebops_factor(model)
124
-
125
- steps_per_epoch = len(x_train) // 128
126
118
  early_stop = keras.callbacks.EarlyStopping(monitor='val_accuracy', mode='max', patience=20, restore_best_weights=True)
127
119
  model.compile(
128
- optimizer=keras.optimizers.Adam(cosine_lr(1e-3, epochs=100, steps_per_epoch=steps_per_epoch)),
120
+ optimizer=keras.optimizers.Adam(cosine_lr(1e-3, epochs=100, steps_per_epoch=len(x_train) // 128)),
129
121
  loss='categorical_crossentropy', metrics=['accuracy'],
130
122
  )
131
123
  history = model.fit(x_train, y_train, validation_data=(x_val, y_val),
132
124
  epochs=100, batch_size=128, callbacks=[early_stop, SparseTrainingMonitor()])
125
+ ```
133
126
 
127
+ After training, plot the diagnostics and read out the learned sparsity to deploy. `plot_history`
128
+ shows the loss breakdown, the learned budget/threshold and the EBOPS in one figure.
129
+ `print_quantization` / `plot_quantization` summarize the per-layer bit-widths. The values to
130
+ deploy are `layer.n_max_pixels` and `layer.threshold` (hls4ml converter will auto-parse these from the model).
131
+
132
+ ```python
134
133
  plot_history(history, early_stopping=early_stop) # loss breakdown, budget, threshold, EBOPS
135
134
  print_quantization(model) # per-layer bit-width distribution and EBOPS
136
135
  plot_quantization(model)
137
136
 
138
137
  ir = model.get_layer('input_reduce')
139
- print(f"deploy with n_max_pixels={ir.n_max_pixels}, threshold={ir.threshold:.3f}")
138
+ print(f"n_max_pixels={ir.n_max_pixels}, threshold={ir.threshold:.3f}")
140
139
  ```
141
140
 
142
141
  ## Converting a trained model to HLS with hls4ml
@@ -147,13 +146,31 @@ print(f"deploy with n_max_pixels={ir.n_max_pixels}, threshold={ir.threshold:.3f}
147
146
  > pip install "git+https://github.com/hftsoi/hls4ml.git@sparsepixels"
148
147
  > ```
149
148
 
150
- Once installed, converting a trained sparsepixels model to HLS is as usual:
149
+ Once installed, pull a config from the trained model, optionally set the per-layer parallelization
150
+ knobs, and convert:
151
151
 
152
152
  ```python
153
153
  import hls4ml
154
154
 
155
155
  hls_config = hls4ml.utils.config_from_keras_model(model, granularity='name')
156
- hls_config.setdefault('Model', {})['PipelineStyle'] = 'dataflow' # use "#pragma HLS DATAFLOW" (instead of the default "#pragma HLS PIPELINE" for io_parallel)
156
+ hls_config.setdefault('Model', {})['PipelineStyle'] = 'dataflow' # "#pragma HLS DATAFLOW"
157
+
158
+ # Optional per-layer parallelization knobs. Omit them for the fully-parallel, lowest-latency default.
159
+ # input_reduce Variant : 'tree' (default, lowest latency) or 'stream' (fewer resources)
160
+ # conv* PixelParallelFactor : active pixels in parallel (<= n_max_pixels)
161
+ # FiltParallelFactor : filters in parallel (<= that conv's filters)
162
+ # pool* PixelParallelFactor : active pixels in parallel
163
+ # ChanParallelFactor : channels in parallel
164
+ # flatten ParallelFactor : scatter positions in parallel (<= out_height * out_width)
165
+ n_sparse = model.get_layer('input_reduce').n_max_pixels
166
+ knobs = {
167
+ 'input_reduce': {'Variant': 'stream'},
168
+ 'conv1': {'PixelParallelFactor': n_sparse, 'FiltParallelFactor': 3},
169
+ 'pool1': {'PixelParallelFactor': n_sparse, 'ChanParallelFactor': 3},
170
+ 'flatten': {'ParallelFactor': 14 * 14}, # this model pools 28*28 -> 14*14
171
+ }
172
+ for name, cfg in knobs.items():
173
+ hls_config['LayerName'].setdefault(name, {}).update(cfg)
157
174
 
158
175
  hls_model = hls4ml.converters.convert_from_keras_model(
159
176
  model,
File without changes
File without changes