精华内容
下载资源
问答
  • 作者|一轩明月编辑|NewBeeNLP2018 年对 NLP 领域来说是个不折不扣的转折年。像 OpenAI GPT 和 BERT 这样的大规模预训练语言模型,用通用模型架构在...

    作者 | 一轩明月  

    编辑 | NewBeeNLP

    2018 年对 NLP 领域来说是个不折不扣的转折年。像 OpenAI GPT 和 BERT 这样的大规模预训练语言模型,用通用模型架构在各种语言任务上都取得了亮眼表现,这和  ImageNet 分类预训练对视觉任务的影响可以等量齐观。前者甚至更胜一筹,NLP 采用的方法简单而强大,预训练时不需要有标签数据,可以随着我们的性子增大训练规模直到极限。

    此前回顾总结的 Embedding技术回顾 文章中,各种 embedding 都和上下文没关系,他们都是基于共现关系而非有序语境来进行学习的。所以 “I am eating an apple”“I have an Apple phone”  两句话中的 “apple” ,两个词指代的事物明显不同但还是会有相同的词嵌入向量。

    此外,早期解决问题的时候,词嵌入都被用作既有任务模型的附加特征,某种程度上所带来的改进有限。

    本文中我们会介绍许多基于情境做 embedding 的方法,以通用格式更简单、经济地将词向量应用到下游任务上。

    CoVe

    「语境词向量(Contextual Word Vectors,Cove[1])」 是一类用编码器做词嵌入的方法,编码器就是基于注意力的 seq-to-seq 机器翻译模型里用的那种。不同于传统的词嵌入方法,CoVe 的词表示是关于整个输入序列的函数。

    NMT 概述

    这里的神经机翻译(NMT[2])模型由一个标准的双层双向 LSTM 编码器,和一个额外的基于注意力的双层单向 LSTM 解码器构成。模型预先在英语-德语翻译任务上进行训练。编码器学习并优化英文单词的 embedding 向量,好将其译为德文。直觉上编码器能在将词转为其他语言形式学到高维语义和句法特征,编码器输出的情景化词嵌入可供各种下游语言任务使用。

    CoVe 中的 NMT 基础模型
    • 源语言(英语)的 字序列:

    • 目标语言(德语)的 字序列:

    • 源字句的 GloVe 向量:

    • 随机初始化目标字句的嵌入向量:

    • biLSTM 编码器输出隐态序列: 。其中前向 LSTM 计算 ,反向计算  

    • 附加的解码器会输出一个字词分布: 其中 是时间维度上的一组隐态

    下游任务使用 CoVe

    NMT 编码器的隐态在其他语言任务中称为「环境向量(context vector)」

    论文将 GloVe 和 CoVe 串联用于问答和分类任务。GloVe 从全局字词共现比率中学习,所以没有句子语境,而 CoVe 是通过处理文本序列得到的,能够捕捉情境信息。

    对特定下游任务,我们先生成输入字词的 GloVe+CoVe 的串联向量,然后将其作为附加特征喂给特定任务模型。

    CoVe embedding 是由编码器生成的,旨在解决机器翻译任务。编码器可以插入任意下游任务模型中

    总结

    CoVe 的局限性很明显:

    1. 预训练受限于有监督翻译任务能得到哪些数据集;

    2. CoVe 对最终效果的贡献受制于任务模型

    下面我们会看到 ELMo 通过无监督预训练克服了问题(1),OpenAI GPT 和 BERT 进一步通过预训练 + 对不同下游任务采用生成模型架构将两个问题都解决了。

    ELMo

    「语言模型嵌入(Embeddings from Language Model,ELMo[3])」 通过 无监督 的方式预训练一个语言模型来获取情境化字词表示。

    双向语言模型

    「双向语言模型(bidirectional Language Model,biLM)」 是 ELMo 的基础,当输入为 个标识组成的序列时, ,语言模型会学着根据历史预测下个标识的概率。

    前向传递期间,历史由目标标识之前的字词构成,

    反向传递时,历史则由目标标识之后的字词构成

    两个方向的预测由多层 LSTM 负责建模,输入标识 层的隐态为 。经 softmax 归一化,用最后一层的隐态 获取标识概率。嵌入层和 softmax 层共享,参数分别为

    ELMo 的 biLSTM 基础模型

    模型要使两个方向的负对数似然概率最小化(= 正确词的对数似然概率最大化):

    ELMo 表示

    层 biLM 基础上,ELMo 针对任务学习了一种线性组合方式,跨层将所有隐态堆叠起来。标识 的隐态表示有 个向量:

    其中 是嵌入层输出, 。每个终端任务都要学习一组线性组合权重 ,并用 softmax 做归一化。比例因子 用于纠正 biLM 隐态分布和任务表示分布之间的偏差。

    为了评估从跨层隐态上得到了哪类信息,分别用不同 biLM 层的表示在语义和语法任务上对 ELMo 进行测试:

    • 「语义任务」词义消除(word sense disambiguation) 任务强调特定语境下的字词含义。biLM 顶层在该任务上比第一层表现更好。

    • 「语法任务」词性标注(part-of-speech tagging) 任务旨在推断某个词在一句话中的语法成分。此时用 biLM 第一层要比用顶层得到的准确率更高。

    对比研究表明语法信息较低的层表示更好,而语义信息更高处的层领悟更深。因为不同层携带着不同类型的信息,将其堆叠起来会有所帮助。

    下游任务使用 ELMo

    和 CoVe 助力不同下游任务的方式类似,ELMo 嵌入向量常见于输入或任务模型的底层。此外,对某些 任务(即 SNLI,SQuAD,不包括 SRL)在输出层加上他们同样有所助益。

    对那些只有少量有监督数据的任务来说,ELMo 带来的提升是最大的。有了 ELMo,即使标签数据再少我们也能取得类似的效果。

    总结

    语言模型的预训练是无监督式的,而且鉴于无标注文本语料之丰富,理论上预训练规模可以尽可能的大些。但它还是得依赖特定任务模型,所以改善只是渐进式的,给每个任务找个好模型架构仍然很重要。

    跨视角训练

    ELMo 中无监督的预训练和指定任务的学习,是在两个独立训练阶段由两个独立模型完成的。「跨视角训练(Cross-View Training,CVT[4])」 将二者结合,组成了一个统一的半监督学习过程,辅助任务中有标注数据的监督学习和无标注数据的无监督学习都能改善 biLSTM 编码器的表示。

    模型架构

    模型由一个双层双向的 LSTM 编码器和一个主预测模块构成。训练时交替将标注数据和无标注数据分批喂给模型。

    • 有标注的样本,所有模型参数都通过标准监督学习进行更新。损失是标准交叉熵。

    • 无标注样本,主预测模块依旧可以得到一个“柔性”目标,尽管我们并不清楚这有多准。两个辅助任务中,预测器只能看到并处理有限视角下的输入内容,比如只用单方向上的编码器隐态表示。我们希望辅助任务的输出能和初步预测目标匹配,这样就能得知输入全貌了。

    这样,编码器被强制将完整的上下文知识提炼为不完整的表示。在这一阶段,biLSTM 编码器处于反向传播状态,但主预测模块是 固定 的。辅助预测和主预测间的差距就是我们要最小化的损失。

    半监督语言模型的跨视角训练

    多任务学习

    在同步训练多个任务的时候,CVT 给附加的任务加了几个主预测模型,它们共享同样的句子表示编码器。监督训练时,随机选择一个任务后,相应预测器参数和表示编码器得到更新。如果是无标注数据样本,联结所有任务优化编码器,力求每个任务上辅助输出和主预测间的差异最小。

    多任务学习有利于增强表示的泛化能力,与此同时还收获一个很好的副产物:从无标注数据得到的全任务标注样本,他们是十分宝贵的数据标签,考虑到跨任务标签有用太稀少。

    下游任务使用 CVT

    理论上讲主预测模块可以采用任意形式,通用的或者视任务而定的都行。CVT 论文里的例子两种情况都有。

    在像 NER 或 POS tagging 这样的序列标注任务(给每个标识分类)中,预测器模块包括两个完整的全连接层,和给输出加上的 softmax 层,以此生成类别标签的概率分布。对每个标识 ,两层对应的隐态为

    第一层里只给辅助任务喂前向或后向 LSTM 状态,因为它们只看到了部分上下文,要么左边要么右边,它们必须学着像语言模型那样,预测给定情境中的下一个标识。fwdbwd 辅助任务只取一个方向。futurepast 任务则分别在前向和后向上多走一步。

    序列标注任务依赖于四个辅助预测模型,它们的输入只有单方向的隐态信息:前,后,将来以及过去

    注意,如果主预测模块有 dropout,那处理有标注数据的时候 dropout 层照常工作,但用无标注数据训练,为辅助任务生成“柔性”目标时不参与计算。

    机器翻译任务中,主预测模块换成了标准的带 attention 的标准单向 LSTM 解码器。涉及两个辅助任务:

    1. dropout,随机使 attention 权重向量的一些值清零;

    2. 预测目标序列的未来词。用固定的主解码器对输入做集束搜索[5]得到的最优预测目标序列就是辅助任务要努力逼近的主预测项了。

    ULMFiT

    受 ImageNet 预训练在计算机视觉任务上攻城略地的启发,生成式预训练 LM + 任务微调的思路最先在 ULMFiT[6])中进行了尝试。基准模型是 AWD-LSTM[7]

    ULMFiT 通过下面三步在下游语言分类任务上取得了良好的迁移学习效果:

    1)「通用 LM 预训练」:Wikipedia 语料

    2)「目标任务 LM 微调」:ULMFiT 提出了两个训练技术来稳定微调过程。

    • 差异化微调(Discriminative fine-tuning) 是基于不同 LM 层学到的信息类型不同这一事实提出的。(上文有[讨论](# ELMo 表示 "讨论"))。ULMFiT 以不同的学习率调教各层, 是第一层的基础学习率, 是第 层,一共 层。

    • 斜三角学习率(Slanted triangular learning rates,STLR) 是种特殊的学习率,先线性增加而后线性减小。增长期较短,便于模型快速收敛到和任务适配的参数空间,衰减期较长便于取得更好的微调效果。

    3)「目标任务分类器微调」:用两个标准前馈层强化预训练 LM,并在末端加上 softmax 归一化来预测目标标签分布。

    • 连接池化(Concat pooling) 对过往隐态取 max-pooling 和 mean-pooling,并将其和最终隐态拼在一起

    • 逐步解封(Gradual unfreezing)是为了避免灾难性遗忘,做法是从最后一层开始逐步解开模型各层。首先解开最后一层,微调一轮,然后是倒数第二层,不断重复直到调整完所有层。

    ULMFiT 的三个训练阶段

    OpenAI GPT

    和 ELMo 想法类似,OpenAI 的「生成式预训练 Transformer(Generative Pre-training Transformer,GPT[8])」 通过在巨量文本语料上训练,极大的增加了无监督语言模型的规模。抛开相似之处,GPT 和 ELMo 主要有两点不同。

    1. 模型架构不同:ELMo 是将独立训练的自左向右和自右向左的多层 LSTM 进行浅拼接,而 GPT 是个多层 transformer 解码器

    2. 情境化嵌入在下游任务中的用法不同:ELMo 是将 embedding 作为额外特征喂给特定任务模型,而 GPT 是将相同的基准模型进行微调来解决各式任务

    拿 Transformer 解码器当语言模型

    相较于原始 Transformer[9] 架构,Transformer 解码器[10]模型丢掉了编码器部分,所以只有一个输入序列,而非两个单独的源序列和目标序列。

    模型在输入序列的 embedding 上加了多层 transformer 块进行处理。每一块内都有一个遮罩多头自注意力(multi-headed self-attention)层和一个元素级前馈(pointwise feed-forward)层。经 softmax 归一化后最终可以得到一个目标标识的分布。

    OpenAI GPT 的 transformer 解码器模型架构

    损失为负对数似然概率,和 ELMo 一样,但没了反向计算。选定目标词前大小为 的上下文窗口,损失为:

    字节对编码

    「字节对编码(Byte Pair Encoding,BPE[11])」 用于给输入序列编码。BPE 原是 20 世纪 90 年代提出的一种数据压缩算法,随后被拿去解决机器翻译里的开放词汇表问题,因为在译为某种新语言的时候很容易遇到少见或未知的词。直觉上稀有或未知词经常可以拆成多个子词,BPE 就不断迭代,贪婪地合并常见字符对以寻找最佳分词方式。

    有监督微调

    OpenAI GPT 所做的最大改进是与任务模型解耦,直接用预训练语言模型!

    以分类任务为例。标注数据集中每个输入有 个标识, ,和一个标签 。GPT 先用预训练过的 transformer 解码器处理输入序列 ,最后一个标识 在最后一层的输出为 。靠着仅有的训练得到的权重矩阵 ,模型可以预测类别标签的分布。

    损失是求实际标签的负对数似然概率的最小化,此外作者发现加上 LM 损失作为辅助损失会有好处,因为:

    1. 训练时利于加速收敛

    2. 改善监督模型的泛化效果

    有了类似设计后,就没必要给其他终端任务定制模型架构了(见下图)。如果输入是多个序列,每对序列间会加上一个特殊的定界标识 $,定界标识的 embedding 是我们要学习的一个新参数,不过会很简短就是了。

    对序列相似度任务来讲,因为排序没必要,先后序都有。多选任务里,情境会和每个候选项进行匹配结对。

    微调过的 GPT transformer 模型对各下游任务的训练目标

    总结

    当时(2018.6)看到这样一个通用框架在大多数语言任务上取得 SOTA,让人感觉很巧妙且倍受鼓舞。一阶段,语言模型的生成式预训练过程可以从尽可能多的免费文本语料中进行学习。二阶段,用一个较小的标注数据集针对特定任务对模型进行微调,并学习一小批新参数。

    GPT 的一个局限之处在于单向性——模型只会自左向右的预测上下文。

    BERT

    「Transformer 双向编码器表示(Bidirectional Encoder Representations from Transformers,BERT[12])」 算是 GPT 的直系后代:在免费语料上训练一个大规模语言模型,然后针对特定任务微调而不需要单独定制网络架构。

    相较于 GPT,BERT 最大的区别和改善之处在于双向训练,模型会学着预测左右两边的上下文,按论文中消融研究结果所述:

    “模型双向性是最重要的新贡献没有之一”

    预训练任务

    BERT 的模型架构为多层双向 Transformer 编码器。

    Transformer 编码器架构

    为了促进双向预测和句子级理解,相较于在基础语言任务上训练(给定场景下预测下一个标识),BERT 要同时完成两项任务。

    「任务 1:遮罩语言模型(Mask language model,MLM)」

    维基百科:“完形填空(填充缺失测验)是一项练习,测试或评估,由一部分语句构成,其中特定项、字词或符号会被删除(填空文本),与试者的任务是替换缺失语言项……该练习首先由 W.L. Taylor 在 1953 年提出”

    毫无疑问,从前后语境中而不单只是从后文中学到的表示能更好的领会个中深意,无论是在语义还是语法上。BERT 通过「遮罩语言模型」任务来实现这一点:

    1. 每个句子随机挡住 15% 的内容。因为如果我们只用特殊占位符 [MASK] 换掉被遮标识,微调的时候特定标识就再也看不到了。所以 BERT 用了几个启发式技巧:

    • a)80% 的概率用 [MASK] 替换选定词

    • b)10% 的概率用随机词替换

    • c)10% 的概率保持不变

    • 模型只预测缺失词,但它不知道哪个词被替换了,或者要预测哪个词。输出大小只有输入的 15%

    • 「任务 2:下一句预测」

      许多下游任务涉及到对句子间关系的理解(QA,NLI),BERT 额外加了一个辅助任务,训练一个二分类器(binary classifier)判断一句话是不是另一句的下文:

      1. 对语句对(A,B)采样:

      • a)50% 的情况 B 是 A 的下文

      • b)50% 的情况不是

    • 模型对两句话进行处理并输出一个二值标签,指明 B 是否就是 A 后的下一句话

    • 上述两个辅助任务的训练数据可以轻易从任意单语语料中获取,所以训练规模不受限制。训练损失是累计平均遮罩 LM 概率,和累计平均下文预测概率。

      BERT,OpenAI GPT 和 ELMo 模型架构对比

      输入嵌入

      输入嵌入是三部分的和:

      1. 「字段标识嵌入(WordPiece tokenization embeddings)」:字段模型原本是针对日语或德语的分词问题提出的。相较于使用自然分隔的英文单词,它们可以进一步分成更小的子词单元便于处理罕见词或未知词。感兴趣的话可以看看分词优化方式的论文[论文1][13][论文2][14]

      2. 「片段嵌入(segment embedding)」:如果输入有两句话,分别有句子 A 和句子 B 的嵌入向量,并用特殊标识 [SEP] 隔开;如果输入只有一句话就只用句子 A 的嵌入向量

      3. 「位置嵌入(position embeddings)」:位置 embedding 需要学习而非硬编码

      BERT 输入表示

      注意第一个标识必须是 [CLS]——之后下游任务预测中会用到的占位符。

      下游任务使用 BERT

      BERT 的微调只需要添加很少的参数,就像 OpenAI GPT。

      对于分类任务,取首个标识 [CLS] 的最终隐态 ,将它和一个小权重矩阵相乘,

      对像 SQuAD 这样的 QA 任务,我们要针对问题预测给定段落的文本跨度。BERT 对每个标识要预测两个概率分布,分别对应描述作为文本范围的开端和末尾的几率。微调时新训练的只有两个小矩阵 ,而 对应两个概率分布。

      总体来讲微调下游任务时加上的内容很少——一两个权重矩阵,负责将 Transform 隐态转换成可解释形式。其他情况的实施细节可以看论文了解。

      微调过的 BERT 模型对不同下游任务的训练目标

      可以列张表比较下 OpenAI GPT 和 BERT 间的差别。


      「OpenAI GPT」「BERT」
      特殊字符只在微调时引入[SEP][CLS]预训练阶段就学习 [SEP][CLS] 和句子 A/B 的 embedding
      训练过程1M 步, batch size 32k 个词1M 步, batch size 128k 个词
      微调所有任务 lr = 5e-5看任务定 lr

      ALBERT

      「精简 BERT(A Lite BERT,ALBERT[15])」 是 BERT 的简化版,相似的配置下参数减少 18 倍,训练速度提升 1.7 倍。ALBERT 所作的改进有三点:排前两位的当然是减少了参数,降低了内存开销从而提升了训练速度,而第三点则是用一个更有挑战性的训练任务替换掉了下一句预测(NSP)任务。

      分解式嵌入参数化

      BERT 中,字段标识 embedding 的大小 被设置成与隐态尺寸 相同,即是说如果想增加模型尺寸(更大的 ),也就得学习更大的标识 embedding,而这又依赖于词表大小   ,计算成本就有点高了。

      理论上,标识嵌入应当学习的是情境独立的表示,而隐态是依赖环境的,所以将隐层大小和词表嵌入的大小分开考虑比较合理。通过分解式嵌入参数化, 的大词表嵌入矩阵就被分成 的两个小矩阵。一般 ,因子分解可以显著降低参数数量。

      跨层参数共享

      参数跨层共享有多种方式:(a)只共享前馈部分(b)只共享注意力参数(c)共享所有参数。该方法可以大量削减参数,同时又不会太伤害性能。

      句子顺序预测

      有趣的是,BERT 的下一句预测(NSP)任务被证明太简单了。ALBERT 换成了句子顺序预测(sentence-order prediction,SOP)的自监督损失,

      • 正样本:同文档中连续的两个部分

      • 负样本:和上面一样但次序颠倒

      对于 NSP 任务,当 A 和 B 的情境不同时,如果模型能检测到所谈话题它就能做出合理预测。相较而言,SOP 更难一些,因为这要求模型充分理解片段间的一致性和顺序关系。

      OpenAI GPT-2

      OpenAI[16] GPT-2[17] 直接继承自 GPT。GPT-2 有 15 亿参数,比原 GPT 大了 10 多倍,在受测的 8 个语言模型数据集上拿了 7 个 SOTA,采用 零尝试迁移配置(zero-shot transfer setting) 不需要任何任务微调。

      预训练数据集包括 80 亿 Web 页面,页面是从 Reddit[18] 上爬下来的合格外链。在小数据集和评估长程依赖(long-term dependency)的数据集上 GPT-2 进步明显。

      零尝试迁移

      GPT-2 的预训练就纯是语言建模。所有下游语言任务都被规制成对条件概率的预测,不存在对任务的微调。

      • 文本生成就是直接用 LM

      • 机器翻译任务,比如英语到汉语,通过 LM 条件化完成。末端加上“英文=中文”和“待翻英文=”两部分

        • 要预测的条件概率可能像这样:P(?| I like green apples. = 我喜欢绿苹果。A cat meows at him. = 一只猫对他喵喵叫。It is raining cats and dogs. =)

      • QA 任务也转成是和翻译类似的形式,给上下文里加上成对的问答

      • 摘要任务是在上下文中给文章末尾加上 TL;DR:

      字节序列 BPE

      和原 GPT 一样,GPT-2 也对 UTF-8 字节序列采用了 BPE。每个字节可以用 8 比特表示 256 种不同的含义,UTF-8 最多可以使用 4 字节来表示一个字符,最高支持 种字符。所以用字节序列表示,我们只需要一个大小为 256 的词汇表,而不需要操心预训练、标识化等内容。尽管有这些好处,当前字节级 LM 仍与 SOTA 字词级 LM 间有着不可忽视的性能差距。

      BPE 不断贪婪地合并共现字节对,为防止常用词出现多个版本表示(由 dogdog.dog!dog?)GPT-2 不许 BPE 跨类别合并字符(dog 不会与 .!?这些标点合并)。这一技巧有效增加了最终字节段的质量。

      通过字节序列表示,GPT-2 可以对任意 Unicode 字符串给出一个概率,而不需要任何预训练步骤。

      模型改进

      相较于 GPT,除了更多的 transformer 层和参数,GPT-2 只做了很少的架构调整:

      • 层归一化[19]移到子块输入上,像是“建块[20]”型残差单元一样(和原来的“瓶颈[21]”类型不同,它是在权重层前进行批归一化)

      • 在最后的自注意块之后加了个层归一化

      • 改良初始化,使其成为模型深度的一个函数

      • 残差层的权重一开始要缩小至 是残差层数量

      • 用更大的词汇表和上下文

      RoBERTa

      「稳健优化版 BERT」( Robustly optimized BERT approach[22])算是训练 BERT 实现更好效果的新凭据,因为作者发现原 BERT 模型明显训练不足。台面上摆的是:

      1. 用更大的 batch size 进行更多步的训练

      2. 删掉下一句预测任务

      3. 训练数据格式上用更长的序列。论文指出用单独的句子作输入会伤害下游性能,应该连续采样多句构建更长的片段

      4. 动态调整遮罩模式。原 BERT 就在预训练时用了一次遮罩,导致训练时都是一个静态罩子。RoBERTa 在 40 轮训练中用了 10 种不同的遮罩方式。

      RoBERTa 还加了新数据集 CommonCrawl News[23] 并进一步证明用更多的数据进行预训练有助于改善下游任务性能。训练方式和 GPT-2 一样,都是在字节序列上做 BPE。研究者还发现超参数的选择对模型性能有重大影响。

      T5

      「文本到文本迁移 Transformer(Text-to-Text Transfer Transformer,T5[24])」 是按原 Transformer[25] 架构实现的编码器-解码器语言模型:标识→ embedding →编码器 →解码器 →输出。

      T5 采用“全能自然语言”框架[26],许多常见 NLP 任务被转换成针对上下文的问答形式。相较于显式的 QA 格式,T5 用较短的任务前缀区分任务意图,并分别对每个任务做了模型微调。文本到文本架构用同样的模型解决不同的任务,极大简化了迁移学习的评估过程。

      T5 任务评估图。文本到文本架构将各任务转换成通用格式:输入文本进目标文本出

      模型在 2019 年 4 月搜集的网络语料上做训练,用上了各种过滤器。模型借助“适配器层”(多加一层做训练)或“逐步解封”(见 [ULMFiT])分别对各下游任务做微调。两个微调方法都是只更新部分参数,同时大部分模型参数不变。T5-11B 在许多 NLP 任务上取得了 SOTA 结果。

      正如作者在文中所讲“......我们的目标不是提出新方法,而是提供一个全面的视角看看这个领域发展到了哪一步”,T5 的长论文详细介绍了许多训练设置和评估过程的细节,对有兴趣从头训练 LM 的读者来说是很好的阅读材料。

      GPT-3

      GPT-3[27]和 GPT-2 架构相同但有 175B 个参数,比 GPT-2(1.5B) 大 10 多倍。此外,GPT-3 使用了稠密模式和局部带状模式交替的稀疏注意力,和稀疏 transformer 里的一样。

      为了把这么大的模型塞到 GPU 集群里,GPT-3 采用了沿深度和宽度方向的分区训练方式。训练数据是 Common Crawl 的过滤板,还额外混合了少数高品质数据集。为了防止下游任务出现在训练数据里造成污染,作者试着从训练集里删掉了所有基准数据集间的重叠部分。不幸的是因为有 bug 过滤过程并不完美。

      GPT-3 训练数据集。注意每个数据集训练时出现比例和数据集本身大小并不相称

      对于所有下游任务的评估,GPT-3 只是在少样本环境下做了测试,没用任何基于梯度的微调,这里的少数样本算是给了部分提示。相比于微调过的 BERT 模型,GPT-3 在很多 NLP 数据集上取得了亮眼表现。

      性能指标随着模型大小和样本数增大一同上涨

      总结


      基础模型预训练下游任务下游模型微调
      CoVeseq2seq NMT 模型有监督基于特征看任务定/
      ELMo双层 biLSTM无监督基于特征看任务定/
      CVT双层 biLSTM半监督基于模型看任务定 / 与任务无关/
      ULMFiTAWD-LSTM无监督基于模型与任务无关所有层;各种训练技巧
      GPTTransformer 解码器无监督基于模型与任务无关预训练层+顶部任务层
      BERTTransformer 编码器无监督基于模型与任务无关预训练层+顶部任务层
      GPT-2Transformer 解码器无监督基于模型与任务无关预训练层+顶部任务层
      RoBERTaTransformer 编码器无监督基于模型与任务无关预训练层+顶部任务层
      T5Transformer无监督基于模型与任务无关分别对每个下游任务微调
      GPT-3Transformer 解码器无监督基于模型与任务无关没有微调

      指标:复杂度

      复杂度通常作为一个固有评估指标,衡量给定情境下语言模型对实际分布的学习效果。

      离散概率分布 的复杂度被定义为熵的乘方:

      给定 个词构成的序列, ,简单假设每个词的频率相同,都是 ,则熵形式如下:

      于是句子复杂度就变为:

      语言模型够好,做预测给的概率就高,因此复杂度越低越好。

      常见任务和数据集

      问答

      • SQuAD[28] (Stanford Question Answering Dataset):阅读理解数据集,由一组基于 维基百科文章提出的问题组成,每个问题的答案对应一段文本

      • RACE[29] (ReAding Comprehension from Examinations):超大规模阅读理解数据集,超 2.8 万篇文章和近 10 万个问题。数据集源自中国中、高中英语考试测验。

      常识推理

      • Story Cloze Test[30]:一个常识推理框架,考察故事理解和生成能力。要求系统在两个选项中,给由多个句子组成的故事选择正确结局。

      • SWAG[31] (Situations With Adversarial Generations):多项选择;包含 11.3 万个结对句子样本,用以评估基本常识推理

      自然语言推理(Natural Language Inference,NLI)

      也叫「文本推演(Text Entailment)」,辨析一个句子可否由另一个句子推理出来

      • RTE[32] (Recognizing Textual Entailment):由文本推演挑战创立的数据集

      • SNLI[33] (Stanford Natural Language Inference):57 万个手写英文句子对,人工打标(entailmentcontradictionneutral)保证分类均衡

      • MNLI[34] (Multi-Genre NLI):类似 SNLI,文本风格和话题种类更多,收集自转录语音,流行小说和政府报告

      • QNLI[35] (Question NLI):转换自 SQuAD 数据集,变成基于(问题,句子)对的二分类任务

      • SciTail[36]:从多选型科学测验和网络语句中收集到的推理数据集

      命名实体识别(Named Entity Recognition,NER)

      给文本中字词序列打标,标签是事物名,比如人名,公司名,基因或蛋白质名等等

      • CoNLL 2003 NER task[37]:材料来自路透社新闻,包括四类命名实体:人,地点,机构组织和杂项实体名

      • OntoNotes 5.0[38]:该语料涵盖英语,阿拉伯语和汉语,标注四个不同的实体类别(PER,LOC,ORG,MISC)

      • Reuters Corpus[39]:路透社新闻报道集

      • Fine-Grained NER (FGN)

      情感分析

      • SST[40] (Stanford Sentiment Treebank)

      • IMDb[41]:大规模影评数据集,打上了情感二分类标签

      语义成分标注(Semantic Role Labeling,SRL)

      对句子的谓词参数结构建模,通常是回答“谁对谁做了什么”

      • CoNLL-2004 & CoNLL-2005[42]

      句子相似度

      也叫复述测验

      • MRPC[43] (MicRosoft Paraphrase Corpus):都是从网络新闻源中抽取的句子对,没对句子都加了注释以表明是否语义等价

      • QQP[44] (Quora Question Pairs)

      • STS Benchmark: Semantic Textual Similarity

      句子接受度

      一项指明句子语法合格率的任务

      • CoLA[45] (Corpus of Linguistic Acceptability):二值单句分类任务

      文本分块

      把文章分成句法特征关系紧密的几部分

      • CoNLL-2000[46]

      词性标注(Part-of-Speech [POS] Tagging)

      给每个标识打上词性成分,比如名词,动词,形容词等。

      机器翻译

      见斯坦福 NLP[47] 页面

      • WMT 2015 English-Czech data (Large)

      • WMT 2014 English-German data (Medium)

      • IWSLT 2015 English-Vietnamese data (Small)

      共指消解

      对指代相同潜在实体的部分聚类

      • CoNLL-2012[48]

      远程依赖

      • LAMBADA[49] (LAnguage Modeling Broadened to Account for Discourse Aspects):从 BookCorpus 抽来的记叙文集,任务是要预测最后一个词。人类要想成功预测需要至少 50 个标识的上下文。

      • Children’s Book Test[50]:语料是来自 Project Gutenberg[51] 的免费图书。任务是要在 10 个候选项中预测缺失词

      多任务基准

      • GLUE 多任务基准[52]

      • decaNLP 基准[53]

      无监督预训练数据集

      • Books corpus[54]:超 7000 本不同的未出版图书,类型覆盖冒险,幻想,浪漫等

      • 1B Word Language Model Benchmark[55]

      • 英文维基百科[56]: 大约 25 亿个词

      一起交流

      想和你一起学习进步!『NewBeeNLP』目前已经建立了多个不同方向交流群(机器学习 / 深度学习 / 自然语言处理 / 搜索推荐 / 图网络 / 面试交流 / 等),名额有限,赶紧添加下方微信加入一起讨论交流吧!(注意一定要备注信息才能通过)

      本文参考资料

      [1]

      Cove: https://arxiv.org/abs/1708.00107

      [2]

      NMT: https://github.com/THUNLP-MT/MT-Reading-List

      [3]

      ELMo: https://arxiv.org/abs/1802.05365

      [4]

      CVT: https://arxiv.org/abs/1809.08370

      [5]

      集束搜索: https://en.wikipedia.org/wiki/Beam_search

      [6]

      ULMFiT: https://arxiv.org/abs/1801.06146

      [7]

      AWD-LSTM: https://arxiv.org/abs/1708.02182

      [8]

      GPT: https://s3-us-west-2.amazonaws.com/openai-assets/research-covers/language-unsupervised/language_understanding_paper.pdf

      [9]

      原始 Transformer: https://arxiv.org/abs/1706.03762

      [10]

      Transformer 解码器: https://arxiv.org/abs/1801.10198

      [11]

      BPE: https://arxiv.org/abs/1508.07909

      [12]

      BERT: https://arxiv.org/abs/1810.04805

      [13]

      [论文1]: https://static.googleusercontent.com/media/research.google.com/en//pubs/archive/37842.pdf

      [14]

      [论文2]: https://arxiv.org/pdf/1609.08144.pdf

      [15]

      ALBERT: https://arxiv.org/abs/1909.11942

      [16]

      OpenAI: https://blog.openai.com/better-language-models/

      [17]

      GPT-2: https://d4mucfpksywv.cloudfront.net/better-language-models/language_models_are_unsupervised_multitask_learners.pdf

      [18]

      Reddit: https://www.reddit.com/

      [19]

      层归一化: https://arxiv.org/abs/1607.06450

      [20]

      建块: https://arxiv.org/abs/1603.05027

      [21]

      瓶颈: https://arxiv.org/abs/1512.03385

      [22]

      Robustly optimized BERT approach: https://arxiv.org/abs/1907.11692

      [23]

      CommonCrawl News: https://commoncrawl.org/2016/10/news-dataset-available/

      [24]

      T5: https://arxiv.org/abs/1910.10683

      [25]

      原 Transformer: https://arxiv.org/abs/1706.03762

      [26]

      “全能自然语言”框架: https://arxiv.org/abs/1806.08730

      [27]

      GPT-3: https://arxiv.org/abs/2005.14165

      [28]

      SQuAD: https://rajpurkar.github.io/SQuAD-explorer/

      [29]

      RACE: http://www.qizhexie.com/data/RACE_leaderboard

      [30]

      Story Cloze Test: http://cs.rochester.edu/nlp/rocstories/

      [31]

      SWAG: https://rowanzellers.com/swag/

      [32]

      RTE: https://aclweb.org/aclwiki/Textual_Entailment_Resource_Pool

      [33]

      SNLI: https://nlp.stanford.edu/projects/snli/

      [34]

      MNLI: https://www.nyu.edu/projects/bowman/multinli/

      [35]

      QNLI: https://gluebenchmark.com/tasks

      [36]

      SciTail: http://data.allenai.org/scitail/

      [37]

      CoNLL 2003 NER task: https://www.clips.uantwerpen.be/conll2003/

      [38]

      OntoNotes 5.0: https://catalog.ldc.upenn.edu/LDC2013T19

      [39]

      Reuters Corpus: https://trec.nist.gov/data/reuters/reuters.html

      [40]

      SST: https://nlp.stanford.edu/sentiment/index.html

      [41]

      IMDb: http://ai.stanford.edu/~amaas/data/sentiment/

      [42]

      CoNLL-2004 & CoNLL-2005: http://www.lsi.upc.edu/~srlconll/

      [43]

      MRPC: https://www.microsoft.com/en-us/download/details.aspx?id=52398

      [44]

      QQP: https://data.quora.com/First-Quora-Dataset-Release-Question-Pairs

      [45]

      CoLA: https://nyu-mll.github.io/CoLA/

      [46]

      CoNLL-2000: https://www.clips.uantwerpen.be/conll2000/chunking/

      [47]

      斯坦福 NLP: https://nlp.stanford.edu/projects/nmt/

      [48]

      CoNLL-2012: http://conll.cemantix.org/2012/data.html

      [49]

      LAMBADA: http://clic.cimec.unitn.it/lambada/

      [50]

      Children’s Book Test: https://research.fb.com/downloads/babi/

      [51]

      Project Gutenberg: https://www.gutenberg.org/

      [52]

      GLUE 多任务基准: https://gluebenchmark.com/

      [53]

      decaNLP 基准: https://decanlp.com/

      [54]

      Books corpus: https://googlebooks.byu.edu/

      [55]

      1B Word Language Model Benchmark: http://www.statmt.org/lm-benchmark/

      [56]

      英文维基百科: https://en.wikipedia.org/wiki/Wikipedia:Database_download#English-language_Wikipedia

      END -



      聊一聊大火的对比学习

      2021-08-12

      文本匹配开山之作--双塔模型及实战

      2021-08-13

      YYDS!一个针对中文的预训练模型

      2021-08-06

      近代自然语言处理技术发展的『第四范式』

      2021-08-04

    展开全文
  • 一张图概括编程语言发展史

    千次阅读 2018-07-26 11:12:21
    一张图概括编程语言发展史 一张图概括编程语言发展史 Intro 编年史 Intro 编程语言是一组用来定义计算机程序的语法规则。它是一种被标准化的交流语言,用来向计算机发出指令。一种计算机语言让程序员...

    一张图概括编程语言发展史

    一张图

    编程语言是一组用来定义计算机程序的语法规则。它是一种被标准化的交流语言,用来向计算机发出指令。一种计算机语言让程序员能够准确地定义计算机所需要使用的数据,并精确地定义在不同情况下所应当采取的行动。尽管人们多次试图创造一种通用的程序设计语言,却没有一次尝试是成功的。之所以有那么多种不同的编程语言存在的原因是设计程序语言的初衷不同,对语言学习曲线的追求不同,不同程序之间的运行成本差异等。

    下面这张图片描绘了整个编程语言的历史。包括各种编程语言的发明人,编程语言的特点和适用领域,被什么网站或公司使用等。

    lang-history

    编年史

    我们一个统计出来256种编程语言,当然,这么多的语言中只有一些是常用的或实用的。

    1951 – Regional Assembly Language
    1952 – Autocode
    1954 – IPL (LISP语言的祖先)
    1955 – FLOW-MATIC (COBOL语言的祖先)
    1957 – FORTRAN (第一个编译型语言)
    1957 – COMTRAN (COBOL语言的祖先)
    1958 – LISP
    1958 – ALGOL 58
    1959 – FACT (COBOL语言的祖先)
    1959 – COBOL
    1959 – RPG
    1962 – APL
    1962 – Simula
    1962 – SNOBOL
    1963 – CPL (C语言的祖先)
    1964 – BASIC
    1964 – PL/I
    1966 – JOSS
    1967 – BCPL (C语言的祖先)
    1968 – Logo
    1969 – B (C语言的祖先)
    1970 – Pascal
    1970 – Forth
    1972 – C
    1972 – Smalltalk
    1972 – Prolog
    1973 – ML
    1975 – Scheme
    1978 – SQL
    1980 – C++ (既有类的C语言,更名于1983年7月)
    1983 – Ada
    1984 – Common Lisp
    1984 – MATLAB
    1985 – Eiffel
    1986 – Objective-C
    1986 – Erlang
    1987 – Perl
    1988 – Tcl
    1988 – Mathematica
    1989 – FL
    1990 – Haskell
    1991 – Python
    1991 – Visual Basic
    1993 – Ruby
    1993 – Lua
    1994 – CLOS (ANSI Common Lisp的一部分)
    1995 – Java
    1995 – Delphi (Object Pascal)
    1995 – JavaScript
    1995 – PHP
    1996 – WebDNA
    1997 – Rebol
    1999 – D
    2000 – ActionScript
    2001 – C#
    2001 – Visual Basic .NET
    2002 – F#
    2003 – Groovy
    2003 – Scala
    2007 – Clojure
    2009 – Go
    2011 – Dart

    从1951年2014年,人类一共发明了256种编程语言,每一种语言的出现都带有某些新特征。编程语言不断的在革新,很快就会有超出这个清单的新编程语言出现


    1801 - Joseph Marie Jacquard用打孔卡为一台织布机编写指令,在挂毯上织出了“hello, world”字样。当时的reddit网友对这项工作的反响并不热烈,因为它既缺少尾递归调用,又不支持并发,而且甚至都没有注意在拼写时恰当地区分大小写。

    Jacquard织布机是第一台可进行程序控制的织布机。用打孔卡进行编程的概念,直到电子计算机被发明出来之后仍然被广泛运用。

    最早的Hello World程序(出自K&R C)打印的是全小写的字符串:”hello, world”。

    在许多英文技术社区里,不正确地使用大小写发贴会被视作是小白的行为。(如把“Python”拼作“python”,把“FreeBSD”拼作“freebsd”,把“Qt”拼作“QT”)


    1842 - Ada Lovelace写了世界上第一个程序。她的努力只遇到了一点点小小的麻烦,那就是:实际上并没有任何计算机能够用来运行她的程序。后来的企业架构师们重新吸收了她的这个技能,用来学习如何更好地使用UML进行编程。

    Ada Lovelace为Charles Babbage的分析机写了一个计算伯努利数的算法实现,因此被后世公认为是世界上第一个程序员。实际上,分析机由于其设计思想过于先进,在当时根本没有 被制造出来。(Babbage的分析机一般被认为是现代电子通用计算机的先驱)

    讽刺现在的某些“软件架构师”顶多只会纸上谈兵地画画UML。


    1936 - Alan Turing发明了世间一切程序语言的最终形态,但很快他就被英国军情六处“请”去当007了,以至于他根本来不及为这些语言申请专利。

    与通用图灵机(Universal Turing machine)等价的语言被称为图灵完备的(Turing
    completeness),它定义了“什么样的语言可以被称作是程序语言”。

    二战期间Turing曾秘密地为英国军方工作,破解德军的Enigma密码机,并在战后被授予大英帝国勋章。但这项事实直到多年以后才向公众公开


    1936 - Alonzo Church同时也发明了世间一切程序语言的最终形态,甚至做得更好。但他的λ演算被绝大部分人忽视了,因为它与C语言“不够像”。尽管存在着这样的批评,但事实上,C在当时还没有被发明出来。

    Church是Turing在Princeton的博士生导师,他在λ演算方面的工作先于Turing指出了不存在一个对可判定性问题的通用解法,这后来证明和Turing针对停机问题提出的图灵机模型是等价的。即著名的Church-Turing论题。

    说Church“甚至做得更好”,因为λ演算为后世所有的函数式语言提供了理论基础。

    现在一种常见的关于函数式编程的批评就是:“它们与C语言不够像”。


    1940年代 - 一些直接采用布线和开关来进行程序控制的“计算机”出现了。工程师们当时这么做,据说是为了避开“用空格还是用制表符缩进”这样的论战。

    据说当时负责设计ENIAC的工程师中间曾经发生过这样的争论:

    • 空格比制表符好。

    • 制表符比空格好。

    • 4个空格比8个空格好。

    • 什么?用2个空格的统统烧死。

    关于这台具有里程碑意义的人类史上第一台电子计算机ENIAC上应该预装何种编辑器,工程师们还发生过这样的争吵:

    • Vim比Emacs好!

    • Emacs比Vim好!

    • 强烈推荐Sublime Text。

    • 你丫用编辑器的都是找虐,IDE才是王道。

    • 没错,要用就用世界上最好的公司微软开发出来的世界上最好的IDE:Visual Studio。

    • 我早就看透了无谓的编辑器论战什么的了,我要告诉楼上吵架的,你们全都是傻逼!

    最后,工程师们一致决定使用布线和开关来为他们即将发明的计算机进行编程,机智地避开了所有这些无谓的争吵,最终齐心协力创造出了人类历史上第一台电子计算机:ENIAC。(鼓掌)

    两位ENIAC程序员在运用敏捷开发方法进行愉快的结对编程。“自从抛弃伴随我多年的Emacs和HHKB Pro、改用布线和开关进行编程之后,我的左手小指麻痹奇迹般地痊愈了。”其中一位接受采访时如是说。另一位则表示:“新的编程方式让曾经专注颈椎病20年的我得到了彻底的康复,不用再整天盯着显示屏,身心同时得到了极大的放松,值得大力推广!”)


    1957 - John Backus和IBM发明了FORTRAN语言。关于IBM或FORTRAN并没有什么特别好笑的地方。除了,写FORTRAN程序的时候不系蓝领带将被编译器视作是一个syntax error。

    蓝领带、白衬衫、深色西装似乎是IBM公司20世纪经典的dress code。

    早期FORTRAN(FORTRAN 77)对程序书写格式的要求那是相当严格。(例如,蛋疼的固定格式缩进)


    1958 - John McCarthy和Paul Graham发明了LISP。由于冷战期间的战略括号资源储备所造成的巨大成本,LISP从未流行过。尽管欠缺足够的流行度,LISP(现在叫做 “Lisp”,有时叫“Arc”)仍然被视作一门有影响力的语言,在关键的算法思想诸如递归(recursion)和提升逼格 (condescension)上尤为典范。

    LISP发明的那一年Paul Graham其实还没有出生。据说是因为某本叫做《Haste and
    Waste》的伪程装黑圣典实在太有名了,以至于许多编程小白们把写这本书的传奇人物同Lisp之间画上了等号。

    提升逼格确实是一种与递归调用同样关键的算法思想。嗯,你懂的。


    1959 - 在输掉了和L. Ron Hubbard之间的一场打赌之后,Grace Hopper和其他几个抖S发明了所谓的“面向Boilerplate的全大写化语言(Capitalization Of Boilerplate Oriented Language,COBOL)”。多年以后,由于一些被误导的、性别歧视主义者对Adm. Hopper关于COBOL的工作的报复,在Ruby技术会议上不时会看到一些厌女主义乃至仇视女性的材料出现。

    L. Ron Hubbard是山达基教(Scientology)的创始人,二战期间曾与Grace
    Hopper同样供职于美国海军。(尚不清楚这两人之间有无其他联系)

    COBOL语言以代码极其冗长和通篇大写字母的书写风格而闻名。

    Adm. Hopper:Grace Murray Hopper女士的军衔是Rear Admiral Lower Half,即美国海军准将。


    1964 - John Kemeny和Thomas Kurtz创造了BASIC,一个为非计算机科学家设计的非结构化的程序语言。

    1965 - Kemeny和Kurtz两人goto到了1964。

    调侃BASIC语言对行号和goto的无节制滥用。


    1970 - Guy Steele和Gerald Sussman创造了Scheme。他们的工作导致了一系列以《Lambda之究极(Lambda the Ultimate)……》为标题开头的论文发表,并在《Lambda之究极厨房神器》这一篇中达到了最高潮。以这篇论文为基础,开始了一个长年累月的、收 视率究极失败的晚间电视购物节目。Lambda们因为其概念相对难以理解而被大众所忽视,直到未来的某一天,Java语言终于让它们变得有名了起来。通过 不包含它们这件事情。


    1970 - Niklaus Wirth创造了Pascal,一个过程式的语言。很快就有人开始声讨Pascal,因为它使用了类似“x := x + y”这样的语法,而不是更为人熟知的类C语法“x = x + y”。尽管存在着这样的批评,而事实上当时C还没有被发明出来。


    1972 - Dennis Ritchie发明了一把射击时能同时向前和向后两个方向发射子弹的绝世好枪。但他对此发明造成的致死和终身残疾数量感到还不够满意,所以他又发明了C语言和Unix。


    1972 - Alain Colmerauer设计了逻辑编程语言Prolog。他的目标是创造一个具有两岁小孩智商的程序语言。为了证明他成功达到了这个目标,他展示了一个Prolog程序,它对于每条查询都会机智地给出相同的回答:“No”。


    1973 - Robin Milner创造了ML,一个建立在M&M类型理论基础上的语言。由ML衍生而来的SML加上了一套形式语义的规范。当被要求给这个形式语义本身 书写一套形式语义时,Milner的脑子爆掉了。其他ML家族的著名语言还包括OCaml,F#,和,Visual Basic。


    1980 - Alan Kay创造了Smalltalk并发明了“面向对象”这个词。当被问到它的含义时,他回答道:“Smalltalk程序本身就是对象。”当被问到对象是由 什么组成时,他回答到:“对象。”当再一次被问到这个问题时,他说“看,它从里到外都是对象。直到你抽出一只乌龟。”

    Smalltalk的设计从很大程度上受到了Logo的影响。


    1983 - 为了纪念伟大的先辈程序员Ada Lovelace那能够写出永远也无法被执行的代码的彪悍技能,Jean Ichbiah和美国国防部创造了Ada语言。尽管缺乏证据显示有任何重要的Ada程序曾经被完成过,历史学家仍然确信Ada是个成功的公益项目,它让数 以千计的国防承包商免于沦落为与黑帮为伍。

    Ada曾经是美国国防部指定的嵌入式计算机系统唯一开发语言,在其研发上耗资巨大。(国防承包商们于是不用靠贩卖军火给黑帮来维持生计了)

    虽然有充分的证据显示Ada的整型范围溢出检查失败导致弄坏了欧空局的一枚Ariane 5运载火箭,不过美国国防部发言人对此表示:关我P事。


    1983 - Bjarne Stroustrup把他所听说过的一切都试图嫁接到C上,创造出了C++。最后得到的语言是如此地复杂,以至于程序必须被送到未来去让“天网”人工智能 进行编译。编译时间难以容忍。天网开展这项服务的动机仍然不为人知,但来自未来的发言人说道:“没什么好担心的,宝贝。”带着一口奥地利腔的机械口音。有 一些来自坊间的推测,所谓的天网只不过是个自命不凡的缓冲区溢出而已。


    1986 - Brad Cox和Tom Love创造了Objective-C,宣称“该语言完美地结合了C的内存安全性与Smalltalk的神奇效率”。现在的历史学家怀疑这两人其实是诵读障碍症患者。

    “C的内存安全性…”

    Smalltalk编译出来的程序以低效缓慢著称。


    1987 - Larry Wall在电脑前打了个盹,Larry Wall的脑门子压到了键盘上。醒来之后,Larry Wall深信 ,在Larry Wall的显示器上出现的神秘字符串并非是随机的,那是某种编程语言之程序样例的神谕。那必是上帝要他的先知,Larry Wall,去设计的。Perl语言就此诞生了。


    1990 - 一个由Simon Peyton-Jones、Paul Hudak、Philip Wadler、Ashton Kutcher和善待动物组织(PETA)组成的委员会创造了Haskell,一种纯函数式的、非严求值的语言。Haskell由于使用了Monad这种 较费解的概念来控制副作用而遭到了一些批评意见。Wadler试图平息这些质疑,他解释说:“一个单子(Monad)说白了不过就是自函子范畴上的一个幺 半群而已,这有什么难以理解的?”


    1991 - 荷兰程序员Guido van Rossum为了一次神秘的手术而进行了一次阿根廷之旅。回来后他带着一个巨大的颅疤,发明了Python,而被数以军团计的追随者们加冕为“终生大独裁 者”,并向全世界宣布“要办到一件事情,只可有唯一的一种方法!”。整个波兰陷入了恐慌。

    BDFL(Benevolent Dictator for
    Life):开源社区一种流行的说法,“仁慈的”终生大独裁者。这个说法最早指的就是Guido van Rossum。

    希特勒在提出建立“纯正的雅利安人国家”“统一的大德意志帝国”并实现了德奥合并之后,翌年便入侵了波兰,引发了第二次世界大战。“我一个人征服了整个欧洲!”(感觉好棒好棒的)


    1995 - 在家门口附近的一个意大利饭馆用餐时,Rasmus Lerdorf意识到他吃的那盘意面正好是一个用来理解WWW万维网的极好模型,而所有的Web应用都应该仿照它们的媒介那样去做。在他的餐巾的背后,他 设计出了著名的“可编程超链接Pasta(Programmable Hyperlinked Pasta,PHP)”语言。PHP的文档至今仍然保留在那片餐巾上。

    PHP最显著的特点就是:代码是可以直接嵌在HTML文档中的。


    1995 - 松本“Mad Matz”行弘创造出了Ruby语言,用来辟谣一些意味不明的、有关澳洲将会变成一片由莫霍克族战士和Tina Turner统治的荒漠的末世预言。该语言后来被它的真正发明者David Heinemeier Hansson重新命名为Ruby on Rails。(关于某个叫松本行弘的人发明了一种叫做Ruby的语言这件事情从未发生过,最好在这篇文章的下一个版本中删掉。 - DHH表示)

    最早关于Ruby的国际会议不是RubyConf,而是每年在澳大利亚举办的OSDConf。

    这里应该是在吐槽Ruby的杀手级应用Ruby on Rails实在太有名了,以至于超越了原来的Ruby语言本身。

    Matz并没有为“Ruby”这个名字注册商标——本着开源的黑客精神。

    而DHH(RoR的作者)却把“Ruby on
    Rails”这个(包含了“Ruby”字样的)名称注册成了商标,并且阻止别人未经授权使用“Rails”这个名字。

    (虽然抢注商标对开源来说未必是一件坏事情——Python基金会今年在欧洲还卷入了一场商标之争了不是)


    1995 - Brendan Eich读完了历史上所有在程序语言设计中曾经出现过的错误,自己又发明了一些更多的错误,然后用它们创造出了LiveScript。之后,为了紧跟 Java语言的时髦潮流,它被重新命名为JavaScript。再然后,为了追随一种皮肤病的时髦潮流,这语言又被命名为ECMAScript。


    1996 - James Gosling发明了Java。Java是一个相对繁冗的、带垃圾收集的、基于类的、静态类型的、单分派的面向对象语言,拥有单实现继承和多接口继承。Sun不遗余力地宣传着Java的独一无二不同凡响之处。


    2001 - Anders Hejlsberg发明了C#。C#是一个相对繁冗的、带垃圾收集的、基于类的、静态类型的、单分派的面向对象语言,拥有单实现继承和多接口继承。微软不遗余力地宣传着C#的独一无二不同凡响之处。


    2003 - 一个叫Martin Odersky的醉汉看见了好时瑞森花生酱杯的广告,展示了某个人的花生酱倒入另一个人的巧克力的场景,他忽然有了个点子。他创造了Scala,一种结合 了面向对象和函数式编程的语言。这同时激怒了两个阵营的忠实信徒,他们立刻宣布要发动圣战烧死异教徒。


    • 转载自微信公众号 STEAM解密
    展开全文
  • 有几个评估语言模型的指标,常见的是以应用为中心的方式,即在具体应用中以更高层级的指标进行评估。 更本质的方式应该是使用困惑度(perplexity)指标。 语言模型的传统方式 传统方式基于 k 阶 markov ...

    前段时间一口气读完了 NN4NLP,很是畅快,非常喜欢作者行文讲解的口吻和逻辑。大概两周读完,每页都有收获,读完后反而担心有所疏漏,知识太多留不住,索性从头来一遍,把学习过程的知识点和思考记录下来,也算精简版供自己今后查阅。
    感兴趣的,可以一起学习讨论,真的很推荐这本书。

    大致介绍下该书。NN4NLP 由 Goldberg 撰写,是 CMU CS11-747 课程的教材,配合公开课食用更佳,公开课链接。本书并非系统介绍 NN 和 NLP,而是聚焦 NN 在 NLP 领域的具体应用,所以分成了四大部分:NN 中前馈神经网络的入门,前馈神经网络在 NLP 中的应用,RNN 等特殊结构在 NLP 中的应用,部分前沿方向介绍。
    因此,本博客也打算分成多篇进行总结,其他篇章请自行搜索本博客。


    自然语言数据

    三、语言模型

    语言模型,即针对某种语言给定句子们分配概率值的任务,或者针对一串单词后跟一个词或者词串的可能性分配概率值的任务,实际这两个任务是等价的,可转换。语言模型在机器翻译和自动语音识别中具有重要作用,即对系统产生的多个翻译或者转录假设进行评分。

    形式上,语言模型的任务是针对任意序列的词串分配概率,利用概率的链式规则,可写作

    这里写图片描述
    添加马尔科夫假设后,可以大大降低计算复杂度,这里用 k 阶马尔科夫假设,即下一个词仅依赖于该词和更前面的 k 个词,可以近似为
    这里写图片描述
    虽然马尔科夫假设明显不合理,但大大降低了计算复杂度,得到的结果也还不错,因此流行了数十年。

    有几个评估语言模型的指标,常见的是以应用为中心的方式,即在具体应用中以更高层级的指标进行评估。
    更本质的方式应该是使用困惑度(perplexity)指标。

    语言模型的传统方式

    传统方式基于 k 阶 markov 假设来 model
    这里写图片描述
    利用最大似然估计,有
    这里写图片描述
    虽然有效,但最大缺点在于存在零事件,即由于训练数据有限导致上式分子经常为0。
    解决方法一是,采用平滑技术。
    解决方法二是,采用 back-off 技术,即如果 k gram 不存在,就用 k-1 gram 来近似。
    目前,非 NN 的最先进技术是使用修正 Kneser-Ney 平滑。

    上述基于平滑 MLE 估计的方法虽然方便训练、实际工作也不错,但有几个重要缺陷:
    ① 平滑技术本身比较复杂,且基于 back-off 到低阶事件。
    ② 很难应用到较大的 n-grams 场景。
    ③ 对上下文基本没有泛化能力,如观察到 black car 和 blue car 对估计未见过的 red car 基本没有影响

    语言模型的 NN 方式

    相应地,基于非线性 NN 的语言模型方法解决了传统语言模型的几个缺陷,即仅以参数数量的线性增长就可以处理较大的上下文尺寸,避免了手工设计 backoff 阶的必要,支持对不同上下文的泛化。

    此时,NN 的输入是 k-gram 的词串,输出是该词串下一个词的预测概率分布。这 k 个词形成一个词窗,每个配以嵌入向量,然后所有串联形成输入向量。如下式
    这里写图片描述
    其中,E 就是嵌入矩阵。

    (1)训练

    通常使用交叉熵损失,但此时要使用较费时的 softmax 操作,从而使得 NN 语言模型难以拓展到大规模词典情况。
    总而言之,扩大 n-gram 在计算复杂度上更好接受,但扩大 vocabulary 更难接受。
    因此,处理在较大 vocabulary 上计算 softmax 而面临的大规模输出空间问题,主流方法有
    ① Hierarchical softmax,即将 softmax 计算构造为树遍历形式
    ② Self-normalizing 方法
    ③ Sampling 方法

    (2)优点

    虽然有上面那个限制,但 NN 语言模型优点还是很多的,至少性能比目前最好的传统语言模型要好。此外,除了对 k-gram 阶数更自由,还能对上下文进行泛化。

    (3)缺点

    比传统语言模型在预测时开销更大;
    难以应用到大规模 vocabulary。

    在机器翻译系统中,神经语言模型并不比 Kneser-Ney 平滑语言模型表现更好,但当两者结合时,系统性能却得到了提升。
    实际上,两者可以互相补充:神经语言模型对未知事件泛化更好,但有时这种泛化却伤害了性能,此时僵化的传统模型反而更好。

    此外,神经语言模型带来了一个很有用的副产品,词向量,即上式中矩阵 E 的行和 W^2 的列,可以用来表征词。

    四、词向量

    1、预训练词向量

    初始化 embedding 向量的方式通常为随机初始化方法:初始化为均匀抽样随机数,即 word2vec 采用的,或者前面讲过的 xavier 初始化方法。
    实际中,通常对普遍出现的特征可以用随机初始化方法,如 pos 或字母。对于出现较少的特征如词,一般用监督或者无监督的预训练方式来初始化。

    (1)监督预训练方式

    假设我们感兴趣的任务 A 只有有限的标注数据,如句法分析任务,同时有一个标注数据充分的辅助任务 B ,如 pos 标注任务。
    此时,我们可以通过预训练词向量以在任务 B 上取得好的预测表现,然后用这个训练好的词向量再来训练任务 A。这样可以充分利用任务 B 上的大量标注数据。
    当训练任务 A 时,可以把预训练词向量固定,或者针对任务 A 继续训练。
    当然,还有种方式是两个目标结合起来进行,这在后面的博文中再介绍吧。

    (2)无监督预训练方式

    可惜的是,通常找不到拥有大量标注数据的辅助任务 B(或者我们想用更好的向量来引导辅助任务 B 的训练),这时就需要无监督辅助任务了,即在大量未标注文本上进行训练,实际即从原始文本中创造无限量的监督训练样本。
    一大好处是可以提供监督训练集中未出现词的词向量。

    无监督预训练方法的关键思想在于,期待相似词的嵌入向量具有相似的向量。
    但词的相似性很难定义,通常是任务相关的。目前主流思路来源于 distributional hypothesis,即出现在相似上下文的词就相似。

    前面提到词向量是语言模型的副产品,实际上,语言模型可以被视为无监督方法,即基于给定词的前 k 个词构成的上下文来预测该词。下面我们介绍具体的词嵌入算法。

    注意,辅助任务(基于什么类型的上下文,预测什么)的选择相对用来训练的学习方法,对结果向量的影响大得多。这方面在词嵌入算法后面介绍。

    2. 词向量训练算法

    将词视作连续空间中的一个点,即词的连续表示,也可以分为两种:分布式表示(Distributional Representations),分散式表示(Distributed Representations)。
    分布式表示,即通过共现矩阵的方式来进行词的表示,词的意义来源于其在语料中的分布。算法学习的是共现模式。
    离散式表示,即将语言表示为稠密、低维、连续的向量,每个词被表示为“a pattern of activations”值构成的向量,词的意义以及与其他词之间的关系通过向量中的激活值和向量之间的相似性来反映。算法学习的是激活模式。

    这两种表征看起来相距甚远,也就产生了不同族的算法和思考方式,但深层次上有关联关系,后面再讲。

    (1)分布式表示

    分布式表示使用 word-context 矩阵来捕捉词的分布式特性,表示为 M,其元素表示一个词和一个 context 之间的信息强度,通常用 PMI 计算。
    从而,词向量可以用矩阵 M 的行来表示。

    word-context 矩阵的一大缺点是数据稀疏和高维度。通常用 SVD 分解为低阶矩阵来表征。(这里讲到的 SVD 分解提到了一些近似理论,很有趣,有空再记录下)

    (2)离散式表示

    离散式表示中,词的含义不仅蕴含在整个词向量的所有维度里,还蕴含在其他词向量的维度中,即离散表示的向量维度是不可解释的,特定维并不对应特定概念。

    前面提到的语言模型的表达式中,矩阵 W^2的列向量或者嵌入矩阵 E 的行向量即词的离散式表示。
    但这毕竟是语言模型任务的副产品,有两个重要要求:
    ① 计算所有词的概率分布,即需要计算涉及词表中所有词的正则化项,计算开销太大
    ② 根据上下文去计算句子级的概率估计,即需要依据链式规则进行分解,从而限制了根据上下文处理 k-grams

    如果只关注结果的词表示,则可完全解决这两个限制。
    Collobert and Weston 与2008提出的模型解决了这个问题,Bengio 在2009又进行了深入处理。
    用 w 表示目标词,c(1:k)表示有序词串,对于只有一个隐层的 MLP,输入是串联 w 和 c(1:k) 的向量表示,输出时对这对 word-context 的打分,如下式:
    这里写图片描述
    训练采用基于豁余的排序损失,以保证对正确 word-context 对的打分高于错误 word-context 对的打分至少豁余量1。即表示为
    这里写图片描述
    其中,w’是词表中的随机词。
    训练过程就是重复处理语料中的 word-context 对,每次都是:随机抽样词 w’,计算 L,更新 U、v 和词、context 的 embedding 来最小化 loss。
    这里提到的随机抽样 word 形成错误的 word-context 对的 negative sampling 法来进行优化,在 word2vec 中也有使用。

    这里介绍下 word2vec,实际上它算一个工具包,实现了两种不同的 context 表征方式(CBOW 和 Skip-Gram),以及两种不同的优化目标(Negative-sampling 和 Hierarchical Softmax)。
    这里介绍下基于Negative-sampling目标的吧。

    类似于Collobert and Weston的方法,也是让网络学着区分好的和差的word-context 对,但 word2vec 做了些改变:将基于豁余的排序损失换成基于 sigmoid 的概率表示;大幅简化了word-context 对的打分定义。
    如基于 CBOW 的 word2vec 实现直接将 context 所有词的 embedding 向量的加和表示为 context 向量,然后 s(w,c)=wc。这样虽然损失了 context 中词序信息,但使得变长 context 成为可能。
    基于 Skip-gram 的 word2vec 实现进一步解耦了 context 中词之间的依赖,虽然解耦很彻底,但实践表现性能很好。

    (3)两者关联

    分布式方法和离散式方法,都是基于 Distributional hypothesis 的,都尝试从词所出现的 context 的相似性上来捕获词的相似性。
    word2vec 训练会得到两个嵌入矩阵,E^W ∈ R^(|V_W × d_emb|) 和 E^C ∈ R^(|V_C × d_emb|),分别表示 word 和 context。一般舍弃 E^C 留下E^W。实际上 E^W × (E^C)^T = M’ ,可以发现,word2vec 隐式分解了word-context 矩阵 M’。

    (4)其他类型算法

    除了上面提的,还有不少 word2vec 算法的变种,目前还未能定性或定量分析他们的性能差异。包括 NCE 和 GloVe。

    3. context 的选择

    预测 word 所用的 context 的选择,对所得的词向量和对相似度的编码具有重要的影响。

    (1)最常用的划窗法,即目标词和前 m 个词以及后 m 个词。
    更大的窗口,倾向于产生更局部的相似性,如dog、bark、leash更可能聚到一起,walked、walking、run 更可能聚到一起。
    更小的窗口,倾向于产生高跟功能性或语义层面的相似性,如 poodle、pitbull、rittweiler,walking、running、approaching。
    如果倾向于更语义层面的相似性,除了小窗口,还可以配合加入 context 的位置信息。

    (2)用目标词所在的句或段的所有词来做 context。
    其实相当于非常大尺寸的窗口。

    (3)用语义窗口来代替词窗口。
    这种方式能产生高度功能性的相似性,即集合起那些能在句子里充当相同角色的词。

    4. 预训练词向量的使用

    使用预训练词向量时,有些选择需要考量。

    (1)预处理
    即使用时直接用还是讲每个向量归一化到单位长度?

    在很多词嵌入算法中,词向量的范数和词频有关,对词向量进行归一化会损失掉频率信息,这可能算是一致性的需要,也可能是一种不该有的信息损失。这和具体任务有关。

    (2)再训练
    即针对目标任务是否以及如何对预训练词向量再精细化训练?

    考量词嵌入矩阵 E ∈ R^|V|×d。

    ① 将 E 视为模型参数,和网络其他参数一样进行更新训练。
    虽然表现不错,但可能由于带来不像要的副作用,即训练数据中存在的词的 representation 将改变,而原有的和其相似的词却没变,这将损害预训练词向量中的泛化能力。

    ② 保持 E 不变。
    虽然保留了预训练词向量的泛化能力,但缺失了对给定任务的适应性。

    ③ 保持 E 不变,但训练一个变换矩阵 T ∈ R^d×d,词向量不再从 E 中查行向量而是矩阵 E’=ET 的行向量。
    T 可以使得预训练向量的某些方面关于给定任务得到再训练。但是,通常任务相关的适配应该是针对所有词的线性变化,而非只是训练数据中出现的那些词。这个方法的缺点就是只能针对那些训练数据中存在的词进行线性变换。

    ④ 保持 E 不变,但训练一个变换矩阵 A ∈ R^|d|×d,词向量不再从 E 中查行向量而是矩阵 E’=E+A 或者 E’=ET+A 的行向量。
    矩阵 A 初始化为零向量,并随着网络进行训练,可以学习到特定词的加性变化。

    5. 基于 Distributional hypothesis 的局限性

    (1)相似性在实践中有很多方面,基于 Distributional hypothesis 的上述方法完全没法控制它们学习到的相似性,只有对 context 进行有条件的筛选能稍微起点控制的作用。

    (2)词所蕴含的很多更细节的性质可能没有反映在文本中,自然也就没法被 representation 捕获。不过这是由于语言文本的 well-documented bias 导致的,自然误导了基于 Distributional hypothesis 的上述方法。

    (3)反义词由于常出现在相同的 context 中,从而认为是相似的。

    (4)基于 Distributional hypothesis 的上述方法得到的最终词向量是和 context 无关了,但实际上词的含义和其 context 有关,即一词多义。显然对一个词在所有使用场景都使用一样的向量是不合理的。

    6. 应用

    (1)获取词向量

    直接使用预训练词向量,要注意应用场景是否一致,毕竟应使用的训练集很可能不一致。
    网上有修改过的 word2vec 二进制文件,可以根据需要修改 context 方式。
    Polyglot project 提供了多语种的预训练词向量。

    (2)词的相似度

    一般用两个词向量的 cosine 相似度。

    (3) 词聚类

    聚好的类可以作为后续学习算法的离散特征使用。

    (4)检索相似的词

    简单的矩阵乘法 s=Ew 得到的就是所有词和词 w 之间的相似度,然后按值取前 k 个最大值即可。

    (5)寻找一组词的最相似词

    (6)剔除最差异的词

    (7)短文本相似度

    短文本计算相似度特别适合使用词向量。通常定义为
    这里写图片描述
    对于归一化了的词向量,可以直接写作
    这里写图片描述

    当然,上述方法表现不错,一般作为很不错的 baseline,但当有训练数据时,用上监督学习可以进一步提升表现,如 Parikh2016 为 SNLI 语义推理任务所提的网络结构

    (8)词的类推

    (9)改造

    当我们找到一个规模更大更有代表性的词对集,比预训练的词向量更能反映想要的相似性,Faruqui2015提出的 retrofitting 方法可以利用这些数据来提高词向量矩阵的质量。主要思想描述如下:
    ① 假设矩阵 E 为预训练词向量矩阵,图 G 用来编码词和词之间的二进制相似性——图上的节点为词,如果两个词相似则用边相连(显然在边上加上权重就实现0-1连续相似性),很简单可以吧上述词对集表征为图 G。
    ② 构造OP:寻找新的词向量矩阵 E’,其中行向量不仅与 E 中相应行相似还和图 G 中相应邻居们的行相似,目标函数表示为
    这里写图片描述

    (10)映射

    当我们拥有两个预训练词向量矩阵,一个词表较小,一个词表较大,两个是分开训练的因此不相容。也许较小词表的矩阵反而是用更复杂的算法训练的,而较大词表的矩阵是从网上下载的。一般这两个词表彼此有重叠的部分,我们期望用较大矩阵 E^L 中的词向量来表示较小矩阵 E^S 中不存在的词的向量。这两个嵌入空间的桥接就需要用到 Kiros2015、Mikolov2013 提出的线性映射方法。主要思想描述如下:
    ① 训练目标是寻找一个好的映射矩阵 M ,对那些两个词表公有的词,将 E^L 相应行更近地映射到 E^S 的相应行,即表示为
    这里写图片描述
    ② 学好的矩阵 M 也可以用来从 E^L 映射 E^S 中没有的相应行。该方法被 Kiros2015用来增加基于 LSTM 句子编码器的词表扩展。

    展开全文
  • 常见的4种编程范式比较

    千次阅读 2021-01-16 23:00:03
    转载声明:感谢原作者分享!如有涉及版权问题,请与我联系。谢谢! 原作者: 向暖 ... 文章目录常见的4种编程范式比较1. 过程化编程 / 命令式编程2....Programming paradigm(编程范式) 是指某种编程语言典.

    转载声明:感谢原作者分享!如有涉及版权问题,请与我联系。谢谢!
    原作者: 向暖
    参考链接: https://juejin.cn/post/6844904078858797063



    在这里插入图片描述


    常见的4种编程范式比较

    Programming paradigm(编程范式) 是指某种编程语言典型的编程风格或编程方式。 编程范式是编程语言的一种分类方式,它并不针对某种编程语言。就编程语言而言,一种编程语言也可以适用多种编程范式。

    常见的编程范式:

    1. 过程化编程 / 命令式编程
    2. 事件驱动编程
    3. 面向对象编程 OOP
    4. 函数式编程 Functional Programming

    1. 过程化编程 / 命令式编程

    过程化编程(Procedural Programming)的步骤:

    1. 我们必须将带解决问题的解决方案抽象为一系列概念化的步骤。
    2. 通过编程的方法将这些步骤转化成程序指令集(算法),而这些指令按照一定的顺序排列,用来说明如何执行一个任务或解决一个问题。这意味着,程序员必须要知道程序要完成什么,并且告诉计算机如何进行所需的计算工作,包括每个细节操作。简而言之,就是把计算机看成一个善始善终服从命令的装置。

    所以在过程化编程中,把待解问题规范化、抽象为某种算法是解决问题的关键步骤。其次,才是编写具体算法和完成相应的算法实现问题的正确解决。

    人们把所有支持过程化编程范式的编程语言都被归纳为过程化编程语言。
    例如机器语言、汇编语言、BASIC、COBOL、C 、FORTRAN、语言等等许多第三代编程语言都被归纳为过程化语言。

    过程化语言适合解决线性的算法问题,强调“自上而下”、“精益求精”的设计方式。


    2. 事件驱动编程

    在过程式的程序设计中,代码本身就给出了程序执行的顺序,尽管执行顺序可能会受到程序输入数据的影响。在事件驱动的程序设计中,程序中的许多部分可能在完全不可预料的时刻被执行。往往这些程序的执行是由用户与正在执行的程序的互动激发所致。

    事件驱动常常用于用户与程序的交互,通过图形用户接口(鼠标、键盘、触摸板)进行交互式的互动。当然,也可以用于异常的处理和响应用户自定义的事件等等。

    事件与轮询的区别:轮询的行为是不断的观察和判断,是一种无休止的行为方式。而事件是静静的等待事情发生。

    属于事件驱动的编程语言有:VB、C#、Java(Java Swing的GUI)等。它们所涉及的事件绝大多数都是GUI(图形用户界面)事件。


    3. 面向对象编程 OOP

    面向对象的程序设计包括三个基本概念:封装性、继承性、多态性。
    面向对象的语言通过类、方法、对象和消息传递,来支持面向对象的程序设计范式。

    1. 对象
      面向对象的程序设计的抽象机制是将待解决问题抽象为面向对象的程序中的对象。利用封装是每个对象都拥有个体的身份。程序便是成堆的对象,彼此通过消息的传递,请求其他对象进行工作。

    2. 对象是类的实体。类是相似对象的集合。类中的对象可以接受相同的消息。即,类包含和描述了具有共同特性(数据元素)和共同行为(功能)的一组对象。
    3. 封装
      封装(有时也被称为信息隐藏)就是把数据和行为结合在一个包中,并对对象的使用者隐藏数据的实现过程。信息隐藏是面向对象编程的基本原则,而封装是实现这一原则的一种方式。封装使对象呈现出“黑盒”特性,这是对象再利用和实现可靠性的关键步骤。
    4. 接口
      接口不是类,而是对符合接口需求的类所作的一套规范。接口说明类应该做什么但不指定如何作的方法。一个类可以有一个或多个接口。
    5. 继承
      继承的思想就是允许在已存在类的基础上构建新的类。一个子类能够继承父类的所有成员,包括属性和方法。
      继承的主要作用:通过实现继承完成代码重用。继承是一种规范的技巧,而不是一种实现的技巧。
    6. 多态
      多态提供了“接口与实现分离”。 多态不但能改变程序的组织架构和可读性,更便利与开发出“可扩充”的程序。
      继承是多态的基础,多态是继承的目的。
      合理的运用基于类继承的多态、基于接口继承的多态和基于模版的多态,能增强程序的简洁性、灵活性、可维护性、可重用性和可扩展性。

    4. 函数式编程 Functional Programming

    函数式编程即是在软件开发的工程中避免使用共享状态(Shared State)可变状态(Mutable Data)以及副作用(Side Effects)

    函数式编程中整个应用由数据驱动,应用的状态在不同纯函数之间流动。
    与偏向命令式编程的面向对象编程而言,函数式编程其更偏向于声明式编程,代码更加简洁明了、更可预测,并且可测试性也更好。

    函数式编程本质上也是一种编程范式(Programming Paradigm),其代表了一系列用于构建软件系统的基本定义准则。
    核心是函数式编程是只使用纯粹的数学函数编程,函数的结果仅取决于参数,而没有副作用,就像 I/O 或者状态转换这样。程序是通过 组合函数function composition 的方法构建的。

    头等函数

    First Class Function 头等函数一类函数

    在计算机科学中,如果一门编程语言把函数看做头等公民就可以认为这门语言支持头等函数。具体也就是说,函数能像参数那样被传递到另一个函数、从另一个函数那像值一样被返回出来、函数可以赋值给变量或者存在数据结构中。

    高阶函数

    High Order Function 高阶函数指操作函数的函数,一般地,有以下两种情况:

    1. 函数可以作为参数被传递
    2. 函数可以作为返回值输出

    First class functions are functions that are treated like an object (or are assignable to a variable).

    Higher order functions are functions that take at least one first class function as a parameter.

    函数式编程语言有:Lisp,ML,Haskell,Erlang,Clojure,Elm,F#,OCaml,等等。

    在JavaScript中,支持函数式编程的特性有:一类函数,高阶函数,一个函数可以作为另外一个函数的参数或返回值,如闭包。

    5. 函数式编程 VS 面向对象

    优点不足
    面向对象编程关于“对象”的一些基础概念理解起来比较容易,方法调用的含义也好解释。面向对象编程通常使用命令式的编码风格,声明式(declarative style)的用得比较少。这样的代码读起来,像是一组直接的、计算机很容易就能遵循的指令。面向对象编程往往需要共享状态。对象及其行为常常会添加到同一个实体上,这样一来,如果一堆函数都要访问这个实体,而且这些函数的执行顺序不确定的话,很可能就会出乱子了,比如竞争条件(race conditions)这种现象(函数 A 依赖于实体的某个属性,但是在 A 访问属性之前,属性已经被函数 B 修改了,那么函数 A 在使用属性的时候,很可能就得不到预期的结果)。
    函数式编程用函数式范式来编程,就不需要担心共享状态或者副作用了。这样就避免了几个函数在调用同一批资源时可能产生的 bug 了。拥有了“无参风格”(point-free style,也叫隐式编程)之类的特性之后,函数式编程就大大简化了,我们也可以用函数式编程的方式来把代码组合成复用性更强的代码了,面向对象编程可做不到这一点。
    函数式编程更偏爱声明式、符号式(denotational style)的编码风格,这样的代码,并不是那种为了实现某种目的而需要按部就班地执行的一大堆指令,而是关注宏观上要做什么。至于具体应该怎么做,就都隐藏在函数内部了。这样一来,要是想重构代码、优化性能,那就大有可为了。
    (译者注:以做一道菜为例,就是由 买菜 -> 洗菜 -> 炒菜 这三步组成,每一步都是函数式编程的一个函数,不管做什么菜,这个流程都是不会变的。而想要优化这个过程,自然就是要深入每一步之中了。这样不管内部如何重构、优化,整体的流程并不会变,这就是函数式编程的好处。)甚至可以把一种算法换成另一种更高效的算法,同时还基本不需要修改代码(比如把及早求值策略(eager evaluation)替换为惰性求值策略(lazy evaluation))。利用纯函数进行的计算,可以很方便地扩展到多处理器环境下,或者应用到分布式计算集群上,同时还不用担心线程资源冲突、竞争条件之类的问题。
    代码如果过度利用了函数式的编程特性(如无参风格、大量方法的组合),就会影响其可读性,从而简洁度有余、易读性不足。
    大部分工程师还是更熟悉面向对象编程、命令式编程,对于刚接触函数式编程的人来说,即使只是这个领域的一些的简单术语,都可能让他怀疑人生。
    函数式编程的学习曲线更陡峭,因为面向对象编程太普及了,学习资料太多了。相比而言,函数式编程在学术领域的应用更广泛一些,在工业界的应用稍逊一筹,自然也就不那么“平易近人”了。
    在探讨函数式编程时,人们往往用 λ 演算、代数、范畴学等学科的专业术语和专业符号来描述相关的概念,那么其他人想要入门函数式编程的话,就得先把这些领域的基础知识搞明白,能不让人头大么。
    展开全文
  • 中文自然语言处理入门实战

    万次阅读 多人点赞 2018-07-03 02:45:10
    本达人课,作为中文自然语言处理边学边实战的入门级教程,以小数据量的“简易版”实例,通过实战带大家快速掌握 NLP 在中文方面开发的基本能力。 本课程共包含 18 节。各小节之间并没有紧密耦合,但是整个内容还是...
  • 编程语言概述 计算机编程语言是程序设计的最重要的工具,它是指计算机能够接受和处理的、具有一定语法规则的语言。 编程语言处在不断的发展和变化中,从最初的机器语言发展到如今的2500种以上的高级语言,每种...
  • 最近沉迷学习R语言,不得不说《R语言实战》真的太详细了,详细的阅读完这本书后,我的收获不小。为了方便自己复习,将这本书的内容整理归纳为两个部分:统计篇(数据分析)和图形篇(数据可视化)。图形篇将会分为四个...
  • 几种常见语言 C语言 概述:C语言是目前使用最广泛的高级程序设计语言之一,它是数值计算的高级语言。 应用:对操作系统和系统使用程序,以及需要对硬件进行操作的场合,用C语言明显优于其他高级语言,许多大型...
  • 原标题:语言学与人工智能的未来当谈及人工智能,很多人会不约而同的认为---机器人!这是受到了思维定式的限制。人工智能,是一种对人类智能现象的模拟,其中包括对人类思维过程的模拟。人工智能的发展似乎并不遵循...
  • 谈谈如何学习R语言 一、谈谈R语言的历史 R语言的前身是S语言,那么S语言又是什么呢?S语言是由贝尔实验室开发出来的一种用来进行数据探索、统计分析、数据可视化的一门解释性语言。 R语言由新西兰奥克兰大学的Ross ...
  • 6 种老而不死的编程语言

    千次阅读 2021-01-12 13:11:00
    Go 开发人员兼作家 Jon Bodner 解释说: 关于 Go 的最常见的抱怨之一是错误处理太冗长。因此,在 6 月初,Go 核心开发人员建议添加一个名为 try 的新内置函数。GitHub 的 issue 现在已打开,以讨论此新功能。一个月...
  • 北京时间11月30日凌晨,在美国拉斯维加斯召开的AWS re: Invent 2018上,和往年一样,AWS CTO Werner·Vogels博士又发布了AWS多项重要的更新,包括数据库、编程工具、架构等多个方面,而在其中,有两项针对于AWS ...
  • 中文文本纠错任务,常见错误类型包括: 谐音字词,如 配眼睛-配眼镜 混淆音字词,如 流浪织女-牛郎织女 字词顺序颠倒,如 伍迪艾伦-艾伦伍迪 字词补全,如爱有天意-假如爱有天意 形似字错误,如 高梁-...
  • 更多R语言信息欢迎关注我的新浪微博:Jenny爱学习微信公众号:R语言数据分析与实践Mac版R语言入门:R语言中的基础操作分析数据时,经常遇到分类变量。例如,假设你有一系列关于人群特征的数据,其中一个指标是瞳孔...
  • 编程语言的发展趋势及未来方向

    千次阅读 2019-05-30 11:48:33
    感谢作者的整理,可以让语言使用者对语言的...国内的计算机教育和工程培训,似乎一直在宣传“语言不重要,重要的是思想”,“语言一通百通”等观点,甚至在许多人眼中“语言的讨论”完全是不入流的,但其实“编程语...
  • 《Julia语言程序设计》是国内第一本介绍Julia语言的书,内容很全面,也极为条理,实践要点也把握的很准确,有些坑,官方文档都没有,书中都交代的很清楚。 该书的介绍如下(链接:...
  • Presentation on theme: "大数据应用人才培养系列教材 Python语言 刘 鹏 张 燕 总主编 李肖俊 主编 刘 河 钟 涛 主编."— Presentation transcript:大数据应用人才培养系列教材 Python语言 刘 鹏 张 燕 总主编 李...
  • JavaScript语言精髓与编程实践(第2版)

    千次阅读 2012-05-04 10:08:02
    JavaScript语言精髓与编程实践(第2版)  周爱民 著 ISBN978-7-121-15640-3 ...本书详细讲述JavaScript 作为一种混合式语言的各方面特性,包括过程式、面向对象、函数式和动态语言特性等,在动态函数式语言
  • Lua语言模型 与 Redis应用

    万次阅读 2016-10-01 10:01:46
    从 2.6版本 起, Redis 开始支持 Lua 脚本 让开发者自己扩展 Redis. 本篇博客主要介绍了 Lua ... 我们希望这篇博客的读者朋友可以在读完这篇文字之后, 体会到 Lua 这门语言不一样的设计哲学, 以及 更加得心应手的使用
  • R语言进阶之图形参数

    2020-12-21 20:31:12
    1. 概述R语言是即使一款功能强大的统计语言,也是一款内容丰富的绘图工具。从原则上讲,你可以用R语言绘制出你能想到的任何图形。你可以使用par()函数来设置绘图的相关参数(字体、坐标轴、标题等),这种方式设置的...
  • 文法和语言的基本知识

    千次阅读 2016-08-11 11:29:22
    本博文中,将概述高级程序语言的结构和主要的共同特征,并介绍程序语言的语法描述方法。主要内介绍内容是程序设计语言的定义,高级语言的一般特性,高级语言的语法描述,上下文无关文法,语法分析树和二义性,...
  • 程序设计语言分类

    千次阅读 2017-03-18 10:34:35
    程序设计语言常见分类
  • 图书简介本教材在作者20多年C/C++语言教学,特别是项目开发的基础上,站在初学者的角度,在注重基础知识学习的同时,以培养学生良好的编程规范意识和编程思想为重点精心编写,例程丰富,层次感强,非常适合作为学习C/...
  • 本文将对NNLM,C&W模型,RNNLM,CBOW模型,Skip-gram模型等经典神经网络语言模型进行介绍,对比分析这些模型的结构和特点,并对神经网络语言模型的构造方法进行讨论。
  • C语言 作用(side effect)是指对数据对象或者文件的修改,序列点(sequence point)是指程序运行中的一个特殊的时间点,在该点之前的所有作用已经结束,并且后续的作用还没发生。测试例子1:#includeint main...
  • 作为课题组的自然语言处理面试官,我出了一道开放性的问题,很简单,面试题为:介绍一下开源中文自然语言处理工具包的常见词性、依存关系标记及其对应的含义。但至今为止,没有人答上来过,这让我感到有点失望。做...
  • Go编程语言规范

    2021-03-12 14:09:33
    Go编程语言规范 版本2021年2月10日 介绍符号源代码表示人物字母和数字词汇要素评论代币分号身份标识关键字词运算符和标点符号整数文字浮点文字虚构文字符文文字字符串文字常数变数种类方法集布尔类型数值类型
  • 目录R语言之生信④TCGA生存分析1=========================================================正文生存分析(Survival analysis)是指根据试验或调查得到的数据对生物或人的生存时间进行分析和推断,研究生存时间和结局...
  • 常用于根治性手术治疗或放疗后的辅助治疗,如乳腺癌术后内分泌疗法等: event free survival(EFS,无事件生存期):指从开始到发生任何事件的时间,这里的事件包括肿瘤进展,死亡,治疗方案的改变,致死作用等...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 31,416
精华内容 12,566
关键字:

常见的副语言包括