Обзор

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

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

Цель проекта

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

Методология и обоснование

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

Приложения и последствия

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

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

Благодарности

Набор данных скопирован из Zenodo: https://zenodo.org/record/53169#.W6HwwP4zbOQ

Создатель: Катер, Якоб Николас; Зёлльнер, Фрэнк Геррит; Бьянкони, Франческо; Мельчерс, Сюзанна М; Шад, Лотар Р.; Гайзер, Тимо; Маркс, Александр; Вайс, Клео-Арон

Заявление об этике

Все эксперименты были одобрены институциональным советом по этике (совет по медицинской этике II, Университетский медицинский центр Мангейма, Гейдельбергский университет, Германия; одобрение 2015–868R-MA). Институциональный совет по этике отказался от необходимости информированного согласия для ретроспективного анализа анонимных образцов. Все эксперименты проводились в соответствии с утвержденными методическими рекомендациями и Хельсинкской декларацией.

Ссылка

Катер Дж.Н., Вейс К.А., Бьянкони Ф., Мельчерс С.М., Шад Л.Р., Гайзер Т., Маркс А. и Цоллнер Ф. (2016). Многоклассовый текстурный анализ в гистологии колоректального рака. Научные отчеты (в печати).

Импорт библиотек и настройка среды

Для обеспечения единообразия и совместимости установите Tensorflow 2.9.1.

!pip install tensorflow==2.9.1

pydot — это библиотека Python, используемая для создания сложных ориентированных и неориентированных графиков. Это интерфейс для Graphviz, программного обеспечения для визуализации графиков с открытым исходным кодом.

Подробнее о пидоте можно узнать здесь..статья Milan Thapa

!pip install pydot
!pip install graphviz

Системные библиотеки:

  • os: Обеспечивает переносимый способ использования функций, зависящих от операционной системы.
  • time: Предоставляет различные функции, связанные со временем.
  • shutil: Предлагает ряд высокоуровневых операций над файлами и коллекциями файлов.
  • pathlib: Предоставляет различные классы, представляющие пути файловой системы с семантикой, подходящей для разных операционных систем.

Библиотеки вычислений данных:

  • cv2: библиотека OpenCV, используемая для задач компьютерного зрения в реальном времени.
  • numpy: Фундаментальный пакет для научных вычислений на Python.
  • pandas: предлагает структуры данных и операции для управления числовыми таблицами и временными рядами.
  • seaborn: Библиотека визуализации статистических данных на основе matplotlib.
  • matplotlib.pyplot: Предоставляет интерфейс, подобный MATLAB, для создания графиков и диаграмм.
  • sklearn.model_selection: Предоставляет функции для разделения наборов данных.
  • sklearn.metrics: предоставляет функции для показателей производительности модели, такие как матрица ошибок и отчет о классификации.

Библиотеки создания моделей:

  • tensorflow: Мощная библиотека для численных вычислений, хорошо подходящая для крупномасштабного машинного обучения.
  • tensorflow.keras: Реализация TensorFlow спецификации Keras API, API нейронных сетей высокого уровня.
  • Sequential: линейный набор слоев для создания модели нейронной сети.
  • Adam и Adamax: Алгоритмы оптимизации для изменения атрибутов нейронной сети.
  • categorical_crossentropy: Функция потерь, обычно используемая для задач классификации нескольких классов.
  • ImageDataGenerator: используется для увеличения данных в реальном времени.
  • Conv2D, MaxPooling2D, Flatten, Dense, Activation, Dropout, BatchNormalization: различные типы слоев нейронной сети.
  • regularizers: Примените штрафы к параметрам слоя или активности слоя во время оптимизации.
  • plot_model: Утилита для построения графика модели и сохранения его в виде изображения.
# system
import os
import time
import shutil
import pathlib
import itertools

# computation & plotting
import cv2
import numpy as np
import pandas as pd
import seaborn as sns
sns.set_style('darkgrid')
import matplotlib.pyplot as plt
from sklearn.model_selection import train_test_split
from sklearn.metrics import confusion_matrix, classification_report

# model
import tensorflow as tf
from tensorflow import keras
from tensorflow.keras.models import Sequential
from tensorflow.keras.optimizers import Adam, Adamax
from tensorflow.keras.metrics import categorical_crossentropy
from tensorflow.keras.preprocessing.image import ImageDataGenerator
from tensorflow.keras.layers import Conv2D, MaxPooling2D, Flatten, \
Dense, Activation, Dropout, BatchNormalization
from tensorflow.keras import regularizers
from tensorflow.keras.utils import plot_model

import warnings
warnings.filterwarnings('ignore')

Во время создания этого проекта данные были скачаны и загружены на личный диск Google. Если вы используете Google Colab, рекомендуется выполнить ту же процедуру. Если нет, сохраните данные в своем проекте.

# mount google drive to retrieve the files
from google.colab import drive
drive.mount('/content/drive')

После подключения Google-диска разархивируйте файлы.

# unzip image data
!unzip "/content/drive/**replace with your file location**"

Создание функций

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

  • define_paths(dir): Обходит каталог, создавая и возвращая два списка: один с путями всех файлов, найденных во вложенных подкаталогах, а другой с соответствующими метками, взятыми из имен папок.
  • define_df(files, classes): принимает список путей к файлам и список классов, преобразует эти списки в объекты Series pandas, затем объединяет их в DataFrame, где один столбец содержит пути к файлам, а другой столбец содержит соответствующие метки.
  • create_df(data_dir): создает кадры данных для обучения, проверки и тестирования наборов данных, сначала вызывая define_paths(data_dir), чтобы получить пути к файлам и метки, а затем define_df(files, classes), чтобы преобразовать эти списки в DataFrame. Затем этот DataFrame разделяется на наборы для обучения, проверки и тестирования со стратифицированной выборкой на основе меток.
def define_paths(dir):
    filepaths = []
    labels = []

    folds = os.listdir(dir)
    for fold in folds:
        foldpath = os.path.join(dir, fold)
        filelist = os.listdir(foldpath)

        for fold_ in filelist:
            foldpath_ = os.path.join(foldpath, fold_)
            filelist_ = os.listdir(foldpath_)

            for file_ in filelist_:
                fpath = os.path.join(foldpath_, file_)
                filepaths.append(fpath)
                labels.append(fold_)

    return filepaths, labels

def define_df(files, classes):
    Fseries = pd.Series(files, name= 'filepaths')
    Lseries = pd.Series(classes, name='labels')
    return pd.concat([Fseries, Lseries], axis= 1)

def create_df(data_dir):

    # train dataframe
    files, classes = define_paths(data_dir)
    df = define_df(files, classes)

    strat = df['labels']
    train_df, dummy_df = train_test_split(df, train_size=0.7, shuffle=True,
                                          random_state=123, stratify=strat)

    # test dataframe
    strat = dummy_df['labels']
    valid_df, test_df= train_test_split(dummy_df, train_size=0.5, shuffle=True,
                                        random_state=123, stratify=strat)

    return train_df, valid_df, test_df

Создайте данные для модели

  • create_model_data(train_df, valid_df, test_df, batch_size): Эта функция подготавливает данные изображения для ввода в модель. Он использует функцию ImageDataGenerator из Keras для преобразования изображений в тензоры и выполнения увеличения данных для набора обучающих данных.

Параметры функции:

  • train_df, valid_df, test_df: кадры данных, содержащие пути к файлам и метки для обучающих, проверочных и тестовых наборов.
  • batch_size: Размер подмножеств, которые будут использоваться при обучении модели.

Внутри функции:

  • img_size и img_shape: переменные, определяющие входную форму модели. img_size определяет высоту и ширину изображений, а img_shape включает количество каналов.
  • Размер пакета для тестового набора: рассчитывается так, чтобы он был меньше или равен 80 и равномерно делил длину тестового набора данных.
  • scalar: функция предварительной обработки для ImageDataGenerator, которая возвращает входное изображение в том виде, в котором оно есть.
  • ImageDataGenerator: используется для предварительной обработки изображений, преобразования их в формат, подходящий для ввода в нейронную сеть, и выполнения методов увеличения данных.
  • flow_from_dataframe: Метод, который выводит генератор, выдающий пакеты изображений и меток.

Возвраты функций:

  • Функция возвращает три генератора: train_gen, valid_gen и test_gen для обучения, проверки и тестирования модели.

Примечание. test_data создается из набора проверки.

def create_model_data(train_df, valid_df, test_df, batch_size):

    img_size = (150, 150)
    channels = 3
    color = 'rgb'
    img_shape = (img_size[0], img_size[1], channels)

    ts_length = len(test_df)
    divisors = [n for n in range(1, ts_length + 1) if ts_length % n == 0]
    divisors_filtered = [n for n in divisors if ts_length / n <= 80]
    sorted_divisors = sorted(divisors_filtered)
    test_batch_size = max(sorted_divisors)
    test_steps = ts_length // test_batch_size

    def scalar(img):
        return img

    tr_gen = ImageDataGenerator(preprocessing_function = scalar,
                                horizontal_flip= True)
    ts_gen = ImageDataGenerator(preprocessing_function = scalar)

    train_gen = tr_gen.flow_from_dataframe(train_df, x_col = 'filepaths',
                                           y_col = 'labels',
                                           target_size = img_size,
                                           class_mode = 'categorical',
                                        color_mode = color, shuffle = True,
                                        batch_size = batch_size)

    valid_gen = ts_gen.flow_from_dataframe(valid_df, x_col = 'filepaths',
                                           y_col = 'labels',
                                           target_size = img_size,
                                           class_mode = 'categorical',
                                        color_mode = color, shuffle = True,
                                        batch_size = batch_size)

    test_gen = ts_gen.flow_from_dataframe(test_df, x_col = 'filepaths',
                                          y_col = 'labels',
                                          target_size = img_size,
                                          class_mode = 'categorical',
                                        color_mode = color, shuffle = False,
                                        batch_size = test_batch_size)

    return train_gen, valid_gen, test_gen

Отображение образцов из набора данных

  • show_images(gen): Эта функция визуализирует образец изображений из генератора, созданного ImageDataGenerator.

Параметр функции:

  • gen: Генератор, создающий пакеты изображений и меток.

Внутри функции:

  • g_dict и classes: извлекает индексы классов из генератора и создает список имен классов.
  • images, labels = next(gen): извлекает из генератора один пакет изображений и меток.
  • length и sample: определяют количество изображений в пакете и ограничивают размер выборки до 25.
  • plt.figure(figsize= (20, 20)): Инициализирует новую фигуру matplotlib указанного размера.
  • Цикл for: для каждого изображения в образце масштабируется изображение, строится его график, определяется класс изображения, добавляется заголовок с именем класса и отключается ось.
  • plt.show(): Отображение графика.
def show_images(gen):

    g_dict = gen.class_indices
    classes = list(g_dict.keys())
    images, labels = next(gen)

    length = len(labels)
    sample = min(length, 25)

    plt.figure(figsize= (8, 8))

    for i in range(sample):
        plt.subplot(5, 5, i + 1)
        image = images[i] / 150
        plt.imshow((image * 150).astype('uint8'))
        index = np.argmax(labels[i])
        class_name = classes[index]
        plt.title(class_name, color= 'green', fontsize= 12)
        plt.axis('off')
        plt.tight_layout()
    plt.show()

Постройте метки

  • plot_label_count(df, plot_title): подсчитывает частоту каждой метки в DataFrame и создает гистограмму.

Параметры функции:

  • df: DataFrame, содержащий данные, включая столбец «метки».
  • plot_title: Название сюжета.

Внутри функции:

  • vcounts, labels, values: извлекает счетчики для каждой метки в DataFrame и сохраняет имена меток и соответствующие счетчики.
  • lcount: общее количество уникальных ярлыков.
  • Если количество меток не превышает 55, вызывается функция plot_labels для создания графика.
  • plot_labels(lcount, labels, values, plot_title): генерирует гистограмму частот каждой метки.

Параметры функции:

  • lcount: общее количество уникальных ярлыков.
  • labels: Список ярлыков.
  • values: Список соответствующих счетчиков для каждой метки.
  • plot_title: Название сюжета.
  • Внутри функции:
  • width: вычисляет ширину графика (максимальная ширина 20).
  • plt.figure(figsize= (width, 5)): Инициализирует новую фигуру matplotlib указанного размера.
  • sns.barplot(labels, values): Создает гистограмму.
  • Устанавливает заголовок, метки по оси X, метки по оси Y, а также отметки X и Y. Добавляет счетчики в середину столбцов на графике.
  • plt.show(): Отображение графика.
def plot_label_count(df, plot_title):

    vcounts = df['labels'].value_counts()
    labels = vcounts.keys().tolist()
    values = vcounts.tolist()
    lcount = len(labels)

    if lcount > 55:
        print('Error:  55 samples minimum required')

    else:
        plot_labels(lcount, labels, values, plot_title)

def plot_labels(lcount, labels, values, plot_title):
    width = lcount * 4
    width = np.min([width, 20])

    plt.figure(figsize= (width, 5))

    form = {'family': 'serif', 'color': 'blue', 'size': 25}
    sns.barplot(labels, values)
    plt.title(f'Images per Label in {plot_title} data',
              fontsize= 24, color= 'blue')
    plt.xticks(rotation= 90, fontsize= 18)
    plt.yticks(fontsize= 18)
    plt.xlabel('CLASS', fontdict= form)
    yaxis_label = 'IMAGE COUNT'
    plt.ylabel(yaxis_label, fontdict= form)

    rotation = 'vertical' if lcount >= 8 else 'horizontal'
    for i in range(lcount):
        plt.text(i, values[i] / 2, str(values[i]), fontsize= 12,
                rotation= rotation, color= 'yellow', ha= 'center')

    plt.show()

Создать обратный вызов

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

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

Код определяет пользовательский класс обратного вызова MyCallback для обучения модели Keras, который наследуется от keras.callbacks.Callback. Этот класс управляет корректировкой скорости обучения и ранней остановкой обучения на основе показателей обучения и проверки.

Инициализация класса __init__(self, model, patience, stop_patience, threshold, factor, batches, epochs, ask_epoch):

  • Инициализирует пользовательский обратный вызов с моделью и различными гиперпараметрами, такими как терпение для снижения скорости обучения и ранней остановки, порог для переключения показателей мониторинга, коэффициент снижения скорости обучения, количество пакетов на эпоху, общее количество эпох и номер эпохи, для запроса остановки обучения. Он также инициализирует некоторые переменные класса для внутреннего использования.

Метод on_train_begin(self, logs= None):

  • В начале обучения спрашивает пользователя, включить ли интерактивную раннюю остановку, и распечатывает заголовок журнала обучения.

Метод on_train_end(self, logs= None):

  • В конце обучения распечатывает общее затраченное время обучения и устанавливает для весов модели лучшие веса, найденные во время обучения.

Метод on_train_batch_end(self, batch, logs= None):

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

Метод on_epoch_begin(self, epoch, logs= None):

  • В начале каждой эпохи записывается время начала эпохи.

Метод on_epoch_end(self, epoch, logs= None):

  • В конце каждой эпохи он выполняет несколько действий:
  • Вычисляет и распечатывает различные показатели обучения и проверки, текущую и следующую скорость обучения, показатели мониторинга, процентное улучшение показателей мониторинга и продолжительность эпохи.
  • Проверяет, улучшились ли показатели обучения или проверки. Если это так, сбрасывается счетчик терпения для снижения скорости обучения и ранней остановки, а также обновляются лучшие веса, если метрика мониторинга улучшилась.
  • Если метрика мониторинга не улучшилась, увеличивается количество терпения. Если терпение исчерпано, снижается скорость обучения и сбрасывается счетчик терпения. Если скорость обучения была снижена определенное количество раз (stop_patience), обучение прекращается.
  • При необходимости пользователь спрашивает, следует ли остановить обучение или продолжить его еще на несколько эпох.
class MyCallback(keras.callbacks.Callback):
    def __init__(self, model, patience, stop_patience, threshold,
                 factor, batches, epochs, ask_epoch):
        super(MyCallback, self).__init__()
        self.model = model
        self.patience = patience
        self.stop_patience = stop_patience
        self.threshold = threshold
        self.factor = factor
        self.batches = batches
        self.epochs = epochs
        self.ask_epoch = ask_epoch
        self.ask_epoch_initial = ask_epoch

        # variables
        self.count = 0
        self.stop_count = 0
        self.best_epoch = 1
        self.initial_lr = float(tf.keras.backend.get_value(model.optimizer.lr))
        self.highest_tracc = 0.0
        self.lowest_vloss = np.inf
        self.best_weights = self.model.get_weights()
        self.initial_weights = self.model.get_weights()


    def on_train_begin(self, logs= None):
        msg = 'Notification to halt training [y/n]?'
        print(msg)
        ans = input('')
        if ans in ['Y', 'y']:
            self.ask_permission = 1
        elif ans in ['N', 'n']:
            self.ask_permission = 0

        msg = ('{0:^8s}{1:^10s}{2:^9s}{3:^9s}{4:^9s}{5:^9s}'
       '{6:^9s}{7:^10s}{8:10s}{9:^8s}').format(
           'Epoch', 'Loss', 'Accuracy', 'V_loss', 'V_acc',
           'LR', 'Next LR', 'Monitor', '% Improv', 'Duration')

        print(msg)
        self.start_time = time.time()

    def on_train_end(self, logs= None):
        stop_time = time.time()
        tr_duration = stop_time - self.start_time
        hours = tr_duration // 3600
        minutes = (tr_duration - (hours * 3600)) // 60
        seconds = tr_duration - ((hours * 3600) + (minutes * 60))

        msg = (f'Training elapsed time was {str(hours)} hours, '
       f'{minutes:4.1f} minutes, {seconds:4.2f} seconds)')

        print(msg)

        self.model.set_weights(self.best_weights)

    def on_train_batch_end(self, batch, logs= None):

        acc = logs.get('accuracy') * 100
        loss = logs.get('loss')

        msg = ('{0:20s}processing batch {1:} of {2:5s}-   accuracy=  {3:5.3f} -'
       'loss: {4:8.5f}').format(' ', str(batch), str(self.batches), acc, loss)

        print(msg, '\r', end= '')


    def on_epoch_begin(self, epoch, logs= None):
        self.ep_start = time.time()

    def on_epoch_end(self, epoch, logs= None):
        ep_end = time.time()
        duration = ep_end - self.ep_start

        lr = float(tf.keras.backend.get_value(self.model.optimizer.lr))
        current_lr = lr
        acc = logs.get('accuracy')
        v_acc = logs.get('val_accuracy')
        loss = logs.get('loss')
        v_loss = logs.get('val_loss')

        if acc < self.threshold:
            monitor = 'accuracy'
            if epoch == 0:
                pimprov = 0.0
            else:
                pimprov = (acc - self.highest_tracc ) * 100 / self.highest_tracc

            if acc > self.highest_tracc:
                self.highest_tracc = acc
                self.best_weights = self.model.get_weights()
                self.count = 0
                self.stop_count = 0
                if v_loss < self.lowest_vloss:
                    self.lowest_vloss = v_loss
                self.best_epoch = epoch + 1

            else:
                if self.count >= self.patience - 1:
                    lr = lr * self.factor
                    tf.keras.backend.set_value(self.model.optimizer.lr, lr)
                    self.count = 0
                    self.stop_count = self.stop_count + 1
                    self.count = 0
                    if v_loss < self.lowest_vloss:
                        self.lowest_vloss = v_loss
                else:
                    self.count = self.count + 1
        else:
            monitor = 'val_loss'
            if epoch == 0:
                pimprov = 0.0

            else:
                pimprov = (self.lowest_vloss - v_loss ) * 100 / self.lowest_vloss

            if v_loss < self.lowest_vloss:
                self.lowest_vloss = v_loss
                self.best_weights = self.model.get_weights()
                self.count = 0
                self.stop_count = 0
                self.best_epoch = epoch + 1

            else:
                if self.count >= self.patience - 1:
                    lr = lr * self.factor
                    self.stop_count = self.stop_count + 1
                    self.count = 0
                    tf.keras.backend.set_value(self.model.optimizer.lr, lr)

                else:
                    self.count = self.count + 1

                if acc > self.highest_tracc:
                    self.highest_tracc = acc

        msg = (f'{str(epoch + 1):^3s}/{str(self.epochs):4s} {loss:^9.3f}'
       f'{acc * 100:^9.3f}{v_loss:^9.5f}{v_acc * 100:^9.3f}{current_lr:^9.5f}'
       f'{monitor:^11s}{pimprov:^10.2f}{lr:^9.5f}'
       f'{duration:^8.2f}')


        print(msg)

        if self.stop_count > self.stop_patience - 1:
            msg = (f'training has been halted at epoch {epoch + 1} after '
            f'{self.stop_patience} adjustments of learning rate with '
            f'no improvement')

            print(msg)
            self.model.stop_training = True

        else:
            if self.ask_epoch != None and self.ask_permission != 0:
                if epoch + 1 >= self.ask_epoch:
                    msg = ('Hit H to halt training, or enter an number of '
                            'epochs to continue training')

                    print(msg)

                    ans = input('')
                    if ans == 'H' or ans == 'h':
                        msg = f'Training has been halted at epoch {epoch + 1}, as requested'
                        print(msg)
                        self.model.stop_training = True #

                    else:
                        try:
                            ans = int(ans)
                            self.ask_epoch += ans
                            msg = (f'Training has been halted at epoch '
                                  f'{epoch + 1}, '
                                  f'as requested')
                            print(msg)
                            msg = ('{0:^8s}{1:^10s}{2:^9s}{3:^9s}{4:^9s}{5:^9s}'
                                      '{6:^9s}{7:^10s}{8:10s}{9:^8s}').format(
                                'Epoch', 'Loss', 'Accuracy', 'V_loss', 'V_acc',
                            'LR', 'Next LR', 'Monitor', '% Improv', 'Duration')

                            print(msg)

                        except Exception:
                            print('Invalid')

Постройте историю обучения

Функция plot_training(hist) используется для визуализации потерь и точности обучения и проверки во время обучения модели.

Параметр функции:

  • hist: объект истории, содержащий показатели потерь и точности модели в каждую эпоху.

Внутри функции:

  • tr_acc, tr_loss, val_acc, val_loss: извлекает точность обучения, потери обучения, точность проверки и потери проверки соответственно из объекта истории.
  • index_loss и val_lowest: определяют эпоху, в которой потери при проверке были наименьшими.
  • index_acc и acc_highest: определяют эпоху, в которой точность проверки была самой высокой.
  • Epochs: Список номеров эпох.
  • loss_label и acc_label: строки, представляющие номер эпохи с наименьшими потерями при проверке и самой высокой точностью проверки соответственно.
  • Инициализирует фигуру matplotlib и стилизует ее, используя стиль «ggplot».
  • Отображает потери при обучении и проверке на первом подграфике и аннотирует точку с наименьшими потерями при проверке.
  • Отображает точность обучения и проверки на втором подграфике и аннотирует точку с самой высокой точностью проверки.
def plot_training(hist):

    tr_acc = hist.history['accuracy']
    tr_loss = hist.history['loss']
    val_acc = hist.history['val_accuracy']
    val_loss = hist.history['val_loss']
    index_loss = np.argmin(val_loss)
    val_lowest = val_loss[index_loss]
    index_acc = np.argmax(val_acc)
    acc_highest = val_acc[index_acc]
    Epochs = [i+1 for i in range(len(tr_acc))]
    loss_label = f'best epoch= {str(index_loss + 1)}'
    acc_label = f'best epoch= {str(index_acc + 1)}'

    # plot training history
    plt.figure(figsize= (20, 8))
    plt.style.use('ggplot')

    plt.subplot(1, 2, 1)
    plt.plot(Epochs, tr_loss, 'b', label= 'Training loss')
    plt.plot(Epochs, val_loss, 'g', label= 'Validation loss')
    plt.scatter(index_loss + 1, val_lowest, s = 100, c = 'red',
                label = loss_label)
    plt.title('Training and Validation Loss')
    plt.xlabel('Epochs')
    plt.ylabel('Loss')
    plt.legend()

    plt.subplot(1, 2, 2)
    plt.plot(Epochs, tr_acc, 'b', label= 'Training Accuracy')
    plt.plot(Epochs, val_acc, 'g', label= 'Validation Accuracy')
    plt.scatter(index_acc + 1 , acc_highest, s = 100, c = 'red',
                label = acc_label)
    plt.title('Training and Validation Accuracy')
    plt.xlabel('Epochs')
    plt.ylabel('Accuracy')
    plt.legend()

    plt.tight_layout
    plt.show()

Создайте матрицу путаницы

Функция plot_confusion_matrix используется для визуализации матрицы путаницы результатов модели классификации.

Параметры функции:

  • cm: Массив матрицы неточностей, полученный с помощью sklearn.metrics.confusion_matrix.
  • classes: Список меток классов.
  • normalize: Если True, матрица путаницы нормализуется по строкам (т. е. по количеству выборок в каждом классе).
  • title: Название графика матрицы неточностей.
  • cmap: Цветовая карта, используемая для раскрашивания матрицы путаницы.

Внутри функции:

  • Инициализирует фигуру matplotlib.
  • Отображает матрицу путаницы в виде изображения, где цвет каждой ячейки сетки представляет количество выборок.
  • Добавляет цветовую полосу сбоку от графика, которая дает представление о том, что означают цвета в матрице путаницы.
  • Устанавливает отметки и метки на осях X и Y в качестве классов.
  • Если normalize равно True, матрица неточностей нормализуется и печатается «Нормализованная матрица неточностей». В противном случае печатается «Матрица путаницы без нормализации».
  • Печатает значения матрицы путаницы.
  • Добавляет значения матрицы путаницы в виде текста в ячейки графика.
  • Устанавливает макет для плотного прилегания и маркирует оси.
def plot_confusion_matrix(cm, classes, normalize = False,
                          title = 'Confusion Matrix', cmap = plt.cm.Reds):

 plt.figure(figsize = (4, 4))
 plt.imshow(cm, interpolation = 'nearest', cmap = cmap)
 plt.title(title)
 plt.colorbar()

 tick_marks = np.arange(len(classes))
 plt.xticks(tick_marks, classes, rotation= 45)
 plt.yticks(tick_marks, classes)

 if normalize:
  cm = cm.astype('float') / cm.sum(axis = 1)[:, np.newaxis]
  print('Normalised Confusion Matrix')

 else:
  print('Confusion Matrix, Without Normalisation')

 print(cm)

 thresh = cm.max() / 2.
 for i, j in itertools.product(range(cm.shape[0]), range(cm.shape[1])):
  plt.text(j, i, cm[i, j], horizontalalignment = 'center',
           color = 'white' if cm[i, j] > thresh else 'black')

 plt.tight_layout()
 plt.ylabel('True Label')
 plt.xlabel('Predicted Label')

EfficientNet: Обзор

EfficientNet представляет собой семейство архитектур Сверточная нейронная сеть (CNN), разработанных с основной целью создания баланса между вычислительными затратами и производительностью модели для задач распознавания изображений. Этот баланс был достигнут за счет систематического увеличения глубины, ширины и разрешения модели.

В основе EfficientNet лежит базовая сеть под названием EfficientNet-B0, которая была разработана с применением традиционного метода составного масштабирования. Однако для масштабирования этой базовой сети был введен новый метод масштабирования, который использует составной коэффициент для одновременного масштабирования глубины, ширины и разрешения сети, а не произвольно.

Варианты эффективной сети

В семействе EfficientNet существует множество моделей, каждая из которых обозначается отдельной буквой и номером, например «B3» в EfficientNet-B3, «B4» в EfficientNet-B4 и так далее. Эти модели имеют разные коэффициенты масштабирования, которые в конечном итоге определяют размер и емкость модели.

Эффективная сеть-B4

EfficientNet-B4 — это одна из моделей EfficientNet, которая была увеличена по сравнению с базовой моделью EfficientNet-B0 с использованием составного коэффициента. Он больше и мощнее своих меньших аналогов (например, EfficientNet-B3 или EfficientNet-B2), но меньше, чем более крупные варианты (например, EfficientNet-B5 или EfficientNet-B6).

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

Загрузить данные

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

  • data_dir: указывает путь к каталогу, содержащему данные.
  • Блок try-except: фиксирует и обрабатывает исключения во время обработки данных и настройки модели.

Внутри блока try:

  • train_df, valid_df, test_df = create_df(data_dir): вызывает функцию create_df с использованием указанного каталога данных, которая возвращает DataFrames для наборов обучения, проверки и тестирования.
  • batch_size = 40: устанавливает размер пакета для генераторов данных равным 40.
  • train_gen, valid_gen, test_gen = create_model_data(train_df, valid_df, test_df, batch_size): вызывает функцию create_model_data, используя созданные DataFrames и размер пакета, которая возвращает генераторы для наборов обучения, проверки и тестирования.

Внутри блока except:

  • print('Invalid input'): печатает сообщение об ошибке, если исключение возникает на этапах обработки данных или настройки модели.
data_dir = '/content/**replace with your file directory**'

try:
    # Get splitted data
    train_df, valid_df, test_df = create_df(data_dir)

    # Get Generators
    batch_size = 40
    train_gen, valid_gen, test_gen = create_model_data(train_df, valid_df,
                                                       test_df, batch_size)

except:
    print('Invalid input')

Отображение выборочных данных

show_images(train_gen)

Модельная архитектура

Последовательная модель Keras с EfficientNetB4

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

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

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

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

Определение параметров модели:

  • img_size: устанавливает целевой размер изображений (150, 150).
  • channels: указывает, что изображения имеют 3 цветовых канала (RGB).
  • img_shape: определяет входную форму для базовой модели как (150, 150, 3).
  • class_count: подсчитывает количество классов в плотном слое.

Инициализировать базовую модель:

  • base_model: Инициализирует модель EfficientNetB4 с предварительно обученными весами ImageNet. Модель настроена без верхнего слоя (include_top = False), с указанной формой изображения и для использования глобального максимального пула.

Определить пользовательскую модель:

  • model: Инициализирует последовательную модель, которая включает базовую модель и несколько дополнительных слоев:
  • BatchNormalization: Нормализует активации предыдущего слоя в каждой партии.
  • Dense: полносвязный слой с 256 модулями, функцией активации ReLU и регуляризаторами L1 и L2.
  • Dropout: случайным образом обнуляет часть входных данных, чтобы предотвратить переобучение. Для обеспечения воспроизводимости ставка установлена ​​на уровне 0,45, а начальное значение - на 123.
  • Dense: выходной слой с количеством единиц, равным количеству классов, с использованием функции активации softmax для многоклассовой классификации.

Компилировать модель:

  • Модель компилируется с использованием оптимизатора Adamax, категориальной функции перекрестных энтропийных потерь и точности в качестве метрики.

Резюме:

  • model.summary(): печатает сводную информацию об архитектуре модели.
img_size = (150, 150)
channels = 3
img_shape = (img_size[0], img_size[1], channels)
class_count = len(list(train_gen.class_indices.keys()))


base_model = tf.keras.applications.efficientnet.EfficientNetB4(
    include_top = False,
    weights = "imagenet",
    input_shape = img_shape,
    pooling= 'max')

model = Sequential([
    base_model,
    BatchNormalization(axis = -1, momentum = 0.99, epsilon = 0.001),
    Dense(256, kernel_regularizer = regularizers.l2(l = 0.016),
          activity_regularizer = regularizers.l1(0.006),
                bias_regularizer = regularizers.l1(0.006),
           activation= 'relu'),
    Dropout(rate = 0.45, seed = 123),
    Dense(class_count, activation = 'softmax')
])

model.compile(Adamax(learning_rate = 0.001), loss = 'categorical_crossentropy',
              metrics = ['accuracy'])

model.summary()

Сводная информация о модели и параметры

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

  • Total params: Обозначает общее количество параметров в модели. Это обучаемые аспекты модели, которые процесс обучения корректирует для минимизации функции потерь. В нашей модели компьютерного зрения всего 18 142 055 параметров.
  • Trainable params: это параметры (т. е. веса и смещения), которые модель изучает во время обучения. Обучаемые параметры могут изменять свои значения посредством обратного распространения ошибки. В нашей модели имеется 18 013 264 обучаемых параметра, что означает, что эти параметры будут обновляться во время обучения, чтобы минимизировать функцию потерь.
  • Non-trainable params: Это параметры, которые не изучаются во время обучения. Они сохраняются постоянными и не обновляются во время обратного распространения ошибки. Необучаемые параметры могут быть параметрами предварительно обученной модели, используемой для трансферного обучения, где веса предварительно обученной модели замораживаются во время обучения, чтобы сохранить уже изученные функции. В нашей модели имеется 128 791 необучаемый параметр, которые остаются постоянными во время обучения.

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

Постройте архитектуру модели

plot_model(model, show_shapes=True,
           show_layer_names=True,
           to_file='model_architecture.png')

image = plt.imread('model_architecture.png')
plt.figure(figsize=(3, 3))
plt.imshow(image)
plt.axis('off')
plt.tight_layout()
plt.show()

Установите базовые параметры обратного вызова

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

Гиперпараметры — это предварительно установленные конфигурации, которые управляют процессом обучения. Они могут существенно влиять на производительность модели и могут включать такие аспекты, как скорость обучения, размер пакета, количество слоев, количество единиц на слой и многое другое.

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

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

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

Определение гиперпараметров:

  • batch_size: количество выборок на одно обновление градиента установлено на 40.
  • epochs: количество эпох для обучения модели установлено на 40.
  • patience: количество эпох без улучшений, после которых скорость обучения будет снижена, установлено на 1.
  • stop_patience: количество эпох без улучшения, после которых тренировка будет остановлена, установлено равным 3.
  • threshold: Порог метрики для переключения метрики мониторинга, установлен на 0,9.
  • factor: Коэффициент, на который будет снижена скорость обучения, установлен на 0,5.
  • ask_epoch: Номер эпохи, для которого требуется досрочная остановка, установлен на 5.
  • batches: количество пакетов за эпоху, рассчитанное как максимальное количество обучающих выборок, разделенное на размер пакета.

Настройте обратные вызовы:

  • callbacks: Инициализирует список обратных вызовов, которые будут использоваться во время обучения. В данном случае он включает экземпляр класса MyCallback, инициализированный моделью и указанными выше гиперпараметрами.
batch_size = 40
epochs = 40
patience = 1
stop_patience = 3
threshold = 0.9
factor = 0.5
ask_epoch = 5
batches = int(np.ceil(len(train_gen.labels) / batch_size))

callbacks = [MyCallback(model = model, patience = patience,
                        stop_patience = stop_patience,
                        threshold = threshold, factor= factor,
                        batches = batches, epochs = epochs,
                        ask_epoch= ask_epoch )]

Обучение модели

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

Модель поезда:

  • history: вызывает метод fit для model для его обучения в течение указанного количества эпох (epochs), используя указанный генератор обучающих данных (train_gen). В процессе обучения предусмотрены следующие параметры:
  • verbose = 0: Для каждой эпохи выходные данные не создаются.
  • callbacks = callbacks: во время обучения использует ранее определенный список обратных вызовов.
  • validation_data = valid_gen: использует указанный генератор проверочных данных (valid_gen).
  • validation_steps = None: Количество шагов, составляющих эпоху для генератора проверки, равно общему количеству выборок в генераторе, деленному на размер пакета.
  • shuffle = False: данные обучения не перемешиваются перед каждой эпохой.

Вывод функции:

  • history: Объект истории, созданный fit(). Его атрибут History.history представляет собой словарь, записывающий значения потерь при обучении и значения метрик в последовательные эпохи, а также значения потерь при проверке и значения метрик проверки.
history = model.fit(x = train_gen, epochs = epochs, verbose = 0,
                    callbacks = callbacks, validation_data = valid_gen,
                    validation_steps = None, shuffle = False)

Построение графика производительности модели

plot_training(history)

Оценка модели

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

Рассчитать размер тестовой партии:

  • ts_length: Общее количество образцов в тестовом наборе.
  • test_batch_size: находит наибольший коэффициент ts_length, который меньше или равен 80, и является размером партии для тестирования.
  • test_steps: количество шагов, составляющих эпоху для генератора тестов, рассчитывается как ts_length // test_batch_size.

Оценить модель:

  • train_score: оценивает модель в генераторе обучающих данных (train_gen), используя вычисленное число test_steps.
  • valid_score: оценивает модель в генераторе проверочных данных (valid_gen), используя вычисленное число test_steps.
  • test_score: оценивает модель в генераторе тестовых данных (test_gen), используя вычисленное число test_steps.

Распечатать оценки:

  • Печатает потери (индекс 0) и точность (индекс 1) модели в обучающих, проверочных и тестовых наборах.
ts_length = len(test_df)
test_batch_size = test_batch_size = max(sorted([ts_length // n for n in \
                                                range(1, ts_length + 1) if\
                                                ts_length%n == 0 and \
                                                ts_length/n <= 80]))
test_steps = ts_length // test_batch_size

train_score = model.evaluate(train_gen, steps = test_steps, verbose= 1)
valid_score = model.evaluate(valid_gen, steps = test_steps, verbose= 1)
test_score = model.evaluate(test_gen, steps = test_steps, verbose= 1)

print("Train Loss: ", train_score[0])
print("Train Accuracy: ", train_score[1])
print('-' * 20)
print("Validation Loss: ", valid_score[0])
print("Validation Accuracy: ", valid_score[1])
print('-' * 20)
print("Test Loss: ", test_score[0])
print("Test Accuracy: ", test_score[1])

Делайте прогнозы

preds = model.predict_generator(test_gen)
y_pred = np.argmax(preds, axis=1)
print(y_pred)

Постройте матрицу неточностей и отчет о классификации

Этот код используется для создания и отображения матрицы путаницы и отчета о классификации прогнозов модели на тестовом наборе.

Извлечение меток классов:

  • g_dict: Получает метки классов сопоставления словаря с соответствующими целочисленными индексами из генератора тестов.
  • classes: Список меток классов.

Создать и построить матрицу неточностей:

  • cm: Вычисляет матрицу путаницы, используя истинные метки из генератора тестов и прогнозируемые метки (y_pred).
  • plot_confusion_matrix(): вызывает ранее определенную функцию plot_confusion_matrix для построения матрицы путаницы с метками классов.

Создать и распечатать отчет о классификации:

  • classification_report(): Создает отчет о классификации, который включает такие показатели, как точность, полнота, показатель f1 и поддержка для каждого класса.
  • print(): Печать отчета о классификации.

Вывод функции:

  • Фрагмент кода ничего не возвращает напрямую. Он генерирует визуальное представление матрицы путаницы и печатает отчет о классификации.
g_dict = test_gen.class_indices
classes = list(g_dict.keys())

# Classification report
print(classification_report(test_gen.classes, y_pred, target_names = classes))

# Confusion matrix
cm = confusion_matrix(test_gen.classes, y_pred)
plot_confusion_matrix(cm = cm, classes = classes, title = 'Confusion Matrix')

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

Математическая формула точности:

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

Математическая формула отзыва:

Оценка F1. Оценка F1 – это среднее гармоническое значение точности и полноты. Наивысшее возможное значение F1 равно 1, что указывает на идеальную точность и полноту, а наименьшее возможное значение равно 0, если точность или полнота равны нулю.

Математическая формула для оценки F1:

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

Краткое содержание

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

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

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

Метрики оценки:

  • Точность: 0,96
  • Напомним: 0,96
  • Оценка F1: 0,96
  • Поддержка: 750

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

Будущая работа и заключение

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

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

Если вы заметили какие-либо ошибки, орфографические ошибки или другие, пожалуйста, свяжитесь со мной, и я их исправлю!