我正在尝试使用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 。
首先,我为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
似乎是一个不错的结果。
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)
(有关此信息的更多详细信息可以找到问题here)。因此,我尝试了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
,以提高结果的准确性,但没有任何明显的改善。
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
参数中犯了一个错误。非常感谢 Romeo Valentin 指出了这一点。我已在秒内更正了此错误。 3 。正确使用tol
参数肯定会改善改善differential_evolution
的结果。
此外,在关于github上的shgo
优化器的issue之后,事实证明,如果使用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])
我已在{strong> sec中将使用shgo
的最小化添加到完整代码中。 3 。
我认为这个问题已基本解决。但是,我仍然想知道,我的神经网络是否真的是执行此类任务的正确选择,或者是否存在更高级的结构或激活函数,可以产生更平滑的函数近似值。