Flower Swift SDK 实战指南:在 iOS 应用中集成联邦学习客户端
【免费下载链接】flowerFlower: A Friendly Federated AI Framework项目地址: https://gitcode.com/GitHub_Trending/flo/flower
Flower(A Friendly Federated AI Framework)为 Swift 开发者提供了一套原生 SDK,让你可以直接在 iOS 应用中实现联邦学习客户端,通过 gRPC 与 Flower 服务器通信,并借助 CoreML 完成本地模型训练。本文以 framework/swift/flwr/README.md 为主线,结合 SDK 源码(FlwrGRPC、Client协议、ParameterConverter等)与仓库内的 FLiOS 端到端示例,系统讲解 Swift SDK 的安装方式、核心用法、通信与序列化原理,帮助你在自己的 Swift 项目中快速落地联邦学习能力。
一、Swift SDK 概览:它能做什么
flwrSwift SDK 的目标是“无缝地将 Flower 联邦 AI 框架集成进你现有的机器学习项目中”。从 Sources/Flower/flwr.docc/flwr.md 的说明可以看出,SDK 的核心能力分为三块:
Client协议:定义了一个 Flower 客户端必须实现的方法,这是所有客户端实现的统一契约;FlwrGRPC:负责与 Flower 服务器建立 gRPC 双向流连接、收发消息;MLFlwrClient:SDK 附带的默认客户端实现,使用 Apple CoreML 作为本地训练流水线(该实现位于 examples/ios/FLiOS/CoreMLClient/MLFlwrClient.swift,属于示例工程的一部分,而 SDK 包内提供协议与通信基础设施,方便你自行实现定制客户端)。
SDK 本身只要求 iOS 14.0 及以上(包级声明为.iOS(.v16),见 Package.swift),因此可以覆盖绝大多数现代 iOS 设备。
二、安装与集成:将 Swift 包手动接入项目
原文档给出的安装方式非常直接:下载 Flower 项目,然后把 Swift 包手动集成进你的工程。
具体步骤如下:
- 获取 Flower 项目源码(例如
git clone仓库后进入本地副本); - 在 Xcode 中打开你的 iOS 工程,通过File → Add Packages…添加本地包,或直接在
Package.swift中通过.package(path:)指向framework/swift/flwr目录; - 在 target 中声明依赖
.product(name: "flwr", package: "flwr")即可使用。
之所以推荐“本地路径集成”,是因为该包当前并未作为远程 SwiftPM 仓库发布,直接以本地路径引入最稳妥。包本身的 Package.swift 结构如下:
// swift-tools-version: 5.9 import PackageDescription let package = Package( name: "flwr", platforms: [.iOS(.v16)], products: [ .library(name: "flwr", targets: ["flwr"]), ], dependencies: [ .package(url: "https://github.com/pvieito/PythonKit.git", branch: "master"), .package(url: "https://github.com/kewlbear/NumPy-iOS.git", branch: "main"), .package(url: "https://github.com/grpc/grpc-swift.git", from: "1.22.0"), .package(url: "https://github.com/apple/swift-protobuf.git", from: "1.26.0"), ], targets: [ .target( name: "flwr", dependencies: [ .product(name: "GRPC", package: "grpc-swift"), .product(name: "NumPy-iOS", package: "NumPy-iOS"), .product(name: "PythonKit", package: "PythonKit"), .product(name: "SwiftProtobuf", package: "swift-protobuf"), ], path: "Sources/Flower"), .testTarget(name: "FlowerTests", dependencies: ["flwr"]), ] )可见 SDK 对外只暴露一个名为flwr的 library 产物,依赖解析(grpc-swift、NumPy-iOS、PythonKit、swift-protobuf)全部交由 SwiftPM 自动完成。首次构建会拉取这些依赖,请确保网络可达;如果是在模拟器/真机上运行,SwiftPM 会自动为 iOS 平台编译相应架构的依赖。
三、核心用法:三行代码启动联邦学习客户端
原文档给出了一个结构化的最小用法示例,这也是接入 SDK 的标准范式。完整保留如下,并补充逐行注释:
import flwr // 1. 构建一个符合 Client 协议的客户端实例 // - layerWrappers: 模型各层的信息(由 MLModelInspect 解析 .mlmodel 得到) // - dataLoader: 封装训练/测试数据批次(MLBatchProvider) // - compiledModelUrl: 编译后的 CoreML 模型路径(MLModel.compileModel 的产物) let mlFlwrClient = MLFlwrClient( layerWrappers: layerWrappers, dataLoader: dataLoader, compiledModelUrl: compiledModelUrl ) // 2. 创建指向 Flower 服务器的 gRPC 通信对象 let flwrGRPC = FlwrGRPC(serverHost: hostname, serverPort: port) // 3. 启动客户端并挂接完成回调 startFlwrGRPC(client: mlFlwrClient) { // completion handler print("Federated learning completed") }这三个步骤分别对应 SDK 的三个核心抽象:客户端实现(Client)、通信通道(FlwrGRPC)、启动入口(startFlwrGRPC)。其中FlwrGRPC的初始化器签名(见 FlwrGRPC.swift)为:
public init( serverHost: String, serverPort: Int, extendedInterceptor: InterceptorExtension? = nil )serverHost:服务器地址(如"localhost"或局域网 IP);serverPort:服务器监听端口(与 Python 端start_server的端口一致,示例中为8080);extendedInterceptor:可选的自定义 gRPC 拦截器,用于观测/记录通信内容,详见下文第五节。
启动入口有两个重载:
public func startFlwrGRPC(client: Client) public func startFlwrGRPC(client: Client, completion: @escaping () -> Void)不带completion的版本内部等价于传入空闭包。当联邦训练流程结束(服务器发送ReconnectIns/关闭流)时,completion会被调用——仓库示例正是借此把 UI 状态切换为“Federated learning completed”。
3.1 主动断开连接
除了被动等待流程结束,FlwrGRPC还提供了主动断开的方法:
public func abortGRPCConnection( reasonDisconnect: ReasonDisconnect, completion: @escaping () -> Void )它会构造一条携带断开原因(ReasonDisconnect)的DisconnectRes消息发给服务器,然后关闭 gRPC 通道与事件循环组。ReasonDisconnect枚举定义于 Typing.swift,可取值为:unknown(0)、reconnect(1)、powerDisconnected(2)(电量断开)、wifiUnavailable(3)(Wi-Fi 不可用)、ack(4)(确认)。移动端场景下,电量与网络状态是客户端脱离联邦训练的主要原因,SDK 为此预留了语义化的断连原因。
四、Client 协议与消息类型:定制你的联邦客户端
Client协议是 SDK 最核心的抽象,定义于 Client.swift:
public protocol Client { /// Return the current local model parameters. func getParameters() -> GetParametersRes /// Return set of client properties. func getProperties(ins: GetPropertiesIns) -> GetPropertiesRes /// Refine the provided parameters using the locally held dataset. func fit(ins: FitIns) -> FitRes /// Evaluate the provided parameters using the locally held dataset. func evaluate(ins: EvaluateIns) -> EvaluateRes }这四个方法与 Flower 联邦学习协议中的服务器→客户端指令一一对应:服务器下发全局参数请求客户端fit(本地训练)、evaluate(本地评估)、getParameters(上传参数)、getProperties(上报属性)。协议扩展(同文件 L35-L39)为getProperties提供了默认实现——返回Status(code: .getPropertiesNotImplemented, ...),因此你实现的客户端即使不提供属性上报也能编译通过。
4.1 消息类型速查
SDK 在 Typing.swift 中定义了完整的消息结构体,与 Flower 的 protobuf 协议一一对应:
| 类型 | 字段 | 语义 |
|---|---|---|
Parameters | tensors: [Data]、tensorType: String | 模型参数(多个张量的字节流) |
GetParametersRes | parameters、status | 返回本地参数 |
GetPropertiesIns/Res | config/properties、status | 属性请求与响应 |
FitIns | parameters、config | 训练指令(含服务器下发的全局参数与配置) |
FitRes | parameters、numExamples、metrics、status | 训练结果 |
EvaluateIns | parameters、config | 评估指令 |
EvaluateRes | loss、numExamples、metrics、status | 评估结果(损失值) |
Reconnect/Disconnect | seconds/reason | 重连与断连消息 |
Status | code、message | 客户端状态 |
Scalar | bool/bytes/float/int/str | 通用标量值容器 |
其中Metrics与Properties均为[String: Scalar]字典别名;Status.code使用Code枚举(ok、getPropertiesNotImplemented、getParametersNotImplemented、fitNotImplemented、evaluateNotImplemented等),便于服务器识别客户端能力缺失。
4.2 服务器消息的分发逻辑
FlwrGRPC收到服务器消息后,交由 MessageHandler.swift 中的handle(client:serverMsg:)按消息类型分发:
switch serverMsg.msg { case .reconnectIns: // 服务器要求重连/结束 let tuple = reconnect(reconnectMsg: serverMsg.reconnectIns) return (disconnectMsg, sleepDuration, false) case .getParametersIns: return (getParameters(client: client), 0, true) case .fitIns: return (fit(client: client, fitMsg: serverMsg.fitIns), 0, true) case .evaluateIns: return (evaluate(client: client, evaluateMsg: serverMsg.evaluateIns), 0, true) case .getPropertiesIns: return (getProperties(client: client, propertiesMsg: serverMsg.getPropertiesIns), 0, true) default: throw FlowerException.UnknownServerMessage }返回的三元组(ClientMessage, sleepDuration, keepConnection)中:false表示流程结束应关闭连接,true表示保持连接等待下一条指令。特别地,reconnect处理逻辑(同文件 L38-L50)会读取服务器下发的seconds字段:若不为 0,则回执reason = .reconnect并携带睡眠时长;若为 0,则回执reason = .ack表示正常结束。这正好解释了上一节completion回调的触发时机。
五、通信层深度解析:gRPC 双向流与连接配置
FlwrGRPC基于grpc-swift实现客户端到服务器的通信,关键实现见 FlwrGRPC.swift。
5.1 连接初始化
初始化时(L79-L101)会完成以下配置:
- 事件循环组:
PlatformSupport.makeEventLoopGroup(loopCount: 1, networkPreference: .best); - Keepalive 策略:
ClientConnectionKeepalive(interval: .seconds(1000), timeout: .seconds(999), permitWithoutCalls: true, maximumPingsWithoutData: 0)——在无 RPC 调用时也允许发送心跳,间隔 1000 秒、超时 999 秒,适合移动端省电场景; - 传输安全:默认
transportSecurity: .plaintext(明文),即客户端默认以非 TLS 方式连接; - 消息长度上限:
maxMessageLength = 536870912(512 MB),并通过 gRPC 自定义 metadata(maxReceiveMessageLength/maxSendMessageLength)随请求一同下发,保证大模型参数(如大型 CNN 权重)也能完整传输。
5.2 双向流启动
startFlwrGRPC(L116-L134)内部创建Flwr_Proto_FlowerServiceNIOClient,调用join(callOptions:handler:)建立双向流式 RPC:
self.bidirectionalStream = grpcClient.join(callOptions: callOptions, handler: { sm in do { let promise = self.eventLoopGroup.next().makePromise(of: GRPCResponse.self) let response = try handle(client: client, serverMsg: sm) promise.succeed(response) self.sendResponse(future: promise.futureResult, completion: completion) } catch { self.log.error("\(error)") } })收到每条服务器消息后,分发到Client实现并异步等待执行结果,再通过sendResponse(L136-L148)把客户端响应写回流中。整个训练循环(多轮fit/evaluate)都在这一条长连接上完成,completion仅在流程终结时触发一次。
5.3 拦截器机制
SDK 内置了一套 gRPC 拦截器管线,位于 Interceptors.swift 与 InterceptorExtension.swift:
FlowerClientInterceptors会记录收发消息的 metadata、消息类型与文本大小,例如> Sending request FitRes with text size ...;- 同时它把原始 gRPC 消息包装成
GRPCPartWrapper(.metadata(header:)、.message(content:)、.end(status:trailers:)),转发给用户自定义的InterceptorExtension。
这意味着你可以在不改动 SDK 源码的前提下,注入自己的拦截器来观测每个请求/响应。仓库中的 FLiOS 示例正是通过FlwrGRPC(serverHost:serverPort:extendedInterceptor: BenchmarkInterceptor())挂接基准测试拦截器(见 FLiOSModel.swift),用于统计联邦训练的通信耗时。
六、参数序列化:PythonKit + NumPy 桥接 CoreML
联邦学习需要在客户端与服务器之间传递模型权重,而 CoreML 的MLMultiArray无法直接塞进 gRPC 消息。SDK 用ParameterConverter解决了这一问题,实现见 Parameter.swift:
- 单例模式:
ParameterConverter.shared,内部持有一个MultiThreadedEventLoopGroup,并在首次使用时通过PythonSupport.initialize()初始化 Python 运行时、NumPySupport.sitePackagesURL.insertPythonPath()注入 NumPy 搜索路径,再Python.import("numpy")导入 NumPy; - 序列化:
multiArrayToData(multiArray:)把MLMultiArray展平为[Float]后转成 NumPy 数组,np.save到临时文件再读回Data;arrayToData(array:shape:)则直接用makeNumpyArray().reshape(shape); - 反序列化:
dataToMultiArray(data:)与dataToArray(data:)把Data写回临时文件后np.load,再还原为MLMultiArray或[Float]; - 资源释放:
finalize()会调用PythonSupport.finalize()并优雅关闭事件循环组。
也就是说,SDK 通过 PythonKit 在 iOS 上借道 NumPy 完成“字节流 ↔ 张量”的转换,张量形状信息也随序列化过程保留。仓库测试 FlowerTests.swift 验证了[Float] → Data → [Float]的往返一致性(含[1,2,3,4]与浮点精度用例),可作为接入自研模型的参考。
七、依赖项说明
原文档列出了三个关键依赖,结合 Package.swift 实际声明的四个包,整理如下:
| 依赖 | 用途 | 版本约束 |
|---|---|---|
grpc-swift | gRPC 通信框架,提供GRPCChannel、双向流、拦截器等 | from: 1.22.0 |
NumPy-iOS | 在 iOS 上编译 NumPy,供参数序列化使用 | branch: main |
PythonKit | Swift 与 Python 运行时互操作,负责调用 NumPy | branch: master |
swift-protobuf | 编译 Flower protobuf 消息(transport.pb.swift、transport.grpc.swift位于 Sources/Flower/FlowerProto) | from: 1.26.0 |
其中PythonKit与NumPy-iOS采用分支依赖,grpc-swift与swift-protobuf采用版本依赖。需要说明的是:这些依赖在构建时会从各自的远程仓库拉取,PythonKit/NumPy-iOS属于社区维护项目,使用前请留意其自身的许可证与维护状态。
八、端到端实践:FLiOS 示例 + Python 服务器
SDK 的完整落地可以参考仓库中的 examples/ios 示例工程(FLiOS)。它是一套面向研究者的 iOS 联邦学习应用,默认使用 MNIST 数据集与手写数字识别模型,引导用户依次完成“准备训练/测试数据 → 初始化本地客户端 → 连接服务器 → 启动联邦训练”的完整流程。
8.1 启动 Python 联邦服务器
在examples/ios目录下先安装 Python 依赖(poetry install或pip install -r requirements.txt),然后启动服务器:
python3 server.py -c 1 -r 1server.py 中-c/--clients指定最小参与客户端数,-r/--rounds指定联邦轮数,默认各为 1;服务器监听[::]:8080,并自定义了SaveModelStrategy(继承FedAvg),每轮聚合后会把权重保存为round-{rnd}-weights.npz供离线分析。
8.2 在 iOS 端运行
- 用 Xcode 打开
FLiOS.xcodeproj,等待 SwiftPM 拉取依赖后编译运行(可在模拟器或真机部署); - 在 App 内依次点击加载训练集与测试集;
- 在界面文本框中填入服务器的 hostname 与端口(默认
localhost:8080,真机场景应填写运行服务器电脑的局域网 IP); - 点击 Start 启动联邦训练,训练结束会显示 “Federated learning completed”。
8.3 客户端实现样例
示例中 MLFlwrClient.swift 是Client协议的 CoreML 实现,其核心逻辑非常值得借鉴:
getParameters():调用parameters.initializeParameters()后用weightsToParameters()导出本地权重,返回GetParametersRes;fit(ins:):先用parametersToWeights(parameters: ins.parameters)把服务器下发的全局参数写入模型配置,再通过MLUpdateTask在本地数据集上训练(runMLTask(configuration:task: .train)),最后返回更新后的参数与样本数;evaluate(ins:):同样写入参数后在测试集上跑 1 个 epoch(configuration.parameters = [epochs: 1]),返回损失值与样本数。
配合 FLiOSModel.swift 中的startFederatedLearning()可以看到完整调用链:MLModel.compileModel编译模型 →MLModelInspect解析层结构 → 构造MLFlwrClient→FlwrGRPC(serverHost:serverPort:extendedInterceptor:)→startFlwrGRPC(client:completion:)。这与你自己的 App 中接入 SDK 的路径完全一致。
九、许可证
Flower Swift SDK 采用Apache-2.0许可证(详见 LICENSE),你可以自由使用、修改与分发,只需保留原始版权声明与许可条款。
总结
Flower Swift SDK 提供了一条在 iOS 原生环境中参与联邦学习的成熟路径:Client协议统一了客户端行为契约,FlwrGRPC封装了 gRPC 双向流通信,ParameterConverter通过 PythonKit/NumPy 桥接 CoreML 张量与字节流,InterceptorExtension则开放了通信观测能力。参考本文的安装步骤、核心用法与 FLiOS 示例,你可以在自己的 Swift 项目中快速搭建起一个可对接 Flower 服务器的联邦学习客户端,并在此基础上按需定制模型训练流程与通信策略。
【免费下载链接】flowerFlower: A Friendly Federated AI Framework项目地址: https://gitcode.com/GitHub_Trending/flo/flower
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考