当前位置: 首页 > news >正文

机器学习第十六章 基于RNN和注意力机制的自然语言处理

前言

我们能建造一台同时掌握书面语言和口头语言的机器吗?这是自然语言处理研究的终极目标,但这太宽泛了,实际上研究者会侧重于更具体的任务,如文本分类、翻译、摘要、问答等。

自然语言任务的一种常见方法是使用循环神经网络。因此,我们将继续探索RNN(在第15章中介绍)​,从字符RNN或char-RNN开始,训练它预测句子中下一个字符。

16.1 使用字符RNN生成莎士比亚文本

16.1.1 构建字符级训练数据集

在自然语言处理任务中,原始文本数据通常需要经过一系列预处理步骤,才能转化为模型可理解的数值形式。对于字符级循环神经网络(char-RNN)而言,这一过程的核心在于将每个字符映射为唯一的整数标识,并进一步构建序列化的训练样本。

具体而言,我们首先需要对文本中的所有字符进行词汇表构建,并为每个字符分配一个整数ID。在实际操作中,TextVectorization 层承担了这一关键职责。它不仅负责将文本字符转换为对应的整数,还会预留特定的ID用于特殊目的:例如,ID 0 通常被指定为填充词元(padding token),用于统一不同长度序列的表示;而 ID 1 则常用于表示未知字符(unknown token),以处理训练集中未曾出现的字符。在构建字符级模型时,如果这些特殊词元并非我们关注的重点,可以根据实际需求进行调整,例如从总字符ID数中减去这些预留ID,以更精确地统计有效字符的数量。

原始文本往往是一个非常长的字符序列,直接用于训练并不高效。为了适应循环神经网络的输入要求,我们需要将这个长序列转化为一系列固定长度的“窗口”数据集。每个窗口代表一个输入序列,而其对应的下一个字符则作为模型的预测目标。这种窗口化的处理方式,使得模型能够从局部上下文学习字符之间的依赖关系,从而预测序列中的下一个元素。

窗口长度的选择是影响模型性能的关键超参数之一。例如,将窗口长度设置为100,意味着模型在预测下一个字符时,会考虑前100个字符的上下文信息。较短的输入序列确实能够加快RNN的训练速度,并降低训练难度。然而,过短的窗口长度会限制模型捕获长距离依赖的能力,使其无法学习到超出该长度的任何模式。反之,过长的窗口长度虽然理论上能提供更丰富的上下文,但也会显著增加训练的复杂性和计算成本,甚至对于 LSTM 和 GRU 这样的高级循环单元,处理极长序列的能力也存在瓶颈。因此,在实践中,需要根据具体任务和计算资源,权衡窗口长度的选择,以在学习效率和模式捕获能力之间找到一个平衡点。

16.1.2 构建与训练字符级RNN模型

在字符级循环神经网络(char-RNN)的构建中,Embedding 层扮演着至关重要的角色。它负责将离散的字符ID映射到连续的、低维的向量空间中,这些向量被称为词嵌入(word embeddings),尽管在此处是字符嵌入。Embedding 层的输入通常是一个二维张量,其形状为 [批量大小, 窗口长度],其中“批量大小”表示一次处理的序列数量,“窗口长度”则对应于每个输入序列的字符数量。经过 Embedding 层处理后,输出将是一个三维张量,形状为 [批量大小, 窗口长度, 嵌入大小],其中“嵌入大小”定义了每个字符向量的维度。这种转换使得模型能够捕捉字符之间的语义和句法关系,为后续的循环层提供更丰富的输入。

模型训练完成后,我们可以利用它来生成新的文本。文本生成的核心在于如何根据模型预测的概率分布选择下一个字符。两种常见的策略是贪婪解码(Greedy Decoding)和随机采样(Random Sampling)。

贪婪解码是一种直接且确定性的方法。它在每一步都选择模型预测概率最高的下一个字符,并将其添加到已生成的文本末尾,然后将扩展后的文本作为新的输入,重复此过程。这种方法虽然简单,但往往会导致生成的文本缺乏多样性,容易陷入局部最优,生成重复或不自然的短语。

相比之下,随机采样则引入了随机性,使得生成的文本更具创造性和多样性。通过 tf.random.categorical() 函数,我们可以根据模型输出的概率分布进行采样,选择下一个字符。这种方法能够避免贪婪解码的局限性,生成更富有趣味和变化的文本。为了进一步提升生成文本的质量和说服力,实践中常采用一些高级采样技术,例如:

  • Top-k 采样:仅从模型预测概率最高的 k 个字符中进行采样,过滤掉低概率的噪声。
  • 核心采样(Nucleus Sampling):从累积概率超过某个阈值的最小字符集合中进行采样,这在保持多样性的同时,也能有效避免生成不合逻辑的字符。
  • 束搜索(Beam Search):这是一种更复杂的解码策略,它在每一步跟踪最有可能的 k 个序列(称为“束”),并在每个解码步骤中尝试将它们扩展一个词,最终保留最有可能的 k 个完整序列。束搜索能够让模型有机会“回溯”并修正先前的错误选择,从而生成语法更流畅、语义更连贯的文本,尤其适用于机器翻译等对准确性要求较高的任务。

值得注意的是,当前模型在学习长距离模式方面存在固有的局限性。如果窗口长度设置为100,模型将难以捕捉超过100个字符的依赖关系。虽然增加窗口长度理论上可以缓解这一问题,但这会显著增加训练的难度和计算资源消耗。即使是 LSTM 和 GRU 这样的高级循环单元,在处理极长序列时也可能面临短期记忆遗忘的问题。为了有效解决这一挑战,一种更为先进的方法是采用有状态 RNN,这将在后续章节中详细探讨。

16.1.4 有状态RNN:长序列记忆的策略

在处理长序列数据时,传统的无状态循环神经网络(RNN)面临着一个核心挑战:它们在每个训练批次结束后会重置其内部状态,这意味着模型无法将从前一个批次中学到的信息传递到下一个批次。这种机制导致模型在处理跨越批次边界的长期依赖关系时表现不佳,尤其是在文本生成、机器翻译等需要捕捉长距离上下文的任务中,其记忆能力受到严重限制。为了克服这一局限性,**有状态RNN(Stateful RNN)**应运而生。

有状态RNN的核心思想在于,它允许模型在处理完一个训练批次后,保留其最终的内部状态(包括隐藏状态和细胞状态,对于LSTM和GRU而言),并将其作为下一个训练批次的初始状态。通过这种方式,信息流得以在连续的批次之间持续传递,使得模型能够有效地学习和利用长期的模式和依赖关系,即使这些模式分布在多个短序列批次中。尽管反向传播仍然只在每个短序列内部进行,但前向传播的记忆链条得以延续,极大地增强了模型对长距离上下文的感知能力。

然而,构建和训练有状态RNN需要遵循一些特定的约束条件。首先,为了确保状态的正确传递和上下文的连续性,批次中的每个输入序列必须严格地从其前一个批次序列结束的位置开始。这意味着在数据准备阶段,需要将原始的长序列数据切分成连续的、非重叠的子序列,并按照原始顺序组织成训练批次。例如,如果原始文本是“A B C D E F G H”,并且批次大小为2,窗口长度为4,那么第一个批次可能是“A B C D”,第二个批次则必须从“E F G H”开始,而不是随机抽取。

其次,有状态RNN在训练完成后进行预测时,也存在一定的限制。为了保持状态的连续性,预测时使用的批次大小必须与训练期间的批次大小保持一致。这在某些场景下可能会带来不便,例如需要对单个序列进行预测时。为了规避这一限制,一种常见的实践是:在训练完成后,创建一个与有状态模型结构相同但设置为无状态的新模型,然后将有状态模型的训练权重复制到这个无状态模型中。这样,新的无状态模型就继承了有状态模型学习到的长期模式,同时又摆脱了批次大小的限制,可以灵活地进行预测。

总而言之,有状态RNN通过在批次之间维护内部状态,为处理长序列数据提供了一种有效的记忆机制。它使得模型能够捕捉到无状态RNN难以企及的长期依赖,从而在需要深刻理解上下文的任务中展现出更强大的性能。但在实际应用中,开发者需要仔细处理数据准备和模型部署的细节,以充分发挥其优势。

16.2 情感分析:理解文本情绪的挑战与策略

情感分析,作为自然语言处理(NLP)领域的一个重要分支,旨在识别和提取文本中所蕴含的情感倾向、观点和态度。它在商业评论分析、社交媒体监控、舆情分析等多个场景中具有广泛的应用价值。然而,要准确地捕捉人类语言中复杂的情感 nuances,并非易事。其中,文本的预处理和模型对变长序列的处理能力,是情感分析任务中不可忽视的关键环节。

在文本预处理阶段,**字节对编码(Byte Pair Encoding, BPE)**是一种被广泛采用的子词分割技术。BPE 的核心思想是将整个训练语料库拆分为单个字符(包括空格),然后通过迭代合并最常见的相邻字符对,逐步构建一个预设大小的词汇表。这种方法能够有效地平衡字符级和词级表示的优势,既能处理未登录词(Out-Of-Vocabulary, OOV)问题,又能捕捉到词语内部的结构信息。例如,对于“unhappily”这样的词,BPE 可能会将其拆分为“un”、“happ”、“ily”,从而在词汇表中包含更小的、更通用的语义单元。

随着技术的发展,研究人员对子词分割方法进行了持续改进,例如通过引入更灵活的分割策略,使得在分词之前可以去除对于特定语言的预处理需求,从而简化了整个处理流程。此外,一些研究还提出了新颖的正则化技术,如子词正则化(Subword Regularization),它通过在训练期间在分词过程中引入一定的随机性,进一步提高了模型的精度和稳健性。这些技术共同提升了模型在处理多样化文本数据时的泛化能力。

然而,即使采用了先进的子词分割技术,情感分析模型在处理变长文本序列时仍然面临挑战。当 TextVectorization 层将不同长度的文本评论转换为词元ID序列时,为了使所有序列长度一致以适应批处理的要求,通常会使用填充词元(Padding Token,通常ID为0)来填充较短的序列。这意味着,许多序列会以大量的填充词元结尾,有时甚至多达几十或几百个。尽管我们可能使用了如 GRU(Gated Recurrent Unit)这样比传统 SimpleRNN 层更优秀的循环单元,但其短期记忆能力仍然有限。当模型不得不处理大量连续的填充词元时,它很可能会“遗忘”掉序列早期包含的真正情感信息,导致模型性能下降,精度停留在接近随机猜测的水平(例如50%左右)。

为了有效解决这一问题,实践中通常有两种策略:

  1. 批次内序列长度统一:将长度相近的句子组织成批次,输入模型进行训练。这种方法可以减少填充词元的数量,从而减轻模型记忆负担,并能显著加快训练速度。
  2. 引入掩码机制:更根本的解决方案是让 RNN 模型在处理过程中“忽略”填充词元。这可以通过**掩码(Masking)**机制来实现。掩码允许模型区分真实数据和填充数据,确保模型只关注有意义的输入,从而避免填充词元对模型学习过程的干扰。掩码机制的引入,使得模型能够更准确地捕捉文本的语义信息,尤其是在处理变长序列时,能够显著提升情感分析的性能。

16.2.1 掩码机制:处理变长序列的关键

在自然语言处理任务中,文本序列的长度往往是可变的。为了能够将这些变长序列输入到神经网络模型中进行批处理,通常需要对较短的序列进行填充(padding),使其与批次中最长的序列长度保持一致。然而,这些填充词元(padding tokens)本身并不包含有意义的信息,如果模型在处理过程中不加区分地对待它们,将会引入噪声,甚至误导模型的学习。掩码(Masking)机制正是为了解决这一问题而设计的,它允许模型在计算过程中“忽略”掉填充部分,只关注真实有效的数据。

掩码机制通常从 Embedding 层开始发挥作用。当 Embedding 层接收到输入序列时,它会根据预设的填充词元ID(通常为0),自动创建一个掩码张量。这个掩码张量是一个布尔类型的张量,其形状与输入序列相同,其中对应于填充词元的位置为 False,而对应于真实词元的位置为 True。一旦 Embedding 层生成了掩码张量,它就会自动将其传播到模型的后续层。如果后续层的 call() 方法支持 mask 参数,那么该层将自动接收并利用这个掩码张量,从而在内部计算中忽略掉被标记为 False 的时间步。

然而,并非所有的神经网络层都能够直接支持掩码机制。例如,卷积层(包括 Conv1D)通常不支持掩码。这是因为卷积操作的本质是局部特征提取,它通过固定大小的卷积核在输入序列上滑动,对局部区域进行加权求和。如果引入掩码,卷积核在滑动过程中遇到被掩盖的区域时,其计算逻辑会变得复杂且不直观,目前业界尚未形成统一且高效的掩码处理方案。

对于循环神经网络(RNN)中的 LSTMGRU 层,情况则更为复杂。在 GPU 上,这些层通常会利用基于 Nvidia cuDNN 库的优化实现,以获得更高的计算效率。然而,cuDNN 优化的 LSTMGRU 层对掩码的支持存在一定的限制:它们仅在所有填充词元都位于序列末尾时才支持掩码。此外,为了启用 cuDNN 优化,还需要对几个超参数使用默认值,例如 activationrecurrent_activationrecurrent_dropoutunrolluse_biasreset_after。如果这些条件不满足,或者填充词元并非全部位于序列末尾,那么这些层将不得不回退到(速度更慢的)默认 GPU 实现,这可能会导致性能下降。

除了上述方法,另一种处理变长序列的策略是使用**不规则张量(Ragged Tensors)**作为模型的输入。不规则张量能够有效地表示具有不同长度的序列集合,而无需进行显式的填充操作。在实践中,只需在创建 TextVectorization 层时将 ragged 参数设置为 True,即可使输入序列以不规则张量的形式表示。这种方法从根本上避免了填充词元带来的问题,使得模型能够更自然地处理变长序列。

最后,值得注意的是,传统的词嵌入方法(如 Word2Vec、GloVe)存在一个固有的局限性:一个词无论其上下文如何,都只有一个固定的表示。例如,单词“right”在“left and right”(左右)和“right and wrong”(对错)这两个短语中,即使代表着完全不同的含义,也会被编码为相同的向量。这种“一词一义”的表示方式,限制了模型对词义多义性的理解能力。为了克服这一挑战,**重用预训练语言模型(Pre-trained Language Models)**已经成为当前自然语言处理领域的常态。这些模型(如 BERT、GPT 系列)通过在海量文本数据上进行预训练,学习到了丰富的上下文信息,能够为同一个词在不同语境下生成不同的、具有语境感知能力的词嵌入,从而极大地提升了模型对语言复杂性的理解能力。

16.3 用于神经机器翻译的编码器—解码器网络:序列到序列学习的核心范式

神经机器翻译(Neural Machine Translation, NMT)是自然语言处理领域的一个重要分支,旨在通过深度学习模型实现不同语言间的自动翻译。传统的基于短语的统计机器翻译系统在处理长距离依赖和语义理解方面存在局限性。编码器—解码器(Encoder-Decoder)网络,特别是结合了循环神经网络(RNN)的序列到序列(Sequence-to-Sequence, Seq2Seq)模型,为NMT带来了革命性的突破。

16.3.1 架构与工作原理

编码器—解码器网络的核心思想是将源语言序列(如英语句子)编码成一个固定长度的上下文向量(context vector),然后由解码器将这个上下文向量解码成目标语言序列(如西班牙语翻译)。

编码器(Encoder)
编码器通常是一个RNN(如LSTM或GRU),它逐个读取源语言序列的词元,并逐步更新其内部状态。在处理完整个源序列后,编码器的最终隐藏状态(或其某种聚合形式)被视为包含了源序列所有信息的上下文向量。这个向量是源序列的语义表示,理论上捕获了源语言句子的所有相关信息。

解码器(Decoder)
解码器也是一个RNN,它以编码器生成的上下文向量作为初始状态,并以特殊的“序列起始”(Start-of-Sequence, SOS)词元作为第一个输入。解码器在每个时间步生成一个目标语言词元,并将其作为下一个时间步的输入(在推理阶段)。这个过程持续进行,直到解码器生成特殊的“序列结束”(End-of-Sequence, EOS)词元,表示翻译完成。

教师强制(Teacher Forcing)
在训练阶段,为了加速模型的收敛并提高性能,通常会采用“教师强制”技术。这意味着在解码器的每个时间步,我们不是将解码器在上一个时间步的预测输出作为当前时间步的输入,而是直接将目标序列中正确的上一个词元作为输入。这种机制能够有效地引导解码器学习正确的序列生成模式,避免了模型在训练初期因自身错误累积而导致的性能下降。

16.3.2 优化策略与实践考量

在实际应用中,为了提升NMT模型的性能和效率,需要考虑多种优化策略:

共享嵌入层
当源语言和目标语言共享大量词汇或具有相似的词法结构时,对编码器和解码器使用相同的词嵌入层可以有效减少模型参数,并可能提升翻译质量。这种共享机制有助于模型学习更通用的语言表示。

输出层优化
对于大规模词汇表(例如,目标语言词汇量达到数万甚至数十万),直接计算每个词元的softmax概率会带来巨大的计算开销。为了解决这个问题,可以采用以下优化方法:

  • 采样Softmax(Sampled Softmax):这是一种近似计算softmax的方法,它只计算正确词元和随机抽样的一小部分负样本词元的logit,从而显著降低计算复杂度。
  • 权重绑定(Weight Tying):将解码器输出层的权重矩阵与词嵌入矩阵的转置进行绑定。这种方法基于一个假设:词嵌入层将词元映射到语义空间,而输出层则将语义空间映射回词元概率,两者在某种程度上是互逆操作。权重绑定可以大幅减少模型参数,加速训练,并在数据量有限时提高模型精度。

推理阶段的挑战
在模型训练完成后进行推理时,解码器不再有“教师”提供正确的上一个词元。此时,解码器必须依赖其自身在上一个时间步的预测输出作为当前时间步的输入。这可能导致错误累积,即一个小的预测错误可能在后续步骤中被放大,从而影响最终的翻译质量。为了缓解这个问题,通常会结合束搜索(Beam Search)等解码策略来寻找最优的翻译序列。

16.3.1 双向RNN:捕捉序列的完整上下文

在处理序列数据时,传统的单向循环神经网络(RNN)在每个时间步生成输出时,只能利用当前及之前时间步的信息。这意味着它只能“看到”序列的过去,而无法预知未来。这种“因果关系”的限制,在许多需要理解完整上下文的任务中,如机器翻译、情感分析、命名实体识别等,会成为一个显著的瓶颈。

原理与优势
双向循环神经网络(Bidirectional RNN, BiRNN)旨在克服单向RNN的这一局限性。其核心思想是在同一序列上运行两个独立的循环层:

  1. 前向层(Forward Layer):从序列的起始(例如,句子的第一个词)向结束(最后一个词)方向处理输入,捕捉正向的依赖关系。
  2. 后向层(Backward Layer):从序列的结束向起始方向处理输入,捕捉反向的依赖关系。

在每个时间步,BiRNN会将这两个方向的隐藏状态(通常是拼接起来)作为该时间步的最终表示。通过这种方式,BiRNN能够整合序列中过去和未来的信息,从而为每个时间步的输出提供更丰富、更全面的上下文理解。例如,在理解“right”一词的含义时,如果它出现在“the right arm”中,前向层会处理“the”和“right”,后向层会处理“arm”和“right”,两者结合就能更准确地判断“right”在此处表示方向或身体部位,而非“正确”。

应用场景
BiRNN特别适用于那些需要全局上下文信息才能做出准确判断的任务。例如:

  • 命名实体识别(NER):识别文本中的人名、地名、组织名等,往往需要结合词语前后的信息。
  • 情感分析:判断文本的情感倾向,有时需要通读整个句子才能得出结论。
  • 机器翻译(编码器部分):编码源语言句子时,理解每个词的完整语境有助于生成更准确的上下文向量。

局限性与注意事项

16.3.2 束搜索:优化序列生成质量的策略

在序列生成任务中,尤其是机器翻译这类需要生成连贯且语法正确的长序列的场景,解码器在每一步的词元选择至关重要。贪婪搜索(Greedy Search)是最直观的策略,即在每一步都选择当前概率最高的词元。然而,这种局部最优的选择往往无法保证全局最优的序列,一旦早期选择出现偏差,后续的生成就可能偏离正确路径,导致生成质量下降。

为了克服贪婪搜索的局限性,束搜索(Beam Search)应运而生。束搜索是一种启发式搜索算法,它在每一步不再仅仅关注当前最优的单个词元,而是维护一个包含k个最有可能的候选序列的“束”(beam)。这里的k被称为束宽度(Beam Width)

工作原理:

  1. 初始化:解码过程从一个空的序列开始,将其作为唯一的候选序列放入束中。
  2. 迭代扩展:在每个解码步骤中,对于束中的每一个候选序列,模型会尝试将其扩展一个词元。这意味着,如果束中当前有k个序列,并且词汇表大小为V,那么理论上会生成k * V个新的候选序列。
  3. 概率评估与剪枝:对于所有新生成的候选序列,模型会计算它们的累积对数概率(通常是累积对数概率,因为对数概率相加可以避免浮点数下溢,且最大化对数概率等价于最大化概率乘积)。然后,从这些扩展后的序列中,只保留累积概率最高的k个序列,作为下一时间步的束。
  4. 终止条件:这个过程持续进行,直到所有候选序列都生成了终止符(如<EOS>),或者达到预设的最大序列长度。最终,从束中选择累积概率最高的序列作为最终的输出。

优势与实践考量:

  • 提升生成质量:束搜索通过考虑多个候选路径,显著增加了找到高质量序列的可能性,尤其是在机器翻译、文本摘要等任务中表现优异。它允许模型“回溯”并修正早期可能存在的次优选择,从而生成更流畅、更准确的输出。
  • 计算效率与束宽度:相较于穷举搜索(理论上会遍历所有可能的序列组合,计算复杂度极高),束搜索通过剪枝大大降低了计算成本。然而,束宽度k的选择是一个权衡:
    • k值越大,搜索空间越大,找到最优序列的可能性越高,但计算成本和内存消耗也随之增加。
    • k值越小,计算效率越高,但可能错过全局最优解,生成质量下降。
      在实际应用中,k通常取值在3到10之间,具体取决于任务的复杂度和可用的计算资源。
  • 长度偏置问题:束搜索倾向于生成较短的序列,因为较短序列的累积对数概率(负值)通常“更高”(更接近0)。为了缓解这一问题,实践中常采用长度归一化(Length Normalization)技术,即在选择最终序列时,将累积对数概率除以序列长度的某个函数(例如,序列长度的平方根),以消除长度带来的偏置。

应用场景

束搜索是现代序列生成模型(如基于Transformer的NMT模型)中不可或缺的一部分,广泛应用于:

  • 神经机器翻译(NMT):生成高质量的译文。
  • 文本摘要:生成简洁准确的摘要。
  • 语音识别:将声学特征转换为文本序列。
  • 图像描述生成:根据图像生成自然语言描述。

通过束搜索,模型能够在保证一定计算效率的前提下,显著提升生成序列的整体质量和流畅性,是连接模型输出概率与最终高质量文本的关键桥梁。

http://www.dtcms.com/a/411160.html

相关文章:

  • 上海建设摩托车官方网站中国宣布入境最新消息2023
  • 商城系统网站建设开发赤水市建设局官方网站
  • 韶关微网站建设阜阳企业做网站
  • 加查网站建设seo网络优化是什么工作
  • 海丰县建设局官方网站自己网站
  • 区块链可投会议CCF B--SIGMETRICS 2026 截止10.14 附录用率
  • 自由贸易试验区网站建设方案网站建设工程师的职位要求
  • 【Coze】【视频】卡通风格历史故事工作流
  • 用dw 网站开发与设计报告苏州园科生态建设集团网站
  • 网站开发运营工作总结网站编程培训
  • opendds初入门之对其支持的tools进行梳理
  • 高通平台WiFi学习-- 详解WLAN进行问题调试时所需的日志及其配置方法
  • 静默期的跃迁:2025 年 AI 技术落地与产业重构路径
  • K8s学习----RBAC 基于角色的访问控制
  • 信阳建设企业网站免费行情网站大全下载
  • 阿里巴巴做实商网站的条件运城小程序开发公司
  • Python爬虫实战:获取豆瓣读书网读者评论信息与数据分析
  • 大连开发区论坛网展示型网站可以优化吗
  • Go语言net/http库使用详解
  • 02-Media-11-video_player.py 对H.264或H.265格式视频播放器的示例程序
  • 服装设计网站免费做好我局门户网站建设工作
  • 数组模拟加法——力扣66.加一
  • 做wish选品网站 数据网站一键生成logo的网站
  • CF Median Splits (中位数映射+前缀和)
  • LeetCode算法日记 - Day 53: 验证二叉搜索树、二叉搜索树的第K小元素
  • 前端Mock工具有哪些?常用前端Mock工具推荐、前端接口模拟工具对比与实战经验
  • 招聘网站排名网站建设家居
  • 【自然语言处理与大模型】RAG发展过程中的三个范式
  • 华为纯血鸿蒙系统怎么安装物联通
  • 基于 PyTorch 的 CIFAR-10 图像分类实践