![](/img/trans.png)
[英]Use callback information for training neural network in tensorflow 2
[英]Use Scipy Optimizer with Tensorflow 2.0 for Neural Network training
在引入 Tensorflow 2.0 后,scipy 接口 (tf.contrib.opt.ScipyOptimizerInterface) 已被刪除。 However, I would still like to use the scipy optimizer scipy.optimize.minimize(method='L-BFGS-B') to train a neural network ( keras model sequential ). 為了讓優化器工作,它需要一個 function fun(x0)作為輸入,其中x0是一個形狀 (n,) 的數組。 因此,第一步是“展平”權重矩陣以獲得具有所需形狀的向量。 為此,我修改了https://pychao.com/2019/11/02/optimize-tensorflow-keras-models-with-l-bfgs-from-tensorflow-probability/提供的代碼。 這提供了一個 function 工廠,旨在創建這樣一個 function fun(x0) 。 但是,代碼似乎不起作用,並且損失 function 並沒有減少。 如果有人能幫我解決這個問題,我將不勝感激。
這是我正在使用的一段代碼:
func = function_factory(model, loss_function, x_u_train, u_train)
# convert initial model parameters to a 1D tf.Tensor
init_params = tf.dynamic_stitch(func.idx, model.trainable_variables)
init_params = tf.cast(init_params, dtype=tf.float32)
# train the model with L-BFGS solver
results = scipy.optimize.minimize(fun=func, x0=init_params, method='L-BFGS-B')
def loss_function(x_u_train, u_train, network):
u_pred = tf.cast(network(x_u_train), dtype=tf.float32)
loss_value = tf.reduce_mean(tf.square(u_train - u_pred))
return tf.cast(loss_value, dtype=tf.float32)
def function_factory(model, loss_f, x_u_train, u_train):
"""A factory to create a function required by tfp.optimizer.lbfgs_minimize.
Args:
model [in]: an instance of `tf.keras.Model` or its subclasses.
loss [in]: a function with signature loss_value = loss(pred_y, true_y).
train_x [in]: the input part of training data.
train_y [in]: the output part of training data.
Returns:
A function that has a signature of:
loss_value, gradients = f(model_parameters).
"""
# obtain the shapes of all trainable parameters in the model
shapes = tf.shape_n(model.trainable_variables)
n_tensors = len(shapes)
# we'll use tf.dynamic_stitch and tf.dynamic_partition later, so we need to
# prepare required information first
count = 0
idx = [] # stitch indices
part = [] # partition indices
for i, shape in enumerate(shapes):
n = np.product(shape)
idx.append(tf.reshape(tf.range(count, count+n, dtype=tf.int32), shape))
part.extend([i]*n)
count += n
part = tf.constant(part)
def assign_new_model_parameters(params_1d):
"""A function updating the model's parameters with a 1D tf.Tensor.
Args:
params_1d [in]: a 1D tf.Tensor representing the model's trainable parameters.
"""
params = tf.dynamic_partition(params_1d, part, n_tensors)
for i, (shape, param) in enumerate(zip(shapes, params)):
model.trainable_variables[i].assign(tf.cast(tf.reshape(param, shape), dtype=tf.float32))
# now create a function that will be returned by this factory
def f(params_1d):
"""
This function is created by function_factory.
Args:
params_1d [in]: a 1D tf.Tensor.
Returns:
A scalar loss.
"""
# update the parameters in the model
assign_new_model_parameters(params_1d)
# calculate the loss
loss_value = loss_f(x_u_train, u_train, model)
# print out iteration & loss
f.iter.assign_add(1)
tf.print("Iter:", f.iter, "loss:", loss_value)
return loss_value
# store these information as members so we can use them outside the scope
f.iter = tf.Variable(0)
f.idx = idx
f.part = part
f.shapes = shapes
f.assign_new_model_parameters = assign_new_model_parameters
return f
這里model是 object tf.keras.Sequential。
預先感謝您的任何幫助!
從 tf1 更改為 tf2 我遇到了同樣的問題,經過一些實驗后,我找到了下面的解決方案,該解決方案顯示了如何在用 tf.function 修飾的函數和 scipy 優化器之間建立接口。 與問題相比,重要的變化是:
jac=True
我在下面提供了一個如何解決玩具問題的示例。
import tensorflow as tf
import numpy as np
import scipy.optimize as sopt
def model(x):
return tf.reduce_sum(tf.square(x-tf.constant(2, dtype=tf.float32)))
@tf.function
def val_and_grad(x):
with tf.GradientTape() as tape:
tape.watch(x)
loss = model(x)
grad = tape.gradient(loss, x)
return loss, grad
def func(x):
return [vv.numpy().astype(np.float64) for vv in val_and_grad(tf.constant(x, dtype=tf.float32))]
resdd= sopt.minimize(fun=func, x0=np.ones(5),
jac=True, method='L-BFGS-B')
print("info:\n",resdd)
顯示
info:
fun: 7.105427357601002e-14
hess_inv: <5x5 LbfgsInvHessProduct with dtype=float64>
jac: array([-2.38418579e-07, -2.38418579e-07, -2.38418579e-07, -2.38418579e-07,
-2.38418579e-07])
message: b'CONVERGENCE: NORM_OF_PROJECTED_GRADIENT_<=_PGTOL'
nfev: 3
nit: 2
status: 0
success: True
x: array([1.99999988, 1.99999988, 1.99999988, 1.99999988, 1.99999988])
為了比較速度,我使用 lbfgs 優化器來解決樣式轉換問題(有關網絡,請參見此處)。 請注意,對於這個問題,網絡參數是固定的,輸入信號是適應的。 由於優化的參數(輸入信號)是一維的,因此不需要函數工廠。
我比較了四種實現
對於這個比較,優化在 300 次迭代后停止(通常為了收斂問題需要 3000 次迭代)
Method runtime(300it) final loss
TF1.12 240s 0.045 (baseline)
TF2.0 (E) 299s 0.045
TF2.0 (G) 233s 0.045
TF2.0/TFP 226s 0.053
TF2.0 急切模式 (TF2.0(E)) 工作正常,但比 TF1.12 基線版本慢約 20%。 帶有 tf.function 的 TF2.0(G) 工作正常,並且比 TF1.12 略快,這是一件好事。
來自 tensorflow_probability (TF2.0/TFP) 的優化器比使用 scipy 的 lbfgs 的 TF2.0(G) 略快,但沒有實現相同的錯誤減少。 事實上,隨着時間的推移損失的減少並不是單調的,這似乎是一個壞兆頭。 比較 lbfgs 的兩種實現(scipy 和 tensorflow_probability=TFP),很明顯 scipy 中的 Fortran 代碼要復雜得多。 因此,TFP 中算法的簡化在這里是有害的,甚至 TFP 在 float32 中執行所有計算的事實也可能是一個問題。
這是一個使用庫 ( autograd_minimize ) 的簡單解決方案,我根據 Roebel 的回答編寫了該庫:
import tensorflow as tf
from autograd_minimize import minimize
def rosen_tf(x):
return tf.reduce_sum(100.0*(x[1:] - x[:-1]**2.0)**2.0 + (1 - x[:-1])**2.0)
res = minimize(rosen_tf, np.array([0.,0.]))
print(res.x)
>>> array([0.99999912, 0.99999824])
它也適用於 keras 模型,如這個簡單的線性回歸示例所示:
import numpy as np
from tensorflow import keras
from tensorflow.keras import layers
from autograd_minimize.tf_wrapper import tf_function_factory
from autograd_minimize import minimize
import tensorflow as tf
#### Prepares data
X = np.random.random((200, 2))
y = X[:,:1]*2+X[:,1:]*0.4-1
#### Creates model
model = keras.Sequential([keras.Input(shape=2),
layers.Dense(1)])
# Transforms model into a function of its parameter
func, params = tf_function_factory(model, tf.keras.losses.MSE, X, y)
# Minimization
res = minimize(func, params, method='L-BFGS-B')
print(res.x)
>>> [array([[2.0000016 ],
[0.40000062]]), array([-1.00000164])]
我猜 SciPy 不知道如何計算 TensorFlow 對象的梯度。 嘗試使用原始函數工廠(即損失后也一起返回梯度),並在scipy.optimize.minimize
設置jac=True
。
我測試了原始 Gist 中的 python 代碼,並用 SciPy 優化器替換了tfp.optimizer.lbfgs_minimize
。 它與BFGS
方法一起使用:
results = scipy.optimize.minimize(fun=func, x0=init_params, jac=True, method='BFGS')
jac=True
意味着 SciPy 知道func
也返回梯度。
然而,對於L-BFGS-B
來說,這很棘手。 經過一番努力,我終於成功了。 我必須注釋掉@tf.function
行並讓func
返回grads.numpy()
而不是原始 TF Tensor。 我猜這是因為L-BFGS-B
的底層實現是一個 Fortran 函數,所以從 tf.Tensor -> numpy array -> Fortran array 轉換數據可能會出現一些問題。 並強制函數func
返回梯度的ndarray
版本解決了問題。 但是這樣就不可能使用@tf.function
。
(類似問題: L-BFGS 是否有 tf.keras.optimizers 實現? )
雖然這不像tf.contrib
那樣合法,但它是一個實現 L-BFGS(和任何其他scipy.optimize.minimize
求解器)供您考慮,以防它適合您的用例:
The package has models that extend keras.Model
and keras.Sequential
models, and can be compiled with `.compile(..., optimizer="L-BFGS") to use L-BFGS in TF2, or compiled with any of the其他標准優化器(因為在隨機和確定性之間切換應該很容易:):
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.