CNTK - Classificação da Rede Neural
Neste capítulo, estudaremos como classificar a rede neural usando CNTK.
Introdução
A classificação pode ser definida como o processo de prever rótulos de saída ou respostas categóricas para os dados de entrada fornecidos. A saída categorizada, que será baseada no que o modelo aprendeu na fase de treinamento, pode ter a forma "Preto" ou "Branco" ou "spam" ou "sem spam".
Por outro lado, matematicamente, é a tarefa de aproximar uma função de mapeamento, digamos f das variáveis de entrada diga X para as variáveis de saída diga Y.
Um exemplo clássico de problema de classificação pode ser a detecção de spam em e-mails. É óbvio que pode haver apenas duas categorias de saída, "spam" e "sem spam".
Para implementar tal classificação, primeiro precisamos fazer o treinamento do classificador onde os e-mails "spam" e "sem spam" seriam usados como dados de treinamento. Depois de o classificador ser treinado com sucesso, ele pode ser usado para detectar um e-mail desconhecido.
Aqui, vamos criar um 4-5-3 NN usando o conjunto de dados de flores de íris com o seguinte -
4 nós de entrada (um para cada valor do preditor).
5 nós de processamento ocultos.
Nós de 3 saídas (porque há três espécies possíveis no conjunto de dados da íris).
Carregando conjunto de dados
Estaremos usando o conjunto de dados de flores de íris, a partir do qual queremos classificar as espécies de flores de íris com base nas propriedades físicas de largura e comprimento das sépalas e largura e comprimento das pétalas. O conjunto de dados descreve as propriedades físicas de diferentes variedades de flores de íris -
Comprimento da sépala
Largura sépala
Comprimento da pétala
Largura da pétala
Classe ie iris setosa ou iris versicolor ou iris virginica
Nós temos iris.CSVarquivo que usamos antes em capítulos anteriores também. Pode ser carregado com a ajuda dePandasbiblioteca. Mas, antes de usá-lo ou carregá-lo para nosso classificador, precisamos preparar os arquivos de treinamento e teste, para que possa ser usado facilmente com o CNTK.
Preparando arquivos de treinamento e teste
O conjunto de dados Iris é um dos conjuntos de dados mais populares para projetos de ML. Tem 150 itens de dados e os dados brutos têm a seguinte aparência -
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
Como dito anteriormente, os primeiros quatro valores em cada linha descrevem as propriedades físicas de diferentes variedades, ou seja, comprimento da sépala, largura da sépala, comprimento da pétala, largura da pétala das flores da íris.
Mas, devemos ter que converter os dados no formato, que pode ser facilmente usado pelo CNTK e esse formato é o arquivo .ctf (criamos um iris.ctf na seção anterior também). Será parecido com o seguinte -
|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
Nos dados acima, a tag | attribute marca o início do valor do recurso e a | species marca os valores do rótulo da classe. Também podemos usar qualquer outro nome de tag de nossa preferência, até mesmo podemos adicionar o ID do item. Por exemplo, observe os seguintes dados -
|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
…
Há um total de 150 itens de dados no conjunto de dados da íris e, para este exemplo, usaremos a regra do conjunto de dados de retenção 80-20, ou seja, 80% (120 itens) dos itens de dados para fins de treinamento e 20% (30 itens) restantes dos itens de dados para teste objetivo.
Construindo modelo de classificação
Primeiro, precisamos processar os arquivos de dados no formato CNTK e, para isso, usaremos a função auxiliar chamada create_reader como segue -
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
Agora, precisamos definir os argumentos de arquitetura para nosso NN e também fornecer a localização dos arquivos de dados. Isso pode ser feito com a ajuda do seguinte código 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)
Agora, com a ajuda da seguinte linha de código, nosso programa criará o NN não treinado -
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)
Agora, uma vez que criamos o modelo dual não treinado, precisamos configurar um objeto de algoritmo de Learner e depois usá-lo para criar um objeto de treinamento Trainer. Vamos usar o aluno SGD ecross_entropy_with_softmax função de perda -
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])
Codifique o algoritmo de aprendizagem da seguinte forma -
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])
Agora, uma vez que terminamos com o objeto Trainer, precisamos criar uma função de leitor para ler os dados de treinamento -
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 }
Agora é hora de treinar nosso modelo 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))
Depois de concluir o treinamento, vamos avaliar o modelo usando itens de dados de teste -
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)
Depois de avaliar a precisão do nosso modelo NN treinado, iremos usá-lo para fazer uma previsão em dados não vistos -
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])
Modelo de classificação completo
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()
Resultado
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]
Salvando o modelo treinado
Este conjunto de dados Iris tem apenas 150 itens de dados, portanto, levaria apenas alguns segundos para treinar o modelo do classificador NN, mas o treinamento em um grande conjunto de dados com centenas ou mil itens de dados pode levar horas ou mesmo dias.
Podemos salvar nosso modelo para que não tenhamos que mantê-lo do zero. Com a ajuda do seguinte código Python, podemos salvar nosso NN treinado -
nn_classifier = “.\\neuralclassifier.model” #provide the name of the file
model.save(nn_classifier, format=C.ModelFormat.CNTKv2)
A seguir estão os argumentos de save() função usada acima -
O nome do arquivo é o primeiro argumento de save()função. Também pode ser escrito junto com o caminho do arquivo.
Outro parâmetro é o format parâmetro que tem um valor padrão C.ModelFormat.CNTKv2.
Carregando o modelo treinado
Depois de salvar o modelo treinado, é muito fácil carregar esse modelo. Só precisamos usar oload ()função. Vamos verificar isso no exemplo a seguir -
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])
A vantagem do modelo salvo é que, depois de carregar um modelo salvo, ele pode ser usado exatamente como se o modelo tivesse acabado de ser treinado.