中文字幕av专区_日韩电影在线播放_精品国产精品久久一区免费式_av在线免费观看网站

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

Pytorch轉變Caffe再轉變om模型轉換流程是怎樣的

發布時間:2021-12-04 18:32:42 來源:億速云 閱讀:175 作者:柒染 欄目:大數據

這篇文章給大家介紹Pytorch轉變Caffe再轉變om模型轉換流程是怎樣的,內容非常詳細,感興趣的小伙伴們可以參考借鑒,希望對大家能有所幫助。

標準網絡

Baseline:PytorchToCaffe

主要功能代碼在:

PytorchToCaffe
+-- Caffe
|   +-- caffe.proto
|   +-- layer_param.py
+-- example
|   +-- resnet_pytorch_2_caffe.py
+-- pytorch_to_caffe.py

直接使用可以參考resnet_pytorch_2_caffe.py,如果網絡中的操作Baseline中都已經實現,則可以直接轉換到Caffe模型。

添加自定義操作

如果遇到沒有實現的操作,則要分為兩種情況來考慮。

Caffe中有對應操作

以arg_max為例分享一下添加操作的方式。

首先要查看Caffe中對應層的參數:caffe.proto為對應版本caffe層與參數的定義,可以看到ArgMax定義了out_max_val、top_k、axis三個參數:

message ArgMaxParameter {
  // If true produce pairs (argmax, maxval)
  optional bool out_max_val = 1 [default = false];
  optional uint32 top_k = 2 [default = 1];
  // The axis along which to maximise -- may be negative to index from the
  // end (e.g., -1 for the last axis).
  // By default ArgMaxLayer maximizes over the flattened trailing dimensions
  // for each index of the first / num dimension.
  optional int32 axis = 3;
}

與Caffe算子邊界中的參數是一致的。

layer_param.py構建了具體轉換時參數類的實例,實現了操作參數從Pytorch到Caffe的傳遞:

def argmax_param(self, out_max_val=None, top_k=None, dim=1):
    argmax_param = pb.ArgMaxParameter()
    if out_max_val is not None:
        argmax_param.out_max_val = out_max_val
    if top_k is not None:
        argmax_param.top_k = top_k
    if dim is not None:
        argmax_param.axis = dim
    self.param.argmax_param.CopyFrom(argmax_param)

pytorch_to_caffe.py中定義了Rp類,用來實現Pytorch操作到Caffe操作的變換:

class Rp(object):
    def __init__(self, raw, replace, **kwargs):
        self.obj = replace
        self.raw = raw

    def __call__(self, *args, **kwargs):
        if not NET_INITTED:
            return self.raw(*args, **kwargs)
        for stack in traceback.walk_stack(None):
            if 'self' in stack[0].f_locals:
                layer = stack[0].f_locals['self']
                if layer in layer_names:
                    log.pytorch_layer_name = layer_names[layer]
                    print('984', layer_names[layer])
                    break
        out = self.obj(self.raw, *args, **kwargs)
        return out

在添加操作時,要使用Rp類替換操作:

torch.argmax = Rp(torch.argmax, torch_argmax)

接下來,要具體實現該操作:

def torch_argmax(raw, input, dim=1):
    x = raw(input, dim=dim)
    layer_name = log.add_layer(name='argmax')
    top_blobs = log.add_blobs([x], name='argmax_blob'.format(type))
    layer = caffe_net.Layer_param(name=layer_name, type='ArgMax',
                                  bottom=[log.blobs(input)], top=top_blobs)
    layer.argmax_param(dim=dim)
    log.cnet.add_layer(layer)
    return x

即實現了argmax操作Pytorch到Caffe的轉換。

Caffe中無直接對應操作

如果要轉換的操作在Caffe中無直接對應的層實現,解決思路主要有兩個:

1)在Pytorch中將不支持的操作分解為支持的操作:

如nn.InstanceNorm2d,實例歸一化在轉換時是用BatchNorm做的,不支持 affine=True 或者track_running_stats=True,默認use_global_stats:false,但om轉換時use_global_stats必須為true,所以可以轉到Caffe,但再轉om不友好。

InstanceNorm是在featuremap的每個Channel上進行歸一化操作,因此,可以實現nn.InstanceNorm2d為:

class InstanceNormalization(nn.Module):
    def __init__(self, dim, eps=1e-5):
        super(InstanceNormalization, self).__init__()
        self.gamma = nn.Parameter(torch.FloatTensor(dim))
        self.beta = nn.Parameter(torch.FloatTensor(dim))
        self.eps = eps
        self._reset_parameters()

    def _reset_parameters(self):
        self.gamma.data.uniform_()
        self.beta.data.zero_()

    def __call__(self, x):
        n = x.size(2) * x.size(3)
        t = x.view(x.size(0), x.size(1), n)
        mean = torch.mean(t, 2).unsqueeze(2).unsqueeze(3).expand_as(x)
        var = torch.var(t, 2).unsqueeze(2).unsqueeze(3).expand_as(x)
        gamma_broadcast = self.gamma.unsqueeze(1).unsqueeze(1).unsqueeze(0).expand_as(x)
        beta_broadcast = self.beta.unsqueeze(1).unsqueeze(1).unsqueeze(0).expand_as(x)
        out = (x - mean) / torch.sqrt(var + self.eps)
        out = out * gamma_broadcast + beta_broadcast
        return out

但在驗證HiLens Caffe算子邊界中發現, om模型轉換不支持Channle維度之外的求和或求均值操作,為了規避這個操作,我們可以通過支持的算子重新實現nn.InstanceNorm2d:

class InstanceNormalization(nn.Module):
    def __init__(self, dim, eps=1e-5):
        super(InstanceNormalization, self).__init__()
        self.gamma = torch.FloatTensor(dim)
        self.beta = torch.FloatTensor(dim)
        self.eps = eps
        self.adavg = nn.AdaptiveAvgPool2d(1)

    def forward(self, x):
        n, c, h, w = x.shape
        mean = nn.Upsample(scale_factor=h)(self.adavg(x))
        var = nn.Upsample(scale_factor=h)(self.adavg((x - mean).pow(2)))
        gamma_broadcast = self.gamma.unsqueeze(1).unsqueeze(1).unsqueeze(0).expand_as(x)
        beta_broadcast = self.beta.unsqueeze(1).unsqueeze(1).unsqueeze(0).expand_as(x)
        out = (x - mean) / torch.sqrt(var + self.eps)
        out = out * gamma_broadcast + beta_broadcast
        return out

經過驗證,與原操作等價,可以轉為Caffe模型

2)在Caffe中通過利用現有操作實現:

在Pytorch轉Caffe的過程中發現,如果存在featuremap + 6這種涉及到常數的操作,轉換過程中會出現找不到blob的問題。我們首先查看pytorch_to_caffe.py中add操作的具體轉換方法:

def _add(input, *args):
    x = raw__add__(input, *args)
    if not NET_INITTED:
        return x
    layer_name = log.add_layer(name='add')
    top_blobs = log.add_blobs([x], name='add_blob')
    if log.blobs(args[0]) == None:
        log.add_blobs([args[0]], name='extra_blob')
    else:
        layer = caffe_net.Layer_param(name=layer_name, type='Eltwise',
                                      bottom=[log.blobs(input),log.blobs(args[0])], top=top_blobs)
        layer.param.eltwise_param.operation = 1 # sum is 1
        log.cnet.add_layer(layer)
    return x

可以看到對于blob不存在的情況進行了判斷,我們只需要在log.blobs(args[0]) == None條件下進行修改,一個自然的想法是利用Scale層實現add操作:

def _add(input, *args):
    x = raw__add__(input, *args)
    if not NET_INITTED:
        return x
    layer_name = log.add_layer(name='add')
    top_blobs = log.add_blobs([x], name='add_blob')
    if log.blobs(args[0]) == None:
        layer = caffe_net.Layer_param(name=layer_name, type='Scale',
                                       bottom=[log.blobs(input)], top=top_blobs)
        layer.param.scale_param.bias_term = True
        weight = torch.ones((input.shape[1]))
        bias = torch.tensor(args[0]).squeeze().expand_as(weight)
        layer.add_data(weight.cpu().data.numpy(), bias.cpu().data.numpy())
        log.cnet.add_layer(layer)
    else:
        layer = caffe_net.Layer_param(name=layer_name, type='Eltwise',
                                      bottom=[log.blobs(input), log.blobs(args[0])], top=top_blobs)
        layer.param.eltwise_param.operation = 1  # sum is 1
        log.cnet.add_layer(layer)
    return x

類似的,featuremap * 6這種簡單乘法也可以通過同樣的方法實現。

踩過的坑

  • Pooling:Pytorch默認 ceil_mode=false,Caffe默認 ceil_mode=true,可能會導致維度變化,如果出現尺寸不匹配的問題可以檢查一下Pooling參數是否正確。另外,雖然文檔上沒有看到,但是 kernel_size > 32 后模型雖然可以轉換,但推理會報錯,這時可以分兩層進行Pooling操作。

  • Upsample :om邊界算子中的Upsample 層scale_factor參數必須是int,不能是size。如果已有模型參數為size也會正常跑完Pytorch轉Caffe的流程,但此時Upsample參數是空的。參數為size的情況可以考慮轉為scale_factor或用Deconvolution來實現。

  • Transpose2d:Pytorch中 output_padding 參數會加在輸出的大小上,但Caffe不會,輸出特征圖相對會變小,此時反卷積之后的featuremap會變大一點,可以通過Crop層進行裁剪,使其大小與Pytorch對應層一致。另外,om中反卷積推理速度較慢,最好是不要使用,可以用Upsample+Convolution替代。

  • Pad:Pytorch中Pad操作很多樣,但Caffe中只能進行H與W維度上的對稱pad,如果Pytorch網絡中有h = F.pad(x, (1, 2, 1, 2), "constant", 0)這種不對稱的pad操作,解決思路為:

  1. 如果不對稱pad的層不存在后續的維度不匹配的問題,可以先判斷一下pad對結果的影響,一些任務受pad的影響很小,那么就不需要修改。

  2. 如果存在維度不匹配的問題,可以考慮按照較大的參數充分pad之后進行Crop,或是將前后兩個(0, 0, 1, 1)與(1, 1, 0, 0)的pad合為一個(1, 1, 1, 1),這要看具體的網絡結構確定。

  3. 如果是Channel維度上的pad如F.pad(x, (0, 0, 0, 0, 0, channel_pad), "constant", 0),可以考慮零卷積后cat到featuremap上:

zero = nn.Conv2d(in_channels, self.channel_pad, kernel_size=3, padding=1, bias=False)
nn.init.constant(self.zero.weight, 0)
pad_tensor = zero(x)
x = torch.cat([x, pad_tensor], dim=1)
  • 一些操作可以轉到Caffe,但om并不支持標準Caffe的所有操作,如果要再轉到om要對照文檔確認好邊界算子。


關于Pytorch轉變Caffe再轉變om模型轉換流程是怎樣的就分享到這里了,希望以上內容可以對大家有一定的幫助,可以學到更多知識。如果覺得文章不錯,可以把它分享出去讓更多的人看到。

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

德州市| 平顺县| 内江市| 吉隆县| 修水县| 宜春市| 和平区| 万山特区| 荔浦县| 绥化市| 海丰县| 绥中县| 道孚县| 庆安县| 东安县| 松潘县| 正蓝旗| 竹山县| 岳阳县| 蒙山县| 永寿县| 鄯善县| 神池县| 伊通| 虎林市| 交口县| 司法| 石景山区| 师宗县| 兴安县| 建湖县| 镇赉县| 辰溪县| 庆安县| 丹凤县| 陆川县| 顺义区| 新蔡县| 景谷| 遂溪县| 澜沧|