Важность переменных в случайных лесах

Значимость переменных в алгоритме случайного леса

Традиционные методы и новые разработки

Особенности (распределительных) случайных лесов. В этой статье: Возможность производить важность переменных. Источник: Автор.

Случайные леса и их обобщения (в частности, Обобщенные случайные леса (GRF) и Распределительные случайные леса (DRF)) являются мощными и простыми в использовании методами машинного обучения, которые не должны отсутствовать в арсенале любого дата-сайентиста. Они не только проявляют устойчивую производительность на большом количестве наборов данных без необходимости настройки, но также легко обрабатывают пропущенные значения и даже предоставляют доверительные интервалы. В этой статье мы сосредоточимся на другой особенности, которую они могут предоставить: понятиях важности переменных. В частности, мы сосредоточимся на:

  1. Традиционном случайном лесе (RF), который используется для прогнозирования условного математического ожидания переменной Y при наличии p предикторов X.
  2. Распределительном случайном лесе, который используется для прогнозирования всей условной функции распределения d-мерной переменной Y при наличии p предикторов X.

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

(1) поиск небольшого количества переменных с максимальной точностью,

(2) обнаружение и ранжирование всех влиятельных переменных для дальнейшего исследования.

Разница между (1) и (2) важна, как только существует зависимость между элементами в X (что почти всегда). Например, если две переменные сильно коррелируют друг с другом и с Y, то один из двух входных параметров может быть убран без ущерба точности для цели (1), так как обе переменные передают одну и ту же информацию. Однако, обе переменные должны быть включены для цели (2), так как эти две переменные могут иметь различные значения на практике для специалистов отрасли.

Сегодня мы сосредоточимся на цели (1) и попытаемся найти меньшее количество предикторов, которые показывают примерно одну и ту же предсказательную точность. Например, в приведенном ниже примере с заработной платой, мы смогли сократить количество предикторов с 79 до примерно 20, при небольшом снижении точности. Эти наиболее важные предикторы содержат переменные, такие как возраст и образование, которые известно влияют на заработную плату. На сайте VoAGI также есть много отличных статей о (2), использующих значения Шепли, такие как эта или эта. Также существуют очень новые и захватывающие академические исследования о том, как эффективно вычислять значения Шепли с помощью случайного леса. Но это материал для второй статьи.

Два показателя, которые мы рассмотрим сегодня, являются более общими показателями важности переменных, которые могут использоваться для любого метода, основанные на принципе “удалить и обучить заново”, который мы рассмотрим ниже. Однако, здесь мы сосредоточимся исключительно на методах, основанных на деревьях. Более того, мы не вдаемся в подробное объяснение методов, а скорее пытаемся сосредоточиться на их применении и почему новые версии предпочтительнее традиционных.

Обзор показателей важности переменных для случайных лесов. Mean Decrease Impurity (MDI) и Mean Decrease Accuracy (MDA) были предложены Брейманом. Однако, из-за их эмпирической природы, остались некоторые проблемы, которые недавно решены в методе Sobol-MDA. Источник: Автор.

Начало

Показатели важности переменных для случайных лесов на самом деле так же стары, как и сами случайные леса. Первый показатель точности – среднее уменьшение точности (MDA) – был предложен Брейманом в его фундаментальной статье о случайных лесах [1]. Идея проста: для каждого измерения j=1,…,p сравнивают точность полного прогноза с точностью прогноза при случайной перестановке X_j. Идея состоит в том, чтобы нарушить связь между X_j и Y и сравнить точность, когда X_j не помогает предсказывать Y по умолчанию, с ситуацией, когда он потенциально может быть полезен.

Существуют различные версии MDA, реализованные на R и Python:

Различные версии MDA, реализованные в различных пакетах. Источник: Таблица 1 в [3]

К сожалению, перестановка переменной X_j таким образом не только ломает ее связь с Y, но и с другими переменными в X. Это не проблема, если X_j независим от всех остальных переменных, но становится проблемой, как только появляется зависимость. Следовательно, [3] показывает, что как только в X появляется зависимость, MDA сходится к чему-то бессмысленному. В частности, MDA может дать высокую важность переменной X_j, которая не важна для предсказания Y, но сильно коррелирует с другой переменной, скажем, X_l, которая фактически важна для предсказания Y (как показано в приведенном ниже примере). В то же время он может не смочь обнаружить действительно значимые переменные, как показано во множестве статей в [3, раздел 2.1]. Интуитивно понятно, что нам хотелось бы измерить производительность модели, если X_j не включается, и вместо этого мы измеряем производительность модели с переставленной переменной X_j.

Второй традиционный показатель точности – среднее уменьшение неопределенности (MDI), которое суммирует взвешенные уменьшения неопределенности по всем узлам, разделяющимся по данной переменной, усредненные по всем деревьям в лесу. К сожалению, MDI изначально плохо определен (не ясно, что он должен измерять), и несколько статей подчеркивают практическую проблему этого подхода (например, [5]). Поэтому мы не будем подробно останавливаться на MDI, так как MDA часто является предпочтительным выбором.

Современные разработки I: Sobol-MDA

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

Давайте разберемся. Во-первых, tau(X)=E[ Y | X] – это функция условного математического ожидания, которую мы хотим оценить. Она является случайной величиной, поскольку это функция случайной величины X. Теперь X^{(-j)} – это вектор p-1, в котором удаляется переменная j. Таким образом, ST^{(j)} – это уменьшение объясненной дисперсии выхода, если удалить j-ю выходную переменную.

Вышеописанное – более традиционный способ записи показателя. Однако, для меня написание:

намного более наглядно. Здесь d – это расстояние между двумя случайными векторами, а для ST^{(j)}, это расстояние является обычным евклидовым расстоянием. Таким образом, верхняя часть ST^{(j)} просто измеряет среднее квадратичное расстояние между желаемым значением (tau(X)) и значением, которое мы получаем без переменной j. Последнее представляет собой

Возникает вопрос, как эффективно оценить это. Оказывается, что достаточно просто оценить tau(X) с использованием RF, а затем удалить X_j и переобучить RF для получения оценки tau(X^{(-j)}), что приводит к последовательной оценке:

где tau_n(X_i) – оценка RF для тестовой точки X_i, используя все p предикторов, а также tau_n(X_i^{(-j)}) – переобученный лес, использующий только p-1 предикторов.

Однако это означает, что лес должен быть переобучен p раз, что не очень эффективно, когда p большое! Поэтому авторы в [3] разработали то, что они называют Sobol-MDA. Вместо переобучения леса каждый раз лес обучается только один раз. Затем тестовые точки проходят через этот же лес, и полученное предсказание “проецируется” для формирования меры в (1). То есть разделение на X_j просто игнорируется (помните, что целью является получение оценки без X_j). Авторы показали, что вычисление (1) с использованием этого принципа проекции также дает последовательную оценку! Это действительно красивая идея и делает алгоритм применимым даже в высоких размерностях.

Иллюстрация принципа проекции. Слева - разделение двумерного пространства при помощи RF. Справа - принцип проекции игнорирует разделение на X^(2), тем самым удаляя его при формировании предсказаний. Как видно, при помощи этого принципа точка X проецируется на X^{(-j)} справа. Источник: Рисунок 1 в [3]

Метод реализован на R в пакете soboldMDA, основанном на очень быстром пакете ranger.

Современные разработки II: чувствительный индекс на основе MMD

Если рассмотреть формулировку, использующую расстояние d, естественно вопроситься, можно ли использовать различные расстояния для получения мер важности переменных в более сложных проблемах. Один такой недавний пример – использование расстояния MMD в качестве d:

Расстояние MMD – замечательный инструмент, который позволяет довольно легко построить расстояние между распределениями, используя ядро k (например, гауссовское ядро):

На данный момент я оставлю подробности для будущих статей. Самое важное – просто то, что I^{(j)} рассматривает более общую цель, чем условное математическое ожидание. Он распознает переменную X_j как важную, как только она влияет на распределение Y хотя бы каким-то образом. Может быть, X_j изменяет только дисперсию или квантили, не затрагивая условное математическое ожидание Y (см. ниже пример). В этом случае Sobol-MDA не распознает X_j как важную, но это делает метод MMD. Однако это не обязательно делает его лучше, это просто другой инструмент: если вас интересует предсказание условного математического ожидания, ST^{(j)} – правильная мера. Однако, если вас интересует предсказание других аспектов распределения, особенно квантилей, лучше использовать I^{(j)}. Опять же, I^{(j)} можно последовательно оценить, используя принцип удаления и повторного обучения (переобучение DRF для j=1,…,p каждый раз с удаленной переменной $j$), или можно использовать тот же подход проекции, что и для Sobol-MDA. В конце этой статьи представлена реализация на основе принципа удаления и повторного обучения. Мы называем этот метод здесь MMD-MDA.

Симулированные данные

Теперь мы продемонстрируем эти два современных метода на простом примере симуляции: сначала мы загружаем и устанавливаем пакет Sobol-MDA из Gitlab, а затем загружаем все необходимые пакеты для этого примера:

library(kernlab)library(drf)library(Matrix)library(DescTools)library(mice)library(sobolMDA)source("compute_drf_vimp.R") ##Содержание этого файла можно найти нижеsource("evaluation.R") ##Содержание этого файла можно найти ниже

Затем мы симулируем на основе этого простого примера: мы берем X_1, X_2, X_4, …, X_10 независимо равномерно распределенными между (-1,1) и создаем зависимость между X_1 и X_3, принимая X_3=X_1 + случайная ошибка. Затем мы симулируем Y следующим образом:

##Симулируем данные, которые изменяются как среднее, так и стандартное отклонение# Симулируем от Xx1 <- runif(n,-1,1)x2 <- runif(n,-1,1)X0 <- matrix(runif(7*n,-1,1), nrow=n, ncol=7)x3 <- x1+ runif(n,-1,1)X <- cbind(x1,x2, x3, X0)# Симулируем зависимую переменную YY <- as.matrix(rnorm(n,mean = 0.8*(x1 > 0), sd = 1 + 1*(x2 > 0)))colnames(X)<-paste0("X", 1:10)head(cbind(Y,X))

Затем мы анализируем подход Sobol-MDA для оценки условного математического ожидания Y при заданном X:

##Учет важности переменных для оценки условного математического ожиданияXY <- as.data.frame(cbind(Xfull, Y))colnames(XY) <- c(paste('X', 1:(ncol(XY)-1), sep=''), 'Y')num.trees <- 500forest <- sobolMDA::ranger(Y ~., data = XY, num.trees = num.trees, importance = 'sobolMDA')sobolMDA <- forest$variable.importancenames(sobolMDA) <- colnames(X)sort(sobolMDA, decreasing = T)          X1           X8           X7           X6           X5           X9  0.062220958  0.021946135  0.016818860  0.016777223 -0.001290326 -0.001540919           X3          X10           X4           X2 -0.001578540 -0.007400854 -0.008299478 -0.020334150 

Как видно, он корректно определяет, что X_1 является наиболее важной переменной, в то время как другие переменные равнозначно важны или не важны. Это имеет смысл, потому что условное математическое ожидание Y изменяется только X_1. Критически важно, что данный метод справляется с этим, несмотря на зависимость между X_1 и X_3. Таким образом, мы успешно достигли цели (1), как объяснялось выше, в этом примере. С другой стороны, мы также можем рассмотреть традиционный MDA:

forest <- sobolMDA::ranger(Y ~., data = XY, num.trees = num.trees, importance = 'permutation')MDA <- forest$variable.importancenames(MDA) <- colnames(X)sort(MDA, decreasing = T)          X1           X3           X6           X7           X8           X2  0.464516976  0.118147061  0.063969310  0.032741521  0.029004312 -0.004494380           X4           X9          X10           X5 -0.009977733 -0.011030996 -0.014281844 -0.018062544 

В этом случае, хотя он корректно определяет X_1 как наиболее важную переменную, он также помещает X_3 на второе место с значением, которое кажется значительно выше, чем у остальных переменных. При этом факте X_3 также неважно, как и X_2, X_4,…, X_10!

Но что, если нас интересует предсказание распределения Y более общим образом, например, для оценки квантилей? В этом случае нам нужна мера, которая способна учитывать влияние X_2 на условную дисперсию Y. Здесь вступает в игру мера важности переменных MMD:

MMDVimp <- compute_drf_vimp(X=X,Y=Y)sort(MMDVimp, decreasing = T)         X2          X1         X10          X6          X8          X3 0.683315006 0.318517259 0.014066410 0.009904518 0.006859128 0.005529749          X7          X9          X4          X5 0.003476256 0.003290550 0.002417677 0.002036174 

Опять же, эта мера способна правильно определить, что важно: X_1 и X_2 являются двумя наиболее важными переменными. И снова, она делает это несмотря на взаимосвязь между X_1 и X_3. Интересно также, что она придает более высокую важность сдвигу дисперсии от X_2, чем сдвигу математического ожидания от X_1.

Реальные данные

Наконец, я представляю реальное применение переменной важности для демонстрации. Обратите внимание, что с помощью DRF мы могли бы рассмотреть даже многомерный Y, но для простоты мы сосредоточимся на одномерной настройке и рассмотрим данные о заработной плате в США из Обследования американского сообщества 2018 года, проведенного Бюро переписи населения США. В первой статье о DRF мы получили данные о приблизительно 1 миллионе работников, работающих на полный рабочий день, из Обследования американского сообщества 2018 года, проведенного Бюро переписи населения США, из которых мы извлекли информацию о заработной плате и все ковариаты, которые могут быть важными для заработной платы. Это богатство данных идеально подходит для экспериментов с методом, таким как DRF (фактически мы будем использовать только небольшой поднабор для этого анализа). Здесь можно найти загруженные нами данные.

# Загрузка данных (https://github.com/lorismichel/drf/blob/master/applications/wage_data/data/datasets/wage_benchmark.Rdata)load("wage_benchmark.Rdata")## Определение обучающих данныхn<-1000Xtrain<-X[1:n,] Ytrain<-Y[1:n,]Xtrain<-cbind(Xtrain,Ytrain[,"male"])colnames(Xtrain)[ncol(Xtrain)]<-"male"Ytrain<-Ytrain[,1, drop=F]## Определение тестовых данныхntest<-2000Xtest<-X[(n+1):(n+ntest),]  Ytest<-Y[(n+1):(n+ntest),]Xtest<-cbind(Xtest,Ytest[,"male"])colnames(Xtest)[ncol(Xtest)]<-"male"Ytest<-Ytest[,1, drop=F]

Теперь мы вычисляем обе меры важности переменных (это займет некоторое время, поскольку реализован только метод drop-and-relearn для DRF):

# Вычисление важности переменных для обеих мер# 1. Sobol-MDAXY <- as.data.frame(cbind(Xtrain, Ytrain))colnames(XY) <- c(paste('X', 1:(ncol(XY)-1), sep=''), 'Y')num.trees <- 500forest <- sobolMDA::ranger(Y ~., data = XY, num.trees = num.trees, importance = 'sobolMDA')SobolMDA <- forest$variable.importancenames(SobolMDA) <- colnames(Xtrain)# 2. MMD-MDAMMDVimp <- compute_drf_vimp(X=Xtrain,Y=Ytrain,silent=T)print("10 наиболее важных переменных для оценки условного ожидания")sort(SobolMDA, decreasing = T)[1:10]print("5 наиболее важных переменных для оценки условного распределения")sort(MMDVimp, decreasing = T)[1:10]

Sobol-MDA:education_level                   age                  male           0.073506769           0.027079349           0.013722756         occupation_11         occupation_43           industry_54           0.013550320           0.010025332           0.007744589           industry_44         occupation_23         occupation_15           0.006657918           0.005772662           0.004610835 marital_never married           0.004545964

MMD-MDA:education_level                   age                  male           0.420316085           0.109212519           0.027356393         occupation_43         occupation_11 marital_never married           0.016861954           0.014122583           0.003449910         occupation_29       marital_married           industry_81           0.002272629           0.002085207           0.001152210           industry_72           0.000984725

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

Чтобы получить небольшой набор предиктивных переменных, теперь можно для j=1,…p-1,

(I) Удалите наименее важную переменную

(II) Вычислите потери (например, среднеквадратическую ошибку) на тестовом наборе

(III) Пересчитать важность переменных для оставшейся переменной

(IV) Повторить до достижения определенного критерия остановки

Можно остановиться, например, если потери превысили 5%. Чтобы мне было проще в этой статье, я просто использую сохраненные значения важности переменных в “SobolMDA” и “MMDVimp” выше. То есть, я игнорирую шаг (III) и учитываю только (I), (II) и (IV). Когда целью оценки является полное условное распределение, шаг (II) также не является совершенно ясным. Мы используем то, что мы называем потерей MMD, подробнее описанную в нашей статье ([4]). Эта потеря учитывает ошибку, которую мы допускаем при предсказании распределения. Для условного среднего мы просто используем среднеквадратическую ошибку. Это выполняется в функции “evalall”, найденной ниже:

# По одной удаляем переменные в соответствии с сохраненными значениями важности в SobolMDA# и MMDVimp.evallistSobol<-evalall(SobolMDA, X=Xtrain ,Y=Ytrain ,Xtest, Ytest, metrics=c("MSE"), num.trees )evallistMMD<-evalall(MMDVimp, X=Xtrain ,Y=Ytrain ,Xtest, Ytest, metrics=c("MMD"), num.trees )plot(evallistSobol$evalMSE, type="l", lwd=2, cex=0.8, col="darkgreen", main="Потери MSE" , xlab="Количество удаленных переменных", ylab="Значения")plot(evallistMMD$evalMMD, type="l", lwd=2, cex=0.8, col="darkgreen", main="Потери MMD" , xlab="Количество удаленных переменных", ylab="Значения")

Это приводит к следующим двум изображениям:

Обратите внимание, что в обоих случаях есть некоторые плавающие линии, что в первую очередь связано с тем, что я не пересчитал важность переменной, например, пропустил шаг (III), и во-вторых, с случайностью лесов. Кроме этого, графики хорошо показывают, как ошибки постепенно увеличиваются с каждой удаленной переменной. Это увеличение сначала медленное для наименее важных переменных, а затем становится быстрее для наиболее важных, как и ожидалось. В частности, потери в обоих случаях практически не изменяются, если удалить 50 наименее важных переменных! Фактически, в обоих случаях можно удалить около 70 переменных, не увеличивая потери более чем на 6%. Следует отметить, что многие предикторы являются частью кодированных по одному значению категориальных переменных, и поэтому нужно быть немного осторожным при удалении предикторов, так как они соответствуют уровням одной категориальной переменной. Однако в реальном приложении это может быть все равно желательным.

Заключение

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

Как уже отмечалось, представленные меры не ограничиваются исключительно случайными лесами, но могут в принципе использоваться более общим образом. Однако леса позволяют элегантный подход проекции, который позволяет расчет меры важности для всех переменных j без необходимости повторного подгонки леса каждый раз (!) Это описано в [3] и [4].

Литература

[1] Брейман, Л. (2001). Случайные леса. Машинное обучение, 45(1): 5–32.

[2] Брейман, Л. (2003a). Настройка, использование и понимание случайных лесов версии 3.1. Технический отчёт, Университет Калифорнии в Беркли, Кафедра статистики.

[3] Бенар, К., Да Вейга, С., и Скорнет, Э. (2022). Уменьшение точности для случайных лесов: несогласованность и практическое решение с помощью метода Соболя-МДА. Биометрика, 109(4): 881–900.

[4] Клемент Бенар, Джеффри Наф и Джули Джосс. Важность переменных на основе ММД для случайных лесов с распределением, 2023.

[5] Штробл, К., Булетстекс, А.-Л., Цейлейс, А., и Хоторн, Т. (2007). Смещение в мерах важности переменных случайных лесов: примеры, источники и решение. BMC Bioinformatics, 8: 25.

Приложение: Код

#### Содержание файла compute_drf_vimp.R #######' Важность переменных для случайных лесов с распределением#'#' @param X Матрица с обучающими входными данными.#' @param Y Матрица с обучающими выходными данными.#' @param X_test Матрица с тестовыми входными данными. Если NULL, используются оценки Out-of-bag.#' @param num.trees Количество деревьев для подгонки DRF. Значение по умолчанию - 500 деревьев.#' @param silent Если FALSE, выводить номер итерации переменной, иначе ничего не выводить. Значение по умолчанию - FALSE.#'#' @return Список значений важности для всех входных переменных.#' @export#'#' @examplescompute_drf_vimp <- function(X, Y, X_test = NULL, num.trees = 500, silent = FALSE){    # Подгонка начального DRF  bandwidth_Y <- drf:::medianHeuristic(Y)  k_Y <- rbfdot(sigma = bandwidth_Y)  K <- kernelMatrix(k_Y, Y, Y)  DRF <- drf(X, Y, num.trees = num.trees)  wall <- predict(DRF, X_test)$weights    # Вычисление нормализационной константы  wbar <- colMeans(wall)  wall_wbar <- sweep(wall, 2, wbar, "-")  I0 <- as.numeric(sum(diag(wall_wbar %*% K %*% t(wall_wbar))))    # Вычисление важности DRF при удалении переменных по одной  I <- sapply(1:ncol(X), function(j) {    if (!silent){print(paste0('Запуск вычисления важности для переменной X', j, '...'))}    DRFj <- drf(X = X[, -j, drop = F], Y = Y, num.trees = num.trees)     DRFpredj <- predict(DRFj, X_test[, -j])    wj <- DRFpredj$weights    Ij <- sum(diag((wj - wall) %*% K %*% t(wj - wall)))/I0    return(Ij)  })    # Вычисление смещения повторного обучения  DRF0 <- drf(X = X, Y = Y, num.trees = num.trees)  DRFpred0 = predict(DRF0, X_test)  w0 <- DRFpred0$weights  vimp0 <- sum(diag((w0 - wall) %*% K %*% t(w0 - wall)))/I0    # Вычисление окончательной важности (удаление смещения и обрезка отрицательных значений)  vimp <- sapply(I - vimp0, function(x){max(0,x)})    names(vimp)<-colnames(X)    return(vimp)  }

#### Содержание файла evaluation.R ######compute_mmd_loss <- function(Y_train, Y_test, weights){  # Y_train <- scale(Y_train)  # Y_test <- scale(Y_test)  bandwidth_Y <- (1/drf:::medianHeuristic(Y_train))^2  k_Y <- rbfdot(sigma = bandwidth_Y)  K_train <- matrix(kernelMatrix(k_Y, Y_train, Y_train), ncol = nrow(Y_train))  K_cross <- matrix(kernelMatrix(k_Y, Y_test, Y_train), ncol = nrow(Y_train))  weights <- matrix(weights, ncol = ncol(weights))  t1 <- diag(weights%*%K_train%*%t(weights))  t2 <- diag(K_cross%*%t(weights))  mmd_loss <- mean(t1) - 2*mean(t2)  mmd_loss}evalall <- function(Vimp, X ,Y ,Xtest, Ytest, metrics=c("MMD","MSE"), num.trees ){    if (ncol(Ytest) > 1 & "MSE" %in% metrics){    metrics <- metrics[!( metrics %in% "MSE") ]  }    # Сортировка в порядке возрастания важности, чтобы сначала удалялись наименее важные переменные  Vimp<-sort(Vimp)    if ( is.null(names(Vimp)) ){    stop("Требуются имена для последующего использования")    }      evalMMD<-matrix(0, nrow=ncol(X))  evalMSE<-matrix(0, nrow=ncol(X))    ###Идея: Создать функцию, которая принимает меру важности переменной и выполняет этот цикл!!    for (j in 1:ncol(X)){               if (j==1){            if ("MMD" %in% metrics){                DRFred<- drf(X=X,Y=Y)        weights<- predict(DRFred, newdata=Xtest)$weights        evalMMD[j]<-compute_mmd_loss(Y_train=Y, Y_test=Ytest, weights)          }            if ("MSE" %in% metrics){                XY <- as.data.frame(cbind(X, Y))        colnames(XY) <- c(paste('X', 1:(ncol(XY)-1), sep=''), 'Y')        RFfull <- sobolMDA::ranger(Y ~., data = XY, num.trees = num.trees)        XtestRF<-Xtest        colnames(XtestRF) <- paste('X', 1:ncol(XtestRF), sep='')        predRF<-predict(RFfull, data=XtestRF)        evalMSE[j] <- mean((Ytest - predRF$predictions)^2)            }    }else{                  if ("MMD" %in% metrics){                DRFred<- drf(X=X[,!(colnames(X) %in% names(Vimp[1:(j-1)])), drop=F],Y=Y)        weights<- predict(DRFred, newdata=Xtest[,!(colnames(Xtest) %in% names(Vimp[1:(j-1)])), drop=F])$weights        evalMMD[j]<-compute_mmd_loss(Y_train=Y, Y_test=Ytest, weights)              }                        if ("MSE" %in% metrics){                XY <- as.data.frame(cbind(X[,!(colnames(X) %in% names(Vimp[1:(j-1)])), drop=F], Y))        colnames(XY) <- c(paste('X', 1:(ncol(XY)-1), sep=''), 'Y')        RFfull <- sobolMDA::ranger(Y ~., data = XY, num.trees = num.trees)        XtestRF<-Xtest[,!(colnames