一、你将实现什么?
我们要实现的目标是:
使用 PyFlink ML 中的 KMeans 聚类算法 ,对一组二维向量进行聚类,并输出:
Features: [x, y] Cluster Id: n
核心流程包括:
- 使用 Python 编写 Flink ML 程序
- 构造训练 + 预测用的数据表(Table)
- 配置并训练 KMeans 模型
- 使用模型对数据进行预测
- 本地运行(
python kmeans_example.py) - 提交到 Flink 集群运行(
flink run -py kmeans_example.py)
二、环境准备
1. 基本要求
要跟着本文操作,需要一台具备以下环境的机器:
- Java 8(Flink 运行在 JVM 上)
- Python 3.7 或 3.8(当前 Flink ML Python SDK 要求的版本)
- 能正常使用
pip
2. 安装 Flink ML Python SDK
Flink ML Python SDK 已经发布到 PyPI,上来先安装它:
bash
python -m pip install apache-flink-ml==2.3.0
提示:如果有多个 Python 版本,确保用的是你想运行脚本的那一个(比如
python3/pip3)。
三、用 Python 编写 Flink ML KMeans 程序
下面我们一步一步搭建一个最小可运行示例。
1. 创建执行环境和 Table 环境
Flink ML 的程序,从 Python 这边来看,第一步和 Java 思路类似:先创建 流执行环境 和 表环境。
python
from pyflink.datastream import StreamExecutionEnvironment
from pyflink.table import StreamTableEnvironment
# 创建一个新的 StreamExecutionEnvironment
env = StreamExecutionEnvironment.get_execution_environment()
# 创建一个 StreamTableEnvironment
t_env = StreamTableEnvironment.create(env)
StreamExecutionEnvironment:控制 Flink 作业的执行(流处理的"地基")。StreamTableEnvironment:在流环境基础上提供 Table API 的能力,Flink ML 底层就是基于 Table API 的。
2. 构造训练 + 预测数据表
接下来我们构造一批二维向量数据,用于训练和预测。
注意这里使用的是 Flink ML 内置的向量类型 DenseVector。
python
from pyflink.common import Types
from pyflink.ml.linalg import Vectors, DenseVectorTypeInfo
# 生成输入数据,对应后面 KMeans 的 features 列
input_data = t_env.from_data_stream(
env.from_collection([
(Vectors.dense([0.0, 0.0]),),
(Vectors.dense([0.0, 0.3]),),
(Vectors.dense([0.3, 3.0]),),
(Vectors.dense([9.0, 0.0]),),
(Vectors.dense([9.0, 0.6]),),
(Vectors.dense([9.6, 0.0]),),
],
type_info=Types.ROW_NAMED(
['features'], # 列名为 "features"
[DenseVectorTypeInfo()] # 列类型为 DenseVector
)))
这里有几个点值得注意:
env.from_collection([...]):从 Python 本地集合构造一个 DataStream。- 每条记录都是形如
(Vectors.dense([...]),)的 单元素 tuple,因为我们只有一列。 Types.ROW_NAMED(['features'], [DenseVectorTypeInfo()]):
给这列字段起名features,并指明该列类型是DenseVector。
这一步很关键,后面 KMeans 要通过列名找到特征列。
最终 input_data 是一个 Table,只有一列 features,每行是一个二维向量,例如 [0.0, 0.3]。
3. 创建、配置、训练 KMeans 模型
引入 KMeans:
python
from pyflink.ml.clustering.kmeans import KMeans
# 创建一个 KMeans 对象并初始化参数
kmeans = KMeans().set_k(2).set_seed(1)
# 训练 KMeans 模型
model = kmeans.fit(input_data)
解释一下:
set_k(2):聚成 2 个簇(cluster)。set_seed(1):设定随机种子,方便结果复现。fit(input_data):在input_data这张表上做训练,返回KMeansModel。
KMeans 和 KMeansModel 都有 getter/setter 用来配置算法参数。
当你调用 fit() 的时候,KMeansModel 会继承 KMeans 上设置的参数,因此大部分配置只需要在 KMeans 对象上设置一次即可。
4. 使用模型进行预测
训练完成后,我们对同一批数据做一次预测:
python
# 使用训练好的 KMeans 模型进行预测
output = model.transform(input_data)[0]
这里 transform 返回的是一个 Table 列表(通常第一个就是我们要的结果),所以用 [0] 取出。
输出表 output 中:
- 包含原来的特征列(默认为
features), - 还会包含一个预测结果列(默认列名可以通过
kmeans.get_prediction_col()获取),里面是对应的 簇 ID。
5. 收集并打印结果
和所有 Flink 程序一样,上面写的代码只是构建了执行计划 ,真正执行要通过 execute_and_collect() 来触发。这里我们把结果从 Table 转为 DataStream,再一条条打印出来:
python
# 提取并展示结果
field_names = output.get_schema().get_field_names()
for result in t_env.to_data_stream(output).execute_and_collect():
features = result[field_names.index(kmeans.get_features_col())]
cluster_id = result[field_names.index(kmeans.get_prediction_col())]
print('Features: ' + str(features) + ' \tCluster Id: ' + str(cluster_id))
output.get_schema().get_field_names():拿到所有列名。kmeans.get_features_col()/kmeans.get_prediction_col():
获取特征列 / 预测列的列名(避免硬编码)。execute_and_collect():
会在本地启动一个 mini cluster 执行作业,并将结果通过 Iterator 的方式拉回来。
四、完整 Python 示例代码
把以上所有片段拼起来,就是完整的 kmeans_example.py:
python
from pyflink.common import Types
from pyflink.datastream import StreamExecutionEnvironment
from pyflink.ml.linalg import Vectors, DenseVectorTypeInfo
from pyflink.ml.clustering.kmeans import KMeans
from pyflink.table import StreamTableEnvironment
# 创建一个新的 StreamExecutionEnvironment
env = StreamExecutionEnvironment.get_execution_environment()
# 创建一个 StreamTableEnvironment
t_env = StreamTableEnvironment.create(env)
# 生成输入数据
input_data = t_env.from_data_stream(
env.from_collection([
(Vectors.dense([0.0, 0.0]),),
(Vectors.dense([0.0, 0.3]),),
(Vectors.dense([0.3, 3.0]),),
(Vectors.dense([9.0, 0.0]),),
(Vectors.dense([9.0, 0.6]),),
(Vectors.dense([9.6, 0.0]),),
],
type_info=Types.ROW_NAMED(
['features'],
[DenseVectorTypeInfo()])))
# 创建一个 kmeans 对象并初始化其参数
kmeans = KMeans().set_k(2).set_seed(1)
# 训练 kmeans 模型
model = kmeans.fit(input_data)
# 使用 kmeans 模型进行预测
output = model.transform(input_data)[0]
# 提取并展示结果
field_names = output.get_schema().get_field_names()
for result in t_env.to_data_stream(output).execute_and_collect():
features = result[field_names.index(kmeans.get_features_col())]
cluster_id = result[field_names.index(kmeans.get_prediction_col())]
print('Features: ' + str(features) + ' \tCluster Id: ' + str(cluster_id))
五、本地运行 Flink ML Python 程序
确保安装好了 apache-flink-ml==2.3.0,然后在保存好 kmeans_example.py 的目录下执行:
bash
python kmeans_example.py
这条命令会:
- 在本地启动一个 mini Flink 集群
- 提交并执行我们刚刚写的 KMeans 程序
- 将预测结果打印到终端
你会看到类似下面的输出(顺序可能略有不同):
text
Features: [9.6,0.0] Cluster Id: 0
Features: [9.0,0.6] Cluster Id: 0
Features: [0.0,0.3] Cluster Id: 1
Features: [0.0,0.0] Cluster Id: 1
Features: [0.3,3.0] Cluster Id: 1
Features: [9.0,0.0] Cluster Id: 0
从结果可以看出:
- 一簇(Cluster 0):大概是那些
x约等于 9 的点 - 另一簇(Cluster 1):大部分是
x较小或者y较大的点
这就是 KMeans 聚类对这些样本的"自动分组"。
六、在 Flink Standalone 集群上运行同一个 Python 程序
本地 mini cluster 跑起来只是第一步,更实际的场景是把同样的 Python 程序跑在真正的 Flink 集群上。
1. 安装并解压 Flink
首先准备 Flink 本身(本文以 1.17 为例):
bash
# 下载 flink-1.17.x 对应的 tgz 之后
tar -xzf flink-*.tgz
cd ${path_to_flink}
确认本机 Java 版本符合要求:
bash
java -version
确保是 Java 8 或以上。
2. 配置 Flink Python 支持与环境变量
在 Flink 解压目录中执行:
bash
cd ${path_to_flink}
cp opt/flink-python* lib/
export FLINK_HOME=`pwd`
cp opt/flink-python* lib/:把 Flink 的 Python 相关 jar 拷贝到lib/中,以便集群启动时加载。- 设置
FLINK_HOME,后面方便使用bin/flink、bin/start-cluster.sh等脚本。
3. 将 Flink ML jar 拷贝到 Flink lib 目录
Flink ML 的底层 jar 也需要部署到 Flink 集群的 lib/ 下:
bash
tar -xzf apache-flink-ml*.tar.gz
cp apache-flink-ml-*/deps/lib/* $FLINK_HOME/lib/
这一步相当于给 Flink 集群装上 Flink ML 的"插件"。
4. 启动 Standalone 集群
在 Flink 根目录执行:
bash
$FLINK_HOME/bin/start-cluster.sh
启动成功后,在浏览器访问:
text
http://localhost:8081
可以看到 Flink Dashboard,确认 JobManager / TaskManager 正常运行。
5. 提交 Python Flink ML 作业
确保 kmeans_example.py 在某个目录(比如你的 home 目录或项目目录),然后执行:
bash
$FLINK_HOME/bin/flink run -py kmeans_example.py
这里的关键点是:
- 使用
flink run命令 - 用
-py参数指定 Python 文件(而不是传一个.jar)
终端会输出类似:
text
Features: [9.6,0.0] Cluster Id: 0
Features: [9.0,0.6] Cluster Id: 0
Features: [0.0,0.3] Cluster Id: 1
Features: [0.0,0.0] Cluster Id: 1
Features: [0.3,3.0] Cluster Id: 1
Features: [9.0,0.0] Cluster Id: 0
同时在 Flink Dashboard 上也可以看到对应 Job 的运行情况。
6. 停止 Standalone 集群
执行完实验后,可以关闭 Flink 集群:
bash
$FLINK_HOME/bin/stop-cluster.sh
七、小结与下一步
这篇文章用 Python 从零构建并运行了一个完整的 Flink ML KMeans 示例,覆盖了:
-
安装 Flink ML Python SDK(
apache-flink-ml==2.3.0) -
用 PyFlink 编写 Flink ML 程序:
- 创建
StreamExecutionEnvironment和StreamTableEnvironment - 构造特征向量输入表
- 配置 KMeans 算法、训练模型、执行预测
- 调用
execute_and_collect()收集并打印结果
- 创建
-
在本地直接运行 Python 脚本
-
下载并配置 Flink 集群,部署 Flink Python & Flink ML 依赖
-
使用
flink run -py在 Standalone 集群上运行同一份 Python 程序,并在 Dashboard 中观察作业
接下来你可以尝试:
- 把
from_collection换成 Kafka / 文件 / 数据库 作为输入源; - 改动
k的值,观察不同簇数对聚类结果的影响; - 引入其他 Flink ML 算法(分类、回归等),写更多 Python 示例;
- 把这个示例改造成实时在线预测服务------例如从实时日志流中提取特征,做在线聚类或异常检测。