当前位置: 首页 > news >正文

auc代码手撕

你好!祝你求职顺利。这段代码非常经典,“手写 AUC”是推荐系统、计算广告以及机器学习算法工程师面试中的绝对高频考点

面试官让你写这段代码,不仅仅是为了看你会不会写循环,主要是想考察你对 AUC 物理意义的理解以及时间复杂度优化的能力。

下面我结合面试场景,为你通俗易懂地逐行拆解这段代码。

💡 核心前提:AUC 的物理意义是什么?

在讲解代码前,你必须记住一句话(面试时直接这么答):

“AUC 的本质是:随机抽取一个正样本和一个负样本,正样本的预测得分大于负样本预测得分的概率。”

所以,计算 AUC 其实就是在计算:正样本得分 > 负样本得分的“对数”,然后除以总对数


📝 逐行代码拆解

1. 准备阶段:把连续分数变成“分桶”

def my_auc(labels, preds, n_bins=100000):neg_his = [0]*n_bins  # 负样本直方图(桶)pos_his = [0]*n_bins  # 正样本直方图(桶)n_pos = 0             # 正样本总数n_neg = 0             # 负样本总数
  • 解释:这里使用了直方图法(分桶法)。通常的概率预测值在 0.01.0 之间。我们将这个区间切分成 100,000 个小桶。pos_hisneg_his 就像两排柜子,用来分别记录各个分数段里有多少个正样本和负样本。

2. 装桶阶段:统计各个分数段的样本分布

    for i in range(len(labels)):cur = int(preds[i]*n_bins)  # 核心:计算当前样本该进哪个桶if labels[i] == 0:neg_his[cur] += 1       # 负样本进对应的负样本桶n_neg += 1else:pos_his[cur] += 1       # 正样本进对应的正样本桶n_pos += 1
  • 解释:遍历所有样本。int(preds[i]*n_bins) 的作用是将小数分数映射到整数桶编号上。例如,预测分数为 0.8,就会被放进第 80000 号桶(0.8 * 100000)。通过这个循环,我们统计出了每个极其微小的分数段内,正样本和负样本的数量,同时也得到了正负样本的总数。

3. 计算组合总数(分母)

    total = n_neg * n_pos
  • 解释:所有的正样本和负样本可以组合成多少对?当然是相乘。这就是我们最终算概率的分母

4. 核心计算阶段:统计“正样本分数 > 负样本分数”的对数(分子)

    sum_neg = 0  # 累计遇到的负样本数量sum_auc = 0  # 累计符合条件的“正>负”样本对数(分子)for i in range(n_bins):# 统计当前桶里的正样本,能和之前/当前的负样本组成多少符合条件的对sum_auc += (sum_neg*pos_his[i] + pos_his[i]*neg_his[i]*0.5)# 将当前桶的负样本数量,加入到累计负样本中,供后面更高分数的正样本使用sum_neg += neg_his[i] 
  • 解释:这是整段代码最精妙的地方。我们从低分(0号桶)到高分(99999号桶)开始遍历。
    • sum_neg * pos_his[i]:因为我们是从低分往高分遍历,所以sum_neg代表分数严格比当前桶低的所有负样本总数。当前桶里如果有正样本(pos_his[i]个),那么这些正样本的分数一定大于这 sum_neg 个负样本。所以构成了 sum_neg * pos_his[i] 对正确的组合。
    • pos_his[i] * neg_his[i] * 0.5:如果正样本和负样本落在了同一个桶里(分数极其接近),谁大谁小呢?我们近似认为它们互有胜负,也就是有 50% 的概率正样本排在前面,所以算作半对,即乘以 0.5
    • sum_neg += neg_his[i]:处理完当前桶后,把当前桶里的负样本也加到 sum_neg 里,因为对于下一个更高分数的桶来说,当前桶的负样本也是属于“分数比较低”的。

5. 返回结果

    return sum_auc/total
  • 解释:将符合条件的对数(分子)除以总对数(分母),得到最终的 AUC 概率值。

🌟 面试加分项(一定要主动和面试官提)

如果面试官问:“为什么要用分桶法来写,而不直接用排序?”你可以这样回答:

  1. 时间复杂度的跨越:如果是传统的方法(把所有样本按照预测分数排序,然后计算 Rank 值),排序的时间复杂度是 \(O(N \log N)\)。而这种“分桶/直方图法”,无论有多少个样本,都只需要遍历两次数组(一次装桶,一次算分),时间复杂度被硬生生降到了 \(O(N)\)
  2. 空间复杂度:由于桶的数量 n_bins 是固定的常量(如 100,000),因此空间复杂度是 \(O(1)\)
  3. 工业界实用性:在实际的搜推广业务中,测试集动辄几亿、几十亿个请求(\(N\) 极大)。如果是 \(O(N \log N)\) 的排序算法,集群根本跑不动。由于机器预估的分数精度通常也就是几位小数,使用 \(O(N)\) 的分桶法(比如切分10万个桶)能以极低的性能开销得到极其近似的真实 AUC 值,这在工业界非常有价值。

(小提示:截图中第 21 行和第 22 行的测试数据里,labels 是 8 个元素,但 preds 是 9 个元素,两边长度不一致跑起来会报错,你在自己电脑上测试时记得补齐哦。)

http://www.jsqmd.com/news/650298/

相关文章:

  • Ubuntu 20.04 LTS 安装NVIDIA驱动,手把手教你搞定那个烦人的蓝色MOK管理界面
  • Driver Store Explorer完整指南:Windows驱动存储区管理神器
  • 如何配置Navicat试用期重置脚本实现Mac数据库工具无限使用
  • 预算有限必看:COD消解仪高性价比品牌推荐 - 品牌推荐大师
  • Figma设计文件与JSON双向转换的终极解决方案:打破设计与开发的数据壁垒
  • 胡桃讲编程:混音教学第三步|AI 翻唱实操:软件 + 模型 + 索引全安装(全链接无遗漏・老本专属)
  • 天津婚姻纠纷律师 姜春梅:以法为盾以情为桥 守护津门家庭安宁|咨询热线 400-0073-869 - 外贸老黄
  • 从零到一:在vspm1.0原型机上实现除法运算的探索与思考
  • 你的智能硬件还只能‘哔哔’响?试试用ESP32和minimp3做个网络电台或语音提示器
  • 别再让表格撑爆你的LaTeX文档了!tabularx + X列类型保姆级教程
  • 告别迷茫!C#连接三菱PLC的两种方式(逻辑站 vs IP直连)保姆级对比与选择指南
  • K-Means聚类算法完整指南:从原理到实战
  • AI为何不能代替真人写作,毕竟还是仅仅是传递
  • 2026国产企业龙虾工具哪家比较好?推荐这款开源高效智能体平台 - 品牌2025
  • AI为何不能代替真人写作,说教再多毕竟也没有改变现实社会
  • 闲置京东e卡别浪费!3招轻松“盘活”,加入“可可收”更省心 - 可可收
  • 高精度vs高性价比?余氯仪十大品牌选购终极攻略 - 陈工日常
  • 跨越架构鸿沟:ARM平台Kettle ETL部署实战避坑指南
  • 【QGIS实战篇】QGIS 3.40 栅格计算器:从公式到场景的完整工作流
  • 2026年惠山区正规的代办营业执照公司推荐,注册公司/资质代办/代办公司/代办营业执照/公司注册,代办营业执照公司选哪家 - 品牌推荐师
  • MATLAB与STK互联实战:自动化构建Walker星座的完整指南
  • 解决Termux中lxml安装问题的实例详解
  • 2026年管式炉行业品牌综合排行:优质厂商实力与口碑全梳理 - 品牌推荐大师1
  • 保姆级教程:从零开始为你的STM32智能车设计一块‘靠谱’的供电底板(含LM2596/LM2587选型)
  • 3大技术突破:深度解析Common Voice 25.0数据集架构与高性能应用
  • foobar2000歌词插件OpenLyrics完整指南:打造终极音乐播放体验
  • 2026高安全性OpenClaw替代工具怎么选?推荐企业级智能体 - 品牌2025
  • 从‘特斯拉线圈’到‘家庭插座’:聊聊交流电系统中‘地线’的前世今生与关键作用
  • mysql如何处理由于网络抖动导致的复制断开_mysql重试机制配置
  • 余氯仪品牌怎么选?十大品牌优缺点全解析,避坑指南 - 陈工日常