python - 为什么 for 循环优化和通过 keras.Model 的个性化 .fit() 方法进行优化之间存在差异?
问题描述
我一直在尝试创建一个 keras.Model,它可以基于 GPD 的样本或其分位数拟合广义帕累托分布 (GPD)。拟合是通过最小化观测值和估计 GPD 的分位数之间的差异来完成的。
进口
import tensorflow as tf
from tensorflow import keras
import tensorflow_probability as tfp
GPD 层
我创建了一个基本的 GPD 层,方法是子类keras.layers.Layer
化并将参数保留在层之外以获得更好的性能。我也在使用tensorflow_probability.distributions.GeneralizedPareto
(见文档)中实现的 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 团队提供的指南。我做了以下子类:
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,),dtype="float32"),
trainable=True,
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, sigma=self.sigma, 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.sigma, 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),limit=1.0,delta=1/(N+1),dtype=tf.float64),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.sigma,self.gamma])
self.optimizer.apply_gradients(zip(gradients, [self.sigma, 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
这种方法导致损失、伽马和西格玛的预期行为。
请注意,这两种优化方法是在相同的时期数、相同的学习率和相同的优化器下执行的。最后的振荡是由于学习率不是最好的。
问题
- 为什么两种方法之间会有如此不同的行为?
- 做的
train_step
事情与里面的循环有什么不同.manual_fit()
吗? - 这是什么原因造成的?这是与梯度计算有关的问题吗?我正在使用的事实
tensorflow_probability.distributions.Distribution
?.fit()
是不是我不知道里面的梯度下降法有什么不同?
解决方案
推荐阅读
- python - Pandas:使用 qcut(data,3) 后,如何找到分位数的范围
- typescript - 如何在 TypeScript 中重新导出单个 lodash 模块
- reactjs - ReactJS 警告组件将不受控制的隐藏字段更改为受控
- mysql - 按“状态”和“用户”字段分组
- python-3.x - 使用 Python 使用嵌套字典填充 MongoDB 数据库
- apache - 如何在 Solr 中配置拼写检查功能?
- c# - C# 类中的条件属性
- glsl - GLSL-> DirectX9 HLSL 端口
- c# - 方法结束后窗口关闭
- node.js - 错误:组件中未定义渲染函数或模板:匿名