test_runner.py 7.77 KB
Newer Older
1
import os, sys
Shucai Xiao's avatar
Shucai Xiao committed
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
import numpy as np
import argparse
import onnx
from onnx import numpy_helper
import migraphx


def parse_args():
    parser = argparse.ArgumentParser(description="MIGraphX test runner")
    parser.add_argument('test_dir',
                        type=str,
                        metavar='test_loc',
                        help='folder where the test is stored')
    parser.add_argument('--target',
                        type=str,
                        default='gpu',
                        help='Specify where the tests execute (ref, gpu)')
    args = parser.parse_args()

    return args


def get_sub_folders(dir_name):
    dir_contents = os.listdir(dir_name)
    folders = []
    for item in dir_contents:
        tmp_item = dir_name + '/' + item
        if os.path.isdir(tmp_item):
            folders.append(item)
    folders.sort()

    return folders


def get_test_cases(dir_name):
    return get_sub_folders(dir_name)


def get_model_name(dir_name):
    dir_contents = os.listdir(dir_name)
    for item in dir_contents:
        file_name = dir_name + '/' + item
        if os.path.isfile(file_name) and file_name.endswith('.onnx'):
            return item

    return ''


def read_pb_file(filename):
    with open(filename, 'rb') as pfile:
        data_str = pfile.read()
        tensor = onnx.TensorProto()
        tensor.ParseFromString(data_str)
        np_array = numpy_helper.to_array(tensor)

57
    return tensor.name, np_array
Shucai Xiao's avatar
Shucai Xiao committed
58
59


60
def wrapup_inputs(io_folder, param_names):
Shucai Xiao's avatar
Shucai Xiao committed
61
    param_map = {}
62
63
    data_array = []
    name_array = []
64
65
66
67
    for i in range(len(param_names)):
        file_name = io_folder + '/input_' + str(i) + '.pb'
        name, data = read_pb_file(file_name)
        param_map[name] = data
68
69
70
71
72
73
74
75
76
77
        data_array.append(data)
        if name:
            name_array.append(name)

    if len(name_array) < len(data_array):
        param_map = {}
        for i in range(len(param_names)):
            param_map[param_names[i]] = data_array[i]

        return param_map
78
79
80
81
82

    for name in param_names:
        if not name in param_map.keys():
            print("Input {} does not exist!".format(name))
            sys.exit()
Shucai Xiao's avatar
Shucai Xiao committed
83
84
85
86

    return param_map


87
88
89
90
91
def read_outputs(io_folder, out_names):
    outputs = []
    data_array = []
    name_array = []
    for i in range(len(out_names)):
Shucai Xiao's avatar
Shucai Xiao committed
92
        file_name = io_folder + '/output_' + str(i) + '.pb'
93
        name, data = read_pb_file(file_name)
94
95
96
97
98
99
100
101
102
103
        data_array.append(data)
        if name:
            name_array.append(name)

    if len(name_array) < len(data_array):
        return data_array

    for name in out_names:
        index = name_array.index(name)
        outputs.append(data_array[index])
Shucai Xiao's avatar
Shucai Xiao committed
104
105
106
107

    return outputs


108
109
110
111
112
113
def model_parameter_names(model_file_name):
    with open(model_file_name, 'rb') as pfile:
        data_str = pfile.read()
        model_proto = onnx.ModelProto()
        model_proto.ParseFromString(data_str)
        init_names = set([(i.name) for i in model_proto.graph.initializer])
Shucai Xiao's avatar
Shucai Xiao committed
114
        param_names = [input.name for input in model_proto.graph.input if input.name not in init_names]
115
116
117

        return param_names

Shucai Xiao's avatar
Shucai Xiao committed
118

119
120
121
122
123
124
125
126
127
def model_output_names(model_file_name):
    with open(model_file_name, 'rb') as pfile:
        data_str = pfile.read()
        model_proto = onnx.ModelProto()
        model_proto.ParseFromString(data_str)
        output_names = [out.name for out in model_proto.graph.output]

        return output_names

Shucai Xiao's avatar
Shucai Xiao committed
128

129
130
def get_input_shapes(sample_case, param_names):
    param_shape_map = {}
131
132
    name_array = []
    shape_array = []
133
134
135
    for i in range(len(param_names)):
        file_name = sample_case + '/input_' + str(i) + '.pb'
        name, data = read_pb_file(file_name)
136
        param_shape_map[name] = data.shape
137
138
139
140
141
142
143
144
145
146
        shape_array.append(data.shape)
        if name:
            name_array.append(name)

    if len(name_array) < len(shape_array):
        param_shape_map = {}
        for i in range(len(param_names)):
            param_shape_map[param_names[i]] = shape_array[i]

        return param_shape_map
147
148

    for name in param_names:
Shucai Xiao's avatar
Shucai Xiao committed
149
        if not name in param_shape_map:
150
151
152
153
154
155
            print("Input {} does not exist!".format(name))
            sys.exit()

    return param_shape_map


Shucai Xiao's avatar
Shucai Xiao committed
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
def run_one_case(model, param_map):
    # convert np array to model argument
    pp = {}
    for key, val in param_map.items():
        pp[key] = migraphx.argument(val)

    # run the model
    model_outputs = model.run(param_map)

    # convert argument to np array
    outputs = []
    for output in model_outputs:
        outputs.append(np.array(output))

    return outputs


def check_correctness(gold_outputs, outputs, rtol=1e-3, atol=1e-3):
    if len(gold_outputs) != len(outputs):
        print("Number of outputs {} is not equal to expected number {}".format(
            len(outputs), len(gold_outputs)))
        return False

    out_num = len(gold_outputs)
    ret = True
    for i in range(out_num):
        if not np.allclose(gold_outputs[i], outputs[i], rtol, atol):
Shucai Xiao's avatar
Shucai Xiao committed
183
            print("\nOutput {} is incorrect ...".format(i))
Shucai Xiao's avatar
Shucai Xiao committed
184
            print("Expected value: \n{}".format(gold_outputs[i]))
Shucai Xiao's avatar
Shucai Xiao committed
185
186
            print("......")
            print("Actual value: \n{}\n".format(outputs[i]))
Shucai Xiao's avatar
Shucai Xiao committed
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
            ret = False

    return ret


def tune_input_shape(model, input_data):
    param_shapes = model.get_parameter_shapes()
    input_shapes = {}
    for name, s in param_shapes.items():
        assert name in input_data
        data_shape = list(input_data[name].shape)
        if not np.array_equal(data_shape, s.lens()):
            input_shapes[name] = data_shape

    return input_shapes


def main():
    args = parse_args()
    test_loc = args.test_dir
    target = args.target

    test_name = os.path.basename(os.path.normpath(test_loc))

    print("Running test \"{}\" on target \"{}\" ...\n".format(
        test_name, target))

    # get model full path
    model_name = get_model_name(test_loc)
    model_path_name = test_loc + '/' + model_name
217
218
219
220
221
222
223
224
225
226
227

    # get param names
    param_names = model_parameter_names(model_path_name)

    # get output names
    output_names = model_output_names(model_path_name)

    # get test cases
    cases = get_test_cases(test_loc)
    sample_case = test_loc + '/' + cases[0]
    param_shapes = get_input_shapes(sample_case, param_names)
228
229
    for name, dims in param_shapes.items():
        print("Input: {}, shape: {}".format(name, dims))
230
    print()
231

Shucai Xiao's avatar
Shucai Xiao committed
232
    # read and compile model
233
    model = migraphx.parse_onnx(model_path_name, map_input_dims=param_shapes)
Shucai Xiao's avatar
Shucai Xiao committed
234
235
236
237
238
239
240
241
242
243
    output_shapes = model.get_output_shapes()

    model.compile(migraphx.get_target(target))

    # get test cases
    case_num = len(cases)
    correct_num = 0
    for case_name in cases:
        io_folder = test_loc + '/' + case_name
        input_data = wrapup_inputs(io_folder, param_names)
244
        gold_outputs = read_outputs(io_folder, output_names)
Shucai Xiao's avatar
Shucai Xiao committed
245
246
247
248
249
250
251
252
253
254
255
256
257

        # if input shape is different from model shape, reload and recompile
        # model
        input_shapes = tune_input_shape(model, input_data)
        if not len(input_shapes) == 0:
            model = migraphx.parse_onnx(model_path_name,
                                        map_input_dims=input_shapes)
            model.compile(migraphx.get_target(target))

        # run the model and return outputs
        output_data = run_one_case(model, input_data)

        # check output correctness
258
        ret = check_correctness(gold_outputs, output_data)
Shucai Xiao's avatar
Shucai Xiao committed
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
        if ret:
            correct_num += 1

        output_str = "PASSED" if ret else "FAILED"
        print("\tCase {}: {}".format(case_name, output_str))

    print("\nTest \"{}\" has {} cases:".format(test_name, case_num))
    print("\t Passed: {}".format(correct_num))
    print("\t Failed: {}".format(case_num - correct_num))
    if case_num > correct_num:
        error_num = case_num - correct_num
        raise ValueError(str(error_num) + " cases failed!")


if __name__ == "__main__":
    main()