zoukankan      html  css  js  c++  java
  • TensorFlow 笔记04

    ▶ 使用类封装写好的 TensorRT 模型,每个函数、类成员各司其职,而不是以前程序那样纯过程式,变量全部摊开

    ● 代码,程序入口 enter.py

      1 import os
      2 import sys
      3 import numpy as np
      4 import tensorrt as trt
      5 import pycuda.autoinit
      6 import pycuda.driver as cuda
      7 from datetime import datetime as dt
      8 
      9 import loadPara as ld
     10 import calibrator
     11 
     12 DEBUG           = True                     
     13 testDataPath    = "./"
     14 calibDataPath   = "./"
     15 tempPath        = "./"
     16 paraFile        = tempPath + "para.h5"
     17 cacheFile       = tempPath + "calib.cache"
     18 outputFile      = tempPath + "output.txt"
     19             
     20 iGpu            = 0
     21 calibCount      = 10                        # int8 校正次数
     22 inputSize       = (1,1,1)                   # 输入数据尺寸,CHW
     23 
     24 class TrtPredictor:
     25     def __init__(self, batchSize, dataType):
     26         self.logger     = trt.Logger(trt.Logger.ERROR)                      # 创建 logger
     27         self.batchSize  = batchSize
     28         self.dataType   = dataType
     29         self.h5f, ...   = fld.loadPara(paraFile)                            # 读取训练好的参数
     30         
     31         trtFilePath = tempPath + "engine-" + self.dataType + ".trt"         # 尝试读取创建好的引擎,没有则现场创建引擎
     32         if os.path.isfile(trtFilePath) and not DEBUG:
     33             f =  open(trtFilePath, 'rb')
     34             engineStr = f.read()                                            # enginStr 不作为成员变量
     35             self.runtime = trt.Runtime(self.logger)                         # 运行时读取文件中的引擎
     36             self.engine = self.runtime.deserialize_cuda_engine(engineStr)
     37             f.close()
     38             print("succeeded loading engine!")
     39         else:                             
     40             self.create_engine()                                            # 创建 engine,并写入文件,方便下次调用
     41             if self.engine == None:
     42                 print("failed building engine!")
     43                 return
     44             engineStr = self.engine.serialize()
     45             f = open(trtFilePath, 'wb')
     46             f.write(engineStr)
     47             f.close()
     48             print("succeeded building engine!")
     49                 
     50         self.context = self.engine.create_execution_context()               # 创建 CUDA 上下文和流
     51         self.stream = cuda.Stream()
     52                 
     53     def __del__(self):
     54         self.context = None
     55         self.engine  = None
     56         ld.close(self.h5f)
     57   
     58     def create_engine(self):                                                # 构造引擎
     59         self.builder = trt.Builder(self.logger)
     60         self.builder.max_batch_size     = 16
     61         self.builder.max_workspace_size = 1 << 30
     62         self.builder.fp16_mode          = self.dataType == 'float16'
     63         self.builder.int8_mode          = self.dataType == 'int8'
     64         self.network                    = self.builder.create_network()
     65         self.builder.strict_type_constraints = True        
     66                
     67         h0 = self.network.add_input("h0", trt.DataType.FLOAT, (1,) + inputSize) # 强制 N 为 1,多的数据堆在更高维度上
     68         
     69         #...                                                                # 中间层
     70         
     71         self.network.mark_output(h0.get_output(0))                          # 标记输出层
     72 
     73         if self.dataType == 'int8':                                         # int8 需要额外的校正,放到 builder 中
     74             self.builder.int8_calibrator = calibrator.MyCalibrator(calibCount, (self.batchSize,) + inputSize, calibDataPath, cacheFile)
     75 
     76         self.engine = self.builder.build_cuda_engine(self.network)          # 创建引擎(最容易失败的地方,返回构造函数后要检查是否成功)
     77     
     78     def infer(self, hInPart, dIn, dOut, hOut):                              # 推理
     79         cuda.memcpy_htod_async(dIn, hInPart, self.stream)
     80         self.context.execute_async(len(hInPart), [int(dIn), int(dOut)], self.stream.handle)
     81         cuda.memcpy_dtoh_async(hOut, dOut, self.stream)            
     82         self.stream.synchronize()
     83                
     84 def predict(hIn, batchSize, dataType):    
     85     predictor = TrtPredictor(batchSize, dataType)                           # 构造一个预测器
     86     
     87     dIn  = cuda.mem_alloc(hIn[0].nbytes * batchSize)                        # 准备主机和设备内存
     88     hOut = np.empty((batchSize,) + tuple(predictor.engine.get_binding_shape(1)), dtype = np.float32)
     89     dOut = cuda.mem_alloc(hOut.nbytes)                                      # dOut 和 hOut 的大小一定是相同的
     90     res=[]
     91     for i in range(0, len(hIn), batchSize):                                 # 分 batch 喂入数据
     92         predictor.infer(hIn[i:i+batchSize], dIn, dOut, hOut)                    
     93         res.append( hOut )
     94     
     95     return res
     96 
     97 if __name__ == "__main__":                                                  # main 函数负责管理 cuda.Device 和 cuda.Context
     98     _ = os.system("clear")
     99     batchSize = int(sys.argv[1])    if len(sys.argv) > 1 and sys.argv[1].isdigit()                         else 1
    100     dataType  = sys.argv[2]         if len(sys.argv) > 2 and sys.argv[2] in ['float32', 'float16', 'int8'] else 'float32'
    101     DEBUG     = int(sys.argv[3])>0  if len(sys.argv) > 3 and sys.argv[3].isdigit()                         else False
    102     if DEBUG:                                                               # 清除建好的 engine 和 校正缓存,重头开始建立                                
    103         oldEngineEAndCache = glob(tempPath+"*.trt") + glob(tempPath+"*.cache")
    104         [ os.remove(oldEngineEAndCache[i]) for i in range(len(oldEngineEAndCache))]
    105     print( "%s, start! GPU =  %s, batchSize = %2d, dataType  = %s" %( dt.now(), cuda.Device(iGpu).name(), batchSize, dataType ) )    
    106     
    107     inputData = loadData(testDataPath)                                      # 读取数据
    108     oF = open(outputFile, 'w')
    109     cuda.Device(iGpu).make_context()
    110 
    111     res = predict(inputData, batchSize, dataType)
    112     for i in range(len(res)):
    113         print( "%d -> %s" % (i,res[i]) )
    114         oF.write(res[i] + '
    ')
    115         
    116     oF.close()
    117     cuda.Context.pop()
    118     print( "%s, finish!" %(dt.now()) )

    ● 代码,矫正器 calibrator.py。核心思想是,手写一个数据生成器供 TensorRT 调用,每次从校正数据集中抽取 batchSize 那么多的数据,计算工作全部由 TensorRT 完成

     1 import os
     2 import numpy as np
     3 import tensorrt as trt
     4 import pycuda.driver as cuda
     5 import pycuda.autoinit
     6 
     7 class MyCalibrator(trt.IInt8EntropyCalibrator2):
     8     def __init__(self, calibCount, inputShape, calibDataPath, cacheFile):
     9         trt.IInt8EntropyCalibrator2.__init__(self)                                              # 基类默认构造函数                                        
    10         self.calibCount     = calibCount                
    11         self.shape          = inputShape
    12         self.calibDataSet   = self.laodData(calibDataPath)                                      # 需要自己实现一个读数据的函数
    13         self.cacheFile      = cacheFile
    14         self.calibData      = np.zeros(self.shape, dtype=np.float32)        
    15         self.dIn            = cuda.mem_alloc(trt.volume(self.shape) * trt.float32.itemsize)     # 准备好校正用的设备内存      
    16         self.oneBatch       = self.batchGenerator()
    17             
    18     def batchGenerator(self):                                                                   # calibrator 的核心,一个提供数据的生成器
    19         for i in range(self.calibCount):
    20             print("> calibration ", i)
    21             self.calibData = np.random.choice(self.calibDataSet, self.shape[0], replace=False)  # 随机选取数据 
    22             yield np.ascontiguousarray(self.calibData, dtype=np.float32)                        # 调整数据格式后抛出   
    23     
    24     def get_batch_size(self):                                                                   # TensorRT 会调用,不能改函数名
    25         return self.shape[0]
    26 
    27     def get_batch(self, names):                                                                 # TensorRT 会调用,不能改函数名,老版本 TensorRT 的输入参数个数可能不一样
    28         try:
    29             data = next(self.oneBatch)                                                          # 生成下一组校正数据,拷贝到设备并返回设备地址,否则退出
    30             cuda.memcpy_htod(self.dIn, data)
    31             return [int(self.dIn)]
    32         except StopIteration:
    33             return None
    34 
    35     def read_calibration_cache(self):                                                           # TensorRT 会调用,不能改函数名
    36         if os.path.exists(self.cacheFile):
    37             print( "cahce file: %s" %(self.cacheFile) )
    38             f = open(self.cacheFile, "rb")
    39             cache = f.read()
    40             f.close()
    41             return cache              
    42             
    43     def write_calibration_cache(self, cache):                                                   # TensorRT 会调用,不能改函数名
    44         print( "cahce file: %s" %(self.cacheFile) )
    45         f = open(self.cacheFile, "wb")
    46         f.write(cache)
    47         f.close()

    ▶ 我的程序在 TensorRT 5 中 float32 和 float16 一切正常,int8 无法正确计算。具体表现为:正确加载 calibrator 调用,部分中间层计算结果与 float32 一模一样(二进制位层次上的相同,显然是采用了 float32 代替进行计算了),部分层所有计算结果与 float32 有分歧(10-2 ~ 10-3 量级上的),在之后多层计算中误差会逐渐放大,最终计算结果与 float32 大相径庭。更新 TensorRT 6 之后问题消失,int8 也能计算正确结果并获得加速。

  • 相关阅读:
    为Fiddler增加Burp-like Inspector扩展 实现类似Burpsuite爆破、一键重放、编码转换等功能
    SVN常见问题总结一
    手把手教你学SVN
    js基本语法汇总
    最全的常用正则表达式大全
    CSS padding margin border属性详解
    从零开始学习jQuery (五) 事件与事件对象
    js正则表达式语法
    浏览器内部工作原理
    原生AJAX入门讲解(含实例)
  • 原文地址:https://www.cnblogs.com/cuancuancuanhao/p/11758908.html
Copyright © 2011-2022 走看看