mirror of https://github.com/alibaba/MNN.git
174 lines
6.0 KiB
Python
174 lines
6.0 KiB
Python
import json
|
|
import os
|
|
import re
|
|
import math
|
|
import shutil
|
|
|
|
_SPACE_PATTERN = re.compile(r'\s+')
|
|
|
|
def _load_tensor(tensor_file):
|
|
text = open(tensor_file).read().strip()
|
|
parts = [v for v in _SPACE_PATTERN.split(text)]
|
|
|
|
numbers_per_line = len(parts)
|
|
index = text.find('\n')
|
|
if index != -1:
|
|
numbers_per_line = len(_SPACE_PATTERN.split(text[:index].strip()))
|
|
|
|
return [float(v) for v in parts], numbers_per_line
|
|
|
|
def _normalize(a):
|
|
sum = 0
|
|
for v in a:
|
|
sum += v * v
|
|
sum = math.sqrt(sum)
|
|
if not sum:
|
|
return a
|
|
return [v/sum for v in a]
|
|
|
|
def norm(a):
|
|
sum = 0
|
|
for v in a:
|
|
sum += v * v
|
|
return math.sqrt(sum)
|
|
|
|
def _normalized_distance(a, b):
|
|
assert len(a) == len(b)
|
|
a = _normalize(a)
|
|
b = _normalize(b)
|
|
|
|
sum = 0
|
|
for i, va in enumerate(a):
|
|
vb = b[i]
|
|
diff = va - vb
|
|
sum += diff * diff
|
|
return math.sqrt(sum)
|
|
|
|
def _distance(a, b):
|
|
assert len(a) == len(b)
|
|
# a = _normalize(a)
|
|
# b = _normalize(b)
|
|
|
|
sum = 0
|
|
for i, va in enumerate(a):
|
|
vb = b[i]
|
|
diff = va - vb
|
|
sum += diff * diff
|
|
return math.sqrt(sum)
|
|
|
|
def _cos_distance(a, b):
|
|
norm_a = norm(a)
|
|
norm_b = norm(b)
|
|
sum = 0
|
|
for i, va in enumerate(a):
|
|
vb = b[i]
|
|
sum += va * vb
|
|
return 1 - sum / (norm_a * norm_b)
|
|
|
|
class Analyzer(object):
|
|
# @param: scale_file - 量化时生成的scale_file
|
|
# @param: normal_output - 运行正常模型时生成的tensor dump目录
|
|
# @param: quant_output - 运行量化模型时生成的tensor dump目录
|
|
def __init__(self, scale_file, normal_output, quant_output):
|
|
self.scale_file = scale_file
|
|
self.normal_output = normal_output
|
|
self.quant_output = quant_output
|
|
self.scales = self._load_scale()
|
|
|
|
def _load_scale(self):
|
|
return json.loads(open(self.scale_file).read())
|
|
|
|
def execute(self):
|
|
dequant_output = self.quant_output + '-dequant'
|
|
shutil.rmtree(dequant_output, ignore_errors=True)
|
|
os.makedirs(dequant_output, exist_ok=True)
|
|
|
|
for op in self.scales:
|
|
if not op['outputs']:
|
|
continue
|
|
outputs = op['outputs']
|
|
|
|
for i, output in enumerate(outputs):
|
|
if not output['scales']:
|
|
continue
|
|
|
|
file_name = '%s_%d' % (op['name'].replace('/', '_'), i)
|
|
normal_tensor_file = os.path.join(
|
|
self.normal_output, file_name)
|
|
quant_tensor_file = os.path.join(self.quant_output, file_name)
|
|
if not os.path.exists(normal_tensor_file):
|
|
continue
|
|
if not os.path.exists(quant_tensor_file):
|
|
continue
|
|
|
|
normal_tensor, numbers_per_line = _load_tensor(normal_tensor_file)
|
|
quant_tensor, _ = _load_tensor(quant_tensor_file)
|
|
|
|
if len(normal_tensor) != len(quant_tensor):
|
|
print('error: normal tensor file: %s count: %d' % (normal_tensor_file, len(normal_tensor)))
|
|
print('error: quant tensor file: %s count: %d' % (quant_tensor_file, len(quant_tensor)))
|
|
sys.exit(1)
|
|
|
|
scales = output['scales']
|
|
assert len(normal_tensor) % len(scales) == 0
|
|
|
|
plane_size = len(normal_tensor) // len(scales)
|
|
|
|
dequant_tensor = []
|
|
non_zero_count = 0
|
|
max_value_count = 0
|
|
min_value_count = 0
|
|
max_value = 127
|
|
min_value = -127
|
|
for i, scale in enumerate(scales):
|
|
plane = quant_tensor[i*plane_size:(i+1)*plane_size]
|
|
for v in plane:
|
|
dequant_tensor.append(v * scale)
|
|
if v:
|
|
non_zero_count += 1
|
|
if v == max_value:
|
|
max_value_count += 1
|
|
if v == min_value:
|
|
min_value_count += 1
|
|
|
|
print(file_name)
|
|
d = _distance(normal_tensor, dequant_tensor)
|
|
normalized_d = _normalized_distance(normal_tensor, dequant_tensor)
|
|
cos_d = _cos_distance(normal_tensor, dequant_tensor)
|
|
print('max rate: %.06f%%' % (max_value_count / non_zero_count * 100))
|
|
print('min rate: %.06f%%' % (min_value_count / non_zero_count * 100))
|
|
print('norm of normal: %.06f' % (norm(normal_tensor)))
|
|
print('norm of quant: %.06f' % (norm(dequant_tensor)))
|
|
print('cos distance: %.08f' % (cos_d))
|
|
print('normalized distance: %.08f' % (normalized_d))
|
|
print('distance: %.08f\n' % (d))
|
|
|
|
# Output dequant tensor
|
|
lines = []
|
|
col = numbers_per_line
|
|
row = len(dequant_tensor) // col
|
|
|
|
assert len(dequant_tensor) % col == 0
|
|
for i in range(row):
|
|
parts = []
|
|
for j in range(col):
|
|
parts.append(('%f' % dequant_tensor[col*i+j]).rstrip('0').rstrip('.'))
|
|
lines.append('\t'.join(parts))
|
|
|
|
dequant_tensor_file = os.path.join(dequant_output, file_name)
|
|
open(dequant_tensor_file, 'w').write('\n'.join(lines))
|
|
|
|
if __name__ == '__main__':
|
|
import sys
|
|
import argparse
|
|
parser = argparse.ArgumentParser()
|
|
parser.add_argument("-s", "--scale-file",
|
|
required=True, help="量化时输出的scale文件")
|
|
parser.add_argument("-n", "--normal-output",
|
|
required=True, help="运行正常模型时生成的tensor dump目录")
|
|
parser.add_argument("-q", "--quant-output", required=True,
|
|
help="运行量化模型时生成的tensor dump目录")
|
|
args = parser.parse_args()
|
|
|
|
Analyzer(args.scale_file, args.normal_output, args.quant_output).execute()
|