MLOps Zoomcamp 2022 第 1 周作业实战:基于 NYC FHV 出租车数据训练乘车时长预测模型
2026/9/14 6:24:49 网站建设 项目流程

MLOps Zoomcamp 2022 第 1 周作业实战:基于 NYC FHV 出租车数据训练乘车时长预测模型

【免费下载链接】mlops-zoomcampFree MLOps course from DataTalks.Club. Register here 👇🏼 to get notified about the next cohort项目地址: https://gitcode.com/GitHub_Trending/ml/mlops-zoomcamp

本篇指南完整还原 MLOps Zoomcamp 2022 期第 1 模块(Introduction)的作业 homework.md:以纽约市 For-Hire Vehicle(FHV)出租车 2021 年 1—2 月行程数据为素材,走一遍“下载数据 → 计算时长 → 剔除离群值 → 处理缺失值 → 独热编码 → 训练线性回归 → 跨月验证”的端到端流程。读完后你可以独立完成该作业的全部 6 道题,并理解每个步骤的代码细节与常见陷阱,为后续模块的实验追踪与部署打下基础。

1. 作业目标与数据来源

作业目标与模块示例保持一致:训练一个简单的模型来预测一次行程的时长(duration,单位:分钟),正如 01-intro/README.md 中 “1.3 (Optional) Training a ride duration prediction model” 一节所做的那样,示例代码见 duration-prediction.ipynb。

与模块示例使用的 Green Taxi(绿色出租车)数据不同,2022 期作业换用了同一 NYC 出租车数据集中的 “For-Hire Vehicle Trip Records”(FHV,租车/商务车行程记录),并且有两个关键注意事项(原文档明确要求):

  • 需要下载2021 年 1 月和 2 月的数据,1 月作为训练集,2 月作为验证集;
  • 必须选用 “For-Hire Vehicle Trip Records”,而不是“High Volume For-Hire Vehicle Trip Records”(高量版本),两者字段与统计口径不同。

数据为 parquet 格式(NYC 出租车数据在 2021 年前后已从 CSV 迁移到 parquet,01-intro/README.md 中特别给出了读取 parquet 的视频说明),文件命名为fhv_tripdata_2021-01.parquetfhv_tripdata_2021-02.parquet,解题 notebook homework.ipynb 正是按此路径读取:

df = pd.read_parquet('./data/fhv_tripdata_2021-01.parquet')

依赖库方面,解题 notebook 只用到 pandas、scikit-learn 三件套:

import pandas as pd import seaborn as sns from sklearn.feature_extraction import DictVectorizer from sklearn.linear_model import LinearRegression from sklearn.metrics import mean_squared_error

2. Q1:读取数据并核对记录数

第一步是读入 1 月数据并回答“1 月共有多少条记录”。四个选项为:1054112 / 1154112 / 1254112 / 1354112

old_len = len(df) # 记录过滤前的总行数,供后续统计“丢了多少行”使用

结合解题 notebook 的中间输出可以印证:经过后续 1–60 分钟过滤后,数据剩余1,109,826行(见X_train.shape的输出(1109826, 525)),被剔除的离群值只有数万行量级,因此总量应在 115 万行一档,正确答案是第二个选项1,154,112

3. Q2:计算 duration 变量

duration定义为下车时间减上车时间,并换算为分钟:

df['duration'] = df.dropOff_datetime - df.pickup_datetime df['duration'] = df.duration.dt.total_seconds() / 60

两点细节值得注意:

  • FHV 数据的列名是小驼峰pickup_datetime/dropOff_datetime,与 Green Taxi 数据的lpep_pickup_datetime/lpep_dropoff_datetime(见 duration-prediction.ipynb)不同,换数据集时列名必须跟着换;
  • dt.total_seconds() / 60把时间差(timedelta)转为分钟浮点数。

题目问“1 月的平均行程时长是多少”,选项为 15.16 / 19.16 / 24.16 / 29.16。解题 notebook 中该单元格保存了执行结果:

df.duration.mean() # 输出:19.1672240937939

即正确答案为19.16

4. 数据准备:剔除时长离群值

检查duration的分布(可用sns.histplot等,示例 notebook 曾用sns.distplot绘制预测值与真实值分布的叠加图)可以发现存在离群值。作业要求只保留 duration 在 1 到 60 分钟(含边界)的记录,并回答“丢掉了多少条记录”:

df = df[(df.duration >= 1) & (df.duration <= 60)].copy()

注意两点实现细节:

  • 布尔索引之后加.copy(),避免 pandas 的 SettingWithCopyWarning,也保证后续fillnaastype原地修改的是独立副本;
  • 边界是闭区间>=<=),这与模块示例中的过滤条件一致。

解题 notebook 保存的特征矩阵形状为(1109826, 525),即过滤后剩1,109,826行。由 Q1 的总量 1,154,112 可以推断,这一步约丢弃44,286条记录(总量减去过滤后行数)。

5. Q3:处理缺失值并统计占比

模型只使用两个特征:上车地点 ID(PUlocationID)和下车地点 ID(DOlocationID。FHV 数据中这两列存在大量缺失值,作业要求用-1填充:

categorical = ['PUlocationID', 'DOlocationID'] df[categorical] = df[categorical].fillna(-1).astype('int')

题目问“填充 NA 之后,上车地点 ID 中-1的占比是多少”,选项为 53% / 63% / 73% / 83%。解题 notebook 未保存该单元格的输出,但 FHV 数据的地点 ID 稀疏度明显高于出租车数据(出租车数据缺失极少,模块示例甚至不需要 fillna),结合选项档位可以推断正确答案为53%——即超过一半的 FHV 行程没有上车地点 ID。这一步的占比也可以理解为后续独热编码中-1这一列在特征矩阵里所代表的样本比例。

6. Q4:独热编码与特征矩阵维度

作业要求对两个地点 ID 做 one-hot 编码,步骤固定为三步(这也是课程反复使用的模式):

  1. 把 DataFrame 转成字典列表;
  2. DictVectorizer拟合(fit);
  3. 得到特征矩阵。
df[categorical] = df[categorical].astype('str') train_dicts = df[categorical].to_dict(orient='records') dv = DictVectorizer() X_train = dv.fit_transform(train_dicts) X_train.shape # 输出:(1109826, 525) len(dv.feature_names_) # 输出:525

关键陷阱DictVectorizer遇到数值型键会做 label encoding 而不是 one-hot,所以必须先astype('str')把 ID 转成字符串(解题 notebook 先fillna(-1).astype('int')再统一astype('str'),最终键为'0''1'… 以及'-1'这样的字符串)。2025 期作业 homework.md 在 Q4 中也专门提示了这一点(“remember to re-cast the ids to strings - otherwise it will label encode them”)。

矩阵维度选项为 2 / 152 / 352 / 525 / 725,正确答案是525:训练集(1 月)中出现过的不同上车/下车地点 ID(含填充的-1)去重后共 525 个,每个 ID 独占一列。dv.feature_names_会给出形如DOlocationID=-1DOlocationID=102PUlocationID=...的列名,可用于解释模型权重。

7. Q5:训练线性回归并计算训练集 RMSE

使用默认参数的普通线性回归(Plain Linear Regression):

y_train = df.duration.values lr = LinearRegression() lr.fit(X_train, y_train) y_pred = lr.predict(X_train) mean_squared_error(y_train, y_pred, squared=False) # 输出:10.528519107212292

注意:这里用的是旧版 APImean_squared_error(..., squared=False)来得到 RMSE;较新的 scikit-learn 版本已直接提供root_mean_squared_error(模块示例 duration-prediction.ipynb 导入的正是后者),两者结果等价。训练集 RMSE 选项为 5.52 / 10.52 / 15.52 / 20.52,正确答案为10.52,即模型在训练集上的平均绝对偏差约为 10.5 分钟。

8. Q6:在 2021 年 2 月验证集上评估模型

验证集是 2 月的 FHV 数据。为了保证训练/验证两侧预处理口径一致,解题 notebook 把 Q1–Q4 的步骤封装成了read_data函数:

def read_data(filename): df = pd.read_parquet(filename) df['duration'] = df.dropOff_datetime - df.pickup_datetime df['duration'] = df.duration.dt.total_seconds() / 60 df = df[(df.duration >= 1) & (df.duration <= 60)].copy() df[categorical] = df[categorical].fillna(-1).astype('int').astype('str') return df df_val = read_data('./data/fhv_tripdata_2021-02.parquet')

验证侧的特征转换有一个必须区分的细节:dv.transform而不是dv.fit_transform——词表(525 列)必须沿用训练集学到的,否则 2 月新出现的地点 ID 会额外增加列,导致与模型权重形状不匹配:

val_dicts = df_val[categorical].to_dict(orient='records') X_val = dv.transform(val_dicts) y_pred = lr.predict(X_val) y_val = df_val.duration.values mean_squared_error(y_val, y_pred, squared=False) # 输出:11.014283211122269

验证集 RMSE 选项为 6.01 / 11.01 / 16.01 / 21.01,正确答案为11.01。验证 RMSE(11.01)仅比训练 RMSE(10.53)略高,说明这个极简模型没有明显过拟合——这也正是后续模块要用 MLflow 做实验追踪、用 Kinesis/Lambda 做部署的基线模型。

9. 答案汇总

题目问题选项答案依据
Q11 月 FHV 记录数1054112 / 1154112 / 1254112 / 13541121,154,112由 notebook 过滤后剩余 1,109,826 行印证档位
Q21 月平均时长(分钟)15.16 / 19.16 / 24.16 / 29.1619.16notebook 输出 19.1672
过滤 1–60 分钟丢弃的记录数开放题约 44,286 条1,154,112 − 1,109,826(推断)
Q3上车地点 ID 缺失占比53% / 63% / 73% / 83%53%推断
Q4特征矩阵列数2 / 152 / 352 / 525 / 725525notebook 输出(1109826, 525)
Q5训练集 RMSE5.52 / 10.52 / 15.52 / 20.5210.52notebook 输出 10.5285
Q6验证集 RMSE6.01 / 11.01 / 16.01 / 21.0111.01notebook 输出 11.0143

原始文档提示:如果你的结果与选项不完全一致,选择最接近的一项即可(parquet 版本、pandas 版本差异都可能导致末位小数偏差)。作业提交截止时间为 2022 年 5 月 24 日(周二)23:00 CET,提交表单链接在原文档 homework.md 中给出;官方解题材料为解题 notebook homework.ipynb 及文档中附带的解题视频。

10. 与模块示例及后续 cohort 的对照

  • 模块示例:duration-prediction.ipynb 使用 Green Taxi 2021 年 1—2 月数据,流程与本作业几乎一一对应(读 parquet → 计算 duration → 1–60 分钟过滤 → DictVectorizer → LinearRegression),额外还引入了数值特征trip_distance、交叉特征PU_DO(上车_下车地点组合),并用Lasso(0.01)LinearRegression做对比(验证集 RMSE 分别约为 7.48 与 11.17),最后用pickle.dump((dv, lr), f_out)把向量化器和模型一起序列化保存。本作业刻意只用两个类别特征,是示例的精简版;
  • 2025 期作业:cohorts/2025/01-intro/homework.md 换成了 Yellow Taxi 2023 年 1—2 月数据,问题改为“列数”“标准差”“保留比例”,但 Q4–Q6 的编码、训练、验证流程完全相同,可对照练习;
  • 后续模块的衔接:本作业产出的(DictVectorizer, LinearRegression)组合正是第 4 周批量打分服务(如 cohorts/2022/04-deployment/homework/batch.py)和第 6 周 Kinesis 流式管道(如 cohorts/2022/03-orchestration/homework.py)所加载的模型形态,第 1 周作业实质上是整门课程部署链路的起点。

11. 实践要点清单

  1. 下载数据时认准For-Hire Vehicle Trip Records(非 High Volume 版),只取 2021-01 与 2021-02 两个文件;
  2. duration 用(dropOff_datetime - pickup_datetime).dt.total_seconds() / 60计算,FHV 列名是小驼峰;
  3. 离群值过滤用闭区间1 <= duration <= 60,布尔索引后加.copy()
  4. 缺失 ID 先fillna(-1)astype('int').astype('str'),确保 DictVectorizer 走 one-hot 而非 label encoding;
  5. 训练集用dv.fit_transform,验证集用dv.transform,词表必须来自训练集;
  6. RMSE 在新版 scikit-learn 中用root_mean_squared_error替代mean_squared_error(squared=False)
  7. 结果与选项不一致时选最接近值,差异通常来自数据快照或库版本。

【免费下载链接】mlops-zoomcampFree MLOps course from DataTalks.Club. Register here 👇🏼 to get notified about the next cohort项目地址: https://gitcode.com/GitHub_Trending/ml/mlops-zoomcamp

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询