paper2skills Playbook

Multi-Touch Attribution Modeling for Digital Advertising

Skill-Ad-Attribution-Modeling · 13-广告分析

causalexperiment广告与投放MAS与智能体工程WF-B 广告优化WF-G Listing内容优化
年化 ROI10万
实现难度⭐⭐⭐☆☆
业务优先级⭐⭐⭐⭐⭐
业务视角
适用角色广告优化师 / 投放负责人 · CMO · 运营负责人
适用平台Amazon PPC(SP/SB/SD)· TikTok Ads · Meta 广告 · 多平台归因
什么情况下用广告账户几十个系列,不知道哪个在真正赚钱;ROAS 看起来好看但实际利润没有提升;预算有限想集中打高价值用户
成功是什么样的每分广告预算有明确 ROI 追踪,砍掉低效渠道后同等预算 ROAS 提升 30-50%
业务痛点
ROAS 好看但利润没有涨不知道哪个素材真的有效归因窗口期不同数据打架TikTok/Meta/Amazon 广告数据整合不了

1. 解决的问题

用户从第一次看到广告到最终下单,平均接触5-7个触点(Facebook视频、Google搜索、TikTok短视频、再营销广告、邮件)。

2. 核心算法逻辑

核心问题:用户从第一次看到广告到最终下单,平均接触57个触点(Facebook视频、Google搜索、TikTok短视频、再营销广告、邮件)。哪个触点真正促成了转化?最后点击(LastClick)模型把功劳全给最后一个触点,严重低估了上层漏斗的价值。

3. 业务应用场景

业务问题:Momcozy 月广告预算50万,分配为Facebook 30万、Google 15万、TikTok 5万。但Last-Click归因显示Google贡献60%转化,Facebook只有25%。团队想砍掉Facebook预算加到Google——这是Last-Click的陷阱。

数据驱动归因分析: 1. 收集用户旅程数据:每个转化的完整触点序列 2. Shapley值计算: - Facebook:边际贡献 38% - Google:边际贡献 32% - TikTok:边际贡献 18% - Email:边际贡献 12% 3. 与Last-Click对比: - Facebook:Last-Click 25% → Shapley 38%(被低估!) - Google:Last-Click 60% → Shapley 32%(被高估!)

决策变化: - 原方案:Facebook 30万 → Google 45万 - 修正后:Facebook 35万、Google 25万、TikTok 15万、Email 5万 - 预期效果:整体ROAS从2.5提升到3.2

4. 输入数据要求

请查看原始代码模板获取输入规格。

5. 输出结果

请查看原始代码模板获取输出规格。

6. 业务价值 / ROI

  • ROI:预算重新分配后ROAS提升20-40%,年节省浪费预算10万+
  • 难度:⭐⭐⭐☆☆(3/5)— Shapley计算复杂,但规则模型简单
  • 优先级:⭐⭐⭐⭐⭐(5/5)— 广告预算分配的前提,没有归因就没有优化

7. 代码模板

代码块数量:1 · 路径:未检测到

"""
Ad Attribution Modeling — 广告归因模型
支持:规则归因、Shapley值、马尔可夫链、移除效应
"""

import numpy as np
import pandas as pd
from itertools import combinations
from collections import defaultdict, Counter


class AttributionModel:
    """广告归因模型"""

    def __init__(self, journeys, conversions):
        """
        Args:
            journeys: list of lists, each inner list is touchpoint sequence
            conversions: list of 0/1, whether each journey converted
        """
        self.journeys = journeys
        self.conversions = conversions
        self.channels = sorted(set(c for j in journeys for c in j))

    def last_click(self):
        """Last-Click归因"""
        attribution = Counter()
        for journey, conv in zip(self.journeys, self.conversions):
            if conv and journey:
                attribution[journey[-1]] += 1
        return dict(attribution)

    def first_click(self):
        """First-Click归因"""
        attribution = Counter()
        for journey, conv in zip(self.journeys, self.conversions):
            if conv and journey:
                attribution[journey[0]] += 1
        return dict(attribution)

    def linear(self):
        """Linear归因"""
        attribution = Counter()
        for journey, conv in zip(self.journeys, self.conversions):
            if conv and journey:
                for ch in journey:
                    attribution[ch] += 1 / len(journey)
        return dict(attribution)

    def position_based(self, first_weight=0.4, last_weight=0.4):
        """Position-Based归因"""
        attribution = Counter()
        for journey, conv in zip(self.journeys, self.conversions):
            if not conv or not journey:
                continue
            n = len(journey)
            if n == 1:
                attribution[journey[0]] += 1
            elif n == 2:
                attribution[journey[0]] += first_weight

8. 论文来源

未自动抽取;请查看原始 Skill 卡片。