INT8-квантование: scale и zero point на Python

После калибровки известны границы значений, и теперь нужно построить целочисленную шкалу. Напишите quantize: рассчитайте масштаб, положение нуля и восстановленные значения. Правила округления, выхода за диапазон и нулевых границ заданы в условии.

Программа курса «Computer Vision Engineer: от детекции и Doc-AI до продакшна».

Масштаб и ноль шкалы

Задание

Калибровочный проход уже сделан: границы, в которых оказались значения, известны. Осталось построить по ним шкалу и уложить в неё числа.

Реализуйте функцию quantize(values, low, high). Аргумент values — значения, которые надо уложить, low и high — нижняя и верхняя границы, снятые калибровкой. Верните кортеж из четырёх элементов: масштаб, ноль шкалы, номера делений и восстановленные значения.

Как устроена шкала. Делений 256, их номера идут от −128 до 127. Настоящий ноль обязан попасть внутрь шкалы, поэтому границы сперва растягиваются до него: нижняя не может оказаться выше нуля, верхняя — ниже. Масштаб — цена одного деления — это расстояние между растянутыми границами, делённое на 255: делений 256, а промежутков между ними на один меньше.

Ноль шкалы — это номер деления, которому соответствует настоящий ноль: -128 - low / scale, где low — уже растянутая нижняя граница. Номер деления бывает только целым, поэтому результат округляется, а если он всё же вышел за шкалу, то обрезается по её краю; дальше в работе участвует уже обрезанное значение.

Номер деления для значения — это value / scale, округлённое и сдвинутое на ноль шкалы. Значение, оказавшееся за границами калибровки, даст номер вне шкалы — такой номер обрезается по ближайшему краю, от −128 до 127. Восстановленное значение получается обратным ходом: номер деления минус ноль шкалы, умноженное на масштаб.

Округление всюду идёт к ближайшему целому, а ровная половина — вверх, в сторону больших чисел: 2,5 даёт 3, а −2,5 даёт −2.

Отдельный случай — обе границы нулевые. Разность границ тогда тоже ноль, и делить на неё нельзя: масштаб в этом случае равен 1.0, ноль шкалы нулевой, все номера делений и все восстановленные значения — нули.

Пример

quantize([0.0, 2.0, -0.5, 3.0], -0.5, 2.0)
# (0.00980392156862745, -77, [-77, 127, -128, 127], [0.0, 2.0, -0.5, 2.0])

Отрезок длиной 2,5 поделён на 255 промежутков, нижняя граница пришлась на деление −128, поэтому настоящему нулю досталось деление −77. Последнее значение выходит за верхнюю границу калибровки, и его прижало к краю шкалы: 3,0 вернулось как 2,0.

Примечание

Вход уже разобран и приходит аргументами: список чисел с плавающей точкой и две границы, тоже числа, причём нижняя не выше верхней. Менять входной список нельзя. Масштаб и восстановленные значения сравниваются с точностью до одной миллиардной, подгонять округление в них не нужно, а вот номера делений и ноль шкалы сравниваются как целые числа — они должны быть типа int. Встроенный round на ровной половине округляет к чётному, а не вверх. Вызывать функцию вручную не нужно, проверка запускает её сама.

Что проверяется

Код проверок скрыт, так что вот полный список того, что в них разбирается:

  • формат ответа: кортеж из четырёх элементов, положительный масштаб, целые ноль шкалы и номера делений в пределах от −128 до 127, оба списка длиной со вход;
  • входной список после вызова не изменился;
  • пример из условия;
  • обе границы положительные — шкала растянута вниз до нуля;
  • обе границы отрицательные — шкала растянута вверх до нуля;
  • настоящий ноль восстанавливается ровно нулём;
  • значения далеко за обеими границами калибровки;
  • значение, попавшее ровно посередине между делениями;
  • границы симметричны относительно нуля;
  • обе границы нулевые;
  • значения ровно на границах калибровки;
  • одно значение;
  • пустой вход.
РешениеPython

Без регистрации. Гостевая проверка не сохраняет прогресс. Для сохранения перейдите к уроку.

Отправьте решение, чтобы увидеть результаты тестов.

Связанные задачи