Socket
Socket
Sign inDemoInstall

nlp-pytorch-zh

Package Overview
Dependencies
0
Maintainers
1
Versions
2
Alerts
File Explorer

Advanced tools

Install Socket

Detect and block malicious and high-risk dependencies

Install

Comparing version 2020.919.0 to 2021.104.0

doc/asset/docsify-apachecn-footer.js

0

doc/asset/docsify-copy-code.min.js

@@ -0,0 +0,0 @@ /*!

!function(){var h={},f={EXPIRE_KEY:"docsify.search.expires",INDEX_KEY:"docsify.search.index"};function l(e){var n={"&":"&amp;","<":"&lt;",">":"&gt;",'"':"&quot;","'":"&#39;"};return String(e).replace(/[&<>"']/g,function(e){return n[e]})}function p(e){return e.text||"table"!==e.type||(e.cells.unshift(e.header),e.text=e.cells.map(function(e){return e.join(" | ")}).join(" |\n ")),e.text}function u(r,e,i,o){void 0===e&&(e="");var s,n=window.marked.lexer(e),c=window.Docsify.slugify,d={};return n.forEach(function(e){if("heading"===e.type&&e.depth<=o){var n=function(e){void 0===e&&(e="");var a={};return{str:e=e&&e.replace(/^'/,"").replace(/'$/,"").replace(/(?:^|\s):([\w-]+:?)=?([\w-%]+)?/g,function(e,n,t){return-1===n.indexOf(":")?(a[n]=t&&t.replace(/&quot;/g,"")||!0,""):e}).trim(),config:a}}(e.text),t=n.str,a=n.config;s=a.id?i.toURL(r,{id:c(a.id)}):i.toURL(r,{id:c(l(e.text))}),d[s]={slug:s,title:t,body:""}}else{if(!s)return;d[s]?d[s].body?(e.text=p(e),d[s].body+="\n"+(e.text||"")):(e.text=p(e),d[s].body=d[s].body?d[s].body+e.text:e.text):d[s]={slug:s,title:"",body:""}}}),c.clear(),d}function c(e){var r=[],i=[];Object.keys(h).forEach(function(n){i=i.concat(Object.keys(h[n]).map(function(e){return h[n][e]}))});var o=(e=e.trim()).split(/[\s\-,\\/]+/);1!==o.length&&(o=[].concat(e,o));function n(e){var n=i[e],s=0,c="",d=n.title&&n.title.trim(),p=n.body&&n.body.trim(),t=n.slug||"";if(d&&(o.forEach(function(e){var n,t=new RegExp(e.replace(/[|\\{}()[\]^$+*?.]/g,"\\$&"),"gi"),a=-1;if(n=d?d.search(t):-1,a=p?p.search(t):-1,0<=n||0<=a){s+=0<=n?3:0<=a?2:0,a<0&&(a=0);var r,i=0;i=0==(r=a<11?0:a-10)?70:a+e.length+60,p&&i>p.length&&(i=p.length);var o="..."+l(p).substring(r,i).replace(t,function(e){return'<em class="search-keyword">'+e+"</em>"})+"...";c+=o}}),0<s)){var a={title:l(d),content:p?c:"",url:t,score:s};r.push(a)}}for(var t=0;t<i.length;t++)n(t);return r.sort(function(e,n){return n.score-e.score})}function i(t,a){var e="auto"===t.paths,n=e?function(r){var i=[];return Docsify.dom.findAll(".sidebar-nav a:not(.section-link):not([data-nosearch])").forEach(function(e){var n=e.href,t=e.getAttribute("href"),a=r.parse(n).path;a&&-1===i.indexOf(a)&&!Docsify.util.isAbsolutePath(t)&&i.push(a)}),i}(a.router):t.paths,r="";if(e&&t.pathNamespaces){var i=n[0];if(Array.isArray(t.pathNamespaces))r=t.pathNamespaces.find(function(e){return i.startsWith(e)})||r;else if(t.pathNamespaces instanceof RegExp){var o=i.match(t.pathNamespaces);o&&(r=o[0])}}var s=function(e){return e?f.EXPIRE_KEY+"/"+e:f.EXPIRE_KEY}(t.namespace)+r,c=function(e){return e?f.INDEX_KEY+"/"+e:f.INDEX_KEY}(t.namespace)+r,d=localStorage.getItem(s)<Date.now();if(h=JSON.parse(localStorage.getItem(c)),d)h={};else if(!e)return;var p=n.length,l=0;n.forEach(function(n){if(h[n])return l++;Docsify.get(a.router.getFile(n),!1,a.config.requestHeaders).then(function(e){h[n]=u(n,e,a.router,t.depth),p===++l&&function(e,n,t){localStorage.setItem(n,Date.now()+e),localStorage.setItem(t,JSON.stringify(h))}(t.maxAge,s,c)})})}var d,m="";function r(e){var n=Docsify.dom.find("div.search"),t=Docsify.dom.find(n,".results-panel"),a=Docsify.dom.find(n,".clear-button"),r=Docsify.dom.find(".sidebar-nav"),i=Docsify.dom.find(".app-name");if(!e)return t.classList.remove("show"),a.classList.remove("show"),t.innerHTML="",void(d.hideOtherSidebarContent&&(r.classList.remove("hide"),i.classList.remove("hide")));var o=c(e),s="";o.forEach(function(e){s+='<div class="matching-post">\n<a href="'+e.url+'">\n<h2>'+e.title+"</h2>\n<p>"+e.content+"</p>\n</a>\n</div>"}),t.classList.add("show"),a.classList.add("show"),t.innerHTML=s||'<p class="empty">'+m+"</p>",d.hideOtherSidebarContent&&(r.classList.add("hide"),i.classList.add("hide"))}function a(e){d=e}function o(e,n){var t=n.router.parse().query.s;a(e),Docsify.dom.style("\n.sidebar {\n padding-top: 0;\n}\n\n.search {\n margin-bottom: 20px;\n padding: 6px;\n border-bottom: 1px solid #eee;\n}\n\n.search .input-wrap {\n display: flex;\n align-items: center;\n}\n\n.search .results-panel {\n display: none;\n}\n\n.search .results-panel.show {\n display: block;\n}\n\n.search input {\n outline: none;\n border: none;\n width: 100%;\n padding: 0 7px;\n line-height: 36px;\n font-size: 14px;\n border: 1px solid transparent;\n}\n\n.search input:focus {\n box-shadow: 0 0 5px var(--theme-color, #42b983);\n border: 1px solid var(--theme-color, #42b983);\n}\n\n.search input::-webkit-search-decoration,\n.search input::-webkit-search-cancel-button,\n.search input {\n -webkit-appearance: none;\n -moz-appearance: none;\n appearance: none;\n}\n.search .clear-button {\n cursor: pointer;\n width: 36px;\n text-align: right;\n display: none;\n}\n\n.search .clear-button.show {\n display: block;\n}\n\n.search .clear-button svg {\n transform: scale(.5);\n}\n\n.search h2 {\n font-size: 17px;\n margin: 10px 0;\n}\n\n.search a {\n text-decoration: none;\n color: inherit;\n}\n\n.search .matching-post {\n border-bottom: 1px solid #eee;\n}\n\n.search .matching-post:last-child {\n border-bottom: 0;\n}\n\n.search p {\n font-size: 14px;\n overflow: hidden;\n text-overflow: ellipsis;\n display: -webkit-box;\n -webkit-line-clamp: 2;\n -webkit-box-orient: vertical;\n}\n\n.search p.empty {\n text-align: center;\n}\n\n.app-name.hide, .sidebar-nav.hide {\n display: none;\n}"),function(e){void 0===e&&(e="");var n='<div class="input-wrap">\n <input type="search" value="'+e+'" aria-label="Search text" />\n <div class="clear-button">\n <svg width="26" height="24">\n <circle cx="12" cy="12" r="11" fill="#ccc" />\n <path stroke="white" stroke-width="2" d="M8.25,8.25,15.75,15.75" />\n <path stroke="white" stroke-width="2"d="M8.25,15.75,15.75,8.25" />\n </svg>\n </div>\n </div>\n <div class="results-panel"></div>\n </div>',t=Docsify.dom.create("div",n),a=Docsify.dom.find("aside");Docsify.dom.toggleClass(t,"search"),Docsify.dom.before(a,t)}(t),function(){var e,n=Docsify.dom.find("div.search"),t=Docsify.dom.find(n,"input"),a=Docsify.dom.find(n,".input-wrap");Docsify.dom.on(n,"click",function(e){return-1===["A","H2","P","EM"].indexOf(e.target.tagName)&&e.stopPropagation()}),Docsify.dom.on(t,"input",function(n){clearTimeout(e),e=setTimeout(function(e){return r(n.target.value.trim())},100)}),Docsify.dom.on(a,"click",function(e){"INPUT"!==e.target.tagName&&(t.value="",r())})}(),t&&setTimeout(function(e){return r(t)},500)}function s(e,n){a(e),function(e,n){var t=Docsify.dom.getNode('.search input[type="search"]');if(t)if("string"==typeof e)t.placeholder=e;else{var a=Object.keys(e).filter(function(e){return-1<n.indexOf(e)})[0];t.placeholder=e[a]}}(e.placeholder,n.route.path),function(e,n){if("string"==typeof e)m=e;else{var t=Object.keys(e).filter(function(e){return-1<n.indexOf(e)})[0];m=e[t]}}(e.noData,n.route.path)}var g={placeholder:"Type to search",noData:"No Results!",paths:"auto",depth:2,maxAge:864e5,hideOtherSidebarContent:!1,namespace:void 0,pathNamespaces:void 0};$docsify.plugins=[].concat(function(e,n){var t=Docsify.util,a=n.config.search||g;Array.isArray(a)?g.paths=a:"object"==typeof a&&(g.paths=Array.isArray(a.paths)?a.paths:"auto",g.maxAge=t.isPrimitive(a.maxAge)?a.maxAge:g.maxAge,g.placeholder=a.placeholder||g.placeholder,g.noData=a.noData||g.noData,g.depth=a.depth||g.depth,g.hideOtherSidebarContent=a.hideOtherSidebarContent||g.hideOtherSidebarContent,g.namespace=a.namespace||g.namespace,g.pathNamespaces=a.pathNamespaces||g.pathNamespaces);var r="auto"===g.paths;e.mounted(function(e){o(g,n),r||i(g,n)}),e.doneEach(function(e){s(g,n),r&&i(g,n)})},$docsify.plugins)}();

230

doc/docs/1.md

@@ -1,20 +0,20 @@

# Chapter 1.基础介绍
# 一、基础介绍
> 本文标题:[Natural-Language-Processing-with-PyTorch(一)](https://yifdu.github.io/2018/12/17/Natural-Language-Processing-with-PyTorch%EF%BC%88%E4%B8%80%EF%BC%89/)
> 本文标题:[Natural-Language-Processing-with-PyTorch(一)](https://yifdu.github.io/2018/12/17/Natural-Language-Processing-with-PyTorch%EF%BC%88%E4%B8%80%EF%BC%89/)
>
> 文章作者:[Yif Du](https://yifdu.github.io/ "访问 Yif Du 的个人博客")
> 文章作者:[Yif Du](https://yifdu.github.io/ "访问 Yif Du 的个人博客")
>
> 发布时间:2018年12月17日 - 09:12
> 发布时间:2018 年 12 月 17 日 - 09:12
>
> 最后更新:2019年02月16日 - 21:02
> 最后更新:2019 年 02 月 16 日 - 21:02
>
> 原始链接:[http://yifdu.github.io/2018/12/17/Natural-Language-Processing-with-PyTorch(一)/](https://yifdu.github.io/2018/12/17/Natural-Language-Processing-with-PyTorch%EF%BC%88%E4%B8%80%EF%BC%89/)
> 原始链接:[http://yifdu.github.io/2018/12/17/Natural-Language-Processing-with-PyTorch(一)/](https://yifdu.github.io/2018/12/17/Natural-Language-Processing-with-PyTorch%EF%BC%88%E4%B8%80%EF%BC%89/)
>
> 许可协议: [署名-非商业性使用-禁止演绎 4.0 国际](https://creativecommons.org/licenses/by-nc-nd/4.0/) 转载请保留原文链接及作者。
> 许可协议:[署名-非商业性使用-禁止演绎 4.0 国际](https://creativecommons.org/licenses/by-nc-nd/4.0/) 转载请保留原文链接及作者。
像Echo (Alexa)、Siri和谷歌Translate这样的家喻户晓的产品名称至少有一个共同点。它们都是自然语言处理(NLP)应用的产物,NLP是本书的两个主要主题之一。NLP是一套运用统计方法的技术,无论是否有语言学的洞见,为了解决现实世界的任务而理解文本。这种对文本的“理解”主要是通过将文本转换为可用的计算表示,这些计算表示是离散或连续的组合结构,如向量或张量、图形和树。
像 Echo (Alexa)、Siri 和谷歌翻译这样的家喻户晓的产品名称至少有一个共同点。它们都是自然语言处理(NLP)应用的产物,NLP 是本书的两个主要主题之一。NLP 是一套运用统计方法的技术,无论是否有语言学的洞见,为了解决现实世界的任务而理解文本。这种对文本的“理解”主要是通过将文本转换为可用的计算表示,这些计算表示是离散或连续的组合结构,如向量或张量、图形和树。
从数据(本例中为文本)中学习适合于任务的表示形式是机器学习的主题。应用机器学习的文本数据有超过三十年的历史,但最近(2008年至2010年开始) [1] 一组机器学习技术,被称为深度学习,继续发展和证明非常有效的各种人工智能(AI)在NLP中的任务,演讲,和计算机视觉。深度学习是我们要讲的另一个主题;因此,本书是关于NLP和深度学习的研究。
从数据(本例中为文本)中学习适合于任务的表示形式是机器学习的主题。应用机器学习的文本数据有超过三十年的历史,但最近(2008 年至 2010 年开始) [1] 一组机器学习技术,被称为深度学习,继续发展和证明非常有效的各种人工智能(AI)在 NLP 中的任务,演讲,和计算机视觉。深度学习是我们要讲的另一个主题;因此,本书是关于 NLP 和深度学习的研究。
简单地说,深度学习使人们能够使用一种称为计算图和数字优化技术的抽象概念有效地从数据中学习表示。这就是深度学习和计算图的成功之处,像谷歌、Facebook和Amazon这样的大型技术公司已经发布了基于它们的计算图形框架和库的实现,以捕捉研究人员和工程师的思维。在本书中,我们考虑PyTorch,一个越来越流行的基于python的计算图框架库来实现深度学习算法。在本章中,我们将解释什么是计算图,以及我们选择使用PyTorch作为框架。机器学习和深度学习的领域是广阔的。在这一章,在本书的大部分时间里,我们主要考虑的是所谓的监督学习;也就是说,使用标记的训练示例进行学习。我们解释了监督学习范式,这将成为本书的基础。如果到目前为止您还不熟悉其中的许多术语,那么您是对的。这一章,以及未来的章节,不仅澄清了这一点,而且深入研究了它们。如果您已经熟悉这里提到的一些术语和概念,我们仍然鼓励您遵循以下两个原因:为本书其余部分建立一个共享的词汇表,以及填补理解未来章节所需的任何空白。
简单地说,深度学习使人们能够使用一种称为计算图和数字优化技术的抽象概念有效地从数据中学习表示。这就是深度学习和计算图的成功之处,像谷歌、Facebook 和 Amazon 这样的大型技术公司已经发布了基于它们的计算图形框架和库的实现,以捕捉研究人员和工程师的思维。在本书中,我们考虑 PyTorch,一个越来越流行的基于 python 的计算图框架库来实现深度学习算法。在本章中,我们将解释什么是计算图,以及我们选择使用 PyTorch 作为框架。机器学习和深度学习的领域是广阔的。在这一章,在本书的大部分时间里,我们主要考虑的是所谓的监督学习;也就是说,使用标记的训练示例进行学习。我们解释了监督学习范式,这将成为本书的基础。如果到目前为止您还不熟悉其中的许多术语,那么您是对的。这一章,以及未来的章节,不仅澄清了这一点,而且深入研究了它们。如果您已经熟悉这里提到的一些术语和概念,我们仍然鼓励您遵循以下两个原因:为本书其余部分建立一个共享的词汇表,以及填补理解未来章节所需的任何空白。

@@ -26,33 +26,37 @@ 本章的目标是:

* 理解什么是计算图
* 掌握PyTorch的基本知识
* 掌握 PyTorch 的基本知识
让我们开始吧!
## The Supervised Learning Paradigm
## 监督学习范式
机器学习中的监督,或者简单的监督学习,是指将目标(被预测的内容)的ground truth用于观察(输入)的情况。例如,在文档分类中,目标是一个分类标签,观察(输入)是一个文档。例如,在机器翻译中,观察(输入)是一种语言的句子,目标是另一种语言的句子。通过对输入数据的理解,我们在图1-1中演示了监督学习范式。 ![nlpp_0101](img/58c13549534aa235ea3328c43765c9f9.jpg "图1-1 监督学习范式,一种从标记输入数据中学习的概念框架。")
机器学习中的监督,或者简单的监督学习,是指将目标(被预测的内容)的真实情况用于观察(输入)的情况。例如,在文档分类中,目标是一个分类标签,观察(输入)是一个文档。例如,在机器翻译中,观察(输入)是一种语言的句子,目标是另一种语言的句子。通过对输入数据的理解,我们在图 1-1 中演示了监督学习范式。
我们可以将监督学习范式分解为六个主要概念,如图1-1所示: Observations: 观察是我们想要预测的东西。我们用x表示观察值。我们有时把观察值称为“输入”。 Targets: 目标是与观察相对应的标签。它通常是被预言的事情。按照机器学习/深度学习中的标准符号,我们用y表示这些。有时,这被称为ground truth 。 Model: 模型是一个数学表达式或函数,它接受一个观察值x,并预测其目标标签的值。 Parameters: 有时也称为权重,这些参数化模型。标准使用的符号w(权重)或 w_hat。 Predictions: 预测,也称为估计,是模型在给定观测值的情况下所猜测目标的值。我们用一个“hat”表示这些。所以,目标y的预测用 y_hat 来表示。 Loss function: 损失函数是比较预测与训练数据中观测目标之间的距离的函数。给定一个目标及其预测,损失函数将分配一个称为损失的标量实值。损失值越低,模型对目标的预测效果越好。我们用L表示损失函数。
![nlpp_0101](img/58c13549534aa235ea3328c43765c9f9.jpg)
虽然在NLP /深度学习建模或编写本书时,这在数学上并不是正式有效,但我们将正式重述监督学习范例,以便为该领域的新读者提供标准术语,以便他们拥有熟悉arXiv研究论文中的符号和写作风格。
我们可以将监督学习范式分解为六个主要概念,如图 1-1 所示: 观察: 观察是我们想要预测的东西。我们用`x`表示观察值。我们有时把观察值称为“输入”。 目标: 目标是与观察相对应的标签。它通常是被预言的事情。按照机器学习/深度学习中的标准符号,我们用`y`表示这些。有时,这被称为真实情况。 模型: 模型是一个数学表达式或函数,它接受一个观察值`x`,并预测其目标标签的值。 参数: 有时也称为权重,这些参数化模型。标准使用的符号`w`(权重)或`w_hat`。 预测: 预测,也称为估计,是模型在给定观测值的情况下所猜测目标的值。我们用一个`hat`表示这些。所以,目标`y`的预测用`y_hat`来表示。 损失函数: 损失函数是比较预测与训练数据中观测目标之间的距离的函数。给定一个目标及其预测,损失函数将分配一个称为损失的标量实值。损失值越低,模型对目标的预测效果越好。我们用`L`表示损失函数。
考虑一个数据集 D={X[i],y[i]},i=1..n,有n个例子。给定这个数据集,我们想要学习一个由权值w参数化的函数(模型)f,也就是说,我们对f的结构做一个假设,给定这个结构,权值w的学习值将充分表征模型。对于一个给定的输入X,模型预测 y_hat 作为目标: y_hat = f(X;W) 在监督学习中,对于训练例子,我们知道观察的真正目标y。这个实例的损失将为 L(y, y_hat) 。然后,监督学习就变成了一个寻找最优参数/权值w的过程,从而使所有n个例子的累积损失最小化。
虽然在 NLP /深度学习建模或编写本书时,这在数学上并不是正式有效,但我们将正式重述监督学习范例,以便为该领域的新读者提供标准术语,以便他们拥有熟悉 arXiv 研究论文中的符号和写作风格。
考虑一个数据集`D={X[i],y[i]}, i=1..n`,有`n`个例子。给定这个数据集,我们想要学习一个由权值`w`参数化的函数(模型)`f`,也就是说,我们对`f`的结构做一个假设,给定这个结构,权值`w`的学习值将充分表征模型。对于一个给定的输入`X`,模型预测`y_hat`作为目标: `y_hat = f(X;W)`在监督学习中,对于训练例子,我们知道观察的真正目标`y`。这个实例的损失将为`L(y, y_hat)`。然后,监督学习就变成了一个寻找最优参数/权值`w`的过程,从而使所有`n`个例子的累积损失最小化。
* * *
利用(随机)梯度下降法进行训练 监督学习的目标是为给定的数据集选择参数值,使损失函数最小化。换句话说,这等价于在方程中求根。我们知道梯度下降法是一种常见的求方程根的方法。回忆一下,在传统的梯度下降法中,我们对根(参数)的一些初值进行猜测,并迭代更新这些参数,直到目标函数(损失函数)的计算值低于可接受阈值(即收敛准则)。对于大型数据集,由于内存限制,在整个数据集上实现传统的梯度下降通常是不可能的,而且由于计算开销,速度非常慢。相反,通常采用一种近似的梯度下降称为随机梯度下降(SGD)。在随机情况下,数据点或数据点的子集是随机选择的,并计算该子集的梯度。当使用单个数据点时,这种方法称为纯SGD,当使用(多个)数据点的子集时,我们将其称为小型批处理SGD。通常情况下,“纯”和“小型批处理”这两个词在根据上下文变得清晰时就会被删除。在实际应用中,很少使用纯SGD,因为它会由于有噪声的更新而导致非常慢的收敛。一般SGD算法有不同的变体,都是为了更快的收敛。在后面的章节中,我们将探讨这些变体中的一些,以及如何使用渐变来更新参数。这种迭代更新参数的过程称为反向传播。反向传播的每个步骤(又名epoch)由向前传递和向后传递组成。向前传递用参数的当前值计算输入并计算损失函数。反向传递使用损失梯度更新参数。
利用(随机)梯度下降法进行训练 监督学习的目标是为给定的数据集选择参数值,使损失函数最小化。换句话说,这等价于在方程中求根。我们知道梯度下降法是一种常见的求方程根的方法。回忆一下,在传统的梯度下降法中,我们对根(参数)的一些初值进行猜测,并迭代更新这些参数,直到目标函数(损失函数)的计算值低于可接受阈值(即收敛准则)。对于大型数据集,由于内存限制,在整个数据集上实现传统的梯度下降通常是不可能的,而且由于计算开销,速度非常慢。相反,通常采用一种近似的梯度下降称为随机梯度下降(SGD)。在随机情况下,数据点或数据点的子集是随机选择的,并计算该子集的梯度。当使用单个数据点时,这种方法称为纯 SGD,当使用(多个)数据点的子集时,我们将其称为小型批量 SGD。通常情况下,“纯”和“小型批量”这两个词在根据上下文变得清晰时就会被删除。在实际应用中,很少使用纯 SGD,因为它会由于有噪声的更新而导致非常慢的收敛。一般 SGD 算法有不同的变体,都是为了更快的收敛。在后面的章节中,我们将探讨这些变体中的一些,以及如何使用渐变来更新参数。这种迭代更新参数的过程称为反向传播。反向传播的每个步骤(又名周期)由向前传递和向后传递组成。向前传递用参数的当前值计算输入并计算损失函数。反向传递使用损失梯度更新参数。
* * *
请注意,到目前为止,这里没有什么是专门针对深度学习或神经网络的。图1-1中箭头的方向表示训练系统时数据的“流”。关于训练和“计算图”中“流”的概念,我们还有更多要说的,但首先,让我们看看如何用数字表示NLP问题中的输入和目标,这样我们就可以训练模型并预测结果。
请注意,到目前为止,这里没有什么是专门针对深度学习或神经网络的。图 1-1 中箭头的方向表示训练系统时数据的“流”。关于训练和“计算图”中“流”的概念,我们还有更多要说的,但首先,让我们看看如何用数字表示 NLP 问题中的输入和目标,这样我们就可以训练模型并预测结果。
## Observation and Target Encoding
## 观测和目标编码
我们需要用数字表示观测值(文本),以便与机器学习算法一起使用。图1-2给出了一个可视化的描述。 ![nlpp_0102](img/a63991e391610b511b1fcd8202f6cb8c.jpg "图1-2 观察和目标编码:注意图1-1中的目标和观察是如何用向量或张量表示的。这被统称为输入“编码”。")
我们需要用数字表示观测值(文本),以便与机器学习算法一起使用。图 1-2 给出了一个可视化的描述。
![nlpp_0102](img/a63991e391610b511b1fcd8202f6cb8c.jpg)
表示文本的一种简单方法是用数字向量表示。有无数种方法可以执行这种映射/表示。事实上,本书的大部分内容都致力于从数据中学习此类任务表示。然而,我们从基于启发式的一些简单的基于计数的表示开始。虽然简单,但是它们非常强大,或者可以作为更丰富的表示学习的起点。所有这些基于计数的表示都是从一个固定维数的向量开始的。
### One-Hot Representation
### 单热表示
顾名思义,one-hot表示从一个零向量开始,如果单词出现在句子或文档中,则将向量中的相应条目设置为1。考虑下面两句话。
顾名思义,单热表示从一个零向量开始,如果单词出现在句子或文档中,则将向量中的相应条目设置为 1。考虑下面两句话。

@@ -65,21 +69,21 @@ ```py

对句子进行标记,忽略标点符号,并将所有的单词都用小写字母表示,就会得到一个大小为8的词汇表:{time, fruit, flies, like, a, an, arrow, banana}。所以,我们可以用一个八维的one-hot向量来表示每个单词。在本书中,我们使用 1[w] 表示令牌/单词w的one-hot表示。
对句子进行标记,忽略标点符号,并将所有的单词都用小写字母表示,就会得到一个大小为 8 的词汇表:`{time, fruit, flies, like, a, an, arrow, banana}`。所以,我们可以用一个八维的单热向量来表示每个单词。在本书中,我们使用`1[w]`表示标记/单词`w`的单热表示。
对于短语、句子或文档,压缩的one-hot表示仅仅是其组成词的逻辑或的one-hot表示。使用图1-3所示的编码,短语“like a banana”的one-hot表示将是一个3×8矩阵,其中的列是8维的one-hot向量。通常还会看到“折叠”或二进制编码,其中文本/短语由词汇表长度的向量表示,用0和1表示单词的缺失或存在。“like a banana”的二进制编码是:[0,0,0,1,1,0,0,1]。
对于短语、句子或文档,压缩的单热表示仅仅是其组成词的逻辑或的单热表示。使用图 1-3 所示的编码,短语`like a banana`的单热表示将是一个`3×8`矩阵,其中的列是 8 维的单热向量。通常还会看到“折叠”或二进制编码,其中文本/短语由词汇表长度的向量表示,用 0 和 1 表示单词的缺失或存在。`like a banana`的二进制编码是:`[0,0,0,1,1,0,0,1]`。
![nlpp_0103](img/bf2571d77271c27a0bb65f60aa4926c0.jpg "图1-3 “Time flies like an arrow”和“Fruit flies like a banana.”这两个句子的one-hot表示形式。")
![nlpp_0103](img/bf2571d77271c27a0bb65f60aa4926c0.jpg)
* * *
NOTE 在这一点上,如果你觉得我们把“flies”的两种不同的意思(或感觉)搞混了,恭喜你,聪明的读者!语言充满了歧义,但是我们仍然可以通过极其简化的假设来构建有用的解决方案。学习特定于意义的表示是可能的,但是我们现在做得有些超前了。
注意:在这一点上,如果你觉得我们把`flies`的两种不同的意思(或感觉)搞混了,恭喜你,聪明的读者!语言充满了歧义,但是我们仍然可以通过极其简化的假设来构建有用的解决方案。学习特定于意义的表示是可能的,但是我们现在做得有些超前了。
* * *
尽管对于本书中的输入,我们很少使用除了one-hot表示之外的其他表示,但是由于NLP中受欢迎、历史原因和完成目的,我们现在介绍术语频率(TF)和术语频率反转文档频率(TF-idf)表示。这些表示在信息检索(IR)中有着悠久的历史,甚至在今天的生产NLP系统中也得到了广泛的应用。(翻译有不足)
尽管对于本书中的输入,我们很少使用除了单热表示之外的其他表示,但是由于 NLP 中受欢迎、历史原因和完成目的,我们现在介绍术语频率(TF)和术语频率反转文档频率(TF-idf)表示。这些表示在信息检索(IR)中有着悠久的历史,甚至在今天的生产 NLP 系统中也得到了广泛的应用。(翻译有不足)
### TF Representation
### TF 表示
短语、句子或文档的TF表示仅仅是构成词的one-hot的总和。为了继续我们愚蠢的示例,使用前面提到的one-hot编码,“Fruit flies like time flies a fruit”这句话具有以下TF表示:[1,2,2,1,1,1,0,0]。注意,每个条目是句子(语料库)中出现相应单词的次数的计数。我们用TF(w)表示一个单词的TF。
短语、句子或文档的 TF 表示仅仅是构成词的单热的总和。为了继续我们愚蠢的示例,使用前面提到的单热编码,`Fruit flies like time flies a fruit`这句话具有以下 TF 表示:[1,2,2,1,1,1,0,0]。注意,每个条目是句子(语料库)中出现相应单词的次数的计数。我们用 TF(w)表示一个单词的 TF。
Example 1-1\. Generating a “collapsed” one-hot or binary representation using scikit-learn
示例 1-1:使用 sklearn 生成“塌陷的”单热或二进制表示

@@ -100,15 +104,15 @@ ```py

![1571305217127](img/1571305217127.jpg "图1-4 由示例1-1生成的折叠one-hot表示。")
![1571305217127](img/1571305217127.jpg "图 1-4 由示例 1-1 生成的折叠单热表示。")
折叠的onehot是一个向量中有多个1的onehot
折叠的单热是一个向量中有多个 1 的单热
### TF-IDF Representation
### TF-IDF 表示
考虑一组专利文件。您可能希望它们中的大多数都有诸如claim、system、method、procedure等单词,并且经常重复多次。TF表示对更频繁的单词进行加权。然而,像“claim”这样的常用词并不能增加我们对具体专利的理解。相反,如果“tetrafluoroethylene”这样罕见的词出现的频率较低,但很可能表明专利文件的性质,我们希望在我们的表述中赋予它更大的权重。反文档频率(IDF)是一种启发式算法,可以精确地做到这一点。
考虑一组专利文件。您可能希望它们中的大多数都有诸如`claim`、`system`、`method`、`procedure`等单词,并且经常重复多次。TF 表示对更频繁的单词进行加权。然而,像`claim`这样的常用词并不能增加我们对具体专利的理解。相反,如果`tetrafluoroethylene`这样罕见的词出现的频率较低,但很可能表明专利文件的性质,我们希望在我们的表述中赋予它更大的权重。反文档频率(IDF)是一种启发式算法,可以精确地做到这一点。
**IDF表示惩罚常见的符号,并奖励向量表示中的罕见符号。** 符号w的IDF(w)对语料库的定义为
**IDF 表示惩罚常见的符号,并奖励向量表示中的罕见符号。** 符号`w`的`IDF(w)`对语料库的定义为
其中 n[w] 是包含单词w的文档数量,N是文档总数。TF-IDF分数就是TF(w) * IDF(w)的乘积。首先,请注意在所有文档(例如, n[w] = N), IDF(w)为0,TF-IDF得分为0,完全惩罚了这一项。其次,如果一个术语很少出现(可能只出现在一个文档中),那么IDF就是log n的最大值。
其中`n[w]`是包含单词`w`的文档数量,`N`是文档总数。TF-IDF 分数就是`TF(w) * IDF(w)`的乘积。首先,请注意在所有文档(例如,`n[w] = N`), `IDF(w)`为 0, TF-IDF 得分为 0,完全惩罚了这一项。其次,如果一个术语很少出现(可能只出现在一个文档中),那么 IDF 就是`log n`的最大值。
Example 1-2\. Generating TF-IDF representation using scikit-learn
示例 1-2:使用 sklearn 生产 TF-IDF 表示

@@ -126,35 +130,37 @@ ```py

![nlpp_01_tfidf](img/2fdbe9e5e1ab207ed4555c3ffd873753.jpg "图 1-5") 在深度学习中,很少看到使用像TF-IDF这样的启发式表示对输入进行编码,因为目标是学习一种表示。通常,我们从一个使用整数索引的one-hot编码和一个特殊的“embedding lookup”层开始构建神经网络的输入。在后面的章节中,我们将给出几个这样做的例子。
![nlpp_01_tfidf](img/2fdbe9e5e1ab207ed4555c3ffd873753.jpg)
### Target Encoding
在深度学习中,很少看到使用像 TF-IDF 这样的启发式表示对输入进行编码,因为目标是学习一种表示。通常,我们从一个使用整数索引的单热编码和一个特殊的“嵌入查找”层开始构建神经网络的输入。在后面的章节中,我们将给出几个这样做的例子。
正如“监督学习范式”所指出的,目标变量的确切性质取决于所解决的NLP任务。例如,在机器翻译、摘要和回答问题的情况下,目标也是文本,并使用前面描述的one-hot编码方法进行编码。
### 目标编码
许多NLP任务实际上使用分类标签,其中模型必须预测一组固定标签中的一个。对此进行编码的一种常见方法是对每个标签使用惟一索引。当输出标签的数量太大时,这种简单的表示可能会出现问题。这方面的一个例子是语言建模问题,在这个问题中,任务是预测下一个单词,给定过去看到的单词。标签空间是一种语言的全部词汇,它可以很容易地增长到几十万,包括特殊字符、名称等等。我们将在后面的章节中重新讨论这个问题以及如何解决这个问题。
正如“监督学习范式”所指出的,目标变量的确切性质取决于所解决的 NLP 任务。例如,在机器翻译、摘要和回答问题的情况下,目标也是文本,并使用前面描述的单热编码方法进行编码。
一些NLP问题涉及从给定文本中预测一个数值。例如,给定一篇英语文章,我们可能需要分配一个数字评分或可读性评分。给定一个餐馆评论片段,我们可能需要预测直到小数点后第一位的星级。给定用户的推文,我们可能需要预测用户的年龄群。有几种方法可以对数字目标进行编码,但是将目标简单地绑定到分类“容器”中(例如,“0-18”、“19-25”、“25-30”等等),并将其视为有序分类问题是一种合理的方法。 binning可以是均匀的,也可以是非均匀的,数据驱动的。虽然关于这一点的详细讨论超出了本书的范围,但是我们提请您注意这些问题,因为在这种情况下,目标编码会显著影响性能,我们鼓励您参阅Dougherty等人(1995)及其引用。
许多 NLP 任务实际上使用分类标签,其中模型必须预测一组固定标签中的一个。对此进行编码的一种常见方法是对每个标签使用惟一索引。当输出标签的数量太大时,这种简单的表示可能会出现问题。这方面的一个例子是语言建模问题,在这个问题中,任务是预测下一个单词,给定过去看到的单词。标签空间是一种语言的全部词汇,它可以很容易地增长到几十万,包括特殊字符、名称等等。我们将在后面的章节中重新讨论这个问题以及如何解决这个问题。
## Computational Graphs
一些 NLP 问题涉及从给定文本中预测一个数值。例如,给定一篇英语文章,我们可能需要分配一个数字评分或可读性评分。给定一个餐馆评论片段,我们可能需要预测直到小数点后第一位的星级。给定用户的推文,我们可能需要预测用户的年龄群。有几种方法可以对数字目标进行编码,但是将目标简单地绑定到分类“容器”中(例如,“0-18”、“19-25”、“25-30”等等),并将其视为有序分类问题是一种合理的方法。 绑定可以是均匀的,也可以是非均匀的,数据驱动的。虽然关于这一点的详细讨论超出了本书的范围,但是我们提请您注意这些问题,因为在这种情况下,目标编码会显著影响性能,我们鼓励您参阅 Dougherty 等人(1995)及其引用。
图1-1将监督学习(训练)范式概括为数据流架构,模型(数学表达式)对输入进行转换以获得预测,损失函数(另一个表达式)提供反馈信号来调整模型的参数。利用计算图数据结构可以方便地实现该数据流。从技术上讲,计算图是对数学表达式建模的抽象。在深度学习的上下文中,计算图的实现(如Theano、TensorFlow和PyTorch)进行了额外的记录(bookkeeping),以实现在监督学习范式中训练期间获取参数梯度所需的自动微分。我们将在“PyTorch基础知识”中进一步探讨这一点。推理(或预测)就是简单的表达式求值(计算图上的正向流)。让我们看看计算图如何建模表达式。考虑表达式:y=wx+b
## 计算图
这可以写成两个子表达式z = wx和y = z + b,然后我们可以用一个有向无环图(DAG)表示原始表达式,其中的节点是乘法和加法等数学运算。操作的输入是节点的传入边,操作的输出是传出边。因此,对于表达式y = wx + b,计算图如图1-6所示。在下一节中,我们将看到PyTorch如何让我们以一种直观的方式创建计算图形,以及它如何让我们计算梯度,而无需考虑任何记录(bookkeeping)。
图 1-1 将监督学习(训练)范式概括为数据流架构,模型(数学表达式)对输入进行转换以获得预测,损失函数(另一个表达式)提供反馈信号来调整模型的参数。利用计算图数据结构可以方便地实现该数据流。从技术上讲,计算图是对数学表达式建模的抽象。在深度学习的上下文中,计算图的实现(如 Theano、TensorFlow 和 PyTorch)进行了额外的记录(bookkeeping),以实现在监督学习范式中训练期间获取参数梯度所需的自动微分。我们将在“PyTorch 基础知识”中进一步探讨这一点。推理(或预测)就是简单的表达式求值(计算图上的正向流)。让我们看看计算图如何建模表达式。考虑表达式:y=wx+b
![nlpp_0105](img/03cf959585965d980473450016aad007.jpg "图1-6 用计算图表示y = wx + b。")
这可以写成两个子表达式`z = wx`和`y = z + b`,然后我们可以用一个有向无环图(DAG)表示原始表达式,其中的节点是乘法和加法等数学运算。操作的输入是节点的传入边,操作的输出是传出边。因此,对于表达式`y = wx + b`,计算图如图 1-6 所示。在下一节中,我们将看到 PyTorch 如何让我们以一种直观的方式创建计算图形,以及它如何让我们计算梯度,而无需考虑任何记录(bookkeeping)。
## PyTorch Basics
![nlpp_0105](img/03cf959585965d980473450016aad007.jpg)
在本书中,我们广泛地使用PyTorch来实现我们的深度学习模型。PyTorch是一个开源、社区驱动的深度学习框架。与Theano、Caffe和TensorFlow不同,PyTorch实现了一种“tape-based automatic differentiation”方法,允许我们动态定义和执行计算图形。这对于调试和用最少的努力构建复杂的模型非常有帮助。
## PyTorch 基础
在本书中,我们广泛地使用 PyTorch 来实现我们的深度学习模型。PyTorch 是一个开源、社区驱动的深度学习框架。与 Theano、Caffe 和 TensorFlow 不同,PyTorch 实现了一种“基于磁带的自动微分”方法,允许我们动态定义和执行计算图形。这对于调试和用最少的努力构建复杂的模型非常有帮助。
* * *
动态 VS 静态计算图 像Theano、Caffe和TensorFlow这样的静态框架需要首先声明、编译和执行计算图。虽然这会导致非常高效的实现(在生产和移动设置中非常有用),但在研究和开发过程中可能会变得非常麻烦。像Chainer、DyNet和PyTorch这样的现代框架实现了动态计算图,从而支持更灵活的命令式开发风格,而不需要在每次执行之前编译模型。动态计算图在建模NLP任务时特别有用,每个输入可能导致不同的图结构。
动态 VS 静态计算图 像 Theano、Caffe 和 TensorFlow 这样的静态框架需要首先声明、编译和执行计算图。虽然这会导致非常高效的实现(在生产和移动设置中非常有用),但在研究和开发过程中可能会变得非常麻烦。像 Chainer、DyNet 和 PyTorch 这样的现代框架实现了动态计算图,从而支持更灵活的命令式开发风格,而不需要在每次执行之前编译模型。动态计算图在建模 NLP 任务时特别有用,每个输入可能导致不同的图结构。
* * *
PyTorch是一个优化的张量操作库,它提供了一系列用于深度学习的包。这个库的核心是张量,它是一个包含一些多维数据的数学对象。0阶张量就是一个数字,或者标量。一阶张量(一阶张量)是一个数字数组,或者说是一个向量。类似地,二阶张量是一个向量数组,或者说是一个矩阵。因此,张量可以推广为标量的n维数组,如图1-7所示
PyTorch 是一个优化的张量操作库,它提供了一系列用于深度学习的包。这个库的核心是张量,它是一个包含一些多维数据的数学对象。0 阶张量就是一个数字,或者标量。一阶张量(一阶张量)是一个数字数组,或者说是一个向量。类似地,二阶张量是一个向量数组,或者说是一个矩阵。因此,张量可以推广为标量的`n`维数组,如图 1-7 所示
**图1-7 还未给出**
**图 1-7 还未给出**
在以下部分中,我们将使用PyTorch学习以下内容:
在以下部分中,我们将使用 PyTorch 学习以下内容:

@@ -165,9 +171,9 @@ * 创建张量

* 用张量计算梯度
* 使用带有gpu的CUDA张量
* 使用带有 gpu 的 CUDA 张量
在本节的其余部分中,我们将首先使用PyTorch来熟悉各种PyTorch操作。我们建议您现在已经安装了PyTorch并准备好了Python 3.5+笔记本,并按照本节中的示例进行操作。我们还建议您完成本节后面的练习。
在本节的其余部分中,我们将首先使用 PyTorch 来熟悉各种 PyTorch 操作。我们建议您现在已经安装了 PyTorch 并准备好了 Python 3.5+ 笔记本,并按照本节中的示例进行操作。我们还建议您完成本节后面的练习。
### Installing PyTorch
### 安装 PyTorch
第一步是通过在pytorch.org上选择您的系统首选项在您的机器上安装PyTorch。选择您的操作系统,然后选择包管理器(我们推荐conda/pip),然后选择您正在使用的Python版本(我们推荐3.5+)。这将生成命令,以便您执行安装PyTorch。在撰写本文时,conda环境的安装命令如下:
第一步是通过在 pytorch.org 上选择您的系统首选项在您的机器上安装 PyTorch。选择您的操作系统,然后选择包管理器(我们推荐`conda/pip`),然后选择您正在使用的 Python 版本(我们推荐 3.5+)。这将生成命令,以便您执行安装 PyTorch。在撰写本文时,conda 环境的安装命令如下:

@@ -181,9 +187,9 @@ ```py

NOTE 如果您有一个支持CUDA的图形处理器单元(GPU),您还应该选择合适的CUDA版本。要了解更多细节,请参考pytorch.org上的安装说明。
注意:如果您有一个支持 CUDA 的图形处理器单元(GPU),您还应该选择合适的 CUDA 版本。要了解更多细节,请参考 pytorch.org 上的安装说明。
* * *
### Creating Tensors
### 创建张量
首先,我们定义一个辅助函数,描述(x),它总结了张量x的各种性质,例如张量的类型、张量的维数和张量的内容:
首先,我们定义一个辅助函数,描述(`x`),它总结了张量`x`的各种性质,例如张量的类型、张量的维数和张量的内容:

@@ -199,5 +205,5 @@ ```py

PyTorch允许我们使用torch包以许多不同的方式创建张量。创建张量的一种方法是通过指定一个随机张量的维数来初始化它,如例1-3所示。
PyTorch 允许我们使用`torch`包以许多不同的方式创建张量。创建张量的一种方法是通过指定一个随机张量的维数来初始化它,如例 1-3 所示。
Example 1-3\. Creating a tensor in PyTorch with torch.Tensor
示例 1-3:在 PyTorch 中使用`torch.Tensor`创建张量

@@ -217,4 +223,6 @@ ```py

我们还可以创建一个张量通过随机初始化值区间上的均匀分布(0,1)或标准正态分布(从均匀分布随机初始化张量,说,是很重要的,正如您将看到的在第三章和第四章),见示例1-4。 Example 1-4\. Creating a randomly initialized tensor
我们还可以创建一个张量通过随机初始化值区间上的均匀分布(`0,1`)或标准正态分布(从均匀分布随机初始化张量,说,是很重要的,正如您将看到的在第三章和第四章),见示例 1-4。
示例 1-4:创建随机初始化的张量
```py

@@ -240,5 +248,5 @@ Input[0]:

我们还可以创建张量,所有张量都用相同的标量填充。对于创建0或1张量,我们有内置函数,对于填充特定值,我们可以使用fill_()方法。任何带有下划线( _ )的PyTorch方法都是指就地(in place)操作;也就是说,它在不创建新对象的情况下就地修改内容,如示例1-5所示。
我们还可以创建张量,所有张量都用相同的标量填充。对于创建 0 或 1 张量,我们有内置函数,对于填充特定值,我们可以使用`fill_()`方法。任何带有下划线(`_`)的 PyTorch 方法都是指就地(in place)操作;也就是说,它在不创建新对象的情况下就地修改内容,如示例 1-5 所示。
Example 1-5\. Creating a filled tensor
示例 1-5:创建填充的张量

@@ -274,4 +282,6 @@ ```py

示例1-6演示了如何通过使用Python列表以声明的方式创建张量。 Example 1-6\. Creating and initializing a tensor from lists
示例 1-6 演示了如何通过使用 Python 列表以声明的方式创建张量。
示例 1-6:从列表创建和初始化张量
```py

@@ -291,5 +301,5 @@ Input[0]:

值可以来自列表(如前面的示例),也可以来自NumPy数组。当然,我们也可以从PyTorch张量变换到NumPy数组。注意,这个张量的类型是一个double张量,而不是默认的FloatTensor。这对应于NumPy随机矩阵的数据类型float64,如示例1-7所示。
值可以来自列表(如前面的示例),也可以来自 NumPy 数组。当然,我们也可以从 PyTorch 张量变换到 NumPy 数组。注意,这个张量的类型是一个`double`张量,而不是默认的`FloatTensor`。这对应于 NumPy 随机矩阵的数据类型`float64`,如示例 1-7 所示。
Example 1-7\. Creating and initializing a tensor from NumPy
示例 1-7:从 NumPy 创建和初始化张量

@@ -311,9 +321,9 @@ ```py

在处理使用Numpy格式数值的遗留库(legacy libraries)时,在NumPy和PyTorch张量之间切换的能力变得非常重要。
在处理使用 Numpy 格式数值的遗留库(legacy libraries)时,在 NumPy 和 PyTorch 张量之间切换的能力变得非常重要。
### Tensor Types and Size
### 张量类型和大小
每个张量都有一个相关的类型和大小。使用torch时的默认张量类型。张量构造函数是torch.FloatTensor。但是,可以在初始化时指定张量,也可以在以后使用类型转换方法将张量转换为另一种类型(float、long、double等)。有两种方法可以指定初始化类型,一种是直接调用特定张量类型(如FloatTensor和LongTensor)的构造函数,另一种是使用特殊的方法torch。张量,并提供dtype,如例1-8所示。
每个张量都有一个相关的类型和大小。使用`torch`时的默认张量类型。张量构造函数是`torch.FloatTensor`。但是,可以在初始化时指定张量,也可以在以后使用类型转换方法将张量转换为另一种类型(`float`、`long`、`double`等)。有两种方法可以指定初始化类型,一种是直接调用特定张量类型(如`FloatTensor`和`LongTensor`)的构造函数,另一种是使用特殊的方法`torch.tensor`,并提供`dtype`,如例 1-8 所示。
Example 1-8\. Tensor properties
示例 1-8:张量属性

@@ -362,9 +372,9 @@ ```py

我们利用张量对象的形状特性和尺寸方法来获取其尺寸的测量值。访问这些度量的两种方法基本上是相同的。在调试PyTorch代码时,检查张量的形状成为必不可少的工具。
我们利用张量对象的形状特性和尺寸方法来获取其尺寸的测量值。访问这些度量的两种方法基本上是相同的。在调试 PyTorch 代码时,检查张量的形状成为必不可少的工具。
### Tensor Operations
### 张量操作
在创建了张量之后,可以像处理传统编程语言类型(如“+”、“-”、“* ”和“/”)那样对它们进行操作。除了操作符,我们还可以使用.add()之类的函数,如示例1-9所示,这些函数对应于符号操作符。
在创建了张量之后,可以像处理传统编程语言类型(如`+`、`-`、`*`和`/`)那样对它们进行操作。除了操作符,我们还可以使用`.add()`之类的函数,如示例 1-9 所示,这些函数对应于符号操作符。
Example 1-9\. Tensor operations: addition
示例 1-9:张量操作:加法

@@ -401,4 +411,6 @@ ```py

还有一些运算可以应用到张量的特定维数上。正如您可能已经注意到的,对于2D张量,我们将行表示为维度0,列表示为维度1,如示例1-10所示。 Example 1-10\. Dimension-based tensor operations
还有一些运算可以应用到张量的特定维数上。正如您可能已经注意到的,对于 2D 张量,我们将行表示为维度 0,列表示为维度 1,如示例 1-10 所示。
示例 1-10:基于维度的张量操作
```py

@@ -449,9 +461,9 @@ Input[0]:

通常,我们需要执行更复杂的操作,包括索引、切片、连接和突变(indexing,slicing,joining and mutation)的组合。与NumPy和其他数字库一样,PyTorch也有内置函数,可以使此类张量操作非常简单。
通常,我们需要执行更复杂的操作,包括索引、切片、连接和突变(indexing,slicing,joining and mutation)的组合。与 NumPy 和其他数字库一样,PyTorch 也有内置函数,可以使此类张量操作非常简单。
### Indexing, slicing, and joining
### 索引,切片和连接
如果您是一个NumPy用户,那么您可能非常熟悉示例1-11中所示的PyTorch的索引和切片方案。
如果您是一个 NumPy 用户,那么您可能非常熟悉示例 1-11 中所示的 PyTorch 的索引和切片方案。
Example 1-11\. Slicing and indexing a tensor
示例 1-11:切片和索引张量

@@ -486,5 +498,5 @@ ```py

示例1-12演示了PyTorch还具有用于复杂索引和切片操作的函数,您可能对有效地访问张量的非连续位置感兴趣。
示例 1-12 演示了 PyTorch 还具有用于复杂索引和切片操作的函数,您可能对有效地访问张量的非连续位置感兴趣。
Example 1-12\. Complex indexing: noncontiguous indexing of a tensor
示例 1-12:复杂索引:张量的非连续索引

@@ -522,5 +534,5 @@ ```py

注意指标(indices)是一个长张量;这是使用PyTorch函数进行索引的要求。我们还可以使用内置的连接函数连接张量,如示例1-13所示,通过指定张量和维度。
注意索引(indices)是一个长张量;这是使用 PyTorch 函数进行索引的要求。我们还可以使用内置的连接函数连接张量,如示例 1-13 所示,通过指定张量和维度。
Example 1-13\. Concatenating tensors
示例 1-13:连接张量

@@ -570,5 +582,5 @@ ```py

PyTorch还在张量上实现了高效的线性代数操作,如乘法、逆和trace,如示例1-14所示。
PyTorch 还在张量上实现了高效的线性代数操作,如乘法、逆和迹,如示例 1-14 所示。
Example 1-14\. Linear algebra on tensors: multiplication
示例 1-14:张量上的线性代数:乘法

@@ -608,9 +620,9 @@ ```py

到目前为止,我们已经研究了创建和操作恒定PyTorch张量对象的方法。就像编程语言(如Python)变量封装一块数据,关于数据的额外信息(如内存地址存储,例如),PyTorch张量处理构建计算图时所需的记账(bookkeeping)所需构建计算图对机器学习只是在实例化时通过启用一个布尔标志。
到目前为止,我们已经研究了创建和操作恒定 PyTorch 张量对象的方法。就像编程语言(如 Python)变量封装一块数据,关于数据的额外信息(如内存地址存储,例如),PyTorch 张量处理构建计算图时所需的记账(bookkeeping)所需构建计算图对机器学习只是在实例化时通过启用一个布尔标志。
### Tensors and Computational Graphs
### 张量和计算图
PyTorch张量类封装了数据(张量本身)和一系列操作,如代数操作、索引操作和整形操作。然而,1-15所示的例子,当requires_grad布尔标志被设置为True的张量,记账操作启用,可以追踪的梯度张量以及梯度函数,这两个需要基于促进梯度学习讨论“监督学习范式”。
PyTorch 张量类封装了数据(张量本身)和一系列操作,如代数操作、索引操作和整形操作。然而,1-15 所示的例子,当`requires_grad`布尔标志被设置为`True`的张量,记账操作启用,可以追踪的梯度张量以及梯度函数,这两个需要基于促进梯度学习讨论“监督学习范式”。
Example 1-15\. Creating tensors for gradient bookkeeping
示例 1-15:为梯度记录创建张量

@@ -655,13 +667,13 @@ ```py

当您使用requires_grad=True创建张量时,您需要PyTorch来管理计算梯度的bookkeeping信息。首先,PyTorch将跟踪向前传递的值。然后,在计算结束时,使用单个标量来计算向后传递。反向传递是通过对一个张量使用backward()方法来初始化的,这个张量是由一个损失函数的求值得到的。向后传递为参与向前传递的张量对象计算梯度值。
当您使用`requires_grad=True`创建张量时,您需要 PyTorch 来管理计算梯度的 bookkeeping 信息。首先,PyTorch 将跟踪向前传递的值。然后,在计算结束时,使用单个标量来计算向后传递。反向传递是通过对一个张量使用`backward()`方法来初始化的,这个张量是由一个损失函数的求值得到的。向后传递为参与向前传递的张量对象计算梯度值。
一般来说,梯度是一个值,它表示函数输出相对于函数输入的斜率。在计算图形设置中,模型中的每个参数都存在梯度,可以认为是该参数对误差信号的贡献。在PyTorch中,可以使用.grad成员变量访问计算图中节点的梯度。优化器使用.grad变量更新参数的值。
一般来说,梯度是一个值,它表示函数输出相对于函数输入的斜率。在计算图形设置中,模型中的每个参数都存在梯度,可以认为是该参数对误差信号的贡献。在 PyTorch 中,可以使用`.grad`成员变量访问计算图中节点的梯度。优化器使用`.grad`变量更新参数的值。
到目前为止,我们一直在CPU内存上分配张量。在做线性代数运算时,如果你有一个GPU,那么利用它可能是有意义的。要利用GPU,首先需要分配GPU内存上的张量。对gpu的访问是通过一个名为CUDA的专门API进行的。CUDA API是由NVIDIA创建的,并且仅限于在NVIDIA gpu上使用。PyTorch提供的CUDA张量对象在使用中与常规cpu绑定张量没有区别,除了内部分配的方式不同。
到目前为止,我们一直在 CPU 内存上分配张量。在做线性代数运算时,如果你有一个 GPU,那么利用它可能是有意义的。要利用 GPU,首先需要分配 GPU 内存上的张量。对 gpu 的访问是通过一个名为 CUDA 的专门 API 进行的。CUDA API 是由 NVIDIA 创建的,并且仅限于在 NVIDIA gpu 上使用。PyTorch 提供的 CUDA 张量对象在使用中与常规 cpu 绑定张量没有区别,除了内部分配的方式不同。
### CUDA Tensors
### CUDA 张量
PyTorch使创建这些CUDA张量变得非常容易(示例1-16),它将张量从CPU传输到GPU,同时维护其底层类型。PyTorch中的首选方法是与设备无关,并编写在GPU或CPU上都能工作的代码。在下面的代码片段中,我们首先使用torch.cuda.is_available()检查GPU是否可用,然后使用torch.device检索设备名。然后,将实例化所有未来的张量,并使用.to(device)方法将其移动到目标设备。
PyTorch 使创建这些 CUDA 张量变得非常容易(示例 1-16),它将张量从 CPU 传输到 GPU,同时维护其底层类型。PyTorch 中的首选方法是与设备无关,并编写在 GPU 或 CPU 上都能工作的代码。在下面的代码片段中,我们首先使用`torch.cuda.is_available()`检查 GPU 是否可用,然后使用`torch.device`检索设备名。然后,将实例化所有未来的张量,并使用`.to(device)`方法将其移动到目标设备。
Example 1-16\. Creating CUDA tensors
示例 1-16:创建 CUDA 张量

@@ -693,5 +705,5 @@ ```py

要对CUDA和非CUDA对象进行操作,我们需要确保它们在同一设备上。如果我们不这样做,计算就会中断,如下面的代码片段所示。例如,在计算不属于计算图的监视指标时,就会出现这种情况。当操作两个张量对象时,确保它们在同一个设备上。例子1-17所示。
要对 CUDA 和非 CUDA 对象进行操作,我们需要确保它们在同一设备上。如果我们不这样做,计算就会中断,如下面的代码片段所示。例如,在计算不属于计算图的监视指标时,就会出现这种情况。当操作两个张量对象时,确保它们在同一个设备上。例子 1-17 所示。
Example 1-17\. Mixing CUDA tensors with CPU bound tensors
示例 1-17:混合 CUDA 张量和 CPU 绑定的张量

@@ -721,3 +733,3 @@ ```py

请记住,将数据从GPU来回移动是非常昂贵的。因此,典型的过程包括在GPU上执行许多并行计算,然后将最终结果传输回CPU。这将允许您充分利用gpu。如果您有几个CUDA-visible设备(即,最佳实践是在执行程序时使用CUDA_VISIBLE_DEVICES环境变量,如下图所示:
请记住,将数据从 GPU 来回移动是非常昂贵的。因此,典型的过程包括在 GPU 上执行许多并行计算,然后将最终结果传输回 CPU。这将允许您充分利用 gpu。如果您有几个 CUDA 可见的设备(即,最佳实践是在执行程序时使用`CUDA_VISIBLE_DEVICES`环境变量,如下图所示:

@@ -729,5 +741,5 @@ ```py

在本书中我们不涉及并行性和多gpu训练,但是它们在缩放实验中是必不可少的,有时甚至在训练大型模型时也是如此。我们建议您参考PyTorch文档和讨论论坛,以获得关于这个主题的更多帮助和支持。
在本书中我们不涉及并行性和多 gpu 训练,但是它们在缩放实验中是必不可少的,有时甚至在训练大型模型时也是如此。我们建议您参考 PyTorch 文档和讨论论坛,以获得关于这个主题的更多帮助和支持。
# Exercises:
# 练习

@@ -766,16 +778,16 @@ 掌握一个主题的最好方法是解决问题。这里有一些热身运动。许多问题将涉及到查阅官方文件[1]和寻找有用的功能。 .

# Summary
# 总结
在这一章中,我们介绍了本书的目标——自然语言处理(NLP)和深度学习——并对监督学习范式进行了详细的理解。在本章的最后,您现在应该熟悉或至少了解各种术语,例如观察、目标、模型、参数、预测、损失函数、表示、学习/培训和推理。您还了解了如何使用单热编码对学习任务的输入(观察和目标)进行编码。我们还研究了基于计数的表示,如TF和TF- idf。我们首先了解了什么是计算图,静态和动态计算图,以及PyTorch张量操纵操作。在第二章中,我们对传统的NLP进行了概述。第二章,这一章应该为你奠定必要的基础,如果你对这本书的主题是新的,并为你的书的其余部分做准备。
在这一章中,我们介绍了本书的目标——自然语言处理(NLP)和深度学习——并对监督学习范式进行了详细的理解。在本章的最后,您现在应该熟悉或至少了解各种术语,例如观察、目标、模型、参数、预测、损失函数、表示、学习/训练和推理。您还了解了如何使用单热编码对学习任务的输入(观察和目标)进行编码。我们还研究了基于计数的表示,如 TF 和 TF-IDF。我们首先了解了什么是计算图,静态和动态计算图,以及 PyTorch 张量操纵操作。在第二章中,我们对传统的 NLP 进行了概述。第二章,这一章应该为你奠定必要的基础,如果你对这本书的主题是新的,并为你的书的其余部分做准备。
重点是TF-IDF
重点是 TF-IDF
词频(TF)=某个词在文章中出现的次数 / 文章中的总词数
`词频(TF)= 某个词在文章中出现的次数 / 文章中的总词数`
逆文档频率(IDF)=log(语料库的文档总数 / (包含该词的文档数+1))
`逆文档频率(IDF)= log(语料库的文档总数 / (包含该词的文档数 + 1))`
TF应该很容易理解就是计算词频,IDF衡量词的常见程度.为了计算IDF我们需要事先准备一个语料库用来模拟语言的使用环境,如果一个词越是常见,那么式子中分母越大,逆文档频率越接近0.这里分母+1是为了避免分母为0的情况出现
TF 应该很容易理解就是计算词频,IDF 衡量词的常见程度.为了计算 IDF 我们需要事先准备一个语料库用来模拟语言的使用环境,如果一个词越是常见,那么式子中分母越大,逆文档频率越接近 0.这里分母`+1`是为了避免分母为 0 的情况出现
TF-IDF=词频(TF)×逆文档频率(IDF)
`TF-IDF = 词频(TF)× 逆文档频率(IDF)`
TF-IDF可以很好的实现提取文章中关键词的目的.
TF-IDF 可以很好的实现提取文章中关键词的目的.

@@ -1,40 +0,48 @@

# Chapter 2.传统NLP快速回顾
# 二、传统 NLP 快速回顾
> 本文标题:[Natural-Language-Processing-with-PyTorch(二)](https://yifdu.github.io/2018/12/18/Natural-Language-Processing-with-PyTorch%EF%BC%88%E4%BA%8C%EF%BC%89/)
> 本文标题:[Natural-Language-Processing-with-PyTorch(二)](https://yifdu.github.io/2018/12/18/Natural-Language-Processing-with-PyTorch%EF%BC%88%E4%BA%8C%EF%BC%89/)
>
> 文章作者:[Yif Du](https://yifdu.github.io/ "访问 Yif Du 的个人博客")
> 文章作者:[Yif Du](https://yifdu.github.io/ "访问 Yif Du 的个人博客")
>
> 发布时间:2018年12月18日 - 13:12
> 发布时间:2018 年 12 月 18 日 - 13:12
>
> 最后更新:2019年02月16日 - 23:02
> 最后更新:2019 年 02 月 16 日 - 23:02
>
> 原始链接:[http://yifdu.github.io/2018/12/18/Natural-Language-Processing-with-PyTorch(二)/](https://yifdu.github.io/2018/12/18/Natural-Language-Processing-with-PyTorch%EF%BC%88%E4%BA%8C%EF%BC%89/)
> 原始链接:[http://yifdu.github.io/2018/12/18/Natural-Language-Processing-with-PyTorch(二)/](https://yifdu.github.io/2018/12/18/Natural-Language-Processing-with-PyTorch%EF%BC%88%E4%BA%8C%EF%BC%89/)
>
> 许可协议: [署名-非商业性使用-禁止演绎 4.0 国际](https://creativecommons.org/licenses/by-nc-nd/4.0/) 转载请保留原文链接及作者。
> 许可协议:[署名-非商业性使用-禁止演绎 4.0 国际](https://creativecommons.org/licenses/by-nc-nd/4.0/) 转载请保留原文链接及作者。
自然语言处理(NLP)和计算语言学(CL)是人类语言计算研究的两个领域。NLP旨在开发解决涉及语言的实际问题的方法,如信息提取、自动语音识别、机器翻译、情绪分析、问答和总结。另一方面,CL使用计算方法来理解人类语言的特性。我们如何理解语言?我们如何产生语言?我们如何学习语言?语言之间有什么关系?
自然语言处理(NLP)和计算语言学(CL)是人类语言计算研究的两个领域。NLP 旨在开发解决涉及语言的实际问题的方法,如信息提取、自动语音识别、机器翻译、情绪分析、问答和总结。另一方面,CL 使用计算方法来理解人类语言的特性。我们如何理解语言?我们如何产生语言?我们如何学习语言?语言之间有什么关系?
在文献中,我们经常看到方法和研究人员的交叉,从CL到NLP,反之亦然。来自语言学习的课程内容可以用来告知NLP中的先验,统计和机器学习的方法可以用来回答CL想要回答的问题。事实上,这些问题中的一些已经扩展到它们自己的学科,如音位学、形态学、句法学、语义学和语用学。
在文献中,我们经常看到方法和研究人员的交叉,从 CL 到 NLP,反之亦然。来自语言学习的课程内容可以用来告知 NLP 中的先验,统计和机器学习的方法可以用来回答 CL 想要回答的问题。事实上,这些问题中的一些已经扩展到它们自己的学科,如音位学、形态学、句法学、语义学和语用学。
在本书中,我们只关注NLP,但是我们经常根据需要从CL中借鉴思想。在我们将自己完全归属于NLP的神经网络方法之前,有必要回顾一下一些传统的NLP概念和方法。这就是本章的目标。
在本书中,我们只关注 NLP,但是我们经常根据需要从 CL 中借鉴思想。在我们将自己完全归属于 NLP 的神经网络方法之前,有必要回顾一下一些传统的 NLP 概念和方法。这就是本章的目标。
如果你有一些NLP的背景,你可以跳过这一章,但你也可以留下来怀旧,为未来建立一个共享的词汇表。
如果你有一些 NLP 的背景,你可以跳过这一章,但你也可以留下来怀旧,为未来建立一个共享的词汇表。
## Corpora, Tokens, and Types
## 语料库,标记和类型
所有的NLP方法,无论是经典的还是现代的,都以文本数据集开始,也称为语料库(复数:corpora)。语料库通常有原始文本(ASCII或UTF-8格式)和与文本相关的任何元数据。原始文本是字符(字节)序列,但是大多数时候将字符分组成连续的称为令牌(Tokens)的连续单元是有用的。在英语中,令牌(Tokens)对应由空格字符或标点分隔的单词和数字序列。
所有的 NLP 方法,无论是经典的还是现代的,都以文本数据集开始,也称为语料库(复数:corpora)。语料库通常有原始文本(ASCII 或 UTF-8 格式)和与文本相关的任何元数据。原始文本是字符(字节)序列,但是大多数时候将字符分组成连续的称为标记(Tokens)的连续单元是有用的。在英语中,标记(Tokens)对应由空格字符或标点分隔的单词和数字序列。
元数据可以是与文本相关联的任何辅助信息,例如标识符,标签和时间戳。 在机器学习术语中,文本及其元数据称为实例或数据点。 语料库(图2-1)是一组实例,也称为数据集。 鉴于本书重点关注机器学习,我们可以自由地交换术语语料库和数据集。
元数据可以是与文本相关联的任何辅助信息,例如标识符,标签和时间戳。 在机器学习术语中,文本及其元数据称为实例或数据点。 语料库(图 2-1)是一组实例,也称为数据集。 鉴于本书重点关注机器学习,我们可以自由地交换术语语料库和数据集。
![metadata_1](img/c35eca55ae92dbc6325e2ba9ac2b7710.jpg "图2-1\. 语料库:NLP任务的起点。")
![metadata_1](img/c35eca55ae92dbc6325e2ba9ac2b7710.jpg)
将文本分解为令牌(Tokens)的过程称为令牌化(tokenization)。世界语的句子,”Maria frapis la verda sorĉistino,“1有六个令牌。令牌化可能比简单地基于非字母数字字符拆分文本更加复杂,如图2-2所示。对于像土耳其语这样的粘合语言来说,分隔空格和标点符号可能是不够的,因此可能需要更专业的技术。正如您将在第4章和第6章中看到的,通过将文本表示为字节流,我们可能能够在某些神经网络模型中完全规避令牌化问题;这对于粘合语言来说变得非常重要。 ![Tokenization](img/2a941f4b722f7fb4031402398e5c0882.jpg "图2-2\. 像土耳其语这样的语言的标记化很快就会变得复杂起来。") 最后,看看下面这条推文: ![Tweet](img/3529b4f55a52c83aaf242bd51bf5ca3a.jpg "图2-3") 标记化tweets涉及到保存话题标签和@handle,将smilies(如:-)和urls分割为一个单元。#MakeAMovieCold标签应该是1个令牌还是4个?虽然大多数研究论文对这一问题并没有给予太多的关注,而且事实上,许多令牌化决策往往是任意的,但是这些决策在实践中对准确性的影响要比公认的要大得多。通常被认为是预处理的繁琐工作,大多数开放源码NLP包为令牌化提供了合理的支持。示例2-1展示了来自NLTK和SpaCy的示例,这是两个用于文本处理的常用包。
将文本分解为标记(Tokens)的过程称为分词(tokenization)。世界语的句子,`Maria frapis la verda sorĉistino`有六个标记。分词可能比简单地基于非字母数字字符拆分文本更加复杂,如图 2-2 所示。对于像土耳其语这样的粘合语言来说,分隔空格和标点符号可能是不够的,因此可能需要更专业的技术。正如您将在第 4 章和第 6 章中看到的,通过将文本表示为字节流,我们可能能够在某些神经网络模型中完全规避分词问题;这对于粘合语言来说变得非常重要。
Example 2-1\. Tokenizing text
![Tokenization](img/2a941f4b722f7fb4031402398e5c0882.jpg)
最后,看看下面这条推文:
![Tweet](img/3529b4f55a52c83aaf242bd51bf5ca3a.jpg)
分词 tweets 涉及到保存话题标签和`@handle`,将表情符号(如`:-)`)和 urls 分割为一个单元。`#MakeAMovieCold`标签应该是 1 个标记还是 4 个?虽然大多数研究论文对这一问题并没有给予太多的关注,而且事实上,许多分词决策往往是任意的,但是这些决策在实践中对准确性的影响要比公认的要大得多。通常被认为是预处理的繁琐工作,大多数开放源码 NLP 包为分词提供了合理的支持。示例 2-1 展示了来自 NLTK 和 SpaCy 的示例,这是两个用于文本处理的常用包。
示例 2-1:对文本分词
```py
Input[0]:
import spacy
nlp = spacy.load(‘en’)
text = “Mary, don’t slap the green witch”
nlp = spacy.load('en')
text = "Mary, don't slap the green witch"
print([str(token) for token in nlp(text.lower())])

@@ -54,15 +62,15 @@ Output[0]:

类型是语料库中唯一的令牌。语料库中所有类型的集合就是它的词汇表或词典。词可以区分为内容词和停止词。像冠词和介词这样的限定词主要是为了达到语法目的,就像填充物承载着内容词一样。
类型是语料库中唯一的标记。语料库中所有类型的集合就是它的词汇表或词典。词可以区分为内容词和停止词。像冠词和介词这样的限定词主要是为了达到语法目的,就像填充物承载着内容词一样。
* * *
ASIDE 这种理解语言的语言学并将其应用于解决自然语言处理问题的过程称为特征工程。为了模型在不同语言之间的方便和可移植性,我们将这一点保持在最低限度。但是在构建和部署真实的生产系统时,特性工程是必不可少的,尽管最近的说法与此相反。对于feature engineering的介绍,一般来说,可以阅读Zheng(2016)的书。
注:这种理解语言的语言学并将其应用于解决自然语言处理问题的过程称为特征工程。为了模型在不同语言之间的方便和可移植性,我们将这一点保持在最低限度。但是在构建和部署真实的生产系统时,特性工程是必不可少的,尽管最近的说法与此相反。对于特征工程的介绍,一般来说,可以阅读 Zheng(2016)的书。
* * *
## Unigrams, Bigrams, Trigrams, …, Ngrams
## 一元组,二元组,三元组,...,N 元组
ngram是文本中出现的固定长度(n)的连续令牌序列。bigram有两个令牌,unigram 只有一个令牌。从文本生成ngram非常简单,如示例2-2所示,但是SpaCy和NLTK等包提供了方便的方法。
N 元组是文本中出现的固定长度(`n`)的连续标记序列。二元组有两个标记,一元组只有一个标记。从文本生成 N 元组非常简单,如示例 2-2 所示,但是 SpaCy 和 NLTK 等包提供了方便的方法。
Example 2-2\. Generating ngrams
示例 2-2:生成 N 元组

@@ -88,9 +96,9 @@ ```py

对于子词(subword)信息本身携带有用信息的某些情况,可能需要生成字符ngram。例如,“methanol”中的后缀“-ol”表示它是一种醇;如果您的任务涉及到对有机化合物名称进行分类,那么您可以看到ngram捕获的子单词(subword)信息是如何有用的。在这种情况下,您可以重用相同的代码,除了将每个字符ngram视为令牌。(这里的subword应该是值类似前缀后缀这种完整单词中的一部分)
对于子词(subword)信息本身携带有用信息的某些情况,可能需要生成字符 N 元组。例如,`methanol`中的后缀`-ol`表示它是一种醇;如果您的任务涉及到对有机化合物名称进行分类,那么您可以看到 N 元组捕获的子单词(subword)信息是如何有用的。在这种情况下,您可以重用相同的代码,除了将每个字符 N 元组视为标记。(这里的子单词应该是值类似前缀后缀这种完整单词中的一部分)
## Lemmas and Stems
## 词形和词干
Lemmas是单词的词根形式。考虑动词fly。它可以被屈折成许多不同的单词——flow、fly、flies、flying、flow等等——而fly是所有这些看似不同的单词的Lemmas。有时,为了保持向量表示的维数较低,将令牌减少到它们的Lemmas可能是有用的。这种简化称为lemmatization,您可以在示例2-3中看到它的作用。
词形是单词的词根形式。考虑动词`fly`。它可以被屈折成许多不同的单词——`flow`、`fly`、`flies`、`flying`、`flow`等等——而`fly`是所有这些看似不同的单词的词形。有时,为了保持向量表示的维数较低,将标记减少到它们的词形可能是有用的。这种简化称为词形还原(lemmatization),您可以在示例 2-3 中看到它的作用。
Example 2-3\. Lemmatization
示例 2-3:词形还原

@@ -100,3 +108,3 @@ ```py

import spacy
nlp = spacy.load(‘en’)
nlp = spacy.load('en')
doc = nlp(u"he was running late")

@@ -113,18 +121,20 @@ for token in doc:

例如,SpaCy使用一个预定义的字典WordNet来提取Lemmas,但是lemmatization可以构建为一个机器学习问题,需要理解语言的形态学。
例如,SpaCy 使用一个预定义的字典 WordNet 来提取词形,但是词形还原可以构建为一个机器学习问题,需要理解语言的形态学。
词干是最普通的lemmatization。它涉及到使用手工制定的规则来去掉单词的结尾,从而将它们简化为一种叫做词干的常见形式。通常在开源包中实现的流行的词干分析器是Porter的Stemmer和Snowball Stemmer。我们留给您去寻找合适的SpaCy/NLTK api来执行词干提取。
词干是最普通的词形还原。它涉及到使用手工制定的规则来去掉单词的结尾,从而将它们简化为一种叫做词干的常见形式。通常在开源包中实现的流行的词干分析器是 Porter 词干提取器和 Snowball 词干提取器。我们留给您去寻找合适的 SpaCy/NLTK api 来执行词干提取。
## Categorizing Sentences and Documents
## 分类句子和文档
对文档进行归类或分类可能是NLP最早的应用之一。我们在第1章中描述的表示(Term-Frequency [TF]和Term-Frequency- invere - document - frequency [TF- idf])对于对较长的文本块(如文档或句子)进行分类和分类非常有用。主题标签的分配、评论情绪的预测、垃圾邮件的过滤、语言识别和邮件分类等问题可以被定义为受监督的文档分类问题。(半监督版本,其中只使用了一个小的标记数据集,非常有用,但超出了本书的范围。)
对文档进行归类或分类可能是 NLP 最早的应用之一。我们在第 1 章中描述的表示(词频(TF)和词频-逆文档频率(TF-idf))对于对较长的文本块(如文档或句子)进行分类和分类非常有用。主题标签的分配、评论情绪的预测、垃圾邮件的过滤、语言识别和邮件分类等问题可以被定义为受监督的文档分类问题。(半监督版本,其中只使用了一个小的标记数据集,非常有用,但超出了本书的范围。)
## Categorizing Words: POS Tagging
## 分类单词:词性标注
我们可以将标记的概念从文档扩展到单个单词或标记。分类单词的一个常见示例是词性标注,如示例2-4所示。 Example 2-4\. Parts of speech
我们可以将标记的概念从文档扩展到单个单词或标记。分类单词的一个常见示例是词性标注,如示例 2-4 所示。
示例 2-4:词性
```py
Input[0]
import spacy
nlp = spacy.load(‘en’)
nlp = spacy.load('en')
doc = nlp(u"Mary slapped the green witch.")

@@ -143,12 +153,16 @@ for token in doc:

## Categorizing Spans: Chunking and Named Entity Recognition
## 分类短语:分块和命名实体识别
通常,我们需要标记文本的范围;即,一个连续的多令牌边界。例如,“Mary slapped the green witch.”我们可能需要识别其中的名词短语(NP)和动词短语(VP),如下图所示: ![Sentence_1](img/77ada2c4a9a2bcb1ad37475522db4edc.jpg "图2-4") 这称为分块(Chunking)或浅解析(Shallow parsing)。浅解析的目的是推导出由名词、动词、形容词等语法原子组成的高阶单位。如果没有训练浅解析模型的数据,可以在词性标记上编写正则表达式来近似浅解析。幸运的是,对于英语和最广泛使用的语言来说,这样的数据和预先训练的模型是存在的。示例2-5给出了一个使用SpaCy的浅解析示例。
通常,我们需要标记文本的范围;即,一个连续的多标记边界。例如,`Mary slapped the green witch.`我们可能需要识别其中的名词短语(NP)和动词短语(VP),如下图所示:
Example 2-5\. Noun chunks
![Sentence_1](img/77ada2c4a9a2bcb1ad37475522db4edc.jpg)
这称为分块(Chunking)或浅解析(Shallow parsing)。浅解析的目的是推导出由名词、动词、形容词等语法原子组成的高阶单位。如果没有训练浅解析模型的数据,可以在词性标记上编写正则表达式来近似浅解析。幸运的是,对于英语和最广泛使用的语言来说,这样的数据和预先训练的模型是存在的。示例 2-5 给出了一个使用 SpaCy 的浅解析示例。
示例 2-5:名词块
```py
Input[0]:
import spacy
nlp = spacy.load(‘en’)
nlp = spacy.load('en')
doc = nlp(u"Mary slapped the green witch.")

@@ -163,32 +177,42 @@ for chunk in doc.noun_chunks:

另一种有用的span类型是命名实体。命名实体是一个字符串,它提到了一个真实世界的概念,如人员、位置、组织、药品名称等等。这里有一个例子: ![Nameentity](img/f01ca7a7f18a64f876482e91e9fcb5e0.jpg "图2-5")
另一种有用的短语类型是命名实体。命名实体是一个字符串,它提到了一个真实世界的概念,如人员、位置、组织、药品名称等等。这里有一个例子:
## Structure of Sentences
![Nameentity](img/f01ca7a7f18a64f876482e91e9fcb5e0.jpg)
浅层解析识别短语单位,而识别它们之间关系的任务称为解析(parsing)。您可能还记得,在初级英语课上,用图表表示句子,如图2-6所示。 ![Parase_1](img/2f1511a2b2c9bf499667cb6649dbda15.jpg "图2-6\. “Mary slapped the green witch”这句话的成分解析。")
## 句子结构
解析树(Parse tree)表示句子中不同的语法单元在层次上是如何相关的。图2-6中的解析树显示了所谓的成分解析。另一种可能更有用的显示关系的方法是使用依赖项解析(dependency parsing),如图2-7所示。 ![Parase_2](img/b4d0b914345a17162773e581f0ee08f9.jpg "图2-7\. “Mary slapped the green witch.”这句话的依赖性解析") 要了解更多关于传统解析的信息,请参阅本章末尾的参考资料部分。
浅层解析识别短语单位,而识别它们之间关系的任务称为解析(parsing)。您可能还记得,在初级英语课上,用图表表示句子,如图 2-6 所示。
## Word Senses and Semantics
![Parase_1](img/2f1511a2b2c9bf499667cb6649dbda15.jpg)
单词有意义,而且通常不止一个。一个词的不同含义称为它的意义(senses)。WordNet是一个长期运行的词汇资源项目,它来自普林斯顿大学,旨在对所有英语单词(嗯,大部分)的含义以及其他词汇关系进行分类。例如,考虑像“plane”这样的单词。图2-8显示了“plane”一词的不同用法。 ![Plane](img/f756e802f10adc3bacf2e61e4b5f160e.jpg "图2-8\. 对“plane”这个词的意义(sense)") 在WordNet这样的项目中数十年的努力是值得的,即使是在有现代方法的情况下。本书后面的章节给出了在神经网络和深度学习方法的背景下使用现有语言资源的例子。词的意义也可以从上下文中归纳出来。从文本中自动发现词义实际上是半监督学习在自然语言处理中的第一个应用。尽管我们在本书中没有涉及到这一点,但我们鼓励您阅读Jurasky and Martin(2014),第17章,Manning and Schutze(1999),第7章。
解析树(Parse tree)表示句子中不同的语法单元在层次上是如何相关的。图 2-6 中的解析树显示了所谓的成分解析。另一种可能更有用的显示关系的方法是使用依赖项解析(dependency parsing),如图 2-7 所示。
## Summary
![Parase_2](img/b4d0b914345a17162773e581f0ee08f9.jpg)
在这一章中,我们回顾了NLP中的一些基本术语和思想,这些在以后的章节中会很有用。本章只涉及了传统NLP所能提供的部分内容。我们忽略了传统NLP的一些重要方面,因为我们想将本书的大部分内容用于NLP的深度学习。然而,重要的是要知道,有大量的NLP研究工作不使用神经网络,但仍具有很高的影响力(即,广泛用于建筑生产系统)。在许多情况下,基于神经网络的方法应该被看作是传统方法的补充而不是替代。有经验的实践者经常使用这两个世界的优点来构建最先进的系统。为了更多地了解NLP的传统方法,我们推荐以下参考资料部分中的标题。
要了解更多关于传统解析的信息,请参阅本章末尾的参考资料部分。
## 单词意义和情感
单词有意义,而且通常不止一个。一个词的不同含义称为它的意义(senses)。WordNet 是一个长期运行的词汇资源项目,它来自普林斯顿大学,旨在对所有英语单词(嗯,大部分)的含义以及其他词汇关系进行分类。例如,考虑像`plane`这样的单词。图 2-8 显示了`plane`一词的不同用法。
![Plane](img/f756e802f10adc3bacf2e61e4b5f160e.jpg)
在 WordNet 这样的项目中数十年的努力是值得的,即使是在有现代方法的情况下。本书后面的章节给出了在神经网络和深度学习方法的背景下使用现有语言资源的例子。词的意义也可以从上下文中归纳出来。从文本中自动发现词义实际上是半监督学习在自然语言处理中的第一个应用。尽管我们在本书中没有涉及到这一点,但我们鼓励您阅读 Jurasky and Martin(2014),第 17 章,Manning and Schutze(1999),第 7 章。
## 总结
在这一章中,我们回顾了 NLP 中的一些基本术语和思想,这些在以后的章节中会很有用。本章只涉及了传统 NLP 所能提供的部分内容。我们忽略了传统 NLP 的一些重要方面,因为我们想将本书的大部分内容用于 NLP 的深度学习。然而,重要的是要知道,有大量的 NLP 研究工作不使用神经网络,但仍具有很高的影响力(即,广泛用于建筑生产系统)。在许多情况下,基于神经网络的方法应该被看作是传统方法的补充而不是替代。有经验的实践者经常使用这两个世界的优点来构建最先进的系统。为了更多地了解 NLP 的传统方法,我们推荐以下参考资料部分中的标题。
## 参考文献
References
1. `Manning, Christopher D., and Hinrich Schütze. (1999). Foundations of statistical natural language processing. MIT press.`
1. Manning, Christopher D., and Hinrich Schütze. (1999). Foundations of statistical natural language processing. MIT press.
2. `Bird, Steven, Ewan Klein, and Edward Loper. (2009). Natural language processing with Python: analyzing text with the natural language toolkit. O'Reilly Media.`
2. Bird, Steven, Ewan Klein, and Edward Loper. (2009). Natural language processing with Python: analyzing text with the natural language toolkit. O’Reilly Media.
3. `Smith, Noah A. (2011). "Linguistic structure prediction." Synthesis lectures on human language technologies.`
3. Smith, Noah A. (2011). “Linguistic structure prediction.” Synthesis lectures on human language technologies.
4. `Jurafsky, Dan, and James H. Martin. (2014). Speech and language processing. Vol. 3. London: Pearson.`
4. Jurafsky, Dan, and James H. Martin. (2014). Speech and language processing. Vol. 3\. London: Pearson.
5. `Russell, Stuart J., and Peter Norvig. (2016). Artificial intelligence: a modern approach. Malaysia: Pearson Education Limited.`
5. Russell, Stuart J., and Peter Norvig. (2016). Artificial intelligence: a modern approach. Malaysia: Pearson Education Limited.
6. Zheng, Alice, and Casari, Amanda. (2018). Feature Engineering for Machine Learning: Principles and Techniques for Data Scientists. O’Reilly Media, Inc.
6. `Zheng, Alice, and Casari, Amanda. (2018). Feature Engineering for Machine Learning: Principles and Techniques for Data Scientists. O'Reilly Media, Inc.`

@@ -1,51 +0,55 @@

# Chapter 4.自然语言处理 Feed-Forward Networks
# 四、自然语言处理的前馈网络
> 本文标题:[Natural-Language-Processing-with-PyTorch(四)](https://yifdu.github.io/2018/12/20/Natural-Language-Processing-with-PyTorch%EF%BC%88%E5%9B%9B%EF%BC%89/)
> 本文标题:[Natural-Language-Processing-with-PyTorch(四)](https://yifdu.github.io/2018/12/20/Natural-Language-Processing-with-PyTorch%EF%BC%88%E5%9B%9B%EF%BC%89/)
>
> 文章作者:[Yif Du](https://yifdu.github.io/ "访问 Yif Du 的个人博客")
> 文章作者:[Yif Du](https://yifdu.github.io/ "访问 Yif Du 的个人博客")
>
> 发布时间:2018年12月20日 - 09:12
> 发布时间:2018 年 12 月 20 日 - 09:12
>
> 最后更新:2018年12月28日 - 11:12
> 最后更新:2018 年 12 月 28 日 - 11:12
>
> 原始链接:[http://yifdu.github.io/2018/12/20/Natural-Language-Processing-with-PyTorch(四)/](https://yifdu.github.io/2018/12/20/Natural-Language-Processing-with-PyTorch%EF%BC%88%E5%9B%9B%EF%BC%89/)
> 原始链接:[http://yifdu.github.io/2018/12/20/Natural-Language-Processing-with-PyTorch(四)/](https://yifdu.github.io/2018/12/20/Natural-Language-Processing-with-PyTorch%EF%BC%88%E5%9B%9B%EF%BC%89/)
>
> 许可协议: [署名-非商业性使用-禁止演绎 4.0 国际](https://creativecommons.org/licenses/by-nc-nd/4.0/) 转载请保留原文链接及作者。
> 许可协议:[署名-非商业性使用-禁止演绎 4.0 国际](https://creativecommons.org/licenses/by-nc-nd/4.0/) 转载请保留原文链接及作者。
在第3章中,我们通过观察感知器来介绍神经网络的基础,感知器是现存最简单的神经网络。感知器的一个历史性的缺点是它不能学习数据中存在的一些非常重要的模式。例如,查看图4-1中绘制的数据点。这相当于非此即彼(XOR)的情况,在这种情况下,决策边界不能是一条直线(也称为线性可分)。在这个例子中,感知器失败了。 ![classifier](img/8dc539599dfa8638d1c07a77f68a8ed6.jpg "图4-1\. XOR数据集中的两个类绘制为圆形和星形。请注意,没有任何一行可以分隔这两个类。")
在第 3 章中,我们通过观察感知器来介绍神经网络的基础,感知器是现存最简单的神经网络。感知器的一个历史性的缺点是它不能学习数据中存在的一些非常重要的模式。例如,查看图 4-1 中绘制的数据点。这相当于非此即彼(XOR)的情况,在这种情况下,决策边界不能是一条直线(也称为线性可分)。在这个例子中,感知器失败了。
在这一章中,我们将探索传统上称为前馈网络的神经网络模型,以及两种前馈神经网络:多层感知器和卷积神经网络。多层感知器在结构上扩展了我们在第3章中研究的简单感知器,将多个感知器分组在一个单层,并将多个层叠加在一起。我们稍后将介绍多层感知器,并在“示例:带有多层感知器的姓氏分类”中展示它们在多层分类中的应用。
![classifier](img/8dc539599dfa8638d1c07a77f68a8ed6.jpg)
本章研究的第二种前馈神经网络,卷积神经网络,在处理数字信号时深受窗口滤波器的启发。通过这种窗口特性,卷积神经网络能够在输入中学习局部化模式,这不仅使其成为计算机视觉的主轴,而且是检测单词和句子等序列数据中的子结构的理想候选。我们在“卷积神经网络”中概述了卷积神经网络,并在“示例:使用CNN对姓氏进行分类”中演示了它们的使用。
在这一章中,我们将探索传统上称为前馈网络的神经网络模型,以及两种前馈神经网络:多层感知器和卷积神经网络。多层感知器在结构上扩展了我们在第 3 章中研究的简单感知器,将多个感知器分组在一个单层,并将多个层叠加在一起。我们稍后将介绍多层感知器,并在“示例:带有多层感知器的姓氏分类”中展示它们在多层分类中的应用。
在本章中,多层感知器和卷积神经网络被分组在一起,因为它们都是前馈神经网络,并且与另一类神经网络——递归神经网络(RNNs)形成对比,递归神经网络(RNNs)允许反馈(或循环),这样每次计算都可以从之前的计算中获得信息。在第6章和第7章中,我们将介绍RNNs以及为什么允许网络结构中的循环是有益的。
本章研究的第二种前馈神经网络,卷积神经网络,在处理数字信号时深受窗口滤波器的启发。通过这种窗口特性,卷积神经网络能够在输入中学习局部化模式,这不仅使其成为计算机视觉的主轴,而且是检测单词和句子等序列数据中的子结构的理想候选。我们在“卷积神经网络”中概述了卷积神经网络,并在“示例:使用 CNN 对姓氏进行分类”中演示了它们的使用。
在本章中,多层感知器和卷积神经网络被分组在一起,因为它们都是前馈神经网络,并且与另一类神经网络——循环神经网络(RNNs)形成对比,循环神经网络(RNNs)允许反馈(或循环),这样每次计算都可以从之前的计算中获得信息。在第 6 章和第 7 章中,我们将介绍 RNNs 以及为什么允许网络结构中的循环是有益的。
在我们介绍这些不同的模型时,确保您理解事物如何工作的一个有用方法是在计算数据张量时注意它们的大小和形状。每种类型的神经网络层对它所计算的数据张量的大小和形状都有特定的影响,理解这种影响可以极大地有助于对这些模型的深入理解。
## The Multilayer Perceptron
## 多层感知机
多层感知器(MLP)被认为是最基本的神经网络构建模块之一。最简单的MLP是对第3章感知器的扩展。感知器将数据向量作为输入,计算出一个输出值。在MLP中,许多感知器被分组,以便单个层的输出是一个新的向量,而不是单个输出值。在PyTorch中,正如您稍后将看到的,这只需设置线性层中的输出特性的数量即可完成。MLP的另一个方面是,它将多个层与每个层之间的非线性结合在一起。
多层感知器(MLP)被认为是最基本的神经网络构建模块之一。最简单的 MLP 是对第 3 章感知器的扩展。感知器将数据向量作为输入,计算出一个输出值。在 MLP 中,许多感知器被分组,以便单个层的输出是一个新的向量,而不是单个输出值。在 PyTorch 中,正如您稍后将看到的,这只需设置线性层中的输出特性的数量即可完成。MLP 的另一个方面是,它将多个层与每个层之间的非线性结合在一起。
最简单的MLP,如图4-2所示,由三个表示阶段和两个线性层组成。第一阶段是输入向量。这是给定给模型的向量。在“示例:对餐馆评论的情绪进行分类”中,输入向量是Yelp评论的一个收缩的one-hot表示。给定输入向量,第一个线性层计算一个隐藏向量——表示的第二阶段。隐藏向量之所以这样被调用,是因为它是位于输入和输出之间的层的输出。我们所说的“层的输出”是什么意思?理解这个的一种方法是隐藏向量中的值是组成该层的不同感知器的输出。使用这个隐藏的向量,第二个线性层计算一个输出向量。在像Yelp评论分类这样的二进制任务中,输出向量仍然可以是1。在多类设置中,您将在本章后面的“示例:带有多层感知器的姓氏分类”一节中看到,输出向量是类数量的大小。虽然在这个例子中,我们只展示了一个隐藏的向量,但是有可能有多个中间阶段,每个阶段产生自己的隐藏向量。最终的隐藏向量总是通过线性层和非线性的组合映射到输出向量。
最简单的 MLP,如图 4-2 所示,由三个表示阶段和两个线性层组成。第一阶段是输入向量。这是给定给模型的向量。在“示例:对餐馆评论的情绪进行分类”中,输入向量是 Yelp 评论的一个收缩的单热表示。给定输入向量,第一个线性层计算一个隐藏向量——表示的第二阶段。隐藏向量之所以这样被调用,是因为它是位于输入和输出之间的层的输出。我们所说的“层的输出”是什么意思?理解这个的一种方法是隐藏向量中的值是组成该层的不同感知器的输出。使用这个隐藏的向量,第二个线性层计算一个输出向量。在像 Yelp 评论分类这样的二进制任务中,输出向量仍然可以是 1。在多类设置中,您将在本章后面的“示例:带有多层感知器的姓氏分类”一节中看到,输出向量是类数量的大小。虽然在这个例子中,我们只展示了一个隐藏的向量,但是有可能有多个中间阶段,每个阶段产生自己的隐藏向量。最终的隐藏向量总是通过线性层和非线性的组合映射到输出向量。
![MLP](img/fbf11b4ef1911b77ba3253eec8dddeef.jpg "图4-2\. 一种具有两个线性层和三个表示阶段(输入向量、隐藏向量和输出向量)的MLP的可视化表示。")
![MLP](img/fbf11b4ef1911b77ba3253eec8dddeef.jpg)
mlp的力量来自于添加第二个线性层和允许模型学习一个线性分割的的中间表示——该属性的能表示一个直线(或更一般的,一个超平面)可以用来区分数据点落在线(或超平面)的哪一边的。学习具有特定属性的中间表示,如分类任务是线性可分的,这是使用神经网络的最深刻后果之一,也是其建模能力的精髓。在下一节中,我们将更深入地研究这意味着什么。
mlp 的力量来自于添加第二个线性层和允许模型学习一个线性分割的的中间表示——该属性的能表示一个直线(或更一般的,一个超平面)可以用来区分数据点落在线(或超平面)的哪一边的。学习具有特定属性的中间表示,如分类任务是线性可分的,这是使用神经网络的最深刻后果之一,也是其建模能力的精髓。在下一节中,我们将更深入地研究这意味着什么。
### A Simple Example: XOR
### 简单示例:XOR
让我们看一下前面描述的XOR示例,看看感知器与MLP之间会发生什么。在这个例子中,我们在一个二元分类任务中训练感知器和MLP:星和圆。每个数据点是一个二维坐标。在不深入研究实现细节的情况下,最终的模型预测如图4-3所示。在这个图中,错误分类的数据点用黑色填充,而正确分类的数据点没有填充。在左边的面板中,从填充的形状可以看出,感知器在学习一个可以将星星和圆分开的决策边界方面有困难。然而,MLP(右面板)学习了一个更精确地对恒星和圆进行分类的决策边界。
让我们看一下前面描述的 XOR 示例,看看感知器与 MLP 之间会发生什么。在这个例子中,我们在一个二元分类任务中训练感知器和 MLP:星和圆。每个数据点是一个二维坐标。在不深入研究实现细节的情况下,最终的模型预测如图 4-3 所示。在这个图中,错误分类的数据点用黑色填充,而正确分类的数据点没有填充。在左边的面板中,从填充的形状可以看出,感知器在学习一个可以将星星和圆分开的决策边界方面有困难。然而,MLP(右面板)学习了一个更精确地对恒星和圆进行分类的决策边界。
![MLP_1](img/63f1236fcd5854c18f80493f93cbfe3c.jpg "图4-3\. 从感知器(左)和MLP(右)学习的XOR问题的解决方案显示。每个数据点的真正类是该点的形状:星形或圆形。错误的分类用块填充,正确的分类没有填充。这些线是每个模型的决策边界。在左边的面板中,感知器学习一个不能正确地将圆与星分开的决策边界。事实上,没有一条线可以。在右边的面板中,MLP学会了从圆中分离星。")
![MLP_1](img/63f1236fcd5854c18f80493f93cbfe3c.jpg)
虽然在图中显示MLP有两个决策边界,这是它的优点,但它实际上只是一个决策边界!决策边界就是这样出现的,因为中间表示法改变了空间,使一个超平面同时出现在这两个位置上。在图4-4中,我们可以看到MLP计算的中间值。这些点的形状表示类(星形或圆形)。我们所看到的是,神经网络(本例中为MLP)已经学会了“扭曲”数据所处的空间,以便在数据通过最后一层时,用一线来分割它们。 ![MLP_2](/2018/12/20/Natural-Language-Processing-with-PyTorch(四)/MLP_2.png “图4-4\. MLP的输入和中间表示是可视化的。从左到右:(1)网络的输入,(2)第一个线性模块的输出,(3)第一个非线性模块的输出,(4)第二个线性模块的输出。如您所见,第一个线性模块的输出将圆和星分组,而第二个线性模块的输出将数据点重新组织为线性可分的。”)
虽然在图中显示 MLP 有两个决策边界,这是它的优点,但它实际上只是一个决策边界!决策边界就是这样出现的,因为中间表示法改变了空间,使一个超平面同时出现在这两个位置上。在图 4-4 中,我们可以看到 MLP 计算的中间值。这些点的形状表示类(星形或圆形)。我们所看到的是,神经网络(本例中为 MLP)已经学会了“扭曲”数据所处的空间,以便在数据通过最后一层时,用一线来分割它们。 MLP 的输入和中间表示是可视化的。从左到右:(1)网络的输入,(2)第一个线性模块的输出,(3)第一个非线性模块的输出,(4)第二个线性模块的输出。如您所见,第一个线性模块的输出将圆和星分组,而第二个线性模块的输出将数据点重新组织为线性可分的。
相反,如图4-5所示,感知器没有额外的一层来处理数据的形状,直到数据变成线性可分的。 ![MLP_3](img/a0cec65c8e77beeb01f46f5fef1a61ad.jpg "图4-5\. 感知器的输入和输出表示。因为它没有像MLP那样的中间表示来分组和重新组织,所以它不能将圆和星分开。")
相反,如图 4-5 所示,感知器没有额外的一层来处理数据的形状,直到数据变成线性可分的。
### Implementing MLPs in PyTorch
![MLP_3](img/a0cec65c8e77beeb01f46f5fef1a61ad.jpg)
在上一节中,我们概述了MLP的核心思想。在本节中,我们将介绍PyTorch中的一个实现。如前所述,MLP除了第3章中简单的感知器之外,还有一个额外的计算层。在我们在例4-1中给出的实现中,我们用PyTorch的两个线性模块实例化了这个想法。线性对象被命名为fc1和fc2,它们遵循一个通用约定,即将线性模块称为“完全连接层”,简称为“fc层”。除了这两个线性层外,还有一个修正的线性单元(ReLU)非线性(在第3章“激活函数”一节中介绍),它在被输入到第二个线性层之前应用于第一个线性层的输出。由于层的顺序性,您必须确保层中的输出数量等于下一层的输入数量。使用两个线性层之间的非线性是必要的,因为没有它,两个线性层在数学上等价于一个线性层4,因此不能建模复杂的模式。MLP的实现只实现反向传播的前向传递。这是因为PyTorch根据模型的定义和向前传递的实现,自动计算出如何进行向后传递和梯度更新。
### 在 PyTorch 中实现 MLP
Example 4-1\. Multilayer Perceptron
在上一节中,我们概述了 MLP 的核心思想。在本节中,我们将介绍 PyTorch 中的一个实现。如前所述,MLP 除了第 3 章中简单的感知器之外,还有一个额外的计算层。在我们在例 4-1 中给出的实现中,我们用 PyTorch 的两个线性模块实例化了这个想法。线性对象被命名为`fc1`和`fc2`,它们遵循一个通用约定,即将线性模块称为“完全连接层”,简称为“fc 层”。除了这两个线性层外,还有一个修正的线性单元(ReLU)非线性(在第 3 章“激活函数”一节中介绍),它在被输入到第二个线性层之前应用于第一个线性层的输出。由于层的顺序性,您必须确保层中的输出数量等于下一层的输入数量。使用两个线性层之间的非线性是必要的,因为没有它,两个线性层在数学上等价于一个线性层 4,因此不能建模复杂的模式。MLP 的实现只实现反向传播的前向传递。这是因为 PyTorch 根据模型的定义和向前传递的实现,自动计算出如何进行向后传递和梯度更新。
示例 4-1:多层感知机
```py

@@ -87,5 +91,5 @@ import torch.nn as nn

在例4-2中,我们实例化了MLP。由于MLP实现的通用性,我们可以为任何大小的输入建模。为了演示,我们使用大小为3的输入维度、大小为4的输出维度和大小为100的隐藏维度。请注意,在print语句的输出中,每个层中的单元数很好地排列在一起,以便为维度3的输入生成维度4的输出。
在例 4-2 中,我们实例化了 MLP。由于 MLP 实现的通用性,我们可以为任何大小的输入建模。为了演示,我们使用大小为 3 的输入维度、大小为 4 的输出维度和大小为 100 的隐藏维度。请注意,在`print`语句的输出中,每个层中的单元数很好地排列在一起,以便为维度 3 的输入生成维度 4 的输出。
Example 4-2\. An example instantiation of an MLP
示例 4-2:NLP 的示例实例化

@@ -111,5 +115,5 @@ ```py

我们可以通过传递一些随机输入来快速测试模型的“连接”,如示例4-3所示。因为模型还没有经过训练,所以输出是随机的。在花费时间训练模型之前,这样做是一个有用的完整性检查。请注意PyTorch的交互性是如何让我们在开发过程中实时完成所有这些工作的,这与使用NumPy或panda没有太大区别:
我们可以通过传递一些随机输入来快速测试模型的“连接”,如示例 4-3 所示。因为模型还没有经过训练,所以输出是随机的。在花费时间训练模型之前,这样做是一个有用的完整性检查。请注意 PyTorch 的交互性是如何让我们在开发过程中实时完成所有这些工作的,这与使用 NumPy 或 panda 没有太大区别:
Example 4-3\. Testing the MLP with random inputs
示例 4-3:使用随机输入测试 MLP

@@ -143,7 +147,7 @@ ```py

学习如何读取PyTorch模型的输入和输出非常重要。在前面的例子中,MLP模型的输出是一个有两行四列的张量。这个张量中的行与批处理维数对应,批处理维数是小批处理中的数据点的数量。列是每个数据点的最终特征向量。在某些情况下,例如在分类设置中,特征向量是一个预测向量。名称为“预测向量”表示它对应于一个概率分布。预测向量会发生什么取决于我们当前是在进行训练还是在执行推理。在训练期间,输出按原样使用,带有一个损失函数和目标类标签的表示。我们将在“示例:带有多层感知器的姓氏分类”中对此进行深入介绍。
学习如何读取 PyTorch 模型的输入和输出非常重要。在前面的例子中,MLP 模型的输出是一个有两行四列的张量。这个张量中的行与批量维数对应,批量维数是小批量中的数据点的数量。列是每个数据点的最终特征向量。在某些情况下,例如在分类设置中,特征向量是一个预测向量。名称为“预测向量”表示它对应于一个概率分布。预测向量会发生什么取决于我们当前是在进行训练还是在执行推理。在训练期间,输出按原样使用,带有一个损失函数和目标类标签的表示。我们将在“示例:带有多层感知器的姓氏分类”中对此进行深入介绍。
但是,如果您想将预测向量转换为概率,则需要额外的步骤。具体来说,您需要softmax函数,它用于将一个值向量转换为概率。softmax有许多根。在物理学中,它被称为玻尔兹曼或吉布斯分布;在统计学中,它是多项式逻辑回归;在自然语言处理(NLP)社区,它是最大熵(MaxEnt)分类器。不管叫什么名字,这个函数背后的直觉是,大的正值会导致更高的概率,小的负值会导致更小的概率。在示例4-3中,apply_softmax参数应用了这个额外的步骤。在例4-4中,您可以看到相同的输出,但是这次将apply_softmax标志设置为True:
但是,如果您想将预测向量转换为概率,则需要额外的步骤。具体来说,您需要 softmax 函数,它用于将一个值向量转换为概率。softmax 有许多根。在物理学中,它被称为玻尔兹曼或吉布斯分布;在统计学中,它是多项式逻辑回归;在自然语言处理(NLP)社区,它是最大熵(MaxEnt)分类器。不管叫什么名字,这个函数背后的直觉是,大的正值会导致更高的概率,小的负值会导致更小的概率。在示例 4-3 中,`apply_softmax`参数应用了这个额外的步骤。在例 4-4 中,您可以看到相同的输出,但是这次将`apply_softmax`标志设置为`True`:
Example 4-4\. MLP with apply_softmax=True
示例 4-4:带有`apply_softmax=True`的 MLP

@@ -163,23 +167,23 @@ ```py

综上所述,mlp是将张量映射到其他张量的线性层。在每一对线性层之间使用非线性来打破线性关系,并允许模型扭曲向量空间。在分类设置中,这种扭曲应该导致类之间的线性可分性。另外,您可以使用softmax函数将MLP输出解释为概率,但是不应该将softmax与特定的损失函数一起使用,因为底层实现可以利用高级数学/计算捷径。
综上所述,mlp 是将张量映射到其他张量的线性层。在每一对线性层之间使用非线性来打破线性关系,并允许模型扭曲向量空间。在分类设置中,这种扭曲应该导致类之间的线性可分性。另外,您可以使用 softmax 函数将 MLP 输出解释为概率,但是不应该将 softmax 与特定的损失函数一起使用,因为底层实现可以利用高级数学/计算捷径。
# Example: Surname Classification with a Multilayer Perceptron
在本节中,我们将MLP应用于将姓氏分类到其原籍国的任务。从公开观察到的数据推断人口统计信息(如国籍)具有从产品推荐到确保不同人口统计用户获得公平结果的应用。人口统计和其他自我识别信息统称为“受保护属性”。“在建模和产品中使用这些属性时,必须小心。”我们首先对每个姓氏的字符进行拆分,并像对待“示例:将餐馆评论的情绪分类”中的单词一样对待它们。除了数据上的差异,字符层模型在结构和实现上与基于单词的模型基本相似.
在本节中,我们将 MLP 应用于将姓氏分类到其原籍国的任务。从公开观察到的数据推断人口统计信息(如国籍)具有从产品推荐到确保不同人口统计用户获得公平结果的应用。人口统计和其他自我识别信息统称为“受保护属性”。“在建模和产品中使用这些属性时,必须小心。”我们首先对每个姓氏的字符进行拆分,并像对待“示例:将餐馆评论的情绪分类”中的单词一样对待它们。除了数据上的差异,字符层模型在结构和实现上与基于单词的模型基本相似.
您应该从这个例子中吸取的一个重要教训是,MLP的实现和训练是从我们在第3章中看到的感知器的实现和培训直接发展而来的。事实上,我们在本书的第3章中提到了这个例子,以便更全面地了解这些组件。此外,我们不包括你可以在“例子:餐馆评论的情绪分类”中看到的代码,如果你想在一个地方看到例子代码,我们强烈建议你跟随补充材料。
您应该从这个例子中吸取的一个重要教训是,MLP 的实现和训练是从我们在第 3 章中看到的感知器的实现和训练直接发展而来的。事实上,我们在本书的第 3 章中提到了这个例子,以便更全面地了解这些组件。此外,我们不包括你可以在“例子:餐馆评论的情绪分类”中看到的代码,如果你想在一个地方看到例子代码,我们强烈建议你跟随补充材料。
本节的其余部分将从姓氏数据集及其预处理步骤的描述开始。然后,我们使用词汇表、向量化器和DataLoader类逐步完成从姓氏字符串到向量化小批处理的管道。如果您通读了第3章,您应该将这些辅助类视为老朋友,只是做了一些小小的修改。
本节的其余部分将从姓氏数据集及其预处理步骤的描述开始。然后,我们使用`Vocabulary`,`Vectorizer`和`DataLoader`类逐步完成从姓氏字符串到向量化小批量的管道。如果您通读了第 3 章,您应该将这些辅助类视为老朋友,只是做了一些小小的修改。
我们将通过描述姓氏分类器模型及其设计背后的思想过程来继续本节。MLP类似于我们在第3章中看到的感知器例子,但是除了模型的改变,我们在这个例子中引入了多类输出及其对应的损失函数。在描述了模型之后,我们完成了训练例程。训练程序与您在“示例:对餐馆评论的情绪进行分类”中看到的非常相似,因此为了简洁起见,我们在这里不像在该部分中那样深入。我们强烈建议您回顾这一节以获得更多的澄清。
我们将通过描述姓氏分类器模型及其设计背后的思想过程来继续本节。MLP 类似于我们在第 3 章中看到的感知器例子,但是除了模型的改变,我们在这个例子中引入了多类输出及其对应的损失函数。在描述了模型之后,我们完成了训练例程。训练程序与您在“示例:对餐馆评论的情绪进行分类”中看到的非常相似,因此为了简洁起见,我们在这里不像在该部分中那样深入。我们强烈建议您回顾这一节以获得更多的澄清。
### The Surname Dataset
### 姓氏数据集
在这个例子中,我们介绍了一个姓氏数据集,它收集了来自18个不同国家的10,000个姓氏,这些姓氏是作者从互联网上不同的姓名来源收集的。该数据集将在本书的几个示例中重用,并具有一些使其有趣的属性。第一个性质是它是相当不平衡的。排名前三的课程占数据的60%以上:27%是英语,21%是俄语,14%是阿拉伯语。剩下的15个民族的频率也在下降——这也是语言特有的特性。第二个特点是,在国籍和姓氏正字法(拼写)之间有一种有效和直观的关系。有些拼写变体与原籍国联系非常紧密(比如“O ‘Neill”、“Antonopoulos”、“Nagasawa”或“Zhu”)。
在这个例子中,我们介绍了一个姓氏数据集,它收集了来自 18 个不同国家的 10,000 个姓氏,这些姓氏是作者从互联网上不同的姓名来源收集的。该数据集将在本书的几个示例中重用,并具有一些使其有趣的属性。第一个性质是它是相当不平衡的。排名前三的课程占数据的 60% 以上:27% 是英语,21% 是俄语,14% 是阿拉伯语。剩下的 15 个民族的频率也在下降——这也是语言特有的特性。第二个特点是,在国籍和姓氏正字法(拼写)之间有一种有效和直观的关系。有些拼写变体与原籍国联系非常紧密(比如`O'Neill`、`Antonopoulos`、`Nagasawa`或`Zhu`)。
为了创建最终的数据集,我们从一个比本书补充材料中包含的版本处理更少的版本开始,并执行了几个数据集修改操作。第一个目的是减少这种不平衡——原始数据集中70%以上是俄文,这可能是由于抽样偏差或俄文姓氏的增多。为此,我们通过选择标记为俄语的姓氏的随机子集对这个过度代表的类进行子样本。接下来,我们根据国籍对数据集进行分组,并将数据集分为三个部分:70%到训练数据集,15%到验证数据集,最后15%到测试数据集,以便跨这些部分的类标签分布具有可比性。
为了创建最终的数据集,我们从一个比本书补充材料中包含的版本处理更少的版本开始,并执行了几个数据集修改操作。第一个目的是减少这种不平衡——原始数据集中 70% 以上是俄文,这可能是由于抽样偏差或俄文姓氏的增多。为此,我们通过选择标记为俄语的姓氏的随机子集对这个过度代表的类进行子样本。接下来,我们根据国籍对数据集进行分组,并将数据集分为三个部分:70% 到训练数据集,15% 到验证数据集,最后 15% 到测试数据集,以便跨这些部分的类标签分布具有可比性。
SurnameDataset的实现与“Example: classification of Sentiment of Restaurant Reviews”中的ReviewDataset几乎相同,只是在**getitem**方法的实现方式上略有不同。回想一下,本书中呈现的数据集类继承自PyTorch的数据集类,因此,我们需要实现两个函数:__getitem**方法,它在给定索引时返回一个数据点;以及**len**方法,该方法返回数据集的长度。“示例:餐厅评论的情绪分类”中的示例与本示例的区别在**getitem__中,如示例4-5所示。它不像“示例:将餐馆评论的情绪分类”那样返回一个向量化的评论,而是返回一个向量化的姓氏和与其国籍相对应的索引:
`SurnameDataset`的实现与“示例:餐馆评论的情感分类”中的`ReviewDataset`几乎相同,只是在`__getitem__`方法的实现方式上略有不同。回想一下,本书中呈现的数据集类继承自 PyTorch 的数据集类,因此,我们需要实现两个函数:`__getitem__`方法,它在给定索引时返回一个数据点;以及`__len__`方法,该方法返回数据集的长度。“示例:餐厅评论的情绪分类”中的示例与本示例的区别在`__getitem__`中,如示例 4-5 所示。它不像“示例:将餐馆评论的情绪分类”那样返回一个向量化的评论,而是返回一个向量化的姓氏和与其国籍相对应的索引:
Example 4-5\. Implementing SurnameDataset.__getitem__()
示例 4-5:实现`SurnameDataset.__getitem__()`

@@ -202,14 +206,18 @@ ```py

### Vocabulary, Vectorizer, and DataLoader
### `Vocabulary`,`Vectorizer`和`DataLoader`
为了使用字符对姓氏进行分类,我们使用词汇表、向量化器和DataLoader将姓氏字符串转换为向量化的minibatches。这些数据结构与“Example: Classifying Sentiment of Restaurant Reviews”中使用的数据结构相同,它们举例说明了一种多态性,这种多态性将姓氏的字符标记与Yelp评论的单词标记相同对待。数据不是通过将字令牌映射到整数来向量化的,而是通过将字符映射到整数来向量化的。
为了使用字符对姓氏进行分类,我们使用`Vocabulary`,`Vectorizer`和`DataLoader`将姓氏字符串转换为向量化的小批量。这些数据结构与“示例:餐馆评论的情感分类”中使用的数据结构相同,它们举例说明了一种多态性,这种多态性将姓氏的字符标记与 Yelp 评论的单词标记相同对待。数据不是通过将字标记映射到整数来向量化的,而是通过将字符映射到整数来向量化的。
THE VOCABULARY CLASS 本例中使用的词汇类与“example: Classifying Sentiment of Restaurant Reviews”中的词汇完全相同,该词汇类将Yelp评论中的单词映射到对应的整数。简要概述一下,词汇表是两个Python字典的协调,这两个字典在令牌(在本例中是字符)和整数之间形成一个双射;也就是说,第一个字典将字符映射到整数索引,第二个字典将整数索引映射到字符。add_token方法用于向词汇表中添加新的令牌,lookup_token方法用于检索索引,lookup_index方法用于检索给定索引的令牌(在推断阶段很有用)。与Yelp评论的词汇表不同,我们使用的是one-hot词汇表,不计算字符出现的频率,只对频繁出现的条目进行限制。这主要是因为数据集很小,而且大多数字符足够频繁。
## 词汇表的类
THE SURNAMEVECTORIZER 虽然词汇表将单个令牌(字符)转换为整数,但SurnameVectorizer负责应用词汇表并将姓氏转换为向量。实例化和使用非常类似于“示例:对餐馆评论的情绪进行分类”中的ReviewVectorizer,但有一个关键区别:字符串没有在空格上分割。姓氏是字符的序列,每个字符在我们的词汇表中是一个单独的标记。然而,在“卷积神经网络”出现之前,我们将忽略序列信息,通过迭代字符串输入中的每个字符来创建输入的收缩one-hot向量表示。我们为以前未遇到的字符指定一个特殊的令牌,即UNK。由于我们仅从训练数据实例化词汇表,而且验证或测试数据中可能有惟一的字符,所以在字符词汇表中仍然使用UNK符号。
本例中使用的词汇类与“示例:餐馆评论的情感分类”中的词汇完全相同,该词汇类将 Yelp 评论中的单词映射到对应的整数。简要概述一下,词汇表是两个 Python 字典的协调,这两个字典在标记(在本例中是字符)和整数之间形成一个双射;也就是说,第一个字典将字符映射到整数索引,第二个字典将整数索引映射到字符。`add_token`方法用于向词汇表中添加新的标记,`lookup_token`方法用于检索索引,`lookup_index`方法用于检索给定索引的标记(在推断阶段很有用)。与 Yelp 评论的词汇表不同,我们使用的是单热词汇表,不计算字符出现的频率,只对频繁出现的条目进行限制。这主要是因为数据集很小,而且大多数字符足够频繁。
您应该注意,虽然我们在这个示例中使用了收缩的one-hot,但是在后面的章节中,您将了解其他向量化方法,它们是one-hot编码的替代方法,有时甚至更好。具体来说,在“示例:使用CNN对姓氏进行分类”中,您将看到一个热门矩阵,其中每个字符都是矩阵中的一个位置,并具有自己的热门向量。然后,在第5章中,您将学习嵌入层,返回整数向量的向量化,以及如何使用它们创建密集向量矩阵。但是现在,让我们看一下示例4-6中SurnameVectorizer的代码。
## `SurnameVectorizer`
Example 4-6\. Implementing SurnameVectorizer
虽然词汇表将单个标记(字符)转换为整数,但`SurnameVectorizer`负责应用词汇表并将姓氏转换为向量。实例化和使用非常类似于“示例:对餐馆评论的情绪进行分类”中的`ReviewVectorizer`,但有一个关键区别:字符串没有在空格上分割。姓氏是字符的序列,每个字符在我们的词汇表中是一个单独的标记。然而,在“卷积神经网络”出现之前,我们将忽略序列信息,通过迭代字符串输入中的每个字符来创建输入的收缩单热向量表示。我们为以前未遇到的字符指定一个特殊的标记,即`UNK`。由于我们仅从训练数据实例化词汇表,而且验证或测试数据中可能有惟一的字符,所以在字符词汇表中仍然使用`UNK`符号。
您应该注意,虽然我们在这个示例中使用了收缩的单热,但是在后面的章节中,您将了解其他向量化方法,它们是单热编码的替代方法,有时甚至更好。具体来说,在“示例:使用 CNN 对姓氏进行分类”中,您将看到一个热门矩阵,其中每个字符都是矩阵中的一个位置,并具有自己的热门向量。然后,在第 5 章中,您将学习嵌入层,返回整数向量的向量化,以及如何使用它们创建密集向量矩阵。但是现在,让我们看一下示例 4-6 中`SurnameVectorizer`的代码。
示例 4-6:实现`SurnameVectorizer`
```py

@@ -228,3 +236,3 @@ class SurnameVectorizer(object):

Returns:
one_hot (np.ndarray): a collapsed one-hot encoding
one_hot (np.ndarray): a collapsed onehot encoding
"""

@@ -258,9 +266,9 @@ vocab = self.surname_vocab

### The Surname Classifier Model
### 姓氏分类器模型
SurnameClassifier是本章前面介绍的MLP的实现(示例4-7)。第一个线性层将输入向量映射到中间向量,并对该向量应用非线性。第二线性层将中间向量映射到预测向量。
`SurnameClassifier`是本章前面介绍的 MLP 的实现(示例 4-7)。第一个线性层将输入向量映射到中间向量,并对该向量应用非线性。第二线性层将中间向量映射到预测向量。
在最后一步中,可选地应用softmax操作,以确保输出和为1;这就是所谓的“概率”。它是可选的原因与我们使用的损失函数的数学公式有关——交叉熵损失。我们研究了“损失函数”中的交叉熵损失。回想一下,交叉熵损失对于多类分类是最理想的,但是在训练过程中软最大值的计算不仅浪费而且在很多情况下并不稳定。
在最后一步中,可选地应用 softmax 操作,以确保输出和为 1;这就是所谓的“概率”。它是可选的原因与我们使用的损失函数的数学公式有关——交叉熵损失。我们研究了“损失函数”中的交叉熵损失。回想一下,交叉熵损失对于多类分类是最理想的,但是在训练过程中软最大值的计算不仅浪费而且在很多情况下并不稳定。
Example 4-7\. The SurnameClassifier as an MLP
示例 4-7:作为 MLP 的`SurnameClassifier`

@@ -305,7 +313,7 @@ ```py

### The Training Routine
### 训练例程
虽然我们使用了不同的模型、数据集和损失函数,但是训练例程是相同的。因此,在例4-8中,我们只展示了args以及本例中的训练例程与“示例:餐厅评论情绪分类”中的示例之间的主要区别。
虽然我们使用了不同的模型、数据集和损失函数,但是训练例程是相同的。因此,在例 4-8 中,我们只展示了`args`以及本例中的训练例程与“示例:餐厅评论情绪分类”中的示例之间的主要区别。
Example 4-8\. The args for classifying surnames with an MLP
示例 4-8:用于使用 MLP 分类姓氏的参数

@@ -332,7 +340,7 @@ ```py

训练中最显著的差异与模型中输出的种类和使用的损失函数有关。在这个例子中,输出是一个多类预测向量,可以转换为概率。正如在模型描述中所描述的,这种输出的损失类型仅限于CrossEntropyLoss和NLLLoss。由于它的简化,我们使用了CrossEntropyLoss。
训练中最显著的差异与模型中输出的种类和使用的损失函数有关。在这个例子中,输出是一个多类预测向量,可以转换为概率。正如在模型描述中所描述的,这种输出的损失类型仅限于`CrossEntropyLoss`和`NLLLoss`。由于它的简化,我们使用了`CrossEntropyLoss`。
在例4-9中,我们展示了数据集、模型、损失函数和优化器的实例化。这些实例应该看起来与“示例:将餐馆评论的情绪分类”中的实例几乎相同。事实上,在本书后面的章节中,这种模式将对每个示例进行重复。
在例 4-9 中,我们展示了数据集、模型、损失函数和优化器的实例化。这些实例应该看起来与“示例:将餐馆评论的情绪分类”中的实例几乎相同。事实上,在本书后面的章节中,这种模式将对每个示例进行重复。
Example 4-9\. Instantiating the dataset, model, loss, and optimizer
示例 4-9:实例化数据集,模型,损失和优化器

@@ -354,6 +362,8 @@ ```py

THE TRAINING LOOP 与“Example: Classifying Sentiment of Restaurant Reviews”中的训练循环相比,本例的训练循环除了变量名以外几乎是相同的。具体来说,示例4-10显示了使用不同的key从batch_dict中获取数据。除了外观上的差异,训练循环的功能保持不变。利用训练数据,计算模型输出、损失和梯度。然后,使用梯度来更新模型。
## 训练循环
Example 4-10\. A snippet of the training loop
与“示例:餐馆评论的情感分类”中的训练循环相比,本例的训练循环除了变量名以外几乎是相同的。具体来说,示例 4-10 显示了使用不同的键从`batch_dict`中获取数据。除了外观上的差异,训练循环的功能保持不变。利用训练数据,计算模型输出、损失和梯度。然后,使用梯度来更新模型。
示例 4-10:训练循环的代码段
```py

@@ -382,13 +392,18 @@ # the training routine is these 5 steps:

### Model Evaluation and Prediction
### 模型评估和预测
要理解模型的性能,您应该使用定量和定性方法分析模型。定量测量出的测试数据的误差,决定了分类器能否推广到不可见的例子。定性地说,您可以通过查看分类器的top-k预测来为一个新示例开发模型所了解的内容的直觉。 EVALUATING ON THE TEST DATASET 评价SurnameClassifier测试数据,我们执行相同的常规的routine文本分类的例子“餐馆评论的例子:分类情绪”:我们将数据集设置为遍历测试数据,调用classifier.eval()方法,并遍历测试数据以同样的方式与其他数据。在这个例子中,调用classifier.eval()可以防止PyTorch在使用测试/评估数据时更新模型参数。
要理解模型的性能,您应该使用定量和定性方法分析模型。定量测量出的测试数据的误差,决定了分类器能否推广到不可见的例子。定性地说,您可以通过查看分类器的前`k`个预测来为一个新示例开发模型所了解的内容的直觉。
该模型对测试数据的准确性达到50%左右。如果您在附带的笔记本中运行训练例程,您会注意到在训练数据上的性能更高。这是因为模型总是更适合它所训练的数据,所以训练数据的性能并不代表新数据的性能。如果您遵循代码,我们鼓励您尝试隐藏维度的不同大小。您应该注意到性能的提高。然而,这种增长不会很大(尤其是与“用CNN对姓氏进行分类的例子”中的模型相比)。其主要原因是收缩的onehot向量化方法是一种弱表示。虽然它确实简洁地将每个姓氏表示为单个向量,但它丢弃了字符之间的顺序信息,这对于识别起源非常重要。
## 在测试数据集上评估
评价`SurnameClassifier`测试数据,我们执行文本分类的例子“餐馆评论的例子:分类情绪”的相同的常规例程:我们将数据集设置为遍历测试数据,调用`classifier.eval()`方法,并遍历测试数据以同样的方式与其他数据。在这个例子中,调用`classifier.eval()`可以防止 PyTorch 在使用测试/评估数据时更新模型参数。
该模型对测试数据的准确性达到 50% 左右。如果您在附带的笔记本中运行训练例程,您会注意到在训练数据上的性能更高。这是因为模型总是更适合它所训练的数据,所以训练数据的性能并不代表新数据的性能。如果您遵循代码,我们鼓励您尝试隐藏维度的不同大小。您应该注意到性能的提高。然而,这种增长不会很大(尤其是与“用 CNN 对姓氏进行分类的例子”中的模型相比)。其主要原因是收缩的单热向量化方法是一种弱表示。虽然它确实简洁地将每个姓氏表示为单个向量,但它丢弃了字符之间的顺序信息,这对于识别起源非常重要。
CLASSIFYING A NEW SURNAME
示例4-11显示了分类新姓氏的代码。给定一个姓氏作为字符串,该函数将首先应用向量化过程,然后获得模型预测。注意,我们包含了apply_softmax标志,所以结果包含概率。模型预测,在多项式的情况下,是类概率的列表。我们使用PyTorch张量最大函数来得到由最高预测概率表示的最优类。
示例 4-11 显示了分类新姓氏的代码。给定一个姓氏作为字符串,该函数将首先应用向量化过程,然后获得模型预测。注意,我们包含了`apply_softmax`标志,所以结果包含概率。模型预测,在多项式的情况下,是类概率的列表。我们使用 PyTorch 张量最大函数来得到由最高预测概率表示的最优类。
Example 4-11\. A function for performing nationality prediction
示例 4-11:执行国籍预测的函数

@@ -412,6 +427,10 @@ ```py

RETRIEVING THE TOP-K PREDICTIONS FOR A NEW SURNAME 不仅要看最好的预测,还要看更多的预测。例如,NLP中的标准实践是采用k-best预测并使用另一个模型对它们重新排序。PyTorch提供了一个torch.topk函数,它提供了一种方便的方法来获得这些预测,如示例4-12所示。
## 为新的姓氏获取前`k`个预测
Example 4-12\. Predicting the top-k nationalities
RETRIEVING THE TOP-K PREDICTIONS FOR A NEW SURNAME
不仅要看最好的预测,还要看更多的预测。例如,NLP 中的标准实践是采用`k`个最佳预测并使用另一个模型对它们重新排序。PyTorch 提供了一个`torch.topk`函数,它提供了一种方便的方法来获得这些预测,如示例 4-12 所示。
示例 4-12:预测前`k`个国籍
```py

@@ -438,10 +457,12 @@ def predict_topk_nationality(name, classifier, vectorizer, k=5):

### ")Regularizing MLPs: Weight Regularization and Structural Regularization (or Dropout)
### MLPs 正则化:权重正则化和结构化正则化(或丢弃)
在第三章中,我们解释了正则化是如何解决过拟合问题的,并研究了两种重要的权重正则化类型——l1和L2。这些权值正则化方法也适用于MLPs和卷积神经网络,我们将在本章后面介绍。除权值正则化外,对于深度模型(即例如本章讨论的前馈网络,一种称为dropout的结构正则化方法变得非常重要。
在第三章中,我们解释了正则化是如何解决过拟合问题的,并研究了两种重要的权重正则化类型——l1 和 L2。这些权值正则化方法也适用于 MLPs 和卷积神经网络,我们将在本章后面介绍。除权值正则化外,对于深度模型(即例如本章讨论的前馈网络,一种称为丢弃的结构正则化方法变得非常重要。
DROPOUT
简单地说,在训练过程中,dropout有一定概率使属于两个相邻层的单元之间的连接减弱。这有什么用呢?我们从斯蒂芬•梅里蒂(Stephen Merity)的一段直观(且幽默)的解释开始: “Dropout,简单地说,是指如果你能在喝醉的时候反复学习如何做一件事,那么你应该能够在清醒的时候做得更好。这一见解产生了许多最先进的结果和一个新兴的领域。” 神经网络——尤其是具有大量分层的深层网络——可以在单元之间创建有趣的相互适应。“Coadaptation”是神经科学中的一个术语,但在这里它只是指一种情况,即两个单元之间的联系变得过于紧密,而牺牲了其他单元之间的联系。这通常会导致模型与数据过拟合。通过概率地丢弃单元之间的连接,我们可以确保没有一个单元总是依赖于另一个单元,从而产生健壮的模型。dropout不会向模型中添加额外的参数,但是需要一个超参数——“drop probability”。drop probability,正如你可能已经猜到的,是单位之间的连接drop的概率。通常将下降概率设置为0.5。例4-13给出了一个带dropout的MLP的重新实现。 Example 4-13\. MLP with dropout
简单地说,在训练过程中,丢弃有一定概率使属于两个相邻层的单元之间的连接减弱。这有什么用呢?我们从斯蒂芬•梅里蒂(Stephen Merity)的一段直观(且幽默)的解释开始: “丢弃,简单地说,是指如果你能在喝醉的时候反复学习如何做一件事,那么你应该能够在清醒的时候做得更好。这一见解产生了许多最先进的结果和一个新兴的领域。” 神经网络——尤其是具有大量分层的深层网络——可以在单元之间创建有趣的相互适应。“共同适应”(Coadaptation)是神经科学中的一个术语,但在这里它只是指一种情况,即两个单元之间的联系变得过于紧密,而牺牲了其他单元之间的联系。这通常会导致模型与数据过拟合。通过概率地丢弃单元之间的连接,我们可以确保没有一个单元总是依赖于另一个单元,从而产生健壮的模型。丢弃不会向模型中添加额外的参数,但是需要一个超参数——“丢弃概率”。丢弃概率,正如你可能已经猜到的,是单位之间的连接被丢弃的概率。通常将下降概率设置为 0.5。例 4-13 给出了一个带丢弃的 MLP 的重新实现。
示例 4-13:带有丢弃的 MLP
```py

@@ -483,46 +504,60 @@ import torch.nn as nn

请注意,dropout只适用于训练期间,不适用于评估期间。作为练习,我们鼓励您试验带有dropout的SurnameClassifier模型,看看它如何更改结果。
请注意,丢弃只适用于训练期间,不适用于评估期间。作为练习,我们鼓励您试验带有丢弃的`SurnameClassifier`模型,看看它如何更改结果。
## Convolutional Neural Networks
## 卷积神经网络
在本章的第一部分中,我们深入研究了MLPs、由一系列线性层和非线性函数构建的神经网络。mlp不是利用顺序模式的最佳工具。例如,在姓氏数据集中,姓氏可以有(不同长度的)段,这些段可以显示出相当多关于其起源国家的信息(如“O’Neill”中的“O”、“Antonopoulos”中的“opoulos”、“Nagasawa”中的“sawa”或“Zhu”中的“Zh”)。这些段的长度可以是可变的,挑战是在不显式编码的情况下捕获它们。
在本章的第一部分中,我们深入研究了 MLPs、由一系列线性层和非线性函数构建的神经网络。mlp 不是利用顺序模式的最佳工具。例如,在姓氏数据集中,姓氏可以有(不同长度的)段,这些段可以显示出相当多关于其起源国家的信息(如`O'Neill`中的`O`、`Antonopoulos`中的`opoulos`、`Nagasawa`中的`sawa`或`Zhu`中的`Zh`)。这些段的长度可以是可变的,挑战是在不显式编码的情况下捕获它们。
在本节中,我们将介绍卷积神经网络(CNN),这是一种非常适合检测空间子结构(并因此创建有意义的空间子结构)的神经网络。CNNs通过使用少量的权重来扫描输入数据张量来实现这一点。通过这种扫描,它们产生表示子结构检测(或不检测)的输出张量。
在本节中,我们将介绍卷积神经网络(CNN),这是一种非常适合检测空间子结构(并因此创建有意义的空间子结构)的神经网络。CNNs 通过使用少量的权重来扫描输入数据张量来实现这一点。通过这种扫描,它们产生表示子结构检测(或不检测)的输出张量。
## 在本节的其余部分中,我们首先描述CNN的工作方式,以及在设计CNN时应该考虑的问题。我们深入研究CNN超参数,目的是提供直观的行为和这些超参数对输出的影响。最后,我们通过几个简单的例子逐步说明CNNs的机制。在“示例:使用CNN对姓氏进行分类”中,我们将深入研究一个更广泛的示例。
在本节的其余部分中,我们首先描述 CNN 的工作方式,以及在设计 CNN 时应该考虑的问题。我们深入研究 CNN 超参数,目的是提供直观的行为和这些超参数对输出的影响。最后,我们通过几个简单的例子逐步说明 CNNs 的机制。在“示例:使用 CNN 对姓氏进行分类”中,我们将深入研究一个更广泛的示例。
HISTORICAL CONTEXT
## 历史背景
## CNNs的名称和基本功能源于经典的数学运算卷积。卷积已经应用于各种工程学科,包括数字信号处理和计算机图形学。一般来说,卷积使用程序员指定的参数。这些参数被指定来匹配一些功能设计,如突出边缘或抑制高频声音。事实上,许多Photoshop滤镜都是应用于图像的固定卷积运算。然而,在深度学习和本章中,我们从数据中学习卷积滤波器的参数,因此它对于解决当前的任务是最优的。
CNNs 的名称和基本功能源于经典的数学运算卷积。卷积已经应用于各种工程学科,包括数字信号处理和计算机图形学。一般来说,卷积使用程序员指定的参数。这些参数被指定来匹配一些功能设计,如突出边缘或抑制高频声音。事实上,许多 Photoshop 滤镜都是应用于图像的固定卷积运算。然而,在深度学习和本章中,我们从数据中学习卷积滤波器的参数,因此它对于解决当前的任务是最优的。
### CNN Hyperparameters
### CNN 超参数
为了理解不同的设计决策对CNN意味着什么,我们在图4-6中展示了一个示例。在本例中,单个“核”应用于输入矩阵。卷积运算(线性算子)的精确数学表达式对于理解这一节并不重要,但是从这个图中可以直观地看出,核是一个小的方阵,它被系统地应用于输入矩阵的不同位置。
为了理解不同的设计决策对 CNN 意味着什么,我们在图 4-6 中展示了一个示例。在本例中,单个“核”应用于输入矩阵。卷积运算(线性算子)的精确数学表达式对于理解这一节并不重要,但是从这个图中可以直观地看出,核是一个小的方阵,它被系统地应用于输入矩阵的不同位置。
虽然经典卷积是通过指定核的具体值来设计的,但是CNN是通过指定控制CNN行为的超参数来设计的,然后使用梯度下降来为给定数据集找到最佳参数。两个主要的超参数控制卷积的形状(称为kernel_size)和卷积将在输入数据张量(称为stride)中相乘的位置。还有一些额外的超参数控制输入数据张量被0填充了多少(称为padding),以及当应用到输入数据张量(称为dilation)时,乘法应该相隔多远。在下面的小节中,我们将更详细地介绍这些超参数。
虽然经典卷积是通过指定核的具体值来设计的,但是 CNN 是通过指定控制 CNN 行为的超参数来设计的,然后使用梯度下降来为给定数据集找到最佳参数。两个主要的超参数控制卷积的形状(称为`kernel_size`)和卷积将在输入数据张量(称为`stride`)中相乘的位置。还有一些额外的超参数控制输入数据张量被 0 填充了多少(称为`padding`),以及当应用到输入数据张量(称为`dilation`)时,乘法应该相隔多远。在下面的小节中,我们将更详细地介绍这些超参数。
DIMENSION OF THE CONVOLUTION OPERATION 首先要理解的概念是卷积运算的维数。在图4-6和本节的其他图中,我们使用二维卷积进行说明,但是根据数据的性质,还有更适合的其他维度的卷积。在PyTorch中,卷积可以是一维、二维或三维的,分别由Conv1d、Conv2d和Conv3d模块实现。一维卷积对于每个时间步都有一个特征向量的时间序列非常有用。在这种情况下,我们可以在序列维度上学习模式。NLP中的卷积运算大多是一维的卷积。另一方面,二维卷积试图捕捉数据中沿两个方向的时空模式;例如,在图像中沿高度和宽度维度——为什么二维卷积在图像处理中很流行。类似地,在三维卷积中,模式是沿着数据中的三维捕获的。例如,在视频数据中,信息是三维的,二维表示图像的帧,时间维表示帧的序列。就本书而言,我们主要使用Conv1d。
## 卷积操作的维度
CHANNELS 非正式地,通道(channel)是指沿输入中的每个点的特征维度。例如,在图像中,对应于RGB组件的图像中的每个像素有三个通道。在使用卷积时,文本数据也可以采用类似的概念。从概念上讲,如果文本文档中的“像素”是单词,那么通道的数量就是词汇表的大小。如果我们更细粒度地考虑字符的卷积,通道的数量就是字符集的大小(在本例中刚好是词汇表)。在PyTorch卷积实现中,输入通道的数量是in_channels参数。卷积操作可以在输出(out_channels)中产生多个通道。您可以将其视为卷积运算符将输入特征维“映射”到输出特征维。图4-7和图4-8说明了这个概念。
首先要理解的概念是卷积运算的维数。在图 4-6 和本节的其他图中,我们使用二维卷积进行说明,但是根据数据的性质,还有更适合的其他维度的卷积。在 PyTorch 中,卷积可以是一维、二维或三维的,分别由`Conv1d`、`Conv2d`和`Conv3d`模块实现。一维卷积对于每个时间步都有一个特征向量的时间序列非常有用。在这种情况下,我们可以在序列维度上学习模式。NLP 中的卷积运算大多是一维的卷积。另一方面,二维卷积试图捕捉数据中沿两个方向的时空模式;例如,在图像中沿高度和宽度维度——为什么二维卷积在图像处理中很流行。类似地,在三维卷积中,模式是沿着数据中的三维捕获的。例如,在视频数据中,信息是三维的,二维表示图像的帧,时间维表示帧的序列。就本书而言,我们主要使用`Conv1d`。
很难立即知道有多少输出通道适合当前的问题。为了简化这个困难,我们假设边界是1,1,024——我们可以有一个只有一个通道的卷积层,也可以有一个只有1,024个通道的卷积层。现在我们有了边界,接下来要考虑的是有多少个输入通道。一种常见的设计模式是,从一个卷积层到下一个卷积层,通道数量的缩减不超过2倍。这不是一个硬性的规则,但是它应该让您了解适当数量的out_channels是什么样子的。
## 通道
KERNEL SIZE
非正式地,通道(channel)是指沿输入中的每个点的特征维度。例如,在图像中,对应于 RGB 组件的图像中的每个像素有三个通道。在使用卷积时,文本数据也可以采用类似的概念。从概念上讲,如果文本文档中的“像素”是单词,那么通道的数量就是词汇表的大小。如果我们更细粒度地考虑字符的卷积,通道的数量就是字符集的大小(在本例中刚好是词汇表)。在 PyTorch 卷积实现中,输入通道的数量是`in_channels`参数。卷积操作可以在输出(`out_channels`)中产生多个通道。您可以将其视为卷积运算符将输入特征维“映射”到输出特征维。图 4-7 和图 4-8 说明了这个概念。
核矩阵的宽度称为核大小(PyTorch中的kernel_size)。在图4-6中,核大小为2,而在图4-9中,我们显示了一个大小为3的内核。您应该形成的直觉是,卷积将输入中的空间(或时间)本地信息组合在一起,每个卷积的本地信息量由内核大小控制。然而,通过增加核的大小,也会减少输出的大小(Dumoulin和Visin, 2016)。这就是为什么当核大小为3时,输出矩阵是图4-9中的2x2,而当核大小为2时,输出矩阵是图4-6中的3x3。
很难立即知道有多少输出通道适合当前的问题。为了简化这个困难,我们假设边界是 1,1,024——我们可以有一个只有一个通道的卷积层,也可以有一个只有 1,024 个通道的卷积层。现在我们有了边界,接下来要考虑的是有多少个输入通道。一种常见的设计模式是,从一个卷积层到下一个卷积层,通道数量的缩减不超过 2 倍。这不是一个硬性的规则,但是它应该让您了解适当数量的`out_channels`是什么样子的。
此外,您可以将NLP应用程序中核大小的行为看作类似于通过查看单词组捕获语言模式的n-gram的行为。使用较小的核大小,可以捕获较小的频繁模式,而较大的核大小会导致较大的模式,这可能更有意义,但是发生的频率更低。较小的核大小会导致输出中的细粒度特性,而较大的核大小会导致粗粒度特性。
## 核大小
STRIDE Stride控制卷积之间的步长。如果步长与核相同,则内核计算不会重叠。另一方面,如果跨度为1,则内核重叠最大。输出张量可以通过增加步幅的方式被有意的压缩来总结信息,如图4-10所示。 PADDING 即使stride和kernel_size允许控制每个计算出的特征值有多大范围,它们也有一个有害的、有时是无意的副作用,那就是缩小特征映射的总大小(卷积的输出)。为了抵消这一点,输入数据张量被人为地增加了长度(如果是一维、二维或三维)、高度(如果是二维或三维)和深度(如果是三维),方法是在每个维度上附加和前置0。这意味着CNN将执行更多的卷积,但是输出形状可以控制,而不会影响所需的核大小、步幅或扩展。图4-11展示了正在运行的填充。 DILATION 膨胀控制卷积核如何应用于输入矩阵。在图4-12中,我们显示,将膨胀从1(默认值)增加到2意味着当应用于输入矩阵时,核的元素彼此之间是两个空格。另一种考虑这个问题的方法是在核中跨跃——在核中的元素或核的应用之间存在一个step size,即存在“holes”。这对于在不增加参数数量的情况下总结输入空间的更大区域是有用的。当卷积层被叠加时,扩张卷积被证明是非常有用的。连续扩张的卷积指数级地增大了“接受域”的大小;即网络在做出预测之前所看到的输入空间的大小。
核矩阵的宽度称为核大小(PyTorch 中的`kernel_size`)。在图 4-6 中,核大小为 2,而在图 4-9 中,我们显示了一个大小为 3 的内核。您应该形成的直觉是,卷积将输入中的空间(或时间)本地信息组合在一起,每个卷积的本地信息量由内核大小控制。然而,通过增加核的大小,也会减少输出的大小(Dumoulin 和 Visin, 2016)。这就是为什么当核大小为 3 时,输出矩阵是图 4-9 中的`2x2`,而当核大小为 2 时,输出矩阵是图 4-6 中的`3x3`。
### Implementing CNNs in PyTorch
此外,您可以将 NLP 应用程序中核大小的行为看作类似于通过查看单词组捕获语言模式的 N 元组的行为。使用较小的核大小,可以捕获较小的频繁模式,而较大的核大小会导致较大的模式,这可能更有意义,但是发生的频率更低。较小的核大小会导致输出中的细粒度特性,而较大的核大小会导致粗粒度特性。
在本节中,我们将通过端到端示例来利用上一节中介绍的概念。一般来说,神经网络设计的目标是找到一个能够完成任务的超参数组态。我们再次考虑在“示例:带有多层感知器的姓氏分类”中引入的现在很熟悉的姓氏分类任务,但是我们将使用CNNs而不是MLP。我们仍然需要应用最后一个线性层,它将学会从一系列卷积层创建的特征向量创建预测向量。这意味着目标是确定卷积层的配置,从而得到所需的特征向量。所有CNN应用程序都是这样的:首先有一组卷积层,它们提取一个feature map,然后将其作为上游处理的输入。在分类中,上游处理几乎总是应用线性(或fc)层。
## 跨步
本节中的实现遍历设计决策,以构建一个特征向量。我们首先构造一个人工数据张量,以反映实际数据的形状。数据张量的大小是三维的——这是向量化文本数据的最小批大小。如果你对一个字符序列中的每个字符使用onehot向量,那么onehot向量序列就是一个矩阵,而onehot矩阵的小批量就是一个三维张量。使用卷积的术语,每个onehot(通常是词汇表的大小)的大小是”input channels”的数量,字符序列的长度是“width”。
跨步控制卷积之间的步长。如果步长与核相同,则内核计算不会重叠。另一方面,如果跨度为 1,则内核重叠最大。输出张量可以通过增加步幅的方式被有意的压缩来总结信息,如图 4-10 所示。
在例4-14中,构造特征向量的第一步是将PyTorch的Conv1d类的一个实例应用到三维数据张量。通过检查输出的大小,你可以知道张量减少了多少。我们建议您参考图4-9来直观地解释为什么输出张量在收缩。
## 填充
Example 4-14\. Artificial data and using a Conv1d class
即使跨步和`kernel_size`允许控制每个计算出的特征值有多大范围,它们也有一个有害的、有时是无意的副作用,那就是缩小特征映射的总大小(卷积的输出)。为了抵消这一点,输入数据张量被人为地增加了长度(如果是一维、二维或三维)、高度(如果是二维或三维)和深度(如果是三维),方法是在每个维度上附加和前置 0。这意味着 CNN 将执行更多的卷积,但是输出形状可以控制,而不会影响所需的核大小、步幅或扩展。图 4-11 展示了正在运行的填充。
## 膨胀
膨胀控制卷积核如何应用于输入矩阵。在图 4-12 中,我们显示,将膨胀从 1(默认值)增加到 2 意味着当应用于输入矩阵时,核的元素彼此之间是两个空格。另一种考虑这个问题的方法是在核中跨跃——在核中的元素或核的应用之间存在一个步长,即存在“洞”。这对于在不增加参数数量的情况下总结输入空间的更大区域是有用的。当卷积层被叠加时,扩张卷积被证明是非常有用的。连续扩张的卷积指数级地增大了“接受域”的大小;即网络在做出预测之前所看到的输入空间的大小。
### 在 PyTorch 实现 CNNs
在本节中,我们将通过端到端示例来利用上一节中介绍的概念。一般来说,神经网络设计的目标是找到一个能够完成任务的超参数组态。我们再次考虑在“示例:带有多层感知器的姓氏分类”中引入的现在很熟悉的姓氏分类任务,但是我们将使用 CNNs 而不是 MLP。我们仍然需要应用最后一个线性层,它将学会从一系列卷积层创建的特征向量创建预测向量。这意味着目标是确定卷积层的配置,从而得到所需的特征向量。所有 CNN 应用程序都是这样的:首先有一组卷积层,它们提取一个特征映射,然后将其作为上游处理的输入。在分类中,上游处理几乎总是应用线性(或 fc)层。
本节中的实现遍历设计决策,以构建一个特征向量。我们首先构造一个人工数据张量,以反映实际数据的形状。数据张量的大小是三维的——这是向量化文本数据的最小批大小。如果你对一个字符序列中的每个字符使用单热向量,那么单热向量序列就是一个矩阵,而单热矩阵的小批量就是一个三维张量。使用卷积的术语,每个单热向量(通常是词汇表的大小)的大小是输入通道的数量,字符序列的长度是宽度。
在例 4-14 中,构造特征向量的第一步是将 PyTorch 的`Conv1d`类的一个实例应用到三维数据张量。通过检查输出的大小,你可以知道张量减少了多少。我们建议您参考图 4-9 来直观地解释为什么输出张量在收缩。
示例 4-14:人造数据和使用`Conv1d`类
```py

@@ -545,5 +580,5 @@ Input[0]

进一步减小输出张量的主要方法有三种。第一种方法是创建额外的卷积并按顺序应用它们。最终,对应的sequence_width (dim=2)维度的大小将为1。我们在例4-15中展示了应用两个额外卷积的结果。一般来说,对输出张量的约简应用卷积的过程是迭代的,需要一些猜测工作。我们的示例是这样构造的:经过三次卷积之后,最终的输出在最终维度上的大小为1。
进一步减小输出张量的主要方法有三种。第一种方法是创建额外的卷积并按顺序应用它们。最终,对应的`sequence_width`(`dim=2`)维度的大小将为 1。我们在例 4-15 中展示了应用两个额外卷积的结果。一般来说,对输出张量的约简应用卷积的过程是迭代的,需要一些猜测工作。我们的示例是这样构造的:经过三次卷积之后,最终的输出在最终维度上的大小为 1。
Example 4-15\. The iterative application of convolutions to data
示例 4-15:卷积在数据上的迭代应用

@@ -571,7 +606,7 @@ ```py

在每次卷积中,通道维数的大小都会增加,因为通道维数是每个数据点的特征向量。张量实际上是一个特征向量的最后一步是去掉讨厌的尺寸=1维。您可以使用squeeze()方法来实现这一点。该方法将删除size=1的所有维度并返回结果。然后,得到的特征向量可以与其他神经网络组件(如线性层)一起使用来计算预测向量。
在每次卷积中,通道维数的大小都会增加,因为通道维数是每个数据点的特征向量。张量实际上是一个特征向量的最后一步是去掉讨厌的尺寸为 1 维。您可以使用`squeeze()`方法来实现这一点。该方法将删除`size=1`的所有维度并返回结果。然后,得到的特征向量可以与其他神经网络组件(如线性层)一起使用来计算预测向量。
另外还有两种方法可以将张量简化为每个数据点的一个特征向量:将剩余的值压平为特征向量,并在额外维度上求平均值。这两种方法如示例4-16所示。使用第一种方法,只需使用PyTorch的view()方法将所有向量平展成单个向量。第二种方法使用一些数学运算来总结向量中的信息。最常见的操作是算术平均值,但沿feature map维数求和和使用最大值也是常见的。每种方法都有其优点和缺点。扁平化保留了所有的信息,但会导致比预期(或计算上可行)更大的特征向量。平均变得与额外维度的大小无关,但可能会丢失信息。
另外还有两种方法可以将张量简化为每个数据点的一个特征向量:将剩余的值压平为特征向量,并在额外维度上求平均值。这两种方法如示例 4-16 所示。使用第一种方法,只需使用 PyTorch 的`view()`方法将所有向量平展成单个向量。第二种方法使用一些数学运算来总结向量中的信息。最常见的操作是算术平均值,但沿特征映射维数求和和使用最大值也是常见的。每种方法都有其优点和缺点。扁平化保留了所有的信息,但会导致比预期(或计算上可行)更大的特征向量。平均变得与额外维度的大小无关,但可能会丢失信息。
Example 4-16\. Two additional methods for reducing to feature vectors
示例 4-16:用于归约到特征向量的两个相加方法

@@ -597,11 +632,11 @@ ```py

为了证明CNN的有效性,让我们应用一个简单的CNN模型来分类姓氏。这项任务的许多细节与前面的MLP示例相同,但真正发生变化的是模型的构造和向量化过程。模型的输入,而不是我们在上一个例子中看到的收缩的onehot,将是一个onehot的矩阵。这种设计将使CNN能够更好地“view”字符的排列,并对在“示例:带有多层感知器的姓氏分类”中使用的收缩的onehot编码中丢失的序列信息进行编码。
为了证明 CNN 的有效性,让我们应用一个简单的 CNN 模型来分类姓氏。这项任务的许多细节与前面的 MLP 示例相同,但真正发生变化的是模型的构造和向量化过程。模型的输入,而不是我们在上一个例子中看到的收缩的单热向量,将是一个单热的矩阵。这种设计将使 CNN 能够更好地“查看”字符的排列,并对在“示例:带有多层感知器的姓氏分类”中使用的收缩的单热编码中丢失的序列信息进行编码。
### The SurnameDataset
虽然姓氏数据集之前在“示例:带有多层感知器的姓氏分类”中进行了描述,但是我们建议您参考“姓氏数据集”来了解它的描述。尽管我们使用了来自“示例:带有多层感知器的姓氏分类”中的相同数据集,但在实现上有一个不同之处:数据集由onehot向量矩阵组成,而不是一个收缩的onehot向量。为此,我们实现了一个数据集类,它跟踪最长的姓氏,并将其作为矩阵中包含的行数提供给矢量化器。列的数量是onehot向量的大小(词汇表的大小)。示例4-17显示了对SurnameDataset.__getitem__的更改;我们显示对SurnameVectorizer的更改。在下一小节向量化。
虽然姓氏数据集之前在“示例:带有多层感知器的姓氏分类”中进行了描述,但是我们建议您参考“姓氏数据集”来了解它的描述。尽管我们使用了来自“示例:带有多层感知器的姓氏分类”中的相同数据集,但在实现上有一个不同之处:数据集由单热向量矩阵组成,而不是一个收缩的单热向量。为此,我们实现了一个数据集类,它跟踪最长的姓氏,并将其作为矩阵中包含的行数提供给向量化器。列的数量是单热向量的大小(词汇表的大小)。示例 4-17 显示了对`SurnameDataset.__getitem__`的更改;我们显示对`SurnameVectorizer`的更改。在下一小节向量化。
我们使用数据集中最长的姓氏来控制onehot矩阵的大小有两个原因。首先,将每一小批姓氏矩阵组合成一个三维张量,要求它们的大小相同。其次,使用数据集中最长的姓氏意味着可以以相同的方式处理每个小批处理。
我们使用数据集中最长的姓氏来控制单热矩阵的大小有两个原因。首先,将每一小批姓氏矩阵组合成一个三维张量,要求它们的大小相同。其次,使用数据集中最长的姓氏意味着可以以相同的方式处理每个小批量。
Example 4-17\. SurnameDataset modified for passing the maximum surname length
示例 4-17:为传递最大姓氏长度而修改的`SurnameDataset`

@@ -626,9 +661,9 @@ ```py

### Vocabulary, Vectorizer, and DataLoader
### `Vocabulary`,`Vectorizer`和`DataLoader`
在本例中,尽管词汇表和DataLoader的实现方式与“示例:带有多层感知器的姓氏分类”中的示例相同,但Vectorizer的vectorize()方法已经更改,以适应CNN模型的需要。具体来说,正如我们在示例4-18中的代码中所示,该函数将字符串中的每个字符映射到一个整数,然后使用该整数构造一个由onehot向量组成的矩阵。重要的是,矩阵中的每一列都是不同的onehot向量。主要原因是,我们将使用的Conv1d层要求数据张量在第0维上具有批处理,在第1维上具有通道,在第2维上具有特性。
在本例中,尽管词汇表和`DataLoader`的实现方式与“示例:带有多层感知器的姓氏分类”中的示例相同,但`Vectorizer`的`vectorize()`方法已经更改,以适应 CNN 模型的需要。具体来说,正如我们在示例 4-18 中的代码中所示,该函数将字符串中的每个字符映射到一个整数,然后使用该整数构造一个由单热向量组成的矩阵。重要的是,矩阵中的每一列都是不同的单热向量。主要原因是,我们将使用的`Conv1d`层要求数据张量在第 0 维上具有批量,在第 1 维上具有通道,在第 2 维上具有特性。
除了更改为使用onehot矩阵之外,我们还修改了矢量化器,以便计算姓氏的最大长度并将其保存为max_surname_length
除了更改为使用单热矩阵之外,我们还修改了向量化器,以便计算姓氏的最大长度并将其保存为`max_surname_length`
Example 4-18\. Implementing the Surname Vectorizer for CNNs
示例 4-18:为 CNN 实现姓氏向量化器

@@ -643,3 +678,3 @@ ```py

Returns:
one_hot_matrix (np.ndarray): a matrix of one-hot vectors
one_hot_matrix (np.ndarray): a matrix of onehot vectors
"""

@@ -679,11 +714,11 @@

### Reimplementing the SurnameClassifier with Convolutional Networks
### 使用 CNN 重新实现`SurnameClassifier`
我们在本例中使用的模型是使用我们在“卷积神经网络”中介绍的方法构建的。实际上,我们在该部分中创建的用于测试卷积层的“人工”数据与姓氏数据集中使用本例中的矢量化器的数据张量的大小完全匹配。正如您在示例4-19中所看到的,它与我们在“卷积神经网络”中引入的Conv1d序列既有相似之处,也有需要解释的新添加内容。具体来说,该模型类似于“卷积神经网络”,它使用一系列一维卷积来增量地计算更多的特征,从而得到一个单特征向量。
我们在本例中使用的模型是使用我们在“卷积神经网络”中介绍的方法构建的。实际上,我们在该部分中创建的用于测试卷积层的“人工”数据与姓氏数据集中使用本例中的向量化器的数据张量的大小完全匹配。正如您在示例 4-19 中所看到的,它与我们在“卷积神经网络”中引入的`Conv1d`序列既有相似之处,也有需要解释的新添加内容。具体来说,该模型类似于“卷积神经网络”,它使用一系列一维卷积来增量地计算更多的特征,从而得到一个单特征向量。
然而,本例中的新内容是使用sequence和ELU PyTorch模块。序列模块是封装线性操作序列的方便包装器。在这种情况下,我们使用它来封装Conv1d序列的应用程序。ELU是类似于第3章中介绍的ReLU的非线性函数,但是它不是将值裁剪到0以下,而是对它们求幂。ELU已经被证明是卷积层之间使用的一种很有前途的非线性(Clevert et al., 2015)。
然而,本例中的新内容是使用`sequence`和 ELU PyTorch 模块。序列模块是封装线性操作序列的方便包装器。在这种情况下,我们使用它来封装`Conv1d`序列的应用程序。ELU 是类似于第 3 章中介绍的 ReLU 的非线性函数,但是它不是将值裁剪到 0 以下,而是对它们求幂。ELU 已经被证明是卷积层之间使用的一种很有前途的非线性(Clevert et al., 2015)。
在本例中,我们将每个卷积的通道数与num_channels超参数绑定。我们可以选择不同数量的通道分别进行卷积运算。这样做需要优化更多的超参数。我们发现256足够大,可以使模型达到合理的性能。
在本例中,我们将每个卷积的通道数与`num_channels`超参数绑定。我们可以选择不同数量的通道分别进行卷积运算。这样做需要优化更多的超参数。我们发现 256 足够大,可以使模型达到合理的性能。
Example 4-19\. The CNN-based SurnameClassifier
示例 4-19:基于 CNN 的`SurnameClassifier`

@@ -742,7 +777,7 @@ ```py

### The Training Routine
### 训练例程
训练程序包括以下似曾相识的的操作序列:实例化数据集,实例化模型,实例化损失函数,实例化优化器,遍历数据集的训练分区和更新模型参数,遍历数据集的验证分区和测量性能,然后重复数据集迭代一定次数。此时,这是本书到目前为止的第三个训练例程实现,应该将这个操作序列内部化。对于这个例子,我们将不再详细描述具体的训练例程,因为它与“示例:带有多层感知器的姓氏分类”中的例程完全相同。但是,输入参数是不同的,您可以在示例4-20中看到。
训练程序包括以下似曾相识的的操作序列:实例化数据集,实例化模型,实例化损失函数,实例化优化器,遍历数据集的训练分区和更新模型参数,遍历数据集的验证分区和测量性能,然后重复数据集迭代一定次数。此时,这是本书到目前为止的第三个训练例程实现,应该将这个操作序列内部化。对于这个例子,我们将不再详细描述具体的训练例程,因为它与“示例:带有多层感知器的姓氏分类”中的例程完全相同。但是,输入参数是不同的,您可以在示例 4-20 中看到。
Example 4-20\. Input arguments to the CNN surname classifier
示例 4-20:CNN 姓氏分类器的输入参数

@@ -771,14 +806,16 @@ ```py

### Model Evaluation and Prediction
### 模型评估和预测
要理解模型的性能,需要对性能进行定量和定性的度量。下面将描述这两个度量的基本组件。我们鼓励您扩展它们,以探索该模型及其所学习到的内容。
Evaluating on the Test Dataset 正如“示例:带有多层感知器的姓氏分类”中的示例与本示例之间的训练例程没有变化一样,执行评估的代码也没有变化。总之,调用分类器的eval()方法来防止反向传播,并迭代测试数据集。与MLP约50%的性能相比,该模型的测试集性能准确率约为56%。尽管这些性能数字绝不是这些特定架构的上限,但是通过一个相对简单的CNN模型获得的改进应该足以让您在文本数据上尝试CNNs。
## 在测试集上评估
Classifying or retrieving top predictions for a new surname
正如“示例:带有多层感知器的姓氏分类”中的示例与本示例之间的训练例程没有变化一样,执行评估的代码也没有变化。总之,调用分类器的`eval()`方法来防止反向传播,并迭代测试数据集。与 MLP 约 50% 的性能相比,该模型的测试集性能准确率约为 56%。尽管这些性能数字绝不是这些特定架构的上限,但是通过一个相对简单的 CNN 模型获得的改进应该足以让您在文本数据上尝试 CNNs。
在本例中,predict_nationality()函数的一部分发生了更改,如示例4-21所示:我们没有使用视图方法重塑新创建的数据张量以添加批处理维度,而是使用PyTorch的unsqueeze()函数在批处理应该在的位置添加大小为1的维度。相同的更改反映在predict_topk_nationality()函数中。
## 为新的姓氏分类或获取最佳预测
Example 4-21\. Using the trained model to make predictions
在本例中,`predict_nationality()`函数的一部分发生了更改,如示例 4-21 所示:我们没有使用视图方法重塑新创建的数据张量以添加批量维度,而是使用 PyTorch 的`unsqueeze()`函数在批量应该在的位置添加大小为 1 的维度。相同的更改反映在`predict_topk_nationality()`函数中。
示例 4-21:使用训练过的模型做出预测
```py

@@ -809,15 +846,15 @@ def predict_nationality(surname, classifier, vectorizer):

## Miscellaneous Topics in CNNs
## CNN 中的其他话题
为了结束我们的讨论,我们概述了几个其他的主题,这些主题是CNNs的核心,但在它们的共同使用中起着主要作用。特别是,您将看到Pooling操作、batch Normalization、network-in-network connection和residual connections的描述。
为了结束我们的讨论,我们概述了几个其他的主题,这些主题是 CNNs 的核心,但在它们的共同使用中起着主要作用。特别是,您将看到池化操作、批量规范化、网络中的网络连接和残差连接的描述。
### Pooling Operation
### 池化操作
Pooling是将高维特征映射总结为低维特征映射的操作。卷积的输出是一个特征映射。feature map中的值总结了输入的一些区域。由于卷积计算的重叠性,许多计算出的特征可能是冗余的。Pooling是一种将高维(可能是冗余的)特征映射总结为低维特征映射的方法。在形式上,池是一种像sum、mean或max这样的算术运算符,系统地应用于feature map中的局部区域,得到的池操作分别称为sum pooling、average pooling和max pooling。池还可以作为一种方法,将较大但较弱的feature map的统计强度改进为较小但较强的feature map。图4-13说明了Pooling。
池化是将高维特征映射总结为低维特征映射的操作。卷积的输出是一个特征映射。特征映射中的值总结了输入的一些区域。由于卷积计算的重叠性,许多计算出的特征可能是冗余的。池化是一种将高维(可能是冗余的)特征映射总结为低维特征映射的方法。在形式上,池是一种像求和,均值或最大值这样的算术运算符,系统地应用于特征映射中的局部区域,得到的池操作分别称为求和池化、平均池化和最大池化。池还可以作为一种方法,将较大但较弱的特征映射的统计强度改进为较小但较强的特征映射。图 4-13 说明了池化。
### ")Batch Normalization (BatchNorm)
### 批量规范化(BatchNorm)
批处理标准化是设计网络时经常使用的一种工具。BatchNorm对CNN的输出进行转换,方法是将激活量缩放为零均值和单位方差。它用于Z-transform的平均值和方差值每批更新一次,这样任何单个批中的波动都不会太大地移动或影响它。BatchNorm允许模型对参数的初始化不那么敏感,并且简化了学习速率的调整(Ioffe and Szegedy, 2015)。在PyTorch中,批处理规范是在nn模块中定义的。例4-22展示了如何用卷积和线性层实例化和使用批处理规范。
批量标准化是设计网络时经常使用的一种工具。BatchNorm 对 CNN 的输出进行转换,方法是将激活量缩放为零均值和单位方差。它用于 Z 转换的平均值和方差值每批更新一次,这样任何单个批中的波动都不会太大地移动或影响它。BatchNorm 允许模型对参数的初始化不那么敏感,并且简化了学习速率的调整(Ioffe and Szegedy, 2015)。在 PyTorch 中,批量规范是在`nn`模块中定义的。例 4-22 展示了如何用卷积和线性层实例化和使用批量规范。
Example 4-22\. Using s Conv1D layer with batch normalization.
示例 4-22:使用`Conv1D`层和批量规范化

@@ -840,12 +877,12 @@ ```py

### ")Network-in-Network Connections (1x1 Convolutions)
### 网络中的网络连接(`1x1`卷积)
Network-in-Network (NiN)连接是具有kernel_size=1的卷积内核,具有一些有趣的特性。具体来说,1x1卷积就像通道之间的一个完全连通的线性层。这在从多通道feature map映射到更浅的feature map时非常有用。在图4-14中,我们展示了一个应用于输入矩阵的NiN连接。如您所见,它将两个通道简化为一个通道。因此,NiN或1x1卷积提供了一种廉价的方法来合并参数较少的额外非线性(Lin et al., 2013)。
网络中的网络连接(NiN)连接是具有`kernel_size=1`的卷积内核,具有一些有趣的特性。具体来说,`1x1`卷积就像通道之间的一个完全连通的线性层。这在从多通道特征映射映射到更浅的特征映射时非常有用。在图 4-14 中,我们展示了一个应用于输入矩阵的 NiN 连接。如您所见,它将两个通道简化为一个通道。因此,NiN 或`1x1`卷积提供了一种廉价的方法来合并参数较少的额外非线性(Lin et al., 2013)。
### Residual Connections/Residual Block
### 残差连接/残差块
CNNs中最重要的趋势之一是Residual connection,它支持真正深层的网络(超过100层)。它也称为skip connection。如果将卷积函数表示为conv,则residual block的输出如下:
CNNs 中最重要的趋势之一是残差连接,它支持真正深层的网络(超过 100 层)。它也称为跳跃连接。如果将卷积函数表示为`conv`,则残差块的输出如下:
## Summary
## 总结
在本章中,您学习了两个基本的前馈架构:多层感知器(MLP;也称为“full-connected”网络)和卷积神经网络(CNN)。我们看到了MLPs在近似任何非线性函数方面的威力,并展示了MLPs在NLP中的应用,以及从姓氏中对国籍进行分类的应用。我们研究了mlp的一个主要缺点/限制—缺乏参数共享—并介绍了卷积网络架构作为一种可能的解决方案。最初为计算机视觉开发的CNNs,现已成为NLP的中流砥柱;主要是因为它们的高效实现和低内存需求。我们研究了卷积的不同变体——填充、扩展和扩展——以及它们如何转换输入空间。本章还专门讨论了卷积滤波器的输入和输出大小选择的实际问题。我们展示了卷积操作如何通过扩展姓氏分类示例来使用convnets来帮助捕获语言中的子结构信息。最后,我们讨论了与卷积网络设计相关的一些杂项但重要的主题:1)Pooling,2)Batchnorm,3)1x1卷积,4)residual connection。在现代CNN的设计中,经常可以看到像Inception架构(Szegedy et al., 2015)那样同时使用许多这样的技巧。在Inception架构中,小心地使用这些技巧可以让卷积网络深入到数百层,不仅精确,而且训练速度快。在第5章中,我们探讨了学习和使用表示表示离散单元的主题,例如使用嵌入式的单词、句子、文档和其他特征类型。
在本章中,您学习了两个基本的前馈架构:多层感知器(MLP;也称为“全连接”网络)和卷积神经网络(CNN)。我们看到了 MLPs 在近似任何非线性函数方面的威力,并展示了 MLPs 在 NLP 中的应用,以及从姓氏中对国籍进行分类的应用。我们研究了 mlp 的一个主要缺点/限制—缺乏参数共享—并介绍了卷积网络架构作为一种可能的解决方案。最初为计算机视觉开发的 CNNs,现已成为 NLP 的中流砥柱;主要是因为它们的高效实现和低内存需求。我们研究了卷积的不同变体——填充、扩展和扩展——以及它们如何转换输入空间。本章还专门讨论了卷积滤波器的输入和输出大小选择的实际问题。我们展示了卷积操作如何通过扩展姓氏分类示例来使用卷积网络来帮助捕获语言中的子结构信息。最后,我们讨论了与卷积网络设计相关的一些杂项但重要的主题:1)池化,2)批量规范化,3)`1x1`卷积,4)残差连接。在现代 CNN 的设计中,经常可以看到像 Inception 架构(Szegedy et al., 2015)那样同时使用许多这样的技巧。在 Inception 架构中,小心地使用这些技巧可以让卷积网络深入到数百层,不仅精确,而且训练速度快。在第 5 章中,我们探讨了学习和使用表示表示离散单元的主题,例如使用嵌入的单词、句子、文档和其他特征类型。

@@ -1,39 +0,41 @@

# Chapter 5.Embedding Words and Types
# 五、嵌入单词和类型
> 本文标题:[Natural-Language-Processing-with-PyTorch(五)](https://yifdu.github.io/2018/12/21/Natural-Language-Processing-with-PyTorch%EF%BC%88%E4%BA%94%EF%BC%89/)
> 本文标题:[Natural-Language-Processing-with-PyTorch(五)](https://yifdu.github.io/2018/12/21/Natural-Language-Processing-with-PyTorch%EF%BC%88%E4%BA%94%EF%BC%89/)
>
> 文章作者:[Yif Du](https://yifdu.github.io/ "访问 Yif Du 的个人博客")
> 文章作者:[Yif Du](https://yifdu.github.io/ "访问 Yif Du 的个人博客")
>
> 发布时间:2018年12月21日 - 12:12
> 发布时间:2018 年 12 月 21 日 - 12:12
>
> 最后更新:2018年12月28日 - 11:12
> 最后更新:2018 年 12 月 28 日 - 11:12
>
> 原始链接:[http://yifdu.github.io/2018/12/21/Natural-Language-Processing-with-PyTorch(五)/](https://yifdu.github.io/2018/12/21/Natural-Language-Processing-with-PyTorch%EF%BC%88%E4%BA%94%EF%BC%89/)
> 原始链接:[http://yifdu.github.io/2018/12/21/Natural-Language-Processing-with-PyTorch(五)/](https://yifdu.github.io/2018/12/21/Natural-Language-Processing-with-PyTorch%EF%BC%88%E4%BA%94%EF%BC%89/)
>
> 许可协议: [署名-非商业性使用-禁止演绎 4.0 国际](https://creativecommons.org/licenses/by-nc-nd/4.0/) 转载请保留原文链接及作者。
> 许可协议:[署名-非商业性使用-禁止演绎 4.0 国际](https://creativecommons.org/licenses/by-nc-nd/4.0/) 转载请保留原文链接及作者。
在实现自然语言处理(NLP)任务时,我们需要处理不同类型的离散类型。最明显的例子是单词。单词来自一个有限的集合(也就是词汇表)。其他离散类型的示例包括字符、部分语言标记、命名实体、命名实体类型、解析特性、产品目录中的项等等。本质上,当任何输入特征来自有限(或可数无限)集时,它都是离散类型。
在实现自然语言处理(NLP)任务时,我们需要处理不同类型的离散类型。最明显的例子是单词。单词来自一个有限的集合(也就是词汇表)。其他离散类型的示例包括字符、部分语言标记、命名实体、命名实体类型、解析特性、产品目录中的项等等。本质上,当任何输入特征来自有限(或可数无限)集时,它都是离散类型。
将离散类型(如单词)表示为密集向量是NLP中深度学习成功的核心。术语“representation learning”和“embedding”是指学习从一种离散类型到向量空间中的一点的映射。当离散类型为词时,密集向量表示称为词嵌入(word embedding)。我们在第2章中看到了基于计数的嵌入方法的例子,比如term - frequency-reverse-document-frequency (TF-IDF)。在本章中,我们主要研究基于学习或基于预测(Baroni et al., 2014)的嵌入方法,即通过最大化特定学习任务的目标来学习表征;例如,根据上下文预测一个单词。基于学习的嵌入方法由于其广泛的适用性和性能而在法理上受到限制。事实上,单词嵌入在NLP任务中的普遍性为它们赢得了“NLP的Sriracha”的称号,因为您可以在任何NLP任务中使用单词嵌入,并期望任务的性能得到改进。但是我们认为这种绰号是误导的,因为与Sriracha不同,嵌入式(embeddings)通常不是作为事后添加到模型中的,而是模型本身的基本组成部分。
将离散类型(如单词)表示为密集向量是 NLP 中深度学习成功的核心。术语“表示学习”和“嵌入”是指学习从一种离散类型到向量空间中的一点的映射。当离散类型为词时,密集向量表示称为词嵌入(word embedding)。我们在第 2 章中看到了基于计数的嵌入方法的例子,比如词频-逆文档频率(TF-IDF)。在本章中,我们主要研究基于学习或基于预测(Baroni et al., 2014)的嵌入方法,即通过最大化特定学习任务的目标来学习表征;例如,根据上下文预测一个单词。基于学习的嵌入方法由于其广泛的适用性和性能而在法理上受到限制。事实上,单词嵌入在 NLP 任务中的普遍性为它们赢得了“NLP 的 Sriracha”的称号,因为您可以在任何 NLP 任务中使用单词嵌入,并期望任务的性能得到改进。但是我们认为这种绰号是误导的,因为与 Sriracha 不同,嵌入(embeddings)通常不是作为事后添加到模型中的,而是模型本身的基本组成部分。
在这一章中,我们讨论与嵌入词相关的向量表示:嵌入词的方法,优化嵌入词的方法,用于监督和非监督语言任务,可视化嵌入词的方法,以及组合嵌入词的句子和文件的方法。但是,您必须记住,我们在这里描述的方法适用于任何离散类型。
## Why Learn Embeddings?
## 为什么要学习嵌入?
在前几章中,您看到了创建单词向量表示的传统方法。具体来说,您了解到可以使用onehot表示—与词汇表大小相同的长度的向量,除了单个位置以外,其他地方都是0,值为1表示特定的单词。此外,您还看到了计数表示——向量的长度与模型中唯一单词的数量相同,但是在向量中与句子中单词的频率相对应的位置上有计数。基于计数的表示也称为分布表示,因为它们的重要内容或意义是由向量中的多个维度表示的。分布表示具有悠久的历史(Firth, 1935),可以很好地用于许多机器学习和神经网络模型。这些表示,不是从数据中学习的,而是启发式构建的。
在前几章中,您看到了创建单词向量表示的传统方法。具体来说,您了解到可以使用单热表示—与词汇表大小相同的长度的向量,除了单个位置以外,其他地方都是 0,值为 1 表示特定的单词。此外,您还看到了计数表示——向量的长度与模型中唯一单词的数量相同,但是在向量中与句子中单词的频率相对应的位置上有计数。基于计数的表示也称为分布表示,因为它们的重要内容或意义是由向量中的多个维度表示的。分布表示具有悠久的历史(Firth, 1935),可以很好地用于许多机器学习和神经网络模型。这些表示,不是从数据中学习的,而是启发式构建的。
分布式表示的名称来源于这样一个事实:由于单词现在由低得多的密集向量表示(比如d = 100,而不是整个词汇的大小,可以是大约105到106或更高), 并且一个单词的含义和其他属性分布在这个密集向量的不同维度上。
分布式表示的名称来源于这样一个事实:由于单词现在由低得多的密集向量表示(比如`d = 100`,而不是整个词汇的大小,可以是大约 105 到 106 或更高), 并且一个单词的含义和其他属性分布在这个密集向量的不同维度上。
低维的学习密集表示比我们在前几章中看到的基于onehot和计数的向量有几个优点。首先,降低维度是计算效率。其次,基于计数的表示导致高维向量在多个维度上冗余地编码相似的信息,并且不共享统计强度。第三,输入中维数过高会导致机器学习和优化中的实际问题,这通常被称为“维数灾难”。“传统上,为了处理这个维度问题,我们使用了像PCA/SVD这样的降维方法,但有点讽刺的是,当维度以百万的顺序排列时(典型的NLP情况),这些方法不能很好地进行缩放。”第四,从特定于任务的数据中学习(或微调)的表示形式最适合手头的任务。使用TF-IDF等启发式方法或SVD等低维方法,不清楚嵌入方法的优化目标是否与任务相关。
低维的学习密集表示比我们在前几章中看到的基于单热和计数的向量有几个优点。首先,降低维度是计算效率。其次,基于计数的表示导致高维向量在多个维度上冗余地编码相似的信息,并且不共享统计强度。第三,输入中维数过高会导致机器学习和优化中的实际问题,这通常被称为“维数灾难”。“传统上,为了处理这个维度问题,我们使用了像 PCA/SVD 这样的降维方法,但有点讽刺的是,当维度以百万的顺序排列时(典型的 NLP 情况),这些方法不能很好地进行缩放。”第四,从特定于任务的数据中学习(或微调)的表示形式最适合手头的任务。使用 TF-IDF 等启发式方法或 SVD 等低维方法,不清楚嵌入方法的优化目标是否与任务相关。
### Efficiency of Embeddings
### 嵌入的有效性
为了理解嵌入是如何工作的,让我们看一个用onehot向量乘以线性层中的权重矩阵的例子,如图5-1所示。在第3章和第4章中,onehot向量的大小与词汇表相同。这个向量被称为“one-hot”,因为它在索引中有一个1,表示存在一个特定的单词。 ![onehot](img/cabb9e708a2dd06f3ebc33ec3d1e8702.jpg "图5-1\. 一个矩阵乘法的例子使用onehot编码向量和一个线性层的权矩阵。因为onehot是0和1,所以1的位置将作为矩阵乘法的选择器。这是通过使用权重矩阵和结果向量中的着色模式直观地显示的。尽管功能强大,但这种查找方法在计算上代价高昂且效率低下,因为onehot向量将权重矩阵中的每个数字相乘,并计算每一行的和。")
为了理解嵌入是如何工作的,让我们看一个用单热向量乘以线性层中的权重矩阵的例子,如图 5-1 所示。在第 3 章和第 4 章中,单热向量的大小与词汇表相同。这个向量被称为“单热”,因为它在索引中有一个 1,表示存在一个特定的单词。
根据定义,接受这个onehot向量作为输入的线性层的权值矩阵必须与这个onehot向量的大小具有相同的行数。当您执行矩阵乘法时,如图5-1所示,结果向量实际上只是选择了由非零项指示的行。从这个观察结果中,我们可以跳过乘法步骤,直接使用整数作为检索所选行的索引。
![onehot](img/cabb9e708a2dd06f3ebc33ec3d1e8702.jpg)
关于嵌入式效率的最后一点注意事项是,尽管图5-1中的示例显示的权重矩阵与传入的onehot向量具有相同的维度,但情况并非总是如此。实际上,嵌入通常用于在低维空间中表示单词,而不是使用一个onehot向量或基于计数的表示。在研究文献中,嵌入的典型尺寸从25维到500维不等,准确的选择可以归结为您必须节省的图形处理器单元(GPU)内存。
根据定义,接受这个单热向量作为输入的线性层的权值矩阵必须与这个单热向量的大小具有相同的行数。当您执行矩阵乘法时,如图 5-1 所示,结果向量实际上只是选择了由非零项指示的行。从这个观察结果中,我们可以跳过乘法步骤,直接使用整数作为检索所选行的索引。
### Approaches to Learning Word Embeddings
关于嵌入效率的最后一点注意事项是,尽管图 5-1 中的示例显示的权重矩阵与传入的单热向量具有相同的维度,但情况并非总是如此。实际上,嵌入通常用于在低维空间中表示单词,而不是使用一个单热向量或基于计数的表示。在研究文献中,嵌入的典型尺寸从 25 维到 500 维不等,准确的选择可以归结为您必须节省的图形处理器单元(GPU)内存。
### 学习单词嵌入的方式
本章的目标不是教授特定的单词嵌入技术,而是理解什么是嵌入,如何以及在哪里使用它们,如何在模型中可靠地使用它们,以及理解它们的局限性。我们之所以这样做,是因为实践者很少发现自己处于需要编写嵌入训练算法的新单词的情况下。然而,在这一节中,我们将简要概述当前训练单词嵌入的方法。所有的单词嵌入方法都是用单词来训练的。但是是以一种监督的方式。这可以通过构造辅助监督任务来实现,在这些任务中,数据被隐式地标记,直观地认为,优化后用于解决辅助任务的表示形式将捕获文本语料库的许多统计和语言属性,以便普遍使用。以下是一些辅助任务的例子:

@@ -45,14 +47,16 @@

当然,这个列表是不完整的,辅助任务的选择取决于算法设计者的直觉和计算费用。例如Glove、Continuous Bag-of-Words、Skipgrams等等。我们参考Goldberg, 2017,第10章,但我们将简要研究CBOW模型。但是,对于大多数目的来说,使用预先训练好的单词嵌入并对它们进行微调就足够了。
当然,这个列表是不完整的,辅助任务的选择取决于算法设计者的直觉和计算费用。例如 Glove、连续词袋、Skipgrams 等等。我们参考 Goldberg, 2017,第 10 章,但我们将简要研究 CBOW 模型。但是,对于大多数目的来说,使用预先训练好的单词嵌入并对它们进行微调就足够了。
### The Practical Use of Pretrained Word Embeddings
### 预训练词嵌入的实践用法
本章的大部分内容,以及本书的后面部分,都涉及到使用经过预先训练的单词嵌入。使用前面描述的许多方法中的一种,可以免费下载和使用预先训练过的单词嵌入、在大型语料(类似于所有新闻、维基百科和通用爬行器)上进行训练的单词嵌入。本章的其余部分将展示如何有效地加载和查找这些嵌入,研究word embedding的一些属性,并展示在NLP任务中使用预先训练的嵌入的示例。
本章的大部分内容,以及本书的后面部分,都涉及到使用经过预先训练的单词嵌入。使用前面描述的许多方法中的一种,可以免费下载和使用预先训练过的单词嵌入、在大型语料(类似于所有新闻、维基百科和通用爬行器)上进行训练的单词嵌入。本章的其余部分将展示如何有效地加载和查找这些嵌入,研究词嵌入的一些属性,并展示在 NLP 任务中使用预先训练的嵌入的示例。
LOADING EMBEDDINGS Word Embedding已经变得非常流行和普及,您可以从原始的Word2Vec、Stanford ‘s GLoVE、Facebook的FastText和许多其他版本下载许多不同的版本。通常,嵌入将以以下格式出现:每行以嵌入的单词/类型开始,然后是一系列数字(即,向量表示)。这个序列的长度就是表示的维数(也就是嵌入维数)。嵌入维数通常是数百。令牌(token)类型的数量通常是词汇表的大小,以百万计。例如,这里是来自手套的狗和猫向量的前七个维度: dog: -1.242 -0.360 0.573 0.367 0.600 -0.189 1.273 … cat: -0.964 -0.610 0.674 0.351 0.413 -0.212 1.380 …
加载嵌入
为了有效地加载和处理嵌入,我们描述了一个实用工具类,即预训练。该类在内存中构建所有单词向量的索引,以方便使用近似的最近邻包进行快速查找和最近邻查询。这个类显示在示例5-1中的第一个输入中。
词嵌入已经变得非常流行和普及,您可以从原始的 Word2Vec、Stanford 的 GLoVE、Facebook 的 FastText 和许多其他版本下载许多不同的版本。通常,嵌入将以以下格式出现:每行以嵌入的单词/类型开始,然后是一系列数字(即,向量表示)。这个序列的长度就是表示的维数(也就是嵌入维数)。嵌入维数通常是数百。标记(token)类型的数量通常是词汇表的大小,以百万计。例如,这里是来自 GloVe 的狗和猫向量的前七个维度: `dog: -1.242 -0.360 0.573 0.367 0.600 -0.189 1.273 … cat: -0.964 -0.610 0.674 0.351 0.413 -0.212 1.380 …`
Example 5-1\. Using pretrained word embeddings
为了有效地加载和处理嵌入,我们描述了一个实用工具类,即预训练。该类在内存中构建所有单词向量的索引,以方便使用近似的最近邻包进行快速查找和最近邻查询。这个类显示在示例 5-1 中的第一个输入中。
示例 5-1:使用预训练词嵌入
```py

@@ -110,10 +114,12 @@ Input[0]

在这些例子中,我们使用Glove word embeddings。下载它们之后,可以使用PretrainedEmbeddings类进行实例化,如示例5-1中的第二个输入所示。
在这些例子中,我们使用 Glove 词嵌入。下载它们之后,可以使用`PretrainedEmbeddings`类进行实例化,如示例 5-1 中的第二个输入所示。
RELATIONSHIPS BETWEEN WORD EMBEDDINGS 词嵌入的核心特征是对句法和语义关系进行编码,这些句法和语义关系表现为词的使用规律。例如,人们谈论猫和狗的方式非常相似(讨论宠物、喂食等)。因此,它们的嵌入彼此之间的距离比它们与其他动物(如鸭子和大象)的距离要近得多。 我们可以从几个方面探讨嵌入词编码的语义关系。最流行的一种方法是类比任务(SAT等考试中常见的推理任务): Word1 : Word2 :: Word3 : **__**
## 词嵌入之间的关系
在这个任务中,你被提供了前三个单词,需要确定第四个单词与前两个单词之间的关系是一致的。使用嵌入词,我们可以对其进行空间编码。首先,我们从单词1中减去单词2。这个差异向量编码了Word1和Word2之间的关系。然后可以将这个差异添加到Word3中,从而生成一个接近第四个单词的向量,即空白符号所在的位置。使用这个结果向量对索引执行最近邻查询可以解决类比问题。计算这个的函数,如例5-2所示,完成了刚刚描述的工作:使用向量算法和近似的最近邻索引,完成类比。
词嵌入的核心特征是对句法和语义关系进行编码,这些句法和语义关系表现为词的使用规律。例如,人们谈论猫和狗的方式非常相似(讨论宠物、喂食等)。因此,它们的嵌入彼此之间的距离比它们与其他动物(如鸭子和大象)的距离要近得多。 我们可以从几个方面探讨嵌入词编码的语义关系。最流行的一种方法是类比任务(SAT 等考试中常见的推理任务): `Word1 : Word2 :: Word3 : __`
Example 5-2\. The analogy task using word embeddings
在这个任务中,你被提供了前三个单词,需要确定第四个单词与前两个单词之间的关系是一致的。使用嵌入词,我们可以对其进行空间编码。首先,我们从单词 1 中减去单词 2。这个差异向量编码了`Word1`和`Word2`之间的关系。然后可以将这个差异添加到`Word3`中,从而生成一个接近第四个单词的向量,即空白符号所在的位置。使用这个结果向量对索引执行最近邻查询可以解决类比问题。计算这个的函数,如例 5-2 所示,完成了刚刚描述的工作:使用向量算法和近似的最近邻索引,完成类比。
示例 5-2:使用词嵌入的类比任务
```py

@@ -184,4 +190,6 @@ Input[0]

有趣的是,简单的单词类比任务可以证明单词嵌入捕获了各种语义和语法关系,如示例5-3所示。 Example 5-3\. A set of linguistic relationships are encoded in vector analogy
有趣的是,简单的单词类比任务可以证明单词嵌入捕获了各种语义和语法关系,如示例 5-3 所示。
示例 5-3:一组语言关系以向量类比编码
```py

@@ -231,5 +239,5 @@ Input[0]

虽然这种关系似乎是系统的语言功能,事情可能变得棘手。如例5-4所示,由于单词向量只是基于共现,关系可能是错误的。
虽然这种关系似乎是系统的语言功能,事情可能变得棘手。如例 5-4 所示,由于单词向量只是基于共现,关系可能是错误的。
Example 5-4\. The analogy can fail on simple scales
示例 5-4:类比可能在简单的规模上失败

@@ -244,5 +252,5 @@ ```py

示例5-5说明了最常见的类比对之一是如何编码带有性别的角色。
示例 5-5 说明了最常见的类比对之一是如何编码带有性别的角色。
Example 5-5\. Gender encoded in vector analogy
示例 5-5:在向量类比中编码的性别

@@ -257,5 +265,5 @@ ```py

事实证明,区分语言规则和文化偏见是困难的。例如,医生并不是事实上的男性,护士也不是事实上的女性,但是这些长期存在于文化中的偏见被观察到作为语言的规律性,并被编入vector这个词中,如例5-6所示。
事实证明,区分语言规则和文化偏见是困难的。例如,医生并不是事实上的男性,护士也不是事实上的女性,但是这些长期存在于文化中的偏见被观察到作为语言的规律性,并被编入`vector`这个词中,如例 5-6 所示。
Example 5-6\. Cultural gender bias encoded in vector analogy
示例 5-6:编码在向量类比中的文化性别偏见

@@ -270,24 +278,28 @@ ```py

考虑到embedding在NLP应用程序中的流行程度和使用正在上升,您需要注意embedding中的偏差。去偏现有词嵌入在一个新的和令人兴奋的研究领域(Bolukbasi et al., 2016)。此外,我们建议您访问ethicsinnlp.org,以获取伦理与NLP交叉的最新结果。
考虑到嵌入在 NLP 应用程序中的流行程度和使用正在上升,您需要注意嵌入中的偏差。去偏现有词嵌入在一个新的和令人兴奋的研究领域(Bolukbasi et al., 2016)。此外,我们建议您访问 ethicsinnlp.org,以获取伦理与 NLP 交叉的最新结果。
## Example: Learning the Continuous Bag of Words Embeddings
## 示例:学习单词嵌入的连续词袋
在本例中,我们将介绍构建和学习通用嵌入词的最著名模型之一,即Word2Vec连续词包(CBOW)模型。在本节中,当我们提到“CBOW任务”或“CBOW分类任务”时,隐含的意思是我们构建分类任务的目的是为了学习CBOW嵌入。CBOW模型是一种多类分类任务,其表现为对单词文本进行扫描,创建单词的上下文窗口,从上下文窗口中删除中心单词,并将上下文窗口分类为丢失的单词。直觉上,你可以把它想象成一个填空任务。有一个句子缺了一个词,模特的工作就是找出那个词应该是什么。
在本例中,我们将介绍构建和学习通用嵌入词的最著名模型之一,即 Word2Vec 连续词包(CBOW)模型。在本节中,当我们提到“CBOW 任务”或“CBOW 分类任务”时,隐含的意思是我们构建分类任务的目的是为了学习 CBOW 嵌入。CBOW 模型是一种多类分类任务,其表现为对单词文本进行扫描,创建单词的上下文窗口,从上下文窗口中删除中心单词,并将上下文窗口分类为丢失的单词。直觉上,你可以把它想象成一个填空任务。有一个句子缺了一个词,模特的工作就是找出那个词应该是什么。
这个例子的目的是介绍nn.Embedding 层,封装嵌入矩阵(embedding matrix)的PyTorch模块。利用嵌入层,我们可以将令牌的整数ID映射到用于神经网络计算的向量上。当优化器更新模型权重以最小化损失时,它还更新向量的值。通过这个过程,模型将学习如何以最有效的方式嵌入单词。
这个例子的目的是介绍`nn.Embedding`层,封装嵌入矩阵(embedding matrix)的 PyTorch 模块。利用嵌入层,我们可以将标记的整数 ID 映射到用于神经网络计算的向量上。当优化器更新模型权重以最小化损失时,它还更新向量的值。通过这个过程,模型将学习如何以最有效的方式嵌入单词。
在本例的其余部分中,我们遵循标准的示例格式。在第一部分,我们介绍数据集,Mary Shelley’s Frankenstein。然后,我们讨论了从令牌到向量化小批处理的向量化pipline。然后,我们概述了CBOW分类模型以及如何使用嵌入层。接下来,我们将介绍训练程序;尽管如此,如果你已经连续阅读了这本书,在这一点上训练应该是相当常规的。最后,我们讨论了模型评估、模型推理以及如何检查模型。
在本例的其余部分中,我们遵循标准的示例格式。在第一部分,我们介绍数据集,玛丽·雪莱(Mary Shelley)的小说《弗兰肯斯坦》。然后,我们讨论了从标记到向量化小批量的向量化流水线。然后,我们概述了 CBOW 分类模型以及如何使用嵌入层。接下来,我们将介绍训练程序;尽管如此,如果你已经连续阅读了这本书,在这一点上训练应该是相当常规的。最后,我们讨论了模型评估、模型推理以及如何检查模型。
### Frankenstein Dataset
### 《弗兰肯斯坦》数据集
在本例中,我们将从玛丽·雪莱(Mary Shelley)的小说《弗兰肯斯坦》(Frankenstein)的数字化版本构建一个文本数据集,可以通过古登堡计划(Project Gutenberg)获得。本节介绍预处理过程,为这个文本数据集构建一个PyTorch数据集类,最后将数据集分解为训练、验证和测试集。
在本例中,我们将从玛丽·雪莱(Mary Shelley)的小说《弗兰肯斯坦》(Frankenstein)的数字化版本构建一个文本数据集,可以通过古登堡计划(Project Gutenberg)获得。本节介绍预处理过程,为这个文本数据集构建一个 PyTorch 数据集类,最后将数据集分解为训练、验证和测试集。
从Project Gutenberg分发的原始文本文件开始,预处理是最小的:我们使用NLTK的Punkt标记器将文本分割成不同的句子。然后,将每个句子转换为小写字母,并完全去掉标点符号。这种预处理允许我们稍后在空白中拆分字符串,以便检索令牌列表。这一预处理功能是从“示例:餐厅评论情绪分类”中重用的。 ![Example](img/10857949f566f1cdd16b43a53c4d40a0.jpg "图5-2\. CBOW任务:用左右语境预测一个单词。上下文窗口在两边的长度都是2。在文本上滑动窗口会产生许多“受监督”的示例,每个示例都有目标词(在中间)。长度不为2的窗口被适当地填充。例如,对于窗口3,考虑到上下文i pitied和my pity,CBOW分类器是用来预测frankenstein的。") 下一步是将数据集枚举为一系列窗口,以便对CBOW模型进行优化。为此,我们迭代每个句子中的令牌列表,并将它们分组到指定窗口大小的窗口中,如图5-2所示。
从古腾堡项目分发的原始文本文件开始,预处理是最小的:我们使用 NLTK 的 Punkt 标记器将文本分割成不同的句子。然后,将每个句子转换为小写字母,并完全去掉标点符号。这种预处理允许我们稍后在空白中拆分字符串,以便检索标记列表。这一预处理功能是从“示例:餐厅评论情绪分类”中重用的。
构建数据集的最后一步是将数据分割为三个集:培训集、验证集和测试集。回想一下,训练和验证集是在模型训练期间使用的:训练集用于更新参数,验证集用于度量模型的性能。测试集最多使用一次,以提供偏差较小的测量。在本例中(以及本书中的大多数示例中),我们使用了70%的训练集、15%的验证集和15%的测试集。
![Example](img/10857949f566f1cdd16b43a53c4d40a0.jpg)
windows和目标的结果数据集装载了一个panda DataFrame,并在CBOW Dataset类中建立了索引。示例5-7展示了**getitem**代码片段,该代码片段利用矢量化器将上下文(左右窗口)转换为矢量。目标——窗口中心的单词——使用词汇表转换为整数。
下一步是将数据集枚举为一系列窗口,以便对 CBOW 模型进行优化。为此,我们迭代每个句子中的标记列表,并将它们分组到指定窗口大小的窗口中,如图 5-2 所示。
Example 5-7\. Constructing a dataset class for the CBOW task
构建数据集的最后一步是将数据分割为三个集:训练集、验证集和测试集。回想一下,训练和验证集是在模型训练期间使用的:训练集用于更新参数,验证集用于度量模型的性能。测试集最多使用一次,以提供偏差较小的测量。在本例中(以及本书中的大多数示例中),我们使用了 70% 的训练集、15% 的验证集和 15% 的测试集。
窗口和目标的结果数据集装载了一个 panda `DataFrame`,并在`CBOWDataset`类中建立了索引。示例 5-7 展示了`__getitem__`代码片段,该代码片段利用向量化器将上下文(左右窗口)转换为向量。目标——窗口中心的单词——使用词汇表转换为整数。
示例 5-7:构造数据集类用于 CBOW 任务
```py

@@ -328,6 +340,8 @@ class CBOWDataset(Dataset):

### Vocabulary, Vectorizer, and DataLoader
### `Vocabulary`,`Vectorizer`和`DataLoader`
在CBOW分类任务中,从文本到向量化的迷你批处理的管道大部分都是标准的:词汇表和DataLoader功能都与“示例:餐厅评论分类情感”中的示例完全一样。然而,与我们在第3章和第4章中看到的矢量化器不同,矢量化器不构造onehot向量。相反,构造并返回一个表示上下文索引的整数向量。示例5-8给出了向量化函数的代码。 Example 5-8\. A Vectorizer for the CBOW data
在 CBOW 分类任务中,从文本到向量化的迷你批量的管道大部分都是标准的:词汇表和`DataLoader`功能都与“示例:餐厅评论分类情感”中的示例完全一样。然而,与我们在第 3 章和第 4 章中看到的向量化器不同,向量化器不构造单热向量。相反,构造并返回一个表示上下文索引的整数向量。示例 5-8 给出了向量化函数的代码。
示例 5-8:用于 CBOW 数据的向量化器
```py

@@ -359,9 +373,9 @@ class CBOWVectorizer(object):

### The CBOW Classifier
### CBOW 分类器
示例5-9中显示的CBOW分类器有三个基本步骤。首先,将表示上下文单词的索引与嵌入层一起使用,为上下文中的每个单词创建向量。其次,目标是以某种方式组合这些向量,使其能够捕获整个上下文。在这个例子中,我们对向量求和。然而,其他选项包括取最大值、平均值,甚至在顶部使用多层感知器(MLP)。第三,上下文向量与线性层一起用来计算预测向量。这个预测向量是整个词汇表的概率分布。预测向量中最大(最可能)的值表示目标词的可能预测——上下文中缺少的中心词。
示例 5-9 中显示的 CBOW 分类器有三个基本步骤。首先,将表示上下文单词的索引与嵌入层一起使用,为上下文中的每个单词创建向量。其次,目标是以某种方式组合这些向量,使其能够捕获整个上下文。在这个例子中,我们对向量求和。然而,其他选项包括取最大值、平均值,甚至在顶部使用多层感知器(MLP)。第三,上下文向量与线性层一起用来计算预测向量。这个预测向量是整个词汇表的概率分布。预测向量中最大(最可能)的值表示目标词的可能预测——上下文中缺少的中心词。
这里使用的嵌入层主要由两个数字参数化:嵌入的数量(词汇表的大小)和嵌入的大小(嵌入维度)。示例5-9中的代码片段中使用了第三个参数:padding_idx。对于像我们这样数据点长度可能不相同的情况,这个参数被用作嵌入层的标记值。该层强制与该索引对应的向量及其梯度都为0。
这里使用的嵌入层主要由两个数字参数化:嵌入的数量(词汇表的大小)和嵌入的大小(嵌入维度)。示例 5-9 中的代码片段中使用了第三个参数:`padding_idx`。对于像我们这样数据点长度可能不相同的情况,这个参数被用作嵌入层的标记值。该层强制与该索引对应的向量及其梯度都为 0。
Example 5-9\. The CBOW Classifier model
示例 5-9:CBOW 分类器模型

@@ -407,6 +421,8 @@ ```py

### Training Routine
### 训练例程
在这个例子中,训练程序遵循我们在整本书中使用的标准。首先,初始化数据集、向量化器、模型、损失函数和优化器。然后,对数据集的训练和验证部分进行一定次数的迭代,优化训练部分的损失最小化,测量验证部分的进度。关于训练程序的更多细节,我们建议您参考“示例:餐厅评论的情绪分类”,在这里我们将详细介绍。示例5-10展示了我们用于训练的参数。 Example 5-10\. Arguments to the CBOW training script
在这个例子中,训练程序遵循我们在整本书中使用的标准。首先,初始化数据集、向量化器、模型、损失函数和优化器。然后,对数据集的训练和验证部分进行一定次数的迭代,优化训练部分的损失最小化,测量验证部分的进度。关于训练程序的更多细节,我们建议您参考“示例:餐厅评论的情绪分类”,在这里我们将详细介绍。示例 5-10 展示了我们用于训练的参数。
示例 5-10:CBOW 训练脚本的参数
```py

@@ -433,25 +449,25 @@ Input[0]

### Model Evaluation and Prediction
### 模型评估和预测
本例中的评估是为测试集中的每个目标和上下文对从提供的单词上下文预测目标单词。正确分类的单词意味着模型正在学习从上下文预测单词。在本例中,该模型在测试集上达到了15%的目标词分类准确率。首先,本例中CBOW的构建旨在说明如何构建通用嵌入。因此,原始实现的许多特性被忽略了,因为它们增加了不必要的复杂性,但对于优化性能却是必要的。第二,我们使用的数据集是微不足道的——一本大约7万字的书不足以在从零开始训练时识别出许多规律。相比之下,最先进Embedding技术通常是在文本为tb的数据集上进行训练。
本例中的评估是为测试集中的每个目标和上下文对从提供的单词上下文预测目标单词。正确分类的单词意味着模型正在学习从上下文预测单词。在本例中,该模型在测试集上达到了 15% 的目标词分类准确率。首先,本例中 CBOW 的构建旨在说明如何构建通用嵌入。因此,原始实现的许多特性被忽略了,因为它们增加了不必要的复杂性,但对于优化性能却是必要的。第二,我们使用的数据集是微不足道的——一本大约 7 万字的书不足以在从零开始训练时识别出许多规律。相比之下,最先进嵌入技术通常是在文本为 tb 的数据集上进行训练。
在这个例子中,我们展示了如何使用PyTorch nn.Embedding层通过设置一个名为CBOW Classification的人工监督任务来从头开始训练嵌入。 在下一个示例中,我们将研究如何在一个语料库中进行预训练嵌入,如何使用它并对其进行微调以进行其他任务。 在机器学习中,使用在一个任务上训练的模型作为另一个任务的初始化器称为转移学习(transfer learning)。
在这个例子中,我们展示了如何使用 PyTorch `nn.Embedding`层通过设置一个名为`CBOWClassification`的人工监督任务来从头开始训练嵌入。 在下一个示例中,我们将研究如何在一个语料库中进行预训练嵌入,如何使用它并对其进行微调以进行其他任务。 在机器学习中,使用在一个任务上训练的模型作为另一个任务的初始化器称为迁移学习(transfer learning)。
## Example: Transfer Learning Using Pretrained Embeddings for Document Classification
## 示例:使用预训练嵌入用于文档分类的迁移学习
前面的示例使用一个嵌入层(embedding layer)做简单分类,这个例子构建在三个方面:首先,通过加载pretrained word embedding,然后微调这些pretrained嵌入整个新闻文章分类,最后用卷积神经网络(CNN)来捕获单词之间的空间关系。
前面的示例使用一个嵌入层(embedding layer)做简单分类,这个例子构建在三个方面:首先,通过加载预训练的词嵌入,然后微调这些预训练嵌入整个新闻文章分类,最后用卷积神经网络(CNN)来捕获单词之间的空间关系。
在这个例子中,我们使用AG News数据集。为了对AG News中的单词序列进行建模,我们引入了词汇表类的一个变体SequenceVocabulary,以捆绑一些对建模序列至关重要的标记。矢量化器演示了如何使用这个类。
在这个例子中,我们使用 AG News 数据集。为了对 AG News 中的单词序列进行建模,我们引入了词汇表类的一个变体`SequenceVocabulary`,以捆绑一些对建模序列至关重要的标记。向量化器演示了如何使用这个类。
在描述了数据集以及向量化的小批处理是如何构建的之后,我们将逐步将预先训练好的单词向量加载到一个Embedding层中,并演示如何根据我们的设置对它们进行定制。然后,将预训练的Embedding层与“用CNN对姓氏进行分类的例子”中使用的CNN结合起来。为了将模型的复杂性扩大到更实际的结构,我们还确定了使用dropout作为正则化技术的地方。给出了模型描述,然后讨论了训练过程。您可能不会感到惊讶的是,与第4章和本章中的前两个示例相比,训练例程几乎没有什么变化。最后,通过在测试集上对模型进行评价并对结果进行讨论,得出了算例。
在描述了数据集以及向量化的小批量是如何构建的之后,我们将逐步将预先训练好的单词向量加载到一个嵌入层中,并演示如何根据我们的设置对它们进行定制。然后,将预训练的嵌入层与“用 CNN 对姓氏进行分类的例子”中使用的 CNN 结合起来。为了将模型的复杂性扩大到更实际的结构,我们还确定了使用丢弃作为正则化技术的地方。给出了模型描述,然后讨论了训练过程。您可能不会感到惊讶的是,与第 4 章和本章中的前两个示例相比,训练例程几乎没有什么变化。最后,通过在测试集上对模型进行评价并对结果进行讨论,得出了算例。
### The AG News Dataset
### AG 新闻数据集
AG news数据集是由学者们在2005年为实验数据挖掘和信息提取方法而收集的100多万篇新闻文章的集合。这个例子的目的是说明预先训练的词嵌入在文本分类中的有效性。在本例中,我们使用精简版的120,000篇新闻文章,这些文章平均分为四类:体育、科学/技术、世界和商业。除了精简数据集之外,我们还将文章标题作为我们的观察重点,并创建多类分类任务来预测给定标题的类别。
AG news 数据集是由学者们在 2005 年为实验数据挖掘和信息提取方法而收集的 100 多万篇新闻文章的集合。这个例子的目的是说明预先训练的词嵌入在文本分类中的有效性。在本例中,我们使用精简版的 120,000 篇新闻文章,这些文章平均分为四类:体育、科学/技术、世界和商业。除了精简数据集之外,我们还将文章标题作为我们的观察重点,并创建多类分类任务来预测给定标题的类别。
和以前一样,我们通过删除标点符号、在标点符号周围添加空格(如逗号、撇号和句点)来预处理文本,并将文本转换为小写。另外,我们将数据集分解为训练、验证和测试集,首先通过类标签聚合数据点,然后将每个数据点分配给三个分割中的一个。通过这种方式,保证了跨分支的类分布是相同的。
和以前一样,我们通过删除标点符号、在标点符号周围添加空格(如逗号、撇号和句点)来预处理文本,并将文本转换为小写。另外,我们将数据集分解为训练、验证和测试集,首先通过类标签聚合数据点,然后将每个数据点分配给三个分割中的一个。通过这种方式,保证了跨分支的类分布是相同的。
AG news dataset的**getitem**,第5-11所示的例子,是一个相当基本的公式你现在应该熟悉:检索的字符串表示模型的输入数据集从一个特定的行,Vectorizer矢量化,并搭配整数代表新闻类别(类标签)。
AG news 数据集的`__getitem__`,第 5-11 所示的例子,是一个相当基本的公式你现在应该熟悉:检索的字符串表示模型的输入数据集从一个特定的行,`Vectorizer`向量化,并搭配整数代表新闻类别(类标签)。
Example 5-11\. The AG News aataset’s__getitem__ method
示例 5-11:AG 新闻数据集的`__getitem__`方法

@@ -494,14 +510,16 @@ ```py

### Vocabulary, Vectorizer, and DataLoader
### `Vocabulary`,`Vectorizer`和`DataLoader`
在这个例子中,我们引入了SequenceVocabulary,它是标准Vocabulary类的子类,它捆绑了用于序列数据的四个特殊标记:UNK标记,MASK标记,BEGIN-SEQUENCE标记和END-SEQUENCE标记。 我们在第6章中更详细地描述了这些令牌,但简而言之,它们有三个不同的用途。 我们在第4章中看到的UNK标记(Unknown的缩写)允许模型学习稀有单词的表示,以便它可以容纳在测试时从未见过的单词。 当我们有可变长度的序列时,MASK令牌充当嵌入层和损失计算的标记。 最后,BEGIN-SEQUENCE和END-SEQUENCE标记给出了关于序列边界的神经网络提示。 图5-3显示了在更广泛的矢量化管道中使用这些特殊标记的结果。
在这个例子中,我们引入了`SequenceVocabulary`,它是标准`Vocabulary`类的子类,它捆绑了用于序列数据的四个特殊标记:`UNK`标记,`MASK`标记,`BEGIN-SEQUENCE`标记和`END-SEQUENCE`标记。 我们在第 6 章中更详细地描述了这些标记,但简而言之,它们有三个不同的用途。 我们在第 4 章中看到的`UNK`标记(Unknown 的缩写)允许模型学习稀有单词的表示,以便它可以容纳在测试时从未见过的单词。 当我们有可变长度的序列时,`MASK`标记充当嵌入层和损失计算的标记。 最后,`BEGIN-SEQUENCE`和`END-SEQUENCE`标记给出了关于序列边界的神经网络提示。 图 5-3 显示了在更广泛的向量化管道中使用这些特殊标记的结果。
![Vec](img/d76dd2ca233f00e8dfd8804b7a2f4d37.jpg) 图5-3\. 矢量化管道的一个简单示例从基本序列序列开始。Sequencevocabulary有四个特殊标记描述在文本。首先,它用于将单词映射到整数序列。因为单词“Jerry”不在序列表中,所以它被映射到&lt;unk&gt;整数。接下来,标记句子边界的特殊标记放在前面并附加到整数中。最后,整数用0填充到特定的长度,这允许数据集中的每个向量都是相同的长度。&lt;/unk&gt;
![Vec](img/d76dd2ca233f00e8dfd8804b7a2f4d37.jpg)
text-to-vectorized-minibatch管道中的第二个组件是Vectorizer,它实例化并封装了SequenceVocabulary的使用。 在这个例子中,Vectorizer遵循我们在第3-5节中演示的模式,通过对特定频率进行计数和阈值处理来限制词汇表中允许的总词集。 此操作的核心目的是通过消除噪声,低频字并限制内存模型的内存使用来提高模型的信号质量。
图 5-3\. 向量化管道的一个简单示例从基本序列序列开始。`Sequencevocabulary`有四个特殊标记描述在文本。首先,它用于将单词映射到整数序列。因为单词`Jerry`不在序列表中,所以它被映射到`unk`整数。接下来,标记句子边界的特殊标记放在前面并附加到整数中。最后,整数用 0 填充到特定的长度,这允许数据集中的每个向量都是相同的长度。
实例化后,Vectorizer的vectorize()方法将新闻标题作为输入,并返回与数据集中最长标题一样长的向量。重要的是,它有两个关键行为。第一个是它在本地存储最大序列长度。通常,数据集跟踪最大序列长度,并且在推断时,测试序列的长度被视为向量的长度。但是,因为我们有CNN模型,所以即使在推理时也要保持静态大小。示例5-11中的代码片段中显示的第二个键行为是它输出一个零填充的整数向量,它表示序列中的单词。此外,这个整数向量具有前缀为开头的BEGIN-SEQUENCE标记的整数,以及附加到向量末尾的END-SEQUENCE标记的整数。从分类器的角度来看,这些特殊标记提供了序列边界的证据,使其能够对边界附近的单词作出反应,而不是对靠近中心的单词作出反应。
文本到向量化的小批量管道中的第二个组件是`Vectorizer`,它实例化并封装了`SequenceVocabulary`的使用。 在这个例子中,`Vectorizer`遵循我们在第 3-5 节中演示的模式,通过对特定频率进行计数和阈值处理来限制词汇表中允许的总词集。 此操作的核心目的是通过消除噪声,低频字并限制内存模型的内存使用来提高模型的信号质量。
Example 5-12\. Implementing a Vectorizer for the AG News dataset
实例化后,`Vectorizer`的`vectorize()`方法将新闻标题作为输入,并返回与数据集中最长标题一样长的向量。重要的是,它有两个关键行为。第一个是它在本地存储最大序列长度。通常,数据集跟踪最大序列长度,并且在推断时,测试序列的长度被视为向量的长度。但是,因为我们有 CNN 模型,所以即使在推理时也要保持静态大小。示例 5-11 中的代码片段中显示的第二个键行为是它输出一个零填充的整数向量,它表示序列中的单词。此外,这个整数向量具有前缀为开头的`BEGIN-SEQUENCE`标记的整数,以及附加到向量末尾的`END-SEQUENCE`标记的整数。从分类器的角度来看,这些特殊标记提供了序列边界的证据,使其能够对边界附近的单词作出反应,而不是对靠近中心的单词作出反应。
示例 5-12:为 AG 新闻数据集实现向量化器
```py

@@ -560,9 +578,9 @@ class NewsVectorizer(object):

### The News Classifier
### 新闻分类器
在本章的前面,我们看到了如何从磁盘加载预训练嵌入,并使用Spotify的Annoy库中的近似最近邻数据结构有效地使用它们。 在该示例中,我们将向量相互比较以发现有趣的语言学见解。 但是,预训练的单词向量具有更有效的用途:我们可以使用它们来初始化嵌入层的嵌入矩阵。
在本章的前面,我们看到了如何从磁盘加载预训练嵌入,并使用 Spotify 的 Annoy 库中的近似最近邻数据结构有效地使用它们。 在该示例中,我们将向量相互比较以发现有趣的语言学见解。 但是,预训练的单词向量具有更有效的用途:我们可以使用它们来初始化嵌入层的嵌入矩阵。
使用单词嵌入(word embedding)作为初始嵌入矩阵的过程首先从磁盘加载嵌入,然后为数据中实际存在的单词选择正确的嵌入子集,然后最后设置嵌入层的权重矩阵 作为加载的子集。 在例5-13中演示了选择子集的第一步和第二步。 通常出现的一个问题是数据集中存在的单词,但不包含在预训练的GloVe嵌入中。 处理此问题的一种常用方法是使用PyTorch库中的初始化方法,例如Xavier Uniform方法,如例5-13所示(Glorot和Bengio,2010)。
使用单词嵌入(word embedding)作为初始嵌入矩阵的过程首先从磁盘加载嵌入,然后为数据中实际存在的单词选择正确的嵌入子集,然后最后设置嵌入层的权重矩阵 作为加载的子集。 在例 5-13 中演示了选择子集的第一步和第二步。 通常出现的一个问题是数据集中存在的单词,但不包含在预训练的 GloVe 嵌入中。 处理此问题的一种常用方法是使用 PyTorch 库中的初始化方法,例如 Xavier Uniform 方法,如例 5-13 所示(Glorot 和 Bengio,2010)。
Example 5-13\. Selecting a subset of the word embeddings based on the vocabulary
示例 5-13:基于词汇表选择词嵌入的子集

@@ -613,5 +631,5 @@ ```py

此示例中的NewsClassifier建立在第4-4节中的ConvNet分类器上,其中我们使用CNN对字符的onehot嵌入对姓氏进行分类。 具体来说,我们使用嵌入层,它将输入标记索引映射到矢量表示。 我们通过替换嵌入层的权重矩阵来使用预训练嵌入子集,如例5-14所示。 然后在前向方法中使用嵌入以从索引映射到向量。 除了嵌入层,一切都与第4-4节中的示例完全相同。
此示例中的`NewsClassifier`建立在第 4-4 节中的 ConvNet 分类器上,其中我们使用 CNN 对字符的单热嵌入对姓氏进行分类。 具体来说,我们使用嵌入层,它将输入标记索引映射到向量表示。 我们通过替换嵌入层的权重矩阵来使用预训练嵌入子集,如例 5-14 所示。 然后在前向方法中使用嵌入以从索引映射到向量。 除了嵌入层,一切都与第 4-4 节中的示例完全相同。
Example 5-14\. Implementing the News Classifier
示例 5-14:实现新闻分类器

@@ -701,7 +719,7 @@ ```py

### The Training Routine
### 训练例程
训练例程包括以下操作序列:实例化数据集; 实例化模型; 实例化损失函数; 实例化优化器; 迭代数据集的训练分区并更新模型参数,迭代数据集的验证分区并测量性能; 然后重复数据集迭代一定次数。 此时,您应该非常熟悉这个序列。 示例5-15中显示了此示例的超参数和其他训练参数。
训练例程包括以下操作序列:实例化数据集; 实例化模型; 实例化损失函数; 实例化优化器; 迭代数据集的训练分区并更新模型参数,迭代数据集的验证分区并测量性能; 然后重复数据集迭代一定次数。 此时,您应该非常熟悉这个序列。 示例 5-15 中显示了此示例的超参数和其他训练参数。
Example 5-15\. Arguments to the CNN news classifier using pretrained embeddings
示例 5-15:使用与训练嵌入的 CNN 新闻分类器的参数

@@ -733,14 +751,16 @@ ```py

### Model Evaluation and Prediction
### 模型评估和分类
在这个例子中,任务是将新闻标题分类到它们各自的类别。正如您在前面的示例中看到的,有两种方法可以理解模型执行任务的好坏:使用测试数据集的定量评估,以及亲自检查分类结果的定性评估。
### Evaluating on the test dataset
### 在测试集上评估
虽然这是你第一次看到的任务分类新闻头条,定量评价例程应该出现一模一样的每一评估程序:设置模型在eval模式关掉辍学和反向传播(使用classifier.eval()),然后遍历测试集以同样的方式作为训练集和验证集。在典型的环境中,您应该尝试不同的训练选项,当您满意时,您应该执行模型评估。我们会把这个留到练习结束。在这个测试集中你能得到的最终准确度是多少?请记住,在整个实验过程中,您只能使用测试集一次。
虽然这是你第一次看到的任务分类新闻头条,定量评价例程应该出现一模一样的每一评估程序:设置模型在求值模式关掉丢弃和反向传播(使用`classifier.eval()`),然后遍历测试集以同样的方式作为训练集和验证集。在典型的环境中,您应该尝试不同的训练选项,当您满意时,您应该执行模型评估。我们会把这个留到练习结束。在这个测试集中你能得到的最终准确度是多少?请记住,在整个实验过程中,您只能使用测试集一次。
Predicting the category of novel news headlines 训练分类器的目标是将其部署到生产环境中,以便能够对不可见的新闻标题执行推理或预测。要预测尚未处理和数据集中的新闻标题的类别,有几个步骤。第一种是对文本进行预处理,其方式类似于对训练中的数据进行预处理。对于推理,我们对输入使用与训练中相同的预处理函数。该预处理字符串使用训练期间使用的矢量化器向量化,并转换为PyTorch张量。接下来,对它应用分类器。计算预测向量的最大值以查找类别的名称。示例5-16给出了代码。
## 预测新的新闻头条的类别
Example 5-16\. Predicting with the trained model
训练分类器的目标是将其部署到生产环境中,以便能够对不可见的新闻标题执行推理或预测。要预测尚未处理和数据集中的新闻标题的类别,有几个步骤。第一种是对文本进行预处理,其方式类似于对训练中的数据进行预处理。对于推理,我们对输入使用与训练中相同的预处理函数。该预处理字符串使用训练期间使用的向量化器向量化,并转换为 PyTorch 张量。接下来,对它应用分类器。计算预测向量的最大值以查找类别的名称。示例 5-16 给出了代码。
示例 5-16:使用训练模型做出预测
```py

@@ -770,6 +790,6 @@ def predict_category(title, classifier, vectorizer, max_length):

## Summary
## 总结
在本章中,我们研究了单词嵌入,这是一种在空间中将单词(如单词)表示为固定维度向量的方式,使得向量之间的距离编码各种语言属性。 重要的是要记住,本章介绍的技术适用于任何离散单元,如句子,段落,文档,数据库记录等。 这使得嵌入技术对于深度学习是必不可少的,特别是在NLP中。 我们展示了如何以黑盒方式使用预训练嵌入。 我们简要讨论了直接从数据中学习这些嵌入的几种方法,包括连续词袋(CBOW)方法。 我们展示了如何在语言建模的背景下训练CBOW模型。 最后,我们通过一个在文档分类等任务中使用预训练embedding和微调embedding的示例。
在本章中,我们研究了单词嵌入,这是一种在空间中将单词(如单词)表示为固定维度向量的方式,使得向量之间的距离编码各种语言属性。 重要的是要记住,本章介绍的技术适用于任何离散单元,如句子,段落,文档,数据库记录等。 这使得嵌入技术对于深度学习是必不可少的,特别是在 NLP 中。 我们展示了如何以黑盒方式使用预训练嵌入。 我们简要讨论了直接从数据中学习这些嵌入的几种方法,包括连续词袋(CBOW)方法。 我们展示了如何在语言建模的背景下训练 CBOW 模型。 最后,我们通过一个在文档分类等任务中使用预训练嵌入和微调嵌入的示例。
不幸的是,本章由于缺乏空间而遗漏了许多重要的主题,例如消除词嵌入,建模上下文和一词多义。语言数据是世界的反映。社会偏见可以通过有偏见的训练语料库编码成模型。在一项研究中,最接近代词“她”的词是家庭主妇,护士,接待员,图书管理员,理发师等,而最接近“他”的词则是外科医生,保护者,哲学家,建筑师,金融家等等。 。对这种有偏见的嵌入进行过培训的模型可以继续做出可能产生不公平结果的决策。不再使用单词嵌入仍然是一个新生的领域,我们建议您阅读Bolukbasi等人.(2016年)和最近的论文引用了这一点。我们使用的嵌入词不依赖于上下文。例如,根据上下文,单词“play”可能有两个不同的含义,但这里讨论的所有嵌入(embedding)方法都会破坏这两个含义。最近的作品如Peters(2018)探索了以上下文为条件提供嵌入的方法。
不幸的是,本章由于缺乏空间而遗漏了许多重要的主题,例如消除词嵌入,建模上下文和一词多义。语言数据是世界的反映。社会偏见可以通过有偏见的训练语料库编码成模型。在一项研究中,最接近代词“她”的词是家庭主妇,护士,接待员,图书管理员,理发师等,而最接近“他”的词则是外科医生,保护者,哲学家,建筑师,金融家等等。 。对这种有偏见的嵌入进行过训练的模型可以继续做出可能产生不公平结果的决策。不再使用单词嵌入仍然是一个新生的领域,我们建议您阅读 Bolukbasi 等人.(2016 年)和最近的论文引用了这一点。我们使用的嵌入词不依赖于上下文。例如,根据上下文,单词`play`可能有两个不同的含义,但这里讨论的所有嵌入(embedding)方法都会破坏这两个含义。最近的作品如 Peters(2018)探索了以上下文为条件提供嵌入的方法。

@@ -1,46 +0,46 @@

# Chapter 6.自然语言处理 Sequence Modeling
# 六、自然语言处理的序列模型
> 本文标题:[Natural-Language-Processing-with-PyTorch(六)](https://yifdu.github.io/2018/12/24/Natural-Language-Processing-with-PyTorch%EF%BC%88%E5%85%AD%EF%BC%89/)
> 本文标题:[Natural-Language-Processing-with-PyTorch(六)](https://yifdu.github.io/2018/12/24/Natural-Language-Processing-with-PyTorch%EF%BC%88%E5%85%AD%EF%BC%89/)
>
> 文章作者:[Yif Du](https://yifdu.github.io/ "访问 Yif Du 的个人博客")
> 文章作者:[Yif Du](https://yifdu.github.io/ "访问 Yif Du 的个人博客")
>
> 发布时间:2018年12月24日 - 12:12
> 发布时间:2018 年 12 月 24 日 - 12:12
>
> 最后更新:2018年12月28日 - 11:12
> 最后更新:2018 年 12 月 28 日 - 11:12
>
> 原始链接:[http://yifdu.github.io/2018/12/24/Natural-Language-Processing-with-PyTorch(六)/](https://yifdu.github.io/2018/12/24/Natural-Language-Processing-with-PyTorch%EF%BC%88%E5%85%AD%EF%BC%89/)
> 原始链接:[http://yifdu.github.io/2018/12/24/Natural-Language-Processing-with-PyTorch(六)/](https://yifdu.github.io/2018/12/24/Natural-Language-Processing-with-PyTorch%EF%BC%88%E5%85%AD%EF%BC%89/)
>
> 许可协议: [署名-非商业性使用-禁止演绎 4.0 国际](https://creativecommons.org/licenses/by-nc-nd/4.0/) 转载请保留原文链接及作者。
> 许可协议:[署名-非商业性使用-禁止演绎 4.0 国际](https://creativecommons.org/licenses/by-nc-nd/4.0/) 转载请保留原文链接及作者。
序列是项目的有序集合。传统的机器学习假设数据点是独立的、相同分布的(IID),但在许多情况下,如语言、语音和时间序列数据,一个数据项取决于它之前或之后的数据项。这种数据也称为序列数据。在人类语言中,顺序信息无处不在。例如,语音可以被看作是音素的基本单元序列。在像英语这样的语言中,句子中的单词不是随意的。他们可能会被它之前或之后的词所束缚。例如,在英语中,介词“of”后面可能跟着冠词“the”;例如,“The lion is the king of the jungle.”。例如,在许多语言中,包括英语,动词的数量必须与句子主语的数量一致。这里有一个例子: The book is on the table The books are on the table. 有时这些依赖项或约束可以是任意长的。例如: The book that I got yesterday is on the table. The books read by the second grade children are shelved in the lower rack. 简而言之,理解序列对于理解人类语言至关重要。在前几章中,我们介绍了前馈神经网络,如多层感知器(MLPs)和卷积神经网络(CNNs),以及向量表示的能力。尽管使用这些技术可以完成大量的自然语言处理(NLP)任务,但正如我们将在本章以及第7章和第8章中学习的那样,它们并不能充分建模序列。
序列是项目的有序集合。传统的机器学习假设数据点是独立的、相同分布的(IID),但在许多情况下,如语言、语音和时间序列数据,一个数据项取决于它之前或之后的数据项。这种数据也称为序列数据。在人类语言中,顺序信息无处不在。例如,语音可以被看作是音素的基本单元序列。在像英语这样的语言中,句子中的单词不是随意的。他们可能会被它之前或之后的词所束缚。例如,在英语中,介词`of`后面可能跟着冠词`the`;例如,`The lion is the king of the jungle.`。例如,在许多语言中,包括英语,动词的数量必须与句子主语的数量一致。这里有一个例子:`The book is on the table The books are on the table.`。有时这些依赖项或约束可以是任意长的。例如: `The book that I got yesterday is on the table. The books read by the second grade children are shelved in the lower rack.`。 简而言之,理解序列对于理解人类语言至关重要。在前几章中,我们介绍了前馈神经网络,如多层感知器(MLPs)和卷积神经网络(CNNs),以及向量表示的能力。尽管使用这些技术可以完成大量的自然语言处理(NLP)任务,但正如我们将在本章以及第 7 章和第 8 章中学习的那样,它们并不能充分建模序列。
传统的方法,模型序列在NLP使用隐马尔科夫模型,条件随机场,和其他类型的概率图形模型,虽然没有讨论在这本书仍然是相关的。我们邀请您(Koller and Friedman, 2009)。
传统的方法,模型序列在 NLP 使用隐马尔科夫模型,条件随机场,和其他类型的概率图形模型,虽然没有讨论在这本书仍然是相关的。我们邀请您(Koller and Friedman, 2009)。
在深度学习中,建模序列涉及到维护隐藏的“状态信息”或隐藏状态。当序列中的每个条目被匹配时——例如,当一个句子中的每个单词被模型看到时——隐藏状态就会被更新。因此,隐藏状态(通常是一个向量)封装了到目前为止序列所看到的一切。这个隐藏的状态向量,也称为序列表示,可以根据我们要解决的任务以无数种方式在许多序列建模任务中使用,从对序列进行分类到预测序列。在本章中,我们将研究序列数据的分类,但是第7章将介绍如何使用序列模型来生成序列。
在深度学习中,建模序列涉及到维护隐藏的“状态信息”或隐藏状态。当序列中的每个条目被匹配时——例如,当一个句子中的每个单词被模型看到时——隐藏状态就会被更新。因此,隐藏状态(通常是一个向量)封装了到目前为止序列所看到的一切。这个隐藏的状态向量,也称为序列表示,可以根据我们要解决的任务以无数种方式在许多序列建模任务中使用,从对序列进行分类到预测序列。在本章中,我们将研究序列数据的分类,但是第 7 章将介绍如何使用序列模型来生成序列。
我们首先介绍最基本的神经网络序列模型:递归神经网络。在此基础上,给出了分类设置中递归神经网络的端到端实例。具体来说,您将看到一个基于字符的RNN来将姓氏分类到它们各自的国籍。姓氏示例表明序列模型可以捕获语言中的正字法(子词)模式。这个示例的开发方式使读者能够将模型应用于其他情况,包括建模文本序列,其中数据项是单词而不是字符。
我们首先介绍最基本的神经网络序列模型:循环神经网络。在此基础上,给出了分类设置中循环神经网络的端到端实例。具体来说,您将看到一个基于字符的 RNN 来将姓氏分类到它们各自的国籍。姓氏示例表明序列模型可以捕获语言中的正字法(子词)模式。这个示例的开发方式使读者能够将模型应用于其他情况,包括建模文本序列,其中数据项是单词而不是字符。
## Introduction to Recurrent Neural Networks
## 循环神经网络简介
递归神经网络(RNNs)的目的是建立张量序列的模型。rnn和前馈网络一样,是一类模型。RNN家族中有几个不同的成员,但在本章中,我们只讨论最基本的形式,有时称为Elman RNN。递归网络(基本的Elman形式和第7章中概述的更复杂的形式)的目标是学习序列的表示。这是通过维护一个隐藏的状态向量来实现的,它捕获了序列的当前状态。隐藏状态向量由当前输入向量和前一个隐藏状态向量计算得到。这些关系如图6-1所示,图6-1显示了计算依赖项的函数(左)视图和“展开”(右)视图。
循环神经网络(RNNs)的目的是建立张量序列的模型。rnn 和前馈网络一样,是一类模型。RNN 家族中有几个不同的成员,但在本章中,我们只讨论最基本的形式,有时称为 Elman RNN。循环网络(基本的 Elman 形式和第 7 章中概述的更复杂的形式)的目标是学习序列的表示。这是通过维护一个隐藏的状态向量来实现的,它捕获了序列的当前状态。隐藏状态向量由当前输入向量和前一个隐藏状态向量计算得到。这些关系如图 6-1 所示,图 6-1 显示了计算依赖项的函数(左)视图和“展开”(右)视图。
![pic1](pic1.png “图6 - 1。(左)Elman RNN的函数视图将递归关系显示为隐藏向量的反馈循环。(右)“展开”视图可以清楚地显示计算关系,因为每个时间步的隐藏向量依赖于该时间步的输入和前一个时间步的隐藏向量。
(左)Elman RNN 的函数视图将递归关系显示为隐藏向量的反馈循环。(右)“展开”视图可以清楚地显示计算关系,因为每个时间步的隐藏向量依赖于该时间步的输入和前一个时间步的隐藏向量。
在每次步骤中使用相同的权重将输入转换为输出是参数共享的另一个例子。在第4章中,我们看到了CNNs如何跨空间共享参数。CNNs使用称为内核的参数来计算来自输入数据子区域的输出。卷积核在输入端平移,从每一个可能的位置计算输出,以学习平移不变性。与此相反,rnn通过依赖一个隐藏的状态向量来捕获序列的状态,从而使用相同的参数来计算每一步的输出。通过这种方式,rnn的目标是通过计算给定的隐藏状态向量和输入向量的任何输出来学习序列不变性。你可以想象一个RNN跨时间共享参数,一个CNN跨空间共享参数。
在每次步骤中使用相同的权重将输入转换为输出是参数共享的另一个例子。在第 4 章中,我们看到了 CNNs 如何跨空间共享参数。CNNs 使用称为内核的参数来计算来自输入数据子区域的输出。卷积核在输入端平移,从每一个可能的位置计算输出,以学习平移不变性。与此相反,rnn 通过依赖一个隐藏的状态向量来捕获序列的状态,从而使用相同的参数来计算每一步的输出。通过这种方式,rnn 的目标是通过计算给定的隐藏状态向量和输入向量的任何输出来学习序列不变性。你可以想象一个 RNN 跨时间共享参数,一个 CNN 跨空间共享参数。
由于单词和句子可以是可变长度的,因此rnn或任何序列模型都应该能够处理可变长度的序列。一种可能的技术是人为地将序列限制在一个固定的长度。在本书中,我们使用另一种技术,称为掩蔽,通过利用序列长度的知识来处理可变长度序列。简而言之,屏蔽允许数据在某些输入不应计入梯度或最终输出时发出信号。PyTorch提供了处理称为packedsequence的可变长度序列的原语,这些序列从这些不太密集的序列中创建密集的张量。“例子:使用字符RNN对姓氏国籍进行分类”就是一个例子。
由于单词和句子可以是可变长度的,因此 rnn 或任何序列模型都应该能够处理可变长度的序列。一种可能的技术是人为地将序列限制在一个固定的长度。在本书中,我们使用另一种技术,称为掩蔽,通过利用序列长度的知识来处理可变长度序列。简而言之,屏蔽允许数据在某些输入不应计入梯度或最终输出时发出信号。PyTorch 提供了处理称为打包的序列的可变长度序列的原语,这些序列从这些不太密集的序列中创建密集的张量。“例子:使用字符 RNN 对姓氏国籍进行分类”就是一个例子。
在这两个图中,输出与隐藏向量相同。这并不总是正确的,但是在Elman RNN的例子中,隐藏的向量是被预测的。”)
在这两个图中,输出与隐藏向量相同。这并不总是正确的,但是在 Elman RNN 的例子中,隐藏的向量是被预测的。
### Implementing an Elman RNN
### 实现 Elman RNN
为了探究RNN的细节,让我们逐步了解Elman RNN的一个简单实现。PyTorch提供了许多有用的类和帮助函数来构建rnn。PyTorch RNN类实现了Elman RNN。在本章中,我们没有直接使用PyTorch的RNN类,而是使用RNNCell,它是对RNN的单个时间步的抽象,并以此构建RNN。我们这样做的目的是显式地向您展示RNN计算。示例6-1中显示的类ElmanRNN利用了RNNCell。RNNCell创建了“递归神经网络导论”中描述的输入隐藏和隐藏权重矩阵。对RNNCell的每次调用都接受一个输入向量矩阵和一个隐藏向量矩阵。它返回一个步骤产生的隐藏向量矩阵。
为了探究 RNN 的细节,让我们逐步了解 Elman RNN 的一个简单实现。PyTorch 提供了许多有用的类和帮助函数来构建 rnn。PyTorch RNN 类实现了 Elman RNN。在本章中,我们没有直接使用 PyTorch 的 RNN 类,而是使用`RNNCell`,它是对 RNN 的单个时间步的抽象,并以此构建 RNN。我们这样做的目的是显式地向您展示 RNN 计算。示例 6-1 中显示的类`ElmanRNN`利用了`RNNCell`。`RNNCell`创建了“循环神经网络导论”中描述的输入隐藏和隐藏权重矩阵。对`RNNCell`的每次调用都接受一个输入向量矩阵和一个隐藏向量矩阵。它返回一个步骤产生的隐藏向量矩阵。
除了控制RNN中的输入和隐藏大小超参数外,还有一个布尔参数用于指定批处理维度是否位于第0维度。这个标志也出现在所有PyTorch RNNs实现中。当设为真时,RNN交换输入张量的第0维和第1维。
除了控制 RNN 中的输入和隐藏大小超参数外,还有一个布尔参数用于指定批量维度是否位于第 0 维度。这个标志也出现在所有 PyTorch RNNs 实现中。当设为真时,RNN 交换输入张量的第 0 维和第 1 维。
在ElmanRNN中,forward()方法循环遍历输入张量,以计算每个时间步长的隐藏状态向量。注意,有一个用于指定初始隐藏状态的选项,但如果没有提供,则使用所有0的默认隐藏状态向量。当ElmanRNN循环遍历输入向量的长度时,它计算一个新的隐藏状态。这些隐藏状态被聚合并最终堆积起来。在返回之前,将再次检查batch_first标志。如果为真,则输出隐藏向量进行排列,以便批处理再次位于第0维上。
在`ElmanRNN`中,`forward()`方法循环遍历输入张量,以计算每个时间步长的隐藏状态向量。注意,有一个用于指定初始隐藏状态的选项,但如果没有提供,则使用所有 0 的默认隐藏状态向量。当`ElmanRNN`循环遍历输入向量的长度时,它计算一个新的隐藏状态。这些隐藏状态被聚合并最终堆积起来。在返回之前,将再次检查`batch_first`标志。如果为真,则输出隐藏向量进行排列,以便批量再次位于第 0 维上。
ElmanRNN的输出是一个三维张量——对于批处理维度上的每个数据点和每个时间步长,都有一个隐藏状态向量。根据手头的任务,可以以几种不同的方式使用这些隐藏向量。您可以使用它们的一种方法是将每个时间步骤分类为一些离散的选项集。该方法是通过调整RNN权值来跟踪每一步预测的相关信息。另外,您可以使用最后一个向量来对整个序列进行分类。这意味着RNN权重将被调整以跟踪对最终分类重要的信息。在本章中,我们只看到分类设置,但在接下来的两章中,我们将更深入地讨论逐步预测。
`ElmanRNN`的输出是一个三维张量——对于批量维度上的每个数据点和每个时间步长,都有一个隐藏状态向量。根据手头的任务,可以以几种不同的方式使用这些隐藏向量。您可以使用它们的一种方法是将每个时间步骤分类为一些离散的选项集。该方法是通过调整 RNN 权值来跟踪每一步预测的相关信息。另外,您可以使用最后一个向量来对整个序列进行分类。这意味着 RNN 权重将被调整以跟踪对最终分类重要的信息。在本章中,我们只看到分类设置,但在接下来的两章中,我们将更深入地讨论逐步预测。
Example 6-1\. An implementation of the Elman RNN using PyTorch’s RNNCell
示例 6-1:使用 PyTorch 的`RNNCell`的 Elman RNN 的实现示例

@@ -108,13 +108,13 @@ ```py

## Example: Classifying Surname Nationality using a Character RNN
## 示例:使用字符 RNN 分类姓氏国籍
现在我们已经概述了RNNs的基本属性,并逐步实现了ElmanRNN,现在让我们将它应用到任务中。我们将考虑的任务是第4章中的姓氏分类任务,在该任务中,字符序列(姓氏)被分类到起源的国籍。
现在我们已经概述了 RNNs 的基本属性,并逐步实现了`ElmanRNN`,现在让我们将它应用到任务中。我们将考虑的任务是第 4 章中的姓氏分类任务,在该任务中,字符序列(姓氏)被分类到起源的国籍。
### The Surnames Dataset
### 姓氏数据集
本例中的数据集是姓氏数据集,前面在第4章中介绍过。每个数据点由姓氏和相应的国籍表示。我们将避免重复数据集的细节,但是您应该参考“姓氏数据集”来刷新关于数据集的一些关键点。
本例中的数据集是姓氏数据集,前面在第 4 章中介绍过。每个数据点由姓氏和相应的国籍表示。我们将避免重复数据集的细节,但是您应该参考“姓氏数据集”来刷新关于数据集的一些关键点。
在本例中,就像“使用CNN对姓氏进行分类”一样,我们将每个姓氏视为字符序列。与往常一样,我们实现一个数据集类,如示例6-2所示,它返回向量化的姓氏和表示其国籍的整数。此外,返回的是序列的长度,它用于下游计算,以知道序列中的最终向量的位置。这是我们熟悉的步骤序列的一部分——实现数据集、向量化器和词汇表——在实际的训练开始之前。
在本例中,就像“使用 CNN 对姓氏进行分类”一样,我们将每个姓氏视为字符序列。与往常一样,我们实现一个数据集类,如示例 6-2 所示,它返回向量化的姓氏和表示其国籍的整数。此外,返回的是序列的长度,它用于下游计算,以知道序列中的最终向量的位置。这是我们熟悉的步骤序列的一部分——实现数据集、向量化器和词汇表——在实际的训练开始之前。
Example 6-2\. Implementing the Dataset for the Surname data
示例 6-2:为姓氏数据实现数据集

@@ -161,9 +161,9 @@ ```py

### The Vectorization Data Structures
### 向量化数据结构
向量化管道的第一阶段是将姓氏中的每个字符标记映射到唯一的整数。为了实现这一点,我们使用了SequenceVocabulary数据结构,这是我们在“示例:使用预先训练的嵌入式进行文档分类的传输学习”中首次介绍和描述的。回想一下,这个数据结构不仅将tweet中的单词映射到整数,而且还使用了四个特殊用途的令牌:UNK令牌、MASK令牌、BEGIN-SEQUENCE令牌和END-SEQUENCE令牌。前两个令牌对语言数据至关重要:UNK令牌用于输入中看不到的词汇表外令牌,而MASK令牌允许处理可变长度的输入。第二个标记为模型提供了句子边界特征,并分别作为前缀和追加到序列中。我们请您参阅“示例:使用预先训练的嵌入来进行文档分类的迁移学习”,以获得关于序列表的更长的描述。
向量化管道的第一阶段是将姓氏中的每个字符标记映射到唯一的整数。为了实现这一点,我们使用了`SequenceVocabulary`数据结构,这是我们在“示例:使用预先训练的嵌入进行文档分类的传输学习”中首次介绍和描述的。回想一下,这个数据结构不仅将推文中的单词映射到整数,而且还使用了四个特殊用途的标记:`UNK`标记、`MASK`标记、`BEGIN-SEQUENCE`标记和`END-SEQUENCE`标记。前两个标记对语言数据至关重要:`UNK`标记用于输入中看不到的词汇表外标记,而`MASK`标记允许处理可变长度的输入。第二个标记为模型提供了句子边界特征,并分别作为前缀和追加到序列中。我们请您参阅“示例:使用预先训练的嵌入来进行文档分类的迁移学习”,以获得关于序列表的更长的描述。
整个向量化过程由SurnameVectorizer管理,它使用序列evocabulary来管理姓氏字符和整数之间的映射。示例6-3展示了它的实现,看起来应该非常熟悉。在“示例:使用预训练嵌入进行文档分类的迁移学习”中,我们研究了如何将新闻文章的标题分类到特定的类别中,而向量化管道几乎是相同的。
整个向量化过程由`SurnameVectorizer`管理,它使用序列`evocabulary`来管理姓氏字符和整数之间的映射。示例 6-3 展示了它的实现,看起来应该非常熟悉。在“示例:使用预训练嵌入进行文档分类的迁移学习”中,我们研究了如何将新闻文章的标题分类到特定的类别中,而向量化管道几乎是相同的。
Example 6-3\. A vectorizer for surnames
示例 6-3:姓氏的向量化器

@@ -214,9 +214,9 @@ ```py

### The SurnameClassifier Model
### `SurnameClassifier`模型
SurnameClassifier模型由嵌入层、ElmanRNN和线性层组成。我们假设模型的输入是在它们被SequenceVocabulary映射到整数之后作为一组整数表示的令牌。模型首先使用嵌入层嵌入整数。然后,利用RNN计算序列表示向量。这些向量表示姓氏中每个字符的隐藏状态。由于目标是对每个姓氏进行分类,因此提取每个姓氏中最终字符位置对应的向量。考虑这个向量的一种方法是,最后的向量是传递整个序列输入的结果,因此是姓氏的汇总向量。这些汇总向量通过线性层来计算预测向量。预测向量用于训练损失,或者我们可以应用softmax函数来创建姓氏的概率分布
`SurnameClassifier`模型由嵌入层、`ElmanRNN`和线性层组成。我们假设模型的输入是在它们被`SequenceVocabulary`映射到整数之后作为一组整数表示的标记。模型首先使用嵌入层嵌入整数。然后,利用 RNN 计算序列表示向量。这些向量表示姓氏中每个字符的隐藏状态。由于目标是对每个姓氏进行分类,因此提取每个姓氏中最终字符位置对应的向量。考虑这个向量的一种方法是,最后的向量是传递整个序列输入的结果,因此是姓氏的汇总向量。这些汇总向量通过线性层来计算预测向量。预测向量用于训练损失,或者我们可以应用 softmax 函数来创建姓氏的概率分布
模型的参数是:嵌入的大小,嵌入的数量(即类的数量,以及RNN的隐藏状态大小。其中两个参数——嵌入的数量和类的数量——由数据决定。其余的超参数是嵌入的大小和隐藏状态的大小。尽管这些模型可以具有任何价值,但通常最好从一些小的、可以快速训练以验证模型是否有效的东西开始。
模型的参数是:嵌入的大小,嵌入的数量(即类的数量,以及 RNN 的隐藏状态大小。其中两个参数——嵌入的数量和类的数量——由数据决定。其余的超参数是嵌入的大小和隐藏状态的大小。尽管这些模型可以具有任何价值,但通常最好从一些小的、可以快速训练以验证模型是否有效的东西开始。
Example 6-4\. Implementing the Surname Classifier Model
示例 6-4:实现姓氏分类器模型

@@ -286,4 +286,6 @@ ```py

您将注意到,正向函数需要序列的长度。长度用于检索从RNN返回的带有名为column_gather函数的张量中每个序列的最终向量,如示例6-5所示。该函数迭代批处理行索引,并检索位于序列相应长度所指示位置的向量。 Example 6-5\. Retrieving the final vector in each sequence using column_gather
您将注意到,正向函数需要序列的长度。长度用于检索从 RNN 返回的带有名为`column_gather`函数的张量中每个序列的最终向量,如示例 6-5 所示。该函数迭代批量行索引,并检索位于序列相应长度所指示位置的向量。
示例 6-5:使用`column_gather`在每个序列中获取最终向量
```py

@@ -313,6 +315,8 @@ def column_gather(y_out, x_lengths):

### The Training Routine and Results
### 训练例程和结果
训练程序遵循标准公式。对于单个批数据,应用模型并计算预测向量。利用横熵损失和地面真值来计算损失值。使用损失值和优化器,计算梯度并使用这些梯度更新模型的权重。对训练数据中的每批重复此操作。对验证数据进行类似的处理,但是将模型设置为eval()模式,以防止在验证数据上反向传播。相反,验证数据仅用于对模型的执行情况给出不那么偏颇的感觉。这个例程在特定的时期重复执行。代码见补充资料我们鼓励您使用超参数来了解影响性能的因素以及影响程度,并将结果制成表格。我们还将为该任务编写合适的基线模型作为练习,让您完成。在“SurnameClassifier模型”中实现的模型是通用的,并不局限于字符。模型中的嵌入层可以映射出离散项序列中的任意离散项;例如,一个句子是一系列的单词。我们鼓励您在其他序列分类任务(如句子分类)中使用示例6-6中的代码。 Example 6-6\. Arguments to the RNN-based Surname Classifier
训练程序遵循标准公式。对于单个批数据,应用模型并计算预测向量。利用横熵损失和地面真值来计算损失值。使用损失值和优化器,计算梯度并使用这些梯度更新模型的权重。对训练数据中的每批重复此操作。对验证数据进行类似的处理,但是将模型设置为`eval()`模式,以防止在验证数据上反向传播。相反,验证数据仅用于对模型的执行情况给出不那么偏颇的感觉。这个例程在特定的时期重复执行。代码见补充资料我们鼓励您使用超参数来了解影响性能的因素以及影响程度,并将结果制成表格。我们还将为该任务编写合适的基线模型作为练习,让您完成。在“`SurnameClassifier`模型”中实现的模型是通用的,并不局限于字符。模型中的嵌入层可以映射出离散项序列中的任意离散项;例如,一个句子是一系列的单词。我们鼓励您在其他序列分类任务(如句子分类)中使用示例 6-6 中的代码。
示例 6-6:基于 RNN 的姓氏分类器的参数
```py

@@ -339,4 +343,4 @@ args = Namespace(

## Summary
## 总结
在本章中,您学习了用于对序列数据建模的递归神经网络,以及最简单的一种递归网络,即Elman RNNs。我们确定序列建模的目标是学习序列的表示(即向量)。根据任务的不同,可以以不同的方式使用这种学习过的表示。我们考虑了一个示例任务,涉及到将这种隐藏状态表示分类到许多类中的一个。姓氏分类任务展示了一个使用RNNs在子词级别捕获信息的示例。
在本章中,您学习了用于对序列数据建模的循环神经网络,以及最简单的一种循环网络,即 Elman RNNs。我们确定序列建模的目标是学习序列的表示(即向量)。根据任务的不同,可以以不同的方式使用这种学习过的表示。我们考虑了一个示例任务,涉及到将这种隐藏状态表示分类到许多类中的一个。姓氏分类任务展示了一个使用 RNNs 在子词级别捕获信息的示例。

@@ -1,61 +0,63 @@

# 7.自然语言处理的中间 Sequence Modeling
# 七、自然语言处理的进阶序列模型
> 本文标题:[Natural-Language-Processing-with-PyTorch(七)](https://yifdu.github.io/2018/12/26/Natural-Language-Processing-with-PyTorch%EF%BC%88%E4%B8%83%EF%BC%89/)
> 本文标题:[Natural-Language-Processing-with-PyTorch(七)](https://yifdu.github.io/2018/12/26/Natural-Language-Processing-with-PyTorch%EF%BC%88%E4%B8%83%EF%BC%89/)
>
> 文章作者:[Yif Du](https://yifdu.github.io/ "访问 Yif Du 的个人博客")
> 文章作者:[Yif Du](https://yifdu.github.io/ "访问 Yif Du 的个人博客")
>
> 发布时间:2018年12月26日 - 09:12
> 发布时间:2018 年 12 月 26 日 - 09:12
>
> 最后更新:2018年12月28日 - 11:12
> 最后更新:2018 年 12 月 28 日 - 11:12
>
> 原始链接:[http://yifdu.github.io/2018/12/26/Natural-Language-Processing-with-PyTorch(七)/](https://yifdu.github.io/2018/12/26/Natural-Language-Processing-with-PyTorch%EF%BC%88%E4%B8%83%EF%BC%89/)
> 原始链接:[http://yifdu.github.io/2018/12/26/Natural-Language-Processing-with-PyTorch(七)/](https://yifdu.github.io/2018/12/26/Natural-Language-Processing-with-PyTorch%EF%BC%88%E4%B8%83%EF%BC%89/)
>
> 许可协议: [署名-非商业性使用-禁止演绎 4.0 国际](https://creativecommons.org/licenses/by-nc-nd/4.0/) 转载请保留原文链接及作者。
> 许可协议:[署名-非商业性使用-禁止演绎 4.0 国际](https://creativecommons.org/licenses/by-nc-nd/4.0/) 转载请保留原文链接及作者。
本章的目标是序列预测。序列预测任务要求我们对序列中的每一项进行标记。这类任务在自然语言处理(NLP)中很常见。一些例子包括语言建模(参见图7-1),在语言建模中,我们在每个步骤中给定单词序列预测下一个单词;词性标注的一部分,对每个词的语法词性进行预测;命名实体识别,我们预测每个词是否属于一个命名实体,如人、位置、产品、组织;等等。有时,在NLP文献中,序列预测任务也被称为序列标记。
本章的目标是序列预测。序列预测任务要求我们对序列中的每一项进行标记。这类任务在自然语言处理(NLP)中很常见。一些例子包括语言建模(参见图 7-1),在语言建模中,我们在每个步骤中给定单词序列预测下一个单词;词性标注的一部分,对每个词的语法词性进行预测;命名实体识别,我们预测每个词是否属于一个命名实体,如人、位置、产品、组织;等等。有时,在 NLP 文献中,序列预测任务也被称为序列标记。
虽然理论上我们可以使用第六章中介绍的Elman递归神经网络(RNNs)进行序列预测任务,但是在实际应用中,它们并不能很好地捕捉到长期的依赖关系,并且表现不佳。在本章中,我们将花一些时间来理解为什么会这样,并学习一种新的RNN架构,称为门控网络。
虽然理论上我们可以使用第六章中介绍的 Elman 循环神经网络(RNNs)进行序列预测任务,但是在实际应用中,它们并不能很好地捕捉到长期的依赖关系,并且表现不佳。在本章中,我们将花一些时间来理解为什么会这样,并学习一种新的 RNN 架构,称为门控网络。
我们还介绍了自然语言生成作为序列预测应用的任务,并探讨了输出序列在某种程度上受到约束的条件生成。
## ")The Problem with Vanilla RNNs (or Elman RNNs)
## 原始 RNN(Elman RNNs)的问题
尽管在第6章中讨论的普通RNN/Elman-RNN非常适合于建模序列,但它有两个问题使其不适用于许多任务:无法保留用于长期预测的信息,以及梯度稳定性。为了理解这两个问题,回想一下,在它们的核心,rnn在每个时间步上使用前一个时间步的隐藏状态向量和当前时间步上的输入向量计算一个隐藏状态向量。正是这种核心计算使得RNN如此强大,但也产生了大量的数值问题。
尽管在第 6 章中讨论的普通 RNN/Elman-RNN 非常适合于建模序列,但它有两个问题使其不适用于许多任务:无法保留用于长期预测的信息,以及梯度稳定性。为了理解这两个问题,回想一下,在它们的核心,rnn 在每个时间步上使用前一个时间步的隐藏状态向量和当前时间步上的输入向量计算一个隐藏状态向量。正是这种核心计算使得 RNN 如此强大,但也产生了大量的数值问题。
Elman RNNs的第一个问题是很难记住长期的信息。例如,在第6章的RNN中,在每次步骤中,我们仅仅更新隐藏的状态向量,而不管它是否有意义。因此,RNN无法控制隐藏状态中保留的值和丢弃的值,而这些值完全由输入决定。直觉上,这是说不通的。我们希望RNN通过某种方式来决定更新是可选的,还是发生了更新,以及状态向量的多少和哪些部分,等等。
Elman RNNs 的第一个问题是很难记住长期的信息。例如,在第 6 章的 RNN 中,在每次步骤中,我们仅仅更新隐藏的状态向量,而不管它是否有意义。因此,RNN 无法控制隐藏状态中保留的值和丢弃的值,而这些值完全由输入决定。直觉上,这是说不通的。我们希望 RNN 通过某种方式来决定更新是可选的,还是发生了更新,以及状态向量的多少和哪些部分,等等。
Elman RNNs的第二个问题是,它们会导致梯度螺旋地失去控制,趋近于零或无穷大。不稳定的梯度,可以螺旋失控被称为消失梯度或爆炸梯度取决于方向梯度的绝对值正在收缩/增长。梯度绝对值非常大或非常小(小于1)都会使优化过程不稳定(Hochreiter et al., 2001;Pascanu et al., 2013)。
Elman RNNs 的第二个问题是,它们会导致梯度螺旋地失去控制,趋近于零或无穷大。不稳定的梯度,可以螺旋失控被称为消失梯度或爆炸梯度取决于方向梯度的绝对值正在收缩/增长。梯度绝对值非常大或非常小(小于 1)都会使优化过程不稳定(Hochreiter et al., 2001;Pascanu et al., 2013)。
在一般的神经网络中,有解决这些梯度问题的方法,如使用校正的线性单元(relu)、梯度裁剪和小心初始化。但是没有一种解决方案能像门控技术那样可靠地工作。
在一般的神经网络中,有解决这些梯度问题的方法,如使用校正的线性单元(relu)、梯度裁剪和小心初始化。但是没有一种解决方案能像门控技术那样可靠地工作。
## Gating as a Solution to a Vanilla RNN’s Challenges
## 原始 RNN 的挑战的门控解决方案
为了直观地理解门控,假设您添加了两个量,a和b,但是您想控制b放入和的多少。数学上,你可以把a + b的和改写成 **a+λb** λ是一个值在0和1之间。如果λ= 0,没有贡献从b如果λ= 1,b完全贡献。这种方式看,你可以解释λ充当一个“开关”或“门”控制的b进入之和。这就是门控机制背后的直觉。现在,让我们重新访问Elman RNN,看看如何将门控与普通的RNN合并以进行条件更新。如果前面的隐藏状态是 h_[t−1] 和当前输入 x[t], Elman RNN的周期性更新看起来像
为了直观地理解门控,假设您添加了两个量,`a`和`b`,但是您想控制`b`放入和的多少。数学上,你可以把`a + b`的和改写成`a+λb`,`λ`是一个值在 0 和 1 之间。如果`λ = 0`,`b`没有贡献,如果`λ = 1`,`b`完全贡献。这种方式看,你可以解释λ充当一个“开关”或“门”控制的`b`进入之和。这就是门控机制背后的直觉。现在,让我们重新访问 Elman RNN,看看如何将门控与普通的 RNN 合并以进行条件更新。如果前面的隐藏状态是`h[t−1]`和当前输入`x[t]`, Elman RNN 的周期性更新看起来像
其中F是RNN的递归计算。显然,这是一个无条件的和,并且有“Vanilla RNNs(或Elman RNNs)的问题”中描述的缺点。现在想象一下,替代常数,如果前面的例子的λ是一个函数之前的隐藏状态向量ht−1和当前输入xt,而且还产生所需的控制行为;也就是说,0到1之间的值。通过这个门控函数,我们的RNN更新方程如下:
其中`F`是 RNN 的递归计算。显然,这是一个无条件的和,并且有“原始 RNNs(或 Elman RNNs)的问题”中描述的缺点。现在想象一下,替代常数,如果前面的例子的λ是一个函数之前的隐藏状态向量`h[t−1]`和当前输入`x[t]`,而且还产生所需的控制行为;也就是说,0 到 1 之间的值。通过这个门控函数,我们的 RNN 更新方程如下:
现在就清楚函数λ控制当前输入的多少可以更新状态ht−1。进一步的函数λ是上下文相关的。这是所有门控网络的基本直觉。λ的函数通常是一个s形的函数,我们知道从第三章到产生一个值在0和1之间。
现在就清楚函数`λ`控制当前输入的多少可以更新状态`h[t−1]`。进一步的函数`λ`是上下文相关的。这是所有门控网络的基本直觉。`λ`的函数通常是一个 s 形的函数,我们知道从第三章到产生一个值在 0 和 1 之间。
在长短期记忆的情况下,这个基本的直觉是扩展仔细将不仅条件更新,而且还故意忘记之前的隐藏状态 h[t−1] 的值。这种“忘记”乘以发生前隐藏状态与另一个函数μ值ht−1,还产生值在0和1之间,取决于当前的输入:
在长短期记忆的情况下,这个基本的直觉是扩展仔细将不仅条件更新,而且还故意忘记之前的隐藏状态`h[t−1]`的值。这种“忘记”乘以发生前隐藏状态与另一个函数`μ`值`h[t−1]`,还产生值在 0 和 1 之间,取决于当前的输入:
您可能已经猜到,μ是另一个控制功能。在实际的LSTM描述中,这变得很复杂,因为门函数是参数化的,导致对未初始化的操作的复杂序列。但是,在掌握了本节的直观知识之后,如果您想深入了解LSTM的更新机制,现在就可以了。我们推荐Christopher Olah的经典文章。在本书中,我们将不涉及这些内容,因为这些细节对于LSTMs在NLP应用程序中的应用和使用并不是必需的。
您可能已经猜到,μ是另一个控制功能。在实际的 LSTM 描述中,这变得很复杂,因为门函数是参数化的,导致对未初始化的操作的复杂序列。但是,在掌握了本节的直观知识之后,如果您想深入了解 LSTM 的更新机制,现在就可以了。我们推荐 Christopher Olah 的经典文章。在本书中,我们将不涉及这些内容,因为这些细节对于 LSTMs 在 NLP 应用程序中的应用和使用并不是必需的。
LSTM只是RNN的许多门控变体之一。另一种越来越流行的门控变量是门控循环单元(GRU;Chung et al., 2015)。幸运的是,在PyTorch中,您可以简单地替换nn。RNN或神经网络。RNNCell nn。LSTM和神经网络。LSTMCell没有其他代码更改来切换到LSTM(为GRU做必要的修改)!
LSTM 只是 RNN 的许多门控变体之一。另一种越来越流行的门控变量是门控循环单元(GRU;Chung et al., 2015)。幸运的是,在 PyTorch 中,您可以简单地替换`nn`。RNN 或神经网络。`RNNCell nn`。LSTM 和神经网络。`LSTMCell`没有其他代码更改来切换到 LSTM(为 GRU 做必要的修改)!
门控机制是“普通RNNs(或Elman RNNs)问题”中列举的问题的有效解决方案。它不仅可以控制更新,还可以控制梯度问题,使训练相对容易。不再赘述,我们将使用两个示例来展示这些封闭体系结构的实际应用。
门控机制是“普通 RNNs(或 Elman RNNs)问题”中列举的问题的有效解决方案。它不仅可以控制更新,还可以控制梯度问题,使训练相对容易。不再赘述,我们将使用两个示例来展示这些封闭体系结构的实际应用。
## Example: A Character-RNN for Generating Surnames
## 示例:用于生成姓氏的字符 RNN
在本例中,我们将完成一个简单的序列预测任务:使用RNNs生成姓氏。在实践中,这意味着对于每个时间步骤,RNN都在计算姓氏中可能的字符集的概率分布。使用这些概率分布,我们可以优化网络来改进它的预测(假设我们知道应该预测哪些字符),也可以使用它们来生成全新的姓氏!
在本例中,我们将完成一个简单的序列预测任务:使用 RNNs 生成姓氏。在实践中,这意味着对于每个时间步骤,RNN 都在计算姓氏中可能的字符集的概率分布。使用这些概率分布,我们可以优化网络来改进它的预测(假设我们知道应该预测哪些字符),也可以使用它们来生成全新的姓氏!
虽然这个任务的数据集已经在前面的例子中使用过,看起来很熟悉,但是在构建用于序列预测的每个数据样本的方式上有一些不同。在描述了数据集和任务之后,概述了支持通过系统簿记进行序列预测的数据结构。
在描述了数据集、任务和支持数据结构之后,我们引入了两个生成姓氏的模型:无条件姓氏生成模型和条件姓氏生成模型。该模型在不了解姓氏的情况下,对姓氏序列进行预测。与此相反,条件模型利用特定的国籍嵌入作为RNN的初始隐藏状态,从而使模型对序列的预测产生偏差。
在描述了数据集、任务和支持数据结构之后,我们引入了两个生成姓氏的模型:无条件姓氏生成模型和条件姓氏生成模型。该模型在不了解姓氏的情况下,对姓氏序列进行预测。与此相反,条件模型利用特定的国籍嵌入作为 RNN 的初始隐藏状态,从而使模型对序列的预测产生偏差。
### The SurnamesDataset
### `SurnamesDataset`
姓氏数据集是姓氏及其来源国的集合,最早出现在“带有多层感知器的姓氏分类”中。到目前为止,该数据集已经被用于一个分类任务——给出一个新的姓氏,正确地将姓氏来自哪个国家。然而,在本例中,我们将展示如何使用数据集来训练一个模型,该模型可以为字符序列分配概率并生成新的序列。
SurnamesDataset类与前几章基本相同:我们使用panda DataFrame加载数据集,并构造了一个向量化器,它将令牌封装为模型和手边任务所需的整数映射。为了适应任务的不同,修改了SurnamesDataset.__getitem**()方法,以输出预测目标的整数序列,如示例7-1所示。该方法引用向量器来计算作为输入的整数序列(from_vector)和作为(to_vector)的整数序列。下一小节将描述向量化的实现。 Example 7-1\. The SurnamesDataset.**getitem__ for a sequence prediction task
`SurnamesDataset`类与前几章基本相同:我们使用 panda `DataFrame`加载数据集,并构造了一个向量化器,它将标记封装为模型和手边任务所需的整数映射。为了适应任务的不同,修改了`SurnamesDataset.__getitem__()`方法,以输出预测目标的整数序列,如示例 7-1 所示。该方法引用向量器来计算作为输入的整数序列(`from_vector`)和作为输出(`to_vector`)的整数序列。下一小节将描述向量化的实现。
示例 7-1:用于序列预测任务的`SurnamesDataset.__getitem__`方法
```py

@@ -98,15 +100,15 @@ class SurnameDataset(Dataset):

### The Vectorization Data Structures
### 向量化数据结构
与前面的示例一样,有三种主要的数据结构将每个姓氏的字符序列转换为其向量化形式:SequenceVocabulary将单个令牌映射到整数,SurnameVectorizer协调整数映射,DataLoader将SurnameVectorizer的结果分组为小批。由于DataLoader实现及其使用在本例中保持不变,我们将跳过其实现细节。
与前面的示例一样,有三种主要的数据结构将每个姓氏的字符序列转换为其向量化形式:`SequenceVocabulary`将单个标记映射到整数,`SurnameVectorizer`协调整数映射,`DataLoader`将`SurnameVectorizer`的结果分组为小批。由于`DataLoader`实现及其使用在本例中保持不变,我们将跳过其实现细节。
### SURNAMEVECTORIZER AND END-OF-SEQUENCE
### `SURNAMEVECTORIZER`和`END-OF-SEQUENCE`
对于序列预测任务,编写训练例程以期望在每个时间步骤中出现两个表示令牌观察和令牌目标的整数序列。通常,我们只想预测我们正在训练的序列,例如本例中的姓氏。这意味着我们只有一个令牌序列可以使用,并通过调整这个令牌序列来构造观察和目标。
对于序列预测任务,编写训练例程以期望在每个时间步骤中出现两个表示标记观察和标记目标的整数序列。通常,我们只想预测我们正在训练的序列,例如本例中的姓氏。这意味着我们只有一个标记序列可以使用,并通过调整这个标记序列来构造观察和目标。
为了将其转化为序列预测问题,使用SequenceVocabulary将每个令牌映射到其适当的索引。然后,序列的起始令牌索引begin_seq_index位于序列的开头,而序列的结束令牌索引end_seq_index位于序列的末尾。此时,每个数据点都是一系列索引,具有相同的第一个和最后一个索引。要创建训练例程所需的输入和输出序列,我们只需使用索引序列的两个切片:第一个切片包含除最后一个之外的所有令牌索引,第二个切片包含除第一个之外的所有令牌索引。当排列和配对在一起时,序列就是正确的输入-输出索引。
为了将其转化为序列预测问题,使用`SequenceVocabulary`将每个标记映射到其适当的索引。然后,序列的起始标记索引`begin_seq_index`位于序列的开头,而序列的结束标记索引`end_seq_index`位于序列的末尾。此时,每个数据点都是一系列索引,具有相同的第一个和最后一个索引。要创建训练例程所需的输入和输出序列,我们只需使用索引序列的两个切片:第一个切片包含除最后一个之外的所有标记索引,第二个切片包含除第一个之外的所有标记索引。当排列和配对在一起时,序列就是正确的输入-输出索引。
为了更明确,我们展示了SurnameVectorizer的代码。在示例7-2中向量化。第一步是将姓氏(字符串)映射到索引(表示这些字符的整数列表)。然后,用序列索引的开始和结束来包装索引:具体来说,begin_seq_index在索引之前,end_seq_index在索引之后。接下来,我们测试vector_length,它通常在运行时提供,但是代码的编写允许向量的任何长度。在训练期间,提供vector_length是很重要的,因为小批是由堆叠的向量表示构造的。如果向量的长度不同,它们不能堆放在一个矩阵中。在测试vector_length之后,创建两个向量:from_vector和to_vector。不包含最后一个索引的索引片放在from_vector中,不包含第一个索引的索引片放在to_vector中。每个向量的剩余位置都填充了mask_index。将序列填充(或填充)到右边是很重要的,因为空位置将改变输出向量,我们希望这些变化发生在序列被看到之后。
为了更明确,我们展示了`SurnameVectorizer`的代码。在示例 7-2 中向量化。第一步是将姓氏(字符串)映射到索引(表示这些字符的整数列表)。然后,用序列索引的开始和结束来包装索引:具体来说,`begin_seq_index`在索引之前,`end_seq_index`在索引之后。接下来,我们测试`vector_length`,它通常在运行时提供,但是代码的编写允许向量的任何长度。在训练期间,提供`vector_length`是很重要的,因为小批是由堆叠的向量表示构造的。如果向量的长度不同,它们不能堆放在一个矩阵中。在测试`vector_length`之后,创建两个向量:`from_vector`和`to_vector`。不包含最后一个索引的索引片放在`from_vector`中,不包含第一个索引的索引片放在`to_vector`中。每个向量的剩余位置都填充了`mask_index`。将序列填充(或填充)到右边是很重要的,因为空位置将改变输出向量,我们希望这些变化发生在序列被看到之后。
Example 7-2\. The code for SurnameVectorizer.vectorize in a sequence prediction task
示例 7-2:序列预测任务中的`SurnameVectorizer.vectorize`代码区

@@ -167,13 +169,13 @@ ```py

### From the ElmanRNN to the GRU
### 从`ElmanRNN`到 GRU
在实践中,从普通的RNN转换到门控变体是非常容易的。在以下模型中,虽然我们使用GRU代替普通的RNN,但是使用LSTM也同样容易。为了使用GRU,我们实例化了torch.nn。GRU模块使用与第六章ElmanRNN相同的参数。
在实践中,从普通的 RNN 转换到门控变体是非常容易的。在以下模型中,虽然我们使用 GRU 代替普通的 RNN,但是使用 LSTM 也同样容易。为了使用 GRU,我们实例化了`torch.nn`。GRU 模块使用与第六章`ElmanRNN`相同的参数。
### Model 1: Unconditioned Surname Generation Model
### 模型 1:非条件姓氏生成模型
第一个模型是无条件的:它在生成姓氏之前不观察国籍。在实践中,非条件意味着GRU的计算不偏向任何国籍。在下一个例子(例子7-3)中,通过初始隐藏向量引入计算偏差。在这个例子中,我们使用一个全为0的向量,这样初始的隐藏状态向量就不会影响计算。 通常,SurnameGenerationModel嵌入字符索引,使用GRU计算其顺序状态,并使用线性层计算令牌预测的概率。更明确地说,非条件SurnameGenerationModel从初始化嵌入层、GRU和线性层开始。 与第6章的序列模型相似,该模型输入了一个整数矩阵。我们使用一个PyTorch嵌入实例char_embed将整数转换为一个三维张量(每个批处理项的向量序列)。这个张量传递给GRU, GRU计算每个序列中每个位置的状态向量。
第一个模型是无条件的:它在生成姓氏之前不观察国籍。在实践中,非条件意味着 GRU 的计算不偏向任何国籍。在下一个例子(例子 7-3)中,通过初始隐藏向量引入计算偏差。在这个例子中,我们使用一个全为 0 的向量,这样初始的隐藏状态向量就不会影响计算。 通常,`SurnameGenerationModel`嵌入字符索引,使用 GRU 计算其顺序状态,并使用线性层计算标记预测的概率。更明确地说,非条件`SurnameGenerationModel`从初始化嵌入层、GRU 和线性层开始。 与第 6 章的序列模型相似,该模型输入了一个整数矩阵。我们使用一个 PyTorch 嵌入实例`char_embed`将整数转换为一个三维张量(每个批量项的向量序列)。这个张量传递给 GRU, GRU 计算每个序列中每个位置的状态向量。
第六章的序列分类与本章序列预测的主要区别在于如何处理由RNN计算出的状态向量。在第6章中,我们为每个批处理索引检索一个向量,并使用这些向量执行预测。在这个例子中,我们将我们的三维张量重塑为一个二维张量(一个矩阵),以便行维表示每个样本(批处理和序列索引)。利用这个矩阵和线性层,我们计算每个样本的预测向量。我们通过将矩阵重新构造成一个三维张量来完成计算。由于排序信息是通过整形操作保存的,所以每个批和序列索引仍然处于相同的位置。我们需要整形的原因是因为线性层需要一个矩阵作为输入。
第六章的序列分类与本章序列预测的主要区别在于如何处理由 RNN 计算出的状态向量。在第 6 章中,我们为每个批量索引检索一个向量,并使用这些向量执行预测。在这个例子中,我们将我们的三维张量重塑为一个二维张量(一个矩阵),以便行维表示每个样本(批量和序列索引)。利用这个矩阵和线性层,我们计算每个样本的预测向量。我们通过将矩阵重新构造成一个三维张量来完成计算。由于排序信息是通过整形操作保存的,所以每个批和序列索引仍然处于相同的位置。我们需要整形的原因是因为线性层需要一个矩阵作为输入。
Example 7-3\. The unconditioned surname generation model
示例 7-3:非条件化的姓氏生成模型

@@ -238,8 +240,10 @@ ```py

### Model 2: Conditioned Surname Generation Model
### 模型 2:条件姓氏生成模型
第二个模型考虑了要生成的姓氏的国籍。在实践中,这意味着有某种机制允许模型对特定姓氏的行为进行偏差。在本例中,我们通过将每个国籍嵌入为隐藏状态大小的向量来参数化RNNs的初始隐藏状态。这意味着模型在调整模型参数的同时,也调整了嵌入矩阵中的值,从而使预测偏于对特定的国籍和姓氏的规律性更加敏感。例如,爱尔兰国籍向量偏向于起始序列“Mc”和“O”。
第二个模型考虑了要生成的姓氏的国籍。在实践中,这意味着有某种机制允许模型对特定姓氏的行为进行偏差。在本例中,我们通过将每个国籍嵌入为隐藏状态大小的向量来参数化 RNNs 的初始隐藏状态。这意味着模型在调整模型参数的同时,也调整了嵌入矩阵中的值,从而使预测偏于对特定的国籍和姓氏的规律性更加敏感。例如,爱尔兰国籍向量偏向于起始序列`Mc`和`O`。
例7-3显示了条件模型之间的差异。具体地说,引入额外的嵌入来将国籍索引映射到与RNN的隐藏层相同大小的向量。然后,在正向函数中嵌入民族指标,作为RNN的初始隐含层简单传入。虽然这是对第一个模型的一个非常简单的修改,但是它对于让RNN根据生成的国籍改变其行为有着深远的影响。 Example 7-4\. The conditioned surname generation model
例 7-3 显示了条件模型之间的差异。具体地说,引入额外的嵌入来将国籍索引映射到与 RNN 的隐藏层相同大小的向量。然后,在正向函数中嵌入民族指标,作为 RNN 的初始隐含层简单传入。虽然这是对第一个模型的一个非常简单的修改,但是它对于让 RNN 根据生成的国籍改变其行为有着深远的影响。
示例 7-4:条件化的姓氏生成模型
```py

@@ -263,9 +267,9 @@ class SurnameGenerationModel(nn.Module):

### Training Routine and Results
### 训练例程和结果
在本例中,我们介绍了用于生成姓氏的字符序列预测任务。虽然许多实现细节和训练例程与第6章的序列分类示例相似,但有几个主要区别。在这一节中,我们将重点讨论差异、使用的超参数和结果。 与前面的例子相比,计算这个例子中的损失需要两个更改,因为我们在序列中的每一步都要进行预测。首先,我们将三维张量重塑为二维张量(矩阵)以满足计算约束。其次,我们协调掩蔽索引,它允许可变长度序列与损失函数,使损失不使用掩蔽位置在其计算。
在本例中,我们介绍了用于生成姓氏的字符序列预测任务。虽然许多实现细节和训练例程与第 6 章的序列分类示例相似,但有几个主要区别。在这一节中,我们将重点讨论差异、使用的超参数和结果。 与前面的例子相比,计算这个例子中的损失需要两个更改,因为我们在序列中的每一步都要进行预测。首先,我们将三维张量重塑为二维张量(矩阵)以满足计算约束。其次,我们协调掩蔽索引,它允许可变长度序列与损失函数,使损失不使用掩蔽位置在其计算。
我们通过使用示例7-5中所示的代码片段来处理这两个问题——三维张量和可变长度序列。首先,预测和目标被标准化为损失函数期望的大小(预测是二维的,目标是一维的)。现在,每一行代表一个示例:按顺序执行一个时间步骤。然后,交叉熵损失用于ignore_index设置为mask_index。这将导致loss函数忽略与ignore_index匹配的目标中的任何位置。
我们通过使用示例 7-5 中所示的代码片段来处理这两个问题——三维张量和可变长度序列。首先,预测和目标被标准化为损失函数期望的大小(预测是二维的,目标是一维的)。现在,每一行代表一个示例:按顺序执行一个时间步骤。然后,交叉熵损失用于`ignore_index`设置为`mask_index`。这将导致损失函数忽略与`ignore_index`匹配的目标中的任何位置。
Example 7-5\. Handling three-dimensional tensors and sequence-wide loss computations
示例 7-5:处理三维张量和序列级损失计算

@@ -294,7 +298,7 @@ ```py

使用这种修正的损失计算,我们构造了一个训练例程,看起来与本书中的每个例子相似。它首先迭代训练数据集,每次只处理一小批数据。对于每个小批量,模型的输出是由输入计算出来的。因为我们在每一个时间步上执行预测,所以模型的输出是一个三维张量。使用前面描述的sequence_loss和优化器,可以计算模型预测的错误信号,并用于更新模型参数。
使用这种修正的损失计算,我们构造了一个训练例程,看起来与本书中的每个例子相似。它首先迭代训练数据集,每次只处理一小批数据。对于每个小批量,模型的输出是由输入计算出来的。因为我们在每一个时间步上执行预测,所以模型的输出是一个三维张量。使用前面描述的`sequence_loss`和优化器,可以计算模型预测的错误信号,并用于更新模型参数。
大多数模型超参数是由字符词汇表的大小决定的。这个大小是可以观察到的作为模型输入的离散令牌的数量,以及每次步骤输出分类中的类的数量。剩下的模型超参数是字符嵌入的大小和内部RNN隐藏状态的大小。示例7-6给出了这些超参数和培训选项。
大多数模型超参数是由字符词汇表的大小决定的。这个大小是可以观察到的作为模型输入的离散标记的数量,以及每次步骤输出分类中的类的数量。剩下的模型超参数是字符嵌入的大小和内部 RNN 隐藏状态的大小。示例 7-6 给出了这些超参数和训练选项。
Example 7-6\. Hyperparameters for surname generation
示例 7-6:姓氏生成的超参数

@@ -323,4 +327,6 @@ ```py

尽管预测的每个字符的准确性是模型性能的度量,但是在本例中,通过检查模型将生成的姓氏类型来进行定性评估会更好。为此,我们在forward()方法中步骤的修改版本上编写一个新的循环,以计算每个时间步骤的预测,并将这些预测用作下一个时间步骤的输入。我们将展示示例7-7中的代码。模型在每个时间步上的输出是一个预测向量,利用softmax函数将预测向量转换为概率分布。利用概率分布,我们利用火炬。多项式抽样函数,它以与索引的概率成比例的速率选择索引。抽样是一个每次产生不同输出的随机过程。 Example 7-7\. Sampling from the unconditioned generation model
尽管预测的每个字符的准确性是模型性能的度量,但是在本例中,通过检查模型将生成的姓氏类型来进行定性评估会更好。为此,我们在`forward()`方法中步骤的修改版本上编写一个新的循环,以计算每个时间步骤的预测,并将这些预测用作下一个时间步骤的输入。我们将展示示例 7-7 中的代码。模型在每个时间步上的输出是一个预测向量,利用 softmax 函数将预测向量转换为概率分布。利用概率分布,我们利用火炬。多项式抽样函数,它以与索引的概率成比例的速率选择索引。抽样是一个每次产生不同输出的随机过程。
示例 7-7:从非条件化生成模型采样
```py

@@ -363,5 +369,5 @@ def sample_from_model(model, vectorizer, num_samples=1, sample_size=20,

您需要将采样的索引从sample_from_model()函数转换为用于人类可读输出的字符串。如示例7-8所示,要做到这一点,需要使用用于向量化姓氏的SequenceVocabulary。在创建字符串时,只使用序列结束索引之前的索引。这是假设模型能够了解姓氏应该在何时结束。
您需要将采样的索引从`sample_from_model()`函数转换为用于人类可读输出的字符串。如示例 7-8 所示,要做到这一点,需要使用用于向量化姓氏的`SequenceVocabulary`。在创建字符串时,只使用序列结束索引之前的索引。这是假设模型能够了解姓氏应该在何时结束。
Example 7-8\. Mapping sampled indices to surname strings
示例 7-8:将采样的索引映射为姓氏字符串

@@ -394,4 +400,6 @@ ```py

使用这些函数,您可以检查模型的输出,如示例7-9所示,以了解模型是否正在学习生成合理的姓氏。从检查输出中我们可以学到什么?我们可以看到,尽管这些姓氏似乎遵循着几种形态模式,但这些姓氏显然并不是来自一个国家或另一个国家。一种可能是,学习姓氏的一般模型会混淆不同民族之间的性格分布。有条件的姓氏生成模型就是用来处理这种情况的。 Example 7-9\. Sampling from the unconditioned model
使用这些函数,您可以检查模型的输出,如示例 7-9 所示,以了解模型是否正在学习生成合理的姓氏。从检查输出中我们可以学到什么?我们可以看到,尽管这些姓氏似乎遵循着几种形态模式,但这些姓氏显然并不是来自一个国家或另一个国家。一种可能是,学习姓氏的一般模型会混淆不同民族之间的性格分布。有条件的姓氏生成模型就是用来处理这种情况的。
示例 7-9:从非条件化模型采样
```py

@@ -416,4 +424,6 @@ Input[0]

对于有条件的SurnameGenerationModel,我们修改sample_from_model()函数来接受国籍索引列表,而不是指定数量的样本。在例7-10中,修改后的函数使用带有国籍嵌入的国籍索引来构造GRU的初始隐藏状态。在此之后,采样过程与非条件模型完全相同。 Example 7-10\. Sampling from a sequence model
对于有条件的`SurnameGenerationModel`,我们修改`sample_from_model()`函数来接受国籍索引列表,而不是指定数量的样本。在例 7-10 中,修改后的函数使用带有国籍嵌入的国籍索引来构造 GRU 的初始隐藏状态。在此之后,采样过程与非条件模型完全相同。
示例 7-10:从序列模型采样
```py

@@ -459,4 +469,6 @@ def sample_from_model(model, vectorizer, nationalities, sample_size=20,

用条件向量采样的有效性意味着我们对生成输出有影响。在示例7-11中,我们迭代国籍索引并从每个索引中取样。为了节省空间,我们只显示一些输出。从这些输出中,我们可以看到,该模型确实采用了姓氏拼写的一些模式。 Example 7-11\. Sampling from a conditioned SurnameGenerationModel (not all outputs are shown)
用条件向量采样的有效性意味着我们对生成输出有影响。在示例 7-11 中,我们迭代国籍索引并从每个索引中取样。为了节省空间,我们只显示一些输出。从这些输出中,我们可以看到,该模型确实采用了姓氏拼写的一些模式。
示例 7-11:从条件`SurnameGenerationModel`采样(没有展示所有输出)
```py

@@ -505,6 +517,8 @@ Input[0]

## Tips and Tricks for Training Sequence Models
## 训练序列模型的提示和技巧
序列模型很难训练,而且在这个过程中会出现许多问题。在这里,我们总结了一些技巧和技巧,我们发现不仅在我们的工作中有用,而且也被其他人在文献报道。 1.如果可能,使用门控变量 门控体系结构通过解决非通配型的许多数值稳定性问题简化了训练。 2.如果可能,请选择GRUs而不是LSTMs GRUs提供了与LSTMs几乎相同的性能,并且使用更少的参数和计算。幸运的是,从PyTorch的角度来看,除了简单地使用不同的模块类之外,在LSTM上使用GRU没有什么可做的。 3.使用Adam作为您的优化器 在第6章、第7章和第8章中,我们只使用Adam作为优化器,这是有充分理由的:它是可靠的,收敛速度更快。对于序列模型尤其如此。如果由于某些原因,您的模型没有与Adam收敛,那么在这种情况下,切换到随机梯度下降可能会有所帮助。 4.梯度剪裁 如果您注意到在应用这些章节中学习到的概念时出现了数字错误,请在训练过程中使用您的代码绘制梯度值。知道愤怒之后,剪掉任何异常值。这将确保更顺利的培训。在PyTorch中,有一个有用的实用程序clip_grad_norm可以为您完成此工作,如示例7-12所示。一般来说,你应该养成剪切渐变的习惯。 Example 7-12\. Applying gradient clipping in PyTorch
序列模型很难训练,而且在这个过程中会出现许多问题。在这里,我们总结了一些技巧和技巧,我们发现不仅在我们的工作中有用,而且也被其他人在文献报道。 1)如果可能,使用门控变量 门控体系结构通过解决非通配型的许多数值稳定性问题简化了训练。 2)如果可能,请选择 GRUs 而不是 LSTMs GRUs 提供了与 LSTMs 几乎相同的性能,并且使用更少的参数和计算。幸运的是,从 PyTorch 的角度来看,除了简单地使用不同的模块类之外,在 LSTM 上使用 GRU 没有什么可做的。 3)使用 Adam 作为您的优化器 在第 6 章、第 7 章和第 8 章中,我们只使用 Adam 作为优化器,这是有充分理由的:它是可靠的,收敛速度更快。对于序列模型尤其如此。如果由于某些原因,您的模型没有与 Adam 收敛,那么在这种情况下,切换到随机梯度下降可能会有所帮助。 4)梯度剪裁 如果您注意到在应用这些章节中学习到的概念时出现了数字错误,请在训练过程中使用您的代码绘制梯度值。知道愤怒之后,剪掉任何异常值。这将确保更顺利的训练。在 PyTorch 中,有一个有用的实用程序`clip_grad_norm`可以为您完成此工作,如示例 7-12 所示。一般来说,你应该养成剪切渐变的习惯。
示例 7-12:在 PyTorch 中应用梯度剪裁
```py

@@ -528,4 +542,4 @@ # define your sequence model

5.早期停止 对于序列模型,很容易过度拟合。我们建议您在评估错误(在开发集上测量的)开始出现时尽早停止培训过程。
5.早期停止 对于序列模型,很容易过度拟合。我们建议您在评估错误(在开发集上测量的)开始出现时尽早停止训练过程。
在第8章中,我们继续讨论序列模型,使用序列到序列模型来预测和生成与输入长度不同的序列,并讨论序列模型的更多变体。
在第 8 章中,我们继续讨论序列模型,使用序列到序列模型来预测和生成与输入长度不同的序列,并讨论序列模型的更多变体。

@@ -1,86 +0,104 @@

# Chapter 8\. Advanced Sequence Modeling for Natural Language Processing
# 八、自然语言处理的高级序列模型
> 本文标题:[Natural-Language-Processing-with-PyTorch(八)](https://yifdu.github.io/2018/12/28/Natural-Language-Processing-with-PyTorch%EF%BC%88%E5%85%AB%EF%BC%89/)
> 本文标题:[Natural-Language-Processing-with-PyTorch(八)](https://yifdu.github.io/2018/12/28/Natural-Language-Processing-with-PyTorch%EF%BC%88%E5%85%AB%EF%BC%89/)
>
> 文章作者:[Yif Du](https://yifdu.github.io/ "访问 Yif Du 的个人博客")
> 文章作者:[Yif Du](https://yifdu.github.io/ "访问 Yif Du 的个人博客")
>
> 发布时间:2018年12月28日 - 09:12
> 发布时间:2018 年 12 月 28 日 - 09:12
>
> 最后更新:2018年12月28日 - 11:12
> 最后更新:2018 年 12 月 28 日 - 11:12
>
> 原始链接:[http://yifdu.github.io/2018/12/28/Natural-Language-Processing-with-PyTorch(八)/](https://yifdu.github.io/2018/12/28/Natural-Language-Processing-with-PyTorch%EF%BC%88%E5%85%AB%EF%BC%89/)
> 原始链接:[http://yifdu.github.io/2018/12/28/Natural-Language-Processing-with-PyTorch(八)/](https://yifdu.github.io/2018/12/28/Natural-Language-Processing-with-PyTorch%EF%BC%88%E5%85%AB%EF%BC%89/)
>
> 许可协议: [署名-非商业性使用-禁止演绎 4.0 国际](https://creativecommons.org/licenses/by-nc-nd/4.0/) 转载请保留原文链接及作者。
> 许可协议:[署名-非商业性使用-禁止演绎 4.0 国际](https://creativecommons.org/licenses/by-nc-nd/4.0/) 转载请保留原文链接及作者。
在本章中,我们以第六章和第七章讨论的序列建模概念为基础,将它们扩展到序列到序列建模的领域,其中模型以一个序列作为输入,并产生另一个可能不同长度的序列作为输出。序列对序列问题的例子随处可见。例如,给定一封电子邮件,我们可能希望预测响应。给出一个法语句子,预测它的英语翻译。或者,给定一篇文章,写一篇摘要。我们还讨论了序列模型的结构变体,特别是双向模型。为了最大限度地利用序列表示,我们介绍了注意机制并对其进行了深入讨论。最后,本章以实现本章描述的概念的神经机器翻译的详细演练结束。
## Sequence-to-Sequence Models, Encoder–Decoder Models, and Conditioned Generation
## 序列到序列模型,编码器-解码器模型,和条件生成
序列到序列(S2S)模型是一种称为编码器-解码器模型的一般模型家族的特殊情况。编码器-解码器模型是两个模型(图8-1)的组合,一个是“编码器”模型,另一个是“解码器”模型,这两个模型通常是联合训练的。编码器模型需要输入并产生一个编码或表示ϕ的输入,通常一个向量。编码器的目标是捕获与当前任务相关的输入的重要属性。解码器的目标是获取编码输入并产生所需的输出。通过对编码器和解码器的理解,我们将S2S模型定义为编码器-解码器模型,其中编码器和解码器是序列模型,输入和输出都是序列,可能长度不同。 ![S2S](img/e35a261d985c5ea7ba83bde48d98929d.jpg "图8-1编码器-解码器模型是由两个联合训练的模型组成的。编码器产生一个代表或编码输入ϕ的解码器用来产生一个输出。") 一种查看编码器-解码器模型的方法是将其作为称为条件生成模型的模型的特殊情况。在conditioned-generation中,替代输入表示ϕ,一般条件上下文c影响译码器产生一个输出。当条件上下文c来自编码器模型时,条件生成与编码器-解码器模型相同。并非所有的条件生成模型都是编码器-解码器模型,因为条件上下文可能来自结构化源。以天气报告生成器为例。温度、湿度、风速和风向的值可以“调节”解码器,生成文本天气报告。在“模型2:条件性姓氏生成模型”中,我们看到了一个基于国籍条件性姓氏生成的例子。图8-2展示了一些条件生成模型的实际示例。
序列到序列(S2S)模型是一种称为编码器-解码器模型的一般模型家族的特殊情况。编码器-解码器模型是两个模型(图 8-1)的组合,一个是“编码器”模型,另一个是“解码器”模型,这两个模型通常是联合训练的。编码器模型需要输入并产生一个编码或表示ϕ的输入,通常一个向量。编码器的目标是捕获与当前任务相关的输入的重要属性。解码器的目标是获取编码输入并产生所需的输出。通过对编码器和解码器的理解,我们将 S2S 模型定义为编码器-解码器模型,其中编码器和解码器是序列模型,输入和输出都是序列,可能长度不同。
![example](img/e0efc32c40cec36d36b5cb2245b6f4b4.jpg "图8 - 2。用编码器-解码器模型解决的任务示例。(一)机器翻译:输入A为法语句,输出B为英语句。(二)邮件回复建议:输入A为邮件文本;输出B是许多可能的答复之一。在(III)中,给出了一个更复杂的例子。在这里,聊天机器人正在回答以a为标记的关于输入图像(a ')的问题,它将响应B的生成条件设置为a和a的编码。所有这些任务也可以看作是条件生成任务。")
![S2S](img/e35a261d985c5ea7ba83bde48d98929d.jpg)
在这一章中,我们深入研究了S2S模型,并在机器翻译任务的背景下进行了说明。考虑一个“智能”的iOS/Android键盘,它可以在你打字时自动将文本转换成表情符号。如果你输入“omg!”房子着火了!,你希望键盘输出类似内联输出的内容。注意,输出的长度(4个令牌)与输入的长度(6个令牌)不同。输出和输入之间的映射称为对齐,如图8-3所示。 ![example2](img/b7d81537e2e606730712b8dcdc5eb05f.jpg "图8 - 3。表情符号翻译是一个S2S预测问题:两个序列中符号之间的对齐表示翻译等值。") 同样,在本例中,输入中的单个令牌可以在输出中生成零个或多个令牌。传统上,许多解决S2S问题的方法都是尝试使用工程和启发式重统计方法。虽然回顾这些方法超出了本章和本书的范围,但是我们建议您阅读Koehn(2009)并参考statmt.org中的参考资料。
一种查看编码器-解码器模型的方法是将其作为称为条件生成模型的模型的特殊情况。在条件生成中,替代输入表示`ϕ`,一般条件上下文`c`影响译码器产生一个输出。当条件上下文`c`来自编码器模型时,条件生成与编码器-解码器模型相同。并非所有的条件生成模型都是编码器-解码器模型,因为条件上下文可能来自结构化源。以天气报告生成器为例。温度、湿度、风速和风向的值可以“调节”解码器,生成文本天气报告。在“模型 2:条件性姓氏生成模型”中,我们看到了一个基于国籍条件性姓氏生成的例子。图 8-2 展示了一些条件生成模型的实际示例。
在第6章中,我们学习了序列模型如何将任意长度的序列编码成向量。在第7章中,我们看到单个向量如何使递归神经网络(RNN)有条件地产生不同的姓氏。S2S模型是这些概念的自然延伸。
![example](img/e0efc32c40cec36d36b5cb2245b6f4b4.jpg)
图8 - 4显示了编码器整个输入一个表示“编码”,ϕ,条件解码器生成正确的输出。您可以使用任何RNN作为编码器,无论是Elman RNN, Long - term Memory (LSTM),还是gate Unit (GRU),。在接下来的两个部分中,我们将介绍现代S2S模型的两个重要组件。首先,我们引入了双向递归模型,该模型将向前和向后传递组合在一个序列上,以创建更丰富的表示。然后,在“从序列中获取更多信息:注意力”中,我们介绍并考察了注意力机制,它在关注与任务相关的输入的不同部分时非常有用。这两个部分对于构建基于S2S模型的解决方案都非常重要。
在这一章中,我们深入研究了 S2S 模型,并在机器翻译任务的背景下进行了说明。考虑一个“智能”的 iOS/Android 键盘,它可以在你打字时自动将文本转换成表情符号。如果你输入`omg!`房子着火了!,你希望键盘输出类似内联输出的内容。注意,输出的长度(4 个标记)与输入的长度(6 个标记)不同。输出和输入之间的映射称为对齐,如图 8-3 所示。
![example3](img/d9e36aa0f27f4c63020a354160813b95.jpg "图8-4\. S2S模式,用于将英语翻译成表情符号。")
![example2](img/b7d81537e2e606730712b8dcdc5eb05f.jpg)
## Capturing More from a Sequence: Bidirectional Recurrent Models
同样,在本例中,输入中的单个标记可以在输出中生成零个或多个标记。传统上,许多解决 S2S 问题的方法都是尝试使用工程和启发式重统计方法。虽然回顾这些方法超出了本章和本书的范围,但是我们建议您阅读 Koehn(2009)并参考 statmt.org 中的参考资料。
理解递归模型的一种方法是把它看作一个将序列编码为向量的黑盒子。在建模序列时,不仅要观察过去的单词,而且还要观察将来出现的单词。考虑以下句子:
在第 6 章中,我们学习了序列模型如何将任意长度的序列编码成向量。在第 7 章中,我们看到单个向量如何使循环神经网络(RNN)有条件地产生不同的姓氏。S2S 模型是这些概念的自然延伸。
The man who hunts ducks out on the weekends. 如果模型只从左到右观察,那么“duck”的表示将不同于从右到左观察单词的模型。人类一直在做这种回溯性的更新。
图 8 - 4 显示了编码器整个输入一个表示“编码”,`ϕ`,条件解码器生成正确的输出。您可以使用任何 RNN 作为编码器,无论是 Elman RNN, 长短期记忆(LSTM),还是门控单元(GRU),。在接下来的两个部分中,我们将介绍现代 S2S 模型的两个重要组件。首先,我们引入了双向递归模型,该模型将向前和向后传递组合在一个序列上,以创建更丰富的表示。然后,在“从序列中获取更多信息:注意力”中,我们介绍并考察了注意力机制,它在关注与任务相关的输入的不同部分时非常有用。这两个部分对于构建基于 S2S 模型的解决方案都非常重要。
因此,如果把过去和未来的信息结合在一起,就能够有力地按顺序表示一个单词的意思。这就是双向递归模型的目标。递归家族的任何模型,如Elmann RNNs或LSTMs或GRUs,都可以用于这种双向表达。与第6章和第7章中的单向模型一样,双向模型可以用于分类和序列标记设置,我们需要预测输入中每个单词的一个标签。图8-5和图8-6详细说明了这一点。在图8-6中,ϕ[love] 是表示、编码或该时刻网络的“隐藏的状态”,当输入的词是”love”步。当我们讨论注意力时,这种状态信息在“从一个序列中获取更多信息:注意力”中变得很重要。
![example3](img/d9e36aa0f27f4c63020a354160813b95.jpg)
![Attention](img/b74e4cae22941eb118bc6f363c85fa2d.jpg "图8-5\. 用于序列分类的双向RNN模型。注意模型“读取”这句话在这两个方向,并产生一个句子表示ϕ的作文向前和向后表示。这里没有显示的是由线性层和softmax组成的最终分类层。") ![Attention2](img/d6df8a4d46a3f6a19896d574f58dbbb2.jpg "图8-6。用于序列标记的双向递归模型。注意输入中的每个单词是如何存在“前向”表示和“后向”表示,它们被连接起来以产生所讨论单词的最终表示。这里没有显示的是最终分类层,在每个时间步骤由线性层和softmax组成。")
## 从序列中捕获更多:双向循环模型
## Capturing More from a Sequence: Attention
理解递归模型的一种方法是把它看作一个将序列编码为向量的黑盒子。在建模序列时,不仅要观察过去的单词,而且还要观察将来出现的单词。考虑以下句子:`The man who hunts ducks out on the weekends.`。 如果模型只从左到右观察,那么`duck`的表示将不同于从右到左观察单词的模型。人类一直在做这种回溯性的更新。
“序列到序列模型,编码器 - 解码器模型和条件生成”中引入的S2S模型公式的一个问题是它将整个输入句子变成单个矢量(“编码”)φ并使用该编码生成输出,如图8-7所示。虽然这可能适用于非常短的句子,但对于长句,这样的模型无法捕获整个输入中的信息;例如,见Bengio等。 (1994)和Le和Zuidema(2016)。这是仅使用最终隐藏状态作为编码的限制。长输入的另一个问题是,当长时间输入反向传播时,梯度消失,使训练变得困难。 ![Attention3](img/63c2d0fdd2e3d4fc9ebaa90f29949bce.jpg "图8-7.使用编码器 - 解码器模型将长法语句子翻译成英语。最终表示φ无法捕获输入中的长程依赖性并使训练变得困难。") 对于曾尝试翻译的双语/多语言读者来说,这种首先编码然后解码的过程可能会有点奇怪。作为人类,我们通常不会提炼句子的含义并从意义中产生翻译。对于图8-7中的示例,“pour” we know there will be a “for”; similarly “breakfast” is on our mind when we see “petit-déjeuner,” and so on. In other words, our mind focuses on the relevant parts of the input while producing output. This phenomenon is called attention. Attention has been widely studied in neuroscience and other allied fields, and it is what makes us quite successful despite having limited memories. Attention happens everywhere. In fact, it is happening right now to you, dear reader. Each. Word. You. Are. Reading. Now. Is. Being. Attended. To. Even if you have an exceptional memory, you’re probably not reading this entire book as a string. When you are reading a word, you are paying attention to the neighboring word, possibly the topic of the Section and Chapter, and so on.
因此,如果把过去和未来的信息结合在一起,就能够有力地按顺序表示一个单词的意思。这就是双向递归模型的目标。递归家族的任何模型,如 Elmann RNNs 或 LSTMs 或 GRUs,都可以用于这种双向表达。与第 6 章和第 7 章中的单向模型一样,双向模型可以用于分类和序列标记设置,我们需要预测输入中每个单词的一个标签。图 8-5 和图 8-6 详细说明了这一点。在图 8-6 中,`ϕ[love]`是表示、编码或该时刻网络的“隐藏的状态”,当输入的词是`love`。当我们讨论注意力时,这种状态信息在“从一个序列中获取更多信息:注意力”中变得很重要。
以类似的方式,我们希望序列生成模型将注意力集中到输入的不同部分,而不仅仅是整个输入的最终总结。这就是注意力机制。第一个包含自然语言处理(NLP)注意概念的模型是Bahdanau等人(2015)的机器翻译模型。从那时起,人们提出了几种注意机制和提高注意的方法。在本节中,我们将回顾一些基本的注意机制,并介绍一些与注意相关的术语。事实证明,注意力对于改进输入和输出复杂的深度学习系统非常有用。事实上,Bahdanau等人通过“BLEU score”(我们在“评估序列生成模型”中看到的)来衡量机器翻译系统的性能,当输入变长时,机器翻译系统在没有注意机制的情况下会下降,如图8-8所示。增加注意力可以解决问题。
![Attention](img/b74e4cae22941eb118bc6f363c85fa2d.jpg)
![example4](img/6fcf2d19b01740d36c409edb3500f7a3.jpg "图8-8\. 为什么需要注意?该图表显示了具有(RNNsearch-30,RNNsearch-50)和没有(RNNenc-30,RNNenc-50)注意力的机器翻译系统的BLEU分数的变化。 RNN * -30和RNN * -50系统分别用长达30和50个单词的句子进行训练。在机器翻译系统中,没有注意,系统的性能随着句子长度的增加而降低。通过注意,较长句子的翻译得到改善,但机器翻译性能的稳定性与训练它的句子的长度有关。 (图由Bahdanau等人提供[2015])")
### Attention in Deep Neural Networks
![Attention2](img/d6df8a4d46a3f6a19896d574f58dbbb2.jpg)
注意力是一种通用的机制,可以用于本书前面讨论过的任何一种模型。但我们在这里用编码器-解码器模型来描述它,因为这些模型是注意力机制真正发挥作用的地方。考虑一个S2S模型。回想一下,在一个典型的S2S模型中,每个时间步生成一个隐藏的状态表示,表示 ϕ[w],特定于该时间步的编码器。(如图8-6所示。)为了引起注意,我们不仅要考虑编码器的最终隐藏状态,还要考虑每个中间步骤的隐藏状态。这些编码器隐藏状态,在某种程度上是非信息性的,称为值。在某些情况下,编码器的隐藏状态也称为键。注意力还取决于调用查询的解码器的前一个隐藏状态。图8-9说明了时间步骤0的所有这些。时间步长t=0的查询向量是一个固定的超参数。注意由一个向量来表示,这个向量的维数与它所关注的值的维数相同。这被称为注意力向量,或注意力权重,有时也称为对齐。注意力权重与编码器状态(“值”)相结合,生成一个有时也称为瞥见的上下文向量。这个上下文向量成为解码器的输入,而不是完整的句子编码。使用兼容性函数更新下一个时间步骤的注意向量。相容函数的确切性质取决于所使用的注意机制。
## 从序列中捕获更多:注意力
![Encoder_Decoder](img/e9a33e716b27aa7486aa9abbfa4f93e8.jpg "图8-9\. 时间步长t=0时的注意。预测的输出是“对”和关注块考虑隐状态的编码器ϕw所有输入的单词。要详细了解这个图(我们强烈推荐),请参见“深度神经网络中的注意”。") 有几种方法可以实现关注。最简单和最常用的是内容感知机制。您可以在“示例:神经机器翻译”中看到内容感知注意力。另一种流行的注意机制是位置感知注意力,它仅依赖于查询向量和密钥。注意权重通常是0到1之间的浮点值。这称为软注意。相反,可以学习二进制0/1向量以引起注意。这被称为硬关注。
“序列到序列模型,编码器 - 解码器模型和条件生成”中引入的 S2S 模型公式的一个问题是它将整个输入句子变成单个向量(“编码”)`φ`并使用该编码生成输出,如图 8-7 所示。虽然这可能适用于非常短的句子,但对于长句,这样的模型无法捕获整个输入中的信息;例如,见 Bengio 等。 (1994)和 Le 和 Zuidema(2016)。这是仅使用最终隐藏状态作为编码的限制。长输入的另一个问题是,当长时间输入反向传播时,梯度消失,使训练变得困难。
图8-9中所示的注意机制取决于输入中所有时间步长的编码器状态。这也被称为全球关注。相反,对于本地注意力,您可以设计一种注意机制,该机制仅依赖于当前时间步长周围的输入窗口。
![Attention3](img/63c2d0fdd2e3d4fc9ebaa90f29949bce.jpg)
有时,特别是在机器翻译中,可以明确地提供对齐信息作为训练数据的一部分。在这种情况下,可以设计受监督的注意力来使用共同训练的单独神经网络来学习注意力功能。对于诸如文档之类的大型输入,可以设计粗粒度到细粒度的注意机制,也称为分级注意,不仅关注立即输入,而且还考虑文档的结构 - 段落,部分,章节等。 Vaswani等人对变压器网络的研究。 (2017),引入多头注意,其中多个注意向量用于跟踪输入的不同区域。他们还普及了自我关注的概念,这是一种机制,通过该机制,模型可以了解输入的哪些区域相互影响。
对于曾尝试翻译的双语/多语言读者来说,这种首先编码然后解码的过程可能会有点奇怪。作为人类,我们通常不会提炼句子的含义并从意义中产生翻译。对于图 8-7 中的示例,当我们看到`pour`,我们知道会有一个`for`; 类似地,当我们看到`petit-déjeuner`时,我们就会想到`breakfast`,等等。 换句话说,我们的思维在产生输出时专注于输入的相关部分。 这种现象称为注意力。 注意已经在神经科学和其他相关领域得到了广泛的研究,这使我们尽管记忆有限,却取得了相当的成功。 注意无处不在。 实际上,亲爱的读者,现在正在发生这种情况。 **现在每个你阅读的单词都受到注意**。 即使您记忆犹新,您可能也不会读整本书。 在阅读单词时,您会注意相邻的单词,可能是本节和本章的主题,等等。
当输入是多模式时 - 例如,图像和语音 - 可以设计多模式注意力。关于注意力的文献虽然很新,但已经非常广泛,这表明了这一主题的重要性。详细介绍它们的每一个都超出了本书的范围,我们将引导您到Luong,Pham和Manning(2011)以及Vaswani等人。 (2017)作为起点。
以类似的方式,我们希望序列生成模型将注意力集中到输入的不同部分,而不仅仅是整个输入的最终总结。这就是注意力机制。第一个包含自然语言处理(NLP)注意概念的模型是 Bahdanau 等人(2015)的机器翻译模型。从那时起,人们提出了几种注意机制和提高注意的方法。在本节中,我们将回顾一些基本的注意机制,并介绍一些与注意相关的术语。事实证明,注意力对于改进输入和输出复杂的深度学习系统非常有用。事实上,Bahdanau 等人通过“BLEU 得分”(我们在“评估序列生成模型”中看到的)来衡量机器翻译系统的性能,当输入变长时,机器翻译系统在没有注意机制的情况下会下降,如图 8-8 所示。增加注意力可以解决问题。
## Evaluating Sequence Generation Models
![example4](img/6fcf2d19b01740d36c409edb3500f7a3.jpg)
当生成任务中可以看到多个有效答案时,精度,召回,准确度和F1等分类指标无法帮助模型 - 单个法语句子可以有多个英语翻译。序列模型根据称为参考输出的预期输出进行评估。在比较不同的模型时,我们使用分数来表明模型输出的“良好”与参考的接近程度。例如,在像机器翻译这样的任务中,如果一个模型只有一个单词关闭,我们可能不希望像另一个产生完全无法理解的答案的模型那样惩罚该模型。单个输入示例可以有多个参考输出。例如,对于特定的法语句子,可能存在多个有效的英语翻译,使用略微不同的单词。序列生成模型有两种评估:人工评估和自动评估。
### 深度神经网络中的注意力
人体评估涉及一个或多个人类受试者,要么对模型输出给出“竖起拇指”或“拇指向下”评级,要么进行编辑以纠正翻译。这导致了一个简单的“错误率”,它非常接近系统输出与人工任务相关的最终目标。人类评价很重要,但是很少使用,因为人类注释者往往是缓慢,昂贵和难以获得的。最后,人类也可能彼此不一致,并且,与任何其他金标准一样,人类评估与注释器间协议率配对。测量注释器间协议率也是另一个昂贵的主张。一种常见的人类评估指标是人为目标翻译错误率(HTER)。 HTER是一个加权编辑距离,由人类为了合理充分的意义和流畅性而“修复”翻译输出而进行的插入,删除和转置次数计算得出(参见图8-10)。
注意力是一种通用的机制,可以用于本书前面讨论过的任何一种模型。但我们在这里用编码器-解码器模型来描述它,因为这些模型是注意力机制真正发挥作用的地方。考虑一个 S2S 模型。回想一下,在一个典型的 S2S 模型中,每个时间步生成一个隐藏的状态表示,表示`ϕ[w]`,特定于该时间步的编码器。(如图 8-6 所示。)为了引起注意,我们不仅要考虑编码器的最终隐藏状态,还要考虑每个中间步骤的隐藏状态。这些编码器隐藏状态,在某种程度上是非信息性的,称为值。在某些情况下,编码器的隐藏状态也称为键。注意力还取决于调用查询的解码器的前一个隐藏状态。图 8-9 说明了时间步骤 0 的所有这些。时间步长`t=0`的查询向量是一个固定的超参数。注意由一个向量来表示,这个向量的维数与它所关注的值的维数相同。这被称为注意力向量,或注意力权重,有时也称为对齐。注意力权重与编码器状态(“值”)相结合,生成一个有时也称为瞥见的上下文向量。这个上下文向量成为解码器的输入,而不是完整的句子编码。使用兼容性函数更新下一个时间步骤的注意向量。相容函数的确切性质取决于所使用的注意机制。
![Just](img/388af7f8313976c5e8fb5b223f851953.jpg "图8-10\. 翻译任务的人工评估。(由菲利普·科恩)。") 另一方面,自动评估操作简单快捷。有两种度量标准可用于自动评估生成的序列。我们再次使用机器翻译作为示例,但这些指标也适用于涉及生成序列的任何任务。这些指标包括基于ngram重叠的指标和困惑。基于Ngram重叠的度量倾向于通过使用ngram重叠统计来计算得分来测量输出相对于参考的接近程度。 BLEU,ROUGE和METEOR是基于ngram重叠的度量的示例。其中,BLEU经受了时间的考验,成为机器翻译文献中的衡量标准.6 BLEU代表“BiLingual Evaluation Understudy”。我们跳过BLEU的确切表述,并建议您阅读Papineni等人。 (2002年)。出于实际目的,我们使用像NLTK7或SacreBLEU8这样的包来计算分数。当参考数据可用时,BLEU本身的计算非常快速和容易。
![Encoder_Decoder](img/e9a33e716b27aa7486aa9abbfa4f93e8.jpg)
困惑是基于信息理论的另一种自动评估指标,您可以将其应用于可以测量输出序列概率的任何情况。对于序列x,如果P(x)是序列的概率,则困惑定义如下:
有几种方法可以实现关注。最简单和最常用的是内容感知机制。您可以在“示例:神经机器翻译”中看到内容感知注意力。另一种流行的注意机制是位置感知注意力,它仅依赖于查询向量和密钥。注意权重通常是 0 到 1 之间的浮点值。这称为软注意。相反,可以学习二进制 0/1 向量以引起注意。这被称为硬关注。
这为我们提供了一种比较不同序列生成模型的简单方法 - 测量保持数据集的模型的困惑度。虽然这很容易计算,但是当用于序列生成评估时,困惑会有许多问题。首先,它是一个膨胀的指标。请注意,困惑的表达式涉及取幂。因此,模型性能(可能性)的微小差异可能导致困惑的巨大差异,从而产生重大进展的错觉。其次,对困惑的改变可能不会转化为通过其他指标观察到的模型错误率的相应变化。最后,就像BLEU和其他基于ngram的指标一样,困惑的改善可能不会转化为人类判断的可察觉的改进。
图 8-9 中所示的注意机制取决于输入中所有时间步长的编码器状态。这也被称为全球关注。相反,对于本地注意力,您可以设计一种注意机制,该机制仅依赖于当前时间步长周围的输入窗口。
在下一节中,我们将跟进机器翻译示例,并通过PyTorch实现将这些概念巧妙地结合在一起。
有时,特别是在机器翻译中,可以明确地提供对齐信息作为训练数据的一部分。在这种情况下,可以设计受监督的注意力来使用共同训练的单独神经网络来学习注意力功能。对于诸如文档之类的大型输入,可以设计粗粒度到细粒度的注意机制,也称为分级注意,不仅关注立即输入,而且还考虑文档的结构 - 段落,部分,章节等。 Vaswani 等人对变压器网络的研究。 (2017),引入多头注意,其中多个注意向量用于跟踪输入的不同区域。他们还普及了自我关注的概念,这是一种机制,通过该机制,模型可以了解输入的哪些区域相互影响。
## Example: Neural Machine Translation
当输入是多模式时 - 例如,图像和语音 - 可以设计多模式注意力。关于注意力的文献虽然很新,但已经非常广泛,这表明了这一主题的重要性。详细介绍它们的每一个都超出了本书的范围,我们将引导您到 Luong,Pham 和 Manning(2011)以及 Vaswani 等人。 (2017)作为起点。
在本例中,我们将介绍S2S模型最常用的实现:机器翻译。随着深度学习在2010年代早期的流行,很明显,使用嵌入式词汇和RNNs是一种非常强大的两种语言之间的翻译方法——只要有足够的数据。引入“序列生成模型评价”中的注意机制,进一步完善了机器翻译模型。在这一部分,我们描述了一个基于Luong, Pham, and Manning(2015)的实现,它简化了S2S模型中的注意方法。
## 评估序列生成模型
当生成任务中可以看到多个有效答案时,精度,召回,准确度和 F1 等分类指标无法帮助模型 - 单个法语句子可以有多个英语翻译。序列模型根据称为参考输出的预期输出进行评估。在比较不同的模型时,我们使用分数来表明模型输出的“良好”与参考的接近程度。例如,在像机器翻译这样的任务中,如果一个模型只有一个单词关闭,我们可能不希望像另一个产生完全无法理解的答案的模型那样惩罚该模型。单个输入示例可以有多个参考输出。例如,对于特定的法语句子,可能存在多个有效的英语翻译,使用略微不同的单词。序列生成模型有两种评估:人工评估和自动评估。
人体评估涉及一个或多个人类受试者,要么对模型输出给出“竖起拇指”或“拇指向下”评级,要么进行编辑以纠正翻译。这导致了一个简单的“错误率”,它非常接近系统输出与人工任务相关的最终目标。人类评价很重要,但是很少使用,因为人类注释者往往是缓慢,昂贵和难以获得的。最后,人类也可能彼此不一致,并且,与任何其他金标准一样,人类评估与注释器间协议率配对。测量注释器间协议率也是另一个昂贵的主张。一种常见的人类评估指标是人为目标翻译错误率(HTER)。 HTER 是一个加权编辑距离,由人类为了合理充分的意义和流畅性而“修复”翻译输出而进行的插入,删除和转置次数计算得出(参见图 8-10)。
![Just](img/388af7f8313976c5e8fb5b223f851953.jpg)
另一方面,自动评估操作简单快捷。有两种度量标准可用于自动评估生成的序列。我们再次使用机器翻译作为示例,但这些指标也适用于涉及生成序列的任何任务。这些指标包括基于 N 元组重叠的指标和困惑。基于 N 元组重叠的度量倾向于通过使用 N 元组重叠统计来计算得分来测量输出相对于参考的接近程度。 BLEU,ROUGE 和 METEOR 是基于 N 元组重叠的度量的示例。其中,BLEU 经受了时间的考验,成为机器翻译文献中的衡量标准。 BLEU 代表“双语评估学习”。我们跳过 BLEU 的确切表述,并建议您阅读 Papineni 等人。 (2002 年)。出于实际目的,我们使用像 NLTK7 或 SacreBLEU8 这样的包来计算分数。当参考数据可用时,BLEU 本身的计算非常快速和容易。
困惑是基于信息理论的另一种自动评估指标,您可以将其应用于可以测量输出序列概率的任何情况。对于序列`x`,如果`P(x)`是序列的概率,则困惑定义如下:
这为我们提供了一种比较不同序列生成模型的简单方法 - 测量保持数据集的模型的困惑度。虽然这很容易计算,但是当用于序列生成评估时,困惑会有许多问题。首先,它是一个膨胀的指标。请注意,困惑的表达式涉及取幂。因此,模型性能(可能性)的微小差异可能导致困惑的巨大差异,从而产生重大进展的错觉。其次,对困惑的改变可能不会转化为通过其他指标观察到的模型错误率的相应变化。最后,就像 BLEU 和其他基于 N 元组的指标一样,困惑的改善可能不会转化为人类判断的可察觉的改进。
在下一节中,我们将跟进机器翻译示例,并通过 PyTorch 实现将这些概念巧妙地结合在一起。
## 示例:神经机器翻译
在本例中,我们将介绍 S2S 模型最常用的实现:机器翻译。随着深度学习在 2010 年代早期的流行,很明显,使用嵌入词汇和 RNNs 是一种非常强大的两种语言之间的翻译方法——只要有足够的数据。引入“序列生成模型评价”中的注意机制,进一步完善了机器翻译模型。在这一部分,我们描述了一个基于 Luong, Pham, 和 Manning(2015)的实现,它简化了 S2S 模型中的注意方法。
我们首先概述数据集和神经机器翻译所需的特殊记账类型。数据集是一个平行的语料库;它由成对的英语句子和相应的法语翻译组成。因为我们正在处理两个可能不同长度的序列,所以我们需要跟踪输入序列和输出序列的最大长度和词汇。在大多数情况下,这个例子是对完整读者在前几章中所看到的内容的直接扩展。
在覆盖数据集和簿记数据结构之后,我们通过参与源序列中的不同位置来遍历模型以及它如何生成目标序列。我们模型中的编码器使用双向GRU(bi-GRU)来计算源序列中每个位置的向量,这些向量由序列的所有部分通知。为此,我们使用PyTorch的PackedSequences数据结构。我们在“NMT模型中的编码和解码”中更深入地介绍了这一点。在“从序列捕获更多:注意力”中讨论的注意机制应用于bi-GRU的输出并用于调节目标序列生成。我们讨论模型的结果以及在“训练常规和结果”中可以改进的方法。
在覆盖数据集和簿记数据结构之后,我们通过参与源序列中的不同位置来遍历模型以及它如何生成目标序列。我们模型中的编码器使用双向 GRU(bi-GRU)来计算源序列中每个位置的向量,这些向量由序列的所有部分通知。为此,我们使用 PyTorch 的`PackedSequences`数据结构。我们在“NMT 模型中的编码和解码”中更深入地介绍了这一点。在“从序列捕获更多:注意力”中讨论的注意机制应用于 bi-GRU 的输出并用于调节目标序列生成。我们讨论模型的结果以及在“训练常规和结果”中可以改进的方法。
### Machine Translation Dataset
### 机器翻译数据集
对于此示例,我们使用来自Tatoeba Project的英语 - 法语句子对的数据集.数据预处理首先将所有句子设为小写,并将NLTK的英语和法语标记符应用于每个句子对。接下来,我们应用NLTK的特定于语言的单词标记生成器来创建标记列表。即使我们进行了进一步的计算,我们将在下一段中描述,但这个标记列表是一个预处理的数据集。
对于此示例,我们使用来自 Tatoeba Project 的英语 - 法语句子对的数据集.数据预处理首先将所有句子设为小写,并将 NLTK 的英语和法语标记符应用于每个句子对。接下来,我们应用 NLTK 的特定于语言的单词标记生成器来创建标记列表。即使我们进行了进一步的计算,我们将在下一段中描述,但这个标记列表是一个预处理的数据集。

@@ -93,8 +111,10 @@ ## 除了刚刚描述的标准预处理之外,我们还使用指定的语法模式列表来选择数据的子集,以简化学习问题。从本质上讲,这意味着我们将数据范围缩小到只有有限范围的句法模式。反过来,这意味着在训练期间,模型将看到更少的变化,并在更短的训练时间内具有更高的性能。

我们用来选择数据子集的句法模式是以“I am”,“he is ”,“she is”,“they are”,“you are”或“we are”开头的英语句子。数据集从135,842个句子对减少到13,062个句子对,系数为10.为了最终确定学习设置,我们将剩余的13,062个句子对分为70%训练,15%验证和15%测试分裂。从刚刚列出的语法开始的每个句子的比例通过首先按句子开始分组,从这些组创建分割,然后合并每个组的分割来保持不变。
我们用来选择数据子集的句法模式是以`I am`,`he is`,`she is`,`they are`,`you are`或`we are`开头的英语句子。数据集从 135,842 个句子对减少到 13,062 个句子对,系数为 10。为了最终确定学习设置,我们将剩余的 13,062 个句子对分为 70% 训练,15% 验证和 15% 测试分裂。从刚刚列出的语法开始的每个句子的比例通过首先按句子开始分组,从这些组创建分割,然后合并每个组的分割来保持不变。
### A Vectorization Pipeline for NMT
### NMT 向量化流水线
对源英语和目标法语句子进行矢量化需要比前面章节中看到的更复杂的管道。复杂性增加有两个原因。首先,源序列和目标序列在模型中具有不同的角色,属于不同的语言,并且以两种不同的方式进行矢量化。其次,作为使用PyTorch的PackedSequences的先决条件,我们按源句的长度对每个小批量进行排序。为了准备这两个复杂性,NMTVectorizer实例化了两个独立的SequenceVocabulary对象和两个最大序列长度的测量,如例8-1所示。 Example 8-1\. Constructing the NMTVectorizer
对源英语和目标法语句子进行向量化需要比前面章节中看到的更复杂的管道。复杂性增加有两个原因。首先,源序列和目标序列在模型中具有不同的角色,属于不同的语言,并且以两种不同的方式进行向量化。其次,作为使用 PyTorch 的`PackedSequences`的先决条件,我们按源句的长度对每个小批量进行排序。为了准备这两个复杂性,`NMTVectorizer`实例化了两个独立的`SequenceVocabulary`对象和两个最大序列长度的测量,如例 8-1 所示。
示例 8-1:构造`NMTVectorizer`
```py

@@ -149,3 +169,3 @@ class NMTVectorizer(object):

复杂性的第一个增加是处理源序列和目标序列的不同方式。源序列在开始时插入BEGIN-OF-SEQUENCE进行矢量化,并将END-OF-SEQUENCE标记添加到结尾。该模型使用bi-GRU为源句子中的每个标记创建摘要向量,并且这些摘要向量极大地受益于具有句子边界的指示。相反,目标序列被矢量化为两个副本,偏移一个标记:第一个副本需要BEGIN-OF-SEQUENCE标记,第二个副本需要END-OF-SEQUENCE标记。如果您从第7章回忆起来,序列预测任务需要在每个时间步骤观察输入令牌和输出令牌。 S2S模型中的解码器正在执行此任务,但增加了编码器上下文的可用性。为了解决这种复杂性,我们制定了核心矢量化方法_vectorize,无论它是源索引还是目标索引都无关紧要。然后,编写两个方法来分别处理源索引和目标索引。最后,使用NMTVectorizer.vectorize方法协调这些索引集,该方法是数据集调用的方法。例8-2显示了代码。
复杂性的第一个增加是处理源序列和目标序列的不同方式。源序列在开始时插入`BEGIN-OF-SEQUENCE`进行向量化,并将`END-OF-SEQUENCE`标记添加到结尾。该模型使用 bi-GRU 为源句子中的每个标记创建摘要向量,并且这些摘要向量极大地受益于具有句子边界的指示。相反,目标序列被向量化为两个副本,偏移一个标记:第一个副本需要`BEGIN-OF-SEQUENCE`标记,第二个副本需要`END-OF-SEQUENCE`标记。如果您从第 7 章回忆起来,序列预测任务需要在每个时间步骤观察输入标记和输出标记。 S2S 模型中的解码器正在执行此任务,但增加了编码器上下文的可用性。为了解决这种复杂性,我们制定了核心向量化方法`_vectorize`,无论它是源索引还是目标索引都无关紧要。然后,编写两个方法来分别处理源索引和目标索引。最后,使用`NMTVectorizer.vectorize`方法协调这些索引集,该方法是数据集调用的方法。例 8-2 显示了代码。

@@ -237,9 +257,9 @@ ```py

复杂性的第二次增加再次来自源序列。为了使用bi-GRU对源序列进行编码,我们使用PyTorch的PackedSequences数据结构。通常,可变长度序列的小批量数字表示为整数矩阵中的行,其中每个序列左对齐并且零填充以适应可变长度。 PackedSequences数据结构通过在每个时间步,一个接一个地连接序列的数据并知道每个时间步的序列数,将可变长度序列表示为数组,如图8-11所示。
复杂性的第二次增加再次来自源序列。为了使用 bi-GRU 对源序列进行编码,我们使用 PyTorch 的`PackedSequences`数据结构。通常,可变长度序列的小批量数字表示为整数矩阵中的行,其中每个序列左对齐并且零填充以适应可变长度。 `PackedSequences`数据结构通过在每个时间步,一个接一个地连接序列的数据并知道每个时间步的序列数,将可变长度序列表示为数组,如图 8-11 所示。
![example5](img/6ed27ac12afc752f06af3ce102e33503.jpg "图8-11。填充序列矩阵及其长度显示在左侧。填充矩阵是通过用0(零)对它们进行右边填充并将它们堆叠为行向量来表示可变长度序列的标准方式。在PyTorch语义中,我们可以将填充序列打包成一个terser表示,Packed Sequences,右侧显示batch_sizes。该表示允许GPU通过跟踪每个时间步中有多少序列(batch_sizes)来逐步执行序列。")
![example5](img/6ed27ac12afc752f06af3ce102e33503.jpg)
创建PackedSequence有两个先决条件:了解每个序列的长度,并按源序列的长度按降序对序列进行排序。为了反映这个新排序的矩阵,小批量中的剩余张量按相同的顺序排序,以便它们与源序列编码保持一致。在例8-3中,generate_batches函数被修改为generate_nmt_batches函数。
创建`PackedSequence`有两个先决条件:了解每个序列的长度,并按源序列的长度按降序对序列进行排序。为了反映这个新排序的矩阵,小批量中的剩余张量按相同的顺序排序,以便它们与源序列编码保持一致。在例 8-3 中,`generate_batches`函数被修改为`generate_nmt_batches`函数。
Example 8-3\. Generating minibatches for the NMT example
示例 8-3:为 NMT 示例生成小批量

@@ -264,7 +284,7 @@ ```py

### Encoding and Decoding in the NMT Model
### NMT 模型的编码和解码
在这个例子中,我们从源序列开始 - 一个英语句子 - 我们生成一个目标序列 - 相应的法语翻译。标准方法是使用“序列到序列模型,编码器 - 解码器模型和条件生成”中描述的编码器 - 解码器模型。在示例8-4和示例8-5中呈现的模型中,编码器首先将每个源序列映射到具有bi-GRU的矢量状态序列(参见“从序列中捕获更多:双向递归模型”)。然后,解码器以解码器的隐藏状态作为其初始隐藏状态开始,并使用注意机制(参见“从序列中捕获更多:注意”)来选择源序列中的不同信息以生成输出序列。在本节的其余部分,我们将更详细地解释此过程。
在这个例子中,我们从源序列开始 - 一个英语句子 - 我们生成一个目标序列 - 相应的法语翻译。标准方法是使用“序列到序列模型,编码器 - 解码器模型和条件生成”中描述的编码器 - 解码器模型。在示例 8-4 和示例 8-5 中呈现的模型中,编码器首先将每个源序列映射到具有 bi-GRU 的向量状态序列(参见“从序列中捕获更多:双向递归模型”)。然后,解码器以解码器的隐藏状态作为其初始隐藏状态开始,并使用注意机制(参见“从序列中捕获更多:注意”)来选择源序列中的不同信息以生成输出序列。在本节的其余部分,我们将更详细地解释此过程。
Example 8-4\. The NMTModel encapsulates and coordinates the encoder and decoder in a single forward method.
示例 8-4:`NMTModel`在单个前向方法中封装和协调了编码器和解码器

@@ -316,4 +336,6 @@ ```py

THE ENCODER Example 8-5\. The encoder embeds the source words and extracts features with a bi-GRU
## 编码器
示例 8-5:编码器嵌入了源单词和使用 bi-GPU 提取的特征
```py

@@ -367,9 +389,9 @@ class NMTEncoder(nn.Module):

通常,编码器将整数序列作为输入,并为每个位置创建特征向量。在该示例中,编码器的输出是这些向量以及用于制作特征向量的bi-GRU的最终隐藏状态。该隐藏状态用于在下一节中初始化解码器的隐藏状态。
通常,编码器将整数序列作为输入,并为每个位置创建特征向量。在该示例中,编码器的输出是这些向量以及用于制作特征向量的 bi-GRU 的最终隐藏状态。该隐藏状态用于在下一节中初始化解码器的隐藏状态。
深入了解编码器,我们首先使用嵌入层嵌入输入序列。通常,只需在嵌入层上设置padding_idx标志,我们就可以使模型处理可变长度序列,因为任何等于padding_idx的位置都会被赋予零值向量,该向量在优化期间不会更新。回想一下,这被称为面具。然而,在这种编码器 - 解码器模型中,掩蔽位置需要以不同方式处理,因为我们使用bi-GRU来编码源序列。主要原因是后向分量可能受到屏蔽位置的影响,其因子与在序列上开始之前遇到的屏蔽位置的数量成比例。
深入了解编码器,我们首先使用嵌入层嵌入输入序列。通常,只需在嵌入层上设置`padding_idx`标志,我们就可以使模型处理可变长度序列,因为任何等于`padding_idx`的位置都会被赋予零值向量,该向量在优化期间不会更新。回想一下,这被称为面具。然而,在这种编码器 - 解码器模型中,掩蔽位置需要以不同方式处理,因为我们使用 bi-GRU 来编码源序列。主要原因是后向分量可能受到屏蔽位置的影响,其因子与在序列上开始之前遇到的屏蔽位置的数量成比例。
为了处理bi-GRU中可变长度序列的掩码位置,我们使用PyTorch的PackedSequence数据结构。 PackedSequences源自CUDA如何允许以批处理格式处理可变长度序列。如果满足两个条件,则可以将任何零填充序列(例如示例8-6中所示的编码器中的嵌入源序列)转换为PackedSequence:提供每个序列的长度,并根据以下顺序对小批量进行排序。这些序列的长度。这在图8-11中以可视方式显示,因为它是一个复杂的主题,我们在例8-6及其输出中再次演示它.
为了处理 bi-GRU 中可变长度序列的掩码位置,我们使用 PyTorch 的`PackedSequence`数据结构。 `PackedSequences`源自 CUDA 如何允许以批量格式处理可变长度序列。如果满足两个条件,则可以将任何零填充序列(例如示例 8-6 中所示的编码器中的嵌入源序列)转换为`PackedSequence`:提供每个序列的长度,并根据以下顺序对小批量进行排序。这些序列的长度。这在图 8-11 中以可视方式显示,因为它是一个复杂的主题,我们在例 8-6 及其输出中再次演示它.
Example 8-6\. A simple demonstration of packed_padded_sequences and pad_packed_sequences
示例 8-6:`packed_padded_sequences`和`pad_packed_sequences`的简单演示

@@ -420,5 +442,5 @@ ```py

我们在生成每个minibatch时处理排序,如上一节所述。然后,如例8-7所示,通过传递嵌入的序列,序列的长度和表示第一个维度是批量维度的布尔标志来激发PyTorch的pack_padded_sequence函数。此函数的输出是PackedSequence。将得到的PackedSequence输入到bi-GRU中以为下游解码器创建状态向量。使用另一个布尔标志将bi-GRU的输出解压缩为完整张量,指示批处理在第一维上。解包操作,如图8-11所示,将每个屏蔽位置15设置为零值向量,保留下游计算的完整性。
我们在生成每个小批量时处理排序,如上一节所述。然后,如例 8-7 所示,通过传递嵌入的序列,序列的长度和表示第一个维度是批量维度的布尔标志来激发 PyTorch 的`pack_padded_sequence`函数。此函数的输出是`PackedSequence`。将得到的`PackedSequence`输入到 bi-GRU 中以为下游解码器创建状态向量。使用另一个布尔标志将 bi-GRU 的输出解压缩为完整张量,指示批量在第一维上。解包操作,如图 8-11 所示,将每个屏蔽位置 15 设置为零值向量,保留下游计算的完整性。
Example 8-7\. The NMT Decoder constructs a target sentence from the encoded source sentence
示例 8-7:NMT 解码器从编码的源句子中构造目标句子

@@ -520,12 +542,14 @@ ```py

在编码器利用其bi-GRU和打包 - 解包协调创建状态向量之后,解码器在时间步骤上迭代以生成输出序列。在功能上,这个循环应该看起来与第7章中的生成循环非常相似,但是有一些差异明显是Luong等人的注意方式的方法选择。首先,在每个时间步骤提供目标序列作为观察。通过使用GRUCell计算隐藏状态。通过将线性层应用于编码器bi-GRU的级联最终隐藏状态来计算初始隐藏状态.在每个时间步骤处对解码器GRU的输入是嵌入式输入令牌和最后时间步骤的上下文的级联向量。向量。上下文向量旨在捕获对该时间步骤有用的信息,并用于调节模型的输出。对于第一个步骤,上下文向量全部为0(零)以表示无上下文并且在数学上仅允许输入对GRU计算做出贡献。
在编码器利用其 bi-GRU 和打包 - 解包协调创建状态向量之后,解码器在时间步骤上迭代以生成输出序列。在功能上,这个循环应该看起来与第 7 章中的生成循环非常相似,但是有一些差异明显是 Luong 等人的注意方式的方法选择。首先,在每个时间步骤提供目标序列作为观察。通过使用`GRUCell`计算隐藏状态。通过将线性层应用于编码器 bi-GRU 的级联最终隐藏状态来计算初始隐藏状态.在每个时间步骤处对解码器 GRU 的输入是嵌入输入标记和最后时间步骤的上下文的级联向量。向量。上下文向量旨在捕获对该时间步骤有用的信息,并用于调节模型的输出。对于第一个步骤,上下文向量全部为 0(零)以表示无上下文并且在数学上仅允许输入对 GRU 计算做出贡献。
使用新的隐藏状态作为查询向量,使用当前时间步骤的关注机制创建一组新的上下文向量。这些上下文向量与隐藏状态连接以创建表示该时间步长处的解码信息的向量。该解码信息状态向量用在分类器(在这种情况下,简单的线性层)中以创建预测向量score_for_y_t_index。这些预测矢量可以使用softmax函数转换为输出词汇表上的概率分布,或者它们可以与交叉熵损失一起使用以优化地面实况目标。在我们转向如何在训练例程中使用预测向量之前,我们首先检查注意力计算本身。
使用新的隐藏状态作为查询向量,使用当前时间步骤的关注机制创建一组新的上下文向量。这些上下文向量与隐藏状态连接以创建表示该时间步长处的解码信息的向量。该解码信息状态向量用在分类器(在这种情况下,简单的线性层)中以创建预测向量`score_for_y_t_index`。这些预测向量可以使用 softmax 函数转换为输出词汇表上的概率分布,或者它们可以与交叉熵损失一起使用以优化地面实况目标。在我们转向如何在训练例程中使用预测向量之前,我们首先检查注意力计算本身。
A CLOSER LOOK AT ATTENTION 了解注意机制在此示例中的工作方式非常重要。回想一下前面的部分,可以使用查询,键和值来描述注意机制。分数函数将查询向量和关键向量作为输入,以计算在值向量中选择的一组权重。在这个例子中,我们使用点积评分函数,但它不是唯一的.在这个例子中,解码器的隐藏状态被用作查询向量,编码器状态向量集是关键和值向量。
## 注意力的详细解释
解码器隐藏状态与编码器状态中的矢量的点积为编码序列中的每个项创建标量。在使用softmax函数时,这些标量变为编码器状态中的矢量的概率分布。这些概率用于在将编码器状态向量加在一起之前对其进行加权,以产生每个批次项目的单个向量。总而言之,允许解码器隐藏状态在每个时间步骤优先加权编码器状态。这就像一个聚光灯,使模型能够学习如何突出显示生成输出序列所需的信息。我们在例8-8中演示了这种版本的注意机制。第一个尝试详细说明操作。此外,它使用视图操作插入大小为1的维度,以便可以针对另一个张量广播张量。在terse_attention版本中,视图操作被更常用的练习替换,取消压缩。此外,不是将元素和求和相乘,而是使用更有效的matmul运算。
了解注意机制在此示例中的工作方式非常重要。回想一下前面的部分,可以使用查询,键和值来描述注意机制。分数函数将查询向量和关键向量作为输入,以计算在值向量中选择的一组权重。在这个例子中,我们使用点积评分函数,但它不是唯一的.在这个例子中,解码器的隐藏状态被用作查询向量,编码器状态向量集是关键和值向量。
Example 8-8\. Attention that does element-wise multiplication and summing more explicitly
解码器隐藏状态与编码器状态中的向量的点积为编码序列中的每个项创建标量。在使用 softmax 函数时,这些标量变为编码器状态中的向量的概率分布。这些概率用于在将编码器状态向量加在一起之前对其进行加权,以产生每个批次项目的单个向量。总而言之,允许解码器隐藏状态在每个时间步骤优先加权编码器状态。这就像一个聚光灯,使模型能够学习如何突出显示生成输出序列所需的信息。我们在例 8-8 中演示了这种版本的注意机制。第一个尝试详细说明操作。此外,它使用视图操作插入大小为 1 的维度,以便可以针对另一个张量广播张量。在`terse_attention`版本中,视图操作被更常用的练习替换,取消压缩。此外,不是将元素和求和相乘,而是使用更有效的`matmul`运算。
示例 8-8:注意力更加显式地执行逐元素乘法和求和
```py

@@ -567,5 +591,5 @@ def verbose_attention(encoder_state_vectors, query_vector):

代码有三个主要修改,以使模型在训练期间采样自己的预测。首先,初始索引更明确地作为BEGIN-OF-SEQUENCE标记索引。其次,为生成循环中的每个步骤绘制随机样本,如果随机样本小于样本概率,则在该迭代期间使用模型的预测。最后,第三个实际采样本身在条件if下使用use_sample。在示例8-9中,注释行显示了如何使用最大预测,而未注释行显示了如何以与其概率成比例的速率实际采样索引。
代码有三个主要修改,以使模型在训练期间采样自己的预测。首先,初始索引更明确地作为`BEGIN-OF-SEQUENCE`标记索引。其次,为生成循环中的每个步骤绘制随机样本,如果随机样本小于样本概率,则在该迭代期间使用模型的预测。最后,第三个实际采样本身在条件`if`下使用`use_sample`。在示例 8-9 中,注释行显示了如何使用最大预测,而未注释行显示了如何以与其概率成比例的速率实际采样索引。
Example 8-9\. The decoder with a sampling procedures (in bold) built into the forward pass
示例 8-9:在前向过程中构建的带有采样过程的解码器(粗体)

@@ -630,42 +654,40 @@ ```py

### Training Routine and Results
### 训练例程和结果
此示例的训练例程几乎与前面章节中介绍的训练例程相同。对于固定数量的历元,我们在称为minibatches的块中迭代数据集。然而,这里的每个小批量由四个张量组成:源序列的整数矩阵,目标序列的两个整数矩阵,以及源序列长度的整数向量。两个靶序列矩阵是靶序列偏移1并用BEGIN-OF-SEQUENCE令牌填充以充当靶序列观察,或END-OF-SEQUENCE令牌充当靶序列预测标记。该模型将源序列和​​目标序列观察作为输入,以产生目标序列预测。在损失函数中使用目标序列预测标签来计算交叉熵损失,然后将其反向传播到每个模型参数以使其知道其梯度。然后调用优化器并将每个模型参数更新一些与梯度成比例的量。
此示例的训练例程几乎与前面章节中介绍的训练例程相同。对于固定数量的历元,我们在称为小批量的块中迭代数据集。然而,这里的每个小批量由四个张量组成:源序列的整数矩阵,目标序列的两个整数矩阵,以及源序列长度的整数向量。两个靶序列矩阵是靶序列偏移 1 并用`BEGIN-OF-SEQUENCE`标记填充以充当靶序列观察,或`END-OF-SEQUENCE`标记充当靶序列预测标记。该模型将源序列和​​目标序列观察作为输入,以产生目标序列预测。在损失函数中使用目标序列预测标签来计算交叉熵损失,然后将其反向传播到每个模型参数以使其知道其梯度。然后调用优化器并将每个模型参数更新一些与梯度成比例的量。
除了数据集的训练部分上的循环之外,验证部分上还有一个循环。验证分数用作模型改进的偏差较小的度量。该过程与训练例程相同,只是模型处于eval模式并且模型未相对于验证数据更新。
除了数据集的训练部分上的循环之外,验证部分上还有一个循环。验证分数用作模型改进的偏差较小的度量。该过程与训练例程相同,只是模型处于求值模式并且模型未相对于验证数据更新。
在训练模型之后,测量性能成为一个重要而重要的问题。在“评估序列生成模型”中描述了几个序列生成评估度量,但是诸如测量预测句子和参考语句之间的n-gram重叠的BLEU的度量已经成为机器翻译领域的标准。聚合结果的评估代码已被省略,但您可以在本书的随附代码库中找到它.在代码中,模型的输出与源句子,参考目标语句和注意概率矩阵聚合在一起。例。最后,为每对源和生成的句子计算BLEU-4。
在训练模型之后,测量性能成为一个重要而重要的问题。在“评估序列生成模型”中描述了几个序列生成评估度量,但是诸如测量预测句子和参考语句之间的 N 元组的重叠的 BLEU 的度量已经成为机器翻译领域的标准。聚合结果的评估代码已被省略,但您可以在本书的随附代码库中找到它.在代码中,模型的输出与源句子,参考目标语句和注意概率矩阵聚合在一起。例。最后,为每对源和生成的句子计算 BLEU-4。
为了定性评估模型的工作情况,我们将注意概率矩阵可视化为源和生成文本之间的对齐。然而,值得注意的是,最近的研究表明,基于注意力的对齐与经典机器翻译中的对齐并不完全相同。基于注意力的对齐分数可以指示解码器的有用信息,例如在生成输出动词时参与句子的主语(Koehn和Knowles,2017),而不是单词和短语之间的对齐指示翻译同义词。
为了定性评估模型的工作情况,我们将注意概率矩阵可视化为源和生成文本之间的对齐。然而,值得注意的是,最近的研究表明,基于注意力的对齐与经典机器翻译中的对齐并不完全相同。基于注意力的对齐分数可以指示解码器的有用信息,例如在生成输出动词时参与句子的主语(Koehn 和 Knowles,2017),而不是单词和短语之间的对齐指示翻译同义词。
我们比较了我们模型的两个版本,它们与目标句子的交互方式不同。第一个版本使用提供的目标序列作为解码器中每个时间步的输入。第二个版本使用预定采样,以允许模型将其自己的预测视为解码器中的输入。这有利于强制模型优化其自身的错误。表8-1显示了BLEU分数。重要的是要记住,为了便于训练,我们选择了标准NMT任务的简化版本,这就是为什么分数似乎高于您在研究文献中通常会发现的分数。虽然第二个模型,即具有预定采样的模型,具有更高的BLEU分数,但是得分相当接近。但这些得分究竟意味着什么呢?为了研究这个问题,我们需要定性地检查模型。
我们比较了我们模型的两个版本,它们与目标句子的交互方式不同。第一个版本使用提供的目标序列作为解码器中每个时间步的输入。第二个版本使用预定采样,以允许模型将其自己的预测视为解码器中的输入。这有利于强制模型优化其自身的错误。表 8-1 显示了 BLEU 分数。重要的是要记住,为了便于训练,我们选择了标准 NMT 任务的简化版本,这就是为什么分数似乎高于您在研究文献中通常会发现的分数。虽然第二个模型,即具有预定采样的模型,具有更高的 BLEU 分数,但是得分相当接近。但这些得分究竟意味着什么呢?为了研究这个问题,我们需要定性地检查模型。
![test](img/d4bdc63406e2c1068c0b8f5935863ac7.jpg "表8 - 1。先前显示的两个模型的BLEU得分;BLEU被计算为1-、2-、3-和4克重叠的简单平均值")
![test](img/d4bdc63406e2c1068c0b8f5935863ac7.jpg)
对于我们更深入的检查,我们绘制注意力分数,以查看它们是否在源句和目标句之间提供任何类型的对齐信息。我们发现在这次检查中两个模型之间形成了鲜明的对比.图8-12显示了具有预定采样的模型的每个解码器时间步长的注意概率分布。在该模型中,注意权重对于从数据集的验证部分采样的句子排列得相当好。
对于我们更深入的检查,我们绘制注意力分数,以查看它们是否在源句和目标句之间提供任何类型的对齐信息。我们发现在这次检查中两个模型之间形成了鲜明的对比.图 8-12 显示了具有预定采样的模型的每个解码器时间步长的注意概率分布。在该模型中,注意权重对于从数据集的验证部分采样的句子排列得相当好。
![test1](img/8ceee1f109cfad6089fa1ae474c8725c.jpg "图8-12\. 具有预定采样的模型的注意力权重矩阵被绘制为模型性能的定性评估")
![test1](img/8ceee1f109cfad6089fa1ae474c8725c.jpg)
## Summary
## 总结
本章重点介绍了在所谓的条件生成模型的条件上下文中生成序列输出。当条件上下文本身来自另一个序列时,我们将其称为序列到序列或S2S模型。我们还讨论了S2S模型如何成为编码器 - 解码器模型的特例。为了充分利用序列,我们讨论了第6章和第7章中讨论的序列模型的结构变体,特别是双向模型。我们还学习了如何结合注意机制来有效捕获更长距离的背景。最后,我们讨论了如何评估序列到序列模型,并使用端到端机器翻译示例进行演示。到目前为止,我们已将本书的每一章专门用于特定的网络架构。在下一章中,我们将前面的所有章节结合在一起,并查看如何使用各种模型体系结构的综合构建许多真实系统的示例。
本章重点介绍了在所谓的条件生成模型的条件上下文中生成序列输出。当条件上下文本身来自另一个序列时,我们将其称为序列到序列或 S2S 模型。我们还讨论了 S2S 模型如何成为编码器 - 解码器模型的特例。为了充分利用序列,我们讨论了第 6 章和第 7 章中讨论的序列模型的结构变体,特别是双向模型。我们还学习了如何结合注意机制来有效捕获更长距离的背景。最后,我们讨论了如何评估序列到序列模型,并使用端到端机器翻译示例进行演示。到目前为止,我们已将本书的每一章专门用于特定的网络架构。在下一章中,我们将前面的所有章节结合在一起,并查看如何使用各种模型体系结构的综合构建许多真实系统的示例。
## References
## 参考
1. Yoshua Bengio, Patrice Simard, and Paolo Frasconi. (1994). “Learning long-term dependencies with gradient descent is difficult.” IEEE transactions on neural networks.
1. `Yoshua Bengio, Patrice Simard, and Paolo Frasconi. (1994). "Learning long-term dependencies with gradient descent is difficult." IEEE transactions on neural networks.`
2. Kishore Papineni, Salim Roukos, Todd Ward, and Wei-Jing Zhu. (2002) “BLEU: a method for automatic evaluation of machine translation.” In Proceedings ACL.. Hal Daumé III, John Langford, Daniel Marcu. (2009). “Search-based Structured Prediction.” In Machine Learning Journal.
2. `Kishore Papineni, Salim Roukos, Todd Ward, and Wei-Jing Zhu. (2002) "BLEU: a method for automatic evaluation of machine translation." In Proceedings ACL.. Hal Daumé III, John Langford, Daniel Marcu. (2009). "Search-based Structured Prediction." In Machine Learning Journal.`
3. Samy Bengio, Oriol Vinyals, Navdeep Jaitly, Noam Shazeer. “Scheduled Sampling for Sequence Prediction with Recurrent Neural Networks.” In Proceedings of NIPS 2015.
3. `Samy Bengio, Oriol Vinyals, Navdeep Jaitly, Noam Shazeer. "Scheduled Sampling for Sequence Prediction with Recurrent Neural Networks." In Proceedings of NIPS 2015.`
4. Minh-Thang Luong, Hieu Pham, and Christopher D. Manning. (2015). “Effective Approaches to Attention-based Neural Machine Translation.” In Proceedings of EMNLP.
4. `Minh-Thang Luong, Hieu Pham, and Christopher D. Manning. (2015). "Effective Approaches to Attention-based Neural Machine Translation." In Proceedings of EMNLP.`
5. Phong Le and Willem Zuidema. (2016). “Quantifying the Vanishing Gradient and Long Distance Dependency Problem in Recursive Neural Networks and Recursive LSTMs.” Proceedings of the 1st Workshop on Representation Learning for NLP.
5. `Phong Le and Willem Zuidema. (2016). "Quantifying the Vanishing Gradient and Long Distance Dependency Problem in Recursive Neural Networks and Recursive LSTMs." Proceedings of the 1st Workshop on Representation Learning for NLP.`
6. Philipp Koehn, Rebecca Knowles. (2017). “Six Challenges for Neural Machine Translation.” In Proceedings of the First Workshop on Neural Machine Translation.
6. `Philipp Koehn, Rebecca Knowles. (2017). "Six Challenges for Neural Machine Translation." In Proceedings of the First Workshop on Neural Machine Translation.`
7. Graham Neubig. (2017). “Neural Machine Translation and Sequence-to-Sequence Models: A Tutorial.” arXiv:1703.01619.
7. `Graham Neubig. (2017). "Neural Machine Translation and Sequence-to-Sequence Models: A Tutorial." arXiv:1703.01619.`
8. Ashish Vaswani, Noam Shazeer, Niki Parmar, Jakob Uszkoreit, Llion Jones, Aidan N. Gomez, Łukasz Kaiser, and Illia Polosukhin. (2017). “Attention is all you need.” In Proceedings of NIPS.
In this chapter, we reserve the symbol ϕ for encodings. This is not possible for streaming applications, but a large number of practical applications of NLP happen in a batch (non-streaming) context anyways. Sentences like the one in this example are called “Garden Path Sentences.” Such sentences are more common than one would imagine, for e.g., newspaper headlines use such constructs regularly. See [https://en.wikipedia.org/wiki/Garden_path_sentence](https://en.wikipedia.org/wiki/Garden_path_sentence). Consider the two meanings of “duck” (i) □ (noun, quack quack) and (ii) evade (verb) The terminology key, values, and query can be quite confusing for the beginner, but we introduce them here anyway because they have now become a standard. It is worth reading this section (8.3.1) a few times until these concepts become clear. The “Key, Value, Query” terminology comes in because the attention was initially thought of as a search task. For an extended review of these concepts and attention in general, visit [https://lilianweng.github.io/lil-log/2018/06/24/attention-attention.html](https://lilianweng.github.io/lil-log/2018/06/24/attention-attention.html). So much that the original 2002 paper that proposed that BLEU received a “test of the time award” in 2018. For an example, see [https://github.com/nltk/nltk/blob/develop/nltk/translate/bleu_score.py](https://github.com/nltk/nltk/blob/develop/nltk/translate/bleu_score.py). SacreBLEU is the standard when it comes to machine translation evaluation. The dataset was retrieved from [http://www.manythings.org/anki/](http://www.manythings.org/anki/). We also include the cases in which these subject-verb pairs are contractions, such as “I’m”, “we’re”, and “he’s”. This simply means that the model will be able to see the entire dataset 10 times faster. It doesn’t exactly follow that the convergence will happen in one-tenth the time, because it could be that the model needs to see this dataset for a smaller number of epochs, or some other confounding factor. Sorting the sequences in order takes advantage of a low-level CUDA primitive for RNNs. You should try to convince yourself of this by either visualizing the computations or drawing them out. As a hint, consider the single recurrent step: the input and last hidden are weighted and added together with the bias. If the input is all 0’s, what effect does the bias have on the output? We utilize the describe function shown in section 1.4. Starting from left to right on the sequence dimension, any position past the known length of the sequence is assumed to be masked. The Vectorizer prepends the BEGIN-OF-SEQUENCE token to the sequence, so the first observation is always a special token indicating the boundary. See section 7.3 of Graham Neubig’s tutorial for a discussion on connecting encoders and decoders in neural machine translation. See (Neubig, 2017). We refer you to Luong, Pham, and Manning (2011), in which they outline three different scoring functions. Each batch item is a sequence and the probabilities for each sequence sum to 1. Broadcasting happens when a tensor has a dimension of size 1\. Let this tensor be called Tensor A. When Tensor A is used in an element-wise mathematical operation (such as addition or subtraction) with another tensor called Tensor B, its shape (the number of elements on each dimension) should be identical except for the dimension with size 1\. The operation of Tensor A on Tensor B is repeated for each position in Tensor B. If Tensor A has a shape (10, 1, 10) and Tensor B has a shape (10, 5, 10), A+B will repeat the addition of Tensor A for each of the five positions in Tensor B. We refer you to two papers on this topic: “Search-based Structured Prediction” by Daumé, Langford, Marcu, and “Scheduled Sampling for Sequence Prediction with Recurrent Neural Networks” by Bengio, Vinyals, Jaitly, Shazeer (2015). If you’re familiar with Monte Carlo sampling for optimization techniques such as Markov Chain Monte Carlo, you will recognize this pattern. Primarily, this is because gradient descent and automatic differentiation is an elegant abstraction between model definitions and their optimization. [https://github.com/joosthub/nlpbook/chapters/chapter_8/example_8_5](https://github.com/joosthub/nlpbook/chapters/chapter_8/example_8_5) We omit a plot for the first model because it attended to only the final state in the encoder RNN. As noted by Koehn and Knowles (2017), the attention weights are endemic of many different situations. We suspect the attention weights in the first model did not need to rely on attention as much because the information it needed was already encoded in the states of the encoder GRU.
8. `Ashish Vaswani, Noam Shazeer, Niki Parmar, Jakob Uszkoreit, Llion Jones, Aidan N. Gomez, Łukasz Kaiser, and Illia Polosukhin. (2017). "Attention is all you need." In Proceedings of NIPS.`

@@ -1,103 +0,117 @@

# Chapter 9.经典, 前沿和后续步骤
# 九、经典, 前沿和后续步骤
> 本文标题:[Natural-Language-Processing-with-PyTorch(九)](https://yifdu.github.io/2018/12/28/Natural-Language-Processing-with-PyTorch%EF%BC%88%E4%B9%9D%EF%BC%89/)
> 本文标题:[Natural-Language-Processing-with-PyTorch(九)](https://yifdu.github.io/2018/12/28/Natural-Language-Processing-with-PyTorch%EF%BC%88%E4%B9%9D%EF%BC%89/)
>
> 文章作者:[Yif Du](https://yifdu.github.io/ "访问 Yif Du 的个人博客")
> 文章作者:[Yif Du](https://yifdu.github.io/ "访问 Yif Du 的个人博客")
>
> 发布时间:2018年12月28日 - 11:12
> 发布时间:2018 年 12 月 28 日 - 11:12
>
> 最后更新:2018年12月28日 - 11:12
> 最后更新:2018 年 12 月 28 日 - 11:12
>
> 原始链接:[http://yifdu.github.io/2018/12/28/Natural-Language-Processing-with-PyTorch(九)/](https://yifdu.github.io/2018/12/28/Natural-Language-Processing-with-PyTorch%EF%BC%88%E4%B9%9D%EF%BC%89/)
> 原始链接:[http://yifdu.github.io/2018/12/28/Natural-Language-Processing-with-PyTorch(九)/](https://yifdu.github.io/2018/12/28/Natural-Language-Processing-with-PyTorch%EF%BC%88%E4%B9%9D%EF%BC%89/)
>
> 许可协议: [署名-非商业性使用-禁止演绎 4.0 国际](https://creativecommons.org/licenses/by-nc-nd/4.0/) 转载请保留原文链接及作者。
> 许可协议:[署名-非商业性使用-禁止演绎 4.0 国际](https://creativecommons.org/licenses/by-nc-nd/4.0/) 转载请保留原文链接及作者。
在本章中,我们将从整本书的角度回顾前面的章节,并了解本书中讨论的看似独立的主题是如何相互依赖的,以及研究人员如何将这些想法混合和匹配以解决手头的问题。我们还总结了自然语言处理(NLP)中的一些经典主题,我们无法在这些封面之间进行深入讨论。最后,我们指出了该领域的前沿,截至2018年。在经验丰富的NLP和深度学习等快速发展的领域,我们必须学习新的想法并使自己保持最新状态。我们致力于学习如何在NLP中学习新主题。
在本章中,我们将从整本书的角度回顾前面的章节,并了解本书中讨论的看似独立的主题是如何相互依赖的,以及研究人员如何将这些想法混合和匹配以解决手头的问题。我们还总结了自然语言处理(NLP)中的一些经典主题,我们无法在这些封面之间进行深入讨论。最后,我们指出了该领域的前沿,截至 2018 年。在经验丰富的 NLP 和深度学习等快速发展的领域,我们必须学习新的想法并使自己保持最新状态。我们致力于学习如何在 NLP 中学习新主题。
## What Have We Learned so Far?
## 我们到目前为止学习了什么?
我们从监督学习范式开始,以及我们如何使用计算图形抽象将复杂的想法编码为可以通过反向传播进行训练的模型。 PyTorch是我们选择的计算框架。编写NLP书籍存在风险,该书使用深度学习将文本输入视为要输入黑盒子的“数据”。在第2章中,我们介绍了NLP和语言学的一些基本概念,为本书的其余部分奠定了基础。激活函数,损失函数和基于梯度的监督学习优化以及“训练 - 评估循环”等基本概念在本章的其他章节中变得非常方便。我们研究了前馈网络的两个例子 - 多层感知器(MLP)和卷积网络。我们看到了L1,L2规范和丢包等正规化机制,使网络更加健壮。 MLP能够在其隐藏层中捕获类似ngram的关系,但是它们效率低下。另一方面,卷积网络使用称为“参数绑定”或“参数共享”的思想以计算有效的方式学习这个子结构。在第6章中,我们看到了循环网络如何能够捕获远程依赖性跨越时间也只有很少的参数。你可以说卷积网络正在跨越空间绑定参数,而循环网络正在跨时间绑定参数。我们看到了三种复发网络的变体,从Elman Recurrent Neural网络(RNN)和门控变体如长短期记忆(LSTM)和门控复发单位(GRU)开始。我们还了解了如何在预测或序列标记设置中使用循环网络,其中在输入的每个时间步骤预测输出。最后,我们介绍了一类称为编码器 - 解码器模型的模型,并研究了序列到序列模型作为解决条件生成问题(如机器翻译)的示例。我们在PyTorch中完成了许多这些主题的端到端示例。
我们从监督学习范式开始,以及我们如何使用计算图形抽象将复杂的想法编码为可以通过反向传播进行训练的模型。 PyTorch 是我们选择的计算框架。编写 NLP 书籍存在风险,该书使用深度学习将文本输入视为要输入黑盒子的“数据”。在第 2 章中,我们介绍了 NLP 和语言学的一些基本概念,为本书的其余部分奠定了基础。激活函数,损失函数和基于梯度的监督学习优化以及“训练 - 评估循环”等基本概念在本章的其他章节中变得非常方便。我们研究了前馈网络的两个例子 - 多层感知器(MLP)和卷积网络。我们看到了 L1,L2 规范和丢包等正规化机制,使网络更加健壮。 MLP 能够在其隐藏层中捕获类似 N 元组的关系,但是它们效率低下。另一方面,卷积网络使用称为“参数绑定”或“参数共享”的思想以计算有效的方式学习这个子结构。在第 6 章中,我们看到了循环网络如何能够捕获远程依赖性跨越时间也只有很少的参数。你可以说卷积网络正在跨越空间绑定参数,而循环网络正在跨时间绑定参数。我们看到了三种复发网络的变体,从 Elman 循环神经网络(RNN)和门控变体如长短期记忆(LSTM)和门控复发单位(GRU)开始。我们还了解了如何在预测或序列标记设置中使用循环网络,其中在输入的每个时间步骤预测输出。最后,我们介绍了一类称为编码器 - 解码器模型的模型,并研究了序列到序列模型作为解决条件生成问题(如机器翻译)的示例。我们在 PyTorch 中完成了许多这些主题的端到端示例。
## Timeless Topics in NLP
## NLP 中的永恒主题
NLP比单本书范围内的内容还多,本书也不例外。在第2章中,我们确定了NLP中的一些核心术语和任务。我们在其余章节中介绍了许多NLP任务,但是在这里我们简要提到一些我们无法部分或全部涵盖的重要主题,因为我们的范围仅限于初始说明书。
NLP 比单本书范围内的内容还多,本书也不例外。在第 2 章中,我们确定了 NLP 中的一些核心术语和任务。我们在其余章节中介绍了许多 NLP 任务,但是在这里我们简要提到一些我们无法部分或全部涵盖的重要主题,因为我们的范围仅限于初始说明书。
### Dialogue and Interactive Systems
### 对话和交互系统
计算机和人类之间的无缝对话被认为是计算的圣杯,并激发了图灵测验和勒伯纳奖。自人工智能(人工智能)早期以来,NLP一直与对话系统相关联,并通过虚构系统在流行文化中普及,如星际迷航中的USS Enterprise上的主计算机和电影2001:A Space Odyssey中的HAL 9000。 1对话和更广泛的交互系统设计领域是一个肥沃的研究领域,亚马逊的Alexa,Apple的Siri和Google的助手等近期产品取得了成功。对话系统可以是开放域(问我什么)或封闭域(例如,航班预订,汽车导航)。该领域的一些重要研究课题包括我们如何对对话行为进行建模,对话背景(见图9-1)和对话状态?我们如何建立多模式对话系统(例如,语音和视觉或文本和视觉输入)?系统如何识别用户意图?我们如何为用户的偏好建模并生成针对用户量身定制的响应?如何回应更人性化的声音?例如,最近的生产对话系统已经开始将诸如“嗯”和“呃”之类的不流畅结合到响应中,以使系统看起来不那么机器人。
计算机和人类之间的无缝对话被认为是计算的圣杯,并激发了图灵测验和勒伯纳奖。自人工智能(人工智能)早期以来,NLP 一直与对话系统相关联,并通过虚构系统在流行文化中普及,如星际迷航中的 USS Enterprise 上的主计算机和电影 2001:A Space Odyssey 中的 HAL 9000。 1 对话和更广泛的交互系统设计领域是一个肥沃的研究领域,亚马逊的 Alexa,Apple 的 Siri 和 Google 的助手等近期产品取得了成功。对话系统可以是开放域(问我什么)或封闭域(例如,航班预订,汽车导航)。该领域的一些重要研究课题包括我们如何对对话行为进行建模,对话背景(见图 9-1)和对话状态?我们如何建立多模式对话系统(例如,语音和视觉或文本和视觉输入)?系统如何识别用户意图?我们如何为用户的偏好建模并生成针对用户量身定制的响应?如何回应更人性化的声音?例如,最近的生产对话系统已经开始将诸如“嗯”和“呃”之类的不流畅结合到响应中,以使系统看起来不那么机器人。
![pic1](img/6978bd3cac4da121ee87ba11b5afff3d.jpg "图9-1。实施对话系统(使用Apple的Siri)。注意对话系统如何维护上下文以回答后续问题;也就是说,它知道将“他们”映射到巴拉克奥巴马的女儿身上。")
![pic1](img/6978bd3cac4da121ee87ba11b5afff3d.jpg)
### Discourse
### 话语
话语涉及理解文本文档的部分整体性质。例如,话语解析的任务涉及理解两个句子在上下文中如何彼此相关。表9-1给出了Penn Discourse Treebank(PDTB)的一些例子来说明这项任务。
话语涉及理解文本文档的部分整体性质。例如,话语解析的任务涉及理解两个句子在上下文中如何彼此相关。表 9-1 给出了 Penn Discourse Treebank(PDTB)的一些例子来说明这项任务。
Table 9-1\. Examples from the CoNLL 2015 Shallow Discourse Processing task ![pic2](img/b9468b8fa88bae589f119466a2354398.jpg) 理解话语还涉及解决其他问题,如回指解析和转喻检测。在Anaphora Resolution中,我们希望将代词的出现解析为它们所引用的实体。如图9-2所示,这可能成为一个复杂的问题。 ![pic3](img/2aaf2adfdd3fa9a320c69390bb6cc52e.jpg "图9-2。回指解决方案的一些问题。在例子(a)中,“它”是指狗还是骨头?在示例(b)中,“它”既不是指它们也不是指它们。在实施例(c)和(d)中,“It”分别指玻璃和啤酒。了解啤酒更有可能起泡作用,对于解决此类指称(选择偏好)至关重要。") 对象可以是转喻,如下例所示: “Beijing imposed trade tariffs in response to tariffs on Chinese goods.”
Table 9-1\. Examples from the CoNLL 2015 Shallow Discourse Processing task
![pic2](img/b9468b8fa88bae589f119466a2354398.jpg)
理解话语还涉及解决其他问题,如回指解析和转喻检测。在 Anaphora Resolution 中,我们希望将代词的出现解析为它们所引用的实体。如图 9-2 所示,这可能成为一个复杂的问题。
![pic3](img/2aaf2adfdd3fa9a320c69390bb6cc52e.jpg)
对象可以是转喻,如下例所示:`Beijing imposed trade tariffs in response to tariffs on Chinese goods.`
北京在这里指的不是中国政府的所在地。有时,成功解决所指对象可能需要使用知识库。
### Information Extraction and Text Mining
### 信息提取和文本挖掘
该行业中遇到的常见问题类别之一涉及信息提取。我们如何从文本中提取实体(人名,产品名称等),事件和关系?我们如何将文本中的实体提及映射到知识库中的条目(又称实体发现,实体链接,插槽填充)?3我们如何首先构建和维护该知识库(知识库群体)?这些是在不同背景下的信息提取研究中经常回答的一些问题。
该行业中遇到的常见问题类别之一涉及信息提取。我们如何从文本中提取实体(人名,产品名称等),事件和关系?我们如何将文本中的实体提及映射到知识库中的条目(又称实体发现,实体链接,插槽填充)?我们如何首先构建和维护该知识库(知识库群体)?这些是在不同背景下的信息提取研究中经常回答的一些问题。
### Document Analysis and Retrieval
### 文档分析和检索
另一种常见的行业NLP问题包括理解大量文档。我们如何从文档中提取主题(主题建模)?我们如何更智能地索引和搜索文档?我们如何理解搜索查询(查询解析)?我们如何为大型集合生成摘要?
另一种常见的行业 NLP 问题包括理解大量文档。我们如何从文档中提取主题(主题建模)?我们如何更智能地索引和搜索文档?我们如何理解搜索查询(查询解析)?我们如何为大型集合生成摘要?
NLP技术的范围和适用性很广,事实上,NLP技术可以应用于存在非结构化或半结构化数据的任何地方。作为一个例子,我们将您介绍给Dill等人。 (2007),他们应用自然语言解析技术来解释蛋白质折叠。
NLP 技术的范围和适用性很广,事实上,NLP 技术可以应用于存在非结构化或半结构化数据的任何地方。作为一个例子,我们将您介绍给 Dill 等人。 (2007),他们应用自然语言解析技术来解释蛋白质折叠。
## Frontiers in NLP
## NLP 的前沿
当该领域正在进行快速创新时,写一篇题为“NLP前沿”的部分似乎是一件愚蠢的事。但是,我们想让您一瞥2018年秋季的最新趋势:
当该领域正在进行快速创新时,写一篇题为“NLP 前沿”的部分似乎是一件愚蠢的事。但是,我们想让您一瞥 2018 年秋季的最新趋势:
1. 将经典NLP文献引入可微学习范式 NLP领域已有几十年的历史,尽管深度学习领域只有几年的历史。许多创新似乎都在研究新的深度学习(可微学习)范式下的传统方法和任务。阅读经典NLP论文(我们建议阅读它们!)时,一个很好的问题是作者试图学习什么?什么是输入/输出表示?我们如何通过前面章节中学到的技术简化这一过程?
2. 模型的组合性原则 在本书中,我们讨论了NLP,MLP,CNN,序列模型,序列到序列模型和基于注意力的模型的不同类型的深度学习架构。值得注意的是,尽管我们单独讨论了这些模型,但纯粹是出于教学原因。在文献中看到的一个趋势是组合不同的架构来完成工作。例如,您可以在单词的字符上编写卷积网络,然后在该表示上写入LSTM,并且通过MLP完成LSTM编码的最终分类。能够根据任务需求组合地组合不同的架构是使深度学习成功的最有力的想法之一。
3. 序列的卷积 我们在序列建模中看到的最近趋势是使用卷积运算完全模拟序列。作为完全卷积机器翻译模型的一个例子,参见Gehring等人。 (2018)。解码步骤使用去卷积操作。这是有利的,因为可以使用全卷积模型显着加速训练。
4. Attention is all you need 最近的另一个趋势是用注意机制取代卷积(Vaswani等,2017)。使用注意机制,特别是称为自我关注和多头注意的变体,您基本上可以捕获通常使用RNN和CNN建模的远程依赖关系。
5. Transfer learning 迁移学习是学习一项任务的表示和使用表示来改进另一项任务的学习的任务。在最近神经网络的复兴和NLP中的深度学习中,使用预训练的单词向量的转移学习技术已经变得无处不在。最近的工作(Radford等,2018; Peters等,2018)证明了语言建模任务的无监督表示如何有助于各种NLP任务,如问答,分类,句子相似性和自然 - 语言推断。
1. 将经典 NLP 文献引入可微学习范式 NLP 领域已有几十年的历史,尽管深度学习领域只有几年的历史。许多创新似乎都在研究新的深度学习(可微学习)范式下的传统方法和任务。阅读经典 NLP 论文(我们建议阅读它们!)时,一个很好的问题是作者试图学习什么?什么是输入/输出表示?我们如何通过前面章节中学到的技术简化这一过程?
2. 模型的组合性原则。在本书中,我们讨论了 NLP,MLP,CNN,序列模型,序列到序列模型和基于注意力的模型的不同类型的深度学习架构。值得注意的是,尽管我们单独讨论了这些模型,但纯粹是出于教学原因。在文献中看到的一个趋势是组合不同的架构来完成工作。例如,您可以在单词的字符上编写卷积网络,然后在该表示上写入 LSTM,并且通过 MLP 完成 LSTM 编码的最终分类。能够根据任务需求组合地组合不同的架构是使深度学习成功的最有力的想法之一。
3. 序列的卷积。我们在序列建模中看到的最近趋势是使用卷积运算完全模拟序列。作为完全卷积机器翻译模型的一个例子,参见 Gehring 等人。 (2018)。解码步骤使用去卷积操作。这是有利的,因为可以使用全卷积模型显着加速训练。
4. 《Attention is all you need》。最近的另一个趋势是用注意机制取代卷积(Vaswani 等,2017)。使用注意机制,特别是称为自我关注和多头注意的变体,您基本上可以捕获通常使用 RNN 和 CNN 建模的远程依赖关系。
5. 迁移学习。迁移学习是学习一项任务的表示和使用表示来改进另一项任务的学习的任务。在最近神经网络的复兴和 NLP 中的深度学习中,使用预训练的单词向量的迁移学习技术已经变得无处不在。最近的工作(Radford 等,2018; Peters 等,2018)证明了语言建模任务的无监督表示如何有助于各种 NLP 任务,如问答,分类,句子相似性和自然 - 语言推断。
此外,强化学习领域最近在对话相关任务方面取得了一些成功,而复杂的自然语言推理任务的记忆和知识基础的建模似乎引起了工业界和学术界的研究人员的高度关注。在下一节中,我们将从经典和前沿转向更直接的事物 - 开发一个系统思考设计生产NLP系统。
此外,强化学习领域最近在对话相关任务方面取得了一些成功,而复杂的自然语言推理任务的记忆和知识基础的建模似乎引起了工业界和学术界的研究人员的高度关注。在下一节中,我们将从经典和前沿转向更直接的事物 - 开发一个系统思考设计生产 NLP 系统。
## Design Patterns for Production NLP Systems
## 生产 NLP 系统的设计模式
生产NLP系统可能很复杂。在构建NLP系统时,重要的是要记住,您正在构建的系统正在解决任务,并且只是实现这一目标的手段。在系统构建期间,工程师,研究人员,设计人员和产品经理可以做出多种选择。虽然我们的书主要关注技术或基础构建块,但将这些构建块放在一起以提出满足您需求的复杂结构将需要一些模式思考。模式思维和描述模式的语言是“在专业领域内描述良好设计实践或有用组织模式的方法。”这在许多学科(Alexander,1979)中很流行,包括软件工程。在本节中,我们将介绍生产NLP系统的一些常见设计和部署模式。这些是团队经常需要做出的选择或权衡,以使产品开发与技术,业务,战略和运营目标保持一致。我们在六个轴下检查这些设计选择:
生产 NLP 系统可能很复杂。在构建 NLP 系统时,重要的是要记住,您正在构建的系统正在解决任务,并且只是实现这一目标的手段。在系统构建期间,工程师,研究人员,设计人员和产品经理可以做出多种选择。虽然我们的书主要关注技术或基础构建块,但将这些构建块放在一起以提出满足您需求的复杂结构将需要一些模式思考。模式思维和描述模式的语言是“在专业领域内描述良好设计实践或有用组织模式的方法。”这在许多学科(Alexander,1979)中很流行,包括软件工程。在本节中,我们将介绍生产 NLP 系统的一些常见设计和部署模式。这些是团队经常需要做出的选择或权衡,以使产品开发与技术,业务,战略和运营目标保持一致。我们在六个轴下检查这些设计选择:
1. Online versus offline systems 在线系统是需要实时或接近实时地进行模型预测的系统。诸如打击垃圾邮件和内容审核等一些任务本质上需要在线系统。另一方面,离线系统不需要实时运行。我们可以将它们构建为在一批输入上有效运行,并且可以利用转换学习等方法。一些在线系统可以是被动的,甚至可以以在线方式进行学习(也称为在线学习),但是许多在线系统是通过定期离线模型构建来构建和部署的,该构建被推向生产。使用在线学习构建的系统应该对对抗性环境特别敏感。最近的一个例子是着名的Twitter聊天机器人Tay,它误入歧途并开始从在线巨魔学习。正如后见之明所预见的那样,Tay很快就开始回应令人反感的推文,其母公司微软不得不在推出后不到一天就关闭了这项服务。系统构建中的典型轨迹是首先构建一个离线系统,将其作为一个“在线”系统进行大量工程工作,然后通过添加反馈循环并可能改变学习方法使其成为“在线学习”系统。虽然这种路径在代码库中增加的复杂性方面是有机的,但它可能会引入诸如处理攻击者之类的盲点等等。图9-3显示了“Facebook免疫系统”作为检测垃圾邮件的在线系统的一个例子(警告:大约2012年。不是当前Facebook基础设施的反映)。请注意在线系统如何比类似的离线系统需要更多的工程设计。 ![pic4](img/0425cc70e45b99ad32c4df9ecd82680b.jpg "Facebook免疫系统:一个在线系统的例子,用于对抗垃圾邮件和滥用(Stein等,2012)。")
2. Interactive versus non-interactive systems 大多数自然语言系统在预测仅来自模型的意义上是非交互式的。实际上,许多生产NLP模型深深嵌入到数据处理的提取,转换和加载(ETL)管道的转换步骤中。在某些情况下,人类参与预测循环可能会有所帮助。图9-4显示了Lilt Inc.的交互式机器翻译界面的一个例子,其中模型和人类共同参与所谓的“混合主动模型”(Green-Initiative Models)中的预测(Green 2014)。交互式系统难以设计,但通过将人类带入循环可以实现非常高的精度。
1. 在线 VS 离线系统。在线系统是需要实时或接近实时地进行模型预测的系统。诸如打击垃圾邮件和内容审核等一些任务本质上需要在线系统。另一方面,离线系统不需要实时运行。我们可以将它们构建为在一批输入上有效运行,并且可以利用转换学习等方法。一些在线系统可以是被动的,甚至可以以在线方式进行学习(也称为在线学习),但是许多在线系统是通过定期离线模型构建来构建和部署的,该构建被推向生产。使用在线学习构建的系统应该对对抗性环境特别敏感。最近的一个例子是着名的 Twitter 聊天机器人 Tay,它误入歧途并开始从在线巨魔学习。正如后见之明所预见的那样,Tay 很快就开始回应令人反感的推文,其母公司微软不得不在推出后不到一天就关闭了这项服务。系统构建中的典型轨迹是首先构建一个离线系统,将其作为一个“在线”系统进行大量工程工作,然后通过添加反馈循环并可能改变学习方法使其成为“在线学习”系统。虽然这种路径在代码库中增加的复杂性方面是有机的,但它可能会引入诸如处理攻击者之类的盲点等等。图 9-3 显示了“Facebook 免疫系统”作为检测垃圾邮件的在线系统的一个例子(警告:大约 2012 年。不是当前 Facebook 基础设施的反映)。请注意在线系统如何比类似的离线系统需要更多的工程设计。
![pic5](img/a8a3d29c19e970bdd9965e839f4d4ee4.jpg) 图9-4。一种人-环机器翻译模型,允许人们修正或改写来自MT系统的建议,以产生非常高质量的翻译。 (图片由Lilt Inc.提供)
![pic4](img/0425cc70e45b99ad32c4df9ecd82680b.jpg)
2. 交互 VS 非交互系统。大多数自然语言系统在预测仅来自模型的意义上是非交互式的。实际上,许多生产 NLP 模型深深嵌入到数据处理的提取,转换和加载(ETL)管道的转换步骤中。在某些情况下,人类参与预测循环可能会有所帮助。图 9-4 显示了 Lilt Inc 的交互式机器翻译界面的一个例子,其中模型和人类共同参与所谓的“混合主动模型”(Green-Initiative Models)中的预测(Green 2014)。交互式系统难以设计,但通过将人类带入循环可以实现非常高的精度。
![pic5](img/a8a3d29c19e970bdd9965e839f4d4ee4.jpg)
图 9-4。一种人-环机器翻译模型,允许人们修正或改写来自 MT 系统的建议,以产生非常高质量的翻译。 (图片由 Lilt Inc 提供)
1. Unimodal versus multimodal systems
在许多情况下,在学习和预测过程中纳入多种模态可能会有所帮助。例如,新闻转录系统不仅使用音频流而且还使用视频帧作为输入是有帮助的。例如,Google最近的一项名为“Look to Listen”(Ephrat et al。,2018)的作品使用多模式输入来解决扬声器源分离的困难问题(又名鸡尾酒会问题)。多模式系统的构建和部署成本很高,但是对于困难的问题,组合来自多个模态的输入提供了单独使用任何单一模态无法实现的信号。我们也在NLP中看到了这个例子。例如,在多模态翻译中,我们可以通过在可用时合并来自多种源语言的输入来提高翻译质量。生成网页主题(主题建模)时,除了网页上的文本外,还可以合并从其中包含的图像中提取的特征,如图9-5所示。
在许多情况下,在学习和预测过程中纳入多种模态可能会有所帮助。例如,新闻转录系统不仅使用音频流而且还使用视频帧作为输入是有帮助的。例如,Google 最近的一项名为《Look to Listen》(Ephrat et al。,2018)的作品使用多模式输入来解决扬声器源分离的困难问题(又名鸡尾酒会问题)。多模式系统的构建和部署成本很高,但是对于困难的问题,组合来自多个模态的输入提供了单独使用任何单一模态无法实现的信号。我们也在 NLP 中看到了这个例子。例如,在多模态翻译中,我们可以通过在可用时合并来自多种源语言的输入来提高翻译质量。生成网页主题(主题建模)时,除了网页上的文本外,还可以合并从其中包含的图像中提取的特征,如图 9-5 所示。
![pic6](img/521d3645c0c41862d747ff49f59b225f.jpg "图9-5。一种多模式系统,它共同利用音频和视频功能来解决像鸡尾酒会问题这样的难题。 (图片由Erphat等人提供,2018年)")
![pic6](img/521d3645c0c41862d747ff49f59b225f.jpg)
1. End-to-end systems versus piecewise systems 自深度学习问世以来,研究人员和工程师可以选择的另一个选择点是构建一个复杂的NLP系统,既可以作为不同单元的管道,也可以作为单片端到端系统。端到端设计在机器翻译,摘要和语音识别等许多领域都具有吸引力,精心设计的端到端系统可以显着降低实施和部署的复杂性,并且肯定会减少代码行数。分段系统(图9-6)将复杂的NLP任务分解为子任务,每个子任务单独优化,独立于最终任务目标。分段系统中的子任务使其非常模块化,易于“修补”生产中的特定问题,但通常会带来一些技术债务。
1. 端到端 VS 分片系统。自深度学习问世以来,研究人员和工程师可以选择的另一个选择点是构建一个复杂的 NLP 系统,既可以作为不同单元的管道,也可以作为单片端到端系统。端到端设计在机器翻译,摘要和语音识别等许多领域都具有吸引力,精心设计的端到端系统可以显着降低实施和部署的复杂性,并且肯定会减少代码行数。分段系统(图 9-6)将复杂的 NLP 任务分解为子任务,每个子任务单独优化,独立于最终任务目标。分段系统中的子任务使其非常模块化,易于“修补”生产中的特定问题,但通常会带来一些技术债务。
![pic7](img/072f1ab8693f1fb574adfb4599ccf28f.jpg "图9-6。分段系统:训练一些传统机器翻译系统的管道,这些系统将MT的任务视为一系列子任务,每个子任务都有自己的模型。将其与“示例:神经机器翻译”中讨论的神经MT模型进行比较。 (图由Hoang等人提供,2009)")
![pic7](img/072f1ab8693f1fb574adfb4599ccf28f.jpg)
1. Closed-domain versus open-domain systems 封闭域系统明确地针对单一目的进行优化:在该域中表现良好。例如,机器翻译系统可以明确优化以与生物医学期刊一起使用 - 这不仅仅涉及生物医学平行语料库的培训。另一方面,开放域系统旨在用于通用目的(例如,Google Translate)。再举一个例子,考虑一个文件标签系统。如果系统只预测了许多预定义类中的一个(典型情况),则会产生一个封闭域系统。但是,如果系统被设计为在运行时发现新类,那么它就是一个开放域系统。在翻译和语音识别系统的背景下,封闭域系统也被称为“有限词汇”系统。
1. 封闭领域 VS 开放领域系统。封闭域系统明确地针对单一目的进行优化:在该域中表现良好。例如,机器翻译系统可以明确优化以与生物医学期刊一起使用 - 这不仅仅涉及生物医学平行语料库的训练。另一方面,开放域系统旨在用于通用目的(例如,Google Translate)。再举一个例子,考虑一个文件标签系统。如果系统只预测了许多预定义类中的一个(典型情况),则会产生一个封闭域系统。但是,如果系统被设计为在运行时发现新类,那么它就是一个开放域系统。在翻译和语音识别系统的背景下,封闭域系统也被称为“有限词汇”系统。
2. Monolingual versus multilingual systems
为单一语言工作而构建的NLP系统称为单语系统。很容易构建和优化单语系统。相比之下,多语言系统可以处理多种语言。在对不同语言的数据集进行培训时,预计它们可以开箱即用。虽然构建多语言系统很有吸引力,但专注于单语版本有其优点。研究人员和工程师可以利用该语言中广泛可用的资源和领域专业知识来生成高质量的系统,否则一般的多语言系统是不可能的。出于这个原因,我们经常发现许多多语言产品被实现为单独优化的单语系统的集合,其中语言识别组件将输入分派给它们。
为单一语言工作而构建的 NLP 系统称为单语系统。很容易构建和优化单语系统。相比之下,多语言系统可以处理多种语言。在对不同语言的数据集进行训练时,预计它们可以开箱即用。虽然构建多语言系统很有吸引力,但专注于单语版本有其优点。研究人员和工程师可以利用该语言中广泛可用的资源和领域专业知识来生成高质量的系统,否则一般的多语言系统是不可能的。出于这个原因,我们经常发现许多多语言产品被实现为单独优化的单语系统的集合,其中语言识别组件将输入分派给它们。
## Where Next?
## 下一步是什么?
使用像PyTorch这样的即将到来的框架和像深度学习这样快速变化的领域,感觉就像在移动地面上建造一座豪宅。在本节中,我们指出了一些与深度学习,PyTorch和NLP相关的资源,以帮助我们的读者继续加强我们在本书中构建的基础。
使用像 PyTorch 这样的即将到来的框架和像深度学习这样快速变化的领域,感觉就像在移动地面上建造一座豪宅。在本节中,我们指出了一些与深度学习,PyTorch 和 NLP 相关的资源,以帮助我们的读者继续加强我们在本书中构建的基础。
我们没有涵盖PyTorch的每一个功能。我们建议您遵循优秀的PyTorch文档并参与PyTorch论坛以继续您的PyTorch实践:
我们没有涵盖 PyTorch 的每一个功能。我们建议您遵循优秀的 PyTorch 文档并参与 PyTorch 论坛以继续您的 PyTorch 实践:
* PyTorch Documentation: [https://pytorch.org/docs](https://pytorch.org/docs)
* [PyTorch 文档](https://pytorch.org/docs)
* PyTorch Forums: [https://discuss.pytorch.org/](https://discuss.pytorch.org/) 深度学习领域本身就是来自工业界和学术界的大量活动。大多数深度学习作品出现在arXiv的不同类别下:
* [PyTorch 论坛](https://discuss.pytorch.org/)。
* 机器学习:https://arxiv.org/list/cs.LG/recent
深度学习领域本身就是来自工业界和学术界的大量活动。大多数深度学习作品出现在 arXiv 的不同类别下:
* 语言和计算:https://arxiv.org/list/cs.CL/recent
* [机器学习](https://arxiv.org/list/cs.LG/recent)
* 人工智能:https://arxiv.org/list/cs.AI/recent
* [语言和计算](https://arxiv.org/list/cs.CL/recent)
了解NLP新作品的最佳方法是遵循以下学术会议:
* [人工智能](https://arxiv.org/list/cs.AI/recent)
了解 NLP 新作品的最佳方法是遵循以下学术会议:
* 计算语言学协会(ACL)

@@ -109,28 +123,28 @@

* ACL的欧洲章节(EACL)
* ACL 的欧洲分部(EACL)
* 计算自然语言学习会议(CoNLL)
我们建议aclweb.org跟踪这些会议和其他会议,研讨会和其他重要的NLP新闻的会议记录。
我们建议 aclweb.org 跟踪这些会议和其他会议,研讨会和其他重要的 NLP 新闻的会议记录。
当您准备超越基础时,您可能会发现自己必须阅读研究论文。阅读论文是一门获得的艺术。您可以在[https://www.cs.jhu.edu/~jason/advice/how-to-read-a-paper.html找到一些有用的阅读NLP论文的提示。](https://www.cs.jhu.edu/~jason/advice/how-to-read-a-paper.html找到一些有用的阅读NLP论文的提示。)
当您准备超越基础时,您可能会发现自己必须阅读研究论文。阅读论文是一门获得的艺术。您可以在[这里](https://www.cs.jhu.edu/~jason/advice/how-to-read-a-paper.html)找到一些有用的阅读 NLP 论文的提示。
最后,我们将继续提供更多教育材料,以补充本书的内容,网址为[https://nlproc.info/pytorch。](https://nlproc.info/pytorch。)
最后,我们将继续提供更多教育材料,[以补充本书的内容](https://nlproc.info/pytorch)。
## References
## 参考
1. Christopher Alexander. The Timeless Way of Building. Oxford University Press, 1979.
1. `Christopher Alexander. The Timeless Way of Building. Oxford University Press, 1979.`
2. Ken A. Dill, Adam Lucas, Julia Hockenmaier, Liang Huang, David Chiang, Aravind K. Joshi. “Computational linguistics: A new tool for exploring biopolymer structures and statistical mechanics.” In Polymer, 2007.
2. `Ken A. Dill, Adam Lucas, Julia Hockenmaier, Liang Huang, David Chiang, Aravind K. Joshi. "Computational linguistics: A new tool for exploring biopolymer structures and statistical mechanics." In Polymer, 2007.`
3. Hieu Hoang, Philipp Koehn, and Adam Lopez. “A Unified Framework for Phrase-Based, Hierarchical, and Syntax-Based Statistical Machine Translation.” In Proceedings of IWSLT, 2009.
3. `Hieu Hoang, Philipp Koehn, and Adam Lopez. "A Unified Framework for Phrase-Based, Hierarchical, and Syntax-Based Statistical Machine Translation." In Proceedings of IWSLT, 2009.`
4. Tao Stein, Erdong Chen, Karan Mangla. “Facebook Immune System.” SNS, 2011
4. `Tao Stein, Erdong Chen, Karan Mangla. "Facebook Immune System." SNS, 2011`
5. Spence Green, “Mixed-Initiative Language Translation.” PhD Thesis, Stanford University, 2014.
5. `Spence Green, "Mixed-Initiative Language Translation." PhD Thesis, Stanford University, 2014.`
6. Ashish Vaswani, Noam Shazeer, Niki Parmar, Jakob Uszkoreit, Llion Jones, Aidan N. Gomez, Lukasz Kaiser, Illia Polosukhin, “Attention Is All You Need.” on arXiv, 2017.
6. `Ashish Vaswani, Noam Shazeer, Niki Parmar, Jakob Uszkoreit, Llion Jones, Aidan N. Gomez, Lukasz Kaiser, Illia Polosukhin, "Attention Is All You Need." on arXiv, 2017.`
7. Ariel Ephrat, Inbar Mosseri, Oran Lang, Tali Dekel, Kevin Wilson, Avinatan Hassidim, William T. Freeman, Michael Rubinstein. “Looking to Listen: A Speaker-Independent Audio-Visual Model for Speech Separation.” SIGGRAPH, 2018.
7. `Ariel Ephrat, Inbar Mosseri, Oran Lang, Tali Dekel, Kevin Wilson, Avinatan Hassidim, William T. Freeman, Michael Rubinstein. "Looking to Listen: A Speaker-Independent Audio-Visual Model for Speech Separation." SIGGRAPH, 2018.`
8. Matthew E. Peters, Mark Neumann, Mohit Iyyer, Matt Gardner, Christopher Clark, Kenton Lee, Luke Zettlemoyer. “Deep contextualized word representations.” ACL 2018.
8. `Matthew E. Peters, Mark Neumann, Mohit Iyyer, Matt Gardner, Christopher Clark, Kenton Lee, Luke Zettlemoyer. "Deep contextualized word representations." ACL 2018.`

@@ -1,4 +0,4 @@

# PyTorch 自然语言处理(Natural Language Processing with PyTorch 中文版)
# PyTorch 自然语言处理
![](https://nlp-pt.apachecn.org/cover.jpg)
![](cover.jpg)

@@ -9,75 +9,58 @@ > 译者:[Yif Du](https://yifdu.github.io/)

>
> 欢迎任何人参与和完善:一个人可以走的很快,但是一群人却可以走的更远。
> 所有模型都是错的,但其中一些是有用的。
本书旨在为新人提供自然语言处理(NLP)和深度学习,以涵盖这两个领域的重要主题。这两个主题领域都呈指数级增长。对于一本介绍深度学习和强调实施的NLP的书,本书占据了重要的中间地带。在写这本书时,我们不得不对哪些材料遗漏做出艰难的,有时甚至是不舒服的选择。对于初学者,我们希望本书能够为基础知识提供强有力的基础,并可以瞥见可能的内容。特别是机器学习和深度学习是一种经验学科,而不是智力科学。我们希望每章中慷慨的端到端代码示例邀请您参与这一经历。当我们开始编写本书时,我们从PyTorch 0.2开始。每个PyTorch更新从0.2到0.4修改了示例。 PyTorch 1.0将于本书出版时发布。本书中的代码示例符合PyTorch 0.4,它应该与即将发布的PyTorch 1.0版本一样工作.1关于本书风格的注释。我们在大多数地方都故意避免使用数学;并不是因为深度学习数学特别困难(事实并非如此),而是因为它在许多情况下分散了本书主要目标的注意力——增强初学者的能力。在许多情况下,无论是在代码还是文本方面,我们都有类似的动机,我们倾向于对简洁性进行阐述。高级读者和有经验的程序员可以找到方法来收紧代码等等,但我们的选择是尽可能明确,以便覆盖我们想要达到的大多数受众。
本书旨在为新人提供自然语言处理(NLP)和深度学习,以涵盖这两个领域的重要主题。这两个主题领域都呈指数级增长。对于一本介绍深度学习和强调实施的 NLP 的书,本书占据了重要的中间地带。在写这本书时,我们不得不对哪些材料遗漏做出艰难的,有时甚至是不舒服的选择。对于初学者,我们希望本书能够为基础知识提供强有力的基础,并可以瞥见可能的内容。特别是机器学习和深度学习是一种经验学科,而不是智力科学。我们希望每章中慷慨的端到端代码示例邀请您参与这一经历。当我们开始编写本书时,我们从 PyTorch 0.2 开始。每个 PyTorch 更新从 0.2 到 0.4 修改了示例。 PyTorch 1.0 将于本书出版时发布。本书中的代码示例符合 PyTorch 0.4,它应该与即将发布的 PyTorch 1.0 版本一样工作。 关于本书风格的注释。我们在大多数地方都故意避免使用数学;并不是因为深度学习数学特别困难(事实并非如此),而是因为它在许多情况下分散了本书主要目标的注意力——增强初学者的能力。在许多情况下,无论是在代码还是文本方面,我们都有类似的动机,我们倾向于对简洁性进行阐述。高级读者和有经验的程序员可以找到方法来收紧代码等等,但我们的选择是尽可能明确,以便覆盖我们想要达到的大多数受众。
> 组织构建[网站]
* [在线阅读](https://nlp-pt.apachecn.org)
* [在线阅读(Gitee)](https://apachecn.gitee.io/nlp-pytorch-zh/)
* [ApacheCN 学习资源](http://docs.apachecn.org/)
* [ApacheCN 面试求职群 724187166](http://shang.qq.com/wpa/qunwpa?idkey=9bcf2fb3985835c9c2f15783ec9c85822e23be1191a6581eaf22f574b5192b19)
* [代码地址](https://github.com/joosthub/PyTorchNLPBook)
+ GitHub Pages(国外): https://nlp-pt.apachecn.org
+ Gitee Pages(国内): https://apachecn.gitee.io/nlp-pytorch-zh
## 贡献指南
> 第三方站长[网站]
本项目需要校对,欢迎大家提交 Pull Request。
+ 顺川页 Page(国内): https://book.ztjs.top/nlp_pytorch
+ 地址A: xxx (欢迎留言,我们完善补充)
> 请您勇敢地去翻译和改进翻译。虽然我们追求卓越,但我们并不要求您做到十全十美,因此请不要担心因为翻译上犯错——在大部分情况下,我们的服务器已经记录所有的翻译,因此您不必担心会因为您的失误遭到无法挽回的破坏。(改编自维基百科)
> 其他补充
## 联系方式
+ [代码地址](https://github.com/joosthub/PyTorchNLPBook)
+ **ApacheCN - 面试求职群【724187166】<a target="_blank" href="//shang.qq.com/wpa/qunwpa?idkey=9bcf2fb3985835c9c2f15783ec9c85822e23be1191a6581eaf22f574b5192b19"><img border="0" src="http://data.apachecn.org/img/logo/ApacheCN-group.png" alt="ApacheCN - 面试求职群[724187166]" title="ApacheCN - 面试求职群[724187166]"></a>**
+ [ApacheCN 学习资源](http://www.apachecn.org/)
+ 电子书下载地址:[epub](https://github.com/apachecn/pytorch-doc-zh/tree/gh-pages/books/nlp-pytorch-zh.epub), [mobi](https://github.com/apachecn/pytorch-doc-zh/tree/gh-pages/books/nlp-pytorch-zh.mobi)
+ pdf下载:https://book.ztjs.top/nlp_pytorch/nlp-pytorch-zh.pdf
+ epub下载:https://book.ztjs.top/nlp_pytorch/nlp-pytorch-zh.epub
+ mobi下载:https://book.ztjs.top/nlp_pytorch/nlp-pytorch-zh.mobi
### 负责人
## 目录
* [飞龙](https://github.com/wizardforcel): 562826179
* [Chapter 1.基础介绍](docs/1.md)
* [Chapter 2.传统NLP快速回顾](docs/2.md)
* [Chapter 3.神经网络基础组件](docs/3.md)
* [Chapter 4.自然语言处理 Feed-Forward Networks](docs/4.md)
* [Chapter 5.Embedding Words and Types](docs/5.md)
* [Chapter 6.自然语言处理 Sequence Modeling](docs/6.md)
* [Chapter 7.自然语言处理的中间 Sequence Modeling](docs/7.md)
* [Chapter 8.用于自然语言处理的高级 Sequence](docs/8.md)
* [Chapter 9.经典, 前沿和后续步骤](docs/9.md)
### 其他
## 编译
* 在我们的 [apachecn/nlp-pytorch-zh](https://github.com/apachecn/nlp-pytorch-zh) github 上提 issue.
* 发邮件到 Email: `apachecn@163.com`.
* 在我们的 [组织学习交流群](http://www.apachecn.org/organization/348.html) 中联系群主/管理员即可.
## 下载
### Docker
```
npm install -g gitbook-cli # 安装 gitbook
gitbook fetch 3.2.3 # 安装 gitbook 子版本
gitbook install # 安装必要的插件
gitbook <build|pdf|epub|mobi> # 编译 HTML/PDF/EPUB/MOBI
docker pull apachecn0/nlp-pytorch-zh
docker run -tid -p <port>:80 apachecn0/nlp-pytorch-zh
# 访问 http://localhost:{port} 查看文档
```
## 精品推荐
### PYPI
> Pytorch 中文文档&中文教程
```
pip install nlp-pytorch-zh
nlp-pytorch-zh <port>
# 访问 http://localhost:{port} 查看文档
```
* Pytorch 中文教程: <https://pytorch.apachecn.org>
### NPM
> 深度学习必学
```
npm install -g nlp-pytorch-zh
nlp-pytorch-zh <port>
# 访问 http://localhost:{port} 查看文档
```
1. [反向传递](/docs/dl/反向传递.md): <https://www.cnblogs.com/charlotte77/p/5629865.html>
2. [CNN原理](/docs/dl/CNN原理.md): <http://www.cnblogs.com/charlotte77/p/7759802.html>
3. [RNN原理](/docs/dl/RNN原理.md): <https://blog.csdn.net/qq_39422642/article/details/78676567>
4. [LSTM原理](/docs/dl/LSTM原理.md): <https://blog.csdn.net/weixin_42111770/article/details/80900575>
## 赞助我们
> 自然语言处理
* Python 自然语言处理 第二版: <https://usyiyi.github.io/nlp-py-2e-zh>
* 推荐一个[liuhuanyong大佬](https://github.com/liuhuanyong)整理的nlp全面知识体系: <https://liuhuanyong.github.io>
* 开源 - 词向量库集合:
* <https://github.com/Embedding/Chinese-Word-Vectors>
* <https://github.com/brightmart/nlp_chinese_corpus>
* <https://github.com/codemayq/chinese_chatbot_corpus>
* <https://github.com/candlewill/Dialog_Corpus>
* 新词发现代码 python 实现: <https://github.com/zhanzecheng/Chinese_segment_augment>
* 电影知识图谱 python 实现: <https://github.com/SimmerChan/KG-demo-for-movie>
* 知识图谱的《红楼梦》人物关系可视化及问答系统 python 实现: <https://github.com/chizhu/KGQA_HLM>
## 关于转载
* 禁止商业化,符合协议规范,备注地址来源,不需要发邮件给我们申请【如果觉得有帮助,欢迎各位踊跃收藏和转载】
![](http://data.apachecn.org/img/about/donate.jpg)

@@ -1,12 +0,10 @@

# Summary
* [入门须知](README.md)
* [Chapter 1.基础介绍](docs/1.md)
* [Chapter 2.传统NLP快速回顾](docs/2.md)
* [Chapter 3.神经网络基础组件](docs/3.md)
* [Chapter 4.自然语言处理 Feed-Forward Networks](docs/4.md)
* [Chapter 5.Embedding Words and Types](docs/5.md)
* [Chapter 6.自然语言处理 Sequence Modeling](docs/6.md)
* [Chapter 7.自然语言处理的中间 Sequence Modeling](docs/7.md)
* [Chapter 8.用于自然语言处理的高级 Sequence](docs/8.md)
* [Chapter 9.经典, 前沿和后续步骤](docs/9.md)
+ [PyTorch 自然语言处理](README.md)
+ [一、基础介绍](docs/1.md)
+ [二、传统 NLP 快速回顾](docs/2.md)
+ [三、神经网络基础组件](docs/3.md)
+ [四、自然语言处理的前馈网络](docs/4.md)
+ [五、嵌入单词和类型](docs/5.md)
+ [六、自然语言处理的序列模型](docs/6.md)
+ [七、自然语言处理的进阶序列模型](docs/7.md)
+ [八、自然语言处理的高级序列模型](docs/8.md)
+ [九、经典, 前沿和后续步骤](docs/9.md)

@@ -26,3 +26,3 @@ #!/usr/bin/env node

requestHandler(req, res) {
requestHandlerUnsafe(req, res) {
var {pathname} = url.parse(req.url)

@@ -79,5 +79,5 @@ var fname = path.join(this.root, pathname)

requestHandlerSafe(req, res) {
requestHandler(req, res) {
try {
this.requestHandler(req, res)
this.requestHandlerUnsafe(req, res)
} catch(ex) {

@@ -99,5 +99,5 @@ this.errorHandler(req, res, ex)

start() {
var server = http.createServer(this.requestHandlerSafe.bind(this))
var server = http.createServer(this.requestHandler.bind(this))
server.listen(this.port, this.host,
() => console.log(`server started in http://${this.host}:${this.port}`))
() => console.log(`server started on http://${this.host}:${this.port}`))
}

@@ -104,0 +104,0 @@

@@ -1,1 +0,1 @@

{"name":"nlp-pytorch-zh","version":"2020.919.0","description":"PyTorch 自然语言处理(Natural Language Processing with PyTorch 中文版)","main":"index.js","scripts":{"test":"echo \"no test specified\""},"author":"ApacheCN","license":"CC BY-NC-SA 4.0","repository":{"type":"git","url":"git+https://github.com/apachecn/nlp-pytorch-zh.git"},"bin":{"nlp-pytorch-zh":"index.js"},"dependencies":{}}
{"name":"nlp-pytorch-zh","version":"2021.104.0","description":"PyTorch 自然语言处理","main":"index.js","scripts":{"test":"echo \"no test specified\""},"author":"ApacheCN","license":"CC BY-NC-SA 4.0","repository":{"type":"git","url":"git+https://github.com/apachecn/nlp-pytorch-zh.git"},"bin":{"nlp-pytorch-zh":"index.js"},"dependencies":{}}

@@ -1,4 +0,4 @@

# PyTorch 自然语言处理(Natural Language Processing with PyTorch 中文版)
# PyTorch 自然语言处理
![](https://nlp-pt.apachecn.org/cover.jpg)
![](cover.jpg)

@@ -9,75 +9,58 @@ > 译者:[Yif Du](https://yifdu.github.io/)

>
> 欢迎任何人参与和完善:一个人可以走的很快,但是一群人却可以走的更远。
> 所有模型都是错的,但其中一些是有用的。
本书旨在为新人提供自然语言处理(NLP)和深度学习,以涵盖这两个领域的重要主题。这两个主题领域都呈指数级增长。对于一本介绍深度学习和强调实施的NLP的书,本书占据了重要的中间地带。在写这本书时,我们不得不对哪些材料遗漏做出艰难的,有时甚至是不舒服的选择。对于初学者,我们希望本书能够为基础知识提供强有力的基础,并可以瞥见可能的内容。特别是机器学习和深度学习是一种经验学科,而不是智力科学。我们希望每章中慷慨的端到端代码示例邀请您参与这一经历。当我们开始编写本书时,我们从PyTorch 0.2开始。每个PyTorch更新从0.2到0.4修改了示例。 PyTorch 1.0将于本书出版时发布。本书中的代码示例符合PyTorch 0.4,它应该与即将发布的PyTorch 1.0版本一样工作.1关于本书风格的注释。我们在大多数地方都故意避免使用数学;并不是因为深度学习数学特别困难(事实并非如此),而是因为它在许多情况下分散了本书主要目标的注意力——增强初学者的能力。在许多情况下,无论是在代码还是文本方面,我们都有类似的动机,我们倾向于对简洁性进行阐述。高级读者和有经验的程序员可以找到方法来收紧代码等等,但我们的选择是尽可能明确,以便覆盖我们想要达到的大多数受众。
本书旨在为新人提供自然语言处理(NLP)和深度学习,以涵盖这两个领域的重要主题。这两个主题领域都呈指数级增长。对于一本介绍深度学习和强调实施的 NLP 的书,本书占据了重要的中间地带。在写这本书时,我们不得不对哪些材料遗漏做出艰难的,有时甚至是不舒服的选择。对于初学者,我们希望本书能够为基础知识提供强有力的基础,并可以瞥见可能的内容。特别是机器学习和深度学习是一种经验学科,而不是智力科学。我们希望每章中慷慨的端到端代码示例邀请您参与这一经历。当我们开始编写本书时,我们从 PyTorch 0.2 开始。每个 PyTorch 更新从 0.2 到 0.4 修改了示例。 PyTorch 1.0 将于本书出版时发布。本书中的代码示例符合 PyTorch 0.4,它应该与即将发布的 PyTorch 1.0 版本一样工作。 关于本书风格的注释。我们在大多数地方都故意避免使用数学;并不是因为深度学习数学特别困难(事实并非如此),而是因为它在许多情况下分散了本书主要目标的注意力——增强初学者的能力。在许多情况下,无论是在代码还是文本方面,我们都有类似的动机,我们倾向于对简洁性进行阐述。高级读者和有经验的程序员可以找到方法来收紧代码等等,但我们的选择是尽可能明确,以便覆盖我们想要达到的大多数受众。
> 组织构建[网站]
* [在线阅读](https://nlp-pt.apachecn.org)
* [在线阅读(Gitee)](https://apachecn.gitee.io/nlp-pytorch-zh/)
* [ApacheCN 学习资源](http://docs.apachecn.org/)
* [ApacheCN 面试求职群 724187166](http://shang.qq.com/wpa/qunwpa?idkey=9bcf2fb3985835c9c2f15783ec9c85822e23be1191a6581eaf22f574b5192b19)
* [代码地址](https://github.com/joosthub/PyTorchNLPBook)
+ GitHub Pages(国外): https://nlp-pt.apachecn.org
+ Gitee Pages(国内): https://apachecn.gitee.io/nlp-pytorch-zh
## 贡献指南
> 第三方站长[网站]
本项目需要校对,欢迎大家提交 Pull Request。
+ 顺川页 Page(国内): https://book.ztjs.top/nlp_pytorch
+ 地址A: xxx (欢迎留言,我们完善补充)
> 请您勇敢地去翻译和改进翻译。虽然我们追求卓越,但我们并不要求您做到十全十美,因此请不要担心因为翻译上犯错——在大部分情况下,我们的服务器已经记录所有的翻译,因此您不必担心会因为您的失误遭到无法挽回的破坏。(改编自维基百科)
> 其他补充
## 联系方式
+ [代码地址](https://github.com/joosthub/PyTorchNLPBook)
+ **ApacheCN - 面试求职群【724187166】<a target="_blank" href="//shang.qq.com/wpa/qunwpa?idkey=9bcf2fb3985835c9c2f15783ec9c85822e23be1191a6581eaf22f574b5192b19"><img border="0" src="http://data.apachecn.org/img/logo/ApacheCN-group.png" alt="ApacheCN - 面试求职群[724187166]" title="ApacheCN - 面试求职群[724187166]"></a>**
+ [ApacheCN 学习资源](http://www.apachecn.org/)
+ 电子书下载地址:[epub](https://github.com/apachecn/pytorch-doc-zh/tree/gh-pages/books/nlp-pytorch-zh.epub), [mobi](https://github.com/apachecn/pytorch-doc-zh/tree/gh-pages/books/nlp-pytorch-zh.mobi)
+ pdf下载:https://book.ztjs.top/nlp_pytorch/nlp-pytorch-zh.pdf
+ epub下载:https://book.ztjs.top/nlp_pytorch/nlp-pytorch-zh.epub
+ mobi下载:https://book.ztjs.top/nlp_pytorch/nlp-pytorch-zh.mobi
### 负责人
## 目录
* [飞龙](https://github.com/wizardforcel): 562826179
* [Chapter 1.基础介绍](docs/1.md)
* [Chapter 2.传统NLP快速回顾](docs/2.md)
* [Chapter 3.神经网络基础组件](docs/3.md)
* [Chapter 4.自然语言处理 Feed-Forward Networks](docs/4.md)
* [Chapter 5.Embedding Words and Types](docs/5.md)
* [Chapter 6.自然语言处理 Sequence Modeling](docs/6.md)
* [Chapter 7.自然语言处理的中间 Sequence Modeling](docs/7.md)
* [Chapter 8.用于自然语言处理的高级 Sequence](docs/8.md)
* [Chapter 9.经典, 前沿和后续步骤](docs/9.md)
### 其他
## 编译
* 在我们的 [apachecn/nlp-pytorch-zh](https://github.com/apachecn/nlp-pytorch-zh) github 上提 issue.
* 发邮件到 Email: `apachecn@163.com`.
* 在我们的 [组织学习交流群](http://www.apachecn.org/organization/348.html) 中联系群主/管理员即可.
## 下载
### Docker
```
npm install -g gitbook-cli # 安装 gitbook
gitbook fetch 3.2.3 # 安装 gitbook 子版本
gitbook install # 安装必要的插件
gitbook <build|pdf|epub|mobi> # 编译 HTML/PDF/EPUB/MOBI
docker pull apachecn0/nlp-pytorch-zh
docker run -tid -p <port>:80 apachecn0/nlp-pytorch-zh
# 访问 http://localhost:{port} 查看文档
```
## 精品推荐
### PYPI
> Pytorch 中文文档&中文教程
```
pip install nlp-pytorch-zh
nlp-pytorch-zh <port>
# 访问 http://localhost:{port} 查看文档
```
* Pytorch 中文教程: <https://pytorch.apachecn.org>
### NPM
> 深度学习必学
```
npm install -g nlp-pytorch-zh
nlp-pytorch-zh <port>
# 访问 http://localhost:{port} 查看文档
```
1. [反向传递](/docs/dl/反向传递.md): <https://www.cnblogs.com/charlotte77/p/5629865.html>
2. [CNN原理](/docs/dl/CNN原理.md): <http://www.cnblogs.com/charlotte77/p/7759802.html>
3. [RNN原理](/docs/dl/RNN原理.md): <https://blog.csdn.net/qq_39422642/article/details/78676567>
4. [LSTM原理](/docs/dl/LSTM原理.md): <https://blog.csdn.net/weixin_42111770/article/details/80900575>
## 赞助我们
> 自然语言处理
* Python 自然语言处理 第二版: <https://usyiyi.github.io/nlp-py-2e-zh>
* 推荐一个[liuhuanyong大佬](https://github.com/liuhuanyong)整理的nlp全面知识体系: <https://liuhuanyong.github.io>
* 开源 - 词向量库集合:
* <https://github.com/Embedding/Chinese-Word-Vectors>
* <https://github.com/brightmart/nlp_chinese_corpus>
* <https://github.com/codemayq/chinese_chatbot_corpus>
* <https://github.com/candlewill/Dialog_Corpus>
* 新词发现代码 python 实现: <https://github.com/zhanzecheng/Chinese_segment_augment>
* 电影知识图谱 python 实现: <https://github.com/SimmerChan/KG-demo-for-movie>
* 知识图谱的《红楼梦》人物关系可视化及问答系统 python 实现: <https://github.com/chizhu/KGQA_HLM>
## 关于转载
* 禁止商业化,符合协议规范,备注地址来源,不需要发邮件给我们申请【如果觉得有帮助,欢迎各位踊跃收藏和转载】
![](http://data.apachecn.org/img/about/donate.jpg)

Sorry, the diff of this file is too big to display

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is too big to display

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc