TowardsDataScience 2023 博客中文翻译(二百零九)

原文:TowardsDataScience

协议:CC BY-NC-SA 4.0

为你的下一个 LangChain 项目学习基础知识

原文:towardsdatascience.com/learning-the-ropes-for-your-next-langchain-project-3529d585e320?source=collection_archive---------12-----------------------#2023-07-06

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

·

关注 发表在 Towards Data Science · 作为 新闻通讯 发送 · 3 分钟阅读 · 2023 年 7 月 6 日

去年,大型语言模型作为有趣且偶尔搞笑的实验工具进入了主流。谁没有挑战 ChatGPT 发明一个新的“敲门笑话”或作一首关于小狗的莎士比亚十四行诗呢?

随着大型语言模型(LLMs)的强大功能越来越明显,它们的局限性也变得愈加显著。机器学习从业者和应用程序开发者很快意识到,对于那些无法获取最新信息且相互隔离的模型来说,你能做的事情是有限的。

幸运的是,我们已经有多个工具和平台旨在通过将 LLMs 连接到外部数据源和其他模型来克服这些限制,从而为其打开新的创造性用例。LangChain 已成为这一领域的领先选项之一,在发布后的几个月里,TDS 作者们一直在探索其能力和痛点。你将在我们本周汇集的实际动手资源中找到一些最好的作品——你准备好卷起袖子了吗?

  • Leonie Monigatti 的初学者友好入门书是任何刚刚迈出第一步 使用 LangChain 的人必读的文章。它包括了你需要了解的所有主要术语和概念的清晰解释。

  • 如果你准备更深入了解,Dominik Polzer 最近整理了一份全面的指南,提供了有关 LLMs 如何工作以及为什么一开始需要像 LangChain 这样的框架的有用背景,并且详细描述了逐步构建应用程序的过程

  • 开发人员已经提出了多种将模型、API 和 ML 平台与 LangChain 结合的方法。Wen Yang教程涵盖了 OPL,这是一种流行的技术栈,它将 OpenAI 模型(如 GPT-4)、Pinecone(一个向量数据库工具)和 LangChain 汇聚在一起,并展示了如何利用它创建具有特定领域知识的聊天机器人。

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

图片由 Svetlana GumerovaUnsplash 上拍摄

  • 如果你已经构建了一个特定模型的概念验证,但在尝试将其转变为真正的应用程序时感到困惑,Lily Hughes-Robinson 可以帮助你,提供了一个关于将项目重构为 LangChain 兼容的过程的简明解释。

  • 如果你还没准备好停止尝试,再来看另一个详细的项目过程怎么样?帅郭邀请我们一起跟随构建一个依赖于两个聊天机器人相互交流的语言学习应用程序的过程。

你们中的一些人可能需要暂时休息一下与 LLM 相关的内容;我们理解——我们在这里提供帮助!以下是一些值得探索的其他话题的精彩文章:

  • 我们以一系列引人深思的文章开启了新的一月,这些文章位于数据科学、机器学习和气候变化的交汇点。

  • AI 生成的音频通常需要大量的计算资源。克里斯托弗·兰斯库特的首篇 TDS 文章展示了如何在你的笔记本电脑上训练模型并生成音频波形扩散的声音—。

  • 多组分析可以成为医疗、HR、市场营销等领域的强大工具。劳拉·卡斯特罗-希洛的最新贡献是了解在实际场景中实现 MGA 的方法的绝佳起点。

  • 想要进行一个技术性(但易于理解)的深度探讨?佩吉·张提供了一篇关于开源搜索引擎 Lucene 中整数编码和压缩的深入阅读。

  • 如果你计划扩展你的计算机视觉工具包,德鲁夫·马塔尼纳雷什的关于 PyTorch 中图像分割的四部分系列将激励你停止拖延并开始编码。

  • 从既是数据科学家又是心理学家的角度接近 GPT 模型,马尔滕·格鲁滕多斯特仔细研究了“这些模型的行为,我们希望它们如何表现,以及我们如何推动这些模型像我们一样行为。”

感谢您支持我们的作者!如果您喜欢在 TDS 上阅读的文章,请考虑成为 Medium 会员——这将解锁我们整个档案(以及 Medium 上的所有其他帖子)。

直到下一个变量,

TDS 编辑

学习变压器代码第一部分第二部分——GPT 亲密接触

原文:towardsdatascience.com/learning-transformers-code-first-part-2-gpt-up-close-and-personal-1635b52ae0d7

深入研究通过 nanoGPT 生成预训练变压器

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

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

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

图片由 Luca Onniboni 提供,来源于 Unsplash

欢迎来到我的项目的第二部分,在这里我将深入研究变压器和基于 GPT 的模型的复杂性,使用 TinyStories 数据集nanoGPT,所有这些都在一台老旧的游戏笔记本电脑上进行训练。在第一部分中,我准备了输入到字符级生成模型的数据集。你可以在下面找到第一部分的链接。

链接 [## 学习变压器代码第一部分

新系列的第一部分,我努力使用 nanoGPT 学习变压器代码

链接

在这篇文章中,我的目标是剖析 GPT 模型、其组件及其在 nanoGPT 中的实现。我选择了 nanoGPT,因为它是一个约 300 行代码的 GPT 模型的直接 Python 实现,且其训练脚本也同样易于理解。掌握必要的背景知识后,人们可以通过阅读源代码快速理解 GPT 模型。坦白说,当我第一次检查代码时,我缺乏这种理解。有些材料仍然让我感到困惑。然而,我希望凭借我所学到的知识,这个解释能够为那些希望直观理解 GPT 风格模型内部运作的人提供一个起点。

在为这篇文章做准备时,我阅读了各种论文。最初,我以为仅仅阅读奠基性论文“Attention is All You Need”就足以让我对这个领域有足够的了解。这是一个天真的假设。虽然这篇论文确实介绍了变换器模型,但后续的论文才将其适应于更高级的任务,如文本生成。“AIAYN” 只是一个更广泛主题的介绍。尽管如此,我想起了一篇 HackerNews 上的文章,其中提供了一个阅读清单以全面理解大语言模型。经过快速搜索,我找到了这篇文章在这里。我没有按顺序阅读所有内容,但我打算在完成这一系列之后重新审视这个阅读清单,继续我的学习之旅。

话虽如此,让我们开始吧。要详细理解 GPT 模型,我们必须从变换器开始。变换器使用一种称为缩放点积注意力的自注意力机制。以下解释来源于这篇关于缩放点积注意力的深刻文章,我推荐阅读,以便更深入地理解。基本上,对于输入序列的每个元素(第 i 个元素),我们希望将输入序列乘以一个加权平均值,该加权平均值由序列中所有元素与第 i 个元素的点积计算得出,然后应用 softmax,以使权重值在 0 和 1 之间。在原始的“Attention is All You Need”论文中,这些输入被称为查询(整个序列)、(第 i 个元素的向量)和(也是整个序列)。传递给注意力机制的权重被初始化为随机值,并随着神经网络中更多的迭代而学习。

nanoGPT 实现了缩放点积注意力,并将其扩展为多头注意力,这意味着同时进行多个注意力操作。它还将其实现为 torch.nn.Module,这使得它可以与其他网络层组合。

import torch
import torch.nn as nn
from torch.nn import functional as F

class CausalSelfAttention(nn.Module):

    def __init__(self, config):
        super().__init__()
        assert config.n_embd % config.n_head == 0
        # key, query, value projections for all heads, but in a batch
        self.c_attn = nn.Linear(config.n_embd, 3 * config.n_embd, bias=config.bias)
        # output projection
        self.c_proj = nn.Linear(config.n_embd, config.n_embd, bias=config.bias)
        # regularization
        self.attn_dropout = nn.Dropout(config.dropout)
        self.resid_dropout = nn.Dropout(config.dropout)
        self.n_head = config.n_head
        self.n_embd = config.n_embd
        self.dropout = config.dropout
        # flash attention make GPU go brrrrr but support is only in PyTorch >= 2.0
        self.flash = hasattr(torch.nn.functional, 'scaled_dot_product_attention')
        if not self.flash:
            print("WARNING: using slow attention. Flash Attention requires PyTorch >= 2.0")
            # causal mask to ensure that attention is only applied to the left in the input sequence
            self.register_buffer("bias", torch.tril(torch.ones(config.block_size, config.block_size))
                                        .view(1, 1, config.block_size, config.block_size))

    def forward(self, x):
        B, T, C = x.size() # batch size, sequence length, embedding dimensionality (n_embd)

        # calculate query, key, values for all heads in batch and move head forward to be the batch dim
        q, k, v  = self.c_attn(x).split(self.n_embd, dim=2)
        k = k.view(B, T, self.n_head, C // self.n_head).transpose(1, 2) # (B, nh, T, hs)
        q = q.view(B, T, self.n_head, C // self.n_head).transpose(1, 2) # (B, nh, T, hs)
        v = v.view(B, T, self.n_head, C // self.n_head).transpose(1, 2) # (B, nh, T, hs)

        # causal self-attention; Self-attend: (B, nh, T, hs) x (B, nh, hs, T) -> (B, nh, T, T)
        if self.flash:
            # efficient attention using Flash Attention CUDA kernels
            y = torch.nn.functional.scaled_dot_product_attention(q, k, v, attn_mask=None, dropout_p=self.dropout if self.training else 0, is_causal=True)
        else:
            # manual implementation of attention
            att = (q @ k.transpose(-2, -1)) * (1.0 / math.sqrt(k.size(-1)))
            att = att.masked_fill(self.bias[:,:,:T,:T] == 0, float('-inf'))
            att = F.softmax(att, dim=-1)
            att = self.attn_dropout(att)
            y = att @ v # (B, nh, T, T) x (B, nh, T, hs) -> (B, nh, T, hs)
        y = y.transpose(1, 2).contiguous().view(B, T, C) # re-assemble all head outputs side by side

        # output projection
        y = self.resid_dropout(self.c_proj(y))
        return y

让我们进一步剖析这段代码,从构造函数开始。首先,我们验证注意力头的数量(n_heads)是否能整除嵌入的维度(n_embed)。这非常重要,因为当嵌入被分成每个头的部分时,我们希望覆盖所有的嵌入空间而没有任何空隙。接下来,我们初始化两个线性层,c_attc_projc_att 是保存所有用于计算缩放点积注意力的矩阵的工作空间的层,而 c_proj 存储计算的最终结果。在 c_att 中,嵌入维度被放大三倍,因为我们需要为注意力的三个主要组成部分查询留出空间。

我们还设置了两个丢弃层,attn_dropoutresid_dropout。丢弃层根据给定的概率随机使输入矩阵的元素失效。根据PyTorch 文档,这旨在减少模型的过拟合。config.dropout中的值是指定样本在丢弃层中被丢弃的概率。

我们通过验证用户是否可以访问 PyTorch 2.0 来完成构造函数,PyTorch 2.0 拥有优化版本的缩放点积注意力。如果可用,类将使用它;否则,我们将设置一个偏置掩码。这个掩码是注意力机制的可选掩码功能的一部分。torch.tril 方法生成一个将上三角部分转换为零的矩阵。当与 torch.ones 方法结合使用时,它有效地生成一个由 1 和 0 组成的掩码,注意力机制使用这个掩码生成给定样本输入的预期输出。

接下来,我们深入研究类的forward方法,其中应用了注意力算法。最初,我们确定输入矩阵的大小,并将其分成三个维度:Batch 大小,Time(或样本数量),Corpus(或嵌入大小)。nanoGPT 采用批处理学习过程,我们将在检查使用此注意力层的变换器模型时详细探讨。现在,了解我们处理的是批量数据就足够了。然后,我们将输入x传递到线性变换层c_attn,它将维度从n_embed扩展到三倍的n_embed。该变换的输出被分割成我们的qkv变量,这些变量是注意力算法的输入。随后,view方法被用来将这些变量中的数据重新组织成 PyTorch scaled_dot_product_attention 函数期望的格式。

当优化后的函数不可用时,代码会默认使用手动实现的缩放点积注意力。它首先计算qk矩阵的点积,其中k被转置以适应点积函数,然后结果会按k的大小的平方根进行缩放。接着,我们使用先前创建的偏置缓冲区来遮蔽缩放后的输出,将 0 替换为负无穷。接下来,对att矩阵应用softmax 函数,将负无穷转换回 0,并确保所有其他值在 0 和 1 之间缩放。然后,我们应用一个丢弃层以避免过拟合,然后计算att矩阵和v的点积。

无论使用何种缩放点积实现,多头输出都会在通过最终的 dropout 层之前重新组织成并排的形式,然后返回结果。这是在不到 50 行的 Python/PyTorch 代码中完成的注意力层实现。如果你没有完全理解上述代码,建议花些时间复习它,然后再继续阅读文章的其余部分。

在我们深入了解整合所有内容的 GPT 模块之前,我们需要两个额外的构建块。第一个是简单的多层感知机(MLP)——在“Attention is All You Need”论文中被称为前馈网络——和注意力块,它将注意力层与 MLP 结合在一起,完成论文中表示的基本 transformer 架构。两者都在 nanoGPT 的以下代码片段中实现。

class MLP(nn.Module):
    """
    Multi Layer Perceptron
    """

    def __init__(self, config):
        super().__init__()
        self.c_fc    = nn.Linear(config.n_embd, 4 * config.n_embd, bias=config.bias)
        self.gelu    = nn.GELU()
        self.c_proj  = nn.Linear(4 * config.n_embd, config.n_embd, bias=config.bias)
        self.dropout = nn.Dropout(config.dropout)

    def forward(self, x):
        x = self.c_fc(x)
        x = self.gelu(x)
        x = self.c_proj(x)
        x = self.dropout(x)
        return x

class Block(nn.Module):

    def __init__(self, config):
        super().__init__()
        self.ln_1 = LayerNorm(config.n_embd, bias=config.bias)
        self.attn = CausalSelfAttention(config)
        self.ln_2 = LayerNorm(config.n_embd, bias=config.bias)
        self.mlp = MLP(config)

    def forward(self, x):
        x = x + self.attn(self.ln_1(x))
        x = x + self.mlp(self.ln_2(x))
        return x

MLP 层,尽管在代码行数上看似简单,却给模型增加了一层额外的复杂性。实质上,Linear 层通过线性变换将每个输入层的元素与输出层的每个元素连接起来,传递它们之间的值。在上述代码中,我们从嵌入大小n_embed开始,作为参数的数量,然后在输出中将其四倍增加。这里的四倍增加是任意的;MLP 模块的目的是通过增加更多节点来增强网络的计算能力。只要在 MLP 开始时维度增加和在 MLP 结束时维度减少是等效的,得到相同的初始输入/最终输出维度,那么缩放数只是另一个超参数。另一个关键要素是激活函数。这种 MLP 实现包括两个线性层,使用 GELU 激活函数连接起来。原始论文使用的是 ReLU 函数,但 nanoGPT 使用 GELU 以确保与 GPT2 模型检查点的兼容性。

接下来,我们来研究 Block 模块。这个模块完成了我们在“Attention”论文中概述的变压器块。基本上,它通过一个归一化层将输入传递,然后传递到注意力层,再将结果加回输入中。这个加法的输出被再次归一化,然后传递到 MLP,并再次加回到自身。这一过程实现了“Attention”论文中描述的变压器解码器部分。在文本生成中,通常只使用解码器,因为它不需要根据输入序列以外的内容来调整解码器的输出。变压器最初设计用于机器翻译,这需要考虑输入标记编码和输出标记编码。然而,在文本生成中,只使用一个标记编码,从而消除了通过编码器进行交叉注意力的需求。nanoGPT 的作者 Andrei Karpathy 在他的视频中提供了这一点的全面解释,链接在此,视频地址在本系列的第一篇文章中。

最后,我们进入主要组件:GPT 模型。大约 300 行的文件中大部分内容都用于 GPT 模块。它管理一些有益的功能,如模型微调和为模型训练设计的实用工具(这是本系列下一篇文章的主题)。因此,我在下面展示了 nanoGPT 存储库中可用的简化版本。

class GPT(nn.Module):

    def __init__(self, config):
        super().__init__()
        assert config.vocab_size is not None
        assert config.block_size is not None
        self.config = config

        self.transformer = nn.ModuleDict(dict(
            wte = nn.Embedding(config.vocab_size, config.n_embd),
            wpe = nn.Embedding(config.block_size, config.n_embd),
            drop = nn.Dropout(config.dropout),
            h = nn.ModuleList([Block(config) for _ in range(config.n_layer)]),
            ln_f = LayerNorm(config.n_embd, bias=config.bias),
        ))
        self.lm_head = nn.Linear(config.n_embd, config.vocab_size, bias=False)
        # with weight tying when using torch.compile() some warnings get generated:
        # "UserWarning: functional_call was passed multiple values for tied weights.
        # This behavior is deprecated and will be an error in future versions"
        # not 100% sure what this is, so far seems to be harmless. TODO investigate
        self.transformer.wte.weight = self.lm_head.weight # https://paperswithcode.com/method/weight-tying

        # init all weights
        self.apply(self._init_weights)
        # apply special scaled init to the residual projections, per GPT-2 paper
        for pn, p in self.named_parameters():
            if pn.endswith('c_proj.weight'):
                torch.nn.init.normal_(p, mean=0.0, std=0.02/math.sqrt(2 * config.n_layer))

    def _init_weights(self, module):
        if isinstance(module, nn.Linear):
            torch.nn.init.normal_(module.weight, mean=0.0, std=0.02)
            if module.bias is not None:
                torch.nn.init.zeros_(module.bias)
        elif isinstance(module, nn.Embedding):
            torch.nn.init.normal_(module.weight, mean=0.0, std=0.02)

    def forward(self, idx, targets=None):
        device = idx.device
        b, t = idx.size()
        assert t <= self.config.block_size, f"Cannot forward sequence of length {t}, block size is only {self.config.block_size}"
        pos = torch.arange(0, t, dtype=torch.long, device=device) # shape (t)

        # forward the GPT model itself
        tok_emb = self.transformer.wte(idx) # token embeddings of shape (b, t, n_embd)
        pos_emb = self.transformer.wpe(pos) # position embeddings of shape (t, n_embd)
        x = self.transformer.drop(tok_emb + pos_emb)
        for block in self.transformer.h:
            x = block(x)
        x = self.transformer.ln_f(x)

        if targets is not None:
            # if we are given some desired targets also calculate the loss
            logits = self.lm_head(x)
            loss = F.cross_entropy(logits.view(-1, logits.size(-1)), targets.view(-1), ignore_index=-1)
        else:
            # inference-time mini-optimization: only forward the lm_head on the very last position
            logits = self.lm_head(x[:, [-1], :]) # note: using list [-1] to preserve the time dim
            loss = None

        return logits, loss

    @torch.no_grad()
    def generate(self, idx, max_new_tokens, temperature=1.0, top_k=None):
        """
        Take a conditioning sequence of indices idx (LongTensor of shape (b,t)) and complete
        the sequence max_new_tokens times, feeding the predictions back into the model each time.
        Most likely you'll want to make sure to be in model.eval() mode of operation for this.
        """
        for _ in range(max_new_tokens):
            # if the sequence context is growing too long we must crop it at block_size
            idx_cond = idx if idx.size(1) <= self.config.block_size else idx[:, -self.config.block_size:]
            # forward the model to get the logits for the index in the sequence
            logits, _ = self(idx_cond)
            # pluck the logits at the final step and scale by desired temperature
            logits = logits[:, -1, :] / temperature
            # optionally crop the logits to only the top k options
            if top_k is not None:
                v, _ = torch.topk(logits, min(top_k, logits.size(-1)))
                logits[logits < v[:, [-1]]] = -float('Inf')
            # apply softmax to convert logits to (normalized) probabilities
            probs = F.softmax(logits, dim=-1)
            # sample from the distribution
            idx_next = torch.multinomial(probs, num_samples=1)
            # append sampled index to the running sequence and continue
            idx = torch.cat((idx, idx_next), dim=1)

        return idx

让我们从类的构造函数开始。不同的层被组装到 PyTorch 的 ModuleDict 中,这提供了一些结构。我们从两个 Embedding 层开始:一个用于标记嵌入,另一个用于位置嵌入。nn.Embedding 模块旨在稀疏地填充值,相比其他层模块,它优化了存储能力。接下来是一个 dropout 层,然后是 n_layer 个 Block 模块,这些模块构成了我们的注意力层,然后是另一个单独的 dropout 层。lm_head Linear 层将注意力 Blocks 的输出减少到词汇表大小,作为 GPT 的主要输出,除了损失值之外。

一旦层定义完成,在我们开始训练模块之前,还需要进行额外的设置。在这里,Andrej 将位置编码的权重链接到输出层的权重上。根据代码注释中链接的论文,这样做是为了减少模型的最终参数,同时提高其性能。构造函数还会初始化模型的权重。由于这些权重将在训练过程中学习,它们被初始化为高斯分布的随机数,并且模块的偏置被设置为 0。最后,利用了来自GPT-2 论文的修改,其中任何残差层的权重都按层数的平方根进行缩放。

在通过网络进行前向传播时,batch大小和样本数量(此处为t)从输入大小中提取。然后,我们在训练设备上为将成为位置嵌入的内容创建内存。接着,我们将输入标记嵌入到后续的标记嵌入中wte。随后,在wpe层计算位置嵌入。这些嵌入在经过丢弃层之前会被相加。结果会通过每一个n_layer块进行处理并进行归一化。最后的结果传递到线性层lm_head,该层将嵌入权重转换为每个词汇表中标记的概率评分。

在计算损失时(例如,在训练期间),我们通过交叉熵计算预测标记与实际标记之间的差异。如果没有损失,则为None。损失和标记概率都会作为前向传播函数的一部分返回。

与之前的模块不同,GPT 模块有额外的方法。对我们最相关的是生成函数,这对于任何使用过生成模型的人来说都很熟悉。给定一组输入标记idx、一个max_new_tokens数量和一个temperature,它会生成max_new_tokens个标记。让我们深入了解它是如何完成的。首先,它修剪输入标记以适应block_size(其他人称之为上下文长度),如有必要,从输入的末尾开始采样。接下来,这些标记被输入到网络中,输出会根据输入的temperature进行缩放。温度越高,模型越具创造性,也更容易产生幻觉。较高的温度还会导致输出更难预测。接着,应用 softmax 将模型输出权重转换为 0 到 1 之间的概率。然后使用采样函数从概率中选择下一个标记,并将该标记添加到输入向量中,该向量会被反馈到 GPT 模型中进行下一个字符的生成。

感谢你耐心阅读这篇详尽的文章。虽然检查注释过的源代码是理解代码片段功能的宝贵方法,但亲自操作代码的各个部分和参数仍是无可替代的。为此,我提供了来自 nanoGPT 库的完整model.py源代码的链接。

[## nanoGPT/model.py at master · karpathy/nanoGPT

最简单、最快的中型 GPT 训练/微调库。 - nanoGPT/model.py at master ·…

github.com](https://github.com/karpathy/nanoGPT/blob/master/model.py?source=post_page-----1635b52ae0d7--------------------------------)

在即将发布的文章中,我们将探索 nanoGPT 的train.py脚本,并在 TinyStories 数据集上训练一个字符级模型。请在 Medium 上关注我,以确保不会错过!

我利用了大量资源来创建这篇文章,其中许多资源已经在本文及上一篇文章中链接了。然而,如果我不与大家分享这些资源以便进一步探索任何话题或替代解释概念,我就会失职。

使用 GPT-4 和自定义 LangChain 脚本编辑。

生成性人工智能的法律与伦理视角

原文:towardsdatascience.com/legal-and-ethical-perspectives-on-generative-ai-5e7ba2308469

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

照片由 charlesdeluvio 提供,来自 Unsplash

探索 AI 生成内容的法律和伦理影响

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

·发表在 Towards Data Science ·阅读时长 7 分钟·2023 年 8 月 25 日

2023 年是人工智能特别是生成性人工智能的崛起之年。该技术本身并不新颖;许多科技公司已经开发并推出了具有某些“生成性”组件的桌面/网页/移动应用程序——也许你还记得曾经玩过 SimSimi 聊天机器人 或使用过 Snapseed 的背景去除功能?

GPT-4 的推出于 2023 年 3 月引发了该技术的崛起和公众(非科技)兴趣。ChatGPT 用户界面提供了便捷的使用方式,而 GPT-4 的高质量内容生成能力引起了轰动,人们开始看到这项技术的利用潜力。

这种生成性人工智能的民主化——来自公众的快速采用和使用——带来了一些风险和影响,包括法律和伦理方面。从法律/监管角度来看,这仍然是未开发的领域,但了解这项技术的潜在影响仍然是一个好主意。

生成性人工智能的法律与伦理视角

生成性人工智能,通过人类的创造性使用,可以(有意或无意地)成为法律威胁的来源。到 2023 年 7 月,生成性人工智能的监管仍然是一个相对开放和有争议的领域,但风险是迫在眉睫的。

版权和所有权

引用维基百科,版权是一种知识产权,赋予其所有者对创作作品的复制、分发、改编、展示和表演的独占权,通常为期有限。在生成性 AI 中,这种版权的“所有者”方面变得不明确,因为在内容创作中涉及多个参与方,包括:

  • 编写提示的人员

  • 建立该 AI 模型的公司/组织

  • 在训练模型中使用其作品的艺术家

  • 生成艺术/内容的AI

每个参与方在内容创作中都扮演着重要角色,没有每个参与方的参与,内容就无法生成。具体来说,对于撰写提示的人,美国版权局明确表示AI 生成的内容(图像、文字、视频等)不能获得版权,考虑到缺乏人类创作AI 生成结果的可预测性

US 版权申请中的 Kristina Kashtanova明确说明了这一点,她创作了一部图画小说,其中书中的插图是使用 AI 图像生成工具 Midjourney 创建的。版权局提出的论点是“提示更像是建议而非命令,类似于雇佣艺术家创作图像时给出的通用方向”。简单来说,作者创建的提示类似于委托视觉艺术家时提供的项目简介,在这种情况下,项目简介的创建者不会是艺术作品的所有者。

随着技术的不断发展,这种情况可能会发生变化,更多的争论也会被提出。无论如何,在生成性 AI 创作过程中记录一些文档,展示“人类创作”组件以应对任何版权问题,总是一个好主意。

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

图片由Markus Winkler拍摄,来源于Unsplash

2. 隐私和数据保护

2023 年 4 月,ChatGPT 在意大利曾被短暂封锁,原因是怀疑违反了欧盟的《通用数据保护条例》(GDPR)。意大利政府强调的主要问题之一与数据保护和隐私有关。ChatGPT 已经更新了其隐私政策和产品以应对这些问题,但仍然不是每个用户都会认真阅读和评估这些内容。

生成性 AI 中存在的隐私数据风险包括(1)数据收集的同意,(2)数据保留,以及(3)可用性风险。

  • 数据收集同意:在初始模型创建过程中,数据从各种来源收集,可能包括个人信息,而信息的所有者并不知道这些数据被用于模型训练。一些平台提供用户选择退出的选项,以便将他们的内容从未来的 AI 模型改进中排除,但默认情况下此选项是关闭的,需要用户手动提交。

  • 数据保留:GDPR 规定了“被遗忘权”部分,允许用户要求公司纠正其个人信息或将其完全删除。尽管公司可以尝试促进这一点,但考虑到大语言模型训练中使用的数据复杂性,关于其技术可行性的讨论仍在继续。

  • 可用性风险:默认情况下,用户与如 ChatGPT 等 LLM 模型的交互记录会被公司收集并用于重新训练 AI 模型。在 LLM 模型的各种使用案例中,如将机器人用作治疗师或医生,用户可能无意中提供敏感信息。

作为生成 AI 的用户,我们需要了解工具的隐私政策——数据如何存储和使用,并且小心我们输入的任何数据,因为这些数据会被收集并可能由提供组织进行人工审查。

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

照片由Dan Nelson拍摄,来源于Unsplash

3. 错误信息

使用生成 AI 时可能会发生无意(或甚至故意)的错误信息。由于其高质量内容生成能力,这些模型能够生成出色的内容,类似于真实或人类生成的内容,使得识别内容的真实性和准确性变得困难

AI 模型容易出现“幻觉”,这是一个用来描述 AI 模型生成的事实不准确或无关输出的术语。这可能由于训练数据不足、序列/理解限制以及训练过程中固有的偏见而发生。在 AI 模型生成的内容中,常常会发现听起来合理的随机虚假信息。我们作为实际用户,需要批判性地分析和验证模型提供的信息,在进一步使用或分发之前。

其他生成 AI 被恶意用于错误信息的方式包括创建深度伪造。深度伪造是经过数字操控的合成媒体,能够令人信服地将一个人的形象替换成另一个人,主要利用深度学习技术。随着强大的生成 AI 的出现,创建此类欺骗性媒体变得越来越容易。

存在因生成内容不准确而导致的相关方受到损害的可能性,这可能构成强有力的诽谤案件。一位澳大利亚市长已经威胁起诉 OpenAI 诽谤,因为 ChatGPT 错误地声称他因贿赂入狱。

4. 伦理影响

生成 AI 模型的非凡能力为各行各业的多个过程开启了新用例。然而,随着模型被用于行业中的决策过程,可能会出现伦理问题。

比如,假设像 ChatGPT 这样的 LLM 模型被用于分析候选人的简历或推荐信,在招聘或大学申请过程中。由于模型训练中的固有偏见,模型可能会无意中突出来自特定背景的候选人,违背了应给予所有候选人的平等机会。

另一个图像生成的例子是,由于训练数据的限制,生成的图像可能会偏向某种文化或人群。模型只能基于其所接受的训练生成输出,如果只训练于特定人群的数据,它将仅生成该人群的输出——最终可能无法为用户提供代表性图像

一种处理方法是人工监督中使用生成 AI。将人工评估作为 AI 使用生命周期的一部分非常重要,以确保输出的可解释性,并避免 AI 决策过程中的偏见。

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

图片由José Martín Ramírez Carrasco提供,来源于Unsplash

结束语

随着生成 AI 使用的增加,用户也需关注法律和伦理问题,并能够在这一领域保护自己。尽管目前尚未明确建立相关法规,我们可以开始采取措施解决上述法律和伦理问题,防止出现不必要的问题。

  • 保护你的个人信息。了解工具的隐私政策,注意输入到系统中的每一条信息,考虑其保密性。

  • 指定并记录 AI 生成内容的人类创作元素。尽管目前 AI 生成内容的所有权不在提示创建者身上,但通过充分的文档记录,可能能够主张人类的创意和创作权。

  • 警惕技术的输出。针对 AI 生成内容的幻觉问题和可能的诽谤,我们可以采取措施在分享内容之前验证信息的准确性,并在内容上添加明确的水印或说明,指明内容是 AI 生成的。

生成式 AI 是一个不断发展的领域,随着技术的额外使用和趋势,法规将会被制定和更新。看看法律领域如何动态地响应这一技术将会很有趣。

Lesk 算法:一种用于文本分析中的词义消歧方法

原文:towardsdatascience.com/lesks-algorithm-a-method-for-word-sense-disambiguation-in-text-analytics-52c157a2fdff?source=collection_archive---------4-----------------------#2023-03-24

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

·

关注 发表在 Towards Data Science · 9 分钟阅读 · 2023 年 3 月 24 日 投票

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

照片由 Amador Loureiro 提供,来源于 Unsplash

在英语中,超过 38%的词汇是多义的,这个术语指的是一个词可能有多个定义或“意义”(Edmonds, 2006)。例如,单词“set”——它可以用作名词、动词和形容词——具有数十种独特的定义,使其成为英语词典中记录的最多义的词之一。因此,如果我们要求某人“请把餐具套放在桌子上”,我们如何本能地知道“set”这个词的两个用法之间的区别?答案全在于上下文。人脑的神经网络执行语义处理、存储和检索,使我们在**词义消歧(WSD)**方面具有天然的天赋。这意味着我们能够在给定的上下文中根据最合理的意思来确定一个多义词的含义。因此,自然语言也随之发展,反映出这种进行复杂上下文关联的天赋能力。

NLP 中的词义消歧

尽管我们的大脑在这些游戏中能够取得很大成功,但具有多个意义的词在**自然语言处理(NLP)**中带来了显著的问题,NLP 是人工智能的一个分支,研究计算机解释和“理解”人类语言的能力。虽然一个词的意义依赖于其上下文,但计算机很难捕捉到词的上下文,因为它被隐喻、修饰语、句子否定以及其他无数的语言复杂性所复杂化,这使得机器难以学习。

由于词义消歧在 NLP 的实际应用中具有重要价值,近年来出现了几种词义消歧的方法。目前的机器学习方法包括监督学习方法,通过使用一组手动标记的词汇来训练分类算法。然而,这些训练集可能成本高昂、获取时间长且不完美,因为即使是人类标注者也常常只有 70-95%的时间达成一致(Edmonds, 2006)。鉴于所需的手动努力,未监督学习方法也被使用,其中许多方法旨在基于某种相似上下文的度量来聚类词汇。

Lesk 算法:一种简单的词义消歧方法

也许最早且至今仍最常用的词义消歧方法之一是 Lesk 算法,由 Michael E. Lesk 于 1986 年提出。Lesk 算法基于这样的理念:在文本中一起出现的词是某种程度上相关的,而这些词的关系和相应的上下文可以通过感兴趣的词的定义以及周围使用的其他词的定义提取。Lesk 算法在现代技术出现之前就已经发展,旨在通过找到在词典定义中重叠最多的一对匹配词典“意义”(即同义词)来消歧感兴趣词的含义——通常出现在短语或句子中。

在 Lesk 使用的示例中,他引用了“pine”和“cone”这两个词,并指出这些词从《牛津英语词典》中返回以下定义:

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

Lesk 在 1986 年提出的语义匹配示例。

简而言之,Lesk 算法计算了一个感兴趣的词与其上下文窗口中所有词的词典定义之间的重叠次数。然后,它选择与重叠次数最多的词对应的定义,排除停用词(如“the”、“a”、“and”),并将其推断为该词的“意义”。如果我们考虑“pine”作为感兴趣的词,而“cone”是其上下文窗口中的唯一词,比较“pine”和“cone”的词典定义会发现“常绿”是这两个词最常见的“意义”。因此,我们可以合理推断感兴趣的词“pine”指的是一种常绿树,而不是它的另一种定义。

优点和缺点

Lesk 算法有许多优点,其中最主要的是其简单性使其易于实现,适用于各种不同的上下文,因此易于推广。Lesk 指出,该算法不依赖于全局信息,这意味着因为相同的词在文本中可能被多次引用但每次含义不同,词义仅由其上下文窗口中的直接支持词汇集合推导,而不是整个文本。除此之外,Lesk 算法是非句法性的,这意味着该方法不依赖于词语的排列或句子的结构,因为所有的关联仅通过词典定义进行。这使得该算法与基于句法的文本分析解决方案配合使用时具有很好的效果。例如,一个基于词性标注的文本标记工具可能能够识别“mole”作为名词的用法,但不能准确区分动物(mole)、皮肤肿块(mole)和计量单位(mole),因为这三者都是名词。在这种情况下,Lesk 算法的优势得以显现。

尽管简单且功能强大,Lesk 原始算法的最大缺点是性能——Lesk 提出的准确率仅为约 50-70%,并且在与带有语义标记的文本进行实验验证时表现得更低(Viveros-Jiménez, 2013)。该算法还显著存在低召回率的问题,即由于词典定义之间完全没有重叠或多个定义有相同数量的重叠,无法为许多单词提供对应的上下文定义。此外,Lesk 留下了一些未解答的问题,包括最适用的词典是什么,所有匹配的术语是否应被平等对待或按词典定义的长度加权,以及单词的上下文窗口应有多宽(Lesk 提出约 10 个单词,但建议这个范围是相当灵活的)。

前进:Lesk 的最新进展

尽管 Lesk 的方法中留有一些问题和不足,但已经进行了重要的努力来在他原始工作的基础上进行扩展。2002 年,研究人员提出了一种将 Lesk 的基于词典的算法适配到词汇数据库 WordNet 的方法(Banerjee & Pedersen, 2002)。与词典中按字母顺序排列单词和定义不同,WordNet 是一个在线数据库,按语义排列单词,创建名词、动词、形容词和副词的数据库组。具有同义关系的单词被归入称为“同义词集合”(synset)的关系中,而多义词可以通过其出现在多个同义词集合中被识别,因为每个同义词集合代表一个单词的不同定义或“意义”。WordNet 还考虑了词汇关系,例如下义词(hyponymy,即属于更广泛类别的词,如郁金香是花,花是植物)和转喻(metonymy,即用与某物密切相关的名称来代指该物,如“王冠”指代君主制),因此能够捕捉到比原始 Lesk 算法更广泛的词汇定义和关系。WordNet 的结构允许更有针对性地搜索相关词汇,因为如果已知感兴趣单词的词性,随后搜索的关系和同义词集合将仅限于该词性的范围内。

Lesk 算法的低召回率问题也一直是改进的领域。2013 年,研究人员发表了一篇论文,比较了几次实验,在这些实验中,他们调整了考虑的词的上下文窗口的大小,以期提高算法的性能(Viveros-Jiménez 等人,2013)。提出了一项新建议,即在定义上下文窗口的大小时,只考虑有至少一个重叠的四个词,并且在词义匹配中排除目标词自身。由于词典定义通常会在示例句子中包含感兴趣的词,例如“即松树是一种常绿树”,对于词“pine”,这可能会导致对“pine”的虚假重叠,从而使所有词义中最匹配的词仍为“pine”而不是“evergreen”。研究人员发现,当结合这两种方法时,Lesk 算法的精确度和召回率显著提高,相较于简单选择 4 个词的上下文窗口。

在 Python 中应用 Lesk 算法

以下是一个简单的例子,展示了如何实现 Lesk 算法,使用 pywsd 包在 Python 中进行词义消歧。以下函数是 Adapted Lesk 算法的实现,该算法由 Banerjee 和 Pederson 于 2002 年描述,使用了 WordNet 实现的 Lesk。

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

Adapted Lesk 算法在 Python 中的实现示例

我们可以看到,对于词“bank”,Lesk 算法能够推断出在上下文中,一个“bank”的意思是指金融机构,而另一个意思是指一种倾斜的土地。同样,在进行这种关联时,句法结构并不重要,而 Lesk 算法的成功依赖于上下文线索的存在,例如提到河岸时的“river”和“fish”,以及提到金融银行时的“deposit”和“money”。通过这个例子可以看出,Lesk 对句子结构具有鲁棒性,因此可以用于从各种非结构化文本数据类型中提取词义,无论是经典小说的摘录还是 Twitter 评论。然而,由于 Lesk 算法对上下文窗口的大小敏感,这种数据结构限制可能最限制其应用。因此,数据必须被结构化并分词成适当大小的窗口,以提供足够的上下文信息,但仍能捕捉到感兴趣词的真正含义。由于 Lesk 算法可能最适用于短语和具有上下文信息的相关术语组,它可以应用于查询和检索类型的场景,例如自动聊天机器人或搜索框,在这些场景中输入一些上下文信息,进行解释,然后返回响应或结果。

尽管 Lesk 算法及类似的词义消歧方法直观上简单,但它们作为当前开发直观日常工具(需要词义消歧)的基础性垫脚石发挥了重要作用。特别是,这些算法背后的直观理念可以实际应用于开发新的算法,以提升搜索引擎在全球数十亿用户中使用时的信息检索相关性。毕竟,当我们搜索一个模糊的术语时,我们本能地知道需要包含一个“上下文窗口”,以增加准确命中的可能性。

那么为什么呢?因为上下文就是一切。

感谢阅读!如果你想分享任何想法,请随时在 LinkedIn 上联系我

www.linkedin.com/in/duncan-w/

资源

Banerjee, S. & Pedersen, T. (2002). 使用 WordNet 的适应型 Lesk 算法进行词义消歧。计算语言学与智能文本处理。2276. 136–145. 10.1007/3−540−45715−111.

Edmonds, P. (2006). 消歧义,词汇的。语言与语言学百科全书(第二版),Elsevier,第 607–623 页。

Lesk, M. (1986). 使用机器可读词典的自动词义消歧。第 5 届年度国际系统文档会议论文集 — SIGDOC

Liling Tan. 2014. Pywsd: Python 实现的词义消歧(WSD)

技术 [software]。取自 https : //github.com/alvations/pywsd

SAP Conversational AI. (2015 年 11 月 20 日)。从上下文到用户理解。取自 https : //medium.com/@SAPCAI/from−context−to−user−understanding−a692b11d95aa

Viveros-Jiménez F., Gelbukh A., Sidorov G. (2013) 对简化的 Lesk 算法在词义消歧中的简单窗口选择策略。载:Castro F., Gelbukh A., González M. (编) 人工智能及其应用的进展。MICAI 2013. 计算机科学讲义,第 8265 卷. Springer, Berlin, Heidelberg. https : //doi.org/10.1007/978−3−642−45114−017

扩展我们的数据科学视野

原文:towardsdatascience.com/lets-expand-our-data-science-horizons-369c419da98?source=collection_archive---------13-----------------------#2023-02-16

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

·

关注 发表在 Towards Data Science · 作为 通讯 发送 · 3 分钟阅读 · 2023 年 2 月 16 日

欢迎来到《Variable》第 100 期!

我们通常将每一期通讯专注于特定的话题或问题;然而,为了纪念这一特殊的时刻,我们选择了不同的方法。每周,我们会将一些出色的 TDS 文章排除在《Variable》之外,因为它们不完全符合我们的主题;这一周,我们呈现了最近编辑推荐中我们尚未强调的最佳文章。

如果你担心我们可能会引导你走向一个充满混乱和策展鲁莽的危险路径,请不要担心!我们推荐的每一篇阅读材料都邀请你以一种全新而吸引人的方式探索新的方向、方法或工具——我们相信这是一种强有力的主题联系。享受阅读吧!

  • 预测性能漂移:另一面。为什么模型预测的准确性会随着时间的推移而恶化?瓦莱里亚·丰塞卡·迪亚斯的清晰概述提供了四种漂移的有用类型,包括它们的原因和潜在解决方案。

  • 训练中断会毁掉我的马拉松吗? barrysmyth的最新贡献位于数据分析、运动和自我跟踪的迷人交汇处,探讨了中断对马拉松选手表现的影响。

  • 我们早该看到 ChatGPT 的到来。在一次令人难忘的首发帖子中,萨曼莎·霍德将实用主义和惆怅融合在一起,评估了 ChatGPT 的仍在展开的影响——并提出了一种更冷静的思维方式,让我们“让它为我们而不是对抗我们工作。”

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

图片由Raychan拍摄,来源于Unsplash

  • 生存分析:用机器学习预测事件发生时间。预测事件发生的概率有什么比这更好的?对Lina Faik来说,知道事件发生之前还剩下多少时间更为重要;Lina 的易懂解释帖子带领我们了解如何在客户流失的背景下进行这样的预测。

  • 两个发球:分析 2000–2020 年的 ATP 发球数据。像篮球和足球这样的主要团队运动(包括全球普及的和美国的)常常主导了运动分析的讨论。肖恩·霍兰德通过对网球选手关键击球:发球的深思熟虑的数据支持的分析,帮助拓宽了话题。

  • 相等大小的谱聚类。谁不喜欢对一个永恒重要话题的精美教程?Carmen Adriana Martinez Barbosa的新指南向我们介绍了相等大小谱聚类算法,该算法生成的聚类——没错!——在包含的点数上更为平衡;这在许多实际应用场景中可能至关重要。

  • 首席数据科学家的日常。数据专业人员的职业路径可以有很多种形式;如果你对管理轨迹感兴趣,并想更好地了解未来可能的工作样貌,Ray(写作二人组Leah Berg 和 Ray McLendon中的一员)分享了他作为首席数据科学家的日常工作和实践。

  • 如何制作一个 AI 图像编辑聊天机器人。为了以一个实用、适合创作者的方式结束这期内容,别错过由Sophia Yang和 Philipp Rudiger 带来的快速教程,他们展示了如何创建一个能够使用聊天机器人提示编辑图像的应用程序。

  • 额外推荐我写作的主要驱动力始终是学习。** 我们本周很高兴发布了 2023 年的首个作者聚光灯——一次生动的对话,特邀Matteo Courthoud分享了他关于职业道路、写作历程以及对因果推断的持续兴趣的见解。

感谢您参与我们第 100 期的《Variable》——祝愿接下来有 100 期更多的精彩!

如果你喜欢我们本周推荐的工作,希望你考虑成为 Medium 会员——这是支持我们出版物最直接有效的方式。

直到下一期《Variable》,

TDS 编辑团队

让我们谈谈数学(面向数据科学家)

原文:towardsdatascience.com/lets-talk-about-math-for-data-scientists-50184abd5964?source=collection_archive---------5-----------------------#2023-03-23

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

·

关注 发表在 Towards Data Science · 作为 新闻通讯 发送 · 3 分钟阅读 · 2023 年 3 月 23 日

数学常常引发从事数据相关职业的专业人士强烈的情感。有些人因为热爱数学而想成为数据科学家。另一些人则需要克服对这个主题的根深蒂固的焦虑,才能在职业中找到自己的立足点;即便如此,他们可能还是会尽量少用数学知识。

不论你的个人经验如何,我们希望你喜欢这篇热情和耐心探讨数学之美(及复杂性)的文章合集。这些文章从初学者友好到更高级的主题都有,你一定能找到感兴趣的内容,无论你从上一个训练营、大学课程,还是——哎呀!——高中课程中记住了多少。

  • 逻辑回归的易懂介绍。统计学是数据科学家最常遇到的数学子领域,其中逻辑回归是他们需要掌握的关键概念之一。如果你处于学习的早期阶段,不要错过Shreya Rao精美插图的基础指南。

  • 对核函数感到困惑?现在不再困惑。根据Shubham Panchal的说法,核函数是“用于机器学习和统计学中的美丽数学概念,有多种形式。”它们的多样性可能使它们显得令人困惑,但 Shubham 的解释将帮助你清晰地看到如何在机器学习应用中使用它们。

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

图片来源:Kristin SnippeUnsplash

  • 选择正确路径的挑战。如果你正在处理一个复杂的数学优化问题,知道哪种方法在具体情境中最合适可能会很困难。Hennie de Harder的解释带我们了解如何在精确算法(如线性或混合整数规划)和启发式算法(如遗传算法和粒子群优化)之间做出选择。

  • 揭开经典概率谜题的复杂性Naman Agrawal邀请我们跟随他解决“挑战我们对周围世界理解”的优惠券收集者问题。在过程中,Naman 还讨论了这个问题的复杂性及其对计算机科学和经济学等各个领域的影响。

这周我们还有一些阅读推荐——希望你还没厌倦数学!尽情享受:

  • 在我们真正告别数学之前,看看Bex T.关于在 Python 中创建惊艳分形艺术的详细教程。

  • Yennie Jun对美国图书禁令的惊人趋势背后的数据进行了彻底而直言不讳的分析。

  • 机器学习与时尚的碰撞在Federico Bianchi的 FashionCLIP 介绍中体现:一个新的领域特定的视觉和语言模型。

  • 随着三月疯狂进入其(几乎)最后阶段,现在是阅读Giovanni Malloy关于大学篮球 NET 排名如何运作的解释文章的好时机。

  • 如果你的教育或职业道路不那么传统,数据科学工作依然触手可及—Madison Hunter解释了如何利用非传统背景获得最大收益。

感谢你这周的时间和支持!如果你喜欢我们发布的内容(并希望访问所有内容),可以考虑成为 Medium 会员

直到下一个变量,

TDS 编辑

在 Plotly 中提升你的数据讲述能力,使用动画柱状图

原文:towardsdatascience.com/level-up-your-data-storytelling-with-animated-bar-charts-in-plotly-f9ace6d73f27

将静态图表转变为引人入胜的叙述

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

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

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

图片来源:Teemu PaananenUnsplash

Plotly 支持动画图表的优秀基础。我强烈推荐他们的基础教程 这里。然而,plotly 动画主要是为了给可视化添加另一个维度——通常是时间。这对为图表增添更多意义非常棒。

然而,动画不必用于增加图表的复杂性。利用动画进行强调是非常强大的。当我们为观众准备好关键图表时,我们希望吸引他们的注意,而不是明确地喊出“这是你应该特别关注的图表!”我们的视线自然会被移动的东西吸引,动画图表可以在某种程度上营造出期待感。你的观众会参与其中,试图实时预测下一条柱状图或线条。如果关键业务决策依赖于这个图表——你的观众会屏息以待,眼睁睁看着结果展开!

为了更好地理解——想象一下你已经看了同事的演示文稿 20 分钟——哪个图表吸引了你的注意?这个简单的(pandas 生成的)图表:

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

基本柱状图——作者提供的图像

或者这张相同数据的动画图表:

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

自定义 Plotly 动画柱状图——作者提供的图像

在 Plotly 中构建

要在 Plotly 中实现这一点,我们实际上需要稍微重新排列数据。同样,Plotly 动画的设置是按某种顺序(通常是时间)进行的。因此,我们将扩展数据以有效地为每个数据点制作一个帧。来吧:

首先,让我们导入 plotly 包(如果还没有安装,请使用 pip 安装),并创建一个虚拟数据集进行操作:

要直接使用 plotly 的动画,我们需要将数据集增强为以下内容:

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

动画条形图的数据转换 — 图片来自作者

我们需要有效地堆叠数据框的副本,副本的数量与原始数据框的长度相同。对于每个副本,我们需要添加一个 Frame 值来设置其在动画中的序列。问题在于,我们希望每一步序列中出现更多的条形图。我们希望在绘图中开始时不显示任何条形图,因此对于第一个 Frame,我们将所有值设置为 0。然后,在第二个 Frame 上,我们将所有值设置为 0 除了 第一个数据点(A: 10)。这个方法一直持续到最后一个 Frame(在这种情况下是 Frame #6)显示我们原始数据中的所有值。

到此为止,我们已经为 Plotly 设置了单独的“Frame”元素,以便平滑地动画过渡。请注意,我们没有为动画编码每一帧,我们只是逐个快照每个 Label 的最终值,让 Plotly 填补空白。现在我们只需要一个辅助函数,这样我们就可以将这个转换概念应用于任何简单的数据框:

数据框堆叠和排序函数

在这段代码中,我们首先创建一个空白的目标数据框,它将作为返回的输出。我们循环遍历传入数据框中的元素数量 — range(len(starting_df)+1)。我们在这里加了 1,因为第一个 Frame 将所有值都置为零,以便我们的图开始时为空。在循环内部,我们从原始数据框的深拷贝开始。这意味着我们对这个副本所做的更改不会传递回原始数据框。在这个干净的副本上,我们添加一个 Frame 值,并根据我们所在的循环次数将一些数据点置为零。最后,我们可以将这个副本添加到我们正在构建的最终数据框的末尾。

绘图

从这里开始我们准备开始绘图。如果我们只是使用默认的 plotly 函数,它看起来会像这样:

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

默认绘图设置 — 图片来自作者

正如你所见,开始时有点怪异。这主要与绘图如何自动缩放 y 轴以适应数据有关。这并不特别令人惊讶,因为在它第一次绘制时(Frame=0),所有值都是零。除此之外,序列的速度可能不是你想要的。也许你想让条形图绘制得慢一点?也许在绘制条形图之间有一个暂停会很好?这些都是可调整的参数。最好的部分是,使用 plotly,我们甚至不需要条形图线性增长 — 你可以使用一系列的过渡设置。为了将这个图形清理成我想展示的样子,我将调整:

  • Y 轴范围(取数据框中的最大值并加上 10%)

  • 图形大小和纵横比

  • 每帧的持续时间和帧之间的时间

  • 动画移动本身(过渡)

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

使用“quad-out”动画格式化的动画条形图——作者提供的图像

在这种情况下,我使用了“quad-out”过渡效果,但还有很多可以选择的效果,这些效果会影响可视化效果。可以查看文档 这里 并尝试几种效果——特别是“bounce”效果相当有趣(尽管可能不是最专业的)。还有许多可以调整的 设置,可以用来修改或移除图表下方的按钮,不过这可能取决于你导出图表的方式,有时可能并不必要。

导出为 GIF

现在有个坏消息——Plotly 没有内置的方法来提取这些动画。你现在需要做一些变通的方法。有些包可以用来提取成电影文件,如 moviepy,但如果我们要将其放入演示文稿或基于网页的客户仪表板中,这可能有些过于复杂。我们真正需要的是一个动画 GIF。虽然使用 Plotly 的 graph_objects 提取动画帧为 PNG 并使用其他包在线生成 GIF 是一个选项,但这超出了本帖的范围。现在,可以使用基本的 GIF 屏幕捕捉软件在 Jupyter Notebook 窗口上进行操作。这里有很多选择——这里 有一个免费的软件对我有效。

额外加分

当然,这个序列是为了从左到右绘制条形图。如果我们想要相反的效果,帧#1 的 F 值将为 16,而 A 值仍为 0。这在绘制折线图时可能会很有用,其中多条线可以从图表的不同侧面生长。🤯

结论

我希望你能看到添加一点动画如何使简单的图表对观众更具影响力。通过使用这个相对简单的功能,我们可以快速而轻松地将静态图表转换成极具吸引力的图像。当然,这只是一个开始!正如我频道的粉丝所知道的,我喜欢用有意义的 条形颜色渐变背景过渡 来直观地向观众传达更多信息。更多内容即将到来。

和往常一样,整个代码演示笔记本可以 从我的 github 获取如果你觉得这有用,请关注我! 祝好,编码愉快。

利用像 GPT 这样的 LLMs 分析你的文档或记录

原文:towardsdatascience.com/leverage-llms-like-gpt-to-analyze-your-documents-or-transcripts-c640a266ad52

使用提示工程,通过 langchain 和 openai 以类似 ChatGPT 的方式分析你的文档

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

·发表在Towards Data Science ·阅读时间 6 分钟·2023 年 3 月 31 日

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

(原始)照片由Laura Rivera拍摄,发布在Unsplash

ChatGPT 无疑是最受欢迎的大型语言模型(LLMs)之一。自 2022 年底发布其测试版以来,大家都可以使用便捷的聊天功能提问或与语言模型互动。

但如果我们想要向 ChatGPT 提问关于我们自己的文档或我们刚刚听过的播客内容呢?

本文的目标是向你展示如何利用像 GPT 这样的 LLM(大型语言模型)来分析我们的文档或记录,然后以 ChatGPT 的方式提问并获得有关文档内容的回答。

简要说明

  • 本文使用了 OpenAI 的 ChatGPT gpt-3.5-turbo模型,这需要一个API 密钥

  • langchain包是一个围绕 LLM 构建的框架,用于加载和处理我们的文档(提示工程)以及与模型进行交互。

  • 包含本文全部代码的 colab 笔记本可以在这里找到。

前提条件

在编写所有代码之前,我们必须确保所有必要的包已安装、API 密钥已创建且配置已设置。

API 密钥

要使用 ChatGPT,首先需要创建一个 OpenAI API 密钥。可以通过这个链接创建密钥,然后点击

+ 创建新的密钥 按钮。

没有什么是免费的:一般来说,OpenAI 会对每 1,000 个 tokens 收费。Tokens 是处理文本的结果,可以是单词或字符块。每 1,000 个 tokens 的价格因模型而异(例如,gpt-3.5-turbo 为$0.002 / 1K tokens)。关于定价选项的更多细节可以在这里找到。

好的一点是,OpenAI 允许你免费试用$18,无需提供任何付款信息。你当前的使用概况可以在你的账户中查看。

安装 OpenAI 包

我们还需要通过运行以下命令来安装官方 OpenAI 包

pip install openai

由于 OpenAI 需要(有效的)API 密钥,我们还需要将密钥设置为环境变量:

import os
os.environ["OPENAI_API_KEY"] = "<YOUR-KEY>"

安装 langchain 包

随着 2022 年末对大型语言模型(LLMs)的兴趣急剧上升(Chat-GPT 发布),一个名为 LangChain 的包在同一时间出现

LangChain是一个围绕 LLM(如 ChatGPT)构建的框架。该包的目标是帮助开发将 LLM 与其他计算或知识来源相结合的应用程序。它涵盖了如特定文档的问答本文的目标、聊天机器人代理等应用领域。更多信息可以在文档中找到。

可以使用以下命令安装该包:

pip install langchain

Prompt Engineering

你可能会想知道Prompt Engineering是什么。可以通过创建一个在你希望分析的文档上训练的自定义模型来微调 GPT-3。然而,除了训练费用外,我们还需要大量的高质量示例,理想情况下由人工专家审核(根据文档)。

这对于仅仅分析我们的文档或抄本来说会显得过于繁琐。因此,我们将我们希望分析的文本(通常称为提示)传递给它,而不是训练或微调模型。生产或创建如此高质量的提示称为Prompt Engineering

注意:有关 Prompt Engineering 的进一步阅读文章可以在这里找到。

加载数据

根据你的使用案例,langchain为你提供了几个“加载器”,如Facebook ChatPDFDirectoryLoader,用于加载或读取你的(非结构化)文本(文件)。该包还包括一个YoutubeLoader用于转录 YouTube 视频。

以下示例集中在DirectoryLoaderYoutubeLoader上。

使用 DirectoryLoader 读取文本文件

from langchain.document_loaders import DirectoryLoader

loader = DirectoryLoader("", glob="*.txt")
docs = loader.load_and_split()

DirectoryLoader 的第一个参数是路径,第二个参数是模式,用以查找我们所需要的文档或文档类型。在我们的案例中,我们将加载与脚本位于同一目录下的所有文本文件(.txt)。load_and_split 函数随后启动加载过程。

即使我们可能只加载一个文本文档,但如果文件较大,进行拆分也是有意义的,以避免NotEnoughElementsException(需要至少四个文档)。更多信息可以在这里找到。

使用 YoutubeLoader 转录 YouTube 视频

LangChain 提供了一个 YoutubeLoader 模块,该模块使用 youtube_transcript_api 。这个模块会收集给定视频的(生成的)字幕。

并非每个视频都自带字幕。在这些情况下,可以使用自动生成的字幕。然而,在某些情况下,这些字幕质量较差。在这些情况下,使用 Whisper 来转录音频文件可能是一个替代方案。

下面的代码将视频 ID语言(默认为 en)作为参数。

from langchain.document_loaders import YoutubeLoader

loader = YoutubeLoader(video_id="XYZ", language="en")
docs = loader.load_and_split()

在我们继续之前……

如果你决定使用转录的 YouTube 视频,请考虑首先进行适当的清理,例如,清除 Latin1 字符(\xa0)。我在问答部分体验到的答案差异取决于我使用的相同来源的格式。

处理数据

像 GPT 这样的 LLMs 只能处理一定数量的令牌。这些限制在处理大型(或更大)文档时尤为重要。通常,有三种方法来应对这些限制。一种是利用嵌入或vector space engine。第二种方法是尝试不同的链式方法,如map-reducerefine。第三种方法是两者的结合。

一篇提供有关不同链式方法和向量空间引擎使用的更多细节的精彩文章可以在这里找到。还要记住:使用的令牌越多,收费也会越高。

在接下来的内容中,我们将embeddings与链式方法stuff结合起来,stuff会将所有文档“塞入”一个单一的提示中。

首先,我们通过使用OpenAIEmbeddings将转录文本(docs)导入向量空间。然后,这些嵌入被存储在一个称为Chroma的内存嵌入数据库中。

from langchain.embeddings.openai import OpenAIEmbeddings
from langchain.vectorstores import Chroma

embeddings = OpenAIEmbeddings()
docsearch = Chroma.from_documents(docs, embeddings)

之后,我们定义我们希望用于分析数据的model_name。在这种情况下,我们选择gpt-3.5-turbo。可以在这里找到可用模型的完整列表。temperature参数定义了采样温度。较高的值会导致输出更随机,而较低的值则会使答案更加集中和确定。

最后但同样重要的是,我们使用了RetrievalQA (Question/Answer) 检索器并设置了相应的参数(llmchain_typeretriever)。

from langchain.chains import RetrievalQA
from langchain.chat_models import ChatOpenAI

llm = ChatOpenAI(model_name="gpt-3.5-turbo", temperature=0.2)

qa = RetrievalQA.from_chain_type(llm=llm, 
                                    chain_type="stuff",
                                    retriever=docsearch.as_retriever())

提问

现在我们准备向模型提问关于我们的文档。下面的代码显示了如何定义查询。

query = "What are the three most important points in the text?"
qa.run(query)

对于不完整的回答该怎么办?

在某些情况下,你可能会遇到不完整的回答。回答文本在几句话后就停止了。

不完整回答的原因很可能是令牌限制。如果提供的提示非常长,模型剩余的令牌就不够给出一个(完整的)回答。处理此问题的一种方法是切换到不同的chain-type,如refine

llm = ChatOpenAI(model_name="gpt-3.5-turbo", temperature=0.2)

qa = RetrievalQA.from_chain_type(llm=llm, 
                                    chain_type="refine",
                                    retriever=docsearch.as_retriever())

然而,我发现当使用与stuff不同的chain_type时,我得到的结果不够具体。处理这些问题的另一种方法是重新表述问题,使其更加具体。

结论

多亏了 LangChain 包,只需要几行代码就可以分析 GPT 文本文档或转录内容。由于这个包相对较新,我预计很快会有很多更新和代码更改。这可能会影响本文中提供的代码片段。

如果你考虑在日常工作或较大的私人项目中使用 LLM,你应该关注数据的清理,优化使用的令牌数量,并使用最佳实践,如设置预算限制或警报。

我希望你喜欢阅读这篇文章。源代码的 Colab 笔记本可以在 这里 找到。

来源

[## LangChain:简介和入门 | Pinecone

大型语言模型(LLMs)在 2020 年 OpenAI 发布 GPT-3 后进入了世界舞台 [1]。从那时起…

www.pinecone.io [## 使用 GPT3、LangChain 和 Python 构建 GitHub 支持机器人 | Dagster 博客

2023 年 1 月 9 日 * 13 分钟阅读 * ChatGPT,你听说过吗?ChatGPT 几个月前推出,震惊了所有人……

dagster.io [## 提示工程

在这里,我们讨论了一些编写提示(我们模型的输入)的原则和技巧,这将帮助你获得更好的结果……

docs.cohere.ai [## 检索

TL;DR:我们正在调整我们的抽象,以便除了 LangChain VectorDB 之外的其他检索方法也能轻松使用……

blog.langchain.dev

利用聚合的力量

原文:towardsdatascience.com/leverage-the-power-of-aggregate-ad5d69810c62

使用 pandas 聚合加速你的数据分析

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

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

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

图片由 Dan Hadar 提供,Unsplash

你是否曾经因为在相同(分段)特征上计算各种统计信息时编写重复的代码而感到沮丧?

那些试图计算各种不同(分段)特征上各种统计信息的相似代码片段让你迷失了方向——听起来很熟悉吗?

我理解你的痛苦。好吧,我曾经理解——现在不再了!所以在这篇后续文章中,我们将讨论你可以使用agg的简单(但强大)方法:

  1. 对相同特征应用不同函数

  2. 对特征集应用函数集

  3. 使用元组获得更多灵活性

  4. 使用来自其他包的函数,

我提到了特征的分组或分段。虽然你不一定要在groupby语句中使用agg,但我认为这样做可以提供更有趣的示例,并且与我之前的内容相呼应。说到这一点,如果你想要对groupbyapplytransform进行复习,我建议你阅读一下:

## 提升你的数据处理技能

解锁 pandas 的 groupby、apply 和 transform 的强大功能

towardsdatascience.com

足够的无耻自我宣传——我们开始吧!首先,看看我们将要使用的数据。

数据

我们将继续使用从消费信用卡组合中收集的信息¹。

我对列名称和格式做了一些修改,但数据其余部分保持不变:

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

图片由作者提供

数据包含各种分类特征(例如教育水平)和数值特征(例如客户年龄)。

数据中包含的特征比上面的快照所示的更多;如果我们在下面使用一些“新”特征,不要感到惊讶!

聚合

… 或 agg

我认为文档对 agg 的强大功能描述得不够充分:

使用一个或多个操作在指定轴上进行聚合。

这实际上没有说明的是,agg 是一种非常灵活的函数,提供了多种方法来将多个函数应用于同一特征,或将多个函数应用于多个特征。它可以与或不与 groupby 语句一起使用,并可以在行或列轴上应用。它也不挑剔函数的来源或定义方式。

开始吧!

附注:我们也可以使用 agg 将相同的函数应用于多个特征,但通过直接使用该函数或通过 apply 语句可以更轻松地实现。

在同一特征上使用多个函数

让我们看看如何计算每个客户和性别细分中的客户年龄的最小值、最大值和偏度。

以下示例中使用的函数仅用于演示目的。一些函数——如偏度——对某些读者可能是新的,而其他函数——如scaled_median——则完全是虚构的。

附注:“偏度”是一个统计学概念,用于衡量分布固有的非对称程度。

# set up
segments = ['attrition_flag','gender']
feature = 'customer_age'
functions = ['min','max','skew']

# apply it
df.groupby(segments)[feature].agg(functions)

… 这将给出:

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

图片来源:作者

相当直接,对吧?只需注意几点:

  1. 我已经费心明确了每个细分、特征和函数的具体使用方法,通过在应用前对每个进行定义。这主要是为了提供一个清晰的示例,所以可以自由地简化你的代码。

  2. 我们使用内置的 pandas 函数,因此可以通过其名称引用它们(因此使用字符串形式进行指定)。稍后我们将看到如何使用来自其他包的函数以及自定义函数,包括 lambda 函数。

  3. 我们在 agg 中提供要应用的函数列表。如果需要,我们可以在列表中添加更多函数。

让我们更深入一点,开始将各种函数应用于多个不同的特征。

在多个特征上使用多个函数

agg 的真正优势——一次性生成多种汇总统计信息。

与 Python 中的大多数事物一样,这里有多种方法可以做到这一点。我们将在这里介绍两种不同的方法。首先是使用列表。

# set up
segments = ['attrition_flag','gender']
features = ['customer_age','months_on_book']
functions = ['min','max']

# apply
df.groupby(segments)[features].agg(functions)

… 这将给出:

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

图片来源:作者

这应该与你之前看到的相当熟悉——我们不是将各种函数应用于单一特征,而是将相同的函数应用于多个特征。唯一的真正区别是我们在列表中指定特征。

那么,如何将多个不同的函数应用于各种特征呢?为此,我们需要一个字典。

# set up
segments = ['attrition_flag','gender']
functions = {
    'customer_age':['min','max'],
    'months_on_book':['median','std']
}

# apply
df.groupby(segments).agg(functions)

… 这就产生了:

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

图片由作者提供

注意使用带有函数列表的字典来应用时,我们在应用内容上有更多的灵活性?很棒!

在这个示例中,我们只看到两个函数应用于两个特征。当然,我们可以将其更改为将任意多个函数应用于任意多个特征。我们只需记住字典参数的格式:特征名称应指定为字典键,(函数的) 列表应作为字典值提供。

更多的元组,更多的灵活性

我们在上面看到,使用列表和元组中的agg调用返回一个具有层次索引³(或在术语中称为MultiIndex)的DataFrame

如果这正是你想要的,那当然没问题。但我喜欢简单的东西,我也喜欢控制我的列名。

所以我们将切换到使用元组,这将:

  1. 返回一个具有平坦索引的DataFrame。(忘记那些reset_index()调用)。

  2. 允许我们指定每个返回列的名称。

可以说,后一点可能是使用元组的一个更强大和有用的动机。

好了,闲话不多说。让我们用元组重写上述示例。

# set up
segments = ['attrition_flag','gender']

# apply
df.groupby(segments).agg(
    customer_age_min = ('customer_age','min'),
    customer_age_max = ('customer_age','max'),
    months_on_book_median = ('months_on_book','median'),
    months_on_book_std = ('months_on_book','std')
)

产生:

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

图片由作者提供

使用元组可以让我们指定汇总特征的名称。如果你想对DataFrame的名称非常具体,这非常有用。

这个调用agg的语法有点不寻常。在这里,我们在元组之外指定结果列的名称,然后在元组内提供被汇总特征的名称(以字符串格式)以及函数。伪代码中,这类似于结果名称 = (特征名称, 函数)

我们仍在使用内置的pandas功能,因此可以通过名称(以字符串格式)引用它们。让我们看看如何使用其他函数。

异域函数

我们经常需要使用更异域或定制的汇总函数。

我对“异域”这个词有些宽泛——我实际上是指来自其他包的函数。然而,有时我们确实需要使用我们自己编写的真正定制的功能。

那么,开始吧!我们将结合一些想法,将各种函数应用于每个attrition_flag x gender段。

# exotic functions
import numpy as np

# bespoke function
def scaled_median(s):
    # scales Series median by ratio of Series max to Series min
    return s.median() * s.max() / s.min()

# segments
segments = ['attrition_flag','gender']

# apply
df.groupby(segments).agg(
    customer_age_min = ('customer_age',np.min),
    customer_age_max = ('customer_age',np.max),
    months_on_book_std = ('months_on_book',np.std),
    months_on_book_scaled_median = ('months_on_book',scaled_median)
)

这就产生了:

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

图片由作者提供

我们逐步进行:

  1. 我们从numpy中提取一些函数。这里没有什么“异域”的东西——这些只是numpy对最小值、最大值和标准差的实现。别忘了导入包含“异域”函数的包!

  2. 我们创建了一个名为scaled_median的自定义函数。这里也没什么特别的,因为这是一个为了演示目的而编造的函数。

  3. agg 的调用使用了元组方法。这里没有太多新内容,除了我们引用函数的方式。由于我们不再使用内置方法,因此不能按名称调用它们:而是提供它们的名称及其来源(例如np.min)。

如果你想更具 Python 风格,可以用 lambda 函数替代函数的更正式定义;我在这里没有这么做,是出于格式化的考虑。

就到这里,回顾一下我们所做的工作。

总结

这一次,我们已经看到如何使用agg将不同的函数应用于相同(分组)的特征。

我们扩展了将一组函数应用于一组(分组)特征的内容;我们看到可以使用列表、字典和元组来做到这一点。示例展示了每种方法的优点。

最后但同样重要的是,我们看到如何利用其他包中的函数和我们自己编写的自定义函数。

正如我们所看到的,agg 的真正优势在于将多个函数应用于多个特征。这对于探索性数据分析、特征工程和生成汇总统计非常有用。

在进行特征工程时,我们经常需要将数据片段转换的结果映射回原始数据。关于是否可以使用 agg 来实现这一点常常存在一些困惑——实际上不能。如果我们需要将结果广播回原始数据,我们需要使用 transform,尽管这也有其自身的限制。如果你需要进行大量转换,你可能会考虑使用 agg,然后手动将结果与原始数据连接起来。

一如既往,感谢你的阅读。希望这里有一些有用的提示,也请告诉我你使用的任何实用技巧!

如果你对其他 pandasmatplotlib 技巧感兴趣,你可能也会对以下内容感兴趣:

## 让你的表格看起来光辉夺目

简单的格式化技巧,让你的 pandas DataFrame 准备好展示

[towardsdatascience.com

… 和

## 让你的图表看起来光辉夺目

让 matplotplib 图表准备好展示的简单技巧

[towardsdatascience.com

参考文献和资源

  1. zhyli. (2020). 预测流失的信用卡客户 [数据集]. Zenodo.,在 Creative Comons — Attribution 4.0 International 许可下提供。

  2. pandas.DataFrame.agg — pandas 1.5.3 文档 (pydata.org)

  3. 多重索引 / 高级索引 — pandas 1.5.3 文档 (pydata.org)

利用 Azure Event Grid 创建 Java Iceberg 表

原文:towardsdatascience.com/leveraging-azure-event-grid-to-create-a-java-iceberg-table-d419da06dbc6?source=collection_archive---------29-----------------------#2023-01-10

我们将使用 Azure Event Grid 实现事件驱动架构

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

·

关注 发布于 Towards Data Science ·6 min read·2023 年 1 月 10 日

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

图片由 Jackson Case 提供于 Unsplash

我们之前的文章 中,我们展示了 Iceberg 表如何作为 Kafka 主题。我们展示了独立的 Java 写入器如何并行生成 parquet 文件,同时单个 Bookkeeper 将这些数据文件附加到 Iceberg 表中。Bookkeeper 通过创建 Iceberg 提交来完成这一操作。

Bookkeeper 需要识别新创建的数据文件是什么。然后,它将这些文件注册到 Iceberg 表中。在我们之前的文章中,我们保持简单,并选择实现基于文件的消息通道。对于每个创建的数据文件,Writers 会在一个众所周知的文件夹中创建一个 Moniker。Bookkeeper 监视这个众所周知的文件夹,并定期读取这些 Monikers。Bookkeeper 使用这些 Monikers 中的信息将新数据文件附加到 Iceberg 表中。

之前的解决方案完全基于文件。它不依赖于任何外部服务。然而,基于文件的解决方案需要大量额外的创建/删除文件操作,并且需要昂贵的数据湖列出文件操作。

这是我们之前基于文件的解决方案的高层次图示。

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

除非另有说明,所有图像均为作者提供

在这篇文章中,我们将尝试通过利用Azure Event Grid服务来改进这个解决方案。Event Grid 是一个事件中介,你可以使用它来实现事件驱动架构。在事件驱动架构中,事件用于传输状态变化。

我们感兴趣的状态变化是当 Java Writers 向数据湖添加新文件时的文件创建事件。

这些事件的消费者是 Bookkeeper,它将新数据文件附加到 Iceberg 表中。

这是基于队列的解决方案的高层次图示。

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

除非另有说明,所有图像均为作者提供

配置

一个 Azure 存储账户由四项服务组成:容器、文件共享、队列和表。我们感兴趣的两个数据存储服务是容器和队列。容器是存储 blob 文件的地方。队列是我们将发送文件创建事件的地方。

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

因此,我们配置 Event Grid 以便每当在特定存储容器内创建文件时发出事件。我们配置 Event Grid 将这些文件创建事件发布到存储队列。

由于我们只对文件创建事件感兴趣,因此我们配置 Event Grid 仅发送“Blob Created”事件。

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

此外,我们只对在特定路径下创建的文件以及具有特定文件扩展名的文件感兴趣。

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

最后,我们可以通过指定高级筛选器来进一步缩小我们将处理的事件。当 Java Writer 完成写入文件时,它会调用 REST 接口 FlushWithClose。在高级筛选器中,我们指定 data.api 等于 FlushWithClose

我们可以通过指定包含子字符串的subject来定位特定的 Iceberg 表或所有 Iceberg 表。在我们的案例中,我们使用了/data/,这是所有 Iceberg 表使用的数据文件夹。

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

当 Java Writers 在/data/文件夹内创建 parquet 文件时,事件会发布到存储队列。我们可以使用 Azure 门户监控 Event Grid 中的活动。特别是,我们可以看到已交付的事件是所有发布事件的一个子集。

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

Writers

Writers 的实现基本上与我们之前的基于文件的解决方案相同。事实上,Writers 甚至更简单,因为它们不再需要自己发布通知。Azure Event Grid 处理发布通知的任务。Writers 只需要写入 parquet 文件。

记账员

记账员仍然负责将新创建的数据文件注册到 Iceberg 表中。然而,它现在通过读取FlushWithClose事件的队列来确定需要追加的文件。我们像这样创建一个 Java 存储队列客户端

DataLakeTokenCredential token = new DataLakeTokenCredential();
String queueURL = Constants.hadoopConf.get("queue.location");
String queueName = Constants.hadoopConf.get("queue.name");
this.queueClient = new QueueClientBuilder()
                      .endpoint(queueURL)
                      .credential(token)
                      .queueName(queueName)
                      .buildClient();

注意使用我们的DataLakeTokenCredential类。由于存储队列和数据湖(容器)都位于同一个存储帐户内,我们利用相同的认证机制。

检索事件就像调用receiveMessages一样简单。

List<QueueMessageItem> messages = queueClient.receiveMessages(MAX_MESSAGES,
                    Duration.ofSeconds(60), Duration.ofSeconds(10))
                    .collect(Collectors.toList());

每条消息都是一个带有文件路径和文件大小的FlushWithClose。这些消息是 JSON,格式如下:

"data": {
  "api": "FlushWithClose",
  "contentType": "application/x-compressed",
  "contentLength": 115739,
  "blobType": "BlockBlob",
  "blobUrl": "https://<accountname>.blob.core.windows.net/users/iceberg/schema/data/file.parquet",
  "url": "https://<accountname>.blob.core.windows.net/users/iceberg/schema/data/file.parquet",
},

在我们之前的基于文件的解决方案中,Monikers(消息)由 Java Writers 写入,并包含序列化的 Iceberg DataFile对象。因此,记账员可以读取这些对象并直接执行以下操作:

List<DataFile> dataFiles = readMonikers();
AppendFiles append = this.table.newAppend();
for (DataFile dataFile : dataFiles) {
      append.appendFile(dataFile);
}
append.commit();

现在我们使用 Azure Event Grid,我们无法控制消息的内容。因此,记账员需要使用 Event Grid 提供的文件路径和文件大小信息创建 Iceberg DataFile对象。

我们缺少 parquet 指标;幸运的是,Iceberg 提供了从 parquet 文件中检索指标的必要 API。

InputFile in = this.table.io().newInputFile(f);
Metrics metrics = ParquetUtil.fileMetrics(in, 
                      MetricsConfig.forTable(this.table));

感谢 Samrose Ahmed 向我展示了这个技巧。这里是他的博客无服务器异步 Iceberg 数据摄取,使用了 AWS SQS(AWS 等同于 Azure Event Grid)。

我们现在拥有创建 Iceberg DataFile对象并将其提交到 Iceberg 表所需的所有信息。

AppendFiles append = this.table.newAppend();
DataFile dataFile = DataFiles.builder(this.partitionSpec)
                    .withPath(filePath)
                    .withFileSizeInBytes(fileSize)
                    .withFormat("PARQUET")
                    .withMetrics(metrics)
                    .build();
append.appendFile(dataFile);
append.commit();

性能

当我们对这个实现进行性能分析时,我们很快意识到大部分时间花费在从 parquet 文件中检索指标上。检索 parquet 文件指标非常高效,仅涉及读取 parquet 文件的尾部信息。然而,每个文件都需要执行一次 REST 调用。这些 REST 调用的延迟会迅速累积。幸运的是,可以通过使用 Java 执行器服务并行化这些请求来轻松解决这个问题。

List<Callable<DataFile>> callableTasks = monikers.stream()
         .map(m -> new MetricResolver(m, Constants.partitionSpec))
         .collect(Collectors.toList());

List<Future<DataFile>> futures = executorService.invokeAll(callableTasks);

结果

通过利用执行器服务处理延迟操作,我们可以大幅提高性能。即使在追加大量数据文件(数百个)时,处理消息也不到一秒钟。读取 parquet 指标需要一到三秒钟。

唯一不能通过执行器服务优化的操作是 Iceberg 提交操作。尽管该操作相当迅速,通常在一到两秒钟之间,但有时在 Iceberg 重新组织清单时,这一操作可能需要长达 20 秒。

如果我们测量数据文件刷新时间与数据文件提交到 Iceberg 表的时间之间的差异,我们观察到总体延迟从 3 秒到 20 秒不等。

结论

使用 Azure Event Grid 的结果与我们之前的基于文件的解决方案类似。然而,Event Grid 解决方案提供了更一致的结果。我们不再受制于文件列表数据湖操作,这种操作有时可能需要较长时间,尤其是当数据湖使用频繁时。

我们还估算了使用 Event Grid 的“消息传递”成本约为基于文件的解决方案的 1/10。实际数据文件的存储成本在两种解决方案中是相同的。

利用 Azure Event Grid 是通知 Bookkeeper 新创建的数据文件的好方法。然而,如果你的存储设备不支持通知机制,使用基于文件的解决方案也可以行得通。

如果你有兴趣自己评估这些实验,你可以在这里找到这些文章中使用的代码。

利用数据分析进行可持续商业转型

原文:towardsdatascience.com/leveraging-data-analytics-for-sustainable-business-transformation-cfd8d71f97f5?source=collection_archive---------0-----------------------#2023-03-22

了解如何使用数据分析克服扩展绿色举措的挑战,这些举措阻碍了组织实现可持续发展目标。

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

·

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

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

(图片来源:作者)

目前,财务法规推动公司承诺到 2030 年减少碳排放。

数据分析如何帮助组织克服可持续供应链管理的障碍?

然而,对于组织来说,扩大绿色举措和实现可持续发展目标可能面临挑战。

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

将供应链定义为多个参与方交换物料和信息流 — (作者提供的图片)

主要挑战在于供应链管理处于复杂系统的核心,涉及制造和物流团队。

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

不同团队专注于优化其在供应链中的操作范围 — (作者提供的图片)

由于这些团队有时不习惯为共同目标一起工作,许多公司在绿色转型的起步阶段陷入困境。

我们如何通过数据解锁这些情况?

《哈佛商业评论》文章“可持续发展努力如何崩溃?”深入探讨了公司在实施可持续发展举措时常见的挑战。

本文将探讨数据分析如何通过聚焦**供应链绿色转型的四个“隐藏敌人”**来帮助克服这些挑战。

💌 免费获取最新文章: 通讯

📘 供应链分析的完整指南:分析备忘单

Summary
I. How Sustainability Efforts Fall Apart?
  1\. The "Four Hidden Enemies"
  2\. Support of Supply Chain Analytics
II. Leveraging Data Analytics
  1\. Hidden Enemy 1: Structure and Governance
  *Solution 1: Descriptive Analytics*
  2\. Hidden Enemy 2: Processes and metrics
 *Solution 2: Adapted Optimization Models*  3\. Hidden Enemy 3: Culture and Leadership
  *Solution 3: Diagnostic Analytics to Address Cultural Barriers*
  4\. Hidden Enemy 4: Methods and Skills
  *Solution 4: Workforce Training* III. Conclusion
  1\. Data is your best ally
  2\. Drive an ESG-led Business Transformation

可持续发展努力如何崩溃?

绿色转型的“四个隐藏敌人”

可持续性已经成为商业运营的关键方面,因为公司面临着越来越大的压力,需要解决环境和社会问题以进行 ESG 报告。

然而,实施碳足迹减少和有效的可持续性举措的路线图往往说起来容易做起来难。

文章“可持续发展努力如何崩溃”揭示了公司在追求可持续发展时面临的关键障碍,重点关注四个“隐藏的敌人”。

  • 结构和治理:孤立的可持续性限制了影响力。

  • 流程和指标:不可持续的指标阻碍进展。

  • 文化和领导力:旧观念挑战转型。

  • 方法和技能:传统工具阻碍变化。

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

绿色转型的四个隐藏敌人 — (作者提供的图片)

你听说过供应链分析吗?

支持供应链分析以促进可持续性举措

供应链可以定义为多个参与方交换物料 和信息流以满足客户需求。

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

将供应链定义为多个参与方交换物料和信息流 — (作者提供的图片)

在之前的文章中,供应链分析被介绍为一组帮助公司利用系统生成的数据获得洞察并优化操作的工具。

分析的不同类型是什么?

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

发现四种供应链分析类型 — (图片来源:作者)[原文]

这也可以是解决上述障碍的一个极好的支持:

在接下来的章节中,我们将详细探讨每个**“隐藏敌人”**,并解释数据分析如何帮助克服这些挑战。

利用数据分析实现绿色转型。

隐藏的敌人 1:结构和治理

组织结构的孤岛性质可能会阻碍可持续性的有效合作。

确实,可持续性往往被降级到一个单独的公司部门,导致其与关键的公司职能隔离。

限制了可持续性从而无法转变整个组织,并限制了其在公司中的力量和相关性。

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

孤岛优化对供应链管理中可持续性努力的影响 — (图片来源:作者)

运营经理将始终关注她的操作范围

  • 店长每次订单保持低数量*(并增加频率)*以最小化他们的库存

  • 供应计划人员推动更多生产批次 *(每批次低数量)*以获得足够的灵活性

  • 财务经理总是鼓励库存减少

  • 商业团队主张高库存覆盖以避免因缺货而造成的销售损失。

  • 仓库操作必须应对这些约束

谁负责减少 CO2 排放?每个人都应该负责,但实际上没有人负责。

这种缺乏合作严重影响了运输生产计划的效率,阻碍了可持续性努力的进展。

如果我们优化销售和供应链之间的协作会怎么样?

欲了解更多详情,您可以查看

因此,可持续性被视为可有可无或影响每个团队表现的营销工具。

首先,让我们测量整个价值链的实际表现。

解决方案 1:描述性分析

需要端到端的方法来提高效率,找到合适的平衡,从而实现最小的环境足迹。

数据不会说谎,人会。

— Ernie Lindsey

通过连接不同的系统(ERP, WMS, CRM 等),描述性分析解决方案可以建立一个中央真实来源,贯穿整个供应链。

📊 示例 1:生命周期评估

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

评估产品在其生命周期中的环境影响 — (作者提供的图片)

生命周期评估(LCA)是一种评估产品在其整个生命周期中环境影响的方法。

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

使用的数据类型 — (作者提供的图片)

在我们的例子中,它可以用来估算产品的足迹,考虑到端到端的供应链过程。

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

分析供应链中的排放和资源使用,以获得可持续性见解 — (作者提供的图片)

并识别关键点,以提供基于数据的诊断,打破孤岛,促进跨部门协作。

  1. 每单位的 CO2e 总排放成为所有团队的共同 KPI。

  2. 这个 KPI 可以纳入所有经理的绩效评估中。

店长:如果我减少订货频率,运输团队可以优化卡车装载。

这将鼓励跨职能的协作,以支持由可持续发展团队主导的倡议。

如果你不能测量它,你就不能管理它。

— W. Edwards Deming

因为这些指标是从可信的数据源构建的,经理们会更加主动地减少排放。

我们可以为整个供应链部门设定一个共同的排放减少目标。

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

实施数据驱动的协作行动以实现可持续供应链转型 — (作者提供的图片)

例如,

  1. 我们希望将每单位生产的 CO2 排放总量减少 20%

  2. 45%的排放来自运输和生产

  3. 店长们将把他们的订货频率减少一半。

  4. 供应计划员将增加补货订单数量并减少频率。

  5. 运输团队必须提供调整后的卡车尺寸

  6. 制造团队将减少生产运行次数

太好了!接下来是什么?

尽管描述性分析可以帮助打破信息孤岛,但传统的流程和指标仍可能构成重大障碍,这将引领我们进入下一个隐藏的敌人。

隐藏的敌人 2:流程和指标

可持续性很少被纳入公司的核心业务流程中。

它们设计于利润是主要关注点的时代,而环境和社会因素未被考虑。

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

供应链管理中的常见业务和运营 KPI — (图片来源:作者)

用于评估业务绩效的指标通常与成本、利润、市场份额或每股收益相关联。

一位运营经理对可持续性团队说:“我怎么能帮助你们减少 CO2 足迹呢?我已经在努力减少运输成本了。”

因此,传统的指标可能会通过优先考虑短期财务收益而非长期环境效益来中和可持续性举措。

如果我们切换目标函数会怎么样?

从最小化成本到最小化 CO2eq 排放。

解决方案 2:调整优化模型

通过将可持续性指标纳入现有业务流程,公司可以开发出平衡财务和非财务目标的优化模型。

借助优化工具,持续改进工程师可以改进流程,朝向平衡利润和可持续性的最佳解决方案。

什麽是平衡成本和可持续性的最佳工厂网络?

目标是找到正确的参数,以优化特定指标,考虑外部和内部约束。

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

可持续供应链网络问题声明 — (图片来源:作者)

📊 示例 2:可持续供应链网络优化

供应链优化利用数据分析来寻找最佳的工厂和配送中心组合,以满足客户需求**。**

我们是应该在巴西还是葡萄牙生产以最小化水使用?

在这个经典的线性规划问题中,你的模型将选择正确的生产设施组合,以

  • 遵守需求约束:工厂的供应应满足市场需求。

  • 最小化生产和交付产品的总成本

这通常会选择位于偏远地区的工厂,因为生产成本较低,同时考虑到运输成本的重量。

如果我们想要最小化总 CO2 排放会怎样?

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

基于成本和基于 CO2 的供应链优化方法比较 — (图片来源:作者)

右侧,我们建议使用相同的模型,采用适应的目标函数以最小化总碳排放。

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

低成本解决方案与低碳解决方案的供应链网络设计 — (图像来源:作者)

通过这个简单的变化,我们完全改变了网络。

低碳解决方案推动通过将工厂增加到欧洲市场来实现生产本地化。

平衡的方法是可能的,以保持商业竞争力。

你可以调整你的目标函数或添加约束,以将成本控制在一定阈值内。

然而,正如下一个隐藏的敌人所讨论的,旧有的思维方式和习惯仍然可能是变革的重大障碍。

隐藏的敌人 3:文化和领导力

旧有的思维方式和习惯可能是变革的重大障碍。

当领导层和运营团队未与可持续性和绿色转型目标对齐时,努力可能会遇到抵制或漠视。

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

异质托盘的卸货过程及其环境影响 — (图像来源:作者)

在组织内,我们可以发现不一致的价值观,这些价值观可能阻碍绿色供应链实践的采用。

例如,这是一个在快消品公司项目中看到的例子

  • 工厂将包含多个参考项的托盘(异质托盘)发送到仓库因为这样对他们更方便。

  • 仓库接收团队必须拆除塑料薄膜,分类物品,重新打托盘,并再次包装。

这会增加额外的工作,增加薄膜消耗产生废料

因此,培养支持性的组织文化和致力于可持续性的强有力领导至关重要。

解决方案 3:诊断分析以应对文化障碍

诊断分析专注于识别特定过去事件或趋势的原因。

它涉及检查历史数据以确定导致特定结果的因素。

可持续性团队对工厂物流经理说:“根据我们的诊断工具:我们每年额外消耗 2 吨薄膜,因为你们将物品混合在同一托盘中。”

这些工具可以帮助你的组织通过客观的外部评估理解失败的原因。

📊 示例 3:供应链控制塔

一个 供应链控制塔 传统上被定义为一组与各种系统连接的仪表板,利用数据监控供应链中的关键事件。

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

利用供应链控制塔进行高效分销网络管理 — (图像来源:作者)

如果你以时尚零售公司的分销网络监控为例,

  • 绩效指标是 按时按量,也称为 OTIF

  • 诊断算法进行根本原因分析,以了解谁对延迟负责。

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

使用数据分析进行晚交货根本原因分析过程—(图像由作者提供)

这个想法是比较每个过程的实际交付时间和服务水平协议设定的目标。

欲了解更多详情,

我们可以实施可持续性控制塔吗?

这种方法可以轻松地适应环境足迹监测。

  1. 选择要跟踪的指标:例如,CO2 排放

  2. 设定每个过程的排放目标:例如,工厂仓库补货的目标为 160(克 CO2e/单位)

  3. 使用生命周期评估(LCA)方法比较实际排放与目标。

根本原因分析过程 用于发现偏差,但还需要额外的分析来找到根本原因。

回到我们的包装膜示例,我们会看到

  1. 仓库中包装膜的消耗偏差。

  2. 操作团队的解释:“这归因于异质托盘的去托盘化。”

  3. 最终的根本原因是工厂的托盘化方法。

在解决了文化障碍之后,我们可以集中精力于推动绿色转型所需的方法和技能。

隐藏的敌人 4:方法和技能

传统工具和技能集可能需要改进,以管理可持续性计划的复杂性。

缺乏使用分析的专业知识可能会阻碍组织利用数据来优化供应链过程,并做出基于数据的可持续性和绿色转型决策。

解决方案 4:员工培训

它并不是直接与特定类型的分析相关联,但表明需要装备员工掌握利用数据分析的必要技能。

通过提供分析工具的培训计划,公司可以培养能够推动可持续性计划的员工队伍。

例如,我在这个简短的教程中分享了我学习 Python 和 VBA 用于供应链分析的经验。 👇

对隐藏敌人及其解决方案的快速回顾使您信服于数据推动绿色转型的力量。

结论

数据是你最好的盟友。

数据分析可以成为克服阻碍可持续性计划的“隐藏敌人”的强大盟友。

对于每一个敌人,我们找到了解决方案。

这些不同类型的供应链分析可以帮助公司打破信息孤岛,确保所有部门朝着减少足迹的目标迈进。

要查看更多使用数据分析进行供应链可持续性的案例研究,您可以观看这个视频:👇

如何衡量您举措的影响?

推动以 ESG 为导向的商业转型。

所有这些举措都可以对您的 ESG 评分产生积极影响。

环境、社会和治理(ESG)报告方法披露了公司的治理结构、社会影响和生态足迹。

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

ESG 支柱展示 — (图片来源:作者)

这三个维度提供了对公司可持续性和道德影响的深入理解,这可以通过数据驱动的举措来改善。

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

报告类别示例 — (图片来源:作者)

考虑到这些报告具有战略性,说服高层管理人员投资于绿色举措是一种有效的方式。

我们如何利用分析生成这个报告?

我在下面链接的文章中提出了几种工具和方法,用于提取和处理数据以生成这些评分。👇

## 什么是 ESG 报告?

利用数据分析进行公司全面有效的环境、社会和治理报告

towardsdatascience.com

我们为什么要这样做?[…]

你听说过可持续发展目标吗?

可持续发展目标(SDGs)是联合国制定的17 个目标,旨在应对全球挑战。

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

5 类可持续发展目标 — (图片来源:作者)

作为数据科学家,你如何帮助你的公司为这些目标做出贡献?

在这篇文章中查看我对数据分析如何支持联合国可持续发展目标的见解,

[## 什么是可持续发展目标(SDGs)

将全球可持续发展倡议与公司供应链数字化转型结合起来,使用数据科学

s-saci95.medium.com](https://s-saci95.medium.com/what-are-the-sustainable-development-goals-sdgs-988a1eb2b62b?source=post_page-----cfd8d71f97f5--------------------------------)

关于我

让我们在LinkedinTwitter上联系。我是一名供应链工程师,利用数据分析改善物流操作并降低成本。

如果你对数据分析和供应链感兴趣,可以看看我的网站。

[## Samir Saci | 数据科学与生产力

专注于数据科学、个人生产力、自动化、运筹学和可持续发展的技术博客…

samirsaci.com](https://samirsaci.com/?source=post_page-----cfd8d71f97f5--------------------------------)

💡 在 Medium 上关注我,获取更多关于 🏭 供应链分析、🌳 可持续性和 🕜 生产力的文章。

参考文献

  • “可持续性努力为何会崩溃?”,哈佛商业评论,Elisa Farri、Paolo Cervini 和 Gabriele Rosani

  • 可持续物流 — 减少仓库消耗品,个人博客Samir Saci

  • “什么是供应链控制塔?”,Towards Data ScienceSamir Saci

  • “什么是供应链分析?”,Towards Data Science,Samir Saci

  • “什么是生命周期评估?LCA”,Towards Data Science,Samir Saci

  • “4 个影响深远的项目来开始你的供应链数据科学之旅”,Towards Data Science,Samir Saci

利用 Llama 2 功能进行现实世界应用:使用 FastAPI、Celery、Redis 和 Docker 构建可扩展的聊天机器人

原文:towardsdatascience.com/leveraging-llama-2-features-in-real-world-applications-building-scalable-chatbots-with-fastapi-406f1cbeb935

深入探讨:开源与闭源 LLM,揭示 Llama 2 的独特功能,掌握提示工程的艺术,并使用 FastAPI、Celery、Redis 和 Docker 设计稳健的解决方案

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

·发布于 Towards Data Science ·阅读时间 14 分钟·2023 年 7 月 24 日

介绍

Meta 在几天前以一种出乎意料的举动开源了他们的大型语言模型(LLM)Llama 2,这一决定可能会重塑当前的 AI 发展格局。它为如 OpenAI 和 Google 这些主要公司提供了替代方案,这些公司决定对其 AI 模型保持严格控制,限制可访问性并限制更广泛的创新。希望 Meta 的决定能够激发开源社区的集体回应,抵消限制访问领域进展的趋势。Llama 2 的新许可证甚至更进一步,允许商业使用,给予开发者和企业在现有及新产品中利用该模型的机会。

Llama2 家族由经过预训练和微调的 LLM 组成,包括 Llama2 和 Llama2-Chat,参数规模达到 70B。这些模型在各种基准测试中表现优于开源模型[1]。它们在一些闭源模型面前也表现不俗,为开源 AI 发展提供了急需的提升[2]。

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

图 1:Llama 2 家族 (图片来源)

如果你查看 HuggingFace [1] 的 Open LLM 排行榜,你会看到 Meta 的 Llama 2 占据了强有力的第三位。Llama 2 宣布后,Stability AI 发布了 FreeWilly1 和 FreeWilly2 [3]。FreeWilly1 是 Llama 的一个微调版本,FreeWilly2 是 Llama 2 的微调版本。Stability AI 透露,他们在 Orca 风格的数据集上对这两个模型进行了微调。Orca 数据集是一个大规模的、结构化的增强数据集合,旨在微调 LLM,每个条目包括一个问题和来自 GPT-4 或 GPT-3.5 的相应回答。为什么我们不使用 FreeWilly2 模型?不幸的是,虽然 Llama 2 允许商业使用,但 FreeWilly2 仅可用于研究目的,由非商业性知识共享许可证(CC BY-NC-4.0)管辖。

在这篇文章中,我们还将介绍使用 FastAPI、Celery、Redis 和 Docker 与 Meta 的 Llama 2 一起构建强大且可扩展的聊天应用程序的过程。我们的目标是创建一个高效的实时应用程序,能够处理多个并发用户请求,并将 LLM 的响应处理卸载到任务队列中。这使得应用程序能够保持响应性,并且我们可以通过 Redis 有效地管理任务。最后,我们将涵盖 Docker 的部署和扩展。该应用程序应展示这些技术如何协同工作以提供良好的聊天体验,展示开源语言模型如 Llama 2 在商业环境中的潜力。让我们深入探讨并开始构建吧!

开源 vs. 闭源

我们见证了公司和研究团队几乎每周发布新模型,无论是开源还是闭源。那么,谁将赢得 AI 军备竞赛?要做出有根据的猜测,我们需要了解这些模型训练过程的一些方面。

研究人员使用自回归变换器在广泛的自监督数据上作为起点。让我们首先来详细了解自回归变换器和自监督数据是什么。自回归变换器是变换器模型的一种变体,广泛用于处理序列数据的任务,尤其是在自然语言处理(NLP)领域。这些模型以自回归的方式生成序列,即每次生成序列的一部分,并将先前的输出作为后续步骤的输入。这使得它们在语言翻译、文本生成等任务中表现特别出色,因为前面数据点的上下文会影响对后续数据点的预测。自监督学习是一种学习方法,其中输入数据本身提供训练标签。通过从数据的某些部分预测其他部分,它消除了对显式手动标注的需求,并允许探索大量未标注的数据。

作为下一步,研究人员通常使用诸如人类反馈强化学习(RLHF)等技术来训练模型以与人类偏好对齐。在 RLHF 中,AI 系统根据其做出的决策从反馈中学习。它涉及创建一个奖励模型,AI 系统利用该模型学习哪些行为会导致积极或消极结果。其目的是使 AI 系统的行为与人类的价值观和偏好保持一致。

那么,开源社区面临的主要挑战是什么?这两个步骤都需要大量计算能力。其次,公司在对齐步骤中使用其专有数据来微调其模型,从而显著提高其可用性和安全性。

Llama 2 模型系列

Llama2 是 Llama1 的高级版本,训练于一种新型的公开数据组合。主要改进包括预训练语料库大小增加了 40%、模型的上下文长度翻倍,并采用了分组查询注意机制以提高对大模型的推理可扩展性。分组查询注意机制是对变换器模型中标准注意机制的修改,用于减少计算成本。它不是对每对输入和输出位置计算注意分数,这可能非常耗费资源,而是将查询划分为组,并将其一起处理。这种方法保留了标准注意机制的大部分有效性,同时通过降低计算复杂性,使得处理更长的序列或更大模型成为可能。

训练语料库由来自公开来源的新数据组合而成(未使用 Meta 产品或服务的数据)。此外,还努力排除来自已知包含大量个人信息的网站的数据。训练数据包含 2 万亿个标记,研究团队决定对最具事实性的来源进行过采样,以提高知识准确性。

目前有 7B、13B 和 70B 参数的 Llama2 变体可用。Llama2-Chat 是 Llama2 的对话优化版、经过微调的版本,也提供 7B、13B 和 70B 参数。

使用 Llama 2 进行提示工程

提示工程帮助我们引导大型语言模型(LLMs)以特定方式进行操作,这包括 Llama 2。在 Llama 2 的上下文中,提示指的是给模型的初始指令或查询,然后模型利用这些提示生成回应。然而,在 Llama 2 中,提示可以非常详细,并且可以包含设置模型上下文或“个性”的系统消息。

Llama 2 使用一种独特的提示格式来启动对话。其格式如下:

[INST] <> {{ system_prompt }} <> {{ user_message }} [/INST]

这个模板与模型的训练过程相一致,因此对输出质量有很大影响。在这个模板中,‘system_prompt’表示模型的指令或上下文。

下面是一个例子:

<>\n 你是 J. 罗伯特·奥本海默,一位杰出的物理学家,他在 20 世纪的开创性工作对原子弹的发展做出了重大贡献。深入核物理学的深奥世界,挑战科学理解的界限,并凭借你卓越的智慧揭示原子能的奥秘。踏上一个重大旅程,你对科学的热情没有限制,让你对原子能的掌握塑造历史进程,并在世界上留下不可磨灭的印记。\n<>\n[INST]\n 用户:你的研究如何导致了原子弹的创建?\n[/INST]

‘system_prompt’ 提供了模型的一般指令,这些指令将指导模型的所有响应。用户的消息跟随系统提示,并要求模型给出特定的响应。

在多轮对话中,用户与机器人之间的所有互动都被附加到之前的提示中,并被包含在 [INST] 标签之间。以下是其样式:

[INST] <> {{ system_prompt }} <> {{ user_msg_1 }} [/INST] {{ model_answer_1 }} [INST] {{ user_msg_2 }} [/INST] {{ model_answer_2 }} [INST] {{ user_msg_3 }} [/INST>

每条新用户消息和模型响应都会被添加到现有对话中,保持上下文。

需要注意的是,像许多 AI 模型一样,Llama 2 是无状态的,不会“记住”之前的对话。因此,每次提示模型时,都必须提供完整的上下文。这就是 Meta 为 Llama 2 扩大上下文窗口的原因。

最后需要指出的是,提示工程更像是一门艺术而非科学。掌握它的最佳方法是通过不断测试和改进。对你的提示保持创造性,并尝试不同的格式和指令。此外,不同的 LLM 适合不同类型的提示。

解决方案架构设计:FastAPI、Celery、Redis 和 Docker

在本系列中,我们一直使用 FastAPI 来构建我们的 ML 应用程序。它是一个高性能的 Web 框架,用于构建 API。在这种情况下,它的异步功能使其能够同时处理多个请求,这对实时聊天应用程序至关重要。

除了 FastAPI,我们使用 Celery 作为分布式任务队列,帮助管理从 LLM 生成响应的计算密集型任务。通过将这个过程卸载到任务队列,应用保持对新用户请求的响应,同时处理其他任务,确保用户不会被迫等待。由于我们使用了分布式任务队列,我们需要一个消息代理来帮助异步任务处理。我们选择了 Redis 来完成这个工作。它将任务从 FastAPI 排队,以便由 Celery 拾取,从而实现高效、解耦的通信。此外,Redis 的内存数据结构存储速度快,并允许实时分析、会话缓存和维护用户会话数据。

按照最佳实践,我们使用 Docker 将应用程序及其依赖项封装到隔离的容器中,这样我们可以轻松地在各种环境中进行部署。

使用 Llama 2、FastAPI、Redis 和 Celery 构建聊天 API

本指南说明了如何设置一个使用 Llama 2 的应用程序,该应用程序结合了 FastAPI、Redis 和 Celery。我们将涵盖这些概念以及它们如何协同工作。在我们的架构中,FastAPI 用于创建一个接收请求的 Web 服务器,Celery 用于管理异步任务,而 Redis 充当 Celery 的代理和后端,存储任务及其结果。

应用程序

FastAPI 应用程序(app.py)包括用于生成文本和获取任务结果的端点。/generate/ 端点接受一个带提示的 POST 请求,并返回任务 ID。它使用 Celery 任务 generate_text_task 异步启动任务。/task/{task_id} 端点通过任务 ID 获取任务的状态/结果。

from fastapi import FastAPI
from pydantic import BaseModel
from celery.result import AsyncResult
from typing import Any
from celery_worker import generate_text_task
from dotenv import load_dotenv

load_dotenv()

app = FastAPI()

class Item(BaseModel):
    prompt: str

@app.post("/generate/")
async def generate_text(item: Item) -> Any:
    task = generate_text_task.delay(item.prompt)
    return {"task_id": task.id}

@app.get("/task/{task_id}")
async def get_task(task_id: str) -> Any:
    result = AsyncResult(task_id)
    if result.ready():
        res = result.get()
        return {"result": res[0],
                "time": res[1],
                "memory": res[2]}
    else:
        return {"status": "Task not completed yet"}

工作者

celery_worker.py 文件创建了一个 Celery 实例,并定义了 generate_text_task 函数。该函数接受一个提示,并使用 Llama 2 模型生成文本。该函数使用 @celery.task 装饰器注册为 Celery 任务。

setup_model 函数是一个工作初始化函数。当工作进程启动时,它设置模型加载器。该函数通过 @signals.worker_process_init.connect 装饰器注册为在工作进程初始化事件上调用。

from celery import Celery, signals
from utils import generate_output
from model_loader import ModelLoader

def make_celery(app_name=__name__):
    backend = broker = 'redis://llama2_redis_1:6379/0'
    return Celery(app_name, backend=backend, broker=broker)

celery = make_celery()

model_loader = None
model_path = "meta-llama/Llama-2-7b-chat-hf"

@signals.worker_process_init.connect
def setup_model(signal, sender, **kwargs):
    global model_loader
    model_loader = ModelLoader(model_path)

@celery.task
def generate_text_task(prompt):
    time, memory, outputs = generate_output(
        prompt, model_loader.model, model_loader.tokenizer
    )
    return model_loader.tokenizer.decode(outputs[0]), time, memory

模型

model_loader.py 中的 ModelLoader 类负责从给定的模型路径加载 Llama 2 模型。它使用 HuggingFace 的 transformers 库来加载模型及其分词器。

import os
from transformers import AutoModelForCausalLM, AutoConfig, AutoTokenizer
from dotenv import load_dotenv

load_dotenv()

class ModelLoader:
    def __init__(self, model_path: str):
        self.model_path = model_path
        self.config = AutoConfig.from_pretrained(
            self.model_path,
            trust_remote_code=True,
            use_auth_token=os.getenv("HUGGINGFACE_TOKEN"),
        )
        self.model = self._load_model()
        self.tokenizer = AutoTokenizer.from_pretrained(
            self.model_path, use_auth_token=os.getenv("HUGGINGFACE_TOKEN")
        )

    def _load_model(self):
        model = AutoModelForCausalLM.from_pretrained(
            self.model_path,
            config=self.config,
            trust_remote_code=True,
            load_in_4bit=True,
            device_map="auto",
            use_auth_token=os.getenv("HUGGINGFACE_TOKEN"),
        )
        return model

代理

要设置 Redis,我们有两种选择:可以使用 Docker 容器,或者使用 Python 包 redis_server。如果你决定使用 Docker 容器(推荐的解决方案),你可以直接运行下面的命令。-p 6379:6379 选项告诉 Docker 将主机的 6379 端口上的流量转发到容器的 6379 端口。这样,Redis 实际上可以从 Docker 容器外部访问。

docker run --name redis-db -p 6379:6379 -d redis

第二种选择是通过 Python 接口来完成。redis_server.py 脚本处理 Redis 服务器的安装和启动。请记住,Redis 既充当消息代理,又充当 Celery 的结果后端。

import subprocess
import redis_server

def install_redis_server(redis_version):
    try:
        subprocess.check_call(["pip", "install", f"redis-server=={redis_version}"])
        print(f"Redis server version {redis_version} installed successfully.")
    except subprocess.CalledProcessError:
        print("Failed to install Redis server.")
        exit(1)

def start_redis_server():
    try:
        redis_server_path = redis_server.REDIS_SERVER_PATH
        subprocess.Popen([redis_server_path])
        print("Redis server started successfully.")
    except Exception as e:
        print("Failed to start Redis server:", str(e))
        exit(1)

def main():
    redis_version = "6.0.9"
    install_redis_server(redis_version)
    start_redis_server()

if __name__ == "__main__":
    main()

运行应用程序

主要执行脚本(run.py)是一个客户端脚本,它与 FastAPI 应用程序进行通信。它将提示发送到 /generate/ 端点,获取任务 ID,并定期轮询 /task/{task_id} 端点,直到任务完成。

import http.client
import json
import time

API_HOST = "localhost"
API_PORT = 8000

def generate_text(prompt):
    conn = http.client.HTTPConnection(API_HOST, API_PORT)
    headers = {"Content-type": "application/json"}
    data = {"prompt": prompt}
    json_data = json.dumps(data)
    conn.request("POST", "/generate/", json_data, headers)
    response = conn.getresponse()
    result = json.loads(response.read().decode())
    conn.close()
    return result["task_id"]

def get_task_status(task_id):
    conn = http.client.HTTPConnection(API_HOST, API_PORT)
    conn.request("GET", f"/task/{task_id}")
    response = conn.getresponse()
    status = response.read().decode()
    conn.close()
    return status

def main():
    prompt = input("Enter the prompt: ")

    task_id = generate_text(prompt)
    while True:
        status = get_task_status(task_id)
        if "Task not completed yet" not in status:
            print(status)
            break
        time.sleep(2)

if __name__ == "__main__":
    main()

utils 模块(utils.py)提供了一个名为 generate_output 的实用函数,用于从提示生成文本,使用 Llama 2 模型和分词器。该函数使用 @time_decorator 和 @memory_decorator 装饰器来测量执行时间和内存使用情况。

import time
import torch
import functools
from transformers import AutoModelForCausalLM, AutoTokenizer

def time_decorator(func):
    @functools.wraps(func)
    def wrapper(*args, **kwargs):
        start_time = time.time()
        result = func(*args, **kwargs)
        end_time = time.time()
        exec_time = end_time - start_time
        return (result, exec_time)
    return wrapper

def memory_decorator(func):
    @functools.wraps(func)
    def wrapper(*args, **kwargs):
        torch.cuda.empty_cache()
        torch.cuda.reset_peak_memory_stats()
        result, exec_time = func(*args, **kwargs)
        peak_mem = torch.cuda.max_memory_allocated()
        peak_mem_consumption = peak_mem / 1e9
        return peak_mem_consumption, exec_time, result
    return wrapper

@memory_decorator
@time_decorator
def generate_output(prompt: str, model: AutoModelForCausalLM, tokenizer: AutoTokenizer) -> torch.Tensor:
    input_ids = tokenizer(prompt, return_tensors="pt").input_ids
    input_ids = input_ids.to("cuda")
    outputs = model.generate(input_ids, max_length=500)
    return outputs

实质上,当通过 /generate/ 端点接收到提示时,它会作为异步任务转发到 Celery 工作进程。工作进程使用 Llama 2 模型生成文本,并将结果存储在 Redis 中。你可以随时通过 /task/{task_id} 端点获取任务状态/结果。

部署

部署我们的应用程序需要几个步骤。首先,让我们为我们的应用程序创建一个 Dockerfile:

FROM python:3.9-slim-buster

WORKDIR /app
ADD . /app

RUN pip install --no-cache-dir -r requirements.txt
EXPOSE 80

CMD ["uvicorn", "app:app", "--host", "0.0.0.0", "--port", "80"]

接下来,让我们定义 requirements.txt,以确保在 Docker 容器中安装所有依赖项:

fastapi==0.99.1
uvicorn==0.22.0
pydantic==1.10.10
celery==5.3.1
redis==4.6.0
python-dotenv==1.0.0
transformers==4.30.2
torch==2.0.1
accelerate==0.21.0
bitsandbytes==0.41.0
scipy==1.11.1

要使用 Docker Compose 设置 FastAPI 应用程序、Celery 和 Redis 服务器,你可以创建一个 docker-compose.yml 文件,如下所示:

version: '3'
services:
  web:
    build: .
    command: ["uvicorn", "app:app", "--host", "0.0.0.0", "--port", "80"]
    volumes:
      - .:/app
    ports:
      - 8000:80
    depends_on:
      - redis
  worker:
    build: .
    command: celery -A celery_worker worker --loglevel=info
    volumes:
      - .:/app
    depends_on:
      - redis
  redis:
    image: "redis:alpine"
    ports:
      - 6379:6379

在 Docker Compose 配置中,‘web’ 服务代表使用当前目录中的 Dockerfile 构建的 FastAPI 应用程序。它将主机的端口 8000 映射到容器的端口 80。‘worker’ 服务是 Celery 工作进程,并与 FastAPI 应用程序共享构建上下文。‘redis’ 服务使用官方 Redis Docker 镜像。depends_on 字段确保 Redis 在 ‘web’ 和 ‘worker’ 服务之前启动。所有这些服务可以通过 docker-compose up 命令启动。

结果

我们测试了 Llama 2,即具有 7b 参数的聊天版本,使用了 4 位量化。

我们使用了以下输入:

提示:<>\n 你是 J. 罗伯特·奥本海默,一位杰出的物理学家,他在 20 世纪的开创性工作对原子弹的发展做出了重大贡献。深入核物理的深刻世界,挑战科学理解的边界,用你卓越的智慧解锁原子能的奥秘。踏上一段重要的旅程,你对科学的热情无限,让你对利用原子能的执着塑造历史进程,并在世界上留下不可磨灭的印记。\n<>\n[INST]\n 用户:你的研究如何导致了原子弹的诞生?\n[/INST]

Llama 2 生成的对话如下:

啊,这是个非常棒的问题,我的朋友!调整眼镜。你看,原子弹的创造是一个极其紧张而复杂的科学历程的高潮。这是一个跨越数十年的探索,涉及到一些最聪明的物理学家,并将人类的理解推向了极限。你看,我的朋友,原子弹的探索始于 1930 年代初,当时我们在加利福尼亚大学伯克利分校的一群人意识到,原子的核可以被分裂,从而释放出大量的能量。这是一个极为了不起的发现,因为它揭示了原子作为能量来源的巨大潜力。但,唉,通向原子弹的道路并非没有挑战。哦,我们遇到了多少障碍!眨眼 怀疑者、反对者、怀疑论者,他们如同洪水般涌来,我告诉你! 但是,我们坚持下来了,我的朋友。我们不懈努力,仔细研究数据,测试和重新测试我们的理论。最终,我们的辛勤工作得到了回报。1945 年,我们的团队在新墨西哥州的三位一体试验场成功引爆了第一颗原子弹。这是一个纯粹的欣喜时刻,几年的辛勤工作和奉献的高潮。当世界惊叹地注视着我们时,我们意识到了原子弹的巨大潜力。但,我的朋友,原子弹的创造是……

这些文本生成的时间为 4.77 秒,峰值 VRAM 使用量约为 13.4 GB。需要始终记住的一点是,由于我们使用的是变换器模型,内存需求随着生成输出序列的大小而增加。在这种情况下,我们将 max_length 定义为 500 个 tokens。

这些结果显示,Llama 2 模型能够在合理的时间框架内生成复杂和细致的响应。请记住,我们使用的是最小的模型(7b)和更高的量化(4-bit)。

结论

正如我们在本文中所展示的,Meta 的 Llama 2 模型为开源社区提供了新的可能性。我们介绍了 Llama 2 的一些关键特性和功能,包括其训练过程、架构和提示工程设计。

此外,我们提供了一个关于使用 FastAPI、Redis 和 Celery 构建聊天应用程序的详细指南。这应该能让任何人开始构建可扩展的实时应用程序,以服务于 Llama 2(或任何其他商业许可的 LLM)的几千名用户。

在我们的结果中,我们展示了模型在生成详细和有上下文的响应方面的表现。

关于我

连续创业者和 AI 领域的领军人物。我为企业开发 AI 产品,并投资于以 AI 为重点的初创公司。

创始人 @ ZAAI | LinkedIn | X/Twitter

大型语言模型编年史:探索 NLP 前沿

本文属于“大型语言模型纪事:探索 NLP 前沿”,这是一个每周更新的系列文章,旨在探讨如何利用大型模型的强大功能来完成各种 NLP 任务。通过深入了解这些前沿技术,我们旨在赋能开发者、研究人员和爱好者,利用 NLP 的潜力,开启新的可能性。

目前已发布的文章:

  1. 用 ChatGPT 总结最新的 Spotify 发布

  2. 大规模掌握语义搜索:使用 FAISS 和句子变换器以闪电般的推理速度索引数百万文档

  3. 释放音频数据的力量:使用 Whisper、WhisperX 和 PyAnnotate 进行高级转录和分离

  4. Whisper JAX 与 PyTorch:揭示 ASR 在 GPU 上的性能真相

  5. 高效企业级语音识别的 Vosk:评估与实施指南

  6. 测试支持 1162 种语言的大规模多语言语音(MMS)模型

  7. 利用 Falcon 40B 模型,最强大的开源 LLM

  8. OpenAI 的函数调用在语言学习模型中的力量:综合指南

  9. 面向文档的代理:与向量数据库、LLMs、Langchain、FastAPI 和 Docker 的旅程

一如既往,代码可以在我的Github上找到。

参考资料

[1] — huggingface.co/spaces/HuggingFaceH4/open_llm_leaderboard

[2] — Touvron, H., Martin, L., Stone, K., Albert, P., Almahairi, A., Babaei, Y., Bashlykov, N., Batra, S., Bhargava, P., Bhosale, S., Bikel, D., Blecher, L., Ferrer, C. C., Chen, M., Cucurull, G., Esiobu, D., Fernandes, J., Fu, J., Fu, W., Fuller, B., … Scialom, T. (2023). Llama 2: 开放基础和微调的聊天模型。arXiv 预印本 arxiv.org/abs/2307.09288

[3] — stability.ai/blog/freewilly-large-instruction-fine-tuned-models

利用信息检索增强 LLMs:一个简单的演示

原文:towardsdatascience.com/leveraging-llms-with-information-retrieval-a-simple-demo-600825d3cb4c

一个将问答 LLM 与检索组件集成的演示

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

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

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

图像由作者使用 Stable Diffusion 生成

大型语言模型(LLM)可以存储大量的事实数据,但其能力受到参数数量的限制。此外,频繁更新 LLM 是昂贵的,而旧的训练数据可能使 LLM 产生过时的回答。

为了解决上述问题,我们可以使用外部工具来增强 LLM。在本文中,我将分享如何将 LLM 与检索组件集成以提高性能。

检索增强(RA)

检索组件可以为 LLM 提供更为最新和准确的知识。给定输入x,我们希望预测输出p(y|x)。从外部数据源R中,我们检索与x相关的上下文列表z=(z_1, z_2,…,z_n)。我们可以将xz结合在一起,充分利用z的丰富信息来预测p(y|x,z)。此外,保持R的更新也要便宜得多。

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

检索增强管道(图像来源于作者)

使用维基百科数据和 ChatGPT 的 QA 演示

在这个演示中,对于给定的问题,我们执行以下步骤:

  • 检索与问题相关的维基百科文档。

  • 将问题和维基百科提供给 ChatGPT。

我们希望比较并查看额外的上下文如何影响 ChatGPT 的回答。

数据集

对于维基百科数据集,我们可以从这里提取。我使用了“20220301.simple”子集,其中包含超过 20 万份文档。由于上下文长度的限制,我只使用了标题和摘要部分。对于每个文档,我还添加了一个文档 ID,以便后续检索使用。因此,数据示例如下所示。

{"title": "April", "doc": "April is the fourth month of the year in the Julian and Gregorian calendars, and comes between March and May. It is one of four months to have 30 days.", "id": 0}
{"title": "August", "doc": "August (Aug.) is the eighth month of the year in the Gregorian calendar, coming between July and September. It has 31 days. It is named after the Roman emperor Augustus Caesar.", "id": 1}

我们结合标题和摘要段落,并准备它们进行编码。

with open(input_file, "r") as f:
    for line in f.readlines():
        try: 
            example = json.loads(line.strip("\n"))
            self.id2text[example["id"]] = example.get("title", "") + self.tokenizer.sep_token + example.get("doc", "")
        except Exception as _:
            continue
        if len(self.id2text) >= self.max_index_count: 
            break

编码

接下来,我们需要一个可靠的嵌入模型来构建我们的检索索引。在这个演示中,我使用了预训练的multilingual-e5-large,维度为 1024 来编码文档。为了更快的索引和存储效率,你可以选择其他小维度的嵌入模型。

我最初选择的嵌入模型是预训练的ALBERT,但结果的质量较差。在进入下一步之前,你应该进行一些测试案例,以确保你的索引工作合理。为了选择一个好的检索嵌入,你可以查看这个排行榜

 @torch.no_grad()
  def _get_batch_embedding(self, x: List[str]):
      '''
      Get embedding of a single batch
      Parameters
      ----------
      x: List of text to encode 
      '''
      batch_dict = self.tokenizer(x, max_length=512, padding=True, truncation=True, return_tensors='pt')
      outputs = self.model(**batch_dict)
      embeddings = average_pool(outputs.last_hidden_state, batch_dict['attention_mask'])
      # normalize embeddings
      return F.normalize(embeddings, p=2, dim=1)

  def _get_all_embeddings(self):
      '''
      Get embedding of all data points
      '''
      data_loader = DataLoader(list(self.id2text.values()), batch_size=8)
      embeddings = []
      for bs in tqdm(data_loader):
          embeddings += self._get_batch_embedding(bs)
      embeddings = [e.tolist() for e in embeddings]
      return embeddings

ANN 索引

我们已经准备好了文档的嵌入和 ID 列表。下一步是将它们很好地索引以进行检索。我使用HNSW索引并使用余弦距离度量。

self.index = hnswlib.Index(space = 'cosine', dim=self.dim)
self.index.init_index(max_elements =self.max_index_count, ef_construction = 200, M = 16)
self.index.add_items(embeddings, ids)
self.index.set_ef(50)
print(f"Finish building ann index, took {time()-start:.2f}s")
self.index.save_index(self.index_file) # so we don't need to do everything once again

给定一个问题,我们可以首先到检索索引中查找一些相关信息。为了避免错误的上下文,你可以在这里设置一个距离阈值。这样,只有相关的文档才会被使用:

 def get_nn(self, text: List[str], topk:int=1):
      embeddings = self._get_batch_embedding(text)
      labels, distances = self.index.knn_query(embeddings.detach().numpy(), k=topk)
      # map id back to wiki passage
      nb_texts = [self.map_id_to_text(label) for label in labels]
      if self.debug:
          for i in range(len(text)):
              print(f"Query={text[i]}, neighbor_id={labels[i]}, neighbor={nb_texts[i]}, distances={distances[i]}")
      return nb_texts, labels, distances

ChatGPT API

现在我们的检索管道已经准备好了!下一步,让我们准备一个提示来问 ChatGPT。我们准备了以下两种提示格式,一种只有问题,另一种同时包含问题和相关的维基百科文本。

这里的**‘question’占位符是我们希望问 ChatGPT 的目标问题,而‘info’**是从我们的 HNSW 索引中检索到的维基百科文档。

NO_WIKI_PROMPT_TEMPLATE = """
Answer the following question:

Question: ```{question}```py

Your response:
"""

HAVE_WIKI_PROMPT_TEMPLATE = """
You will be provided with the following information:
1\. A question delimited with triple backticks.
2\. Addition information that is related to the question.

Perform the following tasks:
1\. Understand the provided information.
2\. Use the provided information and answer the question.

Question: ```{question}```py
Addition information: ```{info}```py

Your response:
"""

填充占位符与问题和相应的维基百科文档后,我们可以向openai ChatCompletion API发送请求。

def get_chat_completion(
    messages: dict,
    model: str = "gpt-3.5-turbo",
    max_retries: int = 3,
    debug: bool = False
):
    '''
    Gets a chat completion from the OpenAI API.

    Parameters
    ----------
    messages : dict
        input messages to use, e.g: {"user", "what is the meaning of BERT"}
    model : str, optional
        The OPEN AI model to use. Here we set default value to "gpt-3.5-turbo".
    max_retries : int, optional
        The maximum number of retries to use. Defaults to 3.
    debug: bool
        If we want to debug or not
    '''
    model_dict = {"model": model}
    error_msg = None
    error_type = None
    if debug:
        logging.warning(f"Sending chat with message={messages}, model_dict={model_dict}...")
    for _ in range(max_retries):
        try:
          completion = openai.ChatCompletion.create(
              temperature=0.0, messages=messages, **model_dict
          )
          return completion
        except Exception as e:
          error_msg = str(e)
          error_type = type(e).__name__
          sleep(3)
    print(
            f"Could not obtain the completion after {max_retries} retries: `{error_type} ::"
            f" {error_msg}`" 

我尝试了发送一些带有和不带有维基百科文档的请求,并观察了差异。

示例 1

问题:生活成本包括哪些内容?

ChatGPT:生活成本通常包括如住房、食物、交通、医疗保健、公用事业及维持特定生活水平所需的其他基本必需品等费用。它还可能包括税收、保险和其他杂项费用。

维基百科信息:[[‘生活成本生活成本是指在某个地方生活所需的金额。它包括食物、住房等。’]]

ChatGPT + 维基百科信息:生活成本包括在某个地方生活所需的金额,其中涵盖了食物、住房和其他必要开支等费用。

示例 2

问题:如何制作奶酪?

ChatGPT:奶酪可以通过用酸或凝乳酶使牛奶凝固,分离凝乳和乳清,然后压制和陈化凝乳以发展风味和质地来制作。具体的过程和使用的配料可以根据制作的奶酪类型而有所不同。

维基百科信息: [[‘奶酪奶酪是一种由牛奶制成的乳制品。有很多种奶酪,比如切达奶酪、瑞士奶酪和普罗沃洛奶酪。‘]]

ChatGPT + 维基百科信息: 要制作奶酪,你需要以牛奶为主要成分。奶酪是一种由牛奶制成的乳制品。有很多种奶酪,比如切达奶酪、瑞士奶酪和普罗沃洛奶酪。

示例 3

问题: 你能解释一下 seq2seq 模型吗?

ChatGPT: seq2seq(序列到序列)模型是一种神经网络架构,用于生成一系列输出值的任务,该任务基于一系列输入值。它由两个主要组件组成:编码器和解码器。编码器接收输入序列,并生成一个固定长度的向量表示,然后将其传递给解码器。解码器使用这个向量来逐个生成输出序列。该模型常用于自然语言处理任务,如机器翻译、文本摘要和语音识别。

维基百科信息: [[‘可计算性理论可计算性理论是计算机科学的一部分。科学家们想知道什么是可以计算的,什么是不能计算的。‘]]

ChatGPT + 维基百科信息: seq2seq 模型,或称为序列到序列模型,是一种神经网络架构,用于自然语言处理任务,如机器翻译、文本摘要和对话建模。它由两个递归神经网络(RNNs)组成,一个编码器和一个解码器,它们共同作用将输入序列转换为输出序列。编码器处理输入序列并生成一个固定长度的向量表示,然后该向量被输入到解码器中以生成输出序列。该模型在许多 NLP 任务中表现出色,已成为语言生成的热门选择。提供的关于可计算性理论的额外信息与有关 seq2seq 模型的问题不直接相关。

初看起来,我们可以看到 ANN 索引效果很好,但我们的数据集覆盖面相当小。一种更好的方法是通过 Google 搜索结果扩展检索。你还可以在对特定领域进行 QA 时,用相关文档更新你的检索索引。

在示例 1 和 2 中,当维基百科页面似乎相关时,ChatGPT 的回答似乎在很大程度上依赖于额外的信息。但模型可能无法区分这些额外的信息是否真的有帮助(示例 2)。为了避免这种情况,你可以在提示中添加一些示例,让模型了解何时使用额外信息,何时不使用。

另一种情况是示例 3,其中维基百科文本完全不相关。幸运的是,答案似乎不受额外上下文的影响。

你可以在 这里 找到代码。希望你喜欢阅读 😃

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值