1 概述
使用机器学习和深度学习进行置信区间、均值和方差的预测有多种方法,如: (1)分位数回归预测; (2)catboost使用RMSEWithUncertainty损失函数预测均值和不确定性; (3)ngboost使用自然提升树自带不确定性; (4)深度学习通过最小化均值和方差的损失函数(需自定义损失函数),最终输出二维张量(均值和方差); 本文介绍使用自定义损失函数,通过Keras框架实现均值和方差的预测。Keras后端使用tensorflow1.13.1版本。
2 原理与实现
2.1 高斯损失函数
高斯损失函数(Gaussian loss function)通常用于回归任务,特别是当目标变量的分布接近高斯分布(正态分布)时。
在机器学习和深度学习领域,损失函数(Loss Function)是用来估量模型的预测值和真实值之间差异的函数。对于回归问题,常用的损失函数包括均方误差(Mean Squared Error, MSE)和平均绝对误差(Mean Absolute Error, MAE)等。
而高斯损失函数并不是一个标准的、广泛使用的数据。不过,若我们尝试将其与高斯分布(正态分布)联系起来,可以考虑使用与高斯分布相关的某种形式的损失函数。
高斯分布(正太分布)的密度函数为:
如果我们希望构建一个与高斯分布有关的损失函数,可以考虑最小化负对数似然(Negative Log Likelihood, NLL),这在统计模型中是一个常见的做法。
对于单个数据点x,其负对数似然损失为:
可变换为:
2.2 自定义高斯损失函数预测均值和方差
使用tensorflow、Keras实现负对数似然损失函数,并预测均值和方差。示例:
# 通过最小化负对数似然损失,预测均值和方差
import numpy as np
import tensorflow as tf
from keras.models import Sequential
from keras.layers import Dense, Lambda
from keras import callbacks
def gaussian_NLL_loss(y_true, y_pred):
# 自定义损失函数:负对数似然函数
mean, variance = tf.split(y_pred, num_or_size_splits=2, axis=1)
loss = tf.reduce_mean(0.5*tf.math.log(variance) +
0.5*tf.math.square(y_true - mean)/variance)
return loss
class LossPrinter(callbacks.Callback):
# 打印loss
def on_batch_end(self, batch, logs={}):
pass
# print('batch: ', batch, 'loss: ', logs.get('loss'))
# 构建数据
X_train = np.random.rand(1000, 10)
y_train = np.random.rand(1000, 1)
# 构建简单模型
model = Sequential([
Dense(64, activation='relu', input_shape=(10,)),
Dense(64, activation='relu'),
Dense(2), # 输出2个值:均值,方差
Lambda(lambda x: tf.concat([
x[:, :1], tf.math.softplus(x[:, 1:])], axis=1)) # 确保方差非负
])
# 编译模型
model.compile(optimizer='adam', loss=gaussian_NLL_loss)
# 训练模型
model.fit(X_train, y_train, epochs=10,
batch_size=32, callbacks=[LossPrinter()])
# 预测
X_test = np.random.rand(5, 10)
y_pred = model.predict(X_test)
mean_pred = y_pred[:, 0]
var_pred = y_pred[:, 1]
std_pred = tf.math.sqrt(var_pred)
print('mean_pred: ', mean_pred)
print('var_pred: ', var_pred)
print('std_pred: ', std_pred)
若使用tensorflow2.x版本,则可以使用其提供的正态分布的对数函数,计算高斯损失:
def gaussian_NLL_loss_2(y_true, y_pred):
# 自定义损失函数2:负对数似然函数(需要使用tensorflow2.x版本)
mean, variance = tf.split(y_pred, num_or_size_splits=2, axis=1)
distribution = tf.distributions.Normal(
loc=mean, scale=tf.math.sqrt(variance))
# tf.distributers.Normal.log_prob: 返回的是正太分布(值)的对数
return -distribution.log_prob(y_true)
2.3 其他损失函数
除了负对数似然(NLL)损失函数外,还有其他几种损失函数可以用于同时预测均值和方差的任务。
- 均方误差(MSE)损失: 虽然MSE通常用于回归任务的均值预测,但也可以将其扩展到方差预测。例如,可以分别计算均值和方差的MSE,并将他们加权求和作为总损失。
- 平均绝对误差(MAE)损失: 类比MSE损失。
- 自定义损失函数: 可以根据具体任务的需求设计自定义损失函数。例如,你可以结合MSE和MAE来创建一个混合损失函数,或者根据预测分布的特性设计更复杂的损失函数。
- KL散度(Kullback-Leibler Divergence): 若希望直接比较两个概率分布(例如,预测分布和真实分布),KL散度是一个合适的选择。它衡量了一个概率分布相对于另一个概率分布的“信息量”或“差异”。
- CRPS(Continuous Ranked Probability Score): CRPS是一种用于评估概率预测准确性的度量,特别适用于连续变量。它考虑了预测分布的整体形状,而不是仅仅关注均值或中位数。
- 分位数损失(Quantile Loss): 若对预测的不确定性有特定的分位数要求(例如,95%置信区间),你可以使用分位数损失来训练模型。这种损失函数鼓励模型在指定的分位数上做出准确的预测。
2.4 自定义损失函数预测均值和方差
# 自定义损失函数:
# 结合均方误差(MSE)和方差的正则化项,最小化损失函数,预测均值和方差
import numpy as np
import tensorflow as tf
from keras.models import Sequential
from keras.layers import Dense, Lambda
def custom_loss(y_true, y_pred):
# 自定义损失函数
mean, variance = tf.split(y_pred, num_or_size_splits=2, axis=1)
# 计算均值的MSE
mean_loss = tf.reduce_mean(tf.square(mean - y_true))
# 计算方差的正则化项,鼓励模型预测较小的方差
var_loss = tf.reduce_mean(variance)
# 结合均值损失和标准差的正则化项,0.1是方差正则化项的权重
total_loss = mean_loss + 0.1*var_loss
return total_loss
# 构建数据
np.random.seed(0)
X_train = np.random.rand(1000, 10)
y_train = np.random.rand(1000, 1)
# 构建简单模型
model = Sequential([
Dense(64, activation='relu', input_shape=(10,)),
Dense(32, activation='relu'),
Dense(2), # 输出2个值:均值,标准差
Lambda(lambda x: tf.concat([
x[:, :1], tf.math.softplus(x[:, 1:])], axis=1)) # 确保标准差非负
])
# 编译模型
model.compile(optimizer='adam', loss=custom_loss)
# 训练模型
model.fit(X_train, y_train, epochs=10,
batch_size=32)
# 预测
X_test = np.random.rand(5, 10)
y_pred = model.predict(X_test)
mean_pred = y_pred[:, 0]
var_pred = y_pred[:, 1]
std_pred = np.sqrt(var_pred)
print('mean_pred: ', mean_pred)
print('var_pred: ', var_pred)
print('std_pred: ', std_pred)