我如何用Python timeit模块捕获返回值



我使用sklearn在for循环中运行了几个机器学习算法,想看看每个算法需要多长时间。问题是我也需要返回一个值,不想要运行它不止一次,因为每个算法需要这么长时间。是否有一种方法可以使用python的timeit模块或类似的函数捕获返回值'clf'…

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)

注:我也不想设置一个全局的'clf',因为我可能想要做多线程或多处理以后。

对于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中删除

问题归结为时间问题。_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.Timer时影响timeit的行为。如果您传递一个字符串语句,那么您必须(类似地)对timeit.template字符串进行猴式补丁。

有趣的是,我也在做机器学习,并且有类似的需求;-)

我通过写一个函数来解决它,如下:

  • 运行你的函数
  • 打印运行时间,以及函数的名称
  • 返回结果

假设你想要time:

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之后,您可以在每个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)

截至2020年,在ippython或jupyter笔记本中,它是

t = %timeit -n1 -r1 -o RandomForest(trainX, trainy)
t.best

如果您不想对timeit进行猴式补丁,您可以尝试使用全局列表,如下所示。这也可以在python 2.7中工作,它在timeit()中没有globals参数:

from timeit import timeit
import time
# Function to time - plaigiarised from answer above :-)
def foo():
    time.sleep(1)
    return 42
result = []
print timeit('result.append(foo())', setup='from __main__ import result, foo', number=1)
print result[0]

将先打印时间,然后再打印结果。

我使用的一种方法是将运行时间"附加"到计时函数的结果。因此,我使用"time"模块编写了一个非常简单的装饰器:

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

然后我对我想要计时的函数使用装饰符

最初的问题希望允许多个结果、多线程和多处理。对于所有这些问题,一个队列就可以解决。

# put the result to the queue inside the function, via globally named qname
def RandomForest(train_input, train_output):
    clf = ensemble.RandomForestClassifier(n_estimators=10)
    clf.fit(train_input, train_output)
    global resultq
    resultq.put(clf)
    return clf
# put the result to the queue inside the function, to a queue parameter
def RandomForest(train_input, train_output,resultq):
    clf = ensemble.RandomForestClassifier(n_estimators=10)
    clf.fit(train_input, train_output)
    resultq.put(clf)
    return clf
# put the result to the queue outside the function
def RandomForest(train_input, train_output):
    clf = ensemble.RandomForestClassifier(n_estimators=10)
    clf.fit(train_input, train_output)
    return clf

#usage:
#     global resultq
#     t=RandomForest(train_input, train_output)
#     resultq.put(t)
# in a timeit usage, add an import for the resultq into the setup.
setup="""
from __main__ import resultq
"""
# # in __main__  # #
#  for multiprocessing and/or mulithreading
import multiprocessing as mp
global resultq=mp.Queue() # The global keyword is unnecessary if in __main__ ' Doesn't hurt
# Alternatively, 
# for multithreading only
import queue
global resultq=queue.Queue() # The global keyword is unnecessary if in __main__ ' Doesn't hurt
#   do processing
# eventually, drain the queue
while not resultq.empty():
  aclf=resultq.get()
  print(aclf)

对于Python 3。我使用这种方法:

# Redefining default Timer template to make 'timeit' return
#     test's 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

相关内容

  • 没有找到相关文章

最新更新