From 2b054f0f6f512fe56b53c618cf9f2f7c74269192 Mon Sep 17 00:00:00 2001 From: Aston Zhang Date: Sun, 5 Dec 2021 09:28:24 +0000 Subject: [PATCH 01/21] rm origins --- Jenkinsfile_origin | 87 ------------------------ config_origin.ini | 166 --------------------------------------------- index_origin.md | 56 --------------- setup_origin.py | 30 -------- 4 files changed, 339 deletions(-) delete mode 100644 Jenkinsfile_origin delete mode 100644 config_origin.ini delete mode 100644 index_origin.md delete mode 100644 setup_origin.py diff --git a/Jenkinsfile_origin b/Jenkinsfile_origin deleted file mode 100644 index dd4bb88c9..000000000 --- a/Jenkinsfile_origin +++ /dev/null @@ -1,87 +0,0 @@ -stage("Build and Publish") { - // such as d2l-en and d2l-zh - def REPO_NAME = env.JOB_NAME.split('/')[0] - // such as en and zh - def LANG = REPO_NAME.split('-')[1] - // The current branch or the branch this PR will merge into - def TARGET_BRANCH = env.CHANGE_TARGET ? env.CHANGE_TARGET : env.BRANCH_NAME - // such as d2l-en-master - def TASK = REPO_NAME + '-' + TARGET_BRANCH - node { - ws("workspace/${TASK}") { - checkout scm - // conda environment - def ENV_NAME = "${TASK}-${EXECUTOR_NUMBER}"; - - sh label: "Build Environment", script: """set -ex - conda env update -n ${ENV_NAME} -f static/build.yml - conda activate ${ENV_NAME} - pip uninstall -y d2lbook - pip install git+https://github.com/d2l-ai/d2l-book - pip list - nvidia-smi - """ - - sh label: "Sanity Check", script: """set -ex - conda activate ${ENV_NAME} - d2lbook build outputcheck tabcheck - """ - - sh label: "Execute Notebooks", script: """set -ex - conda activate ${ENV_NAME} - ./static/cache.sh restore _build/eval/data - d2lbook build eval - ./static/cache.sh store _build/eval/data - """ - - sh label: "Execute Notebooks [PyTorch]", script: """set -ex - conda activate ${ENV_NAME} - ./static/cache.sh restore _build/eval_pytorch/data - d2lbook build eval --tab pytorch - d2lbook build slides --tab pytorch - ./static/cache.sh store _build/eval_pytorch/data - """ - - sh label: "Execute Notebooks [TensorFlow]", script: """set -ex - conda activate ${ENV_NAME} - ./static/cache.sh restore _build/eval_tensorflow/data - export TF_CPP_MIN_LOG_LEVEL=3 - d2lbook build eval --tab tensorflow - ./static/cache.sh store _build/eval_tensorflow/data - """ - - sh label:"Build HTML", script:"""set -ex - conda activate ${ENV_NAME} - ./static/build_html.sh - """ - - sh label:"Build PDF", script:"""set -ex - conda activate ${ENV_NAME} - d2lbook build pdf - """ - - if (env.BRANCH_NAME == 'release') { - sh label:"Release", script:"""set -ex - conda activate ${ENV_NAME} - d2lbook build pkg - d2lbook deploy html pdf pkg colab sagemaker slides --s3 s3://en.d2l.ai/ - """ - - sh label:"Release d2l", script:"""set -ex - conda activate ${ENV_NAME} - pip install setuptools wheel twine - python setup.py bdist_wheel - # twine upload dist/* - """ - } else { - sh label:"Publish", script:"""set -ex - conda activate ${ENV_NAME} - d2lbook deploy html pdf slides --s3 s3://preview.d2l.ai/${JOB_NAME}/ - """ - if (env.BRANCH_NAME.startsWith("PR-")) { - pullRequest.comment("Job ${JOB_NAME}/${BUILD_NUMBER} is complete. \nCheck the results at http://preview.d2l.ai/${JOB_NAME}/") - } - } - } - } -} diff --git a/config_origin.ini b/config_origin.ini deleted file mode 100644 index 5d4697faa..000000000 --- a/config_origin.ini +++ /dev/null @@ -1,166 +0,0 @@ -[project] - -# The project name, used as the filename of the package and the PDF file. For -# example, if set to d2l-book, then will build d2l-book.zip and d2l-book.pdf -name = d2l-en - -# Book title. It will be displayed on the top-right of the HTML page and the -# front page of the PDF file -title = Dive into Deep Learning - -author = Aston Zhang, Zachary C. Lipton, Mu Li, and Alexander J. Smola - -copyright = 2021, All authors. Licensed under CC-BY-SA-4.0 and MIT-0. - -release = 0.16.1 - - -[build] - -# A list of wildcards to indicate the markdown files that need to be evaluated as -# Jupyter notebooks. -notebooks = *.md */*.md - -# A list of files that will be copied to the build folder. -resources = img/ d2l/ d2l.bib setup.py - -# Files that will be skipped. -exclusions = README.md STYLE_GUIDE.md INFO.md CODE_OF_CONDUCT.md CONTRIBUTING.md contrib/*md - -# If True (default), then will evaluate the notebook to obtain outputs. -eval_notebook = True - -tabs = mxnet, pytorch, tensorflow - -[html] - -# A list of links that is displayed on the navbar. A link consists of three -# items: name, URL, and a fontawesome icon -# (https://fontawesome.com/icons?d=gallery). Items are separated by commas. -# PDF, http://numpy.d2l.ai/d2l-en.pdf, fas fa-file-pdf, -header_links = Courses, https://courses.d2l.ai, fas fa-user-graduate, - PDF, https://d2l.ai/d2l-en.pdf, fas fa-file-pdf, - All Notebooks, https://d2l.ai/d2l-en.zip, fas fa-download, - Discuss, https://discuss.d2l.ai/c/5, fab fa-discourse, - GitHub, https://github.com/d2l-ai/d2l-en, fab fa-github, - 中文版, https://zh.d2l.ai, fas fa-external-link-alt - -favicon = static/favicon.png - -html_logo = static/logo-with-text.png - - -[pdf] - -# The file used to post-process the generated tex file. -post_latex = ./static/post_latex/main.py - -latex_logo = static/logo.png -main_font = Source Serif Pro -sans_font = Source Sans Pro -mono_font = Inconsolata - - -[library] - -version_file = d2l/__init__.py - -[library-mxnet] - -lib_file = d2l/mxnet.py -lib_name = np - -# Map from d2l.xx to np.xx -simple_alias = ones, zeros, arange, meshgrid, sin, sinh, cos, cosh, tanh, - linspace, exp, log, tensor -> array, normal -> random.normal, - rand -> random.rand, matmul -> dot, int32, float32, - concat -> concatenate, stack, abs, eye - -# Map from d2l.xx(a, *args, **kwargs) to a.xx(*args, **kwargs) -fluent_alias = numpy -> asnumpy, reshape, to -> as_in_context, reduce_sum -> sum, - argmax, astype - -alias = - size = lambda a: a.size - transpose = lambda a: a.T - -reverse_alias = - d2l.size\(([\w\_\d]+)\) -> \1.size - d2l.transpose\(([\w\_\d]+)\) -> \1.T - -[library-pytorch] - -lib_file = d2l/torch.py -lib_name = torch - -simple_alias = ones, zeros, tensor, arange, meshgrid, sin, sinh, cos, cosh, - tanh, linspace, exp, log, normal, rand, matmul, int32, float32, - concat -> cat, stack, abs, eye - -fluent_alias = numpy -> detach().numpy, size -> numel, reshape, to, - reduce_sum -> sum, argmax, astype -> type, transpose -> t -alias = - -reverse_alias = - -[library-tensorflow] - -lib_file = d2l/tensorflow.py -lib_name = tf - -simple_alias = reshape, ones, zeros, meshgrid, sin, sinh, cos, cosh, tanh, - linspace, exp, normal -> random.normal, rand -> random.uniform, - matmul, reduce_sum, argmax, tensor -> constant, - arange -> range, astype -> cast, int32, float32, transpose, - concat, stack, abs, eye - -fluent_alias = numpy, - -alias = - size = lambda a: tf.size(a).numpy() - -reverse_alias = - d2l.size\(([\w\_\d]+)\) -> tf.size(\1).numpy() - -[deploy] - -other_file_s3urls = s3://d2l-webdata/releases/d2l-en/d2l-en-0.7.0.zip - s3://d2l-webdata/releases/d2l-en/d2l-en-0.7.1.zip - -google_analytics_tracking_id = UA-96378503-10 - - -[colab] - -github_repo = mxnet, d2l-ai/d2l-en-colab - pytorch, d2l-ai/d2l-pytorch-colab - tensorflow, d2l-ai/d2l-tensorflow-colab - -replace_svg_url = img, http://d2l.ai/_images - -libs = mxnet, mxnet, -U mxnet-cu101==1.7.0 - mxnet, d2l, d2l==RELEASE - pytorch, d2l, d2l==RELEASE - tensorflow, d2l, d2l==RELEASE - - -[sagemaker] - -github_repo = mxnet, d2l-ai/d2l-en-sagemaker - pytorch, d2l-ai/d2l-pytorch-sagemaker - tensorflow, d2l-ai/d2l-tensorflow-sagemaker - -kernel = mxnet, conda_mxnet_p36 - pytorch, conda_pytorch_p36 - tensorflow, conda_tensorflow_p36 - -libs = mxnet, mxnet, -U mxnet-cu101==1.7.0 - mxnet, d2l, .. # installing d2l - pytorch, d2l, .. # installing d2l - tensorflow, d2l, .. # installing d2l - -[slides] - -top_right = - -github_repo = pytorch, d2l-ai/d2l-pytorch-slides diff --git a/index_origin.md b/index_origin.md deleted file mode 100644 index ae52852cb..000000000 --- a/index_origin.md +++ /dev/null @@ -1,56 +0,0 @@ ---- -source: https://github.com/d2l-ai/d2l-en/blob/master/index.md -commit: 9bf95b1 ---- - -Dive into Deep Learning -======================== - -```eval_rst -.. raw:: html - :file: frontpage.html -``` - - -```toc -:maxdepth: 1 - -chapter_preface/index -chapter_installation/index -chapter_notation/index -``` - - -```toc -:maxdepth: 2 -:numbered: - -chapter_introduction/index -chapter_preliminaries/index -chapter_linear-networks/index -chapter_multilayer-perceptrons/index -chapter_deep-learning-computation/index -chapter_convolutional-neural-networks/index -chapter_convolutional-modern/index -chapter_recurrent-neural-networks/index -chapter_recurrent-modern/index -chapter_attention-mechanisms/index -chapter_optimization/index -chapter_computational-performance/index -chapter_computer-vision/index -chapter_natural-language-processing-pretraining/index -chapter_natural-language-processing-applications/index -chapter_recommender-systems/index -chapter_generative-adversarial-networks/index -chapter_appendix-mathematics-for-deep-learning/index -chapter_appendix-tools-for-deep-learning/index - -``` - - -```toc -:maxdepth: 1 - -chapter_references/zreferences -``` - diff --git a/setup_origin.py b/setup_origin.py deleted file mode 100644 index 75efab865..000000000 --- a/setup_origin.py +++ /dev/null @@ -1,30 +0,0 @@ -""" ---- -source: https://github.com/d2l-ai/d2l-en/blob/master/setup.py -commit: 9e55a9c ---- -""" - -from setuptools import setup, find_packages -import d2l - -requirements = [ - 'jupyter', - 'numpy', - 'matplotlib', - 'pandas' -] - -setup( - name='d2l', - version=d2l.__version__, - python_requires='>=3.5', - author='D2L Developers', - author_email='d2l.devs@gmail.com', - url='https://d2l.ai', - description='Dive into Deep Learning', - license='MIT-0', - packages=find_packages(), - zip_safe=True, - install_requires=requirements, -) From 7b2a3f4f87d880b547cd9ca2480804b9bc658dd0 Mon Sep 17 00:00:00 2001 From: Aston Zhang <22279212+astonzhang@users.noreply.github.com> Date: Sun, 5 Dec 2021 01:29:16 -0800 Subject: [PATCH 02/21] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 4682b7baa..d9058efd0 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ [![Build Status](http://ci.d2l.ai/job/d2l-zh/job/master/badge/icon)](http://ci.d2l.ai/job/d2l-zh/job/master/) -[第一版:zh-v1.D2L.ai](https://zh-v1.d2l.ai/) | [第二版预览版:zh-v2.D2L.ai](https://zh-v2.d2l.ai) | 安装和使用书中源代码:[第一版](https://zh-v1.d2l.ai/chapter_prerequisite/install.html) [第二版](https://zh-v2.d2l.ai/chapter_installation/index.html) | 当前版本: v2.0.0-alpha2 +[第一版:zh-v1.D2L.ai](https://zh-v1.d2l.ai/) | [第二版预览版:zh.D2L.ai](https://zh.d2l.ai) | 安装和使用书中源代码:[第一版](https://zh-v1.d2l.ai/chapter_prerequisite/install.html) [第二版](https://zh.d2l.ai/chapter_installation/index.html) | 当前版本: v2.0.0-alpha2
理解深度学习的最佳方法是学以致用。
From 3016db956e0ec02d3f7f9db7e570997211527277 Mon Sep 17 00:00:00 2001 From: A-FM Date: Mon, 6 Dec 2021 13:38:37 +0800 Subject: [PATCH 03/21] Troubleshoot the data synchronization failure (#1021) * in-place * in-place --- chapter_computational-performance/multiple-gpus.md | 2 +- chapter_computational-performance/multiple-gpus_origin.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/chapter_computational-performance/multiple-gpus.md b/chapter_computational-performance/multiple-gpus.md index 0eb351fff..dbe58b8ba 100644 --- a/chapter_computational-performance/multiple-gpus.md +++ b/chapter_computational-performance/multiple-gpus.md @@ -221,7 +221,7 @@ def allreduce(data): for i in range(1, len(data)): data[0][:] += data[i].to(data[0].device) for i in range(1, len(data)): - data[i] = data[0].to(data[i].device) + data[i][:] = data[0].to(data[i].device) ``` 通过在不同设备上创建具有不同值的向量并聚合它们。 diff --git a/chapter_computational-performance/multiple-gpus_origin.md b/chapter_computational-performance/multiple-gpus_origin.md index df03e438e..d221df44e 100644 --- a/chapter_computational-performance/multiple-gpus_origin.md +++ b/chapter_computational-performance/multiple-gpus_origin.md @@ -218,7 +218,7 @@ def allreduce(data): for i in range(1, len(data)): data[0][:] += data[i].to(data[0].device) for i in range(1, len(data)): - data[i] = data[0].to(data[i].device) + data[i][:] = data[0].to(data[i].device) ``` Let us test this by creating vectors with different values on different devices and aggregate them. From b2db8400d2e5189260a3d5bf2be1f2a1f81fb113 Mon Sep 17 00:00:00 2001 From: xiaotinghe Date: Mon, 6 Dec 2021 22:36:00 +0800 Subject: [PATCH 04/21] [polish] chapter 6,7,13 peer review (#1030) --- chapter_computer-vision/anchor.md | 102 ++++++++--------- chapter_computer-vision/bounding-box.md | 16 +-- chapter_computer-vision/fcn.md | 24 ++-- chapter_computer-vision/fine-tuning.md | 64 +++++------ chapter_computer-vision/image-augmentation.md | 10 +- chapter_computer-vision/index.md | 2 +- chapter_computer-vision/kaggle-cifar10.md | 20 ++-- chapter_computer-vision/kaggle-dog.md | 8 +- chapter_computer-vision/neural-style.md | 106 +++++++++--------- .../object-detection-dataset.md | 2 +- chapter_computer-vision/ssd.md | 2 +- chapter_computer-vision/transposed-conv.md | 2 +- chapter_convolutional-modern/alexnet.md | 8 +- chapter_convolutional-modern/batch-norm.md | 10 +- chapter_convolutional-modern/densenet.md | 6 +- chapter_convolutional-modern/googlenet.md | 10 +- chapter_convolutional-modern/nin.md | 3 +- chapter_convolutional-modern/resnet.md | 4 +- .../channels.md | 4 +- .../conv-layer.md | 2 +- .../lenet.md | 6 +- .../pooling.md | 6 +- .../custom-layer.md | 6 +- 23 files changed, 208 insertions(+), 215 deletions(-) diff --git a/chapter_computer-vision/anchor.md b/chapter_computer-vision/anchor.md index 159c4d457..c7517faa9 100644 --- a/chapter_computer-vision/anchor.md +++ b/chapter_computer-vision/anchor.md @@ -1,19 +1,19 @@ # 锚框 :label:`sec_anchor` -目标检测算法通常会在输入图像中采样大量的区域,然后判断这些区域中是否包含我们感兴趣的目标,并调整区域边缘从而更准确地预测目标的*真实边界框*(ground-truth bounding box)。 +目标检测算法通常会在输入图像中采样大量的区域,然后判断这些区域中是否包含我们感兴趣的目标,并调整区域边界从而更准确地预测目标的*真实边界框*(ground-truth bounding box)。 不同的模型使用的区域采样方法可能不同。 -这里我们介绍其中的一种方法:它以每个像素为中心生成多个大小和宽高比(aspect ratio)不同的边界框。 -这些边界框被称为*锚框*(anchor box)我们将在 :numref:`sec_ssd`中基于锚框设计一个目标检测模型。 +这里我们介绍其中的一种方法:以每个像素为中心,生成多个缩放比和宽高比(aspect ratio)不同的边界框。 +这些边界框被称为*锚框*(anchor box)我们将在 :numref:`sec_ssd`中设计一个基于锚框的目标检测模型。 -首先,让我们修改打印精度,以获得更简洁的输出。 +首先,让我们修改输出精度,以获得更简洁的输出。 ```{.python .input} %matplotlib inline from d2l import mxnet as d2l from mxnet import gluon, image, np, npx -np.set_printoptions(2) # 精简打印精度 +np.set_printoptions(2) # 精简输出精度 npx.set_np() ``` @@ -23,19 +23,19 @@ npx.set_np() from d2l import torch as d2l import torch -torch.set_printoptions(2) # 精简打印精度 +torch.set_printoptions(2) # 精简输出精度 ``` ## 生成多个锚框 假设输入图像的高度为$h$,宽度为$w$。 -我们以图像的每个像素为中心生成不同形状的锚框:*比例*为$s\in (0, 1]$,*宽高比*(宽高比)为$r > 0$。 +我们以图像的每个像素为中心生成不同形状的锚框:*缩放比*为$s\in (0, 1]$,*宽高比*为$r > 0$。 那么[**锚框的宽度和高度分别是$ws\sqrt{r}$和$hs/\sqrt{r}$。**] 请注意,当中心位置给定时,已知宽和高的锚框是确定的。 -要生成多个不同形状的锚框,让我们设置一系列刻度$s_1,\ldots, s_n$和一系列宽高比$r_1,\ldots, r_m$。 +要生成多个不同形状的锚框,让我们设置许多缩放比(scale)取值$s_1,\ldots, s_n$和许多宽高比(aspect ratio)取值$r_1,\ldots, r_m$。 当使用这些比例和长宽比的所有组合以每个像素为中心时,输入图像将总共有$whnm$个锚框。 -尽管这些锚框可能会覆盖所有地面真实边界框,但计算复杂性很容易过高。 +这些锚框可能会覆盖所有真实边界框,但这么多的锚框很容易带来过高的计算复杂性。 在实践中,(**我们只考虑**)包含$s_1$或$r_1$的(**组合:**) (** @@ -45,8 +45,8 @@ $$(s_1, r_1), (s_1, r_2), \ldots, (s_1, r_m), (s_2, r_1), (s_3, r_1), \ldots, (s 也就是说,以同一像素为中心的锚框的数量是$n+m-1$。 对于整个输入图像,我们将共生成$wh(n+m-1)$个锚框。 -上述生成锚框的方法可以在以下`multibox_prior`函数中实现。 -我们指定输入图像、尺度列表和宽高比列表,然后此函数将返回所有的锚框。 +上述生成锚框的方法在下面的`multibox_prior`函数中实现。 +我们指定输入图像、尺寸列表和宽高比列表,然后此函数将返回所有的锚框。 ```{.python .input} #@save @@ -61,8 +61,8 @@ def multibox_prior(data, sizes, ratios): # 为了将锚点移动到像素的中心,需要设置偏移量。 # 因为一个像素的的高为1且宽为1,我们选择偏移我们的中心0.5 offset_h, offset_w = 0.5, 0.5 - steps_h = 1.0 / in_height # y轴上缩放步长 - steps_w = 1.0 / in_width # x轴上缩放步长 + steps_h = 1.0 / in_height # 在y轴上缩放步长 + steps_w = 1.0 / in_width # 在x轴上缩放步长 # 生成锚框的所有中心点 center_h = (d2l.arange(in_height, ctx=device) + offset_h) * steps_h @@ -103,8 +103,8 @@ def multibox_prior(data, sizes, ratios): # 为了将锚点移动到像素的中心,需要设置偏移量。 # 因为一个像素的的高为1且宽为1,我们选择偏移我们的中心0.5 offset_h, offset_w = 0.5, 0.5 - steps_h = 1.0 / in_height # y轴上缩放步长 - steps_w = 1.0 / in_width # x轴上缩放步长 + steps_h = 1.0 / in_height # 在y轴上缩放步长 + steps_w = 1.0 / in_width # 在x轴上缩放步长 # 生成锚框的所有中心点 center_h = (torch.arange(in_height, device=device) + offset_h) * steps_h @@ -154,10 +154,10 @@ Y = multibox_prior(X, sizes=[0.75, 0.5, 0.25], ratios=[1, 2, 0.5]) Y.shape ``` -将锚框变量`Y`的形状更改为(图像高度、图像宽度、以同一像素为中心的锚框的数量,4)后,我们就可以获得以指定像素的位置为中心的所有锚框了。 +将锚框变量`Y`的形状更改为(图像高度,图像宽度,以同一像素为中心的锚框的数量,4)后,我们可以获得以指定像素的位置为中心的所有锚框。 在接下来的内容中,我们[**访问以(250,250)为中心的第一个锚框**]。 它有四个元素:锚框左上角的$(x, y)$轴坐标和右下角的$(x, y)$轴坐标。 -将两个轴的坐标分别除以图像的宽度和高度后,所得的值就介于0和1之间。 +将两个轴的坐标各分别除以图像的宽度和高度后,所得的值介于0和1之间。 ```{.python .input} #@tab all @@ -165,7 +165,7 @@ boxes = Y.reshape(h, w, 5, 4) boxes[250, 250, 0, :] ``` -为了[**显示以图像中一个像素为中心的所有锚框**],我们定义了以下`show_bboxes`函数来在图像上绘制多个边界框。 +为了[**显示以图像中以某个像素为中心的所有锚框**],我们定义了下面的`show_bboxes`函数来在图像上绘制多个边界框。 ```{.python .input} #@tab all @@ -192,11 +192,11 @@ def show_bboxes(axes, bboxes, labels=None, colors=None): bbox=dict(facecolor=color, lw=0)) ``` -正如我们刚才看到的,变量`boxes`中$x$轴和$y$轴的坐标值已分别除以图像的宽度和高度。 +正如你所看到的,变量`boxes`中$x$轴和$y$轴的坐标值已分别除以图像的宽度和高度。 绘制锚框时,我们需要恢复它们原始的坐标值。 因此,我们在下面定义了变量`bbox_scale`。 -现在,我们可以绘制出图像中所有以(250、250)为中心的锚框了。 -如下所示,尺度为0.75且宽高比为1的蓝色锚框很好地围绕着图像中的狗。 +现在,我们可以绘制出图像中所有以(250,250)为中心的锚框了。 +如下所示,缩放比为0.75且宽高比为1的蓝色锚框很好地围绕着图像中的狗。 ```{.python .input} #@tab all @@ -208,19 +208,19 @@ show_bboxes(fig.axes, boxes[250, 250, :, :] * bbox_scale, 's=0.75, r=0.5']) ``` -## [**交并比(IoU)**] +## [**交并比(IoU)**] 我们刚刚提到某个锚框“较好地”覆盖了图像中的狗。 如果已知目标的真实边界框,那么这里的“好”该如何如何量化呢? 直观地说,我们可以衡量锚框和真实边界框之间的相似性。 -我们知道*Jaccard系数*可以衡量两组之间的相似性。 -给定集合$\mathcal{A}$和$\mathcal{B}$,他们的Jaccard系数是他们交集的大小除以他们并集的大小: +我们知道*杰卡德系数*(Jaccard)可以衡量两组之间的相似性。 +给定集合$\mathcal{A}$和$\mathcal{B}$,他们的杰卡德系数是他们交集的大小除以他们并集的大小: $$J(\mathcal{A},\mathcal{B}) = \frac{\left|\mathcal{A} \cap \mathcal{B}\right|}{\left| \mathcal{A} \cup \mathcal{B}\right|}.$$ 事实上,我们可以将任何边界框的像素区域视为一组像素。通 -过这种方式,我们可以通过其像素集的Jaccard索引来测量两个边界框的相似性。 -对于两个边界框,我们通常将他们的Jaccard指数称为*交并比*(intersection over union,IoU),即两个边界框相交面积与相并面积之比,如 :numref:`fig_iou`所示。 +过这种方式,我们可以通过其像素集的杰卡德系数来测量两个边界框的相似性。 +对于两个边界框,我们通常将它们的杰卡德系数称为*交并比*(intersection over union,IoU),即两个边界框相交面积与相并面积之比,如 :numref:`fig_iou`所示。 交并比的取值范围在0和1之间:0表示两个边界框无重合像素,1表示两个边界框完全重合。 ![交并比是两个边界框相交面积与相并面积之比。](../img/iou.svg) @@ -279,21 +279,21 @@ def box_iou(boxes1, boxes2): return inter_areas / union_areas ``` -## 标注训练数据的锚框 +## 在训练数据中标注锚框 :label:`subsec_labeling-anchor-boxes` 在训练集中,我们将每个锚框视为一个训练样本。 为了训练目标检测模型,我们需要每个锚框的*类别*(class)和*偏移量*(offset)标签,其中前者是与锚框相关的对象的类别,后者是真实边界框相对于锚框的偏移量。 -在预测期间,我们为每个图像生成多个锚框,预测所有锚框的类和偏移量,根据预测的偏移量调整它们的位置以获得预测的边界框,最后只输出符合特定条件的预测边界框。 +在预测时,我们为每个图像生成多个锚框,预测所有锚框的类别和偏移量,根据预测的偏移量调整它们的位置以获得预测的边界框,最后只输出符合特定条件的预测边界框。 -我们知道,目标检测训练集附带了“真实边界框”的位置及其包围物体类别的标签。 -要标记任何生成的锚框,我们可以参考“分配到的”最接近此锚框的真实边界框的位置和类别标签。 -在下文中,我们将介绍把最接近的真实边界框分配给锚框的算法。 +我们知道,目标检测训练集带有“真实边界框”的位置及其包围物体类别的标签。 +要标记任何生成的锚框,我们可以参考分配到的最接近此锚框的真实边界框的位置和类别标签。 +在下文中,我们将介绍一个算法,它能够把最接近的真实边界框分配给锚框。 ### [**将真实边界框分配给锚框**] 给定图像,假设锚框是$A_1, A_2, \ldots, A_{n_a}$,真实边界框是$B_1, B_2, \ldots, B_{n_b}$,其中$n_a \geq n_b$。 -让我们定义一个矩阵$\mathbf{X} \in \mathbb{R}^{n_a \times n_b}$,其中$i^\mathrm{th}$行和$j^\mathrm{th}$列中的元素$x_{ij}$是锚框$A_i$和真实边界框$B_j$的IoU。 +让我们定义一个矩阵$\mathbf{X} \in \mathbb{R}^{n_a \times n_b}$,其中第$i$行、第$j$列的元素$x_{ij}$是锚框$A_i$和真实边界框$B_j$的IoU。 该算法包含以下步骤: 1. 在矩阵$\mathbf{X}$中找到最大的元素,并将它的行索引和列索引分别表示为$i_1$和$j_1$。然后将真实边界框$B_{j_1}$分配给锚框$A_{i_1}$。这很直观,因为$A_{i_1}$和$B_{j_1}$是所有锚框和真实边界框配对中最相近的。在第一个分配完成后,丢弃矩阵中${i_1}^\mathrm{th}$行和${j_1}^\mathrm{th}$列中的所有元素。 @@ -311,7 +311,7 @@ def box_iou(boxes1, boxes2): ![将真实边界框分配给锚框。](../img/anchor-label.svg) :label:`fig_anchor_label` -此算法在以下`assign_anchor_to_bbox`函数中实现。 +此算法在下面的`assign_anchor_to_bbox`函数中实现。 ```{.python .input} #@save @@ -369,21 +369,21 @@ def assign_anchor_to_bbox(ground_truth, anchors, device, iou_threshold=0.5): ### 标记类别和偏移量 -现在我们可以为每个锚框标记分类和偏移量了。 +现在我们可以为每个锚框标记类别和偏移量了。 假设一个锚框$A$被分配了一个真实边界框$B$。 一方面,锚框$A$的类别将被标记为与$B$相同。 -另一方面,锚框$A$的偏移量将根据$B$和$A$中心坐标的相对位置、以及这两个框的相对大小进行标记。 -鉴于数据集内不同的框的位置和大小不同,我们可以对那些相对位置和大小应用变换,使其获得更均匀分布、易于适应的偏移量。 +另一方面,锚框$A$的偏移量将根据$B$和$A$中心坐标的相对位置以及这两个框的相对大小进行标记。 +鉴于数据集内不同的框的位置和大小不同,我们可以对那些相对位置和大小应用变换,使其获得分布更均匀且易于拟合的偏移量。 在这里,我们介绍一种常见的变换。 [**给定框$A$和$B$,中心坐标分别为$(x_a, y_a)$和$(x_b, y_b)$,宽度分别为$w_a$和$w_b$,高度分别为$h_a$和$h_b$。 -我们可以将$A$的偏移量标记为 +我们可以将$A$的偏移量标记为: $$\left( \frac{ \frac{x_b - x_a}{w_a} - \mu_x }{\sigma_x}, \frac{ \frac{y_b - y_a}{h_a} - \mu_y }{\sigma_y}, \frac{ \log \frac{w_b}{w_a} - \mu_w }{\sigma_w}, \frac{ \log \frac{h_b}{h_a} - \mu_h }{\sigma_h}\right),$$ **] -其中常量的默认值是 $\mu_x = \mu_y = \mu_w = \mu_h = 0, \sigma_x=\sigma_y=0.1$ 和 $\sigma_w=\sigma_h=0.2$。 +其中常量的默认值为 $\mu_x = \mu_y = \mu_w = \mu_h = 0, \sigma_x=\sigma_y=0.1$ , $\sigma_w=\sigma_h=0.2$。 这种转换在下面的 `offset_boxes` 函数中实现。 ```{.python .input} @@ -399,10 +399,10 @@ def offset_boxes(anchors, assigned_bb, eps=1e-6): return offset ``` -如果一个锚框没有被分配真实边界框,我们只需将锚框的类别标记为“背景”类(background)。 -背景类的锚框通常被称为“负类”锚框,其余的被称为“正类”锚框。 +如果一个锚框没有被分配真实边界框,我们只需将锚框的类别标记为“背景”(background)。 +背景类别的锚框通常被称为“负类”锚框,其余的被称为“正类”锚框。 我们使用真实边界框(`labels`参数)实现以下`multibox_target`函数,来[**标记锚框的类别和偏移量**](`anchors`参数)。 -此函数将背景类设置为零,然后将新类的整数索引递增一。 +此函数将背景类别的索引设置为零,然后将新类别的整数索引递增一。 ```{.python .input} #@save @@ -476,10 +476,10 @@ def multibox_target(anchors, labels): ### 一个例子 -让我们通过一个具体的例子来说明锚箱标签。 -我们在加载的图像中为狗和猫定义了地面真实边界框,其中第一个元素是类别(0代表狗,1代表猫),其余四个元素是左上角和右下角的$(x, y)$轴坐标(范围介于0和1之间)。 +让我们通过一个具体的例子来说明锚框标签。 +我们已经为加载图像中的狗和猫定义了真实边界框,其中第一个元素是类别(0代表狗,1代表猫),其余四个元素是左上角和右下角的$(x, y)$轴坐标(范围介于0和1之间)。 我们还构建了五个锚框,用左上角和右下角的坐标进行标记:$A_0, \ldots, A_4$(索引从0开始)。 -然后我们[**在图像中绘制这些地面真相边界框和锚框**]。 +然后我们[**在图像中绘制这些真实边界框和锚框**]。 ```{.python .input} #@tab all @@ -496,7 +496,7 @@ show_bboxes(fig.axes, anchors * bbox_scale, ['0', '1', '2', '3', '4']); 使用上面定义的`multibox_target`函数,我们可以[**根据狗和猫的真实边界框,标注这些锚框的分类和偏移量**]。 在这个例子中,背景、狗和猫的类索引分别为0、1和2。 -下面我们为锚框和真实边界框范例添加了维度。 +下面我们为锚框和真实边界框样本添加一个维度。 ```{.python .input} labels = multibox_target(np.expand_dims(anchors, axis=0), @@ -547,7 +547,7 @@ labels[0] ## 使用非极大值抑制预测边界框 :label:`subsec_predicting-bounding-boxes-nms` -在预测期间,我们先为图像生成多个锚框,再为这些锚框一一预测类别和偏移量。 +在预测时,我们先为图像生成多个锚框,再为这些锚框一一预测类别和偏移量。 一个“预测好的边界框”则根据其中某个带有预测偏移量的锚框而生成。 下面我们实现了`offset_inverse`函数,该函数将锚框和偏移量预测作为输入,并[**应用逆偏移变换来返回预测的边界框坐标**]。 @@ -616,7 +616,7 @@ def nms(boxes, scores, iou_threshold): ``` 我们定义以下`multibox_detection`函数来[**将非极大值抑制应用于预测边界框**]。 -如果你发现实现有点复杂,请不要担心:我们将在实现之后,马上用一个具体的示例来展示它是如何工作的。 +如果你发现实现有点复杂,请不要担心。我们将在实现之后,马上用一个具体的例子来展示它是如何工作的。 ```{.python .input} #@save @@ -738,7 +738,7 @@ output = multibox_detection(cls_probs.unsqueeze(dim=0), output ``` -删除-1(背景)类的预测边界框后,我们可以[**输出由非极大值抑制保存的最终预测边界框**]。 +删除-1类别(背景)的预测边界框后,我们可以[**输出由非极大值抑制保存的最终预测边界框**]。 ```{.python .input} #@tab all @@ -751,12 +751,12 @@ for i in d2l.numpy(output[0]): ``` 实践中,在执行非极大值抑制前,我们甚至可以将置信度较低的预测边界框移除,从而减少此算法中的计算量。 -我们也可以对非极大值抑制的输出结果进行后处理,例如,只保留置信度更高的结果作为最终输出。 +我们也可以对非极大值抑制的输出结果进行后处理。例如,只保留置信度更高的结果作为最终输出。 ## 小结 * 我们以图像的每个像素为中心生成不同形状的锚框。 -* 交并比(IoU)也被称为Jaccard指数,用于衡量两个边界框的相似性。它是相交面积与相并面积的比率。 +* 交并比(IoU)也被称为杰卡德系数,用于衡量两个边界框的相似性。它是相交面积与相并面积的比率。 * 在训练集中,我们需要给每个锚框两种类型的标签。一个是与锚框中目标检测的类别,另一个是锚框真实相对于边界框的偏移量。 * 在预测期间,我们可以使用非极大值抑制(NMS)来移除类似的预测边界框,从而简化输出。 @@ -765,7 +765,7 @@ for i in d2l.numpy(output[0]): 1. 在`multibox_prior`函数中更改`sizes`和`ratios`的值。生成的锚框有什么变化? 1. 构建并可视化两个IoU为0.5的边界框。它们是怎样重叠的? 1. 在 :numref:`subsec_labeling-anchor-boxes`和 :numref:`subsec_predicting-bounding-boxes-nms`中修改变量`anchors`,结果如何变化? -1. 非极大值抑制是一种贪婪的算法,它通过*移除*来抑制预测的边界框。是否存在一种可能,被移除的一些框实际上是有用的?如何修改这个算法来柔和地抑制?你可以参考Soft-NMS :cite:`Bodla.Singh.Chellappa.ea.2017`。 +1. 非极大值抑制是一种贪心算法,它通过*移除*来抑制预测的边界框。是否存在一种可能,被移除的一些框实际上是有用的?如何修改这个算法来柔和地抑制?你可以参考Soft-NMS :cite:`Bodla.Singh.Chellappa.ea.2017`。 1. 如果非手动,非最大限度的抑制可以被学习吗? :begin_tab:`mxnet` diff --git a/chapter_computer-vision/bounding-box.md b/chapter_computer-vision/bounding-box.md index 29aa61e83..12964be38 100644 --- a/chapter_computer-vision/bounding-box.md +++ b/chapter_computer-vision/bounding-box.md @@ -4,14 +4,14 @@ 在前面的章节(例如 :numref:`sec_alexnet`— :numref:`sec_googlenet`)中,我们介绍了各种图像分类模型。 在图像分类任务中,我们假设图像中只有一个主要物体对象,我们只关注如何识别其类别。 然而,很多时候图像里有多个我们感兴趣的目标,我们不仅想知道它们的类别,还想得到它们在图像中的具体位置。 -在计算机视觉里,我们将这类任务称为*目标检测*(object detection)或*对象识别*(object recognition)。 +在计算机视觉里,我们将这类任务称为*目标检测*(object detection)或*目标识别*(object recognition)。 目标检测在多个领域中被广泛使用。 -例如,在无人驾驶里,我们需要通过识别拍摄到的视频图像里的车辆、行人、道路和障碍的位置来规划行进线路。 +例如,在无人驾驶里,我们需要通过识别拍摄到的视频图像里的车辆、行人、道路和障碍物的位置来规划行进线路。 机器人也常通过该任务来检测感兴趣的目标。安防领域则需要检测异常目标,如歹徒或者炸弹。 在接下来的几节中,我们将介绍几种用于目标检测的深度学习方法。 -我们将首先介绍对象的*位置*。 +我们将首先介绍目标的*位置*。 ```{.python .input} %matplotlib inline @@ -54,10 +54,10 @@ d2l.plt.imshow(img); ## 边界框 在目标检测中,我们通常使用*边界框*(bounding box)来描述对象的空间位置。 -边界框是矩形的,由矩形左上角的$x$和$y$坐标以及右下角的坐标决定。 +边界框是矩形的,由矩形左上角的以及右下角的$x$和$y$坐标决定。 另一种常用的边界框表示方法是边界框中心的$(x, y)$轴坐标以及框的宽度和高度。 -在这里,我们[**定义在这两种表示之间进行转换的函数**]:`box_corner_to_center`从两角表示转换为中心宽度表示,而`box_center_to_corner`反之亦然。 +在这里,我们[**定义在这两种表示法之间进行转换的函数**]:`box_corner_to_center`从两角表示法转换为中心宽度表示法,而`box_center_to_corner`反之亦然。 输入参数`boxes`可以是长度为4的张量,也可以是形状为($n$,4)的二维张量,其中$n$是边界框的数量。 ```{.python .input} @@ -86,7 +86,7 @@ def box_center_to_corner(boxes): ``` 我们将根据坐标信息[**定义图像中狗和猫的边界框**]。 -图像中坐标的原点是图像的左上角,右侧和向下分别是$x$和$y$轴的正方向。 +图像中坐标的原点是图像的左上角,向右的方向为$x$轴的正方向,向下的方向为$y$轴的正方向。 ```{.python .input} #@tab all @@ -94,7 +94,7 @@ def box_center_to_corner(boxes): dog_bbox, cat_bbox = [60.0, 45.0, 378.0, 516.0], [400.0, 112.0, 655.0, 493.0] ``` -我们可以通过转换两次来验证两个边界框转换函数的正确性。 +我们可以通过转换两次来验证边界框转换函数的正确性。 ```{.python .input} #@tab all @@ -133,7 +133,7 @@ fig.axes.add_patch(bbox_to_rect(cat_bbox, 'red')); ## 练习 -1. 找到另一张图像,然后尝试标记包含该对象的边界框。比较标签边界框和类别:哪些通常需要更长时间? +1. 找到另一张图像,然后尝试标记包含该对象的边界框。比较标注边界框和标注类别哪个需要更长的时间? 1. 为什么`box_corner_to_center`和`box_center_to_corner`的输入参数的最内层维度总是4? :begin_tab:`mxnet` diff --git a/chapter_computer-vision/fcn.md b/chapter_computer-vision/fcn.md index 3c379c271..e8a01bc8b 100644 --- a/chapter_computer-vision/fcn.md +++ b/chapter_computer-vision/fcn.md @@ -1,10 +1,10 @@ # 全卷积网络 :label:`sec_fcn` -如 :numref:`sec_semantic_segmentation`中所介绍的那样,语义分割能对图像中的每个像素分类。 +如 :numref:`sec_semantic_segmentation`中所介绍的那样,语义分割是对图像中的每个像素分类。 *全卷积网络*(fully convolutional network,FCN)采用卷积神经网络实现了从图像像素到像素类别的变换 :cite:`Long.Shelhamer.Darrell.2015`。 -与我们之前在图像分类或目标检测部分介绍的卷积神经网络不同,全卷积网络将中间层特征图的高和宽变换回输入图像的尺寸:这是通过 :numref:`sec_transposed_conv`中引入的*转置卷积*(transposed convolution)实现的。 -因此,输出的类别预测与输入图像在像素级别上具有一一对应关系:给定空间维上的位置,通道维的输出即该位置对应像素的类别预测。 +与我们之前在图像分类或目标检测部分介绍的卷积神经网络不同,全卷积网络将中间层特征图的高和宽变换回输入图像的尺寸:这是通过在 :numref:`sec_transposed_conv`中引入的*转置卷积*(transposed convolution)实现的。 +因此,输出的类别预测与输入图像在像素级别上具有一一对应关系:通道维的输出即该位置对应像素的类别预测。 ```{.python .input} %matplotlib inline @@ -29,13 +29,13 @@ from torch.nn import functional as F 下面我们了解一下全卷积网络模型最基本的设计。 如 :numref:`fig_fcn`所示,全卷积网络先使用卷积神经网络抽取图像特征,然后通过$1\times 1$卷积层将通道数变换为类别个数,最后在 :numref:`sec_transposed_conv`中通过转置卷积层将特征图的高和宽变换为输入图像的尺寸。 -因此,模型输出与输入图像的高和宽相同,且最终输出的通道包含了该空间位置像素的类别预测。 +因此,模型输出与输入图像的高和宽相同,且最终输出通道包含了该空间位置像素的类别预测。 ![全卷积网络](../img/fcn.svg) :label:`fig_fcn` -下面,我们[**使用在ImageNet数据集上预训练的ResNet-18模型来提取图像特征**],并将该网络实例记为`pretrained_net`。 -该模型的最后几层包括全局平均汇聚层和全连接层,然而全卷积网络中不需要它们。 +下面,我们[**使用在ImageNet数据集上预训练的ResNet-18模型来提取图像特征**],并将该网络记为`pretrained_net`。 +ResNet-18模型的最后几层包括全局平均汇聚层和全连接层,然而全卷积网络中不需要它们。 ```{.python .input} pretrained_net = gluon.model_zoo.vision.resnet18_v2(pretrained=True) @@ -48,8 +48,8 @@ pretrained_net = torchvision.models.resnet18(pretrained=True) list(pretrained_net.children())[-3:] ``` -接下来,我们[**创建一个全卷积网络实例`net`**]。 -它复制了Resnet-18中大部分的预训练层,但除去最终的全局平均汇聚层和最接近输出的全连接层。 +接下来,我们[**创建一个全卷积网络`net`**]。 +它复制了ResNet-18中大部分的预训练层,除了最后的全局平均汇聚层和最接近输出的全连接层。 ```{.python .input} net = nn.HybridSequential() @@ -62,7 +62,7 @@ for layer in pretrained_net.features[:-2]: net = nn.Sequential(*list(pretrained_net.children())[:-2]) ``` -给定高度和宽度分别为320和480的输入,`net`的前向传播将输入的高和宽减小至原来的$1/32$,即10和15。 +给定高度为320和宽度为480的输入,`net`的前向传播将输入的高和宽减小至原来的$1/32$,即10和15。 ```{.python .input} X = np.random.uniform(size=(1, 3, 320, 480)) @@ -76,7 +76,7 @@ net(X).shape ``` 接下来,我们[**使用$1\times1$卷积层将输出通道数转换为Pascal VOC2012数据集的类数(21类)。**] -最后,我们需要(**将要素地图的高度和宽度增加32倍**),从而将其变回输入图像的高和宽。 +最后,我们需要(**将特征图的高度和宽度增加32倍**),从而将其变回输入图像的高和宽。 回想一下 :numref:`sec_padding`中卷积层输出形状的计算方法: 由于$(320-64+16\times2+32)/32=10$且$(480-64+16\times2+32)/32=15$,我们构造一个步幅为$32$的转置卷积层,并将卷积核的高和宽设为$64$,填充为$16$。 我们可以看到如果步幅为$s$,填充为$s/2$(假设$s/2$是整数)且卷积核的高和宽为$2s$,转置卷积核会将输入的高和宽分别放大$s$倍。 @@ -226,7 +226,7 @@ train_iter, test_iter = d2l.load_data_voc(batch_size, crop_size) ## [**训练**] 现在我们可以训练全卷积网络了。 -这里的损失函数和准确率计算与图像分类中的并没有本质上的不同,因为我们使用转置卷积层的通道来预测像素的类别,所以在损失计算中通道维是指定的。 +这里的损失函数和准确率计算与图像分类中的并没有本质上的不同,因为我们使用转置卷积层的通道来预测像素的类别,所以需要在损失计算中指定通道维。 此外,模型基于每个像素的预测类别是否正确来计算准确率。 ```{.python .input} @@ -331,7 +331,7 @@ d2l.show_images(imgs[::3] + imgs[1::3] + imgs[2::3], 3, n, scale=2); 1. 如果将转置卷积层改用Xavier随机初始化,结果有什么变化? 1. 调节超参数,能进一步提升模型的精度吗? 1. 预测测试图像中所有像素的类别。 -1. 最初的全卷积网络的论文中 :cite:`Long.Shelhamer.Darrell.2015`还使用了卷积神经网络的某些中间层的输出。试着实现这个想法。 +1. 最初的全卷积网络的论文中 :cite:`Long.Shelhamer.Darrell.2015`还使用了某些卷积神经网络中间层的输出。试着实现这个想法。 :begin_tab:`mxnet` [Discussions](https://discuss.d2l.ai/t/3298) diff --git a/chapter_computer-vision/fine-tuning.md b/chapter_computer-vision/fine-tuning.md index 482701594..97c8f203c 100644 --- a/chapter_computer-vision/fine-tuning.md +++ b/chapter_computer-vision/fine-tuning.md @@ -2,31 +2,31 @@ :label:`sec_fine_tuning` 在前面的一些章节中,我们介绍了如何在只有6万张图像的Fashion-MNIST训练数据集上训练模型。 -我们还描述了学术界当下使用最广泛的大规模图像数据集ImageNet,它有超过1,000万的图像和1,000类的物体。 +我们还描述了学术界当下使用最广泛的大规模图像数据集ImageNet,它有超过1000万的图像和1000类的物体。 然而,我们平常接触到的数据集的规模通常在这两者之间。 -假设我们想识别图片中不同类型的椅子,然后向用户推荐购买链接。 +假如我们想识别图片中不同类型的椅子,然后向用户推荐购买链接。 一种可能的方法是首先识别100把普通椅子,为每把椅子拍摄1000张不同角度的图像,然后在收集的影像数据集上训练一个分类模型。 尽管这个椅子数据集可能大于Fashion-MNIST数据集,但实例数量仍然不到ImageNet中的十分之一。 -这可能会导致这个椅子数据集上适合ImageNet的复杂模型过拟合。 -此外,由于训练示例数量有限,训练模型的准确性可能无法满足实际要求。 +适合ImageNet的复杂模型可能会在这个椅子数据集上过拟合。 +此外,由于训练样本数量有限,训练模型的准确性可能无法满足实际要求。 为了解决上述问题,一个显而易见的解决方案是收集更多的数据。 但是,收集和标记数据可能需要大量的时间和金钱。 -例如,为了收集ImageNet数据集,研究人员从研究资金中花费了数百万美元。 +例如,为了收集ImageNet数据集,研究人员花费了数百万美元的研究资金。 尽管目前的数据收集成本已大幅降低,但这一成本仍不能忽视。 另一种解决方案是应用*迁移学习*(transfer learning)将从*源数据集*学到的知识迁移到*目标数据集*。 -例如,尽管ImageNet数据集中的大多数图像与椅子无关,但在此数据集上训练的模型可能会提取更常规的图像特征,这有助于识别边缘、纹理、形状和对象合成。 -这些类似的功能也可能有效地识别椅子。 +例如,尽管ImageNet数据集中的大多数图像与椅子无关,但在此数据集上训练的模型可能会提取更通用的图像特征,这有助于识别边缘、纹理、形状和对象组合。 +这些类似的特征也可能有效地识别椅子。 ## 步骤 在本节中,我们将介绍迁移学习中的常见技巧:*微调*(fine-tuning)。如 :numref:`fig_finetune`所示,微调包括以下四个步骤: 1. 在源数据集(例如ImageNet数据集)上预训练神经网络模型,即*源模型*。 -1. 创建一个新的神经网络模型,即*目标模型*。这将复制源模型上的所有模型设计及其参数,但输出层除外。我们假定这些模型参数包含从源数据集中学到的知识,这些知识也将适用于目标数据集。我们还假设源模型的输出层与源数据集的标签密切相关;因此不在目标模型中使用该层。 -1. 向目标模型添加输出层,其输出数量是目标数据集中的类别数。然后随机初始化该层的模型参数。 +1. 创建一个新的神经网络模型,即*目标模型*。这将复制源模型上的所有模型设计及其参数(输出层除外)。我们假定这些模型参数包含从源数据集中学到的知识,这些知识也将适用于目标数据集。我们还假设源模型的输出层与源数据集的标签密切相关;因此不在目标模型中使用该层。 +1. 向目标模型添加输出层,其输出数是目标数据集中的类别数。然后随机初始化该层的模型参数。 1. 在目标数据集(如椅子数据集)上训练目标模型。输出层将从头开始进行训练,而所有其他层的参数将根据源模型的参数进行微调。 ![微调。](../img/finetune.svg) @@ -37,7 +37,7 @@ ## 热狗识别 让我们通过具体案例演示微调:热狗识别。 -我们将在一个小型数据集上微调ResNet模型,它已在ImageNet数据集上进行了预训练。 +我们将在一个小型数据集上微调ResNet模型。该模型已在ImageNet数据集上进行了预训练。 这个小型数据集包含数千张包含热狗和不包含热狗的图像,我们将使用微调模型来识别图像中是否包含热狗。 ```{.python .input} @@ -64,11 +64,11 @@ import os 我们使用的[**热狗数据集来源于网络**]。 该数据集包含1400张热狗的“正类”图像,以及包含尽可能多的其他食物的“负类”图像。 -两个类别的1000张图片用于训练,其余的则用于测试。 +含着两个类别的1000张图片用于训练,其余的则用于测试。 -解压下载的数据集后,我们获得了两个文件夹`hotdog/train`和`hotdog/test`。 -这两个文件夹都有`hotdog`(有热狗)和`not-hotdog`(无热狗)个子文件夹, -其中任何一个文件夹都包含相应类的图像。 +解压下载的数据集,我们获得了两个文件夹`hotdog/train`和`hotdog/test`。 +这两个文件夹都有`hotdog`(有热狗)和`not-hotdog`(无热狗)两个子文件夹, +子文件夹内都包含相应类的图像。 ```{.python .input} #@tab all @@ -94,7 +94,7 @@ train_imgs = torchvision.datasets.ImageFolder(os.path.join(data_dir, 'train')) test_imgs = torchvision.datasets.ImageFolder(os.path.join(data_dir, 'test')) ``` -下面显示了前8个正类示例和最后8张负类图片。 +下面显示了前8个正类样本图片和最后8张负类样本图片。 正如你所看到的,[**图像的大小和纵横比各有不同**]。 ```{.python .input} @@ -106,13 +106,13 @@ d2l.show_images(hotdogs + not_hotdogs, 2, 8, scale=1.4); 在训练期间,我们首先从图像中裁切随机大小和随机长宽比的区域,然后将该区域缩放为$224 \times 224$输入图像。 在测试过程中,我们将图像的高度和宽度都缩放到256像素,然后裁剪中央$224 \times 224$区域作为输入。 -此外,对于三个RGB(红、绿和蓝)颜色通道,我们*标准化*每个通道。 -具体而言,通道的平均值将从该通道的每个值中减去,然后将结果除以该通道的标准差。 +此外,对于RGB(红、绿和蓝)颜色通道,我们分别*标准化*每个通道。 +具体而言,该通道的每个值减去该通道的平均值,然后将结果除以该通道的标准差。 [~~数据增广~~] ```{.python .input} -# 使用三个RGB通道的均值和标准偏差,以标准化每个通道 +# 使用RGB通道的均值和标准差,以标准化每个通道 normalize = gluon.data.vision.transforms.Normalize( [0.485, 0.456, 0.406], [0.229, 0.224, 0.225]) @@ -131,7 +131,7 @@ test_augs = gluon.data.vision.transforms.Compose([ ```{.python .input} #@tab pytorch -# 使用三个RGB通道的均值和标准偏差,以标准化每个通道 +# 使用RGB通道的均值和标准差,以标准化每个通道 normalize = torchvision.transforms.Normalize( [0.485, 0.456, 0.406], [0.229, 0.224, 0.225]) @@ -150,7 +150,7 @@ test_augs = torchvision.transforms.Compose([ ### [**定义和初始化模型**] -我们使用在ImageNet数据集上预训练的Resnet-18作为源模型。 +我们使用在ImageNet数据集上预训练的ResNet-18作为源模型。 在这里,我们指定`pretrained=True`以自动下载预训练的模型参数。 如果你首次使用此模型,则需要连接互联网才能下载。 @@ -164,16 +164,16 @@ pretrained_net = torchvision.models.resnet18(pretrained=True) ``` :begin_tab:`mxnet` -预训练的源模型实例包含两个变量:`features`和`output`。 +预训练的源模型实例包含两个成员变量:`features`和`output`。 前者包含除输出层以外的模型的所有层,后者是模型的输出层。 此划分的主要目的是促进对除输出层以外所有层的模型参数进行微调。 -源模型的变量`output`如下所示。 +源模型的成员变量`output`如下所示。 :end_tab: :begin_tab:`pytorch` 预训练的源模型实例包含许多特征层和一个输出层`fc`。 此划分的主要目的是促进对除输出层以外所有层的模型参数进行微调。 -下面给出了源模型的变量`fc`。 +下面给出了源模型的成员变量`fc`。 :end_tab: ```{.python .input} @@ -185,21 +185,15 @@ pretrained_net.output pretrained_net.fc ``` -在ResNet的全局平均池化层后,全连接层汇集转换为ImageNet数据集的1000个类输出。 +在ResNet的全局平均汇聚层后,全连接层转换为ImageNet数据集的1000个类输出。 之后,我们构建一个新的神经网络作为目标模型。 它的定义方式与预训练源模型的定义方式相同,只是最终层中的输出数量被设置为目标数据集中的类数(而不是1000个)。 -在下面的代码中,目标模型实例`finetune_net`的变量特征中的模型参数被初始化为源模型相应层的模型参数。 -由于功能中的模型参数是在ImageNet数据集上预训练的,并且足够好,因此通常只需要较小的学习率即可微调这些参数。 +在下面的代码中,目标模型`finetune_net`中成员变量`features`的参数被初始化为源模型相应层的模型参数。 +由于模型参数是在ImageNet数据集上预训练的,并且足够好,因此通常只需要较小的学习率即可微调这些参数。 -变量输出中的模型参数是随机初始化的,通常需要更高的学习率才能从头开始训练。 -假设`Trainer`实例中的学习率为1,我们将迭代中变量输出中模型参数的学习率设置为10。 - -在下面的代码中,初始化目标模型实例`finetune_net`输出层之前的模型参数,以对源模型中相应层的参数进行建模。 -由于这些模型参数是通过ImageNet上的预训练获得的,因此它们很有效, -所以我们只需使用较小的学习率进行*微调*这样的预训练参数。 -相比之下,输出层中的模型参数是随机初始化的,通常需要更高的学习率,从头开始学习。 -这里,我们设基本学习率为$\eta$,迭代输出层学习率为$10\eta$。 +成员变量`output`的参数是随机初始化的,通常需要更高的学习率才能从头开始训练。 +假设`Trainer`实例中的学习率为$\eta$,我们将成员变量`output`中参数的学习率设置为$10\eta$。 ```{.python .input} finetune_net = gluon.model_zoo.vision.resnet18_v2(classes=2) @@ -314,7 +308,7 @@ for param in finetune_net.parameters(): param.requires_grad = False ``` -4. 事实上,`ImageNet`数据集中有一个“热狗”类。我们可以通过以下代码获取其输出层中的相应权重参数,但是我们怎样才能利用这个权重参数? +4. 事实上,`ImageNet`数据集中有一个“热狗”类别。我们可以通过以下代码获取其输出层中的相应权重参数,但是我们怎样才能利用这个权重参数? ```{.python .input} weight = pretrained_net.output.weight diff --git a/chapter_computer-vision/image-augmentation.md b/chapter_computer-vision/image-augmentation.md index 7b42c0472..e1d9d6ee8 100644 --- a/chapter_computer-vision/image-augmentation.md +++ b/chapter_computer-vision/image-augmentation.md @@ -28,7 +28,7 @@ from torch import nn ## 常用的图像增广方法 -我们对常用图像增广方法的探索中,我们将使用下面这个尺寸为$400\times 500$的图像作为示例。 +在对常用图像增广方法的探索时,我们将使用下面这个尺寸为$400\times 500$的图像作为示例。 ```{.python .input} d2l.set_figsize() @@ -55,7 +55,7 @@ def apply(img, aug, num_rows=2, num_cols=4, scale=1.5): ### 翻转和裁剪 -[**左右翻转图像**]通常不会改变对象的类别。这是最早和最广泛使用的图像增广方法之一。 +[**左右翻转图像**]通常不会改变对象的类别。这是最早且最广泛使用的图像增广方法之一。 接下来,我们使用`transforms`模块来创建`RandomFlipLeftRight`实例,这样就各有50%的几率使图像向左或向右翻转。 ```{.python .input} @@ -378,13 +378,13 @@ train_with_data_aug(train_augs, test_augs, net) ## 小结 -* 图像增广基于现有的训练数据生成随机图像,来提高模型的概化能力。 -* 为了在预测过程中得到确切的结果,我们通常对训练样本只进行图像增广,而在预测过程中不使用随机操作的图像增广。 +* 图像增广基于现有的训练数据生成随机图像,来提高模型的泛化能力。 +* 为了在预测过程中得到确切的结果,我们通常对训练样本只进行图像增广,而在预测过程中不使用带随机操作的图像增广。 * 深度学习框架提供了许多不同的图像增广方法,这些方法可以被同时应用。 ## 练习 -1. 在不使用图像增广的情况下训练模型:`train_with_data_aug(no_aug, no_aug)`。比较使用和不使用图像增广的训练结果和测试精度。这个对比实验能支持图像增广可以减轻过拟合的论点吗?为什么? +1. 在不使用图像增广的情况下训练模型:`train_with_data_aug(no_aug, no_aug)`。比较使用与不使用图像增广的训练结果和测试精度。这个对比实验能支持图像增广可以减轻过拟合的论点吗?为什么? 2. 在基于CIFAR-10数据集的模型训练中结合多种不同的图像增广方法。它能提高测试准确性吗? 3. 参阅深度学习框架的在线文档。它还提供了哪些其他的图像增广方法? diff --git a/chapter_computer-vision/index.md b/chapter_computer-vision/index.md index 2734c3e9d..fd66d341d 100644 --- a/chapter_computer-vision/index.md +++ b/chapter_computer-vision/index.md @@ -3,7 +3,7 @@ 近年来,深度学习一直是提高计算机视觉系统性能的变革力量。 无论是医疗诊断、自动驾驶,还是智能滤波器、摄像头监控,许多计算机视觉领域的应用都与我们当前和未来的生活密切相关。 -可以说,最先进的计算机视觉应用程序与深度学习几乎是不可分割的。 +可以说,最先进的计算机视觉应用与深度学习几乎是不可分割的。 有鉴于此,本章将重点介绍计算机视觉领域,并探讨最近在学术界和行业中具有影响力的方法和应用。 在 :numref:`chap_cnn`和 :numref:`chap_modern_cnn`中,我们研究了计算机视觉中常用的各种卷积神经网络,并将它们应用到简单的图像分类任务中。 diff --git a/chapter_computer-vision/kaggle-cifar10.md b/chapter_computer-vision/kaggle-cifar10.md index 8abcdd696..c4aa14b6b 100644 --- a/chapter_computer-vision/kaggle-cifar10.md +++ b/chapter_computer-vision/kaggle-cifar10.md @@ -3,10 +3,10 @@ 之前几节中,我们一直在使用深度学习框架的高级API直接获取张量格式的图像数据集。 但是在实践中,图像数据集通常以图像文件的形式出现。 -在本节中,我们将从原始图像文件开始,然后逐步组织、阅读,然后将它们转换为张量格式。 +在本节中,我们将从原始图像文件开始,然后逐步组织、读取并将它们转换为张量格式。 -我们在 :numref:`sec_image_augmentation`中对CIFAR-10数据集做了一个实验,这是计算机视觉领域中的一个重要的数据集。 -在本节中,我们将运用我们在前几节中学到的知识来参加涉及CIFAR-10图像分类问题的Kaggle竞赛,(**比赛的网址是https://www.kaggle.com/c/cifar-10**)。 +我们在 :numref:`sec_image_augmentation`中对CIFAR-10数据集做了一个实验。CIFAR-10是计算机视觉领域中的一个重要的数据集。 +在本节中,我们将运用我们在前几节中学到的知识来参加CIFAR-10图像分类问题的Kaggle竞赛,(**比赛的网址是https://www.kaggle.com/c/cifar-10**)。 :numref:`fig_kaggle_cifar10`显示了竞赛网站页面上的信息。 为了能提交结果,你需要首先注册Kaggle账户。 @@ -99,13 +99,13 @@ def read_csv_labels(fname): return dict(((name, label) for name, label in tokens)) labels = read_csv_labels(os.path.join(data_dir, 'trainLabels.csv')) -print('# 训练示例 :', len(labels)) +print('# 训练样本 :', len(labels)) print('# 类别 :', len(set(labels.values()))) ``` 接下来,我们定义`reorg_train_valid`函数来[**将验证集从原始的训练集中拆分出来**]。 -此函数中的参数`valid_ratio`是验证集中的示例数与原始训练集中的示例数之比。 -更具体地说,令$n$等于示例最少的类别中的图像数量,而$r$是比率。 +此函数中的参数`valid_ratio`是验证集中的样本数与原始训练集中的样本数之比。 +更具体地说,令$n$等于样本最少的类别中的图像数量,而$r$是比率。 验证集将为每个类别拆分出$\max(\lfloor nr\rfloor,1)$张图像。 让我们以`valid_ratio=0.1`为例,由于原始的训练集有50000张图像,因此`train_valid_test/train`路径中将有45000张图像用于训练,而剩下5000张图像将作为路径`train_valid_test/valid`中的验证集。 组织数据集后,同类别的图像将被放置在同一文件夹下。 @@ -121,9 +121,9 @@ def copyfile(filename, target_dir): #@save def reorg_train_valid(data_dir, labels, valid_ratio): """将验证集从原始的训练集中拆分出来""" - # 训练数据集中示例最少的类别中的示例数 + # 训练数据集中样本最少的类别中的样本数 n = collections.Counter(labels.values()).most_common()[-1][1] - # 验证集中每个类别的示例数 + # 验证集中每个类别的样本数 n_valid_per_label = max(1, math.floor(n * valid_ratio)) label_count = {} for train_file in os.listdir(os.path.join(data_dir, 'train')): @@ -166,7 +166,7 @@ def reorg_cifar10_data(data_dir, valid_ratio): 在这里,我们只将样本数据集的批量大小设置为32。 在实际训练和测试中,应该使用Kaggle竞赛的完整数据集,并将`batch_size`设置为更大的整数,例如128。 -我们将10%的训练示例作为调整超参数的验证集。 +我们将10%的训练样本作为调整超参数的验证集。 ```{.python .input} #@tab all @@ -233,7 +233,7 @@ transform_test = torchvision.transforms.Compose([ ## 读取数据集 -接下来,我们[**读取由原始图像组成的数据集**],每个示例都包括一张图片和一个标签。 +接下来,我们[**读取由原始图像组成的数据集**],每个样本都包括一张图片和一个标签。 ```{.python .input} train_ds, valid_ds, train_valid_ds, test_ds = [ diff --git a/chapter_computer-vision/kaggle-dog.md b/chapter_computer-vision/kaggle-dog.md index 33037dabe..406787a3e 100644 --- a/chapter_computer-vision/kaggle-dog.md +++ b/chapter_computer-vision/kaggle-dog.md @@ -6,7 +6,7 @@ 你需要一个Kaggle账户才能提交结果。 在这场比赛中,我们将识别120类不同品种的狗。 -这个数据集实际上是著名的ImageNet的数据集子集,却与 :numref:`sec_kaggle_cifar10`中CIFAR-10数据集中的图像不同。 +这个数据集实际上是著名的ImageNet的数据集子集。与 :numref:`sec_kaggle_cifar10`中CIFAR-10数据集中的图像不同, ImageNet数据集中的图像更高更宽,且尺寸不一。 ![狗的品种鉴定比赛网站,你可以通过单击“数据”选项卡来获得比赛数据集。](../img/kaggle-dog.jpg) @@ -33,7 +33,7 @@ import os ## 获取和整理数据集 -比赛数据集分为训练集和测试集,其中分别包含三个RGB(彩色)通道的10222和10357张JPEG图像。 +比赛数据集分为训练集和测试集,分别包含RGB(彩色)通道的10222张、10357张JPEG图像。 在训练数据集中,有120种犬类,如拉布拉多、贵宾、腊肠、萨摩耶、哈士奇、吉娃娃和约克夏等。 ### 下载数据集 @@ -203,9 +203,9 @@ test_iter = torch.utils.data.DataLoader(test_ds, batch_size, shuffle=False, ## [**微调预训练模型**] 同样,本次比赛的数据集是ImageNet数据集的子集。 -因此,我们可以使用 :numref:`sec_fine_tuning`中讨论的方法在完整ImageNet数据集上选择预训练的模型,然后使用该模型提取图像要素,以便将其输入到定制的小规模输出网络中。 +因此,我们可以使用 :numref:`sec_fine_tuning`中讨论的方法在完整ImageNet数据集上选择预训练的模型,然后使用该模型提取图像特征,以便将其输入到定制的小规模输出网络中。 深度学习框架的高级API提供了在ImageNet数据集上预训练的各种模型。 -在这里,我们选择预训练的ResNet-34模型,我们只需重复使用此模型的输出层(即提取的要素)的输入。 +在这里,我们选择预训练的ResNet-34模型,我们只需重复使用此模型的输出层(即提取的特征)的输入。 然后,我们可以用一个可以训练的小型自定义输出网络替换原始输出层,例如堆叠两个完全连接的图层。 与 :numref:`sec_fine_tuning`中的实验不同,以下内容不重新训练用于特征提取的预训练模型,这节省了梯度下降的时间和内存空间。 diff --git a/chapter_computer-vision/neural-style.md b/chapter_computer-vision/neural-style.md index 614ef8512..d9eea505f 100644 --- a/chapter_computer-vision/neural-style.md +++ b/chapter_computer-vision/neural-style.md @@ -1,40 +1,40 @@ -# 样式迁移 +# 风格迁移 -如果你是一位摄影爱好者,你也许接触过滤波器。它能改变照片的颜色样式,从而使风景照更加锐利或者令人像更加美白。但一个滤波器通常只能改变照片的某个方面。如果要照片达到理想中的样式,你可能需要尝试大量不同的组合。这个过程的复杂程度不亚于模型调参。 +如果你是一位摄影爱好者,你也许接触过滤波器。它能改变照片的颜色风格,从而使风景照更加锐利或者令人像更加美白。但一个滤波器通常只能改变照片的某个方面。如果要照片达到理想中的风格,你可能需要尝试大量不同的组合。这个过程的复杂程度不亚于模型调参。 -在本节中,我们将介绍如何使用卷积神经网络,自动将一个图像中的样式应用在另一图像之上,即*样式迁移*(style transfer) :cite:`Gatys.Ecker.Bethge.2016`。 -这里我们需要两张输入图像:一张是*内容图像*,另一张是*样式图像*。 -我们将使用神经网络修改内容图像,使其在样式上接近样式图像。 -例如, :numref:`fig_style_transfer`中的内容图像为本书作者在西雅图郊区的雷尼尔山国家公园拍摄的风景照,而样式图像则是一幅主题为秋天橡树的油画。 -最终输出的合成图像应用了样式图像的油画笔触让整体颜色更加鲜艳,同时保留了内容图像中物体主体的形状。 +在本节中,我们将介绍如何使用卷积神经网络,自动将一个图像中的风格应用在另一图像之上,即*风格迁移*(style transfer) :cite:`Gatys.Ecker.Bethge.2016`。 +这里我们需要两张输入图像:一张是*内容图像*,另一张是*风格图像*。 +我们将使用神经网络修改内容图像,使其在风格上接近风格图像。 +例如, :numref:`fig_style_transfer`中的内容图像为本书作者在西雅图郊区的雷尼尔山国家公园拍摄的风景照,而风格图像则是一幅主题为秋天橡树的油画。 +最终输出的合成图像应用了风格图像的油画笔触让整体颜色更加鲜艳,同时保留了内容图像中物体主体的形状。 -![输入内容图像和样式图像,输出样式迁移后的合成图像](../img/style-transfer.svg) +![输入内容图像和风格图像,输出风格迁移后的合成图像](../img/style-transfer.svg) :label:`fig_style_transfer` ## 方法 - :numref:`fig_style_transfer_model`用简单的例子阐述了基于卷积神经网络的样式迁移方法。 + :numref:`fig_style_transfer_model`用简单的例子阐述了基于卷积神经网络的风格迁移方法。 首先,我们初始化合成图像,例如将其初始化为内容图像。 -该合成图像是样式迁移过程中唯一需要更新的变量,即样式迁移所需迭代的模型参数。 +该合成图像是风格迁移过程中唯一需要更新的变量,即风格迁移所需迭代的模型参数。 然后,我们选择一个预训练的卷积神经网络来抽取图像的特征,其中的模型参数在训练中无须更新。 -这个深度卷积神经网络凭借多个层逐级抽取图像的特征,我们可以选择其中某些层的输出作为内容特征或样式特征。 -以 :numref:`fig_style_transfer_model`为例,这里选取的预训练的神经网络含有3个卷积层,其中第二层输出内容特征,第一层和第三层输出样式特征。 +这个深度卷积神经网络凭借多个层逐级抽取图像的特征,我们可以选择其中某些层的输出作为内容特征或风格特征。 +以 :numref:`fig_style_transfer_model`为例,这里选取的预训练的神经网络含有3个卷积层,其中第二层输出内容特征,第一层和第三层输出风格特征。 -![基于卷积神经网络的样式迁移。实线箭头和虚线箭头分别表示前向传播和反向传播](../img/neural-style.svg) +![基于卷积神经网络的风格迁移。实线箭头和虚线箭头分别表示前向传播和反向传播](../img/neural-style.svg) :label:`fig_style_transfer_model` -接下来,我们通过前向传播(实线箭头方向)计算样式迁移的损失函数,并通过反向传播(虚线箭头方向)迭代模型参数,即不断更新合成图像。 -样式迁移常用的损失函数由3部分组成: +接下来,我们通过前向传播(实线箭头方向)计算风格迁移的损失函数,并通过反向传播(虚线箭头方向)迭代模型参数,即不断更新合成图像。 +风格迁移常用的损失函数由3部分组成: (i)*内容损失*使合成图像与内容图像在内容特征上接近; -(ii)*样式损失*使合成图像与样式图像在样式特征上接近; -(iii)*总变差损失*则有助于减少合成图像中的噪点。 -最后,当模型训练结束时,我们输出样式迁移的模型参数,即得到最终的合成图像。 +(ii)*风格损失*使合成图像与风格图像在风格特征上接近; +(iii)*全变分损失*则有助于减少合成图像中的噪点。 +最后,当模型训练结束时,我们输出风格迁移的模型参数,即得到最终的合成图像。 -在下面,我们将通过代码来进一步了解样式迁移的技术细节。 +在下面,我们将通过代码来进一步了解风格迁移的技术细节。 -## [**阅读内容和样式图像**] +## [**阅读内容和风格图像**] -首先,我们读取内容和样式图像。 +首先,我们读取内容和风格图像。 从打印出的图像坐标轴可以看出,它们的尺寸并不一样。 ```{.python .input} @@ -126,12 +126,12 @@ pretrained_net = gluon.model_zoo.vision.vgg19(pretrained=True) pretrained_net = torchvision.models.vgg19(pretrained=True) ``` -为了抽取图像的内容特征和样式特征,我们可以选择VGG网络中某些层的输出。 +为了抽取图像的内容特征和风格特征,我们可以选择VGG网络中某些层的输出。 一般来说,越靠近输入层,越容易抽取图像的细节信息;反之,则越容易抽取图像的全局信息。 为了避免合成图像过多保留内容图像的细节,我们选择VGG较靠近输出的层,即*内容层*,来输出图像的内容特征。 -我们还从VGG中选择不同层的输出来匹配局部和全局的样式,这些图层也称为*样式层*。 +我们还从VGG中选择不同层的输出来匹配局部和全局的风格,这些图层也称为*风格层*。 正如 :numref:`sec_vgg`中所介绍的,VGG网络使用了5个卷积块。 -实验中,我们选择第四卷积块的最后一个卷积层作为内容层,选择每个卷积块的第一个卷积层作为样式层。 +实验中,我们选择第四卷积块的最后一个卷积层作为内容层,选择每个卷积块的第一个卷积层作为风格层。 这些层的索引可以通过打印`pretrained_net`实例获取。 ```{.python .input} @@ -139,7 +139,7 @@ pretrained_net = torchvision.models.vgg19(pretrained=True) style_layers, content_layers = [0, 5, 10, 19, 28], [25] ``` -使用VGG层抽取特征时,我们只需要用到从输入层到最靠近输出层的内容层或样式层之间的所有层。 +使用VGG层抽取特征时,我们只需要用到从输入层到最靠近输出层的内容层或风格层之间的所有层。 下面构建一个新的网络`net`,它只保留需要用到的VGG的所有层。 ```{.python .input} @@ -155,7 +155,7 @@ net = nn.Sequential(*[pretrained_net.features[i] for i in ``` 给定输入`X`,如果我们简单地调用前向传播`net(X)`,只能获得最后一层的输出。 -由于我们还需要中间层的输出,因此这里我们逐层计算,并保留内容层和样式层的输出。 +由于我们还需要中间层的输出,因此这里我们逐层计算,并保留内容层和风格层的输出。 ```{.python .input} #@tab all @@ -172,9 +172,9 @@ def extract_features(X, content_layers, style_layers): ``` 下面定义两个函数:`get_contents`函数对内容图像抽取内容特征; -`get_styles`函数对样式图像抽取样式特征。 -因为在训练时无须改变预训练的VGG的模型参数,所以我们可以在训练开始之前就提取出内容特征和样式特征。 -由于合成图像是样式迁移所需迭代的模型参数,我们只能在训练过程中通过调用`extract_features`函数来抽取合成图像的内容特征和样式特征。 +`get_styles`函数对风格图像抽取风格特征。 +因为在训练时无须改变预训练的VGG的模型参数,所以我们可以在训练开始之前就提取出内容特征和风格特征。 +由于合成图像是风格迁移所需迭代的模型参数,我们只能在训练过程中通过调用`extract_features`函数来抽取合成图像的内容特征和风格特征。 ```{.python .input} def get_contents(image_shape, device): @@ -203,8 +203,8 @@ def get_styles(image_shape, device): ## [**定义损失函数**] -下面我们来描述样式迁移的损失函数。 -它由内容损失、样式损失和总变差损失3部分组成。 +下面我们来描述风格迁移的损失函数。 +它由内容损失、风格损失和全变分损失3部分组成。 ### 内容损失 @@ -224,17 +224,17 @@ def content_loss(Y_hat, Y): return torch.square(Y_hat - Y.detach()).mean() ``` -### 样式损失 +### 风格损失 -样式损失与内容损失类似,也通过平方误差函数衡量合成图像与样式图像在样式上的差异。 -为了表达样式层输出的样式,我们先通过`extract_features`函数计算样式层的输出。 +风格损失与内容损失类似,也通过平方误差函数衡量合成图像与风格图像在风格上的差异。 +为了表达风格层输出的风格,我们先通过`extract_features`函数计算风格层的输出。 假设该输出的样本数为1,通道数为$c$,高和宽分别为$h$和$w$,我们可以将此输出转换为矩阵$\mathbf{X}$,其有$c$行和$hw$列。 -这个矩阵可以被看作是由$c$个长度为$hw$的向量$\mathbf{x}_1, \ldots, \mathbf{x}_c$组合而成的。其中向量$\mathbf{x}_i$代表了通道$i$上的样式特征。 +这个矩阵可以被看作是由$c$个长度为$hw$的向量$\mathbf{x}_1, \ldots, \mathbf{x}_c$组合而成的。其中向量$\mathbf{x}_i$代表了通道$i$上的风格特征。 -在这些向量的*格拉姆矩阵*$\mathbf{X}\mathbf{X}^\top \in \mathbb{R}^{c \times c}$中,$i$行$j$列的元素$x_{ij}$即向量$\mathbf{x}_i$和$\mathbf{x}_j$的内积。它表达了通道$i$和通道$j$上样式特征的相关性。我们用这样的格拉姆矩阵来表达样式层输出的样式。 +在这些向量的*格拉姆矩阵*$\mathbf{X}\mathbf{X}^\top \in \mathbb{R}^{c \times c}$中,$i$行$j$列的元素$x_{ij}$即向量$\mathbf{x}_i$和$\mathbf{x}_j$的内积。它表达了通道$i$和通道$j$上风格特征的相关性。我们用这样的格拉姆矩阵来表达风格层输出的风格。 需要注意的是,当$hw$的值较大时,格拉姆矩阵中的元素容易出现较大的值。 此外,格拉姆矩阵的高和宽皆为通道数$c$。 -为了让样式损失不受这些值的大小影响,下面定义的`gram`函数将格拉姆矩阵除以了矩阵中元素的个数,即$chw$。 +为了让风格损失不受这些值的大小影响,下面定义的`gram`函数将格拉姆矩阵除以了矩阵中元素的个数,即$chw$。 ```{.python .input} #@tab all @@ -244,7 +244,7 @@ def gram(X): return d2l.matmul(X, X.T) / (num_channels * n) ``` -自然地,样式损失的平方误差函数的两个格拉姆矩阵输入分别基于合成图像与样式图像的样式层输出。这里假设基于样式图像的格拉姆矩阵`gram_Y`已经预先计算好了。 +自然地,风格损失的平方误差函数的两个格拉姆矩阵输入分别基于合成图像与风格图像的风格层输出。这里假设基于风格图像的格拉姆矩阵`gram_Y`已经预先计算好了。 ```{.python .input} def style_loss(Y_hat, gram_Y): @@ -257,11 +257,11 @@ def style_loss(Y_hat, gram_Y): return torch.square(gram(Y_hat) - gram_Y.detach()).mean() ``` -### 总变差损失 +### 全变分损失 有时候,我们学到的合成图像里面有大量高频噪点,即有特别亮或者特别暗的颗粒像素。 -一种常见的去噪方法是*总变差去噪*(total variation denoising): -假设$x_{i, j}$表示坐标$(i, j)$处的像素值,降低总变差损失 +一种常见的去噪方法是*全变分去噪*(total variation denoising): +假设$x_{i, j}$表示坐标$(i, j)$处的像素值,降低全变分损失 $$\sum_{i, j} \left|x_{i, j} - x_{i+1, j}\right| + \left|x_{i, j} - x_{i, j+1}\right|$$ @@ -277,14 +277,14 @@ def tv_loss(Y_hat): ### 损失函数 [**风格转移的损失函数是内容损失、风格损失和总变化损失的加权和**]。 -通过调节这些权重超参数,我们可以权衡合成图像在保留内容、迁移样式以及去噪三方面的相对重要性。 +通过调节这些权重超参数,我们可以权衡合成图像在保留内容、迁移风格以及去噪三方面的相对重要性。 ```{.python .input} #@tab all content_weight, style_weight, tv_weight = 1, 1e3, 10 def compute_loss(X, contents_Y_hat, styles_Y_hat, contents_Y, styles_Y_gram): - # 分别计算内容损失、样式损失和总变差损失 + # 分别计算内容损失、风格损失和全变分损失 contents_l = [content_loss(Y_hat, Y) * content_weight for Y_hat, Y in zip( contents_Y_hat, contents_Y)] styles_l = [style_loss(Y_hat, Y) * style_weight for Y_hat, Y in zip( @@ -297,7 +297,7 @@ def compute_loss(X, contents_Y_hat, styles_Y_hat, contents_Y, styles_Y_gram): ## [**初始化合成图像**] -在样式迁移中,合成的图像是训练期间唯一需要更新的变量。因此,我们可以定义一个简单的模型`SynthesizedImage`,并将合成的图像视为模型参数。模型的前向传播只需返回模型参数即可。 +在风格迁移中,合成的图像是训练期间唯一需要更新的变量。因此,我们可以定义一个简单的模型`SynthesizedImage`,并将合成的图像视为模型参数。模型的前向传播只需返回模型参数即可。 ```{.python .input} class SynthesizedImage(nn.Block): @@ -320,7 +320,7 @@ class SynthesizedImage(nn.Module): return self.weight ``` -下面,我们定义`get_inits`函数。该函数创建了合成图像的模型实例,并将其初始化为图像`X`。样式图像在各个样式层的格拉姆矩阵`styles_Y_gram`将在训练前预先计算好。 +下面,我们定义`get_inits`函数。该函数创建了合成图像的模型实例,并将其初始化为图像`X`。风格图像在各个风格层的格拉姆矩阵`styles_Y_gram`将在训练前预先计算好。 ```{.python .input} def get_inits(X, device, lr, styles_Y): @@ -344,7 +344,7 @@ def get_inits(X, device, lr, styles_Y): ## [**训练模型**] -在训练模型进行样式迁移时,我们不断抽取合成图像的内容特征和样式特征,然后计算损失函数。下面定义了训练循环。 +在训练模型进行风格迁移时,我们不断抽取合成图像的内容特征和风格特征,然后计算损失函数。下面定义了训练循环。 ```{.python .input} def train(X, contents_Y, styles_Y, device, lr, num_epochs, lr_decay_epoch): @@ -396,7 +396,7 @@ def train(X, contents_Y, styles_Y, device, lr, num_epochs, lr_decay_epoch): ``` 现在我们[**训练模型**]: -首先将内容图像和样式图像的高和宽分别调整为300和450像素,用内容图像来初始化合成图像。 +首先将内容图像和风格图像的高和宽分别调整为300和450像素,用内容图像来初始化合成图像。 ```{.python .input} device, image_shape = d2l.try_gpu(), (450, 300) @@ -415,20 +415,20 @@ _, styles_Y = get_styles(image_shape, device) output = train(content_X, contents_Y, styles_Y, device, 0.3, 500, 50) ``` -我们可以看到,合成图像保留了内容图像的风景和物体,并同时迁移了样式图像的色彩。例如,合成图像具有与样式图像中一样的色彩块,其中一些甚至具有画笔笔触的细微纹理。 +我们可以看到,合成图像保留了内容图像的风景和物体,并同时迁移了风格图像的色彩。例如,合成图像具有与风格图像中一样的色彩块,其中一些甚至具有画笔笔触的细微纹理。 ## 小结 -* 样式迁移常用的损失函数由3部分组成:(i)内容损失使合成图像与内容图像在内容特征上接近;(ii)样式损失令合成图像与样式图像在样式特征上接近;(iii)总变差损失则有助于减少合成图像中的噪点。 +* 风格迁移常用的损失函数由3部分组成:(i)内容损失使合成图像与内容图像在内容特征上接近;(ii)风格损失令合成图像与风格图像在风格特征上接近;(iii)全变分损失则有助于减少合成图像中的噪点。 * 我们可以通过预训练的卷积神经网络来抽取图像的特征,并通过最小化损失函数来不断更新合成图像来作为模型参数。 -* 我们使用格拉姆矩阵表达样式层输出的样式。 +* 我们使用格拉姆矩阵表达风格层输出的风格。 ## 练习 -1. 选择不同的内容和样式层,输出有什么变化? +1. 选择不同的内容和风格层,输出有什么变化? 1. 调整损失函数中的权重超参数。输出是否保留更多内容或减少更多噪点? -1. 替换实验中的内容图像和样式图像,你能创作出更有趣的合成图像吗? -1. 我们可以对文本使用样式迁移吗?提示:你可以参阅调查报告 :cite:`Hu.Lee.Aggarwal.2020`。 +1. 替换实验中的内容图像和风格图像,你能创作出更有趣的合成图像吗? +1. 我们可以对文本使用风格迁移吗?提示:你可以参阅调查报告 :cite:`Hu.Lee.Aggarwal.2020`。 :begin_tab:`mxnet` [Discussions](https://discuss.d2l.ai/t/3299) diff --git a/chapter_computer-vision/object-detection-dataset.md b/chapter_computer-vision/object-detection-dataset.md index 31aa0058b..8eb0c39d5 100644 --- a/chapter_computer-vision/object-detection-dataset.md +++ b/chapter_computer-vision/object-detection-dataset.md @@ -165,7 +165,7 @@ batch = next(iter(train_iter)) batch[0].shape, batch[1].shape ``` -## [**示范**] +## [**演示**] 让我们展示10幅带有真实边界框的图像。 我们可以看到在所有这些图像中香蕉的旋转角度、大小和位置都有所不同。 diff --git a/chapter_computer-vision/ssd.md b/chapter_computer-vision/ssd.md index 387654a2f..a33169b2b 100644 --- a/chapter_computer-vision/ssd.md +++ b/chapter_computer-vision/ssd.md @@ -3,7 +3,7 @@ 在 :numref:`sec_bbox`— :numref:`sec_object-detection-dataset`中,我们分别介绍了边界框、锚框、多尺度目标检测和用于目标检测的数据集。 现在我们已经准备好使用这样的背景知识来设计一个目标检测模型:单发多框检测(SSD) :cite:`Liu.Anguelov.Erhan.ea.2016`。 -该模型简单、快速且被广泛使用。尽管这只是大量目标检测模型中的一个,但本节中的一些设计原则和实现细节也适用于其他模型。 +该模型简单、快速且被广泛使用。尽管这只是其中一种目标检测模型,但本节中的一些设计原则和实现细节也适用于其他模型。 ## 模型 diff --git a/chapter_computer-vision/transposed-conv.md b/chapter_computer-vision/transposed-conv.md index 8e5b5b3b0..646410179 100644 --- a/chapter_computer-vision/transposed-conv.md +++ b/chapter_computer-vision/transposed-conv.md @@ -8,7 +8,7 @@ 为了实现这一点,尤其是在空间维度被卷积神经网络层缩小后,我们可以使用另一种类型的卷积神经网络层,它可以增加上采样中间层特征图的空间维度。 在本节中,我们将介绍 *转置卷积*(transposed convolution) :cite:`Dumoulin.Visin.2016`, -用于扭转下采样导致的空间尺寸减小。 +用于逆转下采样导致的空间尺寸减小。 ```{.python .input} from mxnet import np, npx, init diff --git a/chapter_convolutional-modern/alexnet.md b/chapter_convolutional-modern/alexnet.md index cf456b364..bf0f14685 100644 --- a/chapter_convolutional-modern/alexnet.md +++ b/chapter_convolutional-modern/alexnet.md @@ -5,14 +5,14 @@ 在计算机视觉中,直接将神经网络与其他机器学习方法进行比较也许不公平。这是因为,卷积神经网络的输入是由原始像素值或是经过简单预处理(例如居中、缩放)的像素值组成的。但在使用传统机器学习方法时,从业者永远不会将原始像素作为输入。在传统机器学习方法中,计算机视觉流水线是由经过人的手工精心设计的特征流水线组成的。对于这些传统方法,大部分的进展都来自于对特征有了更聪明的想法,并且学习到的算法往往归于事后的解释。 -虽然上世纪90年代就有了一些神经网络加速器,但仅靠它们还不足以开发出有大量参数的深层多通道多层卷积神经网络。此外,当时的数据集仍然相对较小。除了这些障碍,训练神经网络的一些关键技巧仍然缺失,包括启发式参数初始化、随机梯度下降的巧妙变体、非挤压激活函数和有效的正则化技术。 +虽然上世纪90年代就有了一些神经网络加速卡,但仅靠它们还不足以开发出有大量参数的深层多通道多层卷积神经网络。此外,当时的数据集仍然相对较小。除了这些障碍,训练神经网络的一些关键技巧仍然缺失,包括启发式参数初始化、随机梯度下降的变体、非挤压激活函数和有效的正则化技术。 因此,与训练*端到端*(从像素到分类结果)系统不同,经典机器学习的流水线看起来更像下面这样: 1. 获取一个有趣的数据集。在早期,收集这些数据集需要昂贵的传感器(在当时最先进的图像也就100万像素)。 2. 根据光学、几何学、其他知识以及偶然的发现,手工对特征数据集进行预处理。 -3. 通过标准的特征提取算法(如SIFT(尺度不变特征变换) :cite:`Lowe.2004`、SURF(加速鲁棒特征) :cite:`Bay.Tuytelaars.Van-Gool.2006`或其他手动调整的流水线来输入数据。 -4. 将提取的特征放到最喜欢的分类器中(例如线性模型或其它核方法),以训练分类器。 +3. 通过标准的特征提取算法,如SIFT(尺度不变特征变换) :cite:`Lowe.2004`和SURF(加速鲁棒特征) :cite:`Bay.Tuytelaars.Van-Gool.2006`或其他手动调整的流水线来输入数据。 +4. 将提取的特征送入最喜欢的分类器中(例如线性模型或其它核方法),以训练分类器。 如果你和机器学习研究人员交谈,你会发现他们相信机器学习既重要又美丽:优雅的理论去证明各种模型的性质。机器学习是一个正在蓬勃发展、严谨且非常有用的领域。然而,如果你和计算机视觉研究人员交谈,你会听到一个完全不同的故事。他们会告诉你图像识别的诡异事实————推动领域进步的是数据特征,而不是学习算法。计算机视觉研究人员相信,从对最终模型精度的影响来说,更大或更干净的数据集、或是稍微改进的特征提取,比任何学习算法带来的进步要大得多。 @@ -67,7 +67,7 @@ AlexNet使用了8层卷积神经网络,并以很大的优势赢得了2012年Im AlexNet和LeNet的架构非常相似,如 :numref:`fig_alexnet`所示。 注意,这里我们提供了一个稍微精简版本的AlexNet,去除了当年需要两个小型GPU同时运算的设计特点。 -![从LeNet(左)到AlexNet(right)](../img/alexnet.svg) +![从LeNet(左)到AlexNet(右)](../img/alexnet.svg) :label:`fig_alexnet` AlexNet和LeNet的设计理念非常相似,但也存在显著差异。 diff --git a/chapter_convolutional-modern/batch-norm.md b/chapter_convolutional-modern/batch-norm.md index 1031ee495..74a023864 100644 --- a/chapter_convolutional-modern/batch-norm.md +++ b/chapter_convolutional-modern/batch-norm.md @@ -91,7 +91,7 @@ $$\mathbf{h} = \phi(\mathrm{BN}(\mathbf{W}\mathbf{x} + \mathbf{b}) ).$$ 首先,将训练好的模型用于预测时,我们不再需要样本均值中的噪声以及在微批次上估计每个小批次产生的样本方差了。 其次,例如,我们可能需要使用我们的模型对逐个样本进行预测。 一种常用的方法是通过移动平均估算整个训练数据集的样本均值和方差,并在预测时使用它们得到确定的输出。 -可见,和dropout一样,批量规范化层在训练模式和预测模式下的计算结果也是不一样的。 +可见,和暂退法一样,批量规范化层在训练模式和预测模式下的计算结果也是不一样的。 ## (**从零实现**) @@ -460,9 +460,9 @@ d2l.train_ch6(net, train_iter, test_iter, num_epochs, lr, d2l.try_gpu()) ## 争议 直观地说,批量规范化被认为可以使优化更加平滑。 -然而,我们必须小心区分投机直觉和对我们观察到的现象的真实解释。 +然而,我们必须小心区分直觉和对我们观察到的现象的真实解释。 回想一下,我们甚至不知道简单的神经网络(多层感知机和传统的卷积神经网络)为什么如此有效。 -即使在dropout和权重衰减的情况下,它们仍然非常灵活,因此无法通过常规的学习理论泛化保证来解释它们是否能够泛化到看不见的数据。 +即使在暂退法和权重衰减的情况下,它们仍然非常灵活,因此无法通过常规的学习理论泛化保证来解释它们是否能够泛化到看不见的数据。 在提出批量规范化的论文中,作者除了介绍了其应用,还解释了其原理:通过减少*内部协变量偏移*(internal covariate shift)。 据推测,作者所说的“内部协变量转移”类似于上述的投机直觉,即变量值的分布在训练过程中会发生变化。 @@ -486,7 +486,7 @@ Ali Rahimi在接受2017年NeurIPS大会的“接受时间考验奖”(Test of * 在模型训练过程中,批量规范化利用小批量的均值和标准差,不断调整神经网络的中间输出,使整个神经网络各层的中间输出值更加稳定。 * 批量规范化在全连接层和卷积层的使用略有不同。 -* 批量规范化层和dropout层一样,在训练模式和预测模式下计算不同。 +* 批量规范化层和暂退层一样,在训练模式和预测模式下计算不同。 * 批量规范化有许多有益的副作用,主要是正则化。另一方面,”减少内部协变量偏移“的原始动机似乎不是一个有效的解释。 ## 练习 @@ -496,7 +496,7 @@ Ali Rahimi在接受2017年NeurIPS大会的“接受时间考验奖”(Test of 1. 绘制训练和测试准确度的提高。 1. 你的学习率有多高? 1. 我们是否需要在每个层中进行批量规范化?尝试一下? -1. 你可以通过批量规范化来替换dropout吗?行为如何改变? +1. 你可以通过批量规范化来替换暂退法吗?行为如何改变? 1. 确定参数`beta`和`gamma`,并观察和分析结果。 1. 查看高级API中有关`BatchNorm`的在线文档,以查看其他批量规范化的应用。 1. 研究思路:想想你可以应用的其他“规范化”转换?你可以应用概率积分变换吗?全秩协方差估计如何? diff --git a/chapter_convolutional-modern/densenet.md b/chapter_convolutional-modern/densenet.md index dd2abc15b..8cf60ab53 100644 --- a/chapter_convolutional-modern/densenet.md +++ b/chapter_convolutional-modern/densenet.md @@ -13,7 +13,7 @@ $$f(x) = f(0) + f'(0) x + \frac{f''(0)}{2!} x^2 + \frac{f'''(0)}{3!} x^3 + \ld $$f(\mathbf{x}) = \mathbf{x} + g(\mathbf{x}).$$ -也就是说,ResNet将$f$分解为两部分:一个简单的线性项和一个更复杂的非线性项。 +也就是说,ResNet将$f$分解为两部分:一个简单的线性项和一个复杂的非线性项。 那么再向前拓展一步,如果我们想将$f$拓展成超过两部分的信息呢? 一种方案便是DenseNet。 @@ -93,7 +93,7 @@ class ConvBlock(tf.keras.layers.Layer): return y ``` -一个*稠密块*由多个卷积块组成,每个卷积块使用相同数量的输出信道。 +一个*稠密块*由多个卷积块组成,每个卷积块使用相同数量的输出通道。 然而,在前向传播中,我们将每个卷积块的输入和输出在通道维上连结。 ```{.python .input} @@ -368,7 +368,7 @@ d2l.train_ch6(net, train_iter, test_iter, num_epochs, lr, d2l.try_gpu()) * 在跨层连接上,不同于ResNet中将输入与输出相加,稠密连接网络(DenseNet)在通道维上连结输入与输出。 * DenseNet的主要构建模块是稠密块和过渡层。 -* 在构建DenseNet时,我们需要通过添加过渡层来控制网络的维数,从而再次减少信道的数量。 +* 在构建DenseNet时,我们需要通过添加过渡层来控制网络的维数,从而再次减少通道的数量。 ## 练习 diff --git a/chapter_convolutional-modern/googlenet.md b/chapter_convolutional-modern/googlenet.md index 2c4559d15..d2ce09179 100644 --- a/chapter_convolutional-modern/googlenet.md +++ b/chapter_convolutional-modern/googlenet.md @@ -19,7 +19,7 @@ GoogLeNet吸收了NiN中串联网络的思想,并在此基础上做了改进 前三条路径使用窗口大小为$1\times 1$、$3\times 3$和$5\times 5$的卷积层,从不同空间大小中提取信息。 中间的两条路径在输入上执行$1\times 1$卷积,以减少通道数,从而降低模型的复杂性。 第四条路径使用$3\times 3$最大汇聚层,然后使用$1\times 1$卷积层来改变通道数。 -这四条路径都使用合适的填充来使输入与输出的高和宽一致,最后我们将每条线路的输出在通道维度上连结,并构成Inception块的输出。在Inception块中,通常调整的超参数是每层输出通道的数量。 +这四条路径都使用合适的填充来使输入与输出的高和宽一致,最后我们将每条线路的输出在通道维度上连结,并构成Inception块的输出。在Inception块中,通常调整的超参数是每层输出通道数。 ```{.python .input} from d2l import mxnet as d2l @@ -126,7 +126,7 @@ class Inception(tf.keras.Model): ## [**GoogLeNet模型**] 如 :numref:`fig_inception_full`所示,GoogLeNet一共使用9个Inception块和全局平均汇聚层的堆叠来生成其估计值。Inception块之间的最大汇聚层可降低维度。 -第一个模块类似于AlexNet和LeNet,Inception块的栈从VGG继承,全局平均汇聚层避免了在最后使用全连接层。 +第一个模块类似于AlexNet和LeNet,Inception块的组合从VGG继承,全局平均汇聚层避免了在最后使用全连接层。 ![GoogLeNet架构。](../img/inception-full.svg) :label:`fig_inception_full` @@ -337,11 +337,11 @@ d2l.train_ch6(net, train_iter, test_iter, num_epochs, lr, d2l.try_gpu()) ## 练习 -1. GoogLeNet有数个后续版本。尝试实现并运行它们,然后观察实验结果。这些后续版本包括: +1. GoogLeNet有一些后续版本。尝试实现并运行它们,然后观察实验结果。这些后续版本包括: * 添加批量规范化层 :cite:`Ioffe.Szegedy.2015`(batch normalization),在 :numref:`sec_batch_norm`中将介绍)。 - * 对Inception模块进行调整。 + * 对Inception模块进行调整 :cite:`Szegedy.Vanhoucke.Ioffe.ea.2016`。 * 使用标签平滑(label smoothing)进行模型正则化 :cite:`Szegedy.Vanhoucke.Ioffe.ea.2016`。 - * 加入残差连接 :cite:`Szegedy.Ioffe.Vanhoucke.ea.2017`,( :numref:`sec_resnet`将介绍)。 + * 加入残差连接 :cite:`Szegedy.Ioffe.Vanhoucke.ea.2017`。( :numref:`sec_resnet`将介绍)。 1. 使用GoogLeNet的最小图像大小是多少? 1. 将AlexNet、VGG和NiN的模型参数大小与GoogLeNet进行比较。后两个网络架构是如何显著减少模型参数大小的? diff --git a/chapter_convolutional-modern/nin.md b/chapter_convolutional-modern/nin.md index c9dff4286..a193e3291 100644 --- a/chapter_convolutional-modern/nin.md +++ b/chapter_convolutional-modern/nin.md @@ -3,8 +3,7 @@ LeNet、AlexNet和VGG都有一个共同的设计模式:通过一系列的卷积层与汇聚层来提取空间结构特征;然后通过全连接层对特征的表征进行处理。 AlexNet和VGG对LeNet的改进主要在于如何扩大和加深这两个模块。 -或者,可以想象在这个过程的早期使用全连接层。 -然而,如果使用稠密层了,可能会完全放弃表征的空间结构。 +或者,可以想象在这个过程的早期使用全连接层。然而,如果使用了全连接层,可能会完全放弃表征的空间结构。 *网络中的网络*(*NiN*)提供了一个非常简单的解决方案:在每个像素的通道上分别使用多层感知机 :cite:`Lin.Chen.Yan.2013` ## (**NiN块**) diff --git a/chapter_convolutional-modern/resnet.md b/chapter_convolutional-modern/resnet.md index 9e1110a17..31b04375b 100644 --- a/chapter_convolutional-modern/resnet.md +++ b/chapter_convolutional-modern/resnet.md @@ -53,7 +53,7 @@ ResNet沿用了VGG完整的$3\times 3$卷积层设计。 残差块里首先有2个有相同输出通道数的$3\times 3$卷积层。 每个卷积层后接一个批量规范化层和ReLU激活函数。 然后我们通过跨层数据通路,跳过这2个卷积运算,将输入直接加在最后的ReLU激活函数前。 -这样的设计要求2个卷积层的输出与输入形状一样,从而可以相加。 +这样的设计要求2个卷积层的输出与输入形状一样,从而使它们可以相加。 如果想改变通道数,就需要引入一个额外的$1\times 1$卷积层来将输入变换成需要的形状后再做相加运算。 残差块的实现如下: @@ -392,7 +392,7 @@ d2l.train_ch6(net, train_iter, test_iter, num_epochs, lr, d2l.try_gpu()) * 学习嵌套函数(nested function)是训练神经网络的理想情况。在深层神经网络中,学习另一层作为恒等映射(identity function)较容易(尽管这是一个极端情况)。 * 残差映射可以更容易地学习同一函数,例如将权重层中的参数近似为零。 * 利用残差块(residual blocks)可以训练出一个有效的深层神经网络:输入可以通过层间的残余连接更快地向前传播。 -* 残差网络(ResNet)对随后的深层神经网络设计产生了深远影响,无论是卷积类网络还是全连接类网络。 +* 残差网络(ResNet)对随后的深层神经网络设计产生了深远影响。 ## 练习 diff --git a/chapter_convolutional-neural-networks/channels.md b/chapter_convolutional-neural-networks/channels.md index 7e15300ec..2dd7ffcc2 100644 --- a/chapter_convolutional-neural-networks/channels.md +++ b/chapter_convolutional-neural-networks/channels.md @@ -9,7 +9,7 @@ ## 多输入通道 -当输入包含多个通道时,需要构造一个与输入数据具有相同输入通道数目的卷积核,以便与输入数据进行互相关运算。假设输入的通道数为$c_i$,那么卷积核的输入通道数也需要为$c_i$。如果卷积核的窗口形状是$k_h\times k_w$,那么当$c_i=1$时,我们可以把卷积核看作形状为$k_h\times k_w$的二维张量。 +当输入包含多个通道时,需要构造一个与输入数据具有相同输入通道数的卷积核,以便与输入数据进行互相关运算。假设输入的通道数为$c_i$,那么卷积核的输入通道数也需要为$c_i$。如果卷积核的窗口形状是$k_h\times k_w$,那么当$c_i=1$时,我们可以把卷积核看作形状为$k_h\times k_w$的二维张量。 然而,当$c_i>1$时,我们卷积核的每个输入通道将包含形状为$k_h\times k_w$的张量。将这些张量$c_i$连结在一起可以得到形状为$c_i\times k_h\times k_w$的卷积核。由于输入和卷积核都有$c_i$个通道,我们可以对每个通道输入的二维张量和卷积核的二维张量进行互相关运算,再对通道求和(将$c_i$的结果相加)得到二维张量。这是多通道输入和多输入通道卷积核之间进行二维互相关运算的结果。 @@ -18,7 +18,7 @@ ![两个输入通道的互相关计算。](../img/conv-multi-in.svg) :label:`fig_conv_multi_in` -为了加深理解,我们将(**实现一下多输入通道互相关运算**)。 +为了加深理解,我们(**实现一下多输入通道互相关运算**)。 简而言之,我们所做的就是对每个通道执行互相关操作,然后将结果相加。 ```{.python .input} diff --git a/chapter_convolutional-neural-networks/conv-layer.md b/chapter_convolutional-neural-networks/conv-layer.md index 28318a078..eab9af6fb 100644 --- a/chapter_convolutional-neural-networks/conv-layer.md +++ b/chapter_convolutional-neural-networks/conv-layer.md @@ -10,7 +10,7 @@ 首先,我们暂时忽略通道(第三维)这一情况,看看如何处理二维图像数据和隐藏表示。在 :numref:`fig_correlation`中,输入是高度为$3$、宽度为$3$的二维张量(即形状为$3 \times 3$)。卷积核的高度和宽度都是$2$,而卷积核窗口(或卷积窗口)的形状由内核的高度和宽度决定(即$2 \times 2$)。 -![二维互相关运算。阴影部分是第一个输出元素,以及用于计算这个输出的输入和核张量元素:$0\times0+1\times1+3\times2+4\times3=19$.](../img/correlation.svg) +![二维互相关运算。阴影部分是第一个输出元素,以及用于计算输出的输入张量元素和核张量元素:$0\times0+1\times1+3\times2+4\times3=19$.](../img/correlation.svg) :label:`fig_correlation` 在二维互相关运算中,卷积窗口从输入张量的左上角开始,从左到右、从上到下滑动。 diff --git a/chapter_convolutional-neural-networks/lenet.md b/chapter_convolutional-neural-networks/lenet.md index 9effacc23..e348a6560 100644 --- a/chapter_convolutional-neural-networks/lenet.md +++ b/chapter_convolutional-neural-networks/lenet.md @@ -2,10 +2,10 @@ :label:`sec_lenet` 通过之前几节,我们学习了构建一个完整卷积神经网络的所需组件。 -回想一下,之前我们将softmax回归模型( :numref:`sec_softmax_scratch`)和多层感知机模型( :numref:`sec_mlp_scratch`)应用于Fashion-MNIST数据集中的服装图片上。 -为了能够应用softmax回归和多层感知机,我们首先将每个大小为$28\times28$的图像展平为一个784固定长度的一维向量,然后用全连接层对其进行处理。 +回想一下,之前我们将softmax回归模型( :numref:`sec_softmax_scratch`)和多层感知机模型( :numref:`sec_mlp_scratch`)应用于Fashion-MNIST数据集中的服装图片。 +为了能够应用softmax回归和多层感知机,我们首先将每个大小为$28\times28$的图像展平为一个784维的固定长度的一维向量,然后用全连接层对其进行处理。 而现在,我们已经掌握了卷积层的处理方法,我们可以在图像中保留空间结构。 -同时,用卷积层代替全连接层的另一个好处是:更简洁的模型所需的参数更少。 +同时,用卷积层代替全连接层的另一个好处是:模型更简洁、所需的参数更少。 在本节中,我们将介绍LeNet,它是最早发布的卷积神经网络之一,因其在计算机视觉任务中的高效性能而受到广泛关注。 这个模型是由AT&T贝尔实验室的研究员Yann LeCun在1989年提出的(并以其命名),目的是识别图像 :cite:`LeCun.Bottou.Bengio.ea.1998`中的手写数字。 diff --git a/chapter_convolutional-neural-networks/pooling.md b/chapter_convolutional-neural-networks/pooling.md index 7ae45b70d..ad0e31d1b 100644 --- a/chapter_convolutional-neural-networks/pooling.md +++ b/chapter_convolutional-neural-networks/pooling.md @@ -1,7 +1,7 @@ # 汇聚层 :label:`sec_pooling` -通常当我们处理图像时,我们希望逐渐降低隐藏表示的空间分辨率,聚集信息,这样随着我们在神经网络中层叠的上升,每个神经元对其敏感的感受野(输入)就越大。 +通常当我们处理图像时,我们希望逐渐降低隐藏表示的空间分辨率、聚集信息,这样随着我们在神经网络中层叠的上升,每个神经元对其敏感的感受野(输入)就越大。 而我们的机器学习任务通常会跟全局图像的问题有关(例如,“图像是否包含一只猫呢?”),所以我们最后一层的神经元应该对整个输入的全局敏感。通过逐渐聚合信息,生成越来越粗糙的映射,最终实现学习全局表示的目标,同时将卷积图层的所有优势保留在中间层。 @@ -15,7 +15,7 @@ 然而,不同于卷积层中的输入与卷积核之间的互相关计算,汇聚层不包含参数。 相反,池运算符是确定性的,我们通常计算汇聚窗口中所有元素的最大值或平均值。这些操作分别称为*最大汇聚层*(maximum pooling)和*平均汇聚层*(average pooling)。 -在这两种情况下,与互相关运算符一样,汇聚窗口从输入张量的左上角开始,从左到右、从上到下的在输入张量内滑动。在汇聚窗口到达的每个位置,它计算该窗口中输入子张量的最大值或平均值,具体取决于是使用了最大汇聚层还是平均汇聚层。 +在这两种情况下,与互相关运算符一样,汇聚窗口从输入张量的左上角开始,从左往右、从上往下的在输入张量内滑动。在汇聚窗口到达的每个位置,它计算该窗口中输入子张量的最大值或平均值。计算最大值或平均值是取决于使用了最大汇聚层还是平均汇聚层。 ![汇聚窗口形状为 $2\times 2$ 的最大汇聚层。着色部分是第一个输出元素,以及用于计算这个输出的输入元素: $\max(0, 1, 3, 4)=4$.](../img/pooling.svg) :label:`fig_pooling` @@ -261,7 +261,7 @@ pool2d(X_padded) 1. 假设汇聚层的输入大小为$c\times h\times w$,则汇聚窗口的形状为$p_h\times p_w$,填充为$(p_h, p_w)$,步幅为$(s_h, s_w)$。这个汇聚层的计算成本是多少? 1. 为什么最大汇聚层和平均汇聚层的工作方式不同? 1. 我们是否需要最小汇聚层?可以用已知函数替换它吗? -1. 除了平均汇聚层和最大汇聚层,是否有其它函数可以考虑(提示:回忆`softmax`)?为什么它可能不受欢迎? +1. 除了平均汇聚层和最大汇聚层,是否有其它函数可以考虑(提示:回想一下`softmax`)?为什么它不流行? :begin_tab:`mxnet` [Discussions](https://discuss.d2l.ai/t/1858) diff --git a/chapter_deep-learning-computation/custom-layer.md b/chapter_deep-learning-computation/custom-layer.md index 5f4769c3b..338eb1d86 100644 --- a/chapter_deep-learning-computation/custom-layer.md +++ b/chapter_deep-learning-computation/custom-layer.md @@ -72,7 +72,7 @@ layer = CenteredLayer() layer(tf.constant([1, 2, 3, 4, 5])) ``` -现在,我们可以[**将层作为组件合并到构建更复杂的模型中**]。 +现在,我们可以[**将层作为组件合并到更复杂的模型中**]。 ```{.python .input} net = nn.Sequential() @@ -116,12 +116,12 @@ tf.reduce_mean(Y) 这些参数可以通过训练进行调整。 我们可以使用内置函数来创建参数,这些函数提供一些基本的管理功能。 比如管理访问、初始化、共享、保存和加载模型参数。 -这样做的好处之一是:我们不需要为每个自定义层编写自定义序列化程序。 +这样做的好处之一是:我们不需要为每个自定义层编写自定义的序列化程序。 现在,让我们实现自定义版本的全连接层。 回想一下,该层需要两个参数,一个用于表示权重,另一个用于表示偏置项。 在此实现中,我们使用修正线性单元作为激活函数。 -该层需要输入参数:`in_units`和`units`,分别表示输入和输出的数量。 +该层需要输入参数:`in_units`和`units`,分别表示输入数和输出数。 ```{.python .input} class MyDense(nn.Block): From 685a4a82b499384c2deec15047096981376076c3 Mon Sep 17 00:00:00 2001 From: yl7466 <49036529+yl7466@users.noreply.github.com> Date: Tue, 7 Dec 2021 02:11:46 +0800 Subject: [PATCH 05/21] fix typo (#1028) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * fix typo * fix typo 我妈 > 我们 --- chapter_linear-networks/softmax-regression.md | 2 +- chapter_multilayer-perceptrons/mlp-scratch.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/chapter_linear-networks/softmax-regression.md b/chapter_linear-networks/softmax-regression.md index 3fe27eae4..dff09eace 100644 --- a/chapter_linear-networks/softmax-regression.md +++ b/chapter_linear-networks/softmax-regression.md @@ -101,7 +101,7 @@ $$ 例如,如果$\hat{y}_1$、$\hat{y}_2$和$\hat{y}_3$分别为0.1、0.8和0.1, 那么我们预测的类别是2,在我们的例子中代表“鸡”。 -然而我妈能否将未规范化的预测$o$直接视作我们感兴趣的输出呢? +然而我们能否将未规范化的预测$o$直接视作我们感兴趣的输出呢? 答案是否定的。 因为将线性层的输出直接视为概率时存在一些问题: 一方面,我们没有限制这些输出数字的总和为1。 diff --git a/chapter_multilayer-perceptrons/mlp-scratch.md b/chapter_multilayer-perceptrons/mlp-scratch.md index 0df8994e1..a20c57814 100644 --- a/chapter_multilayer-perceptrons/mlp-scratch.md +++ b/chapter_multilayer-perceptrons/mlp-scratch.md @@ -87,7 +87,7 @@ W1 = tf.Variable(tf.random.normal( b1 = tf.Variable(tf.zeros(num_hiddens)) W2 = tf.Variable(tf.random.normal( shape=(num_hiddens, num_outputs), mean=0, stddev=0.01)) -b2 = tf.Variable(tf.random.normal([num_outputs], stddev=.01)) +b2 = tf.Variable(tf.zeros(num_outputs)) params = [W1, b1, W2, b2] ``` From e8473558c2e3f3379c4193b6e813d17c9bb462c6 Mon Sep 17 00:00:00 2001 From: xiaotinghe Date: Tue, 7 Dec 2021 05:20:41 +0800 Subject: [PATCH 06/21] remove space in comments (#1035) Co-authored-by: Ubuntu --- .../attention-scoring-functions.md | 82 ++++++------- .../bahdanau-attention.md | 64 +++++------ .../multihead-attention.md | 108 +++++++++--------- .../nadaraya-waston.md | 68 +++++------ .../self-attention-and-positional-encoding.md | 6 +- chapter_attention-mechanisms/transformer.md | 48 ++++---- .../multiple-gpus-concise.md | 2 +- .../multiple-gpus.md | 4 +- chapter_computer-vision/anchor.md | 76 ++++++------ chapter_computer-vision/bounding-box.md | 4 +- chapter_computer-vision/fine-tuning.md | 2 +- chapter_computer-vision/kaggle-cifar10.md | 8 +- chapter_computer-vision/kaggle-dog.md | 6 +- .../object-detection-dataset.md | 12 +- .../semantic-segmentation-and-dataset.md | 24 ++-- chapter_computer-vision/ssd.md | 12 +- chapter_convolutional-modern/batch-norm.md | 18 +-- chapter_convolutional-modern/googlenet.md | 30 ++--- chapter_convolutional-modern/nin.md | 6 +- chapter_convolutional-modern/resnet.md | 8 +- .../channels.md | 4 +- .../conv-layer.md | 4 +- .../lenet.md | 14 +-- .../model-construction.md | 8 +- .../image-classification-dataset.md | 16 +-- .../linear-regression-concise.md | 12 +- .../linear-regression-scratch.md | 22 ++-- chapter_linear-networks/linear-regression.md | 12 +- .../softmax-regression-scratch.md | 22 ++-- .../kaggle-house-price.md | 6 +- .../underfit-overfit.md | 16 +-- .../weight-decay.md | 2 +- .../natural-language-inference-attention.md | 8 +- .../natural-language-inference-bert.md | 8 +- .../bert-dataset.md | 4 +- .../bert-pretraining.md | 6 +- .../bert.md | 6 +- .../subword-embedding.md | 2 +- .../word-embedding-dataset.md | 10 +- chapter_optimization/adadelta.md | 8 +- chapter_optimization/gd.md | 8 +- chapter_optimization/lr-scheduler.md | 14 +-- chapter_optimization/minibatch-sgd.md | 14 +-- chapter_optimization/sgd.md | 10 +- chapter_preliminaries/autograd.md | 8 +- chapter_preliminaries/calculus.md | 10 +- chapter_preliminaries/linear-algebra.md | 6 +- .../machine-translation-and-dataset.md | 2 +- chapter_recurrent-modern/seq2seq.md | 56 ++++----- .../language-models-and-dataset.md | 2 +- .../rnn-concise.md | 16 +-- .../rnn-scratch.md | 6 +- .../text-preprocessing.md | 2 +- 53 files changed, 466 insertions(+), 466 deletions(-) diff --git a/chapter_attention-mechanisms/attention-scoring-functions.md b/chapter_attention-mechanisms/attention-scoring-functions.md index f1b9e16cb..477eac760 100644 --- a/chapter_attention-mechanisms/attention-scoring-functions.md +++ b/chapter_attention-mechanisms/attention-scoring-functions.md @@ -80,8 +80,8 @@ import tensorflow as tf ```{.python .input} #@save def masked_softmax(X, valid_lens): - """通过在最后一个轴上掩蔽元素来执行 softmax 操作""" - # `X`: 3D张量,`valid_lens`: 1D或2D 张量 + """通过在最后一个轴上掩蔽元素来执行softmax操作""" + # `X`:3D张量,`valid_lens`:1D或2D张量 if valid_lens is None: return npx.softmax(X) else: @@ -100,8 +100,8 @@ def masked_softmax(X, valid_lens): #@tab pytorch #@save def masked_softmax(X, valid_lens): - """通过在最后一个轴上掩蔽元素来执行 softmax 操作""" - # `X`: 3D张量,`valid_lens`: 1D或2D 张量 + """通过在最后一个轴上掩蔽元素来执行softmax操作""" + # `X`:3D张量,`valid_lens`:1D或2D张量 if valid_lens is None: return nn.functional.softmax(X, dim=-1) else: @@ -120,8 +120,8 @@ def masked_softmax(X, valid_lens): #@tab tensorflow #@save def masked_softmax(X, valid_lens): - """通过在最后一个轴上掩蔽元素来执行 softmax 操作""" - # `X`: 3D张量,`valid_lens`: 1D或2D 张量 + """通过在最后一个轴上掩蔽元素来执行softmax操作""" + # `X`:3D张量,`valid_lens`:1D或2D张量 if valid_lens is None: return tf.nn.softmax(X, axis=-1) else: @@ -201,7 +201,7 @@ class AdditiveAttention(nn.Block): """加性注意力""" def __init__(self, num_hiddens, dropout, **kwargs): super(AdditiveAttention, self).__init__(**kwargs) - # 使用 'flatten=False' 只转换最后一个轴,以便其他轴的形状保持不变 + # 使用'flatten=False'只转换最后一个轴,以便其他轴的形状保持不变 self.W_k = nn.Dense(num_hiddens, use_bias=False, flatten=False) self.W_q = nn.Dense(num_hiddens, use_bias=False, flatten=False) self.w_v = nn.Dense(1, use_bias=False, flatten=False) @@ -210,17 +210,17 @@ class AdditiveAttention(nn.Block): def forward(self, queries, keys, values, valid_lens): queries, keys = self.W_q(queries), self.W_k(keys) # 在维度扩展后, - # `queries` 的形状:(`batch_size`,查询的个数,1,`num_hidden`) - # `key` 的形状:(`batch_size`,1,“键-值”对的个数,`num_hiddens`) + # `queries`的形状:(`batch_size`,查询的个数,1,`num_hidden`) + # `key`的形状:(`batch_size`,1,“键-值”对的个数,`num_hiddens`) # 使用广播的方式进行求和 features = np.expand_dims(queries, axis=2) + np.expand_dims( keys, axis=1) features = np.tanh(features) - # `self.w_v` 仅有一个输出,因此从形状中移除最后那个维度。 - # `scores` 的形状:(`batch_size`,查询的个数,“键-值”对的个数) + # `self.w_v`仅有一个输出,因此从形状中移除最后那个维度。 + # `scores`的形状:(`batch_size`,查询的个数,“键-值”对的个数) scores = np.squeeze(self.w_v(features), axis=-1) self.attention_weights = masked_softmax(scores, valid_lens) - # `values` 的形状:(`batch_size`,“键-值”对的个数,值的维度) + # `values`的形状:(`batch_size`,“键-值”对的个数,值的维度) return npx.batch_dot(self.dropout(self.attention_weights), values) ``` @@ -239,16 +239,16 @@ class AdditiveAttention(nn.Module): def forward(self, queries, keys, values, valid_lens): queries, keys = self.W_q(queries), self.W_k(keys) # 在维度扩展后, - # `queries` 的形状:(`batch_size`,查询的个数,1,`num_hidden`) - # `key` 的形状:(`batch_size`,1,“键-值”对的个数,`num_hiddens`) + # `queries`的形状:(`batch_size`,查询的个数,1,`num_hidden`) + # `key`的形状:(`batch_size`,1,“键-值”对的个数,`num_hiddens`) # 使用广播方式进行求和 features = queries.unsqueeze(2) + keys.unsqueeze(1) features = torch.tanh(features) - # `self.w_v` 仅有一个输出,因此从形状中移除最后那个维度。 - # `scores` 的形状:(`batch_size`,查询的个数,“键-值”对的个数) + # `self.w_v`仅有一个输出,因此从形状中移除最后那个维度。 + # `scores`的形状:(`batch_size`,查询的个数,“键-值”对的个数) scores = self.w_v(features).squeeze(-1) self.attention_weights = masked_softmax(scores, valid_lens) - # `values` 的形状:(`batch_size`,“键-值”对的个数,值的维度) + # `values`的形状:(`batch_size`,“键-值”对的个数,值的维度) return torch.bmm(self.dropout(self.attention_weights), values) ``` @@ -256,7 +256,7 @@ class AdditiveAttention(nn.Module): #@tab tensorflow #@save class AdditiveAttention(tf.keras.layers.Layer): - """Additive attention.""" + """Additiveattention.""" def __init__(self, key_size, query_size, num_hiddens, dropout, **kwargs): super().__init__(**kwargs) self.W_k = tf.keras.layers.Dense(num_hiddens, use_bias=False) @@ -267,17 +267,17 @@ class AdditiveAttention(tf.keras.layers.Layer): def call(self, queries, keys, values, valid_lens, **kwargs): queries, keys = self.W_q(queries), self.W_k(keys) # 在维度扩展后, - # `queries` 的形状:(`batch_size`,查询的个数,1,`num_hidden`) - # `key` 的形状:(`batch_size`,1,“键-值”对的个数,`num_hiddens`) + # `queries`的形状:(`batch_size`,查询的个数,1,`num_hidden`) + # `key`的形状:(`batch_size`,1,“键-值”对的个数,`num_hiddens`) # 使用广播方式进行求和 features = tf.expand_dims(queries, axis=2) + tf.expand_dims( keys, axis=1) features = tf.nn.tanh(features) - # `self.w_v` 仅有一个输出,因此从形状中移除最后那个维度。 - # `scores` 的形状:(`batch_size`,查询的个数,“键-值”对的个数) + # `self.w_v`仅有一个输出,因此从形状中移除最后那个维度。 + # `scores`的形状:(`batch_size`,查询的个数,“键-值”对的个数) scores = tf.squeeze(self.w_v(features), axis=-1) self.attention_weights = masked_softmax(scores, valid_lens) - # `values` 的形状:(`batch_size`,“键-值”对的个数,值的维度) + # `values`的形状:(`batch_size`,“键-值”对的个数,值的维度) return tf.matmul(self.dropout( self.attention_weights, **kwargs), values) ``` @@ -289,7 +289,7 @@ class AdditiveAttention(tf.keras.layers.Layer): ```{.python .input} queries, keys = d2l.normal(0, 1, (2, 1, 20)), d2l.ones((2, 10, 2)) -# `values` 的小批量数据集中,两个值矩阵是相同的 +# `values`的小批量数据集中,两个值矩阵是相同的 values = np.arange(40).reshape(1, 10, 4).repeat(2, axis=0) valid_lens = d2l.tensor([2, 6]) @@ -301,7 +301,7 @@ attention(queries, keys, values, valid_lens) ```{.python .input} #@tab pytorch queries, keys = d2l.normal(0, 1, (2, 1, 20)), d2l.ones((2, 10, 2)) -# `values` 的小批量,两个值矩阵是相同的 +# `values`的小批量,两个值矩阵是相同的 values = torch.arange(40, dtype=torch.float32).reshape(1, 10, 4).repeat( 2, 1, 1) valid_lens = d2l.tensor([2, 6]) @@ -315,7 +315,7 @@ attention(queries, keys, values, valid_lens) ```{.python .input} #@tab tensorflow queries, keys = tf.random.normal(shape=(2, 1, 20)), tf.ones((2, 10, 2)) -# `values` 的小批量,两个值矩阵是相同的 +# `values`的小批量,两个值矩阵是相同的 values = tf.repeat(tf.reshape( tf.range(40, dtype=tf.float32), shape=(1, 10, 4)), repeats=2, axis=0) valid_lens = tf.constant([2, 6]) @@ -369,13 +369,13 @@ class DotProductAttention(nn.Block): super(DotProductAttention, self).__init__(**kwargs) self.dropout = nn.Dropout(dropout) - # `queries` 的形状:(`batch_size`,查询的个数,`d`) - # `keys` 的形状:(`batch_size`,“键-值”对的个数,`d`) - # `values` 的形状:(`batch_size`,“键-值”对的个数,值的维度) - # `valid_lens` 的形状: (`batch_size`,) 或者 (`batch_size`,查询的个数) + # `queries`的形状:(`batch_size`,查询的个数,`d`) + # `keys`的形状:(`batch_size`,“键-值”对的个数,`d`) + # `values`的形状:(`batch_size`,“键-值”对的个数,值的维度) + # `valid_lens`的形状:(`batch_size`,)或者(`batch_size`,查询的个数) def forward(self, queries, keys, values, valid_lens=None): d = queries.shape[-1] - # 设置 `transpose_b=True` 为了交换 `keys` 的最后两个维度 + # 设置`transpose_b=True`为了交换`keys`的最后两个维度 scores = npx.batch_dot(queries, keys, transpose_b=True) / math.sqrt(d) self.attention_weights = masked_softmax(scores, valid_lens) return npx.batch_dot(self.dropout(self.attention_weights), values) @@ -390,13 +390,13 @@ class DotProductAttention(nn.Module): super(DotProductAttention, self).__init__(**kwargs) self.dropout = nn.Dropout(dropout) - # `queries` 的形状:(`batch_size`,查询的个数,`d`) - # `keys` 的形状:(`batch_size`,“键-值”对的个数,`d`) - # `values` 的形状:(`batch_size`,“键-值”对的个数,值的维度) - # `valid_lens` 的形状: (`batch_size`,) 或者 (`batch_size`,查询的个数) + # `queries`的形状:(`batch_size`,查询的个数,`d`) + # `keys`的形状:(`batch_size`,“键-值”对的个数,`d`) + # `values`的形状:(`batch_size`,“键-值”对的个数,值的维度) + # `valid_lens`的形状:(`batch_size`,)或者(`batch_size`,查询的个数) def forward(self, queries, keys, values, valid_lens=None): d = queries.shape[-1] - # 设置 `transpose_b=True` 为了交换 `keys` 的最后两个维度 + # 设置`transpose_b=True`为了交换`keys`的最后两个维度 scores = torch.bmm(queries, keys.transpose(1,2)) / math.sqrt(d) self.attention_weights = masked_softmax(scores, valid_lens) return torch.bmm(self.dropout(self.attention_weights), values) @@ -406,15 +406,15 @@ class DotProductAttention(nn.Module): #@tab tensorflow #@save class DotProductAttention(tf.keras.layers.Layer): - """Scaled dot product attention.""" + """Scaleddotproductattention.""" def __init__(self, dropout, **kwargs): super().__init__(**kwargs) self.dropout = tf.keras.layers.Dropout(dropout) - # `queries` 的形状:(`batch_size`,查询的个数,`d`) - # `keys` 的形状:(`batch_size`,“键-值”对的个数,`d`) - # `values` 的形状:(`batch_size`,“键-值”对的个数,值的维度) - # `valid_lens` 的形状: (`batch_size`,) 或者 (`batch_size`,查询的个数) + # `queries`的形状:(`batch_size`,查询的个数,`d`) + # `keys`的形状:(`batch_size`,“键-值”对的个数,`d`) + # `values`的形状:(`batch_size`,“键-值”对的个数,值的维度) + # `valid_lens`的形状:(`batch_size`,)或者(`batch_size`,查询的个数) def call(self, queries, keys, values, valid_lens, **kwargs): d = queries.shape[-1] scores = tf.matmul(queries, keys, transpose_b=True)/tf.math.sqrt( diff --git a/chapter_attention-mechanisms/bahdanau-attention.md b/chapter_attention-mechanisms/bahdanau-attention.md index 1d476f826..735a17f26 100644 --- a/chapter_attention-mechanisms/bahdanau-attention.md +++ b/chapter_attention-mechanisms/bahdanau-attention.md @@ -105,33 +105,33 @@ class Seq2SeqAttentionDecoder(AttentionDecoder): self.dense = nn.Dense(vocab_size, flatten=False) def init_state(self, enc_outputs, enc_valid_lens, *args): - # `outputs`的形状为 (`num_steps`,`batch_size`,`num_hiddens`) - # `hidden_state[0]`的形状为 (`num_layers`,`batch_size`,`num_hiddens`) + # `outputs`的形状为(`num_steps`,`batch_size`,`num_hiddens`) + # `hidden_state[0]`的形状为(`num_layers`,`batch_size`,`num_hiddens`) outputs, hidden_state = enc_outputs return (outputs.swapaxes(0, 1), hidden_state, enc_valid_lens) def forward(self, X, state): - # `enc_outputs`的形状为 (`batch_size`, `num_steps`, `num_hiddens`). - # `hidden_state[0]`的形状为 (`num_layers`, `batch_size`, + # `enc_outputs`的形状为(`batch_size`,`num_steps`,`num_hiddens`). + # `hidden_state[0]`的形状为(`num_layers`,`batch_size`, # `num_hiddens`) enc_outputs, hidden_state, enc_valid_lens = state - # 输出 `X`的形状为 (`num_steps`, `batch_size`, `embed_size`) + # 输出`X`的形状为(`num_steps`,`batch_size`,`embed_size`) X = self.embedding(X).swapaxes(0, 1) outputs, self._attention_weights = [], [] for x in X: - # `query`的形状为 (`batch_size`, 1, `num_hiddens`) + # `query`的形状为(`batch_size`,1,`num_hiddens`) query = np.expand_dims(hidden_state[0][-1], axis=1) - # `context`的形状为 (`batch_size`, 1, `num_hiddens`) + # `context`的形状为(`batch_size`,1,`num_hiddens`) context = self.attention( query, enc_outputs, enc_outputs, enc_valid_lens) # 在特征维度上连结 x = np.concatenate((context, np.expand_dims(x, axis=1)), axis=-1) - # 将 `x` 变形为 (1, `batch_size`, `embed_size` + `num_hiddens`) + # 将`x`变形为(1,`batch_size`,`embed_size`+`num_hiddens`) out, hidden_state = self.rnn(x.swapaxes(0, 1), hidden_state) outputs.append(out) self._attention_weights.append(self.attention.attention_weights) - # 全连接层变换后, `outputs`的形状为 - # (`num_steps`, `batch_size`, `vocab_size`) + # 全连接层变换后,`outputs`的形状为 + # (`num_steps`,`batch_size`,`vocab_size`) outputs = self.dense(np.concatenate(outputs, axis=0)) return outputs.swapaxes(0, 1), [enc_outputs, hidden_state, enc_valid_lens] @@ -156,33 +156,33 @@ class Seq2SeqAttentionDecoder(AttentionDecoder): self.dense = nn.Linear(num_hiddens, vocab_size) def init_state(self, enc_outputs, enc_valid_lens, *args): - # `outputs`的形状为 (`batch_size`,`num_steps`,`num_hiddens`). - # `hidden_state`的形状为 (`num_layers`,`batch_size`,`num_hiddens`) + # `outputs`的形状为(`batch_size`,`num_steps`,`num_hiddens`). + # `hidden_state`的形状为(`num_layers`,`batch_size`,`num_hiddens`) outputs, hidden_state = enc_outputs return (outputs.permute(1, 0, 2), hidden_state, enc_valid_lens) def forward(self, X, state): - # `enc_outputs`的形状为 (`batch_size`, `num_steps`, `num_hiddens`). - # `hidden_state`的形状为 (`num_layers`, `batch_size`, + # `enc_outputs`的形状为(`batch_size`,`num_steps`,`num_hiddens`). + # `hidden_state`的形状为(`num_layers`,`batch_size`, # `num_hiddens`) enc_outputs, hidden_state, enc_valid_lens = state - # 输出 `X`的形状为 (`num_steps`, `batch_size`, `embed_size`) + # 输出`X`的形状为(`num_steps`,`batch_size`,`embed_size`) X = self.embedding(X).permute(1, 0, 2) outputs, self._attention_weights = [], [] for x in X: - # `query`的形状为 (`batch_size`, 1, `num_hiddens`) + # `query`的形状为(`batch_size`,1,`num_hiddens`) query = torch.unsqueeze(hidden_state[-1], dim=1) - # `context`的形状为 (`batch_size`, 1, `num_hiddens`) + # `context`的形状为(`batch_size`,1,`num_hiddens`) context = self.attention( query, enc_outputs, enc_outputs, enc_valid_lens) # 在特征维度上连结 x = torch.cat((context, torch.unsqueeze(x, dim=1)), dim=-1) - # 将 `x` 变形为 (1, `batch_size`, `embed_size` + `num_hiddens`) + # 将`x`变形为(1,`batch_size`,`embed_size`+`num_hiddens`) out, hidden_state = self.rnn(x.permute(1, 0, 2), hidden_state) outputs.append(out) self._attention_weights.append(self.attention.attention_weights) - # 全连接层变换后, `outputs`的形状为 - # (`num_steps`, `batch_size`, `vocab_size`) + # 全连接层变换后,`outputs`的形状为 + # (`num_steps`,`batch_size`,`vocab_size`) outputs = self.dense(torch.cat(outputs, dim=0)) return outputs.permute(1, 0, 2), [enc_outputs, hidden_state, enc_valid_lens] @@ -209,25 +209,25 @@ class Seq2SeqAttentionDecoder(AttentionDecoder): self.dense = tf.keras.layers.Dense(vocab_size) def init_state(self, enc_outputs, enc_valid_lens, *args): - # `outputs`的形状为 (`num_steps`,`batch_size`,`num_hiddens`) - # `hidden_state[0]`的形状为 (`num_layers`,`batch_size`,`num_hiddens`) + # `outputs`的形状为(`num_steps`,`batch_size`,`num_hiddens`) + # `hidden_state[0]`的形状为(`num_layers`,`batch_size`,`num_hiddens`) outputs, hidden_state = enc_outputs return (outputs, hidden_state, enc_valid_lens) def call(self, X, state, **kwargs): - # `enc_outputs`的形状为 (`batch_size`, `num_steps`, `num_hiddens`). - # `hidden_state[0]`的形状为 (`num_layers`, `batch_size`, + # `enc_outputs`的形状为(`batch_size`,`num_steps`,`num_hiddens`). + # `hidden_state[0]`的形状为(`num_layers`,`batch_size`, # `num_hiddens`) enc_outputs, hidden_state, enc_valid_lens = state - # 输出 `X`的形状为 (`num_steps`, `batch_size`, `embed_size`) - X = self.embedding(X) # 输入 `X`的形状为 (`batch_size`, `num_steps`) + # 输出`X`的形状为(`num_steps`,`batch_size`,`embed_size`) + X = self.embedding(X) # 输入`X`的形状为(`batch_size`,`num_steps`) X = tf.transpose(X, perm=(1, 0, 2)) outputs, self._attention_weights = [], [] for x in X: - # `query`的形状为 (`batch_size`, 1, `num_hiddens`) + # `query`的形状为(`batch_size`,1,`num_hiddens`) query = tf.expand_dims(hidden_state[-1], axis=1) - # `context`的形状为 (`batch_size`, 1, `num_hiddens`) + # `context`的形状为(`batch_size`,1,`num_hiddens`) context = self.attention(query, enc_outputs, enc_outputs, enc_valid_lens, **kwargs) # 在特征维度上连结 @@ -236,8 +236,8 @@ class Seq2SeqAttentionDecoder(AttentionDecoder): hidden_state = out[1:] outputs.append(out[0]) self._attention_weights.append(self.attention.attention_weights) - # 全连接层变换后, `outputs`的形状为 - # (`num_steps`, `batch_size`, `vocab_size`) + # 全连接层变换后,`outputs`的形状为 + # (`num_steps`,`batch_size`,`vocab_size`) outputs = self.dense(tf.concat(outputs, axis=1)) return outputs, [enc_outputs, hidden_state, enc_valid_lens] @@ -255,7 +255,7 @@ encoder.initialize() decoder = Seq2SeqAttentionDecoder(vocab_size=10, embed_size=8, num_hiddens=16, num_layers=2) decoder.initialize() -X = d2l.zeros((4, 7)) # (`batch_size`, `num_steps`) +X = d2l.zeros((4, 7)) # (`batch_size`,`num_steps`) state = decoder.init_state(encoder(X), None) output, state = decoder(X, state) output.shape, len(state), state[0].shape, len(state[1]), state[1][0].shape @@ -269,7 +269,7 @@ encoder.eval() decoder = Seq2SeqAttentionDecoder(vocab_size=10, embed_size=8, num_hiddens=16, num_layers=2) decoder.eval() -X = d2l.zeros((4, 7), dtype=torch.long) # (`batch_size`, `num_steps`) +X = d2l.zeros((4, 7), dtype=torch.long) # (`batch_size`,`num_steps`) state = decoder.init_state(encoder(X), None) output, state = decoder(X, state) output.shape, len(state), state[0].shape, len(state[1]), state[1][0].shape diff --git a/chapter_attention-mechanisms/multihead-attention.md b/chapter_attention-mechanisms/multihead-attention.md index 414bec4b4..6101ac18d 100644 --- a/chapter_attention-mechanisms/multihead-attention.md +++ b/chapter_attention-mechanisms/multihead-attention.md @@ -98,27 +98,27 @@ class MultiHeadAttention(nn.Block): self.W_o = nn.Dense(num_hiddens, use_bias=use_bias, flatten=False) def forward(self, queries, keys, values, valid_lens): - # `queries`,`keys`,`values` 的形状: + # `queries`,`keys`,`values`的形状: # (`batch_size`,查询或者“键-值”对的个数,`num_hiddens`) # `valid_lens` 的形状: - # (`batch_size`,) 或 (`batch_size`,查询的个数) - # 经过变换后,输出的 `queries`,`keys`,`values` 的形状: - # (`batch_size` * `num_heads`,查询或者“键-值”对的个数, - # `num_hiddens` / `num_heads`) + # (`batch_size`,)或(`batch_size`,查询的个数) + # 经过变换后,输出的`queries`,`keys`,`values` 的形状: + # (`batch_size`*`num_heads`,查询或者“键-值”对的个数, + # `num_hiddens`/`num_heads`) queries = transpose_qkv(self.W_q(queries), self.num_heads) keys = transpose_qkv(self.W_k(keys), self.num_heads) values = transpose_qkv(self.W_v(values), self.num_heads) if valid_lens is not None: - # 在轴 0,将第一项(标量或者矢量)复制 `num_heads` 次, + # 在轴0,将第一项(标量或者矢量)复制`num_heads`次, # 然后如此复制第二项,然后诸如此类。 valid_lens = valid_lens.repeat(self.num_heads, axis=0) - # `output` 的形状: (`batch_size` * `num_heads`,查询的个数, - # `num_hiddens` / `num_heads`) + # `output`的形状:(`batch_size`*`num_heads`,查询的个数, + # `num_hiddens`/`num_heads`) output = self.attention(queries, keys, values, valid_lens) - # `output_concat` 的形状: (`batch_size`,查询的个数,`num_hiddens`) + # `output_concat`的形状:(`batch_size`,查询的个数,`num_hiddens`) output_concat = transpose_output(output, self.num_heads) return self.W_o(output_concat) ``` @@ -139,28 +139,28 @@ class MultiHeadAttention(nn.Module): self.W_o = nn.Linear(num_hiddens, num_hiddens, bias=bias) def forward(self, queries, keys, values, valid_lens): - # `queries`,`keys`,`values` 的形状: + # `queries`,`keys`,`values`的形状: # (`batch_size`,查询或者“键-值”对的个数,`num_hiddens`) # `valid_lens` 的形状: - # (`batch_size`,) 或 (`batch_size`,查询的个数) - # 经过变换后,输出的 `queries`,`keys`,`values` 的形状: - # (`batch_size` * `num_heads`,查询或者“键-值”对的个数, - # `num_hiddens` / `num_heads`) + # (`batch_size`,)或(`batch_size`,查询的个数) + # 经过变换后,输出的`queries`,`keys`,`values` 的形状: + # (`batch_size`*`num_heads`,查询或者“键-值”对的个数, + # `num_hiddens`/`num_heads`) queries = transpose_qkv(self.W_q(queries), self.num_heads) keys = transpose_qkv(self.W_k(keys), self.num_heads) values = transpose_qkv(self.W_v(values), self.num_heads) if valid_lens is not None: - # 在轴 0,将第一项(标量或者矢量)复制 `num_heads` 次, + # 在轴0,将第一项(标量或者矢量)复制`num_heads`次, # 然后如此复制第二项,然后诸如此类。 valid_lens = torch.repeat_interleave( valid_lens, repeats=self.num_heads, dim=0) - # `output` 的形状: (`batch_size` * `num_heads`,查询的个数, - # `num_hiddens` / `num_heads`) + # `output`的形状:(`batch_size`*`num_heads`,查询的个数, + # `num_hiddens`/`num_heads`) output = self.attention(queries, keys, values, valid_lens) - # `output_concat` 的形状: (`batch_size`,查询的个数,`num_hiddens`) + # `output_concat`的形状:(`batch_size`,查询的个数,`num_hiddens`) output_concat = transpose_output(output, self.num_heads) return self.W_o(output_concat) ``` @@ -181,27 +181,27 @@ class MultiHeadAttention(tf.keras.layers.Layer): self.W_o = tf.keras.layers.Dense(num_hiddens, use_bias=bias) def call(self, queries, keys, values, valid_lens, **kwargs): - # `queries`,`keys`,`values` 的形状: + # `queries`,`keys`,`values`的形状: # (`batch_size`,查询或者“键-值”对的个数,`num_hiddens`) # `valid_lens` 的形状: - # (`batch_size`,) 或 (`batch_size`,查询的个数) - # 经过变换后,输出的 `queries`,`keys`,`values` 的形状: - # (`batch_size` * `num_heads`,查询或者“键-值”对的个数, - # `num_hiddens` / `num_heads`) + # (`batch_size`,)或(`batch_size`,查询的个数) + # 经过变换后,输出的`queries`,`keys`,`values` 的形状: + # (`batch_size`*`num_heads`,查询或者“键-值”对的个数, + # `num_hiddens`/`num_heads`) queries = transpose_qkv(self.W_q(queries), self.num_heads) keys = transpose_qkv(self.W_k(keys), self.num_heads) values = transpose_qkv(self.W_v(values), self.num_heads) if valid_lens is not None: - # 在轴 0,将第一项(标量或者矢量)复制 `num_heads` 次, + # 在轴0,将第一项(标量或者矢量)复制`num_heads`次, # 然后如此复制第二项,然后诸如此类。 valid_lens = tf.repeat(valid_lens, repeats=self.num_heads, axis=0) - # `output` 的形状: (`batch_size` * `num_heads`,查询的个数, - # `num_hiddens` / `num_heads`) + # `output`的形状:(`batch_size`*`num_heads`,查询的个数, + # `num_hiddens`/`num_heads`) output = self.attention(queries, keys, values, valid_lens, **kwargs) - # `output_concat` 的形状: (`batch_size`,查询的个数,`num_hiddens`) + # `output_concat`的形状:(`batch_size`,查询的个数,`num_hiddens`) output_concat = transpose_output(output, self.num_heads) return self.W_o(output_concat) ``` @@ -213,24 +213,24 @@ class MultiHeadAttention(tf.keras.layers.Layer): ```{.python .input} #@save def transpose_qkv(X, num_heads): - """为了多注意力头的并行计算而变换形状。""" - # 输入 `X` 的形状: (`batch_size`,查询或者“键-值”对的个数,`num_hiddens`) - # 输出 `X` 的形状: (`batch_size`,查询或者“键-值”对的个数,`num_heads`, - # `num_hiddens` / `num_heads`) + """为了多注意力头的并行计算而变换形状""" + # 输入`X`的形状:(`batch_size`,查询或者“键-值”对的个数,`num_hiddens`) + # 输出`X`的形状:(`batch_size`,查询或者“键-值”对的个数,`num_heads`, + # `num_hiddens`/`num_heads`) X = X.reshape(X.shape[0], X.shape[1], num_heads, -1) - # 输出 `X` 的形状: (`batch_size`,`num_heads`,查询或者“键-值”对的个数, - # `num_hiddens` / `num_heads`) + # 输出`X`的形状:(`batch_size`,`num_heads`,查询或者“键-值”对的个数, + # `num_hiddens`/`num_heads`) X = X.transpose(0, 2, 1, 3) - # 最终输出的形状: (`batch_size` * `num_heads`, 查询或者“键-值”对的个数, - # `num_hiddens` / `num_heads`) + # 最终输出的形状:(`batch_size`*`num_heads`,查询或者“键-值”对的个数, + # `num_hiddens`/`num_heads`) return X.reshape(-1, X.shape[2], X.shape[3]) #@save def transpose_output(X, num_heads): - """逆转 `transpose_qkv` 函数的操作。""" + """逆转`transpose_qkv`函数的操作""" X = X.reshape(-1, num_heads, X.shape[1], X.shape[2]) X = X.transpose(0, 2, 1, 3) return X.reshape(X.shape[0], X.shape[1], -1) @@ -240,24 +240,24 @@ def transpose_output(X, num_heads): #@tab pytorch #@save def transpose_qkv(X, num_heads): - """为了多注意力头的并行计算而变换形状。""" - # 输入 `X` 的形状: (`batch_size`,查询或者“键-值”对的个数,`num_hiddens`) - # 输出 `X` 的形状: (`batch_size`,查询或者“键-值”对的个数,`num_heads`, - # `num_hiddens` / `num_heads`) + """为了多注意力头的并行计算而变换形状""" + # 输入`X`的形状:(`batch_size`,查询或者“键-值”对的个数,`num_hiddens`) + # 输出`X`的形状:(`batch_size`,查询或者“键-值”对的个数,`num_heads`, + # `num_hiddens`/`num_heads`) X = X.reshape(X.shape[0], X.shape[1], num_heads, -1) - # 输出 `X` 的形状: (`batch_size`,`num_heads`,查询或者“键-值”对的个数, - # `num_hiddens` / `num_heads`) + # 输出`X`的形状:(`batch_size`,`num_heads`,查询或者“键-值”对的个数, + # `num_hiddens`/`num_heads`) X = X.permute(0, 2, 1, 3) - # 最终输出的形状: (`batch_size` * `num_heads`, 查询或者“键-值”对的个数, - # `num_hiddens` / `num_heads`) + # 最终输出的形状:(`batch_size`*`num_heads`,查询或者“键-值”对的个数, + # `num_hiddens`/`num_heads`) return X.reshape(-1, X.shape[2], X.shape[3]) #@save def transpose_output(X, num_heads): - """逆转 `transpose_qkv` 函数的操作。""" + """逆转`transpose_qkv`函数的操作""" X = X.reshape(-1, num_heads, X.shape[1], X.shape[2]) X = X.permute(0, 2, 1, 3) return X.reshape(X.shape[0], X.shape[1], -1) @@ -267,24 +267,24 @@ def transpose_output(X, num_heads): #@tab tensorflow #@save def transpose_qkv(X, num_heads): - """为了多注意力头的并行计算而变换形状。""" - # 输入 `X` 的形状: (`batch_size`,查询或者“键-值”对的个数,`num_hiddens`) - # 输出 `X` 的形状: (`batch_size`,查询或者“键-值”对的个数,`num_heads`, - # `num_hiddens` / `num_heads`) + """为了多注意力头的并行计算而变换形状""" + # 输入`X`的形状:(`batch_size`,查询或者“键-值”对的个数,`num_hiddens`) + # 输出`X`的形状:(`batch_size`,查询或者“键-值”对的个数,`num_heads`, + # `num_hiddens`/`num_heads`) X = tf.reshape(X, shape=(X.shape[0], X.shape[1], num_heads, -1)) - # 输出 `X` 的形状: (`batch_size`,`num_heads`,查询或者“键-值”对的个数, - # `num_hiddens` / `num_heads`) + # 输出`X`的形状:(`batch_size`,`num_heads`,查询或者“键-值”对的个数, + # `num_hiddens`/`num_heads`) X = tf.transpose(X, perm=(0, 2, 1, 3)) - # 最终输出的形状: (`batch_size` * `num_heads`, 查询或者“键-值”对的个数, - # `num_hiddens` / `num_heads`) + # 最终输出的形状:(`batch_size`*`num_heads`,查询或者“键-值”对的个数, + # `num_hiddens`/`num_heads`) return tf.reshape(X, shape=(-1, X.shape[2], X.shape[3])) #@save def transpose_output(X, num_heads): - """逆转 `transpose_qkv` 函数的操作。""" + """逆转`transpose_qkv`函数的操作""" X = tf.reshape(X, shape=(-1, num_heads, X.shape[1], X.shape[2])) X = tf.transpose(X, perm=(0, 2, 1, 3)) return tf.reshape(X, shape=(X.shape[0], X.shape[1], -1)) diff --git a/chapter_attention-mechanisms/nadaraya-waston.md b/chapter_attention-mechanisms/nadaraya-waston.md index 6f664e67e..b47456394 100644 --- a/chapter_attention-mechanisms/nadaraya-waston.md +++ b/chapter_attention-mechanisms/nadaraya-waston.md @@ -193,39 +193,39 @@ $$\begin{aligned} f(x) &=\sum_{i=1}^n \alpha(x, x_i) y_i\\ &= \sum_{i=1}^n \frac 你会发现新的模型预测线是平滑的,并且比平均汇聚的预测更接近真实。 ```{.python .input} -# `X_repeat` 的形状: (`n_test`, `n_train`), +# `X_repeat`的形状:(`n_test`,`n_train`), # 每一行都包含着相同的测试输入(例如:同样的查询) X_repeat = d2l.reshape(x_test.repeat(n_train), (-1, n_train)) -# `x_train` 包含着键。`attention_weights` 的形状:(`n_test`, `n_train`), +# `x_train`包含着键。`attention_weights`的形状:(`n_test`,`n_train`), # 每一行都包含着要在给定的每个查询的值(`y_train`)之间分配的注意力权重 attention_weights = npx.softmax(-(X_repeat - x_train)**2 / 2) -# `y_hat` 的每个元素都是值的加权平均值,其中的权重是注意力权重 +# `y_hat`的每个元素都是值的加权平均值,其中的权重是注意力权重 y_hat = d2l.matmul(attention_weights, y_train) plot_kernel_reg(y_hat) ``` ```{.python .input} #@tab pytorch -# `X_repeat` 的形状: (`n_test`, `n_train`), +# `X_repeat`的形状:(`n_test`,`n_train`), # 每一行都包含着相同的测试输入(例如:同样的查询) X_repeat = d2l.reshape(x_test.repeat_interleave(n_train), (-1, n_train)) -# `x_train` 包含着键。`attention_weights` 的形状:(`n_test`, `n_train`), +# `x_train`包含着键。`attention_weights`的形状:(`n_test`,`n_train`), # 每一行都包含着要在给定的每个查询的值(`y_train`)之间分配的注意力权重 attention_weights = nn.functional.softmax(-(X_repeat - x_train)**2 / 2, dim=1) -# `y_hat` 的每个元素都是值的加权平均值,其中的权重是注意力权重 +# `y_hat`的每个元素都是值的加权平均值,其中的权重是注意力权重 y_hat = d2l.matmul(attention_weights, y_train) plot_kernel_reg(y_hat) ``` ```{.python .input} #@tab tensorflow -# `X_repeat` 的形状: (`n_test`, `n_train`), +# `X_repeat`的形状:(`n_test`,`n_train`), # 每一行都包含着相同的测试输入(例如:同样的查询) X_repeat = tf.repeat(tf.expand_dims(x_train, axis=0), repeats=n_train, axis=0) -# `x_train` 包含着键。`attention_weights` 的形状:(`n_test`, `n_train`), +# `x_train`包含着键。`attention_weights`的形状:(`n_test`,`n_train`), # 每一行都包含着要在给定的每个查询的值(`y_train`)之间分配的注意力权重 attention_weights = tf.nn.softmax(-(X_repeat - tf.expand_dims(x_train, axis=1))**2/2, axis=1) -# `y_hat` 的每个元素都是值的加权平均值,其中的权重是注意力权重 +# `y_hat`的每个元素都是值的加权平均值,其中的权重是注意力权重 y_hat = tf.matmul(attention_weights, tf.expand_dims(y_train, axis=1)) plot_kernel_reg(y_hat) ``` @@ -343,12 +343,12 @@ class NWKernelRegression(nn.Block): self.w = self.params.get('w', shape=(1,)) def forward(self, queries, keys, values): - # `queries` 和 `attention_weights` 的形状为 (查询数,“键-值”对数) + # `queries`和`attention_weights`的形状为(查询数,“键-值”对数) queries = d2l.reshape( queries.repeat(keys.shape[1]), (-1, keys.shape[1])) self.attention_weights = npx.softmax( -((queries - keys) * self.w.data())**2 / 2) - # `values` 的形状为 (查询数,“键-值”对数) + # `values`的形状为(查询数,“键-值”对数) return npx.batch_dot(np.expand_dims(self.attention_weights, 1), np.expand_dims(values, -1)).reshape(-1) ``` @@ -361,12 +361,12 @@ class NWKernelRegression(nn.Module): self.w = nn.Parameter(torch.rand((1,), requires_grad=True)) def forward(self, queries, keys, values): - # `queries` 和 `attention_weights` 的形状为 (查询个数,“键-值”对个数) + # `queries`和`attention_weights`的形状为(查询个数,“键-值”对个数) queries = d2l.reshape( queries.repeat_interleave(keys.shape[1]), (-1, keys.shape[1])) self.attention_weights = nn.functional.softmax( -((queries - keys) * self.w)**2 / 2, dim=1) - # `values` 的形状为 (查询个数,“键-值”对个数) + # `values`的形状为(查询个数,“键-值”对个数) return torch.bmm(self.attention_weights.unsqueeze(1), values.unsqueeze(-1)).reshape(-1) ``` @@ -380,10 +380,10 @@ class NWKernelRegression(tf.keras.layers.Layer): def call(self, queries, keys, values, **kwargs): # 对于训练,“查询”是`x_train`。“键”是每个点的训练数据的距离。“值”为'y_train'。 - # `queries` 和 `attention_weights` 的形状为 (查询个数,“键-值”对个数) + # `queries`和`attention_weights`的形状为(查询个数,“键-值”对个数) queries = tf.repeat(tf.expand_dims(queries, axis=1), repeats=keys.shape[1], axis=1) self.attention_weights = tf.nn.softmax(-((queries - keys) * self.w)**2 /2, axis =1) - # `values` 的形状为 (查询个数,“键-值”对个数) + # `values`的形状为(查询个数,“键-值”对个数) return tf.squeeze(tf.matmul(tf.expand_dims(self.attention_weights, axis=1), tf.expand_dims(values, axis=-1))) ``` @@ -396,41 +396,41 @@ class NWKernelRegression(tf.keras.layers.Layer): 从而得到其对应的预测输出。 ```{.python .input} -# `X_tile` 的形状: (`n_train`,`n_train`),每一行都包含着相同的训练输入 +# `X_tile`的形状:(`n_train`,`n_train`),每一行都包含着相同的训练输入 X_tile = np.tile(x_train, (n_train, 1)) -# `Y_tile` 的形状: (`n_train`,`n_train`),每一行都包含着相同的训练输出 +# `Y_tile`的形状:(`n_train`,`n_train`),每一行都包含着相同的训练输出 Y_tile = np.tile(y_train, (n_train, 1)) -# `keys` 的形状: ('n_train','n_train' - 1) +# `keys`的形状:('n_train','n_train'-1) keys = d2l.reshape(X_tile[(1 - d2l.eye(n_train)).astype('bool')], (n_train, -1)) -# `values` 的形状: ('n_train','n_train' - 1) +# `values`的形状:('n_train','n_train'-1) values = d2l.reshape(Y_tile[(1 - d2l.eye(n_train)).astype('bool')], (n_train, -1)) ``` ```{.python .input} #@tab pytorch -# `X_tile` 的形状: (`n_train`,`n_train`),每一行都包含着相同的训练输入 +# `X_tile`的形状:(`n_train`,`n_train`),每一行都包含着相同的训练输入 X_tile = x_train.repeat((n_train, 1)) -# `Y_tile` 的形状: (`n_train`,`n_train`),每一行都包含着相同的训练输出 +# `Y_tile`的形状:(`n_train`,`n_train`),每一行都包含着相同的训练输出 Y_tile = y_train.repeat((n_train, 1)) -# `keys` 的形状: ('n_train','n_train' - 1) +# `keys`的形状:('n_train','n_train'-1) keys = d2l.reshape(X_tile[(1 - d2l.eye(n_train)).type(torch.bool)], (n_train, -1)) -# `values` 的形状: ('n_train','n_train' - 1) +# `values`的形状:('n_train','n_train'-1) values = d2l.reshape(Y_tile[(1 - d2l.eye(n_train)).type(torch.bool)], (n_train, -1)) ``` ```{.python .input} #@tab tensorflow -# `X_tile` 的形状: (`n_train`,`n_train`),每一行都包含着相同的训练输入 +# `X_tile`的形状:(`n_train`,`n_train`),每一行都包含着相同的训练输入 X_tile = tf.repeat(tf.expand_dims(x_train, axis=0), repeats=n_train, axis=0) -# `Y_tile` 的形状: (`n_train`,`n_train`),每一行都包含着相同的训练输出 +# `Y_tile`的形状:(`n_train`,`n_train`),每一行都包含着相同的训练输出 Y_tile = tf.repeat(tf.expand_dims(y_train, axis=0), repeats=n_train, axis=0) -# `keys` 的形状: ('n_train','n_train' - 1) +# `keys`的形状:('n_train','n_train'-1) keys = tf.reshape(X_tile[tf.cast(1 - tf.eye(n_train), dtype=tf.bool)], shape=(n_train, -1)) -# `values` 的形状: ('n_train','n_train' - 1) +# `values`的形状:('n_train','n_train'-1) values = tf.reshape(Y_tile[tf.cast(1 - tf.eye(n_train), dtype=tf.bool)], shape=(n_train, -1)) ``` @@ -461,7 +461,7 @@ animator = d2l.Animator(xlabel='epoch', ylabel='loss', xlim=[1, 5]) for epoch in range(5): trainer.zero_grad() - # L2 Loss = 1/2 * MSE Loss + # L2Loss=1/2*MSELoss l = loss(net(x_train, keys, values), y_train) / 2 l.sum().backward() trainer.step() @@ -491,9 +491,9 @@ for epoch in range(5): [**预测结果绘制**]的线不如之前非参数模型的平滑。 ```{.python .input} -# `keys` 的形状: (`n_test`,`n_train`),每一行包含着相同的训练输入(例如,相同的键) +# `keys`的形状:(`n_test`,`n_train`),每一行包含着相同的训练输入(例如,相同的键) keys = np.tile(x_train, (n_test, 1)) -# `value` 的形状: (`n_test`,`n_train`) +# `value`的形状:(`n_test`,`n_train`) values = np.tile(y_train, (n_test, 1)) y_hat = net(x_test, keys, values) plot_kernel_reg(y_hat) @@ -501,9 +501,9 @@ plot_kernel_reg(y_hat) ```{.python .input} #@tab pytorch -# `keys` 的形状: (`n_test`,`n_train`),每一行包含着相同的训练输入(例如,相同的键) +# `keys`的形状:(`n_test`,`n_train`),每一行包含着相同的训练输入(例如,相同的键) keys = x_train.repeat((n_test, 1)) -# `value` 的形状: (`n_test`,`n_train`) +# `value`的形状:(`n_test`,`n_train`) values = y_train.repeat((n_test, 1)) y_hat = net(x_test, keys, values).unsqueeze(1).detach() plot_kernel_reg(y_hat) @@ -511,9 +511,9 @@ plot_kernel_reg(y_hat) ```{.python .input} #@tab tensorflow -# `keys` 的形状: (`n_test`,`n_train`),每一行包含着相同的训练输入(例如,相同的键) +# `keys`的形状:(`n_test`,`n_train`),每一行包含着相同的训练输入(例如,相同的键) keys = tf.repeat(tf.expand_dims(x_train, axis=0), repeats=n_test, axis=0) -# `value` 的形状: (`n_test`,`n_train`) +# `value`的形状:(`n_test`,`n_train`) values = tf.repeat(tf.expand_dims(y_train, axis=0), repeats=n_test, axis=0) y_hat = net(x_test, keys, values) plot_kernel_reg(y_hat) diff --git a/chapter_attention-mechanisms/self-attention-and-positional-encoding.md b/chapter_attention-mechanisms/self-attention-and-positional-encoding.md index 1770c6ecc..92e26663a 100644 --- a/chapter_attention-mechanisms/self-attention-and-positional-encoding.md +++ b/chapter_attention-mechanisms/self-attention-and-positional-encoding.md @@ -151,7 +151,7 @@ class PositionalEncoding(nn.Block): def __init__(self, num_hiddens, dropout, max_len=1000): super(PositionalEncoding, self).__init__() self.dropout = nn.Dropout(dropout) - # 创建一个足够长的 `P` + # 创建一个足够长的`P` self.P = d2l.zeros((1, max_len, num_hiddens)) X = d2l.arange(max_len).reshape(-1, 1) / np.power( 10000, np.arange(0, num_hiddens, 2) / num_hiddens) @@ -171,7 +171,7 @@ class PositionalEncoding(nn.Module): def __init__(self, num_hiddens, dropout, max_len=1000): super(PositionalEncoding, self).__init__() self.dropout = nn.Dropout(dropout) - # 创建一个足够长的 `P` + # 创建一个足够长的`P` self.P = d2l.zeros((1, max_len, num_hiddens)) X = d2l.arange(max_len, dtype=torch.float32).reshape( -1, 1) / torch.pow(10000, torch.arange( @@ -192,7 +192,7 @@ class PositionalEncoding(tf.keras.layers.Layer): def __init__(self, num_hiddens, dropout, max_len=1000): super().__init__() self.dropout = tf.keras.layers.Dropout(dropout) - # 创建一个足够长的 `P` + # 创建一个足够长的`P` self.P = np.zeros((1, max_len, num_hiddens)) X = np.arange(max_len, dtype=np.float32).reshape( -1,1)/np.power(10000, np.arange( diff --git a/chapter_attention-mechanisms/transformer.md b/chapter_attention-mechanisms/transformer.md index 7e476eaa3..5b9aef990 100644 --- a/chapter_attention-mechanisms/transformer.md +++ b/chapter_attention-mechanisms/transformer.md @@ -127,7 +127,7 @@ ln.initialize() bn = nn.BatchNorm() bn.initialize() X = d2l.tensor([[1, 2], [2, 3]]) -# 在训练模式下计算 `X` 的均值和方差 +# 在训练模式下计算`X`的均值和方差 with autograd.record(): print('层规范化:', ln(X), '\n批量规范化:', bn(X)) ``` @@ -137,7 +137,7 @@ with autograd.record(): ln = nn.LayerNorm(2) bn = nn.BatchNorm1d(2) X = d2l.tensor([[1, 2], [2, 3]], dtype=torch.float32) -# 在训练模式下计算 `X` 的均值和方差 +# 在训练模式下计算`X`的均值和方差 print('layer norm:', ln(X), '\nbatch norm:', bn(X)) ``` @@ -323,7 +323,7 @@ class TransformerEncoder(d2l.Encoder): use_bias)) def forward(self, X, valid_lens, *args): - # 因为位置编码值在 -1 和 1 之间, + # 因为位置编码值在-1和1之间, # 因此嵌入值乘以嵌入维度的平方根进行缩放, # 然后再与位置编码相加。 X = self.pos_encoding(self.embedding(X) * math.sqrt(self.num_hiddens)) @@ -355,7 +355,7 @@ class TransformerEncoder(d2l.Encoder): num_heads, dropout, use_bias)) def forward(self, X, valid_lens, *args): - # 因为位置编码值在 -1 和 1 之间, + # 因为位置编码值在-1和1之间, # 因此嵌入值乘以嵌入维度的平方根进行缩放, # 然后再与位置编码相加。 X = self.pos_encoding(self.embedding(X) * math.sqrt(self.num_hiddens)) @@ -385,7 +385,7 @@ class TransformerEncoder(d2l.Encoder): num_layers)] def call(self, X, valid_lens, **kwargs): - # 因为位置编码值在 -1 和 1 之间, + # 因为位置编码值在-1和1之间, # 因此嵌入值乘以嵌入维度的平方根进行缩放, # 然后再与位置编码相加。 X = self.pos_encoding(self.embedding(X) * tf.math.sqrt( @@ -429,7 +429,7 @@ encoder(tf.ones((2, 100)), valid_lens, training=False).shape ```{.python .input} class DecoderBlock(nn.Block): - """解码器中第 i 个块""" + """解码器中第i个块""" def __init__(self, num_hiddens, ffn_num_hiddens, num_heads, dropout, i, **kwargs): super(DecoderBlock, self).__init__(**kwargs) @@ -446,9 +446,9 @@ class DecoderBlock(nn.Block): def forward(self, X, state): enc_outputs, enc_valid_lens = state[0], state[1] # 训练阶段,输出序列的所有词元都在同一时间处理, - # 因此 `state[2][self.i]` 初始化为 `None`。 + # 因此`state[2][self.i]`初始化为`None`。 # 预测阶段,输出序列是通过词元一个接着一个解码的, - # 因此 `state[2][self.i]` 包含着直到当前时间步第 `i` 个块解码的输出表示 + # 因此`state[2][self.i]`包含着直到当前时间步第`i`个块解码的输出表示 if state[2][self.i] is None: key_values = X else: @@ -457,8 +457,8 @@ class DecoderBlock(nn.Block): if autograd.is_training(): batch_size, num_steps, _ = X.shape - # `dec_valid_lens` 的开头: (`batch_size`, `num_steps`), - # 其中每一行是 [1, 2, ..., `num_steps`] + # `dec_valid_lens`的开头:(`batch_size`,`num_steps`), + # 其中每一行是[1,2,...,`num_steps`] dec_valid_lens = np.tile(np.arange(1, num_steps + 1, ctx=X.ctx), (batch_size, 1)) else: @@ -468,7 +468,7 @@ class DecoderBlock(nn.Block): X2 = self.attention1(X, key_values, key_values, dec_valid_lens) Y = self.addnorm1(X, X2) # “编码器-解码器”注意力。 - # 'enc_outputs' 的开头: ('batch_size', 'num_steps', 'num_hiddens') + # 'enc_outputs'的开头:('batch_size','num_steps','num_hiddens') Y2 = self.attention2(Y, enc_outputs, enc_outputs, enc_valid_lens) Z = self.addnorm2(Y, Y2) return self.addnorm3(Z, self.ffn(Z)), state @@ -477,7 +477,7 @@ class DecoderBlock(nn.Block): ```{.python .input} #@tab pytorch class DecoderBlock(nn.Module): - """解码器中第 i 个块""" + """解码器中第i个块""" def __init__(self, key_size, query_size, value_size, num_hiddens, norm_shape, ffn_num_input, ffn_num_hiddens, num_heads, dropout, i, **kwargs): @@ -496,9 +496,9 @@ class DecoderBlock(nn.Module): def forward(self, X, state): enc_outputs, enc_valid_lens = state[0], state[1] # 训练阶段,输出序列的所有词元都在同一时间处理, - # 因此 `state[2][self.i]` 初始化为 `None`。 + # 因此`state[2][self.i]`初始化为`None`。 # 预测阶段,输出序列是通过词元一个接着一个解码的, - # 因此 `state[2][self.i]` 包含着直到当前时间步第 `i` 个块解码的输出表示 + # 因此`state[2][self.i]`包含着直到当前时间步第`i`个块解码的输出表示 if state[2][self.i] is None: key_values = X else: @@ -506,8 +506,8 @@ class DecoderBlock(nn.Module): state[2][self.i] = key_values if self.training: batch_size, num_steps, _ = X.shape - # `dec_valid_lens` 的开头: (`batch_size`, `num_steps`), - # 其中每一行是 [1, 2, ..., `num_steps`] + # `dec_valid_lens`的开头:(`batch_size`,`num_steps`), + # 其中每一行是[1,2,...,`num_steps`] dec_valid_lens = torch.arange( 1, num_steps + 1, device=X.device).repeat(batch_size, 1) else: @@ -517,7 +517,7 @@ class DecoderBlock(nn.Module): X2 = self.attention1(X, key_values, key_values, dec_valid_lens) Y = self.addnorm1(X, X2) # 编码器-解码器注意力。 - # `enc_outputs` 的开头: (`batch_size`, `num_steps`, `num_hiddens`) + # `enc_outputs`的开头:(`batch_size`,`num_steps`,`num_hiddens`) Y2 = self.attention2(Y, enc_outputs, enc_outputs, enc_valid_lens) Z = self.addnorm2(Y, Y2) return self.addnorm3(Z, self.ffn(Z)), state @@ -526,7 +526,7 @@ class DecoderBlock(nn.Module): ```{.python .input} #@tab tensorflow class DecoderBlock(tf.keras.layers.Layer): - """解码器中第 i 个块""" + """解码器中第i个块""" def __init__(self, key_size, query_size, value_size, num_hiddens, norm_shape, ffn_num_hiddens, num_heads, dropout, i, **kwargs): super().__init__(**kwargs) @@ -541,9 +541,9 @@ class DecoderBlock(tf.keras.layers.Layer): def call(self, X, state, **kwargs): enc_outputs, enc_valid_lens = state[0], state[1] # 训练阶段,输出序列的所有词元都在同一时间处理, - # 因此 `state[2][self.i]` 初始化为 `None`。 + # 因此`state[2][self.i]`初始化为`None`。 # 预测阶段,输出序列是通过词元一个接着一个解码的, - # 因此 `state[2][self.i]` 包含着直到当前时间步第 `i` 个块解码的输出表示 + # 因此`state[2][self.i]`包含着直到当前时间步第`i`个块解码的输出表示 if state[2][self.i] is None: key_values = X else: @@ -551,8 +551,8 @@ class DecoderBlock(tf.keras.layers.Layer): state[2][self.i] = key_values if kwargs["training"]: batch_size, num_steps, _ = X.shape - # `dec_valid_lens` 的开头: (`batch_size`, `num_steps`), - # 其中每一行是 [1, 2, ..., `num_steps`] + # `dec_valid_lens`的开头:(`batch_size`,`num_steps`), + # 其中每一行是[1,2,...,`num_steps`] dec_valid_lens = tf.repeat(tf.reshape(tf.range(1, num_steps + 1), shape=(-1, num_steps)), repeats=batch_size, axis=0) @@ -563,7 +563,7 @@ class DecoderBlock(tf.keras.layers.Layer): X2 = self.attention1(X, key_values, key_values, dec_valid_lens, **kwargs) Y = self.addnorm1(X, X2, **kwargs) # 编码器-解码器注意力。 - # `enc_outputs` 的开头: (`batch_size`, `num_steps`, `num_hiddens`) + # `enc_outputs`的开头:(`batch_size`,`num_steps`,`num_hiddens`) Y2 = self.attention2(Y, enc_outputs, enc_outputs, enc_valid_lens, **kwargs) Z = self.addnorm2(Y, Y2, **kwargs) return self.addnorm3(Z, self.ffn(Z), **kwargs), state @@ -869,7 +869,7 @@ print(dec_self_attention_weights.shape, dec_inter_attention_weights.shape) ```{.python .input} #@tab all -# Plus one to include the beginning-of-sequence token +# Plusonetoincludethebeginning-of-sequencetoken d2l.show_heatmaps( dec_self_attention_weights[:, :, :, :len(translation.split()) + 1], xlabel='Key positions', ylabel='Query positions', diff --git a/chapter_computational-performance/multiple-gpus-concise.md b/chapter_computational-performance/multiple-gpus-concise.md index 155f91a1b..0ee1c7d10 100644 --- a/chapter_computational-performance/multiple-gpus-concise.md +++ b/chapter_computational-performance/multiple-gpus-concise.md @@ -137,7 +137,7 @@ weight.data(devices[0])[0], weight.data(devices[1])[0] ```{.python .input} #@save def evaluate_accuracy_gpus(net, data_iter, split_f=d2l.split_batch): - """使用多个GPU计算数据集上模型的精度。""" + """使用多个GPU计算数据集上模型的精度""" # 查询设备列表 devices = list(net.collect_params().values())[0].list_ctx() # 正确预测的数量,预测的总数量 diff --git a/chapter_computational-performance/multiple-gpus.md b/chapter_computational-performance/multiple-gpus.md index dbe58b8ba..af4ddb330 100644 --- a/chapter_computational-performance/multiple-gpus.md +++ b/chapter_computational-performance/multiple-gpus.md @@ -352,7 +352,7 @@ def train(num_gpus, batch_size, lr): train_batch(X, y, device_params, devices, lr) npx.waitall() timer.stop() - # 在GPU 0 上评估模型 + # 在GPU0上评估模型 animator.add(epoch + 1, (d2l.evaluate_accuracy_gpu( lambda x: lenet(x, device_params[0]), test_iter, devices[0]),)) print(f'测试精度:{animator.Y[0][-1]:.2f},{timer.avg():.1f}秒/轮,' @@ -376,7 +376,7 @@ def train(num_gpus, batch_size, lr): train_batch(X, y, device_params, devices, lr) torch.cuda.synchronize() timer.stop() - # 在GPU 0上评估模型 + # 在GPU0上评估模型 animator.add(epoch + 1, (d2l.evaluate_accuracy_gpu( lambda x: lenet(x, device_params[0]), test_iter, devices[0]),)) print(f'测试精度:{animator.Y[0][-1]:.2f},{timer.avg():.1f}秒/轮,' diff --git a/chapter_computer-vision/anchor.md b/chapter_computer-vision/anchor.md index c7517faa9..435d88805 100644 --- a/chapter_computer-vision/anchor.md +++ b/chapter_computer-vision/anchor.md @@ -51,7 +51,7 @@ $$(s_1, r_1), (s_1, r_2), \ldots, (s_1, r_m), (s_2, r_1), (s_3, r_1), \ldots, (s ```{.python .input} #@save def multibox_prior(data, sizes, ratios): - """生成以每个像素为中心具有不同形状的锚框。""" + """生成以每个像素为中心具有不同形状的锚框""" in_height, in_width = data.shape[-2:] device, num_sizes, num_ratios = data.ctx, len(sizes), len(ratios) boxes_per_pixel = (num_sizes + num_ratios - 1) @@ -71,7 +71,7 @@ def multibox_prior(data, sizes, ratios): shift_x, shift_y = shift_x.reshape(-1), shift_y.reshape(-1) # 生成“boxes_per_pixel”个高和宽, - # 之后用于创建锚框的四角坐标 (xmin, xmax, ymin, ymax) + # 之后用于创建锚框的四角坐标(xmin,xmax,ymin,ymax) w = np.concatenate((size_tensor * np.sqrt(ratio_tensor[0]), sizes[0] * np.sqrt(ratio_tensor[1:]))) \ * in_height / in_width # 处理矩形输入 @@ -93,7 +93,7 @@ def multibox_prior(data, sizes, ratios): #@tab pytorch #@save def multibox_prior(data, sizes, ratios): - """生成以每个像素为中心具有不同形状的锚框。""" + """生成以每个像素为中心具有不同形状的锚框""" in_height, in_width = data.shape[-2:] device, num_sizes, num_ratios = data.device, len(sizes), len(ratios) boxes_per_pixel = (num_sizes + num_ratios - 1) @@ -113,7 +113,7 @@ def multibox_prior(data, sizes, ratios): shift_y, shift_x = shift_y.reshape(-1), shift_x.reshape(-1) # 生成“boxes_per_pixel”个高和宽, - # 之后用于创建锚框的四角坐标 (xmin, xmax, ymin, ymax) + # 之后用于创建锚框的四角坐标(xmin,xmax,ymin,ymax) w = torch.cat((size_tensor * torch.sqrt(ratio_tensor[0]), sizes[0] * torch.sqrt(ratio_tensor[1:])))\ * in_height / in_width # 处理矩形输入 @@ -171,7 +171,7 @@ boxes[250, 250, 0, :] #@tab all #@save def show_bboxes(axes, bboxes, labels=None, colors=None): - """显示所有边界框。""" + """显示所有边界框""" def _make_list(obj, default_values=None): if obj is None: obj = default_values @@ -232,23 +232,23 @@ $$J(\mathcal{A},\mathcal{B}) = \frac{\left|\mathcal{A} \cap \mathcal{B}\right|}{ ```{.python .input} #@save def box_iou(boxes1, boxes2): - """计算两个锚框或边界框列表中成对的交并比。""" + """计算两个锚框或边界框列表中成对的交并比""" box_area = lambda boxes: ((boxes[:, 2] - boxes[:, 0]) * (boxes[:, 3] - boxes[:, 1])) - # `boxes1`, `boxes2`, `areas1`, `areas2`的形状: - # `boxes1`:(boxes1的数量, 4), - # `boxes2`:(boxes2的数量, 4), - # `areas1`:(boxes1的数量,), + # `boxes1`,`boxes2`,`areas1`,`areas2`的形状: + # `boxes1`:(boxes1的数量,4), + # `boxes2`:(boxes2的数量,4), + # `areas1`:(boxes1的数量,), # `areas2`:(boxes2的数量,) areas1 = box_area(boxes1) areas2 = box_area(boxes2) - # `inter_upperlefts`, `inter_lowerrights`, `inters`的形状: - # (boxes1的数量, boxes2的数量, 2) + # `inter_upperlefts`,`inter_lowerrights`,`inters`的形状: + # (boxes1的数量,boxes2的数量,2) inter_upperlefts = np.maximum(boxes1[:, None, :2], boxes2[:, :2]) inter_lowerrights = np.minimum(boxes1[:, None, 2:], boxes2[:, 2:]) inters = (inter_lowerrights - inter_upperlefts).clip(min=0) - # `inter_areas` and `union_areas`的形状: (boxes1的数量, boxes2的数量) + # `inter_areas`and`union_areas`的形状:(boxes1的数量,boxes2的数量) inter_areas = inters[:, :, 0] * inters[:, :, 1] union_areas = areas1[:, None] + areas2 - inter_areas return inter_areas / union_areas @@ -258,22 +258,22 @@ def box_iou(boxes1, boxes2): #@tab pytorch #@save def box_iou(boxes1, boxes2): - """计算两个锚框或边界框列表中成对的交并比。""" + """计算两个锚框或边界框列表中成对的交并比""" box_area = lambda boxes: ((boxes[:, 2] - boxes[:, 0]) * (boxes[:, 3] - boxes[:, 1])) - # `boxes1`, `boxes2`, `areas1`, `areas2`的形状: - # `boxes1`:(boxes1的数量, 4), - # `boxes2`:(boxes2的数量, 4), - # `areas1`:(boxes1的数量,), + # `boxes1`,`boxes2`,`areas1`,`areas2`的形状: + # `boxes1`:(boxes1的数量,4), + # `boxes2`:(boxes2的数量,4), + # `areas1`:(boxes1的数量,), # `areas2`:(boxes2的数量,) areas1 = box_area(boxes1) areas2 = box_area(boxes2) - # `inter_upperlefts`, `inter_lowerrights`, `inters`的形状: - # (boxes1的数量, boxes2的数量, 2) + # `inter_upperlefts`,`inter_lowerrights`,`inters`的形状: + # (boxes1的数量,boxes2的数量,2) inter_upperlefts = torch.max(boxes1[:, None, :2], boxes2[:, :2]) inter_lowerrights = torch.min(boxes1[:, None, 2:], boxes2[:, 2:]) inters = (inter_lowerrights - inter_upperlefts).clamp(min=0) - # `inter_areas` and `union_areas`的形状: (boxes1的数量, boxes2的数量) + # `inter_areas`and`union_areas`的形状:(boxes1的数量,boxes2的数量) inter_areas = inters[:, :, 0] * inters[:, :, 1] union_areas = areas1[:, None] + areas2 - inter_areas return inter_areas / union_areas @@ -316,9 +316,9 @@ def box_iou(boxes1, boxes2): ```{.python .input} #@save def assign_anchor_to_bbox(ground_truth, anchors, device, iou_threshold=0.5): - """将最接近的真实边界框分配给锚框。""" + """将最接近的真实边界框分配给锚框""" num_anchors, num_gt_boxes = anchors.shape[0], ground_truth.shape[0] - # 位于第i行和第j列的元素 x_ij 是锚框i和真实边界框j的IoU + # 位于第i行和第j列的元素x_ij是锚框i和真实边界框j的IoU jaccard = box_iou(anchors, ground_truth) # 对于每个锚框,分配的真实边界框的张量 anchors_bbox_map = np.full((num_anchors,), -1, dtype=np.int32, ctx=device) @@ -343,9 +343,9 @@ def assign_anchor_to_bbox(ground_truth, anchors, device, iou_threshold=0.5): #@tab pytorch #@save def assign_anchor_to_bbox(ground_truth, anchors, device, iou_threshold=0.5): - """将最接近的真实边界框分配给锚框。""" + """将最接近的真实边界框分配给锚框""" num_anchors, num_gt_boxes = anchors.shape[0], ground_truth.shape[0] - # 位于第i行和第j列的元素 x_ij 是锚框i和真实边界框j的IoU + # 位于第i行和第j列的元素x_ij是锚框i和真实边界框j的IoU jaccard = box_iou(anchors, ground_truth) # 对于每个锚框,分配的真实边界框的张量 anchors_bbox_map = torch.full((num_anchors,), -1, dtype=torch.long, @@ -390,7 +390,7 @@ $$\left( \frac{ \frac{x_b - x_a}{w_a} - \mu_x }{\sigma_x}, #@tab all #@save def offset_boxes(anchors, assigned_bb, eps=1e-6): - """对锚框偏移量的转换。""" + """对锚框偏移量的转换""" c_anc = d2l.box_corner_to_center(anchors) c_assigned_bb = d2l.box_corner_to_center(assigned_bb) offset_xy = 10 * (c_assigned_bb[:, :2] - c_anc[:, :2]) / c_anc[:, 2:] @@ -407,7 +407,7 @@ def offset_boxes(anchors, assigned_bb, eps=1e-6): ```{.python .input} #@save def multibox_target(anchors, labels): - """使用真实边界框标记锚框。""" + """使用真实边界框标记锚框""" batch_size, anchors = labels.shape[0], anchors.squeeze(0) batch_offset, batch_mask, batch_class_labels = [], [], [] device, num_anchors = anchors.ctx, anchors.shape[0] @@ -442,7 +442,7 @@ def multibox_target(anchors, labels): #@tab pytorch #@save def multibox_target(anchors, labels): - """使用真实边界框标记锚框。""" + """使用真实边界框标记锚框""" batch_size, anchors = labels.shape[0], anchors.squeeze(0) batch_offset, batch_mask, batch_class_labels = [], [], [] device, num_anchors = anchors.device, anchors.shape[0] @@ -555,7 +555,7 @@ labels[0] #@tab all #@save def offset_inverse(anchors, offset_preds): - """根据带有预测偏移量的锚框来预测边界框。""" + """根据带有预测偏移量的锚框来预测边界框""" anc = d2l.box_corner_to_center(anchors) pred_bbox_xy = (offset_preds[:, :2] * anc[:, 2:] / 10) + anc[:, :2] pred_bbox_wh = d2l.exp(offset_preds[:, 2:] / 5) * anc[:, 2:] @@ -583,7 +583,7 @@ def offset_inverse(anchors, offset_preds): ```{.python .input} #@save def nms(boxes, scores, iou_threshold): - """对预测边界框的置信度进行排序。""" + """对预测边界框的置信度进行排序""" B = scores.argsort()[::-1] keep = [] # 保留预测边界框的指标 while B.size > 0: @@ -601,7 +601,7 @@ def nms(boxes, scores, iou_threshold): #@tab pytorch #@save def nms(boxes, scores, iou_threshold): - """对预测边界框的置信度进行排序。""" + """对预测边界框的置信度进行排序""" B = torch.argsort(scores, dim=-1, descending=True) keep = [] # 保留预测边界框的指标 while B.numel() > 0: @@ -622,7 +622,7 @@ def nms(boxes, scores, iou_threshold): #@save def multibox_detection(cls_probs, offset_preds, anchors, nms_threshold=0.5, pos_threshold=0.009999999): - """使用非极大值抑制来预测边界框。""" + """使用非极大值抑制来预测边界框""" device, batch_size = cls_probs.ctx, cls_probs.shape[0] anchors = np.squeeze(anchors, axis=0) num_classes, num_anchors = cls_probs.shape[1], cls_probs.shape[2] @@ -633,7 +633,7 @@ def multibox_detection(cls_probs, offset_preds, anchors, nms_threshold=0.5, predicted_bb = offset_inverse(anchors, offset_pred) keep = nms(predicted_bb, conf, nms_threshold) - # 找到所有的 non_keep 索引,并将类设置为背景 + # 找到所有的non_keep索引,并将类设置为背景 all_idx = np.arange(num_anchors, dtype=np.int32, ctx=device) combined = d2l.concat((keep, all_idx)) unique, counts = np.unique(combined, return_counts=True) @@ -642,7 +642,7 @@ def multibox_detection(cls_probs, offset_preds, anchors, nms_threshold=0.5, class_id[non_keep] = -1 class_id = class_id[all_id_sorted].astype('float32') conf, predicted_bb = conf[all_id_sorted], predicted_bb[all_id_sorted] - # `pos_threshold` 是一个用于非背景预测的阈值 + # `pos_threshold`是一个用于非背景预测的阈值 below_min_idx = (conf < pos_threshold) class_id[below_min_idx] = -1 conf[below_min_idx] = 1 - conf[below_min_idx] @@ -658,7 +658,7 @@ def multibox_detection(cls_probs, offset_preds, anchors, nms_threshold=0.5, #@save def multibox_detection(cls_probs, offset_preds, anchors, nms_threshold=0.5, pos_threshold=0.009999999): - """使用非极大值抑制来预测边界框。""" + """使用非极大值抑制来预测边界框""" device, batch_size = cls_probs.device, cls_probs.shape[0] anchors = anchors.squeeze(0) num_classes, num_anchors = cls_probs.shape[1], cls_probs.shape[2] @@ -669,7 +669,7 @@ def multibox_detection(cls_probs, offset_preds, anchors, nms_threshold=0.5, predicted_bb = offset_inverse(anchors, offset_pred) keep = nms(predicted_bb, conf, nms_threshold) - # 找到所有的 non_keep 索引,并将类设置为背景 + # 找到所有的non_keep索引,并将类设置为背景 all_idx = torch.arange(num_anchors, dtype=torch.long, device=device) combined = torch.cat((keep, all_idx)) uniques, counts = combined.unique(return_counts=True) @@ -678,7 +678,7 @@ def multibox_detection(cls_probs, offset_preds, anchors, nms_threshold=0.5, class_id[non_keep] = -1 class_id = class_id[all_id_sorted] conf, predicted_bb = conf[all_id_sorted], predicted_bb[all_id_sorted] - # `pos_threshold` 是一个用于非背景预测的阈值 + # `pos_threshold`是一个用于非背景预测的阈值 below_min_idx = (conf < pos_threshold) class_id[below_min_idx] = -1 conf[below_min_idx] = 1 - conf[below_min_idx] @@ -698,7 +698,7 @@ def multibox_detection(cls_probs, offset_preds, anchors, nms_threshold=0.5, anchors = d2l.tensor([[0.1, 0.08, 0.52, 0.92], [0.08, 0.2, 0.56, 0.95], [0.15, 0.3, 0.62, 0.91], [0.55, 0.2, 0.9, 0.88]]) offset_preds = d2l.tensor([0] * d2l.size(anchors)) -cls_probs = d2l.tensor([[0] * 4, # 背景的预测概率 +cls_probs = d2l.tensor([[0] * 4, # 背景的预测概率 [0.9, 0.8, 0.7, 0.1], # 狗的预测概率 [0.1, 0.2, 0.3, 0.9]]) # 猫的预测概率 ``` diff --git a/chapter_computer-vision/bounding-box.md b/chapter_computer-vision/bounding-box.md index 12964be38..4994fb56f 100644 --- a/chapter_computer-vision/bounding-box.md +++ b/chapter_computer-vision/bounding-box.md @@ -110,8 +110,8 @@ box_center_to_corner(box_corner_to_center(boxes)) == boxes #@tab all #@save def bbox_to_rect(bbox, color): - # 将边界框 (左上x, 左上y, 右下x, 右下y) 格式转换成 matplotlib 格式: - # ((左上x, 左上y), 宽, 高) + # 将边界框(左上x,左上y,右下x,右下y)格式转换成matplotlib格式: + # ((左上x,左上y),宽,高) return d2l.plt.Rectangle( xy=(bbox[0], bbox[1]), width=bbox[2]-bbox[0], height=bbox[3]-bbox[1], fill=False, edgecolor=color, linewidth=2) diff --git a/chapter_computer-vision/fine-tuning.md b/chapter_computer-vision/fine-tuning.md index 97c8f203c..566731d05 100644 --- a/chapter_computer-vision/fine-tuning.md +++ b/chapter_computer-vision/fine-tuning.md @@ -232,7 +232,7 @@ def train_fine_tuning(net, learning_rate, batch_size=128, num_epochs=5): ```{.python .input} #@tab pytorch -# 如果 `param_group=True`,输出层中的模型参数将使用十倍的学习率 +# 如果`param_group=True`,输出层中的模型参数将使用十倍的学习率 def train_fine_tuning(net, learning_rate, batch_size=128, num_epochs=5, param_group=True): train_iter = torch.utils.data.DataLoader(torchvision.datasets.ImageFolder( diff --git a/chapter_computer-vision/kaggle-cifar10.md b/chapter_computer-vision/kaggle-cifar10.md index c4aa14b6b..126e156bc 100644 --- a/chapter_computer-vision/kaggle-cifar10.md +++ b/chapter_computer-vision/kaggle-cifar10.md @@ -73,7 +73,7 @@ import shutil d2l.DATA_HUB['cifar10_tiny'] = (d2l.DATA_URL + 'kaggle_cifar10_tiny.zip', '2068874e4b9a9f0fb07ebe0ad2b29754449ccacd') -# 如果你使用完整的Kaggle竞赛的数据集,设置`demo`为 False +# 如果你使用完整的Kaggle竞赛的数据集,设置`demo`为False demo = True if demo: @@ -91,9 +91,9 @@ else: #@tab all #@save def read_csv_labels(fname): - """读取 `fname` 来给标签字典返回一个文件名。""" + """读取`fname`来给标签字典返回一个文件名""" with open(fname, 'r') as f: - # 跳过文件头行 (列名) + # 跳过文件头行(列名) lines = f.readlines()[1:] tokens = [l.rstrip().split(',') for l in lines] return dict(((name, label) for name, label in tokens)) @@ -114,7 +114,7 @@ print('# 类别 :', len(set(labels.values()))) #@tab all #@save def copyfile(filename, target_dir): - """将文件复制到目标目录。""" + """将文件复制到目标目录""" os.makedirs(target_dir, exist_ok=True) shutil.copy(filename, target_dir) diff --git a/chapter_computer-vision/kaggle-dog.md b/chapter_computer-vision/kaggle-dog.md index 406787a3e..2b250238e 100644 --- a/chapter_computer-vision/kaggle-dog.md +++ b/chapter_computer-vision/kaggle-dog.md @@ -90,7 +90,7 @@ reorg_dog_data(data_dir, valid_ratio) ```{.python .input} transform_train = gluon.data.vision.transforms.Compose([ - # 随机裁剪图像,所得图像为原始面积的0.08到1之间,高宽比在3/4和4/3之间。 + # 随机裁剪图像,所得图像为原始面积的0.08到1之间,高宽比在3/4和4/3之间。 # 然后,缩放图像以创建224x224的新图像 gluon.data.vision.transforms.RandomResizedCrop(224, scale=(0.08, 1.0), ratio=(3.0/4.0, 4.0/3.0)), @@ -110,8 +110,8 @@ transform_train = gluon.data.vision.transforms.Compose([ ```{.python .input} #@tab pytorch transform_train = torchvision.transforms.Compose([ - # 随机裁剪图像,所得图像为原始面积的0.08到1之间,高宽比在3/4和4/3之间。 - # 然后,缩放图像以创建224 x 224的新图像 + # 随机裁剪图像,所得图像为原始面积的0.08到1之间,高宽比在3/4和4/3之间。 + # 然后,缩放图像以创建224x224的新图像 torchvision.transforms.RandomResizedCrop(224, scale=(0.08, 1.0), ratio=(3.0/4.0, 4.0/3.0)), torchvision.transforms.RandomHorizontalFlip(), diff --git a/chapter_computer-vision/object-detection-dataset.md b/chapter_computer-vision/object-detection-dataset.md index 8eb0c39d5..338f6f54e 100644 --- a/chapter_computer-vision/object-detection-dataset.md +++ b/chapter_computer-vision/object-detection-dataset.md @@ -47,7 +47,7 @@ d2l.DATA_HUB['banana-detection'] = ( ```{.python .input} #@save def read_data_bananas(is_train=True): - """读取香蕉检测数据集中的图像和标签。""" + """读取香蕉检测数据集中的图像和标签""" data_dir = d2l.download_extract('banana-detection') csv_fname = os.path.join(data_dir, 'bananas_train' if is_train else 'bananas_val', 'label.csv') @@ -68,7 +68,7 @@ def read_data_bananas(is_train=True): #@tab pytorch #@save def read_data_bananas(is_train=True): - """读取香蕉检测数据集中的图像和标签。""" + """读取香蕉检测数据集中的图像和标签""" data_dir = d2l.download_extract('banana-detection') csv_fname = os.path.join(data_dir, 'bananas_train' if is_train else 'bananas_val', 'label.csv') @@ -90,7 +90,7 @@ def read_data_bananas(is_train=True): ```{.python .input} #@save class BananasDataset(gluon.data.Dataset): - """一个用于加载香蕉检测数据集的自定义数据集。""" + """一个用于加载香蕉检测数据集的自定义数据集""" def __init__(self, is_train): self.features, self.labels = read_data_bananas(is_train) print('read ' + str(len(self.features)) + (f' training examples' if @@ -108,7 +108,7 @@ class BananasDataset(gluon.data.Dataset): #@tab pytorch #@save class BananasDataset(torch.utils.data.Dataset): - """一个用于加载香蕉检测数据集的自定义数据集。""" + """一个用于加载香蕉检测数据集的自定义数据集""" def __init__(self, is_train): self.features, self.labels = read_data_bananas(is_train) print('read ' + str(len(self.features)) + (f' training examples' if @@ -126,7 +126,7 @@ class BananasDataset(torch.utils.data.Dataset): ```{.python .input} #@save def load_data_bananas(batch_size): - """加载香蕉检测数据集。""" + """加载香蕉检测数据集""" train_iter = gluon.data.DataLoader(BananasDataset(is_train=True), batch_size, shuffle=True) val_iter = gluon.data.DataLoader(BananasDataset(is_train=False), @@ -138,7 +138,7 @@ def load_data_bananas(batch_size): #@tab pytorch #@save def load_data_bananas(batch_size): - """加载香蕉检测数据集。""" + """加载香蕉检测数据集""" train_iter = torch.utils.data.DataLoader(BananasDataset(is_train=True), batch_size, shuffle=True) val_iter = torch.utils.data.DataLoader(BananasDataset(is_train=False), diff --git a/chapter_computer-vision/semantic-segmentation-and-dataset.md b/chapter_computer-vision/semantic-segmentation-and-dataset.md index a13615820..db881158d 100644 --- a/chapter_computer-vision/semantic-segmentation-and-dataset.md +++ b/chapter_computer-vision/semantic-segmentation-and-dataset.md @@ -62,7 +62,7 @@ voc_dir = d2l.download_extract('voc2012', 'VOCdevkit/VOC2012') ```{.python .input} #@save def read_voc_images(voc_dir, is_train=True): - """读取所有VOC图像并标注。""" + """读取所有VOC图像并标注""" txt_fname = os.path.join(voc_dir, 'ImageSets', 'Segmentation', 'train.txt' if is_train else 'val.txt') with open(txt_fname, 'r') as f: @@ -82,7 +82,7 @@ train_features, train_labels = read_voc_images(voc_dir, True) #@tab pytorch #@save def read_voc_images(voc_dir, is_train=True): - """读取所有VOC图像并标注。""" + """读取所有VOC图像并标注""" txt_fname = os.path.join(voc_dir, 'ImageSets', 'Segmentation', 'train.txt' if is_train else 'val.txt') mode = torchvision.io.image.ImageReadMode.RGB @@ -141,7 +141,7 @@ VOC_CLASSES = ['background', 'aeroplane', 'bicycle', 'bird', 'boat', ```{.python .input} #@save def voc_colormap2label(): - """构建从RGB到VOC类别索引的映射。""" + """构建从RGB到VOC类别索引的映射""" colormap2label = np.zeros(256 ** 3) for i, colormap in enumerate(VOC_COLORMAP): colormap2label[ @@ -150,7 +150,7 @@ def voc_colormap2label(): #@save def voc_label_indices(colormap, colormap2label): - """将VOC标签中的RGB值映射到它们的类别索引。""" + """将VOC标签中的RGB值映射到它们的类别索引""" colormap = colormap.astype(np.int32) idx = ((colormap[:, :, 0] * 256 + colormap[:, :, 1]) * 256 + colormap[:, :, 2]) @@ -161,7 +161,7 @@ def voc_label_indices(colormap, colormap2label): #@tab pytorch #@save def voc_colormap2label(): - """构建从RGB到VOC类别索引的映射。""" + """构建从RGB到VOC类别索引的映射""" colormap2label = torch.zeros(256 ** 3, dtype=torch.long) for i, colormap in enumerate(VOC_COLORMAP): colormap2label[ @@ -170,7 +170,7 @@ def voc_colormap2label(): #@save def voc_label_indices(colormap, colormap2label): - """将VOC标签中的RGB值映射到它们的类别索引。""" + """将VOC标签中的RGB值映射到它们的类别索引""" colormap = colormap.permute(1, 2, 0).numpy().astype('int32') idx = ((colormap[:, :, 0] * 256 + colormap[:, :, 1]) * 256 + colormap[:, :, 2]) @@ -196,7 +196,7 @@ y[105:115, 130:140], VOC_CLASSES[1] ```{.python .input} #@save def voc_rand_crop(feature, label, height, width): - """随机裁剪特征和标签图像。""" + """随机裁剪特征和标签图像""" feature, rect = image.random_crop(feature, (width, height)) label = image.fixed_crop(label, *rect) return feature, label @@ -206,7 +206,7 @@ def voc_rand_crop(feature, label, height, width): #@tab pytorch #@save def voc_rand_crop(feature, label, height, width): - """随机裁剪特征和标签图像。""" + """随机裁剪特征和标签图像""" rect = torchvision.transforms.RandomCrop.get_params( feature, (height, width)) feature = torchvision.transforms.functional.crop(feature, *rect) @@ -241,7 +241,7 @@ d2l.show_images(imgs[::2] + imgs[1::2], 2, n); ```{.python .input} #@save class VOCSegDataset(gluon.data.Dataset): - """一个用于加载VOC数据集的自定义数据集。""" + """一个用于加载VOC数据集的自定义数据集""" def __init__(self, is_train, crop_size, voc_dir): self.rgb_mean = np.array([0.485, 0.456, 0.406]) self.rgb_std = np.array([0.229, 0.224, 0.225]) @@ -275,7 +275,7 @@ class VOCSegDataset(gluon.data.Dataset): #@tab pytorch #@save class VOCSegDataset(torch.utils.data.Dataset): - """一个用于加载VOC数据集的自定义数据集。""" + """一个用于加载VOC数据集的自定义数据集""" def __init__(self, is_train, crop_size, voc_dir): self.transform = torchvision.transforms.Normalize( @@ -352,7 +352,7 @@ for X, Y in train_iter: ```{.python .input} #@save def load_data_voc(batch_size, crop_size): - """加载VOC语义分割数据集。""" + """加载VOC语义分割数据集""" voc_dir = d2l.download_extract('voc2012', os.path.join( 'VOCdevkit', 'VOC2012')) num_workers = d2l.get_dataloader_workers() @@ -369,7 +369,7 @@ def load_data_voc(batch_size, crop_size): #@tab pytorch #@save def load_data_voc(batch_size, crop_size): - """加载VOC语义分割数据集。""" + """加载VOC语义分割数据集""" voc_dir = d2l.download_extract('voc2012', os.path.join( 'VOCdevkit', 'VOC2012')) num_workers = d2l.get_dataloader_workers() diff --git a/chapter_computer-vision/ssd.md b/chapter_computer-vision/ssd.md index a33169b2b..7529cd8a8 100644 --- a/chapter_computer-vision/ssd.md +++ b/chapter_computer-vision/ssd.md @@ -290,7 +290,7 @@ class TinySSD(nn.Block): super(TinySSD, self).__init__(**kwargs) self.num_classes = num_classes for i in range(5): - # 即赋值语句 `self.blk_i = get_blk(i)` + # 即赋值语句`self.blk_i=get_blk(i)` setattr(self, f'blk_{i}', get_blk(i)) setattr(self, f'cls_{i}', cls_predictor(num_anchors, num_classes)) setattr(self, f'bbox_{i}', bbox_predictor(num_anchors)) @@ -298,7 +298,7 @@ class TinySSD(nn.Block): def forward(self, X): anchors, cls_preds, bbox_preds = [None] * 5, [None] * 5, [None] * 5 for i in range(5): - # `getattr(self, 'blk_%d' % i)` 即访问 `self.blk_i` + # `getattr(self,'blk_%d'%i)`即访问`self.blk_i` X, anchors[i], cls_preds[i], bbox_preds[i] = blk_forward( X, getattr(self, f'blk_{i}'), sizes[i], ratios[i], getattr(self, f'cls_{i}'), getattr(self, f'bbox_{i}')) @@ -318,7 +318,7 @@ class TinySSD(nn.Module): self.num_classes = num_classes idx_to_in_channels = [64, 128, 128, 128, 128] for i in range(5): - # 即赋值语句 `self.blk_i = get_blk(i)` + # 即赋值语句`self.blk_i=get_blk(i)` setattr(self, f'blk_{i}', get_blk(i)) setattr(self, f'cls_{i}', cls_predictor(idx_to_in_channels[i], num_anchors, num_classes)) @@ -328,7 +328,7 @@ class TinySSD(nn.Module): def forward(self, X): anchors, cls_preds, bbox_preds = [None] * 5, [None] * 5, [None] * 5 for i in range(5): - # `getattr(self, 'blk_%d' % i)` 即访问 `self.blk_i` + # `getattr(self,'blk_%d'%i)`即访问`self.blk_i` X, anchors[i], cls_preds[i], bbox_preds[i] = blk_forward( X, getattr(self, f'blk_{i}'), sizes[i], ratios[i], getattr(self, f'cls_{i}'), getattr(self, f'bbox_{i}')) @@ -436,7 +436,7 @@ def calc_loss(cls_preds, cls_labels, bbox_preds, bbox_labels, bbox_masks): ```{.python .input} def cls_eval(cls_preds, cls_labels): - # 由于类别预测结果放在最后一维, `argmax` 需要指定最后一维。 + # 由于类别预测结果放在最后一维,`argmax`需要指定最后一维。 return float((cls_preds.argmax(axis=-1).astype( cls_labels.dtype) == cls_labels).sum()) @@ -447,7 +447,7 @@ def bbox_eval(bbox_preds, bbox_labels, bbox_masks): ```{.python .input} #@tab pytorch def cls_eval(cls_preds, cls_labels): - # 由于类别预测结果放在最后一维, `argmax` 需要指定最后一维。 + # 由于类别预测结果放在最后一维,`argmax`需要指定最后一维。 return float((cls_preds.argmax(dim=-1).type( cls_labels.dtype) == cls_labels).sum()) diff --git a/chapter_convolutional-modern/batch-norm.md b/chapter_convolutional-modern/batch-norm.md index 74a023864..d1e7a576e 100644 --- a/chapter_convolutional-modern/batch-norm.md +++ b/chapter_convolutional-modern/batch-norm.md @@ -104,7 +104,7 @@ from mxnet.gluon import nn npx.set_np() def batch_norm(X, gamma, beta, moving_mean, moving_var, eps, momentum): - # 通过 `autograd` 来判断当前模式是训练模式还是预测模式 + # 通过`autograd`来判断当前模式是训练模式还是预测模式 if not autograd.is_training(): # 如果是在预测模式下,直接使用传入的移动平均所得的均值和方差 X_hat = (X - moving_mean) / np.sqrt(moving_var + eps) @@ -135,7 +135,7 @@ import torch from torch import nn def batch_norm(X, gamma, beta, moving_mean, moving_var, eps, momentum): - # 通过 `is_grad_enabled` 来判断当前模式是训练模式还是预测模式 + # 通过`is_grad_enabled`来判断当前模式是训练模式还是预测模式 if not torch.is_grad_enabled(): # 如果是在预测模式下,直接使用传入的移动平均所得的均值和方差 X_hat = (X - moving_mean) / torch.sqrt(moving_var + eps) @@ -201,12 +201,12 @@ class BatchNorm(nn.Block): self.moving_var = np.ones(shape) def forward(self, X): - # 如果 `X` 不在内存上,将 `moving_mean` 和 `moving_var` - # 复制到 `X` 所在显存上 + # 如果`X`不在内存上,将`moving_mean`和`moving_var` + # 复制到`X`所在显存上 if self.moving_mean.ctx != X.ctx: self.moving_mean = self.moving_mean.copyto(X.ctx) self.moving_var = self.moving_var.copyto(X.ctx) - # 保存更新过的 `moving_mean` 和 `moving_var` + # 保存更新过的`moving_mean`和`moving_var` Y, self.moving_mean, self.moving_var = batch_norm( X, self.gamma.data(), self.beta.data(), self.moving_mean, self.moving_var, eps=1e-12, momentum=0.9) @@ -232,12 +232,12 @@ class BatchNorm(nn.Module): self.moving_var = torch.ones(shape) def forward(self, X): - # 如果 `X` 不在内存上,将 `moving_mean` 和 `moving_var` - # 复制到 `X` 所在显存上 + # 如果`X`不在内存上,将`moving_mean`和`moving_var` + # 复制到`X`所在显存上 if self.moving_mean.device != X.device: self.moving_mean = self.moving_mean.to(X.device) self.moving_var = self.moving_var.to(X.device) - # 保存更新过的 `moving_mean` 和 `moving_var` + # 保存更新过的`moving_mean`和`moving_var` Y, self.moving_mean, self.moving_var = batch_norm( X, self.gamma, self.beta, self.moving_mean, self.moving_var, eps=1e-5, momentum=0.9) @@ -332,7 +332,7 @@ net = nn.Sequential( ```{.python .input} #@tab tensorflow -# 回想一下,这个函数必须传递给 `d2l.train_ch6`。 +# 回想一下,这个函数必须传递给`d2l.train_ch6`。 # 或者说为了利用我们现有的CPU/GPU设备,需要在`strategy.scope()`建立模型 def net(): return tf.keras.models.Sequential([ diff --git a/chapter_convolutional-modern/googlenet.md b/chapter_convolutional-modern/googlenet.md index d2ce09179..2f3a95edb 100644 --- a/chapter_convolutional-modern/googlenet.md +++ b/chapter_convolutional-modern/googlenet.md @@ -28,20 +28,20 @@ from mxnet.gluon import nn npx.set_np() class Inception(nn.Block): - # `c1`--`c4` 是每条路径的输出通道数 + # `c1`--`c4`是每条路径的输出通道数 def __init__(self, c1, c2, c3, c4, **kwargs): super(Inception, self).__init__(**kwargs) - # 线路1,单1 x 1卷积层 + # 线路1,单1x1卷积层 self.p1_1 = nn.Conv2D(c1, kernel_size=1, activation='relu') - # 线路2,1 x 1卷积层后接3 x 3卷积层 + # 线路2,1x1卷积层后接3x3卷积层 self.p2_1 = nn.Conv2D(c2[0], kernel_size=1, activation='relu') self.p2_2 = nn.Conv2D(c2[1], kernel_size=3, padding=1, activation='relu') - # 线路3,1 x 1卷积层后接5 x 5卷积层 + # 线路3,1x1卷积层后接5x5卷积层 self.p3_1 = nn.Conv2D(c3[0], kernel_size=1, activation='relu') self.p3_2 = nn.Conv2D(c3[1], kernel_size=5, padding=2, activation='relu') - # 线路4,3 x 3最大汇聚层后接1 x 1卷积层 + # 线路4,3x3最大汇聚层后接1x1卷积层 self.p4_1 = nn.MaxPool2D(pool_size=3, strides=1, padding=1) self.p4_2 = nn.Conv2D(c4, kernel_size=1, activation='relu') @@ -62,18 +62,18 @@ from torch import nn from torch.nn import functional as F class Inception(nn.Module): - # `c1`--`c4` 是每条路径的输出通道数 + # `c1`--`c4`是每条路径的输出通道数 def __init__(self, in_channels, c1, c2, c3, c4, **kwargs): super(Inception, self).__init__(**kwargs) - # 线路1,单1 x 1卷积层 + # 线路1,单1x1卷积层 self.p1_1 = nn.Conv2d(in_channels, c1, kernel_size=1) - # 线路2,1 x 1卷积层后接3 x 3卷积层 + # 线路2,1x1卷积层后接3x3卷积层 self.p2_1 = nn.Conv2d(in_channels, c2[0], kernel_size=1) self.p2_2 = nn.Conv2d(c2[0], c2[1], kernel_size=3, padding=1) - # 线路3,1 x 1卷积层后接5 x 5卷积层 + # 线路3,1x1卷积层后接5x5卷积层 self.p3_1 = nn.Conv2d(in_channels, c3[0], kernel_size=1) self.p3_2 = nn.Conv2d(c3[0], c3[1], kernel_size=5, padding=2) - # 线路4,3 x 3最大汇聚层后接1 x 1卷积层 + # 线路4,3x3最大汇聚层后接1x1卷积层 self.p4_1 = nn.MaxPool2d(kernel_size=3, stride=1, padding=1) self.p4_2 = nn.Conv2d(in_channels, c4, kernel_size=1) @@ -92,20 +92,20 @@ from d2l import tensorflow as d2l import tensorflow as tf class Inception(tf.keras.Model): - # `c1`--`c4` 是每条路径的输出通道数 + # `c1`--`c4`是每条路径的输出通道数 def __init__(self, c1, c2, c3, c4): super().__init__() - # 线路1,单1 x 1卷积层 + # 线路1,单1x1卷积层 self.p1_1 = tf.keras.layers.Conv2D(c1, 1, activation='relu') - # 线路2,1 x 1卷积层后接3 x 3卷积层 + # 线路2,1x1卷积层后接3x3卷积层 self.p2_1 = tf.keras.layers.Conv2D(c2[0], 1, activation='relu') self.p2_2 = tf.keras.layers.Conv2D(c2[1], 3, padding='same', activation='relu') - # 线路3,1 x 1卷积层后接5 x 5卷积层 + # 线路3,1x1卷积层后接5x5卷积层 self.p3_1 = tf.keras.layers.Conv2D(c3[0], 1, activation='relu') self.p3_2 = tf.keras.layers.Conv2D(c3[1], 5, padding='same', activation='relu') - # 线路4,3 x 3最大汇聚层后接1 x 1卷积层 + # 线路4,3x3最大汇聚层后接1x1卷积层 self.p4_1 = tf.keras.layers.MaxPool2D(3, 1, padding='same') self.p4_2 = tf.keras.layers.Conv2D(c4, 1, activation='relu') diff --git a/chapter_convolutional-modern/nin.md b/chapter_convolutional-modern/nin.md index a193e3291..44a0f6eb9 100644 --- a/chapter_convolutional-modern/nin.md +++ b/chapter_convolutional-modern/nin.md @@ -89,7 +89,7 @@ net.add(nin_block(96, kernel_size=11, strides=4, padding=0), nin_block(10, kernel_size=3, strides=1, padding=1), # 全局平均汇聚层将窗口形状自动设置成输入的高和宽 nn.GlobalAvgPool2D(), - # 将四维的输出转成二维的输出,其形状为(批量大小, 10) + # 将四维的输出转成二维的输出,其形状为(批量大小,10) nn.Flatten()) ``` @@ -106,7 +106,7 @@ net = nn.Sequential( # 标签类别数是10 nin_block(384, 10, kernel_size=3, strides=1, padding=1), nn.AdaptiveAvgPool2d((1, 1)), - # 将四维的输出转成二维的输出,其形状为(批量大小, 10) + # 将四维的输出转成二维的输出,其形状为(批量大小,10) nn.Flatten()) ``` @@ -125,7 +125,7 @@ def net(): nin_block(10, kernel_size=3, strides=1, padding='same'), tf.keras.layers.GlobalAveragePooling2D(), tf.keras.layers.Reshape((1, 1, 10)), - # 将四维的输出转成二维的输出,其形状为(批量大小, 10) + # 将四维的输出转成二维的输出,其形状为(批量大小,10) tf.keras.layers.Flatten(), ]) ``` diff --git a/chapter_convolutional-modern/resnet.md b/chapter_convolutional-modern/resnet.md index 31b04375b..229ea832f 100644 --- a/chapter_convolutional-modern/resnet.md +++ b/chapter_convolutional-modern/resnet.md @@ -319,18 +319,18 @@ net = nn.Sequential(b1, b2, b3, b4, b5, ```{.python .input} #@tab tensorflow -# 回想之前我们定义一个函数,以便用它在 `tf.distribute.MirroredStrategy` 的范围, +# 回想之前我们定义一个函数,以便用它在`tf.distribute.MirroredStrategy`的范围, # 来利用各种计算资源,例如gpu。另外,尽管我们已经创建了b1、b2、b3、b4、b5, # 但是我们将在这个函数的作用域内重新创建它们 def net(): return tf.keras.Sequential([ - # The following layers are the same as b1 that we created earlier + # Thefollowinglayersarethesameasb1thatwecreatedearlier tf.keras.layers.Conv2D(64, kernel_size=7, strides=2, padding='same'), tf.keras.layers.BatchNormalization(), tf.keras.layers.Activation('relu'), tf.keras.layers.MaxPool2D(pool_size=3, strides=2, padding='same'), - # The following layers are the same as b2, b3, b4, and b5 that we - # created earlier + # Thefollowinglayersarethesameasb2,b3,b4,andb5thatwe + # createdearlier ResnetBlock(64, 2, first_block=True), ResnetBlock(128, 2), ResnetBlock(256, 2), diff --git a/chapter_convolutional-neural-networks/channels.md b/chapter_convolutional-neural-networks/channels.md index 2dd7ffcc2..0cb613531 100644 --- a/chapter_convolutional-neural-networks/channels.md +++ b/chapter_convolutional-neural-networks/channels.md @@ -36,7 +36,7 @@ import torch ```{.python .input} #@tab mxnet, pytorch def corr2d_multi_in(X, K): - # 先遍历 “X” 和 “K” 的第0个维度(通道维度),再把它们加在一起 + # 先遍历“X”和“K”的第0个维度(通道维度),再把它们加在一起 return sum(d2l.corr2d(x, k) for x, k in zip(X, K)) ``` @@ -46,7 +46,7 @@ from d2l import tensorflow as d2l import tensorflow as tf def corr2d_multi_in(X, K): - # 先遍历 “X” 和 “K” 的第0个维度(通道维度),再把它们加在一起 + # 先遍历“X”和“K”的第0个维度(通道维度),再把它们加在一起 return tf.reduce_sum([d2l.corr2d(x, k) for x, k in zip(X, K)], axis=0) ``` diff --git a/chapter_convolutional-neural-networks/conv-layer.md b/chapter_convolutional-neural-networks/conv-layer.md index eab9af6fb..4ddaac7cd 100644 --- a/chapter_convolutional-neural-networks/conv-layer.md +++ b/chapter_convolutional-neural-networks/conv-layer.md @@ -50,7 +50,7 @@ from torch import nn ```{.python .input} #@tab mxnet, pytorch def corr2d(X, K): #@save - """计算二维互相关运算。""" + """计算二维互相关运算""" h, w = K.shape Y = d2l.zeros((X.shape[0] - h + 1, X.shape[1] - w + 1)) for i in range(Y.shape[0]): @@ -65,7 +65,7 @@ from d2l import tensorflow as d2l import tensorflow as tf def corr2d(X, K): #@save - """计算二维互相关运算。""" + """计算二维互相关运算""" h, w = K.shape Y = tf.Variable(tf.zeros((X.shape[0] - h + 1, X.shape[1] - w + 1))) for i in range(Y.shape[0]): diff --git a/chapter_convolutional-neural-networks/lenet.md b/chapter_convolutional-neural-networks/lenet.md index e348a6560..4a11085cd 100644 --- a/chapter_convolutional-neural-networks/lenet.md +++ b/chapter_convolutional-neural-networks/lenet.md @@ -44,7 +44,7 @@ net.add(nn.Conv2D(channels=6, kernel_size=5, padding=2, activation='sigmoid'), nn.AvgPool2D(pool_size=2, strides=2), nn.Conv2D(channels=16, kernel_size=5, activation='sigmoid'), nn.AvgPool2D(pool_size=2, strides=2), - # 默认情况下,“Dense” 会自动将形状为(批量大小,通道数,高度,宽度)的输入, + # 默认情况下,“Dense”会自动将形状为(批量大小,通道数,高度,宽度)的输入, # 转换为形状为(批量大小,通道数*高度*宽度)的输入 nn.Dense(120, activation='sigmoid'), nn.Dense(84, activation='sigmoid'), @@ -144,7 +144,7 @@ train_iter, test_iter = d2l.load_data_fashion_mnist(batch_size=batch_size) ```{.python .input} def evaluate_accuracy_gpu(net, data_iter, device=None): #@save - """使用GPU计算模型在数据集上的精度。""" + """使用GPU计算模型在数据集上的精度""" if not device: # 查询第一个参数所在的第一个设备 device = list(net.collect_params().values())[0].list_ctx()[0] metric = d2l.Accumulator(2) # 正确预测的数量,总预测的数量 @@ -157,7 +157,7 @@ def evaluate_accuracy_gpu(net, data_iter, device=None): #@save ```{.python .input} #@tab pytorch def evaluate_accuracy_gpu(net, data_iter, device=None): #@save - """使用GPU计算模型在数据集上的精度。""" + """使用GPU计算模型在数据集上的精度""" if isinstance(net, nn.Module): net.eval() # 设置为评估模式 if not device: @@ -188,7 +188,7 @@ def evaluate_accuracy_gpu(net, data_iter, device=None): #@save ```{.python .input} #@save def train_ch6(net, train_iter, test_iter, num_epochs, lr, device): - """用GPU训练模型(在第六章定义)。""" + """用GPU训练模型(在第六章定义)""" net.initialize(force_reinit=True, ctx=device, init=init.Xavier()) loss = gluon.loss.SoftmaxCrossEntropyLoss() trainer = gluon.Trainer(net.collect_params(), @@ -226,7 +226,7 @@ def train_ch6(net, train_iter, test_iter, num_epochs, lr, device): #@tab pytorch #@save def train_ch6(net, train_iter, test_iter, num_epochs, lr, device): - """用GPU训练模型(在第六章定义)。""" + """用GPU训练模型(在第六章定义)""" def init_weights(m): if type(m) == nn.Linear or type(m) == nn.Conv2d: nn.init.xavier_uniform_(m.weight) @@ -269,7 +269,7 @@ def train_ch6(net, train_iter, test_iter, num_epochs, lr, device): ```{.python .input} #@tab tensorflow class TrainCallback(tf.keras.callbacks.Callback): #@save - """一个以可视化的训练进展的回调。""" + """一个以可视化的训练进展的回调""" def __init__(self, net, train_iter, test_iter, num_epochs, device_name): self.timer = d2l.Timer() self.animator = d2l.Animator( @@ -301,7 +301,7 @@ class TrainCallback(tf.keras.callbacks.Callback): #@save #@save def train_ch6(net_fn, train_iter, test_iter, num_epochs, lr, device): - """用GPU训练模型(在第六章定义)。""" + """用GPU训练模型(在第六章定义)""" device_name = device._device_name strategy = tf.distribute.OneDeviceStrategy(device_name) with strategy.scope(): diff --git a/chapter_deep-learning-computation/model-construction.md b/chapter_deep-learning-computation/model-construction.md index eb2dc40d9..a166d1aab 100644 --- a/chapter_deep-learning-computation/model-construction.md +++ b/chapter_deep-learning-computation/model-construction.md @@ -207,9 +207,9 @@ class MLP(tf.keras.Model): # 调用`MLP`的父类`Model`的构造函数来执行必要的初始化。 # 这样,在类实例化时也可以指定其他函数参数,例如模型参数`params`(稍后将介绍) super().__init__() - # Hidden layer + # Hiddenlayer self.hidden = tf.keras.layers.Dense(units=256, activation=tf.nn.relu) - self.out = tf.keras.layers.Dense(units=10) # Output layer + self.out = tf.keras.layers.Dense(units=10) # Outputlayer # 定义模型的前向传播,即如何根据输入`X`返回所需的模型输出 def call(self, X): @@ -276,7 +276,7 @@ net(X) class MySequential(nn.Block): def add(self, block): # 这里,`block`是`Block`子类的一个实例,我们假设它有一个唯一的名称。我们把它 - # 保存在'Block'类的成员变量`_children` 中。`block`的类型是OrderedDict。 + # 保存在'Block'类的成员变量`_children`中。`block`的类型是OrderedDict。 # 当`MySequential`实例调用`initialize`函数时,系统会自动初始化`_children` # 的所有成员 self._children[block.name] = block @@ -295,7 +295,7 @@ class MySequential(nn.Module): super().__init__() for idx, module in enumerate(args): # 这里,`module`是`Module`子类的一个实例。我们把它保存在'Module'类的成员 - # 变量`_modules` 中。`module`的类型是OrderedDict + # 变量`_modules`中。`module`的类型是OrderedDict self._modules[str(idx)] = module def forward(self, X): diff --git a/chapter_linear-networks/image-classification-dataset.md b/chapter_linear-networks/image-classification-dataset.md index 88bc8ece4..7fe20d2de 100644 --- a/chapter_linear-networks/image-classification-dataset.md +++ b/chapter_linear-networks/image-classification-dataset.md @@ -94,7 +94,7 @@ Fashion-MNIST中包含的10个类别,分别为t-shirt(T恤)、trouser( ```{.python .input} #@tab all def get_fashion_mnist_labels(labels): #@save - """返回Fashion-MNIST数据集的文本标签。""" + """返回Fashion-MNIST数据集的文本标签""" text_labels = ['t-shirt', 'trouser', 'pullover', 'dress', 'coat', 'sandal', 'shirt', 'sneaker', 'bag', 'ankle boot'] return [text_labels[int(i)] for i in labels] @@ -105,7 +105,7 @@ def get_fashion_mnist_labels(labels): #@save ```{.python .input} #@tab mxnet, tensorflow def show_images(imgs, num_rows, num_cols, titles=None, scale=1.5): #@save - """绘制图像列表。""" + """绘制图像列表""" figsize = (num_cols * scale, num_rows * scale) _, axes = d2l.plt.subplots(num_rows, num_cols, figsize=figsize) axes = axes.flatten() @@ -121,7 +121,7 @@ def show_images(imgs, num_rows, num_cols, titles=None, scale=1.5): #@save ```{.python .input} #@tab pytorch def show_images(imgs, num_rows, num_cols, titles=None, scale=1.5): #@save - """绘制图像列表。""" + """绘制图像列表""" figsize = (num_cols * scale, num_rows * scale) _, axes = d2l.plt.subplots(num_rows, num_cols, figsize=figsize) axes = axes.flatten() @@ -171,7 +171,7 @@ show_images(X, 2, 9, titles=get_fashion_mnist_labels(y)); batch_size = 256 def get_dataloader_workers(): #@save - """在非Windows的平台上,使用4个进程来读取数据。""" + """在非Windows的平台上,使用4个进程来读取数据""" return 0 if sys.platform.startswith('win') else 4 # 通过ToTensor实例将图像数据从uint8格式变换成32位浮点数格式,并除以255使得所有像素的数值 @@ -187,7 +187,7 @@ train_iter = gluon.data.DataLoader(mnist_train.transform_first(transformer), batch_size = 256 def get_dataloader_workers(): #@save - """使用4个进程来读取数据。""" + """使用4个进程来读取数据""" return 4 train_iter = data.DataLoader(mnist_train, batch_size, shuffle=True, @@ -219,7 +219,7 @@ f'{timer.stop():.2f} sec' ```{.python .input} def load_data_fashion_mnist(batch_size, resize=None): #@save - """下载Fashion-MNIST数据集,然后将其加载到内存中。""" + """下载Fashion-MNIST数据集,然后将其加载到内存中""" dataset = gluon.data.vision trans = [dataset.transforms.ToTensor()] if resize: @@ -236,7 +236,7 @@ def load_data_fashion_mnist(batch_size, resize=None): #@save ```{.python .input} #@tab pytorch def load_data_fashion_mnist(batch_size, resize=None): #@save - """下载Fashion-MNIST数据集,然后将其加载到内存中。""" + """下载Fashion-MNIST数据集,然后将其加载到内存中""" trans = [transforms.ToTensor()] if resize: trans.insert(0, transforms.Resize(resize)) @@ -254,7 +254,7 @@ def load_data_fashion_mnist(batch_size, resize=None): #@save ```{.python .input} #@tab tensorflow def load_data_fashion_mnist(batch_size, resize=None): #@save - """下载Fashion-MNIST数据集,然后将其加载到内存中。""" + """下载Fashion-MNIST数据集,然后将其加载到内存中""" mnist_train, mnist_test = tf.keras.datasets.fashion_mnist.load_data() # 将所有数字除以255,使所有像素值介于0和1之间,在最后添加一个批处理维度, # 并将标签转换为int32。 diff --git a/chapter_linear-networks/linear-regression-concise.md b/chapter_linear-networks/linear-regression-concise.md index 92b9c98ab..d163bb3fd 100644 --- a/chapter_linear-networks/linear-regression-concise.md +++ b/chapter_linear-networks/linear-regression-concise.md @@ -53,7 +53,7 @@ features, labels = d2l.synthetic_data(true_w, true_b, 1000) ```{.python .input} def load_array(data_arrays, batch_size, is_train=True): #@save - """构造一个Gluon数据迭代器。""" + """构造一个Gluon数据迭代器""" dataset = gluon.data.ArrayDataset(*data_arrays) return gluon.data.DataLoader(dataset, batch_size, shuffle=is_train) ``` @@ -61,7 +61,7 @@ def load_array(data_arrays, batch_size, is_train=True): #@save ```{.python .input} #@tab pytorch def load_array(data_arrays, batch_size, is_train=True): #@save - """构造一个PyTorch数据迭代器。""" + """构造一个PyTorch数据迭代器""" dataset = data.TensorDataset(*data_arrays) return data.DataLoader(dataset, batch_size, shuffle=is_train) ``` @@ -69,7 +69,7 @@ def load_array(data_arrays, batch_size, is_train=True): #@save ```{.python .input} #@tab tensorflow def load_array(data_arrays, batch_size, is_train=True): #@save - """构造一个TensorFlow数据迭代器。""" + """构造一个TensorFlow数据迭代器""" dataset = tf.data.Dataset.from_tensor_slices(data_arrays) if is_train: dataset = dataset.shuffle(buffer_size=1000) @@ -140,7 +140,7 @@ Keras会自动推断每个层输入的形状。 :end_tab: ```{.python .input} -# `nn` 是神经网络的缩写 +# `nn`是神经网络的缩写 from mxnet.gluon import nn net = nn.Sequential() net.add(nn.Dense(1)) @@ -148,14 +148,14 @@ net.add(nn.Dense(1)) ```{.python .input} #@tab pytorch -# `nn` 是神经网络的缩写 +# `nn`是神经网络的缩写 from torch import nn net = nn.Sequential(nn.Linear(2, 1)) ``` ```{.python .input} #@tab tensorflow -# `keras` 是TensorFlow的高级API +# `keras`是TensorFlow的高级API net = tf.keras.Sequential() net.add(tf.keras.layers.Dense(1)) ``` diff --git a/chapter_linear-networks/linear-regression-scratch.md b/chapter_linear-networks/linear-regression-scratch.md index 56b987871..0c1759b14 100644 --- a/chapter_linear-networks/linear-regression-scratch.md +++ b/chapter_linear-networks/linear-regression-scratch.md @@ -56,7 +56,7 @@ $$\mathbf{y}= \mathbf{X} \mathbf{w} + b + \mathbf\epsilon.$$ ```{.python .input} #@tab mxnet, pytorch def synthetic_data(w, b, num_examples): #@save - """生成 y = Xw + b + 噪声。""" + """生成y=Xw+b+噪声""" X = d2l.normal(0, 1, (num_examples, len(w))) y = d2l.matmul(X, w) + b y += d2l.normal(0, 0.01, y.shape) @@ -66,7 +66,7 @@ def synthetic_data(w, b, num_examples): #@save ```{.python .input} #@tab tensorflow def synthetic_data(w, b, num_examples): #@save - """生成 y = Xw + b + 噪声。""" + """生成y=Xw+b+噪声""" X = d2l.zeros((num_examples, w.shape[0])) X += tf.random.normal(shape=X.shape) y = d2l.matmul(X, tf.reshape(w, (-1, 1))) + b @@ -202,7 +202,7 @@ b = tf.Variable(tf.zeros(1), trainable=True) ```{.python .input} #@tab all def linreg(X, w, b): #@save - """线性回归模型。""" + """线性回归模型""" return d2l.matmul(X, w) + b ``` @@ -215,7 +215,7 @@ def linreg(X, w, b): #@save ```{.python .input} #@tab all def squared_loss(y_hat, y): #@save - """均方损失。""" + """均方损失""" return (y_hat - d2l.reshape(y, y_hat.shape)) ** 2 / 2 ``` @@ -235,7 +235,7 @@ def squared_loss(y_hat, y): #@save ```{.python .input} def sgd(params, lr, batch_size): #@save - """小批量随机梯度下降。""" + """小批量随机梯度下降""" for param in params: param[:] = param - lr * param.grad / batch_size ``` @@ -243,7 +243,7 @@ def sgd(params, lr, batch_size): #@save ```{.python .input} #@tab pytorch def sgd(params, lr, batch_size): #@save - """小批量随机梯度下降。""" + """小批量随机梯度下降""" with torch.no_grad(): for param in params: param -= lr * param.grad / batch_size @@ -253,7 +253,7 @@ def sgd(params, lr, batch_size): #@save ```{.python .input} #@tab tensorflow def sgd(params, grads, lr, batch_size): #@save - """小批量随机梯度下降。""" + """小批量随机梯度下降""" for param, grad in zip(params, grads): param.assign_sub(lr*grad/batch_size) ``` @@ -293,7 +293,7 @@ for epoch in range(num_epochs): for X, y in data_iter(batch_size, features, labels): with autograd.record(): l = loss(net(X, w, b), y) # `X`和`y`的小批量损失 - # 计算l关于[`w`, `b`]的梯度 + # 计算l关于[`w`,`b`]的梯度 l.backward() sgd([w, b], lr, batch_size) # 使用参数的梯度更新参数 train_l = loss(net(features, w, b), labels) @@ -305,8 +305,8 @@ for epoch in range(num_epochs): for epoch in range(num_epochs): for X, y in data_iter(batch_size, features, labels): l = loss(net(X, w, b), y) # `X`和`y`的小批量损失 - # 因为`l`形状是(`batch_size`, 1),而不是一个标量。`l`中的所有元素被加到一起, - # 并以此计算关于[`w`, `b`]的梯度 + # 因为`l`形状是(`batch_size`,1),而不是一个标量。`l`中的所有元素被加到一起, + # 并以此计算关于[`w`,`b`]的梯度 l.sum().backward() sgd([w, b], lr, batch_size) # 使用参数的梯度更新参数 with torch.no_grad(): @@ -320,7 +320,7 @@ for epoch in range(num_epochs): for X, y in data_iter(batch_size, features, labels): with tf.GradientTape() as g: l = loss(net(X, w, b), y) # `X`和`y`的小批量损失 - # 计算l关于[`w`, `b`]的梯度 + # 计算l关于[`w`,`b`]的梯度 dw, db = g.gradient(l, [w, b]) # 使用参数的梯度更新参数 sgd([w, b], [dw, db], lr, batch_size) diff --git a/chapter_linear-networks/linear-regression.md b/chapter_linear-networks/linear-regression.md index 9022f493f..f944221b3 100644 --- a/chapter_linear-networks/linear-regression.md +++ b/chapter_linear-networks/linear-regression.md @@ -263,30 +263,30 @@ b = d2l.ones(n) ```{.python .input} #@tab all class Timer: #@save - """记录多次运行时间。""" + """记录多次运行时间""" def __init__(self): self.times = [] self.start() def start(self): - """启动计时器。""" + """启动计时器""" self.tik = time.time() def stop(self): - """停止计时器并将时间记录在列表中。""" + """停止计时器并将时间记录在列表中""" self.times.append(time.time() - self.tik) return self.times[-1] def avg(self): - """返回平均时间。""" + """返回平均时间""" return sum(self.times) / len(self.times) def sum(self): - """返回时间总和。""" + """返回时间总和""" return sum(self.times) def cumsum(self): - """返回累计时间。""" + """返回累计时间""" return np.array(self.times).cumsum().tolist() ``` diff --git a/chapter_linear-networks/softmax-regression-scratch.md b/chapter_linear-networks/softmax-regression-scratch.md index c47f46f05..622458365 100644 --- a/chapter_linear-networks/softmax-regression-scratch.md +++ b/chapter_linear-networks/softmax-regression-scratch.md @@ -238,7 +238,7 @@ Gmail做分类时可能在内部估计概率,但最终它必须在类中选择 ```{.python .input} #@tab all def accuracy(y_hat, y): #@save - """计算预测正确的数量。""" + """计算预测正确的数量""" if len(y_hat.shape) > 1 and y_hat.shape[1] > 1: y_hat = d2l.argmax(y_hat, axis=1) cmp = d2l.astype(y_hat, y.dtype) == y @@ -261,7 +261,7 @@ accuracy(y_hat, y) / len(y) ```{.python .input} #@tab mxnet, tensorflow def evaluate_accuracy(net, data_iter): #@save - """计算在指定数据集上模型的精度。""" + """计算在指定数据集上模型的精度""" metric = Accumulator(2) # 正确预测数、预测总数 for X, y in data_iter: metric.add(accuracy(net(X), y), d2l.size(y)) @@ -271,7 +271,7 @@ def evaluate_accuracy(net, data_iter): #@save ```{.python .input} #@tab pytorch def evaluate_accuracy(net, data_iter): #@save - """计算在指定数据集上模型的精度。""" + """计算在指定数据集上模型的精度""" if isinstance(net, torch.nn.Module): net.eval() # 将模型设置为评估模式 metric = Accumulator(2) # 正确预测数、预测总数 @@ -290,7 +290,7 @@ def evaluate_accuracy(net, data_iter): #@save ```{.python .input} #@tab all class Accumulator: #@save - """在`n`个变量上累加。""" + """在`n`个变量上累加""" def __init__(self, n): self.data = [0.0] * n @@ -324,7 +324,7 @@ evaluate_accuracy(net, test_iter) ```{.python .input} def train_epoch_ch3(net, train_iter, loss, updater): #@save - """训练模型一个迭代周期(定义见第3章)。""" + """训练模型一个迭代周期(定义见第3章)""" # 训练损失总和、训练准确度总和、样本数 metric = Accumulator(3) if isinstance(updater, gluon.Trainer): @@ -344,7 +344,7 @@ def train_epoch_ch3(net, train_iter, loss, updater): #@save ```{.python .input} #@tab pytorch def train_epoch_ch3(net, train_iter, loss, updater): #@save - """训练模型一个迭代周期(定义见第3章)。""" + """训练模型一个迭代周期(定义见第3章)""" # 将模型设置为训练模式 if isinstance(net, torch.nn.Module): net.train() @@ -373,7 +373,7 @@ def train_epoch_ch3(net, train_iter, loss, updater): #@save ```{.python .input} #@tab tensorflow def train_epoch_ch3(net, train_iter, loss, updater): #@save - """训练模型一个迭代周期(定义见第3章)。""" + """训练模型一个迭代周期(定义见第3章)""" # 训练损失总和、训练准确度总和、样本数 metric = Accumulator(3) for X, y in train_iter: @@ -406,7 +406,7 @@ def train_epoch_ch3(net, train_iter, loss, updater): #@save ```{.python .input} #@tab all class Animator: #@save - """在动画中绘制数据。""" + """在动画中绘制数据""" def __init__(self, xlabel=None, ylabel=None, legend=None, xlim=None, ylim=None, xscale='linear', yscale='linear', fmts=('-', 'm--', 'g-.', 'r:'), nrows=1, ncols=1, @@ -455,7 +455,7 @@ class Animator: #@save ```{.python .input} #@tab all def train_ch3(net, train_iter, test_iter, loss, num_epochs, updater): #@save - """训练模型(定义见第3章)。""" + """训练模型(定义见第3章)""" animator = Animator(xlabel='epoch', xlim=[1, num_epochs], ylim=[0.3, 0.9], legend=['train loss', 'train acc', 'test acc']) for epoch in range(num_epochs): @@ -482,7 +482,7 @@ def updater(batch_size): ```{.python .input} #@tab tensorflow class Updater(): #@save - """用小批量随机梯度下降法更新参数。""" + """用小批量随机梯度下降法更新参数""" def __init__(self, params, lr): self.params = params self.lr = lr @@ -511,7 +511,7 @@ train_ch3(net, train_iter, test_iter, cross_entropy, num_epochs, updater) ```{.python .input} #@tab all def predict_ch3(net, test_iter, n=6): #@save - """预测标签(定义见第3章)。""" + """预测标签(定义见第3章)""" for X, y in test_iter: break trues = d2l.get_fashion_mnist_labels(y) diff --git a/chapter_multilayer-perceptrons/kaggle-house-price.md b/chapter_multilayer-perceptrons/kaggle-house-price.md index 86b936e2f..7e846c464 100644 --- a/chapter_multilayer-perceptrons/kaggle-house-price.md +++ b/chapter_multilayer-perceptrons/kaggle-house-price.md @@ -44,7 +44,7 @@ DATA_URL = 'http://d2l-data.s3-accelerate.amazonaws.com/' ```{.python .input} #@tab all def download(name, cache_dir=os.path.join('..', 'data')): #@save - """下载一个DATA_HUB中的文件,返回本地文件名。""" + """下载一个DATA_HUB中的文件,返回本地文件名""" assert name in DATA_HUB, f"{name} 不存在于 {DATA_HUB}" url, sha1_hash = DATA_HUB[name] os.makedirs(cache_dir, exist_ok=True) @@ -73,7 +73,7 @@ def download(name, cache_dir=os.path.join('..', 'data')): #@save ```{.python .input} #@tab all def download_extract(name, folder=None): #@save - """下载并解压zip/tar文件。""" + """下载并解压zip/tar文件""" fname = download(name) base_dir = os.path.dirname(fname) data_dir, ext = os.path.splitext(fname) @@ -87,7 +87,7 @@ def download_extract(name, folder=None): #@save return os.path.join(base_dir, folder) if folder else data_dir def download_all(): #@save - """下载DATA_HUB中的所有文件。""" + """下载DATA_HUB中的所有文件""" for name in DATA_HUB: download(name) ``` diff --git a/chapter_multilayer-perceptrons/underfit-overfit.md b/chapter_multilayer-perceptrons/underfit-overfit.md index da847a435..05e050d21 100644 --- a/chapter_multilayer-perceptrons/underfit-overfit.md +++ b/chapter_multilayer-perceptrons/underfit-overfit.md @@ -309,8 +309,8 @@ features = np.random.normal(size=(n_train + n_test, 1)) np.random.shuffle(features) poly_features = np.power(features, np.arange(max_degree).reshape(1, -1)) for i in range(max_degree): - poly_features[:, i] /= math.gamma(i + 1) # `gamma(n)` = (n-1)! -# `labels`的维度: (`n_train` + `n_test`,) + poly_features[:, i] /= math.gamma(i + 1) # `gamma(n)`=(n-1)! +# `labels`的维度:(`n_train`+`n_test`,) labels = np.dot(poly_features, true_w) labels += np.random.normal(scale=0.1, size=labels.shape) ``` @@ -322,7 +322,7 @@ labels += np.random.normal(scale=0.1, size=labels.shape) ```{.python .input} #@tab pytorch, tensorflow -# NumPy ndarray转换为tensor +# NumPyndarray转换为tensor true_w, features, poly_features, labels = [d2l.tensor(x, dtype= d2l.float32) for x in [true_w, features, poly_features, labels]] ``` @@ -339,8 +339,8 @@ features[:2], poly_features[:2, :], labels[:2] ```{.python .input} #@tab mxnet, tensorflow def evaluate_loss(net, data_iter, loss): #@save - """评估给定数据集上模型的损失。""" - metric = d2l.Accumulator(2) # 损失的总和, 样本数量 + """评估给定数据集上模型的损失""" + metric = d2l.Accumulator(2) # 损失的总和,样本数量 for X, y in data_iter: l = loss(net(X), y) metric.add(d2l.reduce_sum(l), d2l.size(l)) @@ -350,8 +350,8 @@ def evaluate_loss(net, data_iter, loss): #@save ```{.python .input} #@tab pytorch def evaluate_loss(net, data_iter, loss): #@save - """评估给定数据集上模型的损失。""" - metric = d2l.Accumulator(2) # 损失的总和, 样本数量 + """评估给定数据集上模型的损失""" + metric = d2l.Accumulator(2) # 损失的总和,样本数量 for X, y in data_iter: out = net(X) y = d2l.reshape(y, out.shape) @@ -445,7 +445,7 @@ def train(train_features, test_features, train_labels, test_labels, ```{.python .input} #@tab all -# 从多项式特征中选择前4个维度,即 1, x, x^2/2!, x^3/3! +# 从多项式特征中选择前4个维度,即1,x,x^2/2!,x^3/3! train(poly_features[:n_train, :4], poly_features[n_train:, :4], labels[:n_train], labels[n_train:]) ``` diff --git a/chapter_multilayer-perceptrons/weight-decay.md b/chapter_multilayer-perceptrons/weight-decay.md index 328b17c3a..2b5751387 100644 --- a/chapter_multilayer-perceptrons/weight-decay.md +++ b/chapter_multilayer-perceptrons/weight-decay.md @@ -397,7 +397,7 @@ def train_concise(wd): for epoch in range(num_epochs): for X, y in train_iter: with tf.GradientTape() as tape: - # `tf.keras` 需要为自定义训练代码手动添加损失。 + # `tf.keras`需要为自定义训练代码手动添加损失。 l = loss(net(X), y) + net.losses grads = tape.gradient(l, net.trainable_variables) trainer.apply_gradients(zip(grads, net.trainable_variables)) diff --git a/chapter_natural-language-processing-applications/natural-language-inference-attention.md b/chapter_natural-language-processing-applications/natural-language-inference-attention.md index 6764c9528..21a5a3a9a 100644 --- a/chapter_natural-language-processing-applications/natural-language-inference-attention.md +++ b/chapter_natural-language-processing-applications/natural-language-inference-attention.md @@ -92,8 +92,8 @@ class Attend(nn.Block): self.f = mlp(num_hiddens=num_hiddens, flatten=False) def forward(self, A, B): - # `A`/`B`的形状:(批量大小,序列 A/B的词元数,`embed_size`) - # `f_A`/`f_B`的形状:(`批量大小`,序列 A/B的词元数,`num_hiddens`) + # `A`/`B`的形状:(批量大小,序列A/B的词元数,`embed_size`) + # `f_A`/`f_B`的形状:(`批量大小`,序列A/B的词元数,`num_hiddens`) f_A = self.f(A) f_B = self.f(B) # `e`的形状:(批量大小,序列A的词元数,序列B的词元数) @@ -115,8 +115,8 @@ class Attend(nn.Module): self.f = mlp(num_inputs, num_hiddens, flatten=False) def forward(self, A, B): - # `A`/`B`的形状:(批量大小,序列 A/B的词元数,`embed_size`) - # `f_A`/`f_B`的形状:(`批量大小`,序列 A/B的词元数,`num_hiddens`) + # `A`/`B`的形状:(批量大小,序列A/B的词元数,`embed_size`) + # `f_A`/`f_B`的形状:(`批量大小`,序列A/B的词元数,`num_hiddens`) f_A = self.f(A) f_B = self.f(B) # `e`的形状:(批量大小,序列A的词元数,序列B的词元数) diff --git a/chapter_natural-language-processing-applications/natural-language-inference-bert.md b/chapter_natural-language-processing-applications/natural-language-inference-bert.md index a127e6552..d125319d2 100644 --- a/chapter_natural-language-processing-applications/natural-language-inference-bert.md +++ b/chapter_natural-language-processing-applications/natural-language-inference-bert.md @@ -142,7 +142,7 @@ class SNLIBERTDataset(gluon.data.Dataset): return token_ids, segments, valid_len def _truncate_pair_of_tokens(self, p_tokens, h_tokens): - # 为BERT输入中的 ''、''和 ''词元保留位置 + # 为BERT输入中的''、''和''词元保留位置 while len(p_tokens) + len(h_tokens) > self.max_len - 3: if len(p_tokens) > len(h_tokens): p_tokens.pop() @@ -195,7 +195,7 @@ class SNLIBERTDataset(torch.utils.data.Dataset): return token_ids, segments, valid_len def _truncate_pair_of_tokens(self, p_tokens, h_tokens): - # 为BERT输入中的 ''、''和 ''词元保留位置 + # 为BERT输入中的''、''和''词元保留位置 while len(p_tokens) + len(h_tokens) > self.max_len - 3: if len(p_tokens) > len(h_tokens): p_tokens.pop() @@ -213,7 +213,7 @@ class SNLIBERTDataset(torch.utils.data.Dataset): 下载完SNLI数据集后,我们通过实例化`SNLIBERTDataset`类来[**生成训练和测试样本**]。这些样本将在自然语言推断的训练和测试期间进行小批量读取。 ```{.python .input} -# 如果出现显存不足错误,请减少“batch_size”。在原始的BERT模型中,`max_len` = 512 +# 如果出现显存不足错误,请减少“batch_size”。在原始的BERT模型中,`max_len`=512 batch_size, max_len, num_workers = 512, 128, d2l.get_dataloader_workers() data_dir = d2l.download_extract('SNLI') train_set = SNLIBERTDataset(d2l.read_snli(data_dir, True), max_len, vocab) @@ -226,7 +226,7 @@ test_iter = gluon.data.DataLoader(test_set, batch_size, ```{.python .input} #@tab pytorch -# 如果出现显存不足错误,请减少“batch_size”。在原始的BERT模型中,`max_len` = 512 +# 如果出现显存不足错误,请减少“batch_size”。在原始的BERT模型中,`max_len`=512 batch_size, max_len, num_workers = 512, 128, d2l.get_dataloader_workers() data_dir = d2l.download_extract('SNLI') train_set = SNLIBERTDataset(d2l.read_snli(data_dir, True), max_len, vocab) diff --git a/chapter_natural-language-processing-pretraining/bert-dataset.md b/chapter_natural-language-processing-pretraining/bert-dataset.md index 4eea9f38f..eb21efdce 100644 --- a/chapter_natural-language-processing-pretraining/bert-dataset.md +++ b/chapter_natural-language-processing-pretraining/bert-dataset.md @@ -158,7 +158,7 @@ def _pad_bert_inputs(examples, max_len, vocab): max_len - len(token_ids)), dtype='int32')) all_segments.append(np.array(segments + [0] * ( max_len - len(segments)), dtype='int32')) - # `valid_lens` 不包括''的计数 + # `valid_lens`不包括''的计数 valid_lens.append(np.array(len(token_ids), dtype='float32')) all_pred_positions.append(np.array(pred_positions + [0] * ( max_num_mlm_preds - len(pred_positions)), dtype='int32')) @@ -187,7 +187,7 @@ def _pad_bert_inputs(examples, max_len, vocab): max_len - len(token_ids)), dtype=torch.long)) all_segments.append(torch.tensor(segments + [0] * ( max_len - len(segments)), dtype=torch.long)) - # `valid_lens` 不包括''的计数 + # `valid_lens`不包括''的计数 valid_lens.append(torch.tensor(len(token_ids), dtype=torch.float32)) all_pred_positions.append(torch.tensor(pred_positions + [0] * ( max_num_mlm_preds - len(pred_positions)), dtype=torch.long)) diff --git a/chapter_natural-language-processing-pretraining/bert-pretraining.md b/chapter_natural-language-processing-pretraining/bert-pretraining.md index b29c99bd5..0e47c6c2a 100644 --- a/chapter_natural-language-processing-pretraining/bert-pretraining.md +++ b/chapter_natural-language-processing-pretraining/bert-pretraining.md @@ -228,7 +228,7 @@ def get_bert_encoding(net, tokens_a, tokens_b=None): #@tab all tokens_a = ['a', 'crane', 'is', 'flying'] encoded_text = get_bert_encoding(net, tokens_a) -# 词元: '', 'a', 'crane', 'is', 'flying', '' +# 词元:'','a','crane','is','flying','' encoded_text_cls = encoded_text[:, 0, :] encoded_text_crane = encoded_text[:, 2, :] encoded_text.shape, encoded_text_cls.shape, encoded_text_crane[0][:3] @@ -240,8 +240,8 @@ encoded_text.shape, encoded_text_cls.shape, encoded_text_crane[0][:3] #@tab all tokens_a, tokens_b = ['a', 'crane', 'driver', 'came'], ['he', 'just', 'left'] encoded_pair = get_bert_encoding(net, tokens_a, tokens_b) -# 词元: '', 'a', 'crane', 'driver', 'came', '', 'he', 'just', -# 'left', '' +# 词元:'','a','crane','driver','came','','he','just', +# 'left','' encoded_pair_cls = encoded_pair[:, 0, :] encoded_pair_crane = encoded_pair[:, 2, :] encoded_pair.shape, encoded_pair_cls.shape, encoded_pair_crane[0][:3] diff --git a/chapter_natural-language-processing-pretraining/bert.md b/chapter_natural-language-processing-pretraining/bert.md index 0d7db4ad3..a73324748 100644 --- a/chapter_natural-language-processing-pretraining/bert.md +++ b/chapter_natural-language-processing-pretraining/bert.md @@ -288,7 +288,7 @@ class NextSentencePred(nn.Block): self.output = nn.Dense(2) def forward(self, X): - # X的形状: (batch size,`num_hiddens`) + # X的形状:(batchsize,`num_hiddens`) return self.output(X) ``` @@ -302,7 +302,7 @@ class NextSentencePred(nn.Module): self.output = nn.Linear(num_inputs, 2) def forward(self, X): - # `X`的形状: (batch size, `num_hiddens`) + # `X`的形状:(batchsize,`num_hiddens`) return self.output(X) ``` @@ -318,7 +318,7 @@ nsp_Y_hat.shape ```{.python .input} #@tab pytorch encoded_X = torch.flatten(encoded_X, start_dim=1) -# NSP的输入形状: (batch size,`num_hiddens`) +# NSP的输入形状:(batchsize,`num_hiddens`) nsp = NextSentencePred(encoded_X.shape[-1]) nsp_Y_hat = nsp(encoded_X) nsp_Y_hat.shape diff --git a/chapter_natural-language-processing-pretraining/subword-embedding.md b/chapter_natural-language-processing-pretraining/subword-embedding.md index d294653de..0a8a96ff2 100644 --- a/chapter_natural-language-processing-pretraining/subword-embedding.md +++ b/chapter_natural-language-processing-pretraining/subword-embedding.md @@ -83,7 +83,7 @@ num_merges = 10 for i in range(num_merges): max_freq_pair = get_max_freq_pair(token_freqs) token_freqs = merge_symbols(max_freq_pair, token_freqs, symbols) - print(f'合并#{i + 1}:', max_freq_pair) + print(f'合并# {i+1}:',max_freq_pair) ``` 在字节对编码的10次迭代之后,我们可以看到列表`symbols`现在又包含10个从其他符号迭代合并而来的符号。 diff --git a/chapter_natural-language-processing-pretraining/word-embedding-dataset.md b/chapter_natural-language-processing-pretraining/word-embedding-dataset.md index 1b3beb2c8..655622707 100644 --- a/chapter_natural-language-processing-pretraining/word-embedding-dataset.md +++ b/chapter_natural-language-processing-pretraining/word-embedding-dataset.md @@ -34,7 +34,7 @@ d2l.DATA_HUB['ptb'] = (d2l.DATA_URL + 'ptb.zip', def read_ptb(): """将PTB数据集加载到文本行的列表中""" data_dir = d2l.download_extract('ptb') - # Read the training set. + # Readthetrainingset. with open(os.path.join(data_dir, 'ptb.train.txt')) as f: raw_text = f.read() return [line.split() for line in raw_text.split('\n')] @@ -125,7 +125,7 @@ corpus[:3] #@tab all #@save def get_centers_and_contexts(corpus, max_window_size): - """返回跳元模型中的中心词和上下文词。""" + """返回跳元模型中的中心词和上下文词""" centers, contexts = [], [] for line in corpus: # 要形成“中心词-上下文词”对,每个句子至少需要有2个词 @@ -168,9 +168,9 @@ f'# “中心词-上下文词对”的数量: {sum([len(contexts) for contexts i #@tab all #@save class RandomGenerator: - """根据n个采样权重在{1, ..., n}中随机抽取""" + """根据n个采样权重在{1,...,n}中随机抽取""" def __init__(self, sampling_weights): - # Exclude + # Exclude self.population = list(range(1, len(sampling_weights) + 1)) self.sampling_weights = sampling_weights self.candidates = [] @@ -289,7 +289,7 @@ def load_data_ptb(batch_size, max_window_size, num_noise_words): #@tab pytorch #@save def load_data_ptb(batch_size, max_window_size, num_noise_words): - """下载PTB数据集,然后将其加载到内存中。""" + """下载PTB数据集,然后将其加载到内存中""" num_workers = d2l.get_dataloader_workers() sentences = read_ptb() vocab = d2l.Vocab(sentences, min_freq=10) diff --git a/chapter_optimization/adadelta.md b/chapter_optimization/adadelta.md index ee3aed00c..854296886 100644 --- a/chapter_optimization/adadelta.md +++ b/chapter_optimization/adadelta.md @@ -55,7 +55,7 @@ def init_adadelta_states(feature_dim): def adadelta(params, states, hyperparams): rho, eps = hyperparams['rho'], 1e-5 for p, (s, delta) in zip(params, states): - # In-place updates via [:] + # In-placeupdatesvia[:] s[:] = rho * s + (1 - rho) * np.square(p.grad) g = (np.sqrt(delta + eps) / np.sqrt(s + eps)) * p.grad p[:] -= g @@ -77,7 +77,7 @@ def adadelta(params, states, hyperparams): rho, eps = hyperparams['rho'], 1e-5 for p, (s, delta) in zip(params, states): with torch.no_grad(): - # In-place updates via [:] + # In-placeupdatesvia[:] s[:] = rho * s + (1 - rho) * torch.square(p.grad) g = (torch.sqrt(delta + eps) / torch.sqrt(s + eps)) * p.grad p[:] -= g @@ -130,8 +130,8 @@ d2l.train_concise_ch11(trainer, {'rho': 0.9}, data_iter) ```{.python .input} #@tab tensorflow -# adadelta is not converging at default learning rate -# but it's converging at lr = 5.0 +# adadeltaisnotconvergingatdefaultlearningrate +# butit'sconvergingatlr=5.0 trainer = tf.keras.optimizers.Adadelta d2l.train_concise_ch11(trainer, {'learning_rate':5.0, 'rho': 0.9}, data_iter) ``` diff --git a/chapter_optimization/gd.md b/chapter_optimization/gd.md index d7cc68181..8ac60ceaf 100644 --- a/chapter_optimization/gd.md +++ b/chapter_optimization/gd.md @@ -73,7 +73,7 @@ import tensorflow as tf def f(x): # 目标函数 return x ** 2 -def f_grad(x): # 目标函数的梯度 (导数) +def f_grad(x): # 目标函数的梯度(导数) return 2 * x ``` @@ -187,8 +187,8 @@ $$\mathbf{x} \leftarrow \mathbf{x} - \eta \nabla f(\mathbf{x}).$$ ```{.python .input} #@tab all def train_2d(trainer, steps=20, f_grad=None): #@save - """用定制的训练机优化2D目标函数。""" - # `s1` 和 `s2` 是稍后将使用的内部状态变量 + """用定制的训练机优化2D目标函数""" + # `s1`和`s2`是稍后将使用的内部状态变量 x1, x2, s1, s2 = -5, -2, 0, 0 results = [(x1, x2)] for i in range(steps): @@ -201,7 +201,7 @@ def train_2d(trainer, steps=20, f_grad=None): #@save return results def show_trace_2d(f, results): #@save - """显示优化过程中2D变量的轨迹。""" + """显示优化过程中2D变量的轨迹""" d2l.set_figsize() d2l.plt.plot(*zip(*results), '-o', color='#ff7f0e') x1, x2 = d2l.meshgrid(d2l.arange(-5.5, 1.0, 0.1), diff --git a/chapter_optimization/lr-scheduler.md b/chapter_optimization/lr-scheduler.md index 996d7e1be..b8d728e16 100644 --- a/chapter_optimization/lr-scheduler.md +++ b/chapter_optimization/lr-scheduler.md @@ -41,13 +41,13 @@ device = d2l.try_gpu() batch_size = 256 train_iter, test_iter = d2l.load_data_fashion_mnist(batch_size=batch_size) -# 代码几乎与 `d2l.train_ch6` 定义在卷积神经网络一章LeNet一节中的相同 +# 代码几乎与`d2l.train_ch6`定义在卷积神经网络一章LeNet一节中的相同 def train(net, train_iter, test_iter, num_epochs, loss, trainer, device): net.initialize(force_reinit=True, ctx=device, init=init.Xavier()) animator = d2l.Animator(xlabel='epoch', xlim=[0, num_epochs], legend=['train loss', 'train acc', 'test acc']) for epoch in range(num_epochs): - metric = d2l.Accumulator(3) # train_loss, train_acc, num_examples + metric = d2l.Accumulator(3) # train_loss,train_acc,num_examples for i, (X, y) in enumerate(train_iter): X, y = X.as_in_ctx(device), y.as_in_ctx(device) with autograd.record(): @@ -95,7 +95,7 @@ device = d2l.try_gpu() batch_size = 256 train_iter, test_iter = d2l.load_data_fashion_mnist(batch_size=batch_size) -# 代码几乎与 `d2l.train_ch6` 定义在卷积神经网络一章LeNet一节中的相同 +# 代码几乎与`d2l.train_ch6`定义在卷积神经网络一章LeNet一节中的相同 def train(net, train_iter, test_iter, num_epochs, loss, trainer, device, scheduler=None): net.to(device) @@ -103,7 +103,7 @@ def train(net, train_iter, test_iter, num_epochs, loss, trainer, device, legend=['train loss', 'train acc', 'test acc']) for epoch in range(num_epochs): - metric = d2l.Accumulator(3) # train_loss, train_acc, num_examples + metric = d2l.Accumulator(3) # train_loss,train_acc,num_examples for i, (X, y) in enumerate(train_iter): net.train() trainer.zero_grad() @@ -125,10 +125,10 @@ def train(net, train_iter, test_iter, num_epochs, loss, trainer, device, if scheduler: if scheduler.__module__ == lr_scheduler.__name__: - # Using PyTorch In-Built scheduler + # UsingPyTorchIn-Builtscheduler scheduler.step() else: - # Using custom defined scheduler + # Usingcustomdefinedscheduler for param_group in trainer.param_groups: param_group['lr'] = scheduler(epoch) @@ -161,7 +161,7 @@ def net(): batch_size = 256 train_iter, test_iter = d2l.load_data_fashion_mnist(batch_size=batch_size) -# 代码几乎与 `d2l.train_ch6` 定义在卷积神经网络一章LeNet一节中的相同 +# 代码几乎与`d2l.train_ch6`定义在卷积神经网络一章LeNet一节中的相同 def train(net_fn, train_iter, test_iter, num_epochs, lr, device=d2l.try_gpu(), custom_callback = False): device_name = device._device_name diff --git a/chapter_optimization/minibatch-sgd.md b/chapter_optimization/minibatch-sgd.md index d88782c64..ce037d3d4 100644 --- a/chapter_optimization/minibatch-sgd.md +++ b/chapter_optimization/minibatch-sgd.md @@ -95,7 +95,7 @@ C = tf.Variable(d2l.normal([256, 256], 0, 1)) 按元素分配只需遍历分别为$\mathbf{B}$和$\mathbf{C}$的所有行和列,即可将该值分配给$\mathbf{A}$。 ```{.python .input} -# Compute A = BC one element at a time +# ComputeA=BConeelementatatime timer.start() for i in range(256): for j in range(256): @@ -106,7 +106,7 @@ timer.stop() ```{.python .input} #@tab pytorch -# Compute A = BC one element at a time +# ComputeA=BConeelementatatime timer.start() for i in range(256): for j in range(256): @@ -116,7 +116,7 @@ timer.stop() ```{.python .input} #@tab tensorflow -# Compute A = BC one element at a time +# ComputeA=BConeelementatatime timer.start() for i in range(256): for j in range(256): @@ -127,7 +127,7 @@ timer.stop() 更快的策略是执行按列分配。 ```{.python .input} -# Compute A = BC one column at a time +# ComputeA=BConecolumnatatime timer.start() for j in range(256): A[:, j] = np.dot(B, C[:, j]) @@ -137,7 +137,7 @@ timer.stop() ```{.python .input} #@tab pytorch -# Compute A = BC one column at a time +# ComputeA=BConecolumnatatime timer.start() for j in range(256): A[:, j] = torch.mv(B, C[:, j]) @@ -532,7 +532,7 @@ def train_concise_ch11(trainer_fn, hyperparams, data_iter, num_epochs=4): optimizer = trainer_fn(net.parameters(), **hyperparams) loss = nn.MSELoss() - # L2 Loss = 1/2 * MSE Loss + # L2Loss=1/2*MSELoss animator = d2l.Animator(xlabel='epoch', ylabel='loss', xlim=[0, num_epochs], ylim=[0.22, 0.35]) n, timer = 0, d2l.Timer() @@ -563,7 +563,7 @@ def train_concise_ch11(trainer_fn, hyperparams, data_iter, num_epochs=2): kernel_initializer=tf.random_normal_initializer(stddev=0.01))) optimizer = trainer_fn(**hyperparams) loss = tf.keras.losses.MeanSquaredError() - # 注意: L2 Loss = 1/2 * MSE Loss。 + # 注意:L2Loss=1/2*MSELoss。 # TensorFlow的MSE损失与MXNet的L2损失大概相差2倍。 # 因此,我们将TensorFlow中的损失减半 animator = d2l.Animator(xlabel='epoch', ylabel='loss', diff --git a/chapter_optimization/sgd.md b/chapter_optimization/sgd.md index ce0a60a62..8bfeca673 100644 --- a/chapter_optimization/sgd.md +++ b/chapter_optimization/sgd.md @@ -64,7 +64,7 @@ def f_grad(x1, x2): # 目标函数的梯度 #@tab mxnet, pytorch def sgd(x1, x2, s1, s2, f_grad): g1, g2 = f_grad(x1, x2) - # Simulate noisy gradient + # Simulatenoisygradient g1 += d2l.normal(0.0, 1, (1,)) g2 += d2l.normal(0.0, 1, (1,)) eta_t = eta * lr() @@ -75,7 +75,7 @@ def sgd(x1, x2, s1, s2, f_grad): #@tab tensorflow def sgd(x1, x2, s1, s2, f_grad): g1, g2 = f_grad(x1, x2) - # Simulate noisy gradient + # Simulatenoisygradient g1 += d2l.normal([1], 0.0, 1) g2 += d2l.normal([1], 0.0, 1) eta_t = eta * lr() @@ -88,7 +88,7 @@ def constant_lr(): return 1 eta = 0.1 -lr = constant_lr # Constant learning rate +lr = constant_lr # Constantlearningrate d2l.show_trace_2d(f, d2l.train_2d(sgd, steps=50, f_grad=f_grad)) ``` @@ -115,7 +115,7 @@ $$ ```{.python .input} #@tab all def exponential_lr(): - # Global variable that is defined outside this function and updated inside + # Globalvariablethatisdefinedoutsidethisfunctionandupdatedinside global t t += 1 return math.exp(-0.1 * t) @@ -130,7 +130,7 @@ d2l.show_trace_2d(f, d2l.train_2d(sgd, steps=1000, f_grad=f_grad)) ```{.python .input} #@tab all def polynomial_lr(): - # Global variable that is defined outside this function and updated inside + # Globalvariablethatisdefinedoutsidethisfunctionandupdatedinside global t t += 1 return (1 + 0.1 * t) ** (-0.5) diff --git a/chapter_preliminaries/autograd.md b/chapter_preliminaries/autograd.md index 35e464436..76d66b470 100644 --- a/chapter_preliminaries/autograd.md +++ b/chapter_preliminaries/autograd.md @@ -54,7 +54,7 @@ x.grad ```{.python .input} #@tab pytorch -x.requires_grad_(True) # 等价于 `x = torch.arange(4.0, requires_grad=True)` +x.requires_grad_(True) # 等价于`x=torch.arange(4.0,requires_grad=True)` x.grad # 默认值是None ``` @@ -163,7 +163,7 @@ t.gradient(y, x) # 被新计算的梯度覆盖 with autograd.record(): y = x * x # `y`是一个向量 y.backward() -x.grad # 等价于y = sum(x * x) +x.grad # 等价于y=sum(x*x) ``` ```{.python .input} @@ -181,7 +181,7 @@ x.grad #@tab tensorflow with tf.GradientTape() as t: y = x * x -t.gradient(y, x) # 等价于 `y = tf.reduce_sum(x * x)` +t.gradient(y, x) # 等价于`y=tf.reduce_sum(x*x)` ``` ## 分离计算 @@ -219,7 +219,7 @@ x.grad == u ```{.python .input} #@tab tensorflow -# 设置 `persistent=True` 来运行 `t.gradient`多次 +# 设置`persistent=True`来运行`t.gradient`多次 with tf.GradientTape(persistent=True) as t: y = x * x u = tf.stop_gradient(y) diff --git a/chapter_preliminaries/calculus.md b/chapter_preliminaries/calculus.md index bd357fcbc..7a0dad2dd 100644 --- a/chapter_preliminaries/calculus.md +++ b/chapter_preliminaries/calculus.md @@ -144,7 +144,7 @@ $$\frac{d}{dx} \left[\frac{f(x)}{g(x)}\right] = \frac{g(x) \frac{d}{dx} [f(x)] - ```{.python .input} #@tab all def use_svg_display(): #@save - """使用svg格式在Jupyter中显示绘图。""" + """使用svg格式在Jupyter中显示绘图""" display.set_matplotlib_formats('svg') ``` @@ -155,7 +155,7 @@ def use_svg_display(): #@save ```{.python .input} #@tab all def set_figsize(figsize=(3.5, 2.5)): #@save - """设置matplotlib的图表大小。""" + """设置matplotlib的图表大小""" use_svg_display() d2l.plt.rcParams['figure.figsize'] = figsize ``` @@ -166,7 +166,7 @@ def set_figsize(figsize=(3.5, 2.5)): #@save #@tab all #@save def set_axes(axes, xlabel, ylabel, xlim, ylim, xscale, yscale, legend): - """设置matplotlib的轴。""" + """设置matplotlib的轴""" axes.set_xlabel(xlabel) axes.set_ylabel(ylabel) axes.set_xscale(xscale) @@ -187,14 +187,14 @@ def set_axes(axes, xlabel, ylabel, xlim, ylim, xscale, yscale, legend): def plot(X, Y=None, xlabel=None, ylabel=None, legend=None, xlim=None, ylim=None, xscale='linear', yscale='linear', fmts=('-', 'm--', 'g-.', 'r:'), figsize=(3.5, 2.5), axes=None): - """绘制数据点。""" + """绘制数据点""" if legend is None: legend = [] set_figsize(figsize) axes = axes if axes else d2l.plt.gca() - # 如果 `X` 有一个轴,输出True + # 如果`X`有一个轴,输出True def has_one_axis(X): return (hasattr(X, "ndim") and X.ndim == 1 or isinstance(X, list) and not hasattr(X[0], "__len__")) diff --git a/chapter_preliminaries/linear-algebra.md b/chapter_preliminaries/linear-algebra.md index 2cb0de8a1..45850f7f6 100644 --- a/chapter_preliminaries/linear-algebra.md +++ b/chapter_preliminaries/linear-algebra.md @@ -476,17 +476,17 @@ A_sum_axis1, A_sum_axis1.shape 沿着行和列对矩阵求和,等价于对矩阵的所有元素进行求和。 ```{.python .input} -A.sum(axis=[0, 1]) # Same as `A.sum()` +A.sum(axis=[0, 1]) # Sameas`A.sum()` ``` ```{.python .input} #@tab pytorch -A.sum(axis=[0, 1]) # Same as `A.sum()` +A.sum(axis=[0, 1]) # Sameas`A.sum()` ``` ```{.python .input} #@tab tensorflow -tf.reduce_sum(A, axis=[0, 1]) # Same as `tf.reduce_sum(A)` +tf.reduce_sum(A, axis=[0, 1]) # Sameas`tf.reduce_sum(A)` ``` [**一个与求和相关的量是*平均值*(mean或average)**]。 diff --git a/chapter_recurrent-modern/machine-translation-and-dataset.md b/chapter_recurrent-modern/machine-translation-and-dataset.md index 8c8430522..4c6b0646e 100644 --- a/chapter_recurrent-modern/machine-translation-and-dataset.md +++ b/chapter_recurrent-modern/machine-translation-and-dataset.md @@ -88,7 +88,7 @@ print(raw_text[:75]) #@tab all #@save def preprocess_nmt(text): - """预处理“英语-法语”数据集。""" + """预处理“英语-法语”数据集""" def no_space(char, prev_char): return char in set(',.!?') and prev_char != ' ' diff --git a/chapter_recurrent-modern/seq2seq.md b/chapter_recurrent-modern/seq2seq.md index c614fb270..de3b12efa 100644 --- a/chapter_recurrent-modern/seq2seq.md +++ b/chapter_recurrent-modern/seq2seq.md @@ -127,14 +127,14 @@ class Seq2SeqEncoder(d2l.Encoder): self.rnn = rnn.GRU(num_hiddens, num_layers, dropout=dropout) def forward(self, X, *args): - # 输出'X'的形状:(`batch_size`, `num_steps`, `embed_size`) + # 输出'X'的形状:(`batch_size`,`num_steps`,`embed_size`) X = self.embedding(X) # 在循环神经网络模型中,第一个轴对应于时间步 X = X.swapaxes(0, 1) state = self.rnn.begin_state(batch_size=X.shape[1], ctx=X.ctx) output, state = self.rnn(X, state) - # `output`的形状: (`num_steps`, `batch_size`, `num_hiddens`) - # `state[0]`的形状: (`num_layers`, `batch_size`, `num_hiddens`) + # `output`的形状:(`num_steps`,`batch_size`,`num_hiddens`) + # `state[0]`的形状:(`num_layers`,`batch_size`,`num_hiddens`) return output, state ``` @@ -152,14 +152,14 @@ class Seq2SeqEncoder(d2l.Encoder): dropout=dropout) def forward(self, X, *args): - # 输出'X'的形状:(`batch_size`, `num_steps`, `embed_size`) + # 输出'X'的形状:(`batch_size`,`num_steps`,`embed_size`) X = self.embedding(X) # 在循环神经网络模型中,第一个轴对应于时间步 X = X.permute(1, 0, 2) # 如果未提及状态,则默认为0 output, state = self.rnn(X) - # `output`的形状: (`num_steps`, `batch_size`, `num_hiddens`) - # `state[0]`的形状: (`num_layers`, `batch_size`, `num_hiddens`) + # `output`的形状:(`num_steps`,`batch_size`,`num_hiddens`) + # `state[0]`的形状:(`num_layers`,`batch_size`,`num_hiddens`) return output, state ``` @@ -178,8 +178,8 @@ class Seq2SeqEncoder(d2l.Encoder): return_state=True) def call(self, X, *args, **kwargs): - # 输入'X'的形状:(`batch_size`, `num_steps`) - # 输出'X'的形状:(`batch_size`, `num_steps`, `embed_size`) + # 输入'X'的形状:(`batch_size`,`num_steps`) + # 输出'X'的形状:(`batch_size`,`num_steps`,`embed_size`) X = self.embedding(X) output = self.rnn(X, **kwargs) state = output[1:] @@ -292,9 +292,9 @@ class Seq2SeqDecoder(d2l.Decoder): return enc_outputs[1] def forward(self, X, state): - # 输出'X'的形状:(`batch_size`, `num_steps`, `embed_size`) + # 输出'X'的形状:(`batch_size`,`num_steps`,`embed_size`) X = self.embedding(X).swapaxes(0, 1) - # `context` 的形状: (`batch_size`, `num_hiddens`) + # `context`的形状:(`batch_size`,`num_hiddens`) context = state[0][-1] # 广播`context`,使其具有与`X`相同的`num_steps` context = np.broadcast_to(context, ( @@ -302,8 +302,8 @@ class Seq2SeqDecoder(d2l.Decoder): X_and_context = d2l.concat((X, context), 2) output, state = self.rnn(X_and_context, state) output = self.dense(output).swapaxes(0, 1) - # `output`的形状: (`batch_size`, `num_steps`, `vocab_size`) - # `state[0]`的形状: (`num_layers`, `batch_size`, `num_hiddens`) + # `output`的形状:(`batch_size`,`num_steps`,`vocab_size`) + # `state[0]`的形状:(`num_layers`,`batch_size`,`num_hiddens`) return output, state ``` @@ -323,15 +323,15 @@ class Seq2SeqDecoder(d2l.Decoder): return enc_outputs[1] def forward(self, X, state): - # 输出'X'的形状:(`batch_size`, `num_steps`, `embed_size`) + # 输出'X'的形状:(`batch_size`,`num_steps`,`embed_size`) X = self.embedding(X).permute(1, 0, 2) # 广播`context`,使其具有与`X`相同的`num_steps` context = state[-1].repeat(X.shape[0], 1, 1) X_and_context = d2l.concat((X, context), 2) output, state = self.rnn(X_and_context, state) output = self.dense(output).permute(1, 0, 2) - # `output`的形状: (`batch_size`, `num_steps`, `vocab_size`) - # `state[0]`的形状: (`num_layers`, `batch_size`, `num_hiddens`) + # `output`的形状:(`batch_size`,`num_steps`,`vocab_size`) + # `state[0]`的形状:(`num_layers`,`batch_size`,`num_hiddens`) return output, state ``` @@ -353,15 +353,15 @@ class Seq2SeqDecoder(d2l.Decoder): return enc_outputs[1] def call(self, X, state, **kwargs): - # 输出'X'的形状:(`batch_size`, `num_steps`, `embed_size`) + # 输出'X'的形状:(`batch_size`,`num_steps`,`embed_size`) X = self.embedding(X) # 广播`context`,使其具有与`X`相同的`num_steps` context = tf.repeat(tf.expand_dims(state[-1], axis=1), repeats=X.shape[1], axis=1) X_and_context = tf.concat((X, context), axis=2) rnn_output = self.rnn(X_and_context, state, **kwargs) output = self.dense(rnn_output[0]) - # `output`的形状: (`batch_size`, `num_steps`, `vocab_size`) - # `state`是一个包含`num_layers`个元素的列表,每个元素的形状: (`batch_size`, `num_hiddens`) + # `output`的形状:(`batch_size`,`num_steps`,`vocab_size`) + # `state`是一个包含`num_layers`个元素的列表,每个元素的形状:(`batch_size`,`num_hiddens`) return output, rnn_output[1:] ``` @@ -484,11 +484,11 @@ sequence_mask(X, tf.constant([1, 2]), value=-1) #@save class MaskedSoftmaxCELoss(gluon.loss.SoftmaxCELoss): """带遮蔽的softmax交叉熵损失函数""" - # `pred` 的形状:(`batch_size`, `num_steps`, `vocab_size`) - # `label` 的形状:(`batch_size`, `num_steps`) - # `valid_len` 的形状:(`batch_size`,) + # `pred`的形状:(`batch_size`,`num_steps`,`vocab_size`) + # `label`的形状:(`batch_size`,`num_steps`) + # `valid_len`的形状:(`batch_size`,) def forward(self, pred, label, valid_len): - # `weights` 的形状:(`batch_size`, `num_steps`, 1) + # `weights`的形状:(`batch_size`,`num_steps`,1) weights = np.expand_dims(np.ones_like(label), axis=-1) weights = npx.sequence_mask(weights, valid_len, True, axis=1) return super(MaskedSoftmaxCELoss, self).forward(pred, label, weights) @@ -499,9 +499,9 @@ class MaskedSoftmaxCELoss(gluon.loss.SoftmaxCELoss): #@save class MaskedSoftmaxCELoss(nn.CrossEntropyLoss): """带遮蔽的softmax交叉熵损失函数""" - # `pred` 的形状:(`batch_size`, `num_steps`, `vocab_size`) - # `label` 的形状:(`batch_size`, `num_steps`) - # `valid_len` 的形状:(`batch_size`,) + # `pred`的形状:(`batch_size`,`num_steps`,`vocab_size`) + # `label`的形状:(`batch_size`,`num_steps`) + # `valid_len`的形状:(`batch_size`,) def forward(self, pred, label, valid_len): weights = torch.ones_like(label) weights = sequence_mask(weights, valid_len) @@ -521,9 +521,9 @@ class MaskedSoftmaxCELoss(tf.keras.losses.Loss): super().__init__(reduction='none') self.valid_len = valid_len - # `pred` 的形状:(`batch_size`, `num_steps`, `vocab_size`) - # `label` 的形状:(`batch_size`, `num_steps`) - # `valid_len` 的形状:(`batch_size`,) + # `pred`的形状:(`batch_size`,`num_steps`,`vocab_size`) + # `label`的形状:(`batch_size`,`num_steps`) + # `valid_len`的形状:(`batch_size`,) def call(self, label, pred): weights = tf.ones_like(label, dtype=tf.float32) weights = sequence_mask(weights, self.valid_len) diff --git a/chapter_recurrent-neural-networks/language-models-and-dataset.md b/chapter_recurrent-neural-networks/language-models-and-dataset.md index 61391e448..96eb4a430 100644 --- a/chapter_recurrent-neural-networks/language-models-and-dataset.md +++ b/chapter_recurrent-neural-networks/language-models-and-dataset.md @@ -278,7 +278,7 @@ d2l.plot([freqs, bigram_freqs, trigram_freqs], xlabel='token: x', #@tab all def seq_data_iter_random(corpus, batch_size, num_steps): #@save """使用随机抽样生成一个小批量子序列""" - # 从随机偏移量开始对序列进行分区,随机范围包括`num_steps - 1` + # 从随机偏移量开始对序列进行分区,随机范围包括`num_steps-1` corpus = corpus[random.randint(0, num_steps - 1):] # 减去1,是因为我们需要考虑标签 num_subseqs = (len(corpus) - 1) // num_steps diff --git a/chapter_recurrent-neural-networks/rnn-concise.md b/chapter_recurrent-neural-networks/rnn-concise.md index 1747ee219..99257c8ee 100644 --- a/chapter_recurrent-neural-networks/rnn-concise.md +++ b/chapter_recurrent-neural-networks/rnn-concise.md @@ -130,7 +130,7 @@ Y.shape, len(state_new), state_new[0].shape ```{.python .input} #@save class RNNModel(nn.Block): - """循环神经网络模型。""" + """循环神经网络模型""" def __init__(self, rnn_layer, vocab_size, **kwargs): super(RNNModel, self).__init__(**kwargs) self.rnn = rnn_layer @@ -140,8 +140,8 @@ class RNNModel(nn.Block): def forward(self, inputs, state): X = npx.one_hot(inputs.T, self.vocab_size) Y, state = self.rnn(X, state) - # 全连接层首先将`Y`的形状改为(`时间步数`*`批量大小`, `隐藏单元数`) - # 它的输出形状是 (`时间步数`*`批量大小`, `词表大小`) + # 全连接层首先将`Y`的形状改为(`时间步数`*`批量大小`,`隐藏单元数`) + # 它的输出形状是(`时间步数`*`批量大小`,`词表大小`) output = self.dense(Y.reshape(-1, Y.shape[-1])) return output, state @@ -153,7 +153,7 @@ class RNNModel(nn.Block): #@tab pytorch #@save class RNNModel(nn.Module): - """循环神经网络模型。""" + """循环神经网络模型""" def __init__(self, rnn_layer, vocab_size, **kwargs): super(RNNModel, self).__init__(**kwargs) self.rnn = rnn_layer @@ -171,19 +171,19 @@ class RNNModel(nn.Module): X = F.one_hot(inputs.T.long(), self.vocab_size) X = X.to(torch.float32) Y, state = self.rnn(X, state) - # 全连接层首先将`Y`的形状改为(`时间步数`*`批量大小`, `隐藏单元数`) - # 它的输出形状是 (`时间步数`*`批量大小`, `词表大小`)。 + # 全连接层首先将`Y`的形状改为(`时间步数`*`批量大小`,`隐藏单元数`) + # 它的输出形状是(`时间步数`*`批量大小`,`词表大小`)。 output = self.linear(Y.reshape((-1, Y.shape[-1]))) return output, state def begin_state(self, device, batch_size=1): if not isinstance(self.rnn, nn.LSTM): - # `nn.GRU` 以张量作为隐状态 + # `nn.GRU`以张量作为隐状态 return torch.zeros((self.num_directions * self.rnn.num_layers, batch_size, self.num_hiddens), device=device) else: - # `nn.LSTM` 以元组作为隐状态 + # `nn.LSTM`以元组作为隐状态 return (torch.zeros(( self.num_directions * self.rnn.num_layers, batch_size, self.num_hiddens), device=device), diff --git a/chapter_recurrent-neural-networks/rnn-scratch.md b/chapter_recurrent-neural-networks/rnn-scratch.md index d723f6b10..555399ac8 100644 --- a/chapter_recurrent-neural-networks/rnn-scratch.md +++ b/chapter_recurrent-neural-networks/rnn-scratch.md @@ -563,7 +563,7 @@ def grad_clipping(grads, theta): #@save def train_epoch_ch8(net, train_iter, loss, updater, device, use_random_iter): """训练模型一个迭代周期(定义见第8章)""" state, timer = None, d2l.Timer() - metric = d2l.Accumulator(2) # 训练损失之和, 词元数量 + metric = d2l.Accumulator(2) # 训练损失之和,词元数量 for X, Y in train_iter: if state is None or use_random_iter: # 在第一次迭代或使用随机抽样时初始化`state` @@ -589,7 +589,7 @@ def train_epoch_ch8(net, train_iter, loss, updater, device, use_random_iter): def train_epoch_ch8(net, train_iter, loss, updater, device, use_random_iter): """训练网络一个迭代周期(定义见第8章)""" state, timer = None, d2l.Timer() - metric = d2l.Accumulator(2) # 训练损失之和, 词元数量 + metric = d2l.Accumulator(2) # 训练损失之和,词元数量 for X, Y in train_iter: if state is None or use_random_iter: # 在第一次迭代或使用随机抽样时初始化`state` @@ -626,7 +626,7 @@ def train_epoch_ch8(net, train_iter, loss, updater, device, use_random_iter): def train_epoch_ch8(net, train_iter, loss, updater, use_random_iter): """训练模型一个迭代周期(定义见第8章)""" state, timer = None, d2l.Timer() - metric = d2l.Accumulator(2) # 训练损失之和, 词元数量 + metric = d2l.Accumulator(2) # 训练损失之和,词元数量 for X, Y in train_iter: if state is None or use_random_iter: # 在第一次迭代或使用随机抽样时初始化`state` diff --git a/chapter_recurrent-neural-networks/text-preprocessing.md b/chapter_recurrent-neural-networks/text-preprocessing.md index 0668b46ce..d5732205a 100644 --- a/chapter_recurrent-neural-networks/text-preprocessing.md +++ b/chapter_recurrent-neural-networks/text-preprocessing.md @@ -145,7 +145,7 @@ class Vocab: #@save def count_corpus(tokens): #@save """统计词元的频率""" - # 这里的 `tokens` 是 1D 列表或 2D 列表 + # 这里的`tokens`是1D列表或2D列表 if len(tokens) == 0 or isinstance(tokens[0], list): # 将词元列表展平成一个列表 tokens = [token for line in tokens for token in line] From 790ed2262656b3e6c5b1db7dc66548ab53ca4125 Mon Sep 17 00:00:00 2001 From: Aston Zhang Date: Tue, 7 Dec 2021 20:30:43 +0000 Subject: [PATCH 07/21] fix bert --- .../bert-dataset.md | 2 +- .../bert-pretraining.md | 4 ++-- d2l/mxnet.py | 2 +- d2l/torch.py | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/chapter_natural-language-processing-pretraining/bert-dataset.md b/chapter_natural-language-processing-pretraining/bert-dataset.md index eb21efdce..d9655c1ac 100644 --- a/chapter_natural-language-processing-pretraining/bert-dataset.md +++ b/chapter_natural-language-processing-pretraining/bert-dataset.md @@ -110,7 +110,7 @@ def _replace_mlm_tokens(tokens, candidate_pred_positions, num_mlm_preds, masked_token = tokens[mlm_pred_position] # 10%的时间:用随机词替换该词 else: - masked_token = random.randint(0, len(vocab) - 1) + masked_token = random.choice(vocab.idx_to_token) mlm_input_tokens[mlm_pred_position] = masked_token pred_positions_and_labels.append( (mlm_pred_position, tokens[mlm_pred_position])) diff --git a/chapter_natural-language-processing-pretraining/bert-pretraining.md b/chapter_natural-language-processing-pretraining/bert-pretraining.md index 0e47c6c2a..94e190c11 100644 --- a/chapter_natural-language-processing-pretraining/bert-pretraining.md +++ b/chapter_natural-language-processing-pretraining/bert-pretraining.md @@ -108,7 +108,7 @@ def _get_batch_loss_bert(net, loss, vocab_size, tokens_X, ```{.python .input} def train_bert(train_iter, net, loss, vocab_size, devices, num_steps): trainer = gluon.Trainer(net.collect_params(), 'adam', - {'learning_rate': 1e-3}) + {'learning_rate': 0.01}) step, timer = 0, d2l.Timer() animator = d2l.Animator(xlabel='step', ylabel='loss', xlim=[1, num_steps], legend=['mlm', 'nsp']) @@ -151,7 +151,7 @@ def train_bert(train_iter, net, loss, vocab_size, devices, num_steps): #@tab pytorch def train_bert(train_iter, net, loss, vocab_size, devices, num_steps): net = nn.DataParallel(net, device_ids=devices).to(devices[0]) - trainer = torch.optim.Adam(net.parameters(), lr=1e-3) + trainer = torch.optim.Adam(net.parameters(), lr=0.01) step, timer = 0, d2l.Timer() animator = d2l.Animator(xlabel='step', ylabel='loss', xlim=[1, num_steps], legend=['mlm', 'nsp']) diff --git a/d2l/mxnet.py b/d2l/mxnet.py index acac29956..fee984ad0 100644 --- a/d2l/mxnet.py +++ b/d2l/mxnet.py @@ -1729,7 +1729,7 @@ def _replace_mlm_tokens(tokens, candidate_pred_positions, num_mlm_preds, masked_token = tokens[mlm_pred_position] # 10%的时间:用随机词替换该词 else: - masked_token = random.randint(0, len(vocab) - 1) + masked_token = random.choice(vocab.idx_to_token) mlm_input_tokens[mlm_pred_position] = masked_token pred_positions_and_labels.append( (mlm_pred_position, tokens[mlm_pred_position])) diff --git a/d2l/torch.py b/d2l/torch.py index d4f8bae27..44542a22d 100644 --- a/d2l/torch.py +++ b/d2l/torch.py @@ -1828,7 +1828,7 @@ def _replace_mlm_tokens(tokens, candidate_pred_positions, num_mlm_preds, masked_token = tokens[mlm_pred_position] # 10%的时间:用随机词替换该词 else: - masked_token = random.randint(0, len(vocab) - 1) + masked_token = random.choice(vocab.idx_to_token) mlm_input_tokens[mlm_pred_position] = masked_token pred_positions_and_labels.append( (mlm_pred_position, tokens[mlm_pred_position])) From af23355977c8f85e1de095ac839a495b02deb91a Mon Sep 17 00:00:00 2001 From: Anirudh Dagar Date: Tue, 7 Dec 2021 23:48:26 +0100 Subject: [PATCH 08/21] [PyTorch] Fix semantic segmentation normalization (#1031) --- chapter_computer-vision/semantic-segmentation-and-dataset.md | 2 +- d2l/torch.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/chapter_computer-vision/semantic-segmentation-and-dataset.md b/chapter_computer-vision/semantic-segmentation-and-dataset.md index db881158d..6e4cade8f 100644 --- a/chapter_computer-vision/semantic-segmentation-and-dataset.md +++ b/chapter_computer-vision/semantic-segmentation-and-dataset.md @@ -289,7 +289,7 @@ class VOCSegDataset(torch.utils.data.Dataset): print('read ' + str(len(self.features)) + ' examples') def normalize_image(self, img): - return self.transform(img.float()) + return self.transform(img.float()/255) def filter(self, imgs): return [img for img in imgs if ( diff --git a/d2l/torch.py b/d2l/torch.py index 44542a22d..b21cef18f 100644 --- a/d2l/torch.py +++ b/d2l/torch.py @@ -1395,7 +1395,7 @@ def __init__(self, is_train, crop_size, voc_dir): print('read ' + str(len(self.features)) + ' examples') def normalize_image(self, img): - return self.transform(img.float()) + return self.transform(img.float()/255) def filter(self, imgs): return [img for img in imgs if ( From d6b0f9e091bd1d2840bfb3e15ac5075825f4b8ba Mon Sep 17 00:00:00 2001 From: Aston Zhang <22279212+astonzhang@users.noreply.github.com> Date: Tue, 7 Dec 2021 14:49:37 -0800 Subject: [PATCH 09/21] Update semantic-segmentation-and-dataset.md --- chapter_computer-vision/semantic-segmentation-and-dataset.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/chapter_computer-vision/semantic-segmentation-and-dataset.md b/chapter_computer-vision/semantic-segmentation-and-dataset.md index 6e4cade8f..d1af2fc3e 100644 --- a/chapter_computer-vision/semantic-segmentation-and-dataset.md +++ b/chapter_computer-vision/semantic-segmentation-and-dataset.md @@ -289,7 +289,7 @@ class VOCSegDataset(torch.utils.data.Dataset): print('read ' + str(len(self.features)) + ' examples') def normalize_image(self, img): - return self.transform(img.float()/255) + return self.transform(img.float() / 255) def filter(self, imgs): return [img for img in imgs if ( From 531697b2772b3331a4922424ac4aa25d1ad37d19 Mon Sep 17 00:00:00 2001 From: Anirudh Dagar Date: Wed, 8 Dec 2021 01:54:53 +0100 Subject: [PATCH 10/21] BUG: Fix BLEU calculation (#1032) --- chapter_recurrent-modern/seq2seq.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/chapter_recurrent-modern/seq2seq.md b/chapter_recurrent-modern/seq2seq.md index de3b12efa..e7e943549 100644 --- a/chapter_recurrent-modern/seq2seq.md +++ b/chapter_recurrent-modern/seq2seq.md @@ -855,11 +855,11 @@ def bleu(pred_seq, label_seq, k): #@save for n in range(1, k + 1): num_matches, label_subs = 0, collections.defaultdict(int) for i in range(len_label - n + 1): - label_subs[''.join(label_tokens[i: i + n])] += 1 + label_subs[' '.join(label_tokens[i: i + n])] += 1 for i in range(len_pred - n + 1): - if label_subs[''.join(pred_tokens[i: i + n])] > 0: + if label_subs[' '.join(pred_tokens[i: i + n])] > 0: num_matches += 1 - label_subs[''.join(pred_tokens[i: i + n])] -= 1 + label_subs[' '.join(pred_tokens[i: i + n])] -= 1 score *= math.pow(num_matches / (len_pred - n + 1), math.pow(0.5, n)) return score ``` From 9773ad84aab686076c3a94afb4bde0f4db9f967a Mon Sep 17 00:00:00 2001 From: goldmermaid Date: Tue, 7 Dec 2021 19:29:33 -0800 Subject: [PATCH 11/21] Align exercise with the english version (#1036) * align exercise with the english version * fix --- .../linear-regression-concise.md | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) diff --git a/chapter_linear-networks/linear-regression-concise.md b/chapter_linear-networks/linear-regression-concise.md index d163bb3fd..a3738646d 100644 --- a/chapter_linear-networks/linear-regression-concise.md +++ b/chapter_linear-networks/linear-regression-concise.md @@ -406,24 +406,19 @@ print('b的估计误差:', true_b - b) ## 练习 -:begin_tab:`mxnet` -1. 如果我们用`l = loss(output, y).mean()`替换`l = loss(output, y)`。为了使代码的行为相同,需要将`trainer.step(batch_size)`更改为`trainer.step(1)`,这是为什么? -1. 查看MXNet文档,了解模块`gluon.loss`和`init`中提供了哪些损失函数和初始化方法。用Huber损失代替原损失。 -1. 你如何访问`dense.weight`的梯度? +1. 如果将小批量的总损失替换为小批量损失的平均值,你需要如何更改学习率? +1. 查看深度学习框架文档,它们提供了哪些损失函数和初始化方法?用Huber损失代替原损失,即 + $$l(y,y') = \begin{cases}|y-y'| -\frac{\sigma}{2} & \text{ if } |y-y'| > \sigma \\ \frac{1}{2 \sigma} (y-y')^2 & \text{ 其它情况}\end{cases}$$ +1. 你如何访问线性回归的梯度? +:begin_tab:`mxnet` [Discussions](https://discuss.d2l.ai/t/1782) :end_tab: :begin_tab:`pytorch` -1. 如果我们用`nn.MSELoss(reduction='sum')`替换`nn.MSELoss()`,为了使代码的行为相同,需要怎么更改学习速率?为什么? -1. 查看PyTorch文档,了解其提供了哪些损失函数和初始化方法。用Huber损失代替原损失。 -1. 你如何访问`net[0].weight`的梯度? - [Discussions](https://discuss.d2l.ai/t/1781) :end_tab: :begin_tab:`tensorflow` -1. 查看TensorFlow文档,了解其提供了哪些损失函数和初始化方法。用Huber损失代替原损失。 - [Discussions](https://discuss.d2l.ai/t/1780) :end_tab: From bd27efee3cff3abf731c7108a6f0b2a1a90ce669 Mon Sep 17 00:00:00 2001 From: xiaotinghe Date: Wed, 8 Dec 2021 14:25:12 +0800 Subject: [PATCH 12/21] forum link (#1038) Co-authored-by: Ubuntu --- chapter_appendix-tools-for-deep-learning/aws.md | 2 +- chapter_appendix-tools-for-deep-learning/contributing.md | 2 +- chapter_appendix-tools-for-deep-learning/jupyter.md | 2 +- chapter_appendix-tools-for-deep-learning/sagemaker.md | 2 +- chapter_attention-mechanisms/attention-cues.md | 6 +++--- chapter_attention-mechanisms/attention-scoring-functions.md | 4 ++-- chapter_attention-mechanisms/bahdanau-attention.md | 4 ++-- chapter_attention-mechanisms/multihead-attention.md | 4 ++-- chapter_attention-mechanisms/nadaraya-waston.md | 4 ++-- .../self-attention-and-positional-encoding.md | 4 ++-- chapter_attention-mechanisms/transformer.md | 4 ++-- chapter_computational-performance/hardware.md | 2 +- chapter_computational-performance/parameterserver.md | 2 +- chapter_convolutional-neural-networks/why-conv.md | 2 +- chapter_deep-learning-computation/deferred-init.md | 4 ++-- chapter_introduction/index.md | 2 +- chapter_multilayer-perceptrons/backprop.md | 2 +- .../finetuning-bert.md | 2 +- .../natural-language-inference-and-dataset.md | 4 ++-- .../natural-language-inference-attention.md | 4 ++-- .../natural-language-inference-bert.md | 4 ++-- .../sentiment-analysis-and-dataset.md | 4 ++-- .../sentiment-analysis-cnn.md | 4 ++-- .../sentiment-analysis-rnn.md | 4 ++-- .../approx-training.md | 2 +- .../bert-dataset.md | 4 ++-- .../bert-pretraining.md | 4 ++-- chapter_natural-language-processing-pretraining/bert.md | 4 ++-- chapter_natural-language-processing-pretraining/glove.md | 2 +- .../similarity-analogy.md | 4 ++-- .../subword-embedding.md | 4 ++-- .../word-embedding-dataset.md | 4 ++-- .../word2vec-pretraining.md | 4 ++-- chapter_natural-language-processing-pretraining/word2vec.md | 2 +- chapter_optimization/adadelta.md | 6 +++--- chapter_recurrent-modern/beam-search.md | 2 +- chapter_recurrent-modern/gru.md | 2 +- chapter_recurrent-neural-networks/rnn-concise.md | 2 +- 38 files changed, 62 insertions(+), 62 deletions(-) diff --git a/chapter_appendix-tools-for-deep-learning/aws.md b/chapter_appendix-tools-for-deep-learning/aws.md index f3ab60655..98c719e3a 100644 --- a/chapter_appendix-tools-for-deep-learning/aws.md +++ b/chapter_appendix-tools-for-deep-learning/aws.md @@ -202,4 +202,4 @@ jupyter notebook 1. 尝试使用不同的GPU服务器。它们有多快? 1. 尝试使用多GPU服务器。你能把事情扩大到什么程度? -[Discussions](https://discuss.d2l.ai/t/423) +[Discussions](https://discuss.d2l.ai/t/5733) diff --git a/chapter_appendix-tools-for-deep-learning/contributing.md b/chapter_appendix-tools-for-deep-learning/contributing.md index 400ebb663..311166b48 100644 --- a/chapter_appendix-tools-for-deep-learning/contributing.md +++ b/chapter_appendix-tools-for-deep-learning/contributing.md @@ -157,4 +157,4 @@ git push 1. 如果发现任何需要改进的地方(例如,缺少引用),请提交Pull请求。 1. 通常更好的做法是使用新分支创建Pull请求。学习如何用[Git分支](https://git-scm.com/book/en/v2/Git-Branching-Branches-in-a-Nutshell)来做这件事。 -[Discussions](https://discuss.d2l.ai/t/426) +[Discussions](https://discuss.d2l.ai/t/5730) diff --git a/chapter_appendix-tools-for-deep-learning/jupyter.md b/chapter_appendix-tools-for-deep-learning/jupyter.md index 90e66c205..b89d30c81 100644 --- a/chapter_appendix-tools-for-deep-learning/jupyter.md +++ b/chapter_appendix-tools-for-deep-learning/jupyter.md @@ -109,4 +109,4 @@ jupyter nbextension enable execute_time/ExecuteTime 1. 使用Jupyter Notebook通过端口转发来远程编辑和运行本书中的代码。 1. 对于两个方矩阵,测量$\mathbf{A}^\top \mathbf{B}$与$\mathbf{A} \mathbf{B}$在$\mathbb{R}^{1024 \times 1024}$中的运行时间。哪一个更快? -[Discussions](https://discuss.d2l.ai/t/421) +[Discussions](https://discuss.d2l.ai/t/5731) diff --git a/chapter_appendix-tools-for-deep-learning/sagemaker.md b/chapter_appendix-tools-for-deep-learning/sagemaker.md index 311dab593..f7ff770c4 100644 --- a/chapter_appendix-tools-for-deep-learning/sagemaker.md +++ b/chapter_appendix-tools-for-deep-learning/sagemaker.md @@ -112,4 +112,4 @@ git pull 1. 使用Amazon SageMaker编辑并运行任何需要GPU的部分。 1. 打开终端以访问保存本书所有notebooks的本地目录。 -[Discussions](https://discuss.d2l.ai/t/422) +[Discussions](https://discuss.d2l.ai/t/5732) diff --git a/chapter_attention-mechanisms/attention-cues.md b/chapter_attention-mechanisms/attention-cues.md index 81f848ac0..ed29c2890 100644 --- a/chapter_attention-mechanisms/attention-cues.md +++ b/chapter_attention-mechanisms/attention-cues.md @@ -169,15 +169,15 @@ show_heatmaps(attention_weights, xlabel='Keys', ylabel='Queries') 1. 随机生成一个$10 \times 10$矩阵并使用`softmax`运算来确保每行都是有效的概率分布,然后可视化输出注意力权重。 :begin_tab:`mxnet` -[Discussions](https://discuss.d2l.ai/t/1596) +[Discussions](https://discuss.d2l.ai/t/5763) :end_tab: :begin_tab:`pytorch` -[Discussions](https://discuss.d2l.ai/t/1592) +[Discussions](https://discuss.d2l.ai/t/5764) :end_tab: :begin_tab:`tensorflow` -[Discussions](https://discuss.d2l.ai/t/1710) +[Discussions](https://discuss.d2l.ai/t/5765) :end_tab: diff --git a/chapter_attention-mechanisms/attention-scoring-functions.md b/chapter_attention-mechanisms/attention-scoring-functions.md index 477eac760..c86ba2b65 100644 --- a/chapter_attention-mechanisms/attention-scoring-functions.md +++ b/chapter_attention-mechanisms/attention-scoring-functions.md @@ -470,9 +470,9 @@ d2l.show_heatmaps(d2l.reshape(attention.attention_weights, (1, 1, 2, 10)), 1. 当查询和键具有相同的矢量长度时,矢量求和作为评分函数是否比“点-积”更好?为什么? :begin_tab:`mxnet` -[Discussions](https://discuss.d2l.ai/t/346) +[Discussions](https://discuss.d2l.ai/t/5751) :end_tab: :begin_tab:`pytorch` -[Discussions](https://discuss.d2l.ai/t/1064) +[Discussions](https://discuss.d2l.ai/t/5752) :end_tab: diff --git a/chapter_attention-mechanisms/bahdanau-attention.md b/chapter_attention-mechanisms/bahdanau-attention.md index 735a17f26..f7ef3469d 100644 --- a/chapter_attention-mechanisms/bahdanau-attention.md +++ b/chapter_attention-mechanisms/bahdanau-attention.md @@ -378,9 +378,9 @@ d2l.show_heatmaps(attention_weights[:, :, :, :len(engs[-1].split()) + 1], 1. 修改实验以将加性注意力打分函数替换为缩放点积注意力,它如何影响训练效率? :begin_tab:`mxnet` -[Discussions](https://discuss.d2l.ai/t/347) +[Discussions](https://discuss.d2l.ai/t/5753) :end_tab: :begin_tab:`pytorch` -[Discussions](https://discuss.d2l.ai/t/1065) +[Discussions](https://discuss.d2l.ai/t/5754) :end_tab: diff --git a/chapter_attention-mechanisms/multihead-attention.md b/chapter_attention-mechanisms/multihead-attention.md index 6101ac18d..2d72077c5 100644 --- a/chapter_attention-mechanisms/multihead-attention.md +++ b/chapter_attention-mechanisms/multihead-attention.md @@ -343,9 +343,9 @@ attention(X, Y, Y, valid_lens, training=False).shape 1. 假设我们有一个完成训练的基于多头注意力的模型,现在希望修剪最不重要的注意力头以提高预测速度。如何设计实验来衡量注意力头的重要性呢? :begin_tab:`mxnet` -[Discussions](https://discuss.d2l.ai/t/1634) +[Discussions](https://discuss.d2l.ai/t/5757) :end_tab: :begin_tab:`pytorch` -[Discussions](https://discuss.d2l.ai/t/1635) +[Discussions](https://discuss.d2l.ai/t/5758) :end_tab: diff --git a/chapter_attention-mechanisms/nadaraya-waston.md b/chapter_attention-mechanisms/nadaraya-waston.md index b47456394..c041696f9 100644 --- a/chapter_attention-mechanisms/nadaraya-waston.md +++ b/chapter_attention-mechanisms/nadaraya-waston.md @@ -561,9 +561,9 @@ d2l.show_heatmaps(tf.expand_dims( 1. 为本节的核回归设计一个新的带参数的注意力汇聚模型。训练这个新模型并可视化其注意力权重。 :begin_tab:`mxnet` -[Discussions](https://discuss.d2l.ai/t/1598) +[Discussions](https://discuss.d2l.ai/t/5759) :end_tab: :begin_tab:`pytorch` -[Discussions](https://discuss.d2l.ai/t/1599) +[Discussions](https://discuss.d2l.ai/t/5760) :end_tab: diff --git a/chapter_attention-mechanisms/self-attention-and-positional-encoding.md b/chapter_attention-mechanisms/self-attention-and-positional-encoding.md index 92e26663a..960c7d242 100644 --- a/chapter_attention-mechanisms/self-attention-and-positional-encoding.md +++ b/chapter_attention-mechanisms/self-attention-and-positional-encoding.md @@ -313,9 +313,9 @@ $2\times 2$投影矩阵不依赖于任何位置的索引$i$。 1. 你能设计一种可学习的位置编码方法吗? :begin_tab:`mxnet` -[Discussions](https://discuss.d2l.ai/t/1651) +[Discussions](https://discuss.d2l.ai/t/5761) :end_tab: :begin_tab:`pytorch` -[Discussions](https://discuss.d2l.ai/t/1652) +[Discussions](https://discuss.d2l.ai/t/5762) :end_tab: diff --git a/chapter_attention-mechanisms/transformer.md b/chapter_attention-mechanisms/transformer.md index 5b9aef990..ecfbb2104 100644 --- a/chapter_attention-mechanisms/transformer.md +++ b/chapter_attention-mechanisms/transformer.md @@ -905,9 +905,9 @@ d2l.show_heatmaps( 1. 如果不使用卷积神经网络,如何设计基于transformer模型的图像分类任务?提示:可以参考Vision Transformer :cite:`Dosovitskiy.Beyer.Kolesnikov.ea.2021`。 :begin_tab:`mxnet` -[Discussions](https://discuss.d2l.ai/t/348) +[Discussions](https://discuss.d2l.ai/t/5755) :end_tab: :begin_tab:`pytorch` -[Discussions](https://discuss.d2l.ai/t/1066) +[Discussions](https://discuss.d2l.ai/t/5756) :end_tab: diff --git a/chapter_computational-performance/hardware.md b/chapter_computational-performance/hardware.md index db776b8e5..c8edb8ca8 100644 --- a/chapter_computational-performance/hardware.md +++ b/chapter_computational-performance/hardware.md @@ -216,4 +216,4 @@ GPU内存的带宽要求甚至更高,因为它们的处理单元比CPU多得 1. 看看Turing T4GPU的性能数字。为什么从FP16到INT8和INT4的性能只翻倍? 1. 一个网络包从旧金山到阿姆斯特丹的往返旅行需要多长时间?提示:你可以假设距离为10000公里。 -[Discussions](https://discuss.d2l.ai/t/2798) +[Discussions](https://discuss.d2l.ai/t/5717) diff --git a/chapter_computational-performance/parameterserver.md b/chapter_computational-performance/parameterserver.md index 35af09e71..2ed1286f0 100644 --- a/chapter_computational-performance/parameterserver.md +++ b/chapter_computational-performance/parameterserver.md @@ -100,4 +100,4 @@ $$\mathbf{g}_{i} = \sum_{k \in \text{workers}} \sum_{j \in \text{GPUs}} \mathbf{ 1. 在计算仍在进行中,可否允许执行异步通信?它将如何影响性能? 1. 怎样处理在长时间运行的计算过程中丢失了一台服务器这种问题?尝试设计一种容错机制来避免重启计算这种解决方案? -[Discussions](https://discuss.d2l.ai/t/2807) +[Discussions](https://discuss.d2l.ai/t/5774) diff --git a/chapter_convolutional-neural-networks/why-conv.md b/chapter_convolutional-neural-networks/why-conv.md index 69f38bc97..8ef7ebf96 100644 --- a/chapter_convolutional-neural-networks/why-conv.md +++ b/chapter_convolutional-neural-networks/why-conv.md @@ -148,4 +148,4 @@ $$[\mathsf{H}]_{i,j,d} = \sum_{a = -\Delta}^{\Delta} \sum_{b = -\Delta}^{\Delta} 1. 卷积层也适合于文本数据吗?为什么? 1. 证明在 :eqref:`eq_2d-conv-discrete`中,$f * g = g * f$。 -[Discussions](https://discuss.d2l.ai/t/1846) +[Discussions](https://discuss.d2l.ai/t/5767) diff --git a/chapter_deep-learning-computation/deferred-init.md b/chapter_deep-learning-computation/deferred-init.md index 6f82b40b6..88a946b54 100644 --- a/chapter_deep-learning-computation/deferred-init.md +++ b/chapter_deep-learning-computation/deferred-init.md @@ -117,11 +117,11 @@ net(X) 1. 如果输入具有不同的维度,你需要做什么?提示:查看参数绑定的相关内容。 :begin_tab:`mxnet` -[Discussions](https://discuss.d2l.ai/t/1834) +[Discussions](https://discuss.d2l.ai/t/5770) :end_tab: :begin_tab:`pytorch` -[Discussions](https://discuss.d2l.ai/t/1834) +[Discussions](https://discuss.d2l.ai/t/5770) :end_tab: :begin_tab:`tensorflow` diff --git a/chapter_introduction/index.md b/chapter_introduction/index.md index ebc33e289..93dadad39 100644 --- a/chapter_introduction/index.md +++ b/chapter_introduction/index.md @@ -755,4 +755,4 @@ Canny边缘检测器 :cite:`Canny.1987` 和SIFT特征提取器 :cite:`Lowe.2004` 1. 如果把人工智能的发展看作一场新的工业革命,那么算法和数据之间的关系是什么?它类似于蒸汽机和煤吗?根本区别是什么? 1. 你还可以在哪里应用端到端的训练方法,比如 :numref:`fig_ml_loop` 、物理、工程和计量经济学? -[Discussions](https://discuss.d2l.ai/t/2088) +[Discussions](https://discuss.d2l.ai/t/1744) diff --git a/chapter_multilayer-perceptrons/backprop.md b/chapter_multilayer-perceptrons/backprop.md index d81574ea3..446a0ee08 100644 --- a/chapter_multilayer-perceptrons/backprop.md +++ b/chapter_multilayer-perceptrons/backprop.md @@ -180,4 +180,4 @@ $\mathbf{W}^{(2)}$的当前值。 1. 你能把它划分到多个GPU上吗? 1. 与小批量训练相比,有哪些优点和缺点? -[Discussions](https://discuss.d2l.ai/t/1816) +[Discussions](https://discuss.d2l.ai/t/5769) diff --git a/chapter_natural-language-processing-applications/finetuning-bert.md b/chapter_natural-language-processing-applications/finetuning-bert.md index 8aabf4c8e..04e9157c8 100644 --- a/chapter_natural-language-processing-applications/finetuning-bert.md +++ b/chapter_natural-language-processing-applications/finetuning-bert.md @@ -64,4 +64,4 @@ 1. 我们如何利用BERT来训练语言模型? 1. 我们能在机器翻译中利用BERT吗? -[Discussions](https://discuss.d2l.ai/t/396) +[Discussions](https://discuss.d2l.ai/t/5729) diff --git a/chapter_natural-language-processing-applications/natural-language-inference-and-dataset.md b/chapter_natural-language-processing-applications/natural-language-inference-and-dataset.md index ae468a326..dde9a0d20 100644 --- a/chapter_natural-language-processing-applications/natural-language-inference-and-dataset.md +++ b/chapter_natural-language-processing-applications/natural-language-inference-and-dataset.md @@ -256,9 +256,9 @@ for X, Y in train_iter: 1. 我们如何更改超参数以减小词表大小? :begin_tab:`mxnet` -[Discussions](https://discuss.d2l.ai/t/394) +[Discussions](https://discuss.d2l.ai/t/5721) :end_tab: :begin_tab:`pytorch` -[Discussions](https://discuss.d2l.ai/t/1388) +[Discussions](https://discuss.d2l.ai/t/5722) :end_tab: diff --git a/chapter_natural-language-processing-applications/natural-language-inference-attention.md b/chapter_natural-language-processing-applications/natural-language-inference-attention.md index 21a5a3a9a..b8bec5833 100644 --- a/chapter_natural-language-processing-applications/natural-language-inference-attention.md +++ b/chapter_natural-language-processing-applications/natural-language-inference-attention.md @@ -381,9 +381,9 @@ predict_snli(net, vocab, ['he', 'is', 'good', '.'], ['he', 'is', 'bad', '.']) 1. 假设我们想要获得任何一对句子的语义相似级别(例如,0到1之间的连续值)。我们应该如何收集和标注数据集?你能设计一个有注意力机制的模型吗? :begin_tab:`mxnet` -[Discussions](https://discuss.d2l.ai/t/395) +[Discussions](https://discuss.d2l.ai/t/5727) :end_tab: :begin_tab:`pytorch` -[Discussions](https://discuss.d2l.ai/t/1530) +[Discussions](https://discuss.d2l.ai/t/5728) :end_tab: diff --git a/chapter_natural-language-processing-applications/natural-language-inference-bert.md b/chapter_natural-language-processing-applications/natural-language-inference-bert.md index d125319d2..b3101d57e 100644 --- a/chapter_natural-language-processing-applications/natural-language-inference-bert.md +++ b/chapter_natural-language-processing-applications/natural-language-inference-bert.md @@ -314,9 +314,9 @@ d2l.train_ch13(net, train_iter, test_iter, loss, trainer, num_epochs, 1. 如何根据一对序列的长度比值截断它们?将此对截断方法与`SNLIBERTDataset`类中使用的方法进行比较。它们的利弊是什么? :begin_tab:`mxnet` -[Discussions](https://discuss.d2l.ai/t/397) +[Discussions](https://discuss.d2l.ai/t/5715) :end_tab: :begin_tab:`pytorch` -[Discussions](https://discuss.d2l.ai/t/1526) +[Discussions](https://discuss.d2l.ai/t/5718) :end_tab: diff --git a/chapter_natural-language-processing-applications/sentiment-analysis-and-dataset.md b/chapter_natural-language-processing-applications/sentiment-analysis-and-dataset.md index 611d06e7b..e3c393600 100644 --- a/chapter_natural-language-processing-applications/sentiment-analysis-and-dataset.md +++ b/chapter_natural-language-processing-applications/sentiment-analysis-and-dataset.md @@ -174,9 +174,9 @@ def load_data_imdb(batch_size, num_steps=500): 1. 你能实现一个函数来将[Amazon reviews](https://snap.stanford.edu/data/web-Amazon.html)的数据集加载到数据迭代器中进行情感分析吗? :begin_tab:`mxnet` -[Discussions](https://discuss.d2l.ai/t/391) +[Discussions](https://discuss.d2l.ai/t/5725) :end_tab: :begin_tab:`pytorch` -[Discussions](https://discuss.d2l.ai/t/1387) +[Discussions](https://discuss.d2l.ai/t/5726) :end_tab: diff --git a/chapter_natural-language-processing-applications/sentiment-analysis-cnn.md b/chapter_natural-language-processing-applications/sentiment-analysis-cnn.md index 9d3f6f875..427f78f05 100644 --- a/chapter_natural-language-processing-applications/sentiment-analysis-cnn.md +++ b/chapter_natural-language-processing-applications/sentiment-analysis-cnn.md @@ -263,9 +263,9 @@ d2l.predict_sentiment(net, vocab, 'this movie is so bad') 1. 在输入表示中添加位置编码。它是否提高了分类的精度? :begin_tab:`mxnet` -[Discussions](https://discuss.d2l.ai/t/393) +[Discussions](https://discuss.d2l.ai/t/5719) :end_tab: :begin_tab:`pytorch` -[Discussions](https://discuss.d2l.ai/t/1425) +[Discussions](https://discuss.d2l.ai/t/5720) :end_tab: diff --git a/chapter_natural-language-processing-applications/sentiment-analysis-rnn.md b/chapter_natural-language-processing-applications/sentiment-analysis-rnn.md index 72623af77..f6381636e 100644 --- a/chapter_natural-language-processing-applications/sentiment-analysis-rnn.md +++ b/chapter_natural-language-processing-applications/sentiment-analysis-rnn.md @@ -207,9 +207,9 @@ predict_sentiment(net, vocab, 'this movie is so bad') 1. 是否可以通过spaCy词元化来提高分类精度?你需要安装Spacy(`pip install spacy`)和英语语言包(`python -m spacy download en`)。在代码中,首先导入Spacy(`import spacy`)。然后,加载Spacy英语软件包(`spacy_en = spacy.load('en')`)。最后,定义函数`def tokenizer(text): return [tok.text for tok in spacy_en.tokenizer(text)]`并替换原来的`tokenizer`函数。请注意GloVe和spaCy中短语标记的不同形式。例如,短语标记“new york”在GloVe中的形式是“new-york”,而在spaCy词元化之后的形式是“new york”。 :begin_tab:`mxnet` -[Discussions](https://discuss.d2l.ai/t/392) +[Discussions](https://discuss.d2l.ai/t/5723) :end_tab: :begin_tab:`pytorch` -[Discussions](https://discuss.d2l.ai/t/1424) +[Discussions](https://discuss.d2l.ai/t/5724) :end_tab: diff --git a/chapter_natural-language-processing-pretraining/approx-training.md b/chapter_natural-language-processing-pretraining/approx-training.md index 57475ebdb..a9cf5d1fc 100644 --- a/chapter_natural-language-processing-pretraining/approx-training.md +++ b/chapter_natural-language-processing-pretraining/approx-training.md @@ -84,4 +84,4 @@ $$\sum_{w \in \mathcal{V}} P(w \mid w_c) = 1.$$ 1. 验证 :eqref:`eq_hi-softmax-sum-one`是否有效。 1. 如何分别使用负采样和分层softmax训练连续词袋模型? -[Discussions](https://discuss.d2l.ai/t/382) +[Discussions](https://discuss.d2l.ai/t/5741) diff --git a/chapter_natural-language-processing-pretraining/bert-dataset.md b/chapter_natural-language-processing-pretraining/bert-dataset.md index d9655c1ac..4a5f08912 100644 --- a/chapter_natural-language-processing-pretraining/bert-dataset.md +++ b/chapter_natural-language-processing-pretraining/bert-dataset.md @@ -344,9 +344,9 @@ len(vocab) 1. 如果我们不过滤出一些不常见的词元,词量会有多大? :begin_tab:`mxnet` -[Discussions](https://discuss.d2l.ai/t/389) +[Discussions](https://discuss.d2l.ai/t/5737) :end_tab: :begin_tab:`pytorch` -[Discussions](https://discuss.d2l.ai/t/1496) +[Discussions](https://discuss.d2l.ai/t/5738) :end_tab: diff --git a/chapter_natural-language-processing-pretraining/bert-pretraining.md b/chapter_natural-language-processing-pretraining/bert-pretraining.md index 94e190c11..12c073013 100644 --- a/chapter_natural-language-processing-pretraining/bert-pretraining.md +++ b/chapter_natural-language-processing-pretraining/bert-pretraining.md @@ -261,9 +261,9 @@ encoded_pair.shape, encoded_pair_cls.shape, encoded_pair_crane[0][:3] 2. 将BERT输入序列的最大长度设置为512(与原始BERT模型相同)。使用原始BERT模型的配置,如$\text{BERT}_{\text{LARGE}}$。运行此部分时是否遇到错误?为什么? :begin_tab:`mxnet` -[Discussions](https://discuss.d2l.ai/t/390) +[Discussions](https://discuss.d2l.ai/t/5742) :end_tab: :begin_tab:`pytorch` -[Discussions](https://discuss.d2l.ai/t/1497) +[Discussions](https://discuss.d2l.ai/t/5743) :end_tab: diff --git a/chapter_natural-language-processing-pretraining/bert.md b/chapter_natural-language-processing-pretraining/bert.md index a73324748..2ee12969f 100644 --- a/chapter_natural-language-processing-pretraining/bert.md +++ b/chapter_natural-language-processing-pretraining/bert.md @@ -418,9 +418,9 @@ class BERTModel(nn.Module): 1. 在BERT的原始实现中,`BERTEncoder`中的位置前馈网络(通过`d2l.EncoderBlock`)和`MaskLM`中的全连接层都使用高斯误差线性单元(Gaussian error linear unit,GELU) :cite:`Hendrycks.Gimpel.2016`作为激活函数。研究GELU与ReLU之间的差异。 :begin_tab:`mxnet` -[Discussions](https://discuss.d2l.ai/t/388) +[Discussions](https://discuss.d2l.ai/t/5749) :end_tab: :begin_tab:`pytorch` -[Discussions](https://discuss.d2l.ai/t/1490) +[Discussions](https://discuss.d2l.ai/t/5750) :end_tab: diff --git a/chapter_natural-language-processing-pretraining/glove.md b/chapter_natural-language-processing-pretraining/glove.md index 9df2f3f6d..20c6f0f20 100644 --- a/chapter_natural-language-processing-pretraining/glove.md +++ b/chapter_natural-language-processing-pretraining/glove.md @@ -94,4 +94,4 @@ $$\mathbf{u}_j^\top \mathbf{v}_i + b_i + c_j \approx \log\, x_{ij}.$$ 1. 如果词$w_i$和$w_j$在同一上下文窗口中同时出现,我们如何使用它们在文本序列中的距离来重新设计计算条件概率$p_{ij}$的方法?提示:参见GloVe论文 :cite:`Pennington.Socher.Manning.2014`的第4.2节。 1. 对于任何一个词,它的中心词偏置和上下文偏置在数学上是等价的吗?为什么? -[Discussions](https://discuss.d2l.ai/t/385) +[Discussions](https://discuss.d2l.ai/t/5736) diff --git a/chapter_natural-language-processing-pretraining/similarity-analogy.md b/chapter_natural-language-processing-pretraining/similarity-analogy.md index d14fbe999..2b1479ac6 100644 --- a/chapter_natural-language-processing-pretraining/similarity-analogy.md +++ b/chapter_natural-language-processing-pretraining/similarity-analogy.md @@ -221,9 +221,9 @@ get_analogy('do', 'did', 'go', glove_6b50d) 1. 当词表非常大时,我们怎样才能更快地找到相似的词或完成一个词的类比呢? :begin_tab:`mxnet` -[Discussions](https://discuss.d2l.ai/t/387) +[Discussions](https://discuss.d2l.ai/t/5745) :end_tab: :begin_tab:`pytorch` -[Discussions](https://discuss.d2l.ai/t/1336) +[Discussions](https://discuss.d2l.ai/t/5746) :end_tab: diff --git a/chapter_natural-language-processing-pretraining/subword-embedding.md b/chapter_natural-language-processing-pretraining/subword-embedding.md index 0a8a96ff2..5718f7faf 100644 --- a/chapter_natural-language-processing-pretraining/subword-embedding.md +++ b/chapter_natural-language-processing-pretraining/subword-embedding.md @@ -145,9 +145,9 @@ print(segment_BPE(tokens, symbols)) 1. 如何扩展字节对编码的思想来提取短语? :begin_tab:`mxnet` -[Discussions](https://discuss.d2l.ai/t/386) +[Discussions](https://discuss.d2l.ai/t/5747) :end_tab: :begin_tab:`pytorch` -[Discussions](https://discuss.d2l.ai/t/386) +[Discussions](https://discuss.d2l.ai/t/5748) :end_tab: \ No newline at end of file diff --git a/chapter_natural-language-processing-pretraining/word-embedding-dataset.md b/chapter_natural-language-processing-pretraining/word-embedding-dataset.md index 655622707..7fb141f79 100644 --- a/chapter_natural-language-processing-pretraining/word-embedding-dataset.md +++ b/chapter_natural-language-processing-pretraining/word-embedding-dataset.md @@ -345,9 +345,9 @@ for batch in data_iter: 1. 本节代码中的哪些其他超参数可能会影响数据加载速度? :begin_tab:`mxnet` -[Discussions](https://discuss.d2l.ai/t/383) +[Discussions](https://discuss.d2l.ai/t/5734) :end_tab: :begin_tab:`pytorch` -[Discussions](https://discuss.d2l.ai/t/1330) +[Discussions](https://discuss.d2l.ai/t/5735) :end_tab: diff --git a/chapter_natural-language-processing-pretraining/word2vec-pretraining.md b/chapter_natural-language-processing-pretraining/word2vec-pretraining.md index 9d82b017b..8373842ce 100644 --- a/chapter_natural-language-processing-pretraining/word2vec-pretraining.md +++ b/chapter_natural-language-processing-pretraining/word2vec-pretraining.md @@ -275,9 +275,9 @@ get_similar_tokens('chip', 3, net[0]) 1. 当训练语料库很大时,在更新模型参数时,我们经常对当前小批量的*中心词*进行上下文词和噪声词的采样。换言之,同一中心词在不同的训练迭代轮数可以有不同的上下文词或噪声词。这种方法的好处是什么?尝试实现这种训练方法。 :begin_tab:`mxnet` -[Discussions](https://discuss.d2l.ai/t/384) +[Discussions](https://discuss.d2l.ai/t/5739) :end_tab: :begin_tab:`pytorch` -[Discussions](https://discuss.d2l.ai/t/1335) +[Discussions](https://discuss.d2l.ai/t/5740) :end_tab: diff --git a/chapter_natural-language-processing-pretraining/word2vec.md b/chapter_natural-language-processing-pretraining/word2vec.md index 0d24676ce..29c642a8f 100644 --- a/chapter_natural-language-processing-pretraining/word2vec.md +++ b/chapter_natural-language-processing-pretraining/word2vec.md @@ -121,4 +121,4 @@ $$\frac{\partial \log\, P(w_c \mid \mathcal{W}_o)}{\partial \mathbf{v}_{o_i}} = 1. 英语中的一些固定短语由多个单词组成,例如“new york”。如何训练它们的词向量?提示:查看word2vec论文的第四节 :cite:`Mikolov.Sutskever.Chen.ea.2013`。 1. 让我们以跳元模型为例来思考word2vec设计。跳元模型中两个词向量的点积与余弦相似度之间有什么关系?对于语义相似的一对词,为什么它们的词向量(由跳元模型训练)的余弦相似度可能很高? -[Discussions](https://discuss.d2l.ai/t/381) +[Discussions](https://discuss.d2l.ai/t/5744) diff --git a/chapter_optimization/adadelta.md b/chapter_optimization/adadelta.md index 854296886..0977bcc38 100644 --- a/chapter_optimization/adadelta.md +++ b/chapter_optimization/adadelta.md @@ -150,13 +150,13 @@ d2l.train_concise_ch11(trainer, {'learning_rate':5.0, 'rho': 0.9}, data_iter) 1. 将Adadelta的收敛行为与AdaGrad和RMSProp进行比较。 :begin_tab:`mxnet` -[Discussions](https://discuss.d2l.ai/t/357) +[Discussions](https://discuss.d2l.ai/t/5771) :end_tab: :begin_tab:`pytorch` -[Discussions](https://discuss.d2l.ai/t/1076) +[Discussions](https://discuss.d2l.ai/t/5772) :end_tab: :begin_tab:`tensorflow` -[Discussions](https://discuss.d2l.ai/t/1077) +[Discussions](https://discuss.d2l.ai/t/5773) :end_tab: diff --git a/chapter_recurrent-modern/beam-search.md b/chapter_recurrent-modern/beam-search.md index fd903389d..a6a461bba 100644 --- a/chapter_recurrent-modern/beam-search.md +++ b/chapter_recurrent-modern/beam-search.md @@ -166,4 +166,4 @@ $\alpha$通常设置为$0.75$。 1. 在 :numref:`sec_rnn_scratch`中,我们基于用户提供的前缀, 通过使用语言模型来生成文本。这个例子中使用了哪种搜索策略?你能改进吗? -[Discussions](https://discuss.d2l.ai/t/2786) +[Discussions](https://discuss.d2l.ai/t/5768) diff --git a/chapter_recurrent-modern/gru.md b/chapter_recurrent-modern/gru.md index 8e809670c..52ade35c9 100644 --- a/chapter_recurrent-modern/gru.md +++ b/chapter_recurrent-modern/gru.md @@ -423,5 +423,5 @@ d2l.train_ch8(model, train_iter, vocab, lr, num_epochs, strategy) :end_tab: :begin_tab:`pytorch` -[Discussions](https://discuss.d2l.ai/t/1056) +[Discussions](https://discuss.d2l.ai/t/2763) :end_tab: diff --git a/chapter_recurrent-neural-networks/rnn-concise.md b/chapter_recurrent-neural-networks/rnn-concise.md index 99257c8ee..c9650fc37 100644 --- a/chapter_recurrent-neural-networks/rnn-concise.md +++ b/chapter_recurrent-neural-networks/rnn-concise.md @@ -287,5 +287,5 @@ d2l.train_ch8(net, train_iter, vocab, lr, num_epochs, strategy) :end_tab: :begin_tab:`tensorflow` -[Discussions](https://discuss.d2l.ai/t/2211) +[Discussions](https://discuss.d2l.ai/t/5766) :end_tab: From fac299bf910dab86623ace7e0c5d4ebc0b1d1072 Mon Sep 17 00:00:00 2001 From: Aston Zhang Date: Wed, 8 Dec 2021 06:42:21 +0000 Subject: [PATCH 13/21] sync lib --- d2l/mxnet.py | 791 +++++++++++++++++++++++++++++++++++-------- d2l/tensorflow.py | 724 +++++++++++++++++++++++++++++++++------ d2l/torch.py | 845 +++++++++++++++++++++++++++++++++++++--------- 3 files changed, 1955 insertions(+), 405 deletions(-) diff --git a/d2l/mxnet.py b/d2l/mxnet.py index fee984ad0..1ebcf7d95 100644 --- a/d2l/mxnet.py +++ b/d2l/mxnet.py @@ -26,20 +26,20 @@ from mxnet.gluon import nn, rnn def use_svg_display(): - """使用svg格式在Jupyter中显示绘图。 + """使用svg格式在Jupyter中显示绘图 Defined in :numref:`sec_calculus`""" display.set_matplotlib_formats('svg') def set_figsize(figsize=(3.5, 2.5)): - """设置matplotlib的图表大小。 + """设置matplotlib的图表大小 Defined in :numref:`sec_calculus`""" use_svg_display() d2l.plt.rcParams['figure.figsize'] = figsize def set_axes(axes, xlabel, ylabel, xlim, ylim, xscale, yscale, legend): - """设置matplotlib的轴。 + """设置matplotlib的轴 Defined in :numref:`sec_calculus`""" axes.set_xlabel(xlabel) @@ -55,7 +55,7 @@ def set_axes(axes, xlabel, ylabel, xlim, ylim, xscale, yscale, legend): def plot(X, Y=None, xlabel=None, ylabel=None, legend=None, xlim=None, ylim=None, xscale='linear', yscale='linear', fmts=('-', 'm--', 'g-.', 'r:'), figsize=(3.5, 2.5), axes=None): - """绘制数据点。 + """绘制数据点 Defined in :numref:`sec_calculus`""" if legend is None: @@ -64,7 +64,7 @@ def plot(X, Y=None, xlabel=None, ylabel=None, legend=None, xlim=None, set_figsize(figsize) axes = axes if axes else d2l.plt.gca() - # 如果 `X` 有一个轴,输出True + # 如果`X`有一个轴,输出True def has_one_axis(X): return (hasattr(X, "ndim") and X.ndim == 1 or isinstance(X, list) and not hasattr(X[0], "__len__")) @@ -86,35 +86,35 @@ def has_one_axis(X): set_axes(axes, xlabel, ylabel, xlim, ylim, xscale, yscale, legend) class Timer: - """记录多次运行时间。""" + """记录多次运行时间""" def __init__(self): """Defined in :numref:`subsec_linear_model`""" self.times = [] self.start() def start(self): - """启动计时器。""" + """启动计时器""" self.tik = time.time() def stop(self): - """停止计时器并将时间记录在列表中。""" + """停止计时器并将时间记录在列表中""" self.times.append(time.time() - self.tik) return self.times[-1] def avg(self): - """返回平均时间。""" + """返回平均时间""" return sum(self.times) / len(self.times) def sum(self): - """返回时间总和。""" + """返回时间总和""" return sum(self.times) def cumsum(self): - """返回累计时间。""" + """返回累计时间""" return np.array(self.times).cumsum().tolist() def synthetic_data(w, b, num_examples): - """生成 y = Xw + b + 噪声。 + """生成y=Xw+b+噪声 Defined in :numref:`sec_linear_scratch`""" X = d2l.normal(0, 1, (num_examples, len(w))) @@ -123,33 +123,33 @@ def synthetic_data(w, b, num_examples): return X, d2l.reshape(y, (-1, 1)) def linreg(X, w, b): - """线性回归模型。 + """线性回归模型 Defined in :numref:`sec_linear_scratch`""" return d2l.matmul(X, w) + b def squared_loss(y_hat, y): - """均方损失。 + """均方损失 Defined in :numref:`sec_linear_scratch`""" return (y_hat - d2l.reshape(y, y_hat.shape)) ** 2 / 2 def sgd(params, lr, batch_size): - """小批量随机梯度下降。 + """小批量随机梯度下降 Defined in :numref:`sec_linear_scratch`""" for param in params: param[:] = param - lr * param.grad / batch_size def load_array(data_arrays, batch_size, is_train=True): - """构造一个Gluon数据迭代器。 + """构造一个Gluon数据迭代器 Defined in :numref:`sec_linear_concise`""" dataset = gluon.data.ArrayDataset(*data_arrays) return gluon.data.DataLoader(dataset, batch_size, shuffle=is_train) def get_fashion_mnist_labels(labels): - """返回Fashion-MNIST数据集的文本标签。 + """返回Fashion-MNIST数据集的文本标签 Defined in :numref:`sec_fashion_mnist`""" text_labels = ['t-shirt', 'trouser', 'pullover', 'dress', 'coat', @@ -157,7 +157,7 @@ def get_fashion_mnist_labels(labels): return [text_labels[int(i)] for i in labels] def show_images(imgs, num_rows, num_cols, titles=None, scale=1.5): - """绘制图像列表。 + """绘制图像列表 Defined in :numref:`sec_fashion_mnist`""" figsize = (num_cols * scale, num_rows * scale) @@ -172,13 +172,13 @@ def show_images(imgs, num_rows, num_cols, titles=None, scale=1.5): return axes def get_dataloader_workers(): - """在非Windows的平台上,使用4个进程来读取数据。 + """在非Windows的平台上,使用4个进程来读取数据 Defined in :numref:`sec_fashion_mnist`""" return 0 if sys.platform.startswith('win') else 4 def load_data_fashion_mnist(batch_size, resize=None): - """下载Fashion-MNIST数据集,然后将其加载到内存中。 + """下载Fashion-MNIST数据集,然后将其加载到内存中 Defined in :numref:`sec_fashion_mnist`""" dataset = gluon.data.vision @@ -194,7 +194,7 @@ def load_data_fashion_mnist(batch_size, resize=None): num_workers=get_dataloader_workers())) def accuracy(y_hat, y): - """计算预测正确的数量。 + """计算预测正确的数量 Defined in :numref:`sec_softmax_scratch`""" if len(y_hat.shape) > 1 and y_hat.shape[1] > 1: @@ -203,7 +203,7 @@ def accuracy(y_hat, y): return float(d2l.reduce_sum(d2l.astype(cmp, y.dtype))) def evaluate_accuracy(net, data_iter): - """计算在指定数据集上模型的精度。 + """计算在指定数据集上模型的精度 Defined in :numref:`sec_softmax_scratch`""" metric = Accumulator(2) # 正确预测数、预测总数 @@ -212,7 +212,7 @@ def evaluate_accuracy(net, data_iter): return metric[0] / metric[1] class Accumulator: - """在`n`个变量上累加。""" + """在`n`个变量上累加""" def __init__(self, n): """Defined in :numref:`sec_softmax_scratch`""" self.data = [0.0] * n @@ -227,7 +227,7 @@ def __getitem__(self, idx): return self.data[idx] def train_epoch_ch3(net, train_iter, loss, updater): - """训练模型一个迭代周期(定义见第3章)。 + """训练模型一个迭代周期(定义见第3章) Defined in :numref:`sec_softmax_scratch`""" # 训练损失总和、训练准确度总和、样本数 @@ -246,7 +246,7 @@ def train_epoch_ch3(net, train_iter, loss, updater): return metric[0] / metric[2], metric[1] / metric[2] class Animator: - """在动画中绘制数据。""" + """在动画中绘制数据""" def __init__(self, xlabel=None, ylabel=None, legend=None, xlim=None, ylim=None, xscale='linear', yscale='linear', fmts=('-', 'm--', 'g-.', 'r:'), nrows=1, ncols=1, @@ -287,7 +287,7 @@ def add(self, x, y): display.clear_output(wait=True) def train_ch3(net, train_iter, test_iter, loss, num_epochs, updater): - """训练模型(定义见第3章)。 + """训练模型(定义见第3章) Defined in :numref:`sec_softmax_scratch`""" animator = Animator(xlabel='epoch', xlim=[1, num_epochs], ylim=[0.3, 0.9], @@ -302,7 +302,7 @@ def train_ch3(net, train_iter, test_iter, loss, num_epochs, updater): assert test_acc <= 1 and test_acc > 0.7, test_acc def predict_ch3(net, test_iter, n=6): - """预测标签(定义见第3章)。 + """预测标签(定义见第3章) Defined in :numref:`sec_softmax_scratch`""" for X, y in test_iter: @@ -314,10 +314,10 @@ def predict_ch3(net, test_iter, n=6): d2l.reshape(X[0:n], (n, 28, 28)), 1, n, titles=titles[0:n]) def evaluate_loss(net, data_iter, loss): - """评估给定数据集上模型的损失。 + """评估给定数据集上模型的损失 Defined in :numref:`sec_model_selection`""" - metric = d2l.Accumulator(2) # 损失的总和, 样本数量 + metric = d2l.Accumulator(2) # 损失的总和,样本数量 for X, y in data_iter: l = loss(net(X), y) metric.add(d2l.reduce_sum(l), d2l.size(l)) @@ -327,10 +327,10 @@ def evaluate_loss(net, data_iter, loss): DATA_URL = 'http://d2l-data.s3-accelerate.amazonaws.com/' def download(name, cache_dir=os.path.join('..', 'data')): - """下载一个DATA_HUB中的文件,返回本地文件名。 + """下载一个DATA_HUB中的文件,返回本地文件名 Defined in :numref:`sec_kaggle_house`""" - assert name in DATA_HUB, f"{name} 不存在于 {DATA_HUB}." + assert name in DATA_HUB, f"{name} 不存在于 {DATA_HUB}" url, sha1_hash = DATA_HUB[name] os.makedirs(cache_dir, exist_ok=True) fname = os.path.join(cache_dir, url.split('/')[-1]) @@ -343,7 +343,7 @@ def download(name, cache_dir=os.path.join('..', 'data')): break sha1.update(data) if sha1.hexdigest() == sha1_hash: - return fname # Hit cache + return fname # 命中缓存 print(f'正在从{url}下载{fname}...') r = requests.get(url, stream=True, verify=True) with open(fname, 'wb') as f: @@ -351,7 +351,7 @@ def download(name, cache_dir=os.path.join('..', 'data')): return fname def download_extract(name, folder=None): - """下载并解压zip/tar文件。 + """下载并解压zip/tar文件 Defined in :numref:`sec_kaggle_house`""" fname = download(name) @@ -362,12 +362,12 @@ def download_extract(name, folder=None): elif ext in ('.tar', '.gz'): fp = tarfile.open(fname, 'r') else: - assert False, '只有zip/tar文件可以被解压缩。' + assert False, '只有zip/tar文件可以被解压缩' fp.extractall(base_dir) return os.path.join(base_dir, folder) if folder else data_dir def download_all(): - """下载DATA_HUB中的所有文件。 + """下载DATA_HUB中的所有文件 Defined in :numref:`sec_kaggle_house`""" for name in DATA_HUB: @@ -382,20 +382,20 @@ def download_all(): 'fa19780a7b011d9b009e8bff8e99922a8ee2eb90') def try_gpu(i=0): - """如果存在,则返回gpu(i),否则返回cpu()。 + """如果存在,则返回gpu(i),否则返回cpu() Defined in :numref:`sec_use_gpu`""" return npx.gpu(i) if npx.num_gpus() >= i + 1 else npx.cpu() def try_all_gpus(): - """返回所有可用的GPU,如果没有GPU,则返回[cpu()]。 + """返回所有可用的GPU,如果没有GPU,则返回[cpu()] Defined in :numref:`sec_use_gpu`""" devices = [npx.gpu(i) for i in range(npx.num_gpus())] return devices if devices else [npx.cpu()] def corr2d(X, K): - """计算二维互相关运算。 + """计算二维互相关运算 Defined in :numref:`sec_conv_layer`""" h, w = K.shape @@ -406,7 +406,7 @@ def corr2d(X, K): return Y def evaluate_accuracy_gpu(net, data_iter, device=None): - """使用GPU计算模型在数据集上的精度。 + """使用GPU计算模型在数据集上的精度 Defined in :numref:`sec_lenet`""" if not device: # 查询第一个参数所在的第一个设备 @@ -418,7 +418,7 @@ def evaluate_accuracy_gpu(net, data_iter, device=None): return metric[0] / metric[1] def train_ch6(net, train_iter, test_iter, num_epochs, lr, device): - """用GPU训练模型(在第六章定义)。 + """用GPU训练模型(在第六章定义) Defined in :numref:`sec_lenet`""" net.initialize(force_reinit=True, ctx=device, init=init.Xavier()) @@ -429,7 +429,7 @@ def train_ch6(net, train_iter, test_iter, num_epochs, lr, device): legend=['train loss', 'train acc', 'test acc']) timer, num_batches = d2l.Timer(), len(train_iter) for epoch in range(num_epochs): - metric = d2l.Accumulator(3) # 训练损失之和,训练精度之和,范例数 + metric = d2l.Accumulator(3) # 训练损失之和,训练准确率之和,范例数 for i, (X, y) in enumerate(train_iter): timer.start() # 下面是与“d2l.train_epoch_ch3”的主要不同 @@ -478,7 +478,7 @@ def forward(self, X): '090b5e7e70c295757f55df93cb0a180b9691891a') def read_time_machine(): - """Load the time machine dataset into a list of text lines. + """将时间机器数据集加载到文本行的列表中 Defined in :numref:`sec_text_preprocessing`""" with open(d2l.download('time_machine'), 'r') as f: @@ -486,7 +486,7 @@ def read_time_machine(): return [re.sub('[^A-Za-z]+', ' ', line).strip().lower() for line in lines] def tokenize(lines, token='word'): - """将文本行拆分为单词或字符词元。 + """将文本行拆分为单词或字符词元 Defined in :numref:`sec_text_preprocessing`""" if token == 'word': @@ -506,16 +506,19 @@ def __init__(self, tokens=None, min_freq=0, reserved_tokens=None): reserved_tokens = [] # 按出现频率排序 counter = count_corpus(tokens) - self.token_freqs = sorted(counter.items(), key=lambda x: x[1], - reverse=True) + self._token_freqs = sorted(counter.items(), key=lambda x: x[1], + reverse=True) # 未知词元的索引为0 - self.unk, uniq_tokens = 0, [''] + reserved_tokens - uniq_tokens += [token for token, freq in self.token_freqs - if freq >= min_freq and token not in uniq_tokens] + self.idx_to_token = [''] + reserved_tokens + self.token_to_idx = {token: idx + for idx, token in enumerate(self.idx_to_token)} self.idx_to_token, self.token_to_idx = [], dict() - for token in uniq_tokens: - self.idx_to_token.append(token) - self.token_to_idx[token] = len(self.idx_to_token) - 1 + for token, freq in self._token_freqs: + if freq < min_freq: + break + if token not in self.token_to_idx: + self.idx_to_token.append(token) + self.token_to_idx[token] = len(self.idx_to_token) - 1 def __len__(self): return len(self.idx_to_token) @@ -530,18 +533,26 @@ def to_tokens(self, indices): return self.idx_to_token[indices] return [self.idx_to_token[index] for index in indices] + @property + def unk(self): # 未知词元的索引为0 + return 0 + + @property + def token_freqs(self): + return self._token_freqs + def count_corpus(tokens): - """统计词元的频率。 + """统计词元的频率 Defined in :numref:`sec_text_preprocessing`""" - # 这里的 `tokens` 是 1D 列表或 2D 列表 + # 这里的`tokens`是1D列表或2D列表 if len(tokens) == 0 or isinstance(tokens[0], list): - # 将词元列表展平成使用词元填充的一个列表 + # 将词元列表展平成一个列表 tokens = [token for line in tokens for token in line] return collections.Counter(tokens) def load_corpus_time_machine(max_tokens=-1): - """返回时光机器数据集的词元索引列表和词表。 + """返回时光机器数据集的词元索引列表和词表 Defined in :numref:`sec_text_preprocessing`""" lines = read_time_machine() @@ -555,10 +566,10 @@ def load_corpus_time_machine(max_tokens=-1): return corpus, vocab def seq_data_iter_random(corpus, batch_size, num_steps): - """使用随机抽样生成一个小批量子序列。 + """使用随机抽样生成一个小批量子序列 Defined in :numref:`sec_language_model`""" - # 从随机偏移量开始对序列进行分区,随机范围包括`num_steps - 1` + # 从随机偏移量开始对序列进行分区,随机范围包括`num_steps-1` corpus = corpus[random.randint(0, num_steps - 1):] # 减去1,是因为我们需要考虑标签 num_subseqs = (len(corpus) - 1) // num_steps @@ -581,7 +592,7 @@ def data(pos): yield d2l.tensor(X), d2l.tensor(Y) def seq_data_iter_sequential(corpus, batch_size, num_steps): - """使用顺序分区生成一个小批量子序列。 + """使用顺序分区生成一个小批量子序列 Defined in :numref:`sec_language_model`""" # 从随机偏移量开始划分序列 @@ -597,7 +608,7 @@ def seq_data_iter_sequential(corpus, batch_size, num_steps): yield X, Y class SeqDataLoader: - """加载序列数据的迭代器。""" + """加载序列数据的迭代器""" def __init__(self, batch_size, num_steps, use_random_iter, max_tokens): """Defined in :numref:`sec_language_model`""" if use_random_iter: @@ -612,7 +623,7 @@ def __iter__(self): def load_data_time_machine(batch_size, num_steps, use_random_iter=False, max_tokens=10000): - """返回时光机器数据集的迭代器和词表。 + """返回时光机器数据集的迭代器和词表 Defined in :numref:`sec_language_model`""" data_iter = SeqDataLoader( @@ -636,7 +647,7 @@ def begin_state(self, batch_size, ctx): return self.init_state(batch_size, self.num_hiddens, ctx) def predict_ch8(prefix, num_preds, net, vocab, device): - """在`prefix`后面生成新字符。 + """在`prefix`后面生成新字符 Defined in :numref:`sec_rnn_scratch`""" state = net.begin_state(batch_size=1, ctx=device) @@ -652,7 +663,7 @@ def predict_ch8(prefix, num_preds, net, vocab, device): return ''.join([vocab.idx_to_token[i] for i in outputs]) def grad_clipping(net, theta): - """裁剪梯度。 + """裁剪梯度 Defined in :numref:`sec_rnn_scratch`""" if isinstance(net, gluon.Block): @@ -665,11 +676,11 @@ def grad_clipping(net, theta): param.grad[:] *= theta / norm def train_epoch_ch8(net, train_iter, loss, updater, device, use_random_iter): - """训练模型一个迭代周期(定义见第8章)。 + """训练模型一个迭代周期(定义见第8章) Defined in :numref:`sec_rnn_scratch`""" state, timer = None, d2l.Timer() - metric = d2l.Accumulator(2) # 训练损失之和, 词元数量 + metric = d2l.Accumulator(2) # 训练损失之和,词元数量 for X, Y in train_iter: if state is None or use_random_iter: # 在第一次迭代或使用随机抽样时初始化`state` @@ -690,7 +701,7 @@ def train_epoch_ch8(net, train_iter, loss, updater, device, use_random_iter): def train_ch8(net, train_iter, vocab, lr, num_epochs, device, use_random_iter=False): - """训练模型(定义见第8章)。 + """训练模型(定义见第8章) Defined in :numref:`sec_rnn_scratch`""" loss = gluon.loss.SoftmaxCrossEntropyLoss() @@ -717,7 +728,7 @@ def train_ch8(net, train_iter, vocab, lr, num_epochs, device, print(predict('traveller')) class RNNModel(nn.Block): - """循环神经网络模型。 + """循环神经网络模型 Defined in :numref:`sec_rnn-concise`""" def __init__(self, rnn_layer, vocab_size, **kwargs): @@ -729,23 +740,500 @@ def __init__(self, rnn_layer, vocab_size, **kwargs): def forward(self, inputs, state): X = npx.one_hot(inputs.T, self.vocab_size) Y, state = self.rnn(X, state) - # 全连接层首先将`Y`的形状改为(`时间步数`*`批量大小`, `隐藏单元数`)。 - # 它的输出形状是 (`时间步数`*`批量大小`, `词表大小`)。 + # 全连接层首先将`Y`的形状改为(`时间步数`*`批量大小`,`隐藏单元数`) + # 它的输出形状是(`时间步数`*`批量大小`,`词表大小`) output = self.dense(Y.reshape(-1, Y.shape[-1])) return output, state def begin_state(self, *args, **kwargs): return self.rnn.begin_state(*args, **kwargs) +d2l.DATA_HUB['fra-eng'] = (d2l.DATA_URL + 'fra-eng.zip', + '94646ad1522d915e7b0f9296181140edcf86a4f5') + +def read_data_nmt(): + """载入“英语-法语”数据集 + + Defined in :numref:`sec_machine_translation`""" + data_dir = d2l.download_extract('fra-eng') + with open(os.path.join(data_dir, 'fra.txt'), 'r', + encoding='utf-8') as f: + return f.read() + +def preprocess_nmt(text): + """预处理“英语-法语”数据集 + + Defined in :numref:`sec_machine_translation`""" + def no_space(char, prev_char): + return char in set(',.!?') and prev_char != ' ' + + # 使用空格替换不间断空格 + # 使用小写字母替换大写字母 + text = text.replace('\u202f', ' ').replace('\xa0', ' ').lower() + # 在单词和标点符号之间插入空格 + out = [' ' + char if i > 0 and no_space(char, text[i - 1]) else char + for i, char in enumerate(text)] + return ''.join(out) + +def tokenize_nmt(text, num_examples=None): + """词元化“英语-法语”数据数据集 + + Defined in :numref:`sec_machine_translation`""" + source, target = [], [] + for i, line in enumerate(text.split('\n')): + if num_examples and i > num_examples: + break + parts = line.split('\t') + if len(parts) == 2: + source.append(parts[0].split(' ')) + target.append(parts[1].split(' ')) + return source, target + +def truncate_pad(line, num_steps, padding_token): + """截断或填充文本序列 + + Defined in :numref:`sec_machine_translation`""" + if len(line) > num_steps: + return line[:num_steps] # 截断 + return line + [padding_token] * (num_steps - len(line)) # 填充 + +def build_array_nmt(lines, vocab, num_steps): + """将机器翻译的文本序列转换成小批量 + + Defined in :numref:`subsec_mt_data_loading`""" + lines = [vocab[l] for l in lines] + lines = [l + [vocab['']] for l in lines] + array = d2l.tensor([truncate_pad( + l, num_steps, vocab['']) for l in lines]) + valid_len = d2l.reduce_sum( + d2l.astype(array != vocab[''], d2l.int32), 1) + return array, valid_len + +def load_data_nmt(batch_size, num_steps, num_examples=600): + """返回翻译数据集的迭代器和词表 + + Defined in :numref:`subsec_mt_data_loading`""" + text = preprocess_nmt(read_data_nmt()) + source, target = tokenize_nmt(text, num_examples) + src_vocab = d2l.Vocab(source, min_freq=2, + reserved_tokens=['', '', '']) + tgt_vocab = d2l.Vocab(target, min_freq=2, + reserved_tokens=['', '', '']) + src_array, src_valid_len = build_array_nmt(source, src_vocab, num_steps) + tgt_array, tgt_valid_len = build_array_nmt(target, tgt_vocab, num_steps) + data_arrays = (src_array, src_valid_len, tgt_array, tgt_valid_len) + data_iter = d2l.load_array(data_arrays, batch_size) + return data_iter, src_vocab, tgt_vocab + +class Encoder(nn.Block): + """编码器-解码器架构的基本编码器接口""" + def __init__(self, **kwargs): + super(Encoder, self).__init__(**kwargs) + + def forward(self, X, *args): + raise NotImplementedError + +class Decoder(nn.Block): + """编码器-解码器架构的基本解码器接口 + + Defined in :numref:`sec_encoder-decoder`""" + def __init__(self, **kwargs): + super(Decoder, self).__init__(**kwargs) + + def init_state(self, enc_outputs, *args): + raise NotImplementedError + + def forward(self, X, state): + raise NotImplementedError + +class EncoderDecoder(nn.Block): + """编码器-解码器架构的基类 + + Defined in :numref:`sec_encoder-decoder`""" + def __init__(self, encoder, decoder, **kwargs): + super(EncoderDecoder, self).__init__(**kwargs) + self.encoder = encoder + self.decoder = decoder + + def forward(self, enc_X, dec_X, *args): + enc_outputs = self.encoder(enc_X, *args) + dec_state = self.decoder.init_state(enc_outputs, *args) + return self.decoder(dec_X, dec_state) + +class Seq2SeqEncoder(d2l.Encoder): + """用于序列到序列学习的循环神经网络编码器 + + Defined in :numref:`sec_seq2seq`""" + def __init__(self, vocab_size, embed_size, num_hiddens, num_layers, + dropout=0, **kwargs): + super(Seq2SeqEncoder, self).__init__(**kwargs) + # 嵌入层 + self.embedding = nn.Embedding(vocab_size, embed_size) + self.rnn = rnn.GRU(num_hiddens, num_layers, dropout=dropout) + + def forward(self, X, *args): + # 输出'X'的形状:(`batch_size`,`num_steps`,`embed_size`) + X = self.embedding(X) + # 在循环神经网络模型中,第一个轴对应于时间步 + X = X.swapaxes(0, 1) + state = self.rnn.begin_state(batch_size=X.shape[1], ctx=X.ctx) + output, state = self.rnn(X, state) + # `output`的形状:(`num_steps`,`batch_size`,`num_hiddens`) + # `state[0]`的形状:(`num_layers`,`batch_size`,`num_hiddens`) + return output, state + +class MaskedSoftmaxCELoss(gluon.loss.SoftmaxCELoss): + """带遮蔽的softmax交叉熵损失函数 + + Defined in :numref:`sec_seq2seq_decoder`""" + # `pred`的形状:(`batch_size`,`num_steps`,`vocab_size`) + # `label`的形状:(`batch_size`,`num_steps`) + # `valid_len`的形状:(`batch_size`,) + def forward(self, pred, label, valid_len): + # `weights`的形状:(`batch_size`,`num_steps`,1) + weights = np.expand_dims(np.ones_like(label), axis=-1) + weights = npx.sequence_mask(weights, valid_len, True, axis=1) + return super(MaskedSoftmaxCELoss, self).forward(pred, label, weights) + +def train_seq2seq(net, data_iter, lr, num_epochs, tgt_vocab, device): + """训练序列到序列模型 + + Defined in :numref:`sec_seq2seq_decoder`""" + net.initialize(init.Xavier(), force_reinit=True, ctx=device) + trainer = gluon.Trainer(net.collect_params(), 'adam', + {'learning_rate': lr}) + loss = MaskedSoftmaxCELoss() + animator = d2l.Animator(xlabel='epoch', ylabel='loss', + xlim=[10, num_epochs]) + for epoch in range(num_epochs): + timer = d2l.Timer() + metric = d2l.Accumulator(2) # 训练损失求和,词元数量 + for batch in data_iter: + X, X_valid_len, Y, Y_valid_len = [ + x.as_in_ctx(device) for x in batch] + bos = np.array([tgt_vocab['']] * Y.shape[0], + ctx=device).reshape(-1, 1) + dec_input = np.concatenate([bos, Y[:, :-1]], 1) # 强制教学 + with autograd.record(): + Y_hat, _ = net(X, dec_input, X_valid_len) + l = loss(Y_hat, Y, Y_valid_len) + l.backward() + d2l.grad_clipping(net, 1) + num_tokens = Y_valid_len.sum() + trainer.step(num_tokens) + metric.add(l.sum(), num_tokens) + if (epoch + 1) % 10 == 0: + animator.add(epoch + 1, (metric[0] / metric[1],)) + print(f'loss {metric[0] / metric[1]:.3f}, {metric[1] / timer.stop():.1f} ' + f'tokens/sec on {str(device)}') + +def predict_seq2seq(net, src_sentence, src_vocab, tgt_vocab, num_steps, + device, save_attention_weights=False): + """序列到序列模型的预测 + + Defined in :numref:`sec_seq2seq_training`""" + src_tokens = src_vocab[src_sentence.lower().split(' ')] + [ + src_vocab['']] + enc_valid_len = np.array([len(src_tokens)], ctx=device) + src_tokens = d2l.truncate_pad(src_tokens, num_steps, src_vocab['']) + # 添加批量轴 + enc_X = np.expand_dims(np.array(src_tokens, ctx=device), axis=0) + enc_outputs = net.encoder(enc_X, enc_valid_len) + dec_state = net.decoder.init_state(enc_outputs, enc_valid_len) + # 添加批量轴 + dec_X = np.expand_dims(np.array([tgt_vocab['']], ctx=device), + axis=0) + output_seq, attention_weight_seq = [], [] + for _ in range(num_steps): + Y, dec_state = net.decoder(dec_X, dec_state) + # 我们使用具有预测最高可能性的词元,作为解码器在下一时间步的输入 + dec_X = Y.argmax(axis=2) + pred = dec_X.squeeze(axis=0).astype('int32').item() + # 保存注意力权重(稍后讨论) + if save_attention_weights: + attention_weight_seq.append(net.decoder.attention_weights) + # 一旦序列结束词元被预测,输出序列的生成就完成了 + if pred == tgt_vocab['']: + break + output_seq.append(pred) + return ' '.join(tgt_vocab.to_tokens(output_seq)), attention_weight_seq + +def bleu(pred_seq, label_seq, k): + """计算BLEU + + Defined in :numref:`sec_seq2seq_training`""" + pred_tokens, label_tokens = pred_seq.split(' '), label_seq.split(' ') + len_pred, len_label = len(pred_tokens), len(label_tokens) + score = math.exp(min(0, 1 - len_label / len_pred)) + for n in range(1, k + 1): + num_matches, label_subs = 0, collections.defaultdict(int) + for i in range(len_label - n + 1): + label_subs[' '.join(label_tokens[i: i + n])] += 1 + for i in range(len_pred - n + 1): + if label_subs[' '.join(pred_tokens[i: i + n])] > 0: + num_matches += 1 + label_subs[' '.join(pred_tokens[i: i + n])] -= 1 + score *= math.pow(num_matches / (len_pred - n + 1), math.pow(0.5, n)) + return score + +def show_heatmaps(matrices, xlabel, ylabel, titles=None, figsize=(2.5, 2.5), + cmap='Reds'): + """显示矩阵热图 + + Defined in :numref:`sec_attention-cues`""" + d2l.use_svg_display() + num_rows, num_cols = matrices.shape[0], matrices.shape[1] + fig, axes = d2l.plt.subplots(num_rows, num_cols, figsize=figsize, + sharex=True, sharey=True, squeeze=False) + for i, (row_axes, row_matrices) in enumerate(zip(axes, matrices)): + for j, (ax, matrix) in enumerate(zip(row_axes, row_matrices)): + pcm = ax.imshow(d2l.numpy(matrix), cmap=cmap) + if i == num_rows - 1: + ax.set_xlabel(xlabel) + if j == 0: + ax.set_ylabel(ylabel) + if titles: + ax.set_title(titles[j]) + fig.colorbar(pcm, ax=axes, shrink=0.6); + +def masked_softmax(X, valid_lens): + """通过在最后一个轴上掩蔽元素来执行softmax操作 + + Defined in :numref:`sec_attention-scoring-functions`""" + # `X`:3D张量,`valid_lens`:1D或2D张量 + if valid_lens is None: + return npx.softmax(X) + else: + shape = X.shape + if valid_lens.ndim == 1: + valid_lens = valid_lens.repeat(shape[1]) + else: + valid_lens = valid_lens.reshape(-1) + # 最后一轴上被掩蔽的元素使用一个非常大的负值替换,从而其softmax输出为0 + X = npx.sequence_mask(X.reshape(-1, shape[-1]), valid_lens, True, + value=-1e6, axis=1) + return npx.softmax(X).reshape(shape) + +class AdditiveAttention(nn.Block): + """加性注意力 + + Defined in :numref:`sec_attention-scoring-functions`""" + def __init__(self, num_hiddens, dropout, **kwargs): + super(AdditiveAttention, self).__init__(**kwargs) + # 使用'flatten=False'只转换最后一个轴,以便其他轴的形状保持不变 + self.W_k = nn.Dense(num_hiddens, use_bias=False, flatten=False) + self.W_q = nn.Dense(num_hiddens, use_bias=False, flatten=False) + self.w_v = nn.Dense(1, use_bias=False, flatten=False) + self.dropout = nn.Dropout(dropout) + + def forward(self, queries, keys, values, valid_lens): + queries, keys = self.W_q(queries), self.W_k(keys) + # 在维度扩展后, + # `queries`的形状:(`batch_size`,查询的个数,1,`num_hidden`) + # `key`的形状:(`batch_size`,1,“键-值”对的个数,`num_hiddens`) + # 使用广播的方式进行求和 + features = np.expand_dims(queries, axis=2) + np.expand_dims( + keys, axis=1) + features = np.tanh(features) + # `self.w_v`仅有一个输出,因此从形状中移除最后那个维度。 + # `scores`的形状:(`batch_size`,查询的个数,“键-值”对的个数) + scores = np.squeeze(self.w_v(features), axis=-1) + self.attention_weights = masked_softmax(scores, valid_lens) + # `values`的形状:(`batch_size`,“键-值”对的个数,值的维度) + return npx.batch_dot(self.dropout(self.attention_weights), values) + +class DotProductAttention(nn.Block): + """缩放点积注意力 + + Defined in :numref:`subsec_additive-attention`""" + def __init__(self, dropout, **kwargs): + super(DotProductAttention, self).__init__(**kwargs) + self.dropout = nn.Dropout(dropout) + + # `queries`的形状:(`batch_size`,查询的个数,`d`) + # `keys`的形状:(`batch_size`,“键-值”对的个数,`d`) + # `values`的形状:(`batch_size`,“键-值”对的个数,值的维度) + # `valid_lens`的形状:(`batch_size`,)或者(`batch_size`,查询的个数) + def forward(self, queries, keys, values, valid_lens=None): + d = queries.shape[-1] + # 设置`transpose_b=True`为了交换`keys`的最后两个维度 + scores = npx.batch_dot(queries, keys, transpose_b=True) / math.sqrt(d) + self.attention_weights = masked_softmax(scores, valid_lens) + return npx.batch_dot(self.dropout(self.attention_weights), values) + +class AttentionDecoder(d2l.Decoder): + """带有注意力机制解码器的基本接口 + + Defined in :numref:`sec_seq2seq_attention`""" + def __init__(self, **kwargs): + super(AttentionDecoder, self).__init__(**kwargs) + + @property + def attention_weights(self): + raise NotImplementedError + +class MultiHeadAttention(nn.Block): + """多头注意力 + + Defined in :numref:`sec_multihead-attention`""" + def __init__(self, num_hiddens, num_heads, dropout, use_bias=False, + **kwargs): + super(MultiHeadAttention, self).__init__(**kwargs) + self.num_heads = num_heads + self.attention = d2l.DotProductAttention(dropout) + self.W_q = nn.Dense(num_hiddens, use_bias=use_bias, flatten=False) + self.W_k = nn.Dense(num_hiddens, use_bias=use_bias, flatten=False) + self.W_v = nn.Dense(num_hiddens, use_bias=use_bias, flatten=False) + self.W_o = nn.Dense(num_hiddens, use_bias=use_bias, flatten=False) + + def forward(self, queries, keys, values, valid_lens): + # `queries`,`keys`,`values`的形状: + # (`batch_size`,查询或者“键-值”对的个数,`num_hiddens`) + # `valid_lens` 的形状: + # (`batch_size`,)或(`batch_size`,查询的个数) + # 经过变换后,输出的`queries`,`keys`,`values` 的形状: + # (`batch_size`*`num_heads`,查询或者“键-值”对的个数, + # `num_hiddens`/`num_heads`) + queries = transpose_qkv(self.W_q(queries), self.num_heads) + keys = transpose_qkv(self.W_k(keys), self.num_heads) + values = transpose_qkv(self.W_v(values), self.num_heads) + + if valid_lens is not None: + # 在轴0,将第一项(标量或者矢量)复制`num_heads`次, + # 然后如此复制第二项,然后诸如此类。 + valid_lens = valid_lens.repeat(self.num_heads, axis=0) + + # `output`的形状:(`batch_size`*`num_heads`,查询的个数, + # `num_hiddens`/`num_heads`) + output = self.attention(queries, keys, values, valid_lens) + + # `output_concat`的形状:(`batch_size`,查询的个数,`num_hiddens`) + output_concat = transpose_output(output, self.num_heads) + return self.W_o(output_concat) + +def transpose_qkv(X, num_heads): + """为了多注意力头的并行计算而变换形状 + + Defined in :numref:`sec_multihead-attention`""" + # 输入`X`的形状:(`batch_size`,查询或者“键-值”对的个数,`num_hiddens`) + # 输出`X`的形状:(`batch_size`,查询或者“键-值”对的个数,`num_heads`, + # `num_hiddens`/`num_heads`) + X = X.reshape(X.shape[0], X.shape[1], num_heads, -1) + + # 输出`X`的形状:(`batch_size`,`num_heads`,查询或者“键-值”对的个数, + # `num_hiddens`/`num_heads`) + X = X.transpose(0, 2, 1, 3) + + # 最终输出的形状:(`batch_size`*`num_heads`,查询或者“键-值”对的个数, + # `num_hiddens`/`num_heads`) + return X.reshape(-1, X.shape[2], X.shape[3]) + + +def transpose_output(X, num_heads): + """逆转`transpose_qkv`函数的操作 + + Defined in :numref:`sec_multihead-attention`""" + X = X.reshape(-1, num_heads, X.shape[1], X.shape[2]) + X = X.transpose(0, 2, 1, 3) + return X.reshape(X.shape[0], X.shape[1], -1) + +class PositionalEncoding(nn.Block): + """位置编码 + + Defined in :numref:`sec_self-attention-and-positional-encoding`""" + def __init__(self, num_hiddens, dropout, max_len=1000): + super(PositionalEncoding, self).__init__() + self.dropout = nn.Dropout(dropout) + # 创建一个足够长的`P` + self.P = d2l.zeros((1, max_len, num_hiddens)) + X = d2l.arange(max_len).reshape(-1, 1) / np.power( + 10000, np.arange(0, num_hiddens, 2) / num_hiddens) + self.P[:, :, 0::2] = np.sin(X) + self.P[:, :, 1::2] = np.cos(X) + + def forward(self, X): + X = X + self.P[:, :X.shape[1], :].as_in_ctx(X.ctx) + return self.dropout(X) + +class PositionWiseFFN(nn.Block): + """基于位置的前馈网络 + + Defined in :numref:`sec_transformer`""" + def __init__(self, ffn_num_hiddens, ffn_num_outputs, **kwargs): + super(PositionWiseFFN, self).__init__(**kwargs) + self.dense1 = nn.Dense(ffn_num_hiddens, flatten=False, + activation='relu') + self.dense2 = nn.Dense(ffn_num_outputs, flatten=False) + + def forward(self, X): + return self.dense2(self.dense1(X)) + +class AddNorm(nn.Block): + """残差连接后进行层规范化 + + Defined in :numref:`sec_transformer`""" + def __init__(self, dropout, **kwargs): + super(AddNorm, self).__init__(**kwargs) + self.dropout = nn.Dropout(dropout) + self.ln = nn.LayerNorm() + + def forward(self, X, Y): + return self.ln(self.dropout(Y) + X) + +class EncoderBlock(nn.Block): + """transformer编码器块 + + Defined in :numref:`sec_transformer`""" + def __init__(self, num_hiddens, ffn_num_hiddens, num_heads, dropout, + use_bias=False, **kwargs): + super(EncoderBlock, self).__init__(**kwargs) + self.attention = d2l.MultiHeadAttention( + num_hiddens, num_heads, dropout, use_bias) + self.addnorm1 = AddNorm(dropout) + self.ffn = PositionWiseFFN(ffn_num_hiddens, num_hiddens) + self.addnorm2 = AddNorm(dropout) + + def forward(self, X, valid_lens): + Y = self.addnorm1(X, self.attention(X, X, X, valid_lens)) + return self.addnorm2(Y, self.ffn(Y)) + +class TransformerEncoder(d2l.Encoder): + """transformer编码器 + + Defined in :numref:`sec_transformer`""" + def __init__(self, vocab_size, num_hiddens, ffn_num_hiddens, + num_heads, num_layers, dropout, use_bias=False, **kwargs): + super(TransformerEncoder, self).__init__(**kwargs) + self.num_hiddens = num_hiddens + self.embedding = nn.Embedding(vocab_size, num_hiddens) + self.pos_encoding = d2l.PositionalEncoding(num_hiddens, dropout) + self.blks = nn.Sequential() + for _ in range(num_layers): + self.blks.add( + EncoderBlock(num_hiddens, ffn_num_hiddens, num_heads, dropout, + use_bias)) + + def forward(self, X, valid_lens, *args): + # 因为位置编码值在-1和1之间, + # 因此嵌入值乘以嵌入维度的平方根进行缩放, + # 然后再与位置编码相加。 + X = self.pos_encoding(self.embedding(X) * math.sqrt(self.num_hiddens)) + self.attention_weights = [None] * len(self.blks) + for i, blk in enumerate(self.blks): + X = blk(X, valid_lens) + self.attention_weights[ + i] = blk.attention.attention.attention_weights + return X + def annotate(text, xy, xytext): d2l.plt.gca().annotate(text, xy=xy, xytext=xytext, arrowprops=dict(arrowstyle='->')) def train_2d(trainer, steps=20, f_grad=None): - """用定制的训练机优化2D目标函数。 + """用定制的训练机优化2D目标函数 Defined in :numref:`subsec_gd-learningrate`""" - # `s1` 和 `s2` 是稍后将使用的内部状态变量 + # `s1`和`s2`是稍后将使用的内部状态变量 x1, x2, s1, s2 = -5, -2, 0, 0 results = [(x1, x2)] for i in range(steps): @@ -758,7 +1246,7 @@ def train_2d(trainer, steps=20, f_grad=None): return results def show_trace_2d(f, results): - """显示优化过程中2D变量的轨迹。 + """显示优化过程中2D变量的轨迹 Defined in :numref:`subsec_gd-learningrate`""" d2l.set_figsize() @@ -835,6 +1323,7 @@ def train_concise_ch11(tr_name, hyperparams, data_iter, num_epochs=2): print(f'loss: {animator.Y[0][-1]:.3f}, {timer.avg():.3f} sec/epoch') class Benchmark: + """用于测量运行时间""" def __init__(self, description='Done'): """Defined in :numref:`sec_hybridize`""" self.description = description @@ -855,7 +1344,7 @@ def split_batch(X, y, devices): gluon.utils.split_and_load(y, devices)) def resnet18(num_classes): - """稍加修改的 ResNet-18 模型。 + """稍加修改的ResNet-18模型 Defined in :numref:`sec_multi_gpu_concise`""" def resnet_block(num_channels, num_residuals, first_block=False): @@ -869,7 +1358,7 @@ def resnet_block(num_channels, num_residuals, first_block=False): return blk net = nn.Sequential() - # 该模型使用了更小的卷积核、步长和填充,而且删除了最大汇聚层。 + # 该模型使用了更小的卷积核、步长和填充,而且删除了最大汇聚层 net.add(nn.Conv2D(64, kernel_size=3, strides=1, padding=1), nn.BatchNorm(), nn.Activation('relu')) net.add(resnet_block(64, 2, first_block=True), @@ -880,7 +1369,7 @@ def resnet_block(num_channels, num_residuals, first_block=False): return net def evaluate_accuracy_gpus(net, data_iter, split_f=d2l.split_batch): - """使用多个GPU计算数据集上模型的精度。 + """使用多个GPU计算数据集上模型的精度 Defined in :numref:`sec_multi_gpu_concise`""" # 查询设备列表 @@ -972,14 +1461,14 @@ def box_center_to_corner(boxes): def bbox_to_rect(bbox, color): """Defined in :numref:`sec_bbox`""" - # 将边界框 (左上x, 左上y, 右下x, 右下y) 格式转换成 matplotlib 格式: - # ((左上x, 左上y), 宽, 高) + # 将边界框(左上x,左上y,右下x,右下y)格式转换成matplotlib格式: + # ((左上x,左上y),宽,高) return d2l.plt.Rectangle( xy=(bbox[0], bbox[1]), width=bbox[2]-bbox[0], height=bbox[3]-bbox[1], fill=False, edgecolor=color, linewidth=2) def multibox_prior(data, sizes, ratios): - """生成以每个像素为中心具有不同形状的锚框。 + """生成以每个像素为中心具有不同形状的锚框 Defined in :numref:`sec_anchor`""" in_height, in_width = data.shape[-2:] @@ -991,8 +1480,8 @@ def multibox_prior(data, sizes, ratios): # 为了将锚点移动到像素的中心,需要设置偏移量。 # 因为一个像素的的高为1且宽为1,我们选择偏移我们的中心0.5 offset_h, offset_w = 0.5, 0.5 - steps_h = 1.0 / in_height # Scaled steps in y-axis - steps_w = 1.0 / in_width # Scaled steps in x-axis + steps_h = 1.0 / in_height # 在y轴上缩放步长 + steps_w = 1.0 / in_width # 在x轴上缩放步长 # 生成锚框的所有中心点 center_h = (d2l.arange(in_height, ctx=device) + offset_h) * steps_h @@ -1001,10 +1490,10 @@ def multibox_prior(data, sizes, ratios): shift_x, shift_y = shift_x.reshape(-1), shift_y.reshape(-1) # 生成“boxes_per_pixel”个高和宽, - # 之后用于创建锚框的四角坐标 (xmin, xmax, ymin, ymax) + # 之后用于创建锚框的四角坐标(xmin,xmax,ymin,ymax) w = np.concatenate((size_tensor * np.sqrt(ratio_tensor[0]), sizes[0] * np.sqrt(ratio_tensor[1:]))) \ - * in_height / in_width # Handle rectangular inputs + * in_height / in_width # 处理矩形输入 h = np.concatenate((size_tensor / np.sqrt(ratio_tensor[0]), sizes[0] / np.sqrt(ratio_tensor[1:]))) # 除以2来获得半高和半宽 @@ -1019,7 +1508,7 @@ def multibox_prior(data, sizes, ratios): return np.expand_dims(output, axis=0) def show_bboxes(axes, bboxes, labels=None, colors=None): - """显示所有边界框。 + """显示所有边界框 Defined in :numref:`sec_anchor`""" def _make_list(obj, default_values=None): @@ -1042,35 +1531,35 @@ def _make_list(obj, default_values=None): bbox=dict(facecolor=color, lw=0)) def box_iou(boxes1, boxes2): - """计算两个锚框或边界框列表中成对的交并比。 + """计算两个锚框或边界框列表中成对的交并比 Defined in :numref:`sec_anchor`""" box_area = lambda boxes: ((boxes[:, 2] - boxes[:, 0]) * (boxes[:, 3] - boxes[:, 1])) - # `boxes1`, `boxes2`, `areas1`, `areas2`的形状: - # `boxes1`:(boxes1的数量, 4), - # `boxes2`:(boxes2的数量, 4), + # `boxes1`,`boxes2`,`areas1`,`areas2`的形状: + # `boxes1`:(boxes1的数量,4), + # `boxes2`:(boxes2的数量,4), # `areas1`:(boxes1的数量,), # `areas2`:(boxes2的数量,) areas1 = box_area(boxes1) areas2 = box_area(boxes2) - # `inter_upperlefts`, `inter_lowerrights`, `inters`的形状: - # (boxes1的数量, boxes2的数量, 2) + # `inter_upperlefts`,`inter_lowerrights`,`inters`的形状: + # (boxes1的数量,boxes2的数量,2) inter_upperlefts = np.maximum(boxes1[:, None, :2], boxes2[:, :2]) inter_lowerrights = np.minimum(boxes1[:, None, 2:], boxes2[:, 2:]) inters = (inter_lowerrights - inter_upperlefts).clip(min=0) - # `inter_areas` and `union_areas`的形状: (boxes1的数量, boxes2的数量) + # `inter_areas`and`union_areas`的形状:(boxes1的数量,boxes2的数量) inter_areas = inters[:, :, 0] * inters[:, :, 1] union_areas = areas1[:, None] + areas2 - inter_areas return inter_areas / union_areas def assign_anchor_to_bbox(ground_truth, anchors, device, iou_threshold=0.5): - """将最接近的真实边界框分配给锚框。 + """将最接近的真实边界框分配给锚框 Defined in :numref:`sec_anchor`""" num_anchors, num_gt_boxes = anchors.shape[0], ground_truth.shape[0] - # 位于第i行和第j列的元素 x_ij 是锚框i和真实边界框j的IoU + # 位于第i行和第j列的元素x_ij是锚框i和真实边界框j的IoU jaccard = box_iou(anchors, ground_truth) # 对于每个锚框,分配的真实边界框的张量 anchors_bbox_map = np.full((num_anchors,), -1, dtype=np.int32, ctx=device) @@ -1091,7 +1580,7 @@ def assign_anchor_to_bbox(ground_truth, anchors, device, iou_threshold=0.5): return anchors_bbox_map def offset_boxes(anchors, assigned_bb, eps=1e-6): - """对锚框偏移量的转换。 + """对锚框偏移量的转换 Defined in :numref:`subsec_labeling-anchor-boxes`""" c_anc = d2l.box_corner_to_center(anchors) @@ -1102,7 +1591,7 @@ def offset_boxes(anchors, assigned_bb, eps=1e-6): return offset def multibox_target(anchors, labels): - """使用真实边界框标记锚框。 + """使用真实边界框标记锚框 Defined in :numref:`subsec_labeling-anchor-boxes`""" batch_size, anchors = labels.shape[0], anchors.squeeze(0) @@ -1135,7 +1624,7 @@ def multibox_target(anchors, labels): return (bbox_offset, bbox_mask, class_labels) def offset_inverse(anchors, offset_preds): - """根据带有预测偏移量的锚框来预测边界框。 + """根据带有预测偏移量的锚框来预测边界框 Defined in :numref:`subsec_labeling-anchor-boxes`""" anc = d2l.box_corner_to_center(anchors) @@ -1146,7 +1635,7 @@ def offset_inverse(anchors, offset_preds): return predicted_bbox def nms(boxes, scores, iou_threshold): - """对预测边界框的置信度进行排序。 + """对预测边界框的置信度进行排序 Defined in :numref:`subsec_predicting-bounding-boxes-nms`""" B = scores.argsort()[::-1] @@ -1163,7 +1652,7 @@ def nms(boxes, scores, iou_threshold): def multibox_detection(cls_probs, offset_preds, anchors, nms_threshold=0.5, pos_threshold=0.009999999): - """使用非极大值抑制来预测边界框。 + """使用非极大值抑制来预测边界框 Defined in :numref:`subsec_predicting-bounding-boxes-nms`""" device, batch_size = cls_probs.ctx, cls_probs.shape[0] @@ -1176,7 +1665,7 @@ def multibox_detection(cls_probs, offset_preds, anchors, nms_threshold=0.5, predicted_bb = offset_inverse(anchors, offset_pred) keep = nms(predicted_bb, conf, nms_threshold) - # 找到所有的 non_keep 索引,并将类设置为背景 + # 找到所有的non_keep索引,并将类设置为背景 all_idx = np.arange(num_anchors, dtype=np.int32, ctx=device) combined = d2l.concat((keep, all_idx)) unique, counts = np.unique(combined, return_counts=True) @@ -1185,7 +1674,7 @@ def multibox_detection(cls_probs, offset_preds, anchors, nms_threshold=0.5, class_id[non_keep] = -1 class_id = class_id[all_id_sorted].astype('float32') conf, predicted_bb = conf[all_id_sorted], predicted_bb[all_id_sorted] - # `pos_threshold` 是一个用于非背景预测的阈值 + # `pos_threshold`是一个用于非背景预测的阈值 below_min_idx = (conf < pos_threshold) class_id[below_min_idx] = -1 conf[below_min_idx] = 1 - conf[below_min_idx] @@ -1200,7 +1689,7 @@ def multibox_detection(cls_probs, offset_preds, anchors, nms_threshold=0.5, '5de26c8fce5ccdea9f91267273464dc968d20d72') def read_data_bananas(is_train=True): - """读取香蕉检测数据集中的图像和标签。 + """读取香蕉检测数据集中的图像和标签 Defined in :numref:`sec_object-detection-dataset`""" data_dir = d2l.download_extract('banana-detection') @@ -1213,14 +1702,13 @@ def read_data_bananas(is_train=True): images.append(image.imread( os.path.join(data_dir, 'bananas_train' if is_train else 'bananas_val', 'images', f'{img_name}'))) - # Here `target` contains (class, upper-left x, upper-left y, - # lower-right x, lower-right y), where all the images have the same - # banana class (index 0) + # 这里的`target`包含(类别,左上角x,左上角y,右下角x,右下角y), + # 其中所有图像都具有相同的香蕉类(索引为0) targets.append(list(target)) return images, np.expand_dims(np.array(targets), 1) / 256 class BananasDataset(gluon.data.Dataset): - """一个用于加载香蕉检测数据集的自定义数据集。 + """一个用于加载香蕉检测数据集的自定义数据集 Defined in :numref:`sec_object-detection-dataset`""" def __init__(self, is_train): @@ -1236,7 +1724,7 @@ def __len__(self): return len(self.features) def load_data_bananas(batch_size): - """加载香蕉检测数据集。 + """加载香蕉检测数据集 Defined in :numref:`sec_object-detection-dataset`""" train_iter = gluon.data.DataLoader(BananasDataset(is_train=True), @@ -1249,7 +1737,7 @@ def load_data_bananas(batch_size): '4e443f8a2eca6b1dac8a6c57641b67dd40621a49') def read_voc_images(voc_dir, is_train=True): - """读取所有VOC图像并标注。 + """读取所有VOC图像并标注 Defined in :numref:`sec_semantic_segmentation`""" txt_fname = os.path.join(voc_dir, 'ImageSets', 'Segmentation', @@ -1277,7 +1765,7 @@ def read_voc_images(voc_dir, is_train=True): 'potted plant', 'sheep', 'sofa', 'train', 'tv/monitor'] def voc_colormap2label(): - """构建从RGB到VOC类别索引的映射。 + """构建从RGB到VOC类别索引的映射 Defined in :numref:`sec_semantic_segmentation`""" colormap2label = np.zeros(256 ** 3) @@ -1287,7 +1775,7 @@ def voc_colormap2label(): return colormap2label def voc_label_indices(colormap, colormap2label): - """将VOC标签中的RGB值映射到它们的类别索引。 + """将VOC标签中的RGB值映射到它们的类别索引 Defined in :numref:`sec_semantic_segmentation`""" colormap = colormap.astype(np.int32) @@ -1296,7 +1784,7 @@ def voc_label_indices(colormap, colormap2label): return colormap2label[idx] def voc_rand_crop(feature, label, height, width): - """随机裁剪特征和标签图像。 + """随机裁剪特征和标签图像 Defined in :numref:`sec_semantic_segmentation`""" feature, rect = image.random_crop(feature, (width, height)) @@ -1304,7 +1792,7 @@ def voc_rand_crop(feature, label, height, width): return feature, label class VOCSegDataset(gluon.data.Dataset): - """一个用于加载VOC数据集的自定义数据集。 + """一个用于加载VOC数据集的自定义数据集 Defined in :numref:`sec_semantic_segmentation`""" def __init__(self, is_train, crop_size, voc_dir): @@ -1336,7 +1824,7 @@ def __len__(self): return len(self.features) def load_data_voc(batch_size, crop_size): - """加载VOC语义分割数据集。 + """加载VOC语义分割数据集 Defined in :numref:`sec_semantic_segmentation`""" voc_dir = d2l.download_extract('voc2012', os.path.join( @@ -1354,17 +1842,17 @@ def load_data_voc(batch_size, crop_size): '2068874e4b9a9f0fb07ebe0ad2b29754449ccacd') def read_csv_labels(fname): - """读取 `fname` 来给标签字典返回一个文件名。 + """读取`fname`来给标签字典返回一个文件名 Defined in :numref:`sec_kaggle_cifar10`""" with open(fname, 'r') as f: - # 跳过文件头行 (列名) + # 跳过文件头行(列名) lines = f.readlines()[1:] tokens = [l.rstrip().split(',') for l in lines] return dict(((name, label) for name, label in tokens)) def copyfile(filename, target_dir): - """将文件复制到目标目录。 + """将文件复制到目标目录 Defined in :numref:`sec_kaggle_cifar10`""" os.makedirs(target_dir, exist_ok=True) @@ -1374,9 +1862,9 @@ def reorg_train_valid(data_dir, labels, valid_ratio): """将验证集从原始的训练集中拆分出来 Defined in :numref:`sec_kaggle_cifar10`""" - # 训练数据集中示例最少的类别中的示例数 + # 训练数据集中样本最少的类别中的样本数 n = collections.Counter(labels.values()).most_common()[-1][1] - # 验证集中每个类别的示例数 + # 验证集中每个类别的样本数 n_valid_per_label = max(1, math.floor(n * valid_ratio)) label_count = {} for train_file in os.listdir(os.path.join(data_dir, 'train')): @@ -1409,20 +1897,20 @@ def reorg_test(data_dir): '319d85e578af0cdc590547f26231e4e31cdf1e42') def read_ptb(): - """将PTB数据集加载到文本行的列表中。 + """将PTB数据集加载到文本行的列表中 Defined in :numref:`sec_word2vec_data`""" data_dir = d2l.download_extract('ptb') - # Read the training set. + # Readthetrainingset. with open(os.path.join(data_dir, 'ptb.train.txt')) as f: raw_text = f.read() return [line.split() for line in raw_text.split('\n')] def subsample(sentences, vocab): - """下采样高频词。 + """下采样高频词 Defined in :numref:`sec_word2vec_data`""" - # 排除未知词元 '' + # 排除未知词元'' sentences = [[token for token in line if vocab[token] != vocab.unk] for line in sentences] counter = d2l.count_corpus(sentences) @@ -1437,7 +1925,7 @@ def keep(token): counter) def get_centers_and_contexts(corpus, max_window_size): - """返回跳元模型中的中心词和上下文词。 + """返回跳元模型中的中心词和上下文词 Defined in :numref:`sec_word2vec_data`""" centers, contexts = [], [] @@ -1456,7 +1944,7 @@ def get_centers_and_contexts(corpus, max_window_size): return centers, contexts class RandomGenerator: - """根据n个采样权重在 {1, ..., n} 中随机抽取。""" + """根据n个采样权重在{1,...,n}中随机抽取""" def __init__(self, sampling_weights): """Defined in :numref:`sec_word2vec_data`""" # Exclude @@ -1474,8 +1962,11 @@ def draw(self): self.i += 1 return self.candidates[self.i - 1] +generator = RandomGenerator([2, 3, 4]) +[generator.draw() for _ in range(10)] + def get_negatives(all_contexts, vocab, counter, K): - """返回负采样中的噪声词。 + """返回负采样中的噪声词 Defined in :numref:`sec_word2vec_data`""" # 索引为1、2、...(索引0是词表中排除的未知标记) @@ -1493,7 +1984,7 @@ def get_negatives(all_contexts, vocab, counter, K): return all_negatives def batchify(data): - """返回带有负采样的跳元模型的小批量样本。 + """返回带有负采样的跳元模型的小批量样本 Defined in :numref:`sec_word2vec_data`""" max_len = max(len(c) + len(n) for _, c, n in data) @@ -1501,14 +1992,15 @@ def batchify(data): for center, context, negative in data: cur_len = len(context) + len(negative) centers += [center] - contexts_negatives += [context + negative + [0] * (max_len - cur_len)] + contexts_negatives += \ + [context + negative + [0] * (max_len - cur_len)] masks += [[1] * cur_len + [0] * (max_len - cur_len)] labels += [[1] * len(context) + [0] * (max_len - len(context))] return (d2l.reshape(d2l.tensor(centers), (-1, 1)), d2l.tensor( contexts_negatives), d2l.tensor(masks), d2l.tensor(labels)) def load_data_ptb(batch_size, max_window_size, num_noise_words): - """下载PTB数据集,然后将其加载到内存中。 + """下载PTB数据集,然后将其加载到内存中 Defined in :numref:`subsec_word2vec-minibatch-loading`""" sentences = read_ptb() @@ -1539,7 +2031,7 @@ def load_data_ptb(batch_size, max_window_size, num_noise_words): 'c1816da3821ae9f43899be655002f6c723e91b88') class TokenEmbedding: - """Token Embedding.""" + """GloVe嵌入""" def __init__(self, embedding_name): """Defined in :numref:`sec_synonyms`""" self.idx_to_token, self.idx_to_vec = self._load_embedding( @@ -1574,7 +2066,7 @@ def __len__(self): return len(self.idx_to_token) def get_tokens_and_segments(tokens_a, tokens_b=None): - """获取输入序列的词元及其片段索引。 + """获取输入序列的词元及其片段索引 Defined in :numref:`sec_bert`""" tokens = [''] + tokens_a + [''] @@ -1586,7 +2078,7 @@ def get_tokens_and_segments(tokens_a, tokens_b=None): return tokens, segments class BERTEncoder(nn.Block): - """BERT encoder. + """BERT编码器 Defined in :numref:`subsec_bert_input_rep`""" def __init__(self, vocab_size, num_hiddens, ffn_num_hiddens, num_heads, @@ -1611,7 +2103,7 @@ def forward(self, tokens, segments, valid_lens): return X class MaskLM(nn.Block): - """BERT的遮蔽语言模型任务 + """BERT的掩蔽语言模型任务 Defined in :numref:`subsec_bert_input_rep`""" def __init__(self, vocab_size, num_hiddens, **kwargs): @@ -1627,8 +2119,8 @@ def forward(self, X, pred_positions): pred_positions = pred_positions.reshape(-1) batch_size = X.shape[0] batch_idx = np.arange(0, batch_size) - # 假设`batch_size=2,`num_pred_positions`=3 - # 那么`batch_idx`是`np.array([0,0,0,1,1])` + # 假设batch_size=2,num_pred_positions=3 + # 那么batch_idx是np.array([0,0,0,1,1]) batch_idx = np.repeat(batch_idx, num_pred_positions) masked_X = X[batch_idx, pred_positions] masked_X = masked_X.reshape((batch_size, num_pred_positions, -1)) @@ -1644,7 +2136,7 @@ def __init__(self, **kwargs): self.output = nn.Dense(2) def forward(self, X): - # `X`的形状: (batch size, `num_hiddens`) + # X的形状:(batchsize,`num_hiddens`) return self.output(X) class BERTModel(nn.Block): @@ -1660,13 +2152,14 @@ def __init__(self, vocab_size, num_hiddens, ffn_num_hiddens, num_heads, self.mlm = MaskLM(vocab_size, num_hiddens) self.nsp = NextSentencePred() - def forward(self, tokens, segments, valid_lens=None, pred_positions=None): + def forward(self, tokens, segments, valid_lens=None, + pred_positions=None): encoded_X = self.encoder(tokens, segments, valid_lens) if pred_positions is not None: mlm_Y_hat = self.mlm(encoded_X, pred_positions) else: mlm_Y_hat = None - # 用于下一句预测的多层感知机分类器的隐藏层。0是“”标记的索引。 + # 用于下一句预测的多层感知机分类器的隐藏层,0是“”标记的索引 nsp_Y_hat = self.nsp(self.hidden(encoded_X[:, 0, :])) return encoded_X, mlm_Y_hat, nsp_Y_hat @@ -1766,7 +2259,7 @@ def _pad_bert_inputs(examples, max_len, vocab): max_len - len(token_ids)), dtype='int32')) all_segments.append(np.array(segments + [0] * ( max_len - len(segments)), dtype='int32')) - # `valid_lens` 不包括''的计数 + # `valid_lens`不包括''的计数 valid_lens.append(np.array(len(token_ids), dtype='float32')) all_pred_positions.append(np.array(pred_positions + [0] * ( max_num_mlm_preds - len(pred_positions)), dtype='int32')) @@ -1783,7 +2276,8 @@ def _pad_bert_inputs(examples, max_len, vocab): class _WikiTextDataset(gluon.data.Dataset): """Defined in :numref:`subsec_prepare_mlm_data`""" def __init__(self, paragraphs, max_len): - # 输入`paragraphs[i]`是代表段落的句子字符串列表;而输出`paragraphs[i]`是代表段落的句子列表,其中每个句子都是词元列表 + # 输入`paragraphs[i]`是代表段落的句子字符串列表; + # 而输出`paragraphs[i]`是代表段落的句子列表,其中每个句子都是词元列表 paragraphs = [d2l.tokenize( paragraph, token='word') for paragraph in paragraphs] sentences = [sentence for paragraph in paragraphs @@ -1815,7 +2309,7 @@ def __len__(self): return len(self.all_token_ids) def load_data_wiki(batch_size, max_len): - """加载WikiText-2数据集。 + """加载WikiText-2数据集 Defined in :numref:`subsec_prepare_mlm_data`""" num_workers = d2l.get_dataloader_workers() @@ -1861,7 +2355,7 @@ def _get_batch_loss_bert(net, loss, vocab_size, tokens_X_shards, '01ada507287d82875905620988597833ad4e0903') def read_imdb(data_dir, is_train): - """读取IMDb评论数据集文本序列和标签。 + """读取IMDb评论数据集文本序列和标签 Defined in :numref:`sec_sentiment`""" data, labels = [], [] @@ -1876,7 +2370,7 @@ def read_imdb(data_dir, is_train): return data, labels def load_data_imdb(batch_size, num_steps=500): - """返回数据迭代器和IMDb评论数据集的词表。 + """返回数据迭代器和IMDb评论数据集的词表 Defined in :numref:`sec_sentiment`""" data_dir = d2l.download_extract('aclImdb', 'aclImdb') @@ -1895,7 +2389,7 @@ def load_data_imdb(batch_size, num_steps=500): return train_iter, test_iter, vocab def predict_sentiment(net, vocab, sequence): - """预测文本序列的情感。 + """预测文本序列的情感 Defined in :numref:`sec_sentiment_rnn`""" sequence = np.array(vocab[sequence.split()], ctx=d2l.try_gpu()) @@ -1907,7 +2401,7 @@ def predict_sentiment(net, vocab, sequence): '9fcde07509c7e87ec61c640c1b2753d9041758e4') def read_snli(data_dir, is_train): - """将SNLI数据集解析为前提、假设和标签。 + """将SNLI数据集解析为前提、假设和标签 Defined in :numref:`sec_natural-language-inference-and-dataset`""" def extract_text(s): @@ -1923,12 +2417,13 @@ def extract_text(s): with open(file_name, 'r') as f: rows = [row.split('\t') for row in f.readlines()[1:]] premises = [extract_text(row[1]) for row in rows if row[0] in label_set] - hypotheses = [extract_text(row[2]) for row in rows if row[0] in label_set] + hypotheses = [extract_text(row[2]) for row in rows if row[0] \ + in label_set] labels = [label_set[row[0]] for row in rows if row[0] in label_set] return premises, hypotheses, labels class SNLIDataset(gluon.data.Dataset): - """用于加载SNLI数据集的自定义数据集。 + """用于加载SNLI数据集的自定义数据集 Defined in :numref:`sec_natural-language-inference-and-dataset`""" def __init__(self, dataset, num_steps, vocab=None): @@ -1936,8 +2431,8 @@ def __init__(self, dataset, num_steps, vocab=None): all_premise_tokens = d2l.tokenize(dataset[0]) all_hypothesis_tokens = d2l.tokenize(dataset[1]) if vocab is None: - self.vocab = d2l.Vocab(all_premise_tokens + all_hypothesis_tokens, - min_freq=5, reserved_tokens=['']) + self.vocab = d2l.Vocab(all_premise_tokens + \ + all_hypothesis_tokens, min_freq=5, reserved_tokens=['']) else: self.vocab = vocab self.premises = self._pad(all_premise_tokens) @@ -1957,7 +2452,7 @@ def __len__(self): return len(self.premises) def load_data_snli(batch_size, num_steps=50): - """下载SNLI数据集并返回数据迭代器和词表。 + """下载SNLI数据集并返回数据迭代器和词表 Defined in :numref:`sec_natural-language-inference-and-dataset`""" num_workers = d2l.get_dataloader_workers() @@ -1973,7 +2468,7 @@ def load_data_snli(batch_size, num_steps=50): return train_iter, test_iter, train_set.vocab def split_batch_multi_inputs(X, y, devices): - """将多输入'X'和'y'拆分到多个设备。 + """将多输入'X'和'y'拆分到多个设备 Defined in :numref:`sec_natural-language-inference-attention`""" X = list(zip(*[gluon.utils.split_and_load( @@ -1981,7 +2476,7 @@ def split_batch_multi_inputs(X, y, devices): return (X, gluon.utils.split_and_load(y, devices, even_split=False)) def predict_snli(net, vocab, premise, hypothesis): - """预测前提和假设之间的逻辑关系。 + """预测前提和假设之间的逻辑关系 Defined in :numref:`sec_natural-language-inference-attention`""" premise = np.array(vocab[premise], ctx=d2l.try_gpu()) diff --git a/d2l/tensorflow.py b/d2l/tensorflow.py index 92f49c1e2..b11d750f9 100644 --- a/d2l/tensorflow.py +++ b/d2l/tensorflow.py @@ -26,20 +26,20 @@ import tensorflow as tf def use_svg_display(): - """使用svg格式在Jupyter中显示绘图。 + """使用svg格式在Jupyter中显示绘图 Defined in :numref:`sec_calculus`""" display.set_matplotlib_formats('svg') def set_figsize(figsize=(3.5, 2.5)): - """设置matplotlib的图表大小。 + """设置matplotlib的图表大小 Defined in :numref:`sec_calculus`""" use_svg_display() d2l.plt.rcParams['figure.figsize'] = figsize def set_axes(axes, xlabel, ylabel, xlim, ylim, xscale, yscale, legend): - """设置matplotlib的轴。 + """设置matplotlib的轴 Defined in :numref:`sec_calculus`""" axes.set_xlabel(xlabel) @@ -55,7 +55,7 @@ def set_axes(axes, xlabel, ylabel, xlim, ylim, xscale, yscale, legend): def plot(X, Y=None, xlabel=None, ylabel=None, legend=None, xlim=None, ylim=None, xscale='linear', yscale='linear', fmts=('-', 'm--', 'g-.', 'r:'), figsize=(3.5, 2.5), axes=None): - """绘制数据点。 + """绘制数据点 Defined in :numref:`sec_calculus`""" if legend is None: @@ -64,7 +64,7 @@ def plot(X, Y=None, xlabel=None, ylabel=None, legend=None, xlim=None, set_figsize(figsize) axes = axes if axes else d2l.plt.gca() - # 如果 `X` 有一个轴,输出True + # 如果`X`有一个轴,输出True def has_one_axis(X): return (hasattr(X, "ndim") and X.ndim == 1 or isinstance(X, list) and not hasattr(X[0], "__len__")) @@ -86,35 +86,35 @@ def has_one_axis(X): set_axes(axes, xlabel, ylabel, xlim, ylim, xscale, yscale, legend) class Timer: - """记录多次运行时间。""" + """记录多次运行时间""" def __init__(self): """Defined in :numref:`subsec_linear_model`""" self.times = [] self.start() def start(self): - """启动计时器。""" + """启动计时器""" self.tik = time.time() def stop(self): - """停止计时器并将时间记录在列表中。""" + """停止计时器并将时间记录在列表中""" self.times.append(time.time() - self.tik) return self.times[-1] def avg(self): - """返回平均时间。""" + """返回平均时间""" return sum(self.times) / len(self.times) def sum(self): - """返回时间总和。""" + """返回时间总和""" return sum(self.times) def cumsum(self): - """返回累计时间。""" + """返回累计时间""" return np.array(self.times).cumsum().tolist() def synthetic_data(w, b, num_examples): - """生成 y = Xw + b + 噪声。 + """生成y=Xw+b+噪声 Defined in :numref:`sec_linear_scratch`""" X = d2l.zeros((num_examples, w.shape[0])) @@ -125,26 +125,26 @@ def synthetic_data(w, b, num_examples): return X, y def linreg(X, w, b): - """线性回归模型。 + """线性回归模型 Defined in :numref:`sec_linear_scratch`""" return d2l.matmul(X, w) + b def squared_loss(y_hat, y): - """均方损失。 + """均方损失 Defined in :numref:`sec_linear_scratch`""" return (y_hat - d2l.reshape(y, y_hat.shape)) ** 2 / 2 def sgd(params, grads, lr, batch_size): - """小批量随机梯度下降。 + """小批量随机梯度下降 Defined in :numref:`sec_linear_scratch`""" for param, grad in zip(params, grads): param.assign_sub(lr*grad/batch_size) def load_array(data_arrays, batch_size, is_train=True): - """构造一个TensorFlow数据迭代器。 + """构造一个TensorFlow数据迭代器 Defined in :numref:`sec_linear_concise`""" dataset = tf.data.Dataset.from_tensor_slices(data_arrays) @@ -154,7 +154,7 @@ def load_array(data_arrays, batch_size, is_train=True): return dataset def get_fashion_mnist_labels(labels): - """返回Fashion-MNIST数据集的文本标签。 + """返回Fashion-MNIST数据集的文本标签 Defined in :numref:`sec_fashion_mnist`""" text_labels = ['t-shirt', 'trouser', 'pullover', 'dress', 'coat', @@ -162,7 +162,7 @@ def get_fashion_mnist_labels(labels): return [text_labels[int(i)] for i in labels] def show_images(imgs, num_rows, num_cols, titles=None, scale=1.5): - """绘制图像列表。 + """绘制图像列表 Defined in :numref:`sec_fashion_mnist`""" figsize = (num_cols * scale, num_rows * scale) @@ -177,7 +177,7 @@ def show_images(imgs, num_rows, num_cols, titles=None, scale=1.5): return axes def load_data_fashion_mnist(batch_size, resize=None): - """下载Fashion-MNIST数据集,然后将其加载到内存中。 + """下载Fashion-MNIST数据集,然后将其加载到内存中 Defined in :numref:`sec_fashion_mnist`""" mnist_train, mnist_test = tf.keras.datasets.fashion_mnist.load_data() @@ -194,7 +194,7 @@ def load_data_fashion_mnist(batch_size, resize=None): batch_size).map(resize_fn)) def accuracy(y_hat, y): - """计算预测正确的数量。 + """计算预测正确的数量 Defined in :numref:`sec_softmax_scratch`""" if len(y_hat.shape) > 1 and y_hat.shape[1] > 1: @@ -203,7 +203,7 @@ def accuracy(y_hat, y): return float(d2l.reduce_sum(d2l.astype(cmp, y.dtype))) def evaluate_accuracy(net, data_iter): - """计算在指定数据集上模型的精度。 + """计算在指定数据集上模型的精度 Defined in :numref:`sec_softmax_scratch`""" metric = Accumulator(2) # 正确预测数、预测总数 @@ -212,7 +212,7 @@ def evaluate_accuracy(net, data_iter): return metric[0] / metric[1] class Accumulator: - """在`n`个变量上累加。""" + """在`n`个变量上累加""" def __init__(self, n): """Defined in :numref:`sec_softmax_scratch`""" self.data = [0.0] * n @@ -227,7 +227,7 @@ def __getitem__(self, idx): return self.data[idx] def train_epoch_ch3(net, train_iter, loss, updater): - """训练模型一个迭代周期(定义见第3章)。 + """训练模型一个迭代周期(定义见第3章) Defined in :numref:`sec_softmax_scratch`""" # 训练损失总和、训练准确度总和、样本数 @@ -256,7 +256,7 @@ def train_epoch_ch3(net, train_iter, loss, updater): return metric[0] / metric[2], metric[1] / metric[2] class Animator: - """在动画中绘制数据。""" + """在动画中绘制数据""" def __init__(self, xlabel=None, ylabel=None, legend=None, xlim=None, ylim=None, xscale='linear', yscale='linear', fmts=('-', 'm--', 'g-.', 'r:'), nrows=1, ncols=1, @@ -297,7 +297,7 @@ def add(self, x, y): display.clear_output(wait=True) def train_ch3(net, train_iter, test_iter, loss, num_epochs, updater): - """训练模型(定义见第3章)。 + """训练模型(定义见第3章) Defined in :numref:`sec_softmax_scratch`""" animator = Animator(xlabel='epoch', xlim=[1, num_epochs], ylim=[0.3, 0.9], @@ -312,7 +312,7 @@ def train_ch3(net, train_iter, test_iter, loss, num_epochs, updater): assert test_acc <= 1 and test_acc > 0.7, test_acc class Updater(): - """用小批量随机梯度下降法更新参数。 + """用小批量随机梯度下降法更新参数 Defined in :numref:`sec_softmax_scratch`""" def __init__(self, params, lr): @@ -323,7 +323,7 @@ def __call__(self, batch_size, grads): d2l.sgd(self.params, grads, self.lr, batch_size) def predict_ch3(net, test_iter, n=6): - """预测标签(定义见第3章)。 + """预测标签(定义见第3章) Defined in :numref:`sec_softmax_scratch`""" for X, y in test_iter: @@ -335,10 +335,10 @@ def predict_ch3(net, test_iter, n=6): d2l.reshape(X[0:n], (n, 28, 28)), 1, n, titles=titles[0:n]) def evaluate_loss(net, data_iter, loss): - """评估给定数据集上模型的损失。 + """评估给定数据集上模型的损失 Defined in :numref:`sec_model_selection`""" - metric = d2l.Accumulator(2) # 损失的总和, 样本数量 + metric = d2l.Accumulator(2) # 损失的总和,样本数量 for X, y in data_iter: l = loss(net(X), y) metric.add(d2l.reduce_sum(l), d2l.size(l)) @@ -348,10 +348,10 @@ def evaluate_loss(net, data_iter, loss): DATA_URL = 'http://d2l-data.s3-accelerate.amazonaws.com/' def download(name, cache_dir=os.path.join('..', 'data')): - """下载一个DATA_HUB中的文件,返回本地文件名。 + """下载一个DATA_HUB中的文件,返回本地文件名 Defined in :numref:`sec_kaggle_house`""" - assert name in DATA_HUB, f"{name} 不存在于 {DATA_HUB}." + assert name in DATA_HUB, f"{name} 不存在于 {DATA_HUB}" url, sha1_hash = DATA_HUB[name] os.makedirs(cache_dir, exist_ok=True) fname = os.path.join(cache_dir, url.split('/')[-1]) @@ -364,7 +364,7 @@ def download(name, cache_dir=os.path.join('..', 'data')): break sha1.update(data) if sha1.hexdigest() == sha1_hash: - return fname # Hit cache + return fname # 命中缓存 print(f'正在从{url}下载{fname}...') r = requests.get(url, stream=True, verify=True) with open(fname, 'wb') as f: @@ -372,7 +372,7 @@ def download(name, cache_dir=os.path.join('..', 'data')): return fname def download_extract(name, folder=None): - """下载并解压zip/tar文件。 + """下载并解压zip/tar文件 Defined in :numref:`sec_kaggle_house`""" fname = download(name) @@ -383,12 +383,12 @@ def download_extract(name, folder=None): elif ext in ('.tar', '.gz'): fp = tarfile.open(fname, 'r') else: - assert False, '只有zip/tar文件可以被解压缩。' + assert False, '只有zip/tar文件可以被解压缩' fp.extractall(base_dir) return os.path.join(base_dir, folder) if folder else data_dir def download_all(): - """下载DATA_HUB中的所有文件。 + """下载DATA_HUB中的所有文件 Defined in :numref:`sec_kaggle_house`""" for name in DATA_HUB: @@ -403,7 +403,7 @@ def download_all(): 'fa19780a7b011d9b009e8bff8e99922a8ee2eb90') def try_gpu(i=0): - """如果存在,则返回gpu(i),否则返回cpu()。 + """如果存在,则返回gpu(i),否则返回cpu() Defined in :numref:`sec_use_gpu`""" if len(tf.config.experimental.list_physical_devices('GPU')) >= i + 1: @@ -411,7 +411,7 @@ def try_gpu(i=0): return tf.device('/CPU:0') def try_all_gpus(): - """返回所有可用的GPU,如果没有GPU,则返回[cpu(),]。 + """返回所有可用的GPU,如果没有GPU,则返回[cpu(),] Defined in :numref:`sec_use_gpu`""" num_gpus = len(tf.config.experimental.list_physical_devices('GPU')) @@ -419,7 +419,7 @@ def try_all_gpus(): return devices if devices else [tf.device('/CPU:0')] def corr2d(X, K): - """计算二维互相关运算。""" + """计算二维互相关运算""" h, w = K.shape Y = tf.Variable(tf.zeros((X.shape[0] - h + 1, X.shape[1] - w + 1))) for i in range(Y.shape[0]): @@ -429,7 +429,7 @@ def corr2d(X, K): return Y class TrainCallback(tf.keras.callbacks.Callback): - """一个以可视化的训练进展的回调。 + """一个以可视化的训练进展的回调 Defined in :numref:`sec_lenet`""" def __init__(self, net, train_iter, test_iter, num_epochs, device_name): @@ -462,7 +462,7 @@ def on_epoch_end(self, epoch, logs): f'{str(self.device_name)}') def train_ch6(net_fn, train_iter, test_iter, num_epochs, lr, device): - """用GPU训练模型(在第六章定义)。 + """用GPU训练模型(在第六章定义) Defined in :numref:`sec_lenet`""" device_name = device._device_name @@ -503,7 +503,7 @@ def call(self, X): '090b5e7e70c295757f55df93cb0a180b9691891a') def read_time_machine(): - """Load the time machine dataset into a list of text lines. + """将时间机器数据集加载到文本行的列表中 Defined in :numref:`sec_text_preprocessing`""" with open(d2l.download('time_machine'), 'r') as f: @@ -511,7 +511,7 @@ def read_time_machine(): return [re.sub('[^A-Za-z]+', ' ', line).strip().lower() for line in lines] def tokenize(lines, token='word'): - """将文本行拆分为单词或字符词元。 + """将文本行拆分为单词或字符词元 Defined in :numref:`sec_text_preprocessing`""" if token == 'word': @@ -531,16 +531,19 @@ def __init__(self, tokens=None, min_freq=0, reserved_tokens=None): reserved_tokens = [] # 按出现频率排序 counter = count_corpus(tokens) - self.token_freqs = sorted(counter.items(), key=lambda x: x[1], - reverse=True) + self._token_freqs = sorted(counter.items(), key=lambda x: x[1], + reverse=True) # 未知词元的索引为0 - self.unk, uniq_tokens = 0, [''] + reserved_tokens - uniq_tokens += [token for token, freq in self.token_freqs - if freq >= min_freq and token not in uniq_tokens] + self.idx_to_token = [''] + reserved_tokens + self.token_to_idx = {token: idx + for idx, token in enumerate(self.idx_to_token)} self.idx_to_token, self.token_to_idx = [], dict() - for token in uniq_tokens: - self.idx_to_token.append(token) - self.token_to_idx[token] = len(self.idx_to_token) - 1 + for token, freq in self._token_freqs: + if freq < min_freq: + break + if token not in self.token_to_idx: + self.idx_to_token.append(token) + self.token_to_idx[token] = len(self.idx_to_token) - 1 def __len__(self): return len(self.idx_to_token) @@ -555,18 +558,26 @@ def to_tokens(self, indices): return self.idx_to_token[indices] return [self.idx_to_token[index] for index in indices] + @property + def unk(self): # 未知词元的索引为0 + return 0 + + @property + def token_freqs(self): + return self._token_freqs + def count_corpus(tokens): - """统计词元的频率。 + """统计词元的频率 Defined in :numref:`sec_text_preprocessing`""" - # 这里的 `tokens` 是 1D 列表或 2D 列表 + # 这里的`tokens`是1D列表或2D列表 if len(tokens) == 0 or isinstance(tokens[0], list): - # 将词元列表展平成使用词元填充的一个列表 + # 将词元列表展平成一个列表 tokens = [token for line in tokens for token in line] return collections.Counter(tokens) def load_corpus_time_machine(max_tokens=-1): - """返回时光机器数据集的词元索引列表和词表。 + """返回时光机器数据集的词元索引列表和词表 Defined in :numref:`sec_text_preprocessing`""" lines = read_time_machine() @@ -580,10 +591,10 @@ def load_corpus_time_machine(max_tokens=-1): return corpus, vocab def seq_data_iter_random(corpus, batch_size, num_steps): - """使用随机抽样生成一个小批量子序列。 + """使用随机抽样生成一个小批量子序列 Defined in :numref:`sec_language_model`""" - # 从随机偏移量开始对序列进行分区,随机范围包括`num_steps - 1` + # 从随机偏移量开始对序列进行分区,随机范围包括`num_steps-1` corpus = corpus[random.randint(0, num_steps - 1):] # 减去1,是因为我们需要考虑标签 num_subseqs = (len(corpus) - 1) // num_steps @@ -606,7 +617,7 @@ def data(pos): yield d2l.tensor(X), d2l.tensor(Y) def seq_data_iter_sequential(corpus, batch_size, num_steps): - """使用顺序分区生成一个小批量子序列。 + """使用顺序分区生成一个小批量子序列 Defined in :numref:`sec_language_model`""" # 从随机偏移量开始划分序列 @@ -623,7 +634,7 @@ def seq_data_iter_sequential(corpus, batch_size, num_steps): yield X, Y class SeqDataLoader: - """加载序列数据的迭代器。""" + """加载序列数据的迭代器""" def __init__(self, batch_size, num_steps, use_random_iter, max_tokens): """Defined in :numref:`sec_language_model`""" if use_random_iter: @@ -638,7 +649,7 @@ def __iter__(self): def load_data_time_machine(batch_size, num_steps, use_random_iter=False, max_tokens=10000): - """返回时光机器数据集的迭代器和词表。 + """返回时光机器数据集的迭代器和词表 Defined in :numref:`sec_language_model`""" data_iter = SeqDataLoader( @@ -648,92 +659,95 @@ def load_data_time_machine(batch_size, num_steps, class RNNModelScratch: """从零开始实现的循环神经网络模型""" def __init__(self, vocab_size, num_hiddens, - init_state, forward_fn): + init_state, forward_fn, get_params): """Defined in :numref:`sec_rnn_scratch`""" self.vocab_size, self.num_hiddens = vocab_size, num_hiddens self.init_state, self.forward_fn = init_state, forward_fn + self.trainable_variables = get_params(vocab_size, num_hiddens) - def __call__(self, X, state, params): + def __call__(self, X, state): X = tf.one_hot(tf.transpose(X), self.vocab_size) X = tf.cast(X, tf.float32) - return self.forward_fn(X, state, params) + return self.forward_fn(X, state, self.trainable_variables) - def begin_state(self, batch_size): + def begin_state(self, batch_size, *args, **kwargs): return self.init_state(batch_size, self.num_hiddens) -def predict_ch8(prefix, num_preds, net, vocab, params): - """在`prefix`后面生成新字符。 +def predict_ch8(prefix, num_preds, net, vocab): + """在`prefix`后面生成新字符 Defined in :numref:`sec_rnn_scratch`""" - state = net.begin_state(batch_size=1) + state = net.begin_state(batch_size=1, dtype=tf.float32) outputs = [vocab[prefix[0]]] - get_input = lambda: d2l.reshape(d2l.tensor([outputs[-1]]), (1, 1)).numpy() + get_input = lambda: d2l.reshape(d2l.tensor([outputs[-1]]), + (1, 1)).numpy() for y in prefix[1:]: # 预热期 - _, state = net(get_input(), state, params) + _, state = net(get_input(), state) outputs.append(vocab[y]) for _ in range(num_preds): # 预测`num_preds`步 - y, state = net(get_input(), state, params) + y, state = net(get_input(), state) outputs.append(int(y.numpy().argmax(axis=1).reshape(1))) return ''.join([vocab.idx_to_token[i] for i in outputs]) def grad_clipping(grads, theta): - """裁剪梯度。 + """裁剪梯度 Defined in :numref:`sec_rnn_scratch`""" theta = tf.constant(theta, dtype=tf.float32) + new_grad = [] + for grad in grads: + if isinstance(grad, tf.IndexedSlices): + new_grad.append(tf.convert_to_tensor(grad)) + else: + new_grad.append(grad) norm = tf.math.sqrt(sum((tf.reduce_sum(grad ** 2)).numpy() - for grad in grads)) + for grad in new_grad)) norm = tf.cast(norm, tf.float32) - new_grad = [] if tf.greater(norm, theta): - for grad in grads: - new_grad.append(grad * theta / norm) + for i, grad in enumerate(new_grad): + new_grad[i] = grad * theta / norm else: - for grad in grads: - new_grad.append(grad) + new_grad = new_grad return new_grad -def train_epoch_ch8(net, train_iter, loss, updater, params, use_random_iter): - """训练模型一个迭代周期(定义见第8章)。 +def train_epoch_ch8(net, train_iter, loss, updater, use_random_iter): + """训练模型一个迭代周期(定义见第8章) Defined in :numref:`sec_rnn_scratch`""" state, timer = None, d2l.Timer() - metric = d2l.Accumulator(2) # 训练损失之和, 词元数量 + metric = d2l.Accumulator(2) # 训练损失之和,词元数量 for X, Y in train_iter: if state is None or use_random_iter: # 在第一次迭代或使用随机抽样时初始化`state` - state = net.begin_state(batch_size=X.shape[0]) + state = net.begin_state(batch_size=X.shape[0], dtype=tf.float32) with tf.GradientTape(persistent=True) as g: - g.watch(params) - y_hat, state= net(X, state, params) + y_hat, state = net(X, state) y = d2l.reshape(tf.transpose(Y), (-1)) l = loss(y, y_hat) + params = net.trainable_variables grads = g.gradient(l, params) grads = grad_clipping(grads, 1) updater.apply_gradients(zip(grads, params)) - # Keras默认返回一个批量中的平均损失 - # l_sum = l * float(d2l.size(y)) if isinstance( - # loss, tf.keras.losses.Loss) else tf.reduce_sum(l) metric.add(l * d2l.size(y), d2l.size(y)) return math.exp(metric[0] / metric[1]), metric[1] / timer.stop() -def train_ch8(net, train_iter, vocab, num_hiddens, lr, num_epochs, strategy, +def train_ch8(net, train_iter, vocab, lr, num_epochs, strategy, use_random_iter=False): - """训练模型(定义见第8章)。 + """训练模型(定义见第8章) Defined in :numref:`sec_rnn_scratch`""" with strategy.scope(): - params = get_params(len(vocab), num_hiddens) - loss = tf.keras.losses.SparseCategoricalCrossentropy(from_logits=True) + loss = tf.keras.losses.SparseCategoricalCrossentropy( + from_logits=True) updater = tf.keras.optimizers.SGD(lr) animator = d2l.Animator(xlabel='epoch', ylabel='perplexity', legend=['train'], xlim=[10, num_epochs]) - predict = lambda prefix: predict_ch8(prefix, 50, net, vocab, params) + predict = lambda prefix: predict_ch8(prefix, 50, net, vocab) # 训练和预测 for epoch in range(num_epochs): - ppl, speed = train_epoch_ch8( - net, train_iter, loss, updater, params, use_random_iter) + ppl, speed = train_epoch_ch8(net, train_iter, loss, updater, + use_random_iter) if (epoch + 1) % 10 == 0: print(predict('time traveller')) animator.add(epoch + 1, [ppl]) @@ -742,15 +756,528 @@ def train_ch8(net, train_iter, vocab, num_hiddens, lr, num_epochs, strategy, print(predict('time traveller')) print(predict('traveller')) +class RNNModel(tf.keras.layers.Layer): + """Defined in :numref:`sec_rnn-concise`""" + def __init__(self, rnn_layer, vocab_size, **kwargs): + super(RNNModel, self).__init__(**kwargs) + self.rnn = rnn_layer + self.vocab_size = vocab_size + self.dense = tf.keras.layers.Dense(vocab_size) + + def call(self, inputs, state): + X = tf.one_hot(tf.transpose(inputs), self.vocab_size) + # rnn返回两个以上的值 + Y, *state = self.rnn(X, state) + output = self.dense(tf.reshape(Y, (-1, Y.shape[-1]))) + return output, state + + def begin_state(self, *args, **kwargs): + return self.rnn.cell.get_initial_state(*args, **kwargs) + +d2l.DATA_HUB['fra-eng'] = (d2l.DATA_URL + 'fra-eng.zip', + '94646ad1522d915e7b0f9296181140edcf86a4f5') + +def read_data_nmt(): + """载入“英语-法语”数据集 + + Defined in :numref:`sec_machine_translation`""" + data_dir = d2l.download_extract('fra-eng') + with open(os.path.join(data_dir, 'fra.txt'), 'r', + encoding='utf-8') as f: + return f.read() + +def preprocess_nmt(text): + """预处理“英语-法语”数据集 + + Defined in :numref:`sec_machine_translation`""" + def no_space(char, prev_char): + return char in set(',.!?') and prev_char != ' ' + + # 使用空格替换不间断空格 + # 使用小写字母替换大写字母 + text = text.replace('\u202f', ' ').replace('\xa0', ' ').lower() + # 在单词和标点符号之间插入空格 + out = [' ' + char if i > 0 and no_space(char, text[i - 1]) else char + for i, char in enumerate(text)] + return ''.join(out) + +def tokenize_nmt(text, num_examples=None): + """词元化“英语-法语”数据数据集 + + Defined in :numref:`sec_machine_translation`""" + source, target = [], [] + for i, line in enumerate(text.split('\n')): + if num_examples and i > num_examples: + break + parts = line.split('\t') + if len(parts) == 2: + source.append(parts[0].split(' ')) + target.append(parts[1].split(' ')) + return source, target + +def truncate_pad(line, num_steps, padding_token): + """截断或填充文本序列 + + Defined in :numref:`sec_machine_translation`""" + if len(line) > num_steps: + return line[:num_steps] # 截断 + return line + [padding_token] * (num_steps - len(line)) # 填充 + +def build_array_nmt(lines, vocab, num_steps): + """将机器翻译的文本序列转换成小批量 + + Defined in :numref:`subsec_mt_data_loading`""" + lines = [vocab[l] for l in lines] + lines = [l + [vocab['']] for l in lines] + array = d2l.tensor([truncate_pad( + l, num_steps, vocab['']) for l in lines]) + valid_len = d2l.reduce_sum( + d2l.astype(array != vocab[''], d2l.int32), 1) + return array, valid_len + +def load_data_nmt(batch_size, num_steps, num_examples=600): + """返回翻译数据集的迭代器和词表 + + Defined in :numref:`subsec_mt_data_loading`""" + text = preprocess_nmt(read_data_nmt()) + source, target = tokenize_nmt(text, num_examples) + src_vocab = d2l.Vocab(source, min_freq=2, + reserved_tokens=['', '', '']) + tgt_vocab = d2l.Vocab(target, min_freq=2, + reserved_tokens=['', '', '']) + src_array, src_valid_len = build_array_nmt(source, src_vocab, num_steps) + tgt_array, tgt_valid_len = build_array_nmt(target, tgt_vocab, num_steps) + data_arrays = (src_array, src_valid_len, tgt_array, tgt_valid_len) + data_iter = d2l.load_array(data_arrays, batch_size) + return data_iter, src_vocab, tgt_vocab + +class Encoder(tf.keras.layers.Layer): + """编码器-解码器架构的基本编码器接口""" + def __init__(self, **kwargs): + super(Encoder, self).__init__(**kwargs) + + def call(self, X, *args, **kwargs): + raise NotImplementedError + +class Decoder(tf.keras.layers.Layer): + """编码器-解码器架构的基本解码器接口 + + Defined in :numref:`sec_encoder-decoder`""" + def __init__(self, **kwargs): + super(Decoder, self).__init__(**kwargs) + + def init_state(self, enc_outputs, *args): + raise NotImplementedError + + def call(self, X, state, **kwargs): + raise NotImplementedError + +class EncoderDecoder(tf.keras.Model): + """编码器-解码器架构的基类 + + Defined in :numref:`sec_encoder-decoder`""" + def __init__(self, encoder, decoder, **kwargs): + super(EncoderDecoder, self).__init__(**kwargs) + self.encoder = encoder + self.decoder = decoder + + def call(self, enc_X, dec_X, *args, **kwargs): + enc_outputs = self.encoder(enc_X, *args, **kwargs) + dec_state = self.decoder.init_state(enc_outputs, *args) + return self.decoder(dec_X, dec_state, **kwargs) + +class Seq2SeqEncoder(d2l.Encoder): + """用于序列到序列学习的循环神经网络编码器 + + Defined in :numref:`sec_seq2seq`""" + def __init__(self, vocab_size, embed_size, num_hiddens, num_layers, dropout=0, **kwargs): + super().__init__(*kwargs) + # 嵌入层 + self.embedding = tf.keras.layers.Embedding(vocab_size, embed_size) + self.rnn = tf.keras.layers.RNN(tf.keras.layers.StackedRNNCells( + [tf.keras.layers.GRUCell(num_hiddens, dropout=dropout) + for _ in range(num_layers)]), return_sequences=True, + return_state=True) + + def call(self, X, *args, **kwargs): + # 输入'X'的形状:(`batch_size`,`num_steps`) + # 输出'X'的形状:(`batch_size`,`num_steps`,`embed_size`) + X = self.embedding(X) + output = self.rnn(X, **kwargs) + state = output[1:] + return output[0], state + +def sequence_mask(X, valid_len, value=0): + """在序列中屏蔽不相关的项 + + Defined in :numref:`sec_seq2seq_decoder`""" + maxlen = X.shape[1] + mask = tf.range(start=0, limit=maxlen, dtype=tf.float32)[ + None, :] < tf.cast(valid_len[:, None], dtype=tf.float32) + + if len(X.shape) == 3: + return tf.where(tf.expand_dims(mask, axis=-1), X, value) + else: + return tf.where(mask, X, value) + +class MaskedSoftmaxCELoss(tf.keras.losses.Loss): + """带遮蔽的softmax交叉熵损失函数 + + Defined in :numref:`sec_seq2seq_decoder`""" + def __init__(self, valid_len): + super().__init__(reduction='none') + self.valid_len = valid_len + + # `pred`的形状:(`batch_size`,`num_steps`,`vocab_size`) + # `label`的形状:(`batch_size`,`num_steps`) + # `valid_len`的形状:(`batch_size`,) + def call(self, label, pred): + weights = tf.ones_like(label, dtype=tf.float32) + weights = sequence_mask(weights, self.valid_len) + label_one_hot = tf.one_hot(label, depth=pred.shape[-1]) + unweighted_loss = tf.keras.losses.CategoricalCrossentropy( + from_logits=True, reduction='none')(label_one_hot, pred) + weighted_loss = tf.reduce_mean((unweighted_loss*weights), axis=1) + return weighted_loss + +def train_seq2seq(net, data_iter, lr, num_epochs, tgt_vocab, device): + """训练序列到序列模型 + + Defined in :numref:`sec_seq2seq_decoder`""" + optimizer = tf.keras.optimizers.Adam(learning_rate=lr) + animator = d2l.Animator(xlabel="epoch", ylabel="loss", + xlim=[10, num_epochs]) + for epoch in range(num_epochs): + timer = d2l.Timer() + metric = d2l.Accumulator(2) # 训练损失总和,词元数量 + for batch in data_iter: + X, X_valid_len, Y, Y_valid_len = [x for x in batch] + bos = tf.reshape(tf.constant([tgt_vocab['']] * Y.shape[0]), + shape=(-1, 1)) + dec_input = tf.concat([bos, Y[:, :-1]], 1) # 强制教学 + with tf.GradientTape() as tape: + Y_hat, _ = net(X, dec_input, X_valid_len, training=True) + l = MaskedSoftmaxCELoss(Y_valid_len)(Y, Y_hat) + gradients = tape.gradient(l, net.trainable_variables) + gradients = d2l.grad_clipping(gradients, 1) + optimizer.apply_gradients(zip(gradients, net.trainable_variables)) + num_tokens = tf.reduce_sum(Y_valid_len).numpy() + metric.add(tf.reduce_sum(l), num_tokens) + if (epoch + 1) % 10 == 0: + animator.add(epoch + 1, (metric[0] / metric[1],)) + print(f'loss {metric[0] / metric[1]:.3f}, {metric[1] / timer.stop():.1f} ' + f'tokens/sec on {str(device)}') + +def predict_seq2seq(net, src_sentence, src_vocab, tgt_vocab, num_steps, + save_attention_weights=False): + """序列到序列模型的预测 + + Defined in :numref:`sec_seq2seq_training`""" + src_tokens = src_vocab[src_sentence.lower().split(' ')] + [ + src_vocab['']] + enc_valid_len = tf.constant([len(src_tokens)]) + src_tokens = d2l.truncate_pad(src_tokens, num_steps, src_vocab['']) + # 添加批量轴 + enc_X = tf.expand_dims(src_tokens, axis=0) + enc_outputs = net.encoder(enc_X, enc_valid_len, training=False) + dec_state = net.decoder.init_state(enc_outputs, enc_valid_len) + # 添加批量轴 + dec_X = tf.expand_dims(tf.constant([tgt_vocab['']]), axis=0) + output_seq, attention_weight_seq = [], [] + for _ in range(num_steps): + Y, dec_state = net.decoder(dec_X, dec_state, training=False) + # 我们使用具有预测最高可能性的词元,作为解码器在下一时间步的输入 + dec_X = tf.argmax(Y, axis=2) + pred = tf.squeeze(dec_X, axis=0) + # 保存注意力权重 + if save_attention_weights: + attention_weight_seq.append(net.decoder.attention_weights) + # 一旦序列结束词元被预测,输出序列的生成就完成了 + if pred == tgt_vocab['']: + break + output_seq.append(pred.numpy()) + return ' '.join(tgt_vocab.to_tokens(tf.reshape(output_seq, + shape = -1).numpy().tolist())), attention_weight_seq + +def bleu(pred_seq, label_seq, k): + """计算BLEU + + Defined in :numref:`sec_seq2seq_training`""" + pred_tokens, label_tokens = pred_seq.split(' '), label_seq.split(' ') + len_pred, len_label = len(pred_tokens), len(label_tokens) + score = math.exp(min(0, 1 - len_label / len_pred)) + for n in range(1, k + 1): + num_matches, label_subs = 0, collections.defaultdict(int) + for i in range(len_label - n + 1): + label_subs[' '.join(label_tokens[i: i + n])] += 1 + for i in range(len_pred - n + 1): + if label_subs[' '.join(pred_tokens[i: i + n])] > 0: + num_matches += 1 + label_subs[' '.join(pred_tokens[i: i + n])] -= 1 + score *= math.pow(num_matches / (len_pred - n + 1), math.pow(0.5, n)) + return score + +def show_heatmaps(matrices, xlabel, ylabel, titles=None, figsize=(2.5, 2.5), + cmap='Reds'): + """显示矩阵热图 + + Defined in :numref:`sec_attention-cues`""" + d2l.use_svg_display() + num_rows, num_cols = matrices.shape[0], matrices.shape[1] + fig, axes = d2l.plt.subplots(num_rows, num_cols, figsize=figsize, + sharex=True, sharey=True, squeeze=False) + for i, (row_axes, row_matrices) in enumerate(zip(axes, matrices)): + for j, (ax, matrix) in enumerate(zip(row_axes, row_matrices)): + pcm = ax.imshow(d2l.numpy(matrix), cmap=cmap) + if i == num_rows - 1: + ax.set_xlabel(xlabel) + if j == 0: + ax.set_ylabel(ylabel) + if titles: + ax.set_title(titles[j]) + fig.colorbar(pcm, ax=axes, shrink=0.6); + +def masked_softmax(X, valid_lens): + """通过在最后一个轴上掩蔽元素来执行softmax操作 + + Defined in :numref:`sec_attention-scoring-functions`""" + # `X`:3D张量,`valid_lens`:1D或2D张量 + if valid_lens is None: + return tf.nn.softmax(X, axis=-1) + else: + shape = X.shape + if len(valid_lens.shape) == 1: + valid_lens = tf.repeat(valid_lens, repeats=shape[1]) + + else: + valid_lens = tf.reshape(valid_lens, shape=-1) + # 最后一轴上被掩蔽的元素使用一个非常大的负值替换,从而其softmax输出为0 + X = d2l.sequence_mask(tf.reshape(X, shape=(-1, shape[-1])), + valid_lens, value=-1e6) + return tf.nn.softmax(tf.reshape(X, shape=shape), axis=-1) + +class AdditiveAttention(tf.keras.layers.Layer): + """Additiveattention. + + Defined in :numref:`sec_attention-scoring-functions`""" + def __init__(self, key_size, query_size, num_hiddens, dropout, **kwargs): + super().__init__(**kwargs) + self.W_k = tf.keras.layers.Dense(num_hiddens, use_bias=False) + self.W_q = tf.keras.layers.Dense(num_hiddens, use_bias=False) + self.w_v = tf.keras.layers.Dense(1, use_bias=False) + self.dropout = tf.keras.layers.Dropout(dropout) + + def call(self, queries, keys, values, valid_lens, **kwargs): + queries, keys = self.W_q(queries), self.W_k(keys) + # 在维度扩展后, + # `queries`的形状:(`batch_size`,查询的个数,1,`num_hidden`) + # `key`的形状:(`batch_size`,1,“键-值”对的个数,`num_hiddens`) + # 使用广播方式进行求和 + features = tf.expand_dims(queries, axis=2) + tf.expand_dims( + keys, axis=1) + features = tf.nn.tanh(features) + # `self.w_v`仅有一个输出,因此从形状中移除最后那个维度。 + # `scores`的形状:(`batch_size`,查询的个数,“键-值”对的个数) + scores = tf.squeeze(self.w_v(features), axis=-1) + self.attention_weights = masked_softmax(scores, valid_lens) + # `values`的形状:(`batch_size`,“键-值”对的个数,值的维度) + return tf.matmul(self.dropout( + self.attention_weights, **kwargs), values) + +class DotProductAttention(tf.keras.layers.Layer): + """Scaleddotproductattention. + + Defined in :numref:`subsec_additive-attention`""" + def __init__(self, dropout, **kwargs): + super().__init__(**kwargs) + self.dropout = tf.keras.layers.Dropout(dropout) + + # `queries`的形状:(`batch_size`,查询的个数,`d`) + # `keys`的形状:(`batch_size`,“键-值”对的个数,`d`) + # `values`的形状:(`batch_size`,“键-值”对的个数,值的维度) + # `valid_lens`的形状:(`batch_size`,)或者(`batch_size`,查询的个数) + def call(self, queries, keys, values, valid_lens, **kwargs): + d = queries.shape[-1] + scores = tf.matmul(queries, keys, transpose_b=True)/tf.math.sqrt( + tf.cast(d, dtype=tf.float32)) + self.attention_weights = masked_softmax(scores, valid_lens) + return tf.matmul(self.dropout(self.attention_weights, **kwargs), values) + +class AttentionDecoder(d2l.Decoder): + """带有注意力机制解码器的基本接口 + + Defined in :numref:`sec_seq2seq_attention`""" + def __init__(self, **kwargs): + super(AttentionDecoder, self).__init__(**kwargs) + + @property + def attention_weights(self): + raise NotImplementedError + +class MultiHeadAttention(tf.keras.layers.Layer): + """多头注意力 + + Defined in :numref:`sec_multihead-attention`""" + def __init__(self, key_size, query_size, value_size, num_hiddens, + num_heads, dropout, bias=False, **kwargs): + super().__init__(**kwargs) + self.num_heads = num_heads + self.attention = d2l.DotProductAttention(dropout) + self.W_q = tf.keras.layers.Dense(num_hiddens, use_bias=bias) + self.W_k = tf.keras.layers.Dense(num_hiddens, use_bias=bias) + self.W_v = tf.keras.layers.Dense(num_hiddens, use_bias=bias) + self.W_o = tf.keras.layers.Dense(num_hiddens, use_bias=bias) + + def call(self, queries, keys, values, valid_lens, **kwargs): + # `queries`,`keys`,`values`的形状: + # (`batch_size`,查询或者“键-值”对的个数,`num_hiddens`) + # `valid_lens` 的形状: + # (`batch_size`,)或(`batch_size`,查询的个数) + # 经过变换后,输出的`queries`,`keys`,`values` 的形状: + # (`batch_size`*`num_heads`,查询或者“键-值”对的个数, + # `num_hiddens`/`num_heads`) + queries = transpose_qkv(self.W_q(queries), self.num_heads) + keys = transpose_qkv(self.W_k(keys), self.num_heads) + values = transpose_qkv(self.W_v(values), self.num_heads) + + if valid_lens is not None: + # 在轴0,将第一项(标量或者矢量)复制`num_heads`次, + # 然后如此复制第二项,然后诸如此类。 + valid_lens = tf.repeat(valid_lens, repeats=self.num_heads, axis=0) + + # `output`的形状:(`batch_size`*`num_heads`,查询的个数, + # `num_hiddens`/`num_heads`) + output = self.attention(queries, keys, values, valid_lens, **kwargs) + + # `output_concat`的形状:(`batch_size`,查询的个数,`num_hiddens`) + output_concat = transpose_output(output, self.num_heads) + return self.W_o(output_concat) + +def transpose_qkv(X, num_heads): + """为了多注意力头的并行计算而变换形状 + + Defined in :numref:`sec_multihead-attention`""" + # 输入`X`的形状:(`batch_size`,查询或者“键-值”对的个数,`num_hiddens`) + # 输出`X`的形状:(`batch_size`,查询或者“键-值”对的个数,`num_heads`, + # `num_hiddens`/`num_heads`) + X = tf.reshape(X, shape=(X.shape[0], X.shape[1], num_heads, -1)) + + # 输出`X`的形状:(`batch_size`,`num_heads`,查询或者“键-值”对的个数, + # `num_hiddens`/`num_heads`) + X = tf.transpose(X, perm=(0, 2, 1, 3)) + + # 最终输出的形状:(`batch_size`*`num_heads`,查询或者“键-值”对的个数, + # `num_hiddens`/`num_heads`) + return tf.reshape(X, shape=(-1, X.shape[2], X.shape[3])) + + +def transpose_output(X, num_heads): + """逆转`transpose_qkv`函数的操作 + + Defined in :numref:`sec_multihead-attention`""" + X = tf.reshape(X, shape=(-1, num_heads, X.shape[1], X.shape[2])) + X = tf.transpose(X, perm=(0, 2, 1, 3)) + return tf.reshape(X, shape=(X.shape[0], X.shape[1], -1)) + +class PositionalEncoding(tf.keras.layers.Layer): + """位置编码 + + Defined in :numref:`sec_self-attention-and-positional-encoding`""" + def __init__(self, num_hiddens, dropout, max_len=1000): + super().__init__() + self.dropout = tf.keras.layers.Dropout(dropout) + # 创建一个足够长的`P` + self.P = np.zeros((1, max_len, num_hiddens)) + X = np.arange(max_len, dtype=np.float32).reshape( + -1,1)/np.power(10000, np.arange( + 0, num_hiddens, 2, dtype=np.float32) / num_hiddens) + self.P[:, :, 0::2] = np.sin(X) + self.P[:, :, 1::2] = np.cos(X) + + def call(self, X, **kwargs): + X = X + self.P[:, :X.shape[1], :] + return self.dropout(X, **kwargs) + +class PositionWiseFFN(tf.keras.layers.Layer): + """基于位置的前馈网络 + + Defined in :numref:`sec_transformer`""" + def __init__(self, ffn_num_hiddens, ffn_num_outputs, **kwargs): + super().__init__(*kwargs) + self.dense1 = tf.keras.layers.Dense(ffn_num_hiddens) + self.relu = tf.keras.layers.ReLU() + self.dense2 = tf.keras.layers.Dense(ffn_num_outputs) + + def call(self, X): + return self.dense2(self.relu(self.dense1(X))) + +class AddNorm(tf.keras.layers.Layer): + """残差连接后进行层规范化 + + Defined in :numref:`sec_transformer`""" + def __init__(self, normalized_shape, dropout, **kwargs): + super().__init__(**kwargs) + self.dropout = tf.keras.layers.Dropout(dropout) + self.ln = tf.keras.layers.LayerNormalization(normalized_shape) + + def call(self, X, Y, **kwargs): + return self.ln(self.dropout(Y, **kwargs) + X) + +class EncoderBlock(tf.keras.layers.Layer): + """transformer编码器块 + + Defined in :numref:`sec_transformer`""" + def __init__(self, key_size, query_size, value_size, num_hiddens, + norm_shape, ffn_num_hiddens, num_heads, dropout, bias=False, **kwargs): + super().__init__(**kwargs) + self.attention = d2l.MultiHeadAttention(key_size, query_size, value_size, num_hiddens, + num_heads, dropout, bias) + self.addnorm1 = AddNorm(norm_shape, dropout) + self.ffn = PositionWiseFFN(ffn_num_hiddens, num_hiddens) + self.addnorm2 = AddNorm(norm_shape, dropout) + + def call(self, X, valid_lens, **kwargs): + Y = self.addnorm1(X, self.attention(X, X, X, valid_lens, **kwargs), **kwargs) + return self.addnorm2(Y, self.ffn(Y), **kwargs) + +class TransformerEncoder(d2l.Encoder): + """transformer编码器 + + Defined in :numref:`sec_transformer`""" + def __init__(self, vocab_size, key_size, query_size, value_size, + num_hiddens, norm_shape, ffn_num_hiddens, num_heads, + num_layers, dropout, bias=False, **kwargs): + super().__init__(**kwargs) + self.num_hiddens = num_hiddens + self.embedding = tf.keras.layers.Embedding(vocab_size, num_hiddens) + self.pos_encoding = d2l.PositionalEncoding(num_hiddens, dropout) + self.blks = [EncoderBlock( + key_size, query_size, value_size, num_hiddens, norm_shape, + ffn_num_hiddens, num_heads, dropout, bias) for _ in range( + num_layers)] + + def call(self, X, valid_lens, **kwargs): + # 因为位置编码值在-1和1之间, + # 因此嵌入值乘以嵌入维度的平方根进行缩放, + # 然后再与位置编码相加。 + X = self.pos_encoding(self.embedding(X) * tf.math.sqrt( + tf.cast(self.num_hiddens, dtype=tf.float32)), **kwargs) + self.attention_weights = [None] * len(self.blks) + for i, blk in enumerate(self.blks): + X = blk(X, valid_lens, **kwargs) + self.attention_weights[ + i] = blk.attention.attention.attention_weights + return X + def annotate(text, xy, xytext): d2l.plt.gca().annotate(text, xy=xy, xytext=xytext, arrowprops=dict(arrowstyle='->')) def train_2d(trainer, steps=20, f_grad=None): - """用定制的训练机优化2D目标函数。 + """用定制的训练机优化2D目标函数 Defined in :numref:`subsec_gd-learningrate`""" - # `s1` 和 `s2` 是稍后将使用的内部状态变量 + # `s1`和`s2`是稍后将使用的内部状态变量 x1, x2, s1, s2 = -5, -2, 0, 0 results = [(x1, x2)] for i in range(steps): @@ -763,7 +1290,7 @@ def train_2d(trainer, steps=20, f_grad=None): return results def show_trace_2d(f, results): - """显示优化过程中2D变量的轨迹。 + """显示优化过程中2D变量的轨迹 Defined in :numref:`subsec_gd-learningrate`""" d2l.set_figsize() @@ -826,7 +1353,7 @@ def train_concise_ch11(trainer_fn, hyperparams, data_iter, num_epochs=2): kernel_initializer=tf.random_normal_initializer(stddev=0.01))) optimizer = trainer_fn(**hyperparams) loss = tf.keras.losses.MeanSquaredError() - # 注意: L2 Loss = 1/2 * MSE Loss。 + # 注意:L2Loss=1/2*MSELoss。 # TensorFlow的MSE损失与MXNet的L2损失大概相差2倍。 # 因此,我们将TensorFlow中的损失减半 animator = d2l.Animator(xlabel='epoch', ylabel='loss', @@ -851,6 +1378,7 @@ def train_concise_ch11(trainer_fn, hyperparams, data_iter, num_epochs=2): print(f'loss: {animator.Y[0][-1]:.3f}, {timer.avg():.3f} sec/epoch') class Benchmark: + """用于测量运行时间""" def __init__(self, description='Done'): """Defined in :numref:`sec_hybridize`""" self.description = description @@ -888,8 +1416,8 @@ def box_center_to_corner(boxes): def bbox_to_rect(bbox, color): """Defined in :numref:`sec_bbox`""" - # 将边界框 (左上x, 左上y, 右下x, 右下y) 格式转换成 matplotlib 格式: - # ((左上x, 左上y), 宽, 高) + # 将边界框(左上x,左上y,右下x,右下y)格式转换成matplotlib格式: + # ((左上x,左上y),宽,高) return d2l.plt.Rectangle( xy=(bbox[0], bbox[1]), width=bbox[2]-bbox[0], height=bbox[3]-bbox[1], fill=False, edgecolor=color, linewidth=2)# Alias defined in config.ini diff --git a/d2l/torch.py b/d2l/torch.py index b21cef18f..5961c39cf 100644 --- a/d2l/torch.py +++ b/d2l/torch.py @@ -32,20 +32,20 @@ from torchvision import transforms def use_svg_display(): - """使用svg格式在Jupyter中显示绘图。 + """使用svg格式在Jupyter中显示绘图 Defined in :numref:`sec_calculus`""" display.set_matplotlib_formats('svg') def set_figsize(figsize=(3.5, 2.5)): - """设置matplotlib的图表大小。 + """设置matplotlib的图表大小 Defined in :numref:`sec_calculus`""" use_svg_display() d2l.plt.rcParams['figure.figsize'] = figsize def set_axes(axes, xlabel, ylabel, xlim, ylim, xscale, yscale, legend): - """设置matplotlib的轴。 + """设置matplotlib的轴 Defined in :numref:`sec_calculus`""" axes.set_xlabel(xlabel) @@ -61,7 +61,7 @@ def set_axes(axes, xlabel, ylabel, xlim, ylim, xscale, yscale, legend): def plot(X, Y=None, xlabel=None, ylabel=None, legend=None, xlim=None, ylim=None, xscale='linear', yscale='linear', fmts=('-', 'm--', 'g-.', 'r:'), figsize=(3.5, 2.5), axes=None): - """绘制数据点。 + """绘制数据点 Defined in :numref:`sec_calculus`""" if legend is None: @@ -70,7 +70,7 @@ def plot(X, Y=None, xlabel=None, ylabel=None, legend=None, xlim=None, set_figsize(figsize) axes = axes if axes else d2l.plt.gca() - # 如果 `X` 有一个轴,输出True + # 如果`X`有一个轴,输出True def has_one_axis(X): return (hasattr(X, "ndim") and X.ndim == 1 or isinstance(X, list) and not hasattr(X[0], "__len__")) @@ -92,35 +92,35 @@ def has_one_axis(X): set_axes(axes, xlabel, ylabel, xlim, ylim, xscale, yscale, legend) class Timer: - """记录多次运行时间。""" + """记录多次运行时间""" def __init__(self): """Defined in :numref:`subsec_linear_model`""" self.times = [] self.start() def start(self): - """启动计时器。""" + """启动计时器""" self.tik = time.time() def stop(self): - """停止计时器并将时间记录在列表中。""" + """停止计时器并将时间记录在列表中""" self.times.append(time.time() - self.tik) return self.times[-1] def avg(self): - """返回平均时间。""" + """返回平均时间""" return sum(self.times) / len(self.times) def sum(self): - """返回时间总和。""" + """返回时间总和""" return sum(self.times) def cumsum(self): - """返回累计时间。""" + """返回累计时间""" return np.array(self.times).cumsum().tolist() def synthetic_data(w, b, num_examples): - """生成 y = Xw + b + 噪声。 + """生成y=Xw+b+噪声 Defined in :numref:`sec_linear_scratch`""" X = d2l.normal(0, 1, (num_examples, len(w))) @@ -129,19 +129,19 @@ def synthetic_data(w, b, num_examples): return X, d2l.reshape(y, (-1, 1)) def linreg(X, w, b): - """线性回归模型。 + """线性回归模型 Defined in :numref:`sec_linear_scratch`""" return d2l.matmul(X, w) + b def squared_loss(y_hat, y): - """均方损失。 + """均方损失 Defined in :numref:`sec_linear_scratch`""" return (y_hat - d2l.reshape(y, y_hat.shape)) ** 2 / 2 def sgd(params, lr, batch_size): - """小批量随机梯度下降。 + """小批量随机梯度下降 Defined in :numref:`sec_linear_scratch`""" with torch.no_grad(): @@ -150,14 +150,14 @@ def sgd(params, lr, batch_size): param.grad.zero_() def load_array(data_arrays, batch_size, is_train=True): - """构造一个PyTorch数据迭代器。 + """构造一个PyTorch数据迭代器 Defined in :numref:`sec_linear_concise`""" dataset = data.TensorDataset(*data_arrays) return data.DataLoader(dataset, batch_size, shuffle=is_train) def get_fashion_mnist_labels(labels): - """返回Fashion-MNIST数据集的文本标签。 + """返回Fashion-MNIST数据集的文本标签 Defined in :numref:`sec_fashion_mnist`""" text_labels = ['t-shirt', 'trouser', 'pullover', 'dress', 'coat', @@ -165,7 +165,7 @@ def get_fashion_mnist_labels(labels): return [text_labels[int(i)] for i in labels] def show_images(imgs, num_rows, num_cols, titles=None, scale=1.5): - """Plot a list of images. + """绘制图像列表 Defined in :numref:`sec_fashion_mnist`""" figsize = (num_cols * scale, num_rows * scale) @@ -185,13 +185,13 @@ def show_images(imgs, num_rows, num_cols, titles=None, scale=1.5): return axes def get_dataloader_workers(): - """使用4个进程来读取数据。 + """使用4个进程来读取数据 Defined in :numref:`sec_fashion_mnist`""" return 4 def load_data_fashion_mnist(batch_size, resize=None): - """下载Fashion-MNIST数据集,然后将其加载到内存中。 + """下载Fashion-MNIST数据集,然后将其加载到内存中 Defined in :numref:`sec_fashion_mnist`""" trans = [transforms.ToTensor()] @@ -208,7 +208,7 @@ def load_data_fashion_mnist(batch_size, resize=None): num_workers=get_dataloader_workers())) def accuracy(y_hat, y): - """计算预测正确的数量。 + """计算预测正确的数量 Defined in :numref:`sec_softmax_scratch`""" if len(y_hat.shape) > 1 and y_hat.shape[1] > 1: @@ -217,18 +217,19 @@ def accuracy(y_hat, y): return float(d2l.reduce_sum(d2l.astype(cmp, y.dtype))) def evaluate_accuracy(net, data_iter): - """计算在指定数据集上模型的精度。 + """计算在指定数据集上模型的精度 Defined in :numref:`sec_softmax_scratch`""" if isinstance(net, torch.nn.Module): net.eval() # 将模型设置为评估模式 metric = Accumulator(2) # 正确预测数、预测总数 - for X, y in data_iter: - metric.add(accuracy(net(X), y), d2l.size(y)) + with torch.no_grad(): + for X, y in data_iter: + metric.add(accuracy(net(X), y), d2l.size(y)) return metric[0] / metric[1] class Accumulator: - """在`n`个变量上累加。""" + """在`n`个变量上累加""" def __init__(self, n): """Defined in :numref:`sec_softmax_scratch`""" self.data = [0.0] * n @@ -243,7 +244,7 @@ def __getitem__(self, idx): return self.data[idx] def train_epoch_ch3(net, train_iter, loss, updater): - """训练模型一个迭代周期(定义见第3章)。 + """训练模型一个迭代周期(定义见第3章) Defined in :numref:`sec_softmax_scratch`""" # 将模型设置为训练模式 @@ -271,7 +272,7 @@ def train_epoch_ch3(net, train_iter, loss, updater): return metric[0] / metric[2], metric[1] / metric[2] class Animator: - """在动画中绘制数据。""" + """在动画中绘制数据""" def __init__(self, xlabel=None, ylabel=None, legend=None, xlim=None, ylim=None, xscale='linear', yscale='linear', fmts=('-', 'm--', 'g-.', 'r:'), nrows=1, ncols=1, @@ -312,7 +313,7 @@ def add(self, x, y): display.clear_output(wait=True) def train_ch3(net, train_iter, test_iter, loss, num_epochs, updater): - """训练模型(定义见第3章)。 + """训练模型(定义见第3章) Defined in :numref:`sec_softmax_scratch`""" animator = Animator(xlabel='epoch', xlim=[1, num_epochs], ylim=[0.3, 0.9], @@ -327,7 +328,7 @@ def train_ch3(net, train_iter, test_iter, loss, num_epochs, updater): assert test_acc <= 1 and test_acc > 0.7, test_acc def predict_ch3(net, test_iter, n=6): - """预测标签(定义见第3章)。 + """预测标签(定义见第3章) Defined in :numref:`sec_softmax_scratch`""" for X, y in test_iter: @@ -339,10 +340,10 @@ def predict_ch3(net, test_iter, n=6): d2l.reshape(X[0:n], (n, 28, 28)), 1, n, titles=titles[0:n]) def evaluate_loss(net, data_iter, loss): - """评估给定数据集上模型的损失。 + """评估给定数据集上模型的损失 Defined in :numref:`sec_model_selection`""" - metric = d2l.Accumulator(2) # 损失的总和, 样本数量 + metric = d2l.Accumulator(2) # 损失的总和,样本数量 for X, y in data_iter: out = net(X) y = d2l.reshape(y, out.shape) @@ -354,10 +355,10 @@ def evaluate_loss(net, data_iter, loss): DATA_URL = 'http://d2l-data.s3-accelerate.amazonaws.com/' def download(name, cache_dir=os.path.join('..', 'data')): - """下载一个DATA_HUB中的文件,返回本地文件名。 + """下载一个DATA_HUB中的文件,返回本地文件名 Defined in :numref:`sec_kaggle_house`""" - assert name in DATA_HUB, f"{name} 不存在于 {DATA_HUB}." + assert name in DATA_HUB, f"{name} 不存在于 {DATA_HUB}" url, sha1_hash = DATA_HUB[name] os.makedirs(cache_dir, exist_ok=True) fname = os.path.join(cache_dir, url.split('/')[-1]) @@ -370,7 +371,7 @@ def download(name, cache_dir=os.path.join('..', 'data')): break sha1.update(data) if sha1.hexdigest() == sha1_hash: - return fname # Hit cache + return fname # 命中缓存 print(f'正在从{url}下载{fname}...') r = requests.get(url, stream=True, verify=True) with open(fname, 'wb') as f: @@ -378,7 +379,7 @@ def download(name, cache_dir=os.path.join('..', 'data')): return fname def download_extract(name, folder=None): - """下载并解压zip/tar文件。 + """下载并解压zip/tar文件 Defined in :numref:`sec_kaggle_house`""" fname = download(name) @@ -389,12 +390,12 @@ def download_extract(name, folder=None): elif ext in ('.tar', '.gz'): fp = tarfile.open(fname, 'r') else: - assert False, '只有zip/tar文件可以被解压缩。' + assert False, '只有zip/tar文件可以被解压缩' fp.extractall(base_dir) return os.path.join(base_dir, folder) if folder else data_dir def download_all(): - """下载DATA_HUB中的所有文件。 + """下载DATA_HUB中的所有文件 Defined in :numref:`sec_kaggle_house`""" for name in DATA_HUB: @@ -409,7 +410,7 @@ def download_all(): 'fa19780a7b011d9b009e8bff8e99922a8ee2eb90') def try_gpu(i=0): - """如果存在,则返回gpu(i),否则返回cpu()。 + """如果存在,则返回gpu(i),否则返回cpu() Defined in :numref:`sec_use_gpu`""" if torch.cuda.device_count() >= i + 1: @@ -417,7 +418,7 @@ def try_gpu(i=0): return torch.device('cpu') def try_all_gpus(): - """返回所有可用的GPU,如果没有GPU,则返回[cpu(),]。 + """返回所有可用的GPU,如果没有GPU,则返回[cpu(),] Defined in :numref:`sec_use_gpu`""" devices = [torch.device(f'cuda:{i}') @@ -425,7 +426,7 @@ def try_all_gpus(): return devices if devices else [torch.device('cpu')] def corr2d(X, K): - """计算二维互相关运算。 + """计算二维互相关运算 Defined in :numref:`sec_conv_layer`""" h, w = K.shape @@ -436,7 +437,7 @@ def corr2d(X, K): return Y def evaluate_accuracy_gpu(net, data_iter, device=None): - """使用GPU计算模型在数据集上的精度。 + """使用GPU计算模型在数据集上的精度 Defined in :numref:`sec_lenet`""" if isinstance(net, nn.Module): @@ -457,7 +458,7 @@ def evaluate_accuracy_gpu(net, data_iter, device=None): return metric[0] / metric[1] def train_ch6(net, train_iter, test_iter, num_epochs, lr, device): - """用GPU训练模型(在第六章定义)。 + """用GPU训练模型(在第六章定义) Defined in :numref:`sec_lenet`""" def init_weights(m): @@ -472,7 +473,7 @@ def init_weights(m): legend=['train loss', 'train acc', 'test acc']) timer, num_batches = d2l.Timer(), len(train_iter) for epoch in range(num_epochs): - # 训练损失之和,训练精度之和,范例数 + # 训练损失之和,训练准确率之和,范例数 metric = d2l.Accumulator(3) net.train() for i, (X, y) in enumerate(train_iter): @@ -526,7 +527,7 @@ def forward(self, X): '090b5e7e70c295757f55df93cb0a180b9691891a') def read_time_machine(): - """Load the time machine dataset into a list of text lines. + """将时间机器数据集加载到文本行的列表中 Defined in :numref:`sec_text_preprocessing`""" with open(d2l.download('time_machine'), 'r') as f: @@ -534,7 +535,7 @@ def read_time_machine(): return [re.sub('[^A-Za-z]+', ' ', line).strip().lower() for line in lines] def tokenize(lines, token='word'): - """将文本行拆分为单词或字符词元。 + """将文本行拆分为单词或字符词元 Defined in :numref:`sec_text_preprocessing`""" if token == 'word': @@ -554,16 +555,19 @@ def __init__(self, tokens=None, min_freq=0, reserved_tokens=None): reserved_tokens = [] # 按出现频率排序 counter = count_corpus(tokens) - self.token_freqs = sorted(counter.items(), key=lambda x: x[1], - reverse=True) + self._token_freqs = sorted(counter.items(), key=lambda x: x[1], + reverse=True) # 未知词元的索引为0 - self.unk, uniq_tokens = 0, [''] + reserved_tokens - uniq_tokens += [token for token, freq in self.token_freqs - if freq >= min_freq and token not in uniq_tokens] + self.idx_to_token = [''] + reserved_tokens + self.token_to_idx = {token: idx + for idx, token in enumerate(self.idx_to_token)} self.idx_to_token, self.token_to_idx = [], dict() - for token in uniq_tokens: - self.idx_to_token.append(token) - self.token_to_idx[token] = len(self.idx_to_token) - 1 + for token, freq in self._token_freqs: + if freq < min_freq: + break + if token not in self.token_to_idx: + self.idx_to_token.append(token) + self.token_to_idx[token] = len(self.idx_to_token) - 1 def __len__(self): return len(self.idx_to_token) @@ -578,18 +582,26 @@ def to_tokens(self, indices): return self.idx_to_token[indices] return [self.idx_to_token[index] for index in indices] + @property + def unk(self): # 未知词元的索引为0 + return 0 + + @property + def token_freqs(self): + return self._token_freqs + def count_corpus(tokens): - """统计词元的频率。 + """统计词元的频率 Defined in :numref:`sec_text_preprocessing`""" - # 这里的 `tokens` 是 1D 列表或 2D 列表 + # 这里的`tokens`是1D列表或2D列表 if len(tokens) == 0 or isinstance(tokens[0], list): - # 将词元列表展平成使用词元填充的一个列表 + # 将词元列表展平成一个列表 tokens = [token for line in tokens for token in line] return collections.Counter(tokens) def load_corpus_time_machine(max_tokens=-1): - """返回时光机器数据集的词元索引列表和词表。 + """返回时光机器数据集的词元索引列表和词表 Defined in :numref:`sec_text_preprocessing`""" lines = read_time_machine() @@ -603,10 +615,10 @@ def load_corpus_time_machine(max_tokens=-1): return corpus, vocab def seq_data_iter_random(corpus, batch_size, num_steps): - """使用随机抽样生成一个小批量子序列。 + """使用随机抽样生成一个小批量子序列 Defined in :numref:`sec_language_model`""" - # 从随机偏移量开始对序列进行分区,随机范围包括`num_steps - 1` + # 从随机偏移量开始对序列进行分区,随机范围包括`num_steps-1` corpus = corpus[random.randint(0, num_steps - 1):] # 减去1,是因为我们需要考虑标签 num_subseqs = (len(corpus) - 1) // num_steps @@ -629,7 +641,7 @@ def data(pos): yield d2l.tensor(X), d2l.tensor(Y) def seq_data_iter_sequential(corpus, batch_size, num_steps): - """使用顺序分区生成一个小批量子序列。 + """使用顺序分区生成一个小批量子序列 Defined in :numref:`sec_language_model`""" # 从随机偏移量开始划分序列 @@ -645,7 +657,7 @@ def seq_data_iter_sequential(corpus, batch_size, num_steps): yield X, Y class SeqDataLoader: - """加载序列数据的迭代器。""" + """加载序列数据的迭代器""" def __init__(self, batch_size, num_steps, use_random_iter, max_tokens): """Defined in :numref:`sec_language_model`""" if use_random_iter: @@ -660,7 +672,7 @@ def __iter__(self): def load_data_time_machine(batch_size, num_steps, use_random_iter=False, max_tokens=10000): - """返回时光机器数据集的迭代器和词表。 + """返回时光机器数据集的迭代器和词表 Defined in :numref:`sec_language_model`""" data_iter = SeqDataLoader( @@ -684,7 +696,7 @@ def begin_state(self, batch_size, device): return self.init_state(batch_size, self.num_hiddens, device) def predict_ch8(prefix, num_preds, net, vocab, device): - """在`prefix`后面生成新字符。 + """在`prefix`后面生成新字符 Defined in :numref:`sec_rnn_scratch`""" state = net.begin_state(batch_size=1, device=device) @@ -700,7 +712,7 @@ def predict_ch8(prefix, num_preds, net, vocab, device): return ''.join([vocab.idx_to_token[i] for i in outputs]) def grad_clipping(net, theta): - """裁剪梯度。 + """裁剪梯度 Defined in :numref:`sec_rnn_scratch`""" if isinstance(net, nn.Module): @@ -713,11 +725,11 @@ def grad_clipping(net, theta): param.grad[:] *= theta / norm def train_epoch_ch8(net, train_iter, loss, updater, device, use_random_iter): - """训练模型一个迭代周期(定义见第8章)。 + """训练网络一个迭代周期(定义见第8章) Defined in :numref:`sec_rnn_scratch`""" state, timer = None, d2l.Timer() - metric = d2l.Accumulator(2) # 训练损失之和, 词元数量 + metric = d2l.Accumulator(2) # 训练损失之和,词元数量 for X, Y in train_iter: if state is None or use_random_iter: # 在第一次迭代或使用随机抽样时初始化`state` @@ -749,7 +761,7 @@ def train_epoch_ch8(net, train_iter, loss, updater, device, use_random_iter): def train_ch8(net, train_iter, vocab, lr, num_epochs, device, use_random_iter=False): - """训练模型(定义见第8章)。 + """训练模型(定义见第8章) Defined in :numref:`sec_rnn_scratch`""" loss = nn.CrossEntropyLoss() @@ -773,7 +785,7 @@ def train_ch8(net, train_iter, vocab, lr, num_epochs, device, print(predict('traveller')) class RNNModel(nn.Module): - """循环神经网络模型。 + """循环神经网络模型 Defined in :numref:`sec_rnn-concise`""" def __init__(self, rnn_layer, vocab_size, **kwargs): @@ -781,7 +793,7 @@ def __init__(self, rnn_layer, vocab_size, **kwargs): self.rnn = rnn_layer self.vocab_size = vocab_size self.num_hiddens = self.rnn.hidden_size - # 如果RNN是双向的(之后将介绍),`num_directions`应该是2,否则应该是1。 + # 如果RNN是双向的(之后将介绍),`num_directions`应该是2,否则应该是1 if not self.rnn.bidirectional: self.num_directions = 1 self.linear = nn.Linear(self.num_hiddens, self.vocab_size) @@ -793,19 +805,19 @@ def forward(self, inputs, state): X = F.one_hot(inputs.T.long(), self.vocab_size) X = X.to(torch.float32) Y, state = self.rnn(X, state) - # 全连接层首先将`Y`的形状改为(`时间步数`*`批量大小`, `隐藏单元数`)。 - # 它的输出形状是 (`时间步数`*`批量大小`, `词表大小`)。 + # 全连接层首先将`Y`的形状改为(`时间步数`*`批量大小`,`隐藏单元数`) + # 它的输出形状是(`时间步数`*`批量大小`,`词表大小`)。 output = self.linear(Y.reshape((-1, Y.shape[-1]))) return output, state def begin_state(self, device, batch_size=1): if not isinstance(self.rnn, nn.LSTM): - # `nn.GRU` 以张量作为隐状态 + # `nn.GRU`以张量作为隐状态 return torch.zeros((self.num_directions * self.rnn.num_layers, batch_size, self.num_hiddens), device=device) else: - # `nn.LSTM` 以张量作为隐状态 + # `nn.LSTM`以元组作为隐状态 return (torch.zeros(( self.num_directions * self.rnn.num_layers, batch_size, self.num_hiddens), device=device), @@ -813,15 +825,524 @@ def begin_state(self, device, batch_size=1): self.num_directions * self.rnn.num_layers, batch_size, self.num_hiddens), device=device)) +d2l.DATA_HUB['fra-eng'] = (d2l.DATA_URL + 'fra-eng.zip', + '94646ad1522d915e7b0f9296181140edcf86a4f5') + +def read_data_nmt(): + """载入“英语-法语”数据集 + + Defined in :numref:`sec_machine_translation`""" + data_dir = d2l.download_extract('fra-eng') + with open(os.path.join(data_dir, 'fra.txt'), 'r', + encoding='utf-8') as f: + return f.read() + +def preprocess_nmt(text): + """预处理“英语-法语”数据集 + + Defined in :numref:`sec_machine_translation`""" + def no_space(char, prev_char): + return char in set(',.!?') and prev_char != ' ' + + # 使用空格替换不间断空格 + # 使用小写字母替换大写字母 + text = text.replace('\u202f', ' ').replace('\xa0', ' ').lower() + # 在单词和标点符号之间插入空格 + out = [' ' + char if i > 0 and no_space(char, text[i - 1]) else char + for i, char in enumerate(text)] + return ''.join(out) + +def tokenize_nmt(text, num_examples=None): + """词元化“英语-法语”数据数据集 + + Defined in :numref:`sec_machine_translation`""" + source, target = [], [] + for i, line in enumerate(text.split('\n')): + if num_examples and i > num_examples: + break + parts = line.split('\t') + if len(parts) == 2: + source.append(parts[0].split(' ')) + target.append(parts[1].split(' ')) + return source, target + +def truncate_pad(line, num_steps, padding_token): + """截断或填充文本序列 + + Defined in :numref:`sec_machine_translation`""" + if len(line) > num_steps: + return line[:num_steps] # 截断 + return line + [padding_token] * (num_steps - len(line)) # 填充 + +def build_array_nmt(lines, vocab, num_steps): + """将机器翻译的文本序列转换成小批量 + + Defined in :numref:`subsec_mt_data_loading`""" + lines = [vocab[l] for l in lines] + lines = [l + [vocab['']] for l in lines] + array = d2l.tensor([truncate_pad( + l, num_steps, vocab['']) for l in lines]) + valid_len = d2l.reduce_sum( + d2l.astype(array != vocab[''], d2l.int32), 1) + return array, valid_len + +def load_data_nmt(batch_size, num_steps, num_examples=600): + """返回翻译数据集的迭代器和词表 + + Defined in :numref:`subsec_mt_data_loading`""" + text = preprocess_nmt(read_data_nmt()) + source, target = tokenize_nmt(text, num_examples) + src_vocab = d2l.Vocab(source, min_freq=2, + reserved_tokens=['', '', '']) + tgt_vocab = d2l.Vocab(target, min_freq=2, + reserved_tokens=['', '', '']) + src_array, src_valid_len = build_array_nmt(source, src_vocab, num_steps) + tgt_array, tgt_valid_len = build_array_nmt(target, tgt_vocab, num_steps) + data_arrays = (src_array, src_valid_len, tgt_array, tgt_valid_len) + data_iter = d2l.load_array(data_arrays, batch_size) + return data_iter, src_vocab, tgt_vocab + +class Encoder(nn.Module): + """编码器-解码器架构的基本编码器接口""" + def __init__(self, **kwargs): + super(Encoder, self).__init__(**kwargs) + + def forward(self, X, *args): + raise NotImplementedError + +class Decoder(nn.Module): + """编码器-解码器架构的基本解码器接口 + + Defined in :numref:`sec_encoder-decoder`""" + def __init__(self, **kwargs): + super(Decoder, self).__init__(**kwargs) + + def init_state(self, enc_outputs, *args): + raise NotImplementedError + + def forward(self, X, state): + raise NotImplementedError + +class EncoderDecoder(nn.Module): + """编码器-解码器架构的基类 + + Defined in :numref:`sec_encoder-decoder`""" + def __init__(self, encoder, decoder, **kwargs): + super(EncoderDecoder, self).__init__(**kwargs) + self.encoder = encoder + self.decoder = decoder + + def forward(self, enc_X, dec_X, *args): + enc_outputs = self.encoder(enc_X, *args) + dec_state = self.decoder.init_state(enc_outputs, *args) + return self.decoder(dec_X, dec_state) + +class Seq2SeqEncoder(d2l.Encoder): + """用于序列到序列学习的循环神经网络编码器 + + Defined in :numref:`sec_seq2seq`""" + def __init__(self, vocab_size, embed_size, num_hiddens, num_layers, + dropout=0, **kwargs): + super(Seq2SeqEncoder, self).__init__(**kwargs) + # 嵌入层 + self.embedding = nn.Embedding(vocab_size, embed_size) + self.rnn = nn.GRU(embed_size, num_hiddens, num_layers, + dropout=dropout) + + def forward(self, X, *args): + # 输出'X'的形状:(`batch_size`,`num_steps`,`embed_size`) + X = self.embedding(X) + # 在循环神经网络模型中,第一个轴对应于时间步 + X = X.permute(1, 0, 2) + # 如果未提及状态,则默认为0 + output, state = self.rnn(X) + # `output`的形状:(`num_steps`,`batch_size`,`num_hiddens`) + # `state[0]`的形状:(`num_layers`,`batch_size`,`num_hiddens`) + return output, state + +def sequence_mask(X, valid_len, value=0): + """在序列中屏蔽不相关的项 + + Defined in :numref:`sec_seq2seq_decoder`""" + maxlen = X.size(1) + mask = torch.arange((maxlen), dtype=torch.float32, + device=X.device)[None, :] < valid_len[:, None] + X[~mask] = value + return X + +class MaskedSoftmaxCELoss(nn.CrossEntropyLoss): + """带遮蔽的softmax交叉熵损失函数 + + Defined in :numref:`sec_seq2seq_decoder`""" + # `pred`的形状:(`batch_size`,`num_steps`,`vocab_size`) + # `label`的形状:(`batch_size`,`num_steps`) + # `valid_len`的形状:(`batch_size`,) + def forward(self, pred, label, valid_len): + weights = torch.ones_like(label) + weights = sequence_mask(weights, valid_len) + self.reduction='none' + unweighted_loss = super(MaskedSoftmaxCELoss, self).forward( + pred.permute(0, 2, 1), label) + weighted_loss = (unweighted_loss * weights).mean(dim=1) + return weighted_loss + +def train_seq2seq(net, data_iter, lr, num_epochs, tgt_vocab, device): + """训练序列到序列模型 + + Defined in :numref:`sec_seq2seq_decoder`""" + def xavier_init_weights(m): + if type(m) == nn.Linear: + nn.init.xavier_uniform_(m.weight) + if type(m) == nn.GRU: + for param in m._flat_weights_names: + if "weight" in param: + nn.init.xavier_uniform_(m._parameters[param]) + + net.apply(xavier_init_weights) + net.to(device) + optimizer = torch.optim.Adam(net.parameters(), lr=lr) + loss = MaskedSoftmaxCELoss() + net.train() + animator = d2l.Animator(xlabel='epoch', ylabel='loss', + xlim=[10, num_epochs]) + for epoch in range(num_epochs): + timer = d2l.Timer() + metric = d2l.Accumulator(2) # 训练损失总和,词元数量 + for batch in data_iter: + optimizer.zero_grad() + X, X_valid_len, Y, Y_valid_len = [x.to(device) for x in batch] + bos = torch.tensor([tgt_vocab['']] * Y.shape[0], + device=device).reshape(-1, 1) + dec_input = torch.cat([bos, Y[:, :-1]], 1) # 强制教学 + Y_hat, _ = net(X, dec_input, X_valid_len) + l = loss(Y_hat, Y, Y_valid_len) + l.sum().backward() # 损失函数的标量进行“反向传播” + d2l.grad_clipping(net, 1) + num_tokens = Y_valid_len.sum() + optimizer.step() + with torch.no_grad(): + metric.add(l.sum(), num_tokens) + if (epoch + 1) % 10 == 0: + animator.add(epoch + 1, (metric[0] / metric[1],)) + print(f'loss {metric[0] / metric[1]:.3f}, {metric[1] / timer.stop():.1f} ' + f'tokens/sec on {str(device)}') + +def predict_seq2seq(net, src_sentence, src_vocab, tgt_vocab, num_steps, + device, save_attention_weights=False): + """序列到序列模型的预测 + + Defined in :numref:`sec_seq2seq_training`""" + # 在预测时将`net`设置为评估模式 + net.eval() + src_tokens = src_vocab[src_sentence.lower().split(' ')] + [ + src_vocab['']] + enc_valid_len = torch.tensor([len(src_tokens)], device=device) + src_tokens = d2l.truncate_pad(src_tokens, num_steps, src_vocab['']) + # 添加批量轴 + enc_X = torch.unsqueeze( + torch.tensor(src_tokens, dtype=torch.long, device=device), dim=0) + enc_outputs = net.encoder(enc_X, enc_valid_len) + dec_state = net.decoder.init_state(enc_outputs, enc_valid_len) + # 添加批量轴 + dec_X = torch.unsqueeze(torch.tensor( + [tgt_vocab['']], dtype=torch.long, device=device), dim=0) + output_seq, attention_weight_seq = [], [] + for _ in range(num_steps): + Y, dec_state = net.decoder(dec_X, dec_state) + # 我们使用具有预测最高可能性的词元,作为解码器在下一时间步的输入 + dec_X = Y.argmax(dim=2) + pred = dec_X.squeeze(dim=0).type(torch.int32).item() + # 保存注意力权重(稍后讨论) + if save_attention_weights: + attention_weight_seq.append(net.decoder.attention_weights) + # 一旦序列结束词元被预测,输出序列的生成就完成了 + if pred == tgt_vocab['']: + break + output_seq.append(pred) + return ' '.join(tgt_vocab.to_tokens(output_seq)), attention_weight_seq + +def bleu(pred_seq, label_seq, k): + """计算BLEU + + Defined in :numref:`sec_seq2seq_training`""" + pred_tokens, label_tokens = pred_seq.split(' '), label_seq.split(' ') + len_pred, len_label = len(pred_tokens), len(label_tokens) + score = math.exp(min(0, 1 - len_label / len_pred)) + for n in range(1, k + 1): + num_matches, label_subs = 0, collections.defaultdict(int) + for i in range(len_label - n + 1): + label_subs[' '.join(label_tokens[i: i + n])] += 1 + for i in range(len_pred - n + 1): + if label_subs[' '.join(pred_tokens[i: i + n])] > 0: + num_matches += 1 + label_subs[' '.join(pred_tokens[i: i + n])] -= 1 + score *= math.pow(num_matches / (len_pred - n + 1), math.pow(0.5, n)) + return score + +def show_heatmaps(matrices, xlabel, ylabel, titles=None, figsize=(2.5, 2.5), + cmap='Reds'): + """显示矩阵热图 + + Defined in :numref:`sec_attention-cues`""" + d2l.use_svg_display() + num_rows, num_cols = matrices.shape[0], matrices.shape[1] + fig, axes = d2l.plt.subplots(num_rows, num_cols, figsize=figsize, + sharex=True, sharey=True, squeeze=False) + for i, (row_axes, row_matrices) in enumerate(zip(axes, matrices)): + for j, (ax, matrix) in enumerate(zip(row_axes, row_matrices)): + pcm = ax.imshow(d2l.numpy(matrix), cmap=cmap) + if i == num_rows - 1: + ax.set_xlabel(xlabel) + if j == 0: + ax.set_ylabel(ylabel) + if titles: + ax.set_title(titles[j]) + fig.colorbar(pcm, ax=axes, shrink=0.6); + +def masked_softmax(X, valid_lens): + """通过在最后一个轴上掩蔽元素来执行softmax操作 + + Defined in :numref:`sec_attention-scoring-functions`""" + # `X`:3D张量,`valid_lens`:1D或2D张量 + if valid_lens is None: + return nn.functional.softmax(X, dim=-1) + else: + shape = X.shape + if valid_lens.dim() == 1: + valid_lens = torch.repeat_interleave(valid_lens, shape[1]) + else: + valid_lens = valid_lens.reshape(-1) + # 最后一轴上被掩蔽的元素使用一个非常大的负值替换,从而其softmax输出为0 + X = d2l.sequence_mask(X.reshape(-1, shape[-1]), valid_lens, + value=-1e6) + return nn.functional.softmax(X.reshape(shape), dim=-1) + +class AdditiveAttention(nn.Module): + """加性注意力 + + Defined in :numref:`sec_attention-scoring-functions`""" + def __init__(self, key_size, query_size, num_hiddens, dropout, **kwargs): + super(AdditiveAttention, self).__init__(**kwargs) + self.W_k = nn.Linear(key_size, num_hiddens, bias=False) + self.W_q = nn.Linear(query_size, num_hiddens, bias=False) + self.w_v = nn.Linear(num_hiddens, 1, bias=False) + self.dropout = nn.Dropout(dropout) + + def forward(self, queries, keys, values, valid_lens): + queries, keys = self.W_q(queries), self.W_k(keys) + # 在维度扩展后, + # `queries`的形状:(`batch_size`,查询的个数,1,`num_hidden`) + # `key`的形状:(`batch_size`,1,“键-值”对的个数,`num_hiddens`) + # 使用广播方式进行求和 + features = queries.unsqueeze(2) + keys.unsqueeze(1) + features = torch.tanh(features) + # `self.w_v`仅有一个输出,因此从形状中移除最后那个维度。 + # `scores`的形状:(`batch_size`,查询的个数,“键-值”对的个数) + scores = self.w_v(features).squeeze(-1) + self.attention_weights = masked_softmax(scores, valid_lens) + # `values`的形状:(`batch_size`,“键-值”对的个数,值的维度) + return torch.bmm(self.dropout(self.attention_weights), values) + +class DotProductAttention(nn.Module): + """缩放点积注意力 + + Defined in :numref:`subsec_additive-attention`""" + def __init__(self, dropout, **kwargs): + super(DotProductAttention, self).__init__(**kwargs) + self.dropout = nn.Dropout(dropout) + + # `queries`的形状:(`batch_size`,查询的个数,`d`) + # `keys`的形状:(`batch_size`,“键-值”对的个数,`d`) + # `values`的形状:(`batch_size`,“键-值”对的个数,值的维度) + # `valid_lens`的形状:(`batch_size`,)或者(`batch_size`,查询的个数) + def forward(self, queries, keys, values, valid_lens=None): + d = queries.shape[-1] + # 设置`transpose_b=True`为了交换`keys`的最后两个维度 + scores = torch.bmm(queries, keys.transpose(1,2)) / math.sqrt(d) + self.attention_weights = masked_softmax(scores, valid_lens) + return torch.bmm(self.dropout(self.attention_weights), values) + +class AttentionDecoder(d2l.Decoder): + """带有注意力机制解码器的基本接口 + + Defined in :numref:`sec_seq2seq_attention`""" + def __init__(self, **kwargs): + super(AttentionDecoder, self).__init__(**kwargs) + + @property + def attention_weights(self): + raise NotImplementedError + +class MultiHeadAttention(nn.Module): + """多头注意力 + + Defined in :numref:`sec_multihead-attention`""" + def __init__(self, key_size, query_size, value_size, num_hiddens, + num_heads, dropout, bias=False, **kwargs): + super(MultiHeadAttention, self).__init__(**kwargs) + self.num_heads = num_heads + self.attention = d2l.DotProductAttention(dropout) + self.W_q = nn.Linear(query_size, num_hiddens, bias=bias) + self.W_k = nn.Linear(key_size, num_hiddens, bias=bias) + self.W_v = nn.Linear(value_size, num_hiddens, bias=bias) + self.W_o = nn.Linear(num_hiddens, num_hiddens, bias=bias) + + def forward(self, queries, keys, values, valid_lens): + # `queries`,`keys`,`values`的形状: + # (`batch_size`,查询或者“键-值”对的个数,`num_hiddens`) + # `valid_lens` 的形状: + # (`batch_size`,)或(`batch_size`,查询的个数) + # 经过变换后,输出的`queries`,`keys`,`values` 的形状: + # (`batch_size`*`num_heads`,查询或者“键-值”对的个数, + # `num_hiddens`/`num_heads`) + queries = transpose_qkv(self.W_q(queries), self.num_heads) + keys = transpose_qkv(self.W_k(keys), self.num_heads) + values = transpose_qkv(self.W_v(values), self.num_heads) + + if valid_lens is not None: + # 在轴0,将第一项(标量或者矢量)复制`num_heads`次, + # 然后如此复制第二项,然后诸如此类。 + valid_lens = torch.repeat_interleave( + valid_lens, repeats=self.num_heads, dim=0) + + # `output`的形状:(`batch_size`*`num_heads`,查询的个数, + # `num_hiddens`/`num_heads`) + output = self.attention(queries, keys, values, valid_lens) + + # `output_concat`的形状:(`batch_size`,查询的个数,`num_hiddens`) + output_concat = transpose_output(output, self.num_heads) + return self.W_o(output_concat) + +def transpose_qkv(X, num_heads): + """为了多注意力头的并行计算而变换形状 + + Defined in :numref:`sec_multihead-attention`""" + # 输入`X`的形状:(`batch_size`,查询或者“键-值”对的个数,`num_hiddens`) + # 输出`X`的形状:(`batch_size`,查询或者“键-值”对的个数,`num_heads`, + # `num_hiddens`/`num_heads`) + X = X.reshape(X.shape[0], X.shape[1], num_heads, -1) + + # 输出`X`的形状:(`batch_size`,`num_heads`,查询或者“键-值”对的个数, + # `num_hiddens`/`num_heads`) + X = X.permute(0, 2, 1, 3) + + # 最终输出的形状:(`batch_size`*`num_heads`,查询或者“键-值”对的个数, + # `num_hiddens`/`num_heads`) + return X.reshape(-1, X.shape[2], X.shape[3]) + + +def transpose_output(X, num_heads): + """逆转`transpose_qkv`函数的操作 + + Defined in :numref:`sec_multihead-attention`""" + X = X.reshape(-1, num_heads, X.shape[1], X.shape[2]) + X = X.permute(0, 2, 1, 3) + return X.reshape(X.shape[0], X.shape[1], -1) + +class PositionalEncoding(nn.Module): + """位置编码 + + Defined in :numref:`sec_self-attention-and-positional-encoding`""" + def __init__(self, num_hiddens, dropout, max_len=1000): + super(PositionalEncoding, self).__init__() + self.dropout = nn.Dropout(dropout) + # 创建一个足够长的`P` + self.P = d2l.zeros((1, max_len, num_hiddens)) + X = d2l.arange(max_len, dtype=torch.float32).reshape( + -1, 1) / torch.pow(10000, torch.arange( + 0, num_hiddens, 2, dtype=torch.float32) / num_hiddens) + self.P[:, :, 0::2] = torch.sin(X) + self.P[:, :, 1::2] = torch.cos(X) + + def forward(self, X): + X = X + self.P[:, :X.shape[1], :].to(X.device) + return self.dropout(X) + +class PositionWiseFFN(nn.Module): + """基于位置的前馈网络 + + Defined in :numref:`sec_transformer`""" + def __init__(self, ffn_num_input, ffn_num_hiddens, ffn_num_outputs, + **kwargs): + super(PositionWiseFFN, self).__init__(**kwargs) + self.dense1 = nn.Linear(ffn_num_input, ffn_num_hiddens) + self.relu = nn.ReLU() + self.dense2 = nn.Linear(ffn_num_hiddens, ffn_num_outputs) + + def forward(self, X): + return self.dense2(self.relu(self.dense1(X))) + +class AddNorm(nn.Module): + """残差连接后进行层规范化 + + Defined in :numref:`sec_transformer`""" + def __init__(self, normalized_shape, dropout, **kwargs): + super(AddNorm, self).__init__(**kwargs) + self.dropout = nn.Dropout(dropout) + self.ln = nn.LayerNorm(normalized_shape) + + def forward(self, X, Y): + return self.ln(self.dropout(Y) + X) + +class EncoderBlock(nn.Module): + """transformer编码器块 + + Defined in :numref:`sec_transformer`""" + def __init__(self, key_size, query_size, value_size, num_hiddens, + norm_shape, ffn_num_input, ffn_num_hiddens, num_heads, + dropout, use_bias=False, **kwargs): + super(EncoderBlock, self).__init__(**kwargs) + self.attention = d2l.MultiHeadAttention( + key_size, query_size, value_size, num_hiddens, num_heads, dropout, + use_bias) + self.addnorm1 = AddNorm(norm_shape, dropout) + self.ffn = PositionWiseFFN( + ffn_num_input, ffn_num_hiddens, num_hiddens) + self.addnorm2 = AddNorm(norm_shape, dropout) + + def forward(self, X, valid_lens): + Y = self.addnorm1(X, self.attention(X, X, X, valid_lens)) + return self.addnorm2(Y, self.ffn(Y)) + +class TransformerEncoder(d2l.Encoder): + """transformer编码器 + + Defined in :numref:`sec_transformer`""" + def __init__(self, vocab_size, key_size, query_size, value_size, + num_hiddens, norm_shape, ffn_num_input, ffn_num_hiddens, + num_heads, num_layers, dropout, use_bias=False, **kwargs): + super(TransformerEncoder, self).__init__(**kwargs) + self.num_hiddens = num_hiddens + self.embedding = nn.Embedding(vocab_size, num_hiddens) + self.pos_encoding = d2l.PositionalEncoding(num_hiddens, dropout) + self.blks = nn.Sequential() + for i in range(num_layers): + self.blks.add_module("block"+str(i), + EncoderBlock(key_size, query_size, value_size, num_hiddens, + norm_shape, ffn_num_input, ffn_num_hiddens, + num_heads, dropout, use_bias)) + + def forward(self, X, valid_lens, *args): + # 因为位置编码值在-1和1之间, + # 因此嵌入值乘以嵌入维度的平方根进行缩放, + # 然后再与位置编码相加。 + X = self.pos_encoding(self.embedding(X) * math.sqrt(self.num_hiddens)) + self.attention_weights = [None] * len(self.blks) + for i, blk in enumerate(self.blks): + X = blk(X, valid_lens) + self.attention_weights[ + i] = blk.attention.attention.attention_weights + return X + def annotate(text, xy, xytext): d2l.plt.gca().annotate(text, xy=xy, xytext=xytext, arrowprops=dict(arrowstyle='->')) def train_2d(trainer, steps=20, f_grad=None): - """用定制的训练机优化2D目标函数。 + """用定制的训练机优化2D目标函数 Defined in :numref:`subsec_gd-learningrate`""" - # `s1` 和 `s2` 是稍后将使用的内部状态变量 + # `s1`和`s2`是稍后将使用的内部状态变量 x1, x2, s1, s2 = -5, -2, 0, 0 results = [(x1, x2)] for i in range(steps): @@ -834,7 +1355,7 @@ def train_2d(trainer, steps=20, f_grad=None): return results def show_trace_2d(f, results): - """显示优化过程中2D变量的轨迹。 + """显示优化过程中2D变量的轨迹 Defined in :numref:`subsec_gd-learningrate`""" d2l.set_figsize() @@ -895,9 +1416,7 @@ def init_weights(m): optimizer = trainer_fn(net.parameters(), **hyperparams) loss = nn.MSELoss() - # 注意: L2 Loss = 1/2 * MSE Loss。 - # PyTorch的MSE损失与MXNet的L2损失大概相差2倍。 - # 因此,我们将PyTorch中的损失减半 + # L2Loss=1/2*MSELoss animator = d2l.Animator(xlabel='epoch', ylabel='loss', xlim=[0, num_epochs], ylim=[0.22, 0.35]) n, timer = 0, d2l.Timer() @@ -918,6 +1437,7 @@ def init_weights(m): print(f'loss: {animator.Y[0][-1]:.3f}, {timer.avg():.3f} sec/epoch') class Benchmark: + """用于测量运行时间""" def __init__(self, description='Done'): """Defined in :numref:`sec_hybridize`""" self.description = description @@ -938,7 +1458,7 @@ def split_batch(X, y, devices): nn.parallel.scatter(y, devices)) def resnet18(num_classes, in_channels=1): - """稍加修改的 ResNet-18 模型。 + """稍加修改的ResNet-18模型 Defined in :numref:`sec_multi_gpu_concise`""" def resnet_block(in_channels, out_channels, num_residuals, @@ -952,12 +1472,13 @@ def resnet_block(in_channels, out_channels, num_residuals, blk.append(d2l.Residual(out_channels, out_channels)) return nn.Sequential(*blk) - # 该模型使用了更小的卷积核、步长和填充,而且删除了最大汇聚层。 + # 该模型使用了更小的卷积核、步长和填充,而且删除了最大汇聚层 net = nn.Sequential( nn.Conv2d(in_channels, 64, kernel_size=3, stride=1, padding=1), nn.BatchNorm2d(64), nn.ReLU()) - net.add_module("resnet_block1", resnet_block(64, 64, 2, first_block=True)) + net.add_module("resnet_block1", resnet_block( + 64, 64, 2, first_block=True)) net.add_module("resnet_block2", resnet_block(64, 128, 2)) net.add_module("resnet_block3", resnet_block(128, 256, 2)) net.add_module("resnet_block4", resnet_block(256, 512, 2)) @@ -1044,14 +1565,14 @@ def box_center_to_corner(boxes): def bbox_to_rect(bbox, color): """Defined in :numref:`sec_bbox`""" - # 将边界框 (左上x, 左上y, 右下x, 右下y) 格式转换成 matplotlib 格式: - # ((左上x, 左上y), 宽, 高) + # 将边界框(左上x,左上y,右下x,右下y)格式转换成matplotlib格式: + # ((左上x,左上y),宽,高) return d2l.plt.Rectangle( xy=(bbox[0], bbox[1]), width=bbox[2]-bbox[0], height=bbox[3]-bbox[1], fill=False, edgecolor=color, linewidth=2) def multibox_prior(data, sizes, ratios): - """生成以每个像素为中心具有不同形状的锚框。 + """生成以每个像素为中心具有不同形状的锚框 Defined in :numref:`sec_anchor`""" in_height, in_width = data.shape[-2:] @@ -1063,8 +1584,8 @@ def multibox_prior(data, sizes, ratios): # 为了将锚点移动到像素的中心,需要设置偏移量。 # 因为一个像素的的高为1且宽为1,我们选择偏移我们的中心0.5 offset_h, offset_w = 0.5, 0.5 - steps_h = 1.0 / in_height # Scaled steps in y axis - steps_w = 1.0 / in_width # Scaled steps in x axis + steps_h = 1.0 / in_height # 在y轴上缩放步长 + steps_w = 1.0 / in_width # 在x轴上缩放步长 # 生成锚框的所有中心点 center_h = (torch.arange(in_height, device=device) + offset_h) * steps_h @@ -1073,10 +1594,10 @@ def multibox_prior(data, sizes, ratios): shift_y, shift_x = shift_y.reshape(-1), shift_x.reshape(-1) # 生成“boxes_per_pixel”个高和宽, - # 之后用于创建锚框的四角坐标 (xmin, xmax, ymin, ymax) + # 之后用于创建锚框的四角坐标(xmin,xmax,ymin,ymax) w = torch.cat((size_tensor * torch.sqrt(ratio_tensor[0]), sizes[0] * torch.sqrt(ratio_tensor[1:])))\ - * in_height / in_width # Handle rectangular inputs + * in_height / in_width # 处理矩形输入 h = torch.cat((size_tensor / torch.sqrt(ratio_tensor[0]), sizes[0] / torch.sqrt(ratio_tensor[1:]))) # 除以2来获得半高和半宽 @@ -1091,7 +1612,7 @@ def multibox_prior(data, sizes, ratios): return output.unsqueeze(0) def show_bboxes(axes, bboxes, labels=None, colors=None): - """显示所有边界框。 + """显示所有边界框 Defined in :numref:`sec_anchor`""" def _make_list(obj, default_values=None): @@ -1114,34 +1635,34 @@ def _make_list(obj, default_values=None): bbox=dict(facecolor=color, lw=0)) def box_iou(boxes1, boxes2): - """计算两个锚框或边界框列表中成对的交并比。 + """计算两个锚框或边界框列表中成对的交并比 Defined in :numref:`sec_anchor`""" box_area = lambda boxes: ((boxes[:, 2] - boxes[:, 0]) * (boxes[:, 3] - boxes[:, 1])) - # `boxes1`, `boxes2`, `areas1`, `areas2`的形状: - # `boxes1`:(boxes1的数量, 4), - # `boxes2`:(boxes2的数量, 4), + # `boxes1`,`boxes2`,`areas1`,`areas2`的形状: + # `boxes1`:(boxes1的数量,4), + # `boxes2`:(boxes2的数量,4), # `areas1`:(boxes1的数量,), # `areas2`:(boxes2的数量,) areas1 = box_area(boxes1) areas2 = box_area(boxes2) - # `inter_upperlefts`, `inter_lowerrights`, `inters`的形状: - # (boxes1的数量, boxes2的数量, 2) + # `inter_upperlefts`,`inter_lowerrights`,`inters`的形状: + # (boxes1的数量,boxes2的数量,2) inter_upperlefts = torch.max(boxes1[:, None, :2], boxes2[:, :2]) inter_lowerrights = torch.min(boxes1[:, None, 2:], boxes2[:, 2:]) inters = (inter_lowerrights - inter_upperlefts).clamp(min=0) - # `inter_areas` and `union_areas`的形状: (boxes1的数量, boxes2的数量) + # `inter_areas`and`union_areas`的形状:(boxes1的数量,boxes2的数量) inter_areas = inters[:, :, 0] * inters[:, :, 1] union_areas = areas1[:, None] + areas2 - inter_areas return inter_areas / union_areas def assign_anchor_to_bbox(ground_truth, anchors, device, iou_threshold=0.5): - """将最接近的真实边界框分配给锚框。 + """将最接近的真实边界框分配给锚框 Defined in :numref:`sec_anchor`""" num_anchors, num_gt_boxes = anchors.shape[0], ground_truth.shape[0] - # 位于第i行和第j列的元素 x_ij 是锚框i和真实边界框j的IoU + # 位于第i行和第j列的元素x_ij是锚框i和真实边界框j的IoU jaccard = box_iou(anchors, ground_truth) # 对于每个锚框,分配的真实边界框的张量 anchors_bbox_map = torch.full((num_anchors,), -1, dtype=torch.long, @@ -1163,7 +1684,7 @@ def assign_anchor_to_bbox(ground_truth, anchors, device, iou_threshold=0.5): return anchors_bbox_map def offset_boxes(anchors, assigned_bb, eps=1e-6): - """对锚框偏移量的转换。 + """对锚框偏移量的转换 Defined in :numref:`subsec_labeling-anchor-boxes`""" c_anc = d2l.box_corner_to_center(anchors) @@ -1174,7 +1695,7 @@ def offset_boxes(anchors, assigned_bb, eps=1e-6): return offset def multibox_target(anchors, labels): - """使用真实边界框标记锚框。 + """使用真实边界框标记锚框 Defined in :numref:`subsec_labeling-anchor-boxes`""" batch_size, anchors = labels.shape[0], anchors.squeeze(0) @@ -1208,7 +1729,7 @@ def multibox_target(anchors, labels): return (bbox_offset, bbox_mask, class_labels) def offset_inverse(anchors, offset_preds): - """根据带有预测偏移量的锚框来预测边界框。 + """根据带有预测偏移量的锚框来预测边界框 Defined in :numref:`subsec_labeling-anchor-boxes`""" anc = d2l.box_corner_to_center(anchors) @@ -1219,7 +1740,7 @@ def offset_inverse(anchors, offset_preds): return predicted_bbox def nms(boxes, scores, iou_threshold): - """对预测边界框的置信度进行排序。 + """对预测边界框的置信度进行排序 Defined in :numref:`subsec_predicting-bounding-boxes-nms`""" B = torch.argsort(scores, dim=-1, descending=True) @@ -1236,7 +1757,7 @@ def nms(boxes, scores, iou_threshold): def multibox_detection(cls_probs, offset_preds, anchors, nms_threshold=0.5, pos_threshold=0.009999999): - """使用非极大值抑制来预测边界框。 + """使用非极大值抑制来预测边界框 Defined in :numref:`subsec_predicting-bounding-boxes-nms`""" device, batch_size = cls_probs.device, cls_probs.shape[0] @@ -1249,7 +1770,7 @@ def multibox_detection(cls_probs, offset_preds, anchors, nms_threshold=0.5, predicted_bb = offset_inverse(anchors, offset_pred) keep = nms(predicted_bb, conf, nms_threshold) - # 找到所有的 non_keep 索引,并将类设置为背景 + # 找到所有的non_keep索引,并将类设置为背景 all_idx = torch.arange(num_anchors, dtype=torch.long, device=device) combined = torch.cat((keep, all_idx)) uniques, counts = combined.unique(return_counts=True) @@ -1258,7 +1779,7 @@ def multibox_detection(cls_probs, offset_preds, anchors, nms_threshold=0.5, class_id[non_keep] = -1 class_id = class_id[all_id_sorted] conf, predicted_bb = conf[all_id_sorted], predicted_bb[all_id_sorted] - # `pos_threshold` 是一个用于非背景预测的阈值 + # `pos_threshold`是一个用于非背景预测的阈值 below_min_idx = (conf < pos_threshold) class_id[below_min_idx] = -1 conf[below_min_idx] = 1 - conf[below_min_idx] @@ -1273,7 +1794,7 @@ def multibox_detection(cls_probs, offset_preds, anchors, nms_threshold=0.5, '5de26c8fce5ccdea9f91267273464dc968d20d72') def read_data_bananas(is_train=True): - """读取香蕉检测数据集中的图像和标签。 + """读取香蕉检测数据集中的图像和标签 Defined in :numref:`sec_object-detection-dataset`""" data_dir = d2l.download_extract('banana-detection') @@ -1286,14 +1807,13 @@ def read_data_bananas(is_train=True): images.append(torchvision.io.read_image( os.path.join(data_dir, 'bananas_train' if is_train else 'bananas_val', 'images', f'{img_name}'))) - # Here `target` contains (class, upper-left x, upper-left y, - # lower-right x, lower-right y), where all the images have the same - # banana class (index 0) + # 这里的`target`包含(类别,左上角x,左上角y,右下角x,右下角y), + # 其中所有图像都具有相同的香蕉类(索引为0) targets.append(list(target)) return images, torch.tensor(targets).unsqueeze(1) / 256 class BananasDataset(torch.utils.data.Dataset): - """一个用于加载香蕉检测数据集的自定义数据集。 + """一个用于加载香蕉检测数据集的自定义数据集 Defined in :numref:`sec_object-detection-dataset`""" def __init__(self, is_train): @@ -1308,7 +1828,7 @@ def __len__(self): return len(self.features) def load_data_bananas(batch_size): - """加载香蕉检测数据集。 + """加载香蕉检测数据集 Defined in :numref:`sec_object-detection-dataset`""" train_iter = torch.utils.data.DataLoader(BananasDataset(is_train=True), @@ -1321,7 +1841,7 @@ def load_data_bananas(batch_size): '4e443f8a2eca6b1dac8a6c57641b67dd40621a49') def read_voc_images(voc_dir, is_train=True): - """读取所有VOC图像并标注。 + """读取所有VOC图像并标注 Defined in :numref:`sec_semantic_segmentation`""" txt_fname = os.path.join(voc_dir, 'ImageSets', 'Segmentation', @@ -1350,7 +1870,7 @@ def read_voc_images(voc_dir, is_train=True): 'potted plant', 'sheep', 'sofa', 'train', 'tv/monitor'] def voc_colormap2label(): - """构建从RGB到VOC类别索引的映射。 + """构建从RGB到VOC类别索引的映射 Defined in :numref:`sec_semantic_segmentation`""" colormap2label = torch.zeros(256 ** 3, dtype=torch.long) @@ -1360,7 +1880,7 @@ def voc_colormap2label(): return colormap2label def voc_label_indices(colormap, colormap2label): - """将VOC标签中的RGB值映射到它们的类别索引。 + """将VOC标签中的RGB值映射到它们的类别索引 Defined in :numref:`sec_semantic_segmentation`""" colormap = colormap.permute(1, 2, 0).numpy().astype('int32') @@ -1369,7 +1889,7 @@ def voc_label_indices(colormap, colormap2label): return colormap2label[idx] def voc_rand_crop(feature, label, height, width): - """随机裁剪特征和标签图像。 + """随机裁剪特征和标签图像 Defined in :numref:`sec_semantic_segmentation`""" rect = torchvision.transforms.RandomCrop.get_params( @@ -1379,7 +1899,7 @@ def voc_rand_crop(feature, label, height, width): return feature, label class VOCSegDataset(torch.utils.data.Dataset): - """一个用于加载VOC数据集的自定义数据集。 + """一个用于加载VOC数据集的自定义数据集 Defined in :numref:`sec_semantic_segmentation`""" @@ -1395,7 +1915,7 @@ def __init__(self, is_train, crop_size, voc_dir): print('read ' + str(len(self.features)) + ' examples') def normalize_image(self, img): - return self.transform(img.float()/255) + return self.transform(img.float() / 255) def filter(self, imgs): return [img for img in imgs if ( @@ -1411,7 +1931,7 @@ def __len__(self): return len(self.features) def load_data_voc(batch_size, crop_size): - """加载VOC语义分割数据集。 + """加载VOC语义分割数据集 Defined in :numref:`sec_semantic_segmentation`""" voc_dir = d2l.download_extract('voc2012', os.path.join( @@ -1429,17 +1949,17 @@ def load_data_voc(batch_size, crop_size): '2068874e4b9a9f0fb07ebe0ad2b29754449ccacd') def read_csv_labels(fname): - """读取 `fname` 来给标签字典返回一个文件名。 + """读取`fname`来给标签字典返回一个文件名 Defined in :numref:`sec_kaggle_cifar10`""" with open(fname, 'r') as f: - # 跳过文件头行 (列名) + # 跳过文件头行(列名) lines = f.readlines()[1:] tokens = [l.rstrip().split(',') for l in lines] return dict(((name, label) for name, label in tokens)) def copyfile(filename, target_dir): - """将文件复制到目标目录。 + """将文件复制到目标目录 Defined in :numref:`sec_kaggle_cifar10`""" os.makedirs(target_dir, exist_ok=True) @@ -1449,9 +1969,9 @@ def reorg_train_valid(data_dir, labels, valid_ratio): """将验证集从原始的训练集中拆分出来 Defined in :numref:`sec_kaggle_cifar10`""" - # 训练数据集中示例最少的类别中的示例数 + # 训练数据集中样本最少的类别中的样本数 n = collections.Counter(labels.values()).most_common()[-1][1] - # 验证集中每个类别的示例数 + # 验证集中每个类别的样本数 n_valid_per_label = max(1, math.floor(n * valid_ratio)) label_count = {} for train_file in os.listdir(os.path.join(data_dir, 'train')): @@ -1484,20 +2004,20 @@ def reorg_test(data_dir): '319d85e578af0cdc590547f26231e4e31cdf1e42') def read_ptb(): - """将PTB数据集加载到文本行的列表中。 + """将PTB数据集加载到文本行的列表中 Defined in :numref:`sec_word2vec_data`""" data_dir = d2l.download_extract('ptb') - # Read the training set. + # Readthetrainingset. with open(os.path.join(data_dir, 'ptb.train.txt')) as f: raw_text = f.read() return [line.split() for line in raw_text.split('\n')] def subsample(sentences, vocab): - """下采样高频词。 + """下采样高频词 Defined in :numref:`sec_word2vec_data`""" - # 排除未知词元 '' + # 排除未知词元'' sentences = [[token for token in line if vocab[token] != vocab.unk] for line in sentences] counter = d2l.count_corpus(sentences) @@ -1512,7 +2032,7 @@ def keep(token): counter) def get_centers_and_contexts(corpus, max_window_size): - """返回跳元模型中的中心词和上下文词。 + """返回跳元模型中的中心词和上下文词 Defined in :numref:`sec_word2vec_data`""" centers, contexts = [], [] @@ -1531,7 +2051,7 @@ def get_centers_and_contexts(corpus, max_window_size): return centers, contexts class RandomGenerator: - """根据n个采样权重在 {1, ..., n} 中随机抽取。""" + """根据n个采样权重在{1,...,n}中随机抽取""" def __init__(self, sampling_weights): """Defined in :numref:`sec_word2vec_data`""" # Exclude @@ -1549,8 +2069,11 @@ def draw(self): self.i += 1 return self.candidates[self.i - 1] +generator = RandomGenerator([2, 3, 4]) +[generator.draw() for _ in range(10)] + def get_negatives(all_contexts, vocab, counter, K): - """返回负采样中的噪声词。 + """返回负采样中的噪声词 Defined in :numref:`sec_word2vec_data`""" # 索引为1、2、...(索引0是词表中排除的未知标记) @@ -1568,7 +2091,7 @@ def get_negatives(all_contexts, vocab, counter, K): return all_negatives def batchify(data): - """返回带有负采样的跳元模型的小批量样本。 + """返回带有负采样的跳元模型的小批量样本 Defined in :numref:`sec_word2vec_data`""" max_len = max(len(c) + len(n) for _, c, n in data) @@ -1576,14 +2099,15 @@ def batchify(data): for center, context, negative in data: cur_len = len(context) + len(negative) centers += [center] - contexts_negatives += [context + negative + [0] * (max_len - cur_len)] + contexts_negatives += \ + [context + negative + [0] * (max_len - cur_len)] masks += [[1] * cur_len + [0] * (max_len - cur_len)] labels += [[1] * len(context) + [0] * (max_len - len(context))] return (d2l.reshape(d2l.tensor(centers), (-1, 1)), d2l.tensor( contexts_negatives), d2l.tensor(masks), d2l.tensor(labels)) def load_data_ptb(batch_size, max_window_size, num_noise_words): - """下载PTB数据集,然后将其加载到内存中。 + """下载PTB数据集,然后将其加载到内存中 Defined in :numref:`subsec_word2vec-minibatch-loading`""" num_workers = d2l.get_dataloader_workers() @@ -1612,9 +2136,9 @@ def __len__(self): dataset = PTBDataset(all_centers, all_contexts, all_negatives) - data_iter = torch.utils.data.DataLoader(dataset, batch_size, shuffle=True, - collate_fn=batchify, - num_workers=num_workers) + data_iter = torch.utils.data.DataLoader( + dataset, batch_size, shuffle=True, + collate_fn=batchify, num_workers=num_workers) return data_iter, vocab d2l.DATA_HUB['glove.6b.50d'] = (d2l.DATA_URL + 'glove.6B.50d.zip', @@ -1630,7 +2154,7 @@ def __len__(self): 'c1816da3821ae9f43899be655002f6c723e91b88') class TokenEmbedding: - """Token Embedding.""" + """GloVe嵌入""" def __init__(self, embedding_name): """Defined in :numref:`sec_synonyms`""" self.idx_to_token, self.idx_to_vec = self._load_embedding( @@ -1665,7 +2189,7 @@ def __len__(self): return len(self.idx_to_token) def get_tokens_and_segments(tokens_a, tokens_b=None): - """获取输入序列的词元及其片段索引。 + """获取输入序列的词元及其片段索引 Defined in :numref:`sec_bert`""" tokens = [''] + tokens_a + [''] @@ -1677,7 +2201,7 @@ def get_tokens_and_segments(tokens_a, tokens_b=None): return tokens, segments class BERTEncoder(nn.Module): - """BERT encoder. + """BERT编码器 Defined in :numref:`subsec_bert_input_rep`""" def __init__(self, vocab_size, num_hiddens, norm_shape, ffn_num_input, @@ -1705,7 +2229,7 @@ def forward(self, tokens, segments, valid_lens): return X class MaskLM(nn.Module): - """BERT的遮蔽语言模型任务 + """BERT的掩蔽语言模型任务 Defined in :numref:`subsec_bert_input_rep`""" def __init__(self, vocab_size, num_hiddens, num_inputs=768, **kwargs): @@ -1720,8 +2244,8 @@ def forward(self, X, pred_positions): pred_positions = pred_positions.reshape(-1) batch_size = X.shape[0] batch_idx = torch.arange(0, batch_size) - # 假设`batch_size=2,`num_pred_positions`=3 - # 那么`batch_idx`是`np.array([0,0,0,1,1])` + # 假设batch_size=2,num_pred_positions=3 + # 那么batch_idx是np.array([0,0,0,1,1]) batch_idx = torch.repeat_interleave(batch_idx, num_pred_positions) masked_X = X[batch_idx, pred_positions] masked_X = masked_X.reshape((batch_size, num_pred_positions, -1)) @@ -1737,7 +2261,7 @@ def __init__(self, num_inputs, **kwargs): self.output = nn.Linear(num_inputs, 2) def forward(self, X): - # `X`的形状: (batch size, `num_hiddens`) + # `X`的形状:(batchsize,`num_hiddens`) return self.output(X) class BERTModel(nn.Module): @@ -1759,13 +2283,14 @@ def __init__(self, vocab_size, num_hiddens, norm_shape, ffn_num_input, self.mlm = MaskLM(vocab_size, num_hiddens, mlm_in_features) self.nsp = NextSentencePred(nsp_in_features) - def forward(self, tokens, segments, valid_lens=None, pred_positions=None): + def forward(self, tokens, segments, valid_lens=None, + pred_positions=None): encoded_X = self.encoder(tokens, segments, valid_lens) if pred_positions is not None: mlm_Y_hat = self.mlm(encoded_X, pred_positions) else: mlm_Y_hat = None - # 用于下一句预测的多层感知机分类器的隐藏层。0是“”标记的索引。 + # 用于下一句预测的多层感知机分类器的隐藏层,0是“”标记的索引 nsp_Y_hat = self.nsp(self.hidden(encoded_X[:, 0, :])) return encoded_X, mlm_Y_hat, nsp_Y_hat @@ -1865,7 +2390,7 @@ def _pad_bert_inputs(examples, max_len, vocab): max_len - len(token_ids)), dtype=torch.long)) all_segments.append(torch.tensor(segments + [0] * ( max_len - len(segments)), dtype=torch.long)) - # `valid_lens` 不包括''的计数 + # `valid_lens`不包括''的计数 valid_lens.append(torch.tensor(len(token_ids), dtype=torch.float32)) all_pred_positions.append(torch.tensor(pred_positions + [0] * ( max_num_mlm_preds - len(pred_positions)), dtype=torch.long)) @@ -1883,7 +2408,8 @@ def _pad_bert_inputs(examples, max_len, vocab): class _WikiTextDataset(torch.utils.data.Dataset): """Defined in :numref:`subsec_prepare_mlm_data`""" def __init__(self, paragraphs, max_len): - # 输入`paragraphs[i]`是代表段落的句子字符串列表;而输出`paragraphs[i]`是代表段落的句子列表,其中每个句子都是词元列表 + # 输入`paragraphs[i]`是代表段落的句子字符串列表; + # 而输出`paragraphs[i]`是代表段落的句子列表,其中每个句子都是词元列表 paragraphs = [d2l.tokenize( paragraph, token='word') for paragraph in paragraphs] sentences = [sentence for paragraph in paragraphs @@ -1915,7 +2441,7 @@ def __len__(self): return len(self.all_token_ids) def load_data_wiki(batch_size, max_len): - """加载WikiText-2数据集。 + """加载WikiText-2数据集 Defined in :numref:`subsec_prepare_mlm_data`""" num_workers = d2l.get_dataloader_workers() @@ -1949,7 +2475,7 @@ def _get_batch_loss_bert(net, loss, vocab_size, tokens_X, '01ada507287d82875905620988597833ad4e0903') def read_imdb(data_dir, is_train): - """读取IMDb评论数据集文本序列和标签。 + """读取IMDb评论数据集文本序列和标签 Defined in :numref:`sec_sentiment`""" data, labels = [], [] @@ -1964,7 +2490,7 @@ def read_imdb(data_dir, is_train): return data, labels def load_data_imdb(batch_size, num_steps=500): - """返回数据迭代器和IMDb评论数据集的词表。 + """返回数据迭代器和IMDb评论数据集的词表 Defined in :numref:`sec_sentiment`""" data_dir = d2l.download_extract('aclImdb', 'aclImdb') @@ -1985,7 +2511,7 @@ def load_data_imdb(batch_size, num_steps=500): return train_iter, test_iter, vocab def predict_sentiment(net, vocab, sequence): - """预测文本序列的情感。 + """预测文本序列的情感 Defined in :numref:`sec_sentiment_rnn`""" sequence = torch.tensor(vocab[sequence.split()], device=d2l.try_gpu()) @@ -1997,7 +2523,7 @@ def predict_sentiment(net, vocab, sequence): '9fcde07509c7e87ec61c640c1b2753d9041758e4') def read_snli(data_dir, is_train): - """将SNLI数据集解析为前提、假设和标签。 + """将SNLI数据集解析为前提、假设和标签 Defined in :numref:`sec_natural-language-inference-and-dataset`""" def extract_text(s): @@ -2013,12 +2539,13 @@ def extract_text(s): with open(file_name, 'r') as f: rows = [row.split('\t') for row in f.readlines()[1:]] premises = [extract_text(row[1]) for row in rows if row[0] in label_set] - hypotheses = [extract_text(row[2]) for row in rows if row[0] in label_set] + hypotheses = [extract_text(row[2]) for row in rows if row[0] \ + in label_set] labels = [label_set[row[0]] for row in rows if row[0] in label_set] return premises, hypotheses, labels class SNLIDataset(torch.utils.data.Dataset): - """用于加载SNLI数据集的自定义数据集。 + """用于加载SNLI数据集的自定义数据集 Defined in :numref:`sec_natural-language-inference-and-dataset`""" def __init__(self, dataset, num_steps, vocab=None): @@ -2026,8 +2553,8 @@ def __init__(self, dataset, num_steps, vocab=None): all_premise_tokens = d2l.tokenize(dataset[0]) all_hypothesis_tokens = d2l.tokenize(dataset[1]) if vocab is None: - self.vocab = d2l.Vocab(all_premise_tokens + all_hypothesis_tokens, - min_freq=5, reserved_tokens=['']) + self.vocab = d2l.Vocab(all_premise_tokens + \ + all_hypothesis_tokens, min_freq=5, reserved_tokens=['']) else: self.vocab = vocab self.premises = self._pad(all_premise_tokens) @@ -2047,7 +2574,7 @@ def __len__(self): return len(self.premises) def load_data_snli(batch_size, num_steps=50): - """下载SNLI数据集并返回数据迭代器和词表。 + """下载SNLI数据集并返回数据迭代器和词表 Defined in :numref:`sec_natural-language-inference-and-dataset`""" num_workers = d2l.get_dataloader_workers() @@ -2065,7 +2592,7 @@ def load_data_snli(batch_size, num_steps=50): return train_iter, test_iter, train_set.vocab def predict_snli(net, vocab, premise, hypothesis): - """预测前提和假设之间的逻辑关系。 + """预测前提和假设之间的逻辑关系 Defined in :numref:`sec_natural-language-inference-attention`""" net.eval() From be7a0717a90509df6830c28f8da9b3f38dfc6cc0 Mon Sep 17 00:00:00 2001 From: Aston Zhang Date: Wed, 8 Dec 2021 06:43:22 +0000 Subject: [PATCH 14/21] bump ver to 2.0.0beta0 --- config.ini | 2 +- d2l/__init__.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/config.ini b/config.ini index 3a2b769e4..4c2ef7d89 100644 --- a/config.ini +++ b/config.ini @@ -8,7 +8,7 @@ author = Aston Zhang, Zachary C. Lipton, Mu Li, and Alexander J. Smola copyright = 2021, All authors. Licensed under CC-BY-SA-4.0 and MIT-0. -release = 2.0.0-alpha2 +release = 2.0.0-beta0 lang = zh diff --git a/d2l/__init__.py b/d2l/__init__.py index 0485dc8b5..685a93002 100644 --- a/d2l/__init__.py +++ b/d2l/__init__.py @@ -8,4 +8,4 @@ """ -__version__ = "2.0.0-alpha2" +__version__ = "2.0.0-beta0" From 463ed6460a0d165125cf767c7295bc95babf8a83 Mon Sep 17 00:00:00 2001 From: Anirudh Dagar Date: Wed, 8 Dec 2021 07:46:51 +0100 Subject: [PATCH 15/21] [PyTorch] Sync loss with reduction='none' and L2Loss behaviour (#1037) * BUG: Fix loss and metric calculation * Sync MSELoss with MXNet L2Loss * Sync MSELoss with MXNet L2Loss undefit-overfit * Sync MSELoss with MXNet L2Loss softmax-regression-scratch * Update d2l lib for train_epoch_ch3 * Use reduction=none minibatch sgd * Update d2l lib for train_concise_ch11 * Fix loss computation in weight decay train_concise * drop 1/2 factor attention * add eqmse label * drop 1/2 underfit-overfit * drop 1/2 softmax-scratch * drop 1/2 * drop 1/2 tf * drop 1/2 tf minibatch sgd * 0.17.1 * translate minibatch sgd * translate sequence Co-authored-by: Aston Zhang --- .../nadaraya-waston.md | 7 ++--- chapter_linear-networks/linear-regression.md | 1 + .../softmax-regression-scratch.md | 6 ++-- .../underfit-overfit.md | 4 +-- .../weight-decay.md | 9 +++--- chapter_optimization/minibatch-sgd.md | 28 +++++++++---------- chapter_recurrent-neural-networks/sequence.md | 11 ++++---- static/build.yml | 2 +- 8 files changed, 32 insertions(+), 36 deletions(-) diff --git a/chapter_attention-mechanisms/nadaraya-waston.md b/chapter_attention-mechanisms/nadaraya-waston.md index c041696f9..7d41b90e5 100644 --- a/chapter_attention-mechanisms/nadaraya-waston.md +++ b/chapter_attention-mechanisms/nadaraya-waston.md @@ -74,6 +74,7 @@ y_truth = f(x_test) # 测试样本的真实输出 n_test = len(x_test) # 测试样本数 n_test ``` + ```{.python .input} #@tab pytorch def f(x): @@ -387,7 +388,6 @@ class NWKernelRegression(tf.keras.layers.Layer): return tf.squeeze(tf.matmul(tf.expand_dims(self.attention_weights, axis=1), tf.expand_dims(values, axis=-1))) ``` - ### 训练 接下来,[**将训练数据集变换为键和值**]用于训练注意力模型。 @@ -461,8 +461,7 @@ animator = d2l.Animator(xlabel='epoch', ylabel='loss', xlim=[1, 5]) for epoch in range(5): trainer.zero_grad() - # L2Loss=1/2*MSELoss - l = loss(net(x_train, keys, values), y_train) / 2 + l = loss(net(x_train, keys, values), y_train) l.sum().backward() trainer.step() print(f'epoch {epoch + 1}, loss {float(l.sum()):.6f}') @@ -479,7 +478,7 @@ animator = d2l.Animator(xlabel='epoch', ylabel='loss', xlim=[1, 5]) for epoch in range(5): with tf.GradientTape() as t: - loss = loss_object(y_train, net(x_train, keys, values))/2 * len(y_train) + loss = loss_object(y_train, net(x_train, keys, values)) * len(y_train) grads = t.gradient(loss, net.trainable_variables) optimizer.apply_gradients(zip(grads, net.trainable_variables)) print(f'epoch {epoch + 1}, loss {float(loss):.6f}') diff --git a/chapter_linear-networks/linear-regression.md b/chapter_linear-networks/linear-regression.md index f944221b3..2465cbd2e 100644 --- a/chapter_linear-networks/linear-regression.md +++ b/chapter_linear-networks/linear-regression.md @@ -111,6 +111,7 @@ $${\hat{\mathbf{y}}} = \mathbf{X} \mathbf{w} + b$$ 平方误差可以定义为以下公式: $$l^{(i)}(\mathbf{w}, b) = \frac{1}{2} \left(\hat{y}^{(i)} - y^{(i)}\right)^2.$$ +:eqlabel:`eq_mse` 常数$\frac{1}{2}$不会带来本质的差别,但这样在形式上稍微简单一些 (因为当我们对损失函数求导后常数系数为1)。 diff --git a/chapter_linear-networks/softmax-regression-scratch.md b/chapter_linear-networks/softmax-regression-scratch.md index 622458365..dc028fa99 100644 --- a/chapter_linear-networks/softmax-regression-scratch.md +++ b/chapter_linear-networks/softmax-regression-scratch.md @@ -357,15 +357,13 @@ def train_epoch_ch3(net, train_iter, loss, updater): #@save if isinstance(updater, torch.optim.Optimizer): # 使用PyTorch内置的优化器和损失函数 updater.zero_grad() - l.backward() + l.sum().backward() updater.step() - metric.add(float(l) * len(y), accuracy(y_hat, y), - y.size().numel()) else: # 使用定制的优化器和损失函数 l.sum().backward() updater(X.shape[0]) - metric.add(float(l.sum()), accuracy(y_hat, y), y.numel()) + metric.add(float(l.sum()), accuracy(y_hat, y), y.numel()) # 返回训练损失和训练精度 return metric[0] / metric[2], metric[1] / metric[2] ``` diff --git a/chapter_multilayer-perceptrons/underfit-overfit.md b/chapter_multilayer-perceptrons/underfit-overfit.md index 05e050d21..9766ba151 100644 --- a/chapter_multilayer-perceptrons/underfit-overfit.md +++ b/chapter_multilayer-perceptrons/underfit-overfit.md @@ -391,7 +391,7 @@ def train(train_features, test_features, train_labels, test_labels, #@tab pytorch def train(train_features, test_features, train_labels, test_labels, num_epochs=400): - loss = nn.MSELoss() + loss = nn.MSELoss(reduction='none') input_shape = train_features.shape[-1] # 不设置偏置,因为我们已经在多项式特征中实现了它 net = nn.Sequential(nn.Linear(input_shape, 1, bias=False)) @@ -400,7 +400,7 @@ def train(train_features, test_features, train_labels, test_labels, batch_size) test_iter = d2l.load_array((test_features, test_labels.reshape(-1,1)), batch_size, is_train=False) - trainer = torch.optim.SGD(net.parameters(), lr=0.01) + trainer = torch.optim.SGD(net.parameters(), lr=0.001) animator = d2l.Animator(xlabel='epoch', ylabel='loss', yscale='log', xlim=[1, num_epochs], ylim=[1e-3, 1e2], legend=['train', 'test']) diff --git a/chapter_multilayer-perceptrons/weight-decay.md b/chapter_multilayer-perceptrons/weight-decay.md index 2b5751387..e3e50ace8 100644 --- a/chapter_multilayer-perceptrons/weight-decay.md +++ b/chapter_multilayer-perceptrons/weight-decay.md @@ -361,7 +361,7 @@ def train_concise(wd): net = nn.Sequential(nn.Linear(num_inputs, 1)) for param in net.parameters(): param.data.normal_() - loss = nn.MSELoss() + loss = nn.MSELoss(reduction='none') num_epochs, lr = 100, 0.003 # 偏置参数没有衰减 trainer = torch.optim.SGD([ @@ -373,11 +373,12 @@ def train_concise(wd): for X, y in train_iter: trainer.zero_grad() l = loss(net(X), y) - l.backward() + l.sum().backward() trainer.step() if (epoch + 1) % 5 == 0: - animator.add(epoch + 1, (d2l.evaluate_loss(net, train_iter, loss), - d2l.evaluate_loss(net, test_iter, loss))) + animator.add(epoch + 1, + (d2l.evaluate_loss(net, train_iter, loss), + d2l.evaluate_loss(net, test_iter, loss))) print('w的L2范数:', net[0].weight.norm().item()) ``` diff --git a/chapter_optimization/minibatch-sgd.md b/chapter_optimization/minibatch-sgd.md index ce037d3d4..264ba1876 100644 --- a/chapter_optimization/minibatch-sgd.md +++ b/chapter_optimization/minibatch-sgd.md @@ -95,7 +95,7 @@ C = tf.Variable(d2l.normal([256, 256], 0, 1)) 按元素分配只需遍历分别为$\mathbf{B}$和$\mathbf{C}$的所有行和列,即可将该值分配给$\mathbf{A}$。 ```{.python .input} -# ComputeA=BConeelementatatime +# 逐元素计算A=BC timer.start() for i in range(256): for j in range(256): @@ -106,7 +106,7 @@ timer.stop() ```{.python .input} #@tab pytorch -# ComputeA=BConeelementatatime +# 逐元素计算A=BC timer.start() for i in range(256): for j in range(256): @@ -116,7 +116,7 @@ timer.stop() ```{.python .input} #@tab tensorflow -# ComputeA=BConeelementatatime +# 逐元素计算A=BC timer.start() for i in range(256): for j in range(256): @@ -127,7 +127,7 @@ timer.stop() 更快的策略是执行按列分配。 ```{.python .input} -# ComputeA=BConecolumnatatime +# 逐列计算A=BC timer.start() for j in range(256): A[:, j] = np.dot(B, C[:, j]) @@ -137,7 +137,7 @@ timer.stop() ```{.python .input} #@tab pytorch -# ComputeA=BConecolumnatatime +# 逐列计算A=BC timer.start() for j in range(256): A[:, j] = torch.mv(B, C[:, j]) @@ -531,8 +531,8 @@ def train_concise_ch11(trainer_fn, hyperparams, data_iter, num_epochs=4): optimizer = trainer_fn(net.parameters(), **hyperparams) - loss = nn.MSELoss() - # L2Loss=1/2*MSELoss + # 注意:MSELoss计算平方误差时不带系数1/2 + loss = nn.MSELoss(reduction='none') animator = d2l.Animator(xlabel='epoch', ylabel='loss', xlim=[0, num_epochs], ylim=[0.22, 0.35]) n, timer = 0, d2l.Timer() @@ -541,14 +541,14 @@ def train_concise_ch11(trainer_fn, hyperparams, data_iter, num_epochs=4): optimizer.zero_grad() out = net(X) y = y.reshape(out.shape) - l = loss(out, y)/2 - l.backward() + l = loss(out, y) + l.mean().backward() optimizer.step() n += X.shape[0] if n % 200 == 0: timer.stop() animator.add(n/X.shape[0]/len(data_iter), - (d2l.evaluate_loss(net, data_iter, loss)/2,)) + (d2l.evaluate_loss(net, data_iter, loss),)) timer.start() print(f'loss: {animator.Y[0][-1]:.3f}, {timer.avg():.3f} sec/epoch') ``` @@ -562,10 +562,8 @@ def train_concise_ch11(trainer_fn, hyperparams, data_iter, num_epochs=2): net.add(tf.keras.layers.Dense(1, kernel_initializer=tf.random_normal_initializer(stddev=0.01))) optimizer = trainer_fn(**hyperparams) + # 注意:MeanSquaredError计算平方误差时不带系数1/2 loss = tf.keras.losses.MeanSquaredError() - # 注意:L2Loss=1/2*MSELoss。 - # TensorFlow的MSE损失与MXNet的L2损失大概相差2倍。 - # 因此,我们将TensorFlow中的损失减半 animator = d2l.Animator(xlabel='epoch', ylabel='loss', xlim=[0, num_epochs], ylim=[0.22, 0.35]) n, timer = 0, d2l.Timer() @@ -573,7 +571,7 @@ def train_concise_ch11(trainer_fn, hyperparams, data_iter, num_epochs=2): for X, y in data_iter: with tf.GradientTape() as g: out = net(X) - l = loss(y, out)/2 + l = loss(y, out) params = net.trainable_variables grads = g.gradient(l, params) optimizer.apply_gradients(zip(grads, params)) @@ -582,7 +580,7 @@ def train_concise_ch11(trainer_fn, hyperparams, data_iter, num_epochs=2): timer.stop() p = n/X.shape[0] q = p/tf.data.experimental.cardinality(data_iter).numpy() - r = (d2l.evaluate_loss(net, data_iter, loss)/2,) + r = (d2l.evaluate_loss(net, data_iter, loss),) animator.add(q, r) timer.start() print(f'loss: {animator.Y[0][-1]:.3f}, {timer.avg():.3f} sec/epoch') diff --git a/chapter_recurrent-neural-networks/sequence.md b/chapter_recurrent-neural-networks/sequence.md index 95121d091..7b3ad4368 100644 --- a/chapter_recurrent-neural-networks/sequence.md +++ b/chapter_recurrent-neural-networks/sequence.md @@ -267,8 +267,8 @@ def get_net(): net.apply(init_weights) return net -# 平方损失 -loss = nn.MSELoss() +# 平方损失。注意:MSELoss计算平方误差时不带系数1/2 +loss = nn.MSELoss(reduction='none') ``` ```{.python .input} @@ -279,7 +279,7 @@ def get_net(): tf.keras.layers.Dense(1)]) return net -# 最小均方损失 +# 最小均方损失。注意:MSELoss计算平方误差时不带系数1/2 loss = tf.keras.losses.MeanSquaredError() ``` @@ -310,7 +310,7 @@ def train(net, train_iter, loss, epochs, lr): for X, y in train_iter: trainer.zero_grad() l = loss(net(X), y) - l.backward() + l.sum().backward() trainer.step() print(f'epoch {epoch + 1}, ' f'loss: {d2l.evaluate_loss(net, train_iter, loss):f}') @@ -327,8 +327,7 @@ def train(net, train_iter, loss, epochs, lr): for X, y in train_iter: with tf.GradientTape() as g: out = net(X) - # 注:L2损失=1/2*MSE损失 - l = loss(y, out) / 2 + l = loss(y, out) params = net.trainable_variables grads = g.gradient(l, params) trainer.apply_gradients(zip(grads, params)) diff --git a/static/build.yml b/static/build.yml index 06fb2f7ea..02ae4f590 100644 --- a/static/build.yml +++ b/static/build.yml @@ -2,7 +2,7 @@ dependencies: - python=3.8 - pip - pip: - - d2l==0.17.0.post0 + - d2l==0.17.1 - git+https://github.com/d2l-ai/d2l-book - mxnet-cu101==1.7.0 - torch==1.8.1+cu101 From e320bf8ac3dfe2ab1a92668fdb2677fc49ab7e12 Mon Sep 17 00:00:00 2001 From: Aston Zhang Date: Wed, 8 Dec 2021 06:47:29 +0000 Subject: [PATCH 16/21] sync lib --- d2l/tensorflow.py | 8 +++----- d2l/torch.py | 16 +++++++--------- 2 files changed, 10 insertions(+), 14 deletions(-) diff --git a/d2l/tensorflow.py b/d2l/tensorflow.py index b11d750f9..b491e37e4 100644 --- a/d2l/tensorflow.py +++ b/d2l/tensorflow.py @@ -1352,10 +1352,8 @@ def train_concise_ch11(trainer_fn, hyperparams, data_iter, num_epochs=2): net.add(tf.keras.layers.Dense(1, kernel_initializer=tf.random_normal_initializer(stddev=0.01))) optimizer = trainer_fn(**hyperparams) + # 注意:MeanSquaredError计算平方误差时不带系数1/2 loss = tf.keras.losses.MeanSquaredError() - # 注意:L2Loss=1/2*MSELoss。 - # TensorFlow的MSE损失与MXNet的L2损失大概相差2倍。 - # 因此,我们将TensorFlow中的损失减半 animator = d2l.Animator(xlabel='epoch', ylabel='loss', xlim=[0, num_epochs], ylim=[0.22, 0.35]) n, timer = 0, d2l.Timer() @@ -1363,7 +1361,7 @@ def train_concise_ch11(trainer_fn, hyperparams, data_iter, num_epochs=2): for X, y in data_iter: with tf.GradientTape() as g: out = net(X) - l = loss(y, out)/2 + l = loss(y, out) params = net.trainable_variables grads = g.gradient(l, params) optimizer.apply_gradients(zip(grads, params)) @@ -1372,7 +1370,7 @@ def train_concise_ch11(trainer_fn, hyperparams, data_iter, num_epochs=2): timer.stop() p = n/X.shape[0] q = p/tf.data.experimental.cardinality(data_iter).numpy() - r = (d2l.evaluate_loss(net, data_iter, loss)/2,) + r = (d2l.evaluate_loss(net, data_iter, loss),) animator.add(q, r) timer.start() print(f'loss: {animator.Y[0][-1]:.3f}, {timer.avg():.3f} sec/epoch') diff --git a/d2l/torch.py b/d2l/torch.py index 5961c39cf..77d4b0525 100644 --- a/d2l/torch.py +++ b/d2l/torch.py @@ -259,15 +259,13 @@ def train_epoch_ch3(net, train_iter, loss, updater): if isinstance(updater, torch.optim.Optimizer): # 使用PyTorch内置的优化器和损失函数 updater.zero_grad() - l.backward() + l.sum().backward() updater.step() - metric.add(float(l) * len(y), accuracy(y_hat, y), - y.size().numel()) else: # 使用定制的优化器和损失函数 l.sum().backward() updater(X.shape[0]) - metric.add(float(l.sum()), accuracy(y_hat, y), y.numel()) + metric.add(float(l.sum()), accuracy(y_hat, y), y.numel()) # 返回训练损失和训练精度 return metric[0] / metric[2], metric[1] / metric[2] @@ -1415,8 +1413,8 @@ def init_weights(m): optimizer = trainer_fn(net.parameters(), **hyperparams) - loss = nn.MSELoss() - # L2Loss=1/2*MSELoss + # 注意:MSELoss计算平方误差时不带系数1/2 + loss = nn.MSELoss(reduction='none') animator = d2l.Animator(xlabel='epoch', ylabel='loss', xlim=[0, num_epochs], ylim=[0.22, 0.35]) n, timer = 0, d2l.Timer() @@ -1425,14 +1423,14 @@ def init_weights(m): optimizer.zero_grad() out = net(X) y = y.reshape(out.shape) - l = loss(out, y)/2 - l.backward() + l = loss(out, y) + l.mean().backward() optimizer.step() n += X.shape[0] if n % 200 == 0: timer.stop() animator.add(n/X.shape[0]/len(data_iter), - (d2l.evaluate_loss(net, data_iter, loss)/2,)) + (d2l.evaluate_loss(net, data_iter, loss),)) timer.start() print(f'loss: {animator.Y[0][-1]:.3f}, {timer.avg():.3f} sec/epoch') From df7ff6d553ff09fee8d726c1be6dfbd9bb07390a Mon Sep 17 00:00:00 2001 From: Aston Zhang Date: Wed, 8 Dec 2021 06:49:57 +0000 Subject: [PATCH 17/21] fix 0.17.1 in installation --- chapter_installation/index.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/chapter_installation/index.md b/chapter_installation/index.md index 7e90b4824..ed8809998 100644 --- a/chapter_installation/index.md +++ b/chapter_installation/index.md @@ -109,7 +109,7 @@ pip install tensorflow-probability==0.11.1 我们的下一步是安装`d2l`包,以方便调取本书中经常使用的函数和类: ```bash -pip install d2l==0.17.0 +pip install d2l==0.17.1 ``` From a98140ecedb4588b9d0101d5841298535cba0bc0 Mon Sep 17 00:00:00 2001 From: xiaotinghe Date: Thu, 9 Dec 2021 02:11:33 +0800 Subject: [PATCH 18/21] remove ` in comments (#1041) Co-authored-by: Ubuntu --- .../attention-scoring-functions.md | 70 +++++------ .../bahdanau-attention.md | 70 +++++------ .../multihead-attention.md | 114 +++++++++--------- .../nadaraya-waston.md | 74 ++++++------ .../self-attention-and-positional-encoding.md | 6 +- chapter_attention-mechanisms/transformer.md | 32 ++--- .../multiple-gpus.md | 4 +- chapter_computer-vision/anchor.md | 32 ++--- chapter_computer-vision/fine-tuning.md | 2 +- chapter_computer-vision/kaggle-cifar10.md | 4 +- .../object-detection-dataset.md | 4 +- chapter_computer-vision/ssd.md | 12 +- chapter_convolutional-modern/batch-norm.md | 28 ++--- chapter_convolutional-modern/densenet.md | 6 +- chapter_convolutional-modern/googlenet.md | 6 +- chapter_convolutional-modern/resnet.md | 2 +- chapter_convolutional-modern/vgg.md | 2 +- .../model-construction.md | 38 +++--- .../parameters.md | 2 +- .../linear-regression-concise.md | 6 +- .../linear-regression-scratch.md | 14 +-- .../softmax-regression-scratch.md | 4 +- .../underfit-overfit.md | 4 +- .../weight-decay.md | 2 +- .../natural-language-inference-attention.md | 28 ++--- .../natural-language-inference-bert.md | 4 +- .../sentiment-analysis-rnn.md | 12 +- .../bert-dataset.md | 16 +-- .../bert.md | 10 +- .../word-embedding-dataset.md | 4 +- chapter_optimization/gd.md | 2 +- chapter_optimization/lr-scheduler.md | 6 +- chapter_preliminaries/autograd.md | 20 +-- chapter_preliminaries/calculus.md | 2 +- chapter_preliminaries/linear-algebra.md | 6 +- chapter_recurrent-modern/seq2seq.md | 64 +++++----- .../language-models-and-dataset.md | 8 +- .../rnn-concise.md | 14 +-- .../rnn-scratch.md | 38 +++--- chapter_recurrent-neural-networks/sequence.md | 2 +- .../text-preprocessing.md | 2 +- 41 files changed, 388 insertions(+), 388 deletions(-) diff --git a/chapter_attention-mechanisms/attention-scoring-functions.md b/chapter_attention-mechanisms/attention-scoring-functions.md index c86ba2b65..aa0030fa6 100644 --- a/chapter_attention-mechanisms/attention-scoring-functions.md +++ b/chapter_attention-mechanisms/attention-scoring-functions.md @@ -81,7 +81,7 @@ import tensorflow as tf #@save def masked_softmax(X, valid_lens): """通过在最后一个轴上掩蔽元素来执行softmax操作""" - # `X`:3D张量,`valid_lens`:1D或2D张量 + # X:3D张量,valid_lens:1D或2D张量 if valid_lens is None: return npx.softmax(X) else: @@ -101,7 +101,7 @@ def masked_softmax(X, valid_lens): #@save def masked_softmax(X, valid_lens): """通过在最后一个轴上掩蔽元素来执行softmax操作""" - # `X`:3D张量,`valid_lens`:1D或2D张量 + # X:3D张量,valid_lens:1D或2D张量 if valid_lens is None: return nn.functional.softmax(X, dim=-1) else: @@ -121,7 +121,7 @@ def masked_softmax(X, valid_lens): #@save def masked_softmax(X, valid_lens): """通过在最后一个轴上掩蔽元素来执行softmax操作""" - # `X`:3D张量,`valid_lens`:1D或2D张量 + # X:3D张量,valid_lens:1D或2D张量 if valid_lens is None: return tf.nn.softmax(X, axis=-1) else: @@ -210,17 +210,17 @@ class AdditiveAttention(nn.Block): def forward(self, queries, keys, values, valid_lens): queries, keys = self.W_q(queries), self.W_k(keys) # 在维度扩展后, - # `queries`的形状:(`batch_size`,查询的个数,1,`num_hidden`) - # `key`的形状:(`batch_size`,1,“键-值”对的个数,`num_hiddens`) + # queries的形状:(batch_size,查询的个数,1,num_hidden) + # key的形状:(batch_size,1,“键-值”对的个数,num_hiddens) # 使用广播的方式进行求和 features = np.expand_dims(queries, axis=2) + np.expand_dims( keys, axis=1) features = np.tanh(features) - # `self.w_v`仅有一个输出,因此从形状中移除最后那个维度。 - # `scores`的形状:(`batch_size`,查询的个数,“键-值”对的个数) + # self.w_v仅有一个输出,因此从形状中移除最后那个维度。 + # scores的形状:(batch_size,查询的个数,“键-值”对的个数) scores = np.squeeze(self.w_v(features), axis=-1) self.attention_weights = masked_softmax(scores, valid_lens) - # `values`的形状:(`batch_size`,“键-值”对的个数,值的维度) + # values的形状:(batch_size,“键-值”对的个数,值的维度) return npx.batch_dot(self.dropout(self.attention_weights), values) ``` @@ -239,16 +239,16 @@ class AdditiveAttention(nn.Module): def forward(self, queries, keys, values, valid_lens): queries, keys = self.W_q(queries), self.W_k(keys) # 在维度扩展后, - # `queries`的形状:(`batch_size`,查询的个数,1,`num_hidden`) - # `key`的形状:(`batch_size`,1,“键-值”对的个数,`num_hiddens`) + # queries的形状:(batch_size,查询的个数,1,num_hidden) + # key的形状:(batch_size,1,“键-值”对的个数,num_hiddens) # 使用广播方式进行求和 features = queries.unsqueeze(2) + keys.unsqueeze(1) features = torch.tanh(features) - # `self.w_v`仅有一个输出,因此从形状中移除最后那个维度。 - # `scores`的形状:(`batch_size`,查询的个数,“键-值”对的个数) + # self.w_v仅有一个输出,因此从形状中移除最后那个维度。 + # scores的形状:(batch_size,查询的个数,“键-值”对的个数) scores = self.w_v(features).squeeze(-1) self.attention_weights = masked_softmax(scores, valid_lens) - # `values`的形状:(`batch_size`,“键-值”对的个数,值的维度) + # values的形状:(batch_size,“键-值”对的个数,值的维度) return torch.bmm(self.dropout(self.attention_weights), values) ``` @@ -267,17 +267,17 @@ class AdditiveAttention(tf.keras.layers.Layer): def call(self, queries, keys, values, valid_lens, **kwargs): queries, keys = self.W_q(queries), self.W_k(keys) # 在维度扩展后, - # `queries`的形状:(`batch_size`,查询的个数,1,`num_hidden`) - # `key`的形状:(`batch_size`,1,“键-值”对的个数,`num_hiddens`) + # queries的形状:(batch_size,查询的个数,1,num_hidden) + # key的形状:(batch_size,1,“键-值”对的个数,num_hiddens) # 使用广播方式进行求和 features = tf.expand_dims(queries, axis=2) + tf.expand_dims( keys, axis=1) features = tf.nn.tanh(features) - # `self.w_v`仅有一个输出,因此从形状中移除最后那个维度。 - # `scores`的形状:(`batch_size`,查询的个数,“键-值”对的个数) + # self.w_v仅有一个输出,因此从形状中移除最后那个维度。 + # scores的形状:(batch_size,查询的个数,“键-值”对的个数) scores = tf.squeeze(self.w_v(features), axis=-1) self.attention_weights = masked_softmax(scores, valid_lens) - # `values`的形状:(`batch_size`,“键-值”对的个数,值的维度) + # values的形状:(batch_size,“键-值”对的个数,值的维度) return tf.matmul(self.dropout( self.attention_weights, **kwargs), values) ``` @@ -289,7 +289,7 @@ class AdditiveAttention(tf.keras.layers.Layer): ```{.python .input} queries, keys = d2l.normal(0, 1, (2, 1, 20)), d2l.ones((2, 10, 2)) -# `values`的小批量数据集中,两个值矩阵是相同的 +# values的小批量数据集中,两个值矩阵是相同的 values = np.arange(40).reshape(1, 10, 4).repeat(2, axis=0) valid_lens = d2l.tensor([2, 6]) @@ -301,7 +301,7 @@ attention(queries, keys, values, valid_lens) ```{.python .input} #@tab pytorch queries, keys = d2l.normal(0, 1, (2, 1, 20)), d2l.ones((2, 10, 2)) -# `values`的小批量,两个值矩阵是相同的 +# values的小批量,两个值矩阵是相同的 values = torch.arange(40, dtype=torch.float32).reshape(1, 10, 4).repeat( 2, 1, 1) valid_lens = d2l.tensor([2, 6]) @@ -315,7 +315,7 @@ attention(queries, keys, values, valid_lens) ```{.python .input} #@tab tensorflow queries, keys = tf.random.normal(shape=(2, 1, 20)), tf.ones((2, 10, 2)) -# `values`的小批量,两个值矩阵是相同的 +# values的小批量,两个值矩阵是相同的 values = tf.repeat(tf.reshape( tf.range(40, dtype=tf.float32), shape=(1, 10, 4)), repeats=2, axis=0) valid_lens = tf.constant([2, 6]) @@ -369,13 +369,13 @@ class DotProductAttention(nn.Block): super(DotProductAttention, self).__init__(**kwargs) self.dropout = nn.Dropout(dropout) - # `queries`的形状:(`batch_size`,查询的个数,`d`) - # `keys`的形状:(`batch_size`,“键-值”对的个数,`d`) - # `values`的形状:(`batch_size`,“键-值”对的个数,值的维度) - # `valid_lens`的形状:(`batch_size`,)或者(`batch_size`,查询的个数) + # queries的形状:(batch_size,查询的个数,d) + # keys的形状:(batch_size,“键-值”对的个数,d) + # values的形状:(batch_size,“键-值”对的个数,值的维度) + # valid_lens的形状:(batch_size,)或者(batch_size,查询的个数) def forward(self, queries, keys, values, valid_lens=None): d = queries.shape[-1] - # 设置`transpose_b=True`为了交换`keys`的最后两个维度 + # 设置transpose_b=True为了交换keys的最后两个维度 scores = npx.batch_dot(queries, keys, transpose_b=True) / math.sqrt(d) self.attention_weights = masked_softmax(scores, valid_lens) return npx.batch_dot(self.dropout(self.attention_weights), values) @@ -390,13 +390,13 @@ class DotProductAttention(nn.Module): super(DotProductAttention, self).__init__(**kwargs) self.dropout = nn.Dropout(dropout) - # `queries`的形状:(`batch_size`,查询的个数,`d`) - # `keys`的形状:(`batch_size`,“键-值”对的个数,`d`) - # `values`的形状:(`batch_size`,“键-值”对的个数,值的维度) - # `valid_lens`的形状:(`batch_size`,)或者(`batch_size`,查询的个数) + # queries的形状:(batch_size,查询的个数,d) + # keys的形状:(batch_size,“键-值”对的个数,d) + # values的形状:(batch_size,“键-值”对的个数,值的维度) + # valid_lens的形状:(batch_size,)或者(batch_size,查询的个数) def forward(self, queries, keys, values, valid_lens=None): d = queries.shape[-1] - # 设置`transpose_b=True`为了交换`keys`的最后两个维度 + # 设置transpose_b=True为了交换keys的最后两个维度 scores = torch.bmm(queries, keys.transpose(1,2)) / math.sqrt(d) self.attention_weights = masked_softmax(scores, valid_lens) return torch.bmm(self.dropout(self.attention_weights), values) @@ -411,10 +411,10 @@ class DotProductAttention(tf.keras.layers.Layer): super().__init__(**kwargs) self.dropout = tf.keras.layers.Dropout(dropout) - # `queries`的形状:(`batch_size`,查询的个数,`d`) - # `keys`的形状:(`batch_size`,“键-值”对的个数,`d`) - # `values`的形状:(`batch_size`,“键-值”对的个数,值的维度) - # `valid_lens`的形状:(`batch_size`,)或者(`batch_size`,查询的个数) + # queries的形状:(batch_size,查询的个数,d) + # keys的形状:(batch_size,“键-值”对的个数,d) + # values的形状:(batch_size,“键-值”对的个数,值的维度) + # valid_lens的形状:(batch_size,)或者(batch_size,查询的个数) def call(self, queries, keys, values, valid_lens, **kwargs): d = queries.shape[-1] scores = tf.matmul(queries, keys, transpose_b=True)/tf.math.sqrt( diff --git a/chapter_attention-mechanisms/bahdanau-attention.md b/chapter_attention-mechanisms/bahdanau-attention.md index f7ef3469d..8be120237 100644 --- a/chapter_attention-mechanisms/bahdanau-attention.md +++ b/chapter_attention-mechanisms/bahdanau-attention.md @@ -105,33 +105,33 @@ class Seq2SeqAttentionDecoder(AttentionDecoder): self.dense = nn.Dense(vocab_size, flatten=False) def init_state(self, enc_outputs, enc_valid_lens, *args): - # `outputs`的形状为(`num_steps`,`batch_size`,`num_hiddens`) - # `hidden_state[0]`的形状为(`num_layers`,`batch_size`,`num_hiddens`) + # outputs的形状为(num_steps,batch_size,num_hiddens) + # hidden_state[0]的形状为(num_layers,batch_size,num_hiddens) outputs, hidden_state = enc_outputs return (outputs.swapaxes(0, 1), hidden_state, enc_valid_lens) def forward(self, X, state): - # `enc_outputs`的形状为(`batch_size`,`num_steps`,`num_hiddens`). - # `hidden_state[0]`的形状为(`num_layers`,`batch_size`, - # `num_hiddens`) + # enc_outputs的形状为(batch_size,num_steps,num_hiddens). + # hidden_state[0]的形状为(num_layers,batch_size, + # num_hiddens) enc_outputs, hidden_state, enc_valid_lens = state - # 输出`X`的形状为(`num_steps`,`batch_size`,`embed_size`) + # 输出X的形状为(num_steps,batch_size,embed_size) X = self.embedding(X).swapaxes(0, 1) outputs, self._attention_weights = [], [] for x in X: - # `query`的形状为(`batch_size`,1,`num_hiddens`) + # query的形状为(batch_size,1,num_hiddens) query = np.expand_dims(hidden_state[0][-1], axis=1) - # `context`的形状为(`batch_size`,1,`num_hiddens`) + # context的形状为(batch_size,1,num_hiddens) context = self.attention( query, enc_outputs, enc_outputs, enc_valid_lens) # 在特征维度上连结 x = np.concatenate((context, np.expand_dims(x, axis=1)), axis=-1) - # 将`x`变形为(1,`batch_size`,`embed_size`+`num_hiddens`) + # 将x变形为(1,batch_size,embed_size+num_hiddens) out, hidden_state = self.rnn(x.swapaxes(0, 1), hidden_state) outputs.append(out) self._attention_weights.append(self.attention.attention_weights) - # 全连接层变换后,`outputs`的形状为 - # (`num_steps`,`batch_size`,`vocab_size`) + # 全连接层变换后,outputs的形状为 + # (num_steps,batch_size,vocab_size) outputs = self.dense(np.concatenate(outputs, axis=0)) return outputs.swapaxes(0, 1), [enc_outputs, hidden_state, enc_valid_lens] @@ -156,33 +156,33 @@ class Seq2SeqAttentionDecoder(AttentionDecoder): self.dense = nn.Linear(num_hiddens, vocab_size) def init_state(self, enc_outputs, enc_valid_lens, *args): - # `outputs`的形状为(`batch_size`,`num_steps`,`num_hiddens`). - # `hidden_state`的形状为(`num_layers`,`batch_size`,`num_hiddens`) + # outputs的形状为(batch_size,num_steps,num_hiddens). + # hidden_state的形状为(num_layers,batch_size,num_hiddens) outputs, hidden_state = enc_outputs return (outputs.permute(1, 0, 2), hidden_state, enc_valid_lens) def forward(self, X, state): - # `enc_outputs`的形状为(`batch_size`,`num_steps`,`num_hiddens`). - # `hidden_state`的形状为(`num_layers`,`batch_size`, - # `num_hiddens`) + # enc_outputs的形状为(batch_size,num_steps,num_hiddens). + # hidden_state的形状为(num_layers,batch_size, + # num_hiddens) enc_outputs, hidden_state, enc_valid_lens = state - # 输出`X`的形状为(`num_steps`,`batch_size`,`embed_size`) + # 输出X的形状为(num_steps,batch_size,embed_size) X = self.embedding(X).permute(1, 0, 2) outputs, self._attention_weights = [], [] for x in X: - # `query`的形状为(`batch_size`,1,`num_hiddens`) + # query的形状为(batch_size,1,num_hiddens) query = torch.unsqueeze(hidden_state[-1], dim=1) - # `context`的形状为(`batch_size`,1,`num_hiddens`) + # context的形状为(batch_size,1,num_hiddens) context = self.attention( query, enc_outputs, enc_outputs, enc_valid_lens) # 在特征维度上连结 x = torch.cat((context, torch.unsqueeze(x, dim=1)), dim=-1) - # 将`x`变形为(1,`batch_size`,`embed_size`+`num_hiddens`) + # 将x变形为(1,batch_size,embed_size+num_hiddens) out, hidden_state = self.rnn(x.permute(1, 0, 2), hidden_state) outputs.append(out) self._attention_weights.append(self.attention.attention_weights) - # 全连接层变换后,`outputs`的形状为 - # (`num_steps`,`batch_size`,`vocab_size`) + # 全连接层变换后,outputs的形状为 + # (num_steps,batch_size,vocab_size) outputs = self.dense(torch.cat(outputs, dim=0)) return outputs.permute(1, 0, 2), [enc_outputs, hidden_state, enc_valid_lens] @@ -209,25 +209,25 @@ class Seq2SeqAttentionDecoder(AttentionDecoder): self.dense = tf.keras.layers.Dense(vocab_size) def init_state(self, enc_outputs, enc_valid_lens, *args): - # `outputs`的形状为(`num_steps`,`batch_size`,`num_hiddens`) - # `hidden_state[0]`的形状为(`num_layers`,`batch_size`,`num_hiddens`) + # outputs的形状为(num_steps,batch_size,num_hiddens) + # hidden_state[0]的形状为(num_layers,batch_size,num_hiddens) outputs, hidden_state = enc_outputs return (outputs, hidden_state, enc_valid_lens) def call(self, X, state, **kwargs): - # `enc_outputs`的形状为(`batch_size`,`num_steps`,`num_hiddens`). - # `hidden_state[0]`的形状为(`num_layers`,`batch_size`, - # `num_hiddens`) + # enc_outputs的形状为(batch_size,num_steps,num_hiddens). + # hidden_state[0]的形状为(num_layers,batch_size, + # num_hiddens) enc_outputs, hidden_state, enc_valid_lens = state - # 输出`X`的形状为(`num_steps`,`batch_size`,`embed_size`) - X = self.embedding(X) # 输入`X`的形状为(`batch_size`,`num_steps`) + # 输出X的形状为(num_steps,batch_size,embed_size) + X = self.embedding(X) # 输入X的形状为(batch_size,num_steps) X = tf.transpose(X, perm=(1, 0, 2)) outputs, self._attention_weights = [], [] for x in X: - # `query`的形状为(`batch_size`,1,`num_hiddens`) + # query的形状为(batch_size,1,num_hiddens) query = tf.expand_dims(hidden_state[-1], axis=1) - # `context`的形状为(`batch_size`,1,`num_hiddens`) + # context的形状为(batch_size,1,num_hiddens) context = self.attention(query, enc_outputs, enc_outputs, enc_valid_lens, **kwargs) # 在特征维度上连结 @@ -236,8 +236,8 @@ class Seq2SeqAttentionDecoder(AttentionDecoder): hidden_state = out[1:] outputs.append(out[0]) self._attention_weights.append(self.attention.attention_weights) - # 全连接层变换后,`outputs`的形状为 - # (`num_steps`,`batch_size`,`vocab_size`) + # 全连接层变换后,outputs的形状为 + # (num_steps,batch_size,vocab_size) outputs = self.dense(tf.concat(outputs, axis=1)) return outputs, [enc_outputs, hidden_state, enc_valid_lens] @@ -255,7 +255,7 @@ encoder.initialize() decoder = Seq2SeqAttentionDecoder(vocab_size=10, embed_size=8, num_hiddens=16, num_layers=2) decoder.initialize() -X = d2l.zeros((4, 7)) # (`batch_size`,`num_steps`) +X = d2l.zeros((4, 7)) # (batch_size,num_steps) state = decoder.init_state(encoder(X), None) output, state = decoder(X, state) output.shape, len(state), state[0].shape, len(state[1]), state[1][0].shape @@ -269,7 +269,7 @@ encoder.eval() decoder = Seq2SeqAttentionDecoder(vocab_size=10, embed_size=8, num_hiddens=16, num_layers=2) decoder.eval() -X = d2l.zeros((4, 7), dtype=torch.long) # (`batch_size`,`num_steps`) +X = d2l.zeros((4, 7), dtype=torch.long) # (batch_size,num_steps) state = decoder.init_state(encoder(X), None) output, state = decoder(X, state) output.shape, len(state), state[0].shape, len(state[1]), state[1][0].shape diff --git a/chapter_attention-mechanisms/multihead-attention.md b/chapter_attention-mechanisms/multihead-attention.md index 2d72077c5..43e3f0c2e 100644 --- a/chapter_attention-mechanisms/multihead-attention.md +++ b/chapter_attention-mechanisms/multihead-attention.md @@ -98,27 +98,27 @@ class MultiHeadAttention(nn.Block): self.W_o = nn.Dense(num_hiddens, use_bias=use_bias, flatten=False) def forward(self, queries, keys, values, valid_lens): - # `queries`,`keys`,`values`的形状: - # (`batch_size`,查询或者“键-值”对的个数,`num_hiddens`) - # `valid_lens` 的形状: - # (`batch_size`,)或(`batch_size`,查询的个数) - # 经过变换后,输出的`queries`,`keys`,`values` 的形状: - # (`batch_size`*`num_heads`,查询或者“键-值”对的个数, - # `num_hiddens`/`num_heads`) + # queries,keys,values的形状: + # (batch_size,查询或者“键-值”对的个数,num_hiddens) + # valid_lens 的形状: + # (batch_size,)或(batch_size,查询的个数) + # 经过变换后,输出的queries,keys,values 的形状: + # (batch_size*num_heads,查询或者“键-值”对的个数, + # num_hiddens/num_heads) queries = transpose_qkv(self.W_q(queries), self.num_heads) keys = transpose_qkv(self.W_k(keys), self.num_heads) values = transpose_qkv(self.W_v(values), self.num_heads) if valid_lens is not None: - # 在轴0,将第一项(标量或者矢量)复制`num_heads`次, + # 在轴0,将第一项(标量或者矢量)复制num_heads次, # 然后如此复制第二项,然后诸如此类。 valid_lens = valid_lens.repeat(self.num_heads, axis=0) - # `output`的形状:(`batch_size`*`num_heads`,查询的个数, - # `num_hiddens`/`num_heads`) + # output的形状:(batch_size*num_heads,查询的个数, + # num_hiddens/num_heads) output = self.attention(queries, keys, values, valid_lens) - # `output_concat`的形状:(`batch_size`,查询的个数,`num_hiddens`) + # output_concat的形状:(batch_size,查询的个数,num_hiddens) output_concat = transpose_output(output, self.num_heads) return self.W_o(output_concat) ``` @@ -139,28 +139,28 @@ class MultiHeadAttention(nn.Module): self.W_o = nn.Linear(num_hiddens, num_hiddens, bias=bias) def forward(self, queries, keys, values, valid_lens): - # `queries`,`keys`,`values`的形状: - # (`batch_size`,查询或者“键-值”对的个数,`num_hiddens`) - # `valid_lens` 的形状: - # (`batch_size`,)或(`batch_size`,查询的个数) - # 经过变换后,输出的`queries`,`keys`,`values` 的形状: - # (`batch_size`*`num_heads`,查询或者“键-值”对的个数, - # `num_hiddens`/`num_heads`) + # queries,keys,values的形状: + # (batch_size,查询或者“键-值”对的个数,num_hiddens) + # valid_lens 的形状: + # (batch_size,)或(batch_size,查询的个数) + # 经过变换后,输出的queries,keys,values 的形状: + # (batch_size*num_heads,查询或者“键-值”对的个数, + # num_hiddens/num_heads) queries = transpose_qkv(self.W_q(queries), self.num_heads) keys = transpose_qkv(self.W_k(keys), self.num_heads) values = transpose_qkv(self.W_v(values), self.num_heads) if valid_lens is not None: - # 在轴0,将第一项(标量或者矢量)复制`num_heads`次, + # 在轴0,将第一项(标量或者矢量)复制num_heads次, # 然后如此复制第二项,然后诸如此类。 valid_lens = torch.repeat_interleave( valid_lens, repeats=self.num_heads, dim=0) - # `output`的形状:(`batch_size`*`num_heads`,查询的个数, - # `num_hiddens`/`num_heads`) + # output的形状:(batch_size*num_heads,查询的个数, + # num_hiddens/num_heads) output = self.attention(queries, keys, values, valid_lens) - # `output_concat`的形状:(`batch_size`,查询的个数,`num_hiddens`) + # output_concat的形状:(batch_size,查询的个数,num_hiddens) output_concat = transpose_output(output, self.num_heads) return self.W_o(output_concat) ``` @@ -181,27 +181,27 @@ class MultiHeadAttention(tf.keras.layers.Layer): self.W_o = tf.keras.layers.Dense(num_hiddens, use_bias=bias) def call(self, queries, keys, values, valid_lens, **kwargs): - # `queries`,`keys`,`values`的形状: - # (`batch_size`,查询或者“键-值”对的个数,`num_hiddens`) - # `valid_lens` 的形状: - # (`batch_size`,)或(`batch_size`,查询的个数) - # 经过变换后,输出的`queries`,`keys`,`values` 的形状: - # (`batch_size`*`num_heads`,查询或者“键-值”对的个数, - # `num_hiddens`/`num_heads`) + # queries,keys,values的形状: + # (batch_size,查询或者“键-值”对的个数,num_hiddens) + # valid_lens 的形状: + # (batch_size,)或(batch_size,查询的个数) + # 经过变换后,输出的queries,keys,values 的形状: + # (batch_size*num_heads,查询或者“键-值”对的个数, + # num_hiddens/num_heads) queries = transpose_qkv(self.W_q(queries), self.num_heads) keys = transpose_qkv(self.W_k(keys), self.num_heads) values = transpose_qkv(self.W_v(values), self.num_heads) if valid_lens is not None: - # 在轴0,将第一项(标量或者矢量)复制`num_heads`次, + # 在轴0,将第一项(标量或者矢量)复制num_heads次, # 然后如此复制第二项,然后诸如此类。 valid_lens = tf.repeat(valid_lens, repeats=self.num_heads, axis=0) - # `output`的形状:(`batch_size`*`num_heads`,查询的个数, - # `num_hiddens`/`num_heads`) + # output的形状:(batch_size*num_heads,查询的个数, + # num_hiddens/num_heads) output = self.attention(queries, keys, values, valid_lens, **kwargs) - # `output_concat`的形状:(`batch_size`,查询的个数,`num_hiddens`) + # output_concat的形状:(batch_size,查询的个数,num_hiddens) output_concat = transpose_output(output, self.num_heads) return self.W_o(output_concat) ``` @@ -214,23 +214,23 @@ class MultiHeadAttention(tf.keras.layers.Layer): #@save def transpose_qkv(X, num_heads): """为了多注意力头的并行计算而变换形状""" - # 输入`X`的形状:(`batch_size`,查询或者“键-值”对的个数,`num_hiddens`) - # 输出`X`的形状:(`batch_size`,查询或者“键-值”对的个数,`num_heads`, - # `num_hiddens`/`num_heads`) + # 输入X的形状:(batch_size,查询或者“键-值”对的个数,num_hiddens) + # 输出X的形状:(batch_size,查询或者“键-值”对的个数,num_heads, + # num_hiddens/num_heads) X = X.reshape(X.shape[0], X.shape[1], num_heads, -1) - # 输出`X`的形状:(`batch_size`,`num_heads`,查询或者“键-值”对的个数, - # `num_hiddens`/`num_heads`) + # 输出X的形状:(batch_size,num_heads,查询或者“键-值”对的个数, + # num_hiddens/num_heads) X = X.transpose(0, 2, 1, 3) - # 最终输出的形状:(`batch_size`*`num_heads`,查询或者“键-值”对的个数, - # `num_hiddens`/`num_heads`) + # 最终输出的形状:(batch_size*num_heads,查询或者“键-值”对的个数, + # num_hiddens/num_heads) return X.reshape(-1, X.shape[2], X.shape[3]) #@save def transpose_output(X, num_heads): - """逆转`transpose_qkv`函数的操作""" + """逆转transpose_qkv函数的操作""" X = X.reshape(-1, num_heads, X.shape[1], X.shape[2]) X = X.transpose(0, 2, 1, 3) return X.reshape(X.shape[0], X.shape[1], -1) @@ -241,23 +241,23 @@ def transpose_output(X, num_heads): #@save def transpose_qkv(X, num_heads): """为了多注意力头的并行计算而变换形状""" - # 输入`X`的形状:(`batch_size`,查询或者“键-值”对的个数,`num_hiddens`) - # 输出`X`的形状:(`batch_size`,查询或者“键-值”对的个数,`num_heads`, - # `num_hiddens`/`num_heads`) + # 输入X的形状:(batch_size,查询或者“键-值”对的个数,num_hiddens) + # 输出X的形状:(batch_size,查询或者“键-值”对的个数,num_heads, + # num_hiddens/num_heads) X = X.reshape(X.shape[0], X.shape[1], num_heads, -1) - # 输出`X`的形状:(`batch_size`,`num_heads`,查询或者“键-值”对的个数, - # `num_hiddens`/`num_heads`) + # 输出X的形状:(batch_size,num_heads,查询或者“键-值”对的个数, + # num_hiddens/num_heads) X = X.permute(0, 2, 1, 3) - # 最终输出的形状:(`batch_size`*`num_heads`,查询或者“键-值”对的个数, - # `num_hiddens`/`num_heads`) + # 最终输出的形状:(batch_size*num_heads,查询或者“键-值”对的个数, + # num_hiddens/num_heads) return X.reshape(-1, X.shape[2], X.shape[3]) #@save def transpose_output(X, num_heads): - """逆转`transpose_qkv`函数的操作""" + """逆转transpose_qkv函数的操作""" X = X.reshape(-1, num_heads, X.shape[1], X.shape[2]) X = X.permute(0, 2, 1, 3) return X.reshape(X.shape[0], X.shape[1], -1) @@ -268,23 +268,23 @@ def transpose_output(X, num_heads): #@save def transpose_qkv(X, num_heads): """为了多注意力头的并行计算而变换形状""" - # 输入`X`的形状:(`batch_size`,查询或者“键-值”对的个数,`num_hiddens`) - # 输出`X`的形状:(`batch_size`,查询或者“键-值”对的个数,`num_heads`, - # `num_hiddens`/`num_heads`) + # 输入X的形状:(batch_size,查询或者“键-值”对的个数,num_hiddens) + # 输出X的形状:(batch_size,查询或者“键-值”对的个数,num_heads, + # num_hiddens/num_heads) X = tf.reshape(X, shape=(X.shape[0], X.shape[1], num_heads, -1)) - # 输出`X`的形状:(`batch_size`,`num_heads`,查询或者“键-值”对的个数, - # `num_hiddens`/`num_heads`) + # 输出X的形状:(batch_size,num_heads,查询或者“键-值”对的个数, + # num_hiddens/num_heads) X = tf.transpose(X, perm=(0, 2, 1, 3)) - # 最终输出的形状:(`batch_size`*`num_heads`,查询或者“键-值”对的个数, - # `num_hiddens`/`num_heads`) + # 最终输出的形状:(batch_size*num_heads,查询或者“键-值”对的个数, + # num_hiddens/num_heads) return tf.reshape(X, shape=(-1, X.shape[2], X.shape[3])) #@save def transpose_output(X, num_heads): - """逆转`transpose_qkv`函数的操作""" + """逆转transpose_qkv函数的操作""" X = tf.reshape(X, shape=(-1, num_heads, X.shape[1], X.shape[2])) X = tf.transpose(X, perm=(0, 2, 1, 3)) return tf.reshape(X, shape=(X.shape[0], X.shape[1], -1)) diff --git a/chapter_attention-mechanisms/nadaraya-waston.md b/chapter_attention-mechanisms/nadaraya-waston.md index 7d41b90e5..4cbdeadad 100644 --- a/chapter_attention-mechanisms/nadaraya-waston.md +++ b/chapter_attention-mechanisms/nadaraya-waston.md @@ -194,39 +194,39 @@ $$\begin{aligned} f(x) &=\sum_{i=1}^n \alpha(x, x_i) y_i\\ &= \sum_{i=1}^n \frac 你会发现新的模型预测线是平滑的,并且比平均汇聚的预测更接近真实。 ```{.python .input} -# `X_repeat`的形状:(`n_test`,`n_train`), +# X_repeat的形状:(n_test,n_train), # 每一行都包含着相同的测试输入(例如:同样的查询) X_repeat = d2l.reshape(x_test.repeat(n_train), (-1, n_train)) -# `x_train`包含着键。`attention_weights`的形状:(`n_test`,`n_train`), -# 每一行都包含着要在给定的每个查询的值(`y_train`)之间分配的注意力权重 +# x_train包含着键。attention_weights的形状:(n_test,n_train), +# 每一行都包含着要在给定的每个查询的值(y_train)之间分配的注意力权重 attention_weights = npx.softmax(-(X_repeat - x_train)**2 / 2) -# `y_hat`的每个元素都是值的加权平均值,其中的权重是注意力权重 +# y_hat的每个元素都是值的加权平均值,其中的权重是注意力权重 y_hat = d2l.matmul(attention_weights, y_train) plot_kernel_reg(y_hat) ``` ```{.python .input} #@tab pytorch -# `X_repeat`的形状:(`n_test`,`n_train`), +# X_repeat的形状:(n_test,n_train), # 每一行都包含着相同的测试输入(例如:同样的查询) X_repeat = d2l.reshape(x_test.repeat_interleave(n_train), (-1, n_train)) -# `x_train`包含着键。`attention_weights`的形状:(`n_test`,`n_train`), -# 每一行都包含着要在给定的每个查询的值(`y_train`)之间分配的注意力权重 +# x_train包含着键。attention_weights的形状:(n_test,n_train), +# 每一行都包含着要在给定的每个查询的值(y_train)之间分配的注意力权重 attention_weights = nn.functional.softmax(-(X_repeat - x_train)**2 / 2, dim=1) -# `y_hat`的每个元素都是值的加权平均值,其中的权重是注意力权重 +# y_hat的每个元素都是值的加权平均值,其中的权重是注意力权重 y_hat = d2l.matmul(attention_weights, y_train) plot_kernel_reg(y_hat) ``` ```{.python .input} #@tab tensorflow -# `X_repeat`的形状:(`n_test`,`n_train`), +# X_repeat的形状:(n_test,n_train), # 每一行都包含着相同的测试输入(例如:同样的查询) X_repeat = tf.repeat(tf.expand_dims(x_train, axis=0), repeats=n_train, axis=0) -# `x_train`包含着键。`attention_weights`的形状:(`n_test`,`n_train`), -# 每一行都包含着要在给定的每个查询的值(`y_train`)之间分配的注意力权重 +# x_train包含着键。attention_weights的形状:(n_test,n_train), +# 每一行都包含着要在给定的每个查询的值(y_train)之间分配的注意力权重 attention_weights = tf.nn.softmax(-(X_repeat - tf.expand_dims(x_train, axis=1))**2/2, axis=1) -# `y_hat`的每个元素都是值的加权平均值,其中的权重是注意力权重 +# y_hat的每个元素都是值的加权平均值,其中的权重是注意力权重 y_hat = tf.matmul(attention_weights, tf.expand_dims(y_train, axis=1)) plot_kernel_reg(y_hat) ``` @@ -344,12 +344,12 @@ class NWKernelRegression(nn.Block): self.w = self.params.get('w', shape=(1,)) def forward(self, queries, keys, values): - # `queries`和`attention_weights`的形状为(查询数,“键-值”对数) + # queries和attention_weights的形状为(查询数,“键-值”对数) queries = d2l.reshape( queries.repeat(keys.shape[1]), (-1, keys.shape[1])) self.attention_weights = npx.softmax( -((queries - keys) * self.w.data())**2 / 2) - # `values`的形状为(查询数,“键-值”对数) + # values的形状为(查询数,“键-值”对数) return npx.batch_dot(np.expand_dims(self.attention_weights, 1), np.expand_dims(values, -1)).reshape(-1) ``` @@ -362,12 +362,12 @@ class NWKernelRegression(nn.Module): self.w = nn.Parameter(torch.rand((1,), requires_grad=True)) def forward(self, queries, keys, values): - # `queries`和`attention_weights`的形状为(查询个数,“键-值”对个数) + # queries和attention_weights的形状为(查询个数,“键-值”对个数) queries = d2l.reshape( queries.repeat_interleave(keys.shape[1]), (-1, keys.shape[1])) self.attention_weights = nn.functional.softmax( -((queries - keys) * self.w)**2 / 2, dim=1) - # `values`的形状为(查询个数,“键-值”对个数) + # values的形状为(查询个数,“键-值”对个数) return torch.bmm(self.attention_weights.unsqueeze(1), values.unsqueeze(-1)).reshape(-1) ``` @@ -380,11 +380,11 @@ class NWKernelRegression(tf.keras.layers.Layer): self.w = tf.Variable(initial_value=tf.random.uniform(shape=(1,))) def call(self, queries, keys, values, **kwargs): - # 对于训练,“查询”是`x_train`。“键”是每个点的训练数据的距离。“值”为'y_train'。 - # `queries`和`attention_weights`的形状为(查询个数,“键-值”对个数) + # 对于训练,“查询”是x_train。“键”是每个点的训练数据的距离。“值”为'y_train'。 + # queries和attention_weights的形状为(查询个数,“键-值”对个数) queries = tf.repeat(tf.expand_dims(queries, axis=1), repeats=keys.shape[1], axis=1) self.attention_weights = tf.nn.softmax(-((queries - keys) * self.w)**2 /2, axis =1) - # `values`的形状为(查询个数,“键-值”对个数) + # values的形状为(查询个数,“键-值”对个数) return tf.squeeze(tf.matmul(tf.expand_dims(self.attention_weights, axis=1), tf.expand_dims(values, axis=-1))) ``` @@ -396,41 +396,41 @@ class NWKernelRegression(tf.keras.layers.Layer): 从而得到其对应的预测输出。 ```{.python .input} -# `X_tile`的形状:(`n_train`,`n_train`),每一行都包含着相同的训练输入 +# X_tile的形状:(n_train,n_train),每一行都包含着相同的训练输入 X_tile = np.tile(x_train, (n_train, 1)) -# `Y_tile`的形状:(`n_train`,`n_train`),每一行都包含着相同的训练输出 +# Y_tile的形状:(n_train,n_train),每一行都包含着相同的训练输出 Y_tile = np.tile(y_train, (n_train, 1)) -# `keys`的形状:('n_train','n_train'-1) +# keys的形状:('n_train','n_train'-1) keys = d2l.reshape(X_tile[(1 - d2l.eye(n_train)).astype('bool')], (n_train, -1)) -# `values`的形状:('n_train','n_train'-1) +# values的形状:('n_train','n_train'-1) values = d2l.reshape(Y_tile[(1 - d2l.eye(n_train)).astype('bool')], (n_train, -1)) ``` ```{.python .input} #@tab pytorch -# `X_tile`的形状:(`n_train`,`n_train`),每一行都包含着相同的训练输入 +# X_tile的形状:(n_train,n_train),每一行都包含着相同的训练输入 X_tile = x_train.repeat((n_train, 1)) -# `Y_tile`的形状:(`n_train`,`n_train`),每一行都包含着相同的训练输出 +# Y_tile的形状:(n_train,n_train),每一行都包含着相同的训练输出 Y_tile = y_train.repeat((n_train, 1)) -# `keys`的形状:('n_train','n_train'-1) +# keys的形状:('n_train','n_train'-1) keys = d2l.reshape(X_tile[(1 - d2l.eye(n_train)).type(torch.bool)], (n_train, -1)) -# `values`的形状:('n_train','n_train'-1) +# values的形状:('n_train','n_train'-1) values = d2l.reshape(Y_tile[(1 - d2l.eye(n_train)).type(torch.bool)], (n_train, -1)) ``` ```{.python .input} #@tab tensorflow -# `X_tile`的形状:(`n_train`,`n_train`),每一行都包含着相同的训练输入 +# X_tile的形状:(n_train,n_train),每一行都包含着相同的训练输入 X_tile = tf.repeat(tf.expand_dims(x_train, axis=0), repeats=n_train, axis=0) -# `Y_tile`的形状:(`n_train`,`n_train`),每一行都包含着相同的训练输出 +# Y_tile的形状:(n_train,n_train),每一行都包含着相同的训练输出 Y_tile = tf.repeat(tf.expand_dims(y_train, axis=0), repeats=n_train, axis=0) -# `keys`的形状:('n_train','n_train'-1) +# keys的形状:('n_train','n_train'-1) keys = tf.reshape(X_tile[tf.cast(1 - tf.eye(n_train), dtype=tf.bool)], shape=(n_train, -1)) -# `values`的形状:('n_train','n_train'-1) +# values的形状:('n_train','n_train'-1) values = tf.reshape(Y_tile[tf.cast(1 - tf.eye(n_train), dtype=tf.bool)], shape=(n_train, -1)) ``` @@ -490,9 +490,9 @@ for epoch in range(5): [**预测结果绘制**]的线不如之前非参数模型的平滑。 ```{.python .input} -# `keys`的形状:(`n_test`,`n_train`),每一行包含着相同的训练输入(例如,相同的键) +# keys的形状:(n_test,n_train),每一行包含着相同的训练输入(例如,相同的键) keys = np.tile(x_train, (n_test, 1)) -# `value`的形状:(`n_test`,`n_train`) +# value的形状:(n_test,n_train) values = np.tile(y_train, (n_test, 1)) y_hat = net(x_test, keys, values) plot_kernel_reg(y_hat) @@ -500,9 +500,9 @@ plot_kernel_reg(y_hat) ```{.python .input} #@tab pytorch -# `keys`的形状:(`n_test`,`n_train`),每一行包含着相同的训练输入(例如,相同的键) +# keys的形状:(n_test,n_train),每一行包含着相同的训练输入(例如,相同的键) keys = x_train.repeat((n_test, 1)) -# `value`的形状:(`n_test`,`n_train`) +# value的形状:(n_test,n_train) values = y_train.repeat((n_test, 1)) y_hat = net(x_test, keys, values).unsqueeze(1).detach() plot_kernel_reg(y_hat) @@ -510,9 +510,9 @@ plot_kernel_reg(y_hat) ```{.python .input} #@tab tensorflow -# `keys`的形状:(`n_test`,`n_train`),每一行包含着相同的训练输入(例如,相同的键) +# keys的形状:(n_test,n_train),每一行包含着相同的训练输入(例如,相同的键) keys = tf.repeat(tf.expand_dims(x_train, axis=0), repeats=n_test, axis=0) -# `value`的形状:(`n_test`,`n_train`) +# value的形状:(n_test,n_train) values = tf.repeat(tf.expand_dims(y_train, axis=0), repeats=n_test, axis=0) y_hat = net(x_test, keys, values) plot_kernel_reg(y_hat) diff --git a/chapter_attention-mechanisms/self-attention-and-positional-encoding.md b/chapter_attention-mechanisms/self-attention-and-positional-encoding.md index 960c7d242..416a21396 100644 --- a/chapter_attention-mechanisms/self-attention-and-positional-encoding.md +++ b/chapter_attention-mechanisms/self-attention-and-positional-encoding.md @@ -151,7 +151,7 @@ class PositionalEncoding(nn.Block): def __init__(self, num_hiddens, dropout, max_len=1000): super(PositionalEncoding, self).__init__() self.dropout = nn.Dropout(dropout) - # 创建一个足够长的`P` + # 创建一个足够长的P self.P = d2l.zeros((1, max_len, num_hiddens)) X = d2l.arange(max_len).reshape(-1, 1) / np.power( 10000, np.arange(0, num_hiddens, 2) / num_hiddens) @@ -171,7 +171,7 @@ class PositionalEncoding(nn.Module): def __init__(self, num_hiddens, dropout, max_len=1000): super(PositionalEncoding, self).__init__() self.dropout = nn.Dropout(dropout) - # 创建一个足够长的`P` + # 创建一个足够长的P self.P = d2l.zeros((1, max_len, num_hiddens)) X = d2l.arange(max_len, dtype=torch.float32).reshape( -1, 1) / torch.pow(10000, torch.arange( @@ -192,7 +192,7 @@ class PositionalEncoding(tf.keras.layers.Layer): def __init__(self, num_hiddens, dropout, max_len=1000): super().__init__() self.dropout = tf.keras.layers.Dropout(dropout) - # 创建一个足够长的`P` + # 创建一个足够长的P self.P = np.zeros((1, max_len, num_hiddens)) X = np.arange(max_len, dtype=np.float32).reshape( -1,1)/np.power(10000, np.arange( diff --git a/chapter_attention-mechanisms/transformer.md b/chapter_attention-mechanisms/transformer.md index ecfbb2104..454ae68ea 100644 --- a/chapter_attention-mechanisms/transformer.md +++ b/chapter_attention-mechanisms/transformer.md @@ -127,7 +127,7 @@ ln.initialize() bn = nn.BatchNorm() bn.initialize() X = d2l.tensor([[1, 2], [2, 3]]) -# 在训练模式下计算`X`的均值和方差 +# 在训练模式下计算X的均值和方差 with autograd.record(): print('层规范化:', ln(X), '\n批量规范化:', bn(X)) ``` @@ -137,7 +137,7 @@ with autograd.record(): ln = nn.LayerNorm(2) bn = nn.BatchNorm1d(2) X = d2l.tensor([[1, 2], [2, 3]], dtype=torch.float32) -# 在训练模式下计算`X`的均值和方差 +# 在训练模式下计算X的均值和方差 print('layer norm:', ln(X), '\nbatch norm:', bn(X)) ``` @@ -446,9 +446,9 @@ class DecoderBlock(nn.Block): def forward(self, X, state): enc_outputs, enc_valid_lens = state[0], state[1] # 训练阶段,输出序列的所有词元都在同一时间处理, - # 因此`state[2][self.i]`初始化为`None`。 + # 因此state[2][self.i]初始化为None。 # 预测阶段,输出序列是通过词元一个接着一个解码的, - # 因此`state[2][self.i]`包含着直到当前时间步第`i`个块解码的输出表示 + # 因此state[2][self.i]包含着直到当前时间步第i个块解码的输出表示 if state[2][self.i] is None: key_values = X else: @@ -457,8 +457,8 @@ class DecoderBlock(nn.Block): if autograd.is_training(): batch_size, num_steps, _ = X.shape - # `dec_valid_lens`的开头:(`batch_size`,`num_steps`), - # 其中每一行是[1,2,...,`num_steps`] + # dec_valid_lens的开头:(batch_size,num_steps), + # 其中每一行是[1,2,...,num_steps] dec_valid_lens = np.tile(np.arange(1, num_steps + 1, ctx=X.ctx), (batch_size, 1)) else: @@ -496,9 +496,9 @@ class DecoderBlock(nn.Module): def forward(self, X, state): enc_outputs, enc_valid_lens = state[0], state[1] # 训练阶段,输出序列的所有词元都在同一时间处理, - # 因此`state[2][self.i]`初始化为`None`。 + # 因此state[2][self.i]初始化为None。 # 预测阶段,输出序列是通过词元一个接着一个解码的, - # 因此`state[2][self.i]`包含着直到当前时间步第`i`个块解码的输出表示 + # 因此state[2][self.i]包含着直到当前时间步第i个块解码的输出表示 if state[2][self.i] is None: key_values = X else: @@ -506,8 +506,8 @@ class DecoderBlock(nn.Module): state[2][self.i] = key_values if self.training: batch_size, num_steps, _ = X.shape - # `dec_valid_lens`的开头:(`batch_size`,`num_steps`), - # 其中每一行是[1,2,...,`num_steps`] + # dec_valid_lens的开头:(batch_size,num_steps), + # 其中每一行是[1,2,...,num_steps] dec_valid_lens = torch.arange( 1, num_steps + 1, device=X.device).repeat(batch_size, 1) else: @@ -517,7 +517,7 @@ class DecoderBlock(nn.Module): X2 = self.attention1(X, key_values, key_values, dec_valid_lens) Y = self.addnorm1(X, X2) # 编码器-解码器注意力。 - # `enc_outputs`的开头:(`batch_size`,`num_steps`,`num_hiddens`) + # enc_outputs的开头:(batch_size,num_steps,num_hiddens) Y2 = self.attention2(Y, enc_outputs, enc_outputs, enc_valid_lens) Z = self.addnorm2(Y, Y2) return self.addnorm3(Z, self.ffn(Z)), state @@ -541,9 +541,9 @@ class DecoderBlock(tf.keras.layers.Layer): def call(self, X, state, **kwargs): enc_outputs, enc_valid_lens = state[0], state[1] # 训练阶段,输出序列的所有词元都在同一时间处理, - # 因此`state[2][self.i]`初始化为`None`。 + # 因此state[2][self.i]初始化为None。 # 预测阶段,输出序列是通过词元一个接着一个解码的, - # 因此`state[2][self.i]`包含着直到当前时间步第`i`个块解码的输出表示 + # 因此state[2][self.i]包含着直到当前时间步第i个块解码的输出表示 if state[2][self.i] is None: key_values = X else: @@ -551,8 +551,8 @@ class DecoderBlock(tf.keras.layers.Layer): state[2][self.i] = key_values if kwargs["training"]: batch_size, num_steps, _ = X.shape - # `dec_valid_lens`的开头:(`batch_size`,`num_steps`), - # 其中每一行是[1,2,...,`num_steps`] + # dec_valid_lens的开头:(batch_size,num_steps), + # 其中每一行是[1,2,...,num_steps] dec_valid_lens = tf.repeat(tf.reshape(tf.range(1, num_steps + 1), shape=(-1, num_steps)), repeats=batch_size, axis=0) @@ -563,7 +563,7 @@ class DecoderBlock(tf.keras.layers.Layer): X2 = self.attention1(X, key_values, key_values, dec_valid_lens, **kwargs) Y = self.addnorm1(X, X2, **kwargs) # 编码器-解码器注意力。 - # `enc_outputs`的开头:(`batch_size`,`num_steps`,`num_hiddens`) + # enc_outputs的开头:(batch_size,num_steps,num_hiddens) Y2 = self.attention2(Y, enc_outputs, enc_outputs, enc_valid_lens, **kwargs) Z = self.addnorm2(Y, Y2, **kwargs) return self.addnorm3(Z, self.ffn(Z), **kwargs), state diff --git a/chapter_computational-performance/multiple-gpus.md b/chapter_computational-performance/multiple-gpus.md index af4ddb330..eecc82b03 100644 --- a/chapter_computational-performance/multiple-gpus.md +++ b/chapter_computational-performance/multiple-gpus.md @@ -271,7 +271,7 @@ print('output:', split) ```{.python .input} #@save def split_batch(X, y, devices): - """将`X`和`y`拆分到多个设备上""" + """将X和y拆分到多个设备上""" assert X.shape[0] == y.shape[0] return (gluon.utils.split_and_load(X, devices), gluon.utils.split_and_load(y, devices)) @@ -281,7 +281,7 @@ def split_batch(X, y, devices): #@tab pytorch #@save def split_batch(X, y, devices): - """将`X`和`y`拆分到多个设备上""" + """将X和y拆分到多个设备上""" assert X.shape[0] == y.shape[0] return (nn.parallel.scatter(X, devices), nn.parallel.scatter(y, devices)) diff --git a/chapter_computer-vision/anchor.md b/chapter_computer-vision/anchor.md index 435d88805..1c0d34b9e 100644 --- a/chapter_computer-vision/anchor.md +++ b/chapter_computer-vision/anchor.md @@ -235,20 +235,20 @@ def box_iou(boxes1, boxes2): """计算两个锚框或边界框列表中成对的交并比""" box_area = lambda boxes: ((boxes[:, 2] - boxes[:, 0]) * (boxes[:, 3] - boxes[:, 1])) - # `boxes1`,`boxes2`,`areas1`,`areas2`的形状: - # `boxes1`:(boxes1的数量,4), - # `boxes2`:(boxes2的数量,4), - # `areas1`:(boxes1的数量,), - # `areas2`:(boxes2的数量,) + # boxes1,boxes2,areas1,areas2的形状: + # boxes1:(boxes1的数量,4), + # boxes2:(boxes2的数量,4), + # areas1:(boxes1的数量,), + # areas2:(boxes2的数量,) areas1 = box_area(boxes1) areas2 = box_area(boxes2) - # `inter_upperlefts`,`inter_lowerrights`,`inters`的形状: + # inter_upperlefts,inter_lowerrights,inters的形状: # (boxes1的数量,boxes2的数量,2) inter_upperlefts = np.maximum(boxes1[:, None, :2], boxes2[:, :2]) inter_lowerrights = np.minimum(boxes1[:, None, 2:], boxes2[:, 2:]) inters = (inter_lowerrights - inter_upperlefts).clip(min=0) - # `inter_areas`and`union_areas`的形状:(boxes1的数量,boxes2的数量) + # inter_areasandunion_areas的形状:(boxes1的数量,boxes2的数量) inter_areas = inters[:, :, 0] * inters[:, :, 1] union_areas = areas1[:, None] + areas2 - inter_areas return inter_areas / union_areas @@ -261,19 +261,19 @@ def box_iou(boxes1, boxes2): """计算两个锚框或边界框列表中成对的交并比""" box_area = lambda boxes: ((boxes[:, 2] - boxes[:, 0]) * (boxes[:, 3] - boxes[:, 1])) - # `boxes1`,`boxes2`,`areas1`,`areas2`的形状: - # `boxes1`:(boxes1的数量,4), - # `boxes2`:(boxes2的数量,4), - # `areas1`:(boxes1的数量,), - # `areas2`:(boxes2的数量,) + # boxes1,boxes2,areas1,areas2的形状: + # boxes1:(boxes1的数量,4), + # boxes2:(boxes2的数量,4), + # areas1:(boxes1的数量,), + # areas2:(boxes2的数量,) areas1 = box_area(boxes1) areas2 = box_area(boxes2) - # `inter_upperlefts`,`inter_lowerrights`,`inters`的形状: + # inter_upperlefts,inter_lowerrights,inters的形状: # (boxes1的数量,boxes2的数量,2) inter_upperlefts = torch.max(boxes1[:, None, :2], boxes2[:, :2]) inter_lowerrights = torch.min(boxes1[:, None, 2:], boxes2[:, 2:]) inters = (inter_lowerrights - inter_upperlefts).clamp(min=0) - # `inter_areas`and`union_areas`的形状:(boxes1的数量,boxes2的数量) + # inter_areasandunion_areas的形状:(boxes1的数量,boxes2的数量) inter_areas = inters[:, :, 0] * inters[:, :, 1] union_areas = areas1[:, None] + areas2 - inter_areas return inter_areas / union_areas @@ -642,7 +642,7 @@ def multibox_detection(cls_probs, offset_preds, anchors, nms_threshold=0.5, class_id[non_keep] = -1 class_id = class_id[all_id_sorted].astype('float32') conf, predicted_bb = conf[all_id_sorted], predicted_bb[all_id_sorted] - # `pos_threshold`是一个用于非背景预测的阈值 + # pos_threshold是一个用于非背景预测的阈值 below_min_idx = (conf < pos_threshold) class_id[below_min_idx] = -1 conf[below_min_idx] = 1 - conf[below_min_idx] @@ -678,7 +678,7 @@ def multibox_detection(cls_probs, offset_preds, anchors, nms_threshold=0.5, class_id[non_keep] = -1 class_id = class_id[all_id_sorted] conf, predicted_bb = conf[all_id_sorted], predicted_bb[all_id_sorted] - # `pos_threshold`是一个用于非背景预测的阈值 + # pos_threshold是一个用于非背景预测的阈值 below_min_idx = (conf < pos_threshold) class_id[below_min_idx] = -1 conf[below_min_idx] = 1 - conf[below_min_idx] diff --git a/chapter_computer-vision/fine-tuning.md b/chapter_computer-vision/fine-tuning.md index 566731d05..0872546c9 100644 --- a/chapter_computer-vision/fine-tuning.md +++ b/chapter_computer-vision/fine-tuning.md @@ -232,7 +232,7 @@ def train_fine_tuning(net, learning_rate, batch_size=128, num_epochs=5): ```{.python .input} #@tab pytorch -# 如果`param_group=True`,输出层中的模型参数将使用十倍的学习率 +# 如果param_group=True,输出层中的模型参数将使用十倍的学习率 def train_fine_tuning(net, learning_rate, batch_size=128, num_epochs=5, param_group=True): train_iter = torch.utils.data.DataLoader(torchvision.datasets.ImageFolder( diff --git a/chapter_computer-vision/kaggle-cifar10.md b/chapter_computer-vision/kaggle-cifar10.md index 126e156bc..7b25ff207 100644 --- a/chapter_computer-vision/kaggle-cifar10.md +++ b/chapter_computer-vision/kaggle-cifar10.md @@ -73,7 +73,7 @@ import shutil d2l.DATA_HUB['cifar10_tiny'] = (d2l.DATA_URL + 'kaggle_cifar10_tiny.zip', '2068874e4b9a9f0fb07ebe0ad2b29754449ccacd') -# 如果你使用完整的Kaggle竞赛的数据集,设置`demo`为False +# 如果你使用完整的Kaggle竞赛的数据集,设置demo为False demo = True if demo: @@ -91,7 +91,7 @@ else: #@tab all #@save def read_csv_labels(fname): - """读取`fname`来给标签字典返回一个文件名""" + """读取fname来给标签字典返回一个文件名""" with open(fname, 'r') as f: # 跳过文件头行(列名) lines = f.readlines()[1:] diff --git a/chapter_computer-vision/object-detection-dataset.md b/chapter_computer-vision/object-detection-dataset.md index 338f6f54e..ccb2ab897 100644 --- a/chapter_computer-vision/object-detection-dataset.md +++ b/chapter_computer-vision/object-detection-dataset.md @@ -58,7 +58,7 @@ def read_data_bananas(is_train=True): images.append(image.imread( os.path.join(data_dir, 'bananas_train' if is_train else 'bananas_val', 'images', f'{img_name}'))) - # 这里的`target`包含(类别,左上角x,左上角y,右下角x,右下角y), + # 这里的target包含(类别,左上角x,左上角y,右下角x,右下角y), # 其中所有图像都具有相同的香蕉类(索引为0) targets.append(list(target)) return images, np.expand_dims(np.array(targets), 1) / 256 @@ -79,7 +79,7 @@ def read_data_bananas(is_train=True): images.append(torchvision.io.read_image( os.path.join(data_dir, 'bananas_train' if is_train else 'bananas_val', 'images', f'{img_name}'))) - # 这里的`target`包含(类别,左上角x,左上角y,右下角x,右下角y), + # 这里的target包含(类别,左上角x,左上角y,右下角x,右下角y), # 其中所有图像都具有相同的香蕉类(索引为0) targets.append(list(target)) return images, torch.tensor(targets).unsqueeze(1) / 256 diff --git a/chapter_computer-vision/ssd.md b/chapter_computer-vision/ssd.md index 7529cd8a8..596ec43bc 100644 --- a/chapter_computer-vision/ssd.md +++ b/chapter_computer-vision/ssd.md @@ -290,7 +290,7 @@ class TinySSD(nn.Block): super(TinySSD, self).__init__(**kwargs) self.num_classes = num_classes for i in range(5): - # 即赋值语句`self.blk_i=get_blk(i)` + # 即赋值语句self.blk_i=get_blk(i) setattr(self, f'blk_{i}', get_blk(i)) setattr(self, f'cls_{i}', cls_predictor(num_anchors, num_classes)) setattr(self, f'bbox_{i}', bbox_predictor(num_anchors)) @@ -298,7 +298,7 @@ class TinySSD(nn.Block): def forward(self, X): anchors, cls_preds, bbox_preds = [None] * 5, [None] * 5, [None] * 5 for i in range(5): - # `getattr(self,'blk_%d'%i)`即访问`self.blk_i` + # getattr(self,'blk_%d'%i)即访问self.blk_i X, anchors[i], cls_preds[i], bbox_preds[i] = blk_forward( X, getattr(self, f'blk_{i}'), sizes[i], ratios[i], getattr(self, f'cls_{i}'), getattr(self, f'bbox_{i}')) @@ -318,7 +318,7 @@ class TinySSD(nn.Module): self.num_classes = num_classes idx_to_in_channels = [64, 128, 128, 128, 128] for i in range(5): - # 即赋值语句`self.blk_i=get_blk(i)` + # 即赋值语句self.blk_i=get_blk(i) setattr(self, f'blk_{i}', get_blk(i)) setattr(self, f'cls_{i}', cls_predictor(idx_to_in_channels[i], num_anchors, num_classes)) @@ -328,7 +328,7 @@ class TinySSD(nn.Module): def forward(self, X): anchors, cls_preds, bbox_preds = [None] * 5, [None] * 5, [None] * 5 for i in range(5): - # `getattr(self,'blk_%d'%i)`即访问`self.blk_i` + # getattr(self,'blk_%d'%i)即访问self.blk_i X, anchors[i], cls_preds[i], bbox_preds[i] = blk_forward( X, getattr(self, f'blk_{i}'), sizes[i], ratios[i], getattr(self, f'cls_{i}'), getattr(self, f'bbox_{i}')) @@ -436,7 +436,7 @@ def calc_loss(cls_preds, cls_labels, bbox_preds, bbox_labels, bbox_masks): ```{.python .input} def cls_eval(cls_preds, cls_labels): - # 由于类别预测结果放在最后一维,`argmax`需要指定最后一维。 + # 由于类别预测结果放在最后一维,argmax需要指定最后一维。 return float((cls_preds.argmax(axis=-1).astype( cls_labels.dtype) == cls_labels).sum()) @@ -447,7 +447,7 @@ def bbox_eval(bbox_preds, bbox_labels, bbox_masks): ```{.python .input} #@tab pytorch def cls_eval(cls_preds, cls_labels): - # 由于类别预测结果放在最后一维,`argmax`需要指定最后一维。 + # 由于类别预测结果放在最后一维,argmax需要指定最后一维。 return float((cls_preds.argmax(dim=-1).type( cls_labels.dtype) == cls_labels).sum()) diff --git a/chapter_convolutional-modern/batch-norm.md b/chapter_convolutional-modern/batch-norm.md index d1e7a576e..9e19cc7ba 100644 --- a/chapter_convolutional-modern/batch-norm.md +++ b/chapter_convolutional-modern/batch-norm.md @@ -104,7 +104,7 @@ from mxnet.gluon import nn npx.set_np() def batch_norm(X, gamma, beta, moving_mean, moving_var, eps, momentum): - # 通过`autograd`来判断当前模式是训练模式还是预测模式 + # 通过autograd来判断当前模式是训练模式还是预测模式 if not autograd.is_training(): # 如果是在预测模式下,直接使用传入的移动平均所得的均值和方差 X_hat = (X - moving_mean) / np.sqrt(moving_var + eps) @@ -135,7 +135,7 @@ import torch from torch import nn def batch_norm(X, gamma, beta, moving_mean, moving_var, eps, momentum): - # 通过`is_grad_enabled`来判断当前模式是训练模式还是预测模式 + # 通过is_grad_enabled来判断当前模式是训练模式还是预测模式 if not torch.is_grad_enabled(): # 如果是在预测模式下,直接使用传入的移动平均所得的均值和方差 X_hat = (X - moving_mean) / torch.sqrt(moving_var + eps) @@ -185,8 +185,8 @@ def batch_norm(X, gamma, beta, moving_mean, moving_var, eps): ```{.python .input} class BatchNorm(nn.Block): - # `num_features`:完全连接层的输出数量或卷积层的输出通道数。 - # `num_dims`:2表示完全连接层,4表示卷积层 + # num_features:完全连接层的输出数量或卷积层的输出通道数。 + # num_dims:2表示完全连接层,4表示卷积层 def __init__(self, num_features, num_dims, **kwargs): super().__init__(**kwargs) if num_dims == 2: @@ -201,12 +201,12 @@ class BatchNorm(nn.Block): self.moving_var = np.ones(shape) def forward(self, X): - # 如果`X`不在内存上,将`moving_mean`和`moving_var` - # 复制到`X`所在显存上 + # 如果X不在内存上,将moving_mean和moving_var + # 复制到X所在显存上 if self.moving_mean.ctx != X.ctx: self.moving_mean = self.moving_mean.copyto(X.ctx) self.moving_var = self.moving_var.copyto(X.ctx) - # 保存更新过的`moving_mean`和`moving_var` + # 保存更新过的moving_mean和moving_var Y, self.moving_mean, self.moving_var = batch_norm( X, self.gamma.data(), self.beta.data(), self.moving_mean, self.moving_var, eps=1e-12, momentum=0.9) @@ -216,8 +216,8 @@ class BatchNorm(nn.Block): ```{.python .input} #@tab pytorch class BatchNorm(nn.Module): - # `num_features`:完全连接层的输出数量或卷积层的输出通道数。 - # `num_dims`:2表示完全连接层,4表示卷积层 + # num_features:完全连接层的输出数量或卷积层的输出通道数。 + # num_dims:2表示完全连接层,4表示卷积层 def __init__(self, num_features, num_dims): super().__init__() if num_dims == 2: @@ -232,12 +232,12 @@ class BatchNorm(nn.Module): self.moving_var = torch.ones(shape) def forward(self, X): - # 如果`X`不在内存上,将`moving_mean`和`moving_var` - # 复制到`X`所在显存上 + # 如果X不在内存上,将moving_mean和moving_var + # 复制到X所在显存上 if self.moving_mean.device != X.device: self.moving_mean = self.moving_mean.to(X.device) self.moving_var = self.moving_var.to(X.device) - # 保存更新过的`moving_mean`和`moving_var` + # 保存更新过的moving_mean和moving_var Y, self.moving_mean, self.moving_var = batch_norm( X, self.gamma, self.beta, self.moving_mean, self.moving_var, eps=1e-5, momentum=0.9) @@ -332,8 +332,8 @@ net = nn.Sequential( ```{.python .input} #@tab tensorflow -# 回想一下,这个函数必须传递给`d2l.train_ch6`。 -# 或者说为了利用我们现有的CPU/GPU设备,需要在`strategy.scope()`建立模型 +# 回想一下,这个函数必须传递给d2l.train_ch6。 +# 或者说为了利用我们现有的CPU/GPU设备,需要在strategy.scope()建立模型 def net(): return tf.keras.models.Sequential([ tf.keras.layers.Conv2D(filters=6, kernel_size=5, diff --git a/chapter_convolutional-modern/densenet.md b/chapter_convolutional-modern/densenet.md index 8cf60ab53..707dee8f3 100644 --- a/chapter_convolutional-modern/densenet.md +++ b/chapter_convolutional-modern/densenet.md @@ -273,7 +273,7 @@ def block_1(): 在每个模块之间,ResNet通过步幅为2的残差块减小高和宽,DenseNet则使用过渡层来减半高和宽,并减半通道数。 ```{.python .input} -# `num_channels`为当前的通道数 +# num_channels为当前的通道数 num_channels, growth_rate = 64, 32 num_convs_in_dense_blocks = [4, 4, 4, 4] @@ -289,7 +289,7 @@ for i, num_convs in enumerate(num_convs_in_dense_blocks): ```{.python .input} #@tab pytorch -# `num_channels`为当前的通道数 +# num_channels为当前的通道数 num_channels, growth_rate = 64, 32 num_convs_in_dense_blocks = [4, 4, 4, 4] blks = [] @@ -307,7 +307,7 @@ for i, num_convs in enumerate(num_convs_in_dense_blocks): #@tab tensorflow def block_2(): net = block_1() - # `num_channels`为当前的通道数 + # num_channels为当前的通道数 num_channels, growth_rate = 64, 32 num_convs_in_dense_blocks = [4, 4, 4, 4] diff --git a/chapter_convolutional-modern/googlenet.md b/chapter_convolutional-modern/googlenet.md index 2f3a95edb..0b744047c 100644 --- a/chapter_convolutional-modern/googlenet.md +++ b/chapter_convolutional-modern/googlenet.md @@ -28,7 +28,7 @@ from mxnet.gluon import nn npx.set_np() class Inception(nn.Block): - # `c1`--`c4`是每条路径的输出通道数 + # c1--c4是每条路径的输出通道数 def __init__(self, c1, c2, c3, c4, **kwargs): super(Inception, self).__init__(**kwargs) # 线路1,单1x1卷积层 @@ -62,7 +62,7 @@ from torch import nn from torch.nn import functional as F class Inception(nn.Module): - # `c1`--`c4`是每条路径的输出通道数 + # c1--c4是每条路径的输出通道数 def __init__(self, in_channels, c1, c2, c3, c4, **kwargs): super(Inception, self).__init__(**kwargs) # 线路1,单1x1卷积层 @@ -92,7 +92,7 @@ from d2l import tensorflow as d2l import tensorflow as tf class Inception(tf.keras.Model): - # `c1`--`c4`是每条路径的输出通道数 + # c1--c4是每条路径的输出通道数 def __init__(self, c1, c2, c3, c4): super().__init__() # 线路1,单1x1卷积层 diff --git a/chapter_convolutional-modern/resnet.md b/chapter_convolutional-modern/resnet.md index 229ea832f..9f247766f 100644 --- a/chapter_convolutional-modern/resnet.md +++ b/chapter_convolutional-modern/resnet.md @@ -319,7 +319,7 @@ net = nn.Sequential(b1, b2, b3, b4, b5, ```{.python .input} #@tab tensorflow -# 回想之前我们定义一个函数,以便用它在`tf.distribute.MirroredStrategy`的范围, +# 回想之前我们定义一个函数,以便用它在tf.distribute.MirroredStrategy的范围, # 来利用各种计算资源,例如gpu。另外,尽管我们已经创建了b1、b2、b3、b4、b5, # 但是我们将在这个函数的作用域内重新创建它们 def net(): diff --git a/chapter_convolutional-modern/vgg.md b/chapter_convolutional-modern/vgg.md index 51724ef5e..8de3f8603 100644 --- a/chapter_convolutional-modern/vgg.md +++ b/chapter_convolutional-modern/vgg.md @@ -190,7 +190,7 @@ net = vgg(small_conv_arch) #@tab tensorflow ratio = 4 small_conv_arch = [(pair[0], pair[1] // ratio) for pair in conv_arch] -# 回想一下,这必须是一个将被放入“d2l.train_ch6()”的函数,为了利用我们现有的CPU/GPU设备,这样模型构建/编译需要在`strategy.scope()`中 +# 回想一下,这必须是一个将被放入“d2l.train_ch6()”的函数,为了利用我们现有的CPU/GPU设备,这样模型构建/编译需要在strategy.scope()中 net = lambda: vgg(small_conv_arch) ``` diff --git a/chapter_deep-learning-computation/model-construction.md b/chapter_deep-learning-computation/model-construction.md index a166d1aab..2902196a1 100644 --- a/chapter_deep-learning-computation/model-construction.md +++ b/chapter_deep-learning-computation/model-construction.md @@ -171,13 +171,13 @@ net(X) class MLP(nn.Block): # 用模型参数声明层。这里,我们声明两个全连接的层 def __init__(self, **kwargs): - # 调用`MLP`的父类`Block`的构造函数来执行必要的初始化。 - # 这样,在类实例化时也可以指定其他函数参数,例如模型参数`params`(稍后将介绍) + # 调用MLP的父类Block的构造函数来执行必要的初始化。 + # 这样,在类实例化时也可以指定其他函数参数,例如模型参数params(稍后将介绍) super().__init__(**kwargs) self.hidden = nn.Dense(256, activation='relu') # 隐藏层 self.out = nn.Dense(10) # 输出层 - # 定义模型的前向传播,即如何根据输入`X`返回所需的模型输出 + # 定义模型的前向传播,即如何根据输入X返回所需的模型输出 def forward(self, X): return self.out(self.hidden(X)) ``` @@ -187,13 +187,13 @@ class MLP(nn.Block): class MLP(nn.Module): # 用模型参数声明层。这里,我们声明两个全连接的层 def __init__(self): - # 调用`MLP`的父类`Module`的构造函数来执行必要的初始化。 - # 这样,在类实例化时也可以指定其他函数参数,例如模型参数`params`(稍后将介绍) + # 调用MLP的父类Module的构造函数来执行必要的初始化。 + # 这样,在类实例化时也可以指定其他函数参数,例如模型参数params(稍后将介绍) super().__init__() self.hidden = nn.Linear(20, 256) # 隐藏层 self.out = nn.Linear(256, 10) # 输出层 - # 定义模型的前向传播,即如何根据输入`X`返回所需的模型输出 + # 定义模型的前向传播,即如何根据输入X返回所需的模型输出 def forward(self, X): # 注意,这里我们使用ReLU的函数版本,其在nn.functional模块中定义。 return self.out(F.relu(self.hidden(X))) @@ -204,14 +204,14 @@ class MLP(nn.Module): class MLP(tf.keras.Model): # 用模型参数声明层。这里,我们声明两个全连接的层 def __init__(self): - # 调用`MLP`的父类`Model`的构造函数来执行必要的初始化。 - # 这样,在类实例化时也可以指定其他函数参数,例如模型参数`params`(稍后将介绍) + # 调用MLP的父类Model的构造函数来执行必要的初始化。 + # 这样,在类实例化时也可以指定其他函数参数,例如模型参数params(稍后将介绍) super().__init__() # Hiddenlayer self.hidden = tf.keras.layers.Dense(units=256, activation=tf.nn.relu) self.out = tf.keras.layers.Dense(units=10) # Outputlayer - # 定义模型的前向传播,即如何根据输入`X`返回所需的模型输出 + # 定义模型的前向传播,即如何根据输入X返回所需的模型输出 def call(self, X): return self.out(self.hidden((X))) ``` @@ -275,9 +275,9 @@ net(X) ```{.python .input} class MySequential(nn.Block): def add(self, block): - # 这里,`block`是`Block`子类的一个实例,我们假设它有一个唯一的名称。我们把它 - # 保存在'Block'类的成员变量`_children`中。`block`的类型是OrderedDict。 - # 当`MySequential`实例调用`initialize`函数时,系统会自动初始化`_children` + # 这里,block是Block子类的一个实例,我们假设它有一个唯一的名称。我们把它 + # 保存在'Block'类的成员变量_children中。block的类型是OrderedDict。 + # 当MySequential实例调用initialize函数时,系统会自动初始化_children # 的所有成员 self._children[block.name] = block @@ -294,8 +294,8 @@ class MySequential(nn.Module): def __init__(self, *args): super().__init__() for idx, module in enumerate(args): - # 这里,`module`是`Module`子类的一个实例。我们把它保存在'Module'类的成员 - # 变量`_modules`中。`module`的类型是OrderedDict + # 这里,module是Module子类的一个实例。我们把它保存在'Module'类的成员 + # 变量_modules中。module的类型是OrderedDict self._modules[str(idx)] = module def forward(self, X): @@ -312,7 +312,7 @@ class MySequential(tf.keras.Model): super().__init__() self.modules = [] for block in args: - # 这里,`block`是`tf.keras.layers.Layer`子类的一个实例 + # 这里,block是tf.keras.layers.Layer子类的一个实例 self.modules.append(block) def call(self, X): @@ -393,14 +393,14 @@ $c$是某个在优化过程中没有更新的指定常量。 class FixedHiddenMLP(nn.Block): def __init__(self, **kwargs): super().__init__(**kwargs) - # 使用`get_constant`函数创建的随机权重参数在训练期间不会更新(即为常量参数) + # 使用get_constant函数创建的随机权重参数在训练期间不会更新(即为常量参数) self.rand_weight = self.params.get_constant( 'rand_weight', np.random.uniform(size=(20, 20))) self.dense = nn.Dense(20, activation='relu') def forward(self, X): X = self.dense(X) - # 使用创建的常量参数以及`relu`和`dot`函数 + # 使用创建的常量参数以及relu和dot函数 X = npx.relu(np.dot(X, self.rand_weight.data()) + 1) # 复用全连接层。这相当于两个全连接层共享参数 X = self.dense(X) @@ -437,13 +437,13 @@ class FixedHiddenMLP(tf.keras.Model): def __init__(self): super().__init__() self.flatten = tf.keras.layers.Flatten() - # 使用`tf.constant`函数创建的随机权重参数在训练期间不会更新(即为常量参数) + # 使用tf.constant函数创建的随机权重参数在训练期间不会更新(即为常量参数) self.rand_weight = tf.constant(tf.random.uniform((20, 20))) self.dense = tf.keras.layers.Dense(20, activation=tf.nn.relu) def call(self, inputs): X = self.flatten(inputs) - # 使用创建的常量参数以及`relu`和`matmul`函数 + # 使用创建的常量参数以及relu和matmul函数 X = tf.nn.relu(tf.matmul(X, self.rand_weight) + 1) # 复用全连接层。这相当于两个全连接层共享参数。 X = self.dense(X) diff --git a/chapter_deep-learning-computation/parameters.md b/chapter_deep-learning-computation/parameters.md index 96496847b..95a6b680b 100644 --- a/chapter_deep-learning-computation/parameters.md +++ b/chapter_deep-learning-computation/parameters.md @@ -298,7 +298,7 @@ TensorFlow在根模块和`keras.initializers`模块中提供了各种初始化 且将偏置参数设置为0。 ```{.python .input} -# 这里的`force_reinit`确保参数会被重新初始化,不论之前是否已经被初始化 +# 这里的force_reinit确保参数会被重新初始化,不论之前是否已经被初始化 net.initialize(init=init.Normal(sigma=0.01), force_reinit=True) net[0].weight.data()[0] ``` diff --git a/chapter_linear-networks/linear-regression-concise.md b/chapter_linear-networks/linear-regression-concise.md index a3738646d..14ec5a16d 100644 --- a/chapter_linear-networks/linear-regression-concise.md +++ b/chapter_linear-networks/linear-regression-concise.md @@ -140,7 +140,7 @@ Keras会自动推断每个层输入的形状。 :end_tab: ```{.python .input} -# `nn`是神经网络的缩写 +# nn是神经网络的缩写 from mxnet.gluon import nn net = nn.Sequential() net.add(nn.Dense(1)) @@ -148,14 +148,14 @@ net.add(nn.Dense(1)) ```{.python .input} #@tab pytorch -# `nn`是神经网络的缩写 +# nn是神经网络的缩写 from torch import nn net = nn.Sequential(nn.Linear(2, 1)) ``` ```{.python .input} #@tab tensorflow -# `keras`是TensorFlow的高级API +# keras是TensorFlow的高级API net = tf.keras.Sequential() net.add(tf.keras.layers.Dense(1)) ``` diff --git a/chapter_linear-networks/linear-regression-scratch.md b/chapter_linear-networks/linear-regression-scratch.md index 0c1759b14..f8698c28f 100644 --- a/chapter_linear-networks/linear-regression-scratch.md +++ b/chapter_linear-networks/linear-regression-scratch.md @@ -292,8 +292,8 @@ loss = squared_loss for epoch in range(num_epochs): for X, y in data_iter(batch_size, features, labels): with autograd.record(): - l = loss(net(X, w, b), y) # `X`和`y`的小批量损失 - # 计算l关于[`w`,`b`]的梯度 + l = loss(net(X, w, b), y) # X和y的小批量损失 + # 计算l关于[w,b]的梯度 l.backward() sgd([w, b], lr, batch_size) # 使用参数的梯度更新参数 train_l = loss(net(features, w, b), labels) @@ -304,9 +304,9 @@ for epoch in range(num_epochs): #@tab pytorch for epoch in range(num_epochs): for X, y in data_iter(batch_size, features, labels): - l = loss(net(X, w, b), y) # `X`和`y`的小批量损失 - # 因为`l`形状是(`batch_size`,1),而不是一个标量。`l`中的所有元素被加到一起, - # 并以此计算关于[`w`,`b`]的梯度 + l = loss(net(X, w, b), y) # X和y的小批量损失 + # 因为l形状是(batch_size,1),而不是一个标量。l中的所有元素被加到一起, + # 并以此计算关于[w,b]的梯度 l.sum().backward() sgd([w, b], lr, batch_size) # 使用参数的梯度更新参数 with torch.no_grad(): @@ -319,8 +319,8 @@ for epoch in range(num_epochs): for epoch in range(num_epochs): for X, y in data_iter(batch_size, features, labels): with tf.GradientTape() as g: - l = loss(net(X, w, b), y) # `X`和`y`的小批量损失 - # 计算l关于[`w`,`b`]的梯度 + l = loss(net(X, w, b), y) # X和y的小批量损失 + # 计算l关于[w,b]的梯度 dw, db = g.gradient(l, [w, b]) # 使用参数的梯度更新参数 sgd([w, b], [dw, db], lr, batch_size) diff --git a/chapter_linear-networks/softmax-regression-scratch.md b/chapter_linear-networks/softmax-regression-scratch.md index dc028fa99..2618331e1 100644 --- a/chapter_linear-networks/softmax-regression-scratch.md +++ b/chapter_linear-networks/softmax-regression-scratch.md @@ -290,7 +290,7 @@ def evaluate_accuracy(net, data_iter): #@save ```{.python .input} #@tab all class Accumulator: #@save - """在`n`个变量上累加""" + """在n个变量上累加""" def __init__(self, n): self.data = [0.0] * n @@ -390,7 +390,7 @@ def train_epoch_ch3(net, train_iter, loss, updater): #@save updater.apply_gradients(zip(grads, params)) else: updater(X.shape[0], tape.gradient(l, updater.params)) - # Keras的`loss`默认返回一个批量的平均损失 + # Keras的loss默认返回一个批量的平均损失 l_sum = l * float(tf.size(y)) if isinstance( loss, tf.keras.losses.Loss) else tf.reduce_sum(l) metric.add(l_sum, accuracy(y_hat, y), tf.size(y)) diff --git a/chapter_multilayer-perceptrons/underfit-overfit.md b/chapter_multilayer-perceptrons/underfit-overfit.md index 9766ba151..88dd0b00c 100644 --- a/chapter_multilayer-perceptrons/underfit-overfit.md +++ b/chapter_multilayer-perceptrons/underfit-overfit.md @@ -309,8 +309,8 @@ features = np.random.normal(size=(n_train + n_test, 1)) np.random.shuffle(features) poly_features = np.power(features, np.arange(max_degree).reshape(1, -1)) for i in range(max_degree): - poly_features[:, i] /= math.gamma(i + 1) # `gamma(n)`=(n-1)! -# `labels`的维度:(`n_train`+`n_test`,) + poly_features[:, i] /= math.gamma(i + 1) # gamma(n)=(n-1)! +# labels的维度:(n_train+n_test,) labels = np.dot(poly_features, true_w) labels += np.random.normal(scale=0.1, size=labels.shape) ``` diff --git a/chapter_multilayer-perceptrons/weight-decay.md b/chapter_multilayer-perceptrons/weight-decay.md index e3e50ace8..69b110706 100644 --- a/chapter_multilayer-perceptrons/weight-decay.md +++ b/chapter_multilayer-perceptrons/weight-decay.md @@ -398,7 +398,7 @@ def train_concise(wd): for epoch in range(num_epochs): for X, y in train_iter: with tf.GradientTape() as tape: - # `tf.keras`需要为自定义训练代码手动添加损失。 + # tf.keras需要为自定义训练代码手动添加损失。 l = loss(net(X), y) + net.losses grads = tape.gradient(l, net.trainable_variables) trainer.apply_gradients(zip(grads, net.trainable_variables)) diff --git a/chapter_natural-language-processing-applications/natural-language-inference-attention.md b/chapter_natural-language-processing-applications/natural-language-inference-attention.md index b8bec5833..728be22e6 100644 --- a/chapter_natural-language-processing-applications/natural-language-inference-attention.md +++ b/chapter_natural-language-processing-applications/natural-language-inference-attention.md @@ -92,17 +92,17 @@ class Attend(nn.Block): self.f = mlp(num_hiddens=num_hiddens, flatten=False) def forward(self, A, B): - # `A`/`B`的形状:(批量大小,序列A/B的词元数,`embed_size`) - # `f_A`/`f_B`的形状:(`批量大小`,序列A/B的词元数,`num_hiddens`) + # A/B的形状:(批量大小,序列A/B的词元数,embed_size) + # f_A/f_B的形状:(批量大小,序列A/B的词元数,num_hiddens) f_A = self.f(A) f_B = self.f(B) - # `e`的形状:(批量大小,序列A的词元数,序列B的词元数) + # e的形状:(批量大小,序列A的词元数,序列B的词元数) e = npx.batch_dot(f_A, f_B, transpose_b=True) - # `beta`的形状:(批量大小,序列A的词元数,`embed_size`), - # 意味着序列B被软对齐到序列A的每个词元(`beta`的第1个维度) + # beta的形状:(批量大小,序列A的词元数,embed_size), + # 意味着序列B被软对齐到序列A的每个词元(beta的第1个维度) beta = npx.batch_dot(npx.softmax(e), B) - # `beta`的形状:(批量大小,序列B的词元数,`embed_size`), - # 意味着序列A被软对齐到序列B的每个词元(`alpha`的第1个维度) + # beta的形状:(批量大小,序列B的词元数,embed_size), + # 意味着序列A被软对齐到序列B的每个词元(alpha的第1个维度) alpha = npx.batch_dot(npx.softmax(e.transpose(0, 2, 1)), A) return beta, alpha ``` @@ -115,17 +115,17 @@ class Attend(nn.Module): self.f = mlp(num_inputs, num_hiddens, flatten=False) def forward(self, A, B): - # `A`/`B`的形状:(批量大小,序列A/B的词元数,`embed_size`) - # `f_A`/`f_B`的形状:(`批量大小`,序列A/B的词元数,`num_hiddens`) + # A/B的形状:(批量大小,序列A/B的词元数,embed_size) + # f_A/f_B的形状:(批量大小,序列A/B的词元数,num_hiddens) f_A = self.f(A) f_B = self.f(B) - # `e`的形状:(批量大小,序列A的词元数,序列B的词元数) + # e的形状:(批量大小,序列A的词元数,序列B的词元数) e = torch.bmm(f_A, f_B.permute(0, 2, 1)) - # `beta`的形状:(批量大小,序列A的词元数,`embed_size`), - # 意味着序列B被软对齐到序列A的每个词元(`beta`的第1个维度) + # beta的形状:(批量大小,序列A的词元数,embed_size), + # 意味着序列B被软对齐到序列A的每个词元(beta的第1个维度) beta = torch.bmm(F.softmax(e, dim=-1), B) - # `beta`的形状:(批量大小,序列B的词元数,`embed_size`), - # 意味着序列A被软对齐到序列B的每个词元(`alpha`的第1个维度) + # beta的形状:(批量大小,序列B的词元数,embed_size), + # 意味着序列A被软对齐到序列B的每个词元(alpha的第1个维度) alpha = torch.bmm(F.softmax(e.permute(0, 2, 1), dim=-1), A) return beta, alpha ``` diff --git a/chapter_natural-language-processing-applications/natural-language-inference-bert.md b/chapter_natural-language-processing-applications/natural-language-inference-bert.md index b3101d57e..c6cc219bc 100644 --- a/chapter_natural-language-processing-applications/natural-language-inference-bert.md +++ b/chapter_natural-language-processing-applications/natural-language-inference-bert.md @@ -213,7 +213,7 @@ class SNLIBERTDataset(torch.utils.data.Dataset): 下载完SNLI数据集后,我们通过实例化`SNLIBERTDataset`类来[**生成训练和测试样本**]。这些样本将在自然语言推断的训练和测试期间进行小批量读取。 ```{.python .input} -# 如果出现显存不足错误,请减少“batch_size”。在原始的BERT模型中,`max_len`=512 +# 如果出现显存不足错误,请减少“batch_size”。在原始的BERT模型中,max_len=512 batch_size, max_len, num_workers = 512, 128, d2l.get_dataloader_workers() data_dir = d2l.download_extract('SNLI') train_set = SNLIBERTDataset(d2l.read_snli(data_dir, True), max_len, vocab) @@ -226,7 +226,7 @@ test_iter = gluon.data.DataLoader(test_set, batch_size, ```{.python .input} #@tab pytorch -# 如果出现显存不足错误,请减少“batch_size”。在原始的BERT模型中,`max_len`=512 +# 如果出现显存不足错误,请减少“batch_size”。在原始的BERT模型中,max_len=512 batch_size, max_len, num_workers = 512, 128, d2l.get_dataloader_workers() data_dir = d2l.download_extract('SNLI') train_set = SNLIBERTDataset(d2l.read_snli(data_dir, True), max_len, vocab) diff --git a/chapter_natural-language-processing-applications/sentiment-analysis-rnn.md b/chapter_natural-language-processing-applications/sentiment-analysis-rnn.md index f6381636e..43f05b3a0 100644 --- a/chapter_natural-language-processing-applications/sentiment-analysis-rnn.md +++ b/chapter_natural-language-processing-applications/sentiment-analysis-rnn.md @@ -36,19 +36,19 @@ class BiRNN(nn.Block): num_layers, **kwargs): super(BiRNN, self).__init__(**kwargs) self.embedding = nn.Embedding(vocab_size, embed_size) - # 将`bidirectional`设置为True以获取双向循环神经网络 + # 将bidirectional设置为True以获取双向循环神经网络 self.encoder = rnn.LSTM(num_hiddens, num_layers=num_layers, bidirectional=True, input_size=embed_size) self.decoder = nn.Dense(2) def forward(self, inputs): - # `inputs`的形状是(批量大小,时间步数) + # inputs的形状是(批量大小,时间步数) # 因为长短期记忆网络要求其输入的第一个维度是时间维, # 所以在获得词元表示之前,输入会被转置。 # 输出形状为(时间步数,批量大小,词向量维度) embeddings = self.embedding(inputs.T) # 返回上一个隐藏层在不同时间步的隐状态, - # `outputs`的形状是(时间步数,批量大小,2*隐藏单元数) + # outputs的形状是(时间步数,批量大小,2*隐藏单元数) outputs = self.encoder(embeddings) # 连结初始和最终时间步的隐状态,作为全连接层的输入, # 其形状为(批量大小,4*隐藏单元数) @@ -64,20 +64,20 @@ class BiRNN(nn.Module): num_layers, **kwargs): super(BiRNN, self).__init__(**kwargs) self.embedding = nn.Embedding(vocab_size, embed_size) - # 将`bidirectional`设置为True以获取双向循环神经网络 + # 将bidirectional设置为True以获取双向循环神经网络 self.encoder = nn.LSTM(embed_size, num_hiddens, num_layers=num_layers, bidirectional=True) self.decoder = nn.Linear(4 * num_hiddens, 2) def forward(self, inputs): - # `inputs`的形状是(批量大小,时间步数) + # inputs的形状是(批量大小,时间步数) # 因为长短期记忆网络要求其输入的第一个维度是时间维, # 所以在获得词元表示之前,输入会被转置。 # 输出形状为(时间步数,批量大小,词向量维度) embeddings = self.embedding(inputs.T) self.encoder.flatten_parameters() # 返回上一个隐藏层在不同时间步的隐状态, - # `outputs`的形状是(时间步数,批量大小,2*隐藏单元数) + # outputs的形状是(时间步数,批量大小,2*隐藏单元数) outputs, _ = self.encoder(embeddings) # 连结初始和最终时间步的隐状态,作为全连接层的输入, # 其形状为(批量大小,4*隐藏单元数) diff --git a/chapter_natural-language-processing-pretraining/bert-dataset.md b/chapter_natural-language-processing-pretraining/bert-dataset.md index 4a5f08912..e657666bc 100644 --- a/chapter_natural-language-processing-pretraining/bert-dataset.md +++ b/chapter_natural-language-processing-pretraining/bert-dataset.md @@ -58,7 +58,7 @@ def _get_next_sentence(sentence, next_sentence, paragraphs): if random.random() < 0.5: is_next = True else: - # `paragraphs`是三重列表的嵌套 + # paragraphs是三重列表的嵌套 next_sentence = random.choice(random.choice(paragraphs)) is_next = False return sentence, next_sentence, is_next @@ -124,7 +124,7 @@ def _replace_mlm_tokens(tokens, candidate_pred_positions, num_mlm_preds, #@save def _get_mlm_data_from_tokens(tokens, vocab): candidate_pred_positions = [] - # `tokens`是一个字符串列表 + # tokens是一个字符串列表 for i, token in enumerate(tokens): # 在遮蔽语言模型任务中不会预测特殊词元 if token in ['', '']: @@ -158,7 +158,7 @@ def _pad_bert_inputs(examples, max_len, vocab): max_len - len(token_ids)), dtype='int32')) all_segments.append(np.array(segments + [0] * ( max_len - len(segments)), dtype='int32')) - # `valid_lens`不包括''的计数 + # valid_lens不包括''的计数 valid_lens.append(np.array(len(token_ids), dtype='float32')) all_pred_positions.append(np.array(pred_positions + [0] * ( max_num_mlm_preds - len(pred_positions)), dtype='int32')) @@ -187,7 +187,7 @@ def _pad_bert_inputs(examples, max_len, vocab): max_len - len(token_ids)), dtype=torch.long)) all_segments.append(torch.tensor(segments + [0] * ( max_len - len(segments)), dtype=torch.long)) - # `valid_lens`不包括''的计数 + # valid_lens不包括''的计数 valid_lens.append(torch.tensor(len(token_ids), dtype=torch.float32)) all_pred_positions.append(torch.tensor(pred_positions + [0] * ( max_num_mlm_preds - len(pred_positions)), dtype=torch.long)) @@ -211,8 +211,8 @@ def _pad_bert_inputs(examples, max_len, vocab): #@save class _WikiTextDataset(gluon.data.Dataset): def __init__(self, paragraphs, max_len): - # 输入`paragraphs[i]`是代表段落的句子字符串列表; - # 而输出`paragraphs[i]`是代表段落的句子列表,其中每个句子都是词元列表 + # 输入paragraphs[i]是代表段落的句子字符串列表; + # 而输出paragraphs[i]是代表段落的句子列表,其中每个句子都是词元列表 paragraphs = [d2l.tokenize( paragraph, token='word') for paragraph in paragraphs] sentences = [sentence for paragraph in paragraphs @@ -249,8 +249,8 @@ class _WikiTextDataset(gluon.data.Dataset): #@save class _WikiTextDataset(torch.utils.data.Dataset): def __init__(self, paragraphs, max_len): - # 输入`paragraphs[i]`是代表段落的句子字符串列表; - # 而输出`paragraphs[i]`是代表段落的句子列表,其中每个句子都是词元列表 + # 输入paragraphs[i]是代表段落的句子字符串列表; + # 而输出paragraphs[i]是代表段落的句子列表,其中每个句子都是词元列表 paragraphs = [d2l.tokenize( paragraph, token='word') for paragraph in paragraphs] sentences = [sentence for paragraph in paragraphs diff --git a/chapter_natural-language-processing-pretraining/bert.md b/chapter_natural-language-processing-pretraining/bert.md index 2ee12969f..ff053ef59 100644 --- a/chapter_natural-language-processing-pretraining/bert.md +++ b/chapter_natural-language-processing-pretraining/bert.md @@ -92,7 +92,7 @@ class BERTEncoder(nn.Block): shape=(1, max_len, num_hiddens)) def forward(self, tokens, segments, valid_lens): - # 在以下代码段中,`X`的形状保持不变:(批量大小,最大序列长度,`num_hiddens`) + # 在以下代码段中,X的形状保持不变:(批量大小,最大序列长度,num_hiddens) X = self.token_embedding(tokens) + self.segment_embedding(segments) X = X + self.pos_embedding.data(ctx=X.ctx)[:, :X.shape[1], :] for blk in self.blks: @@ -122,7 +122,7 @@ class BERTEncoder(nn.Module): num_hiddens)) def forward(self, tokens, segments, valid_lens): - # 在以下代码段中,`X`的形状保持不变:(批量大小,最大序列长度,`num_hiddens`) + # 在以下代码段中,X的形状保持不变:(批量大小,最大序列长度,num_hiddens) X = self.token_embedding(tokens) + self.segment_embedding(segments) X = X + self.pos_embedding.data[:, :X.shape[1], :] for blk in self.blks: @@ -288,7 +288,7 @@ class NextSentencePred(nn.Block): self.output = nn.Dense(2) def forward(self, X): - # X的形状:(batchsize,`num_hiddens`) + # X的形状:(batchsize,num_hiddens) return self.output(X) ``` @@ -302,7 +302,7 @@ class NextSentencePred(nn.Module): self.output = nn.Linear(num_inputs, 2) def forward(self, X): - # `X`的形状:(batchsize,`num_hiddens`) + # X的形状:(batchsize,num_hiddens) return self.output(X) ``` @@ -318,7 +318,7 @@ nsp_Y_hat.shape ```{.python .input} #@tab pytorch encoded_X = torch.flatten(encoded_X, start_dim=1) -# NSP的输入形状:(batchsize,`num_hiddens`) +# NSP的输入形状:(batchsize,num_hiddens) nsp = NextSentencePred(encoded_X.shape[-1]) nsp_Y_hat = nsp(encoded_X) nsp_Y_hat.shape diff --git a/chapter_natural-language-processing-pretraining/word-embedding-dataset.md b/chapter_natural-language-processing-pretraining/word-embedding-dataset.md index 7fb141f79..00c90b0a4 100644 --- a/chapter_natural-language-processing-pretraining/word-embedding-dataset.md +++ b/chapter_natural-language-processing-pretraining/word-embedding-dataset.md @@ -132,7 +132,7 @@ def get_centers_and_contexts(corpus, max_window_size): if len(line) < 2: continue centers += line - for i in range(len(line)): # 上下文窗口中间`i` + for i in range(len(line)): # 上下文窗口中间i window_size = random.randint(1, max_window_size) indices = list(range(max(0, i - window_size), min(len(line), i + 1 + window_size))) @@ -178,7 +178,7 @@ class RandomGenerator: def draw(self): if self.i == len(self.candidates): - # 缓存`k`个随机采样结果 + # 缓存k个随机采样结果 self.candidates = random.choices( self.population, self.sampling_weights, k=10000) self.i = 0 diff --git a/chapter_optimization/gd.md b/chapter_optimization/gd.md index 8ac60ceaf..2a35bbaa9 100644 --- a/chapter_optimization/gd.md +++ b/chapter_optimization/gd.md @@ -188,7 +188,7 @@ $$\mathbf{x} \leftarrow \mathbf{x} - \eta \nabla f(\mathbf{x}).$$ #@tab all def train_2d(trainer, steps=20, f_grad=None): #@save """用定制的训练机优化2D目标函数""" - # `s1`和`s2`是稍后将使用的内部状态变量 + # s1和s2是稍后将使用的内部状态变量 x1, x2, s1, s2 = -5, -2, 0, 0 results = [(x1, x2)] for i in range(steps): diff --git a/chapter_optimization/lr-scheduler.md b/chapter_optimization/lr-scheduler.md index b8d728e16..51e633447 100644 --- a/chapter_optimization/lr-scheduler.md +++ b/chapter_optimization/lr-scheduler.md @@ -41,7 +41,7 @@ device = d2l.try_gpu() batch_size = 256 train_iter, test_iter = d2l.load_data_fashion_mnist(batch_size=batch_size) -# 代码几乎与`d2l.train_ch6`定义在卷积神经网络一章LeNet一节中的相同 +# 代码几乎与d2l.train_ch6定义在卷积神经网络一章LeNet一节中的相同 def train(net, train_iter, test_iter, num_epochs, loss, trainer, device): net.initialize(force_reinit=True, ctx=device, init=init.Xavier()) animator = d2l.Animator(xlabel='epoch', xlim=[0, num_epochs], @@ -95,7 +95,7 @@ device = d2l.try_gpu() batch_size = 256 train_iter, test_iter = d2l.load_data_fashion_mnist(batch_size=batch_size) -# 代码几乎与`d2l.train_ch6`定义在卷积神经网络一章LeNet一节中的相同 +# 代码几乎与d2l.train_ch6定义在卷积神经网络一章LeNet一节中的相同 def train(net, train_iter, test_iter, num_epochs, loss, trainer, device, scheduler=None): net.to(device) @@ -161,7 +161,7 @@ def net(): batch_size = 256 train_iter, test_iter = d2l.load_data_fashion_mnist(batch_size=batch_size) -# 代码几乎与`d2l.train_ch6`定义在卷积神经网络一章LeNet一节中的相同 +# 代码几乎与d2l.train_ch6定义在卷积神经网络一章LeNet一节中的相同 def train(net_fn, train_iter, test_iter, num_epochs, lr, device=d2l.try_gpu(), custom_callback = False): device_name = device._device_name diff --git a/chapter_preliminaries/autograd.md b/chapter_preliminaries/autograd.md index 76d66b470..684c297c0 100644 --- a/chapter_preliminaries/autograd.md +++ b/chapter_preliminaries/autograd.md @@ -46,15 +46,15 @@ x 注意,一个标量函数关于向量$\mathbf{x}$的梯度是向量,并且与$\mathbf{x}$具有相同的形状。 ```{.python .input} -# 我们通过调用`attach_grad`来为一个张量的梯度分配内存 +# 我们通过调用attach_grad来为一个张量的梯度分配内存 x.attach_grad() -# 在我们计算关于`x`的梯度后,我们将能够通过'grad'属性访问它,它的值被初始化为0 +# 在我们计算关于x的梯度后,我们将能够通过'grad'属性访问它,它的值被初始化为0 x.grad ``` ```{.python .input} #@tab pytorch -x.requires_grad_(True) # 等价于`x=torch.arange(4.0,requires_grad=True)` +x.requires_grad_(True) # 等价于x=torch.arange(4.0,requires_grad=True) x.grad # 默认值是None ``` @@ -66,7 +66,7 @@ x = tf.Variable(x) (**现在让我们计算$y$。**) ```{.python .input} -# 把代码放到`autograd.record`内,以建立计算图 +# 把代码放到autograd.record内,以建立计算图 with autograd.record(): y = 2 * np.dot(x, x) y @@ -158,17 +158,17 @@ t.gradient(y, x) # 被新计算的梯度覆盖 这里(**,我们的目的不是计算微分矩阵,而是单独计算批量中每个样本的偏导数之和。**) ```{.python .input} -# 当我们对向量值变量`y`(关于`x`的函数)调用`backward`时,将通过对`y`中的元素求和来创建 -# 一个新的标量变量。然后计算这个标量变量相对于`x`的梯度 +# 当我们对向量值变量y(关于x的函数)调用backward时,将通过对y中的元素求和来创建 +# 一个新的标量变量。然后计算这个标量变量相对于x的梯度 with autograd.record(): - y = x * x # `y`是一个向量 + y = x * x # y是一个向量 y.backward() x.grad # 等价于y=sum(x*x) ``` ```{.python .input} #@tab pytorch -# 对非标量调用`backward`需要传入一个`gradient`参数,该参数指定微分函数关于`self`的梯度。 +# 对非标量调用backward需要传入一个gradient参数,该参数指定微分函数关于self的梯度。 # 在我们的例子中,我们只想求偏导数的和,所以传递一个1的梯度是合适的 x.grad.zero_() y = x * x @@ -181,7 +181,7 @@ x.grad #@tab tensorflow with tf.GradientTape() as t: y = x * x -t.gradient(y, x) # 等价于`y=tf.reduce_sum(x*x)` +t.gradient(y, x) # 等价于y=tf.reduce_sum(x*x) ``` ## 分离计算 @@ -219,7 +219,7 @@ x.grad == u ```{.python .input} #@tab tensorflow -# 设置`persistent=True`来运行`t.gradient`多次 +# 设置persistent=True来运行t.gradient多次 with tf.GradientTape(persistent=True) as t: y = x * x u = tf.stop_gradient(y) diff --git a/chapter_preliminaries/calculus.md b/chapter_preliminaries/calculus.md index 7a0dad2dd..006d14cc4 100644 --- a/chapter_preliminaries/calculus.md +++ b/chapter_preliminaries/calculus.md @@ -194,7 +194,7 @@ def plot(X, Y=None, xlabel=None, ylabel=None, legend=None, xlim=None, set_figsize(figsize) axes = axes if axes else d2l.plt.gca() - # 如果`X`有一个轴,输出True + # 如果X有一个轴,输出True def has_one_axis(X): return (hasattr(X, "ndim") and X.ndim == 1 or isinstance(X, list) and not hasattr(X[0], "__len__")) diff --git a/chapter_preliminaries/linear-algebra.md b/chapter_preliminaries/linear-algebra.md index 45850f7f6..e4cff91f4 100644 --- a/chapter_preliminaries/linear-algebra.md +++ b/chapter_preliminaries/linear-algebra.md @@ -476,17 +476,17 @@ A_sum_axis1, A_sum_axis1.shape 沿着行和列对矩阵求和,等价于对矩阵的所有元素进行求和。 ```{.python .input} -A.sum(axis=[0, 1]) # Sameas`A.sum()` +A.sum(axis=[0, 1]) # SameasA.sum() ``` ```{.python .input} #@tab pytorch -A.sum(axis=[0, 1]) # Sameas`A.sum()` +A.sum(axis=[0, 1]) # SameasA.sum() ``` ```{.python .input} #@tab tensorflow -tf.reduce_sum(A, axis=[0, 1]) # Sameas`tf.reduce_sum(A)` +tf.reduce_sum(A, axis=[0, 1]) # Sameastf.reduce_sum(A) ``` [**一个与求和相关的量是*平均值*(mean或average)**]。 diff --git a/chapter_recurrent-modern/seq2seq.md b/chapter_recurrent-modern/seq2seq.md index e7e943549..ea822a14b 100644 --- a/chapter_recurrent-modern/seq2seq.md +++ b/chapter_recurrent-modern/seq2seq.md @@ -127,14 +127,14 @@ class Seq2SeqEncoder(d2l.Encoder): self.rnn = rnn.GRU(num_hiddens, num_layers, dropout=dropout) def forward(self, X, *args): - # 输出'X'的形状:(`batch_size`,`num_steps`,`embed_size`) + # 输出'X'的形状:(batch_size,num_steps,embed_size) X = self.embedding(X) # 在循环神经网络模型中,第一个轴对应于时间步 X = X.swapaxes(0, 1) state = self.rnn.begin_state(batch_size=X.shape[1], ctx=X.ctx) output, state = self.rnn(X, state) - # `output`的形状:(`num_steps`,`batch_size`,`num_hiddens`) - # `state[0]`的形状:(`num_layers`,`batch_size`,`num_hiddens`) + # output的形状:(num_steps,batch_size,num_hiddens) + # state[0]的形状:(num_layers,batch_size,num_hiddens) return output, state ``` @@ -152,14 +152,14 @@ class Seq2SeqEncoder(d2l.Encoder): dropout=dropout) def forward(self, X, *args): - # 输出'X'的形状:(`batch_size`,`num_steps`,`embed_size`) + # 输出'X'的形状:(batch_size,num_steps,embed_size) X = self.embedding(X) # 在循环神经网络模型中,第一个轴对应于时间步 X = X.permute(1, 0, 2) # 如果未提及状态,则默认为0 output, state = self.rnn(X) - # `output`的形状:(`num_steps`,`batch_size`,`num_hiddens`) - # `state[0]`的形状:(`num_layers`,`batch_size`,`num_hiddens`) + # output的形状:(num_steps,batch_size,num_hiddens) + # state[0]的形状:(num_layers,batch_size,num_hiddens) return output, state ``` @@ -178,8 +178,8 @@ class Seq2SeqEncoder(d2l.Encoder): return_state=True) def call(self, X, *args, **kwargs): - # 输入'X'的形状:(`batch_size`,`num_steps`) - # 输出'X'的形状:(`batch_size`,`num_steps`,`embed_size`) + # 输入'X'的形状:(batch_size,num_steps) + # 输出'X'的形状:(batch_size,num_steps,embed_size) X = self.embedding(X) output = self.rnn(X, **kwargs) state = output[1:] @@ -292,18 +292,18 @@ class Seq2SeqDecoder(d2l.Decoder): return enc_outputs[1] def forward(self, X, state): - # 输出'X'的形状:(`batch_size`,`num_steps`,`embed_size`) + # 输出'X'的形状:(batch_size,num_steps,embed_size) X = self.embedding(X).swapaxes(0, 1) - # `context`的形状:(`batch_size`,`num_hiddens`) + # context的形状:(batch_size,num_hiddens) context = state[0][-1] - # 广播`context`,使其具有与`X`相同的`num_steps` + # 广播context,使其具有与X相同的num_steps context = np.broadcast_to(context, ( X.shape[0], context.shape[0], context.shape[1])) X_and_context = d2l.concat((X, context), 2) output, state = self.rnn(X_and_context, state) output = self.dense(output).swapaxes(0, 1) - # `output`的形状:(`batch_size`,`num_steps`,`vocab_size`) - # `state[0]`的形状:(`num_layers`,`batch_size`,`num_hiddens`) + # output的形状:(batch_size,num_steps,vocab_size) + # state[0]的形状:(num_layers,batch_size,num_hiddens) return output, state ``` @@ -323,15 +323,15 @@ class Seq2SeqDecoder(d2l.Decoder): return enc_outputs[1] def forward(self, X, state): - # 输出'X'的形状:(`batch_size`,`num_steps`,`embed_size`) + # 输出'X'的形状:(batch_size,num_steps,embed_size) X = self.embedding(X).permute(1, 0, 2) - # 广播`context`,使其具有与`X`相同的`num_steps` + # 广播context,使其具有与X相同的num_steps context = state[-1].repeat(X.shape[0], 1, 1) X_and_context = d2l.concat((X, context), 2) output, state = self.rnn(X_and_context, state) output = self.dense(output).permute(1, 0, 2) - # `output`的形状:(`batch_size`,`num_steps`,`vocab_size`) - # `state[0]`的形状:(`num_layers`,`batch_size`,`num_hiddens`) + # output的形状:(batch_size,num_steps,vocab_size) + # state[0]的形状:(num_layers,batch_size,num_hiddens) return output, state ``` @@ -353,15 +353,15 @@ class Seq2SeqDecoder(d2l.Decoder): return enc_outputs[1] def call(self, X, state, **kwargs): - # 输出'X'的形状:(`batch_size`,`num_steps`,`embed_size`) + # 输出'X'的形状:(batch_size,num_steps,embed_size) X = self.embedding(X) - # 广播`context`,使其具有与`X`相同的`num_steps` + # 广播context,使其具有与X相同的num_steps context = tf.repeat(tf.expand_dims(state[-1], axis=1), repeats=X.shape[1], axis=1) X_and_context = tf.concat((X, context), axis=2) rnn_output = self.rnn(X_and_context, state, **kwargs) output = self.dense(rnn_output[0]) - # `output`的形状:(`batch_size`,`num_steps`,`vocab_size`) - # `state`是一个包含`num_layers`个元素的列表,每个元素的形状:(`batch_size`,`num_hiddens`) + # output的形状:(batch_size,num_steps,vocab_size) + # state是一个包含num_layers个元素的列表,每个元素的形状:(batch_size,num_hiddens) return output, rnn_output[1:] ``` @@ -484,11 +484,11 @@ sequence_mask(X, tf.constant([1, 2]), value=-1) #@save class MaskedSoftmaxCELoss(gluon.loss.SoftmaxCELoss): """带遮蔽的softmax交叉熵损失函数""" - # `pred`的形状:(`batch_size`,`num_steps`,`vocab_size`) - # `label`的形状:(`batch_size`,`num_steps`) - # `valid_len`的形状:(`batch_size`,) + # pred的形状:(batch_size,num_steps,vocab_size) + # label的形状:(batch_size,num_steps) + # valid_len的形状:(batch_size,) def forward(self, pred, label, valid_len): - # `weights`的形状:(`batch_size`,`num_steps`,1) + # weights的形状:(batch_size,num_steps,1) weights = np.expand_dims(np.ones_like(label), axis=-1) weights = npx.sequence_mask(weights, valid_len, True, axis=1) return super(MaskedSoftmaxCELoss, self).forward(pred, label, weights) @@ -499,9 +499,9 @@ class MaskedSoftmaxCELoss(gluon.loss.SoftmaxCELoss): #@save class MaskedSoftmaxCELoss(nn.CrossEntropyLoss): """带遮蔽的softmax交叉熵损失函数""" - # `pred`的形状:(`batch_size`,`num_steps`,`vocab_size`) - # `label`的形状:(`batch_size`,`num_steps`) - # `valid_len`的形状:(`batch_size`,) + # pred的形状:(batch_size,num_steps,vocab_size) + # label的形状:(batch_size,num_steps) + # valid_len的形状:(batch_size,) def forward(self, pred, label, valid_len): weights = torch.ones_like(label) weights = sequence_mask(weights, valid_len) @@ -521,9 +521,9 @@ class MaskedSoftmaxCELoss(tf.keras.losses.Loss): super().__init__(reduction='none') self.valid_len = valid_len - # `pred`的形状:(`batch_size`,`num_steps`,`vocab_size`) - # `label`的形状:(`batch_size`,`num_steps`) - # `valid_len`的形状:(`batch_size`,) + # pred的形状:(batch_size,num_steps,vocab_size) + # label的形状:(batch_size,num_steps) + # valid_len的形状:(batch_size,) def call(self, label, pred): weights = tf.ones_like(label, dtype=tf.float32) weights = sequence_mask(weights, self.valid_len) @@ -743,7 +743,7 @@ def predict_seq2seq(net, src_sentence, src_vocab, tgt_vocab, num_steps, def predict_seq2seq(net, src_sentence, src_vocab, tgt_vocab, num_steps, device, save_attention_weights=False): """序列到序列模型的预测""" - # 在预测时将`net`设置为评估模式 + # 在预测时将net设置为评估模式 net.eval() src_tokens = src_vocab[src_sentence.lower().split(' ')] + [ src_vocab['']] diff --git a/chapter_recurrent-neural-networks/language-models-and-dataset.md b/chapter_recurrent-neural-networks/language-models-and-dataset.md index 96eb4a430..e8aa04a3f 100644 --- a/chapter_recurrent-neural-networks/language-models-and-dataset.md +++ b/chapter_recurrent-neural-networks/language-models-and-dataset.md @@ -278,23 +278,23 @@ d2l.plot([freqs, bigram_freqs, trigram_freqs], xlabel='token: x', #@tab all def seq_data_iter_random(corpus, batch_size, num_steps): #@save """使用随机抽样生成一个小批量子序列""" - # 从随机偏移量开始对序列进行分区,随机范围包括`num_steps-1` + # 从随机偏移量开始对序列进行分区,随机范围包括num_steps-1 corpus = corpus[random.randint(0, num_steps - 1):] # 减去1,是因为我们需要考虑标签 num_subseqs = (len(corpus) - 1) // num_steps - # 长度为`num_steps`的子序列的起始索引 + # 长度为num_steps的子序列的起始索引 initial_indices = list(range(0, num_subseqs * num_steps, num_steps)) # 在随机抽样的迭代过程中, # 来自两个相邻的、随机的、小批量中的子序列不一定在原始序列上相邻 random.shuffle(initial_indices) def data(pos): - # 返回从`pos`位置开始的长度为`num_steps`的序列 + # 返回从pos位置开始的长度为num_steps的序列 return corpus[pos: pos + num_steps] num_batches = num_subseqs // batch_size for i in range(0, batch_size * num_batches, batch_size): - # 在这里,`initial_indices`包含子序列的随机起始索引 + # 在这里,initial_indices包含子序列的随机起始索引 initial_indices_per_batch = initial_indices[i: i + batch_size] X = [data(j) for j in initial_indices_per_batch] Y = [data(j + 1) for j in initial_indices_per_batch] diff --git a/chapter_recurrent-neural-networks/rnn-concise.md b/chapter_recurrent-neural-networks/rnn-concise.md index c9650fc37..5a065b9ea 100644 --- a/chapter_recurrent-neural-networks/rnn-concise.md +++ b/chapter_recurrent-neural-networks/rnn-concise.md @@ -140,8 +140,8 @@ class RNNModel(nn.Block): def forward(self, inputs, state): X = npx.one_hot(inputs.T, self.vocab_size) Y, state = self.rnn(X, state) - # 全连接层首先将`Y`的形状改为(`时间步数`*`批量大小`,`隐藏单元数`) - # 它的输出形状是(`时间步数`*`批量大小`,`词表大小`) + # 全连接层首先将Y的形状改为(时间步数*批量大小,隐藏单元数) + # 它的输出形状是(时间步数*批量大小,词表大小) output = self.dense(Y.reshape(-1, Y.shape[-1])) return output, state @@ -159,7 +159,7 @@ class RNNModel(nn.Module): self.rnn = rnn_layer self.vocab_size = vocab_size self.num_hiddens = self.rnn.hidden_size - # 如果RNN是双向的(之后将介绍),`num_directions`应该是2,否则应该是1 + # 如果RNN是双向的(之后将介绍),num_directions应该是2,否则应该是1 if not self.rnn.bidirectional: self.num_directions = 1 self.linear = nn.Linear(self.num_hiddens, self.vocab_size) @@ -171,19 +171,19 @@ class RNNModel(nn.Module): X = F.one_hot(inputs.T.long(), self.vocab_size) X = X.to(torch.float32) Y, state = self.rnn(X, state) - # 全连接层首先将`Y`的形状改为(`时间步数`*`批量大小`,`隐藏单元数`) - # 它的输出形状是(`时间步数`*`批量大小`,`词表大小`)。 + # 全连接层首先将Y的形状改为(时间步数*批量大小,隐藏单元数) + # 它的输出形状是(时间步数*批量大小,词表大小)。 output = self.linear(Y.reshape((-1, Y.shape[-1]))) return output, state def begin_state(self, device, batch_size=1): if not isinstance(self.rnn, nn.LSTM): - # `nn.GRU`以张量作为隐状态 + # nn.GRU以张量作为隐状态 return torch.zeros((self.num_directions * self.rnn.num_layers, batch_size, self.num_hiddens), device=device) else: - # `nn.LSTM`以元组作为隐状态 + # nn.LSTM以元组作为隐状态 return (torch.zeros(( self.num_directions * self.rnn.num_layers, batch_size, self.num_hiddens), device=device), diff --git a/chapter_recurrent-neural-networks/rnn-scratch.md b/chapter_recurrent-neural-networks/rnn-scratch.md index 555399ac8..42846fe37 100644 --- a/chapter_recurrent-neural-networks/rnn-scratch.md +++ b/chapter_recurrent-neural-networks/rnn-scratch.md @@ -206,11 +206,11 @@ def init_rnn_state(batch_size, num_hiddens): ```{.python .input} def rnn(inputs, state, params): - # `inputs`的形状:(`时间步数量`,`批量大小`,`词表大小`) + # inputs的形状:(时间步数量,批量大小,词表大小) W_xh, W_hh, b_h, W_hq, b_q = params H, = state outputs = [] - # `X`的形状:(`批量大小`,`词表大小`) + # X的形状:(批量大小,词表大小) for X in inputs: H = np.tanh(np.dot(X, W_xh) + np.dot(H, W_hh) + b_h) Y = np.dot(H, W_hq) + b_q @@ -221,11 +221,11 @@ def rnn(inputs, state, params): ```{.python .input} #@tab pytorch def rnn(inputs, state, params): - # `inputs`的形状:(`时间步数量`,`批量大小`,`词表大小`) + # inputs的形状:(时间步数量,批量大小,词表大小) W_xh, W_hh, b_h, W_hq, b_q = params H, = state outputs = [] - # `X`的形状:(`批量大小`,`词表大小`) + # X的形状:(批量大小,词表大小) for X in inputs: H = torch.tanh(torch.mm(X, W_xh) + torch.mm(H, W_hh) + b_h) Y = torch.mm(H, W_hq) + b_q @@ -236,11 +236,11 @@ def rnn(inputs, state, params): ```{.python .input} #@tab tensorflow def rnn(inputs, state, params): - # `inputs`的形状:(`时间步数量`,`批量大小`,`词表大小`) + # inputs的形状:(时间步数量,批量大小,词表大小) W_xh, W_hh, b_h, W_hq, b_q = params H, = state outputs = [] - # `X`的形状:(`批量大小`,`词表大小`) + # X的形状:(批量大小,词表大小) for X in inputs: X = tf.reshape(X,[-1,W_xh.shape[0]]) H = tf.tanh(tf.matmul(X, W_xh) + tf.matmul(H, W_hh) + b_h) @@ -361,7 +361,7 @@ Y.shape, len(new_state), new_state[0].shape ```{.python .input} def predict_ch8(prefix, num_preds, net, vocab, device): #@save - """在`prefix`后面生成新字符""" + """在prefix后面生成新字符""" state = net.begin_state(batch_size=1, ctx=device) outputs = [vocab[prefix[0]]] get_input = lambda: d2l.reshape( @@ -369,7 +369,7 @@ def predict_ch8(prefix, num_preds, net, vocab, device): #@save for y in prefix[1:]: # 预热期 _, state = net(get_input(), state) outputs.append(vocab[y]) - for _ in range(num_preds): # 预测`num_preds`步 + for _ in range(num_preds): # 预测num_preds步 y, state = net(get_input(), state) outputs.append(int(y.argmax(axis=1).reshape(1))) return ''.join([vocab.idx_to_token[i] for i in outputs]) @@ -378,7 +378,7 @@ def predict_ch8(prefix, num_preds, net, vocab, device): #@save ```{.python .input} #@tab pytorch def predict_ch8(prefix, num_preds, net, vocab, device): #@save - """在`prefix`后面生成新字符""" + """在prefix后面生成新字符""" state = net.begin_state(batch_size=1, device=device) outputs = [vocab[prefix[0]]] get_input = lambda: d2l.reshape(d2l.tensor( @@ -386,7 +386,7 @@ def predict_ch8(prefix, num_preds, net, vocab, device): #@save for y in prefix[1:]: # 预热期 _, state = net(get_input(), state) outputs.append(vocab[y]) - for _ in range(num_preds): # 预测`num_preds`步 + for _ in range(num_preds): # 预测num_preds步 y, state = net(get_input(), state) outputs.append(int(y.argmax(dim=1).reshape(1))) return ''.join([vocab.idx_to_token[i] for i in outputs]) @@ -395,7 +395,7 @@ def predict_ch8(prefix, num_preds, net, vocab, device): #@save ```{.python .input} #@tab tensorflow def predict_ch8(prefix, num_preds, net, vocab): #@save - """在`prefix`后面生成新字符""" + """在prefix后面生成新字符""" state = net.begin_state(batch_size=1, dtype=tf.float32) outputs = [vocab[prefix[0]]] get_input = lambda: d2l.reshape(d2l.tensor([outputs[-1]]), @@ -403,7 +403,7 @@ def predict_ch8(prefix, num_preds, net, vocab): #@save for y in prefix[1:]: # 预热期 _, state = net(get_input(), state) outputs.append(vocab[y]) - for _ in range(num_preds): # 预测`num_preds`步 + for _ in range(num_preds): # 预测num_preds步 y, state = net(get_input(), state) outputs.append(int(y.numpy().argmax(axis=1).reshape(1))) return ''.join([vocab.idx_to_token[i] for i in outputs]) @@ -566,7 +566,7 @@ def train_epoch_ch8(net, train_iter, loss, updater, device, use_random_iter): metric = d2l.Accumulator(2) # 训练损失之和,词元数量 for X, Y in train_iter: if state is None or use_random_iter: - # 在第一次迭代或使用随机抽样时初始化`state` + # 在第一次迭代或使用随机抽样时初始化state state = net.begin_state(batch_size=X.shape[0], ctx=device) else: for s in state: @@ -578,7 +578,7 @@ def train_epoch_ch8(net, train_iter, loss, updater, device, use_random_iter): l = loss(y_hat, y).mean() l.backward() grad_clipping(net, 1) - updater(batch_size=1) # 因为已经调用了`mean`函数 + updater(batch_size=1) # 因为已经调用了mean函数 metric.add(l * d2l.size(y), d2l.size(y)) return math.exp(metric[0] / metric[1]), metric[1] / timer.stop() ``` @@ -592,14 +592,14 @@ def train_epoch_ch8(net, train_iter, loss, updater, device, use_random_iter): metric = d2l.Accumulator(2) # 训练损失之和,词元数量 for X, Y in train_iter: if state is None or use_random_iter: - # 在第一次迭代或使用随机抽样时初始化`state` + # 在第一次迭代或使用随机抽样时初始化state state = net.begin_state(batch_size=X.shape[0], device=device) else: if isinstance(net, nn.Module) and not isinstance(state, tuple): - # `state`对于`nn.GRU`是个张量 + # state对于nn.GRU是个张量 state.detach_() else: - # `state`对于`nn.LSTM`或对于我们从零开始实现的模型是个张量 + # state对于nn.LSTM或对于我们从零开始实现的模型是个张量 for s in state: s.detach_() y = Y.T.reshape(-1) @@ -614,7 +614,7 @@ def train_epoch_ch8(net, train_iter, loss, updater, device, use_random_iter): else: l.backward() grad_clipping(net, 1) - # 因为已经调用了`mean`函数 + # 因为已经调用了mean函数 updater(batch_size=1) metric.add(l * d2l.size(y), d2l.size(y)) return math.exp(metric[0] / metric[1]), metric[1] / timer.stop() @@ -629,7 +629,7 @@ def train_epoch_ch8(net, train_iter, loss, updater, use_random_iter): metric = d2l.Accumulator(2) # 训练损失之和,词元数量 for X, Y in train_iter: if state is None or use_random_iter: - # 在第一次迭代或使用随机抽样时初始化`state` + # 在第一次迭代或使用随机抽样时初始化state state = net.begin_state(batch_size=X.shape[0], dtype=tf.float32) with tf.GradientTape(persistent=True) as g: y_hat, state = net(X, state) diff --git a/chapter_recurrent-neural-networks/sequence.md b/chapter_recurrent-neural-networks/sequence.md index 7b3ad4368..efb90a4ae 100644 --- a/chapter_recurrent-neural-networks/sequence.md +++ b/chapter_recurrent-neural-networks/sequence.md @@ -231,7 +231,7 @@ labels = d2l.reshape(x[tau:], (-1, 1)) ```{.python .input} #@tab all batch_size, n_train = 16, 600 -# 只有前`n_train`个样本用于训练 +# 只有前n_train个样本用于训练 train_iter = d2l.load_array((features[:n_train], labels[:n_train]), batch_size, is_train=True) ``` diff --git a/chapter_recurrent-neural-networks/text-preprocessing.md b/chapter_recurrent-neural-networks/text-preprocessing.md index d5732205a..aaecca137 100644 --- a/chapter_recurrent-neural-networks/text-preprocessing.md +++ b/chapter_recurrent-neural-networks/text-preprocessing.md @@ -145,7 +145,7 @@ class Vocab: #@save def count_corpus(tokens): #@save """统计词元的频率""" - # 这里的`tokens`是1D列表或2D列表 + # 这里的tokens是1D列表或2D列表 if len(tokens) == 0 or isinstance(tokens[0], list): # 将词元列表展平成一个列表 tokens = [token for line in tokens for token in line] From a30ff701ea009217731c4dcc71c34569a2889297 Mon Sep 17 00:00:00 2001 From: Jannchie Date: Thu, 9 Dec 2021 03:12:08 +0900 Subject: [PATCH 19/21] Update linear-algebra.md (#1040) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 删除了多余的逗号。 --- chapter_preliminaries/linear-algebra.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/chapter_preliminaries/linear-algebra.md b/chapter_preliminaries/linear-algebra.md index e4cff91f4..2fa06dab0 100644 --- a/chapter_preliminaries/linear-algebra.md +++ b/chapter_preliminaries/linear-algebra.md @@ -898,7 +898,7 @@ tf.norm(tf.ones((4, 9))) 例如,矩阵可以分解为因子,这些分解可以显示真实世界数据集中的低维结构。 机器学习的整个子领域都侧重于使用矩阵分解及其向高阶张量的泛化,来发现数据集中的结构并解决预测问题。 我们相信,一旦你开始动手尝试并在真实数据集上应用了有效的机器学习模型,你会更倾向于学习更多数学。 -因此,这一节到此结束,我们保留在后面介绍更多数学知识的权利,。 +因此,这一节到此结束,我们保留在后面介绍更多数学知识的权利。 如果你渴望了解有关线性代数的更多信息,你可以参考[线性代数运算的在线附录](https://d2l.ai/chapter_appendix-mathematics-for-deep-learning/geometry-linear-algebraic-ops.html)或其他优秀资源 :cite:`Strang.1993,Kolter.2008,Petersen.Pedersen.ea.2008`。 From 1064a9019a9cc3e97b1b2b4ee9eaef597b16ad38 Mon Sep 17 00:00:00 2001 From: Ryan <45566932+Ryan-ly@users.noreply.github.com> Date: Thu, 9 Dec 2021 02:13:00 +0800 Subject: [PATCH 20/21] Update anchor.md (#1034) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Update anchor.md ### 一个例子 中翻译不准确的地方 锚箱标签->锚框标签 //锚框在本书中使用的更多,锚箱看起来很奇怪 地面真实边界框->真实边界框 //ground truth中的ground翻译为了地面,但是ground truth应该指的是真实框而与地面无关 * Update anchor.md Hey @Ryan-ly , from your inspiration, i removed other `地面` occurrences. Thanks! Co-authored-by: goldmermaid --- chapter_computer-vision/anchor.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/chapter_computer-vision/anchor.md b/chapter_computer-vision/anchor.md index 1c0d34b9e..8278cdcc1 100644 --- a/chapter_computer-vision/anchor.md +++ b/chapter_computer-vision/anchor.md @@ -35,7 +35,7 @@ torch.set_printoptions(2) # 精简输出精度 要生成多个不同形状的锚框,让我们设置许多缩放比(scale)取值$s_1,\ldots, s_n$和许多宽高比(aspect ratio)取值$r_1,\ldots, r_m$。 当使用这些比例和长宽比的所有组合以每个像素为中心时,输入图像将总共有$whnm$个锚框。 -这些锚框可能会覆盖所有真实边界框,但这么多的锚框很容易带来过高的计算复杂性。 +尽管这些锚框可能会覆盖所有真实边界框,但计算复杂性很容易过高。 在实践中,(**我们只考虑**)包含$s_1$或$r_1$的(**组合:**) (** From 2464143546f13f53527ef1197b97c7459ceefd38 Mon Sep 17 00:00:00 2001 From: Aston Zhang Date: Wed, 8 Dec 2021 19:45:12 +0000 Subject: [PATCH 21/21] Link SM studio lab to getting started page --- static/build_html.sh | 2 +- static/frontpage/frontpage.html | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/static/build_html.sh b/static/build_html.sh index 97b03469b..312b6a1fa 100755 --- a/static/build_html.sh +++ b/static/build_html.sh @@ -20,5 +20,5 @@ done # Add SageMaker Studio Lab buttons for f in _build/html/chapter*/*.html; do - sed -i s/Open\ the\ notebook\ in\ Colab\<\\\/div\>\<\\\/div\>\<\\\/div\>\<\\\/h1\>/Open\ the\ notebook\ in\ Colab\<\\\/div\>\<\\\/div\>\<\\\/div\>\\ \\ \\<\\\/i\>\ SageMaker\ Studio\ Lab\ \<\\\/button\>\<\\\/a\>\\ Open\ the\ notebook\ in\ SageMaker\ Studio\ Lab\<\\\/div\>\<\\\/h1\>/g $f + sed -i s/Open\ the\ notebook\ in\ Colab\<\\\/div\>\<\\\/div\>\<\\\/div\>\<\\\/h1\>/Open\ the\ notebook\ in\ Colab\<\\\/div\>\<\\\/div\>\<\\\/div\>\\ \\ \\<\\\/i\>\ SageMaker\ Studio\ Lab\ \<\\\/button\>\<\\\/a\>\\ Open\ the\ notebook\ in\ SageMaker\ Studio\ Lab\<\\\/div\>\<\\\/h1\>/g $f done diff --git a/static/frontpage/frontpage.html b/static/frontpage/frontpage.html index 553201059..cd2187fe3 100644 --- a/static/frontpage/frontpage.html +++ b/static/frontpage/frontpage.html @@ -373,7 +373,7 @@

每一小节都是可以运行的 Jupyter 记事本

本地运行