LangChain入门学习笔记(二)——LangChain表达式语言(LCEL)

基于LangChain框架编写大模型应用的过程就像垒积木,其中的积木就是Prompts,LLMs和各种OutputParser等。如何将这些积木组织起来,除了使用基本Python语法调用对应类的方法,一种更灵活的方法就是使用位于LangChain-Core层中的LCEL(LangChain Expression Language)。

使用LCEL,我们可以获得流式支持、异步和并行执行支持,同时提供了中间结果访问、验证输入输出模式及无缝的LangSmith追踪和LangServe部署。通过这些能力,LCEL为用户提供了一种高效、可靠和易于维护的方式来构建和部署复杂的LLM应用程序。

在前一篇文章《LangChain入门学习笔记(一)——Hello World》中

chain = prompt | llm | output_parser

这一行的代码跟其他行Python语法不同,是的,它就是使用了LCEL进行的编写。使用LCEL的好处可以参考官方文档的这篇文章。

Runnable接口

LCEL的基础是Runnable接口。通过实现Runnable接口,LCEL定义了一组具有通用调用方式的方法集。LangChain代码中定义的Runnable如下:

它有一系列的子类,比如PromptTemplate、LLM和StrOutputParser(还有更多),这些组件子类都间接继承自Runnable(继承自RunnableSequence,而后者又继承自Runnable)。

Runnable的__or__()方法重新定义了"|"语法,所以基于LCEL的chain就能通过或(也类似shell中的管道)操作符号"|"串起来。这也就是前面提到的“chain = prompt | llm | output_parser”这行代码虽然看上去跟普通Python不一样,但它是合法的,原因就在这里。__or__()的定义如下:

def __or__(self,other: Union[Runnable[Any, Other],Callable[[Any], Other],Callable[[Iterator[Any]], Iterator[Other]],Mapping[str, Union[Runnable[Any, Other], Callable[[Any], Other], Any]],],
) -> RunnableSerializable[Input, Other]:"""Compose this runnable with another object to create a RunnableSequence."""return RunnableSequence(self, coerce_to_runnable(other))

可以看出通过"|"(__or__()重定义的管道操作)前后两个组件合成一个Runnable的子孙类RunnableSequence对象返回,从而达到串起来形成链(chain)的目的。

此外,Runnable还定义了一系列的标准方法,方便统一使用这套标准的定义:

stream/astream: 同步/异步流式化组件响应。

invoke/ainvoke: 同步/异步接收输入并调用链(chain)。

batch/abatch: 同步/异步接收批量输入并调用链(chain)。

input_schema/output_schema: 描述Runnable的输入/输出的结构,用于类型检查和可视化调试。

继承自Runnable的各类组件,输入输出类型如下表所示:

组件输入类型输出类型
Prompt字典PromptValue
ChatModel单个String,ChatMessage列表,或者PromptValueChatMessage
LLM单个String,ChatMessage列表,或者PromptValueString
OutputParserLLM或ChatModel的输出根据所使用的Parser确定
Retriever单个StringDocument列表
Tool依赖所使用的Tool,可能是单个String也可能是字典依赖所使用的Tool

LCEL的基元操作

LangChain表达式语言LCEL包含了一些基础的操作,称为基元操作(Primitives)。

串形化(Sequences)

如前介绍Runnable时所述,通过管道操作符"|",将前一个Runnable组件的RunnableSequence输出结果作为下一个Runnable组件的输入,以此类推形成一个chain,再调用.invoke()方法发生“链式反应”得到最终输出。

下面用代码举例,为了国内访问方便,未使用官网的OpenAI接口,取而代之的是阿里的百炼,接入方法见阿里云文档《如何集成langchain》。

首先安装对应的Python模块:

pip install langchain-bailian

阿里云账号授权及环境变量设置,可以参考这篇文章《搭建访问阿里云百炼大模型环境》。然后可以编写Python代码如下进行调用:

import os
from langchain_core.output_parsers import StrOutputParser
from langchain_core.prompts import ChatPromptTemplate
from langchain_bailian import Bailian# 构建prompt
prompt = ChatPromptTemplate.from_template("给我讲一个关于 {topic}的笑话")# 构建LLM模型
# 需要在环境变量设置ACCESS_KEY_ID、ACCESS_KEY_SECRET、AGENT_KEY和APP_ID
access_key_id = os.environ.get("ACCESS_KEY_ID")
access_key_secret = os.environ.get("ACCESS_KEY_SECRET")
agent_key = os.environ.get("AGENT_KEY")
app_id = os.environ.get("APP_ID")llm = Bailian(access_key_id=access_key_id,access_key_secret=access_key_secret,agent_key=agent_key,app_id=app_id)chain = prompt | llm | StrOutputParser()print(chain.invoke({"topic": "人才"}))

上面的"chain = prompt | llm | StrOutputParser()"这一行代码能够串联运行,是因为:

  • prompt和llm以及StrOutputParser都是Runnable的子类,重定义了"|"操作符;
  • 前一个的输出类型和后一个的输入类型相同;

调用chain的invode方法,输出结果如下:

还可以将上面的chain作为另一个链中的一个输入,这需要用到一种称作强制转换(Coercion)的概念。

比如我们再假设需要构造一个链来评估前面生成的笑话故事,可以在前面代码后面添加:

analysis_pompt = ChatPromptTemplate.from_template("这个笑话好笑吗?{joke}")
composed_chain = {"joke": chain} | analysis_pompt | llm | StrOutputParser()
print(composed_chain.invoke({"topic": "人才"}))

这里{"joke": chain}会被自动解析成RunnableParallel,并行运行chain后返回一个包含结果的字典作为analysis_prompt的输入,而后者的输入类型正是字典类型,所以整个组合链成功。运行结果如下:

当然也可以使用代码完成上面的串形化,只是看上去不那么“美观”,例子如下:

from langchain_core.runnables import RunnableParallelcomposed_chain_with_pipe = (RunnableParallel({"joke": chain}).pipe(analysis_prompt).pipe(model).pipe(StrOutputParser())
)

显然使用"|"更显得简单明了。

并行化(Parallelism)

LangChain的基本形态是一条链将每个组件串起来处理,如果涉及到不同任务需要不同的链处理的话,就需要应用并行化能力。这里的并行不是新起线程进程的并发,而是执行多个链,在不同链之间是同时进行而又相互有关联。

假设这么一个场景:我们需要一个LLM给我们讲笑话,另一个LLM作诗,可以这么编写:

import os
from langchain_core.prompts import ChatPromptTemplate
from langchain_bailian import Bailian
from langchain_core.runnables import RunnableParallel# 构建LLM模型
# 需要在环境变量设置ACCESS_KEY_ID、ACCESS_KEY_SECRET、AGENT_KEY和APP_ID
access_key_id = os.environ.get("ACCESS_KEY_ID")
access_key_secret = os.environ.get("ACCESS_KEY_SECRET")
agent_key = os.environ.get("AGENT_KEY")
app_id = os.environ.get("APP_ID")llm = Bailian(access_key_id=access_key_id,access_key_secret=access_key_secret,agent_key=agent_key,app_id=app_id)# 一个链讲关于给定topic的笑话
joke_chain = ChatPromptTemplate.from_template("给我讲一个关于{topic}的笑话") | llm
# 一个链作关于给定topic的诗
poem_chain = ChatPromptTemplate.from_template("写一首关于{topic}的两行诗") | llm
# 生成并行处理的两个链
map_chain = RunnableParallel(joke=joke_chain, poem=poem_chain)print(map_chain.invoke({"topic": "人才"}))

通过RunnableParallel对象生成了包含两个chain的并行处理链,返回包含键值分别为"joke"和"poem"的map对象:

参数传递

创建好chain之后,它处理的输入数据不可能固定不变,如何将外部输入传给chain并保持不变就需要RunnablePassthrough;在chain处理的过程中可能会产生新的数据传给下游组件使用,这时需要RunnablePassthrough.assign()静态方法传给指定函数。

“直通车”RunnablePassthrough

RunnablePassthrough能保持你传入的输入不变,可以简单理解它是输入的变量符。

看官方文档例子:

from langchain_core.runnables import RunnableParallel, RunnablePassthroughrunnable = RunnableParallel(passed=RunnablePassthrough(),modified=lambda x: x["num"] + 1,
)print(runnable.invoke({"num": 1}))

对于passed,被RunnablePassthrough直接不变的赋予了传入的{"num": 1},对于modified则通过lambda表达式取得输入的"num"键值并加1,所以返回了2。最终的结果如下:

添加字段的“Assign”

在实际应用中,我们可能会产生一些中间计算的值——也许是业务计算结果或者设置观测用的值。这时我们使用RunnablePassthrough.assign()静态方法将这些值添加到一些设定的变量上。来看官方文档例子:

from langchain_core.runnables import RunnableParallel, RunnablePassthroughrunnable = RunnableParallel(extra=RunnablePassthrough.assign(mult=lambda x: x["num"] * 3),modified=lambda x: x["num"] + 1,
)print(runnable.invoke({"num": 1}))

我们先看这段代码的返回:

对比前面“RunnablePassthrough”一节的输出,我们通过RunnablePassthrough.assign()给输入的字段新增了mult的键值对。

处理逻辑的控制

在实际处理中,除了数据的变化之外,我们可能对处理链的逻辑也要做到控制。

自定义处理函数

可能我们需要对输入的prompt进行一些自定义的处理,这时候将自定义的处理逻辑函数作为传参对象传入RunnableLambda,不过要注意的是目前RunnableLambda只接受一个参数的函数定义。具体例子见下面,其中"b"的输入“{"text1": itemgetter("foo"), "text2": itemgetter("bar")}”封装为一个参数传给multiple_length_function,后者其实需要的参数是两个:

import os
from operator import itemgetterfrom langchain_bailian import Bailian
from langchain_core.prompts import ChatPromptTemplate
from langchain_core.runnables import RunnableLambdadef length_function(text):return len(text)def _multiple_length_function(text1, text2):return len(text1) * len(text2)def multiple_length_function(_dict):return _multiple_length_function(_dict["text1"], _dict["text2"])prompt = ChatPromptTemplate.from_template("what is {a} + {b}")access_key_id = os.environ.get("ACCESS_KEY_ID")
access_key_secret = os.environ.get("ACCESS_KEY_SECRET")
agent_key = os.environ.get("AGENT_KEY")
app_id = os.environ.get("APP_ID")model = Bailian(access_key_id=access_key_id,access_key_secret=access_key_secret,agent_key=agent_key,app_id=app_id)chain1 = prompt | model# 传入的Json字段值经过RunnableLambda处理
# "a"通过itemgetter("foo")获得"hello",再经过length_function处理,结果是 "a": 5
# "b"通过itemgetter得到{"text1":"hello", "text2":"world!",再经过multiple_length_function处理,结果是"b": 30
# "b"的处理有个注意事项,RunnableLambda接受的函数是只能有一个参数,所以这里的multiple_length_function是个接受封装了text1和text2的对象
chain = ({"a": itemgetter("foo") | RunnableLambda(length_function),"b": {"text1": itemgetter("foo"), "text2": itemgetter("bar")} | RunnableLambda(multiple_length_function),}| prompt| model
)# "a": 5; "b": 30, 所以传入的prompt是"what is 5 + 30",结果是35。
print(chain.invoke({"foo": "hello", "bar": "world!"}))

最终输出:

RunnableLambda还能使用RunnableConfig传递一些回调、标签和其他配置信息。使用样例可以参看这里的“Accepting a Runnable Config”一节。

配置Prompt和LLM的字段属性

LLM的模型参数

有时候我们需要提供用户一些选择,让他们能够指定提示语或者模型的参数,这里使用configurable_fields方法。(由于bailian中的temperature在平台的应用中心配置,没有看到可以通过代码配置的方法,下面采用Ollama本地化部署llama3进行实验。具体方法可以参考前一篇文章这里的“LangChain本地运行LLM”部分。)

from langchain_community.llms import Ollama
from langchain_core.prompts import PromptTemplate
from langchain_core.runnables import ConfigurableField# 通过configurable_fields设置temperature为可配置字段
model = Ollama(model="llama3", temperature=0).configurable_fields(temperature=ConfigurableField(id="llm_temperature",name="LLM Temperature",description="The temperature of the LLM",)
)prompt = PromptTemplate.from_template("pick a random number above {x}")chain = prompt | model
# 使用初始化时temperature为0的配置
print(chain.invoke({"x": 0}))# 根据id设置参数temperature为0.99
print(chain.with_config(configurable={"llm_temperature": 0.99}).invoke({"x": 0}))

从上面代码中,我们通过configurable_fields设置了temperature为可配置的变量字段,指定id为"llm_temperature"。后面在使用时,通过with_config方法的configurable参数指定这个id进行赋值(比如例子中将初始化设置的0改为了0.99)。多次执行可以发现temperature在0.99时明显比为0时更随机些,说明设置有效:

prompt的模版选择

有时候我们可以复用一些现成的prompt模板,在不同场景下选择不同的prompt库进行加载。这时候选择使用HubRunnable的configurable_fields方法(继承自RunnableSerializable)进行参数设置:

from langchain.runnables.hub import HubRunnable
from langchain_core.runnables import ConfigurableField# 使用了"rlm/rag-prompt"的模板
prompt = HubRunnable("rlm/rag-prompt").configurable_fields(owner_repo_commit=ConfigurableField(id="hub_commit",name="Hub Commit",description="The Hub commit to pull from",)
)
# 打印"rlm/rag-prompt"的模板被赋值后的ChatPromptValue内容
print(prompt.invoke({"question": "foo", "context": "bar"}))# 使用了"rlm/rag-prompt-llama"的模板,打印被赋值后的内容
print(prompt.with_config(configurable={"hub_commit": "rlm/rag-prompt-llama"}).invoke({"question": "foo", "context": "bar"}
))# prompt在调用with_config之后,还是原来的对象
# 下面两个print方法打印结果后可以发现new_prompt用的"rlm/rag-prompt-llama", prompt还是用的"rlm/rag-prompt"
new_prompt = prompt.with_config(configurable={"hub_commit": "rlm/rag-prompt-llama"})
print(new_prompt.invoke({"question": "foo", "context": "bar"}))print(prompt.invoke({"question": "foo", "context": "bar"}))

输出的结果是:

HubRunnable传入的参数"rlm/rag-prompt"是prompt模板路径,具体内容可以参考仓库地址:

  • "rlm/rag-prompt":LangSmithicon-default.png?t=N7T8https://smith.langchain.com/hub/rlm/rag-prompt
  • "rlm/rag-prompt-llama":LangSmithicon-default.png?t=N7T8https://smith.langchain.com/hub/rlm/rag-prompt-llama其他模板可以访问下面地址查询:LangSmithicon-default.png?t=N7T8https://smith.langchain.com/hub
备选prompt设置

通过HubRunnable我们可以从prompt的模板仓库中选择prompt模板进行提示语的生成。另一种提供多种prompt的方法是通过configurable_alternatives方法(继承自RunnableSerializable)给prompt赋予多个prompt备选。我们来看下面例子:

from langchain_community.llms import Ollama
from langchain_core.prompts import PromptTemplate
from langchain_core.runnables import ConfigurableFieldmodel = Ollama(model="llama3", temperature=0)
prompt = PromptTemplate.from_template("Tell me a joke about {topic}"
).configurable_alternatives(# 指定id,在后面设置配置值时进行引用ConfigurableField(id="prompt"),# 默认key,在后面设置时用这里的值"joke"指定前面的prompt “Tell me a joke about {topic}"default_key="joke",# 添加一个prompt选项,命名为"poem",后面用"poem"指定时将使用这里的promptpoem=PromptTemplate.from_template("Write a short poem about {topic}"),# 添加一个prompt选项,命名为"poem",后面用"poem"指定时将使用这里的promptessay=PromptTemplate.from_template("Write a short essay about {topic}"),# 后面还可以继续添加
)
chain = prompt | model# 直接使用chain中默认定义的prompt,写个笑话
print(chain.invoke({"topic": "bears"}))print("************")
# 切换到"poem"这个prompt, 写一首诗
print(chain.with_config(configurable={"prompt": "poem"}).invoke({"topic": "bears"}))print("************")
# 切换到"essay"这个prompt, 写一篇短文
print(chain.with_config(configurable={"prompt": "essay"}).invoke({"topic": "bears"}))print("************")
# 切换到"joke"默认key, 写个笑话。这里明确指定名称,其实可以不指定,直接就是使用了默认prompt
# 如果在configurable_alternatives方法中没有指定default_key值(在这里指定了"joke"),那么"prompt"的值就是"default"
print(chain.with_config(configurable={"prompt": "joke"}).invoke({"topic": "bears"}))

通过configurable_alternatives的设置,我们提供了默认的"joke"(使用定义时from_template方法中指定的prompt), "poem","essay"三个prompt选项。如果需要使用哪个prompt,通过with_config方法给ConfigurableField中指定的id赋值即可。上面代码输出的结果如下:

备选LLM设置

通过configurable_alternatives方法,我们还能设置备选的大模型。使用方式和prompt类似,在初始化大模型实例时调用configurable_alternatives方法提供多个备选大模型。

import osfrom langchain_bailian import Bailian
from langchain_community.llms import Ollama
from langchain_core.prompts import PromptTemplate
from langchain_core.runnables import ConfigurableFieldaccess_key_id = os.environ.get("ACCESS_KEY_ID")
access_key_secret = os.environ.get("ACCESS_KEY_SECRET")
agent_key = os.environ.get("AGENT_KEY")
app_id = os.environ.get("APP_ID")model = Ollama(model="llama3", temperature=0).configurable_alternatives(ConfigurableField(id="llm"),default_key="llama3",bailian=Bailian(access_key_id=access_key_id,access_key_secret=access_key_secret,agent_key=agent_key,app_id=app_id)
)
prompt = PromptTemplate.from_template("Tell me a joke about {topic}"
)
chain = prompt | model# 使用llama3写个笑话
print(chain.invoke({"topic": "bears"}))print("************")
# 切换到使用百炼写个笑话
print(chain.with_config(configurable={"llm": "bailian"}).invoke({"topic": "bears"}))print("************")
# 切换回llama3写个笑话
print(chain.with_config(configurable={"llm": "llama3"}).invoke({"topic": "bears"}))

在上面代码中,定义了llama3的大模型实例,并且定义了bailian作为备选模型。后面代码通过指定"llm"的值来切换使用的LLM。执行结果:

上面备选prompt和llm设置好之后,可以同时选择备选prompt和llm,比如下面代码同时指定了prompt使用"poem"这个来生成一首诗,使用的llm是"llama3":

# 在chain中同时指定prompt和llm
chain.with_config(configurable={"prompt": "poem", "llm": "llama3"}).invoke({"topic": "bears"}
)

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

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

相关文章

SwiftUI 6.0(Xcode 16)全新 @Entry 和 @Previewable 宏让开发妙趣横生

概览 如火如荼的 WWDC 2024 已进入第五天,苹果开发平台中众多海量新功能都争先恐后的喷薄欲出。 在这里就让我们从中挑两个轻松有趣的新功能展示给小伙伴们吧:它们分别是 全新的 Entry 和 Previewable 宏。 在本篇博文中,您将学到如下内容&a…

【C++ 11 新特性】lambda 表达式详解

文章目录 1. 常见 lambda 面试题🖊 1. 常见 lambda 面试题🖊 🍎① 如果⼀个 lambda 表达式作为参数传递给⼀个函数,那这个函数可以使⽤这个 lambda 表达式捕获的变量吗 ? 🐧 函数本身无法直接访问到 lambda表达式捕获…

vue3实现表格的分页以及确认消息弹窗

表格的分页实例展示 效果1:表格按照每行10条数据分页,且编号也会随之分页自增 实现按照页码分页效果 第二页 展示编号根据分页自动增长 固定表格高度 这边设置了滚动条,同时表格高度实现自适应滚动条高度 template部分 表格代码 编号是按照页码条数进行循环并根据索引自增…

力扣191. 位1的个数

Problem: 191. 位1的个数 文章目录 题目描述思路复杂度Code 题目描述 思路 题目规定数值的范围不会超过32位整形数 1.定义统计个数的变量oneCount;由于每次与给定数字求与的变量mask初始化为1 2.for循环从0~32,每一次拿mask与给定数字求与运算&#xff…

【Linux应用】Linux系统的设备管理——Udev

1.udev概述 udev是 Linux2.6内核里的一个功能,它替代了原来的 devfs,成为当前 Linux 默认的设备管理工具,能够根据系统中的硬件设备的状态动态更新设备文件,包括设备文件的创建,删除等。 udev以守护进程的形式运行&am…

YOLOv10的使用总结

目录 YOLOv10介绍 部署和使用示例 微调训练 YOLO模型因其在计算成本和检测性能之间的平衡而在实时目标检测中很受欢迎。前几天YOLOv10也刚刚发布了。我们这篇文章就来看看YOLOv10有哪些改进,如何部署,以及微调。 概述 实时物体检测旨在以较低的延迟准…

CSS 实现电影信息卡片

CSS 实现电影信息卡片 效果展示 CSS 知识点 CSS 综合知识运用 页面整体布局 <div class"card"><div class"poster"><img src"./poster.jpg" /></div><div class"details"><img src"./avtar…

这 10 种架构师,不合格!

大家好&#xff0c;我是君哥。 架构师这个岗位是好多程序员努力的方向&#xff0c;尤其是刚毕业的时候&#xff0c;对架构师有一种崇拜感。毕竟从初级到架构要经历好几次级别飞跃。 工作时间久了&#xff0c;发现架构师这个岗位&#xff0c;其实定义非常广泛&#xff0c;根据工…

Element-ui中Table表格无法显示

Element-ui中Table表格无法显示 在使用过程中发现样式正常显示但是table就是不显示&#xff0c;研究了一段时间后&#xff0c;发现问题是项目结构的问题 当你创建vue和安装el的时候&#xff0c;一定要注意进入到正确的项目文件夹&#xff0c;如果在外面也出现一个package.jso…

【iOS】UI学习——cell的复用及自定义cell

目录 前言cell的复用手动&#xff08;非注册&#xff09;自动&#xff08;注册&#xff09; 自定义cell总结 前言 Cell复用和自定义Cell是在开发iOS应用时常见的一种优化技巧和定制需求。   Cell复用是UITableView或UICollectionView的一个重要优化机制。当用户滚动这些视图时…

图解 Twitter 架构图

写在前面 两年前&#xff0c;马老板收购了twitter&#xff0c;并且做了一系列的大动作。那么今天我们来看一下这个全球最火的软件之一的架构。 Twitter解析 开始之前&#xff0c;我先提前说明一下&#xff0c;我之前不是做搜推广的&#xff0c;所以对这些了解不是很深&…

实战项目: 负载均衡

0. 前言 这个项目使用了前后端,实现一个丐版的LeetCode刷题网站,并根据每台主机的实际情况,选择对应的主机,负载均衡的调度 0.1 所用技术与开发环境 所用技术: C STL 标准库 Boost 准标准库 ( 字符串切割 ) cpp- httplib 第三方开源网络库 ctemplate 第三方开源前端网…

【Java】已解决:java.lang.OutOfMemoryError: Java heap space

文章目录 一、问题分析背景二、可能出错的原因三、错误代码示例四、正确代码示例五、注意事项 已解决Java&#xff1a;java.lang.OutOfMemoryError: Java heap space 一、问题分析背景 在Java开发过程中&#xff0c;有时我们会遇到java.lang.OutOfMemoryError: Java heap spa…

容器镜像外网同步方案

目录 一、目的 二、安装nexus 1、购买香港云主机​编辑 2、安装nexus 3、启动nexus 服务 4、放行安全组 三、配置nexus 1、登录nexus管理页面 2、修改nexus密码 3、创建 Blob 存储空间(可选) 4、创建 镜像代理仓库 5、Realms配置 四、拉取镜像 1、配置docker 2、…

Floyd-Warshall

应用场景 要求出每两点之间的最短路。或判断两点之间的连通性&#xff08;两点之间是否有路径&#xff09;。 板子 代码&#xff08;必背!!!&#xff09; for(int k 1; k < n; k)for(int i 1; i < n; i)for(int j 1; j < n; j)d[i][j] min(d[i][j], d[i][k] …

C数据结构:排序

目录 冒泡排序 选择排序 堆排序 插入排序 希尔排序 快速排序 hoare版本 挖坑法 前后指针法 快速排序优化 三数取中法 小区间优化 快速排序非递归 栈版本 队列版本 归并排序 归并排序非递归 ​编辑 计数排序 各排序时间、空间、稳定汇总 冒泡排序 void Bub…

内存-VSS、RSS、PSS、USS

一、 VSS 虚拟耗用内存大小&#xff0c;是进程可以访问的所有虚拟内存的总量&#xff0c;包括进程独自占用的物理内存、和其他进程共享的内存、分配但未使用的内存。 RSS 驻留内存大小&#xff0c;是进程当前实际占用的物理内存大小&#xff0c;包括进程独自占用的物理内存、…

【Java】图的初识

文章目录 【Java】图的初识图是什么图的基本组成部分图的类型图的表示方法图的常见操作 Java中图的表示方法邻接矩阵邻接表 常见操作图的遍历深度优先搜索&#xff08;DFS&#xff09;广度优先搜索&#xff08;BFS) 结论 【Java】图的初识 图是什么 图是一种数学概念&#xf…

Unity 使用TextMeshPro实现图文混排

最后实现出的效果是这样的 开始实现 准备两张图 选中图片右键->Create->TextMeshPro->Sprite Asset 然后文件夹内就会出现一个同名的这个文件 新建一个Text Inspector面板 点击最底下的Extra Settings 然后把刚刚创建的SpriteAsset拖过来 放到对应的地方 然后…

富瀚微FH8322 ISP图像调试—BLC校正

1、简单介绍 目录 1、简单介绍 2、调试方法 3、输出结果 富瀚微平台调试有一段时间了&#xff0c;一直没有总结&#xff0c;我们调试ISP的时候&#xff0c;首先一步时确定好sensor的黑电平值&#xff0c;黑电平如果不准&#xff0c;则会影响到后面的颜色及对比度相关模块。…