机器学习101

摘要

本教程将介绍如何使用机器学习的方法,对鸢(yuan一声)尾花按照种类进行分类。

教程将使用Tensorflow的eager模式来:

  1. 建立一个模型
  2. 用示例数据进行训练
  3. 使用该模型对未知数据进行预测。

读者并不需要机器学习的经验,但是需要懂一些Python。

Tensorflow编程

Tensorflow提供了很多的API,但建议从从以下高级TensorFlow概念开始学习:

  • 在开发环境中开启eager模式
  • 使用Datasets API导入数据
  • 使用TensorFlow的Keras API来构建模型和layer。

通常情况下,TensorFlow程序会按照下面的流程编写:

  1. 导入和解析数据集。
  2. 选择模型的类型。
  3. 训练模型。
  4. 使用训练后的模型做预测。

第一个程序

教程将会使用jupyter notebook在浏览器中执行Python代码。谷歌提供了一个现成的工具Colab notebook
eager模式在TensorFlow 1.7版本开始支持。

开启eager模式

eager模式能让代码立刻运行,返回具体的结果,而不是等计算图绘制完成后再执行。一旦在代码中开启了eager模式,就不能关掉了。具体说明见eager模式指导。

from __future__ import absolute_import, division, print_function

import os
import matplotlib.pyplot as plt

import tensorflow as tf
import tensorflow.contrib.eager as tfe

tf.enable_eager_execution()

print("TensorFlow version: {}".format(tf.VERSION))
print("EAGER execution: {}".format(tf.executing_eagerly()))

输出

TensorFlow version: 1.7.0
EAGER execution: True

鸢尾花分类问题

假设你是一个植物学家,现在要寻找一种能够对发现的鸢尾花分类的进行自动分类的方法。机器学习提供了许多算法来对花进行分类,比如,一个复杂的机器学习程序可以根据照片对花进行分类。鸢尾花问题简单一些,我们根据萼片和花瓣的长度和宽度测量值对其进行分类。

鸢尾花大约有300种,不过我们的程序只区分以下三种:

  • 山鸢尾(iris setosa)
  • 维吉尼亚鸢尾(iris virginica)
  • 杂色鸢尾(iris versicolor)
机器学习101_第1张图片

幸运的是,有人已经创建了一个有萼片和花瓣测量结果组成的120组鸢尾花数据集。这是一个对机器学习初学者的经典数据集。

导入和解析数据集

使用Python下载数据集文件,并结构化数据

下载数据集

train_dataset_url = 'http://download.tensorflow.org/data/iris_training.csv'
train_dataset_fp = tf.keras.utils.get_file(fname=os.path.basename(train_dataset_url), origin=train_dataset_url)
print("Local copy of the dataset file: {}".format(train_dataset_fp))

输出

Local copy of the dataset file: /home/jovyan/.keras/datasets/iris_training.csv

检查数据

下载下来的数据使用csv格式存储,可以head -n5看看前五条数据。

!head -n5 {train_dataset_fp}

结果

120,4,setosa,versicolor,virginica
6.4,2.8,5.6,2.2,2
5.0,2.3,3.3,1.0,1
4.9,2.5,4.5,1.7,2
4.9,3.1,1.5,0.1,0

可以看到:

  1. 第一条包含了数据集的信息:
  2. 一共有120组数据。每条都包含了4个特征和三个可能的标签之一。
  3. 后续行是数据记录,每行一个样本,其中:
  4. 前4栏是特征,在这里,这些字段保存花朵测量的数据,是浮点数。
  5. 最后一栏是标签,也是我们想要预测的结果。在这个数据集中,它是0,1或者2,每个数字对应一种花名。

每个标签都会与一个字符串相关联(例如“setosa”),但是使用数字会让程序处理得更快。标签号码会映射到一个名字,比如

  • 0: Iris setosa
  • 1: Iris versicolor
  • 2: Iris virginica

关于特征和标签的更多内容,请看ML Terminology section of the Machine Learning Crash Course

解析数据集

由于数据集是csv格式的文本,因此需要将特征和标签值解析为模型可以使用的格式。文件中的每一行都会被传给parse_csv函数,该函数会抓取前四个特征值并将它们合并为单个tensor,然后自后一个字段会被解析为标签。最后函数返回特征tensor和标签tensor

def parse_csv(line):
  example_defaults = [[0.], [0.], [0.], [0.], [0]]  # sets field types
  parsed_line = tf.decode_csv(line, example_defaults)
  # First 4 fields are features, combine into single tensor
  features = tf.reshape(parsed_line[:-1], shape=(4,))
  # Last field is the label
  label = tf.reshape(parsed_line[-1], shape=())
  return features, label

创建用于训练的tf.data.Dataset

TensorFlow的Dataset API能够处理给模型提供数据的很多常见场景。这是一个高级API,可用来读取数据并将其转换为可训练数据格式。

该程序使用tf.data..TextlineDataset来读取CSV格式的文件,然后通过parse_csv函数解析其中的数据。tf.data.Dataset将输入流程表示为元素集合和一系列对这些元素起作用的转换。转换的方法被链接在一起或者按顺序调用--只要确保对返回的Dataset对象保留引用即可。

如果样本是随机排列的话,训练的效果是做好的。将buffer_size设置为大于样本数量的值,然后调用tf.data.Dataset.shuffle打乱输入数据条目的顺序。为了加速训练的速度,将[batch size]设置为32,来每次处理32个样本。

train_dataset = tf.data.TextLineDataset(train_dataset_fp)
train_dataset = train_dataset.skip(1)
train_dataset = train_dataset.map(parse_csv)
train_dataset = train_dataset.shuffle(buffer_size=1000)
train_dataset = train_dataset.batch(32)

features, label = tfe.Iterator(train_dataset).next()
print('example features:', features[0])
print('example label:', label[0])

输出为

example features: tf.Tensor([7.7 3.  6.1 2.3], shape=(4,), dtype=float32)
example label: tf.Tensor(2, shape=(), dtype=int32)

选择模型类型

为什么需要模型呢?

模型是特征与标签之间的关系。对于鸢尾花分类问题来说,模型定义了萼片和花瓣测量结果与鸢尾花种类之间的关系。简单的模型可以用简单的代数来描述,但是复杂的机器学习模型有有很多难以概括的参数。

可以在不使用机器学习的情况下,确定四种特征与鸢尾花种类之间的关系吗?就是说,能否用传统的编程技术(比如大量的条件语句)来创建模型呢?如果有足够长的时间来进行研究,也许能发现这些特征值和鸢尾花物种之间的关系。不过对于更复杂的数据集来说,这样的方法会变得困难,甚至变得不可能实现。

一个好的机器学习方法能确定这个模型。如果将足够多有代表性的样本提供给正确的机器学习模型,程序就能找到特征值和B标签之间正确的关系。

选择模型

已经有很多的机器学习模型存在了,需要一些经验才能为训练选择合适的模型。这里将使用神经网络来解决鸢尾花分类问题。神经网络能找出特征值和标签之间的复杂关系。它是由一个或多个隐藏层的高度结构化的计算图。每个隐藏层由一个或多个神经元组成。有好几类神经网络存在,本教程使用密集的,或者被称为完全连接的神经网络:某一层的神经元接接收来自前一层中每个神经元的输入连接。下图展示了一个由一个输入层,两个隐藏层和一个输出层组成的密集神经网络:

机器学习101_第2张图片

当训练了上图中的模型后,输入未标记的样本时,会产生三个预测,分别是该花为鸢尾属物种的可能性。这种预测被称为推断。在这个例子中,输出预测的总和是1.0。在上图中,预测结果是

  • 0.03: 山鸢尾
  • 0.95: 杂色鸢尾
  • 0.02: 维吉尼亚鸢尾

也就是说,模型预测,这个没有被标记的样本时杂色鸢尾。

使用Keras创建模型

TensorFlow的tf.keras API时创建模型和图层的首选方式。Keras会处理将所有内容连接在一起的复杂性,这让构建模型并进行实验变得很容易。详情请见Keras文档。

tf.keras.Sequential模型是一个线性堆栈层。其初始化需要一个图层实例列表,在本教程的示例中,领个密集图层各有10个节点,一个输出图层3个代表预测标签的节点。第一层的input_shape参数是必须的,对应于数据集中特征的数量。

model = tf.keras.Sequential([
    tf.keras.layers.Dense(10, activation='relu', input_shape=(4,)),
    tf.keras.layers.Dense(10, activation='relu'),
    tf.keras.layers.Dense(3)
])

激活函数(代码中的activation)决定了单个神经元到下一层的输出。这个工作模式总体上和大脑神经元的连接方式相同。有许多可用的激活函数,隐藏层通常使用修正线性单元(即代码中的relu)。

隐藏层和神经元的理想数量取决于问题和数据集。像机器学习的其他很多方面一样,神经网络的各个部分的选择需要知识和实践。作为一个经验法则,增加隐藏层和神经元的数量通常会创建一个更强大的模型,这需要更多的数据来进行有效的训练。

训练模型

训练是机器学习中模型逐步优化或者说是模型学习数据集的阶段。训练的目标是充分了解训练数据集的结构,以及预测未知数据。如果通过训练对数据集了解太多,则预测仅适用于所看到的数据,而不能适用于一般的情况。这个问题被称之为过拟合--就像程序记住了答案而不是理解如何解决问题一样。

鸢尾花分类问题是监督式机器学习的一个例子,该模型从包含标签的样本中开始训练。在非监督式机器学习中,样本中不包含标签,相反,模型通常会在特征中找到模式。

定义损失和梯度函数

训练和评估阶段都需要计算模型的损失。这可以用来衡量预测结果和期望标签之间的差距有多大,换句话说:模型的表现有多糟糕。我们想要最小化或者说优化这个差值。

我们使用tf.losses.sparse_softmax_cross_entropy来计算损失,这个方法接受模型的预测和期望的标签作为参数。随着返回的损失值增大,预测的结果也随着变差。

def loss(model, x, y):
    y_ = model(x)
    return tf.losses.sparse_softmax_cross_entropy(labels=y, logits=y_)

def grad(model, inputs, targets):
    with tfe.GradientTape() as tape:
        loss_value = loss(model, inputs, targets)
    return tape.gradient(loss_value, model.variables)

上面的代码中grad函数调用loss函数和tfe.GradientTape来记录用于优化模型梯度的操作。更多的例子见eager教程。

创建优化器

优化器将计算出的梯度应用于模型的变量以最小化loss函数。可以把情况想象成一个曲面,通过在这个曲面上到处移动,来找到最低点

机器学习101_第3张图片
image

梯度指向上升速度最快的方向,所以我们将以相反的方向行进,并沿着山丘向下移动。通过迭代计算每个步骤(或学习速率)的损失和梯度,我们将在训练期间调整模型。慢慢的,模型会找到权重和偏差的最佳组合,以最大限度地减少损失。损失越低,模型的预测效果就越好。

TensorFlow有很多用于训练的优化算法。本教程中的模型使用tf.train.GradientDescentOptimizer,这个优化器实现了标准梯度下降算法(SGD)。learning_rate 为每次迭代的步长。这是一个超参数,通常通过调整该参数来获得更好的结果。

optimizer = tf.train.GradientDescentOptimizer(learning_rate=0.01)

训练迭代

现在万事俱备,该模型已准备好接受训练了!训练循环将数据集样本提供给模型,以帮助它做出更好的预测。下面的代码设置了一些训练步骤:

  1. 迭代每个周期。每个周期是对整个数据集的一次完整遍历。
  2. 在该周期内,对训练数据集中的每个样本进行迭代,以获取其特征(x)和标签(y)。
  3. 使用样本中特征进行预测,并于标签进行比较。测量预测的不准确性并使用它来计算模型的损失和梯度。
  4. 使用optimizer来更新模型的变量。
  5. 跟踪一些统计数据以进行可视化展示。
  6. 为每个周期执行一次上面的操作。

num_epochs是循环访问数据集集合的次数。反过来说,长时间训练模型并不能保证模型变得更好。num_epochs是一个可以调整的超参数,需要经验和实践才能找到正确的值。

train_loss_results = []
train_accuracy_results = []

num_epoches = 201
for epoch in range(num_epoches):
    epoch_loss_avg = tfe.metrics.Mean()
    epoch_accuracy = tfe.metrics.Accuracy()
    for x, y in tfe.Iterator(train_dataset):
        grads = grad(model, x, y)
        optimizer.apply_gradients(zip(grads, model.variables), global_step=tf.train.get_or_create_global_step())
        epoch_loss_avg(loss(model, x, y))
        epoch_accuracy(tf.argmax(model(x), axis=1, output_type=tf.int32), y)
        
    train_loss_results.append(epoch_loss_avg.result())
    train_accuracy_results.append(epoch_accuracy.result())
    
    if epoch % 50 == 0:
        print("Epoch {:03d}: Loss: {:.3f}, Accuracy: {:.3%}".format(
            epoch, epoch_loss_avg.result(), epoch_accuracy.result()))

输出如下:

Epoch 000: Loss: 1.005, Accuracy: 50.833%
Epoch 050: Loss: 0.384, Accuracy: 85.000%
Epoch 100: Loss: 0.257, Accuracy: 95.833%
Epoch 150: Loss: 0.183, Accuracy: 97.500%
Epoch 200: Loss: 0.134, Accuracy: 97.500%

可视化展示损失

打印出训练的进度是很有用的,但是如果能更直观的看到整个过程就更好了。TensorFlow集成了一个非常好用的可视化工具TensorBoard,不过这里我会使用mathplotlib模块创建基本的图表。

要看懂这样的图表需要一些经验,但是我们期望的是看到损失下降,准确度上升。

fig, axes = plt.subplots(2, sharex=True, figsize=(12, 8))
fig.suptitle('Training Metrics')

axes[0].set_ylabel("Loss", fontsize=14)
axes[0].plot(train_loss_results)

axes[1].set_ylabel("Accuracy", fontsize=14)
axes[1].set_xlabel("Epoch", fontsize=14)
axes[1].plot(train_accuracy_results)
plt.show()
机器学习101_第4张图片

评估模型的有效性

现在模型已经过了训练,我们可以得到它表现的统计数据。

评估意味着确定模型预测的准确度。为了确定模型在鸢尾花分类问题上的有效性,先将一些萼片和花瓣的测量结果传递给模型,要求模型预测它们代表的鸢尾花种类,然后将预测结果与实际的标签进行比较。下表展示了一个比较准确的模型,在5次预测中正确了4次,达到了80%的准确率。

样本特征 标签 模型预测
5.9 3.0 4.3 1.5 1 1
6.9 3.1 5.4 2.1 2 2
5.1 3.3 1.7 0.5 0 0
6.0 3.4 4.5 1.6 1 2
5.5 2.5 4.0 1.3 1 1

设置测试数据集

评估模型和训练模型是相似的,两者最大的区别是评估的样本来自单独的测试集,而不是训练集,为了公平评估模型的有效性,用于评估模型的样本必须和用于训练模型的样本不同。

设置测试数据集和设置训练数据集差不多。下载CSV文件,解析数据,然后打乱数据顺序:

test_url = 'http://download.tensorflow.org/data/iris_test.csv'
test_fp = tf.keras.utils.get_file(fname=os.path.basename(test_url), origin=test_url)
test_dataset = tf.data.TextLineDataset(test_fp)
test_dataset = test_dataset.skip(1)
test_dataset = test_dataset.map(parse_csv)
test_dataset = test_dataset.shuffle(1000)
test_dataset = test_dataset.batch(32)

输出为

Downloading data from http://download.tensorflow.org/data/iris_test.csv
8192/573 [============================================================================================================================================================================================================================================================================================================================================================================================================================================] - 0s 0us/step

在测试数据集上评估模型

和训练不同,评估测试数据只需要一个周期。在下面的代码中,我们遍历测试集中的每个示例,并将模型的预测与实际的标签进行比较。这用于在整个测试集中测量模型的准确性。

test_accuracy = tfe.metrics.Accuracy()

for (x, y) in tfe.Iterator(test_dataset):
    prediction = tf.argmax(model(x), axis=1, output_type=tf.int32)
    test_accuracy(prediction, y)
    
print("Test set accuracy: {:.3%}".format(test_accuracy.result()))

输出为

Test set accuracy: 96.667%

使用训练好的模型进行预测

我们已经训练了一个模型,并且“证明”了它能够对分辨鸢尾花的不同种类--尽管不是百分百准确。现在来使用训练好的模型对无标签样本做一些预测。

在实际场景中,无标签样本可能有多个来源,比如应用程序,CSV文件和feeds数据。现在,我们将手动提供三个无标签样本来预测其标签。每个种类被一个数字代表:

  • 0: 山鸢尾
  • 1:杂色鸢尾
  • 2:维吉尼亚鸢尾
class_ids = ['Iris setosa', 'Iris versicolor', 'Iris virginica']

predict_dataset = tf.convert_to_tensor([
    [5.1, 3.3, 1.7, 0.5,],
    [5.9, 3.0, 4.2, 1.5,],
    [6.9, 3.1, 5.4, 2.1]
])

predictions = model(predict_dataset)

for i, logits in enumerate(predictions):
    class_idx = tf.argmax(logits).numpy()
    name = class_ids[class_idx]
    print("Example {} prediction: {}".format(i, name))

预测结果为:

Example 0 prediction: Iris setosa
Example 1 prediction: Iris versicolor
Example 2 prediction: Iris virginica

预测全部正确!

要想深入了解机器学习模型,请查看TensorFlow编程指南。

本文翻译自Get started with eager execution

你可能感兴趣的:(机器学习101)