多年来,Apple 的机器学习框架变得越来越强大。Style Transfer或Neural Style Transfer是框架提供的一种技术。通过风格转换,您可以将一张图片变成一张新图片,并将其风格化为第三张图片。在本教程中,您将学习如何利用 Apple 的 ML 框架直接在您的 iOS 设备上实现这一目标!
iOS 和 macOS 中的机器学习 (ML) 依赖于两个核心 Apple 框架 - CreateML和CoreML。CreateML 允许您构建和训练 ML 模型,CoreML 允许您运行这些模型。
CreateML 最初仅可用于 macOS 以生成模型。然后,您可以捆绑这些模型以在 macOS、iOS 或 iPadOS 应用程序中使用。
2021 年,Apple 将该框架提供给 iOS 15 和 iPadOS 15。现在,CreateML 框架允许在设备上训练和创建模型。
在本教程中,您将学习:
- 如何在设备上创建 ML 模型。
- 如何使用模型生成风格化图像。
- 机器学习训练和预测的核心概念。
注意:iOS 模拟器不支持本教程中使用的 API。您需要 iOS 设备和此应用的开发者帐户。选择模拟器作为部署目标会使 Xcode 生气并且无法编译!:]
入门
启动项目是一个名为Petra的简单应用程序。
在 Xcode 中打开启动项目,然后在您的设备上构建并运行它。让自己熟悉应用程序的 UI 和代码库。
这个应用程序有一个单一的屏幕,允许您选择两个图像。第一个图像是一个样式图像,它指示需要什么样式。第二个图像,即用户选择的图像,将应用所需的样式。
底部的按钮应用样式! 应该将用户图像转换为选定的样式。在本教程结束时放入缺失的部分后,此按钮将起作用。
对于这个应用程序,您将创建一个MLModel,用于对您的宠物照片进行样式化。
在本教程中,您将训练模型将样式应用于宠物,例如猫或狗,或者大象或鸡。您可以应用相同的原则来创建专为您的宠物训练的模型。
什么是风格转移?
风格转移是一种将一幅图像的内容与另一幅图像的风格相融合的技术。ML 模型从样式输入中学习样式,包括图案、颜色或纹理。然后它使用它以该样式重新组合内容输入。
该模型从样式内容创建构建块。然后它会使用这些块重新创建您的内容,就像您在 Minecraft 游戏中所做的那样。
在上面的示例中,平铺/图案图像A充当用于提取样式(例如,颜色、图案、纹理)的源。该样式应用于图像B以生成风格化的输出图像C。
听起来像魔术,不是吗?
这种技术有各种各样的应用,从数字艺术到虚拟现实。正如在 Google Stadia 平台上演示的那样,它还在游戏中找到了自己的位置。Apple 的 ARKit 还支持风格转换以对特定场景进行风格化。
准备数据集
要创建模型,您必须使用机器学习算法处理训练集。iOS 中的 CreateML 框架也不例外,它提供了训练模型所需的所有功能。
训练数据应尽可能接近预期的用户内容。
例如,对于一个风格化人脸的应用程序,您需要人像来训练模型。
对于本教程,您将需要宠物图像来训练模型。您将使用来自Kaggle的一小部分数据,其中包含猫和狗的图片。不要担心下载整个数据集。示例项目已经有我们的小型数据集,您将在本教程中使用它。
在Finder中打开项目目录,找到TrainingData文件夹。
出于演示目的,该项目包含一个示例风格化图像。您可以在项目中的Assets.xcassets下看到 PresetStyle_1图片。**
回顾一下,这就是您在启动项目中所拥有的:
- 训练数据:包含宠物图像的目录作为我们的训练集。
- 样本风格图像:具有独特纹理、颜色或图案的样本艺术或平铺图像。
注意:您也可以包含自己的一组宠物图像以提高模型的准确性。
接下来,您将使用数据创建和训练模型。
训练模型
与任何机器学习一样,模型训练是风格迁移的主要步骤。让训练开始吧!
打开MLModelHandling组中的MLModelTrainer.swift。
trainModel(using:validationImage:sessionDir:onCompletion:)用下面的代码替换方法的内容。
// 1
let dataSource = MLStyleTransfer.DataSource.images(
styleImage: styleImage,
contentDirectory: Constants.Path.trainingImagesDir ?? Bundle.main.bundleURL,
processingOption: nil)
// 2
let sessionParams = MLTrainingSessionParameters(
sessionDirectory: sessionDir,
reportInterval: Constants.MLSession.reportInterval,
checkpointInterval: Constants.MLSession.checkpointInterval,
iterations: Constants.MLSession.iterations)
// 3
let modelParams = MLStyleTransfer.ModelParameters(
algorithm: .cnn,
validation: .content(validationImage),
maxIterations: Constants.MLModelParam.maxIterations,
textelDensity: Constants.MLModelParam.styleDensity,
styleStrength: Constants.MLModelParam.styleStrength)
以下是代码的情况:
-
您创建dataSource,它是一个MLStyleTransfer.DataSource。这告诉模型要从哪里训练数据。
-
您可以通过以下方式为培训课程指定参数
MLTrainingSessionParameters:sessionDirectory:保存会话期间生成的数据。 湾。reportInterval:会话报告进度的迭代次数。 C。checkpointInterval:会话保存检查点之后的迭代次数。 d。迭代:为会话运行的迭代总数。 -
您还设置
ModelParameters:algorithm:风格迁移任务的训练算法,优先考虑速度()或质量()。 湾。验证:在训练期间用于验证的图像 - 来自训练集中的一张。 C。maxIterations:样式迁移模型可以使用的最大训练迭代次数。当您在 中设置迭代时,这将被忽略。 d。textelDensity:定义样式细节的粗细程度。 e. styleStrength:指定样式对内容图像的影响程度。
您将在以下部分中查看其中一些参数的影响。
modelParams最后,在同一方法后面添加以下代码:
// 4
guard let job = try? MLStyleTransfer.train(
trainingData: dataSource,
parameters: modelParams,
sessionParameters: sessionParams) else {
onCompletion(nil)
return
}
这MLStyleTransfer.train(trainingData:parameters:sessionParameters:)是一种可以做到这一切的方法!您将上面创建的参数和数据源传递给它。它返回一个用于处理训练结果的MLJob对象。
调用此方法会启动异步风格迁移模型训练会话。
接下来,您将学习如何使用训练的输出。
设备上模型生成
训练完成后,模型就可以使用了。现在你没有对创建的模型做任何事情。所以让我们现在解决这个问题。
打开MLModelTrainer.swift。然后将下面的代码添加到trainModel(using:validationImage:sessionDir:onCompletion:)方法的末尾:
// 5
let modelPath = sessionDir.appendingPathComponent(Constants.Path.modelFileName)
job.result.sink(receiveCompletion: { result in
debugPrint(result)
}, receiveValue: { model in
do {
try model.write(to: modelPath)
onCompletion(modelPath)
return
} catch {
debugPrint("Error saving ML Model: (error.localizedDescription)")
}
onCompletion(nil)
})
.store(in: &subscriptions)
在这里,一旦训练完成,您将使用Combine来获得结果。在receiveValue闭包中,您将获得经过训练的模型,我们将其存储在文件系统中以供以后使用。
构建并运行。
当您运行应用程序时,为您的可爱宠物选择一张风格的图片和一张图片(如果您没有宠物,请在互联网上查找图片!)。然后您可以选择应用样式! 看看会发生什么。
注意:培训可能需要一段时间才能完成。观察 Xcode 控制台的进度。
即使已完成,您也不会看到宠物的程式化图像。但是,模型生成已完成。Xcode 控制台显示类似于以下内容的信息:
注意: 查看参考部分的链接以了解有关各种损失方程的更多信息。您会注意到的一件事是,随着迭代次数的增加,总损失会不断减少。总损失是内容损失和风格损失的总和。因此,总损失越少,模型的效率就越高。
这些设备生成重要数据。要查看它们,请从 Xcode 菜单中选择Window ▸ Devices and Simulators 。 现在,在Devices下选择您的设备,然后在Installed Apps下选择Petra。选择Actions按钮并选择Download Container,如下面的屏幕截图所示。******
将文件保存到您的 Mac。这可能需要一段时间才能下载,因此请确保等到下载完成。使用Finder导航到下载的文件,右键单击 —显示包内容以查看内部。内容如下所示:
导航到AppData ▸ Documents ▸ Session。每个会话将在此处创建一个文件夹,即每次您点击“应用样式”按钮时。
在每个会话的文件夹中,您将看到训练数据、各种元数据,最重要的是,生成的 CoreML 模型保存在文件名StyleTransfer.mlmodel下。
从用户数据中学习
当您使用用户数据时,设备上学习的定制变得无限。因为灵感随时随地都会来袭,用户可以简单地拍照并用于造型。在这种情况下,个性化并不意味着失去您的隐私,因为数据永远不必通过设备上的学习离开设备。
从用户数据中学习可以改善结果。然而,训练集应该尽可能接近预期的输入图像。如果用户有一只宠物猫,那么包含猫图像的训练集会产生更好的结果。
同样,由于照明和背景的差异,使用室内图像训练的模型在室外测试图像上可能表现不佳,如下所示。
好的培训会带来好的学习。但在某些情况下,过多的迭代和过多的训练数据可能会导致过度拟合。这意味着该模型正在学习不适用于大多数情况的细微差别。在这种情况下,减少训练迭代会有所帮助。
打开Constants.swift文件并检查以下内容:
enum MLModelParam {
static var maxIterations = 200
static var styleDensity = 128 // Multiples of 4
static var styleStrength = 5 // Range 1 to 10
}
模型训练使用此处定义的值。您将很快看到这些值如何影响样式转换输出。
现在,转到MLModelTrainer.swift文件并找到下面的代码。
// 3
let modelParams = MLStyleTransfer.ModelParameters(
algorithm: .cnn,
validation: .content(validationImage),
maxIterations: Constants.MLModelParam.maxIterations,
textelDensity: Constants.MLModelParam.styleDensity,
styleStrength: Constants.MLModelParam.styleStrength)
正如您之前看到的,您可以使用两种不同算法之一 -.cnn和.cnnLite. 根据 Apple 的文档:
- cnn:一种风格转移训练算法,可生成优先考虑图像质量而非速度的模型。
- cnnLite:一种风格转移训练算法,可生成将速度置于图像质量之上的模型。
使用这些参数,您将能够微调您的模型以获得更好的个性化。
在下一个课程中,您将充分利用模型和训练并实际设置图像样式。准备好?
风格开启!
现在您已经有了一个完美的模型,可以使用它来生成您独特的宠物艺术。
打开MLPredictor.swift并将方法的内容替换为predictUsingModel(_: inputImage: onCompletion:)以下代码。
// 1
guard
let compiledModel = try? MLModel.compileModel(at: modelPath),
let mlModel = try? MLModel.init(contentsOf: compiledModel)
else {
debugPrint("Error reading the ML Model")
return onCompletion(nil)
}
// 2
let imageOptions: [MLFeatureValue.ImageOption: Any] = [
.cropAndScale: VNImageCropAndScaleOption.centerCrop.rawValue
]
guard
let cgImage = inputImage.cgImage,
let imageConstraint = mlModel.modelDescription.inputDescriptionsByName["image"]?.imageConstraint,
let inputImg = try? MLFeatureValue(cgImage: cgImage, constraint: imageConstraint, options: imageOptions),
let inputImage = try? MLDictionaryFeatureProvider(dictionary: ["image": inputImg])
else {
return onCompletion(nil)
}
这里发生了一些事情:
- 在您可以使用模型进行预测之前,首先编译模型。然后将其初始化为
MLModel对象。编译后的文件作为ModelName.mlmodelc保存在临时目录中。
注意:mlmodel与编译后的文件相比,文件占用的空间更少mlmodelc。这是一种节省应用程序空间或从服务器下载模型的技术。
- 在这里,您创建
MLDictionaryFeatureProvider模型所需的字典以进行预测。您还使用模型的约束和选项格式化输入图像。您生成一个MLFeatureValue并传递给MLDictionaryFeatureProvider.
要最终生成风格化图像,请在其后添加以下代码。
// 3
guard
let stylizedImage = try? mlModel.prediction(from: inputImage),
let imgBuffer = stylizedImage.featureValue(for: "stylizedImage")?.imageBufferValue
else {
return onCompletion(nil)
}
let stylizedUIImage = UIImage(withCVImageBuffer: imgBuffer)
return onCompletion(stylizedUIImage)
这是该代码的作用:
- 调用该
prediction(from:)方法执行单个预测并输出MLFeatureProvider。然后从中提取生成的图像并从图像缓冲区创建一个UIImage实例。
瞧!这stylizedUIImage是与您的宠物图像混合风格的输出图像。
构建并运行。
点击应用样式! 选择宠物形象和风格后。与以前一样,该处理需要几分钟才能完成。之后,您现在应该在屏幕上看到结果:
只用几行代码就可以训练和生成风格化的图像,这不是很神奇吗?
注意:风格化的图像可能看起来被裁剪。这是因为它符合模型接受的尺寸。.cropAndScale您像.centerCrop前面一样设置值imageOptions。
等待!那不是全部!如果结果达不到您的想象,您可以对其进行微调。
您现在将看到调整训练模型的参数如何影响结果。前往Constants.swift并找到MLModelParam枚举。在接下来的部分中,您将了解每一个的含义。随意玩数字并随时重新运行应用程序。
风格密度
样式密度也称为Textel Density。这控制了模型应该学习风格的粗略或精细程度。当您使用更高/更低的值时,这会产生明显的风格化结果。
样式密度当前设置为 128,小于默认值 256。请注意,这应始终为 4 的倍数,推荐范围为 64 到 1024。
在下面的示例中,您可以看到样式密度如何影响输出。
风格力量
较高的值会使模型学习更多的风格和更少的内容。较小的值学会做更少的样式。
在示例中,它设置为 5,这是默认值。它的范围可以从 1 到 10。
以下是它如何影响您的结果的示例:
请注意,当您设置会话参数时,模型参数中的maxIterations值将被忽略。iterations
玩转迭代、样式密度和样式强度,为您的宠物打造一个讨人喜欢的模型。:]
就是这样!恭喜您完成了本教程并学习了如何训练和使用 ML 模型进行风格迁移!
点击下载项目资料与查看原文
这里也推荐一些面试相关的内容!