第8章:LangChain检索增强生成RAG--2.2Core RAG APIs

LangChain4j 提供了一套丰富的 API,用于构建从简单到高级的检索增强生成(RAG)系统。这些 API 覆盖了从文档加载、预处理、嵌入到检索的全过程,使得开发者可以灵活地构建智能问答系统。以下是 Core RAG APIs 的核心组件及其功能的总结

这篇文章内容比较多,请分章节阅读,对做RAG非常有用

Core RAG APIs

LangChain4j 提供了一系列丰富的 API,帮助你构建从简单到高级的自定义 RAG 管道。以下是主要的领域类和 API。

1 Document(文档)

Document 类表示一个完整的文档,如单个 PDF 文件或网页。目前,Document 仅支持文本信息,但未来将支持图像和表格。

Metadata.from(Map):从一个 Map 创建 MetadataMetadata.put(String key, String value) / put(String, int) / 等等:向 Metadata 中添加一个条目。
Metadata.getString(String key) / getInteger(String key) / 等等:返回 Metadata 条目的值,并将其转换为所需的类型。
Metadata.containsKey(String key):检查 Metadata 是否包含指定键的条目。
Metadata.remove(String key):通过键从 Metadata 中移除一个条目。
Metadata.copy():返回 Metadata 的一个副本。
Metadata.toMap():将 Metadata 转换为一个 Map

2 Metadata(元数据)

Metadata的概念

每个 Document 都包含 Metadata。它存储了关于文档的元信息,例如文档的名称、来源、最后更新日期、所有者,或其他任何相关的细节。

Metadata 以键值对的形式存储,其中键是 String
类型,值可以是以下类型之一:String、Integer、Long、Float、Double。

Metadata 是一种键值对存储结构,用于描述文档的元信息。这些元信息可以包括但不限于:

  • 文档的名称(name)
  • 文档的来源(source)
  • 文档的最后更新日期(lastUpdateDate)
  • 文档的所有者(owner)
  • 其他任何相关的细节

Metadata 的用途有多个方面:

  1. 增强 LLM 的理解
    当将文档内容传递给语言模型时,Metadata 中的信息(如文档名称和来源)可以被包含在提示中,帮助语言模型更好地理解文档的上下文。
    例如,如果文档的来源是一个特定的网站或作者,这些信息可以帮助语言模型生成更准确的回答。

  2. 过滤和搜索
    在检索相关文档时,可以通过 Metadata 条目进行过滤。例如,你可以通过 owner 字段将搜索范围限制为特定用户的所有文档。
    这种过滤机制可以提高检索的效率和准确性。

  3. 同步更新
    当文档的来源(如网页或文件)被更新时,可以通过 Metadata 中的标识符(如 id 或 source)快速定位到对应的文档,并在嵌入存储(EmbeddingStore)中进行更新,以保持数据的一致性。

有用的元数据方法:

  • Metadata.from(Map):从一个 Map 创建 Metadata。
  • Metadata.put(String key, String value) / put(String, int) / 等等:向 Metadata 中添加一个条目。
  • Metadata.getString(String key) / getInteger(String key) / 等等:返回 Metadata 条目的值,并将其转换为所需的类型。
  • Metadata.containsKey(String key):检查 Metadata 是否包含指定键的条目。
  • Metadata.remove(String key):通过键从 Metadata 中移除一个条目。
  • Metadata.copy():返回 Metadata 的一个副本。
  • Metadata.toMap():将 Metadata 转换为一个 Map。

Metadata 是 LangChain4j 中的一个重要组件,用于存储和管理文档的元信息。它通过提供丰富的操作方法,使得开发者可以灵活地处理文档的上下文信息,从而提高语言模型的理解能力和检索效率。通过合理使用 Metadata,开发者可以构建更加智能和高效的问答系统。

3 Document Loader(文档加载器)

LangChain4j 提供的多种文档加载器(DocumentLoader),这些加载器用于从不同的数据源加载文档。文档加载器是构建智能问答系统时非常重要的组件,因为它们负责将文档数据导入到系统中,以便后续进行处理和检索。以下是每个加载器的详细解析:

1. FileSystemDocumentLoader

从本地文件系统加载文档。它支持从指定路径加载单个文件或整个目录中的文件。

  • 用途:适用于本地存储的文档,例如 PDF、TXT、HTML 等。
  • 示例:
// 加载单个文件
Document document = FileSystemDocumentLoader.loadDocument("/path/to/file.txt");

// 加载整个目录中的所有文件
List<Document> documents = FileSystemDocumentLoader.loadDocuments("/path/to/directory");

2. ClassPathDocumentLoader

从类路径(classpath)加载文档。它允许你加载存储在项目资源文件夹中的文档。

  • 用途:适用于开发和测试阶段,尤其是当文档存储在项目的资源文件夹中时。
  • 示例:
// 加载类路径中的文件
Document document = ClassPathDocumentLoader.loadDocument("resources/file.txt");

3. UrlDocumentLoader

从 URL 加载文档。它支持从网络地址加载文档,例如网页或在线文件。

  • 用途:适用于从互联网加载文档,例如从博客、新闻网站或在线文档库加载内容。
  • 示例
// 加载网页内容
Document document = UrlDocumentLoader.loadDocument("https://example.com/page.html");

4. AmazonS3DocumentLoader

从 Amazon S3 存储桶加载文档。它支持从 AWS S3 存储桶加载文件。

  • 用途:适用于使用 AWS S3 存储文档的场景。
  • 示例
// 加载 S3 存储桶中的文件
Document document = AmazonS3DocumentLoader.loadDocument("s3://bucket-name/path/to/file.txt");

5. AzureBlobStorageDocumentLoader

从 Azure Blob Storage 加载文档。它支持从 Azure Blob 存储加载文件。

  • 用途:适用于使用 Azure Blob Storage 存储文档的场景。-
  • 示例
// 加载 Azure Blob 存储中的文件
Document document = AzureBlobStorageDocumentLoader
                   .loadDocument("azure://container-name/path/to/file.txt");

6. GitHubDocumentLoader

从 GitHub 仓库加载文档。它支持从 GitHub 仓库加载文件或整个目录。

  • 用途:适用于从 GitHub 仓库加载文档,例如开源项目文档或代码注释。
  • 示例
// 加载 GitHub 仓库中的文件
Document document = GitHubDocumentLoader
 				.loadDocument("https://github.com/user/repo/path/to/file.txt");

7. GoogleCloudStorageDocumentLoader

从 Google Cloud Storage 加载文档。它支持从 Google Cloud Storage 存储桶加载文件。

  • 用途:适用于使用 Google Cloud Storage 存储文档的场景。
  • 示例
// 加载 Google Cloud Storage 存储桶中的文件
Document document = GoogleCloudStorageDocumentLoader.loadDocument("gs://bucket-name/path/to/file.txt");

8. SeleniumDocumentLoader

使用 Selenium 从网页加载文档。它支持从动态生成的网页加载内容,例如需要 JavaScript 渲染的页面。

  • 用途:适用于加载需要动态渲染的网页内容。
  • 示例
// 加载动态网页内容
Document document = SeleniumDocumentLoader.loadDocument("https://example.com/dynamic-page");

9. TencentCosDocumentLoader

从腾讯云 COS(Cloud Object Storage)加载文档。它支持从腾讯云 COS 存储桶加载文件。

  • 用途:适用于使用腾讯云 COS 存储文档的场景。
  • 示例
// 加载腾讯云 COS 存储桶中的文件
Document document = TencentCosDocumentLoader
					.loadDocument("cos://bucket-name/path/to/file.txt");

4 Document Parser(文档解析器)

LangChain4j 中的 DocumentParser 接口及其多种实现。DocumentParser 的作用是从各种文件格式中提取文本内容,以便后续处理和分析

DocumentParser 接口用于解析不同格式的文件,如 PDF、DOC、TXT 等。LangChain4j 提供了多种实现,例如 ApacheTikaDocumentParser 可以自动检测和解析几乎所有文件格式。

LangChain4j 提供了多种 DocumentParser 实现,支持从不同格式的文件中提取文本内容。这些解析器包括:

  • TextDocumentParser:适用于纯文本文件。
  • ApachePdfBoxDocumentParser:适用于 PDF 文件。
  • ApachePoiDocumentParser:适用于 Microsoft Office 文件。
  • ApacheTikaDocumentParser:适用于多种文件格式。

通过合理选择和使用这些解析器,开发者可以灵活地处理各种文档格式,从而为后续的文档处理和检索提供支持。此外,LangChain4j 还支持通过 SPI 自动加载默认解析器,简化了开发流程

1. TextDocumentParser

TextDocumentParser 是一个简单的解析器,用于解析纯文本格式的文件,例如 TXT、HTML 和 Markdown(MD)文件。

  • 用途:适用于简单文本文件,尤其是那些不需要复杂解析的文件。
  • 示例:
Document document = FileSystemDocumentLoader
		.loadDocument("/path/to/file.txt", new TextDocumentParser());

2. ApachePdfBoxDocumentParser

ApachePdfBoxDocumentParser 使用 Apache PDFBox 库来解析 PDF 文件。

  • 用途:适用于 PDF 文件,能够提取文本内容。
  • 示例:
Document document = FileSystemDocumentLoader
			.loadDocument("/path/to/file.pdf", new ApachePdfBoxDocumentParser());

3. ApachePoiDocumentParser

ApachePoiDocumentParser 使用 Apache POI 库来解析 Microsoft Office 文件格式,例如 DOC、DOCX、PPT、PPTX、XLS 和 XLSX。

  • 用途:适用于 Microsoft Office 文件,能够提取文本内容。
  • 示例:
Document document = FileSystemDocumentLoader.loadDocument("/path/to/file.docx", new ApachePoiDocumentParser());

4. ApacheTikaDocumentParser

ApacheTikaDocumentParser 使用 Apache Tika 库来自动检测并解析几乎所有现有的文件格式。

  • 用途:适用于多种文件格式,尤其是当不确定文件类型时。
  • 示例:
Document document = FileSystemDocumentLoader
		.loadDocument("/path/to/file.unknown", new ApacheTikaDocumentParser());

5 Document Transformer(文档转换器)

DocumentTransformer 的实现可以执行多种文档转换操作,包括:

  • 清理(Cleaning):从文档的文本中移除不必要的噪声,这可以节省令牌(tokens)并减少干扰。
  • 过滤(Filtering):从搜索中完全排除特定的文档。
  • 丰富(Enriching):向文档中添加额外信息,以潜在地增强搜索结果。
  • 总结(Summarizing):对文档进行总结,并将其简短的总结存储在元数据中。稍后,这些总结可以被包含在每个 TextSegment 中(我们将在下面讨论),以潜在地改善搜索效果。
  • 等等

在这个阶段,也可以添加、修改或删除元数据条目。

目前,LangChain4j 提供的现成实现只有 langchain4j-document-transformer-jsoup 模块中的 HtmlToTextDocumentTransformer,它可以从未加工的 HTML 中提取所需的文本内容和元数据条目。

由于没有一种放之四海而皆准的解决方案,我们建议根据你的独特数据实现自己的 DocumentTransformer。

1. 清理(Cleaning)

功能:从文档的文本中移除不必要的噪声,例如多余的空格、HTML 标签、特殊字符等。
目的

  • 节省令牌:减少文档的大小,从而节省在语言模型中使用的令牌数量。
  • 减少干扰:提高文档的可读性和语言模型的理解能力。
    示例:
Document document = new Document("This is a sample document with unnecessary noise. \n\n");
document = new CleaningDocumentTransformer().transform(document);

2. 过滤(Filtering)

功能:从搜索结果中完全排除特定的文档。
目的

  • 提高相关性:确保搜索结果只包含与查询相关的文档。
  • 节省资源:避免处理无关的文档,节省计算资源。
    示例
List<Document> documents = ...; // 从某个来源加载的文档列表
documents = new FilteringDocumentTransformer()
			.transform(documents);

3. 丰富(Enriching)

功能:向文档中添加额外信息,例如关键词、摘要、来源等。
目的

  • 增强搜索结果:通过添加更多上下文信息,提高文档在搜索中的相关性。
  • 提供更多信息:为语言模型提供更多背景信息,帮助其生成更准确的回答。
    示例
Document document = new Document("This is a sample document.");
document = new EnrichingDocumentTransformer()
			.transform(document);

4. 总结(Summarizing)

功能:对文档进行总结,并将总结存储在元数据中。
目的

  • 提高效率:通过总结,语言模型可以更快地理解文档的核心内容。
  • 改善搜索:总结可以被包含在每个 TextSegment 中,从而提高搜索的准确性。
    示例
Document document = new Document("This is a long document with detailed information.");
document = new SummarizingDocumentTransformer().transform(document);
String summary = document.getMetadata().getString("summary");

5. 元数据操作

在文档转换阶段,还可以添加、修改或删除元数据条目。
目的

  • 提供上下文:元数据可以为文档提供额外的上下文信息。
  • 动态调整:根据需要动态调整文档的元数据。
    示例
Document document = new Document("This is a sample document.");
document.getMetadata().put("source", "file://path/to/document.txt");
document.getMetadata().put("author", "John Doe");

6. 现成实现

目前,LangChain4j 提供的现成实现只有 HtmlToTextDocumentTransformer,它使用 Jsoup 库从未加工的 HTML 中提取所需的文本内容和元数据条目。
用途

  • HTML 文档处理:适用于从网页或 HTML 文件中提取纯文本内容。
  • 元数据提取:可以提取 HTML 中的元数据,例如标题、描述等。

示例

Document document = FileSystemDocumentLoader
		.loadDocument("/path/to/file.html", new HtmlToTextDocumentTransformer());

7. 自定义实现

由于没有一种通用的解决方案,LangChain4j 建议开发者根据自己的数据特点实现自己的 DocumentTransformer。
建议

  • 自定义逻辑:根据你的数据格式和需求,实现特定的转换逻辑。
  • 灵活性:自定义转换器可以更好地适应你的应用场景。

示例

public class CustomDocumentTransformer implements DocumentTransformer {
    @Override
    public Document transform(Document document) {
        // 自定义转换逻辑
        String cleanedText = document.getText().replaceAll("\\s+", " ");
        document.setText(cleanedText);
        document.getMetadata().put("customKey", "customValue");
        return document;
    }
}

6 Text Segment(文本片段)

一旦你的文档被加载,下一步就是将它们分割成更小的片段(分块)。LangChain4j 的领域模型中包含了一个 TextSegment 类,它代表文档的一个片段。顾名思义,TextSegment 只能表示文本信息。

TextSegment 是 LangChain4j 中的一个重要类,用于表示文档的一个片段。通过将文档分割成片段,可以更高效地处理和检索文档内容,同时减少令牌消耗和提高检索质量。开发者可以根据具体需求选择是否分割文档,并采用合适的策略来确保片段提供足够的上下文信息。通过合理使用
TextSegment,可以显著提高 RAG 流程的效率和效果。

1. TextSegment 的作用

TextSegment 是 LangChain4j 中的一个类,用于表示文档的一个片段。它只能包含文本信息,不支持其他类型的内容(如图像或表格)。通过将文档分割成多个片段,可以更高效地处理和检索文档内容。

2. 是否需要分割文档?

是否需要将文档分割成片段取决于多个因素,包括语言模型的上下文窗口大小、处理效率、成本以及检索质量。以下是两种常见的处理方法:

2.1 不分割文档(原子性文档)

在这种方法中,每个文档被视为一个不可分割的整体。在 RAG 流程中,检索最相关的 N 个文档并将其注入到提示中。这种方法的优点是不会丢失上下文,但缺点包括:

  • 消耗更多的令牌,因为需要处理整个文档。
  • 文档可能包含多个部分或主题,而这些部分并非都与查询相关。
  • 向量搜索的质量可能受到影响,因为不同长度的文档被压缩成固定长度的向量。
2.2 分割文档(片段化)

在这种方法中,文档被分割成更小的片段,如章节、段落或句子。在 RAG 流程中,检索最相关的 N 个片段并将其注入到提示中。这种方法的优点包括:

  • 提高向量搜索的质量,因为片段更小且更易于处理。
  • 减少令牌消耗,因为只处理相关片段。
  • 提高检索效率,因为片段更小且更易于检索。

然而,这种方法的挑战在于确保每个片段都提供足够的上下文信息,以便语言模型能够理解。如果上下文不足,语言模型可能会误解片段内容并产生幻觉。为了解决这个问题,可以采用以下高级技术:

  • 句子窗口检索:检索片段周围的句子,提供更多的上下文。
  • 自动合并检索:自动合并相关片段,提供更完整的上下文。
  • 父文档检索:检索片段的父文档,提供更广泛的上下文。

3. TextSegment 的方法

TextSegment 提供了以下方法,用于操作和管理文本片段:

  • TextSegment.text():返回片段的文本内容。
  • TextSegment.metadata():返回片段的元数据。
  • TextSegment.from(String, Metadata):从文本和元数据创建一个新的 TextSegment。
  • TextSegment.from(String):从文本创建一个新的 TextSegment,使用空的元数据。

4. 示例代码

以下是一个示例,展示如何使用 TextSegment:

// 创建一个 TextSegment
Metadata metadata = Metadata.from(Map.of("source", "file1.txt", "author", "John Doe"));
TextSegment segment = TextSegment.from("This is a sample text segment.", metadata);

// 获取文本内容
String text = segment.text(); // 返回 "This is a sample text segment."

// 获取元数据
Metadata segmentMetadata = segment.metadata(); // 返回包含 source 和 author 的 Metadata

5. 分割文档时,如何选择合适的分割大小

选择合适的片段大小(Segment Size)是文档分割过程中的一个重要决策,因为它直接影响到检索效率、语言模型的处理能力和生成结果的质量。以下是一些选择合适片段大小的建议和考虑因素:

1. 语言模型的上下文窗口限制

语言模型(LLM)通常有一个固定的上下文窗口大小,即它能够处理的最大文本量。例如:

  • GPT-3 的上下文窗口为 2048 个令牌(tokens)。
  • GPT-4 的上下文窗口为 8192 个令牌。

如果片段大小超过语言模型的上下文窗口,那么在处理时可能会被截断,导致信息丢失。因此,片段大小应小于或等于语言模型的上下文窗口大小。
建议

  • 如果使用 GPT-3,片段大小可以设置为 1500-2000 个令牌。
  • 如果使用 GPT-4,片段大小可以设置为 6000-7000 个令牌。
2. 检索效率

较小的片段可以提高检索效率,因为它们更容易被嵌入模型处理,并且在向量数据库中的相似性搜索会更快。然而,如果片段过小,可能会丢失上下文信息,导致语言模型无法理解片段的完整含义。
建议

  • 最小片段大小:至少包含一个完整的句子或段落,以确保有足够的上下文。
  • 最大片段大小:不超过语言模型的上下文窗口大小。
3. 信息完整性

片段应包含足够的上下文信息,以便语言模型能够理解其内容。如果片段过小,可能会丢失重要的上下文信息,导致语言模型产生幻觉(hallucinations)。如果片段过大,则可能会包含无关信息,增加处理成本。
建议

  • 自然边界:根据文档的自然结构进行分割,例如按段落、章节或句子分割。
  • 重叠:在片段之间设置一定的重叠,以确保上下文的连贯性。例如,每个片段可以有 50-100 个令牌的重叠。
4. 成本考虑

较大的片段会消耗更多的令牌,增加处理成本。较小的片段可以减少令牌消耗,但可能会降低检索质量。
建议

  • 平衡成本和质量:选择一个既能满足语言模型上下文需求,又能减少令牌消耗的片段大小。
  • 实验和优化:通过实验找到最适合你数据和需求的片段大小。
5. 实践中的片段大小

在实际应用中,片段大小通常根据文档类型和语言模型的特性进行调整。以下是一些常见的片段大小范围:

  • 文本文件(TXT):每个片段包含 200-500 个单词,大约 1000-2000 个令牌。
  • PDF 文件:每个片段包含 1-2 个段落,大约 300-500 个令牌。
  • HTML 页面每个片段包含一个完整的段落或一个 HTML 元素(如 <p> 或 <div>),大约 200-300 个令牌。
6. 示例

假设你使用的是 GPT-3,其上下文窗口为 2048 个令牌。你可以选择以下片段大小:

  • 段落分割:每个片段包含一个完整的段落,大约 300-500 个令牌。
  • 句子分割:每个片段包含 3-5 个句子,大约 200-300 个令牌。
  • 章节分割:每个片段包含一个完整的章节,但不超过 2000 个令牌。

示例代码

// 使用 DocumentSplitter 将文档分割成片段
DocumentSplitter splitter = DocumentSplitters.byParagraph(500, 50); // 每个片段最多 500 个令牌,重叠 50 个令牌
List<TextSegment> segments = splitter.split(document);

7 Document Splitter(文档分割器)

DocumentSplitter 是 LangChain4j 中用于将文档分割成更小片段(TextSegment)的工具。分割文档是 RAG(检索增强生成)流程中的一个重要步骤,因为它可以提高检索效率和语言模型的处理能力。以下是每个分割器的详细解析:

1. DocumentByParagraphSplitter

按段落分割文档,每个段落被视为一个独立的单元。
用途:适用于需要按自然段落结构分割文档的场景。
示例

DocumentSplitter splitter = new DocumentByParagraphSplitter(500, 50); // 每个片段最多 500 个令牌,重叠 50 个令牌
List<TextSegment> segments = splitter.split(document);
2. DocumentByLineSplitter

按行分割文档,每行被视为一个独立的单元。
用途:适用于按行分割的文本文件,例如代码文件或日志文件。
示例

DocumentSplitter splitter = new DocumentByLineSplitter(100, 10); // 每行最多 100 个字符,重叠 10 个字符
List<TextSegment> segments = splitter.split(document);
3. DocumentBySentenceSplitter

按句子分割文档,使用自然语言处理库(如 OpenNLP)检测句子边界。
用途:适用于需要按句子结构分割文档的场景,尤其是语言模型需要理解句子上下文的情况。
示例

DocumentSplitter splitter = new DocumentBySentenceSplitter(200, 20); // 每个句子最多 200 个令牌,重叠 20 个令牌
List<TextSegment> segments = splitter.split(document);
4. DocumentByWordSplitter

按单词分割文档,每个单词被视为一个独立的单元。
用途:适用于需要按单词分割的场景,例如词频分析或关键词提取。
示例

DocumentSplitter splitter = new DocumentByWordSplitter(50, 5); // 每个片段最多 50 个单词,重叠 5 个单词
List<TextSegment> segments = splitter.split(document);
5. DocumentByCharacterSplitter

按字符分割文档,每个字符被视为一个独立的单元。
用途:适用于需要按字符分割的场景,例如字符级别的语言模型。
示例

DocumentSplitter splitter = new DocumentByCharacterSplitter(100, 10); // 每个片段最多 100 个字符,重叠 10 个字符
List<TextSegment> segments = splitter.split(document);
6. DocumentByRegexSplitter

使用正则表达式分割文档,可以根据自定义的模式分割文档。
用途:适用于需要根据特定模式分割文档的场景,例如分割特定格式的日志文件或代码文件。
示例

DocumentSplitter splitter = new DocumentByRegexSplitter("\\n\\n", 500, 50); // 使用两个换行符分割,每个片段最多 500 个令牌,重叠 50 个令牌
List<TextSegment> segments = splitter.split(document);
7. 递归分割器(Recursive Splitter)

递归分割器可以处理无法放入单个 TextSegment 的较大单元。它会调用一个子分割器,将这些单元进一步分割成更细粒度的单元。
用途:适用于需要处理大型文档或复杂结构的场景。
示例

DocumentSplitter splitter = DocumentSplitters.recursive(500, 50, new DocumentByParagraphSplitter()); // 递归分割,每个片段最多 500 个令牌,重叠 50 个令牌
List<TextSegment> segments = splitter.split(document);
8. 片段的元数据

在分割过程中,所有元数据条目都会从原始文档复制到每个 TextSegment 中,并为每个片段添加一个唯一的 “index” 元数据条目。这有助于在后续处理中跟踪片段的来源和顺序。
示例

Metadata metadata = document.getMetadata();
for (TextSegment segment : segments) {
    System.out.println(segment.getMetadata().getString("index")); // 输出每个片段的索引
}
9. 总结

DocumentSplitter 是 LangChain4j 中的一个重要接口,用于将文档分割成更小的片段(TextSegment)。通过选择合适的分割器和参数,可以优化文档的处理效率和语言模型的生成质量。以下是选择分割器时需要考虑的关键点

  • 上下文窗口:片段大小应小于或等于语言模型的上下文窗口。
  • 自然边界:根据文档的自然结构进行分割,例如按段落、句子或章节。
  • 重叠:在片段之间设置一定的重叠,以确保上下文的连贯性。
  • 实验和优化:通过实验找到最适合你数据和需求的分割策略。
    通过合理使用 DocumentSplitter,可以显著提高 RAG 流程的效率和效果。

8 Text Segment Transformer(文本片段转换器)

TextSegmentTransformer 类似于 DocumentTransformer,但用于转换文本片段。TextSegmentTransformer 是一个接口,用于对 TextSegment 进行转换和优化,以提高后续处理和检索的效果。

TextSegmentTransformer 是 LangChain4j 中的一个重要接口,用于对 TextSegment进行转换和优化。通过实现自定义的TextSegmentTransformer,开发者可以根据自己的数据特点和需求,对文本片段进行清理、丰富和格式调整。在每个TextSegment 中包含文档的标题或摘要是一种有效的技术,可以显著提高检索效果和语言模型的生成质量。

1. TextSegmentTransformer 的作用

TextSegmentTransformer 用于对 TextSegment 进行转换和优化。它可以帮助:

  • 清理文本:移除不必要的噪声,节省令牌并减少干扰。
  • 丰富内容:添加额外信息,例如文档标题或摘要,以提高检索效果。
  • 调整格式:根据需要调整文本片段的格式,使其更适合语言模型处理。
2. 为什么需要自定义实现?

与 DocumentTransformer 类似,TextSegmentTransformer 没有通用的解决方案,因为不同的数据集和应用场景可能需要不同的处理逻辑。因此,LangChain4j 建议开发者根据自己的需求实现自定义的TextSegmentTransformer。

3. 提高检索效果的技术

一种有效的技术是在每个 TextSegment 中包含文档的标题或简短摘要。这样做的好处包括:

  • 提供上下文:标题或摘要可以为语言模型提供额外的上下文信息,帮助其更好地理解片段的内容。
  • 提高相关性:在检索时,这些额外信息可以帮助语言模型更准确地判断- 片段的相关性,从而提高检索质量。
  • 减少幻觉:通过提供更多的上下文信息,可以减少语言模型生成无关内容的可能性。
4. 示例代码

以下是一个简单的 TextSegmentTransformer 实现示例,展示如何在每个 TextSegment 中添加文档标题和摘要:

public class TitleAndSummaryTextSegmentTransformer implements TextSegmentTransformer {
    @Override
    public TextSegment transform(TextSegment textSegment) {
        // 获取文档的标题和摘要
        String title = textSegment.getMetadata().getString("title");
        String summary = textSegment.getMetadata().getString("summary");

        // 构造新的文本内容,包含标题和摘要
        String transformedText = title + "\n\n" + summary + "\n\n" + textSegment.text();

        // 创建新的 TextSegment
        return TextSegment.from(transformedText, textSegment.getMetadata());
    }
}
5. 使用自定义 TextSegmentTransformer

在实际应用中,你可以将自定义的 TextSegmentTransformer 集成到文档处理流程中。例如:
写一个自定义的转换类

public class TitleAndSummaryTextSegmentTransformer implements TextSegmentTransformer {

    @Override
    public TextSegment transform(TextSegment textSegment) {
        // 获取文档的标题和摘要
        String title = textSegment.getMetadata().getString("title");
        String summary = textSegment.getMetadata().getString("summary");

        // 构造新的文本内容,包含标题和摘要
        String transformedText = title + "\n\n" + summary + "\n\n" + textSegment.text();

        // 创建新的 TextSegment
        return TextSegment.from(transformedText, textSegment.getMetadata());
    }
}

使用自定义转换类

// 加载文档
Document document = FileSystemDocumentLoader.loadDocument("/path/to/file.txt");

// 分割文档为文本片段
DocumentSplitter splitter = new DocumentByParagraphSplitter(500, 50);
List<TextSegment> segments = splitter.split(document);

// 应用自定义 TextSegmentTransformer
TitleAndSummaryTextSegmentTransformer transformer = new TitleAndSummaryTextSegmentTransformer();
List<TextSegment> transformedSegments = segments.stream()
    .map(transformer::transform)
    .collect(Collectors.toList());

// 输出转换后的片段
transformedSegments.forEach(segment -> System.out.println(segment.text()));

9 Embedding(嵌入)

Embedding 类封装了表示文本语义的数值向量。LangChain4j 提供了多种嵌入模型,用于将文本转换为嵌入。

概述

Embedding 类封装了一个数值向量,该向量表示已嵌入内容的“语义含义”(通常是文本,例如 TextSegment)。

了解更多关于向量嵌入的信息,请参考以下资源:

  • What are Vector Embeddings? | A Comprehensive Vector Embeddings Guide
  • What are Vector Embeddings | Pinecone
  • Meet AI’s multitool: Vector embeddings

有用的方法

  • Embedding.dimension():返回嵌入向量的维度(长度)。
  • CosineSimilarity.between(Embedding, Embedding):计算两个嵌入之间的余弦相似度。
  • Embedding.normalize():归一化嵌入向量(原地操作)。

以下是一个简单的示例,展示如何使用 Embedding 类及其方法:

import dev.langchain4j.embedding.Embedding;
import dev.langchain4j.embedding.CosineSimilarity;

// 创建两个嵌入向量
Embedding embedding1 = new Embedding(new double[]{0.1, 0.2, 0.3});
Embedding embedding2 = new Embedding(new double[]{0.2, 0.3, 0.4});

// 计算嵌入向量的维度
int dimension1 = embedding1.dimension(); // 返回 3
int dimension2 = embedding2.dimension(); // 返回 3

// 计算两个嵌入向量之间的余弦相似度
double similarity = CosineSimilarity.between(embedding1, embedding2); // 返回相似度值

// 归一化嵌入向量
embedding1.normalize(); // 原地归一化
embedding2.normalize(); // 原地归一化

深度剖析

1. Embedding 的作用

Embedding 是一种将数据(如文本、图像、音频等)转换为数值向量的技术,这些向量能够捕捉数据的语义含义和上下文信息。通过嵌入向量,机器学习算法可以更有效地处理和理解数据。

2. 嵌入向量的类型

嵌入向量有多种类型,适用于不同的数据和应用场景:

  • 文本嵌入(Text Embeddings):将单词、句子或文档转换为向量。
  • 图像嵌入(Image Embeddings):将图像转换为向量,用于图像分类、相似性搜索等。
  • 用户嵌入(User Embeddings):将用户的行为和偏好转换为向量,用于个性化推荐。
  • 产品嵌入(Product Embeddings):将产品的属性和特征转换为向量,用于推荐系统。
3. 嵌入向量的创建

嵌入向量可以通过以下方式创建:

  • 预训练模型:使用现成的预训练模型(如 Word2Vec、GloVe、BERT)将文本转换为嵌入向量。
  • 自定义模型:使用深度学习模型(如卷积神经网络 CNN、Transformer)对特定数据集进行训练,生成嵌入向量。
4. 嵌入向量的应用

嵌入向量在多个领域有广泛应用:

  • 自然语言处理(NLP):情感分析、文本分类、机器翻译、问答系统。
  • 推荐系统:基于用户和物品的嵌入向量进行个性化推荐。
  • 搜索引擎:通过嵌入向量实现语义搜索。
  • 图像和视频分析:图像分类、目标检测、相似性搜索。
  • 异常检测:通过嵌入向量检测数据中的异常模式。
5. 嵌入向量的相似性度量

嵌入向量之间的相似性可以通过以下方法计算:

  • 余弦相似度(Cosine Similarity):计算两个向量之间的夹角余弦值,值越接近 1 表示越相似。
  • 欧几里得距离(Euclidean Distance):计算两个向量之间的直线距离,值越小表示越相似。
  • 点积(Dot Product):计算两个向量的点积,值越大表示越相似。

10 Embedding Model(嵌入模型)

概述

EmbeddingModel 接口表示一种特殊的模型,能够将文本转换为嵌入(文本的向量表示)。目前支持的嵌入模型可以在这里找到。
有用的方法

  • EmbeddingModel.embed(String):将给定的文本嵌入。
  • EmbeddingModel.embed(TextSegment):将给定的 TextSegment 嵌入。
  • EmbeddingModel.embedAll(List):将给定的 TextSegment 列表嵌入。
  • EmbeddingModel.dimension():返回此模型生成的嵌入的维度。

示例代码

import dev.langchain4j.model.embedding.EmbeddingModel;
import dev.langchain4j.data.segment.TextSegment;

// 创建一个嵌入模型实例
EmbeddingModel embeddingModel = new OpenAiEmbeddingModel("your-api-key", "text-embedding-ada-002");

// 嵌入单个文本
Embedding embedding = embeddingModel.embed("This is a sample text.");

// 嵌入 TextSegment
TextSegment textSegment = TextSegment.from("This is another sample text.");
Embedding segmentEmbedding = embeddingModel.embed(textSegment);

// 嵌入多个 TextSegment
List<TextSegment> textSegments = List.of(
    TextSegment.from("First sample text."),
    TextSegment.from("Second sample text.")
);
List<Embedding> embeddings = embeddingModel.embedAll(textSegments);

// 获取嵌入的维度
int dimension = embeddingModel.dimension();
System.out.println("Embedding dimension: " + dimension);

EmbeddingModel 是 LangChain4j 中的一个核心接口,用于将文本或其他数据类型转换为嵌入向量。嵌入向量是一种数值表示,能够捕捉数据的语义含义和上下文信息,使得机器学习算法可以更有效地处理和理解数据

11 Embedding Store(嵌入存储)

EmbeddingStore 是 LangChain4j 中的一个核心接口,用于存储和检索嵌入向量。嵌入存储通常被称为向量数据库,它允许高效地存储和搜索相似的嵌入向量

EmbeddingStore 是 LangChain4j中的一个重要接口,用于存储和检索嵌入向量。这些向量可以单独存储,也可以与原始数据(如TextSegment)一起存储。通过向量数据库,可以高效地执行相似性搜索和嵌入管理。LangChain4j提供了多种嵌入存储的实现,开发者可以根据具体需求选择合适的存储方案。

11.1 EmbeddingStore 概述

11.1.1 EmbeddingStore 的作用

EmbeddingStore 的主要作用是存储和检索嵌入向量。这些向量可以单独存储,也可以与原始数据(如 TextSegment)一起存储。通过向量数据库,可以高效地执行以下操作:

  • 存储嵌入:将嵌入向量及其对应的原始数据存储在数据库中。
  • 搜索相似嵌入:通过向量相似性搜索,找到与给定向量最相似的嵌入。
  • 管理嵌入:添加、移除或更新嵌入向量。
11.1.2 支持的嵌入存储

LangChain4j 支持多种嵌入存储的实现,包括但不限于:

  • 内存存储(InMemoryEmbeddingStore):适用于开发和测试阶段,将嵌入存储在内存中。
  • Milvus:一个高性能的向量数据库,适用于大规模生产环境。
  • Pinecone:一个云原生的向量数据库,提供高效的相似性搜索。
  • PGVector:基于 PostgreSQL 的向量数据库扩展,适用于需要与关系数据库集成的场景。
  • Qdrant:一个高性能的向量数据库,支持多种索引类型。
11.1.3 嵌入存储的方法
  • EmbeddingStore 提供了以下方法:
  • add(Embedding):将给定的嵌入添加到存储中,并返回一个随机 ID。
  • add(String id, Embedding):将给定的嵌入及其指定的 ID 添加到存储中。
  • add(Embedding, TextSegment):将给定的嵌入及其关联的 TextSegment 添加到存储中,并返回一个随机 ID。
  • addAll(List):将给定的嵌入列表添加到存储中,并返回一个随机 ID 列表。
  • addAll(List, List):将给定的嵌入列表及其关联的 TextSegment 列表添加 到存储中,并返回一个随机 ID 列表。
  • addAll(List ids, List, List):将给定的嵌入列表及其关联的 ID 和 TextSegment 列表添加到存储中。
  • search(EmbeddingSearchRequest):搜索最相似的嵌入。
  • remove(String id):通过 ID 从存储中移除单个嵌入。
  • removeAll(Collection ids):通过 ID 从存储中移除多个嵌入。
  • removeAll(Filter):从存储中移除所有匹配指定过滤器的嵌入。
  • removeAll():从存储中移除所有嵌入。
11.1.4 示例代码

以下是一个简单的示例,展示如何使用 EmbeddingStore:

import dev.langchain4j.embedding.Embedding;
import dev.langchain4j.embedding.EmbeddingStore;
import dev.langchain4j.embedding.EmbeddingSearchRequest;
import dev.langchain4j.embedding.EmbeddingSearchResult;

// 创建一个嵌入存储实例
EmbeddingStore embeddingStore = new InMemoryEmbeddingStore<>();

// 创建嵌入向量
Embedding embedding1 = new Embedding(new double[]{0.1, 0.2, 0.3});
Embedding embedding2 = new Embedding(new double[]{0.2, 0.3, 0.4});

// 添加嵌入到存储中
String id1 = embeddingStore.add(embedding1);
String id2 = embeddingStore.add(embedding2);

// 添加嵌入及其关联的 TextSegment
TextSegment textSegment1 = TextSegment.from("This is a sample text.");
TextSegment textSegment2 = TextSegment.from("This is another sample text.");
embeddingStore.add(embedding1, textSegment1);
embeddingStore.add(embedding2, textSegment2);

// 搜索最相似的嵌入
EmbeddingSearchRequest request = new EmbeddingSearchRequest(embedding1, 5);
EmbeddingSearchResult result = embeddingStore.search(request);
List<Embedding> similarEmbeddings = result.getMatches();

// 移除嵌入
embeddingStore.remove(id1);
embeddingStore.removeAll(List.of(id2));

// 移除所有嵌入
embeddingStore.removeAll();
11.1.5 嵌入存储的应用

嵌入存储在多种应用场景中非常有用,例如:

  • 语义搜索:通过嵌入向量实现语义相似性搜索。
  • 推荐系统:通过嵌入向量推荐相似的项目。
  • 自然语言处理:用于情感分析、文本分类等任务。

11.2 EmbeddingSearchRequest(嵌入搜索请求)

EmbeddingSearchRequest 表示在 EmbeddingStore 中进行搜索的请求。它具有以下属性:

  • Embedding queryEmbedding:是一个嵌入向量,用作搜索的参考点。搜索时,嵌入存储会找到与这个参考向量最相似的嵌入向量。
  • int maxResults:返回的最大结果数。这是一个可选参数,默认值为 3。
  • double minScore:最小分数,范围从 0 到 1(包括 0 和 1)。只有分数大于或等于 minScore 的嵌入才会被返回。这是一个可选参数,默认值为 0。
  • Filter filter:在搜索过程中应用于元数据(Metadata)的过滤器。只有元数据与过滤器匹配的 TextSegment 会被返回。

11.3 Filter(过滤器)

Filter 是一个用于在执行向量搜索时过滤元数据条目的工具。目前支持以下过滤器类型/操作:

  • IsEqualTo:等于某个值。
  • IsNotEqualTo:不等于某个值。
  • IsGreaterThan:大于某个值。
  • IsGreaterThanOrEqualTo:大于或等于某个值。
  • IsLessThan:小于某个值。
  • IsLessThanOrEqualTo:小于或等于某个值。
  • IsIn:在某个集合中。
  • IsNotIn:不在某个集合中。
  • ContainsString:包含某个字符串。
  • And:逻辑与。
  • Not:逻辑非。
  • Or:逻辑或。

注意: 并非所有嵌入存储都支持元数据过滤。具体支持情况可以参考相关文档。 一些支持元数据过滤的存储可能不支持所有过滤器类型/操作。例如,ContainsString 目前仅被 Milvus、PgVector 和
Qdrant 支持。

11.4 EmbeddingSearchResult(嵌入搜索结果)

EmbeddingSearchResult 表示在 EmbeddingStore 中搜索的结果。它包含一个 EmbeddingMatch 列表。

11.5 EmbeddingMatch(嵌入匹配)

EmbeddingMatch 表示一个匹配的嵌入,包括其相关性分数、ID 和原始嵌入数据(通常是 TextSegment)。


11.6 示例代码

搜索与存储的简单示例
以下是一个简单的示例,展示如何使用 EmbeddingSearchRequest 和 EmbeddingStore 进行搜索:

import dev.langchain4j.embedding.Embedding;
import dev.langchain4j.embedding.EmbeddingStore;
import dev.langchain4j.embedding.EmbeddingSearchRequest;
import dev.langchain4j.embedding.EmbeddingSearchResult;
import dev.langchain4j.embedding.EmbeddingMatch;
import dev.langchain4j.filter.Filter;
import dev.langchain4j.filter.IsEqualTo;

// 创建一个嵌入存储实例
EmbeddingStore embeddingStore = new InMemoryEmbeddingStore<>();

// 创建嵌入向量
Embedding queryEmbedding = new Embedding(new double[]{0.1, 0.2, 0.3});

// 创建搜索请求
EmbeddingSearchRequest request = new EmbeddingSearchRequest(
    queryEmbedding, 
    5, // 最多返回 5 个结果
    0.75 // 最小相似度分数为 0.75
);

// 应用元数据过滤器
Filter filter = new IsEqualTo("source", "file1.txt");
request.setFilter(filter);

// 执行搜索
EmbeddingSearchResult result = embeddingStore.search(request);

// 处理搜索结果
List<EmbeddingMatch> matches = result.getMatches();
for (EmbeddingMatch match : matches) {
    System.out.println("Match ID: " + match.getId());
    System.out.println("Score: " + match.getScore());
    System.out.println("TextSegment: " + match.getTextSegment().text());
}

搜索结果与嵌入匹配示例

import dev.langchain4j.embedding.Embedding;
import dev.langchain4j.embedding.EmbeddingStore;
import dev.langchain4j.embedding.EmbeddingSearchRequest;
import dev.langchain4j.embedding.EmbeddingSearchResult;
import dev.langchain4j.embedding.EmbeddingMatch;
import dev.langchain4j.data.segment.TextSegment;

// 创建一个嵌入存储实例
EmbeddingStore embeddingStore = new InMemoryEmbeddingStore<>();

// 创建查询嵌入
Embedding queryEmbedding = new Embedding(new double[]{0.1, 0.2, 0.3});

// 创建搜索请求
EmbeddingSearchRequest request = new EmbeddingSearchRequest(queryEmbedding, 5, 0.75);

// 执行搜索
EmbeddingSearchResult result = embeddingStore.search(request);

// 处理搜索结果
List<EmbeddingMatch> matches = result.getMatches();
for (EmbeddingMatch match : matches) {
    System.out.println("Match ID: " + match.getId());
    System.out.println("Score: " + match.getScore());
    System.out.println("Embedded Data: " + match.getTextSegment().text());
}

12 EmbeddingStoreIngestor(嵌入存储摄取器)

EmbeddingStoreIngestor 是 LangChain4j 中的一个工具类,用于将文档摄取到嵌入存储中。它提供了灵活的配置选项,使得开发者可以根据需要对文档进行预处理、分割和嵌入

EmbeddingStoreIngestor 是 LangChain4j中的一个强大工具,用于将文档摄取到嵌入存储中。它支持多种可选功能,包括文档转换、分割和片段转换,使得开发者可以根据需要对文档进行预处理。通过合理配置EmbeddingStoreIngestor,可以显著提高嵌入存储的效率和质量。

1. 基本功能

EmbeddingStoreIngestor 的核心功能是将文档嵌入并存储到嵌入存储中。它支持以下操作:

  • 嵌入文档:使用指定的嵌入模型将文档转换为嵌入向量。
  • 存储嵌入:将嵌入向量及其对应的文档存储到嵌入存储中。

2. IngestionResult

IngestionResult 是 EmbeddingStoreIngestor 的 ingest() 方法的返回值,包含以下信息:

  • TokenUsage:显示嵌入过程中使用的令牌数量。
  • 其他信息:可能包含摄取过程中的其他有用信息。

示例代码如下:

EmbeddingStoreIngestor ingestor = EmbeddingStoreIngestor.builder()
        .embeddingModel(embeddingModel)
        .embeddingStore(embeddingStore)
        .build();

ingestor.ingest(document1);
ingestor.ingest(document2, document3);
IngestionResult ingestionResult = ingestor.ingest(List.of(document4, document5, document6));

3. 可选功能

EmbeddingStoreIngestor 提供了多种可选功能,用于在嵌入之前对文档进行预处理:

  • DocumentTransformer:用于清理、丰富或格式化文档。
  • DocumentSplitter:用于将文档分割成较小的 TextSegment,以提高相似性搜索的质量并减少发送给 LLM 的提示的大小和成本。
  • TextSegmentTransformer:用于清理、丰富或格式化 TextSegment。
EmbeddingStoreIngestor ingestor = EmbeddingStoreIngestor.builder()

    // 为每个文档添加 userId 元数据条目,以便后续可以根据它进行过滤
    .documentTransformer(document -> {
        document.metadata().put("userId", "12345");
        return document;
    })

    // 将每个文档分割成每个包含 1000 个令牌的 `TextSegment`,并有 200 个令牌的重叠
    .documentSplitter(DocumentSplitters.recursive(1000, 200, new OpenAiTokenizer()))

    // 为每个 `TextSegment` 添加文档名称,以提高搜索质量
    .textSegmentTransformer(textSegment -> TextSegment.from(
            textSegment.metadata("file_name") + "\n" + textSegment.text(),
            textSegment.metadata()
    ))

    .embeddingModel(embeddingModel)
    .embeddingStore(embeddingStore)
    .build();

4. 示例代码

以下是一个完整的示例,展示如何配置和使用 EmbeddingStoreIngestor:

import dev.langchain4j.data.document.Document;
import dev.langchain4j.data.segment.TextSegment;
import dev.langchain4j.embedding.EmbeddingModel;
import dev.langchain4j.embedding.EmbeddingStore;
import dev.langchain4j.embedding.EmbeddingStoreIngestor;
import dev.langchain4j.embedding.IngestionResult;
import dev.langchain4j.splitter.DocumentSplitter;
import dev.langchain4j.splitter.DocumentSplitters;
import dev.langchain4j.tokenizer.OpenAiTokenizer;

// 创建嵌入模型和嵌入存储实例
EmbeddingModel embeddingModel = ...; // 使用 OpenAI 或其他嵌入模型
EmbeddingStore embeddingStore = new InMemoryEmbeddingStore<>();

// 创建文档摄取器
EmbeddingStoreIngestor ingestor = EmbeddingStoreIngestor.builder()
    // 添加 userId 元数据条目
    .documentTransformer(document -> {
        document.metadata().put("userId", "12345");
        return document;
    })
    // 将文档分割成每个包含 1000 个令牌的 TextSegment,重叠 200 个令牌
    .documentSplitter(DocumentSplitters.recursive(1000, 200, new OpenAiTokenizer()))
    // 为每个 TextSegment 添加文档名称
    .textSegmentTransformer(textSegment -> TextSegment.from(
        textSegment.metadata("file_name") + "\n" + textSegment.text(),
        textSegment.metadata()
    ))
    .embeddingModel(embeddingModel)
    .embeddingStore(embeddingStore)
    .build();

// 加载文档
Document document1 = ...; // 加载文档
Document document2 = ...;
Document document3 = ...;

// 摄取文档
ingestor.ingest(document1);
ingestor.ingest(document2, document3);

// 批量摄取文档
List<Document> documents = List.of(document1, document2, document3);
IngestionResult ingestionResult = ingestor.ingest(documents);

// 查看摄取结果
System.out.println("Tokens used: " + ingestionResult.getTokenUsage());

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/973633.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

零基础学python--------第三节:Python的流程控制语法

Python&#xff0c;浮点数 11.345(单&#xff1a;4个字节&#xff0c; 双&#xff1a;8个字节) 。 十进制的数字25 ---> 11001 讲一个小数转化为二进制&#xff1a; 不断的乘以2 。取整数部分。 十进制的0.625 ----> 二进制&#xff1a; 0&#xff0c; 101 。 0.3 ---…

MKS SERVO42E57E 闭环步进电机_系列10 STM32_脉冲和串口例程

文章目录 第1部分 产品介绍第2部分 相关资料下载2.1 MKS E系列闭环步进驱动资料2.2 源代码下载2.3 上位机下载 第3部分 脉冲控制电机运行示例第4部分 读取参数示例4.1 读取电机实时位置4.2 读取电机实时转速4.3 读取电机输入脉冲数4.4 读取电机位置误差4.5 读取电机IO端口状态 …

小米路由器 AX3000T 降级后无法正常使用,解决办法

问题描述 买了个 AX3000T 路由器&#xff0c;想安装 OpenWRT 或者 安装 Clash 使用&#xff0c;看教程说是需要降级到 v1.0.47 版本。 结果刷机之后路由器无法打开了&#xff0c;一直黄灯亮&#xff0c;中间灭一下&#xff0c;又是黄灯长亮&#xff0c;没有 WIFI 没有连接。以…

强化学习-GAE方法

2016-ICLR-HIGH-DIMENSIONAL CONTINUOUS CONTROL USING GENERALIZED ADVANTAGE ESTIMATION 解决问题 强化学习的目标为最大化策略的预期总回报&#xff0c;其中一个主要困难为 行为对reward的影响存在一个长时间的延迟&#xff08;credit assignment problem&#xff09;。价…

写大论文的word版本格式整理,实现自动生成目录、参考文献序号、公式序号、图表序号

前情提要&#xff1a;最近开始写大论文&#xff0c;发现由于内容很多导致用老方法一个一个改的话超级麻烦&#xff0c;需要批量自动化处理&#xff0c;尤其是序号&#xff0c;在不断有增添删减的情况时序号手动调整很慢也容易出错&#xff0c;所以搞一个格式总结&#xff0c;记…

清华大学deepseek教程第四版 DeepSeek+DeepResearch 让科研像聊天一样简单(附下载)

deepseek使用教程系列 DeepSeekDeepResearch 让科研像聊天一样简单(附下载) https://pan.baidu.com/s/1VMgRmCSEzNvhLZQc8mu6iQ?pwd1234 提取码: 1234 或 https://pan.quark.cn/s/f3d4511b790a

面阵工业相机提高餐饮业生产效率

餐饮行业是一个快节奏、高要求的领域&#xff0c;该领域对生产过程中每一个阶段的效率和准确性都有很高的要求。在食品加工、包装、质量控制和库存管理等不同生产阶段实现生产效率的优化是取得成功的关键步骤。面阵工业相机能够一次性捕捉对象的二维区域图像&#xff0c;并支持…

Linux基础开发工具的使用(apt、vim、gcc、g++、gdb、make、makefile)

Linux软件包管理器–apt Linux安装软件的方式 在Linux下安装软件的方法有以下三种&#xff1a; 下载到程序的源代码&#xff0c;自己编译出可执行程序获取deb安装包、然后使用dpkg命令安装。&#xff08;不解决依赖关系&#xff09;通过apt进行安装软件。 小知识点&#xf…

【vue项目如何利用event-stream实现文字流式输出效果】

引言 在现代 Web 应用中&#xff0c;实时数据展示是一个常见需求&#xff0c;例如聊天消息逐字显示、日志实时推送、股票行情更新等。传统的轮询或一次性数据加载方式无法满足这类场景的流畅体验&#xff0c;而 流式传输&#xff08;Streaming&#xff09; 技术则能实现数据的…

算法——Boyer-Moore算法

引言 在字符串匹配算法中&#xff0c;Boyer-Moore算法以其高效性和巧妙的设计而著称。它广泛用于文本搜索、编译器词法分析、信息检索等领域。本文将详细解读Boyer-Moore算法的原理、步骤&#xff0c;并通过实践案例展示其应用。 Boyer-Moore算法简介 Boyer-Moore算法是一种…

智能网络感知,打造极致流畅的鸿蒙原生版中国移动云盘图文体验

背景 中国移动云盘&#xff08;原“和彩云网盘”&#xff09;是中国移动重磅推出的安全、智能、不限速、移动用户免流的智能云盘&#xff0c;致力于成为5G时代用户个人与家庭的数字资产管理中心&#xff0c;是中国移动继语音、短信、流量后的“第四项基础服务”。 照片、音视…

Windows 快速搭建C++开发环境,安装C++、CMake、QT、Visual Studio、Setup Factory

安装C 简介 Windows 版的 GCC 有三个选择&#xff1a; CygwinMinGWmingw-w64 Cygwin、MinGW 和 mingw-w64 都是在 Windows 操作系统上运行的工具集&#xff0c;用于在 Windows 环境下进行开发和编译。 Cygwin 是一个在 Windows 上运行的开源项目&#xff0c;旨在提供类Uni…

VS Code 如何搭建C/C++开发环境

目录 1.VS Code是什么 2. VS Code的下载和安装 2.1 下载和安装 2.2.1 下载 2.2.2 安装 2.2 环境的介绍 2.3 安装中文插件 3. VS Code配置C/C开发环境 3.1 下载和配置MinGW-w64编译器套件 3.1.1 下载 3.1.2 配置 3.2 安装C/C插件 3.3 重启VSCode 4. 在VSCode上编写…

2024年国赛高教杯数学建模A题板凳龙闹元宵解题全过程文档及程序

2024年国赛高教杯数学建模 A题 板凳龙闹元宵 原题再现 “板凳龙”&#xff0c;又称“盘龙”&#xff0c;是浙闽地区的传统地方民俗文化活动。人们将少则几十条&#xff0c;多则上百条的板凳首尾相连&#xff0c;形成蜿蜒曲折的板凳龙。盘龙时&#xff0c;龙头在前领头&#x…

详解同为科技桌面PDU系列产品特点

同为科技的桌面PDU系列产品是依据自身在电气联接领域25年专业积累并精心设计&#xff0c;产品采用模块化结构&#xff0c;实现各种功能、输出插口、输入方式可根据用户需求以模块组合的方式构建定制化产品。 桌面PDU产品特点 工业级材质和结构设计 桌面PDU系列产品采用一体成…

【排版教程】如何在Word/WPS中优雅的插入参考文献

材料展示 随便选取一段综述内容&#xff0c;以及对应的参考文献&#xff0c;如下图所示&#xff1a; 1 参考文献编辑 首先对参考文献部分进行编辑&#xff0c;将其设置自动编号 在段落中&#xff0c;选择悬挂缩进 在编号中&#xff0c;设置自定义编号&#xff0c;然后按照…

STM32 看门狗

目录 背景 独立看门狗&#xff08;IWDG&#xff09; 寄存器访问保护 窗口看门狗&#xff08;WWDG&#xff09; 程序 独立看门狗 设置独立看门狗程序 第一步、使能对独立看门狗寄存器的写操作 第二步、设置预分频和重装载值 第三步、喂狗 第四步、使能独立看门狗 喂狗…

【第二节】C++设计模式(创建型模式)-抽象工厂模式

目录 引言 一、抽象工厂模式概述 二、抽象工厂模式的应用 三、抽象工厂模式的适用场景 四、抽象工厂模式的优缺点 五、总结 引言 抽象工厂设计模式是一种创建型设计模式&#xff0c;旨在解决一系列相互依赖对象的创建问题。它与工厂方法模式密切相关&#xff0c;但在应用…

docker基操

docker基操 首先就是安装docker使用docker:创建容器-制作一个镜像-加载镜像首先就是安装docker 随便找一个教程安装就可以,安装过程中主要是不能访问谷歌,下面这篇文章写了镜像的一些问题: 安装docker的网络问题 使用docker:创建容器-制作一个镜像-加载镜像 主要是参考:这篇…

3D打印注塑件-省模具费90%的解决方案

"开模费用50万&#xff0c;首批订单才200件&#xff1f;" 这是许多制造企业的真实困境。传统注塑工艺动辄数周的开模周期和5-50万元的模具成本&#xff0c;让中小企业的产品迭代举步维艰。 在传统制造流程中&#xff0c;注塑件的生产往往需要高昂的模具开发费用和较…