Руководство по прогнозированию диагноза инсульта с помощью нейронной сети на основе встраивания категориальных признаков.
По данным Всемирной организации здравоохранения, инсульт занимает второе место среди причин смерти в мире, на него приходится примерно 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) Ограничение
В отличие от классических методов машинного обучения, нейронные сети способны изучать нелинейности в данных. Однако из бесчисленных недостатков использования подходов моделирования нейронных сетей к табличным задачам одной общей проблемой является потребность в огромных объемах обучающих данных. Кроме того, необходимость указывать размеры каждой категориальной переменной вставляет гиперпараметр, который необходимо настроить, чтобы выжать максимум из этой модели.
В заключение мы увидели иллюстрации этапов проектирования данных и реализации модели, показывающих, как применять стратегию внедрения категориальных признаков к табличным данным.
Полный код доступен здесь.