Pull to refresh

Comments 16

Спасибо за статью.
Random Forest как раз устойчив к незначимым атрибутам :) а как можно интерпретировать такую модель я плохо представляю (кроме, как раз, выделения важных параметров), но мне интересно. Может вы знаете какие-нибудь примеры наглядные?
А чем ещё занимаются в ритейловой аналитике, если не секрет?
В данном примере для меня было интересно увидеть, во-первых, влияет ли наличие описания товара на вероятность его покупки и, во-вторых, что, конкретно, в описании важно для покупателя. В результате очевидно, что описание влияет, но гораздо меньше, чем цена или состояние товара, как и ожидалось. Правда, оказалось неожиданно, что покупатель всё ещё реагирует на такие слова как «good» и «great».
В ритейле множество задач, которые в большинстве случаев решаются либо с использованием «экспертных» знаний, либо методом проб и ошибок. Например, где открыть новый магазин, чтобы покупателям было удобно и, соответственно, он был прибыльным. Какие товары поставить на рядом на полку, чтобы увеличить продажи. Какую назначить продажную цену для товара, чтобы получить максимальную прибыль и не потерять покупателя.
Спасибо. То есть интерпретация в примере и есть выделение важных признаков.
Правда, оказалось неожиданно, что покупатель всё ещё реагирует на такие слова как «good» и «great».
Можно ли делать выводы именно о причинности в данном случае?
Логистическая модель склонна к overfitting. Всегда нужно смотреть на результат cross-validation, я уверен что для логистической модели он будет ниже, поэтому вы так сильно опустились в итоговой таблице. По опыту, для текста хорошо работает LSA + XGBoost/RandomForest. Также не вижу у вас tf-idf взвешивания, крайне полезная штука.

Если запускать RandomForest напрямую на DT-матрице, стоит потюнинговать его параметры: 1000+ деревьев и количество фич, отобранных для каждого дерева, взять приблизительно равным квадратному корню из количества столбцов (кол-ва уникальных термов)
Небольшое замечание: AUC — это «Area under curve», сама по себе это не метрика, т.к. «curve» бывают разные. Популярные варианты — ROC-AUC и PR-AUC. Хотя по примеру и так все ясно, все равно лучше явно указывать, какая метрика используется.
Я тоже проходил этот курс, и тоже участвовал в этом соревновании Kaggle.
Мой результат — 0.863 на Private Board.
Если позволите, процитирую свой же текст, который я выкладывал на форуме Kaggle. Прошу прощения, что не перевожу обратно на русский (честно — лень).

On the last day of the competition I tried to build new variable based on startprice, because as I read in forum, there are reasons to modify it somehow. My idea was to compare startprice with an average price for a given item. The main problem is how to calculate this average price.

My ideas and steps:

1. For calculating average market prices for items we take into account ONLY non-biddable and sold items because biddable startprice could be much lower than actual sold price of that item. This is very important. If we knew actual sold prices for biddable items (and not only startprice), we could have included them also. And we cannot take unsold prices because they can be higher and don't represent actual market prices. Though we get only 220 (~12%) such items out of 1861 and this is rather small amount. Because of this fact it doesn't contain data for certain triplets (productline, condition, startprice).

2. Based on the previous table, we calculate 4 tables with average prices, using different aggregating criteria. I would use these tables on the stage 4 (see below).

3. Merge all tables in one table.

4. Main logic:

  • 4.1. If we have avg price for certain (condition, productline, storage) triplet for a given item, use this value as average price. This case accounts for 81% of all items in eBayTest dataset.
  • 4.2. Else if we have avg price for certain (condition, productline) pair for a given item, use this value as average price. This case accounts for additional 13% of all items in eBayTest dataset.
  • 4.3. Else use special calculated price based on item productline and condition separately. The trick here is to calculate avg prices for productlines only for condition==«Used» items, because it is the only condition which is present for all 10 productlines in the table with prices (remember that I got only 220 items out of 1861 for this table). And then we can adjust this average price with a coefficient for the item based on its actual condition. It is less precise scenario than two previous ones, but it is more preferable than just avg productline price without condition adjustment. This case accounts for 5% of all items in eBayTest dataset.
  • 4.4. Then calculate norm_price as ratio between start_price and this average price (obtained in previous 4.1-4.3 steps). So norm_price=start_price/average_price.


5. Use randomForest(sold ~ norm_price + biddable + productline+condition+storage, data=...)

This algorithm increases AUC on my train data by 0.02-0.03 points in average (i.e. for example, from 0.87 to 0.89) in comparison with RF with startprice instead of norm_price. It is interesting that in private LB it got 0.863 and in public LB it got only 0.833. But I suppose it's not just a fluke, because it proves better quality both on train data and private LB in my case.

This norm_price is very well interpretable:

  • norm_price>1 means that an item price is higher than average for items of this group (productline, condition, storage)
  • norm_price=1 means that the price is just as average
  • norm_price<1 means that the price is below average (for example, 0.5 means that item if 2 times cheaper that average price for this item)

As I suppose I could had tried to use some additional function with norm_price to even more increase AUC, but I had no more submission attempts and spare time. Also I suppose that even raw norm_price works quite well at least for random forests.

I've tried to work with text corpus, but I was confused because importance of terms from description was not very high in comparison to other variables. In addition there were certain technical problems with stemming of the text corpus.

I noticed that it can be promising to mix log regression and RF in one ensemble. You just check AUC and accuracy of mix model with different weights for LogReg and RF models. I tried (0.0;1.0), (0.1;0.9),....(0.9;0.1), (1.0;0.0). And then you choose weights with the highest AUC/accuracy, for example, 0.9*RF+0.1*LogReg. This is simple idea, though I haven't used it for the final submission and used only pure RF just to make my model simple.


Script
eBayTrain = read.csv(«eBayiPadTrain.csv», stringsAsFactors=FALSE)
eBayTest = read.csv(«eBayiPadTest.csv», stringsAsFactors=FALSE)

# correct several items productline — it seems that certain productlines corresponds to the same product
# see Wikipedia for details of Apple products
eBayTrain[which(eBayTrain$productline==«iPad 5»),«productline»]=«iPad Air»
eBayTrain[which(eBayTrain$productline==«iPad mini Retina»),«productline»]=«iPad mini 2»

# For calculating average market prices for items we take into account ONLY non-biddable
# and sold items because biddable startprice could be much lower than actual sold price
# of that item. This is very important. And we cannot take unsold prices because they
# can be higher and doesn't represent actual market prices.
eBayTrain_new=eBayTrain[eBayTrain$biddable==0 & eBayTrain$sold==1,]

# Based on the previous table, we calculate 4 tables with averages.
# Note that ak_productline_used contains average price for productlines only for condition==«Used»
# — this is because it is the only condition which is present in eBayTrain for every productline
ak_cps=aggregate(startprice~storage+condition+productline, data=eBayTrain_new, mean, na.rm=TRUE)
ak_cp=aggregate(startprice~productline+condition, data=eBayTrain_new, mean, na.rm=TRUE)
ak_condition=aggregate(startprice~condition, data=eBayTrain_new, mean, na.rm=TRUE)
ak_productline_used=aggregate(startprice~productline, data=eBayTrain_new[eBayTrain_new$condition==«Used»,], mean, na.rm=TRUE)
colnames(ak_cps)[4]=«avg_price_cps»
colnames(ak_cp)[3]=«avg_price_cp»
colnames(ak_productline_used)[2]=«avg_price_p_used»

# per_used_price is the avg prices ratio between given condition and «Used» condition
ak_condition$per_used_price=ak_condition$startprice/ak_condition[ak_condition$condition==«Used»,«startprice»]
ak_condition$startprice=NULL

eBayTest$sold=0
eBayAll=rbind(eBayTrain,eBayTest)
df3 < — merge(eBayAll, ak_cps, by=c(«productline»,«condition»,«storage»), all.x = TRUE)

df3 < — merge(df3, ak_cp, by=c(«productline»,«condition»), all.x = TRUE)
df3 < — merge(df3, ak_productline_used, by=c(«productline»), all.x = TRUE)
df3 < — merge(df3, ak_condition, by=c(«condition»), all.x = TRUE)

# calc_price = avg price for a given productline (only condition==«Used» included)
# from table ak_productline_used multiplied by the ratio from ak_condition table between
# given condition and «Used» condition

df3$calc_price=df3$avg_price_p*df3$per_used_price

#nrow(test[is.na(test$avg_price_cps)==FALSE,])/nrow(test)
#[1] 0.8132832
#(nrow(test[is.na(test$avg_price_cp)==FALSE,])-nrow(test[is.na(test$avg_price_cps)==FALSE,]))/nrow(test)
#[1] 0.1340852
#nrow(test[is.na(test$avg_price_cp)==TRUE,])/nrow(test)
#[1] 0.05263158

#main logic:
# 1. If ak_cps contains data for certain (condition, productline, storage) triplet
# for a given item, use this value as average price.
# 2. Else if ak_cp contains data for certain (condition, productline) pair
# for a given item, use this value as average price.
# 3. Else use special calculated price baced on item productline and condition separately.
# It is the less precise scenario than two previous ones, but it is more preferable than
# just avg productline price without condition adjustment.
# 4. Then calculate norm_price as ratio between start_price and this average price
# (obtained from previous 1-3 steps)

df3$avg_price_cp1=ifelse(is.na(df3$avg_price_cps)==TRUE,df3$avg_price_cp,df3$avg_price_cps)
df3$avg_price_cp2=ifelse(is.na(df3$avg_price_cp1)==TRUE,df3$calc_price,df3$avg_price_cp1)
df3$norm_price=(df3$startprice/df3$avg_price_cp2)

df3$sold=as.factor(df3$sold)
df3$biddable=as.factor(df3$biddable)
df3$productline=as.factor(df3$productline)
df3$storage=as.factor(df3$storage)
df3$condition=as.factor(df3$condition)
df3$carrier=as.factor(df3$carrier)
df3$cellular=as.factor(df3$cellular)
df3$color=as.factor(df3$color)

df3=df3[order(df3$UniqueID),]

train=head(df3, nrow(eBayTrain))
test=tail(df3, nrow(eBayTest))
test$sold=NULL

library(caTools)
set.seed(3000)
spl = sample.split(train$sold, SplitRatio = 0.7)
train0 = subset(train, spl==TRUE)
test0 = subset(train, spl==FALSE)

# we can use norm_price directly or try to calculate some formula, for example
# log(norm_price+1). But at least for random forest just norm_price is enough as I suppose

#LogMod2=glm(sold ~ norm_price + biddable + productline+condition,data=train0,family=binomial)
#PredLog2=predict(LogMod2,newdata = test0,type=«response»)

library(randomForest)
RFMod2=randomForest(sold ~ norm_price + biddable + productline+condition+storage,data=train0)
PredRF2=predict(RFMod2,newdata = test0,type=«prob»)
#table(test0$sold,PredRF2[,2]>=0.5)
#library(ROCR)
#pred=prediction(PredRF2[,2],test0$sold)
#performance(pred,«auc»)@y.values

#MySubmission = data.frame(UniqueID = eBayTest$UniqueID, Probability1 = PredRF2[,2])
#write.csv(MySubmission, «SubmissionRF_norm_price4.csv», row.names=FALSE)

Кагл штука замечательная. Я из него вынес море знаний о машинном обучении, различных библиотекахи и алгоритмах.

Проблема в том, что твои результаты на нём не котируются при приёме на работу. Среди тех, кто работает в Data Science, но кагл видел только на картинке, а сам ни одного сабмишена никогда не сделал(а таких 100% среди тех, с кем у меня было интервью), бытует мнение, что кагл — это набор искуственных данных, работа над которыми никак не связана с REAL Data Science. Я согласен, что у кагла своя специфика, и многие допущения, которые позволяют важать из данных всё, что можно, чтобы получить заоблачную точность, на практике не будут применимы, например в силу того, что решение должно быть масштабируемо. Но тем не менее, feature engineering, feature extraction точно такой же. Ньансы работы с различными метриками, проблемы cross validation, всё ровно то же.

Хотя, скорее всего, я что-то делаю неправильно. Народ, кто разибрается, как мне кагловские результаты правильно вставить в резюме? https://www.kaggle.com/iglovikov
Извините, а в чем проблема вставить просто ссылку, именно так, как вы ее вставили в ваше сообщение выше? По-моему, достаточно информативно и при этом максимально лаконично. Кому надо — тот перейдет.
Дополнение: я бы еще написал «Top 5% in several competitions» перед или после ссылки, чтобы было заметно при беглом просмотре.
Посмотрите как это делают другие. Найти легко, открывайте профили из топ 100, находите ссылку на linkedin и там есть примеры.
Ну и статус мастера не помешает для резюме взять.
Недавно видел вакансии DataRobot, там даже на удалёнку людей искали, в требованиях было наличие master :).

Мастера, наверно, взять можно, но непросто. Вот так сходу, прищурившись посмотреть на данные и сделать победный(ну или хотя бы топ 10) сабмишн у меня знаний/навыка не хватает. Для того, чтобы вылезти в топ10 долгим и кропотливым трудом, нужно время: чтение статей и книг на тему, анализ решений из похожих по духу/метрике соревнований, просмотр лекций на edX, Coursera, Youtube, да и просто написание кода и проверка хороших и не очень идей на cross validation — всё это требует уйму времни. Всё это можно сильно ускорить, если есть команда единомышленников, которые тоже работают над этой задачей и вы обмениваетесь идеями о том, что работает, но, что чаще, о том, что не работает. Например, на кагле в топе много ребят с МГУ, и выступают они замечательно. Есть у меня подозрение, что они на тему кагла хоть иногда, но общаются. Я поначалу пытался аспирантов со своего факультета в UC Davis, Physics раскрутить на командную работу — но толком не пошло, система обучения в аспирантуре в США построена так, что ты должен, не разгибаясь, как негр на плантации двигать науку в какую-нибудь сторону 24 часа в сутки, плюс за время обучения ты привыкаешь, что статья в резенцизруемом журнале — это достижение всей твоей жизни (по сути, чуть ни единственный критерий оценки тебя в качестве двигателя науки), поэтому все пытаются что-нибудь опубликовать, и это тоже не добавляет мотивации найти время на кагл. (Публикуются они тоже плохо. В силу того, что с данными в физике работают крайне неэффективно. Вот тут бы опыт кагла им сильно помог, как минимум с точки зрения exploratory analysis.)

Поэтому, после неудачной попытки командной работы, я сам ковырялся с совревнованиями, параллельно дописывая диссертацию, а потом пытась найти работу.

В SF Bay Area, где я сейчас обитаю, море очень серьёзных людей и в машинном обучении, и в частоности в кагле, но, чтобы с ними познакомится, нужно время, а время в данный момент уходит на прорешивание задач с Glassdoor и HackerRank, которые могут попасться на интервью.

В общем Kaggle Master помог бы найти работу, но чтобы его получить, мне сначала нужно найти работу, чтобы хоть какое-то свободное время появилось. Кстати, вопрос денег тоже как-то, но стоит. Многие задачи мой лаптоп не потянет, а AWS стоит хоть и небольших, но денег.

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

Если вас кто-то нибудь посоветует небольшой компании, то, как минимум, с вас не будут спрашивать задачки с HackerRank.

Ну а с kaggle, по моему мнению, всё намного проще. Просто нужно играть по его правилам. С мастером мне повезло, я получил его в первом соревновании, над которым решил серьёзно поработать, а не просто пару часов посидеть, как в предыдущих. Это было соревнование по трудоустройство от facebook и я занял там 7е место. Над задачей посидел порядка 40 часов, т.е. полную рабочую неделю.

С тех пор у меня сложилось мнение, что новички на kaggle платят только своим временем. Любой новичок с достаточными навыками программирования может финишировать в топ 100, если выберет хорошее соревнование и потратит нужное количество времени.

Я не помню соревнований, где бы весь топ 10 состоял из очень сложных решений, в основном сложные решения только у призёров. Исключением являются задачи, которые связаны с deep learning. Поэтому, чтобы попасть в топ, не обязательно знать кучу теории и иметь гору опыта. Главное полностью понимать поставленную задачу. Ведь в нормальных соревнованиях, где все поля описаны и не зашифрованы, около 80% от победы зависит от того, какие фичи смогли придумать участники.

Поэтому знаний и опыта у вас точно хватает, нужно просто немного усердия и везения :).

По поводу МГУ и общения, в Москве постоянно митапы проходят, где прошедшие задачки разбирают.
По поводу МГУ и общения, в Москве постоянно митапы проходят, где прошедшие задачки разбирают.

А что это за встречи, в каком формате они проходят (можно ли прийти со стороны)?
Мне разные форматы попадались, я сам не был там.
Если хотите быть в курсе таких мероприятий, то подпишитесь на русскоязычные группы о DS в соц. сетях.
модель с результатом 0.84724 заняла 211 место из 1884

Какая точность у модели, занявшей 1е место?
0.88308 — это Private LB. Или вы имеете в виду Public LB?
См. здесь
Кстати, там в форуме некоторые участники, занявшие высокие места (в т.ч. и первое) описывают свой подход к решению задачи. Есть и скрипты.
Sign up to leave a comment.