MoreRSS

site iconShadow Walker | 松烟阁修改

Where other men are limited by morality or law, remember, everything is permitted. I walk in the darkness to serve the light.
请复制 RSS 到你的阅读器,或快速订阅到 :

Inoreader Feedly Follow Feedbin Local Reader

Shadow Walker | 松烟阁的 RSS 预览

Agentic AI

2025-01-19 17:27:50

What

AI Agent 当前还是一个比较新兴的基于 LLM 的概念,Google 最近发布了关于它的白皮书(Agents | Kaggle),最近我看到一个说法:「AI 是风口,AI Agent则是风口的平方」,所以我打算结构性的梳理一下 AI Agent 的概念。

我个人粗浅地将 AI Agent 分为两个层次 Generative AI Agent 和 Agentic AI:

  • Generative AI Agent,当前经常提到的 AI Agent 大多是指 Generative AI Agent,它将推理(Reasoning)、逻辑(Logic)和对外部信息的访问(External Info Access)都与生成式人工智能模型(LLM)相联系,从而形成了 Agent 的概念,或者说是一个超出生成式人工智能模型(LLM)独立功能的应用。
  • Agentic AI,Agentic AI 也被称为 Agentic Systems,它是指能够自主行动来实现目标而无需有人介入的系统和模型。Agentic AI 系统了解用户的目标以及他们试图解决的问题的背景,并可以主动为解决问题进行推理和行动。

由于 AI Agent 发展还在早期,Agentic AI 目前还是大家对 AI Agent 终态的美好想象,所以本文主要就先梳理清楚 Generative AI Agent,后面提到的 AI Agent 均指 Generative AI Agent。

技术架构

为了理解 AI Agent 的内部工作原理,我们首先介绍 AI Agent 运行和决策的基础组件。这些组件的组合可以描述为认知架构(cognitive architecture),并且可以通过混合和匹配这些组件来实现许多这样的架构。专注于核心功能,Agent 的认知架构中有三个基本组件,如下图所示。

图片源自Google AI Agent白皮书
  1. Model,AI Agent 架构中的 model 模型是指将用作 Agent 流程的集中决策者的语言模型 (LM),通常是指 claude、gemini、chatgpt 等 LLM。
  2. Tools,由于 Model 受到无法与外界互动的限制,所以需要 tools 工具来弥补这一缺陷,tools 使得 Agent 能够与外部数据和服务进行交互,从而解锁和释放模型 LLM 的能力。
  3. Orchestration Layer,编排层描述了一个循环过程,它控制着 AI Agent 如何获取信息、执行一些内部推理,并使用该推理来指导其下一步行动或决策。

Workflow V.S. Agent

了解完 AI Agent 的技术架构之后,其实会有一个疑惑:「从架构来看 AI Agent 就是在 LLM 的基础上增加了与外部交互的工作流,为什么它会被提到如此高的地位?」。

对于这个疑问我想先对比一下 workflows 和 agents:

  • Workflows,是通过预定义的代码路径来协调 LLM 和工具的系统
  • Agents,是 LLM 动态指导其自身流程和工具使用的系统,从而控制其完成任务的方式

Agent 与 Workflow 最大的区别就是 Agent 可以使用认知架构(Cognitive Architecture)来实现其最终目标,方法是迭代处理信息、做出明智的决策并根据先前的输出完善下一步行动。Agent 认知架构的核心是编排层,负责维护记忆、状态、推理和规划。它使用快速发展的即时工程领域和相关框架来指导推理和规划,使 Agent 能够更有效地与其环境交互并完成任务,例如使用 LLM 的 ReAct、CoT、ToT 等推理技术来根据任务目标选择行为和工具。

Agent 常见的模式

  • The augmented LLM(增强型 LLM),增强的LLM,增强了检索、工具和记忆等功能。我们当前的模型可以主动使用这些功能——生成自己的搜索查询,选择适当的工具,并确定要保留的信息。
  • Prompt chaining(提示链),提示链将任务分解为一系列步骤,其中每个LLM调用处理前一个的输出。你可以在任何中间步骤添加程序检查(参见下图中的“门”)以确保过程仍在正轨上。何时使用此工作流: 此工作流非常适合可以轻松且清晰地分解为固定子任务的情况。主要目标是通过使每个 LLM 调用变得更简单,以牺牲延迟来换取更高的准确性。
  • Route(路由),路由对输入进行分类,并将其导向专门的后续任务。这种工作流程允许分离关注点,并构建更专业的提示。没有这种工作流程,优化一种输入可能会损害其他输入的性能。何时使用此工作流: 路由适用于复杂的任务,其中存在可以单独处理的明确类别,并且分类可以通过LLM或更传统的分类模型/算法准确处理。
  • Parallelization(并行化),LLMs 有时可以同时处理一个任务,并通过编程方式聚合它们的输出。何时使用此工作流程: 当可以将子任务并行化以提高速度,或者需要多个视角或尝试以获得更可靠的结果时,并行化是有效的。对于具有多个考虑因素的复杂任务,LLMs 通常在每个考虑因素由单独的 LLM 调用处理时表现更好,从而能够专注于每个具体方面。这种工作流程,即并行化,表现为两种主要变体:
    • Sectioning(分块):将任务分解为独立的子任务并行运行。
    • Voting(投票): 多次运行相同任务以获得多样化的输出。
  • Orchestrator-workers(编排者-工作者),在编排者-工作者工作流中,中央LLM动态地分解任务,将它们委派给工作者LLMs, 并合成其结果。何时使用此工作流:此工作流非常适合复杂的任务,您无法预测所需的子任务(例如在编程中,需要更改的文件数量和每个文件中的更改性质可能取决于任务)。虽然它在拓扑上相似,但与并行化的主要区别在于其灵活性——子任务不是预先定义的,而是由协调者根据具体输入来确定。
  • Evaluator-optimizer(评估器-优化器),在评估器-优化器工作流中,一个 LLM 调用生成响应,而另一个则在一个循环中提供评估和反馈。何时使用此工作流: 当我们有明确的评估标准,并且迭代改进能够提供可衡量的价值时,此工作流特别有效。良好的适应性有两个标志,首先,当人类能够清楚地表达他们的反馈时,LLM 的响应可以明显改善;其次,LLM 能够提供此类反馈。这类似于人类作者在撰写一篇精心打磨的文档时所经历的迭代写作过程。
  • Agents(智能体),随着 LLMs 在关键能力上逐渐成熟——理解复杂输入、进行推理和规划、可靠地使用工具以及从错误中恢复,agent 正在生产中出现。agent 的工作始于人类用户的命令或互动讨论。一旦任务明确,agent 将独立地进行计划和操作,必要时返回向人类用户寻求更多信息或判断。在执行过程中,agent 需要在每一步从环境中获取“真实情况”(如工具调用结果或代码执行)以评估其进展。然后,agent 可以在检查点或遇到阻碍时暂停以获得人类反馈。任务通常在完成后终止,但也常见设置停止条件(例如最大迭代次数)以保持控制。AI Agent 可以处理复杂的任务,但它们的实现通常很简单。它们通常是使用基于环境反馈的工具循环的 LLMs。因此,设计工具集及其文档时必须清晰且周到。何时使用 Agent:Agent 可以用于开放式问题,当难以或无法预测所需的步骤数量,并且无法硬编码固定路径时。LLM 可能会运行多个回合,您必须对其决策有一定的信任。AI Agent 的自主性使它们在受信任的环境中非常适合扩展任务

When

对程序员而言什么时候需要用到 AI Agent 呢?

在使用 LLMs 构建应用程序时,Claude 建议寻找最简单的解决方案,并且只在必要时增加复杂性。这可能意味着根本不需要构建 agentic system,agentic system 通常会在延迟和成本上做出妥协以换取更好的任务表现,您应该考虑这种权衡是否有意义。

当需要更多复杂性时,workflow 为明确定义的任务提供可预测性和一致性,而 agent 则是在需要大规模灵活性和模型驱动决策时的更好选择。然而,对于许多应用程序来说,通过检索和上下文示例优化单个 LLM 调用通常就足够了。

How

虽然语言模型 LLM 擅长处理信息,但它们缺乏直接感知和影响现实世界的能力,这限制了它们在需要与外部系统或数据交互的情况下的实用性。从某种意义上说,语言模型 LLM 的好坏取决于它从训练数据中学到的东西,但无论我们向模型投入多少数据,它们仍然缺乏与外界交互的基本能力,如何实现 tools 才能使我们的模型能够与外部系统进行实时、上下文感知的交互就是 AI Agent 需要做的第一件事情。

Google AI Agent 白皮书发布的时候,在技术架构中解决与外部系统交互和感知的方法包括:

  • Functions
  • Extensions
  • Data Stores

Extensions

理解 AI Agent Extensions 的最简单方法是将其视为以标准化方式弥合 API 和 Agent 之间的差距,从而允许 Agent 无缝执行 API,而不管其底层实现如何。Extensions 通过以下方式弥合 AI Agent 与 API 之间的差距:

  1. 使用示例教 AI Agent 如何使用 API
  2. 教AI Agent 成功调用 API 端点所需的参数
图片源自Google AI Agent白皮书

Extension 示例:

import vertexai
import pprint
PROJECT_ID = "YOUR_PROJECT_ID"
REGION = "us-central1"
vertexai.init(project=PROJECT_ID, location=REGION)
from vertexai.preview.extensions import Extension
extension_code_interpreter = Extension.from_hub("code_interpreter")
CODE_QUERY = """Write a python method to invert a binary tree in O(n) time."""
response = extension_code_interpreter.execute(
	operation_id = "generate_and_execute",
	operation_params = {"query": CODE_QUERY})
print("Generated Code:")
pprint.pprint({response['generated_code']})
# The above snippet will generate the following code.

# Generated Code:
class TreeNode:
	def __init__(self, val=0, left=None, right=None):
		self.val = val
		self.left = left
		self.right = right

def invert_binary_tree(root):
	"""
	Inverts a binary tree.
	Args:
	root: The root of the binary tree.
	Returns:
	The root of the inverted binary tree.
	"""
	if not root:
		return None

	# Swap the left and right children recursively
	root.left, root.right = invert_binary_tree(root.right), invert_binary_tree(root.left)
	return root

# Example usage:
# Construct a sample binary tree
root = TreeNode(4)
root.left = TreeNode(2)
root.right = TreeNode(7)
root.left.left = TreeNode(1)
root.left.right = TreeNode(3)
root.right.left = TreeNode(6)
root.right.right = TreeNode(9)
# Invert the binary tree
inverted_root = invert_binary_tree(root)

Functions

Functions 就是被预先定义好的独立的代码模块,可以完成特定任务,并可根据需要重复使用。LLM 可以采用一组已知的 Functions,并根据其规范决定何时使用每个函数以及函数需要哪些参数。

Functions 与 Extensions 比较类似,但也有几个不同之处:

  1. LLM 输出 Functions 及其参数,但不进行实时 API 调用,调用实际 API 的逻辑和执行将从 Agent 转移回 Client
  2. Functions 在 Client 执行,而 Extensions 在 Agent 执行
图片源自Google AI Agent白皮书

开发人员选择使用函数而不是扩展的原因有很多,但一些常见的用例是:

  • API 调用需要在应用程序堆栈的另一层进行,在 AI Agent 架构流程之外(例如中间件系统、前端框架等)
  • 安全或身份验证限制阻止 Agent 直接调用 API(例如 API 未暴露给互联网,或基础设施无法访问)
  • 时间或操作顺序限制阻止 Agent 实时进行 API 调用(即批量操作、人工审核等)
  • 需要将 Agent 无法执行的额外数据转换逻辑应用于 API 响应(例如,考虑一个不提供过滤机制来限制返回结果数量的 API 端点。在客户端使用函数为开发人员提供了进行这些转换的额外机会。)
  • 开发人员希望在不为 API 端点部署额外基础设施的情况下迭代 Agent 开发(即函数调用可以充当 API 的“存根”)

Function 示例:

# function tool
def display_cities(cities: list[str], preferences: Optional[str] = None):
	"""Provides a list of cities based on the user's search query and preferences.
	Args:
		preferences (str): The user's preferences for the search, like skiing,
		beach, restaurants, bbq, etc.
		cities (list[str]): The list of cities being recommended to the user.
	Returns:
		list[str]: The list of cities being recommended to the user.
	"""
	return cities

# example calling function in Agent
from vertexai.generative_models import GenerativeModel, Tool, FunctionDeclaration
model = GenerativeModel("gemini-1.5-flash-001")
display_cities_function = FunctionDeclaration.from_func(display_cities)
tool = Tool(function_declarations=[display_cities_function])
message = "I’d like to take a ski trip with my family but I’m not sure where
to go."
res = model.generate_content(message, tools=[tool])
print(f"Function Name: {res.candidates[0].content.parts[0].function_call.name}")
print(f"Function Args: {res.candidates[0].content.parts[0].function_call.args}")

> Function Name: display_cities
> Function Args: {'preferences': 'skiing', 'cities': ['Aspen', 'Vail',
'Park City']}
图片源自Google AI Agent白皮书

Data Store

Data Store 允许开发人员以原始格式向 Agent 提供额外数据,从而无需进行耗时的数据转换、模型再训练或微调。Data Store 将传入的文档转换为一组矢量数据库嵌入,Agent 可以使用这些嵌入来提取所需的信息,以补充其下一步操作或对用户的响应。

图片源自Google AI Agent白皮书

在 AI Agent 的上下文中,Data Store 通常实现为向量数据库,开发人员希望 Agent 在运行时能够访问该数据库。虽然我们不会在这里深入介绍向量数据库,但要理解的关键点是它们以向量嵌入的形式存储数据,这是一种高维向量或所提供数据的数学表示。近年来,数据存储在语言模型中的最常见使用示例之一是实现基于检索增强生成 (RAG) 的应用程序。这些应用程序试图通过让模型访问各种格式的数据来扩展模型知识的广度和深度,使其超越基础训练数据:

  • 网站内容
  • PDF、Word 文档、CSV、电子表格等格式的结构化数据。
  • HTML、PDF、TXT 等格式的非结构化数据。

随着 AI Agent 的迭代优化,基于 Data Store 的 RAG 技术也在更新,可以简单的看一下传统 RAG 与 Agentic RAG 的对比(后面再写专题细细研究):

References

  1. What Is Agentic AI, and How Will It Change Work?
  2. Building effective agents | Anthropic
  3. Agents | Kaggle

对天赋祛魅,对努力去蔽

2025-01-11 23:18:59

按语

偶然间从自习室 STUDY ROOM 的一期播客中知道了一本书《异类》(英文名是 Outlier),作者是马尔科姆・格拉德威尔,作者作为播客界的绝对的 OG,他是懂得怎么讲故事的,虽然源自这本书的「一万小时定律」备受争议,但是不可忽视的是这本书用大量的故事为读者展现了努力、天赋与成功之间的关系。

24年初的时候我也思考过关于努力和天赋的问题,今天对于这个问题有了一些更新。

祛魅

祛魅 disenchantment,源自马克斯·韦伯借用弗里德里希·席勒的理论来描述现代化、官僚化的世俗西方社会,在这种社会中自然理解能力比信仰更有价值、过程都以合理的目标为方向,与韦伯所说的“这个世界上遗留着好的迷人的花园”的传统社会相对立。

“祛魅”和“复魅”的概念,在西方后现代哲学著作中使用的比较多,但学术界没有公认准确的定义。“祛魅”有時也稱為“除魅”,是指剥去附着在事物表面上的那层虚假的东西,即“魅”;“复魅”有时也译为“返魅”,是主张返回事物的自然状态,恢复事物的本来面貌

去蔽

当学习一物时,亦即看清一物时,我们能得到它的「真理」。此真理不是前设的,不是带有预先立场的。海德格尔认为,真理的意义为「去蔽」。真理的希腊文是 λθεια (aletheia),字是希腊文中的否定前缀(如同英文中的 un, dis, in, im),∧θη 是遮蔽,那就是「去除遮蔽」的意思。那就是说,真理的意义就是扫开遮蔽,让呈现之物被看见。

由柏拉图开始,每当哲学家思考他物时,都是由思考者主导,因而会先对真理作出定义,再按它来规定自己的思考。当哲学家定义真理时,亦同时限制了、规定了真理的意义。海德格尔称这种来自为人主导的哲学为人本主义(humanism),而这是他所反对的。他认为,真理不由人规定,只有当人不再固执,而是如其所是的去接受一物,如其所是的去描述一物,让它自然而然地呈现它自己,它就呈现出它的真理。

可以如此理解:我们理解事物时会带有很多预设,这些预设来自不同地方,例如文化、社会、科学等,这些预设影响我们对这些事物的了解,如果要看到事物本物,就必须将这些遮蔽扫除,这就是「去蔽」的意思。

祛魅天赋

天才并非一开始就表现出众,一开始他只是比别人优秀一点点!大多是马太效应作用下,逐步放大那一点点的优势,紧随着优劣分化后的资源倾斜和正反馈,优秀者只会越来越优秀。
  • 加拿大顶级冰球运动员的出生天赋

顶级加拿大冰球运动员的生日集中在1-3月,因为加拿大的冰球选拔是从9-10岁开始,优秀的选手会组成巡回赛小组,拥有更好的教练,队友,更多的比赛数量。而体育运动,众所周知,在青少年时期的体育竞技,身体发育情况是决定性因素。同年龄的孩子,越早出生越有优势。加拿大冰球的选拔周期是1月1日,从上一年1月1日到12月31日的孩子分在同一组,那么1-3月的孩子身体发育情况好,更容易在起步阶段进入巡回赛小组,并一直优秀下去,成为顶级运动员。

  • 中国人的数学天赋

人对数字的记忆周期是2秒钟,中文的数字发音非常简洁,数学系统描述数字也非常直观。比如中文对数字的发音都是单音节,非常好记忆,而且大于10的数字规律很直观,比如11就是10和1,但是英文则是 eleven,这样特殊的单词,没有规律。这导致4岁的中国孩子平均可以数到40,而同龄的美国孩子则只能数到15,大多数的美国孩子即使到了5岁也不能数到50。

  • 困在生活里的高智商天赋

在电视真人秀节目《以一敌百》中曾说道爱因斯坦的智商是150,而兰根的智商是195。高智商的兰根由于母亲的疏忽错过了奖学金申请,无法和校方沟通把上午的课换到上午,和不能融入学校生活等原因,只上了一半的大学,没有取得学位就离开学校了。他在工作的期间,深入阅读哲学、数学和物理学著作,他草草完成了自己的论文,构建大学认知的理论模型理论。但由于没有大学的支持,他的论文从来没有任何一家学生杂志社发表。

瑞文智商测试的实验告诉我们,如果一个人的智商达到了120左右,那么再增加智商并不会在现实世界给他带来明显的优势,其实你不需要最好,只需要足够好就可以了。很多在我们看来是个人原因的事情,其实背后有非常复杂的社会家庭原因,不要以为自己只是个人,你是整个文化血脉的一部分,我们所谓天才只是一切结果的初始「动量」

去蔽努力

Innate ability does not exist and ability is actually a function of effort expended.

重新认识努力

努力与成功不是互为充要条件的,但是努力是一定会有回报的,但不一定是心想事成。不管发生什么,都要努力,不以成功为目标,就是要努力,因为很多时候成功和幸运看似偶然,但当幸福来敲门时,你也得努力保证自己站在门后面,不是吗!? 努力是因为我想努力,不能有悲壮感,不能去苛求努力就一定会成功!

李诞说过一句话「你是你的目的。你25岁那年有个耗尽全力去解决没有解决掉的事,你觉得五年之后还会记得吗?但是你五年之后是不是还跟25岁的自己一样呢,这个才是重要的」。努力是非常对的,这个事做好了,做对了,做错了不是很重要,关键是你怎么理解这件事情,你怎么升级自己对做事情的认知,你还会不会在同样的事情上跌倒第二次。错不可怕,你知道错在哪里就好了。至于没有结果的事,它肯定是有结果的,只是不一定是你想要的结果。从成功的角度来说,这未必是坏事,当年看起来完全是做错了的事情,后面也可能会有一些意想不到的报偿。

关于躺平

我深怕自己本非美玉,故而不敢加以刻苦琢磨,却又半信自己是块美玉,故又不肯庸庸碌碌,与瓦砾为伍。于是我渐渐地脱离凡尘,疏远世人,结果便是一任愤懑与羞恨日益助长内心那怯弱的自尊心。其实,任何⼈都是驯兽师,而那野兽,无非就是各人的性情而已。——《山月记》中岛敦

通过上面的讨论其实可以看出来,成功是有迹可循的,但绝对不是「基因突变」式的天赋直接带来的,成功虽然是玄学,但努力一定是它的必须项。刘嘉玲说过:努力的人不一定成功,但成功的人一定很努力。

当前下行周期的背景下很多人都选择躺平,躺平只是因为看不见努力的路,如果能看见确定性的期待的未来,没有任何人会躺平,人生充满激情与价值感,是基因就带的编码。所以,战略性后退(躺平)与努力并不冲突,我们只是在为下一次积攒能力。

埋头以一种近乎于天真跟愚蠢的方式去努力,也许才是成功的要素。

References

  1. 祛魅 - 维基百科,自由的百科全书
  2. 海德格尔的思想 - 维基百科,自由的百科全书
  3. 海德格尔:如果想了解事物本质,就必须放下自身的预设
  4. Outliers, The Story of Success 《异类:不一样的成功启示录》 读书笔_HenryYUE_新浪博客

2024

2024-12-31 23:54:39

陆陆续续一年一度的年终总结开始了,今年看到有不少人在揶揄所谓的年终总结:「总结毫无意义,没完成的事忘记吧,完成的你已经收益」,「平时养成日记或者笔记的习惯,要什么总结」。不过,我还是觉得自己在写年终总结的时候,实际是在与这一年的自己进行对话,除了简单的清点与记录,更是一种自我认知的形式,不仅帮助我反思事情本身,而且会帮我看到自己的变化与成长,说到底年终总结的本质是我自己对抗时间无常的一种手段。

今年还是老规矩依旧通过 Kepano 的40个问题进行总结回顾,另外今年的年终总结我还想做一点变化,因为「其实读者们并不关心我的2024是什么样的,或者说我的2024对读者而言无非是茶余饭后的消遣没有什么价值」,于是我想着在自我总结和反思之前,先列一个2024年度清单,把我觉得所有2024年碰到值得列出来的东西都列举上,希望这个2024年度清楚对读者们有用。最后,如果你对2024的40个问题部分感兴趣或者有共情,欢迎你也跟我讲讲你的故事和思考。

2024年度清单

Annual Proust Questionnaire

1. 你今年做了哪些之前从未做过的事?

  • 给自己放假,独自去一个想了很想去的地方玩
  • 开发了 AI Agent 应用 —— 自动代码漏洞分析与修复、自动文章配图、自动文章标签、自动文章配音

2. 你有没有遵守年初时和自己许下的约定?

  • 职业发展,2024年度的职业发展符合自己的预期,经历了低谷和压力也慢慢摸索出自己的路径和方向;
  • 学习成长,专业写作练习没有达到目标,感觉这种技能属性的提升还是需要找专业的人辅导,不过以传播为目标的写作练习非我本意,所以没有找到很好的资源;英文完成了年度目标,但是距离可以 CEFR B1 的目标还有很远的距离,继续努力;技术向的提升,例如 SEO、CSS 等有了满意的提升;
  • 生活目标,受到了 QMAS 的影响导致 Stripe 开户受到了影响,来年需要改变策略;家庭旅游方面基本上是奔着消费降级去的,不过还是不错的体验;
  • 财务管理,有了一些基本理财知识,能够利用掌握的理财知识进行操作并获得收益,例如期权 covered call 操作;
  • 阅读清单,今年的阅读计划完成度是有43%(17本),今年最喜欢的一本书是《拐点》;阅读给我最大的帮助是逃离了纷杂的社交媒体,并且多了很多输入源;

3. 你身边有人生孩子了吗?

  • 有,同事、同学、朋友都有生孩子的,大多都是二胎,我自己则是非常抗拒生二胎的。我也在反思原因,主要是未来的不确定和家庭的不确定吧。生活这么苦累何必再带一个生命来受罪呢!

4. 你身边有人去世了吗?

  • 有,接连有两位亲人去世了。经历过生死以后会心软,会看淡,会释怀,没有什么比活着重要,也没有什么比自己更重要。

5. 你去了哪些城市/州/国家?

  • 漳州
  • 广州
  • 上海
  • 北京
  • 南麂岛
  • 香港
  • 日本

6. 明年你想要获得哪些你今年没有的东西?

  • English Native Speaker
  • 日语 N3
  • Side Hustle
  • GIP

7. 今年的哪个或哪些日子会铭刻在你的记忆中,为什么?

  • 发布自己作品的时候那个晚上,有读者私信述说跟我阐述观点的共鸣,突然间我意识到我还没有学会述说;
  • 在我快扛不下去的那几天,有一个人为了我的一句话,从美国闪现杭州,给我很大支持和抚慰;

8. 你今年最大的成就是什么?

  • AI Agent 应用在个人和工作的项目中运行起来
  • 副业被动收入开始有起色,曲线已经开始有明显的上扬的趋势

9. 你今年最大的失败是什么?

  • 处理亲密关系上是失败的,我以为是我不够好,是我陪伴少,是我性格有缺陷。但是经历背叛、被选择、被算计、被诋毁之后,我发现我的一切在自我面前一无是处。既然总要有个人为争吵道歉,为什么不可以是我。我只有承认失败才能继续往前看,站在自己的角度理解别人,站在别人的角度释怀自己。

10. 你今年还遇到过哪些困难?

  • 工作当作碰到了很多技术、软能力上的挑战和困难,技术上主要面对是安全领域的问题工业界和学术界都没有很好的解决方法;软能力上主要是面对项目管理、设计、沟通等方面的问题。所幸,这一年跌跌撞撞的,这些困难也都熬过来了(^ω^)

11. 你今年是否生过病或受过伤?

  • 阳了一次,没有用试剂检测就是症状跟去年一样,扛了一个礼拜自己好了

12. 你今年买过的最好的东西是什么?

  • MARO 17男士洗发水,没有一款洗发水的味道让我如此喜欢,淡淡的、凉凉的

13. 谁的行为值得去表扬?

  • 我自己吧,夸奖一下这一年的历练之后练就了平稳情绪的自己。这一年我经历了压力、经历了绝望、经历了无助、经历了触底反弹、经历了柳暗花明、经历了抚慰释怀。一桩桩一件件都是我脚踏实地的熬过去的,有问题解决之后的开怀大笑,也有无奈接受之后的摇头苦笑

14. 谁的行为令你感到震惊?

  • 一个曾经很信任的人,曾经对她抱有无条件的信任和安全感,只是一切消散时光长河中

15. 你大部分的钱都花到哪里去了?

  • 给家人置办了一个大件

16. 有什么事让你感到超级、超级、超级兴奋?

  • 个人 AI 产品上线的时候

17. 哪首歌会永远让你想起这一年?

  • 《Save Your Tears》 The Weeknd

18. 与去年的这个时候相比,你是:感到更快乐还是更悲伤了?变得更瘦还是更胖了?变得更富还是更穷了?

  • 更平静了,工作、家庭、大环境等等让我走出焦虑和情绪,坦然面对已经或者即将到来的一切;体重好像没有什么波动,或许我就是那个不会胖的人吧;经济上没有什么大的的变化,以符合预期的状态缓慢上升中;

19. 你希望自己能做得更多的是什么?

  • 结构化思考问题的能力提升
  • 产品/价值问题的探索

20. 你希望自己能做得更少的是什么?

  • 为他人妥协
  • 短视频

21. 你是如何度过节假日的?

  • 学英语、写博客、看剧、陪孩子,长假陪家人旅行

22. 你今年坠入爱河了吗?

  • 没有,跟异性聊天的时候,偶有走心 Crush 的感受,为此我还写下一段话「喜欢,不一定要成为配偶、情侣、情人、密友。成为朋友、同事、供应商、合作伙伴、熟人,或者许久才联系一次的人也很好。以何种身份交往,共度时光的长短,亲近亲密的程度,都无需执着。交谈,吃饭,一起完成一件小事,都是确实的生命体验。只要心无杂念,就能完整彻底地拥有那一刻的人和世界」

23. 你是否有讨厌某个你去年此时不觉得讨厌的人呢?

  • 有,讨厌到不愿意提、不愿意想

24. 你最喜欢的电视节目是什么?

  • 喜剧之王单口季

25. 你读过最好的一本书是什么?

  • 《拐点》—— 万维钢

26. 你今年发现的最好听的一首歌是什么?

  • DJ TO MUCH BEER MANGKANE,看到万茜话剧结束返场的时候跳的 Charleston Dance,里面就用到了这段配音,心情低落的时候就会去找来听听,会让我感到很开心

27. 你今年看过最喜欢的一部电影是什么?

  • 好东西 Her Story

28. 你今年吃过最好吃的一顿饭是什么?

  • 冬至那天老妈包的饺子

29. 有什么是你想要且得到了的?

  • 个人作品和副业有可喜的营收增长

30. 有什么是你想要却没有得到的?

  • QMAS 没有按照预期拿到结果,按照现在行情我估计得放弃了

31. 你生日那天做了什么?

  • 跟家人一起庆祝

32. 有什么还未发生的事,如果发生了,会让你的这一年变得无比满足?

  • 副业营收增长

33. 你会如何描述你今年的个人时尚风格?

  • Rustic + Morandi Colors

34. 是什么让你保持理智?

  • 第一性原理和底线思维,凡事都知道

35. 你最欣赏哪个名人/公众人物?

  • 付航,他让我明白了一个道理「做自己就好,明确的爱,直接的厌恶,真诚的喜欢」

36. 哪个政治问题最令你有感而发?

  • Donald Trump 遭遇刺杀和大选获胜,以前不喜欢 Trump 大嘴巴瞎搞,被刺杀之后挥拳喊口号的那张照片让我看到政治强人的风采,美国是个慕强的国家

37. 你想念哪些人?

  • 2013浙大校医院走廊门口座椅上那个爱我的人,我依旧记得她衣服上的香味

38. 在你新认识的人之中,谁是最好的?

  • Nicole 和轩轩,会感谢碰到这样的朋友

39. 今年你学到了什么宝贵的人生经验?

  • 这个世界除了自己,其他都是所谓的「价值」交换

40. 能够总结你这一年的一句话是什么?

  • 你所有的压力,都是因为你太想要了,你所有的痛苦,都是因为你太较真了。有些事不能尽你心意,就在提醒你该转弯了。如果事事都是如意,那就不叫生活了,所以睡前原谅一切,醒来不问过往,珍惜所有的不期而遇,看淡所有的不辞而别。人生,一站有一站的风景,一岁有一岁的味道,你的年龄应该成为你生命的勋章而不是你伤感的理由。生活嘛,慢慢来,你又不差。

References

  1. kepano/40-questions: Questions that I ask myself at the end of each year and each decade.

Obsidian基于AI自动为文章配图

2024-12-16 22:30:42

我在 Obsidian 中管理 Newsletter 特别是文章发布之前经常需要花很长的时间在网络上搜索与文章主题匹配的图片作为文章的 featured image,同时还需要考虑版权、匹配度等等问题,如果 AI 能自动帮我完成这件事情那就太好了,顺着这个思路我发现 wordpress 已经支持类似的功能了,唯一的缺点就是得花钱🤪。于是我就想着在 personal assistant 插件中支持这个功能,这篇文章就介绍一下我是怎么在 Obsidian 插件中设计和构建这个能力的。

方案设计

基于众所周知的原因,ChatGPT、Claude、Stable Diffusion、DALL-E 这些最优秀的 AI 服务,在国内基本上是没发正常访问的,所以目标对象肯定是国内可用的大模型服务,同一个大模型服务既要支持文本生成,又要支持图片生成,通义千问服务可能唯一能满足我要求的了。我想这在公有云领域,这就是所谓的「Vendor Lock-in」。

personal assistant 基于通义千问大模型的 featured image 生成的工作流程如下:

  1. 完成文档的编辑
  2. 根据文档内容生成能够匹配文档内容的图片描述
  3. 根据图片描述生成图片艺术风格、技术参数、图片描述等内容
  4. 根据图片提示词生成图片
  5. 下载图片到本地并在 Obsidian 展示

效果演示

使用 AI 生成 featured image 的步骤:

  1. 需要在 Obsidian 中安装 personal assistant 插件;
  2. personal assistant 插件配置 Qwen API token(参考 README);
  3. Obsidian 文档处于 Edit View;

效果如下所示:

0:00
/1:47

后续计划

  • 类似微信公众号目前支持的听书功能,打算在 Obsidian 和 Ghost 也支持上
  • AI 自动为 vault 的笔记文档建立关联关系

小本本系列:大模型中的文本向量text embeddings

2024-12-08 19:39:43

作为人类,我们可以阅读并理解文本(至少可以理解一部分),然而计算机“用数字思考”,因此它们无法自动理解单词和句子的含义。如果我们要让计算机理解自然语言,就需要将这些信息转换成计算机可以处理的格式——数字向量。这篇文章就研究一下科学家是如何一步一步让计算机理解和认识人类的语言的。

人们早在多年前就学会了如何将文本转换为机器可理解的格式(最早的版本之一是ASCII)。这种方法有助于渲染和传输文本,但并不编码单词的意义。当时,标准的搜索技术是关键词搜索,即仅查找包含特定单词或N元语法的所有文档。

然后,在几十年后,嵌入式表示(embeddings)出现了。我们可以计算单词、句子甚至图像的嵌入式表示。嵌入式表示也是数字向量,但它们能够捕捉意义。因此,你可以使用它们进行语义搜索,甚至处理不同语言的文档。

词袋模型 bag of words

将文本转换为向量的最基本方法是词袋模型。让我们以理查德·P·费曼的一句名言为例:“我们很幸运生活在一个我们仍在不断发现的时代”。我们将使用这句话来说明词袋模型的方法。

获得词袋向量的第一步是将文本拆分为单词(标记),然后将单词还原为其基本形式。例如,“running”将转换为“run”。这个过程称为词干提取(stemming,即将文本拆分为单词并进行词干提取)。

如下代码所示,用 NLTK 实现词袋模型:

from nltk.stem import SnowballStemmer
from nltk.tokenize import word_tokenize
import nltk
import os

# Resource punkt_tab not found. Try using the NLTK Downloader
if not os.path.exists("./tokenizers/punkt_tab.zip"):
    nltk.download("punkt_tab", download_dir="./")

text = "We are lucky to live in an age in which we are still making discoveries"

# tokenization - splitting text into words
words = word_tokenize(text)
print(words)
# ['We', 'are', 'lucky', 'to', 'live', 'in', 'an', 'age', 'in', 'which',
#  'we', 'are', 'still', 'making', 'discoveries']

stemmer = SnowballStemmer(language="english")
stemmed_words = list(map(lambda x: stemmer.stem(x), words))
print(stemmed_words)
# ['we', 'are', 'lucki', 'to', 'live', 'in', 'an', 'age', 'in', 'which',
#  'we', 'are', 'still', 'make', 'discoveri']

import collections
bag_of_words = collections.Counter(stemmed_words)
print(bag_of_words)
# {'we': 2, 'are': 2, 'in': 2, 'lucki': 1, 'to': 1, 'live': 1,
# 'an': 1, 'age': 1, 'which': 1, 'still': 1, 'make': 1, 'discoveri': 1}

实际上,如果我们想将文本转换为向量,不仅要考虑文本中的单词,还要考虑整个词汇表。假设我们的词汇表中还包括“i”、“you”和“study”,我们就可以从费曼的这句名言创建一个向量。

# bag of words vector
{'we': 2, 'are': 2, 'in': 2, 'lucki': 1, 'to': 1, 'live': 1,'an': 1, 'age': 1, 'which': 1, 'still': 1, 'make': 1, 'discoveri': 1, 'i': 0, 'you': 0, 'study': 0}

这种方法相当基础,它不考虑单词的语义意义,因此句子“the girl is studying data science”和“the young woman is learning AI and ML”在向量空间中不会接近彼此。

TF-IDF 模型

词袋模型的一个稍作改进的版本是TF-IDF(Term Frequency — Inverse Document Frequency,词频-逆文档频率)。它是两个度量值的乘积。
$$TF-IDF(t,d,D) = TF(t,d) \times IDF(t, D) $$

  • 词频 (Term Frequency, TF) 表示单词在文档中出现的频率。最常见的计算方法是将该词在文档中的原始计数(类似于词袋模型)除以文档中的总词数。
    $$TF(t,d)=\frac{number\ of\ times\ term\ t\ appears\ in\ the\ document\ d}{number\ of\ terms\ in\ document\ d}$$
  • 逆文档频率 (Inverse Document Frequency, IDF) 表示单词提供的信息量。例如,像“a”或“that”这样的词不会给你关于文档主题的任何额外信息。相反,像“ChatGPT”或“生物信息学”这样的词可以帮助你确定领域(但不适用于这个句子)。它通过计算包含该词的文档数与总文档数的比例的对数来得到。IDF值越接近0,表示这个词越常见,提供的信息越少。
    $$IDF(t,D)=log(\frac{total\ number\ of\ document\ in\ corpus\ document\ D}{number\ of\ documents\ containing\ term\ t})$$

最终,我们会得到一些向量,其中常见的词(如“I”或“you”)具有较低的权重,而多次出现在文档中的罕见词则具有较高的权重。这种策略会带来稍微更好的结果,但它仍然无法捕捉语义意义。

这种方法的另一个挑战是它会产生非常稀疏的向量。向量的长度等于语料库的大小。英语中大约有47万独特的单词(来源),因此我们将得到巨大的向量。由于一个句子通常不会超过50个不同的词,因此向量中99.99%的值将是0,不携带任何信息。鉴于这一点,科学家们开始思考密集向量表示。

词语潜在信息(Latent Information)与神经网络方法(Neural Network Method)
英文语境下,当我们谈论狗(dog)时,可能会使用除了“dog”之外的其他词语。我们是否应该在我们的分类方案中考虑像“canine”(犬类)或“feline”(猫科)这样的术语?我们需要为这些词添加一个新的项,例如 (dogx, caty, caninez, felinei)。

在英语中,一个大约30,000个词的词汇表对于这种词袋模型来说效果很好。在计算机世界中,我们可以比实体图书馆更平滑地扩展这些维度,但原则上问题类似。在高维度下,事情变得难以管理。随着组合爆炸,算法运行缓慢,稀疏性(大多数文档对大多数术语的计数为0)对统计和机器学习构成了问题。

因此,要将一本书投影到潜在空间(latent space)中,我们需要一个大矩阵,定义词汇表中每个观察到的术语对每个潜在术语的贡献程度。

有几个不同的算法可以从足够大的文档集合中推断出这一点:潜在语义分析(Latent Semantic Analysis, LSA),它使用术语-文档矩阵的奇异值分解(基本上是高级线性代数),以及潜在狄利克雷分配(Latent Dirichlet Allocation, LDA),它使用一种称为狄利克雷过程的统计方法。

我们使用词频作为某种更为模糊的主题性的代理。通过将这些词频投影到嵌入空间中,我们不仅可以降低维度,还可以推断出比原始词频更好地指示主题性的潜在变量。为此,我们需要一个定义良好的算法,如LSA,它可以处理文档语料库,找到从词袋输入到嵌入空间向量的良好映射。基于神经网络的方法使我们能够推广这一过程,并突破LSA的限制。

word2vec 模型

最著名的密集表示方法之一是word2vec,这是谷歌在2013年提出的,论文标题为“Efficient Estimation of Word Representations in Vector Space”,作者是Mikolov等人。

在这篇论文中提到了两种不同的word2vec方法:

  1. Continuous Bag of Words (CBOW):在这种方法中,目标是根据上下文中的单词预测中间的单词。具体来说,给定一个单词序列,CBOW模型试图根据周围的单词预测中心单词。这种方法的优点是训练速度较快,因为每次更新都利用了更多的信息。
  2. Skip-gram:与CBOW相反,Skip-gram的目标是根据一个中心单词预测其周围的单词。也就是说,给定一个单词,模型试图预测这个单词周围的上下文单词。Skip-gram模型在捕捉罕见词的语义方面表现更好,但训练速度较慢,因为它每次更新只利用了一个单词的信息。

这两种方法都能生成词的密集向量表示,这些向量不仅包含了词的频率信息,还捕捉了词之间的语义关系。例如,使用word2vec生成的向量可以反映出“king”和“queen”之间的关系类似于“man”和“woman”之间的关系。

word2vec的成功在于它能够有效地将高维的词袋模型转化为低维的密集向量,同时保留了词的语义信息。这使得在自然语言处理任务中,如语义搜索、情感分析和机器翻译等,可以更高效地使用这些向量。

高维词向量的密集表示的核心思想是训练两个模型:编码器(encoder)和解码器(decoder)。以skip-gram模型为例,我们可以将单词“christmas”传递给编码器。编码器会生成一个向量,然后将这个向量传递给解码器,期望解码器能够输出“merry”、“to”和“you”等上下文单词。

具体步骤如下:

  1. 输入层
    • 在skip-gram模型中,输入是一个单词(例如“christmas”)。
  2. 编码器
    • 编码器将输入单词映射到一个固定长度的密集向量。这个向量通常被称为词嵌入(word embedding)。例如,如果词嵌入的维度是100,那么“christmas”会被映射到一个100维的向量。
  3. 解码器
    • 解码器接收这个100维的向量,并尝试预测该单词的上下文单词(例如“merry”、“to”、“you”)。解码器通常是一个多层神经网络,最后一层是一个softmax层,用于输出每个可能的上下文单词的概率分布。
  4. 损失函数
    • 模型的训练目标是最小化预测的上下文单词与实际上下文单词之间的差异。常用的损失函数是交叉熵损失(cross-entropy loss)。

训练过程:

  • 前向传播
    • 输入单词“christmas”通过编码器生成一个100维的向量。
    • 这个向量通过解码器,解码器输出一个概率分布,表示每个可能的上下文单词出现的概率。
  • 反向传播
    • 计算预测的上下文单词与实际上下文单词之间的损失。
    • 使用反向传播算法调整编码器和解码器的参数,以最小化损失。

通过这种方式,skip-gram模型能够生成高质量的词嵌入,这些嵌入不仅保留了词的频率信息,还捕捉了词之间的语义关系,为各种自然语言处理任务提供了强大的支持。这个模型开始考虑单词的意义,因为它是在单词的上下文中进行训练的。然而,它忽略了形态学(我们从单词部分可以获得的信息,例如,“-less”意味着缺乏某物)。这一缺点后来通过查看子词skip-grams在GloVe中得到了解决。

此外,word2vec只能处理单词,但我们希望对整个句子进行编码。因此,让我们进入下一个演进步骤,即transformers。

transformer 模型

下一次演变与 Vaswani 等人在论文“Attention is All You Need”中引入的变压器方法有关。变压器能够生成信息丰富的密集向量,并成为现代语言模型的主要技术。

变压器允许使用相同的“核心”模型,并针对不同的用例进行微调,而无需重新训练核心模型(这需要大量时间和成本)。这导致了预训练模型的兴起。其中一个早期流行的模型是Google AI的BERT(基于变压器的双向编码器表示)。

内部而言,BERT仍然像word2vec一样在词元级别上操作,但我们仍然希望获得句子嵌入。因此,一个简单的方法可能是取所有词元向量的平均值。不幸的是,这种方法的表现不佳。

这个问题在2019年随着Sentence-BERT的发布得到了解决。Sentence-BERT在语义文本相似性任务上超越了所有先前的方法,并允许计算句子嵌入。

text embeddings 计算验证

我使用阿里云大模型服务的text-embedding-v2来生成文本嵌入向量。结果,我们得到了一个1536维的浮点数向量。现在我们可以对所有数据重复这一过程,并开始分析这些值。

from dotenv import load_dotenv, find_dotenv

_ = load_dotenv(find_dotenv("./env/.env"))

import dashscope
from http import HTTPStatus
from pprint import pprint

resp = dashscope.TextEmbedding.call(
    model=dashscope.TextEmbedding.Models.text_embedding_v2,
    input="We are lucky to live in an age in which we are still making discoveries.",
    dimension=1536,
)
pprint(resp['output']) if resp.status_code == HTTPStatus.OK else print(resp)

# output
# {'embeddings': [{'embedding': [0.022378576171554372,
#                               -0.027432455162420308,
#                               -0.00355793080956962,
#                               -0.030121118785560987,
#                               ...
#                               ],
#                'text_index': 0}]}

向量距离

嵌入实际上是向量。因此,如果我们想了解两个句子之间的相似程度,可以计算它们之间向量的距离。距离越小,表示它们的语义意义越接近。

可以使用不同的度量来测量两个向量之间的距离:

  • 欧几里得距离(L2)
  • 曼哈顿距离(L1)
  • 点积
  • 余弦距离

欧几里得距离(L2)

定义两点(或向量)之间距离的最标准方法是欧几里得距离或L2范数。这种度量在日常生活中最常用,例如,当我们谈论两个城镇之间的距离时。

以下是L2距离的视觉表示和公式:

$$\text{L2距离} = \sqrt{(x_2 - x_1)^2 + (y_2 - y_1)^2 + \cdots + (z_2 - z_1)^2}$$

曼哈顿距离(L1)

另一种常用的距离是L1范数或曼哈顿距离。这种距离是以纽约的曼哈顿岛命名的。该岛的街道布局呈网格状,两个点之间的最短路径将是L1距离,因为需要沿着网格行走。

$$\text{L1距离} = \sum_{i=1}^{n} |x_i - y_i|$$

点积

另一种查看向量之间距离的方法是计算点积或标量积。以下是公式,我们可以轻松实现它:

$$\text{点积} = \vec{a} \cdot \vec{b} = |\vec{a}||\vec{b}|cos\theta= \sum_{i=1}^{n} a_i b_i$$

这种度量的解释有点棘手。一方面,它显示了向量是否指向同一方向。另一方面,结果高度依赖于向量的大小。例如,让我们计算两对向量之间的点积:

  • (1, 1) 和 (1, 1)
  • (1, 1) 和 (10, 10)

在这两种情况下,向量都是共线的,但在第二种情况下,点积大十倍:2 对 20。

余弦相似度

余弦相似度经常被使用。余弦相似度是点积除以向量的模长(或范数)的归一化结果。

让我们谈谈这种度量的物理意义。余弦相似度等于两个向量之间的夹角的余弦值。向量越接近,度量值越高。

$$\text{余弦相似度} = \frac{\vec{a} \cdot \vec{b}}{|\vec{a}| |\vec{b}|}$$

距离计算方法选择

你可以使用任何距离来比较你得到的嵌入。例如,我计算了不同聚类之间的平均距离。无论是L2距离还是余弦相似度,都展示了类似的结果:

  • 同一聚类内的对象比与其他聚类的对象更接近。解释我们的结果时需要注意,对于L2距离,更接近意味着距离更低;而对于余弦相似度,更接近的对象的度量值更高。不要混淆。
  • 我们可以发现某些主题彼此非常接近,例如,“政治”和“经济”或“人工智能”和“数据科学”。

然而,对于NLP任务,最佳实践通常是使用余弦相似度。背后的原因包括:

  • 余弦相似度的范围在-1到1之间,而L1和L2距离是没有界的,因此更容易解释。
  • 从实际角度来看,计算点积比计算欧几里得距离的平方根更有效。
  • 余弦相似度受维度诅咒的影响较小(维度越高,向量之间的距离分布越窄)。

向量可视化

理解数据的最佳方式是将其可视化。不幸的是,嵌入有1536个维度,因此直接查看数据非常困难。然而,有一种方法:我们可以使用降维技术将向量投影到二维空间中。

注意: 以下可视化数据来自 Stack Exchange Data Dump,由于数据量是92.3G,我就没有自己本地跑可视化结果,本文可视化视图来自 Mariya Mansurova 的Text Embeddings: Comprehensive Guide

PCA

最基本的降维技术是主成分分析(PCA)

t-SNE

PCA是一种线性算法,而在现实生活中大多数关系是非线性的。因此,由于非线性问题,我们可能无法很好地分离聚类。让我们尝试使用一种非线性算法——t-SNE,看看它是否能显示出更好的结果。

根据t-SNE可视化的推理

来源内容:

Is it safe to drink the water from the fountains found all over 
the older parts of Rome?

When I visited Rome and walked around the older sections, I saw many 
different types of fountains that were constantly running with water. 
Some went into the ground, some collected in basins, etc.

Is the water coming out of these fountains potable? Safe for visitors 
to drink from? Any etiquette regarding their use that a visitor 
should know about?

t-SNE嵌入:

我们可以在t-SNE可视化中找到这条内容,并发现它实际上靠近咖啡聚类。

这句话的意思是,在t-SNE可视化中,这条关于罗马喷泉水的问题被映射到了一个与咖啡相关的话题附近。这可能表明,尽管这两者看似不相关,但它们在某些方面存在一定的语义相似性,或者在数据集中它们经常一起出现。

References

  1. Text Embeddings: Comprehensive Guide | by Mariya Mansurova | Towards Data Science
  2. Transformers, Explained
  3. The illustrated transformer
  4. Sentence embeddings
  5. why should euclidean distance not be the default distance measure
  6. An intuitive introduction to text embeddings

小本本系列:大模型中的向量嵌入Vector embeddings

2024-11-30 23:16:35

Vector embeddings are a method to convert non-structured data, such as text, images, and videos, into numerical representations that capture their meanings and relationships. This allows computers, which only understand numbers, to process and interpret these data more effectively. Embeddings are crucial for tasks like semantic similarity and are used in various AI models, including LLMs, RAG, and multimodal processing.

vector embeddings 的感性理解

vector embeddings 的出现最本质的原因是科学家为了解决一个问题 —— 让只认识0、1数字,只会做逻辑运算和浮点运算的计算机,能够理解人类语言、文字、图像、视频等数据包含的语义以及它们之间的关系。例如,「泰迪」、「狗」、「犬」、「哺乳动物」这几个单词在不同的上下文中其实是同一个意思,即一种四足、有尾巴、有尖牙的哺乳动物,这就是文字、图像、视频中包括的语义信息,科学家希望计算机可以理解这些。

既然计算机只认识数字,只会做运算,科学家的想法很直接,那就我们就将人类使用的文字、句子、文章、书本、图像、视频等等非结构化的数据转换成数字来描述,帮助计算机高效的理解和处理它们,于是就有了 vector embeddings。

vector embeddings 是什么

vector embeddings(向量嵌入)是一种将单词、句子和其他数据转换为数字的方法,这些数字捕获了它们的含义和关系。它们将不同类型的数据表示为高维空间(多维空间)中的点,其中相似的数据点聚集在一起。这些数值表示有助于机器更有效地理解和处理这些数据。

向量嵌入在处理语义相似度时至关重要。vector(向量)仅仅是一系列数字;vector embedding(向量嵌入)是一系列代表输入数据的数字。通过使用 vector embeddings,我们可以结构化非结构化数据或通过将其转换为一系列数字来处理任何类型的数据。这种方法使我们能够对输入数据执行数学运算,而不是依赖定性比较。

vector V.S. embedding

在 vector embeddings (向量嵌入)的上下文中,embedding(嵌入)和 vector(向量)是同一回事。两者都指数据的数值表示,其中每个数据点都由高维空间中的向量表示。

“vector 向量”仅指具有特定维数的数字数组。在 vector embedding(向量嵌入)的情况下,这些向量表示上述任何数据点在一个连续的空间中。

“embedding 嵌入”专门指将数据表示为 vector(向量)的技术,以捕获有意义的信息、语义关系或上下文特征。embedding 嵌入旨在捕获数据的底层结构或属性,通常通过训练算法或模型来学习。

虽然嵌入和向量可以在向量嵌入的上下文中互换使用,“嵌入”强调以有意义和结构化的方式表示数据的概念,而“向量”则指数值表示本身。

如何构建 vector embeddings

vector embeddings 是深度学习模型中输入数据的内部表示,也称为嵌入模型或深度神经网络。那么,我们如何提取这些信息呢?

我们通过移除最后一层并从倒数第二层获取输出,来获得 vector 向量。神经网络的最后一层通常输出模型的预测,因此我们取倒数第二层的输出。vector embedding(向量嵌入)是馈送到神经网络预测层的数据。

vector embedding(向量嵌入)的维数等于模型中倒数第二层的尺寸,因此与向量的尺寸或长度可互换。常见的向量维数包括384(例如 Sentence Transformers Mini-LM 生成的 vector)、768(例如 Sentence Transformers MPNet 生成的 vector)、1,536(例如 OpenAI 生成的 vector)和2,048(例如 ResNet-50 生成的 vector)。

奇怪的 vector embeddings 维数 768、1024、1536、2048

最早的 GPT-2 设置中,头的数量是 12(dimension heads),它可以整除 768。

这个数字来自于超参数优化(hyperparameter optimization)。使用 4096 大小的嵌入和 1 层的神经网络,或者使用 16 大小的嵌入和 2B 参数的神经网络是没有意义的,这些值之间需要一个良好的平衡。

那么为什么是 768 而不是 769 呢?我们通常使用 2 的幂(或接近的值)来尝试超参数,因为它们在计算上更快,并且更适合 GPU 内存分配(就像你的屏幕分辨率一样,GPU 只是一个计算矩阵的大型机器)。768 = 512 + 256 = 2**9 + 2**8

vector embeddings 维度的含义

我曾经查找资料并试图弄懂 vector embeddings(向量嵌入)中每个维度的含义。最终的答案是,单个维度没有任何意义。vector embeddings 中的单个维度过于抽象,无法确定其含义。然而,当我们将所有维度放在一起时,它们提供了输入数据的语义含义。

向量的维度是不同属性的高级抽象表示。表示的属性取决于训练数据和模型本身。文本和图像模型生成不同的嵌入,因为它们针对根本不同的数据类型进行训练。即使是不同的文本模型也会生成不同的嵌入。有时它们在大小上不同;其他时候,它们在表示的属性上不同。例如,在法律数据上训练的模型将学习与在医疗保健数据上训练的模型不同的事物。

图像 vector embeddings

2012 年 AlexNet 的出现标志着图像识别技术的飞跃。自那时以来,计算机视觉领域取得了无数进展。最新的知名图像识别模型是 ResNet-50,它是一个基于前代 ResNet-34 架构的 50 层深度残差网络。

尝试使用 microsoft/resnet-50 · Hugging Face生成图像的 vector embeddings:

# Load model directly
from transformers import AutoFeatureExtractor, AutoModelForImageClassification
from PIL import Image


extractor = AutoFeatureExtractor.from_pretrained("microsoft/resnet-50")
model = AutoModelForImageClassification.from_pretrained("microsoft/resnet-50")

image = Image.open("<image path>")
# image = Resize(size=(256, 256))(image)

inputs = extractor(images=image, return_tensors="pt")
# print(inputs)

outputs = model(**inputs)
vector_embeddings = outputs[1][-1].squeeze()

文本 vector embeddings

人工智能对自然语言的处理已经从基于规则的嵌入发展到了一个新的高度。从最初的神经网络开始,我们通过 RNN 添加了递归关系来跟踪时间步长。从那时起,我们使用 Transformer 来解决序列转导问题。

Transformer 由编码器、注意力矩阵和解码器组成。编码器将输入编码为表示状态的矩阵,注意力矩阵和解码器对状态和注意力矩阵进行解码,以预测正确的下一个标记来完成输出序列。GPT 是迄今为止最流行的语言模型,它由严格的解码器组成。它们对输入进行编码并预测正确的下一个标记。

尝试使用 sentence-transformers生成文本的 vector embeddings:

from sentence_transformers import SentenceTransformer

model = SentenceTransformer("<model-name>")
vector_embeddings = model.encode(“<input>”)

多模态 vector embeddings

以图生文为例,使用开源模型 CLIP VIT 来生成 embeddings:

# Load model directly
from transformers import AutoProcessor, AutoModelForZeroShotImageClassification


processor = AutoProcessor.from_pretrained("openai/clip-vit-large-patch14")
model = AutoModelForZeroShotImageClassification.from_pretrained("openai/clip-vit-large-patch14")
from PIL import Image


image = Image.open("<image path>")
# image = Resize(size=(256, 256))(image)


inputs = extractor(images=image, return_tensors="pt")
# print(inputs)


outputs = model(**inputs)
vector_embeddings = outputs[1][-1].squeeze()

语音 vector embeddings

以语音生成文字为例,使用开源模型 Whisper 模型来获取 embedding:

import torch
from transformers import AutoFeatureExtractor, WhisperModel
from datasets import load_dataset


model = WhisperModel.from_pretrained("openai/whisper-base")
feature_extractor = AutoFeatureExtractor.from_pretrained("openai/whisper-base")
ds = load_dataset("hf-internal-testing/librispeech_asr_dummy", "clean", split="validation")
inputs = feature_extractor(ds[0]["audio"]["array"], return_tensors="pt")
input_features = inputs.input_features
decoder_input_ids = torch.tensor([[1, 1]]) * model.config.decoder_start_token_id
vector_embedding = model(input_features, decoder_input_ids=decoder_input_ids).last_hidden_state

视频 vector embeddings

视频的 embeddings 比语音、图片的 embeddings 更加复杂,它需要多模态的处理来保证语音与图片的同步,以 DeepMind 开源模型 multimodal perceiver 为例生成视频的 vector embeddings(注意代码 outputs[1][-1].squeeze()):

def autoencode_video(images, audio):
     # only create entire video once as inputs
     inputs = {'image': torch.from_numpy(np.moveaxis(images, -1, 2)).float().to(device),
               'audio': torch.from_numpy(audio).to(device),
               'label': torch.zeros((images.shape[0], 700)).to(device)}
     nchunks = 128
     reconstruction = {}
     for chunk_idx in tqdm(range(nchunks)):
          image_chunk_size = np.prod(images.shape[1:-1]) // nchunks
          audio_chunk_size = audio.shape[1] // SAMPLES_PER_PATCH // nchunks
          subsampling = {
               'image': torch.arange(
                    image_chunk_size * chunk_idx, image_chunk_size * (chunk_idx + 1)),
               'audio': torch.arange(
                    audio_chunk_size * chunk_idx, audio_chunk_size * (chunk_idx + 1)),
               'label': None,
          }
     # forward pass
          with torch.no_grad():
               outputs = model(inputs=inputs, subsampled_output_points=subsampling)


          output = {k:v.cpu() for k,v in outputs.logits.items()}
          reconstruction['label'] = output['label']
          if 'image' not in reconstruction:
               reconstruction['image'] = output['image']
               reconstruction['audio'] = output['audio']
          else:
               reconstruction['image'] = torch.cat(
                    [reconstruction['image'], output['image']], dim=1)
               reconstruction['audio'] = torch.cat(
                    [reconstruction['audio'], output['audio']], dim=1)
          vector_embeddings = outputs[1][-1].squeeze()
	 # finally, reshape image and audio modalities back to original shape
     reconstruction['image'] = torch.reshape(reconstruction['image'], images.shape)
     reconstruction['audio'] = torch.reshape(reconstruction['audio'], audio.shape)
     return reconstruction


     return None

References

  1. Motivation to use 768 dimensional embeddings from Transformers?
  2. How to Get the Right Vector Embeddings
  3. What are Vector Embeddings?