source_converter_lib.py 7.8 KB
Newer Older
Y
yejianwu 已提交
1 2 3 4 5 6 7 8 9 10 11 12 13 14
# Copyright 2018 Xiaomi, Inc.  All rights reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

L
Liangliang He 已提交
15
import datetime
Y
yejianwu 已提交
16 17 18
import os
import uuid
import numpy as np
19
import hashlib
Y
yejianwu 已提交
20

Y
yejianwu 已提交
21
from mace.proto import mace_pb2
Y
yejianwu 已提交
22 23 24 25
from jinja2 import Environment, FileSystemLoader

GENERATED_NAME = set()

L
Liangliang He 已提交
26

27
def generate_obfuscated_name(namespace, name):
L
Liangliang He 已提交
28 29 30 31 32 33 34 35 36 37 38 39
    md5 = hashlib.md5()
    md5.update(namespace)
    md5.update(name)
    md5_digest = md5.hexdigest()

    name = md5_digest[:8]
    while name in GENERATED_NAME:
        name = md5_digest
        assert name not in GENERATED_NAME
    GENERATED_NAME.add(name)
    return name

Y
yejianwu 已提交
40 41

def generate_tensor_map(tensors):
L
Liangliang He 已提交
42 43 44 45 46 47
    tensor_map = {}
    for t in tensors:
        if t.name not in tensor_map:
            tensor_map[t.name] = generate_obfuscated_name("tensor", t.name)
    return tensor_map

Y
yejianwu 已提交
48 49

def generate_in_out_map(ops, tensor_map):
L
Liangliang He 已提交
50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68
    in_out_map = {}
    for op in ops:
        op.name = generate_obfuscated_name("op", op.name)
        for input_name in op.input:
            if input_name not in in_out_map:
                if input_name in tensor_map:
                    in_out_map[input_name] = tensor_map[input_name]
                else:
                    in_out_map[input_name] = generate_obfuscated_name(
                        "in", input_name)
        for output_name in op.output:
            if output_name not in in_out_map:
                if output_name in tensor_map:
                    in_out_map[output_name] = tensor_map[output_name]
                else:
                    in_out_map[output_name] = generate_obfuscated_name(
                        "out", output_name)
    return in_out_map

Y
yejianwu 已提交
69 70

def obfuscate_name(net_def):
L
Liangliang He 已提交
71 72 73 74 75 76 77 78 79 80 81 82 83 84 85
    input_node = "mace_input_node"
    output_node = "mace_output_node"
    tensor_map = generate_tensor_map(net_def.tensors)
    in_out_map = generate_in_out_map(net_def.op, tensor_map)
    for t in net_def.tensors:
        if input_node not in t.name and output_node not in t.name:
            t.name = tensor_map[t.name]
    for op in net_def.op:
        for i in range(len(op.input)):
            if input_node not in op.input[i]:
                op.input[i] = in_out_map[op.input[i]]
        for i in range(len(op.output)):
            if output_node not in op.output[i]:
                op.output[i] = in_out_map[op.output[i]]

Y
yejianwu 已提交
86

87 88 89 90 91 92 93 94
def normalize_op_name(op_name):
    idx = op_name.rfind(':')
    if idx == -1:
        return op_name
    else:
        return op_name[:idx]


Y
yejianwu 已提交
95
def rename_tensor(net_def):
L
Liangliang He 已提交
96 97 98
    tensor_map = {}
    for t in net_def.tensors:
        if t.name not in tensor_map:
99 100
            tensor_map[t.name] = "_" + normalize_op_name(t.name).replace("/",
                                                                         "_")
L
Liangliang He 已提交
101 102 103 104 105 106 107 108 109
            t.name = tensor_map[t.name]
    for op in net_def.op:
        for i in range(len(op.input)):
            if op.input[i] in tensor_map:
                op.input[i] = tensor_map[op.input[i]]
        for i in range(len(op.output)):
            if op.output[i] in tensor_map:
                op.output[i] = tensor_map[op.output[i]]

Y
yejianwu 已提交
110 111

class TensorInfo:
112
    def __init__(self, id, t, runtime, gpu_data_type):
L
Liangliang He 已提交
113 114 115
        self.id = id
        self.data_type = mace_pb2.DataType.Name(t.data_type)
        if t.data_type == mace_pb2.DT_FLOAT:
116
            if runtime == 'gpu' and gpu_data_type == 'half':
L
Liangliang He 已提交
117 118 119 120 121 122 123 124 125 126 127 128 129
                self.data_type = mace_pb2.DT_HALF
                self.data = bytearray(
                    np.array(t.float_data).astype(np.float16).tobytes())
            else:
                self.data_type = mace_pb2.DT_FLOAT
                self.data = bytearray(
                    np.array(t.float_data).astype(np.float32).tobytes())
        elif t.data_type == mace_pb2.DT_INT32:
            self.data = bytearray(
                np.array(t.int32_data).astype(np.int32).tobytes())
        elif t.data_type == mace_pb2.DT_UINT8:
            self.data = bytearray(
                np.array(t.int32_data).astype(np.uint8).tolist())
130 131
        else:
            raise Exception('Tensor data type %s not supported' % t.data_type)
L
Liangliang He 已提交
132

Y
yejianwu 已提交
133 134

def stringfy(value):
L
Liangliang He 已提交
135 136 137
    return ', '.join('"{0}"'.format(w) for w in value)


138 139
def convert_to_source(net_def, model_checksum, weight_checksum, template_dir,
                      obfuscate, model_tag, output, runtime, embed_model_data,
140
                      winograd_conv, gpu_data_type):
L
Liangliang He 已提交
141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159
    if obfuscate:
        obfuscate_name(net_def)
    else:
        rename_tensor(net_def)

    # Capture our current directory
    print template_dir

    # Create the jinja2 environment.
    j2_env = Environment(
        loader=FileSystemLoader(template_dir), trim_blocks=True)
    j2_env.filters['stringfy'] = stringfy
    output_dir = os.path.dirname(output) + '/'
    # generate tensor source files
    template_name = 'tensor_source.jinja2'
    model_data = []
    offset = 0
    counter = 0
    for t in net_def.tensors:
160
        tensor_info = TensorInfo(counter, t, runtime, gpu_data_type)
L
Liangliang He 已提交
161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179
        # align
        if tensor_info.data_type != 'DT_UINT8' and offset % 4 != 0:
            padding = 4 - offset % 4
            model_data.extend(bytearray([0] * padding))
            offset += padding
        source = j2_env.get_template(template_name).render(
            tensor_info=tensor_info,
            tensor=t,
            tag=model_tag,
            offset=offset,
        )
        model_data.extend(tensor_info.data)
        offset += len(tensor_info.data)
        with open(output_dir + 'tensor' + str(counter) + '.cc', "wb") as f:
            f.write(source)
        counter += 1

    # generate tensor data
    template_name = 'tensor_data.jinja2'
Y
yejianwu 已提交
180
    source = j2_env.get_template(template_name).render(
L
Liangliang He 已提交
181 182 183 184 185 186 187
        tag=model_tag,
        embed_model_data=embed_model_data,
        model_data_size=offset,
        model_data=model_data)
    with open(output_dir + 'tensor_data' + '.cc', "wb") as f:
        f.write(source)
    if not embed_model_data:
Y
yejianwu 已提交
188 189
        with open(output_dir + model_tag + '.data', "wb") as f:
            f.write(bytearray(model_data))
L
Liangliang He 已提交
190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207

    # generate op source files
    template_name = 'operator.jinja2'
    counter = 0
    op_size = len(net_def.op)
    for start in range(0, op_size, 10):
        source = j2_env.get_template(template_name).render(
            start=start,
            end=min(start + 10, op_size),
            net=net_def,
            tag=model_tag,
            runtime=runtime,
        )
        with open(output_dir + 'op' + str(counter) + '.cc', "wb") as f:
            f.write(source)
        counter += 1

    # generate model source files
L
Liangliang He 已提交
208
    build_time = datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')
L
Liangliang He 已提交
209 210
    template_name = 'model.jinja2'
    tensors = [
211
        TensorInfo(i, net_def.tensors[i], runtime, gpu_data_type)
L
Liangliang He 已提交
212 213
        for i in range(len(net_def.tensors))
    ]
214 215 216
    checksum = model_checksum
    if weight_checksum is not None:
        checksum = "{},{}".format(model_checksum, weight_checksum)
Y
yejianwu 已提交
217
    source = j2_env.get_template(template_name).render(
L
Liangliang He 已提交
218 219 220 221
        tensors=tensors,
        net=net_def,
        tag=model_tag,
        runtime=runtime,
L
Liangliang He 已提交
222 223 224
        obfuscate=obfuscate,
        embed_model_data=embed_model_data,
        winograd_conv=winograd_conv,
225
        checksum=checksum,
L
Liangliang He 已提交
226
        build_time=build_time)
L
Liangliang He 已提交
227 228 229 230 231 232 233 234
    with open(output, "wb") as f:
        f.write(source)

    # generate model header file
    template_name = 'model_header.jinja2'
    source = j2_env.get_template(template_name).render(tag=model_tag, )
    with open(output_dir + model_tag + '.h', "wb") as f:
        f.write(source)