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

https://archive.ics.uci.edu/ml/datasets/Early+stage+diabetes+risk+prediction+dataset.

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

Как вы можете видеть, у нас есть всего 17 переменных со значениями двоичной записи для каждого поля, кроме «Возраст». Отсюда мы откроем набор данных в среде записной книжки, чтобы изучить его подробнее. Для этого проекта я использовал Google Colab, который основан на среде ноутбука Jupyter и не требует какой-либо настройки перед использованием.

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

from google.colab import files
uploaded = files.upload()

Оттуда мы загрузим некоторые необходимые библиотеки.

import pandas as pd 
import numpy as np
import matplotlib.pyplot as plt
from sklearn.preprocessing import OneHotEncoder
from sklearn.preprocessing import StandardScaler
from sklearn.compose import ColumnTransformer
from sklearn.pipeline import Pipeline
import sys
np.set_printoptions(threshold=sys.maxsize)

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

Dia_df = pd.read_csv('diabetes_data_upload.csv')
Dia_df.head()
Dia_df.info()

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

Прежде чем мы это сделаем, мы разделим наши данные на обучающий набор и тестовый набор. Таким образом, мы можем получить представление о том, как наша модель будет обобщать новые данные, которых она раньше не видела. Затем мы разделим обучающие экземпляры и пометим экземпляры. Поскольку «Класс» — это метка, которую мы пытаемся предсказать, мы создаем ряд только с этим значением в нем, называемым dia_labels.

from sklearn.model_selection import train_test_split
train_set, test_set = train_test_split(Dia_df, test_size = 0.2, random_state = 42)
dia_train = train_set.drop("class", axis=1) # drop labels for training set
dia_labels = train_set["class"].copy()

Как упоминалось ранее, нам нужно будет переформатировать данные в структуру, которую можно будет использовать в алгоритме машинного обучения. Для этого мы преобразуем DataFrame dia_train в массив NumPy. Для этого нам нужно преобразовать слова в числа. Мой подход состоял в том, чтобы преобразовать записи «Да» в 1, а записи «Нет» в 0. Я мог бы сделать это вручную, но я хотел построить конвейер из своей модели, чтобы он мог быть более динамичным в будущем. Я начал со следующего класса и конвейера.

from sklearn.preprocessing import LabelEncoder
class MultiColumnLabelEncoder:
    def __init__(self,columns = None):
        self.columns = columns # array of column names to encode
def fit(self,X,y=None):
        return self # not relevant here
def transform(self,X):
        '''
        Transforms columns of X specified in self.columns using
        LabelEncoder(). If no columns specified, transforms all
        columns in X.
        '''
        output = X.copy()
        if self.columns is not None:
            for col in self.columns:
                output[col] = LabelEncoder().fit_transform(output[col])
        else:
            for colname,col in output.iteritems():
                output[colname] = LabelEncoder().fit_transform(col)
        return output
def fit_transform(self,X,y=None):
        return self.fit(X,y).transform(X)
encoding_pipeline = Pipeline([
    ('encoding',MultiColumnLabelEncoder(columns=['Polyuria', 'Polydipsia', 'sudden weight loss', 'weakness', 'Polyphagia', 'Genital thrush', 'visual blurring', 'Itching', 'Irritability', 'delayed healing','partial paresis',  'muscle stiffness', 'Alopecia', 'Obesity']))
])

Этот код позволит нам преобразовать большинство значений наших столбцов после выполнения fit_transform(), но нам все еще нужно внести некоторые изменения в столбцы «Возраст» и «Пол». Вам может быть интересно, почему нам нужно вносить изменения в «Возраст», поскольку он уже заполнен числами. Причина в масштабе этих чисел по сравнению со всем остальным. Поскольку другие столбцы содержат только значения 1 и 0, модель будет придавать гораздо большее значение столбцу «Возраст». Чтобы справиться с этим, мы можем использовать встроенный преобразователь StandardScaler Sklearn, который встроен в sklearn.preprocessing.

Наконец, у нас есть столбец «Пол», который является категоричным. Мы наивно предполагаем, что «мужчина» и «женщина» имеют одинаковый вес, когда дело доходит до определения того, к какому «классу» они попадут. Мы можем использовать другой встроенный преобразователь под названием OneHotEncoder(). Эту функцию также можно найти в sklearn.preprocessing. OneHotEncoder создает матрицу с мужским и женским столбцами. Если кто-то мужчина, в столбце для мужчин будет 1, а для женщин — 0. Противоположное было бы верно для женщины.

Поскольку в столбце «Пол» есть только две разные категории, мы могли бы использовать обычную кодировку, поэтому мы могли бы сделать экземпляры «мужчины» 1 и экземпляры «женщины» 0, все в одном столбце. По сути, это будет просто использование одного значения для выполнения математических операций за кулисами. Поскольку теоретически в будущем может быть вариант «Другое» для столбца «Пол», я решил использовать OneHotEncoding.

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

Теперь мы объединим все эти методы в один конвейер для обработки всех наших столбцов.

enc_attribs = ['Polyuria', 'Polydipsia', 'sudden weight loss', 'weakness', 'Polyphagia', 'Genital thrush', 'visual blurring', 'Itching', 'Irritability', 'delayed healing', 'partial paresis', 'muscle stiffness', 'Alopecia', 'Obesity']
num_attribs = ['Age']
cat_attribs = list(train_set[["Gender"]])
full_pipeline = ColumnTransformer([                           
        ("num", StandardScaler(), num_attribs),
        ("cat", OneHotEncoder(), cat_attribs),
        ("encoding", encoding_pipeline, enc_attribs)
    ], remainder='passthrough')
dia_prepared = full_pipeline.fit_transform(dia_train)

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

dia_prepared

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

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

from sklearn.svm import SVC
clf = SVC()
clf.fit(dia_prepared, dia_labels)

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

from sklearn.metrics import accuracy_score
diabetes_predictions = clf.predict(dia_prepared)

accuracy_score(dia_labels, diabetes_predictions)

from sklearn.metrics import confusion_matrix
cm = confusion_matrix(dia_labels, diabetes_predictions)
print(cm)
fig = plt.figure()
ax = fig.add_subplot(111)
cax = ax.matshow(cm)
plt.title('Confusion Matrix')
fig.colorbar(cax)
plt.xlabel('Predicted')
plt.ylabel('Actual')
plt.show()

Мы получили точность 97,6% с 4 ложноотрицательными и 6 ложноположительными результатами.

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

Сначала мы удалим метку из тестового набора так же, как мы удалили ее из обучающего набора.

X_test = test_set.drop("class", axis=1) # drop labels for testing set
y_test = test_set["class"].copy()

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

full_pipeline = ColumnTransformer([                           
        ("num", StandardScaler(), num_attribs),
        ("cat", OneHotEncoder(), cat_attribs),
        ("encoding", encoding_pipeline, enc_attribs)
    ], remainder='passthrough')
X_test_prepared = full_pipeline.fit_transform(X_test)

X_test_prepared

Теперь мы можем делать прогнозы, вызывая прогноз в нашей модели «clf» и передавая «X_test_prepared».

test_predictions = clf.predict(X_test_prepared)
accuracy_score(y_test, test_predictions)
cm = confusion_matrix(y_test, test_predictions)
print(cm)
fig = plt.figure()
ax = fig.add_subplot(111)
cax = ax.matshow(cm)
plt.title('Confusion Matrix')
fig.colorbar(cax)
plt.xlabel('Predicted')
plt.ylabel('Actual')
plt.show()

Наша модель работает с точностью 96,2% с 1 ложноотрицательным и 3 ложноположительными результатами.

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

sample_data = dia_train.iloc[5:10]
full_pipeline_with_predictor = Pipeline([
        ("preparation", full_pipeline),
        ("class", SVC())
    ])
full_pipeline_with_predictor.fit(dia_train, dia_labels)
full_pipeline_with_predictor.predict(sample_data)

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

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

diabetes_model = full_pipeline_with_predictor
import joblib
joblib.dump(diabetes_model, "diabetes_model.pkl")

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

diabetes_model_loaded = joblib.load("diabetes_model.pkl")

diabetes_model_loaded.predict(sample_data)

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

https://github.com/jjevans25/UCI-Diabetes-Data/blob/master/Diabetes_Model.ipynb

— — — — — — — — — — — — — — — — — — — — — — — — — —

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