Реализация нейронной сети в распознавании изображений
Наша следующая задача — обучить нейронную сеть с помощью ранее размеченных изображений классифицировать новые тестовые изображения. Поэтому мы будем использовать модуль nn для построения нашей нейронной сети.
Существуют следующие шаги для реализации нейронной сети для распознавания изображений:
На первом этапе мы определим класс, который будет использоваться для создания экземпляров нашей нейронной модели. Этот класс будет унаследован от модуля nn, поэтому сначала нам нужно импортировать пакет nn.
from torch import nn class classifier(nn.Module):
За нашим классом будет следовать метод init(). В init() первый аргумент всегда будет self, вторым аргументом будет количество входных узлов, которые мы будем вызывать, третьим аргументом – количество узлов в скрытом слое, четвертым будет количество узлов во втором скрытом слое, а последним аргументом будет количество узлов в выходном слое.
def __init__(self,input_layer,hidden_layer1,hidden_layer2,output_layer):
На втором этапе мы вызываем метод init() для предоставления различных методов и атрибутов и инициализируем входной слой, как скрытый, так и выходной. Важно помнить, что мы будем иметь дело с полносвязной нейронной сетью.
super(),__init__() self.linear1=nn.Linear(input_layer,hidden_layer1) self.linear2=nn.Linear(hidden_layer1,hidden_layer2) self.linear3=nn.Linear(hidden_layer2,output_layer) def __init__(self,input_layer,hidden_layer1,hidden_layer2,output_layer):
Теперь мы сделаем прогноз, но перед этим мы импортируем пакет torch.nn.functional, а затем воспользуемся функцией forward() и поместим self в качестве первого аргумента и x для любых входных данных, которые мы попытаемся спрогнозировать.
import torch.nn.functional as func def forward(self,x):
Теперь любой ввод, который будет передан в функцию forward(), будет передан объекту linear1, и мы будем использовать relu функцию , а не сигмоидную. Вывод этого будет поступать в качестве ввода в наш второй скрытый слой, а вывод второго скрытого слоя будет подаваться в выходной слой и возвращать вывод последнего слоя.
Примечание. Мы не будем применять какие-либо функции активации в выходном слое, если имеем дело с мультиклассовым набором данных.
x=func.relu(self.linear1(x)) x=func.relu(self.linear2(x)) x=self.linear3(x) return x
На следующем этапе мы настроим конструктор модели. В соответствии с нашим инициализатором мы должны установить входные размеры, размеры скрытого слоя и выходные размеры.
Интенсивность пикселей изображения будет передана нашему входному слою. Поскольку каждое изображение имеет размер 28 * 28 пикселей, всего 784 пикселя будут переданы в нашу нейронную сеть. Итак, мы передадим 784 в качестве первого аргумента, мы возьмем 125 и 60 узлов в первом и втором скрытом слое, а в выходном слое мы возьмем десять узлов.
model=classification1(784,125,65,10)
Теперь мы определим нашу функцию потерь. nn.CrossEntropyLoss() используется для многоклассовой классификации. Эта функция представляет собой комбинацию функции log_softmax() и функции NLLLoss(), которая представляет собой отрицательную логарифмическую потерю. Мы используем кросс-энтропию для любой задачи обучения и классификации с n классами. Таким образом, она использует логарифмические вероятности, поэтому мы передаем вывод строки, а не вывод функции активации softmax.
criteron=nn.CrossEntropyLoss()
После этого воспользуемся уже знакомым нам оптимизатором, т.е. Adam:
optimizer=torch.optim.Adam(model.parameters(),lr=0.001)
На следующем шаге мы укажем количество эпох. Мы инициализируем количество эпох и анализируем потери в каждую эпоху с помощью графика. Мы инициализируем два списка, т.е. loss_history и correct history.
loss_history=[] correct_history=[]
Мы начнем с итерации по каждой эпохе. Мы должны пройти по каждому отдельному обучающему пакету, предоставленному нам обучающим загрузчиком. Каждый обучающий пакет содержит сто изображений, а также сто меток.
for e in range(epochs): for input, labels in training_loader:
Когда мы перебираем наши пакеты изображений, мы должны сгладить их и изменить их форму с помощью метода просмотра.
Примечание . Форма каждого тензора изображения (1, 28 и 28), что означает в общей сложности 784 пикселя.
В соответствии со структурой нейронной сети входные значения будут умножаться на нашу матрицу весов, соединяющую входной слой с первым скрытым слоем. Чтобы провести это умножение, мы должны сделать наши изображения одномерными. Вместо того, чтобы каждое изображение состояло из 28 строк по два столбца, мы должны свести его в одну строку по 784 пикселя.
inputs=input.view(input.shape[0],-1)
Теперь, с помощью этих входов, мы получаем выход:
С помощью выходных данных мы рассчитаем общую категориальную кросс-энтропийную потерю, и выходные данные в конечном итоге сравниваются с фактическими метками. Мы также определим ошибку на основе кросс-энтропийного критерия. Перед выполнением какой-либо части тренировочного прохода мы должны настроить оптимизатор, как мы это делали раньше.
loss1=criteron(outputs,labels) optimizer.zero_grad() loss1.backward() optimizer.step()
Чтобы отслеживать потери в каждую эпоху, мы инициализируем переменную loss, т. е. running_loss. Для каждой потери, которая рассчитывается как для партии, мы должны сложить все для каждой отдельной партии, а затем вычислить окончательные потери для каждой эпохи.
Теперь мы добавим этот накопленный убыток за всю эпоху в наш список убытков. Для этого мы используем оператор else после оператора цикла. Итак, после завершения цикла for вызывается оператор else. В этом операторе else мы будем печатать накопленные потери, которые были рассчитаны для всего набора данных в эту конкретную эпоху.
epoch_loss=loss/len(training_loader) loss_history.append(epoch_loss)
Следующим шагом мы найдем точность нашей сети. Инициализируем правильную переменную и присвоим ей нулевое значение. Мы сравним прогнозы, сделанные моделью для каждого обучающего изображения, с фактическими метками изображений, чтобы показать, сколько из них оправдались в течение эпохи.
Для каждого изображения мы возьмем максимальное значение балла. В таком случае возвращается кортеж. Первое значение, которое он возвращает, — это фактическое верхнее значение — максимальный балл, который был получен моделью для каждого отдельного изображения в этом пакете изображений.
Итак, первое значение кортежа нас не интересует, а второе будет соответствовать лучшим прогнозам, сделанным моделью, которую мы назовем preds. Он вернет индекс максимального значения для этого изображения.
Каждое выходное изображение будет представлять собой набор значений с индексами в диапазоне от 0 до 9, так что набор данных MNIST содержит классы от 0 до 9. Отсюда следует, что прогноз, при котором определяется максимальное значение, соответствует прогнозу, сделанному моделью. Мы сравним все эти прогнозы, сделанные моделью, с фактическими метками изображений, чтобы увидеть, сколько из них они оправдали.
correct+=torch.sum(preds==labels.data)
Это даст количество правильных прогнозов для каждой отдельной партии изображений. Мы определим точность эпохи так же, как потерю эпохи, и напечатаем как потерю эпохи, так и точность:
epoch_acc=correct.float()/len(training_loader) print('training_loss:,'.format(epoch_loss,epoch_acc.item()))
Это даст ожидаемый результат:
Теперь мы добавим точность для всей эпохи в наш список correct_history, и для лучшей визуализации мы отобразим как потерю эпохи, так и точность.
plt.plot(loss_history,label='Running Loss History') plt.plot(correct_history,label='Running correct History')
Полный код
import torch import matplotlib.pyplot as plt import numpy as np import torch.nn.functional as func from torch import nn from torchvision import datasets, transforms transform1=transforms.Compose([transforms.ToTensor(),transforms.Normalize((0.5,),(0.5,))]) training_dataset=datasets.MNIST(root='./data',train=True,download=True,transform=transform1) training_loader=torch.utils.data.DataLoader(dataset=training_dataset,batch_size=100,shuffle=True) def im_convert(tensor): image=tensor.clone().detach().numpy() image=image.transpose(1,2,0) print(image.shape) image=image*(np.array((0.5,0.5,0.5))+np.array((0.5,0.5,0.5))) image=image.clip(0,1) return image dataiter=iter(training_loader) images,labels=dataiter.next() fig=plt.figure(figsize=(25,4)) for idx in np.arange(20): ax=fig.add_subplot(2,10,idx+1) plt.imshow(im_convert(images[idx])) ax.set_title([labels[idx].item()]) class classification1(nn.Module): def __init__(self,input_layer,hidden_layer1,hidden_layer2,output_layer): super().__init__() self.linear1=nn.Linear(input_layer,hidden_layer1) self.linear2=nn.Linear(hidden_layer1,hidden_layer2) self.linear3=nn.Linear(hidden_layer2,output_layer) def forward(self,x): x=func.relu(self.linear1(x)) x=func.relu(self.linear2(x)) x=self.linear3(x) return x model=classification1(784,125,65,10) criteron=nn.CrossEntropyLoss() optimizer=torch.optim.Adam(model.parameters(),lr=0.0001) epochs=12 loss_history=[] correct_history=[] for e in range(epochs): loss=0.0 correct=0.0 for input,labels in training_loader: inputs=input.view(input.shape[0],-1) outputs=model(inputs) loss1=criteron(outputs,labels) optimizer.zero_grad() loss1.backward() optimizer.step() _,preds=torch.max(outputs,1) loss+=loss1.item() correct+=torch.sum(preds==labels.data) else: epoch_loss=loss/len(training_loader) epoch_acc=correct.float()/len(training_loader) loss_history.append(epoch_loss) correct_history.append(epoch_acc) print('training_loss:,'.format(epoch_loss,epoch_acc.item()))