介绍
在本文中,我们介绍并展示了如何使用开源包RecList beta进行评估;RecList是一种通用的即插即用方法,用于扩大测试规模,并提供了一个易于扩展的接口,适用于定制的使用场景。RecList是一个开源项目,在GitHub上免费提供。
RecList可以让您将代码中的评估部分分离出来,并将其封装在一个类中,该类会自动处理其他一些事情(例如存储和记录)。
RecList的alpha版本大约一年多前发布。自那时以来,RecList已经获得了400多个GitHub星标。
我们已经使用RecList并对其进行了大规模压力测试,以在2022年的CIKM(信息与知识管理国际会议)上进行了一个推荐系统挑战赛,并目前正在准备2023年在KDD(知识发现与数据挖掘国际会议)上的下一个挑战赛。RecList使我们能够为所有参与者系统化地进行评估。这样,一旦每个人都使用相同的RecList,比较不同的评估结果就变得容易了。
重新思考API
通常只有在构建完成后,您才会意识到如何改进它。
对于那些使用过RecList 1.0的人来说,我们对RecList的API进行了重大更新。最初,我们对代码结构和输入/输出配对有更严格的限制。
实际上,当我们实现RecList时,我们的目标是为推荐系统的评估提供一个更通用的API,提供一些开箱即用的功能。然而,为了做到这一点,我们不得不创建几个抽象接口,供用户实现。
例如,原始的RecList 1.0要求用户将自己的模型和数据集封装到预定义的抽象类(即RecModel和RecDataset)中。这允许我们实现一组通过这些抽象连接的常见行为。然而,我们很快意识到,这可能常常使流程复杂化,并且需要很多额外的工作,一些人可能不喜欢。
在RecList 2.0中,我们决定将这些约束变为可选的:我们使测试更加灵活。用户可以定义自己的评估用例,将其包装在一个方便的装饰器中,已经实现了元数据存储和记录。然后用户可以与其他人分享测试接口,他们可以运行相同的实验。
RecList 2.0实际应用
现在,让我们来探索如何使用RecList编写和运行评估流程的简单用例。我们将使用非常简单的模型,在输出随机数字的情况下降低了机器学习项目的复杂性。
一个简单的用例
我们创建一个非常简单的用例,使用一个非常简单的数据集。假设我们有一个目标整数序列,每个整数都有一个关联的类别。我们只是简单地生成一些随机数据。
n = 10000
target = [randint(0, 1) for _ in range(n)]
metadata = {"categories": [choice(["red", "blue", "yellow"])
for _ in range(n)]}
我们非常简单的数据集应该类似于这样:
>>> target
[0, 1, 0, 1, 1, 0]
>>> metadata
{"categories" : ["red", "blue", "yellow", "blue", "yellow", "yellow"]}
一个简单的模型
现在,让我们假设我们有一个DummyModel,它在随机情况下输出整数。当然,正如我们之前说的,这并不是一个“好”的模型,但这是一个很好的抽象,我们可以用它来看到整个评估流程。
class DummyModel:
def __init__(self, n):
self.n = n
def predict(self):
from random import randint
return [randint(0, 1) for _ in range(self.n)]
simple_model = DummyModel(n)
# let's run some predictions
predictions = simple_model.predict()
那么,我们如何运行评估呢?
简单的RecList
RecList是一个Python类,继承了我们的RecList抽象类的功能。RecList实现了RecTests,这是一种简单的抽象,可以帮助您系统化地进行评估。例如,下面是一个可能的准确度测试。
@rec_test(test_type="Accuracy", display_type=CHART_TYPE.SCALAR)
def accuracy(self):
"""
Compute the accuracy
"""
from sklearn.metrics import accuracy_score
return accuracy_score(self.target, self.predictions)
我们正在使用sklearn的准确度度量标准,并将其封装在另一个方法中。这与简单的准确度函数有何不同呢?嗯,装饰器允许我们引入一些额外的功能:例如,rectest现在会自动将信息存储在本地文件夹中。另外,定义一个图表类型可以让我们为这些结果创建一些可视化。
如果我们想要一个更复杂的测试怎么办?例如,如果我们想要查看在不同的类别之间准确度的稳定性(例如,红色物体的准确度是否高于黄色物体)?
@rec_test(test_type="SlicedAccuracy", display_type=CHART_TYPE.SCALAR)
def sliced_accuracy_deviation(self):
"""
Compute the accuracy by slice
"""
from reclist.metrics.standard_metrics import accuracy_per_slice
return accuracy_per_slice(
self.target, self.predictions, self.metadata["categories"])
现在让我们来看一个完整的RecList的示例!
class BasicRecList(RecList):
def __init__(self, target, metadata, predictions, model_name, **kwargs):
super().__init__(model_name, **kwargs)
self.target = target
self.metadata = metadata
self.predictions = predictions
@rec_test(test_type="SlicedAccuracy", display_type=CHART_TYPE.SCALAR)
def sliced_accuracy_deviation(self):
"""
Compute the accuracy by slice
"""
from reclist.metrics.standard_metrics import accuracy_per_slice
return accuracy_per_slice(
self.target, self.predictions, self.metadata["categories"]
)
@rec_test(test_type="Accuracy", display_type=CHART_TYPE.SCALAR)
def accuracy(self):
"""
Compute the accuracy
"""
from sklearn.metrics import accuracy_score
return accuracy_score(self.target, self.predictions)
@rec_test(test_type="AccuracyByCountry", display_type=CHART_TYPE.BARS)
def accuracy_by_country(self):
"""
Compute the accuracy by country
"""
# TODO: note that is a static test,
# used to showcase the bin display
from random import randint
return {"US": randint(0, 100),
"CA": randint(0, 100),
"FR": randint(0, 100)}
只需要几行代码就可以将我们所需的一切放在一个地方。我们可以将这段代码重复使用于新的模型,或者添加测试并重新运行过去的模型。
只要您的指标返回一些值,您可以以任何您喜欢的方式实现它们。例如,这个BasicRecList在特定的上下文中评估特定的模型。但是,您完全可以生成更具体的模型reclist(例如,GPT-RecList)或数据集特定的reclist(例如,IMDB-Reclist)。
运行并获取输出
让我们运行RecList。我们需要目标数据、元数据和预测结果。我们还可以指定一个日志记录器和一个元数据存储。
rlist = BasicRecList(
target=target,
metadata=metadata,
predictions=predictions,
model_name="myRandomModel",
)
# run reclist
rlist(verbose=True)
这个过程的输出是什么呢?在命令行中,我们将看到以下一组结果:对于每个测试,我们都有一个实际的得分。
指标也会自动进行绘图。例如,AccuracyByCountry应该显示类似于这样的内容:
除此之外,RecList还保存了一个JSON文件,其中包含刚刚运行的所有实验的信息:
{
"metadata": {
"model_name": "myRandomModel",
"reclist": "BasicRecList",
"tests": [
"sliced_accuracy",
"accuracy",
"accuracy_by_country"
]
},
"data": [
{
"name": "SlicedAccuracy",
"description": "Compute the accuracy by slice",
"result": 0.00107123176804103,
"display_type": "CHART_TYPE.SCALAR"
},
...
}
令人兴奋的是,只需要几行额外的代码,我们就能自动处理大部分的日志记录!
使用在线日志记录器和元数据存储
默认情况下,RecList运行器将使用以下日志记录器和元数据设置。
logger=LOGGER.LOCAL,
metadata_store= METADATA_STORE.LOCAL,
然而,并没有阻止我们使用在线和云解决方案。例如,我们提供对CometML和Neptune API的包装,以便您可以直接在评估流程中使用它们。我们还支持S3数据存储。
例如,只需向BasicReclist添加几个参数,就可以在Neptune上记录信息(我们也为Comet.ml提供类似的支持)!
rlist = BasicRecList(
target=target,
model_name="myRandomModel",
predictions=predictions,
metadata=metadata,
logger=LOGGER.NEPTUNE,
metadata_store= METADATA_STORE.LOCAL,
NEPTUNE_KEY=os.environ["NEPTUNE_KEY"],
NEPTUNE_PROJECT_NAME=os.environ["NEPTUNE_PROJECT_NAME"],
)
# run reclist
rlist(verbose=True)
以非常类似的方式,只需添加以下内容:
bucket=os.environ["S3_BUCKET"]
这将使我们能够使用S3存储桶来存储元数据(当然,您仍然需要设置一些环境变量)。
结论
创建RecList是为了使推荐系统的评估更加系统化和有组织。希望这次大规模的API重构能帮助人们构建更可靠的评估流程!