tensorRT5.1.5.0实践 EfficientNet+Pytorch的转换尝试即准确度测试

15 篇文章 0 订阅

更新到2019.8.5
参考EfficientNet-Pytorch.

efficientnet_pytorch模块

总结

pytorch中有为efficientnet专门写好的网络模型,写在efficientnet_pytorch模块中。
模块包含EfficientNet的op-for-op的pytorch实现,也实现了预训练模型和示例。
这个代码是简单,而且高度可扩展的(extensible),并且容易集成到自己的项目中。
目前可以达到的目的:
(1)加载与训练的EfficientNet模型
(2)使用EfficientNet模型进行分类或特征提取
(3)在ImageNet或者自己的的图片集上评估EfficientNet
即将推出的功能:
(1)使用简单的命令在ImageNet上从头开始训练新模型
(2)在自己的数据集上快速的对EfficientNet做finetune
(3)输出产品级别的EfficientNet

内容

About EfficientNet

EfficientNets是一系列图像分类模型,实现了state-of-art的accuracy,但是比以前的模型smaller和faster。作者是基于AutoML和Compound Scaling开发的EfficientNets,首先使用AutoML Mobile框架开发一个名为EfficientNet-B0的移动规模基线网络(mobile-size baseline network);然后,我们使用复合缩放( compound scaling)方法来扩展此基线以获得EfficientNet-B1到B7。
EfficientNets在ImageNet上实现了最先进的精度,效率提高了一个数量级:
在高精度制度下,我们的EfficientNet-B7在ImageNet上以66M参数和37B FLOPS实现了最先进的84.4%前1 / 97.1%前5精度,在CPU推理上小8.4倍,快6.1倍比以前最好的Gpipe。
在中等精度方案中,我们的EfficientNet-B1比ResNet-152小7.6倍,CPU推理速度快5.7倍,具有类似的ImageNet精度。
与广泛使用的ResNet-50相比,在类似的FLOPS约束下,我们的EfficientNet-B4将前1精度从ResNet-50的76.3%提高到82.6%(+ 6.3%)。

About EfficientNet PyTorch

EfficientNet PyTorch是PyTorch对EfficientNet的重新实现。 它与原始的TensorFlow实现一致,因此很容易从TensorFlow检查点加载权重。 同时,我们的目标是使PyTorch实现尽可能简单,灵活和可扩展。

Usage

加载EfficientNet

from efficientnet_pytorch import EfficientNet
model = EfficientNet.from_name('efficientnet-b0')

加载预训练EfficientNet

from efficientnet_pytorch import EfficientNet
model = EfficientNet.from_pretrained('efficientnet-b0')

Onnx+EfficientNet实践

即利用efficientnet_pytorch模块提供的网络模型,自己训练,然后转成onnx,再转成tensorRT的engine,最后直接使用engine进行推断,来检测推断速度和准确率。

1.只跑了一个iteration的model

数据集:自己公司的车辆数据集,train:400万,test:22000
模型版本:efficientnet-b0
num_classes:1852

创建onnx文件

设置固定的输入size为(224,224)
用torch.load载入模型权重,然后既可以用torch.onnx.export()方法转成onnx格式文件

在tensorRT上测试准确度和时间

def main():

    onnx_path='./onnx_file/0net_params_8_5.onnx'
    engine_path="./trt_file/"+(onnx_path.split('/')[2]).split('.')[0]+".trt"
    test_dir = './label_txt/val.txt'
    num_classes=1852
    img_size=224



    # the mean value and std value is pre-calculated
    valiTransform = transforms.Compose([
        transforms.Resize((img_size,img_size)),
        transforms.ToTensor(),
        transforms.Normalize([0.3497724, 0.35888246, 0.37229323],[0.2726704, 0.2739602, 0.2761853])
    ])

    test_data = MyDataset(txt_path=test_dir, transform=valiTransform)
    test_loader = DataLoader(dataset=test_data, batch_size=1)
    test_data = MyDataset(txt_path=test_dir, transform=valiTransform)
    criterion = nn.CrossEntropyLoss().cuda()  # 选择损失函数
    print("The length of test data set is %s"%(test_data.__len__()))


    # initialize
    sum_time=0
    cls_num = num_classes
    loss_sigma=0.0
    conf_mat = np.zeros([cls_num, cls_num])

    with get_engine(onnx_path,engine_path) as engine ,engine.create_execution_context() as context:

        # because the input_size is fixed, the buffers should be allcated in advanced for all the data(img)
        inputs_alloc, outputs_alloc, bindings, stream = common.allocate_buffers(engine)
        for i,data in enumerate(test_loader):

            # read the data
            inputs,labels=data
            inputs=inputs.numpy()

            # get engine to inference
            print("Reading engine from file {}".format(engine_path))
            inputs_alloc[0].host = inputs
            t_start=time.time()
            trt_outputs = common.do_inference(context, bindings=bindings, inputs=inputs_alloc, outputs=outputs_alloc,stream=stream)
            t_end=time.time()
            t_time=t_end-t_start
            sum_time += t_time
            print('The {}th img, inference time : {}' .format(i, str(t_time)))

            trt_outputs = np.array(trt_outputs)
            outputs = torch.from_numpy(trt_outputs)
            outputs.detach()

            # 计算loss
            loss = criterion(outputs, labels)
            loss_sigma += loss.item()

            # 统计
            _, predicted = torch.max(outputs.data, 1)

            # 统计混淆矩阵
            for j in range(len(labels)):
                cate_i = labels[j].cpu().numpy()
                pre_i = predicted[j].cpu().numpy()
                conf_mat[cate_i, pre_i] += 1.0


    print('avg time: ' + str(sum_time / test_data.__len__()))
    print('{} set Accuracy:{:.2%}'.format('test', conf_mat.trace() / conf_mat.sum()))

在预处理和后处理这里,直接用numpy处理,总写不太对,就偷懒了。。日后填坑

结果

在这个只跑了一个iteration的model上

使用tensorRT的engen:
  前馈时间:0.003499486s
  准确度:91.64%

而在pytorch上的:
  前馈时间:0.01249988s
  准确度:91.04%

pytorch积累

1. torch.max()方法

value, index=torch.max(tensor,num)

tensor为输入的tensor,num为操作的维度
两个返回值,value为最大值,index为索引。
eg:

tensor=torch.randn(4,5)
value,index=torch.max(tensor,0)

则tensor:

tensor([[ 0.3045, -0.3509,  0.8248,  1.1156, -1.0433],
        [ 1.0962,  0.8329,  1.0494, -0.7632, -0.2968],
        [ 0.8749,  0.5894, -2.2095, -0.8238, -0.5258],
        [ 1.5428, -0.2508, -0.8234, -0.9917, -1.2586]])

value:

tensor([ 1.5428,  0.8329,  1.0494,  1.1156, -0.2968])

即按照每列的最大值计算,并且输出下索引index:

tensor([3, 1, 1, 0, 1])

把维度调成1的话,即value,index=torch,max(tensor,1),就是每行的最大值和索引。

2. torchvision.transform

这里学习了这个torchvision.transform
pytorch中的图像预处理包,一般用compose把多个步骤整合到一起。
eg:

transform.Compose([transform.Resize((224,224)), transform.ToTensor, transform.Normalize(...)])

.

3.torch.squeeze()和torch.unsqueeze()的用法

对数据维度进行亚索或者解压。
torch.squeeze() 这个函数主要对数据的维度进行压缩,去掉维数为1的的维度,比如是一行或者一列这种,一个一行三列(1,3)的数去掉第一个维数为一的维度之后就变成(3)行。squeeze(a)就是将a中所有为1的维度删掉。不为1的维度没有影响。a.squeeze(N) 就是去掉a中指定的维数为一的维度。还有一种形式就是b=torch.squeeze(a,N) a中去掉指定的定的维数为一的维度。
**torch.unsqueeze()**这个函数主要是对数据维度进行扩充。给指定位置加上维数为一的维度,比如原本有个三行的数据(3),在0的位置加了一维就变成一行三列(1,3)。a.squeeze(N) 就是在a中指定位置N加上一个维数为1的维度。还有一种形式就是b=torch.squeeze(a,N) a就是在a中指定位置N加上一个维数为1的维度。

4.数据读取

我的代码中用到了

   test_data = MyDataset(txt_path=test_dir, transform=valiTransform)
   test_loader = DataLoader(dataset=test_data, batch_size=1)

MyDataset

MyDataset是自己定义的数据结构,继承于torch.utils.data.Dataset

class MyDataset(Dataset):

    def __init__(self,txt_path,transform=None,target_transform=None):
        fh=open(txt_path,'r')
        imgs=[]
        for line in fh:
            line=line.strip('\n')
            words=line.split()
            path=words[0]
            if (len(words)>2):
                for i in range(1,len(words)-1):
                    path+=' '+words[i]
            imgs.append((path,int(words[-1])))
        self.imgs=imgs
        self.transform=transform
        self.target_transform=target_transform

    def __getitem__(self,index):
        fn,label=self.imgs[index]
        img=Image.open(fn).convert('RGB')
        if self.transform is not None:
            img=self.transform(img)
        return img,label

    def __len__(self):
        return len(self.imgs)

torch.utils.data的学习

torch.utils.data主要包括三个类:pytorch实现自由的数据读取-torch.utils.data的学习

(1)class torch.utils.data.Dataset

创建数据集,有_getitem__(self, index)函数来根据索引序号获取图像和标签,有_len_(self)函数来获取数据集的长度。

其他数据集必须是它的子类

(2)class torch.utils.data.sample.Sampler(data_source)

参数: data_source (Dataset) – dataset to sample from
作用: 创建一个采样器, class torch.utils.data.sampler.Sampler是所有的Sampler的基类, 其中,iter(self)函数来获取一个迭代器,对数据集中元素的索引进行迭代,len(self)方法返回迭代器中包含元素的长度.

(3)class torch.utils.data.DataLoader(dataset, batch_size=1, shuffle=False, sampler=None, batch_sampler=None, num_workers=0, collate_fn=, pin_memory=False, drop_last=False, timeout=0, worker_init_fn=None
  • dataset (Dataset): 加载数据的数据集,也就是上面所说的 torch.utils.data.Datase对象
  • batch_size (int, optional): 每批加载多少个样本,默认为1
  • shuffle (bool, optional): 设置为“真”时,在每个epoch对数据打乱.(默认:False)
  • sampler (Sampler, optional): 定义从数据集中提取样本的策略,返回一个样本Sampler对象。
  • batch_sampler (Sampler, optional): like sampler, but returns a batch of indices at a time 返回一批样本. 与atch_size, shuffle, sampler和 drop_last互斥.
  • num_workers (int, optional): 用于加载数据的子进程数。0表示数据将在主进程中加载​​。(默认:0)
  • collate_fn (callable, optional): 合并样本列表以形成一个 mini-batch. # callable可调用对象
  • pin_memory (bool, optional): 如果为 True, 数据加载器会将张量复制到 CUDA 固定内存中,然后再返回它们.
  • drop_last (bool, optional): 设定为 True 如果数据集大小不能被批量大小整除的时候, 将丢掉最后一个不完整的batch,(默认:False).
  • timeout (numeric, optional): 如果为正值,则为从工作人员收集批次的超时值。应始终是非负的。(默认:0)
  • worker_init_fn (callable, optional): If not None, this will be called on each worker subprocess with the worker id (an int in [0, num_workers - 1]) as input, after seeding and before data loading. (default: None).

5.torch.topk()

torch.topk

torch.topk(input,k,dim=None,largest=True,sorted=True,out=None) -> (Tensor,LongTensor)

沿给定dim维度返回输入张量input中 k 个最大值。
如果不指定dim,则默认为input的最后一维。
如果为largest为 False ,则返回最小的 k 个值。

返回一个元组 (values,indices),其中indices是原始输入张量input中测元素下标。
如果设定布尔值sorted 为_True_,将会确保返回的 k 个值被排序。

参数:
input (Tensor) – 输入张量
k (int) – “top-k”中的k
dim (int, optional) – 排序的维
largest (bool, optional) – 布尔值,控制返回最大或最小值
sorted (bool, optional) – 布尔值,控制返回值是否排序
out (tuple, optional) – 可选输出张量 (Tensor, LongTensor) output buffer

  • 1
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 7
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值