Die Feature Pyramid im RetinaNet enthält Inputs aus den drei obersten Blöcken des ResNets: C3, C4 und C5.
Diese Blöcke werden zunächst mithilfe einer -Convolution auf die richtige Größe gebracht. Anschließend werden mittels der Pyramide neue Feature Maps erstellt:
P3 ergibt sich als 3x3-Convolution aus der Summe von C3 und C4 (nach der 1x1-Convolution und Upsampling von C4),
P4 ergibt sich als 3x3-Convolution aus der Summe von C4 und C5 (nach der 1x1-Convolution und Upsampling von C5),
P5 ergibt sich als 3x3-Convolution aus C5 (nach der 1x1-Convolution),
P6 ergibt sich als 3x3-Convolution (mit Stride 2) aus C5 (ohne 1x1-Convolution),
P7 ergibt sich als 3x3-Convolution aus P6 (nach einer ReLU).
class FeaturePyramid(keras.layers.Layer): """Builds the Feature Pyramid with the feature maps from the backbone. Attributes: num_classes: Number of classes in the dataset. backbone: The backbone to build the feature pyramid from. Currently supports ResNet50 only. """ def __init__(self, backbone=None, **kwargs): super().__init__(name="FeaturePyramid", **kwargs) self.backbone = backbone if backbone else get_backbone() self.conv_c3_1x1 = keras.layers.Conv2D(256, 1, 1, "same") self.conv_c4_1x1 = keras.layers.Conv2D(256, 1, 1, "same") self.conv_c5_1x1 = keras.layers.Conv2D(256, 1, 1, "same") self.conv_c3_3x3 = keras.layers.Conv2D(256, 3, 1, "same") self.conv_c4_3x3 = keras.layers.Conv2D(256, 3, 1, "same") self.conv_c5_3x3 = keras.layers.Conv2D(256, 3, 1, "same") self.conv_c6_3x3 = keras.layers.Conv2D(256, 3, 2, "same") self.conv_c7_3x3 = keras.layers.Conv2D(256, 3, 2, "same") self.upsample_2x = keras.layers.UpSampling2D(2) def call(self, images, training=False): c3_output, c4_output, c5_output = self.backbone(images, training=training) p3_output = self.conv_c3_1x1(c3_output) p4_output = self.conv_c4_1x1(c4_output) p5_output = self.conv_c5_1x1(c5_output) p4_output = p4_output + self.upsample_2x(p5_output) p3_output = p3_output + self.upsample_2x(p4_output) p3_output = self.conv_c3_3x3(p3_output) p4_output = self.conv_c4_3x3(p4_output) p5_output = self.conv_c5_3x3(p5_output) p6_output = self.conv_c6_3x3(c5_output) p7_output = self.conv_c7_3x3(tf.nn.relu(p6_output)) return p3_output, p4_output, p5_output, p6_output, p7_output
Definition: Regressions-Heads im RetinaNet
Die Regression-Heads erhalten jeweils eine Featuremap aus dem FPN (also P3, …, P7) als Input - in der Illustration des Heads ist das der erste grüne Layer.
Anschließend gibt es 4 Conv-Layer (die for-loop im Code), die von einem letzten, fünften Conv Layer abgeschlossen werden, der die entsprechenden Outputs generiert.
def build_head(output_filters, bias_init): """Builds the class/box predictions head. Arguments: output_filters: Number of convolution filters in the final layer. bias_init: Bias Initializer for the final convolution layer. Returns: A keras sequential model representing either the classification or the box regression head depending on `output_filters`. """ head = keras.Sequential([keras.Input(shape=[None, None, 256])]) kernel_init = tf.initializers.RandomNormal(0.0, 0.01) for _ in range(4): head.add( keras.layers.Conv2D(256, 3, padding="same", kernel_initializer=kernel_init) ) head.add(keras.layers.ReLU()) head.add( keras.layers.Conv2D( output_filters, 3, 1, padding="same", kernel_initializer=kernel_init, bias_initializer=bias_init, ) ) return head