用 Python 跑通第一个 Flink ML 项目KMeans 聚类从本地到集群实战

一、你将实现什么?

我们要实现的目标是:

使用 PyFlink ML 中的 KMeans 聚类算法 ,对一组二维向量进行聚类,并输出:
Features: [x, y] Cluster Id: n

核心流程包括:

  1. 使用 Python 编写 Flink ML 程序
  2. 构造训练 + 预测用的数据表(Table)
  3. 配置并训练 KMeans 模型
  4. 使用模型对数据进行预测
  5. 本地运行(python kmeans_example.py
  6. 提交到 Flink 集群运行(flink run -py kmeans_example.py

二、环境准备

1. 基本要求

要跟着本文操作,需要一台具备以下环境的机器:

  • Java 8(Flink 运行在 JVM 上)
  • Python 3.7 或 3.8(当前 Flink ML Python SDK 要求的版本)
  • 能正常使用 pip

Flink ML Python SDK 已经发布到 PyPI,上来先安装它:

bash 复制代码
python -m pip install apache-flink-ml==2.3.0

提示:如果有多个 Python 版本,确保用的是你想运行脚本的那一个(比如 python3 / pip3)。

下面我们一步一步搭建一个最小可运行示例。

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

KMeansKMeansModel 都有 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))

确保安装好了 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 聚类对这些样本的"自动分组"。

本地 mini cluster 跑起来只是第一步,更实际的场景是把同样的 Python 程序跑在真正的 Flink 集群上。

首先准备 Flink 本身(本文以 1.17 为例):

bash 复制代码
# 下载 flink-1.17.x 对应的 tgz 之后
tar -xzf flink-*.tgz
cd ${path_to_flink}

确认本机 Java 版本符合要求:

bash 复制代码
java -version

确保是 Java 8 或以上。

在 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/flinkbin/start-cluster.sh 等脚本。

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 正常运行。

确保 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 示例,覆盖了:

  1. 安装 Flink ML Python SDK(apache-flink-ml==2.3.0

  2. 用 PyFlink 编写 Flink ML 程序:

    • 创建 StreamExecutionEnvironmentStreamTableEnvironment
    • 构造特征向量输入表
    • 配置 KMeans 算法、训练模型、执行预测
    • 调用 execute_and_collect() 收集并打印结果
  3. 在本地直接运行 Python 脚本

  4. 下载并配置 Flink 集群,部署 Flink Python & Flink ML 依赖

  5. 使用 flink run -py 在 Standalone 集群上运行同一份 Python 程序,并在 Dashboard 中观察作业

接下来你可以尝试:

  • from_collection 换成 Kafka / 文件 / 数据库 作为输入源;
  • 改动 k 的值,观察不同簇数对聚类结果的影响;
  • 引入其他 Flink ML 算法(分类、回归等),写更多 Python 示例;
  • 把这个示例改造成实时在线预测服务------例如从实时日志流中提取特征,做在线聚类或异常检测。
相关推荐
Wiktok2 小时前
【WIT】解决导入pywinauto相关库会导致程序UI界面(tkinter/pyside6)浏览文件等操作卡住问题
python·ui·pywinauto
Hello.Reader2 小时前
Flink SQL 的 LIMIT 子句语义、坑点与实战技巧
sql·flink·wpf
Hello.Reader2 小时前
Flink SQL 集合运算UNION / INTERSECT / EXCEPT 以及 IN / EXISTS 在流式场景下怎么用?
数据库·sql·flink
噔噔噔噔@3 小时前
详细介绍Python+Pytest+BDD+Playwright,用FSM打造高效测试框架
开发语言·python·pytest
sg_knight3 小时前
什么是设计模式?为什么 Python 也需要设计模式
开发语言·python·设计模式
零日失眠者3 小时前
【Python好用到哭的库】pandas-数据分析神器
后端·python·ai编程
零日失眠者3 小时前
【Python好用到哭的库】numpy-数值计算基础
后端·python·ai编程
创新技术阁3 小时前
CryptoAiAdmin 项目后端启动过程详解
后端·python·fastapi
WXG10113 小时前
【Flask-8】程序打包
开发语言·python