Инициализация весов для модели глубокого обучения - PullRequest
1 голос
/ 12 октября 2019

Я изучаю книгу о глубоком обучении, которая инициализирует веса между двумя слоями нейронов как:

w = np.random.randn(layers[i] + 1, layers[i + 1] + 1)
self.W.append(w / np.sqrt(layers[i]))

В соответствии с книгой, деление на np.sqrt (layer [i]) во второй строкекода делается по следующей причине:

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

Что именно это значит? И как это повлияет, если мы этого не сделаем?

1 Ответ

0 голосов
/ 13 октября 2019

Инициализация весов очень важна для устранения градиентов исчезновения / взрыва. Чтобы выходные данные / градиенты (обратное направление) проходили правильно, дисперсия выходов каждого слоя должна быть равна дисперсии его входных данных. Аналогично градиентам в обратном направлении. входной и выходной потоки слоя называются веером и веером слоя.

Чтобы лучше объяснить, что я имею в виду выше, позвольте мне датьты пример. Предположим, что у нас есть сто последовательных слоев, и мы применяем расчет прямой связи с линейной активацией (в конце концов, это просто матричное умножение), данные представляют собой 500 выборок из 100 объектов:

neurons, features = 100, 100
n_layers = 100

X = np.random.normal(size=(500, features))  # your input
mean, var = 0, 0
for layer in range(n_layers):
    W = np.random.normal(size=(features, neurons))
    X = np.dot(X, W)
    mean = mean + X.mean()
    var = var + X.var()
mean/n_layers, np.sqrt(var/n_layers)

# output:
(-4.055498760574568e+95, 8.424477240271639e+98)

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

fan_in = 1000  # change it to any number

X = np.random.normal(size=(100, fan_in)) 
W = np.random.normal(size=(fan_in, 1))

np.dot(X, W).std()

# result:
32.764359213560454

Это происходит потому, что мы суммируем fan_in (1000 в вышеописанном случае) произведения поэлементного умножения одного элемента входов X на один столбецW. Поэтому, если мы масштабируем все веса на 1 / sqrt (fan_in) , чтобы сохранить распределение потока, как показано в следующем фрагменте:

neurons, features = 100, 100
n_layers = 100

X = np.random.normal(size=(500, features))  # your input
mean, var = 0, 0
for layer in range(n_layers):
    W = np.random.normal(size=(features, neurons), scale=np.sqrt(1 / neurons))  # scaled the weights with the fan-in 
    X = np.dot(X, W)
    mean = mean + X.mean()
    var = var + X.var()
mean/n_layers, np.sqrt(var/n_layers)

# output:
(0.0002608301398189543, 1.021452570914829)

Вы можете прочитать большеоб инициализации ядра в следующем блоге

...