Microsoft Cognitive Toolkit 简明教程
CNTK - In-Memory and Large Datasets
在本教程中,我们将学习如何在 CNTK 中处理内存和大型数据集。
Training with small in memory datasets
当我们讨论将数据馈送至 CNTK 训练器的问题时,可能有许多方法,但这取决于数据集的大小以及格式。数据集可以是小内存数据集或者大数据集。
在本节中,我们将处理内存数据集。为此,我们将使用以下两个框架 −
-
Numpy
-
Pandas
Using Numpy arrays
在此,我们将使用基于 numpy 的随机生成数据集在 CNTK 中进行操作。在本例中,我们将模拟用于二进制分类问题的的数据。假设我们有一组包含 4 个特征的观测值,并且希望使用我们的深度学习模型来预测两个可能的标签。
Implementation Example
为此,我们首先必须生成一组标签,其中包含我们要预测的标签的 one-hot 向量表示。可以通过以下步骤来完成此操作 −
Step 1 − 导入 numpy 包,如下所示 −
import numpy as np
num_samples = 20000
Step 2 − 接下来,使用 np.eye 函数按如下方式生成标签映射 −
label_mapping = np.eye(2)
Step 3 − 现在,使用 np.random.choice 函数按如下方式收集 20000 个随机样本 −
y = label_mapping[np.random.choice(2,num_samples)].astype(np.float32)
Step 4 − 现在,最后使用 np.random.random 函数生成随机浮点数数组,如下所示 −
x = np.random.random(size=(num_samples, 4)).astype(np.float32)
一旦生成随机浮点数数组,我们需要将它们转换为 32 位浮点数,以便与 CNTK 预期的格式相匹配。让我们按照以下步骤执行此操作 −
Step 5 − 从 cntk.layers 模块导入 Dense 和 Sequential 层函数,如下所示 −
from cntk.layers import Dense, Sequential
Step 6 − 现在,我们需要导入网络中各层的激活函数。让我们导入 sigmoid 作为激活函数 −
from cntk import input_variable, default_options
from cntk.ops import sigmoid
Step 7 − 现在,我们需要导入损失函数来训练网络。让我们导入 binary_cross_entropy 作为损失函数 −
from cntk.losses import binary_cross_entropy
Step 8 − 接下来,我们需要为网络定义默认选项。在此,我们将 sigmoid 激活函数作为默认设置。此外,使用 Sequential 层函数按如下方式创建模型 −
with default_options(activation=sigmoid):
model = Sequential([Dense(6),Dense(2)])
Step 9 − 接下来,初始化 input_variable ,其中 4 个输入特征用作网络输入。
features = input_variable(4)
Step 10 − 现在,为了完成它,我们需要将特征变量连接到神经网络。
z = model(features)
因此,现在我们有了一个神经网络,借助于以下步骤,让我们使用内存数据集对其进行训练 −
Step 11 − 为了训练这个神经网络,我们首先需要从 cntk.learners 模块中导入学习器。我们将导入 sgd 学习器,如下所示 −
from cntk.learners import sgd
Step 12 − 同时从 cntk.logging 模块中导入 ProgressPrinter 。
from cntk.logging import ProgressPrinter
progress_writer = ProgressPrinter(0)
Step 13 − 接下来,为标签定义一个新的输入变量,如下所示 −
labels = input_variable(2)
Step 14 − 为了训练神经网络模型,接下来我们需要使用 binary_cross_entropy 函数定义一个损失。此外,提供模型 z 和标签变量。
loss = binary_cross_entropy(z, labels)
Step 15 − 接下来,初始化 sgd 学习器,如下所示 −
learner = sgd(z.parameters, lr=0.1)
Step 16 − 最后,在损失函数上调用 train 方法。此外,还为其提供输入数据、 sgd 学习器和 progress_printer 。
training_summary=loss.train((x,y),parameter_learners=[learner],callbacks=[progress_writer])
Complete implementation example
import numpy as np
num_samples = 20000
label_mapping = np.eye(2)
y = label_mapping[np.random.choice(2,num_samples)].astype(np.float32)
x = np.random.random(size=(num_samples, 4)).astype(np.float32)
from cntk.layers import Dense, Sequential
from cntk import input_variable, default_options
from cntk.ops import sigmoid
from cntk.losses import binary_cross_entropy
with default_options(activation=sigmoid):
model = Sequential([Dense(6),Dense(2)])
features = input_variable(4)
z = model(features)
from cntk.learners import sgd
from cntk.logging import ProgressPrinter
progress_writer = ProgressPrinter(0)
labels = input_variable(2)
loss = binary_cross_entropy(z, labels)
learner = sgd(z.parameters, lr=0.1)
training_summary=loss.train((x,y),parameter_learners=[learner],callbacks=[progress_writer])
Output
Build info:
Built time: *** ** **** 21:40:10
Last modified date: *** *** ** 21:08:46 2019
Build type: Release
Build target: CPU-only
With ASGD: yes
Math lib: mkl
Build Branch: HEAD
Build SHA1:ae9c9c7c5f9e6072cc9c94c254f816dbdc1c5be6 (modified)
MPI distribution: Microsoft MPI
MPI version: 7.0.12437.6
-------------------------------------------------------------------
average since average since examples
loss last metric last
------------------------------------------------------
Learning rate per minibatch: 0.1
1.52 1.52 0 0 32
1.51 1.51 0 0 96
1.48 1.46 0 0 224
1.45 1.42 0 0 480
1.42 1.4 0 0 992
1.41 1.39 0 0 2016
1.4 1.39 0 0 4064
1.39 1.39 0 0 8160
1.39 1.39 0 0 16352
Using Pandas DataFrames
Numpy 数组在它们能包含的内容方面非常有限,并且是存储数据的最基本方式之一。例如,一个 n 维数组可以包含单一数据类型的数据。但在另一方面,对于许多现实世界的案例,我们需要一个库,它可以在单个数据集中处理多种数据类型。
一个称为 Pandas 的 Python 库可以更容易地处理此类数据集。它引入了数据框 (DF) 的概念,并允许我们加载存储在各种格式中的磁盘上的数据集作为 DF。例如,我们可以读取存储为 CSV、JSON、Excel 等格式的 DF。
您可以在链接中更详细地了解 Python Pandas 库:/python_pandas/index.html[ [role="bare"] [role="bare"]https://www.tutorialspoint.com/python_pandas/index.htm .]
Implementation Example
在这个示例中,我们将使用基于四项属性对三种可能的鸢尾花物种进行分类的示例。我们也在前面的部分中创建了这个深度学习模型。模型如下 −
from cntk.layers import Dense, Sequential
from cntk import input_variable, default_options
from cntk.ops import sigmoid, log_softmax
from cntk.losses import binary_cross_entropy
model = Sequential([
Dense(4, activation=sigmoid),
Dense(3, activation=log_softmax)
])
features = input_variable(4)
z = model(features)
以上模型包含一个隐藏层和一个具有三个神经元的输出层,以匹配我们可预测的类数。
接下来,我们将使用 train 方法和 loss 函数来训练网络。为此,我们必须首先加载和预处理虹膜数据集,以便它与神经网络的预期布局和数据格式匹配。这可以通过以下步骤来完成 −
Step 1 − 导入 numpy 和 Pandas 包,如下所示 −
import numpy as np
import pandas as pd
Step 2 − 接下来,使用 read_csv 函数将数据集加载到内存中 −
df_source = pd.read_csv(‘iris.csv’, names = [‘sepal_length’, ‘sepal_width’,
‘petal_length’, ‘petal_width’, ‘species’], index_col=False)
Step 3 − 现在,我们需要创建一个字典,它将数据集中的标签与其对应的数字表示相映射。
label_mapping = {‘Iris-Setosa’ : 0, ‘Iris-Versicolor’ : 1, ‘Iris-Virginica’ : 2}
Step 4 − 现在,通过在 DataFrame 上使用 iloc 索引器,选择前四列,如下所示 −
x = df_source.iloc[:, :4].values
Step 5 −接下来,我们需要选择物种列作为数据集的标签。它可以按以下方式完成 −
y = df_source[‘species’].values
Step 6 − 现在,我们需要映射数据集中的标签,可以使用 label_mapping 完成此操作。此外,使用 one_hot 编码将它们转换为 one-hot 编码数组。
y = np.array([one_hot(label_mapping[v], 3) for v in y])
Step 7 − 接下来,为了将特征和映射的标签与 CNTK 一起使用,我们需要将它们都转换为浮点数 −
x= x.astype(np.float32)
y= y.astype(np.float32)
众所周知,标签以字符串的形式存储在数据集中,而 CNTK 无法处理这些字符串。这就是需要使用 one-hot 编码向量来表示标签的原因。为此,我们可以定义一个函数 one_hot 如下所示 −
def one_hot(index, length):
result = np.zeros(length)
result[index] = index
return result
现在,我们有了正确格式的 numpy 数组,借助以下步骤,我们可以使用它们来训练我们的模型 −
Step 8 − 首先,我们需要导入损失函数来训练网络。让我们导入 binary_cross_entropy_with_softmax 作为损失函数 −
from cntk.losses import binary_cross_entropy_with_softmax
Step 9 − 要训练此 NN,我们还需要从 cntk.learners 模块中导入 learner。我们将导入 sgd learner,如下所示 −
from cntk.learners import sgd
Step 10 − 同时从 cntk.logging 模块中导入 ProgressPrinter 。
from cntk.logging import ProgressPrinter
progress_writer = ProgressPrinter(0)
Step 11 − 接下来,定义标签的新输入变量,如下所示 −
labels = input_variable(3)
Step 12 − 为了训练 NN 模型,接下来我们需要使用 binary_cross_entropy_with_softmax 函数定义损失。还要提供模型 z 和标签变量。
loss = binary_cross_entropy_with_softmax (z, labels)
Step 13 − 接下来,初始化 sgd learner,如下所示 −
learner = sgd(z.parameters, 0.1)
Step 14 − 最后,调用损失函数上的 train 方法。此外,还需要向它提供输入数据、 sgd learner 和 progress_printer 。
training_summary=loss.train((x,y),parameter_learners=[learner],callbacks=
[progress_writer],minibatch_size=16,max_epochs=5)
Complete implementation example
from cntk.layers import Dense, Sequential
from cntk import input_variable, default_options
from cntk.ops import sigmoid, log_softmax
from cntk.losses import binary_cross_entropy
model = Sequential([
Dense(4, activation=sigmoid),
Dense(3, activation=log_softmax)
])
features = input_variable(4)
z = model(features)
import numpy as np
import pandas as pd
df_source = pd.read_csv(‘iris.csv’, names = [‘sepal_length’, ‘sepal_width’, ‘petal_length’, ‘petal_width’, ‘species’], index_col=False)
label_mapping = {‘Iris-Setosa’ : 0, ‘Iris-Versicolor’ : 1, ‘Iris-Virginica’ : 2}
x = df_source.iloc[:, :4].values
y = df_source[‘species’].values
y = np.array([one_hot(label_mapping[v], 3) for v in y])
x= x.astype(np.float32)
y= y.astype(np.float32)
def one_hot(index, length):
result = np.zeros(length)
result[index] = index
return result
from cntk.losses import binary_cross_entropy_with_softmax
from cntk.learners import sgd
from cntk.logging import ProgressPrinter
progress_writer = ProgressPrinter(0)
labels = input_variable(3)
loss = binary_cross_entropy_with_softmax (z, labels)
learner = sgd(z.parameters, 0.1)
training_summary=loss.train((x,y),parameter_learners=[learner],callbacks=[progress_writer],minibatch_size=16,max_epochs=5)
Output
Build info:
Built time: *** ** **** 21:40:10
Last modified date: *** *** ** 21:08:46 2019
Build type: Release
Build target: CPU-only
With ASGD: yes
Math lib: mkl
Build Branch: HEAD
Build SHA1:ae9c9c7c5f9e6072cc9c94c254f816dbdc1c5be6 (modified)
MPI distribution: Microsoft MPI
MPI version: 7.0.12437.6
-------------------------------------------------------------------
average since average since examples
loss last metric last
------------------------------------------------------
Learning rate per minibatch: 0.1
1.1 1.1 0 0 16
0.835 0.704 0 0 32
1.993 1.11 0 0 48
1.14 1.14 0 0 112
[………]
Training with large datasets
在上一节中,我们使用 Numpy 和 Pandas 处理了内存中的小数据集,但并非所有数据集都如此之小。尤其是包含图像、视频、声音样本的数据集很大。 MinibatchSource 是一个组件,它可以分块加载数据,由 CNTK 提供,用于处理如此大的数据集。 MinibatchSource 组件的一些功能如下 −
-
MinibatchSource 可以通过自动随机化从数据源读取的样本,防止 NN 过拟合。
-
它有一个内置的转换管道,可用于增强数据。
-
它在与训练过程分离的后台线程上加载数据。
在以下章节中,我们将探讨如何使用带有内存外数据的小批量源来处理大型数据集。我们还将探讨如何使用它来输入 NN 训练。
Creating MinibatchSource instance
在上一节中,我们使用了鸢尾花示例,并使用 Pandas DataFrames 处理了内存中的小型数据集。在这里,我们将使用 MinibatchSource 替换使用 pandas DF 中数据的部分代码。首先,我们需要按照以下步骤创建 MinibatchSource 实例 −
Implementation Example
Step 1 − 首先,从 cntk.io 模块中导入 minibatchsource 的组件,如下所示 −
from cntk.io import StreamDef, StreamDefs, MinibatchSource, CTFDeserializer,
INFINITY_REPEAT
Step 2 − 现在,通过使用 StreamDef 类,为标签创建一个流定义。
labels_stream = StreamDef(field=’labels’, shape=3, is_sparse=False)
Step 3 − 接下来的步骤是创建用于读取输入文件中的特征,创建另一个 StreamDef 实例,如下所示。
feature_stream = StreamDef(field=’features’, shape=4, is_sparse=False)
Step 4 − 现在,我们需要提供 iris.ctf 文件作为输入,并初始化 deserializer ,如下所示−
deserializer = CTFDeserializer(‘iris.ctf’, StreamDefs(labels=
label_stream, features=features_stream)
Step 5 − 最后,我们需要通过使用 deserializer 作为参数创建 minisourceBatch 的实例,如下所示 −
Minibatch_source = MinibatchSource(deserializer, randomize=True)
Creating a MinibatchSource instance - Complete implementation example
from cntk.io import StreamDef, StreamDefs, MinibatchSource, CTFDeserializer, INFINITY_REPEAT
labels_stream = StreamDef(field=’labels’, shape=3, is_sparse=False)
feature_stream = StreamDef(field=’features’, shape=4, is_sparse=False)
deserializer = CTFDeserializer(‘iris.ctf’, StreamDefs(labels=label_stream, features=features_stream)
Minibatch_source = MinibatchSource(deserializer, randomize=True)
Creating MCTF file
如上所示,我们从“iris.ctf”文件中获取数据。该文件具有称为 CNTK 文本格式(CTF)的文件格式。对于我们上述创建的 MinibatchSource 实例,创建 CTF 文件以获取数据是强制性的。我们来看看如何创建一个 CTF 文件。
Implementation Example
Step 1 − 首先,我们需要导入 panda 和 numpy 包,如下所示 −
import pandas as pd
import numpy as np
Step 2 − 接下来,我们需要将我们的数据文件,即 iris.csv 载入内存中。然后,将其存储在 df_source 变量中。
df_source = pd.read_csv(‘iris.csv’, names = [‘sepal_length’, ‘sepal_width’, ‘petal_length’, ‘petal_width’, ‘species’], index_col=False)
Step 3 − 现在,通过使用 iloc 索引器作为特征,取前四列的内容。同时,使用 species 列中的数据,如下所示 −
features = df_source.iloc[: , :4].values
labels = df_source[‘species’].values
Step 4 − 接下来,我们需要创建标签名称与其数字表示形式之间的映射。它可以通过创建 label_mapping ,如下所示,完成 −
label_mapping = {‘Iris-Setosa’ : 0, ‘Iris-Versicolor’ : 1, ‘Iris-Virginica’ : 2}
Step 5 − 现在,将标签转换为一组独热编码向量,如下所示 −
labels = [one_hot(label_mapping[v], 3) for v in labels]
现在,就如我们在之前做过的那样,创建一个名为 one_hot 的实用函数来编码标签。它可以按照以下方法完成 −
def one_hot(index, length):
result = np.zeros(length)
result[index] = 1
return result
由于我们已经载入并预处理了数据,所以这是将数据以 CTF 文件格式存储在磁盘上的时候了。我们可以借助以下 Python 代码来完成此操作 −
With open(‘iris.ctf’, ‘w’) as output_file:
for index in range(0, feature.shape[0]):
feature_values = ‘ ‘.join([str(x) for x in np.nditer(features[index])])
label_values = ‘ ‘.join([str(x) for x in np.nditer(labels[index])])
output_file.write(‘features {} | labels {} \n’.format(feature_values, label_values))
Creating a MCTF file - Complete implementation example
import pandas as pd
import numpy as np
df_source = pd.read_csv(‘iris.csv’, names = [‘sepal_length’, ‘sepal_width’, ‘petal_length’, ‘petal_width’, ‘species’], index_col=False)
features = df_source.iloc[: , :4].values
labels = df_source[‘species’].values
label_mapping = {‘Iris-Setosa’ : 0, ‘Iris-Versicolor’ : 1, ‘Iris-Virginica’ : 2}
labels = [one_hot(label_mapping[v], 3) for v in labels]
def one_hot(index, length):
result = np.zeros(length)
result[index] = 1
return result
With open(‘iris.ctf’, ‘w’) as output_file:
for index in range(0, feature.shape[0]):
feature_values = ‘ ‘.join([str(x) for x in np.nditer(features[index])])
label_values = ‘ ‘.join([str(x) for x in np.nditer(labels[index])])
output_file.write(‘features {} | labels {} \n’.format(feature_values, label_values))
Feeding the data
一旦你创建了 MinibatchSource, 实例,我们需要对其进行训练。我们可以使用与在处理小型内存数据集时相同训练逻辑。这里,我们将使用 MinibatchSource 实例作为对损失函数进行 train 方法输入,如下所示 −
Implementation Example
Step 1 − 为了记录训练过程的输出,首先从 cntk.logging 模块导入 ProgressPrinter,如下所示 −
from cntk.logging import ProgressPrinter
Step 2 − 接下来,为了设置训练,从 cntk.train 模块中导入 trainer 和 training_session 模块,如下所示 −
from cntk.train import Trainer,
Step 3 − 现在,我们需要定义一些常量集,如 minibatch_size 、 samples_per_epoch 和 num_epochs ,如下所示 −
minbatch_size = 16
samples_per_epoch = 150
num_epochs = 30
Step 4 − 接下来,为了让 CNTK 了解如何在训练期间读取数据,我们需要定义网络的输入变量和微型批次源中的流之间的映射。
input_map = {
features: minibatch.source.streams.features,
labels: minibatch.source.streams.features
}
Step 5 − 接下来,为了记录训练过程的输出,初始化 progress_printer 变量,并将其设置为一个新的 ProgressPrinter 实例,如下所示 −
progress_writer = ProgressPrinter(0)
Step 6 − 最后,我们需要调用作为损失的 train 方法,如下所示 −
train_history = loss.train(minibatch_source,
parameter_learners=[learner],
model_inputs_to_streams=input_map,
callbacks=[progress_writer],
epoch_size=samples_per_epoch,
max_epochs=num_epochs)
Feeding the data - Complete implementation example
from cntk.logging import ProgressPrinter
from cntk.train import Trainer, training_session
minbatch_size = 16
samples_per_epoch = 150
num_epochs = 30
input_map = {
features: minibatch.source.streams.features,
labels: minibatch.source.streams.features
}
progress_writer = ProgressPrinter(0)
train_history = loss.train(minibatch_source,
parameter_learners=[learner],
model_inputs_to_streams=input_map,
callbacks=[progress_writer],
epoch_size=samples_per_epoch,
max_epochs=num_epochs)