我的编程空间,编程开发者的网络收藏夹
学习永远不晚

揭开机器学习转换器架构的神秘面纱

短信预约 -IT技能 免费直播动态提醒
省份

北京

  • 北京
  • 上海
  • 天津
  • 重庆
  • 河北
  • 山东
  • 辽宁
  • 黑龙江
  • 吉林
  • 甘肃
  • 青海
  • 河南
  • 江苏
  • 湖北
  • 湖南
  • 江西
  • 浙江
  • 广东
  • 云南
  • 福建
  • 海南
  • 山西
  • 四川
  • 陕西
  • 贵州
  • 安徽
  • 广西
  • 内蒙
  • 西藏
  • 新疆
  • 宁夏
  • 兵团
手机号立即预约

请填写图片验证码后获取短信验证码

看不清楚,换张图片

免费获取短信验证码

揭开机器学习转换器架构的神秘面纱

审校 | 重楼

自2017年推出以来,转换器Transformers已成为机器学习领域的一支突出力量,彻底改变了专业翻译和自动完成服务的能力。

最近,随着OpenAI公司ChatGPT和Meta公司LLama等大型语言模型的出现,转换器的受欢迎程度飙升。所有上述这些模型都建立在转换器架构的基础上,引起了业界极大的关注。通过利用转换器的力量,这些模型在自然语言理解和生成方面取得了显著突破。

尽管目前网络上已经存储很多很好的资源可以解释转换器的工作方式,但我发现自己仅停留在一个理解转换器数学工作原理层次上很难直观地解释转换器是如何工作的。在进行了多次采访,与我的同事交谈,并就这个问题进行了闪电式的简短演讲之后,我发现似乎很多人都存在这样的问题!

在这篇博客文章中,我将力提供一个关于转换器如何在不依赖代码或数学原理的情况下工作原理的高级解释。我的目标是避免混淆技术术语,避免与以前的体系结构进行比较。虽然我会尽量保持简单,但这并不容易,因为转换器非常复杂,但我希望它能更好地直观地了解它们做什么以及如何做。

什么是转换器?

转换器是一种神经网络架构,非常适合处理序列作为输入的任务。在这种情况下,序列最常见的例子可能是一个句子,我们可以将其视为一组有序的单词。

这些模型的目的是为序列中的每个元素创建一个数字表示用于封装关于元素及其相邻上下文的基本信息。然后,可以将得到的数字表示传递给下游网络,下游网络可以利用这些信息来执行各种任务,包括生成和分类。

通过创建这样丰富的表示,这些模型使下游网络能够更好地理解输入序列中的潜在模式和关系,这增强了它们生成连贯和上下文相关输出的能力。

转换器的关键优势在于它们能够处理序列中的范围的依赖关系,并且效率很高;能够并行处理序列。这对于机器翻译、情感分析和文本生成等任务特别有用。

Azure OpenAI服务DALL-E模型生成的图像,其中带有以下提示:“The green and black Matrix code in the shape of Optimus Prime擎天柱形状的绿色和黑色矩阵代码

输入到转换器的内容

要将输入馈送到转换器中,我们必须首先将其转换为标记序列——表示我们输入的一组整数。

由于转换器最初应用于自然语言处理领域,所以让我们首先考虑这个场景。将一个句子转换为一系列标记的最简单方法是定义一个词汇表,该词汇表充当查找表,将单词映射为整数;我们可以保留一个特定的数字来表示这个词汇表中不包含的任何单词,这样我们就可以总是分配一个整数值。

在实践中,这是一种过于简单的文本编码方式,因为catcats等词被视为完全不同的标记,尽管它们是对同一动物的单数和复数描述!为了克服这一点,人们设计了不同的标记化策略,如字节对编码在对单词进行索引之前,将其分解成更小的块。此外,添加特殊的标记来表示句子的开头和结尾等特征,为模型提供额外的上下文,这通常很有用。

让我们考虑下面的例子,以更好地理解标记化过程。

“Hello there, isn’t the weather nice today in Drosval?你好,德罗斯瓦尔今天天气好吗?

这里,Drosval是GPT-4使用以下提示生成的名称:“Can you create a fictional place name that sounds like it could belong to David Gemmell’s Drenai universe?你能创建一个听起来可能属于David Gemmell的Drenai宇宙的虚构地名吗?”;这是故意选择,因为它不应该出现在任何训练过的机器模型的词汇表中。

借助转换器库中的 bert-base-uncased分词器,将上面的语句转换为以下标记序列:

表示每个单词的整数将根据特定的模型训练和标记化策略而变化。解码后,我们可以看到每个标记所代表的单词:

有趣的是,我们可以看到这与我们当初的输入不同。其中添加了一些特殊的标记,我们的缩写被拆分为多个标记,我们虚构的地名由不同的“块”表示。当我们使用前面所述的bert-base-uncased模型时,我们也失去了所有的大写上下文

然而,虽然我们在示例中使用了一个句子,但转换器并不局限于文本输入;该体系结构在视觉任务上也取得了良好的效果。为了将图像转换为序列,ViT译者注:是指转换器在CV领域中的两个经典算法之一,另一个算法是DeiT)的作者将图像切片为不重叠的16x16像素块,并在将其传递到模型中之前将其连接成长向量。如果我们在推荐系统中使用转换器,一种方法可以是使用用户浏览的最后n个项目的项目ID作为我们网络的输入。如果我们能够为我们的域创建一个有意义的输入标记表示,我们就可以将其输入到转换器网络中。

嵌入我们的标记

一旦我们有了一个整数序列来表示我们的输入,我们就可以将它们转换为嵌入。嵌入是一种表示信息的方式,可以通过机器学习算法轻松处理;他们的目的是通过将信息表示为一系列数字来捕捉以压缩格式编码的标记的含义。最初,嵌入被初始化为随机数序列,并且在训练期间学习有意义的表示。然而,这些嵌入有一个固有的限制:它们没有考虑到标记出现的上下文。这有两个方面。

一个问题是,根据任务的不同,当我们嵌入标记时,我们可能还希望保留标记的顺序;这在NLP等领域尤其重要否则我们基本上会采用单词袋方法。为了克服这一点,我们将位置编码应用于嵌入。虽然有多种方法可以创建位置嵌入,但主要思想是我们有另一组嵌入,它们表示输入序列中每个标记的位置,并与我们的标记嵌入相结合。

另一个问题是,根据周围的标记内容标记可能有不同的含义。考虑以下句子:

It’s dark, who turned off the light?天黑了,谁关灯了?

Wow, this parcel is really light!哇,这个包裹真轻!

在这里,“light”这个词被用于两个不同的上下文,在不同的上下文它有完全不同的含义!然而,根据标记化策略,嵌入可能是相同的。在转换器中,这是由它的注意力机制来处理的。

从概念上讲,什么是注意力?

转换器架构使用的最重要的机制可能是注意力,它使网络能够了解输入序列的哪些部分与给定任务最相关。对于序列中的每个标记,注意力机制识别哪些其他标记对于理解给定上下文中的当前标记很重要。在我们探索如何在转换器中实现这一点之前,让我们从简单的内容开始,试着理解注意力机制在概念上试图实现什么,以便建立我们的直觉理解基础

理解注意力的一种方法是将其视为一种方法,该方法将每个标记嵌入替换为包含关于其相邻标记的信息的嵌入;而不是对每个标记使用相同的嵌入,而不管其上下文如何。如果我们知道哪些标记与当前标记相关,那么捕获此上下文的一种方法是创建这些嵌入的加权平均值,或者更一般地说,线性组合。

让我们考虑一个简单的例子,说明如何查找我们前面看到的一个句子。在应用注意力之前,序列中的嵌入没有其邻的上下文。因此,我们可以将单词light的嵌入可视化为以下线性组合。

在这里,我们可以看到,我们的权重只是单位矩阵。在应用我们的注意力机制后,我们想学习一个权重矩阵,这样我们就可以用类似于下面的方式来表达我们的light嵌入。

这一次,对与我们选择的标记的序列的最相关部分相对应的嵌入赋予更大的权重;这应当确保在新的嵌入向量中捕获最重要的上下文。

包含当前上下文信息的嵌入有时被称为上下文嵌入,这最终是我们试图创建的。

既然我们已经对注意力试图实现的目标有了很高的理解,那么让我们在下一节中探讨一下这是如何实际实现的。

注意力是如何计算的?

注意力有多种类型,主要区别在于用于执行线性组合的权重的计算方式。在这里,我们考虑一下原始论文中介绍的缩放点积注意力,因为这是最常见的方法。在本节中,假设我们所有的嵌入都已进行了位置编码。

回想一下,我们的目标是使用原始嵌入的线性组合来创建上下文嵌入,让我们从简单的讲解开始,假设我们可以将所需的所有必要信息编码到我们学习的嵌入向量中,我们所需要计算的只是权重。

要计算权重,我们必须首先确定哪些标记彼此相关。为了实现这一点,我们需要在两个嵌入之间建立一个相似性的概念。表示这种相似性的一种方法是使用点积,我们希望学习嵌入,这样得分越高,两个单词就越相似。

对于每个标记,我们需要计算其与序列中其他标记的相关性,我们可以将其推广为矩阵乘法,这为我们提供了权重矩阵;其通常被称为注意力得分。为了确保我们的权重总和为1,我们还应用SoftMax函数。然而,由于矩阵乘法可以产生任意大的数字,这可能导致SoftMax函数对于大的注意力分数返回非常小的梯度;这可能导致训练过程中的梯度消失问题。为了抵消这种影响,在应用SoftMax之前,将注意力分数乘以比例因子。

现在,为了得到我们的上下文嵌入矩阵,我们可以将注意力得分与原始嵌入矩阵相乘;这相当于我们的嵌入的线性组合。

简化的注意力计算:假设嵌入是位置编码的

虽然模型可能学习足够复杂的嵌入,以生成注意力得分和随后的上下文嵌入;我们试图将大量信息压缩到嵌入维度中,嵌入维度通常很小。

因此,为了让模型更容易学习这项任务,让我们介绍一些更容易学习的参数!与其直接使用嵌入矩阵,不如让它通过三个独立的线性层(矩阵乘法);这应该使模型能够“注意”嵌入的不同部分。如下图所示:

缩放后的点积自注意:假设嵌入是位置编码的

从图像中,我们可以看到线性投影被标记为Q、K和V。在最初的论文中,这些投影被命名为Query、Key和Value,据说是从信息检索中获得的灵感。就我个人而言,我从未发现这种类比有助于我的理解,所以我倾向于不关注这一点;为了与文献保持一致,我遵循了这里的术语,并明确表示这些线性层是不同的。

现在我们了解了这个过程是如何工作的,我们可以把注意力计算看作一个有三个输入的单个块,这些输入将传递给Q、K和V。

当我们将相同的嵌入矩阵传递给Q、K和V时,这被称为自注意。

什么是多头注意力?

在实践中,我们经常并行使用多个自注意块,以便使转换器能够同时关注输入序列的不同部分——这被称为多头注意(multi-head attention)

多头注意力背后的想法很简单,多个独立的自我注意力块的输出被连接在一起,然后通过线性层。这个线性层使模型能够学习组合来自每个注意力头部的上下文信息。

在实践中,每个自注意块中使用的隐藏维度大小通常被选择为原始嵌入大小除以注意头的数量;以保持嵌入矩阵的形状。

转换器还由什么组成?

尽管介绍转换器的论文(现在臭名昭著)被命名为“注意力”,但这有点令人困惑,因为转换器的组件不仅仅是注意力!

其实,转换器块还包含以下内容:

  • 前馈神经网络(FFN):一种两层神经网络,独立应用于批量和序列中的每个标记嵌入。FFN块的目的是将额外的可学习参数引入到转换器中,这些参数负责确保上下文嵌入是不同的和分散的。最初的论文使用了GeLU激活函数,但FFN的组件可能因架构而异。
  • 层规范化:有助于稳定深度神经网络的训练,包括转换器。它使每个序列的激活函数规范化,防止它们在训练过程中变得过大或过小;这可能导致梯度相关的问题,例如梯度消失或爆炸。这种稳定性对于有效训练非常深入的转换器模型至关重要。
  • 跳过连接:与ResNet架构一样,残差连接用于缓解消失梯度问题并提高训练稳定性。

虽然转换器架构自引入以来一直保持相当稳定,但层规范化块的位置可能因转换器架构而异。原始架构,现在称为后层规范(post-layer norm),如下所示:

如下图所示,在最近的体系结构中,最常见的放置是层规范(pre-layer norm),它将规范化块放置在跳过连接中的自注意块和FFN块之前。

转换器有哪些不同类型?

虽然现在有许多不同的转换器架构,但大多数可以分为三种主要类型。

编码器架构

编码器模型旨在产生可用于下游任务(如分类或命名实体识别)的上下文嵌入,因为注意力机制能够注意整个输入序列;这就是本文迄今为止所探讨的体系结构类型。最流行的编码器专用转换器系列是BERT及其变体。

在将我们的数据通过一个或多个转换器块之后,我们有一个复杂的上下文嵌入矩阵,表示序列中每个标记的嵌入。然而,要将其用于诸如分类之类的下游任务,我们只需要进行一次预测。传统上,第一个标记被获取,并通过分类头;其通常包含Dropout层和Linear层。可以通过SoftMax函数将这些层的输出转换为类概率。下面描述了一个这样的例子。

解码器架构

与编码器架构几乎相同,关键区别在于解码器架构采用了屏蔽(或因果)自注意层,因此注意机制只能注意输入序列的当前和先前元素;这意味着生成的上下文嵌入只考虑先前的上下文。流行的仅解码器的模型包括GPT系列

这通常是通过用二进制下三角矩阵屏蔽注意力得分,并用负无穷大替换未屏蔽的元素来实现的;当通过以下SoftMax操作时,这将确保这些位置的注意力得分等于零。我们可以更新我们以前的自我注意图,将其包括在内,如下所示

屏蔽自注意计算:假设采用位置编码嵌入

由于解码器只能从当前位置向后参与计算,因此解码器架构通常用于自回归任务,如序列生成。然而,当使用上下文嵌入来生成序列时,与使用编码器相比,还有一些额外的考虑因素。下面显示了一个示例。

我们可以注意到,虽然解码器为输入序列中的每个标记生成上下文嵌入,但在生成序列时,我们通常使用与最终标记相对应的嵌入作为后续层的输入。

此外,在将SoftMax函数应用于logits之后,如果不应用过滤方案,我们将在模型词汇表中的每个标记上接收概率分布;这可能非常大!通常,我们希望使用各种过滤策略来减少潜在选项的数量,其中一些最常见的方法是:

  • 温度调整:温度Temperature)是一个应用于SoftMax操作内部的参数,它会影响生成文本的随机性。它通过改变输出单词的概率分布来确定模型输出的创造性或重点内容。温度参数越高,分布越平坦,输出越多样化。
  • Top-P采样:这种方法基于给定的概率阈值过滤下一个标记的潜在候选者的数量,并基于高于该阈值的候选者重新分布概率分布。
  • Top-K采样:这种方法根据其logit或概率得分(取决于实现)将潜在候选者的数量限制为K个最可能的标记有关这些方法的更多详细信息,请访问链接:https://peterchng.com/blog/2023/05/02/token-selection-strategies-top-k-top-p-and-temperature/
    一旦我们改变或减少了下一个标记的潜在候选者的概率分布,我们就可以从中采样来得到我们的预测——这只是从多项式分布中采样。然后将预测的标记附加到输入序列并反馈到模型中,直到生成了期望数量的标记,或者模型生成了停止标记;表示序列结束的特殊标记。

编码器-解码器架构

最初,转换器是作为机器翻译的一种架构提出的,并使用编码器和解码器来实现这一目标;使用所述编码器来创建中间表示。虽然编码器-解码器转换器已经变得不那么常见,但诸如T5之类的架构展示了如何将诸如问题回答、总结和分类之类的任务构建为序列到序列的问题,并使用这种方法来解决。

与编码器-解码器架构的关键区别在于,解码器使用编码器-解码器注意力,其在注意力计算期间使用编码器的输出(作为K和V)和解码器块的输入(作为Q)。这与自注意形成对比,在自注意中,相同的输入嵌入矩阵用于所有输入。除此之外,整个生成过程与仅使用解码器架构非常相似。

我们可以将编码器-解码器架构可视化,如下图所示。在这里,为了简化图,我选择描绘原始论文中所见的转换器的后层规范变体;其中规范层位于注意块之后。

结论

总之,希望本文提供一种关于转换器工作原理的直觉理解帮助,有助于以一种易于理解的方式把握此架构中的一些细节,并成为揭开现代转换器架构神秘面纱的良好起点!

最后,除非另有说明,否则所有图像均由作者创作。

参考资料

  • [1706.03762] Attention Is All You Need (arxiv.org)
    https://arxiv.org/abs/1706.03762。
  • Recent Advances in Google Translate — Google Research Blog
    https://blog.research.google/2020/06/recent-advances-in-google-translate.html。
  • How GitHub Copilot is getting better at understanding your code — The GitHub Blog
    https://github.blog/2023-05-17-how-github-copilot-is-getting-better-at-understanding-your-code/。
  • Introducing ChatGPT (openai.com):https://openai.com/blog/chatgpt。
  • gpt-4.pdf (openai.com)
    https://cdn.openai.com/papers/gpt-4.pdf
  • Introducing LLaMA: A foundational, 65-billion-parameter language model (meta.com)
    https://ai.meta.com/blog/large-language-model-llama-meta-ai/。
  • The Illustrated Transformer — Jay Alammar — Visualizing machine learning one concept at a time. (jalammar.github.io):http://jalammar.github.io/illustrated-transformer/。
  • Byte-Pair Encoding tokenization — Hugging Face NLP Course:https://huggingface.co/learn/nlp-course/chapter6/5?fw=pt。
  • Drenai series | David Gemmell Wiki | Fandom:https://davidgemmell.fandom.com/wiki/Drenai_series。
  • bert-base-uncased · Hugging Face
    https://huggingface.co/bert-base-uncased。
  • Transformers (huggingface.co)
    https://huggingface.co/docs/transformers/index。
  • [2010.11929v2] An Image is Worth 16x16 Words: Transformers for Image Recognition at Scale (arxiv.org)
  • Getting Started With Embeddings (huggingface.co):https://huggingface.co/blog/getting-started-with-embeddings。
  • A Gentle Introduction to the Bag-of-Words Model — MachineLearningMastery.com:https://machinelearningmastery.com/gentle-introduction-bag-words-model/。
  • [2104.09864] RoFormer: Enhanced Transformer with Rotary Position Embedding (arxiv.org):https://arxiv.org/abs/2104.09864。
  • Scaled Dot-Product Attention Explained | Papers With Code:https://paperswithcode.com/method/scaled。
  • Softmax function — Wikipedia:https://en.wikipedia.org/wiki/Softmax_function。
  • [1607.06450] Layer Normalization (arxiv.org):https://arxiv.org/abs/1607.06450。
  • Vanishing gradient problem — Wikipedia:https://en.wikipedia.org/wiki/Vanishing_gradient_problem。
  • [1512.03385] Deep Residual Learning for Image Recognition (arxiv.org)
    https://arxiv.org/abs/1512.03385。
  • [1810.04805] BERT: Pre-training of Deep Bidirectional Transformers for Language Understanding (arxiv.org):https://arxiv.org/abs/1810.04805。
  • [2005.14165] Language Models are Few-Shot Learners (arxiv.org):https://arxiv.org/abs/2005.14165。
  • Token selection strategies: Top-K, Top-P, and Temperature (peterchng.com):https://peterchng.com/blog/2023/05/02/token-selection-strategies-top-k-top-p-and-temperature/。
  • Multinomial distribution — Wikipedia:https://en.wikipedia.org/wiki/Multinomial_distribution。
  • [1910.10683] Exploring the Limits of Transfer Learning with a Unified Text-to-Text Transformer (arxiv.org):https://arxiv.org/abs/1910.10683。

译者介绍

朱先忠,51CTO社区编辑,51CTO专家博客、讲师,潍坊一所高校计算机教师,自由编程界老兵一枚。

原文De-coded: Transformers explained in plain English,作者:Chris HughesChris Hughes


免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

揭开机器学习转换器架构的神秘面纱

下载Word文档到电脑,方便收藏和打印~

下载Word文档

猜你喜欢

揭开机器学习转换器架构的神秘面纱

自2017年推出以来,转换器(Transformers)已成为机器学习领域的一支突出力量,彻底改变了专业翻译和自动完成 服务的能力。

揭开AI、机器学习和深度学习的神秘面纱

深度学习、机器学习、人工智能——这些流行词皆代表了分析学的未来。在这篇文章中,我们将通过一些真实世界的案例来解释什么是机器学习和深度学习。在以后的文章中,我们将探索垂直用例。

揭开深度强化学习的神秘面纱

深度强化学习是人工智能最令人关注的分支之一。它是人工智能领域一些技术最显著成就的背后支撑,包括在棋盘和电子游戏、自动驾驶汽车、机器人和人工智能硬件设计方面中击败人类冠军。

揭开CSS属性选择器的神秘面纱

CSS属性选择器的秘密揭示CSS属性选择器是一种非常有用和强大的工具,它允许我们通过元素的属性值来选择和样式化特定的元素。这些属性选择器可以根据元素的属性值、属性值的出现位置以及属性值的特定字符等条件进行匹配和选择。本文将通过具体的代码示
揭开CSS属性选择器的神秘面纱
2024-01-15

揭开 PHP 微服务架构的神秘面纱:组件和通信

PHP 微服务架构是一种构建现代化、可扩展和高性能应用程序的强大方法。本文将深入探讨 PHP 微服务架构的组件和通信机制,揭开其神秘面纱。
揭开 PHP 微服务架构的神秘面纱:组件和通信
2024-02-16

Vuex 状态管理:揭开 React 受宠神器的神秘面纱

Vuex 是一个用于 Vue.js 的状态管理模式,它可以帮助你管理应用程序的状态。通过使用 Vuex,你可以轻松地将应用程序的状态存储在单一的地方,并且可以很容易地共享和维护这些状态。
Vuex 状态管理:揭开 React 受宠神器的神秘面纱
2024-02-21

揭开Groq LPU神秘面纱:世界最快硬件加速器的底层架构设计!

Groq一夜爆火的背后,是自研的语言处理单元硬件LPU,近日,Substack的专栏作家Abhinav Upadhyay为我们一步步揭示了LPU底层架构的奥秘。
AI数据2024-11-30

数据库加速器:揭开性能调优的神秘面纱

数据库加速器通过优化数据访问、减少数据冗余和改进查询性能来提高数据库的整体性能。
数据库加速器:揭开性能调优的神秘面纱
2024-02-21

转换机器学习:面向多学科问题,构建机器学习新生态

近日发表在 PNAS 的一项研究开发了一种名为转换机器学习的方法,能够综合利用多个相关任务的数据及多种学习方法,提取编码于训练模型中不同来源的先验知识,尤其适用于药物设计等对可解释性有强需求的科学研究领域。

Python异步编程: 并发编程的利器, 揭开其神秘面纱

Python异步编程是一种强大的技术,可以实现高并发、高性能的程序,本文将揭开异步编程的神秘面纱,带你领略其魅力。
Python异步编程: 并发编程的利器, 揭开其神秘面纱
2024-02-25

揭开数据库触发器的神秘面纱:深入探索触发器的运作机制

数据库触发器是一种数据库对象,它可以自动执行某些操作,对数据库中数据的改变做出相应反应。触发器主要用于确保数据库数据的完整性和一致性,并简化某些任务的执行。
揭开数据库触发器的神秘面纱:深入探索触发器的运作机制
2024-02-08

编程热搜

  • Python 学习之路 - Python
    一、安装Python34Windows在Python官网(https://www.python.org/downloads/)下载安装包并安装。Python的默认安装路径是:C:\Python34配置环境变量:【右键计算机】--》【属性】-
    Python 学习之路 - Python
  • chatgpt的中文全称是什么
    chatgpt的中文全称是生成型预训练变换模型。ChatGPT是什么ChatGPT是美国人工智能研究实验室OpenAI开发的一种全新聊天机器人模型,它能够通过学习和理解人类的语言来进行对话,还能根据聊天的上下文进行互动,并协助人类完成一系列
    chatgpt的中文全称是什么
  • C/C++中extern函数使用详解
  • C/C++可变参数的使用
    可变参数的使用方法远远不止以下几种,不过在C,C++中使用可变参数时要小心,在使用printf()等函数时传入的参数个数一定不能比前面的格式化字符串中的’%’符号个数少,否则会产生访问越界,运气不好的话还会导致程序崩溃
    C/C++可变参数的使用
  • css样式文件该放在哪里
  • php中数组下标必须是连续的吗
  • Python 3 教程
    Python 3 教程 Python 的 3.0 版本,常被称为 Python 3000,或简称 Py3k。相对于 Python 的早期版本,这是一个较大的升级。为了不带入过多的累赘,Python 3.0 在设计的时候没有考虑向下兼容。 Python
    Python 3 教程
  • Python pip包管理
    一、前言    在Python中, 安装第三方模块是通过 setuptools 这个工具完成的。 Python有两个封装了 setuptools的包管理工具: easy_install  和  pip , 目前官方推荐使用 pip。    
    Python pip包管理
  • ubuntu如何重新编译内核
  • 改善Java代码之慎用java动态编译

目录