Руководство по выполнению комплексных проектов компьютерного зрения с PyTorch-Lightning, Comet ML и Gradio
Компьютерное зрение на данный момент является модным словечком. Однако создание успешных проектов в этой области сопряжено со многими проблемами. Это потому, что эти проекты требуют больших знаний математики, мощности компьютера и времени. К счастью, для решения этих проблем появилось множество современных фреймворков и библиотек.
Сегодня я расскажу вам, как реализовать сквозной проект классификации изображений с помощью библиотек Lightning, Comet ML и Gradio. Во-первых, мы создадим модель глубокого обучения с помощью Lightning. После этого мы будем отслеживать гиперпараметры, мониторить метрики и сохранять модель с помощью Comet ML. Наконец, мы создадим приложение для обнаружения рака с помощью Gradio. После завершения нашего проекта это приложение будет выглядеть так:
Вот темы, которые мы рассмотрим в этом блоге:
- Что такое PyTorch-Lightning и Comet ML?
- Подготовка данных с помощью модуля данных Lightning
- Построение модели с помощью Lightning Module
- Мониторинг метрик с Comet ML
- Создание приложения для обнаружения рака с помощью Gradio
Давайте сначала взглянем с высоты птичьего полета на то, что такое Lightning и Comet ML.
PyTorch-молния
Как вы знаете, PyTorch — популярный фреймворк для построения моделей глубокого обучения. Однако, когда вы увеличиваете сложность модели, этапы моделирования, скорее всего, станут беспорядочными. Тут в дело вступает Молния.
Lightning — это высокоуровневая и гибкая платформа глубокого обучения, упрощающая управление кодом PyTorch. Используя Lightning, вы можете автоматизировать задачи обучения, такие как построение модели, загрузка данных, создание контрольных точек модели и ведение журнала. Чтобы начать работу с этим фреймворком, Lightning за 15 минут — отличный учебник.
Комета МЛ
В конце концов, наша цель — получить лучшую модель. Для этого мы стремимся найти оптимальное сочетание гиперпараметров. Вот тут-то и вступает в игру Комета МЛ.
Comet ML – это платформа MLOps, которая помогает отслеживать гиперпараметры, контролировать показатели модели и оптимизировать модель. Чтобы использовать платформу Comet ML, вы можете создать бесплатную учетную запись, посетив Comet. Если вы используете Comet ML впервые, вы можете ознакомиться с кратким руководством.
Давайте продолжим и воплотим то, о чем мы говорили, в действие с проектом.
Классификация изображений для обнаружения рака
Как мы все знаем, рак — это сложное и распространенное заболевание, от которого страдают миллионы людей во всем мире. К счастью, последние технологии компьютерного зрения позволили обнаружить рак по изображениям. В этом разделе мы расскажем, как это сделать.
Набор данных, который мы собираемся использовать, — это набор данных гистопатологического обнаружения рака.
Этот набор данных состоит из 327 680 цветных изображений, полученных путем извлечения их из гистопатологических сканов срезов лимфатических узлов. Если вы не знакомы с этой областью, не волнуйтесь. Наша цель — построить модель, которая сможет определить наличие канцерогенных элементов на изображении.
Пожалуйста, имейте в виду, что вы можете найти блокнот, который мы собираемся использовать в этом блоге, здесь. Начнем с изучения меток изображений.
# Reading the labels cancer_labels = pd.read_csv("train_labels.csv") # Visualizing the labels labels_count = cancer_labels.label.value_counts() plt.pie(labels_count, labels=['No Cancer', 'Cancer'], startangle=180, autopct='%1.1f') plt.figure(figsize=(16,16)) plt.show()
Как видите, ярлыков «не рак» немного больше. Давайте перейдем к рассмотрению нескольких изображений в наборе данных.
# Visualizing the images base_dir = 'histopathologic-cancer-detection/' fig = plt.figure(figsize=(25, 6)) train_imgs = os.listdir(base_dir+"train") for idx, img in enumerate(np.random.choice(train_imgs, 20)): ax = fig.add_subplot(2, 20//2, idx+1, xticks=[], yticks=[]) im = Image.open(base_dir+"train/" + img) plt.imshow(im) lab = cancer_labels.loc[cancer_labels['id'] == img.split('.')[0], 'label'].values[0] ax.set_title('Label: %s'%lab)
Как видите, каждое изображение снабжено бинарной меткой, показывающей наличие метастатической ткани. Переходим к этапу предварительной обработки данных.
Предварительная обработка данных
Как мы уже упоминали, набор данных состоит из 327 680 цветных изображений, но мы собираемся использовать 10 000 случайно выбранных изображений из этого набора данных. Для этого сначала выберем 8000 индексов для обучающего набора и 2000 индексов для тестового набора.
# Selecting randomly the indexes from the dataset np.random.seed(0) train_imgs_orig = os.listdir(f"{base_dir}/train") selected_image_list = [] for img in np.random.choice(train_imgs_orig, 10000): selected_image_list.append(img) # Creating index variables for the training and test sets np.random.shuffle(selected_image_list) cancer_train_idx = selected_image_list[:8000] cancer_test_idx = selected_image_list[8000:10000]
Отлично, мы создали индексные переменные для выбора изображений. Теперь давайте сохраним изображения в каталогах train и test, используя эти индексы.
# Saving the training images os.mkdir('cancer_train_dataset/') for fname in cancer_train_idx: src = os.path.join(f'{base_dir}/train', fname) dst = os.path.join('cancer_train_dataset', fname) shutil.copyfile(src, dst) # Saving the test images os.mkdir('cancer_test_dataset/') for fname in cancer_test_idx: src = os.path.join(f'{base_dir}/train', fname) dst = os.path.join('cancer_test_dataset/', fname) shutil.copyfile(src, dst)
Теперь давайте создадим фрейм данных Pandas, содержащий метки обучающего и тестового изображений.
# Creating a dataframe with labels selected_image_labels = pd.DataFrame() id_list = [] label_list = [] for img in selected_image_list: label_tuple = cancer_labels.loc[cancer_labels['id'] == img.split('.')[0]] id_list.append(label_tuple['id'].values[0]) label_list.append(label_tuple['label'].values[0]) selected_image_labels['id'] = id_list selected_image_labels['label'] = label_list # Creating a variable in dictionary format to use data loading img_class_dict = {k:v for k, v in zip(selected_image_labels.id, selected_image_labels.label)}
Отлично, мы выполнили этапы предварительной обработки данных. Мы готовы загрузить набор данных с помощью Lightning.
Как команде Uber удается упорядочивать свои данные и сплачивать команду? Отслеживание эксперимента кометы. Узнайте больше от Olcay Cirit от Uber.
Загрузка набора данных
Сложно управлять данными, которые будут использоваться для обучения модели. Он включает в себя все необходимые шаги для обработки, загрузки и преобразования данных, а также загрузчики данных для обучения, проверки, тестирования и прогнозирования.
В этом разделе мы увидим, как выполнить эти шаги с помощью Lightning DataModule
, который поможет вам легко создавать модели, не зависящие от набора данных.
Обратите внимание, что мы не можем использовать этот модуль напрямую. Чтобы загрузить данные, нам сначала нужно создать наш собственный класс следующим образом:
# Creating the custom class to load the data class LoadCancerDataset(Dataset): def __init__(self, datafolder, transform, labels_dict={}): self.datafolder = datafolder self.image_files_list = [s for s in os.listdir(datafolder)] self.transform = transform self.labels_dict = labels_dict self.labels = [labels_dict[i.split('.')[0]] for i in self.image_files_list] def __len__(self): return len(self.image_files_list) def __getitem__(self, idx): img_name = os.path.join(self.datafolder, self.image_files_list[idx]) image = Image.open(img_name) image = self.transform(image) img_name_short = self.image_files_list[idx].split('.')[0] label = self.labels_dict[img_name_short] return image, label
Этот класс помогает нам прочитать все изображения в папке. Давайте продолжим и создадим класс DataModule
для выполнения шагов обработки данных.
class CancerDataModule(pl.LightningDataModule): def __init__(self, batch_size, num_workers, data_dir): super().__init__() self.data_dir = data_dir self.batch_size = batch_size self.num_workers = num_workers # Preparing the dataset def prepare_data(self): """ The prepare_data method was intentionally left empty as we have the dataset in our directory. """ pass # Setuping the dataset def setup(self, stage=None): # Assigning train and val datasets to use in dataloaders if stage == "fit" or stage is None: train_set_full = LoadCancerDataset( datafolder=f'{self.data_dir}/cancer_train_dataset', transform=T.Compose([ T.Resize(224), T.RandomHorizontalFlip(), T.RandomVerticalFlip(), T.ToTensor() ]), labels_dict=img_class_dict ) train_set_size = int(len(train_set_full) * 0.9) valid_set_size = len(train_set_full) - train_set_size self.train_ds, self.val_ds = random_split(train_set_full, [train_set_size, valid_set_size]) # Assigning test dataset to use in dataloader if stage == "test" or stage is None: self.test_ds = LoadCancerDataset( datafolder=f'{self.data_dir}/cancer_test_dataset', transform=T.Compose([ T.Resize(224), T.ToTensor()]), labels_dict=img_class_dict ) # Creating the dataloaders def train_dataloader(self): return DataLoader(self.train_ds,batch_size=self.batch_size, num_workers=self.num_workers,shuffle=True) def val_dataloader(self): return DataLoader(self.val_ds,batch_size=self.batch_size, num_workers=self.num_workers,shuffle=False) def test_dataloader(self): return DataLoader( self.test_ds,batch_size=self.batch_size, num_workers=self.num_workers,shuffle=False)
Обратите внимание, что если вы используете чистый PyTorch для кодирования этих шагов, вам нужно написать больше кода. Выполнить эти шаги с помощью Lightning DataModule
совсем несложно.
Отлично, мы создали класс для управления данными. Перейдем к построению модели.
Построение модели с помощью Lightning
На данный момент мы подготовили данные, которые будем использовать для обучения модели. В этом разделе мы собираемся создать архитектуру нашей модели для классификации изображений. Для этого мы собираемся использовать технику трансферного обучения.
Перенос обучения – это метод, который позволяет нам использовать предварительно обученную модель. Что нам нужно сделать, так это адаптировать предварительно обученную модель для нашей задачи. Предварительно обученная модель, которую мы собираемся использовать, — это архитектура ResNet-50. Эта архитектура часто используется для классификации изображений. Он состоит из 50 слоев, содержащих сверточные слои, объединяющие слои и полносвязные слои.
Теперь мы собираемся использовать LightningModule
для построения нашей модели ResNet. Этот модуль помогает нам организовать наш код PyTorch и легко управлять этапами обучения, проверки и тестирования.
class CancerImageClassifier(pl.LightningModule): def __init__(self, learning_rate = 0.001, num_classes = 2): super().__init__() self.learning_rate = learning_rate self.loss_fn = nn.CrossEntropyLoss() self.num_classes = num_classes # Defining metrics self.accuracy = Accuracy(task="binary", num_classes=num_classes) self.f1_score = F1Score(task="binary", num_classes=num_classes) self.history = {'train_loss':[],'train_acc':[],'val_loss':[],'val_acc' : []} # Defining the model architecture self.pretrain_model = resnet50(weights=ResNet50_Weights.DEFAULT) self.pretrain_model.eval() for param in self.pretrain_model.parameters(): param.requires_grad = False self.pretrain_model.fc = nn.Sequential( nn.Linear(self.pretrain_model.fc.in_features, 1024), nn.ReLU(), nn.Dropout(), nn.Linear(1024,self.num_classes) ) # To run data through the model def forward(self, input): output=self.pretrain_model(input) return output def training_step(self, batch, batch_idx): outputs, targets, loss, preds = self._common_step(batch, batch_idx) train_accuracy = self.accuracy(preds, targets) self.history['train_loss'].append(loss.item()) self.history['train_acc'].append(train_accuracy.item()) self.log_dict( {"train_loss": loss,"train_acc": train_accuracy,}, on_step=False, on_epoch=True, prog_bar=True) return {"loss":loss, 'train_acc': train_accuracy} def validation_step(self, batch, batch_idx): outputs, targets, loss, preds = self._common_step(batch, batch_idx) val_accuracy = self.accuracy(preds, targets) self.history['val_loss'].append(loss.item()) self.history['val_acc'].append(val_accuracy.item()) self.log_dict( {"val_loss": loss,"val_acc": val_accuracy}, on_step=False, on_epoch=True, prog_bar=True, ) return {"loss":loss, 'val_acc': val_accuracy} def test_step(self, batch, batch_idx): outputs, targets, loss, preds = self._common_step(batch, batch_idx) test_accuracy = self.accuracy(preds, targets) f1_score = self.f1_score(preds, targets) self.log_dict( {"test_loss": loss, "test_acc": test_accuracy, "test_f1_score": f1_score}, on_step=False, on_epoch=True, prog_bar=True, ) return {"test_loss":loss, "test_accuracy":test_accuracy, "test_f1_score": f1_score} def _common_step(self, batch, batch_idx): inputs, targets = batch outputs = self.forward(inputs) loss = self.loss_fn(outputs, targets) preds = torch.argmax(outputs, dim=1) return outputs, targets, loss, preds def configure_optimizers(self): params = self.parameters() optimizer = optim.Adam(params=params, lr = self.learning_rate) return optimizer
Как видите, код, используемый для построения модели, более понятен и его легче отслеживать, чем чистые коды PyTorch.
Одна из моих любимых особенностей Lightning — простота отслеживания показателей. Здесь мы использовали метод self.log_dict
для регистрации метрик на этапах обучения и тестирования.
Итак, мы создали архитектуру нашей модели. Давайте продолжим и посмотрим, как отслеживать наши гиперпараметры, метрики и модель.
Отслеживание с помощью Comet Logger
У меня для вас отличные новости. Lightning поддерживает платформу Comet ML. Это означает, что вы можете отслеживать гиперпараметры модели, отслеживать показатели модели и регистрировать свою модель в своей учетной записи Comet ML.
В Lightning есть класс CometLogger, который можно использовать для беспрепятственной регистрации метрик, гиперпараметров, весов моделей и многого другого. Просто создайте экземпляр объекта CometLogger и передайте его в Trainer
Lightning. Давай сделаем это.
# Creating an experiment with your API key comet_logger = CometLogger( api_key= "your_api_key", workspace="your_workspace", project_name="your_project_name" )
Отлично, теперь у нас есть объект для отслеживания гиперпараметров и метрик. Что нам нужно сделать, так это передать этот объект в Trainer
, как показано ниже. Теперь мы готовы обучить модель.
Обучение модели
После организации нашего кода PyTorch в LightningModule
Trainer
выполняет все остальное автоматически. Обратите внимание, что объект Trainer
предлагает гораздо больше, чем «обучение». Это помогает нам устанавливать такие параметры, как выбор аппаратного обеспечения (ЦП/ГП/ЦП), периоды обучения и тестирования, регистрация комет, поддержка 16-битного обучения и так далее.
Прежде чем создавать объект тренера, давайте создадим экземпляр объекта DataModule
следующим образом:
my_dataloader = CancerDataModule( batch_size=128, num_workers=2, data_dir="./")
После этого создадим объект модели из класса CancerImageClassifier
.
my_model = CancerImageClassifier( num_classes=2, learning_rate=0.001)
Далее создадим экземпляр объекта Trainer
, а затем вызовем метод fit
для обучения модели.
my_trainer = pl.Trainer( logger=comet_logger, accelerator="auto", devices="auto", max_epochs=15) my_trainer.fit(my_model, my_dataloader)
Когда мы запускаем эти коды, начинается процесс обучения. Вы можете отслеживать показатели на панели управления Comet ML следующим образом:
На данный момент мы обучили нашу модель и изучили ее показатели, такие как точность, потери и оценка F1, на панели управления Comet ML. Перейдем к оценке модели.
Оценка модели
Теперь у нас есть хорошая модель для классификации изображений, но как наша модель работает на тестовом наборе? Чтобы выяснить это, мы можем использовать объект Trainer
, вызвав тестовый метод.
# Model evaluation my_dataloader.setup() my_trainer.test(model=my_model, dataloaders=my_dataloader.test_dataloader())
Как видите, метрики на тестовом наборе близки к метрикам на обучающем наборе. Наконец, давайте сохраним нашу модель в Comet ML и завершим наш эксперимент следующим образом:
# Saving Model in Comet-ML from comet_ml.integration.pytorch import log_model log_model(comet_logger.experiment, my_model, model_name="my_pl_model") # Ending our experiment comet_logger.experiment.end()
Создание приложения с Gradio
Gradio – это библиотека Python, позволяющая создавать демонстрационные приложения. После обучения модели вы можете использовать эту библиотеку для создания простого веб-интерфейса, доступного для просмотра любому пользователю. Во-первых, мы собираемся определить функцию для предсказания метки изображения.
# Defining a function to predict the label of an image def predict(inp): image_transform = transforms.Compose([ transforms.Resize(size=(224,224)), transforms.ToTensor()]) labels = ['normal', 'cancer'] inp = image_transform(inp).unsqueeze(dim=0) with torch.no_grad(): prediction = torch.nn.functional.softmax(model(inp)) confidences = {labels[i]: float(prediction.squeeze()[i]) for i in range(len(labels))} return confidences
Далее давайте создадим интерфейс Gradio, используя следующие коды.
# Creating a Gradio interface gr.Interface(fn=predict, inputs=gr.Image(type="pil"), outputs=gr.Label(num_top_classes=2), title=title, description=description, article=article, examples=['image-1.jpg', 'image-2.jpg']).launch()
Вот и все! Наше приложение готово и выглядит так:
Используя это приложение, вы можете предсказать, является ли изображение метастатической ткани раковым.
Обратите внимание, что вы можете найти файлы, которые я использовал для создания этого приложения, здесь.
Заворачивать
Когда дело доходит до реализации сложных моделей глубокого обучения, вы сталкиваетесь со многими проблемами. К счастью, вы можете решить эти проблемы с помощью современных библиотек, таких как Lightning и Comet ML.
В этом блоге мы узнали, как реализовать комплексный проект глубокого обучения с использованием современных библиотек. Lightning позволяет нам легко упорядочивать коды PyTorch для построения моделей. Класс CometLogger в Lightning помогает нам отслеживать показатели нашей модели. Gradio позволяет нам создать приложение для обнаружения рака.
Вот и все. Спасибо за прочтение. Подключим YouTube | Твиттер | ЛинкедИн.
Ресурсы
Примечание редактора. Heartbeat — это интернет-издание и сообщество, созданное участниками и посвященное предоставлению лучших образовательных ресурсов для специалистов по науке о данных, машинному обучению и глубокому обучению. Мы стремимся поддерживать и вдохновлять разработчиков и инженеров из всех слоев общества.
Независимая от редакции, Heartbeat спонсируется и публикуется Comet, платформой MLOps, которая позволяет специалистам по данным и командам машинного обучения отслеживать, сравнивать, объяснять и оптимизировать свои эксперименты. Мы платим нашим авторам и не продаем рекламу.
Если вы хотите внести свой вклад, перейдите к нашему призыву к участию. Вы также можете подписаться на получение нашего еженедельного информационного бюллетеня (Еженедельник глубокого обучения), заглянуть в блог Comet, присоединиться к нам в Slack и подписаться на Comet в Twitter и LinkedIn для получения ресурсов и событий. и многое другое, что поможет вам быстрее создавать более качественные модели машинного обучения.