CNTK - klasyfikacja sieci neuronowych

W tym rozdziale zajmiemy się klasyfikacją sieci neuronowych za pomocą CNTK.

Wprowadzenie

Klasyfikację można zdefiniować jako proces przewidywania kategorialnych etykiet wyjściowych lub odpowiedzi dla danych wejściowych. Skategoryzowane dane wyjściowe, które będą oparte na tym, czego model nauczył się w fazie szkolenia, mogą mieć postać, na przykład „Czarny” lub „Biały”, „Spam” lub „Brak spamu”.

Z drugiej strony, matematycznie, jest to zadanie przybliżenia funkcji mapującej, powiedzmy f ze zmiennych wejściowych powiedz X do zmiennych wyjściowych powiedz Y.

Klasycznym przykładem problemu klasyfikacyjnego może być wykrywanie spamu w wiadomościach e-mail. Oczywiste jest, że mogą istnieć tylko dwie kategorie wyników, „spam” i „brak spamu”.

Aby zaimplementować taką klasyfikację, musimy najpierw przeprowadzić szkolenie klasyfikatora, w którym wiadomości e-mail „spam” i „bez spamu” byłyby używane jako dane szkoleniowe. Po pomyślnym przeszkoleniu klasyfikator może zostać użyty do wykrycia nieznanej wiadomości e-mail.

Tutaj utworzymy 4-5-3 NN przy użyciu zestawu danych kwiatu tęczówki, który ma następujące parametry -

  • Węzły 4-wejściowe (po jednym dla każdej wartości predyktora).

  • 5-ukrytych węzłów przetwarzania.

  • Węzły 3-wyjściowe (ponieważ w zestawie danych tęczówki istnieją trzy możliwe gatunki).

Ładowanie zbioru danych

Będziemy korzystać z zestawu danych o kwiatach irysa, na podstawie którego będziemy chcieli sklasyfikować gatunki kwiatów irysa na podstawie fizycznych właściwości szerokości i długości działek oraz szerokości i długości płatków. Zbiór danych opisuje właściwości fizyczne różnych odmian kwiatów tęczówki -

  • Długość działki

  • Szerokość działki

  • Długość płatka

  • Szerokość płatka

  • Klasa tj. Iris setosa lub iris versicolor lub iris virginica

Mamy iris.CSVplik, którego używaliśmy również wcześniej w poprzednich rozdziałach. Można go załadować za pomocąPandasbiblioteka. Ale zanim go użyjemy lub załadujemy do naszego klasyfikatora, musimy przygotować pliki szkoleniowe i testowe, aby można było go łatwo używać z CNTK.

Przygotowywanie plików szkoleniowych i testowych

Zbiór danych Iris jest jednym z najpopularniejszych zbiorów danych dla projektów ML. Zawiera 150 pozycji danych, a surowe dane wyglądają następująco -

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

Jak powiedziano wcześniej, pierwsze cztery wartości w każdym wierszu opisują właściwości fizyczne różnych odmian, tj. Długość działek, szerokość działek, długość płatków, szerokość płatków irysów.

Ale powinniśmy przekonwertować dane do formatu, który może być łatwo użyty przez CNTK, a tym formatem jest plik .ctf (utworzyliśmy również jeden iris.ctf w poprzedniej sekcji). Będzie wyglądać następująco -

|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

W powyższych danych znacznik | atrybuty oznacza początek wartości cechy, a | gatunek oznacza wartości etykiety klasy. Możemy również użyć dowolnych innych nazw tagów, nawet jeśli możemy dodać identyfikator przedmiotu. Na przykład spójrz na następujące dane -

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

W zbiorze danych tęczówki znajduje się łącznie 150 elementów danych i dla tego przykładu będziemy używać reguły zbioru danych 80–20 wstrzymanych, tj. 80% (120 elementów) elementów danych do celów szkoleniowych i pozostałe 20% (30 elementów) elementów danych do testowania cel, powód.

Konstruowanie modelu klasyfikacyjnego

Najpierw musimy przetworzyć pliki danych w formacie CNTK i do tego użyjemy funkcji pomocniczej o nazwie create_reader w następujący sposób -

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

Teraz musimy ustawić argumenty architektury dla naszego NN, a także podać lokalizację plików danych. Można to zrobić za pomocą następującego kodu Pythona -

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)

Teraz, z pomocą następującego wiersza kodu, nasz program utworzy nieprzeszkolony 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)

Teraz, gdy stworzyliśmy podwójny nietrenowany model, musimy skonfigurować obiekt algorytmu ucznia, a następnie użyć go do utworzenia obiektu szkoleniowego Trainer. Będziemy używać uczącego się SGD icross_entropy_with_softmax funkcja straty -

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

Zakoduj algorytm uczenia się w następujący sposób -

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

Teraz, kiedy skończyliśmy pracę z obiektem Trainer, musimy stworzyć funkcję czytnika, aby odczytać dane szkoleniowe

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 }

Nadszedł czas, aby wyszkolić nasz model 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))

Kiedy już skończyliśmy szkolenie, oceńmy model za pomocą elementów danych testowych -

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)

Po ocenie dokładności naszego wyszkolonego modelu NN, będziemy go używać do prognozowania niewidocznych danych -

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

Kompletny model klasyfikacji

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

Wynik

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]

Zapisywanie wytrenowanego modelu

Ten zestaw danych Iris zawiera tylko 150 elementów danych, dlatego wytrenowanie modelu klasyfikatora NN zajmie tylko kilka sekund, ale uczenie na dużym zestawie danych zawierającym sto lub tysiące elementów danych może zająć godziny lub nawet dni.

Możemy zapisać nasz model, aby nie musieć zachowywać go od zera. Z pomocą śledzenia kodu Pythona możemy zapisać nasz wyszkolony NN -

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

Poniżej przedstawiono argumenty save() funkcja używana powyżej -

  • Nazwa pliku jest pierwszym argumentem save()funkcjonować. Można go również zapisywać wraz ze ścieżką do pliku.

  • Kolejnym parametrem jest format parametr, który ma wartość domyślną C.ModelFormat.CNTKv2.

Ładowanie wytrenowanego modelu

Po zapisaniu wytrenowanego modelu bardzo łatwo jest go załadować. Musimy tylko użyćload ()funkcjonować. Sprawdźmy to w poniższym przykładzie -

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

Zaletą zapisanego modelu jest to, że po załadowaniu zapisanego modelu można go używać dokładnie tak, jakby model został właśnie wytrenowany.