Содержание

  1. Вступление
  2. Понимание данных
  3. Исследовательский анализ данных
  4. Функциональная инженерия
  5. Связь между географическими местоположениями с LSTM
  6. Описание методологии
  7. Окончательные предварительно обработанные данные
  8. Обучение и оценка
  9. Заключение
  10. использованная литература

Вступление

По мере того, как мы глубоко погружаемся в сферу искусственного интеллекта, мы сталкиваемся с путями, с помощью которых ИИ может использовать человеческую жизнь и потенциально спасти ее от бедствий в ближайшем будущем. Одним из таких небольших шагов к этому далекому, но многообещающему будущему является использование данных из прошлого и поиск некоторых закономерностей, которые являются универсальными по своей природе и которые могут обещать некоторые точные прогнозы, тем самым помогая людям минимизировать последствия стихийных бедствий. В этой статье я собираюсь объяснить свой подход к прогнозированию ущерба, нанесенного зданию во время землетрясения в Непале в 2015 году. Конкурс проводится компанией Driven Data.

Полный код этого проекта можно найти здесь.

Понимание данных

Мы пытаемся предсказать порядковую переменную damage_grade, которая представляет уровень повреждения здания, пострадавшего от землетрясения. Различают 3 степени повреждений:

  • 1 означает низкий урон
  • 2 представляет собой средний урон
  • 3 представляет собой почти полное разрушение

В этом наборе данных 39 столбцов, где столбец building_id является уникальным и случайным идентификатором.

Описание

  • geo_level_1_id, geo_level_2_id, geo_level_3_id (тип: int): географический регион, в котором находится здание, от самого большого (уровень 1) до наиболее специфичного субрегиона (уровень 3). Возможные значения: уровень 1: 0-30, уровень 2: 0-1427, уровень 3: 0-12567.
  • count_floors_pre_eq (тип: int): количество этажей в здании до землетрясения.
  • age (тип: int): возраст здания в годах.
  • area_percentage (тип: int): нормализованная площадь контура здания.
  • height_percentage (тип: int): нормализованная высота контура здания.
  • land_surface_condition (тип: категориальный): состояние поверхности земли, на которой построено здание. Возможные значения: n, o, t.
  • foundation_type (тип: категориальный): тип фундамента при строительстве. Возможные значения: h, i, r, u, w.

Подробную информацию обо всех 38 функциях можно найти здесь.

Загрузка набора данных

X = pd.read_csv('/content/drive/My Drive/data_ml/Richters Predictor Earthquake Damage/train_values_RP.csv', index_col='building_id')
Y= pd.read_csv('/content/drive/My Drive/data_ml/Richters Predictor Earthquake Damage/train_labels_RP.csv',index_col='building_id')

Исследовательский анализ данных

  1. Посчитать участок застройки

Большинству зданий нанесен ущерб 2-го уровня.

Особенности проектирования идентификатора географического уровня

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

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

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

geo_level_3_id имеет 11595 различных типов значений, geo_level_2_id имеет 1414 различных типов значений. geo_level_1_id имеет 64 различных типа значений (хотя у него не так много значений, но мы его разработали).

Чтобы преодолеть это, я применил технику разработки функций , заменив значения их условными вероятностями по отношению к каждому результату. Таким образом, каждый столбец geo_lev заменен на 3 столбцы для каждого результата (y).

Например, P (y = 1 | x = 6) для geo_level_1 вместо того, где X[geo_level_1_id]=6 и damage_grade = 1 .. Мы делаем это для всех geo_уровней со всеми результатами.

Фрагмент кода разработки функций

fea = pd.merge(X, Y, on='building_id')
damage1 = {}
damage2 = {}
damage3 = {}
for i,j in (X['geo_leveldamage_gradeid'].value_counts()).iteritems():
    n1 = len(fea[fea['damage_grade']==1][fea['geo_leveldamage_gradeid']==i])
    n2 = len(fea[fea['damage_grade']==2][fea['geo_leveldamage_gradeid']==i])
    n3 = len(fea[fea['damage_grade']==3][fea['geo_leveldamage_gradeid']==i])
damage1[i] = n1/j
damage2[i] = n2/j
damage3[i] = n3/j
list1 = []
list2 = []
list3 = []
for i in X['geo_leveldamage_gradeid']:
    list1.append(damage1.get(i))
    list2.append(damage2.get(i))
    list3.append(damage3.get(i))
X['prob1_geo1'] = list1
X['prob2_geo1'] = list2
X['prob3_geo1'] = list3

Связь между географическими уровнями с LSTM

Эта часть немного сложна. Изучая данные, я обнаружил некоторую взаимосвязь между этими тремя уровнями локаций. Гео-уровень-3 - это самое мелкомасштабное местоположение под гео-уровнем 2, которое снова находится ниже-1, являясь крупномасштабным местоположением.

Для конкретного значения местоположения-1 и местоположения-2 существует большое количество одинаковых значений местоположения-3. Пример-

LSTM

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

Я применил аналогичный метод для создания этих геоуровней. Кажется, существует последовательная связь между географическими уровнями, поскольку уровень-1 является наивысшим и так далее. Итак, я создал специальный ввод для LSTM, который представляет собой последовательность гео-уровня 1 и гео-уровня 2, а вывод - гео-уровень-3

  • Интуитивно мы даем последовательность геоуровня 1 и уровня 2 в качестве входных данных для нашей модели, которая предсказывает географический уровень 3.
  • После обучения данных мы берем выходные данные слоя LSTM (промежуточный слой) и используем их в качестве функций для нашего прогнозирования землетрясений.
  • Идея состоит в том, чтобы найти взаимосвязь между этими геоуровнями в математическом формате.

Описание методологии

Для начала нам нужно понять, какой тип ввода требует RNN.

Тип входных данных, требуемых LSTM:

( # Datapts, # Timestamps, dimension of vector ) 

Timpstamps означает «количество слов или количество последовательностей» в точке данных. Это ось времени.

Чтобы получить правильный формат, мы имитируем работу слоя встраивания и функции токенизатора keras.

  • Сначала я объединяю geo_leveldamage_gradeid и geo_level1id вместе и передаю их через one_hot_encoder, чтобы получить общий словарь или измерение каждой последовательности. Это похоже на то, как мы делаем это в НЛП, у нас есть слова как последовательность, и мы передаем ее через Tokenizer, чтобы получить vocab_size.
# l1 is series df of lev1 # l2 is series df of lev2
inp = pd.concat([l1,l2]).to_numpy().reshape(-1,1)
#get shared vocab of l1 and l2
onehot = OneHotEncoder(sparse=False)
onehot.fit(inp)
  • Теперь я трансформирую l1 и l2 по отдельности, чтобы получить их горячие векторы. Это приводит к тому же размеру вектора для последовательности-1, которая является geo_level_1, и последовательности-2, которая является geo_level_2, в соответствии с требованиями RNN.
l1_hot = onehot.transform(l1)
l1_hot.shape
>>> (347469, 1419)
l2_hot = onehot.transform(l2)
l2_hot.shape
>>> (347469, 1419)           #both have same # datapts and dimension
  • Теперь последняя часть - получение правильной формы ввода для LSTM. Ему нужна последовательность данных с каждой последовательностью one_hot рядом. Мы даем его с помощью стека numpy.
fin_inp = np.stack((l1_hot,l2_hot), axis=1)
fin_inp.shape
   >>> (347469, 2, 1419)

Это именно то, что LSTM хочет в качестве входных данных. Это легко описать на рисунке ниже.

Примечание. Мы не можем использовать word_embedding , потому что здесь нет встраивания слов, есть только один_hot_vector каждой последовательности, которые складываются вместе для создания правильной формы ввода для РНН. В основном имитирует алгоритм встраивания

Модель RNN

inpx = Input( shape=fin_inp.shape[1:] )
lstm = LSTM(20, )(inpx)
lvl3 = Dense(l3_hot.shape[1], activation='sigmoid')(lstm)
modelf = Model(inputs= inpx, outputs=lvl3)
modelf.compile(loss='binary_crossentropy', optimizer='adam')
modelf.summary()
>>> Output
Model: "model" _________________________________________________________________ Layer (type)                 Output Shape              Param #    ================================================================= input_1 (InputLayer)         [(None, 2, 1419)]         0          _________________________________________________________________ lstm (LSTM)                  (None, 20)                115200     _________________________________________________________________ dense (Dense)                (None, 11861)             249081     ================================================================= Total params: 364,281 
Trainable params: 364,281 
Non-trainable params: 0

Подгонка модели

l3_hot = np.array(pd.get_dummies(l3)) #l3 is geo_lev_3 df
modelf.fit(fin_inp, l3_hot, epochs=10, batch_size=64)
Epoch 1/10
5430/5430 [=================] - 33s 6ms/step - loss: 0.0185
Epoch 2/10
5430/5430 [=================] - 33s 6ms/step - loss: 8.3740e-04
Epoch 3/10
5430/5430 [=================] - 34s 6ms/step - loss: 8.3151e-04
.
.
.
Epoch 9/10
5430/5430 [=================] - 33s 6ms/step - loss: 7.0521e-04
Epoch 10/10
5430/5430 [=================] - 32s 6ms/step - loss: 6.7878e-04

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

Layers [1] - это результат слоя LSTM, который мы хотим

inter = Model(inputs=modelf.input, outputs=modelf.layers[1].output)

Получение fin_inp тем же методом и прогнозирование получения необходимых функций.

fin_inp_train = np.stack((l1_hot,l2_hot), axis=1)
fin_inp_train.shape
>>> (260601, 2, 1419)
fin_inp_test = np.stack((l1_hot,l2_hot), axis=1)
fin_inp_test.shape
>>> (86868, 2, 1419)

geo_fea_train = inter.predict(fin_inp_train)
geo_fea_test = inter.predict(fin_inp_train)

Окончательные предварительно обработанные данные

Для наших окончательных данных мы отбрасываем geo_levels и создаем one_hot всех оставшихся функций. Сложите их вместе с нашими недавно созданными функциями geo_fea_train.

Примечание: рассчитанные вероятности уже добавлены к данным.

fx_tr = np.hstack((np.array(pd.get_dummies(X.copy().drop(['geo_leveldamage_gradeid', 'geo_level1id', 'geo_level2id'], axis=1))), geo_fea_train))
fx_tr.shape
  >>> (260601, 94)
fx_te = np.hstack((np.array(pd.get_dummies(test.copy().drop(['geo_leveldamage_gradeid', 'geo_level1id', 'geo_level2id'], axis=1))), geo_fea_test))
fx_te.shape
 >>> (86868, 94)
fy_tr = Y.to_numpy().ravel()
fy_tr.shape
 >>> (260601,)

Обучение и оценка

Здесь мы используем поиск по сетке CV и случайный лес.

param = {'n_estimators': [ 500, 1000], 
'min_samples_split':[20, 50, 500]}
clf = RandomForestClassifier()
gd_sr = GridSearchCV(estimator=clf,param_grid=param,scoring='f1_micro',cv=5,n_jobs=-1)
gd_sr.fit(fx_tr, fy_tr)

Выход

gd_sr.best_score_
>>> 0.75467870076
gd_sr.best_params_
>>> {'min_samples_split': 20, 'n_estimators': 1000}

Заключение

Используя лучшие параметры, я обучил модель и предсказал тестовые значения. Занял позицию среди 3% лучших в конкурсе.

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



Https://colah.github.io/posts/2015-08-Understanding-LSTMs/