版本记录
版本号 | 时间 |
---|---|
V1.0 | 2018.10.16 星期二 |
前言
目前世界上科技界的所有大佬一致认为人工智能是下一代科技革命,苹果作为科技界的巨头,当然也会紧跟新的科技革命的步伐,其中ios API 就新出了一个框架
Core ML
。ML是Machine Learning
的缩写,也就是机器学习,这正是现在很火的一个技术,它也是人工智能最核心的内容。感兴趣的可以看我写的下面几篇。
1. Core ML框架详细解析(一) —— Core ML基本概览
2. Core ML框架详细解析(二) —— 获取模型并集成到APP中
3. Core ML框架详细解析(三) —— 利用Vision和Core ML对图像进行分类
4. Core ML框架详细解析(四) —— 将训练模型转化为Core ML
5. Core ML框架详细解析(五) —— 一个Core ML简单示例(一)
6. Core ML框架详细解析(六) —— 一个Core ML简单示例(二)
7. Core ML框架详细解析(七) —— 减少Core ML应用程序的大小(一)
8. Core ML框架详细解析(八) —— 在用户设备上下载和编译模型(一)
9. Core ML框架详细解析(九) —— 用一系列输入进行预测(一)
10. Core ML框架详细解析(十) —— 集成自定义图层(一)
11. Core ML框架详细解析(十一) —— 创建自定义图层(一)
12. Core ML框架详细解析(十二) —— 用scikit-learn开始机器学习(一)
开始
首先看一下本文的写作环境
Swift 4, iOS 11, Xcode 9
Apple的Core ML
和Vision
框架已经让开发人员进入了一个勇敢的机器学习新世界,并带来了令人兴奋的可能性。Vision
允许您检测和跟踪面部,Apple的Machine Learning page提供检测对象和场景的即用型模型,以及用于自然语言处理的NSLinguisticTagger
。如果您想构建自己的模型,请尝试使用Apple的新Turi Create来扩展其使用您的数据预先训练的模型之一。
但是,如果您想要做什么需要更加个性化的东西?然后,是时候进入机器学习(ML),使用谷歌,微软,亚马逊或伯克利的众多框架之一。而且,为了让生活更加精彩,您需要选择一种新的编程语言和一套新的开发工具。
在这个Keras
机器学习教程中,您将学习如何训练深度学习卷积神经网络模型,将其转换为Core ML,并将其集成到iOS应用程序中。您将学习一些ML术语,使用一些新工具,并在此过程中学习一些Python。
示例项目使用ML的Hello-World
示例 - 一种对手写数字进行分类的模型,在MNIST dataset上进行训练。
Why Use Keras? - 为什么使用Keras?
ML模型涉及许多复杂的代码,操纵数组和矩阵。但ML已经存在了很长时间,研究人员已经创建了库,使像我们这样的人更容易创建ML模型。其中许多是用Python
编写的,尽管研究人员还使用R,SAS,MATLAB和其他软件。但您可能会在基于Python的工具中找到所需的一切:
-
scikit-learn
提供了一种运行许多经典ML算法的简便方法,例如线性回归和支持向量机。 - 另一方面是
PyTorch
和Google
的TensorFlow
,它可以让您更好地控制深度学习模型的内部工作。 - 微软的
CNTK
和Berkeley
的Caffe
是类似的深度学习框架,它们使用Python API
来访问他们的C ++引擎。
那么Keras在哪里适合?这是TensorFlow
和CNTK
的包装,亚马逊的MXNet
即将推出。 (它也与Theano
合作,但蒙特利尔大学于2017年9月停止了这项工作。)它提供了一个易于使用的API,用于构建模型,您可以在一个后端训练,并在另一个后端部署。
使用Keras
而不是直接使用TensorFlow
的另一个原因是coremltools包括Keras
转换器,但不包括TensorFlow
转换器 - 尽管存在TensorFlow to CoreML converter和MXNet to CoreML converter。虽然Keras
支持CNTK
作为后端,但coremltools
仅适用于Keras + TensorFlow
。
注意:在使用这些工具之前,您是否需要学习Python?好吧,我没有。当你完成本教程时,你会发现Python语法与Swift类似:更加简化,缩进是语法的重要部分。如果您感到紧张,请在浏览器选项卡中保持打开状态,以便快速参考:Crash Course in Python for Machine Learning Developers。
另一个注意事项:研究人员同时使用
Python 2
和Python 3
,但coremltools
在Python 2.7
中运行得更好。
开始进入正题
打开项目起始文件夹:它包含一个入门iOS应用程序,您将在其中添加ML模型和代码以使用它。 它还包含一个docker-keras
文件夹,其中包含本教程的Jupyter
笔记本。
1. Setting Up Docker - 设置Docker
Docker是一个容器平台,允许您在自定义环境中部署应用程序 - 有点像虚拟机,但different。 通过安装Docker,您可以访问大量的ML资源,这些资源主要作为Docker镜像中的交互式Jupyter notebooks分发。
注意:安装Docker并构建映像需要几分钟,因此请在等待时阅读
ML in a Nutshell
。
下载,安装和启动Docker Community Edition for Mac。 在终端中,一次输入以下命令:
cd <where you unzipped starter>/starter/docker-keras
docker build -t keras-mnist .
docker run --rm -it -p 8888:8888 -v $(pwd)/notebook:/workspace/notebook keras-mnist
最后一个命令将Docker
容器的notebook
文件夹映射到本地notebook
文件夹,因此即使在您注销Docker
服务器之后,您也可以访问notebook
所写的文件。
在命令输出的最后是包含token的URL。 它看起来像这样,但具有不同的标记值:
http://0.0.0.0:8888/?token=7b189c8e200f49dcc33845d39101e8a0ab257db5f3b539a7
将此URL粘贴到浏览器中以登录Docker
容器的notebook
服务器。
打开notebook
文件夹,然后打开keras_mnist.ipynb
。 点击Not Trusted
按钮将其更改为Trusted
:这样您就可以在notebook
文件夹中保存对notebook
以及模型文件所做的更改。
2. ML in a Nutshell - Nutshell中的ML
Arthur Samuel
将机器学习定义为“研究领域,让计算机具有无需明确编程即可学习的能力”。 您有数据,它具有一些可用于对数据进行分类的功能,或者用它来进行一些预测,但是您没有用于这种计算的明确的公式,因此您无法编写程序来执行此操作。 如果您有“足够”的数据样本,则可以训练计算机模型以识别此数据中的模式,然后将其学习应用于新数据。 当您知道所有训练数据的正确结果时,它被称为监督学习:然后模型仅根据已知结果检查其预测,并调整自身以减少误差并提高准确性。 无监督学习超出了本教程的范围。
Weights & Threshold - 权重和阈值
假设您想和一群朋友一起选择一家餐厅共进晚餐。 有几个因素会影响您的决定:饮食限制,公共交通,价格范围,食物类型,儿童友好等。您为每个因素分配一个权重,以表明其对您决定的重要性。 然后,对于选项列表中的每个餐馆,您可以根据餐厅满足该因素的程度为每个因素分配一个值。 您将每个因子值乘以系数的权重,然后将它们相加以获得加权和。 结果最高的餐厅是最佳选择。 使用此模型的另一种方法是生成二进制输出:是或否。 您设置了一个阈值,并从列表中删除任何加权总和低于此阈值的餐馆。
Training an ML Model - 训练ML模型
提出权重并不是一件容易的事。 但幸运的是,您有很多以前的晚餐数据,包括选择了哪家餐厅,因此您可以训练ML模型来计算产生相同结果的权重,尽可能接近。 然后将这些计算出的权重应用于未来的决策。
要训练ML模型,首先要使用随机权重,将它们应用于训练数据,然后将计算出的输出与已知输出进行比较以计算误差。 这是一个具有最小值的多维函数,训练的目标是确定非常接近此最小值的权重。 权重也需要处理新数据:如果大量验证数据的误差高于训练数据的误差,那么模型就会过度拟合 - 权重对训练数据越适合,表明训练错误检测到一些不会推广到新数据的功能。
Stochastic Gradient Descent - 随机梯度下降
要计算减少误差的权重,可以在当前图形位置计算误差函数的梯度,然后调整权重以“降低”斜率。 这称为梯度下降,在训练期间多次发生。 对于大型数据集,使用所有数据计算梯度需要很长时间。 随机梯度下降(Stochastic gradient descent - SGD)
从随机选择的小批量训练数据中估计梯度 - 例如在选举日之前对选民进行调查:如果您的样本代表整个数据集,则调查结果可准确预测最终结果。
Optimizers - 优化器
错误函数是块状的:你必须小心不要走得太远,否则你可能会错过最低限度。 你的步数也需要有足够的动力来推动你摆脱任何虚假的最低限度。 ML研究人员为设计优化算法付出了很多努力。 目前最受欢迎的是Adam
(Adaptive Moment estimation
- 自适应力矩估计),它结合了以前最受欢迎的RMSprop
(Root Mean Square propagation
- 均方根传播)和AdaGrad
(Adaptive Gradient algorithm
- 自适应梯度算法)的特征。
Keras Code Time! - Keras代码时间!
好的,Docker
容器现在应该准备就绪:返回并按照说明打开notebook
。 是时候写一些Keras
代码了!
在具有匹配标题的keras_mnist.ipynb
单元格中输入以下代码。 在每个单元格中输入代码后,按Control-Enter
运行它。 代码运行时,In []:
标签中会出现一个星号,然后会出现一个数字,以显示运行单元格的顺序。 当您登录notebook
时,所有内容都会保留在内存中。 每隔一段时间,点击Save and Checkpoint
按钮。
注意:双击
markdown
单元格以添加自己的注释;按Control-Enter
以呈现markdown
并运行Python
代码。 您还可以使用其他笔记本按钮添加或复制粘贴单元格,以及移动单元格。
Import Utilities & Dependencies - 导入实用项和依赖项
输入以下代码,然后运行它以检查Keras
版本。
from __future__ import print_function
from matplotlib import pyplot as plt
import keras
from keras.datasets import mnist
from keras.models import Sequential
from keras.layers import Dense, Dropout, Flatten
from keras.layers import Conv2D, MaxPooling2D
from keras.utils import np_utils
from keras import backend as K
import coremltools
# coremltools supports Keras version 2.0.6
print('keras version ', keras.__version__)
__future__
是Python 2
和Python 3
之间的兼容层:Python 2有一个print
命令(没有括号),但Python 3需要print()
函数。 导入print_function
允许您在Python 2代码中使用print()
语句。
Keras
使用NumPy
数学库来操作数组和矩阵。 Matplotlib
是NumPy
的绘图库:您将使用它来检查训练数据项。
注意:由于due to NumPy 1.14,您可能会看到FutureWarning。
导入keras
后,打印其版本:coremltools
支持2.0.6版,如果使用更高版本,将发出警告。 Keras已经拥有MNIST
数据集,因此您可以导入它。 然后接下来的三行导入模型组件。 您导入NumPy
实用程序,并为后端提供带有import backend as K
的标签,您将使用它来检查image_data_format
。
最后,导入coremltools
,您将在此notebook
的末尾使用它。
Load & Pre-Process Data - 加载和预处理数据
1. Training & Validation Data Sets - 训练和验证数据集
首先,获取您的数据! 输入以下代码并运行它:下载数据需要一段时间。
(x_train, y_train), (x_val, y_val) = mnist.load_data()
这将从https://s3.amazonaws.com/img-datasets/mnist.npz下载数据,对数据项进行混洗,并在训练数据集和验证数据集之间进行拆分。 验证数据有助于检测模型过度拟合到训练数据的问题。 训练步骤使用训练的参数来计算验证数据的输出。 您将设置回调以监控验证丢失和准确性,以保存对验证数据执行最佳的模型,并且如果验证丢失或准确性未能在太多时期(重复)中提高,则可能提前停止。
2. Inspect x & y Data - Inspect x&y数据
下载完成后,在下一个单元格中输入以下代码,然后运行它以查看您获得的内容。
注意:您不必输入以#开头的行。 这些是注释,其中大部分都是为了向您展示运行单元格时
notebook
应显示的内容。
# Inspect x data
print('x_train shape: ', x_train.shape)
# Displays (60000, 28, 28)
print(x_train.shape[0], 'training samples')
# Displays 60000 train samples
print('x_val shape: ', x_val.shape)
# Displays (10000, 28, 28)
print(x_val.shape[0], 'validation samples')
# Displays 10000 validation samples
print('First x sample\n', x_train[0])
# Displays an array of 28 arrays, each containing 28 gray-scale values between 0 and 255
# Plot first x sample
plt.imshow(x_train[0])
plt.show()
# Inspect y data
print('y_train shape: ', y_train.shape)
# Displays (60000,)
print('First 10 y_train elements:', y_train[:10])
# Displays [5 0 4 1 9 2 1 3 1 4]
您有60,000
个28×28
像素的训练样本和10,000
个验证样本。 第一个训练样本是一个包含28个数组的数组,每个数组包含0到255之间的28个灰度值。查看非零值,您可以看到类似数字5的形状。
果然,plt
代码显示第一个训练样本是手写的5:
y
数据是一个60000元素的数组,包含训练样本的正确分类:第一个训练样本为5,下一个为0,依此类推。
3. Set Input & Output Dimensions - 设置输入和输出尺寸
输入这两行,然后运行单元格以设置x输入和y输出的基本尺寸。
img_rows, img_cols = x_train.shape[1], x_train.shape[2]
num_classes = 10
MNIST
数据项是28×28像素的图像,您希望将每个数据分类为0到9之间的数字。
您可以使用x_train.shape
值来设置图像行和列的数量。 x_train.shape
是一个包含3个元素的数组:
- 0) 数据样本数:60000
- 1) 每个数据样本的行数:28
- 2) 每个数据样本的列数:28
4. Reshape x Data & Set Input Shape
该模型需要略有不同的“shape”
的数据。 输入以下代码,然后运行它。
# Set input_shape for channels_first or channels_last
if K.image_data_format() == 'channels_first':
x_train = x_train.reshape(x_train.shape[0], 1, img_rows, img_cols)
x_val = x_val.reshape(x_val.shape[0], 1, img_rows, img_cols)
input_shape = (1, img_rows, img_cols)
else:
x_train = x_train.reshape(x_train.shape[0], img_rows, img_cols, 1)
x_val = x_val.reshape(x_val.shape[0], img_rows, img_cols, 1)
input_shape = (img_rows, img_cols, 1)
卷积神经网络认为图像具有宽度,高度和深度。 深度维度称为通道,并包含颜色信息。 灰度图像有1个通道;RGB图像有3个通道。
像TensorFlow
和CNTK
这样的Keras
后端,期望图像数据采用channels-last format (rows, columns, channels)
或 channels-first format (channels, rows, columns)
。 reshape
函数将通道插入正确的位置。
您还可以在正确的末尾设置初始input_shape
和通道。
5. Inspect Reshaped x Data
输入下面的代码,然后运行它以查看形状的变化情况。
print('x_train shape:', x_train.shape)
# x_train shape: (60000, 28, 28, 1)
print('x_val shape:', x_val.shape)
# x_val shape: (10000, 28, 28, 1)
print('input_shape:', input_shape)
# input_shape: (28, 28, 1)
TensorFlow
图像数据格式是最后一个通道,因此x_train.shape
和x_val.shape
现在最后有一个新元素1。
6. Convert Data Type & Normalize Values - 转换数据类型和规范化值
模型需要特定格式的数据值。 输入以下代码,然后运行它。
x_train = x_train.astype('float32')
x_val = x_val.astype('float32')
x_train /= 255
x_val /= 255
MNIST
图像数据值的类型为uint8
,范围为[0,255]
,但Keras
需要float32类型的值,范围为[0,1]。
7. Inspect Normalized x Data
输入以下代码,然后运行它以查看对x数据的更改。
print('First x sample, normalized\n', x_train[0])
# An array of 28 arrays, each containing 28 arrays, each with one value between 0 and 1
现在每个值都是一个数组,值是浮点数,非零值介于0和1之间。
8. Reformat y Data
y数据是一个60000
个元素的数组,包含训练样本的正确分类,但是只有10个类别并不明显。 输入以下代码,仅运行一次以重新格式化y数据。
print('y_train shape: ', y_train.shape)
# (60000,)
print('First 10 y_train elements:', y_train[:10])
# [5 0 4 1 9 2 1 3 1 4]
# Convert 1-dimensional class arrays to 10-dimensional class matrices
y_train = np_utils.to_categorical(y_train, num_classes)
y_val = np_utils.to_categorical(y_val, num_classes)
print('New y_train shape: ', y_train.shape)
# (60000, 10)
y_train
是一维数组,但该模型需要一个60000 x 10
矩阵来表示10个类别。 您还必须为10000元素的y_val
数组进行相同的转换。
9. Inspect Reformatted y Data - 检查重新格式化的y数据
输入以下代码,然后运行它以查看y数据的更改方式。
print('New y_train shape: ', y_train.shape)
# (60000, 10)
print('First 10 y_train elements, reshaped:\n', y_train[:10])
# An array of 10 arrays, each with 10 elements,
# all zeros except at index 5, 0, 4, 1, 9 etc.
y_train
现在是一个包含10个元素数组的数组,每个数组都包含除了图像匹配的索引之外的所有零。
Define Model Architecture - 定义模型架构
模型架构是炼金术的一种形式,如完美烧烤酱或garam masala
的秘密家庭食谱。 您可以从通用架构开始,然后调整它以利用输入数据中的对称性,或者生成具有特定特征的模型。
以下是来自两位研究人员的模型:Sri Raghu Malireddi和Keras作者的François Chollet。 Chollet
是通用的,Malireddi's
旨在生产适合移动应用的小模型。
输入以下代码,然后运行它以查看模型摘要。
1. Malireddi’s Architecture
model_m = Sequential()
model_m.add(Conv2D(32, (5, 5), input_shape=input_shape, activation='relu'))
model_m.add(MaxPooling2D(pool_size=(2, 2)))
model_m.add(Dropout(0.5))
model_m.add(Conv2D(64, (3, 3), activation='relu'))
model_m.add(MaxPooling2D(pool_size=(2, 2)))
model_m.add(Dropout(0.2))
model_m.add(Conv2D(128, (1, 1), activation='relu'))
model_m.add(MaxPooling2D(pool_size=(2, 2)))
model_m.add(Dropout(0.2))
model_m.add(Flatten())
model_m.add(Dense(128, activation='relu'))
model_m.add(Dense(num_classes, activation='softmax'))
# Inspect model's layers, output shapes, number of trainable parameters
print(model_m.summary())
2. Chollet’s Architecture
model_c = Sequential()
model_c.add(Conv2D(32, (3, 3), input_shape=input_shape, activation='relu'))
# Note: hwchong, elitedatascience use 32 for second Conv2D
model_c.add(Conv2D(64, (3, 3), activation='relu'))
model_c.add(MaxPooling2D(pool_size=(2, 2)))
model_c.add(Dropout(0.25))
model_c.add(Flatten())
model_c.add(Dense(128, activation='relu'))
model_c.add(Dropout(0.5))
model_c.add(Dense(num_classes, activation='softmax'))
# Inspect model's layers, output shapes, number of trainable parameters
print(model_c.summary())
虽然Malireddi
的架构比Chollet
有更多的卷积层(Conv2D)
,但它的运行速度要快得多,而且结果模型要小得多。
3. Model Summaries - 模型摘要
快速浏览这两个模型的模型摘要:
model_m:
Layer (type) Output Shape Param #
=================================================================
conv2d_6 (Conv2D) (None, 24, 24, 32) 832
_________________________________________________________________
max_pooling2d_5 (MaxPooling2 (None, 12, 12, 32) 0
_________________________________________________________________
dropout_6 (Dropout) (None, 12, 12, 32) 0
_________________________________________________________________
conv2d_7 (Conv2D) (None, 10, 10, 64) 18496
_________________________________________________________________
max_pooling2d_6 (MaxPooling2 (None, 5, 5, 64) 0
_________________________________________________________________
dropout_7 (Dropout) (None, 5, 5, 64) 0
_________________________________________________________________
conv2d_8 (Conv2D) (None, 5, 5, 128) 8320
_________________________________________________________________
max_pooling2d_7 (MaxPooling2 (None, 2, 2, 128) 0
_________________________________________________________________
dropout_8 (Dropout) (None, 2, 2, 128) 0
_________________________________________________________________
flatten_3 (Flatten) (None, 512) 0
_________________________________________________________________
dense_5 (Dense) (None, 128) 65664
_________________________________________________________________
dense_6 (Dense) (None, 10) 1290
=================================================================
Total params: 94,602
Trainable params: 94,602
Non-trainable params: 0
model_c:
Layer (type) Output Shape Param #
=================================================================
conv2d_4 (Conv2D) (None, 26, 26, 32) 320
_________________________________________________________________
conv2d_5 (Conv2D) (None, 24, 24, 64) 18496
_________________________________________________________________
max_pooling2d_4 (MaxPooling2 (None, 12, 12, 64) 0
_________________________________________________________________
dropout_4 (Dropout) (None, 12, 12, 64) 0
_________________________________________________________________
flatten_2 (Flatten) (None, 9216) 0
_________________________________________________________________
dense_3 (Dense) (None, 128) 1179776
_________________________________________________________________
dropout_5 (Dropout) (None, 128) 0
_________________________________________________________________
dense_4 (Dense) (None, 10) 1290
=================================================================
Total params: 1,199,882
Trainable params: 1,199,882
Non-trainable params: 0
底线Total params
是尺寸差异的主要原因:Chollet
的1,199,882比Malireddi
的94,602多12.5倍。 而这正是模型尺寸的差异:4.8MB对380KB。
Malireddi
的模型有三个Conv2D
图层,每个图层后跟一个MaxPooling2D
图层,它将图层的宽度和高度减半。 这使得第一个密集层的参数数量远远小于Chollet,并解释了为什么Malireddi的模型要小得多并且训练速度要快得多。 卷积层的实现是高度优化的,因此额外的卷积层提高了准确性,而不会增加训练时间。 但较小的致密层比Chollet的运行速度快得多。
在等待下一步完成运行时,我将在Explanations
部分告诉您有关图层,输出形状和参数编号的信息。
后记
本篇主要讲述了使用Keras和Core ML开始机器学习,感兴趣的给个赞或者关注~~~