| |
|
开发:
C++知识库
Java知识库
JavaScript
Python
PHP知识库
人工智能
区块链
大数据
移动开发
嵌入式
开发工具
数据结构与算法
开发测试
游戏开发
网络协议
系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程 数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁 |
-> 人工智能 -> T5:Exploring the Limits of Transfer Learning with a UnifiedText-to-Text Transformer -> 正文阅读 |
|
[人工智能]T5:Exploring the Limits of Transfer Learning with a UnifiedText-to-Text Transformer |
论文:https://arxiv.org/pdf/1910.10683.pdf 目录 2.2 The Colossal Clean Crawled Corpus ?3.2.2 Comparing different model structures 3.5.3 Combining multi-task learning with fine-tuning 0 Abstract在迁移学习中,模型首先在数据丰富的任务上进行预训练,然后在下游任务上进行微调。 在本文中,我们通过引入一个统一的框架,将所有基于文本的语言问题转换为文本到文本的格式,来探索自然语言处理中迁移学习技术的前景。 我们的系统研究比较了几十个语言理解任务的预训练目标、架构、未标记数据集、迁移方法和其他因素。 1 Introduction工作的基本思想是将每一个文本处理问题都视为一个“文本到文本”的问题,即把文本作为输入,产生新的文本作为输出。这种方法受到了以前NLP任务统一框架的启发,包括将所有文本问题转换为问答(McCann et al.,2018),语言建模(Radford et al.,2019),或跨度提取Keskar et al. (2019b)任务。 文本到文本框架允许我们将相同的模型、目标、训练程序和解码过程直接应用到我们考虑的每一项任务中。我们通过评估各种基于英语的自然语言处理问题的表现来利用这种灵活性,包括问答、文档概括和情感分类,仅举几个例子。通过这种统一的方法,我们可以比较不同迁移学习目标、未标记数据集和其他因素的有效性,同时通过扩展模型和数据集来探索NLP迁移学习的限制。 目标不是提出新的方法,而是对该领域的现状提供一个全面的视角。因此,我们的工作主要包括对现有技术的调查、探索和经验比较。我们还探索了当前方法的局限性,方法是扩大我们系统研究(训练模型高达110亿个参数)的洞察力,以在我们考虑的许多任务中获得最先进的结果。为了进行这种规模的实验,我们引入了“庞大的干净爬行语料库”(C4),这是一个由从网络上搜集的数百千兆字节的干净英语文本组成的数据集。认识到迁移学习的主要效用是在数据稀缺的环境中利用预训练模型的可能性,我们发布了我们的代码、数据集和预训练模型。 2 Setup在展示本文大规模实证研究的结果之前,先回顾一下我们的结果所需的必要背景主题,包括transformer模型架构和评估的下游任务。 还介绍了个问题作为文本到文本任务处理的方法,并描述了“庞大干净的爬行语料库”(C4),这是我们作为未标记文本数据的来源创建的常见的基于爬行的数据集。本文将模型和框架称为“Text-to-Text Transfer Transformer”(T5)。 2.1 Model除了下面提到的细节和在第3.2节中探索的变体之外,本模型并没有明显偏离transformer架构, transformer的主要构件是self-attention,self-attention是attention的一种变体,它通过用序列其余部分的加权平均值替换每个元素来处理序列。最初的 Transformer 由encoder-decoder架构组成,旨在用于序列到序列的任务。 最近,使用由单个 Transformer 层堆栈组成的模型也变得很普遍,这些模型具有不同形式的自注意力,用于生成适用于语言建模或分类和跨度预测任务的架构。在 3.2 节中经验性地探索了这些架构变体。 本文的编码器-解码器 Transformer 实现非常接近其最初提出的形式。 首先,将输入的token序列映射到嵌入序列,然后将其传递给编码器。编码器由一堆“块(blocks)”组成,每个“块”都包含两个子组件:一个自注意力层,然后是一个小型前馈网络。层标准化应用于每个子组件的输入。我们使用简化版本的层归一化,其中仅重新调整激活值,不应用附加偏差。在层归一化之后,残差跳过连接将每个子组件的输入添加到其输出中。 Dropout 应用于前馈网络、skip connection、注意力权重以及整个堆栈的输入和输出。 解码器在结构上与编码器相似,只是它在每个关注编码器输出的自注意力层之后包括一个标准的注意力机制。解码器中的自注意力机制还使用了一种自回归或因果自注意力(causal self-attention)的形式,它只允许模型关注过去的输出。最终解码器块的输出被馈送到具有 softmax 输出的密集层,其权重与输入嵌入矩阵共享。 Transformer 中的所有注意力机制都被分成独立的“头(heads)”,其输出在进一步处理之前被连接起来。 由于自注意是与顺序无关的(即它是一个对集合的操作),因此通常会向Transformer提供一个显式的位置信号。虽然原始Transformer使用正弦位置信号或学习位置嵌入,但最近使用相对位置嵌入变得更加普遍。相对位置嵌入不是对每个位置使用固定的嵌入,而是根据自我注意机制中比较的“键”和“查询”之间的偏移量产生不同的学习嵌入。我们使用一种简化的位置嵌入形式,其中每个“embedding”只是一个标量,添加到用于计算注意力权重的相应的logit中。为了提高效率,我们还在模型中的所有层之间共享位置嵌入参数,尽管在给定层内,每个注意力头使用不同的学习位置嵌入。通常,学习固定数量的嵌入,每个嵌入对应于一系列可能的键查询偏移量。在这项工作中,我们为所有模型使用了32个嵌入,其范围以对数方式增大到偏量 128,超过该偏移量我们将所有相对位置分配给相同的嵌入。请注意,给定层对超过128?个标记的相对位置不敏感,但后续层可以通过组合来自前一层的局部信息来构建对更大偏移量的敏感性。
总而言之,我们的模型大致相当于原始Transformer。除了1、去除层范数偏差,2、将层归一化放在残差路径之外,3、使用不同的位置嵌入方案。
由于这些架构变化与我们在迁移学习的实证调查中考虑的实验因素正交,因此我们将消除它们对未来工作的影响。
2.2 The Colossal Clean Crawled Corpus
以前关于??NLP??迁移学习的大部分工作都利用大型未标记数据集进行无监督学习。在本文中,我们感兴趣的是测量这些未标记数据的质量、特征和大小的影响。为了生成满足我们需求的数据集,我们利用Common??Crawl??作为从网络上抓取的文本来源。
Common??Crawl??是一个公开可用的网络存档,它通过从抓取的??HTML??文件中删除标记和其他非文本内容来提供“网络提取的文本”?。这个过程每月产生大约??20TB??的抓取文本数据。但是大多数生成的文本不是自然语言。相反,它主要包含乱码或样板文本,如菜单、错误消息或重复文本。此外,大量抓取的文本包含不太可能对我们考虑的任何任务有帮助的内容(冒犯性语言、占位符文本、源代码等)。为了解决这些问题,我们使用以下启发式方法来清理Common??Crawl?的网络提取文本:
此外,由于我们的大部分下游任务都集中在英语文本上,我们使用langdetect7以至少0.99的概率过滤掉任何未被分类为英语的页面。我们的启发式方法受到过去使用Common??Crawl作为??NLP??数据源的工作的启发。
为了组装我们的基础数据集,我们从2019年4月下载了网络提取的文本,并应用了上述过滤。这产生的文本集合不仅比用于预训练的大多数数据集(约750?GB)大几个数量级,而且还包含相当干净和自然的英文文本。我们将此数据集称为“Colossal??Clean??Crawled??Corpus”(或简称?C4)并将其作为??TensorFlow??数据集的一部分发布。在第3.4节中考虑了使用该数据集的各种替代版本的影响。
2.3 Downstream Tasks
目标是衡量一般语言学习能力。
因此,我们研究了一系列不同基准的下游性能,包括机器翻译、 问答、抽象摘要和文本分类。具体来说,我们测量了GLUE和SuperGLUE文本分类元基准的性能;CNN/Daily Mail抽象摘要;SQuAD问答;和?WMT?英语到德语、法语和罗马尼亚语的翻译。所有数据均来自TensorFlow?Datasets。
GLUE和?SuperGLUE均包含一组文本分类任务,旨在测试一般语言理解能力。
实验中,模型被输入问题及其上下文,并被要求逐个生成答案。对于??WMT??英语到德语,我们使用与News??Commentary??v13,??Common??Crawl,??Europarl??v7和newstest2013相同的训 练数据作为验证集。对于英语到法语,我们使用2015??年和newstest2014的标准训练 数据作为验证集。对于英语到罗马尼亚语,这是一个标准的低资源机器翻译基准,我们使用?WMT??2016?的训练和验证集。我们只对英语数据进行预训练,因此为 了学习翻译给定模型,需要学习以新语言生成文本。
2.4 Input and Output Format
为了在上述不同的任务集上训练单个模型,将考虑的所有任务转换为“文本到文本”格式,即在任务中为模型提供一些文本作为上下文或条件,然后被要求产生一些输出文本。该框架为预训练和微调提供了一致的训练目标。具体来说,该模型采用最大似然目标,无论任务如何,为了指定模型应该执行的任务,我们在原始输入序列中添加一个特定于任务的(文本)前缀,然后再将其提供给模型。
? 例如:机器翻译:“That is good.” ,英翻德 ????????在输入句子前加上“translate English to German: ”的 prefix ,
我们能够直接将我们考虑的所有任务转换为文本到文本格式
但STS?B除外,它是一个回归任务,其目标是预测1?到?5?之间的相似性分数。我们发现这些分数中的大多数都以?0.2为增量进行注释,因此我们只需将任何分数四舍五入到最接近的增量0.2,并将结果转换为数字的文字字符串表示(例如,浮点值2.57??将映射到字符串“?2.6”)。在测试的时候,如果模型输出一个字符串,对应一个?1?到?5?之间的数字,我们将其转换为浮点值;否则,我们将模型的预测视为不正确这有效地将??STS?B回归问题重铸为21-class classifification problem。
3. Experiments
NLP??迁移学习的最新进展来自各种发展,例如新的预训练目标、模型架构、未标记的数据集等。在本节中,我们对这些技术进行了实证调查,希望能够梳理出它们的贡献和意义。
我们通过采用合理的baseline(在第3.1节中描述)并一次更改设置的一个方面来系统地研究这些贡献;我们对模型架构(第3.2节),无监督的目标(第3.3节)、预训练数据集(第3.4节)、转移方法(第3.5节),以及缩放(第3.6节)进行empirical comparison,在本节的高潮部分,我们将研究中的见解与scale相结合,以在我们考虑的许多任务中获得最先进state-of-the-art的结果(第3.7节)。 3.1 Baseline
使用一个简单的去噪目标来预训练一个标准Transformer,分别对我们的每个下游任务进行微调。我们在以下小节中描述了这个实验设置的细节。
3.1.1 Model
对于我们的模型,我们使用标准编码器?解码器??Transformer。
虽然许多用于??NLP??迁移学习的现代方法使用仅由单个“堆栈”组成的??Transformer??架构,但是我们发现使用标准的编码器?解码器结构在生成和分类任务上都取得了很好的效果。
将在3.2节探讨不同模型架构的性能。
本文选用的baseline模型与Bert_base很接近,编码器和解码器都由??12??个块组成(每个块包括自注意力、可选的编码器?解码器、注意力和前馈网络)。每个块中的前馈网络由一个输出维度为dff??=??3072??的密集层组成,然后是一个ReLU?非线性和另一个密集层。所有注意力机制的“关键”和“价值”矩阵的内部维度为dkv??=??64,所有注意力机制都有??12个头。所有其他子层和嵌入的维数为dmodel??=??768。总的来说,这导致模型具有大约??2.2??亿个参数。
由于baseline模型采用了双层的stacks,而非一个,所以参数量是Bert_base的2倍。
对于正则化,模型各处使用 dropout = 0.1。
3.1.2 Training
所有的任务都被表述为text-to-text,始终使用标准的最大似然进行训练,即 使用teacher forcing和交叉熵损失,优化器:AdaFactor。在测试时,使用贪婪解码(即在每个时间步选择最高概率的logit)
在进行微调之前,我们在C4上对每个模型进行了2的19次方= 524,288步的预训练,最大序列长度512,batch size = 128,并且 pack 每个 batch 使其包含大约?512?128=个token。因此共计训练了??即大约 34B 的token,这比 BERT 的 127B 和 RoBERTa 的 2.2T 要少得多。请注意,??仅覆盖整个 C4 数据集的一小部分,因此我们在预训练期间不会有重复数据。?
预训练时使用 inverse square root 调整学习率:,?n?为当前训练迭代数,?k?为 warm-up 的步骤数(所有实验都设为??),即前??次实验设置了 0.01 的恒定学习率,之后以指数形式衰减学习率。虽然使用三角学习率 triangular learning ratet结果更好,但需要提前知道训练步骤的总数。由于在某些实验中我们将改变训练步骤的数量,因此我们选择了更通用的inverse square root。? 模型在所有任务上微调了??步。选择此值是为了在高资源任务与低资源任务之间进行权衡,前者需要更多的微调步骤,后者很快会过拟合。在微调期间,每批个令牌 不变并使用0.001的恒定学习率,每 5,000 个步骤保存一个检查点,并在模型检查点上报告验证集最佳结果。对于在多个任务上进行了微调的模型,我们分别为每个任务选择最佳检查点。对于第 3.7 节中的实验以外的所有实验,我们在验证集中报告结果,以避免在测试集中执行模型选择。 3.1.3 Vocabulary
我们使用??SentencePiece将文本编码为??WordPiece??标记。对于所有实验,我们使用32,000?个单词的词汇表。由于我们最终对英语到德语、法语 和罗马尼亚语翻译的模型进行了微调,因此我们还要求我们的词汇表涵盖这些非英语语言。
为了解决这个问题,我们将??C4??中使用的??Common??Crawl??scrape??页面分类为德语、法语和罗马尼亚语。然后,我们用?10??部分英语?C4?数据和各1?部分分类为德语、法语或罗马尼亚语的数据训练我们的??SentencePiece??模型。这个词汇表在我们模型的输入和输出中共享。
我们的词汇表使我们的模型只能处理预定的、固定的语言集。
3.1.4 Unsupervised ObjectiveT5的预训练包含无监督和有监督两部分。 无监督部分使用的是Google构建的近800G的语料(论文称之为C4),而训练目标则跟BERT类似,只不过改成了Seq2Seq版本,我们可以将它看成一个高级版的完形填空问题: 输入:明月几时有,[M0]问青天,不知[M1],今夕是何年。我欲[M2]归去,唯恐琼楼玉宇,高处[M3];起舞[M4]清影,何似在人间。 而有监督部分,则是收集了常见的NLP监督任务数据,并也统一转化为SeqSeq任务来训练。比如情感分类可以这样转化: 输入:识别该句子的情感倾向:这趟北京之旅我感觉很不错。 主题分类可以这样转化: 输入:面是一则什么新闻?八个月了,终于又能在赛场上看到女排姑娘们了。 阅读理解可以这样转化: 输入:阅读理解:特朗普与拜登共同竞选下一任美国总统。根据上述信息回答问题:特朗普是哪国人?
利用未标记的数据来预训练我们的模型需要一个objective,objective不需要标签,但是可以使模型得到在下游任务中有用的可概括的知识。
将预训练和微调所有模型参数的迁移学习范式应于NLP问题的初步工作:使用因果语言建模目标进行预训练。然而,最近已经表明“denoising”objective(也称为“掩码语言建模”)可以产生更好的性能。
在“denoising”objective中,模型被训练以预测输入中丢失或损坏的token。受?BERT?的“掩码语言建模”目标和“word??dropout”正则化技术,我们设计了一个随机抽样的目标,然后在输入序列中丢弃??15%??的token。所有连续的丢弃token都被单个token替换。每个哨兵token都分配有一个对序列唯 一的token?ID。哨兵?ID是添加到我们的词汇表中的特殊token,不对应于任何单词。
然后,目标对应于所有丢弃的token span,由输入序列中使用的相同哨兵token加上最终哨兵token分隔,以标记目标序列的结束。我们选择对连续的token span进行掩码,并仅预测丢弃的token,这是为了降低预训练的计算成本。
下图显示了应用此目标所产生的转换示例。我们在经验上将此目标与第3.3节中的许多其他变体
进行了比较。
?
? 3.1.5 Baseline Performance
在本节中,我们使用上述基线任务来展示结果,以了解我们的下游任务套件的预期性能。
理想情况下,我们会多次重复研究中的每个实验,以获得结果的置信区间。
但是由于非常昂贵,所以选择更便宜的替代方案,我们从头开始训练我们的基线模型10?次(即使用不同的随机初始化和数据集洗牌并假设这些基本模型运行的方差也适用于每个实验变体。
?
3.2 Architectures3.2.1 Model structuresAttention masks:
?首先作者们先对预训练模型中的多种模型架构(Transformer)进行了比对,最主要的模型架构可以分成下面三种。
上面这些模型架构都是 Transformer 构成,之所以有这些变换,主要是对其中注意力机制的 Mask 操作。
?3.2.2 Comparing different model structures为了提供合理的比较方法,我们考虑了编码器-解码器模型的多种配置。我们将?BERTBASE?大小的层块中的层数和参数分别称为 L 和 P 。我们将使用 M 来指代L + L层编码器-解码器模型或仅L层的解码器模型处理给定输入目标对所需的FLOP数量。总的来说,我们将进行比较:
3.2.3 Objectives基本语言建模目标以及第 3.1.4 节中描述的降噪目标作为无监督的目标。 对于在进行预测之前先提取前缀的模型(编码器-解码器模型和前缀LM),我们从未标记的数据集中采样了一段文本,并选择一个随机点将其分为前缀和目标部分。 对于标准语言模型,我们训练模型以预测从开始到结束的整个跨度。 我们的无监督降噪目标是为 text-to-text 模型设计的; 为了使其适应语言模型,我们将输入和目标连接起来,如3.2.1节所述。 3.2.4 Results不同模型的对比结果如 Table 2所示:
与语言建模目标相比,使用降噪目标始终可以带来更好的下游任务性能。在以下部分中,我们将对无监督目标进行更详细的探讨。 3.3 Unsupervised objectives本小节探索无监督目标过程中所做的选择,这是其流程图。第一步,对高层次方法(自监督的预训练方法)进行对比,选出性能最好的一个:Bert-style;第二步,对文本一部分进行破坏时的策略,replace span(小段替换)法效果最好;第三步,破坏比例,最后选用破坏15%;第四步,破坏长度选定,Replace Span 需要决定对大概多长的小段进行破坏,于是对不同长度进行探索,最后选择3 的破坏时小段长度。
本文对比的无监督目标函数有:prefix language modeling、masked language modeling (MLM)和deshuffling objective 这三种。 第一个方面,高层次方法(自监督的预训练方法)对比,总共三种方式。
这三种目标函数的示例如 Table 3 中前3行所示:
其中发现 Bert-style 最好,进入下一轮。 第二方面,对文本一部分进行破坏时的策略,也分三种方法。
? 此轮获胜的是?Replace Span 法,类似做法如 SpanBERT 也证明了有效性。 进入下一轮。 第三方面,到底该对文本百分之多少进行破坏呢,挑了 4 个值,10%,15%,25%,50%,最后发现 BERT 的?15%?就很 ok了。
接着进入更细节,第四方面,因为 Replace Span 需要决定对大概多长的小段进行破坏,于是对不同长度进行探索,2,3,5,10 这四个值,对比性能结果,最终模型选用的是长度为3,因为长度短,训练速度越快。
终于获得了完整的 T5 模型,还有它的训练方法。
到此基本上 T5 预训练就大致说完了。 3.4 Pre-training Data set3.4.1 Unlabeled Data Sets在以下数据集上进行预训练后比较基线模型的性能:
从C4里面分出各种类型的数据集,单独训练 T5 模型,之后看在下游任务的表现,发现一些情况领域内的预训练数据可以增强下游任务。而 C4 完整数据集因为数据太多太杂,可能反而不如这种领域内较少数据集。
表 8 显示了每个数据集预训练后获得的结果。明显的收获是,C4中删除启发式过滤会降低性能,并使未过滤的变体在每个任务中表现最差。?
3.4.2 Pre-training Data set Size
本文创建C4的方法旨在能够创建非常大的预训练数据集。对大量数据的访问使我们能够对模型进行预训练,而无需重复样本。
目前尚不清楚在预训练期间重复样本是会对下游性能有所帮助还是有害,因为我们的预训练目标本身就是随机的,并且可以帮助防止模型多次看到相同的数据。
从 C4 中抽出不同量数据做实验,发现数据少时,模型会记住数据所以之后表现会比较差。 最终的下游性能如表9所示,随着数据集大小缩小而下降。我们怀疑这可能是由于该模型开始记住预训练数据集。为了测量这是否成立,我们在图6中绘制了每种数据集大小的训练损失。的确,随着预训练数据集的大小缩小,该模型获得的训练损失明显较小,这表明可能存在记忆。
3.5 Training Strategy针对 MTDNN 给 T5 做了一系列类似训练,在一堆监督和非监督数据上进行预训练。结果发现,只要混合训练比例调得OK,和前面说的非监督预训练性能差不多。 3.5.1 Fine-tuning Methods微调模型的所有参数可能会导致结果欠佳,我们专注于两种替代的微调方法,这些方法仅更新编码器-解码器模型的参数的子集。
?表10?表明,adapter layers 可能是一种在较少参数上进行微调的有前途的技术,只要将维度适当地缩放到任务大小即可。 3.5.2 Multi-task learning所谓多任务学习是同时在多个任务上训练一个模型,其目的是训练一个能同时处理多个任务的模型,即该模型中的绝大数参数在多个任务之间是共享的。本文对此目标稍稍宽松,转而研究对多个任务同时进行训练的方法,以便最终生成对每个单独任务都表现良好的独立参数。所以,本文这里是简化版的多任务学习,并不热衷于多任务之间的参数共享,而是更关注于用同一个时间训练多个任务。例如,我们或许可以在多个任务上训练一个模型,但是应用到具体任务时候,可以针对不同的任务选用不同的checkpoint。这就放宽了多任务学习框架,与当下我们所考虑的 预训练-微调 的方法 相比, 它的基础才显得牢固。还注意到,在本文统一的text-to-text框架中,“多任务学习”简单地对应于将数据集混合在一起。相比之下,此前NLP中的多任务学习大多数都是为每个任务添加特定的分类网络或使用不同的损失函数。 那么多任务中,一个非常重要的问题来了,每个任务需要用多少数据进行训练?本文探索了三种方案:Examples-proportional mixing、Temperature-scaled mixing 和 Equal mixing。 实验发现,多任务训练一般是无法于预训练-微调方法相媲美的。
3.5.3 Combining multi-task learning with fine-tuning进一步研究了如何缩小多任务训练和 预训练-微调 的差距。研究了以下三种方案: 上述几种方案的对比结果见于 Table 12: 从中可以看出,多任务预训练+微调 的方式可以取得于baseline近似的结果。这表明多任务学习之后,再进行微调确实有助于缓解不同混合比例之间的一些权衡。另外,抛弃一个任务(“leave-one-out”)的训练结果仅仅轻微下降,说明模型在多个任务上训练确实可以应用于新的任务上。多任务预训练可能不会导致剧烈的任务干扰。 3.6 Scaling接着又做了当放大模型某方面规模的相关实验,分别是增大模型,增大数据,还有在一定资源限制下的集成。 结论是,当这些因素放大时对性能都有提高,但其中大模型是最必要的。 这里说的规模,涉及 模型的规模、训练的时长规模 和 batch size。 具体结果如 Table 13 所示: 4. Reflflection本文提出的text-to-text 框架将NLP进行统一,并详尽地分析了架构、无监督目标函数、数据集、训练方法和规模等因素的影响。 大模型的难处:我们也知道大模型表现好,终究不是长久之计,distillation、parameter sharing和conditional computation 或许是一条新出路。 更高效地抽取知识:我们需要一个更有效的方法来学到通用的知识,强烈怀疑BERT-style loss的效率。 形式化任务之间的相似性:需要一个衡量pre-training和下游任务相似性的方法。 与语言无关的模型:English-only pre-training没能在翻译任务上达到SOTA的表现,说明单一语言还是有局限性。不受语言限制的模型可能是未来的一个研究方向。 参考T5: Text-to-Text Transfer Transformer 阅读笔记 - 知乎 |
|
|
上一篇文章 下一篇文章 查看所有文章 |
|
开发:
C++知识库
Java知识库
JavaScript
Python
PHP知识库
人工智能
区块链
大数据
移动开发
嵌入式
开发工具
数据结构与算法
开发测试
游戏开发
网络协议
系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程 数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁 |
360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 | -2024/11/25 22:59:39- |
|
网站联系: qq:121756557 email:121756557@qq.com IT数码 |