潇大

Mar 03, 2024

Video2Article: 要点匹配与纵横交错(3)

上一篇文章中,我们提到可以将整段的文本转换成带有大纲的要点格式,但是并没有讲怎么将这个生成的大纲“合并”到原来的文段上,这一篇中我就来阐述一下其中的技术细节。整体上这个任务会拆解为3个小任务:
  1. 解析带大纲的 Markdown 语法树
  1. 使用嵌入模型计算要点和正文段落的相似度
  1. 参考 LCS 算法实现要点和段落匹配
后面分节依次讲解。

1. 解析 Markdown 语法树

Markdown 是一种较为常见、方便书写的文本格式,同时也是上一阶段LLM任务的输出格式。这里就需要提取出它的标题信息
标题层级、以及每个标题下的文本要点。
直接写匹配的代码会比较费时,于是我就在网上寻找是否有类似的 Python 库,找到了不少,重点列几个。
首先在 Stackoverflow 上找到一个类似的 【parse and traverse】 需求 python - Parse and traverse elements from a Markdown file - Stack Overflow,其高赞的回答引起了我的兴趣,因为他说并不推荐 Python-Markdown 并且他自己还是 Python-Markdown 的作者。如此的坦诚让我对它的回答产生了兴趣。
库名
项目地址
Python-Markdown
Mistune
Mistletoe

1.1 解析成 AST

我顺着这个回答找到了 Mistune 这个库,并翻到了它的教程指引。总统来说,它的作用是将一段 markdown 文本渲染为 html 格式,也可以通过插件的方式,提供更多渲染功能。然而,Mistune 还提供了一种解析 AST (Abstract Syntax Tree) 的方式:
import mistune markdown = mistune.create_markdown(renderer='ast') text = 'hello **world**' markdown(text)
通过传入 ast,就可以将示例文本解析如下:
# ==> [ { 'type': 'paragraph', 'children': [ {'type': 'text', 'raw': 'hello '}, {'type': 'strong', 'children': [{'type': 'text', 'raw': 'world'}]} ] } ]
这个基本上就是我想要的功能了,通过读取 AST 树,就可以了解到其构成,获取想要的内容。

1.2 从 AST 还原

虽然解析的问题解决了,但是此刻我依旧不知道如何从一个修改过的 AST 还原成 markdown,因为最终这个任务需要生成图文,那自然 markdown 格式是最方便的。
于是我顺着那个老哥的推荐继续向下,找到了 Mistletoe 这个库。并且找到了这个库作者写给开发者的文档,就是在这个文档中,他明确提到了 AST 的运作原理。在文档的最后,他举了一个从 Markdown 生成 Markdown 的例子。
💡
假设你有一些Markdown,你想做些处理,然后再次输出为Markdown。由于Markdown的文本性质,通常可以使用文本搜索和替换工具来实现这一点。但并不总是如此。 例如,如果要替换纯文本中的文本片段,而不是嵌入的代码示例中的文本片段,则搜索并替换方法将不起作用。
在这种情况下,就可以使用 mistletoe 的 MarkdownRenderer :
  1. 将Markdown解析为AST(通常保存在 Document 标记中)。
  1. 对AST进行修改。
  1. 使用 MarkdownRenderer.render() 渲染回Markdown。
而实际运行的代码如下:
import mistletoe with open("README.md", "r") as fin: with MarkdownRenderer() as renderer: document = mistletoe.Document(fin) process_ast(document) md = renderer.render(document) print(md)
借助这个库,就可以提取出标题、段落等信息,为下一步的输入以及最后的拼接还原,做好了充足的准备。

2. 计算要点和文本的相似度

举例来说,如下是一段生成的带大纲的要点内容,以及其原始内容文本内容。前者是由后者概括总结生成的。当下的任务是,把右侧内容以一种合理的形式填充到左侧的框架梗概之中。
但是下面例子中,左侧大纲文本只有7个要点(slot),而右侧则有 9 个段落,两者不相等,要如何映射过去呢?
💡
[带大纲的文本]

利用企业微信搭建AI机器人

  • 文章介绍了如何使用企业微信创建AI机器人,为企业和个人提供各种服务。

注册企业微信

  • 提供了注册企业微信的步骤,包括随意填写企业信息,录入管理员信息并进行扫码验证。

创建企业微信机器人

  • 说明了创建应用的过程,以创建谷歌Gemini机器人为例。

设置可信域名

  • 针对未认证企业,建议使用华为云函数进行免费验证,并详细解释了验证步骤。

集成AI大模型

  • 介绍了Chat CPT on WeChat项目,支持多种AI技术和多模态功能。

接口示例

  • 强调了Google Gemini AI的新颖性和免费API特性。

扩展功能集成

  • 推荐了NAS Tools用于电影下载功能,以及Chat GLM3和Home Assistant结合实现智能家居控制。
 
💡
[原始内容,有删节]
这不是普通的微信好友,这是我的家族企业。我们点击查看详情,实际上其中只有我一个是真实用户,其余皆为AI机器人。[…]
本期内容我们将分享如何免费注册一家一人制企业,从而“白嫖”企业微信的所有功能。[…]
首先,我们来看看第一步——注册企业微信。此处附有链接,只需点击进入。[…]
接下来,我们要开始创建企业微信机器人:点击“管理应用”,再选择“创建应用”。为了紧跟潮流,我在本期视频中将以创建一个谷歌Gemini机器人为例 […]
待应用创建完毕后,向下查找并点击“设置可信域名”,这是非常关键的一环。如果是刚创建的企业,尚未获得认证,那么可以借助华为云函数来进行免费验证。[…]
创建完云函数后,进入“设置”选项卡,点击“触发器”,并依次点击“创建触发器”→“创建分组”,[…]
回到企业微信后台,在“可信域名”位置粘贴之前生成的那个URL,注意要去掉末尾斜杠并将请求方法从HTTP改为无特定要求的状态。[…]
在企业微信端确认保存更改后,显示“修改成功”,这意味着我们的企业微信机器人已经具备开启所有功能的能力。[…] 我选用的是最新推出的Google Gemini AI,它不仅新颖而且现阶段其API仍保持完全免费状态。
受限于篇幅,本期主要聚焦于对接AI大模型的实现步骤。如果需要集成电影下载功能,推荐查看NAS Tools工具;而要实现智能家居控制的话,可以通过组合Chat GLM3和Home Assistant来达成目标。[…]

2.1 计算文本向量嵌入

对于任意的文段,很难确保两者数量是相等的,于是便需要一个方法,能够计算文本之间的相似度。从文本中分词并使用关键词匹配,是一种不错的做法。而另一种做法,则是使用文本向量嵌入。
💡
文本向量嵌入是一种表示技术,它将文本(如单词、短语或文档)转化为一系列的实数向量。这种向量的特点是,语义相近的文本在向量空间中的距离也相近,这使得机器能够理解和处理文本数据。这项技术常常用于自然语言处理(NLP)的各种任务,如情感分析、文本分类和推荐系统等。
现如今,已经有很多开源的模型可以提供文本向量嵌入的计算。如果不想本地部署,也可以调用 OpenAI Embedding 的接口来实现。
经过计算,左侧信息对应的文本嵌入向量是这样的:
要点对应层级
文字内容
文本嵌入向量
H1
文章介绍了如何使用企业微信创建AI机器人,为企业和个人提供各种服务。
[-0.03085 -0.04855 -0.0344 ... -0.01874 0.0305 -0.0206 ]
H2
提供了注册企业微信的步骤,包括随意填写企业信息,录入管理员信息并进行扫码验证。
[-0.006527 -0.07294 -0.02509 ... -0.00958 0.02565 -0.02151 ]
H2
说明了创建应用的过程,以创建谷歌Gemini机器人为例。
[-0.06097 -0.06647 0.03268 ... 0.002405 -0.01404 -0.02138 ]
H3
针对未认证企业,建议使用华为云函数进行免费验证,并详细解释了验证步骤。
[ 0.01604 -0.0347 -0.03897 ... 0.007103 -0.00497 -0.03986 ]
H2
介绍了Chat CPT on WeChat项目,支持多种AI技术和多模态功能。
[ 0.00418 -0.022 -0.02782 ... -0.0759 0.04807 -0.0474 ]
H3
强调了Google Gemini AI的新颖性和免费API特性。
[-0.03232 -0.0179 -0.007637 ... -0.02066 -0.02895 -0.00454 ]
H2
推荐了NAS Tools用于电影下载功能,以及Chat GLM3和Home Assistant结合实现智能家居控制。
[-0.00862 0.003813 -0.02989 ... 0.01499 -0.02501 -0.06464 ]
同样的,对于右侧的文本,也可用类似的方式去计算其文本嵌入向量,结果如下:
段落编号
文字内容
文本嵌入向量
1
这不是普通的微信好友,这是我的家族企业。[…]
[ 0.03986 -0.02347 0.01912 ... -0.02864 0.01813 -0.0288 ]
2
本期内容我们将分享如何免费注册一家一人制企业。[…]
[ 0.000532 -0.05658 -0.002401 ... -0.02426 0.01541 -0.02089 ]
3
首先,我们来看看第一步——注册企业微信。[…]
[ 0.0499 -0.03812 0.0083 ... -0.001751 0.00808 0.0067 ]
4
接下来,我们要开始创建企业微信机器人:点击“管理应用”,再选择“创建应用”。[…]
5
待应用创建完毕后,向下查找并点击“设置可信域名”。[…]
6
创建完云函数后,进入“设置”选项卡,点击“触发器”。[…]
7
回到企业微信后台,在“可信域名”位置粘贴之前生成的那个URL。[…]
[ 0.01271 0.02037 0.03073 ... -0.00922 0.0354 -0.02211 ]
8
在企业微信端确认保存更改后,显示“修改成功”。[…]
[-0.001373 -0.01369 0.01886 ... -0.012764 0.02904 -0.0561 ]
9
受限于篇幅,本期主要聚焦于对接AI大模型的实现步骤。[…]
[-0.010544 0.00959 -0.00388 ... 0.0705 -0.01765 -0.0657 ]

2.2 计算相似度矩阵

这里的向量都是归一化长度的,这意味着我们可以用向量内积的方式,去度量它们的相似度。内积越大,说明向量越相似,也就说明两者对应的文本内容也就越解决。
为此,我计算了两两之间的内积,制作画成了下表的格式。
1
2
3
4
5
6
7
8
9
H1
0.764
0.585
0.589
0.763
0.5103
0.4211
0.5127
0.622
0.4983
H2
0.6216
0.733
0.7925
0.623
0.639
0.453
0.625
0.62
0.387
H2
0.6343
0.5166
0.5615
0.844
0.503
0.4036
0.4524
0.5864
0.4282
H3
0.5166
0.5913
0.585
0.5273
0.882
0.6484
0.8047
0.531
0.414
H2
0.5454
0.3489
0.3853
0.4539
0.337
0.3816
0.3486
0.6406
0.561
H3
0.4023
0.302
0.2756
0.5435
0.385
0.3499
0.285
0.3857
0.365
H2
0.39
0.3076
0.317
0.43
0.3735
0.3801
0.3372
0.4087
0.781
如果将每一列的最大值用着重颜色标出,就会发现形成了一条从左上角到右下角的蜿蜒曲折的通路。这恰好就是将文段嵌入到大纲的一种方式。而且你会发现,由于文段嵌入的顺序性,这条曲折通路只能向右平移、或者是向下延申,而不能反方向行进,不然就会造成嵌入的混乱。
尽管在表格中还有一些稍大的值散落在通路周围,但是为了绕路选择他们,就会出现“捡了芝麻,丢了西瓜”的情况,在整体上是非最优的。

3. 要点和段落匹配

为了找到要点和段落的匹配,这个问题其实很像 LCS (Longest Common SubSequence) 问题,只不过原来问题中的定义是0或1的整数,这里变成0~1之间的浮点数。为了解决这类问题,通常的做法是动态规划。
💡
动态规划是一种用于求解最优化问题的算法策略。它的基本思想是将一个复杂的问题分解为一系列简单的子问题,同时保存子问题的答案,避免重复计算。这种方法只适用于满足“无后效性”和“最优子结构”条件的问题。无后效性是指子问题的解一旦确定,就不再改变,不受在这之后做出的决定的影响。最优子结构是指问题的最优解包含其子问题的最优解。
因此,只需确定其最优子问题是什么。我们需要找到一条从左上角到右下角的路径,并且使得这条路径途径的数值加起来尽可能地大。
考虑文本长度固定,但是要点个数递增。当要点数目只有1个时,答案是平凡的。因此可作为初始的结构。我们考虑如下的递推关系:
对于路径上的任意一个节点,其上一个节点有三种可能:
  1. 从左边:这代表这多个文段对应者同一个要点的情况,是可能的
  1. 从左上:这代表着切换到下一个要点和文段间的对应关系
  1. 从上方:这代表着一个文段对应着多个要点(为了方便计数,这里会取其中相似度最高的那个要点作为输出值,而不是简单的相加)
并且根据其最大值实际出现的分支,确定一个方向,最终计算结果如下。
局部分数求和,以及反推路径: [[0.76 * 1.35 ← 1.94 ← 2.70 ← 3.21 ← 3.63 ← 4.14 ← 4.77 ← 5.27 ←] [0.62 ↑ 1.50↖ 2.29 ← 2.91 ← 3.55 ← 4.00 ← 4.63 ← 5.25 ← 5.64 ←] [0.62 ↑ 1.28 ↑ 2.06↖ 3.13↖ 3.64 ← 4.04 ← 4.49 ← 5.21↖ 5.68↖] [0.50 ↑ 1.28 ↑ 2.06 ↑ 2.82 ↑ 4.02↖ 4.66 ← 5.47 ← 6.00 ← 6.41 ←] [0.50 ↑ 1.04 ↑ 1.86 ↑ 2.74 ↑ 3.47 ↑ 4.40↖ 5.01↖ 6.11↖ 6.67 ←] [0.36 ↑ 0.99 ↑ 1.75 ↑ 2.74 ↑ 3.47 ↑ 4.37 ↑ 4.95 ↑ 5.86 ↑ 6.47↖] [0.35 ↑ 0.99 ↑ 1.75 ↑ 2.63 ↑ 3.46 ↑ 4.37 ↑ 4.95 ↑ 5.86 ↑ 6.64↖]]
由此,我们便通过定量计算的方式,得到了和上一节中的那条最短路径。
 
 

Copyright © 2025 潇大

logo