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

Рак кожи - самый распространенный вид рака. Это происходит из-за аномального роста клеток кожи, обычно на участках, подверженных воздействию солнечного света. Существует три основных типа рака кожи: базальноклеточный рак, плоскоклеточный рак и меланома. В частности, меланома является причиной 75% смертей от рака кожи, несмотря на то, что она является наименее распространенным раком кожи.

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

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

Примечание. Следующий код реализован на ядре Kaggle с использованием набора данных SIIM-ISIC Melanoma Classification, предоставленного Kaggle. Вы можете найти весь код здесь.

Мы выполним следующие шаги для реализации алгоритма.

Импорт библиотек

Давайте начнем реализацию с импорта необходимых библиотек внутри ядра Kaggle, как показано ниже:

import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns
import cv2
import pydicom as dicom
import pandas as pd
import numpy as np
from sklearn.metrics import accuracy_score, confusion_matrix
from sklearn.metrics import mean_squared_error, r2_score

Импортированная библиотека pydicom используется для работы с изображениями, содержащимися в наборе данных. Эти изображения относятся к типу DICOM (Digital Imaging and Communications in Medicine), который является международным стандартом для передачи, хранения, извлечения, печати, обработки и отображения информации медицинских изображений.

Анализ набора данных

Следующим шагом является импорт набора данных SIIM-ISIC Melanoma Classification, предоставленного Kaggle.

df = pd.read_csv('/kaggle/input/siim-isic-melanoma-classification/train.csv')
df.head()

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

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

Классификация изображений

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

Признаки меланомы включают:

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

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

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

s0 = df.target[df.target.eq(0)].sample(50).index
s1 = df.target[df.target.eq(1)].sample(60).index
df = df.loc[s0.union(s1)]
df['target'].value_counts()

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

Давайте теперь посмотрим на изображения. Эти изображения представлены в формате DICOM, поэтому для чтения изображений мы используем функцию dicom.dcmread (), предоставляемую библиотекой pydicom.

image = '/kaggle/input/siim-isic-melanoma-classification/train/' + df['image_name'][1512] +'.dcm'
ds = dicom.dcmread(image)
plt.imshow(ds.pixel_array)

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

Поэтому для каждого изображения в наборе данных мы считываем изображение с помощью dicom.dmread () и извлекаем пиксели с помощью ds.pixel_array. Эти пиксели многомерны, поэтому мы преобразуем их в одномерный массив с помощью функции flatten. Затем мы добавляем эти изображения в формате пикселей в список с названием изображения.

images = []
for x in df['image_name']:
    image = '/kaggle/input/siim-isic-melanoma-classification/train/' + x +'.dcm'
    ds = dicom.dcmread(image)
    pixels = ds.pixel_array
    images.append(pixels.flatten())

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

import tensorflow as tf
images = tf.keras.preprocessing.sequence.pad_sequences(
  images,
  maxlen = 720,
  dtype = "int32",
  padding = "pre",
  truncating = "pre",
  value = 0
)

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

test = df.tail(50)
test.head()

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

test_images = []
count = 0
for x in test['image_name']:
    image = '/kaggle/input/siim-isic-melanoma-classification/train/' + x +'.dcm'
    ds = dicom.dcmread(image)
    pixels = ds.pixel_array
    
    test_images.append(pixels.flatten())
    count +=1
    print(count)
test_images = tf.keras.preprocessing.sequence.pad_sequences(
  test_images,
  maxlen = 720,
  dtype = "int32",
  padding = "pre",
  truncating = "pre",
  value = 0
)

Наконец, пришло время обучить нашу модель.

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

Мы установим значение

X = изображения (список, содержащий изображения в формате пикселей)

y = np.array (df [‘target’]) (значения, указывающие, является ли поражение на изображении доброкачественным или злокачественным)

1. Логистическая регрессия

from sklearn.linear_model import LogisticRegression
X = images
y = np.array(df['target'])
classifier_lr = LogisticRegression()
classifier_lr.fit(X,y)
X_test = test_images
y_test = np.array(test['target'])
y_pred_lr = classifier_lr.predict(X_test)
print('Accuracy Score: ',accuracy_score(y_test,y_pred_lr))
print('Confusion Matrix: \n',confusion_matrix(y_test,y_pred_lr))

2. Машина опорных векторов

from sklearn import svm
X = images
y = np.array(df['target'])
classifier_svm = svm.SVC()
classifier_svm.fit(X,y)
X_test = test_images
y_test = np.array(test['target'])
y_pred_svm = classifier_svm.predict(X_test)
print('Accuracy Score: ',accuracy_score(y_test,y_pred_svm))
print('Confusion Matrix: \n',confusion_matrix(y_test,y_pred_svm))

3. Дерево решений

from sklearn.tree import DecisionTreeClassifier
X = images
y = np.array(df['target'])
classifier_dt = DecisionTreeClassifier()
classifier_dt.fit(X,y)
X_test = test_images
y_test = np.array(test['target'])
y_pred_dt = classifier_dt.predict(X_test)
print('Accuracy Score: ',accuracy_score(y_test,y_pred_dt))
print('Confusion Matrix: \n',confusion_matrix(y_test,y_pred_dt))

4. Случайный лес

from sklearn.ensemble import RandomForestClassifier
X = images
y = np.array(df['target'])
classifier_rf = RandomForestClassifier()
classifier_rf.fit(X,y)
X_test = test_images
y_test = np.array(test['target'])
y_pred_rf = classifier_rf.predict(X_test)
print('Accuracy Score: ',accuracy_score(y_test,y_pred_rf))
print('Confusion Matrix: \n',confusion_matrix(y_test,y_pred_rf))

5. Адаптивное ускорение

from sklearn.ensemble import AdaBoostClassifier
X = images
y = np.array(df['target'])
classifier_ab = AdaBoostClassifier()
classifier_ab.fit(X,y)
X_test = test_images
y_test = np.array(test['target'])
y_pred_ab = classifier_ab.predict(X_test)
print('Accuracy Score: ',accuracy_score(y_test,y_pred_ab))
print('Confusion Matrix: \n',confusion_matrix(y_test,y_pred_ab))

6. Повышение градиента

from sklearn.ensemble import GradientBoostingClassifier
X = images
y = np.array(df['target'])
classifier_gb = GradientBoostingClassifier()
classifier_gb.fit(X,y)
X_test = test_images
y_test = np.array(test['target'])
y_pred_gb = classifier_gb.predict(X_test)
print('Accuracy Score: ',accuracy_score(y_test,y_pred_gb))
print('Confusion Matrix: \n',confusion_matrix(y_test,y_pred_gb))

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

Классификация с использованием записей пациентов

Давайте начнем с переименования «anatom_site_general_challenge» в «site» для нашего удобства.

df = df.rename(columns = {'anatom_site_general_challenge':'site'})

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

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

df = df.dropna(axis=0, how = 'any')

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

age = []
for i in range(df.shape[0]):
    try: 
        if df['target'][i] == 1:
            age.append(df['age_approx'][i]) 
    except:
        pass
plt.figure(figsize=(15,5))
plt.subplot(1,2,1)
sns.distplot(age)
plt.title('Distribution of age of people having malignant cancer')
plt.subplot(1,2,2)
sns.countplot(y = age)
plt.ylabel('Age')
plt.title('Count plot of age of people having malignant cancer')

Следующие моменты можно вывести из приведенных выше графиков:

  • Возраст большинства пациентов, страдающих злокачественными новообразованиями, составляет от 40 до 80 лет.
  • Максимальное количество больных раком имеет возрастные значения более 60, за ними следуют люди с возрастными значениями от 55 до 60.
  • Количество пациентов, страдающих онкологическими заболеваниями и имеющих возраст до 10 лет, очень мало.

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

site = []
for i in range(df.shape[0]):
    try: 
        if df['target'][i] == 1:
            site.append(df['site'][i]) 
    except:
        pass
sns.countplot(y = site)
sns.countplot(y = site,palette="rocket")
plt.title('Graph showing count of patients having cancer and the site it is located in')
plt.ylabel('Site')

Следующие моменты можно вывести из приведенных выше графиков:

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

Как видите, атрибуты пол, сайт и диагноз содержат категориальные данные. Итак, мы используем pandas.get_dummies () для преобразования категориальных данных в фиктивные или индикаторные переменные, понятные модели.

Мы также удаляем ненужные столбцы, benign_malignant и Patient_id.

Это можно сделать следующим образом:

df = pd.get_dummies(df, columns = ['sex'],drop_first=True)
df = pd.get_dummies(df, columns = ['site'],drop_first=True)
df = pd.get_dummies(df, columns = ['diagnosis'],drop_first=True)
df = df.drop('diagnosis_unknown', axis = 1)
df = df.drop(['benign_malignant', 'patient_id'], axis = 1)
df.head()

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

plt.figure(figsize = (10,10))
sns.heatmap(df.corr()[['target']].sort_values('target').tail(16), annot = True)

Из приведенной выше тепловой карты можно сделать вывод о следующих моментах:

  • диагностика_меланома имеет прямую корреляцию с целевым значением. Следовательно, если у человека диагностирована меланома, у него рак, а если у человека не диагностирована меланома, у него нет рака.
  • age_approx, sex_male и site_upper extremity положительно коррелируют с целью.
  • Diagnos_nevus, site_lower extremity и site_torso отрицательно коррелируют с целью.

Это также можно интерпретировать по графикам, приведенным ниже.

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

Начнем с создания набора данных для поезда и тестирования.

X = df[['diagnosis_melanoma','site_torso','diagnosis_nevus','site_lower extremity','site_upper extremity', 'sex_male', 'age_approx']]
y = df['target']
from sklearn.model_selection import train_test_split
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size = 0.3, random_state = 1)

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

После успешного обучения алгоритмов мы получаем следующие оценки точности:

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

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

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

Карта пациента выглядит следующим образом:

image_name = ISIC_0149568

age_approx = 55

sex_male = 0

site_lower extremity = 0

site_torso = 0

site_upper extremity = 1

диагноз_меланома = 1

диагностика_nevus = 0

image_path = '/kaggle/input/siim-isic-melanoma-classification/train/ISIC_0149568.dcm'
details = [[55,0,0,0,1,1,0]]
image_to_test = []
ds = dicom.dcmread(image_path)
pixels = ds.pixel_array
plt.imshow(pixels)
image_to_test.append(pixels.flatten())
image_to_test = tf.keras.preprocessing.sequence.pad_sequences(
  image_to_test,
  maxlen = 720,
  dtype = "int32",
  padding = "pre",
  truncating = "pre",
  value = 0
)
print(train2.predict(image_to_test))
if train1.predict(details) == [1]:
    
    result1 = 'Malignant'
else:
    result1 = 'Benign'
if train2.predict(image_to_test) == [1]:
    result2 = 'Malignant'
else:
    result2 = 'Benign'
print('Result from patient details: ', result1)
print('Result from patient image: ', result2)

image_path сохраняет путь к image_name.

Details хранит сведения о пациентах.

train1 - это модель, обученная с использованием записей пациентов.

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

После выполнения кода получаем следующий результат:

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

Найдите здесь весь код.

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

использованная литература