Как я могу получить возвращаемое значение с помощью модуля Python Timeit?

Я запускаю несколько алгоритмов машинного обучения со sklearn в цикле for и хочу посмотреть, сколько времени займет каждый из них. Проблема в том, что мне также нужно возвращать значение, и НЕ нужно запускать его более одного раза, потому что каждый алгоритм занимает так много времени. Есть ли способ получить возвращаемое значение 'clf' с помощью модуля Python Timeit или аналогичного с такой функцией...

def RandomForest(train_input, train_output):
    clf = ensemble.RandomForestClassifier(n_estimators=10)
    clf.fit(train_input, train_output)
    return clf

когда я вызываю такую функцию

t = Timer(lambda : RandomForest(trainX,trainy))
print t.timeit(number=1)

P.S. Я также не хочу устанавливать глобальный "clf", потому что я мог бы хотеть сделать многопоточность или многопроцессорность позже.

Ответ 1

Проблема сводится к тому, что timeit._template_func не возвращает возвращаемое значение функции:

def _template_func(setup, func):
    """Create a timer function. Used if the "statement" is a callable."""
    def inner(_it, _timer, _func=func):
        setup()
        _t0 = _timer()
        for _i in _it:
            _func()
        _t1 = _timer()
        return _t1 - _t0
    return inner

Мы можем согнуть timeit к нашей воле с помощью небольшого количества исправлений:

import timeit
import time

def _template_func(setup, func):
    """Create a timer function. Used if the "statement" is a callable."""
    def inner(_it, _timer, _func=func):
        setup()
        _t0 = _timer()
        for _i in _it:
            retval = _func()
        _t1 = _timer()
        return _t1 - _t0, retval
    return inner

timeit._template_func = _template_func

def foo():
    time.sleep(1)
    return 42

t = timeit.Timer(foo)
print(t.timeit(number=1))

возвращает

(1.0010340213775635, 42)

Первое значение - результат timeit (в секундах), второе значение - возвращаемое значение функции.

Обратите внимание, что вышеприведенный патч обезьяны влияет только на поведение timeit, когда передан вызываемый вызов timeit.Timer. Если вы передадите строковый оператор, то вам придется (аналогичным образом) обезьянько-патчить строку timeit.template.

Ответ 2

Как ни странно, я также занимаюсь машинным обучением и имею аналогичные требования ;-)

Я решил это следующим образом, написав функцию, которая:

  • выполняет вашу функцию
  • печатает время выполнения вместе с названием вашей функции
  • возвращает результаты

Допустим, вы хотите время:

clf = RandomForest(train_input, train_output)

Затем выполните:

clf = time_fn( RandomForest, train_input, train_output )

Stdout покажет что-то вроде:

mymodule.RandomForest: 0.421609s

Код для time_fn:

import time

def time_fn( fn, *args, **kwargs ):
    start = time.clock()
    results = fn( *args, **kwargs )
    end = time.clock()
    fn_name = fn.__module__ + "." + fn.__name__
    print fn_name + ": " + str(end-start) + "s"
    return results

Ответ 3

Для Python 3.5 вы можете переопределить значение timeit.template

timeit.template = """
def inner(_it, _timer{init}):
    {setup}
    _t0 = _timer()
    for _i in _it:
        retval = {stmt}
    _t1 = _timer()
    return _t1 - _t0, retval
"""

unutbu answer работает для python 3.4, но не 3.5, так как функция _template_func, кажется, была удалена в 3.5

Ответ 4

Если я хорошо понимаю, после Python 3.5 вы можете определять глобалы в каждом экземпляре Timer, не определяя их в своем блоке кода. Я не уверен, что у него будут те же проблемы с распараллеливанием.

Мой подход будет примерно таким:

clf = ensemble.RandomForestClassifier(n_estimators=10)
myGlobals = globals()
myGlobals.update({'clf'=clf})
t = Timer(stmt='clf.fit(trainX,trainy)', globals=myGlobals)
print(t.timeit(number=1))
print(clf)

Ответ 5

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

def timed(func):
    def func_wrapper(*args, **kwargs):
        import time
        s = time.clock()
        result = func(*args, **kwargs)
        e = time.clock()
        return result + (e-s,)
    return func_wrapper

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

Ответ 6

Для Python 3.X я использую этот подход:

# Redefining default Timer template to make 'timeit' return
#     test execution timing and the function return value
new_template = """
def inner(_it, _timer{init}):
    {setup}
    _t0 = _timer()
    for _i in _it:
        ret_val = {stmt}
    _t1 = _timer()
    return _t1 - _t0, ret_val
"""
timeit.template = new_template