1. 什么是数据科学中的实验设计不是“做实验”而是“让数据自己开口说话”你有没有遇到过这种情况花两周时间训练了一个看起来很漂亮的模型AUC高达0.92结果一上线效果还不如上个月用Excel手动分组的规则或者业务方急吼吼地要你“验证一下新推荐策略是否有效”你拉了一周AB测试数据发现新策略点击率高了0.3%p值0.048——刚想发喜报却发现对照组里混进了大量刚注册的新用户而实验组全是老用户又或者你辛辛苦苦跑通了整个因果推断流程最后被一句“这个变量根本没法干预”直接判了死刑这些都不是模型没调好也不是代码有bug而是数据还没准备好开口说话你就急着让它发表演讲。数据科学里的“实验设计”说白了就是给数据搭建一个能讲真话、讲清楚、讲得有说服力的舞台。它不等于实验室里穿白大褂、戴护目镜的操作它更像一位经验丰富的法庭律师——不是靠嗓门大赢官司而是靠证据链的严密性、证人证词的可信度、交叉质询的精准度让事实自己浮出水面。关键词里反复出现的“Towards AI — Multidisciplinary Science Journal”恰恰点出了这件事的本质它从来就不是纯统计或纯工程的事而是统计学、领域知识、工程实现、业务逻辑四股力量拧成的一根绳。你懂p值但不懂用户生命周期模型会告诉你“显著”但你无法判断这“显著”是真实效应还是因为新用户天然更爱点广告你懂AB测试框架但不懂埋点逻辑那“流量分配”可能从第一步就歪了你懂因果图但不懂产品功能边界再漂亮的do-calculus也推不出可落地的行动项。所以这篇文章要聊的不是教科书里那个抽象的“DOEDesign of Experiments”定义而是我过去八年在电商、金融、SaaS三个行业亲手踩过坑、改过方案、救过火的真实战场笔记。它适合三类人刚转行的数据科学家总在模型指标和业务反馈之间感到撕裂资深算法工程师开始带团队、要对结果负责发现光会调参远远不够还有业务方伙伴想真正看懂一份实验报告背后到底有多少水分。我们不谈虚的只讲怎么把“设计实验”这件事变成你日常工作中可拆解、可检查、可复盘的硬核动作。2. 实验设计的整体思路从“我想验证什么”到“数据能证明什么”的三次转身很多人一上来就想“我要做个AB测试”。这就像医生一进门就说“我要开刀”却没问清病人到底哪里疼、疼了多久、有没有基础病。实验设计的第一步永远不是选工具、写SQL而是完成三次关键的思维转身。这三次转身决定了你的实验是金矿还是填不满的坑。2.1 第一次转身从“业务假设”到“可证伪的因果命题”业务方说“我们觉得新首页布局能提升用户停留时长。” 这是一个典型的、模糊的、不可证伪的业务直觉。它没说明“谁”的停留时长、“在什么条件下”、“提升多少才算有效”。如果直接拿这个去设计实验结局往往是数据跑出来你发现整体停留时长涨了5%但新用户跌了15%老用户涨了20%——你根本不知道该庆功还是该写检讨。我的做法是立刻把它翻译成一个结构化因果命题必须包含四个要素主体Who、干预What、结果Outcome、情境Context。比如上面那句话我会和产品、运营一起抠出主体注册满30天、近7天有至少1次付费行为的活跃用户排除新用户干扰和沉默用户噪音干预将原首页顶部Banner区的“限时折扣”模块替换为“个性化商品推荐流”明确改动点而非笼统的“新布局”结果用户当日首次打开App后的平均页面停留时长秒且仅计算进入首页后的首屏停留排除跳转流失干扰情境实验在每周二至周四的上午10点至下午4点进行避开周末流量高峰和夜间低活时段保证信号纯净。这个过程我称之为“命题手术”。每一次讨论都在帮业务方把模糊的“感觉”切开露出里面可测量、可归因、可重复的肌肉组织。很多项目死在第一步不是因为技术不行而是因为大家还在用不同语言说话。我坚持用这个四要素模板写进PRD初稿哪怕多花两小时也比后期数据打架强十倍。2.2 第二次转身从“理想世界”到“现实约束”的妥协与加固有了清晰命题下一步是面对冰冷的现实。理想中我们想要一个完美的随机对照试验RCT用户被完全随机分入A/B组两组除干预外其他条件100%一致。但现实是APP版本碎片化、网络延迟导致分流失败、用户跨设备登录、甚至运营临时加塞的Push推送都会让这个“完美”瞬间崩塌。我的应对策略是“三层加固法”第一层分流机制加固。绝不依赖前端JS做分流易被篡改、加载失败。我们采用服务端网关层分流基于用户ID哈希后取模确保分流逻辑稳定、可追溯、不可绕过。同时强制要求所有实验流量必须经过统一的“实验网关”中间件任何绕过它的请求一律打标为“无效流量”后续分析自动剔除。这个中间件我们内部叫它“守门人”它不生产数据但它决定哪些数据有资格进入分析池。第二层观测窗口加固。不看“实验启动后7天”的笼统数据。我们定义“有效观测期”从用户首次被分入实验组并成功看到新界面起往后精确计算7×24小时。期间若用户切换设备、清除缓存导致ID丢失则该用户后续行为全部标记为“失联”不计入最终分析。这听起来严苛但它逼着工程团队把用户ID的跨端一致性做到极致——而这恰恰是长期数据资产的基石。第三层混淆变量加固。提前列出所有可能干扰结果的变量并设计监控方案。比如上面那个首页实验我们必须同步监控当日App整体崩溃率崩溃用户不可能停留、当日服务器响应延迟P95慢则用户直接退出、当日是否有大型营销活动如618大促会淹没实验信号。这些不是事后补救而是实验启动前就在监控大盘里建好实时告警。一旦某项指标异常系统自动暂停实验并触发人工复核流程。这三次转身本质上是在构建一个“信任契约”业务方信任你能把他们的想法翻译成可执行的命题工程方信任你提出的约束是合理且可落地的而你自己则必须信任这套流程能帮你筛掉噪音留下真相。2.3 第三次转身从“单点验证”到“系统认知”的升维思考一个成功的AB测试不该是终点而应是起点。很多团队做完一次实验报告一交就归档了。但真正的价值在于把单次实验嵌入到一个持续演进的“认知系统”里。我的做法是建立“实验知识图谱”。每次实验结束后除了常规的统计报告我们强制录入三个维度命题维度记录原始业务假设、转化后的因果命题、最终验证结论支持/不支持/ inconclusive以及关键置信度如p值、Cohens d效应量。执行维度记录分流方式、样本量计算依据用的是Z检验还是贝叶斯方法最小可检测效应MDE设为多少、实际观测期、关键混淆变量监控结果。归因维度记录实验中暴露的深层问题。比如某次推荐策略实验失败表面看是点击率未提升但深入分析发现新策略导致商品曝光集中度陡增长尾商品曝光下降40%——这揭示了当前推荐系统存在严重的“马太效应”这才是真正需要解决的系统性问题。这个图谱不是文档库而是活的数据库。当新需求进来我们第一件事不是写代码而是查图谱类似命题是否做过当时的执行约束是什么归因结论指向哪个系统模块它让每一次实验都成为下一次决策的燃料而不是孤岛。3. 核心细节解析样本量、随机化、指标选择每一个都是生死线实验设计里有三个环节看似是技术细节实则是整条链路的“阿喀琉斯之踵”。任何一个出错前面所有努力都可能归零。我见过太多团队花了90%精力在模型上却在样本量计算上用Excel随便估了一个数最后发现统计功效只有30%等于拿着望远镜找蚂蚁——不是没有是你根本看不见。3.1 样本量不是“越多越好”而是“刚刚够好”的精密计算样本量计算常被误解为一个数学题。其实它是一个商业决策题核心在于权衡三样东西你愿意为一次错误结论付出多大代价你希望捕捉到多小的真实效应你手头有多少资源可以烧我们不用“传统”的公式硬算而是用模拟驱动法Simulation-Driven Approach。以电商首页改版为例目标指标是“人均页面停留时长”历史均值为120秒标准差为85秒。业务认为如果新方案能让均值提升5秒即125秒就值得全量。那么我们的任务不是算“需要多少人”而是回答“如果真实提升就是5秒我用X个用户做实验有多大把握能检测出来统计功效如果真实提升是0我又有多大可能误判为有效一类错误”具体操作用Python生成10000组模拟数据每组包含A组120±85和B组125±85各N个样本对每组数据运行t检验记录p值0.05的比例这就是统计功效调整N值直到功效稳定在80%以上行业通用底线同时确保一类错误率严格控制在5%。提示千万别忽略“最小可检测效应MDE”的业务意义。MDE5秒听起来很小但如果日活100万人均停留提升5秒每天就多出500万秒的用户注意力——这可能意味着多卖出几千单。所以MDE不是统计参数它是业务价值的翻译器。我和业务方沟通时永远用“每天多出X万秒”“每月多带来Y万GMV”来解释MDE而不是干巴巴的“5秒”。3.2 随机化从“伪随机”到“抗干扰随机”的工程实践随机化是RCT的灵魂但也是最容易被现实击穿的脆弱点。常见的“伪随机”陷阱包括时间戳随机用time.time()取毫秒做种子。问题同一秒内大量请求种子相同分流结果一致造成时间片污染。用户ID简单哈希用MD5(user_id)取后两位。问题ID本身有规律如按注册时间递增哈希后仍可能呈现周期性导致分组不均。前端JavaScript随机浏览器环境差异大部分低端机型JS执行慢或失败导致分流逻辑失效。我们的解决方案是“双保险哈希动态盐值”# 服务端分流核心逻辑伪代码 def get_experiment_group(user_id, experiment_name, saltNone): # 盐值动态生成结合实验名、当前日期年月日、一个预设密钥 if not salt: salt f{experiment_name}_{datetime.now().strftime(%Y%m%d)}_my_secret_key # 使用SHA256输入为 user_id salt确保强随机性 hash_input f{user_id}{salt}.encode(utf-8) hash_obj hashlib.sha256(hash_input) hash_hex hash_obj.hexdigest() # 取哈希值前8位转为整数再对分组数取模 # 例如8位十六进制最大为 0xffffffff 4294967295足够大 int_val int(hash_hex[:8], 16) return int_val % num_groups # num_groups 通常是2A/B或更多这个方案的关键在于“盐值”。它让同一个user_id在不同实验、不同日期得到完全不同的分组结果彻底切断了分组间的相关性。更重要的是它把随机化逻辑牢牢锁死在服务端前端只负责“展示”不参与“决策”。上线前我们会用历史用户ID全量跑一遍验证A/B组人数偏差是否在±0.5%以内——这是硬性红线超了就必须回滚排查。3.3 指标选择警惕“幻觉指标”与“幽灵指标”指标是实验的眼睛。选错了指标就像给侦察兵配了一副哈哈镜。我总结了两类最危险的指标陷阱幻觉指标Illusion Metric看起来很美但和业务目标弱相关甚至负相关。典型代表是“DAU日活用户数”。某次我们测试一个新签到功能DAU涨了3%一片欢腾。但深入看新增的全是薅羊毛的“僵尸号”他们签到后立刻卸载对留存、付费毫无贡献。后来我们把核心指标换成“7日留存率”和“首单转化率”结果立刻反转新功能让这两项指标双双下滑。从此我们规定任何实验主指标Primary Metric必须是能直接映射到公司OKR的1-2个指标且必须是滞后性指标Lagging Indicator比如收入、留存、NPS而不是先行性指标Leading Indicator如点击、曝光。幽灵指标Ghost Metric数据能取到但定义模糊、口径不一、无法归因。最常见的是“GMV成交总额”。问题在于一笔订单是用户在A组看到商品详情页后下单还是在B组看到首页推荐后下单如果用户跨组浏览GMV该算在哪一组我们的铁律是主指标必须是“原子级可归因”的。对于GMV我们只统计“用户在实验组内完成的、且从实验触点如首页推荐流直接点击进入的商品所产生的GMV”。为此我们在埋点时强制要求每个实验触点携带唯一的exp_id和exp_group参数并在订单表里关联存储。没有这个关联这笔GMV在实验分析中就是“幽灵”直接剔除。注意永远设置“护栏指标Guardrail Metrics”。它们不决定实验成败但一旦恶化必须立即熔断。比如首页实验的护栏指标是“页面加载失败率”和“用户投诉率”。前者超过1%后者超过0.01%无论主指标多漂亮实验自动暂停。这就像汽车的安全气囊——你希望它永远不弹出但必须存在。4. 实操过程从立项到结案一个完整实验的七步走纸上谈兵终觉浅。下面我以一个真实的SaaS产品“智能报表导出功能”优化实验为例带你走完从立项到结案的完整七步。这不是理论流程而是我们团队每天在Jira里实际推进的Checklist。4.1 Step 1立项与命题手术耗时1-2天输入产品经理PRD草稿“新导出功能支持PDF/Excel一键切换提升用户效率”。动作召集数据、产品、前端、后端、客服代表开1.5小时“命题手术会”。输出《实验命题说明书》主体近30天内使用过报表导出功能≥3次的付费客户排除试用用户和低频用户干预在导出按钮旁增加“PDF/Excel”双格式切换Tab默认保持原PDF用户可一键切至Excel结果用户单次导出操作的平均耗时秒从点击“导出”到收到邮件/下载完成提示情境仅对Chrome/Firefox最新两个大版本生效排除网络延迟2s的请求。交付物签字确认的《实验命题说明书》PDF作为后续所有工作的唯一基准。4.2 Step 2方案设计与工程排期耗时3-5天核心产出《实验技术方案书》包含分流逻辑服务端网关哈希分流盐值report_export_v2_20240601_secret埋点规范exp_idreport_export_v2,exp_groupA/B,eventexport_start/export_success/export_fail所有事件必须携带user_id和session_id护栏指标监控页面JS错误率、API 5xx错误率、用户主动取消导出率回滚预案一键开关5分钟内可全量切回旧版。关键动作与前端负责人一起Review埋点代码用Charles抓包验证确保每个事件参数100%准确。我坚持“不亲眼看到抓包数据不签字放行”。4.3 Step 3样本量与观测期敲定耗时0.5天基于历史数据当前导出平均耗时18.2秒标准差12.5秒业务MDE期望降低至16.5秒降幅9.3%这是客服反馈“用户普遍抱怨导出慢”的临界点模拟计算需每组约12500个有效样本才能达到80%功效α0.05观测期按日活和分流比例推算需连续运行14天覆盖完整工作周周末。4.4 Step 4上线与冷启动耗时1天不是直接全量。我们采用“灰度三步走”Step 11%流量只对内部员工开放验证分流、埋点、功能无硬伤Step 25%流量开放给10个高配合度的KA客户收集主观反馈“切换方便吗”“有困惑吗”Step 3100%实验流量正式启动14天观测期。每日晨会数据同学播报前24小时关键指标基线A/B组人数、平均耗时、护栏指标任何一项偏差5%立即拉群排查。4.5 Step 5数据采集与清洗耗时贯穿观测期自动化清洗脚本核心逻辑-- 只保留“有效导出会话” WITH valid_sessions AS ( SELECT user_id, exp_group, session_id, MIN(CASE WHEN eventexport_start THEN event_time END) as start_time, MIN(CASE WHEN eventexport_success THEN event_time END) as success_time, MAX(CASE WHEN eventexport_fail THEN 1 ELSE 0 END) as is_failed FROM events WHERE exp_id report_export_v2 AND event IN (export_start, export_success, export_fail) GROUP BY user_id, exp_group, session_id ) SELECT exp_group, AVG(success_time - start_time) as avg_duration_sec FROM valid_sessions WHERE is_failed 0 AND success_time IS NOT NULL AND start_time IS NOT NULL AND (success_time - start_time) BETWEEN 1 AND 300 -- 过滤异常值1秒作弊或5分钟超时 GROUP BY exp_group;人工抽查每天随机抽100条原始日志用Excel手动验算3条确保SQL逻辑无误。这是防止“Garbage In, Garbage Out”的最后一道闸。4.6 Step 6分析与归因耗时2-3天不止看p值。我们输出三张核心图表主指标对比图A/B组平均耗时箱线图含中位数、四分位距、异常值直观展示分布差异效应量热力图按用户等级S/A/B/C、行业金融/零售/制造、地域华东/华北/华南分层看效应是否稳健护栏指标趋势图叠加在主指标图上确认无副作用。归因分析发现B组新功能耗时虽降但“导出失败率”上升0.8%。深挖日志发现是Excel格式在处理超大数据集10万行时内存溢出。结论新功能对中小客户有效但对大客户需单独优化。这不是“实验失败”而是发现了更精细的用户分层机会。4.7 Step 7结案与知识沉淀耗时1天输出《实验结案报告》结构固定【一句话结论】新导出功能在中小客户中显著降低导出耗时9.3%p0.001但大客户失败率上升建议分场景灰度。【数据附录】完整统计表均值、标准差、t值、p值、效应量d、分层分析表、护栏指标表【行动项】① 本周内对大客户启用“大文件专用导出通道”② 下季度将“导出耗时”纳入SaaS产品SLA。更新实验知识图谱录入本次实验的所有维度信息供未来参考。5. 常见问题与排查技巧实录那些没人告诉你的“脏活累活”再完美的设计也会在真实世界里撞墙。下面这些是我和团队在无数个深夜、无数个紧急会议中用真金白银买来的教训。它们不会出现在教科书里但能让你少走三年弯路。5.1 问题1“p值0.05但业务方说感觉不对”现象AB测试显示新策略点击率提升1.2%p0.03统计显著。但运营同学反馈一线销售说客户抱怨变多了。排查路径先看数据质量检查埋点是否漏打用SELECT COUNT(*) FROM events WHERE exp_idxxx AND eventclick和SELECT COUNT(*) FROM page_views WHERE exp_idxxx对比如果后者远大于前者说明埋点丢失严重。再看用户分层用SQL按“客户行业”分组发现金融行业提升3.5%但制造业下降0.8%。原来新策略的UI动效在制造业客户常用的老旧平板上卡顿导致误点率飙升。最后看行为链路不只是看“点击”看“点击后是否完成核心动作如填写表单”。发现制造业客户点击率虽降但表单提交率反升2%说明他们更谨慎但转化质量更高。独家技巧“三明治分析法”。每次看主指标必须同时看上层用户来源/设备、本层主指标、下层后续转化三个维度的数据。像三明治一样夹住主指标才能看清它到底是“真肉”还是“空气”。5.2 问题2“实验跑了10天A/B组人数严重不均”现象计划50/50分流结果A组12000人B组8500人偏差达20%。根因与解决最常见根因分流逻辑未覆盖所有入口。比如APP有“首页”“搜索页”“消息页”三个入口都能触发导出但分流代码只在首页埋点里生效。排查命令Hive/Spark SQL-- 查看各入口的分流覆盖率 SELECT entrance_page, COUNT(DISTINCT CASE WHEN exp_groupA THEN user_id END) as A_users, COUNT(DISTINCT CASE WHEN exp_groupB THEN user_id END) as B_users, COUNT(DISTINCT user_id) as total_users FROM events WHERE exp_id xxx GROUP BY entrance_page;解决方案立刻补全所有入口的分流逻辑并用“分流覆盖率仪表盘”长期监控。我们要求任何实验各主要入口的分流覆盖率必须≥99.5%否则视为实验无效。5.3 问题3“实验结束但数据还在延迟入库”现象观测期已结束但BI看板上最后24小时的数据迟迟不更新无法及时分析。根因与解决根因数据管道Data Pipeline存在TTLTime-To-Live延迟尤其是日志从客户端上报→Kafka→Flink实时处理→Hive分区表链路长偶发积压。我们的SOP实验启动前在数据平台申请“实验专属数据通道”该通道享有最高优先级Flink任务独占CPU资源观测期中每天10:00和16:00自动运行SELECT COUNT(*) FROM events WHERE ds ${yesterday} AND exp_id xxx与昨日同期对比偏差5%则告警结案日不等BI看板直接连Hive用SELECT * FROM events WHERE ds ${start_date} AND ds ${end_date} AND exp_id xxx LIMIT 100手动抽样验证数据完整性。避坑心得永远不要相信“数据会自动准时”。在数据科学里“准时”是设计出来的不是等出来的。我把数据管道的SLA服务等级协议写进了每个实验的立项文档和业务方共同签字——这是对彼此最基础的尊重。5.4 问题4“为什么同样的实验在不同季度结果相反”现象去年Q3做的“邮件标题优化”实验新标题打开率2.1%今年Q2重跑结果-0.7%。深度归因不是数据错了而是世界变了。我们调取了两年的用户画像数据发现去年Q3新用户占比35%他们对“限时”“抢购”等字眼敏感今年Q2新用户占比仅18%主力是老用户他们更反感营销话术偏好“简洁”“专业”。解决方案建立“实验时效性标签”。在知识图谱里为每次实验打上valid_period: Q3_2023并强制要求重跑历史实验必须同步分析同期的用户结构变化。这让我们意识到很多“经典实验”本质是“快照”不是“定律”。提示我有个个人习惯每次实验结案都会在报告末尾加一行小字“本实验结论的有效期截至2024年12月31日。到期前需重新评估用户结构变化。” 这不是画蛇添足而是对数据谦卑的最好体现。6. 工具与协作让实验设计从“个人手艺”变成“团队能力”再好的方法论如果不能被团队轻松复用就是空中楼阁。我们花了近两年把上述所有经验沉淀为一套轻量级但极其锋利的“实验操作系统”Experiment OS它不是什么高大上的平台而是一套组合拳。6.1 核心工具链极简但致命精准分流与实验管理自研轻量级ExpManager服务Go语言提供HTTP APIPOST /assign传入user_id和exp_name返回group和exp_idGET /status?exp_namexxx实时查看A/B组人数、分流成功率、最近1小时错误率。优势无状态、水平扩展、100%服务端控制运维成本趋近于零。指标计算与归因基于dbtdata build tool构建的标准化模型层。所有实验指标都通过统一的stg_experiments模型加工确保“一个定义处处复用”。比如avg_export_duration这个指标在所有实验报告里SQL逻辑完全一致杜绝了“张三算A李四算B”的混乱。知识沉淀Notion数据库。每个实验一个Page字段包括命题、方案、样本量、关键数据截图、归因结论、时效性标签、关联的Jira Ticket。权限开放给全员新人入职第一周任务就是刷完最近20个实验的Page——这是最快理解业务逻辑的方式。6.2 协作机制打破“数据-产品-工程”的巴别塔最大的障碍从来不是技术而是语言不通。我们的破局点是把协作仪式化、可视化、轻量化。每周“实验门诊”30分钟固定在周一上午。任何角色带着一个不超过3句话的实验卡What/Why/Where stuck来现场“挂号”。数据同学现场用ExpManager查分流状态产品同学当场确认命题工程同学承诺修复时间。没有PPT只有白板和马克笔。这个会我们坚持了142周从未间断。“实验护照”制度每位新成员入职会领到一本实体小册子《实验护照》。里面没有理论只有10个真实案例的“翻车现场”和“救火步骤”。每完成一个实验导师在对应页签字。集齐10个签名才能独立发起实验。这本小册子是我们团队最珍贵的文化符号。“失败博物馆”在办公区一面墙上贴着所有“失败实验”的精简海报标题、一句话失败原因、一个最大教训。比如“‘一键登录’实验因未兼容企业微信SSO导致B端客户集体登出”。这不是羞辱而是最高效的集体学习。每次有新人问“为什么我们要做这么复杂的分流”我就带他看这张海报——答案比千言万语都响亮。7. 最后一点体会实验设计是数据科学家的“成人礼”写到这里我想说点掏心窝的话。刚入行时我也痴迷于模型的复杂度以为调出一个F10.95的模型就是巅峰。直到第一次我设计的实验上线后业务方拿着报告问我“所以我们接下来该做什么” 我哑口无言。那一刻我明白模型是锤子实验设计才是蓝图。没有蓝图再好的锤子也只能把钉子砸进地板里而不是建起一座桥。实验设计逼着你离开舒适区你要和产品聊清楚“用户到底在想什么”而不是只看漏斗数据你要和工程较真“这个埋点能不能100%准确”而不是只管SQL跑不跑得通你要和老板解释“为什么这次实验要花14天而不是3天”而不是只汇报一个p值。它不是一个技术模块而是一种思维方式的重塑——从“我能算出什么”转向“世界愿意告诉我什么”从“如何证明我对”转向“如何设计一个让我能被证伪的场景”。这条路没有捷径。我踩过的每一个坑都成了今天带团队时给新人划下的警戒线。如果你正站在这个路口别怕慢别怕问“傻问题”更别怕推翻自己昨天的设计。因为真正的数据科学不是关于数据有多“大”而是关于你有多“诚”——诚实地面对数据的噪声诚实地承认自己的无知诚实地为每一个结论搭起一座经得起推敲的桥。这座桥连接的不是A点和B点而是你和真相之间那条最短、也最坚实的距离。