From 375a475e7c60179c1acea1b87535ba5408c3a963 Mon Sep 17 00:00:00 2001 From: Aston Zhang Date: Thu, 26 Apr 2018 22:41:47 +0000 Subject: [PATCH] auto para done --- README.md | 2 +- chapter_gluon-advances/auto-parallelism.md | 74 ++++++++++++---------- chapter_gluon-advances/lazy-evaluation.md | 6 +- chapter_gluon-basics/use-gpu.md | 16 ++--- 4 files changed, 52 insertions(+), 46 deletions(-) diff --git a/README.md b/README.md index a6bbc830..a1e7395b 100644 --- a/README.md +++ b/README.md @@ -130,7 +130,7 @@ metric,指标 normalization,归一化 -operator,运算子 +operator,运算符 optimizer,优化器 diff --git a/chapter_gluon-advances/auto-parallelism.md b/chapter_gluon-advances/auto-parallelism.md index b6da6371..b9113e50 100644 --- a/chapter_gluon-advances/auto-parallelism.md +++ b/chapter_gluon-advances/auto-parallelism.md @@ -1,84 +1,85 @@ # 自动并行计算 -在[“惰性计算”](./lazy-evaluation.md)一节里我们提到MXNet后端会自动构建计算图。通过计算图,系统可以知道所有计算的依赖关系,并可以选择将没有依赖关系的多个任务并行执行来获得性能的提升。再次以[“惰性计算”](./lazy-evaluation.md)一节中的计算图(图8.1)为例。其中`a = nd.ones((1, 2))`和`b = nd.ones((1, 2))`这两步计算之间并没有依赖关系。因此,系统可以选择并行执行它们。 +在[“惰性计算”](./lazy-evaluation.md)一节里我们提到MXNet后端会自动构建计算图。通过计算图,系统可以知道所有计算的依赖关系,并可以选择将没有依赖关系的多个任务并行执行来获得性能的提升。以[“惰性计算”](./lazy-evaluation.md)一节中的计算图(图8.1)为例。其中`a = nd.ones((1, 2))`和`b = nd.ones((1, 2))`这两步计算之间并没有依赖关系。因此,系统可以选择并行执行它们。 +通常一个运算符会用掉一个CPU/GPU上所有计算资源。例如,`dot`操作符会用到所有CPU(即使是有多个CPU)或单个GPU上所有线程。因此在单CPU/GPU上并行运行多个运算符可能效果并不明显。本节中探讨的自动并行计算主要关注多CPU/GPU的并行计算,以及计算和通讯的并行。 -通常一个运算符,例如`+`或者`dot`,会用掉一个计算设备上所有计算资源。`dot`同样用到所有CPU的核(即使是有多个CPU)和单GPU上所有线程。因此在单设备上并行运行多个运算符可能效果并不明显。自动并行主要的用途是多设备的计算并行,和计算与通讯的并行。 +首先导入本节中实验所需的包。注意,我们需要至少一个GPU才能运行本节实验。 -【注意】本章需要至少一个GPU才能运行。 +```{.python .input} +import mxnet as mx +from mxnet import nd +from time import time +``` -## 多设备的并行计算 +## 多CPU/GPU的并行计算 -我们首先定义一个函数,它做10次矩阵乘法。 +我们先介绍多CPU/GPU的并行计算,例如程序中的计算既发生在CPU,又发生在GPU之上。 -```{.python .input} -from mxnet import nd +先定义一个函数,令它做10次矩阵乘法。 +```{.python .input} def run(x): - return [nd.dot(x,x) for i in range(10)] + return [nd.dot(x, x) for _ in range(10)] ``` -我们分别计算在CPU和GPU上运行时间 +接下来,分别在CPU和GPU上创建NDArray。 ```{.python .input} -from mxnet import gpu -from time import time - x_cpu = nd.random.uniform(shape=(2000, 2000)) -x_gpu = nd.random.uniform(shape=(6000, 6000), ctx=gpu(0)) -nd.waitall() +x_gpu = nd.random.uniform(shape=(6000, 6000), ctx=mx.gpu(0)) +``` -# 预热阶段。 -run(x_cpu) +然后,分别使用它们在CPU和GPU上运行`run`函数并打印所需时间。 + +```{.python .input} +run(x_cpu) # 预热开始。 run(x_gpu) -nd.waitall() +nd.waitall() # 预热结束。 start = time() run(x_cpu) nd.waitall() -print('Run on CPU: %f sec'%(time()-start)) +print('run on CPU: %f sec'%(time()-start)) start = time() run(x_gpu) nd.waitall() -print('Run on GPU: %f sec'%(time()-start)) +print('run on GPU: %f sec'%(time()-start)) ``` -我们去掉两次`run`之间的`waitall`,希望系统能自动并行这两个任务: +我们去掉`run(x_cpu)`和`run(x_gpu)`两个计算任务之间的`nd.waitall()`,希望系统能自动并行这两个任务。 ```{.python .input} start = time() run(x_cpu) run(x_gpu) nd.waitall() -print('Run on both CPU and GPU: %f sec'%(time()-start)) +print('run on both CPU and GPU: %f sec'%(time()-start)) ``` -可以看到两个一起执行时,总时间不是分开执行的总和。这个表示后端系统能有效并行执行它们。 +可以看到,当两个计算任务一起执行时,执行总时间小于它们分开执行的总和。这表示,MXNet能有效地在多CPU/GPU上自动并行计算。 -## 计算和通讯的并行 +## 计算和通讯的并行计算 -在多设备计算中,我们经常需要在设备之间复制数据。例如下面我们在GPU上计算,然后将结果复制回CPU。 +在多CPU/GPU计算中,我们经常需要在CPU/GPU之间复制数据,造成数据的通讯。举个例子,在下面例子中,我们在GPU上计算,然后将结果复制回CPU。我们分别打印GPU上计算时间和GPU到CPU的通讯时间。 ```{.python .input} -from mxnet import cpu - def copy_to_cpu(x): - """copy data to a device""" - return [y.copyto(cpu()) for y in x] + return [y.copyto(mx.cpu()) for y in x] start = time() y = run(x_gpu) nd.waitall() -print('Run on GPU: %f sec'%(time()-start)) +print('run on GPU: %f sec' % (time() - start)) start = time() copy_to_cpu(y) nd.waitall() -print('Copy to CPU: %f sec'%(time() - start)) +print('copy to CPU: %f sec' % (time() - start)) ``` -同样我们去掉运行和复制之间的`waitall`: +我们去掉计算和通讯之间的`waitall`函数,打印这两个任务完成的总时间。 ```{.python .input} start = time() @@ -89,16 +90,19 @@ t = time() - start print('Run on GPU then Copy to CPU: %f sec'%(time() - start)) ``` -可以看到总时间小于前面两者之和。这个任务稍微不同于上面,因为运行和复制之间有依赖关系。就是`y[i]`必须先计算好才能复制到CPU。但在计算`y[i]`的时候系统可以复制`y[i-1]`,从而获得总运行时间的减少。 +可以看到,执行计算和通讯的总时间小于两者分别执行的耗时之和。需要注意的是,这个计算并通讯的任务不同于前面多CPU/GPU的并行计算中的任务。这里的运行和通讯之间有依赖关系:`y[i]`必须先计算好才能复制到CPU。所幸的是,在计算`y[i]`的时候系统可以复制`y[i-1]`,从而减少计算和通讯的总运行时间。 ## 小结 -* MXNet能够自动并行执行没有数据依赖关系的任务从而提升系统性能。 +* MXNet能够通过自动并行计算提升计算性能,例如多CPU/GPU的并行和计算与通讯的并行。 + ## 练习 -* `run`里面计算了10次运算,他们也没有依赖关系。看看系统有没有自动并行执行他们 -* 试试有更加复杂数据依赖的任务,看看系统能不能得到正确的结果,而且性能有提升吗? +* 本节中定义的`run`函数里做了10次运算。它们之间也没有依赖关系。看看MXNet有没有自动并行执行它们。 + +* 试试包含更加复杂的数据依赖的计算任务。MXNet能不能得到正确结果并提升计算性能? + ## 扫码直达[讨论区](https://discuss.gluon.ai/t/topic/1883) diff --git a/chapter_gluon-advances/lazy-evaluation.md b/chapter_gluon-advances/lazy-evaluation.md index c805706e..7028f39c 100644 --- a/chapter_gluon-advances/lazy-evaluation.md +++ b/chapter_gluon-advances/lazy-evaluation.md @@ -52,7 +52,7 @@ print(y) print('workloads are completed: %f sec' % (time() - start)) ``` -的确,除非我们需要打印或者保存计算结果,我们基本无需关心目前结果在内存中是否已经计算好了。只要数据是保存在NDArray里并使用MXNet提供的运算子,MXNet后端将默认使用惰性计算来获取最高的计算性能。 +的确,除非我们需要打印或者保存计算结果,我们基本无需关心目前结果在内存中是否已经计算好了。只要数据是保存在NDArray里并使用MXNet提供的运算符,MXNet后端将默认使用惰性计算来获取最高的计算性能。 ## 用同步函数让前端等待计算结果 @@ -231,7 +231,9 @@ print('increased memory: %f MB' % (get_mem() - mem)) ## 小结 -* 惰性计算有助于提升计算性能。 +* MXNet包括用户直接用来交互的前端和系统用来执行计算的后端。 + +* MXNet能够通过惰性计算提升计算性能。 * 我们建议使用每个小批量训练或预测时至少使用一个同步函数,从而避免将过多计算任务同时添加进后端。 diff --git a/chapter_gluon-basics/use-gpu.md b/chapter_gluon-basics/use-gpu.md index e1cc9b1e..62e7cc94 100644 --- a/chapter_gluon-basics/use-gpu.md +++ b/chapter_gluon-basics/use-gpu.md @@ -32,7 +32,7 @@ from mxnet import gluon, nd import sys ``` -MXNet使用`context`来指定用来存储和计算的设备。默认情况下,MXNet会将数据开在主内存,然后利用CPU来计算。在MXNet中,CPU和GPU可分别由`mx.cpu()`和`mx.gpu()`来表示。需要注意的是,`mx.cpu()`表示所有的物理CPU和内存。这意味着计算上会尽量使用所有的CPU核。但`mx.gpu()`只代表一块显卡和相应的显卡内存。如果有多块GPU,我们用`mx.gpu(i)`来表示第$i$块GPU($i$从0开始)。 +MXNet使用`context`来指定用来存储和计算的CPU/GPU。默认情况下,MXNet会将数据开在主内存,然后利用CPU来计算。在MXNet中,CPU和GPU可分别由`mx.cpu()`和`mx.gpu()`来表示。需要注意的是,`mx.cpu()`表示所有的物理CPU和内存。这意味着计算上会尽量使用所有的CPU核。但`mx.gpu()`只代表一块显卡和相应的显卡内存。如果有多块GPU,我们用`mx.gpu(i)`来表示第$i$块GPU($i$从0开始)。 ```{.python .input n=3} [mx.cpu(), mx.gpu(), mx.gpu(1)] @@ -40,7 +40,7 @@ MXNet使用`context`来指定用来存储和计算的设备。默认情况下, ## NDArray的GPU计算 -每个NDArray都有一个`context`属性来表示它存在哪个设备上。默认情况下,NDArray存在CPU上。因此,之前我们每次打印NDArray的时候都会看到`@cpu(0)`这个标识。 +每个NDArray都有一个`context`属性来表示它存在哪个CPU/GPU上。默认情况下,NDArray存在CPU上。因此,之前我们每次打印NDArray的时候都会看到`@cpu(0)`这个标识。 ```{.python .input n=4} x = nd.array([1,2,3]) @@ -49,7 +49,7 @@ print('x: ', x, '\ncontext of x: ', x.context) ### GPU上的存储 -我们可以在创建NDArray的时候通过`ctx`指定存储设备。 +我们可以在创建NDArray的时候通过`ctx`指定存储的CPU/GPU。 ```{.python .input n=5} a = nd.array([1, 2, 3], ctx=mx.gpu()) @@ -59,7 +59,7 @@ c = nd.random.uniform(shape=(2, 3), ctx=mx.gpu(1)) print('a: ', a, '\nb: ', b, '\nc: ', c) ``` -我们可以通过`copyto`和`as_in_context`函数在设备之间传输数据。 +我们可以通过`copyto`和`as_in_context`函数在CPU/GPU之间传输数据。 ```{.python .input n=7} y = x.copyto(mx.gpu()) @@ -80,13 +80,13 @@ print('z_target and z share memory? ', z_target is z) ### GPU上的计算 -MXNet的计算会在数据的`context`上执行。为了使用GPU计算,我们只需要事先将数据放在GPU上面。而计算结果会自动保存在相同的设备上。 +MXNet的计算会在数据的`context`上执行。为了使用GPU计算,我们只需要事先将数据放在GPU上面。而计算结果会自动保存在相同的GPU上。 ```{.python .input n=9} nd.exp(z + 2) * y ``` -注意,MXNet要求计算的所有输入数据都在同一个设备上。这个设计的原因是设备之间的数据交互通常比较耗时。因此,MXNet希望用户确切地指明计算的输入数据都在同一个设备上。例如,如果将CPU上的`x`和GPU上的`y`做运算,会出现错误信息。 +注意,MXNet要求计算的所有输入数据都在同一个CPU/GPU上。这个设计的原因是不同CPU/GPU之间的数据交互通常比较耗时。因此,MXNet希望用户确切地指明计算的输入数据都在同一个CPU/GPU上。例如,如果将CPU上的`x`和GPU上的`y`做运算,会出现错误信息。 ### 其他复制到主内存的操作 @@ -100,7 +100,7 @@ print(y.sum().asscalar()) ## Gluon的GPU计算 -同NDArray类似,Gluon的大部分函数可以通过`ctx`指定设备。下面代码将模型参数初始化在GPU上。 +同NDArray类似,Gluon的大部分函数可以通过`ctx`指定CPU/GPU。下面代码将模型参数初始化在GPU上。 ```{.python .input n=12} net = gluon.nn.Sequential() @@ -123,7 +123,7 @@ net[0].weight.data() ## 小结 -* 通过`context`,我们可以在不同的设备上存储数据和计算。 +* 通过`context`,我们可以在不同的CPU/GPU上存储数据和计算。 ## 练习 -- GitLab