Использование нейронных сетей в задачах оптимизации - PullRequest
5 голосов
/ 09 июля 2019

Я пытаюсь использовать Keras для настройки нейронной сети, которая должна приближаться к неизвестной функции F(x). Результирующее приближение нейронной сети для F(x) следует затем использовать для нахождения минимума суммы G(x) + F(x), где G(x) - некоторая произвольная функция. Проблема, с которой я сейчас сталкиваюсь, заключается в том, что приближение нейронной сети для F(x) недостаточно гладкое, и, следовательно, локальный оптимизатор застревает в крошечных локальных минимумах. Я был бы очень благодарен за любые идеи, чтобы улучшить мои результаты или решить эту проблему.

Простой пример: минимизация дисперсии

Позвольте мне проиллюстрировать проблему на очень упрощенном примере: я попытаюсь научить нейронную сеть функции F(x) = 4*var(x) с x = [x1,...,xN] и 0 <= xi <= 1, где var(x) означает дисперсию вектора x. Впоследствии я попытаюсь найти минимум представления нейронной сети F(x) при ограничении, что x имеет данное среднее значение. Полный код для этого примера можно найти в сек. 3 ниже.

1. Создание и обучение нейронной сети

Сначала я создаю нейронную сеть для приближения F(x):

N = 6  # Dimension of input vector x

# Set up the neural network
model = Sequential()
model.add(Dense(50, activation='sigmoid', input_dim=N))
model.add(Dense(1, activation='sigmoid'))
model.compile(optimizer='adam', loss='mse')

Впоследствии я генерирую тренировочные данные и обучаю модель:

# Generate training data
n_train = 100000  # Number of training samples
X_train = random((n_train, N))
y_train = 4*X_train.var(axis=1)

# Train the model
model.fit(X_train, y_train, epochs=100, batch_size=32, validation_split=0.2)

После завершения обучения я проверяю точность модели. Для этой цели я использую test(100) (для определения функции test см. Полный код в sec. 3 ниже), а для моей конкретной модели я получаю среднюю ошибку 0.00517, которая кажется быть довольно хорошим результатом.

2. Минимизация F(x)

Имея приближение нейронной сети для F(x) Я хочу найти его минимум при условии, что x имеет заданное среднее значение. С этой целью я попробую локальный оптимизатор minimize, а также глобальный оптимизатор differential_evolution от scipy.optimize.

Локальная оптимизация

Я стараюсь минимизировать F(x) при ограничении mean(x) = 0.5. Ясно, что точный результат F_min = 0, то есть минимум дисперсии при данном ограничении, был бы получен для равномерного распределения x = [0.5, 0.5, 0.5, 0.5, 0.5, 0.5]. Я специально выбрал плохой начальный вектор x0, чтобы проверить, может ли оптимизатор найти свой путь к минимуму:

# Constraint
avg = 0.5  # Given average value of x
cons = {'type': 'eq', 'fun': lambda x: x.mean()-avg}

# Start vector
x0 = avg * np.array([2, 2, 2, 0, 0, 0])

# Minimization of neural-network representation
res_ML = minimize(lambda x: model.predict([[x]]), x0,
                  bounds=N*[(0, 1)], constraints=cons)

# Minimization of the exact function
res_ex = minimize(lambda x: 4*x.var(), x0,
                  bounds=N*[(0, 1)], constraints=cons)

Результаты для моей модели следующие:

>>> res_ML.success
True

>>> res_ML.x
array([1., 1., 1., 0., 0., 0.])

>>> res_ex.x
array([0.5, 0.5, 0.5, 0.5, 0.5, 0.5])

Используя представление нейронной сети для F(x), оптимизатор сразу застрял. Используя точную функцию F(x) = 4*var(x), оптимизатор нашел правильный результат.

Глобальная оптимизация

Вместо локального оптимизатора я думал попробовать глобальный оптимизатор. Сначала я попытался использовать shgo из scipy.optimize, поскольку он поддерживает ограничения, однако кажется, что он не может найти минимум F(x), даже если используется точная функция (более подробную информацию об этой проблеме можно найти здесь ). Поэтому я попытался differential_evolution. Поскольку differential_evolution не поддерживает ограничения, я применяю условие mean(x) = 0.5, используя функцию штрафа:

# Minimization of neural-network representation
res2_ML = differential_evolution(lambda x: model.predict([[x]]) +
                                 1e3*(np.mean(x)-avg)**2, bounds=N*[(0, 1)])

# Minimization of the exact function
res2_ex = differential_evolution(lambda x: 4*x.var() + 1e3*(np.mean(x)-avg)**2,
                                 bounds=N*[(0, 1)])

Результаты, которые я получаю, таковы:

>>> res2_ML.success
True

>>> res2_ML.x
array([0.50276561, 0.49869386, 0.49310187, 0.49895304, 0.4987404 ,
       0.50770651])

>>> res2_ex.x
array([0.5, 0.5, 0.5, 0.5, 0.5, 0.5])

>>> [float(model.predict([[x]])) for x in (res2_ML.x, res2_ex.x)]
[0.05173008516430855, 0.05170735716819763]

Результат, полученный с использованием приближения нейронной сети для F(x), уже лучше, чем в случае локальной оптимизации, но он все еще не оптимален. Проблема не в том, что модель фактически предсказывает минимум в точке res2_ML.x, как видно из последней строки, поскольку прогноз модели для правильного вектора res2_ex.x фактически ниже. Я также пытался использовать tol=1e-12 в вызове differential_evolution, чтобы повысить точность результата, но без какого-либо заметного улучшения.

3. Полный код

import numpy as np
from numpy.random import random
from tensorflow.keras.layers import Dense
from tensorflow.keras.models import Sequential
from scipy.optimize import minimize, differential_evolution

# Parameters
N = 6             # Lenght of input vectors
n_train = 100000  # Number of training samples

# Generate training data
X_train = random((n_train, N))
y_train = 4*X_train.var(axis=1)

# Set up and train the neural network
model = Sequential()
model.add(Dense(50, activation='sigmoid', input_dim=N))
model.add(Dense(1, activation='sigmoid'))
model.compile(optimizer='adam', loss='mse')
model.fit(X_train, y_train, epochs=100, batch_size=32, validation_split=0.2)


# ############################ Local Optimization #############################


# Constraint
avg = 0.5  # Given average value of x
cons = {'type': 'eq', 'fun': lambda x: x.mean()-avg}

# Start vector
x0 = avg * np.array([2, 2, 2, 0, 0, 0])

# Minimization of neural-network representation
res_ML = minimize(lambda x: model.predict([[x]]), x0,
                  bounds=N*[(0, 1)], constraints=cons)

# Minimization of the exact function
res_ex = minimize(lambda x: 4*x.var(), x0,
                  bounds=N*[(0, 1)], constraints=cons)


# ########################### Global Optimization #############################


# Minimization of neural-network representation using differential_evolution
res2_ML = differential_evolution(lambda x: model.predict([[x]]) +
                                 1e3*(np.mean(x)-avg)**2, bounds=N*[(0, 1)],
                                 tol=1e-6)

# Minimization of neural-network representation using shgo
res3_ML = shgo(lambda x: model.predict([[x]]), bounds=N*[(0, 1)],
               constraints=cons, sampling_method='sobol')

# Minimization of the exact function
res2_ex = differential_evolution(lambda x: 4*x.var() + 1e3*(np.mean(x)-avg)**2,
                                 bounds=N*[(0, 1)])


# ############################# Helper Function ###############################


def test(n_test):
    '''
    Function for testing the model.
    '''
    x = random((n_test, N))             # Test data
    pred = model.predict([x])           # Model prediction
    exct = 4*x.var(axis=1)              # Exact values
    diff = np.abs(pred.flatten()-exct)  # Difference
    # Print the test results to screen
    print('\npred.   | exact   | diff.')
    print('---------------------------')
    for k in range(n_test):
        print('%.5f | %.5f | %.5f' % (pred[k], exct[k], diff[k]))
    print('---------------------------')
    print('       avg. error | %.5f' % diff.mean())

Обновление

Я допустил ошибку с параметром tol для метода differential_evolution. Большое спасибо Ромео Валентин за указание на это. Я исправил эту ошибку в сек. 3 . При правильном использовании параметра tol улучшает результаты differential_evolution.

Кроме того, после публикации проблемы относительно оптимизатора shgo на github оказалось, что оптимизатор shgo работает хорошо, если используется метод выборки sobol:

# Minimization of neural-network representation using shgo
res3_ML = shgo(lambda x: model.predict([[x]]), bounds=N*[(0, 1)],
               constraints=cons, sampling_method='sobol')

При использовании этого метода выборки результат идеален:

>>> res3_ML.success
True

>>> res3_ML.x
array([0.5, 0.5, 0.5, 0.5, 0.5, 0.5])

Я добавил минимизацию с помощью shgo к полному коду за сек. 3 .

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

...