Понимание машинного обучения в здравоохранении

Машинное обучение широко применяется в здравоохранении, например, в медицинской диагностике [1]. Одним из часто используемых наборов данных для исследований рака является набор данных по диагностике рака молочной железы штата Висконсин (WBCD) [2]. Как и в других областях, модели машинного обучения, используемые в здравоохранении, по-прежнему остаются черными ящиками. Как описано в [3], понимание причин, лежащих в основе прогнозов модели машинного обучения, очень важно для определения доверия, если врач планирует принять меры по лечению рака на основе прогноза диагноза. Такое понимание также может помочь врачам, специализирующимся в данной области, находить ошибки в прогнозах моделей машинного обучения. Этого можно добиться разными способами. Один из методов называется «объяснимое машинное обучение» [4].

В этой статье я использую набор данных WBCD [2], чтобы продемонстрировать, как реализовать объяснимое машинное обучение, чтобы сделать ненадежный прогноз достоверным.

1. Объяснимое машинное обучение

Как описано в [4], объяснимое машинное обучение в этой статье относится к апостериорному анализу и методам, которые используются для понимания предсказаний предварительно обученной модели. Существуют различные апостериорные методы, такие как генерация кода причины, локальная и глобальная визуализация прогнозов модели и т. Д. [4]. В этой статье я использую генерацию кода причины как апостериорный метод. В частности, Local Interpretable Model-Agnostic Explanations (LIME) [3] используется для объяснения того, какие функции используются моделью машинного обучения для принятия решения о прогнозировании.

2. Подготовка набора данных

Как описано ранее, набор данных, используемый в этой статье, является общедоступным набором данных WBCD [2]. В этом наборе данных для каждого образца были предоставлены следующие 9 функций, которые будут использоваться в качестве входных данных для модели машинного обучения:

  • Толщина комка
  • Однородность размера ячеек
  • Однородность формы ячейки
  • Маргинальная адгезия
  • Размер одной эпителиальной клетки
  • Голые ядра
  • Блочный хроматин
  • Нормальные ядрышки
  • Митозы

2.1 Загрузка данных

Предполагая, что набор данных WBCD уже был загружен на локальный компьютер в виде файла csv груди-рака-wisconsin.csv, файл набора данных можно затем загрузить в фрейм данных Pandas следующее:

feature_names = ["CT","UCSize","UCShape","MA","SECSize","BN","BC","NN","Mitoses"]
columns = ["ID"]
columns.extend(feature_names)
columns.extend(["Diagnosis"])
raw_data = pd.read_csv('breast-cancer-wisconsin.csv', na_values='?', header=None, index_col=['ID'], names = columns)
raw_data = raw_data.reset_index(drop=True)
raw_data.head(10)

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

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

  • Отсутствующие данные

Следующая команда проверяет, для каких функций отсутствуют данные:

data = raw_data.copy()
data.isnull().sum()

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

  • Маленький размер набора данных

Набор данных WBCD [2] включает всего 699 образцов, что слишком мало для машинного обучения. Эту проблему небольшого размера данных можно решить путем увеличения данных (подробности см. В разделе Несбалансированный набор данных).

  • Разные шкалы значений характеристик

Диапазоны значений различных функций различаются. Класс Scale ниже предназначен для преобразования значений функций в тот же диапазон [0, 1]. Этот класс разработан таким образом, что его можно использовать с другими классами для формирования конвейера позже.

from sklearn.preprocessing import MinMaxScaler
from sklearn.base import BaseEstimator, TransformerMixin
minMaxScaler = MinMaxScaler()
minMaxScaler.fit(features[feature_names])
class Scale(BaseEstimator, TransformerMixin):
    def __init__(self):
        pass
    def fit(self, X, y=None):
        return self
    
    def transform(self, X, y=None):  
        if type(X) == pd.Series or type(X) == pd.DataFrame:
            X1 = X.values 
            X1 = X1.reshape(1, -1)
        else:
            X1 = X.copy()
         
        X1 = minMaxScaler.transform(X1) 
            
        return X1
  • Несбалансированный набор данных

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

data['Diagnosis'].value_counts()

Количество выборок данных с разными метками не сбалансировано. Имеется 458 образцов данных, помеченных как 2 (т. Е. B (доброкачественный)), но есть только 241 образец данных, помеченных как 4 (т. Е. M (злокачественный)).

Чтобы сбалансировать набор данных, для простоты копия 241 выборки данных, помеченная как M, добавляется в исходный набор данных.

  • Перекошенный набор данных

Следующий код можно использовать для визуализации распределения значений функций:

data[numerical].hist(bins=20, figsize=(15, 10))

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

class Sqrt(BaseEstimator, TransformerMixin):
    def __init__(self):
        pass
    def fit(self, X, y=None):
        return self
    
    def transform(self, X, y=None):  
        if type(X) == pd.Series or type(X) == pd.DataFrame:
            X1 = X.copy()
            
            if X1.isnull().values.any():
                X1 = X1.fillna(0)
                
            X1 = X1.abs() # avoid negative values
        else:
            X1 = X.copy()
            X1 = np.absolute(X1) # avoid negative values
        
        X1 = np.sqrt(X1) 
            
        return X1

2.3 Отделение меток от функций

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

labels   = data['Diagnosis']
features = data.drop(['Diagnosis'], axis = 1)

2.4 Разделение набора данных на наборы данных для обучения и тестирования

Функции и метки случайным образом разделены на две части: 75% для обучения модели и 25% для тестирования модели:

from sklearn.model_selection import train_test_split
X_train, X_test, y_train, y_test = train_test_split(features, labels, test_size=0.25, random_state=42)

3. Модель машинного обучения.

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

from sklearn.ensemble import RandomForestClassifier
rfc = RandomForestClassifier(n_estimators=100)
rfc.fit(X_train, y_train)
score = rfc.score(X_test, y_test)

Обученная модель достигла точности 98,23%.

4. Метод LIME для объяснения прогнозов.

Как описано в [3], определение доверия к индивидуальным прогнозам важно при принятии решений с помощью моделей машинного обучения. Например, когда машинное обучение используется для медицинской диагностики, такой как диагностика рака груди в Висконсине [1], прогнозы нельзя просто использовать для действий, поскольку последствия могут быть катастрофическими. В этом разделе показано, как использовать LIME [3] для объяснения индивидуальных прогнозов в качестве решения для определения доверия.

4.1 Создание конвейера

Ключевым моментом генерации кода причины в LIME является связывание результата прогнозирования модели с исходными непреобразованными значениями признаков. С этой целью метод LIME использует конвейер машинного обучения (включая конвейер предварительной обработки данных в начале и модель машинного обучения в конце конвейера) в качестве входных данных для объяснения результатов прогнозирования модели. Для этой цели предназначен следующий конвейер:

from sklearn.pipeline import make_pipeline
scale = Scale()
sqrt  = Sqrt()
# rfc is a pre-trained Random Forest model
machine_learning_pipeline = make_pipeline(scale, sqrt, rfc)

4.2 Выбор LIME Explainer

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

from lime.lime_tabular import LimeTabularExplainer
class_names = ['Benign', 'Malignant']
explainer = LimeTabularExplainer(feature_names=feature_names, 
                                 class_names=class_names, 
                                 training_data=X_train.values)

4.3 Объяснение предсказания модели

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

def explain(feature_vector, label=1): # feature_vector - a Pandas Series of features
    exp = explainer.explain_instance(feature_vector, machine_learning_pipeline.predict_proba, num_features=9)
    
    fig = plot(exp, label)
    exp.show_in_notebook(show_table=True, show_all=False)

Метод show_in_notebook () объяснителя LIME показывает связи между прогнозом и соответствующими исходными значениями функций в формате записной книжки.

Функция plot ниже предназначена для создания гистограммы для отображения результатов объяснения модели LIME:

def plot(exp, label=1):
        exp_list = exp.as_list()
        fig = plt.figure()
        vals = [x[1] for x in exp_list]
        names = [x[0] for x in exp_list]
        vals.reverse()
        names.reverse()
        colors = ['green' if x <= 0 else 'red' for x in vals]
        pos = np.arange(len(exp_list)) + .5
        plt.barh(pos, vals, align='center', color=colors)
        plt.yticks(pos, names)
        if exp.mode == "classification":
            title = 'Local explanation for class {}'.format(exp.class_names[label])
        else:
            title = 'Local explanation'
        plt.title(title)
        
        return fig

Пример 1. Объяснение прогноза злокачественности

Ниже приведен образец (вектор признаков) M (злокачественный):

sample_M = raw_data.loc[5, feature_names]

Результат прогнозирования этого вектора признаков можно объяснить с помощью LIME следующим образом:

explain(m_feature_vector, machine_learning_pipeline, label=1)

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

Пример 2. Объяснение прогноза доброкачественности

Ниже приведен образец вектора признаков B (доброкачественный):

sample_B = raw_data.loc[1, feature_names]

Результат прогнозирования этого вектора признаков может быть объяснен с помощью LIME следующим образом:

explain(sample_B, machine_learning_pipeline, label=0)

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

5. Вывод

В этой статье я использовал метод LIME [3] и набор данных WBCD [2], чтобы продемонстрировать, как объяснить результаты прогнозирования модели машинного обучения в диагностике рака груди.

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

Блокнот Jupyter со всем исходным кодом из этой статьи доступен на Github [6].

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

[1] Набор данных диагностики рака груди, штат Висконсин

[2] Набор данных и описание WBCD

[3] М. Т. Рибейро, С. Сингх и К. Гестрин, Почему я должен вам доверять?
Объясняя предсказания любого классификатора »

[4] П. Холл и Н. Гилл, Введение в интерпретируемость машинного обучения, прикладная перспектива справедливости, подотчетности, прозрачности и объяснимого ИИ, второе издание, O’Reilly Media, Inc., 2019

[5] М. Т. Рибейро, С. Сингх и К. Гестрин, Локальные интерпретируемые модельно-агностические объяснения (LIME): Введение

[6] Юй Хуанг, блокнот Jupyter в Github

ЗАЯВЛЕНИЕ О РАСКРЫТИИ ИНФОРМАЦИИ: © 2020. Мнения, выраженные в этой статье, принадлежат автору и не обязательно отражают точку зрения Аргоннской национальной лаборатории.