Автор: Винай Кулкарни

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

Теория пакетной нормализации

Во время обучения нейронной сети мы должны следить за тем, чтобы сеть обучалась быстрее. Один из способов сделать это быстрее — нормализовать входные данные в сеть, а также нормализовать прерывистые слои сети. Эта нормализация промежуточного уровня называется пакетной нормализацией. Преимущество пакетной нормы также заключается в том, что она помогает свести к минимуму внутренний ковариатный сдвиг, как описано в этой статье.

Такие фреймворки, как TensorFlow, Keras и Caffe, имеют одно и то же представление с разными символами, прикрепленными к нему. В общем, нормализация партии может быть описана следующей математикой:

Уравнение пакетной нормализации

Здесь уравнение (1.1) является представлением Keras/TensorFlow. тогда как уравнение (1.2) является представлением, используемым структурой Caffe. В этой статье стиль уравнения (1.1) принят для продолжения контекста.

Теперь изменим уравнение (1.1) следующим образом:

Теперь, соблюдая уравнение (1.4), остается возможность оптимизации для уменьшения количества умножений и сложений. Коэффициент гребенки смещения (читается как комбинированное смещение) может быть рассчитан в автономном режиме для каждого канала. Также отношение «гамма/кварт (дисперсия)» может быть рассчитано в автономном режиме и может использоваться при реализации уравнения пакетной нормы. Это уравнение можно использовать в квантованной модели вывода для уменьшения сложности.

Квантовая модель вывода

Модель логического вывода, которая должна быть развернута на периферийных устройствах, обычно представляет собой процессоры, поддерживающие целочисленную арифметику, такие как процессоры серии ARM Cortex-M/A или устройства FPGA. Теперь, чтобы сделать модель логического вывода совместимой с архитектурой пограничных устройств, создадим симуляцию в Python. А затем преобразуйте цепочку входных, весовых и выходных данных модели вывода в формат с фиксированной точкой. В формате с фиксированной запятой Q для 8 битов выбирается для представления в целочисленном формате. Эта имитационная модель поможет вам быстрее разработать модель логического вывода на устройстве, а также поможет вам оценить точность модели.

например: Q2.6 представляет 6 бит дробного числа и 2 бита целого числа.

Теперь способ представления формата Q для каждого слоя выглядит следующим образом:

  1. Возьмите Максимум и Минимум входов, выходов и каждого слоя/веса.
  2. Получите дробные биты, необходимые для представления динамического диапазона (с использованием максимума/минимума), как показано ниже, используя функцию Python:

def get_fract_bits(tensor_float):
# Assumption is that out of 8 bits, one bit is used as sign
fract_dout = 7 - np.ceil(np.log2(abs(tensor_float).max()))

fract_dout = fract_dout.astype('int8')

return fract_dout

3. Теперь целочисленные биты являются 7-fractional_bits, так как один бит зарезервирован для представления знака.
4. Для начала выполните это на входе, а затем следуют уровни 1, 2… и т. д.
5. Выполните шаг квантования для весов, а затем для вывода, предполагая один пример ввода. Предполагается, что ввод нормализуется, чтобы мы могли обобщить формат Q, в противном случае это может привести к некоторой потере данных при подаче ненормализованного другого ввода.
6. Это установит формат Q для ввода, веса и выходы.

Пример:
Давайте рассмотрим Resnet-50 как модель, подлежащую квантованию. Давайте воспользуемся встроенным в Keras Resnet-50, обученным с помощью Imagenet.
#Creating the model

def model_create():

model = tf.compat.v1.keras.applications.resnet50.ResNet50(

include_top=True,

weights='imagenet',

input_tensor=None,

input_shape=None,

pooling=None,

classes=1000)

return model

Давайте подготовим ввод для resnet-50. Изображение ниже взято из набора данных ImageNet.

def prepare_input():

img = image.load_img(

"D:\\Elephant_water.jpg",

target_size=(224,224)

)

x_test = image.img_to_array(img)

x_test = np.expand_dims(x_test,axis=0)

x = preprocess_input(x_test) # from tensorflow.compat.v1.keras.applications.resnet50 import preprocess_input, decode_predictions

return x

Теперь давайте вызовем две вышеупомянутые функции и узнаем формат Q для ввода.

model = model_create()

x = prepare_input()

Если вы наблюдаете ввод «x», его динамический диапазон составляет от -123,68 до 131,32. Это затрудняет вписывание 8 бит, так как у нас есть только 7 бит для представления этих чисел, учитывая один бит знака. Следовательно, формат Q для этого ввода станет Q8.0, где 7 битов - это входные числа и 1 бит знака. Следовательно, он обрезает данные в диапазоне от -128 до +127 (от -²⁷ до ²⁷ -1). таким образом, мы бы потеряли некоторые данные в этом преобразовании входного квантования (наиболее очевидно, что 131,32 обрезается до 127), потерю которых можно увидеть с помощью отношения сигнала к квантованию шума, которое будет описано вскоре ниже.

Если вы будете следовать одному и тому же методу для каждого веса и выходных данных слоев, у нас будет некоторый формат Q, который мы можем исправить для имитации квантования.

# Lets get first layer properties (padding, _) = model.layers[1].padding # lets get second layer properties wts = model.layers[2].get_weights() strides = model.layers[2].strides hparameters =dict( pad=padding[0], stride=strides[0] # Lets Quantize the weights . quant_bits = 8 # This will be our data path. wts_qn,wts_bits_fract = Quantize(W,quant_bits) # Both weights and biases will be quantized with wts_bits_fract. # lets quantize Bias also at wts_bits_fract b_qn = (np.round(b *(2<<wts_bits_fract))).astype('int8') names_model,names_pair = getnames_layers(model) layers_op = get_each_layers(model,x,names_model) quant_bits = 8 print("Running conv2D") # Lets extract the first layer output from convolution block. Z_fl = layers_op[2] # This Number is first convolution. # Find out the maximum bits required for final convolved value. fract_dout = get_fract_bits(Z_fl) fractional_bits = [0,wts_bits_fract,fract_dout] # Quantized convolution here. x.astype('int8'), b_qn[np.newaxis,np.newaxis,np.newaxis,...], hparameters, fractional_bits)
Z, cache_conv = conv_forward(

x.astype('int8'),

wts_qn,

b_qn[np.newaxis,np.newaxis,np.newaxis,...],

hparameters,

fractional_bits)

Теперь, если вы наблюдаете приведенный выше фрагмент кода, операция свертки будет принимать входные данные, веса и выходные данные с определенными дробными битами. представляет 0 бит для дробного представления входных данных (Q8.0)
2-й элемент представляет 7 бит для дробного представления весов (Q1.7).
3-й элемент представляет -3 бита для дробного представления выходных данных ( Q8.0, но для целочисленного представления требуются дополнительные 3 бита, поскольку диапазон выходит за пределы 8-битного представления).

Это нужно будет повторить для каждого слоя, чтобы получить формат Q.

Теперь, когда знакомство с квантованием установлено, мы можем перейти к влиянию этого квантования на SQNR и, следовательно, на точность.

Отношение сигнала к шуму квантования

Поскольку мы сократили динамический диапазон от представления с плавающей запятой до представления с фиксированной запятой с помощью формата Q, мы дискретизировали значения до ближайшего возможного целочисленного представления. Это вводит шум квантования, который может быть математически определен отношением сигнала к шуму квантования.

Как показано в приведенном выше уравнении, мы будем измерять отношение мощности сигнала к мощности шума. Это представление, применяемое в логарифмической шкале, преобразуется в дБ (10log10SQNR). Здесь сигнал — это ввод с плавающей запятой, который мы квантуем до ближайшего целого числа, а шум — это шум квантования.
пример: пример ввода слона имеет максимальное значение 131,32, но мы представляем его до ближайшего возможное целое число, равное 127. Следовательно, это создает шум квантования = 131,32–127 = 4,32. до 48 дБ (6 * no_of_bits).

Это отражение SQNR на точность может быть установлено для каждой отдельной сети в зависимости от структуры. Но косвенно можно сказать, что чем лучше SQNR, тем выше точность.

Свертка в квантованных средах:

Операция свертки в CNN хорошо известна, когда мы умножаем ядро ​​на вход и накапливаем, чтобы получить результаты. В этом процессе мы должны помнить, что мы работаем с 8 битами в качестве входных данных, поэтому результат умножения требует не менее 16 бит, а затем накапливает его в 32-битном аккумуляторе, что поможет сохранить точность результата. Затем результат округляется или усекается до 8 бит, чтобы передать 8-битную ширину данных.

def conv_single_step_quantized(a_slice_prev, W, b,ip_fract,wt_fract,fract_dout):

"""

Apply one filter defined by parameters W on a single slice (a_slice_prev) of the output activation

of the previous layer.

Arguments:

a_slice_prev -- slice of input data of shape (f, f, n_C_prev)

W -- Weight parameters contained in a window - matrix of shape (f, f, n_C_prev)

b -- Bias parameters contained in a window - matrix of shape (1, 1, 1)

Returns:

Z -- a scalar value, result of convolving the sliding window (W, b) on a slice x of the input data

"""

# Element-wise product between a_slice and W. Do not add the bias yet.

s = np.multiply(a_slice_prev.astype('int16'),W) # Let result be held in 16 bit

# Sum over all entries of the volume s.

Z = np.sum(s.astype('int32')) # Final result be stored in int32.

# The Result of 32 bit is to be trucated to 8 bit to restore the data path.

# Add bias b to Z. Cast b to a float() so that Z results in a scalar value.

# Bring bias to 32 bits to add to Z.

Z = Z + (b << ip_fract).astype('int32')

# Lets find out how many integer bits are taken during addition.

# You can do this by taking leading no of bits in C/Assembly/FPGA programming

# Here lets simulate

Z = Z >> (ip_fract+wt_fract - fract_dout)

if(Z > 127):

Z = 127

elif(Z < -128):

Z = -128

else:

Z = Z.astype('int8')

return Z

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

Норма партии в квантованной среде

Как показано в уравнении 1.4, мы изменили представление, чтобы уменьшить сложность и выполнить пакетную нормализацию. В приведенном ниже коде показана та же реализация.

def calculate_bn(x,bn_param,Bn_fract_dout):

x_ip = x[0]
x_fract_bits = x[1]

bn_param_gamma_s = bn_param[0][0]

bn_param_fract_bits = bn_param[0][1]

op = x_ip*bn_param_gamma_s.astype(np.int16) # x*gamma_s

# This output will have x_fract_bits + bn_param_fract_bits

fract_bits =x_fract_bits + bn_param_fract_bits

bn_param_bias = bn_param[1][0]

bn_param_fract_bits = bn_param[1][1]

bias = bn_param_bias.astype(np.int16)

# lets adjust bias to fract bits

bias = bias << (fract_bits - bn_param_fract_bits)

op = op + bias # + bias

# Convert this op back to 8 bits, with Bn_fract_dout as fractional bits

op = op >> (fract_bits - Bn_fract_dout)

BN_op = op.astype(np.int8)

return BN_op

Теперь, когда эти части созданы для модели логического вывода квантования, мы можем видеть влияние пакетной нормы на квантование.

Результаты

Resnet-50, обученный с помощью ImageNet, используется для моделирования Python для квантования модели логического вывода. Из приведенных выше разделов мы связываем части вместе, чтобы проанализировать только первую свертка, за которой следует слой Batch Norm.

Операция свертки является самой тяжелой в сети с точки зрения сложности, а также с точки зрения поддержания точности модели. Итак, давайте посмотрим на данные свертки после того, как мы квантовали их до 8 бит. На приведенном ниже рисунке слева представлены выходные данные свертки 64 каналов (или примененных фильтров), среднее значение которых берется для сравнения. Синий цвет — это ссылка с плавающей запятой, а зеленый — квантованная реализация. График разницы (левая сторона) показывает, насколько велика вариация между плавающей и квантованной. Линия, нарисованная на этом рисунке разности, является средней, значение которой составляет около 4. Это означает, что мы получаем среднюю разницу между значениями с плавающей запятой и квантованными значениями, близкой к значению 4.

Теперь давайте посмотрим на правый рисунок, который представляет собой раздел «Пакетная нормализация». Как вы можете видеть, зеленая и синяя кривые расположены так близко друг к другу, а диапазон их различий сократился до менее чем 0,5 диапазона. Средняя линия составляет около 0,135, что раньше было около 4 в случае свертки. Это указывает на то, что мы уменьшаем наши различия между плавающей и квантованной реализацией со среднего значения 4 до 0,135 (почти близко к 0).

Теперь давайте посмотрим на график SQNR, чтобы оценить влияние Batch Norm.

Отношение сигнала к шуму квантования для последовательности слоев

На всякий случай, если значения не видны, у нас есть следующие номера SQNR
Входной SQNR: 25,58 дБ (вход в модель)
SQNR свертки: -4,4 дБ (выход 1-й свертки)
Batch-Norm SQNR: 20,98 дБ (выход пакетной нормализации)

Как вы можете видеть, входное SQNR составляет около 25,58 дБ, которое уменьшается до -4,4 дБ, что указывает на огромные потери из-за ограничения в представлении за пределами 8 бит. Но надежда не потеряна, так как нормализация партии помогает восстановить SQNR обратно до 20,98 дБ, приближая его к входному SQNR.

Заключение

  1. Пакетная нормализация помогает скорректировать среднее значение, тем самым упорядочивая вариации квантования по каналам.
  2. Пакетная нормализация восстанавливает SQNR. Как видно из приведенной выше демонстрации, мы видим восстановление SQNR по сравнению со слоем свертки.
  3. Если желательна квантованная модель логического вывода на границе, рассмотрите возможность включения пакетной нормализации, так как она действует как восстановление потерь квантования, а также помогает поддерживать точность, наряду с обучающими преимуществами более быстрой сходимости.
  4. Сложность пакетной нормализации можно уменьшить, используя (1.4), чтобы многие параметры можно было вычислять в автономном режиме, чтобы снизить нагрузку на пограничное устройство.

Ссылки

Первоначально опубликовано на https://ignitarium.com.