Почему в SVC от Sklearn время обучения не строго линейно относительно максимальной итерации при большом размере метки?

Я проводил анализ, пытаясь увидеть связь между временем обучения и максимальной итерацией в SVC. Данные, которые я использую, представляют собой произвольно сгенерированное число, и я построил график времени обучения против max_iter соответствия SVC. Я проверил журналы, и каждый двоичный классификатор достиг max_iter (я выводил все журналы консоли, которые показывали подробные предупреждения для каждого двоичного классификатора, и подсчитываю их). Однако я предполагал, что время обучения будет строго линейно по отношению к итерации, но на самом деле в случае, если данные обучения имеют много меток, например. скажем 40, тогда график не показывает, что это линейно. введите описание изображения здесь

Кажется, что по мере увеличения максимальной итерации каждая итерация занимает немного меньше времени, чем раньше. Хотя если мы изменили label_size на 2 (что означает, что каждое соответствие содержит только 1 двоичный классификатор), линия будет прямой.

введите описание изображения здесь

Что вызывает это?

Вот мой исходный код:

# -*- coding: utf-8 -*-
import numpy as np
from sklearn.svm import SVC
import time
import pandas as pd


def main(row_size, label_size):
    np.random.seed(2019)
    y = np.array([i for i in range(label_size) for j in range(row_size
                 / label_size)])
    if len(y) < row_size:
        y = np.append(y, [y[-1]] * (row_size - len(y)))
    X = np.random.rand(row_size, 300)
    print X.shape, y.shape
    return (X, y)


def train_svm(X, y, max_iter):
    best_params = {'C': 1}
    clf = SVC(
        C=best_params['C'],
        kernel=str('linear'),
        probability=False,
        class_weight='balanced',
        max_iter=max_iter,
        random_state=2018,
        verbose=True,
        )
    start = time.time()
    clf.fit(X, y)
    end = time.time()
    return end - start


if __name__ == '__main__':
    row_size = 20000
    m_iter = range(10, 401, 20)
    label_size = [40]
    data = {
        'label_size': [],
        'max_iter': [],
        'row_size': [],
        'time': [],
        }
    for it in m_iter:
        for l in label_size:
            (X, y) = main(row_size, l)
            t = train_svm(X, y, max_iter=it)
            data['label_size'].append(l)
            data['max_iter'].append(it)
            data['row_size'].append(row_size)
            data['time'].append(t)
            df = pd.DataFrame(data)
            df.to_csv('svc_iter.csv', index=None)

person Richie F.    schedule 29.09.2018    source источник
comment
Как вы уверены, что я почти уверен, что каждый двоичный классификатор достиг max_iter? вы указали параметр max_iter вручную? ... Я говорю это потому, что: max_iter по умолчанию - это -1, то есть без ограничений, поэтому каждый раз он может быть другим !.   -  person Yahya    schedule 30.09.2018
comment
@Yahya Да, я указывал max_iter и каждый раз проверял логи. Вот как я могу это построить   -  person Richie F.    schedule 01.10.2018
comment
Можете выложить код?   -  person Yahya    schedule 01.10.2018
comment
@Yahya Только что сделал.   -  person Richie F.    schedule 01.10.2018


Ответы (1)


Что ж, может быть множество причин для этого "очень небольшого изменения". Scikit-Learn не работает изначально, он построен на разных библиотеках и может использовать множество оптимизаторов ... и т. Д.!

Кроме того, ваш первый график очень близок к линейному!

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

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

Для получения дополнительных сведений о математическом подходе см. этот статья, раздел 6.2. Метод разложения..


Более того, в частности, SVM реализует два трюка, которые называются сжатие и кеширование для метод разложения.

  1. Идея сжатия состоит в том, что оптимальное решение α двойственной задачи SVM может содержать некоторые ограниченные элементы (т.е. α i = 0 или C). Эти элементы могли быть уже ограничены в середине итераций разложения. Чтобы сэкономить время обучения, метод сжатия пытается идентифицировать и удалить некоторые ограниченные элементы, поэтому решается меньшая проблема оптимизации.
  2. Идея кэширования - это эффективный метод сокращения вычислительного времени метода декомпозиции, поэтому элементы вычисляются по мере необходимости. Мы можем использовать доступную память (называемую кешем ядра) для хранения недавно использованных перестановка матрицы Q ij. Тогда некоторые элементы ядра , возможно, не потребуется пересчитывать.

Дополнительные сведения о математическом подходе см. В этой статье, раздел 5 Сжатие и кеширование.


Техническое подтверждение:

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

Использование сжатия и кеширования

Значение по умолчанию параметра shrinking в sklearn SVC равно установлен на True, оставив это как есть, произвел следующий вывод:

со столом усадки

График дает:

с усаживающимся участком

Обратите внимание, как в какой-то момент время заметно падает, отражая эффект сжатия и кеширования.

Без использования сжатия и кеширования

Используя тот же точный подход, но на этот раз, явно задав для параметра shrinking значение False следующим образом:

 clf = SVC(
        C=best_params['C'],
        kernel=str('linear'),
        probability=False,
        class_weight='balanced',
        max_iter=max_iter,
        random_state=2018,
        verbose=True,
        shrinking=False
        )

Получил следующий вывод:

без усадки таблицы

График дает:

без усадки

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


Сравнение корреляций Пирсона

Корреляция Пирсона


В заключении:

Без использования сжатия и кеширования (обновлено позже с кешированием) линейность улучшилась, хотя она не на 100% линейна, но если принять во внимание, что Scikit-Learn внутренне использует libsvm библиотека для обработки всех вычислений. И эта библиотека обернута с использованием C и Cython, вы будете более терпимо относиться к вашему определению «линейной» связи между maximum iterations и time. Кроме того, здесь - это отличное обсуждение того, почему алгоритмы не могут каждый раз давать одно и то же точное определенное время выполнения.


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

интервальный график с усадкой

При этом он сохраняет практически тот же поток без использования уловок оптимизации.

интервальный график без усадки


Важное обновление

Оказалось, что вышеупомянутая причина этой проблемы (например, сжатие и кеширование) верна, или, точнее, это очень важный фактор этого явления.

Но я упустил следующее:

Я говорил о сжатии и кешировании, но пропустил более поздний параметр для кеширования, который по умолчанию установлен на 200 МБ.

Повторение одного и того же моделирования более одного раза и установка параметра cache_size на очень маленькое число (потому что ноль недопустим и вызывает ошибку) в дополнение к shrinking=False, привело к чрезвычайно близкому -to линейный узор между max_iter и time:

clf = SVC(
        C=best_params['C'],
        kernel=str('linear'),
        probability=False,
        class_weight='balanced',
        max_iter=max_iter,
        random_state=2018,
        verbose=False,
        shrinking=False,
        cache_size = 0.000000001
        )

окончательный результат


Между прочим, вам не нужно устанавливать verbose=True, вы можете проверить, достигло ли оно максимальной итерации, с помощью ConvergenceWarning, поэтому вы можете перенаправить эти предупреждения в файл, и вам будет в миллион раз легче следить, просто добавьте этот код :

import warnings, sys
def customwarn(message, category, filename, lineno, file=None, line=None):
    with open('warnings.txt', 'a') as the_file:
        the_file.write(warnings.formatwarning(message, category, filename, lineno))
warnings.showwarning = customwarn

Также вам не нужно повторно генерировать набор данных после каждой итерации, поэтому выньте его из цикла следующим образом:

(X, y) = main(row_size, 40)
    for it in m_iter:
        ....
        ....

Заключительный вывод

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

person Yahya    schedule 02.10.2018
comment
1. Похоже, что использование shrink=False вообще не меняет время обучения для меня. 2. Каков интервал в вашем последнем сюжете? - person Richie F.; 02.10.2018
comment
@RichieF. Под интервалом я подразумеваю разницу между временем каждой итерации. - person Yahya; 02.10.2018
comment
@RichieF. Теперь вы даже можете увидеть эффект, используя shrinking или / и caching - person Yahya; 03.10.2018