Не е нужно да сте доктор по данни, за да разберете машинното обучение. Тази статия преминава през просто упражнение, използвайки широко достъпен набор от данни и алгоритми за машинно обучение с отворен код, за да предскаже резултатите на пациентите и да предложи осезаема възвръщаемост на инвестициите. Готов? Хайде да го направим.

Основни инструменти

Кодът и алгоритмите с отворен код, с които ще работя, са написани на Python, изключително популярен, добре поддържан и развиващ се език за анализ на данни. Най-лесният начин да започнете е да използвате приложение за кодиране на нотационен стил, Jupyter Notebook, за писане на код в кодови клетки и анотиране в текстови клетки. Преносимите компютри Jupyter могат да се създават и хостват онлайн, което означава, че те се грижат за целия хостинг, GPU обработка и конфигурация. Всичко, което правите, е да пишете и изпълнявате своя код. Още по-лесен начин да започнете е с вашия акаунт в Google да създадете бележник Google Colab, който ви позволява да стартирате Python във вашия браузър с лесно споделяне и сътрудничество на бележници. Вашият бележник създаден ли е? Нека намерим данните, с които да работим.

Източници на данни

Машинното обучение е форма на изкуствен интелект, която използва данни и алгоритми за прогнозиране на резултати, когато резултатът не е известен. По този начин ключовият вход за машинното обучение са данните. Вместо да създавате свои собствени набори от данни, за да обучавате моделите с променливата за реакция, обозначена за събития, настъпили с известни резултати, можете да използвате набори, които са открито достъпни от източници като Kaggle, UCI, AWS Open Data или „Отворени данни на правителството на САЩ“.

Фокусирам се върху сектора на здравеопазването и Kaggle разполага с някои страхотни набори от данни. По-конкретно, наборът от данни за „Инсулт“ е интригуващ с променливата за отговор „Инсулт“, която вече е обозначена в записите на събития на пациенти. Тази променлива за отговор ще ни е необходима за модели за обучение по-късно. И така, очертавайки прогнозата, към която се стремим: има ли пациентът вероятно да получи инсулт или да няма инсулт въз основа на категоричните данни от досиетата на пациентите.

Проучвателен анализ на данни и предварителна обработка

Първо, като използвате бележника на Colab, прочетете набора от данни и започнете да изследвате качеството на данните.

import pandas as pd
stroke_df = pd.read_csv('/content/sample_data/healthcare-dataset-stroke-data.csv')
print(stroke_df.shape)
print(stroke_df.head(10))
print(stroke_df.describe().T)

Виждам, че има 201 NaN стойности за променлива BMI. Решавам да припиша стойности за ИТМ, тъй като има 40 пациенти, преживели инсулт, които нямат стойност на ИТМ. Медианата е избрана пред средната, тъй като ИТМ има дълга дясна опашка.

stroke_df.fillna(stroke_df.median(), inplace=True)
stroke_df

След това категоричният набор от данни се трансформира преди обработка чрез алгоритми. Характеристики само с 2 стойности бяха трансформирани в 1 и 0 с помощта на картографиране на класове: ever_married, Residence_type, gender. Функции с повече от 2 стойности бяха трансформирани с помощта на One-Hot Label Encoding с вградената функция get_dummies на Pandas: work_type, smoking_status.

import pandas as pd
import numpy as np
# transform nominal variables that only have 2 values
class_mapping = {label: idx for idx, label in enumerate(np.unique(stroke_df['ever_married']))}
print(class_mapping)
stroke_df['ever_married'] = stroke_df['ever_married'].map(class_mapping)
class_mapping = {label: idx for idx, label in enumerate(np.unique(stroke_df['Residence_type']))}
print(class_mapping)
stroke_df['Residence_type'] = stroke_df['Residence_type'].map(class_mapping)
class_mapping = {label: idx for idx, label in enumerate(np.unique(stroke_df['gender']))}
print(class_mapping)
stroke_df['gender'] = stroke_df['gender'].map(class_mapping)
# transform nominal variables that have more than 2 values
stroke_df[['work_type','smoking_status']] = stroke_df[['work_type','smoking_status']].astype(str)
# concatenate the nominal variables from pd.getdummies and the ordinal variables to form the final dataset
transpose = pd.get_dummies(stroke_df[['work_type','smoking_status']])
stroke_dummies_df = pd.concat([stroke_df,transpose],axis=1)[['id','age','hypertension','heart_disease','ever_married','Residence_type','avg_glucose_level','bmi','gender','work_type_Govt_job','work_type_Never_worked','work_type_Private','work_type_Self-employed','work_type_children','smoking_status_Unknown','smoking_status_formerly smoked','smoking_status_never smoked','smoking_status_smokes','stroke']]
stroke_dummies_df

След това анализирам променливите за изкривяване с вградените в Python matplotlib и seaborn функции.

import seaborn as sns
import matplotlib.pyplot as plt
sns.set(style="darkgrid")
sns.histplot(data=stroke_df, x="gender", discrete=True)
plt.show()

На този етап има добро разбиране на набора от данни, но как да разберем кои променливи влияят най-много на променливата на отговора „удар“? За да отговоря на този въпрос, провеждам корелационен анализ, за ​​да определя важността на характеристиките, използвайки Random Forest Classifier от sklearn.ensemble. По-долу можем ясно да видим променливите като възраст, средно ниво на глюкоза и ИТМ имат най-силно влияние върху инсулт.

Нека разгледаме малко по-отблизо нашата променлива за отговор „удар“. В края на краищата ние се опитваме да предвидим дали пациентът ще получи или няма да получи инсулт. Само 249 записа от 5109 записа имат обозначение с инсулт, което е значителен класов дисбаланс. Ако използваме тези данни, за да обучим модела, класовият дисбаланс вероятно ще изкриви производителността. За да преодолея това, използвах техника за наука за данни, наречена SMOTE от библиотеката за обучение на дисбаланс. SMOTE използва най-близките съседи, за да интерполира точки от данни в евклидовото пространство. Специално използвах SMOTE за номинални и непрекъснати функции (SMOTE-NC), който създава синтетични данни за категорични характеристики. След стартиране на SMOTE_NC, данните за променливата на отговора бяха увеличени от 249 на 4869 — предоставяйки повече данни за изпълнение на модела.

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

Сега, когато наборът от данни е анализиран, почистен, трансформиран и балансиран — данните трябва да бъдат разделени между тренировъчни данни (използвани за обучение на модела) и тестови данни (които да бъдат преминати през модела, след като бъде обучен).

# This is what the train/test dataset looks like if we do not apply SMOTENC.
from sklearn.model_selection import train_test_split
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, stratify=y, random_state=1)
print(X.shape, y.shape, X_train.shape, y_train.shape, X_test.shape, y_test.shape)

(5109, 17) (5109,) (4087, 17) (4087,) (1022, 17) (1022,)

# We will create the train and test dataset from the X and y data after running SMOTENC
from sklearn.model_selection import train_test_split
X_smote_train, X_smote_test, y_smote_train, y_smote_test = train_test_split(X_smote, y_smote, test_size=0.2, random_state=1)
print(X.shape, y.shape, X_smote_train.shape, y_smote_train.shape, X_smote_test.shape, y_smote_test.shape)

(5109, 17) (5109,) (7776, 17) (7776,) (1944, 17) (1944,)

Можем да видим очаквания процент данни за набори от данни за влак и тест с увеличен обем данни след стартиране на SMOTE-NC. За да щракна два пъти върху моделите на данни, изпълнявам PCA (анализ на главните компоненти) намаляване на размерността. Процесът на PCA идентифицира области с максимална вариация в данните с големи размери и проектира върху по-малко измерения в подпространство. „Това“ е страхотна статия, която се гмурка дълбоко в тази техника.

pca = PCA(n_components=3)
#dimensionality reduction:
X_smote_train_pca = pca.fit_transform(X_smote_train_std)
X_smote_test_pca = pca.transform(X_smote_test_std)
X_smote_train_pca_df = pd.DataFrame(X_smote_train_pca)
X_smote_train_pca_df.columns = ['PCA1', 'PCA2', 'PCA3']
x1 = X_smote_train_pca_df['PCA1']
y1 = X_smote_train_pca_df['PCA2']
z1 = X_smote_train_pca_df['PCA3']
sns.set(style= 'darkgrid')
fig = plt.figure(figsize= (16, 16))
ax = plt.axes(projection='3d')
ax.scatter3D(x1, y1, z1,
alpha=1,
c = y_smote_train,
cmap = 'cool',
depthshade=False,s=10)
ax.legend(y_smote_train)
plt.show()

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

Определяне на най-добрия алгоритъм

Ако не сте запознати с моделите за машинно обучение за Python, ценен ресурс за преглед е Scikit-Learn. Ще започна да пускам тези набори от данни чрез няколко класификатора, регресия и алгоритми за клъстериране, за да намеря най-доброто представяне на прогнозата. Първо, нека използваме основна логистична регресия.

from sklearn.preprocessing import StandardScaler
from sklearn.decomposition import PCA
from sklearn.linear_model import LogisticRegression
from sklearn.pipeline import make_pipeline
pipe_lr = make_pipeline(StandardScaler(),
PCA(n_components=4),
LogisticRegression(random_state=1, solver='lbfgs'))
pipe_lr.fit(X_smote_train, y_smote_train)
y_smote_pred = pipe_lr.predict(X_smote_test)
print('Test accuracy: %.4f' % pipe_lr.score(X_smote_test, y_smote_test))

Точност на теста: 0.7140

Моделът на логистичната регресия върна само 71% точност на прогнозиране, не толкова впечатляващо, колкото бихме искали, но подозирахме това от формата на данните. За да тествам множество модели чрез повтарящ се конвейер, създавам конвейер и изпълнявам кръстосано валидиране на K-Folds, за да оценя точността на данните във всеки следващ модел. Следните модели бяха използвани за преминаване през набора от данни за обучение, оценка на точността, след това преминаване през набора от тестови данни и идентифициране на ефективността.

Можем да видим в този набор от данни Random Forest Classifier се представя по-добре от Regression, SVM и K-Nearest Neighbors.

Резултат и ROI

Моят обучен Random Forest модел върху набора от данни за инсулт SMOTE-NC може да предвиди инсулт на пациент 94% от времето със същите категориални променливи, по-конкретно възраст, средно ниво на глюкоза и ИТМ.

В този набор от данни имаше 249 пациенти с променлива отговор Stroke = True. Следователно този модел може точно да открие 236 пациенти от този набор от данни, които ще получат инсулт. В литературата се посочва, че средната цена на хоспитализациите за инсулт е 20 396 $ на пациент (Wang et. al. 2013). С този прост алгоритъм за машинно обучение, ранната намеса може да избегне разходите за хоспитализация, равняващи се на $4,8 милиона.

Сега това упражнение беше извършено с помощта на безплатни инструменти с отворен код, набори от данни и алгоритми, така че внедряването му в производствена среда е трудно. Въпреки това, не е необходимо да сте специалист по данни, за да провеждате машинно обучение, а само малко начален код и инструментите, които вече са налични.

Данните могат да бъдат генератор на приходи и могат да осигурят финансова възвръщаемост чрез прогнози за машинно обучение.

Данните са актив. Данните са твой приятел.