Обучите нейронную сеть абсолютной функции с минимальным количеством слоев

Я пытаюсь научить нейронную сеть изучать y = |x| функция. Как мы знаем, абсолютная функция имеет две разные линии, соединяющиеся друг с другом в нулевой точке. Итак, я пытаюсь использовать следующую последовательную модель:

Скрытый слой: 2 Плотный слой (релу активации) Выходной слой: 1 плотный слой

после обучения модели она соответствует только половине функции. Чаще всего это правая сторона, иногда левая. Как только я добавляю еще 1 слой в скрытый слой, чтобы вместо 2 у меня было 3, он идеально подходит для этой функции. Может ли кто-нибудь объяснить, почему нужен дополнительный слой, если абсолютная функция имеет только один разрез?

Вот код:

import numpy as np


X = np.linspace(-1000,1000,400)
np.random.shuffle(X)
Y = np.abs(X)

# Reshape data to fit the model input
X = X.reshape(-1, 1)
Y = Y.reshape(-1, 1)

import tensorflow as tf
import tensorflow as tf
import numpy as np
import matplotlib.pyplot as plt

# Build the model
model = tf.keras.models.Sequential([
    tf.keras.layers.Dense(2, activation='relu'),
    tf.keras.layers.Dense(1)
])

# Compile the model
model.compile(optimizer='adam', loss='mse',metrics=['mae'])
model.fit(X, Y, epochs=1000)
# Predict using the model
Y_pred = model.predict(X)

# Plot the results
plt.scatter(X, Y, color='blue', label='Actual')
plt.scatter(X, Y_pred, color='red', label='Predicted')
plt.title('Actual vs Predicted')
plt.xlabel('X')
plt.ylabel('Y')
plt.legend()
plt.show()

График для 2 плотных слоев:

График для 3 плотных слоев:

Вероятно, просто проблема с инициализацией. Если оба модуля инициализированы на «одной и той же стороне» (т. е. оба имеют одинаковый знак для своих весов), они никогда не смогут переключиться, потому что для другой стороны градиент не получен (из-за жесткого выхода 0). На самом деле это должно сработать в 50% случаев или около того. С большим количеством слоев вероятность этого становится менее вероятной, поскольку у вас фактически больше юнитов, поэтому вероятность того, что все они «на одной стороне», уменьшается в геометрической прогрессии.

xdurch0 12.04.2024 08:02

То есть вы имеете в виду, что, например, relu выводит 0 для второго перцептрона, он никогда не будет его обновлять, потому что он 0, и будет фокусироваться только на первом?

onik 12.04.2024 18:39

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

xdurch0 12.04.2024 20:46
Почему в Python есть оператор "pass"?
Почему в Python есть оператор "pass"?
Оператор pass в Python - это простая концепция, которую могут быстро освоить даже новички без опыта программирования.
Некоторые методы, о которых вы не знали, что они существуют в Python
Некоторые методы, о которых вы не знали, что они существуют в Python
Python - самый известный и самый простой в изучении язык в наши дни. Имея широкий спектр применения в области машинного обучения, Data Science,...
Основы Python Часть I
Основы Python Часть I
Вы когда-нибудь задумывались, почему в программах на Python вы видите приведенный ниже код?
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
Алиса и Боб имеют неориентированный граф из n узлов и трех типов ребер:
Оптимизация кода с помощью тернарного оператора Python
Оптимизация кода с помощью тернарного оператора Python
И последнее, что мы хотели бы показать вам, прежде чем двигаться дальше, это
Советы по эффективной веб-разработке с помощью Python
Советы по эффективной веб-разработке с помощью Python
Как веб-разработчик, Python может стать мощным инструментом для создания эффективных и масштабируемых веб-приложений.
5
3
90
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Имея два плотных слоя, вы, по сути, имеете входной и выходной слой. Судя по вашему коду, у вас есть только два слоя, но нет скрытых слоев в вашей модели. Это позволяет аппроксимировать функции 1-й степени, например линии для классификации, регрессии и т. д. Градиент рассчитывается только дважды за итерацию, что дает вам по сути две опорные точки — отлично подходит для линий, но не очень хорошо для притч.

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

np.abs(x) похож на притчу и требует трехслойной модели. Если вам интересно, как и почему работают нейронные сети, на Coursera есть очень хороший курс от Эндрю Нг «Машинное обучение». Это бесплатно, если вам не нужен сертификат.

На самом деле это не так, потому что в приведенном выше коде первый слой Dense является скрытым слоем (и входные данные по сути являются входным слоем, а последний слой Dense — выходным слоем). Чтобы воспроизвести описанную вами ситуацию, нам нужна NN только с 1 слоем. Кажется, решение этого конкретного вопроса описано в первом комментарии под публикацией, мы можем просто проверить его, используя Dense с разными значениями tf.keras.utils.set_random_seed(seed).

mz2300 09.05.2024 05:54

Нет, модель выше имеет только два определенных слоя. Первый плотный слой должен быть входным слоем. Никакого дополнительного входного слоя нигде не определено.

emely_pi 09.05.2024 05:57

Спасибо за ответ @emely_pi, но первый слой - это скрытый слой, потому что входной слой настраивается автоматически и поэтому здесь не написано, поэтому у меня Входной слой => Плотный слой с 2 ​​выходами => 1 выходной слой, поэтому с средний слой должен иметь возможность аппроксимировать функции x^2. Если бы это было не так, я бы получил ошибку, потому что форма первого слоя с двумя входами не соответствовала бы форме X, которая имеет один вход

onik 09.05.2024 11:16
Ответ принят как подходящий

Это зависит от инициализации веса.

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

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

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

Good_init устанавливает один вес на положительное, другой на отрицательное значения -> Проблема решена. оба bad_inits устанавливают веса на один и тот же знак, и будет изучена только половина домена.

import tensorflow as tf
import tensorflow as tf
import numpy as np
import matplotlib.pyplot as plt

X = np.linspace(-1000,1000,400)
np.random.shuffle(X)
Y = np.abs(X)

# Reshape data to fit the model input
X = X.reshape(-1, 1)
Y = Y.reshape(-1, 1)

from keras import backend as K

def good_init(shape, dtype=None):
    # one positive, one negative weight
    val=np.linspace(-1,1,np.prod(shape)).reshape(shape)
    return K.variable(value=val, dtype=dtype)

def bad_init_right(shape, dtype=None):
    # both weights positive, only right side works
    val=np.linspace(-1,1,np.prod(shape)).reshape(shape)
    val=np.abs(val)
    return K.variable(value=val, dtype=dtype)


def bad_init_left(shape, dtype=None):
    # both weights negative, only right side works
    val=np.linspace(-1,1,np.prod(shape)).reshape(shape)
    val=-np.abs(val)
    return K.variable(value=val, dtype=dtype)

# Build the model
model = tf.keras.models.Sequential([
    tf.keras.layers.Dense(2, activation='relu', kernel_initializer=bad_init_left),
    tf.keras.layers.Dense(1)
])

# Compile the model
model.compile(optimizer='adam', loss='mse',metrics=['mae'])
model.fit(X, Y, epochs=100)
# Predict using the model
Y_pred = model.predict(X)

# Plot the results
plt.scatter(X, Y, color='blue', label='Actual')
plt.scatter(X, Y_pred, color='red', label='Predicted')
plt.title('Actual vs Predicted')
plt.xlabel('X')
plt.ylabel('Y')
plt.legend()
plt.show()

у вас все получилось, спасибо! И последний вопрос: почему тогда добавление слоев помогает модели?

onik 11.05.2024 23:59

Если вы добавите третий нейрон, tf.keras.layers.Dense(2,activation='relu') -> tf.keras.layers.Dense(3,activation='relu'), вы увеличите вероятность наличия веса у обоих знаки от 50% до 75%.

Felix Zimmermann 12.05.2024 00:24

Другие вопросы по теме