Метод случайного леса с бэггингом и бутстрепом

Случайный лес и деревья с бэггингом

Здравствуйте и вновь добро пожаловать на занятия по теме «Машинное обучение с учителем на языке Python: ансамблевые методы».

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

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

Вначале идёт импорт всего необходимого. Обратите внимание, что библиотека SciKit-Learn имеет собственные регрессор и классификатор с бэггингом, но они не позволяют устанавливать максимальную глубину дерева, поэтому я решил импортировать написанные нами ранее классы BaggedTreeRegressor и BaggedTreeClassifier.

import numpy as np

import pandas as pd

import matplotlib.pyplot as plt

from sklearn.ensemble import RandomForestRegressor,BaggingRegressor, RandomForestClassifier, BaggingClassifier

from util import BaggedTreeRegressor,BaggedTreeClassifier

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

#make simpleregression data

N = 15

D = 100

X = (np.random.random((N, D)) – 0.5)*10

Y = X.sum(axis=1)**2+ 0.5*np.random.randn(N)

Ntrain = N/2

Xtrain = X[:Ntrain]

Ytrain = Y[:Ntrain]

Xtest = X[Ntrain:]

Ytest = Y[Ntrain:]

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

# from rf_classification import get_data

# X, Y = get_data()

# Ntrain = int(0.8*len(X))

# Xtrain, Ytrain = X[:Ntrain], Y[:Ntrain]

# Xtest, Ytest = X[Ntrain:], Y[Ntrain:]

Итретий набор данных – это также использовавшиеся нами ранее цены нанедвижимость. Если вы хотите запустить какой-либо из этих наборов, уберитепометку комментариев из нужного набора  ипометьте остальные как комментарии.

# from rf_regression import get_data

# Xtrain, Ytrain, Xtest, Ytest = get_data()

Далееидёт наш главный цикл. Переменной T присвоенозначение 300, то есть мы будем тестировать ансамбли до 300 деревьев. Вначале мыпроверяем, не равно ли число деревьев нулю, потому что не бывает ансамблей изнуля деревьев. Далее мы обучаем наши модели и проверяем их работу напроверочном наборе. Обратите внимание, что я не указывал здесь глубинудеревьев, но вы можете попробовать указать.

T = 300

test_error_rf = np.empty(T)

test_error_bag = np.empty(T)

for num_trees in xrange(T):

  if num_trees== 0:

   test_error_rf[num_trees] = None

   test_error_bag[num_trees] = None

  else:

    rf =RandomForestRegressor(n_estimators=num_trees)

    # rf =RandomForestClassifier(n_estimators=num_trees)

    rf.fit(Xtrain, Ytrain)

   test_error_rf[num_trees] = rf.score(Xtest, Ytest)

    bg =BaggedTreeRegressor(n_estimators=num_trees)

    # bg =BaggedTreeClassifier(n_estimators=num_trees)

   bg.fit(Xtrain, Ytrain)

   test_error_bag[num_trees] = bg.score(Xtest, Ytest)

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

  ifnum_trees % 10 == 0:

    print“num_trees:”, num_trees

Ипоследнее – выведение на экран результата – ошибку проверочного набора дляслучайного леса и ансамбля деревьев с бэггингом.

plt.plot(test_error_rf, label=’rf’)

plt.plot(test_error_bag, label=’bag’)

plt.legend()

plt.show()

Давайтепосмотрим на результаты.

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

Реализация «не как случайный лес»

Поскольку в этом курсе мы не будем создавать полную версию случайного леса, во всяком случае, не на данном этапе, было бы любопытно: а что, если взять принципы метода случайного леса и попробовать их применить на чём-то другом? Применим бэггинг для каждой созданной базовой модели, обучим её на выбоках методом бутстрепа, а также используем подмножество признаков. Разница между этим опытом и настоящим случайным лесом в том, что в настоящем методе случайного леса для каждого разделителя в дереве создаётся вновь отобранное случайное подмножество признаков, тогда как в нашем псевдослучайном лесу это будет сделано лишь однажды, а каждое дерево будет обучаться лишь с использованием только этого подмножества. Это позволит нам использовать класс DecisionTree библиотеки SciKit-Learn. Вместо того чтобы писать код с нуля, мы просто пройдёмся по нему, поскольку он очень похож на уже написанный. Если вы не хотите писать код сами, а лишь запустить его, то соответствующий файл в репозитарии называется rf_vs_bag2.py.

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

import numpy as np

import pandas as pd

import matplotlib.pyplot as plt

from sklearn.tree import DecisionTreeClassifier

from sklearn.ensemble import RandomForestRegressor,BaggingRegressor, RandomForestClassifier, BaggingClassifier

from util import BaggedTreeRegressor,BaggedTreeClassifier

# make simple regression data

# N = 15

# D = 100

# X = (np.random.random((N, D)) – 0.5)*10

# Y = X.sum(axis=1)**2 + 0.5*np.random.randn(N)

# Ntrain = N/2

# Xtrain = X[:Ntrain]

# Ytrain = Y[:Ntrain]

# Xtest = X[Ntrain:]

# Ytest = Y[Ntrain:]

from rf_classification import get_data

X, Y = get_data()

Ntrain = int(0.8*len(X))

Xtrain, Ytrain = X[:Ntrain], Y[:Ntrain]

Xtest, Ytest = X[Ntrain:], Y[Ntrain:]

# from rf_regression import get_data

# Xtrain, Ytrain, Xtest, Ytest = get_data()

Далееопределяем класс NotAsRandomForest («не какслучайный лес»), поскольку он случайный, не настолько, как в настоящемслучайном лесе.

class NotAsRandomForest:

  def __init__(self, n_estimators):

    self.B = n_estimators

Наибольшиеотличия в функциях fit и predict. Функция fitочень схожа на модель дерева с бэггингом с тем исключением, что сейчас намнеобходимо сохранять признаки, которые мы использовали, что окажется полезнымдля функции predict. Кроме того, функция fit имеет новую переменную M, показывающий,сколько признаков содержится в подмножестве. Параметр replaceв переменной features имеет значение False,поскольку мы не хотим использовать один и тот же параметр более одного раза.

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

  def fit(self, X, Y, M=None):

    N, D =X.shape

    if M isNone:

      M =int(np.sqrt(D))

    self.models= []

   self.features = []

    for b in xrange(self.B):

      tree =DecisionTreeClassifier()

      # samplefeatures

      features =np.random.choice(D, size=M, replace=False)

      # sampletraining samples

      idx =np.random.choice(N, size=N, replace=True)

      Xb =X[idx]

      Yb =Y[idx]

     tree.fit(Xb[:, features], Yb)

     self.features.append(features)

     self.models.append(tree)

В функции predict всё ожидаемо – мы суммируем все прогнозы, делим на общее значение и округляем. Обратите внимание, что мы сохраняем признаки, поскольку каждая модель имеет свой набор признаков.

  def predict(self, X):

    N = len(X)

    P =np.zeros(N)

    forfeatures, tree in zip(self.features, self.models):

      P +=tree.predict(X[:, features])

    returnnp.round(P / self.B)

  def score(self, X, Y):

    P =self.predict(X)

    returnnp.mean(P == Y)

Далеевсё аналогично примеру, в котором шло сравнение случайного леса с бэггингом,только добавлен ещё один массив для сохранения ошибок.

T = 500

test_error_prf = np.empty(T)

test_error_rf = np.empty(T)

test_error_bag = np.empty(T)

for num_trees in xrange(T):

  if num_trees== 0:

   test_error_prf[num_trees] = None

   test_error_rf[num_trees] = None

   test_error_bag[num_trees] = None

  else:

    rf =RandomForestClassifier(n_estimators=num_trees)

   rf.fit(Xtrain, Ytrain)

   test_error_rf[num_trees] = rf.score(Xtest, Ytest)

    bg =BaggedTreeClassifier(n_estimators=num_trees)

   bg.fit(Xtrain, Ytrain)

   test_error_bag[num_trees] = bg.score(Xtest, Ytest)

    prf =NotAsRandomForest(n_estimators=num_trees)

   prf.fit(Xtrain, Ytrain)

   test_error_prf[num_trees] = prf.score(Xtest, Ytest)

  if num_trees %10 == 0:

    print“num_trees:”, num_trees

plt.plot(test_error_rf, label=’rf’)

plt.plot(test_error_prf, label=’pseudo rf’)

plt.plot(test_error_bag, label=’bag’)

plt.legend()

plt.show()

Ещёраз хочу предупредить, что программа работает очень медленно, поэтому когдабудете запускать её сами, убедитесь, что у вас хватит на это времени. Итак,запустим и посмотрим на результаты.

Получилось очень любопытно: из графика видно, что в случае псевдослучайного леса точность возрастает при добавлении количества деревьев.

Связь с исключением глубокого обучения

И под конец давайте рассмотрим интересную связь между глубоким обучением и методом случайного леса.

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

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

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

Проблеманейронных сетей в том, что им требуется очень много времени для обучения. Еслиу нас есть нейронная сеть, которой нужен для обучения 1 час, и мы хотим обучитьансамбль из 200 нейронных сетей, то нам для обучения понадобится 200 часов – аэто более 8 дней. Допустим теперь, что в нашей нейронной сети 1000 узлов –вполне разумное число. 21000 – это примерно 10301 – оченьбольшое число. Мы не можем обучить такое количество нейронных сетей в любоеразумное время.

Сутьв том, что исключение имитирует ансамбль из 2N нейронных сетей путём случайногоисключения узлов в процессе обучения и умножает их количество на (1 –вероятность_исключения) во время прогноза. Таким образом, оно позволяетсоздавать ансамбли без настоящего их создания. Кроме того, оно работает схожимсо случайным лесом образом в том плане, что позволяет случайным образомвыбирать, какие признаки использовать в каждом слое, подобно тому как дерево вслучайном лесу случайным образом выбирает, какие признаки рассматривать вкаждом узле.

Понравилась статья? Поделить с друзьями:
Добавить комментарий

;-) :| :x :twisted: :smile: :shock: :sad: :roll: :razz: :oops: :o :mrgreen: :lol: :idea: :grin: :evil: :cry: :cool: :arrow: :???: :?: :!: