【LangChain学习之旅】—(11) 记忆:通过Memory记住用户上次的对话细节

news2024/9/22 14:29:31

【LangChain学习之旅】—(11) 记忆:通过Memory记住客户上次买花时的对话细节

  • 使用 ConversationChain
  • 使用 ConversationBufferMemory
  • 使用 ConversationBufferWindowMemory
  • 使用 ConversationSummaryMemory
  • 使用 ConversationSummaryBufferMemory
  • 总结时刻

Reference:LangChain 实战课

在默认情况下,无论是 LLM 还是代理都是无状态的,每次模型的调用都是独立于其他交互的。也就是说,我们每次通过 API 开始和大语言模型展开一次新的对话,它都不知道你其实昨天或者前天曾经和它聊过天了。你肯定会说,不可能啊,每次和 ChatGPT 聊天的时候,ChatGPT 明明白白地记得我之前交待过的事情。
在这里插入图片描述

的确如此,ChatGPT 之所以能够记得你之前说过的话,正是因为它使用了记忆(Memory)机制,记录了之前的对话上下文,并且把这个上下文作为提示的一部分,在最新的调用中传递给了模型。在聊天机器人的构建中,记忆机制非常重要。

使用 ConversationChain

不过,在开始介绍 LangChain 中记忆机制的具体实现之前,先重新看一下我们上一节课曾经见过的 ConversationChain。

这个 Chain 最主要的特点是,它提供了包含 AI 前缀和人类前缀的对话摘要格式,这个对话格式和记忆机制结合得非常紧密。让我们看一个简单的示例,并打印出 ConversationChain 中的内置提示模板,你就会明白这个对话格式的意义了。

from langchain import OpenAI
from langchain.chains import ConversationChain

# 初始化大语言模型
llm = OpenAI(
    temperature=0.5,
    model_name="gpt-3.5-turbo-instruct"
)

# 初始化对话链
conv_chain = ConversationChain(llm=llm)

# 打印对话的模板
print(conv_chain.prompt.template)

输出:

The following is a friendly conversation between a human and an AI. The AI is talkative and provides lots of specific details from its context. If the AI does not know the answer to a question, it truthfully says it does not know.

Current conversation:
{history}
Human: {input}
AI:

这里的提示为人类(我们)和人工智能(text-davinci-003)之间的对话设置了一个基本对话框架:这是人类和 AI 之间的友好对话。AI 非常健谈并从其上下文中提供了大量的具体细节。 (The following is a friendly conversation between a human and an AI. The AI is talkative and provides lots of specific details from its context. )

同时,这个提示试图通过说明以下内容来减少幻觉,也就是尽量减少模型编造的信息:

如果 AI 不知道问题的答案,它就会如实说它不知道。”(If the AI does not know the answer to a question, it truthfully says it does not know.)
之后,我们看到两个参数 {history} 和 {input}。

  • {history} 是存储会话记忆的地方,也就是人类和人工智能之间对话历史的信息。

  • {input} 是新输入的地方,你可以把它看成是和 ChatGPT 对话时,文本框中的输入。

这两个参数会通过提示模板传递给 LLM,我们希望返回的输出只是对话的延续。
在这里插入图片描述
那么当有了 {history} 参数,以及 Human 和 AI 这两个前缀,我们就能够把历史对话信息存储在提示模板中,并作为新的提示内容在新一轮的对话过程中传递给模型。—— 这就是记忆机制的原理。

下面就让我们来在 ConversationChain 中加入记忆功能。

使用 ConversationBufferMemory

在 LangChain 中,通过 ConversationBufferMemory(缓冲记忆)可以实现最简单的记忆机制。

下面,我们就在对话链中引入 ConversationBufferMemory。

from langchain import OpenAI
from langchain.chains import ConversationChain
from langchain.chains.conversation.memory import ConversationBufferMemory

# 初始化大语言模型
llm = OpenAI(
    temperature=0.5,
    model_name="gpt-3.5-turbo-instruct")

# 初始化对话链
conversation = ConversationChain(
    llm=llm,
    memory=ConversationBufferMemory()
)

# 第一天的对话
# 回合1
conversation("我姐姐明天要过生日,我需要一束生日花束。")
print("第一次对话后的记忆:", conversation.memory.buffer)

输出:

第一次对话后的记忆: 
Human: 我姐姐明天要过生日,我需要一束生日花束。
AI:  哦,你姐姐明天要过生日,那太棒了!我可以帮你推荐一些生日花束,你想要什么样的?我知道有很多种,比如玫瑰、康乃馨、郁金香等等。

在下一轮对话中,这些记忆会作为一部分传入提示。

# 回合2
conversation("她喜欢粉色玫瑰,颜色是粉色的。")
print("第二次对话后的记忆:", conversation.memory.buffer)

输出:

第二次对话后的记忆: 
Human: 我姐姐明天要过生日,我需要一束生日花束。
AI:  哦,你姐姐明天要过生日,那太棒了!我可以帮你推荐一些生日花束,你想要什么样的?我知道有很多种,比如玫瑰、康乃馨、郁金香等等。
Human: 她喜欢粉色玫瑰,颜色是粉色的。
AI:  好的,那我可以推荐一束粉色玫瑰的生日花束给你。你想要多少朵?我可以帮你定制一束,比如说十朵、二十朵或者更多?

下面,我们继续对话,同时打印出此时提示模板的信息。

# 回合3 (第二天的对话)
conversation("我又来了,还记得我昨天为什么要来买花吗?")
print("/n第三次对话后时提示:/n",conversation.prompt.template)
print("/n第三次对话后的记忆:/n", conversation.memory.buffer)

模型输出:

Human: 我姐姐明天要过生日,我需要一束生日花束。
AI:  哦,你姐姐明天要过生日,那太棒了!我可以帮你推荐一些生日花束,你想要什么样的?我知道有很多种,比如玫瑰、康乃馨、郁金香等等。
Human: 她喜欢粉色玫瑰,颜色是粉色的。
AI:  好的,那我可以推荐一束粉色玫瑰的生日花束给你,你想要多少朵?
Human: 我又来了,还记得我昨天为什么要来买花吗?
AI:  是的,我记得你昨天来买花是因为你姐姐明天要过生日,你想要买一束粉色玫瑰的生日花束给她。

实际上,这些聊天历史信息,都被传入了 ConversationChain 的提示模板中的 {history} 参数,构建出了包含聊天记录的新的提示输入。

有了记忆机制,LLM 能够了解之前的对话内容,这样简单直接地存储所有内容为 LLM 提供了最大量的信息,但是新输入中也包含了更多的 Token(所有的聊天历史记录),这意味着响应时间变慢和更高的成本。而且,当达到 LLM 的令牌数(上下文窗口)限制时,太长的对话无法被记住(对于 text-davinci-003 和 gpt-3.5-turbo,每次的最大输入限制是 4096 个 Token)。

下面我们来看看针对 Token 太多、聊天历史记录过长的一些解决方案。

使用 ConversationBufferWindowMemory

说到记忆,我们人类的大脑也不是无穷无尽的。所以说,有的时候事情太多,我们只能把有些遥远的记忆抹掉。毕竟,最新的经历最鲜活,也最重要。

ConversationBufferWindowMemory 是缓冲窗口记忆,它的思路就是只保存最新最近的几次人类和 AI 的互动。因此,它在之前的“缓冲记忆”基础上增加了一个窗口值 k。这意味着我们只保留一定数量的过去互动,然后“忘记”之前的互动。

下面看一下示例。

from langchain import OpenAI
from langchain.chains import ConversationChain
from langchain.chains.conversation.memory import ConversationBufferWindowMemory

# 创建大语言模型实例
llm = OpenAI(
    temperature=0.5,
    model_name="gpt-3.5-turbo-instruct")

# 初始化对话链
conversation = ConversationChain(
    llm=llm,
    memory=ConversationBufferWindowMemory(k=1)
)

# 第一天的对话
# 回合1
result = conversation("我姐姐明天要过生日,我需要一束生日花束。")
print(result)
# 回合2
result = conversation("她喜欢粉色玫瑰,颜色是粉色的。")
# print("\n第二次对话后的记忆:\n", conversation.memory.buffer)
print(result)

# 第二天的对话
# 回合3
result = conversation("我又来了,还记得我昨天为什么要来买花吗?")
print(result)

第一回合的输出:

{'input': '我姐姐明天要过生日,我需要一束生日花束。', 
'history': '',
 'response': ' 哦,你姐姐明天要过生日!那太棒了!你想要一束什么样的花束呢?有很多种类可以选择,比如玫瑰花束、康乃馨花束、郁金香花束等等,你有什么喜欢的吗?'}

第二回合的输出:

{'input': '她喜欢粉色玫瑰,颜色是粉色的。', 
'history': 'Human: 我姐姐明天要过生日,我需要一束生日花束。\nAI:  哦,你姐姐明天要过生日!那太棒了!你想要一束什么样的花束呢?有很多种类可以选择,比如玫瑰花束、康乃馨花束、郁金香花束等等,你有什么喜欢的吗?', 
'response': ' 好的,那粉色玫瑰花束怎么样?我可以帮你找到一束非常漂亮的粉色玫瑰花束,你觉得怎么样?'}

第三回合的输出:

{'input': '我又来了,还记得我昨天为什么要来买花吗?', 
'history': 'Human: 她喜欢粉色玫瑰,颜色是粉色的。\nAI:  好的,那粉色玫瑰花束怎么样?我可以帮你找到一束非常漂亮的粉色玫瑰花束,你觉得怎么样?', 
'response': '  当然记得,你昨天来买花是为了给你喜欢的人送一束粉色玫瑰花束,表达你对TA的爱意。'}

在给定的例子中,设置 k=1,这意味着窗口只会记住与 AI 之间的最新的互动,即只保留上一次的人类回应和 AI 的回应。

在第三个回合,当我们询问“还记得我昨天为什么要来买花吗?”,由于我们只保留了最近的互动(k=1),模型已经忘记了正确的答案。所以,虽然它说记得,但只能模糊地说出“喜欢的人”,而没有说关键字“姐姐”。不过,如果(我是说如果哈)在第二个回合,模型能回答“我可以帮你为你姐姐找到…”,那么,尽管我们没有第一回合的历史记录,但凭着上一个回合的信息,模型还是有可能推断出昨天来的人买花的真实意图。

尽管这种方法不适合记住遥远的互动,但它非常擅长限制使用的 Token 数量。如果只需要记住最近的互动,缓冲窗口记忆是一个很好的选择。但是,如果需要混合远期和近期的互动信息,则还有其他选择。

使用 ConversationSummaryMemory

上面说了,如果模型在第二轮回答的时候,能够说出“我可以帮你为你姐姐找到…”,那么在第三轮回答时,即使窗口大小 k=1,还是能够回答出正确答案。

这是为什么?

因为模型在回答新问题的时候,对之前的问题进行了总结性的重述

ConversationSummaryMemory(对话总结记忆)的思路就是将对话历史进行汇总,然后再传递给 {history} 参数。这种方法旨在通过对之前的对话进行汇总来避免过度使用 Token。

ConversationSummaryMemory 有这么几个核心特点。

  1. 汇总对话:此方法不是保存整个对话历史,而是每次新的互动发生时对其进行汇总,然后将其添加到之前所有互动的“运行汇总”中。
  2. 使用 LLM 进行汇总:该汇总功能由另一个 LLM 驱动,这意味着对话的汇总实际上是由 AI 自己进行的。
  3. 适合长对话:对于长对话,此方法的优势尤为明显。虽然最初使用的 Token 数量较多,但随着对话的进展,汇总方法的增长速度会减慢。与此同时,常规的缓冲内存模型会继续线性增长。

下面,我们来看看使用 ConversationSummaryMemory 的代码示例。

from langchain.chains.conversation.memory import ConversationSummaryMemory

# 初始化对话链
conversation = ConversationChain(
    llm=llm,
    memory=ConversationSummaryMemory(llm=llm)
)

第一回合的输出:

{'input': '我姐姐明天要过生日,我需要一束生日花束。', 
'history': '', 
'response': ' 我明白,你需要一束生日花束。我可以为你提供一些建议吗?我可以推荐一些花束给你,比如玫瑰,康乃馨,百合,仙客来,郁金香,满天星等等。挑选一束最适合你姐姐的生日花束吧!'}

第二回合的输出:

{'input': '她喜欢粉色玫瑰,颜色是粉色的。', 
'history': "\nThe human asked what the AI thinks of artificial intelligence. The AI thinks artificial intelligence is a force for good because it will help humans reach their full potential. The human then asked the AI for advice on what type of flower bouquet to get for their sister's birthday, to which the AI provided a variety of suggestions.", 
'response': ' 为了为你的姐姐的生日准备一束花,我建议你搭配粉色玫瑰和白色康乃馨。你可以在玫瑰花束中添加一些紫色的满天星,或者添加一些绿叶以增加颜色对比。这将是一束可爱的花束,让你姐姐的生日更加特别。'}

第三回合的输出:

{'input': '我又来了,还记得我昨天为什么要来买花吗?', 
'history': "\n\nThe human asked what the AI thinks of artificial intelligence. The AI thinks artificial intelligence is a force for good because it will help humans reach their full potential. The human then asked the AI for advice on what type of flower bouquet to get for their sister's birthday, to which the AI suggested pink roses and white carnations with the addition of purple aster flowers and green leaves for contrast. This would make a lovely bouquet to make the sister's birthday extra special.",
'response': ' 确实,我记得你昨天想买一束花给你的姐姐作为生日礼物。我建议你买粉红色的玫瑰花和白色的康乃馨花,再加上紫色的雏菊花和绿叶,这样可以让你的姐姐的生日更加特别。'}

看得出来,这里的 ‘history’,不再是之前人类和 AI 对话的简单复制粘贴,而是经过了总结和整理之后的一个综述信息。

这里,我们不仅仅利用了 LLM 来回答每轮问题,还利用 LLM 来对之前的对话进行总结性的陈述,以节约 Token 数量。这里,帮我们总结对话的 LLM,和用来回答问题的 LLM,可以是同一个大模型,也可以是不同的大模型。

ConversationSummaryMemory 的优点是对于长对话,可以减少使用的 Token 数量,因此可以记录更多轮的对话信息,使用起来也直观易懂。不过,它的缺点是,对于较短的对话,可能会导致更高的 Token 使用。另外,对话历史的记忆完全依赖于中间汇总 LLM 的能力,还需要为汇总 LLM 使用 Token,这增加了成本,且并不限制对话长度。

通过对话历史的汇总来优化和管理 Token 的使用,ConversationSummaryMemory 为那些预期会有多轮的、长时间对话的场景提供了一种很好的方法。然而,这种方法仍然受到 Token 数量的限制。在一段时间后,我们仍然会超过大模型的上下文窗口限制。

而且,总结的过程中并没有区分近期的对话和长期的对话(通常情况下近期的对话更重要),所以我们还要继续寻找新的记忆管理方法。

使用 ConversationSummaryBufferMemory

我要为你介绍的最后一种记忆机制是 ConversationSummaryBufferMemory,即对话总结缓冲记忆,它是一种混合记忆模型,结合了上述各种记忆机制,包括 ConversationSummaryMemory 和 ConversationBufferWindowMemory 的特点。这种模型旨在在对话中总结早期的互动,同时尽量保留最近互动中的原始内容。

它是通过 max_token_limit 这个参数做到这一点的。当最新的对话文字长度在 300 字之内的时候,LangChain 会记忆原始对话内容;当对话文字超出了这个参数的长度,那么模型就会把所有超过预设长度的内容进行总结,以节省 Token 数量。

from langchain.chains.conversation.memory import ConversationSummaryBufferMemory

# 初始化对话链
conversation = ConversationChain(
    llm=llm,
    memory=ConversationSummaryBufferMemory(
        llm=llm,
        max_token_limit=300))

第一回合的输出:

{'input': '我姐姐明天要过生日,我需要一束生日花束。', 
'history': '', 
'response': ' 哇,你姐姐要过生日啊!那太棒了!我建议你去买一束色彩鲜艳的花束,因为这样可以代表你给她的祝福和祝愿。你可以去你家附近的花店,或者也可以从网上订购,你可以看看有没有特别的花束,比如彩色玫瑰或者百合花,它们会更有特色。'}

第二回合的输出:

{'input': '她喜欢粉色玫瑰,颜色是粉色的。', 
'history': 'Human: 我姐姐明天要过生日,我需要一束生日花束。\nAI:  哇,你姐姐要过生日啊!那太棒了!我建议你去买一束色彩鲜艳的花束,因为这样可以代表你给她的祝福和祝愿。你可以去你家附近的花店,或者也可以从网上订购,你可以看看有没有特别的花束,比如彩色玫瑰或者百合花,它们会更有特色。', 
'response': ' 好的,那粉色玫瑰就是一个很好的选择!你可以买一束粉色玫瑰花束,这样你姐姐会很开心的!你可以在花店里找到粉色玫瑰,也可以从网上订购,你可以根据你的预算,选择合适的数量。另外,你可以考虑添加一些装饰,比如细绳、彩带或者小礼品'}

第三回合的输出:

{'input': '我又来了,还记得我昨天为什么要来买花吗?', 
'history': "System: \nThe human asked the AI for advice on buying a bouquet for their sister's birthday. The AI suggested buying a vibrant bouquet as a representation of their wishes and blessings, and recommended looking for special bouquets like colorful roses or lilies for something more unique.\nHuman: 她喜欢粉色玫瑰,颜色是粉色的。\nAI:  好的,那粉色玫瑰就是一个很好的选择!你可以买一束粉色玫瑰花束,这样你姐姐会很开心的!你可以在花店里找到粉色玫瑰,也可以从网上订购,你可以根据你的预算,选择合适的数量。另外,你可以考虑添加一些装饰,比如细绳、彩带或者小礼品", 
'response': ' 是的,我记得你昨天来买花是为了给你姐姐的生日。你想买一束粉色玫瑰花束来表达你的祝福和祝愿,你可以在花店里找到粉色玫瑰,也可以从网上订购,你可以根据你的预算,选择合适的数量。另外,你可以考虑添加一些装饰,比如细绳、彩带或者小礼品}

不难看出,在第二回合,记忆机制完整地记录了第一回合的对话,但是在第三回合,它察觉出前两轮的对话已经超出了 300 个字节,就把早期的对话加以总结,以节省 Token 资源。

ConversationSummaryBufferMemory 的优势是通过总结可以回忆起较早的互动,而且有缓冲区确保我们不会错过最近的互动信息。当然,对于较短的对话,ConversationSummaryBufferMemory 也会增加 Token 数量。

总体来说,ConversationSummaryBufferMemory 为我们提供了大量的灵活性。它是我们迄今为止的唯一记忆类型,可以回忆起较早的互动并完整地存储最近的互动。在节省 Token 数量方面,ConversationSummaryBufferMemory 与其他方法相比,也具有竞争力。

总结时刻

今天介绍了一种对话链和四种类型的对话记忆机制,那么我们可以通过一个表格对这四种类型的记忆做一个整体比较。四种记忆机制的比较如下:

在这里插入图片描述

网上还有人总结了一个示意图,体现出了当对话轮次逐渐增加时,各种记忆机制对 Token 的消耗数量。意图向我们表达的是:有些记忆机制,比如说 ConversationSummaryBufferMemory 和 ConversationSummaryMemory,在对话轮次较少的时候可能会浪费一些 Token,但是多轮对话过后,Token 的节省就逐渐体现出来了。

当然 ConversationBufferWindowMemory 对于 Token 的节省最为直接,但是它会完全遗忘掉 K 轮之前的对话内容,因此对于某些场景也不是最佳选择。

在这里插入图片描述

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

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

相关文章

VsCode搭建Spring Boot项目环境

VsCode搭建Spring Boot项目环境 1、前提条件:配置Java环境 下载安装JDK配置环境变量 2、VsCode配置SpringBoot环境 安装扩展 配置Maven 找到Maven配置文件,进行打开settings.json,添加如下代码: "workbench.iconThem…

码界深潜:全面解读软件工程的艺术与科学

🏡 基石构筑篇——软件工程基础理论及技能 🔢 编程语言选型与精修 于软件工程之浩瀚宇宙中,编程语言犹如各色画笔,每种语言的特性对应不同的创作领域。譬如Java倚仗跨平台兼容性和强大的面向对象机制,在企业级应用程序…

《GitHub新手入门指南:从零开始掌握基本用法》

在现代软件开发和技术社区中,GitHub已经成为了一个不可或缺的平台。它不仅是一个代码托管平台,更是一个技术交流、学习分享的社交平台。但对于初学者来说,GitHub可能会有些令人望而却步。本文将详细介绍GitHub的基本用法,帮助新手快速入门并融入这个充满活力的技术社区。 …

while 循环

语法格式: while (表达式) { 若干语句 } 执行规则 STEP1:计算表达式的值,如果 该值是true时,就进行STEP2, 否则执行STEP3。 STEP2:执行循环体,再进行 STEP1。 STEP3:结束while语句的…

WhatsApp API号注册平台价格对比:帮你选择性价比最高的服务

WhatsApp作为全球使用人数众多的即时通讯工具,推出的API服务为企业提供了强大的客户互动能力。然而面对众多提供WhatsApp API号注册的平台,企业在选择时很容易感到困惑。这篇文章将会对目前市面上比较主流的WhatsApp API号注册平台进行价格对比&#xff…

知识产权实缴注册资本的税务问题

2023年12月29日颁布的新《公司法》很快就要实施了。新《公司法》规定:公司注册资本需自公司成立之日起5年内缴足。因此,很多企业抓住政策的红利,通过知识产权实缴的方式实缴注册资本,这样可以不出现金就完成股东实缴注册资本的义务…

1.4 控制流程语句

1.4 控制流程语句 在C语言中,控制流程语句用于决定程序执行的流程。主要的控制流程语句包括条件语句和循环语句。 1.4.1 条件语句 条件语句允许根据条件的真假来执行不同的代码块。 1.4.1.1 if语句 if语句用于执行一个代码块,当给定的条件为真时执行…

Git 版本控制

Git 版本控制 1. About Version Control (关于版本控制)1.1. Local Version Control Systems (本地版本控制系统)1.2. Centralized Version Control Systems (集中化的版本控制系统)1.3. Distributed Version Control Systems (分布式版本控制系统) 2. 换行符的处理3. keyboard…

将一个时间序列中的每个元素转换为指定的日期格式

【小白从小学Python、C、Java】 【计算机等考500强证书考研】 【Python-数据分析】 将一个时间序列中的每个元素 转换为指定的日期格式 Series.dt.strftime() 选择题 以下关于代码输出结果的说法中正确的是? import pandas as pd ts pd.Series(pd.date_range("2024-03…

如何在 Java 中以编程的方式将 CSV 转为 Excel XLSX 文件

前言 Microsoft Excel的XLSX格式以及基于文本的CSV(逗号分隔值)格式,是数据交换中常见的文件格式。应用程序通过实现对这些格式的读写支持,可以显著提升性能。在本文中,小编将为大家介绍如何借助葡萄城公司地Java API…

数据结构c版(2)——二叉树

本章我们来了解一下二叉树这一概念。 目录 1.树概念及结构 1.1树的概念​​​​​​​ 1.2 树的特点: 1.3 树的相关概念 1.4 树的表示​​​​​​​ 1.5 树在实际中的运用(表示文件系统的目录树结构) 2.二叉树概念及结构 2.1概念 …

H3C接入交换机收到大量上行口(连接汇聚交换机)TC报文该怎么处理?(排查思路及解决办法)

一、问题描述 H3C接入交换机收到大量上行口的TC报文。 二、告警信息 三、TC报文排查思路 1、检查物理链路是否有问题。 2、检查是否有可能存在环路。 3、终端接入的端口开启STP边缘端口。 4、根桥的下联端口开启STP根保护。 5、如果对端设备不是管辖的范围且无法管理到,可考虑…

「优选算法刷题」:在每个树行中找最大值

一、题目 给定一棵二叉树的根节点 root &#xff0c;请找出该二叉树中每一层的最大值。 示例1&#xff1a; 输入: root [1,3,2,5,3,null,9] 输出: [1,3,9]示例2&#xff1a; 输入: root [1,2,3] 输出: [1,3]提示&#xff1a; 二叉树的节点个数的范围是 [0,104]-231 < N…

WebSocket介绍+3分钟时间使用WebSocket搭建属自己的聊天室

WebSocket 的由来 在 WebSocket 出现之前&#xff0c;我们想实现实时通信、变更推送、服务端消息推送功能&#xff0c;我们一般的方案是使用 Ajax 短轮询、长轮询两种方式&#xff1a;比如我们想实现一个服务端数据变更时&#xff0c;立即通知客户端功能&#xff0c;没有 WebS…

【探索AI】二十一 深度学习之第4周:循环神经网络(RNN)与长短时记忆(LSTM)

循环神经网络&#xff08;RNN&#xff09;与长短时记忆&#xff08;LSTM&#xff09; RNN的基本原理与结构LSTM的原理与实现序列建模与文本生成任务实践&#xff1a;使用RNN或LSTM进行文本分类或生成任务步骤 1: 数据准备步骤 2: 构建模型步骤 3: 定义损失函数和优化器步骤 4: …

数字电路三宝:锁存器、寄存器和触发器

在数字电路设计中&#xff0c;很多电子工程师经常会用到锁存器、寄存器和触发器&#xff0c;它们各自承担着不同的功能&#xff0c;但共同为数字电路的稳定性和高效性提供了坚强保障&#xff0c;下面将谈谈这三大元件&#xff0c;希望对小伙伴们有所帮助。 1、锁存器&#xff0…

HOOPS Communicator对3D大模型轻量化加载与渲染的4种解决方案

今天给大家介绍一些关于3D Web轻量化引擎HOOPS Commuicator的关键概念&#xff0c;这些概念可以帮您在HOOPS Communicator流缓存服务器之上更好地构建您自己的模型流服务器。如果您是有大型数据集&#xff0c;那么&#xff0c;使用流缓存服务器可以极大地帮助您最大限度地减少内…

EthSign联合创始人 POTTER LI 确认出席Hack .Summit() 香港区块链开发者大会!

thSign联合创始人 POTTER LI确认将出席由 Hack VC 主办&#xff0c;并由 AltLayer 和 Berachain 联合主办&#xff0c;与 SNZ 和数码港合作&#xff0c;由 Techub News 承办的Hack.Summit() 2024区块链开发者盛会。 Potter Li&#xff0c;南加州大学应有数学系&#xff0c;南加…

hook函数——useReducer

目录 1.useReducer定义2.useReducer用法3.useState和useReducer区别 1.useReducer定义 const [state, dispatch] useReducer(reducer, initialArg, init?) reducer&#xff1a;用于更新 state 的纯函数。参数为 state 和 action&#xff0c;返回值是更新后的 state。state …

excel统计分析——拉丁方设计

参考资料&#xff1a;生物统计学 拉丁方设计也是随机区组设计&#xff0c;是对随机区组设计的一种改进。它在行的方向和列的方向都可以看成区组&#xff0c;因此能实现双向误差的控制。在一般的试验设计中&#xff0c;拉丁方常被看作双区组设计&#xff0c;用于提高发现处理效应…