Медицинское машинное обучение

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

Разработка модели машинного обучения для определения наличия у человека сердечного заболевания по боли в груди и другим признакам.

Предпосылки

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

  1. Python
  2. Pandas (библиотека анализа данных)
  3. Numpy (библиотека научных вычислений)
  4. Scikit Learn (для предварительной обработки данных и выбора модели)

Набор данных

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

На странице UCI в качестве главного исследователя, ответственного за сбор данных, упоминаются следующие:
1. Венгерский институт кардиологии. Будапешт: Андраш Яноси, доктор медицины
2. Университетская больница, Цюрих, Швейцария: Уильям Штайнбрунн, доктор медицины
3. Университетская больница, Базель, Швейцария: Маттиас Пфистерер, доктор медицинских наук
4. В.А. Медицинский центр, Фонд клиники Лонг-Бич и Кливленда: Роберт Детрано, доктор медицины, доктор философии.

Введение

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

Часть кода

2.1 Подготовка данных

Прежде всего, давайте взглянем на наш словарь данных:

Здесь у нас есть всего 14 столбцов, 13 столбцов для наших функций и 1 столбец для метки.

import pandas as pd
df = pd.read_csv('data.csv')

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

df.head()

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

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

df = pd.read_csv('data.csv', sep=",", names=["Age", "Sex", "CP", "Trestbps", "Chol", "Fbs", "Restecg", "Thalach", "Exang", "Oldpeak", "Slope", "CA", "Thal", "Label"])
df.head()

Теперь это выглядит хорошо.

2.2 Изучение данных

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

df.info()

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

Теперь без промедления приступим к разработке функций.

2.3 Разработка функций

Как мы все знаем, Feature Engineering - самая важная часть в Data Science. Отсюда мы сможем получить представление о наших данных и разработать лучшие функции, которые положительно влияют на нашу классификацию.

2.3.1 Исправление данных

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

Если мы посмотрим на описание данных, предоставленное UCI, мы увидим, что значения столбца label (num) разделены на 2 категории:

  1. значение 0: означает отсутствие сердечных заболеваний
  2. значение 1: означает наличие болезни сердца

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

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

def mapLabels(value):
    if value > 0:
        return 1
    else:
        return 0

приведенная выше функция принимает значение и возвращает 1, если значение больше 0, иначе возвращает 0.

Сопоставим это с нашим ярлыком:

df['Label'] = df['Label'].map(mapLabels)
df['Label'].value_counts()

Еще одна нестандартная вещь в наших данных:

в нашем столбце CA и Thal он является типом данных объекта, но значения являются плавающими.

import numpy as np
df.select_dtypes([np.object]).head()

Здесь мы используем numpy для выбора типа данных объекта из нашего набора данных.

Немного покопавшись, я обнаружил, что столбцы CA и Thal имеют некоторые нулевые значения, установленные как «?».

Исправим и эти данные:

def caToFloat(value):
    if not value == '?':
       return float(value)
    else:
        return 0
df['CA'] = df['CA'].map(caToFloat)

Здесь мы преобразуем значения нашего столбца CA в значение с плавающей запятой и любое значение с '?' В 0. Так как данных с '?' Было мало, а наши данные изначально имели размер [0–3] ] категории; их можно сопоставить с 0.

для данных Thal:

df['Thal'].value_counts()

Как мы видим, большинство наших данных имеют значение 3,0, поэтому мы можем сопоставить эти значения '?' со значением 3,0, поскольку они только 2 цифрами.

df['Thal'] = df['Thal'].map(lambda x : 3.0 if x == '?' else float(x))
df['Thal'].value_counts()

Теперь давайте проверим информацию о наших данных:

df.info()

Все наши значения числовые.

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

df.loc[df['Age'] <= 16, 'Age']  = 0,
df.loc[(df['Age'] > 16) & (df['Age'] <= 26), 'Age']  = 1,
df.loc[(df['Age'] > 26) & (df['Age'] <= 36), 'Age']  = 2,
df.loc[(df['Age'] > 36) & (df['Age'] <= 62), 'Age']  = 3,
df.loc[df['Age'] > 16, 'Age']  = 4

Здесь мы разделили наши данные о возрасте на [0,1,2,3,4] классы, где:

Ребенок: 0 Молодой: 1 Взрослый: 2 Средний возраст: 3 Старший: 4

df['Age'].value_counts()

Как видите, в наших данных нет детей или подростков.

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

2.4 Предварительная обработка данных

Пришло время подготовить наши данные для классификации.

labels = df['Label']
features = df.drop(['Label], axis=1)

Здесь мы отделяем функции и метки от нашего фрейма данных.

Теперь мы собираемся разделить наши данные на обучающие и тестовые данные с помощью scikitlearn.

from sklearn.model_selection import train_test_split
tran_x, test_x, train_y, test_y = train_test_split(features,labels, shuffle=True)

2.5 Выбор модели

Теперь мы готовы обучать наши данные.

Чтобы выбрать нашу лучшую модель и обучить на ней наши данные, мы будем использовать sklearn.

from sklearn.svm import SVC
from sklearn.tree import DecisionTreeClassifier
from sklearn.ensemble import RandomForestClassifier
from sklearn.naive_bayes import GaussianNB
from sklearn.model_selection import KFold, cross_val_score
from sklearn.ensemble import GradientBoostingClassifier
k_fold = KFold(n_splits=12, shuffle=True, random_state=0)

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

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

Здесь мы собираемся использовать 12 разделений KFold.

Теперь давайте обучим нашу первую модель, которая является SVC или SVM (Support Vector Machine).

clf = SVC(gamma='auto')
scoring = 'accuracy'
score = cross_val_score(clf, train_x, train_y, cv=k_fold, n_jobs=1, scoring=scoring)
print(score)
## OUTPUT: 61.38

От SVC мы получаем только 61,38% точности.

Посмотрим и на другой модели:

Классификатор повышения градиента

clf = GradientBoostingClassifier()
scoring = 'accuracy'
score = cross_val_score(clf, train_x, train_y, cv=k_fold, n_jobs=1, scoring=scoring)
round(np.mean(score)*100, 2)
## OUTPUT: 77.35

Классификатор дерева решений

clf = DecisionTreeClassifier()
scoring = 'accuracy'
score = cross_val_score(clf, train_x, train_y, cv=k_fold, n_jobs=1, scoring=scoring)
round(np.mean(score)*100, 2)
## OUTPUT: 75.91

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

clf = RandomForestClassifier(n_estimators=10)
scoring = 'accuracy'
score = cross_val_score(clf, train_x, train_y, cv=k_fold, n_jobs=1, scoring=scoring)
round(np.mean(score)*100, 2)
## OUTPUT: 83.28

Наивный байесовский

clf = GaussianNB()
scoring = 'accuracy'
score = cross_val_score(clf, train_x, train_y, cv=k_fold, n_jobs=1, scoring=scoring)
round(np.mean(score)*100, 2)
## OUTPUT : 85.95

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

Наши оценки низкие, потому что мы неправильно спроектировали наши данные. Вы можете получить больше очков, если будете больше очищать данные и выбирать функции.

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

2.6 Модель обучения

Давайте обучим нашу модель с помощью алгоритма наивного байесовского классификатора:

clf = GaussianNB()
clf.fit(train_x, train_y)

Теперь посмотрим на наши прогнозы:

predictions = clf.predict(test_x)
values = list(zip(predictions, test_y.values))
status = []
for x, y in list_of_values:
    status.append(x == y)
list_of_values = list(zip(predictions, test_y.values, status))
final_df = pd.DataFrame(list_of_values, columns=['Predicted', 'Actual', "Status"])

Здесь мы видим, что у нас также есть несколько ложных классификаций, вы можете улучшить их, улучшив наш выбор функций.

Вывод

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

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

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

На этом пока все. Надеюсь, вам понравился проект и вы кое-чему научились.