当前位置:首页|资讯|深度学习|机器学习

基于关系型深度学习的自助机器学习

作者:51CTO发布时间:2024-11-19

本文将探讨直接在关系数据库上执行机器学习的新方法——关系型深度学习。

本文示例项目数据集的关系模式(作者提供图片)

在本文中,我们将深入探讨一种有趣的深度学习(DL)新方法,称为关系型深度学习(RDL)。我们还将通过在一家电子商务公司的真实数据库(不是数据集!)上做一些RDL来获得一些实践经验。

简介

在现实世界中,我们通常有一个关系数据库,我们想在这个数据库上运行一些机器学习任务。但是,有时候数据库需要高度规范化;这意味着,大量耗时的特征工程和粒度损失,因为我们必须进行大量的聚合操作。更重要的是,我们可以构建无数种可能的特征组合,每种组合都可能产生良好的性能(【文献2】)。这意味着,我们可能会在数据库表格中留下一些与ML任务相关的信息。

这类似于计算机视觉的早期,在深度神经网络出现之前,特征工程任务是基于像素值形式手工完成的。如今,模型直接使用原始像素,而不再依赖于这个中间环节。

关系型深度学习

关系型深度学习(RDL)承诺用表格形式学习实现同样的事情。也就是说,它消除了通过直接在关系数据库上学习来构建特征矩阵的额外步骤。RDL通过将数据库及其关系转换为图来实现这一点;其中,表中的一行成为节点,表之间的关系成为边,行值作为节点特征存储在节点内。

在本文中,我们将使用Kaggle的电子商务数据集,该数据集包含有关星形模式中电子商务平台的交易数据,其中包含一个核心事实表(交易)和一些维度表。完整的代码可以在链接处的笔记本文件中找到。

在本文中,我们将使用relbench库来执行RDL。在relbench中,我们必须做的第一件事是指定关系数据库的模式。下面给出一个示例,说明我们如何对数据库中的“事务”表执行此操作。我们将表作为pandas数据帧给出,并指定主键和时间戳列。主键列用于唯一标识实体。时间戳确保我们只能在预测未来交易时从过去的交易中学习。在这种构图中,这意味着信息只能从时间戳较低的节点(即过去)流向时间戳较高的节点。此外,我们指定关系中存在的外键。在这种情况下,事务表具有列“customer_key”,该列是指向“customer_dim”表的外键。

复制

tables['transactions'] = Table(

df=pd.DataFrame(t),

pkey_col='t_id',

fkey_col_to_pkey_table={

'customer_key': 'customers',

'item_key': 'products',

'store_key': 'stores'

},

time_col='date'

其余的表需要以相同的方式定义。请注意,如果你已经有了数据库模式,这也可以通过自动化的方式实现。由于数据集来自Kaggle,所以我需要手动创建模式。我们还需要将日期列转换为实际的pandas日期时间对象,并删除任何NaN值。

复制

class EcommerceDataBase(Dataset):

#创建你自己的数据集的示例:https://github.com/snap-stanford/relbench/blob/main/tutorials/custom_dataset.ipynb

val_timestamp = pd.Timestamp(year=2018, month=1, day=1)

test_timestamp = pd.Timestamp(year=2020, month=1, day=1)

def make_db(self) -> Database:

tables = {}

customers = load_csv_to_db(BASE_DIR + '/customer_dim.csv').drop(columns=['contact_no', 'nid']).rename(columns={'coustomer_key': 'customer_key'})

stores = load_csv_to_db(BASE_DIR + '/store_dim.csv').drop(columns=['upazila'])

products = load_csv_to_db(BASE_DIR + '/item_dim.csv')

transactions = load_csv_to_db(BASE_DIR + '/fact_table.csv').rename(columns={'coustomer_key': 'customer_key'})

times = load_csv_to_db(BASE_DIR + '/time_dim.csv')

t = transactions.merge(times[['time_key', 'date']], on='time_key').drop(columns=['payment_key', 'time_key', 'unit'])

t['date'] = pd.to_datetime(t.date)

t = t.reset_index().rename(columns={'index': 't_id'})

t['quantity'] = t.quantity.astype(int)

t['unit_price'] = t.unit_price.astype(float)

products['unit_price'] = products.unit_price.astype(float)

t['total_price'] = t.total_price.astype(float)

print(t.isna().sum(axis=0))

print(products.isna().sum(axis=0))

print(stores.isna().sum(axis=0))

print(customers.isna().sum(axis=0))

tables['products'] = Table(

df=pd.DataFrame(products),

pkey_col='item_key',

fkey_col_to_pkey_table={},

time_col=None

tables['customers'] = Table(

df=pd.DataFrame(customers),

pkey_col='customer_key',

fkey_col_to_pkey_table={},

time_col=None

tables['transactions'] = Table(

df=pd.DataFrame(t),

pkey_col='t_id',

fkey_col_to_pkey_table={

'customer_key': 'customers',

'item_key': 'products',

'store_key': 'stores'

},

time_col='date'

tables['stores'] = Table(

df=pd.DataFrame(stores),

pkey_col='store_key',

fkey_col_to_pkey_table={}

return Database(tables)

至关重要的是,作者引入了训练表的概念。这个训练表基本上定义了ML任务。这里的想法是,我们想预测数据库中某个实体的未来状态(即未来值)。我们通过指定一个表来实现这一点,其中每一行都有一个时间戳、实体的标识符和我们想要预测的一些值。id用于指定实体,时间戳指定我们需要预测实体的时间点。这也将限制可用于推断此实体值的数据(即仅过去的数据)。值本身就是我们想要预测的(即真实数据值)。

就我们而言,我们有一个与客户互动的在线平台。我们希望预测客户在未来30天内的收入。我们可以使用DuckDB执行的SQL语句创建训练表。这是RDL的一大优势,因为我们可以仅使用SQL创建任何类型的ML任务。例如,我们可以定义一个查询来选择未来30天内买家的购买数量,以进行流失预测。

复制

df = duckdb.sql(f"""

select

timestamp,

customer_key,

sum(total_price) as revenue

from

timestamp_df t

left join

transactions ta

on

ta.date <= t.timestamp + INTERVAL '{self.timedelta}'

and ta.date > t.timestamp

group by timestamp, customer_key

""").df().dropna()

结果将是一个数据库表格,其中seller_id是我们想要预测的实体的关键字,收入是目标,时间戳是我们需要进行预测的时间(即我们只能使用到目前为止的数据进行预测)。

训练表(作者提供图片)

下面是创建“customer_venue”任务的完整代码。

复制

class CustomerRevenueTask(EntityTask):

# 自定义任务示例:https://github.com/snap-stanford/relbench/blob/main/tutorials/custom_task.ipynb

task_type = TaskType.REGRESSION

entity_col = "customer_key"

entity_table = "customers"

time_col = "timestamp"

target_col = "revenue"

timedelta = pd.Timedelta(days=30) # 我们想要预测未来的收入。

metrics = [r2, mae]

num_eval_timestamps = 40

def make_table(self, db: Database, timestamps: "pd.Series[pd.Timestamp]") -> Table:

timestamp_df = pd.DataFrame({"timestamp": timestamps})

transactions = db.table_dict["transactions"].df

df = duckdb.sql(f"""

select

timestamp,

customer_key,

sum(total_price) as revenue

from

timestamp_df t

left join

transactions ta

on

ta.date <= t.timestamp + INTERVAL '{self.timedelta}'

and ta.date > t.timestamp

group by timestamp, customer_key

""").df().dropna()

print(df)

return Table(

df=df,

fkey_col_to_pkey_table={self.entity_col: self.entity_table},

pkey_col=None,

time_col=self.time_col,

至此,我们已经完成了大部分工作。其余的工作流程都是类似的,独立于机器学习任务。我能够从relbench提供的示例笔记本文件中复制大部分代码。

例如,我们需要对节点特征进行编码。在这里,我们可以使用GloVe嵌入(【译者注】个别网文中翻译为“手套嵌入”)来编码所有文本特征,如产品描述和产品名称。

复制

from typing import List, Optional

from sentence_transformers import SentenceTransformer

from torch import Tensor

class GloveTextEmbedding:

def __init__(self, device: Optional[torch.device

] = None):

self.model = SentenceTransformer(

"sentence-transformers/average_word_embeddings_glove.6B.300d",

device=device,

def __call__(self, sentences: List[str]) -> Tensor:

return torch.from_numpy(self.model.encode(sentences))

之后,我们可以将这些转换应用于我们的数据并构建图表。

复制

from torch_frame.config.text_embedder import TextEmbedderConfig

from relbench.modeling.graph import make_pkey_fkey_graph

text_embedder_cfg = TextEmbedderConfig(

text_embedder=GloveTextEmbedding(device=device), batch_size=256

data, col_stats_dict = make_pkey_fkey_graph(

db,

col_to_stype_dict=col_to_stype_dict, # speficied column types

text_embedder_cfg=text_embedder_cfg, # our chosen text encoder

cache_dir=os.path.join(

root_dir, f"rel-ecomm_materialized_cache"

), # store materialized graph for convenience

其余的代码将从标准层构建GNN(图神经网络),对循环训练进行编码,并进行一些评估。为了简单起见,我将把这段代码从本文中删除,因为它非常标准,在各个任务中都是一样的。你可以在链接https://github.com/LaurinBrechter/GraphTheory/tree/main/rdl处查看对应的笔记本文件。

训练结果(作者提供图片)

因此,我们可以训练这个GNN,使其r2达到0.3左右,MAE达到500。这意味着,它预测卖家在未来30天的收入,平均误差为+-500美元。当然,我们不知道这是好是坏,也许通过经典机器学习和特征工程的结合,我们可以得到80%的r2。

结论

关系型深度学习是一种有趣的机器学习新方法,特别是当我们有一个复杂的关系模式时,手动特征工程太费力了。它使我们能够仅使用SQL定义ML任务,这对于那些不深入研究数据科学但仅了解一些SQL的人来说尤其有用。这也意味着,我们可以快速迭代,并对不同的任务进行大量实验。

同时,这种方法也存在自己的问题,例如训练GNN和从关系模式构建图存在不少困难。此外,还有一个问题是,RDL在性能方面能在多大程度上与经典ML模型竞争。过去,我们已经看到,在表格预测问题上,XGboost等模型已被证明比神经网络更好。


Copyright © 2024 aigcdaily.cn  北京智识时代科技有限公司  版权所有  京ICP备2023006237号-1