在优化问题中使用神经网络

TMu*_*r83 6 optimization machine-learning scipy neural-network keras

我正在尝试使用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具有给定均值的神经网络表示的最小值。此示例的完整代码可以在sec中找到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')
Run Code Online (Sandbox Code Playgroud)

随后,我生成训练数据并训练模型:

# 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)
Run Code Online (Sandbox Code Playgroud)

训练完成后,我测试模型的准确性。为此,我使用了test(100)(关于test函数的定义,请参见下面第3节中的完整代码),对于我的特定模型,我得到的平均误差0.00517似乎是一个很好的结果。

2.最小化 F(x)

对于神经网络,F(x)我想在x具有给定平均值的约束下找到其最小值。为了达到这个目的,我将尝试当地的优化minimize以及全球优化differential_evolutionscipy.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)
Run Code Online (Sandbox Code Playgroud)

我的模型的结果如下:

>>> 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])
Run Code Online (Sandbox Code Playgroud)

使用的神经网络表示形式F(x),优化器立即陷入困境。使用确切的功能F(x) = 4*var(x),优化器可以找到正确的结果。

全局优化

我正在考虑尝试使用全局优化器,而不是本地优化器。首先,我尝试使用shgofrom,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)])
Run Code Online (Sandbox Code Playgroud)

我得到的结果如下:

>>> 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]
Run Code Online (Sandbox Code Playgroud)

通过使用神经网络逼近获得的结果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())
Run Code Online (Sandbox Code Playgroud)

更新资料

tol为该differential_evolution方法的参数弄错了。非常感谢罗密欧·瓦伦丁指出这一点。我已在几秒钟内纠正了此错误3tol正确使用参数肯定可以改善的结果differential_evolution

此外,在github上发布了有关优化器的问题shgo,事实证明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')
Run Code Online (Sandbox Code Playgroud)

使用这种采样方法,结果是完美的:

>>> res3_ML.success
True

>>> res3_ML.x
array([0.5, 0.5, 0.5, 0.5, 0.5, 0.5])
Run Code Online (Sandbox Code Playgroud)

我将最小化使用添加shgo到了完整的代码中。3

我认为这个问题已经基本解决。但是,我仍然想知道,我的神经网络是否真的适合此类任务,或者是否存在更高级的结构或激活函数,从而可以得出更平滑的函数近似值。