основное введение:
Это настоящая игра. Источником вопроса о конкуренции являются большие данные 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. Мы можем узнать, просто подумав:
- user_id не имеет практического значения, это просто кодовое имя
- shop_id является целью нашего прогноза, наше требование к заголовку состоит в том, что мы предсказываем shop_id, где пользователь основан на другой информации, поэтому shop_id является нашей целью обучения
- Широта и долгота — полезная информация, связанная с нашим местоположением.
- wifi_id позволяет нам узнать, какой это маршрутизатор, расположение этого другого маршрутизатора отличается, поэтому это полезно
- wifi_strong — это мощность сигнала, связанная с нашим расстоянием от маршрутизатора, полезная
- con_sta — это состояние подключения, то есть подключено оно или нет. Изначально я увидел, что данные в основном не связаны, и подумал, что это бесполезно. Позже эксперт напомнил ему, что если кто-то автоматически подключается к вайфаю магазина, значит, он часто заходит, это тоже полезно для оценки покупателей.
- Мы видим, что 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", этот параметр определяет функцию потерь, которую необходимо минимизировать. Наиболее распространенные значения:
binary:logistic
Логистическая регрессия для бинарной классификации, возвращающая предсказанные вероятности (не классы).multi:softmax
Мультиклассификатор с использованием softmax, возвращающий предсказанные классы (не вероятности). В этом случае также необходимо задать еще один параметр: num_class (количество классов).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, чтобы также написать метку каждой строки.
приложение
использованная литература
- Серия машинного обучения (12)_Полное руководство по настройке параметров XGBoost (с кодом Python) http://blog.csdn.net/han_xiaoyang/article/details/52665396
- Соревнование 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)