离线推理项目迁移至mindspore(三)——代码迁移

离线推理项目迁移至mindspore(三)——代码迁移

友情链接:

上一篇:离线推理项目迁移至mindspore(二)——模型加载

5 代码迁移

在上一篇文章中,模型迁移的部分已经完成,当然,整个迁移工程并没有结束

本篇文章主要进行代码部分的迁移

5.1 x2mindspore

在mindspore中,有一个工具叫做x2mindspore,它可以将torch的代码转为mindspore的代码

不过,不推荐使用x2mindspore进行代码迁移,主要有以下原因:

  • x2mindspore给出的等效方法性能可能并不是最佳的等效方法
  • 有些时候,使用x2mindspore反而会在项目中引入更多的错误
  • 即使未出现错误,也会出现与原模型得到的结果相差甚大
  • x2mindspore并不是对所有方法都有效的,部分方法依然需要手动调整

因此,依靠x2mindspore可以转换,但效果不好,所以推荐熟悉mindspore文档,自行书写

5.2 代码迁移总述

既然不能直接使用x2mindspore脚本进行迁移,那么就需要进行等效代码的书写与替换

这里以mindspore1.10为例,来进行代码迁移

对于代码迁移部分,可以参考下面两篇文档:

  1. mindspore1.10文档对应
  2. mindspore 1.10与torch对应

Tips:2中的文档部分对应的函数并不是最佳方案,仅仅作为参考使用

5.3 等效代码替换

5.3.1 总述

对于torch中的功能,大多数在mindspore上有等效的代码,这时候只需要将代码进行等效就可以了

一般而言,代码的等效有一个规律:

  • torch.nn<==>mindspore.nn
  • torch.nn.funcational<==>mindspore.ops
  • mindspore文档中,如果给出的等效方法是大写字母的类(如ops.MatMul),一般都有小写字母的方法对应(如ops.matmul),只不过在文档中没有列出
  • mindspore2.x版本中,大多数方法的名称与torch中等效方法名称一致
5.3.2 常用的代码等效表格
torchmindspore 1.10.0
torch.unsqueeze(input, dim)
torch.Tensor.unsqueeze(dim)
mindspore.ops.expand_dims(input_x, axis)
torch.nn.functional.normalize(input, p=2, dim=1, eps=1e-12, out=None)mindspore.ops.L2Normalize(axis=0, epsilon=1e-4)(input_x)$ ^{[1]}$
torch.Tensor.transpose(dim0, dim1)mindspore.Tensor.transpose(*axes) [ 2 ] ^{[2]} [2]
torch.mm(input, mat2, out=None)mindspore.ops.matmul(x1, x2)
torch.from_numpy(ndarray)mindspore.Tensor.from_numpy(array)
torch.stack(tensors, dim=0, out=None)mindspore.ops.stack(input_x, axis=0)
torch.topk(input, k, dim=None, largest=True, sorted=True, out=None)mindspore.ops.top_k(input_x, k, sorted=True) [ 3 ] ^{[3]} [3]
torch.Tensor.expand(*sizes)mindspore.ops.broadcast_to(x, shape)
torch.sum(input, dtype=None)
torch.sum(input, dim, keepdim=False, dtype=None)
mindspore.ops.reduce_sum(x, axis)
torch.Tensor.sum(dim=None, keepdim=False, dtype=None)mindspore.Tensor.sum(axis=None, dtype=None, keepdims=False, initial=None)
torch.clamp(input, min, max, out=None)mindspore.ops.clip_by_value(x, clip_value_min=None, clip_value_max=None) [ 4 ] ^{[4]} [4]
torch.Tensor.size()mindspore.Tensor.shape
torch.nn.Parameter(data=None, requires_grad=True)mindspore.Parameter(default_input, name=None, requires_grad=True, layerwise_parallel=False, parallel_optimizer=True)
torch.randn(*size, out=None, dtype=None, layout=torch.strided, device=None, requires_grad=False)mindspore.ops.standard_normal(shape, seed=0, seed2=0)
torch.cat(tensors, dim=0, out=None)mindspore.ops.concat(input_x, axis=0)
torch.ones_like(input, dtype=None, layout=None, device=None, requires_grad=False, memory_format=torch.preserve_format)mindspore.ops.ones_like(input_x)
torch.einsum(equation, *operands)mindspore.ops.Einsum(equation)(input_x) [ 5 ] ^{[5]} [5]
torch.nn.functional.softmax(input, dim=None, _stacklevel=3, dtype=None)mindspore.ops.softmax(x, axis=- 1)
torch.argmax(input, dim, keepdim=False)mindspore.ops.argmax(x, axis=-1, output_type=mstype.int32)

注:

[1] torch中,可以指定参数p来使用Lp范式;mindspore中,只能计算L2范式

[2] 该函数的参数与torch中差别较大,请参考官方文档说明

[3] 该函数只能计算每行中最大的k个元素(相当于torch中的dim=1)

[4] 该函数中,clip_value_min,clip_value_max两个参数的数据类型是ms.Tensor不可以是int、float

[5] 该方法只能在GPU上可以运行,使用CPU、Ascend设备不可使用该方法

5.4 等效代码书写

对于代码,大部分都可以直接进行等效,但在一个项目中,可能会出现以下问题:

  1. 情况一:存在等效代码,但它的等效代码有设备要求,在当前设备上不能运行

  2. 情况二:在源项目中,使用了由torch、transformers构建的其它包(如sentence_transformer),但它们在mindspore中没有对应的包

对于上面两种情况,就只能进行等效代码的书写了

5.4.1 情况一

对于情况一,一般来讲,有以下解决办法:

  1. 借助mindspore内的方法,构建等效方法
  2. 使用与torch、transformers无关的包进行替代

以torch.einsum方法为例,在mindspore中,它的等效方法是

  • ops.Einsum(1.10)
  • ops.einsum(2.x)

但是,参考官方文档,这两种等效方法只能在GPU设备上运行

在这里插入图片描述
在这里插入图片描述

如果要在非GPU设备上运行,就需要书写等效代码

可以用以下方法来解决这个问题:

  1. 对原来的代码进行拆分分析,将它拆为分步运算,并使用mindspore中的方法替代分步运算,得到结果
  2. 使用np.einsum方法替代,在转回ms.Tensor

对于方法2, 由于numpy只能在CPU上运行,会存在数据在非GPU非CPU设备CPU设备之间不断传递,对性能有影响

5.4.2 情况二

对于情况二,一般也只有查阅官方文档、或者查看这个包的源码,找到它的torch写法,再改为mindspore写法

比如下面这段代码:

from sentence_transformers import SentenceTransformer

model_path = 'model/model'
model = SentenceTransformer(model_path)
res = model.encode(...)

显然,这里的model.encode是不能直接替代的,在官方文档中,给出了它的等效方法:

from transformers import AutoTokenizer, AutoModel
import torch


#Mean Pooling - Take attention mask into account for correct averaging
def mean_pooling(model_output, attention_mask):
    token_embeddings = model_output[0] #First element of model_output contains all token embeddings
    input_mask_expanded = attention_mask.unsqueeze(-1).expand(token_embeddings.size()).float()
    sum_embeddings = torch.sum(token_embeddings * input_mask_expanded, 1)
    sum_mask = torch.clamp(input_mask_expanded.sum(1), min=1e-9)
    return sum_embeddings / sum_mask



#Sentences we want sentence embeddings for
sentences = ['This framework generates embeddings for each input sentence',
             'Sentences are passed as a list of string.',
             'The quick brown fox jumps over the lazy dog.']

#Load AutoModel from huggingface model repository
tokenizer = AutoTokenizer.from_pretrained("sentence-transformers/all-MiniLM-L6-v2")
model = AutoModel.from_pretrained("sentence-transformers/all-MiniLM-L6-v2")

#Tokenize sentences
encoded_input = tokenizer(sentences, padding=True, truncation=True, max_length=128, return_tensors='pt')

#Compute token embeddings
with torch.no_grad():
    model_output = model(**encoded_input)

#Perform pooling. In this case, mean pooling
sentence_embeddings = mean_pooling(model_output, encoded_input['attention_mask'])

那么,只需要进行将上面代码有mindspore中的代码替代,就可以起到同样的效果

# 以BERT为例
from mindformers import BertTokenizer, BertForPreTraining
from mindspore import ops
import mindspore as ms


def mean_pooling(model_output, attention_mask):
    token_embeddings = model_output[0]
    input_mask_expanded = ops.broadcast_to(ops.expand_dims(attention_mask, axis=-1), token_embeddings.shape).astype(ms.float32)
    sum_embeddings = ops.reduce_sum(token_embeddings * input_mask_expanded, 1)
    sum_mask = ops.clip_by_value(input_mask_expanded.sum(1), clip_value_min=ms.Tensor(1e-9, ms.float32))
    return sum_embeddings / sum_mask

sentences = ['This framework generates embeddings for each input sentence',
             'Sentences are passed as a list of string.',
             'The quick brown fox jumps over the lazy dog.']


tokenizer = BertTokenizer.from_pretrained("sentence-transformers/all-MiniLM-L6-v2")
model = BertForPreTraining.from_pretrained("sentence-transformers/all-MiniLM-L6-v2")

encoded_input = tokenizer(sentences, padding=True, truncation=True, max_length=128, return_tensors='ms')
model_output = model(**encoded_input)
sentence_embeddings = mean_pooling(model_output, encoded_input['attention_mask'])
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值