Обучение ансамблю AdaBoost (классификация)

машинное обучение

1. Концепция

1.1 League of Legends или AdaBoost?

Идея игры ЛОЛ группой такова: когда противник начинает группу, у каждого из пяти героев своя роль, танк должен быть мясистым, а АЦП должно хватать на вывод... Итак , чем комплекснее тип, тем легче противнику уничтожить группу. По сути, модель AdaBoost такая же, как и у группы LOL. Столкнувшись с набором данных, каждый базовый ученик (герой) имеет свою собственную направленность, и чем полнее охват, тем лучше эффект обучения. При этом ** имеет определенную последовательность: ** сначала управление, а потом вывод. . .

键盘侠

1.2 Настоящий АдаБуст

1.2.1 Пример пространства

Предположим, что задан двоичный набор обучающих данных.T=\{(\boldsymbol{x_1}, y_1),(\boldsymbol{x_2}, y_2),...,(\boldsymbol{x_N}, y_N)\}\boldsymbol{x_i}Является1*dразмерный вектор, меткаy_i \in Y=\{-1, 1\}.

1.2.2 Выражения
\begin{align} F_T(\boldsymbol{x})=\sum_{t=1}^{T} \alpha_tf_t(\boldsymbol{x})\tag{1.1} \end{align}

в формуле(1.1)середина,f_t(\boldsymbol{x})результат вычисления слабого ученика,f_tмодель слабого ученика,Tчисло слабых учащихся. Во время каждой итерации каждая итерация выбирает слабого ученика и присваивает ему вес.\alpha_t, то для первогоiДля образца(i\leq N), вся модель AdaBoosttошибка обучения за эпохуE_tможно выразить как:

\begin{align} E_t=\sum_{i=1}^{N}E[F_{t-1}(\boldsymbol{x_i})+\alpha_t f_t(\boldsymbol{x_i})]\tag{1.2} \end{align}

в формуле(1.4)серединаF_{t-1}(\boldsymbol{x_i})основан на предыдущемt-1Модель, полученная итерацией,f_t(\boldsymbol{x_i})является слабым учеником этого раунда обучения и будет добавлен в окончательную модель после завершения обучения.

1.2.3 Функция потерь AdaBoost

Выражение функции:

L(y, f(x))=e^{[-yf(x)]} \tag{1.3}

图1.1 函数图像

1.2.4 Прошлое и настоящее AdaBoost

Предположим, послеm-1после генерации(m<T), дляiДля примера общественность(1.1)Это может быть выражено как

\begin{align} F_{m-1}(\boldsymbol{x_i})=&\alpha_1f_1(\boldsymbol{x_i})+\alpha_2f_2(\boldsymbol{x_i})+...+\alpha_{m-1}f_{m-1}(\boldsymbol{x_i}) \tag{1.4} \end{align}

По формуле(1.3)имеют

\begin{align} F_{m}(\boldsymbol{x_i})=F_{m-1}(\boldsymbol{x_i})+\alpha_{m}f_{m}(\boldsymbol{x_i}) \tag{1.5} \end{align}

Когда функция потерь экспоненциальная потеря:

\begin{align} E=&\sum_{i=1}^{N}e^{-y_iF_{m}(\boldsymbol{x_i})}\\ =&\sum_{i=1}^{N}e^{-y_i(F_{m-1}(\boldsymbol{x_i})+\alpha_{m}f_{m}(\boldsymbol{x_i}))}\\ =&\sum_{i=1}^{N}e^{-y_iF_{m-1}(\boldsymbol{x_i})-y_i\alpha_{m}f_{m}(\boldsymbol{x_i})}\\ =&\sum_{i=1}^{N}e^{-y_iF_{m-1}(\boldsymbol{x_i})}e^{-y_i \alpha_{m}f_{m}(\boldsymbol{x_i})}\tag{1.6} \end{align}

формула(1.6), так какe^{-y_iF_{m-1}(\boldsymbol{x_i})}является фиксированным значением, обозначаемым какw_i. Итак, предположимw_i^{(1)}=1, и когдаm>1когда естьw_i^{(m)}=e^{-y_iF_{m-1}(\boldsymbol{x_i})}, то формула(1.6)можно записать как:

\begin{align} E=&\sum_{i=1}^{N}w_i^{(m)}e^{-y_i\alpha_{m}f_{m}(\boldsymbol{x_i})}\tag{1.7} \end{align}

Если модель AdaBoost может правильно классифицировать в демонстрационном пространстве, есть:y_if_{m}(\boldsymbol{x_i})=1; при неправильной классификации:y_if_{m}(\boldsymbol{x_i})=-1. Следовательно, формула(1.7)можно записать как:

\begin{align} E=&\sum_{y_i=f_{m}(\boldsymbol{x_i})}^{N}w_i^{(m)} e^{-\alpha_{m}}+ \sum_{y_i \neq f_{m}(\boldsymbol{x_i})}^{N}w_i^{(m)} e^{\alpha_{m}} \\ =& \sum_{y_i = f_{m}(\boldsymbol{x_i})}^{N}w_i^{(m)}e^{-\alpha_{m}}+  \sum_{y_i \neq f_{m}(\boldsymbol{x_i})}^{N} w_i^{(m)}e^{\alpha_{m}}- \sum_{y_i \neq f_{m}(\boldsymbol{x_i})}^{N}w_i^{(m)}e^{-\alpha_{m}}+  \sum_{y_i \neq f_{m}(\boldsymbol{x_i})}^{N}w_i^{(m)}e^{-\alpha_{m}}\\ \\ =&\sum_{i=1}^{N} w_i^{(m)} e^{- \alpha_{m}}+ \sum_{y_i \neq f_{m}(\boldsymbol{x_i})}^{N}w_i^{(m)} e^{\alpha_{m}}- \sum_{y_i \neq f_{m}(\boldsymbol{x_i})}^{N}w_i^{(m)} e^{-\alpha_{m}} \\ \\ =&\sum_{i=1}^{N}w_i^{(m)} e^{-\alpha_{m}}+ \sum_{y_i \neq f_{m}(\boldsymbol{x_i})}^{N}w_i^{(m)}(e^{\alpha_{m}}-e^{-\alpha_{m}})\tag{1.8} \end{align}

в формуле(1.8), чтобы сделать классификацию модели AdaBoost максимально корректной, то есть найти\alpha_{m}сделать\sum_{y_i \neq f_{m}(\boldsymbol{x_i})}^{N}w_i^{(m)}срок как можно меньше (при условии\alpha_{m}>0), так что\alpha_{m}искать выводимеют:

\begin{align} \partial{E}/\partial{\alpha_{m}}=&-\sum_{i=1}^{N}w_i^{(m)} e^{-\alpha_{m}}+ \sum_{y_i \neq f_{m}(\boldsymbol{x_i})}^{N}w_i^{(m)}(e^{\alpha_{m}}+e^{-\alpha_{m}})\tag{1.9} \end{align}

Пусть формула(1.9)за0:

\begin{align} 0=-\sum_{i=1}^{N}w_i^{(m)} e^{-\alpha_{m}}+ \sum_{y_i \neq f_{m}(\boldsymbol{x_i})}^{N}w_i^{(m)}(e^{\alpha_{m}}+e^{-\alpha_{m}})\\ e^{-\alpha_{m}} \sum_{y_i=f_{m}(\boldsymbol{x_i})}^{N}w_i^{(m)} = e^{\alpha_{m}} \sum_{y_i\neq f_{m}(\boldsymbol{x_i})}^{N}w_i^{(m)}\tag{1.10} \end{align}

так какe^{-\alpha_{m}}иiневажно, формула(1.10)Возьмем логарифм слева и справа:

\begin{align}\\ -\alpha_{m}+In(\sum_{y_i=f_{m}(\boldsymbol{x_i})}^{N}w_i^{(m)}) =& \alpha_{m}+In(\sum_{y_i\neq f_{m}(\boldsymbol{x_i})}^{N}w_i^{(m)})\\ 2\alpha_{m} =& In(\sum_{y_i=f_{m}(\boldsymbol{x_i})}^{N}w_i^{(m)}) - In(\sum_{y_i\neq f_{m}(\boldsymbol{x_i})}^{N}w_i^{(m)}) \\ \alpha_{m} =& \frac{1}{2} In(\frac{\sum_{y_i=f_{m}(\boldsymbol{x_i})}^{N}w_i^{(m)}}{\sum_{y_i\neq f_{m}(\boldsymbol{x_i})}^{N}w_i^{(m)}}) \tag{1.11} \end{align}

потому чтоmСпустя время частота ошибок\varepsilon_mза:

\begin{align} \varepsilon_m=\frac{\sum_{y_i \neq f_{m}(\boldsymbol{x_i})}^{N}w_i^{(m)}}{\sum_{i=1}^{N}w_i^{(m)}} \tag{1.12} \end{align}

Итак, формула(1.11)за

\begin{align}\\ \alpha_{m} =& \frac{1}{2} In(\frac{1-\varepsilon_m}{\varepsilon_m}) \tag{1.11} \end{align}

Наконец, обновивF_{m-1}заF_{m}(\boldsymbol{x_i})=F_{m-1}(\boldsymbol{x_i})+\alpha_mf_m(\boldsymbol{x_i})Производительность модели AdaBoost можно улучшить.

1.2.5 Основной процесс

西瓜书174


2. Другое понимание AdaBoost

艾瑞博迪嗨起来!

3. Дерево усиления

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

3.1 Данные

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

图3.1 数据情况概览

1. Присвойте каждой выборке одинаковый вес, указав, что они в первую очередь одинаково важны для классификатора.
w=\frac{1}{data_size} = \frac{1}{8}
2. Подсчитать количество правильных и неправильных классификаций при разных собственных значениях и вычислить их коэффициенты Джини.

图3.2 三个决策树桩的计算结果
Поскольку коэффициент Джини признака «Вес пациента» наименьший, в качестве первого классификатора выбирается пень дерева принятия решений, образованный этим признаком.

Когда функция «Вес пациента» используется в качестве критерия принятия решения, только на всем тренировочном наборе1ошибка, то есть\varepsilon_1=\frac{1}{8}. По формуле(1.11), вес классификатора можно рассчитать как:

\begin{align}\\ \alpha_{1} =& \frac{1}{2} In(\frac{1-\varepsilon_1}{\varepsilon_1})\\ =& \frac{1}{2} In(\frac{1-\frac{1}{8}}{\frac{1}{8}}) \\ =& 0.97 \end{align}
3. Перемасштабировать распределение данных по весам.
  • Для неправильных образцов увеличьте вес.

    图3.3 第一个分类器错误的样本
    w_3 = \frac{1}{8} * e^{-0.97*(1*-1)}=0.33

  • Для правильно классифицированных образцов уменьшите веса.

    图3.4 第一个分类器正确的样本
    w_0=w_1=w_2=\frac{1}{8} * e^{-0.97*(1*1)}=0.05 w_4=w_5=w_6=w_7=\frac{1}{8} * e^{-0.97*(-1*-1)}=0.05

  • Регулярные веса.w_i=\frac{w_i}{\sum_{j=0}^{7}w_j}=\frac{w_i}{0.68}

    图3.5 具有新权重的数据集

4. Создайте новое распределение данных (чтобы оно имело ту же форму, что и исходные данные).

В этом случае рассмотрите данные на рис. 3.5 как распределение вероятностей. выбрать один наугад(0,1)цифры внутриn:

  • какn_1 \in (0, 0.07], то первый0данные бара (Да, Да, 205, Да) добавляются вновый набор данных;
  • какn \in (0.07, 0.14], то первый2данные бара (Нет, Да, 180, Да) добавляются вновый набор данных;
  • какn \in (0.21, 0.7], то первый2данные (Да, Да, 167, Да) добавляются вновый набор данных; ... и так далее

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

图3.6 第一轮结束后新的数据集

Перейдите к шагу 1. Заканчивается до тех пор, пока в модели не окажется указанное количество классификаторов.


4. Код

исходный код

class AdaBoost(object):
    def __init__(self, n_estimator, weaker_learner, column_type):
        '''
        AdaBoost 模型初始化
        :param n_estimator: 选取n_estimator个弱学习器
        '''
        # 弱学习器的数量
        self.n_estimator = n_estimator
        # 每一列数据的类型(连续/离散)
        self.column_type = column_type
        # 初始化每个学习器权重
        self.alpha = [1] * n_estimator
        # 初始弱学习器数组
        self.weaker_learners = []

        # 初始化样本数据集大小以及特征数量
        self.data_size, self.feature_size = 0, 0

        # 特征的索引值
        self.feature_indices = []

        # 初始化弱学习器
        self.weaker_learner = weaker_learner
        # 初始化训练集的权值分布
        self.w = None

    def __initArgs__(self, _X, _Y):
        '''
        相关参数初始化
        :param _X: 训练集特征值
        :param _Y: 训练集标签值
        :return:
        '''
        self.train_x = _X
        self.train_y = _Y
        self.data_size, self.feature_size = _X.shape
        self.feature_indices = [_ for _ in range(self.feature_size)]

    def getErrorRate(self, true_y, fake_y):
        '''
        计算第ith_estimator个弱学习器的误差
        :param fake_y: 弱学习器的编号
        :return: 弱学习器的错误率
        '''
        aggErrors = np.multiply(np.mat(true_y) != np.mat(fake_y), np.ones(fake_y.shape))
        return aggErrors.sum() / true_y.shape[0]

    def getAlpha(self, error_rate):
        '''
        计算第ith_estimator个弱学习器的权重
        :param error_rate: 错误率
        :return: 弱学习器对应的权重
        '''

        return 0.5 * np.log((1-error_rate)/error_rate)

    def fit(self, _X, _Y):
        '''
        AdaBoost 模型训练
        :param _X: 特征值
        :param _Y: 标签值
        :return:
        '''
        self.__initArgs__(_X, _Y)
        for ith_estimator in range(self.n_estimator):
            print('%d\'s estimator:' % ith_estimator)

            # 初始化分布
            self.w = 1 / self.data_size * np.ones((self.data_size, 1))

            # 新建一个弱学习器
            # 寻找最优的划分节点
            weaker_learner = self.weaker_learner(ith_estimator, self.column_type)

            weaker_learner.fit(_X, _Y)

            weaker_learner_result = weaker_learner.predict(_X)

            self.weaker_learners.append((weaker_learner.__root__.feature_index, weaker_learner))

            # 计算错误率
            error_rate = self.getErrorRate(self.train_y, weaker_learner_result)
            print('error_rate:', error_rate)

            # 计算该弱学习器的权重
            ith_alpha = self.getAlpha(error_rate)
            print('alpha:', ith_alpha)
            self.alpha[ith_estimator] = ith_alpha

            print(self.train_y)
            print(weaker_learner_result)

            # 更新w
            w_tmp = - ith_alpha * np.multiply(self.train_y, weaker_learner_result)
            self.w = np.multiply(self.w, np.exp(w_tmp))
            print('update weights:', self.w)
            # 规范化w
            self.w /= self.w.sum()
            print('normal weights:', self.w)

            # 如果错误率比随机猜还差,那就停止
            if error_rate > 0.5:
                break
            else:
                _X, _Y = self.resampleData()
                print('resample data')
                print(_X)
                print(_Y)

        print('train done')

    def resampleData(self):
        '''
        数据重采样,先计算每个样本需要被抽取的次数,
        然后列索引按照抽取次数从大到小排序(注意是列)
        :return:
        '''
        # 确定每个样本需要抽取的次数

        nums = list(np.multiply(self.w.T[0], self.data_size))

        # 将权重的索引按 权重的大小排序(从大到小)
        idx = list(np.argsort(self.w.T[0]))
        idx.reverse()


        new_index = []
        for id in idx:
            num_arr = (int(nums[id]) + 1) * [id]
            new_index.extend(num_arr)
            if len(new_index) == self.data_size:
                break
        return self.train_x[new_index], self.train_y[new_index]

    def predict(self, X):
        '''
        预测
        :param x: 1*d 维的特征值
        :return: 预测结果
        '''
        print('predict')
        result = []
        for x in X:
            res = 0
            for index, weaker_learner in enumerate(self.weaker_learners):
                res += self.alpha[index] * weaker_learner[1].predict(
                    np.array([x])
                )
            result.append(1 if res > 0 else -1)
        return np.array([result]).T

5. Резюме

Самая большая проблема в машинном обучении — работа с наборами данных.пространственная катастрофаВообще говоря, после уменьшения количества признаков, несмотря на то, что скорость вычислений улучшилась, призрак знает, уменьшилась ли точность подгонки модели после удаления признаков. Модель AdaBoost отличается от алгоритмов SVM и нейронных сетей.В модели AdaBoost вам нужно выбрать только те функции, которые вы считаете полезными.Пока есть достаточное количество братьев (основных учеников), можно нарисовать любую модель (конечно , такого рода переоснащение модели того не стоит).

PS: после того, как вы привыкли к индексу строк и столбцов pandas, использование Numpy — это просто яма.


6. Ссылки