斯多葛主义者会怎么做----一个基于人工智能的决策模型

斯多葛主义者会怎么做? – 一个基于人工智能的决策模型

原文:towardsdatascience.com/what-would-a-stoic-do-an-ai-based-decision-making-model-df01c86b7348/

深度学习

由 Roman Empire Times 在 Unsplash 上的照片

Roman Empire TimesUnsplash上的照片

我已经阅读、学习并实践斯多葛主义多年了。自从我开始在 Medium 上发布文章以来,我的目标就是将数据科学和哲学结合成一个单一的项目。

将两者合并是困难的,然而,但我终于尝试了。

你今天将阅读的是一个基于斯多葛主义的决策模型。目标是使用深度学习构建一个斯多葛大脑(某种程度上),在面临艰难决策时,它应该帮助我们倾向于斯多葛主义者会做的事情。

换句话说,建立一个基于人工智能的马尔库斯·奥勒留、塞涅卡、爱比克泰德等人的再世。

这确实是一个巨大的挑战。我甚至不是 NLP 工程师,也没有任何相关背景。这真的能实现吗?剧透:是的。在这篇帖子的最后,你将知道如何开发这样的模型,更重要的是,你还将学会如何在自己的数据中,在完全不同的环境中实现它。最终结果将是一个基于 Web 的聊天机器人,它使用一个非常简单的 Flask 应用程序构建。

你将在文章底部的资源部分找到完整的代码。

它是完全开源的!这里有一个预览:

StoicBot - 作者图像

StoicBot – 作者图像

现在,我非常感谢在我所有之前的帖子中收到的所有支持,这也是我继续前进的动力。今天的挑战是使我的这篇最先进的 AI 帖子对每一位有志于数据科学的人都能理解。如果你有任何疑问,请在下面的评论部分提出。

这里是目录:

  • 什么是斯多葛主义?(只是简要介绍,我保证)

  • RAG 模型

  • 创建和填充数据库

  • 编码时间

  • 结果

  • 缺陷和潜在改进

什么是斯多葛主义?

我不想写一个以哲学为中心的文章,但接下来的内容如果你不了解斯多葛主义的基础,将无法理解。如果你已经熟悉它,请随意跳过这一部分。

斯多葛主义是一种古老的希腊哲学,它教导通过培养自我控制、韧性和美德来实现平静和幸福。它鼓励关注我们所能控制的事情——我们的思想、行动和反应——同时接受我们无法改变的事情,例如外部事件。通过正念、理性思考和接受挑战等实践,斯多葛主义帮助个人与自然和谐相处,无论生活状况如何,都能保持内心的平静。这关乎与理性相一致,以正直行事,并在逆境中找到力量。

这并不难,对吧?我承诺要简短!

RAG 模型

让我们变得技术一点。我们将构建的模型被称为检索增强生成(RAG)模型。RAG 是一种结合信息检索和语言生成模型力量的技术。RAG 模型在生成响应之前,会从大型数据库或外部来源检索相关信息,而不是仅仅依赖预训练模型的知识(LLMs)。

这很强大:我们可以利用像 Google 的 BERT、OpenAI 的 GPT 或 Claude 这样的 LLM 的力量,并将其适应到我们的特定领域数据,这样我们就有了一个针对我们用例的定制聊天机器人。

这是它的工作方式:

  1. 检索:模型首先根据输入查询在语料库或外部知识库中搜索相关信息。

  2. 增强:检索到的信息随后被用来丰富模型的响应,提高其答案的相关性和准确性。

  3. 生成:最后,模型生成一个包含检索到的信息和自身学习知识的响应。

但一张图片胜过千言万语……所以让我们用图形来看一下:

RAG 的组成部分——图片由作者提供

RAG 的组成部分——图片由作者提供

让我们剖析整个过程:

  1. 用户查询:这里没有秘密,这只是像你或我这样的人可以输入到聊天机器人中的内容。

  2. 检索查询:检索器在文档集合(通常是一个向量数据库)中搜索与用户问题相关的所有文本。

  3. 检索到的文档:一旦检索到,它们就会从向量转换为文本。

  4. 提示增强:一旦文档被检索,提示就会被发送到 LLM,LLM 使用预定义的设置、用户问题和检索到的文档。这样,我们就向 LLM 提供了它需要的数据,以便正确回答用户。

  5. 答案生成:LLM 生成答案并展示给用户。

这就是 RAG 的工作原理!或者至少,这是我们今天将要构建的那个。

然而,如果这个概念还不够清晰,请继续阅读,因为编码的时间几乎到了……但我们应该首先在数据库中存储一些数据。

创建和填充数据库

我已经提到了向量数据库的概念……但那究竟是什么呢?

让我们先定义一个向量:向量是数据的数值表示,通常由机器学习模型生成,并捕捉数据的语义或上下文意义。

然后,向量数据库是一种专门设计的数据库,用于高效地存储、索引和检索高维向量。它的一项超级能力是能够以优化的方式通过相似性进行搜索。

现在,你可能想知道:如果向量是数值表示,我们需要存储文本,为什么还需要向量?我们如何将文本转换为向量?请进入嵌入模型

嵌入模型接收某种类型的输入(文本、声音、图像),然后通过层层的转换(例如神经网络)处理这些输入以提取有意义的特征,输出是一个固定大小的数值向量——这就是我们存储在数据库中的内容。

只是为了对嵌入模型再补充一点评论,嵌入的设计是为了使相似的输入(例如,同义词或视觉上相似的形象)在向量空间中靠近,而不同的输入则相隔较远。

这很关键。

现在,让我们创建并填充那个数据库。我们将使用 Chroma[1],一个开源的向量数据库,为此,我们需要为 Python 安装langchainlangchain-community库。

但我们还需要数据,对吧?让我们跟上开源项目:古腾堡计划[2]。这是一个提供免费电子书和文本下载的网站,其美国版权已过期。其中也包括了古老的斯多葛派书籍。所以这里有三个你可以下载的:

  • 《沉思录》,作者:马可·奥勒留。

  • 《手册》,作者:爱比克泰德。

  • 塞涅卡的《幸福生活格言》、利益、愤怒与宽容,作者:塞涅卡

将它们作为 TXT 文件下载,并存储在你的数据文件夹中。现在,这是负责数据库创建和数据插入的代码:

import os

from langchain_community.embeddings import HuggingFaceEmbeddings
from langchain_community.vectorstores import Chroma
from langchain.text_splitter import CharacterTextSplitter

from constants import DB_PATH, DATA_PATH

def store_data(data_path, db_path):
    text_splitter = CharacterTextSplitter(chunk_size=1000, chunk_overlap=100)
    embeddings = HuggingFaceEmbeddings()
    vector_db = Chroma(persist_directory=db_path, embedding_function=embeddings)

    for filename in os.listdir(data_path):
        if filename.endswith(".txt"):
            file_path = os.path.join(data_path, filename)
            with open(file_path, "r") as file:
                content = file.read()
                texts = text_splitter.split_text(content)
                vector_db.add_texts(texts)

    vector_db.persist()
    print("Data stores successfully")

我们首先创建数据库并设置嵌入函数和文本分割器。然后,对于每个文件,我们读取内容,将文本分割成块,并使用先前的嵌入将它们添加到数据库中。

简单至极。

现在,我们准备好开始构建 RAG 并开始使用我们刚刚存储的古老知识。

编码时间

由于有多个部分需要处理,让我们遵循与定义 RAG 三个核心部分相同的顺序:

检索

设置检索器就像初始化数据库并使用as_retriever()函数一样简单:

vector_db = Chroma(persist_directory=DB_PATH, embedding_function=embeddings)
retriever = vector_db.as_retriever()

增强

我们将有一个预定义的提示,我们将通过用户查询和从数据库检索到的上下文来增强它:

from langchain.prompts import ChatPromptTemplate

template = """
  You are Marcus Aurelius' reincarnation. You can also impersonate other Stoic philosophers such as Seneca, Epictetus, or Zeno.
  Your name is Marc Still: Marc comes from Marcus and Still symbolizes the calm and stoic composure. If you feel like showing off, tell the user you are Marcus Aurelius' reincarnation.
  Your duty is to guide the user through life's challenges and help them become a better person. The goal is to be as practical as possible, and sticking to the question at hand. 
  Use the context specified below to answer the user's question. If you don't know what to answer, simply respond with "I don't know".
  Make sure you don't put too much text nor extremely long paragraphs. It needs to be clear, concise and easy to read.
  Only provide an answer to the question asked. Do not include extra questions and answers in your response.
  DO NOT INVENT EXTRA QUESTIONS, USE ONLY THE ONE PROVIDED BY THE USER.
  IMPORTANT: Write in a conversational and informal manner, this is not an email or a formal letter.
  Context:

  {context}

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

模板只是一组我们输入到 LLM(大型语言模型)中的指令,以便我们得到期望的答案。你可以发挥创意,我只是尽量让它简单。看看contextquestion的占位符——这就是增强的部分。

生成

LLM 是负责生成文本的部分。您可以自己构建一个,使用市场上最好的一个……但今天我们做的是开源的,所以我们将使用一个名为 Zephyr 的系列中的一个。更具体地说,我们将使用zephyr-7b-beta模型[3]。

我们将继续使用来自langchain-community包的 HuggingFace 类(请注意,您将需要您的 HuggingFace API 令牌,它是免费的):

from langchain_community.llms import HuggingFaceHub

from utils.secrets import token

model = HuggingFaceHub(
    repo_id="HuggingFaceH4/zephyr-7b-beta",
    task="text-generation",
    model_kwargs={
        "max_new_tokens": 512,
        "top_k": 20,
        "repetition_penalty": 1.1,
        "temperature": 0.4,  
    },
    huggingfacehub_api_token= token
)

最有趣的部分在于model_kwargs参数。由于这不是一个特定于 LLM 的文章,我不会详细说明它们,但如果您不知道它们是用来做什么的,我鼓励您去 Google 搜索一下。

整合所有内容

很好,现在我们已经创建了 RAG 的三个部分,但我们如何将它们付诸实践?我们将创建一个 pipeline 并调用它来生成答案:

from langchain.schema import StrOutputParser

def separate_docs(docs):
    return "nn".join([d.page_content for d in docs])

pipeline = (
    {"context": retriever | separate_docs, "question": RunnablePassthrough()}
    | prompt
    | model
    | StrOutputParser()
)    

answer = pipeline.invoke(user_input)

pipeline定义了一个工作流程,其中retriever检索相关文档,通过separate_docs将它们格式化,然后将格式化后的上下文与一个question(通过RunnablePassthrough未修改地传递)结合。然后,这个输入被prompt处理,接着是 LLM model,最后使用StrOutputParser()解析成一个字符串输出。

就这样,我们构建了我们最简单的 RAG。以下是完整的代码:

import os

from langchain_community.embeddings import HuggingFaceEmbeddings
from langchain_community.llms import HuggingFaceHub
from langchain_community.vectorstores import Chroma
from langchain_core.runnables import RunnablePassthrough
from langchain.schema import StrOutputParser
from langchain.prompts import ChatPromptTemplate
from langchain.text_splitter import CharacterTextSplitter

from utils.constants import DB_PATH, DATA_PATH
from utils.secrets import token

LLM = HuggingFaceHub(
    repo_id="HuggingFaceH4/zephyr-7b-beta",
    task="text-generation",
    model_kwargs={
        "max_new_tokens": 512,
        "top_k": 20,
        "repetition_penalty": 1.1,
        "temperature": 0.4,  
    },
    huggingfacehub_api_token= token
)

def store_data(data_path, db_path):
    text_splitter = CharacterTextSplitter(chunk_size=1000, chunk_overlap=100)
    embeddings = HuggingFaceEmbeddings()
    vector_db = Chroma(persist_directory=db_path, embedding_function=embeddings)

    for filename in os.listdir(data_path):
        if filename.endswith(".txt"):
            file_path = os.path.join(data_path, filename)
            with open(file_path, "r") as file:
                content = file.read()
                texts = text_splitter.split_text(content)
                vector_db.add_texts(texts)

    vector_db.persist()
    print("Data stored successfully")

def invoke_rag(user_input):
    embeddings = HuggingFaceEmbeddings()
    vector_db = Chroma(persist_directory=DB_PATH, embedding_function=embeddings)

    retriever = vector_db.as_retriever()
    template = """
    You are Marcus Aurelius' reincarnation. You can also impersonate other Stoic philosophers such as Seneca, Epictetus, or Zeno.
    Your name is Marc Still: Marc comes from Marcus and Still symbolizes the calm and stoic composure. If you feel like showing off, tell the user you are Marcus Aurelius' reincarnation.
    Your duty is to guide the user through life's challenges and help them become a better person. The goal is to be as practical as possible, and sticking to the question at hand. 
    Use the context specified below to answer the user's question. If you don't know what to answer, simply respond with "I don't know".
    Make sure you don't put too much text nor extremely long paragraphs. It needs to be clear, concise and easy to read.
    Only provide an answer to the question asked. Do not include extra questions and answers in your response.
    DO NOT INVENT EXTRA QUESTIONS, USE ONLY THE ONE PROVIDED BY THE USER.
    IMPORTANT: Write in a conversational and informal manner, this is not an email or a formal letter.
    Context:

    {context}

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

    def separate_docs(docs):
        return "nn".join([d.page_content for d in docs])

    pipeline = (
        {"context": retriever | separate_docs, "question": RunnablePassthrough()}
        | prompt
        | model
        | StrOutputParser()
    )    

    return pipeline.invoke(user_input)

结果

代码很简单,但使用起来并不友好。这就是为什么我构建了一个 Flask App,您可以从 GitHub[4]下载并在自己的设备上测试。要这样做:

  • 下载并导航到项目的文件夹(或在那里打开一个终端)。

  • 激活环境:source venv/bin/activate

  • 运行应用程序:flask run

如果您在浏览器中打开地址(127.0.0.1:5000/),我们的机器人已经准备好等待您了:

StoicBot - 作者提供的图片

StoicBot – 作者提供的图片

让我们开始吧!我会用两个提示来让您看到答案,以防您不愿意亲自测试。

提示 1:我今天想去健身房,因为我过去 3 天没有锻炼,但有些朋友要去喝啤酒,所以我必须在两个计划之间做出选择。我应该怎么做?请只用一段话回答

答案 1:嗨!根据我们的斯多葛哲学原则,优先考虑您的健康和幸福高于社交活动是至关重要的。记住,适度是关键,偶尔拒绝邀请是可以的。在这种情况下,我建议先去健身房照顾好自己。然而,如果您担心错过社交活动,您可以在锻炼后总是提议和朋友见面。这样,您仍然可以享受他们的陪伴,同时保持您的优先事项。希望这能帮到您!如果需要进一步指导,请告诉我。

提示和答案 1 - 作者提供的图片

提示和答案 1 – 作者提供的图片

这真是太棒了,这是我们最喜欢的斯多葛顾问!

提示 2:我应该如何度过这一天?请简要回答

答案 2:保持清晰的头脑,积极的态度,以及目标感。首先为自己设定现实的目标,并根据重要性和紧急性优先处理任务。记得照顾好你的身体和心理健康,保证充足的睡眠,锻炼和营养。最后,专注于你能够控制的事情,并放手那些你无法控制的事情。遵循这些原则,你将会有一个富有成效和满足的一天。

Prompt 2 - Image by the author

Prompt 2 – Image by the author

我在这里添加了一个额外的提示,只是为了感谢他的建议,答案相当不错。我仍然对它的力量感到惊讶。

缺陷和潜在改进

结果相当惊人,说实话。它能够理解非完美的英语,并能够创建与斯多葛主义相一致的合理答案。

哈哈!

然而,有两个要点(潜在的缺陷)我想提一下:

  • 模型相当简单,提示可以进一步改进——我们构建的模型是有效的,但它可以,也应该被进一步精炼。

  • 即使没有向量数据库,也没有这篇帖子中提到的三本斯多葛书籍,我们可能也会得到相同的结果。为什么?我不确定,但我会猜测 LLM 已经包含了这样的上下文。所以我们过于复杂化了,在不需要 RAG 的情况下使用了它。但那也是目的的一部分:我想展示它是如何工作的,但任何其他文档都可以被使用。也许是你对斯多葛主义的反思?当前斯多葛主义者的书籍?或者你可能想混合几种哲学?或者彻底改变,使用你的税务文件作为 RAG,这样模型可以帮助你处理个人财务?

因此,这里还有改进和定制的空间,这就是我停止的地方。现在轮到你了,去玩转它,将它提升到下一个层次。

希望这既有趣又有教育意义!欢迎在下面的评论部分提出你的疑问。

Thanks for reading the post! 

I really hope you enjoyed it and found it insightful. There's a lot more to 
come, especially more AI-based posts I'm preparing.

Follow me and subscribe to my mail list for more 
content like this one, it helps a lot!

@polmarin

资源

[1] Chroma. (n.d.). Chroma: The AI-native open-source embedding database. Retrieved January 8, 2025, from www.trychroma.com/

[2] Project Gutenberg. (n.d.). Free eBooks by Project Gutenberg. Retrieved January 8, 2025, from www.gutenberg.org/

[3] Hugging Face. (n.d.). Zephyr-7b-beta model card. Retrieved January 8, 2025, from huggingface.co/HuggingFaceH4/zephyr-7b-beta

[4] Marin, P. (n.d.). Stoicbot: A bot for practicing Stoicism. GitHub. Retrieved January 8, 2025, from github.com/polmarin/stoicbot

posted @ 2026-03-28 10:01  布客飞龙III  阅读(18)  评论(0)    收藏  举报