在 PyTorch 中对 CPU(而非 GPU)上的深度模型进行基准测试的最佳实践?

Moh*_*mba 3 python pytorch

我不太确定如何在 PyTorch 中测量 CPU 上深度模型的执行时间,仅用于推理。我在这里列出了其中一些,但它们可能不准确。如果需要,请更正它们,并在需要时提及更多。我在 PyTorch 版本 1.3.1 和 Intel Xeon 上运行,具有 64GB RAM、3.5GHz 处理器和 8 个内核。

  1. 我们应该使用time.time()?

    • 我知道对于 GPU 来说,这是一个非常糟糕的主意。对于 GPU 我做如下
with torch.no_grad():
    wTime = 0
    start = torch.cuda.Event(enable_timing=True)
    end = torch.cuda.Event(enable_timing=True)
    out = model(input) # JUST FOR WARMUP

    start.record()
    for i in range(200):
        input = torch.rand(1,1,2048,2048).to(device)

#        beg = time.time() DO NOT USE FOR GPU

        got = net_amplifier(low,for_amplifier)

#        wTime+=time.time()-beg DO NOT USE FOR GPU
    end.record()
    torch.cuda.synchronize()

    print('execution time in MILLISECONDS: {}'.format(start.elapsed_time(end)/200))

Run Code Online (Sandbox Code Playgroud)

对于此代码执行是在 GPU 上完成的。如果我必须在 CPU 上运行它们,应该进行哪些更改?会time.time()吗?

  1. 我们应该使用volatile?
    • 我认为在 v0.3 之后现在不鼓励使用 if volatile。但是如果我使用 eval 模式和 no_grad() 是否仍然有帮助
input = Variable(torch.randn(1, 3, 227, 227), volatile=True) 
 model(input) 
Run Code Online (Sandbox Code Playgroud)
  1. 是否应该清除页面缓存?

    • 我知道这样做的一种方法是使用 sudo sh -c "/bin/echo 1 > /proc/sys/vm/drop_caches"
  2. 我应该删除nn.Sequential()并直接放入前部吗

所有使用 copy_ 的方法都需要一些时间来执行,尤其是在 CPU 上这可能会很慢。此外,nn.Sequential() 模块比仅在前向传递时执行它们要慢。我认为这是由于在执行 Sequential 模块时需要创建一些开销。

我在同一个链接上不明白的另一件事是

如果您遇到这些小数字的性能问题,您可以尝试使用 torch.set_flush_denormal(True) 在 CPU 上禁用非正规浮点数。

  1. 应该torch.set_num_threads(int)用吗?如果是,可以提供演示代码吗?

  2. 文档中These context managers are thread local, so they won’t work if you send work to another thread using the :module:`threading` module, etc.给出的含义是什么。

请列出在 CPU 中计算执行时间的更多问题。 谢谢

Szy*_*zke 5

  1. 我们应该使用 time.time() 吗?

是的,CPU没问题

  1. 我们应该使用 volatile 吗?

正如你所说,它已被弃用。由于0.4.0 torch.Tensor已与torch.Variable(它也已弃用)合并,因此torch.no_grad应使用上下文管理器。

  1. 是否应该清除页面缓存?

我不这么认为,除非你知道这是一个问题

  1. 我应该删除 nn.Sequential() 并直接放入前部吗

不,torch.nn.Sequential您的模型应该没有或可以忽略不计的性能负担。它的前进只是:

def forward(self, input):
    for module in self:
        input = module(input)
    return input
Run Code Online (Sandbox Code Playgroud)

如果您遇到这些小数字的性能问题,您可以尝试使用 torch.set_flush_denormal(True) 在 CPU 上禁用非正规浮点数。

刷新非正规数字(下溢的数字)意味着严格替换它们,0.0如果您有很多非常小的数字,这可能有助于提高您的性能。PyTorch 文档给出的示例

>>> torch.set_flush_denormal(True)
True
>>> torch.tensor([1e-323], dtype=torch.float64)
tensor([ 0.], dtype=torch.float64)
>>> torch.set_flush_denormal(False)
True
>>> torch.tensor([1e-323], dtype=torch.float64)
tensor(9.88131e-324 *
       [ 1.0000], dtype=torch.float64)
Run Code Online (Sandbox Code Playgroud)

应该使用 torch.set_num_threads(int) 吗?如果是,可以提供演示代码吗?

根据本文档,如果您不分配太多线程(可能最多与 CPU 中的内核数一样多,因此您可以尝试 8 个),这可能会有所帮助。

所以这段代码开头的部分可能会有所帮助:

torch.set_num_threads(8)
Run Code Online (Sandbox Code Playgroud)

您可能想检查数字,看看每个值是否有帮助以及有多大帮助。

这些上下文管理器是线程本地的,因此如果您使用 :module:threading 模块等将工作发送到另一个线程,它们将无法工作,如文档中所述。

如果您使用模块 liketorch.multiprocessing并运行torch.multiprocessing.spawn(或类似)并且您的进程之一不会进入上下文管理器块,则不会关闭渐变(在 的情况下torch.no_grad)。此外,如果您使用 Python 的线程,则只有遇到块的线程才会关闭(或打开,这取决于)渐变。

这段代码会让你一目了然:

import threading

import torch


def myfunc(i, tensor):
    if i % 2 == 0:
        with torch.no_grad():
            z = tensor * 2
    else:
        z = tensor * 2
    print(i, z.requires_grad)


if __name__ == "__main__":
    tensor = torch.randn(5, requires_grad=True)
    with torch.no_grad():
        for i in range(10):
            t = threading.Thread(target=myfunc, args=(i, tensor))
            t.start()
Run Code Online (Sandbox Code Playgroud)

哪些输出(顺序可能会有所不同):

0 False
1 True
2 False
3 True
4 False
6 False
5 True
7 True
8 False
9 True
Run Code Online (Sandbox Code Playgroud)

还要注意torch.no_grad()in__main__对产生的线程没有影响(也不会torch.enable_grad)。

请列出在 CPU 中计算执行时间的更多问题。

转换为torchscript(请参阅此处)可能会有所帮助,从针对您的架构的源代码构建 PyTorch 以及它的功能和大量其他内容,这个问题太宽泛了。