如何解决为什么 for 循环优化和通过 keras.Model 的个性化 .fit() 方法优化之间存在差异? 进口
我一直在尝试创建一个 keras.Model,它可以根据 GPD 的样本或其分位数拟合广义帕累托分布 (GPD)。拟合是通过最小化观测值与估计 GPD 的分位数之间的差异来完成的。
进口
import tensorflow as tf
from tensorflow import keras
import tensorflow_probability as tfp
GPD 层
我通过对 keras.layers.Layer
进行子类化并按照建议将参数保留在层之外来创建一个基本的 GPD 层,以获得更好的性能。我还使用了在 tensorflow_probability.distributions.GeneralizedPareto
(see doc) 中实现的 GPD。 GPD 的两个参数是 gamma
和 sigma
。
class GPD_layer(keras.layers.Layer):
def __init__(self):
super(GPD_layer,self).__init__()
def call(self,gamma,sigma,num):
pareto = tfp.distributions.GeneralizedPareto(
loc=0.0,scale=sigma,concentration=gamma
)
return pareto.quantile(tf.cast(tf.range(start=1/(num+1),limit=1.0,delta=1/(num+1),dtype=tf.float64),dtype=tf.float32))
GPD 模型
我创建了一个 keras.Model
子类来拥有合适的模型并能够像常规 keras.Model 一样对其进行训练。遵循 Keras 团队提供的 guide。我做了以下子类:
class GPD_model(keras.Model):
def __init__(self):
super(GPD_model,self).__init__()
self.gpd_layer = GPD_layer()
self.gamma = tf.Variable(
initial_value=tf.random_normal_initializer(mean=0.2)(shape=(1,),dtype="float32"),trainable=True,name="gamma")
self.sigma = tf.Variable(
initial_value=tf.random_normal_initializer(mean=1.0)(shape=(1,name="sigma")
def call(self,num,training=None,*args,**kwargs):
if training:
return self.gpd_layer(gamma=self.gamma,sigma=self.sigma,num=num)
return self.gpd_layer(gamma=self.gamma,num=num)
def train_step(self,data):
num = data.shape[0]
with tf.GradientTape() as tape:
X_gpd = self(num = num,training=True)
loss = tf.norm(data-X_gpd,ord=1)
gradients = tape.gradient(loss,[self.sigma,self.gamma])
self.optimizer.apply_gradients(zip(gradients,self.gamma]))
return {"loss":loss}
训练模型
N=1000
gamma_th = 0.4
sigma_th = 2.0
pareto = tfp.distributions.GeneralizedPareto(loc=0,scale=sigma_th,concentration=gamma_th)
X_train = pareto.quantile(tf.cast(tf.range(start=1/(N+1),delta=1/(N+1),dtype=tf.float32))
model = GPD_model()
model.compile(
optimizer=keras.optimizers.SGD(learning_rate=1e-2),loss="mae",run_eagerly=True
)
history = model.fit(X_train,epochs=200,batch_size=X_train.shape[0])
这种训练导致 gamma 值变为负值并且似乎发散缓慢,并对损失产生以下影响:
创建其他方法
这是 GPD_model
类的一种方法,类似于外部训练循环。
def manual_fit(self,data,epochs):
history = {"loss":[],"sigma":[],"gamma":[]}
num = data.shape[0]
for step in range(epochs):
with tf.GradientTape() as tape:
Y = self(num = num)
loss = tf.norm(data-Y,ord=1)
gradients = tape.gradient(loss,self.gamma])
self.optimizer.apply_gradients(zip(gradients,self.gamma]))
history["loss"].append(loss)
history["sigma"].append(tf.constant(self.sigma))
history["gamma"].append(tf.constant(gamma))
print("\n",str(step+1)+"/"+str(epochs))
print([f"{k}: {history[k][-1]}" for k in history.keys()])
return history
此方法导致损失、伽玛和西格玛的预期行为。
请注意,这两种优化方法是在相同的 epoch 数、相同的学习率和相同的优化器下执行的。最后的振荡是由于学习率不是最好的。
问题
- 为什么这两种方法的行为如此不同?
-
train_step
与.manual_fit()
中的循环有什么不同吗? - 这是什么原因造成的?这是与梯度计算有关的问题吗?我正在使用
tensorflow_probability.distributions.Distribution
的事实?.fit()
内部的梯度下降方法有什么不同吗?
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。