Нелинейные вычисления в глубоких линейных сетях

Nonlinear Computation In Deep Linear Networks

Мы показали, что глубокие линейные сети (в их реализации с использованием арифметики с плавающей запятой) на самом деле не являются линейными и способны выполнять нелинейные вычисления. Мы использовали стратегии эволюции для поиска параметров в линейных сетях, которые задействуют эту особенность, позволяя нам решать нетривиальные задачи.

Нейронные сети состоят из набора линейных слоев, за которыми следуют нелинейные функции, такие как гиперболический тангенс или выпрямленный линейный блок (ReLU). Без нелинейности последовательные линейные слои в теории были бы математически эквивалентны одному единственному линейному слою. Поэтому кажется удивительным, что арифметика с плавающей запятой обладает достаточной нелинейностью для создания обучаемых глубоких сетей.

Предыстория

Числа, используемые компьютерами, не являются идеальными математическими объектами, а представляют собой приближенные значения, использующие конечное число бит. Числа с плавающей запятой обычно применяются компьютерами для представления математических объектов. Каждое число с плавающей запятой представлено комбинацией дробной части и экспоненты. В стандарте IEEE float32 для дроби используется 23 бита, для экспоненты — 8 и один бит для знака.

Single-precision floating-point format

Источник изображения:  Википедия

В результате этих соглашений и используемого двоичного формата наименьшее нормальное ненулевое число (в двоичной системе) равно 1.0…0×2^-126, которое далее мы будем называть min. Однако следующее представимое число равно 1.0…01×2^-126, что можно записать как min + 0.0…01×2^-126. Очевидно, что зазор между ними в 2^20 раз меньше, чем расстояние между 0 и min. В формате float32 числа, меньшие наименьшего представимого числа, округляются до нуля. Из-за этого «андерфлоу» в районе нуля все вычисления с числами с плавающей запятой становятся нелинейными.

Исключением из этих ограничений являются денормализованные числа, обработку которых на некотором вычислительном оборудовании можно отключать. Хотя в GPU и cuBLAS денормализованные числа включены по умолчанию, TensorFlow собирает все свои примитивы с отключенными денормализованными числами (с установленным флагом ftz=true). Это означает, что любая не-матричная операция умножения, написанная на TensorFlow, имеет неявную нелинейность (при условии, что масштаб вычислений близок к 1e-38).

Таким образом, хотя в целом разница между любым «математическим» числом и его нормальным представлением во float невелика, около нуля образуется большой разрыв, и погрешность аппроксимации может быть весьма существенной.

Image1

Это может приводить к странным эффектам, когда привычные правила математики перестают работать. Например,  (a+b)×c (a + b) \times c перестает быть равным a ×c+b× c \times c + b \times c

Например, если задать  a=0.4×min a = 0.4 \times min ,  b=0.5×min b = 0.5 \times min и c=1/ min c = 1 / min .

Тогда:  (a+b)×c=(0.4×min+0.5×min)×1/min=(0+0)×1/min=0 (a+b) \times c = (0.4 \times min + 0.5 \times min) \times 1 / min = (0 + 0) \times 1 / min = 0 .Однако:  (a×c)+(b×c)=0.4×min/min+0.5×min×1/min= 0.9 (a \times c) + (b \times c) = 0.4 \times min / min + 0.5 \times min \times 1 / min = 0.9 .

В другом примере мы можем задать a=2.5×min a = 2.5 \times min ,  b=−1.6×min b = -1.6 \times min и  c=1× min c = 1 \times min .

Тогда:  (a+b)+c=(0)+1×min=min (a+b) + c = (0) + 1 \times min = min .Однако:  (b+c)+a=(0×min)+2.5×min=2.5× min (b+c) + a = (0 \times min) + 2.5 \times min = 2.5 \times min .

В этом самом малом масштабе фундаментальная операция сложения становится нелинейной!

Использование нелинейностей со стратегиями эволюции

Мы хотели узнать, можно ли использовать эту присущую системе нелинейность в качестве вычислительной нелинейности, поскольку это позволило бы глубоким линейным сетям выполнять нелинейные вычисления. Сложность заключается в том, что современные библиотеки дифференцирования «не видят» эти нелинейности на самом малом масштабе. Из-за этого было бы сложно или невозможно обучить нейронную сеть их использованию с помощью обратного распространения ошибки.

Мы можем использовать стратегии эволюции (ES) для оценки градиентов без необходимости полагаться на символьное дифференцирование. Используя ES, мы действительно можем задействовать поведение float32 вблизи нуля в качестве вычислительной нелинейности. При обучении на MNIST глубокая линейная сеть, обученная методом обратного распространения ошибки, достигает точности на обучающей выборке в 94% и точности на тестовой выборке в 92%. Напротив, та же линейная сеть может достичь >99% точности на обучении и 96,7% на тесте, если ее обучать с помощью ES, обеспечивая при этом достаточную малость активаций для попадания в нелинейный диапазон float32. Этот прирост производительности обусловлен тем, что ES использует нелинейности в представлении float32. Эти мощные нелинейности позволяют любому слою генерировать новые признаки, представляющие собой нелинейные комбинации признаков более низкого уровня. Вот структура сети:

Python

1
x = tf.placeholder(dtype=tf.float32, shape=[batch_size,784])
2
y = tf.placeholder(dtype=tf.float32, shape=[batch_size,10])
3

4
w1 = tf.Variable(np.random.normal(scale=np.sqrt(2./784),size=[784,512]).astype(np.float32))
5
b1 = tf.Variable(np.zeros(512,dtype=np.float32))
6
w2 = tf.Variable(np.random.normal(scale=np.sqrt(2./512),size=[512,512]).astype(np.float32))
7
b2 = tf.Variable(np.zeros(512,dtype=np.float32))
8
w3 = tf.Variable(np.random.normal(scale=np.sqrt(2./512),size=[512,10]).astype(np.float32))
9
b3 = tf.Variable(np.zeros(10,dtype=np.float32))
10

11
params = [w1,b1,w2,b2,w3,b3]
12
nr_params = sum([np.prod(p.get_shape().as_list()) for p in params])
13
scaling = 2**125
14

15
def get_logits(par):
16
h1 = tf.nn.bias_add(tf.matmul(x , par[0]), par[1]) / scaling
17
h2 = tf.nn.bias_add(tf.matmul(h1, par[2]) , par[3] / scaling)
18
o = tf.nn.bias_add(tf.matmul(h2, par[4]), par[5]/ scaling)*scaling
19
return o

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

Автор

Якоб Ферстер (Jakob Foerster)

Полный текст статьи читайте на OpenAI