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

Im работает несколько алгоритмов машинного обучения со sklearn в цикле for и хочет увидеть, как долго каждый из них занимает. Проблема в том, что мне также нужно вернуть значение и не хочу запускать его больше одного раза, потому что каждый алгоритм занимает так много времени. Есть ли способ захватить возвращаемое значение "clf", используя модуль timeit python или аналогичный с такой функцией...

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", потому что я могу захотеть сделать многопоточность или многопроцессорность позже.

5 ответов


проблема сводится к 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 когда вызвать is прошло timeit.Timer. Если вы передадите строковый оператор, вам придется (аналогично) исправлять timeit.template строку.


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

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

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


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

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

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

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

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

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

мой подход будет что-то вроде:

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)

подход, который я использую, - это "добавить" время работы к результатам функции 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

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