编者按:当模型在生产中出现的输入与练习期间供给的散布不对应时,通常会发生数据漂移。
Vatsal P.的这篇文章,介绍了怎么经过漂移方针直观了解数据漂移程度,并n经过一个运用组成数据的例子来展现怎么运用Python核算数据随时刻的漂移方针。
以下是译文,Enjoy!
作者 | Vatsal P.
编译 | 岳扬
由涌现AIGC引擎生成
本文主要介绍了数据漂移的基本概念以及怎么运用Python处理数据漂移问题。本文触及两种核算漂移方针的办法,即穿插熵和KL散度的完结和区别。
以下是这篇文章的大纲:
- 什么是数据漂移?
- 漂移方针-
- 穿插熵
- KL散度
- 处理方案架构
- 程序依靠要求
- 程序完结
- 生成数据
- 练习模型
- 生成调查成果
- 核算漂移方针
- 随时刻改变的漂移可视化
- 核算漂移方针的妨碍
- 结语
1 什么是数据漂移?
MLOps是构建机器学习模型并将其部署到生产环境中的一个组成部分。数据漂移能够归于MLOps中模型监控的领域。它指的是量化调查数据相关于练习数据的改变,这些改变随着时刻的推移,会对模型的猜测质量发生巨大的影响,而且往往是更糟的影响。盯梢与练习特征和猜测有关的漂移方针应该是模型监测和识别模型何时应该从头练习的重要组成部分。
能够参考作者的另一篇文章(pub.towardsai.net/monitoring-… ,了解在生产环境中监控ML模型相关概念和架构的更多细节。
你可能不想监测与你的模型猜测或模型特征相关的漂移,比方当你在进行模型猜测的基础上定时从头练习模型。此为一种与时刻序列模型的使用相关的常见状况。但是,你能够去追踪其他方针,以确认你所生成的模型质量。本文将主要关注那些与经典机器学习(分类、回归和聚类)相关的模型。
2 漂移方针
下文概述的两种方针都是量化概率散布类似程度的统计办法。
2.1 穿插熵
穿插熵能够经过以下公式界说:
穿插熵计实在的概率散布
- p:实在的概率散布
- q:估量的概率散布
从信息论的角度来看,熵反映了消除不确认性所需的信息量[3]。请注意,散布A和B的穿插熵将与散布B和A的穿插熵不同。
2.2 KL散度
Kullback Leibler散度,也称为KL散度,能够经过以下公式界说:
- P:实在的概率散布
- Q:估量的概率散布
然后,Kullback-Leibler散度是运用针对Q优化的编码而不是针对P优化的编码对P的样本进行编码所需的比特数的平均差[1]。请注意,散布A和B的KL散度与散布B和A的KL散度不同。
这两种衡量都不是距离衡量(distance metrics),由于这些衡量缺少对称性。
entropy / KL divergence of A,B != entropy / KL divergence of B,A
3 处理方案架构
下图概述了机器学习生命周期的运转方式,同时也包含了模型监控。正如上文说明,为了监测模型的功能,应该在练习阶段保存各种数据,也便是用于练习模型的特征和方针数据。这样就能够供给一个实在的基础数据源,以便与新的调查成果进行比较。
图片模型监测架构,图片由作者供给
3.1 程序依靠要求
以下Python模块及其版本是下文源代码运转的依靠。这些都是闻名的数据科学/数据剖析/机器学习的库,所以对大多数用户来说,装置特定的版本应该不是什么大问题。
Python=3.9.12
pandas>=1.4.3
numpy>=1.23.2
scipy>=1.9.1
matplotlib>=3.5.1
sklearn>=1.1.2
4 程序完结
下面将经过一个运用组成数据的例子来展现怎么核算数据随时刻的漂移方针。请注意,该示例生成的值不会与你操作生成的值一致,由于它们是随机生成的。此外,由于是随机生成的,所以从供给的可视化图像和数据中并不能解说成果。咱们的目的是供给可重复运用和可从头配置的代码供你运用。
4.1 生成数据
import uuid
import random
import pandas as pd
import numpy as np
from scipy.stats import entropy
from matplotlib import pyplot as plt
from sklearn.ensemble import RandomForestRegressor
from sklearn.model_selection import train_test_split
# generate data
def generate_data(n):
"""
This function will generate n rows of sample data.
params:
n (Int) : The number of rows you want to generate
returns:
A pandas dataframe with n rows.
"""
data = {
'uuid' : [str(uuid.uuid4()) for _ in range(n)],
'feature1' : [random.random() for _ in range(n)],
'feature2' : [random.random() for _ in range(n)],
'feature3' : [random.random() for _ in range(n)],
'target' : [sum([random.random(), random.random(), random.random()]) for _ in range(n)]
}
return pd.DataFrame(data)
sample_df = generate_data(1000)
上述代码将生成一个组成数据集,该数据集由1000行和列uuid、feature1、feature2、feature3、target组成。这是咱们的基础数据,模型将在此基础上进行练习。
4.2 练习模型
# train model
ft_cols = ['feature1', 'feature2', 'feature3']
X = sample_df[ft_cols].values
Y = sample_df['target'].values
X_train, X_test, y_train, y_test = train_test_split(
X, Y, test_size=0.3
)
rfr = RandomForestRegressor().fit(X_train, y_train)
为了本教程的目的,上述代码允许运用者依据咱们上面生成的特征和方针创立一个随机森林回归模型。假设这个模型会被推送到生产环境中,并且每天都会被调用。
4.3 生成调查成果
# generate observations
obs_df = generate_data(1500)
obs_df.drop(columns = ['target'], inplace = True)
# generate predictions
obs_df['prediction'] = obs_df[ft_cols].apply(lambda x : rfr.predict([x])[0], axis = 1)
obs_df = obs_df.rename(columns = {
'feature1' : 'obs_feature1',
'feature2' : 'obs_feature2',
'feature3' : 'obs_feature3'
})
上面的代码将生成第一天与模型投入生产并被调用的特征相关观测数据。现在咱们能够直观地看到实在练习数据与观测数据之间的差异。
plt.plot(sample_df['feature1'], alpha = 0.5, label = 'Ground Truth')
plt.plot(obs_df['obs_feature1'], alpha = 0.5, label = 'Observation')
plt.legend()
plt.title("Visualization of Feature1 Training Data vs Observations")
plt.show()
图片练习数据与feature1的观测数据可视化,图片由作者供给
从上图能够看出,在模型投入生产的第一天,该特征的观测值比实际状况要多。这是一个比较费事的问题,由于咱们不能去比较两个长度不一样的数值列表。如果咱们比较两个长度不同的数组,就会发生错误的成果。现在,为了核算漂移方针,咱们需求使观测值的长度与实在数据的长度持平。咱们能够经过创立N个buckets,并确认每个bucket中的观测值的频率来完结这一需求。其实本质上这是创立一个直方图,下面的代码片段能够显现这些散布的相互关系。
plt.hist(sample_df['feature1'], alpha = 0.5, label = 'Ground Truth', histtype = 'step')
plt.hist(obs_df['obs_feature1'], alpha = 0.5, label = 'Observation', histtype = 'step')
plt.legend()
plt.title("Feature Distribution of Ground Truth Data and Observation Data")
plt.show()
图片实在数据的特征散布与feature1的观测数据,图片由作者供给
现在两个数据集的规模相同,咱们能够比较两个散布的漂移状况。
4.4 核算漂移方针
def data_length_normalizer(gt_data, obs_data, bins = 100):
"""
Data length normalizer will normalize a set of data points if they
are not the same length.
params:
gt_data (List) : The list of values associated with the training data
obs_data (List) : The list of values associated with the observations
bins (Int) : The number of bins you want to use for the distributions
returns:
The ground truth and observation data in the same length.
"""
if len(gt_data) == len(obs_data):
return gt_data, obs_data
# scale bins accordingly to data size
if (len(gt_data) > 20*bins) and (len(obs_data) > 20*bins):
bins = 10*bins
# convert into frequency based distributions
gt_hist = plt.hist(gt_data, bins = bins)[0]
obs_hist = plt.hist(obs_data, bins = bins)[0]
plt.close() # prevents plot from showing
return gt_hist, obs_hist
def softmax(vec):
"""
This function will calculate the softmax of an array, essentially it will
convert an array of values into an array of probabilities.
params:
vec (List) : A list of values you want to calculate the softmax for
returns:
A list of probabilities associated with the input vector
"""
return(np.exp(vec)/np.exp(vec).sum())
def calc_cross_entropy(p, q):
"""
This function will calculate the cross entropy for a pair of
distributions.
params:
p (List) : A discrete distribution of values
q (List) : Sequence against which the relative entropy is computed.
returns:
The calculated entropy
"""
return entropy(p,q)
def calc_drift(gt_data, obs_data, gt_col, obs_col):
"""
This function will calculate the drift of two distributions given
the drift type identifeid by the user.
params:
gt_data (DataFrame) : The dataset which holds the training information
obs_data (DataFrame) : The dataset which holds the observed information
gt_col (String) : The training data column you want to compare
obs_col (String) : The observation column you want to compare
returns:
A drift score
"""
gt_data = gt_data[gt_col].values
obs_data = obs_data[obs_col].values
# makes sure the data is same size
gt_data, obs_data = data_length_normalizer(
gt_data = gt_data,
obs_data = obs_data
)
# convert to probabilities
gt_data = softmax(gt_data)
obs_data = softmax(obs_data)
# run drift scores
drift_score = calc_cross_entropy(gt_data, obs_data)
return drift_score
calc_drift(
gt_data = sample_df,
obs_data = obs_df,
gt_col = 'feature1',
obs_col = 'obs_feature1'
)
上面的代码概述了怎么核算调查数据相关于练习数据的漂移(运用scipy中的entropy完结)。首要经过matplotlib中的hist办法将输入向量的巨细归一化为相同的长度,经过softmax函数将这些值转换为概率,最后经过熵函数核算出漂移方针。
4.5 随时刻改变的漂移方针可视化
drift_scores = {k:[] for k in ft_cols}
days = 5
for i in range(days):
# calculate drift for all features and store results
for i in ft_cols:
drift = calc_drift(
gt_data = sample_df,
obs_data = generate_data(1500),
gt_col = 'feature1',
obs_col = 'feature1'
)
drift_scores[i].append(drift)
drift_df = pd.DataFrame(drift_scores)
# visualize drift
drift_df.plot(kind = 'line')
plt.title("Drift Scores Over Time for Each Feature")
plt.ylabel("Drift Score")
plt.xlabel("Times Model Was Used")
plt.show()
图片与模型中每个特征相关的漂移方针可视化,随模型在生产中的运用时刻而改变,图片由作者供给
对基于数据集发生的成果能够设定阈值,如果模型的大多数重要特征的漂移分数超越该阈值,这将是从头练习模型的一个重要方针。关于基于树的模型,能够经过sklearn或SHAP来识别每一个特征的重要性。
5 核算漂移方针的妨碍
在核算机器学习模型的数据漂移方针时,可能会遇到各种妨碍:
- 处理值为0的特征值或猜测值。这将发生与两个漂移完结相关的除以0的错误。关于该问题,一个快速而简单的处理办法是用一个非常接近于零的极小值来替代零。由于监测到的数据漂移可能是一种他人没有遇到的状况,所以要了解这对你正在处理的问题会发生什么影响。
- 比较一对长度不相同的散布。假设你在与每个特征和方针相关的1,000个观测值上练习模型,而每天生成猜测时,依据平台取得的流量显现特征和方针的调查量从1,000到10,000不等。这显着是有问题的,由于你不能比较两个不同长度的散布。为了处理这个问题,能够运用上文代码完结中所做的分选办法,将练习数据和观测值分选成相同巨细的组,然后在这些数据之上核算漂移。这种状况能够经过matplotlib库中的histogram办法轻松完结。
- 在运用softmax函数将频率转换为概率时得到NaN值。这是由于softmax函数依靠指数,在softmax的输出中得到NaN的成果是由于核算机无法核算一个大数字的指数。关于这种状况,需求另一种不运用softmax的完结办法,或许研讨怎样将你传入的数值规范化,以便softmax能够作业。
6 结语
这篇文章的重点是详细介绍怎么在经典机器学习的使用中核算数据漂移。本文回忆了常见的漂移核算方针(如KL Divergence和Cross Entropy)相关的原理和完结。
此外,这篇文章还概述了咱们在测验核算漂移时遇到的一些常见问题。比方当有零值时,除以零的错误和关于比较一对巨细不一样的散布的问题。需求注意,这篇文章主要对那些不常常从头练习模型的人有帮助。模型监测将作为一种重要手法来衡量一个模型是否成功,并确认它的功能何时因漂移而出现让步。这两者都是从头练习或从头进入模型开发阶段的方针。
欢迎在我的GitHub页面上检查与本教程相关的资源库。(github.com/vatsal220/m…
参考资料
en.wikipedia.org/wiki/Kullba…
en.wikipedia.org/wiki/Cross_…
stats.stackexchange.com/questions/3…
docs.scipy.org/doc/scipy/r…
本文经原作者授权,由Baihai IDP编译。如需转载译文,请联系获取授权。(原文链接:towardsdatascience.com/calculating…