zl程序教程

您现在的位置是:首页 >  硬件

当前栏目

【构建ML驱动的应用程序】第 3 章 :构建您的第一个端到端管道

驱动应用程序 构建 第一个 ML 管道 端到
2023-09-14 09:14:47 时间

   🔎大家好,我是Sonhhxg_柒,希望你看完之后,能对你有所帮助,不足请指正!共同学习交流🔎

📝个人主页-Sonhhxg_柒的博客_CSDN博客 📃

🎁欢迎各位→点赞👍 + 收藏⭐️ + 留言📝​

📣系列专栏 - 机器学习【ML】 自然语言处理【NLP】  深度学习【DL】

 🖍foreword

✔说明⇢本人讲解主要包括Python、机器学习(ML)、深度学习(DL)、自然语言处理(NLP)等内容。

如果你对这个系列感兴趣的话,可以关注订阅哟👋

文章目录

最简单的脚手架

ML 编辑器的原型

解析和清理数据

分词文本

生成特征

测试您的工作流程

用户体验

建模结果

找到影响瓶颈

ML 编辑器原型评估

模型

用户体验

结论


第一部分,我们首先介绍如何从产品需求到候选建模方法。然后,我们进入规划阶段,描述了如何找到相关资源并利用它们来制定构建内容的初步计划。最后,我们讨论了如何构建功能系统的初始原型是取得进展的最佳方式。这就是我们将在本章中介绍的内容。

第一次迭代将在设计上乏善可陈。它的目标是让我们拥有管道的所有部分,以便我们可以优先考虑接下来要改进的部分。拥有完整的原型是识别产品的最简单方法Monica Rogati 在“Monica Rogati:如何选择和确定 ML 项目的优先级”中描述的影响瓶颈。

让我们从构建可以根据输入生成预测的最简单管道开始。

最简单的脚手架

“从一个简单的管道开始”中,我们描述了大多数机器学习模型是如何构成的两条管道,训练和推理。训练使我们能够生成高质量的模型,而推理是将结果提供给用户。有关训练和推理之间的区别的更多信息,请参阅“从简单的管道开始” 。

为了应用程序的第一个原型,我们将专注于能够向用户交付结果。这意味着在我们在第 2 章中描述的两个管道中,我们将从推理管道开始。这将使我们能够快速检查用户如何与模型的输出进行交互,从而收集有用的信息以简化模型的训练。

如果我们只关注推理,暂时忽略训练。由于我们不是在训练模型,因此我们可以编写一些简单的规则。编写此类规则或启发式方法通常是一种很好的入门方式。这是构建原型的最快方法,让我们可以立即看到完整应用程序的简化版本。

如果我们无论如何都打算实施 ML 解决方案(正如我们稍后将在本书中介绍的那样),这可能看起来是多余的,但它是一个关键的强制函数,可以让我们面对我们的问题并设计一组关于如何最好地解决它的初始假设.

构建、验证和更新关于数据建模最佳方式的假设是迭代模型构建过程的核心部分,它甚至在我们构建第一个模型之前就开始了!

笔记

这里是我所指导的研究员使用的项目中的几个很好的启发式示例洞察数据科学。

  • 代码质量评估:何时为了构建一个旨在根据代码样本预测编码员在 HackerRank(一个竞争性编码网站)上是否表现良好的模型,Daniel 首先计算左括号和右括号、方括号和大括号的数量。

    在大多数正常工作的代码中,左括号和右括号的计数是匹配的,因此这条规则被证明是一个非常强大的基线。此外,这让他有直觉,将他的建模重点放在使用抽象语法树来捕获有关代码的更多结构信息上。

  • 树木计数:当试图从卫星图像中计算城市中的树木时,在查看了一些数据后,迈克首先设计了一个规则来估计树木密度,该规则基于计算给定图像中绿色像素的比例。

    事实证明,这种方法适用于分散开来的树木,但当涉及到成片的树林时就失败了。同样,这有助于定义下一个建模步骤,重点是构建可以处理密集分组树的管道。

绝大多数 ML 项目应该从类似的启发式开始。关键是要记住基于专家知识和数据探索来设计它,并用它来确认初始假设并加速迭代。

有了启发式方法后,就可以创建一个管道来收集输入、对其进行预处理、对其应用规则并提供结果。这可以像您可以从终端调用的 Python 脚本一样简单,也可以是收集用户摄像头画面然后提供实时结果的 Web 应用程序。

这里的重点是为您的产品做我们为您的 ML 方法所做的同样的事情,尽可能地简化它,并构建它,以便您拥有一个简单的功能版本。这通常被称为 MVP(最小可行产品),是一种久经考验的方法,可以尽快获得有用的结果。

ML 编辑器的原型

为了在我们的 ML 编辑器中,我们将利用常见的编辑建议来制定一些关于什么是好问题或坏问题的规则,并将这些规则的结果显示给用户。

对于从命令行获取用户输入并返回建议的项目的最小版本,我们只需要编写四个函数,如下所示:

input_text = parse_arguments()
processed = clean_input(input_text)
tokenized_sentences = preprocess_input(processed)
suggestions = get_suggestions(tokenized_sentences)

让我们深入了解它们中的每一个!我们将使参数解析器保持简单,并从用户那里获取一串文本开始,不带任何选项。您可以在本书的GitHub 存储库中找到该示例和所有其他代码示例的源代码。

解析和清理数据

首先,我们只需解析来自命令行的传入数据。这在 Python 中编写起来相对简单。

def parse_arguments():
    """

    :return: The text to be edited
    """
    parser = argparse.ArgumentParser(
        description="Receive text to be edited"
    )
    parser.add_argument(
        'text',
        metavar='input text',
        type=str
    )
    args = parser.parse_args()
    return args.text

每当模型根据用户输入运行时,您应该从验证和验证它开始!在我们的例子中,用户将输入数据,因此我们将确保他们的输入包含我们可以解析的字符。为了清理我们的输入,我们将删除非 ASCII 字符。这不应该过多地限制我们用户的创造力,并允许我们对文本中的内容做出合理的假设。

def clean_input(text):
    """

    :param text: User input text
    :return: Sanitized text, without non ascii characters
    """
    # To keep things simple at the start, let's only keep ASCII characters
    return str(text.encode().decode('ascii', errors='ignore'))

现在,我们需要预处理我们的输入并提供建议。为了让我们开始,我们将依靠我们在“最简单的方法:成为算法”中提到的一些关于文本分类的现有研究。这将涉及对“告诉”和“说”等单词进行计数,并计算音节、单词和句子的汇总统计数据以估计句子的复杂性。

要计算单词级别的统计信息,我们需要能够从句子中识别单词。在自然语言处理领域,这被称为标记化

分词文本

代币化这并不简单,而且您能想到的大多数简单方法(例如根据空格或句点将我们的输入拆分为单词)在实际文本中都将失败,因为单词的分隔方式多种多样。考虑这句话,斯坦福大学的NLP 类作为示例提供,例如:

“Mr. O’Neill thinks that the boys’ stories about Chile’s capital aren’t amusing.”

由于存在具有各种含义的句点和撇号,大多数简单的方法在这句话上都会失败。我们将利用流行的开源库nltk而不是构建我们自己的分词器,它允许我们通过两个简单的步骤完成此操作,如下所示:

def preprocess_input(text):
    """

    :param text: Sanitized text
    :return: Text ready to be fed to analysis, by having sentences and
    words tokenized
    """
    sentences = nltk.sent_tokenize(text)
    tokens = [nltk.word_tokenize(sentence) for sentence in sentences]
    return tokens

一旦我们的输出经过预处理,我们就可以使用它来生成有助于判断问题质量的特征。

生成特征

最后一步是编写一些我们可以用来向用户提供建议的规则。对于这个简单的原型,我们将从计算一些常用动词和连接词的频率开始,然后计算副词的使用情况并确定Flesch 可读性分数。然后,我们会将这些指标的报告返回给我们的用户:

def get_suggestions(sentence_list):
    """
    Returns a string containing our suggestions
    :param sentence_list: a list of sentences, each being a list of words
    :return: suggestions to improve the input
    """
    told_said_usage = sum(
        (count_word_usage(tokens, ["told", "said"]) for tokens in sentence_list)
    )
    but_and_usage = sum(
        (count_word_usage(tokens, ["but", "and"]) for tokens in sentence_list)
    )
    wh_adverbs_usage = sum(
        (
            count_word_usage(
                tokens,
                [
                    "when",
                    "where",
                    "why",
                    "whence",
                    "whereby",
                    "wherein",
                    "whereupon",
                ],
            )
            for tokens in sentence_list
        )
    )
    result_str = ""
    adverb_usage = "Adverb usage: %s told/said, %s but/and, %s wh adverbs" % (
        told_said_usage,
        but_and_usage,
        wh_adverbs_usage,
    )
    result_str += adverb_usage
    average_word_length = compute_total_average_word_length(sentence_list)
    unique_words_fraction = compute_total_unique_words_fraction(sentence_list)

    word_stats = "Average word length %.2f, fraction of unique words %.2f" % (
        average_word_length,
        unique_words_fraction,
    )
    # Using HTML break to later display on a webapp
    result_str += "<br/>"
    result_str += word_stats

    number_of_syllables = count_total_syllables(sentence_list)
    number_of_words = count_total_words(sentence_list)
    number_of_sentences = len(sentence_list)

    syllable_counts = "%d syllables, %d words, %d sentences" % (
        number_of_syllables,
        number_of_words,
        number_of_sentences,
    )
    result_str += "<br/>"
    result_str += syllable_counts

    flesch_score = compute_flesch_reading_ease(
        number_of_syllables, number_of_words, number_of_sentences
    )

    flesch = "%d syllables, %.2f flesch score: %s" % (
        number_of_syllables,
        flesch_score,
        get_reading_level_from_flesch(flesch_score),
    )

    result_str += "<br/>"
    result_str += flesch

    return result_str

瞧,我们现在可以从命令行调用我们的应用程序并实时查看其结果。它还不是很有用,但我们有一个起点可以测试和迭代,我们接下来会做。

测试您的工作流程

现在我们已经构建了这个原型,我们可以测试我们关于构建问题的方式以及我们提出的解决方案的有用性的假设。在本节中,我们将查看初始规则的客观质量,并检查我们是否以有用的方式呈现输出。

正如 Monica Rogati 之前分享的那样,“通常情况下,即使您的模型很成功,您的产品也会死掉。” 如果我们选择的方法在衡量问题质量方面表现出色,但我们的产品没有向用户提供任何改进写作的建议,那么尽管我们的方法质量很高,我们的产品也不会有用。查看我们完整的管道,让我们评估当前用户体验的有用性和我们手工制作模型的结果。

用户体验

让我们首先检查我们的产品使用起来是否令人满意,这与我们模型的质量无关。换句话说,如果我们想象我们最终会得到一个表现足够好的模型,那么这是向我们的用户呈现结果的最有用的方式吗?

例如,如果我们要进行树木普查,我们可能希望将我们的结果作为对整个城市的长期分析的总结来呈现。我们可能希望包括报告的树木数量、每个社区的细分统计数据以及黄金标准测试集上的误差度量。

换句话说,我们希望确保我们呈现的结果是有用的(或者如果我们改进我们的模型将会有用)。当然,另一方面,我们也希望我们的模型表现良好。这是我们将评估的下一个方面。

建模结果

我们在“衡量成功”中提到了关注正确指标的价值。有早期的工作原型将使我们能够识别和迭代我们选择的指标,以确保它们代表产品成功。

例如,如果我们正在构建一个系统来帮助用户搜索附近的租车,我们可能会使用诸如贴现累积收益 (DCG)。DCG 通过在最相关的项目比其他项目更早返回时输出最高分数来衡量排名质量(有关排名指标的更多信息,请参阅关于 DCG 的维基百科文章)。在最初构建我们的工具时,我们可能假设我们希望至少有一个有用的建议出现在我们的前五个结果中。因此,我们使用 5 分的 DCG 对我们的模型进行评分。但是,当让用户试用该工具时,我们可能会注意到用户只会考虑显示的前三个结果。在那种情况下,我们应该将我们的成功指标从 DCG 的 5 更改为 3。

同时考虑用户体验和模型性能的目标是确保我们在最具影响力的方面开展工作。如果您的用户体验很差,改进您的模型也无济于事。事实上,您可能会意识到使用完全不同的模型会更好!让我们看两个例子。

找到影响瓶颈

查看建模结果和当前产品展示的目的是确定下一步要解决的挑战。大多数时候,这将意味着迭代我们向用户呈现结果的方式(这可能意味着改变我们训练模型的方式)或通过识别关键故障点来提高模型性能。

尽管我们将在第 III 部分更深入地进行错误分析,我们应该确定故障模式和解决它们的适当方法。重要的是要确定要处理的最具影响力的任务是在建模领域还是在产品领域,因为它们各自需要不同的补救措施。让我们看一个例子:

在产品方面

让我们假设你已经建立了一个模型来查看研究论文的图像并预测它们是否会被顶级会议接受(参见 Jia-Bin Huang 的论文“Deep Paper Gestalt”,它解决了这个问题)。但是,您注意到仅向用户返回拒绝概率并不是最令人满意的输出。在这种情况下,改进您的模型将无济于事。专注于从模型中提取建议是有意义的,这样我们就可以帮助我们的用户改进他们的论文并增加他们被接受的机会。

在模型方面

你有建立了一个信用评分模型,并注意到在所有其他因素相同的情况下,它将更高的违约风险分配给某个族群。这可能是由于您一直使用的训练数据存在偏差,因此您应该收集更具代表性的数据并构建新的清洁和增强管道来尝试解决这个问题。在这种情况下,无论您以何种方式呈现结果,都需要固定模型。像这样的例子很常见,这也是为什么您应该始终比聚合指标更深入地研究并查看模型对不同数据片段的影响的原因。这就是我们将在第 5 章中做的事情。

为了进一步说明这一点,让我们为我们的 ML 编辑器完成这个练习。

ML 编辑器原型评估

让我们看看我们的初始管道在用户体验和模型性能方面的表现如何。让我们首先向我们的应用程序添加一些输入。我们将从测试一个简单的问题、一个复杂的问题和一个完整的段落开始。

由于我们使用的是阅读轻松评分,因此理想情况下,我们希望我们的工作流程能够为简单句子返回高分,为复杂句子返回低分,并提供改进段落的建议。让我们通过我们的原型实际运行几个例子。

简单的问题:

$ python ml_editor.py  "Is this workflow any good?"
Adverb usage: 0 told/said, 0 but/and, 0 wh adverbs
Average word length 3.67, fraction of unique words 1.00
6 syllables, 5 words, 1 sentences
6 syllables, 100.26 flesch score: Very easy to read

令人费解的问题:

$ python ml_editor.py  "Here is a needlessly obscure question, that"\
"does not provide clearly which information it would"\
"like to acquire, does it?"

Adverb usage: 0 told/said, 0 but/and, 0 wh adverbs
Average word length 4.86, fraction of unique words 0.90
30 syllables, 18 words, 1 sentences
30 syllables, 47.58 flesch score: Difficult to read

整个段落(你会从前面认出):

$ python ml_editor.py "Ideally, we would like our workflow to return a positive"\
" score for the simple sentence, a negative score for the convoluted one, and "\
"suggestions for improving our paragraph. Is that the case already?"
Adverb usage: 0 told/said, 1 but/and, 0 wh adverbs
Average word length 4.03, fraction of unique words 0.76
52 syllables, 33 words, 2 sentences
52 syllables, 56.79 flesch score: Fairly difficult to read

让我们使用刚刚定义的两个方面来检查这些结果。

模型

目前尚不清楚我们的结果是否与我们认为的高质量写作相符。令人费解的句子和整个段落的可读性得分相似。现在,我将首先承认我的文章有时难以阅读,但前面的段落比我们之前测试过的令人费解的句子更容易理解。

我们从文本中提取的属性不一定与“好文”最相关。这通常是由于没有足够清楚地定义成功:给定两个问题,我们怎么能说一个比另一个好?当我们在下一章构建数据集时,我们会更清楚地定义它。

正如预期的那样,我们有一些建模工作要做,但我们是否以有用的方式呈现结果?

用户体验

从前面显示的结果中可以看出两个问题。我们返回的信息既压倒性又无关紧要。我们产品的目标是为我们的用户提供可操作的建议。功能和可读性分数是质量指标,但不会帮助用户决定如何改进他们的提交。我们可能希望将我们的建议归结为一个分数,以及改进它的可行建议。

例如,我们可以建议一般性的更改,例如使用更少的副词,或者通过建议单词和句子级别的更改在更精细的级别上工作。理想情况下,我们可以通过突出显示或下划线输入中需要用户注意的部分来呈现结果。我在图 3-1中添加了它的外观模型。

图 3-1。更具可操作性的写作建议

即使我们无法直接突出显示输入字符串中的建议,我们的产品也可以从提供类似于图 3-1右侧的建议中获益,这些建议比分数列表更具可操作性。

结论

我们已经构建了一个初始推理原型,并用它来评估我们的启发式方法和我们产品的工作流程的质量。这使我们能够缩小我们的性能标准并迭代我们希望向用户呈现结果的方式。

对于 ML 编辑器,我们了解到,我们既应该通过提供可操作的建议来专注于提供更好的用户体验,又应该通过查看数据来更清楚地定义好问题的构成要素来改进我们的建模方法。

在前三章中,我们使用我们的产品目标来定义要采用的初始方法,探索现有资源来为我们的方法制定计划,并构建初始原型来验证我们的计划和假设。

现在,是时候深入研究 ML 项目中通常最容易被忽视的部分了——探索我们的数据集。在第 4 章中,我们将了解如何收集初始数据集、评估其质量并迭代标记其子集以帮助指导我们的特征生成和建模决策。