Langchain:数据连接封装、缓存封装和LCEL学习和探索

news2024/12/23 23:57:55

🌵  目录  🌵

😋 数据连接封装 🍔

文档加载器:Document Loaders

文档处理器:TextSplitter

 向量数据库与向量检索

 总结

🍉 缓存封装:Memory 🏖️

对话上下文:ConversationBufferMemory

只保留一个窗口的上下文:ConversationBufferWindowMemory 

通过Token数控制上下文长度:ConversationTokenBufferMemory

更多尝试

总结

🌞 Chain 和 LangChain Expression Language (LCEL) 🔆

Pipeline 式调用 PromptTemplate, LLM 和 OutputParser

用 LCEL 实现 RAG

通过 LCEL 实现 Function Calling

通过 LCEL,还可以实现


        本文将继续延续Langchain专栏文章,本文将讲解Langchain的数据连接封装、缓存封装和LCEL,逐渐深入学习Langchain的高级能力,帮助我们更好更快的接触大模型。

        初识Langchain可以看看这篇文章:

直通车:LangChain:大模型框架的深度解析与应用探索-CSDN博客

😋数据连接封装

        对外部进行加载,如果你需要可以做一些处理、转换,可以做embedding然后放在store(向量数据)里,然后通过retrieve访问向量数据库形式进行检索。这是一个外部数据连接进来的一个模块的划分和逻辑上 的Pipeline。

文档加载器:Document Loaders

# pip install pypdf
from langchain_community.document_loaders import PyPDFLoader

loader = PyPDFLoader("2401.12599v1.pdf")
pages = loader.load_and_split()

print(pages[0].page_content)

文档处理器:TextSplitter

        切割器,按照字符切割。示例代码,真正要使用实现的粒度要比该示例粒度都要细。

from langchain_community.document_loaders import PyPDFLoader

loader = PyPDFLoader("2401.12599v1.pdf")
pages = loader.load_and_split()

print(pages[0].page_content)

from langchain.text_splitter import RecursiveCharacterTextSplitter

# 文档切分
text_splitter = RecursiveCharacterTextSplitter(
    chunk_size=300,
    chunk_overlap=200, 
    length_function=len,
    add_start_index=True,
)

paragraphs = text_splitter.create_documents([pages[0].page_content])
for para in paragraphs:
    print(para.page_content)
    print('-------')

LangChain 的 PDFLoader 和 TextSplitter 实现都比较粗糙,实际生产中不建议使用。

 向量数据库与向量检索

        它封装了和三方数据库的链接和检索。因为本身就是一个接口的封装,比如调用chroma是一套接口协议,调用pytorch是一套接口协议,它都不一样,通过Langchain封装了你都可以用同一套接口去访问向量数据库了,将来我要换一个向量数据库,我都不需要大规模的改代码都能使用同一套接口去实现。

# pip install chromadb
# 加载 .env 到环境变量
from dotenv import load_dotenv, find_dotenv
_ = load_dotenv(find_dotenv())

from langchain.document_loaders import UnstructuredMarkdownLoader
from langchain_openai import OpenAIEmbeddings
from langchain.text_splitter import RecursiveCharacterTextSplitter
from langchain.vectorstores import Chroma
from langchain_openai import ChatOpenAI
from langchain.chains import RetrievalQA
from langchain_community.document_loaders import PyPDFLoader

# 加载文档
loader = PyPDFLoader("2401.12599v1.pdf")
pages = loader.load_and_split()

# 文档切分
text_splitter = RecursiveCharacterTextSplitter(
    chunk_size=300,
    chunk_overlap=100,
    length_function=len,
    add_start_index=True,
)

texts = text_splitter.create_documents([pages[2].page_content,pages[3].page_content])

# 灌库
embeddings = OpenAIEmbeddings()
db = Chroma.from_documents(texts, embeddings)

# LangChain内置的 RAG 实现
qa_chain = RetrievalQA.from_chain_type(
    llm=ChatOpenAI(temperature=0),
    retriever=db.as_retriever()
)

query = "llm基于哪些数据来训练?"
response = qa_chain.invoke(query)
print(response["result"])

# LLM(Large Language Models)基于大量的文本数据进行训练。这些数据可以包括互联网上的网页、书籍、新闻文章、论文等各种文本资源。通过对这些数据进行深度学习训练,LLM可以学习到丰富的语言知识和模式,从而具备处理和生成文本的能力。

更多的三方检索组件链接,参考:Vector stores | 🦜️🔗 LangChain

 总结

  1. 文档处理部分 LangChain 实现较为粗糙,实际生产中不建议使用
  2. 与向量数据库的链接部分本质是接口封装,向量数据库需要自己选型

🍉缓存封装:Memory

对话上下文:ConversationBufferMemory

        它的一个特点是通过一个Buffer存储上下文,并且可以存储多行会话,当需要展示历史会话是可以按写入顺序全部打印出来。

from langchain.memory import ConversationBufferMemory, ConversationBufferWindowMemory

history = ConversationBufferMemory()
history.save_context({"input": "hello"}, {"output": "hello"})
print(history.load_memory_variables({}))
history.save_context({"input": "nice to meet you"}, {"output": "nice to meet you too"})
print(history.load_memory_variables({}))  
history.save_context({"input": "good bye"}, {"output": "good bye"})
print(history.load_memory_variables({}))  

# {'history': 'Human: hello\nAI: hello'}
# {'history': 'Human: hello\nAI: hello\nHuman: nice to meet you\nAI: nice to meet you too'}
# {'history': 'Human: hello\nAI: hello\nHuman: nice to meet you\nAI: nice to meet you too\nHuman: good bye\nAI: bye'}

只保留一个窗口的上下文:ConversationBufferWindowMemory 

        与ConversationBufferMemory不同的时候它能根据预设的会话大小进行截断,不会造成因为会话太多导致报错。这种只能保证轮数,但不能保证不会报错。

from langchain.memory import ConversationBufferWindowMemory

window = ConversationBufferWindowMemory(k=2)# 保留多少轮问答
window.save_context({"input": "hello"}, {"output": "hello"})
print(window.load_memory_variables({}))
window.save_context({"input": "nice to meet you"}, {"output": "nice to meet you too"})
print(window.load_memory_variables({}))
window.save_context({"input": "how old are you?"}, {"output": "#!@!#!!¥"})
print(window.load_memory_variables({}))
window.save_context({"input": "good bye"}, {"output": "bye"})
print(window.load_memory_variables({}))

# {'history': 'Human: hello\nAI: hello'}
# {'history': 'Human: hello\nAI: hello\nHuman: nice to meet you\nAI: nice to meet you too'}
# {'history': 'Human: nice to meet you\nAI: nice to meet you too\nHuman: how old are you?# \nAI: #!@!#!!¥'}
# {'history': 'Human: how old are you?\nAI: #!@!#!!¥\nHuman: good bye\nAI: bye'}

通过Token数控制上下文长度:ConversationTokenBufferMemory

        能够限制Memory最多能存多少个Token,超过Token就遗弃,它可以保证调用会话不会报错。

# 加载 .env 到环境变量
from dotenv import load_dotenv, find_dotenv
_ = load_dotenv(find_dotenv())

from langchain.memory import ConversationTokenBufferMemory
from langchain_openai import ChatOpenAI

memory = ConversationTokenBufferMemory(
    llm=ChatOpenAI(),
    # max_token_limit=40
)
memory.save_context(
    {"input": "你好啊"}, {"output": "你好,我是你的AI助手。"})
memory.save_context(
    {"input": "你会干什么"}, {"output": "我什么都会"})

print(memory.load_memory_variables({}))
# {'history': 'Human: 你会干什么\nAI: 我什么都会'}

更多尝试

ConversationSummaryMemory: 对上下文做摘要

传送门:Conversation Summary | 🦜️🔗 LangChain

ConversationSummaryBufferMemory: 保存 Token 数限制内的上下文,对更早的做摘要

传送门:Conversation Summary Buffer | 🦜️🔗 LangChain

VectorStoreRetrieverMemory: 将 Memory 存储在向量数据库中,根据用户输入检索回最相关的部分

传送门:Backed by a Vector Store | 🦜️🔗 LangChain

总结

  1. LangChain 的 Memory 管理机制属于可用的部分,尤其是简单情况如按轮数或按 Token 数管理;
  2. 对于复杂情况,它不一定是最优的实现,例如检索向量库方式,建议根据实际情况和效果评估;
  3. 但是它对内存的各种维护方法的思路在实际生产中可以借鉴

🌞Chain 和 LangChain Expression Language (LCEL)

        LangChain Expression Language(LCEL)是一种声明式语言,可轻松组合不同的调用顺序构成 Chain。LCEL 自创立之初就被设计为能够支持将原型投入生产环境,无需代码更改,从最简单的“提示+LLM”链到最复杂的链(已有用户成功在生产环境中运行包含数百个步骤的 LCEL Chain)。

LCEL 的一些亮点包括:

  1. 流支持:使用 LCEL 构建 Chain 时,你可以获得最佳的首个令牌时间(即从输出开始到首批输出生成的时间)。对于某些 Chain,这意味着可以直接从 LLM 流式传输令牌到流输出解析器,从而以与 LLM 提供商输出原始令牌相同的速率获得解析后的、增量的输出。

  2. 异步支持:任何使用 LCEL 构建的链条都可以通过同步 API(例如,在 Jupyter 笔记本中进行原型设计时)和异步 API(例如,在 LangServe 服务器中)调用。这使得相同的代码可用于原型设计和生产环境,具有出色的性能,并能够在同一服务器中处理多个并发请求。

  3. 优化的并行执行:当你的 LCEL 链条有可以并行执行的步骤时(例如,从多个检索器中获取文档),我们会自动执行,无论是在同步还是异步接口中,以实现最小的延迟。

  4. 重试和回退:为 LCEL 链的任何部分配置重试和回退。这是使链在规模上更可靠的绝佳方式。目前我们正在添加重试/回退的流媒体支持,因此你可以在不增加任何延迟成本的情况下获得增加的可靠性。

  5. 访问中间结果:对于更复杂的链条,访问在最终输出产生之前的中间步骤的结果通常非常有用。这可以用于让最终用户知道正在发生一些事情,甚至仅用于调试链条。你可以流式传输中间结果,并且在每个 LangServe 服务器上都可用。

  6. 输入和输出模式:输入和输出模式为每个 LCEL 链提供了从链的结构推断出的 Pydantic 和 JSONSchema 模式。这可以用于输入和输出的验证,是 LangServe 的一个组成部分。

  7. 无缝 LangSmith 跟踪集成:随着链条变得越来越复杂,理解每一步发生了什么变得越来越重要。通过 LCEL,所有步骤都自动记录到 LangSmith,以实现最大的可观察性和可调试性。

  8. 无缝 LangServe 部署集成:任何使用 LCEL 创建的链都可以轻松地使用 LangServe 进行部署。 

直通车:LangChain Expression Language (LCEL) | 🦜️🔗 LangChain

 Pipeline 式调用 PromptTemplate, LLM 和 OutputParser

from dotenv import load_dotenv, find_dotenv
_ = load_dotenv(find_dotenv())

from langchain.output_parsers import PydanticOutputParser
from langchain_openai import ChatOpenAI
from langchain.prompts import ChatPromptTemplate
from langchain.schema import StrOutputParser
from langchain.schema.runnable import RunnablePassthrough
from pydantic import BaseModel, Field, validator
from typing import List, Dict, Optional
from enum import Enum

# 输出结构
class SortEnum(str, Enum):
    data = 'data'
    price = 'price'

class OrderingEnum(str, Enum):
    ascend = 'ascend'
    descend = 'descend'

class Semantics(BaseModel):
    name: Optional[str] = Field(description="流量包名称",default=None)
    price_lower: Optional[int] = Field(description="价格下限",default=None)
    price_upper: Optional[int] = Field(description="价格上限",default=None)
    data_lower: Optional[int] = Field(description="流量下限",default=None)
    data_upper: Optional[int] = Field(description="流量上限",default=None)
    sort_by: Optional[SortEnum] = Field(description="按价格或流量排序",default=None)
    ordering: Optional[OrderingEnum] = Field(description="升序或降序排列",default=None)

# OutputParser
parser = PydanticOutputParser(pydantic_object=Semantics)

# Prompt 模板
prompt = ChatPromptTemplate.from_messages(
    [
        (
            "system",
            "将用户的输入解析成JSON表示。输出格式如下:\n{format_instructions}\n不要输出未提及的字段。",
        ),
        ("human", "{query}"),
    ]
).partial(format_instructions=parser.get_format_instructions())

# 模型
model = ChatOpenAI(temperature=0)

# LCEL 表达式
#  | 表示从左到右运行,| 前面的操作符是管道符,它表示将前一个操作符的输出作为下一个操作符的
# 1.拿用户输入的填充到模板中
# 2.将 RunnablePassthrough 对象传递给 prompt,并返回一个新的 RunnablePassthrough 对象
# 3.将新的 RunnablePassthrough 对象传递给 model,并返回一个新的 RunnablePassthrough 对象
# 4.将新的 RunnablePassthrough 对象传递给 parser 格式化输出
runnable = (
    {"query": RunnablePassthrough()} | prompt | model | parser
)

# 运行
print(runnable.invoke("不超过100元的流量大的套餐有哪些"))

注意: 在当前的文档中 LCEL 产生的对象,被叫做 runnable 或 chain,经常两种叫法混用。本质就是一个自定义调用流程。

 使用 LCEL 的价值,也就是 LangChain 的核心价值。
官方从不同角度给出了举例说明:https://python.langchain.com/docs/expression_language/why

通过 LCEL 实现 RAG

from dotenv import load_dotenv, find_dotenv
_ = load_dotenv(find_dotenv())

from langchain.prompts import ChatPromptTemplate
from langchain_openai import OpenAIEmbeddings
from langchain_text_splitters import RecursiveCharacterTextSplitter
from langchain_community.vectorstores import FAISS
from langchain_openai import ChatOpenAI
from langchain_community.document_loaders import PyPDFLoader

# 模型
model = ChatOpenAI(model="gpt-4-turbo", temperature=0)

# 加载文档
loader = PyPDFLoader("2401.12599v1.pdf")
pages = loader.load_and_split()

# 文档切分
text_splitter = RecursiveCharacterTextSplitter(
    chunk_size=200,
    chunk_overlap=100,
    length_function=len,
    add_start_index=True,
)

texts = text_splitter.create_documents(
    [page.page_content for page in pages[:4]]
)

# 灌库
embeddings = OpenAIEmbeddings(model="text-embedding-ada-002")
db = FAISS.from_documents(texts, embeddings)

# 检索 top-1 结果
retriever = db.as_retriever(search_kwargs={"k": 5})


from langchain.schema.output_parser import StrOutputParser
from langchain.schema.runnable import RunnablePassthrough
from langchain.schema.output_parser import StrOutputParser
from langchain.schema.runnable import RunnablePassthrough

# Prompt模板
template = """Answer the question based only on the following context:
{context}

Question: {question}
"""
prompt = ChatPromptTemplate.from_template(template)

# Chain
rag_chain = (
    {"question": RunnablePassthrough(), "context": retriever}
    | prompt
    | model
    | StrOutputParser()
)

invoke = rag_chain.invoke("Llama llm基于哪些数据来训练?")
print(invoke)
# 基于上下文的信息,大型语言模型(Large language models,LLM)主要是根据来自公开可用的互联网资源训练的。这些资源包括网页、书籍、新闻和对话文本。因此,Llama LLM是基于这些类型的数据来训练的。

通过 LCEL 实现 Function Calling

from dotenv import load_dotenv, find_dotenv
_ = load_dotenv(find_dotenv())

from langchain_openai import ChatOpenAI
# 提供了一个注解器
from langchain_core.tools import tool

# 模型
model = ChatOpenAI(model="gpt-4-turbo", temperature=0)

# 通过这个注解标记一个函数,它将被 LangChain 用来调用和执行。
@tool
def multiply(first_int: int, second_int: int) -> int:
    """两个整数相乘"""
    return first_int * second_int

@tool
def add(first_int: int, second_int: int) -> int:
    """两数之和"""
    return first_int + second_int

@tool
def exponentiate(base: int, exponent: int) -> int:
    """乘方"""
    return base**exponent

from langchain_core.output_parsers import StrOutputParser
from langchain.output_parsers import JsonOutputToolsParser

tools = [multiply, add, exponentiate]

# 通过语言模型调用function calling
# 带有分支的 LCEL
# 它会将你定义的函数,生成openai的schema,然后调用openai的模型
llm_with_tools = model.bind_tools(tools) | {
    # llm自己去判断是返回function calling的指示还是返回文本回复
    "functions": JsonOutputToolsParser(),
    "text": StrOutputParser()
}

# result = llm_with_tools.invoke("1024的16倍是多少")
# result = llm_with_tools.invoke("1+1等于多少")
result = llm_with_tools.invoke("你是谁")
print(result)
# {'functions': [{'args': {'first_int': 1024, 'second_int': 16}, 'type': 'multiply'}], 'text': ''}
# {'functions': [{'args': {'first_int': 1, 'second_int': 1}, 'type': 'add'}], 'text': ''}
# {'functions': [], 'text': '我是一个聊天机器人。我可以回答你的问题,提供帮助和建议。\n我是一个聊天机器人。我可以回答你的问题,提供帮助和建议。'}

注意:如果执行报NotImplementedError错误,可以更新langchain版本试试:

python -m pip install --upgrade pip

pip install -qU langchain-openai

更多实现

  1. 配置运行时变量:Configure runtime chain internals | 🦜️🔗 LangChain
  2. 故障回退:Fallbacks | 🦜️🔗 LangChain
  3. 并行调用:Parallel: Format data | 🦜️🔗 LangChain
  4. 逻辑分支:Route logic based on input | 🦜️🔗 LangChain
  5. 调用自定义流式函数:Lambda: Run custom functions | 🦜️🔗 LangChain
  6. 链接外部 Memory:Add message history (memory) | 🦜️🔗 LangChain

更多例子:LangChain Expression Language (LCEL) | 🦜️🔗 LangChain 

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

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

相关文章

Docker | 基础指令

环境:centos8 参考: 安装 Docker | Docker 从入门到实践https://vuepress.mirror.docker-practice.com/install/ 安装Docker 卸载旧版本,安装依赖包,添加yum软件源,更新 yum 软件源缓存,安装 docker-ce…

保研笔试复习——nju

文章目录 一、单选计算机网络计算机组成原理数字逻辑电路数据结构操作系统微机系统 多选题计算机网络计算机系统结构操作系统 免责声明:题目源自于网络,侵删。 就在今天2024-5-18,考的题下面的只有一道AVL的原题,其他都不是原题&a…

Python操作MySQL数据库的工具--sqlalchemy

文章目录 一、pymysql和sqlalchemy的区别二、sqlalchemy的详细使用1.安装库2.核心思想3.整体思路4.sqlalchemy需要连接数据库5.使用步骤1.手动提前创建数据库2.使用代码创建数据表3.用代码操作数据表3.1 增加数据3.2 查询数据3.3 删除数据3.4 修改数据 一、pymysql和sqlalchemy…

mysql 按区间统计 3 分钟维度

根据 UNIX_TIMESTAMP 去掉分钟后的的位数 思路如下select UNIX_TIMESTAMP(now()) 当前时间 秒,now() 当前时间,FROM_UNIXTIME(FLOOR(UNIX_TIMESTAMP(CURRENT_TIMESTAMP) / (3 * 60)) * (3 * 60)) 3分钟为分隔去掉多余位数当前时间 秒 当前时间 3分钟为分隔去掉多余…

电磁仿真软件CST六面体网格和六面体TLM网格的区别【仿真入门】

六面体网格(1) Time Domain Solver中使用的Hexahedral Mesh! 网格可以说是为了Maxwell方程式计算,将仿真结构分割成许多小的网格单元。因此,仿真计算中识别的结构是网格结构。 Time Domain Solver中使用的Hexahedra…

什么是线程安全?如何保证线程安全?

目录 一、引入线程安全 👇 二、 线程安全👇 1、线程安全概念 🔍 2、线程不安全的原因 🔍 抢占式执行(罪魁祸首,万恶之源)导致了线程之间的调度是“随机的” 多个线程修改同一个变量 修改…

每日一题《leetcode--116.填充每个结点的下一个右侧结点》

https://leetcode.cn/problems/populating-next-right-pointers-in-each-node/ 题目要求给每个结点的next指针进行填充,使每个结点的next指针指向其下一个右侧结点。如果右侧没有结点,则将next指针设置为空。 struct Node* connect(struct Node* root) {…

OpenP2P使用分享

软件介绍 大家好,今天我跟大家分享另一种内网穿透的方式。说起内网穿透,可能大家首先想到的是VPN,它是一种常见的内网穿透技术,能够让我们在远程访问内网资源。但是,VPN需要公网IP和服务器,而且配置相对复…

智能的PHP开发工具PhpStorm v2024.1全新发布——支持PHPUnit 11.0

PhpStorm是一个轻量级且便捷的PHP IDE,其旨在提高用户效率,可深刻理解用户的编码,提供智能代码补全,快速导航以及即时错误检查。可随时帮助用户对其编码进行调整,运行单元测试或者提供可视化debug功能。 立即获取PhpS…

路由聚合和VRRP技术

实验拓扑图: 实验需求 1、内网IP地址使用172.16.0.0/16 2、SW1和SW2之间互为备份; 3、VRRP/stp/vlan/eth-trunk均使用; 4、所有pc均通过DHCP获取IP地址; 5、ISP只配置IP地址; 6、所有电脑可以正常访问ISP路由器环…

什么是云计算安全?如何保障云计算安全

云计算彻底改变了数据存储的世界,它使企业可以远程存储数据并随时随地从任何位置访问数据。存和取变得简单,也使得云上数据极易造成泄露或者被篡改,所以云计算安全就显得非常重要了。那么什么是云计算安全? 其实,云计…

EN6347QI 开关稳压器 4A 贴片QFN-38 参数资料 应用案例 相关型号

EN6347QI 是一款直流/直流开关转换器。它是一款高效率的 buck (降压) 转换器,内置了电感器,能够提供高达 4A 的输出电流。其工作电压范围为 4.5V 至 12V,输出电压可调,最高可达 15V。EN6347QI 适合于各种电子设备中,用…

K8s deployment 进阶

文章目录 K8s deployment 进阶Deployment 更新策略RecreateRollingUpdatemaxSurge 和 maxUnavailable minReadySecondsprogressDeadlineSeconds Deployment 版本回滚Deployment 实现灰度发布 K8s deployment 进阶 Deployment 更新策略 Recreate 重建 (Recreate)&…

Vue进阶之Vue项目实战(三)

Vue项目实战 图表渲染安装echarts图表渲染器(图表组件)图表举例:创建 ChartsRenderer.vue创建 ChartsDataTransformer.ts基于 zrender 开发可视化物料安装 zrender画一个矩形画一个柱状图基于svg开发可视化物料svg小示例使用d3进行图表渲染安装d3基本使用地图绘制本地持久化拓…

【堡垒机小知识】堡垒机资产监控能监控哪些东西呢?

堡垒机,重要的网络安全工具,其资产监控功能在保障系统稳定运行、防范潜在风险方面发挥着至关重要的作用。但不少小伙伴对于监控内容不清楚,这里我们就来一起简单看看,仅供参考~ 堡垒机资产监控能监控哪些东西呢? 【…

Chrome DevTools 使用指南- 控制台篇

1.引言 在当今的前端开发中,Chrome DevTools 已成为开发者们不可或缺的工具。其中控制台是 DevTools 中最常用和最重要的部分之一。 本指南将详细介绍 Chrome DevTools 控制台的各种功能和使用技巧,帮助您更好地利用控制台进行高效调试和开发。无论您是…

微服务Day7学习

文章目录 数据聚合聚合分类 自动补全DSL实现Bucket聚合DSL实现Metrics聚合RestAPI实现聚合多条件聚合对接前端接口拼音分词器自定义分词器自动补全查询实现酒店搜索框自动补全 数据同步数据同步思路分析利用mq实现mysql与elasticsearch数据同步 集群介绍搭建ES集群 数据聚合 聚…

flume使用实例

1、监听端口a1.sources.r1.type netcat 配置文件nc-flume-console.conf # Name the components on this agent a1 表示jvm进程名 a1.sources r1 a1.sinks k1 a1.channels c1 # Describe/configure the source a1.sources.r1.type netcat a1.sources.r1.bind node…

什么是固态继电器?

固态继电器是不需要使用任何机械部件的开关继电器。这通常使它们具有比普通机电继电器寿命更长的优势,然而,尽管固态继电器速度快且耐用,但仍具有某些设计规定。 固态继电器风靡全球,彻底改变了从农业自动化到航空航天等各个行业…

Pytorch梯度下降算法(Gradient Descent)

intro 其实对于我们将要学的梯度最小函数,目的就是先得到loss损失最小的值,然后根据这个最小的值去得到w。 初始点在initial guess这个位置,我们希望找到最小的权重点global cost minimum,我们到底是让这个点左移寻找还是右移寻…