评估大型语言模型的文本生成
用于衡量神经文本与人类文本之间差距的度量标准
https://medium.com/@mina.ghashami?source=post_page---byline--d4a4baee49a8--------------------------------https://towardsdatascience.com/?source=post_page---byline--d4a4baee49a8-------------------------------- Mina Ghashami
·发表于 Towards Data Science ·6 分钟阅读·2024 年 1 月 20 日
–
图片来源:unsplash.com
最近,大型语言模型在生成类人文本方面展示了惊人的能力。现在有许多度量标准可以衡量由大型语言模型生成的文本与参考人类文本的接近度/相似度。实际上,缩小这种差距是一个活跃的研究领域。
在这篇文章中,我们将探讨两种广为人知的自动评估机器生成文本的度量标准。
BERTScore
假设你有一段由人类生成的参考文本和一段由大型语言模型(LLM)生成的机器文本。为了计算这两段文本之间的语义相似度,BERTScore 计算了标记嵌入的成对余弦相似度。请看下面的图像:
图片来源 [1]
这里参考文本是 “今天天气很冷”,而机器生成的候选文本是 “今天很冷”。如果我们计算 n-gram 相似度,这两段文本的分数会很低。然而,我们知道它们在语义上是非常相似的。所以 BERTScore 计算了每个标记在这两段文本中的上下文嵌入…
评估时间序列中异常值处理影响的终极指南
敏感性分析、模型验证、特征重要性等!
https://medium.com/@saranobregafn?source=post_page---byline--b4fac4cabe94--------------------------------https://towardsdatascience.com/?source=post_page---byline--b4fac4cabe94-------------------------------- Sara Nóbrega
·发布于 Towards Data Science ·阅读时长 19 分钟·2024 年 11 月 13 日
–
来源:DaLL-E。
(如果你没有会员资格,请阅读本文 点击这里)**.
设想一下: 你正在处理时间序列数据,寻找模式并调查随时间变化的趋势。
你已经对你的时间序列数据进行了探索性数据分析,并且已经寻找了最佳的异常值检测方法。
在检测之后,无论是忽略它们、删除它们,还是更常见的情况是,你已经对它们进行了转换。
现在是时候评估这种处理的影响了:你的数据分布发生了什么变化?你的机器学习模型在预测目标变量时效果如何?
此外,人们可能会好奇:
-
你将使用哪些指标来评估模型的表现?
-
你将如何可视化数据分布的变化?
-
哪些因素可能影响了你模型的预测?
-
数据中是否存在任何可能影响评估的偏差?
我们将通过以下方法回答其中的一些问题…
评估机器学习中的训练-测试集划分策略:超越基础
创建合适的测试集并安然入睡。
https://federicorucci.medium.com/?source=post_page---byline--c3e84b58ddce--------------------------------https://towardsdatascience.com/?source=post_page---byline--c3e84b58ddce-------------------------------- Federico Rucci
·发表于Towards Data Science ·阅读时间:5 分钟·2024 年 9 月 30 日
–
在这篇文章中,我想探讨一个常常被提问者和回答者忽视的问题:“如何将数据集划分为训练集和测试集?”
在处理监督学习问题时,通常做法是将数据集划分为(至少)两部分:训练集和测试集。训练集用于研究现象,而测试集则用于验证学习到的信息是否能够在“未知”数据上进行复制,也就是说,验证数据是否能够应用于在前阶段没有出现过的数据。
许多人通常采用标准、显而易见的方法来做出这一决策。常见且不那么令人兴奋的回答是:“我随机划分可用数据,将 20%到 30%保留为测试集。”
那些深入研究的人会添加分层随机抽样的概念:即在保持一个或多个变量的固定比例的同时进行随机抽样。假设我们处在一个二分类的情境中,且目标变量的先验概率为 5%。在目标变量上进行分层随机抽样意味着获取一个训练集和一个测试集,它们在目标变量的先验比例上保持 5%的比例。
这种推理有时是必要的,例如在非常不平衡的分类问题中,但它们并不会为…
使用 PydanticAI 进行智能体应用的评估驱动开发
一个开源的、模型无关的智能体框架,支持依赖注入
https://lakshmanok.medium.com/?source=post_page---byline--d9293ac81d91--------------------------------https://towardsdatascience.com/?source=post_page---byline--d9293ac81d91-------------------------------- Lak Lakshmanan
·发表于Towards Data Science ·12 分钟阅读·2024 年 12 月 21 日
–
理想情况下,你可以在开发过程中就评估智能体应用,而不是将评估视为事后的事情。不过,要实现这一点,你需要能够模拟你正在开发的智能体的内部和外部依赖。我对 PydanticAI 感到非常兴奋,因为它从根本上就支持依赖注入。它是第一个让我能够以评估驱动的方式构建智能体应用的框架。
克拉科夫布料大厅的图像,由作者使用 Google Imagen 生成。这个建筑是分阶段建设的,经过几个世纪的改进,改进的方向基于当前建筑的不足之处。换句话说,这是一种以评估驱动的开发方式。
在这篇文章中,我将讨论核心挑战,并演示如何使用 PydanticAI 以评估驱动的方式开发一个简单的智能体。
开发 GenAI 应用时的挑战
和许多 GenAI 开发者一样,我一直在等待一个支持完整开发生命周期的智能体框架。每当一个新框架出现时,我都会尝试,期望这次能是那个“终极框架”——例如,我的文章中提到过的 DSPy、Langchain、LangGraph 和 Autogen。
我发现,在开发基于 LLM 的应用程序时,软件开发者面临一些核心挑战。如果你正在构建一个简单的 GenAI 概念验证(PoC),这些挑战通常不是阻碍因素,但如果你在生产环境中构建基于 LLM 的应用程序,它们会成为问题。
什么挑战?
(1) 非确定性:与大多数软件 API 不同,向 LLM 发送完全相同的输入,每次调用可能会返回不同的输出。那么,你该如何开始测试这样的应用程序呢?
(2) LLM 的局限性:像 GPT-4、Claude 和 Gemini 这样的基础模型受限于其训练数据(例如,无法访问企业机密信息)、能力(例如,无法调用企业 API 和数据库),并且不能进行规划/推理。
(3) LLM 灵活性:即使你决定坚持使用来自单一供应商的 LLM(如 Anthropic),你可能会发现每个步骤需要不同的 LLM——也许你的工作流中的某个步骤需要一个低延迟的小型语言模型(Haiku),另一个步骤需要强大的代码生成能力(Sonnet),而第三个步骤需要出色的上下文意识(Opus)。
(4) 变化速率:GenAI 技术发展迅速。最近,许多改进出现在基础模型的能力上。基础模型不再只是根据用户提示生成文本。它们现在是多模态的,可以生成结构化输出,并且具备记忆能力。然而,如果你试图以 LLM 无关的方式构建,通常会失去开启这些功能的低级 API 访问权限。
为了解决第一个问题——非确定性问题,你的软件测试需要纳入一个评估框架。你永远不会有完全完美的软件;相反,你需要能够设计出一个在 x%准确的情况下运行的软件,构建保护措施和人工监督以捕捉例外,并实时监控系统以发现回归问题。实现这一能力的关键是评估驱动开发(我自己的术语),它是软件中测试驱动开发的扩展。
评估驱动开发。作者草图。
对于挑战#2 中的所有 LLM 局限性,目前的解决方法是使用代理架构(如 RAG),为 LLM 提供工具访问权限,并采用诸如反射(Reflection)、反应(ReACT)和思维链(Chain of Thought)等模式。因此,你的框架需要能够协调代理。然而,评估可以调用外部工具的代理是困难的。你需要能够为这些外部依赖项注入代理,以便单独测试它们,并在构建过程中进行评估。
为了处理挑战 #3,代理需要能够调用不同类型基础模型的能力。你的代理框架需要在代理工作流的单个步骤粒度上是与 LLM 无关的。为了应对变化速度的问题(挑战 #4),你需要保留对基础模型 API 的低级访问权限,并且能够去除不再需要的代码部分。
有没有一个框架能满足所有这些标准?很长一段时间,答案是否定的。我能做到的最接近的方式是使用 Langchain、pytest 的依赖注入以及 deepeval,像这样(完整示例请见这里):
from unittest.mock import patch, Mock
from deepeval.metrics import GEval
llm_as_judge = GEval(
name="Correctness",
criteria="Determine whether the actual output is factually correct based on the expected output.",
evaluation_params=[LLMTestCaseParams.INPUT, LLMTestCaseParams.ACTUAL_OUTPUT],
model='gpt-3.5-turbo'
)
@patch('lg_weather_agent.retrieve_weather_data', Mock(return_value=chicago_weather))
def eval_query_rain_today():
input_query = "Is it raining in Chicago?"
expected_output = "No, it is not raining in Chicago right now."
result = lg_weather_agent.run_query(app, input_query)
actual_output = result[-1]
print(f"Actual: {actual_output} Expected: {expected_output}")
test_case = LLMTestCase(
input=input_query,
actual_output=actual_output,
expected_output=expected_output
)
llm_as_judge.measure(test_case)
print(llm_as_judge.score)
本质上,我会为每次 LLM 调用构建一个 Mock 对象(如上例中的 chicago_weather),并在需要模拟代理工作流的部分时,将 LLM 调用(如上例中的 retrieve_weather_data)替换为硬编码的对象。依赖注入到处都是,你需要一堆硬编码的对象,调用工作流变得非常难以跟踪。注意,如果没有依赖注入,就无法测试这样的函数:显然,外部服务会返回当前天气,而对于像“现在下雨吗?”这样的问题,无法确定正确的答案。
那么…是否有一个支持依赖注入、Pythonic、提供低级 LLM 访问、与模型无关、支持逐步评估构建且易于使用和跟踪的代理框架呢?
几乎做到了。PydanticAI 满足了前三个要求;第四个(低级 LLM 访问)是不可能的,但设计上并不排斥这一点。在本文的其余部分,我将向你展示如何以评估驱动的方式使用它来开发一个代理应用。
1. 你的第一个 PydanticAI 应用程序
让我们从构建一个简单的 PydanticAI 应用开始。这个应用将使用 LLM 回答有关山脉的问题:
agent = llm_utils.agent()
question = "What is the tallest mountain in British Columbia?"
print(">> ", question)
answer = agent.run_sync(question)
print(answer.data)
在上面的代码中,我创建了一个代理(稍后我会告诉你如何做),然后调用 run_sync 传入用户提示,并获取 LLM 的响应。run_sync 是一种让代理调用 LLM 并等待响应的方式。其他方式是异步执行查询,或者流式返回响应。(完整代码 在这里,如果你想跟着一起做)。
运行上述代码,你将得到类似这样的结果:
>> What is the tallest mountain in British Columbia?
The tallest mountain in British Columbia is **Mount Robson**, at 3,954 metres (12,972 feet).
要创建代理,先创建一个模型,然后告诉代理在所有步骤中使用该模型。
import pydantic_ai
from pydantic_ai.models.gemini import GeminiModel
def default_model() -> pydantic_ai.models.Model:
model = GeminiModel('gemini-1.5-flash', api_key=os.getenv('GOOGLE_API_KEY'))
return model
def agent() -> pydantic_ai.Agent:
return pydantic_ai.Agent(default_model())
default_model() 背后的思路是使用像 Gemini Flash 这样相对廉价但快速的模型作为默认模型。然后,你可以通过传递不同的模型给 run_sync() 来根据需要更改特定步骤中使用的模型。
PydanticAI 模型支持看起来较为稀疏,但最常用的模型——来自 OpenAI、Groq、Gemini、Mistral、Ollama 和 Anthropic 的当前前沿模型——都得到了支持。通过 Ollama,你可以访问 Llama3、Starcoder2、Gemma2 和 Phi3。似乎没有什么显著缺失。
2. 使用结构化输出的 Pydantic
前一部分的示例返回的是自由格式的文本。在大多数智能体工作流程中,你会希望 LLM 返回结构化数据,以便可以直接在程序中使用。
考虑到这个 API 来自 Pydantic,返回结构化输出是相当直接的。只需将期望的输出定义为数据类(完整代码在这里):
from dataclasses import dataclass
@dataclass
class Mountain:
name: str
location: str
height: float
当你创建智能体时,告诉它期望的输出类型:
agent = Agent(llm_utils.default_model(),
result_type=Mountain,
system_prompt=(
"You are a mountaineering guide, who provides accurate information to the general public.",
"Provide all distances and heights in meters",
"Provide location as distance and direction from nearest big city",
))
另请注意使用系统提示来指定单位等。
在三个问题上运行此代码后,我们得到:
>> Tell me about the tallest mountain in British Columbia?
Mountain(name='Mount Robson', location='130km North of Vancouver', height=3999.0)
>> Is Mt. Hood easy to climb?
Mountain(name='Mt. Hood', location='60 km east of Portland', height=3429.0)
>> What's the tallest peak in the Enchantments?
Mountain(name='Mount Stuart', location='100 km east of Seattle', height=3000.0)
但是这个智能体有多好呢?罗布森山的高度正确吗?斯图尔特山真的是“魔法山脉”中最高的峰吗?这些信息可能是虚构的!
除非你将智能体与参考答案进行评估,否则你无法知道一个智能体应用的好坏。你不能仅凭眼睛“估算”。不幸的是,这正是许多 LLM 框架的不足之处——它们使得在开发 LLM 应用时评估变得非常困难。
3. 与参考答案进行评估
当你开始与参考答案进行对比评估时,PydanticAI 开始展现其优势。一切都非常符合 Python 风格,因此你可以非常简单地构建自定义评估指标。
例如,下面是我们如何在三个标准上评估返回的 Mountain 对象,并创建一个综合得分(完整代码在这里):
def evaluate(answer: Mountain, reference_answer: Mountain) -> Tuple[float, str]:
score = 0
reason = []
if reference_answer.name in answer.name:
score += 0.5
reason.append("Correct mountain identified")
if reference_answer.location in answer.location:
score += 0.25
reason.append("Correct city identified")
height_error = abs(reference_answer.height - answer.height)
if height_error < 10:
score += 0.25 * (10 - height_error)/10.0
reason.append(f"Height was {height_error}m off. Correct answer is {reference_answer.height}")
else:
reason.append(f"Wrong mountain identified. Correct answer is {reference_answer.name}")
return score, ';'.join(reason)
现在,我们可以在一组问题和参考答案的数据集上运行此功能:
questions = [
"Tell me about the tallest mountain in British Columbia?",
"Is Mt. Hood easy to climb?",
"What's the tallest peak in the Enchantments?"
]
reference_answers = [
Mountain("Robson", "Vancouver", 3954),
Mountain("Hood", "Portland", 3429),
Mountain("Dragontail", "Seattle", 2690)
]
total_score = 0
for l_question, l_reference_answer in zip(questions, reference_answers):
print(">> ", l_question)
l_answer = agent.run_sync(l_question)
print(l_answer.data)
l_score, l_reason = evaluate(l_answer.data, l_reference_answer)
print(l_score, ":", l_reason)
total_score += l_score
avg_score = total_score / len(questions)
运行此代码后,我们得到:
>> Tell me about the tallest mountain in British Columbia?
Mountain(name='Mount Robson', location='130 km North-East of Vancouver', height=3999.0)
0.75 : Correct mountain identified;Correct city identified;Height was 45.0m off. Correct answer is 3954
>> Is Mt. Hood easy to climb?
Mountain(name='Mt. Hood', location='60 km east of Portland, OR', height=3429.0)
1.0 : Correct mountain identified;Correct city identified;Height was 0.0m off. Correct answer is 3429
>> What's the tallest peak in the Enchantments?
Mountain(name='Dragontail Peak', location='14 km east of Leavenworth, WA', height=3008.0)
0.5 : Correct mountain identified;Height was 318.0m off. Correct answer is 2690
Average score: 0.75
罗布森山的高度偏差了 45 米;龙尾峰的高度偏差了 318 米。你会如何修正这个问题?
没错。你会使用 RAG 架构,或者为智能体提供一个能够提供正确高度信息的工具。我们就使用后者的方法,看看如何用 Pydantic 实现。
请注意,如何通过评估驱动开发向我们展示了改善智能体应用的前进道路。
4a. 使用工具
PydanticAI 支持多种向智能体提供工具的方式。在这里,我为一个函数添加了注解,以便在需要时调用它来获取山的高度(完整代码在这里):
agent = Agent(llm_utils.default_model(),
result_type=Mountain,
system_prompt=(
"You are a mountaineering guide, who provides accurate information to the general public.",
"Use the provided tool to look up the elevation of many mountains."
"Provide all distances and heights in meters",
"Provide location as distance and direction from nearest big city",
))
@agent.tool
def get_height_of_mountain(ctx: RunContext[Tools], mountain_name: str) -> str:
return ctx.deps.elev_wiki.snippet(mountain_name)
然而,这个函数做了一些奇怪的事情。它从智能体的运行时上下文中提取了一个名为 elev_wiki 的对象。这个对象在我们调用 run_sync 时被传入:
class Tools:
elev_wiki: wikipedia_tool.WikipediaContent
def __init__(self):
self.elev_wiki = OnlineWikipediaContent("List of mountains by elevation")
tools = Tools() # Tools or FakeTools
l_answer = agent.run_sync(l_question, deps=tools) # note how we are able to inject
因为运行时上下文可以传递给每次代理调用或工具调用,所以我们可以用它来进行依赖注入。在下一节中你会看到这一点。
维基本身只是查询在线的维基百科(代码在这里),提取页面内容并将适当的山脉信息传递给代理:
import wikipedia
class OnlineWikipediaContent(WikipediaContent):
def __init__(self, topic: str):
print(f"Will query online Wikipedia for information on {topic}")
self.page = wikipedia.page(topic)
def url(self) -> str:
return self.page.url
def html(self) -> str:
return self.page.html()
事实上,当我们运行时,现在得到了正确的高度:
Will query online Wikipedia for information on List of mountains by elevation
>> Tell me about the tallest mountain in British Columbia?
Mountain(name='Mount Robson', location='100 km west of Jasper', height=3954.0)
0.75 : Correct mountain identified;Height was 0.0m off. Correct answer is 3954
>> Is Mt. Hood easy to climb?
Mountain(name='Mt. Hood', location='50 km ESE of Portland, OR', height=3429.0)
1.0 : Correct mountain identified;Correct city identified;Height was 0.0m off. Correct answer is 3429
>> What's the tallest peak in the Enchantments?
Mountain(name='Mount Stuart', location='Cascades, Washington, US', height=2869.0)
0 : Wrong mountain identified. Correct answer is Dragontail
Average score: 0.58
4b. 依赖注入一个模拟服务
每次在开发或测试期间等待维基百科的 API 调用是不好的做法。相反,我们希望模拟维基百科的响应,这样我们就能快速开发,并确保得到预期的结果。
做这个非常简单。我们创建一个假的维基百科服务:
class FakeWikipediaContent(WikipediaContent):
def __init__(self, topic: str):
if topic == "List of mountains by elevation":
print(f"Will used cached Wikipedia information on {topic}")
self.url_ = "https://en.wikipedia.org/wiki/List_of_mountains_by_elevation"
with open("mountains.html", "rb") as ifp:
self.html_ = ifp.read().decode("utf-8")
def url(self) -> str:
return self.url_
def html(self) -> str:
return self.html_
然后,在开发过程中,将这个假对象注入到代理的运行时上下文中:
class FakeTools:
elev_wiki: wikipedia_tool.WikipediaContent
def __init__(self):
self.elev_wiki = FakeWikipediaContent("List of mountains by elevation")
tools = FakeTools() # Tools or FakeTools
l_answer = agent.run_sync(l_question, deps=tools) # note how we are able to inject
这次当我们运行时,评估使用了缓存的维基百科内容:
Will used cached Wikipedia information on List of mountains by elevation
>> Tell me about the tallest mountain in British Columbia?
Mountain(name='Mount Robson', location='100 km west of Jasper', height=3954.0)
0.75 : Correct mountain identified;Height was 0.0m off. Correct answer is 3954
>> Is Mt. Hood easy to climb?
Mountain(name='Mt. Hood', location='50 km ESE of Portland, OR', height=3429.0)
1.0 : Correct mountain identified;Correct city identified;Height was 0.0m off. Correct answer is 3429
>> What's the tallest peak in the Enchantments?
Mountain(name='Mount Stuart', location='Cascades, Washington, US', height=2869.0)
0 : Wrong mountain identified. Correct answer is Dragontail
Average score: 0.58
仔细观察上面的输出——它与零-shot 示例中的错误不同。在第二部分中,LLM 将温哥华选为最接近罗布森山的城市,将龙尾山选为魔法山脉中最高的峰。这些答案恰好是正确的。现在,它选择了贾斯珀和斯图尔特山。我们需要做更多工作来修复这些错误——但基于评估的开发至少给了我们一个前进的方向。
当前的局限性
PydanticAI 非常新,还有一些可以改进的地方:
-
当前没有对模型本身的底层访问。例如,不同的基础模型支持上下文缓存、提示缓存等。PydanticAI 中的模型抽象没有提供设置这些功能的方法。理想情况下,我们可以通过传递
kwargs
的方式来实现这种设置。 -
创建两个版本的代理依赖关系,一个真实的和一个假的,是很常见的。如果我们能够标注一个工具或提供一种简单的方法来在两种类型的服务之间切换,那将是非常好的。
-
在开发过程中,你不需要那么多的日志记录。但当你运行代理时,通常会希望记录提示和响应。有时,你还需要记录中间的响应。实现这个目标的方法似乎是一个叫做 Logfire 的商业产品。一个与 PydanticAI 库集成的开源、与云平台无关的日志框架将是理想的选择。
可能这些服务已经存在,我没注意到,或者在你读这篇文章的时候它们已经被实现了。不管是哪种情况,都请为未来的读者留个评论。
总的来说,我喜欢 PydanticAI——它提供了一种非常简洁且符合 Python 风格的方式,来以评估驱动的方式构建代理应用。
建议的下一步:
-
这是那种你通过实际运行示例会受益的博客文章,因为它描述了一个开发过程以及一个新的库。这个 GitHub 仓库包含了我在这篇文章中演示的 PydanticAI 示例:
github.com/lakshmanok/lakblogs/tree/main/pydantic_ai_mountains
。按照 README 中的说明进行操作试试。 -
Pydantic AI 文档:
ai.pydantic.dev/
-
使用 Mock 对象修补 Langchain 工作流。我的“前置”解决方案:
github.com/lakshmanok/lakblogs/blob/main/genai_agents/eval_weather_agent.py
使用聊天格式的评估
将聊天模板应用于生成式语言模型的评估测试
https://medium.com/@daniel_furman?source=post_page---byline--7604067023c9--------------------------------https://towardsdatascience.com/?source=post_page---byline--7604067023c9-------------------------------- Daniel Furman
·发布于 Towards Data Science ·7 分钟阅读·2024 年 2 月 21 日
–
图片由 Google DeepMind 提供,来源于 Unsplash
“构建扎实的评估应该是任何基于 LLM 的系统或产品的起点(以及传统的机器学习系统)。” — Eugene Yan, 链接
简要总结
聊天模型通常在使用提示模板格式的数据集上进行微调。这些聊天模板是编程好的“食谱”,能够将一次聊天对话转化为一个字符串。在预测时,通常需要匹配大语言模型(LLM)期望的聊天格式——如果不这么做,通常会被指出会导致性能下降 [1]。但是,实际上我们是否在评估基准上看到了这些性能下降?
注意:本博客适合具有 Python 编程和神经语言建模基础知识的读者。
介绍
如果你已经在 OpenAI 的聊天 API 上构建过应用,下面的代码将是你熟悉的。底层,这些输入会通过 ChatML 格式转换成一个可分词的字符串:
from openai import OpenAI
client = OpenAI()
response = client.chat.completions.create(
model="gpt-3.5-turbo",
messages=[
{"role": "system", "content": "You are a helpful assistant."},
{"role": "user", "content": "Who won the world series in 2020?"},
{"role": "assistant", "content": "The Los Angeles Dodgers won the World Series in 2020."},
{"role": "user", "content": "Where was it played?"}
]
)
"<|im_start|>system
You are a helpful assistant.
<|im_start|>user
Who won the world series in 2020?<|im_end|>
<|im_start|>assistant
The Los Angeles Dodgers won the World Series in 2020.<|im_end|>
<|im_start|>user
Where was it played?<|im_end|>
<|im_start|>assistant"
事实证明,LLM 研究社区中有各种各样的聊天模板。以开源模型 Mixtral-8x7B-Instruct-v0.1
. 为例,它的格式与上面提到的 gpt-3.5-turbo
看起来截然不同:
from transformers import AutoTokenizer
tokenizer = AutoTokenizer.from_pretrained("mistralai/Mixtral-8x7B-Instruct-v0.1")
chat = [
{"role": "user", "content": "Hello, how are you?"},
{"role": "assistant", "content": "I'm doing great. How can I help you today?"},
{"role": "user", "content": "Write me a haiku about coding."},
]
tokenizer.apply_chat_template(chat, tokenize=False)
"<s>[INST] Hello, how are you? [/INST]I'm doing great. How can I help you today?</s> [INST] Write me a haiku about coding. [/INST]"
为什么要使用聊天模板呢?其实,强烈建议在预测时匹配期望的聊天模板(例如,参见仓库中的“指令格式”信息,针对Mixtral-8x7B-Instruct-v0.1
)。而且,对于像gpt-3.5-turbo
这样的专有聊天模型,聊天模板通常在端点背后自动应用,无论你是否喜欢!
但是我们怎么知道聊天格式是否真的在提高我们的性能呢?这就是语言模型评估的作用。
语言模型评估
评估用于衡量 AI/ML 模型的性能,它们可以有许多不同的形式和大小。评估包括两个核心组件:针对特定任务策划的数据集和与之相关的衡量模型性能的指标。
生成性语言模型评估包含一些额外的细节。例如,不同的框架以不同方式衡量文本生成性能——即使是相同的评估也会有所不同(参考)。因此,在跨研究比较分数时,非常重要的一点是要确认结果是使用相同的代码和配置计算的,以避免错误分析。
超级的指令遵循评估(IFEval)[2]在这里用于我们的测试。该评估包括 541 个提示,用来衡量语言模型遵循可验证自然语言指令的能力。这些可验证指令的示例包括:
“写 450 到 500 字”,“你的所有输出应该是 JSON 格式”,“包括一个标题,并将其放入两个方括号中,例如[[ title ]]”
对于给定的响应和可验证指令,我们使用以下四个指标来检查该指令是否已被遵循:
1. 提示级严格准确度:每个提示中所有可验证指令都被遵循的百分比。
2. 指令级严格准确度:可验证的指令中被遵循的百分比。
3. 提示级宽松准确度:使用宽松标准计算的提示级准确度。
4. 指令级宽松准确度:使用宽松标准计算的指令级准确度。
这四个指标的平均值在此计算(表格 1),主要目的是使用一个捕捉最广泛信号的单一指标。
IFEval 是探索聊天模板影响的理想测试,因为该测试专门设计用来衡量在聊天数据上的指令遵循能力。另一个有趣的问题是,聊天模板是否对那些不太适合聊天数据的评估产生积极影响——这是一个留待未来研究的话题。
IFEval 的聊天模板
Eleuther.AI 的lm-eval是事实上的开源语言模型评估工具包。由于更多模型的聊天模板功能是用户常请求的新增功能,因此我们很容易与其他开发者协作,专注于在🤗模型类中实现这一功能。目前,开发工作正在add-chat-templating
分支中进行(链接),由问题#1098(链接)和#1209(链接)推动。当使用此分支时,我们可以按如下方式将聊天格式应用于评估:
!lm_eval --model hf \
--model_args=pretrained=meta-llama/Llama-2-70b-chat-hf,dtype="bfloat16",parallelize=True,device_map="auto",use_chat_template=True,system_prompt="You are a helpful assistant." \
--tasks ifeval \
--batch_size 16 \
--output_path output/Llama-2-70b-chat-hf \
--log_samples \
--num_fewshot 0
新引入的触发器use_chat_template
和system_prompt
出现在model_args
的右侧,用于控制聊天模板的应用方式。在当前分支的实验性版本中,代码在应用聊天模板前后打印第一个提示。这是上述代码块的效果:
# First element before prompt formatting...
('Write a 300+ word summary of the wikipedia page "https://en.wikipedia.org/wiki/Raymond_III,_Count_of_Tripoli". Do not use any commas and highlight at least 3 sections that has titles in markdown format, for example *highlighted section part 1*, *highlighted section part 2*, *highlighted section part 3*.', {'until': [], 'do_sample': False, 'temperature': 0.0, 'max_gen_toks': 1280})
# First element after prompt formatting...
('<s>[INST] <<SYS>>\nYou are a helpful assistant.\n<</SYS>>\n\nWrite a 300+ word summary of the wikipedia page "https://en.wikipedia.org/wiki/Raymond_III,_Count_of_Tripoli". Do not use any commas and highlight at least 3 sections that has titles in markdown format, for example *highlighted section part 1*, *highlighted section part 2*, *highlighted section part 3*. [/INST]', {'until': [], 'do_sample': False, 'temperature': 0.0, 'max_gen_toks': 1280})
输出已采用所需的聊天模板!
我们现在准备进行 A/B 测试,评估聊天模板对 IFEval 的影响。我们为实验选择了一些流行的 LLM,每个模型都有自己独特的聊天模板。在较大的模型方面,我们选择了 70B 参数的Llama-2–70b-chat
,两种 47B 参数模型的变体,Mixtral-8x7B-Instruct-v0.1
和Nous-Hermes-2-Mixtral-8x7B-DPO
,以及 34B 参数的Nous-Hermes-2-Yi-34B
。在较小的模型方面,我们有三个 7B 参数的模型:Mistral-Instruct-7B-v0.2
、Zephyr-7b-beta
和Starling-LM-7B-alpha
。至于系统提示,兼容模型使用了简单的提示“你是一个有帮助的助手。”更多关于这七个模型的详细信息请参见下文[3]。
现在,毫不拖延,我们的结果:
表 1:来自 IFEval 的 A/B 测试结果,按模型大小降序排列(链接)。有关更多详细信息,请参见下面的“附加说明”部分,例如运行日志的链接。为了保证可重复性,实验在半精度 bfloat16 模型上执行,工作站配置了 2 个 H100 80GB SXM5 芯片,并使用了lm-eval
包的分支,哈希值为0c0c314c0df4c10f35bf7c17dc80f745f8027e9b。
🔥 聊天模板对 IFEval 评分产生了重大影响!Nous-Hermes-2-Mixtral-8x7B-DPO
作为测试中表现最好的模型,平均得分约为 63%。相比之下,Zephyr-7b-beta
是表现最差的模型,但却从聊天模板中获得了最大的提升——惊人的 +39%!作为参考,IFEval 论文中报告的 gpt-4
(2023 年 11 月)平均得分约为 81%,PaLM 2S
(2023 年 8 月)为约 51% [2]。
总结来说,这些结果揭示了几个关键的洞察:
-
聊天模板对开源 LLM 的指令跟随有积极影响,其影响程度因模型而异。
-
开源 LLM 在遵循自然语言指令方面不如 SOA 专有模型,如
gpt-4
。
结论
聊天模板在我们的实验中显著提升了 IFEval 的评分,这在各种格式和模型中都得到了证明。然而,我并不一定期待这些效果能普遍适用于所有 LM 评估。为了进一步探讨聊天模板对基准的影响,下一步包括进行以下实验:
-
更多类似 IFEval 的指令跟随评估
-
一般用途评估,例如 🤗 的开放 LLM 排行榜
-
上下文检索评估,例如“Needle in a Haystack”
-
还有更多,更多内容!
从三万英尺的高度来看,现在是进行 LM 评估研究的好时机——首先,因为更强大的 LLM 需要新一代测试来有效评估它们。无论是创建自己的评估方法,还是在现有方法的基础上进行改进,研究评估是为开放科学社区做出贡献的一种重要方式。
引用
[1] Matthew Carrigan(2023),聊天模板:终结沉默的性能杀手,Hugging Face。
[2] Zhou 等人(2023),大规模语言模型的指令跟随评估,arXiv。
- 数据集许可:此处使用的 IFEval 数据集对所有人公开,无限制使用(Apache-2.0 许可证)。
[3] 此处使用的模型,按大小排列(所有模型均已获得研究使用的宽松许可)。
-
Llama-2–70b-chat
(链接)— Meta -
Mixtral-8x7B-Instruct-v0.1
(链接)— Mistral.AI -
Nous-Hermes-2-Mixtral-8x7B-DPO
(链接)— Nous-Research -
Nous-Hermes-2-Yi-34B
(链接)— Nous-Research -
Starling-LM-7B-alpha
(链接)— Berkeley NEST -
Zephyr-7B-beta
(链接)— Hugging Face -
Mistral-7B-Instruct-v0.2
(链接) — Mistral.AI
其他注意事项
-
查看用于运行实验的代码,可以在这里找到。
-
要审计结果,请查看每次运行的输出,这里,以及 Zeno 日志,这里和这里(模型在 2 个批次中运行)。请注意,Zeno 日志尚未捕捉到聊天模板应用于提示的过程——这是开发待办事项中的一项内容。
-
在计算方面,使用了 RunPod (链接) 访问带有 Nvidia GPU 芯片的工作站——特别是一个拥有 2 个 H100 80 GB SXM5 芯片的集群。总的来说,实验包括了 14 次 IFEval 的运行,总共积累了约 6 小时的集群运行时间。
-
通过置信区间估计我们的结果中的统计不确定性(使用了自助法重抽样方法)。这些 95%的置信区间大约在+/- 2.75%到 4.25%之间——相对于聊天模板应用的测量效果来说,这个范围较小。
事件研究设计:初学者指南
它们是什么,它们又不是什麼
https://medium.com/@arieda.muco?source=post_page---byline--bbacd3ac93b3--------------------------------https://towardsdatascience.com/?source=post_page---byline--bbacd3ac93b3-------------------------------- Arieda Muço
·发表于 Towards Data Science ·阅读时长 8 分钟·2024 年 7 月 20 日
–
在本文中,我尝试澄清应用计量经济学家的工具箱中的基本工具:差异中的差异(DiD)和事件研究设计。本文主要受到我的学生们的启发,简明地介绍了基本概念,并解决了常见的误解,这些误解经常让实践者感到困惑。
如果你想知道为什么标题专注于事件研究,而我又在谈论 DiD,那是因为在因果推断方面,事件研究是差异中的差异的一个推广。
但在深入探讨之前,请让我向你保证,如果你感到困惑,可能是有其正当理由的。近年来,DiD 文献的快速发展带来了许多新的方法论,使得跟上其步伐变得具有挑战性。事件研究设计的起源也未必能帮助澄清这些问题……
事件研究的起源
金融学的起点
事件研究起源于金融学,旨在评估特定事件(如财报公告或并购)对股价的影响。事件研究由 Ball 和 Brown(1968)开创,为该方法奠定了基础。
财务中的事件研究
方法论
在金融学中,事件研究方法论涉及识别一个事件窗口,用于衡量“异常收益”,即…
机器学习生命周期的每个步骤简单解释
一份全面的机器学习生命周期指南,逐步讲解并提供 Python 示例
https://medium.com/@pelletierhaden?source=post_page---byline--d1bca7c1772f--------------------------------https://towardsdatascience.com/?source=post_page---byline--d1bca7c1772f-------------------------------- Haden Pelletier
·发表于 Towards Data Science ·15 分钟阅读·2024 年 11 月 26 日
–
机器学习生命周期。图片来自作者
机器学习生命周期
如果你在数据科学领域待了一段时间,你很可能听过这个流行的术语。
机器学习生命周期。
听起来很复杂,但实际上它归结为:
-
机器学习是一个动态和活跃的过程——它没有严格的开始或结束。
-
一旦模型被训练并部署,它很可能需要随着时间的推移重新训练,从而重新启动整个生命周期。
-
然而,生命周期中的一些步骤需要按照正确的顺序执行,并且需要谨慎操作。
当你在谷歌搜索机器学习生命周期时,每个来源可能会给出略有不同的步骤数量和名称。
然而,你会注意到,大部分情况下,生命周期包含以下步骤:问题定义、数据收集与预处理、特征工程、模型选择与训练、模型评估、部署和监控。
1. 定义问题
你可以用 Python 的 textwrap 模块做的一切
了解你可以用 Python 的textwrap
模块做的所有事情,包括格式化、文本换行、修剪等等。
https://medium.com/@martin.heinz?source=post_page---byline--0d82c377a4c8--------------------------------https://towardsdatascience.com/?source=post_page---byline--0d82c377a4c8-------------------------------- Martin Heinz
·发布在Towards Data Science ·阅读时长 5 分钟·2024 年 2 月 7 日
–
图片来自Hello Sunday,Unsplash
Python 有许多格式化字符串和文本的选项,包括 f-string、format()
函数、模板等等。然而,有一个模块很少有人知道,它叫做textwrap
。
本模块专门为帮助你处理换行、缩进、修剪等任务而构建,在本文中,我们将探讨你可以用它做的所有事情。
Shorten
让我们从textwrap
模块中的一个非常简单但非常实用的函数——shorten
开始:
from textwrap import shorten
shorten("This is a long text or sentence.", width=10)
# 'This [...]'
shorten("This is a long text or sentence.", width=15)
# 'This is a [...]'
shorten("This is a long text or sentence.", width=15, placeholder=" <...>")
# 'This is a <...>'
顾名思义,shorten
允许我们在指定的字符串过长时将文本修剪为一定长度(width
)。默认情况下,修剪后的文本占位符为[...]
,但可以通过placeholder
参数覆盖此默认值。
Wrap
这个模块中的另一个更有趣的函数是wrap
。它的明显用途是将长文本拆分为相同长度的多行,但我们还能用它做更多的事情:
from textwrap import wrap
s = '1234567890'
wrap(s, 3)
# ['123', '456', '789', '0']
在这个示例中,我们将一个字符串分成相等的块,这在批处理时比仅仅格式化更有用。
然而,使用这个函数时需要注意一些事项:
s = '12\n3 45678\t9\n0'
wrap(s, 3)
# ['12', '3 ', '456', '78', '9 0']
# the first ("12") element "includes" newline
# the 4th element ("78") "includes" tab
wrap(s, 3, drop_whitespace=False, tabsize=1)
# ['12 ', '3 ', '456', '78 ', '9 0']
使用wrap
时,您应该小心空格——上面您可以看到换行符、制表符和空格字符的行为。您可以看到第一个元素(12
)*"包含"换行符,第 4 个元素(78
)“包含”*制表符,但这些默认会被丢弃,因此这些元素的字符数只有 2 个,而不是 3 个。
我们可以指定drop_whitespace
关键字参数来保留它们并保持块的适当长度。
这可能显而易见,但wrap
对于将整个文件重新格式化为特定的行宽也是非常有用的:
with open("some-text.md", "r", encoding="utf-8") as f:
formatted = wrap(f.read(), width=80) # List of lines
formatted = fill(f.read(), width=80) # Single string that includes line breaks
# ... write it back
我们还可以使用fill
函数,它是"\n".join(wrap(text, ...))
的简写。这两者的区别在于,wrap
会给我们一个行列表,我们需要自己将它们连接起来,而fill
则会给我们一个已经使用换行符连接的单一字符串。
TextWrapper
textwrap
模块还包括一个更强大的wrap
函数,它是一个TextWrapper
类:
import textwrap
w = textwrap.TextWrapper(width=120, placeholder=" <...>")
for s in list_of_strings:
w.wrap(s)
# ...
如果我们需要多次使用相同参数调用wrap
,如上所示,这个类及其wrap
方法是非常有用的。
在查看TextWrapper
时,让我们也尝试一些其他的关键字参数:
user = "John"
prefix = user + ": "
width = 50
wrapper = TextWrapper(initial_indent=prefix, width=width, subsequent_indent=" " * len(prefix))
messages = ["...", "...", "..."]
for m in messages:
print(wrapper.fill(m))
# John: Lorem Ipsum is simply dummy text of the
# printing and typesetting industry. Lorem
# John: Ipsum has been the industry's standard dummy
# text ever since the 1500s, when an
# John: unknown printer took a galley of type and
# scrambled it to make a type specimen
在这里我们可以看到initial_indent
和subsequent_indent
的使用,分别用于缩进段落的第一行和后续行。还有一些其他选项,您可以在文档中找到。
此外,由于TextWrapper
是一个类,我们还可以扩展它并完全重写它的一些方法:
from textwrap import TextWrapper
class DocumentWrapper(TextWrapper):
def wrap(self, text):
split_text = text.split('\n')
lines = [line for par in split_text for line in TextWrapper.wrap(self, par)]
return lines
text = """First line,
Another, much looooooonger line of text and/or sentence"""
d = DocumentWrapper(width=50)
print(d.fill(text))
# First line,
# Another, much looooooonger line of text and/or
# sentence
这是一个很好的例子,展示了如何修改wrap
方法,以保留现有的换行符并正确打印它们。
要了解如何使用TextWrapper
处理多个段落的完整示例,请查看这篇文章。
缩进
最后,textwrap
还包括两个用于缩进的函数,第一个是dedent
:
# Ugly formatting:
multiline_string = """
First line
Second line
Third line
"""
from textwrap import dedent
multiline_string = """
First line
Second line
Third line
"""
print(dedent(multiline_string))
# First line
# Second line
# Third line
# Notice the leading blank line...
# You can use:
multiline_string = """\
First line
Second line
Third line
"""
# or
from inspect import cleandoc
cleandoc(multiline_string)
# 'First line\nSecond line\nThird line'
默认情况下,Python 中的多行字符串会保留字符串中使用的任何缩进,因此我们需要使用上面片段中第一个变量所示的丑陋格式。但我们可以使用dedent
函数来改善格式——我们只需按自己喜欢的方式缩进变量值,然后在使用它之前调用dedent
。
另外,我们也可以使用inspect.cleandoc
,它也会去除前导换行符。然而,这个函数会将空格编码为特殊字符(\n
和\t
),因此您可能需要重新格式化它。
自然,当有dedent
时,也需要有indent
函数:
from textwrap import indent
indented = indent(text, " ", lambda x: not text.splitlines()[0] in x)
我们只需提供文本和每行将缩进的字符串(这里是 4 个空格,我们可以——例如——使用>>>
使其看起来像 REPL)。此外,我们还可以提供一个谓词,用来决定该行是否应该缩进。在上面的例子中,lambda
函数使得字符串的第一行(段落)不被缩进。
结束语
textwrap
是一个简单的模块,只有少数几个函数/方法,但它再次证明了 Python 确实为那些不一定需要标准库的功能提供了*“开箱即用”*的支持,当你恰好需要它们时,它们能为你节省大量时间。
如果你恰好做很多文本处理工作,那么我也推荐你查看一下专门处理文本的整个文档部分。这里有许多你可能没意识到需要的模块和小功能。😉
本文最初发布在 martinheinz.dev
你可能还喜欢…
[## 你可以用 Python 的 Bisect 模块做的一切
学习如何使用 Python 的“bisect”模块来优化搜索并保持数据排序
关于图形数据库和 Neo4j 的所有你需要知道的事
了解图形数据库:关键概念与优势
https://medium.com/@martin-jurran?source=post_page---byline--b9154f57dad0--------------------------------https://towardsdatascience.com/?source=post_page---byline--b9154f57dad0-------------------------------- 马丁·朱兰
·发表于数据科学之道(Towards Data Science) ·13 分钟阅读·2024 年 7 月 26 日
–
(图片由作者提供,插图来自高桥美船(Takashi Mifune)在免费使用下)
存储和处理数据是软件工程中的基本任务。在大型专业开发的早期阶段,关系数据库如 Oracle、IBM DB2 和 SQL 占据主导地位。数据操作系统无法轻松处理结构化或关系型数据,而只能处理扁平化的数据表示。[1] 图形数据库试图弥合关系型数据和扁平数据表示之间的差距,同时使访问信息变得更加容易。[2] 这种类型数据库的最受欢迎代表是 Neo4j。[3]
Name: Neo4j
Software Type: Graph Database (GDB)
Initial Release: 2007
Origin: Neo4j, Inc.
Target Platform: Cross-Platform, e.g. Windows, Linux, ..
Languages: Implemented in Java and Scala, Web-Tools in Typescript, Cloud functionalities in Go
Website: https://neo4j.com/
介绍
当今世界的各种交易越来越依赖数字化。也就是说,由于大多数国家(例如德国)卡片和电子支付方式的使用显著增加。[4] 随着交易变得更加数字化,IC3 投诉统计等指标表明,数字犯罪活动也在增加。[10] 例如,作为支付处理软件供应商的 TransUnion 报告称,全球数字欺诈尝试增长了 149%。[5]
如果不使用图形数据库,涉及此类活动的人的交易和关系需要以关系型的方式建模。但是,如果使用更合适的数据库类型——图形数据库,建模和访问关系数据要容易得多。
让我们通过一些例子来看看图形数据库在哪些方面非常有用。
示例 1 — 揭露巴拿马文件 ⚖️
《巴拿马文件》Neo4j 数据库数据模型。[13](图片由作者提供)
2016 年,大量文件泄露给了德国记者巴斯蒂安·奥伯迈耶(Bastian Obermayer),他来自报纸南德意志报。[6]
泄露的文件包含了前所未见的大规模税务逃避和洗钱线索。此次泄露包含了 2.6TB 的数据,由 1150 万个单独的文件组成。[7]
为了处理和调查主要由商业主体之间的关系数据构成的数据,基于图形的方法显而易见。提到的人物和公司可以作为1) 节点,而关系的类型和属性则作为2) 边。在本节的特色图片中,您可以看到《巴拿马文件》是如何在 Neo4j 中建模的。
示例 2 — 物业管理 ️🛫️
示例:如何在 Neo4j 中建模一个物业管理的应用场景(图片由作者提供,插图来自Takashi Mifune 在免费使用许可下)
如同那些在机场待得太久的人所证明的那样,机场里有无数对象,它们之间相互关联。
从飞机到航站楼,再到登机口和餐厅,机场的每个元素都是互联的,并依赖于多种关系来顺利运作。
这就是图形数据库所能支持的功能。通过将机场、航站楼、飞机、餐厅等作为图中的节点,以及它们之间的关系作为边,物业管理人员可以更深入地了解机场的运营情况。
示例 3 — JR 东日本的火车运营 🚆
我们针对东京公共交通示例的 Cypher 查询结果。[ 14 ](图片由作者提供)
公共交通涉及许多不同的对象,如车站和站台,这些对象之间有相互连接。
这些连接可以有额外的细节,例如在公共交通网络中从一个点到另一个点的旅行时间。
这种网络的一个例子是东京的火车线路和车站。当我们将它们加载到 Neo4j 中时,可以使用它的查询语言 Cypher 来运行查询,收集有关这些对象及其连接的信息。[14]
MATCH (source:Station {name: '高田馬場'}), (destination:Station {name: '池袋'})
MATCH path = shortestPath((source)-[r*]-(destination))
RETURN path,
reduce(cost = 0, rel in relationships(path) |
cost + coalesce(rel.cost, 0)) AS total_cost LIMIT 1
本节的特色图片展示了我们查询的结果。为了验证我们的结果是否正确,我使用了谷歌地图来计算路线,令我惊讶的是,结果与使用 Cypher 查询得到的结果相同。用 Neo4j 实现路线优化算法一定很容易。
在谷歌地图中查看 Cypher 查询结果:我们的输出是有意义的,Neo4j 帮助计算了最短路线。(图片由作者提供)
如果你想亲自尝试,我已经 fork 了原始的代码库,并包含了我请求的 Cypher 文件。为了获得最佳体验,建议你具备一些基础的日语知识。祝你玩得开心!🤗
[## GitHub - martinjurran/neo4j-train-route-sample: 一个用于最短路径查询或行程规划查询的示例数据库…
一个用于最短路径查询或行程规划查询的示例数据库,使用 Neo4j(用日语)。…
github.com](https://github.com/martinjurran/neo4j-train-route-sample?source=post_page-----b9154f57dad0--------------------------------)
图数据库的替代方案
有许多场景可以通过图形来展示。这些数据也可以存储在关系数据库中——每种对象类型有一个表,通过外键来建模它们与其他对象的关系。然后,可以使用SQL查询中的连接来实现数据处理。随着NoSQL解决方案的趋势[8],根据具体的使用场景,使用除SQL之外的数据库变体变得更为可接受。
如果你在犹豫是否应该选择图数据库,看看这个概述:
重要的数据库类型及其典型应用和最流行的代表(照片由作者提供)
图数据库 Neo4j
图数据库的第一个,也是最著名的代表,是 Neo4j。这个名字原本计划为 NeoDB,但 NeoDB.com 在发布时已被占用,所以团队决定将应用命名为 Neo4j。
今天,Neo4j 不再是一个嵌入式 Java 应用程序,但它的名称中仍然保留了一段历史。
Neo4j 的历史
Neo4j 历史上重要事件的自创时间轴(照片由作者提供)
2000 年:概念化。 创始人们对基于关系数据库(RDMBS)的内容管理系统(CMS)感到困扰,该系统使用的是 Informix。[15]。在CMS中实现他们的用例导致了大量复杂的 SQL 查询的编写,而这些查询越来越难以维护。
创始人们认为他们的数据是相互连接的,构成了我们相关内容项目、元数据、标签和元标签之间的一种路径[12],这最终促使了属性图模型的发展。
2000–2002: 在 RDBMS 之上的图层。 第一步是在名为 Informix 的关系数据库之上编写一个图层[12]。[15]
2002: Neo4j 的第一个版本。 在 Informix 之上的图层遇到了一些挑战。问题在于,Informix 并未针对处理图状数据之间的所有关系进行优化[15]。
面对使用 RDBMS 处理连接数据的挑战,开发者决定创建一种针对连接数据优化的新型数据库[15]。
2007: 图数据库一词的发明。 图数据库这一词由 Emil Eifrem 发明,基于 Facebook 营销声明“我们是社交图谱的工具”而产生。他将“图”和“数据库”两个词结合,这就诞生了图数据库这一术语[16] [17]。
2007: Neo4j Technologies 的创立。 这家公司成立了,但主要从事咨询工作,因为当时还没有自己的产品可以销售[17]。
2007: 图数据库完全本地化。 原型变成了一个完全本地化的图数据库[18]。最初它是作为一个嵌入式 Java 数据库启动的[8]。
2010: Neo4j 1.0 的发布。 [18]
2011: Cypher 的开发。 第一个面向属性图的声明式查询语言诞生了[18]。它的灵感来自于 MS Visio 中将对象和关系放置在用户界面上的方式,因此它是一种非常人性化的查询模型[8]。
随着时间的推移,Neo4j 从一次飞行中草拟的原型[21],发展为一个独立的数据库应用,满足了客户的需求。这些需求也反映在解决方案的架构中,接下来会进行阐述。
利益相关者
要了解Neo4j的架构,必须理解可能影响架构决策的利益相关者,正如Rozanski 和 Woods所述。[20]
以下权力/利益矩阵列出了应用程序的利益相关者,并将他们置于不同的视角,以可视化他们对架构决策的影响可能性。
Neo4j 的权力/利益矩阵(作者拍摄的照片)
那些既拥有高权力又有高兴趣的利益相关者,如 Neo4j 的投资者[22]、Neo4j 公司本身及其合作伙伴[23],对软件架构决策有着重要的影响力。需要密切管理他们。[24]
尽管会考虑到企业客户的需求和要求,但由于他们在软件上的投资不如其他利益相关者,因此他们的影响相对有限。需要保持他们的满意度。[24]
竞争者可能通过挑战其市场地位间接影响 Neo4j 的方向。开发者通常已经为他们的产品设定了技术栈,并没有足够的能力单独影响 Neo4j 的架构。他们需要被监控。[24]
开源社区和个人贡献者在塑造软件架构的讨论和发现漏洞方面起着重要作用。尽管如此,他们的影响力有限。正确的做法是保持他们的知情。[24]
依赖关系
为了更好地理解 Neo4j 所基于的架构,了解其当前的依赖关系以及这些依赖关系如何塑造 Neo4j 成为今天的解决方案是至关重要的。
Neo4j 的依赖关系上下文(作者提供的照片)
架构目标
下表展示了 Neo4j 的主要架构目标,表中顺序代表其重要性。现在,让我们探索软件为实现这些目标所采用的各种技术。
达成所有架构目标(作者提供的照片,插图来自Takashi Mifune,使用自由许可)
1. 🟢 易用性。 这是一个独立的平台,具有图形原生的数据建模、用户友好的查询语言 Cypher 以及易于理解的全面开发者文档。这使得使用 Neo4j 变得简单,所有相关的利益相关者都能获得良好的体验。(开发者视角)
2. 🟠 性能。 Neo4j 是一个图形原生数据库,配备了多种优化功能,确保复杂图形数据的快速查询响应时间。该平台还拥有高并发性和一致性特性。此外,图算法的运行效率非常高。
3. 🔵 可靠性。 对任何数据库平台来说,可靠性至关重要。Neo4j 通过 ACID 事务、监控、事件日志记录、TLS 数据加密和权限系统实现这一点。
4. 🟣 安全性。 数据涉及时,安全性尤为重要。Neo4j 通过事件日志记录、TLS 数据加密、精细粒度的权限系统以及静态数据加密来实现安全。
5. 🟡 互操作性。 能在所有相关的目标系统上运行。能够轻松集成到现有环境中。支持多种数据访问客户端。
6. 🔴可用性。 能在集群中运行,从而提高可用性以满足企业需求。
7. 🟤 可扩展性。 支持水平和垂直扩展,能够处理越来越多的数据和查询量。
8. ⚪ 可扩展性。 无论是 Neo4j 开发团队还是外部社区开发者,都可以轻松扩展解决方案。这适用于修改现有组件或添加新功能。Neo4j 对新的技术和方法趋势持开放态度。
🟢 易用性
大多数开发人员至少对关系数据库的工作原理有基本的了解,但基于图的解决方案对大多数人来说是新的。为了确保 Neo4j 的成功,开发者能够迅速适应该解决方案,并在没有不必要障碍的情况下构建可行的产品至关重要。
公共交通的概念性和实施数据模型(作者提供的照片)
支持开发者适应的是,图形数据库通常与其概念数据模型相同。它们是无模式的,像大多数 NoSQL 解决方案一样。数据以节点和边的形式表示。
非开发人员可以通过交互式 Neo4j 浏览器探索数据。这包括从白板上的初步构想到使用 Cypher 语言(等同于 SQL)开发查询。
在 Neo4j 浏览器中探索数据,基于 Cypher 查询的结果(作者提供的照片)
Neo4j 高效执行涉及多个节点和边的复杂查询的能力,主要归功于其专用的图数据模型。关系直接以数据库中的数据结构形式体现,允许“指针”在没有多次子查询和连接的情况下遍历。Neo4j 不是基于现有数据库,而是一个高度专业化的实现。
🟠 性能
Neo4j 是一个高度优化的图形原生数据库,专门优化以处理大规模的图操作。图查询执行得更快、更高效,因为所有相关数据都存储在一个地方并以图的形式反映出来。
此外,Neo4j 实现了多个专为图形优化的性能提升:
-
B 树 用于快速检索图数据集中的节点和边
-
专为图形数据设计的索引,可以为单个属性定义多个索引选项
⚪ 可扩展性
Neo4j 提供了官方的 Bolt 驱动程序,支持 .NET、Java、JavaScript、Go 和 Python。社区实现的驱动程序也可以用于 C/C++ 和 PHP。此外,无论使用什么编程语言,都可以通过 HTTP API 访问 Neo4j 数据库。
有时,开发者需要通过创建自己的过程或函数来扩展 Neo4j 数据库的功能。Neo4j 提供了插件和未管理的服务器扩展,用于 HTTP 端点,支持此功能。文档提供了 JVM 语言的实现指南。
Neo4j 提供了多种方式来扩展其功能并保持与趋势的同步。开发者可以通过 Cypher 使用 Bolt 和 HTTP 访问协议,或创建自己的插件来改进 Neo4j 软件。
Neo4j 最近的更新包括 GraphQL 集成和对流行流媒体解决方案(如 Kafka 和 Spark)的连接器。此外,Neo4j 提供了一个 Graph Data Science 库,提供多种算法和机器学习建模选项,涵盖与数据分析和机器学习相关的用例。
**全局视野(**🟢/🟠/🔵/🟣/🟡/🔴/🟤/⚪)
现在,我们已经探索了一些个别架构目标以及 Neo4j 如何解决它们,让我们退一步,展望整体架构图。
想象一个互联的架构目标图,每个节点代表一个特定目标,例如数据一致性或可用性,每个关系代表这些目标如何相互关联或依赖:
Neo4j 企业架构目标图(作者提供的照片)
架构结构
为了更好地理解 Neo4j 如何实现其架构目标,我们应从更技术的角度来看待该解决方案。
关于典型 Neo4j 设置的粗略概览 [22](作者提供的照片)
Neo4j 对软件架构师的优势
现在你已经掌握了有关图形数据库和 Neo4j 的所有信息。那么,如何利用这些信息呢?使用图形数据库可以立即为你和你的工作带来积极的影响:
-
高性能的复杂查询
-
数据建模的更大灵活性
-
处理非结构化数据的能力
-
改进的可扩展性和可用性
-
更容易与其他技术集成
-
更高的业务敏捷性
结论
Neo4j 因其处理图形数据的能力而在现代软件架构中获得认可。但今天,它已经不仅仅是处理图形数据的万能解决方案。
对于今天的软件架构师而言,选择专用数据库来处理特定用例非常重要,而不是围绕单一数据后端构建完整的解决方案。尤其是在微服务架构仍在持续增长的情况下,这一点尤为有效。
如果你还没有开始,我个人鼓励你探索一些测试用例,以发现图数据库的强大功能以及它们如何为你带来益处。无论是物流、金融、医疗、社交媒体还是电子商务,图数据库都能提供传统数据库无法提供的洞察。
来源
[1, 2] Silvescu, Adrian & Caragea, Doina & Atramentov, Anna. (2002). 图数据库。
[3] DB-Engines 图数据库管理系统排名 2024 db-engines.com/en/ranking/graph+dbms)
)
[4] 德国联邦银行 (2022 年 7 月 7 日). 2021 年德国支付行为报告。 www.bundesbank.de/en/press/press-releases/payment-behaviour-in-germany-in-2021-894120
[5] Leonhardt, M. (2021 年 6 月 3 日). 美国在线欺诈尝试增加了 25%——原因在这里。 CNBC. www.cnbc.com/2021/06/03/why-online-fraud-attempts-are-up-25percent-in-the-us.html
[6] Clark, Nicola (2016 年 4 月 5 日). “一条神秘信息‘对数据感兴趣?’是如何引发巴拿马文件的”. 纽约时报. ISSN 0362–4331. 存档 于 2016 年 8 月 15 日。
[7] “关于巴拿马文件调查”. 国际调查记者联盟. 2018 年 1 月 31 日。 存档 于 2020 年 7 月 24 日。
[8] Emil Eifrem (2017 年 7 月 27 日), 你好,世界:Neo4j 公司 neo4j.com/blog/hello-world-neo4j-inc/
[9] Dr. Jim Webber (2022 年 6 月 8 日). Neo4j 的个人历史. Neo4j Inc. www.youtube.com/watch?v=YB723cp9jgM
[10] 美国联邦调查局 (2023). 网络犯罪报告。 www.ic3.gov/Media/PDF/AnnualReport/2023_IC3Report.pdf
[11] Gopala Kr (2017). Neo4j 架构。 github.com/gopala-kr/10-weeks/blob/master/Projects-Blogs/07-bigdata-databases/neo4j-architecture.md
[12] Dr. Jim Webber(2017 年 5 月 2 日),Neo4j 作为原生图数据库的工程演变。neo4j.com/blog/evolution-neo4j-native-graph-database/
[13] William Lyon(2018 年 12 月 3 日),在 Neo4j 中对巴拿马文件数据的图形可视化,medium.com/neo4j/graph-visualization-of-panama-papers-data-in-neo4j-9c08ca17039c
[14] ggszk(2020 年),Neo4j 示例数据库:东京铁路路线(日本语),github.com/ggszk/neo4j-train-route-sample
[15] Emil Eifrem(2016 年 3 月 29 日),DB-Engines,Informix 与 Neo4j:起源故事,neo4j.com/blog/db-engines-informix-neo4j/?ref=blog
[16] Emil Eifrem (未知),图数据库的诞生:Neo4j 如何构建其产品和类别,neo4j.com/news/birth-graph-databases-neo4j-built-product-category/
[17] Alastair Dryburgh(2007 年 3 月 22 日),成长故事:名字的神奇力量,www.forbes.com/sites/alastairdryburgh/2017/03/22/growth-stories-the-magical-power-of-a-name/#49b4ebe56db9
[18] Neo4j 公司,Neo4j 的历史——开源,大社区,neo4j.com/open-source-project/
[19] Emil Eifrem(2016 年 3 月 22 日),Twitter 帖子,twitter.com/emileifrem/status/712327903032188928
[20] Rozanski, Nick 和 Eóin Woods。《软件系统架构:与利益相关者一起使用视角和观点》。Addison-Wesley,2012 年。
[21] Emil Eifrem(2022 年 8 月 8 日),这个数据库的第一个代码是在印度孟买的 IIT 编写的,www.youtube.com/watch?v=Nhi4XwmCh9A
[22] Crunchbase,Neo4j 简介,www.crunchbase.com/organization/neo-technology
[23] Neo4j 公司,合作伙伴目录,neo4j.com/partners/directory/
[24] Latha Thamma Reddi(2023 年 4 月 14 日),使用权力利益网格进行利益相关者分析,www.projectmanagement.com/wikis/368897/stakeholder-analysis--using-the-power-interest-grid
图标由かわいいフリー素材集 いらすとや (irasutoya.com),© Takashi Mifune
(作者提供的照片,插图由Takashi Mifune 在免费使用许可下提供)
数据科学的演变:现代端到端数据科学家的新时代技能
从 Python 脚本编写到数据工程、MLOps 与生成型 AI
https://col-jung.medium.com/?source=post_page---byline--1ab87a8839ab--------------------------------https://towardsdatascience.com/?source=post_page---byline--1ab87a8839ab-------------------------------- Col Jung
·发布于 Towards Data Science ·阅读时间:21 分钟·2024 年 7 月 23 日
–
图片:Headway (Unsplash)
在 1980 年代,华尔街发现物理学家擅长解决复杂的金融问题,这些问题为他们的公司带来了大量财富。成为一名“量化分析师”意味着加入当时最热门的职业。
二十年后,在 2000 年代末,随着全球即将迎来大数据革命,出现了类似的趋势,企业开始寻找一种新型专业人才,能够从海量数据中筛选出有价值的洞察力以带来丰厚的回报。
这一新兴领域被称为数据科学。
2018 年,在完成前沿癌症治疗建模的博士学位后,我从学术界转向工业界,并开始为澳大利亚最大的一家银行工作。(更多内容请查看我新的分析 YouTube 频道)
我和来自全国各大顶尖大学的七位其他 STEM 博士候选人一起加入了该项目,我们每个人专攻不同领域,如糖尿病研究、机器学习、神经科学和火箭工程等。
尽管我们分散在公司的各个角落,但最终我们都进入了银行的大数据部门——这成了我们至今还会开玩笑的事…
进化国际象棋难题
进化人工智能的探索
https://medium.com/@bobby.elmes?source=post_page---byline--e23e6a58963a--------------------------------https://towardsdatascience.com/?source=post_page---byline--e23e6a58963a-------------------------------- Robert Elmes
·发表于数据科学探索 ·阅读时长:7 分钟·2024 年 3 月 23 日
–
一个国际象棋难题,采用进化理论生成。白方两步将死…
进化算法(EAs)是人工智能的一个子集,通过模仿生物进化的方法来解决问题。从优化神经网络到资源调度,它们在现实世界中有着广泛的应用。它们的魅力在于解决问题的方式发生了转变,重点不再是描述达到目标的步骤,而是描述目标的样貌。
在本文中,我将探讨如何利用这一出色的人工智能生成国际象棋难题、它带来的好处以及我们需要考虑的缺点。
国际象棋难题是一个合法的棋盘位置,其中一个独特的走法组合会导致胜利,通常以将死结束。它们通常通过分析人类玩家之间竞争性游戏的数据库来发现。
通过仅使用代码、随机性和一点生物学知识生成自己的难题,可以创建一个有趣且多样的难题数据库。让我们探索一下如何实现。
进化算法通常通过随机生成大量结果的种群,然后使用启发式方法选择“最适合”的结果,最后将这些“最适合”的结果用于生成后续的随机种群。它们的灵感来自达尔文的自然选择理论,在一个种群中,那些更可能生存的动物也更可能将其特征传递给下一代。经过多代的演化,有时甚至是成千上万代,种群会收敛到一个最优解。那么,我们如何将这一原理应用于国际象棋呢?
种群生成
在国际象棋中,我们可以通过模拟比赛来创建一个随机合法位置的群体,其中程序轮流进行随机的黑白双方移动若干次。通过重复这一过程成千上万次,可以分析大量的随机位置的适应性。
以下是我的 Board 类中的一个函数,它返回一个棋步列表。
public List<(int[] from, int[] to)> GetAllPotentialMoves(Colour currentColour)
{
var activePieces = ActivePieces.Find(p => p.colour == currentColour);
var allLegalMoves = new List<(int[] from, int[] to)>();
foreach (var piece in activePieces.pieces)
{
var moves = piece.GetLegalMoves(this);
allLegalMoves.AddRange(moves);
}
return allLegalMoves;
}
适者生存
一旦生成了一个位置群体,真正棘手的部分开始了。任何进化算法的关键在于如何评估你的启发式。在我的案例中,只有那些有单一解法并导致将军的位置才会被考虑为谜题。在筛选这些结果后,启发式是衡量选择正确棋步以赢得比赛的难度。但计算机程序如何评估一个人类解读国际象棋位置的难度呢?
通过一种偏向棋盘上骑士的启发式生成的谜题。2 步将军
一种方法是考察谜题的结构。国王安全吗?是否有那些不解决谜题但看起来很不错的棋步?我们是否牺牲了任何棋子?我们移动的是什么棋子?通过评估多个因素,我们可以创建一个难度衡量标准。这个方法的问题是,很难根据这么多因素来决定如何创建最终得分。僵化的规则也完全忽视了人类感知中的偏差。可能甚至是微小的棋盘变化,使得某些人更难选出正确的棋步。
那么,如何更好地了解人类的表现呢?通过利用充满真实比赛的大型数据库,机器学习模型已经训练成能够像某一等级的玩家一样下棋。通过这些模型,我们可以更好地理解不同能力的玩家可能如何尝试解谜。一个经过 1200 等级训练的 AI 能解开这个谜题吗?1600、1900 呢?这种方法的好处在于它能更深入地探究真实玩家的思维。然而,机器学习模型也不是没有缺点。这些 AI 并不像真实玩家那样下棋,它们更像是玩家的近似模型。它们还在真实、常规的比赛中训练,这意味着它们在评估随机的国际象棋位置时可能不可靠。
通过将机器学习模型与复杂且详细的基于规则的评估相结合,我创造了一种“两全其美”的场景。这是一种启发式方法,既能理解谜题的构成,又能考虑人类可能如何解决它。
下一代
一旦找到一组最佳谜题,下一步就是创建新一代谜题。这可以通过许多受进化启发的技术来实现。我选择使用交叉和变异。
交叉涉及随机合并两个结果的特征,希望能够得到两者的最佳特征。我们可以通过回溯到某个共享的起始点,然后选择用来到达每个结果的合法棋步,来交叉相似的国际象棋位置。也许移动皇后使得一个谜题具有了非常好的特性,而移动骑士则让另一个谜题变得有趣。通过结合这两种特性,我们可以创建出更具吸引力的问题。
类似地,我们可以通过回溯然后向前走若干步来改变谜题。根据你回溯和前进的步数,谜题可能会发生细微或巨大的变化。突变过多可能会导致算法永远无法改进,而突变过少则可能使你的最佳结果过快地收敛到一个单一的值。
那么……问题是什么呢?
进化算法最常见的问题是收敛太快。最初,我生成的谜题在仅仅经过几代后就停止了改进。在现实世界中,山脉、沙漠和海洋等物理边界阻止了种群之间的基因交换,保持了遗传多样性。没有足够的遗传多样性,种群的进化就会受到限制。通过将较小的国际象棋谜题种群并行运行一段时间,我为它们提供了足够的“呼吸空间”,以保持一些多样性并避免过早收敛。
进化算法也可能非常缓慢。国际象棋当然也不例外。在数百万个棋盘位置上进行启发式评估需要大量的处理能力。通常,你运行国际象棋引擎的时间越长,它预测下一步最佳棋着的准确性就越高。通过找到分析每个位置所需时间的最佳平衡点,挑选出最有前景的棋局,并更详细地分析它们,我可以在合理的范围内优化时间。决定何时停止生成也至关重要。如果一个样本在经过几代之后停止改进,那么也许最好从新的随机种群开始,因为它可能无法进一步改进。在经过无数次优化后,我的家用电脑能够每天使用进化算法生成超过 1000 个具有挑战性的谜题。
最后,诊断错误可能是极其困难的。对于许多程序,你可以根据特定的输入预期得到某些输出。而在进化算法中情况则不同。我花了很多时间琢磨为什么我的种群会过快收敛。是位置生成的问题吗?是进化方法的问题,可能是启发式方法的问题?当程序的预期输出无法明确界定时,很容易忽略某些地方没有按预期工作。
结果
然而,尽管存在一些问题,这种 AI 技术的强大力量和潜力依然光芒四射,人人可见。仅凭我那台旧电脑,我在 3 个月内就生成了近 50,000 个国际象棋难题,涵盖了大量奇特而美妙的棋局。
算法的随机性意味着它能够创造出极其丰富多彩、千变万化的难题。在国际象棋中,我们很少见到一些有趣的战术问题,比如皇后牺牲、骑士升变和“过路兵”吃子,而通过进化算法这些问题很容易生成,而用真实棋局的数据库则难以找到。然而,这些难题的荒诞性质使它们对实际场景的适用性较差。虽然非常有趣,但也可以说,基于真实棋局的难题更适合学习国际象棋中的常见模式。
除了极具生产力外,这个算法还异常灵活。沙特兰奇、倾斜棋盘等,扩展进化算法以适应任何衍生版的国际象棋都非常简单。正是这种可扩展性,使得进化技术在这里表现得尤为出色。你无法通过游戏数据库来实现这一点,因为这些数据库根本不存在!
由算法生成的沙特兰奇(Shatranj)难题。你能在 2 步内将白方国王将死吗?
结语
尽管对于许多人来说,AI 的这一领域可能已经被遗忘,但我展示了如何利用进化算法为现实世界的问题创造出新颖的解决方案。这个技术仍有许多未开发的潜力。随着生成式 AI 的崛起,我不禁想知道,未来人们还会为进化算法发现哪些有趣的应用…
你可以在我的网站上亲自体验这些难题,chesspuzzler.com。
除非另有说明,所有图片均由作者提供。
通过 ELLA 和 VOYAGER 研究长期机器学习:为何 LLML 是 AI 领域下一次革命性突破的第二部分
通过高效终身学习算法(ELLA)和 VOYAGER 理解终身学习的力量
https://medium.com/@almond.maj?source=post_page---byline--bea36a01f529--------------------------------https://towardsdatascience.com/?source=post_page---byline--bea36a01f529-------------------------------- Anand Majmudar
·发表于Towards Data Science ·阅读时间:9 分钟·2024 年 1 月 17 日
–
AI 机器人驾驶太空飞船,由 GPT-4 生成
如果你还没有阅读,第一部分:LLML 的起源我鼓励你先读一读,在那里我们探讨了 LLML 在强化学习中的应用。现在,既然我们已经了解了 LLML 的起源,我们可以将其应用于其他领域,特别是监督学习中的多任务学习,来展示 LLML 的一些真正的力量。
监督型 LLML:高效终身学习算法
高效终身学习算法旨在训练一个能够同时在多个任务上表现出色的模型。ELLA 操作于多任务监督学习环境中,具有多个任务 T_1…T_n,每个任务有对应的特征 X_1…X_n 和 y_1…y_n(这些任务的维度可能不同)。我们的目标是学习函数 f_1,…, f_n,其中 f_1: X_1 -> y_1。基本上,每个任务都有一个函数,该函数将任务对应的特征作为输入,并输出其 y 值。
从高层次来看,ELLA 为所有任务保持一个共享的“知识”向量基础,当遇到新任务时,ELLA 利用来自新任务数据的知识来优化基础。此外,在学习这个新任务时,更多的信息被加入到这个基础中,从而提高所有未来任务的学习效果!
Ruvolo 和 Eaton 在三个场景中使用了 ELLA:地雷探测、面部表情识别和考试成绩预测!作为一个小小的预告,让你对 ELLA 的强大功能产生兴趣,它在这些数据集上实现了高达 1,000 倍的时间效率提升,几乎没有牺牲性能能力!
现在,让我们深入探讨 ELLA 的技术细节!当尝试推导这样一个算法时,第一个可能出现的问题是
我们到底如何找到在知识库中与每个任务相关的信息?
ELLA 通过修改每个 t 的 f 函数来实现这一点。它不再是一个函数 f(x) = y,而是 f(x, θ_t) = y,其中θ_t 是特定于任务 t 的,可以通过知识库向量的线性组合表示。通过这种系统,我们现在将所有任务映射到相同的基准维度,并可以使用简单的线性距离来衡量相似性!
现在,我们如何为每个任务推导θ_t?
这个问题是 ELLA 算法的核心洞察力,所以让我们详细看看它。我们将知识基向量表示为矩阵 L。给定权重向量 s_t,我们将每个θ_t 表示为 Ls_t,即基向量的线性组合。
我们的目标是最小化每个任务的损失,同时最大化任务之间共享的信息。我们通过最小化目标函数 e_T 来实现这一点:
其中ℓ是我们选择的损失函数。
本质上,第一个子句考虑了我们特定任务的损失,第二个子句试图最小化我们的权重向量并使其稀疏,最后一个子句试图最小化我们的基向量。
这个方程有两个低效之处(看看你是否能找出是什么)!第一个低效之处是我们的方程依赖于所有之前的训练数据(具体来说是内层求和),我们可以想象这非常繁琐。我们通过使用泰勒级数近似来缓解这个低效问题。第二个低效之处是我们需要重新计算每一个 s_t 来评估 L 的一个实例。我们通过移除 z 上的最小化,并改为在 t 最后一次交互时计算 s,从而消除了这个低效问题。我鼓励你阅读原始论文以获得更详细的解释!
现在我们有了目标函数,我们希望创建一种方法来优化它!
在训练过程中,我们将每次迭代视为一个单元,其中我们从单个任务接收一批训练数据,然后计算 s_t,最后更新 L。在算法的开始,我们将 T(任务计数器)、A、b 和 L 初始化为零。现在,对于每批数据,我们根据数据是来自已知任务还是未知任务来进行分类处理。
如果我们遇到来自新任务的数据,我们将 T 加 1,并为这个新任务初始化 X_t 和 y_t,将它们设置为我们当前的 X 和 y 批次。
如果我们遇到已经见过的数据,我们的过程变得更加复杂。我们再次将新的 X 和 y 添加到我们当前的 X_t 和 y_t 的记忆中(通过遍历所有数据,我们将为每个任务拥有一完整的 X 和 y 集合!)。我们还会递增地更新 A 和 b 的值(我稍后会解释这一点,现在先记住这一点!)。
现在我们检查是否想要结束训练循环。我们将(θ_t, D_t)设置为我们常规学习器对批量数据的输出。
然后我们检查是否结束循环(如果我们已经看过所有训练数据)。如果没有结束,我们继续计算 s 并更新 L。
为了计算 s,我们首先仅使用批量数据来计算最优模型θ_t,这将依赖于我们的具体任务和损失函数。
然后我们计算 D_t,并且随机或选择一个θ_t 来初始化 L 的所有零列(如果某个基础向量未被使用,则会发生这种情况)。在线性回归中,
而在逻辑回归中
然后,我们通过求解 L1 正则化回归问题来使用 L 计算 s_t:
在我们更新 L 的最后一步时,我们取
,找到梯度为 0 的地方,然后解 L。通过这样做,我们增加了 L 的稀疏性!然后我们输出 L 的更新列向量化形式为
为了避免对所有任务求和以计算 A 和 b,我们在每个任务到来时逐步构建它们。
一旦我们遍历完所有批次数据,就意味着我们已经正确学习了所有任务并完成了!
ELLA 的强大之处在于它的许多效率优化,其中最主要的是它使用θ函数来准确理解哪些基础知识是有用的!如果你对 ELLA 有更深入的了解兴趣,我强烈建议你查看原始论文中的伪代码和解释。
使用 ELLA 作为基础,我们可以设想创建一个具有普遍性的人工智能,它可以学习任何呈现给它的任务。我们再次具有这样一个特性:随着我们的知识基础的增长,它所包含的‘相关信息’也越来越多,这将进一步加速学习新任务的速度!看起来,ELLA 有可能成为未来超级智能人工学习者的核心!
旅行者
当我们将人工智能的新突破 LLM 与终身学习结合时,会发生什么呢?我们得到的是一个能够战胜 Minecraft 的系统(这是实际论文的设置)!
王冠志、谢宇琪等人看到了 GPT-4 的强大能力所带来的新机遇,并决定将其与迄今为止学到的终身学习理念结合,创造出 Voyager。
在学习游戏中,典型的算法会被赋予预定义的最终目标和检查点,存在的唯一目的就是追求这些目标。然而,在像 Minecraft 这样的开放世界游戏中,有许多可能的目标可以追求,并且有无限的空间可以探索。如果我们的目标是近似于人类般的自我激励,并结合传统 Minecraft 基准测试中的时间效率提升,比如获取钻石?具体来说,假设我们希望我们的代理能够决定可行的、有趣的任务,学习并记住技能,并以“自我激励”的方式继续探索和寻求新目标。
为了实现这些目标,王、谢等人创建了 Voyager,他们称之为首个基于 LLM 的具身终身学习代理!
Voyager 是如何工作的?
在大规模应用中,Voyager 使用 GPT-4 作为其主要的“智能功能”,该模型本身可以分为三个部分:
-
自动化课程: 这决定了要追求的目标,可以看作是模型的“动机”。通过 GPT-4 实现,他们指示模型优化难度较大但可行的目标,并“尽可能多地发现不同的事物”(可以阅读原文了解他们的具体提示)。如果我们在四轮迭代提示机制循环中,代理的环境没有发生变化,我们就选择一个新的任务!
-
技能库: 一个包含可执行动作的集合,如 craftStoneSword() 或 getWool(),随着学习者的探索而逐渐增加难度。这个技能库以向量数据库的形式表示,其中键是 GPT-3.5 生成的技能描述的嵌入向量,以及以代码形式表示的可执行技能。GPT-4 生成了技能的代码,优化了其通用性,并通过在代理环境中使用技能的反馈进行了优化!
-
迭代提示机制: 这是与 Minecraft 环境交互的元素。它首先执行 Minecraft 的接口,获取当前环境的信息,例如它的背包中的物品和它能观察到的周围生物。然后它提示 GPT-4 并执行输出中指定的动作,同时提供有关指定动作是否不可能的反馈。这个过程会重复,直到当前任务(由自动化课程决定)完成。完成后,我们将学到的技能添加到技能库中。例如,如果我们的任务是制作一把石剑,我们现在就将技能 craftStoneSword() 加入到技能库中。最后,我们向自动化课程请求一个新的目标。
那么,终身学习在这一切中扮演什么角色?
当我们遇到新任务时,我们查询我们的技能数据库,以找到与当前任务最相关的前 5 个技能(例如,任务 getDiamonds() 的相关技能可能是 craftIronPickaxe() 和 findCave())。
因此,我们已经利用先前的任务来更高效地学习新的任务:这就是终身学习的本质!通过这种方法,Voyager 不断探索和成长,学习新的技能,拓展它的可能性边界,增加目标的雄心壮志,从而持续提高新学习技能的能力!
与 AutoGPT、ReAct 和 Reflexion 等其他模型相比,Voyager 发现的新项目是这些模型的 3.3 倍,导航距离是它们的 2.3 倍,每次提示迭代解锁木质级别的速度是它们的 15.3 倍,而且是唯一一个解锁了技术树钻石级别的模型!此外,在训练之后,当被放入一个完全陌生、没有任何物品的环境时,Voyager 始终能够解决以前未见过的任务,而其他模型在 50 次提示内都无法解决任何任务。
为了展示终身学习的重要性,没有技能库的情况下,模型在学习新任务时的进展在 125 次迭代后停滞不前,而有了技能库,它的进展以相同的高速度持续上升!
现在想象这个代理应用于现实世界!想象一个拥有无限时间和无限动力的学习者,随着拥有的先验知识越来越多,它能够不断拓展自己的可能性边界,学习得越来越快!我希望到现在为止,我已经充分展示了终身机器学习的力量以及它推动 AI 下一次变革的能力!
如果你对 LLML 更感兴趣,我鼓励你阅读 Zhiyuan Chen 和 Bing Liu 的书籍,它阐述了 LLML 可能走向的未来路径!
感谢你一直看到这里!如果你感兴趣,可以访问我的网站 anandmaj.com,那里有我的其他写作、项目和艺术作品,也可以在 Twitter 上关注我@almondgodd。
原始论文及其他资料:
Eaton 和 Ruvolo:高效的终身学习算法
Wang、Xie 等:Voyager
Chen 和 Liu,《终身机器学习》(启发我写这篇文章!):www.cs.uic.edu/~liub/lifelong-machine-learning-draft.pdf
使用课程的无监督终身学习:par.nsf.gov/servlets/purl/10310051
深度终身学习:towardsdatascience.com/deep-lifelong-learning-drawing-inspiration-from-the-human-brain-c4518a2f4fb9
神经启发的 AI:www.cell.com/neuron/pdf/S0896-6273(17)30509-3.pdf
体现终身学习的应用:lis.csail.mit.edu/embodied-lifelong-learning-for-decision-making/
用于情感分类的终身学习:arxiv.org/abs/1801.02808
终身机器人学习: www.sciencedirect.com/science/article/abs/pii/092188909500004Y
知识基础理念: arxiv.org/ftp/arxiv/papers/1206/1206.6417.pdf
Q 学习: link.springer.com/article/10.1007/BF00992698
AGI LLLM 大型语言模型: towardsdatascience.com/towards-agi-llms-and-foundational-models-roles-in-the-lifelong-learning-revolution-f8e56c17fa66
DEPS: arxiv.org/pdf/2302.01560.pdf
Voyager: arxiv.org/pdf/2305.16291.pdf
元强化学习调查: arxiv.org/abs/2301.08028
自然语言处理(NLP)与其他学科领域的影响关系研究
关于 NLP 论文随时间推移在引用其他学科的比例逐渐下降的警示性说明
https://medium.com/@jpwahle?source=post_page---byline--26e66f79dabb--------------------------------https://towardsdatascience.com/?source=post_page---byline--26e66f79dabb-------------------------------- Jan Philip Wahle
·发表于Towards Data Science ·13 分钟阅读·2024 年 1 月 9 日
–
“尘世乐园”是荷兰画家海罗尼穆斯·博斯(Hieronymus Bosch)创作的三联画(约 1490–1510)。这幅画以其复杂而富有想象力的图像而闻名,具有多种解释方式,包括作为不同思想和实体交织的表现。
自然语言处理(NLP)有望在全球范围内产生深远的影响。然而,显著的进展也伴随着巨大的风险。要应对这些风险,需要广泛地与多个学科领域进行互动。请与我们一起踏上这一经验性和视觉化的探索之旅(包括数据和可视化内容),在此过程中我们将探讨以下问题:
-
哪些学科领域在影响自然语言处理?影响的程度如何?
-
自然语言处理正在影响哪些学科领域?影响的程度如何?
-
这些变化随时间如何演变?
本文展示了我们在 EMNLP 2023 会议论文中的一些关键结果:
Jan Philip Wahle, Terry Ruas, Mohamed Abdalla, Bela Gipp, Saif M. Mohammad. 我们引用的正是我们所影响的领域:自然语言处理与其他学科之间的影响桥梁 (2023) 《2023 年自然语言处理经验方法会议(EMNLP)》论文集,新加坡.* BibTeX
本文由 Jan Philip Wahle 和 Saif M. Mohammad 撰写
原始创意:Saif M. Mohammad
动机
科学的一个迷人之处在于不同学科如何相互作用并相互影响。许多重大突破都是来自多个学科的协同作用。例如,量子力学的概念是一种融合了普朗克关于量子化能级的想法、爱因斯坦的光电效应和玻尔的原子模型的理论。
一个学科领域的思想和成果对世界的帮助程度是其影响力的衡量标准。
发展对一个领域影响力的更好理解具有多重好处,比如理解促进创新的因素和抑制创新的因素,理解一个领域在哪些方面取得了成功,哪些方面仍然难以捉摸,或者是谁是受益的主要利益相关者,谁又被抛在了后头。
领域间影响的机制复杂,但科学影响力的一个显著标志是引用。一个源领域引用目标领域的程度是目标领域对源领域影响力的粗略指标。然而,我们需要注意,并非所有引用都是平等的,且可能受到各种偏见的影响。尽管如此,从总体上仍然可以得出有意义的推论;例如,如果领域 x 对目标领域 y 的引用比例明显增加,相较于其他领域对目标领域的引用比例,那么很可能 x 对 y 的影响力增加了。
为什么是 NLP?
虽然研究影响力对于任何学科都是有用的,但我们专注于自然语言处理(NLP)研究,原因非常关键。
NLP 正处于一个转折点。近期大规模语言模型的发展吸引了科学界、工业界和公众的广泛关注。
因此,尽管存在重大风险,NLP 准备发挥巨大的影响力。此外,语言是社会性的,其应用具有复杂的社会影响。因此,负责任的研究与开发需要广泛阅读相关文献(可以说,这对于 NLP 比其他领域更为重要)。
通过追踪数十万次引用,我们系统性且定量地分析了各学科领域对 NLP 的影响以及 NLP 对它们的影响的广泛趋势。
我们使用 Semantic Scholar’s 的 学科属性 来将论文分类为 23 个领域,例如数学、医学或计算机科学。一篇论文可以属于一个或多个领域。例如,一篇以计算机算法为基础的医学应用论文可能同时属于医学和计算机科学领域。NLP 本身是计算机科学、机器学习和语言学的跨学科子领域。我们将一篇论文归类为 NLP,若它出现在 ACL Anthology 中,这是公认的最大 NLP 文献库(尽管它并不是所有 NLP 论文的完整集合)。
数据概览
-
来自各领域的 2.09 亿篇论文和 25 亿次引用(Semantic Scholar):对于每个引用,都会记录引用论文和被引用论文的学科领域。
-
Semantic Scholar的学科领域属性将论文分类为 23 个领域,例如数学、医学或计算机科学。
-
1965 到 2022 年间的 77K 篇 NLP 论文(ACL 文集)
问题 1:谁影响 NLP?谁受 NLP 的影响?
为了理解这一点,我们特别关注两种类型的引用:
-
外部引用:哪些领域被 NLP 论文引用?
-
内部引用:哪些领域引用 NLP 论文?
图 1 展示了 NLP 与 CS/非 CS 论文之间引用流的可视化。
图 1:NLP 引用计算机科学(CS)领域的论文与其他领域的论文相比,比例是多少?这里,我们展示了来自 CS 和非 CS 领域对 NLP 的引用(右侧),以及来自 NLP 对 CS 和非 CS 领域的引用(左侧)。
在所有引用中,79.4%来自计算机科学(CS)论文。类似地,超过五分之四的引用(81.8%)来自 NLP 论文,指向 CS 论文。但这一情况随时间变化吗?NLP 是否一直在引用这么多 CS?
图 2(a)展示了 NLP 对 CS 和非 CS 论文的引用百分比随时间的变化;图 2(b)展示了 CS 和非 CS 论文对 NLP 的引用百分比随时间的变化。
https://github.com/OpenDocCN/towardsdatascience-blog-zh-2024/raw/master/docs/img/2ed6733c6022cb28ecb3531cd06a90f6.pnghttps://github.com/OpenDocCN/towardsdatascience-blog-zh-2024/raw/master/docs/img/96c81a2596b62efd6b947e9d315bfea9.png
图 2:CS 论文的引用百分比如何随时间变化?这里,我们展示了来自 NLP 对 CS 和非 CS 的引用百分比(a),以及来自 CS 和非 CS 对 NLP 的引用百分比(b),这些数据基于 NLP 的所有引用,并采用三年的移动平均。
观察到,1990 年时,只有大约 54%的外部引用是来自 CS,但这个比例稳步上升,到 2020 年已达 83%。这是一个显著的变化,显示了 NLP 在这些年里是如何变得以 CS 为中心的。关于内部引用的图表(图 2(b))显示,NLP 从 CS 领域接收到的大多数引用,也从大约 64%稳步增加到 2020 年的 81%。
图 3 展示了当我们只考虑非 CS 领域时的 Sankey 图:
图 3:NLP 引用最多且被引用最多的非 CS 领域是什么?这里,我们展示了来自非 CS 领域对 NLP 的引用(右侧),以及来自 NLP 对非 CS 领域的引用(左侧)。
我们看到,语言学、数学、心理学和社会学是 NLP 引用最多的非 CS 领域。它们也是引用 NLP 最多的非 CS 领域,尽管数学和心理学的顺序有所交换。
语言学在 NLP 对非 CS 论文的所有引用中占 42.4%,而在非 CS 论文对 NLP 的所有引用中,45.1%来自语言学。NLP 引用数学的频率高于心理学,但 NLP 的引用来源中,心理学的比例大于数学。
接着我们想知道这些引用百分比是如何随着时间变化的。我们猜测 NLP 对语言学的引用增加了,但到底增加了多少呢?过去其他学科的引用分布情况又如何?
图 4 展示了 NLP 到非 CS 论文的引用份额(图 4(a))和非 CS 论文到 NLP 的引用份额(图 4(b))随着时间的推移的变化。
https://github.com/OpenDocCN/towardsdatascience-blog-zh-2024/raw/master/docs/img/a3ffdae97897817aa7bd4ca44c536d88.pnghttps://github.com/OpenDocCN/towardsdatascience-blog-zh-2024/raw/master/docs/img/70d6031cba6d328134927ca91257d27f.png
图 4:从所有非 CS 领域,NLP 引用最多或最常被引用的领域是哪些?这种情况随着时间的推移如何变化?在这里,我们展示了(a)NLP 引用到非 CS 领域的引用百分比,以及(b)非 CS 领域引用到 NLP 的引用百分比,分别占所有来自 NLP 的非 CS 引用和所有指向 NLP 的非 CS 引用的比例。
注意到,语言学在 2000 年至 2020 年间对于 NLP 的相关性经历了明显的(相对)下降(图 4)。NLP 对语言学的外部引用从 60.3%降至 26.9%(a),对 NLP 的引文从 62.7%降至 39.6%(b)。这种相对下降似乎主要是由于 NLP 对数学的引用比例增加,以及心理学和数学对 NLP 的引用比例上升。
总结: 随着时间的推移,计算机科学(CS)领域的内外引用数量都在增加。这些结果还显示出语言学影响力的逐渐减弱,以及数学(可能是由于以数学为主的深度学习和大型语言模型的日益主导地位)和心理学(可能是由于 NLP 应用中越来越多使用心理学的行为、情感和幸福感模型)的显著上升。数学的影响力大幅增加,似乎主要取代了原本由语言学占据的影响力。
Q2. 哪些领域的 NLP 引用频率高于平均水平?
正如我们在本博客之前所知,15.3%的 NLP 非计算机科学(非 CS)领域引用指向了数学,但这与其他领域引用数学的情况相比如何呢?我们引用数学的频率是否高于其他领域的平均水平?
为了回答这个问题,我们计算了 NLP 领域对某一学科f的外部引用百分比与各个学科对f的外部引用百分比的宏观平均值之间的差异。我们将这个指标称为外部相对引文显著性(ORCP)。如果 NLP 对f的 ORCP 大于 0,则表示 NLP 对f的引用百分比高于该学科的平均值。ORCP 的计算方式如下:
其中,F是所有学科的集合,N是学科的总数,C是某一学科对另一个学科的引用次数。
图 5 展示了 NLP 在各个学科的 ORCP 值的图示。
图 5:NLP 相比其他学科平均引用某一领域的比例有多突出?NLP 的外向相对引用显著度(ORCP)体现了这一点。这里,我们展示了 23 个学科领域的 ORCP 分数。高分(>0%)表示 NLP 比平均水平更多地引用该领域,而低分(<0%)则表示 NLP 比平均水平少引用该领域。
NLP 引用计算机科学的频率明显高于平均水平(ORCP = 73.9%)。这一分数意味着 NLP 对数学的引用频率比其他学科对数学的引用频率高出 73.9 个百分点。
量化自然语言处理(NLP)比其他领域引用计算机科学(CS)的频率,帮助我们了解 NLP 在多大程度上借鉴了计算机科学的思想。尽管语言学是语言理论的主要来源,NLP 论文引用语言学的频率仅比平均水平高出 6.3 个百分点(显著低于计算机科学)。有趣的是,尽管心理学是 NLP 引用的第三大非计算机科学领域(见图 3),它的 ORCP 为-5.0,表明 NLP 引用心理学的频率明显低于其他领域对心理学的引用频率。
Q3.NLP 的内向性有多强?
在此背景下,我们所说的“内向性”是指一个学科在多大程度上依赖于自身文献,而非借鉴其他学科的思想。估算内向性的一种方式是计算论文引用同一学科的比例与引用其他学科论文的比例。对于 NLP 及 23 个学科领域,我们测量了这一内部引用比例,即一个学科引用自身的论文占所有引用的比例。
图 6 展示了跨时间的内部引用比例。
图 6:一篇论文引用自己领域的论文与引用其他领域的论文的比例是多少?尽管各个领域引用自己领域的论文与引用其他领域的论文的比例基本保持稳定,但 NLP 在引用自己领域的论文方面有所增长。
1980 年,只有 5%的 NLP 论文(即每 20 篇引用中有一篇)引用了其他 NLP 论文(见图 6)。从那时起,这一比例显著增加,2000 年达到了 20%,2020 年达到了 40%,每十年增长 10 个百分点。到 2022 年,NLP 的内部引用比例达到了所有领域的平均水平。
与语言学等其他领域相比,NLP 在跨领域引用的增长尤为强劲,尤其是在起步阶段较低的情况下。这可能是因为 NLP 作为一个学科,成立较晚,且在 1980 年代和 1990 年代时规模较小。语言学的内部引用比例从 1980 年的 21%缓慢增长至 2010 年的 33%,但此后开始下降。有趣的是,数学和心理学的内部引用比例在一段时间内保持稳定,但最近经历了快速增长。
鉴于一篇论文也可以属于一个或多个学科,我们还可以通过测量一篇论文所属的学科数量,并与其他学科进行比较,来估算论文的跨学科程度。
图 7 显示了每篇论文平均涉及的学科数量随时间的变化。
图 7:NLP 论文的跨学科程度如何?一篇论文可以属于一个或多个学科。例如,关于语言模型在医学领域应用的论文同时属于医学和 NLP 学科。尽管过去四十年中,涉及多个学科的论文数量有所增加,但 NLP 论文却越来越趋向于只涉及单一学科。
1980 年,NLP 论文的平均学科数与其他学科相当。然而,从 1980 年到 2020 年,NLP 和其他学科的趋势发生了明显分化。尽管其他学科的论文逐渐趋向于更多学科交叉,但 NLP 论文则越来越少关注多个学科。
Q4:是否存在一个底线指标,能够捕捉外部引用多样性的程度?NLP 论文的外部引用多样性(通过此指标衡量)随时间如何变化?同样的问题也适用于流入引用?
为了用一个单一指标捕捉 NLP 引用不同学科的多样性,我们引入了引用学科多样性指数(CFDI)。
CFDI 衡量一篇论文在引用不同学科时的多样性。简单来说,较高的CFDI表示一篇论文引用了来自多个学科的论文。这个指标提供了学术跨学科影响力扩展的洞察。CFDI的定义基于基尼-辛普森指数,公式如下:
这里,xf表示学科f中的论文数量,N表示总引用量。接近 1 的得分表明,来自目标学科(在本例中为 NLP)对 23 个学科的引用数量大致均匀。得分为 0 则表示所有引用都集中在一个学科。流入的 CFDI 以类似的方式计算,只不过考虑的是其他学科对目标学科(NLP)的引用。
图 8 展示了外部 CFDI(a)和流入 CFDI(b)随时间的变化。
https://github.com/OpenDocCN/towardsdatascience-blog-zh-2024/raw/master/docs/img/9867ae74e22489bbdbb0be5336bb6dfd.pnghttps://github.com/OpenDocCN/towardsdatascience-blog-zh-2024/raw/master/docs/img/73c2ca9e660d1e0aeb5added8b468b77.png
图 8:我们在引用不同学科时的多样性如何?引用学科多样性指数(CFDI)反映了这一点。接近 1 的得分表示我们均等地引用了多个学科,而接近 0 的低得分则表明我们集中引用了特定学科。这里,我们展示了 NLP 的 CFDI 以及 NLP 引用的三大学科的 CFDI,分别针对(a)外部引用和(b)流入引用。“Avg.”表示 23 个学科的宏观平均 CFDI。
尽管平均的外部 CFDI 随着时间的推移缓慢增加,但在过去四十年中,NLP 的 CFDI 却经历了快速下降。从 1980 年到 2020 年,平均外部 CFDI 增长了 0.08,而 NLP 的外部 CFDI 下降了大约 30%(0.18)。语言学和心理学的 CFDI 变化趋势与平均值类似,而数学在四十年间外部 CFDI 增加了 0.16。
与外部 CFDI 类似,NLP 论文的内部 CFDI 也随着时间的推移显著下降,表明其引用的来源主要来自一个(或少数几个)领域,而非多个领域。图 5(b)展示了这一趋势。虽然其他领域和平均值的内部 CFDI 在 1980 到 2010 年间保持稳定,NLP 的内部 CFDI 则从 0.59 降至 0.42。在 2010 年代,所有领域的 CFDI 都出现下降,但 NLP 的下降尤其明显(从 0.42 降至 0.31)。
主要结论
-
NLP 主要吸取来自计算机科学(CS)的思想(>80%)。在计算机科学之后,NLP 最受语言学、数学、心理学和社会学的影响。
-
随着时间的推移,NLP 对非计算机科学领域的引用以及引用不同领域的多样性有所下降,而其他领域则保持稳定。
-
NLP 领域的论文正变得越来越不具跨学科性,越来越多地局限于单一领域,并引用自己领域的文献。
讨论。关于科学影响的一个关键点是,作为研究人员,我们不仅仅是影响的被动对象。尽管某些影响力很难忽视(比如来自同行的广泛支持和同行评审过程),但其他影响则是研究人员主动与相关文献互动的直接结果(可能来自不同领域)。
我们(可以)选择与哪些文献互动,从而受益。
同样,尽管我们偶尔可能会影响到其他领域,但我们(可以)选择与其他领域互动,让他们更加关注我们的工作。
这种互动可以通过在会议上的跨领域交流、为其他领域目标受众撰写的博客文章等方式进行。
在过去五年里,NLP 技术被广泛部署,直接和间接地影响了数十亿人。也有大量实例表明,显然在开发这些系统时并未充分考虑,导致了各种不良后果。已有充分证明,开发更好系统的一个关键因素是广泛地涉猎各类文献和思想,尤其是吸收计算机科学之外的思想(如心理学、社会科学、语言学等)。
在这种背景下,NLP 对更广泛研究文献的缺乏互动,尤其是在边缘化群体仍面临技术带来重大风险的情况下,揭示了 NLP 领域的严重问题。
我们不仅没有像其他领域那样更多地与外部文献互动,甚至与其他领域的互动还明显较少。这一趋势只会变得更糟。
好消息是,这种情况可以改变。认为引用模式是“注定发生”的,或者认为个别研究者和团队在引用哪些文献时没有选择权,这种看法是一个神话。我们可以主动选择我们参与的文献。我们可以更加努力地将我们的工作与语言学、心理学、社会科学等领域的思想联系起来。通过语言和计算,我们可以更加关注对其他领域重要的问题。通过关注其他领域的工作,我们可以将他们的思想带到自然语言处理(NLP)中的新形式。
作为研究人员、导师、评审和委员会成员,我们应该问问自己,我们是如何:
影响跨领域思想交流的广泛性
并有意识地反对:
过度关注计算机科学的作品,而忽视来自其他领域的相关工作,如心理学、社会学和语言学。
结语
计算跨领域影响力的在线工具
为了促进我们对来自各个领域文献引用的反思,我们创建了一个在线公共工具,它可以计算个别论文甚至一组论文(例如,作者简介、会议论文集)的跨领域引用量。只需插入 Semantic Scholar 或 ACL Anthology 的 URL,该工具就会计算出诸如最受引用领域、引用领域多样性等各种指标。或者上传一个 PDF 草稿,工具会解析参考文献并将其链接到相应的领域。
你将能够回答以下问题:
-
哪些领域对我(作为作者)的影响最大?
-
我的提交草稿的领域多样性如何?
-
与本次会议相关的最重要的领域是什么?
通过回答这些问题,你可能会进一步反思:
-
“是否有其他领域的思想可以创新性地应用于我的研究?”
-
“我是否可以扩大我的文献搜索,涵盖来自其他领域的作品?”
若想快速了解该工具,请观看这段简短视频。
致谢
本项工作部分得到了德国学术交流服务(DAAD)9187215 号资助、下萨克森州科学与文化部资助,以及大众汽车基金会的支持。感谢 Roland Kuhn、Andreas Stephan、Annika Schulte-Hürrmann 和 Tara Small 的深思熟虑的讨论。
除非另有说明,所有图片均由作者提供。
相关的有趣作品
-
处理和其他学术领域(EMNLP, 2023)](https://arxiv.org/abs/2310.14870)
简·菲利普·瓦尔赫
德国哥廷根大学研究员
加拿大国家研究委员会访问研究员
推特: @jpwahle
网页: jpwahle.com/
赛义夫·M·穆罕默德
加拿大国家研究委员会高级研究科学家
推特: @saifmmohammad
Excel 电子表格对于大数据来说已经死了。公司需要更多的 Python 来代替。
意见
数据变得太复杂,Excel 已经跟不上了
https://arijoury.medium.com/?source=post_page---byline--71b8b3dbe19a--------------------------------https://towardsdatascience.com/?source=post_page---byline--71b8b3dbe19a-------------------------------- Ari Joury, PhD
·发布于 Towards Data Science ·11 分钟阅读·2024 年 11 月 18 日
–
Python 正在接管曾经由 Excel 主导的领域。图片由 Leonardo AI 生成
电子表格拖慢了我们的工作进程。尽管在企业界广泛依赖 Excel,但固守它就像是在一辆故障的赛车上进行一级方程式比赛。当然,它很熟悉,也很普及。而且公平地说,它确实适用于许多任务,从投资银行的简单数据提取到相当复杂的保险定价模型。
然而,当数据中包含成千上万条记录、通常是相互关联的表格和复杂的聚类时,使用 Excel 处理今天复杂的数据可能变得非常危险。试想一下:Excel 的行数限制臭名昭著,导致了 高调的灾难事件,例如英国 COVID-19 数据事故,成千上万的测试结果因为 Excel 的限制而未被记录。
或者考虑一下那些无数浪费的时间,反复检查手动输入的数据,最后仍然得出可能受人为错误影响的报告。事实是,当你处理的数据分析达到一定复杂度时,Excel 就成了累赘。
在一个即使是简单的消费类应用也能更快、更精准地处理复杂数据的时代,我们为什么仍然在处理这些…
用我们的最新数学和统计必读书单扩展你的数据科学工具箱
https://towardsdatascience.medium.com/?source=post_page---byline--3da19a5184c2--------------------------------https://towardsdatascience.com/?source=post_page---byline--3da19a5184c2-------------------------------- TDS 编辑
·发表于 Towards Data Science ·发送为 新闻通讯 ·4 分钟阅读·2024 年 4 月 25 日
–
受到启发,想写下你的第一篇 TDS 文章吗? 我们始终欢迎新作者的投稿。
数据科学家在日常工作中使用的数学基本原理可能已经存在了几个世纪,但这并不意味着我们应该像第一次学习时那样仅仅学一遍,然后将知识存放在某个尘封的心理阁楼里。实践方法、工具和应用案例不断发展,随之而来的是需要保持与时俱进。
本周,我们很高兴分享一系列强大的近期数学与统计必读书单,涵盖了各种问题与应用。从利用(非常)小的数据集到以易懂、生动的方式呈现线性回归,我们相信你一定能找到新的、实用的内容来探索。让我们一起深入了解吧!
-
N-of-1 试验与分析你自己的健身数据 N-of-1 研究的理念是,即使你使用的数据仅来自一个人的输入,你依然能够得出有意义的见解。这对于设计个性化的健康管理策略具有深远的潜力,或者在Merete Lutz的迷人项目中,建立酒精消费与睡眠质量之间的有意义联系。
-
**你的时间序列预测有多可靠?**做出长期预测很容易;做出准确的长期预测就没有那么简单了。Bradley Stephen Shaw最近分享了一份实用指南,帮助你通过有效使用交叉验证、可视化和统计假设检验来确定你预测的可靠性边界。
-
使用 LangChain 代理构建数学应用尽管大规模语言模型(LLMs)在过去几年中取得了重大进展,但数学仍然是它们的难点之一。在她最新的实践教程中,Tahreem Rasul分析了我们在尝试让这些模型执行数学和统计操作时所面临的挑战,并概述了使用 LangChain 代理、OpenAI 和 Chainlit 构建基于 LLM 的数学应用的解决方案。
图片来源:Chloe Frost-Smith在Unsplash上的作品
-
中心极限定理的证明看到一个抽象概念变得具体,并且在这个过程中变得更加易于理解和直观,始终是一件令人欣喜的事情。这正是Sachin Date在他最新的深入分析中所做的,他通过糖果的例子向我们展示了中心极限定理的内部原理,“这是统计科学中最深远且令人愉快的定理之一”。
-
用 8 种图表向外行解释线性回归即使你是一个专业的数据科学家或机器学习工程师,完全理解你的统计分析的意义,很多同事和其他利益相关者可能并不理解。这就是强大可视化效果能够产生重大影响的地方,Conor O’Sullivan通过八种不同的残差、权重、效应和 SHAP 图表有效地解释了线性回归模型。
本周有想扩展数学和统计学之外的领域吗?我们希望是这样!以下是我们最近在其他主题上的一些精选阅读:
-
如果你正在考虑通过参与开源项目来回馈社区,千万不要错过Mike Clayton对他修复流行的 Pandas 库中的漏洞经历的精彩总结。
-
气候变化可能是我们今天面临的决定性全球挑战;Thu Vu分享了一个基于数据的、关于其规模的有益视角,并反思了人工智能在帮助我们缓解部分后果方面的潜力。
-
对于那些有动手实践兴趣的人,我们强烈推荐Alison Yuhan Yao的新半自动图像分割标注教程,基于一个最近聚焦于 T 台秀图像的项目。
-
强有力的单元测试实践在软件开发者中很常见;Jonathan Serrano也倡导在数据科学和机器学习工作流中更广泛地采用这些实践,并解释了这种前期投入如何在长期内带来回报。
-
机器学习产品经理正在密切关注驱动其工具的技术基础设施,但正如Janna Lipenkova所强调的,确保它们提供流畅的用户体验同样至关重要。
-
当前的就业市场对许多数据专业人士来说是充满挑战的,这已不是什么秘密。Erin Wilson对她最近求职旅程的视觉总结提供了充足的灵感—以及务实的见解,来支持你在求职过程中的努力。
-
推动类人机器人进入生产线主流需要什么条件?Nikolaus Correll从机器人创新前沿报道,分析了人工智能的最新进展如何推动该领域的重大转变。
感谢您支持我们作者的工作!我们热衷于发布新作者的文章,因此,如果您最近写了一篇有趣的项目演示、教程或关于我们核心主题的理论反思,请不要犹豫,与我们分享。
直到下一个变量,
TDS 团队
数据科学学生的期望与现实
我不仅仅是在电脑前输入数字
https://medium.com/@gurmankdhaliwal2?source=post_page---byline--bb0a3b86780f--------------------------------https://towardsdatascience.com/?source=post_page---byline--bb0a3b86780f-------------------------------- Gurman Dhaliwal
·发表于Towards Data Science ·4 分钟阅读·2024 年 4 月 15 日
–
选择大学专业对我来说很困难。那感觉像是迈出了投身职业生涯的第一步,而我想要什么都尝试一下。我喜欢数学和编程,但我也希望找到一个能让我发挥创造力、提供交流平台,并且足够灵活,能够探索不同领域的工作。经过一些研究,UC 圣地亚哥的 Halıcıoğlu 数据科学研究所(HDSI)的数据科学项目看起来很适合我。尽管我选择了这条道路,但我依然心存疑虑,最初的假设也反映了这种怀疑。然而,随着我进入最后几个学期,我很高兴(也很惊讶!)发现我的实际经历与最初的预期有了很大的不同。
期望 #1:数据科学会是大量重复的数学和编程课程。
现实情况:虽然数学和编程是支柱,但课程内容其实有很多变化。**
回顾过去,我的课程比我预期的要多样化。编程和数学课占多数,但每门课程都从不同角度探讨核心主题,并为我们提供了各种工具。该领域的多样性也显著增加,从统计公平性定义到生物信息学都有涉及。我还发现了自己特别喜欢的领域,如医疗保健、数据伦理和隐私保护。这帮助我在早期就扩大了对数据科学家角色和行业的认识。
**预期 #2:我大多数时间会独自工作。
现实:我与他人合作很多,这让我变得更好。**
我喜欢与人合作。想法能更快地产生。我觉得更有创造力,也更有趣!然而,我最初还是屈服于刻板印象,想象自己会大部分时间独自一人埋头做数据科学作业,几乎整天都趴在笔记本电脑前,所以当我发现有这么多小组合作时,我感到很惊讶。几乎所有的编程和数学课程都鼓励我们与至少一个其他人合作。与我不认识的人见面和合作将我推出了舒适区,提升了我的团队合作和沟通技巧。即使在工作环境中,当我的工作是独立完成时,我也发现与其他实习生合作让我成为了一个更好的数据科学家。虽然我们每个人都有类似的基础技能,但依靠彼此利用不同的优势和关注点,使我们整体表现更好。
**预期 #3:数据科学与机器学习是一样的。
现实:机器学习只是数据科学项目生命周期的一部分。**
公平地说,刚开始我的数据科学之旅时,我对数据科学或机器学习(ML)如何定义了解不多。尽管如此,当我进入 HDSI 项目时,我认为数据科学就等同于机器学习。我想象大部分课程和工作将集中在创建预测模型和深入研究神经网络。然而,数据科学课程和工作的重点更多是在数据清洗、数据过期和可视化上,而机器学习分析所花的时间比你预期的要少… 至少目前是这样。
**预期 #4:我的角色可能会被自动化。
现实:某些职责可以自动化,但数据科学家作为问题解决者的创造力是无法被自动化的。**
这种担忧始于我第一次参加自然语言处理课程时,教授展示了 GPT-3 写代码的速度有多快。作为一名入门级的数据科学家,这让我感到很有压力——我该如何与那些能比我读得更快写出正确 SQL 查询的模型竞争呢?然而,这个练习的目的是为了说明,我们作为技术人员的角色,不仅仅是学习如何使用工具和理解使其发挥作用的内在过程。大型语言模型仍然无法正确完成你的作业,但最终(不可避免地)它们会不断改进,届时,我对它们将更多地作为数据科学家的帮助而非负担充满乐观。与数据科学家不同,LLM(大型语言模型)并不是问题解决者。它们无法生成原创的想法,利用创造力解决模糊的问题,也无法有效地与不同的听众沟通。未来这种情况可能会有所变化,但通过我的教育和职业经历,我相信自己依然能够在这个领域产生积极的影响。
重点总结
作为我数据科学之旅的一部分,我学会了接受现实中不可预见的挑战。我意识到数据科学的广度和深度非常适合做各种事情:研究、编程、分析以及讲故事。基于这一点,我对选择数据科学这条道路充满信心,也期待着职业生涯的下一个阶段带来什么。
预期之外的意外:测量惊讶的数学艺术
为什么泰勒·斯威夫特和梅西如此传奇的统计学原因
https://tuannguyen-doan.medium.com/?source=post_page---byline--28c04f0e8a1a--------------------------------https://towardsdatascience.com/?source=post_page---byline--28c04f0e8a1a-------------------------------- Tuan Doan
·发表于 Towards Data Science ·阅读时间 7 分钟·2024 年 10 月 1 日
–
泰勒·斯威夫特在“Eras Tour”演唱会上的表演。图片由Paolo Villanueva提供,来自维基百科,CC BY 2.0 许可证。
我怎么也买不到“Eras Tour”的票!!
在经历了几个小时的虚拟排队、与崩溃的服务器战斗、疯狂刷新页面后,就像其他无数失望的“Swifties”一样,我也不禁想知道,为什么一场巡演能够如此疯狂受欢迎,甚至连购买票的机会都像中了大奖一样难得。
但是,这场为了抢购门票的艰难斗争不仅仅证明了 Swift 的受欢迎程度——它也是“Eras Tour”令人震惊成功的预兆。它不仅打破了记录;它摧毁了记录。整个巡演的总收入约为估计的 21.65 亿美元,远远超过了之前的记录保持者——埃尔顿·约翰的多年告别巡演“Farewell Yellow Brick Road”,该巡演的收入为 9.39 亿美元。
我们生活在一个充满超级 lative 的时代。每天,我们都会被头条新闻轰炸,宣称各个领域的“最大”、“最快”或“最成功”的成就。但我们如何真正衡量这些成就的特殊性呢?当泰勒·斯威夫特的“Eras Tour”打破票房记录,或者梅西在一年内贡献了 106 个进球时,我们究竟应该有多惊讶?
实验追踪与超参数调整:使用 DVC 组织你的试验
图像由 Midjourney 生成
学习如何在调整模型超参数时避免迷失在众多实验中
https://eryk-lewinson.medium.com/?source=post_page---byline--d17f47f38754--------------------------------https://towardsdatascience.com/?source=post_page---byline--d17f47f38754-------------------------------- Eryk Lewinson
·发表于 Towards Data Science ·13 分钟阅读·2024 年 3 月 14 日
–
在本系列的前几部分中,我已经解释了跟踪机器学习实验的好处,并展示了如何通过 DVC 简单地实现这一目标。然而,在系列中至今未深入探讨的一个方面是超参数调整(HPT)。
虽然我们的一些实验可能涉及更改数据集、代码库、添加或删除特征,或者修复一些偶发的 bug,但这些实验的数量可能仍然是可控的,因为它们需要我们手动编写代码或进行一些分析。
然而,当我们考虑超参数调整时,事情就容易失控。在之前的部分中,我展示了通过推荐的设置,我们可以轻松地通过 params.yaml
文件来控制模型的超参数。此外,通过使用 DVC,我们可以通过对该文件进行版本控制,轻松跟踪实验。然而,这仍然涉及根据我们的专业知识或直觉手动调整超参数。如果我们采用像网格搜索这样的程序,我们可能会用不同的超参数组合进行上千次的模型拟合和评估,而这一切仅仅在短短的时间内就完成了……
与 MLFlow 和 Microsoft Fabric 的实验
Fabric 疯狂系列第四部分
https://medium.com/@roger_noble?source=post_page---byline--68f43043ff34--------------------------------https://towardsdatascience.com/?source=post_page---byline--68f43043ff34-------------------------------- Roger Noble
·发表于 Towards Data Science ·10 分钟阅读·2024 年 4 月 22 日
–
图片来源:作者和 ChatGPT。“设计一幅插图,展示数据实验的图像,聚焦于篮球数据”的提示。ChatGPT,4,OpenAI,2024 年 4 月 15 日。chat.openai.com.
特别感谢 Martim Chaves 共同撰写了这篇文章并开发了示例脚本。
毋庸置疑,机器学习(ML)系统需要精心调优才能真正发挥作用,而模型在第一次运行时完美工作是极为罕见的情况!
在开始你的 ML 之旅时,一个容易陷入的陷阱是尝试很多不同的方式来提高性能,但却没有记录这些配置。这会导致你很难知道哪个配置(或配置组合)表现最佳。
在开发模型时,有许多可以调整的“旋钮”和“杠杆”,通常提高性能的最佳方法是尝试不同的配置,看看哪个效果最好。这些内容包括改进使用的特征、尝试不同的模型架构、调整模型的超参数等。实验需要系统化,并且结果需要记录。因此,拥有一个良好的实验设置对于任何实用的 ML 系统开发至关重要,就像源代码管理对于代码开发的重要性一样。
这是实验开始发挥作用的地方。实验是一种跟踪不同配置及其结果的方法。
在 Fabric 中使用实验的好处是,它们实际上是MLFlow的一个封装,MLFlow 是一个非常流行的开源平台,用于管理端到端的机器学习生命周期。这意味着我们可以使用 MLFlow 提供的所有强大功能,但又不必担心设置一个需要协作环境的 MLFlow 基础设施。这使我们可以专注于更有趣的部分 😎!
在这篇文章中,我们将讨论如何在 Fabric 中使用实验,以及如何记录和分析这些实验的结果。具体来说,我们将涵盖:
-
MLFlow 是如何工作的?
-
创建和设置实验
-
运行实验和记录结果
-
分析结果
从高层次来看,MLFlow 是一个帮助管理端到端机器学习生命周期的平台。它是一个帮助跟踪实验、将代码打包成可重现运行、共享和部署模型的工具。它本质上是一个专门用于跟踪你运行的各种实验配置和结果的数据库。
在 MLFlow 中有两个主要的组织结构——实验和运行。
实验是一个运行的集合,其中每个运行是执行一段代码、一个函数或一个脚本。这可能是训练一个模型,但也可以用于跟踪任何在不同运行间可能会变化的内容。实验是一种将相关运行进行分组的方式。
对于每个运行,可以记录信息并将其附加到该运行上——这些信息可以是指标、超参数、标签、工件(例如图表、文件或其他有用的输出),甚至是模型!通过将模型附加到运行上,我们可以追踪哪个模型在某个运行中被使用,以及它的表现如何。可以将其视为模型的版本控制,这也是我们将在下一篇文章中深入探讨的内容。
运行可以被过滤和比较。这使我们能够了解哪些运行更成功,并选择表现最佳的运行,使用其配置(例如,在部署中)。
现在我们已经介绍了 MLFlow 的基本工作原理,接下来让我们了解如何在 Fabric 中使用它!
创建和设置实验
就像在 Fabric 中的一切一样,创建项目可以通过几种方式完成,既可以通过工作区中的**+ 新建**菜单,也可以使用数据科学体验或通过代码。在这种情况下,我们将使用数据科学体验。
图 1——使用 UI 创建实验。图像来源:作者。
一旦完成,为了在 Notebook 中使用该实验,我们需要import mlflow
并设置实验名称:
import mlflow
experiment_name = "[name of the experiment goes here]"
# Set the experiment
mlflow.set_experiment(experiment_name)
另外,实验也可以通过代码创建,这需要一个额外的命令:
import mlflow
experiment_name = "[name of the experiment goes here]"
# First create the experiment
mlflow.create_experiment(name=experiment_name)
# Then select it
mlflow.set_experiment(experiment_name)
请注意,如果已存在相同名称的实验,create_experiment
将抛出一个错误。我们可以通过先检查实验是否存在,只有在不存在时才创建它来避免这个问题:
# Check if experiment exists
# if not, create it
if not mlflow.get_experiment_by_name(experiment_name):
mlflow.create_experiment(name=experiment_name)
现在我们已经在当前上下文中设置了实验,我们可以开始运行将保存到该实验中的代码。
运行实验并记录结果
为了开始将我们的结果记录到实验中,我们需要启动一个运行。这个操作是通过start_run()
函数完成的,并返回一个run
上下文管理器。以下是如何启动一个运行的示例:
# Start the training job with `start_run()`
with mlflow.start_run(run_name="example_run") as run:
# rest of the code goes here
一旦运行开始,我们就可以开始记录度量、参数和工件。下面是一个使用简单模型和数据集的代码示例,我们记录了模型的得分和使用的超参数:
# Set the hyperparameters
hyper_params = {"alpha": 0.5, "beta": 1.2}
# Start the training job with `start_run()`
with mlflow.start_run(run_name="simple_training") as run:
# Create model and dataset
model = create_model(hyper_params)
X, y = create_dataset()
# Train model
model.fit(X, y)
# Calculate score
score = lr.score(X, y)
# Log metrics and hyper-parameters
print("Log metric.")
mlflow.log_metric("score", score)
print("Log params.")
mlflow.log_param("alpha", hyper_params["alpha"])
mlflow.log_param("beta", hyper_params["beta"])
在我们上面的示例中,训练了一个简单的模型,并计算了其得分。请注意,如何使用mlflow.log_metric("metric_name", metric)
来记录度量,并使用mlflow.log_param("param_name", param)
来记录超参数。
数据
现在让我们看一下用于训练我们基于篮球比赛结果的模型的代码。我们所查看的数据来自 2024 年美国大学篮球锦标赛,这些数据来自 2024 年 3 月机器学习狂热 Kaggle 竞赛,相关细节可以在此处找到,且该数据集使用 CC BY 4.0 许可协议。
在我们的设置中,我们想尝试三种不同的模型,这些模型使用了越来越多的参数。对于每个模型,我们还想尝试三种不同的学习率(一个控制我们在每次迭代中调整网络权重多少的超参数)。目标是找到最佳的模型和学习率组合,以便在测试集上获得最佳的Brier 得分。
模型
为了定义模型架构,我们使用了 TensorFlow,创建了三个简单的神经网络。以下是帮助定义模型的函数。
from tensorflow.keras.models import Sequential
from tensorflow.keras.layers import Dense
def create_model_small(input_shape):
model = Sequential([
Dense(64, activation='relu', input_shape=(input_shape,)),
Dense(1, activation='sigmoid')
])
return model
def create_model_medium(input_shape):
model = Sequential([
Dense(64, activation='relu', input_shape=(input_shape,)),
Dense(64, activation='relu'),
Dense(1, activation='sigmoid')
])
return model
def create_model_large(input_shape):
model = Sequential([
Dense(128, activation='relu', input_shape=(input_shape,)),
Dense(64, activation='relu'),
Dense(64, activation='relu'),
Dense(1, activation='sigmoid')
])
return model
通过这种方式创建模型,使我们可以轻松地尝试不同的架构,并查看它们的表现。我们可以使用字典创建一个小型的模型工厂,让我们能够轻松地创建我们想要实验的模型。
我们还定义了输入形状,即可用特征的数量。我们决定将模型训练 100 个 epoch,这应该足以让模型收敛🤞。
model_dict = {
'model_sma': create_model_small, # small
'model_med': create_model_medium, # medium
'model_lar': create_model_large # large
}
input_shape = X_train_scaled_df.shape[1]
epochs = 100
在这初步设置之后,是时候对模型字典进行迭代了。对于每个模型,都会创建一个实验。请注意,我们使用了之前的代码片段,其中我们首先检查实验是否存在,只有在实验不存在时才会创建它。否则,我们只需设置它。
import mlflow
for model_name in model_dict:
# create mlflow experiment
experiment_name = "experiment_v2_" + model_name
# Check if experiment exists
# if not, create it
if not mlflow.get_experiment_by_name(experiment_name):
mlflow.create_experiment(name=experiment_name)
# Set experiment
mlflow.set_experiment(experiment_name)
设置完实验后,我们针对每个模型进行了三次运行,尝试不同的学习率[0.001, 0.01, 0.1]
。
for model_name in model_dict:
# Set the experiment
...
learning_rate_list = [0.001, 0.01, 0.1]
for lr in learning_rate_list:
# Create run name for better identification
run_name = f"{model_name}_{lr}"
with mlflow.start_run(run_name=run_name) as run:
...
# Train model
# Save metrics
然后,在每次运行中,我们初始化了一个模型,编译并训练它。编译和训练是在一个单独的函数中完成的,接下来我们将详细讲解。由于我们希望设置学习率,因此必须手动初始化 Adam 优化器。我们使用均方误差(MSE)损失函数作为指标,保存具有最佳验证损失的模型,并记录训练和验证损失,以确保模型在收敛。
def compile_and_train(model, X_train, y_train, X_val, y_val, epochs=100, learning_rate=0.001):
# Instantiate the Adam optimiser with the desired learning rate
optimiser = Adam(learning_rate=learning_rate)
model.compile(optimizer=optimiser, loss='mean_squared_error', metrics=['mean_squared_error'])
# Checkpoint to save the best model according to validation loss
checkpoint_cb = ModelCheckpoint("best_model.h5", save_best_only=True, monitor='val_loss')
history = model.fit(X_train, y_train, validation_data=(X_val, y_val),
epochs=epochs, callbacks=[checkpoint_cb], verbose=1)
# Load and return the best model saved during training
best_model = load_model("best_model.h5")
return history, best_model
在初始化模型、编译并训练它之后,接下来的步骤是记录训练和验证损失,计算测试集的 Brier 分数,然后记录得分和使用的学习率。通常我们还会使用 step
参数在 log_metric
中记录训练和验证损失,像这样:
# Log training and validation losses
for epoch in range(epochs):
train_loss = history.history['loss'][epoch]
val_loss = history.history['val_loss'][epoch]
mlflow.log_metric("train_loss", train_loss, step=epoch)
mlflow.log_metric("val_loss", val_loss, step=epoch)
然而,我们选择自己使用 matplotlib
创建训练和验证损失图,并将其记录为一个工件。
以下是绘图函数:
import matplotlib.pyplot as plt
def create_and_save_plot(train_loss, val_loss, model_name, lr):
epochs = range(1, len(train_loss) + 1)
# Creating the plot
plt.figure(figsize=(10, 6))
plt.plot(epochs, train_loss, 'b', label='Training loss')
plt.plot(epochs, val_loss, 'r', label='Validation loss')
plt.title('Training and Validation Loss')
plt.xlabel('Epochs')
plt.ylabel('Loss')
plt.legend()
plt.grid(True)
plt.title(f"Training and Validation Loss (M: {model_name}, LR: {lr})")
# Save plot to a file
plot_path = f"{model_name}_{lr}_loss_plot.png"
plt.savefig(plot_path)
plt.close()
return plot_path
将所有内容整合起来,以下是该代码的样子:
with mlflow.start_run(run_name=run_name) as run:
# Create model and dataset
model = model_dictmodel_name
# Train model
history, best_model = compile_and_train(model,
X_train_scaled_df, y_train,
X_validation_scaled_df, y_validation,
epochs,
lr)
# Log training and validation loss plot as an artifact
train_loss = history.history['loss']
val_loss = history.history['val_loss']
plot_path = create_and_save_plot(train_loss, val_loss, model_name, lr)
mlflow.log_artifact(plot_path)
# Calculate score
brier_score = evaluate_model(best_model, X_test_scaled_df, y_test)
# Log metrics and hyper-parameters
mlflow.log_metric("brier", brier_score)
# Log hyper-param
mlflow.log_param("lr", lr)
# Log model
...
对于每次运行,我们还记录了模型,这对后续会很有用。
实验已被运行,为每个模型创建了一个实验,并为每个实验进行了三次不同的运行,使用了不同的学习率。
分析结果
现在我们已经运行了一些实验,是时候分析结果了!为此,我们可以回到工作区,在那里我们可以找到新创建的实验以及多个运行。
图 2 — 实验列表。图片由作者提供。
点击一个实验后,以下是我们将看到的内容:
图 3 — 实验界面。图片由作者提供。
在左侧,我们会看到与该实验相关的所有运行。在这种情况下,我们正在查看小模型实验。对于每次运行,都会有两个工件,即验证损失图和训练好的模型。还有关于运行的属性信息——状态和持续时间,以及记录的指标和超参数。
通过点击查看运行列表,在比较运行部分下,我们可以比较不同的运行。
图 4 — 比较运行。图片由作者提供。
在运行列表视图中,我们可以选择希望比较的运行。在指标比较选项卡中,我们可以找到展示 Brier 分数与学习率关系的图表。在我们的案例中,看起来学习率越低,得分越好。我们甚至可以进一步创建更多图表,展示不同指标与其他超参数的关系(如果不同的指标和超参数已被记录的话)。
图 5 — 展示 Brier 分数与学习率关系的图表。图片由作者提供。
也许我们希望筛选运行——可以使用筛选器来完成此操作。例如,我们可以选择 Brier 分数低于 0.25 的运行。您可以根据记录的指标和参数以及运行的属性创建筛选器。
图 6 — 根据 Brier 得分筛选运行。图像由作者提供。
通过这样做,我们可以直观地比较不同的运行并评估哪个配置带来了最佳性能。这也可以通过代码实现 —— 这将是下一篇文章进一步探讨的内容。
使用实验 UI,我们能够直观地探索不同的实验和运行,按需进行比较和筛选,以了解哪个配置效果最佳。
结论
这就是我们对 Fabric 实验的探索总结!
我们不仅介绍了如何创建和设置实验,还讲解了如何运行实验并记录结果。我们还展示了如何分析结果,使用实验 UI 来比较和筛选运行。
在下一篇文章中,我们将讨论如何选择最佳模型,并展示如何部署它。敬请期待!
原文发布于 https://nobledynamic.com ,发布时间为 2024 年 4 月 22 日。
机器学习中的可解释性、可解释性和可观察性
这些术语通常用来描述模型的透明度,但它们到底是什么意思?
https://medium.com/@jasonyzhong06?source=post_page---byline--515a2ac8234a--------------------------------https://towardsdatascience.com/?source=post_page---byline--515a2ac8234a-------------------------------- Jason Zhong
·发表于Towards Data Science ·6 分钟阅读·2024 年 6 月 30 日
–
模型洞察。截图来自Xplainable。
机器学习(ML)由于能够从大数据集中生成准确的预测和可操作的洞察,已经在各个行业中变得越来越普及。全球有 34%的公司已部署机器学习,并报告在客户保持、收入增长和成本效率方面取得了显著改善 (IBM, 2022)。这一机器学习采用激增的原因在于模型变得更加易于访问,且能够以更高的准确性生成结果,在多个领域超越了传统的商业方法。
然而,随着机器学习模型变得越来越复杂,同时又被广泛依赖,透明度的需求变得日益重要。根据 IBM 的全球采用指数,80%的企业认为能够确定模型如何得出决策是一个关键因素。这在医疗保健和刑事司法等行业尤为重要,在这些领域,模型及其所做决策的信任与问责至关重要。透明度的缺乏可能是限制这些行业广泛使用机器学习的因素,可能会阻碍运营速度、决策过程和整体效率的显著提升。
三个关键术语——可解释性、可理解性和可观察性——被广泛认为构成了机器学习模型的透明度。
尽管这些概念很重要,研究人员仍未能为它们建立严格的定义和区分,这源于缺乏数学形式化以及无法通过特定指标来衡量它们(Linardatos et al., 2020)。
可解释性
可解释性没有标准的定义,但通常被认为指的是**“针对人工智能透明度和信任问题所做的运动、倡议和努力”** (Adadi & Berrada, 2018)。Bibal 等人(2021)旨在制定法律要求的指导方针,得出的结论是,一个可解释的模型必须能够“(i) [提供] 用于做出决策的主要特征,(ii) [提供] 所有处理过的特征,(iii) [提供] 对决策的全面解释,(iv) [提供] 对整个模型的可理解表示”。他们将可解释性定义为提供“有关如何做出特定决策的有意义的见解”,这需要“一种思维过程,能够让决策对用户有意义(即让他能理解该决策)”。因此,可解释性指的是理解支持决策的模型内部逻辑和机制。
可解释性的历史例子是 AlphaGo(一个算法)与李世石(被认为是史上最优秀的围棋选手之一)之间的围棋对局。在第二局比赛中,AlphaGo 的第 19 手棋被专家和创作者广泛认为是“如此令人惊讶,[颠覆]了几百年的传统智慧”(Coppey, 2018)。这一着棋极为‘非人类’,但却是决定性的一着,最终使得该算法赢得了比赛。尽管人类后来能够确定这一着棋的动机,但他们无法解释为什么模型选择这一着棋,而不是其他着棋,缺乏对模型逻辑的内部理解。这展示了机器学习超越人类能力的非凡计算能力,但也提出了一个问题:这足以让我们盲目信任它们的决策吗?
虽然准确性是采用机器学习的关键因素,但在许多情况下,可解释性被认为比准确性更为重要。
医生们不愿意,也有充分理由不愿意接受一个如果无法提供决策背后内部逻辑的模型,即便该模型从长远来看对患者有益,特别是当模型输出结果为“不应去除癌症肿瘤”时。这是机器学习,尽管具有巨大潜力,未能在许多领域得到充分利用的主要限制因素之一。
可解释性
可解释性通常被认为与可解释性相似,并且常常可以互换使用。然而,普遍认为可解释性指的是基于输入理解整体决策的能力,而不需要完全理解模型是如何生成输出的。因此,可解释性被视为比可解释性更广泛的术语。Doshi-Velez 和 Kim (2017) 将可解释性定义为**“能够以人类可以理解的术语进行解释或呈现的能力”**。另一个流行的可解释性定义是“人类理解决策原因的程度”(Miller, 2019)。
在实际应用中,一个可解释的模型可能是能够通过可识别的模式和特征(例如毛发的存在)预测家庭宠物图像是动物的模型。然而,这个模型缺乏对内部逻辑或过程的理解,这使得该模型无法解释。
尽管许多研究人员在相同的语境中使用“可解释性”(interpretability)和“可解释性”(explainability)这两个词,但“可解释性”通常指的是对模型内部工作原理的更深入理解。
Doshi-Velez 和 Kim (2017) 提出了评估可解释性的三种方法。第一种方法是应用级评估。这包括通过将模型与领域专家进行任务对比评估,确保模型的有效性。一个例子是将 CT 扫描模型的性能与放射科医生使用相同数据的表现进行比较。第二种方法是人类级评估,要求外行评估解释的质量,例如选择他们认为更高质量的模型解释。最后一种方法是功能性基础评估,不需要人类参与。相反,模型是根据可解释性的某种正式定义进行评估的。这可能包括展示一个已经被证明是可解释的模型在预测准确性上的提高。假设是,如果预测准确性有所提高,那么可解释性就更高,因为模型已经通过基础合理的推理生成了正确的输出。
可观察性
机器学习可观测性是指了解机器学习模型在生产环境中的表现。Mahinda (2023)235) 将可观测性定义为“通过系统的输出测量和理解系统状态的一种手段”,并进一步指出它“是操作系统和基础设施的必要实践,其可靠性依赖于此”。可观测性旨在解决一个潜在问题,即在研发中表现出色的模型可能在部署中不如预期。 这种差异通常是由于模型遇到的实际数据与最初训练时使用的历史数据之间的差异所致。因此,持续监控输入数据和模型性能至关重要。在涉及高风险问题的行业中,确保模型按预期表现是采用的关键前提。
可观测性是保持模型在现实条件下性能的关键方面。
可观测性由两种主要方法组成,监控和可解释性 (机器学习模型可观测性指南, n.d.)。
在部署过程中,可以使用多种指标来监控模型的性能,例如精确度、F1 得分和 AUC ROC。通常,当某个值达到时,系统会触发警报,从而促使对问题根源的及时调查。
可解释性是可观测性的一个重要组成部分。了解为什么一个模型在某个数据集上的表现不佳,对于能够改进模型,使其在未来相似情况下表现更优至关重要。如果无法理解形成决策的底层逻辑,就无法对模型进行改进。
结论
随着机器学习的进一步普及,模型透明度的重要性成为确保决策背后信任和问责的关键因素。
可解释性使用户能够理解机器学习模型的内部逻辑,增强对模型预测结果的信心。可解释性确保模型预测背后的理由能够被验证和证明。可观测性提供对模型性能的监控和洞察,帮助在生产环境中迅速而准确地发现操作问题。
尽管机器学习有巨大的潜力,但基于我们无法完全理解的模型决策进行操作所带来的风险不容忽视。因此,在机器学习系统的开发和集成中,必须优先考虑可解释性、可解释性和可观测性。
创建具有高预测准确性的透明模型一直是并将继续带来巨大的挑战。然而,这一追求将带来负责任且知情的决策,远远超越当前的模型。
可解释的通用机器学习管道与 MLflow
一个端到端的示范,将预处理器和解释器包装成一个算法无关的机器学习管道,使用mlflow.pyfunc
https://menawang.medium.com/?source=post_page---byline--2494ca1b3f96--------------------------------https://towardsdatascience.com/?source=post_page---byline--2494ca1b3f96-------------------------------- Mena Wang, PhD
·发表于 Towards Data Science ·13 分钟阅读·2024 年 11 月 26 日
–
图片由 Hannah Murrell 提供,来源 Unsplash
介绍
MLOps 中的一个常见挑战是迁移不同算法或框架时的麻烦。为了解决这个问题,这是我关于使用mlflow.pyfunc
进行通用模型构建的第二篇文章。
在我之前的文章中,我提供了一个适合初学者的逐步示范,展示如何创建一个极简的算法无关模型包装器。
一个适合初学者的逐步指南,展示如何使用 mlflow.pyfunc 创建通用机器学习管道
towardsdatascience.com
为了推进我们的旅程,在本文结束时,我们将构建一个更为复杂的机器学习管道,具备以下功能:
-
该管道支持分类(二分类)和回归任务。它适用于 scikit-learn 模型以及其他遵循 scikit-learn 接口的算法(即,fit、predict/predict_proba)。
-
引入一个功能完备的
预处理器
,它可以在训练数据上拟合,然后用于转换新数据,以供模型使用。这个预处理器可以处理数值型和类别型特征,并能通过各种插补策略处理缺失值。 -
添加一个
explainer
来阐明模型的推理过程,这对于模型选择、监控和实现至关重要。由于不同机器学习算法对 SHAP 值的实现各异,这项任务可能会很棘手。但没问题,我们将在本文中解决这个挑战。😎
与前一篇文章一致,
-
你将看到切换不同自定义预处理器是多么简单,类似于切换不同的机器学习算法。
-
这个机器学习管道将所有自定义的管道元素封装在背后,同时仍然提供统一的
pyfunc
模型表示,以简化模型的部署、重新部署和下游评分。
🔗 所有代码和配置可以在GitHub上找到。🧰
预处理器(V1)
许多机器学习算法——例如线性模型(如线性回归、支持向量机)、基于距离的模型(如 KNN、PCA)以及基于梯度的模型(如梯度提升方法或梯度下降优化)——通常在对输入特征进行缩放后表现更好,因为缩放可以防止具有较大范围的特征主导学习过程。此外,现实世界中的数据通常包含缺失值。因此,在这个第一版中,我们将构建一个预处理器,它可以训练来缩放新数据并填充缺失值,为模型的使用做准备。
一旦这个预处理器构建完成,我将演示如何轻松地将它集成到pyfunc
机器学习管道中。听起来不错吧?我们开始吧。🤠
class PreProcessor(BaseEstimator, TransformerMixin):
"""
Custom preprocessor for numeric features.
- Handles scaling of numeric data
- Performs imputation of missing values
Attributes:
transformer (Pipeline): Pipeline for numeric preprocessing
features (List[str]): Names of input features
"""
def __init__(self):
"""
Initialize preprocessor.
- Creates placeholder for transformer pipeline
"""
self.transformer = None
def fit(self, X, y=None):
"""
Fits the transformer on the provided dataset.
- Configures scaling for numeric features
- Sets up imputation for missing values
- Stores feature names for later use
Parameters:
X (pd.DataFrame): The input features to fit the transformer.
y (pd.Series, optional): Target variable, not used in this method.
Returns:
PreProcessor: The fitted transformer instance.
"""
self.features = X.columns.tolist()
if self.features:
self.transformer = Pipeline(steps=[
('imputer', SimpleImputer(strategy='median')),
('scaler', StandardScaler())
])
self.transformer.fit(X[self.features])
return self
def transform(self, X):
"""
Transform input data using fitted pipeline.
- Applies scaling to numeric features
- Handles missing values through imputation
Parameters:
X (pd.DataFrame): Input features to transform
Returns:
pd.DataFrame: Transformed data with scaled and imputed features
"""
X_transformed = pd.DataFrame()
if self.features:
transformed_data = self.transformer.transform(X[self.features])
X_transformed[self.features] = transformed_data
X_transformed.index = X.index
return X_transformed
def fit_transform(self, X, y=None):
"""
Fits the transformer on the input data and then transforms it.
Parameters:
X (pd.DataFrame): The input features to fit and transform.
y (pd.Series, optional): Target variable, not used in this method.
Returns:
pd.DataFrame: The transformed data.
"""
self.fit(X, y)
return self.transform(X)
这个预处理器可以在训练数据上进行拟合,然后用于处理任何新的数据。它将成为下面机器学习管道中的一个元素,但当然,我们也可以独立使用或测试它。让我们创建一个合成数据集,并使用预处理器来转换它。
# Set parameters for synthetic data
n_feature = 10
n_inform = 4
n_redundant = 0
n_samples = 1000
# Generate synthetic classification data
X, y = make_classification(
n_samples=n_samples,
n_features=n_feature,
n_informative=n_inform,
n_redundant=n_redundant,
shuffle=False,
random_state=12
)
# Create feature names
feat_names = [f'inf_{i+1}' for i in range(n_inform)] + \
[f'rand_{i+1}' for i in range(n_feature - n_inform)]
# Convert to DataFrame with named features
X = pd.DataFrame(X, columns=feat_names)
# Split data into train and test sets
X_train, X_test, y_train, y_test = train_test_split(
X, y,
test_size=0.2,
random_state=22
)
以下是{sweetViz}报告在缩放前后的截图;你可以看到,缩放没有改变每个特征分布的基本形状,只是重新缩放并移动了它。顺便说一下,只需要两行代码就能生成一份非常全面的 EDA 报告,{sweetViz}的代码可以在上面链接的 GitHub 仓库中找到。🥂
预处理前后 SweetViz 报告的截图
带预处理器的机器学习管道
现在,让我们创建一个mlflow.pyfunc
风格的机器学习管道,它可以封装这个预处理器。
class ML_PIPELINE(mlflow.pyfunc.PythonModel):
"""
Custom ML pipeline for classification and regression.
- work with any scikit-learn compatible model
- Combines preprocessing and model training
- Handles model predictions
- Compatible with MLflow tracking
- Supports MLflow deployment
Attributes:
model (BaseEstimator or None): A scikit-learn compatible model instance
preprocessor (Any or None): Data preprocessing pipeline
config (Any or None): Optional config for model settings
task(str): Type of ML task ('classification' or 'regression')
"""
def __init__(self, model=None, preprocessor=None, config=None):
"""
Initialize the ML_PIPELINE.
Parameters:
model (BaseEstimator, optional):
- Scikit-learn compatible model
- Defaults to None
preprocessor (Any, optional):
- Transformer or pipeline for data preprocessing
- Defaults to None
config (Any, optional):
- Additional model settings
- Defaults to None
"""
self.model = model
self.preprocessor = preprocessor
self.config = config
self.task = "classification" if hasattr(self.model, "predict_proba") else "regression"
def fit(self, X_train: pd.DataFrame, y_train: pd.Series):
"""
Train the model on provided data.
- Applies preprocessing to features
- Fits model on transformed data
Parameters:
X_train (pd.DataFrame): Training features
y_train (pd.Series): Target values
"""
X_train_preprocessed = self.preprocessor.fit_transform(X_train.copy())
self.model.fit(X_train_preprocessed, y_train)
def predict(
self, context: Any, model_input: pd.DataFrame
) -> np.ndarray:
"""
Generate predictions using trained model.
- Applies preprocessing to new data
- Uses model to make predictions
Parameters:
context (Any): Optional context information provided
by MLflow during the prediction phase
model_input (pd.DataFrame): Input features
Returns:
Any: Model predictions or probabilities
"""
processed_model_input = self.preprocessor.transform(model_input.copy())
if self.task == "classification":
prediction = self.model.predict_proba(processed_model_input)[:,1]
elif self.task == "regression":
prediction = self.model.predict(processed_model_input)
return prediction
上面定义的机器学习管道将预处理器和机器学习算法作为参数。以下是使用示例
# define the ML pipeline instance with lightGBM classifier
ml_pipeline = ML_PIPELINE(model = lgb.LGBMClassifier(),
preprocessor = PreProcessor())
就是这么简单!🎉 如果你想尝试其他算法,只需像下面一样交换即可。作为包装器,它可以封装回归和分类算法。对于后者,将返回预测的概率,如上例所示。
# define the ML pipeline instance with random forest regressor
ml_pipeline = ML_PIPELINE(model = RandomForestRegressor(),
preprocessor = PreProcessor())
如下方代码片段所示,向算法传递超参数非常简单,这使得该 ML 管道成为超参数调优的完美工具。我将在后续的文章中详细讲解这个话题。
params = {
'n_estimators': 100,
'max_depth': 6,
'learning_rate': 0.1
}
model = xgb.XGBClassifier(**params)
ml_pipeline = ML_PIPELINE(model = model,
preprocessor = PreProcessor())
因为这个 ML 管道是基于mlflow.pyfunc
版本构建的。我们可以使用mlflow
自动保存的丰富元数据进行日志记录,供下游使用。部署后,我们可以将元数据作为context
传递给模型,在predict
函数中使用,如下所示。更多信息和演示可以在我之前的文章中找到,链接已在文中给出。
# train the ML pipeline
ml_pipeline.fit(X_train, y_train)
# use the trained pipeline for prediction
y_prob = ml_pipeline.predict(
context=None, # provide metadata for model in production
model_input=X_test
)
auc = roc_auc_score(y_test, y_prob)
print(f"auc: {auc:.3f}")
预处理器(V2)
上面的预处理器到目前为止表现良好,但我们将通过下面的两种方式进行改进,然后展示如何轻松切换预处理器。
-
允许用户自定义预处理过程。例如,指定填充策略。
-
扩展预处理器的能力,以处理类别特征。
class PreProcessor_v2(BaseEstimator, TransformerMixin):
"""
Custom transformer for data preprocessing.
- Scales numeric features
- Encodes categorical features
- Handles missing values via imputation
- Compatible with scikit-learn pipeline
Attributes:
num_impute_strategy (str): Numeric imputation strategy
cat_impute_strategy (str): Categorical imputation strategy
num_transformer (Pipeline): Numeric preprocessing pipeline
cat_transformer (Pipeline): Categorical preprocessing pipeline
transformed_cat_cols (List[str]): One-hot encoded column names
num_features (List[str]): Numeric feature names
cat_features (List[str]): Categorical feature names
"""
def __init__(self, num_impute_strategy='median',
cat_impute_strategy='most_frequent'):
"""
Initialize the transformer.
- Sets up numeric data transformer
- Sets up categorical data transformer
- Configures imputation strategies
Parameters:
num_impute_strategy (str): Strategy for numeric missing values
cat_impute_strategy (str): Strategy for categorical missing values
"""
self.num_impute_strategy = num_impute_strategy
self.cat_impute_strategy = cat_impute_strategy
def fit(self, X, y=None):
"""
Fit transformer on input data.
- Identifies feature types
- Configures feature scaling
- Sets up encoding
- Fits imputation strategies
Parameters:
X (pd.DataFrame): Input features
y (pd.Series, optional): Target variable, not used
Returns:
CustomTransformer: Fitted transformer
"""
self.num_features = X.select_dtypes(include=np.number).columns.tolist()
self.cat_features = X.select_dtypes(exclude=np.number).columns.tolist()
if self.num_features:
self.num_transformer = Pipeline(steps=[
('imputer', SimpleImputer(strategy=self.num_impute_strategy)),
('scaler', StandardScaler())
])
self.num_transformer.fit(X[self.num_features])
if self.cat_features:
self.cat_transformer = Pipeline(steps=[
('imputer', SimpleImputer(strategy=self.cat_impute_strategy)),
('encoder', OneHotEncoder(handle_unknown='ignore'))
])
self.cat_transformer.fit(X[self.cat_features])
return self
def get_transformed_cat_cols(self):
"""
Get transformed categorical column names.
- Creates names after one-hot encoding
- Combines category with encoded values
Returns:
List[str]: One-hot encoded column names
"""
cat_cols = []
cats = self.cat_features
cat_values = self.cat_transformer['encoder'].categories_
for cat, values in zip(cats, cat_values):
cat_cols += [f'{cat}_{value}' for value in values]
return cat_cols
def transform(self, X):
"""
Transform input data.
- Applies fitted scaling
- Applies fitted encoding
- Handles numeric and categorical features
Parameters:
X (pd.DataFrame): Input features
Returns:
pd.DataFrame: Transformed data
"""
X_transformed = pd.DataFrame()
if self.num_features:
transformed_num_data = self.num_transformer.transform(X[self.num_features])
X_transformed[self.num_features] = transformed_num_data
if self.cat_features:
transformed_cat_data = self.cat_transformer.transform(X[self.cat_features]).toarray()
self.transformed_cat_cols = self.get_transformed_cat_cols()
transformed_cat_df = pd.DataFrame(transformed_cat_data, columns=self.transformed_cat_cols)
X_transformed = pd.concat([X_transformed, transformed_cat_df], axis=1)
X_transformed.index = X.index
return X_transformed
def fit_transform(self, X, y=None):
"""
Fit and transform input data.
- Fits transformer to data
- Applies transformation
- Combines both operations
Parameters:
X (pd.DataFrame): Input features
y (pd.Series, optional): Target variable, not used
Returns:
pd.DataFrame: Transformed data
"""
self.fit(X, y)
return self.transform(X)
自定义预处理器的轻松切换
就是这样:一个新的预处理器,它 1)更加可定制,2)能够处理数值特征和类别特征。让我们用它定义一个 ML 管道实例。
# Define a PreProcessor (V2) instance while specifying impute strategy
preprocessor = PreProcessor_v2(
num_impute_strategy = 'mean'
)
# Define an ML Pipeline instance with this preprocessor
ml_pipeline = ML_PIPELINE(
model = xgb.XGBClassifier(), # switch ML algorithms
preprocessor = PreProcessor # switch pre-processors
)
让我们用另一个包含数值特征和类别特征的合成数据集测试这个新的 ML 管道实例。
# add missings
np.random.seed(42)
missing_rate = 0.20
n_missing = int(np.floor(missing_rate * X.size))
rows = np.random.randint(0, X.shape[0], n_missing)
cols = np.random.randint(0, X.shape[1], n_missing)
X.values[rows, cols] = np.nan
actual_missing_rate = X.isna().sum().sum() / X.size
print(f"Target missing rate: {missing_rate:.2%}")
print(f"Actual missing rate: {actual_missing_rate:.2%}")
# change X['inf_1] to categorical
percentiles = [0, 0.1, 0.5, 0.9, 1]
labels = ['bottom', 'lower-mid', 'upper-mid', 'top']
X['inf_1'] = pd.qcut(X['inf_1'], q=percentiles, labels=labels)
就是这样——这个 ML 管道在新数据上运行顺利。然而,正如预期的那样,如果我们用之前的预处理器定义 ML 管道,然后在这个数据集上运行它,我们将遇到错误,因为之前的预处理器并没有设计来处理类别特征。
# create an ML pipeline instance with PreProcessor v1
ml_pipeline = ML_PIPELINE(
model = lgb.LGBMClassifier(verbose = -1),
preprocessor = PreProcessor()
)
try:
ml_pipeline.fit(X_train, y_train)
except Exception as e:
print(f"Error: {e}")
Error: Cannot use median strategy with non-numeric data:
could not convert string to float: 'lower-mid'
可解释的 ML 管道的好处
在 ML 管道中添加解释器在多个方面都非常有帮助:
-
模型选择:通过评估模型推理的合理性,它有助于我们选择最佳模型。两个算法在像 AUC 或精度这样的指标上可能表现相似,但它们依赖的关键特征可能不同。与领域专家一起回顾模型的推理,讨论在这种情况下哪个模型更合理是一个好主意。
-
故障排除:一种有助于模型改进的策略是分析错误背后的推理。例如,在分类问题中,我们可以识别出模型最有信心的假阳性(即预测的可能性最高),并调查推理中出了什么问题,哪些关键特征导致了错误。
-
模型监控:除了数据漂移和性能指标等典型监控元素外,监控模型推理同样具有重要意义。如果生产中驱动模型决策的关键特征发生了显著变化,我希望能够收到警报。
-
模型实现:在某些场景中,提供模型推理和模型预测的结合对于最终用户来说是非常有益的。例如,为了帮助客户服务人员最有效地挽留流失客户,我们可以提供流失评分以及贡献该评分的客户特征。
将解释器添加到机器学习管道中
因为我们的机器学习管道是算法无关的,因此解释器也必须能够跨算法工作。
SHAP(Shapley 加性解释)值是我们目的的理想选择,因为它们基于博弈论提供理论上稳健的解释。它们设计上能够在各种算法中一致工作,包括基于树的和非基于树的模型,对于后者会有一些近似。此外,SHAP 还提供丰富的可视化功能,并被广泛认为是行业标准。
在下面的笔记本中,我深入探讨了 SHAP 在各种机器学习算法中的实现的相似性与差异。
要为我们的机器学习管道创建一个通用的解释器,需要解决的关键差异是
1. 模型是否被
***shap.Explainer***
直接支持
特定模型的 SHAP 解释器比模型无关的解释器更高效。因此,我们在这里采用的方法是
-
首先尝试使用直接的 SHAP 解释器来适应模型类型,
-
如果这失败了,则回退到使用 predict 函数的模型无关解释器。
2. SHAP 值的形状
对于二分类问题,SHAP 值可以有两种格式/形状。
- 格式 1:仅显示对正类的影响
shape = (n_samples, n_features) # 2d array
- 格式 2:显示对两个类别的影响
shape = (n_samples, n_features, n_classes) # 3d array
- 以下的解释器实现总是展示对正类的影响。当 SHAP 值中同时有正类和负类的影响时,它会选择正类的影响。
请参见下面的代码,了解上述方法的实现。
class ML_PIPELINE(mlflow.pyfunc.PythonModel):
"""
Custom ML pipeline for classification and regression.
- Works with scikit-learn compatible models
- Handles data preprocessing
- Manages model training and predictions
- Provide global and local model explanation
- Compatible with MLflow tracking
- Supports MLflow deployment
Attributes:
model (BaseEstimator or None): A scikit-learn compatible model instance
preprocessor (Any or None): Data preprocessing pipeline
config (Any or None): Optional config for model settings
task(str): Type of ML task ('classification' or 'regression')
both_class (bool): Whether SHAP values include both classes
shap_values (shap.Explanation): SHAP values for model explanation
X_explain (pd.DataFrame): Processed features for SHAP explanation
"""
# ------- same code as above ---------
def explain_model(self,X):
"""
Generate SHAP values and plots for model interpretation.
This method:
1\. Transforms the input data using the fitted preprocessor
2\. Creates a SHAP explainer appropriate for the model type
3\. Calculates SHAP values for feature importance
4\. Generates a summary plot of feature importance
Parameters:
X : pd.DataFrame
Input features to generate explanations for.
Returns: None
The method stores the following attributes in the class:
- self.X_explain : pd.DataFrame
Transformed data with original numeric values for interpretation
- self.shap_values : shap.Explanation
SHAP values for each prediction
- self.both_class : bool
Whether the model outputs probabilities for both classes
"""
X_transformed = self.preprocessor.transform(X.copy())
self.X_explain = X_transformed.copy()
# get pre-transformed values for numeric features
self.X_explain[self.preprocessor.num_features] = X[self.preprocessor.num_features]
self.X_explain.reset_index(drop=True)
try:
# Attempt to create an explainer that directly supports the model
explainer = shap.Explainer(self.model)
except:
# Fallback for models or shap versions where direct support may be limited
explainer = shap.Explainer(self.model.predict, X_transformed)
self.shap_values = explainer(X_transformed)
# get the shape of shap values and extract accordingly
self.both_class = len(self.shap_values.values.shape) == 3
if self.both_class:
shap.summary_plot(self.shap_values[:,:,1])
elif self.both_class == False:
shap.summary_plot(self.shap_values)
def explain_case(self,n):
"""
Generate SHAP waterfall plot for one specific case.
- Shows feature contributions
- Starts from base value
- Ends at final prediction
- Shows original feature values for better interpretability
Parameters:
n (int): Case index (1-based)
e.g., n=1 explains the first case.
Returns:
None: Displays SHAP waterfall plot
Notes:
- Requires explain_model() first
- Shows positive class for binary tasks
"""
if self.shap_values is None:
print("""
Please explain model first by running
`explain_model()` using a selected dataset
""")
else:
self.shap_values.data = self.X_explain
if self.both_class:
shap.plots.waterfall(self.shap_values[:,:,1][n-1])
elif self.both_class == False:
shap.plots.waterfall(self.shap_values[n-1])
现在,更新后的机器学习管道实例可以通过一行代码为你创建解释性图表。😎
用于模型全局解释的 SHAP 图
用于特定案例局部解释的 SHAP 图
记录并使用模型
当然,你可以使用mlflow
记录训练好的机器学习管道,并享受所有关于模型部署和可重复性的元数据。在下面的截图中,你可以看到,除了 pickle 保存的pyfunc
模型本身,Python 环境、指标和超参数都已经在下面的几行代码中记录下来了。想了解更多,请参考我之前关于mlflow.pyfunc
的文章,链接已在文中提到。
# Log the model with MLflow
with mlflow.start_run() as run:
# Log the custom model with auto-captured conda environment
model_info = mlflow.pyfunc.log_model(
artifact_path="model",
python_model=ml_pipeline,
conda_env=mlflow.sklearn.get_default_conda_env()
)
# Log model parameters
mlflow.log_params(ml_pipeline.model.get_params())
# Log metrics
mlflow.log_metric("rmse", rmse)
# Get the run ID
run_id = run.info.run_id
使用 mlflow 记录丰富的模型元数据和工件
结论与下一步
就是这样,一个通用且可解释的机器学习管道,适用于分类和回归算法。拿走代码并扩展它以适应你的使用案例。🤗 如果你觉得这个有用,请给我一个掌声 👏🥰
为了进一步推进mlflow.pyfunc
系列的旅程,以下是我正在考虑的一些话题。欢迎留言告诉我你希望看到哪些内容。🥰
-
特征选择
-
超参数调优
-
如果不选择在现成算法中挑选一个,而是决定集成多个算法或拥有高度定制的解决方案,他们依然可以享受通用模型表示和通过
mlflow.pyfunc
的无缝迁移。
敬请关注并在Medium上关注我。😁
💼LinkedIn | 😺GitHub | 🕊️Twitter/X
除非另有说明,所有图片均由作者提供。