Microsoft认知工具包 - 快速指南



Microsoft认知工具包(CNTK) - 简介

本章,我们将学习什么是CNTK,它的特性,1.0版和2.0版之间的区别以及2.7版的重要亮点。

什么是Microsoft认知工具包(CNTK)?

Microsoft认知工具包(CNTK),以前称为计算网络工具包,是一个免费、易于使用、开源、商用级的工具包,使我们能够训练深度学习算法,使其像人脑一样学习。它使我们能够创建一些流行的深度学习系统,例如前馈神经网络时间序列预测系统和卷积神经网络(CNN)图像分类器

为了获得最佳性能,其框架函数是用C++编写的。虽然我们可以使用C++调用其函数,但最常用的方法是使用Python程序。

CNTK的特性

以下是Microsoft CNTK最新版本提供的一些特性和功能

内置组件

  • CNTK具有高度优化的内置组件,可以处理来自Python、C++或BrainScript的多维密集或稀疏数据。

  • 我们可以实现CNN、FNN、RNN、批量归一化和带注意力的序列到序列。

  • 它为我们提供了从Python在GPU上添加新的用户定义核心组件的功能。

  • 它还提供自动超参数调整。

  • 我们可以实现强化学习、生成对抗网络(GAN)、监督学习和无监督学习。

  • 对于海量数据集,CNTK具有内置的优化读取器。

高效利用资源

  • CNTK通过1位SGD为我们提供了在多个GPU/机器上的高精度并行性。

  • 为了将最大的模型放入GPU内存中,它提供了内存共享和其他内置方法。

轻松表达我们自己的网络

  • CNTK具有完整的API,用于从Python、C++和BrainScript定义您自己的网络、学习器、读取器、训练和评估。

  • 使用CNTK,我们可以轻松地使用Python、C++、C#或BrainScript评估模型。

  • 它提供高级和低级API。

  • 根据我们的数据,它可以自动调整推理。

  • 它具有完全优化的符号循环循环神经网络(RNN)。

测量模型性能

  • CNTK提供各种组件来衡量您构建的神经网络的性能。

  • 从您的模型和相关的优化器生成日志数据,我们可以使用这些数据来监控训练过程。

1.0版与2.0版

下表比较了CNTK 1.0版和2.0版

1.0版 2.0版
它于2016年发布。 它是1.0版的重大改写,于2017年6月发布。
它使用一种称为BrainScript的专有脚本语言。 其框架函数可以使用C++、Python调用。我们可以轻松地在C#或Java中加载我们的模块。BrainScript也受2.0版支持。
它在Windows和Linux系统上运行,但不直接在Mac OS上运行。 它还在Windows(Win 8.1、Win 10、Server 2012 R2及更高版本)和Linux系统上运行,但不直接在Mac OS上运行。

2.7版的重要亮点

2.7版是Microsoft认知工具包的最后一个主要发布版本。它完全支持ONNX 1.4.1。以下是此最新发布的CNTK版本的一些重要亮点。

  • 完全支持ONNX 1.4.1。

  • 支持Windows和Linux系统的CUDA 10。

  • 它支持ONNX导出中的高级循环神经网络(RNN)循环。

  • 它可以导出超过2GB的ONNX格式模型。

  • 它支持BrainScript脚本语言的训练操作中的FP16。

Microsoft认知工具包(CNTK) - 入门

在这里,我们将了解如何在Windows和Linux上安装CNTK。此外,本章还解释了安装CNTK包、安装Anaconda的步骤、CNTK文件、目录结构和CNTK库组织。

先决条件

为了安装CNTK,我们的计算机上必须安装Python。您可以访问链接https://pythonlang.cn/downloads/并为您的操作系统(即Windows和Linux/Unix)选择最新版本。有关Python的基础教程,您可以参考链接https://tutorialspoint.com/python3/index.htm

Python Downloads

CNTK支持Windows和Linux,因此我们将逐步介绍两者。

在Windows上安装

为了在Windows上运行CNTK,我们将使用Python的Anaconda版本。我们知道,Anaconda是Python的再发行版。它包括CNTK用于执行各种有用计算的附加包,例如ScipyScikit-learn

因此,首先让我们看看在您的计算机上安装Anaconda的步骤:

步骤1-首先从公共网站https://anaconda.net.cn/distribution/下载安装文件。

步骤2-下载安装文件后,启动安装并按照链接https://docs.anaconda.net.cn/anaconda/install/中的说明进行操作。

步骤3-安装完成后,Anaconda还将安装其他一些实用程序,这些实用程序将自动将所有Anaconda可执行文件包含在您的计算机PATH变量中。我们可以从此提示符管理我们的Python环境,安装包和运行Python脚本。

安装CNTK包

Anaconda安装完成后,您可以使用最常用的方法通过pip可执行文件安装CNTK包,使用以下命令:

pip install cntk

还有其他几种方法可以在您的计算机上安装Cognitive Toolkit。Microsoft有一套简洁的文档,详细解释了其他安装方法。请访问链接https://docs.microsoft.com/en-us/cognitive-toolkit/Setup-CNTK-on-your-machine

在Linux上安装

在Linux上安装CNTK与在Windows上安装略有不同。在这里,对于Linux,我们将使用Anaconda安装CNTK,但是我们将使用基于终端的安装程序在Linux上安装Anaconda,而不是Anaconda的图形安装程序。尽管安装程序几乎适用于所有Linux发行版,但我们将描述限制在Ubuntu。

因此,首先让我们看看在您的计算机上安装Anaconda的步骤:

安装Anaconda的步骤

步骤1-在安装Anaconda之前,请确保系统完全是最新的。要检查,首先在终端中执行以下两个命令:

sudo apt update
sudo apt upgrade

步骤2-计算机更新后,从公共网站https://anaconda.net.cn/distribution/获取最新Anaconda安装文件的URL。

步骤3-复制URL后,打开终端窗口并执行以下命令:

wget -0 anaconda-installer.sh url SHAPE \* MERGEFORMAT 
     y

	
	
	             f
 
 
      x
	  
|                     }

url占位符替换为从Anaconda网站复制的URL。

步骤4-接下来,我们可以使用以下命令安装Anaconda:

sh ./anaconda-installer.sh

上述命令将默认在我们的主目录中安装Anaconda3

安装CNTK包

Anaconda安装完成后,您可以使用最常用的方法通过pip可执行文件安装CNTK包,使用以下命令:

pip install cntk

检查CNTK文件和目录结构

一旦CNTK作为Python包安装,我们可以检查其文件和目录结构。它位于C:\Users\\Anaconda3\Lib\site-packages\cntk,如下面的屏幕截图所示。

Files and Directory Structure

验证CNTK安装

一旦CNTK作为Python包安装,您应该验证CNTK是否已正确安装。从Anaconda命令行,通过输入ipython启动Python解释器。然后,通过输入以下命令导入CNTK

import cntk as c

导入后,使用以下命令检查其版本:

print(c.__version__)

解释器将响应已安装的CNTK版本。如果它没有响应,则安装过程中会出现问题。

CNTK库的组织

CNTK(技术上是一个Python包)被组织成13个高级子包和8个较小的子包。下表包含10个最常用的包

序号 包名称和描述
1

cntk.io

包含用于读取数据的函数。例如:next_minibatch()

2

cntk.layers

包含用于创建神经网络的高级函数。例如:Dense()

3

cntk.learners

包含用于训练的函数。例如:sgd()

4

cntk.losses

包含用于测量训练误差的函数。例如:squared_error()

5

cntk.metrics

包含用于测量模型误差的函数。例如:classificatoin_error

6

cntk.ops

包含用于创建神经网络的低级函数。例如:tanh()

7

cntk.random

包含用于生成随机数的函数。例如:normal()

8

cntk.train

包含训练函数。例如:train_minibatch()

9

cntk.initializer

包含模型参数初始化器。例如:normal()uniform()

10

cntk.variables

包含低级构造。例如:Parameter()Variable()

Microsoft认知工具包(CNTK) - CPU和GPU

Microsoft认知工具包提供了两个不同的构建版本,即仅CPU和仅GPU。

仅CPU构建版本

CNTK 的仅 CPU 版本使用优化的 Intel MKLML,其中 MKLML 是 MKL(数学核心库)的子集,并作为 Intel MKL for MKL-DNN 的终止版本随 Intel MKL-DNN 一起发布。

仅 GPU 版本

另一方面,CNTK 的仅 GPU 版本使用高度优化的 NVIDIA 库,例如CUBcuDNN。它支持跨多个 GPU 和多台机器的分布式训练。为了在 CNTK 中实现更快的分布式训练,GPU 版本还包括:

  • MSR 开发的 1 位量化 SGD。

  • 块动量 SGD 并行训练算法。

在 Windows 上启用 CNTK 的 GPU

在上一节中,我们了解了如何安装 CNTK 的基本版本以与 CPU 一起使用。现在让我们讨论如何安装 CNTK 以与 GPU 一起使用。但是,在深入研究之前,首先您应该拥有一张受支持的显卡。

目前,CNTK 支持至少支持 CUDA 3.0 的 NVIDIA 显卡。要确保,您可以访问https://developer.nvidia.com/cuda-gpus查看您的 GPU 是否支持 CUDA。

因此,让我们看看在 Windows 操作系统上启用 CNTK 的 GPU 的步骤:

步骤 1 - 根据您使用的显卡,您首先需要拥有适用于您显卡的最新 GeForce 或 Quadro 驱动程序。

步骤 2 - 下载驱动程序后,您需要从 NVIDIA 网站https://developer.nvidia.com/cuda-90-download-archive?target_os=Windows&target_arch=x86_64安装适用于 Windows 的 CUDA 工具包版本 9.0。安装后,运行安装程序并按照说明操作。

步骤 3 - 接下来,您需要从 NVIDIA 网站https://developer.nvidia.com/rdp/form/cudnn-download-survey安装 cuDNN 二进制文件。对于 CUDA 9.0 版本,cuDNN 7.4.1 运行良好。基本上,cuDNN 是 CUDA 之上的一个层,由 CNTK 使用。

步骤 4 - 下载 cuDNN 二进制文件后,您需要将 zip 文件解压到 CUDA 工具包安装的根文件夹中。

步骤 5 - 这是最后一步,它将启用 CNTK 中的 GPU 使用。在 Windows 操作系统的 Anaconda 提示符中执行以下命令:

pip install cntk-gpu

在 Linux 上启用 CNTK 的 GPU

让我们看看如何在 Linux 操作系统上启用 CNTK 的 GPU:

下载 CUDA 工具包

首先,您需要从 NVIDIA 网站https://developer.nvidia.com/cuda-90-download-archive?target_os=Linux&target_arch=x86_64&target_distro=Ubuntu&target_version=1604&target_type=runfilelocal安装 CUDA 工具包。

运行安装程序

现在,一旦磁盘上有二进制文件,请通过打开终端并执行以下命令以及屏幕上的说明来运行安装程序:

sh cuda_9.0.176_384.81_linux-run

修改 Bash 配置文件

在 Linux 机器上安装 CUDA 工具包后,您需要修改 BASH 配置文件。为此,首先在文本编辑器中打开 $HOME/.bashrc 文件。现在,在脚本的末尾,包含以下几行:

export PATH=/usr/local/cuda-9.0/bin${PATH:+:${PATH}}
export LD_LIBRARY_PATH=/usr/local/cuda-9.0/lib64\
${LD_LIBRARY_PATH:+:${LD_LIBRARY_PATH}}
Installing

安装 cuDNN 库

最后,我们需要安装 cuDNN 二进制文件。它可以从 NVIDIA 网站https://developer.nvidia.com/rdp/form/cudnn-download-survey下载。对于 CUDA 9.0 版本,cuDNN 7.4.1 运行良好。基本上,cuDNN 是 CUDA 之上的一个层,由 CNTK 使用。

下载适用于 Linux 的版本后,使用以下命令将其解压到/usr/local/cuda-9.0文件夹:

tar xvzf -C /usr/local/cuda-9.0/ cudnn-9.0-linux-x64-v7.4.1.5.tgz

根据需要更改文件名的路径。

CNTK - 序列分类

在本章中,我们将详细了解 CNTK 中的序列及其分类。

张量

CNTK 工作的基础概念是张量。基本上,CNTK 的输入、输出以及参数都组织为张量,这通常被认为是广义矩阵。每个张量都有一个

  • 秩为 0 的张量是标量。

  • 秩为 1 的张量是向量。

  • 秩为 2 的张量是矩阵。

这里,这些不同的维度被称为

静态轴和动态轴

顾名思义,静态轴在整个网络的生命周期中长度相同。另一方面,动态轴的长度在不同的实例之间可能会有所不同。事实上,在呈现每个小批量之前通常不知道它们的长度。

动态轴类似于静态轴,因为它们也定义了张量中包含数字的有意义的分组。

示例

为了更清楚地说明,让我们看看如何在 CNTK 中表示短视频片段的小批量。假设视频片段的分辨率均为 640 * 480。此外,剪辑采用彩色拍摄,通常用三个通道编码。这进一步意味着我们的迷你批次具有以下内容:

  • 长度分别为 640、480 和 3 的 3 个静态轴。

  • 两个动态轴;视频的长度和迷你批次轴。

这意味着,如果一个迷你批次包含 16 个视频,每个视频长 240 帧,则将表示为16*240*3*640*480 张量。

在 CNTK 中使用序列

让我们首先了解长短期记忆网络,然后了解 CNTK 中的序列。

长短期记忆网络 (LSTM)

Long-Short Term Memory Network

长短期记忆 (LSTM) 网络由 Hochreiter & Schmidhuber 引入。它解决了让基本循环层长时间记住事物的问题。LSTM 的架构如上图所示。我们可以看到它有输入神经元、记忆单元和输出神经元。为了解决梯度消失问题,长短期记忆网络使用显式记忆单元(存储先前值)和以下门:

  • 遗忘门 - 顾名思义,它告诉记忆单元忘记先前的值。记忆单元存储值,直到门即“遗忘门”告诉它忘记它们。

  • 输入门 - 顾名思义,它向单元添加新内容。

  • 输出门 - 顾名思义,输出门决定何时将向量从单元传递到下一个隐藏状态。

在 CNTK 中使用序列非常容易。让我们借助以下示例来看:

import sys
import os
from cntk import Trainer, Axis
from cntk.io import MinibatchSource, CTFDeserializer, StreamDef, StreamDefs,\
   INFINITELY_REPEAT
from cntk.learners import sgd, learning_parameter_schedule_per_sample
from cntk import input_variable, cross_entropy_with_softmax, \
   classification_error, sequence
from cntk.logging import ProgressPrinter
from cntk.layers import Sequential, Embedding, Recurrence, LSTM, Dense
def create_reader(path, is_training, input_dim, label_dim):
   return MinibatchSource(CTFDeserializer(path, StreamDefs(
      features=StreamDef(field='x', shape=input_dim, is_sparse=True),
      labels=StreamDef(field='y', shape=label_dim, is_sparse=False)
   )), randomize=is_training,
   max_sweeps=INFINITELY_REPEAT if is_training else 1)
def LSTM_sequence_classifier_net(input, num_output_classes, embedding_dim,
LSTM_dim, cell_dim):
   lstm_classifier = Sequential([Embedding(embedding_dim),
      Recurrence(LSTM(LSTM_dim, cell_dim)),
      sequence.last,
      Dense(num_output_classes)])
return lstm_classifier(input)
def train_sequence_classifier():
   input_dim = 2000
   cell_dim = 25
   hidden_dim = 25
   embedding_dim = 50
   num_output_classes = 5
   features = sequence.input_variable(shape=input_dim, is_sparse=True)
   label = input_variable(num_output_classes)
   classifier_output = LSTM_sequence_classifier_net(
   features, num_output_classes, embedding_dim, hidden_dim, cell_dim)
   ce = cross_entropy_with_softmax(classifier_output, label)
   pe =      classification_error(classifier_output, label)
   rel_path = ("../../../Tests/EndToEndTests/Text/" +
      "SequenceClassification/Data/Train.ctf")
   path = os.path.join(os.path.dirname(os.path.abspath(__file__)), rel_path)
   reader = create_reader(path, True, input_dim, num_output_classes)
input_map = {
   features: reader.streams.features,
   label: reader.streams.labels
}
lr_per_sample = learning_parameter_schedule_per_sample(0.0005)
progress_printer = ProgressPrinter(0)
trainer = Trainer(classifier_output, (ce, pe),
sgd(classifier_output.parameters, lr=lr_per_sample),progress_printer)
minibatch_size = 200
for i in range(255):
   mb = reader.next_minibatch(minibatch_size, input_map=input_map)
trainer.train_minibatch(mb)
   evaluation_average = float(trainer.previous_minibatch_evaluation_average)
   loss_average = float(trainer.previous_minibatch_loss_average)
return evaluation_average, loss_average
if __name__ == '__main__':
   error, _ = train_sequence_classifier()
   print(" error: %f" % error)
average  since  average  since  examples
loss     last   metric   last
------------------------------------------------------
1.61    1.61    0.886     0.886     44
1.61     1.6    0.714     0.629    133
 1.6    1.59     0.56     0.448    316
1.57    1.55    0.479      0.41    682
1.53     1.5    0.464     0.449   1379
1.46     1.4    0.453     0.441   2813
1.37    1.28     0.45     0.447   5679
 1.3    1.23    0.448     0.447  11365

error: 0.333333

上述程序的详细解释将在下一节中介绍,尤其是在构建循环神经网络时。

CNTK - 逻辑回归模型

本章介绍如何在 CNTK 中构建逻辑回归模型。

逻辑回归模型的基础知识

逻辑回归是最简单的机器学习技术之一,是一种专门用于二元分类的技术。换句话说,在需要预测变量的值可能只有两个分类值之一的情况下创建预测模型。逻辑回归最简单的例子之一是根据人的年龄、声音、头发等等来预测此人是否为男性或女性。

示例

让我们借助另一个例子从数学角度了解逻辑回归的概念:

假设,我们想根据申请人的债务、收入信用评级来预测贷款申请的信用价值;0 表示拒绝,1 表示批准。我们用 X1 表示债务,用 X2 表示收入,用 X3 表示信用评级。

在逻辑回归中,我们为每个特征确定一个权重值,用w表示,并确定一个偏差值,用b表示。

现在假设:

X1 = 3.0
X2 = -2.0
X3 = 1.0

并且假设我们确定权重和偏差如下:

W1 = 0.65, W2 = 1.75, W3 = 2.05 and b = 0.33

现在,为了预测类别,我们需要应用以下公式:

Z = (X1*W1)+(X2*W2)+(X3+W3)+b
i.e. Z = (3.0)*(0.65) + (-2.0)*(1.75) + (1.0)*(2.05) + 0.33
= 0.83

接下来,我们需要计算P = 1.0/(1.0 + exp(-Z))。这里,exp() 函数是欧拉数。

P = 1.0/(1.0 + exp(-0.83)
= 0.6963

P 值可以解释为类别为 1 的概率。如果 P < 0.5,则预测类别 = 0;否则预测 (P >= 0.5) 类别 = 1。

为了确定权重和偏差的值,我们必须获得一组具有已知输入预测值和已知正确类别标签值的训练数据。之后,我们可以使用一种算法(通常是梯度下降)来查找权重和偏差的值。

LR 模型实现示例

对于此 LR 模型,我们将使用以下数据集:

1.0, 2.0, 0
3.0, 4.0, 0
5.0, 2.0, 0
6.0, 3.0, 0
8.0, 1.0, 0
9.0, 2.0, 0
1.0, 4.0, 1
2.0, 5.0, 1
4.0, 6.0, 1
6.0, 5.0, 1
7.0, 3.0, 1
8.0, 5.0, 1

要在 CNTK 中启动此 LR 模型实现,我们首先需要导入以下包:

import numpy as np
import cntk as C

程序的结构如下所示,包含 main() 函数:

def main():
print("Using CNTK version = " + str(C.__version__) + "\n")

现在,我们需要将训练数据加载到内存中,如下所示:

data_file = ".\\dataLRmodel.txt"
print("Loading data from " + data_file + "\n")
features_mat = np.loadtxt(data_file, dtype=np.float32, delimiter=",", skiprows=0, usecols=[0,1])
labels_mat = np.loadtxt(data_file, dtype=np.float32, delimiter=",", skiprows=0, usecols=[2], ndmin=2)

现在,我们将创建一个训练程序,该程序创建一个与训练数据兼容的逻辑回归模型:

features_dim = 2
labels_dim = 1
X = C.ops.input_variable(features_dim, np.float32)
y = C.input_variable(labels_dim, np.float32)
W = C.parameter(shape=(features_dim, 1)) # trainable cntk.Parameter
b = C.parameter(shape=(labels_dim))
z = C.times(X, W) + b
p = 1.0 / (1.0 + C.exp(-z))
model = p

现在,我们需要创建学习器和训练器,如下所示:

ce_error = C.binary_cross_entropy(model, y) # CE a bit more principled for LR
fixed_lr = 0.010
learner = C.sgd(model.parameters, fixed_lr)
trainer = C.Trainer(model, (ce_error), [learner])
max_iterations = 4000

LR 模型训练

一旦我们创建了 LR 模型,接下来就是开始训练过程:

np.random.seed(4)
N = len(features_mat)
for i in range(0, max_iterations):
row = np.random.choice(N,1) # pick a random row from training items
trainer.train_minibatch({ X: features_mat[row], y: labels_mat[row] })
if i % 1000 == 0 and i > 0:
mcee = trainer.previous_minibatch_loss_average
print(str(i) + " Cross-entropy error on curr item = %0.4f " % mcee)

现在,借助以下代码,我们可以打印模型权重和偏差:

np.set_printoptions(precision=4, suppress=True)
print("Model weights: ")
print(W.value)
print("Model bias:")
print(b.value)
print("")
if __name__ == "__main__":
main()

训练逻辑回归模型 -完整示例

import numpy as np
import cntk as C
   def main():
print("Using CNTK version = " + str(C.__version__) + "\n")
data_file = ".\\dataLRmodel.txt" # provide the name and the location of data file
print("Loading data from " + data_file + "\n")
features_mat = np.loadtxt(data_file, dtype=np.float32, delimiter=",", skiprows=0, usecols=[0,1])
labels_mat = np.loadtxt(data_file, dtype=np.float32, delimiter=",", skiprows=0, usecols=[2], ndmin=2)
features_dim = 2
labels_dim = 1
X = C.ops.input_variable(features_dim, np.float32)
y = C.input_variable(labels_dim, np.float32)
W = C.parameter(shape=(features_dim, 1)) # trainable cntk.Parameter
b = C.parameter(shape=(labels_dim))
z = C.times(X, W) + b
p = 1.0 / (1.0 + C.exp(-z))
model = p
ce_error = C.binary_cross_entropy(model, y) # CE a bit more principled for LR
fixed_lr = 0.010
learner = C.sgd(model.parameters, fixed_lr)
trainer = C.Trainer(model, (ce_error), [learner])
max_iterations = 4000
np.random.seed(4)
N = len(features_mat)
for i in range(0, max_iterations):
row = np.random.choice(N,1) # pick a random row from training items
trainer.train_minibatch({ X: features_mat[row], y: labels_mat[row] })
if i % 1000 == 0 and i > 0:
mcee = trainer.previous_minibatch_loss_average
print(str(i) + " Cross-entropy error on curr item = %0.4f " % mcee)
np.set_printoptions(precision=4, suppress=True)
print("Model weights: ")
print(W.value)
print("Model bias:")
print(b.value)
if __name__ == "__main__":
  main()

输出

Using CNTK version = 2.7
1000 cross entropy error on curr item = 0.1941
2000 cross entropy error on curr item = 0.1746
3000 cross entropy error on curr item = 0.0563
Model weights:
[-0.2049]
   [0.9666]]
Model bias:
[-2.2846]

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

一旦 LR 模型经过训练,我们就可以使用它进行预测,如下所示:

首先,我们的评估程序导入 numpy 包并将训练数据加载到特征矩阵和类别标签矩阵中,方法与我们上面实现的训练程序相同:

import numpy as np
def main():
data_file = ".\\dataLRmodel.txt" # provide the name and the location of data file
features_mat = np.loadtxt(data_file, dtype=np.float32, delimiter=",",
skiprows=0, usecols=(0,1))
labels_mat = np.loadtxt(data_file, dtype=np.float32, delimiter=",",
skiprows=0, usecols=[2], ndmin=2)

接下来,是时候设置训练程序确定的权重和偏差的值了:

print("Setting weights and bias values \n")
weights = np.array([0.0925, 1.1722], dtype=np.float32)
bias = np.array([-4.5400], dtype=np.float32)
N = len(features_mat)
features_dim = 2

接下来,我们的评估程序将通过遍历每个训练项目来计算逻辑回归概率,如下所示:

print("item pred_prob pred_label act_label result")
for i in range(0, N): # each item
   x = features_mat[i]
   z = 0.0
   for j in range(0, features_dim):
   z += x[j] * weights[j]
   z += bias[0]
   pred_prob = 1.0 / (1.0 + np.exp(-z))
  pred_label = 0 if pred_prob < 0.5 else 1
   act_label = labels_mat[i]
   pred_str = ‘correct’ if np.absolute(pred_label - act_label) < 1.0e-5 \
    else ‘WRONG’
  print("%2d %0.4f %0.0f %0.0f %s" % \ (i, pred_prob, pred_label, act_label, pred_str))

现在让我们演示如何进行预测:

x = np.array([9.5, 4.5], dtype=np.float32)
print("\nPredicting class for age, education = ")
print(x)
z = 0.0
for j in range(0, features_dim):
z += x[j] * weights[j]
z += bias[0]
p = 1.0 / (1.0 + np.exp(-z))
print("Predicted p = " + str(p))
if p < 0.5: print("Predicted class = 0")
else: print("Predicted class = 1")

完整的预测评估程序

import numpy as np
def main():
data_file = ".\\dataLRmodel.txt" # provide the name and the location of data file
features_mat = np.loadtxt(data_file, dtype=np.float32, delimiter=",",
skiprows=0, usecols=(0,1))
labels_mat = np.loadtxt(data_file, dtype=np.float32, delimiter=",",
skiprows=0, usecols=[2], ndmin=2)
print("Setting weights and bias values \n")
weights = np.array([0.0925, 1.1722], dtype=np.float32)
bias = np.array([-4.5400], dtype=np.float32)
N = len(features_mat)
features_dim = 2
print("item pred_prob pred_label act_label result")
for i in range(0, N): # each item
   x = features_mat[i]
   z = 0.0
   for j in range(0, features_dim):
     z += x[j] * weights[j]
   z += bias[0]
   pred_prob = 1.0 / (1.0 + np.exp(-z))
   pred_label = 0 if pred_prob < 0.5 else 1
   act_label = labels_mat[i]
   pred_str = ‘correct’ if np.absolute(pred_label - act_label) < 1.0e-5 \
     else ‘WRONG’
  print("%2d %0.4f %0.0f %0.0f %s" % \ (i, pred_prob, pred_label, act_label, pred_str))
x = np.array([9.5, 4.5], dtype=np.float32)
print("\nPredicting class for age, education = ")
print(x)
z = 0.0
for j in range(0, features_dim):
   z += x[j] * weights[j]
z += bias[0]
p = 1.0 / (1.0 + np.exp(-z))
print("Predicted p = " + str(p))
if p < 0.5: print("Predicted class = 0")
else: print("Predicted class = 1")
if __name__ == "__main__":
  main()

输出

设置权重和偏差值。

Item  pred_prob  pred_label  act_label  result
0   0.3640         0             0     correct
1   0.7254         1             0      WRONG
2   0.2019         0             0     correct
3   0.3562         0             0     correct
4   0.0493         0             0     correct
5   0.1005         0             0     correct
6   0.7892         1             1     correct
7   0.8564         1             1     correct
8   0.9654         1             1     correct
9   0.7587         1             1     correct
10  0.3040         0             1      WRONG
11  0.7129         1             1     correct
Predicting class for age, education =
[9.5 4.5]
Predicting p = 0.526487952
Predicting class = 1

CNTK - 神经网络(NN) 概念

本章介绍关于 CNTK 的神经网络概念。

众所周知,使用多层神经元来构建神经网络。但是,问题出现了,在 CNTK 中我们如何模拟 NN 的层?这可以通过层模块中定义的层函数来完成。

层函数

实际上,在 CNTK 中,使用层具有明显的函数式编程风格。层函数看起来像一个常规函数,它产生一个具有预定义参数集的数学函数。让我们看看如何借助层函数创建最基本的层类型 Dense。

示例

借助以下基本步骤,我们可以创建最基本的层类型:

步骤 1 - 首先,我们需要从 CNTK 的 layers 包中导入 Dense 层函数。

from cntk.layers import Dense

步骤 2 - 接下来,我们需要从 CNTK 根包中导入 input_variable 函数。

from cntk import input_variable

步骤 3 - 现在,我们需要使用 input_variable 函数创建一个新的输入变量。我们还需要提供它的 size。

feature = input_variable(100)

步骤 4 - 最后,我们将使用 Dense 函数创建一个新层,并提供我们想要的神经元数量。

layer = Dense(40)(feature)

现在,我们可以调用已配置的 Dense 层函数来将 Dense 层连接到输入。

完整的实现示例

from cntk.layers import Dense
from cntk import input_variable
feature= input_variable(100)
layer = Dense(40)(feature)

自定义层

正如我们所看到的,CNTK 为我们提供了一套相当不错的默认设置来构建 NN。根据我们选择的激活函数和其他设置,NN 的行为和性能有所不同。这是另一个非常有用的词干提取算法。这就是为什么了解我们可以配置什么内容很重要。

配置 Dense 层的步骤

NN 中的每个层都有其独特的配置选项,当我们谈论 Dense 层时,我们有以下重要的设置需要定义:

  • shape - 顾名思义,它定义了层的输出形状,这进一步决定了该层中的神经元数量。

  • activation - 它定义了该层的激活函数,因此它可以转换输入数据。

  • init - 它定义了该层的初始化函数。当我们开始训练 NN 时,它将初始化该层的参数。

让我们看看我们可以使用哪些步骤来配置Dense层:

步骤 1 - 首先,我们需要从 CNTK 的 layers 包中导入Dense层函数。

from cntk.layers import Dense

步骤2 − 接下来,我们需要从 CNTK ops 包中导入 sigmoid 算子。它将用作激活函数。

from cntk.ops import sigmoid

步骤3 − 现在,我们需要从 initializer 包中导入 glorot_uniform 初始化器。

from cntk.initializer import glorot_uniform

步骤4 − 最后,我们将使用 Dense 函数创建一个新层,并将神经元数量作为第一个参数提供。此外,将 sigmoid 算子作为激活函数,并将 glorot_uniform 作为该层的初始化函数。

layer = Dense(50, activation = sigmoid, init = glorot_uniform)

完整的实现示例 -

from cntk.layers import Dense
from cntk.ops import sigmoid
from cntk.initializer import glorot_uniform
layer = Dense(50, activation = sigmoid, init = glorot_uniform)

优化参数

到目前为止,我们已经了解了如何创建神经网络的结构以及如何配置各种设置。在这里,我们将了解如何优化神经网络的参数。借助学习器训练器这两个组件的组合,我们可以优化神经网络的参数。

训练器组件

用于优化神经网络参数的第一个组件是训练器组件。它基本上实现了反向传播过程。如果我们谈论它的工作原理,它会将数据通过神经网络以获得预测。

之后,它使用另一个称为学习器的组件来获取神经网络中参数的新值。一旦获得新值,它就会应用这些新值并重复此过程,直到满足退出条件。

学习器组件

用于优化神经网络参数的第二个组件是学习器组件,它主要负责执行梯度下降算法。

CNTK 库中包含的学习器

以下是 CNTK 库中包含的一些有趣的学习器的列表 -

  • 随机梯度下降 (SGD) − 此学习器表示基本的随机梯度下降,没有任何额外功能。

  • 动量随机梯度下降 (MomentumSGD) − 使用 SGD,此学习器应用动量来克服局部最大值的问题。

  • RMSProp − 此学习器为了控制下降速率,使用了衰减学习率。

  • Adam − 此学习器为了随时间推移降低下降速率,使用了衰减动量。

  • Adagrad − 此学习器对频繁出现和不频繁出现的特征使用不同的学习率。

CNTK - 创建第一个神经网络

本章将详细阐述在 CNTK 中创建神经网络。

构建网络结构

为了应用 CNTK 概念来构建我们的第一个神经网络,我们将使用神经网络根据萼片宽度和长度以及花瓣宽度和长度的物理属性对鸢尾花物种进行分类。我们将使用的这个数据集是鸢尾花数据集,它描述了不同品种鸢尾花的物理属性 -

  • 萼片长度
  • 萼片宽度
  • 花瓣长度
  • 花瓣宽度
  • 类别,即山鸢尾、杂色鸢尾或弗吉尼亚鸢尾

在这里,我们将构建一个称为前馈神经网络的常规神经网络。让我们看看构建神经网络结构的实现步骤 -

步骤1 − 首先,我们将从 CNTK 库中导入必要的组件,例如我们的层类型、激活函数和允许我们为神经网络定义输入变量的函数。

from cntk import default_options, input_variable
from cntk.layers import Dense, Sequential
from cntk.ops import log_softmax, relu

步骤2 − 之后,我们将使用 sequential 函数创建我们的模型。创建完成后,我们将向其中输入我们想要的层。在这里,我们将在神经网络中创建两层不同的层;一层有四个神经元,另一层有三个神经元。

model = Sequential([Dense(4, activation=relu), Dense(3, activation=log_sogtmax)])

步骤3 − 最后,为了编译神经网络,我们将网络绑定到输入变量。它具有一个具有四个神经元的输入层和一个具有三个神经元的输出层。

feature= input_variable(4)
z = model(feature)

应用激活函数

有很多激活函数可供选择,选择正确的激活函数肯定会对深度学习模型的性能产生重大影响。

在输出层

在输出层选择激活函数将取决于我们将用模型解决的问题类型。

  • 对于回归问题,我们应该在输出层使用线性激活函数

  • 对于二元分类问题,我们应该在输出层使用 sigmoid 激活函数

  • 对于多类分类问题,我们应该在输出层使用 softmax 激活函数

  • 在这里,我们将构建一个用于预测三个类别之一的模型。这意味着我们需要在输出层使用 softmax 激活函数

在隐藏层

在隐藏层选择激活函数需要一些实验来监控性能,以查看哪个激活函数效果好。

  • 在分类问题中,我们需要预测样本属于特定类别的概率。这就是为什么我们需要一个激活函数来为我们提供概率值。为了达到这个目标, sigmoid 激活函数可以帮助我们。

  • 与 sigmoid 函数相关的主要问题之一是梯度消失问题。为了克服这个问题,我们可以使用 ReLU 激活函数,它将所有负值转换为零,并作为正值的直通滤波器。

选择损失函数

一旦我们有了神经网络模型的结构,我们就必须对其进行优化。为了优化,我们需要一个损失函数。与激活函数不同的是,我们可选择的损失函数很少。但是,选择损失函数将取决于我们将用模型解决的问题类型。

例如,在分类问题中,我们应该使用一个可以衡量预测类别和实际类别之间差异的损失函数。

损失函数

对于我们将用神经网络模型解决的分类问题,交叉熵损失函数是最佳选择。在 CNTK 中,它实现为cross_entropy_with_softmax,可以从cntk.losses包中导入,如下所示:

label= input_variable(3)
loss = cross_entropy_with_softmax(z, label)

指标

有了神经网络模型的结构和要应用的损失函数,我们就具备了开始制定优化深度学习模型方案的所有要素。但是,在深入研究之前,我们应该了解指标。

cntk.metrics

CNTK 有一个名为cntk.metrics的包,我们可以从中导入我们将使用的指标。由于我们正在构建一个分类模型,我们将使用classification_error指标,它将产生一个介于 0 和 1 之间的数字。介于 0 和 1 之间的数字表示正确预测的样本的百分比 -

首先,我们需要从cntk.metrics包中导入指标 -

from cntk.metrics import classification_error
error_rate = classification_error(z, label)

上述函数实际上需要神经网络的输出和预期标签作为输入。

CNTK - 训练神经网络

在这里,我们将了解如何在 CNTK 中训练神经网络。

在 CNTK 中训练模型

在上一节中,我们已经定义了深度学习模型的所有组件。现在是时候训练它了。正如我们前面讨论的那样,我们可以使用学习器训练器的组合在 CNTK 中训练神经网络模型。

选择学习器并设置训练

在本节中,我们将定义学习器。CNTK 提供了多种学习器可供选择。对于我们在前面几节中定义的模型,我们将使用随机梯度下降 (SGD) 学习器

为了训练神经网络,让我们使用以下步骤配置学习器训练器 -

步骤1 − 首先,我们需要从cntk.learners包中导入sgd函数。

from cntk.learners import sgd

步骤2 − 接下来,我们需要从cntk.train.trainer包中导入Trainer函数。

from cntk.train.trainer import Trainer

步骤3 − 现在,我们需要创建一个学习器。可以通过调用sgd函数并提供模型的参数和学习率的值来创建它。

learner = sgd(z.parametrs, 0.01)

步骤4 − 最后,我们需要初始化训练器。必须向其提供网络、损失指标的组合以及学习器

trainer = Trainer(z, (loss, error_rate), [learner])

控制优化速度的学习率应为 0.1 到 0.001 之间的较小数字。

选择学习器并设置训练 - 完整示例

from cntk.learners import sgd
from cntk.train.trainer import Trainer
learner = sgd(z.parametrs, 0.01)
trainer = Trainer(z, (loss, error_rate), [learner])

将数据馈送到训练器

一旦我们选择并配置了训练器,就该加载数据集了。我们将鸢尾花数据集保存为 .CSV 文件,我们将使用名为pandas的数据整理包来加载数据集。

从 .CSV 文件加载数据集的步骤

步骤1 − 首先,我们需要导入pandas包。

from import pandas as pd

步骤2 − 现在,我们需要调用名为read_csv的函数来从磁盘加载 .csv 文件。

df_source = pd.read_csv(‘iris.csv’, names = [‘sepal_length’, ‘sepal_width’, 
‘petal_length’, ‘petal_width’, index_col=False)

加载数据集后,我们需要将其拆分为一组特征和一个标签。

将数据集拆分为特征和标签的步骤

步骤1 − 首先,我们需要从数据集中选择所有行和前四列。这可以通过使用iloc函数来完成。

x = df_source.iloc[:, :4].values

步骤2 − 接下来,我们需要从鸢尾花数据集中选择 species 列。我们将使用 values 属性来访问底层的numpy数组。

x = df_source[‘species’].values

将 species 列编码为数值向量表示的步骤

正如我们前面讨论的那样,我们的模型基于分类,它需要数值输入值。因此,这里我们需要将 species 列编码为数值向量表示。让我们看看执行此操作的步骤 -

步骤1 − 首先,我们需要创建一个列表表达式来迭代数组中的所有元素。然后为每个值在 label_mapping 字典中执行查找。

label_mapping = {‘Iris-Setosa’ : 0, ‘Iris-Versicolor’ : 1, ‘Iris-Virginica’ : 2}

步骤2 − 接下来,将这个转换后的数值转换为独热编码向量。我们将使用one_hot函数,如下所示 -

def one_hot(index, length):
result = np.zeros(length)
result[index] = 1
return result

步骤3 − 最后,我们需要将这个转换后的列表转换为numpy数组。

y = np.array([one_hot(label_mapping[v], 3) for v in y])

检测过拟合的步骤

当你的模型记住样本但无法从训练样本中推导出规则时,这种情况就是过拟合。借助以下步骤,我们可以检测模型的过拟合 -

步骤1 − 首先,从sklearn包的model_selection模块中导入train_test_split函数。

from sklearn.model_selection import train_test_split

步骤2 − 接下来,我们需要使用特征 x 和标签 y 调用 train_test_split 函数,如下所示 -

x_train, x_test, y_train, y_test = train_test_split(X, y, test_size=0-2, 
stratify=y)

我们指定了 0.2 的 test_size 以留出 20% 的总数据。

label_mapping = {‘Iris-Setosa’ : 0, ‘Iris-Versicolor’ : 1, ‘Iris-Virginica’ : 2}

将训练集和验证集馈送到我们的模型的步骤

步骤1 − 为了训练我们的模型,首先,我们将调用train_minibatch方法。然后向它提供一个字典,该字典将输入数据映射到我们用来定义神经网络及其关联损失函数的输入变量。

trainer.train_minibatch({ features: X_train, label: y_train})

步骤2 − 接下来,使用以下 for 循环调用train_minibatch -

for _epoch in range(10):
trainer.train_minbatch ({ feature: X_train, label: y_train})
print(‘Loss: {}, Acc: {}’.format(
trainer.previous_minibatch_loss_average,
trainer.previous_minibatch_evaluation_average))

将数据馈送到训练器 - 完整示例

from import pandas as pd
df_source = pd.read_csv(‘iris.csv’, names = [‘sepal_length’, ‘sepal_width’, ‘petal_length’, ‘petal_width’, index_col=False)
x = df_source.iloc[:, :4].values
x = df_source[‘species’].values
label_mapping = {‘Iris-Setosa’ : 0, ‘Iris-Versicolor’ : 1, ‘Iris-Virginica’ : 2}
def one_hot(index, length):
result = np.zeros(length)
result[index] = 1
return result
y = np.array([one_hot(label_mapping[v], 3) for v in y])
from sklearn.model_selection import train_test_split
x_train, x_test, y_train, y_test = train_test_split(X, y, test_size=0-2, stratify=y)
label_mapping = {‘Iris-Setosa’ : 0, ‘Iris-Versicolor’ : 1, ‘Iris-Virginica’ : 2}
trainer.train_minibatch({ features: X_train, label: y_train})
for _epoch in range(10):
trainer.train_minbatch ({ feature: X_train, label: y_train})
print(‘Loss: {}, Acc: {}’.format(
trainer.previous_minibatch_loss_average,
trainer.previous_minibatch_evaluation_average))

衡量神经网络的性能

为了优化我们的神经网络模型,每当我们通过训练器传递数据时,它都会通过我们为训练器配置的指标来衡量模型的性能。这种在训练期间对神经网络模型性能的测量是在训练数据上进行的。但另一方面,为了对模型性能进行全面分析,我们还需要使用测试数据。

因此,为了使用测试数据来衡量模型的性能,我们可以调用训练器上的test_minibatch方法,如下所示 -

trainer.test_minibatch({ features: X_test, label: y_test})

使用神经网络进行预测

一旦你训练了一个深度学习模型,最重要的事情就是使用它进行预测。为了使用上面训练的神经网络进行预测,我们可以遵循以下步骤 -

步骤1 − 首先,我们需要使用以下函数从测试集中选择一个随机项 -

np.random.choice

步骤2 − 接下来,我们需要使用sample_index从测试集中选择样本数据。

步骤 3 − 现在,为了将神经网络 (NN) 的数值输出转换为实际标签,创建一个反向映射。

步骤 4 − 现在,使用选定的样本数据。通过调用 NN z 作为函数来进行预测。

步骤 5 − 现在,一旦你得到预测输出,将值最大的神经元的索引作为预测值。可以使用numpy包中的np.argmax函数来实现。

步骤 6 − 最后,使用inverted_mapping将索引值转换为真实标签。

使用神经网络进行预测 -完整示例

sample_index = np.random.choice(X_test.shape[0])
sample = X_test[sample_index]
inverted_mapping = {
   1:’Iris-setosa’,
   2:’Iris-versicolor’,
   3:’Iris-virginica’
}
prediction = z(sample)
predicted_label = inverted_mapping[np.argmax(prediction)]
print(predicted_label)

输出

训练上述深度学习模型并运行后,您将得到以下输出:

Iris-versicolor

CNTK - 内存数据集和大型数据集

本章我们将学习如何在CNTK中处理内存中和大型数据集。

使用小型内存数据集进行训练

当我们谈到将数据馈送到CNTK训练器时,可能有许多方法,但这将取决于数据集的大小和数据的格式。数据集可以是小型的内存数据集或大型数据集。

在本节中,我们将使用内存数据集。为此,我们将使用以下两个框架:

  • Numpy
  • Pandas

使用Numpy数组

在这里,我们将使用基于numpy的随机生成数据集在CNTK中工作。在这个例子中,我们将模拟二元分类问题的数 据。假设我们有一组具有4个特征的观察结果,并希望用我们的深度学习模型预测两个可能的标签。

实现示例

为此,我们首先必须生成一组包含标签的独热向量表示的标签,我们想要预测。这可以通过以下步骤完成:

步骤 1 − 如下导入numpy包:

import numpy as np
num_samples = 20000

步骤 2 − 接下来,使用np.eye函数生成标签映射,如下所示:

label_mapping = np.eye(2)

步骤 3 − 现在,使用np.random.choice函数,收集20000个随机样本,如下所示:

y = label_mapping[np.random.choice(2,num_samples)].astype(np.float32)

步骤 4 − 现在最后使用np.random.random函数,生成一个随机浮点值的数组,如下所示:

x = np.random.random(size=(num_samples, 4)).astype(np.float32)

一旦我们生成一个随机浮点值数组,我们需要将它们转换为32位浮点数,以便它可以与CNTK期望的格式匹配。让我们按照以下步骤操作:

步骤 5 − 从cntk.layers模块导入Dense和Sequential层函数,如下所示:

from cntk.layers import Dense, Sequential

步骤 6 − 现在,我们需要为网络中的层导入激活函数。让我们导入sigmoid作为激活函数。

from cntk import input_variable, default_options
from cntk.ops import sigmoid

步骤 7 − 现在,我们需要导入损失函数来训练网络。让我们导入binary_cross_entropy作为损失函数。

from cntk.losses import binary_cross_entropy

步骤 8 − 接下来,我们需要定义网络的默认选项。在这里,我们将提供sigmoid激活函数作为默认设置。还可以使用Sequential层函数创建模型,如下所示:

with default_options(activation=sigmoid):
model = Sequential([Dense(6),Dense(2)])

步骤 9 − 接下来,初始化一个具有4个输入特征的input_variable作为网络的输入。

features = input_variable(4)

步骤 10 − 现在,为了完成它,我们需要将特征变量连接到NN。

z = model(features)

所以,现在我们有一个NN,借助以下步骤,让我们使用内存数据集对其进行训练:

步骤 11 − 要训练此NN,首先需要从cntk.learners模块导入学习器。我们将导入sgd学习器,如下所示:

from cntk.learners import sgd

步骤 12 − 同时从cntk.logging模块导入ProgressPrinter

from cntk.logging import ProgressPrinter
progress_writer = ProgressPrinter(0)

步骤 13 − 接下来,为标签定义一个新的输入变量,如下所示:

labels = input_variable(2)

步骤 14 − 为了训练NN模型,接下来,我们需要使用binary_cross_entropy函数定义损失。还要提供模型z和标签变量。

loss = binary_cross_entropy(z, labels)

步骤 15 − 接下来,初始化sgd学习器,如下所示:

learner = sgd(z.parameters, lr=0.1)

步骤 16 − 最后,在损失函数上调用train方法。还要提供输入数据、sgd学习器和progress_printer

training_summary=loss.train((x,y),parameter_learners=[learner],callbacks=[progress_writer])

完整的实现示例

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])

输出

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

使用Pandas DataFrames

Numpy数组在其可以包含的内容方面非常有限,并且是存储数据的最基本方法之一。例如,单个n维数组可以包含单个数据类型的数据。但另一方面,对于许多现实世界的案例,我们需要一个可以处理单个数据集中多个数据类型的库。

名为Pandas的Python库之一使处理此类数据集更容易。它引入了DataFrame (DF)的概念,并允许我们将存储在各种格式中的磁盘数据集加载为DF。例如,我们可以读取存储为CSV、JSON、Excel等的DF。

您可以在 https://tutorialspoint.com/python_pandas/index.htm了解更多关于Python Pandas库的细节。

实现示例

在这个例子中,我们将使用根据四个属性对三种可能的鸢尾花物种进行分类的例子。我们之前也已经创建了这个深度学习模型。模型如下:

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函数来训练网络。为此,我们必须首先加载和预处理鸢尾花数据集,使其与NN的预期布局和数据格式匹配。这可以通过以下步骤完成:

步骤 1 − 如下导入numpyPandas包:

import numpy as np
import pandas as pd

步骤 2 − 接下来,使用read_csv函数将数据集加载到内存中:

df_source = pd.read_csv(‘iris.csv’, names = [‘sepal_length’, ‘sepal_width’,
 ‘petal_length’, ‘petal_width’, ‘species’], index_col=False)

步骤 3 − 现在,我们需要创建一个字典,它将数据集中的标签与其对应的数字表示进行映射。

label_mapping = {‘Iris-Setosa’ : 0, ‘Iris-Versicolor’ : 1, ‘Iris-Virginica’ : 2}

步骤 4 − 现在,通过在DataFrame上使用iloc索引器,选择前四列,如下所示:

x = df_source.iloc[:, :4].values

步骤 5 −接下来,我们需要选择物种列作为数据集的标签。这可以按如下方式完成:

y = df_source[‘species’].values

步骤 6 − 现在,我们需要映射数据集中的标签,这可以通过使用label_mapping来完成。还要使用one-hot编码将它们转换为one-hot编码数组。

y = np.array([one_hot(label_mapping[v], 3) for v in y])

步骤 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数组,借助以下步骤,我们可以使用它们来训练我们的模型:

步骤 8 − 首先,我们需要导入损失函数来训练网络。让我们导入binary_cross_entropy_with_softmax作为损失函数:

from cntk.losses import binary_cross_entropy_with_softmax

步骤 9 − 要训练此NN,我们还需要从cntk.learners模块导入学习器。我们将导入sgd学习器,如下所示:

from cntk.learners import sgd

步骤 10 − 同时从cntk.logging模块导入ProgressPrinter

from cntk.logging import ProgressPrinter
progress_writer = ProgressPrinter(0)

步骤 11 − 接下来,为标签定义一个新的输入变量,如下所示:

labels = input_variable(3)

步骤 12 − 为了训练NN模型,接下来,我们需要使用binary_cross_entropy_with_softmax函数定义损失。还要提供模型z和标签变量。

loss = binary_cross_entropy_with_softmax (z, labels)

步骤 13 − 接下来,初始化sgd学习器,如下所示:

learner = sgd(z.parameters, 0.1)

步骤 14 − 最后,在损失函数上调用train方法。还要提供输入数据、sgd学习器和progress_printer

training_summary=loss.train((x,y),parameter_learners=[learner],callbacks=
[progress_writer],minibatch_size=16,max_epochs=5)

完整的实现示例

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)

输出

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
[………]

使用大型数据集进行训练

在上一节中,我们使用Numpy和pandas处理了小型内存数据集,但并非所有数据集都这么小。特别是包含图像、视频、声音样本的数据集很大。MinibatchSource是一个组件,它可以分块加载数据,由CNTK提供,用于处理此类大型数据集。MinibatchSource组件的一些特性如下:

  • MinibatchSource可以通过自动随机化从数据源读取的样本,防止NN过度拟合。

  • 它具有内置的转换管道,可用于增强数据。

  • 它在与训练过程分开的后台线程上加载数据。

在以下各节中,我们将探讨如何将小批量源与内存外数据一起使用以处理大型数据集。我们还将探讨如何使用它来馈送用于训练NN的数据。

创建MinibatchSource实例

在上一节中,我们使用了鸢尾花示例,并使用Pandas DataFrames处理了小型内存数据集。在这里,我们将使用MinibatchSource替换使用来自pandas DF的数据的代码。首先,我们需要使用以下步骤创建一个MinibatchSource实例:

实现示例

步骤 1 − 首先,从cntk.io模块导入小批量源的组件,如下所示:

from cntk.io import StreamDef, StreamDefs, MinibatchSource, CTFDeserializer,
 INFINITY_REPEAT

步骤 2 − 现在,使用StreamDef类,为标签创建一个流定义。

labels_stream = StreamDef(field=’labels’, shape=3, is_sparse=False)

步骤 3 − 接下来,要读取输入文件中的特征字段,请创建另一个StreamDef实例,如下所示。

feature_stream = StreamDef(field=’features’, shape=4, is_sparse=False)

步骤 4 − 现在,我们需要提供iris.ctf文件作为输入并初始化deserializer,如下所示:

deserializer = CTFDeserializer(‘iris.ctf’, StreamDefs(labels=
label_stream, features=features_stream)

步骤 5 − 最后,我们需要使用deserializer创建minisourceBatch实例,如下所示:

Minibatch_source = MinibatchSource(deserializer, randomize=True)

创建MinibatchSource实例 - 完整实现示例

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)

创建MCTF文件

正如您在上面看到的,我们正在从“iris.ctf”文件获取数据。它具有称为CNTK文本格式(CTF)的文件格式。必须创建一个CTF文件才能获取上面创建的MinibatchSource实例的数据。让我们看看如何创建一个CTF文件。

实现示例

步骤 1 − 首先,我们需要导入pandas和numpy包,如下所示:

import pandas as pd
import numpy as np

步骤 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)

步骤 3 − 现在,使用iloc索引器作为特征,获取前四列的内容。还要使用来自物种列的数据,如下所示:

features = df_source.iloc[: , :4].values
labels = df_source[‘species’].values

步骤 4 − 接下来,我们需要创建标签名称与其数字表示之间的映射。这可以通过创建label_mapping来完成,如下所示:

label_mapping = {‘Iris-Setosa’ : 0, ‘Iris-Versicolor’ : 1, ‘Iris-Virginica’ : 2}

步骤 5 − 现在,将标签转换为一组one-hot编码向量,如下所示:

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))

创建MCTF文件 - 完整实现示例

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))

数据馈送

创建**MinibatchSource**实例后,我们需要对其进行训练。我们可以使用与处理小型内存数据集时相同的训练逻辑。这里,我们将使用**MinibatchSource**实例作为损失函数训练方法的输入,如下所示:

实现示例

**步骤 1** - 为了记录训练会话的输出,首先从**cntk.logging**模块导入ProgressPrinter,如下所示:

from cntk.logging import ProgressPrinter

**步骤 2** - 接下来,要设置训练会话,请从**cntk.train**模块导入**trainer**和**training_session**,如下所示:

from cntk.train import Trainer, 

**步骤 3** - 现在,我们需要定义一些常量,例如**minibatch_size**、**samples_per_epoch**和**num_epochs**,如下所示:

minbatch_size = 16
samples_per_epoch = 150
num_epochs = 30

**步骤 4** - 接下来,为了让CNTK知道如何在训练期间读取数据,我们需要定义网络输入变量和微批次源中的流之间的映射。

input_map = {
     features: minibatch.source.streams.features,
     labels: minibatch.source.streams.features
}

**步骤 5** - 接下来,要记录训练过程的输出,请使用新的**ProgressPrinter**实例初始化**progress_printer**变量,如下所示:

progress_writer = ProgressPrinter(0)

**步骤 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)

数据馈送 - 完整的实现示例

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)

输出

-------------------------------------------------------------------
average   since   average   since  examples
loss      last     metric   last
------------------------------------------------------
Learning rate per minibatch: 0.1
1.21      1.21      0        0       32
1.15      0.12      0        0       96
[………]

CNTK - 性能测量

本章将解释如何在CNKT中衡量模型性能。

验证模型性能的策略

构建机器学习模型后,我们通常使用一组数据样本对其进行训练。通过这种训练,我们的机器学习模型学习并推导出一些一般规则。当我们将新样本(即与训练时提供的样本不同的样本)馈送到模型时,机器学习模型的性能至关重要。在这种情况下,模型的行为会有所不同。它在对这些新样本进行良好预测方面可能效果较差。

但模型也必须对新样本表现良好,因为在生产环境中,我们将获得与用于训练目的的样本数据不同的输入。因此,我们应该使用一组与用于训练目的的样本不同的样本验证机器学习模型。在这里,我们将讨论两种不同的技术来创建用于验证神经网络的数据集。

留出数据集

这是创建用于验证神经网络的数据集的最简单方法之一。顾名思义,在这种方法中,我们将从训练中保留一组样本(例如20%),并用它来测试我们机器学习模型的性能。下图显示了训练样本和验证样本之间的比例:

Hold-out Dataset

留出数据集模型确保我们有足够的数据来训练我们的机器学习模型,同时我们也将有合理的样本数量来获得对模型性能的良好衡量。

为了包含在训练集和测试集中,最好从主数据集中选择随机样本。它确保训练集和测试集之间均匀分布。

以下是一个例子,我们使用**scikit-learn**库中的**train_test_split**函数创建自己的留出数据集。

示例

from sklearn.datasets import load_iris
iris = load_iris()
X = iris.data
y = iris.target
from sklearn.model_selection import train_test_split
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=1)
# Here above test_size = 0.2 represents that we provided 20% of the data as test data.
from sklearn.neighbors import KNeighborsClassifier
from sklearn import metrics
classifier_knn = KNeighborsClassifier(n_neighbors=3)
classifier_knn.fit(X_train, y_train)
y_pred = classifier_knn.predict(X_test)
# Providing sample data and the model will make prediction out of that data
sample = [[5, 5, 3, 2], [2, 4, 3, 5]]
preds = classifier_knn.predict(sample)
pred_species = [iris.target_names[p] for p in preds] print("Predictions:", pred_species)

输出

Predictions: ['versicolor', 'virginica']

在使用CNTK时,每次训练模型时都需要随机化数据集的顺序,因为:

  • 深度学习算法受随机数生成器的强烈影响。

  • 我们在训练期间向神经网络提供样本的顺序极大地影响其性能。

使用留出数据集技术的主要缺点是它不可靠,因为有时我们会得到非常好的结果,但有时我们会得到糟糕的结果。

K折交叉验证

为了使我们的机器学习模型更可靠,有一种称为K折交叉验证的技术。本质上,K折交叉验证技术与之前的技术相同,但它会重复多次——通常约5到10次。下图表示其概念:

K-fold cross validation

K折交叉验证的工作原理

可以通过以下步骤了解K折交叉验证的工作原理:

**步骤 1** - 与留出数据集技术一样,在K折交叉验证技术中,首先需要将数据集分成训练集和测试集。理想情况下,比例为80-20,即80%的训练集和20%的测试集。

**步骤 2** - 接下来,我们需要使用训练集训练我们的模型。

**步骤 3** - 最后,我们将使用测试集来衡量模型的性能。留出数据集技术和k折交叉验证技术之间的唯一区别在于,上述过程通常会重复5到10次,最后计算所有性能指标的平均值。该平均值将是最终的性能指标。

让我们来看一个小型数据集的例子:

示例

from numpy import array
from sklearn.model_selection import KFold
data = array([0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1.0])
kfold = KFold(5, True, 1)
for train, test in kfold.split(data):
   print('train: %s, test: %s' % (data[train],(data[test]))

输出

train: [0.1 0.2 0.4 0.5 0.6 0.7 0.8 0.9], test: [0.3 1. ]
train: [0.1 0.2 0.3 0.4 0.6 0.8 0.9 1. ], test: [0.5 0.7]
train: [0.2 0.3 0.5 0.6 0.7 0.8 0.9 1. ], test: [0.1 0.4]
train: [0.1 0.3 0.4 0.5 0.6 0.7 0.9 1. ], test: [0.2 0.8]
train: [0.1 0.2 0.3 0.4 0.5 0.7 0.8 1. ], test: [0.6 0.9]

正如我们看到的,由于使用了更现实的训练和测试场景,k折交叉验证技术为我们提供了更稳定的性能衡量,但缺点是验证深度学习模型时需要花费大量时间。

CNTK不支持k折交叉验证,因此我们需要编写自己的脚本来实现。

检测欠拟合和过拟合

无论我们使用留出数据集还是k折交叉验证技术,我们都会发现指标的输出对于用于训练的数据集和用于验证的数据集将是不同的。

检测过拟合

所谓的过拟合现象是指我们的机器学习模型对训练数据建模非常出色,但在测试数据上表现不佳,即无法预测测试数据。

当机器学习模型学习训练数据中的特定模式和噪声到一定程度时,就会发生这种情况,这会对模型从训练数据泛化到新的(即未见过的)数据的能力产生负面影响。在这里,噪声是数据集中不相关的信噪比或随机性。

以下是我们可以用来检测我们的模型是否过拟合的两种方法:

  • 过拟合模型将在我们用于训练的相同样本上表现良好,但在新样本(即与训练样本不同的样本)上表现非常差。

  • 如果测试集上的指标低于我们在训练集上使用的相同指标,则模型在验证期间过拟合。

检测欠拟合

在我们的机器学习中可能出现的另一种情况是欠拟合。在这种情况下,我们的机器学习模型没有很好地对训练数据建模,并且无法预测有用的输出。当我们开始训练第一个时期时,我们的模型将欠拟合,但随着训练的进行,欠拟合程度会降低。

检测我们的模型是否欠拟合的一种方法是查看训练集和测试集的指标。如果测试集上的指标高于训练集上的指标,则我们的模型将欠拟合。

CNTK - 神经网络分类

在本章中,我们将学习如何使用CNTK对神经网络进行分类。

简介

分类可以定义为根据给定的输入数据预测类别输出标签或响应的过程。基于模型在训练阶段学习到的内容的分类输出可以采用“黑色”或“白色”或“垃圾邮件”或“非垃圾邮件”等形式。

另一方面,在数学上,它是逼近映射函数(例如**f**)的任务,该函数将输入变量(例如X)映射到输出变量(例如Y)。

分类问题的经典例子可以是电子邮件中的垃圾邮件检测。很明显,输出只有两个类别,“垃圾邮件”和“非垃圾邮件”。

为了实现这种分类,我们首先需要对分类器进行训练,其中“垃圾邮件”和“非垃圾邮件”电子邮件将用作训练数据。一旦分类器成功训练,就可以用来检测未知的电子邮件。

在这里,我们将使用虹膜花数据集创建一个4-5-3神经网络,该数据集具有以下特征:

  • 4个输入节点(每个预测值一个)。

  • 5个隐藏处理节点。

  • 3个输出节点(因为虹膜数据集中有三种可能的物种)。

加载数据集

我们将使用虹膜花数据集,我们希望根据萼片宽度和长度以及花瓣宽度和长度的物理特性对虹膜花的种类进行分类。该数据集描述了不同品种的虹膜花的物理特性:

  • 萼片长度

  • 萼片宽度

  • 花瓣长度

  • 花瓣宽度

  • 类别,即山鸢尾、杂色鸢尾或弗吉尼亚鸢尾

我们有**iris.CSV**文件,我们在之前的章节中也使用过它。它可以用**Pandas**库的帮助加载。但是,在我们使用它或将其加载到我们的分类器中之前,我们需要准备训练和测试文件,以便它可以轻松地与CNTK一起使用。

准备训练和测试文件

虹膜数据集是机器学习项目中最流行的数据集之一。它有150个数据项,原始数据如下所示:

5.1 3.5 1.4 0.2 setosa
4.9 3.0 1.4 0.2 setosa
…
7.0 3.2 4.7 1.4 versicolor
6.4 3.2 4.5 1.5 versicolor
…
6.3 3.3 6.0 2.5 virginica
5.8 2.7 5.1 1.9 virginica

如前所述,每行前四个值描述了不同品种的物理特性,即虹膜花的萼片长度、萼片宽度、花瓣长度、花瓣宽度。

但是,我们必须将数据转换为可以被CNTK轻松使用的格式,该格式是.ctf文件(我们在上一节中也创建了一个iris.ctf文件)。它将如下所示:

|attribs 5.1 3.5 1.4 0.2|species 1 0 0
|attribs 4.9 3.0 1.4 0.2|species 1 0 0
…
|attribs 7.0 3.2 4.7 1.4|species 0 1 0
|attribs 6.4 3.2 4.5 1.5|species 0 1 0
…
|attribs 6.3 3.3 6.0 2.5|species 0 0 1
|attribs 5.8 2.7 5.1 1.9|species 0 0 1

在上述数据中,|attribs标签标记特征值的开始,|species标签标记类标签值。我们也可以使用任何其他我们想要的标签名称,甚至可以添加项目ID。例如,请看以下数据:

|ID 001 |attribs 5.1 3.5 1.4 0.2|species 1 0 0 |#setosa
|ID 002 |attribs 4.9 3.0 1.4 0.2|species 1 0 0 |#setosa
…
|ID 051 |attribs 7.0 3.2 4.7 1.4|species 0 1 0 |#versicolor
|ID 052 |attribs 6.4 3.2 4.5 1.5|species 0 1 0 |#versicolor
…

虹膜数据集中共有150个数据项,对于此示例,我们将使用80-20留出数据集规则,即80%(120个项目)的数据项用于训练目的,其余20%(30个项目)的数据项用于测试目的。

构建分类模型

首先,我们需要处理CNTK格式的数据文件,为此我们将使用名为**create_reader**的辅助函数,如下所示:

def create_reader(path, input_dim, output_dim, rnd_order, sweeps):
x_strm = C.io.StreamDef(field='attribs', shape=input_dim, is_sparse=False)
y_strm = C.io.StreamDef(field='species', shape=output_dim, is_sparse=False)
streams = C.io.StreamDefs(x_src=x_strm, y_src=y_strm)
deserial = C.io.CTFDeserializer(path, streams)
mb_src = C.io.MinibatchSource(deserial, randomize=rnd_order, max_sweeps=sweeps)
return mb_src

现在,我们需要为我们的神经网络设置体系结构参数,并提供数据文件的位置。这可以使用以下python代码完成:

def main():
print("Using CNTK version = " + str(C.__version__) + "\n")
input_dim = 4
hidden_dim = 5
output_dim = 3
train_file = ".\\...\\" #provide the name of the training file(120 data items)
test_file = ".\\...\\" #provide the name of the test file(30 data items)

现在,借助以下代码行,我们的程序将创建未经训练的神经网络:

X = C.ops.input_variable(input_dim, np.float32)
Y = C.ops.input_variable(output_dim, np.float32)
with C.layers.default_options(init=C.initializer.uniform(scale=0.01, seed=1)):
hLayer = C.layers.Dense(hidden_dim, activation=C.ops.tanh, name='hidLayer')(X)
oLayer = C.layers.Dense(output_dim, activation=None, name='outLayer')(hLayer)
nnet = oLayer
model = C.ops.softmax(nnet)

现在,一旦我们创建了未经训练的双重模型,我们需要设置一个Learner算法对象,然后使用它来创建一个Trainer训练对象。我们将使用SGD学习器和**cross_entropy_with_softmax**损失函数:

tr_loss = C.cross_entropy_with_softmax(nnet, Y)
tr_clas = C.classification_error(nnet, Y)
max_iter = 2000
batch_size = 10
learn_rate = 0.01
learner = C.sgd(nnet.parameters, learn_rate)
trainer = C.Trainer(nnet, (tr_loss, tr_clas), [learner])

编写学习算法如下:

max_iter = 2000
batch_size = 10
lr_schedule = C.learning_parameter_schedule_per_sample([(1000, 0.05), (1, 0.01)])
mom_sch = C.momentum_schedule([(100, 0.99), (0, 0.95)], batch_size)
learner = C.fsadagrad(nnet.parameters, lr=lr_schedule, momentum=mom_sch)
trainer = C.Trainer(nnet, (tr_loss, tr_clas), [learner])

现在,一旦我们完成了Trainer对象,我们需要创建一个reader函数来读取训练数据:

rdr = create_reader(train_file, input_dim, output_dim, rnd_order=True, sweeps=C.io.INFINITELY_REPEAT)
iris_input_map = { X : rdr.streams.x_src, Y : rdr.streams.y_src }

现在是训练我们的神经网络模型的时候了:

for i in range(0, max_iter):
curr_batch = rdr.next_minibatch(batch_size, input_map=iris_input_map) trainer.train_minibatch(curr_batch)
if i % 500 == 0:
mcee = trainer.previous_minibatch_loss_average
macc = (1.0 - trainer.previous_minibatch_evaluation_average) * 100
print("batch %4d: mean loss = %0.4f, accuracy = %0.2f%% " \ % (i, mcee, macc))

一旦我们完成了训练,让我们使用测试数据项评估模型:

print("\nEvaluating test data \n")
rdr = create_reader(test_file, input_dim, output_dim, rnd_order=False, sweeps=1)
iris_input_map = { X : rdr.streams.x_src, Y : rdr.streams.y_src }
num_test = 30
all_test = rdr.next_minibatch(num_test, input_map=iris_input_map) acc = (1.0 - trainer.test_minibatch(all_test)) * 100
print("Classification accuracy = %0.2f%%" % acc)

在评估我们训练好的神经网络模型的准确性之后,我们将使用它对未见数据进行预测:

np.set_printoptions(precision = 1, suppress=True)
unknown = np.array([[6.4, 3.2, 4.5, 1.5]], dtype=np.float32)
print("\nPredicting Iris species for input features: ")
print(unknown[0]) pred_prob = model.eval(unknown)
np.set_printoptions(precision = 4, suppress=True)
print("Prediction probabilities are: ")
print(pred_prob[0])

完整的分类模型

Import numpy as np
Import cntk as C
def create_reader(path, input_dim, output_dim, rnd_order, sweeps):
x_strm = C.io.StreamDef(field='attribs', shape=input_dim, is_sparse=False)
y_strm = C.io.StreamDef(field='species', shape=output_dim, is_sparse=False)
streams = C.io.StreamDefs(x_src=x_strm, y_src=y_strm)
deserial = C.io.CTFDeserializer(path, streams)
mb_src = C.io.MinibatchSource(deserial, randomize=rnd_order, max_sweeps=sweeps)
return mb_src
def main():
print("Using CNTK version = " + str(C.__version__) + "\n")
input_dim = 4
hidden_dim = 5
output_dim = 3
train_file = ".\\...\\" #provide the name of the training file(120 data items)
test_file = ".\\...\\" #provide the name of the test file(30 data items)
X = C.ops.input_variable(input_dim, np.float32)
Y = C.ops.input_variable(output_dim, np.float32)
with C.layers.default_options(init=C.initializer.uniform(scale=0.01, seed=1)):
hLayer = C.layers.Dense(hidden_dim, activation=C.ops.tanh, name='hidLayer')(X)
oLayer = C.layers.Dense(output_dim, activation=None, name='outLayer')(hLayer)
nnet = oLayer
model = C.ops.softmax(nnet)
tr_loss = C.cross_entropy_with_softmax(nnet, Y)
tr_clas = C.classification_error(nnet, Y)
max_iter = 2000
batch_size = 10
learn_rate = 0.01
learner = C.sgd(nnet.parameters, learn_rate)
trainer = C.Trainer(nnet, (tr_loss, tr_clas), [learner])
max_iter = 2000
batch_size = 10
lr_schedule = C.learning_parameter_schedule_per_sample([(1000, 0.05), (1, 0.01)])
mom_sch = C.momentum_schedule([(100, 0.99), (0, 0.95)], batch_size)
learner = C.fsadagrad(nnet.parameters, lr=lr_schedule, momentum=mom_sch)
trainer = C.Trainer(nnet, (tr_loss, tr_clas), [learner])
rdr = create_reader(train_file, input_dim, output_dim, rnd_order=True, sweeps=C.io.INFINITELY_REPEAT)
iris_input_map = { X : rdr.streams.x_src, Y : rdr.streams.y_src }
for i in range(0, max_iter):
curr_batch = rdr.next_minibatch(batch_size, input_map=iris_input_map) trainer.train_minibatch(curr_batch)
if i % 500 == 0:
mcee = trainer.previous_minibatch_loss_average
macc = (1.0 - trainer.previous_minibatch_evaluation_average) * 100
print("batch %4d: mean loss = %0.4f, accuracy = %0.2f%% " \ % (i, mcee, macc))
print("\nEvaluating test data \n")
rdr = create_reader(test_file, input_dim, output_dim, rnd_order=False, sweeps=1)
iris_input_map = { X : rdr.streams.x_src, Y : rdr.streams.y_src }
num_test = 30
all_test = rdr.next_minibatch(num_test, input_map=iris_input_map) acc = (1.0 - trainer.test_minibatch(all_test)) * 100
print("Classification accuracy = %0.2f%%" % acc)
np.set_printoptions(precision = 1, suppress=True)
unknown = np.array([[7.0, 3.2, 4.7, 1.4]], dtype=np.float32)
print("\nPredicting species for input features: ")
print(unknown[0])
pred_prob = model.eval(unknown)
np.set_printoptions(precision = 4, suppress=True)
print("Prediction probabilities: ")
print(pred_prob[0])
if __name__== ”__main__”:
main()

输出

Using CNTK version = 2.7
batch 0: mean loss = 1.0986, mean accuracy = 40.00%
batch 500: mean loss = 0.6677, mean accuracy = 80.00%
batch 1000: mean loss = 0.5332, mean accuracy = 70.00%
batch 1500: mean loss = 0.2408, mean accuracy = 100.00%
Evaluating test data
Classification accuracy = 94.58%
Predicting species for input features:
[7.0 3.2 4.7 1.4]
Prediction probabilities:
[0.0847 0.736 0.113]

保存训练好的模型

这个 Iris 数据集只有 150 个数据项,因此训练 NN 分类器模型只需要几秒钟,但是在一个拥有数百或数千个数据项的大型数据集上训练可能需要数小时甚至数天。

我们可以保存我们的模型,这样我们就不用从头开始重新训练了。借助以下 Python 代码,我们可以保存我们训练好的 NN:

nn_classifier = “.\\neuralclassifier.model” #provide the name of the file
model.save(nn_classifier, format=C.ModelFormat.CNTKv2)

以下是上面使用的 save() 函数的参数:

  • 文件名是 save() 函数的第一个参数。它也可以与文件路径一起写入。

  • 另一个参数是 format 参数,其默认值为 C.ModelFormat.CNTKv2

加载训练好的模型

保存训练好的模型后,加载该模型非常容易。我们只需要使用 load() 函数。让我们在下面的例子中检查一下:

import numpy as np
import cntk as C
model = C.ops.functions.Function.load(“.\\neuralclassifier.model”)
np.set_printoptions(precision = 1, suppress=True)
unknown = np.array([[7.0, 3.2, 4.7, 1.4]], dtype=np.float32)
print("\nPredicting species for input features: ")
print(unknown[0])
pred_prob = model.eval(unknown)
np.set_printoptions(precision = 4, suppress=True)
print("Prediction probabilities: ")
print(pred_prob[0])

保存模型的好处是,一旦加载了保存的模型,就可以像刚刚训练好的模型一样使用。

CNTK - 神经网络二元分类

本章让我们了解一下如何使用 CNTK 进行神经网络二元分类。

使用 NN 进行二元分类类似于多类分类,唯一不同的是只有两个输出节点,而不是三个或更多。在这里,我们将使用两种技术(单节点和双节点技术)使用神经网络进行二元分类。单节点技术比双节点技术更常见。

加载数据集

为了使用 NN 实现这两种技术,我们将使用钞票数据集。该数据集可以从 UCI 机器学习资源库下载,网址为 https://archive.ics.uci.edu/ml/datasets/banknote+authentication

在我们的示例中,我们将使用 50 个真钞数据项(伪造类 = 0),和前 50 个假钞数据项(伪造类 = 1)。

准备训练和测试文件

完整的数据集有 1372 个数据项。原始数据集如下所示:

3.6216, 8.6661, -2.8076, -0.44699, 0
4.5459, 8.1674, -2.4586, -1.4621, 0
…
-1.3971, 3.3191, -1.3927, -1.9948, 1
0.39012, -0.14279, -0.031994, 0.35084, 1

现在,首先我们需要将此原始数据转换为双节点 CNTK 格式,如下所示:

|stats 3.62160000 8.66610000 -2.80730000 -0.44699000 |forgery 0 1 |# authentic 
|stats 4.54590000 8.16740000 -2.45860000 -1.46210000 |forgery 0 1 |# authentic 
. . .
|stats -1.39710000 3.31910000 -1.39270000 -1.99480000 |forgery 1 0 |# fake 
|stats 0.39012000 -0.14279000 -0.03199400 0.35084000 |forgery 1 0 |# fake

您可以使用以下 Python 程序从原始数据创建 CNTK 格式的数据:

fin = open(".\\...", "r") #provide the location of saved dataset text file.
for line in fin:
   line = line.strip()
   tokens = line.split(",")
   if tokens[4] == "0":
    print("|stats %12.8f %12.8f %12.8f %12.8f |forgery 0 1 |# authentic" % \
(float(tokens[0]), float(tokens[1]), float(tokens[2]), float(tokens[3])) )
   else:
    print("|stats %12.8f %12.8f %12.8f %12.8f |forgery 1 0 |# fake" % \
(float(tokens[0]), float(tokens[1]), float(tokens[2]), float(tokens[3])) )
fin.close()

双节点二元分类模型

双节点分类和多类分类之间几乎没有区别。在这里,我们首先需要处理 CNTK 格式的数据文件,为此我们将使用名为 create_reader 的辅助函数,如下所示:

def create_reader(path, input_dim, output_dim, rnd_order, sweeps):
x_strm = C.io.StreamDef(field='stats', shape=input_dim, is_sparse=False)
y_strm = C.io.StreamDef(field='forgery', shape=output_dim, is_sparse=False)
streams = C.io.StreamDefs(x_src=x_strm, y_src=y_strm)
deserial = C.io.CTFDeserializer(path, streams)
mb_src = C.io.MinibatchSource(deserial, randomize=rnd_order, max_sweeps=sweeps)
return mb_src

现在,我们需要为我们的神经网络设置体系结构参数,并提供数据文件的位置。这可以使用以下python代码完成:

def main():
print("Using CNTK version = " + str(C.__version__) + "\n")
input_dim = 4
hidden_dim = 10
output_dim = 2
train_file = ".\\...\\" #provide the name of the training file
test_file = ".\\...\\" #provide the name of the test file

现在,借助以下代码行,我们的程序将创建未经训练的神经网络:

X = C.ops.input_variable(input_dim, np.float32)
Y = C.ops.input_variable(output_dim, np.float32)
with C.layers.default_options(init=C.initializer.uniform(scale=0.01, seed=1)):
hLayer = C.layers.Dense(hidden_dim, activation=C.ops.tanh, name='hidLayer')(X)
oLayer = C.layers.Dense(output_dim, activation=None, name='outLayer')(hLayer)
nnet = oLayer
model = C.ops.softmax(nnet)

现在,一旦我们创建了未经训练的双节点模型,我们需要设置一个 Learner 算法对象,然后使用它来创建一个 Trainer 训练对象。我们将使用 SGD 学习器和 cross_entropy_with_softmax 损失函数:

tr_loss = C.cross_entropy_with_softmax(nnet, Y)
tr_clas = C.classification_error(nnet, Y)
max_iter = 500
batch_size = 10
learn_rate = 0.01
learner = C.sgd(nnet.parameters, learn_rate)
trainer = C.Trainer(nnet, (tr_loss, tr_clas), [learner])

现在,一旦我们完成了 Trainer 对象,我们需要创建一个 reader 函数来读取训练数据:

rdr = create_reader(train_file, input_dim, output_dim, rnd_order=True, sweeps=C.io.INFINITELY_REPEAT)
banknote_input_map = { X : rdr.streams.x_src, Y : rdr.streams.y_src }

现在,是时候训练我们的 NN 模型了:

for i in range(0, max_iter):
curr_batch = rdr.next_minibatch(batch_size, input_map=iris_input_map) trainer.train_minibatch(curr_batch)
if i % 500 == 0:
mcee = trainer.previous_minibatch_loss_average
macc = (1.0 - trainer.previous_minibatch_evaluation_average) * 100
print("batch %4d: mean loss = %0.4f, accuracy = %0.2f%% " \ % (i, mcee, macc))

训练完成后,让我们使用测试数据项评估模型:

print("\nEvaluating test data \n")
rdr = create_reader(test_file, input_dim, output_dim, rnd_order=False, sweeps=1)
banknote_input_map = { X : rdr.streams.x_src, Y : rdr.streams.y_src }
num_test = 20
all_test = rdr.next_minibatch(num_test, input_map=iris_input_map) acc = (1.0 - trainer.test_minibatch(all_test)) * 100
print("Classification accuracy = %0.2f%%" % acc)

在评估我们训练好的神经网络模型的准确性之后,我们将使用它对未见数据进行预测:

np.set_printoptions(precision = 1, suppress=True)
unknown = np.array([[0.6, 1.9, -3.3, -0.3]], dtype=np.float32)
print("\nPredicting Banknote authenticity for input features: ")
print(unknown[0])
pred_prob = model.eval(unknown)
np.set_printoptions(precision = 4, suppress=True)
print("Prediction probabilities are: ")
print(pred_prob[0])
if pred_prob[0,0] < pred_prob[0,1]:
  print(“Prediction: authentic”)
else:
  print(“Prediction: fake”)

完整的双节点分类模型

def create_reader(path, input_dim, output_dim, rnd_order, sweeps):
x_strm = C.io.StreamDef(field='stats', shape=input_dim, is_sparse=False)
y_strm = C.io.StreamDef(field='forgery', shape=output_dim, is_sparse=False)
streams = C.io.StreamDefs(x_src=x_strm, y_src=y_strm)
deserial = C.io.CTFDeserializer(path, streams)
mb_src = C.io.MinibatchSource(deserial, randomize=rnd_order, max_sweeps=sweeps)
return mb_src
def main():
print("Using CNTK version = " + str(C.__version__) + "\n")
input_dim = 4
hidden_dim = 10
output_dim = 2
train_file = ".\\...\\" #provide the name of the training file
test_file = ".\\...\\" #provide the name of the test file
X = C.ops.input_variable(input_dim, np.float32)
Y = C.ops.input_variable(output_dim, np.float32)
withC.layers.default_options(init=C.initializer.uniform(scale=0.01, seed=1)):
hLayer = C.layers.Dense(hidden_dim, activation=C.ops.tanh, name='hidLayer')(X)
oLayer = C.layers.Dense(output_dim, activation=None, name='outLayer')(hLayer)
nnet = oLayer
model = C.ops.softmax(nnet)
tr_loss = C.cross_entropy_with_softmax(nnet, Y)
tr_clas = C.classification_error(nnet, Y)
max_iter = 500
batch_size = 10
learn_rate = 0.01
learner = C.sgd(nnet.parameters, learn_rate)
trainer = C.Trainer(nnet, (tr_loss, tr_clas), [learner])
rdr = create_reader(train_file, input_dim, output_dim, rnd_order=True, sweeps=C.io.INFINITELY_REPEAT)
banknote_input_map = { X : rdr.streams.x_src, Y : rdr.streams.y_src }
for i in range(0, max_iter):
curr_batch = rdr.next_minibatch(batch_size, input_map=iris_input_map) trainer.train_minibatch(curr_batch)
if i % 500 == 0:
mcee = trainer.previous_minibatch_loss_average
macc = (1.0 - trainer.previous_minibatch_evaluation_average) * 100
print("batch %4d: mean loss = %0.4f, accuracy = %0.2f%% " \ % (i, mcee, macc))
print("\nEvaluating test data \n")
rdr = create_reader(test_file, input_dim, output_dim, rnd_order=False, sweeps=1)
banknote_input_map = { X : rdr.streams.x_src, Y : rdr.streams.y_src }
num_test = 20
all_test = rdr.next_minibatch(num_test, input_map=iris_input_map) acc = (1.0 - trainer.test_minibatch(all_test)) * 100
print("Classification accuracy = %0.2f%%" % acc)
np.set_printoptions(precision = 1, suppress=True)
unknown = np.array([[0.6, 1.9, -3.3, -0.3]], dtype=np.float32)
print("\nPredicting Banknote authenticity for input features: ")
print(unknown[0])
pred_prob = model.eval(unknown)
np.set_printoptions(precision = 4, suppress=True)
print("Prediction probabilities are: ")
print(pred_prob[0])
if pred_prob[0,0] < pred_prob[0,1]:
print(“Prediction: authentic”)
else:
print(“Prediction: fake”)
if __name__== ”__main__”:
main()

输出

Using CNTK version = 2.7
batch 0: mean loss = 0.6928, accuracy = 80.00%
batch 50: mean loss = 0.6877, accuracy = 70.00%
batch 100: mean loss = 0.6432, accuracy = 80.00%
batch 150: mean loss = 0.4978, accuracy = 80.00%
batch 200: mean loss = 0.4551, accuracy = 90.00%
batch 250: mean loss = 0.3755, accuracy = 90.00%
batch 300: mean loss = 0.2295, accuracy = 100.00%
batch 350: mean loss = 0.1542, accuracy = 100.00%
batch 400: mean loss = 0.1581, accuracy = 100.00%
batch 450: mean loss = 0.1499, accuracy = 100.00%
Evaluating test data
Classification accuracy = 84.58%
Predicting banknote authenticity for input features:
[0.6 1.9 -3.3 -0.3]
Prediction probabilities are:
[0.7847 0.2536]
Prediction: fake

单节点二元分类模型

实现程序与我们上面针对双节点分类所做的几乎一样。主要区别在于使用双节点分类技术时。

我们可以使用 CNTK 内置的 classification_error() 函数,但在单节点分类中,CNTK 不支持 classification_error() 函数。这就是我们需要实现一个程序定义的函数的原因,如下所示:

def class_acc(mb, x_var, y_var, model):
num_correct = 0; num_wrong = 0
x_mat = mb[x_var].asarray()
y_mat = mb[y_var].asarray()
for i in range(mb[x_var].shape[0]):
   p = model.eval(x_mat[i]
   y = y_mat[i]
   if p[0,0] < 0.5 and y[0,0] == 0.0 or p[0,0] >= 0.5 and y[0,0] == 1.0:
num_correct += 1
 else:
  num_wrong += 1
return (num_correct * 100.0)/(num_correct + num_wrong)

有了这个改变,让我们看看完整的单节点分类示例:

完整的单节点分类模型

import numpy as np
import cntk as C
def create_reader(path, input_dim, output_dim, rnd_order, sweeps):
x_strm = C.io.StreamDef(field='stats', shape=input_dim, is_sparse=False)
y_strm = C.io.StreamDef(field='forgery', shape=output_dim, is_sparse=False)
streams = C.io.StreamDefs(x_src=x_strm, y_src=y_strm)
deserial = C.io.CTFDeserializer(path, streams)
mb_src = C.io.MinibatchSource(deserial, randomize=rnd_order, max_sweeps=sweeps)
return mb_src
def class_acc(mb, x_var, y_var, model):
num_correct = 0; num_wrong = 0
x_mat = mb[x_var].asarray()
y_mat = mb[y_var].asarray()
for i in range(mb[x_var].shape[0]):
  p = model.eval(x_mat[i]
  y = y_mat[i]
  if p[0,0] < 0.5 and y[0,0] == 0.0 or p[0,0] >= 0.5 and y[0,0] == 1.0:
  num_correct += 1
 else:
  num_wrong += 1
return (num_correct * 100.0)/(num_correct + num_wrong)
def main():
print("Using CNTK version = " + str(C.__version__) + "\n")
input_dim = 4
hidden_dim = 10
output_dim = 1
train_file = ".\\...\\" #provide the name of the training file
test_file = ".\\...\\" #provide the name of the test file
X = C.ops.input_variable(input_dim, np.float32)
Y = C.ops.input_variable(output_dim, np.float32)
with C.layers.default_options(init=C.initializer.uniform(scale=0.01, seed=1)):
hLayer = C.layers.Dense(hidden_dim, activation=C.ops.tanh, name='hidLayer')(X)
oLayer = C.layers.Dense(output_dim, activation=None, name='outLayer')(hLayer)
model = oLayer
tr_loss = C.cross_entropy_with_softmax(model, Y)
max_iter = 1000
batch_size = 10
learn_rate = 0.01
learner = C.sgd(model.parameters, learn_rate)
trainer = C.Trainer(model, (tr_loss), [learner])
rdr = create_reader(train_file, input_dim, output_dim, rnd_order=True, sweeps=C.io.INFINITELY_REPEAT)
banknote_input_map = {X : rdr.streams.x_src, Y : rdr.streams.y_src }
for i in range(0, max_iter):
curr_batch = rdr.next_minibatch(batch_size, input_map=iris_input_map) trainer.train_minibatch(curr_batch)
if i % 100 == 0:
mcee=trainer.previous_minibatch_loss_average
ca = class_acc(curr_batch, X,Y, model)
print("batch %4d: mean loss = %0.4f, accuracy = %0.2f%% " \ % (i, mcee, ca))
print("\nEvaluating test data \n")
rdr = create_reader(test_file, input_dim, output_dim, rnd_order=False, sweeps=1)
banknote_input_map = { X : rdr.streams.x_src, Y : rdr.streams.y_src }
num_test = 20
all_test = rdr.next_minibatch(num_test, input_map=iris_input_map)
acc = class_acc(all_test, X,Y, model)
print("Classification accuracy = %0.2f%%" % acc)
np.set_printoptions(precision = 1, suppress=True)
unknown = np.array([[0.6, 1.9, -3.3, -0.3]], dtype=np.float32)
print("\nPredicting Banknote authenticity for input features: ")
print(unknown[0])
pred_prob = model.eval({X:unknown})
print("Prediction probability: ")
print(“%0.4f” % pred_prob[0,0])
if pred_prob[0,0] < 0.5:
  print(“Prediction: authentic”)
else:
  print(“Prediction: fake”)
if __name__== ”__main__”:
   main()

输出

Using CNTK version = 2.7
batch 0: mean loss = 0.6936, accuracy = 10.00%
batch 100: mean loss = 0.6882, accuracy = 70.00%
batch 200: mean loss = 0.6597, accuracy = 50.00%
batch 300: mean loss = 0.5298, accuracy = 70.00%
batch 400: mean loss = 0.4090, accuracy = 100.00%
batch 500: mean loss = 0.3790, accuracy = 90.00%
batch 600: mean loss = 0.1852, accuracy = 100.00%
batch 700: mean loss = 0.1135, accuracy = 100.00%
batch 800: mean loss = 0.1285, accuracy = 100.00%
batch 900: mean loss = 0.1054, accuracy = 100.00%
Evaluating test data
Classification accuracy = 84.00%
Predicting banknote authenticity for input features:
[0.6 1.9 -3.3 -0.3]
Prediction probability:
0.8846
Prediction: fake

CNTK - 神经网络回归

本章将帮助您了解关于 CNTK 的神经网络回归。

简介

众所周知,为了从一个或多个预测变量中预测数值,我们使用回归。让我们以预测某个 100 个城镇中的一个城镇的房屋中位数为例。为此,我们有以下数据:

  • 每个城镇的犯罪统计数据。

  • 每个城镇房屋的年龄。

  • 衡量每个城镇到黄金地段的距离。

  • 每个城镇的学生与教师比例。

  • 每个城镇的种族人口统计数据。

  • 每个城镇的房屋中位数。

基于这五个预测变量,我们想预测房屋中位数。为此,我们可以创建一个类似于以下的线性回归模型:

Y = a0+a1(crime)+a2(house-age)+(a3)(distance)+(a4)(ratio)+(a5)(racial)

在上式中:

Y 是预测的中位数

a0 是一个常数,并且

a1 到 a5 都是与我们上面讨论的五个预测变量相关的常数。

我们还可以使用神经网络作为替代方法。它将创建一个更准确的预测模型。

在这里,我们将使用 CNTK 创建一个神经网络回归模型。

加载数据集

为了使用 CNTK 实现神经网络回归,我们将使用波士顿地区房屋价值数据集。该数据集可以从 UCI 机器学习资源库下载,网址为 https://archive.ics.uci.edu/。该数据集共有 14 个变量和 506 个实例。

但是,在我们的实现程序中,我们将使用 14 个变量中的 6 个和 100 个实例。在 6 个变量中,5 个作为预测变量,1 个作为要预测的值。在 100 个实例中,我们将使用 80 个用于训练,20 个用于测试。我们想要预测的值是城镇的房屋中位数。让我们看看我们将使用的五个预测变量:

  • 城镇的人均犯罪率 - 我们预计较小的值与该预测变量相关。

  • 自住单元的比例 - 建于 1940 年之前的房屋 - 我们预计较小的值与该预测变量相关,因为较大的值意味着更老的房屋。

  • 到波士顿五个就业中心的加权距离。

  • 地区学校学生与教师比例。

  • 城镇黑人居民比例的间接指标。

准备训练和测试文件

和之前一样,我们首先需要将原始数据转换为 CNTK 格式。我们将使用前 80 个数据项用于训练,因此制表符分隔的 CNTK 格式如下:

|predictors 1.612820 96.90 3.76 21.00 248.31 |medval 13.50
|predictors 0.064170 68.20 3.36 19.20 396.90 |medval 18.90
|predictors 0.097440 61.40 3.38 19.20 377.56 |medval 20.00
. . .

接下来的 20 个项目也转换为 CNTK 格式,将用于测试。

构建回归模型

首先,我们需要处理 CNTK 格式的数据文件,为此,我们将使用名为 create_reader 的辅助函数,如下所示:

def create_reader(path, input_dim, output_dim, rnd_order, sweeps):
x_strm = C.io.StreamDef(field='predictors', shape=input_dim, is_sparse=False)
y_strm = C.io.StreamDef(field='medval', shape=output_dim, is_sparse=False)
streams = C.io.StreamDefs(x_src=x_strm, y_src=y_strm)
deserial = C.io.CTFDeserializer(path, streams)
mb_src = C.io.MinibatchSource(deserial, randomize=rnd_order, max_sweeps=sweeps)
return mb_src

接下来,我们需要创建一个辅助函数,该函数接受一个 CNTK 小批量对象并计算自定义准确性指标。

def mb_accuracy(mb, x_var, y_var, model, delta):
   num_correct = 0
   num_wrong = 0
   x_mat = mb[x_var].asarray()
   y_mat = mb[y_var].asarray()
for i in range(mb[x_var].shape[0]):
  v = model.eval(x_mat[i])
  y = y_mat[i]
if np.abs(v[0,0] – y[0,0]) < delta:
   num_correct += 1
else:
   num_wrong += 1
return (num_correct * 100.0)/(num_correct + num_wrong)

现在,我们需要为我们的神经网络设置体系结构参数,并提供数据文件的位置。这可以使用以下python代码完成:

def main():
print("Using CNTK version = " + str(C.__version__) + "\n")
input_dim = 5
hidden_dim = 20
output_dim = 1
train_file = ".\\...\\" #provide the name of the training file(80 data items)
test_file = ".\\...\\" #provide the name of the test file(20 data items)

现在,借助以下代码行,我们的程序将创建未经训练的神经网络:

X = C.ops.input_variable(input_dim, np.float32)
Y = C.ops.input_variable(output_dim, np.float32)
with C.layers.default_options(init=C.initializer.uniform(scale=0.01, seed=1)):
hLayer = C.layers.Dense(hidden_dim, activation=C.ops.tanh, name='hidLayer')(X)
oLayer = C.layers.Dense(output_dim, activation=None, name='outLayer')(hLayer)
model = C.ops.alias(oLayer)

现在,一旦我们创建了未经训练的双节点模型,我们需要设置一个 Learner 算法对象。我们将使用 SGD 学习器和 squared_error 损失函数:

tr_loss = C.squared_error(model, Y)
max_iter = 3000
batch_size = 5
base_learn_rate = 0.02
sch=C.learning_parameter_schedule([base_learn_rate, base_learn_rate/2], minibatch_size=batch_size, epoch_size=int((max_iter*batch_size)/2))
learner = C.sgd(model.parameters, sch)
trainer = C.Trainer(model, (tr_loss), [learner])

现在,一旦我们完成了学习算法对象,我们需要创建一个 reader 函数来读取训练数据:

rdr = create_reader(train_file, input_dim, output_dim, rnd_order=True, sweeps=C.io.INFINITELY_REPEAT)
boston_input_map = { X : rdr.streams.x_src, Y : rdr.streams.y_src }

现在,是时候训练我们的 NN 模型了:

for i in range(0, max_iter):
curr_batch = rdr.next_minibatch(batch_size, input_map=boston_input_map) trainer.train_minibatch(curr_batch)
if i % int(max_iter/10) == 0:
mcee = trainer.previous_minibatch_loss_average
acc = mb_accuracy(curr_batch, X, Y, model, delta=3.00)
print("batch %4d: mean squared error = %8.4f, accuracy = %5.2f%% " \ % (i, mcee, acc))

训练完成后,让我们使用测试数据项评估模型:

print("\nEvaluating test data \n")
rdr = create_reader(test_file, input_dim, output_dim, rnd_order=False, sweeps=1)
boston_input_map = { X : rdr.streams.x_src, Y : rdr.streams.y_src }
num_test = 20
all_test = rdr.next_minibatch(num_test, input_map=boston_input_map)
acc = mb_accuracy(all_test, X, Y, model, delta=3.00)
print("Prediction accuracy = %0.2f%%" % acc)

在评估我们训练好的神经网络模型的准确性之后,我们将使用它对未见数据进行预测:

np.set_printoptions(precision = 2, suppress=True)
unknown = np.array([[0.09, 50.00, 4.5, 17.00, 350.00], dtype=np.float32)
print("\nPredicting median home value for feature/predictor values: ")
print(unknown[0])
pred_prob = model.eval({X: unknown)
print("\nPredicted value is: ")
print(“$%0.2f (x1000)” %pred_value[0,0])

完整的回归模型

import numpy as np
import cntk as C
def create_reader(path, input_dim, output_dim, rnd_order, sweeps):
x_strm = C.io.StreamDef(field='predictors', shape=input_dim, is_sparse=False)
y_strm = C.io.StreamDef(field='medval', shape=output_dim, is_sparse=False)
streams = C.io.StreamDefs(x_src=x_strm, y_src=y_strm)
deserial = C.io.CTFDeserializer(path, streams)
mb_src = C.io.MinibatchSource(deserial, randomize=rnd_order, max_sweeps=sweeps)
return mb_src
def mb_accuracy(mb, x_var, y_var, model, delta):
num_correct = 0
num_wrong = 0
x_mat = mb[x_var].asarray()
y_mat = mb[y_var].asarray()
for i in range(mb[x_var].shape[0]):
   v = model.eval(x_mat[i])
   y = y_mat[i]
if np.abs(v[0,0] – y[0,0]) < delta:
   num_correct += 1
else:
   num_wrong += 1
return (num_correct * 100.0)/(num_correct + num_wrong)
def main():
print("Using CNTK version = " + str(C.__version__) + "\n")
input_dim = 5
hidden_dim = 20
output_dim = 1
train_file = ".\\...\\" #provide the name of the training file(80 data items)
test_file = ".\\...\\" #provide the name of the test file(20 data items)
X = C.ops.input_variable(input_dim, np.float32)
Y = C.ops.input_variable(output_dim, np.float32)
with C.layers.default_options(init=C.initializer.uniform(scale=0.01, seed=1)):
hLayer = C.layers.Dense(hidden_dim, activation=C.ops.tanh, name='hidLayer')(X)
oLayer = C.layers.Dense(output_dim, activation=None, name='outLayer')(hLayer)
model = C.ops.alias(oLayer)
tr_loss = C.squared_error(model, Y)
max_iter = 3000
batch_size = 5
base_learn_rate = 0.02
sch = C.learning_parameter_schedule([base_learn_rate, base_learn_rate/2], minibatch_size=batch_size, epoch_size=int((max_iter*batch_size)/2))
learner = C.sgd(model.parameters, sch)
trainer = C.Trainer(model, (tr_loss), [learner])
rdr = create_reader(train_file, input_dim, output_dim, rnd_order=True, sweeps=C.io.INFINITELY_REPEAT)
boston_input_map = { X : rdr.streams.x_src, Y : rdr.streams.y_src }
for i in range(0, max_iter):
curr_batch = rdr.next_minibatch(batch_size, input_map=boston_input_map) trainer.train_minibatch(curr_batch)
if i % int(max_iter/10) == 0:
   mcee = trainer.previous_minibatch_loss_average
   acc = mb_accuracy(curr_batch, X, Y, model, delta=3.00)
   print("batch %4d: mean squared error = %8.4f, accuracy = %5.2f%% " \ % (i, mcee, acc))
   print("\nEvaluating test data \n")
   rdr = create_reader(test_file, input_dim, output_dim, rnd_order=False, sweeps=1)
   boston_input_map = { X : rdr.streams.x_src, Y : rdr.streams.y_src }
   num_test = 20
all_test = rdr.next_minibatch(num_test, input_map=boston_input_map)
acc = mb_accuracy(all_test, X, Y, model, delta=3.00)
print("Prediction accuracy = %0.2f%%" % acc)
np.set_printoptions(precision = 2, suppress=True)
unknown = np.array([[0.09, 50.00, 4.5, 17.00, 350.00], dtype=np.float32)
print("\nPredicting median home value for feature/predictor values: ")
print(unknown[0])
pred_prob = model.eval({X: unknown)
print("\nPredicted value is: ")
print(“$%0.2f (x1000)” %pred_value[0,0])
if __name__== ”__main__”:
   main()

输出

Using CNTK version = 2.7
batch 0: mean squared error = 385.6727, accuracy = 0.00%
batch 300: mean squared error = 41.6229, accuracy = 20.00%
batch 600: mean squared error = 28.7667, accuracy = 40.00%
batch 900: mean squared error = 48.6435, accuracy = 40.00%
batch 1200: mean squared error = 77.9562, accuracy = 80.00%
batch 1500: mean squared error = 7.8342, accuracy = 60.00%
batch 1800: mean squared error = 47.7062, accuracy = 60.00%
batch 2100: mean squared error = 40.5068, accuracy = 40.00%
batch 2400: mean squared error = 46.5023, accuracy = 40.00%
batch 2700: mean squared error = 15.6235, accuracy = 60.00%
Evaluating test data
Prediction accuracy = 64.00%
Predicting median home value for feature/predictor values:
[0.09 50. 4.5 17. 350.]
Predicted value is:
$21.02(x1000)

保存训练好的模型

这个波士顿房屋价值数据集只有 506 个数据项(其中我们只使用了 100 个)。因此,训练 NN 回归模型只需要几秒钟,但是在一个拥有数百或数千个数据项的大型数据集上训练可能需要数小时甚至数天。

我们可以保存我们的模型,这样我们就不用从头开始重新训练了。借助以下 Python 代码,我们可以保存我们训练好的 NN:

nn_regressor = “.\\neuralregressor.model” #provide the name of the file
model.save(nn_regressor, format=C.ModelFormat.CNTKv2)

以下是上面使用的 save() 函数的参数:

  • 文件名是 save() 函数的第一个参数。它也可以与文件路径一起写入。

  • 另一个参数是 format 参数,其默认值为 C.ModelFormat.CNTKv2

加载训练好的模型

保存训练好的模型后,加载该模型非常容易。我们只需要使用 load() 函数。让我们在下面的例子中检查一下:

import numpy as np
import cntk as C
model = C.ops.functions.Function.load(“.\\neuralregressor.model”)
np.set_printoptions(precision = 2, suppress=True)
unknown = np.array([[0.09, 50.00, 4.5, 17.00, 350.00], dtype=np.float32)
print("\nPredicting area median home value for feature/predictor values: ")
print(unknown[0])
pred_prob = model.eval({X: unknown)
print("\nPredicted value is: ")
print(“$%0.2f (x1000)” %pred_value[0,0])

保存模型的好处是,一旦加载了保存的模型,就可以像刚刚训练好的模型一样使用。

CNTK - 分类模型

本章将帮助您了解如何在 CNTK 中衡量分类模型的性能。让我们从混淆矩阵开始。

混淆矩阵

混淆矩阵 - 一个包含预测输出与预期输出的表格是衡量分类问题性能最简单的方法,其中输出可以是两种或多种类型的类别。

为了理解它的工作原理,我们将为一个二元分类模型创建一个混淆矩阵,该模型预测信用卡交易是正常的还是欺诈的。它显示如下:

实际欺诈 实际正常

预测欺诈

真阳性

假阳性

预测正常

假阴性

真阴性

如我们所见,上面的示例混淆矩阵包含 2 列,一列用于欺诈类,另一列用于正常类。同样,我们也有 2 行,一行用于欺诈类,另一行用于正常类。以下是与混淆矩阵相关的术语的解释:

  • 真阳性 - 当数据点的实际类别和预测类别都为 1 时。

  • 真阴性 - 当数据点的实际类别和预测类别都为 0 时。

  • 假阳性 - 当数据点的实际类别为 0,而预测类别为 1 时。

  • 假阴性 - 当数据点的实际类别为 1,而预测类别为 0 时。

让我们看看如何从混淆矩阵中计算不同的事物:

  • 准确率 - 这是我们的 ML 分类模型做出的正确预测的数量。可以使用以下公式计算:

  • Accuracy
  • 精确率 - 它告诉我们,在我们预测的所有样本中,有多少样本被正确预测。可以使用以下公式计算:

  • Precision
  • 召回率或灵敏度 - 召回率是我们的 ML 分类模型返回的阳性数量。换句话说,它告诉我们数据集中有多少欺诈案例实际上被模型检测到。可以使用以下公式计算:

  • Recall
  • 特异性 - 与召回率相反,它给出我们的 ML 分类模型返回的阴性数量。可以使用以下公式计算:

  • specificity

F-测度

我们可以使用 F-测度作为混淆矩阵的替代方法。其主要原因是,我们不能同时最大化召回率和精确率。这两个指标之间存在非常强的关系,这可以通过以下示例来理解:

假设我们想使用深度学习 (DL) 模型将细胞样本分类为癌细胞或正常细胞。为了达到最高的精确度,我们需要将预测结果的数量减少到1。虽然这可以使我们达到大约100%的精确度,但召回率将会非常低。

另一方面,如果我们想要达到最高的召回率,我们需要尽可能多地进行预测。虽然这可以使我们达到大约100%的召回率,但精确度将会非常低。

在实践中,我们需要找到一种在精确度和召回率之间取得平衡的方法。F 值度量允许我们做到这一点,因为它表示精确度和召回率之间的调和平均值。

F-measure

这个公式称为F1 值,其中附加项B设置为1,以获得精确度和召回率的相等比率。为了强调召回率,我们可以将因子B设置为2。另一方面,为了强调精确度,我们可以将因子B设置为0.5。

使用CNTK衡量分类性能

在上一节中,我们使用鸢尾花数据集创建了一个分类模型。在这里,我们将使用混淆矩阵和F值度量来衡量其性能。

创建混淆矩阵

我们已经创建了模型,因此我们可以开始验证过程,其中包括**混淆矩阵**。首先,我们将借助于**scikit-learn**中的**confusion_matrix**函数来创建混淆矩阵。为此,我们需要测试样本的真实标签和相同测试样本的预测标签。

让我们使用以下Python代码计算混淆矩阵:

from sklearn.metrics import confusion_matrix
y_true = np.argmax(y_test, axis=1)
y_pred = np.argmax(z(X_test), axis=1)
matrix = confusion_matrix(y_true=y_true, y_pred=y_pred)
print(matrix)

输出

[[10 0 0]
[ 0 1 9]
[ 0 0 10]]

我们还可以使用热力图函数来可视化混淆矩阵,如下所示:

import seaborn as sns
import matplotlib.pyplot as plt
g = sns.heatmap(matrix,
     annot=True,
     xticklabels=label_encoder.classes_.tolist(),
     yticklabels=label_encoder.classes_.tolist(),
     cmap='Blues')
g.set_yticklabels(g.get_yticklabels(), rotation=0)
plt.show()
Iris

我们还应该有一个单一的性能指标,我们可以用它来比较模型。为此,我们需要使用CNTK中metrics包中的**classification_error**函数计算分类错误,就像创建分类模型时所做的那样。

现在,要计算分类错误,请使用包含数据集的损失函数执行测试方法。之后,CNTK将把我们为此函数提供的样本作为输入,并根据输入特征X_**test**进行预测。

loss.test([X_test, y_test])

输出

{'metric': 0.36666666666, 'samples': 30}

实现F值

为了实现F值,CNTK还包含一个名为fmeasures的函数。在训练神经网络时,我们可以使用此函数,将单元**cntk.metrics.classification_error**替换为**cntk.losses.fmeasure**,方法如下:

import cntk
@cntk.Function
def criterion_factory(output, target):
   loss = cntk.losses.cross_entropy_with_softmax(output, target)
metric = cntk.losses.fmeasure(output, target)
   return loss, metric

使用cntk.losses.fmeasure函数后,我们将得到**loss.test**方法调用的不同输出,如下所示:

loss.test([X_test, y_test])

输出

{'metric': 0.83101488749, 'samples': 30}

CNTK - 回归模型

在这里,我们将学习关于衡量回归模型性能的方法。

回归模型验证的基础

众所周知,回归模型不同于分类模型,因为对于单个样本没有对错的二元度量。在回归模型中,我们想要衡量预测值与实际值的接近程度。预测值越接近预期输出,模型的性能就越好。

在这里,我们将使用不同的误差率函数来衡量用于回归的神经网络的性能。

计算误差范围

如前所述,在验证回归模型时,我们不能说预测是对还是错。我们希望我们的预测尽可能接近真实值。但是,这里可以接受小的误差范围。

计算误差范围的公式如下:

Error Margin

这里:

**预测值** = 用帽子表示的y

**真实值** = 预测的y

首先,我们需要计算预测值和真实值之间的距离。然后,为了获得整体误差率,我们需要将这些平方距离相加并计算平均值。这称为**均方误差**函数。

但是,如果我们想要表示误差范围的性能指标,我们需要一个表示绝对误差的公式。**平均绝对误差**函数的公式如下:

Mean absolute

上述公式取预测值和真实值之间的绝对距离。

使用CNTK衡量回归性能

在这里,我们将了解如何结合CNTK使用我们讨论过的不同度量。我们将使用一个回归模型,根据以下步骤预测汽车的每加仑英里数。

实现步骤:

**步骤1** - 首先,我们需要从**cntk**包中导入所需的组件,如下所示:

from cntk import default_option, input_variable
from cntk.layers import Dense, Sequential
from cntk.ops import relu

**步骤2** - 接下来,我们需要使用**default_options**函数定义一个默认激活函数。然后,创建一个新的Sequential层集,并提供两个各有64个神经元的Dense层。然后,我们将一个额外的Dense层(将作为输出层)添加到Sequential层集,并提供1个没有激活函数的神经元,如下所示:

with default_options(activation=relu):
model = Sequential([Dense(64),Dense(64),Dense(1,activation=None)])

**步骤3** - 创建网络后,我们需要创建一个输入特征。我们需要确保它与我们将用于训练的特征具有相同的形状。

features = input_variable(X.shape[1])

**步骤4** - 现在,我们需要创建一个大小为1的另一个**input_variable**。它将用于存储神经网络的预期值。

target = input_variable(1)
z = model(features)

现在,我们需要训练模型,为此,我们将分割数据集并使用以下实现步骤进行预处理:

**步骤5** - 首先,从sklearn.preprocessing导入StandardScaler以获得-1和+1之间的值。这将有助于我们避免神经网络中的梯度爆炸问题。

from sklearn.preprocessing import StandardScalar

**步骤6** - 接下来,从sklearn.model_selection导入train_test_split,如下所示:

from sklearn.model_selection import train_test_split

**步骤7** - 使用**drop**方法从数据集中删除**mpg**列。最后,使用**train_test_split**函数将数据集分割成训练集和验证集,如下所示:

x = df_cars.drop(columns=[‘mpg’]).values.astype(np.float32)
y=df_cars.iloc[: , 0].values.reshape(-1, 1).astype(np.float32)
scaler = StandardScaler()
X = scaler.fit_transform(x)
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2)

**步骤8** - 现在,我们需要创建一个大小为1的另一个input_variable。它将用于存储神经网络的预期值。

target = input_variable(1)
z = model(features)

我们已经分割并预处理了数据,现在我们需要训练神经网络。就像在前面几节创建回归模型时所做的那样,我们需要定义损失函数和**度量**函数的组合来训练模型。

import cntk
def absolute_error(output, target):
   return cntk.ops.reduce_mean(cntk.ops.abs(output – target))
@ cntk.Function
def criterion_factory(output, target):
   loss = squared_error(output, target)
   metric = absolute_error(output, target)
   return loss, metric

现在,让我们看看如何使用训练好的模型。对于我们的模型,我们将使用criterion_factory作为损失和度量组合。

from cntk.losses import squared_error
from cntk.learners import sgd
from cntk.logging import ProgressPrinter
progress_printer = ProgressPrinter(0)
loss = criterion_factory (z, target)
learner = sgd(z.parameters, 0.001)
training_summary=loss.train((x_train,y_train),parameter_learners=[learner],callbacks=[progress_printer],minibatch_size=16,max_epochs=10)

完整的实现示例

from cntk import default_option, input_variable
from cntk.layers import Dense, Sequential
from cntk.ops import relu
with default_options(activation=relu):
model = Sequential([Dense(64),Dense(64),Dense(1,activation=None)])
features = input_variable(X.shape[1])
target = input_variable(1)
z = model(features)
from sklearn.preprocessing import StandardScalar
from sklearn.model_selection import train_test_split
x = df_cars.drop(columns=[‘mpg’]).values.astype(np.float32)
y=df_cars.iloc[: , 0].values.reshape(-1, 1).astype(np.float32)
scaler = StandardScaler()
X = scaler.fit_transform(x)
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2)
target = input_variable(1)
z = model(features)
import cntk
def absolute_error(output, target):
   return cntk.ops.reduce_mean(cntk.ops.abs(output – target))
@ cntk.Function
def criterion_factory(output, target):
loss = squared_error(output, target)
metric = absolute_error(output, target)
return loss, metric
from cntk.losses import squared_error
from cntk.learners import sgd
from cntk.logging import ProgressPrinter
progress_printer = ProgressPrinter(0)
loss = criterion_factory (z, target)
learner = sgd(z.parameters, 0.001)
training_summary=loss.train((x_train,y_train),parameter_learners=[learner],callbacks=[progress_printer],minibatch_size=16,max_epochs=10)

输出

-------------------------------------------------------------------
average  since   average   since  examples
loss     last    metric    last
------------------------------------------------------
Learning rate per minibatch: 0.001
690       690     24.9     24.9       16
654       636     24.1     23.7       48
[………]

为了验证我们的回归模型,我们需要确保该模型处理新数据的效果与处理训练数据一样好。为此,我们需要使用测试数据在**loss**和**metric**组合上调用**test**方法,如下所示:

loss.test([X_test, y_test])

输出:

{'metric': 1.89679785619, 'samples': 79}

CNTK - 内存不足的数据集

本章将解释如何衡量内存外数据集的性能。

在前面几节中,我们讨论了验证神经网络性能的各种方法,但是我们讨论的方法是处理适合内存的数据集的方法。

这里出现了一个问题,那就是内存外数据集怎么办,因为在生产环境中,我们需要大量数据来训练**神经网络**。在本节中,我们将讨论如何在使用小批量源和手动小批量循环时衡量性能。

小批量源

在处理内存外数据集(即小批量源)时,我们需要对损失和度量进行略微不同的设置,这与我们在处理小型数据集(即内存内数据集)时使用的设置不同。首先,我们将了解如何设置将数据馈送到神经网络模型训练器的方法。

以下是实现步骤:

**步骤1** - 首先,从**cntk.io**模块导入用于创建小批量源的组件,如下所示:

from cntk.io import StreamDef, StreamDefs, MinibatchSource, CTFDeserializer,
 INFINITY_REPEAT
 

**步骤2** - 接下来,创建一个名为**create_datasource**的新函数。此函数将有两个参数,即文件名和限制,默认值为**INFINITELY_REPEAT**。

def create_datasource(filename, limit =INFINITELY_REPEAT)

**步骤3** - 现在,在函数内部,使用**StreamDef**类为读取具有三个特征的labels字段的标签创建一个流定义。我们还需要将**is_sparse**设置为**False**,如下所示:

labels_stream = StreamDef(field=’labels’, shape=3, is_sparse=False)

**步骤4** - 接下来,创建另一个**StreamDef**实例来读取输入文件中的features字段。

feature_stream = StreamDef(field=’features’, shape=4, is_sparse=False)

**步骤5** - 现在,初始化**CTFDeserializer**实例类。指定我们需要反序列化的文件名和流,如下所示:

deserializer = CTFDeserializer(filename, StreamDefs(labels=
label_stream, features=features_stream)

**步骤6** - 接下来,我们需要使用反序列化器创建minisourceBatch的实例,如下所示:

Minibatch_source = MinibatchSource(deserializer, randomize=True, max_sweeps=limit)
return minibatch_source

**步骤7** - 最后,我们需要提供训练和测试源,我们在前面的章节中也创建了这些源。我们使用鸢尾花数据集。

training_source = create_datasource(‘Iris_train.ctf’)
test_source = create_datasource(‘Iris_test.ctf’, limit=1)

创建**MinibatchSource**实例后,我们需要对其进行训练。我们可以使用与处理小型内存内数据集时相同的训练逻辑。在这里,我们将使用**MinibatchSource**实例作为损失函数的train方法的输入,如下所示:

以下是实现步骤:

**步骤1** - 为了记录训练会话的输出,首先从**cntk.logging**模块导入**ProgressPrinter**,如下所示:

from cntk.logging import ProgressPrinter

**步骤2** - 接下来,要设置训练会话,请从**cntk.train**模块导入**trainer**和**training_session**,如下所示:

from cntk.train import Trainer, training_session

**步骤3** - 现在,我们需要定义一些常量,例如**minibatch_size**、**samples_per_epoch**和**num_epochs**,如下所示:

minbatch_size = 16
samples_per_epoch = 150
num_epochs = 30
max_samples = samples_per_epoch * num_epochs

**步骤4** - 接下来,为了了解如何在CNTK中读取训练期间的数据,我们需要定义网络的输入变量与小批量源中的流之间的映射。

input_map = {
   features: training_source.streams.features,
   labels: training_source.streams.labels
}

**步骤5** - 接下来,要记录训练过程的输出,请使用新的**ProgressPrinter**实例初始化**progress_printer**变量。此外,初始化**trainer**并为其提供模型,如下所示:

progress_writer = ProgressPrinter(0)
trainer: training_source.streams.labels

**步骤6** - 最后,要启动训练过程,我们需要调用**training_session**函数,如下所示:

session = training_session(trainer,
   mb_source=training_source,
   mb_size=minibatch_size,
   model_inputs_to_streams=input_map,
   max_samples=max_samples,
   test_config=test_config)
session.train()

训练模型后,我们可以通过使用**TestConfig**对象并将其分配给**train_session**函数的**test_config**关键字参数来为此设置添加验证。

以下是实现步骤:

**步骤1** - 首先,我们需要从**cntk.train**模块导入**TestConfig**类,如下所示:

from cntk.train import TestConfig

**步骤2** - 现在,我们需要使用**test_source**作为输入创建一个新的**TestConfig**实例:

Test_config = TestConfig(test_source)

完整示例

from cntk.io import StreamDef, StreamDefs, MinibatchSource, CTFDeserializer, INFINITY_REPEAT
def create_datasource(filename, limit =INFINITELY_REPEAT)
labels_stream = StreamDef(field=’labels’, shape=3, is_sparse=False)
feature_stream = StreamDef(field=’features’, shape=4, is_sparse=False)
deserializer = CTFDeserializer(filename, StreamDefs(labels=label_stream, features=features_stream)
Minibatch_source = MinibatchSource(deserializer, randomize=True, max_sweeps=limit)
return minibatch_source
training_source = create_datasource(‘Iris_train.ctf’)
test_source = create_datasource(‘Iris_test.ctf’, limit=1)
from cntk.logging import ProgressPrinter
from cntk.train import Trainer, training_session
minbatch_size = 16
samples_per_epoch = 150
num_epochs = 30
max_samples = samples_per_epoch * num_epochs
input_map = {
   features:   training_source.streams.features,
   labels: training_source.streams.labels
 }
progress_writer = ProgressPrinter(0)
trainer: training_source.streams.labels
session = training_session(trainer,
   mb_source=training_source,
   mb_size=minibatch_size,
   model_inputs_to_streams=input_map,
   max_samples=max_samples,
   test_config=test_config)
session.train()
from cntk.train import TestConfig
Test_config = TestConfig(test_source)

输出

-------------------------------------------------------------------
average   since   average   since  examples
loss      last    metric    last
------------------------------------------------------
Learning rate per minibatch: 0.1
1.57      1.57     0.214    0.214   16
1.38      1.28     0.264    0.289   48
[………]
Finished Evaluation [1]: Minibatch[1-1]:metric = 69.65*30;

手动小批量循环

如上所述,通过在使用CNTK的常规API进行训练时使用度量,可以轻松地衡量神经网络模型在训练期间和训练后的性能。但是,另一方面,在使用手动小批量循环时,事情不会那么容易。

在这里,我们使用以下模型,该模型具有4个输入和3个输出,来自鸢尾花数据集,在前面的章节中也创建过:

from cntk import default_options, input_variable
from cntk.layers import Dense, Sequential
from cntk.ops import log_softmax, relu, sigmoid
from cntk.learners import sgd
model = Sequential([
   Dense(4, activation=sigmoid),
   Dense(3, activation=log_softmax)
])
features = input_variable(4)
labels = input_variable(3)
z = model(features)

接下来,模型的损失定义为交叉熵损失函数和F值度量的组合,如前几节中所用。我们将使用**criterion_factory**实用程序将其创建为CNTK函数对象,如下所示:

import cntk
from cntk.losses import cross_entropy_with_softmax, fmeasure
@cntk.Function
def criterion_factory(outputs, targets):
   loss = cross_entropy_with_softmax(outputs, targets)
   metric = fmeasure(outputs, targets, beta=1)
   return loss, metric
loss = criterion_factory(z, labels)
learner = sgd(z.parameters, 0.1)
label_mapping = {
   'Iris-setosa': 0,
   'Iris-versicolor': 1,
   'Iris-virginica': 2
}

现在,由于我们已经定义了损失函数,我们将了解如何在训练器中使用它来设置手动训练会话。

以下是实现步骤:

**步骤1** - 首先,我们需要导入所需的包,例如**numpy**和**pandas**,以加载和预处理数据。

import pandas as pd
import numpy as np

**步骤2** - 接下来,为了在训练期间记录信息,请导入**ProgressPrinter**类,如下所示:

from cntk.logging import ProgressPrinter

**步骤3** - 然后,我们需要从cntk.train模块导入trainer模块,如下所示:

from cntk.train import Trainer

**步骤4** - 接下来,创建一个新的**ProgressPrinter**实例,如下所示:

progress_writer = ProgressPrinter(0)

**步骤5** - 现在,我们需要使用损失、学习器和**progress_writer**作为参数初始化训练器,如下所示:

trainer = Trainer(z, loss, learner, progress_writer)

**步骤6** - 接下来,为了训练模型,我们将创建一个循环,该循环将迭代数据集三十次。这将是外部训练循环。

for _ in range(0,30):

**步骤7** - 现在,我们需要使用pandas从磁盘加载数据。然后,为了以**小批量**加载数据集,将**chunksize**关键字参数设置为16。

input_data = pd.read_csv('iris.csv',
names=['sepal_length', 'sepal_width','petal_length','petal_width', 'species'],
index_col=False, chunksize=16)

**步骤8** - 现在,创建一个内部训练for循环来迭代每个**小批量**。

for df_batch in input_data:

步骤 9 − 现在在这个循环内部,使用iloc索引器读取前四列作为训练特征,并将它们转换为float32类型 −

feature_values = df_batch.iloc[:,:4].values
feature_values = feature_values.astype(np.float32)

步骤 10 − 现在,读取最后一列作为训练标签,如下所示 −

label_values = df_batch.iloc[:,-1]

步骤 11 − 接下来,我们将使用独热向量将标签字符串转换为其数值表示,如下所示 −

label_values = label_values.map(lambda x: label_mapping[x])

步骤 12 − 之后,获取标签的数值表示。接下来,将它们转换为NumPy数组,以便更容易地处理它们,如下所示 −

label_values = label_values.values

步骤 13 − 现在,我们需要创建一个新的NumPy数组,其行数与我们已转换的标签值相同。

encoded_labels = np.zeros((label_values.shape[0], 3))

步骤 14 − 现在,为了创建独热编码标签,请根据数值标签值选择列。

encoded_labels[np.arange(label_values.shape[0]), label_values] = 1.

步骤 15 − 最后,我们需要在训练器上调用train_minibatch方法,并为小批量提供已处理的特征和标签。

trainer.train_minibatch({features: feature_values, labels: encoded_labels})

完整示例

from cntk import default_options, input_variable
from cntk.layers import Dense, Sequential
from cntk.ops import log_softmax, relu, sigmoid
from cntk.learners import sgd
model = Sequential([
   Dense(4, activation=sigmoid),
   Dense(3, activation=log_softmax)
])
features = input_variable(4)
labels = input_variable(3)
z = model(features)
import cntk
from cntk.losses import cross_entropy_with_softmax, fmeasure
@cntk.Function
def criterion_factory(outputs, targets):
   loss = cross_entropy_with_softmax(outputs, targets)
   metric = fmeasure(outputs, targets, beta=1)
   return loss, metric
loss = criterion_factory(z, labels)
learner = sgd(z.parameters, 0.1)
label_mapping = {
   'Iris-setosa': 0,
   'Iris-versicolor': 1,
   'Iris-virginica': 2
}
import pandas as pd
import numpy as np
from cntk.logging import ProgressPrinter
from cntk.train import Trainer
progress_writer = ProgressPrinter(0)
trainer = Trainer(z, loss, learner, progress_writer)
for _ in range(0,30):
   input_data = pd.read_csv('iris.csv',
      names=['sepal_length', 'sepal_width','petal_length','petal_width', 'species'],
      index_col=False, chunksize=16)
for df_batch in input_data:
   feature_values = df_batch.iloc[:,:4].values
   feature_values = feature_values.astype(np.float32)
   label_values = df_batch.iloc[:,-1]
label_values = label_values.map(lambda x: label_mapping[x])
label_values = label_values.values
   encoded_labels = np.zeros((label_values.shape[0], 3))
   encoded_labels[np.arange(label_values.shape[0]), 
label_values] = 1.
   trainer.train_minibatch({features: feature_values, labels: encoded_labels})

输出

-------------------------------------------------------------------
average    since    average   since  examples
loss       last      metric   last
------------------------------------------------------
Learning rate per minibatch: 0.1
1.45       1.45     -0.189    -0.189   16
1.24       1.13     -0.0382    0.0371  48
[………]

在上面的输出中,我们得到了训练期间损失和指标的输出。这是因为我们将指标和损失组合在一个函数对象中,并在训练器配置中使用了进度打印器。

现在,为了评估模型性能,我们需要执行与训练模型相同的任务,但是这次,我们需要使用Evaluator实例来测试模型。这在下面的Python代码中显示 −

from cntk import Evaluator
evaluator = Evaluator(loss.outputs[1], [progress_writer])
input_data = pd.read_csv('iris.csv',
   names=['sepal_length', 'sepal_width','petal_length','petal_width', 'species'],
index_col=False, chunksize=16)
for df_batch in input_data:
   feature_values = df_batch.iloc[:,:4].values
   feature_values = feature_values.astype(np.float32)
   label_values = df_batch.iloc[:,-1]
   label_values = label_values.map(lambda x: label_mapping[x])
   label_values = label_values.values
   encoded_labels = np.zeros((label_values.shape[0], 3))
   encoded_labels[np.arange(label_values.shape[0]), label_values] = 1.
   evaluator.test_minibatch({ features: feature_values, labels:
      encoded_labels})
evaluator.summarize_test_progress()

现在,我们将得到类似于以下内容的输出 −

输出

Finished Evaluation [1]: Minibatch[1-11]:metric = 74.62*143;

CNTK - 监控模型

本章,我们将了解如何在CNTK中监控模型。

简介

在前面的章节中,我们对我们的NN模型进行了一些验证。但是,在训练过程中监控我们的模型是否也必要且可行呢?

是的,我们已经使用ProgressWriter类来监控我们的模型,还有许多其他的方法可以做到这一点。在深入了解这些方法之前,让我们先了解CNTK中的监控机制以及如何使用它来检测NN模型中的问题。

CNTK中的回调函数

实际上,在训练和验证期间,CNTK允许我们在API的多个位置指定回调函数。首先,让我们仔细看看CNTK何时调用回调函数。

CNTK何时调用回调函数?

CNTK将在训练和测试数据集的以下时刻调用回调函数 −

  • 一个小批量完成。

  • 在训练期间完成对数据集的完整扫描。

  • 测试小批量完成。

  • 在测试期间完成对数据集的完整扫描。

指定回调函数

在使用CNTK时,我们可以在API的多个位置指定回调函数。例如 −

在损失函数上调用train时?

在这里,当我们在损失函数上调用train时,我们可以通过callbacks参数指定一组回调函数,如下所示 −

training_summary=loss.train((x_train,y_train),
parameter_learners=[learner],
callbacks=[progress_writer]),
minibatch_size=16, max_epochs=15)

在使用小批量数据源或使用手动小批量循环时 −

在这种情况下,我们可以在创建Trainer时指定用于监控目的的回调函数,如下所示 −

from cntk.logging import ProgressPrinter
callbacks = [
   ProgressPrinter(0)
]
Trainer = Trainer(z, (loss, metric), learner, [callbacks])

各种监控工具

让我们学习不同的监控工具。

ProgressPrinter

在阅读本教程时,您会发现ProgressPrinter是最常用的监控工具。ProgressPrinter监控工具的一些特性包括 −

ProgressPrinter类实现基于控制台的基本日志记录来监控我们的模型。如果需要,它可以将日志记录到磁盘。

在分布式训练场景中尤其有用。

在我们无法登录到控制台查看Python程序输出的场景中,它也非常有用。

借助以下代码,我们可以创建一个ProgressPrinter实例 −

ProgressPrinter(0, log_to_file=’test.txt’)

我们将得到类似于我们在前面章节中看到的输出 −

Test.txt
CNTKCommandTrainInfo: train : 300
CNTKCommandTrainInfo: CNTKNoMoreCommands_Total : 300
CNTKCommandTrainBegin: train
-------------------------------------------------------------------
average since average since examples
loss last metric last
------------------------------------------------------
Learning rate per minibatch: 0.1
1.45 1.45 -0.189 -0.189 16
1.24 1.13 -0.0382 0.0371 48
[………]

TensorBoard

使用ProgressPrinter的一个缺点是,我们无法很好地查看损失和指标随时间的变化情况。TensorBoardProgressWriter是CNTK中ProgressPrinter类的绝佳替代方案。

在使用它之前,我们需要首先使用以下命令安装它 −

pip install tensorboard

现在,为了使用TensorBoard,我们需要在我们的训练代码中设置TensorBoardProgressWriter,如下所示 −

import time
from cntk.logging import TensorBoardProgressWriter
tensorbrd_writer = TensorBoardProgressWriter(log_dir=’logs/{}’.format(time.time()),freq=1,model=z)

训练NN模型完成后,最好在TensorBoardProgressWriter实例上调用close方法。

我们可以使用以下命令可视化TensorBoard日志数据 −

Tensorboard –logdir logs

CNTK - 卷积神经网络

本章,让我们学习如何在CNTK中构建卷积神经网络(CNN)。

简介

卷积神经网络(CNN)也由具有可学习权重和偏差的神经元组成。因此,它们与普通神经网络(NN)类似。

如果我们回忆一下普通NN的工作原理,每个神经元接收一个或多个输入,取加权和,并通过激活函数产生最终输出。这里的问题是,如果CNN和普通NN有如此多的相似之处,那么是什么让这两个网络彼此不同呢?

使它们不同的在于输入数据的处理和层类型?普通NN忽略输入数据的结构,所有数据在馈送到网络之前都转换为一维数组。

但是,卷积神经网络架构可以考虑图像的二维结构,对其进行处理,并允许它提取特定于图像的属性。此外,CNN具有一个或多个卷积层和池化层的优势,它们是CNN的主要构建块。

这些层之后是一个或多个全连接层,就像标准的多层NN一样。因此,我们可以将CNN视为全连接网络的特例。

卷积神经网络(CNN)架构

CNN的架构基本上是一个层列表,它将图像体积的三维(即宽度、高度和深度)转换为三维输出体积。这里需要注意的一点是,当前层中的每个神经元都连接到前一层输出的小块,这就像将N*N滤波器叠加在输入图像上。

它使用M个滤波器,这些滤波器基本上是特征提取器,可以提取边缘、角点等特征。以下是用于构建卷积神经网络(CNN)的层[INPUT-CONV-RELU-POOL-FC]

  • INPUT −顾名思义,此层保存原始像素值。原始像素值表示图像数据本身。例如,INPUT [64×64×3]是一个具有64宽度、64高度和3深度的3通道RGB图像。

  • CONV −此层是CNN的构建块之一,因为大部分计算都在此层中完成。例如,如果我们在上面提到的INPUT [64×64×3]上使用6个滤波器,这可能会导致体积[64×64×6]。

  • RELU −也称为整流线性单元层,它将激活函数应用于前一层的输出。换句话说,RELU会向网络添加非线性。

  • POOL −此层,即池化层是CNN的另一个构建块。此层的'主要任务是下采样,这意味着它独立地作用于输入的每个切片并对其进行空间调整大小。

  • FC −称为全连接层,更具体地说,是输出层。它用于计算输出类别分数,生成的输出是大小为1*1*L的体积,其中L是对应于类别分数的数字。

下图表示CNN的典型架构 −

Cnns Architecture

创建CNN结构

我们已经了解了CNN的架构和基础知识,现在我们将使用CNTK构建卷积网络。在这里,我们将首先了解如何将CNN的结构组合在一起,然后我们将研究如何训练它的参数。

最后,我们将了解如何通过使用各种不同的层设置更改其结构来改进神经网络。我们将使用MNIST图像数据集。

因此,让我们首先创建一个CNN结构。通常,当我们构建CNN来识别图像中的模式时,我们会执行以下操作 −

  • 我们使用卷积层和池化层的组合。

  • 网络末尾的一个或多个隐藏层。

  • 最后,我们使用softmax层完成网络以进行分类。

借助以下步骤,我们可以构建网络结构 −

步骤 1 −首先,我们需要导入CNN所需的层。

from cntk.layers import Convolution2D, Sequential, Dense, MaxPooling

步骤 2 −接下来,我们需要导入CNN的激活函数。

from cntk.ops import log_softmax, relu

步骤 3 −之后,为了稍后初始化卷积层,我们需要导入glorot_uniform_initializer,如下所示 −

from cntk.initializer import glorot_uniform

步骤 4 −接下来,要创建输入变量,请导入input_variable函数。并导入default_option函数,以便更容易地配置NN。

from cntk import input_variable, default_options

步骤 5 −现在,为了存储输入图像,创建一个新的input_variable。它将包含三个通道,即红色、绿色和蓝色。它的大小为28 x 28像素。

features = input_variable((3,28,28))

步骤 6 −接下来,我们需要创建另一个input_variable来存储要预测的标签。

labels = input_variable(10)

步骤 7 −现在,我们需要为NN创建default_option。并且,我们需要使用glorot_uniform作为初始化函数。

with default_options(initialization=glorot_uniform, activation=relu):

步骤 8 −接下来,为了设置NN的结构,我们需要创建一个新的Sequential层集。

步骤 9 −现在,我们需要在Sequential层集中添加一个Convolutional2D层,其filter_shape为5,strides设置为1。此外,启用填充,以便填充图像以保留原始尺寸。

model = Sequential([
Convolution2D(filter_shape=(5,5), strides=(1,1), num_filters=8, pad=True),

步骤 10 −现在是时候添加一个MaxPooling层了,其filter_shape为2,strides设置为2,以将图像压缩一半。

MaxPooling(filter_shape=(2,2), strides=(2,2)),

步骤 11 −现在,就像在步骤9中所做的那样,我们需要添加另一个Convolutional2D层,其filter_shape为5,strides设置为1,使用16个滤波器。此外,启用填充,以便保留前一层池化层生成的图像的大小。

Convolution2D(filter_shape=(5,5), strides=(1,1), num_filters=16, pad=True),

步骤 12 −现在,就像在步骤10中所做的那样,添加另一个MaxPooling层,其filter_shape为3,strides设置为3,以将图像缩小到三分之一。

MaxPooling(filter_shape=(3,3), strides=(3,3)),

步骤 13 −最后,为10个可能的类别添加一个具有十个神经元的Dense层,网络可以预测。为了将网络转换为分类模型,请使用log_softmax激活函数。

Dense(10, activation=log_softmax)
])

创建CNN结构的完整示例

from cntk.layers import Convolution2D, Sequential, Dense, MaxPooling
from cntk.ops import log_softmax, relu
from cntk.initializer import glorot_uniform
from cntk import input_variable, default_options
features = input_variable((3,28,28))
labels = input_variable(10)
with default_options(initialization=glorot_uniform, activation=relu):
model = Sequential([
   Convolution2D(filter_shape=(5,5), strides=(1,1), num_filters=8, pad=True),
MaxPooling(filter_shape=(2,2), strides=(2,2)),
   Convolution2D(filter_shape=(5,5), strides=(1,1), num_filters=16, pad=True),
MaxPooling(filter_shape=(3,3), strides=(3,3)),
Dense(10, activation=log_softmax)
])
z = model(features)

使用图像训练CNN

网络结构搭建完成后,接下来就是训练网络。但在开始训练之前,我们需要设置小批量数据源(minibatch sources),因为训练处理图像的神经网络(NN)需要的内存比大多数计算机都多。

我们在之前的章节中已经创建了小批量数据源。以下是设置两个小批量数据源的 Python 代码:

有了`create_datasource`函数,我们现在可以创建两个独立的数据源(一个用于训练,一个用于测试)来训练模型。

train_datasource = create_datasource('mnist_train')
test_datasource = create_datasource('mnist_test', max_sweeps=1, train=False)

现在,图像准备就绪,我们可以开始训练我们的神经网络了。与之前的章节一样,我们可以使用损失函数上的`train`方法启动训练。以下是相应的代码:

from cntk import Function
from cntk.losses import cross_entropy_with_softmax
from cntk.metrics import classification_error
from cntk.learners import sgd
@Function
def criterion_factory(output, targets):
loss = cross_entropy_with_softmax(output, targets)
metric = classification_error(output, targets)
return loss, metric
loss = criterion_factory(z, labels)
learner = sgd(z.parameters, lr=0.2)

通过之前的代码,我们已经为神经网络设置了损失函数和学习器。以下代码将训练和验证神经网络:

from cntk.logging import ProgressPrinter
from cntk.train import TestConfig
progress_writer = ProgressPrinter(0)
test_config = TestConfig(test_datasource)
input_map = {
   features: train_datasource.streams.features,
   labels: train_datasource.streams.labels
}
loss.train(train_datasource,
     max_epochs=10,
     minibatch_size=64,
     epoch_size=60000,
        parameter_learners=[learner],
     model_inputs_to_streams=input_map,
     callbacks=[progress_writer, test_config])

完整实现示例

from cntk.layers import Convolution2D, Sequential, Dense, MaxPooling
from cntk.ops import log_softmax, relu
from cntk.initializer import glorot_uniform
from cntk import input_variable, default_options
features = input_variable((3,28,28))
labels = input_variable(10)
with default_options(initialization=glorot_uniform, activation=relu):
model = Sequential([
   Convolution2D(filter_shape=(5,5), strides=(1,1), num_filters=8, pad=True),
MaxPooling(filter_shape=(2,2), strides=(2,2)),
   Convolution2D(filter_shape=(5,5), strides=(1,1), num_filters=16, pad=True),
MaxPooling(filter_shape=(3,3), strides=(3,3)),
Dense(10, activation=log_softmax)
])
z = model(features)
import os
from cntk.io import MinibatchSource, StreamDef, StreamDefs, ImageDeserializer, INFINITELY_REPEAT
import cntk.io.transforms as xforms
def create_datasource(folder, train=True, max_sweeps=INFINITELY_REPEAT):
   mapping_file = os.path.join(folder, 'mapping.bin')
   image_transforms = []
   if train:
    image_transforms += [
     xforms.crop(crop_type='randomside', side_ratio=0.8),
     xforms.scale(width=28, height=28, channels=3, interpolations='linear')
]
   stream_definitions = StreamDefs(
   features=StreamDef(field='image', transforms=image_transforms),
    labels=StreamDef(field='label', shape=10)
)
   deserializer = ImageDeserializer(mapping_file, stream_definitions)
return MinibatchSource(deserializer, max_sweeps=max_sweeps)
train_datasource = create_datasource('mnist_train')
test_datasource = create_datasource('mnist_test', max_sweeps=1, train=False)
from cntk import Function
from cntk.losses import cross_entropy_with_softmax
from cntk.metrics import classification_error
from cntk.learners import sgd
@Function
def criterion_factory(output, targets):
   loss = cross_entropy_with_softmax(output, targets)
   metric = classification_error(output, targets)
return loss, metric
loss = criterion_factory(z, labels)
learner = sgd(z.parameters, lr=0.2)
from cntk.logging import ProgressPrinter
from cntk.train import TestConfig
progress_writer = ProgressPrinter(0)
test_config = TestConfig(test_datasource)
input_map = {
   features: train_datasource.streams.features,
   labels: train_datasource.streams.labels
}
loss.train(train_datasource,
     max_epochs=10,
     minibatch_size=64,
     epoch_size=60000,
        parameter_learners=[learner],
     model_inputs_to_streams=input_map,
     callbacks=[progress_writer, test_config])

输出

-------------------------------------------------------------------
average  since  average  since  examples
loss     last   metric   last
------------------------------------------------------
Learning rate per minibatch: 0.2
142      142      0.922   0.922    64
1.35e+06 1.51e+07 0.896   0.883    192
[………]

图像变换

正如我们所看到的,训练用于图像识别的神经网络很困难,而且它们需要大量数据进行训练。另一个问题是,它们倾向于过度拟合训练中使用的图像。例如,如果我们只有直立姿势的人脸照片,我们的模型将很难识别旋转到其他方向的人脸。

为了克服这个问题,我们可以使用图像增强,而CNTK在为图像创建小批量数据源时支持特定的变换。我们可以使用以下几种变换:

  • 我们可以通过几行代码随机裁剪用于训练的图像。

  • 我们还可以使用缩放和颜色变换。

让我们通过下面的Python代码来看看如何通过在前面用于创建小批量数据源的函数中包含裁剪变换来更改变换列表。

import os
from cntk.io import MinibatchSource, StreamDef, StreamDefs, ImageDeserializer, INFINITELY_REPEAT
import cntk.io.transforms as xforms
def create_datasource(folder, train=True, max_sweeps=INFINITELY_REPEAT):
   mapping_file = os.path.join(folder, 'mapping.bin')
   image_transforms = []
   if train:
   image_transforms += [
     xforms.crop(crop_type='randomside', side_ratio=0.8),
xforms.scale(width=28, height=28, channels=3, interpolations='linear')
]
   stream_definitions = StreamDefs(
   features=StreamDef(field='image', transforms=image_transforms),
labels=StreamDef(field='label', shape=10)
)
   deserializer = ImageDeserializer(mapping_file, stream_definitions)
return MinibatchSource(deserializer, max_sweeps=max_sweeps)

借助上述代码,我们可以增强函数以包含一组图像变换,这样,在训练时可以随机裁剪图像,从而获得更多图像变化。

CNTK - 循环神经网络

现在,让我们了解如何在CNTK中构建循环神经网络(RNN)。

简介

我们学习了如何使用神经网络对图像进行分类,这是深度学习中的一个标志性任务。但是,神经网络擅长且研究活跃的另一个领域是循环神经网络(RNN)。在这里,我们将了解RNN是什么以及如何在需要处理时间序列数据的情况下使用它。

什么是循环神经网络?

循环神经网络(RNN)可以定义为一种特殊的神经网络,能够进行时间推理。RNN主要用于需要处理随时间变化的值(即时间序列数据)的场景。为了更好地理解它,让我们比较一下常规神经网络和循环神经网络:

  • 我们知道,在常规神经网络中,我们只能提供一个输入。这限制了它只能产生一个预测结果。例如,我们可以使用常规神经网络进行文本翻译工作。

  • 另一方面,在循环神经网络中,我们可以提供一系列样本,从而得到单个预测结果。换句话说,使用RNN,我们可以根据输入序列预测输出序列。例如,RNN在翻译任务中已经进行了一些相当成功的实验。

循环神经网络的用途

RNN可以多种方式使用。其中一些如下:

预测单个输出

在深入探讨RNN如何根据序列预测单个输出的步骤之前,让我们看看基本的RNN是什么样的:

Single Output

正如上图所示,RNN包含一个反馈连接到输入,每当我们馈送一系列值时,它将把序列中的每个元素作为时间步进行处理。

此外,由于反馈连接,RNN可以将生成的输出与序列中下一个元素的输入结合起来。通过这种方式,RNN将在整个序列上构建一个记忆,可用于进行预测。

为了用RNN进行预测,我们可以执行以下步骤:

  • 首先,为了创建初始隐藏状态,我们需要馈送输入序列的第一个元素。

  • 之后,为了产生更新的隐藏状态,我们需要获取初始隐藏状态并将其与输入序列中的第二个元素结合。

  • 最后,为了产生最终的隐藏状态并预测RNN的输出,我们需要获取输入序列中的最后一个元素。

通过这种方式,借助这种反馈连接,我们可以训练RNN识别随时间发生的模式。

预测序列

上面讨论的基本RNN模型也可以扩展到其他用例。例如,我们可以使用它根据单个输入预测一系列值。在这种情况下,为了用RNN进行预测,我们可以执行以下步骤:

  • 首先,为了创建初始隐藏状态并预测输出序列中的第一个元素,我们需要将输入样本馈送到神经网络。

  • 之后,为了产生更新的隐藏状态和输出序列中的第二个元素,我们需要将初始隐藏状态与相同的样本结合。

  • 最后,为了再次更新隐藏状态并预测输出序列中的最后一个元素,我们需要再次馈送样本。

预测序列

我们已经了解了如何根据序列预测单个值以及如何根据单个值预测序列。现在让我们看看如何预测序列的序列。在这种情况下,为了用RNN进行预测,我们可以执行以下步骤:

  • 首先,为了创建初始隐藏状态并预测输出序列中的第一个元素,我们需要获取输入序列中的第一个元素。

  • 之后,为了更新隐藏状态并预测输出序列中的第二个元素,我们需要获取初始隐藏状态。

  • 最后,为了预测输出序列中的最后一个元素,我们需要获取更新的隐藏状态和输入序列中的最后一个元素。

RNN的工作原理

为了理解循环神经网络(RNN)的工作原理,我们需要首先了解网络中循环层的工作原理。因此,让我们首先讨论如何使用标准循环层预测输出。

使用标准RNN层预测输出

正如我们前面所讨论的,RNN中的基本层与神经网络中的常规层有很大的不同。在上一节中,我们还在图中演示了RNN的基本架构。为了更新序列中第一个时间步的隐藏状态,我们可以使用以下公式:

Rnn Layer

在上式中,我们通过计算初始隐藏状态和一组权重之间的点积来计算新的隐藏状态。

现在对于下一步,当前时间步的隐藏状态将用作序列中下一个时间步的初始隐藏状态。这就是为什么为了更新第二个时间步的隐藏状态,我们可以重复在第一步中执行的计算,如下所示:

First Step

接下来,我们可以重复更新序列中第三步(也是最后一步)的隐藏状态的过程,如下所示:

Last Step

当我们处理了序列中的所有上述步骤后,我们可以计算输出,如下所示:

Calculate Output

对于上述公式,我们使用了第三组权重和来自最终时间步的隐藏状态。

高级循环单元

基本循环层的主要问题是梯度消失问题,因此它在学习长期相关性方面不是很好。简而言之,基本循环层不能很好地处理长序列。这就是为什么其他一些更适合处理较长序列的循环层类型如下所示:

长短期记忆网络(LSTM)

Long-Short Term Memory (LSTM)

长短期记忆 (LSTM) 网络由 Hochreiter & Schmidhuber 引入。它解决了让基本循环层长时间记住事物的问题。LSTM 的架构如上图所示。我们可以看到它有输入神经元、记忆单元和输出神经元。为了解决梯度消失问题,长短期记忆网络使用显式记忆单元(存储先前值)和以下门:

  • **遗忘门** —顾名思义,它告诉记忆单元忘记之前的数值。记忆单元存储数值,直到门(即“遗忘门”)告诉它忘记它们。

  • **输入门** —顾名思义,它向单元添加新内容。

  • **输出门** —顾名思义,输出门决定何时将向量从单元传递到下一个隐藏状态。

门控循环单元(GRU)

Gated Recurrent Units (GRUs)

**门控循环单元**(GRU)是LSTM网络的一个轻微变体。它少了一个门,并且与LSTM的连接方式略有不同。其架构如上图所示。它具有输入神经元、门控记忆单元和输出神经元。门控循环单元网络具有以下两个门:

  • **更新门** —它确定以下两件事:

    • 应该保留多少来自最后状态的信息?

    • 应该从前一层输入多少信息?

  • **重置门** —重置门的功能与LSTM网络的遗忘门非常相似。唯一的区别是它的位置略有不同。

与长短期记忆网络相比,门控循环单元网络运行速度更快,更容易运行。

创建RNN结构

在我们开始对任何数据源的输出进行预测之前,我们需要首先构建RNN,构建RNN与我们在上一节中构建常规神经网络的方式非常相似。以下是构建一个RNN的代码:

from cntk.losses import squared_error
from cntk.io import CTFDeserializer, MinibatchSource, INFINITELY_REPEAT, StreamDefs, StreamDef
from cntk.learners import adam
from cntk.logging import ProgressPrinter
from cntk.train import TestConfig
BATCH_SIZE = 14 * 10
EPOCH_SIZE = 12434
EPOCHS = 10

堆叠多层

我们还可以在CNTK中堆叠多个循环层。例如,我们可以使用以下层的组合:

from cntk import sequence, default_options, input_variable
from cntk.layers import Recurrence, LSTM, Dropout, Dense, Sequential, Fold
features = sequence.input_variable(1)
with default_options(initial_state = 0.1):
   model = Sequential([
      Fold(LSTM(15)),
      Dense(1)
   ])(features)
target = input_variable(1, dynamic_axes=model.dynamic_axes)

正如我们在上述代码中看到的,我们有以下两种在CNTK中建模RNN的方法:

  • 首先,如果我们只需要循环层的最终输出,我们可以将`Fold`层与循环层(如GRU、LSTM甚至RNNStep)结合使用。

  • 其次,作为另一种方法,我们还可以使用`Recurrence`块。

使用时间序列数据训练RNN

构建模型后,让我们看看如何在CNTK中训练RNN:

from cntk import Function
@Function
def criterion_factory(z, t):
   loss = squared_error(z, t)
   metric = squared_error(z, t)
   return loss, metric
loss = criterion_factory(model, target)
learner = adam(model.parameters, lr=0.005, momentum=0.9)

现在,为了将数据加载到训练过程中,我们必须从一组CTF文件中反序列化序列。以下代码包含`create_datasource`函数,这是一个有用的实用程序函数,用于创建训练和测试数据源。

target_stream = StreamDef(field='target', shape=1, is_sparse=False)
features_stream = StreamDef(field='features', shape=1, is_sparse=False)
deserializer = CTFDeserializer(filename, StreamDefs(features=features_stream, target=target_stream))
   datasource = MinibatchSource(deserializer, randomize=True, max_sweeps=sweeps)
return datasource
train_datasource = create_datasource('Training data filename.ctf')#we need to provide the location of training file we created from our dataset.
test_datasource = create_datasource('Test filename.ctf', sweeps=1) #we need to provide the location of testing file we created from our dataset.

现在,我们已经设置了数据源、模型和损失函数,我们可以开始训练过程了。这与我们在前面使用基本神经网络时非常相似。

progress_writer = ProgressPrinter(0)
test_config = TestConfig(test_datasource)
input_map = {
   features: train_datasource.streams.features,
   target: train_datasource.streams.target
}
history = loss.train(
   train_datasource,
   epoch_size=EPOCH_SIZE,
   parameter_learners=[learner],
   model_inputs_to_streams=input_map,
   callbacks=[progress_writer, test_config],
   minibatch_size=BATCH_SIZE,
   max_epochs=EPOCHS
)

我们将得到类似于以下的输出:

输出:

average  since  average  since  examples
loss      last  metric  last
------------------------------------------------------
Learning rate per minibatch: 0.005
0.4      0.4    0.4      0.4      19
0.4      0.4    0.4      0.4      59
0.452    0.495  0.452    0.495   129
[…]

验证模型

实际上,使用RNN进行预测与使用任何其他CNK模型进行预测非常相似。唯一的区别是,我们需要提供序列而不是单个样本。

现在,我们的RNN最终完成了训练,我们可以使用一些样本序列对其进行测试来验证模型,如下所示:

import pickle
with open('test_samples.pkl', 'rb') as test_file:
test_samples = pickle.load(test_file)
model(test_samples) * NORMALIZE

输出:

array([[ 8081.7905],
[16597.693 ],
[13335.17 ],
...,
[11275.804 ],
[15621.697 ],
[16875.555 ]], dtype=float32)
广告
© . All rights reserved.