-
Notifications
You must be signed in to change notification settings - Fork 2
/
Copy pathconverters.jl
293 lines (248 loc) · 8.25 KB
/
converters.jl
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
KL = include("./KnetLayers/src/KnetLayers.jl")
onnx_utils = include("./onnx_utils.jl")
function ONNXtoGraph(file)
"""
Given the path of an ONNX file, construct its graph.
"""
f = readproto(open(file), Proto.ModelProto());
convert(f).graph
end
function PrintGraph(g)
"""
Prints the ONNX graph.
"""
println("model inputs: ", (x->x.name).(g.input))
println("model outputs: ", (x->x.name).(g.output))
for (i, node) in enumerate(g.node)
print("(op", i, ") ", node.op_type)
println()
for (i, input) in enumerate(node.input)
println("\tinput", i, ": " , input)
end
for (i, output) in enumerate(node.output)
println("\toutput", i, ": " , output)
end
end
end
function convert(node, g)
"""
Given a node, calls the appropriate constructor for the corresponding (args, layer, outs)
"""
if node.op_type == "Gemm"; return converter_gemm(node, g); end
if node.op_type == "Add"; return converter_add(node, g); end
if node.op_type == "Relu"; return converter_relu(node, g); end
if node.op_type == "LeakyRelu"; return converter_leakyrelu(node,g); end
if node.op_type == "Conv"; return converter_cnn(node,g); end
if node.op_type == "MaxPool"; return converter_maxpool(node,g); end
if node.op_type == "AveragePool"; return converter_avgpool(node,g); end
if node.op_type == "Dropout"; return converter_dropout(node,g); end
if node.op_type == "Flatten"; return converter_flatten(node,g); end
if node.op_type == "Constant"; return converter_constant(node, g); end
if node.op_type == "Shape"; return converter_shape(node, g); end
if node.op_type == "Squeeze"; return converter_squeeze(node, g); end
if node.op_type == "Unsqueeze"; return converter_unsqueeze(node,g); end
if node.op_type == "Concat"; return converter_concat(node,g); end
if node.op_type == "Gather"; return converter_gather(node,g); end
if node.op_type == "ConstantOfShape"; return converter_ConstantOfShape(node, g); end
if node.op_type == "RNN"; return converter_rnn(node, g);
else; println("ONNX Operation not yet implemented: ", node.op_type);
end
end
# Unit Layer for Testing
struct dummy_layer
end
(d::dummy_layer)(x) = x
function converter_rnn(node, g)
return (node.input, dummy_layer(), node.output)
end
# TODO: convert all onnx params to knet params (use convert_params(w))
"""
Converters Begin Here
# A converter's inputs: graph node and the graph
# they return 3 elements:
# - args: the names of the tensors that will be needed for the calculations. These are just the names: strings.
# - layer: a KnetLayer will be constructed. If the weights are in the initializer, the layer will be modified with them.
# - outs: the names of the tensors that are the outputs of the calculations. These are just the names: strings.
"""
# GEMM - trains bias also for gemm that is not a linear layer, fix that, write new gemm and a separate linear
function converter_gemm(node, g)
input1 = node.input[1]
#the layer is a Knet Layer
layer = KnetOnnx.KnetLayers.Linear(input=1,output=1)
# use g.initializer to modify KnetLayer
w_name = node.input[2]
b_name = node.input[3]
w = g.initializer[w_name]
w = transpose(w)
b = g.initializer[b_name]
w = KnetOnnx.KnetLayers.ConvertParams(w)
b = KnetOnnx.KnetLayers.ConvertParams(b)
layer.bias = b
layer.mult.weight = w
# return input tensor NAMES, it is called args: [input1, ...]
# you can take the inputs from model.tensors using these names
args = [input1]
outs = [node]
# returns these 3, use these to create ModelLayer
(args, layer, node.output)
end
# ADD
function converter_add(node, g)
args = node.input
outs = node.output
layer = KL.Add()
return (args, layer, outs)
end
# RELU - done
function converter_relu(node, g)
args = node.input
layer = KL.ReLU()
outs = node.output
(args, layer, outs)
end
# LEAKY RELU - done
function converter_leakyrelu(node, g)
args = node.input
alpha = node.attribute[:alpha]
layer = KL.LeakyReLU(alpha)
outs = node.output
(args, layer, outs)
end
# CONV
#conv1 = KnetOnnx.KnetLayers.Conv(;height=3, width=3, inout = 3=>64)
#currently treating [1,1,1,1] padding as an integer 1, same for stride
function converter_cnn(node, g)
args = [node.input[1]]
out = node.output
padding = 0
strides = 0
if :pads in keys(node.attribute); padding = node.attribute[:pads][1]; end
if :strides in keys(node.attribute); stride = node.attribute[:strides][1]; end
layer = KnetOnnx.KL.Conv(height=1,width=1,inout=1=>1; padding = padding, stride = stride)
if length(node.input) >= 2
w_name = node.input[2]
w = g.initializer[w_name]
#might cause a problem later on with different convs
layer.weight = w
end
if length(node.input) >= 3
b_name = node.input[3]
b = g.initializer[b_name]
layer.bias = reshape(b, 1, 1, size(b)[1], 1)
end
(args, layer, out)
end
# MaxPool
#currently treating [1,1,1,1] padding as an integer 1, same for
function converter_maxpool(node, g)
args = node.input
outs = node.output
stride = 0
padding = 0
if :pads in keys(node.attribute); padding = node.attribute[:pads][1]; end
if :strides in keys(node.attribute); stride = node.attribute[:strides][1]; end
layer = KL.Pool(padding=padding, stride=stride, mode=0)
(args, layer, outs)
end
# AveragePool
function converter_avgpool(node, g)
args = node.input
outs = node.output
stride = 0
padding = 0
if :pads in keys(node.attribute); padding = node.attribute[:pads][1]; end
if :strides in keys(node.attribute); stride = node.attribute[:strides][1]; end
layer = KL.Pool(padding=padding, stride=stride, mode=1)
(args, layer, outs)
end
# DROPOUT
function converter_dropout(node, g)
args = node.input
outs = node.output
layer = KL.Dropout(p = node.attribute[:ratio])
(args, layer, outs)
end
# FLATTEN
function converter_flatten(node, g)
args = node.input
outs = node.output
layer = KL.Flatten()
(args, layer, outs)
end
# BATCHNORM
function node_to_batchnorm(node, g)
momentum = node.attribute[:momentum]
epsilon = node.attribute[:epsilon]
spatial = node.attribute[:spatial]
scale = g.initializer[node.input[2]]
B = g.initializer[node.input[3]]
mean = g.initializer[node.input[4]]
variance = g.initializer[node.input[5]]
KL.BatchNorm(length(scale); momentum=momentum, mean=mean, var=variance)
end
# IMAGE SCALER
function node_to_imagescaler(node, g)
bias = node.attribute[:bias]
scale = node.attribute[:scale]
#ScalerLayer(x) = scale .* x
end
# RNN
function node_to_RNN(node, g)
activations = node.attribute[:activations]
hidden_size = node.attribute[:hidden_size]
end
# CONSTANT
function converter_constant(node, g)
args = node.input
outs = node.output
onnx_constant_value = node.attribute[:value]
julia_constant_value = onnx_utils.UInt8toFloat32(onnx_constant_value)
layer = KL.constant_layer(julia_constant_value)
return (args, layer, outs)
end
# SHAPE
function converter_shape(node, g)
args = node.input
outs = node.output
layer = KL.shape_layer()
(args, layer, outs)
end
# UNSQUEEZE
function converter_unsqueeze(node, g)
args = node.input
outs = node.output
layer = KL.unsqueeze_layer(node.attribute[:axes])
(args, layer, outs)
end
# SQUEEZE
function converter_squeeze(node, g)
args = node.input
outs = node.output
layer = KL.squeeze_layer(node.attribute[:axes])
(args, layer, outs)
end
# CONCAT
function converter_concat(node, g)
args = node.input
outs = node.output
axis = node.attribute[:axis]
layer = KL.Concat(axis)
(args, layer, outs)
end
# Gather
function converter_gather(node, g)
args = node.input
outs = node.output
axis = node.attribute[:axis] +1 #+1 is for Julia
layer = KL.Gather(axis)
(args, layer, outs)
end
#
function converter_ConstantOfShape(node, g)
args = node.input
outs = node.output
onnx_constant_value = node.attribute[:value]
julia_constant_value = onnx_utils.UInt8toFloat32(onnx_constant_value)
layer = KL.ConstantOfShape(julia_constant_value[1])
(args, layer, outs)
end