Обнаружение рака кожи с помощью TensorFlow
В этой статье мы узнаем, как реализовать модель обнаружения рака кожи с помощью Tensorflow. Мы будем использовать набор данных, содержащий изображения для двух категорий: злокачественных и доброкачественных. Мы будем использовать технику трансферного обучения, чтобы достичь лучших результатов при меньшем количестве тренировок. Мы будем использовать архитектуру EfficientNet в качестве основы нашей модели вместе с предварительно обученными весами того же самого, полученного путем обучения его на наборе данных сети изображений.
Импорт библиотек
Библиотеки Python позволяют нам очень легко обрабатывать данные и выполнять типичные и сложные задачи с помощью одной строки кода.
- Pandas — эта библиотека помогает загружать фрейм данных в формате 2D-массива и имеет несколько функций для выполнения задач анализа за один раз.
- Массивы Numpy очень быстрые и могут выполнять большие вычисления за очень короткое время.
- Matplotlib — эта библиотека используется для рисования визуализаций.
- Sklearn — этот модуль содержит несколько библиотек с предварительно реализованными функциями для выполнения задач от предварительной обработки данных до разработки и оценки моделей.
- Tensorflow — это библиотека с открытым исходным кодом, которая используется для машинного обучения и искусственного интеллекта и предоставляет ряд функций для реализации сложных функций с помощью одной строки кода.
Python3
import numpy as np import pandas as pd import seaborn as sb import matplotlib.pyplot as plt from glob import glob from PIL import Image from sklearn.model_selection import train_test_split import tensorflow as tf from tensorflow import keras from keras import layers from functools import partial AUTO = tf.data.experimental.AUTOTUNE import warnings warnings.filterwarnings( "ignore" ) |
Теперь давайте проверим количество изображений, которые у нас есть. Вы можете скачать набор данных изображения отсюда.
Python3
images = glob( "train/*/*.jpg" ) len (images) |
Выход:
2637
Python3
df = pd.DataFrame({ "filepath" : images}) df[ "label" ] = df[ "filepath" ]. str .split( "/" , expand = True )[ 1 ] df.head() |
Выход:
Преобразование меток в 0 и 1 сохранит нашу работу по кодированию меток, поэтому давайте сделаем это прямо сейчас.
Python3
df[ "label_bin" ] = np.where(df[ "label" ].values = = "malignant" , 1 , 0 ) df.head() |
Выход:
Python3
x = df[ "label" ].value_counts() plt.pie(x.values, labels = x.index, autopct = "%1.1f%%" ) plt.show() |
Выход:
Для каждого из классов было дано примерно равное количество изображений, поэтому дисбаланс данных здесь не является проблемой.
Python3
for cat in df[ "label" ].unique(): temp = df[df[ "label" ] = = cat] index_list = temp.index fig, ax = plt.subplots( 1 , 4 , figsize = ( 15 , 5 )) fig.suptitle(f "Images for {cat} category . . . ." , fontsize = 20 ) for i in range ( 4 ): index = np.random.randint( 0 , len (index_list)) index = index_list[index] data = df.iloc[index] image_path = data[ 0 ] img = np.array(Image. open (image_path)) ax[i].imshow(img) plt.tight_layout() plt.show() |
Выход:
Теперь давайте разделим данные на обучающую и проверочную части с помощью функции train_test_split.
Python3
features = df[ "filepath" ] target = df[ "label_bin" ] X_train, X_val, Y_train, Y_val = train_test_split(features, target, test_size = 0.15 , random_state = 10 ) X_train.shape, X_val.shape |
Выход:
((2241,), (396,))
Python3
def decode_image(filepath, label = None ): img = tf.io.read_file(filepath) img = tf.image.decode_jpeg(img) img = tf.image.resize(img, [ 224 , 224 ]) img = tf.cast(img, tf.float32) / 255.0 if label = = None : return img return img, label |
Конвейеры ввода изображений были реализованы ниже, поэтому мы можем передавать их без необходимости предварительной загрузки всех данных.
Python3
train_ds = ( tf.data.Dataset .from_tensor_slices((X_train, Y_train)) . map (decode_image, num_parallel_calls = AUTO) . map (partial(process_data), num_parallel_calls = AUTO) .batch( 32 ) .prefetch(AUTO) ) val_ds = ( tf.data.Dataset .from_tensor_slices((X_val, Y_val)) . map (decode_image, num_parallel_calls = AUTO) .batch( 32 ) .prefetch(AUTO) ) |
Теперь, когда конвейеры ввода данных готовы, давайте перейдем к части моделирования.
Разработка модели
Для этой задачи мы будем использовать архитектуру EfficientNet и использовать преимущества предварительно обученных весов таких больших сетей.
Архитектура модели
Мы реализуем модель с использованием функционального API Keras, которая будет содержать следующие части:
- В данном случае базовой моделью является модель EfficientNet.
- Слой Flatten сглаживает выходные данные базовой модели.
- Тогда у нас будет два полносвязных слоя, за которыми следует вывод сглаженного слоя.
- Мы включили несколько слоев BatchNormalization, чтобы обеспечить стабильное и быстрое обучение, и слой Dropout перед последним слоем, чтобы избежать любой возможности переобучения.
- Последний слой — это выходной слой, который выводит мягкие вероятности для трех классов.
Python3
from tensorflow.keras.applications.efficientnet import EfficientNetB7 pre_trained_model = EfficientNetB7( input_shape = ( 224 , 224 , 3 ), weights = "imagenet" , include_top = False ) for layer in pre_trained_model.layers: layer.trainable = False |
Выход:
258076736/258076736 [==============================] - 3s 0us/step
Python3
from tensorflow.keras import Model inputs = layers. Input (shape = ( 224 , 224 , 3 )) x = layers.Flatten()(inputs) x = layers.Dense( 256 , activation = "relu" )(x) x = layers.BatchNormalization()(x) x = layers.Dense( 256 , activation = "relu" )(x) x = layers.Dropout( 0.3 )(x) x = layers.BatchNormalization()(x) outputs = layers.Dense( 1 , activation = "sigmoid" )(x) model = Model(inputs, outputs) |
При составлении модели мы предоставляем эти три основных параметра:
- оптимизатор — это метод, который помогает оптимизировать функцию стоимости с помощью градиентного спуска.
- потеря — функция потерь, с помощью которой мы отслеживаем, улучшается ли модель с обучением или нет.
- Метрики — это помогает оценить модель, предсказывая данные обучения и проверки.
Python3
model. compile ( loss = tf.keras.losses.BinaryCrossentropy(from_logits = True ), optimizer = "adam" , metrics = [ "AUC" ] ) |
Теперь давайте обучим нашу модель.
Python3
history = model.fit(train_ds, validation_data = val_ds, epochs = 5 , verbose = 1 ) |
Выход:
Epoch 1/5 71/71 [==============================] - 5s 54ms/step - loss: 0.5478 - auc: 0.8139 - val_loss: 2.6825 - val_auc: 0.6711 Epoch 2/5 71/71 [==============================] - 3s 49ms/step - loss: 0.4547 - auc: 0.8674 - val_loss: 1.1363 - val_auc: 0.8328 Epoch 3/5 71/71 [==============================] - 3s 48ms/step - loss: 0.4288 - auc: 0.8824 - val_loss: 0.8702 - val_auc: 0.8385 Epoch 4/5 71/71 [==============================] - 3s 48ms/step - loss: 0.4044 - auc: 0.8933 - val_loss: 0.6367 - val_auc: 0.8561 Epoch 5/5 71/71 [==============================] - 3s 49ms/step - loss: 0.3891 - auc: 0.9019 - val_loss: 0.9296 - val_auc: 0.8558
Давайте визуализируем потери при обучении и проверке и AUC для каждой эпохи.
Python3
hist_df = pd.DataFrame(history.history) hist_df.head() |
Выход:
Python3
hist_df[ "loss" ].plot() hist_df[ "val_loss" ].plot() plt.title( "Loss v/s Validation Loss" ) plt.legend() plt.show() |
Выход:
Потери при обучении со временем уменьшились не так сильно, как потери при проверке.
Python3
hist_df[ "auc" ].plot() hist_df[ "val_auc" ].plot() plt.title( "AUC v/s Validation AUC" ) plt.legend() plt.show() |