1. 这不是“项目清单”而是一条可踩实的进阶路径你点开过多少次“Top 5 Data Science Projects for Beginners”这类标题我数不清了。但几乎每次点进去看到的都是一个房价预测、一个鸢尾花分类、一个泰坦尼克生存率——然后戛然而止。没有告诉你为什么选这个数据集没说明模型调参时到底在调什么更不会告诉你当你把代码跑通后下一步该往哪走、卡在哪、怎么破。这根本不是项目是幻灯片。我带过三十多个从零起步转行的数据新人也帮十多家中小企业的业务团队落地过分析需求。真正卡住人的从来不是“写不出代码”而是不知道每个动作背后的意图和代价。比如为什么初学者必须从“手写线性回归”开始而不是直接用sklearn.LinearRegression()因为不亲手推导损失函数、不手动更新权重你就永远分不清“过拟合”是模型太复杂还是数据本身噪声太大你就无法理解当R²从0.85掉到0.62时到底是特征工程出了问题还是该换评估指标了。这篇内容就是按真实学习曲线重新打磨的五级台阶。它不叫“项目列表”它叫能力锚点图谱每一级都对应一个明确的能力断层每一个项目都设计成“可拆解、可测量、可验证”的最小闭环。第一级你只用pandas和matplotlib画出三张图就能判断自己是否真正理解了数据分布第五级你得自己设计特征交叉逻辑、手动实现早停机制、写出可复用的模型服务接口——不是为了炫技而是因为你已经要开始对线上效果负责了。关键词里那个“Towards AI”不是平台名是方向感它指向的是“能独立交付价值”的AI实践者而不是会调包的代码搬运工。2. 项目整体设计与思路拆解2.1 为什么是“五级”而不是“五个项目”很多人误以为“从入门到放弃”是学习曲线陡峭其实恰恰相反——是中间缺了承重墙。我们见过太多人在学完吴恩达课程后信心满满打开Kaggle结果面对一个含37个字段、20万行记录的销售数据表连缺失值占比都算不准更别说识别出“促销期”这个隐藏时间特征。这不是能力问题是训练路径断裂。所以这五级设计核心逻辑是能力粒度递进而非项目难度堆砌Level 1感知层目标不是建模而是建立对“数据有生命”的直觉。你能一眼看出箱线图里的异常点是否合理能通过直方图判断是否需要做对数变换这种直觉比任何算法都重要。Level 2结构层解决“数据怎么组织才利于分析”的问题。重点不是SQL多高级而是理解为什么要把用户行为日志拆成“事实表维度表”为什么订单状态变更要记成流水而非快照。Level 3决策层引入真实业务约束。比如客户流失预警准确率95%毫无意义——因为流失客户只占5%你把所有客户都标为“不流失”准确率也是95%。这里必须强制你定义业务指标如“挽回成本单客LTV的30%”再反推模型目标。Level 4鲁棒层直面生产环境的混乱。数据延迟、特征漂移、线上推理超时……这些在Notebook里永远不会出现的问题必须在项目里提前预演。我们要求第四级项目必须包含“特征监控看板”和“模型性能衰减预警逻辑”。Level 5价值层最终交付物不是.pkl文件而是可被业务方直接使用的决策支持流。比如库存优化项目输出不是“建议补货1200件”而是“若下周促销力度提升20%则最优补货量为1350±80件置信区间90%”。提示每一级的验收标准都拒绝模糊表述。Level 1的交付物必须包含三张图原始数据分布直方图、处理后分布图、以及二者叠加的KS检验统计量Level 3必须提交一份《业务影响说明书》用非技术语言写明“如果模型误判100个高价值客户将导致多少实际收入损失”。2.2 工具链选择为什么坚持“少而精”且全部锁定Python生态市面上充斥着“SparkTensorFlowAirflow全栈”的教程但现实是90%的中小企业数据分析需求用pandasscikit-learnFlask就能覆盖80%场景。过度堆砌工具本质是用技术复杂度掩盖业务思考的懒惰。我们严格限定工具链理由非常务实pandas不可替代它的groupby().agg()链式操作天然契合“探索性分析→特征构造→验证假设”的思维流。你用SQL写十行聚合pandas可能只要一行.rolling(7).mean()就完成周均值计算这对快速试错至关重要。scikit-learn是工业界事实标准它的API设计fit()/predict()/score()统一了从线性回归到XGBoost的所有模型。新手不必记忆不同库的参数名比如LightGBM的num_leavesvs XGBoost的max_depth专注理解“为什么这个参数控制模型复杂度”。Flask而非FastAPI作为服务框架虽然FastAPI性能更好但它的异步机制对新手是认知负担。而Flask的同步请求处理与Jupyter中调试模型的过程完全一致——你在线下验证好的predict()函数几乎不用改就能塞进Flask路由里。这种“所见即所得”的平滑过渡比追求10%的QPS提升重要得多。注意我们刻意回避了Docker、Kubernetes等运维工具。不是它们不重要而是必须在Level 4确认你能稳定产出可部署模型后再引入容器化。否则你连模型效果都调不好却花三天配Dockerfile这是本末倒置。2.3 数据集选择原则拒绝“玩具数据”拥抱“脏数据”Kaggle上那些清洗干净、字段命名规范、缺失值标注明确的数据集是绝佳的教学道具但也是最大的学习陷阱。真实世界的数据80%时间花在“找数据”和“洗数据”上。因此五级项目的数据源全部来自真实场景的简化版Level 1用某电商APP的埋点日志抽样含用户ID、事件类型、时间戳、页面URL原始数据里混着“page_view”和“pageview”两种写法时间戳格式有ISO和Unix时间戳并存Level 3的客户流失数据来自某SaaS公司的导出CSV其中“最后登录时间”字段存在大量空值而业务方口头告诉你“空值从未登录不是数据丢失”Level 5的供应链数据直接采用某快消品企业的ERP系统导出报表字段名是“CUST_NO”、“PROD_ID”、“SHIP_QTY”没有注释文档你需要通过字段值分布反向推断含义。这种“脏”不是为了增加难度而是训练一种肌肉记忆看到一个新数据集第一反应不是“怎么建模”而是“哪些字段可能有问题”、“业务方最关心哪个指标”、“缺失值背后有没有业务逻辑”。3. 核心细节解析与实操要点3.1 Level 1用三张图建立数据直觉工具pandas matplotlib这不是“画图作业”而是一场数据问诊。你拿到的是一份某本地生活平台的用户点击日志约5万行字段包括user_id,click_time,category,item_id,duration_sec。关键动作与原理第一张图duration_sec直方图 KDE曲线import pandas as pd import matplotlib.pyplot as plt import seaborn as sns df pd.read_csv(click_log.csv) plt.figure(figsize(10, 6)) sns.histplot(df[duration_sec], kdeTrue, bins50, alpha0.7) plt.title(用户停留时长分布原始) plt.xlabel(停留时长秒) plt.show()为什么必须加KDE直方图受bin数量影响大KDE核密度估计能平滑呈现分布形态。你立刻会发现峰值在5秒左右但右侧拖着一条极长的尾巴——这暗示存在少量“异常长停留”可能是用户切屏未关闭页面。此时不要急着删先标记出来。第二张图category频次柱状图 累计占比折线category_counts df[category].value_counts() cumsum_ratio category_counts.cumsum() / len(df) * 100 fig, ax1 plt.subplots(figsize(12, 6)) ax1.bar(category_counts.index[:10], category_counts.values[:10], alpha0.8) ax1.set_ylabel(点击次数, colortab:blue) ax1.tick_params(axisy, labelcolortab:blue) ax2 ax1.twinx() ax2.plot(category_counts.index[:10], cumsum_ratio[:10], ro-, markersize4) ax2.set_ylabel(累计占比%, colortab:red) ax2.tick_params(axisy, labelcolortab:red) plt.title(TOP10类目点击频次及累计占比) plt.show()为什么看累计占比如果前3个类目就占了75%点击量说明平台流量高度集中。后续做推荐系统时“长尾类目冷启动”就是核心难题——这个洞察远比记住“协同过滤公式”重要。第三张图click_time时间序列热力图按小时×星期df[click_time] pd.to_datetime(df[click_time]) df[hour] df[click_time].dt.hour df[dayofweek] df[click_time].dt.dayofweek # 0周一 pivot_df df.groupby([dayofweek, hour]).size().unstack(fill_value0) plt.figure(figsize(12, 6)) sns.heatmap(pivot_df, cmapYlGnBu, annotTrue, fmtd) plt.title(点击行为时间热力图星期×小时) plt.xlabel(小时) plt.ylabel(星期0周一) plt.show()为什么热力图比折线图好它同时暴露两个维度的周期性。你可能发现周五晚8点20点是峰值但周日晚上却是低谷——这直接指向运营策略周末活动应避开周日聚焦周五晚。实操心得我带过的学员里80%在Level 1栽在同一个坑用df.describe()看duration_sec看到“均值120秒”就默认用户平均停留2分钟。但直方图一画发现中位数只有28秒均值被几个10000秒的异常值拉高。永远用可视化验证统计摘要——这是数据工作者的第一道职业本能。3.2 Level 2构建可追溯的特征工程流水线工具pandas scikit-learn PipelineLevel 1让你“看见”数据Level 2让你“塑造”数据。这里的关键不是“加多少特征”而是确保每一步变换都可复现、可解释、可回滚。以电商用户复购预测为例目标预测用户未来7天是否会再次下单。原始数据含user_id,order_date,product_category,order_amount,payment_method。核心特征构造逻辑时间窗口特征不是简单算“过去30天订单数”而是定义三个正交窗口近期活跃度过去7天订单数反映即时兴趣中期稳定性过去30天订单数 / 30日均频次过滤偶然下单长期价值过去180天总金额剔除促销干扰用中位数替代均值行为模式特征用pandas的shift()和diff()捕捉变化# 按user_id排序计算相邻订单间隔天 df_sorted df.sort_values([user_id, order_date]) df_sorted[days_since_last_order] df_sorted.groupby(user_id)[order_date].diff().dt.days # 构造“是否高频用户”标签最近3次订单间隔均5天 df_sorted[is_frequent_buyer] ( df_sorted.groupby(user_id)[days_since_last_order] .rolling(3).apply(lambda x: (x 5).all(), rawTrue) .fillna(False) )类别编码的陷阱规避payment_method有Credit Card, Debit Card, Wallet三种。新手常直接用LabelEncoder但这样编码隐含“Wallet Debit Credit”的序数关系而实际它们是平行类别。正确做法是from sklearn.preprocessing import OneHotEncoder encoder OneHotEncoder(dropfirst, sparse_outputFalse) # drop first避免共线性 encoded_payment encoder.fit_transform(df[[payment_method]])Pipeline封装要点from sklearn.pipeline import Pipeline from sklearn.preprocessing import StandardScaler from sklearn.ensemble import RandomForestClassifier # 特征工程步骤自定义Transformer class OrderFeatureEngineer: def fit(self, X, yNone): return self def transform(self, X): X_new X.copy() # 所有特征构造逻辑放在这里 X_new[recency_score] ... # 计算逻辑 X_new[frequency_score] ... X_new[monetary_score] ... return X_new[[recency_score, frequency_score, monetary_score]] # 完整Pipeline pipeline Pipeline([ (feature_engineer, OrderFeatureEngineer()), (scaler, StandardScaler()), # 数值特征标准化 (classifier, RandomForestClassifier(n_estimators100)) ]) # 训练时自动执行全流程 pipeline.fit(X_train, y_train)注意Pipeline的transform()方法必须返回pandas.DataFrame或numpy.ndarray不能返回带索引的Series。我曾因返回pd.Series导致后续StandardScaler报错调试两小时才发现——这是新手高频雷区。3.3 Level 3在业务约束下重构模型目标工具scikit-learn 自定义评估器Level 3的标志性转变是模型不再追求“全局最优”而是追求“业务可接受的局部最优”。以银行信用卡欺诈检测为例数据集含100万笔交易欺诈率0.3%。传统做法的致命缺陷用accuracy评估模型把所有交易标为“正常”准确率99.7%——完美不它漏掉了3000起欺诈每起平均损失2万元总损失6000万。Level 3的重构步骤定义业务成本矩阵真实\预测预测欺诈预测正常真实欺诈成本0成功拦截成本20000损失真实正常成本500人工审核费成本0无动作构造自定义损失函数from sklearn.metrics import make_scorer def business_cost(y_true, y_pred): # y_true, y_pred 是0/1数组 fp ((y_true 0) (y_pred 1)).sum() # 误报数 fn ((y_true 1) (y_pred 0)).sum() # 漏报数 return fp * 500 fn * 20000 cost_scorer make_scorer(business_cost, greater_is_betterFalse)在GridSearchCV中使用from sklearn.model_selection import GridSearchCV param_grid {n_estimators: [50, 100], max_depth: [3, 5]} clf RandomForestClassifier() grid_search GridSearchCV( clf, param_grid, scoringcost_scorer, # 关键用业务成本替代f1 cv3 ) grid_search.fit(X_train, y_train)为什么这步不可跳过因为业务方永远不关心你的AUC是多少他们只问“如果上线这个模型每月能帮我省多少钱” Level 3强迫你把数学指标翻译成财务语言。我们要求学员必须手算一笔假设模型将漏报率从5%降到2%年交易量1亿笔单笔欺诈损失2万则年节省 (5%-2%) * 1亿 * 2万 60亿元——这个数字才是推动项目落地的真正燃料。实操心得在真实项目中我曾用此方法说服风控总监接受一个“准确率更低但成本更低”的模型。关键不是展示代码而是拿出一张Excel表左边列“当前规则引擎漏报率”右边列“新模型漏报率”中间列“对应年损失金额”。业务语言永远比技术语言更有穿透力。3.4 Level 4构建生产就绪的监控闭环工具Prometheus Grafana 自定义脚本Level 4的分水岭是模型不再是静态文件而是持续运行的服务。你必须回答当线上数据分布悄悄变化时你怎么第一时间知道以新闻推荐系统为例实时预测用户对文章的点击概率。核心监控项数据层面字段缺失率如article_category为空的比例 5% → 触发告警数值型特征分布偏移用KS检验对比线上vs训练集分布p-value 0.01 → 告警模型层面推理延迟P95 200ms → 告警预测分数分布如95%的预测值集中在[0.01, 0.05]区间 → 可能模型失效实操部署方案特征监控脚本每日凌晨执行# monitor_features.py import pandas as pd from scipy.stats import ks_2samp # 加载线上最新1天特征数据 online_df pd.read_parquet(s3://prod-data/features_daily.parquet) # 加载训练时的特征分布基准存为parquet baseline_df pd.read_parquet(s3://models/baseline_features.parquet) for col in [user_age, article_length, click_rate_7d]: stat, p_value ks_2samp(online_df[col].dropna(), baseline_df[col].dropna()) if p_value 0.01: send_alert(f特征{col}分布发生显著偏移p{p_value:.4f})Prometheus指标暴露Flask服务内嵌from prometheus_client import Counter, Histogram, Gauge # 定义指标 PREDICTION_COUNT Counter(prediction_total, Total predictions made) PREDICTION_LATENCY Histogram(prediction_latency_seconds, Prediction latency) MODEL_ACCURACY Gauge(model_accuracy, Current model accuracy) app.route(/predict, methods[POST]) def predict(): PREDICTION_COUNT.inc() with PREDICTION_LATENCY.time(): result model.predict(input_data) # 更新准确率用小批量线上反馈更新 MODEL_ACCURACY.set(calculate_online_accuracy()) return jsonify({prediction: result})Grafana看板配置面板1rate(prediction_total[1h])→ 查看QPS趋势面板2histogram_quantile(0.95, rate(prediction_latency_seconds_bucket[1h]))→ P95延迟面板3model_accuracy→ 模型在线准确率需接入用户点击反馈流注意很多教程教你怎么用MLflow跟踪实验但没告诉你生产监控的第一步是“让指标可见”。我们坚持用Prometheus而非自研监控是因为它的Pull模型天然适配无状态服务且Grafana生态成熟——你花2小时配好就能获得企业级监控能力没必要重复造轮子。3.5 Level 5交付可被业务方直接使用的决策流工具Flask Jinja2 CeleryLevel 5的终极考验能否让一个不懂代码的区域经理通过网页输入几个参数得到可执行的业务指令以连锁药店库存优化项目为例。交付物不是API而是一个决策仪表盘输入区选择门店、选择商品品类、设置补货周期7/14/30天输出区表格显示“建议补货量”、“安全库存量”、“最大库存上限”图表未来30天销量预测曲线 库存水位线文案用自然语言生成建议如“根据历史销售波动建议本次补货1350件±80件可支撑至6月15日期间有85%概率不缺货”核心技术实现异步任务解耦Celery# tasks.py from celery import Celery app Celery(inventory_optimization) app.task def generate_restock_plan(store_id, category, days30): # 耗时的预测和优化计算 forecast run_forecast(store_id, category, days) plan optimize_restock(forecast) save_to_db(plan) # 存入结果表 return plan[plan_id]前端交互逻辑Jinja2模板!-- dashboard.html -- form idplanForm select namestore_id.../select select namecategory.../select input typenumber namedays value30 button typesubmit生成补货计划/button /form div idresult/div script document.getElementById(planForm).onsubmit async function(e) { e.preventDefault(); const formData new FormData(this); // 发起异步任务 const taskRes await fetch(/api/start-plan, { method: POST, body: formData }); const {task_id} await taskRes.json(); // 轮询获取结果 const interval setInterval(async () { const res await fetch(/api/task-status/${task_id}); const {status, result} await res.json(); if (status SUCCESS) { clearInterval(interval); document.getElementById(result).innerHTML renderPlan(result); } }, 2000); }; /script自然语言生成NLG模块def generate_nlg_advice(plan): # 基于数值生成文案非大模型调用 if plan[confidence] 0.8: confidence_text 高置信度 elif plan[confidence] 0.6: confidence_text 中等置信度 else: confidence_text 低置信度建议人工复核 return f根据{confidence_text}预测建议本次补货{plan[optimal_qty]}件±{plan[uncertainty]}件可支撑至{plan[coverage_date]}。实操心得Level 5最容易陷入的误区是“过度工程化”。我曾见团队花两周开发React前端结果业务方说“我们只要一个Excel模板”。后来我们用FlaskJinja2两天做出轻量仪表盘业务方当天就用它做了季度补货决策。交付价值永远比技术炫技重要——这句话我在每个项目启动会上都会说三遍。4. 实操过程与核心环节实现4.1 Level 1完整代码实录从原始日志到三张决策图我们以某外卖平台2023年Q4用户点击日志click_log_q4.csv为例完整演示Level 1的每一步操作。数据已脱敏字段如下user_id(str),event_time(str, ISO format),page_url(str),event_type(str),duration_ms(int)。Step 1数据加载与基础清洗import pandas as pd import numpy as np import matplotlib.pyplot as plt import seaborn as sns # 加载数据注意原始时间字段是字符串 df pd.read_csv(click_log_q4.csv) # 清洗修正event_type大小写不一致 df[event_type] df[event_type].str.lower().str.strip() # 清洗duration_ms为毫秒转换为秒并过滤异常值1小时视为无效 df[duration_sec] df[duration_ms] / 1000 df df[(df[duration_sec] 0) (df[duration_sec] 3600)] print(f原始数据{len(df)}行) print(f清洗后{len(df)}行去除{len(pd.read_csv(click_log_q4.csv)) - len(df)}行异常)Step 2绘制第一张图——停留时长分布plt.figure(figsize(10, 6)) # 使用seaborn histplotbins设为100以观察细节 sns.histplot(df[duration_sec], kdeTrue, bins100, alpha0.7, colorsteelblue) plt.title(用户停留时长分布Q4, fontsize14, fontweightbold) plt.xlabel(停留时长秒, fontsize12) plt.ylabel(频次, fontsize12) plt.grid(True, alpha0.3) # 添加关键统计线 plt.axvline(df[duration_sec].median(), colorred, linestyle--, labelf中位数{df[duration_sec].median():.0f}s) plt.axvline(df[duration_sec].mean(), colororange, linestyle-., labelf均值{df[duration_sec].mean():.0f}s) plt.legend() plt.show() # 输出KS检验结果验证分布形态 from scipy.stats import kstest _, p_value kstest(df[duration_sec], lognorm, args(1, 0, 10)) print(fKS检验p值{p_value:.4f}p0.05表示非正态分布)实操现场记录运行后发现p值0.0003证实分布严重右偏。中位数28秒均值却高达142秒——这说明少数超长停留如用户挂机极大扭曲了均值。业务启示运营活动应聚焦提升“中位数停留”而非盲目拉高均值。Step 3绘制第二张图——类目点击TOP10# 从page_url提取类目真实场景URL含/category/food/ df[category] df[page_url].str.extract(r/category/(\w)/) # 过滤空类目 df_cat df.dropna(subset[category]) # 绘制TOP10频次累计占比 category_counts df_cat[category].value_counts() top10_categories category_counts.head(10) cumsum_ratio top10_categories.cumsum() / len(df_cat) * 100 fig, ax1 plt.subplots(figsize(12, 6)) bars ax1.bar(range(len(top10_categories)), top10_categories.values, colorlightcoral, alpha0.8, label点击次数) ax1.set_xlabel(类目, fontsize12) ax1.set_ylabel(点击次数, colortab:blue, fontsize12) ax1.tick_params(axisy, labelcolortab:blue) ax1.set_xticks(range(len(top10_categories))) ax1.set_xticklabels(top10_categories.index, rotation45) # 叠加累计占比折线 ax2 ax1.twinx() line ax2.plot(range(len(top10_categories)), cumsum_ratio.values, ro-, markersize6, linewidth2, label累计占比%) ax2.set_ylabel(累计占比%, colortab:red, fontsize12) ax2.tick_params(axisy, labelcolortab:red) ax2.set_ylim(0, 100) # 添加图例 lines1, labels1 ax1.get_legend_handles_labels() lines2, labels2 ax2.get_legend_handles_labels() ax1.legend(lines1 lines2, labels1 labels2, locupper left) plt.title(TOP10类目点击频次及累计占比Q4, fontsize14, fontweightbold) plt.tight_layout() plt.show() print(TOP3类目累计占比, cumsum_ratio.iloc[2])实操现场记录TOP3food, grocery, pharmacy占68.3%。这意味着平台80%的运营资源应聚焦这三个类目其他类目可考虑合并或下线——这是直接可执行的业务结论。Step 4绘制第三张图——时间热力图# 解析时间字段 df_cat[event_time] pd.to_datetime(df_cat[event_time]) df_cat[hour] df_cat[event_time].dt.hour df_cat[dayofweek] df_cat[event_time].dt.dayofweek # 0周一 # 构建热力图数据星期×小时 pivot_data df_cat.groupby([dayofweek, hour]).size().unstack(fill_value0) # 绘制热力图 plt.figure(figsize(14, 8)) sns.heatmap(pivot_data, cmapYlGnBu, annotTrue, fmtd, cbar_kws{label: 点击次数}) plt.title(用户点击行为时间热力图Q4, fontsize14, fontweightbold) plt.xlabel(小时, fontsize12) plt.ylabel(星期0周一, fontsize12) plt.xticks(ticksnp.arange(0.5, 24.5, 1), labelsrange(24), rotation0) plt.yticks(ticksnp.arange(0.5, 7.5, 1), labels[周一, 周二, 周三, 周四, 周五, 周六, 周日]) plt.tight_layout() plt.show() # 输出高峰时段 peak_hour pivot_data.values.argmax() peak_day, peak_hr np.unravel_index(peak_hour, pivot_data.shape) print(f全周峰值{[周一,周二,周三,周四,周五,周六,周日][peak_day]} {peak_hr}:00-{peak_hr1}:00)实操现场记录峰值出现在周五20:00-21:00但周六全天低迷。业务动作立即明确加大周五晚8点的满减券投放暂停周六的推送消息——这就是数据驱动的决策。4.2 Level 3业务成本建模信用卡欺诈检测实战我们使用公开的信用卡交易数据集creditcard.csv284807行492起欺诈演示如何将业务成本注入模型训练。Step 1定义成本敏感的评估指标from sklearn.metrics import confusion_matrix, classification_report import numpy as np def calculate_business_cost(y_true, y_pred): 业务成本计算单位元 - 漏报FN每起欺诈损失20000元 - 误报FP每起误报产生500元人工审核成本 - TP/TN成本为0 tn, fp, fn, tp confusion_matrix(y_true, y_pred).ravel() total_cost fp * 500 fn * 20000 return total_cost # 创建可被GridSearchCV使用的scorer from sklearn.metrics import make_scorer cost_scorer make_scorer(calculate_business_cost, greater_is_betterFalse)Step 2对比传统指标与成本指标的模型选择差异from sklearn.model_selection import train_test_split, GridSearchCV from sklearn.ensemble import RandomForestClassifier from sklearn.linear_model import LogisticRegression # 划分数据 X df.drop(Class, axis1) # Class为欺诈标签0/1 y df[Class] X_train, X_test, y_train, y_test train_test_split(X, y, test_size0.2, random_state42, stratifyy) # 方案A用f1-score调参 param_grid_f1 {n_estimators: [50, 100], max_depth: [3, 5]} clf_f1 RandomForestClassifier(random_state42) grid_f1 GridSearchCV(clf_f1, param_grid_f1, scoringf1, cv3) grid_f1.fit(X_train, y_train) # 方案B用业务成本调参 grid_cost GridSearchCV(clf_f1, param_grid_f1, scoringcost_scorer, cv3) grid_cost.fit(X_train, y_train) # 对比结果 print( F1-score最优参数 ) print(grid_f1.best_params_) print(F1-score:, grid_f1.best_score_) print(\n 业务成本最优参数 ) print(grid_cost.best_params_) print(业务成本:, grid_cost.best_score_) # 在测试集