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

原文:TowardsDataScience

协议:CC BY-NC-SA 4.0

如何在 10 分钟内利用 LlamaIndex 构建定制的 GPT 聊天机器人

原文:towardsdatascience.com/how-i-built-a-custom-gpt-based-chatbot-in-under-10-minutes-with-llamaindex-2102f0173420

Python 的概述和实现

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

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

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

照片来源:Miguel Á. Padriñán: www.pexels.com/photo/two-white-message-balloons-1111368/

不久前,我读到了一篇由 Jerry Liu 撰写的文章,介绍了 LlamaIndex,这是一种利用 GPT 来综合响应用户提供信息的接口。

[## 如何利用非结构化数据和 LlamaIndex 将 LLM 的力量带入你的数据

(合著者:LlamaIndex 的创始人 Jerry Liu 和 Unstructured 的 CEO Brian Raymond)

medium.com

它立刻引起了我的注意,我知道我必须亲自尝试一下。

毕竟,许多在世界上引起轰动的大型语言模型(LLM)由于没有用用户可用的数据进行训练,因此用途有限。

因此,仍然需要可定制的 LLM,以满足用户的需求。简单来说,企业需要能够处理文本总结、问答(Q&A)以及使用他们自己信息生成文本的 LLM。

为了看看 LlamaIndex 是否有潜力满足这一需求,我尝试了它的功能,并对我能够做到的事情感到真正惊讶。我甚至在大约 10 分钟内构建了一个 Q&A 聊天机器人!

在这里,我将带你深入了解我的旅程。

LlamaIndex 的功能是什么?

LlamaIndex 通过将 LLM 连接到用户提供的信息来生成查询响应。

文档中详细描述的,使用 LlamaIndex 涉及以下步骤:

  1. 加载文档

  2. 将文档解析成节点(可选)

  3. 构建索引

  4. 在构建的索引上构建索引(可选)

  5. 查询索引

实质上,LlamaIndex 将数据加载到文档对象中,然后将其转换为索引。当索引接收到查询时,它将查询传递给 GPT 提示,以合成响应。默认情况下,这使用的是 OpenAI 的 text-davinci-003 模型。

虽然这个过程听起来相当复杂,但实际上可以用很少的代码来执行,正如你将要发现的那样。

设置

为了测试 LlamaIndex 的多样性,我最终构建了 3 个不同的聊天机器人,每个机器人都使用不同的数据源。为了简洁起见,之前提到的可选步骤(即第 2 步和第 4 步)将被省略。

首先,让我们处理好前提条件。

可以使用以下命令通过 pip 安装 LlamaIndex 和 OpenAI:

pip install llama-index
pip install openai

用户还需要一个来自 OpenAI 的 API 密钥:

import os

os.environ['OPENAI_API_KEY'] = 'API_KEY' 

项目还需要以下导入:

加载数据

数据可以手动加载,也可以通过数据加载器加载。在这个案例中,我加载了 3 种数据:

第一个索引将使用本地 .txt 文件创建,该文件位于名为 data 的文件夹中。这些数据将手动加载。

第二个索引将使用来自 Wikipedia 页面关于苹果的数据创建。它可以通过 LlamaIndex 的数据加载器之一进行加载。

第三个索引将使用展示如何制作香草蛋糕的 YouTube 视频构建。这些数据也将通过数据加载器加载。

构建索引

所有数据加载到文档对象中后,我们可以为每个聊天机器人构建索引。

每个索引可以通过一行代码从文档对象构建。

吃惊吗?从加载数据到创建索引,使用 LlamaIndex 只需要几行代码!

查询索引

现在,构建的索引可以为任何给定的查询生成响应。再次强调,这一步也可以用一行代码完成。

  1. 查询使用 .txt 文件构建的索引

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

代码输出(作者创建)

如果你在想,这就是正确的答案。

2. 查询使用 Wikipedia 页面(主题:苹果)构建的索引

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

代码输出(作者创建)

3. 查询使用 YouTube 视频(主题:香草蛋糕食谱)构建的索引

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

代码输出(由作者创建)

最后,值得注意的是,索引仅在包含所需上下文时才会提供查询的答案。

这是如何使用 YouTube 视频数据创建的相同索引对完全不相关的查询的响应。

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

代码输出(由作者创建)

幸运的是,LlamaIndex 似乎已采取措施防止幻觉(即模型自信地给出未由数据支持的答案)。

使用 Web 应用部署聊天机器人

最后,我们可以创建一个 Web 应用,以便与最终用户分享构建的索引。

为此,我们需要首先使用save_to_disk方法保存索引。

这些索引将用于 Streamlit 应用。整个应用的底层代码如下:

在应用中,用户可以选择他们希望根据其提问的数据源,并在提供的框中输入他们的查询。

我们可以看到在运行应用后,索引的表现如何:

streamlit run app.py

查询使用.txt 文件(我最喜欢的水果)构建的索引:

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

Streamlit 应用(由作者创建)

查询使用维基百科页面(苹果)构建的索引:

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

Streamlit 应用(由作者创建)

查询使用 YouTube 视频(香草蛋糕食谱)构建的索引:

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

Streamlit 应用(由作者创建)

很酷,对吧?我们在短短 10 分钟内构建了一个功能完整的 Web 应用!

结论

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

图片由Prateek Katyal提供,来源于Unsplash

到目前为止,我仅实现了 LlamaIndex 接口的基本功能。这个项目中有很多领域尚未探索,例如自定义 LLMs 和使用非默认设置。

欲了解更多信息,请访问文档

如果你打算自己尝试这个工具,请注意使用 OpenAI 的 API 所产生的费用。这个项目只花了我$1,但这归功于我处理的小文档(定价基于你使用的令牌数量)。如果你过于投入,可能会收到一张令人不快的账单。

最后,创建 Q&A 聊天机器人所用的所有源代码可以在这个 Github 库中访问:

[## GitHub - anair123 的创建自定义 GPT 聊天机器人]

你现在无法执行该操作。你在另一个标签页或窗口中登录了。你在另一个标签页中已退出…

github.com

感谢阅读!

如何构建带有 AI 生成曲目的 Lo-fi 音乐网页播放器

原文:towardsdatascience.com/how-i-built-a-lo-fi-music-web-player-with-ai-generated-tracks-36f3915e39f8?source=collection_archive---------4-----------------------#2023-01-12

使用 Tone.js 和 LSTM 模型制作

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

·

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

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

图片来源于 rupixen.comUnsplash

介绍

自大学以来,低保真嘻哈音乐一直是我学习时的首选伴侣。它以相对简单的音乐结构营造出舒适和宁静的氛围。一些爵士和弦进行、律动的鼓点、环境声音和怀旧的电影台词可以让我们制作出相当不错的低保真嘻哈曲目。除了音乐方面,动画视觉效果也是低保真美学的关键部分,与水、风和火的自然声音一起营造氛围。

创建我自己的低保真网页播放器的想法是在一个星期天下午,当时我正在学习深度生成模型。我做了一些研究,并在假期期间完成了这个项目。该网页播放器提供两个选项:用户可以选择基于 Tone.js 编码的真实歌曲的低保真曲目,或者选择 AI 生成的独奏曲目。两个选项都会与用户在前一步中选择的鼓循环、环境声音和台词叠加在一起。本文主要讨论如何使用 LSTM 模型生成 midi 曲目,并在最后简要讨论如何使用 Tone.js 制作一首歌曲。

你可以在这里尝试网页版播放器,我建议使用桌面版 Chrome 浏览器以获得最佳体验。

LSTM 模型架构与 Midi 生成

在之前的帖子中,我解释了什么是 LSTM 网络。简要回顾一下,它是一种特殊类型的 RNN,能够更好地处理长期依赖。它还具有递归结构,将来自前一个时间步的输出传递到当前时间步。为了更好地理解它,我们可以展开网络,将 LSTM 单元看作是多个相同网络的副本,每个副本将信息传递给下一个时间步,如下所示。

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

展开 LSTM;作者创建

每个单元格包含四个主要组件,使其能够更好地处理长期依赖:

  • 遗忘门:决定遗忘哪些信息

  • 输入门:决定更新和存储哪些信息到我们的单元状态中

  • 单元状态更新:进行逐元素操作以更新单元状态

  • 输出门:决定输出哪些信息

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

在 LSTM 单元内部;作者创建

训练数据准备

对于我们训练模型所使用的音乐数据格式,我们有几种选择:原始音频、音频特征(例如 Mel 频谱图)或符号音乐表示(例如 midi 文件)。我们的目标是生成一个独奏曲目(即音符、和弦和休止符的序列),以便在其他组件(如鼓循环)上叠加,因此 midi 文件是实现我们目标的最简单和最有效的格式。原始音频的训练计算开销非常大。换句话说,以 48000kHz 采样的音乐片段意味着每秒音频中有 48000 个数据点。即使我们将其下采样到 8kHz,这仍然是每秒 8000 个数据点。此外,仅有旋律或和弦进行的干净音频极其稀少。然而,如果我们足够努力,仍然可以找到一些只包含和弦进行/旋律的 midi 文件。

在这个项目中,我使用了一些来自 YouTube 创作者Michael Kim-Sheng的 lo-fi midi 样本,他慷慨地允许我使用他的文件。我还利用了一些在这个Cymatics lo-fi toolkit中授权用于商业用途的 midi 文件。为了确保我在质量数据上训练我的模型(适用于 lo-fi 嘻哈的合理和弦进行和节拍),我听了一部分来自每个来源的曲目,并筛选出我的训练数据集。模型架构的灵感来自于经典钢琴作曲家库这里

加载和编码 midi 文件

一个 Python 包music21可以用于加载 midi 文件。Music21 解析 midi 文件并将每个音乐组件存储到特定的 Python 对象中。换句话说,音符将被保存为 Note 对象,和弦将被保存为 Chord 对象,而休止符将被保存为 Rest 对象。它们的名称、持续时间、音高类别和其他属性可以通过点号表示法访问。Music21 将音乐片段存储在如下面所示的层次结构中,我们可以相应地提取必要的信息。如果你对如何使用这个包感兴趣,包的官方网站有一个适合初学者的用户指南,The Sound of AI 的 Valerio Velardo 也有一个教程来介绍如何使用 music21。

如前所述,music21 将每个音符、休止符和和弦存储为一个 Python 对象,因此下一步是对它们进行编码,并将它们映射到模型可以训练的整数上。模型输出应该包含不仅仅是音符,还包括和弦和休止符,因此我们将分别对每种类型进行编码,并将编码值映射到整数。我们对所有的 midi 文件执行此操作,并将它们连接成一个序列以训练模型。

  • 和弦:获取和弦中音符的音高名称,将它们转换为正常顺序,并用点连接成字符串格式,“#.#.#”

  • 音符:使用音高名称作为编码

  • 休止符:编码为字符串“r”

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

Midi 编码与映射

创建输入和目标对

现在我们已经得到了适合模型的 midi 数据编码。下一步是准备输入和目标对,以便输入到模型中。在一个简单的监督分类机器学习问题中,存在输入和目标对。例如,一个对狗品种进行分类的模型将以狗的毛色、身高、体重和眼睛颜色作为输入,而标签/目标将是狗所属的具体品种。在我们的情况下,输入将是从时间步 i 开始的长度为 k 的序列,而对应的目标将是时间步 i+k 的数据点。因此,我们将遍历编码后的音符序列,并为模型创建输入和目标对。最后一步,我们将输入的维度转换为适合 keras 的格式,并对输出进行 one-hot 编码。

模型结构

如前所述,我们将使用 LSTM 层作为核心结构。此外,该网络还使用了以下组件:

  • Dropout 层:通过在训练期间以一定频率随机将输入单元设置为 0 来规范化网络并防止过拟合(在我们的例子中,频率为 0.3)

  • 密集层:完全连接前一层,并在每个节点执行矩阵-向量乘法。最后一层密集层需要具有与网络中唯一音符/和弦/休止符的总数相同的节点数。

  • 激活层:如果用于隐藏层,则为我们的网络添加非线性;如果用于输出层,则帮助网络进行分类。

model = Sequential()
model.add(LSTM(
    256,
    input_shape=(network_input.shape[1], network_input.shape[2]),
    return_sequences=True
))
model.add(Dropout(0.3))
model.add(LSTM(512, return_sequences=True))
model.add(Dropout(0.3))
model.add(LSTM(256))
model.add(Dense(256))
model.add(Dropout(0.3))
model.add(Dense(n_vocab))
model.add(Activation('softmax'))
model.compile(loss='categorical_crossentropy', optimizer='rmsprop')
model.fit(network_input, network_output, epochs=200, batch_size=128)

对于这个示例网络,使用了 3 层 LSTM,每层 LSTM 后面跟有 2 层 dropout 层。接着是 2 层全连接的密集层,最后是一个 softmax 激活函数。由于输出是分类的,我们将使用分类交叉熵作为损失函数。使用了 RMSprop 优化器,这在 RNN 中相当常见。还添加了检查点,以便在不同的训练轮次中定期保存权重,并在模型训练结束之前使用。请随意调整模型结构,并尝试不同的优化器、轮次和批量大小。

输出生成与解码回 Midi 音符

输出生成过程类似于训练过程——我们给模型一个长度为 m 的序列(我们也称之为序列 m 以简化符号表示),并要求它预测下一个数据点。这个序列 m 的起始索引是从输入序列中随机选择的,但如果我们愿意,也可以指定一个特定的起始索引。模型输出是一个来自 softmax 的概率列表,告诉我们每个类别作为下一个数据点的适合程度。我们将选择概率最高的类别。为了生成长度为 j 的序列,我们会通过删除序列 m 的第一个元素并将最近生成的数据点添加到这个序列 m 中来重复这个过程,直到模型生成 j 个新的数据点。

从上一段生成的数据仍然是整数,因此我们使用编码过程中相同的映射将其解码回音符/和弦/休止符。如果它是和弦字符串格式,我们将从字符串“#.#.#.#”中读取整数符号,并创建一个 music21.chord 对象。如果它是音符或休止符,我们将相应地创建一个对应的音符和休止符对象。同时,我们在每个时间步将生成的新数据点追加到预测输出序列中。有关此过程的示例,请参见下面的示例流程,我们使用 3 个数据点的输入序列生成 4 个数据点的序列。

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

输出生成和 MIDI 解码

现在我们有了一系列的音符、和弦和休止符。我们可以将它们放入一个 music21 流中并写出 MIDI 文件,在这种情况下,所有的音符都会是四分音符。为了保持输出的趣味性,我添加了一个代码片段,随机抽取一个时长来指定每个音符或和弦的时长(默认的概率分布是 0.65 用于八分音符,0.25 用于十六分音符,0.05 用于四分音符和二分音符)。休止符默认为十六分休止符,以避免音符之间的沉默过长。

NOTE_TYPE = {
            "eighth": 0.5,
            "quarter": 1,
            "half": 2,
            "16th": 0.25
        }
offset = 0
output_notes = []
for pattern in prediction_output:
    curr_type = numpy.random.choice(list(NOTE_TYPE.keys()), p=[0.65,0.05,0.05, 0.25])

    # pattern is a chord
    if ('.' in pattern) or pattern.isdigit():
        notes_in_chord = pattern.split('.')
        notes = []
        for current_note in notes_in_chord:
            new_note = note.Note(int(current_note))
            new_note.storedInstrument = instrument.Piano()
            notes.append(new_note)
        new_chord = chord.Chord(notes, type=curr_type)
        new_chord.offset = offset
        output_notes.append(new_chord)
    elif str(pattern).upper() == "R":
        curr_type = '16th'
        new_rest = note.Rest(type=curr_type)
        new_rest.offset = offset
        output_notes.append(new_rest)
    # pattern is a note
    else:
        new_note = note.Note(pattern, type=curr_type)
        new_note.offset = offset
        new_note.storedInstrument = instrument.Piano()
        output_notes.append(new_note)
# increase offset each iteration so that notes do not stack
    offset += NOTE_TYPE[curr_type]
midi_stream = stream.Stream(output_notes)
midi_stream.write('midi', fp='test_output.mid')

一旦我们用不同的参数运行几次模型,并挑选出我们喜欢的曲目,我们会在任何数字音频工作站(DAW)中选择一种 lofi 风格的乐器效果,以便我们生成的曲目听起来更像真正的音乐。然后,我们转到 JavaScript 来构建我们的网页播放器。

使用Tone.js构建网页播放器

Tone.js 是一个用于在浏览器中创建互动音乐的网络音频框架。你可以使用它来构建很多有趣的互动网站(见演示)。但在我们的案例中,它提供了一个全局传输功能,以确保我们的鼓点、环境声音、引号和旋律同时播放。它还允许我们编写音乐乐谱、采样乐器、添加音效(混响、增益等)以及在 JavaScript 中创建循环。代码框架的感谢归功于Kathryn。如果你想要一个快速有效的 Tone.js 速成课程,我强烈推荐他们网站上的用例示例。最重要的收获是,对于我们创建的每个声音事件,我们需要通过toDestination()将它们连接到 AudioDestinationNode(即我们的扬声器),或者通过samplePlayer.chain(effect1, Tone.Destination)来添加音效。然后,通过Tone.Transport,我们将能够在主输出上启动、暂停和调度事件。

循环音频片段

鼓点、环境声音、引号和预生成的 AI 轨道都是通过 Player 类加载到网页播放器中的音频文件(.mp3 或.wav)。在加载来自网站的用户输入事件后,这些事件会被输入到 Tone.js 的 Part 类中以创建循环。

鼓点每 8 小节循环一次,环境音效每 12 小节循环一次,AI 独奏轨道每 30 小节循环一次。引号部分不循环,从第 5 小节开始。

使用乐器样本创建旋律和和弦进程

Tone.js 不提供我们在 DAW 中看到的软件乐器选项,只有采样器,允许我们通过加载几个音符来采样自己的乐器。然后,采样器会自动重新调整样本的音高,以创建未明确包含的音高。

然后,我们可以通过指定音符和音符出现的时间来写入旋律和和弦进程。我建议使用 TransportTime 来精确编码节拍。TransportTime 的格式为"BARS:QUARTERS:SIXTEENTHS",并使用零基数编号。例如,"0:0:2"表示音符将在第一小节的两个十六分音符后出现。"2:1:0"表示音符将在第三小节,经过一个四分音符后出现。我这样为 3 首现有歌曲编写了旋律和和弦进程:FKJ 的《Ylang Ylang》、Camille 的《La Festin》和 Tyler, the Creator 的《See You Again》。

网页播放器设计

我添加了函数,通过不同的环境声音输入来改变网页播放器的背景,以便每种环境下显示更合适的 gif。同时还有一个与歌曲音符连接的可视化工具,由 p5.js 制作,以增加视觉吸引力。

未来工作

LSTM 模型

  • 添加序列开始和序列结束标记,以便模型可以学习音乐模式,当歌曲结束时。

  • 融入音符时长的编码,以便启用节拍跟踪。

网络播放器

  • 将后端 AI 模型连接到网络播放器,将会非常酷,这样输出可以实时生成。目前的障碍是模型生成输出需要几分钟,但我们可能可以利用预训练模型 API。

  • 如果网络播放器允许用户 1) 输入他们自己选择的和弦进行 2) 写下文本并对其进行情感分析并输出匹配情感的和弦进行,那么用户互动将大大改善。

结论

关于代码和训练数据集,请参阅 GitHub 仓库这里。虽然这只是一个简单的 lo-fi 网络播放器,但我在 LSTM 模型和 Tone.js 上玩得很开心。每次看到我们如何将技术融入音乐体验中,总是让我感到惊讶。

我是如何构建一个编程语言的:通往成功的(艰难)之路

原文:towardsdatascience.com/how-i-built-a-programming-language-the-difficult-path-to-success-38aefa2bb69e?source=collection_archive---------14-----------------------#2023-07-20

坦白说,“艰难”这个词还不足以形容。

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

·

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

我的上一篇文章介绍了我的编程语言的语法,并提供了一个关于我如何构建它的总体思路。

但我决定写另一篇文章,专注于我朝最终结果前进的历程,因为说实话,这段旅程确实有更多的挫折而非成功,我必须克服的挑战也非常令人生畏。希望这篇文章也能成为那些正在进行类似项目、面临相似困难的人们的动力,提醒他们不要放弃,知道曾经有其他人也处于相同的位置,并最终达成了他们的目标。

那你为什么应该在意呢?

毕竟,你们都来自不同的背景,无论是数据科学,还是经典的 Python 和 Java。

即使你不感兴趣于构建自己的编程语言,许多我在这次经历中提升的软技能肯定会对你们所有人(尤其是对编程感兴趣的人)有启发作用(希望也是激励作用)。

你写程序时,有多少次每一行看起来都正确,但却得到一个似乎几乎无法调试的神秘错误,这时候你开始失去动力,想着要放弃。

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

照片由 Tim GouwUnsplash 提供

在构建语言的过程中,我遇到了那些非常常见的问题,经过最终克服和坚持,这些经验希望能够传达一个值得铭记的信息,当你继续阅读这篇文章时!

开始动手

这个想法最初是在我上八年级(或者九年级,不太记得了)时产生的,但那时我几乎不了解条件语句和循环,所以我就把它忘了。

但是多年后,同样的想法重新点燃了我的脑海,只是我完全不知道该如何开始。

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

照片由 Towfiqu barbhuiyaUnsplash 提供

就像所有其他我不知道如何做的事情一样,我在谷歌上搜索了一下。

“如何制作自己的编程语言”。

好吧,结果相当有希望。我花了很长时间逐行分析这些代码,抓住了要点:

编程语言有两种类型:解释型和编译型。解释型语言通常构建起来更简单,但较慢。另一方面,编译型语言将你的代码转换为机器代码,然后执行它(编译型语言通常更快)。

构建语言的一般过程是:

  • 定义语言的目的和语法: 对我来说这很简单(我知道我想构建一个与数学相关的语言),为了节省工作量并使语言尽可能高级,我决定只保留几个(基本且有用的)函数和两个变量,一个函数和一个浮点数。

  • 构建词法分析器和语法分析器: 词法分析器是任何语言的第一个组件,将代码中的每个单词/短语分类(如关键字、操作符、注释等)。但仅靠这些词法单元本身并没有意义,要使其有意义,需要构建一个语法分析器。语法分析器确保词法单元遵循语言的语法,并将其放入抽象语法树(AST)中。

  • 执行代码: AST 中的每个节点代表我们原始词法分析器收集的一个标记,包括函数名。因此,运行我们的 AST 允许我们逐行执行编写的代码;这就是解释型编程语言的基本原理。

但编译器需要一些额外的步骤;你需要使用流行的库,如 LLVMlibgccjit,将代码转换为编译后的可执行文件。

编译型还是解释型?

一方面,我可以创建一种解释型语言,这相当具有挑战性,需要我投入大量时间和精力。另一方面,我可以创建一种编译型语言,这更具挑战性,需要我投入更多时间和精力。

但构建自己编译型语言的想法实在是太吸引人了,我陷入了这个陷阱。

建议:不要这样做。

尽管有关于使用 LLVM 构建编译器的资源(强烈推荐观看那段 100 秒的视频,既有趣又有些教育意义),但由于文档过于复杂,我很快就迷失了。

我猜测,你可能比学习使用 LLVM 构建编译器的基础知识要更快地学会 Java 的基础知识。

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

图片由 Emile Perron 提供,来源于 Unsplash

巨大的时间浪费(还是说不是)?

对我来说,不幸的是,我花了两个多月的时间,先是折腾 LLVM,然后是 libgccjit。后者看起来更有希望,我甚至为编译器编写了代码,但随后遇到了大量关于设置 libgccjit 包的问题;我甚至下载了它的全部源代码,并将其放在与我的编译器代码相同的目录中。但仍然没有任何效果。我浏览了无数个 Reddit 和 StackOverflow 的帖子,但没有任何结果。

当 StackOverflow 无法帮助你时,你知道情况非常糟糕。

处于放弃的边缘

此时,我真的陷入了一个新的低谷,因为我很长时间没有取得丝毫进展,开始贬低自己所做的所有工作,比如从头开始构建词法分析器和解析器,而没有使用流行的 lex/yacc 和 flex/Bison 工具。在这时,我问自己:“我是否应该把这一切都扔进(虚拟的)垃圾桶里?”我很想说“是的”,但我不那么容易放弃,特别是在涉及编程相关的项目时。

所以我去散步,清理了思路,然后回到设计图板,这次的目标不同:构建一个解释型语言。

最终完成了它。

一旦我决定要构建一个解释器,我也做出了放弃 C 语言、回到 Python 的艰难决定。在正常情况下,最好使用较低级的语言来构建解释性语言,因为它们的速度弥补了解释器的慢速。

但我当时正处于绝望的时期,那时候需要采取绝望的措施。

回到印度后,我花了接下来的一周处于失眠状态,字面意义上全天 24 小时工作(如果不算早餐、午餐和晚餐的话,大约是 21 小时——我吃得很慢)。

接下来发生的事情是我第一次遇到的,并且可能不会再发生——我的代码第一次尝试就成功了!没有调试,没有更多的困惑。

当我运行我的源代码时,每一个组件:从词法分析器到解析器及 AST,都正常工作,并正确执行了每一行代码。

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

图片由 the blowup 提供,来源于 Unsplash

反思与最终的备注

上面的引用可能听起来有些陈词滥调,但这并不会削弱其有效性;在构建 AdvAnalysis 的过程中,我经历了比成功更多的失败,但最终,我却脱颖而出。

虽然你可以学习在构建自己的编程语言时需要注意的事项(例如编译型与解释型、应遵循的过程),但我得到的主要经验教训(希望你也能得到)是,无论程序中不断遇到令人困惑的错误多么令人沮丧,只要你能够保持耐心,回到起点并采取不同的方法,最终,它总会如你所期待的那样运作。

说实话,作为一名编程爱好者,你可能早就知道了这一点。 😃

我如何用 ChatGPT 编写了自己的私人法语 tutor

原文:towardsdatascience.com/how-i-coded-my-own-private-french-tutor-out-of-chatgpt-16b3e15007bb?source=collection_archive---------1-----------------------#2023-06-30

逐步指南,讲述了我如何利用最新的人工智能服务来学习一门新语言,从架构到提示工程

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

·

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

讨论中提到的外语 tutor 的代码可以在 [companion](https://github.com/shakedzy/companion) 我的 GitHub 页面上的 repo中找到,您可以在任何非商业用途下自由使用。

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

使用 Dall-E 制作

所以在推迟了一段时间之后,我决定重新开始我的法语学习。当我报名参加课程时,这个想法突然出现——如果我能编程让 ChatGPT 成为我的个人法语导师会怎么样?如果我能 与它对话 ,而它会回应我呢? 作为一名与 LLMs 合作的数据科学家,这似乎是值得构建的东西。我是说,是的,我可以直接和我的法语妻子对话,但这不如设计一个由 ChatGPT 构建的个人导师来得酷。爱你,亲爱的❤️。

但说真的,这个项目不仅仅是“另一个酷炫的代码玩具”。生成性 AI 正向我们生活的每一个领域进军,而大型语言模型(LLMs)似乎在这里占据主导地位。如今,一个人通过访问这些模型所能做的事情令人瞠目结舌,我认为这个项目值得我投入时间——我相信也值得你的时间——主要有两个原因:

  • 使用 ChatGPT 作为知名的在线工具是很强大的,但将 LLM 集成到你的代码中是完全不同的事情。LLMs 仍然有些不可预测,当你的产品依赖于 LLM——或任何其他生成 AI 模型——作为核心产品时,你需要学会真正控制生成 AI。这并不像听起来那么简单。

  • 获取第一个工作版本只用了几天工作时间。在生成 AI 和 LLMs 出现之前,这需要几个月,并且可能需要不止一个人。利用这些工具快速创建强大应用的力量是你真正需要自己尝试的——这是未来,至少在我看来,我们不会回头。

而且,这个项目实际上可以做一些好事。我妈妈真的希望找一个可以练习英语的对象。现在她可以做到,而且费用不到每月 3 美元。我的妻子的妈妈想开始学习韩语。情况相同,费用相同。当然,我自己也在使用!这个项目真的帮助了人们,费用比一杯小咖啡还少。如果你问我,这才是真正的生成 AI 革命。

从零开始

从高层次来看,我需要的有 4 个要素:

  • 语音转文本,将我的声音转录为文字

  • 大型语言模型,最好是一个聊天型 LLM,我可以向它提问并获得回答

  • 文本转语音,将 LLM 的回答转换为声音

  • 翻译,将我不完全理解的法语文本转换为英语(或希伯来语,我的母语)

幸运的是,现在是 2023 年,以上提到的一切都变得非常非常容易获得。我还选择使用托管服务和 API,而不是在本地运行这些服务,因为这样推理速度会更快。此外,这些 API 的个人使用价格非常低,使得这个决定毫无疑问。

经过尝试几种替代方案后,我选择了 OpenAI 的 Whisper 和 ChatGPT 作为我的语音转文本和大型语言模型,Google 的文本转语音和翻译作为剩余模块。创建 API 密钥并设置这些服务非常简单,我能够通过它们原生的 Python 库在几分钟内与所有服务进行通信。

经过测试这些服务后,我真正震惊的是我正在构建的导师不仅仅是一个英法翻译老师;由于 Whisper、ChatGPT 和 Google Translate & TTS 支持几十种语言,这可以用来学习几乎任何语言,同时使用任何其他语言进行交流。这真是疯狂!

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

由 Dall-E 制作

架构和线程处理

首先,让我们确保整体流程得到充分理解:(1) 我们从录制用户的声音开始,(2) 该声音发送到 Whisper API,并返回文本。(3) 文本被添加到聊天记录中,并发送到 ChatGPT,(4) ChatGPT 返回书面回复。其回复被**(5)** 发送到 Google Text-to-speech,Google 返回的音频文件将**(6)** 被播放。

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

高级架构

我第一个实际步骤是将其拆解为组件并设计整体架构。我知道我需要一个 UI,最好是 Web UI,因为现在通过浏览器启动应用程序比使用独立的可执行文件更简单。我还需要一个“后端”,即实际的 Python 代码,与所有不同的服务进行通信。但为了提供实时的流畅体验,我意识到我需要将其拆分为不同的线程。

主线程将运行大部分代码:它将把我的录音转录为文本(通过 Whisper),将此文本显示在聊天界面上,然后将导师的书面回复(由 ChatGPT 接收)也显示在聊天屏幕上。但我必须将导师的文本转语音移到一个单独的线程——否则我们将得到:

  • 导师的声音只能在从 ChatGPT 接收到整个消息后才会被听到,而且其回复可能会很长

  • 它将阻止用户在导师讲话时进行回应

这不是我想要的“流动”行为;我希望导师在消息显示在屏幕上时就开始讲话,并且绝对不要因为音频仍在播放而阻止用户并阻止其回应。

为此,项目的文本转语音部分被拆分为 两个 额外的线程。当从 ChatGPT 收到导师的响应时,每个完整的句子被传递到另一个线程,再从那里发送到文本转语音服务,转换为声音文件。我想在这里强调一下 文件 这个词——由于我将文本逐句发送到 TTS 服务,所以我也有多个音频文件,每个句子一个,需要按正确的顺序播放。这些声音文件然后从另一个线程播放,确保音频播放不会阻塞程序的其余部分运行。

使所有这些工作,以及源于 UI-服务器交互的其他几个问题,是 这个 项目中复杂的部分。令人惊讶,对吧——软件工程才是最难的。

设计用户界面

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

项目的用户界面

嗯,我知道我需要一个用户界面,我也大致知道我希望它看起来是什么样的——但编写用户界面超出了我的知识范围。所以我决定尝试一种新方法:我让 ChatGPT 为我编写用户界面。

为此,我使用了实际的 ChatGPT 服务(而非 API),并使用了 GPT-4(是的,我是一个自豪的付费用户!)。令人惊讶的是,我的初始提示:

Write a Python web UI for a chatbot application. The text box where 
the user enters his prompt is located at the bottom of the screen, and 
all previous messages are kept on screen

交付了一个惊人的初步结果,最终得到了一个 Python-Flask 后端、jQuery 代码、HTML 和匹配的 CSS。但这只是我希望获得的所有功能的约 80%,所以我花了大约 10 个小时与 GPT-4 来回沟通,一次一个请求,优化和升级我的用户界面。

如果我让它看起来很简单,我想明确说明其实并非如此。我添加的请求越多,GPT-4 就越困惑,并且交付的代码出现故障,这在某些时候比让它修复要手动纠正更容易。我有很多请求:

  • 在每条消息旁边添加一个个人资料图片

  • 为每条消息添加一个按钮,用于重新播放其音频

  • 为每条法语消息添加一个按钮,将其翻译添加到原始文本下方

  • 添加一个保存会话和加载会话的按钮

  • 添加一个暗模式选项,使其自动选择正确的模式

  • 添加一个“工作中”图标,以便在等待服务响应时显示

  • 还有更多……

尽管通常情况下 GPT 的代码无法直接运行,但考虑到我在前端领域知识有限,结果仍然令人惊叹——远远超出了我仅凭谷歌搜索和 StackOverflow 能做到的任何事情。我在学习如何制作更好的提示方面也取得了很大进展。考虑到这一点,也许我应该再写一篇博客,专门讲述从零开始与 LLM 一起构建产品的经验教训……(嗯, 我确实做过)。

提示工程

对于这部分内容,我将假设你对如何通过 API 与聊天 LLM(如 ChatGPT)进行通信有一些基本了解。如果没有,你可能会有些迷失。

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

由 Dall-E 制作

最后但绝对不是最不重要的 — 我让 GPT 扮演一个私人导师的角色。

作为起点,我在聊天开始时添加了一个 系统提示。由于与 LLM 的聊天基本上是用户和机器人相互发送的消息列表,系统提示通常是聊天的第一条消息,用于向机器人描述它应该如何表现以及期望它做什么。我的系统提示看起来像这样(由花括号包围的参数会被运行时值替代):

You are a {language} teacher named {teacher_name}. 
You are on a 1-on-1 session with your student, {user_name}. {user_name}'s 
{language} level is: {level}.
Your task is to assist your student in advancing their {language}.
* When the session begins, offer a suitable session for {user_name}, unless
asked for something else.
* {user_name}'s native language is {user_language}. {user_name} might 
address you in their own language when felt their {language} is not well 
enough. When that happens, first translate their message to {language}, 
and then reply.
* IMPORTANT: If your student makes any mistakes, be it typo or grammar, 
you MUST first correct your student and only then reply.
* You are only allowed to speak {language}.

这实际上产生了相当不错的结果,但似乎随着聊天的进行,我给机器人的行为指令(“当我错了时纠正我”,“始终用法语回应”)的有效性逐渐减弱。

为了应对这种逐渐消失的行为,我想出了一个有趣的解决方案;我在将用户消息发送给 GPT 之前对其进行了处理。不论用户的消息是什么,我都会在其中添加额外的文本:

[User message goes here]
---
IMPORTANT: 
* If I replied in {language} and made any mistakes (grammar, typos, etc), 
you must correct me before replying
* You must keep the session flow, you're response cannot end the session. 
Try to avoid broad questions like "what would you like to do", and prefer 
to provide me with related questions and exercises. 
* You MUST reply in {language}.

在每个用户消息的末尾添加这些内容确保了 LLM 按照我希望的方式回应。值得一提的是,我添加的长后缀是用英语书写的,而用户的消息可能不是。因此,我在原始消息和我的附加内容之间添加了一个明确的分隔符(---),以结束原始消息的上下文并开始新的上下文。还请注意,由于这个后缀是添加到用户的消息中,因此是用第一人称(“我”,“我自己”等)书写的。这个小技巧显著改善了结果和行为。虽然这可能是显而易见的,但值得强调的是,这个后缀不会显示在聊天界面中,用户不知道它被添加到他们的消息中。它在幕后插入,在与聊天历史记录一起发送给 ChatGPT 之前。

我想要的另一种行为是让导师先发言,也就是说,ChatGPT 将在会话开始时发送第一条消息,而不等待用户启动会话。显然,这不是 ChatGPT 设计时的功能。

当我尝试让 ChatGPT 在仅包含系统提示的消息历史记录中回复时,我发现 ChatGPT “丢失了”,开始与自己进行聊天,扮演用户和机器人。不管我尝试什么,我都无法让它在用户先说话之前正确启动会话。

然后我有了一个想法。当会话初始化时,我代表用户向 ChatGPT 发送以下消息:

Greet me, and then suggest 3 optional subjects for our lesson suiting my 
level. You must reply in {language}.

这个请求旨在使 GPT 的响应看起来正如我所认为的那样,应该是机器人的适当会话初始化方式。然后我从聊天中删除了我的消息,让它看起来好像是机器人自行启动了会话。

概述

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

使用 Dall-E 制作,已编辑

从一个有趣的小念头开始,几乎在瞬间变为现实,这完全是在一个非常忙碌的人空闲时间中完成的。任务变得如此简单,我感到非常惊讶。仅仅一年前,像 ChatGPT 这样的工具还是科幻小说中的内容,而现在我可以在自己个人的笔记本电脑上进行塑造。

这是未来的开始,无论未来会发生什么——至少我知道,我会用一种新的外语做好准备。再见!

如何使用 Python 创造 DALL-E 10000 份积分无法购买的生成艺术

原文:towardsdatascience.com/how-i-created-generative-art-with-python-that-10000-dall-e-credits-could-not-buy-fcf804b61437?source=collection_archive---------1-----------------------#2023-07-19

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

·

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

Python 和 Pillow: 如何编写 DALL-E 无法做到的代码

在这篇博客文章中,我将展示一些我使用 Python 编程语言创建的生成艺术作品,具体利用了 Pillow 和 Torch 库。我的艺术创作灵感来源于奥地利音乐作曲家和视觉艺术家罗曼·豪本斯托克-拉马蒂的视觉构图。

在 2021 年初,我经常浏览 Catawiki,因为我想购买一些艺术品来装饰我的家庭办公室。当我在 2021 年初在 Catawiki 上发现 Haubenstock-Ramati 的作品时,我立刻被他复杂而美丽的参数艺术所吸引。我一直想用我的编程技能做一些创意性的工作,因此受到启发开发了可以生成类似输出的代码。下面的图像是激发我灵感的其中一幅图像,由 Roman Haubenstock-Ramati 创作。

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

Konstellationen,1970/1971 年,由 Roman Haubenstock-Ramati 创作

在 2022 年 4 月 Dall-E 2 发布后,我尝试使用该模型生成应该类似于 Haubenstock-Ramati 作品的艺术作品。要求模型这样做是一个有争议的话题,因为关于 AI 模型能否生成如此类似于艺术家作品的输出,以至于这些输出可能被视为对原作的版权侵权,存在有效的担忧。这一讨论超出了这篇博客的范围,但我想澄清的是,我输入到 Dall-E 的提示并不是为了生成 Haubenstock-Ramati 作品的精确复制品或贬低他的作品。我编写的代码也是如此,它们并不是为了分发他的作品的副本,而仅仅是演示如何使用 Python 创建视觉几何构图。

DALL-E 的输出很有趣,但并没有完全捕捉到他原作的精髓。输出缺乏 Haubenstock-Ramati 艺术作品中存在的精确约束和复杂性。我尝试了许多不同的提示,但始终无法接近我想要的效果。

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

Dall-E 根据我的提示生成的一些输出:“创建一幅罗曼·豪本斯托克-拉马蒂风格的绘画作品,融入图形符号和实验音乐构图的元素。画作应以黑白色为主,具有大胆的线条和几何形状,并应包含一个中心主题,代表作品的主题。”

为了简化过程,我向 Dall-E 提出了一个更简单的请求:“画一条垂直线连接到一个矩形,再连接一个正方形到这条线,然后用另一条垂直线将正方形连接到另一个矩形,最后用另一条垂直线将矩形连接到一个圆。”出乎意料的是,结果并不如我所愿。尽管提示很简单,Dall-E 仍然难以理解形状之间的预期关系,产生了意外的结果。

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

Dall-E 根据提示生成的图像:“绘制一个垂直线连接一个矩形,将一个方块连接到这条线,再用另一条垂直线连接方块到另一个矩形,最后用另一条垂直线将矩形连接到一个圆形。”

我清楚地意识到 Dall-E 无法处理几何约束的提示,我尝试了一个更简单的提示:“创建一个仅显示两条正交线的图”。这也被证明过于困难。

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

Dall-E 生成的图像根据提示:“创建一个仅显示两条正交线的图”

Dall-E 不能完成这一任务让我感到惊讶,但考虑到像 Dall-E 这样的模型如何工作,这并不令人意外。它基于潜在扩散,这本质上是一个噪声过程,不适合基于约束的精确提示。

接下来,我将展示我生成的图像,并详细讨论如何编写类似的代码。

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

使用我的代码生成的图像的单一示例。

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

一个展示我代码生成不同图像的 gif,显示了相同参数下图像的多样性。

我使用 Python 和 Pillow 制作了这些图像,不涉及任何机器学习。我的代码生成的图像通过 Torch 引入了随机性,Torch 是一个多功能的软件包,我因为其熟悉和方便而使用。它通常用于机器学习(ML)。但这些图像并不是通过机器学习(ML)生成的。

你可能会想知道图像的多样性来源于哪里,我个人喜欢我的代码能够生成给人类似感觉但仔细看又各不相同的图像。输出的多样性是一个至关重要的特性。我的代码生成的图像的变化源自于对随机变量的复杂使用。在概率论和统计学中,随机变量是其可能值是随机现象的结果的变量。

现在我将描述我代码生成图像的过程,并展示一些 Python 示例,展示这个生成过程从高层次的视角看起来是怎样的。

我们可以将生成过程分为 3 步。

  • 步骤 1:生成中心图形。这是通过采样一个矩形、一条线、一种矩形、一个方块、一条线和一个圆形来完成的。这些图形被放置在固定的位置,形状的大小由随机变量确定。

  • 步骤 2:从三个不同的分布中采样三个具有线条和相邻的簇。在每个簇中,放置了若干条具有不同起始和结束点的垂直线。

  • 步骤 3:在线条的簇中采样并绘制圆形和矩形。

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

动图显示了单个图像的逐步生成过程。

步骤 1

为了理解随机变量在我的代码中的作用,请考虑我们图像创建过程中的第一步:形成一个纵向矩形,其特点是高度大于宽度。这个矩形,虽然看似简单,却体现了随机变量的作用。

矩形可以分解为四个主要元素:起始的 x 和 y 坐标,以及结束的 x 和 y 坐标。现在,这些点在从特定分布中选择时,转变为随机变量。但我们如何决定这些点的范围,或更具体地说,它们来自的分布呢?答案在于统计学中最常见且关键的分布之一:正态分布。

正态分布由两个参数定义——均值 (μ) 和标准差 (σ),在我们的图像生成过程中发挥着关键作用。均值 μ 表示分布的中心,因此充当随机变量值围绕的点。标准差 σ 量化了分布中的离散程度。它决定了随机变量可能取的值范围。本质上,较大的标准差会导致生成图像的多样性更大。

import torch
canvas_height = 1000
canvas_width = 1500

#loop to show different values
for i in range(5):
    #create normal distribution to sample from
    start_y_dist = torch.distributions.Normal(canvas_height * 0.8, canvas_height * 0.05)
    #sample from distribution
    start_y = int(start_y_dist.sample())

    #create normal distribution to sample height from
    height_dist = torch.distributions.Normal(canvas_height * 0.2, canvas_height * 0.05)

    height = int(height_dist.sample())
    end_y = start_y + height

    #start_x is fixed because of this being centered
    start_x = canvas_width // 2
    width_dist = torch.distributions.Normal(height * 0.5, height * 0.1)

    width = int(width_dist.sample())
    end_x = start_x + width

    print(f"start_x: {start_x}, end_x: {end_x}, start_y: {start_y}, end_y: {end_y}, width: {width}, height: {height}")
start_x: 750, end_x: 942, start_y: 795, end_y: 1101, width: 192, height: 306
start_x: 750, end_x: 835, start_y: 838, end_y: 1023, width: 85, height: 185
start_x: 750, end_x: 871, start_y: 861, end_y: 1061, width: 121, height: 200
start_x: 750, end_x: 863, start_y: 728, end_y: 962, width: 113, height: 234
start_x: 750, end_x: 853, start_y: 812, end_y: 986, width: 103, height: 174

采样一个正方形看起来非常相似,我们只需采样高度或宽度,因为它们是一样的。采样一个圆形则更简单,因为我们只需采样半径。

在 Python 中绘制矩形是一个简单的过程,特别是当使用 Pillow 库时。以下是如何做到这一点:

from PIL import Image, ImageDraw

# Create a new image with white background

# Loop to draw rectangles
for i in range(5):
    img = Image.new('RGB', (canvas_width, canvas_height), 'white')

    draw = ImageDraw.Draw(img)

    # Creating normal distributions to sample from
    start_y_dist = torch.distributions.Normal(canvas_height * 0.8, canvas_height * 0.05)
    start_y = int(start_y_dist.sample())

    height_dist = torch.distributions.Normal(canvas_height * 0.2, canvas_height * 0.05)
    height = int(height_dist.sample())
    end_y = start_y + height

    start_x = canvas_width // 2
    width_dist = torch.distributions.Normal(height * 0.5, height * 0.1)
    width = int(width_dist.sample())
    end_x = start_x + width

    # Drawing the rectangle
    draw.rectangle([(start_x, start_y), (end_x, end_y)], outline='black')

    img.show()

步骤 2

在这些图像中的垂直线的背景下,我们考虑了三个随机变量,即:

  1. 线的起始 y 坐标(y_start)

  2. 线的结束 y 坐标(y_end)

  3. 线的 x 坐标(x)

由于我们处理的是垂直线,每条线只需要采样一个 x 坐标。线的宽度是恒定的,由画布的大小控制。

需要一些额外的逻辑来确保线条不交叉。为此,我们基本上需要将图像视为一个网格,并跟踪被占用的位置。为了简化问题,我们暂时忽略这一点。

这是一个在 Python 中的示例。

import torch
from PIL import Image, ImageDraw

# Setting the size of the canvas
canvas_size = 1000
# Number of lines
num_lines = 10
# Create distributions for start and end y-coordinates and x-coordinate
y_start_distribution = torch.distributions.Normal(canvas_size / 2, canvas_size / 4)
y_end_distribution = torch.distributions.Normal(canvas_size / 2, canvas_size / 4)
x_distribution = torch.distributions.Normal(canvas_size / 2, canvas_size / 4)
# Sample from the distributions for each line
y_start_points = y_start_distribution.sample((num_lines,))
y_end_points = y_end_distribution.sample((num_lines,))
x_points = x_distribution.sample((num_lines,))
# Create a white canvas
image = Image.new('RGB', (canvas_size, canvas_size), 'white')
draw = ImageDraw.Draw(image)
# Draw the lines
for i in range(num_lines):
    draw.line([(x_points[i], y_start_points[i]), (x_points[i], y_end_points[i])], fill='black')
# Display the image
image.show()

然而,这样做只会得到直线。另一部分是线末端的圆形,我称这些为相邻圆。随机变量也决定它们的过程。首先,是否会有一个相邻圆是从伯努利分布中采样的,而形状的位置(左、中、右)是从均匀分布中采样的。

圆形可以完全由一个参数定义:它的半径。我们可以将线段长度视为影响圆形半径的条件。这形成了一个条件概率模型,其中圆形的半径(R)依赖于线段长度(L)。我们使用条件高斯分布。该分布的均值(μ)是线段长度平方根的函数,而标准差(σ)是一个常数。

我们最初建议给定线段长度 L 的半径 R 服从正态分布。这被表示为 R | L ~ N(μ(L), σ²),其中 N 是正态(高斯)分布,σ 是标准差。

然而,这存在一个小问题:正态分布包括了采样负值的可能性。在我们的场景中,这个结果是不可能的,因为半径不能为负。

为了规避这个问题,我们可以使用半正态分布。这个分布与正态分布类似,由一个尺度参数 σ 定义,但关键的是,它被限制为非负值。给定线段长度的半径服从半正态分布:R | L ~ HN(σ),其中 HN 表示半正态分布。这样,σ 由所需均值确定,即 σ = √(2L) / √(2/π),确保所有采样的半径都是非负的,并且分布的均值是 √(2L)。

from PIL import Image, ImageDraw
import numpy as np
import torch
# Define your line length
L = 3000

# Calculate the desired mean for the half-normal distribution
mu = np.sqrt(L * 2)

# Calculate the scale parameter that gives the desired mean
scale = mu / np.sqrt(2 / np.pi)

# Create a half-normal distribution with the calculated scale parameter
dist = torch.distributions.HalfNormal(scale / 3)

# Sample and draw multiple circles
for _ in range(10):
    # Create a new image with white background
    img_size = (2000, 2000)
    img = Image.new('RGB', img_size, (255, 255, 255))
    draw = ImageDraw.Draw(img)

    # Define the center of the circles
    start_x = img_size[0] // 2
    start_y = img_size[1] // 2
    # Sample a radius from the distribution
    r = int(dist.sample())

    print(f"Sampled radius: {r}")

    # Define the bounding box for the circle
    bbox = [start_x - r, start_y - r, start_x + r, start_y + r]

    # Draw the circle onto the image
    draw.ellipse(bbox, outline ='black',fill=(0, 0, 0))

    # Display the image
    img.show()

第 3 步

我们过程中的第 3 步是第 1 步和第 2 步元素的结合。在第 1 步中,我们处理了在设定位置采样和绘制矩形的任务。在第 2 步中,我们学会了如何使用正态分布在画布的一部分上绘制线条。此外,我们还掌握了如何采样和绘制圆形的知识。

当我们过渡到第 3 步时,我们将重新利用前面步骤中的技巧。我们的目标是将方形和圆形和谐地分布在我们之前采样的线条周围。正态分布将在这个任务中再次派上用场。

我们将重用用于创建线条集群的参数。然而,为了增强视觉效果并避免重叠,我们对均值(μ)和标准差值引入了一些噪声。

在这一步中,我们的任务不是定位线条,而是放置采样的矩形和圆形。我鼓励你尝试这些技巧,并看看是否能将圆形和矩形添加到你的线条集群中。

在这篇博客文章中,我已经解剖并简化了支撑我的代码的过程,以便更深入地理解其操作方式。我展示了像 Dall-E 这样的生成性 AI 模型在遵循精确约束方面的困难。

编写产生这些图像的代码对我来说是一次极好的体验。看到每写一行代码,图像逐渐变化,真的很酷。我希望这篇博客文章激发了你对艺术与编程交汇点的兴趣。我鼓励你利用你的编程技能,通过代码让你的想象力变为现实。无需耗尽你的 Dall-E 额度,创造的力量就在你的指尖。

我在 2022 年的哭泣情况:对 365 天个人数据的分析

原文:towardsdatascience.com/how-i-cried-in-2022-an-analysis-of-365-days-of-personal-data-190082a3c65

利用我收集的个人数据对我的哭泣模式进行调查

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

·发表于Towards Data Science ·阅读时间 9 分钟·2023 年 1 月 6 日

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

本文中一张条形图的 Stable Diffusion 渲染图。由作者生成。

本文最初 发布在我的博客上

我对收集个人数据情有独钟。2022 年的每一天,我都会填写我制作的 Google 表单,记录我是否哭泣、是否锻炼、是否喝咖啡或是否洗头等项目。我还从 Apple Health 和 Google 位置历史中收集数据,以获得我全年模式和行为的更完整图景。在本文中,我通过结合所有这些数据提供了对 2022 年个人经历的洞察。

对我而言,2022 年是一个充满巨大变化和新机会的年份——我搬到了纽约,开始了一份新工作,并且旅行了许多城市。为了以数据科学家的方式反思刚过去的一年,我将所有这些数据结合起来进行分析,以了解我的哭泣模式——我在哪里哭泣,何时哭泣,哭泣的频率,以及一点点关于我为何哭泣的洞察。希望这些洞察能为我在新的一年中面对更多的哭泣日做好准备。(注:这些分析纯粹是为了趣味,并非十分严谨。未做任何统计声明。)

该文章最初发布在我的博客上。如果你喜欢这篇文章,你可以关注我的博客以获取更多类似文章。

第一部分:分析结构化数据中的哭泣模式

数据来源概述

我结合了以下数据:

  • 苹果健康数据,导出为 CSV(按照说明)。包括步行速度、步态不对称和行走/跑步的距离。

  • Garmin(运动手表)数据,导出为 CSV。包括心率、步数和爬楼梯的层数。

  • Flo 的月经健康数据,导出为 CSV

  • Google 位置历史数据,通过Google Takeout获得。Google 位置数据在地理位置(非常具体的纬度和经度坐标)以及时间(精确到分钟)方面非常详细。我将纬度/经度四舍五入到城市级别,并选择了我每天所在的最常见城市。

  • Google 表单调查数据,导出为 CSV。包括我做了什么样的运动、喝了多少咖啡,以及是否哭泣。

在 2022 年,我在许多不同的地点哭泣。

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

按地点分解的哭泣天数。图表由作者创建。

在 2022 年,我共哭泣了 48 天。我将这些天数分配到纽约(我住的地方)、多伦多(我伴侣所在的地方)、西雅图(我的工作地点)、探访父母和旅行中。近 36%的哭泣天数是在多伦多。即使考虑到我在多伦多待了 49 天(相比在纽约的 239 天),我在多伦多哭泣的频率仍然比其他地方高得多。

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

每个地点的哭泣天数分解。图表由作者创建。

我还查看了在特定地点哭泣的频率(在城市中哭泣的天数百分比除以在该城市花费的总天数)。与其他地点相比,我在多伦多花费了近四分之一的时间在哭泣。

那么,为什么我在多伦多哭得这么多呢?一个原因可能是因为我在 2022 年 1 月和 2 月待在那里——那里不仅冷得刺骨,而且多伦多那时仍在封锁中。这可能解释了为什么我在多伦多哭了那么多时间。那里没有其他事情可做。

我等到周末来哭。

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

按工作日分解的哭泣天数。图表由作者创建。

我超过 50%的哭泣天数是在周末。我在周六和周日哭泣的次数远远超过了工作日。没有什么周末活动能比哭泣更有趣了。

我哭泣时是否运动都无所谓。

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

每个地点的运动分解。图表由作者创建。

锻炼对心理健康非常有益。我猜测在锻炼的日子里我可能会哭得少一点。上面是我在不同地点做的锻炼类型的分解。我在纽约和多伦多主要做武术(泰拳、拳击和一次咏春拳课程),而在我去西雅图或探望父母期间做了更多的有氧运动(跑步、骑车、徒步)。跳舞(萨尔萨舞、嘻哈舞)、步行(指那些我步行超过 10K 步但没有记录特定锻炼的日子)和其他锻炼(包括瑜伽、家庭锻炼和去普通健身房)则分布在不同地点。

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

每项练习的哭泣天数分解。图表由作者创建。

我计算了在做特定练习的日子里哭泣的百分比,相对于我做这些练习的总天数。乍一看,似乎我在跳舞的日子哭得更多。但实际上,我在整年里只去过 8 天跳舞,所以我在这 8 天中的 2 天哭泣并不足以强烈说明跳舞导致哭泣(或反之,哭泣导致跳舞?虽然那样的话会很有趣)。

另一方面,我在做武术的日子里哭泣最少。这是合理的,因为我去拳击馆的一个重要原因是发泄我的愤怒和挫折感。即使这种相关性可能是虚假的,这仍然是继续我所做的事情的好理由。

我对那些我完全不锻炼的日子里我哭泣的程度感到惊讶。我想知道,在我没有锻炼的日子里,我并不是把所有额外的时间都用来在家哭泣。

我在月经周期的不同阶段哭泣

我在 2022 年花了大量时间了解女性月经周期的不同阶段如何影响情绪、荷尔蒙健康等。我对自己在月经周期的哪个阶段哭泣更多感到好奇。根据我的经验,在月经开始前我总是哭泣,所以我猜测月经开始前我会看到更多的哭泣。

我查看了每个周期日子的哭泣百分比。我给相应的月经周期的日子上了色:月经期(第 1–5 天)、卵泡期(第 5–14 天)、排卵期(第 14–15 天)、黄体期(第 15–28 天)。

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

每天的哭泣天数分解,基于我的月经周期。图表由作者创建。

我在月经第 16 天哭泣的情况最严重。这通常是在排卵后,考虑到那时荷尔蒙波动很大,这也是合理的。我在第 21 和 22 天哭泣也很多,这大约是在月经开始前一周,通常是我感觉最糟糕的经前综合症时期。我在第 13 和 15 天(排卵前)以及第 5、8 和 9 天(经期结束后的前几天)哭泣最少。

所以,现实情况并不像“我在月经前哭得很多”那么明确。我在我的 28 天周期的所有阶段都会哭泣,但并不是每一天都一样。我在荷尔蒙波动较大的日子,例如排卵后和月经阶段开始前,会更多地哭泣。但我现在知道要记住,排卵后的时间特别容易流泪。

第二部分:分析非结构化文本中的哭泣模式

我无法在不包括至少一点机器学习的情况下分析我的个人数据。在文章的第二部分,我使用了非结构化数据来进一步分析我的哭泣习惯。

我在 2022 年每天都写日志。我使用了OpenAI 的文本嵌入将每一天的日志映射到文档级嵌入(本质上是捕捉文本片段精髓的一系列数字)。

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

2022 年日志嵌入的二维表示,经过 PCA 处理。图由作者创建。

这些嵌入的维度非常高,因此我使用 PCA 将嵌入降至 2 维。我绘制了前两个主成分,并根据那天是否哭泣来给每个文档嵌入上色。(注意:前两个主成分只解释了整个方差的 7%,这并不高)。乍一看,哭泣和不哭泣的日子的嵌入之间似乎没有明显的区别。这可能是因为哭泣的原因因每次情况而异,并且我每次哭泣时写作的方式可能不相同。

预测未来的哭泣日

最后,我想看看是否可以预测未来我更有可能哭泣的日子。

对于机器学习的朋友们:我根据时间将数据集划分为训练集和测试集(80%训练,20%测试)。我将数据分为训练数据和测试数据。在测试数据中,只有 12 天有哭泣(总共 72 天)。这是一个类别不平衡的例子,其中不哭泣的天数远多于哭泣的天数。在建模方面,我尽可能保持简单。我使用了一个现成的Gradient Boosting Classifier from sklearn。我尝试了更简单的模型,例如逻辑回归和随机森林,但结果太差没有包括那些。我没有进行任何超参数调优或额外特征工程。

我建立了两个分类器。每个分类器预测在给定的一天我是否哭泣:

  • 第一部分基于所有结构化特征(例如 Google 位置、Apple 健康、调查数据)做出了预测。

  • 第二部分基于日志嵌入做出了预测

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

对比使用和未使用嵌入的分类器的混淆矩阵。图由作者创建。

对于机器学习爱好者,我展示了每个分类器结果的混淆矩阵。第一个模型(未经嵌入训练)更容易预测一天是哭泣的,即使实际上不是。第二个模型(经过嵌入训练)并没有错误地将一天预测为哭泣,但也错过了大多数实际的哭泣天数。这两个模型的差异不大。

这一切意味着——这两个模型在实际检测哭泣的天数方面都不太好。由于数据不平衡(不哭泣的天数远多于哭泣的天数),通过简单地预测“未哭泣”每天来获得高准确率是很容易的。然而,(至少在这个早期阶段的建模中,未进行任何复杂处理)得出关于哭泣的明确指标的简单结论是困难的。特别是我的日记条目,并没有给出清晰的哭泣指示。这进一步支持了每次哭泣的原因、类型和本质都是多变的这一观点。预测我在某一天是否会哭泣是相当困难的!

结论

我喜欢新年——这是我最喜欢的节日。我喜欢在新年里立下决心和反思过去的一年。用我的个人数据来回顾我的一年——包括 2022 年的哭泣和锻炼习惯——感觉特别。

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

饼图展示了三种不同类别中哭泣与不哭泣的百分比。图形由作者创建。

并不是所有的见解都很有用。根据这些饼图,我在洗头、做艺术和喝咖啡的日子里哭泣的频率更高。由于这三种活动都带给我快乐或对我有益,我不会停止做这些活动。

如果我有更多时间,我希望能包含生活中其他方面的数据,例如 Spotify(音乐听取习惯)、Toggl(用于跟踪工作时间)和支出跟踪(我的钱都花到哪里去了?)。此外,我还希望使用 Apple 屏幕数据(目前无法导出)和睡眠数据(没有跟踪)。这些是我希望在明年的分析中包含的内容!

感谢阅读我的文章!如果你有任何反馈,请随时留下评论!如果你对数据清理过程感兴趣,请告诉我,我也可以分享那部分内容。

这篇文章最初发布在我的博客上。如果你喜欢这篇文章,可以在www.artfish.ai关注更多类似文章。

我是如何在 6 个月内获得数据分析师职位的

原文:towardsdatascience.com/how-i-got-a-data-analyst-job-in-6-months-cc6180de06c3

以及你如何在 2023 年通过利用 ChatGPT 和生成性 AI 做同样的事情。

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

·发布于 Towards Data Science ·10 分钟阅读·2023 年 11 月 18 日

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

图片来自 Motion Array

在 2019 年,我在国内一所最负盛名的大学攻读计算机科学学位——希望这能让我在数据行业找到一份工作。

然而,到我大学的第二年时,现实开始显现。

当我意识到我的学位并没有为我在该领域的工作做好准备时,我开始申请技术相关的实习——数据科学、数据分析和网页开发。

我不知道如何编程,缺乏解决商业问题所需的技术专长,也没有可以在简历上展示的市场技能。

在申请实习过程中经历了多次拒绝后,我意识到我不能再依靠我的学位来教会我获得技术行业工作的必要技能。

我花了所有的空闲时间参加在线课程并建立个人项目。

仅仅用了 6 个月的自学时间,我便获得了第一个数据分析师实习机会。

这次实习为我转型进入数据科学领域铺平了道路。现在我正在担任全职数据科学家。

在这篇文章中,我将分享我在 6 个月内自学数据分析的步骤。

想要视频版,请点击 这里

视频版

为什么你应该成为数据分析师?

当我开始时,我的初衷是成为一名数据科学家

然而,我发现作为数据分析师找工作要容易得多,因为这个角色的入门门槛比数据科学低。

这是因为大多数数据科学职位要求你拥有统计学或计算机科学等定量领域的硕士学位。

如果你没有技术背景,那么进入数据分析领域会比进入数据科学领域容易得多。

此外,大多数数据分析师职位并不要求你有数学机器学习的背景——这两者都是数据科学家必须具备的技能。

数据分析师到底做什么?

虽然数据分析师的具体角色可能根据公司和行业有所不同,但这里是一般的工作流程:

你提取组织的电子表格或数据库中的数据,分析它,并向业务解释你的发现。

然后,公司会根据你的发现做出业务决策(与销售、营销、财务等相关)。

执行这种分析所用的工具会根据你的角色有所不同。有些角色只要求你掌握 Excel,而其他角色则要求你具备数据库和编程语言的知识。

我是如何在 6 个月内学习数据分析的?

这是我在 2020 年学习数据分析所采取的步骤:

1. SQL

SQL 代表结构化查询语言。它是一种允许你与公司数据库进行沟通的语言。

它让你从大型数据库中提取数据,转换数据,并利用这些数据解决业务问题。

这是数据分析师职位招聘中最受欢迎的技术技能之一。

我在刚开始时参加了 Udacity 上的一个免费的课程,名为SQL for Data Analysis

这将教会你所有需要知道的概念,以便开始在关系数据库中处理数据——例如数据聚合、联接,甚至一些更复杂的主题,如子查询和窗口函数。

我推荐这个 Udacity 课程而不是互联网上成千上万的其他资源的原因是:

  1. 这直接切入重点。它教授你成为数据分析师所需的所有 SQL 知识。其他课程深入探讨如 ER 图和创建数据库等主题,这些对数据分析师的工作实际上并不必要。

  2. 每个主题之后都有练习题。你学习一个概念后,他们会给你一个业务问题。你需要编写 SQL 查询来解决这个问题,使用你刚学到的概念。

在我 3 年的技术经验中,我意识到通过实践你学到的东西要比仅仅从视频内容中获取信息多得多。

自己编写一行代码会比观看其他人输入 100 行程序学到更多东西。

我发现这个课程在理论和实践之间达到了完美的平衡。

2. BI 工具

我做的第二件事是学习使用商业智能(BI)工具。

商业智能(BI)工具让你将原始数据转换为仪表板或人们可以轻松理解的可视化格式。

简单来说,BI 工具将数据转换为可操作的见解

再次强调,这里有很多东西要学习——数据可视化的基础创建计算字段构建仪表板

而且很容易在互联网上大量资源中迷失方向。

所以,我建议你这样做:

市场上两个最受欢迎的 BI 工具包括 Tableau 和 PowerBI,根据 2022 年的调查,Tableau 的需求更高

当你刚开始时,我建议你先参加一个 Tableau 在线课程,以学习基础知识。

完成此课程后,你将能够将原始数据转化为互动仪表板。

一旦你完成了这门课程,决定这是否是你想进一步学习的内容。

即使在数据分析领域,也有专门从事不同工作的人员。我认识一些分析师,他们的唯一工作就是使用 BI 工具构建仪表板。

如果你觉得自己更具创造力,喜欢创建可视化的过程,你可以深入研究并专注于仪表板。

你甚至可以获得Tableau 专家认证。

就我个人而言,我更像是一个通才。我做一点所有的工作,知道足够将原始数据转化为功能性仪表板。

3. Python(可选)

最终,我学习了 Python 编程。

我认为这对于数据分析师来说是一个可选技能——它并不是大多数角色的严格前提。

你应该学习 Python 如果

  1. 你计划将来转型到数据科学或其他更技术性的领域。

  2. 你有时间。这个过程花费了我大约 4-5 个月的练习,而我已有一些计算机科学学位的编程知识。

  3. 你想要争取更高的薪水。

对我来说,这绝对是值得的,因为学习编码为我打开了许多机会的大门。

我能够争取到更高的薪水,获得多个自由职业编程角色,并随后过渡到数据科学领域。

我还发现,一旦我学会了 Python,掌握其他编程语言就变得容易得多。

但是,如果你的唯一目标是尽快获得数据分析师的职位,你可以跳过这一步。

如果你确实想学 Python,你可以参加 Freecodecamp 的 Python for Beginners 课程。然后跟着在 LeetcodeHackerrank 上的练习题。

这里有一篇更详细的文章,解释了如何从零开始学习编码。

如果我能在 2023 年重新开始,我会做些什么不同的事情?

现在你知道了我如何在 2023 年学习数据分析,如果我今天必须重新开始,这里是我会做的事情:

1. 我会学习 Excel

当我作为数据分析师实习生加入我的第一家公司时,我被期望在 Excel 中进行并展示大量的分析。

这是因为 SQL 和 Python 对许多公司来说有些多余。他们中的很多人只是将数据存储在 Excel 文件中。

利益相关者和决策者也喜欢看到以 Excel 表格呈现的数据,因为他们对这种界面很熟悉。

他们中的大多数人不在乎你那些花哨的 Python 代码。

如果你时间紧迫,需要快速找到工作,或者确实没有精力学习太多工具,我建议你只学习 Excel。

一旦你真正掌握了在 Excel 中分析数据的技能,你可以获得许多入门级的数据分析师职位。

当然,这些工作可能比那些需要 SQL 和编程技能的工作薪资低,但这是一个很好的起点。

2. 我会利用 ChatGPT 和 AI 学习数据分析

ChatGPT 对数据分析师来说是一个颠覆性工具。

它可以在几分钟内分析数据。

GPT-4 中的高级数据分析插件允许你上传数据集并自动化大量与 Excel 相关的工作。

它还可以创建可视化,生成 SQL 查询,并编写 Python 代码:

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

图片由作者提供(数据集由作者创建用于演示目的)

如果你不学习如何使用 ChatGPT 进行数据分析,你将比那些正在使用它的分析师处于劣势。

使用 ChatGPT,我能够将数据分析的速度提高了一倍。

365DataScience 有一个叫做 使用 ChatGPT 进行数据分析 的课程,我强烈推荐。

此外,Microsoft Copilot 也是一个很棒的工具,可以使用简单的文本提示将数据转化为商业洞察。

你只需输入一个问题,例如“哪个客户产生了最高的销售额”,它就会为你创建一个图表——你不需要输入公式或进行任何手动分析。

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

图片由作者提供

ChatGPT 和 Copilot 在数据分析领域都是颠覆性工具。学会使用它们。

摘要:2023 年如何学习数据分析

好吧,让我们快速回顾一下。

如果你时间紧迫,只能学习一个工具,那么学习 Excel用于数据分析的 ChatGPT。这些要求是进入该领域的最低标准。

如果你有时间全职学习 6 个月,学习 ExcelSQLBI 工具ChatGPT,以及可选的 Python

求职申请流程

一旦你掌握了我之前列出的技能,你可以开始申请数据分析师的职位列表。

申请实习

我建议从实习开始,因为它们的门槛较低。公司实际上是付钱让你在为他们工作时学习。

如果一切顺利,实习可以迅速转变为全职工作机会。我的 6 个月实习在我大学毕业后变成了一个永久职位。

提高你被录用的机会

当我刚开始申请实习时,遇到了多次拒绝。

大多数公司在我通过 LinkedIn 和 Indeed 申请时根本没有回应。我经常被“幽灵”了。

当我这样做时,我的成功率显著提高:

  1. 我列出了我所在地区招聘数据分析师的公司。

  2. 我筛选了我想要工作的公司。

  3. 我找到了该职位的招聘经理并给他们发送了个性化的邮件。

下面是我在申请数据分析师职位时发送的个性化邮件的一个例子:

Hi *recruiter name*,

I am Natassha, currently a final year computer science student.
I am writing to you because I'd like to seek an internship in *company name* from March 2021, for a period of around 4-5 months. I am available for a full-time internship during this period.
I am currently doing a part time internship with *company name*, and have worked on data analytics projects with clients such as *client names*. I also am a prolific content writer in some of the top Medium publications, and am a top writer in the field of artificial intelligence.
I would love to have the opportunity to utilise my skills in my area of expertise, and learn while working closely with a team of dynamic individuals.
I will link my portfolio, along with my LinkedIn profile below. If you are interested in knowing more about the work I do, please do revert back to me. I will then share my resume with you and be happy to discuss further.
Portfolio site: https://natassha.github.io/natasshaselvaraj/
LinkedIn: https://www.linkedin.com/in/natassha-selvaraj-33430717a/

Thanks for taking your time to read this. I look forward to hearing back from you.

Regards,
Natassha

发送这封邮件后,我获得了面试机会,最终在公司获得了实习职位!

另一个增加你作为数据分析师被聘用机会的方法是创建作品集项目

这里是我的一个数据分析师作品集项目的例子。

随意从这个项目中汲取灵感并复制它,创造属于你自己的项目。

最后,我想通过谈论一个大多数人在学习困难内容时会遇到的问题来结束本文:深入研究

如果你试图进入数据行业,互联网上可用的信息量可能令人畏惧。

这在生成式 AI 时代尤其如此——几乎每周都会推出新的 API 和与数据相关的功能。

仅在过去一年中,数据分析领域就因 Microsoft Copilot、Advanced Data Analysis、Noteable 和 Pandas AI 的引入而发生了显著变化。

虽然我总是推荐将 AI 融入你的工作流程,但几乎不可能跟上每个新功能的推出,尤其是如果你是该领域的完全初学者。

鉴于此,我建议使用本文之前提到的步骤来学习数据分析的基础知识。利用像 ChatGPT 这样的 AI 工具学习提示工程的基础,但不要在网上资源的海洋中迷失方向。

一旦你熟悉了分析师的核心工作流程,你可以随时开始引入新工具以最大化效率。

这样,你可以在熟悉基础知识的同时,利用最先进的技术使自己从其他分析师中脱颖而出。

这就是本文的全部内容,感谢阅读!

如何利用开源 LLM 在大型计算项目中实现巨额节省

原文:towardsdatascience.com/how-i-leveraged-open-source-llms-to-achieve-massive-savings-on-a-large-compute-project-bd8bb3c7267

利用开源 LLM 和 GPU 租赁实现大型计算项目的成本效益。

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

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

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

图片由 Alexander Grey 提供,来源于 Unsplash

介绍

在大型语言模型(LLM)的世界中,计算成本可能是一个重大障碍,尤其是在大型项目中。我最近开始了一个需要运行 4,000,000 个提示的项目,平均输入长度为 1000 个 tokens,平均输出长度为 200 个 tokens。这接近 50 亿 tokens!传统的按 token 计费的方法(如 GPT-3.5 和 GPT-4 所用的)会导致高额账单。然而,我发现通过利用开源 LLM,我可以将定价模型转变为按小时计算计算时间,从而节省大量成本。本文将详细说明我采取的方法,并对每种方法进行比较。请注意,虽然我分享了定价经验,但这些费用可能会变化,并可能因您的地区和具体情况而有所不同。关键点是,利用开源 LLM 和按小时租赁 GPU 的成本节省潜力,而不是具体的报价。

ChatGPT API

我在一个小数据子集上使用了 GPT-3.5 和 GPT-4 进行了初步测试。两个模型都表现出色,但 GPT-4 在大多数情况下始终优于 GPT-3.5。为了让你了解成本,通过 Open AI API 运行全部 400 万个提示的成本大致如下:

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

运行 400 万个提示的总成本,输入长度为 1000 个 tokens,输出长度为 200 个 tokens

虽然 GPT-4 提供了一些性能上的好处,但相较于其对我的输出所增加的增量性能,成本却过高。相比之下,尽管 GPT-3.5 Turbo 更加实惠,但在性能上有所欠缺,在 2–3% 的提示输入中出现明显错误。鉴于这些因素,我不愿意在一个本质上是个人项目的项目上投资 $7,600。

开源模型

开源模型来救援!使用开源模型,定价模式非常不同:你只需按计算时间的小时收费。因此,你的目标是最大化每小时的计算迭代次数。此外,像 Petals.ml 这样的解决方案,你可以免费运行你的计算(有一定限制)!

在尝试了 Hugging Face 上的各种模型后,我发现一个名为 Stable Beluga 2 的 LLama-2 70B 微调版本给了我很好的性能,无需任何微调!它的表现优于 GPT-3.5 Turbo,但略低于 GPT-4。然而,这个模型仍然非常庞大,需要非常强大的 GPU。通常,最佳实践是使用尽可能小的模型来高效完成任务。因此,我尝试了 Stable Beluga 2 的 7B 版本。性能仍然不够好!

微调

为了提升任务性能,我使用了 GPT-4 和 Petals.ml(Stable Beluga 2 70B)来生成微调数据集。我通过 Petals.ml 生成了 25K 个提示完成对,并通过 GPT-4 API 生成了 2K 个。请注意,Petals.ml 允许你使用 bit-torrent 技术免费运行开源 LLM 模型。然而,推理时间并不理想:进行 4mm 迭代需要超过一年。

此外,Petals.ml 免费托管了这个模型。因此,我利用 Petals.ml 生成了 25,000 个提示完成对。 我还使用了 GPT-4 API 生成了额外的 2K 个提示完成对。事后看来,我可能在训练数据上做得过头了。我阅读过报告显示,少至 500 个微调样本就足以提升 LLM 的性能。无论如何,这里是使用 Open AI API 运行 2K 提示的总成本:

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

运行 2K 个提示完成对的总成本

没错:$84。你能猜到接下来发生了什么吗?使用这 27K 个提示完成对,我对最小的 LLama 2 7B 进行了微调,结果,这个新模型在我的用例中表现极其出色。总微调成本仅为 $6.6,因为我租用了一个 A100 GPU 6 小时。最终,我微调后的模型表现优于 GPT-3.5,略逊于 GPT-4。

推理

现在让我们谈谈推断计算成本。我的模型至少需要 20GB 的 VRAM,因此我们至少需要 RTX 3090。目前,有四个很好的 GPU 租赁选项:AWS、LambdaLabs、RunPodVast.AI。根据我的经验,Vast.AI 的价格最好,但可靠性最差。AWS 非常可靠,但比其他选项更贵。RunPod 价格优越,用户界面也很好。我个人想尽量降低成本,所以决定使用 Vast.AI 进行这个项目。Vast.AI 是一个点对点 GPU 租赁服务,因此它可能也是最不安全的。经过在 Vast.AI 上寻找最佳性价比的服务器后,以下是我找到的最佳选项:

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

Vast.AI 使用微调 LLama2 7b 模型的总项目成本。总成本是通过每秒的迭代次数、GPU 租赁的每小时价格和所需的总迭代次数来计算的。

如上所示,推断的总成本可以低于$99。在我的案例中,我启动了大约 10 台服务器,并计算了每台服务器每秒能产生的迭代次数。对于这个特别的项目,利用了微调的 Llama2–7B 变体,RTX A5000 在性价比方面是一个很好的选择。我还应该提到,如果没有开源库*VLLM*的极快速度,这一切都不可能实现。我的计算完全依赖于 VLLM 的支持。没有 VLLM,计算时间将增加 20 倍! 你也可以看看总运行时间并发笑——638 小时就是 26 天;然而,你可以轻松地在多个 GPU/服务器之间并行处理任务。

结论

总之,利用开源 LLM 以及将定价模式从按令牌计费改为按计算时间计费,使得在这个大型计算项目中实现了显著的成本节省。总成本,包括提示生成、微调和推断,仅为$189.58。这与使用像 GPT-4 或 GPT-3.5 Turbo 这样的模型的费用形成了鲜明对比,后者的费用分别为$167,810 和$7410.42。

这个项目的主要收获是利用开源 LLM 可以显著降低成本。通过按小时租用 GPU,费用与计算时间挂钩,而不是提示的数量,这可以带来巨大的节省,尤其是对于大型项目。这种方法不仅使这些项目在财务上更具可行性,而且为个人和小型组织提供了承担大型计算项目的机会,而不会产生过高的费用。

这个项目的成功突显了开源模型在大型语言模型和 AI 领域的价值与潜力,证明了创新且具有成本效益的解决方案在解决复杂计算任务中的力量。

定价说明

本文提供的定价信息基于我的个人经验,旨在作为一般比较。价格可能会根据你所在的地区和具体情况有所不同。关键点是利用开源 LLM 和按小时租赁 GPU 的潜在成本节省,而不是具体的报价。

📢 嗨!如果你觉得这篇文章有帮助,请考虑:

👏 鼓掌 50 次(这很有帮助!)

✏️ 留下评论

🌟 突出你觉得有洞察力的部分

👣 关注我

有任何问题吗?🤔 请随时提问。以这种方式支持我是一种免费且简单的方式来感谢我的详细教程文章!😊

GPU 租赁推荐:

最佳选择:RunPod.io GPU 租赁

最便宜:Vast.AI

其他深度学习博客

高效服务开源 LLM

Andrew Ng 的计算机视觉 — 11 节课学到的知识

Andrew Ng 的深度学习专修课程 — 21 节课学到的知识

我如何设置数据科学项目(使用 VS Code 和 DVC)

原文:towardsdatascience.com/how-i-setup-environment-for-data-science-projects-feat-git-vs-code-and-dvc-273a696ce8d7?source=collection_archive---------8-----------------------#2023-01-26

来源于软件开发的灵感

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

·

关注 发表在 数据科学前沿 ·6 分钟阅读·2023 年 1 月 26 日

介绍

设置开发环境通常是开始任何编码项目时的第一步。一个有效的开发环境可以极大提升生产力,帮助我们产生高质量的工作。然而,与软件开发等其他领域相比,数据科学中的这个过程非常模糊,因为其自身的独特性和挑战。

在这篇文章中,我将解释如何为数据科学项目设置工作环境,包括我的动机以及我和我的团队使用的工具。

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

图片来源于Remy_LozUnsplash

动机

当我还是软件工程师时,加入一个团队时我做的第一件事就是设置开发环境。这通常包括检查代码仓库、安装所需的库/框架以及熟悉第三方工具。一旦完成,你就有了一个可以与团队一起使用的工作环境。但当我转向数据科学时,这样的过程在我的公司并不存在。数据科学家在基于云的笔记本服务上工作,代码没有版本控制,数据被上传到云存储却没有任何文档。融入团队完全是一片混乱,更不用说与他人协作了。

为了减少痛苦,我开始设计一个标准的团队工作环境,以软件开发中的环境为灵感。然而,数据科学不同于软件开发。简单地复制和粘贴是不够的。数据科学有其自身的挑战,因此需要一些修改。以下是我在设计数据科学工作环境时考虑的 3 个额外需求:

  1. 代码和数据跟踪

    与软件项目不同,数据科学项目不仅仅是代码的产物,还有数据。一个有效的数据科学工作环境需要对代码和数据进行版本控制。

  2. 无缝的远程开发体验

    处理超出普通机器容量的数据是数据科学项目中的常见情况,这就是为什么人们更喜欢在远程工作站上运行代码。我们设计的环境应该使远程开发体验尽可能无缝。

  3. 易于使用的实验跟踪

    数据科学工作是实验性的。数据科学家可以整天调整超参数,以比较其对模型的影响。因此,易于使用的实验跟踪设施是必不可少的。

既然我们已经明确了需求,下一步就是找到实现这些需求的工具。

工具

Git + DVC

从项目跟踪开始,Git 是多年来软件开发中的通用标准,因此代码版本控制只有一个选项。然而,数据版本控制更为棘手。Git 被设计用于处理小型和基于文本的文件,而我们的大多数机器学习项目包含大量和无结构的数据。我们探索了两个选项:Git LFS 和 DVC。最终我们选择了 DVC,因为它的功能更适合数据科学。

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

用 Git 和 DVC 跟踪代码和数据(图片由作者提供)。

Visual Studio Code

如前所述,ML 项目通常需要超出本地机器的计算能力。这就是为什么 ML 从业者使用 Google Colab、SageMaker 和 Vertex AI 等基于云的 notebook 服务。但 Jupyter notebook 不具备现代 IDE 的任何编码辅助功能。那些功能如果使用得当,可能会大幅提高生产力。因此,我们从 Jupyter notebook 迁移到了 Visual Studio Code。

对于计算资源,我们使用 VS Code 的远程开发功能连接到云远程服务器上的代码和数据仓库。这样,我们不必在高计算能力和良好的开发体验之间做出选择。我们可以兼得二者。有关 VS Code 远程开发的更多细节可以在这里找到。

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

在 Jupyter notebook 上编码 VS 在 VS Code 上编码(图片由作者提供)。

DVC VS Code 扩展

数据科学工作流的另一个关键方面是实验跟踪。数据科学家在获得最终模型之前会反复运行不同参数的 ML 实验。跟踪这些实验是一个极其耗费精力的任务。有许多实验跟踪工具,如 TensorBoard、MLFlow 和 WandB 等。我们对这些工具的担忧是,它们要么是需要托管的独立服务,要么是需要将数据发送到其 API 的第三方 SaaS。

我们最终选择了 dvc metrics,这是 DVC 的一个功能。DVC metrics 允许你将指标保存到 JSON 或 YAML 文件(我们在使用 DVC 之前已经这样做了)并指定这是一个指标文件。DVC 会开始类似于其他数据那样跟踪它,只是你可以使用 CLI 绘制、可视化和比较该文件的内容。通过 CLI 生成的实验报告在用户友好性方面明显不如 TensorBoard 和 MLFlow 提供的那些。幸运的是,DVC 提供了一个 VS Code 扩展,这有助于将这个问题缓解到我们认为可以接受的程度。

我们发现使用 DVC 管理实验的一个局限性是,当多个工程师同时在同一个模型上工作并希望比较他们的结果时,他们必须克隆其他人的分支,因为指标是在 Git 上跟踪的。由于我们团队较小,这种情况不常发生。因此,这是我们可以接受的局限性。

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

DVC VS Code 扩展(图片来自 Code Extension Marketplace)。

工作流程

以下是我们在开发数据科学项目时使用的流程。

  1. 创建 git 和 DVC 仓库

    我们首先创建一个使用 Cookie Cutter Data Science 项目模板的 git 仓库。拥有一个标准项目模板有助于开发人员更顺畅地协作,因为项目已经有了明确的结构。一旦代码设置完成,我们使用 DVC 将数据添加到项目中,并推送到我们的远程存储,以便团队可以访问。

  2. 设置远程开发环境 接下来是设置一个数据科学家运行实验的开发服务器。我们打开一个 EC2 实例,登录到服务器,从 git 中检出代码,并使用 DVC 从远程存储中拉取数据。

  3. 实施与实验

    现在开发环境已准备就绪,我们可以开始工作。如前所述,我们使用 VS Code 进行开发,需要安装两个扩展:Remote SSHDVC。第一个用于远程连接到上一阶段创建的环境,以便我们可以在高性能机器上运行代码。DVC 扩展主要用作实验跟踪工具。安装和配置完毕后,数据科学家可以开始编写代码、运行实验和调整参数。

  4. 打开合并请求并进行代码审查

    一旦数据科学家获得满意的模型,他们将提交所有更改并向 git 仓库打开合并请求,要求进行代码审查。当拉取请求被提出时,我们的 CI/CD 流水线将创建一个报告,比较新分支与目标分支的指标,以便其他人可以在批准/拒绝请求之前查看新更改对模型的影响(如果你对这部分感兴趣,请查看这篇文章)。

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

工作流程概述(图由作者提供)。

结论

就是这样。这就是我如何使用 Git、VS Code 和 DVC 为数据科学项目设置开发环境的方法。一旦我的团队开始在我们的团队中使用这个标准,新成员的入职过程变得更加顺利,因为对新成员应该使用的工具和流程没有了模糊之处。此外,团队成员之间的合作变得更加有效,因为他们使用的是相同的语言

最初发表于 https://thanakornp.com.

我作为全职数据科学家如何跟上最新的 AI 趋势

原文:towardsdatascience.com/how-i-stay-up-to-date-with-the-latest-ai-trends-as-a-full-time-data-scientist-1e535dd0cd79

不,我不会只是让 ChatGPT 告诉我

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

·发表于Towards Data Science ·阅读时长 8 分钟·2023 年 5 月 1 日

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

图片来源:Werner Du plessisUnsplash

数据科学发展迅速。

比如说,真的很快。

五年前,如果你说你对 AI 感兴趣,那大致等于你拥有量子数学的博士学位或背上纹了“我爱π”的纹身。现在,我的妈妈给我发 ChatGPT 的搞笑图片,而你甚至可以购买教婴儿神经网络的图画书

依靠伪造 AI 对话的日子已经一去不复返了。如今,你必须真正掌握相关知识。那么,如何跟上人工智能和数据科学领域的最新动态呢?

尽管已有大量优秀的建议,但我发现很多其他人推荐的策略并不适合我。作为一名在行业中工作的数据科学家,我很少有时间进行研究,把所有时间都花在阅读学术论文和技术发布文档上是不现实的。我需要那些简洁、明了且应用性的资源——帮助我理解如何在日常工作中使用新技术的资源。

在这本指南中,我将总结对我有效的资源。我的目标是强调那些从应用角度有帮助的资源(而不仅仅是从商业或研究角度)。如果你是数据科学家或有志成为数据科学家,我希望这些资源能帮助你保持相关性,并在日常工作中发现创新机会。

说到这里,我们开始吧。

阅读其他数据科学团队的博客

许多数据科学团队都有博客——我个人喜欢的博客包括NetflixTripadvisorDuolingoMetaSpotify。我喜欢这些博客的原因在于它们非常实用。虽然许多学术研究论文关注数据科学和机器学习的理论方面,但公司博客往往强调数据科学如何在实践中解决现实世界的问题。

作为一名在工业界工作的数据科学家,我发现这些内容非常有帮助。我不需要筛选大量的理论术语或解读数百个灰度流程图。我可以直接切入问题的核心,理解数据科学是如何应用于解决我在日常工作中遇到的各种问题的。

比如前几天,我在寻找有关 A/B 测试的信息:这是数据科学中一个我个人经验较少的常见框架。我找到了一些还算可以的文章,然后偶然发现了这个Netflix 技术博客上的精彩系列文章。它不仅解答了我关于 A/B 测试的问题,还充满了 A/B 测试在 Netflix 中应用的实际例子。这对理解理论和实践都非常有帮助。

开始在线写作

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

图片由Daniel Thomas提供,发布在Unsplash

我开始在 Medium 上写作的主要原因之一是为了帮助我学习。

不是为了教学或营销,而是为了学习

人们常说你记住阅读内容的 10%,看到的 20%,而*95%*则是你所教授的内容。这些具体数字的准确性是次要的:重点在于,如果你仅仅依赖于阅读他人的博客,你可能无法充分获取或记住你所阅读的所有信息。开设一个数据科学博客是巩固知识和强化学习内容的极好方式。它迫使你逐步思考问题,并帮助你深入理解主题。

如果写作在线上的想法让你感到有些不自在,不用担心。我曾经也有同样的感觉!但问问自己:最糟糕的情况是什么?在我的情况下,我发现最糟糕的结果可能只是同事们的轻微戏弄。总体而言,我认为我可以应对这种情况,而且考虑到我从写作中获得的经验,这完全值得。

如果你不确定写什么,你可能会喜欢我上周写的 这篇 文章,关于如何想出数据科学项目创意:

如何寻找独特的数据科学项目创意,使你的作品集脱颖而出 [## 如何寻找独特的数据科学项目创意,使你的作品集脱颖而出]

忘记 Titanic 和 MNIST:选择一个独特的项目,提升你的技能并帮助你从人群中脱颖而出

如何寻找独特的数据科学项目创意,使你的作品集脱颖而出

或者,看看 Towards Data Science 的 常见问题页面 上的一些建议:这是我在寻找初始创意时查看的地方。

订阅 TLDR AI Newsletter

阅读和撰写博客是跟上数据科学和 AI 最新 技术 发展的好方法,但如果你想了解 商业 方面的内容,我建议订阅 AI 专注的新闻简报,这些简报跟踪初创公司、收购和研究趋势。

我个人最喜欢的是 TLDR AI Newsletter:他们每天发送一封邮件,总结行业中的关键新闻,这比他们更成熟的 科技新闻简报 要专注得多。其他的选择包括 MIT 技术评论的 The Download 和 DeepLearning.AI 的 The Batch。我喜欢这些新闻简报,因为它们让我只需 2 分钟就能阅读,并帮助我快速了解 AI 世界之外发生的事情。

关注‘每周 ML 论文’ GitHub 仓库

DAIR.AI 维护了一个很棒的 GitHub 仓库,每周更新一份包含 10 篇新机器学习论文的列表。对于每篇论文,DAIR 提供了简要总结和链接到一条推文,进一步解释论文的发现。如果你不想‘关注’这个 GitHub 仓库或 订阅邮件更新,你也可以在 Twitter 上关注 DAIR.AI。

就个人而言,我发现这些简短的总结比你从 Google Scholar 或 arXiv 等服务获得的提醒要“更具实用性”。当在 Google Scholar 和 arXiv 上设置提醒时,你很快就会被通知淹没,而且,如果你像我一样,可能会因为信息过载而完全不参与。DAIR.AI 的一个好处是,它们为你完成了繁重的工作,筛选出最有趣和前沿的论文,并给你选择要么阅读更多,要么快速丢弃并继续前进的选项。

订阅 YouTube 上的数据科学频道

Two Minute Papers 是一个完全符合其名字的 YouTube 频道。每周,该频道上传两个新视频,每个视频旨在提炼最近研究论文的发现,其中许多集中在 AI 领域。就在我写这段文字时,他们的 AI and Deep Learning 播放列表中有 495 个视频。

订阅这个频道是跟踪 AI 研究最新进展的好方法。我特别喜欢他们经典的“OpenAI 玩捉迷藏”视频,但说实话,有这么多精彩的视频,很难选择。

我特别喜欢的另外两个频道是 StatQuest with Josh Starmer3Blue1Brown。我喜欢这些频道,因为它们用一种非常直观和易于理解的方式解释统计学和机器学习概念,而不假设大量的先前知识。虽然这些频道更著名的是它们的入门课程,但它们也发布了关于机器学习前沿主题的解释性视频,例如 3Blue1Brown 最近的视频关于卷积:

参加网络研讨会和午餐学习

很多组织会主办免费的网络研讨会,讲解数据科学和 AI 的最新创新。就个人而言,我很喜欢这些,因为预订网络研讨会迫使我腾出时间用于学习和发展,这对确保我有保护的时间来跟上最新进展非常有帮助。

比如说,如果你在日常工作中使用像 Google BigQuery 或 AWS RDS 这样的云数据库系统,你可能会受益于参加 GoogleAWS 主办的网络研讨会,这些研讨会讲述了如何充分利用这些工具。我最近参加了其中一个(一个非常棒的 BigQuery 网络研讨会),主题是优化 SQL 代码以减少成本和查询运行时间。

在 Twitter 上关注一些 AI 大佬

如果你像我一样,很容易迷失在许多 AI 新闻稿和技术文档的细节中。Twitter 是阅读专家更直接意见和建议的好地方。我特别推荐关注Yann LeCunTimnit GebruGeoffrey HintonAndrew NgChristopher Manning。像这些 AI 研究人员在 Twitter 上分享他们工作的最新动态,并提供有用的小贴士,这些信息并不总是出现在科学论文中。

然而,我喜欢 Twitter 的主要原因是,它是关注在行业(而不仅仅是研究)中工作的数据科学家和 AI 从业者的好方式。像Chris AlbonJay AlammarCassie Kozyrkof这样的人处理的是与数据科学家在行业中日常工作密切相关的问题,关注他们是了解其他数据科学组织正在做什么的好方法。

还有一件事:Twitter 比 arXiv 更幽默。

结论

如果你想在 AI 和数据科学领域建立职业生涯,紧跟最新趋势是绝对必要的。

然而,根据我的经验,我发现很容易被特定任务的琐碎细节吸引,而忽视了整个行业的动态。在这篇文章中,我的目标是突出那些帮助我保持最新趋势和发掘日常工作中创新机会的策略。如果你认为我遗漏了什么宝贵的见解,请在评论中告诉我——我很想听听对你有效的方法。

还有一件事——你能成为我那 1%的人吗?

在 Medium 上,点击‘关注’按钮的读者不到 1%,所以无论你是在 Medium、Twitter还是LinkedIn,你的关注都非常重要。

如果你想无限访问我的所有故事(以及 Medium.com 的其他内容),你可以通过我的推荐链接注册,每月$5。与通过普通注册页面相比,这不会额外增加你的成本,并且有助于支持我的写作,因为我会获得少量佣金。

我是如何将 ChatGPT 转变为类似 SQL 的图像和视频数据集翻译器

原文:towardsdatascience.com/how-i-turned-chatgpt-into-an-sql-like-translator-for-image-and-video-datasets-7b22b318400a?source=collection_archive---------1-----------------------#2023-06-08

这是一个涉及提示工程、软件工程、试验和错误、以及辛勤工作的过程

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

·

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

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

VoxelGPT 使用自然语言查询图像数据集。图片由作者提供。

与表格数据不同,计算机视觉任务的数据集是非结构化的——可以想象成一堆像素、大量标签、标签包以及一些有时结构化的元数据。然而,那些从事计算机视觉工作的人仍然需要能够高效地筛选这些数据宝库,以理解数据集,准备训练集和测试集,发现模式,识别边缘案例,并评估模型性能。

当我需要理解视觉数据时(这基本上是一直需要的),我使用开源库 FiftyOne,它定义了一种强大的 Python 语法来查询计算机视觉数据。这有点像计算机视觉数据的 SQL,它允许我以编程方式筛选、排序和语义切片由图像、视频甚至 3D 点云组成的数据集。

几个月前,随着 ChatGPT 热潮的全面爆发,我在 OpenAI 的网站上看到一个例子应用,将自然语言输入转换为 SQL 查询。虽然这个应用程序相当基础,而计算机视觉数据则复杂得多,但这让我思考:是否可以为图像和视频数据集做类似的事情?换句话说:

我们能否利用大型语言模型 (LLM) 的多功能性,将自然语言查询转换为对非结构化计算机视觉数据集的过滤视图?

答案是?是的,我们可以!

将 LLM 的通用语言和推理能力与 FiftyOne 的查询语言相结合,我们在 Voxel51 的团队构建了 VoxelGPT:一个开源 AI 助手,可以让你无需编写一行代码即可全面查询计算机视觉数据集!

我们使用了 langchaintiktokenfiftyone 完成了这个任务。

你可以在 gpt.fiftyone.ai 免费试用!

这篇文章将带你了解构建领域特定 LLM 应用程序的提示工程、软件工程和大量试错过程。

在这个过程中,我们多次遇到瓶颈,并担心任务是否不可行。如果你正在尝试并挣扎于构建一个 LLM 驱动的应用程序,我希望这篇文章能给你突破自己困境的灵感!

这篇文章的结构如下:

  • 类似 SQL 的图像和视频查询

  • 定义总体任务

  • 为模型提供上下文

  • 生成和利用示例

  • 拆解问题

  • 投入生产

查询语言

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

VoxelGPT 使用自然语言查询图像数据集。图片由作者提供。

在我们深入讨论如何使用 LLM 生成查询之前,值得花一点时间描述一下我们希望模型翻译的查询语言。以下是你需要的基本信息。有关更全面的概述,请参阅FiftyOne 用户指南。如果你已经熟悉这些语言,可以直接跳到下一部分。

  • 数据集: Dataset是 SQL 中的Table或 pandas 中的DataFrame的计算机视觉类比。它包含与一组媒体文件相关的所有信息。

  • 样本: Sample类似于数据表中的一行。样本是Dataset的基本元素。每个样本都有一个filepath,指向一个媒体文件,并存储与该数据片段相关的所有其他信息。

  • 字段: Field类似于数据表中的列,因为它定义了样本的属性(例如,图像宽度、高度和文件路径)。然而,字段是灵活的,它们可以包含其他字段(见下文的Label)。

  • 标签: Label是一个存储语义真实或预测信息的Field。例如,对象检测存储在Detections标签字段中,而分类存储在Classification标签字段中。

与 SQL 或 pandas 一样,你可以使用查询操作来过滤数据。例如,你可能想要查询数据表以获取:

所有在“A”列中条目大于 0.5 的行

然而,与数据表适合进行数字切片和过滤不同,非结构化计算机视觉数据更适合进行语义切片,例如:

在至少有 3 个非“狗”地面真实检测的图像中,检索高置信度的“大”边界框“狗”预测

对非结构化数据进行语义切片需要更大的灵活性。

为了实现这种灵活性,FiftyOne 定义了一组ViewStage方法,这些方法封装了允许的查询操作,如过滤、匹配、选择、排序和排除。这些方法大致类似于 SQL 中的SELECTWHEREORDER BY,但由于查询的空间更大,方法数量也多得多,每个方法都有许多使用场景。有关详细讨论,请参见这个视图备忘单

你可以通过将多个ViewStage操作按顺序组合来获取Dataset的任意子集,这个子集称为DatasetView

下面是查询语言在实际操作中的样子:给定一个名为my_dataset的数据集,如果我们想要获取所有在包含“猫”的 10 张随机图片的"predictions"标签字段中的“狗”检测,我们可以使用以下查询:

view = my_dataset.match(
        F("ground_truth.detections.label").contains(["cat"])
    ).filter_labels(
        "predictions", 
        F("label") == "dog"
    ).take(10)

match()限制为在"ground_truth"检测字段中包含“猫”的图像,然后filter_labels()深入到"predictions"字段中,仅检索“狗”检测,最后take()随机选择一个子集。

定义任务

当我们开始这个旅程时,我们并不知道 LLMs 能做什么。早在 2022 年 12 月,我曾玩过 ChatGPT,并撰写了一篇文章计算机视觉中的隧道视野:ChatGPT 能看见吗?,探讨了聊天机器人对计算机视觉概念的理解。当时,一个通用语言模型能为基本的图像处理任务编写大部分正确的 Python 代码已经令人印象深刻,但在计算机视觉应用中使用 GPT 生成的代码(没有人工验证)的想法似乎不切实际。

在看到文本到 SQL 的示例后,并阅读了 GPT-4 reportedly 以优异的成绩通过 BAR 考试(以及其他考试)的消息后,我决定再次尝试 GPT-for-CV。我让 GPT-4 生成了一些简单查询的 Python 代码,对其回应感到惊喜。GPT-4 的即用结果的真实性还有待提高,这是可以理解的,因为我没有提供任何关于 FiftyOne 库的信息。但使其有效的前景令人兴奋。

需要统一性

当我们实验不同的提示技术时(我们稍后会讨论),如果我们想将其纳入更大的管道中,立刻就显现出我们需要明确任务

如果任由其自行运作,GPT 模型的回应在格式上会因提示而异。

这里只是两个例子,我们将很快进行分析:

提示 1:

你的任务是将输入的自然语言查询转换为 Python 代码,以生成计算机视觉库 FiftyOne 的 ViewStages。

这是你的第一个自然语言查询:“仅包含狗的图像”

给我 FiftyOne 代码。

回应 1:

当然,这里是生成仅包含狗的图像的 FiftyOne 代码:

import fiftyone as fo
import fiftyone.zoo as foz

# Load the dataset
dataset = foz.load_zoo_dataset(
    "open-images-v6",
    split="validation",
    label_types=["detections"],
    classes=["Dog"],
)

# Create a view stage that filters out images without any dog detections
view_stage = fo.ViewStage("detections", fo.FilterField("detections", "label", "Dog"))

# Apply the view stage to the dataset
dataset = dataset.add_stage(view_stage)

# View the dataset
session = fo.launch_app(dataset)

这段代码加载了 Open Images v6 验证数据集,仅包含“检测”标签类型的狗。然后,它创建了一个视图阶段,过滤掉没有狗检测的图像。最后,将视图阶段应用到数据集中,并在 FiftyOne 应用中启动。

提示 2:

你的任务是将输入的自然语言查询转换为 Python 代码,以生成 FiftyOne 机器学习库的 ViewStages。

这是你的第一个自然语言查询:“最独特的假阳性检测图像”

给我 FiftyOne 代码。

回应 2:

当然,这里是使用 FiftyOne 库在 Python 中生成“最独特的假阳性检测图像”自然语言查询的 FiftyOne 代码:

import fiftyone as fo
import fiftyone.zoo as foz

# Load your dataset
dataset = fo.load_dataset("your_dataset_name")

# Define your false positive detection model
model = foz.load_zoo_model("your_model_name")

# Define your pipeline
pipeline = [
    {"$set": {"predictions": {"$predict": model}}},
    {"$match": {"predictions.mistakes.false_positive": True}},
    {"$group": {"_id": "$filepath"}},
    {"$count": "num_images"},
    {"$sort": {"num_images": -1}},
]

# Run your pipeline and display the results in a view stage
view = dataset.aggregate(pipeline).limit(10).display()

在这段代码中,我们首先加载 FiftyOne 库和任何必要的依赖项。然后,我们使用 fo.load_dataset()加载你的数据集。

接下来,我们使用 foz.load_zoo_model()定义你的假阳性检测模型。该函数从 FiftyOne Zoo Model Zoo 加载一个预训练模型,其中包括广泛的计算机视觉最先进模型。

之后,我们使用 s e t 、 set、 setmatch、 g r o u p 、 group、 groupcount 和 s o r t 聚合阶段定义我们的管道。管道首先使用 sort 聚合阶段定义我们的管道。管道首先使用 sort聚合阶段定义我们的管道。管道首先使用set 为数据集中每个样本添加一个包含模型预测的字段。然后,我们使用$match 过滤样本,仅包括那些检测到假阳性的样本。

接下来,我们使用 g r o u p 按文件路径对样本进行分组,并使用 group 按文件路径对样本进行分组,并使用 group按文件路径对样本进行分组,并使用count 计算每个组中的图像数量。最后,我们使用$sort 按图像数量的降序对组进行排序。

最后,我们在你的数据集上运行管道,并使用 limit()将结果限制为检测到最多假阳性的前 10 张图像。然后,我们使用 display()在视图阶段展示结果。

尽管这两个示例的提示结构相同,但响应在几个关键方面有所不同。响应 1 尝试通过向数据集添加ViewStage来创建DatasetView。响应 2 定义并应用了一个MongoDB 聚合管道,随后使用limit()方法(应用Limit阶段)将视图限制为 10 个样本,并使用了一个不存在(即幻觉)display()方法。此外,尽管响应 1 加载了一个实际的数据集(Open Images V6),响应 2 实际上是模板代码,因为"your_dataset_name""your_model_name"需要填写。

这些示例还突出了以下问题:

  1. 模板代码:一些响应包含了导入模块、实例化数据集(和模型)以及可视化视图(session = fo.launch_app(dataset))的代码。

  2. 解释性文本:在许多情况下——包括教育背景——模型解释其“推理”是一个积极的。如果我们希望代表用户执行查询,这些解释性文本就会成为障碍。一些查询甚至导致多个被文本分隔的代码块。

我们真正想要的是让 LLM 回应可以直接复制粘贴到 Python 进程中的代码,而不附带额外的负担。作为对模型进行提示的第一次尝试,我开始将以下文本作为前缀添加到任何自然语言查询中:

Your task is to convert input natural language queries into Python code to generate ViewStages for the computer vision library FiftyOne.
Here are some rules:
- Avoid all header code like importing packages, and all footer code like saving the dataset or launching the FiftyOne App.
- Just give me the final Python code, no intermediate code snippets or explanation.
- always assume the dataset is stored in the Python variable `dataset`
- you can use the following ViewStages to generate your response, in any combination: exclude, exclude_by, exclude_fields, exclude_frames,

关键是,我定义了一个任务,并设置了规则,指导模型允许和不允许做的事情。

注意:随着响应格式的更加统一,我在这时从 ChatGPT 聊天界面转向使用 OpenAI 的 API 中的 GPT-4。

限制范围

我们的团队还决定,至少在开始时,我们将限制我们要求 LLM 执行的范围。尽管 fiftyone 查询语言本身功能全面,但要求预训练模型在没有任何微调的情况下执行任意复杂的任务是令人失望的。开始时要简单,然后逐步增加复杂性。

对于此实验,我们施加了以下限制:

  • 仅限图像和视频:不要指望 LLM 查询 3D 点云或分组数据集。

  • 忽略变化的 **ViewStages**:大多数 ViewStages 遵循相同的基本规则,但有一些例外。Concat 是唯一一个接受第二个 DatasetViewViewStagesMongo 使用 MongoDB 聚合语法;GeoNear 具有一个 query 参数,该参数接受一个 fiftyone.utils.geojson.geo_within() 对象;而 GeoWithin 需要一个 2D 数组来定义“within”适用的区域。我们决定忽略 ConcatMongoGeoWithin,并支持所有 GeoNear 的使用 除了 query 参数。

  • 坚持两个阶段:虽然模型能够组合任意数量的阶段是很好的,但在我见过的大多数工作流程中,一两个 ViewStages 就足以创建所需的 DatasetView。该项目的目标不是陷入细节,而是为计算机视觉从业者构建一些有用的东西。

提供上下文

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

VoxelGPT 使用自然语言查询图像数据集。图片由作者提供。

除了给模型一个明确的“任务”和提供清晰的指示外,我们发现通过提供有关 FiftyOne 查询语言如何工作的更多信息,可以提高性能。如果没有这些信息,LLM 就像在黑暗中盲目飞行,只是在抓取和伸手进入黑暗中。

例如,在提示 2 中,当我要求获取假阳性预测时,响应尝试用predictions.mistakes.false_positive来引用这些假阳性。就 ChatGPT 而言,这似乎是存储和访问假阳性信息的合理方法。

模型不知道在 FiftyOne 中,检测预测的真实性/虚假性是通过 dataset.evaluate_detections() 评估的,运行该评估后,你可以通过匹配 eval_fp>0 来检索所有具有假阳性的图像:

images_with_fp = dataset.match(F("eval_fp")>0)

我尝试通过提供额外的规则来澄清任务,例如:

- When a user asks for the most "unique" images, they are referring to the "uniqueness" field stored on samples.
- When a user asks for the most "wrong" or "mistaken" images, they are referring to the "mistakenness" field stored on samples.
- If a user doesn't specify a label field, e.g. "predictions" or "ground_truth" to which to apply certain operations, assume they mean "ground_truth" if a ground_truth field exists on the data.

我还提供了有关标签类型的信息:

- Object detection bounding boxes are in [top-left-x, top-left-y, width, height] format, all relative to the image width and height, in the range [0, 1]
- possible label types include Classification, Classifications, Detection, Detections, Segmentation, Keypoint, Regression, and Polylines

此外,虽然通过提供允许的视图阶段列表,我能够推动模型使用这些阶段,但它并不知道

  • 给定阶段相关时,或者

  • 如何以语法正确的方式使用阶段

为了填补这一空白,我想给 LLM 提供有关每个视图阶段的信息。我编写了代码来遍历视图阶段(你可以使用 fiftyone.list_view_stages() 列出这些阶段),存储文档字符串,然后将文档字符串的文本拆分为描述和输入/参数。

然而,我很快遇到了一个问题:上下文长度。

使用 OpenAI API 的基础 GPT-4 模型时,我已经接近了 8,192 令牌的上下文长度。这是在添加示例或任何有关数据集的信息之前!

OpenAI 确实有一个具有 32,768 令牌上下文的 GPT-4 模型,理论上我可以使用,但一个粗略的计算让我相信这可能会很贵。如果我们填满了整个 32k 令牌上下文,根据OpenAI 的定价,每次查询大约需要 2 美元!

相反,我们的团队重新考虑了我们的方法,并进行了以下操作:

  • 切换到 GPT-3.5

  • 最小化令牌数

  • 更加挑剔地选择输入信息

切换到 GPT-3.5

没有什么是免费的——这确实导致了稍微较低的性能,至少在初期。在项目过程中,我们通过提示工程恢复并远远超越了这一点!在我们的案例中,这个努力是值得的。在其他情况下,可能就不是这样了。

最小化令牌数

由于上下文长度成为限制因素,我使用了以下简单的技巧:使用 ChatGPT 优化提示!

一次处理一个ViewStage,我将原始描述和输入列表提供给 ChatGPT,并附上一个要求 LLM 最小化文本的令牌数的提示,同时保留所有语义信息。使用tiktoken来计算原始和压缩版本中的令牌数,我能够将令牌数减少约 30%。

更加挑剔

尽管提供上下文对模型很有帮助,但某些信息比其他信息更有用,具体取决于任务。如果模型仅需要生成涉及两个ViewStages的 Python 查询,它可能不会从其他ViewStages的输入信息中获得太大帮助。

我们知道需要一种方法来根据输入的自然语言查询选择相关信息。然而,这不会像在描述和输入参数上执行相似性搜索那样简单,因为前者通常以与后者非常不同的语言出现。我们需要一种将输入和信息选择联系起来的方法。

结果发现,那个链接是示例

示例

生成示例

如果你曾经使用过 ChatGPT 或其他 LLM,你可能亲身体验过,即使只提供一个相关的示例,也能大幅提高性能。

作为起点,我想出了 10 个完全合成的示例,并将这些示例通过在任务规则和ViewStage描述下方添加以下内容传递给 GPT-3.5:

以下是 A、B 形式的输入-输出对的一些示例:

A) "Filepath starts with '/Users'"
B) `dataset.match(F("filepath").starts_with("/Users"))`

A) "Predictions with confidence > 0.95"
B) `dataset.filter_labels("predictions", F("confidence") > 0.95)`

…

仅凭这 10 个示例,模型响应的质量有了显著提高,因此我们的团队决定对此采取系统化的方式。

  1. 首先,我们仔细检查了我们的文档,找出了通过ViewStages组合创建的所有视图示例。

  2. 然后,我们查看了ViewStages的列表,并添加了示例,以便尽可能全面地覆盖用法语法。为此,我们确保每个参数或关键字至少有一个示例,以便为模型提供一个可遵循的模式。

  3. 在覆盖了用法语法之后,我们更改了示例中字段和类的名称,以防止模型对名称与阶段之间的关联产生错误的假设。例如,我们不希望模型仅仅因为所有match_labels()的示例都包含“person”类,就强烈关联“person”类与match_labels()方法。

选择类似的示例

在这个示例生成过程结束时,我们已经有了数百个示例——远远超过了可以容纳的上下文长度。幸运的是,这些示例包含了(作为输入)自然语言查询,我们可以直接与用户的自然语言查询进行比较。

为了进行这种比较,我们使用 OpenAI 的text-embedding-ada–002模型预计算了这些示例查询的嵌入。在运行时,用户的查询会使用相同的模型进行嵌入,然后选择与自然语言查询最相似的示例——通过余弦距离来确定。最初,我们使用了ChromaDB来构建内存中的向量数据库。然而,由于我们处理的是数百或数千个向量,而不是数十万或数百万个向量,实际上转而使用精确向量搜索更为合理(此外,我们还减少了依赖)。

管理这些示例和提示的组件变得越来越困难,因此我们在这时开始使用LangChain 的 Prompts 模块。最初,我们能够使用他们的相似性示例选择器来选择最相关的示例,但最终我们不得不编写一个自定义的ExampleSelector,以便对预过滤有更多控制。

过滤适当的示例

在计算机视觉查询语言中,查询的适当语法可能取决于数据集中样本的媒体类型:例如,视频有时需要与图像不同地处理。为了避免通过给出看似矛盾的示例而使模型感到困惑,或通过迫使模型基于媒体类型推断而使任务复杂化,我们决定仅给出对给定数据集语法正确的示例。在向量搜索的上下文中,这被称为预过滤

这个想法效果很好,以至于我们最终将相同的考虑应用于数据集的其他特性。在某些情况下,差异仅仅是语法上的——在查询标签时,访问Detections标签的语法与访问Classification标签的语法不同。其他过滤器则更具战略性:有时我们不希望模型了解查询语言的某些特性。

例如,我们不想给 LLM 提供使用它无法访问的计算的示例。如果没有为特定数据集构建文本相似性索引,则向模型提供查找与自然语言查询最佳视觉匹配的示例是没有意义的。类似地,如果数据集中没有任何评估运行,则查询真实正例和假阳性将产生错误或空结果。

你可以在 GitHub 仓库中的view_stage_example_selector.py中查看完整的示例预处理管道。

根据示例选择上下文信息

对于给定的自然语言查询,我们然后使用ExampleSelector选择的示例来决定在上下文中提供哪些额外信息。

特别是,我们统计了这些选择示例中每个ViewStage的出现次数,确定了出现频率最高的五个``ViewStages,并在我们的提示中添加了关于这些ViewStages`的描述和输入参数信息。这么做的理由是,如果一个阶段在类似的查询中频繁出现,它很可能(但不能保证)与该查询相关。

如果不相关,那么描述将帮助模型确定它不相关。如果相关,那么关于输入参数的信息将帮助模型生成语法正确的ViewStage操作。

分而治之

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

VoxelGPT 使用自然语言查询图像数据集。图片由作者提供。

到目前为止,我们专注于将尽可能多的相关信息——而且只是相关信息——挤进一个提示中。但这种方法已接近其极限。

即使不考虑每个数据集都有自己的字段和类名,可能的 Python 查询空间也太大了。

为了取得进展,我们需要将问题分解成更小的部分。借鉴近期的方法,包括链式思考提示选择推理提示,我们将生成DatasetView的问题分成了四个不同的选择子问题。

  1. 算法

  2. 算法运行

  3. 相关字段

  4. 相关的类名

然后我们将这些选择“链接”串联起来,并将它们的输出传递给模型,在最终提示中用于DatasetView推理。

对于这些子任务,统一性和简洁性的原则适用。我们尽可能地回收现有示例中的自然语言查询,但明确简化了每个选择任务的所有输入和输出的格式。对一个链接来说最简单的东西可能对另一个链接并不是最简单的!

算法

在 FiftyOne 中,来自数据集计算的信息被存储为一个“运行”(“run”)。这包括诸如 uniqueness 这样的计算,它衡量每张图像相对于数据集中其他图像的独特性,以及 hardness,它量化模型在尝试学习此样本时会遇到的难度。它还包括 similarity 的计算,这涉及为与每个样本相关联的嵌入生成向量索引,甚至包括我们之前提到的 evaluation 计算。

每个这些计算生成不同类型的结果对象,每种结果对象都有自己的 API。此外,ViewStages 和这些计算之间没有一一对应的关系。以独特性(uniqueness)为例。

独特性计算结果存储在每张图像的一个浮点值字段中(默认值为 "uniqueness")。这意味着根据情况,你可能希望按独特性排序:

view = dataset.sort_by("uniqueness")

检索独特性高于某个阈值的样本:

from fiftyone import ViewField as F
view = dataset.match(F("uniqueness") > 0.8)

或者仅显示独特性字段:

view = dataset.select_fields("uniqueness")

在这个选择步骤中,我们任务 LLM 预测可能与用户自然语言查询相关的计算。这个任务的一个示例是:

Query: "most unique images with a false positive"
Algorithms used: ["uniqueness", "evaluation"]

算法运行

一旦识别出可能相关的计算算法,我们会任务 LLM 选择每个计算的最合适运行。这是必需的,因为某些计算可以在同一数据集上使用不同配置运行多次,而 ViewStage 可能仅在正确的“运行”下才有意义。

一个很好的例子是相似度运行。假设你在你的数据上测试两个模型(InceptionV3 和 CLIP),并且你已经为每个模型在数据集中生成了向量相似度索引。当使用 SortBySimilarity 视图阶段时,哪些图像被确定为与哪些其他图像最相似可能会强烈依赖于嵌入模型,因此以下两个查询可能会生成不同的结果:

## query A:
"show me the 10 most similar images to image 1 with CLIP"

## query B:
"show me the 10 most similar images to image 1 with InceptionV3"

这个运行选择过程对每种计算类型分别处理,因为每种计算需要修改后的任务规则和示例。

相关字段

链中的这个链接涉及识别所有与自然语言查询相关的字段名称,这些字段名称 与计算运行相关。例如,并非所有包含预测的 数据集 都将这些标签存储在名为 "predictions" 的字段下。根据个人、数据集和应用的不同,预测可能存储在名为 "pred""resnet""fine-tuned""predictions_05_16_2023" 或完全不同的名称的字段中。

此任务的示例包括查询、数据集中所有字段的名称和类型,以及相关字段的名称:

Query: "Exclude model2 predictions from all samples"
Available fields: "[id: string, filepath: string, tags: list, ground_truth: Detections, model1_predictions: Detections, model2_predictions: Detections, model3_predictions: Detections]"
Required fields: "[model2_predictions]"

相关的类名

对于标签字段如分类和检测,将自然语言查询转换为 Python 代码需要使用数据集中实际类的名称。为此,我让 GPT-3.5 执行命名实体识别以识别输入查询中的标签类。

在查询“样本中至少有一个牛预测且没有马”的情况下,模型的任务是识别"horse""cow"。这些识别出的名称随后与在之前步骤中选择的标签字段的类名进行比较——首先是区分大小写的,然后是不区分大小写的,最后是不区分复数的。

如果在数据集中未找到命名实体和类名之间的匹配,我们将退回到语义匹配:"people""person""table""dining table",和"animal"[“cat”, “dog", “horse", …]

每当匹配不完全相同时,我们使用匹配的类名来更新传递给最终推断步骤的查询:

query: "20 random images with a table"
## becomes:
query: "20 random images with a dining table"

ViewStage 推断

一旦所有这些选择完成,类似的示例、相关的描述和相关的数据集信息(选择的算法运行、字段和类)将与(可能修改过的)查询一起传递给模型。

与其指示模型以dataset.view1().view2()…viewn()的形式返回代码,我们最终去掉了dataset部分,而是要求模型将ViewStages作为列表返回。当时,我对这种改进性能感到惊讶,但回想起来,它与任务拆分越多,LLM 表现越好的见解相一致。

使其可用

创建一个 LLM 驱动的玩具很酷,但将相同的内核转变为 LLM 驱动的应用程序则更酷。以下是我们如何做到这一点的简要概述。

单元测试

当我们将其从一个原则证明转变为一个稳健的工程系统时,我们使用单元测试来压力测试管道并识别薄弱点。链中链接的模块化特性意味着每一步可以单独进行单元测试、验证和迭代,而无需运行整个链。

这导致了更快的改进,因为在提示工程团队中,不同的个人或小组可以并行处理链中的不同链接。此外,这还导致了成本减少,因为理论上,你只需要运行 LLM 推断的单个步骤来优化链中的单个链接。

评估 LLM 生成的代码

我们使用 Python 的eval()函数将 GPT-3.5 的响应转换为DatasetView。然后,我们设置 FiftyOne App 的session状态以显示该视图。

输入验证

垃圾输入 → 垃圾输出。为了避免这种情况,我们运行验证以确保用户的自然语言查询是合理的。

首先,我们使用 OpenAI 的审核端点。然后,我们将任何提示分类到以下四种情况之一:

1: 合理且完整:该提示可以合理地转换为用于查询数据集的 Python 代码。

所有带狗检测的图像

2: 合理但不完整:该提示合理,但在没有额外信息的情况下无法转换为 DatasetView。例如,如果我们有两个模型对数据进行预测,那么仅提到“我的模型”的提示是不足够的:

检索我的模型的错误预测

3: 超出范围:我们正在构建一个生成查询视图的应用程序,应用于计算机视觉数据集。虽然底层的 GPT-3.5 模型是通用 LLM,但我们的应用不应变成一个与数据集相邻的离线 ChatGPT 会话。如下提示应被拒绝:

像我五岁一样解释量子计算

4: 不合理:给定一个随机字符串,尝试生成数据集视图是不合适的——从哪里开始呢?!

Azlsakjdbiayervbg

在尝试将用户的输入查询转换为视图阶段序列之前,我们将输入传递到带有验证指令和示例的模型中。根据响应,我们要么提示用户提供更多信息或更合理的查询,要么继续进行数据集视图生成流程。

总结

VoxelGPT 实战!视频由作者提供。

一个简单的想法引发了一个疯狂的想法,这段旅程将那个疯狂的想法变成了现实。通过提示工程、一些真正的软件工程、大量的辛勤工作以及健康的黑魔法,我们的小团队创建了一个 LLM 驱动的应用程序,将自然语言查询转换为计算机视觉数据集的过滤视图。

要点很简单:定义任务、规定规则、限制范围、简化、选择一致性、分解问题,并保持相关性。

当我们将所有部分组合在一起时,它看起来像这样:

  1. 验证查询

  2. 查找类似的示例

  3. 检索相关文档

  4. 确定潜在的算法/计算

  5. 选择这些算法最可能的运行结果

  6. 确定在查询中访问/利用的字段(属性)

  7. 推断每个标签字段的类名称

  8. 生成查询的视图阶段列表

  9. 组成视图阶段并返回过滤后的数据集视图

  10. 胜利

VoxelGPT 远非完美。但这从未是目标。每一步都有改进的空间——更不用说更多的示例了!还可以将其作为构建 AutoGPT 风格的计算机视觉任务委托器的基础。

VoxelGPT 的任何贡献都欢迎。它是免费的开源软件!🙂

如何将公司的文档转变为可搜索的数据库,利用 OpenAI

原文:towardsdatascience.com/how-i-turned-my-companys-docs-into-a-searchable-database-with-openai-4f2d34bd8736?source=collection_archive---------0-----------------------#2023-04-25

以及你如何用同样的方法处理你的文档

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

·

关注 发表在 Towards Data Science ·15 分钟阅读·2023 年 4 月 25 日

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

图片来自 Unsplash。

在过去的六个月里,我一直在系列 A 初创公司 Voxel51 工作,并且是开源计算机视觉工具包 FiftyOne的创建者。作为一名机器学习工程师和开发者推广员,我的工作是倾听我们的开源社区,满足他们的需求——新的功能、集成、教程、研讨会,等等。

几周前,我们为 FiftyOne 添加了对向量搜索引擎和文本相似性查询的原生支持,以便用户可以通过简单的自然语言查询在他们(通常是庞大的——包含数百万或数千万样本的)数据集中找到最相关的图像。

这让我们处于一个奇特的位置:现在使用开源 FiftyOne 的人可以轻松地用自然语言查询搜索数据集,但使用我们的文档仍然需要传统的关键字搜索。

我们有大量文档,这有利有弊。作为用户,我有时发现,由于文档量庞大,找到我确切需要的信息比我希望的时间要长。

我不打算就此罢休……所以我在业余时间构建了这个:

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

从命令行语义化搜索公司文档。图片由作者提供。

所以,这是我如何将我们的文档转换为语义搜索的向量数据库:

  • 将所有文档转换为统一格式

  • 将文档拆分为块并添加了一些自动清理

  • 为每个块计算了嵌入

  • 从这些嵌入生成了一个向量索引

  • 定义了索引查询

  • 将所有内容封装在用户友好的命令行界面和 Python API 中

你可以在 voxel51/fiftyone-docs-search 仓库中找到本帖的所有代码,并且可以通过 pip install -e . 在本地编辑模式中轻松安装包。

更好的是,如果你想使用这种方法为你自己的网站实现语义搜索,你可以跟着做!以下是你需要的材料:

  • 安装 openai Python 包并创建一个账户: 你将使用此账户将文档和查询发送到推断端点,该端点会返回每个文本片段的嵌入向量。

  • 安装 qdrant-client Python 包并通过 Docker 启动 Qdrant 服务器:你将使用 Qdrant 为文档创建一个本地托管的向量索引,查询将对其进行运行。Qdrant 服务将在 Docker 容器中运行。

将文档转换为统一格式

我公司的文档都托管为 HTML 文档,地址是 docs.voxel51.com。一个自然的起点是使用 Python 的 requests 库下载这些文档,并用 Beautiful Soup 解析文档。

作为开发者(以及我们许多文档的作者),我认为自己可以做得更好。我已经在本地计算机上有一个 GitHub 仓库的工作副本,其中包含生成 HTML 文档所用的所有原始文件。我们的某些文档是用 Sphinx ReStructured Text (RST) 编写的,而其他文档,如教程,则从 Jupyter notebooks 转换为 HTML。

我(错误地)认为,离 RST 和 Jupyter 文件的原始文本越近,事情就会越简单。

RST

在 RST 文档中,部分由仅包含 =-_ 字符的行来划分。例如,这是 FiftyOne 用户指南中的一个文档,其中包含所有三种划分符:

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

开源 FiftyOne 文档中的 RST 文档。图片由作者提供。

然后,我可以移除所有的 RST 关键字,如 toctreecode-blockbutton_link(还有许多其他的),以及随关键字、区块开始或区块描述符出现的 :::..

链接也很容易处理:

no_links_section = re.sub(r"<[^>]+>_?","", section)

当我想从 RST 文件中提取部分锚点时,情况开始变得棘手。我们许多部分明确指定了锚点,而其他部分则在转换为 HTML 时被推断出来。

这里有一个例子:

.. _brain-embeddings-visualization:

Visualizing embeddings
______________________

The FiftyOne Brain provides a powerful
:meth:`compute_visualization() <fiftyone.brain.compute_visualization>` method
that you can use to generate low-dimensional representations of the samples
and/or individual objects in your datasets.

These representations can be visualized natively in the App's
:ref:`Embeddings panel <app-embeddings-panel>`, where you can interactively
select points of interest and view the corresponding samples/labels of interest
in the :ref:`Samples panel <app-samples-panel>`, and vice versa.

.. image:: /images/brain/brain-mnist.png
   :alt: mnist
   :align: center

There are two primary components to an embedding visualization: the method used
to generate the embeddings, and the dimensionality reduction method used to
compute a low-dimensional representation of the embeddings.

Embedding methods
-----------------

The `embeddings` and `model` parameters of
:meth:`compute_visualization() <fiftyone.brain.compute_visualization>`
support a variety of ways to generate embeddings for your data:

在我们用户指南文档中的 brain.rst 文件(上面展示的部分),Visualizing embeddings 部分有一个由 .. _brain-embeddings-visualization: 指定的锚点 #brain-embeddings-visualization。然而,紧随其后的 Embedding methods 子部分有一个自动生成的锚点。

另一个很快出现的困难是如何处理 RST 中的表格。List tables 相对简单。例如,这是我们 View Stages 备忘单中的一个列表表格:

.. list-table::

   * - :meth:`match() <fiftyone.core.collections.SampleCollection.match>`
   * - :meth:`match_frames() <fiftyone.core.collections.SampleCollection.match_frames>`
   * - :meth:`match_labels() <fiftyone.core.collections.SampleCollection.match_labels>`
   * - :meth:`match_tags() <fiftyone.core.collections.SampleCollection.match_tags>`

Grid tables 另一方面,很快就会变得凌乱。它们给文档作者提供了很大的灵活性,但这种灵活性使得解析起来非常麻烦。看看我们 Filtering 备忘单中的这张表格:

+-----------------------------------------+-----------------------------------------------------------------------+
| Operation                               | Command                                                               |
+=========================================+=======================================================================+
| Filepath starts with "/Users"           |  .. code-block::                                                      |
|                                         |                                                                       |
|                                         |     ds.match(F("filepath").starts_with("/Users"))                     |
+-----------------------------------------+-----------------------------------------------------------------------+
| Filepath ends with "10.jpg" or "10.png" |  .. code-block::                                                      |
|                                         |                                                                       |
|                                         |     ds.match(F("filepath").ends_with(("10.jpg", "10.png"))            |
+-----------------------------------------+-----------------------------------------------------------------------+
| Label contains string "be"              |  .. code-block::                                                      |
|                                         |                                                                       |
|                                         |     ds.filter_labels(                                                 |
|                                         |         "predictions",                                                |
|                                         |         F("label").contains_str("be"),                                |
|                                         |     )                                                                 |
+-----------------------------------------+-----------------------------------------------------------------------+
| Filepath contains "088" and is JPEG     |  .. code-block::                                                      |
|                                         |                                                                       |
|                                         |     ds.match(F("filepath").re_match("088*.jpg"))                      |
+-----------------------------------------+-----------------------------------------------------------------------+

在表格中,行可以占用任意数量的行,列的宽度也可以变化。网格表格单元格中的代码块也很难解析,因为它们占用多行空间,因此它们的内容与其他列的内容交错在一起。这意味着这些表格中的代码块需要在解析过程中有效地重建。

这不是世界末日,但也不是理想状态。

Jupyter

Jupyter notebooks 解析起来相对简单。我能够将 Jupyter notebook 的内容读取为字符串列表,每个单元格一个字符串:

import json
ifile = "my_notebook.ipynb"
with open(ifile, "r") as f:
    contents = f.read()
contents = json.loads(contents)["cells"]
contents = [(" ".join(c["source"]), c['cell_type'] for c in contents]

此外,部分由以 # 开头的 Markdown 单元格划分。

尽管面临 RST 带来的挑战,我决定转向 HTML 并平等对待我们的所有文档。

HTML

我通过bash generate_docs.bash从本地安装生成了 HTML 文档,并开始用 Beautiful Soup 解析它们。然而,我很快意识到,当 RST 代码块和包含内联代码的表格被转换为 HTML 时,虽然渲染正确,但 HTML 本身却非常难以处理。以我们的过滤器备忘单为例。

当在浏览器中渲染时,我们过滤器备忘单中日期和时间部分之前的代码块如下所示:

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

开源 FiftyOne 文档中的备忘单截图。图片由作者提供。

然而,原始 HTML 看起来是这样的:

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

RST 备忘单转换为 HTML。图片由作者提供。

这不是不可能解析,但也远非理想。

Markdown

幸运的是,我通过使用 markdownify 将所有 HTML 文件转换为 Markdown 解决了这些问题。Markdown 有几个关键优点,使其成为这个工作的最佳选择。

  1. 比 HTML 更干净:代码格式从一串杂乱的span元素简化为用单个`标记的内联代码片段,代码块则用三重引号标记 ```py ``````pybefore and after. This also made it easy to split into text and code.
  2. Still contained anchors: unlike raw RST, this Markdown included section heading anchors, as the implicit anchors had already been generated. This way, I could link not just to the page containing the result, but to the specific section or subsection of that page.
  3. Standardization: Markdown provided a mostly uniform formatting for the initial RST and Jupyter documents, allowing us to give their content consistent treatment in the vector search application.

Note on LangChain

Some of you may know about the open source library LangChain for building applications with LLMs, and may be wondering why I didn’t just use LangChain’s Document Loaders and Text Splitters. The answer: I needed more control!

Processing the documents

Once the documents had been converted to Markdown, I proceeded to clean the contents and split them into smaller segments.

Cleaning

Cleaning most consisting in removing unnecessary elements, including:

  • Headers and footers
  • Table row and column scaffolding — e.g. the |’s in |select()| select_by()|
  • Extra newlines
  • Links
  • Images
  • Unicode characters
  • Bolding — i.e. **text**text

I also removed escape characters that were escaping from characters which have special meaning in our docs: _ and *. The former is used in many method names, and the latter, as usual, is used in multiplication, regex patterns, and many other places:


document = document.replace("\_", "_").replace("\*", "*")

```py

## Splitting documents into semantic blocks

With the contents of our docs cleaned, I proceeded to split the docs into bite-sized blocks.

First, I split each document into sections. At first glance, it seems like this can be done by finding any line that starts with a `#` character. In my application, I did not differentiate between h1, h2, h3, and so on (`#` , `##` , `###`), so checking the first character is sufficient. However, this logic gets us in trouble when we realize that `#` is also employed to allow comments in Python code.

To bypass this problem, I split the document into text blocks and code blocks:

text_and_code = page_md.split(‘```py’)
text = text_and_code[::2]
code = text_and_code[1::2]


然后,我用 `#` 标记文本块中的新节的开始。我从这一行中提取了节标题和锚点:

```py
def extract_title_and_anchor(header):
    header = " ".join(header.split(" ")[1:])
    title = header.split("[")[0]
    anchor = header.split("(")[1].split(" ")[0]
    return title, anchor

然后将每个文本或代码块分配到适当的部分。

起初,我还尝试将文本块拆分成段落,假设因为一个部分可能包含许多不同主题的信息,该部分的嵌入可能与只涉及其中一个主题的文本提示的嵌入不相似。然而,这种方法导致大多数搜索查询的最佳匹配过于集中在单行段落上,结果显示这些搜索结果并不十分有用。

查看附带的 GitHub 仓库 ,了解这些方法的实现,并尝试在您自己的文档中应用!

使用 OpenAI 嵌入文本和代码块

将文档转换、处理并拆分为字符串后,我为这些块生成了嵌入向量。由于大型语言模型本质上灵活且通常能力强,我决定将文本块和代码块视为相同的文本,并用相同的模型进行嵌入。

我使用了 OpenAI 的 text-embedding-ada-002 模型,因为它易于使用,且在所有 OpenAI 的嵌入模型中(在 BEIR 基准上)表现最佳,并且也是最便宜的。实际上,它的费用非常低($0.0004/1K 标记),以至于为 FiftyOne 文档生成所有嵌入仅花费了几美分!正如 OpenAI 自己所说,“我们建议几乎所有用例都使用 text-embedding-ada-002。它更好、更便宜、更简单。”

使用这个嵌入模型,你可以生成一个 1536 维的向量,表示任何输入提示,最多 8,191 个标记(大约 30,000 个字符)。

要开始使用,你需要创建一个 OpenAI 账户,在 platform.openai.com/account/api-keys 生成 API 密钥,使用以下命令将该 API 密钥导出为环境变量:

export OPENAI_API_KEY="<MY_API_KEY>"

你还需要安装 openai Python 库

pip install openai

我编写了一个 OpenAI API 的包装器,该包装器接受文本提示并返回嵌入向量:

MODEL = "text-embedding-ada-002"

def embed_text(text):
    response = openai.Embedding.create(
        input=text,
        model=MODEL
    )
    embeddings = response['data'][0]['embedding']
    return embeddings

为了生成所有文档的嵌入,我们只需对所有文档中的每个子部分——文本和代码块——应用此函数。

创建 Qdrant 向量索引

拥有嵌入后,我创建了一个向量索引用于搜索。我选择使用 Qdrant 的原因与我们选择在 FiftyOne 中添加本地 Qdrant 支持的原因相同:它是开源的、免费的,并且易于使用。

要开始使用 Qdrant,你可以拉取一个预构建的 Docker 镜像并运行容器:

docker pull qdrant/qdrant
docker run -d -p 6333:6333 qdrant/qdrant

此外,你还需要安装 Qdrant Python 客户端:

pip install qdrant-client

我创建了 Qdrant 集合:

import qdrant_client as qc
import qdrant_client.http.models as qmodels

client = qc.QdrantClient(url="localhost")
METRIC = qmodels.Distance.DOT
DIMENSION = 1536
COLLECTION_NAME = "fiftyone_docs"

def create_index():
    client.recreate_collection(
    collection_name=COLLECTION_NAME,
    vectors_config = qmodels.VectorParams(
            size=DIMENSION,
            distance=METRIC,
        )
    )

然后我为每个子部分(文本或代码块)创建了一个向量:

import uuid
def create_subsection_vector(
    subsection_content,
    section_anchor,
    page_url,
    doc_type
    ):

    vector = embed_text(subsection_content)
    id = str(uuid.uuid1().int)[:32]
    payload = {
        "text": subsection_content,
        "url": page_url,
        "section_anchor": section_anchor,
        "doc_type": doc_type,
        "block_type": block_type
    }
    return id, vector, payload

对于每个向量,你可以提供额外的上下文作为 payload 的一部分。在这种情况下,我包括了结果可以找到的 URL(及锚点)、文档的 类型,以便用户可以指定是否希望搜索所有文档,或只是某些类型的文档,以及生成嵌入向量的字符串内容。我还添加了块类型(文本或代码),以便如果用户正在寻找代码片段,他们可以将搜索定制为此目的。

然后我将这些向量一个页面一个页面地添加到索引中:

def add_doc_to_index(subsections, page_url, doc_type, block_type):
    ids = []
    vectors = []
    payloads = []

    for section_anchor, section_content in subsections.items():
        for subsection in section_content:
            id, vector, payload = create_subsection_vector(
                subsection,
                section_anchor,
                page_url,
                doc_type,
                block_type
            )
            ids.append(id)
            vectors.append(vector)
            payloads.append(payload)

    ## Add vectors to collection
    client.upsert(
        collection_name=COLLECTION_NAME,
        points=qmodels.Batch(
            ids = ids,
            vectors=vectors,
            payloads=payloads
        ),
    )

查询索引

一旦创建了索引,可以通过使用相同的嵌入模型对查询文本进行嵌入,并在索引中搜索相似的嵌入向量来执行对索引文档的搜索。使用 Qdrant 向量索引,可以通过 Qdrant 客户端的 search() 命令执行基本查询。

为了使公司的文档可搜索,我希望允许用户按文档的部分以及编码的块类型进行筛选。在向量搜索术语中,筛选结果同时确保返回预定数量的结果(由top_k参数指定)被称为预筛选

为了实现这一点,我编写了一个程序化过滤器:

def _generate_query_filter(query, doc_types, block_types):
    """Generates a filter for the query.
    Args:
        query: A string containing the query.
        doc_types: A list of document types to search.
        block_types: A list of block types to search.
    Returns:
        A filter for the query.
    """
    doc_types = _parse_doc_types(doc_types)
    block_types = _parse_block_types(block_types)

    _filter = models.Filter(
        must=[
            models.Filter(
                should= [
                    models.FieldCondition(
                        key="doc_type",
                        match=models.MatchValue(value=dt),
                    )
                for dt in doc_types
                ],

            ),
            models.Filter(
                should= [
                    models.FieldCondition(
                        key="block_type",
                        match=models.MatchValue(value=bt),
                    )
                for bt in block_types
                ]  
            )
        ]
    )

    return _filter

内部的_parse_doc_types()_parse_block_types()函数处理参数为字符串、列表或 None 的情况。

然后我写了一个query_index()函数,它接受用户的文本查询,进行预筛选,搜索索引,并从负载中提取相关信息。该函数返回一个元组列表,形式为(url, contents, score),其中得分表示结果与查询文本的匹配程度。

def query_index(query, top_k=10, doc_types=None, block_types=None):
    vector = embed_text(query)
    _filter = _generate_query_filter(query, doc_types, block_types)

    results = CLIENT.search(
        collection_name=COLLECTION_NAME,
        query_vector=vector,
        query_filter=_filter,
        limit=top_k,
        with_payload=True,
        search_params=_search_params,
    )

    results = [
        (
            f"{res.payload['url']}#{res.payload['section_anchor']}",
            res.payload["text"],
            res.score,
        )
        for res in results
    ]

    return results

编写搜索包装器

最后一步是为用户提供一个干净的界面,以便在这些“向量化”文档中进行语义搜索。

我编写了一个print_results()函数,它接受查询、query_index()的结果和一个score参数(是否打印相似度分数),并以易于理解的方式打印结果。我使用了rich Python 包来格式化终端中的超链接,以便在支持超链接的终端中,点击超链接将打开默认浏览器中的页面。如果需要,我还使用了webbrowser来自动打开最顶端结果的链接。

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

显示带有丰富超链接的搜索结果。图片由作者提供。

对于基于 Python 的搜索,我创建了一个FiftyOneDocsSearch类来封装文档搜索行为,因此一旦实例化了FiftyOneDocsSearch对象(可能使用默认的搜索参数设置):

from fiftyone.docs_search import FiftyOneDocsSearch
fosearch = FiftyOneDocsSearch(open_url=False, top_k=3, score=True)

你可以通过调用这个对象在 Python 中进行搜索。例如,要查询“如何加载数据集”,只需运行:

fosearch(“How to load a dataset”)

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

在 Python 进程中对公司的文档进行语义搜索。图片由作者提供。

我还使用了argparse来通过命令行提供此文档搜索功能。当包被安装后,可以通过 CLI 搜索文档:

fiftyone-docs-search query "<my-query>" <args 

仅为有趣,因为fiftyone-docs-search query有点繁琐,我在我的.zsrch文件中添加了一个别名:

alias fosearch='fiftyone-docs-search query'

使用这个别名,可以通过命令行搜索文档:

fosearch "<my-query>" args

结论

在开始之前,我已经把自己看作是公司开源 Python 库 FiftyOne 的重度用户。我编写了许多文档,并且每天都在使用(并继续使用)这个库。但将我们的文档转变为可搜索的数据库的过程迫使我以更深刻的方式理解我们的文档。当你为他人构建某些东西时,它也能帮助到你,这总是很棒的!

这是我学到的:

  • Sphinx RST 使用繁琐:它能生成美观的文档,但解析起来有点麻烦

  • 不要在预处理上过度:OpenAI 的 text-embeddings-ada-002 模型非常擅长理解文本字符串背后的意义,即使它有稍微不典型的格式。再也不用进行词干提取和费力地去除停用词及各种字符了。

  • 小的语义有意义的片段最好:将文档分解成尽可能小的有意义的部分,并保留上下文。对于较长的文本,搜索查询与索引中文本部分的重叠更可能被段落中的不相关文本遮掩。如果你将文档拆分得过小,许多索引条目可能包含很少的语义信息。

  • 向量搜索非常强大:只需最少的工作量,不需任何微调,我就能显著提高文档的可搜索性。从初步估计来看,这种改进的文档搜索比旧的关键字搜索方法返回相关结果的可能性提高了两倍以上。此外,这种向量搜索方法的语义特性意味着用户现在可以用任意措辞、任意复杂的查询进行搜索,并且保证得到指定数量的结果。

如果你发现自己(或他人)不断地挖掘或筛选大量文档以寻找特定信息,我鼓励你将这一过程调整到你的用例中。你可以修改它以适用于你的个人文档或公司的档案。如果你这样做,我保证你会从中得到新的启发!

这里有一些方法可以扩展到你自己的文档中!

  • 混合搜索: 将向量搜索与传统的关键字搜索结合起来

  • 全球化:使用Qdrant Cloud在云中存储和查询集合

  • 引入网络数据:使用requests直接从网络下载 HTML

  • 自动更新:使用Github Actions在底层文档更改时触发嵌入的重新计算

  • 嵌入:将其包装在一个 Javascript 元素中,替代传统的搜索框

构建包所用的所有代码都是开源的,可以在voxel51/fiftyone-docs-search仓库中找到。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值