人工智能和机器学习之线性代数(三)
本文人工智能和机器学习之线性代数(三)将构建一个图像搜索引擎,将介绍基本原理和构建过程所用到的工具和技术。
文章目录
- 人工智能和机器学习之线性代数(三)
- 神经网络简介
- 最初的神经网络
- 卷积神经网络
- GPT
- CLIP – Contrastive Language-Image Pretraining
- Imagenette – mini-ImageNet
- Vector Database 矢量数据库
- Similarity Search 相似性搜素
- Cosine Similarity 余弦相似度
- 总结
神经网络简介
神经网络曾经被视为对人脑的拙劣模仿,但在 2012 年,多伦多大学的研究人员构建了一种神经网络架构,该架构能够产生比 ImageNet
挑战赛的所有其他非神经网络解决方案更好的结果。那是深度学习革命的开始,这场革命发展并演变成 2020 年代初的生成式 AI 技术浪潮。
神经网络由神经元和它们之间的连接组成,它们有多种排列方式,也就是架构。以下是您可能听说过的三种架构:(最初版本vanilla
)神经网络、卷积神经网络 (CNN) 和 GPT-1(GPT-4 和 ChatGPT 的前身)。
最初的神经网络
在上图中,浅灰色的连接具有正权重,而紫红色的连接具有负权重。并非所有的神经元对之间都有连接,但对于那些有连接的神经元,权重指定了连接的强度。当神经网络架构首次在计算机程序中初始化时,其神经元按照架构设计指定的方式排列,但其权重通常是随机初始化或从正态分布中提取的。在此初始状态下,神经网络无法产生任何有用的输出,因此它必须经历一个称为训练的过程。完成训练后,将建立权重,以便神经网络能够生成有用的输出。事实上,有无限多组权重,所以只需要在训练后得出一组就可以了。
神经网络接收一个输入,对该输入执行一堆数学计算,并生成一个输出。然后,训练过程将神经网络的输出与预期输出(即基本实况)进行比较。然后,计算真实值和输出之间的差异(即误差)用于通知神经网络调整其权重,以便下次看到此输入时,它将生成更接近真实值的输出。
换句话说,在训练过程中,权重会根据它们与误差的关系一点一点地调整,最终,训练会强制整个神经元和连接网络能够产生有用的输出。误差如何导致权重调整的复杂性称为反向传播,将在以后的神经网络文章中介绍,但现在,只需知道训练神经网络有点类似于训练具有许多测验问题的学生并标记错误的答案,以便学生下次有望正确回答问题就足够了。
卷积神经网络
GPT
CLIP – Contrastive Language-Image Pretraining
CLIP 代表对比语言-图像预训练,是 OpenAI 于 2021 年发布的一种特定的神经网络架构。这种架构允许神经网络处理文本(又名语言)和图像(又名视觉)。因为它可以处理两种不同的模态,所以我们称之为语言和视觉多模态模型。下面是 OpenAI 的一张图表,显示了 CLIP 是如何训练的,具体参考Learning Transferable Visual Models From Natural Language Supervision。
如上所述,仅了解神经网络架构不足以使模型有用。我们必须拥有一套这样一组“权重”。不幸的是,对于业余爱好者、从业者和资源匮乏的机构来说,如今对于超大型神经网络来说,从头开始训练神经网络以获得一组好的、有用的权重可能非常昂贵。近年来,最先进模型中的神经元和连接数量呈指数级增长,因此最大的模型包含数十亿或数万亿的权重。在数十亿个数据点上进行训练时,调整所有这些权重所需的计算能力可能要花费数百百万或数十亿美元。幸运的是,一些 AI 研究实验室已经承担了训练此类模型的成本,并已向公众发布权重。预加载有用权重的神经网络称为预训练模型。
首先,我们将下载 CLIP 模型的一组权重。
model = CLIPModel.from_pretrained("openai/clip-vit-base-patch32")
回顾一下,CLIP 是一种语言和视觉多模态模型,这意味着它接受文本和图像输入。但是,在为其提供任何文本或图像输入之前,必须使用处理器为模型准备输入。
processor = CLIPProcessor.from_pretrained("openai/clip-vit-base-patch32")
CLIPProcessor
实际上是图像处理器和文本处理器的包装类。
- 如果为 CLIP 提供文本输入,则分词器会将输入文本转换为模型可以理解的向量。
- 如果为 CLIP 提供图像输入,则图像处理器会调整图像像素值的大小、裁剪和调整,以便它们适合模型。
尝试一下 CLIP 模型:
import requests
url = "http://images.cocodataset.org/val2017/000000039769.jpg"
image = Image.open(requests.get(url, stream=True).raw)
inputs = processor(images=image, return_tensors="pt")
image_features = model.get_image_features(**inputs)
image_features.shape
//torch.Size([1, 512])
在上面的代码中示例,下载包含两只猫的图像。然后,通过将图像馈送到 CLIP 中来处理图像,并将输出embeddings
存储在 image_features 中。
在上面,向 CLIP 提供 224x224 像素的图像作为输入,其输出是 512 维embeddings
(向量)。embeddings
本身是无用的,但是与加载了相同权重集的同一 CLIP 模型输出的其他 512 维embeddings
相比,embeddings
开始具有意义。
Imagenette – mini-ImageNet
ImageNet 是一个包含 1400 多万张图像的可视化数据库,其中包含 20000 多种对象,例如太阳镜、摇篮和猫鼬。虽然可以尝试在这 1400 万张图像中构建一个图像搜索引擎,但出于教学目的,将使用一个名为 Imagenette 的 ImageNet 子集,这是一个数据集,仅包含 ImageNet 的 20000 个类别中的 10 个。
$ tree -L 2 imagenette2-160
imagenette2-160
├── noisy_imagenette.csv
├── train
│ ├── n01440764
│ ├── n02102040
│ ├── n02979186
│ ├── n03000684
│ ├── n03028079
│ ├── n03394916
│ ├── n03417042
│ ├── n03425413
│ ├── n03445777
│ └── n03888257
└── val├── n01440764├── n02102040├── n02979186├── n03000684├── n03028079├── n03394916├── n03417042├── n03425413├── n03445777└── n0388825723 directories, 1 file
Imagenette数据集由 train 和 val 目录组成,它们代表训练和验证数据集,但我们将只关注位于 train 中的图像。在 train 中,有 10 个子目录,每个子目录都包含一个图像类别。后续编写代码将为训练数据集中的每个图像生成一个 CLIP embeddings
(向量)。
Vector Database 矢量数据库
现在我们已经有了图像的embeddings
(向量),我们需要将它们存储在矢量数据库中。与标准关系数据库相比,将embeddings
存储在 vector 数据库中有很多好处:
- 高效的相似性搜索:矢量数据库针对高维矢量数据进行了优化,可以执行快速的相似性搜索。这对于基于视觉相似性的图像检索至关重要。
- 可扩展性:矢量数据库旨在处理大量高维数据,使其更适合存储和查询数百万或数十亿个图像
embeddings
(向量)。 - 专门的索引:矢量数据库使用为矢量数据量身定制的专门的索引结构,与关系数据库中使用的传统索引方法相比,可以更快地检索。
- 对向量运算的原生支持:向量数据库为向量运算提供内置函数,如余弦相似度、欧几里得距离和点积,这些函数对于比较图像
embeddings
(向量)至关重要。
AI/ML 和embeddings
(向量)的兴起导致了矢量数据库服务的增加。Pinecone
是最著名的之一,在本文中重点介绍它,因为它们提供免费的套餐。
首先,创建一个 Pinecone
索引,参见官方教程。
现在有了一个索引,可以将向量上传到它。只需调用 upsert 方法。
index.upsert(vectors=[{"id": "/path/to/img1", "values": [-0.12, 0.05, -0.23, 0.18, -0.07, 0.31]},{"id": "/path/to/img2", "values": [0.42, -0.19, 0.27, -0.35, 0.11, -0.28]},{"id": "/path/to/img3", "values": [-0.51, 0.63, -0.17, 0.45, -0.38, 0.22]},{"id": "/path/to/img4", "values": [0.78, -0.41, 0.56, -0.72, 0.89, -0.25]}]
)
在上面的示例中,已经为每个图像生成了 CLIP embeddings
(向量),以便可以将它们上传到矢量数据库。
Similarity Search 相似性搜素
现在我们已经将图像嵌入上传到矢量数据库,剩下的工作就是将用户查询图像与存储在数据库中的图像进行比较,并查找并返回最相似的图像。
现在,我们有一个充满embeddings
(向量)的向量数据库,使用点积
来比较查询图像的embeddings
(向量)。虽然使用暴力 for 循环可以完成这项工作,但它不会扩展,因为向量数据库包含越来越多的embeddings
(向量)(例如 10 亿个)。正如上面所读到的,现代向量数据库使用更高效的算法在数百万亿个向量中大规模运行点积。最大的开源贡献之一来自 Facebook 在 2017 年,当时他们发布了 Faiss
,即是 Facebook AI 相似性搜索。该软件实现了对 10 亿个高维向量的首次相似性搜索,这为向量数据库的兴起做出了贡献。Pinecone
通过索引查询函数为我们提供了这个强大的功能:
query_vector = [0.23, -0.21, 0.36, -0.92, 0.19, -0.23]
index.query(namespace="my-namespace",vector=query_vector,top_k=3,include_values=True
)
在上面的函数调用中,假设向量数据库包含许多相同维度的embeddings
(向量),则查询函数将要求 Pinecone
搜索与query_vector
最相似的三个嵌入(top_k 参数指定要返回的embeddings
数量)。在后台,Pinecone
正在使用 Faiss
或其某些变体以及其他优化,以便可以使用点积以高效的方式将 query_vector
与许多其他embeddings
(向量)进行比较。
Cosine Similarity 余弦相似度
两个embeddings
(向量)之间的点积是可以用来衡量相似性的一个度量。但还有其他相似性指标。让我们了解余弦相似性。之前了解了点积的余弦公式。
两个向量之间的余弦相似度,这个数字是相似性的度量 – 它确定两个向量是否指向大致相同的方向。
当不想在相似度计算中包含向量大小时,余弦相似度通常用作两个embeddings
(向量)之间语义相似性的度量。
总结
本文阐述了如何构建图像搜索引擎。结合之前 人工智能和机器学习之线性代数(一) 和人工智能和机器学习之线性代数(二)系列以及本文,我们已经学习了线性代数和 PyTorch 的最基本的基础知识,并将这些新知识应用于构建有用的文本或图像搜索引擎。