PostgresML – PostgreSQL 的机器学习扩展,用于在数据库内部直接训练、部署和推理机器学习模型。
概要
-- 安装扩展
CREATE EXTENSION postgresml;
-- 训练一个模型
SELECT pgml.train(
project_name => '我的项目',
task => 'regression', -- 或 'classification'
relation_name => '训练数据表',
y_column_name => '目标列',
algorithm => 'linear_regression', -- 或 'xgboost', 'random_forest' 等
hyperparams => '{"learning_rate": 0.1}'
);
-- 使用训练好的模型进行预测
SELECT pgml.predict(
project_name => '我的项目',
features => ARRAY[1.0, 2.0, '特征值三']
);
-- 部署一个预训练模型(例如,用于文本处理)
SELECT pgml.deploy(
model_name => 'distilbert-base-uncased-finetuned-sst-2-english',
strategy => 'load'
);
-- 使用预训练模型进行预测
SELECT pgml.predict(
model_name => 'distilbert-base-uncased-finetuned-sst-2-english',
inputs => ARRAY['This movie is fantastic!']
);
描述
PostgresML 是一个开源的 PostgreSQL 扩展,它将机器学习(ML)和人工智能(AI)能力直接带入数据库引擎中。其核心设计理念是“将模型移动到数据旁”,而非传统的“将数据移动到模型旁”。这一范式转变带来了诸多优势,尤其是在数据密集型应用中。
传统机器学习工作流通常涉及一个复杂的数据管道:使用 ETL(Extract, Transform, Load)工具从数据库中提取海量数据,通过网络传输到专门的数据科学环境(如 Python 环境配合 Scikit-learn、TensorFlow 或 PyTorch),进行模型训练和评估,最后再将训练好的模型部署到一个独立的服务中,用于在线推理。这个过程不仅繁琐、延迟高,还带来了数据安全与隐私的挑战。
PostgresML 通过将 ML 功能作为 PostgreSQL 的一部分,极大地简化了这一流程。用户可以直接使用 SQL 语句来:
- 访问数据:无缝利用数据库内的任何表或视图作为训练集。
- 训练模型:调用内置的、经过优化的算法(如线性回归、XGBoost)或加载强大的预训练模型(如 Hugging Face Transformers)进行训练。
- 进行预测:在 SQL 查询中直接调用模型进行实时或批量预测,结果可以像普通数据一样被立即用于
WHERE
子句、JOIN
操作或业务逻辑判断。
本文旨在系统性地介绍 PostgresML 的核心概念、安装配置、工作流程、实战案例以及高级特性,为从初学者到专业研究人员的广大读者提供一份全面且可操作的技术参考。
安装与配置
PostgresML 的推荐安装方式是使用 Docker,这可以避免复杂的依赖问题,并提供一个即开即用的环境。
使用 Docker Compose 快速启动
创建一个名为 docker-compose.yml
的文件,内容如下:
version: '3.8'
services:
postgres:
image: ghcr.io/postgresml/postgresml:2.8.4
ports:
- "5432:5432"
environment:
POSTGRES_USER: pgml_user
POSTGRES_PASSWORD: my_secure_password
POSTGRES_DB: pgml_db
volumes:
- pgml_data:/var/lib/postgresql/data
command: >
postgres
-c shared_preload_libraries=pgml
-c pgml.venv=/opt/venv
-c pgml.decoders=2
volumes:
pgml_data:
文件说明:
image
: 指定了 PostgresML 的官方镜像,其中已预装了 PostgreSQL、PostgresML 扩展以及 Python 机器学习环境。ports
: 将容器的 5432 端口映射到宿主机的 5432 端口,方便连接。environment
: 设置了数据库的用户名、密码和数据库名。volumes
: 创建一个持久化卷pgml_data
,确保数据库重启后数据不会丢失。command
: 这是关键配置。
shared_preload_libraries=pgml
: 告诉 PostgreSQL 在启动时预加载 PostgresML 共享库。pgml.venv=/opt/venv
: 指向容器内的 Python 虚拟环境,PostgresML 将在此环境中安装和管理 Python 依赖(如 Scikit-learn, XGBoost, PyTorch)。pgml.decoders=2
: 预留两个工作进程用于模型推理,可以根据你的 CPU 核心数调整。
在 docker-compose.yml
文件所在目录,执行以下命令启动服务:
docker-compose up -d
连接与验证
服务启动后,使用 psql
命令行工具连接到数据库:
psql -h localhost -U pgml_user -d pgml_db
系统会提示你输入密码 my_secure_password
。
连接成功后,验证 PostgresML 扩展是否已安装并可用:
\dx postgresml
你应该能看到类似以下的输出,表明扩展已正确安装:
List of installed extensions
Name | Version | Schema | Description
-----------+---------+------------+-------------------------------------------------------------------
postgresml| 2.8.4 | public | PostgresML: an in-database machine learning framework
(1 row)
至此,PostgresML 环境已配置完毕,可以开始使用了。
核心概念与工作流
在深入代码之前,理解 PostgresML 的几个核心概念至关重要。
- Project (项目): 一个项目是围绕一个特定业务目标(如“预测客户流失”、“识别垃圾邮件”)的最高层抽象。一个项目下可以包含多个不同算法或超参数训练出的模型。
- Model (模型): 模型是训练任务的产物。每次调用
pgml.train()
都会生成一个新的模型版本,并自动分配一个唯一的 ID。PostgresML 会自动保存模型的快照、训练日志、评估指标和超参数,确保实验的可复现性。 - Deployment (部署): 将一个训练好的模型或一个预训练模型设置为“活动”状态,使其可以通过
pgml.predict()
函数被调用。一个项目一次只能有一个部署的模型。
标准机器学习工作流
PostgresML 的工作流与传统机器学习流程高度一致,但所有步骤都通过 SQL 在数据库内完成。
- 数据准备: 在 PostgreSQL 中创建表并插入/导入数据。数据可以是任何 SQL 可以查询的来源,包括其他表、视图、外部数据包装器(FDW)等。
- 模型训练: 使用
pgml.train()
函数,指定项目名、任务类型、数据源、目标列和算法,来启动一个训练任务。 - 模型评估: 训练完成后,PostgresML 会自动计算并存储评估指标(如回归任务的 R²、MSE,分类任务的准确率、F1 分数)。你可以通过
pgml.inspect_model()
函数查看这些指标,以判断模型性能。 - 模型部署: 从一个项目的多个模型版本中选择性能最好的一个,使用
pgml.deploy()
将其部署为当前服务的模型。 - 模型预测: 在 SQL 查询中,使用
pgml.predict()
函数,传入特征数据,获取模型的预测结果。
实战案例
本节将通过三个由浅入深的案例,演示如何使用 PostgresML 解决实际问题。
案例一:线性回归 - 预测房价
这是一个经典的入门级回归任务。假设我们有一个包含房屋特征和价格的数据集。
步骤 1: 创建并填充数据表
-- 创建一个存储房屋信息的表
CREATE TABLE houses (
id SERIAL PRIMARY KEY,
size NUMERIC, -- 房屋面积(平方米)
bedrooms INTEGER, -- 卧室数量
bathrooms INTEGER, -- 卫生间数量
price NUMERIC -- 房价(单位:万元)
);
-- 插入一些示例数据
INSERT INTO houses (size, bedrooms, bathrooms, price) VALUES
(120, 3, 2, 350),
(80, 2, 1, 220),
(150, 4, 2, 480),
(95, 2, 1, 280),
(200, 5, 3, 650),
(65, 1, 1, 180),
(110, 3, 2, 320),
(180, 4, 3, 560);
步骤 2: 训练线性回归模型
我们将使用 linear_regression
算法来预测 price
。
SELECT pgml.train(
project_name => 'house_price_prediction',
task => 'regression',
relation_name => 'houses',
y_column_name => 'price',
algorithm => 'linear_regression'
);
执行后,你会看到一个输出,包含训练任务的 ID 和状态。PostgresML 会在后台异步执行训练。对于小数据集,这个过程几乎是瞬时的。
步骤 3: 检查模型状态和评估指标
训练完成后,我们可以查看模型详情。
-- 查看项目中所有模型
SELECT * FROM pgml.models WHERE project = 'house_price_prediction' ORDER BY created_at DESC;
-- 查看最新模型的详细信息和评估指标
SELECT pgml.inspect_model(
project_name => 'house_price_prediction',
algorithm => 'linear_regression'
);
pgml.inspect_model
的输出是一个 JSON 对象,包含了丰富的信息,其中 metrics
部分是我们最关心的:
{
"r2": 0.987, -- R-squared,决定系数,越接近1越好
"mse": 245.12, -- Mean Squared Error,均方误差,越小越好
"mae": 12.34, -- Mean Absolute Error,平均绝对误差,越小越好
...
}
步骤 4: 部署模型
虽然对于只有一个模型的项目,PostgresML 会自动部署最新训练的模型,但显式部署是一个好习惯。
SELECT pgml.deploy(
project_name => 'house_price_prediction',
strategy => 'best_score', -- 部署评估分数最高的模型
metric => 'r2' -- 以 R2 分数作为评判标准
);
步骤 5: 进行预测
现在,我们可以用这个模型来预测新房屋的价格了。
-- 预测一个 100 平米,2 卧室,1 卫生间的房子价格
SELECT pgml.predict(
project_name => 'house_price_prediction',
features => ARRAY[100, 2, 1]
) AS predicted_price;
输出结果可能如下:
predicted_price
-----------------
268.54
(1 row)
我们也可以批量预测,这极大地提高了效率:
-- 假设有一个待评估的新房列表
CREATE TABLE new_houses (
id SERIAL PRIMARY KEY,
size NUMERIC,
bedrooms INTEGER,
bathrooms INTEGER
);
INSERT INTO new_houses (size, bedrooms, bathrooms) VALUES
(105, 2, 1),
(175, 4, 2);
-- 批量预测并展示结果
SELECT
nh.id,
nh.size,
nh.bedrooms,
nh.bathrooms,
pgml.predict('house_price_prediction', ARRAY[nh.size, nh.bedrooms, nh.bathrooms]) AS predicted_price
FROM new_houses nh;
案例二:分类任务 - 垃圾邮件识别
这是一个二分类问题。我们将使用 XGBoost 算法,它是一种高效且广泛使用的梯度提升决策树算法。
(梯度提升决策树 Gradient Boosting Decision Tree: 一种集成学习算法,通过迭代地训练一系列弱决策树,每棵新树都试图纠正前一棵树的错误,最终将所有树的结果加权求和得到强预测器。)
步骤 1: 创建并填充数据表
CREATE TABLE emails (
id SERIAL PRIMARY KEY,
subject TEXT,
body TEXT,
is_spam BOOLEAN -- TRUE 表示垃圾邮件
);
-- 插入示例数据
INSERT INTO emails (subject, body, is_spam) VALUES
('恭喜您中奖了!', '点击链接领取您的百万大奖...', TRUE),
('会议通知', '请准时参加明天下午3点的项目评审会议。', FALSE),
('限时优惠', '全场商品一折起,仅限今日!', TRUE),
('项目周报', '附件是本周的项目进展报告,请查收。', FALSE),
('账户安全提醒', '您的账户存在异常,请立即点击链接验证。', TRUE),
('Re: 关于合同', '合同条款已确认,没有问题。', FALSE);
步骤 2: 训练 XGBoost 分类模型
对于文本数据,PostgresML 会自动进行向量化(一种将文本转换为数值特征的过程)。
SELECT pgml.train(
project_name => 'spam_detection',
task => 'classification',
relation_name => 'emails',
y_column_name => 'is_spam',
algorithm => 'xgboost'
);
步骤 3: 检查模型
SELECT pgml.inspect_model('spam_detection', 'xgboost');
在 metrics
中,你会看到分类相关的指标,如 accuracy
(准确率)、precision
(精确率)、recall
(召回率)、f1
(F1分数)和 roc_auc
(ROC曲线下面积)。
(F1分数 F1-Score: 精确率和召回率的调和平均数,是综合评价分类模型性能的指标,尤其在类别不均衡的数据集上更有意义。)
步骤 4: 预测新邮件
-- 预测一封新邮件是否为垃圾邮件
SELECT
pgml.predict(
'spam_detection',
ARRAY['独家福利,内部消息!', '朋友,我有一个稳赚不赔的投资机会...']
) AS is_spam_prediction;
XGBoost 分类器通常输出的是类别的概率。我们可以使用 pgml.predict_proba()
来获取更详细的信息:
SELECT
pgml.predict_proba(
'spam_detection',
ARRAY['独家福利,内部消息!', '朋友,我有一个稳赚不赔的投资机会...']
) AS prediction_probabilities;
输出可能是一个 JSON 数组,表示属于每个类别的概率:
[[0.05, 0.95]] -- [[属于非垃圾邮件的概率, 属于垃圾邮件的概率]]
案例三:使用预训练模型 - 文本情感分析
PostgresML 的强大之处在于其能够集成和部署来自 Hugging Face Hub 的海量预训练模型。这使得复杂的自然语言处理(NLP)任务变得异常简单。我们将使用一个预训练好的 DistilBERT 模型来进行英文情感分析。
(DistilBERT: BERT (Bidirectional Encoder Representations from Transformers) 的一个轻量级、蒸馏版本。它保留了 BERT 97% 的性能,但体积更小、速度更快,非常适合在生产环境中部署。)
步骤 1: 部署预训练模型
我们不需要本地数据来训练,而是直接从 Hugging Face Hub 加载模型。
SELECT pgml.deploy(
model_name => 'distilbert-base-uncased-finetuned-sst-2-english',
strategy => 'load' -- 'load' 策略表示从远程仓库下载并加载模型
);
这个过程可能需要一些时间,因为 PostgresML 需要从网络下载模型文件(通常几百MB)并加载到内存中。你可以通过查询 pgml.deployments
表来检查部署状态。
SELECT * FROM pgml.deployments;
当 status
列变为 successful
时,模型就准备就绪了。
步骤 2: 进行情感预测
创建一个包含待分析文本的表:
CREATE TABLE movie_reviews (
id SERIAL PRIMARY KEY,
review_text TEXT
);
INSERT INTO movie_reviews (review_text) VALUES
('This movie was absolutely brilliant. The acting was superb.'),
('I was really disappointed. The plot was predictable and boring.'),
('It was an okay movie, not great but not terrible either.');
现在,使用部署的模型进行预测:
SELECT
id,
review_text,
pgml.predict(
'distilbert-base-uncased-finetuned-sst-2-english',
ARRAY[review_text]
) AS sentiment
FROM movie_reviews;
输出结果将是模型的分类标签,例如 POSITIVE
或 NEGATIVE
。
id | review_text | sentiment
----+-----------------------------------------------------------+-----------
1 | This movie was absolutely brilliant. The acting was superb. | POSITIVE
2 | I was really disappointed. The plot was predictable... | NEGATIVE
3 | It was an okay movie, not great but not terrible either. | NEUTRAL
(3 rows)
注意: 这个特定的微调模型可能只输出 POSITIVE
和 NEGATIVE
。NEUTRAL
的出现取决于模型的训练数据。要获取每个标签的概率,同样可以使用 pgml.predict_proba
。
这个案例展示了 PostgresML 如何将前沿的深度学习模型以极低的门槛集成到数据库应用中,开发者无需编写任何 Python 代码,仅用 SQL 就能实现强大的 NLP 功能。
高级特性
对于更专业的用户,PostgresML 提供了一系列高级功能来满足复杂的需求。
超参数调优
模型的性能很大程度上依赖于超参数的选择。pgml.train()
函数允许通过 hyperparams
参数传入一个 JSON 对象来指定超参数。
-- 为 XGBoost 模型指定超参数
SELECT pgml.train(
'spam_detection',
task => 'classification',
relation_name => 'emails',
y_column_name => 'is_spam',
algorithm => 'xgboost',
hyperparams => '{
"n_estimators": 200, -- 树的数量
"max_depth": 5, -- 树的最大深度
"learning_rate": 0.05, -- 学习率
"subsample": 0.8 -- 训练每棵树时使用的样本比例
}'::jsonb
);
你可以通过训练多个具有不同超参数的模型,然后比较它们的评估指标,来找到最佳的超参数组合。更高级的自动化超参数搜索(如网格搜索、随机搜索)可以通过编写 pl/pgSQL
函数或外部脚本来实现。
模型版本管理与快照
每次调用 pgml.train()
都会产生一个新的模型版本,并拥有一个唯一的 ID。PostgresML 自动管理这些版本,包括模型文件、代码快照和训练环境。这意味着你可以随时回滚到之前的任何一个版本。
-- 查看项目 'spam_detection' 的所有模型版本
SELECT id, algorithm, created_at, metrics->>'f1' as f1_score
FROM pgml.models
WHERE project = 'spam_detection'
ORDER BY created_at DESC;
-- 假设我们发现 ID 为 15 的旧模型效果更好,可以部署它
SELECT pgml.deploy('spam_detection', model_id => 15);
这种严格的版本控制确保了实验的可复现性和生产环境的稳定性。
与 PostgreSQL 生态的深度集成
PostgresML 不是一个孤岛,它可以与 PostgreSQL 的其他强大功能无缝协作。
- 在
pl/pgSQL
函数中使用: 你可以将预测逻辑封装在数据库函数中,便于业务逻辑调用。
CREATE OR REPLACE FUNCTION is_spam(subject TEXT, body TEXT)
RETURNS BOOLEAN AS $$
BEGIN
RETURN pgml.predict('spam_detection', ARRAY[subject, body])::BOOLEAN;
END;
$$ LANGUAGE plpgsql;
-- 直接调用函数
SELECT is_spam('free money', 'click here');
- 物化视图缓存预测结果: 对于不经常变化但需要频繁查询的预测结果,可以使用物化视图来缓存,避免重复计算。
CREATE MATERIALIZED VIEW customer_churn_predictions AS
SELECT
customer_id,
pgml.predict('churn_model', ARRAY[age, tenure, monthly_charges]) AS churn_probability
FROM customers;
-- 查询时非常快
SELECT * FROM customer_churn_predictions WHERE churn_probability > 0.8;
-- 当底层数据更新后,可以刷新物化视图
REFRESH MATERIALIZED VIEW customer_churn_predictions;
- 结合
pg_cron
定期重训模型: 可以使用pg_cron
扩展来定期(如每天凌晨)自动重训模型,以适应数据分布的变化。
-- 首先安装 pg_cron: CREATE EXTENSION pg_cron;
-- 每天凌晨 3 点重训房价预测模型
SELECT cron.schedule(
'0 3 * * *', -- cron 表达式
$$SELECT pgml.train('house_price_prediction', 'regression', 'houses', 'price', 'linear_regression')$$
);
性能考量与最佳实践
为了在生产环境中高效地使用 PostgresML,需要考虑以下几点:
- 硬件资源:
- CPU: 模型训练和推理都是计算密集型任务。更多的 CPU 核心可以显著加速训练过程(许多算法支持并行化)。
- 内存: 大型模型(尤其是深度学习模型)需要大量内存来加载。确保数据库服务器有足够的 RAM。
- GPU: 对于深度学习模型,GPU 能提供数十倍甚至上百倍的加速。PostgresML 支持 NVIDIA GPU,需要在配置中启用并安装相应的 CUDA 驱动和库。
- 数据准备:
- 数据类型: 使用最高效的数据类型。例如,对于整数列,使用
INTEGER
或BIGINT
而非NUMERIC
。 - 索引: 为训练和预测查询中频繁使用的列(特别是
WHERE
子句和JOIN
条件中的列)创建索引,可以加速数据检索。 - 数据清洗: 在训练前,处理好缺失值、异常值。虽然某些算法能处理缺失值,但主动清洗通常效果更好。
- 预测策略:
- 批量预测: 尽可能使用批量预测(
SELECT pgml.predict(...) FROM my_table;
),而不是在应用代码中循环进行单次预测。批量预测减少了函数调用的开销,并且 PostgresML 可以进行内部优化。 - 异步预测: 对于非实时的预测任务,可以将预测请求存入一个队列表,然后由后台工作进程批量处理,并将结果写回另一个表。
- 数据库负载:
- 大规模的模型训练会消耗大量 CPU 和 I/O 资源,可能影响数据库上其他事务的性能。建议在业务低峰期(如夜间)进行重训。
- 考虑使用读写分离架构,将模型训练任务放在一个专用的只读副本上执行。
局限性与未来展望
局限性
- 算法覆盖范围: 尽管涵盖了最常用的算法,但与 Python 丰富的生态系统(如 Scikit-learn, PyTorch)相比,PostgresML 内置的算法种类仍然有限。对于非常前沿或特定的模型架构,可能仍需依赖外部环境。
- 自定义灵活性: 在 PostgresML 内部实现高度自定义的特征工程流程或模型结构相对困难。它的优势在于标准化和高效的流程,而非研究级的灵活性。
- 单机扩展性: 目前,PostgresML 的训练主要在单个数据库实例上运行。对于超大规模数据集(TB级别),其扩展性可能不如分布式计算框架(如 Spark MLlib)。
未来展望
PostgresML 作为一个活跃的开源项目,正在快速发展。未来的方向可能包括:
- 更广泛的算法支持: 集成更多来自 Python 生态的成熟算法。
- 增强的 GPU 支持: 优化 GPU 在训练和推理中的使用,支持更多类型的深度学习模型。
- 分布式训练: 探索与 PostgreSQL 的分布式解决方案(如 Citus)结合,实现跨节点的分布式模型训练。
- MLOps 集成: 提供更完善的模型监控、漂移检测和自动化 CI/CD 流程。
参见
- PostgresML 官方文档: <https://postgresml.org/docs/>
- PostgresML GitHub 仓库: <https://github.com/postgresml/postgresml>
- PostgreSQL 官方文档: <https://www.postgresql.org/docs/>
- Hugging Face Hub: <https://huggingface.co/models>
作者
本文由 PostgresML 社区贡献者编写,旨在推广数据库内机器学习的理念与实践。