从 BigQuery 导出模型到 REST 服务:data-engineering-zoomcamp 的 TensorFlow Serving + Docker 部署实战
【免费下载链接】data-engineering-zoomcampData Engineering Zoomcamp is a free 9-week course on building production-ready data pipelines. Join the course here 👇🏼项目地址: https://gitcode.com/GitHub_Trending/da/data-engineering-zoomcamp
在 Data Engineering Zoomcamp 的「Data Warehousing」模块中,我们不仅用 SQL 在 BigQuery 内部完成了 taxi 小费预测模型的训练、评估与调参,还可以把模型「搬出」数仓:导出到 Cloud Storage、拉取到本地、放进 Docker 容器里用 TensorFlow Serving 提供 REST 预测服务。本篇技术指南以课程文档 06-deploying-a-machine-learning-model.md 为主体,完整拆解「导出 → 复制 → 容器化托管 → HTTP 推理」这条端到端部署链路,并补充仓库内训练脚本与 SQL 文件的源码级佐证。读完你将掌握:如何用bq导出 BigQuery ML 模型、如何按 TensorFlow Serving 的目录约定组织模型文件、如何用一条docker run把模型变成可被curl调用的线上服务。
本篇在课程中的位置:模型从哪里来
在开始部署之前,先明确被部署的对象。本模块的上一单元 05-machine-learning-in-bigquery.md 完成了模型训练:我们以yellow_tripdata_partitioned为基础,把PULocationID、DOLocationID、payment_type三个类别列强转为STRING,构建了 ML 特征表yellow_tripdata_ml,然后用model_type='linear_reg'创建了预测tip_amount(小费金额)的线性回归模型tip_model。这些 SQL 全部收录在 big_query_ml.sql 中,例如:
-- CREATE MODEL WITH DEFAULT SETTING CREATE OR REPLACE MODEL `taxi-rides-ny.nytaxi.tip_model` OPTIONS (model_type='linear_reg', input_label_cols=['tip_amount'], DATA_SPLIT_METHOD='AUTO_SPLIT') AS SELECT * FROM `taxi-rides-ny.nytaxi.yellow_tripdata_ml` WHERE tip_amount IS NOT NULL;本单元就是这个训练的「下半场」:把训练好的tip_model从 BigQuery 中导出,脱离数仓环境,作为独立的服务运行。训练数据与建表语句可进一步参考 big_query.sql(外部表、分区表与聚簇表的构建)和本模块总览 README.md。
部署的完整闭环:一条端到端链路
整条部署流程是一条可复现的命令链,每一步都有明确的工具与产物:
| 阶段 | 工具/载体 | 产物 |
|---|---|---|
| 模型训练 | BigQuery ML(SQL) | taxi-rides-ny.nytaxi.tip_model |
| 模型导出 | bq extract -m | Cloud Storage 桶中的tip_model目录 |
| 本地获取 | gsutil cp -r | 本地/tmp/model/tip_model(SavedModel 结构) |
| 容器托管 | Docker + TensorFlow Serving | 监听8501端口的 REST 服务 |
| 在线推理 | curl/ Postman POST | 每笔行程的predicted_tip_amount |
这个闭环的价值在于:模型以 SQL 形式在数仓内训练,却能以标准 TensorFlow SavedModel 格式输出,最终被通用的模型服务框架接管——训练与推理环境解耦,模型生命周期得以延长到生产环境。
导出模型到 Cloud Storage
首先确保已完成gcloud auth login(视频中已执行),接着用 BigQuery 命令行工具bq将模型提取到 Cloud Storage 桶:
bq --project_id taxi-rides-ny extract -m nytaxi.tip_model gs://taxi_ml_model/tip_model命令要点:
--project_id taxi-rides-ny:指定模型所在的项目 ID;extract:bq的导出子命令,此处针对的是模型(-m),而非表;nytaxi.tip_model:数据集.模型名,即 big_query_ml.sql 中CREATE OR REPLACE MODEL创建的那个模型;gs://taxi_ml_model/tip_model:目标桶与目录,导出完成后桶内会出现同名tip_model文件夹。
将模型复制到本地
导出到 GCS 后,用gsutil把整个目录拉到本地:
mkdir /tmp/model gsutil cp -r gs://taxi_ml_model/tip_model /tmp/modelgsutil cp -r的复制输出恰好揭示了导出的 BigQuery 模型到底是什么:一个标准的 TensorFlow 模型——包含assets、variables两个目录,以及若干元数据文件(如saved_model.pb、fingerprint.pb等.pb结尾的协议缓冲文件)。这正是 TensorFlow SavedModel 的约定布局:variables存放训练得到的权重,assets存放模型附带的资源,saved_model.pb描述模型的计算图与签名(signature)。
理解这一点很关键:BigQuery ML 的线性回归模型在导出时被序列化为 TensorFlow 生态通用的 SavedModel 格式,这意味着后续任何兼容 TensorFlow 的服务框架(包括 TensorFlow Serving)都能无缝加载它。
用 Docker 运行 TensorFlow Serving
TensorFlow Serving 对模型目录有严格的约定:外层目录以「模型名」命名,其下每个子目录对应一个「版本号」,版本号必须是数字。因此我们创建serving_dir/tip_model/1,把模型文件放入版本目录:
mkdir -p serving_dir/tip_model/1 cp -r /tmp/model/tip_model/* serving_dir/tip_model/1 docker pull tensorflow/serving docker run -p 8501:8501 \ --mount type=bind,source=`pwd`/serving_dir/tip_model,target=/models/tip_model \ -e MODEL_NAME=tip_model -t tensorflow/serving &逐步拆解这条docker run:
-p 8501:8501:把容器内 TensorFlow Serving 的 REST API 端口(默认8501)映射到宿主机的同名端口;官方镜像通常同时开放8500供 gRPC 调用,本示例走 REST,故只需映射8501;--mount type=bind,source=$(pwd)/serving_dir/tip_model,target=/models/tip_model:把本地 serving 目录绑定挂载到容器内的/models/tip_model。注意 TensorFlow Serving 默认在/models/<模型名>下加载模型,因此挂载目标路径的末段必须与MODEL_NAME一致;-e MODEL_NAME=tip_model:通过环境变量告诉 TensorFlow Serving 要服务哪个模型,此值必须与目录名(tip_model)完全对应;-t:分配伪终端,方便观察日志;末尾的&让容器在后台运行。
服务目录的位置很灵活,放在项目目录或临时目录均可——关键是目录结构符合「模型名/版本号」的约定。启动后可用docker ps确认容器状态:
检查模型:REST 元数据接口
TensorFlow Serving 暴露了 REST API,模型元数据位于http://localhost:8501/v1/models/tip_model。用 GET 请求(视频中使用 Postman)即可查询模型状态:
curl http://localhost:8501/v1/models/tip_model返回的 JSON 中,model_version_status字段会给出每个版本的状态。响应表明tip_model的版本 1 状态为AVAILABLE——模型已成功加载,没有报错。这一步相当于部署后的「健康检查」:在发起任何预测之前,先用元数据接口确认模型已就绪。
通过 HTTP 发起预测
模型就绪后,真正的预测通过 POST 完成。请求体为 JSON,instances数组中携带与训练时完全一致的列:passenger_count(乘客数)、trip_distance(行程距离)、PULocationID(上车地点 ID)、DOLocationID(下车地点 ID)、payment_type(支付方式)、fare_amount(车费)、tolls_amount(过路费)。对照 big_query_ml.sql 中的特征选择语句,可以看到这 7 个字段正是训练时SELECT出的特征(不含标签tip_amount)。
curl -d '{"instances": [{"passenger_count":1, "trip_distance":12.2, "PULocationID":"193", "DOLocationID":"264", "payment_type":"1","fare_amount":20.4,"tolls_amount":0.0}]}' \ -X POST http://localhost:8501/v1/models/tip_model:predict请求细节说明:
- 端点
:predict是 TensorFlow Serving REST 推理的固定后缀,完整路径为/v1/models/<模型名>:predict; instances是推理负载的容器,可一次传入多条样本;- 类别特征以字符串形式传入(如
"PULocationID":"193"、"payment_type":"1"),与训练阶段强转为STRING的特征类型保持一致——模型在训练时对这三个类别列做了自动 one-hot 编码,推理请求也必须按类别语义传值。
对这一笔行程(1 名乘客、12.2 英里、车费 20.4 美元),模型预测小费约为 3.2 美元:
更有说服力的验证是「扰动输入、观察输出」:把payment_type从1(卡片支付)改为2(现金支付),其余特征不变,再次发送请求,预测小费骤降至约 0.26 美元。这说明模型确实学习到了支付方式对是否给小费的强影响——现金支付场景下小费预测大幅下降,符合直觉,也反向印证了模型在 05-machine-learning-in-bigquery.md 中ML.EXPLAIN_PREDICT的结论:payment_type等类别特征是模型最依赖的信号之一。
部署细节与常见坑
结合仓库代码与上述操作,整理几个容易踩坑、但理解后即可绕过的关键点:
1. 目录命名必须与MODEL_NAME严格一致。容器内默认的模型根目录是/models,挂载目标/models/tip_model的末段、-e MODEL_NAME的值、serving 目录的外层文件夹名三者必须统一为tip_model,否则 TensorFlow Serving 找不到模型。
2. 版本目录必须是数字。TensorFlow Serving 以「版本号目录」区分模型的多版本加载,1、2这样的数字目录会被识别为版本;若目录名不是纯数字,模型无法被正确加载。
3. 端口映射只暴露了 REST。官方镜像默认同时开放8500(gRPC)与8501(HTTP/REST)。本示例只映射了8501,若需要使用 gRPC 客户端,需额外映射8500。
4. 推理请求的特征类型要与训练一致。模型训练时把PULocationID、DOLocationID、payment_type作为类别特征处理(见 big_query_ml.sql 中的CAST(... AS STRING)),推理请求中这三个字段应传字符串而非裸数字,否则输入语义会与训练分布产生偏差。
5. 健康检查先于推理。部署后先用GET /v1/models/tip_model确认版本状态为AVAILABLE,再执行 POST 推理,能显著减少「服务未就绪导致推理失败」的排查成本。
小结:一个完整的模型生命周期闭环
至此,一条完整的链路走通:用 SQL 在 BigQuery 内训练线性回归模型 →bq extract -m导出到 Cloud Storage →gsutil cp拉取到本地 → 按「模型名/版本号」组织目录 →docker run启动 TensorFlow Serving → 通过curl/Postman 调用 REST 接口获得预测结果。
其价值不止于「能跑」:训练环节充分利用数仓内计算(数据无需搬出 BigQuery),推理环节则把模型交付给标准化的容器服务——这正是数据工程中「模型管理」与「模型服务」两个环节的典型衔接方式。若要继续深入,可以查阅本模块 README.md 中的单元列表与作业,把这一部署链路纳入你自己的数据管道练习中。
【免费下载链接】data-engineering-zoomcampData Engineering Zoomcamp is a free 9-week course on building production-ready data pipelines. Join the course here 👇🏼项目地址: https://gitcode.com/GitHub_Trending/da/data-engineering-zoomcamp
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考