машинное обучение
May 29, 2021

Предсказываем с моделями машинного обучения

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

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

data_m['расходы'] = 2*np.sqrt(data_m['зарплата'])+0.5*data_m['сумма_помощи']

Для предсказания будем использовать популярную реализацию алгоритма градиентного бустинга LightGBM.

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

Сначала реализуем этап обучения:

X_tr, X_ts, y_tr, y_ts = train_test_split(data_m[['зарплата', 'сумма_помощи']], data_m['расходы'], test_size=0.2)
train_data = lgb.Dataset(X_tr, label=y_tr)
test_data = lgb.Dataset(X_ts, label=y_ts)
params = {"objective": "regression"}
bst = lgb.train(params, train_set = train_data, valid_sets=[test_data])

С помощью train_test_split мы разделили данные на обучающую и тестовую выборки, затем заполнили внутренний тип библиотеки lightgbm - Dataset данными для обучения и тренировки, задали параметры модели в словаре params (пока достаточно лишь задания типа задачи - классификация/регрессия).

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

y_pr = bst.predict(X_ts, num_iteration=bst.best_iteration)

Как можно заметить, алгоритм смог даже с настройками по умолчанию уловить закономерности в формировании расходов и достаточно точно предсказать данные.

Ниже привожу код для генерации исследованного набора данных:

import pandas as pd
import numpy as np
import string
import lightgbm as lgb
from sklearn.model_selection import train_test_split
from sklearn.metrics import mean_absolute_error, mean_absolute_percentage_error

np.random.seed(0)
letters = np.array(list(string.ascii_uppercase))
digits = np.arange(0,10)
people_num = 10000
towns_num = 200 # количество городов, нацело делит people_num для удобства
# людям соответствуют по три случайные буквы алфавита (имя, фамилия)
names = np.random.choice(letters, size=(people_num,3))
# зарплата от 20 до 50 тыс. рублей, случайное равномерное распределение
salary = np.random.uniform(20,500, size=people_num)
# индекс города - по семь цифр из алфавита, их меньше чем людей,
# так как предполагаем одинаковые города для разных людей
loc_ind_p = np.random.choice(digits, size=(towns_num,7))
loc_ind = np.tile(loc_ind_p, (people_num//towns_num,1))
# склеим буквы имен и фамилий, а также
# индексы городов
names = pd.DataFrame(names, dtype='str').apply(lambda x:' '.join(x),axis=1)
loc_ind = pd.DataFrame(loc_ind, dtype='str').apply(lambda x:''.join(x),axis=1)
data = pd.DataFrame({'ФИО':names, 'индекс_города':loc_ind, 
                         'зарплата':salary})
data['зарплата'] = np.round(data['зарплата'],2)
helps = np.random.uniform(5,10, size = towns_num)
town_help = pd.DataFrame({'индекс_города':pd.DataFrame(loc_ind_p, dtype='str')
                                          .apply(lambda x:''.join(x), axis=1),
                          'сумма_помощи':helps})
town_help['сумма_помощи'] = np.round(town_help['сумма_помощи'],2)
data_m = pd.merge(data, town_help, left_on = 'индекс_города', 
                    right_on='индекс_города')
# перемешиваем данные
data_m = data_m.sample(frac=1).reset_index(drop=True)
data_m['расходы'] = 2*np.sqrt(data_m['зарплата'])+\
                        0.5*data_m['сумма_помощи']