Highlight
Create ML 在 2020 年新增了 Action Classification(动作分类)模板。它基于 Vision framework 的 Body Pose Estimation(身体姿态估计)提取视频中的骨骼关键点,然后用这些关键点训练一个分类模型。
核心内容
以前,Create ML 已经能用运动传感器数据训练 Activity Classification(活动分类)模型。问题是,很多动作并没有手表或传感器输入,只有手机、摄像头或一段视频。健身动作、体操训练、舞蹈动作都属于这类场景。
Action Classification(动作分类)把输入换成视频里的身体姿态序列。Vision framework 先从每一帧提取人体姿态,Create ML 再把一段时间窗口内的姿态序列训练成分类模型。模型的输出是预定义动作标签,例如 jumping jacks、lunges、squats。
这套流程分成两段。训练阶段,开发者在 Create ML app 里准备按动作分类的视频,设置 Action Duration(动作持续时间)和 Augmentations(数据增强),然后导出 .mlmodel。运行阶段,app 用 Vision 从相机帧或视频帧提取姿态,把连续 60 帧拼成模型输入,再得到动作标签和置信度。
Session 也明确了边界:这个模型由 Vision 的人体姿态估计驱动,适合人的身体动作;它不适合识别动物或物体动作。训练数据也要围绕一个人、完整身体、稳定镜头和一致帧率来准备。
详细内容
用文件夹或标注文件准备训练数据
(03:45)训练数据的基本形态是一组视频文件。每个视频只包含一种动作,文件夹名就是模型要预测的标签。Session 的例子收集了 jumping jacks、squats 等五类运动,还加入了 other 和 none 两个负类。
(05:28)如果一段 montage video(混剪视频)里包含多个动作,就用 CSV 或 JSON 标出每个动作的起止时间。官方代码片段给了 JSON 结构:
[
{
"file_name": "Montage1.mov",
"label": "Squats",
"start_time": 4.5,
"end_time": 8
}
]
关键点:
file_name指向包含动作片段的视频文件。label是这一段视频对应的动作类别,会成为模型预测的标签。start_time和end_time标出动作在视频中的时间范围。- 这种方式适合别人已经剪好或从网上取得的混剪素材。
在 Create ML 里设置动作窗口
(07:52)Action Duration 是训练时最关键的参数之一。复杂动作需要更长窗口,例如舞蹈动作可能需要十秒;短而重复的健身动作可以用两秒窗口。Create ML 会根据视频帧率和动作持续时间算出帧数,Session 的健身模型使用 60 帧作为 prediction window(预测窗口)。
(08:39)Augmentations 用现有视频生成训练变化。Session 演示了 horizontal flip(水平翻转):如果训练视频里人物都朝左,水平翻转会生成镜像样本,让模型覆盖两个方向。
(09:40)训练分成两步。第一步是 feature extraction(特征提取),Vision 会分析每一帧,并编码身体上 18 个 landmarks(关键点)的位置。每个关键点记录 x 坐标、y 坐标和 confidence(置信度)。第二步才是用这些姿态特征训练分类模型。
用 Vision 从视频或图片中提取姿态
(14:05)模型接收 Vision 提取出来的人体姿态,原始视频先经过姿态提取。最小入口是 VNDetectHumanBodyPoseRequest:
import Vision
let request = VNDetectHumanBodyPoseRequest()
关键点:
import Vision引入人体姿态估计所在的框架。VNDetectHumanBodyPoseRequest创建身体姿态请求,用来检测视频帧或图片中的人体关键点。
(14:10)处理一个视频文件时,可以把这个 request 交给 VNVideoProcessor:
import Vision
let videoURL = URL(fileURLWithPath: "your-video-file.MOV")
let startTime = CMTime.zero
let endTime = CMTime.indefinite
let request = VNDetectHumanBodyPoseRequest(completionHandler: { request, error in
let poses = request.results as! [VNRecognizedPointsObservation]
})
let processor = VNVideoProcessor(url: videoURL)
try processor.add(request)
try processor.analyze(with: CMTimeRange(start: startTime, end: endTime))
关键点:
videoURL指向要分析的视频文件。startTime和endTime定义分析范围;这里从开头分析到视频结束。- completion handler 里从
request.results取得[VNRecognizedPointsObservation]。 VNVideoProcessor负责把 request 应用到视频帧。analyze(with:)按给定时间范围执行分析。
(14:26)处理图片或相机帧时,Session 使用 VNImageRequestHandler:
import Vision
let request = VNDetectHumanBodyPoseRequest()
// Use either one from image URL, CVPixelBuffer, CMSampleBuffer, CGImage, CIImage, etc. in image request handler, based on the context.
let handler = VNImageRequestHandler(url: URL(fileURLWithPath: "your-image.jpg"))
try handler.perform([request])
let poses = request.results as! [VNRecognizedPointsObservation]
关键点:
- 同一个
VNDetectHumanBodyPoseRequest可以用于图片输入。 VNImageRequestHandler的输入可以来自 image URL,也可以来自CVPixelBuffer、CMSampleBuffer、CGImage、CIImage。perform([request])执行姿态检测。- 结果同样是
[VNRecognizedPointsObservation]。
把连续姿态拼成模型输入
(14:57)Create ML 导出的动作分类模型接收的是一个三维 multi-array。每一帧姿态先转成 [1, 3, 18],60 帧再拼成 [60, 3, 18]:
import Vision
import CoreML
// Assume pose1, pose2, ..., have been obtained from a video file or camera stream.
let pose1: VNRecognizedPointsObservation
let pose2: VNRecognizedPointsObservation
// ...
// Get a [1, 3, 18] dimension multi-array for each frame
let poseArray1 = try pose1.keypointsMultiArray()
let poseArray2 = try pose2.keypointsMultiArray()
// ...
// Get a [60, 3, 18] dimension prediction window from 60 frames
let modelInput = MLMultiArray(concatenating: [poseArray1, poseArray2], axis: 0, dataType: .float)
关键点:
keypointsMultiArray()是 Vision 提供的便捷 API,把一帧姿态转成模型需要的 multi-array。- 单帧数组维度是
[1, 3, 18],对应一个时间点、三个数值通道和 18 个身体关键点。 MLMultiArray(concatenating:axis:dataType:)沿时间轴拼接多帧姿态。- 训练时窗口大小是 60,运行时也要准备 60 帧窗口。
在 app 里维护窗口并预测
(16:27)Xcode demo 把姿态提取、窗口缓存和预测封装在 Predictor 里:
import Foundation
import CoreML
import Vision
@available(iOS 14.0, *)
class Predictor {
/// Fitness classifier model.
let fitnessClassifier = FitnessClassifier()
/// Vision body pose request.
let humanBodyPoseRequest = VNDetectHumanBodyPoseRequest()
/// A rotation window to save the last 60 poses from past 2 seconds.
var posesWindow: [VNRecognizedPointsObservation?] = []
init() {
posesWindow.reserveCapacity(predictionWindowSize)
}
/// Extracts poses from a frame.
func processFrame(_ samplebuffer: CMSampleBuffer) throws -> [VNRecognizedPointsObservation] {
// Perform Vision body pose request
let framePoses = extractPoses(from: samplebuffer)
// Select the most promiment person.
let pose = try selectMostProminentPerson(from: framePoses)
// Add the pose to window
posesWindow.append(pose)
return framePoses
}
// Make a prediction when window is full, periodically
var isReadyToMakePrediction: Bool {
posesWindow.count == predictionWindowSize
}
/// Make a model prediction on a window.
func makePrediction() throws -> PredictionOutput {
// Prepare model input: convert each pose to a multi-array, and concatenate multi-arrays.
let poseMultiArrays: [MLMultiArray] = try posesWindow.map { person in
guard let person = person else {
// Pad 0s when no person detected.
return zeroPaddedMultiArray()
}
return try person.keypointsMultiArray()
}
let modelInput = MLMultiArray(concatenating: poseMultiArrays, axis: 0, dataType: .float)
// Perform prediction
let predictions = try fitnessClassifier.prediction(poses: modelInput)
// Reset poses window
posesWindow.removeFirst(predictionInterval)
return (
label: predictions.label,
confidence: predictions.labelProbabilities[predictions.label]!
)
}
}
关键点:
FitnessClassifier()是从 Create ML 导出的 Core ML 模型。humanBodyPoseRequest只初始化一次,Session 建议避免每帧重复创建。posesWindow保存最近 60 帧姿态,对应训练时的两秒窗口。processFrame(_:)从CMSampleBuffer中提取姿态,并选出一个最显著的人。- Action Classifier 只接收单人输入;多人画面需要自己实现选择逻辑。
- 没检测到人时,示例用
zeroPaddedMultiArray()补零。 fitnessClassifier.prediction(poses:)返回预测标签和各类别置信度。removeFirst(predictionInterval)让窗口向前滑动,方便连续预测。
训练和使用时的限制
(22:00)模型需要 repetition and variety(重复和多样性)。Session 建议每个动作大约准备 50 个视频,并覆盖不同人的风格、能力和速度。如果用户会从侧面或背面运动,训练数据也要包含这些角度。
(23:35)Action Duration 要匹配动作长度,各类动作时长也要尽量接近。视频帧率会影响实际窗口长度,所以训练和测试视频的平均帧率需要保持一致。
(24:21)运行时只选择一个人。如果 Vision 检测到多个人,app 可以提醒用户只保留一个人在画面内,也可以根据姿态关键点坐标按大小或位置选择一个人。
(24:52)如果要计数重复动作,每个训练视频最好只包含一次单个动作。预测时还需要可靠的触发点,或用平滑逻辑更新计数器。
核心启发
- 做一个无需触屏的居家健身计时器:当模型识别到 jumping jacks、lunges 或 squats 时自动启动计时,停止动作后回到
other状态。Session 的 demo 就用连续预测驱动挑战计时,适合把手机放远后训练。 - 做一个动作质量评分工具:用预测结果里的 confidence value(置信度)给动作质量打分。Session 明确提到可以把置信度用于评估动作质量,并与训练视频中的示例动作比较。
- 做一个重复次数计数器:把训练视频整理成“一段视频只包含一次单个动作”,运行时用 prediction window 和 smoothing logic 更新计数。这个方案适合深蹲、弓步、开合跳这类重复动作。
- 做一个训练数据检查清单:在用户导入视频前检查是否只有一个人、全身是否入镜、镜头是否稳定、衣服是否遮挡动作。Session 把这些都列为影响模型效果的关键条件。
- 做一个混剪视频标注小工具:让用户在长视频上标出
file_name、label、start_time和end_time,再导出 JSON 或 CSV 给 Create ML 训练。这样可以复用已有素材,不必先把每段动作剪成独立视频。
关联 Session
- Explore the Action & Vision app — 继续拆解一个使用动作分类、姿态检测和 Vision 能力的运动分析示例 app。
- Detect Body and Hand Pose with Vision — 讲解身体姿态和手部姿态检测,补足本 session 中 Vision 姿态输入的底层能力。
- Control training in Create ML with Swift — 展示如何用 Swift 控制 Create ML 训练流程,适合把模型训练从 GUI 扩展到脚本化流程。
评论
GitHub Issues · utterances