Над проектом работали следующие люди:

Джайшрути Махадеван, Салони Агравал, Ниммиша Горакссакар и Адитья Нараянан

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

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

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

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

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

Таким образом, сравнительный анализ проводится между четырьмя различными алгоритмами CNN: Xception, MobileNet, DenseNet201; с последующим ансамблевым подходом (мажоритарным голосованием) для получения окончательных прогнозов.

Поэтому мы сможем построить лучшую модель с хорошим уровнем точности.

Поэтому основной задачей данной системы является достижение максимальной точности прогнозирования кожных заболеваний. Все эти алгоритмы реализованы на 9 типах заболеваний (Меланома, Меланоцитарный невус, Базальноклеточный рак, Актинический кератоз, Доброкачественный кератоз, Дерматофиброма, Сосудистое поражение, Плоскоклеточный рак, Неизвестно)

Для разработки и проверки предложенной схемы было собрано более 25 000 образцов изображений кожных заболеваний.

Давайте начнем,

Сначала мы импортируем необходимые библиотеки

#Importing libraries
import os
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import tensorflow as tf
import csv
from tensorflow.keras.applications.xception import Xception, preprocess_input, decode_predictions
from tensorflow.keras.applications.mobilenet import MobileNet
from tensorflow.keras.preprocessing import image
from tensorflow.keras.models import Model
from tensorflow.keras.utils import to_categorical
from tensorflow.keras.models import load_model
from tensorflow import keras
from tensorflow.keras.models import Model,Sequential
from tensorflow.keras.layers import Dense, GlobalAveragePooling2D, Flatten, BatchNormalization, Activation, Dropout
from tensorflow.keras.callbacks import ModelCheckpoint, TensorBoard
from tensorflow.keras.preprocessing.image import ImageDataGenerator
from tensorflow.keras.callbacks import ReduceLROnPlateau
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import LabelEncoder
from sklearn.metrics import accuracy_score
from sklearn.metrics import roc_curve, auc
from sklearn.cluster import KMeans
from skimage import io # for loading images
import imageio # export images to files
import sys
from mpl_toolkits.mplot3d import Axes3D
from sklearn.preprocessing import StandardScaler
from tensorflow.keras.preprocessing import image
from tensorflow.keras.applications.densenet import preprocess_input, decode_predictions
from tensorflow.keras.models import Model
from tensorflow.keras.layers import Dense, GlobalAveragePooling2D, Dropout
from tensorflow.keras import backend as K
from tensorflow.keras.optimizers import Adam
from tensorflow.keras.applications.densenet import DenseNet201
from tensorflow.keras.callbacks import ReduceLROnPlateau
from tensorflow.keras import layers
from sklearn.model_selection import cross_validate
from sklearn import preprocessing
from sklearn.model_selection import StratifiedShuffleSplit
from sklearn.metrics import confusion_matrix
import seaborn as sns
from sklearn import metrics
from sklearn.metrics import precision_score
from sklearn.metrics import recall_score
from sklearn.metrics import f1_score

Далее мы установим случайное начальное число, чтобы каждый человек получил одинаковый результат.

#Deciding random seed
np.random.seed(42)
tf.random.set_seed(42)

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

Полученное число затем используется в качестве начального числа для генерации следующего «случайного» числа.

Когда вы устанавливаете начальное значение (каждый раз), оно каждый раз делает одно и то же, давая вам одни и те же числа.

Если вам нужны кажущиеся случайными числа, не устанавливайте начальное число.

Затем мы загружаем данные, извлеченные из Kaggle, на Google Диск.



# Connecting to google drive for data
from google.colab import drive
drive.mount('/content/gdrive')
root_path = 'gdrive/MyDrive/'

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

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

# Reading and creating a dataframe
!pip install -U -q PyDrive
from pydrive.auth import GoogleAuth
from pydrive.drive import GoogleDrive
from google.colab import auth
from oauth2client.client import GoogleCredentials
# Authenticate and create the PyDrive client.
auth.authenticate_user()
gauth = GoogleAuth()
gauth.credentials = GoogleCredentials.get_application_default()
drive = GoogleDrive(gauth)

Давайте импортируем один из файлов CSV

link = 'https://drive.google.com/file/d/1qP-Q-GyGRWQ0Y47irTZxjen0ZlVF2be0/view?usp=sharing'
import pandas as pd
# to get the id part of the file
id = link.split("/")[-2]
downloaded = drive.CreateFile({'id':id})
downloaded.GetContentFile('ISIC_2019_Training_GroundTruth.csv')
df = pd.read_csv('ISIC_2019_Training_GroundTruth.csv')
df.head(10)

Как видно из функции head, в списке 10 верхних строк CSV-файла данные представлены в формате One Hot Encoding Format (т. е. каждая строка или изображение может иметь только 1 заболевание в качестве вывода).

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

df.describe()

Затем мы проверим, есть ли какие-либо пустые строки или изображения без каких-либо данных в соответствующей строке, используя функцию info ().

df.info()

Мы можем проверить форму фрейма данных, используя функцию .shape().

df.shape

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

# Plotting histograms of all 9 diseases
df.hist(bins=50, figsize=(20,15))
plt.show()

Мы можем сделать вывод из гистограммы, которая показывает, что код заболевания NV имеет наибольшее количество случаев из набора данных, за которым следуют MEL и BCC для остальных заболеваний, количество изображений меньше. Существует неравномерное распределение различных заболеваний, которые мы рассмотрим в более поздней части проекта.

#Combining Image url with csv file
df['image_url'] = 'e'    # creating a column with dummy value 'e' at the beginning
for i in range(len(df)):
image_id = df.iloc[i]['image']
df.iloc[i, df.columns.get_loc('image_url')] = '/content/'+ str(image_id)+'.jpg'
print(df)

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

Чтение и создание фрейма данных второго CSV-файла с диска ISIC_2019_Training_Metadata

link_2 = 'https://drive.google.com/file/d/1yFbYZxpF-8PCOzas0Oo_XNjF7Y9g5_eb/view?usp=sharing'
import pandas as pd
# to get the id part of the file
id_2 = link_2.split("/")[-2]
downloaded_2 = drive.CreateFile({'id':id_2})
downloaded_2.GetContentFile('ISIC_2019_Training_Metadata.csv')
df2 = pd.read_csv('ISIC_2019_Training_Metadata.csv')
print(df2)

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

df2.info()

Используя функцию .info(), мы видим пустые значения в полях age_приблизительно, anatom_site_general, lesion_id и sex.

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

#Merging both dataframes df and df2 with image column common
df3 = df.merge(df2, left_on='image',right_on='image')
df3.head(10)

Давайте отбросим те строки, в которых есть пустые значения, используя функцию dropna().

# Dropping rows with NaN values
df3.dropna(axis=0,inplace=True)

Давайте проверим фрейм данных df3 с помощью команды .info().

df3.info()

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

df3.drop(['sex','age_approx','lesion_id','anatom_site_general'], axis=1, inplace=True)

Давайте посмотрим на столбцы df3

df3.columns

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

df3_df = df3[df3['DF']==1]
df3_vasc = df3[df3['VASC']==1]
df3_ak = df3[df3['AK']==1]
df3_scc = df3[df3['SCC']==1]
df3_bcc = df3[df3['BCC']==1]
df3_mel = df3[df3['MEL']==1]
df3_bkl = df3[df3['BKL']==1]
df3_nv = df3[df3['NV']==1]

Теперь давайте посмотрим, все ли кожные заболевания, помеченные как df, относятся к категории df3_df соответственно.

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

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

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

Этот метод также имеет несколько параметров, которые необходимо кратко объяснить:

  1. dataframe: Pandas DataFrame, который содержит имена изображений и целевые значения.
  2. каталог: путь к папке, содержащей все изображения.
  3. x_col: имя столбца в DataFrame с именами изображений.
  4. y_col: имя столбца в DataFrame, в котором есть целевые значения.
  5. class_mode: установите значение binary для одномерных двоичных меток, тогда как категориальный для двумерных меток с однократным кодированием.
  6. target_size: размер входных изображений.
  7. batch_size: размер пакетов данных.
  8. seed: установите, чтобы воспроизвести результат.
df_path = r'C:\Users\ganes\images_full\DF_Aug'
vasc_path = r'C:\Users\ganes\images_full\VASC_Aug'
ak_path = r'C:\Users\ganes\images_full\AK_Aug'
scc_path = r'C:\Users\ganes\images_full\SCC_Aug'
bcc_path = r'C:\Users\ganes\images_full\BCC_Aug'
mel_path = r'C:\Users\ganes\images_full\MEL_Aug'
bkl_path = r'C:\Users\ganes\images_full\BKL_Aug'

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

nv_path = r'C:\Users\ganes\images_full\NV_full'

Давайте воспользуемся функцией ImageDataGenerator и сохраним данные в переменной Datagen. Здесь мы поворачиваем изображения на 30 градусов, смещаем ширину изображения на 0,1 пикселя, а высота изображения также смещается на 0,1 пикселя, мы также выполняем горизонтальное и вертикальное переворачивание изображения, поскольку пользователи могут загружать изображение в различной ориентации, и модель должна быть обучена различным типам ввода изображений.

datagen = ImageDataGenerator(rotation_range=30, width_shift_range=0.1, height_shift_range=0.1, horizontal_flip=True, vertical_flip = True)

Затем мы проведем аугментацию данных для болезни BCC.

for i in df3_bcc['image_url']:
image = np.expand_dims(imageio.imread(i), 0)
datagen.fit(image)
for x, val in zip(datagen.flow(image,
save_to_dir=bcc_path,
save_prefix='BCC',
save_format='jpg'),range(4)) :
pass

Функция expand_dims() используется для расширения формы массива. Вставьте новую ось, которая появится в позиции оси в развернутой форме массива.

Давайте сделаем аугментацию данных для болезни MEL

for i in df3_mel['image_url']:
image = np.expand_dims(imageio.imread(i), 0)
datagen.fit(image)
for x, val in zip(datagen.flow(image,
save_to_dir=mel_path,
save_prefix='MEL',
save_format='jpg'),range(3)) :
pass

Давайте сделаем аугментацию данных для болезни BKL

for i in df3_bkl['image_url']:
image = np.expand_dims(imageio.imread(i), 0)
datagen.fit(image)
for x, val in zip(datagen.flow(image,
save_to_dir=bkl_path,
save_prefix='BKL',
save_format='jpg'),range(5)) :
pass

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

print("AK:",len(df3_ak),"BCC:",len(df3_bcc),"BKL:",len(df3_bkl),"DF:",len(df3_df),"MEL:",len(df3_mel),"NV:",len(df3_nv),"SCC:",len(df3_scc),"VASC:",len(df3_vasc))

После аугментации общее количество изображений будет:

Мы можем использовать библиотеку Shutil, чтобы переместить соответствующие изображения в имя класса.

Модуль Shutil в Python предоставляет множество функций высокоуровневых операций над файлами и коллекциями файлов. Он входит в стандартные служебные модули Python. Этот модуль помогает автоматизировать процесс копирования и удаления файлов и каталогов.

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

import shutil
import os
dest = df_path
n = []
for i in df3_df['image_url']:
try:
shutil.move(i, dest)
except:
n.append(i)
continue

Далее нам нужно будет создать CSV-файл для дополненных изображений, используя следующие команды.

#CSV generated for DF disease
with open(r'C:/Users/ganes/df.csv', 'a', newline='') as file:
writer = csv.writer(file)
img_name = os.listdir(df_path)
for j in range(len(img_name)):
writer.writerow([img_name[j], 0,0,0,0,0,1,0,0,0, r'C:/Users/ganes/images_full/DF_Aug/'+img_name[j]])

Приведенная выше команда создает файл CSV для болезни ДФ.

#CSV generated for VASC disease
with tf.device('/device:GPU:0'):
with open(r'C:/Users/ganes/vasc.csv', 'a', newline='') as file:
writer = csv.writer(file)
img_name = os.listdir(vasc_path)
for j in range(len(img_name)):
writer.writerow([img_name[j], 0,0,0,0,0,0,1,0,0, r'C:/Users/ganes/images_full/VASC_Aug/'+img_name[j]])

Приведенная выше команда создает файл CSV для болезни VASC.

#CSV generated for AK disease
with open(r'C:/Users/ganes/ak.csv', 'a', newline='') as file:
writer = csv.writer(file)
img_name = os.listdir(ak_path)
for j in range(len(img_name)):
writer.writerow([img_name[j], 0,0,0,1,0,0,0,0,0, r'C:/Users/ganes/images_full/AK_Aug/'+img_name[j]])

Приведенная выше команда создает файл CSV для болезни АК.

#CSV generated for SCC disease
with open(r'C:/Users/ganes/scc.csv', 'a', newline='') as file:
writer = csv.writer(file)
img_name = os.listdir(scc_path)
for j in range(len(img_name)):
writer.writerow([img_name[j], 0,0,0,0,0,0,0,1,0, r'C:/Users/ganes/images_full/SCC_Aug/'+img_name[j]])

Приведенная выше команда создает файл CSV для болезни SCC.

#CSV generated for BCC disease
with open(r'C:/Users/ganes/bcc.csv', 'a', newline='') as file:
writer = csv.writer(file)
img_name = os.listdir(bcc_path)
for j in range(len(img_name)):
writer.writerow([img_name[j], 0,0,1,0,0,0,0,0,0, r'C:/Users/ganes/images_full/BCC_Aug/'+img_name[j]])

Приведенная выше команда создает файл CSV для болезни BCC.

#CSV generated for MEL disease
with open(r'C:/Users/ganes/mel.csv', 'a', newline='') as file:
writer = csv.writer(file)
img_name = os.listdir(mel_path)
for j in range(len(img_name)):
writer.writerow([img_name[j], 1,0,0,0,0,0,0,0,0,r'C:/Users/ganes/images_full/MEL_Aug/'+img_name[j]])

Приведенная выше команда создает файл CSV для заболевания MEL.

#CSV generated for BKL disease
with open(r'C:/Users/ganes/bkl.csv', 'a', newline='') as file:
writer = csv.writer(file)
img_name = os.listdir(bkl_path)
for j in range(len(img_name)):
writer.writerow([img_name[j], 0,0,0,0,1,0,0,0,0,r'C:/Users/ganes/images_full/BKL_Aug/'+img_name[j]])

Приведенная выше команда создает файл CSV для болезни BKL.

#CSV generated for NV disease
with open(r'C:/Users/ganes/nv.csv', 'a', newline='') as file:
writer = csv.writer(file)
img_name = os.listdir(nv_path)
for j in range(len(img_name)):
writer.writerow([img_name[j], 0,1,0,0,0,0,0,0,0,r'C:/Users/ganes/images_full/NV_full/'+img_name[j]])

Приведенная выше команда создает файл CSV для заболевания NV.

Теперь, когда у нас есть все CSV-файлы всех болезней, давайте прочитаем эти CSV-файлы.

ak = pd.read_csv(r'C:/Users/ganes/ak.csv')
bcc = pd.read_csv(r'C:/Users/ganes/bcc.csv')
bkl = pd.read_csv(r'C:/Users/ganes/bkl.csv')
df = pd.read_csv(r'C:/Users/ganes/df.csv')
mel = pd.read_csv(r'C:/Users/ganes/mel.csv')
nv = pd.read_csv(r'C:/Users/ganes/nv.csv')
scc = pd.read_csv(r'C:/Users/ganes/scc.csv')
vasc = pd.read_csv(r'C:/Users/ganes/vasc.csv')

Мы можем объединить все файлы в один фрейм данных, используя следующую команду

df3 = pd.concat([mel, nv, bcc, ak, bkl, df, vasc, scc])

Теперь у нас есть 62419 изображений после аугментации данных.

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

#Splitting dataset into X and y
df4_y = df3[df3.columns[1:10]]
df4_X = df3.drop(df3.columns[1:10], axis=1)
df4_y

Мы видим, что df4_y содержит фактические значения заболеваний для каждого изображения, которые мы можем использовать для сравнения с прогнозами модели, чтобы проверить точность модели.

Мы видим, что df4_x содержит имя файла изображения и место, где оно хранится.

Давайте создадим новую метку столбца, чтобы указать, какое заболевание имеет каждое изображение.

# Creating a new column 'label'
dummy = df4_y
dummy['label'] = "UNK"
for i in dummy.columns:
# i is column name
x = 0
for j in dummy[i]:
# j is the value of the column i
if j==1.0:
dummy.iloc[x, dummy.columns.get_loc('label')] = i
x+=1
dummy

Чтобы проверить распределение каждого класса в df4_y, мы будем использовать следующую команду.

# Distibution of each class in df4_y
df4_y['label'].value_counts()

Давайте определим номера классов для каждой метки

#Defining class numbers
def assignNewLabels(label):
if label == 'MEL':
return 0
elif label == 'NV':
return 1
elif label == 'BCC':
return 2
elif label=='AK':
return 3
elif label=='BKL':
return 4
elif label=='DF':
return 5
elif label=='VASC':
return 6
elif label=='SCC':
return 7
else:
return 8
dummy['Num_label'] = dummy['label'].apply(assignNewLabels)
dummy

Давайте сохраним фрейм данных в df4_y_new

df4_y_new = dummy['Num_label']
df4_y_new

Давайте применим стратифицированную выборку к набору данных для равномерного распределения каждого класса.

· Он используется для устранения систематической ошибки выборки в наборе тестов.

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

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

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

Как работает стратифицированная выборка.

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

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

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

#Splitting datasets in to  X_train_full, X_test, y_train_full, y_test
X_train_full, X_test, y_train_full, y_test = train_test_split( df4_X, df4_y_new, test_size=0.15, random_state=42, stratify=df4_y_new)

Попробуем напечатать форму X_train_full, X_test, y_train_full и y_test.

print(X_train_full.shape)
print(X_test.shape)
print(y_train_full.shape)
print(y_test.shape)

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

y_train_full.value_counts()

Давайте проверим то же самое и для y_test.

Далее мы разделим данные из полных данных поезда на данные проверки и данные поезда.

#Splitting datasets into validation data as well by taking 10% from x_train and y_train
X_train, X_valid, y_train, y_valid = train_test_split( X_train_full, y_train_full, test_size=0.15, random_state=42, stratify=y_train_full)

Точно так же мы можем распечатать форму и проверить распределение

После того, как все данные установлены, мы перейдем к части проекта, связанной с набором данных Tensorflow Batch. Мы установим размер изображения 224 X 224, и соответственно будут загружены каталог поезда и проверки.

IMG_SIZE = (224,224)
train_dir = '/content/gdrive/MyDrive/train'
valid_dir = '/content/gdrive/MyDrive/valid'

Затем мы будем использовать функцию image_dataset_from_directory tensorflow для загрузки изображений в виде тензора с размером пакета по умолчанию, равным 32.

train_dataset = tf.keras.utils.image_dataset_from_directory(train_dir,
shuffle=True,
batch_size=32,
image_size=IMG_SIZE,
labels='inferred',
label_mode='categorical',
color_mode='rgb',
seed=42)

Найдено 39334 файла, принадлежащих 8 классам.

Мы делаем аналогичный способ для набора данных проверки

valid_dataset = tf.keras.utils.image_dataset_from_directory(valid_dir,
shuffle=True,
batch_size=32,
image_size=IMG_SIZE,
labels='inferred',
label_mode='categorical',
color_mode='rgb',
seed=42)

Найдено 16749 файлов, принадлежащих 8 классам.

Наконец, мы сделали бы то же самое и для тестового набора данных.

test_dataset = tf.keras.utils.image_dataset_from_directory('/content/gdrive/MyDrive/test_new/',
shuffle=False,
batch_size=32,
image_size=IMG_SIZE,
labels='inferred',
label_mode='categorical',
color_mode='rgb',
seed=42)

Найдено 6346 файлов, принадлежащих 8 классам.

Давайте проверим имя класса, используя функцию train_dataset.class_names.

class_names = train_dataset.class_names
print(class_names)

[‘AK’, ‘BCC’, ‘BKL’, ‘DF’, ‘MEL’, ‘NV’, ‘SCC’, ‘VASC’]

Далее, как часть преобразования данных, давайте нормализуем значения пикселей в диапазоне от 0 до 1, используя функцию Layers.Rescaling.

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

Этот слой изменяет масштаб каждого значения ввода (часто изображения) путем умножения на масштаб и добавления смещения.

Например:

1. Чтобы изменить масштаб ввода в диапазоне [0, 255], чтобы он был в диапазоне [0, 1], вы должны передать масштаб = 1./255.

2. Чтобы изменить масштаб ввода в диапазоне [0, 255], чтобы он был в диапазоне [-1, 1], вы должны передать масштаб = 1./127,5, смещение = -1.

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

normalization_layer = layers.Rescaling(1./255)

Затем мы выполним нормализацию набора данных поезда.

normalized_train_ds = train_dataset.map(lambda x, y: (normalization_layer(x), y))
image_batch, labels_batch = next(iter(normalized_train_ds))
first_image = image_batch[0]
# Notice the pixel values are now in `[0,1]`.
print(np.min(first_image), np.max(first_image))

0.0 0.96666706

Давайте выполним это и для набора данных проверки.

normalized_valid_ds = valid_dataset.map(lambda x, y: (normalization_layer(x), y))
image_batch, labels_batch = next(iter(normalized_valid_ds))
first_image = image_batch[0]
# Notice the pixel values are now in `[0,1]`.
print(np.min(first_image), np.max(first_image))

0.1607832 0.8899341

Давайте выполним это и для тестирования набора данных.

normalized_test_ds = test_dataset.map(lambda x, y: (normalization_layer(x), y))
image_batch, labels_batch = next(iter(normalized_test_ds))
first_image = image_batch[0]
# Notice the pixel values are now in `[0,1]`.
print(np.min(first_image), np.max(first_image))

0.0 0.96386576

Далее давайте создадим функцию для построения кривой точности и потерь.

#Plotting of accuracy and loss
%matplotlib inline
#%config InlineBackend.figure_format = 'svg'
def plot_metric(history, metric):
train_metrics = history.history[metric]
val_metrics = history.history['val_'+metric]
epochs = range(1, len(train_metrics) + 1)
plt.plot(epochs, train_metrics)
plt.plot(epochs, val_metrics)
plt.title('Training and validation '+ metric)
plt.xlabel("Epochs")
plt.ylabel(metric)
plt.legend(["train_"+metric, 'val_'+metric])
plt.show()

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

#AUTOTUNE = tf.data.AUTOTUNE
train_dataset = normalized_train_ds #.prefetch(buffer_size=AUTOTUNE)
validation_dataset = normalized_valid_ds #.prefetch(buffer_size=AUTOTUNE)
test_dataset = normalized_test_ds

Присвоим num_classes длительности заболеваний

num_classes = len(class_names)

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

xception_model = tf.keras.models.load_model('/content/gdrive/MyDrive/models/xception_2.h5')
mobilenet_model = tf.keras.models.load_model('/content/gdrive/MyDrive/models/Mobilenet.h5')
densenet_model = tf.keras.models.load_model('/content/gdrive/MyDrive/models/Densenet201.h5')

Далее воспользуемся функцией ReduceLROnPlateau.

learning_rate_reduction = tf.keras.callbacks.ReduceLROnPlateau(
monitor='val_loss', factor=0.5, patience=10, verbose=0,
mode='auto', min_lr = 0.000001
)

Уменьшите скорость обучения, когда метрика перестала улучшаться. Модели часто выигрывают от снижения скорости обучения в 2–10 раз, когда обучение стагнирует. Этот обратный вызов отслеживает количество, и если не наблюдается улучшения для «терпения» количества эпох, скорость обучения снижается.

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

test_path = '/content/gdrive/MyDrive/test_new/'

Начнем с модели Xception.

base_model_Xception = Xception(include_top = False, weights = 'imagenet', input_shape = (224, 224, 3))
for layer in base_model_Xception.layers:
print(layer.name)
if hasattr(layer, 'moving_mean') and hasattr(layer, 'moving_variance'):
layer.trainable = True
K.eval(K.update(layer.moving_mean, K.zeros_like(layer.moving_mean)))
K.eval(K.update(layer.moving_variance, K.zeros_like(layer.moving_variance)))
else:
layer.trainable = False
print(len(base_model_Xception.layers))

Xception — это сверточная нейронная сеть, состоящая из 71 слоя.

Модель, которую мы использовали для этого проекта, имеет глубину 132 слоя.

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

Xception — это архитектура глубокой сверточной нейронной сети, которая включает в себя Depthwise Separable Convolutions.

Xception означает «экстремальное начало», оно доводит принципы начала до крайности. В Inception свертки 1x1 использовались для сжатия исходного ввода, и для каждого из этих входных пространств мы использовали разные типы фильтров для каждого пространства глубины. Xception просто отменяет этот шаг. Вместо этого он сначала применяет фильтры к каждой карте глубины, а затем, наконец, сжимает входное пространство с помощью свертки 1X1, применяя его по всей глубине. Этот метод практически идентичен разделимой по глубине свертке — операции, которая использовалась при проектировании нейронных сетей еще в 2014 году. Между Inception и Xception есть еще одно отличие. Наличие или отсутствие нелинейности после первой операции. В начальной модели за обеими операциями следует нелинейность ReLU, однако Xception не вносит никакой нелинейности.

Давайте проверим последний слой модели Xception.

last_layer_xcep = base_model_Xception.get_layer('block14_sepconv2_act')
print('last layer output shape:', last_layer_xcep.output_shape)
last_output_xcep = last_layer_xcep.output

выходная форма последнего слоя: (Нет, 7, 7, 2048)

Теперь давайте сгладим выходной слой, добавим полносвязный плотный слой, добавим отсев и последний слой softmax для классификации.

# Flatten the output layer to 1 dimension
x = layers.GlobalMaxPooling2D()(last_output_xcep)
# Add a fully connected layer with 512 hidden units and ReLU activation
x = layers.Dense(512, activation='relu')(x)
# Add a dropout rate of 0.5
x = layers.Dropout(0.5)(x)
# Add a final softmax layer for classification
x = layers.Dense(num_classes, activation='softmax')(x)
# Configure and compile the model
xception_model_new = Model(base_model_Xception.input, x)

Так что же такое GlobalMaxPooling2D()?

Обычный максимальный слой пула с размером пула, равным размеру входных данных (если быть точным, минус размер фильтра + 1). MaxPooling1D принимает аргумент pool_length, а GlobalMaxPooling1D — нет.

Например, если вход слоя максимального пула равен 0,1,2,2,5,1,2, выход глобального максимального пула равен 5, тогда как обычный слой максимального пула с размером пула равен 3 выходам 2,2,5,5. ,5 (при условии, что шаг=1).

Давайте посмотрим сводку модели

xception_model_new.summary()

Далее воспользуемся оптимизатором Adam и скомпилируем модель.

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

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

optimizer_adam = Adam(learning_rate=0.0001, beta_1=0.9, beta_2=0.999, epsilon=None, decay=0.0, amsgrad=True)
xception_model_new.compile(optimizer = optimizer_adam,
loss = 'categorical_crossentropy',
metrics = ['accuracy'])

Adam (Adaptive Moment Estimation) работает с импульсами первого и второго порядка. Интуиция за Адамом заключается в том, что мы не хотим катиться так быстро только потому, что можем перепрыгнуть через минимум, мы хотим немного уменьшить скорость для тщательного поиска. В дополнение к хранению экспоненциально затухающих средних значений квадратов градиентов в прошлом, таких как AdaDelta, Адамтакже хранит экспоненциально затухающие средние значения прошлых градиентов M (т).

M(t) и V(t) — это значения первого момента, который является средним, и второго момента, который является нецентрированная дисперсия градиентовсоответственно.

Первый и второй порядок импульса

Здесь мы берем среднее значение M(t) и V(t), так что E[m(t)] может быть равно E[g(t)], где E[f(x)] – ожидаемое значение f(x).

Чтобы обновить параметр:

Обновите параметры

Значения для β1 равны 0,9, 0,999 для β2 и (10 x exp(-8)) для ‘ϵ’.

Далее давайте обучим модель Xception.

tensor_board = TensorBoard(log_dir='./logs', histogram_freq = 0, batch_size = None)
checkpointer = tf.keras.callbacks.ModelCheckpoint(filepath='Saved models/weights.best.xception.hdf5',
verbose=1,
save_best_only=False)
history_xception_new = xception_model_new.fit(train_dataset,
validation_data = validation_dataset,
epochs = 100,
callbacks=[checkpointer, tensor_board, learning_rate_reduction],
verbose=1)

Мы достигли 98,31% точности обучения для модели Xception, а точность проверки — 88,19%. Потеря обучения составляет 0,0542, потеря проверки составляет 0,4674.

Сохраним модель.

xception_model_new.save("/content/gdrive/MyDrive/models/xception_2.h5")

Давайте построим график зависимости эпохи от точности, используя функцию plot_metric.

plot_metric(history_xception_new, 'accuracy')

Давайте теперь построим график эпохи и потерь, используя функцию plot_metric.

plot_metric(history_xception_new, 'loss')

Теперь давайте проверим точность, передав тестовые данные с помощью функции оценки.

loss_test, acc_test = xception_model.evaluate(test_dataset)

199/199 [==============================] — 168 с 803 мс/шаг — потери: 0,3939 — точность: 0,8943

Точность тестирования модели Xception составляет 89,43%.

Теперь давайте воспользуемся функцией model.predict для тестового набора данных, чтобы получить массив данных типа float.

y_pred_xcep = xception_model.predict(test_dataset)
y_pred_xcep

Давайте воспользуемся методом argmax(), присутствующим в математическом модуле тензорного потока. Этот метод используется для нахождения максимального значения по осям.

predicted_xcep = tf.argmax(y_pred_xcep, axis=1)
predicted_xcep

‹tf.Tensor: shape=(6346,), dtype=int64, numpy=array([0, 0, 0, …, 7, 7, 7])›

Давайте объединим значения x и y в тестовом наборе данных.

true_xcep = tf.concat([y for x, y in test_dataset], axis=0)
true_xcep

Давайте воспользуемся методом argmax для true_xcep.

true_xcep = tf.argmax(true_xcep, axis=1)
true_xcep

Давайте создадим цикл if else внутри цикла for, чтобы напечатать название класса болезни вместо присвоенного номера класса.

# Xception Predictions
for i in range(0,len(predicted_xcep.numpy())):
if predicted_xcep.numpy()[i] == 0:
print("AK")
elif predicted_xcep.numpy()[i] == 1:
print("BCC")
elif predicted_xcep.numpy()[i] == 2:
print("BKL")
elif predicted_xcep.numpy()[i] == 3:
print("DF")
elif predicted_xcep.numpy()[i] == 4:
print("MEL")
elif predicted_xcep.numpy()[i] == 5:
print("NV")
elif predicted_xcep.numpy()[i] == 6:
print("SCC")
elif predicted_xcep.numpy()[i] == 7:
print("VASC")

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

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

Это чрезвычайно полезно для измерения полноты, точности, специфичности, точности и, что наиболее важно, кривых AUC-ROC.

# Confusion matrix for Xception
conf_mat_xcep = confusion_matrix(true_xcep,predicted_xcep)

Давайте построим матрицу путаницы для модели Xception, используя библиотеку seaborn и matplotlib.

# Plotting the confusion matrix
plt.figure(figsize = (15,8))
ax = sns.heatmap(conf_mat_xcep, annot=True, cmap='BuPu', fmt='.0f')
ax.set_title('Seaborn Confusion Matrix for Xception\n\n')
ax.set_xlabel('\nPredicted Values')
ax.set_ylabel('Actual Values ')
ax.set_xticklabels(['AK', 'BCC', 'BKL', 'DF', 'MEL', 'NV', 'SCC', 'VASC'])
ax.set_yticklabels(['AK', 'BCC', 'BKL', 'DF', 'MEL', 'NV', 'SCC', 'VASC'])
## Display the visualization of the Confusion Matrix.
plt.show()

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

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

print(metrics.classification_report(true_xcep, predicted_xcep, digits=8))

Точность тестирования модели Xception составляет 89,42% для 6346 случаев, которые были переданы ей.

На этом модель Xception заканчивается, теперь мы сосредоточимся на модели мобильной сети.

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

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

base_model_mobilenet = MobileNet(include_top = False, weights = 'imagenet', input_shape = (224, 224, 3))
for layer in base_model_mobilenet.layers:
print(layer.name)
if hasattr(layer, 'moving_mean') and hasattr(layer, 'moving_variance'):
layer.trainable = True
K.eval(K.update(layer.moving_mean, K.zeros_like(layer.moving_mean)))
K.eval(K.update(layer.moving_variance, K.zeros_like(layer.moving_variance)))
else:
layer.trainable = False
print(len(base_model_mobilenet.layers))

Наша модель Mobilenet имеет 86 уровней глубины.

Придадим форму последнему слою.

last_layer_mobi = base_model_mobilenet.get_layer('conv_pw_13_relu')
print('last layer output shape:', last_layer_mobi.output_shape)
last_output_mobi = last_layer_mobi.output

выходная форма последнего слоя: (Нет, 7, 7, 1024)

Давайте сгладим выходной слой до 1 измерения, добавим коэффициент отсева 0,5, добавим плотный слой с функцией активации как softmax.

# Flatten the output layer to 1 dimension
x = layers.GlobalMaxPooling2D()(last_output_mobi)
# Add a fully connected layer with 512 hidden units and ReLU activation
x = layers.Dense(512, activation='relu')(x)
# Add a dropout rate of 0.5
x = layers.Dropout(0.5)(x)
# Add a final softmax layer for classification
x = layers.Dense(8, activation='softmax')(x)
# Configure and compile the model
mobilenet_model_new = Model(base_model_mobilenet.input, x)

Давайте посмотрим на сводку модели

mobilenet_model_new.summary()

Давайте оптимизируем модель с помощью Adam и скомпилируем модель

optimizer_adam = Adam(learning_rate=0.0001, beta_1=0.9, beta_2=0.999, epsilon=None, decay=0.0, amsgrad=True)globa
mobilenet_model_new.compile(optimizer = optimizer_adam,
loss = 'categorical_crossentropy',
metrics = ['accuracy'])

Давайте обучим модель

checkpointer = ModelCheckpoint(filepath='Saved models/weights.best.mobilenet.hdf5',verbose=1,save_best_only=False)
history_mobilenet = mobilenet_model_new.fit(train_dataset,
validation_data = validation_dataset,epochs = 200, callbacks=[checkpointer, learning_rate_reduction],verbose=1)

Точность обучения модели Mobilenet составляет 93,49%, а точность проверки — 84,08%, потери при обучении — 0,1836, а потери при проверке — 0,8408.

Давайте построим эпохи против точности на графике

# Epochs vs Accuracy
plot_metric(history_mobilenet,'accuracy')

Давайте построим Эпохи против Потери

# Epochs vs Loss
plot_metric(history_mobilenet,'loss')

Сохраним модель мобильной сети

#Saving the Mobilenet model with .h5 extension
mobilenet_model_new.save("/content/gdrive/MyDrive/models/Mobilenet.h5")

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

Таким образом, это завершает модель мобильной сети, которая дает точность тестирования 86,57%.

Далее мы сосредоточимся на модели Densenet201.

DenseNet (Dense Convolutional Network) — это архитектура, которая направлена ​​на то, чтобы сделать сети глубокого обучения еще более глубокими, но в то же время сделать их более эффективными для обучения за счет использования более коротких соединений между слоями.

В архитектуре DenseNet каждый уровень связан со всеми остальными уровнями, отсюда и название сверточной сети с плотным подключением. … Для каждого слоя карты объектов всех предыдущих слоев используются в качестве входных данных, а его собственные карты объектов используются в качестве входных данных для каждого последующего слоя.

pre_trained_densenet_model = DenseNet201(input_shape=(224, 224, 3), include_top=False, weights="imagenet")

Загружаем предварительно обученную модель плотной сети

for layer in pre_trained_densenet_model.layers:
print(layer.name)
if hasattr(layer, 'moving_mean') and hasattr(layer, 'moving_variance'):
layer.trainable = True
K.eval(K.update(layer.moving_mean, K.zeros_like(layer.moving_mean)))
K.eval(K.update(layer.moving_variance, K.zeros_like(layer.moving_variance)))
else:
layer.trainable = False
print(len(pre_trained_densenet_model.layers))

Количество слоев в Densenet — 707.

Давайте определим модель

# Flatten the output layer to 1 dimension
x = layers.GlobalMaxPooling2D()(last_output)
# Add a fully connected layer with 512 hidden units and ReLU activation
x = layers.Dense(512, activation='relu')(x)
# Add a dropout rate of 0.7
x = layers.Dropout(0.5)(x)
# Add a final softmax layer for classification
x = layers.Dense(num_classes, activation='softmax')(x)
# Configure and compile the model
densenet_model = Model(pre_trained_densenet_model.input, x)
optimizer = Adam(lr=0.0001, beta_1=0.9, beta_2=0.999, epsilon=None, decay=0.0, amsgrad=True)
densenet_model.compile(loss='categorical_crossentropy',
optimizer=optimizer,
metrics=['accuracy'])

Распечатаем сводку модели

densenet_model.summary()

Давайте обучим модель

tensor_board = TensorBoard(log_dir='./logs', histogram_freq = 0, batch_size = None)
checkpointer_densenet = ModelCheckpoint(filepath='Saved models/weights.best.densenet.hdf5',
verbose=1,
save_best_only=False)
history_densenet = densenet_model.fit(train_dataset,validation_data = validation_dataset,
epochs = 50,
callbacks=[checkpointer_densenet,tensor_board, learning_rate_reduction],
verbose=1)

Точность обучения для модели плотной сети составляет 99,40%, а точность проверки — 89,08%, потери при обучении — 0,0187, а потери при проверке — 0,5521.

Когда мы строим график зависимости эпохи от точности и эпохи от потерь, мы получаем следующие графики.

Модель DenseNet имеет точность тестирования 91,09%.

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

from collections import Counter
def Majority_Voting(predicted_dense, predicted_mobi, predicted_xcep):
final_pred = []
for i in range(0, 6346):
x = []
x = Counter([predicted_dense.numpy()[i], predicted_mobi.numpy()[i], predicted_xcep.numpy()[i]])
final_pred = np.append(final_pred, x.most_common(1)[0][0])
return final_pred

Используя метод голосования по большинству в Ensemble, мы получаем точность 91,96%.

Ссылки:

https://www.kaggle.com/andrewmvd/isic-2019 https://www.tensorflow.org/tutorials/images/classification https://github.com/SwagatSBhuyan/Skin-Cancer-Classification -Использование алгоритма глубокого обучения CNN

https://androidkt.com/how-to-set-steps-per-epoch-validation-steps-and-validation-split-in-kerass-fit-method/ https://www.mathworks.com /help/deeplearning/ref/xception.html https://vitalflux.com/machine-learning-training-validation-test-data-set/

Ссылка на репозиторий Github:

https://github.com/vbangpmn/Skin_disease_identification