使用迁移学习构建身体部位识别器
虽然在移动设备中用于医疗目的的AI的是可行的,但我们想要制作一个概念证明,可以基于实时摄像头输入识别不同的身体部位。这种类型的计算机视觉称为图像分类。图像分类的一个要求是访问大型标记数据集以训练我们的机器学习模型。在进行概念验证时,您并不总是能够访问这些大量数据,这个障碍的可能解决方案是迁移学习。
迁移学习
迁移学习是一种技术,我们采用已经成功训练某个任务的机器学习模型,并将其重新用于执行不同的任务。例如,对于图像分类,我们将采用在Imagenet上训练的模型,并在我们自己的图像数据集上重新训练它。这种预训练的机器学习模型已经能够识别大量的物体,因此我们可以使用它来提取图像分类固有的一般特征,如形状和边界。这个预训练好的网络应该能够很好地概括我们的新分类问题。
正确的数据和图像增强
对于这个概念证明,目标是以90%的准确度预测手,下臂和面部,因此我们可以将模型结合到概念验证移动应用中。我们一开始收集了6个人的360张图片,每个人每个身体部位20张。接下来,我们对数据集进行10x图像增强,使用这个库将数据集扩大到3600个图像。通过增强,我们对图像执行一系列的任务,如翻转,添加噪声,改变对比度,锐化,反转通道,屏蔽小区域;每一步创建一个不同但相似的图像。下面的图片可以看到一些例子。
使用图像增强训练数据
特征提取VS权重初始化
在构建了包含3个类的3600个样本的数据集之后,是时候训练我们的机器学习模型了。我们可以遵循几种策略来执行迁移学习,但我们将在迁移学习范围的每一端研究两个示例:特征提取和权重初始化。
特征提取包括使用先前网络学习的表示来生成新颖图像的新特征。然后,我们从头开始训练一个具有这些特征的新分类器。通过权重初始化,我们从预训练的机器学习模型开始,解冻模型的最终层,允许权重微调并拟合我们的数据集。
特征提取可以在本地CPU上完成,但代价是准确性。对于我们的身体部位检测模型,我们使用了权重初始化。
训练我们的模型
我们使用Keras作为在Tensorflow之上运行的Python库来训练我们的机器学习模型。首先,我们必须确定我们想要使用哪种预训练的模型。由于其在移动设备上的尺寸和性能,我们选择在Imagenet上训练的mobilenet v1 224 作为我们实施迁移学习的预训练模型。我们导入卷积base,通过设置最后一个可训练的层来解冻权重,然后编译我们的模型。
将训练数据输入脚本的方法非常简单:Keras ImageDataGenerator希望将训练和验证图像组织成一个训练和验证文件夹,其中图像按子文件夹中的每个类进行排序,每个名称代表图像类。
最后我们称之为fit_generator,训练我们的模型并保存我们训练过的模型。Python代码如下:
from keras import models from keras import layers from keras import optimizers from keras.applications import MobileNet from keras.preprocessing.image import ImageDataGenerator import os conv_base = MobileNet(weights='imagenet', include_top=False, input_shape=(224, 224, 3)) model = models.Sequential() model.add(conv_base) model.add(layers.Flatten()) model.add(layers.Dense(256, activation='relu')) model.add(layers.Dense(7, activation='sigmoid')) model.summary() conv_base.trainable = True model.compile(optimizer=optimizers.RMSprop(lr=2e-5), loss='categorical_crossentropy', metrics=['acc']) conv_base.summary() base_dir = '../../data/' train_dir = os.path.join(base_dir, 'train') validation_dir = os.path.join(base_dir, 'validation') train_datagen = ImageDataGenerator(rescale=1./255) validation_datagen = ImageDataGenerator(rescale=1. / 255) train_generator = train_datagen.flow_from_directory( # This is the target directory train_dir, # All images will be resized to 224x224 target_size=(224, 224), batch_size=20, # Since we use binary_crossentropy loss, we need binary labels class_mode='categorical') validation_generator = validation_datagen.flow_from_directory( validation_dir, target_size=(224, 224), batch_size=20, class_mode='categorical') history = model.fit_generator( train_generator, steps_per_epoch=100, epochs=100, validation_data=validation_generator, validation_steps=50, verbose=2) model.save('init_weights_V1.0.h5')
将.h5转换为CoreML
为了能够在iOS应用程序中使用我们训练过的机器学习模型,我们必须将其转换为CoreML。幸运的是,有一个库可以做到这一点:coremltools。我们只需要导入我们的模型并将其转换为coreML模型。
import coremltools coreml_model = coremltools.converters.keras.convert('init_weights_V1.0.h5') coreml_model.save('my_model.mlmodel')
在iOS推理
一旦我们有一个训练过的模型,就该把它用在一台设备上了。我们选择了iOS和CoreML,希望它像Apple承诺的一样容易使用,我们并没有感到失望。将训练有素的模型集成到iOS应用程序中,为其提供数据并从中获取结果非常简单。
将模型拖入我们的项目后,我们可以在Xcode中看到模型评估参数,这在我们的应用程序中设置模型的输入和输出时非常方便。在这里我们可以看到模型期望一个224x224像素的图像作为输入,并且它输出3个概率 - 每个身体部位一个。
当我们导入.mlmodel时,Xcode会自动生成一个Swift源文件,其中包含模型及其输入和输出的辅助类。这减少了我们自己编写的样板文件的数量,以便在应用程序和模型之间建立接口。
之后,大部分工作都在应用程序本身 - 设置视频捕获和格式化数据以符合我们模型的期望。在我们的例子中,我们以固定的间隔从实时视频输入中获取帧,将它们裁剪到屏幕中心的方形区域,将它们调整为224x224像素,并将它们从iOS的内部图像格式转换为原始RGB数据。然后我们将它传递给我们的CoreML模型并等待异步返回结果。swift 代码如下:
guard let image = captureImage(), let imageArray = try formatImageForModel(image) else { return } let prediction = try self.model.prediction(input: imageArray) DispatchQueue.main.async { let result = prediction.result for idx in 0 ..< result.count { if let bodyPart = BodyPart(rawValue: idx) { self.probabilitiesView.configure(for: bodyPart, withProbability: result[idx].floatValue) } } }
实际调用模型所需的代码量极少:一行请求预测,一行获取结果。应用程序开发人员根本不需要任何机器学习知识来使用它 - 他只需要准确了解输入和输出以及它们的格式。
我们的预测请求的结果是最终softmax层的输出 - 对于我们能够识别的每种类型的身体部位,数字在0和1之间。我们在应用程序中枚举这些并将索引和一些显示属性关联到它们,因此我们可以轻松地从原始数字转换为应用程序中的人类可读结果。
在iPhone上,最终结果如下所示:
性能
识别精度超出了我们的预期:该模型可靠地识别面部,手和下臂。当我们将30个测试图像输入到模型中时,我们会看到一致的预测。
可以预见,运行时性能在很大程度上取决于设备的处理能力。在iPhone 8或iPhone X等最新iPhone型号上,预测需要20-25ms - 实时有效,考虑到相机以每秒30帧的速度拍摄图像。
在低端设备上,速度也不那么快:一台5年的iPhone 5S(220-250ms)需要大约10倍的时间来处理一个帧。然而,由于我们不阻塞主线程但是异步等待结果,用户看到的唯一效果,就是稍微延迟识别反馈。无论处理能力如何,预测精度都是一样的。
实际上,目前使用的大多数智能手机都能够在实时摄像头上使用这种模式,并提供快速、可靠的用户体验。
结论
在小数据集上训练一个良好的图像分类器可以通过迁移学习快速完成。当我们引入超过三个body-part类别时,我们期望我们需要做更多的微调以获得相同的性能