使用 BigQuery Graph 进行欺诈检测

1. 简介

营销标题 - 金融技术领域的欺诈行为

欺诈活动通常涉及由关联实体组成的隐藏网络,例如多个账号共用同一电子邮件地址、电话号码或实际地址。传统的关联数据库可能难以高效查询这些复杂的多跳关系。

借助 BigQuery Graph,您可以使用图数据库大规模分析这些网络。您可以在现有 BigQuery 表的基础上定义属性图,并使用 Graph Query Language (GQL) 在数据中查找模式。

图网络在欺诈检测方面的一个常见应用是阻止与欺诈网络关联的配送地址的订单,或阻止属于欺诈网络的付款。

在此 Codelab 中,您将使用 BigQuery Graph 构建欺诈检测解决方案。您将从 Cloud Storage 加载数据,创建属性图,并使用图查询来识别可疑的关联。

学习内容

  • 如何创建 BigQuery 数据集并加载数据。
  • 如何使用 DDL 定义属性图表。
  • 如何使用 GQL 查询图。
  • 如何使用图分析来检测欺诈行为。

所需条件

  • 启用了结算功能的 Google Cloud 项目。
  • BigQuery 笔记本环境(BigQuery Studio 或 Colab Enterprise)。

费用

本实验使用可计费的 Google Cloud 资源。假设您在完成教程后删除资源,预计费用将低于 5 美元。

2. 准备工作

选择或创建 Google Cloud 项目

  1. 在 Google Cloud Console 的“项目选择器”页面上,选择或创建一个 Google Cloud 项目。
  2. 确保您的 Google Cloud 项目已启用结算功能。了解如何检查结算功能是否已启用

选择您的环境

您需要一个笔记本环境才能运行本实验。您可以使用 BigQuery StudioColab Enterprise

  1. 在 Google Cloud 控制台中前往 BigQuery 页面。
  2. 您将使用 Python 笔记本运行图查询。

启动 Cloud Shell

  1. 点击 Google Cloud 控制台顶部的激活 Cloud Shell
  2. 验证身份验证:
gcloud auth list
  1. 确认您的项目:
gcloud config get project
  1. 根据需要进行设置:
export PROJECT_ID=<YOUR_PROJECT_ID>
gcloud config set project $PROJECT_ID

启用 API

运行以下命令以启用所需的 BigQuery API:

gcloud services enable bigquery.googleapis.com

3. 加载数据

在此步骤中,您将创建一个 BigQuery 数据集并从 Cloud Storage 加载示例数据。

示例数据包含多个 CSV 文件,这些文件表示模拟的零售环境:

  • customers.csv:客户账号信息。
  • emails.csv:电子邮件地址。
  • phones.csv:电话号码。
  • addresses.csv:实际地址。
  • customer_emails.csvcustomer_phones.csvcustomer_addresses.csv:关联表。
  • orders.csv:订单历史记录,包括欺诈标记。

创建数据集

创建一个名为 fraud_demo 的数据集来保存表。

  1. 在此 Codelab 中,我们将执行 SQL 命令。您可以在 BigQuery Studio > SQL 编辑器中运行这些命令,也可以在 Cloud Shell 中使用 bq query 命令。新建 SQL 查询我们假设您使用的是 BigQuery SQL 编辑器,以便更好地体验多行创建语句。
CREATE SCHEMA IF NOT EXISTS `fraud_demo` OPTIONS(location="US");

加载表格

运行以下 SQL 语句,将数据从 Cloud Storage 加载到您的数据集中。

LOAD DATA OVERWRITE `fraud_demo.customers`
FROM FILES (
  format = 'CSV',
  uris = ['gs://sample-data-and-media/fraud-demo-data/customers.csv'],
  skip_leading_rows = 1
);

LOAD DATA OVERWRITE `fraud_demo.emails`
FROM FILES (
  format = 'CSV',
  uris = ['gs://sample-data-and-media/fraud-demo-data/emails.csv'],
  skip_leading_rows = 1
);

LOAD DATA OVERWRITE `fraud_demo.phones`
FROM FILES (
  format = 'CSV',
  uris = ['gs://sample-data-and-media/fraud-demo-data/phones.csv'],
  skip_leading_rows = 1
);

LOAD DATA OVERWRITE `fraud_demo.addresses`
FROM FILES (
  format = 'CSV',
  uris = ['gs://sample-data-and-media/fraud-demo-data/addresses.csv'],
  skip_leading_rows = 1
);

LOAD DATA OVERWRITE `fraud_demo.customer_emails`
FROM FILES (
  format = 'CSV',
  uris = ['gs://sample-data-and-media/fraud-demo-data/customer_emails.csv'],
  skip_leading_rows = 1
);

LOAD DATA OVERWRITE `fraud_demo.customer_phones`
FROM FILES (
  format = 'CSV',
  uris = ['gs://sample-data-and-media/fraud-demo-data/customer_phones.csv'],
  skip_leading_rows = 1
);

LOAD DATA OVERWRITE `fraud_demo.customer_addresses`
FROM FILES (
  format = 'CSV',
  uris = ['gs://sample-data-and-media/fraud-demo-data/customer_addresses.csv'],
  skip_leading_rows = 1
);

LOAD DATA OVERWRITE `fraud_demo.orders`
FROM FILES (
  format = 'CSV',
  uris = ['gs://sample-data-and-media/fraud-demo-data/orders.csv'],
  skip_leading_rows = 1
);

4. 创建属性图

现在,数据已加载完毕,您可以定义属性图了。属性图由节点(实体)和边(关系)组成。

在此实验中,节点如下:

  • 客户:表示账号持有人。
  • 电话:表示电话号码。
  • 电子邮件:表示电子邮件地址。
  • 地址:表示实际地址。

边包括:

  • OwnsPhone:将客户与电话相关联。
  • OwnsEmail:将客户与电子邮件地址相关联。
  • LinkedToAddress:将客户与地址相关联。

创建图表

运行以下 DDL 语句,在 fraud_demo 数据集中创建名为 FraudDemo 的图。

CREATE OR REPLACE PROPERTY GRAPH fraud_demo.FraudDemo
  NODE TABLES(
    fraud_demo.customers
      KEY(account_id)
      LABEL Customer PROPERTIES(
        account_id,
        name),

    fraud_demo.emails
      KEY(email)
      LABEL Email PROPERTIES(
        email,
        email_type),

    fraud_demo.phones
      KEY(phone_number)
      LABEL Phone PROPERTIES(
        phone_number,
        phone_type),

    fraud_demo.addresses
      KEY(address)
      LABEL Address PROPERTIES(
        address,
        address_type)
  )
  EDGE TABLES(
    fraud_demo.customer_emails
      KEY(account_id, email)
      SOURCE KEY(account_id) REFERENCES customers(account_id)
      DESTINATION KEY(email) REFERENCES emails(email)
      LABEL OwnsEmail PROPERTIES(
        account_id,
        email,
        last_updated_ts),

    fraud_demo.customer_phones
      KEY(account_id, phone_number)
      SOURCE KEY(account_id) REFERENCES customers(account_id)
      DESTINATION KEY(phone_number) REFERENCES phones(phone_number)
      LABEL OwnsPhone PROPERTIES(
        account_id,
        phone_number,
        last_updated_ts),

    fraud_demo.customer_addresses
      KEY(account_id, address)
      SOURCE KEY(account_id) REFERENCES customers(account_id)
      DESTINATION KEY(address) REFERENCES addresses(address)
      LABEL LinkedToAddress PROPERTIES(
        account_id,
        address,
        last_updated_ts)
  );

5. 分析网络(2 跳)

在 BigQuery Studio 中打开新笔记本

创建新笔记本

在本 Codelab 的可视化和推荐部分,我们将使用 BigQuery Studio 中的 Google Colab 笔记本。这样,我们就可以轻松直观地查看图表结果。

BigQuery 图笔记本以 IPython 魔法命令的形式实现。通过添加 %%bigquery magic 命令和 TO_JSON 函数,您可以直观呈现结果,如下几个部分所示。在此步骤中,您将运行图查询,以查找账号之间的简单关联。这是一个“2 跳”查询,因为它从起始节点出发,经过 2 跳来查找相关节点(例如,客户 -> 电子邮件 -> 客户)。

我们将首先调查 Nicole Wade 的账号。我们希望找到与她相关的所有账号(通过 2 跳)。

运行 2 跳查询

在笔记本中运行以下查询。

%%bigquery --graph
GRAPH fraud_demo.FraudDemo
MATCH 
 p=(a:Customer) 
  ( -[e:OwnsEmail|OwnsPhone|LinkedToAddress WHERE e.last_updated_ts < '2025-07-30']- (n) ){2}
WHERE a.account_id IN ("d2f1f992-d116-41b3-955b-6c76a3352657")
  -- Verify the final node in the hop array is a Customer
  AND 'Customer' IN UNNEST(LABELS(n[OFFSET(1)]))

RETURN TO_JSON(p) AS paths

了解结果

此查询执行以下操作:

  1. Customer 节点开始,以 account_id“d2f1f992-d116-41b3-955b-6c76a3352657”(Nicole Wade) 为根。
  2. 沿着任意一条边(OwnsEmailOwnsPhoneLinkedToAddress)到达连接节点(PhoneEmailAddress)。
  3. 从该连接节点沿边返回到其他 Customer 节点。
  4. 根据时间戳 (last_updated_ts) 过滤边,以查看特定时间的网络状态。

您应该会看到 Zachary CordovaBrenda Brown 通过同一地址与 Nicole 关联。

6. 分析网络(4 跳)

在此步骤中,您将扩展查询以查找更复杂的关系。我们将寻找四度人脉。这样一来,我们便可以找到通过多个中间实体(例如,客户 A -> 电子邮件地址 -> 客户 B -> 电话号码 -> 客户 C)关联的账号。

我们还将观察此网络随时间的变化。

“之前”状态

首先,我们来看看 2025 年 7 月 30 日的网络。

请运行以下查询:

%%bigquery --graph
%%bigquery --graph

MATCH p= ANY SHORTEST (a:Customer) 
  ( -[e:OwnsEmail|OwnsPhone|LinkedToAddress WHERE e.last_updated_ts < '2025-07-30']- (n) ){3, 5}(reachable_a:Customer)
WHERE a.account_id IN ("d2f1f992-d116-41b3-955b-6c76a3352657")
  -- Ensure the final node in the dynamic chain is actually a Customer
  AND 'Customer' IN UNNEST(LABELS(n[OFFSET(ARRAY_LENGTH(n) - 1)]))
GRAPH fraud_demo.FraudDemo
RETURN 
  TO_JSON(p) AS paths,                   -- Array of all traversed edges
  ARRAY_LENGTH(e) AS hop_count

“之后”状态

现在,我们来看看两周后的网络情况。我们将运行相同的查询,但不会添加日期限制。

请运行以下查询:

%%bigquery --graph
GRAPH fraud_demo.FraudDemo
MATCH p= ANY SHORTEST (a:Customer) 
  ( -[e:OwnsEmail|OwnsPhone|LinkedToAddress]- (n) ){3, 5}(reachable_a:Customer)
WHERE a.account_id IN ("d2f1f992-d116-41b3-955b-6c76a3352657")
  -- Ensure the final node in the dynamic chain is actually a Customer
  AND 'Customer' IN UNNEST(LABELS(n[OFFSET(ARRAY_LENGTH(n) - 1)]))

RETURN 
  TO_JSON(p) AS paths,                   -- Array of all traversed edges
  ARRAY_LENGTH(e) AS hop_count

了解结果

移除日期过滤条件后,您将针对完整的数据集进行查询。您会发现,该网络已显著扩大。妮可·韦德现在是规模更大、联系更紧密的群体的一员。关联网络的这种快速扩张强烈表明可能存在欺诈活动,例如欺诈团伙随时间推移共享资源。

7. 生成欺诈报告

在此步骤中,您将把图分析与商家数据(订单)相结合,生成全面的欺诈报告。您将能够识别存在风险的账号和潜在的欺诈性订单。

此查询更为复杂。它使用 GRAPH_TABLE 在标准 SQL 中运行图查询,并计算我们在上一步中观察到的“之前”和“之后”状态之间的网络规模变化 (diff)。

运行欺诈报告查询

在笔记本中运行以下查询。

%%bigquery --graph
WITH num_orders AS (
  SELECT account_id, COUNT(1) AS num_order
  FROM fraud_demo.orders
  WHERE order_time > '2025-07-30'
  GROUP BY account_id
),

orders AS (
  SELECT account_id, order_id, fraud, order_total
  FROM fraud_demo.orders
  WHERE order_time > '2025-07-30'
),

-- Use Quantified Path Patterns to find connections up to 4 hops away
latest_connect AS (
  SELECT 
    account_id, 
    ARRAY_LENGTH(ARRAY_AGG(DISTINCT connected_id)) AS size
  FROM GRAPH_TABLE(
    fraud_demo.FraudDemo
    MATCH (a:Customer)-[:OwnsEmail|OwnsPhone|LinkedToAddress]-{4}(connected:Customer)
    RETURN a.account_id AS account_id, connected.account_id AS connected_id
  )
  GROUP BY account_id
),

prev_connect AS (
  SELECT 
    account_id, 
    ARRAY_LENGTH(ARRAY_AGG(DISTINCT connected_id)) AS size
  FROM GRAPH_TABLE(
    fraud_demo.FraudDemo
    -- Apply the timestamp filter to EVERY edge in the 4-hop chain
    MATCH (a:Customer)
          (-[e:OwnsEmail|OwnsPhone|LinkedToAddress WHERE e.last_updated_ts < '2025-07-30']-(n)){4}
    WHERE 'Customer' IN UNNEST(LABELS(n[OFFSET(3)]))
    RETURN a.account_id AS account_id, n[OFFSET(3)].account_id AS connected_id
  )
  GROUP BY account_id
),

edge_changes AS (
  SELECT account_id, MAX(last_updated_ts) AS max_last_updated_ts
  FROM fraud_demo.customer_addresses
  GROUP BY account_id
)

SELECT
    la.account_id,
    o.order_id,
    la.size AS latest_size,
    COALESCE(pa.size, 0) AS previous_size,
    la.size - COALESCE(pa.size, 0) AS diff,
    nos.num_order,
    o.fraud AS reported_as_fraud,
    o.order_total,

    CASE
      WHEN (la.size - COALESCE(pa.size, 0)) > 10 AND nos.num_order IS NULL THEN "CUSTOMER AT RISK"
      WHEN (la.size - COALESCE(pa.size, 0)) > 10 AND nos.num_order IS NOT NULL AND o.fraud THEN "CONFIRMED FRAUD ORDER"
      WHEN (la.size - COALESCE(pa.size, 0)) > 10 AND nos.num_order IS NOT NULL AND NOT o.fraud THEN "POTENTIAL FRAUD ORDER"
      ELSE ""
    END AS notes
FROM latest_connect la
LEFT JOIN prev_connect pa ON la.account_id = pa.account_id
LEFT JOIN num_orders nos ON la.account_id = nos.account_id
LEFT JOIN orders o ON la.account_id = o.account_id
INNER JOIN edge_changes ec ON la.account_id = ec.account_id
WHERE nos.num_order > 1 OR (la.size - COALESCE(pa.size, 0)) > 10
ORDER BY diff DESC

了解结果

此报告会显示以下内容:

  • account_id:正在分析的账号的 ID。
  • order_id:最近的订单 ID。
  • latest_size:当前已连接的网络的大小。
  • previous_size:2 周前的网络规模。
  • diff:网络规模的增长。
  • num_order:近期的订单数量。
  • reported_as_fraud:相应订单是否已被标记为欺诈性订单。
  • order_total:订单总金额。
  • notes:根据网络增长和订单历史记录计算得出的风险状态。

您会看到 diff 值较大且订单总金额较高的账号,这些账号非常适合进一步调查。“客户存在风险”和“潜在欺诈性订单”备注有助于确定这些账号的优先级。

8. 大规模检测

在最后这一分析步骤中,您将以更大的规模直观呈现网络。您将查询一组可疑账号之间的关联,而不是从单个账号开始。

这有助于您了解多项独立调查是否实际上属于同一大型欺诈团伙。

运行缩放查询

在笔记本中运行以下查询。

%%bigquery --graph
GRAPH fraud_demo.FraudDemo
MATCH
p= ANY SHORTEST (a:Customer) 
  ( -[e:OwnsEmail|OwnsPhone|LinkedToAddress]- (n) ){3, 5}
(reachable_a:Customer)
-- these IDs are from the previous results
WHERE a.account_id in (   "845f2b14-cd10-4750-9f28-fe542c4a731b"
  , "3ff59684-fbf9-40d7-8c41-285ade5002e6"
  , "8887c17b-e6fb-4b3b-8c62-cb721aafd028"
  , "03e777e5-6fb4-445d-b48c-cf42b7620874"
  , "81629832-eb1d-4a0e-86da-81a198604898"
  , "845f2b14-cd10-4750-9f28-fe542c4a731b",
   "89e9a8fe-ffc4-44eb-8693-a711a3534849"
 )
 LIMIT 400
RETURN TO_JSON(p) as paths

了解结果

此查询会返回一个复杂图表,显示指定的可疑账号如何重叠和共享资源。您现在看到的是大规模欺诈检测,可识别可能需要协调响应的活动集群。

9. 清理

为避免系统因本 Codelab 中使用的资源向您的 Google Cloud 账号收取费用,您应该删除数据集和属性图。

运行以下 SQL 语句以清理您的环境。

DROP PROPERTY GRAPH IF EXISTS fraud_demo.FraudDemo;
DROP SCHEMA IF EXISTS fraud_demo CASCADE;

10. 恭喜

恭喜!您已成功使用 BigQuery Graph 构建了欺诈检测解决方案。

您已了解如何:

  • 将数据从 Cloud Storage 加载到 BigQuery。
  • 使用 DDL 定义属性图。
  • 使用 GQL 查询图,以查找简单关系和复杂关系。
  • 将图分析与商家数据相结合,以识别风险。
  • 大规模直观呈现网络。

更多资源