【问题标题】:How can I capture return value with Python timeit module?如何使用 Python timeit 模块捕获返回值?
【发布时间】:2014-09-08 20:08:18
【问题描述】:

我正在使用 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”,因为我以后可能想做多线程或多处理。

【问题讨论】:

  • 如果你强制使用number=1,为什么还要使用timeittimeit 对于自动处理 repetitive 计时很有用,在这种情况下,您不知道应该运行多少时间才能获得良好的计时等。在您的情况下,只需使用 time 就可以了并且您不需要任何 hack 即可获得返回值。
  • 你能提供一个示例链接让我看看你指的是什么吗?我谷歌时间,你可能正在谈论的模块似乎只涉及格式化日期和时区等
  • 从未听说过time.time()?还是time.clock()timeit 模块使用这些函数来执行计时。如果您只需要进行 one 计时,则可以直接调用它们,就像在 unutbu 答案中使用 _timer 函数一样(实际上是对 time.time 或 @987654334 的引用@ 取决于操作系统)。
  • @Bakuriu 我知道 timeit 还可以做其他事情,比如关闭垃圾收集以确保我们进行公平比较。也就是说,我们正在查看执行时间,而不是挂起时间。

标签: python python-2.7 scikit-learn timeit


【解决方案1】:

对于 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's answer 适用于 python 3.4 但不适用于 3.5,因为 _template_func 函数似乎已在 3.5 中删除

【讨论】:

    【解决方案2】:

    问题归结为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 的行为,当callable 被传递timeit.Timer。如果您传递一个字符串语句,那么您必须(类似地)猴子修补 timeit.template 字符串。

    【讨论】:

    • 嗯,这似乎是返回函数而不是函数返回值。但我要做的是用 ret_val = t.timeit(number=1)[1]() 捕获它以实际运行该函数并让我取回该值。这不是运行该函数两次吗?
    • 鉴于您发布的代码,我不明白为什么 t.timeit 应该返回一个函数。当你运行我发布的代码时,你得到的结果和我一样吗?如果是这样,那么您需要比较该代码和您的代码之间的不同之处(特别注意传递和返回的对象的 type。)
    • 你说得对,我仍在使用 timeit.Timer( lambda: dummy) 而不仅仅是 timeit.Timer( dummy)。 StackOverflow 上有一些非常聪明的人。该死的,我喜欢这个网站。
    • 从看源码看timeit;看来该模块的目的是在命令行中将其用作优化代码和 Python 本身的测试工具。如果您正在编写应用程序来测试某些东西;说 API 调用的速度,你可能会更好地使用 time.perf_counter 两次并对两个数字进行减法。
    【解决方案3】:

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

    我通过编写一个函数解决了如下问题:

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

    假设你想计时:

    clf = RandomForest(train_input, train_output)
    

    然后做:

    clf = time_fn( RandomForest, train_input, train_output )
    

    标准输出将显示如下内容:

    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
    

    【讨论】:

      【解决方案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)
      

      【讨论】:

      • 好镜头,绝对是更优雅的解决方案,它还允许将字典传递给timeit.Timer。谢谢分享
      【解决方案5】:

      截至 2020 年,在 ipython 或 jupyter notebook 中是

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

      【讨论】:

      • 您正在混合结果:OP 想要定时函数 clf 的结果,以便不运行此函数两​​次(一次获取结果,一次获取时间),而不是“魔术”timeit IPython 函数的结果(-o 确实提供了)。
      【解决方案6】:

      如果您不想对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]
      

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

      【讨论】:

        【解决方案7】:

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

        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
        

        然后我将装饰器用于我想要计时的功能。

        【讨论】:

          【解决方案8】:

          对于 Python 3.X,我使用这种方法:

          # 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
          

          【讨论】:

            猜你喜欢
            • 2012-01-03
            • 2014-01-25
            • 2016-09-09
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多