В этом блоге описаны все шаги и подходы, необходимые для создания модели классификации рентгеновских изображений с использованием таких концепций глубокого обучения, как CNN, Res-Net и т. Д., С библиотекой PyTorch.

Если вам просто скучно читать и практиковать концепции, лучший способ расширить свои знания с помощью забавы - создать собственную модель проекта. Это, безусловно, повысит вашу уверенность в себе и расширит ваше обучение в конкретной области.

В этом блоге мы создаем нашу собственную модель проекта с нуля, охватывая все шаги, необходимые в процессе ее построения. Предпосылки для этой модели - вы должны быть знакомы с концепциями глубокого обучения, такими как нейронная сеть, CNN, модели ResNet и т. Д., И вы должны знать, как использовать их с библиотекой PyTorch. Наша главная цель - найти правильный путь, а не теоретические концепции, лежащие в его основе, так что давайте начнем с этого.

Введение:

В этой модели мы классифицируем рентгеновские снимки легких, чтобы определить, является ли человек нормальным или страдает пневмонией [ воспалением легких, вызванным бактериальной или вирусной инфекцией, при которой воздушные мешочки заполняются гноем и могут стать твердым. Воспаление может поражать оба легких (двойная пневмония) или только одно (простая пневмония)]. Поскольку мы имеем дело с классификацией заболевания, поэтому точность нашей модели становится очень важным, нам нужно максимизировать его, насколько это возможно.

Для этого мы используем CORONA HACK-Chest Xray-dataset, который мы получаем от kaggle (вы можете загрузить данные, щелкнув здесь). Эти данные содержат 5309 тренировок. изображения и 624 тестовых изображения. Мы используем различные модели, чтобы проверить точность наших прогнозов. Все взятые фрагменты и код запускаются на kaggle Notebook (включая файл данных).

Мы разделяем весь процесс моделирования на 5 этапов: -

1. Подготовка данных

2. Исследование данных

3. Выбор модели и определение архитектуры модели.

4. Модель обучения и проверки

5. Тестовая модель

1. Подготовка данных

Первым шагом к определению любой модели является подготовка данных для модели. Поскольку модели глубокого обучения в основном зависят от данных, то есть чем больше данных они получают, тем лучше они работают.

Наш набор данных содержит 3 файла:

а) Coronahack-Chest-XRay-Dataset - T его папка содержит 5309 тренировочных изображений в папке поездов и 624 тестовых изображения в тестовой папке.

б) Chest_xray_Corona_Metadata.csv - этот файл содержит подробную информацию обо всех изображениях, таких как имя изображения, метка, тип_набора данных и т. д., в формате csv.

c) Chest_xray_Corona_dataset_Summary.csv - этот файл содержит сводку данных в формате csv.

Сначала загрузите CSV-файл с метаданными и визуализируйте его -

Теперь разделите поезд и тестовый набор -

Итак, наша задача - загрузить данные из папки изображений в csv под их конкретными именами, затем разделить файл csv для обучения и тестирования csv, затем пометить данные файлов 0,1 и, наконец, преобразовать изображения (jpeg, png, jpg и т. д.) в матрицы пикселей и каналов (1 * 32 * 32), которые может прочитать наша модель. Для этого мы используем класс PyTorch Dataset Class (из torch.utils.data import Dataset)

Разделение набора поездов на набор для обучения и набор для проверки -

2. Исследование данных

Этот шаг охватывает исследование данных, поскольку наши данные состоят из изображений, поэтому мы охватываем выборку изображений, просмотр некоторых изображений из набора для обучения и проверки, просмотр пакета изображений, проверку того, что все изображения должны быть помечены, и другие вещи по вашему выбору. я буду использовать только несколько из них.

Давайте визуализируем некоторые изображения из набора для обучения и проверки -

В моделях глубокого обучения в большинстве случаев у нас есть огромный набор данных с большими векторами признаков, поэтому лучший подход - разделить данные на пакеты, а затем передать эти пакеты данных в модель. Это очень помогает за счет сокращения количества времени

batch_size = 64

train_loader = DataLoader(train_ds, batch_size, shuffle=True,num_workers=3,pin_memory=True)
val_loader = DataLoader(val_ds, batch_size*2,num_workers=3,pin_memory=True)

Теперь давайте визуализируем одну партию обучающего набора -


for xb,yb in train_loader:
    plt.figure(figsize=(12,12))
    plt.imshow(make_grid(xb).permute(1,2,0))
    break

3. Выбор модели и определение ее архитектуры.

После загрузки данных в соответствии с потребностями модели следующим шагом будет ее построение. Выбор модели полностью основан на потребности в данных. Для классификации изображений модель, которая, вероятно, дает лучший результат с простой структурой и пониманием, - это CNN (сверточная нейронная сеть). Существует несколько способов построить модель, но всегда хорошо начинать с простых.

  • Вот почему сначала мы создадим простую модель нейронной логистической регрессии.
  • Затем мы создаем сверточную нейронную сеть (CNN).
  • И в конце мы объединяем CNN с концепциями остатков и создаем модель ResNet.

Наша основная цель - проверить, какая модель лучше всего подходит для наших данных. Как я уже говорил ранее, глубокое обучение всегда основывается на данных, которые мы получаем, вы никогда не можете быть уверены, что эта модель лучше всего или не только по ее архитектуре, ее производительность полностью основана на об обучении и используемом наборе данных.

Мы создаем функцию точности, чтобы проверить точность модели, проверяя прогнозируемые результаты с фактическими метками:
def accuracy(outputs,labels):
_,preds=torch.max(outputs,dim=1)
return torch.tensor(torch.sum(preds==labels).item()/len(preds))

Сначала создадим простую модель нейронной логистической регрессии -

class LogisticModel(nn.Module):
    def __init__(self):
        super().__init__()
        self.linear = nn.Linear(input_size, output_size)
        
    def forward(self, xb):
        xb = xb.reshape(-1, 1024)
        out = self.linear(xb)
        return out
    
    def training_step(self, batch):
        images, labels = batch 
        out = self(images)                  # Generate predictions
        loss = F.cross_entropy(out, labels) # Calculate loss
        return loss
    
    def validation_step(self, batch):
        images, labels = batch 
        out = self(images)                    # Generate predictions
        loss = F.cross_entropy(out, labels)   # Calculate loss
        acc = accuracy(out, labels)           # Calculate accuracy
        return {'val_loss': loss, 'val_acc': acc}
        
    def validation_epoch_end(self, outputs):
        batch_losses = [x['val_loss'] for x in outputs]
        epoch_loss = torch.stack(batch_losses).mean()   # Combine losses
        batch_accs = [x['val_acc'] for x in outputs]
        epoch_acc = torch.stack(batch_accs).mean()      # Combine accuracies
        return {'val_loss': epoch_loss.item(), 'val_acc': epoch_acc.item()}
    
    def epoch_end(self, epoch, result):
        print("Epoch [{}], val_loss: {:.4f}, val_acc: {:.4f}".format(epoch, result['val_loss'], result['val_acc']))

Теперь давайте создадим модель CNN - сначала определите базовый класс CNN

def accuracy(outputs,labels): #this function is used to check accuracy of model
    _,preds=torch.max(outputs,dim=1)
    return torch.tensor(torch.sum(preds==labels).item()/len(preds))

class cnnBase(nn.Module):
    def training_step(self,batch):
        xb,labels=batch
        out=self(xb)
        loss=F.cross_entropy(out,labels)
        return loss
    def validation_step(self, batch):
        images, labels = batch 
        out = self(images)                    # Generate predictions
        loss = F.cross_entropy(out, labels)   # Calculate loss
        acc = accuracy(out, labels)           # Calculate accuracy
        return {'val_loss': loss.detach(), 'val_acc': acc}
        
    def validation_epoch_end(self, outputs):
        batch_losses = [x['val_loss'] for x in outputs]
        epoch_loss = torch.stack(batch_losses).mean()   # Combine losses
        batch_accs = [x['val_acc'] for x in outputs]
        epoch_acc = torch.stack(batch_accs).mean()      # Combine accuracies
        return {'val_loss': epoch_loss.item(), 'val_acc': epoch_acc.item()}
    
    def epoch_end(self, epoch, result):
        print("Epoch [{}], train_loss: {:.4f}, val_loss: {:.4f}, val_acc: {:.4f}".format(
            epoch, result['train_loss'], result['val_loss'], result['val_acc']))

Теперь расширите этот базовый класс классом модели CNN, который придает этой модели архитектуру CNN -

class CnnModel(cnnBase):
    def __init__(self):
        super().__init__()
        self.network=nn.Sequential(
                     nn.Conv2d(1,32,kernel_size=3,stride=1,padding=1),
                     nn.ReLU(),
                     nn.Conv2d(32,64,kernel_size=3,stride=1,padding=1),
                     nn.ReLU(),
                     nn.MaxPool2d(2,2), #64*16*16
            
                     nn.Conv2d(64, 128, kernel_size=3, stride=1, padding=1),
                     nn.ReLU(),
                     nn.Conv2d(128, 128, kernel_size=3, stride=1, padding=1),
                     nn.ReLU(),
                     nn.MaxPool2d(2,2), # output: 128*8*8

                     nn.Conv2d(128, 256, kernel_size=3, stride=1, padding=1),
                     nn.ReLU(),
                     nn.Conv2d(256, 256, kernel_size=3, stride=1, padding=1),
                     nn.ReLU(),
                     nn.MaxPool2d(2,2), # output: 256*4*4

                     nn.Flatten(), 
                     nn.Linear(256*4*4, 1024),
                     nn.ReLU(),
                     nn.Linear(1024, 512),
                     nn.ReLU(),
                     nn.Linear(512, 2))
        
    def forward(self, xb):
        return self.network(xb)

Теперь давайте определим модель ResNet - сначала определим базовый класс, содержащий этапы обучения, проверки и т. д.

class ResNetBase(nn.Module):
    def training_step(self, batch):
        images, labels = batch 
        out = self(images)                  # Generate predictions
        loss = F.cross_entropy(out, labels) # Calculate loss
        return loss
    
    def validation_step(self, batch):
        images, labels = batch 
        out = self(images)                    # Generate predictions
        loss = F.cross_entropy(out, labels)   # Calculate loss
        acc = accuracy(out, labels)           # Calculate accuracy
        return {'val_loss': loss.detach(), 'val_acc': acc}
        
    def validation_epoch_end(self, outputs):
        batch_losses = [x['val_loss'] for x in outputs]
        epoch_loss = torch.stack(batch_losses).mean()   # Combine losses
        batch_accs = [x['val_acc'] for x in outputs]
        epoch_acc = torch.stack(batch_accs).mean()      # Combine accuracies
        return {'val_loss': epoch_loss.item(), 'val_acc': epoch_acc.item()}
    
    def epoch_end(self, epoch, result):
        print("Epoch [{}],train_loss: {:.4f}, val_loss: {:.4f}, val_acc: {:.4f}".format(
            epoch,result['train_loss'], result['val_loss'], result['val_acc']))

После этого расширяет этот базовый класс с помощью модели ResNet, которая дает ему структуру модели ResNet -

def conv_block(in_channels, out_channels, pool=False):
    layers = [nn.Conv2d(in_channels, out_channels, kernel_size=3, padding=1), 
              nn.BatchNorm2d(out_channels), 
              nn.ReLU(inplace=True)]
    if pool: layers.append(nn.MaxPool2d(2))
    return nn.Sequential(*layers)

class ResNet(ResNetBase):
    def __init__(self, in_channels, num_classes):
        super().__init__()
        
        self.conv1 = conv_block(in_channels, 64) 
        self.conv2 = conv_block(64, 128, pool=True)# 128*16*16
        self.res1 = nn.Sequential(conv_block(128, 128), conv_block(128, 128))
        
        self.conv3 = conv_block(128, 256, pool=True) # 256*8*8
        self.conv4 = conv_block(256, 512, pool=True) # 512*4*4
        self.res2 = nn.Sequential(conv_block(512, 512), conv_block(512, 512))
        
        self.classifier = nn.Sequential(nn.MaxPool2d(4), 
                                        nn.Flatten(), 
                                        nn.Linear(512, num_classes))
        
    def forward(self, xb):
        out = self.conv1(xb)
        out = self.conv2(out)
        out = self.res1(out) + out
        out = self.conv3(out)
        out = self.conv4(out)
        out = self.res2(out) + out
        out = self.classifier(out)
        return out

4. Модель обучения и проверки

После определения архитектуры модели следующим шагом будет обучение модели на обучающих данных, а затем оценка производительности модели на данных проверки. Это даст вам приблизительное представление о производительности вашей модели. На этом этапе вы можете опробовать различные гиперпараметры. , различное количество итераций и т. д., чтобы увидеть, какой из них лучше всего подходит для модели и дает максимальную точность.

После завершения этапа обучения мы должны проверить нашу модель, передав эти параметры, и проверить точность набора для проверки.

Вы можете построить график точности с количеством итераций, чтобы увидеть рост модели с итерациями, или построить график train_loss и val_loss, чтобы увидеть, переоборудована ли наша модель или нет.

Обучающая модель нейронной логистической регрессии -

def evaluate(model, val_loader):
    outputs = [model.validation_step(batch) for batch in val_loader]
    return model.validation_epoch_end(outputs)

def fit(epochs, lr, model, train_loader, val_loader, opt_func=torch.optim.SGD):
    history = []
    optimizer = opt_func(model.parameters(), lr)
    for epoch in range(epochs):
        # Training Phase 
        for batch in train_loader:
            loss = model.training_step(batch)
            loss.backward()
            optimizer.step()
            optimizer.zero_grad()
        # Validation phase
        result = evaluate(model, val_loader)
        model.epoch_end(epoch, result)
        history.append(result)
    return history

Теперь давайте посмотрим на результат логистической модели.

Используя эту модель, мы получаем точность проверки около 80%, это неплохое начало для такой очень простой модели.

Обучающая модель CNN -

@torch.no_grad()
def evaluate(model, val_loader): #This function is used to evaluate validation set
    model.eval()
    outputs = [model.validation_step(batch) for batch in val_loader]
    return model.validation_epoch_end(outputs)

def fit(epochs, lr, model, train_loader, val_loader, opt_func=torch.optim.SGD):
    history = []
    optimizer = opt_func(model.parameters(), lr)
    for epoch in range(epochs):
        # Training Phase 
        model.train()
        train_losses = []
        for batch in train_loader:
            loss = model.training_step(batch)
            train_losses.append(loss)
            loss.backward()
            optimizer.step()
            optimizer.zero_grad()
        # Validation phase
        result = evaluate(model, val_loader)
        result['train_loss'] = torch.stack(train_losses).mean().item()
        model.epoch_end(epoch, result)
        history.append(result)
    return history

Теперь давайте посмотрим на результат модели CNN -

Мы получаем точность проверки около 97% всего за 8 итераций, что довольно хорошо, чем у нашей предыдущей модели. Вы можете еще больше повысить ее, повторяя ее несколько раз или изменив значения скорости обучения.

Обучающая модель ResNet9 -

#Training the Model
@torch.no_grad()
def evaluate(model, val_loader):
    model.eval()
    outputs = [model.validation_step(batch) for batch in val_loader]
    return model.validation_epoch_end(outputs)

def fit_one_cycle(epochs, lr, model, train_loader, val_loader, 
                  opt_func=torch.optim.SGD):
    torch.cuda.empty_cache()
    history = []
    
    # Set up cutom optimizer with weight decay
    optimizer = opt_func(model.parameters(), lr)
    
    for epoch in range(epochs):
        # Training Phase 
        model.train()
        train_losses = []
        
        for batch in train_loader:
            loss = model.training_step(batch)
            train_losses.append(loss)
            loss.backward()
            
            optimizer.step()
            optimizer.zero_grad()
            
        # Validation phase
        result = evaluate(model, val_loader)
        result['train_loss'] = torch.stack(train_losses).mean().item()
        model.epoch_end(epoch, result)
        history.append(result)
    return history

Давайте посмотрим на результат модели ResNet9 -

Мы получаем точность около 95% после всего 4 итераций модели, что довольно хорошо, и эта модель требует минимального времени для вычисления результата. Эту точность можно дополнительно улучшить, увеличив количество итераций или попробовав другие значения гиперпараметров.

Выполняя моделирование с помощью ResNet, мы можем внести несколько улучшений в функцию соответствия и архитектуру модели, что поможет нам повысить производительность нашей модели:

  1. Пакетная нормализация
  2. Скорость обучения Планирование
  3. Снижение веса
  4. Градиентная обрезка
  5. Оптимизатор адама

Поскольку эта модель не слишком сложна, использовать их здесь не обязательно, но вы можете их опробовать.

5. Тестовая модель

После всех вышеперечисленных шагов последним шагом является проверка точности нашей модели путем тестирования с помощью Test Dataset. Это расскажет вам о том, как на самом деле работает модель. К концу этого вы можете изменить свою модель дальше, чтобы получить лучшие результаты.

Результат модели логистической регрессии на тестовых данных -

Результат модели CNN на тестовых данных -

Результат модели ResNet на тестовых данных -

Вывод:

Сравнивая все модели, мы можем легко сделать вывод, что лучший результат дает ResNet Model всего за 4 итерации, CNN Model также дает впечатляющие результаты.

Следует отметить один ключевой момент: все эти точности мы получили, просто выполнив от 5 до 8 итераций модели, что делает модель эффективной по отношению ко времени, и с этим небольшим набором данных такие модели, как CNN и ResNet, смогут обеспечить точность около Точность 95%. Эту точность можно легко увеличить, увеличивая количество итераций или применяя различные итерации.

На этом мы завершаем пятиступенчатый подход, необходимый для построения модели. Между ними есть и другие различные этапы, такие как перемещение модели и значений данных в графический процессор и т. Д.

Все фрагменты, которые я использовал, предназначены только для понимания и прояснения модели. Здесь вы можете найти весь код проекта и начать практиковаться (Файл кода).

Я надеюсь, что это поможет вам немного понять, как начать писать свою собственную модель. Как я уже сказал ранее, глубокое обучение - это практика и обучение, так что начните практиковаться и учиться. Спасибо, что прочитали эту статью, если вам понравилась эта статья, пожалуйста, хлопните в ладоши и поделитесь с друзьями. А пока до свидания с моей стороны ..

Удачного обучения 😊😊😊