Научите и напишите практическую программу XGBoost

машинное обучение алгоритм
Научите и напишите практическую программу XGBoost

основное введение:

Это настоящая игра. Источником вопроса о конкуренции являются большие данные Tianchi.«Точно определите местонахождение магазина, в котором находится пользователь в торговом центре». Существует 1,14 миллиона исходных данных, которые очень сложно подсчитать. Чтобы дать новичкам лучший опыт обучения и больше основ, я сократил набор данных и поместил его наздесь, пароль: ndfd. Для всех, чтобы скачать.

В моих данных данные выглядят так:train.csv

user_id идентификатор пользователя time_stamp отметка времени
latitude широта wifi_strong 1-10 Десять сильных сигналов Wi-Fi
longitude долгота wifi_id 1-10 десять идентификаторов Wi-Fi
shop_id идентификатор магазина con_sta 1-10 Десять статусов соединения Wi-Fi

test.csv

user_id идентификатор пользователя time_stamp отметка времени
latitude широта wifi_id 1-10 десять идентификаторов Wi-Fi
longitude долгота con_sta 1-10 Десять статусов соединения Wi-Fi
row_id отметка линии wifi_strong 1-10 Десять сильных сигналов Wi-Fi
shop_id идентификатор магазина

Смысл этого вопроса в том, что в торговом центре из-за ограничения различных слоев и точности GPS мы не можем точно знать, в каком магазине находится пользователь, основываясь только на широте и долготе. Мы можем точно определить, в каком магазине находится пользователь, благодаря соединению между мобильным телефоном и 10 ближайшими точками Wi-Fi. Компаниям удобно размещать рекламу соответствующих магазинов в зависимости от местонахождения пользователя.

начать настоящий бой

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

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

Мы должны избавиться от инерции мышления, когда мы находимся в реальном бою машинного обучения — все должно быть продумано, прежде чем мы сможем бежать. Это очень интересная инерция мышления, как ее объяснить? Например, в этом конкурсе я также изучал общение, когда я увидел десять значений мощности Wi-Fi, я захотел найти связь между ними, а затем запрограммировал на поиск точного местоположения человека. По сути, наше мышление остается на уровне явного программирования, и мы чувствуем, что только тогда, когда программа написана четко, можно достичь поставленной цели. Но на самом деле обработка больших данных не принцип. Независимо от того, с какими данными сталкивается дерево решений, будь то время или географическое положение, дерево генерируется в соответствии с определенными правилами, и, наконец, новые данные прогоняются по дереву, чтобы получить прогнозируемый результат. Другими словами, нам не нужно тратить много энергии на размышления о конкретном физическом значении каждых данных, просто поместите их в модель. (Настройка параметров должна просто учитывать физический смысл, чтобы придать вес каждому из данных, что будет обсуждаться позже)

Анализ данных

Значение наших данных указано в таблице выше.У нас есть идентификатор пользователя, широта и долгота, отметка времени, идентификатор магазина и информация о Wi-Fi. Мы можем узнать, просто подумав:

  1. user_id не имеет практического значения, это просто кодовое имя
  2. shop_id является целью нашего прогноза, наше требование к заголовку состоит в том, что мы предсказываем shop_id, где пользователь основан на другой информации, поэтому shop_id является нашей целью обучения
  3. Широта и долгота — полезная информация, связанная с нашим местоположением.
  4. wifi_id позволяет нам узнать, какой это маршрутизатор, расположение этого другого маршрутизатора отличается, поэтому это полезно
  5. wifi_strong — это мощность сигнала, связанная с нашим расстоянием от маршрутизатора, полезная
  6. con_sta — это состояние подключения, то есть подключено оно или нет. Изначально я увидел, что данные в основном не связаны, и подумал, что это бесполезно. Позже эксперт напомнил ему, что если кто-то автоматически подключается к вайфаю магазина, значит, он часто заходит, это тоже полезно для оценки покупателей.
  7. Мы видим, что test.csv в целом похож, и есть лишний row_id, следует обратить внимание на соответствующие результаты вывода.

подготовка библиотеки Python

import pandas as pd
import xgboost as xgb
from sklearn import preprocessing

Наш XGBoost относительно прост, поэтому мы используем три самые необходимые библиотеки, библиотеку обработки данных pandas, библиотеку xgboost и импортируем библиотеку предварительной обработки из известной библиотеки машинного обучения sklearn, Эта библиотека pandas имеет множество функций инкапсуляции для базовой обработки данных. , им проще пользоваться. Нажмите здесь, чтобы увидеть примерСсылка на сайт, я написал pandas.Dataframe, чтобы в основном разобрать данные.

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

Сначала нам нужно импортировать часть данных:

train = pd.read_csv(r'D:\XGBoost_learn\mall_location\train2.csv')
tests = pd.read_csv(r'D:\XGBoost_learn\mall_location\test_pre.csv')

Мы используем панд вread_csvФункция читает файл csv напрямую. Полное название CSV-файла — Comma-Separated Values ​​file, то есть все данные разделяются запятыми, что является относительно лаконичным и также является широко используемым форматом для различных соревнований по данным. На что нам нужно обратить внимание, так это на проблему пути, под окнами\, под линуксом/, это другое. И путь, который мы пишем, часто совпадает с полем функции в библиотеке, поэтому добавьте его в начало пути.rЧтобы запретить сопоставление с Curry, совпадение сообщает об ошибке.rОзначает сырой, сырой, все понимают его по названию.

нашtime_stampЭто оказался тип данных str, компьютер не будет знать, что это такое, только строка строк. Итак, мы конвертируем в обработку даты и времени:

train['time_stamp'] = pd.to_datetime(pd.Series(train['time_stamp']))
tests['time_stamp'] = pd.to_datetime(pd.Series(tests['time_stamp']))

И поезд, и тесты обрабатываются. Это также отражает силу панд. Далее мы посмотрим, как выглядят данные time_stamp:2017/8/6 21:20, глядя на набор данных, мы видим, что это данные с десятиминутной точностью (зернистостью). Я чувствую, что эти данные содержат слишком много информации. Собирать их воедино расточительно (на самом деле, их легко переобучить, т. к. узел будет очень сильно разделен (ладно), давайте разберем его:

train['Year'] = train['time_stamp'].apply(lambda x: x.year)
train['Month'] = train['time_stamp'].apply(lambda x: x.month)
train['weekday'] = train['time_stamp'].dt.dayofweek
train['time'] = train['time_stamp'].dt.time
tests['Year'] = tests['time_stamp'].apply(lambda x: x.year)
tests['Month'] = tests['time_stamp'].apply(lambda x: x.month)
tests['weekday'] = tests['time_stamp'].dt.dayofweek
tests['time'] = tests['time_stamp'].dt.time

Внимательные друзья могут обнаружить, что здесь есть два способа писать, один из них.apply(lambda x: x.year),Что это означает? На самом деле это способ написания анонимной функции. Анонимная функция означает, что мы хотим написать функцию, но не хотим думать о том, как назвать функцию. В настоящее время нам нужна анонимная функция для реализовать несколько небольших функций. Здесь мы используем.apply(lambda x: x.year)на самом деле называетсяapplyФункция, это означает добавить этот столбец, содержимое добавленного столбца是x.year. Если мы чувствуем, что это не интуитивно понятно для написания, мы также можем написать это так:

YearApply(x):
   return x.year
   
train['Year'] = train['time_stamp'].apply(YearApply)

Оба написания имеют одинаковое значение. При вызове дня недели и даты и времени мы используем функции в numpydt, использование показано в коде. На самом деле этоweekdayЭто также можно записать так:train['weekday'] = train['time_stamp'].apply(lambda x: x.weekday()), обратите внимание на лишние скобки, потому что为weekdayЧтобы получить его, его нужно вычислить, поэтому внутренняя функция также вызывается. Зачем использоватьweekdayНу, потому что день недели более характерен для покупок, чем день недели. Далее отбрасываем этот time_stamp, потому что уже есть годы и месяцы:

train = train.drop('time_stamp', axis=1)
tests = tests.drop('time_stamp', axis=1)

Затем отбросьте пропущенные значения или заполните пропущенные значения.

train = train.dropna(axis=0)
tests = tests.fillna(method='pad')

Мы видим, что я использую два разных способа обработки обучающего набора и тестового набора. В обучающем наборе данных больше, а доля пропущенных значений относительно мала, поэтому используются все пропущенные значенияdropnaФункция, файл тестов является тестовым набором и не может потерять часть информации, даже если в данных много пропущенных значений, поэтому мы используем различные методы для его восполнения, здесь мы используем предыдущий метод дополнения не -нан значения(method=“pad”), конечно, есть и другие способы, например, дополнить наиболее часто встречающимся значением в этом столбце.

class DataFrameImputer(TransformerMixin):
   def fit(self, X, y=None):
       for c in X:
           if X[c].dtype == np.dtype('O'):
               fill_number = X[c].value_counts().index[0]
               self.fill = pd.Series(fill_number, index=X.columns)
           else:
               fill_number = X[c].median()
               self.fill = pd.Series(fill_number, index=X.columns)
       return self
       
       def transform(self, X, y=None):
           return X.fillna(self.fill)
       
train = DataFrameImputer().fit_transform(train)

Этот фрагмент кода немного неудобен, он означает, что для каждого c в X, если тип X[c] равенobject(‘O’выражатьobject), то[X[c].value_counts().index[0]передать нулевое значение,[X[c].value_counts().index[0]Указывает число с наиболее повторяющимся появлением, если неobjectтипа, пришлите обратноX[c].median(), что является медианой этих чисел.

Здесь мы можем использовать print для вывода того, как выглядят наши данные.

print(train.info())
<class 'pandas.core.frame.DataFrame' at 0x0000024527C50D08>
Int64Index: 467 entries, 0 to 499
Data columns (total 38 columns):
user_id          467 non-null object
shop_id          467 non-null object
longitude        467 non-null float64
latitude         467 non-null float64
wifi_id1         467 non-null object
wifi_strong1     467 non-null int64
con_sta1         467 non-null bool
wifi_id2         467 non-null object
wifi_strong2     467 non-null int64
con_sta2         467 non-null object
wifi_id3         467 non-null object
wifi_strong3     467 non-null float64
con_sta3         467 non-null object
wifi_id4         467 non-null object
wifi_strong4     467 non-null float64
con_sta4         467 non-null object
wifi_id5         467 non-null object
wifi_strong5     467 non-null float64
con_sta5         467 non-null object
wifi_id6         467 non-null object
wifi_strong6     467 non-null float64
con_sta6         467 non-null object
wifi_id7         467 non-null object
wifi_strong7     467 non-null float64
con_sta7         467 non-null object
wifi_id8         467 non-null object
wifi_strong8     467 non-null float64
con_sta8         467 non-null object
wifi_id9         467 non-null object
wifi_strong9     467 non-null float64
con_sta9         467 non-null object
wifi_id10        467 non-null object
wifi_strong10    467 non-null float64
con_sta10        467 non-null object
Year             467 non-null int64
Month            467 non-null int64
weekday          467 non-null int64
time             467 non-null object
dtypes: bool(1), float64(10), int64(5), object(22)
memory usage: 139.1+ KB
None

Мы можем четко видеть структуру нашего кода, сколько в нем столбцов, сколько значений находится под каждым столбцом и т. д. Мы можем судить о том, есть ли нулевые значения, исходя из количества значений. Мы добавляем это перед обработкой отсутствующих значенийprint(train.info())получите:

<class 'pandas.core.frame.DataFrame' at 0x000001ECFA6D6718>
RangeIndex: 500 entries, 0 to 499

В нем 500 значений, а после обработки осталось всего 467 значений, что говорит о том, что многие из них отброшены. Точно так же мы можем вывести тестовую информацию:

<class 'pandas.core.frame.DataFrame' at 0x0000019E13A96F48>
RangeIndex: 500 entries, 0 to 499

500 значений не меньше. Все дополнено. Здесь я взял только заголовок выходной информации и не стал вставлять сюда всю информацию, потому что вся информация очень длинная. Заметим, что эти данныеbool,float,int,objectЧетыре типа, наш XGBoost — это дерево регрессии, которое может обрабатывать только числовые данные, поэтому нам нужно преобразовать. Что нам делать с этими строковыми типами данных? Мы используем метод LabelEncoder:

for f in train.columns:
    if train[f].dtype=='object':
        if f != 'shop_id':
            print(f)
            lbl = preprocessing.LabelEncoder()
            train[f] = lbl.fit_transform(list(train[f].values))
for f in tests.columns:
    if tests[f].dtype == 'object':
        print(f)
        lbl = preprocessing.LabelEncoder()
        tests[f] = lbl.fit_transform(list(tests[f].values))

Смысл этого кода в том, чтобы вызвать метод LabelEncoder в предобработке в sklearn для маркировки данных.Основная функция — превратить их в цифровые данные, а некоторые нормализовать, чтобы они работали быстрее и так далее. Давайте посмотрим на этот код, lbl — это просто сокращение от LabelEncoder,lbl = preprocessing.LabelEncoder(), в этом коде всего одна подстановка, кажется, что строка не такая уж и длинная, и на самом деле ничего не запускается. второе предложениеlbl.fit_transform(list(train[f].values))Он кодирует каждое значение в поезде, и мы выводим его до и после него.train[f].valuesВидно, что:

print(train[f].values)
train[f] = lbl.fit_transform(list(train[f].values))
print(train[f].values)

я добавил эту строку0и/Цель состоит в том, чтобы разделить выходные данные. у нас есть:

user_id
['u_376' 'u_376' 'u_1041' 'u_1158' 'u_1654' 'u_2733' 'u_2848' 'u_3063'
 'u_3063' 'u_3063' 'u_3604' 'u_4250' 'u_4508' 'u_5026' 'u_5488' 'u_5488'
 'u_5602' 'u_5602' 'u_5602' 'u_5870' 'u_6429' 'u_6429' 'u_6870' 'u_6910'
 'u_7037' 'u_7079' 'u_7869' 'u_8045' 'u_8209']
[ 7  7  0  1  2  3  4  5  5  5  6  8  9 10 11 11 12 12 12 13 14 14 15 16 17
 18 19 20 21]

Мы видим, что LabelEncoder преобразует наши данные типа str в числа. по собственному набору стандартов. По данным тестов мы видим, что я индивидуальноshop_idизбегали. Причина этого в том, чтоshop_idЭто данные, которые мы хотим отправить, и не может быть никакого поведения при кодировании.Это состояние str должно поддерживаться.

Затем вам нужно преобразовать обучение и тесты в матричные типы, чтобы упростить операции XGBoost:

feature_columns_to_use = ['Year', 'Month', 'weekday',
'time', 'longitude', 'latitude',
'wifi_id1', 'wifi_strong1', 'con_sta1',
 'wifi_id2', 'wifi_strong2', 'con_sta2',
'wifi_id3', 'wifi_strong3', 'con_sta3',
'wifi_id4', 'wifi_strong4', 'con_sta4',
'wifi_id5', 'wifi_strong5', 'con_sta5',
'wifi_id6', 'wifi_strong6', 'con_sta6',
'wifi_id7', 'wifi_strong7', 'con_sta7',
'wifi_id8', 'wifi_strong8', 'con_sta8',
'wifi_id9', 'wifi_strong9', 'con_sta9',
'wifi_id10', 'wifi_strong10', 'con_sta10',]
train_for_matrix = train[feature_columns_to_use]
test_for_matrix = tests[feature_columns_to_use]
train_X = train_for_matrix.as_matrix()
test_X = test_for_matrix.as_matrix()
train_y = train['shop_id']

Цель для обучения - нашаshop_id,такtrain_yдаshop_id.

Импортируйте модель для создания дерева решений

gbm = xgb.XGBClassifier(silent=1, max_depth=10, n_estimators=1000, learning_rate=0.05)
gbm.fit(train_X, train_y)

На самом деле эти два предложения можно объединить в одно предложение, мы находимся вXGBClassifierВ нем задаются параметры, все его параметры и их значения по умолчанию (значения по умолчанию) я пишу здесь,Контент взят из исходного кода XGBoost.:

  • max_depth=3, что представляет максимальную глубину дерева, по умолчанию три уровня. Чем больше max_depth, тем более конкретные и локальные образцы будет изучать модель.
  • learning_rate=0,1, скорость обучения, то есть коэффициент, умноженный на градиент бустинга, чем меньше, тем медленнее спуск, но тем точнее спуск.
  • n_estimators= 100, что является максимальным количеством итераций слабого ученика или максимальным количеством слабых учеников. Вообще говоря, если n_estimators слишком мало, его легко недообучить.Если n_estimators слишком велико, объем вычислений будет слишком большим, и после того, как n_estimators достигнет определенного числа, улучшение модели, полученное за счет увеличения n_estimators, будет небольшим, поэтому обычно выбирают умеренное значение. По умолчанию 100.
  • silent= True, хотим ли мы выводить информацию в фоновом режиме при обучении дерева xgboost, True означает, что будет выводиться вся информация связующего дерева.
  • objective="binary:logistic", этот параметр определяет функцию потерь, которую необходимо минимизировать. Наиболее распространенные значения:
  1. binary:logisticЛогистическая регрессия для бинарной классификации, возвращающая предсказанные вероятности (не классы).
  2. multi:softmaxМультиклассификатор с использованием softmax, возвращающий предсказанные классы (не вероятности). В этом случае также необходимо задать еще один параметр: num_class (количество классов).
  3. multi:softprobТо же, что параметр multi:softmax, но возвращает вероятность того, что все данные принадлежат каждой категории.
  • nthread=-1, многопоточное управление, в соответствии с настройками ядра вашего компьютера, вы можете установить столько потоков, сколько хотите.Если вы хотите использовать все ядра, не устанавливайте их, алгоритм автоматически распознает
  • `gamma= 0, когда узел разделен, узел будет разделен только в том случае, если значение функции потерь упадет после разделения. Гамма определяет минимальное снижение функции потерь, необходимое для разделения узла. Чем больше значение этого параметра, тем более консервативным будет алгоритм. Значение этого параметра тесно связано с функцией потерь, поэтому его необходимо корректировать.
  • min_child_weight=1, определите минимальную сумму выборочных весов конечных узлов. и ГБМmin_child_leafПараметры похожи, но не идентичны. Этот параметр XGBoost представляет собой сумму минимальных весов выборки, а параметр GBM — минимальное общее количество выборок. Этот параметр используется дляизбегать переоснащения. Когда его значение велико, это может помешать модели изучить локальные специальные выборки. Но если это значение слишком велико, это приведет к недообучению. Этот параметр необходимо настроить с помощью CV
  • max_delta_step=0, определите минимальную сумму выборочных весов конечных узлов. Похож на параметр min_child_leaf в GBM, но не совсем такой же. Этот параметр XGBoost представляет собой сумму минимальных весов выборки, а параметр GBM — минимальное общее количество выборок. Этот параметр используется дляизбегать переоснащения. Когда его значение велико, это может помешать модели изучить локальные специальные выборки. Но если это значение слишком велико, это приведет к недообучению. Этот параметр необходимо настроить с помощью CV.
  • subsample=1, точно так же, как параметр подвыборки в GBM. этот параметрУправляет долей случайной выборки для каждого дерева. Уменьшение значения этого параметра делает алгоритм более консервативным и позволяет избежать переобучения. Однако, если это значение установлено слишком маленьким, это может привести к недообучению. Типичное значение: 0,5-1
  • colsample_bytree=1, используется для управления долей случайного количества столбцов в каждом дереве (каждый столбец является функцией). Типичное значение: 0,5-1
  • colsample_bylevel=1, который используется для управления пропорцией выборки количества столбцов для каждого разделения каждого уровня дерева. На самом деле параметр subsample и параметр colsample_bytree могут играть аналогичную роль.
  • reg_alpha=0, член регуляризации L1 для весов. (Аналогично регрессии Лассо). Его можно применять к очень большим размерам, что делает алгоритм быстрее.
  • reg_lambda=1, член регуляризации L2 веса. Этот параметр используется для управления частью регуляризации XGBoost. Чем больше этот параметр, тем больше вы можете оштрафовать сложность дерева.
  • scale_pos_weight=1, когда выборки каждой категории очень несбалансированные, установка этого параметра в положительное значение может сделать
  • base_score=0,5, начальная оценка прогноза для всех экземпляров, глобальное смещение, при достаточном количестве итераций изменение этого значения не даст большого эффекта.
  • seed=0, устанавливается начальное число случайного числа, оно может воспроизводить результаты случайных данных, а также может использоваться для настройки параметров

Данные передаются через дерево для генерации результатов прогнозирования.

predictions = gbm.predict(test_X)

Пропустите данные в тестах через сгенерированную модель, чтобы получить результат прогноза.

submission = pd.DataFrame({'row_id': tests['row_id'],
                            'shop_id': predictions})
print(submission)
submission.to_csv("submission.csv", index=False)

Запишите результаты прогноза в файл csv. Обращаем внимание на формат записываемого файла,row_idспереди,shop_idпозади.index=Falseозначает не писать имя строки. Измените значение на True, чтобы также написать метку каждой строки.



приложение

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

  1. Серия машинного обучения (12)_Полное руководство по настройке параметров XGBoost (с кодом Python) http://blog.csdn.net/han_xiaoyang/article/details/52665396
  2. Соревнование Kaggle: Катастрофа на Титанике: https://www.kaggle.com/c/titanic

полный код

import pandas as pd
import xgboost as xgb
from sklearn import preprocessing


train = pd.read_csv(r'D:\mall_location\train.csv')
tests = pd.read_csv(r'D:\mall_location\test.csv')

train['time_stamp'] = pd.to_datetime(pd.Series(train['time_stamp']))
tests['time_stamp'] = pd.to_datetime(pd.Series(tests['time_stamp']))

print(train.info())

train['Year'] = train['time_stamp'].apply(lambda x:x.year)
train['Month'] = train['time_stamp'].apply(lambda x: x.month)
train['weekday'] = train['time_stamp'].apply(lambda x: x.weekday())
train['time'] = train['time_stamp'].dt.time
tests['Year'] = tests['time_stamp'].apply(lambda x: x.year)
tests['Month'] = tests['time_stamp'].apply(lambda x: x.month)
tests['weekday'] = tests['time_stamp'].dt.dayofweek
tests['time'] = tests['time_stamp'].dt.time
train = train.drop('time_stamp', axis=1)
train = train.dropna(axis=0)
tests = tests.drop('time_stamp', axis=1)
tests = tests.fillna(method='pad')
for f in train.columns:
    if train[f].dtype=='object':
        if f != 'shop_id':
            print(f)
            lbl = preprocessing.LabelEncoder()
            train[f] = lbl.fit_transform(list(train[f].values))
for f in tests.columns:
    if tests[f].dtype == 'object':
        print(f)
        lbl = preprocessing.LabelEncoder()
        lbl.fit(list(tests[f].values))
        tests[f] = lbl.transform(list(tests[f].values))


feature_columns_to_use = ['Year', 'Month', 'weekday',
'time', 'longitude', 'latitude',
'wifi_id1', 'wifi_strong1', 'con_sta1',
 'wifi_id2', 'wifi_strong2', 'con_sta2',
'wifi_id3', 'wifi_strong3', 'con_sta3',
'wifi_id4', 'wifi_strong4', 'con_sta4',
'wifi_id5', 'wifi_strong5', 'con_sta5',
'wifi_id6', 'wifi_strong6', 'con_sta6',
'wifi_id7', 'wifi_strong7', 'con_sta7',
'wifi_id8', 'wifi_strong8', 'con_sta8',
'wifi_id9', 'wifi_strong9', 'con_sta9',
'wifi_id10', 'wifi_strong10', 'con_sta10',]

big_train = train[feature_columns_to_use]
big_test = tests[feature_columns_to_use]
train_X = big_train.as_matrix()
test_X = big_test.as_matrix()
train_y = train['shop_id']

gbm = xgb.XGBClassifier(silent=1, max_depth=10,
                    n_estimators=1000, learning_rate=0.05)
gbm.fit(train_X, train_y)
predictions = gbm.predict(test_X)

submission = pd.DataFrame({'row_id': tests['row_id'],
                            'shop_id': predictions})
print(submission)
submission.to_csv("submission.csv",index=False)