代碼閱讀-deformable DETR (一)

因?yàn)橄鄬?duì)transformer做一些改動(dòng)看看效果喷众,所以接下來這幾天先來看看deformable DETR的代碼實(shí)現(xiàn)。

先來看models的內(nèi)容:


models文件

文件position_encoding.py

"""
Various positional encodings for the transformer.
"""
import math
import torch
from torch import nn
from util.misc import NestedTensor

頭文件中只有一個(gè)NestedTensor需要解釋玫坛,其本身其實(shí)是一種對(duì)于多個(gè)tensor的集合的封裝,讓該集合的tensor同時(shí)變換包晰。定義如下:

class NestedTensor(object):
    def __init__(self, tensors, mask: Optional[Tensor]):
        self.tensors = tensors
        self.mask = mask

對(duì)位置進(jìn)行sine公式構(gòu)造編碼的類PositionEmbeddingSine

class PositionEmbeddingSine(nn.Module):
    """
    This is a more standard version of the position embedding, very similar to the one
    used by the Attention is all you need paper, generalized to work on images.
    """
    def __init__(self, num_pos_feats=64, temperature=10000, normalize=False, scale=None):
        super().__init__()
        self.num_pos_feats = num_pos_feats  #每一個(gè)點(diǎn)的編碼長(zhǎng)度
        self.temperature = temperature
        self.normalize = normalize
        if scale is not None and normalize is False:
            raise ValueError("normalize should be True if scale is passed")
        if scale is None:
            scale = 2 * math.pi
        self.scale = scale

    def forward(self, tensor_list: NestedTensor):
        x = tensor_list.tensors
        mask = tensor_list.mask
        assert mask is not None
        not_mask = ~mask
        y_embed = not_mask.cumsum(1, dtype=torch.float32)  # 列累加和湿镀,唯一的位置表示數(shù)字
        x_embed = not_mask.cumsum(2, dtype=torch.float32) # 行累加和
        if self.normalize:
            eps = 1e-6
            y_embed = (y_embed - 0.5) / (y_embed[:, -1:, :] + eps) * self.scale
            x_embed = (x_embed - 0.5) / (x_embed[:, :, -1:] + eps) * self.scale

        dim_t = torch.arange(self.num_pos_feats, dtype=torch.float32, device=x.device)  # 特征維度上的索引值
        dim_t = self.temperature ** (2 * (dim_t // 2) / self.num_pos_feats) 

        pos_x = x_embed[:, :, :, None] / dim_t
        pos_y = y_embed[:, :, :, None] / dim_t
        pos_x = torch.stack((pos_x[:, :, :, 0::2].sin(), pos_x[:, :, :, 1::2].cos()), dim=4).flatten(3)
        pos_y = torch.stack((pos_y[:, :, :, 0::2].sin(), pos_y[:, :, :, 1::2].cos()), dim=4).flatten(3)
        pos = torch.cat((pos_y, pos_x), dim=3).permute(0, 3, 1, 2)
        return pos

這部分來自于<Attention is all your need>中的公式:

position embedding

最終獲得每個(gè)位置獨(dú)一無二的長(zhǎng)度為num_pos_feats的位置編碼。

除此之外杜窄,還有可以學(xué)習(xí)的位置編碼

class PositionEmbeddingLearned(nn.Module):
    """
    Absolute pos embedding, learned.
    """
    def __init__(self, num_pos_feats=256):
        super().__init__()
        self.row_embed = nn.Embedding(50, num_pos_feats)
        self.col_embed = nn.Embedding(50, num_pos_feats)
        self.reset_parameters()

    def reset_parameters(self):
        nn.init.uniform_(self.row_embed.weight)
        nn.init.uniform_(self.col_embed.weight)

    def forward(self, tensor_list: NestedTensor):
        x = tensor_list.tensors
        h, w = x.shape[-2:]
        i = torch.arange(w, device=x.device)
        j = torch.arange(h, device=x.device)
        x_emb = self.col_embed(i)
        y_emb = self.row_embed(j)
        pos = torch.cat([
            x_emb.unsqueeze(0).repeat(h, 1, 1),
            y_emb.unsqueeze(1).repeat(1, w, 1),
        ], dim=-1).permute(2, 0, 1).unsqueeze(0).repeat(x.shape[0], 1, 1, 1)
        return pos

這個(gè)里面對(duì)于我來說可能nn.Embedding這層還是第一次見肠骆,這里其實(shí)表示產(chǎn)生50個(gè)長(zhǎng)度為num\_pos\_feats的可學(xué)習(xí)的編碼向量,這個(gè)50應(yīng)該是實(shí)際情況設(shè)置的塞耕,比如featmap的尺寸大小。然后這一層forward時(shí)是選擇對(duì)應(yīng)索引的編碼向量嘴瓤。比如x_emb=self.col_embed(i)就是選擇i對(duì)應(yīng)的索引所指向的編碼向量扫外。
兩個(gè)不同編碼方式同意的接口:

def build_position_encoding(args):
    N_steps = args.hidden_dim // 2
    if args.position_embedding in ('v2', 'sine'):
        # TODO find a better way of exposing other arguments
        position_embedding = PositionEmbeddingSine(N_steps, normalize=True)
    elif args.position_embedding in ('v3', 'learned'):
        position_embedding = PositionEmbeddingLearned(N_steps)
    else:
        raise ValueError(f"not supported {args.position_embedding}")
    return position_embedding

backbone.py文件里主要有一個(gè)torchvision.model._utils.IntermediateLayerGetter, 這個(gè)函數(shù)主要是從模型中設(shè)置輸出的層,代碼如下:

class IntermediateLayerGetter(nn.ModuleDict):
    """
    #Module封裝器廓脆,用以返回model的中間若干層輸出
    Module wrapper that returns intermediate layers from a model
    # 有一個(gè)強(qiáng)假設(shè)筛谚,即模塊在使用時(shí)與模型中注冊(cè)順序相同
    It has a strong assumption that the modules have been registered
    into the model in the same order as they are used.
    # 這表示forward中的module不能使用兩次
    This means that one should **not** reuse the same nn.Module
    twice in the forward if you want this to work.
    # 另外,只能query model中直接注冊(cè)的層停忿,而不能使用間接的層驾讲,比如resnet中層可能為layer1.1.conv1等,此時(shí)只能query layer1席赂, 而不能使用layer1.1
    Additionally, it is only able to query submodules that are directly
    assigned to the model. So if `model` is passed, `model.feature1` can
    be returned, but not `model.feature1.layer2`.

    Arguments:
        model (nn.Module): model on which we will extract the features
        # 模型:用于被選擇層的model吮铭,比如resnet
        return_layers (Dict[name, new_name]): a dict containing the names
            of the modules for which the activations will be returned as
            the key of the dict, and the value of the dict is the name
            of the returned activation (which the user can specify).
        # 字典,設(shè)定被選擇的層颅停,name是model的層名谓晌, new_name是重新命名的層
    
    def __init__(self, model, return_layers):
        if not set(return_layers).issubset([name for name, _ in model.named_children()]):
            raise ValueError("return_layers are not present in model")
 
        orig_return_layers = return_layers
        return_layers = {k: v for k, v in return_layers.items()}
        layers = OrderedDict()
        for name, module in model.named_children():
            layers[name] = module
            if name in return_layers:
                del return_layers[name]
            if not return_layers:
                break
 
        super(IntermediateLayerGetter, self).__init__(layers)
        self.return_layers = orig_return_layers
 
    def forward(self, x):
        out = OrderedDict()
        for name, module in self.named_children():
            x = module(x)
            if name in self.return_layers:
                out_name = self.return_layers[name]
                out[out_name] = x
        return out

舉個(gè)例子:

m = torchvision.models.resnet50(pretrained=True)
new_m = torchvision.models._utils.IntermediateLayerGetter(m,{'layer1': '1', 'layer2': '2'})
out = new_m(torch.rand(1, 3, 224, 224))
print([(k, v.shape) for k, v in out.items()])

可以發(fā)現(xiàn)重新命名了,且內(nèi)容從resnet50的layer1和layer2取出癞揉。

backbone的創(chuàng)建函數(shù):

def build_backbone(args):
    position_embedding = build_position_encoding(args)  # 位置編碼纸肉,每個(gè)位置對(duì)應(yīng)一個(gè)向量, NxdxHxW
    train_backbone = args.lr_backbone > 0  # 是否固定訓(xùn)練好的參數(shù)
    return_interm_layers = args.masks or (args.num_feature_levels > 1)  # 返回的中間層溺欧, True的話返回2,3柏肪,4層
    backbone = Backbone(args.backbone, train_backbone, return_interm_layers, args.dilation)
    model = Joiner(backbone, position_embedding)  # 返回每個(gè)中間層的輸出姐刁,和對(duì)應(yīng)的pos編碼
    return model

這兩個(gè)文件主要是定義了backbone的內(nèi)容,提供圖像CNN之后的特征以及對(duì)應(yīng)的位置編碼烦味,下面我們關(guān)注一下transformer的內(nèi)容聂使。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市拐叉,隨后出現(xiàn)的幾起案子岩遗,更是在濱河造成了極大的恐慌,老刑警劉巖凤瘦,帶你破解...
    沈念sama閱讀 217,542評(píng)論 6 504
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件宿礁,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡蔬芥,警方通過查閱死者的電腦和手機(jī)梆靖,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,822評(píng)論 3 394
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來笔诵,“玉大人返吻,你說我怎么就攤上這事『跣觯” “怎么了测僵?”我有些...
    開封第一講書人閱讀 163,912評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)谢翎。 經(jīng)常有香客問我捍靠,道長(zhǎng),這世上最難降的妖魔是什么森逮? 我笑而不...
    開封第一講書人閱讀 58,449評(píng)論 1 293
  • 正文 為了忘掉前任榨婆,我火速辦了婚禮,結(jié)果婚禮上褒侧,老公的妹妹穿的比我還像新娘良风。我一直安慰自己,他們只是感情好闷供,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,500評(píng)論 6 392
  • 文/花漫 我一把揭開白布烟央。 她就那樣靜靜地躺著,像睡著了一般这吻。 火紅的嫁衣襯著肌膚如雪吊档。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,370評(píng)論 1 302
  • 那天唾糯,我揣著相機(jī)與錄音怠硼,去河邊找鬼鬼贱。 笑死,一個(gè)胖子當(dāng)著我的面吹牛香璃,可吹牛的內(nèi)容都是我干的这难。 我是一名探鬼主播,決...
    沈念sama閱讀 40,193評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼葡秒,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼姻乓!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起眯牧,我...
    開封第一講書人閱讀 39,074評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤蹋岩,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后学少,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體剪个,經(jīng)...
    沈念sama閱讀 45,505評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,722評(píng)論 3 335
  • 正文 我和宋清朗相戀三年版确,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了扣囊。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,841評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡绒疗,死狀恐怖侵歇,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情吓蘑,我是刑警寧澤惕虑,帶...
    沈念sama閱讀 35,569評(píng)論 5 345
  • 正文 年R本政府宣布,位于F島的核電站磨镶,受9級(jí)特大地震影響枷遂,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜棋嘲,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,168評(píng)論 3 328
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望矩桂。 院中可真熱鬧沸移,春花似錦、人聲如沸侄榴。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,783評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽癞蚕。三九已至蕊爵,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間桦山,已是汗流浹背攒射。 一陣腳步聲響...
    開封第一講書人閱讀 32,918評(píng)論 1 269
  • 我被黑心中介騙來泰國(guó)打工醋旦, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人会放。 一個(gè)月前我還...
    沈念sama閱讀 47,962評(píng)論 2 370
  • 正文 我出身青樓饲齐,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親咧最。 傳聞我的和親對(duì)象是個(gè)殘疾皇子捂人,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,781評(píng)論 2 354

推薦閱讀更多精彩內(nèi)容