知识图谱:通往精准RAG应用的秘钥

在本文中,我很高兴介绍我结合文本嵌入和知识(图)嵌入的实验以及对 RAG 性能的观察。我将首先使用简单的开放框架独立解释文本和知识嵌入的概念,然后,我们将了解如何在 RAG 应用程序中使用两者。这是一篇相当长的文章,我故意不想写多个部分,所以你们都掌握了流程,并按照我在下面分享的顺序尝试这个

第 1 部分:什么是文本嵌入 (TE)?& 它们在 RAG 实现中如何存储和使用?
第 2 部分:什么是知识(图)嵌入 (KGE) 以及它们如何存储?
第 3 部分:知识(图)嵌入与文本嵌入有何不同,并分析它们在 RAG 的使用上下文中是否互补

结论:在 RAG 中结合文本和知识嵌入的好处以及总体总结

第 1 部分:文本嵌入和 RAG 实现

如果您尝试涉足自然语言处理 (NLP),您可能在探索语言模型和机器学习时遇到过“文本嵌入”一词。那么,文本嵌入到底是什么?它们是如何工作的?请允许我以更容易理解的方式解释它。

文本嵌入是单词或短语的数字表示,可以有效地捕获其含义和上下文。将它们视为单词的唯一标识符——捕获它们所代表的单词含义的简洁向量。这些嵌入使计算机能够增强对文本的理解和处理,使其能够在文本分类、情感分析和机器翻译等各种 NLP 任务中表现出色。

我们利用 Word2Vec、GloVe 或 BERT 等预训练模型来生成文本嵌入。这些模型已经过对大量文本数据的广泛训练,并获得了对有关单词及其关系的语义信息进行编码的能力。

1)Tokenize [决定我们用来生成令牌的算法。[基于单词(我在下面选择的一个)、基于字符、子词等)请参阅我的关于使用图形技术进行高级标记化的文章2)将标记编码为向量(字节对编码等)

现在,让我们探索使用简单的 Python 代码片段 (word2vec) 生成文本嵌入的过程:

# Code Implementation: Generating Text Embeddings`          `import numpy as np`          `from gensim.models import Word2Vec`                   `# Sample sentences`          `sentences = [          ``     ["I", "love", "natural", "language", "processing"],           ``     ["Text", "embeddings", "are", "fascinating"],           ``["NLP", "makes", "computers", "understand", "language"]`          `]`                   `# Train Word2Vec model`          `model = Word2Vec(sentences, vector_size=5, window=5, min_count=1, sg=1)`          `# Get embeddings for words`          `word_embeddings = {}`          `for word in model.wv.index_to_key:`              `word_embeddings[word] = model.wv[word]`                   `# Print embeddings`          `for word, embedding in word_embeddings.items():          ``    print(f"Embedding for '{word}': {embedding}")

在此代码中,我们通过在一组例句上进行训练来开发 Word2Vec 模型。然后该模型为每个单词生成嵌入。这些嵌入捕获了句子中单词之间的语义关系。使用 Word2Vec 生成文本嵌入的代码片段的输出如下:

`Embedding for'I': [-0.01978252  0.02348454 -0.0405227  -0.01806103  0.00496107]`          `Embedding for'love': [ 0.01147135 -0.00716509 -0.02319919  0.03274594 -0.00713439]`          `Embedding for'natural': [ 0.03319094  0.02570618  0.02645341 -0.00284445 -0.01343429]`          `Embedding for'language': [-0.01165106 -0.02851446 -0.01676577 -0.01542572 -0.02357706]`          `Embedding for'processing': [-0.00205235  0.01240269 -0.03660049 -0.0240654  -0.03612582]`          `Embedding for'Text': [ 0.02553515  0.03493764  0.01932768 -0.02028572  0.02185934]`          `Embedding for'embeddings': [ 0.01769094  0.02836292 -0.02720205 -0.01580047 -0.0323391 ]`          `Embedding for'are': [ 0.01449668  0.0178032   0.02154245 -0.02403528 -0.03612522]`          `Embedding for'fascinating': [ 0.0389471   0.00991404  0.0198368  -0.02466156 -0.03446501]`          `Embedding for'NLP': [ 0.00828243 -0.02125732  0.01082581  0.02250805  0.02270168]`          `Embedding for'makes': [ 0.01696491  0.0151721  -0.02186292 -0.01510419 -0.02021307]`          `Embedding for'computers': [ 0.00983663 -0.02679762  0.03002482 -0.02373362 -0.01307139]`          `Embedding for'understand': [-0.0326019   0.01835899  0.01538064 -0.01008516  0.01717436]`    

在上面的输出中:

1)每一行对应一个单词的嵌入向量。
2)每行都以单词开头,后面是表示为数值列表的嵌入向量。3)例如,单词“love”的嵌入:[-0.01978252 0.02348454 -0.0405227 -0.01806103 0.00496107]。

在 RAG 中实现文本嵌入

现在我们已经掌握了生成文本嵌入的过程,让我们探索它们在检索增强生成模型(RAG)中的应用。RAG 结合基于检索和生成的方法,利用文本嵌入来掌握输入查询的上下文,并在 NLP 任务的检索阶段提取相关信息。

步骤 1:标记化和编码。

现在让我们尝试利用预训练模型(例如 BERT)对输入查询进行标记化和编码。这会将查询转换为捕获其语义和上下文的数字表示。

# Code Implementation: Tokenization and Encoding`          `from transformers import BertTokenizer, BertModel`                   `# Initialize BERT tokenizer and model`          `tokenizer = BertTokenizer.from_pretrained('bert-base-uncased')`          `model = BertModel.from_pretrained('bert-base-uncased')`                    `# Tokenize and encode the input query`          `query = "What is the capital of France?"`          `input_ids = tokenizer.encode(query, add_special_tokens=True, return_tensors="pt")

在这里,我们使用 BERT 将输入查询标记化并编码为数字 ID。由于 BERT 模型初始化和标记化过程涉及加载大型预训练模型,标记化和编码步骤的输出包括以下组件:

1)输入 ID:这些是输入查询中标记的数字表示形式。每个 token 都会被转换成一个 ID,该 ID 与其在 BERT 词汇表中的索引相对应。

2)注意掩码:这是一个二进制掩码,指示哪些标记是实际单词 (1),哪些是填充标记 (0)。它确保模型在处理过程中只关注真实的令牌。

3).令牌类型 ID(对于 BERT 等模型):这表明在多个分段的情况下每个令牌属于哪个分段或句子。对于单句输入,所有标记类型 ID 通常设置为 0。

输出是包含这些组件的字典,可以用作 BERT 模型的输入以进行进一步处理。

以下是输出内容的示例:

{          ``     'input_ids': tensor([[  101,  2054,  2003,  1996,  3007,  1997,  2605,  1029,   102,     0]]),           ``     'attention_mask': tensor([[1, 1, 1, 1, 1, 1, 1, 1, 1, 0]]),           ``'token_type_ids': tensor([[0, 0, 0, 0, 0, 0, 0, 0, 0, 0]])`          `}

In this example: 在这个例子中:
input_ids 包含输入查询中标记的数字 ID。 attention_mask 指示哪些标记是实际单词 (1),哪些是填充标记 (0)。token_type_ids 表示每个标记所属的片段或句子(在本例中,第一个句子为 0)。

步骤2:检索和相似性匹配

接下来,我们根据编码的查询从语料库中检索相关段落。我们使用余弦相似度计算查询嵌入和段落嵌入之间的相似度得分。

# Code Implementation: Retrieval and Similarity Matching`          `from sklearn.metrics.pairwise import cosine_similarity`                  `# Retrieve passages and compute similarity scores`          `query_embedding = model(input_ids)[0].mean(dim=1).detach().numpy()`          `passage_embeddings = ...  # Retrieve passage embeddings`          `similarity_scores = cosine_similarity(query_embedding, passage_embeddings)

在这里,我们选择相似度得分最高的段落并将其与相似度得分一起输出。

由于检索和相似性匹配步骤涉及计算查询嵌入和段落嵌入之间的相似性分数,因此我可以提供相似性分数可能是什么样子的示例。假设我们有三个样本段落,我们计算查询嵌入和这些段落的嵌入之间的余弦相似度得分。以下是输出的示例:

similarity_scores = [0.75, 0.82, 0.65]

在这个例子中:

相似度得分 0.75 表示查询与第一段之间有中等相似度。

相似度得分 0.82 表明查询与第二个段落之间具有很高的相似度。

相似度得分 0.65 表示查询与第三段之间的相似度较低。

相似度分数表示每个段落与输入查询之间的相似程度,分数越高表示相似度越强。在 RAG 模型中,获得最高相似度得分的段落被认为与进一步处理最相关。

第 3 步:增强上下文

最后,我们将相似度得分最高的段落指定为最相关的段落。这段文字提供了模型生成阶段的相关信息。

# Select passage with highest similarity score`          `max_similarity_index = np.argmax(similarity_scores)`          `selected_passage = passages[max_similarity_index]`                    `# Output selected passage and similarity score`          `print("Selected Passage:")`          `print(selected_passage)`          `print("Similarity Score:", similarity_scores[0][max_similarity_index])

假设第二个段落具有最高的相似度得分 (0.82),并被选为最相关的段落。以下是输出的示例:

Selected Passage:`          `"Barack Obama served as the 44th president of the United States."`          `Similarity Score:0.82

在这个例子中:

所选段落是“Barack Obama served as the 44th president of the United States.”

相似度得分为 0.82,表明该段落与输入查询之间具有很高的相似度。

文本嵌入是自然语言处理(NLP)领域极其强大的工具,它使计算机能够有效地理解和处理文本信息。它们对广泛的任务产生重大影响,例如回答问题、生成文本和分析情绪。通过在 RAG 等高级模型中利用文本嵌入,我们可以显着提高其性能和精度,从而得到更敏锐、更适合上下文的响应。随着您在 NLP 探索中取得进展,获得文本嵌入方面的专业知识对于释放语言模型的完整功能和推动自然语言处理的进步至关重要。

第 2 部分:知识(图)嵌入和实现

现在让我们转向高级知识图的世界,看看定义和实现知识嵌入,表示从非结构化数据构建结构域。

知识(图)和嵌入简介

知识图是一种非常有效的信息组织方式,以有意义的方式连接实体及其关系。这些图表充当组织良好的信息仓库,捕获现实世界对象的含义及其联系。然而,这个过程并没有随着知识图的发展而结束。探索知识图嵌入领域对于释放其全部潜力至关重要。

让我们使用具有不同实体及其相互关系的简单文本来探索知识图嵌入,或者简称为知识嵌入。从提取数据开始到生成嵌入并捕获紧凑向量中文本的真正含义。获取这些嵌入的目标是促进对各种预测任务(如实体分类、链接预测或推荐系统)的图形元素(实体、关系)的操作。

我们保持了 KG 的基本结构,并简化了 KG 组件的使用。将 KG 元素表示为嵌入后,使用评分函数来评估三元组的合理性,例如“Tim”、“is an”、“Artist”。

以下是实现知识(图)嵌入的步骤:

第 1 步:三重提取和处理

给定非结构化文本,我们首先使用斯坦福大学的 OpenIE 框架提取关键实体、关系和属性。一旦提取出三元组,我们就清理/协调它们。

考虑输入文本 “Hawaii is a state in the United States. Barack Obama served as the 44th president of the United States. The Louvre Museum is located in Paris, France.”

from openie import StanfordOpenIE`                    `text = "Hawaii is a state in the United States. Barack Obama served as the 44th president of the United States. The Louvre Museum is located in Paris, France."`                  `with StanfordOpenIE() as client:          ``     triples = client.annotate(text)           ``     for triple in triples:           ``print(triple)`                        `cleaned_triples = [(subject.lower(), relation.lower(), object.lower()) for (subject, relation, object) in triples]``    print("Cleaned Triples:", cleaned_triples)

上述代码的输出是

('Hawaii', 'is', 'a state in the United States')`          `('Barack Obama', 'servedas', 'the44th president of the United States')`          `('The Louvre Museum', 'is located in', 'Paris, France')`                  `Cleaned Triples: [('hawaii', 'is', 'a state in the united states'), ('barack obama', 'servedas', 'the44th president of the united states'), ('the louvre museum', 'is located in', 'paris, france')]

我们现在准备使用这些信息并创建知识图

第二步:知识图谱构建

使用 NetworkX 框架,我们现在将使用上面清理的三元组并构建一个具有实体 → 节点和关系 → 边的知识图。这是实现:

`import networkx as nx`                   `# Create a directed graph`          `knowledge_graph = nx.DiGraph()`                   `# Add nodes and edges from cleaned triples`          `for (subject, relation, object) in cleaned_triples:          ``knowledge_graph.add_edge(subject, object, relation=relation)`                   `# Visualize the knowledge graph`          `nx.draw(knowledge_graph, with_labels=True)`    

输出是一个图表,如下所示:

第 3 步:实体解析和归因

实体解析在各种 NLP 应用中发挥着关键作用,包括信息提取、问答、知识图谱构建等。通过准确地将文本中的实体提及与结构化知识表示中的相应实体联系起来,实体解析使机器能够更有效地理解和推理自然语言,从而促进广泛的下游任务和应用。

从根本上来说,实体解析解决了自然语言中的歧义性和可变性的挑战。在日常语言使用中,诸如人、地点、组织和概念之类的实体经常使用不同的名称、同义词、缩写或变体来引用。例如,

“Obama,” “the former U.S. president,” or simply “he.” Additionally, entities with similar names or attributes may exist, leading to potential confusion or ambiguity. For instance, “Paris” could refer to the capital city of France or a different location with the same name.

让我们详细介绍一下每种实体解析技术,并根据所使用的文本提供示例:

精确匹配:在文本中,提及“Hawaii”可以直接链接到图中标记为“Hawaii”的节点,因为它们完全匹配。

部分匹配:如果文本提到“USA”而不是“United States”,部分匹配算法可能会识别两者之间的相似性,并将提及链接到图中标记为“United States”的节点。

命名实体识别 (NER):使用 NER,系统可能会将“Barack Obama”识别为文本中提到的人物实体。然后可以将该提及链接到图中标记为“Barack Obama”的相应节点。

共指解析:如果文本提到“他担任总统”,则共指解析可以将“他”链接回文本前面提到的“巴拉克·奥巴马”,然后将其链接到图中标记为“巴拉克·奥巴马”的相应节点。

消歧:假设文本提到“巴黎”,但没有指定它是指法国的城市还是其他地点。消歧技术可能会考虑上下文信息或外部知识源来确定它指的是“法国巴黎”并将其链接到图中的相应节点。

一旦确定了正确的实体链接,文本中的提及就会链接到知识库或知识图中相应的实体。使用精度、召回率和 F1 分数等指标来评估实体解析系统的性能,将预测的实体链接与地面事实或黄金标准进行比较。下面给出了上述构造图的示例实体解析。灰色圆圈表示给定实体的类类型解析。

步骤4:知识(图)嵌入生成

作为最后一步,我们现在将为实体和关系生成嵌入。有多种模型,但在本练习中,我们将使用 TransE。

from pykeen.pipeline import pipeline`          `   ``   `          `# Define pipeline configuration`          `pipeline_config = {          ``     "dataset": "nations",           ``     "model": {           ``         "name": "TransE",           ``         "embedding_dim": 50           ``     },           ``     "training": {           ``         "num_epochs": 100,           ``         "learning_rate": 0.01           ``}`          `}`          `   `          `# Create and run pipeline`          `result = pipeline(          ``     **pipeline_config,           ``     random_seed=1234,           ``use_testing_data=True`          `)`          `   `          `# Access trained embeddings`          `entity_embeddings = result.model.entity_embeddings`          `relation_embeddings = result.model.relation_embeddings`                  `# Print embeddings`          `print("Entity Embeddings:", entity_embeddings)`          `print("Relation Embeddings:", relation_embeddings)`    `这是输出:``Entity Embeddings: [Embedding dimension:(120, 50)]`          `Relation Embeddings: [Embedding dimension:(120, 50)]

知识嵌入的向量如下所示:

实体嵌入:

“Hawaii”: [0.1, -0.2, 0.5, …]``“United States”: [0.3, 0.4, -0.1, …]``“Barack Obama”: [-0.2, 0.6, 0.2, …]``“Louvre Museum”: [-0.5, 0.1, 0.7, …]

Relation Embeddings: 关系嵌入:

`“is a state in”: [0.2, -0.3, 0.1, …]``“served as”: [0.4, 0.2, -0.5, …]``“is located in”: [-0.1, 0.5, 0.3, …]`    

第 3 部分:结合文本和知识(图)嵌入

在我们尝试组合嵌入之前,让我们首先了解这些嵌入带来的价值并验证它们是否完全互补。

文本嵌入和知识嵌入在自然语言处理 (NLP) 中具有不同的用途,并表示语言和语义信息的不同方面。让我们根据提供的输出来比较和对比这两种类型的嵌入:

实现文本和知识嵌入的组合

该代码通过将文本嵌入和知识嵌入组合到单个嵌入空间中来集成它们,然后根据查询和段落的组合嵌入之间的余弦相似度从知识库中检索相关段落。输出显示相关段落及其与查询的相似度分数。

`import numpy as np`          `from sklearn.metrics.pairwise import cosine_similarity`          `   ``   `          `# Sample knowledge embeddings`          `knowledge_embeddings = { } #initialise the above knowledge embeddgings output`          `   ``   `          `# Sample text embeddings`          `text_embeddings = { } #initialise the above text embeddgings output          ``# Consider passages from knowledge base`          `knowledge_base = {          ``     "Passage 1": "Hawaii is a state in the United States.",           ``     "Passage 2": "Barack Obama served as the 44th president of the United States.",           ``# Add more passages as needed`          `}`          `   ``   `          `# Function to combine text and knowledge embeddings`          `def combine_embeddings(text_emb, know_emb):          ``     combined_emb = {}           ``     for entity, t_emb in text_emb.items():           ``         if entity in know_emb:           ``             combined_emb[entity] = np.concatenate([t_emb, know_emb[entity]])           ``         else:           ``combined_emb[entity] = t_emb`              `return combined_emb`          `   `          `# Function to retrieve relevant passages using combined embeddings`          `def retrieve_passages(query_emb, knowledge_base_emb):          ``     similarities = {}           ``     for passage, kb_emb in knowledge_base_emb.items():           ``         sim = cosine_similarity([query_emb], [kb_emb])[0][0]           ``         similarities[passage] = sim           ``sorted_passages = sorted(similarities.items(), key=lambda x: x[1], reverse=True)`              `return sorted_passages`                    `# Example usage`          `combined_embeddings = combine_embeddings(text_embeddings, knowledge_embeddings)`          `query = "query"`          `relevant_passages = retrieve_passages(combined_embeddings[query], knowledge_embeddings)`          `   ``   `          `# Print relevant passages`          `for passage, similarity in relevant_passages:          ``     print("Passage:", passage)           ``     print("Similarity:", similarity)           ``print()`   

具有相似性的输出如下:

Passage: Passage 1`          `Similarity: 0.946943628930774`          `   `          `Passage: Passage 2`          `Similarity: 0.9397945401928656

我们可以实现其他相似性框架,并选择最适合业务环境的框架。

结论

在检索增强生成 (RAG) 模型中同时使用文本嵌入和知识嵌入可以通过多种方式增强模型的性能和功能:

全面理解:文本嵌入捕获单个单词或短语的语义,而知识嵌入捕获实体之间的显式关系。通过两种类型嵌入的集成,RAG 模型可以更全面地掌握输入文本和存储在知识图中的组织信息。

上下文相关性:文本嵌入通过分析输入文本中的单词共现来提供有价值的上下文洞察,而知识嵌入则通过检查知识图中实体之间的关系来提供上下文相关性。通过组合不同类型的嵌入,RAG 模型能够生成与输入文本语义相关且与结构化知识上下文一致的响应。

改进的答案检索:由于知识嵌入在检索组件中的集成,在 RAG 模型中利用结构化知识可以显着改进答案选择。通过利用知识嵌入从知识库中索引和检索相关段落,RAG 模型能够检索不仅更准确而且信息更丰富的响应。

增强答案生成:文本嵌入通过结合广泛的语言特征和语义细微差别来增强 RAG 模型的生成组件。通过在答案生成过程中集成文本嵌入和知识嵌入,RAG 模型能够生成表现出语言流畅性、语义相关性和结构化知识的坚实基础的响应。

对歧义的鲁棒性:通过利用文本嵌入和知识嵌入,RAG 模型增强了对自然语言中歧义和可变性的适应能力。文本嵌入捕获非结构化文本中存在的可变性和歧义性,而知识嵌入提供明确的语义关系以增强和澄清模型的理解。

有效的知识整合:知识嵌入允许RAG模型将知识图或知识库中的结构化知识无缝集成到生成过程中。通过知识嵌入和文本嵌入的集成,RAG 模型实现了结构化知识和非结构化文本的无缝融合,从而产生信息更丰富、上下文相关的响应。

如何学习大模型 AI ?

由于新岗位的生产效率,要优于被取代岗位的生产效率,所以实际上整个社会的生产效率是提升的。

但是具体到个人,只能说是:

“最先掌握AI的人,将会比较晚掌握AI的人有竞争优势”。

这句话,放在计算机、互联网、移动互联网的开局时期,都是一样的道理。

我在一线互联网企业工作十余年里,指导过不少同行后辈。帮助很多人得到了学习和成长。

我意识到有很多经验和知识值得分享给大家,也可以通过我们的能力和经验解答大家在人工智能学习中的很多困惑,所以在工作繁忙的情况下还是坚持各种整理和分享。但苦于知识传播途径有限,很多互联网行业朋友无法获得正确的资料得到学习提升,故此将并将重要的AI大模型资料包括AI大模型入门学习思维导图、精品AI大模型学习书籍手册、视频教程、实战学习等录播视频免费分享出来。

在这里插入图片描述

第一阶段(10天):初阶应用

该阶段让大家对大模型 AI有一个最前沿的认识,对大模型 AI 的理解超过 95% 的人,可以在相关讨论时发表高级、不跟风、又接地气的见解,别人只会和 AI 聊天,而你能调教 AI,并能用代码将大模型和业务衔接。

  • 大模型 AI 能干什么?
  • 大模型是怎样获得「智能」的?
  • 用好 AI 的核心心法
  • 大模型应用业务架构
  • 大模型应用技术架构
  • 代码示例:向 GPT-3.5 灌入新知识
  • 提示工程的意义和核心思想
  • Prompt 典型构成
  • 指令调优方法论
  • 思维链和思维树
  • Prompt 攻击和防范

第二阶段(30天):高阶应用

该阶段我们正式进入大模型 AI 进阶实战学习,学会构造私有知识库,扩展 AI 的能力。快速开发一个完整的基于 agent 对话机器人。掌握功能最强的大模型开发框架,抓住最新的技术进展,适合 Python 和 JavaScript 程序员。

  • 为什么要做 RAG
  • 搭建一个简单的 ChatPDF
  • 检索的基础概念
  • 什么是向量表示(Embeddings)
  • 向量数据库与向量检索
  • 基于向量检索的 RAG
  • 搭建 RAG 系统的扩展知识
  • 混合检索与 RAG-Fusion 简介
  • 向量模型本地部署

第三阶段(30天):模型训练

恭喜你,如果学到这里,你基本可以找到一份大模型 AI相关的工作,自己也能训练 GPT 了!通过微调,训练自己的垂直大模型,能独立训练开源多模态大模型,掌握更多技术方案。

到此为止,大概2个月的时间。你已经成为了一名“AI小子”。那么你还想往下探索吗?

  • 为什么要做 RAG
  • 什么是模型
  • 什么是模型训练
  • 求解器 & 损失函数简介
  • 小实验2:手写一个简单的神经网络并训练它
  • 什么是训练/预训练/微调/轻量化微调
  • Transformer结构简介
  • 轻量化微调
  • 实验数据集的构建

第四阶段(20天):商业闭环

对全球大模型从性能、吞吐量、成本等方面有一定的认知,可以在云端和本地等多种环境下部署大模型,找到适合自己的项目/创业方向,做一名被 AI 武装的产品经理。

  • 硬件选型
  • 带你了解全球大模型
  • 使用国产大模型服务
  • 搭建 OpenAI 代理
  • 热身:基于阿里云 PAI 部署 Stable Diffusion
  • 在本地计算机运行大模型
  • 大模型的私有化部署
  • 基于 vLLM 部署大模型
  • 案例:如何优雅地在阿里云私有部署开源大模型
  • 部署一套开源 LLM 项目
  • 内容安全
  • 互联网信息服务算法备案

学习是一个过程,只要学习就会有挑战。天道酬勤,你越努力,就会成为越优秀的自己。

如果你能在15天内完成所有的任务,那你堪称天才。然而,如果你能完成 60-70% 的内容,你就已经开始具备成为一名大模型 AI 的正确特征了。

这份完整版的大模型 AI 学习资料已经上传CSDN,朋友们如果需要可以微信扫描下方CSDN官方认证二维码免费领取【保证100%免费

在这里插入图片描述

  • 14
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值