下载
中文
注册

样例参考

样例代码

使用在线推理需要充分考虑到sess.run首次执行时需要对模型进行编译和优化,耗时会增多。在编写推理应用时,应尽量保证应用生命周期内不频繁初始化。本例中,我们使用将推理过程封装到Classifier类中,以便应用可以控制Classifier对象的生命周期。

样例代码infer_from_pb.py:

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
# 通过加载已经训练好的pb模型,执行推理
import tensorflow as tf
import os
import argparse
from tensorflow.core.protobuf.rewriter_config_pb2 import RewriterConfig
import npu_bridge
import time
import numpy as np

def parse_args():
    '''
    用户自定义模型路径、输入、输出
    '''
    parser = argparse.ArgumentParser(formatter_class=argparse.ArgumentDefaultsHelpFormatter)
    parser.add_argument('--batchsize', default=1,
                        help="""batchsize""")
    parser.add_argument('--model_path', default='pb/resnet50HC.pb',
                        help="""pb path""")
    parser.add_argument('--image_path', default='image-50000',
                        help="""the data path""")
    parser.add_argument('--label_file', default='val_label.txt',
                        help="""label file""")
    parser.add_argument('--input_tensor_name', default='input_data:0',
                        help="""input_tensor_name""")
    parser.add_argument('--output_tensor_name', default='resnet_model/final_dense:0',
                        help="""output_tensor_name""")
    args, unknown_args = parser.parse_known_args()
    if len(unknown_args) > 0:
        for bad_arg in unknown_args:
            print("ERROR: Unknown command line arg: %s" % bad_arg)
        raise ValueError("Invalid command line arg(s)")
    return args

def read_file(image_name, path):
    '''
    从标签文件中读取图片的相关信息
    '''
    with open(path, 'r') as cs:
        rs_list = cs.readlines()
        for name in rs_list:
            if image_name in str(name):
                num = str(name).split(" ")[1]
                break
    return int(num) + 1

def normalize(inputs):
    '''
    图像归一化 
    '''
    mean = [121.0, 115.0, 100.0]
    std =  [70.0, 68.0, 71.0]
    mean = tf.expand_dims(tf.expand_dims(mean, 0), 0)
    std = tf.expand_dims(tf.expand_dims(std, 0), 0)
    inputs = inputs - mean
    inputs = inputs * (1.0 / std)
    return inputs

def image_process(image_path, label_file):
    '''
    对输入图像进行一定的预处理
    '''
    imagelist = []
    labellist = []
    images_count = 0
    for file in os.listdir(image_path):
        with tf.Session().as_default():
            image_file = os.path.join(image_path, file)
            image_name = image_file.split('/')[-1].split('.')[0]
            #images preprocessing
            image= tf.gfile.FastGFile(image_file, 'rb').read()
            img = tf.image.decode_jpeg(image, channels=3)
            bbox = tf.constant([0.1,0.1,0.9,0.9])
            img = tf.image.crop_and_resize(img[None, :, :, :], bbox[None, :], [0], [224, 224])[0]
            img = tf.clip_by_value(img, 0., 255.)
            img = normalize(img)
            img = tf.cast(img, tf.float16)
            images_count = images_count + 1
            img = img.eval()
            imagelist.append(img)
            tf.reset_default_graph()

            # read image label from label_file
            label = read_file(image_name, label_file)
            labellist.append(label)

    return np.array(imagelist), np.array(labellist),images_count


class Classifier(object):
    #set batchsize:
    args = parse_args()
    batch_size = int(args.batchsize)

    def __init__(self):

        # 昇腾AI处理器模型编译和优化配置
        config = tf.ConfigProto()
        custom_op = config.graph_options.rewrite_options.custom_optimizers.add()
        custom_op.name = "NpuOptimizer"
        # 配置1: 选择在昇腾AI处理器上执行推理
        custom_op.parameter_map["use_off_line"].b = True
        # 配置2:在线推理场景下建议保持默认值force_fp16,使用float16精度推理,以获得较优的性能
        custom_op.parameter_map["precision_mode"].s = tf.compat.as_bytes("force_fp16")
        # 配置3:图执行模式,推理场景下请配置为0,训练场景下为默认1
        custom_op.parameter_map["graph_run_mode"].i = 0
        # 配置4:关闭remapping和MemoryOptimizer
        config.graph_options.rewrite_options.remapping = RewriterConfig.OFF
        config.graph_options.rewrite_options.memory_optimization = RewriterConfig.OFF
        # 加载模型,并指定该模型的输入和输出节点
        args = parse_args()
        self.graph = self.__load_model(args.model_path)
        self.input_tensor = self.graph.get_tensor_by_name(args.input_tensor_name)
        self.output_tensor = self.graph.get_tensor_by_name(args.output_tensor_name)

        # 由于首次执行session run会触发模型编译,耗时较长,可以将session的生命周期和实例绑定
        self.sess = tf.Session(config=config, graph=self.graph)

    def __load_model(self, model_file):
        """
        加载静态图
        """
        with tf.gfile.GFile(model_file, "rb") as gf:
            graph_def = tf.GraphDef()
            graph_def.ParseFromString(gf.read())

        with tf.Graph().as_default() as graph:
            tf.import_graph_def(graph_def, name="")

        return graph

    def do_infer(self, batch_data):
        """
        执行推理
        """
        out_list = []
        total_time = 0
        i = 0
        for data in batch_data:
            t = time.time()
            out = self.sess.run(self.output_tensor, feed_dict={self.input_tensor: data})
            if i > 0:
                total_time = total_time + time.time() - t
            i = i + 1
            out_list.append(out)
        return np.array(out_list), total_time

    def batch_process(self, image_data, label_data):
        """
        批处理
        """
        # 获取当前输入数据的批次信息,自动将数据调整为固定批次
        n_dim = image_data.shape[0]
        batch_size = self.batch_size

        # 如果数据不足以用于整个批次,则需要进行数据补齐
        m = n_dim % batch_size
        if m < batch_size and m > 0:
            # 不足部分的数据以0进行填充补齐
            pad = np.zeros((batch_size - m, 224, 224, 3)).astype(np.float32)
            image_data = np.concatenate((image_data, pad), axis=0)

        # 定义可以被分成的最小批次
        mini_batch = []
        mini_label = []
        i = 0
        while i < n_dim:
            # Define the Minis that can be divided into several batches
            mini_batch.append(image_data[i: i + batch_size, :, :, :])
            mini_label.append(label_data[i: i + batch_size])
            i += batch_size

        return mini_batch, mini_label

def main():
    args = parse_args()
    top1_count = 0
    top5_count = 0
    # 数据预处理
    tf.reset_default_graph()
    print("########NOW Start Preprocess!!!#########")
    images, labels, images_count = image_process(args.image_path, args.label_file)
    # 批处理
    print("########NOW Start Batch!!!#########")
    classifier = Classifier()
    batch_images, batch_labels= classifier.batch_process(images, labels)
    # 开始执行推理
    print("########NOW Start inference!!!#########")
    batch_logits, total_time = classifier.do_infer(batch_images)
    # 计算精度
    batchsize = int(args.batchsize)
    total_step = int(images_count / batchsize)
    print("########NOW Start Compute Accuracy!!!#########")
    for i in range(total_step):
        top1acc = tf.reduce_sum(tf.cast(tf.equal(tf.argmax(batch_logits[i], 1), batch_labels[i]), tf.float32))
        top5acc = tf.reduce_sum(tf.cast(tf.nn.in_top_k(batch_logits[i], batch_labels[i], 5), tf.float32))
        with tf.Session().as_default():
            tf.reset_default_graph()
            top1_count += top1acc.eval()
            top5_count += top5acc.eval()

    print('+----------------------------------------+')
    print('the correct num is {}, total num is {}.'.format(top1_count, total_step * batchsize))
    print('Top1 accuracy:', top1_count / (total_step * batchsize) * 100)
    print('Top5 accuracy:', top5_count / (total_step * batchsize) * 100)
    print('images number = ', total_step * batchsize)
    print('images/sec = ', (total_step * batchsize) / total_time)
    print('+----------------------------------------+')

if __name__ == '__main__':
    main()

样例执行

以ResNet50模型为例,执行在线推理样例。

  1. 下载预训练模型。
    1. 在Ascend Gitee仓 > ModelZoo-TensorFlow的resnet50_for_TensorFlow路径下,参考README下载已经训练好的原始模型文件“resnet50_tensorflow_1.7.pb”。
    2. 准备好样例数据集imagenet2012,您可以从imagenet官方网站https://www.image-net.org/获取数据集。

    下载的预训练模型文件只支持输入batchsize为1的推理场景,用户也可根据实际,将训练产生的“.ckpt”模型文件转换冻结为自定义batchsize的“.pb”文件。

  2. 编辑推理脚本。

    创建“infer_from_pb.py”模型脚本文件,并参考样例代码写入相关代码。

  3. 配置在线推理进程依赖的环境变量。
    除此之外,还需进行如下配置:
     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    # 请依据实际在下列场景中选择一个进行训练依赖包安装路径的环境变量设置。具体如下(以HwHiAiUser安装用户为例):
    # 场景一:昇腾设备安装部署开发套件包Ascend-cann-toolkit(此时开发环境可进行训练任务)。
    . /home/HwHiAiUser/Ascend/ascend-toolkit/set_env.sh 
    # 场景二:昇腾设备安装部署软件包Ascend-cann-nnae。
    . /home/HwHiAiUser/Ascend/nnae/set_env.sh 
    
    # tfplugin包依赖。
    . /home/HwHiAiUser/Ascend/tfplugin/set_env.sh
    
    # 若运行环境中存在多个python3版本时,需要在环境变量中配置python的安装路径。如下配置以安装python3.7.5为例,可根据实际修改。
    export PATH=/usr/local/python3.7.5/bin:$PATH
    export LD_LIBRARY_PATH=/usr/local/python3.7.5/lib:$LD_LIBRARY_PATH
    
    # 添加当前脚本所在路径到PYTHONPATH,例如:
    export PYTHONPATH="$PYTHONPATH:/root/models"
    # 指定任务ID
    export JOB_ID=10087       # 任务ID,用户自定义,仅支持大小写字母,数字,中划线,下划线。不建议使用以0开始的纯数字
    

    若训练所在系统环境需要升级gcc(例如CentOS、Debian和BClinux系统),则“LD_LIBRARY_PATH”配置项处动态库查找路径需要添加“${install_path}/lib64”,其中“{install_path}”为gcc升级安装路径。请参见5

  4. 执行推理。

    python3 infer_from_pb.py --model_path=./resnet50_tensorflow_1.7.pb --image_path=/data/dataset/imagenet2012/val --label_file=/data/dataset/imagenet2012/val_label.txt --input_tensor_name=Placeholder:0 --output_tensor_name=fp32_vars/dense/BiasAdd:0

    上述为样例输入,用户可根据实际修改传入参数。用户在不确定pb模型文件节点名时,可参考读取pb模型文件的节点名称获取模型的输入输出节点名。

    在计算在线推理性能时,由于首轮推理会进行算子和图编译,耗时较长,因此从第二轮开始计时。