Проблема классификации в машинном обучении заключается в том, что входным данным присваивается метка на основе их характеристик. Примерами являются распознавание лиц, обнаружение спама и распознавание цифр. Это метод контролируемого обучения, в котором используются дискретные данные. В зависимости от данных существуют разные типы классификации, т. е. бинарная, многоклассовая и многоуровневая, но в этой статье основное внимание уделяется многоклассовой классификации.
1. Получите и разархивируйте набор данных
Используемые данные получены из Kaggle и содержат 4242 помеченных изображения цветов: ромашки (0), одуванчики (1), розы (2), подсолнухи (3) и тюльпаны (4). В скобках указаны названия цветов.
Загрузка набора данных Kaggle может быть выполнена с помощью следующих шагов:
1. !pip install -q kaggle 2. from google.colab import files 3. upload *kaggle.json* file 4. ! mkdir ~/.kaggle 5. ! cp kaggle.json ~/.kaggle/ 6. ! chmod 600 ~/.kaggle/kaggle.json 7. ! kaggle datasets list 8. ! kaggle datasets download <name of dataset> 9. ! unzip <name-of-file>
2. Импортируйте соответствующие библиотеки
Затем мы импортируем необходимые библиотеки и пакеты. Ведутся споры о том, какой тип фреймворка лучше всего использовать для машинного обучения, но лично я предпочитаю PyTorch, так как его проще использовать и понимать начинающим. Мы также импортируем библиотеки для визуализации изображений и результатов.
import torch import pandas as pd import cv2 import numpy as np import torchvision import torch.nn as nn from torch.utils.data import Dataset import torchvision.transforms as transforms from torch.utils.data.dataloader import DataLoader from torchvision.datasets import ImageFolder import matplotlib.pyplot as plt from PIL import Image from sklearn.model_selection import train_test_split
3. Разделение наборов данных на наборы для обучения, проверки и тестирования.
Прежде чем разделить данные, мы сначала создадим функцию для изменения размера и нормализации изображений. Это связано с тем, что скорость обработки модели сильно зависит от размера изображений, а уменьшение размера изображения означает более быструю обработку. Нормализация данных — это их деление на норму вектора. Он используется для приведения всех числовых значений в наборе данных к одному масштабу без изменения различий в диапазонах или потери информации.
def prepare_imgs(imgs): prep = torchvision.transforms.Compose([ torchvision.transforms.Resize((224, 224)), # smaller sized imagaes give faster results torchvision.transforms.ToTensor(), # Trnsfor to tensor torchvision.transforms.Normalize(mean =[0.6520, 0.6200, 0.5089] , std =[0.2128, 0.2142, 0.3178] ) # Normalise to get data in range ]) transformed_img = prep(imgs) return transformed_img
Затем с помощью функции torchvision.dataset все изображения преобразуются в тензоры, нормализуются, масштабируются и затем сохраняются в папке. После чего папку можно разделить на наборы для обучения, проверки и тестирования.
dataset = torchvision.datasets.ImageFolder('/content/flowers', transform = prepare_imgs) plt.imshow(Image.open(dataset.imgs[3000][0]))
train_set, rem_set = train_test_split(dataset, test_size= 0.2) print('the training set size is: ', len(train_set)) print('Remaining set size is :', len(rem_set))
Разделите оставшийся набор на проверку и тестирование:
validation_set, test_set = train_test_split(rem_set, test_size= 0.05) print('the validation set size is: ', len(validation_set)) print('Test set size is :', len(test_set))
Затем мы используем загрузчик данных для более легкого доступа к набору данных. Как следует из названия, для загрузки данных используется загрузчик данных. Он делает это, создавая мини-пакеты набора данных и перемешивая набор данных, чтобы мы не получали одинаковые изображения.
batch_size = 32 #This is the number of samples used to estimate error gradient before updating the weights num_work= 2 # how many subprocess wil be carried out for dataloding train_loader = DataLoader(train_set, batch_size, shuffle= True, num_workers=num_work) val_loader = DataLoader(validation_set, batch_size, shuffle= True, num_workers=num_work) test_loader =DataLoader(test_set, batch_size, shuffle= True, num_workers=num_work)
4. Создание функции точности
Точность — это процент правильных прогнозов. Он рассчитывается путем деления правильного прогноза на общее количество прогнозов. Мы рассчитываем точность модели, оценивая предсказанные метки по сравнению с фактическими. В этом случае на выходе CNN будет список из 5 длин вероятности каждой метки. Чтобы получить предсказанную метку, мы берем индекс наибольшей вероятности в списке.
def accuracy(output, labels): _, preds = torch.max(output, dim=1) return torch.tensor(torch.sum(preds == labels).item() / len(preds) ) *100
5. Построение CNN (сверточной нейронной сети)
CNN — это среда глубокого обучения, в основном применяемая к изображениям. Он берет входное изображение и применяет к нему методы, чтобы отличить его от других.
Он способен распознавать особенности во входных изображениях, которые служат основой для классификации, а также уменьшает размер изображений для более быстрой обработки. не теряя деталей.
a.) Слой свертки
Слой свертки CNN служит для применения фильтра/ядра к изображению и, таким образом, извлечения признаков. Думайте об этом как о фильтре повышения резкости, который более четко показывает детали изображения.
Это ядро скользит по изображению для всех трех каналов (RGB), пока изображение не будет полностью свернуто. Фильтры часто представляют собой ядра 3x3, но размеры могут различаться, однако они должны представлять собой квадратную матрицу.
Операция свертки выполняется путем точечного умножения изображения и дополненного ядра в области Фурье.
В PyTorch сверточный слой вызывается с помощью torch.nn.Conv2d().
Выход сверточного слоя можно рассчитать следующим образом:
b.) Слой пула
Этот уровень отвечает за уменьшение размера, тем самым уменьшая вычислительную мощность, используемую для обработки изображений. Он также удобен для извлечения из изображения инвариантных к вращению и смещению функций.
Два типа объединения: максимальное и среднее. Максимальный пул берет максимальный пиксель из части изображения, посещаемой ядром. Он также выполняет шумоподавление. В то время как средний пул принимает среднее значение пикселей в части, посещаемой ядром.
В pytorch максимальный пул равен torch.nn.MaxPool2d(), а средний пул равен torch.nn .AvgPool2d()
Выход из слоя пула рассчитывается как:
c.) Полносвязные слои
Эти слои отвечают за соединение нейронов в нейронной сети. Это то, что гарантирует, что компьютер изучит функции, извлеченные из слоев свертки и объединения. Здесь также применяются веса в нейронной сети.
Некоторые линейные слои включают:
– ReLu (выпрямленный линейный слой)
– Softmax
– Линейный слой
- Сигмовидный слой
CNN:
model = nn.Sequential( #layer1 nn.Conv2d(in_channels=3, out_channels=32, kernel_size=3, stride=1, padding=1), # Output size= 16 x224 x224 nn.ReLU(), nn.Conv2d(in_channels=32, out_channels =64,kernel_size=3, stride=1, padding=1), nn.ReLU(), nn.MaxPool2d(2,2),# Output size= 64 x 112 x 112 #Layer 2 nn.Conv2d(in_channels= 64, out_channels=128,kernel_size=3, stride=1, padding=1), nn.ReLU(), nn.Conv2d(in_channels=128, out_channels=128, kernel_size=3, stride=1, padding=1), nn.ReLU(), nn.MaxPool2d(2,2), # Output size= 128 x 56 x 56 #Layer 3 nn.Conv2d(in_channels= 128, out_channels=256,kernel_size=3, stride=1, padding=1), nn.ReLU(), nn.Conv2d(in_channels=256, out_channels=256, kernel_size=3, stride=1, padding=1), nn.ReLU(), nn.MaxPool2d(2,2), # Output size= 256 x 28 x 28 # Fully connected layers nn.Flatten(), nn.Linear(256*28*28, 1024), nn.ReLU(), nn.Linear(1024, 512), nn.ReLU(), nn.Linear(512, 5), ) device= torch.device('cuda' if torch.cuda.is_available() else 'cpu') model.to(device) # Note: # stride in a cnn is resposible for the amount of movement a kernel moves over an image
6. Определение оптимизатора и выбор функции потерь
В кейсе используется оптимизатор adam, подробнее о нем можно узнать по этой ссылке
optimizer =torch.optim.Adam(params=model.parameters(), lr= 0.001)
Кросс-энтропийные потери — это тип функции потерь, наиболее часто используемый в алгоритмах классификации. В классификации каждая метка класса имеет вероятность 1 и нулевую вероятность для других классов. Модель сообщает нам вероятность того, что входное изображение принадлежит каждой метке класса, а потеря перекрестной энтропии вычисляет разницу в распределении вероятностей.
def calc_loss(prediction ,actual): loss = nn.CrossEntropyLoss() return loss(prediction, actual)
7. Обучение модели
Теперь, наконец, пришло время обучить модель! Модель обучается на 30 эпох с записью потерь и точности в каждую эпоху.
def train_model(model, optim, train_data, epochs=30): ''' model: the model to be trained optim: The optimizer chosen in this case Adam train_data: training data ''' #training sequence epoch_loss = [] epoch_accuracy =[] for epoch in range(epochs): for img, label in train_data: #transforming images to tensor and resizing # img =prepare_imgs(img) images already transformed to tensor img =img.to(device) label = label.to(device) #resetting gradients optim.zero_grad() #running img through model preds = model(img) #calculate loss #cross entropy loss a reducing loss is good as the closer to zero the more accurate _loss = calc_loss(preds, label) loss =_loss.item() #Calclulate accuracy acc = accuracy(preds, label) #loss step backwards _loss.backward() optim.step() # results epoch_loss.append(_loss.detach().cpu().numpy()) epoch_accuracy.append(acc.detach().cpu().numpy()) print( 'Epoch: {}'.format(epoch+1), 'loss: {:.4f}'.format(loss), 'accuracy: {:.2f}'.format(acc) ) return epoch_loss, epoch_accuracy loss, acc= train_model(model, optimizer, train_loader)
Затем мы проверяем модель с помощью набора проверки, чтобы проверить точность обученной модели и посмотреть, действительно ли она предсказывает, как должна.
def validation(model, optim, val_data, epochs=30): val_loss =[] val_accuracy =[] for epoch in range(epochs): for img, label in val_data: img =img.to(device) label = label.to(device) #running img through model preds = model(img) #calculate loss loss = calc_loss(preds, label) #Calclulate accuracy acc = accuracy(preds, label) val_accuracy.append(acc.detach().cpu().numpy()) val_loss.append(loss.detach().cpu().numpy()) print( 'Epoch {}'.format(epoch+1), 'loss {:.2f}'.format(loss), 'accuracy {:.2f} '.format(acc)) return val_loss, val_accuracy val_loss, val_acc = validation(model, optimizer, val_loader)
Сохраните обученную модель, чтобы ее можно было загружать снова и снова.
torch.save(model.state_dict(), 'Flowers_class.pth')
8. Оценка модели
Строим диаграмму потерь при обучении и валидации, чтобы лучше понять, что происходит.
Чтобы показать, что модель работает, мы также используем ее для оценки некоторых изображений в тестовом наборе и демонстрации результатов.
Готово! Поздравляем, вы только что построили работающую CNN!