TowardsDataScience 2023 博客中文翻译(三百一十三)

原文:TowardsDataScience

协议:CC BY-NC-SA 4.0

数据科学家的工具箱:解析

原文:towardsdatascience.com/the-data-scientists-toolbox-parsing-86ae196a9db5?source=collection_archive---------7-----------------------#2023-11-11

如果您有合适的工具,解析复杂文档将会变得很容易

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传 Douglas Blank, PhD

·

阅读更多 发表在向数据科学迈进 ·9 分钟读·2023 年 11 月 11 日

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

本文讨论的基于 Python 的新 rd2md 解析器和转换器的源代码。图片由作者提供。

对于许多数据科学家来说,将复杂文档转换为可用数据是一个常见问题。让我们看看一个复杂的文档,并探讨不同的转换数据的方法。

简介;

我们将在开发复杂解析器的过程中探索这些规则:

Rule 1: Be lazy; don’t do any more than is what is needed

Rule 2: Start with the easy parts of the problem.

Rule 3: Don’t be afraid to throw away code and start over!

Rule 4: Use the simplest method possible to get the job done.

问题

作为一家机器学习公司的研究主管,我经常面临各种问题,需要进行探索和解决方案设计。上周出现了一个有趣的小问题:我们需要一种方法来为我们的开源 R SDK生成 markdown 文档,以便机器学习实验记录重要细节。我们需要一个快速的解决方案,而不花费太多时间。

这个问题可能比数据科学家每天遇到的稍微复杂一些,但它将作为一个很好的示例,展示如何使用不同的解析方法。而且作为额外奖励,我们将得到一个填补特定领域的开源项目。让我们深入了解一下吧!

在听到这个问题后,我的第一个研发规则开始起作用:

规则 1:要懒惰;只做必要的事情(懒惰被拉里·沃尔认为是程序员的三大美德之一)。

所以我开始查看将 R 代码转换为 markdown 是否已经解决了。看来确实解决了!然而,在尝试了我能找到的所有可用程序(如 R 的旧版 Rd2md)之后,它们都无法工作,且 git 仓库也不再活跃。好吧,我只能靠自己了。如果我是一名更好的 R 程序员,我可能会尝试修复现有的解决方案。但我更喜欢 Python,认为它是一个很好的解析示例。所以,是的,我们将用 Python 解析 R 文档。

所以,我开始编写一些代码。这让我想起了我的下一个研发规则:

规则 2:从问题的简单部分开始。

规则 2 可能只是我满足需要一些即时反馈的方式。但它也解决了一个更重要的问题:如果你从简单的部分开始,也许困难的部分就不会那么难。它还作为一个热身,开始解决一个问题。我通常在编码解决方案时有一两个错误的开始。这导致了我的下一个规则:

规则 3:不要害怕丢弃代码并重新开始!

最后,当你走在正确的道路上时,最后一个规则是:

规则 4:使用最简单的方法完成工作。

好的,那么将 R 文档文件转换为 markdown 的最简单方法是什么?首先,什么是 R 文档文件?R 文档直接从 R 代码转换为类似于LaTeX的东西。这是一个示例(文件以 .Rd 结尾):

% Generated by roxygen2: do not edit by hand
% Please edit documentation in R/experiment.R
\name{Experiment}
\alias{Experiment}
\title{A Comet Experiment object}
\description{
A comet experiment object can be used to modify or get information about an active
experiment. All methods documented here are the different ways to interact with an
experiment. Use \code{\link[=create_experiment]{create_experiment()}} to create or \code{\link[=get_experiment]{get_experiment()}} to
retrieve a Comet experiment object.
}

目标是将 LaTeX 转换为看起来像这样的 markdown:

## Description

A comet experiment object can be used to modify or get information about an active
experiment. All methods documented here are the different ways to interact with an
experiment. Use `create_experiment()` to create or `get_experiment()` to
retrieve a Comet experiment object.

渲染效果如下:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

示例的 markdown 输出。图片由作者提供。

好吧,我们从一个非常简单的开始。我们将逐行分析 Rd 文件,像这样:

doc = Documentation()
...
for line in lines:
    if line.startswith("%"):
        pass
    elif line.startswith("\\name{"):
        matches = re.search("{(.*)}", line)
        groups = groups()
        name = groups[0]
        doc.set_name(name)
    ...

在这段代码中,我们查看一行是否以“%”开头,如果是,我们就跳过它(它只是 Rd 文件中的注释)。同样,如果它以“\name”开头,那么我们就设置当前文档名称。请注意,如果我们不使用“raw” Python 字符串,则需要转义反斜杠。代码**re.search(“{(.*)}”, line)**假设这一行会包含结束的大括号。在我们的 SDK 中所有示例中,这一假设都是成立的,所以按照规则 3,我不会让这段代码变得更复杂。

请注意,我们在处理文件中的行之前构造了一个Documentation() 实例。我们这样做是为了收集所有部分,然后在最后调用doc.generate()。我们这样做(而不是实时生成 markdown)是因为我们解析的一些项在 markdown 中的顺序可能会不同。

我们可以以完全相同的方式处理一些 R 代码:在 Rd 文件中查找模式,并立即处理它。然而,让我们来看一下下一个无法以这种方式处理的部分:

\usage{
create_experiment(
  experiment_name = NULL,
  project_name = NULL,
  workspace_name = NULL,
  api_key = NULL,
  keep_active = TRUE,
  log_output = TRUE,
  log_error = FALSE,
  log_code = TRUE,
  log_system_details = TRUE,
  log_git_info = FALSE
)
}

使用部分总是以**\usage{** 开头,并以单个 } 结束。由于是这样,我们可以利用这些事实创建一个稍微复杂一点的解析器:

...
for line in lines:
    ....
    elif line.startswith("\\usage{"):
        usage = ""
        line = fp_in.readline().rstrip()
        while line != "}":
            usage += line + "\n"
            line = fp_in.readline().rstrip()
        doc.set_usage(usage)

这将逐行读取,收集**\usage{}**部分中的所有文本。

当我们转到下一个最复杂的部分时,我们必须开始变得有点聪明,并且首次使用“状态”这一概念。

考虑这段 LaTeX 代码:

\item{log_error}{If \code{TRUE}, all output from 'stderr' (which includes errors,
warnings, and messages) will be redirected to the Comet servers to display as message
logs for the experiment. Note that unlike \code{auto_log_output}, if this option is on then
these messages will not be shown in the console and instead they will only be logged
to the Comet experiment. This option is set to \code{FALSE} by default because of this
behavior.}

这很棘手。顶层格式是:

\item{NAME}{DESCRIPTION}

然而,DESCRIPTION 本身可以包含大括号项。如果你有这一段代码作为字符串(即使包含换行),你可以使用 Python 的 re(正则表达式)模块,如下所示:

text = """\item{log_error}{If \code{TRUE}, all output from 'stderr' (which includes errors,
warnings, and messages) will be redirected to the Comet servers to display as message
logs for the experiment. Note that unlike \code{auto_log_output}, if this option is on then
these messages will not be shown in the console and instead they will only be logged
to the Comet experiment. This option is set to \code{FALSE} by default because of this
behavior.}"""

matches = re.search("{(.*)}{(.*)}", text, re.DOTALL)

你可以通过matches.groups() 获取 NAME 和 DESCRIPTION。正则表达式模式**“{(.)}{(.)}”中的括号表示匹配两个组:第一个组位于第一个大括号集之间,第二个组位于下一个大括号集之间。这很好用,前提是text** 只是那个部分。为了能够在不先拆分该部分的情况下进行解析,我们实际上必须逐个字符地解析文本。但这并不难。

这是一个小函数,它将在给定文件指针的情况下获取多个大括号部分(在现代 Python 行话中也称为“类文件”):

def get_curly_contents(number, fp):
    retval = []
    count = 0
    current = ""
    while True:
        char = fp.read(1)
        if char == "}":
            count -= 1
            if count == 0:
                if current.startswith("{"):
                    retval.append(current[1:])
                elif current.startswith("}{"):
                    retval.append(current[2:])
                else:
                    raise Exception("malformed?", current)
                current = ""
        elif char == "{":
            count += 1
        if len(retval) == number:
            return retval
        current += char

在函数**get_curly_contents()**中,你需要传入大括号部分的数量和一个文件指针。因此,要从文件中获取 2 个大括号部分,你可以这样做:

fp = open(FILENAME)
name, description = get_curly_contents(2, fp)

get_curly_contents() 是这个项目中几乎最复杂的部分。它有三个状态变量:retvalcountcurrentretval 是已解析部分的列表。count 是当前大括号项的深度。current 是当前正在处理的内容。这个函数实际上在一些地方非常有用,正如我们将看到的那样。

最后,还有一个更复杂的层面。问题区域是 R 类定义中的Method子章节。这是一个简化的示例:

\if{html}{\out{<hr>}}
\if{html}{\out{<a id="method-Experiment-new"></a>}}
\if{latex}{\out{\hypertarget{method-Experiment-new}{}}}
\subsection{Method \code{new()}}{
Do not call this function directly. Use \code{create_experiment()} or \code{get_experiment()} instead.
\subsection{Usage}{
\if{html}{\out{<div class="r">}}\preformatted{Experiment$new(
  experiment_key,
  project_name = NULL
)}\if{html}{\out{</div>}}
}

\subsection{Arguments}{
\if{html}{\out{<div class="arguments">}}
\describe{
\item{\code{experiment_key}}{The key of the \code{Experiment}.}

\item{\code{project_name}}{The project name (can also be specified using the \code{COMET_PROJECT_NAME}
parameter as an environment variable or in a comet config file).}
}
\if{html}{\out{</div>}}
}
}

这很复杂,因为我们有嵌套的部分:UsageArgumentsMethod 里面。我们将为这次解析拿出全部的解析工具。

为了简化这个过程,我们首先要做的是“分词”Method子章节。这是一个将文本拆分成相关字符串的 fancy 词汇。例如,考虑这个 LaTeX 文本:

\subsection{Usage}{
\if{html}{\out{<div class="r">}}\preformatted{Experiment$new(
  experiment_key,
  project_name = NULL
)}\if{html}{\out{</div>}}
}

它可以被分解成一个字符串列表,如下:

[
 "\\", "subsection", "{", "Usage", "}", "\\", "if",
 "{", "html", "}", "{", "\\", "out", "{", "<", "div", 
 " ",  "class", "=", "\"r\"", ">", "}", "}", "\\", 
 "preformatted", "{", "Experiment$new", "(", "experiment_key",
 "project_name", "=", "NULL", ")", "}", "\\", "if",
 "{", "html", "}", "{", "\\", "out", "{", "<", "/", "div",
 ">", "}", "}", "}"
]

一组分词字符串使你能够轻松地将其处理成子部分。此外,你还可以轻松地“前瞻”一个或多个标记,以查看接下来会发生什么。这对于正则表达式或处理单个字符而非标记时可能很困难。以下是解析分词部分的一个示例:

doc = Documentation()
...
method = Method()
position = 0
preamble = ""
tokens = tokenize(text)
while position < len(tokens):
    token = tokens[position]
    if token == "\\":
        if tokens[position + 1] == "subsection":
            in_preamble = False
            if tokens[position + 3] == "Usage":
                position, usage = get_tokenized_section(
                    position + 5, tokens
                ) 
                method.set_usage(usage)
            elif tokens[position + 3] == "Arguments":
                # skip this, we'll get with describe
                position += 5
            elif tokens[position + 3] == "Examples":
                position, examples = get_tokenized_section(
                    position + 5, tokens
                )
                method.set_examples(examples)
            elif tokens[position + 3] == "Returns":
                position, returns = get_tokenized_section(
                    position + 5, tokens
                ) 
                method.set_returns(returns)
            else:
                raise Exception("unkown subsection:", tokens[position + 3])
        elif tokens[position + 1] == "describe":
            position, describe = get_tokenized_section(position + 2, tokens)  # noqa
            method.set_describe(describe)
        else:
            # \html
            position += 1
    else:
        if in_preamble:
            preamble += token
        position += 1

method.set_preamble(preamble)
doc.add_method(method)

就是这样!要查看完成的项目,请查看新的基于 Python 的 rd2md。它是一个 pip-installable,开源的 Python 库,用于从 R 的 Rd 文件生成 markdown。我们在这里使用了它的 R 文档:

www.comet.com/docs/v2/api-and-sdk/r-sdk/overview/

这是一个下午的临时小项目吗?是的。它由不低于 4 种不同的解析方法组成。但它能完成任务,而且据我所知,它是唯一有效的 Rd 到 markdown 转换器。如果我要重构它,我可能会先分词整个文件,然后使用上面展示的最后一种方法进行处理。记住规则 3:不要害怕丢弃代码并重新开始!

如果你想为 GitHub 仓库做贡献,请随意。如果你有问题,请在 Issues 中告诉我们。

对人工智能、机器学习和数据科学感兴趣?考虑点赞和关注一下吧。Doug 是 comet.com的研究主管,该公司专注于机器学习实验跟踪和模型监控。

GPT-4 的去污染评估

原文:towardsdatascience.com/the-decontaminated-evaluation-of-gpt-4-38a27fc45c30?source=collection_archive---------4-----------------------#2023-03-27

GPT-4 不会很快成为你的律师

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传 本杰明·玛丽

·

关注 发表在 数据科学前沿 ·7 分钟阅读·2023 年 3 月 27 日

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图片来源:Pixabay

GPT-4 在三月由 OpenAI 宣布,伴随令人印象深刻的展示和突出的声明。

这些声明大多数来自他们对 GPT-4 的自我评估。

OpenAI 使用了许多现有的专业和学术考试进行评估。

但在公共基准测试中评估大型语言模型是极具挑战性的。

像 GPT-4 这样的模型可能会受到“数据污染”,即它们可能在评估数据上进行了训练。

为什么这是个问题?

让我们举个例子。

GPT-4 在 LSAT 考试中进行了评估。为了进行科学可信的评估,OpenAI 必须检查用于评估的 LSAT 问题是否不在 GPT-4 的训练数据中。如果在其中,GPT-4 可能已经记住了这些问题,那么在评估时显然会在这些特定问题上表现更好。

就像一个在考试前已经知道考试题目的人。

你可以说这就像是作弊。

GPT-4 技术报告中,OpenAI 透露了关于 GPT-4 的少数几件事,其中之一是他们评估中的数据污染。他们公开了量化和评估这种污染的策略,并从观察中得出了几个结论。

在这篇文章中,我回顾和讨论了 OpenAI 如何处理 GPT-4 的数据污染。我揭示了他们方法中的几个陷阱。

我无法同意他们的几个结论。

评估数据的去污染

为了检查训练数据和评估数据之间是否存在交集,OpenAI 使用了一种依赖于子字符串匹配算法的非常简单的技术(在技术报告第 28 页描述)。

首先,他们删除了训练数据和评估数据(考试)中的所有空格和符号。他们保留了数字。

然后,他们随机挑选了考试中每个问题的 3 个 50 字符的子字符串(或等效的)。如果这些子字符串中的任何一个恰好出现在 GPT-4 的训练数据中,则该问题会从评估数据中删除。

使用这种方法,他们做出了两个关键选择。

首先,这种方法是随机的。

对于问题非常长的考试来说,选择 3 个随机子字符串尤其具有问题。

例如,统一律师资格考试中的一个问题可能包含 1,500 个 50 字符的序列。注意:它们是非常长的问题, 查看一些示例

在 1,500 个子字符串中随机选择 3 个,这意味着该去污染策略完全忽略了每个问题的大部分内容。

这种策略无法可靠地检测出问题的大部分是否存在于训练数据中。

我们可以想象这些考试问题中的一些可能已经在 GPT-4 训练数据中被研究或讨论过,但只是部分而非全部,因为它们是非常长的问题。因此,在这种情况下,部分但重要的匹配将不会被检测到。

统一律师资格考试有 400 道题目。但是通过随机检查每个问题的 3 个子字符串,OpenAI 并未发现这些问题出现在训练数据中。

第二个关键选择是他们去污染了评估数据,而不是训练数据。

从训练数据中删除问题,重新训练 GPT-4,然后再在考试中评估它显然会花费过高。

然而,如果他们在开发过程中早些时候评估了这种污染,即在训练之前,他们本可以从训练数据中移除所有的考试示例。

还要注意,他们在去污染过程中没有包括 RLHF 的数据。如果一个考试的问题在 RLHF 中,它将保留在评估数据中。

定义

RLHF 代表来自人类反馈的强化学习。GPT-4 在预训练后,通过在人工反馈上进行强化学习进一步微调,以提高其性能。这个“反馈”数据集没有被检查以进行去污染。

不包括 RLHF 训练数据的主要原因是利用 RLHF 进行微调并未显著提高 GPT-4 的性能。它们仅观察到在 RLHF 后训练的平均得分提高了+0.3%。

它是被污染的

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图片来自 Pixabay

每个考试的污染细节在报告的第 30 页中给出。

在用于评估的 49 个考试中,发现有 12 个完全没有出现在训练数据中。它们是:所有 Leetcode 数据集、Uniform Bar Exam、SAT EBRW 考试和一些 AP 考试。

总体而言,用于评估的考试包含 4,123 个问题。其中 545**.5**个问题已在训练数据中找到。注意:为什么会有“.5*”?据我了解,如果有匹配,OpenAI 会完全移除该问题。但对于考试“USA Biolympiad Semifinal Exam 2020”,包含 150 个问题,他们指出他们移除了 3.00%的问题(见论文的表 10)。150 的 3%是 4.5。这些数字中的一个可能是错误的。*

这是 13.2%的评估数据被污染。

有趣的是,对于几个考试,去污染似乎改善了 GPT-4 的结果。

这与直觉相反。

我们可能认为,如果被移除的问题在训练数据中,GPT-4 应该擅长回答它们,因为它有机会记住它们。

但我们对这些被排除的问题一无所知。

对于一些考试来说,它们可能是最困难的,因此在从评估中排除这些问题后正确答案的百分比更高。

OpenAI 声称污染没有显著影响。他们指出:

总体来看,大多数考试中,污染和视力的影响相对较小。 (表 9 的标题)

退化通常很小,正面和负面效果一样多 […] (表 10 的标题)

这是“总体”结论。如果我们更仔细地查看结果,这并不那么明显。让我们看看一些细节。

在技术报告的表 10 中,OpenAI 还在每个考试中对两个独立的问题集合评估了 GPT-4:

  • “contaminated”:这个集合仅包含在训练数据中找到的问题。

  • “non-contaminated”:这个集合包含了所有剩余的问题。

这是一个有趣的实验。GPT-4 在这两种数据集(第 5 和第 6 列)上的表现对某些考试变化极大,例如 AMC 12 的表现从 41.67%到 0%。

对于其他一些考试,GPT-4 在没有使用的评估数据(未受污染)上的表现更好。

这是否意味着 GPT-4 在训练期间未见过的问题上表现更好?

不,“受污染”和“未受污染”只是两种不同的评估数据。

GPT-4 可能因多种原因在两个数据集中的表现不同,例如问题的主题、长度、难度等。

GPT-4 对这些考试表现如何?

让我们具体看看 LSAT 考试。假设 160 以上的分数在此考试中是一个好分数。

GPT-4 获得了 163 的分数。在去污染后,移除 39%的问题,GPT-4 获得了更高的 167 分。

我们可以得出结论,GPT-4 能在 LSAT 考试中取得好分数吗?

是的,我们可以。但前提是允许作弊。

一方面,我们有完整的考试,GPT-4 的分数为 163。这是一个好分数,但 GPT-4 在考试前见过一些问题。

另一方面,如果我们移除 39%的问题进行去污染,这就不再是 LSAT 考试了。没有人能通过 61%的 LSAT。这种考试并不存在。

此外,移除的 39% 问题可能包含最难的问题。我们不知道在这 61%的 LSAT 中,167 的分数好坏如何。

对于所有其他用于评估的“受污染”考试,我们可以类似地推理。

一些考试没有“受污染”,例如统一律师资格考试和 Leet 代码问题,但还有其他问题。

我不会在这里讨论这些问题。Arvind NarayananSayash Kapoor 已经在他们的权威文章中讨论了这些问题的结果,你可以在这里阅读:

[## GPT-4 和专业基准:错误的问题的错误答案

我们不知道答案,但我们希望能将一些现实注入对话中。OpenAI 可能违反了…

aisnakeoil.substack.com](https://aisnakeoil.substack.com/p/gpt-4-and-professional-benchmarks?source=post_page-----38a27fc45c30--------------------------------)

结论

正如我在介绍中写的,评估大型语言模型的数据污染是一个极其困难的任务。

在收集和预处理训练数据时,理想情况下我们应该已经确定了一份需要从训练数据中排除的公共相关考试和基准的清单。

尽管如此,我的观点是,OpenAI 训练 GPT-4 时包含所有这些考试实际上是非常有意义的。

目标也是让 GPT-4 尽可能地适应这些考试提出的问题。我可以看到 GPT-4 在这个领域的许多潜在应用,比如帮助学生和老师准备考试。

然而,这个选择是有代价的:我们不能用这些考试来以科学的可信度评估 GPT-4。

如果你喜欢这篇文章并且有兴趣阅读接下来的文章,支持我的工作的最佳方式是通过这个链接成为 Medium 会员:

[## 通过我的推荐链接加入 Medium - Benjamin Marie

阅读 Benjamin Marie 的每一个故事(以及 Medium 上的其他成千上万的作家)。你的会员费直接支持…

medium.com](https://medium.com/@bnjmn_marie/membership?source=post_page-----38a27fc45c30--------------------------------)

如果你已经是会员并且想要支持这项工作, 只需在 Medium 上关注我

从基础逻辑门到深度神经网络:权威感知机教程

原文:towardsdatascience.com/the-definitive-perceptron-guide-fd384eb93382

朝着掌握 AI 的方向前进

数学、二分类、逻辑门等

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传 Joseph Robinson, Ph.D.

·发布于 Towards Data Science ·阅读时间 21 分钟·2023 年 4 月 28 日

TL;DR

感知机的世界令人着迷,因为这些模型是现代人工智能的基础。在这篇博客文章中,我们将简明扼要地讲述感知机的故事,从它的神经网络起源到其演变为多层感知机及更高级的模型。我们将探讨驱动这个模型的基本数学,使其能够作为二分类器、模拟计算机晶体管、乘法器和逻辑门。此外,我们还将考察感知机模型如何为更高级的分类器奠定基础,包括逻辑回归、支持向量机和深度学习。我们将提供示例代码片段和插图以增强理解。此外,我们还将使用实际案例来了解何时以及如何使用感知机模型。

本指南是任何对数据科学感兴趣的人的宝贵资源,无论其专业水平如何。我们将探讨感知机模型,这一模型自人工智能早期便存在,并且至今依然相关。我们将深入了解其历史、工作原理及与其他模型的比较。此外,我们还将构建模型和逻辑门,并提供对未来发展的见解。无论你是自学的数据科学家、AI 从业者还是有经验的机器学习专业人士,你都会在这本全面的指南中找到有价值的内容。

目录

1. 介绍

1.1 感知机模型的简史

1.2. 感知机模型在机器学习中的重要性

2. 感知机模型背后的数学

2.1. 线性可分性

2.2. 感知机学习算法

2.3. 感知机收敛定理

3. 感知机模型作为二分类器

3.1. 线性分类

3.2. 感知器模型的局限性

3.3. 感知器模型的多类分类

4. 逻辑门与感知器模型

4.1. 感知器如何用于生成逻辑门

4.2. 示例:使用感知器实现 NAND 门

4.3. 扩展到其他逻辑门:AND、OR、XOR

5. 感知器用于乘法和晶体管类似功能

5.1. 感知器与晶体管的类比

5.2. 使用感知器进行乘法

5.3. 感知器的未来与硬件实现

6. 比较感知器模型与逻辑回归

6.1. 感知器与逻辑回归的相似性

6.2. 感知器与逻辑回归的差异

6.3. 在感知器与逻辑回归之间的选择

7. 感知器模型的创意与独特应用

7.1. 光学字符识别(OCR)

7.2. 音乐类型分类

7.3. 入侵检测系统

7.4. 情感分析

8. 感知器模型的演变及其在深度学习中的遗产

8.1. 感知器到多层感知器(MLPs)的演变

8.2. 深度学习与感知器的遗产

8.3. 感知器与深度学习的未来

9. 结论

· 参考文献

· 联系方式

1. 介绍

1.1 感知器模型的简史

Warren McCulloch 和 Walter Pitts 在 1943 年的人工神经元研究[1]启发了一位名叫 Frank Rosenblatt 的心理学家在 1957 年制造了感知器模型[2]。Rosenblatt 的感知器是第一个用算法描述的神经网络(NN),为现代机器学习(ML)技术铺平了道路。发现后,感知器受到了科学家和公众的广泛关注。有些人认为这一新技术对智能机器至关重要——一个学习和改变的模型[3]。

然而,感知器的受欢迎程度并没有持续。然后,在 1969 年,Marvin Minsky 和 Seymour Papert 出版了他们的书《感知器》,书中强调了感知器模型的局限性,同时揭示了它无法解决像 XOR 分类这样的难题[4](第三部分)。这项工作引发了对神经网络的重大兴趣丧失,转而关注其他方法。感知器的早期历程列于图 1

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 1. 感知器历史上的重要里程碑(1943–1982)。图由作者创作。

虽然花费了十多年时间,但 1980 年代对神经网络的兴趣得以重新点燃。部分感谢于 Rumelhart、Hinton 和 Williams 通过反向传播算法引入的多层神经网络训练[5](第五部分)。

2012 年,计算能力、大数据、RELU 等非线性激活以及 dropout 技术的重大进展促成了最全面的卷积神经网络的诞生。ImageNet 提供的大型标注数据集在填充其容量方面发挥了重要作用。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 2. 感知机历史上的重要里程碑(1985–1997)。图由作者绘制。

今天对深度学习的狂热由此兴起。因此,感知机模型在其基础中扮演了关键角色—图 2图 3列出了剩余的里程碑(图 1的延续)。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 3. 感知机历史上的重要里程碑(2006–2018)。图由作者绘制。

1.2. 感知机模型在机器学习中的重要性

尽管有其局限性,感知机模型仍然是机器学习中的一个重要构建块。它是人工神经网络的基础组成部分,现在这些网络被广泛用于各种应用,从图像识别到理解人类语言。

感知机模型的简洁性使其成为新手入门机器学习的绝佳起点。它使线性分类和从数据中学习变得易于理解。此外,感知机算法可以很容易地修改以创建更复杂的模型,例如多层感知机(MLP)和支持向量机(SVM),这些模型可以在更多情况下使用,并解决许多原始感知机模型无法解决的问题。

在接下来的部分,我们将介绍感知机模型背后的数学,如何将其用作二分类器和构建逻辑门,以及如何用于执行类似计算机晶体管的乘法任务。我们还将讨论感知机模型与逻辑回归之间的区别,并展示感知机模型如何以新颖和令人兴奋的方式使用。

2. 感知机模型背后的数学

2.1. 线性可分性

从本质上讲,感知机模型是一个线性分类器。它旨在找到一个“超平面”(二维空间中的一条线、三维空间中的一个平面,或更高维度的类似物)来分隔两个数据类别。为了使数据集具有线性可分性,超平面必须正确地分类所有数据点[6]。

从数学上讲,感知机模型可以表示如下:

y = f(w * x + b)

x是输入向量;w是权重向量;b是偏置项;f是激活函数。在感知机的情况下,激活函数是一个阶跃函数,将输出映射为 1 或 0,表示两个类别(图 4)。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 4. 单位阶跃函数的描述,包含将输出映射为 0 或 1 的分段条件。图由作者绘制。

感知机模型可以扩展到具有多个输入特征x,定义如下:

y = f(w_1 * x_1 + w_1 * x_1 ... w_n * x_n + b)

上述方程及其输出的阶跃函数被激活(即,通过 0 关闭或通过 1 打开),如下图所示,图 5

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 5. 多变量线性分类。注意加权和通过激活函数,上述阶跃函数——来源 link

2.2. 感知器学习算法

感知器学习算法是一种保持权重和偏置最新以减少分类错误的方法[2]。该算法可以总结如下:

  1. 将权重和偏置初始化为小的随机值。

  2. 对于每对输入输出(x, d),计算预测输出y = f(w * x + b)

  3. 根据误差e = d - y更新权重和偏置:

w = w + η * e * x

b = b + η * e,

其中η是学习率,一个小的正数,控制更新的步长。

  1. 对固定次数的迭代或直到误差收敛,重复步骤 2 和 3。

我们可以使用 Python 和 Sklearn 快速实现上述步骤:

import numpy as np
from sklearn.linear_model import Perceptron

X = np.array([2, 3], [1, 4], [4, 1], [3, 2])
y = np.array([1, 1, 0, 0])

perceptron = Perceptron()
perceptron.fit(X, y)

然后,使用拟合的模型,我们可以进行如下预测:

new_data_point = np.array([[1, 2]])
prediction = perceptron.predict(new_data_point)
print(prediction)

如果数据是线性可分的,[7]中的感知器学习算法保证收敛。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 6. 布尔分类,其中类别是线性可分的。图像由作者创建。

2.3. 感知器收敛定理

1960 年,Rosenblatt 证明了感知器收敛定理。该定理指出,如果数据集可以线性分隔,感知器学习算法将在有限的步骤内找到解决方案[8]。该定理表明,只要时间足够,感知器模型将找到最佳的权重和偏置,以对所有数据点进行线性分隔的分类。

但如果数据集不是线性可分的,感知器学习算法可能找不到合适的解决方案或收敛。因此,研究人员开发了更复杂的算法,如多层感知器和支持向量机,这些算法可以处理不能直线分隔的数据[9]。

3. 感知器模型作为二分类器

3.1. 线性分类

如前所述,感知器模型是一种线性分类器。它创建了一个决策边界,这是一个特征空间中的直线,用于分隔两个类别[6]。当添加新数据点时,感知器模型根据其在决策边界上的位置对其进行排序。感知器运行快速且易于使用,因为它简单,但只能解决数据可以线性分隔的问题。

3.2. 感知器模型的局限性

感知器模型的一个大问题是它不能处理无法用直线分隔的数据。异或问题是一些数据集无法通过单个超平面分隔的例子,这使得感知器无法找到解决方案[4]。研究人员开发了更高级的方法来绕过这个问题,例如多层感知器,它们有多个神经网络层,能够学习进行不沿直线的决策[5]。

感知器模型对学习率和初始权重的设置也很敏感。例如,如果学习率过低,收敛可能会很慢,而较大的学习率可能会导致振荡或发散。同样,初始权重的选择会影响解决方案的收敛速度以及最终效果[10]。

3.3. 感知器模型的多类分类

尽管基本的感知器模型是为两类问题设计的,但通过训练多个感知器分类器(每个类别一个),它可以解决多于两类的问题[11]。最常见的方法是“一对多(OvA)”,其中训练一个单独的感知器来区分各类。然后,在分类新数据点时,选择输出值最高的感知器作为预测类别。

另一种方法是“一对一(OvO)”方法,其中对每对类别训练一个感知器。最终的分类决策是通过投票机制做出的,每个感知器对其预测的类别进行投票,票数最多的类别被选择。虽然 OvO 需要训练比 OvA 更多的分类器,但每个感知器只需要处理数据的一个较小子集,这对大型数据集或高计算复杂度的问题可能更有利。

4. 逻辑门与感知器模型

4.1. 感知器如何用于生成逻辑门

感知器模型可以用来表示逻辑门,这些逻辑门是数字电路的最基本组成部分。通过适当调整感知器的权重和偏置,它可以被训练执行逻辑操作,如与(AND)、或(OR)和非(NOT)[12]。感知器与逻辑门之间的联系表明,神经网络不仅可以进行计算,还具有模拟复杂系统的潜力。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 6. 线性可分逻辑门:与(AND)或(OR)(分别为左侧和中间)。另一方面,异或(XOR) 不能通过单一线性分类器(右侧)进行分离,但可以通过两层网络进行分离(稍后会详细介绍)——该图由作者创建。

4.2. 示例:使用感知器实现 NAND 门

NAND 门是一个基本的逻辑门,只有当两个输入都为 1 时,输出才为 0,在其他情况下输出为 1。NAND 门的真值表如下:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

NAND 门真值表。表格由作者创建。

要使用感知器实现 NAND 门,我们可以手动设置权重和偏置,或使用感知器学习算法来训练感知器。以下是可能的权重和偏置配置:

w1 = -1

w2 = -1

b = 1.5

使用这些参数,感知器可以表示为:

y = f((-1 * A) + (-1 * B) + 1.5)

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 6. 训练数据、图形表示以及 AND 门的线性函数。图由作者创建。

在这里,f 是步进函数,AB 是输入。如果使用真值表中的值测试此设置,你将从 NAND 门获得正确的结果:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 7. 逻辑 NAND 的真值表,以及上述训练的感知器的输出和作者创建的结果。

在 Python 中,NAND 可以如下实现:

def nand_gate(x1, x2):
    w1, w2, b = -1, -1, 1.5
return int(w1 * x1 + w2 * x2 + b > 0)

binary_inputs = [(0,0), (0,1), (1,0), (1,1)]
for A and B in binary_inputs:
print(f"(A, B) --> nand_gate(A, B)")

正如预期的那样,重现上述总结 NAND 门的表格

(0, 0) --> 1
(0, 1) --> 1
(1, 0) --> 1
(1, 1) --> 0

NAND 门可以用于构建所有其他门,因为它在功能上是完整的,这意味着任何其他逻辑函数都可以仅使用 NAND 门来推导。以下是如何使用 NAND 门创建一些基本门的简要说明:

  1. NOT 门:将 NAND 门的两个输入连接到输入值。

  2. AND 门:首先创建一个 NAND 门,然后将输出通过一个 NOT 门。

  3. OR 门:在将每个输入馈送到 NAND 门之前,对每个输入应用 NOT 门。

要创建一个接受任意数量输入的 NAND 门,可以使用 Python 定义一个函数,该函数接受一个输入列表并返回 NAND 输出。以下是演示此操作的代码片段:

def nand_gate(inputs):
assert len(inputs) > 1, "At least two inputs are required."

# Helper function to create a 2-input AND gate
def and_gate (x1, x2):
    w1, w2, b = 1, 1, -1.5
return int(w1 * x1 + w2 * x2 + b > 0)

Reduce the inputs to a single NAND output using the helper function.
result = and_gate(inputs[0], inputs[1])
for i in range (2, len (inputs)):
result = and_gate(result, inputs[i])
  return 0 if result > 0 else 1

# Example usage
inputs = [(0, 0, 0, 0),
          (0, 0, 0, 1),
          (0, 0, 1, 0),
          (0, 0, 1, 1),
          (0, 1, 0, 0),
          (0, 1, 0, 1),
          (0, 1, 1, 1),
          (1, 0, 0, 0),
          (1, 0, 0, 1),
          (1, 0, 1, 0),
          (1, 0, 1, 1),
          (1, 1, 0, 0),
          (1, 1, 0, 1),
          (1, 1, 1, 0),
          (1, 1, 1, 1)]

for A0, A1, A2, and A3 inputs:
  output = nand_gate((A0, A1, A2, A3))
  print(f"({A0}, {A1}, {A2}, {A3}) --> {output}")

该函数使用一个辅助函数(即and_gate)来创建一个具有两个或更多输入的 NAND 门。然后在给定的输入上重复 AND 操作。最终结果是 NAND 门的输出,具有任意数量的输入位,即 AND 门的取反值。

4.3. 扩展到其他逻辑门:AND、OR、XOR

类似地,感知器可以建模其他逻辑门,如 AND、OR 和 NOT。例如,具有权重 w1 = 1w2 = 1b = -1.5 的感知器可以表示一个 AND 门。

def and_gate(x1, x2):
    w1, w2, b = 1, 1, -1.5
    return int(w1 * x1 + w2 * x2 + b > 0)

binary_inputs = [(0,0), (0,1), (1,0), (1,1)]
for A, B in binary_inputs:
  print(f"({A}, {B}) --> {and_gate(A, B)}")

再次,输出模仿预期的 AND 门。

(0, 0) --> 0
(0, 1) --> 0
(1, 0) --> 0
(1, 1) --> 1

然而,单个感知器无法建模 XOR 门,因为 XOR 门不是线性可分的。相反,必须使用多层感知器或感知器组合来解决 XOR 问题[5]。

5. 用于乘法和类似晶体管功能的感知器

5.1. 感知器与晶体管之间的类比

晶体管是电子设备的基本构建块。它们负责执行像加法和乘法这样的简单任务。有趣的是,感知器也可以被视为展示类似功能的计算单元。例如,感知器在机器学习和人工神经元中被使用。相比之下,晶体管是物理部件,改变电信号的流动 [13]。尽管如此,正如上一节所示,这两种系统都可以建模和执行逻辑运算。

5.2. 使用感知器进行乘法运算

我们可以利用感知器的二进制运算能力来执行乘法。例如,考虑两个二进制位(即AB)的展开,可以将其表示为一个简单的 AND 门。正如第四部分所示,AND 门可以用感知器建模。

但是,对于涉及多个二进制位的更复杂的乘法任务,我们需要添加更多的部件,如半加器和全加器,这些部件需要逻辑门的组合 [14]。使用感知器来构建这些部件使得构建可以执行二进制乘法的人工神经网络成为可能。

例如,假设我们要乘以两个 2 位的二进制数,A1A0B1B0。那么,我们可以将乘法分解为一系列的 AND 运算和加法:

  1. 计算部分乘积:P00 = A0 * B0P01 = A0 * B1P10 = A1 * B0P11 = A1 * B1

  2. 使用半加器和全加器将部分乘积相加,得到一个 4 位的二进制乘积。

每个 AND 运算和加法都可以通过感知器或表示所需逻辑门的感知器组来完成。

使用上一节中设置的 AND 门函数,我们可以在 Python 中实现基于感知器的乘法:

A1A0 = [1, 0]
B1B0 = [1, 1]

P00 = and_gate(A1A0[1], B1B0[1])
P01 = and_gate(A1A0[1], B1B0[0])
P10 = and_gate(A1A0[0], B1B0[1])
P11 = and_gate(A1A0[0], B1B0[0])

# Implement a simple adder using perceptron-based logic gates
result = [P00, P01 ^ P10, (P01 & P10) ^ P11, P11]
print(result)

5.3. 感知器及其硬件实现的未来

尽管感知器可以像晶体管一样执行基本的数学运算,但其硬件实现的效率不如传统晶体管。然而,近期在神经形态计算方面的改进表明,可能有办法制造类似于神经网络的硬件,如感知器 [15]。这些神经形态芯片可能有助于机器学习任务减少能源消耗,并开启对计算机新思维方式的探索。

6. 感知器模型与逻辑回归的比较

6.1. 感知器与逻辑回归的相似性

感知器模型和逻辑回归都是线性分类器,可以用来解决二分类问题。它们都依赖于找到一个决策边界(一个超平面),以在特征空间中分隔不同的类别 [6]。此外,它们还可以通过一对多和一对一等技术扩展以处理多分类问题 [11]。

让我们来看一下 Python 实现的区别:

from sklearn.linear_model import LogisticRegression

log_reg = LogisticRegression()
log_reg.fit(X, y)

new_data_point = np.array([[1, 2]])
prob_prediction = log_reg.predict_proba(new_data_point)
print(prob_prediction)
import numpy as np
from sklearn.linear_model import Perceptron, LogisticRegression

# Dataset
X = np.array([[2, 3], [1, 4], [4, 1], [3, 2]])
y = np.array([1, 1, 0, 0])

# Train Perceptron
perceptron = Perceptron()
perceptron.fit(X, y)

# Train Logistic Regression
log_reg = LogisticRegression()
log_reg.fit(X, y)

# New data point
new_data_point = np.array([[1, 2]])

# Perceptron prediction
perc_prediction = perceptron.predict(new_data_point)
print("Perceptron prediction:", perc_prediction)

# Logistic Regression prediction
log_reg_prediction = log_reg.predict(new_data_point)
print("Logistic Regression prediction:", log_reg_prediction)

# Logistic Regression probability prediction
prob_prediction = log_reg.predict_proba(new_data_point)
print("Logistic Regression probability prediction:", prob_prediction)

这将输出:

Perceptron prediction: [1]
Logistic Regression prediction: [1]
Logistic Regression probability prediction: [[0.33610873 0.66389127]]

6.2. 感知器与逻辑回归的区别

尽管感知器模型和逻辑回归有一些相似之处,但两者之间存在一些本质区别:

  1. 激活函数:感知器模型使用阶跃函数作为其激活函数,而逻辑回归使用逻辑(Sigmoid)函数[10]。这种差异导致感知器具有二元输出(01)。与此同时,逻辑回归生成一个概率值(介于 0 和 1 之间),表示实例属于特定类别的可能性。

  2. 损失函数:感知器学习算法最小化误分类错误,而逻辑回归最小化对数似然或交叉熵损失[16]。这种区别使逻辑回归对数据集中的噪声和异常值更具鲁棒性,因为它考虑了错误的幅度,而不仅仅是误分类实例的数量。

  3. 收敛性:感知器学习算法在数据线性可分时可以收敛,但在其他情况下可能无法收敛[7]。另一方面,逻辑回归使用基于梯度的优化技术,如梯度下降或牛顿-拉夫森方法,这些方法能够保证在对数似然等凸损失函数中找到全局最优解[17]。

  4. 非线性可分数据:尽管感知器模型在处理非线性可分数据时会遇到困难,但逻辑回归可以通过引入高阶多项式特征或使用核方法来扩展处理非线性决策边界[18]。

6.3. 选择感知器与逻辑回归

感知器模型和逻辑回归的选择取决于问题和数据集。逻辑回归更可靠,可以处理更广泛的问题,因为它基于概率并且可以建模非线性决策边界。然而,在某些情况下,特别是处理可以线性分离的数据时,感知器模型可能更易于使用且计算资源消耗更少。

7. 感知器模型的创造性和独特应用

7.1. 光学字符识别(OCR)

感知器模型已被应用于光学字符识别(OCR)任务,其目标是识别并将打印或手写文本转换为机器编码文本[19]。感知器或其他机器学习算法通常用于 OCR 任务,以预处理将被读取的图像,从中提取特征并进行分类。感知器模型对于字符可以通过直线分离的 OCR 任务是一个不错的选择,因为它易于使用且与计算机配合良好。

7.2. 音乐类型分类

感知机也可以用于音乐流派分类,这涉及识别给定音频轨迹的流派。可以训练感知机模型将音频分类为已设置的流派 [20]。这通过提取音频信号的相关部分,如频谱特征或时间特征,然后将其组合起来来完成。尽管深度学习和卷积神经网络等更先进的方法通常能提供更好的结果,但感知机模型仍能很好地工作,特别是在只有少数几个流派或特征可以线性分离的情况下。

7.3. 入侵检测系统

入侵检测系统,或称为 IDS,广泛用于网络安全中,以查找恶意行为或未经授权的访问计算机网络。IDS 可以使用感知机作为分类器,通过查看数据包大小、协议类型和网络流量连接长度来确定活动是常规的还是恶意的 [21]。支持向量机和深度学习可能更擅长检测,但感知机模型可以用于简单的 IDS 任务或作为比较点。

7.4. 情感分析

感知机可以应用于情感分析,这是一个自然语言处理任务,旨在确定文本中表达的情感(例如,正面、负面或中性)。通过将文本转换为数值特征向量,如词频-逆文档频率(TF-IDF)表示 [22],可以训练感知机模型根据其语气分类文本。尽管更先进的技术如递归神经网络或变换器在情感分析性能上已经超越了感知机,但感知机仍然可以作为文本分类的入门方法或特定用例的简单替代方案。

8. 感知机模型的演变及其在深度学习中的遗产

8.1. 感知机到多层感知机(MLPs)的演变

感知机模型已经能够解决具有明确决策边界的问题,但在需要明确决策边界的任务中仍存在困难。多层感知机(MLPs)的引入,包含多个感知机样单位的层,标志着人工神经网络的显著进步 [5]。MLPs 可以逼近任何连续函数,只要具有足够数量的隐藏层和神经元 [23]。通过采用反向传播算法,MLPs 可以训练以解决更复杂的任务,例如 XOR 问题,这是单个感知机无法解决的。

8.2. 深度学习与感知机的遗产

感知机模型奠定了深度学习的基础,深度学习是机器学习的一个子领域,专注于具有多层(深度神经网络)的神经网络。感知机模型是卷积神经网络(CNNs)和递归神经网络(RNNs)等深度学习技术的基础,这些技术在图像分类、自然语言处理和语音识别等任务中达到了最先进的性能 [24]。

在 CNNs 中,来自感知器的加权输入信号和激活函数的思想被传递到卷积层。这些层通过对输入区域应用滤波器来学习数据中的空间层次结构。同样,RNNs 通过添加递归连接来建立在感知器模型的基础上。这使得网络能够学习序列数据中的时间依赖关系 [25]。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

深度学习与其他模型的对比:Google 趋势随时间变化。图片由作者根据 Carrie Fowle’s TDS Medium 博客 (link) 制作。

8.3. 感知器与深度学习的未来

尽管基础,更多复杂的深度学习技术已经主要取代了感知器模型。然而,它仍然对机器学习有价值,因为它是一种简单而有效的方式来教授神经网络的基础知识,并为构建更复杂的模型提供了思路。随着深度学习的不断进步,感知器模型的核心思想和原则可能仍会保持不变,并影响新架构和算法的设计。

9. 结论

本博客全面探讨了感知器模型及其数学、二元分类和逻辑门生成应用。通过理解这些基础知识,我们已经解锁了在各种实际应用中利用感知器的潜力,甚至可以构建更高级的模型,如多层感知器(MLPs)和卷积神经网络(CNNs)。

我们还比较了感知器和逻辑回归,通过考察感知器作为机器学习中更高级技术的基础,突出了它们的异同。我们进一步探讨了感知器在人工智能中的作用、历史意义和持续影响。

请记住,感知器只是其中的一部分。还有无数其他模型和技术,无论是已被发现的还是待发现的,每个都有独特的优点和应用。尽管如此,借助本教程提供的坚实基础,你已准备好迎接人工智能领域中的挑战和机遇。

我希望本博客既引人入胜又富有启发性,鼓励你继续学习和尝试感知器模型及其他相关内容。拥抱你获得的新知识,让你的创造力和好奇心引导你探索人工智能和机器学习的精彩世界。请在下方分享你的想法和评论!

参考文献

[1] McCulloch, W.S., & Pitts, W. (1943). 神经活动中固有思想的逻辑演算。数学生物物理学公报,5,115–133。

[2] Rosenblatt, F. (1958). 感知器是脑中信息存储和组织的概率模型。心理学评论,65(6),386–408。

[3] 纽约时报 (1958 年 7 月 8 日). 一种新型海军设备通过实践学习. 纽约时报.

[4] Minsky, M. 和 Papert, S. (1969). 感知器:计算几何入门, MIT Press.

[5] Rumelhart, D. E., Hinton, G. E., 和 Williams, R. J. (1986). 通过反向传播错误学习表示. Nature, 323 (6088), 533–536.

[6] Duda, R. O., Hart, P. E., 和 Stork, D. G. (2001). 模式分类 (第 2 版). Wiley.

[7] Novikoff, A. B. (1962), 关于感知器收敛证明. 自动机数学理论研讨会, 12, 615–622.

[8] Rosenblatt, F. (1960). 感知器:认知系统中统计可分离性的理论 (项目 PARA 报告 60–3777). 康奈尔航空实验室.

[9] Cortes, C. 和 Vapnik, V. (1995). 支持向量网络. 机器学习, 20(3), 273–297.

[10] Bishop, C. M. (2006). 模式识别与机器学习, Springer.

[11] Rifkin, R. 和 Klautau, A. (2004). 为一对多分类辩护. 机器学习研究期刊, 5, 101–141.

[12] Minsky, M. L. (1961). 迈向人工智能的步骤. IRE 会议录, 49(1), 8–30.

[13] Horowitz, P. 和 Hill, W. (1989). 电子艺术 (第 2 版). 剑桥大学出版社.

[14] Mano, M. M. 和 Ciletti, M. D. (2007). 数字设计 (第 4 版). Prentice Hall.

[15] Merolla, P. A., Arthur, J. V., Alvarez-Icaza, R., Cassidy, A. S., Sawada, J., Akopyan, F.,… 和 Modha, D. S. (2014). 百万尖峰神经元集成电路与可扩展通信网络和接口. Science, 345 (6197), 668–673.

[16] Hastie, T., Tibshirani, R., 和 Friedman, J. (2009). 统计学习的元素:数据挖掘、推断与预测 (第 2 版). Springer.

[17] Nocedal, J. 和 Wright, S. (2006). 数值优化 (第 2 版). Springer.

[18] Schölkopf, B. 和 Smola, A. J. (2002). 使用核的学习:支持向量机、正则化、优化及其他. MIT Press.

[19] LeCun, Y., Boser, B., Denker, J. S., Henderson, D., Howard, R. E., Hubbard, W., 和 Jackel, L. D. (1989). 反向传播应用于手写邮政编码识别. 神经计算, 1(4), 541–551.

[20] Tzanetakis, G. 和 Cook, P. (2002). 音频信号的音乐类型分类. IEEE 语音与音频处理汇刊, 10(5), 293–302.

[21] Garcia-Teodoro, P., Diaz-Verdejo, J., Maciá-Fernández, G., 和 Vázquez, E. (2009). 基于异常的网络入侵检测:技术、系统和挑战. 计算机与安全, 28 (1–2), 18–28.

[22] Pang, B., Lee, L., 和 Vaithyanathan, S. (2002). 竖起大拇指?使用机器学习技术进行情感分类. ACL-02 自然语言处理经验方法会议论文集, 10, 79–86.

[23] Hornik, K., Stinchcombe, M., 和 White, H. (1989). 多层前馈网络是通用逼近器. 神经网络, 2(5), 359–366.

[24] LeCun, Y., Bengio, Y., 和 Hinton, G. (2015). 深度学习. Nature, 521 (7553), 436–444.

[25] Hochreiter, S., & Schmidhuber, J. (1997). 长期记忆。神经计算,9(8), 1735–1780。

联系方式

想要联系?请关注罗宾逊博士LinkedInTwitterFacebookInstagram。访问我的主页获取论文、博客、邮箱注册等更多信息!

[## AI 研究工程师与企业家 | 约瑟夫·P·罗宾逊

研究员与企业家,您好!作为一名研究员,罗宾逊博士提出并采用了先进的 AI 技术来理解…

www.jrobs-vision.com

日常(AI)物品的设计

原文:towardsdatascience.com/the-design-of-everyday-ai-things-26516d928566?source=collection_archive---------3-----------------------#2023-10-06

构建优秀生成式 AI 工具的 UI/UX 原则

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传 Sam Stone

·

关注 发表在 Towards Data Science ·10 min 阅读·2023 年 10 月 6 日

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图片来源:DreamStudio

唐·诺曼 1988 年的设计经典《日常物品的设计》阐述了用户体验原则,这些原则至今影响着卓越的硬件和软件设计。尽管诺曼举了门把手和电灯开关等类比例子,他的原则广泛适用于软件,包括生成式 AI 产品。在关于生成式 AI 的炒作中,很容易忘记即使是最先进模型驱动的产品,如果缺乏良好的 UI/UX,也会失败。

许多新的 AI 工具引起了大量兴趣,但随后用户留存表现平平(Sequoia 在这里详细说明)。AI 噱头驱动了“游客”注册,但新用户很难理解或从产品中获得真正的价值。这就是经典的“幻灭低谷”,当核心技术(生成模型)领先时,而支持技术(UI/UX 设计)滞后。

本文详细说明了如何将三个核心用户体验(UX)概念应用于生成式 AI 产品:1) 可供性,2) 反馈,和 3) 约束。 将这些概念应用于生成式 AI 可以得出一些结论,我们将探讨,包括:

  • 不要追求一击即中

  • 用户反馈不是免费的

  • 对聊天机器人界面保持怀疑态度

以下示例来自于工作场所生产力工具(部分灵感来源于我在 Tome(一个 AI 驱动的思想塑造与分享平台)工作中的经验),但这些策略广泛适用,从开发工具到社交媒体再到电子商务。

主题 1: 反馈

向用户快速、清晰地反馈请求的操作对于任何技术都是至关重要的。对于生成式 AI 系统来说,反馈尤其重要,因为其输出具有延迟性和复杂性。反馈是双向的。系统必须从用户那里获取相关的反馈,而不至于让人厌烦,以便在短期内生成更好的输出,并在中长期内促成更好的产品版本。

以延迟为基础进行构建

生成式 AI 模型的响应时间通常在个位到十几秒之间。乍一看,等待十秒钟以获得一个引人注目的备忘录、一幅精彩的图像或一段美丽的视频似乎不是什么问题。没有生成式 AI 时,这些操作需要数小时——无论是 10 秒、1 秒还是 100 毫秒,谁在乎呢?

但用户并不是在优化机会成本的经济学家。他们已经习惯了非 AI 工具,以至于期望软件的速度快到被认为是即时的。这导致了一些用户在面对显然不即时的 AI 产品时遇到的挑战:

  1. 对系统是否正常工作以及是否需要重试/重新启动的困惑。

  2. 高感知迭代成本。而且由于 AI 生成的第一个结果通常不是用户想要的,用户往往需要进行迭代。

  3. 用户开始多任务处理的可能性很高。一旦用户切换离开你的应用程序,就没有保证他们会再回来。

有一些减少延迟效果的良好策略早于生成式 AI 之前就已经存在。这些包括加载动画、进度条和后台处理(用户被引导去执行其他任务,并在当前任务完成时收到通知)。一种较新的策略,特定于 LLM 功能,是逐字(或逐字符)将文本流式传输到用户界面,而不是一次性渲染完整输出。由于许多模型生成单词的速度比用户阅读的速度快,这可以将感知的延迟降低到接近零。

不要追求一杆进洞

减少延迟的一个特别有效的策略是将工作流程分解成小步骤,在每一步提供系统反馈并征求用户反馈。这使用户能够以越来越大的信心朝着输出前进,确信系统将准确地提供用户所需的内容。在设计良好的迭代工作流程中,初始步骤的延迟较低——用户对最终输出会逐步增加信任。如果你对获得所需的工件非常有信心,那么你愿意等待十秒钟来完成最后一步。

迭代工作流程比提高延迟容忍度还有一个更强大的好处:它们使用户能够生成更符合预期的输出。生成模型有时可以从简单的用户提示中生成用户所需的内容。直接从输入到“完美”最终输出的过程是一个惊人的用户体验;这就像一杆进洞。就像一杆进洞一样,这种情况非常少见。

挑战不在于模型“聪明”到什么程度,而在于模型需要什么上下文和信息来实现用户的愿景。考虑一个希望总结其团队季度表现的销售经理。她见过数十份季度销售报告,并且对公司规范(如语气、细节水平、长度和视觉布局)非常熟悉。如果她需要同事为她撰写这样的报告,她会简单地要求“季度销售报告”,并期望同事已经了解这些规范。

所以当这位销售经理希望从 AI 工具中获得这样的报告时,她不清楚需要告诉工具什么规范,以及工具已经知道了什么。这时,迭代工作流程特别有用。她可以从一些简单且熟悉的内容开始,比如请求“季度销售报告”,然后工具可以帮助她准确地确定她的具体要求。Zach Lloyd 在这篇关于 AI 设计的合理文章中称这种模式为“提问和调整”。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Tome 的大纲编辑器是一个迭代人工智能工作流中的中间步骤的示例,位于提示和最终输出(一个多页演示文稿)之间。tome.page

用户反馈不是免费的

在许多经典的机器学习产品中,每一次用户交互都会生成一条新的训练数据,从而改善产品的下一版本。每一次用户点击搜索结果都有助于改进搜索模型。每一封用户标记为垃圾邮件的邮件都有助于改进垃圾邮件分类模型。

但许多生成性人工智能产品缺乏固有的“物理”特性,即用户交互会机械地导致模型改进。对于输出是复杂文本、图像等的人工智能产品,很难区分挫败的退出(即用户未能获得他们想要的输出而退出)与满意的退出(即用户获得了他们想要的内容而离开)。一些产品会征求用户的自愿反馈(例如点赞/点踩),但完成率往往非常低,并且反馈本身经常存在选择偏差。

设计一个工作流,其中用户的自然下一步行动可以表明他们对前一个人工智能输出的感知,这要好得多。一种模式,在文本模型中最常见,是内联建议:如果用户接受建议并继续写作,那就是一个强烈的信号,表明他们对建议持积极态度。另一种模式是记录哪些人工智能输出被保存、编辑或共享。这些与用户满意度并不完全相关——用户可能会因为图像特别怪异而分享它——但在整体使用中,它们是不错的代理指标。

主题 2:可用性

可用性是一个提示(通常是视觉的),它建议如何以及何时使用某个功能。良好的可用性使用户能够直观地与产品互动,而无需 extensive instructions or experience。我们将探索生成性人工智能在用户旅程中的三个步骤的可用性:发现人工智能的切入点、提供正确的输入给人工智能,以及使用人工智能的输出。

发现人工智能切入点

许多工作工具正在添加大量的人工智能功能,这些功能在创作过程中的不同点都适用。使用人工智能功能的高级切入点包括:

  1. 从头开始帮我

  2. 扩展我所开始的

  3. 编辑我所创建的内容

这些不同的切入点导致了显著不同的界面,甚至在人工智能界面发展的早期阶段就已经如此。对于(1),自由文本或“空白画布”界面已经成为早期的主流范式。对于(2),内联生成(也称为自动完成)往往主导文本生成功能(如 Github Copilot),而“给我更多类似的”则主导图像生成功能(如 Midjourney)。对于(3),界面往往集中于高亮、选择或上传现有内容(如 Grammarly)。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Whimsical 的 AI 思维导图帮助用户从头开始。 whimsical.com

对于已经发现一个 AI 入口点的用户而言,他们可能会轻易得出“这就是 AI 所在之处”的结论,而忽视其他功能。优秀的产品通过在用户工作流中最可能有用的时刻向用户介绍各种 AI 入口点来减少这一问题。

输入 AI 信息

许多生成性 AI 工作流的核心输入是自由文本输入,也称为“提示”。不幸的是,好的提示是复杂的、快速发展的,并且在工具之间不一致。好的产品帮助用户通过示例提示和工具提示来构建提示策略。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Perplexity 在其主页上包含了一些示例提示,以说明超出典型搜索引擎的使用案例。 www.perplexity.ai/

好的界面还帮助用户理解 AI 所拥有的上下文——以及它所缺乏的。当使用强大的 AI 时,合理的用户可能会得出结论,无论他们在应用中看到什么,AI 也必须能够看到和理解。例如,如果我能看到与 AI 的过去对话,AI 肯定也必须知道这一点(这是 ChatGPT 普及的行为)。但并非所有 AI 都如此工作!一些系统了解用户之前的提示,有些甚至了解比过去提示更多的上下文——而有些则只了解用户当前的交互和其他任何信息。用户不应通过反复试验来弄清楚系统知道什么和不知道什么。

使用 AI 输出

当系统生成了优秀的 AI 输出时,我们很容易认为成功已经到手。但即使输出表现良好,对于用户来说,这可能也是一个令人困惑的时刻。

首先,新用户经常不知道如何保存输出。即使输出很不错,许多用户仍然希望进行迭代,看看是否能从好变得更好。但害怕丢失现有工作可能会导致犹豫和挫折感。

其次,用户可能会困惑于如何改进输出。假设他们使用了“从头开始”的 AI 功能,他们是否应该回到最开始?他们是否需要转到不同的 AI 入口点,如“扩展”或“编辑”?许多用户可能遇到过像 ChatGPT 这样的产品,其中输出不是直接可编辑的;如果输出是可编辑的,用户可能需要一个编辑工具。

主题 3:约束

约束限制输入和输出,以帮助用户更快更好地工作。好的约束对用户来说是明确的。如果系统可以帮助用户实现目标——但仅仅是部分实现或部分时间——那么最好完全阻止这条路径,而不是提供一个不可靠的体验。

大型语言模型开辟了广阔的新用户体验(这就是我喜欢从事这项工作的原因!),产品创造者应热衷于放松来自确定性软件的传统约束。尽管如此,无论大型语言模型变得多么智能,总会有一些深思熟虑的约束存在的空间。

输入:不要害怕控制

受到 ChatGPT 成功的启发,许多生成型人工智能工具将自由文本框作为唯一或主要的用户输入。但用户的意图的许多方面通过分类或数字输入更能得到有效表达。在创建文档时,大多数用户会考虑语言(一个分类)和长度(一个数值)。用户可能不会在自由文本提示中提及这些属性,但这并不意味着他们不在意。通过离散的、有界的控制(如下拉菜单或滑块)来征询这些输入,系统帮助获取所需的输入,以提供用户脑海中的内容。对于帮助用户导航离散控制,有一些经久不衰的原则:设置良好的默认值,逻辑地分组控制,并通过工具提示或标签解释控制。

关于控制,设置良好的默认值是设计的关键部分。在绝大多数情况下(远远超过 90%),用户不会更改默认设置,即使这样做对他们有利。将良好的默认值与用户偏好的变异结合的一个机会是通过硬编码规则或人工智能动态调整默认值。

输出:并非所有可以生成的内容都应该生成

对于生成型人工智能产品,有许多情况是基础模型可以生成某些内容,但用户宁愿什么也不做,也不愿意应对误导性或刺耳的输出。

对于大多数工作相关任务,用户宁愿选择“我不知道”也不愿接受需要验证或反驳的潜在错误答案。这项哈佛大学在咨询公司 BCG 进行的研究展示了当人工智能回答超出其“信心边界”的问题时,如何影响工作质量,而用户对边界并不知情,因此没有充分审查输出。

减少幻觉的方法正在迅速发展(例如,检索增强生成),我怀疑幻觉将在几年后成为一个“解决”的问题——但今天,当事实准确性至关重要时,输出仍然是一个重要的考虑约束的地方。

法律和伦理问题是限制用户面对输出的第二个原因。仅仅因为底层模型可以生成某个主题的文本或图像,并不意味着这样做是合乎良心的。然而,许多情况下,当系统将用户请求分类为“超出范围”时,用户的意图实际上是善意的。通过一点帮助,用户可以重新表述他们的请求,以保持在范围内。例如,一些图像生成工具拒绝包含“child”一词的提示。但如果用户想生成一个有孩子的家庭的图像,他们可以提示“家庭四口”或“父母和儿子女儿”。关键在于,限制对用户来说是明确的。

随着生成式 AI 产品的流行,优秀的产品设计师和产品经理会记住:成功不仅仅源于 AI 的智能程度,还在于产品如何引导用户通过 AI 驱动的工作流程。核心设计概念如反馈、可用性和限制依然重要,但它们的实施战术和模式正在迅速演变。善用这些设计模式对任何希望超越初期炒作周期并提供持久、广泛使用产品的 AI 公司至关重要。

监控生产环境中的机器学习模型的难点

原文:towardsdatascience.com/the-difficulties-of-monitoring-machine-learning-models-in-production-ed3f87e88253

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传 Maciej Balawejder

·发表于 Towards Data Science ·7 分钟阅读·2023 年 3 月 14 日

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

照片由 Luke Chesser 提供,来自 Unsplash

成为数据科学家可能听起来像是一份简单的工作 — 准备数据、训练模型并将其部署到生产环境中。然而,现实却远非如此。这个工作更像是照顾一个婴儿 — 一个永无止境的监控周期,确保一切正常。

挑战在于需要关注三个关键组件:代码、数据和模型本身。每个元素都有其自身的难点,使得在生产环境中的监控变得困难。

在这篇文章中,我们将深入探讨这些挑战,并提及可能的解决方法。

模型失败的两种方式

‍模型未能进行预测

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

代码中的错误示例。图像由作者提供。图像由作者提供。

当我们谈论模型未能进行预测时,意味着它无法生成输出。由于模型始终是更大软件系统的一部分,它还会面临更多技术挑战。以下是一些可能的例子:

  • 语言障碍 — 将一个用一种编程语言构建的模型集成到用另一种语言编写的系统中。这可能需要额外的代码或“粘合”代码来连接两种语言,增加了复杂性和失败的风险。

  • 维护代码 — 正如我们所知,库和其他依赖项不断更新,因此它们的功能命令等也会变化。跟踪这些变化与代码的关系很重要,以确保代码不会过时。

  • 扩展问题 — 随着我们的模型用户越来越多,基础设施可能无法足够强大以处理所有请求。

良好的软件监控和维护系统应能防止可能的问题。即使发生了问题,我们也会直接获得异常信息。在下一节中,我们将深入探讨那些检测不那么明显的问题。

模型的预测失败

在这种情况下,模型生成了输出,但其性能正在下降。这种失败特别棘手,因为它通常是无声的——没有明显的警报或指示器表明发生了什么。整个管道或应用程序可能看起来运作正常,但模型生成的预测可能已不再有效。

这个问题有以下两个原因:

协变量漂移

输入特征的分布随时间变化。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

训练和生产数据中 CLV 预测的协变量漂移分布。作者提供的图片。

上图展示了一个社交媒体平台上客户生命周期价值 (CLV) 预测的假设场景。训练数据的分布严重偏向年轻客户。然而,当模型在生产中部署时,分布发生了变化,年长客户略有增加。

一个可能的解释是,当训练数据被收集时,平台的大多数用户是年轻的。随着平台的流行,年长的客户开始注册。这个新用户群体的特征与历史用户不同,这意味着模型现在更容易出错。

数据漂移的检测相对简单。我们需要比较不同时间段的分布,以识别数据的变化。棘手的部分是,并非所有漂移都会导致性能下降。

有时,数据的变化可能不会影响模型的整体性能。这是因为并非每个输入特征对输出的贡献都是一样的。只有重要特征的变化会对模型的整体性能产生显著影响。

概念漂移

模型输入和输出之间的关系发生了变化。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

训练和生产数据中 CLV 与“年龄”特征的关系。作者提供的图片。

让我们回到客户生命周期价值预测的例子。如我们所见,年轻客户的 CLV 在生产中有所下降。这可能是由于年轻用户迁移到其他社交媒体平台造成的,比如我们看到的从 Facebook 到 Instagram 的快速转移。在训练时提取的年龄与 CLV 的关系在生产中已不再相关。

与协方差转移不同,概念漂移几乎总是影响模型的商业影响。更具挑战性的是,概念漂移并不容易检测。一个可能的解决方案是对标签进行相关性分析,或在分析和参考期训练并比较两个独立模型。另一种方法是仔细监控模型性能随时间的变化。如果性能下降,这可能表明概念漂移正在发生。

然而,监控模型性能并非总是容易的,尤其是当目标标签的访问受到限制时。这是一个关键挑战,我们将在下一段中更详细地探讨。

真实值的可用性

如前所述,访问目标值是监控生产中机器学习模型的关键方面。根据这些值的可用性,我们可以区分三种类型:

即时

真实值是立即可用的。

一个典型的即时可用性例子是汽车到达预测。在旅行完成后,我们可以立即评估预测结果,并获得模型的实时性能。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

使用 NannyML 进行即时真实值的性能监控。图片由作者提供。

上图展示了分类问题中的一种常见场景,其中参考期代表测试数据集,分析期代表来自生产的实时数据流。在右侧,我们可以看到一个表格数据的例子,并且突出显示了目标值。通过即时访问真实值,我们可以不断监控模型的性能。如果性能低于某个阈值,如从六月到十月所示,这表明我们需要调查下降的原因。

对目标值的即时访问使得监控和评估模型性能变得更加容易。然而,世界是复杂的,获取即时真实值并非总是那么简单。

延迟

真实值在时间上被推迟。

对服装公司的需求预测是一个很好的例子,说明真实值的延迟。这些公司使用机器学习模型预测下一个季节的需求。然而,评估预测结果是一项棘手的任务,因为他们必须等待 3 个月,直到季节结束才能衡量预测的准确性。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

使用 NannyML 进行带有延迟真实值的性能监控。图片由作者提供。

正如右侧所示,我们的表格数据由于延迟而缺少关键的目标值。这种信息缺失在图表中体现出来,我们可以看到从五月到十一月模型的 ROC AUC 性能存在一个间隙。这使得实时了解模型的表现及其决策是否仍然准确变得具有挑战性。

缺失

完全没有真实数据。

在某些情况下,例如完全自动化的流程中,可能无法获得真实数据。一个例子是使用机器学习模型进行保险定价。这些模型在生产环境中部署,根据人口统计信息或车辆信息预测保险价格。然而,由于这个过程是自动化的,没有人参与评估预测的准确性。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

使用 NannyML 在缺乏真实数据的情况下进行性能监控。

当我们查看表格数据时,我们可以看到目标值完全缺失。这在我们的性能图中反映出来,部署后图表是空白的。这使得很难清楚地了解我们的模型实际表现如何,以及其预测是否仍然可靠。

尽管缺乏真实数据听起来可能令人沮丧,但这并不意味着模型的性能无法评估。即使模型没有目标值,它仍然会生成输出,我们可以将其与之前的数据进行比较。这些信息足以估计其性能。虽然真实数据的可用性使得评估模型性能的任务更容易,但即使在其缺失的情况下,也可以进行评估。

结论

作为数据科学家,关键责任之一是确保整个模型和管道顺利运行。然而,这通常是一个具有挑战性的任务,因为在生产过程中可能会出现几个障碍,例如:

  • 代码 — 模型因错误无法进行预测

  • 数据 — 协变量偏移、概念漂移或对真实数据的有限访问

但不用担心;现在你知道了需要注意的事项以及这些问题发生的原因,你将能够轻松应对它们。

祝监控愉快!

这篇博客最初发布于 https://www.nannyml.com/blog

ETL 的 Docker Compose:Meerschaum Compose

原文:towardsdatascience.com/the-docker-compose-of-etl-meerschaum-compose-777e0e7304d1?source=collection_archive---------15-----------------------#2023-06-19

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图片由CHUTTERSNAP拍摄,刊登于Unsplash

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传 本尼特·米尔斯

·

关注 发表在Towards Data Science · 6 分钟阅读 · 2023 年 6 月 19 日

本文介绍了Meerschaum Compose,这是一个用于在 YAML 中定义 ETL 流水线的工具,以及一个数据工程框架Meerschaum的插件。

Docker 的出现彻底改变了我们设计、构建和运行云应用程序的方式。然而,开发者很快意识到其灵活性使得协作变得困难,因此 docker-compose 成为管理环境和多容器项目的首选工具。

类似地,这个一致环境的问题也出现在 ETL 框架 Meerschaum 中。随着越来越多的数据工程师使用该平台构建他们的管道,管道的动态特性意味着需要一个解决方案来提供项目级的隔离。

受到 Docker Compose 的启发,这个解决方案以一个名为 Meerschaum Compose 的项目形式出现。我每天在工作和个人项目中使用 Compose 来构建和管理数据管道,今天我想展示如何使用 Compose 构建 ETL 项目。

Compose 如何驯服引擎

权力越大,责任越大。在 Docker 的情况下,这种责任由一个名为docker-compose.yml的清单文件处理,该文件描述了应用程序内的服务应该如何运行。该文件充当了活文档,便于原型设计并向 CloudOps 描述预期环境。通过一个简单的标准,Docker Compose 通过提供一种方便的方式来标准化和共享多服务项目的环境,从而填补了开发过程中的空白。

Meerschaum Compose 服务于类似的目的:在mrsm-compose.yml文件中,你可以指定项目可能需要的一切:预期的 环境插件管道连接器

命令

当你遇到一个新的 Compose 项目(在下面的屏幕截图中使用 Tech Slam ‘N Eggs 演示项目)时,尝试这些命令来熟悉环境:

注意: 如果你想在 Docker 容器中运行 Compose,请参见 这个仓库

**mrsm compose run**

注册管道并逐一同步它们。这对于确保执行顺序以及使用数据库更新管道参数非常有用。一个常见的模式是将不同阶段的 Compose 文件链接在一起,作为更大的 ETL 过程的一部分:

mrsm compose run --file mrsm-compose-00-extract.yaml && \
mrsm compose run --file mrsm-compose-01-transform.yaml && \
mrsm compose run --file mrsm-compose-02-load.yaml

注意: 命令mrsm compose sync pipes是并行执行的,最适用于 Compose 文件中的管道彼此独立的情况。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Compose 运行命令一次同步一个管道。

**mrsm compose explain**

解析 Compose 文件并打印定义管道的当前环境和状态。这在故障排除和理解项目结构时非常有用。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Compose explain 命令打印定义管道的状态。

**mrsm compose up --dry**

注册并更新远程管道的参数。这隐含地作为mrsm compose run的一部分运行,并应在标准的 Meerschaum 操作(如mrsm compose sync pipes)之前运行。--dry标志可以防止同步作业运行。

注意: Compose 将 标记管道 与项目名称。如果在一个项目中使用多个 compose 文件,请设置键 project_name

**mrsm compose down -v**

停止作业并删除管道。这类似于 docker compose down -v(即 -v 表示“卷”)。

**mrsm compose <action>**

从项目环境中执行标准的 Meerschaum 操作(例如 sync pipesdelete pipes、自定义操作)。我经常运行 mrsm compose python 以跳入项目环境中的 REPL。

每当你运行 mrsm compose 命令时,标志 --tags {project_name} 会被附加(除非被覆盖),以确保你只与项目中的管道进行交互。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

一个 Meerschaum Compose 项目的示例,用于天气数据的 ETL。

管道

谦逊的 管道 是 Meerschaum 对增量 ETL 的抽象。管道具有输入和输出 连接器 并存储 参数 来配置其同步过程的行为。这可以是简单的 SQL 查询,也可以包括用于插件的自定义键。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

上述 Compose 项目的管道通过 web UI 展示

由于管道的元数据与其表一起存储,它们易于编辑(无论是通过 edit pipes 还是在 web UI 上),这有助于原型设计。但这种动态特性引入了本文开头描述的同样问题:为了扩展开发,需要一个 Compose 文件来定义项目组件,以便于版本控制。

根据 Meerschaum Compose 规范,管道在 sync:pipes 键下的列表中定义。每个项目定义了构建管道所需的键和参数,就像你期望数据库中的管道所反映的蓝图。

例如,以下代码片段将定义一个管道,该管道会将一个 weather 表从远程 PostgreSQL 数据库(在下面定义为 sql:source)同步到本地 SQLite 文件(在本项目中定义为 sql:dest)。

sync:
  pipes:
    - connector: "sql:source"
      metric: "weather"
      target: "weather"
      columns:
        datetime: "timestamp"
        station: "station"
      parameters:
        fetch:
          backtrack_minutes: 1440
        query: |-
          SELECT timestamp, station, temperature
          FROM weather

config:
  meerschaum:
    instance: "sql:dest"
    connectors:
      sql:
        source: "postgresql://user:pass@host:5432/db"
        dest: "sqlite:tmp/dest.db"

这个示例将使用日期时间轴 timestamp 进行范围边界(向回追溯 1 天)逐步更新一个名为 weather 的表,这一列加上 ID 列 station 一起构成一个复合主键,用于去重。

URI 是字面上写的示例;如果你正在提交一个 compose 文件,请引用环境变量(例如 $SECRET_URI)或你的主 Meerschaum 配置(例如 MRSM{meerschaum:connectors:sql:source})。

连接器

首先,简单回顾一下Meerschaum 连接器:你可以通过几种方式定义连接器,其中最受欢迎的是通过环境变量。假设你在环境文件中定义了连接秘密:

export MRSM_SQL_REMOTE='postgresql://user:pass@host:5432/db'
export MRSM_FOO_BAR='{
    "user": "abc",
    "password": "def"
}'

第一个环境变量MRSM_SQL_REMOTE定义了连接器sql:remote。如果你引入了这个文件,你可以通过命令mrsm show connectors sql:remote来验证这个连接器。

第二个变量是如何定义自定义FooConnector的示例,你可以通过插件中的@make_connector装饰器来创建它。自定义连接器是一个强大的工具,但现在,这里是基本结构:

from meerschaum.connectors import make_connector, Connector

@make_connector
class FooConnector(Connector):
    REQUIRED_ATTRIBUTES = ['username', 'password']

    def fetch(pipe, **kwargs):
        docs = []
        return docs

所以我们刚刚回顾了如何在主机环境中定义连接器。让我们看看如何在 Meerschaum 项目中使这些主机连接器可用。在 Compose 文件中,我们项目所需的所有连接器都在config:meerschaum:connectors下定义。使用MRSM{}语法来引用主机环境中的键,并将它们传递到项目中。

config:
  meerschaum:
    instance: "sql:app"
    connectors:
      sql:
        app: MRSM{meerschaum:connectors:sql:remote}
      foo:
        bar: MRSM{meerschaum:connectors:foo:bar}

插件

Meerschaum 可以通过插件进行轻松扩展,这些插件是 Python 模块。插件可以获取数据、实现自定义连接器和/或扩展 Meerschaum(例如,自定义操作、标志、API 端点等)。

Meerschaum 支持多个插件目录(通过MRSM_PLUGINS_DIR),可以在mrsm-compose.yaml中的plugins_dir键下设置(默认是plugins目录)。

将你的插件存储在 Compose 项目中,可以清楚地说明你期望插件的使用方式。例如,MongoDBConnector 项目中的 Compose 文件演示了自定义连接器如何同时作为连接器和实例使用。

包管理

当你第一次开始使用 Meerschaum Compose 时,首先会注意到它会开始安装大量的 Python 包。不要担心你的环境——所有东西都安装在你项目的root子目录中的虚拟环境中(有点讽刺,对吧?)。你可以用mrsm compose init安装插件的依赖项。

为了在项目之间共享包,请在mrsm-compose.yml中设置root_dir键为新的路径。删除这个root目录将有效卸载 Compose 下载的所有包,同时保持你的主机环境不变。

结论

Meerschaum Compose 已经成为我构建中型 ETL 项目的首选工具。它的工作流程类似于Meltanodbt,但入门门槛更低,对 ETL 过程的控制更为动态。这是一种组织插件、连接器和管道的整洁工作流程,非常适合团队合作。

你可以通过Meerschaum Compose 模板仓库快速启动一个新项目——查看MongoDBConnector 插件Tech Slam ‘N Eggs 演示以获取实际示例。

随意将你的项目添加到Awesome Meerschaum 列表

恐怖的对手:机器学习中的数据泄漏

原文:towardsdatascience.com/the-dreaded-antagonist-data-leakage-in-machine-learning-5f08679852cc?source=collection_archive---------6-----------------------#2023-05-19

可能是机器学习中最被低估的概念之一

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传 Andreas Lukita

·

关注 发表在 Towards Data Science · 13 分钟阅读 · 2023 年 5 月 19 日

我参加了超过 5 门商务分析和机器学习课程,包括面对面课程和在线课程。令人惊讶的是,只有一门课程稍微触及了数据泄漏的话题。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图片由 Luis Tosta 提供,来源于 Unsplash

讨论数据泄漏时,在没有机器学习背景的情况下,我们通常指的是机密信息在没有适当安全措施或许可的情况下转移到第三方,导致隐私和安全的泄露¹。

尽管这个概念有些类似,但在机器学习的背景下并不完全是这样。这在机器学习领域的含义是:

数据泄漏发生在测试数据集的信息错误地被包含在训练数据集中。²

结果是什么? 训练过程中性能指标表现得不切实际地好,但当模型真正投入使用时表现很差。

简单来说, 模型记住了它不该访问的信息,导致训练过程中性能指标被人为地夸大。

还是无法理解? 好吧,想象一下。你正在为即将到来的数学考试复习。你做了很多练习题以便每天进步。然后,你发现考试题目不小心泄露到了网上。你获得了这些关键信息,并决定在这张试卷上练习(你在这个考试前不应知道的数据集上训练自己,因此,你“记住”了题目模式)。结果?你对这张试卷变得过于熟悉,表现指标异常优秀,但当你真的投入到实际情况中时……(我们还是不提了)。

目录

  • 机器学习中的数据泄漏类型(目标泄漏训练-测试污染,数据预处理中的泄漏

  • 机器学习中数据泄漏的后果

  • 预防机器学习中的数据泄漏(手动审查数据清理和预处理使用管道适当的验证技术

  • 现实世界数据集示例:泰坦尼克号数据集

  • 数据泄漏第一种示例:将目标存活作为特征包含

  • 数据泄漏第二种示例:混淆训练和测试数据的记录

  • 数据泄漏第三种示例:错误的数据预处理步骤

  • 数据泄漏第四种示例:将特征舱位包含为模型中的一部分

目标泄漏

关于目标泄漏,识别可能不会那么简单明了。想象一下:你正在构建一个模型来预测客户是否会取消他们的月度订阅服务(即流失)。乍一看,将**“客户服务电话的数量”**作为特征包含在模型中似乎并没有问题,因为你可能认为更多的客户服务电话与更高的流失概率相关。

然而,经过仔细检查发现,“客户服务电话数量” 是客户流失的结果,而不是一个贡献特征。已经决定流失的客户只是打电话解决未结清的问题,然后最终取消订阅。因此,在预测客户是否会流失时,这些信息是不可用的(换句话说,我们只知道已经决定流失的客户的信息)。

将目标变量作为特征变量的一部分,或任何直接或间接从目标变量派生的代理变量,可能导致数据泄漏。

训练-测试污染和数据预处理中的泄漏

这些情况指的是对训练集和测试集应用相同的预处理步骤。例如,当我们进行特征缩放、估计缺失值和去除异常值等数据预处理步骤时,我们应该确保不会像下面所示的那样从测试数据集中“学习”³。

scaler = StandardScaler()
scaler.fit(X_train)
scaler.transform(X_train)
scaler.transform(X_test)

在这里,我们在预处理步骤之前将数据集分成训练集和测试集,以便我们只能拟合训练数据集。请注意,我们不应该在整个数据集(包括训练集和测试集)上拟合,因为这会导致数据泄漏(我们的模型学会了它不应该学习的东西,换句话说,在预测时对测试数据集的信息是不知道的)。

在训练数据上拟合,在训练和测试数据上转换。

机器学习中数据泄漏的后果

在机器学习项目中未能检测到数据泄漏的后果是巨大的——它们带来了虚假的希望。有没有遇到过训练性能极高而测试数据集表现非常差的情况?数据泄漏可能是罪魁祸首。这里的关键字是过拟合和无法泛化。这是因为模型学会了记忆噪声和无关信息,导致在面对真实测试数据集时表现不佳²。

最终结果?

你会做出不准确的模型评估和不可靠的预测。真是资源浪费!

防止数据泄漏:手动审查

是的,我们都明白。手动审核效率低下且非常耗时。然而,花时间研究特征与目标变量之间的关系,也许是检测数据泄漏最一致的方式,因此,非常值得。当一个特征与目标变量的相关性非常高时,我们应该怀疑并进一步调查这种关系。有时,进行探索性数据分析(EDA)可能有助于揭示特征与目标之间的相关性。此外,全面的领域知识和专业技能可以帮助确定一个特征是否应该包含在模型中。记住,当有疑问时,总是要问自己这个指导性问题。

“这个特征是否包含在预测时不可用的信息?”

如果上述问题的答案是“是”,那么包括该特征可能会导致数据泄漏。

防止数据泄漏:管道是王道

我之前参加的商业分析和机器学习课程中,没有提到构建机器学习预处理管道。最常见的做法是编写到处都是的“意大利面条”代码,而没有任何工作流程标准化。虽然这对很多人来说可能很熟悉,但这并不是最佳实践——其中一个原因是可能会将数据泄漏引入模型。我第一次接触到利用管道的想法是来自于书籍Data Cleaning and Exploration with Machine Learning⁴。从这本书中,我学到的关键经验包括通过将每个预处理步骤作为变量参数嵌入到make_pipeline方法中,并分别为数值型、分类变量和二进制变量分开处理。

简单来说,管道是一系列线性的数据预处理步骤,依次执行。管道提供了一个清晰有序的链式过程,用于自动化机器学习项目的工作流程。我们可以利用 scikit-learn 的 Pipeline 类⁵,它接受一个元组列表作为输入,每个元组代表管道中的一个步骤。每个元组的第一个元素是表示步骤名称的字符串,第二个元素是 scikit-learn 转换器或估计器对象的实例。当然,还有一个简化的方法,就是**make_pipeline**,它不要求我们命名估计器(我们都是懒惰的生物)。记住,估计器需要具有**fit****transform**方法。

为什么要使用 Pipeline?

管道自动处理所有数据处理过程。它还确保每个步骤仅在训练数据上进行拟合,从而防止数据泄漏,并确保各阶段按正确顺序进行。

这里有一个示例:我们需要对数据集中不同类型的数据进行数据预处理,即数值型、分类型和二元特征,每种特征都有不同的步骤。我们可以利用**make_pipeline**将过程按顺序安排,让 Pipeline 处理所有后台工作。这将返回一个**Pipeline**对象,该对象具有多个属性和方法可供调用。例如,我们可以调用**fit**(X_train, y_train)**score**(X_test, y_test)来分别拟合和评估模型。

from sklearn.preprocessing import StandardScaler
from sklearn.impute import SimpleImputer
from sklearn.pipeline import make_pipeline
from feature_engine.encoding import OneHotEncoder
from preprocfunc import OutlierTrans #self-created Python class

standardtrans = make_pipeline(OutlierTrans(2), 
                              StandardScaler()
                             )

categoricaltrans = make_pipeline(SimpleImputer(strategy="most_frequent"), 
                                 OneHotEncoder(drop_last=True)
                                )

binarytrans = make_pipeline(SimpleImputer(strategy="most_frequent")
                           )

columntrans = ColumnTransformer(transformers=[
    ("standard", standardtrans, numerical_cols),
    ("categorical", categoricaltrans, ['gender']),
    ("binary", binarytrans, ['completedba'])
])

lr = LinearRegression()
pipe = make_pipeline(columntrans, KNNImputer(n_neighbors=5), lr)

防止数据泄漏:交叉验证

本节内容受到《数据清洗与机器学习探索》⁴一书的启发。我从书中获得的另一个观点是将 Pipeline 和交叉验证的概念结合起来。是的,它们并不是互相排斥的!训练和测试数据集的选择非常关键,如果做得不对可能会导致数据泄漏。当我们不执行交叉验证来评估模型时,我们面临着对训练数据过拟合以及在新的、未见过的数据上表现不佳的风险。我们进行的一次性训练测试拆分可能使模型学习到某个特定特征,这个特征可能对该拆分是独有的,而不具有普遍性。

为什么要进行交叉验证?

交叉验证使我们能够更精确地预测模型在全新、未测试数据上的表现。通过使用交叉验证,我们可以在多个数据子集上测试模型的有效性。

我们可以利用scikit-learn的 K 折交叉验证来实现这一点。

K 折交叉验证的工作原理是什么?数据首先被分成k个大小相等的折叠,然后在k-1个折叠上训练模型,再在最后一个折叠上测试模型。在这个过程中,每个折叠都作为一次测试集,这个过程重复进行 k 次。在迭代结束时,通过对 k 次迭代结果的平均来估算模型的性能。当 k 设置为 1 时,这意味着我们回退到通常的训练测试拆分。我们在整个数据集上训练模型,并在一个独立的数据集上进行测试。

好消息是,我们可以从 Pipeline 中未完成的地方继续进行。

from sklearn.model_selection import cross_validate, KFold

ttr = TransformedTargetRegressor(regressor=pipe, transformer=StandardScaler())

kf = KFold(n_splits=5, shuffle=True, random_state=0)
scores = cross_validate(ttr, 
                        X=X_train, 
                        y=y_train, 
                        cv=kf, 
                        scoring=('r2', 'neg_mean_absolute_error'), 
                        n_jobs=1) 

真实世界数据集示例:泰坦尼克号数据集

泰坦尼克号。经典。泰坦尼克号数据集是经典的机器学习问题,我们为每个乘客提供了一组特征,例如他们的年龄、性别、票务等级、登船地点,以及他们是否有家人在船上⁶。使用这些特征,目标是训练一个机器学习模型来预测乘客是否**幸存**。以下是一个简短的预测版本,未涉及超参数调整和特征选择。

使用以下代码清理原始数据集。

import numpy as np
import pandas as pd
from sklearn.impute import SimpleImputer

df = pd.read_csv("../dataset/titanic/csv_result-phpMYEkMl.csv")

#Change column names, replace "?" to "NaN", change data types
def tweak_df(df):
    features = ["PassengerId", "Survived", "Pclass", "Name", "Sex", "Age", "SibSp", "Parch", "Ticket", "Fare", "Cabin", "Embarked"]
    return (df
            .rename(columns={"id": "PassengerId", "'pclass'": "Pclass", "'survived'": "Survived", "'name'": "Name", "'sex'": "Sex", "'age'": "Age", "'sibsp'": "SibSp", "'parch'": "Parch", "'ticket'": "Ticket", "'fare'": "Fare", "'cabin'": "Cabin", "'embarked'": "Embarked"})
            [features]
            .replace('?', np.nan)
            .astype({'Age': 'float', 'Fare': 'float16'})
           )

#Splitting dataset into train, validation, test, and unseen
X_train_val_test, X_unseen, y_train_val_test, y_unseen = train_test_split(tweak_df(df).drop(columns=['Survived']), tweak_df(df).Survived, test_size=0.33, random_state=42)
X_train, X_val_test, y_train, y_val_test = train_test_split(X_train_val_test, y_train_val_test, test_size=0.4, random_state=42)
X_val, X_test, y_val, y_test = train_test_split(X_val_test, y_val_test, test_size=0.5, random_state=42)

#Extensive cleanup
def tweak_titanic_cleaned(train_df):

    impute_table = (train_df
                     .assign(SibSp=lambda df_: np.where(df_.SibSp==0, 0, 1),
                             Parch=lambda df_: np.where(df_.Parch==0, 0, 1))
                     .groupby(['SibSp', 'Parch'])
                     ['Age']
                     .agg('mean')
                   )

    train_df_intermediary = (train_df
                             .assign(SibSp=lambda df_: np.where(df_.SibSp==0, 0, 1),
                                     Parch=lambda df_: np.where(df_.Parch==0, 0, 1),)
                            )

    condlist = [((train_df_intermediary.Age.isna()) & (train_df_intermediary.SibSp == 0) & (train_df_intermediary.Parch == 0)),
                ((train_df_intermediary.Age.isna()) & (train_df_intermediary.SibSp == 0) & (train_df_intermediary.Parch == 1)),
                ((train_df_intermediary.Age.isna()) & (train_df_intermediary.SibSp == 1) & (train_df_intermediary.Parch == 0)),
                ((train_df_intermediary.Age.isna()) & (train_df_intermediary.SibSp == 1) & (train_df_intermediary.Parch == 1)),]

    choicelist = [impute_table.iloc[0],
                  impute_table.iloc[1],
                  impute_table.iloc[2],
                  impute_table.iloc[3],]

    bins = [0, 12, 18, 30, 50, 100]
    labels = ['Child', 'Teenager', 'Young Adult', 'Adult', 'Senior']
    features = ["Survived", "Pclass","Sex","Fare","Embarked","AgeGroup","SibSp","Parch","IsAlone","Title"]

    return (train_df
             .assign(Embarked=lambda df_: SimpleImputer(strategy="most_frequent").fit_transform(df_.Embarked.values.reshape(-1,1)),
                     Age=lambda df_: np.select(condlist, choicelist, df_.Age),
                     IsAlone=lambda df_: np.where(df_.SibSp + df_.Parch > 0, 0, 1),
                     Title=lambda df_: df_.Name.str.extract(',(.*?)\.'))
             .assign(AgeGroup=lambda df_: pd.cut(df_.Age, bins=bins, labels=labels),
                     Title=lambda df_: df_.Title.replace(['Dr', 'Rev', 'Major', 'Col', 'Capt', 'Sir', 'Lady', 'Don', 'Jonkheer', 'Countess', 'Mme', 'Ms', 'Mlle','the Countess'], 
                                                         'Other'))
             .set_index("PassengerId")
             [features]
            )

让我解释一下我所实现的数据预处理步骤的理念:

  1. 使用 scikit-learn 的 SimpleImputer 类用最频繁的条目填补 Embarked 列中的缺失数据。

  2. 使用基于乘客是否有家人随行的条件的均值列表来填补 Age 列中的缺失数据。(即,如果乘客没有家人随行,则我们用其他没有家人随行的乘客的均值进行填补)

  3. 创建了一个特征 IsAlone 用于表示乘客是否有家人随行

  4. 创建了一个特征 Title 用于表示乘客的头衔

  5. 创建了一个特征 AgeGroup 用于将乘客划分为 5 个不同的年龄组。

数据泄漏第 1 种示例:将目标 **survived** 作为特征

#Intentionally add target variable to list of features
X_train = tweak_titanic_cleaned(pd
 .concat([X_train, pd.DataFrame(y_train)], axis=1))

X_val = tweak_titanic_cleaned(pd
 .concat([X_val, pd.DataFrame(y_val)], axis=1))

X_test = tweak_titanic_cleaned(pd
 .concat([X_test, pd.DataFrame(y_test)], axis=1))

# Prepare the training data
X_train = pd.get_dummies(X_train, columns=["Survived", "Pclass", "Sex", "Embarked", "AgeGroup", "IsAlone", "Title"], drop_first=True)
X_val = pd.get_dummies(X_val, columns=["Survived", "Pclass", "Sex", "Embarked", "AgeGroup", "IsAlone", "Title"], drop_first=True)

# Scale numerical columns
scaler = MinMaxScaler()
num_cols = ["Fare","SibSp","Parch"]
X_train[num_cols] = scaler.fit_transform(X_train[num_cols])
X_val[num_cols] = scaler.transform(X_val[num_cols])

# Fit and evaluate Logistic Regression model
lr_model = LogisticRegression(random_state=0)
lr_model.fit(X_train, y_train)

# Make predictions on validation data
y_pred_val = lr_model.predict(X_val)

# Evaluate model on validation data
acc_val = round(accuracy_score(y_val, y_pred_val) * 100, 2)
print("Logistic Regression Model accuracy on validation data:", acc_val)

正如你可能预期的那样,将目标变量 survived 作为特征有效地使我们的模型变得毫无用处,因为它在验证数据上的准确率现在是 100.0%。进行预测没有意义。这种错误很容易发现,但不常见。

数据泄漏第 2 种示例:混淆训练数据和测试数据的记录

如果测试数据被意外地包含在训练集中,模型可能会在这些泄漏的信息上进行训练,从而在测试集上表现得不切实际。以下是一个故意编造的例子,展示了如何将部分测试集包含到训练集中。

X_train = (pd
           .concat([tweak_titanic_cleaned(X_train), 
                    tweak_titanic_cleaned(X_val).iloc[:150, :]])
          )
y_train = (pd
           .concat([y_train, 
                    y_val.iloc[:150]])
          )

X_val = tweak_titanic_cleaned(X_val)

# Prepare the training data
X_train = pd.get_dummies(X_train, columns=["Pclass", "Sex", "Embarked", "AgeGroup", "IsAlone", "Title"], drop_first=True)
X_val = pd.get_dummies(X_val, columns=["Pclass", "Sex", "Embarked", "AgeGroup", "IsAlone", "Title"], drop_first=True)

# Scale numerical columns
scaler = MinMaxScaler()
num_cols = ["Fare","SibSp","Parch"]
X_train[num_cols] = scaler.fit_transform(X_train[num_cols])
X_val[num_cols] = scaler.transform(X_val[num_cols])

# Fit and evaluate Logistic Regression model
lr_model = LogisticRegression(random_state=0)
lr_model.fit(X_train, y_train)

# Make predictions on validation data
y_pred_val = lr_model.predict(X_val)

# Evaluate model on validation data
acc_val = round(accuracy_score(y_val, y_pred_val) * 100, 2)
print("Logistic Regression Model accuracy on validation data:", acc_val)

数据泄漏第 3 种示例:错误的数据预处理步骤

在这里,我们应该在预处理步骤之前将数据集分成训练集和测试集。如果我们在拆分数据集之前进行预处理步骤,我们可能会意外地从测试数据集中学习,从而导致模型性能被过度夸大。

# Prepare the training data
df_leaked = pd.get_dummies(tweak_titanic_cleaned(X_train_val_test), columns=["Pclass", "Sex", "Embarked", "AgeGroup", "IsAlone", "Title"], drop_first=True)

# Scale numerical columns
scaler = MinMaxScaler()
num_cols = ["Fare","SibSp","Parch"]
df_leaked[num_cols] = scaler.fit_transform(df_leaked[num_cols])

# Split the data into train, validation, and test sets
X_train, X_val_test, y_train, y_val_test = train_test_split(df_leaked, y_train_val_test, test_size=0.4, random_state=42)
X_val, X_test, y_val, y_test = train_test_split(X_val_test, y_val_test, test_size=0.5, random_state=42)

正确的步骤是对训练数据集进行 fit_transform,对测试数据集进行 transform

数据泄漏第 4 种示例:将舱位特征作为模型的一部分

这个数据泄漏问题不容易发现,可能需要一些领域知识来理解。主要问题是**“这个特征是否包含在预测时不可用的信息?”** 如果答案是肯定的,那么很有可能存在数据泄漏。

在这种情况下,“预测”是在乘客已经登船且事件已经发生后进行的。目标是基于事件发生后的现有数据(即乘客等级、年龄等)预测乘客是否会生存。在预测时,舱位号信息不可用,因为它仅在乘客登船后才分配给乘客。

不是每位乘客的舱号在数据集中都有记录,实际上,我们有大量缺失的数据。即使对于那些有记录的乘客,舱号也可能不准确或不完整。因此,我们在开发估算生存模型时不能将舱号作为预测因子,因为它可能并不对所有乘客都准确或可用。

想象一下,我们决定将舱号作为模型中的一个预测因子。在训练模型时,它使用舱号进行预测,并且表现得非常好。但是,当我们尝试在现实世界中使用模型时,我们可能没有所有乘客的舱号,或者我们拥有的舱号可能是错误的。这意味着即使模型在训练过程中非常准确,它在实际应用中可能表现不好。

一个可能的解决方案是删除这个特征,并在模型构建中排除它。

后记

防止数据泄露确实是一个具有挑战性的任务。研究特征与目标变量之间的关系是揭示这个问题的关键。下次当你看到模型的性能异常高时,也许更好地学会坐下来观察,因为不是所有事情都需要反应。

感谢阅读,祝建模愉快!

如果你从这篇文章中获得了有用的信息,请考虑在 Medium 上给我一个 关注。简单,每周一篇文章,保持更新,领先一步!

你可以在 LinkedIn 上与我联系: https://www.linkedin.com/in/andreaslukita7/

参考文献:

  1. Forcepoint. 什么是数据泄露?数据泄露的定义、解释和探索www.forcepoint.com/cyber-edu/data-leakage

  2. Analytics Vidhya. 数据泄露及其对机器学习模型性能的影响www.analyticsvidhya.com/blog/2021/07/data-leakage-and-its-effect-on-the-performance-of-an-ml-model/

  3. JFrog. 小心数据泄露——你的机器学习模型中的潜在陷阱jfrog.com/community/data-science/be-careful-from-data-leakage-2/

  4. Michael Walker 的《机器学习中的数据清理与探索》:www.packtpub.com/product/data-cleaning-and-exploration-with-machine-learning/9781803241678

  5. Scikit-learn Pipeline. scikit-learn.org/stable/modules/generated/sklearn.pipeline.Pipeline.html#sklearn.pipeline.Pipeline

  6. Titanic 数据集。 www.openml.org/search?type=data&status=active&id=40945&sort=runs

  7. github.com/datasciencedojo/datasets/blob/master/titanic.csv

DVC 指南:所有数据科学项目的数据版本控制

原文:towardsdatascience.com/the-dvc-guide-data-version-control-for-all-your-data-science-projects-382d5b5aab00

使数据版本控制与代码版本控制一样熟悉

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传 Yash Prakash

·发表于Towards Data Science ·6 分钟阅读·2023 年 2 月 17 日

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图片由Dmitri Sobolevski提供,来源于Unsplash

作为数据科学家,我们对不同版本的代码、模型和数据进行实验。此外,我们甚至使用像 Git 这样的版本控制系统来管理我们的代码、跟踪版本、前进和后退,并与团队分享我们的代码。

代码的版本控制很重要,因为它有助于在更大规模上重现软件。数据的版本控制之所以重要,是因为它有助于在任何时间点由团队或组织中的任何开发人员使用相似的指标开发机器学习模型。

因此,版本控制你的模型和数据至关重要。但经验丰富的软件工程师会知道,使用 Git 来存储大文件是一个大忌。

Git 不仅在处理大文件时效率低下,而且也不是存储大数据文件的标准化环境。大多数数据存储在 AWS S3 桶、Google Cloud Storage 或任何机构的远程存储服务器上。

那么我们如何对数据进行版本控制呢?这就引出了 DVC。

介绍 DVC

DVC是一个数据版本控制系统,与 Git 密切配合以跟踪我们的数据文件。它甚至有类似于 Git 的语法,所以学习起来相当容易。

让我们在这篇文章中深入了解 DVC 的一些优秀数据版本控制功能。但首先,让我们创建一个新的项目文件夹和虚拟环境,并将其作为 Python 包进行安装:

$ pip install "dvc[all]"

或者如果你使用Pipenv

$ pipenv shell
$ pipenv install "dvc[all]"

你应该会看到如下输出:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

安装 DVC:图片来自作者

现在,让我们初始化一个 git 仓库。你应该会看到以下输出:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

dvc init:作者提供的图像

完美!我们现在可以继续将数据添加到 DVC 中。

将数据添加 + 提交到 DVC

我在项目的数据文件夹中有一个数据文件,如下所示:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

文件夹结构:作者提供的图像

要从终端运行大小检查,请使用:

$ ls -lh data

你将看到以下输出,数据文件显示为 5.2 MB。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

数据文件检查:作者提供的图像

现在,我们可以将这个数据文件添加到 DVC。运行:

$ dvc add data/train_shakespeare.txt

你将看到以下输出,提示我们运行 git add 命令:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

dvc 添加的数据:作者提供的图像

我们现在将运行 git add 命令:

$ git add data/train_shakespeare.txt.dvc data/.gitignore

现在我们已经将新的 .dvc 文件添加到我们的 git 跟踪中,我们可以继续提交它到我们的 git:

$ git commit -m "added data."

为我们的数据设置远程存储

我们可以简单地利用 Google Drive 存储我们的版本化数据集,在本教程中我们将正是这样做。

让我们在 Google Drive 中创建一个新文件夹,并查看其 URL:

https://drive.google.com/drive/u/0/folders/cVtFRMoZKxe5iNMd-K_T50Ie

粗体突出显示的是我们想要复制到终端的文件夹 ID,以便 DVC 可以在新创建的 Drive 文件夹中跟踪我们的数据。

让我们来做:

$ dvc remote add -d storage gdrive://cVtFRMoZKxe5iNMd-K_T50Ie

现在是将我们的更改提交到 git 的时候了:

$ git commit .dvc/config -m "Configured remote storage."

完美!现在我们可以将数据推送到远程存储。

$ dvc push 

它会要求输入认证代码,或直接带你到浏览器进行认证,只需按照指示操作即可。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Google Drive 中的远程数据:作者提供的图像

拉取远程数据

如果你或你的同事想要访问远程存储的数据,可以使用 pull 命令完成。

但首先,让我们删除本地存储的数据及其缓存,以便我们可以从远程拉取:

$ rm -f data/train_shakespeare.txt
$ rm -rf .dvc/cache

现在,拉取:

$ dvc pull

你会看到拉取文件时的以下输出:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

从远程拉取文件:作者提供的图像

如你所见,一旦 dvc 追踪你的数据文件,从远程存储拉取它就变得轻而易举。

跟踪数据的不同版本

想象一下,如果我们想跟踪同一个数据文件的新版本,我们可以轻松地将其添加到 dvc,然后再次添加到 git:

$ dvc add data/train_shakespeare.txt
$ git add data/train_shakespeare.txt.dvc

现在,你会看到一个新的 .dvc 文件版本准备提交到我们的 git:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

提交新的 .dvc 文件更改:作者提供的图像

提交文件。

现在,我们可以将最新的数据集推送到远程存储:

$ dvc push

查看我们的 Google Drive,我们可以看到我们有两个版本的数据存储:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Google Drive 数据版本:作者提供的图像

返回到不同的数据集版本

使用 DVC,回到数据集的旧版本变得容易。

如果我们查看到目前为止项目的 git log,我们会看到我们已经将两个 .dvc 文件版本提交到 git:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

因此,我们必须返回到之前的**.dvc**文件版本,因为这是 git 正在跟踪的版本。

首先,简单地执行 Git checkout 到较旧的提交,如下所示:

$ git checkout HEAT¹ data/train_shakespeare.txt.dvc 

第二步,进行 dvc 的 checkout:

$ dvc checkout

你会看到以下输出。我们现在已经将数据文件恢复到之前的版本了!

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

恢复数据到之前的版本:来自作者的图像

此外,如果你想保留这些数据集更改,只需再次提交到 git:

$ git commit data/train_shakespeare.txt.dvc -m "reverted data changes."

完美!到目前为止,你已经学习了 DVC 的大部分基本数据版本控制功能。干得好!

最后的话…

DVC 在数据科学项目的数据版本控制中提供了巨大的帮助,希望你在阅读完这篇文章后,能对如何开始使用 DVC 有一些有用的了解。

在一些示例项目中进行实践,并探索 DVC 文档,将是提升你使用这个惊人工具的技能的最佳方法。

如果你喜欢这篇文章,每周我都会发布一篇故事,在其中分享来自数据科学和编程领域的小知识。关注我,绝不错过! 😄

你也可以在LinkedInTwitter上与我联系。

你可能会喜欢的另外几篇文章:

## 可重用的 Python 日志模板,适用于所有数据科学应用 ## 使用 Docker Compose 和 GitHub Actions 的简单 Python CI/CD 管道

## 使用 Docker Compose 和 GitHub Actions 的简单 Python CI/CD 管道 ## 可重用的 Python 日志模板,适用于所有数据科学应用

在 VPS 上持续部署一个实际项目

## 使用 Docker Compose 和 GitHub Actions 的简单 Python CI/CD 管道

动态批量模型:一种混合整数编程方法

原文:towardsdatascience.com/the-dynamic-lot-size-model-a-mixed-integer-programming-approach-4a9440ba124e?source=collection_archive---------8-----------------------#2023-02-01

经典库存管理优化问题,使用 Python 和 Pyomo 解决

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传 布鲁诺·斯卡利亚 C. F. 莱特

·

关注 发表在 Towards Data Science · 7 分钟阅读 · 2023 年 2 月 1 日

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图片由 CHUTTERSNAP 提供,来源于 Unsplash

批量定制问题是具有生产批次之间设置的生产计划问题。由于这些设置,通常在每个周期内生产给定产品的成本过高(Suwondo & Yuliando, 2012)。相比之下,较少的设置与更高的持有库存成本相关。因此,为了获得最佳成本,需要平衡这些操作方面。

在本文中,Wagner & Whitin(1958)提出的问题将使用混合整数规划方法进行实现。为此,将使用 Python 库pyomo(Bynum 等,2021)和CBC 求解器。个人而言,我发现这个问题对于介绍离散规划中的库存管理概念非常有用,这在更复杂的问题中随处可见。请注意,在他们的原始工作中,Wagner & Whitin(1958)开发了一种求解该问题的精确算法,这与这里采用的方法有所不同。

你可以在这里找到包含问题完整实现的笔记本。

如果你对数值优化和/或混合整数规划不熟悉,你可能需要先查看这个关于该主题的介绍。

## 混合整数线性规划简介:背包问题

学习如何使用 scipy 和 pyomo 在 Python 中解决优化问题

towardsdatascience.com

问题陈述

假设在给定的时间范围T内,你可以预测每个时间点t的给定产品的需求d。此外,假设你可以预测每个时间点的设置成本s和库存持有成本h。如果在某个时间点t生产了任何数量的产品,则应发生相应的设置成本s。此外,在每个周期结束时,应产生持有库存成本,对应于h乘以t的期末库存。

如果问题是非限制性的,如 Wagner & Whitin(1958)所考虑的,可以认为每个需求都应得到满足。此外,假设单位生产成本与时间无关,尽管问题结构的微小变化可能会轻易引入这些成本。你的目标是确定每一时刻生产多少单位的产品,以便最小化设置和持有库存成本。

我将在下一节中详细描述每个建模组件,但可以通过以下方程总结所考虑的完整优化问题。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

动态批量模型作为混合整数规划问题。 (图片由作者提供)。

建模

首先,让我们导入所使用的 Python 库:

import numpy as np
import pandas as pd
import pyomo.environ as pyo
import matplotlib.pyplot as plt

现在,让我们按照原始文章(Wagner & Whitin, 1958)中的示例来实例化数据集。

months = np.arange(12, dtype=int) + 1
setup_cost = np.array([85, 102, 102, 101, 98, 114, 105, 86, 119, 110, 98, 114])
demand = np.array([69, 29, 36, 61, 61, 26, 34, 67, 45, 67, 79, 56])
inventory = np.ones(12)

dataset = pd.DataFrame(
    {"setup_cost": setup_cost, "inventory_cost": inventory, "demand": demand},
    index=months,
)

由于我们在这个示例中使用了 Pyomo,我们必须首先实例化 pyomo 模型。在这个示例中,我选择使用 ConcreteModel 方法,因为我发现它对于简单问题更直接。或者,也可以使用 AbstractModel。这两种方法在这里有更详细的描述。

model = pyo.ConcreteModel()

在这个问题中,唯一被考虑的集合是计划时间范围 T。让我们将其实例化为一个 pyomo 对象。

model.T = pyo.Set(initialize=list(dataset.index))

接下来,我们将实例化从问题数据集中导入的模型参数。它们如下:

  • d:每个时间 t 的需求

  • s:每个时间 t 的设定成本

  • h:每个时间 t 的持有库存成本

model.d = pyo.Param(model.T, initialize=dataset.demand)
model.s = pyo.Param(model.T, initialize=dataset.setup_cost)
model.h = pyo.Param(model.T, initialize=dataset.inventory_cost)

以及决策变量:

  • x:每个时间 t 生产的产品数量

  • y:二进制变量,标记每个时间 t 的设定成本

  • I:每个时间 t 的最终库存

model.x = pyo.Var(model.T, within=pyo.NonNegativeReals)
model.y = pyo.Var(model.T, within=pyo.Binary)
model.I = pyo.Var(model.T, within=pyo.NonNegativeReals)

库存平衡约束表示在某一时期 t 的库存等于前一时期末的库存加上生产量减去该时期的需求。我们在这里假设初始库存为零。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

库存平衡约束。 (图片由作者提供)。

def inventory_rule(model, t):
    if t == model.T.first():
        return model.I[t] == model.x[t] - model.d[t]
    else:
        return model.I[t] == model.I[model.T.prev(t)] + model.x[t] - model.d[t]

model.inventory_rule = pyo.Constraint(model.T, rule=inventory_rule)

现在让我们制定 big M 约束以识别具有设定成本的时期。我们的目的是,如果在时间 t 生产了任何数量的产品,则 y 应为 1,否则为 0。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

大 M 约束。 (图片由作者提供)。

通常,你可能会想选择一个足够大的 M 值,以避免从整数决策空间中去除可行解。这样可以在 分支定界算法 中产生更好的 best-bound 值,而不是选择任意大的值。

在这个问题中,只有在持有库存成本从时间 t-nt 小于或等于 t 时的设定成本时,才有意义在时间 t-n 生产一定需求量 d。因此,我们可以通过以下几行计算某一时刻 t 的“最大智能生产”:

def get_max_antecip(t, dataset=dataset):
    """
    Returns the first instant in which it might be
    intelligent to produce a certain demand
    """

    total_inv = 0
    d = dataset.demand[t]
    s = dataset.setup_cost[t]
    out = t

    for i in range(1, t - dataset.index[0] + 1):
        h = dataset.inventory_cost[t - i]
        if total_inv + h * d <= s:
            total_inv = total_inv + h * d
            out = t - i
        else:
            break

    return out

def get_max_prod(t, dataset=dataset):
    df = dataset.query(f"max_antecip <= {t} & index >= {t}")
    return df.demand.sum()

dataset["max_antecip"] = [get_max_antecip(t, dataset=dataset) for t in dataset.index]
dataset["max_prod"] = [get_max_prod(t, dataset=dataset) for t in dataset.index]

现在我们可以为每个时间段定义一个合适的 M 值,并实例化 big M 约束。

model.M = pyo.Param(model.T, initialize=dataset.max_prod)

def active_prod(model, t):
    return model.x[t] <= model.M[t] * model.y[t]

model.active_prod = pyo.Constraint(model.T, rule=active_prod)

最后,目标函数将计划时间范围内所有持有库存和设定成本相加。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

目标函数。 (图片由作者提供)。

def total_cost(model):
    holding = sum(model.h[t] * model.I[t] for t in model.T)
    setup = sum(model.s[t] * model.y[t] for t in model.T)
    return setup + holding

model.obj = pyo.Objective(rule=total_cost, sense=pyo.minimize)

为了解决模型,我使用了开源求解器 CBC。你可以从 AMPL这个链接 下载 CBC 二进制文件。你还可以在 这里 找到安装教程。由于 CBC 可执行文件已包含在我的系统 PATH 变量中,我可以在不指定可执行文件路径的情况下实例化求解器。如果你的系统没有包含,可通过关键字参数“executable”解析到你的可执行文件路径。

# executable=YOUR_PATH
solver = pyo.SolverFactory("cbc")

另外,也可以使用 GLPK 来解决这个问题(或任何其他与pyomo兼容的求解器)。最新的 GLPK 版本可以在 这里 找到,Windows 可执行文件可以在 这里 找到。

solver.solve(model, tee=True)

完成了!结果已存储在我们的模型中,并将在下一节中详细分析。

结果

首先,让我们打印简单场景的成本,其中每个需求都应该在其对应的瞬时生产。

# Obtain the maximum cost for comparison
max_cost = dataset.setup_cost.sum()
print(f"Maximum cost: {max_cost:.1f}")

这应该返回 1234.0。

接下来,让我们从目标函数中获取新的成本并比较结果:

opt_value = model.obj()
print(f"Best cost {opt_value}")
print(f"% savings {100 * (1 - opt_value / max_cost) :.2f}")

哇!新的成本是 864.0,节省了近 30%!

让我们在数据集中添加新列,并绘制生产与需求的关系,以便更好地可视化结果:

# Include production as a column
dataset["production"] = [model.x[t].value for t in dataset.index]

# Plot figure
fig, ax = plt.subplots(figsize=[6, 3], dpi=100)
x = dataset.index
width = 0.35
ax.bar(x - width/2, dataset.production, width, color="darkgreen", label="production")
ax.bar(x + width/2, dataset.demand, width, color="navy", label="demand")
ax.set_xticks(x)
ax.set_ylabel("Qtd")
ax.set_xlabel("t")
ax.legend()
fig.tight_layout()
plt.show()

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

计划期结果。(图像由作者提供)。

可以注意到,除了瞬时 10(其只有一个需求)之外,其他需求被组合在同一生产瞬时,以显著减少整体设置成本。

结论

在这篇文章中,动态批量模型(Wagner & Whitin, 1958)被形式化并解决为一个混合整数规划问题。与简单的解决方案相比,优化结果可以将总体成本减少 30%。在这种情况下,库存持有和设置成本得到了合理平衡。完整的解决方案可以在这个 git 仓库 中找到。

进一步阅读

对整数规划有更多兴趣的人可以参考 Wolsey (2020);有关运筹学的内容,可以参考 Winston & Goldberg (2004)。

分支限界算法是解决整数和混合整数问题中最常用的算法。有兴趣了解其机制的读者可以参考我之前的 Medium 文章。

参考文献

Bynum, M. L. 等,2021。Pyomo-optimization modeling in python. Springer。

Suwondo, E., & Yuliando, H. (2012). 动态批量问题:关于模型和高效算法的综述。农业工业期刊, 1(1), 36。

Wagner, H. M., & Whitin, T. M. (1958). 动态版本的经济批量模型。管理科学, 5(1), 89–96。

Winston, W. L. & Goldberg, J. B., 2004. 运筹学:应用与算法. 第 4 版. 加州贝尔蒙特:汤姆森·布鲁克斯/科尔贝尔蒙特。

Wolsey, L. A., 2020. 整数规划. 第 2 版. 约翰·威利与儿子公司。

使用大型语言模型的最简单方法?

原文:towardsdatascience.com/the-easiest-way-to-interact-with-language-models-4da158cfb5c5?source=collection_archive---------0-----------------------#2023-03-26

LangChain 概述

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传 Sophia Yang, Ph.D.

·

关注 发表在 Towards Data Science ·7 分钟阅读·2023 年 3 月 26 日

LangChain 是与大型语言模型交互和构建应用程序的最简单方法吗?它是一个开源工具,并且最近添加了 ChatGPT 插件。它提供了许多我认为有用的功能:

✨ 与包括 OpenAI、Cohere、Huggingface 在内的各种 LLM 提供商集成。

✨ 使用你自己的文档创建一个问答或文本摘要机器人

✨ 提供 OpenAI ChatGPT Retriever 插件

✨ 使用 LangChain Memory 处理聊天记录

✨ 将各种 LLM 结合在一起,并使用一系列工具,如 Google 搜索、Python REPL 等。

✨ 还有更多功能

更令人惊讶的是,LangChain 是开源的,这是一项社区努力。在这篇博客文章中,我将介绍我认为 LangChain 中有用的 6 个功能。

你可以在我的 Github repo here 找到这篇博客文章的代码。你需要安装所需的包,导入包,并设置 API 密钥(请参见 my notebook 中的第 1–3 个单元格)然后再运行下面的代码。

1. LangChain 与许多 LLM 提供商集成

使用相同的接口,你可以访问来自多个 LLM 提供商的许多 LLM 模型,包括 OpenAI、Cohere、AI21、Huggingface Hub、Azure OpenAI、Manifest、Goose AI、Writer、Banana、Modal、StochasticAI、Cerebrium、Petals、Forefront AI、PromptLayer OpenAI、Anthropic、DeepInfra 和自托管模型。

这是访问 OpenAI ChatGPT 模型和 GPT3 模型、Cohere 的 command-xlarge 模型,以及托管在 Hugging Face Hub 上的 Google 的 Flan T5 模型的示例。

chatgpt = ChatOpenAI(model_name='gpt-3.5-turbo')
gpt3 = OpenAI(model_name='text-davinci-003')
cohere = Cohere(model='command-xlarge')
flan = HuggingFaceHub(repo_id="google/flan-t5-xl")

然后我们可以给每个模型提供一个提示,看看它们返回什么。需要注意的是,对于聊天模型,我们可以指定消息是人类消息、AI 消息还是系统消息。这就是为什么对于 ChatGPT 模型,我将我的消息指定为人类消息。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

2. 外部文档的问答

你对使用外部文档构建问答机器人感兴趣吗?你的文档保存在 PDF、txt 文件,甚至 Notion 中吗?那如果我还想要基于 YouTube 视频进行问答怎么办?

没问题,LangChain 能满足你的需求。LangChain 提供了许多文档加载器:文件加载器、目录加载器、Notion、ReadTheDocs、HTML、PDF、PowerPoint、电子邮件、GoogleDrive、Obsidian、Roam、EverNote、YouTube、Hacker News、GitBook、S3 文件、S3 目录、GCS 文件、GCS 目录、Web 基础、IMSDb、AZLyrics、College Confidential、Gutenberg、Airbyte Json、CoNLL-U、iFixit、Notebook、Copypaste、CSV、Facebook Chat、图片、Markdown、SRT、Telegram、URL、Word 文档、Blackboard。你有其他没有列出的来源吗?因为 LangChain 是开源的,你可以贡献并添加到 LangChain 中!

这里是一个示例,我们使用 TextLoader 加载本地 txt 文件,创建 VectoreStore 索引,并查询你的索引。直观地说,你的文档将被拆分并嵌入到向量中,并存储在向量数据库中。查询将找到在向量数据库中最接近问题向量的向量。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

令人兴奋的消息是 LangChain 最近集成了ChatGPT Retrieval Plugin,因此人们可以使用这个检索器代替索引。索引和检索器有什么区别?根据LangChain, “索引是一种支持高效搜索的数据结构,而检索器是使用索引来查找并返回与用户查询相关的文档的组件。索引是检索器执行其功能所依赖的关键组件。”

3. 保留/总结聊天历史

当您与 ChatGPT 聊天时,它不会保留您的聊天历史记录。您需要将聊天内容复制并粘贴到新的提示中,以便它能够了解历史记录。LangChain 通过提供处理聊天历史记录的几种不同选项来解决这个问题——保留所有对话、保留最新的 k 次对话、总结对话以及上述选项的组合。

这是一个示例,我们使用ConversationBufferWindowMemory来保持最新的 1 轮对话,使用ConversationSummaryMemory来总结之前的对话,并使用CombinedMemory来结合这两种方法:

from langchain.llms import OpenAI
from langchain.prompts import PromptTemplate
from langchain.chains import ConversationChain
from langchain.memory import ConversationBufferWindowMemory, CombinedMemory, ConversationSummaryMemory

conv_memory = ConversationBufferWindowMemory(
    memory_key="chat_history_lines",
    input_key="input",
    k=1
)

summary_memory = ConversationSummaryMemory(llm=OpenAI(), input_key="input")
# Combined
memory = CombinedMemory(memories=[conv_memory, summary_memory])
_DEFAULT_TEMPLATE = """The following is a friendly conversation between a human and an AI. The AI is talkative and provides lots of specific details from its context. If the AI does not know the answer to a question, it truthfully says it does not know.

Summary of conversation:
{history}
Current conversation:
{chat_history_lines}
Human: {input}
AI:"""
PROMPT = PromptTemplate(
    input_variables=["history", "input", "chat_history_lines"], template=_DEFAULT_TEMPLATE
)
llm = OpenAI(temperature=0)
conversation = ConversationChain(
    llm=llm, 
    verbose=True, 
    memory=memory,
    prompt=PROMPT
)

这里是结果,您可以看到对话摘要以及最新对话传递到提示中的内容。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

4. 链接

有时您可能想要链式连接不同的 LLM。例如,

  • 您可能希望第一个 LLM 的输出作为第二个 LLM 的输入。

  • 或者,您可能希望总结第一和第二个 LLM 的输出,并将摘要作为输入传递给第三个 LLM。

  • 或者,您可能希望将您的语言模型与另一个实用工具链进行链式操作,以进行数学运算或 Python 操作。

这是最基本的SimpleSequentialChain方法的一个示例。第一个链要求 ChatGPT 为一家制造彩色袜子的公司提供一个好的产品名称。ChatGPT 返回了“Rainbow Sox Co.”。第二个链使用这个名称作为输入,并要求 ChatGPT 为公司“Rainbow Sox Co.”编写一句广告语。ChatGPT 写道:“让 Rainbow Sox Co.为您的步伐增添一抹色彩!”

from langchain.chat_models import ChatOpenAI
from langchain import LLMChain
from langchain.prompts.chat import (
    ChatPromptTemplate,
    HumanMessagePromptTemplate,
)
human_message_prompt = HumanMessagePromptTemplate(
        prompt=PromptTemplate(
            template="What is a good name for a company that makes {product}?",
            input_variables=["product"],
        )
    )
chat_prompt_template = ChatPromptTemplate.from_messages([human_message_prompt])
chat = ChatOpenAI(temperature=0.9)
chain = LLMChain(llm=chat, prompt=chat_prompt_template)
second_prompt = PromptTemplate(
    input_variables=["company_name"],
    template="Write a catchphrase for the following company: {company_name}",
)
chain_two = LLMChain(llm=llm, prompt=second_prompt)
from langchain.chains import SimpleSequentialChain
overall_chain = SimpleSequentialChain(chains=[chain, chain_two], verbose=True)

# Run the chain specifying only the input variable for the first chain.
catchphrase = overall_chain.run("colorful socks")
print(catchphrase)

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

5. 代理

代理可以访问语言模型和一套工具,例如 Google 搜索、Python REPL、数学计算器等。代理使用 LLM 和各种框架来决定为哪些任务使用哪些工具。这与 ChatGPT 插件非常相似。以下是一个示例:

在这个示例中,我们加载了两个工具:serpapi 用于 Google 搜索,llm-math 用于数学计算。我们询问了“莱昂纳多·迪卡普里奥的女朋友是谁?她目前的年龄提高到 0.43 的幂是多少?”语言模型首先理解了问题,并决定执行搜索操作以触发搜索 API 调用。然后它从搜索代理那里获取了结果,决定使用 llm-math 进行计算,最后得到了最终结果。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

在另一个例子中,我们可以使用 Python REPL 执行 Python 代码进行计算,而不是使用 llm-math 作为计算器:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

6. ChatGPT 插件

ChatGPT 插件刚刚在本周推出,而 LangChain 已经添加了 ChatGPT 插件功能。这里是一个例子,我们使用 AIPluginTool 从购物网站加载插件。这个插件与 OpenAI 使用的完全相同。当我们问“在 Klarna 上有哪些 T 恤?”时,LLM 决定使用 Klarna 插件并得到了一个 OpenAI 规范系列的响应,然后决定发起请求以获取数据,最终返回了很好的答案。

from langchain.chat_models import ChatOpenAI
from langchain.agents import load_tools, initialize_agent
from langchain.tools import AIPluginTool
tool = AIPluginTool.from_plugin_url("https://www.klarna.com/.well-known/ai-plugin.json")
llm = ChatOpenAI(temperature=0)
tools = load_tools(["requests"] )
tools += [tool]

agent_chain = initialize_agent(tools, llm, agent="zero-shot-react-description", verbose=True)

agent_chain.run("what t shirts are available in klarna?")

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

结论

总体而言,我对 LangChain 的能力和社区快速添加新功能的速度感到非常印象深刻。还有许多其他功能我在这篇博客文章中没有提到,包括数据增强、少量学习、评估、模型比较等。我希望这篇博客文章能帮助你更好地理解各种 LangChain 功能。祝你探索愉快!

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

照片由 Aida L 提供,发布在 Unsplash

. . .

Sophia Yang 于 2023 年 3 月 26 日发布

Sophia Yang 是 Anaconda 的高级数据科学家。通过 LinkedInTwitterYouTube 与我联系,并加入 DS/ML 读书俱乐部 ❤️

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值