Skip to content

Instantly share code, notes, and snippets.

@YimianDai
Last active September 6, 2019 00:42
Show Gist options
  • Save YimianDai/0cfc345c49208bf351ccde723e72a9cf to your computer and use it in GitHub Desktop.
Save YimianDai/0cfc345c49208bf351ccde723e72a9cf to your computer and use it in GitHub Desktop.
Class-SSD

SSD-Data-Flow

__init__

            self.cls_decoder = MultiPerClassDecoder(len(self.classes) + 1, thresh=0.01)

这个输出跟 training loss 没关系,不会影响 training

hybrid_forward

    def hybrid_forward(self, F, x):
        """Hybrid forward"""
        features = self.features(x)
  1. 得到的 features 是 List, 里面的每个元素都是 (B, C, H_i, W_i) 的 feature cube
        cls_preds = [F.flatten(F.transpose(cp(feat), (0, 2, 3, 1)))
                     for feat, cp in zip(features, self.class_predictors)]
  1. 这里的 cp(feat) 是每层 ConvPredictor 的输出,(B, num_anchors x (fg_cls + 1), H_i, W_i),只不过 H 和 W 每层因为下采样的关系不一样
  2. F.transpose(cp(feat), (0, 2, 3, 1)) 将其变换成 (B, H_i, W_i, num_anchors x (fg_cls + 1))
  3. F.flatten 之后将其变为 (B, H_i x W_i x num_anchors x (fg_cls + 1))
  4. 最后得到的 cls_preds 是一个 List,里面的每个元素都是 (N, H_i x W_i x num_anchors x (fg_cls + 1)) 矩阵,i 表示第 i 层输出
        box_preds = [F.flatten(F.transpose(bp(feat), (0, 2, 3, 1)))
                     for feat, bp in zip(features, self.box_predictors)]
  1. 每一个 feat(B, C, H_i, W_i) 的 MXNet.NDArray, bp(feat)(B, num_anchors x 4, H_i, W_i) 的 MXNet.NDArray, F.transpose(bp(feat), (0, 2, 3, 1)) 则是 (B, H_i, W_i, num_anchors x 4) 的 MXNet.NDArray, 再经过 flatten 后得到的是 (B, H_i x W_i x num_anchors x 4) 的 MXNet.NDArray
  2. 因此,最后得到的 box_preds 是一个 List,里面的每个元素都是 (B, H_i x W_i x num_anchors x 4) 矩阵,i 表示第 i 层输出
        anchors = [F.reshape(ag(feat), shape=(1, -1))
                   for feat, ag in zip(features, self.anchor_generators)]
  1. 输入的 features 是 List, 里面的每个元素都是 (B, C, H_i, W_i) 的 feature cube
  2. ag(feat) 输出的是 (1, H_i x W_i x num_anchors, 4) 的 MXNet.NDArray
  3. 经过 F.reshape(ag(feat) 后得到的是 (1, H_i x W_i x num_anchors x 4) 的 MXNet.NDArray
  4. 最后得到的 anchors 是 List of MXNet.NDArray,里面的每个元素是 (1, H_i x W_i x num_anchors x 4) 的 MXNet.NDArray
        cls_preds = F.concat(*cls_preds, dim=1).reshape((0, -1, self.num_classes + 1))
  1. F.concat(*cls_preds, dim=1) 这一步得到 (B, H_1 x W_1 x num_anchors x (fg_cls + 1) + ... + H_6 x W_6 x num_anchors x (fg_cls + 1)) 的矩阵
  2. reshape((0, -1, self.num_classes + 1)) 得到的 (B, H_1 x W_1 x num_anchors + ... + H_6 x W_6 x num_anchors, fg_cls + 1)
  3. 因此 cls_preds(B, H_1 x W_1 x num_anchors + ... + H_6 x W_6 x num_anchors, fg_cls + 1) 的矩阵
        box_preds = F.concat(*box_preds, dim=1).reshape((0, -1, 4))
  1. F.concat(*box_preds, dim=1) 得到的是 (N, H_1 x W_1 x num_anchors x 4 + H_2 x W_2 x num_anchors x 4 + ... + H_6 x W_6 x num_anchors x 4) 的 MXNet.NDArray
  2. 经过 reshape((0, -1, 4)) 后得到的 box_preds(N, H_1 x W_1 x num_anchors + H_2 x W_2 x num_anchors + ... + H_6 x W_6 x num_anchors, 4) 的 MXNet.NDArray
        anchors = F.concat(*anchors, dim=1).reshape((1, -1, 4))
  1. F.concat(*anchors, dim=1) 得到的是 (1, H_1 x W_2 x num_anchors x 4 + ... + H_6 x W_6 x num_anchors x 4) 的 MXNet.NDArray
  2. 经过 reshape((1, -1, 4)) 后得到的 anchors(1, H_1 x W_2 x num_anchors + ... + H_6 x W_6 x num_anchors, 4) 的 MXNet.NDArray
        if autograd.is_training():
            return [cls_preds, box_preds, anchors]
  1. 因此,如果是训练状态返回的是一个 List of MXNet.NDArray,分别如下
  2. cls_preds 是一个 (B, H_1 x W_1 x num_anchors + ... + H_6 x W_6 x num_anchors, fg_cls + 1) 的 MXNet.NDArray
  3. box_preds 是一个 (B, H_1 x W_1 x num_anchors + ... + H_6 x W_6 x num_anchors, 4) 的 MXNet.NDArray
  4. anchors 是一个 (1, H_1 x W_2 x num_anchors + ... + H_6 x W_6 x num_anchors, 4) 的 MXNet.NDArray
        bboxes = self.bbox_decoder(box_preds, anchors)
  1. bbox_decoder 做的就是把 (cx, cy, w, h) 这样的编码变成 x_{min}, y_{min}, x_{max}, y_{max}
  2. 得到的 bboxes 仍然是 (N, H_1 x W_1 x num_anchors + ... + H_6 x W_6 x num_anchors, 4) 的 MXNet.NDArray,只不过编码变成了 x_{min}, y_{min}, x_{max}, y_{max}
        cls_ids, scores = self.cls_decoder(F.softmax(cls_preds, axis=-1))
  1. F.softmax(cls_preds, axis=-1) 是将 cls_preds 里面的 Prediction score(经过 softmax 之前的),经过 softmax 之后转换成 Prediction Probability,尺寸还是 (N, H_1 x W_1 x num_anchors + ... + H_6 x W_6 x num_anchors, self.num_classes + 1)
  2. 得到的 cls_ids 还是 (B, H_1 x W_1 x num_anchors + ... + H_6 x W_6 x num_anchors, fg_class) 的矩阵,只不过对应的 score value 如果小于 thresh,则是 -1,否则是 class id
  3. 得到的 scores 也是 (B, H_1 x W_1 x num_anchors + ... + H_6 x W_6 x num_anchors, fg_class) 的矩阵,只不过对应的 score value 如果小于 thresh,则是 0,否则是该类的 Probability
        results = []
        for i in range(self.num_classes):
            cls_id = cls_ids.slice_axis(axis=-1, begin=i, end=i+1)
            score = scores.slice_axis(axis=-1, begin=i, end=i+1)
            # per class results
            per_result = F.concat(*[cls_id, score, bboxes], dim=-1)
            results.append(per_result)
        result = F.concat(*results, dim=1)

对于循环内的每一个:

  1. cls_id 是 (B, H_1 x W_1 x num_anchors + ... + H_6 x W_6 x num_anchors, 1) 的矩阵
  2. score 是 (B, H_1 x W_1 x num_anchors + ... + H_6 x W_6 x num_anchors, 1) 的矩阵
  3. bboxes 是 (B, H_1 x W_1 x num_anchors + ... + H_6 x W_6 x num_anchors, 4) 的矩阵

因此:

  1. per_result 是 (B, H_1 x W_1 x num_anchors + ... + H_6 x W_6 x num_anchors, 6) 的矩阵,6 维中的第一个是 class id, 第二个是 Probability,3 - 6 是 bbox 的坐标
  2. result 是 (B, H_1 x W_1 x num_anchors x num_classes + ... + H_6 x W_6 x num_anchors x num_classes, 6) 的矩阵
        if self.nms_thresh > 0 and self.nms_thresh < 1:
            result = F.contrib.box_nms(
                result, overlap_thresh=self.nms_thresh, topk=self.nms_topk, valid_thresh=0.01,
                id_index=0, score_index=1, coord_start=2, force_suppress=False)
            if self.post_nms > 0:
                result = result.slice_axis(axis=1, begin=0, end=self.post_nms)
        ids = F.slice_axis(result, axis=2, begin=0, end=1)
        scores = F.slice_axis(result, axis=2, begin=1, end=2)
        bboxes = F.slice_axis(result, axis=2, begin=2, end=6)
        return ids, scores, bboxes            
  1. 经过 box_nmsslice_axis 之后,result 是 b x post_nms x 6 的矩阵
  2. 这里可以看到 返回的 ids, scores, bboxes 其实都是固定数目的,self.post_nms 是 100,所以会返回 100 个预测结果,但这里绝大部分的 Probability 都是 0.02、0.03 这样的,在 demo_ssd.py 中,因为会有一个 thresh 比如 0.5,只会画出 大于 thresh 的框,所以抑制了绝大部分 Background
  3. ids 是 b x post_nms x 1 的矩阵
  4. scores 是 b x post_nms x 1 的矩阵
  5. bboxes 是 b x post_nms x 4 的矩阵
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment