移动广告反欺诈实战:如何量化并提升 IP 情报库与 AppsFlyer Protect360 判定的一致性
## 背景 做移动效果投放的团队,大多会同时依赖两套信号做反作弊: - **AppsFlyer Protect360(P360)**:基于全网归因生态的判定结果,作为渠道结算的"官方口径"; - **自建 IP 情报库**:第三方 IP 归属、代理/VPN 标记、数据中心识别等静态属性,用于内部风控和业务侧拦截。 实际工作中经常遇到的问题是:P360 判了假量,查自己的 IP 库却发现 IP 是"干净住宅宽带";或者库标了代理,P360 却判为有效安装。分歧到底该信谁?匹配度低是库的问题还是口径的问题? 本文分享一套我们在实践中沉淀的评估与优化流程,核心观点是:**两者不可能也不需要 100% 一致,目标是让分歧可解释、可量化、可处置**。 ## 一、先理解分歧的来源:两边看的不是同一个东西 把两个系统当成"同一个裁判的两次判罚"是认知误区。 P360 的 IP 相关判定是行为信号与生态信号的叠加:已知高风险 IP 名单(来自全网归因数据积累)、同网段激活的聚集异常、IP 与设备其他维度(地理、时区、指纹)的矛盾等。 而自建 IP 库输出的是静态属性:IP 类型(数据中心/住宅/移动网络)、代理标记、地理位置、ASN。 因此典型的"不一致"有两种合理解释: - P360 判假但库显示正常:作弊方使用了住宅代理,单看 IP 属性完全正常,P360 靠的是聚集性和生态黑名单; - 库标代理但 P360 判真:真实用户挂 VPN 产生了正常行为,IP 属性不等于作弊。 接受这一点之后,剩下的工作就是把两类问题分开:**数据对齐问题**(不该有的不一致)和**信号互补问题**(合理的不一致)。 ## 二、数据对齐:排除"伪不一致" ### 1. IP 归一化 Dual-stack 环境下同一用户会同时携带 IPv4 和 IPv6 地址,比对前必须做归一(6to4/Teredo 映射、格式统一、去端口、去前导零)。CSV 导出时尤其注意 Excel 打开可能导致前导零丢失,建议全程用 Python/pandas 处理。 ### 2. Join Key 与 ID 可用性 跨表关联优先用 GAID;注意部分流量 IDFA/GAID 缺失,需要 IMEI/OAID 或设备指纹兜底,否则"对不上"会被误算成"不一致"。 ### 3. 时间窗口对齐 P360 的归因后识别存在回流滞后:安装先计为有效,之后才被判假更新。直接用实时数据比对,会把"尚未判"误判为"漏判"。建议: - 通过 Data Locker 按天拉全量回传数据; - 设 7~14 天观察窗口,窗口内的判定视为终态再参与比对; - IP 库查询时记录库版本和时间戳,住宅代理池轮换极快,用当前库回查一个月前的 IP 没有意义。 ### 4. 库自身的覆盖质量 匹配度上限由库决定。东南亚、中东、拉美部分运营商的 IP 段归属更新滞后,需要确认所用数据源在目标 GEO 的覆盖度。业界常用的 IP 情报数据源有 Digital Element、IP2Location 等,数据口径和更新频率各有侧重,关键场景建议交叉使用两家以上,避免单源误判。如果比对分歧集中在特定国家/运营商,多半是区域性覆盖问题,而不是系统性问题。 ## 三、量化评估:用混淆矩阵管理匹配度 凭感觉说"匹配度不高"没有操作价值,用混淆矩阵把它量化。以 P360 判定为基准标签,IP 库输出为预测: ||P360 判假|P360 判真| | --- | --- | --- | |库标风险|一致命中|库的误报 → 核查库质量| | --- | --- | --- | |库标正常|P360 独有信号 → 重点分析|一致| 四个象限对应四种动作: - **左上(一致判假)**:正常区间,持续监控占比即可; - **右上(库标风险、P360 判真)**:检查是否为真实用户挂 VPN 的误报,是的话在本方风控中对白名单场景放行;若集中在某网段则反馈给数据厂商修正; - **左下(P360 判假、库标正常)**:价值最高的一档。逐批核查是否住宅代理(换情报源交叉验证),或是否聚集性作弊但单 IP 属性正常。若这一档占比持续偏高,说明缺的是行为维度能力,而不是 IP 维度; - **右下**:无需处理。 跟踪两个指标: - **一致率 = (左上+右下) / 总样本**,按月出报表; - **分歧集中度**:分歧若集中在少数 GEO/渠道/网段,是覆盖问题,定向修复即可;若高度分散,是口径或流程问题,需要系统性排查。 ## 四、代码实现:一个简单的评估流程 假设已经从 Data Locker 拿到 P360 原始数据(含 IP、判定原因、时间戳),并有 IP 库的离线查询接口: ``` import pandas as pd # 1. 加载 P360 归因后识别数据,并做时间窗口过滤 df = pd.read_csv("p360_post_attrib.csv") df = df[df["days_since_install"] >= 14] # 等待判定回流 # 2. IP 归一化 df["ip_norm"] = df["IP"].str.strip().str.lower() # 3. 批量查询 IP 库(示意:接本地 Redis 部署的离线库) df["ip_label"] = df["ip_norm"].map(query_ip_intel) # 返回 risk / normal # 4. 构建混淆矩阵 matrix = pd.crosstab( df["p360_verdict"].map({1: "p360_fraud", 0: "p360_valid"}), df["ip_label"].map({"risk": "lib_risk", "normal": "lib_normal"}) ) print(matrix) print("一致率:", (matrix.loc["p360_fraud", "lib_risk"] + matrix.loc["p360_valid", "lib_normal"]) / matrix.values.sum()) ``` ### 5. 分歧下钻 # 左下象限:P360 判假但库标正常 —— 逐 C 段分析 gap = df[(df["p360_verdict"] == 1) & (df["ip_label"] == "normal")].copy() gap["ip_c"] = gap["ip_norm"].str.rsplit(".", n=1).str[0] gap.groupby("ip_c").size().sort_values(ascending=False).head(20) 下钻结果通常能直接看出:是少数几个 C 段高度聚集(设备农场,走本地黑名单沉淀),还是大面积分散(住宅代理或库覆盖问题)。 ## 五、反馈闭环:让两套信号互相校准 **P360 → IP 库**:左下象限验证过的 IP 段沉淀进本地黑名单,分两层——证据充分(聚集性 + 多源交叉)进硬黑名单,业务侧直接拦截;证据单薄进灰名单降权观察。 **IP 库 → P360**:对高置信的本地黑名单网段,可通过 AppsFlyer 的 Validation Rules 配置前置拦截,减少"先归因、后判假"带来的回滚损耗。 **误伤处理**:小众运营商或企业 NAT 出口(一个出口 IP 对应大量真实用户)容易触发 IP 复用类判定。遇到系统性误伤,向 AppsFlyer 支持提交证据(ISP 属性、该 IP 下的真实用户行为数据),并在本方评估模型中对这类 GEO 临时放宽聚合阈值。 ## 六、落地优先级 按投入产出比排序: 1. **先量化**:没有混淆矩阵,一切都是感觉; 1. **再查对齐**:归一化、join key、时间窗口是最高频的"伪不一致"来源; 1. **最后补数据**:分歧确实由覆盖不足导致的,再考虑增加情报源或更换数据源。 最后提醒一点:IP 库和 P360 的关系不是替代,而是互补——静态属性负责"广谱筛查",行为判定负责"精确打击",本地黑名单是两者的交集沉淀。把这三层各自的边界想清楚,比追求单一指标的匹配度更有意义。