人工智能学习

集成向量数据库与LLM:实战指南

本文主要是介绍集成向量数据库与LLM:实战指南,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!
了解如何使用向量数据库来增强大型语言模型,以实现精确、上下文感知的人工智能解决方案。学习使用 Qwak 构建更智能的机器人。

由 Qwak 的解决方案架构师 Grig Duta 撰写

欢迎来到我们的实战指南,在这里我们将深入探讨大型语言模型(LLMs)及其与向量数据库的协同作用。LLMs 在科技界已成为游戏规则的改变者,推动了应用程序开发的创新。然而,当它们单独使用时,其全部潜力往往未能充分发挥。这时,向量数据库登场了,它们增强了 LLMs 的能力,使其不仅能生成任何响应,而且能生成正确的响应。

通常,大型语言模型(LLMs)是在广泛的数据集上进行训练的,这使它们具有广泛的知识,但也可能导致特定知识领域的空白。有时,它们甚至会输出偏离主题或有偏见的信息——这是从浩瀚且未经过滤的网络中学习的副产品。为了解决这个问题,我们引入了向量数据库的概念。这些数据库以一种称为“向量嵌入”的独特格式存储数据,使LLMs能够更准确地理解和利用信息。

本指南介绍如何使用向量数据库构建大型语言模型(LLM),并改进LLM在此流程中的使用。我们将探讨如何结合这两种技术使LLM更加准确和有用,特别是在特定主题上。

接下来,我们简要概述向量数据库,解释向量嵌入的概念及其在增强AI和机器学习应用中的作用。我们将向您展示这些数据库与传统数据库有何不同,以及为什么它们更适合处理诸如文本、图像和复杂模式等非结构化数据的AI驱动任务。

进一步,我们将探讨这项技术在构建一个Closed-QA机器人的实际应用。这个机器人由Falcon-7B和ChromaDB驱动,展示了当大型语言模型(LLMs)与合适的工具和技术结合时的有效性。

到本指南结束时,你将更清楚地了解如何利用大型语言模型(LLMs)和向量数据库的力量来创建不仅创新而且具有上下文感知和可靠性的应用程序。无论你是AI爱好者还是资深开发者,本指南都旨在帮助你轻松自信地掌握这一激动人心的领域。

向量数据库简述

在深入了解什么是向量数据库之前,理解向量嵌入的概念至关重要。向量嵌入在机器学习中非常重要,用于将原始数据转换为AI系统可以理解的数值格式。这包括将数据(如文本或图像)转换为高维空间中的数字序列,称为向量。高维数据指的是具有许多属性或特征的数据,每个特征代表一个不同的维度。这些维度有助于捕捉数据的细微特征。

创建向量嵌入的过程始于输入数据,这些数据可以是句子中的单词或图像中的像素。大型语言模型和其他AI算法分析这些数据并识别其关键特征。例如,在文本数据中,这可能涉及理解单词的意义及其在句子中的上下文。然后,嵌入模型将这些特征转换为数值形式,为每条数据创建一个向量。向量中的每个数字代表数据的一个特定特征,而这些数字共同封装了原始输入的精髓,使其能够被机器处理。

这些向量是高维的,因为它们包含了许多数字,每个数字对应数据的不同特征。这种高维度使得向量能够捕捉到复杂且详细的信息,从而成为AI模型的强大工具。模型使用这些嵌入来识别数据中的模式、关系和潜在结构。

向量数据库设计用于提供优化的存储和查询能力,以适应向量嵌入的独特性质。它们在提供高效搜索能力、高性能、可扩展性和通过比较和识别数据点之间的相似性来进行数据检索方面表现出色。

这些复杂的高维信息的数值表示使向量数据库区别于主要存储文本和数字等格式数据的传统系统。它们的主要优势在于管理和查询诸如图像、视频和文本等数据类型,特别是在这些数据被转换为向量格式用于机器学习和人工智能应用时尤为有用。

在接下来的说明中,我们展示了将文本转换为word向量的过程。这一步在自然语言处理中至关重要,使我们能够量化和分析语言关系。例如,“小狗”的向量表示在向量空间中会比“房子”更接近“狗”,这反映了它们的语义接近度。这种向量表示方法同样适用于类比关系。例如,“男人”和“女人”之间的向量距离和方向可以类似于“国王”和“王后”之间的距离和方向。这说明了词向量不仅表示单词,还允许在多维向量空间中对它们的语义关系进行有意义的比较。

来源

向量数据库在大型语言模型兴起之前

向量数据库,设计用于处理向量嵌入,在机器学习和AI领域有几个关键应用场景:

相似性搜索: 这是向量数据库的核心功能之一。它们可以在高维空间中快速找到与给定查询相似的数据点。这对于图像或音频检索等应用非常重要,因为在这些应用中,你需要找到与特定输入相似的项目。以下是一些行业用例示例:

  • 电子商务 : 通过允许客户根据参考图片搜索视觉上相似的产品来增强产品发现。
  • 音乐流媒体服务 : 根据用户的喜爱曲目找到并推荐具有相似音频特征的歌曲。
  • 医疗影像 : 通过检索显示类似病征的医学影像(如X光或MRI)来辅助放射科医生进行比较分析。

推荐系统:向量数据库通过处理用户和项目的嵌入来支持推荐系统。它们可以将用户与最符合其兴趣或过去互动的项目(如产品、电影或文章)进行匹配。以下是行业中的几个用例:

  • 流媒体平台 : 通过推荐与观众观看历史相匹配的电影和电视节目来个性化观看体验。
  • 在线零售商 : 根据购物者的浏览和购买历史推荐产品,增强交叉销售和追加销售的机会。
  • 新闻聚合器 : 通过匹配文章与读者过去的参与模式和偏好来提供个性化的新闻推送。

基于内容的检索: 在这里,使用向量数据库根据内容的实际内容进行搜索,而不是传统的元数据。这在处理像文本和图像这样的非结构化数据时特别相关,因为需要分析内容本身来进行检索。以下是几个行业用例:

  • 数字资产管理 : 帮助公司管理庞大的数字媒体库,通过基于视觉或音频内容特征的搜索和检索图像或视频。
  • 法律和合规 : 在大量文件中搜索特定信息或与法律案件或合规查询相关的文档。
  • 学术研究 : 帮助研究人员找到与其工作内容相似的学术文章和研究论文,即使没有提到特定关键词。

这一点关于基于内容的检索越来越重要,并且促进了新型应用:

增强LLM的上下文理解能力:通过存储和处理文本嵌入,向量数据库使LLM能够执行更细致和上下文感知的信息检索。它们有助于理解大量文本的语义内容,在回答复杂查询、保持对话上下文或生成相关内容等任务中至关重要。这一应用正迅速成为向量数据库的重要用例,展示了它们增强高级AI系统如LLM的能力。

向量数据库 vs. 传统数据库

传统的关系型数据库在管理结构化数据方面表现出色,依赖精确匹配和明确的条件逻辑。它们维护数据完整性,适合需要精确、结构化数据处理的应用程序。然而,它们严格的模式设计使得在处理像大型语言模型(LLMs)和生成式AI这样的AI应用中所需的非结构化数据的语义和上下文细微差别时,适应性较差。

另一方面,NoSQL 数据库相比传统的 SQL 系统提供了更多的灵活性。它们可以处理半结构化和非结构化数据,如 JSON 文档,这使得它们在 AI 和机器学习应用场景中更具适应性。尽管如此,即使是 NoSQL 数据库在处理 LLMs 和生成式 AI 所必需的复杂、高维向量数据方面也可能存在不足,这些数据通常涉及解释上下文、模式和语义内容,而不仅仅是简单的数据检索。

向量数据库填补了这一空白。它们专为AI为中心的场景设计,将数据处理为向量,能够有效地管理非结构化数据的复杂性。在与大型语言模型(LLMs)一起工作时,向量数据库支持诸如相似性搜索和上下文理解等操作,提供了超越传统SQL和灵活NoSQL数据库的能力。它们在近似值处理和模式识别方面的熟练程度,使其特别适合需要精细数据解释而非精确数据匹配的AI应用。

提高向量数据库性能

优化向量数据库的性能对于依赖快速和准确检索高维数据的应用程序来说非常重要。这包括提高查询速度、确保高准确性以及保持可扩展性以高效处理不断增长的数据量和用户请求。优化的一个重要部分围绕着索引策略,这些策略用于更高效地组织和搜索向量数据。以下我们将进一步探讨这些索引策略以及它们如何有助于提高向量数据库的性能。

索引策略

向量数据库中的索引策略旨在促进快速准确地检索与查询向量相似的向量。这些策略可以显著影响搜索操作的速度和准确性。

  • 量化:量化是指将向量映射到向量空间中的有限参考点集,从而压缩向量数据。通过将搜索限制在参考点子集而不是整个数据集上,这种方法可以减少存储需求并加快搜索过程。量化有多种形式,包括标量量化和向量量化,每种量化方法在搜索速度和准确性之间都有不同的权衡。

量化技术特别适合于管理大规模数据集的应用程序,其中存储和内存效率至关重要。它在需要在查询速度和准确性之间取得平衡的环境中表现出色,因此非常适合可以容忍一定程度精度损失的速度敏感型应用。然而,对于需要最高精度和最小信息损失的应用场景(如精确的科学研究),由于数据压缩和搜索精度之间的固有取舍,不太推荐使用量化技术。

  • 分层可导航小世界(HNSW) 图:HNSW 是一种索引策略,它构建了一个分层图,每一层代表数据集的不同粒度。搜索从顶层开始,该层包含较少且距离较远的点,然后向下移动到更详细的层次。这种策略允许快速遍历数据集,通过快速缩小相似向量的候选集,显著减少了搜索时间。

HNSW 图在查询速度和准确性之间取得了很好的平衡,非常适合需要即时响应时间的实时搜索应用和推荐系统。它们在中等至大型数据集上表现良好,提供了可扩展的搜索能力。然而,对于极大数据集,它们的内存消耗可能会成为一个限制,因此在内存资源受限或数据集大小远超实际内存容量的场景中,它们可能不太理想。

  • 倒排文件索引 (IVF):IVF 方法使用 k-means 等算法将向量空间划分为预定义数量的簇。每个向量被分配到最近的簇,在搜索时,仅考虑最相关的簇中的向量。这种方法减少了搜索范围,提高了查询速度。结合其他技术,如量化(结果为 IVFADC — 带有不对称距离计算的倒排文件索引),可以进一步通过减少距离计算的计算成本来提高性能。

倒排文件索引(IVF)方法推荐用于处理可扩展搜索环境中高维数据,通过聚类相似项高效地缩小搜索范围。这种方法特别适用于相对静态的数据集,在这种情况下,偶尔重新聚类的开销是可以管理的。然而,对于低维数据,IVF可能不是最佳选择,因为可能会出现过度分割的情况,或者对于需要最低可能延迟的应用程序来说,聚类过程和跨多个聚类进行搜索的需求可能会引入额外的查询时间。

优化的额外考虑因素
  • 维度缩减:在应用索引策略之前,减少向量的维度是有益的。技术如PCA或自编码器可以在减少数据复杂性的同时保留其核心特征,从而提高索引效率和搜索操作的速度。
  • 并行处理:许多索引策略可以并行化,既可以使用多核CPU,也可以使用GPU。这种并行处理能力允许同时处理多个查询,显著提高吞吐量并减少大规模应用的响应时间。
  • 动态索引:对于频繁更新数据的数据库,允许高效插入和删除向量而不需对索引进行重大重组的动态索引策略至关重要。这可以确保数据库保持响应性和及时更新,同时将性能下降降至最低。

通过这些索引策略和考虑因素来提升向量数据库的性能,需要深入了解底层数据和应用程序的具体需求。通过仔细选择和调整这些策略,开发人员可以显著提高基于向量的应用程序的响应速度和可扩展性,确保它们满足实际应用场景的需求。

使用向量数据库为LLM丰富上下文

大型语言模型(LLMs)如 Facebook 的 LLama2 或 TIIUAE 的 Falcon,通过生成类似人类的文本大大提升了 AI 的能力。然而,由于它们是基于广泛、通用的数据集进行训练的,因此在处理专业化的上下文时面临挑战。

解决上下文限制可以采用两种主要方法:

  1. 定向训练 : 这种方法涉及重新训练或 微调LLM 以专注于特定领域的数据集。虽然这种方法可以显著提升模型在特定主题或行业方面的专业能力,但对于许多组织或个人来说,这种方法往往不可行。原因包括与训练所需的计算资源相关的高昂成本以及有效重新训练如此复杂模型所需的专业知识:有效重新训练LLM需要深入了解机器学习、自然语言处理以及模型的具体架构。
  2. 通过向量数据库添加上下文 : 另一种方法是通过向LLM的提示中直接添加上下文,利用来自向量数据库的数据。在这种设置中,向量数据库存储专门的信息作为向量嵌入,这些嵌入可以被LLM检索并用于增强其响应。这种方法允许在不需要大量重新训练的情况下包含相关和专业的知识。对于缺乏定向训练资源的组织或个人来说,这种方法特别有用,因为它利用了现有模型的能力,同时提供了有针对性的上下文洞察。

第二种选项称为 RAG,我们将在接下来的章节中更详细地探讨它。

来源

使用Falcon-7B和ChromaDB构建Closed-QA机器人

在本节中,我们将概述如何使用向量数据库构建大型语言模型(LLM)的过程。该模型是一个Closed-QA bot。此机器人旨在通过一系列集成的技术组件有效回答科学相关的问题:

  1. databricks-dolly-15kHuggingFace 数据集:这是一个开源的数据集,由 Databricks 员工生成的指令跟随记录组成。该数据集旨在用于训练大型语言模型(LLMs)、合成数据生成和数据增强。数据集包括各种类型的提示和响应,涵盖头脑风暴、分类、封闭问答、生成、信息提取、开放问答和总结等类别。
  2. Chroma 作为向量存储(知识库):我们使用 Chroma 作为主要的向量存储,作为我们机器人的知识库。
  3. Sentence Transformers 用于语义搜索:具体来说,我们使用 Sentence Transformers 中的‘multi-qa-MiniLM-L6-cos-v1’模型,该模型针对语义搜索应用进行了优化。该模型负责生成嵌入并将其存储在 Chroma 中。
  4. Falcon 7B Instruct模型:作为我们的开源生成模型,Falcon 7B 是一个解码器模型,具有 70 亿参数。由 TII 开发,它是在一个广泛的 1,500B 令牌数据集 RefinedWeb 上训练的,该数据集补充了经过精心策划的语料库。值得注意的是,其更大的版本 Falcon 40B 在 Hugging Face 的 Open LLM Leaderboard 上排名首位。
设置环境

为了实现本文讨论的代码,需要安装以下内容:

    !pip install -qU \  
        transformers==4.30.2 \  
        torch==2.0.1+cu118 \  
        einops==0.6.1 \  
        accelerate==0.20.3 \  
        datasets==2.14.5 \  
        chromadb \  
        sentence-transformers==2.2.2

该代码最初在 Qwak 的 Workspaces 的 ​​gpu.a10.2xl 实例 上运行。需要注意的是,运行 Falcon-7B-Instruct 模型所需的特定代码可能会根据使用的硬件配置有所不同。

构建“知识库”

首先,我们获取 Databricks-Dolly 数据集,重点关注 closed_qa 类别。这些条目通常因其对精确信息的需求而具有特定性,这使得它们对一般训练的大型语言模型 (LLM) 构成了挑战。

    from datasets import load_dataset  

    # 加载数据集的训练部分  
    train_dataset = load_dataset("databricks/databricks-dolly-15k", split='train')  

    # 过滤数据集,仅保留类别为 'closed_qa' 的条目  
    closed_qa_dataset = train_dataset.filter(lambda example: example['category'] == 'closed_qa')  

    print(closed_qa_dataset[0])

一个典型的数据集条目如下所示:

    {  
      "instruction": "田村知明是什么时候出生的?",  
      "context": "田村知明于1981年7月10日出生在日本熊本县。高中毕业后,他在2000年加入了J1联赛俱乐部福冈AVISPA。他的职业生涯涉及多个位置和俱乐部,从福冈AVISPA的中场球员到奥ita Trinita、山形山头队、神户维斯特尔和熊本红队等俱乐部的防守中场和中后卫。他还曾在印度尼西亚的佩塞拉·拉蒙甘队效力,之后回到日本加入吉拉万茨·北九州队,并于2012年退役。",  
      "response": "田村知明出生于1981年7月10日。",  
      "category": "closed_qa"  
    }

接下来,我们将专注于为每组指令及其相应上下文生成词嵌入,并将它们整合到我们的向量数据库ChromaDB中。

Chroma DB 是一个开源的向量存储系统,特别擅长管理向量嵌入。它适用于语义搜索引擎等应用,在自然语言处理和机器学习领域尤为重要。作为内存数据库,Chroma DB 的高效性使得快速访问和操作数据成为可能,这对于高速数据处理至关重要。其友好的 Python 设置增强了它在我们项目中的吸引力,简化了集成到我们工作流程的过程。更多详细文档请参阅:Chroma DB 文档。

来源

为了生成答案的嵌入,我们使用了 multi-qa-MiniLM-L6-cos-v1,该模型专门用于语义搜索场景。给定一个问题/搜索查询,该模型能够找到相关的文本片段,这正是我们所期望的。

在下面的例子中,我们展示了如何将嵌入存储在 Chroma 的内存集合中。

    import chromadb  
    from sentence_transformers import SentenceTransformer  

    class VectorStore:  

        def __init__(self, collection_name):  
           # 初始化嵌入模型  
            self.embedding_model = SentenceTransformer('sentence-transformers/multi-qa-MiniLM-L6-cos-v1')  
            self.chroma_client = chromadb.Client()  
            self.collection = self.chroma_client.create_collection(name=collection_name)  

        # 方法用于使用数据集中的嵌入填充向量存储  
        def populate_vectors(self, dataset):  
            for i, item in enumerate(dataset):  
                combined_text = f"{item['instruction']}. {item['context']}"  
                embeddings = self.embedding_model.encode(combined_text).tolist()  
                self.collection.add(embeddings=[embeddings], documents=[item['context']], ids=[f"id_{i}"])  

        # 方法用于根据查询在ChromaDB集合中搜索相关上下文  
        def search_context(self, query, n_results=1):  
            query_embeddings = self.embedding_model.encode(query).tolist()  
            return self.collection.query(query_embeddings=query_embeddings, n_results=n_results)  

    # 示例用法  
    if __name__ == "__main__":  
       # 使用集合名称初始化处理程序  
        vector_store = VectorStore("知识库")  

        # 假设closed_qa_dataset已定义并可用  
        vector_store.populate_vectors(closed_qa_dataset)

对于每个数据集条目,我们生成并存储将“指令”和“上下文”字段结合后的嵌入,其中上下文作为检索时LLM提示中的文档。

接下来,我们将使用 Falcon-7b-instruct LLM 生成对封闭信息查询的响应,无需额外上下文,展示我们丰富知识库的有效性。

生成基本答案

对于我们的生成文本任务,我们将利用来自Hugging Face的falcon-7b-instruct模型的能力。该模型是阿布扎比科技创新研究所开发的创新Falcon系列的一部分。

让Falcon-7B-Instruct脱颖而出的是它高效地平衡了高级功能和可管理的规模。它设计用于复杂的文本理解和生成任务,提供与更大、闭源模型相当的性能,但以更精简的形式呈现。这使得它成为我们项目的理想选择,在需要深入的语言理解而不受更大模型开销影响的情况下尤为合适。

如果你计划运行Falcon-7B-Instruct模型,无论是本地机器还是远程服务器,都需要注意硬件要求。如HuggingFace的文档中所述,该模型至少需要16GB的内存。然而,为了获得最佳性能和更快的响应时间,强烈建议使用GPU。

    import transformers  
    import torch  
    from transformers import AutoTokenizer, AutoModelForCausalLM  

    class Falcon7BInstructModel:  

        def __init__(self):  
            # 模型名称  
            model_name = "tiiuae/falcon-7b-instruct"  
            self.pipeline, self.tokenizer = self.initialize_model(model_name)  

        def initialize_model(self, model_name):  
            # 分词器初始化  
            tokenizer = AutoTokenizer.from_pretrained(model_name)  

            # 设置文本生成管道  
            pipeline = transformers.pipeline(  
                "text-generation",  
                model=model_name,  
                tokenizer=tokenizer,  
                torch_dtype=torch.bfloat16,  
                trust_remote_code=True,  
                device_map="auto",  
            )  

            return pipeline, tokenizer  

        def generate_answer(self, question, context=None):  
            # 准备输入提示  
            prompt = question if context is None else f"{context}\n\n{question}"  

            # 生成响应  
            sequences = self.pipeline(  
                prompt,  
                max_length=500,  
                do_sample=True,  
                top_k=10,  
                num_return_sequences=1,  
                eos_token_id=self.tokenizer.eos_token_id,  
            )  

            # 提取并返回生成的文本  
            return sequences['generated_text']

这段代码示例基于Hugging Face的文档,非常清晰易懂。

让我们拆解其主要组件以更好地理解:

  • 分词器 是自然语言处理(NLP)模型如 Falcon-7B-Instruct 中的关键组件。它的主要作用是将输入文本转换为模型可以理解的格式。本质上,它将文本分解成更小的单元,称为令牌。这些令牌可以是单词、子词或甚至是字符,这取决于分词器的设计。在 Falcon-7B-Instruct 模型的上下文中,AutoTokenizer.from_pretrained(model) 这个调用加载了一个专门为此模型设计的分词器,确保文本的分词方式与模型的训练方式一致。
  • 管道 是 transformers 库中的一个高层次工具,它抽象了处理数据和从模型获取预测所涉及的许多复杂性。它内部处理多个步骤,例如对输入文本进行分词、将令牌输入模型,然后将模型的输出处理成人类可读的形式。在这个脚本中,管道被设置为“文本生成”,这意味着它被优化为接收一个提示(如用户的问题),并根据该提示生成文本的延续。

示例用法:

    # 初始化Falcon模型类  
    falcon_model = Falcon7BInstructModel()  

    user_question = "Tomoaki Komorida是什么时候出生的?"  

    # 使用LLM生成用户问题的答案  
    answer = falcon_model.generate_answer(user_question)  

    print(f"结果: {answer}")

正如你可能猜到的,这里是给定用户问题的模型输出:

    { answer: “我没有关于古本耕治的出生日期的信息。” }

利用 Falcon-7B-Instruct 而不提供额外上下文会得到一个负面回应,因为它没有经过这种“鲜为人知”的信息的训练。这说明了在生成更具体和有用的答案时,需要提供更丰富的上下文。

生成上下文相关的答案

现在,让我们通过从向量存储中检索相关上下文来提升生成模型的能力。

有趣的是,我们使用相同的 VectorStore 类来生成嵌入并向量以及从用户问题中获取上下文:

    # 假设 vector_store 和 falcon_model 已经初始化  

    # 从 VectorStore 中获取上下文,假设它已经被填充  
    context_response = vector_store.search_context(user_question)  

    # 从响应中提取上下文文本  
    # 假设上下文在 'context' 键的第一个元素中  
    context = "".join(context_response['context'][0])   

    # 使用 Falcon 模型生成答案,结合获取的上下文  
    enriched_answer = falcon_model.generate_answer(user_question, context=context)  

    print(f"结果: {enriched_answer}")

当然,我们LLM提供的上下文丰富答案准确而迅速:

    古居.tomosaic.智生于1981年7月10日。

注:人名“Tomoaki Komorida”在中文中通常保留原文,此处假设为“古居智”,请根据实际情况调整。

总结

在我们的详细探索中,我们已经向您展示了如何构建一个大型语言模型(LLM)应用程序,并通过自定义数据集丰富了该模型。很明显,管理这样一个模型、实验不同的数据集、设置必要的基础设施并实现一个功能性的解决方案绝非易事。然而,这就是 Qwak 的优势所在,它简化了这一复杂的过程。使用 Qwak,您不仅在管理模型,更是在从概念到部署的旅程中有效提高了效率,使您的环境中上下文感知的 LLM 在短短几个小时内即可运行。

展望未来,我们非常激动能够通过不断改进现有功能来提升您使用 Qwak 的体验。我们当前的重点是增强与我们 向量存储 的集成,提供更强大的 ETL(提取、转换、加载)和可视化功能。

下一步步骤

要查看本文中讨论的应用程序的全面构建和部署示例,请访问我们的示例仓库。我们已经详细准备了所有内容,以确保您的旅程既顺畅又富有信息。

我们希望这篇文章能激发你的兴趣和好奇心。我们邀请你开始自己的冒险,使用 Qwak 和向量数据库构建大型语言模型 (LLM)。今天就开始你的旅程,探索这个前沿平台提供的各种可能性,完全免费。祝你建模愉快!

原发布于 https://www.qwak.com .

这篇关于集成向量数据库与LLM:实战指南的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!