CNTK - 神经网络分类

  • 简述

    在本章中,我们将研究如何使用 CNTK 对神经网络进行分类。
  • 介绍

    分类可以定义为预测给定输入数据的分类输出标签或响应的过程。分类输出将基于模型在训练阶段所学的内容,可以具有“黑色”或“白色”或“垃圾邮件”或“无垃圾邮件”等形式。
    另一方面,在数学上,任务是从输入变量 X 到输出变量 Y逼近映射函数f 。
    分类问题的一个典型例子是电子邮件中的垃圾邮件检测。很明显,只能有两类输出,“垃圾邮件”和“无垃圾邮件”。
    为了实现这样的分类,我们首先需要对分类器进行训练,其中“垃圾邮件”和“无垃圾邮件”电子邮件将用作训练数据。一旦分类器训练成功,它就可以用来检测未知的电子邮件。
    在这里,我们将使用具有以下内容的鸢尾花数据集创建一个 4-5-3 NN -
    • 4 个输入节点(每个预测值一个)。
    • 5个隐藏的处理节点。
    • 3 个输出节点(因为 iris 数据集中有三种可能的物种)。
  • 加载数据集

    我们将使用鸢尾花数据集,我们希望根据萼片宽度和长度以及花瓣宽度和长度的物理特性对鸢尾花的种类进行分类。该数据集描述了不同品种鸢尾花的物理特性 -
    • 萼片长度
    • 萼片宽度
    • 花瓣长度
    • 花瓣宽度
    • 类即鸢尾花或花斑鸢尾或鸢尾花
    我们也有之前章节中使用过的iris.CSV文件。它可以在Pandas库的帮助下加载。但是,在使用它或将它加载到我们的分类器之前,我们需要准备训练和测试文件,以便它可以轻松地与 CNTK 一起使用。
  • 准备培训和测试文件

    Iris 数据集是 ML 项目中最受欢迎的数据集之一。它有 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
    
    …
    
    
    iris 数据集中共有 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
    
    
    现在,我们需要为我们的 NN 设置架构参数,并提供数据文件的位置。它可以在以下 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)
    
    
    现在,借助以下代码行,我们的程序将创建未经训练的 NN -
    
    
    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 }
    
    
    现在是时候训练我们的 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)
    
    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)
    
    
    在评估了我们训练有素的 NN 模型的准确性之后,我们将使用它来对看不见的数据进行预测 -
    
    
    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()函数的第一个参数。它也可以与文件的路径一起写入。
    • 另一个参数是具有默认值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])
    
    
    保存模型的好处是,一旦您加载保存的模型,就可以像刚刚训练模型一样使用它。