Description
Presently, the injector architecture for layer outputs works with linear models as during run-time, the execution stops at the chosen layer n
, injects faults into that layer and continues with the next n+1
layer. Suppose that the n
layer outputs feed back into n-1
layer (a non-linear connection), the current design would not handle that.
We have a workaround for such a non-linear architecture such as the SqueezeNet model which looks like so:
where the fire modules consist of "squeeze" and "expand" layers, with the expand layers splitting into two parallel convolutional layers of different left (1x1) and right (3x3) filters:
Keras stores all layers in order so the left and right layers are also stored with serial consecutive indices. This means for certain n
layers in the fire modules, the outputs feed into two parallel layers n1 + 1
and n2 + 1
and so we manually figured out the exception layers and handled the four cases like so:
while(True):
randnum = random.randint(0, len(model.layers) - 2)
mid_layer = model.layers[randnum]
get_output = K.function([model.layers[0].input], [mid_layer.output])
mid_layer_outputs = get_output([x_test])
elem_shape = mid_layer_outputs[0].shape
mid_layer_outputs[0] = mid_layer_outputs[0].flatten()
num = mid_layer_outputs[0].shape[0]
if(num >= sz):
break
ind = random.sample(range(num), sz)
for item in ind:
val = mid_layer_outputs[0][item]
pos = random.randint(0, 31)
val_ = bitflip(val, pos)
mid_layer_outputs[0][item] = val_
mid_layer_outputs[0] = mid_layer_outputs[0].reshape(elem_shape)
if (randnum in [6, 13, 21, 28, 36, 43, 50, 57]):
get_output2 = K.function([model.layers[0].input], [model.layers[randnum + 1].output])
ff_mlo = get_output2([x_test])
get_pred1 = K.function([model.layers[randnum + 2].input], [model.layers[randnum + 2].output])
pred1 = get_pred1([mid_layer_outputs]);pred10 = get_pred1([ff_mlo])
get_pred2 = K.function([model.layers[randnum + 4].input], [model.layers[-1].output])
pred = get_pred2([pred1, pred10]);
elif (randnum in [7, 14, 22, 29, 37, 44, 51, 58]):
get_output2 = K.function([model.layers[0].input], [model.layers[randnum - 1].output])
ff_mlo = get_output2([x_test])
get_pred1 = K.function([model.layers[randnum + 2].input], [model.layers[randnum + 2].output])
pred1 = get_pred1([mid_layer_outputs]);pred10 = get_pred1([ff_mlo])
get_pred2 = K.function([model.layers[randnum + 3].input], [model.layers[-1].output])
pred = get_pred2([pred1, pred10]);
elif (randnum in [8, 15, 23, 30, 38, 45, 52, 59]):
get_output2 = K.function([model.layers[0].input], [model.layers[randnum + 1].output])
ff_mlo = get_output2([x_test])
get_pred2 = K.function([model.layers[randnum + 2].input], [model.layers[-1].output])
pred = get_pred2([mid_layer_outputs, ff_mlo]);
elif (randnum in [9, 16, 24, 31, 39, 46, 53, 60]):
get_output2 = K.function([model.layers[0].input], [model.layers[randnum - 1].output])
ff_mlo = get_output2([x_test])
get_pred2 = K.function([model.layers[randnum + 1].input], [model.layers[-1].output])
pred = get_pred2([mid_layer_outputs, ff_mlo]);
else:
get_pred = K.function([model.layers[randnum + 1].input], [model.layers[-1].output])
pred = get_pred([mid_layer_outputs])
return pred
Obviously, this is cumbersome to figure out and be done for each non-linear architecture. Hence we need a way to generalize - this could be using the inbound nodes of the Keras layers so we know which layers to connect to during dynamic injection at any layer.