本文共 10202 字,大约阅读时间需要 34 分钟。
当机器学习(ML)获得了检测图像之间的视觉模式的能力时,它开始进入医疗行业,这是医生和技术人员需要多年才能掌握的技能。
具体来说,用于医学领域计算机视觉任务的ML模型使用独立图像的数据集进行训练,以学习识别它们的相似性和差异性。
2016年,ML被用于对结肠癌上皮细胞的T淋巴细胞进行高精度分类(Chen,C.L.等人)。由于在细胞分类项目中的成功应用以及之前的其他尝试,ML有望显著加快疾病识别的进程。
皮肤生长过程中出现的恶性黑色素瘤(皮肤癌)通常在其早期阶段未被注意到,这通常是由于患者的犹豫或医疗服务提供商的诊断错误造成的——我们的移动应用程序正致力于解决这一问题。尤其是随着COVID-19的流行,人们对去医院或诊所更加犹豫。一个可以在手机上下载的应用程序可以缓解人们的焦虑。
基本上,我们的目标是让人们不必马上去会面一个医生就可以得到医疗照顾。
为了将机器学习融入到我们的应用程序中,我们使用苹果的Create ML模型构建框架来训练ML模型,以检测恶性痣和良性痣之间的差异,然后将该模型嵌入到iOS应用程序中。
我们选择Create ML和Xcode是因为Create ML上生成的模型的精度比其他框架高。然而,目前Create ML模型只能被合并到基于Xcode的应用程序中。
我们的应用程序名为Mole Scan,评估用户输入的痣或异常皮肤生长图像,并给出良性或恶性分类。这个应用程序(理论上)可以被一个普通人使用,不需要咨询医生就可以快速无焦虑地识别出他们的痣。
这种筛选过程的简化为用户带来了经济和情感上的好处。用户不需要支付不必要的皮肤科医生的费用,除非他们觉得应用程序给出的诊断是不准确的或者他们得到了一个恶性的结果。
它还可以为那些忙碌或没有资源去看皮肤科医生的人节省时间,潜在地将用户从等待和接受医生检查所带来的情绪痛苦中解脱出来。
目前,App Store中只有一个免费的应用程序可以评估痣,并尝试将其归类为癌变或其他类型;我们的模型能够超过这个单独的竞争对手,准确率为98%。准确率是由程序从400个图像的测试集和2400个图像的验证集中正确分类的图像量来计算的。由于我们训练了大量的图像,所以我们的准确率很高。
重要提示:用户将被建议去看医生,而不管应用程序对他们的痣如何分类。
我们的应用程序(Mole Scan)是免费和准确的,因此所有背景的人都可以在任何适合他们的时间获得可信的医疗护理。Mole Scan即将于2021年1月面世。
在接下来的几节中,我们将带领您如何使用Create ML和Xcode创建像我们这样的应用程序,并讨论我们制作应用程序的具体过程。
当我们在Create ML中创建模型时,我们需要准备好一个数据集,以便上传到模型中进行训练。在收集图像之前,你需要决定图像的分类或者标签是什么,也就是模型尝试预测的值。
如果您没有计划要使用什么图像,或者如果您只想制作一个示例项目,以下是可用医疗数据集的列表:
根据数据集的下载方式,您可能不需要执行以下步骤。
为了演示的目的,我们将构建一个简单的模型,将输入图像分为两种动物之一:狮子或老虎。这将帮助我们演示创建痣分类模型的基本过程。但是在我们跳到演示动物分类器之前,我想分享一些关于在一个具体的任务上使用一个大的自定义数据集是什么样子的更多信息。
在我们的模型中,我们使用了8000张良性和恶性痣的照片。我们从2000张恶性图像(来自ISIC数据集和与皮肤科医生的合作)和8000张良性图像开始。由于ISIC数据库中良性痣的图像是恶性痣的10倍多,因此我们可以使用的良性和恶性图像数量之间存在不可避免的差异。
ISIC数据集旨在供医生学习,并为用户提供大量的皮肤生长图像。它包括来自多个数据集的图像,例如HAM1000和SONIC皮肤癌数据集。有些过滤器可以打开或关闭,这样数据集只显示特定类型的皮肤癌图像。我们使用滤镜来显示痣的恶性或良性图像,并对每个图像进行筛选,根据以下标准进行选择:
您可能已经注意到,数据集中共有23906张图像,如上面的屏幕截图所示,但其中只有大约3000张是恶性的。在这些恶性图像中,大约10%是重复的,超过四分之一的图像因为不符合上述标准而无法使用。我们从皮肤科得到了大约500张恶性痣的匿名图片。
通过数据增强,我们能够从最初的2000张恶性图像中生成8000张。我们的增强过程很简单-我们把最初的2000向左旋转了三次,每一组都单独保存,加起来一共8000张。
良性分类不需要增加,因为ISIC数据集充满了良性痣的大型非显微图像。
我们每个类使用6600个图像来训练模型,每个类有1200个用于验证,400个用于测试,总共16000个图像的数据集。
模型的训练准确率为99%,验证率为97%,测试准确率为98%。
既然我们已经了解了我们定制的数据集的大致情况,那么让我们用Create ML构建一个简单的演示分类模型,这样您就可以更好地理解如何使用框架了。为此,我们将使用一个简单的用例–狮子和老虎。一般来说,作为训练数据输入的照片越多,模型训练后的精度就越高。
为了区分不太复杂的图像,每个类至少使用20个。根据苹果开发者网站的说法,10张已经足够了,但是每个类的额外图片使模型更加精确。
上面的屏幕截图显示了应该如何安排文件夹,其中有两个单独的文件夹用于训练和测试。文件夹的名称是图像的标签,因为Create ML上的图像分类模板不需要生成手工标记的图像。使用训练文件夹中的图像对模型进行训练后,将使用测试数据检查模型的准确性(即,查看它对未披露数据的概括程度)。
我们使用了20张狮子和老虎的图片作为训练数据文件夹(因为这只是一个演示,准确度对我们来说并不重要,但对于任何有效的项目都应该如此),在测试文件夹中使用了5张。根据经验,至少85%的图像应该在train文件夹中。
如果你把训练图像放在测试文件夹中,结果就会有缺陷,并且每次运行都会给出误报(至少可以说,这是不道德的,尤其是在医疗应用中)。
到目前为止,不幸的是,在Linux或Windows上没有简单的方法来运行Xcode。如果您无法访问macOS来运行Xcode,可以尝试将其安装到您的设备上。
Xcode需要一个小时到一天的时间来完全下载,它会占用机器上大约10GB的存储空间。从App Store下载Xcode11.6(在编写本文时)之后,创建一个新的项目。出于我们的目的,我们选择了一个单视图应用程序作为模板,但是您可以选择任何提供的模板,这取决于您计划如何使用它。
我们将这个新的项目命名为Media Test,但任何名称都可以,因为它不会影响应用程序的功能。Mole Scan是使用SwiftUI创建的,但是我们在本教程中使用的是StoryBoard。
请参阅上图了解大致的方向。一定要建立一个新的文件夹,并把它放在一个容易找到的地方,否则,在以后为ML模型创建图像文件夹时会很困难。现在Xcode项目已经创建好了,转到顶部导航栏中的Xcode菜单和“open developer tool”,其中应该有一个Create ML选项。
选择正确的Create ML模板,这取决于您正在处理的项目类型:
确保将这个ML模型文件放在前面创建的同一个Xcode文件夹中。
创建模型后,您可以将图像的数据集文件夹拖放到软件中显示“训练数据”和“测试数据”的地方,或者从设备中选择文件夹。请确保不要将这些文件夹或将图像放入USB或外部硬盘驱动器中,因为Create ML无法定位它们。
一旦你输入了你的数据,你的屏幕应该与下面的屏幕截图相似-你可以在准备好的时候按下训练按钮:
单击testing选项查看模型统计信息。一旦模型经过训练,您会得到一个统计页面,面如下所示:
如果精度低于100%,请不要担心,特别是如果观察的对象有许多复杂的特性,或者您没有使用数千幅图像。生成模型后,将输出标签下的蓝色小图标“ML Model”拖到桌面上。这个模型可以在您的Xcode项目中使用,如您所愿。
要制作一个基本的应用程序界面你可以使用训练好的模型,您将使用两个变量:一个UIImageView从图库加载照片,一个UIButton用于用相机拍照。
首先,按住ctrl将UIImageView拖动到辅助编辑器类声明下面,创建一个outlet。拖动时,按钮和插入位置之间应该出现一条黄线,选择“weak”选项。
按住ctrl将拍照按钮拖动到辅助编辑器刚才创建的图像视图outlet下面,会弹出一个小框,要求您指定操作的详细信息。选择动作。您可以将此变量命名为任何名称,但请确保保持一致,并在以后访问此变量时使用相同的名称。另外,通过检查上面的编码空间,确保您正在编辑按钮所在的视图控制器,它应该具有.swift文件的名称。
这里是我们需要添加自己的代码的地方。创建一个名为imagePicker的变量,并使用以下代码行将其插入viewDidLoad()函数的上方:
var imagePicker: UIImagePickerController!
然后创建一个名为imagePickerController的函数,声明如下:
func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]) //header declaration {imagePicker.dismiss(animated: true, completion: nil) //dismiss when loadeduiImageView.image = info[.originalImage] as? UIImage } // load image
这段代码应该放在viewDidLoad()函数之后。在拍照按钮函数内部,放置以下四行代码,它们确定该操作的委托和源。
imagePicker = UIImagePickerController()imagePicker.delegate = selfimagePicker.sourceType = .camerapresent(imagePicker, animated: true, completion: nil)
总之,如果您正确地完成了上述步骤,您的代码应该如下所示:
// Created by Vidushi Meel and Asritha Bodepudi on 7/14/20.//import UIKitclass SecondViewController: UIViewController, UINavigationControllerDelegate, UIImagePickerControllerDelegate {@IBOutlet weak var uiImageView: UIImageView!@IBAction func takePhoto(_ sender: UIButton) {imagePicker = UIImagePickerController()imagePicker.delegate = selfimagePicker.sourceType = .camerapresent(imagePicker, animated: true, completion: nil)}var imagePicker: UIImagePickerController!override func viewDidLoad() {super.viewDidLoad()// Do any additional setup after loading the view.}func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]) {imagePicker.dismiss(animated: true, completion: nil)uiImageView.image = info[.originalImage] as? UIImage}}}
我们在拍照之外再创建一个选择照片的选项,与takePhoto()函数类似创建一个chooseImage()函数:
@IBAction func chooseImage(_ sender: UIButton) {if UIImagePickerController.isSourceTypeAvailable(.savedPhotosAlbum){imagePicker.delegate = selfimagePicker.sourceType = .savedPhotosAlbumimagePicker.allowsEditing = falsepresent(imagePicker, animated: true, completion: nil)}}
一旦您的代码看起来与上面的类似,您就可以保存用户拍摄的照片,然后按照下面的步骤使用前面加入的模型运行它。
现在已经创建了一个模型并创建了“拍照/选择图片”功能,我们需要将该模型与图像视图连接起来,以便模型处理用户输入的图像并对其进行分类。
首先,我们需要在具有UIImageView的ViewController中加入一个UITextView。模型将使用它来传达图像的分类。
按住ctrl将文本框拖动到类的顶部,以创建一个变量声明:
@IBOutlet weak var textView: UITextView! //variable declaration
要将模型与上载的图像连接起来,您需要将下面detect()函数添加到ViewController viewDidLoad()函数之后的地方。
func detect(image: CIImage) {guard let model = try? VNCoreMLModel(for: mediumtestmodel_1().model) else { fatalError("Loading CoreML Model Failed.")}let request = VNCoreMLRequest(model: model) { (request, error) inguard let results = request.results as? [VNClassificationObservation] else { fatalError("Model failed to process image.")}if let firstResult = results.first {self.textView.text = firstResult.identifier.capitalizedprint (firstResult.identifier.capitalized)}}let handler = VNImageRequestHandler(ciImage: image)do { try handler.perform([request])} catch { print(error)}}
请确保将mediumtestmodel_1().model的实例更改为您保存的模型的名称。除了上面的代码之外,还需要在imagePickerController()函数的底部添加此条件语句。
if let userPickedImage = info[UIImagePickerController.InfoKey.originalImage] as? UIImage {uiImageView.image = userPickedImageguard let ciImage = CIImage(image: userPickedImage) else {fatalError(“Cannot convert to CIImage.”)} detect(image: ciImage)} imagePicker.dismiss(animated: true, completion: nil)
在viewDidLoad()函数里super.viewDidLoad()行之后添加如下几行,这让程序知道在家在view controller之后如何做。
imagePicker.delegate = selfimagePicker.sourceType = .photoLibraryimagePicker.allowsEditing = falseself.textView.text = ""
下面是模型的运行情况:
以下是将狮子和老虎模型整合到应用程序中的完整代码:
// ViewController.swift// Mole Scan App// Created by Vidushi Meel and Asritha Bodepudi on 7/14/20.import UIKitimport CoreMLimport Visionclass SecondViewController: UIViewController, UINavigationControllerDelegate, UIImagePickerControllerDelegate {var imagePicker = UIImagePickerController()@IBOutlet weak var uiImageView: UIImageView!@IBAction func takePhoto(_ sender: UIButton) {imagePicker = UIImagePickerController()imagePicker.delegate = selfimagePicker.sourceType = .camerapresent(imagePicker, animated: true, completion: nil)}@IBOutlet weak var textView: UITextView!@IBAction func chooseImage(_ sender: UIButton) {if UIImagePickerController.isSourceTypeAvailable(.savedPhotosAlbum){imagePicker.delegate = selfimagePicker.sourceType = .savedPhotosAlbumimagePicker.allowsEditing = falsepresent(imagePicker, animated: true, completion: nil)}}override func viewDidLoad() {super.viewDidLoad()imagePicker.delegate = selfimagePicker.sourceType = .photoLibraryimagePicker.allowsEditing = falseself.textView.text = ""}func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]) {imagePicker.dismiss(animated: true, completion: nil)uiImageView.image = info[.originalImage] as? UIImageif let userPickedImage = info[UIImagePickerController.InfoKey.originalImage] as? UIImage {uiImageView.image = userPickedImageguard let ciImage = CIimage(image: userPickedImage) else {fatalError("Cannot convert to CIImage.")} detect(image: ciImage)}imagePicker.dismiss(animated: true, completion: nil)}func detect(image: CIImage) {guard let model = try? VNCoreMLModel(for: mediumtestmodel_1().model) else { //remember to replace mediumtestmodel_1 with your own model namefatalError("Loading CoreML Model Failed.")}let request = VNCoreMLRequest(model: model) { (request, error) inguard let results = request.results as? [VNClassificationObservation] else {fatalError("Model failed to process image.")}if let firstResult = results.first {self.textView.text = firstResult.identifier.capitalizedprint (firstResult.identifier.capitalized)}} let handler do { try handler.perform([request])} catch { print(error) } } }
在区分狮子和老虎时,演示模型会在类中找到模式以正确识别图像。我们的痣扫描模型也是如此,只不过是分为恶性和良性。因为痣的图像看起来非常相似,所以我们使用了数千个图像来训练模型。狮子和老虎看起来更明显,这就是演示模型为什么模型不需要太多的图像。
上面的截图和视频显示了我们的应用程序界面到目前为止是如何工作的。它可以保存在身体不同位置的痣的图像,当用户返回应用程序时,他们可以选择身体上表示痣的点,并查看他们之前拍摄的照片。我们希望增加更多的功能,使界面更友好。在未来,我们希望通过添加更多的图片和指引,使应用程序更加人性化。我们也在积极研究一种方法,让用户可以直接从应用程序中联系医生或在日历中安排日程,还可以将应用程序中的所有照片和分类导出到他们的相簿中。
最后提醒:Mole Scan将于2021年1月在应用商店发布
原文链接
转载地址:http://jvifo.baihongyu.com/