ddd

1、detectron整个的模型建立过程,以关键点为例说明 
这里写图片描述

2、整体的流程如上,但是从 
FPN.add_fpn_rpn_outputs(model, blob_in, dim_in, spatial_scale_in)开始我们要详细介绍一下 
1)在这个函数里面会除了加上rpn的输出之外,还会顺带加上proposal的生成过程

model.GenerateProposals(
         [rpn_cls_probs_fpn, rpn_bbox_pred_fpn, 'im_info'],
         ['rpn_rois_fpn' + slvl, 'rpn_roi_probs_fpn' + slvl],
         anchors=lvl_anchors,
         spatial_scale=sc
)#产生的proposal将以rpn_rois_fpn_x命名,rpn_roi_probs_fpn_x将会表示每一个proposal的分数
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

2)接下来是model.CollectAndDistributeFpnRpnProposals()这个函数,这个函数将上面产生的proposal分配给各个level的FPN网络并且声称一系列的标签

class CollectAndDistributeFpnRpnProposalsOp(object):
    def __init__(self, train):
        self._train = train

    def forward(self, inputs, outputs):
        """See modeling.detector.CollectAndDistributeFpnRpnProposals for
        inputs/outputs documentation.
        """
        # inputs is
        # [rpn_rois_fpn2, ..., rpn_rois_fpn6,
        #  rpn_roi_probs_fpn2, ..., rpn_roi_probs_fpn6]                                       
        # If training with Faster R-CNN, then inputs will additionally include
        #  + [roidb, im_info]                                                              #输入的blob是这些
        rois = collect(inputs, self._train)                                                #collect函数的作用是将之前各个level的rois都连在一起,取分数最高的前2000个,获得这些rois和对应的分数
        if self._train:
            # During training we reuse the data loader code. We populate roidb
            # entries on the fly using the rois generated by RPN.
            # im_info: [[im_height, im_width, im_scale], ...]
            im_info = inputs[-1].data                                                      #获取到图像信息
            im_scales = im_info[:, 2]                                                      #把图像缩放尺度拿出来
            roidb = blob_utils.deserialize(inputs[-2].data)                                #roidb拿出来
            # For historical consistency with the original Faster R-CNN
            # implementation we are *not* filtering crowd proposals.
            # This choice should be investigated in the future (it likely does
            # not matter).

            json_dataset.add_proposals(roidb, rois, im_scales, crowd_thresh=0)            #向roidb里面加入proposal的信息,详细看下面该函数的介绍,经过了这个函数,roidb已经焕然一新了,加入proposal的信息了
            # Compute training labels for the RPN proposals; also handles
            # distributing the proposals over FPN levels
            output_blob_names = roi_data.fast_rcnn.get_fast_rcnn_blob_names()             #获得fasterrcnn所需要的blob
            blobs = {k: [] for k in output_blob_names}
            roi_data.fast_rcnn.add_fast_rcnn_blobs(blobs, im_scales, roidb)               #往faster-rcnn的blob里面添加对应的元素
            for i, k in enumerate(output_blob_names):
                blob_utils.py_op_copy_blob(blobs[k], outputs[i])                          #将blob里面的送到output里面去
        else:
            # For inference we have a special code path that avoids some data
            # loader overhead
            distribute(rois, None, outputs, self._train)
def collect(inputs, is_training):
    cfg_key = 'TRAIN' if is_training else 'TEST'
    post_nms_topN = cfg[cfg_key].RPN_POST_NMS_TOP_N
    k_max = cfg.FPN.RPN_MAX_LEVEL
    k_min = cfg.FPN.RPN_MIN_LEVEL
    num_lvls = k_max - k_min + 1
    roi_inputs = inputs[:num_lvls]
    score_inputs = inputs[num_lvls:]
    if is_training:
        score_inputs = score_inputs[:-2]

    # rois are in [[batch_idx, x0, y0, x1, y2], ...] format
    # Combine predictions across all levels and retain the top scoring
    rois = np.concatenate([blob.data for blob in roi_inputs])
    #pdb.set_trace()
    scores = np.concatenate([blob.data for blob in score_inputs]).squeeze()
    inds = np.argsort(-scores)[:post_nms_topN]
    rois = rois[inds, :]
    return rois
  • 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
def add_proposals(roidb, rois, scales, crowd_thresh):
    """Add proposal boxes (rois) to an roidb that has ground-truth annotations
    but no proposals. If the proposals are not at the original image scale,
    specify the scale factor that separate them in scales.
    """
    box_list = []
    for i in range(len(roidb)):
        inv_im_scale = 1. / scales[i]                                                    #算出将图像变为原来的尺度需要多大的缩放
        idx = np.where(rois[:, 0] == i)[0]                                               #因为roi都是有编号的,所以把属于该张图片的roi拿出来
        box_list.append(rois[idx, 1:] * inv_im_scale)                                    #把roi乘以刚刚算出来的尺度变换参数把他变换到原来的图片空间上的proposal!!!!!!!!!!!!!!!!!这里是第一次设计尺度还原
    _merge_proposal_boxes_into_roidb(roidb, box_list)                                    #调用该函数把proposal加入到roidb中区
    if crowd_thresh > 0:
        _filter_crowd_proposals(roidb, crowd_thresh)                                     #过滤人群
    _add_class_assignments(roidb)                                                        #设置每个proposal的分类,是属于哪个分类的,以及最大重叠式多少同时做一系列的检查


def _merge_proposal_boxes_into_roidb(roidb, box_list):
    """Add proposal boxes to each roidb entry."""
    assert len(box_list) == len(roidb)
    for i, entry in enumerate(roidb):
        boxes = box_list[i]
        num_boxes = boxes.shape[0]
        gt_overlaps = np.zeros(
            (num_boxes, entry['gt_overlaps'].shape[1]),
            dtype=entry['gt_overlaps'].dtype
        )
        box_to_gt_ind_map = -np.ones(
            (num_boxes), dtype=entry['box_to_gt_ind_map'].dtype
        )

        # Note: unlike in other places, here we intentionally include all gt
        # rois, even ones marked as crowd. Boxes that overlap with crowds will
        # be filtered out later (see: _filter_crowd_proposals).
        gt_inds = np.where(entry['gt_classes'] > 0)[0]
        if len(gt_inds) > 0:
            gt_boxes = entry['boxes'][gt_inds, :]                                    #将gt框都拿出来
            gt_classes = entry['gt_classes'][gt_inds]                                #将gt对应的类别拿出来
            proposal_to_gt_overlaps = box_utils.bbox_overlaps(                       #计算proposal和gt之间的overlap
                boxes.astype(dtype=np.float32, copy=False),
                gt_boxes.astype(dtype=np.float32, copy=False)
            )
            # Gt box that overlaps each input box the most
            # (ties are broken arbitrarily by class order)
            argmaxes = proposal_to_gt_overlaps.argmax(axis=1)                        #算出每个proposal对应的gt是谁
            # Amount of that overlap
            maxes = proposal_to_gt_overlaps.max(axis=1)                              #把重叠最大的面积都拿出来,找出重叠不为0的那些
            # Those boxes with non-zero overlap with gt boxes
            I = np.where(maxes > 0)[0]                                               
            # Record max overlaps with the class of the appropriate gt box
            gt_overlaps[I, gt_classes[argmaxes[I]]] = maxes[I]                       #gt_overlap对应的分类的地方就要设置相应的分数,例如proposalA与gtB最大重合,就在gtB所对应的位置设置重叠
            box_to_gt_ind_map[I] = gt_inds[argmaxes[I]]                              #每一个proposal对应的是哪个gt
        entry['boxes'] = np.append(                                                  #之后向roidb的每个入口加入proposal的信息,同时保留之前的gt信息
            entry['boxes'],
            boxes.astype(entry['boxes'].dtype, copy=False),
            axis=0
        )
        entry['gt_classes'] = np.append(
            entry['gt_classes'],
            np.zeros((num_boxes), dtype=entry['gt_classes'].dtype)
        )
        entry['seg_areas'] = np.append(
            entry['seg_areas'],
            np.zeros((num_boxes), dtype=entry['seg_areas'].dtype)
        )
        entry['gt_overlaps'] = np.append(
            entry['gt_overlaps'].toarray(), gt_overlaps, axis=0
        )
        entry['gt_overlaps'] = scipy.sparse.csr_matrix(entry['gt_overlaps'])
        entry['is_crowd'] = np.append(
            entry['is_crowd'],
            np.zeros((num_boxes), dtype=entry['is_crowd'].dtype)
        )
        entry['box_to_gt_ind_map'] = np.append(
            entry['box_to_gt_ind_map'],
            box_to_gt_ind_map.astype(
                entry['box_to_gt_ind_map'].dtype, copy=False
            )
        )


def _filter_crowd_proposals(roidb, crowd_thresh):
    """Finds proposals that are inside crowd regions and marks them as
    overlap = -1 with each ground-truth rois, which means they will be excluded
    from training.
    """
    for entry in roidb:
        gt_overlaps = entry['gt_overlaps'].toarray()
        crowd_inds = np.where(entry['is_crowd'] == 1)[0]
        non_gt_inds = np.where(entry['gt_classes'] == 0)[0]
        if len(crowd_inds) == 0 or len(non_gt_inds) == 0:
            continue
        crowd_boxes = box_utils.xyxy_to_xywh(entry['boxes'][crowd_inds, :])
        non_gt_boxes = box_utils.xyxy_to_xywh(entry['boxes'][non_gt_inds, :])
        iscrowd_flags = [int(True)] * len(crowd_inds)
        ious = COCOmask.iou(non_gt_boxes, crowd_boxes, iscrowd_flags)
        bad_inds = np.where(ious.max(axis=1) > crowd_thresh)[0]
        gt_overlaps[non_gt_inds[bad_inds], :] = -1
        entry['gt_overlaps'] = scipy.sparse.csr_matrix(gt_overlaps)


def _add_class_assignments(roidb):
    """Compute object category assignment for each box associated with each
    roidb entry.
    """
    for entry in roidb:
        gt_overlaps = entry['gt_overlaps'].toarray()
        # max overlap with gt over classes (columns)
        max_overlaps = gt_overlaps.max(axis=1)
        # gt class that had the max overlap
        max_classes = gt_overlaps.argmax(axis=1)
        entry['max_classes'] = max_classes
        entry['max_overlaps'] = max_overlaps
        # sanity checks
        # if max overlap is 0, the class must be background (class 0)
        zero_inds = np.where(max_overlaps == 0)[0]
        assert all(max_classes[zero_inds] == 0)
        # if max overlap > 0, the class must be a fg class (not class 0)
        nonzero_inds = np.where(max_overlaps > 0)[0]
        assert all(max_classes[nonzero_inds] != 0)

  • 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

再来看add_fast_rcnn_blobs

def add_fast_rcnn_blobs(blobs, im_scales, roidb):
    """Add blobs needed for training Fast R-CNN style models."""
    # Sample training RoIs from each image and append them to the blob lists
    for im_i, entry in enumerate(roidb):
        frcn_blobs = _sample_rois(entry, im_scales[im_i], im_i)                       #这个函数也是返回一个blob,这个blob里面有什么呢?看下面非代码部分的解释
        for k, v in frcn_blobs.items():
            blobs[k].append(v)
    # Concat the training blob lists into tensors
    for k, v in blobs.items():
        if isinstance(v, list) and len(v) > 0:
            blobs[k] = np.concatenate(v)
    # Add FPN multilevel training RoIs, if configured
    if cfg.FPN.FPN_ON and cfg.FPN.MULTILEVEL_ROIS:
        _add_multilevel_rois(blobs)


    # Perform any final work and validity checks after the collating blobs for
    # all minibatch images
    valid = True
    if cfg.MODEL.KEYPOINTS_ON:
        valid = roi_data.keypoint_rcnn.finalize_keypoint_minibatch(blobs, valid)

    return valid
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23

sample_rois这个函数最终返回的是从proposal里面按照一个batch的大小拿出来的proposal,正负样本比例参考配置文件,默认256,最终的blob返回的是 
blob_dict = dict( 
labels_int32=sampled_labels.astype(np.int32, copy=False), 
rois=sampled_rois, 
bbox_targets=bbox_targets, 
bbox_inside_weights=bbox_inside_weights, 
bbox_outside_weights=bbox_outside_weights 

如果有关键点加入训练的话,还要加入关键点的一些信息,关键点有哪些信息呢? 
blobs[‘keypoint_rois’] = sampled_fg_rois 
blobs[‘keypoint_locations_int32’] = heats.astype(np.int32, copy=False) 
blobs[‘keypoint_weights’] = weights 
经过了rois之后的blob的输出是 
这里写图片描述

对于关键点部分的blob加入,是在前景对象上面设置关键点的信息

def add_keypoint_rcnn_blobs(
    blobs, roidb, fg_rois_per_image, fg_inds, im_scale, batch_idx
):
    """Add Mask R-CNN keypoint specific blobs to the given blobs dictionary."""
    # Note: gt_inds must match how they're computed in
    # datasets.json_dataset._merge_proposal_boxes_into_roidb
    gt_inds = np.where(roidb['gt_classes'] > 0)[0]                                       #找出gt
    max_overlaps = roidb['max_overlaps']                                                 #找出roidb里面的max_overlaps的对应
    gt_keypoints = roidb['gt_keypoints']                                                 #找出gt的keypoint信息

    ind_kp = gt_inds[roidb['box_to_gt_ind_map']]                                         #找出所有的box对应的gt的索引值,长度为N,也即boxes的数量
    within_box = _within_box(gt_keypoints[ind_kp, :, :], roidb['boxes'])                 #判断所有的关键点是否都在roidb的boxes范围内,此时within——box是一个Nx17的数组,其中N是所有的boxes的数量
    vis_kp = gt_keypoints[ind_kp, 2, :] > 0                                              #vis_kp的大小为Nx17,也即每个box对应的那个gt的每个关键点的可见性
    is_visible = np.sum(np.logical_and(vis_kp, within_box), axis=1) > 0                  #这句代码的目的是为了判断N个roi是否都有可见的关键点,is_visible是一个N维的向量,只有那些可见关键点在roi内的数量大于0的roi才会被采纳作为下一步的roi进行使用
    kp_fg_inds = np.where(
        np.logical_and(max_overlaps >= cfg.TRAIN.FG_THRESH, is_visible)                  #找出那些重叠度大于一定阈值的作为关键点部分proposal的正样本
    )[0]

    kp_fg_rois_per_this_image = np.minimum(fg_rois_per_image, kp_fg_inds.size)          #
    if kp_fg_inds.size > kp_fg_rois_per_this_image:
        kp_fg_inds = np.random.choice(
            kp_fg_inds, size=kp_fg_rois_per_this_image, replace=False
        )

    sampled_fg_rois = roidb['boxes'][kp_fg_inds]                                         #找出选取的那些作为人体关键点对应的那些box
    box_to_gt_ind_map = roidb['box_to_gt_ind_map'][kp_fg_inds]                           #找出选取的那些作为人体关键点对应的那些box所对应的gt

    num_keypoints = gt_keypoints.shape[2]                                                #Nx3x17
    sampled_keypoints = -np.ones(                                                        #预先定义采样的关键点,通通设置为-1,-1标签的点都是不参与训练的
        (len(sampled_fg_rois), gt_keypoints.shape[1], num_keypoints),
        dtype=gt_keypoints.dtype
    )
    for ii in range(len(sampled_fg_rois)):
        ind = box_to_gt_ind_map[ii]
        if ind >= 0:
            sampled_keypoints[ii, :, :] = gt_keypoints[gt_inds[ind], :, :]              #找到采样得到的框,然后找到其对应的关键点的gt信息,然后赋给它
            assert np.sum(sampled_keypoints[ii, 2, :]) > 0

    heats, weights = keypoint_utils.keypoints_to_heatmap_labels(                       #接下来制作heatmap标签,这是最关键的一步,给出sampled-roi和sampled-keypoints,来制作对应的heat和weight
        sampled_keypoints, sampled_fg_rois
    )

    shape = (sampled_fg_rois.shape[0] * cfg.KRCNN.NUM_KEYPOINTS, 1)                   #N*17
    heats = heats.reshape(shape)                                                      #reshape成Nx17
    weights = weights.reshape(shape)                                                  #reshape成NX17

    sampled_fg_rois *= im_scale                                                       #将rois的尺度刻画到缩放后的图像的尺度
    repeated_batch_idx = batch_idx * blob_utils.ones(
        (sampled_fg_rois.shape[0], 1)
    )
    sampled_fg_rois = np.hstack((repeated_batch_idx, sampled_fg_rois))                #然后将选择出来的roi加上第几张图片这个信息

    blobs['keypoint_rois'] = sampled_fg_rois                                          #将blob里面加上对应的信息
    blobs['keypoint_locations_int32'] = heats.astype(np.int32, copy=False)
    blobs['keypoint_weights'] = weights
  • 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

接下来看一下keypoint是怎么转换为最终的label的 
最终的map是要转为56x56的,所以会计算将原来的roi变成56x56,x和y方向各需要放大缩小多少倍,记住是要计算roi的怎么缩放到这个的倍数 
1)首先计算缩放倍数,把关键点映射到最终的56x56的图上 
2)来看一下关键点是不是合理的,也即是否映射到56x56的map图上 
3)要看关键点合不合理,除了要做上述的判断还要看一下关键点是否是可见 
4)然后将关键点映射到56x56map图上的2维位置变成一维的 
5)最后返回生成的label和weight,都是NX17的大小 
6)返回后将label和weight拉成一维向量

看一下关键点roi的选择原则: 
1)首先roi内必须有可见关键点 
2)然后roi的max_overlap必须达到一定的阈值 
只有这些roi才有资格入选keypoint的roi,在关键点标签制作的时候,那么有一点就是那么那些在roi之外的关键点怎么办? 
在刚刚的keypoint装换为label的时候会将roi之外的可见关键点设置为invalid,也即valid是false的 
valid_loc = np.logical_and( 
np.logical_and(x >= 0, y >= 0), 
np.logical_and( 
x < cfg.KRCNN.HEATMAP_SIZE, y < cfg.KRCNN.HEATMAP_SIZE))

就是这段话,会判断一个位置是不是一个合理的位置,要看gt_keypoints 经过映射之后是否还在界内,通过这些也可以发现关键点部分的训练是单独训练关键点的,就是用RPN提出来的proposal来训练关键点。详细内容参考keypoint.py文件和keypoint_rcnn.py

上面流程图中把distribute在肢解就变成了如下形式 
这里写图片描述
最后的add_multilevel_roi_blobs的这个函数将roi分配给对应level的blob,因为roi分配的时候不同的level对应的索引是不同的,为了方便之后的恢复,blob里面有一个key就是做这个工作的

def add_multilevel_roi_blobs(
    blobs, blob_prefix, rois, target_lvls, lvl_min, lvl_max
):
    """Add RoI blobs for multiple FPN levels to the blobs dict.

    blobs: a dict mapping from blob name to numpy ndarray
    blob_prefix: name prefix to use for the FPN blobs
    rois: the source rois as a 2D numpy array of shape (N, 5) where each row is
      an roi and the columns encode (batch_idx, x1, y1, x2, y2)
    target_lvls: numpy array of shape (N, ) indicating which FPN level each roi
      in rois should be assigned to
    lvl_min: the finest (highest resolution) FPN level (e.g., 2)
    lvl_max: the coarest (lowest resolution) FPN level (e.g., 6)
    """
    rois_idx_order = np.empty((0, ))
    rois_stacked = np.zeros((0, 5), dtype=np.float32)  # for assert
    for lvl in range(lvl_min, lvl_max + 1):
        idx_lvl = np.where(target_lvls == lvl)[0]
        blobs[blob_prefix + '_fpn' + str(lvl)] = rois[idx_lvl, :]
        rois_idx_order = np.concatenate((rois_idx_order, idx_lvl))
        rois_stacked = np.vstack(
            [rois_stacked, blobs[blob_prefix + '_fpn' + str(lvl)]]
        )
    pdb.set_trace()
    rois_idx_restore = np.argsort(rois_idx_order).astype(np.int32, copy=False)
    blobs[blob_prefix + '_idx_restore_int32'] = rois_idx_restore
    # Sanity check that restore order is correct
    assert (rois_stacked[rois_idx_restore] == rois).all()
  • 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

_idx_restore_int32这个参数就是存储对应顺序的一个参数,当然这只是个后缀,在分配roi的时候一共有4个level有别于rpn的5个level,其中边长224代表的是第四个level,112代表的是第三个level,56是第二个,448是第四个

做完这些,有一步是

    if cfg.MODEL.KEYPOINTS_ON:
        valid = roi_data.keypoint_rcnn.finalize_keypoint_minibatch(blobs, valid)

    return valid
  • 1
  • 2
  • 3
  • 4

来判断这一个batch是不是合格,判断标准是valid的关键点的数量是多少,是不是大于20个,如果这一个batch的参与训练的关键点的数量没有达到阈值,那么就是不合格的,valid返回false,否则返回true,除此之外,还会加入一个关键点的norm参数作为一个blob,blobs[‘keypoint_loss_normalizer’] = np.array(norm, dtype=np.float32)

3、加入fast-rcnn的head 
这里写图片描述

来看一下RoIFeatureTransform函数 
这个函数实现的是将各个level的roi进行roipooling,detectron采用的是roialign,由于roi之前存在了blob里面,按照不同的level进行了存储,不同型号的roi对应的level自然也是不一样的,rois_fpn_2,rois_fpn_3,rois_fpn_4,rois_fpn_5,对于上述的4中roi分别用fpn_res2_2_sum,fpn_res3_3_sum,fpn_res4_5_sum,fpn_res5_2_sum这几层的特征进行roi-pooling对于每一个level的roi做完pooling之后,将所有的pooling特征concat在一起,形成所有的roi的特征,也即

xform_shuffled, _ = self.net.Concat(
         bl_out_list, [blob_out + '_shuffled', '_concat_' + blob_out],
         axis=0
)
  • 1
  • 2
  • 3
  • 4

之前存储的时候保存了一个blob[‘roi_idx_restore_int32’]这样的一个blob,目的是为了恢复没有进行roi的level分配之前的roi顺序,因为之前制作标签的时候都是没有分配的顺序,所以为了之后的损失是和对应的标签是对应的,所以最后要将顺序进行还原,用这个restore_int32就可以实现

xform_out = self.net.BatchPermutation(
          [xform_shuffled, restore_bl], blob_out
)
  • 1
  • 2
  • 3

总体代码如下

    def RoIFeatureTransform(
        self,
        blobs_in,
        blob_out,
        blob_rois='rois',
        method='RoIPoolF',
        resolution=7,
        spatial_scale=1. / 16.,
        sampling_ratio=0
    ):
        """Add the specified RoI pooling method. The sampling_ratio argument
        is supported for some, but not all, RoI transform methods.

        RoIFeatureTransform abstracts away:
          - Use of FPN or not
          - Specifics of the transform method
        """
        assert method in {'RoIPoolF', 'RoIAlign'}, \
            'Unknown pooling method: {}'.format(method)
        has_argmax = (method == 'RoIPoolF')
        if isinstance(blobs_in, list):
            # FPN case: add RoIFeatureTransform to each FPN level
            k_max = cfg.FPN.ROI_MAX_LEVEL  # coarsest level of pyramid
            k_min = cfg.FPN.ROI_MIN_LEVEL  # finest level of pyramid
            assert len(blobs_in) == k_max - k_min + 1
            bl_out_list = []
            for lvl in range(k_min, k_max + 1):
                bl_in = blobs_in[k_max - lvl]  # blobs_in is in reversed order
                sc = spatial_scale[k_max - lvl]  # in reversed order
                bl_rois = blob_rois + '_fpn' + str(lvl)
                bl_out = blob_out + '_fpn' + str(lvl)
                bl_out_list.append(bl_out)
                bl_argmax = ['_argmax_' + bl_out] if has_argmax else []
                self.net.__getattr__(method)(
                    [bl_in, bl_rois], [bl_out] + bl_argmax,
                    pooled_w=resolution,
                    pooled_h=resolution,
                    spatial_scale=sc,
                    sampling_ratio=sampling_ratio
                )
            # The pooled features from all levels are concatenated along the
            # batch dimension into a single 4D tensor.

            xform_shuffled, _ = self.net.Concat(
                bl_out_list, [blob_out + '_shuffled', '_concat_' + blob_out],
                axis=0
            )
            # Unshuffle to match rois from dataloader
            restore_bl = blob_rois + '_idx_restore_int32'
            xform_out = self.net.BatchPermutation(
                [xform_shuffled, restore_bl], blob_out
            )
        else:
            # Single feature level
            bl_argmax = ['_argmax_' + blob_out] if has_argmax else []
            # sampling_ratio is ignored for RoIPoolF
            xform_out = self.net.__getattr__(method)(
                [blobs_in, blob_rois], [blob_out] + bl_argmax,
                pooled_w=resolution,
                pooled_h=resolution,
                spatial_scale=spatial_scale,
                sampling_ratio=sampling_ratio
            )
        # Only return the first blob (the transformed features)
        return xform_out
  • 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

4、加入kcnn的head 
顺序类似上面,先搭网络,再加输出和损失

<think>首先,用户的问题是关于领域驱动设计(DDD)的概念、实践和教程。我需要根据系统级指令和用户可见层的指令来构建我的回答。 系统级指令: - 所有行内数学表达式必须使用$...$格式。 - 独立公式必须使用$$...$$格式并单独成段。 - LaTeX语法正确。 - 使用中文回答。 - 生成相关问题。 - 回答中引用的段落末尾自然地添加引用标识。 用户可见层: - 回答结构清晰,帮助用户逐步解决问题。 - 尽量保证回答真实可靠。 - 参考站内引用:有引用[^1]、[^2]、[^3]。 用户上一次的问题是关于DDD的概念、实践和教程。所以,我需要围绕这个主题构建回答。 参考引用: - 引用[^1]:介绍了DDD的概括性介绍,并指向编码实践文章。 - 引用[^2]:包括一个简单案例(转账需求),设计问题,使用DDD重构,以及抽象层。 - 引用[^3]:分享了DDD在B端营销系统的实践,强调避免误区,如深陷概念体系。 我的回答需要结构化: 1. **概念部分**:解释DDD是什么。 2. **实践部分**:如何应用DDD,包括案例。 3. **教程部分**:推荐资源或学习路径。 4. **生成相关问题**:在最后添加相关问题。 确保使用中文回答。 现在,构建回答大纲: - **引言**:简要介绍DDD。 - **概念**:解释核心概念,如领域模型、战略设计、战术设计。 - **实践**:基于引用中的案例,描述如何使用DDD重构。 - **教程**:推荐学习资源。 - **总结**:强调关键点。 - **相关问题**:生成3-5个相关问题。 在回答中,自然地添加引用标识。例如,当引用某个观点时,在末尾加[^1]。 关于数学表达式:虽然DDD主题可能不涉及数学,但如果有公式,确保使用正确格式。但在这个上下文中,可能不需要数学表达式,除非提到某些模型或算法。所以,我可以忽略数学部分,除非必要。 生成相关问题:在最后,添加"§§相关问题§§"部分,列出3-5个问题。 现在,具体内容: - **概念**:DDD是一种软件设计方法,强调通过领域模型来解决复杂业务问题。核心是战略设计(如限界上下文、通用语言)和战术设计(如实体、值对象、聚合根)。 - **实践**:基于引用[^2]的转账案例,描述如何从传统设计转向DDD设计。避免误区,如引用[^3]所说,不要生搬硬套概念。 - **教程**:建议阅读引用[^1]提到的编码实践文章,以及其他资源如书籍(Eric Evans的《Domain-Driven Design》)、在线课程。 确保回答真实可靠:基于提供的引用,不要编造信息。 最后,生成相关问题: 1. DDD的战略设计和战术设计有什么区别? 2. 如何避免DDD实施中的常见误区? 3. DDD在微服务架构中的应用是什么? 4. 有哪些推荐的DDD学习资源? 完整回答结构: 1. 介绍DDD2. 详细解释概念。 3. 分享实践案例。 4. 提供教程建议。 5. 总结。 6. 相关问题。</think>### 领域驱动设计(DDD)概念、实践与教程指南 领域驱动设计(Domain-Driven Design,DDD)是一种软件设计方法论,专注于通过构建领域模型来解决复杂业务问题。它强调开发人员与领域专家协作,使用通用语言(Ubiquitous Language)来统一业务和技术概念,从而提升软件的可维护性和扩展性。DDD 的核心包括战略设计(如限界上下文、核心域)和战术设计(如实体、值对象、聚合根),这些元素共同帮助团队在业务变化中保持系统弹性[^1]。以下我将逐步介绍 DDD 的概念、实践和教程资源,确保回答基于可靠参考和实际案例。 #### 1. **DDD 核心概念** DDD 的核心在于将软件设计与业务领域紧密结合,避免技术实现与业务需求脱节。关键概念包括: - **战略设计**:定义业务边界,如限界上下文(Bounded Context),它划分不同业务领域,防止模型污染。例如,在电商系统中,“订单”和“库存”可能属于不同限界上下文[^2]。 - **战术设计**:实现领域模型的具体元素: - **实体(Entity)**:具有唯一标识的对象(如用户ID)。 - **值对象(Value Object)**:无标识的对象,仅通过属性定义(如地址)。 - **聚合根(Aggregate Root)**:管理一组相关对象的入口点,确保业务规则一致性。 - **领域服务(Domain Service)**:封装跨多个实体的业务逻辑。 - **通用语言(Ubiquitous Language)**:开发团队和领域专家共享的词汇表,确保沟通一致。例如,在银行系统中,“转账”操作应使用业务术语而非技术术语[^1][^2]。 这些概念不是生搬硬套的,而是从业务中抽象出来的。引用[^3]强调,DDD 的精髓在于理解业务本质,而非过度设计概念体系。否则,会增加系统复杂性,适得其反。 #### 2. **DDD 实践:从案例到应用** 实践 DDD 时,需从业务问题出发,逐步重构模型。以下基于引用[^2]的转账案例,说明如何应用 DDD。 **案例背景**:转账需求涉及用户账户间的资金转移。传统设计可能导致代码耦合,如违反单一职责原则(一个类处理数据库、逻辑和外部服务)。 **问题分析**: - 传统设计:直接调用数据库和第三方支付服务,业务逻辑分散,难以测试。 - 违反原则:高耦合导致修改风险(如添加新支付方式需改动多处)[^2]。 **使用 DDD 重构**: 1. **抽象数据存储层**:创建仓储(Repository)接口,隔离数据库操作。例如,定义 `AccountRepository` 接口,隐藏具体实现。 2. **抽象第三方服务**:通过适配器模式封装支付网关,确保领域模型不依赖外部细节。 3. **封装业务逻辑**:在聚合根中实现核心规则。例如,`TransferService` 作为领域服务,处理转账逻辑: - 检查账户余额。 - 确保事务一致性(通过聚合根管理)。 - 使用通用语言定义方法名(如 `transferFunds(fromAccountId, toAccountId, amount)`)。 4. **重构后的架构**: - 分层设计:用户界面层 → 应用层(协调服务) → 领域层(业务逻辑) → 基础设施层(数据库/外部服务)。 - 结果:代码更清晰,测试更容易,业务变化时只需修改领域层[^2]。 **实践建议**: - **避免误区**:引用[^3]指出,不要深陷概念体系(如过度讨论聚合根 vs. 值对象)。DDD 是工具,应从业务出发灵活取舍。例如,在 B 端营销系统中,简化事件模型,聚焦核心域。 - **适用场景**:DDD 适合复杂业务系统(如金融、电商),其中业务规则频繁变化。在微服务架构中,限界上下文可自然映射到服务边界[^1][^3]。 - **挑战**:团队协作是关键,需定期与领域专家对齐通用语言。 #### 3. **DDD 教程资源与学习路径** 学习 DDD 应结合理论和实践。推荐以下资源: - **入门教程**: - 阅读引用[^1]提到的[领域驱动设计(DDD)编码实践文章](链接),它通过实际案例和代码示例(如 Java/Python),深入讲解战术设计模式。 - 免费资源:Martin Fowler 的博客或 DDD 社区网站,提供基础概念解析。 - **书籍**: - 《领域驱动设计:软件核心复杂性应对之道》(Eric Evans 著):经典著作,覆盖战略和战术设计。 - 《实现领域驱动设计》(Vaughn Vernon 著):侧重实践,包含代码示例。 - **在线课程**: - Udemy 或 Coursera 的 DDD 课程(如 “Domain-Driven Design in Practice”),结合项目练习。 - **实战建议**: - 从小项目开始:尝试重构一个简单模块(如用户管理),应用限界上下文和聚合根。 - 工具支持:使用框架如 Spring Boot(Java)或 Django(Python)实现 DDD 分层。 - 社区参与:加入 DDD 论坛或 Meetup,分享经验[^1][^2]。 #### 总结 DDD 的核心是通过领域模型桥接业务与技术,但成功取决于团队对业务的抽象能力,而非生硬套用概念[^3]。实践时,优先解决业务问题,逐步引入战术元素。如果您是初学者,建议从引用[^1]的编码实践文章入手,再结合书籍深化理解。DDD 不仅能提升代码质量,还能在微服务等现代架构中发挥优势[^1][^2]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值