You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
`Sinabs` can parse a `torch.nn.Sequential` like architecture, so it is recommended to
43
-
use a `Sequential` like network.
42
+
`Sinabs` can parse a `torch.nn.Sequential` like architecture, so it is recommended to use a `Sequential` like network.
44
43
45
44
As of `v3.1.0`, we released a network graph extraction feature that helps users deploy their networks with more complex architectures into the devkit.
46
45
Our `Speck` chip, in fact, supports branched architectures. With the graph extraction feature, we support a range of network structures, as shown below:

52
50
53
51
Two networks with merging outputs:
54
52
55
53

56
54
57
-
A network with a merge and a split:
55
+
A network with residual connections:
58
56
59
-

57
+

58
+
59
+
A more complex network:
60
60
61
+

61
62
62
63
Note: with the graph extracture feature it is possible to implement recurrent neural networks. However, this is not recommended or supported as it can result in deadlock on the chip.
63
64
65
+
Note2: the use of two parallel network although supported by our chip was not fully considered in our sinabs implementation.
66
+
64
67
## How to make use of the graph extraction feature?
65
68
66
69
For general architectures, users need to define their classes, by defining at least the `__init__` method with all the layers, as well as an appropriate `forward` method.
67
70
68
71
Here is an example to define a network with a merge and a split:
69
72
70
73
```python
74
+
import torch
71
75
import torch.nn as nn
72
76
73
77
from sinabs.activation.surrogate_gradient_fn import PeriodicExponential
74
78
from sinabs.layers import IAFSqueeze, Merge, SumPool2d
75
79
80
+
76
81
classSNN(nn.Module):
77
82
def__init__(self, batch_size) -> None:
78
83
super().__init__()
79
-
80
-
self.conv1= nn.Conv2d(2, 1, 2, 1, bias=False)
81
-
self.iaf1= IAFSqueeze(
84
+
# -- graph node A --
85
+
self.conv_A= nn.Conv2d(2, 4, 2, 1, bias=False)
86
+
self.iaf_A= IAFSqueeze(
82
87
batch_size=batch_size,
83
88
min_v_mem=-1.0,
84
89
spike_threshold=1.0,
85
90
surrogate_grad_fn=PeriodicExponential(),
86
91
)
87
-
88
-
self.conv2= nn.Conv2d(1, 1, 2, 1, bias=False)
89
-
self.iaf2= IAFSqueeze(
92
+
# -- graph node B --
93
+
self.conv_B= nn.Conv2d(4, 4, 2, 1, bias=False)
94
+
self.iaf2_B= IAFSqueeze(
90
95
batch_size=batch_size,
91
96
min_v_mem=-1.0,
92
97
spike_threshold=1.0,
93
98
surrogate_grad_fn=PeriodicExponential(),
94
99
)
95
-
self.pool2= SumPool2d(2, 2)
96
-
97
-
self.conv3= nn.Conv2d(1, 1, 2, 1, bias=False)
98
-
self.iaf3= IAFSqueeze(
100
+
self.pool_B= SumPool2d(2, 2)
101
+
# -- graph node C --
102
+
self.conv_C= nn.Conv2d(4, 4, 2, 1, bias=False)
103
+
self.iaf_C= IAFSqueeze(
99
104
batch_size=batch_size,
100
105
min_v_mem=-1.0,
101
106
spike_threshold=1.0,
102
107
surrogate_grad_fn=PeriodicExponential(),
103
108
)
104
-
self.pool3 = SumPool2d(2, 2)
105
-
self.pool3a = SumPool2d(5, 5)
106
-
107
-
self.conv4 = nn.Conv2d(1, 1, 2, 1, bias=False)
108
-
self.iaf4 = IAFSqueeze(
109
+
self.pool_C = SumPool2d(2, 2)
110
+
# -- graph node D --
111
+
self.conv_D = nn.Conv2d(4, 4, 2, 1, bias=False)
112
+
self.iaf_D = IAFSqueeze(
109
113
batch_size=batch_size,
110
114
min_v_mem=-1.0,
111
115
spike_threshold=1.0,
112
116
surrogate_grad_fn=PeriodicExponential(),
113
117
)
114
-
self.pool4 = SumPool2d(3, 3)
115
-
116
-
self.flat1 = nn.Flatten()
117
-
self.flat2 = nn.Flatten()
118
-
119
-
self.conv5 = nn.Conv2d(1, 1, 2, 1, bias=False)
120
-
self.iaf5 = IAFSqueeze(
118
+
# -- graph node E --
119
+
self.conv_E = nn.Conv2d(4, 4, 2, 1, bias=False)
120
+
self.iaf3_E = IAFSqueeze(
121
121
batch_size=batch_size,
122
122
min_v_mem=-1.0,
123
123
spike_threshold=1.0,
124
124
surrogate_grad_fn=PeriodicExponential(),
125
125
)
126
-
127
-
self.fc2 = nn.Linear(25, 10, bias=False)
128
-
self.iaf2_fc = IAFSqueeze(
126
+
self.pool_E = SumPool2d(2, 2)
127
+
# -- graph node F --
128
+
self.conv_F = nn.Conv2d(4, 4, 2, 1, bias=False)
129
+
self.iaf_F = IAFSqueeze(
130
+
batch_size=batch_size,
131
+
min_v_mem=-1.0,
132
+
spike_threshold=1.0,
133
+
surrogate_grad_fn=PeriodicExponential(),
134
+
)
135
+
# -- graph node G --
136
+
self.fc3 = nn.Linear(144, 10, bias=False)
137
+
self.iaf3_fc = IAFSqueeze(
129
138
batch_size=batch_size,
130
139
min_v_mem=-1.0,
131
140
spike_threshold=1.0,
@@ -134,43 +143,47 @@ class SNN(nn.Module):
134
143
135
144
# -- merges --
136
145
self.merge1 = Merge()
137
-
self.merge2 = Merge()
146
+
147
+
# -- falts --
148
+
self.flat_D = nn.Flatten()
149
+
self.flat_F = nn.Flatten()
138
150
139
151
defforward(self, x):
140
152
# conv 1 - A/0
141
-
con1_out=self.conv1(x)
142
-
iaf1_out=self.iaf1(con1_out)
153
+
convA_out=self.conv_A(x)
154
+
iaf_A_out=self.iaf_A(convA_out)
143
155
144
156
# conv 2 - B/1
145
-
conv2_out=self.conv2(iaf1_out)
146
-
iaf2_out=self.iaf2(conv2_out)
147
-
pool2_out=self.pool2(iaf2_out)
157
+
conv_B_out=self.conv_B(iaf_A_out)
158
+
iaf_B_out=self.iaf2_B(conv_B_out)
159
+
pool_B_out=self.pool_B(iaf_B_out)
148
160
149
161
# conv 3 - C/2
150
-
conv3_out =self.conv3(iaf1_out)
151
-
iaf3_out =self.iaf3(conv3_out)
152
-
pool3_out =self.pool3(iaf3_out)
153
-
pool3a_out =self.pool3a(iaf3_out)
154
-
155
-
# conv 4 - D/3
156
-
merge1_out =self.merge1(pool2_out, pool3_out)
157
-
conv4_out =self.conv4(merge1_out)
158
-
iaf4_out =self.iaf4(conv4_out)
159
-
pool4_out =self.pool4(iaf4_out)
160
-
flat1_out =self.flat1(pool4_out)
161
-
162
-
# conv 5 - E/4
163
-
conv5_out =self.conv5(pool3a_out)
164
-
iaf5_out =self.iaf5(conv5_out)
165
-
flat2_out =self.flat2(iaf5_out)
166
-
167
-
# fc 2 - F/5
168
-
merge2_out =self.merge2(flat2_out, flat1_out)
169
-
170
-
fc2_out =self.fc2(merge2_out)
171
-
iaf2_fc_out =self.iaf2_fc(fc2_out)
172
-
173
-
return iaf2_fc_out
162
+
conv_C_out =self.conv_C(pool_B_out)
163
+
iaf_C_out =self.iaf_C(conv_C_out)
164
+
pool_C_out =self.pool_C(iaf_C_out)
165
+
166
+
# conv 4 - D/4
167
+
conv_D_out =self.conv_D(pool_C_out)
168
+
iaf_D_out =self.iaf_D(conv_D_out)
169
+
# fc 1 - E/3
170
+
conv_E_out =self.conv_E(pool_B_out)
171
+
iaf3_E_out =self.iaf3_E(conv_E_out)
172
+
pool_E_out =self.pool_E(iaf3_E_out)
173
+
174
+
# fc 2 - F/6
175
+
conv_F_out =self.conv_F(pool_E_out)
176
+
iaf_F_out =self.iaf_F(conv_F_out)
177
+
178
+
# fc 2 - G/5
179
+
flat_D_out =self.flat_D(iaf_D_out)
180
+
flat_F_out =self.flat_F(iaf_F_out)
181
+
182
+
merge1_out =self.merge1(flat_D_out, flat_F_out)
183
+
fc3_out =self.fc3(merge1_out)
184
+
iaf3_fc_out =self.iaf3_fc(fc3_out)
185
+
186
+
return iaf3_fc_out
174
187
```
175
188
176
189
## Can I achieve a "Residual Connection" like ResNet does?
@@ -180,9 +193,9 @@ change the `samna.speck2f.configuration.CNNLayerDestination.layer` to achieve th
180
193
familiar with the `samna-configuration`.
181
194
You can also make use of our network graph extraction feature, to implement residual networks.
182
195
183
-
## How to use "Residual Connection" manually?
196
+
## How can I define "Residual Connection" manually?
184
197
185
-
Alright! Here I will give an example of achieving the "Residual Connection" by manually modify the `samna-configuration`.
198
+
You can also achieve "Residual Connection" by manually modify the `samna-configuration`.
186
199
187
200
Let's say you want an architecture like below:
188
201
@@ -221,7 +234,7 @@ class ResidualBlock(nn.Module):
221
234
222
235
```
223
236
224
-
Since currently Sinabs can only parse Sequential like network, we need to do some tedious work like below:
0 commit comments