Machine-Learning-Mastery-Keras-深度学习教程-四-

88 阅读1小时+

Machine Learning Mastery Keras 深度学习教程(四)

原文:Machine Learning Mastery

协议:CC BY-NC-SA 4.0

如何使用 Python 和 Keras 网格搜索深度学习模型的超参数

原文: machinelearningmastery.com/grid-search-hyperparameters-deep-learning-models-python-keras/

超参数优化是深度学习的重要组成部分。

众所周知的原因之一是神经网络很难配置,并且要设置很多参数,最重要的是,单个模型的训练速度可能非常慢。

在这篇文章中,您将了解如何使用 scikit-learn python 机器学习库中的网格搜索功能来调整 Keras 深度学习模型的超参数。

阅读这篇文章后你会知道:

  • 如何封装 Keras 模型用于 scikit-learn 以及如何使用网格搜索。
  • 如何网格搜索常见的神经网络参数,如学习率,dropout 率,迭代次数和神经元数量。
  • 如何在自己的项目中定义自己的超参数调整实验。

让我们开始吧!

  • 2016 年 11 月更新:修复了在代码示例中显示网格搜索结果的小问题。
  • 2016 年 10 月更新:更新了 Keras 1.1.0,TensorFlow 0.10.0 和 scikit-learn v0.18 的示例。
  • 2017 年 3 月更新:更新了 Keras 2.0.2,TensorFlow 1.0.1 和 Theano 0.9.0 的示例。
  • 2017 年 9 月更新:更新了使用 Keras 2“epochs”代替 Keras 1“nb_epochs”的示例。
  • 更新 March / 2018 :添加了备用链接以下载数据集,因为原始图像已被删除。

How to Grid Search Hyperparameters for Deep Learning Models in Python With Keras

照片由 3V Photo提供 ,并保留所属权利。

概述

在这篇文章中,我想向您展示如何使用 scikit-learn 网格搜索功能,并为您提供一组示例,您可以将这些示例复制并粘贴到您自己的项目中作为学习的起点。

以下是我们将要讨论的主题列表:

  1. 如何在 scikit-learn 中使用 Keras 模型。
  2. 如何在 scikit-learn 中使用网格搜索。
  3. 如何调整批量大小和训练迭代次数。
  4. 如何调整优化算法。
  5. 如何调整学习率和冲量单元。
  6. 如何调整网络权重参数初始化。
  7. 如何调整激活函数。
  8. 如何调节 dropout 正则化。
  9. 如何调整隐藏层中的神经元数量。

如何在 scikit-learn 中使用 Keras 模型

Keras 模型可以通过 KerasClassifierKerasRegressor 类封装来使用 scikit-learn。

要使用这些封装器,您必须定义一个创建并返回 Keras 顺序模型的函数,然后在构造 KerasClassifier 类时将此函数传递给 build_fn 参数。

例如:

def create_model():
	...
	return model

model = KerasClassifier(build_fn=create_model)

KerasClassifier 类的构造函数可以使用传递给 model.fit()的调用的默认参数,例如迭代数和批量大小。

例如:

def create_model():
	...
	return model

model = KerasClassifier(build_fn=create_model, epochs=10)

KerasClassifier 类的构造函数也可以采用传递给自定义 create_model()函数的新参数。这些新参数也必须在 create_model()函数的签名中使用默认参数进行定义。

例如:

def create_model(dropout_rate=0.0):
	...
	return model

model = KerasClassifier(build_fn=create_model, dropout_rate=0.2)

您可以在 Keras API 文档中了解有关 scikit-learn 封装器的更多信息。

如何在 scikit-learn 中使用网格搜索

网格搜索是一种模型超参数优化技术。

在 scikit-learn 中,这种技术由 GridSearchCV 类提供。

构造此类时,必须提供一个超参数字典,以便在 param_grid 参数中进行评估,这是模型参数名称和要尝试的值所组成数组的一种映射。

默认情况下,精度是需要优化的分数,但其他评分标准也可以在 GridSearchCV 构造函数的分数参数中指定。

默认情况下,网格搜索仅使用一个线程,通过将 GridSearchCV 构造函数中的 n_jobs 参数设置为-1,该进程将使用计算机上的所有计算资源,根据您的 Keras 后端,这种方法可能会干扰主要的神经网络训练过程。

然后,GridSearchCV流程将为每种参数组合构建和评估一个模型。 尽管可以通过为GridSearchCV构造函数指定 cv 参数来覆盖交叉验证,但是交叉验证用于评估每个单独的模型,并且使用默认的三折交叉验证。

下面是定义简单网格搜索的示例:

param_grid = dict(epochs=[10,20,30])
grid = GridSearchCV(estimator=model, param_grid=param_grid, n_jobs=-1)
grid_result = grid.fit(X, Y)

完成后,您可以在 grid.fit()返回的结果对象中访问网格搜索的结果。 best_score_ 成员提供对优化过程中观察到的最佳分数的访问, 并且best_params_ 描述了获得最佳结果的参数组合。

您可以在 scikit-learn API 文档中了解有关 GridSearchCV 类的更多信息。

问题描述

既然我们知道如何使用 scras 模型学习 keras 模型以及如何在 scikit-learn 中使用网格搜索,那么让我们看看一堆例子。

所有例子都将在一个名为 Pima Indians 糖尿病分类数据集的小型标准机器学习数据集上进行演示。这是一个包含所有数字属性的小型数据集,易于使用。

  1. 下载数据集并将其直接放入您当前正在使用的名称 pima-indians-diabetes.csv (更新:从这里下载)。

在我们继续本文中的示例时,我们将汇总最佳参数。这不是网格搜索的最佳方式,因为参数可以交互,但它有利于演示目的。

并行化网格搜索的注意事项

所有示例都配置为使用并行性( n_jobs = -1 ).

如果您收到如下错误:

INFO (theano.gof.compilelock): Waiting for existing lock by process '55614' (I am process '55613')
INFO (theano.gof.compilelock): To manually release the lock, delete ...

终止进程并更改代码以不并行执行网格搜索,设置 n_jobs = 1

如何调整批量大小和迭代次数

在第一个简单的例子中,我们考虑调整批大小和网络拟合时的迭代次数。

迭代梯度下降中的批大小是在更新权重之前向网络显示的模式数,它也是网络训练的优化,定义了一次读取多少个模式并保留在内存中。

迭代次数是训练期间整个训练数据集显示给网络的次数,一些网络对批量大小敏感,例如 LSTM 递归神经网络和卷积神经网络。

在这里,我们将评估一套不同的迷你批量大小,从 10 到 100,并且将其步长设为 20。

完整的代码清单如下。

# 使用 sklearn 网格化搜索批大小和迭代次数
import numpy
from sklearn.model_selection import GridSearchCV
from keras.models import Sequential
from keras.layers import Dense
from keras.wrappers.scikit_learn import KerasClassifier
# KerasClassifier 所需的创建模型的函数
def create_model():
	# 创建模型
	model = Sequential()
	model.add(Dense(12, input_dim=8, activation='relu'))
	model.add(Dense(1, activation='sigmoid'))
	# 编译模型
	model.compile(loss='binary_crossentropy', optimizer='adam', metrics=['accuracy'])
	return model
# 固定随机种子再现性
seed = 7
numpy.random.seed(seed)
# 加载数据集
dataset = numpy.loadtxt("pima-indians-diabetes.csv", delimiter=",")
# split into input (X) and output (Y) variables
X = dataset[:,0:8]
Y = dataset[:,8]
# 创建模型
model = KerasClassifier(build_fn=create_model, verbose=0)
# 定义为网络搜索模式
batch_size = [10, 20, 40, 60, 80, 100]
epochs = [10, 50, 100]
param_grid = dict(batch_size=batch_size, epochs=epochs)
grid = GridSearchCV(estimator=model, param_grid=param_grid, n_jobs=-1)
grid_result = grid.fit(X, Y)
# 结果汇总
print("Best: %f using %s" % (grid_result.best_score_, grid_result.best_params_))
means = grid_result.cv_results_['mean_test_score']
stds = grid_result.cv_results_['std_test_score']
params = grid_result.cv_results_['params']
for mean, stdev, param in zip(means, stds, params):
    print("%f (%f) with: %r" % (mean, stdev, param))

运行此示例将生成以下输出:

Best: 0.686198 using {'epochs': 100, 'batch_size': 20}
0.348958 (0.024774) with: {'epochs': 10, 'batch_size': 10}
0.348958 (0.024774) with: {'epochs': 50, 'batch_size': 10}
0.466146 (0.149269) with: {'epochs': 100, 'batch_size': 10}
0.647135 (0.021236) with: {'epochs': 10, 'batch_size': 20}
0.660156 (0.014616) with: {'epochs': 50, 'batch_size': 20}
0.686198 (0.024774) with: {'epochs': 100, 'batch_size': 20}
0.489583 (0.075566) with: {'epochs': 10, 'batch_size': 40}
0.652344 (0.019918) with: {'epochs': 50, 'batch_size': 40}
0.654948 (0.027866) with: {'epochs': 100, 'batch_size': 40}
0.518229 (0.032264) with: {'epochs': 10, 'batch_size': 60}
0.605469 (0.052213) with: {'epochs': 50, 'batch_size': 60}
0.665365 (0.004872) with: {'epochs': 100, 'batch_size': 60}
0.537760 (0.143537) with: {'epochs': 10, 'batch_size': 80}
0.591146 (0.094954) with: {'epochs': 50, 'batch_size': 80}
0.658854 (0.054904) with: {'epochs': 100, 'batch_size': 80}
0.402344 (0.107735) with: {'epochs': 10, 'batch_size': 100}
0.652344 (0.033299) with: {'epochs': 50, 'batch_size': 100}
0.542969 (0.157934) with: {'epochs': 100, 'batch_size': 100}

我们可以看到批大小为 20,迭代次数为 100 时达到了 68%准确度的最佳结果。

如何调整训练优化算法

Keras 提供一套不同的最先进的优化算法。

在此示例中,我们调整用于训练网络的优化算法,每个算法都使用默认参数。

这是一个奇怪的例子,因为通常您会先选择一种方法,而不是专注于调整问题的参数(例如,参见下一个例子)。

在这里,我们将评估 Keras API 支持的优化算法套件。

完整的代码清单如下。

# 使用 sklearn 网格化搜索批大小和迭代次数
import numpy
from sklearn.model_selection import GridSearchCV
from keras.models import Sequential
from keras.layers import Dense
from keras.wrappers.scikit_learn import KerasClassifier
#  KerasClassifier 类所需要的创建模型的函数
def create_model(optimizer='adam'):
	# 创建模型
	model = Sequential()
	model.add(Dense(12, input_dim=8, activation='relu'))
	model.add(Dense(1, activation='sigmoid'))
	# Compile model
	model.compile(loss='binary_crossentropy', optimizer=optimizer, metrics=['accuracy'])
	return model
# 固定随机种子再现性
seed = 7
numpy.random.seed(seed)
# 加载数据集
dataset = numpy.loadtxt("pima-indians-diabetes.csv", delimiter=",")
# 将数据集分割为输入变量和输出变量
X = dataset[:,0:8]
Y = dataset[:,8]
# 创建模型
model = KerasClassifier(build_fn=create_model, epochs=100, batch_size=10, verbose=0)
# 定义为网格化搜索参数
optimizer = ['SGD', 'RMSprop', 'Adagrad', 'Adadelta', 'Adam', 'Adamax', 'Nadam']
param_grid = dict(optimizer=optimizer)
grid = GridSearchCV(estimator=model, param_grid=param_grid, n_jobs=-1)
grid_result = grid.fit(X, Y)
# 汇总结果
print("Best: %f using %s" % (grid_result.best_score_, grid_result.best_params_))
means = grid_result.cv_results_['mean_test_score']
stds = grid_result.cv_results_['std_test_score']
params = grid_result.cv_results_['params']
for mean, stdev, param in zip(means, stds, params):
    print("%f (%f) with: %r" % (mean, stdev, param))

运行此示例将生成以下输出。

Best: 0.704427 using {'optimizer': 'Adam'}
0.348958 (0.024774) with: {'optimizer': 'SGD'}
0.348958 (0.024774) with: {'optimizer': 'RMSprop'}
0.471354 (0.156586) with: {'optimizer': 'Adagrad'}
0.669271 (0.029635) with: {'optimizer': 'Adadelta'}
0.704427 (0.031466) with: {'optimizer': 'Adam'}
0.682292 (0.016367) with: {'optimizer': 'Adamax'}
0.703125 (0.003189) with: {'optimizer': 'Nadam'}

结果表明 ADAM 优化算法是最好的,准确度大约为 70%。

如何调整学习率和冲量单元

通常会预先选择优化算法来训练您的网络并调整其参数。

到目前为止,最常见的优化算法是普通的老式随机梯度下降(SGD),因为它非常清晰。在这个例子中,我们将研究优化 SGD 学习率和冲量单元。

学习率控制在每次迭代结束时更新权重的程度,并且冲量单元控制允许上一个更新影响当前权重更新的量。

我们将尝试一套小的标准学习率和 0.2 到 0.8 的冲量单元,步长为 0.2,以及 0.9(因为它在实践中可能是一个受欢迎的值)。

通常,在这样的优化中也包括迭代次数是个好主意,因为每个迭代学习量(学习率),每次迭代的更新数量(批量大小)和迭代次数存在依赖关系。

完整的代码清单如下。

# 使用 sklearn 网格化搜索学习率和冲量单元
import numpy
from sklearn.model_selection import GridSearchCV
from keras.models import Sequential
from keras.layers import Dense
from keras.wrappers.scikit_learn import KerasClassifier
from keras.optimizers import SGD
#KerasClassifier 类所需要的创建模型的函数
def create_model(learn_rate=0.01, momentum=0):
	# 创建模型
	model = Sequential()
	model.add(Dense(12, input_dim=8, activation='relu'))
	model.add(Dense(1, activation='sigmoid'))
	# 编译模型
	optimizer = SGD(lr=learn_rate, momentum=momentum)
	model.compile(loss='binary_crossentropy', optimizer=optimizer, metrics=['accuracy'])
	return model
# 固定随机种子再现性
seed = 7
numpy.random.seed(seed)
# load dataset
dataset = numpy.loadtxt("pima-indians-diabetes.csv", delimiter=",")
# 将数据划分为输入变量和输出变量
X = dataset[:,0:8]
Y = dataset[:,8]
# 创建模型
model = KerasClassifier(build_fn=create_model, epochs=100, batch_size=10, verbose=0)
# 定义网格化搜索参数
learn_rate = [0.001, 0.01, 0.1, 0.2, 0.3]
momentum = [0.0, 0.2, 0.4, 0.6, 0.8, 0.9]
param_grid = dict(learn_rate=learn_rate, momentum=momentum)
grid = GridSearchCV(estimator=model, param_grid=param_grid, n_jobs=-1)
grid_result = grid.fit(X, Y)
# 结果汇总
print("Best: %f using %s" % (grid_result.best_score_, grid_result.best_params_))
means = grid_result.cv_results_['mean_test_score']
stds = grid_result.cv_results_['std_test_score']
params = grid_result.cv_results_['params']
for mean, stdev, param in zip(means, stds, params):
    print("%f (%f) with: %r" % (mean, stdev, param))

运行此示例将生成以下输出。

Best: 0.680990 using {'learn_rate': 0.01, 'momentum': 0.0}
0.348958 (0.024774) with: {'learn_rate': 0.001, 'momentum': 0.0}
0.348958 (0.024774) with: {'learn_rate': 0.001, 'momentum': 0.2}
0.467448 (0.151098) with: {'learn_rate': 0.001, 'momentum': 0.4}
0.662760 (0.012075) with: {'learn_rate': 0.001, 'momentum': 0.6}
0.669271 (0.030647) with: {'learn_rate': 0.001, 'momentum': 0.8}
0.666667 (0.035564) with: {'learn_rate': 0.001, 'momentum': 0.9}
0.680990 (0.024360) with: {'learn_rate': 0.01, 'momentum': 0.0}
0.677083 (0.026557) with: {'learn_rate': 0.01, 'momentum': 0.2}
0.427083 (0.134575) with: {'learn_rate': 0.01, 'momentum': 0.4}
0.427083 (0.134575) with: {'learn_rate': 0.01, 'momentum': 0.6}
0.544271 (0.146518) with: {'learn_rate': 0.01, 'momentum': 0.8}
0.651042 (0.024774) with: {'learn_rate': 0.01, 'momentum': 0.9}
0.651042 (0.024774) with: {'learn_rate': 0.1, 'momentum': 0.0}
0.651042 (0.024774) with: {'learn_rate': 0.1, 'momentum': 0.2}
0.572917 (0.134575) with: {'learn_rate': 0.1, 'momentum': 0.4}
0.572917 (0.134575) with: {'learn_rate': 0.1, 'momentum': 0.6}
0.651042 (0.024774) with: {'learn_rate': 0.1, 'momentum': 0.8}
0.651042 (0.024774) with: {'learn_rate': 0.1, 'momentum': 0.9}
0.533854 (0.149269) with: {'learn_rate': 0.2, 'momentum': 0.0}
0.427083 (0.134575) with: {'learn_rate': 0.2, 'momentum': 0.2}
0.427083 (0.134575) with: {'learn_rate': 0.2, 'momentum': 0.4}
0.651042 (0.024774) with: {'learn_rate': 0.2, 'momentum': 0.6}
0.651042 (0.024774) with: {'learn_rate': 0.2, 'momentum': 0.8}
0.651042 (0.024774) with: {'learn_rate': 0.2, 'momentum': 0.9}
0.455729 (0.146518) with: {'learn_rate': 0.3, 'momentum': 0.0}
0.455729 (0.146518) with: {'learn_rate': 0.3, 'momentum': 0.2}
0.455729 (0.146518) with: {'learn_rate': 0.3, 'momentum': 0.4}
0.348958 (0.024774) with: {'learn_rate': 0.3, 'momentum': 0.6}
0.348958 (0.024774) with: {'learn_rate': 0.3, 'momentum': 0.8}
0.348958 (0.024774) with: {'learn_rate': 0.3, 'momentum': 0.9}

我们可以看到相对 SGD 在这个问题上表现不是很好,但是使用 0.01 的学习率和 0.0 的冲量单元获得了 68%的精确度

如何调整网络权重初始化

神经网络权重初始化过去很简单:使用较小的随机值。

现在有一套不同的技术可供选择。 Keras 提供清单

在此示例中,我们将通过评估所有可用技术来调整网络权重初始化的选择。

我们将在每一层使用相同的权重初始化方法,理想情况下,根据每层使用的激活函数,使用不同的权重初始化方案可能更好,在下面的示例中,因为是二分类预测,我们使用线性修正单元作为隐藏层,并且我们使用 sigmoid 作为输出层。

完整的代码清单如下。

# 使用 sklearn 网格化搜索权重参数初始化
import numpy
from sklearn.model_selection import GridSearchCV
from keras.models import Sequential
from keras.layers import Dense
from keras.wrappers.scikit_learn import KerasClassifier
#  KerasClassifier 需要的创建模型的函数
def create_model(init_mode='uniform'):
	# 创建模型
	model = Sequential()
	model.add(Dense(12, input_dim=8, kernel_initializer=init_mode, activation='relu'))
	model.add(Dense(1, kernel_initializer=init_mode, activation='sigmoid'))
	# 编译模型
	model.compile(loss='binary_crossentropy', optimizer='adam', metrics=['accuracy'])
	return model
# 固定随机种子再现性
seed = 7
numpy.random.seed(seed)
# 加载数据集
dataset = numpy.loadtxt("pima-indians-diabetes.csv", delimiter=",")
# 将输入化为·输入变量 X 和输出变量 y
X = dataset[:,0:8]
Y = dataset[:,8]
# 创建模型
model = KerasClassifier(build_fn=create_model, epochs=100, batch_size=10, verbose=0)
#定义网格化搜索的参数
init_mode = ['uniform', 'lecun_uniform', 'normal', 'zero', 'glorot_normal', 'glorot_uniform', 'he_normal', 'he_uniform']
param_grid = dict(init_mode=init_mode)
grid = GridSearchCV(estimator=model, param_grid=param_grid, n_jobs=-1)
grid_result = grid.fit(X, Y)
# 汇总结果
print("Best: %f using %s" % (grid_result.best_score_, grid_result.best_params_))
means = grid_result.cv_results_['mean_test_score']
stds = grid_result.cv_results_['std_test_score']
params = grid_result.cv_results_['params']
for mean, stdev, param in zip(means, stds, params):
    print("%f (%f) with: %r" % (mean, stdev, param))

运行此示例将生成以下输出。

Best: 0.720052 using {'init_mode': 'uniform'}
0.720052 (0.024360) with: {'init_mode': 'uniform'}
0.348958 (0.024774) with: {'init_mode': 'lecun_uniform'}
0.712240 (0.012075) with: {'init_mode': 'normal'}
0.651042 (0.024774) with: {'init_mode': 'zero'}
0.700521 (0.010253) with: {'init_mode': 'glorot_normal'}
0.674479 (0.011201) with: {'init_mode': 'glorot_uniform'}
0.661458 (0.028940) with: {'init_mode': 'he_normal'}
0.678385 (0.004872) with: {'init_mode': 'he_uniform'}

我们可以看到,使用均匀权重初始化方案实现了最佳结果,能够达到大概 72%的表现。

如何调整神经元激活函数

激活功能控制各个神经元的非线性以及何时触发。

通常,线性修正激活函数是最流行的,过去则是 sigmoid 和 tanh 函数,这些函数可能仍然更适合于不同的问题。

在这个例子中,我们将评估 Keras 中可用的不同激活函数套件。我们将仅在隐藏层中使用这些函数,因为我们在输出中需要 sigmoid 激活函数以用于二分类问题。

通常,将数据准备到不同传递函数的范围是一个好主意,在这种情况下我们不需要这样做。

完整的代码清单如下。

# 使用 sklearn 网格化搜索激活函数
import numpy
from sklearn.model_selection import GridSearchCV
from keras.models import Sequential
from keras.layers import Dense
from keras.wrappers.scikit_learn import KerasClassifier
#  KerasClassifier 所需要的创建模型函数
def create_model(activation='relu'):
	# 创建模型
	model = Sequential()
	model.add(Dense(12, input_dim=8, kernel_initializer='uniform', activation=activation))
	model.add(Dense(1, kernel_initializer='uniform', activation='sigmoid'))
	# 编译模型
	model.compile(loss='binary_crossentropy', optimizer='adam', metrics=['accuracy'])
	return model
# 固定随机种子再现性
seed = 7
numpy.random.seed(seed)
# 加载数据集
dataset = numpy.loadtxt("pima-indians-diabetes.csv", delimiter=",")
# split into input (X) and output (Y) variables
X = dataset[:,0:8]
Y = dataset[:,8]
# 创建模型
model = KerasClassifier(build_fn=create_model, epochs=100, batch_size=10, verbose=0)
# 定义网格化搜索参数
activation = ['softmax', 'softplus', 'softsign', 'relu', 'tanh', 'sigmoid', 'hard_sigmoid', 'linear']
param_grid = dict(activation=activation)
grid = GridSearchCV(estimator=model, param_grid=param_grid, n_jobs=-1)
grid_result = grid.fit(X, Y)
# 结果汇总
print("Best: %f using %s" % (grid_result.best_score_, grid_result.best_params_))
means = grid_result.cv_results_['mean_test_score']
stds = grid_result.cv_results_['std_test_score']
params = grid_result.cv_results_['params']
for mean, stdev, param in zip(means, stds, params):
    print("%f (%f) with: %r" % (mean, stdev, param))

运行此示例将生成以下输出。

Best: 0.722656 using {'activation': 'linear'}
0.649740 (0.009744) with: {'activation': 'softmax'}
0.720052 (0.032106) with: {'activation': 'softplus'}
0.688802 (0.019225) with: {'activation': 'softsign'}
0.720052 (0.018136) with: {'activation': 'relu'}
0.691406 (0.019401) with: {'activation': 'tanh'}
0.680990 (0.009207) with: {'activation': 'sigmoid'}
0.691406 (0.014616) with: {'activation': 'hard_sigmoid'}
0.722656 (0.003189) with: {'activation': 'linear'}

令人惊讶的是(至少对我而言),“线性”激活功能获得了最佳结果,精确度约为 72%。

如何调整 dropout 正则化

在这个例子中,我们将研究调整正则化的 dropout 率,以限制过拟合并提高模型的推广能力。

为了获得良好的结果,dropout 最好与权重约束相结合,例如最大范数约束。

有关在 Keras 深度学习模型中使用 dropout 的更多信息,请参阅帖子:

这涉及拟合 dropout 率和权重约束,们将尝试 0.0 到 0.9 之间的 dropout 失百分比(1.0 没有意义)和 0 到 5 之间的 maxnorm 权重约束值。

完整的代码清单如下。

#使用 sklearn 网格化搜索 dropout 率
import numpy
from sklearn.model_selection import GridSearchCV
from keras.models import Sequential
from keras.layers import Dense
from keras.layers import Dropout
from keras.wrappers.scikit_learn import KerasClassifier
from keras.constraints import maxnorm
# KerasClassifier 需要的创建模型的函数
def create_model(dropout_rate=0.0, weight_constraint=0):
	# 创建模型
	model = Sequential()
	model.add(Dense(12, input_dim=8, kernel_initializer='uniform', activation='linear', kernel_constraint=maxnorm(weight_constraint)))
	model.add(Dropout(dropout_rate))
	model.add(Dense(1, kernel_initializer='uniform', activation='sigmoid'))
	# 编译模型
	model.compile(loss='binary_crossentropy', optimizer='adam', metrics=['accuracy'])
	return model
# 固定随机种子再现性
seed = 7
numpy.random.seed(seed)
# 加载数据集
dataset = numpy.loadtxt("pima-indians-diabetes.csv", delimiter=",")
# split into input (X) and output (Y) variables
X = dataset[:,0:8]
Y = dataset[:,8]
# 创建模型
model = KerasClassifier(build_fn=create_model, epochs=100, batch_size=10, verbose=0)
# 定义网格化搜索参数
weight_constraint = [1, 2, 3, 4, 5]
dropout_rate = [0.0, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9]
param_grid = dict(dropout_rate=dropout_rate, weight_constraint=weight_constraint)
grid = GridSearchCV(estimator=model, param_grid=param_grid, n_jobs=-1)
grid_result = grid.fit(X, Y)
# 结果汇总
print("Best: %f using %s" % (grid_result.best_score_, grid_result.best_params_))
means = grid_result.cv_results_['mean_test_score']
stds = grid_result.cv_results_['std_test_score']
params = grid_result.cv_results_['params']
for mean, stdev, param in zip(means, stds, params):
    print("%f (%f) with: %r" % (mean, stdev, param))

运行此示例将生成以下输出。

Best: 0.723958 using {'dropout_rate': 0.2, 'weight_constraint': 4}
0.696615 (0.031948) with: {'dropout_rate': 0.0, 'weight_constraint': 1}
0.696615 (0.031948) with: {'dropout_rate': 0.0, 'weight_constraint': 2}
0.691406 (0.026107) with: {'dropout_rate': 0.0, 'weight_constraint': 3}
0.708333 (0.009744) with: {'dropout_rate': 0.0, 'weight_constraint': 4}
0.708333 (0.009744) with: {'dropout_rate': 0.0, 'weight_constraint': 5}
0.710937 (0.008438) with: {'dropout_rate': 0.1, 'weight_constraint': 1}
0.709635 (0.007366) with: {'dropout_rate': 0.1, 'weight_constraint': 2}
0.709635 (0.007366) with: {'dropout_rate': 0.1, 'weight_constraint': 3}
0.695312 (0.012758) with: {'dropout_rate': 0.1, 'weight_constraint': 4}
0.695312 (0.012758) with: {'dropout_rate': 0.1, 'weight_constraint': 5}
0.701823 (0.017566) with: {'dropout_rate': 0.2, 'weight_constraint': 1}
0.710938 (0.009568) with: {'dropout_rate': 0.2, 'weight_constraint': 2}
0.710938 (0.009568) with: {'dropout_rate': 0.2, 'weight_constraint': 3}
0.723958 (0.027126) with: {'dropout_rate': 0.2, 'weight_constraint': 4}
0.718750 (0.030425) with: {'dropout_rate': 0.2, 'weight_constraint': 5}
0.721354 (0.032734) with: {'dropout_rate': 0.3, 'weight_constraint': 1}
0.707031 (0.036782) with: {'dropout_rate': 0.3, 'weight_constraint': 2}
0.707031 (0.036782) with: {'dropout_rate': 0.3, 'weight_constraint': 3}
0.694010 (0.019225) with: {'dropout_rate': 0.3, 'weight_constraint': 4}
0.709635 (0.006639) with: {'dropout_rate': 0.3, 'weight_constraint': 5}
0.704427 (0.008027) with: {'dropout_rate': 0.4, 'weight_constraint': 1}
0.717448 (0.031304) with: {'dropout_rate': 0.4, 'weight_constraint': 2}
0.718750 (0.030425) with: {'dropout_rate': 0.4, 'weight_constraint': 3}
0.718750 (0.030425) with: {'dropout_rate': 0.4, 'weight_constraint': 4}
0.722656 (0.029232) with: {'dropout_rate': 0.4, 'weight_constraint': 5}
0.720052 (0.028940) with: {'dropout_rate': 0.5, 'weight_constraint': 1}
0.703125 (0.009568) with: {'dropout_rate': 0.5, 'weight_constraint': 2}
0.716146 (0.029635) with: {'dropout_rate': 0.5, 'weight_constraint': 3}
0.709635 (0.008027) with: {'dropout_rate': 0.5, 'weight_constraint': 4}
0.703125 (0.011500) with: {'dropout_rate': 0.5, 'weight_constraint': 5}
0.707031 (0.017758) with: {'dropout_rate': 0.6, 'weight_constraint': 1}
0.701823 (0.018688) with: {'dropout_rate': 0.6, 'weight_constraint': 2}
0.701823 (0.018688) with: {'dropout_rate': 0.6, 'weight_constraint': 3}
0.690104 (0.027498) with: {'dropout_rate': 0.6, 'weight_constraint': 4}
0.695313 (0.022326) with: {'dropout_rate': 0.6, 'weight_constraint': 5}
0.697917 (0.014382) with: {'dropout_rate': 0.7, 'weight_constraint': 1}
0.697917 (0.014382) with: {'dropout_rate': 0.7, 'weight_constraint': 2}
0.687500 (0.008438) with: {'dropout_rate': 0.7, 'weight_constraint': 3}
0.704427 (0.011201) with: {'dropout_rate': 0.7, 'weight_constraint': 4}
0.696615 (0.016367) with: {'dropout_rate': 0.7, 'weight_constraint': 5}
0.680990 (0.025780) with: {'dropout_rate': 0.8, 'weight_constraint': 1}
0.699219 (0.019401) with: {'dropout_rate': 0.8, 'weight_constraint': 2}
0.701823 (0.015733) with: {'dropout_rate': 0.8, 'weight_constraint': 3}
0.684896 (0.023510) with: {'dropout_rate': 0.8, 'weight_constraint': 4}
0.696615 (0.017566) with: {'dropout_rate': 0.8, 'weight_constraint': 5}
0.653646 (0.034104) with: {'dropout_rate': 0.9, 'weight_constraint': 1}
0.677083 (0.012075) with: {'dropout_rate': 0.9, 'weight_constraint': 2}
0.679688 (0.013902) with: {'dropout_rate': 0.9, 'weight_constraint': 3}
0.669271 (0.017566) with: {'dropout_rate': 0.9, 'weight_constraint': 4}
0.669271 (0.012075) with: {'dropout_rate': 0.9, 'weight_constraint': 5}

我们可以看到,20%的 dropout 率和 4 的最大权重约束能够达到最佳精确度约为 72%。

如何调整隐藏层中的神经元数量

层中神经元的数量是调整的重要参数。通常,层中的神经元的数量控制网络的表示能力,至少在拓扑中的那个点处。

此外,通常,足够大的单层网络可以近似于任何其他神经网络,至少在理论上

在这个例子中,我们将研究调整单个隐藏层中的神经元数量,我们将以 5 的步长尝试 1 到 30 的值。

较大的网络需要更多的训练,并且至少批大小和迭代次数应理想地用神经元的数量来优化。

完整的代码清单如下。

# 使用 sklearn 网格化搜索神经元数量
import numpy
from sklearn.model_selection import GridSearchCV
from keras.models import Sequential
from keras.layers import Dense
from keras.layers import Dropout
from keras.wrappers.scikit_learn import KerasClassifier
from keras.constraints import maxnorm
# KerasClassifier 所需要的创建模型的函数
def create_model(neurons=1):
	# 创建模型
	model = Sequential()
	model.add(Dense(neurons, input_dim=8, kernel_initializer='uniform', activation='linear', kernel_constraint=maxnorm(4)))
	model.add(Dropout(0.2))
	model.add(Dense(1, kernel_initializer='uniform', activation='sigmoid'))
	# 编译模型
	model.compile(loss='binary_crossentropy', optimizer='adam', metrics=['accuracy'])
	return model
# 固定随机种子再现性
seed = 7
numpy.random.seed(seed)
# 加载数据集
dataset = numpy.loadtxt("pima-indians-diabetes.csv", delimiter=",")
#将数据集划分为输入变量 X 和输出变量 y
X = dataset[:,0:8]
Y = dataset[:,8]
# 创建模型
model = KerasClassifier(build_fn=create_model, epochs=100, batch_size=10, verbose=0)
# 定义网格化搜索的参数
neurons = [1, 5, 10, 15, 20, 25, 30]
param_grid = dict(neurons=neurons)
grid = GridSearchCV(estimator=model, param_grid=param_grid, n_jobs=-1)
grid_result = grid.fit(X, Y)
# 结果汇总
print("Best: %f using %s" % (grid_result.best_score_, grid_result.best_params_))
means = grid_result.cv_results_['mean_test_score']
stds = grid_result.cv_results_['std_test_score']
params = grid_result.cv_results_['params']
for mean, stdev, param in zip(means, stds, params):
    print("%f (%f) with: %r" % (mean, stdev, param))

运行此示例将生成以下输出。

Best: 0.714844 using {'neurons': 5}
0.700521 (0.011201) with: {'neurons': 1}
0.714844 (0.011049) with: {'neurons': 5}
0.712240 (0.017566) with: {'neurons': 10}
0.705729 (0.003683) with: {'neurons': 15}
0.696615 (0.020752) with: {'neurons': 20}
0.713542 (0.025976) with: {'neurons': 25}
0.705729 (0.008027) with: {'neurons': 30}

我们可以看到,在隐藏层中具有 5 个神经元的网络实现了最佳结果,精度约为 71%。

超参数优化提示

本节列出了调整神经网络超参数时要考虑的一些方便提示。

  • k 折交叉验证:您可以看到本文中示例的结果显示出一些差异,使用默认的 3 折交叉验证,但是 k = 5 或 k = 10 可能更稳定,请仔细选择交叉验证配置以确保结果稳定。
  • 回顾整个网格:不要只关注最佳结果,检查整个结果网格并寻找支持配置决策的趋势。
  • 并行化:如果可以的话,使用你所有的核心,神经网络训练很慢,我们经常想尝试很多不同的参数,考虑搞砸很多 AWS 实例
  • 使用数据集样本:因为网络训练很慢,所以尝试在训练数据集的较小样本上训练它们,只是为了了解参数的一般方向而不是最佳配置。
  • 从粗网格开始:从粗粒度网格开始,一旦缩小范围,就可以缩放到更细粒度的网格。
  • 不转移结果:结果通常是特定于问题的。尝试在您看到的每个新问题上避免喜欢的配置。您在一个问题上发现的最佳结果不太可能转移到您的下一个项目,而是寻找更广泛的趋势,例如层数或参数之间的关系。
  • 再现性是一个问题:虽然我们在 NumPy 中为随机数生成器设置种子,但结果不是 100%可重复的,当网格搜索包装 Keras 模型时,重复性要高于本文中提供的内容。

摘要

在这篇文章中,您了解了如何使用 Keras 和 scikit-learn 在 Python 中调整深度学习网络的超参数。

具体来说,你学到了:

  • 如何包装 Keras 模型用于 scikit-learn 以及如何使用网格搜索
  • 如何为 Keras 模型网格搜索一套不同的标准神经网络参数
  • 如何设计自己的超参数优化实验

你有调整大型神经网络超参数的经验吗?请在下面分享您的故事。

您对神经网络的超参数优化还是关于这篇文章有什么疑问?在评论中提出您的问题,我会尽力回答。

使用 Python 和 Keras 将卷积神经网络用于手写数字识别

原文: machinelearningmastery.com/handwritten-digit-recognition-using-convolutional-neural-networks-python-keras/

图像数据中的物体识别是深度学习技术的广泛示例。

用于机器学习和深度学习的物体识别的“hello world”是用于手写数字识别的 MNIST 数据集。

在这篇文章中,您将了解如何使用 Keras 深度学习库开发深度学习模型,以便在 Python 中使用 MNIST 手写数字识别任务实现近乎最优表现。

完成本教程后,您将了解:

  • 如何在 Keras 中加载 MNIST 数据集。
  • 如何开发和评估 MNIST 问题的基线神经网络模型。
  • 如何实现和评估一个简单的 MNIST 卷积神经网络。
  • 如何实现接近最先进的 MNIST 深度学习模型。

让我们开始吧!

  • 2016 年 10 月更新:更新了 Keras 1.1.0,TensorFlow 0.10.0 和 scikit-learn v0.18 的示例。
  • 2017 年 3 月更新:更新了 Keras 2.0.2,TensorFlow 1.0.1 和 Theano 0.9.0 的示例。

Handwritten Digit Recognition using Convolutional Neural Networks in Python with Keras

照片由 Jamie提供,并保留所属权利。

MNIST 手写数字识别问题的描述

MNIST 问题是由 Yann LeCun,Corinna Cortes 和 Christopher Burges 开发的用于评估手写数字分类问题的机器学习模型的数据集。

该数据集由许多可从国家标准与技术研究所(NIST)获得的扫描文档数据集构建。这是数据集作为 Modified NIST 或 MNIST 数据集的名称来源。

数字图像取自各种扫描文档,标准化并居中,这使其成为评估模型的优秀数据集,使开发人员能够专注于机器学习,只需要很少的数据清理或数据预处理工作。

每个图像是 28 乘 28 像素的正方形(总共 784 个像素),数据集的标准输出用于评估和比较模型,其中 60,000 个图像用于训练模型,并且有单独的 10,000 个图像集用于测试模型。

这是一项数字识别任务,因此,有 10 个数字(0 到 9)或 10 个类来预测,使用预测误差报告结果,预测误差仅仅是反向分类精度。

一些优异的成果实现了小于 1%的预测误差,最先进的方案是使用大型卷积神经网络可以实现约 0.2%的预测误差。有一份最新的成果列表,以及有关 MNIST 和 Rodrigo Benenson 网页上其他数据集的相关论文的链接。

在 Keras 中加载 MNIST 数据集

Keras 深度学习库提供了一种加载 MNIST 数据集的便捷方法。

第一次调用此函数时会自动下载数据集,并将其作为 15MB 文件存储在〜/ .keras / datasets / mnist.pkl.gz 的主目录中。

这对于开发和测试深度学习模型非常方便。

为了演示加载 MNIST 数据集是多么容易,我们将首先编写一个小脚本来下载和可视化训练数据集中的前 4 个图像。

# 绘制 ad hoc minst 实例图像
from keras.datasets import mnist
import matplotlib.pyplot as plt
# 加载数据集
(X_train, y_train), (X_test, y_test) = mnist.load_data()
# 绘制灰度级别的 4 幅图像
plt.subplot(221)
plt.imshow(X_train[0], cmap=plt.get_cmap('gray'))
plt.subplot(222)
plt.imshow(X_train[1], cmap=plt.get_cmap('gray'))
plt.subplot(223)
plt.imshow(X_train[2], cmap=plt.get_cmap('gray'))
plt.subplot(224)
plt.imshow(X_train[3], cmap=plt.get_cmap('gray'))
# 图像显示
plt.show()

您可以看到下载和加载 MNIST 数据集就像调用mnist.load_data()函数一样简单。运行上面的示例,您应该看到以下图像:

Examples from the MNIST dataset

MNIST 数据集中的示例

具有多层感知机的基线模型

我们真的需要像卷积神经网络这样的复杂模型来获得 MNIST 的最佳结果吗?

使用具有单个隐藏层的非常简单的神经网络模型,您可以获得非常好的结果。在本节中,我们将创建一个简单的多层感知机模型,其误差率为 1.74%,我们将使用它作为比较更复杂的卷积神经网络模型的基准。

让我们从导入我们需要的类和函数开始。

import numpy
from keras.datasets import mnist
from keras.models import Sequential
from keras.layers import Dense
from keras.layers import Dropout
from keras.utils import np_utils

将随机数生成器初始化为常量始终是一个好主意,以确保脚本的结果是可重现的。

# 固定随机种子再现性
seed = 7
numpy.random.seed(seed)

现在我们可以使用 Keras 辅助函数加载 MNIST 数据集。

# 加载数据集
(X_train, y_train), (X_test, y_test) = mnist.load_data()

训练数据集被构造为实例,图像宽度和图像高度的三维数组。对于多层感知机模型,我们必须将图像缩小为像素向量,在这种情况下,28×28 大小的图像将是 784 像素输入值。

我们可以使用 NumPy 数组上的 reshape()函数轻松地进行转换,我们还可以通过强制像素值的精度为 32 位来降低我们的内存需求,这是 Keras 使用的默认精度。

# 将像素矩阵转换为向量
num_pixels = X_train.shape[1] * X_train.shape[2]
X_train = X_train.reshape(X_train.shape[0], num_pixels).astype('float32')
X_test = X_test.reshape(X_test.shape[0], num_pixels).astype('float32')

像素值是 0 到 255 之间的灰度级,在使用神经网络模型时,执行输入值的某些缩放几乎总是一个好主意。因为这些比例是众所周知的并且表现良好,所以我们可以通过将每个值除以最大值 255 来非常快速地将像素值标准化到 0 和 1 的范围。

# 标准化输入 0-255 to 0-1
X_train = X_train / 255
X_test = X_test / 255

最后,输出变量是 0 到 9 之间的整数。这是一个多分类问题,因此,较好的实现方法是使用类值的单热编码,将类整数的向量转换为二进制矩阵。

我们可以使用 Keras 中内置的 np_utils.to_categorical()辅助函数轻松完成此操作。

# 单热编码输出
y_train = np_utils.to_categorical(y_train)
y_test = np_utils.to_categorical(y_test)
num_classes = y_test.shape[1]

我们现在准备创建我们简单的神经网络模型,我们将在函数中定义我们的模型。如果您想稍后扩展示例并尝试获得更好的分数,这将会非常方便。

# 定义基线模型
def baseline_model():
	# 创建模型
	model = Sequential()
	model.add(Dense(num_pixels, input_dim=num_pixels, kernel_initializer='normal', activation='relu'))
	model.add(Dense(num_classes, kernel_initializer='normal', activation='softmax'))
	# 编译模型
	model.compile(loss='categorical_crossentropy', optimizer='adam', metrics=['accuracy'])
	return model

该模型是一个简单的神经网络,其中一个隐藏层具有与输入相同数量的神经元(784),非线性激活函数被用于隐藏层中的神经元。

在输出层上使用 softmax 激活函数将输出转换为类似概率的值,并允许选择 10 个类别中的一个类作为模型的输出预测,对数损失用作损失函数(在 Keras 中称为 categorical_crossentropy),并且使用有效的 ADAM 梯度下降算法来学习权重。

我们现在可以拟合和评估模型,该模型将拟合 10 次迭代,每 200 个图像更新一次。测试数据用作验证数据集,允许您在训练时查看模型的表现。参数verbose=2表示将每个训练时期的输出减少到一行。

最后,测试数据集用于评估模型并打印分类错误率。

# 构建模型
model = baseline_model()
# 拟合模型
model.fit(X_train, y_train, validation_data=(X_test, y_test), epochs=10, batch_size=200, verbose=2)
# 最后,评估模型
scores = model.evaluate(X_test, y_test, verbose=0)
print("Baseline Error: %.2f%%" % (100-scores[1]*100))

在 CPU 上运行时运行该示例可能需要几分钟,您应该看到下面的输出,在极少数代码行中定义的这种非常简单的网络实现了可观的 1.91%的错误率。

Train on 60000 samples, validate on 10000 samples
Epoch 1/10
8s - loss: 0.2797 - acc: 0.9209 - val_loss: 0.1413 - val_acc: 0.9576
Epoch 2/10
8s - loss: 0.1117 - acc: 0.9677 - val_loss: 0.0919 - val_acc: 0.9702
Epoch 3/10
8s - loss: 0.0718 - acc: 0.9796 - val_loss: 0.0782 - val_acc: 0.9774
Epoch 4/10
8s - loss: 0.0505 - acc: 0.9858 - val_loss: 0.0758 - val_acc: 0.9762
Epoch 5/10
8s - loss: 0.0374 - acc: 0.9892 - val_loss: 0.0670 - val_acc: 0.9792
Epoch 6/10
8s - loss: 0.0268 - acc: 0.9929 - val_loss: 0.0630 - val_acc: 0.9803
Epoch 7/10
8s - loss: 0.0210 - acc: 0.9945 - val_loss: 0.0604 - val_acc: 0.9815
Epoch 8/10
8s - loss: 0.0140 - acc: 0.9969 - val_loss: 0.0620 - val_acc: 0.9808
Epoch 9/10
8s - loss: 0.0107 - acc: 0.9978 - val_loss: 0.0598 - val_acc: 0.9812
Epoch 10/10
7s - loss: 0.0080 - acc: 0.9985 - val_loss: 0.0588 - val_acc: 0.9809
Baseline Error: 1.91%

用于 MNIST 的简单卷积神经网络

现在我们已经看到如何加载 MNIST 数据集并在其上训练一个简单的多层感知机模型,现在可以开发更复杂的卷积神经网络或 CNN 模型。

Keras 确实为创建卷积神经网络提供了很多函数。

在本节中,我们将为 MNIST 创建一个简单的 CNN,演示如何使用现代 CNN 实现的所有方面,包括卷积层,池化层和全连接层。

第一步是导入所需的类和函数。

import numpy
from keras.datasets import mnist
from keras.models import Sequential
from keras.layers import Dense
from keras.layers import Dropout
from keras.layers import Flatten
from keras.layers.convolutional import Conv2D
from keras.layers.convolutional import MaxPooling2D
from keras.utils import np_utils
from keras import backend as K
K.set_image_dim_ordering('th')

同样,我们总是将随机数生成器初始化为恒定的随机种子值,以便重现结果。

# 固定随机种子再现性
seed = 7
numpy.random.seed(seed)

接下来,我们需要加载 MNIST 数据集并对其进行整形,以便它适合用于训练 CNN。在 Keras 中,用于二维卷积的层期望像素值具有[像素] [宽度] [高度]的尺寸。

在 RGB 的情况下,对于红色,绿色和蓝色分量,第一维像素将是 3,并且对于每个彩色图像将具有 3 维图像分量输入,在 MNIST 中像素值是灰度级的情况下,像素维度将设置为 1。

# 加载数据
(X_train, y_train), (X_test, y_test) = mnist.load_data()
# reshape to be [samples][pixels][width][height]
X_train = X_train.reshape(X_train.shape[0], 1, 28, 28).astype('float32')
X_test = X_test.reshape(X_test.shape[0], 1, 28, 28).astype('float32')

和之前前一样,最好将像素值标准化为 0 和 1 范围,并对输出变量进行热编码。

#标准化输入 from 0-255 to 0-1
X_train = X_train / 255
X_test = X_test / 255
# 单热编码输出 outputs
y_train = np_utils.to_categorical(y_train)
y_test = np_utils.to_categorical(y_test)
num_classes = y_test.shape[1]

接下来我们定义我们的神经网络模型

卷积神经网络比标准的多层感知机更复杂,因此我们将首先使用一个简单的结构,使用所有元素来获得最先进的结果。下面总结了网络的架构。

  1. 第一个隐藏层是卷积层,称为 Convolution2D。该层具有 32 个特征图,其大小为 5×5,并具有非线性激活函数。这是输入层,预期结构轮廓在[像素] [宽度] [高度]以上的图像。
  2. 接下来,我们定义一个池化层,它采用最大的 MaxPooling2D。池大小设置为 2×2。
  3. 下一层是使用 dropout 的正则化层,称为 Dropout。它被配置为随机消除层中 20%的神经元以减少过拟合。
  4. 接下来是将 2D 矩阵数据转换为名为 Flatten 的向量的层。它允许输出由标准的完全连接层处理。
  5. 接下来是具有 128 个神经元和非线性激活函数的完全连接层。
  6. 最后,输出层有 10 个类的 10 个神经元和 softmax 激活函数,为每个类输出类似概率的预测。

如前所述,使用对数损失和 ADAM 梯度下降算法训练模型。

def baseline_model():
	# 创建模型
	model = Sequential()
	model.add(Conv2D(32, (5, 5), input_shape=(1, 28, 28), activation='relu'))
	model.add(MaxPooling2D(pool_size=(2, 2)))
	model.add(Dropout(0.2))
	model.add(Flatten())
	model.add(Dense(128, activation='relu'))
	model.add(Dense(num_classes, activation='softmax'))
	# 编译模型
	model.compile(loss='categorical_crossentropy', optimizer='adam', metrics=['accuracy'])
	return model

我们使用多层感知机以与以前相同的方式评估模型,CNN 拟合用于 10 个迭代次数,批大小设置为 200。

# 构建模型
model = baseline_model()
# 拟合模型
model.fit(X_train, y_train, validation_data=(X_test, y_test), epochs=10, batch_size=200, verbose=2)
# 最后评估模型
scores = model.evaluate(X_test, y_test, verbose=0)
print("CNN Error: %.2f%%" % (100-scores[1]*100))

运行该示例,在每个时期打印训练和验证测试的准确率,并且在末尾打印分类的错误率。

时期可能需要大约 45 秒才能在 GPU 上运行(例如在 AWS 上)。您可以看到网络的错误率达到 1.03,这比我们上面的简单多层感知机模型要好。

Train on 60000 samples, validate on 10000 samples
Epoch 1/10
60000/60000 [==============================] - 120s - loss: 0.2346 - acc: 0.9334 - val_loss: 0.0774 - val_acc: 0.9762
Epoch 2/10
60000/60000 [==============================] - 42s - loss: 0.0716 - acc: 0.9782 - val_loss: 0.0454 - val_acc: 0.9856
Epoch 3/10
60000/60000 [==============================] - 42s - loss: 0.0520 - acc: 0.9842 - val_loss: 0.0429 - val_acc: 0.9853
Epoch 4/10
60000/60000 [==============================] - 42s - loss: 0.0406 - acc: 0.9868 - val_loss: 0.0369 - val_acc: 0.9876
Epoch 5/10
60000/60000 [==============================] - 42s - loss: 0.0331 - acc: 0.9898 - val_loss: 0.0345 - val_acc: 0.9884
Epoch 6/10
60000/60000 [==============================] - 42s - loss: 0.0265 - acc: 0.9917 - val_loss: 0.0323 - val_acc: 0.9905
Epoch 7/10
60000/60000 [==============================] - 42s - loss: 0.0220 - acc: 0.9931 - val_loss: 0.0337 - val_acc: 0.9894
Epoch 8/10
60000/60000 [==============================] - 42s - loss: 0.0201 - acc: 0.9934 - val_loss: 0.0316 - val_acc: 0.9892
Epoch 9/10
60000/60000 [==============================] - 42s - loss: 0.0163 - acc: 0.9947 - val_loss: 0.0281 - val_acc: 0.9908
Epoch 10/10
60000/60000 [==============================] - 42s - loss: 0.0135 - acc: 0.9956 - val_loss: 0.0327 - val_acc: 0.9897
CNN Error: 1.03%

用于 MNIST 的较大卷积神经网络

现在我们已经看到了如何创建一个简单的 CNN,让我们来看看能够接近最新结果的模型。

我们导入类和函数,然后加载与前一个 CNN 示例相同的预处理数据。

# MNIST Dataset 更大的 CNN
import numpy
from keras.datasets import mnist
from keras.models import Sequential
from keras.layers import Dense
from keras.layers import Dropout
from keras.layers import Flatten
from keras.layers.convolutional import Conv2D
from keras.layers.convolutional import MaxPooling2D
from keras.utils import np_utils
from keras import backend as K
K.set_image_dim_ordering('th')
# 固定随机种子再现性
seed = 7
numpy.random.seed(seed)
# 加载数据
(X_train, y_train), (X_test, y_test) = mnist.load_data()
# reshape to be [samples][pixels][width][height]
X_train = X_train.reshape(X_train.shape[0], 1, 28, 28).astype('float32')
X_test = X_test.reshape(X_test.shape[0], 1, 28, 28).astype('float32')
# 标准化输入 0-255 to 0-1
X_train = X_train / 255
X_test = X_test / 255
#  单热编码输出
y_train = np_utils.to_categorical(y_train)
y_test = np_utils.to_categorical(y_test)
num_classes = y_test.shape[1]

这次我们定义了一个大型 CNN 架构,其中包含额外的卷积,最大池化层和完全连接的层。网络拓扑可以总结如下。

  1. 具有 30 个大小为 5×5 的特征图的卷积层。
  2. 池化层最多超过 2 * 2 patches。
  3. 具有 15 个维度为 3×3 的特征图的卷积层。
  4. 池化层最多超过 2 * 2 patches。
  5. dropout 层的概率设置为 20%。
  6. 将矩阵转换为向量。
  7. 完全连接的层有 128 个神经元和非线性激活函数。
  8. 完全连接的层有 50 个神经元和非线性激活函数。
  9. 输出层。
# 定义一个大模型
def larger_model():
	# 创建模型
	model = Sequential()
	model.add(Conv2D(30, (5, 5), input_shape=(1, 28, 28), activation='relu'))
	model.add(MaxPooling2D(pool_size=(2, 2)))
	model.add(Conv2D(15, (3, 3), activation='relu'))
	model.add(MaxPooling2D(pool_size=(2, 2)))
	model.add(Dropout(0.2))
	model.add(Flatten())
	model.add(Dense(128, activation='relu'))
	model.add(Dense(50, activation='relu'))
	model.add(Dense(num_classes, activation='softmax'))
	# 编译模型
	model.compile(loss='categorical_crossentropy', optimizer='adam', metrics=['accuracy'])
	return model

与前两个实验一样,该模型拟合 10 个迭代次数,批大小为 200。

# 构建模型
model = larger_model()
# 拟合模型
model.fit(X_train, y_train, validation_data=(X_test, y_test), epochs=10, batch_size=200)
# 最后评估模型
scores = model.evaluate(X_test, y_test, verbose=0)
print("Large CNN Error: %.2f%%" % (100-scores[1]*100))

运行示例将在每个迭代期间的训练集和验证集上输出分类精确度,并最终显示分类错误率。

该模型每个跌打的运行大约需要 100 秒,这个略大的模型实现了 0.89%的可观分类错误率。

Train on 60000 samples, validate on 10000 samples
Epoch 1/10
60000/60000 [==============================] - 45s - loss: 0.3912 - acc: 0.8798 - val_loss: 0.0874 - val_acc: 0.9726
Epoch 2/10
60000/60000 [==============================] - 43s - loss: 0.0944 - acc: 0.9713 - val_loss: 0.0603 - val_acc: 0.9800
Epoch 3/10
60000/60000 [==============================] - 43s - loss: 0.0697 - acc: 0.9781 - val_loss: 0.0377 - val_acc: 0.9880
Epoch 4/10
60000/60000 [==============================] - 44s - loss: 0.0558 - acc: 0.9819 - val_loss: 0.0331 - val_acc: 0.9885
Epoch 5/10
60000/60000 [==============================] - 44s - loss: 0.0480 - acc: 0.9852 - val_loss: 0.0300 - val_acc: 0.9900
Epoch 6/10
60000/60000 [==============================] - 44s - loss: 0.0430 - acc: 0.9862 - val_loss: 0.0293 - val_acc: 0.9897
Epoch 7/10
60000/60000 [==============================] - 44s - loss: 0.0385 - acc: 0.9877 - val_loss: 0.0260 - val_acc: 0.9911
Epoch 8/10
60000/60000 [==============================] - 44s - loss: 0.0349 - acc: 0.9895 - val_loss: 0.0264 - val_acc: 0.9910
Epoch 9/10
60000/60000 [==============================] - 44s - loss: 0.0332 - acc: 0.9898 - val_loss: 0.0222 - val_acc: 0.9931
Epoch 10/10
60000/60000 [==============================] - 44s - loss: 0.0289 - acc: 0.9908 - val_loss: 0.0226 - val_acc: 0.9918
Large CNN Error: 0.82%

这不是最优化的网络拓扑,也不是复现最近的论文的网络结构,您可以尝试多次调整和改进此模型。

您可以达到的最佳错误率分数是多少?

在评论中发布您的配置和最佳分数。

关于 MNIST 的资源

MNIST 数据集得到了很好的研究,以下是您可能希望了解的一些其他资源。

摘要

在这篇文章中,您发现了 MNIST 手写数字识别问题和使用 Keras 库在 Python 中开发的深度学习模型,这些模型能够获得出色的结果。

通过本教程,您了解到:

  • 如何在 Keras 中加载 MNIST 数据集并生成数据集的图。
  • 如何重塑 MNIST 数据集并开发一个简单但表现良好的多层感知机模型来解决这个问题。
  • 如何使用 Keras 为 MNIST 创建卷积神经网络模型。
  • 如何为具有近乎世界级成果的 MNIST 开发和评估更大的 CNN 模型。

您对深度学习或此帖的手写识别有任何疑问吗?在评论中提出您的问题,我会尽力回答。

如何计算深度学习模型的精确率、召回率、F1 等

原文:machinelearningmastery.com/how-to-calc…

最后更新于 2020 年 8 月 27 日

一旦适合深度学习神经网络模型,就必须在测试数据集上评估其表现。

这很重要,因为报告的表现允许您在候选模型之间进行选择,并向利益相关者传达模型在解决问题方面有多好。

就您可以用来报告模型表现的指标而言,Keras 深度学习 API 模型非常有限。

我经常被问到的问题,比如:

如何计算模型的精确率和召回率?

以及:

如何计算我的模型的 F1 分数或混淆矩阵?

在本教程中,您将通过一个循序渐进的示例发现如何计算度量来评估您的深度学习神经网络模型。

完成本教程后,您将知道:

  • 如何使用 Sklearn metrics API 评估深度学习模型?
  • 如何用 Sklearn API 要求的最终模型进行类和概率预测。
  • 如何用 Sklearn API 为一个模型计算精确率、召回率、F1-评分、ROC AUC 等。

用我的新书用 Python 深度学习来启动你的项目,包括分步教程和所有示例的 Python 源代码文件。

我们开始吧。

  • 2020 年 1 月更新:更新了 Keras 2.3 和 TensorFlow 2.0 的 API。

How to Calculate Precision, Recall, F1, and More for Deep Learning Models

如何计算深度学习模型的精确率、召回率、F1 和更多信息约翰摄,版权所有。

教程概述

本教程分为三个部分;它们是:

  1. 二元分类问题
  2. 多层感知机模型
  3. 如何计算模型度量

二元分类问题

我们将使用一个标准的二元分类问题作为本教程的基础,称为“两个圆”问题。

它被称为两个圆的问题,因为这个问题是由点组成的,当绘制时,显示两个同心圆,每个类一个。因此,这是二元分类问题的一个例子。这个问题有两个输入,可以解释为图上的 x 和 y 坐标。每个点属于内圆或外圆。

Sklearn 库中的 make_circles()函数允许您从两个圆的问题中生成样本。“ n_samples ”参数允许您指定要生成的样本数量,在两个类之间平均分配。“噪声”参数允许您指定向每个点的输入或坐标添加多少随机统计噪声,从而使分类任务更具挑战性。“ random_state ”参数指定伪随机数发生器的种子,确保每次运行代码时生成相同的样本。

下面的示例生成 1000 个样本,统计噪声为 0.1,种子为 1。

# generate 2d classification dataset
X, y = make_circles(n_samples=1000, noise=0.1, random_state=1)

一旦生成,我们就可以创建一个数据集图来了解分类任务的挑战性。

下面的示例生成样本并绘制它们,根据类别给每个点着色,其中属于类别 0(外圈)的点被着色为蓝色,属于类别 1(内圈)的点被着色为橙色。

# Example of generating samples from the two circle problem
from sklearn.datasets import make_circles
from matplotlib import pyplot
from numpy import where
# generate 2d classification dataset
X, y = make_circles(n_samples=1000, noise=0.1, random_state=1)
# scatter plot, dots colored by class value
for i in range(2):
	samples_ix = where(y == i)
	pyplot.scatter(X[samples_ix, 0], X[samples_ix, 1])
pyplot.show()

运行该示例会生成数据集并在图形上绘制点,清楚地显示属于类别 0 和类别 1 的点的两个同心圆。

Scatter Plot of Samples From the Two Circles Problem

两个圆问题的样本散点图

多层感知机模型

我们将开发一个多层感知机或 MLP 模型来解决二元分类问题。

这个模型不是针对问题优化的,但是很有技巧(比随机好)。

生成数据集的样本后,我们将它们分成两个相等的部分:一个用于训练模型,一个用于评估训练好的模型。

# split into train and test
n_test = 500
trainX, testX = X[:n_test, :], X[n_test:, :]
trainy, testy = y[:n_test], y[n_test:]

接下来,我们可以定义我们的 MLP 模型。该模型很简单,需要来自数据集的 2 个输入变量、一个具有 100 个节点的隐藏层和一个 ReLU 激活函数,然后是一个具有单个节点和 sigmoid 激活函数的输出层。

该模型将预测一个介于 0 和 1 之间的值,该值将被解释为输入示例属于类别 0 还是类别 1。

# define model
model = Sequential()
model.add(Dense(100, input_dim=2, activation='relu'))
model.add(Dense(1, activation='sigmoid'))

模型将使用二元交叉熵损失函数进行拟合,我们将使用有效的亚当版本的随机梯度下降。该模型还将监控分类精确率度量。

# compile model
model.compile(loss='binary_crossentropy', optimizer='adam', metrics=['accuracy'])

我们将为 300 个训练时期拟合模型,默认批量为 32 个样本,并在每个训练时期结束时在测试数据集上评估模型的表现。

# fit model
history = model.fit(trainX, trainy, validation_data=(testX, testy), epochs=300, verbose=0)

在训练结束时,我们将在训练和测试数据集上再次评估最终模型,并报告分类精确率。

# evaluate the model
_, train_acc = model.evaluate(trainX, trainy, verbose=0)
_, test_acc = model.evaluate(testX, testy, verbose=0)

最后,模型在列车上的表现和在训练期间记录的测试集将使用线图来绘制,损失和分类精确率各一个。

# plot loss during training
pyplot.subplot(211)
pyplot.title('Loss')
pyplot.plot(history.history['loss'], label='train')
pyplot.plot(history.history['val_loss'], label='test')
pyplot.legend()
# plot accuracy during training
pyplot.subplot(212)
pyplot.title('Accuracy')
pyplot.plot(history.history['accuracy'], label='train')
pyplot.plot(history.history['val_accuracy'], label='test')
pyplot.legend()
pyplot.show()

将所有这些元素结合在一起,下面列出了训练和评估两个圆圈问题上的 MLP 的完整代码列表。

# multilayer perceptron model for the two circles problem
from sklearn.datasets import make_circles
from keras.models import Sequential
from keras.layers import Dense
from matplotlib import pyplot
# generate dataset
X, y = make_circles(n_samples=1000, noise=0.1, random_state=1)
# split into train and test
n_test = 500
trainX, testX = X[:n_test, :], X[n_test:, :]
trainy, testy = y[:n_test], y[n_test:]
# define model
model = Sequential()
model.add(Dense(100, input_dim=2, activation='relu'))
model.add(Dense(1, activation='sigmoid'))
# compile model
model.compile(loss='binary_crossentropy', optimizer='adam', metrics=['accuracy'])
# fit model
history = model.fit(trainX, trainy, validation_data=(testX, testy), epochs=300, verbose=0)
# evaluate the model
_, train_acc = model.evaluate(trainX, trainy, verbose=0)
_, test_acc = model.evaluate(testX, testy, verbose=0)
print('Train: %.3f, Test: %.3f' % (train_acc, test_acc))
# plot loss during training
pyplot.subplot(211)
pyplot.title('Loss')
pyplot.plot(history.history['loss'], label='train')
pyplot.plot(history.history['val_loss'], label='test')
pyplot.legend()
# plot accuracy during training
pyplot.subplot(212)
pyplot.title('Accuracy')
pyplot.plot(history.history['accuracy'], label='train')
pyplot.plot(history.history['val_accuracy'], label='test')
pyplot.legend()
pyplot.show()

在中央处理器上运行该示例非常快速地符合模型(不需要图形处理器)。

:考虑到算法或评估程序的随机性,或数值精确率的差异,您的结果可能会有所不同。考虑运行该示例几次,并比较平均结果。

对该模型进行了评估,报告在列车和测试集上的分类准确率分别约为 83%和 85%。

Train: 0.838, Test: 0.850

创建一个图形,显示两条线图:一条是列车和测试集上损失的学习曲线,另一条是列车和测试集上的分类。

这些图表明这个模型很适合这个问题。

Line Plot Showing Learning Curves of Loss and Accuracy of the MLP on the Two Circles Problem During Training

训练中 MLP 对两个圆问题的损失和准确性学习曲线的线图

如何计算模型度量

也许你需要使用 Keras 度量 API 不支持的额外度量来评估你的深度学习神经网络模型。

Keras 度量 API 是有限的,您可能想要计算诸如精确率、召回率、F1 等度量。

计算新度量的一种方法是在 Keras API 中自己实现它们,并让 Keras 在模型训练和模型评估期间为您计算它们。

有关这种方法的帮助,请参见教程:

这在技术上具有挑战性。

一个更简单的替代方法是使用您的最终模型对测试数据集进行预测,然后使用 Sklearn metrics API 计算您想要的任何度量。

除了分类精确率之外,二元分类问题的神经网络模型通常还需要三个指标:

  • 精确
  • 回忆
  • F1 分数

在本节中,我们将使用 Sklearn metrics API 计算这三个指标以及分类精确率,我们还将计算三个不太常见但可能有用的附加指标。它们是:

这不是 Sklearn 支持的分类模型的完整指标列表;尽管如此,计算这些指标将向您展示如何使用 Sklearn API 计算您可能需要的任何指标。

有关支持的指标的完整列表,请参见:

本节中的示例将计算 MLP 模型的度量,但是计算度量的相同代码也可以用于其他模型,例如神经网络和中枢神经网络。

我们可以使用前面几节中相同的代码来准备数据集,以及定义和拟合模型。为了使示例更简单,我们将把这些步骤的代码放入简单的函数中。

首先,我们可以定义一个名为 get_data() 的函数,该函数将生成数据集并将其拆分为训练集和测试集。

# generate and prepare the dataset
def get_data():
	# generate dataset
	X, y = make_circles(n_samples=1000, noise=0.1, random_state=1)
	# split into train and test
	n_test = 500
	trainX, testX = X[:n_test, :], X[n_test:, :]
	trainy, testy = y[:n_test], y[n_test:]
	return trainX, trainy, testX, testy

接下来,我们将定义一个名为 get_model() 的函数,该函数将定义 MLP 模型并将其拟合到训练数据集上。

# define and fit the model
def get_model(trainX, trainy):
	# define model
	model = Sequential()
	model.add(Dense(100, input_dim=2, activation='relu'))
	model.add(Dense(1, activation='sigmoid'))
	# compile model
	model.compile(loss='binary_crossentropy', optimizer='adam', metrics=['accuracy'])
	# fit model
	model.fit(trainX, trainy, epochs=300, verbose=0)
	return model

然后我们可以调用 get_data() 函数准备数据集,调用 get_model() 函数拟合并返回模型。

# generate data
trainX, trainy, testX, testy = get_data()
# fit model
model = get_model(trainX, trainy)

现在我们已经有了一个适合训练数据集的模型,我们可以使用 Sklearn metrics API 中的度量来评估它。

首先,我们必须使用模型进行预测。大多数度量函数需要真实类值(例如 testy )和预测类值(yhat _ class)之间的比较。我们可以使用模型上的*predict _ class()*函数直接用我们的模型预测类值。

有些指标,比如 ROC AUC,需要预测类概率( yhat_probs )。这些可以通过调用模型上的*预测()*函数来检索。

有关使用 Keras 模型进行预测的更多帮助,请参见帖子:

我们可以用这个模型进行分类和概率预测。

# predict probabilities for test set
yhat_probs = model.predict(testX, verbose=0)
# predict crisp classes for test set
yhat_classes = model.predict_classes(testX, verbose=0)

预测以二维数组的形式返回,测试数据集中的每个示例对应一行,预测对应一列。

Sklearn metrics API 期望使用 1D 阵列的实际值和预测值进行比较,因此,我们必须将 2D 预测阵列简化为 1D 阵列。

# reduce to 1d array
yhat_probs = yhat_probs[:, 0]
yhat_classes = yhat_classes[:, 0]

我们现在准备为我们的深度学习神经网络模型计算度量。我们可以从计算分类准确度、精确度、召回率和 F1 分数开始。

# accuracy: (tp + tn) / (p + n)
accuracy = accuracy_score(testy, yhat_classes)
print('Accuracy: %f' % accuracy)
# precision tp / (tp + fp)
precision = precision_score(testy, yhat_classes)
print('Precision: %f' % precision)
# recall: tp / (tp + fn)
recall = recall_score(testy, yhat_classes)
print('Recall: %f' % recall)
# f1: 2 tp / (2 tp + fp + fn)
f1 = f1_score(testy, yhat_classes)
print('F1 score: %f' % f1)

请注意,计算度量就像选择我们感兴趣的度量并调用传递真实类值( testy )和预测类值(yhat _ class)的函数一样简单。

我们还可以计算一些额外的指标,如科恩的 kappa、ROC AUC 和混淆矩阵。

请注意,ROC AUC 需要预测的类概率( yhat_probs )作为参数,而不是预测的类(yhat _ class)。

# kappa
kappa = cohen_kappa_score(testy, yhat_classes)
print('Cohens kappa: %f' % kappa)
# ROC AUC
auc = roc_auc_score(testy, yhat_probs)
print('ROC AUC: %f' % auc)
# confusion matrix
matrix = confusion_matrix(testy, yhat_classes)
print(matrix)

现在我们知道了如何使用 Sklearn API 计算深度学习神经网络的指标,我们可以将所有这些元素结合成一个完整的示例,如下所示。

# demonstration of calculating metrics for a neural network model using sklearn
from sklearn.datasets import make_circles
from sklearn.metrics import accuracy_score
from sklearn.metrics import precision_score
from sklearn.metrics import recall_score
from sklearn.metrics import f1_score
from sklearn.metrics import cohen_kappa_score
from sklearn.metrics import roc_auc_score
from sklearn.metrics import confusion_matrix
from keras.models import Sequential
from keras.layers import Dense

# generate and prepare the dataset
def get_data():
	# generate dataset
	X, y = make_circles(n_samples=1000, noise=0.1, random_state=1)
	# split into train and test
	n_test = 500
	trainX, testX = X[:n_test, :], X[n_test:, :]
	trainy, testy = y[:n_test], y[n_test:]
	return trainX, trainy, testX, testy

# define and fit the model
def get_model(trainX, trainy):
	# define model
	model = Sequential()
	model.add(Dense(100, input_dim=2, activation='relu'))
	model.add(Dense(1, activation='sigmoid'))
	# compile model
	model.compile(loss='binary_crossentropy', optimizer='adam', metrics=['accuracy'])
	# fit model
	model.fit(trainX, trainy, epochs=300, verbose=0)
	return model

# generate data
trainX, trainy, testX, testy = get_data()
# fit model
model = get_model(trainX, trainy)

# predict probabilities for test set
yhat_probs = model.predict(testX, verbose=0)
# predict crisp classes for test set
yhat_classes = model.predict_classes(testX, verbose=0)
# reduce to 1d array
yhat_probs = yhat_probs[:, 0]
yhat_classes = yhat_classes[:, 0]

# accuracy: (tp + tn) / (p + n)
accuracy = accuracy_score(testy, yhat_classes)
print('Accuracy: %f' % accuracy)
# precision tp / (tp + fp)
precision = precision_score(testy, yhat_classes)
print('Precision: %f' % precision)
# recall: tp / (tp + fn)
recall = recall_score(testy, yhat_classes)
print('Recall: %f' % recall)
# f1: 2 tp / (2 tp + fp + fn)
f1 = f1_score(testy, yhat_classes)
print('F1 score: %f' % f1)

# kappa
kappa = cohen_kappa_score(testy, yhat_classes)
print('Cohens kappa: %f' % kappa)
# ROC AUC
auc = roc_auc_score(testy, yhat_probs)
print('ROC AUC: %f' % auc)
# confusion matrix
matrix = confusion_matrix(testy, yhat_classes)
print(matrix)

:考虑到算法或评估程序的随机性,或数值精确率的差异,您的结果可能会有所不同。考虑运行该示例几次,并比较平均结果。

运行该示例准备数据集,拟合模型,然后计算并报告在测试数据集上评估的模型的度量。

Accuracy: 0.842000
Precision: 0.836576
Recall: 0.853175
F1 score: 0.844794
Cohens kappa: 0.683929
ROC AUC: 0.923739
[[206  42]
 [ 37 215]]

如果您需要解释给定指标的帮助,或许可以从 Sklearn API 文档中的“分类指标指南”开始:分类指标指南

此外,查看维基百科页面了解你的指标;比如:精准与召回,维基百科

进一步阅读

如果您想更深入地了解这个主题,本节将提供更多资源。

邮件

应用程序接口

文章

摘要

在本教程中,您通过一个逐步的示例发现了如何计算度量来评估您的深度学习神经网络模型。

具体来说,您了解到:

  • 如何使用 Sklearn metrics API 评估深度学习模型?
  • 如何用 Sklearn API 要求的最终模型进行类和概率预测。
  • 如何用 Sklearn API 为一个模型计算精确率、召回率、F1-score、ROC、AUC 等。

你有什么问题吗? 在下面的评论中提问,我会尽力回答。

如何用 Keras 做出预测

原文: machinelearningmastery.com/how-to-make-classification-and-regression-predictions-for-deep-learning-models-in-keras/

一旦您在 Keras 中选择并拟合了最终的深度学习模型,您就可以使用它来对新数据实例做出预测。

初学者对如何做到这一点有一些困惑。我经常看到以下问题:

如何在 Keras 中使用我的模型做出预测?

在本教程中,您将了解如何使用 Keras Python 库通过最终的深度学习模型进行分类和回归预测。

完成本教程后,您将了解:

  • 如何最终确定模型以便为预测做好准备。
  • 如何对 Keras 中的分类问题进行类别概率预测。
  • 如何在 Keras 中进行回归预测。

让我们开始吧!

How to Make Classification and Regression Predictions for Deep Learning Models in Keras

照片由mstk east提供,并保留所属权利。

教程概述

本教程分为 3 个部分,分别是:

  1. 完成模型
  2. 分类预测
  3. 回归预测

1.完成模型

在做出预测之前,必须训练最终模型。

您可能使用 k 折交叉验证或训练/测试分割数据来训练许多模型,这样做是为了让能够利用模型能估计样本外的数据,例如一些新数据。

如果这些模型已达到目的,则可以丢弃。

您现在必须在所有可用数据上训练最终模型,您可以在此处了解有关如何训练最终模型的更多信息:

2.分类预测

分类问题是模型学习输入要素和作为标签的输出要素之间的映射的问题,例如“_ 垃圾邮件 ”和“ 非垃圾邮件 _”。

下面是 Keras 中针对简单的两类(二元)分类问题开发的最终神经网络模型的示例。

如果您不熟悉在 Keras 中开发神经网络模型,请参阅帖子:

# 训练最终模型的示例
from keras.models import Sequential
from keras.layers import Dense
from sklearn.datasets.samples_generator import make_blobs
from sklearn.preprocessing import MinMaxScaler
# 生成 2 维分类数据集
X, y = make_blobs(n_samples=100, centers=2, n_features=2, random_state=1)
scalar = MinMaxScaler()
scalar.fit(X)
X = scalar.transform(X)
# 定义并拟合最终模型
model = Sequential()
model.add(Dense(4, input_dim=2, activation='relu'))
model.add(Dense(4, activation='relu'))
model.add(Dense(1, activation='sigmoid'))
model.compile(loss='binary_crossentropy', optimizer='adam')
model.fit(X, y, epochs=200, verbose=0)

完成后,您可能希望将模型保存到文件,例如通过 Keras API 保存后,您可以随时加载模型并使用它做出预测,有关此示例,请参阅帖子:

为简单起见,我们将跳过本教程中的示例。

我们可能希望使用最终模型进行两种类型的分类预测,它们是类别预测和概率预测。

类别预测

给出最终模型和一个或多个数据实例的类别预测,预测数据实例的类别。

我们并不知道新数据的分类结果,这就是我们首先需要定义模型的原因。

我们可以使用 predict_classes() 函数在 Keras 中使用我们最终的分类模型来预测新数据实例的类别。请注意,此函数仅适用于 Sequential模型,而不适用于使用函数 API 开发的其他模型。

例如,我们在名为 Xnew 的数组中有一个或多个数据实例,可以传递给我们模型上的 predict_classes()函数,以便预测数组中每个实例的类别。

Xnew = [[...], [...]]
ynew = model.predict_classes(Xnew)

让我们通过一个例子来具体演示这个过程:

# 对分类问题做出类别预测的示例
from keras.models import Sequential
from keras.layers import Dense
from sklearn.datasets.samples_generator import make_blobs
from sklearn.preprocessing import MinMaxScaler
#生成 2 维的分类数据集
X, y = make_blobs(n_samples=100, centers=2, n_features=2, random_state=1)
scalar = MinMaxScaler()
scalar.fit(X)
X = scalar.transform(X)
# 定义并拟合最终模型
model = Sequential()
model.add(Dense(4, input_dim=2, activation='relu'))
model.add(Dense(4, activation='relu'))
model.add(Dense(1, activation='sigmoid'))
model.compile(loss='binary_crossentropy', optimizer='adam')
model.fit(X, y, epochs=500, verbose=0)
# 不知分类结果的新的实例
Xnew, _ = make_blobs(n_samples=3, centers=2, n_features=2, random_state=1)
Xnew = scalar.transform(Xnew)
# 做出预测
ynew = model.predict_classes(Xnew)
# show the inputs and predicted outputs
for i in range(len(Xnew)):
	print("X=%s, Predicted=%s" % (Xnew[i], ynew[i]))

运行该示例预测三个新数据实例的类,然后将数据和预测一起打印。

X=[0.89337759 0.65864154], Predicted=[0]
X=[0.29097707 0.12978982], Predicted=[1]
X=[0.78082614 0.75391697], Predicted=[0]

如果您只有一个新的数据实例,则可以将其作为包含在数组中的实例提供给 predict_classes() 函数,例如:

# 对分类问题做出类别预测的示例
from keras.models import Sequential
from keras.layers import Dense
from sklearn.datasets.samples_generator import make_blobs
from sklearn.preprocessing import MinMaxScaler
from numpy import array
# 生成 2 维分类数据集
X, y = make_blobs(n_samples=100, centers=2, n_features=2, random_state=1)
scalar = MinMaxScaler()
scalar.fit(X)
X = scalar.transform(X)
# 定义并拟合最终模型
model = Sequential()
model.add(Dense(4, input_dim=2, activation='relu'))
model.add(Dense(4, activation='relu'))
model.add(Dense(1, activation='sigmoid'))
model.compile(loss='binary_crossentropy', optimizer='adam')
model.fit(X, y, epochs=500, verbose=0)
# 不知结果的新的实例
Xnew = array([[0.89337759, 0.65864154]])
# make a prediction
ynew = model.predict_classes(Xnew)
# 输入和输出的预测结果
print("X=%s, Predicted=%s" % (Xnew[0], ynew[0]))

运行该示例将打印单个实例和预测类。

X=[0.89337759 0.65864154], Predicted=[0]

关于类别标签的注意事项

请注意,在准备数据时,您将把域中的类值(例如字符串)映射到整数值。您可能使用过 LabelEncoder

LabelEncoder可用于通过 inverse_transform() 函数将整数转换回字符串值。

因此,您可能希望在拟合最终模型时保存(pickle)用于编码y值的LabelEncoder

概率预测

您可能希望进行的另一种类型的预测是数据实例属于每个类别的概率。

这被称为概率预测,其中,给定新实例,模型将为每个结果类别的概率返回为 0 和 1 之间的值。

您可以通过调用 predict_proba() 函数在 Keras 中进行这些类型的预测,如下所示:

Xnew = [[...], [...]]
ynew = model.predict_proba(Xnew)

在二元(二进制)分类问题的情况下,通常在输出层中使用 sigmoid激活函数,预测概率被视为属于类 1 的观测值的概率,或被反转(1-概率)以给出类 0 的概率。

在多分类问题的情况下,通常在输出层上使用 softmax 激活函数,并且将每个类的概率观测值作为向量返回。

以下示例对数据实例的Xnew数组中的每个示例进行概率预测。

# 为分类问题做出概率预测的示例
from keras.models import Sequential
from keras.layers import Dense
from sklearn.datasets.samples_generator import make_blobs
from sklearn.preprocessing import MinMaxScaler
# 生成 2 维分类数据集
X, y = make_blobs(n_samples=100, centers=2, n_features=2, random_state=1)
scalar = MinMaxScaler()
scalar.fit(X)
X = scalar.transform(X)
#定义并拟合最终模型
model = Sequential()
model.add(Dense(4, input_dim=2, activation='relu'))
model.add(Dense(4, activation='relu'))
model.add(Dense(1, activation='sigmoid'))
model.compile(loss='binary_crossentropy', optimizer='adam')
model.fit(X, y, epochs=500, verbose=0)
# 不知分类结果的新的实例
Xnew, _ = make_blobs(n_samples=3, centers=2, n_features=2, random_state=1)
Xnew = scalar.transform(Xnew)
# 做出预测
ynew = model.predict_proba(Xnew)
# show the inputs and predicted outputs
for i in range(len(Xnew)):
	print("X=%s, Predicted=%s" % (Xnew[i], ynew[i]))

运行实例会进行概率预测,然后打印输入数据实例以及属于类 1 的每个实例的概率。

X=[0.89337759 0.65864154], Predicted=[0.0087348]
X=[0.29097707 0.12978982], Predicted=[0.82020265]
X=[0.78082614 0.75391697], Predicted=[0.00693122]

如果您想向用户提供专业的概率解释,这的应用程序中会对您有所帮助。

3.回归预测

回归是一种监督学习问题,在给定输入示例的情况下,模型学习到适当输出量的映射,例如“0.1”和“0.2”等。

下面是用于回归的最终 Keras 模型的示例。

# 训练最终回归模型的示例
from keras.models import Sequential
from keras.layers import Dense
from sklearn.datasets import make_regression
from sklearn.preprocessing import MinMaxScaler
# 生成回归数据集
X, y = make_regression(n_samples=100, n_features=2, noise=0.1, random_state=1)
scalarX, scalarY = MinMaxScaler(), MinMaxScaler()
scalarX.fit(X)
scalarY.fit(y.reshape(100,1))
X = scalarX.transform(X)
y = scalarY.transform(y.reshape(100,1))
# 定义并拟合最终模型
model = Sequential()
model.add(Dense(4, input_dim=2, activation='relu'))
model.add(Dense(4, activation='relu'))
model.add(Dense(1, activation='linear'))
model.compile(loss='mse', optimizer='adam')
model.fit(X, y, epochs=1000, verbose=0)

我们可以通过在最终模型上调用 predict() 函数来使用最终的回归模型预测数量。

predict() 函数获取一个或多个数据实例的数组。

下面的示例演示了如何对具有未知预期结果的多个数据实例进行回归预测。

# 为回归问题做出最终预测的示例
from keras.models import Sequential
from keras.layers import Dense
from sklearn.datasets import make_regression
from sklearn.preprocessing import MinMaxScaler
# 生成回归数据集
X, y = make_regression(n_samples=100, n_features=2, noise=0.1, random_state=1)
scalarX, scalarY = MinMaxScaler(), MinMaxScaler()
scalarX.fit(X)
scalarY.fit(y.reshape(100,1))
X = scalarX.transform(X)
y = scalarY.transform(y.reshape(100,1))
# 定义并拟合最终模型
model = Sequential()
model.add(Dense(4, input_dim=2, activation='relu'))
model.add(Dense(4, activation='relu'))
model.add(Dense(1, activation='linear'))
model.compile(loss='mse', optimizer='adam')
model.fit(X, y, epochs=1000, verbose=0)
# 不知结果的新的数据实例
Xnew, a = make_regression(n_samples=3, n_features=2, noise=0.1, random_state=1)
Xnew = scalarX.transform(Xnew)
# 做出预测
ynew = model.predict(Xnew)
#打印输入值和预测的输出
for i in range(len(Xnew)):
	print("X=%s, Predicted=%s" % (Xnew[i], ynew[i]))

运行该示例会进行多次预测,然后并排打印输入和预测以供审阅。

X=[0.29466096 0.30317302], Predicted=[0.17097184]
X=[0.39445118 0.79390858], Predicted=[0.7475489]
X=[0.02884127 0.6208843 ], Predicted=[0.43370453]

可以使用相同的函数来对单个数据实例做出预测,只要它适当地包装在列表或者数组的环境中即可。

例如:

# 做出回归预测的实例
from keras.models import Sequential
from keras.layers import Dense
from sklearn.datasets import make_regression
from sklearn.preprocessing import MinMaxScaler
from numpy import array
# 生成回归数据集
X, y = make_regression(n_samples=100, n_features=2, noise=0.1, random_state=1)
scalarX, scalarY = MinMaxScaler(), MinMaxScaler()
scalarX.fit(X)
scalarY.fit(y.reshape(100,1))
X = scalarX.transform(X)
y = scalarY.transform(y.reshape(100,1))
# 定义并拟合最终模型
model = Sequential()
model.add(Dense(4, input_dim=2, activation='relu'))
model.add(Dense(4, activation='relu'))
model.add(Dense(1, activation='linear'))
model.compile(loss='mse', optimizer='adam')
model.fit(X, y, epochs=1000, verbose=0)
# 不知结果的新的实例
Xnew = array([[0.29466096, 0.30317302]])
#做出预测
ynew = model.predict(Xnew)
# 打印输入和预测的输出
print("X=%s, Predicted=%s" % (Xnew[0], ynew[0]))

运行该示例进行单个预测并打印数据实例和预测以供审阅。

X=[0.29466096 0.30317302], Predicted=[0.17333156]

进一步阅读

如果您希望深入了解,本节将提供有关该主题的更多资源。

摘要

在本教程中,您了解了如何使用 Keras Python 库通过最终的深度学习模型进行分类和回归预测。

具体来说,你学到了:

  • 如何最终确定模型以便为预测做好准备。
  • 如何对 Keras 中的分类问题进行类别和概率预测。
  • 如何在 Keras 中进行回归预测。

你有任何问题吗? 在下面的评论中提出您的问题,我会尽力回答。

深度学习类别变量的 3 种编码方式

原文:machinelearningmastery.com/how-to-prep…

最后更新于 2020 年 8 月 27 日

像 Keras 中的机器学习和深度学习模型一样,要求所有的输入和输出变量都是数字的。

这意味着,如果您的数据包含分类数据,您必须将其编码为数字,然后才能拟合和评估模型。

最流行的两种技术是整数编码一种热门编码,尽管一种叫做的新技术学习嵌入可能会在这两种方法之间提供一个有用的中间地带。

在本教程中,您将发现在 Keras 中开发神经网络模型时如何编码分类数据。

完成本教程后,您将知道:

  • 使用机器学习和深度学习模型时处理分类数据的挑战。
  • 如何对建模的类别变量进行整数编码和热编码?
  • 如何学习嵌入分布式表示作为类别变量神经网络的一部分。

用我的新书用 Python 深度学习来启动你的项目,包括分步教程和所有示例的 Python 源代码文件。

我们开始吧。

How to Encode Categorical Data for Deep Learning in Keras

如何在 Keras 中为深度学习编码分类数据 图片由 Ken Dixon 提供,保留部分权利。

教程概述

本教程分为五个部分;它们是:

  1. 分类数据的挑战
  2. 乳腺癌分类数据集
  3. 如何对分类数据进行有序编码
  4. 如何对分类数据进行热编码
  5. 如何对分类数据使用学习嵌入

分类数据的挑战

类别变量是其值具有标签值的变量。

例如,变量可以是“颜色”,可以取值“红色”、“绿色”和“蓝色

有时,分类数据可能在类别之间具有有序关系,例如“第一”、“第二”和“第三”这种类型的分类数据被称为序数,附加的排序信息可能是有用的。

机器学习算法和深度学习神经网络要求输入和输出变量都是数字。

这意味着分类数据必须编码成数字,然后我们才能使用它来拟合和评估模型。

有许多方法可以为建模编码类别变量,尽管最常见的有以下三种:

  1. 整数编码:每个唯一的标签映射到一个整数。
  2. 一个热编码:每个标签映射到一个二进制向量。
  3. 学习嵌入:学习类别的分布式表示。

我们将仔细研究如何使用这些方法中的每一种来编码分类数据,以便在 Keras 中训练深度学习神经网络。

乳腺癌分类数据集

作为本教程的基础,我们将使用自 20 世纪 80 年代以来在机器学习中广泛研究的所谓的“乳腺癌”数据集。

数据集将乳腺癌患者数据分类为癌症复发或不复发。有 286 个例子和 9 个输入变量。这是一个二元分类问题。

该数据集上合理的分类准确度分数在 68%和 73%之间。我们将针对该区域,但请注意,本教程中的模型并未优化:它们旨在演示编码方案

您可以下载数据集,并将文件保存为当前工作目录中的“乳腺癌. csv ”。

查看数据,我们可以看到所有九个输入变量都是绝对的。

具体来说,所有变量都是带引号的字符串;有些是序数,有些不是。

'40-49','premeno','15-19','0-2','yes','3','right','left_up','no','recurrence-events'
'50-59','ge40','15-19','0-2','no','1','right','central','no','no-recurrence-events'
'50-59','ge40','35-39','0-2','no','2','left','left_low','no','recurrence-events'
'40-49','premeno','35-39','0-2','yes','3','right','left_low','yes','no-recurrence-events'
'40-49','premeno','30-34','3-5','yes','2','left','right_up','no','recurrence-events'
...

我们可以使用熊猫库将这个数据集加载到内存中。

...
# load the dataset as a pandas DataFrame
data = read_csv(filename, header=None)
# retrieve numpy array
dataset = data.values

加载后,我们可以将列拆分为输入( X )和输出( y )进行建模。

...
# split into input (X) and output (y) variables
X = dataset[:, :-1]
y = dataset[:,-1]

最后,我们可以强制输入数据中的所有字段都是字符串,以防 Pandas 尝试自动将一些字段映射到数字(它确实尝试了)。

我们还可以将输出变量整形为一列(例如,2D 形状)。

...
# format all fields as string
X = X.astype(str)
# reshape target to be a 2d array
y = y.reshape((len(y), 1))

我们可以将所有这些结合到一个有用的函数中,以便以后重用。

# load the dataset
def load_dataset(filename):
	# load the dataset as a pandas DataFrame
	data = read_csv(filename, header=None)
	# retrieve numpy array
	dataset = data.values
	# split into input (X) and output (y) variables
	X = dataset[:, :-1]
	y = dataset[:,-1]
	# format all fields as string
	X = X.astype(str)
	# reshape target to be a 2d array
	y = y.reshape((len(y), 1))
	return X, y

加载后,我们可以将数据分成训练集和测试集,这样我们就可以拟合和评估深度学习模型。

我们将使用 Sklearn 中的 train_test_split()函数,使用 67%的数据进行训练,33%的数据进行测试。

...
# load the dataset
X, y = load_dataset('breast-cancer.csv')
# split into train and test sets
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.33, random_state=1)

将所有这些元素结合在一起,下面列出了加载、拆分和汇总原始分类数据集的完整示例。

# load and summarize the dataset
from pandas import read_csv
from sklearn.model_selection import train_test_split

# load the dataset
def load_dataset(filename):
	# load the dataset as a pandas DataFrame
	data = read_csv(filename, header=None)
	# retrieve numpy array
	dataset = data.values
	# split into input (X) and output (y) variables
	X = dataset[:, :-1]
	y = dataset[:,-1]
	# format all fields as string
	X = X.astype(str)
	# reshape target to be a 2d array
	y = y.reshape((len(y), 1))
	return X, y

# load the dataset
X, y = load_dataset('breast-cancer.csv')
# split into train and test sets
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.33, random_state=1)
# summarize
print('Train', X_train.shape, y_train.shape)
print('Test', X_test.shape, y_test.shape)

运行该示例会报告训练集和测试集的输入和输出元素的大小。

我们可以看到,我们有 191 个示例用于培训,95 个示例用于测试。

Train (191, 9) (191, 1)
Test (95, 9) (95, 1)

现在我们已经熟悉了数据集,让我们看看如何对它进行编码以进行建模。

如何对分类数据进行有序编码

序数编码包括将每个唯一标签映射到一个整数值。

因此,它有时被简单地称为整数编码。

只有当类别之间存在已知的关系时,这种类型的编码才是真正合适的。

对于数据集中的一些变量,这种关系确实存在,理想情况下,在准备数据时应该利用这种关系。

在这种情况下,我们将忽略任何可能存在的序数关系,并假设所有变量都是分类的。使用序数编码仍然是有帮助的,至少作为其他编码方案的参考点。

我们可以使用 Sklearn 中的序数编码器()将每个变量编码为整数。这是一个灵活的类,如果已知任何这样的顺序,它确实允许将类别的顺序指定为参数。

:我会留给你一个练习,更新下面的例子,尝试为那些具有自然排序的变量指定顺序,看看是否对模型表现有影响。

对变量进行编码的最佳实践是在训练数据集上进行编码,然后将其应用于训练和测试数据集。

下面的函数名为 prepare_inputs() ,获取列车和测试集的输入数据,并使用顺序编码对其进行编码。

# prepare input data
def prepare_inputs(X_train, X_test):
	oe = OrdinalEncoder()
	oe.fit(X_train)
	X_train_enc = oe.transform(X_train)
	X_test_enc = oe.transform(X_test)
	return X_train_enc, X_test_enc

我们还需要准备目标变量。

这是一个二元分类问题,所以我们需要将两个类标签映射为 0 和 1。

这是一种序数编码,Sklearn 提供了专门为此目的设计的 LabelEncoder 类。虽然标签编码器是为编码单个变量而设计的,但是我们也可以很容易地使用普通编码器来获得同样的结果。

prepare_targets() 整数编码列车和测试集的输出数据。

# prepare target
def prepare_targets(y_train, y_test):
	le = LabelEncoder()
	le.fit(y_train)
	y_train_enc = le.transform(y_train)
	y_test_enc = le.transform(y_test)
	return y_train_enc, y_test_enc

我们可以调用这些函数来准备我们的数据。

...
# prepare input data
X_train_enc, X_test_enc = prepare_inputs(X_train, X_test)
# prepare output data
y_train_enc, y_test_enc = prepare_targets(y_train, y_test)

我们现在可以定义一个神经网络模型。

我们将在所有这些示例中使用相同的通用模型。具体来说,就是一个多层感知机(MLP)神经网络,其中一个隐藏层有 10 个节点,输出层有一个节点用于进行二进制分类。

下面的代码没有涉及太多细节,而是定义了模型,将其放在训练数据集上,然后在测试数据集上对其进行评估。

...
# define the model
model = Sequential()
model.add(Dense(10, input_dim=X_train_enc.shape[1], activation='relu', kernel_initializer='he_normal'))
model.add(Dense(1, activation='sigmoid'))
# compile the keras model
model.compile(loss='binary_crossentropy', optimizer='adam', metrics=['accuracy'])
# fit the keras model on the dataset
model.fit(X_train_enc, y_train_enc, epochs=100, batch_size=16, verbose=2)
# evaluate the keras model
_, accuracy = model.evaluate(X_test_enc, y_test_enc, verbose=0)
print('Accuracy: %.2f' % (accuracy*100))

如果你是在 Keras 开发神经网络的新手,我推荐这个教程:

将所有这些联系在一起,下面列出了用序数编码准备数据以及对数据拟合和评估神经网络的完整示例。

# example of ordinal encoding for a neural network
from pandas import read_csv
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import LabelEncoder
from sklearn.preprocessing import OrdinalEncoder
from keras.models import Sequential
from keras.layers import Dense

# load the dataset
def load_dataset(filename):
	# load the dataset as a pandas DataFrame
	data = read_csv(filename, header=None)
	# retrieve numpy array
	dataset = data.values
	# split into input (X) and output (y) variables
	X = dataset[:, :-1]
	y = dataset[:,-1]
	# format all fields as string
	X = X.astype(str)
	# reshape target to be a 2d array
	y = y.reshape((len(y), 1))
	return X, y

# prepare input data
def prepare_inputs(X_train, X_test):
	oe = OrdinalEncoder()
	oe.fit(X_train)
	X_train_enc = oe.transform(X_train)
	X_test_enc = oe.transform(X_test)
	return X_train_enc, X_test_enc

# prepare target
def prepare_targets(y_train, y_test):
	le = LabelEncoder()
	le.fit(y_train)
	y_train_enc = le.transform(y_train)
	y_test_enc = le.transform(y_test)
	return y_train_enc, y_test_enc

# load the dataset
X, y = load_dataset('breast-cancer.csv')
# split into train and test sets
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.33, random_state=1)
# prepare input data
X_train_enc, X_test_enc = prepare_inputs(X_train, X_test)
# prepare output data
y_train_enc, y_test_enc = prepare_targets(y_train, y_test)
# define the  model
model = Sequential()
model.add(Dense(10, input_dim=X_train_enc.shape[1], activation='relu', kernel_initializer='he_normal'))
model.add(Dense(1, activation='sigmoid'))
# compile the keras model
model.compile(loss='binary_crossentropy', optimizer='adam', metrics=['accuracy'])
# fit the keras model on the dataset
model.fit(X_train_enc, y_train_enc, epochs=100, batch_size=16, verbose=2)
# evaluate the keras model
_, accuracy = model.evaluate(X_test_enc, y_test_enc, verbose=0)
print('Accuracy: %.2f' % (accuracy*100))

在任何现代硬件(不需要图形处理器)上运行该示例只需几秒钟即可适应该模型。

在每个训练时期结束时报告模型的损失和精确率,最后报告模型在测试数据集上的精确率。

:考虑到算法或评估程序的随机性,或数值精确率的差异,您的结果可能会有所不同。考虑运行该示例几次,并比较平均结果。

在这种情况下,我们可以看到模型在测试数据集上达到了大约 70%的准确率。

还不错,因为序数关系只存在于一些输入变量中,而对于那些存在序数关系的变量,它在编码中并不被认可。

...
Epoch 95/100
 - 0s - loss: 0.5349 - acc: 0.7696
Epoch 96/100
 - 0s - loss: 0.5330 - acc: 0.7539
Epoch 97/100
 - 0s - loss: 0.5316 - acc: 0.7592
Epoch 98/100
 - 0s - loss: 0.5302 - acc: 0.7696
Epoch 99/100
 - 0s - loss: 0.5291 - acc: 0.7644
Epoch 100/100
 - 0s - loss: 0.5277 - acc: 0.7644

Accuracy: 70.53

这为处理分类数据提供了一个很好的起点。

更好更通用的方法是使用一个热编码。

如何对分类数据进行热编码

单一热编码适用于类别之间不存在关系的分类数据。

它包括用二进制向量表示每个类别变量,每个唯一标签有一个元素,用 1 标记类标签,所有其他元素为 0。

例如,如果我们的变量是“ color ”,标签是“ red ”、“ green ”和“ blue ”,我们将这些标签中的每一个编码为三元素二进制向量,如下所示:

  • 红色:[1,0,0]
  • 绿色:[0,1,0]
  • 蓝色:[0,0,1]

然后,数据集中的每个标签将被替换为一个向量(一列变成三列)。这是对所有类别变量进行的,因此在乳腺癌数据集的情况下,我们的九个输入变量或列变为 43。

Sklearn 库提供了 OneHotEncoder 来自动对一个或多个变量进行热编码。

下面的 prepare_inputs() 函数为上一节中的示例提供了一个插入替换函数。它没有使用 T2 普通编码器,而是使用了 T4 统一编码器。

# prepare input data
def prepare_inputs(X_train, X_test):
	ohe = OneHotEncoder()
	ohe.fit(X_train)
	X_train_enc = ohe.transform(X_train)
	X_test_enc = ohe.transform(X_test)
	return X_train_enc, X_test_enc

将这些联系在一起,下面列出了一个完整的例子,它对乳腺癌分类数据集进行了热编码,并用神经网络对其进行建模。

# example of one hot encoding for a neural network
from pandas import read_csv
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import LabelEncoder
from sklearn.preprocessing import OneHotEncoder
from keras.models import Sequential
from keras.layers import Dense

# load the dataset
def load_dataset(filename):
	# load the dataset as a pandas DataFrame
	data = read_csv(filename, header=None)
	# retrieve numpy array
	dataset = data.values
	# split into input (X) and output (y) variables
	X = dataset[:, :-1]
	y = dataset[:,-1]
	# format all fields as string
	X = X.astype(str)
	# reshape target to be a 2d array
	y = y.reshape((len(y), 1))
	return X, y

# prepare input data
def prepare_inputs(X_train, X_test):
	ohe = OneHotEncoder()
	ohe.fit(X_train)
	X_train_enc = ohe.transform(X_train)
	X_test_enc = ohe.transform(X_test)
	return X_train_enc, X_test_enc

# prepare target
def prepare_targets(y_train, y_test):
	le = LabelEncoder()
	le.fit(y_train)
	y_train_enc = le.transform(y_train)
	y_test_enc = le.transform(y_test)
	return y_train_enc, y_test_enc

# load the dataset
X, y = load_dataset('breast-cancer.csv')
# split into train and test sets
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.33, random_state=1)
# prepare input data
X_train_enc, X_test_enc = prepare_inputs(X_train, X_test)
# prepare output data
y_train_enc, y_test_enc = prepare_targets(y_train, y_test)
# define the  model
model = Sequential()
model.add(Dense(10, input_dim=X_train_enc.shape[1], activation='relu', kernel_initializer='he_normal'))
model.add(Dense(1, activation='sigmoid'))
# compile the keras model
model.compile(loss='binary_crossentropy', optimizer='adam', metrics=['accuracy'])
# fit the keras model on the dataset
model.fit(X_train_enc, y_train_enc, epochs=100, batch_size=16, verbose=2)
# evaluate the keras model
_, accuracy = model.evaluate(X_test_enc, y_test_enc, verbose=0)
print('Accuracy: %.2f' % (accuracy*100))

示例 one hot 对输入分类数据进行了编码,标签也对目标变量进行了编码,就像我们在上一节中所做的那样。然后在准备好的数据集上拟合相同的神经网络模型。

:考虑到算法或评估程序的随机性,或数值精确率的差异,您的结果可能会有所不同。考虑运行该示例几次,并比较平均结果。

在这种情况下,模型表现相当好,达到了大约 72%的准确率,接近上一节看到的结果。

更公平的比较是将每个配置运行 10 或 30 次,并使用平均精确率来比较表现。回想一下,在本教程中,我们更关注如何对分类数据进行编码,而不是在这个特定的数据集上获得最佳分数。

...
Epoch 95/100
 - 0s - loss: 0.3837 - acc: 0.8272
Epoch 96/100
 - 0s - loss: 0.3823 - acc: 0.8325
Epoch 97/100
 - 0s - loss: 0.3814 - acc: 0.8325
Epoch 98/100
 - 0s - loss: 0.3795 - acc: 0.8325
Epoch 99/100
 - 0s - loss: 0.3788 - acc: 0.8325
Epoch 100/100
 - 0s - loss: 0.3773 - acc: 0.8325

Accuracy: 72.63

序数编码和一种热编码可能是两种最流行的方法。

一种更新的技术类似于一种热编码,被设计用于神经网络,称为学习嵌入。

如何对分类数据使用学习嵌入

学习嵌入,或简称为“嵌入”,是分类数据的分布式表示。

每个类别被映射到一个不同的向量,并且向量的属性在训练神经网络时被调整或学习。向量空间提供了类别的投影,允许那些相近或相关的类别自然地聚集在一起。

这既提供了序数关系的好处,允许从数据中学习任何这样的关系,又提供了一种热编码,为每个类别提供向量表示。与一种热编码不同,输入向量不是稀疏的(没有很多零)。缺点是它需要作为模型的一部分进行学习,并创建更多的输入变量(列)。

该技术最初是为了提供单词的分布式表示而开发的,例如允许相似的单词具有相似的向量表示。因此,该技术通常被称为单词嵌入,并且在文本数据的情况下,已经开发了算法来学习独立于神经网络的表示。关于这个主题的更多信息,请查看帖子:

使用嵌入的另一个好处是,每个类别映射到的学习向量可以适合具有中等技能的模型,但是向量可以被提取并通常用作一系列不同模型和应用上的类别的输入。也就是说,它们可以被学习和重用。

嵌入可以通过嵌入层在 Keras 中使用。

关于在 Keras 中学习文本数据的单词嵌入的示例,请参见文章:

每个类别变量需要一个嵌入层,嵌入期望类别是有序编码的,尽管类别之间没有关系。

每个嵌入还需要用于分布式表示(向量空间)的维数。在自然语言应用程序中,使用 50、100 或 300 维是很常见的。对于我们的小例子,我们将维数固定为 10,但这是任意的;你应该用其他价值观来做实验。

首先,我们可以使用序数编码来准备输入数据。

我们将开发的模型将对每个输入变量进行一次单独的嵌入。因此,该模型将采用九个不同的输入数据集。因此,我们将使用标签编码器分别分割输入变量和序数编码(整数编码),并返回单独准备的训练和测试输入数据集的列表。

下面的 prepare_inputs() 函数实现了这一点,枚举每个输入变量,使用最佳实践对每个变量进行整数编码,并返回编码的训练和测试变量(或单变量数据集)列表,这些变量可以用作我们模型的输入。

# prepare input data
def prepare_inputs(X_train, X_test):
	X_train_enc, X_test_enc = list(), list()
	# label encode each column
	for i in range(X_train.shape[1]):
		le = LabelEncoder()
		le.fit(X_train[:, i])
		# encode
		train_enc = le.transform(X_train[:, i])
		test_enc = le.transform(X_test[:, i])
		# store
		X_train_enc.append(train_enc)
		X_test_enc.append(test_enc)
	return X_train_enc, X_test_enc

现在我们可以构建模型了。

在这种情况下,我们必须以不同的方式构建模型,因为我们将有九个输入层,九个嵌入层的输出(九个不同的 10 元素向量)需要连接成一个长向量,然后作为输入传递到密集层。

我们可以使用功能性的 Keras API 来实现这一点。如果您是 Keras 功能 API 的新手,请查看帖子:

首先,我们可以枚举每个变量,构造一个输入层,并将其连接到一个嵌入层,并将两个层都存储在列表中。在定义模型时,我们需要一个对所有输入层的引用,并且我们需要一个对每个嵌入层的引用,以便用一个合并层来集中它们。

...
# prepare each input head
in_layers = list()
em_layers = list()
for i in range(len(X_train_enc)):
	# calculate the number of unique inputs
	n_labels = len(unique(X_train_enc[i]))
	# define input layer
	in_layer = Input(shape=(1,))
	# define embedding layer
	em_layer = Embedding(n_labels, 10)(in_layer)
	# store layers
	in_layers.append(in_layer)
	em_layers.append(em_layer)

然后我们可以合并所有的嵌入层,定义隐藏层和输出层,然后定义模型。

...
# concat all embeddings
merge = concatenate(em_layers)
dense = Dense(10, activation='relu', kernel_initializer='he_normal')(merge)
output = Dense(1, activation='sigmoid')(dense)
model = Model(inputs=in_layers, outputs=output)

当使用具有多个输入的模型时,我们将需要为每个输入指定一个具有一个数据集的列表,例如,在我们的数据集的情况下,一个由九个数组组成的列表,每个数组有一列。谢天谢地,这是我们从 prepare_inputs() 函数返回的格式。

因此,拟合和评估模型看起来就像上一节一样。

另外,我们将通过调用 plot_model() 函数来绘制模型,并将其保存到文件中。这需要安装 pygraphviz 和 pydot,这在某些系统上可能会很麻烦。如果有麻烦就评论一下进口声明,打电话给 plot_model()

...
# compile the keras model
model.compile(loss='binary_crossentropy', optimizer='adam', metrics=['accuracy'])
# plot graph
plot_model(model, show_shapes=True, to_file='embeddings.png')
# fit the keras model on the dataset
model.fit(X_train_enc, y_train_enc, epochs=20, batch_size=16, verbose=2)
# evaluate the keras model
_, accuracy = model.evaluate(X_test_enc, y_test_enc, verbose=0)
print('Accuracy: %.2f' % (accuracy*100))

将所有这些结合起来,下面列出了在多输入层模型中对每个分类输入变量使用单独嵌入的完整示例。

# example of learned embedding encoding for a neural network
from numpy import unique
from pandas import read_csv
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import LabelEncoder
from keras.models import Model
from keras.layers import Input
from keras.layers import Dense
from keras.layers import Embedding
from keras.layers.merge import concatenate
from keras.utils import plot_model

# load the dataset
def load_dataset(filename):
	# load the dataset as a pandas DataFrame
	data = read_csv(filename, header=None)
	# retrieve numpy array
	dataset = data.values
	# split into input (X) and output (y) variables
	X = dataset[:, :-1]
	y = dataset[:,-1]
	# format all fields as string
	X = X.astype(str)
	# reshape target to be a 2d array
	y = y.reshape((len(y), 1))
	return X, y

# prepare input data
def prepare_inputs(X_train, X_test):
	X_train_enc, X_test_enc = list(), list()
	# label encode each column
	for i in range(X_train.shape[1]):
		le = LabelEncoder()
		le.fit(X_train[:, i])
		# encode
		train_enc = le.transform(X_train[:, i])
		test_enc = le.transform(X_test[:, i])
		# store
		X_train_enc.append(train_enc)
		X_test_enc.append(test_enc)
	return X_train_enc, X_test_enc

# prepare target
def prepare_targets(y_train, y_test):
	le = LabelEncoder()
	le.fit(y_train)
	y_train_enc = le.transform(y_train)
	y_test_enc = le.transform(y_test)
	return y_train_enc, y_test_enc

# load the dataset
X, y = load_dataset('breast-cancer.csv')
# split into train and test sets
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.33, random_state=1)
# prepare input data
X_train_enc, X_test_enc = prepare_inputs(X_train, X_test)
# prepare output data
y_train_enc, y_test_enc = prepare_targets(y_train, y_test)
# make output 3d
y_train_enc = y_train_enc.reshape((len(y_train_enc), 1, 1))
y_test_enc = y_test_enc.reshape((len(y_test_enc), 1, 1))
# prepare each input head
in_layers = list()
em_layers = list()
for i in range(len(X_train_enc)):
	# calculate the number of unique inputs
	n_labels = len(unique(X_train_enc[i]))
	# define input layer
	in_layer = Input(shape=(1,))
	# define embedding layer
	em_layer = Embedding(n_labels, 10)(in_layer)
	# store layers
	in_layers.append(in_layer)
	em_layers.append(em_layer)
# concat all embeddings
merge = concatenate(em_layers)
dense = Dense(10, activation='relu', kernel_initializer='he_normal')(merge)
output = Dense(1, activation='sigmoid')(dense)
model = Model(inputs=in_layers, outputs=output)
# compile the keras model
model.compile(loss='binary_crossentropy', optimizer='adam', metrics=['accuracy'])
# plot graph
plot_model(model, show_shapes=True, to_file='embeddings.png')
# fit the keras model on the dataset
model.fit(X_train_enc, y_train_enc, epochs=20, batch_size=16, verbose=2)
# evaluate the keras model
_, accuracy = model.evaluate(X_test_enc, y_test_enc, verbose=0)
print('Accuracy: %.2f' % (accuracy*100))

运行该示例如上所述准备数据,拟合模型,并报告表现。

:考虑到算法或评估程序的随机性,或数值精确率的差异,您的结果可能会有所不同。考虑运行该示例几次,并比较平均结果。

在这种情况下,模型表现相当好,与我们在前面部分看到的一个热编码相匹配。

由于学习的向量是在一个熟练的模型中训练的,因此可以保存它们,并将其用作对相同数据进行操作的其他模型中这些变量的一般表示。探索这种编码的一个有用且令人信服的理由。

...
Epoch 15/20
 - 0s - loss: 0.4891 - acc: 0.7696
Epoch 16/20
 - 0s - loss: 0.4845 - acc: 0.7749
Epoch 17/20
 - 0s - loss: 0.4783 - acc: 0.7749
Epoch 18/20
 - 0s - loss: 0.4763 - acc: 0.7906
Epoch 19/20
 - 0s - loss: 0.4696 - acc: 0.7906
Epoch 20/20
 - 0s - loss: 0.4660 - acc: 0.7958

Accuracy: 72.63

为了确认我们对模型的理解,创建了一个图,并将其保存到当前工作目录中的文件 embeddings.png 中。

该图显示了九个输入,每个输入映射到一个 10 元素向量,这意味着模型的实际输入是一个 90 元素向量。

:点击图片查看大版。

Plot of the Model Architecture With Separate Inputs and Embeddings for each Categorical Variable

每个类别变量有单独输入和嵌入的模型架构图 点击放大。

常见问题

本节列出了编码分类数据时的一些常见问题和答案。

问:如果我有数字和分类数据的混合会怎么样?

或者,如果我有分类和顺序数据的混合呢?

您需要分别准备或编码数据集中的每个变量(列),然后将所有准备好的变量重新连接到一个数组中,以便拟合或评估模型。

问:如果我有上百个类别呢?

或者,如果我连接多个一个热编码向量来创建一个数千元素的输入向量呢?

你可以使用一个热门的编码多达成千上万的类别。此外,使用大向量作为输入听起来很吓人,但是模型通常可以处理它。

尝试嵌入;它提供了更小的向量空间(投影)的好处,表示可以有更多的意义。

问:什么编码技术最好?

这是不可知的。

使用您选择的模型在数据集上测试每种技术(以及更多),并发现最适合您的案例的技术。

进一步阅读

如果您想更深入地了解这个主题,本节将提供更多资源。

邮件

应用程序接口

资料组

摘要

在本教程中,您发现了在 Keras 中开发神经网络模型时如何编码分类数据。

具体来说,您了解到:

  • 使用机器学习和深度学习模型时处理分类数据的挑战。
  • 如何对建模的类别变量进行整数编码和热编码?
  • 如何学习嵌入分布式表示作为类别变量神经网络的一部分。

你有什么问题吗? 在下面的评论中提问,我会尽力回答。