Skip to content

Commit a20508a

Browse files
author
Robert Muchsel
authored
Improve messages for reshaping and multi-pass (#115)
1 parent 491cf8d commit a20508a

File tree

10 files changed

+82
-43
lines changed

10 files changed

+82
-43
lines changed

README.md

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# MAX78000 Model Training and Synthesis
22

3-
_March 15, 2021_
3+
_March 17, 2021_
44

55
The Maxim Integrated AI project is comprised of four repositories:
66

@@ -422,6 +422,8 @@ For example, if 192-channel data is read using 64 active processors, Data Memory
422422

423423
*Note: Multi-pass also works with channel counts that are not a multiple of 64, and can be used with less than 64 active processors.*
424424

425+
*Note: For all multi-pass cases, the processor count per pass is rounded up to the next multiple of 4.*
426+
425427
### Streaming Mode
426428

427429
The machine also implements a streaming mode. Streaming allows input data dimensions that exceed the available per-channel data memory in the accelerator.
@@ -1237,6 +1239,8 @@ The following table describes the most important command line arguments for `ai8
12371239

12381240
The [quick-start guide](https://github.com/MaximIntegratedAI/MaximAI_Documentation/blob/master/Guides/YAML%20Quickstart.md) provides a short overview of the purpose and structure of the YAML network description file.
12391241

1242+
The following is a detailed guide into all supported configuration options.
1243+
12401244
An example network description for the ai85net5 architecture and MNIST is shown below:
12411245

12421246
```yaml
@@ -1325,7 +1329,7 @@ Data sets are for example `mnist`, `fashionmnist`, and `cifar-10`.
13251329

13261330
##### `output_map` (Optional)
13271331

1328-
The global `output_map`, if specified, overrides the memory instances where the last layer outputs its results. If not specified, this will be either the `output_processors` specified for the last layer, or, if that key does not exist, default to the number of processors needed for the output channels, starting at 0.
1332+
The global `output_map`, if specified, overrides the memory instances where the last layer outputs its results. If not specified, this will be either the `output_processors` specified for the last layer, or, if that key does not exist, default to the number of processors needed for the output channels, starting at 0. Please also see `output_processors`.
13291333

13301334
Example:
13311335
`output_map: 0x0000000000000ff0`
@@ -1350,7 +1354,7 @@ This key allows overriding of the processing sequence. The default is `0` for th
13501354

13511355
`processors` is specified as a 64-bit hexadecimal value. Dots (‘.’) and a leading ‘0x’ are ignored.
13521356

1353-
*Note: When using multi-pass (i.e., using more than 64 channels), the number processors is an integer division of the channel count, rounded up. For example, 60 processors are specified for 120 channels.*
1357+
*Note: When using multi-pass (i.e., using more than 64 channels), the number processors is an integer division of the channel count, rounded up to the next multiple of 4. For example, 52 processors are required for 100 channels (since 100 div 2 = 50, and 52 is the next multiple of 4). For best efficiency, use channel counts that are multiples of 4.*
13541358

13551359
Example for three processors 0, 4, and 8:
13561360
`processors: 0x0000.0000.0000.0111`

README.pdf

259 Bytes
Binary file not shown.

izer/cmsisnn.py

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -337,7 +337,7 @@ def run_eltwise(
337337
data = np.squeeze(data, axis=0)
338338

339339
# Convolution or passthrough
340-
if operator[ll] == op.CONV2D:
340+
if operator[ll] in [op.CONV2D, op.LINEAR]:
341341
if flatten[ll]:
342342
in_chan *= input_dim[ll][0] * input_dim[ll][1]
343343
data = data.reshape(in_chan, 1, 1)
@@ -442,7 +442,7 @@ def run_eltwise(
442442
f'pool with stride {pool_stride_str[ll]}')
443443
else:
444444
c_file.write('no pooling')
445-
if operator[ll] in [op.CONV1D, op.CONV2D, op.CONVTRANSPOSE2D]:
445+
if operator[ll] in [op.CONV1D, op.CONV2D, op.CONVTRANSPOSE2D, op.LINEAR]:
446446
conv_str = f', {op.string(operator[ll])} with kernel size ' \
447447
f'{kernel_size_str[ll]}, ' \
448448
f'stride {stride_str[ll]}, ' \
@@ -512,7 +512,8 @@ def run_eltwise(
512512
and padding[ll][0] == padding[ll][1] \
513513
and stride[ll][0] == stride[ll][1]:
514514
# Detect fully connected layers
515-
if in_dim == [1, 1] and output_dim[ll] == [1, 1]:
515+
if operator[ll] == op.LINEAR:
516+
assert in_dim == [1, 1] and output_dim[ll] == [1, 1]
516517
c_file.write(f' arm_fully_connected_q7({source}, '
517518
f'weights_{ll}, {in_chan}, {output_chan[ll]}, 7, '
518519
f'{7 - output_shift[ll]}, bias_{ll}, {buffer1}, '

izer/izer.py

Lines changed: 23 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -369,18 +369,28 @@ def main():
369369
eprint('Cannot concatenate outputs of different dimensions in layer '
370370
f'{ll}: {dim} vs {output_dim[e]}.')
371371
auto_input_dim[ll] = dim
372+
prev_op = operator[in_sequences[ll][0]]
372373
else:
373374
auto_input_dim[ll] = output_dim[in_sequences[ll]]
375+
prev_op = operator[in_sequences[ll]]
374376
else:
375377
auto_input_dim[ll] = output_dim[prev_sequence[ll]]
378+
prev_op = operator[prev_sequence[ll]]
376379
if conf_input_dim[ll] is None:
377380
input_dim[ll] = auto_input_dim[ll]
381+
# Print warning when going from 1D to 2D without explicitly reformatting the input
382+
if input_dim[ll][1] == 1 and operator[ll] in [op.CONV2D, op.CONVTRANSPOSE2D] \
383+
and prev_op == op.CONV1D:
384+
wprint(f'Using 1-dimensional data {input_dim[ll][0]}x{input_dim[ll][1]} for '
385+
f'layer {ll} with a {op.string(operator[ll])} operator. '
386+
'Use `in_dim:` to reshape the data to two dimensions, or to silence '
387+
'this message.')
378388
else:
379389
input_dim[ll] = conf_input_dim[ll]
380390
if operator[ll] != op.CONV1D:
381391
if pool_stride[ll][0] != pool_stride[ll][1]:
382-
eprint(f'{op.string(operator[ll])} in layer {ll} does not support non-square '
383-
f'pooling stride (currently set to '
392+
eprint(f'{op.string(operator[ll])} in layer {ll} does not support '
393+
f'non-square pooling stride (currently set to '
384394
f'{pool_stride[ll][0]}x{pool_stride[ll][1]}).')
385395
pooled_size = [(input_dim[ll][0] + pool_stride[ll][0] - pool[ll][0]
386396
- pool_dilation[ll][0] + 1) // pool_stride[ll][0],
@@ -398,12 +408,12 @@ def main():
398408

399409
if operator[ll] != op.CONV1D:
400410
if stride[ll][0] != stride[ll][1]:
401-
eprint(f'{op.string(operator[ll])} in layer {ll} does not support non-square '
402-
f'stride (currently set to {stride[ll][0]}x{stride[ll][1]}).')
411+
eprint(f'{op.string(operator[ll])} in layer {ll} does not support '
412+
f'non-square stride (currently set to {stride[ll][0]}x{stride[ll][1]}).')
403413
if operator[ll] != op.CONVTRANSPOSE2D and stride[ll][0] != 1:
404-
eprint(f'{op.string(operator[ll])} in layer {ll} does not support stride other '
405-
f'than 1 (currently set to {stride[ll][0]}x{stride[ll][1]}).')
406-
if operator[ll] in [op.NONE, op.CONV2D]:
414+
eprint(f'{op.string(operator[ll])} in layer {ll} does not support stride '
415+
f'other than 1 (currently set to {stride[ll][0]}x{stride[ll][1]}).')
416+
if operator[ll] in [op.NONE, op.CONV2D, op.LINEAR]:
407417
output_dim[ll] = [(pooled_size[0] - dilation[ll][0] * (kernel_size[ll][0] - 1)
408418
- 1 + 2 * padding[ll][0]) // stride[ll][0] + 1,
409419
(pooled_size[1] - dilation[ll][1] * (kernel_size[ll][1] - 1)
@@ -425,15 +435,15 @@ def main():
425435
input_channels[ll] //= pooled_dim[ll][0] * pooled_dim[ll][1]
426436
assert input_channels[ll] > 0
427437
if padding[ll][0] >= 3 and not tc.dev.SUPPORT_ARBITRARY_PADDING:
428-
eprint(f'{op.string(operator[ll])} in layer {ll} does not support `pad` >= 3 '
429-
f'(currently set to {padding[ll][0]}).')
438+
eprint(f'{op.string(operator[ll])} in layer {ll} does not support '
439+
f'`pad` >= 3 (currently set to {padding[ll][0]}).')
430440
else:
431441
if padding[ll][0] >= 3 and not tc.dev.SUPPORT_ARBITRARY_PADDING:
432-
eprint(f'{op.string(operator[ll])} in layer {ll} does not support `pad` >= 3 '
433-
f'(currently set to {padding[ll][0]}).')
442+
eprint(f'{op.string(operator[ll])} in layer {ll} does not support '
443+
f'`pad` >= 3 (currently set to {padding[ll][0]}).')
434444
if stride[ll][0] != 1 and not tc.dev.SUPPORT_ARBITRARY_STRIDE:
435-
eprint(f'{op.string(operator[ll])} in layer {ll} does not support stride other '
436-
f'than 1 (currently set to {stride[ll][0]}).')
445+
eprint(f'{op.string(operator[ll])} in layer {ll} does not support stride '
446+
f'other than 1 (currently set to {stride[ll][0]}).')
437447
output_dim[ll] = [(pooled_size[0] - dilation[ll][0] * (kernel_size[ll][0] - 1) - 1 +
438448
2 * padding[ll][0]) // stride[ll][0] + 1,
439449
1]

izer/max7800x.py

Lines changed: 18 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -534,7 +534,7 @@ def create_net( # pylint: disable=too-many-arguments,too-many-locals,too-many-b
534534
if not overwrite:
535535
eprint('The target folder', target_dir, 'exists. Use --overwrite to proceed.')
536536
else:
537-
wprint('--overwrite specified, writing to ', target_dir, ' even though it exists.')
537+
wprint('--overwrite specified, writing to', target_dir, 'even though it exists.')
538538

539539
# Redirect stdout?
540540
if log:
@@ -568,28 +568,28 @@ def create_net( # pylint: disable=too-many-arguments,too-many-locals,too-many-b
568568
processors_used |= bits
569569

570570
if input_chan[ll] > tc.dev.MAX_CHANNELS:
571-
eprint(f'Layer {ll} is configured for {input_chan[ll]} inputs, which exceeds '
572-
f'the system maximum of {tc.dev.MAX_CHANNELS}.')
571+
eprint(f'Layer {ll} is configured for {input_chan[ll]} input channels, which '
572+
f'exceeds the system maximum of {tc.dev.MAX_CHANNELS}.')
573573
if output_chan[ll] > tc.dev.MAX_CHANNELS:
574-
eprint(f'Layer {ll} is configured for {output_chan[ll]} outputs, which exceeds '
575-
f'the system maximum of {tc.dev.MAX_CHANNELS}.')
574+
eprint(f'Layer {ll} is configured for {output_chan[ll]} output channels, which '
575+
f'exceeds the system maximum of {tc.dev.MAX_CHANNELS}.')
576576
if (ll != start_layer or not fast_fifo_quad) \
577577
and popcount(processor_map[ll]) != in_expand_thresh[ll]:
578-
eprint(f'Layer {ll} has {input_chan[ll]} inputs with input expansion '
579-
f'{in_expand[ll]}, {operands[ll]} operands, threshold {in_expand_thresh[ll]}, '
580-
f'but enabled processor map 0x{processor_map[ll]:016x} '
578+
eprint(f'Layer {ll} has {input_chan[ll]} input channels using {in_expand[ll]} '
579+
f'passes, and {operands[ll]} operands ({in_expand_thresh[ll]} processors '
580+
f'per pass), but the enabled processor map 0x{processor_map[ll]:016x} '
581581
f'has {popcount(processor_map[ll])} bits instead of the '
582582
f'expected number of {in_expand_thresh[ll]}.')
583583
if ll == start_layer and fast_fifo_quad \
584584
and popcount(processor_map_0) != in_expand_thresh[ll]:
585-
eprint(f'Layer {ll} has {input_chan[ll]} inputs with input expansion '
586-
f'{in_expand[ll]}, threshold {in_expand_thresh[ll]}, but '
585+
eprint(f'Layer {ll} has {input_chan[ll]} input channels using {in_expand[ll]} '
586+
f'passes ({in_expand_thresh[ll]} processors per pass), but the '
587587
f'enabled processor map 0x{processor_map[ll]:016x} '
588588
f'has {popcount(processor_map[ll])} bits instead of the '
589589
f'expected number of {in_expand_thresh[ll]}.')
590590
if popcount(output_processor_map[ll]) != out_expand_thresh[ll]:
591-
eprint(f'Layer {ll} has {output_chan[ll]} outputs with output expansion '
592-
f'{out_expand[ll]}, threshold {out_expand_thresh[ll]}, but '
591+
eprint(f'Layer {ll} has {output_chan[ll]} output channels using {out_expand[ll]} '
592+
f'passes ({out_expand_thresh[ll]} processors per pass), but the '
593593
f'processor output map 0x{output_processor_map[ll]:016x} '
594594
f'has {popcount(output_processor_map[ll])} bits instead of the '
595595
f'expected number of {out_expand_thresh[ll]}.')
@@ -1434,7 +1434,7 @@ def create_net( # pylint: disable=too-many-arguments,too-many-locals,too-many-b
14341434
# Write Pointer Timeslot Offset Register
14351435
# Used for 1x1 convolution, and pooling without convolution
14361436
val = 0
1437-
if operator[ll] == op.CONV2D:
1437+
if operator[ll] in [op.CONV2D, op.LINEAR]:
14381438
if kernel_size[ll] == [1, 1] and conv_groups[ll] == 1:
14391439
val = 1
14401440
elif conv_groups[ll] > 1 and not broadcast_mode[ll]:
@@ -1615,7 +1615,7 @@ def create_net( # pylint: disable=too-many-arguments,too-many-locals,too-many-b
16151615
if operator[ll] == op.CONV1D:
16161616
val |= kernel_size[ll][0] << 8 | 1 << 12
16171617
assert kernel_size[ll][0] < 2**4
1618-
elif (operator[ll] == op.CONV2D and kernel_size[ll] == [1, 1]
1618+
elif (operator[ll] in [op.CONV2D, op.LINEAR] and kernel_size[ll] == [1, 1]
16191619
or operator[ll] == op.NONE and operands[ll] == 1):
16201620
val |= 1 << 8
16211621
if operands[ll] > 1:
@@ -1624,7 +1624,7 @@ def create_net( # pylint: disable=too-many-arguments,too-many-locals,too-many-b
16241624
if (pool[ll][0] > 1 or pool[ll][1] > 1) \
16251625
and pool_first[ll]:
16261626
val |= 1 << 16
1627-
if operator[ll] != op.NONE: # in [op.CONV2D, op.CONVTRANSPOSE2D]:
1627+
if operator[ll] != op.NONE: # CONV2D, LINEAR, CONVTRANSPOSE2D
16281628
val |= 1 << 17
16291629
assert 0 <= oned_sad < 2**4
16301630
val |= oned_sad << 4
@@ -1634,7 +1634,7 @@ def create_net( # pylint: disable=too-many-arguments,too-many-locals,too-many-b
16341634

16351635
# Configure tram pointer max
16361636
if operator[ll] == op.CONV1D or \
1637-
operator[ll] == op.CONV2D and kernel_size[ll] == [1, 1]:
1637+
operator[ll] in [op.CONV2D, op.LINEAR] and kernel_size[ll] == [1, 1]:
16381638
if flatten_prod >= 2**4:
16391639
assert flatten_prod < 2**16
16401640
val = flatten_prod << 16 | (2 * flatten_prod + 1)
@@ -1950,7 +1950,7 @@ def create_net( # pylint: disable=too-many-arguments,too-many-locals,too-many-b
19501950
val = override_rollover
19511951
elif not tc.dev.REQUIRE_NEW_STREAMING:
19521952
if big_data[ll]:
1953-
# FIXME stream_start + max(stride[ll][1], pool_stride[ll][1])
1953+
# FIXME: stream_start + max(stride[ll][1], pool_stride[ll][1])
19541954
val = 12
19551955
else:
19561956
val = stream_start + (pool[ll][0] - 1) * input_dim[ll][1] \
@@ -2477,7 +2477,7 @@ def run_eltwise(
24772477
data = np.squeeze(data, axis=0)
24782478

24792479
# Convolution or passthrough
2480-
if operator[ll] == op.CONV2D:
2480+
if operator[ll] in [op.CONV2D, op.LINEAR]:
24812481
if flatten[ll]:
24822482
in_chan *= pooled_dim[ll][0] * pooled_dim[ll][1]
24832483
data = data.reshape(in_chan, 1, 1)

izer/op.py

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
CONV1D = 1
1212
CONV2D = 2
1313
CONVTRANSPOSE2D = 3
14+
LINEAR = 4
1415

1516
ACT_RELU = 1
1617
ACT_ABS = 2
@@ -32,6 +33,7 @@
3233
CONV1D: 'conv1d',
3334
CONV2D: 'conv2d',
3435
CONVTRANSPOSE2D: 'convtranspose2d',
36+
LINEAR: 'linear',
3537
}
3638

3739
ELT_NAMES = {
@@ -50,18 +52,20 @@
5052
ELTWISE_OR: 0b10,
5153
}
5254

55+
UNKNOWN = '????'
56+
5357

5458
def string(
5559
op,
5660
elt=False,
5761
):
5862
"""
59-
Return string representation of operator `op`
63+
Return string representation of operator `op`.
6064
"""
6165
if not elt:
62-
return OP_NAMES[op] if op in OP_NAMES else '????'
66+
return OP_NAMES[op] if op in OP_NAMES else UNKNOWN
6367
# else:
64-
return ELT_NAMES[op] if op in ELT_NAMES else '????'
68+
return ELT_NAMES[op] if op in ELT_NAMES else UNKNOWN
6569

6670

6771
def eltwise(
@@ -88,9 +92,9 @@ def act_string(
8892
act,
8993
):
9094
"""
91-
Return string representation of activation `act`
95+
Return string representation of activation `act`.
9296
"""
9397
if act is None:
9498
return ACT_NAMES[NONE]
9599
# else:
96-
return ACT_NAMES[act] if act in ACT_NAMES else '????'
100+
return ACT_NAMES[act] if act in ACT_NAMES else UNKNOWN

izer/yamlcfg.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -280,7 +280,7 @@ def error_exit(message, sequence):
280280
padding[sequence] = [0, 0]
281281
elif conv in ['linear', 'fc', 'mlp']:
282282
# Emulate using Conv2D with 1x1 kernels and 1x1 data
283-
operator[sequence] = op.CONV2D
283+
operator[sequence] = op.LINEAR
284284
kernel_size[sequence] = FC_KERNEL
285285
padding[sequence] = [0, 0]
286286
else:

tests/sample_test_power-ai87.npy

512 KB
Binary file not shown.

tests/test-power-ai87.yaml

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
arch: test
2+
dataset: test_power-ai87
3+
4+
layers:
5+
- out_offset: 0x4000
6+
processors: 0xffffffffffffffff
7+
operation: conv2d
8+
kernel_size: 3x3
9+
activation: none
10+
readahead: True
11+
calcx4: True
12+
output_shift: -3
13+
- out_offset: 0x0000
14+
processors: 0xffffffffffffffff
15+
operation: conv2d
16+
kernel_size: 3x3
17+
activation: none
18+
readahead: True
19+
calcx4: True
20+
output_shift: -3

tests/weights_test_power-ai87.npy

576 KB
Binary file not shown.

0 commit comments

Comments
 (0)