Руководство по прогнозированию диагноза инсульта с помощью нейронной сети на основе встраивания категориальных признаков.
По данным Всемирной организации здравоохранения, инсульт занимает второе место среди причин смерти в мире, на него приходится примерно 11% всех смертей.
Прогнозирование того, будет ли у пациента инсульт, на основе таких параметров, как пол, возраст, различные заболевания и статус курения, является ключом к снижению общего числа смертей, вызванных инсультом.
Традиционные парадигмы обучения, использующие древовидные алгоритмы, часто принимают в качестве входных данных закодированные категориальные признаки наряду с числовыми признаками для прогнозирования целевой переменной. Выбор идеального метода для кодирования категориальных переменных становится проблематичным при работе с категориями высокой кардинальности. В первую очередь это связано с тем, что выбор определенных методов в такой ситуации вызовет проблемы с моделями со взрывоопасными пространствами признаков и, следовательно, будет склонен к переоснащению.
Идея использования вложений популярна в настройках обработки естественного языка. BERT, являющийся одним из ярких примеров, использовал предварительную подготовку глубоких двунаправленных представлений из текста с использованием преобразователя. В результате нижестоящие задачи, использующие это изученное представление, часто демонстрируют более высокую производительность после тонкой настройки, чем их аналоги, обучающиеся на необработанных данных или с нуля.
Текст, будучи многомерным, часто преобразуется в низкоразмерное пространство встраивания с помощью слоя встраивания до подачи в нейронную сеть.
В машинном обучении существуют разнообразные методы кодирования категориальных переменных. Некоторые из этих методов лучше всего подходят для некоторых задач. Но большинство методов кодирования признаков требуют бесчисленных проб и ошибок. В конечном счете, лучший метод помогает улучшить сигнал предсказания и лучше всего зафиксировать распределение признаков по данным обучения/тестирования.
В этой статье мы обсудим технику представления категориальных переменных табличной задачи в пространстве вложения для обучения нейронной сети.
Содержание
- Быстрый исследовательский анализ данных
- Конвейер подготовки данных
- Реализация модели
- Ограничения
1) Быстрый исследовательский анализ данных
Давайте быстро взглянем на данные.
display(df.head())
Распределение количества для целевой переменной
df.stroke.value_counts().plot.bar()
Проверьте, есть ли у нас пропущенные значения
df.isna().sum()
Индекс массы тела (ИМТ), по-видимому, имеет несколько отсутствующих значений. Поскольку это также числовая переменная, мы будем использовать простое усреднение, чтобы вменить ее пропущенные значения.
# Here we use simple mean value to impute missing values in BMI df['bmi'] = df['bmi'].fillna(df['bmi'].mean())
2) Конвейер подготовки данных
Цель этой задачи — подготовить и отформатировать данные для подачи в нейронную сеть. Во-первых, мы укажем размеры пространства вложения для всех категориальных переменных. Мы устанавливаем размер встроенной функции равным половине числа уникальных категорий.
# extract categorical features categorical_features = df.columns[df.dtypes == 'object'].tolist() # extract numerical features numerical_features = df.columns.difference(categorical_features + ['id', 'stroke']).tolist() embedding_dims = { k : (df[k].nunique(), df[k].nunique() // 2) for k in categorical_features } print( embedding_dims )
Затем мы будем обрабатывать числовые и категориальные переменные параллельно, используя приведенный ниже код.
# Convert data into a multi-input list format to match # the model architecture def feature_transformer(train, test): input_list_train = [] input_list_test = [] # Extract all columns(categorical) to be fed into an # embedding layer for c in categorical_features: # all unique categories in column `c` raw_vals = np.unique(train[c]) val_map = {} for i in range(len(raw_vals)): # encode unique categories in column `c` val_map[raw_vals[i]] = i input_list_train.append(train[c].map(val_map).values) input_list_test.append(test[c].map(val_map).values) # Extract remaining non-embedding columns i.e numeric input of # the feedforward embedding layer num_cols = [c for c in train.columns if (not c in categorical_features + ['id', 'stroke']) ] input_list_train.append(train[num_cols].values) input_list_test.append(test[num_cols].values) return input_list_train, input_list_test
3) Реализация модели
Мы создадим архитектуру нашей модели, используя Keras.
def get_model(num_numeric_cols): act_fn = 'relu' # hidden activation function dr = 0.2 # dropout rate lr = 0.001 # learning rate inputs = [] embeddings = [] for i in embedding_dims.keys(): input = tf.keras.layers.Input(shape=(1), dtype = tf.int32) x, y = embedding_dims[i] emb = tf.keras.layers.Embedding(x, y, input_length = 1)(input) emb = tf.keras.layers.Reshape(target_shape=(y,))(emb) inputs.append(input) embeddings.append(emb) inp = tf.keras.Input(shape=(num_numeric_cols, ), name ="numeric_features", dtype = tf.float32) num_emb = tf.keras.layers.Dense(256, activation = act_fn)(inp) inputs.append(inp) embeddings.append(num_emb) x = tf.keras.layers.Concatenate()(embeddings) x = tf.keras.layers.Dense( 128, activation = act_fn)(x) x = tf.keras.layers.BatchNormalization()(x) x = tf.keras.layers.Dropout(rate=dr)(x) x = tf.keras.layers.Dense( 128, activation = act_fn)(x) x = tf.keras.layers.BatchNormalization()(x) x = tf.keras.layers.Dropout(rate=dr)(x) preds = tf.keras.layers.Dense(1,activation = 'sigmoid', name ="classifier")(x) model = tf.keras.models.Model(inputs = inputs ,outputs = preds ) opt = tf.keras.optimizers.Adam(learning_rate=lr) model.compile(loss= "binary_crossentropy", optimizer= opt, metrics = ['Accuracy'] ) return model
Наш тренировочный цикл может быть выражен ниже;
# Let's create a simple 80/20 train/test split # we will stratify the split by the target because we have data # imbalance problem df_train, df_test, y_train, y_test = train_test_split(df, df['stroke'], test_size =0.2, stratify=df['stroke'], shuffle=True, random_state =2023) # create feature transformations X_train, X_test = feature_transformer( train = df_train, test = df_test ) Y_train , Y_test = y_train.values, y_test.values batch_size = 32 epochs = 100 n_num_cols = len(numerical_features) # initialize early stopping es = tf.keras.callbacks.EarlyStopping(patience=20, mode='min', verbose=1, monitor = "val_loss", min_delta = 0.00001, restore_best_weights=True) # initialize model model = get_model(num_numeric_cols = n_num_cols) model.fit( X_train, Y_train, validation_data = (X_test, Y_test), batch_size = batch_size, epochs = epochs, callbacks = [es], ) Epoch 1/100 128/128 [==============================] - 3s 9ms/step - loss: 0.5026 - Accuracy: 0.7921 - val_loss: 0.4618 - val_Accuracy: 0.8268 Epoch 2/100 128/128 [==============================] - 1s 7ms/step - loss: 0.2476 - Accuracy: 0.9325 - val_loss: 0.1662 - val_Accuracy: 0.9511 Epoch 3/100 128/128 [==============================] - 1s 7ms/step - loss: 0.2021 - Accuracy: 0.9408 - val_loss: 0.1742 - val_Accuracy: 0.9511 Epoch 4/100 128/128 [==============================] - 1s 8ms/step - loss: 0.1833 - Accuracy: 0.9447 - val_loss: 0.1589 - val_Accuracy: 0.9511 Epoch 5/100 128/128 [==============================] - 1s 8ms/step - loss: 0.1918 - Accuracy: 0.9437 - val_loss: 0.1688 - val_Accuracy: 0.9442
4) Ограничение
В отличие от классических методов машинного обучения, нейронные сети способны изучать нелинейности в данных. Однако из бесчисленных недостатков использования подходов моделирования нейронных сетей к табличным задачам одной общей проблемой является потребность в огромных объемах обучающих данных. Кроме того, необходимость указывать размеры каждой категориальной переменной вставляет гиперпараметр, который необходимо настроить, чтобы выжать максимум из этой модели.
В заключение мы увидели иллюстрации этапов проектирования данных и реализации модели, показывающих, как применять стратегию внедрения категориальных признаков к табличным данным.
Полный код доступен здесь.