Нейронная сеть для квадратного (x ^ 2) приближения - PullRequest
6 голосов
/ 14 марта 2019

Я новичок в TensorFlow и Data Science.Я сделал простой модуль, который должен выяснить отношения между входными и выходными числами.В этом случае х и х в квадрате.Код в Python:

import numpy as np
import tensorflow as tf

# TensorFlow only log error messages.
tf.logging.set_verbosity(tf.logging.ERROR)

features = np.array([-10, -9, -8, -7, -6, -5, -4, -3, -2, -1, 0, 1, 2, 3, 4, 5, 6, 7, 8,
                    9, 10], dtype = float)
labels = np.array([100, 81, 64, 49, 36, 25, 16, 9, 4, 1, 0, 1, 4, 9, 16, 25, 36, 49, 64,
                    81, 100], dtype = float)

model = tf.keras.Sequential([
    tf.keras.layers.Dense(units = 1, input_shape = [1])
])

model.compile(loss = "mean_squared_error", optimizer = tf.keras.optimizers.Adam(0.0001))
model.fit(features, labels, epochs = 50000, verbose = False)
print(model.predict([4, 11, 20]))

Я пробовал различное количество единиц, добавлял больше слоев и даже использовал функцию активации relu, но результаты всегда были неправильными.Он работает с другими отношениями, такими как х и 2х. В чем здесь проблема?

Ответы [ 2 ]

7 голосов
/ 14 марта 2019

Проблема в том, что x*x - это совсем другой зверь, чем a*x.

Обратите внимание, что делает обычная "нейронная сеть": она укладывается y = f(W*x + b) несколько раз, никогда не умножается x с собой.Следовательно, вы никогда не получите идеальную реконструкцию x*x.Если вы не установите f(x) = x*x или аналогичный.

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

И еще одно философское замечание: в машинном обучении мне кажется более полезным думать о хорошем/ плохо, а не правильно / неправильно.Особенно с регрессией, вы не можете получить «правильный» результат, если у вас нет точной модели.В этом случае нечему учиться.


На самом деле есть некоторые архитектуры NN, умножающие f(x) на g(x), прежде всего LSTM и Сети магистралей .Но даже у них один или оба из f(x), g(s) ограничены (с помощью логистической сигмоиды или tanh), поэтому они не могут полностью смоделировать x*x.


Поскольку существует некоторое недопонимание, выраженное вкомментарии, позвольте мне подчеркнуть несколько моментов:

  1. Вы можете приблизительно ваши данные.
  2. Чтобы преуспеть в любом смысле, вам нужен скрытый слой .
  3. Но больше никаких данных не требуется, хотя, если вы закроете пространство, модель подойдет ближе, см. ответ пустынного .

В качестве примера приведем пример модели с одним скрытым слоем из 10 единиц с активной активацией, обученной SGD со скоростью обучения 1e-3 за 15 000 итераций для минимизации MSE ваших данных.,Лучший из пяти прогонов:

Performance of a simple NN trained on OP's data

Вот полный код для воспроизведения результата.К сожалению, я не могу установить Keras / TF в моей текущей среде, но я надеюсь, что код PyTorch доступен: -)

#!/usr/bin/env python
import torch
import torch.nn as nn
import matplotlib.pyplot as plt

X = torch.tensor([range(-10,11)]).float().view(-1, 1)
Y = X*X

model = nn.Sequential(
    nn.Linear(1, 10),
    nn.Tanh(),
    nn.Linear(10, 1)
)

optimizer = torch.optim.SGD(model.parameters(), lr=1e-3)
loss_func = nn.MSELoss()
for _ in range(15000):
    optimizer.zero_grad()
    pred = model(X)
    loss = loss_func(pred, Y)
    loss.backward()
    optimizer.step()

x = torch.linspace(-12, 12, steps=200).view(-1, 1)
y = model(x)
f = x*x

plt.plot(x.detach().view(-1).numpy(), y.detach().view(-1).numpy(), 'r.', linestyle='None')
plt.plot(x.detach().view(-1).numpy(), f.detach().view(-1).numpy(), 'b')
plt.show()
4 голосов
/ 17 марта 2019

Вы делаете две очень основные ошибки:

  • Ваша ультрапростая модель (однослойная сеть с одним блоком) вряд ли вообще может рассматриваться как нейронная сеть, не говоря уже о "глубокой"изучение "один (так как ваш вопрос помечен)
  • Аналогично, ваш набор данных (всего 20 образцов) также очень мал

Понятно, что нейронные сети должны бытьнекоторая сложность, если они решают проблемы, даже такие «простые», как x*x;и где они действительно сияют, когда питаются большими обучающими наборами данных.

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

Сказав это, приведем довольно простую демонстрацию трехслойной нейронной сети вКерас для аппроксимации функции x*x, используя в качестве входных данных 10 000 случайных чисел, сгенерированных в [-50, 50]:

import numpy as np
import keras
from keras.models import Sequential
from keras.layers import Dense
from keras.optimizers import Adam
from keras import regularizers
import matplotlib.pyplot as plt

model = Sequential()
model.add(Dense(8, activation='relu', kernel_regularizer=regularizers.l2(0.001), input_shape = (1,)))
model.add(Dense(8, activation='relu', kernel_regularizer=regularizers.l2(0.001)))
model.add(Dense(1))

model.compile(optimizer=Adam(),loss='mse')

# generate 10,000 random numbers in [-50, 50], along with their squares
x = np.random.random((10000,1))*100-50
y = x**2

# fit the model, keeping 2,000 samples as validation set
hist = model.fit(x,y,validation_split=0.2,
             epochs= 15000,
             batch_size=256)

# check some predictions:
print(model.predict([4, -4, 11, 20, 8, -5]))
# result:
[[ 16.633354]
 [ 15.031291]
 [121.26833 ]
 [397.78638 ]
 [ 65.70035 ]
 [ 27.040245]]

Ну, не так уж и плохо!Помните, что NN являются функциональными аппроксиматорами : мы не должны ожидать, что они точно воспроизводят функциональные отношения и не "знают", что результаты для 4 и -4 должны быть идентичными.

Давайте сгенерируем несколько новых случайных данных в [-50,50] (помните, для всех практических целей это невидимые данные для модели) и нанесите их вместе с исходными, чтобы получитьболее общая картина:

plt.figure(figsize=(14,5))
plt.subplot(1,2,1)
p = np.random.random((1000,1))*100-50 # new random data in [-50, 50]
plt.plot(p,model.predict(p), '.')
plt.xlabel('x')
plt.ylabel('prediction')
plt.title('Predictions on NEW data in [-50,50]')

plt.subplot(1,2,2)
plt.xlabel('x')
plt.ylabel('y')
plt.plot(x,y,'.')
plt.title('Original data')

Результат:

enter image description here

Ну, это действительно похоже на хорошее приближение ...

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

Последнее, что нужно иметь в виду, это то, что, хотя мы и получили приличное приближениедаже с нашей относительно простой моделью мы должны ожидать , а не экстраполяцию , то есть хорошую производительность за пределами [-50, 50];подробности см. в моем ответе в . Плохо ли глубокое обучение при подборе простых нелинейных функций вне рамок обучения?

...