Springboot 整合 Java DL4J 构建自然语言处理之机器翻译系统

🧑 博主简介:CSDN博客专家历代文学网(PC端可以访问:https://literature.sinhy.com/#/literature?__c=1000,移动端可微信小程序搜索“历代文学”)总架构师,15年工作经验,精通Java编程高并发设计Springboot和微服务,熟悉LinuxESXI虚拟化以及云原生Docker和K8s,热衷于探索科技的边界,并将理论知识转化为实际应用。保持对新技术的好奇心,乐于分享所学,希望通过我的实践经历和见解,启发他人的创新思维。在这里,我希望能与志同道合的朋友交流探讨,共同进步,一起在技术的世界里不断学习成长。

在这里插入图片描述


在这里插入图片描述

Springboot 整合 Java DL4J 构建自然语言处理之机器翻译系统

一、引言

在当今全球化的时代,不同语言之间的交流日益频繁,机器翻译系统的重要性不言而喻。传统的机器翻译方法往往存在准确性流畅性方面的局限,而随着深度学习技术的发展,我们有机会构建更为高效准确机器翻译系统

机器翻译旨在让计算机自动将一种自然语言文本转换为另一种自然语言文本。 早期的基于规则和统计的机器翻译方法,需要人工构建大量的语法规则和词库,并且在处理复杂语义和上下文时表现不佳。深度学习技术的出现为机器翻译带来了新的思路。通过构建神经网络模型,可以自动从大量的语料数据中学习语言的特征和模式,从而提高翻译的准确性和流畅性

在这个项目中,我们将使用Spring Boot框架整合Java Deeplearning4j库来构建一个多语言翻译软件。Spring Boot能够为我们提供便捷的项目构建和管理方式,而Deeplearning4j则是一个专门为Java编写的深度学习库,它提供了丰富的工具和算法来构建神经网络模型。这个机器翻译系统将能够实现不同语言之间的自动翻译,为多语言交流提供有力的支持。

二、技术概述

(一)Spring Boot

Spring Boot 是一个用于快速构建独立、生产级别的基于 Spring 的应用程序的框架。它简化了 Spring 应用程序的开发过程,提供了自动配置、起步依赖等功能,使得开发者可以更加专注于业务逻辑的实现。在本项目中,Spring Boot 将用于构建后端服务,提供 RESTful API 接口,接收用户的翻译请求,并返回翻译结果。

(二)Deeplearning4j

Deeplearning4j 是一个基于 Java 和 Scala 的深度学习库,支持多种神经网络架构,如深度神经网络(DNN)、卷积神经网络(CNN)、循环神经网络(RNN)等。它提供了丰富的 API 和工具,方便开发者进行深度学习模型的训练和部署。在本项目中,Deeplearning4j 将用于构建机器翻译模型,实现语言的自动翻译。

(三)自然语言处理

自然语言处理(NLP)是计算机科学领域与人工智能领域中的一个重要方向,它研究能实现人与计算机之间用自然语言进行有效通信的各种理论和方法。在本项目中,自然语言处理技术将用于对输入的文本进行预处理,包括分词、词性标注、命名实体识别等,为机器翻译模型提供更加准确的输入数据。

(四)机器翻译

机器翻译是指利用计算机将一种自然语言翻译成另一种自然语言的技术。它可以分为基于规则的机器翻译、基于统计的机器翻译和基于神经网络的机器翻译等不同类型。在本项目中,我们将采用基于神经网络的机器翻译技术,利用深度学习模型自动学习语言之间的翻译规则,提高翻译的准确性和流畅性。

三、神经网络选择及理由

(一)选择的神经网络

在本项目中,我们将选择循环神经网络(RNN)中的长短期记忆网络(LSTM)作为机器翻译模型的基础架构。

循环神经网络(RNN) 是一种专门用于处理序列数据的神经网络。在机器翻译中,源语言和目标语言的文本都是序列数据。RNN的特点是它的神经元之间有循环连接,使得它能够处理序列中的长期依赖关系。

然而,传统的RNN存在梯度消失或梯度爆炸的问题,当处理较长的序列时,很难有效地学习到序列中的长期依赖关系。

长短期记忆网络LSTM)和门控循环单元GRU)是RNN的变体,它们通过引入门控机制来解决梯度消失和梯度爆炸的问题。

LSTM有三个门输入门遗忘门输出门。输入门决定了新的信息如何进入细胞状态,遗忘门决定了细胞状态中哪些信息应该被遗忘,输出门决定了细胞状态中的哪些信息应该被输出。GRU则是将遗忘门和输入门合并成一个更新门,简化了结构,但同样能够有效地处理序列中的长期依赖关系。

(二)选择理由

  1. 处理序列数据:机器翻译的输入和输出都是序列数据,即一段文本。RNN 能够有效地处理序列数据,通过记忆历史信息来预测下一个单词或字符。LSTM 是一种改进的 RNN 架构,它能够更好地处理长期依赖关系,避免了传统 RNN 中存在的梯度消失和梯度爆炸问题。
  2. 提高翻译准确性LSTM 能够自动学习语言之间的翻译规则,通过大量的训练数据不断优化模型参数,提高翻译的准确性。与基于规则的机器翻译和基于统计的机器翻译相比,基于神经网络的机器翻译具有更高的翻译质量和更好的泛化能力。
  3. 适应不同语言LSTM 可以适应不同的语言,只需要提供相应的训练数据即可。它不需要手动设计翻译规则,能够自动学习语言的特征和规律,适用于多种语言之间的翻译任务。

四、数据集格式

(一)数据集来源

我们可以使用公开的机器翻译数据集,如 WMT(Workshop on Machine Translation)数据集、OpenSubtitles 数据集等。这些数据集包含了大量的双语平行语料,可以用于训练机器翻译模型。

(二)数据集格式

数据集通常以文本文件的形式存储,每行包含一个源语言句子和对应的目标语言句子,中间用制表符或空格分隔。例如:

Hello world! 你好,世界!
How are you? 你好吗?

在处理数据集时,我们需要将文本进行预处理,包括分词、编码等操作,以便将其输入到神经网络模型中进行训练。

(三)样例表格

源语言句子目标语言句子
I love you.我爱你。
Good morning!早上好!
Thank you.谢谢。

五、技术实现

(一)Maven 依赖

在项目的 pom.xml 文件中,我们需要添加以下 Maven 依赖:

<dependency><groupId>org.deeplearning4j</groupId><artifactId>deeplearning4j-core</artifactId><version>1.0.0-beta7</version>
</dependency>
<dependency><groupId>org.deeplearning4j</groupId><artifactId>deeplearning4j-nn</artifactId><version>1.0.0-beta7</version>
</dependency>
<dependency><groupId>org.deeplearning4j</groupId><artifactId>deeplearning4j-ui</artifactId><version>1.0.0-beta7</version>
</dependency>
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId>
</dependency>

(二)数据预处理

  1. 分词

    • 使用自然语言处理工具包(如 Stanford NLP、NLTK 等)对源语言和目标语言句子进行分词处理,将句子分割成单词或字符序列。
    • 例如,对于句子“I love you.”,经过分词处理后得到的序列为[“I”, “love”, “you”]。
  2. 编码

    • 将分词后的单词或字符序列进行编码,将其转换为数字表示。可以使用词向量模型(如 Word2Vec、GloVe 等)将单词转换为向量表示,或者使用字符编码(如 ASCII 编码、UTF-8 编码等)将字符转换为数字表示。
    • 例如,对于单词“I”,可以使用词向量模型将其转换为一个向量表示,或者使用字符编码将其转换为数字 73(ASCII 编码中“I”的对应值)。
  3. 填充和截断

    • 由于不同的句子长度可能不同,为了便于输入到神经网络模型中进行训练,我们需要对句子进行填充和截断处理,使得所有的句子长度相同。
    • 可以使用填充字符(如“PAD”)对长度不足的句子进行填充,使用截断操作对长度过长的句子进行截断。
    • 例如,对于句子序列[“I love you.”, “Good morning!”],经过填充和截断处理后得到的序列为[“I love you. PAD PAD PAD”, “Good morning! PAD PAD PAD”]。

(三)构建神经网络模型

  1. 定义模型架构
    • 使用 Deeplearning4j 提供的 API 定义 LSTM 模型的架构。可以设置模型的层数、隐藏单元数量、输入维度、输出维度等参数。
    • 例如,以下代码定义了一个两层的 LSTM 模型,每层包含 128 个隐藏单元,输入维度为 100,输出维度为 200:
MultiLayerConfiguration conf = new NeuralNetConfiguration.Builder().seed(12345).updater(new Nesterovs(0.01, 0.9)).list().layer(0, new GravesLSTM.Builder().nIn(100).nOut(128).activation(Activation.TANH).build()).layer(1, new GravesLSTM.Builder().nIn(128).nOut(128).activation(Activation.TANH).build()).layer(2, new RnnOutputLayer.Builder(LossFunctions.LossFunction.MCXENT).activation(Activation.SOFTMAX).nIn(128).nOut(200).build()).pretrain(false).backprop(true).build();
  1. 初始化模型
    • 使用定义好的模型架构创建一个神经网络模型,并进行初始化操作。可以使用随机初始化或预训练的模型参数进行初始化。
    • 例如,以下代码创建了一个基于上述模型架构的神经网络模型,并使用随机初始化进行初始化:
MultiLayerNetwork model = new MultiLayerNetwork(conf);
model.init();
  1. 配置训练参数
    • 设置模型的训练参数,如学习率、批次大小、训练轮数等。可以根据数据集的大小和模型的复杂度进行调整。
    • 例如,以下代码设置了模型的学习率为 0.01,批次大小为 32,训练轮数为 100:
model.setLearningRate(0.01);
model.setBatchSize(32);
model.setNumEpochs(100);

(四)模型训练

  1. 加载数据集
    • 使用数据加载器(如 CSVRecordReader、TextLineRecordReader 等)加载预处理后的数据集。可以将数据集分为训练集、验证集和测试集,用于模型的训练、评估和测试。
    • 例如,以下代码使用 CSVRecordReader 加载一个包含源语言和目标语言句子的 CSV 文件,并将其分为训练集和测试集:
RecordReader recordReader = new CSVRecordReader(0, ',');
recordReader.initialize(new FileSplit(new File("data.csv")));
int numExamples = recordReader.totalOutcomes();
int trainSize = (int) (numExamples * 0.8);
int testSize = numExamples - trainSize;
DataSetIterator trainIterator = new RecordReaderDataSetIterator(recordReader, trainSize, 100, 200, true);
DataSetIterator testIterator = new RecordReaderDataSetIterator(recordReader, testSize, 100, 200, false);
  1. 训练模型
    • 使用训练数据集对模型进行训练,通过反向传播算法不断优化模型参数,提高模型的性能。可以使用训练过程中的验证集来评估模型的性能,并根据评估结果调整训练参数。
    • 例如,以下代码使用训练数据集对模型进行训练,并在每一轮训练结束后使用验证集进行评估:
for (int epoch = 0; epoch < model.getNumEpochs(); epoch++) {model.fit(trainIterator);Evaluation eval = model.evaluate(testIterator);System.out.println("Epoch " + epoch + ", accuracy: " + eval.accuracy());
}

(五)模型预测

  1. 加载模型
    • 在进行模型预测之前,需要先加载已经训练好的模型。可以使用模型保存和加载工具(如 ModelSerializer、HDF5ModelSaver 等)将模型保存到文件中,然后在需要的时候加载模型进行预测。
    • 例如,以下代码加载一个已经保存到文件中的模型:
MultiLayerNetwork model = ModelSerializer.restoreMultiLayerNetwork("model.h5");
  1. 进行预测
    • 使用加载的模型对输入的源语言句子进行翻译预测。将源语言句子进行预处理后,输入到模型中进行预测,得到目标语言句子的预测结果。
    • 例如,以下代码使用加载的模型对输入的源语言句子进行翻译预测:
String sourceSentence = "I love you.";
INDArray input = preprocessSentence(sourceSentence);
INDArray output = model.output(input);
String targetSentence = postprocessOutput(output);
System.out.println("Translation: " + targetSentence);

(六)接口实现

  1. 创建 RESTful API 接口
    • 使用 Spring Boot 提供的 API 创建一个 RESTful API 接口,接收用户的翻译请求,并返回翻译结果。可以使用@RestController注解定义一个控制器类,使用@RequestMapping注解定义接口的 URL 路径和请求方法。
    • 例如,以下代码创建了一个名为TranslationController的控制器类,其中包含一个名为translate的方法,用于接收用户的翻译请求,并返回翻译结果:
@RestController
@RequestMapping("/api/translation")
public class TranslationController {@Autowiredprivate MultiLayerNetwork model;@RequestMapping(value = "/{sourceLanguage}/{targetLanguage}/{sourceSentence}", method = RequestMethod.GET)public String translate(@PathVariable String sourceLanguage, @PathVariable String targetLanguage, @PathVariable String sourceSentence) {// 进行翻译预测String targetSentence = performTranslation(sourceLanguage, targetLanguage, sourceSentence);return targetSentence;}private String performTranslation(String sourceLanguage, String targetLanguage, String sourceSentence) {// 进行数据预处理INDArray input = preprocessSentence(sourceSentence);// 进行模型预测INDArray output = model.output(input);// 进行后处理String targetSentence = postprocessOutput(output);return targetSentence;}
}
  1. 测试接口
    • 使用 Postman 或其他 API 测试工具对创建的 RESTful API 接口进行测试,验证接口的功能和性能。可以发送不同的翻译请求,检查返回的翻译结果是否正确。

六、单元测试

(一)测试数据准备

  1. 准备一些源语言和目标语言的句子对,用于测试模型的翻译功能。可以从公开的数据集或自己创建的测试数据集中选择一些句子对。
  2. 将测试数据存储在一个文本文件或数据库中,以便在测试过程中进行读取和使用。

(二)测试用例编写

  1. 使用 JUnit 或其他测试框架编写测试用例,对模型的翻译功能进行测试。可以创建一个测试类,在其中定义多个测试方法,每个测试方法对应一个测试用例。
  2. 在测试方法中,使用模型对源语言句子进行翻译预测,然后将预测结果与目标语言句子进行比较,判断翻译是否正确。可以使用断言语句(如assertEqualsassertTrue等)来验证测试结果。
  3. 例如,以下代码是一个使用 JUnit 编写的测试用例,用于测试模型的翻译功能:
import org.deeplearning4j.nn.multilayer.MultiLayerNetwork;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.mockito.Mockito;import static org.junit.jupiter.api.Assertions.assertEquals;public class TranslationServiceTest {private MultiLayerNetwork model;private TranslationService translationService;@BeforeEachpublic void setUp() {model = Mockito.mock(MultiLayerNetwork.class);translationService = new TranslationService(model);}@Testpublic void testTranslateEnglishToChinese() {String sourceSentence = "I love you.";String targetSentence = "我爱你。";Mockito.when(model.output(Mockito.any())).thenReturn(generateOutputForChineseSentence());String translatedSentence = translationService.translate("en", "zh", sourceSentence);assertEquals(targetSentence, translatedSentence);}@Testpublic void testTranslateChineseToEnglish() {String sourceSentence = "我爱你。";String targetSentence = "I love you.";Mockito.when(model.output(Mockito.any())).thenReturn(generateOutputForEnglishSentence());String translatedSentence = translationService.translate("zh", "en", sourceSentence);assertEquals(targetSentence, translatedSentence);}private INDArray generateOutputForEnglishSentence() {// 生成模拟的英文句子的输出return null;}private INDArray generateOutputForChineseSentence() {// 生成模拟的中文句子的输出return null;}
}

(三)预期输出

  1. 对于每个测试用例,预期输出应该是正确的翻译结果。如果模型的翻译功能正常,那么测试用例应该通过,即断言语句应该成功。
  2. 如果模型的翻译功能出现问题,那么测试用例应该失败,即断言语句应该抛出异常。可以根据测试结果进行调试和优化,直到模型的翻译功能达到预期要求。

七、总结

本文介绍了如何使用 Spring Boot 整合 Deeplearning4j 在自然语言处理领域实现一个机器翻译系统。通过选择合适的神经网络架构(LSTM)、处理数据集、构建和训练模型、实现接口以及进行单元测试等步骤,我们成功地实现了一个能够自动翻译不同语言的系统。这个系统具有较高的翻译准确性和流畅性,可以为用户提供更加高效、准确的翻译服务。在实际应用中,我们可以根据具体的需求对系统进行进一步的优化和扩展,如增加更多的语言支持、提高模型的性能等。

八、参考资料文献

  1. Deeplearning4j 官方文档
  2. Spring Boot 官方文档
  3. 自然语言处理入门
  4. 深度学习入门:基于 Python 的理论与实践

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

NLP算法工程师精进之路:顶会论文研读精华

1.学术能力培养 全部论文资料下载&#xff1a; 将论文和 GitHub 资源库匹配 papers with code https://paperswithcode.com/OpenGitHub 新项目快报Github pwc&#xff1a;https://github.com/zziz/pwc GitXiv&#xff1a;http://www.gitxiv.com/ 文章撰写 Overleaf [Autho…

【C++动态规划 01背包】2787. 将一个数字表示成幂的和的方案数

本文涉及知识点 C动态规划 C背包问题 LeetCode2787. 将一个数字表示成幂的和的方案数 给你两个 正 整数 n 和 x 。 请你返回将 n 表示成一些 互不相同 正整数的 x 次幂之和的方案数。换句话说&#xff0c;你需要返回互不相同整数 [n1, n2, …, nk] 的集合数目&#xff0c;满…

vue下载安装

目录 vue工具前置要求&#xff1a;安装node.js并配置好国内镜像源下载安装 vue 工具 系统&#xff1a;Windows 11 前置要求&#xff1a;安装node.js并配置好国内镜像源 参考&#xff1a;本人写的《node.js下载、安装、设置国内镜像源&#xff08;永久&#xff09;&#xff…

Spring整合Mybatis过程

配置文件 springConfig --> [jdbcConfig mybatisConfig] jdbc配置文件进行基本的数据库连接池配置 mybatis配置文件进行SqlSessionFactory Bean 和 MapperScannerConfigurer Bean的创建 在Spring容器启动时&#xff0c;系统会根据配置创建并初始化所有MyBatis所需的Bean…

Kafka 客户端工具使用分享【offsetexplorer】

前言&#xff1a; 前面我们使用 Spring Boot 继承 Kafka 完成了消息发送&#xff0c;有朋友会问 Kafka 有没有好用的客户端工具&#xff0c;RabbitMQ、RocketMQ 都有自己的管理端&#xff0c;那 Kafka 如何去查看发送出去的消息呢&#xff1f; 本篇我们就来分享一个好用的工具…

ctfshow(151->154)--文件上传漏洞--.user.ini

Web151 进入界面&#xff1a; 审计&#xff1a; 提示是前台校验。 存在图片上传。 思路&#xff1a; 先编写一个一句话木马文件&#xff1a; //shell.php <?php eval($_POST[1]); ?>既然是前端校验&#xff0c;我们查看页面源代码找到相关的校验内容&#xff1a…

Ubuntu使用Tesla P4配置Anaconda+CUDA+PyTorch

我们之前测试了在Windows系统如何安装Tesla M4&#xff08;成了&#xff01;Tesla M4Windows 10AnacondaCUDA 11.8cuDNNPython 3.11&#xff09;&#xff0c;前面安装好了Ubuntu 22.04.4的操作系统&#xff08;Ubuntu 22.04.4安装Docker引擎&#xff09;。今天&#xff0c;简单…

少儿编程参培意愿地图:一二线城市热情高涨,低线城市市场待挖掘

随着少儿编程的普及&#xff0c;编程教育逐渐走进越来越多家庭。然而&#xff0c;少儿编程的地域分布显示出明显的差异&#xff1a;在一二线城市中&#xff0c;家长对少儿编程的接受度和参与度显著高于低线城市。本文将通过对地域分布和家长态度的分析&#xff0c;探讨少儿编程…

基于SSM演出道具租赁系统的设计

管理员账户功能包括&#xff1a;系统首页&#xff0c;个人中心&#xff0c;用户管理&#xff0c;商家管理&#xff0c;道具类型管理&#xff0c;道具出租管理&#xff0c;租赁订单管理&#xff0c;道具归还管理&#xff0c;系统管理 商家账号功能包括&#xff1a;系统首页&…

【Spring】Spring 核心和设计思想

Spring 核心和设计思想 1.什么是 Spring1.1 传统程序开发1.2 控制反转程序开发 2.理解 Spring IoC 1.什么是 Spring 我们通常所说的 Spring 指的是 Spring Framework&#xff08;Spring 框架&#xff09;&#xff0c;它是⼀个开源框架&#xff0c;有着活跃而庞大的社区&#x…

【C语言学习笔记】

C语言发展史&#xff1a; 1960 原型A语言->ALGOL语言 1963 CPL语言1967 BCPL1970 B语言1973 C语言 C语言特点&#xff1a; 基础性语言语法简洁 紧凑 方便 灵活(得益于指针)运算符 数据结构丰富结构化 模块化编程移植性好 执行效率…

STL学习-无序容器-unordered set和unorderde multiset

1.定义及初始化 #include <unordered set> #include <iostream> using namespace std; //输出s中的所有元素 template<typename T> void Show(const T& s) { for(auto&x:s) cout << x<<" ";cout << endl; } int main()…

玩转Docker | Docker基础入门与常用命令指南

玩转Docker | Docker基础入门与常用命令指南 引言基本概念help帮助信息常用命令管理镜像运行容器构建镜像其他Docker命令整理结语引言 Docker 是一种开源的应用容器引擎,它允许开发者将应用程序及其依赖打包进一个可移植的容器中,然后发布到任何流行的 Linux 机器上。这大大简…

基于SSM医药进出口交易系统的设计

管理员账户功能包括&#xff1a;系统首页&#xff0c;个人中心&#xff0c;商品信息管理&#xff0c;仓储部门管理&#xff0c;供应部门管理&#xff0c;业务部门管理&#xff0c;客户管理&#xff0c;财务部管理 业务部门账号功能包括&#xff1a;系统首页&#xff0c;个人中…

2024年大湾区杯粤港澳金融数学建模赛题浅析——助攻快速选题

一图流 赛题难度 A:B2:1 选题人数 A:B2:3 A题&#xff1a;证券市场投资风险控制模型设计 问题简述 随着金融市场的发展&#xff0c;系统性风险的管理变得越来越重要。本题要求通过量化方法测度和监测系统性风险&#xff0c;设计风险计量指标&#xff0c;并基于这些指标构建预…

胡壮麟《语言学教程》第五版PDF英文版+中文版翻译

胡壮麟《语言学教程》中文版&#xff1a;https://pan.quark.cn/s/9491130ec572 《语言学教程》&#xff08;英文版&#xff09;是一部经典的语言学教材&#xff0c;自 1988 年面世以来&#xff0c;被众多高校广泛采用&#xff0c;长销不衰。该教材自出版以来不断修订&#xff…

基于Pycharm和Django模型技术的数据迁移

1.配置数据库 在trip_server/settings.py中修改配置&#xff1a; 其格式可访问官网&#xff1a;Settings | Django documentation | Django 1.1 配置数据库 文件地址&#xff1a;trip_server/settings.py 配置前需要创建&#xff08;NaviCat&#xff09;个人数据库 "…

java访问华为网管软件iMaster NCE的北向接口

最近做的一个项目&#xff0c;需要读取华为一个叫iMaster NCE的网管软件的北向接口。这个iMaster NCE&#xff08;以下简称NCE&#xff09;用于管理项目的整个网络&#xff0c;尤其是光网络。业主要求我们访问该软件提供的对外接口&#xff0c;读取一些网络信息&#xff0c;比如…

InstructIR: High-Quality Image Restoration Following Human Instructions 论文阅读笔记

这是Radu大佬所在的Wrzburg大学的computer vision lab实验室发表在ECCV2024上的一篇论文&#xff0c;代码开源。文章提出了一种文本引导的All-in-One的restoration模型&#xff0c;如下图所示&#xff1a; 这个工作其实跟"InstructPix2Pix: Learning to Follow Image Edit…

【实用教程】.NET C# PDF 生成技术:哪种方法适合您的项目?

概述&#xff1a;本文介绍了 TX Text Control 真正的 WYSIWYG&#xff08;所见即所得&#xff09;模板系统所带来的独特优势&#xff0c;与传统的 PDF 生成系统&#xff08;如 HTML 到 PDF 转换器或需要自定义编程的 PDF 库&#xff09;相比&#xff0c;模板是可视化设计的&…