Абстрактный

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

Контекст

Снижение детской смертности отражено в нескольких Целях устойчивого развития Организации Объединенных Наций и является ключевым показателем человеческого прогресса. ООН ожидает, что к 2030 году страны положат конец предотвратимой смертности новорожденных и детей в возрасте до 5 лет, при этом все страны будут стремиться снизить смертность в возрасте до 5 лет как минимум до 25 случаев на 1000 живорождений.

Параллельно понятию детской смертности, конечно же, существует материнская смертность, на долю которой приходится 295 000 смертей во время и после беременности и родов (по состоянию на 2017 год). Подавляющее большинство этих смертей (94%) произошло в условиях ограниченных ресурсов, и большинство из них можно было бы предотвратить.

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

Данные

Этот набор данных содержит 2126 записей характеристик, извлеченных из исследований кардиотокограммы, которые затем были разделены тремя экспертами-акушерами на 3 класса:

Нормальный, подозрительный и патологический

Цель

Создайте мультиклассовую модель для классификации характеристик КТГ по трем состояниям здоровья плода.

Ссылка на набор данных

https://www.kaggle.com/andrewmvd/fetal-health-classification

Импорт необходимых библиотек

import matplotlib.pyplot as plt
import seaborn as sns
from sklearn.model_selection import train_test_split
from sklearn import metrics
from sklearn.linear_model import LogisticRegression
from sklearn.preprocessing import StandardScaler
from imblearn.over_sampling import SMOTE
from sklearn.neighbors import KNeighborsClassifier
from sklearn.naive_bayes import GaussianNB
from sklearn.tree import DecisionTreeClassifier
from sklearn import tree
from sklearn.ensemble import RandomForestClassifier,VotingClassifier,GradientBoostingClassifier,AdaBoostClassifier
from sklearn.svm import SVC
from mlxtend.classifier import StackingClassifier
from sklearn import model_selection
from xgboost.sklearn import XGBClassifier
from sklearn.model_selection import GridSearchCV
sns.set(color_codes=True) # adds a nice background to the graphs
%matplotlib inline

Чтение набора данных

df = pd.read_csv('../input/fetal-health-classification/fetal_health.csv')
df.head()
df.shape
(2126, 22)

В наборе данных 2126 строк и 22 объекта.

Информация о данных

df.info()
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 2126 entries, 0 to 2125
Data columns (total 22 columns):
 #   Column                                                  Non-Null Count  Dtype  
---  ------                                                  --------------  -----  
 0   baseline value                                          2126 non-null   float64
 1   accelerations                                           2126 non-null   float64
 2   fetal_movement                                          2126 non-null   float64
 3   uterine_contractions                                    2126 non-null   float64
 4   light_decelerations                                     2126 non-null   float64
 5   severe_decelerations                                    2126 non-null   float64
 6   prolongued_decelerations                                2126 non-null   float64
 7   abnormal_short_term_variability                         2126 non-null   float64
 8   mean_value_of_short_term_variability                    2126 non-null   float64
 9   percentage_of_time_with_abnormal_long_term_variability  2126 non-null   float64
 10  mean_value_of_long_term_variability                     2126 non-null   float64
 11  histogram_width                                         2126 non-null   float64
 12  histogram_min                                           2126 non-null   float64
 13  histogram_max                                           2126 non-null   float64
 14  histogram_number_of_peaks                               2126 non-null   float64
 15  histogram_number_of_zeroes                              2126 non-null   float64
 16  histogram_mode                                          2126 non-null   float64
 17  histogram_mean                                          2126 non-null   float64
 18  histogram_median                                        2126 non-null   float64
 19  histogram_variance                                      2126 non-null   float64
 20  histogram_tendency                                      2126 non-null   float64
 21  fetal_health                                            2126 non-null   float64
dtypes: float64(22)

Проверка нулевых значений

df.isnull().sum()
baseline value                                            0
accelerations                                             0
fetal_movement                                            0
uterine_contractions                                      0
light_decelerations                                       0
severe_decelerations                                      0
prolongued_decelerations                                  0
abnormal_short_term_variability                           0
mean_value_of_short_term_variability                      0
percentage_of_time_with_abnormal_long_term_variability    0
mean_value_of_long_term_variability                       0
histogram_width                                           0
histogram_min                                             0
histogram_max                                             0
histogram_number_of_peaks                                 0
histogram_number_of_zeroes                                0
histogram_mode                                            0
histogram_mean                                            0
histogram_median                                          0
histogram_variance                                        0
histogram_tendency                                        0
fetal_health                                              0
dtype: int64

В наборе данных нет нулевых значений.

Парный сюжет

Одномерный анализ

Проверка асимметрии

df.skew()
baseline value                                             0.020312
accelerations                                              1.204392
fetal_movement                                             7.811477
uterine_contractions                                       0.159315
light_decelerations                                        1.718437
severe_decelerations                                      17.353457
prolongued_decelerations                                   4.323965
abnormal_short_term_variability                           -0.011829
mean_value_of_short_term_variability                       1.657339
percentage_of_time_with_abnormal_long_term_variability     2.195075
mean_value_of_long_term_variability                        1.331998
histogram_width                                            0.314235
histogram_min                                              0.115784
histogram_max                                              0.577862
histogram_number_of_peaks                                  0.892886
histogram_number_of_zeroes                                 3.920287
histogram_mode                                            -0.995178
histogram_mean                                            -0.651019
histogram_median                                          -0.478414
histogram_variance                                         3.219974
histogram_tendency                                        -0.311632
fetal_health                                               1.849934
dtype: float64

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

df[df.duplicated()]

В наборе данных есть дубликаты

df_dup = df.drop_duplicates(subset = None , keep = 'first', inplace = False)

После удаления дубликатов у нас есть 2113 строк и 22 функции.

График корреляции

Target = df["fetal_health"]
corr = df.corr()
mask = np.zeros_like(corr)
mask[np.triu_indices_from(mask)] = True
with sns.axes_style("white"):
    f, ax = plt.subplots(figsize=(15, 15))
    ax = sns.heatmap(corr,mask=mask,square=True,linewidths=2.5,cmap="viridis",annot=True)

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

Счетчик цели

sns.countplot(Target)
plt.show()

print("Count of type 1.0 fetal health in the dataset ",len(df.loc[df["fetal_health"]==1.0]))
print("Count of type 2.0 fetal health in the dataset ",len(df.loc[df["fetal_health"]==2.0]))
print("Count of type 3.0 fetal health in the dataset ",len(df.loc[df["fetal_health"]==3.0]))

Подсчет здоровья плода типа 1.0 в наборе данных 1655
Подсчет здоровья плода типа 2.0 в наборе данных 295
Подсчет здоровья плода типа 3.0 в наборе данных 176

Разделение набора данных между независимыми объектами и зависимыми объектами

X = df_dup.iloc[:,:-1]
y = df_dup.iloc[:,-1]

Масштабирование набора данных

scale = StandardScaler()
X = scale.fit_transform(X)
X = pd.DataFrame(X,columns=df_dup.iloc[:,:-1].columns)

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

from imblearn.over_sampling import RandomOverSampler
ROS = RandomOverSampler(random_state=42)
X_ros, y_ros = ROS.fit_resample(X,y)
from collections import Counter
print('Resampled dataset shape %s' % Counter(y_ros))

Счетчик формы набора данных с повторной выборкой ({2.0: 1646, 1.0: 1646, 3.0: 1646})

import statsmodels.api as sm
X = sm.add_constant(X)
X_train, X_test, y_train, y_test = train_test_split(X, y, random_state = 10, test_size = 0.2)

print('X_train', X_train.shape)
print('y_train', y_train.shape)

print('X_test', X_test.shape)
print('y_test', y_test.shape)

X_train (1690, 22)
y_train (1690,)
X_test (423, 22)
y_test (423,)

print("{0:0.2f}% data is in training set".format((len(X_train)/len(df.index)) * 100))
print("{0:0.2f}% data is in test set".format((len(X_test)/len(y.index)) * 100))

79,49 % данных находятся в обучающем наборе
20,02 % данных находятся в тестовом наборе

Функция для создания отчета о поезде

def get_train_report(model):
    
    train_pred = model.predict(X_train)
    return(classification_report(y_train, train_pred))

Функция для создания отчета о тестировании

def get_test_report(model):
    test_pred = model.predict(X_test)
    return(classification_report(y_test, test_pred))

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

decision_tree_classification = DecisionTreeClassifier(criterion = 'entropy', random_state = 10)
decision_tree = decision_tree_classification.fit(X_train, y_train)
from sklearn.metrics import classification_report
train_report = get_train_report(decision_tree)
print(train_report)
precision    recall  f1-score   support

         1.0       1.00      1.00      1.00      1320
         2.0       1.00      1.00      1.00       237
         3.0       1.00      1.00      1.00       133

    accuracy                           1.00      1690
   macro avg       1.00      1.00      1.00      1690
weighted avg       1.00      1.00      1.00      1690
test_report = get_test_report(decision_tree)
print(test_report)
precision    recall  f1-score   support

         1.0       0.95      0.97      0.96       326
         2.0       0.78      0.76      0.77        55
         3.0       0.95      0.83      0.89        42

    accuracy                           0.93       423
   macro avg       0.89      0.86      0.87       423
weighted avg       0.93      0.93      0.93       423

Настройка гиперпараметров в классификаторе дерева решений

dt_model = DecisionTreeClassifier(criterion = 'gini',
                                  max_depth = 5,
                                  min_samples_split = 4,
                                  max_leaf_nodes = 6,
                                  random_state = 10)

# fit the model using fit() on train data
decision_tree = dt_model.fit(X_train, y_train)
train_report = get_train_report(decision_tree)

# print the performance measures
print('Train data:\n', train_report)
test_report = get_test_report(decision_tree)

# print the performance measures
print('Test data:\n', test_report)
Train data:
               precision    recall  f1-score   support

         1.0       0.93      0.97      0.95      1320
         2.0       0.82      0.62      0.71       237
         3.0       0.90      0.92      0.91       133

    accuracy                           0.92      1690
   macro avg       0.88      0.84      0.85      1690
weighted avg       0.91      0.92      0.91      1690

Test data:
               precision    recall  f1-score   support

         1.0       0.92      0.95      0.94       326
         2.0       0.76      0.62      0.68        55
         3.0       0.87      0.81      0.84        42

    accuracy                           0.90       423
   macro avg       0.85      0.79      0.82       423
weighted avg       0.89      0.90      0.89       423

Случайный лесной классификатор

rf_classification = RandomForestClassifier(n_estimators = 10, random_state = 10)

# use fit() to fit the model on the train set
rf_model = rf_classification.fit(X_train, y_train)
train_report = get_train_report(rf_model)
print(train_report)
precision    recall  f1-score   support

         1.0       0.99      1.00      1.00      1320
         2.0       0.99      0.97      0.98       237
         3.0       1.00      0.98      0.99       133

    accuracy                           0.99      1690
   macro avg       1.00      0.98      0.99      1690
weighted avg       0.99      0.99      0.99      1690

test_report = get_test_report(rf_model)
print(test_report)
precision    recall  f1-score   support

         1.0       0.93      0.98      0.96       326
         2.0       0.82      0.67      0.74        55
         3.0       0.92      0.79      0.85        42

    accuracy                           0.92       423
   macro avg       0.89      0.81      0.85       423
weighted avg       0.92      0.92      0.92       423

Особенности сюжета важности

important_features = pd.DataFrame({'Features': X_train.columns, 
                                   'Importance': rf_model.feature_importances_})

# sort the dataframe in the descending order according to the feature importance
important_features = important_features.sort_values('Importance', ascending = False)

# create a barplot to visualize the features based on their importance
sns.barplot(x = 'Importance', y = 'Features', data = important_features)

# add plot and axes labels
# set text size using 'fontsize'
plt.title('Feature Importance', fontsize = 15)
plt.xlabel('Importance', fontsize = 15)
plt.ylabel('Features', fontsize = 15)

# display the plot
plt.show()

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

K ближайших соседей

from sklearn.metrics import confusion_matrix,roc_curve
knn_classification = KNeighborsClassifier(n_neighbors = 3)

# fit the model using fit() on train data
knn_model = knn_classification.fit(X_train, y_train)
train_report = get_train_report(knn_model)
print(train_report)
precision    recall  f1-score   support

         1.0       0.96      0.99      0.98      1320
         2.0       0.89      0.79      0.84       237
         3.0       0.94      0.88      0.91       133

    accuracy                           0.95      1690
   macro avg       0.93      0.89      0.91      1690
weighted avg       0.95      0.95      0.95      1690
test_report = get_test_report(knn_model)
print(test_report)
precision    recall  f1-score   support

         1.0       0.94      0.96      0.95       326
         2.0       0.70      0.67      0.69        55
         3.0       0.89      0.74      0.81        42

    accuracy                           0.90       423
   macro avg       0.84      0.79      0.81       423
weighted avg       0.90      0.90      0.90       423

Настройка гиперпараметров на классификаторе KNN

tuned_paramaters = {'n_neighbors': np.arange(1, 25, 2),
                   'metric': ['hamming','euclidean','manhattan','Chebyshev']}
 
# instantiate the 'KNeighborsClassifier' 
knn_classification = KNeighborsClassifier()

knn_grid = GridSearchCV(estimator = knn_classification, 
                        param_grid = tuned_paramaters, 
                        cv = 5, 
                        scoring = 'accuracy')

# fit the model on X_train and y_train using fit()
knn_grid.fit(X_train, y_train)

# get the best parameters
print('Best parameters for KNN Classifier: ', knn_grid.best_params_, '\n')

Лучшие параметры для классификатора KNN: {'метрика': 'manhattan', 'n_neighbors': 7}

from sklearn.model_selection import cross_val_score
error_rate = []

# use for loop to build a knn model for each K
for i in np.arange(1,25,2):
    
    # setup a knn classifier with k neighbors
    # use the 'euclidean' metric 
    knn = KNeighborsClassifier(i, metric = 'euclidean')
   
    # fit the model using 'cross_val_score'
    # pass the knn model as 'estimator'
    # use 5-fold cross validation
    score = cross_val_score(knn, X_train, y_train, cv = 5)
    
    # calculate the mean score
    score = score.mean()
    
    # compute error rate 
    error_rate.append(1 - score)

# plot the error_rate for different values of K 
plt.plot(range(1,25,2), error_rate)

# add plot and axes labels
# set text size using 'fontsize'
plt.title('Error Rate', fontsize = 15)
plt.xlabel('K', fontsize = 15)
plt.ylabel('Error Rate', fontsize = 15)
# set the x-axis labels
plt.xticks(np.arange(1, 25, step = 2))

# plot a vertical line across the minimum error rate
plt.axvline(x = 7, color = 'red')

# display the plot
plt.show()

Мы видим, что оптимальное значение K = 7, полученное из GridSearchCV, приводит к самой низкой частоте ошибок.

train_report = get_train_report(knn_grid)
print(train_report)
precision    recall  f1-score   support

         1.0       0.95      0.98      0.96      1320
         2.0       0.84      0.72      0.77       237
         3.0       0.98      0.83      0.90       133

    accuracy                           0.93      1690
   macro avg       0.92      0.84      0.88      1690
weighted avg       0.93      0.93      0.93      1690
test_report = get_test_report(knn_grid)
print(test_report)
precision    recall  f1-score   support

         1.0       0.91      0.97      0.94       326
         2.0       0.69      0.62      0.65        55
         3.0       0.86      0.60      0.70        42

    accuracy                           0.88       423
   macro avg       0.82      0.73      0.77       423
weighted avg       0.88      0.88      0.88       423

Наивный байесовский классификатор Гаусса

gnb = GaussianNB()
# fit the model using fit() on train data
gnb_model = gnb.fit(X_train, y_train)
test_report = get_test_report(gnb_model)
print(test_report)
precision    recall  f1-score   support
         1.0       0.98      0.86      0.91       326
         2.0       0.48      0.87      0.62        55
         3.0       0.78      0.69      0.73        42
    accuracy                           0.84       423
   macro avg       0.75      0.81      0.76       423
weighted avg       0.89      0.84      0.86       423

Классификатор Adaboost

ada_model = AdaBoostClassifier(n_estimators = 40, random_state = 10)
ada_model.fit(X_train, y_train)
ada_model = AdaBoostClassifier(n_estimators = 40, random_state = 10)
ada_model.fit(X_train, y_train)
AdaBoostClassifier(n_estimators=40, random_state=10)
test_report = get_test_report(ada_model)
print(test_report)
precision    recall  f1-score   support

         1.0       0.92      0.94      0.93       326
         2.0       0.66      0.69      0.67        55
         3.0       0.94      0.74      0.83        42

    accuracy                           0.89       423
   macro avg       0.84      0.79      0.81       423
weighted avg       0.89      0.89      0.89       423
    accuracy                           0.89       423
   macro avg       0.84      0.79      0.81       423
weighted avg       0.89      0.89      0.89       423
ada_model = AdaBoostClassifier(n_estimators = 40, random_state = 10)
ada_model.fit(X_train, y_train)
AdaBoostClassifier(n_estimators=40, random_state=10)
test_report = get_test_report(ada_model)
print(test_report)
precision    recall  f1-score   support

         1.0       0.92      0.94      0.93       326
         2.0       0.66      0.69      0.67        55
         3.0       0.94      0.74      0.83        42

    accuracy                           0.89       423
   macro avg       0.84      0.79      0.81       423
weighted avg       0.89      0.89      0.89       423

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

gboost_model = GradientBoostingClassifier(n_estimators = 150, max_depth = 10, random_state = 10)
gboost_model.fit(X_train, y_train)
GradientBoostingClassifier(max_depth=10, n_estimators=150, random_state=10)
test_report = get_test_report(gboost_model)
print(test_report)
precision    recall  f1-score   support

         1.0       0.95      0.98      0.97       326
         2.0       0.87      0.82      0.84        55
         3.0       0.95      0.86      0.90        42

    accuracy                           0.94       423
   macro avg       0.92      0.88      0.90       423
weighted avg       0.94      0.94      0.94       423

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

xgb_model = XGBClassifier(max_depth = 10, gamma = 1)
xgb_model.fit(X_train, y_train)
XGBClassifier(base_score=0.5, booster='gbtree', colsample_bylevel=1,
              colsample_bynode=1, colsample_bytree=1, gamma=1, gpu_id=-1,
              importance_type='gain', interaction_constraints='',
              learning_rate=0.300000012, max_delta_step=0, max_depth=10,
              min_child_weight=1, missing=nan, monotone_constraints='()',
              n_estimators=100, n_jobs=4, num_parallel_tree=1,
              objective='multi:softprob', random_state=0, reg_alpha=0,
              reg_lambda=1, scale_pos_weight=None, subsample=1,
              tree_method='exact', validate_parameters=1, verbosity=None)
test_report = get_test_report(xgb_model)
print(test_report)
precision    recall  f1-score   support

         1.0       0.95      0.98      0.96       326
         2.0       0.88      0.82      0.85        55
         3.0       0.94      0.81      0.87        42

    accuracy                           0.94       423
   macro avg       0.93      0.87      0.89       423
weighted avg       0.94      0.94      0.94       423

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

svc_model = SVC(kernel='poly',probability=True)
svc_model.fit(X_train,y_train)
SVC(kernel='poly', probability=True)
test_report = get_test_report(svc_model)
print(test_report)
precision    recall  f1-score   support

         1.0       0.92      0.97      0.95       326
         2.0       0.69      0.60      0.64        55
         3.0       0.90      0.67      0.77        42

    accuracy                           0.89       423
   macro avg       0.84      0.75      0.78       423
weighted avg       0.89      0.89      0.89       423

Классификатор голосования

clf1 = KNeighborsClassifier(n_neighbors = 7 , weights = 'distance', metric='manhattan' )
clf2 = GradientBoostingClassifier(n_estimators = 150,max_depth = 10,random_state=1)
votingclf = VotingClassifier(estimators=[('knn',clf1),('grb', clf2)],voting='hard')
votingclf = votingclf.fit(X_train,y_train)
test_report = get_test_report(votingclf)
print(test_report)
precision    recall  f1-score   support
         1.0       0.92      0.98      0.95       326
         2.0       0.82      0.65      0.73        55
         3.0       0.97      0.67      0.79        42
    accuracy                           0.91       423
   macro avg       0.90      0.77      0.82       423
weighted avg       0.91      0.91      0.90       423

Результат

Мы пробовали разные алгоритмы для этого набора данных, среди которых алгоритмы на основе повышения, т. е. алгоритмы повышения XG Boost и Gradient работают лучше всего для этого набора данных с точностью 94% в тестовом наборе данных, а показатель f1 для XGBoost составляет 0,96, 0,85 и 0,87 соответственно для три класса, за которыми следует классификатор дерева решений без его гипернастройки с точностью 93% на тестовых данных.

Надеюсь, вам понравился анализ!

Вы можете подписаться на меня в Linkedin, Github и Kaggle.

Ссылка на Github для этого проекта

https://github.com/ratul442/Fetal-Health-Classification-Prediction

Каггл Ссылка

https://www.kaggle.com/ratul6/fetal-health-classification-prediction-94-xgboost

LinkedIn Ссылка

https://www.linkedin.com/in/ratul-ghosh-8048a8148/