Статьи

Энтузиаст R становится питоном!

Я провел так много лет, используя и передавая свою любовь к R и используя Python минимально. Недавно прочитав о машинном обучении в Python, я решил заняться забавным небольшим ML-проектом, используя Python от начала до конца.

Ниже следует использование аккуратного набора данных из хранилища машинного обучения UCI . Данные содержат результаты теста по математике 649 учеников в 2 португальских школах. Что хорошо в этом наборе данных, так это то, что в дополнение к оценкам учащихся по 3 математике, им также удалось собрать целый набор демографических переменных (и некоторых поведенческих). Это привело меня к вопросу о том, насколько хорошо вы можете предсказать итоговый результат теста по математике, основываясь только на демографии и поведении. Другими словами, кто, вероятно, преуспеет, и кто, вероятно, танк?

Я должен признать, прежде чем я продолжу, я изначально намеревался провести этот анализ только на Python, но на самом деле я чувствовал себя потерянным на три четверти пути и просто выполнил всю проклятую вещь в R. Как только я закончил анализ в R к своему После этого я вернулся к анализу Python и продолжал, пока не добился своего разумного удовлетворения. По этой причине на каждом этапе анализа я покажу вам код, который я использовал в Python, результаты, а затем то же самое в R. Не рассматривайте это как сравнение возможностей машинного обучения Python против R как такового. Пожалуйста, воспринимайте это как сравнение моего понимания того, как проводить машинное обучение в Python против R!

Без дальнейших церемоний, давайте начнем с некоторых операторов импорта в Python и операторов библиотеки в R:

#Python Code
from pandas import *
from matplotlib import *
import seaborn as sns
sns.set_style("darkgrid")
import matplotlib.pyplot as plt
%matplotlib inline # I did this in ipython notebook, this makes the graphs show up inline in the notebook.
import statsmodels.formula.api as smf
from scipy import stats
from numpy.random import uniform
from numpy import arange
from sklearn.ensemble import RandomForestRegressor
from sklearn.metrics import mean_squared_error
from math import sqrt
mat_perf = read_csv('/home/inkhorn/Student Performance/student-mat.csv', delimiter=';')

Я хотел бы прокомментировать количество операторов импорта, которые я написал в этом скрипте Python. Одиннадцать !! Это даже нормально? Обратите внимание на меньшее количество операторов библиотеки в моем блоке кода R ниже:

#R Code
library(ggplot2)
library(dplyr)
library(ggthemr)
library(caret)
ggthemr('flat') # I love ggthemr!
mat_perf = read.csv('student-mat.csv', sep = ';')

Теперь давайте сделаем краткий график нашей целевой переменной — баллов по итоговому тесту по математике для студентов под названием «G3».

#Python Code
sns.set_palette("deep", desat=.6)
sns.set_context(context='poster', font_scale=1)
sns.set_context(rc={"figure.figsize": (8, 4)})
plt.hist(mat_perf.G3)
plt.xticks(range(0,22,2))

Распределение итоговых тестов по математике («G3»)
История Python - G3

Это выглядит довольно приятно для моих глаз. Теперь давайте посмотрим код для того же самого в R (я знаю, визуальная тема отличается. Так что подайте в суд на меня!)

#R Code
ggplot(mat_perf) + geom_histogram(aes(x=G3), binwidth=2)

Hist - G3

Вы заметите, что мне не нужно настраивать параметры палитры или размера шрифта для графика R, потому что я использовал очень забавный пакет ggthemr . Вы выбираете нужную визуальную тему, объявляете ее на ранней стадии, и тогда все последующие сюжеты будут иметь одинаковую тему! Однако я скрыл команду, изменяющую высоту и ширину фигуры. Я установил размер рисунка с помощью rmarkdown, иначе я бы просто изменил размер вручную, используя меню экспорта в рамке графика в RStudio. Я думаю, что оба графика выглядят довольно хорошо, хотя я очень неравнодушен к работе с ggthemr!

Однофакторные оценки значения переменных для выбора признаков

Ниже, что я сделал на обоих языках, чтобы циклически перебирал каждую переменную в наборе данных (исключая предыдущие результаты тестов), вставлял имя переменной в словарь / список и получал меру важности того, насколько прогнозирующей является эта переменная, отдельно, итоговой оценки по математике (переменная G3). Конечно, если переменная является качественной, тогда я получаю F балл от ANOVA, и если она количественная, то я получаю балл по регрессии.

В случае Python это достигается в обоих случаях с помощью функции ols из пакета statsmodels scipy. В случае R я добился этого, используя функцию aov для качественной и функцию lm для количественных переменных. Численный результат, как вы увидите из графиков, тот же.

#Python Code
test_stats = {'variable': [], 'test_type' : [], 'test_value' : []}

for col in mat_perf.columns[:-3]:
    test_stats['variable'].append(col)
    if mat_perf[col].dtype == 'O':
        # Do ANOVA
        aov = smf.ols(formula='G3 ~ C(' + col + ')', data=mat_perf, missing='drop').fit()
        test_stats['test_type'].append('F Test')
        test_stats['test_value'].append(round(aov.fvalue,2))
    else:
        # Do correlation
        print col + '\n'
        model = smf.ols(formula='G3 ~ ' + col, data=mat_perf, missing='drop').fit()
        value = round(model.tvalues[1],2)
        test_stats['test_type'].append('t Test')
        test_stats['test_value'].append(value)

test_stats = DataFrame(test_stats)
test_stats.sort(columns='test_value', ascending=False, inplace=True)
#R Code
test.stats = list(test.type = c(), test.value = c(), variable = c())

for (i in 1:30) {
  test.stats$variable[i] = names(mat_perf)[i]
  if (is.factor(mat_perf[,i])) {
    anova = summary(aov(G3 ~ mat_perf[,i], data=mat_perf))
    test.stats$test.type[i] = "F test"
    test.stats$test.value[i] = unlist(anova)[7]
  }
  else {
    reg = summary(lm(G3 ~ mat_perf[,i], data=mat_perf))
    test.stats$test.type[i] = "t test"
    test.stats$test.value[i] = reg$coefficients[2,3]
  }

}

test.stats.df = arrange(data.frame(test.stats), desc(test.value))
test.stats.df$variable = reorder(test.stats.df$variable, -test.stats.df$test.value)

А теперь для графиков. Опять же, вы увидите немного больше кода для графа Python против графа R. Возможно, кто-то сможет показать мне код, который не содержит столько строк, или, может быть, именно так обстоят дела с графикой в ​​Python. Не стесняйтесь обучать меня 🙂

#Python Code
f, (ax1, ax2) = plt.subplots(2,1, figsize=(48,18), sharex=False)
sns.set_context(context='poster', font_scale=1)
sns.barplot(x='variable', y='test_value', data=test_stats.query("test_type == 'F Test'"), hline=.1, ax=ax1, x_order=[x for x in test_stats.query("test_type == 'F Test'")['variable']])
ax1.set_ylabel('F Values')
ax1.set_xlabel('')

sns.barplot(x='variable', y='test_value', data=test_stats.query("test_type == 't Test'"), hline=.1, ax=ax2, x_order=[x for x in test_stats.query("test_type == 't Test'")['variable']])
ax2.set_ylabel('t Values')
ax2.set_xlabel('')

sns.despine(bottom=True)
plt.tight_layout(h_pad=3)

Python Bar Plot - одномерные оценки значения переменных

#R Code
ggplot(test.stats.df, aes(x=variable, y=test.value)) +
  geom_bar(stat="identity") +
  facet_grid(.~test.type ,  scales="free", space = "free") +
  theme(axis.text.x = element_text(angle = 45, vjust=.75, size=11))

Гистограмма - одномерные оценки переменной значимости

Как видите, оценки, которые я генерировал на обоих языках, были, к счастью, одинаковыми. Моей следующей мыслью было использовать только те переменные с тестовым значением (F или t) 3,0 или выше. Ниже вы увидите, что это привело к довольно серьезному снижению прогнозирующей способности по сравнению с либеральным выбором функций.

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

Обучение первой модели случайного леса

#Python code
usevars =  [x for x in test_stats.query("test_value >= 3.0 | test_value <= -3.0")['variable']]
mat_perf['randu'] = np.array([uniform(0,1) for x in range(0,mat_perf.shape[0])])

mp_X = mat_perf[usevars]
mp_X_train = mp_X[mat_perf['randu'] <= .67]
mp_X_test = mp_X[mat_perf['randu'] > .67]

mp_Y_train = mat_perf.G3[mat_perf['randu'] <= .67]
mp_Y_test = mat_perf.G3[mat_perf['randu'] > .67]

# for the training set
cat_cols = [x for x in mp_X_train.columns if mp_X_train[x].dtype == "O"]
for col in cat_cols:
    new_cols = get_dummies(mp_X_train[col])
    new_cols.columns = col + '_' + new_cols.columns
    mp_X_train = concat([mp_X_train, new_cols], axis=1)

# for the testing set
cat_cols = [x for x in mp_X_test.columns if mp_X_test[x].dtype == "O"]
for col in cat_cols:
    new_cols = get_dummies(mp_X_test[col])
    new_cols.columns = col + '_' + new_cols.columns
    mp_X_test = concat([mp_X_test, new_cols], axis=1)

mp_X_train.drop(cat_cols, inplace=True, axis=1)
mp_X_test.drop(cat_cols, inplace=True, axis=1)

rf = RandomForestRegressor(bootstrap=True,
           criterion='mse', max_depth=2, max_features='auto',
           min_density=None, min_samples_leaf=1, min_samples_split=2,
           n_estimators=100, n_jobs=1, oob_score=True, random_state=None,
           verbose=0)
rf.fit(mp_X_train, mp_Y_train)

После того, как я прошел часть, где я сконструировал наборы для обучения и тестирования (с отфильтрованными «неважными» переменными), я столкнулся с настоящим раздражением. Я узнал, что категориальные переменные должны быть преобразованы в фиктивные переменные перед выполнением моделирования (где каждый уровень категориальной переменной получает свою собственную переменную, содержащую 1 и 0. 1 означает, что уровень присутствовал в этой строке, а 0 означает, что уровень отсутствовал в этом ряду, так называемое «горячее кодирование»). Я полагаю, вы могли бы утверждать, что это требует меньше вычислительных затрат на процедуры моделирования, но когда вы работаете с древовидными ансамблями, я думаю, что это недостаток. Допустим, у вас есть категориальная переменная с 5 функциями, от «а» до «е». Так уж сложилось, что когда вы сравниваете разбивку по той категориальной переменной, где «abc» находится с одной стороны, а «de» — с другой, в зависимой переменной есть очень существенная разница. Как одна горячая кодировка собирается захватить это? И затем, ваш набор данных, который имел определенное количество столбцов, теперь имеет 5 дополнительных столбцов из-за кодировки. «Бла», говорю я!

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

#R Code
keep.vars = match(filter(test.stats.df, abs(test.value) >= 3)$variable, names(mat_perf))
ctrl = trainControl(method="repeatedcv", number=10, selectionFunction = "oneSE")
mat_perf$randu = runif(395)
test = mat_perf[mat_perf$randu > .67,]
trf = train(mat_perf[mat_perf$randu <= .67,keep.vars], mat_perf$G3[mat_perf$randu <= .67],
            method="rf", metric="RMSE", data=mat_perf,
            trControl=ctrl, importance=TRUE)

Подожди минуту. Действительно ли я просто обучил модель случайного леса на R, провел перекрестную проверку и подготовил набор данных для тестирования с помощью 5 команд!?!? Это было намного проще, чем делать эти приготовления и не выполнять перекрестную проверку в Python! Я действительно пытался выяснить перекрестную проверку в sklearn, но потом у меня возникли проблемы с доступом к переменным значениям. Мне очень нравится пакет caret 🙂 Далее, давайте посмотрим, как каждая из моделей делала это на своем тестовом наборе:

Тестирование первой модели случайного леса

#Python Code
y_pred = rf.predict(mp_X_test)
sns.set_context(context='poster', font_scale=1)
first_test = DataFrame({"pred.G3.keepvars" : y_pred, "G3" : mp_Y_test})
sns.lmplot("G3", "pred.G3.keepvars", first_test, size=7, aspect=1.5)
print 'r squared value of', stats.pearsonr(mp_Y_test, y_pred)[0]**2
print 'RMSE of', sqrt(mean_squared_error(mp_Y_test, y_pred))

Диаграмма разброса Python - первая модель пред против фактической

R ^ 2 значение 0,104940038879
RMSE 4,66552400292

Здесь, как и во всех случаях при прогнозировании с использованием sklearn, я использую метод прогнозирования для генерации прогнозируемых значений из модели с использованием набора тестирования, а затем строю прогноз («pred.G3.keepvars») против фактических значений (« G3 ″) с помощью функции lmplot. Мне нравится синтаксис, который использует функция lmplot из пакета seaborn, поскольку он прост и знаком мне из мира R (где аргументы состоят из «переменной X, переменной Y, имени набора данных, других эстетических аргументов). Как видно из приведенного выше графика и значения R ^ 2, эта модель отстой. Еще одна вещь, которая мне здесь нравится — это качество графика, который выводит Seaborn. Мило! Это выглядит довольно современно, текст очень читабелен, и ничто не выглядит резким или неровным в сюжете. Хорошо, теперь давайте посмотрим на код и вывод в R, используя те же предикторы.

#R Code
test$pred.G3.keepvars = predict(trf, test, "raw")
cor.test(test$G3, test$pred.G3.keepvars)$estimate[[1]]^2
summary(lm(test$G3 ~ test$pred.G3.keepvars))$sigma
ggplot(test, aes(x=G3, y=pred.G3.keepvars)) + geom_point() + stat_smooth(method="lm") + scale_y_continuous(breaks=seq(0,20,4), limits=c(0,20))

Scatter Plot - Первая модель против фактической

R ^ 2 значение 0,198648
RMSE 4,148194

Ну, похоже, эта модель отстой немного меньше, чем модель Python. С точки зрения качества сюжет выглядит очень красиво (спасибо еще раз, ggplot2 и ggthemr!), Хотя по умолчанию параметр альфа не установлен для учета переполнения. Страница документации для ggplot2 предлагает установить альфа = .05, но для этого конкретного набора данных лучше установить его на .5.

Наконец, в этом разделе давайте рассмотрим значения переменных, сгенерированные для каждой модели обучения:

#Python Code
importances = DataFrame({'cols':mp_X_train.columns, 'imps':rf.feature_importances_})
print importances.sort(['imps'], ascending=False)

             cols      imps
3        failures  0.641898
0            Medu  0.064586
10          sex_F  0.043548
19  Mjob_services  0.038347
11          sex_M  0.036798
16   Mjob_at_home  0.036609
2             age  0.032722
1            Fedu  0.029266
15   internet_yes  0.016545
6     romantic_no  0.013024
7    romantic_yes  0.011134
5      higher_yes  0.010598
14    internet_no  0.007603
4       higher_no  0.007431
12        paid_no  0.002508
20   Mjob_teacher  0.002476
13       paid_yes  0.002006
18     Mjob_other  0.001654
17    Mjob_health  0.000515
8       address_R  0.000403
9       address_U  0.000330
#R Code
varImp(trf)

## rf variable importance
## 
##          Overall
## failures 100.000
## romantic  49.247
## higher    27.066
## age       17.799
## Medu      14.941
## internet  12.655
## sex        8.012
## Fedu       7.536
## Mjob       5.883
## paid       1.563
## address    0.000

Мое первое наблюдение заключается в том, что мне было намного проще получить значения переменных в R, чем в Python. Далее вы наверняка увидите симптом фиктивного кодирования, который я должен был сделать для категориальных переменных. Это не весело, но мы будем переживать этот пример анализа, верно? Теперь давайте посмотрим, какие переменные оказались на вершине:

Принимая во внимание, что ошибки, уровень образования матери, пол и работа матери заняли верхнюю строчку списка для модели Python, первые 4 отличались от неудач в модели R.

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

Обучение и тестирование второй модели случайного леса

#Python Code

#aav = almost all variables
mp_X_aav = mat_perf[mat_perf.columns[0:30]]
mp_X_train_aav = mp_X_aav[mat_perf['randu'] <= .67]
mp_X_test_aav = mp_X_aav[mat_perf['randu'] > .67]

# for the training set
cat_cols = [x for x in mp_X_train_aav.columns if mp_X_train_aav[x].dtype == "O"]
for col in cat_cols:
    new_cols = get_dummies(mp_X_train_aav[col])
    new_cols.columns = col + '_' + new_cols.columns
    mp_X_train_aav = concat([mp_X_train_aav, new_cols], axis=1)

# for the testing set
cat_cols = [x for x in mp_X_test_aav.columns if mp_X_test_aav[x].dtype == "O"]
for col in cat_cols:
    new_cols = get_dummies(mp_X_test_aav[col])
    new_cols.columns = col + '_' + new_cols.columns
    mp_X_test_aav = concat([mp_X_test_aav, new_cols], axis=1)

mp_X_train_aav.drop(cat_cols, inplace=True, axis=1)
mp_X_test_aav.drop(cat_cols, inplace=True, axis=1)

rf_aav = RandomForestRegressor(bootstrap=True, 
           criterion='mse', max_depth=2, max_features='auto',
           min_density=None, min_samples_leaf=1, min_samples_split=2,
           n_estimators=100, n_jobs=1, oob_score=True, random_state=None,
           verbose=0)
rf_aav.fit(mp_X_train_aav, mp_Y_train)

y_pred_aav = rf_aav.predict(mp_X_test_aav)
second_test = DataFrame({"pred.G3.almostallvars" : y_pred_aav, "G3" : mp_Y_test})
sns.lmplot("G3", "pred.G3.almostallvars", second_test, size=7, aspect=1.5)
print 'r squared value of', stats.pearsonr(mp_Y_test, y_pred_aav)[0]**2
print 'RMSE of', sqrt(mean_squared_error(mp_Y_test, y_pred_aav))

График разброса Python - вторая модель пред против фактической

R ^ 2 значение 0,226587731888
RMSE 4,3338674965

По сравнению с первой моделью Python R ^ 2 на этой модели более чем вдвое выше (первый R ^ 2 был .10494), а RMSE ниже на 7,1% (первая была 4.6666254). Предсказанный против фактического графика подтверждает, что прогнозы все еще не выглядят фантастическими по сравнению с фактическими данными, что, вероятно, является основной причиной, по которой среднеквадратическое среднеквадратичное отклонение не уменьшилось так сильно. Теперь к коду R используем те же предикторы:

#R code
trf2 = train(mat_perf[mat_perf$randu <= .67,1:30], mat_perf$G3[mat_perf$randu <= .67],
            method="rf", metric="RMSE", data=mat_perf,
            trControl=ctrl, importance=TRUE)
test$pred.g3.almostallvars = predict(trf2, test, "raw")
cor.test(test$G3, test$pred.g3.almostallvars)$estimate[[1]]^2
summary(lm(test$G3 ~ test$pred.g3.almostallvars))$sigma
ggplot(test, aes(x=G3, y=pred.g3.almostallvars)) + geom_point() + 
  stat_smooth() + scale_y_continuous(breaks=seq(0,20,4), limits=c(0,20))

Scatter Plot - Вторая модель Пред против фактической

R ^ 2 значение 0,3262093
СКО 3,8037318

По сравнению с первой моделью R, R ^ 2 на этой примерно в 1,64 раза выше (первый R ^ 2 был .19865), а RMSE ниже на 8,3% (первая была 4,148194). Хотя эта конкретная модель действительно лучше предсказывает значения в наборе тестов, чем та, которая построена в Python с использованием тех же переменных, я все же не решусь предположить, что этот процесс по своей природе лучше для этого набора данных. Из-за случайности, присущей случайным лесам, один прогон обучения может быть достаточно удачным для получения результатов, подобных описанным выше, тогда как в других случаях результаты могут быть даже несколько хуже, чем то, что мне удалось получить в Python. Я подтвердил это, и на самом деле большинство дополнительных прогонов этой модели в R, по-видимому, приводили к R ^ 2 около 0,20 и RMSE около 4,2.

Опять же, давайте посмотрим на переменные значения, генерируемые для каждой модели обучения:

#Python Code
importances_aav = DataFrame({'cols':mp_X_train_aav.columns, 'imps':rf_aav.feature_importances_})
print importances_aav.sort(['imps'], ascending=False)

                 cols      imps
5            failures  0.629985
12           absences  0.057430
1                Medu  0.037081
41      schoolsup_yes  0.036830
0                 age  0.029672
23       Mjob_at_home  0.029642
16              sex_M  0.026949
15              sex_F  0.026052
40       schoolsup_no  0.019097
26      Mjob_services  0.016354
55       romantic_yes  0.014043
51         higher_yes  0.012367
2                Fedu  0.011016
39     guardian_other  0.010715
37    guardian_father  0.006785
8               goout  0.006040
11             health  0.005051
54        romantic_no  0.004113
7            freetime  0.003702
3          traveltime  0.003341
#R Code
varImp(trf2)

## rf variable importance
## 
##   only 20 most important variables shown (out of 30)
## 
##            Overall
## absences    100.00
## failures     70.49
## schoolsup    47.01
## romantic     32.20
## Pstatus      27.39
## goout        26.32
## higher       25.76
## reason       24.02
## guardian     22.32
## address      21.88
## Fedu         20.38
## school       20.07
## traveltime   20.02
## studytime    18.73
## health       18.21
## Mjob         17.29
## paid         15.67
## Dalc         14.93
## activities   13.67
## freetime     12.11

Теперь в обоих случаях мы видим, что пропуски и неудачи считаются двумя наиболее важными переменными для прогнозирования итоговой оценки по математике. Это имеет смысл для меня, но, откровенно говоря, немного грустно, что две наиболее важные переменные настолько отрицательны 🙁 Перейдем к третьей модели случайного леса, содержащей все, начиная со второй, с добавлением оценок студентов на втором экзамене по математике. !

Обучение и тестирование третьей модели случайного леса

#Python Code

#allv = all variables (except G1)
allvars = range(0,30)
allvars.append(31)
mp_X_allv = mat_perf[mat_perf.columns[allvars]]
mp_X_train_allv = mp_X_allv[mat_perf['randu'] <= .67]
mp_X_test_allv = mp_X_allv[mat_perf['randu'] > .67]

# for the training set
cat_cols = [x for x in mp_X_train_allv.columns if mp_X_train_allv[x].dtype == "O"]
for col in cat_cols:
    new_cols = get_dummies(mp_X_train_allv[col])
    new_cols.columns = col + '_' + new_cols.columns
    mp_X_train_allv = concat([mp_X_train_allv, new_cols], axis=1)

# for the testing set
cat_cols = [x for x in mp_X_test_allv.columns if mp_X_test_allv[x].dtype == "O"]
for col in cat_cols:
    new_cols = get_dummies(mp_X_test_allv[col])
    new_cols.columns = col + '_' + new_cols.columns
    mp_X_test_allv = concat([mp_X_test_allv, new_cols], axis=1)

mp_X_train_allv.drop(cat_cols, inplace=True, axis=1)
mp_X_test_allv.drop(cat_cols, inplace=True, axis=1)

rf_allv = RandomForestRegressor(bootstrap=True, 
           criterion='mse', max_depth=2, max_features='auto',
           min_density=None, min_samples_leaf=1, min_samples_split=2,
           n_estimators=100, n_jobs=1, oob_score=True, random_state=None,
           verbose=0)
rf_allv.fit(mp_X_train_allv, mp_Y_train)

y_pred_allv = rf_allv.predict(mp_X_test_allv)
third_test = DataFrame({"pred.G3.plusG2" : y_pred_allv, "G3" : mp_Y_test})
sns.lmplot("G3", "pred.G3.plusG2", third_test, size=7, aspect=1.5)
print 'r squared value of', stats.pearsonr(mp_Y_test, y_pred_allv)[0]**2
print 'RMSE of', sqrt(mean_squared_error(mp_Y_test, y_pred_allv))

График разброса Python - Третья модель Пред против фактической

R ^ 2 значение 0,836089929903
RMSE 2,11895794845

Очевидно, что мы добавили весьма прогнозируемую информацию здесь, добавив оценки от их второго экзамена по математике (имя переменной было «G2»). Сначала я неохотно добавлял эту переменную, потому что, когда вы прогнозируете тестовые отметки с помощью предыдущих тестовых отметок, тогда это препятствует тому, чтобы модель была полезной намного раньше в том году, когда эти тесты не проводились. Тем не менее, я действительно хотел посмотреть, как будет выглядеть модель, когда я ее все равно включу! Теперь давайте посмотрим, насколько прогнозирующими были эти переменные, когда я поместил их в модель в R:

#R Code
trf3 = train(mat_perf[mat_perf$randu <= .67,c(1:30,32)], mat_perf$G3[mat_perf$randu <= .67], 
             method="rf", metric="RMSE", data=mat_perf, 
             trControl=ctrl, importance=TRUE)
test$pred.g3.plusG2 = predict(trf3, test, "raw")
cor.test(test$G3, test$pred.g3.plusG2)$estimate[[1]]^2
summary(lm(test$G3 ~ test$pred.g3.plusG2))$sigma
ggplot(test, aes(x=G3, y=pred.g3.plusG2)) + geom_point() + 
  stat_smooth(method="lm") + scale_y_continuous(breaks=seq(0,20,4), limits=c(0,20))

Scatter Plot - Третья модель Пред против фактической

R ^ 2 значение 0,9170506
RMSE 1,3336087

Что ж, похоже, что снова у нас есть случай, когда модель R оказалась лучше, чем модель Python. Я нахожу примечательным, что когда вы смотрите на график рассеяния для модели Python, вы можете видеть, как выглядят шаги в точках, когда вы сканируете свои глаза от нижней левой части линии тренда до верхней правой части. Похоже, что модель случайного леса в R выиграла от процесса настройки, и в результате распределение остатков более гомоскедастично и, очевидно, ближе к линии регрессии, чем модель Python. Мне все еще интересно, насколько похожими были бы эти результаты, если бы я выполнил анализ Python, настроившись при перекрестной проверке, как в R!

В последний раз давайте рассмотрим значения переменных, сгенерированные для каждой модели обучения:

#Python Code
importances_allv = DataFrame({'cols':mp_X_train_allv.columns, 'imps':rf_allv.feature_importances_})
print importances_allv.sort(['imps'], ascending=False)

                 cols      imps
13                 G2  0.924166
12           absences  0.075834
14          school_GP  0.000000
25        Mjob_health  0.000000
24       Mjob_at_home  0.000000
23          Pstatus_T  0.000000
22          Pstatus_A  0.000000
21        famsize_LE3  0.000000
20        famsize_GT3  0.000000
19          address_U  0.000000
18          address_R  0.000000
17              sex_M  0.000000
16              sex_F  0.000000
15          school_MS  0.000000
56       romantic_yes  0.000000
27      Mjob_services  0.000000
11             health  0.000000
10               Walc  0.000000
9                Dalc  0.000000
8               goout  0.000000
#R Code
varImp(trf3)

## rf variable importance
## 
##   only 20 most important variables shown (out of 31)
## 
##            Overall
## G2         100.000
## absences    33.092
## failures     9.702
## age          8.467
## paid         7.591
## schoolsup    7.385
## Pstatus      6.604
## studytime    5.963
## famrel       5.719
## reason       5.630
## guardian     5.278
## Mjob         5.163
## school       4.905
## activities   4.532
## romantic     4.336
## famsup       4.335
## traveltime   4.173
## Medu         3.540
## Walc         3.278
## higher       3.246

Теперь это ОЧЕНЬ говорит, и дает мне представление о том, почему график рассеяния из модели Python имел такое качество лестницы. Модель R учитывает больше переменных, чем модель Python. Очевидно, что G2 выигрывает в обеих моделях, но я полагаю, что он настолько омрачил все остальное в модели Python, что по какой-то причине он просто не нашел применения для какой-либо другой переменной, кроме отсутствий.

Вывод

Это было весело! Для всей работы, которую я делал в Python, я использовал IPython Notebook. Будучи заядлым пользователем RStudio, я не привык к интерактивному кодированию на основе веб-браузера, как, например, в IPython Notebook. Я обнаружил, что мне это нравится, и нашел, что это полезно для размещения информации, которую я использовал для написания этого поста в блоге (по той же причине я также изложил R-часть этого анализа в RMarkdown). Что мне не понравилось в IPython Notebook, так это то, что когда вы закрываете его / закрываете / затем снова его инициализируете, все объекты, которые формируют ваши данные и анализ, исчезают, и все, что у вас осталось, — это результаты. Затем вы должны перезапустить весь ваш код, чтобы ваши объекты снова находились в памяти. Было бы неплохо иметь какую-то удобную функцию для сохранения всего на диск, чтобы вы могли перезагрузить его позже.

Я часто спотыкался, пытаясь выяснить, какие пакеты Python использовать для каждой конкретной цели, и я был легко разочарован. Я должен был постоянно напоминать себе, что это кривая обучения в той же степени, как и для меня, пока я учил R. Это разочарование не должно быть сдерживающим фактором для того, чтобы поднять его и научиться делать машинное обучение в Python. Другая часть моего разочарования была в том, что я не мог получить переменные значения из моих моделей Random Forest в Python, когда я строил их с использованием перекрестной проверки и поиска по сетке. Если у вас есть ссылка, чтобы поделиться со мной, которая показывает пример этого, я был бы рад прочитать ее.

Мне понравился seaborn, и я думаю, что если я потрачу на него больше времени, то, возможно, он послужит достойной альтернативой графике в ggplot2. При этом я потратил так много времени на использование ggplot2, что иногда мне интересно, есть ли что-нибудь, что может сравниться с его гибкостью и элегантностью!

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

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