前端也可以这样零基础入门Pinecone四

假如你和我一样在准备24年的春招,在前端全栈外,再准备一些AI的内容是非常有必要的。24年是AI红利年,AIGC+各种岗位大厂机会会多些,同意的请点赞。也欢迎朋友们加我微信shunwuyu, 一起交流。

前言

在前面的文章中,我们利用Pinecone向量数据库实现了自然语义搜索、RAG、推荐系统,本文一起来看下如何实现时尚产品混合搜索。当我们提出"dark blue french connection jeans for men"问题时,它会返回我们数据集里的相关裤子图片。文生图,喔, 特6。

安装依赖

yaml 复制代码
# HuggingFace 数据集, 
!pip install datasets 
# pinecone 向量数据库 
!pip install pinecone-client
# 命令行工具 
!pip install tqdm
# LLM
!pip install openai
# pinecone的文本工具库,用于创建稀疏向量
!pip install pinecone-text

数据集

我们将使用Hugging Face的ashraq/fashion-product-images-small,这个数据集中包含时尚品的数据和图片。

下载数据集,查看数据

ini 复制代码
# HuggingFace 的datasets 加载预定义的数据集
from datasets import load_dataset

fashion = load_dataset(
    # 数据集名称
    "ashraq/fashion-product-images-small",
    # 数据集会分这不同的子集,如训练集train、验证集validation和测试集test,用于不同的阶段
    # split train 希望加载的是预训练部分
    split="train"
)
fashion

从上图可以看在到,有44072行数据,最后一列image是图片,其余为相应文本字段。本文我们要挑战的就是如何用文本搜索图片。

查看图片

ini 复制代码
# 取出所有的图片列
images = fashion['image']
# 移除image列后,就是文本列,我们交给了metadata
metadata = fashion.remove_columns('image')
# 显示其中一张图片,显示如下 
images[900]

查看metada数据

ini 复制代码
# Hugging Face 的数据集调用to_pandas方法生成pandas的DataFrame
metadata = metadata.to_pandas()
# 显示前五条
metadata.head()

在之前学习到的Sematic Search, productDisplayName列的内容和用户的搜索相似性,我们很熟悉。productDisplayName列与image列也有属于同一行的关联, 等下会怎么做呢? 一起期待。

使用BM25模型创建稀疏向量

以下两个概念比较复杂,我们先来玩下:

  • 稀疏向量

指大部分元素为零的向量,应用在自然语言处理、推荐系统等领域。每个文档表示为一个稀疏向量,其中非零元素对应文档中出现过的词汇及其权重。可以节省存储空间和计算资源。

  • BM25

BM25是一种用于信息检索的排名算法,它基于词频(TF)、逆文档频率(IDF)以及其他统计特性来估计一个文档对于给定查询的相关性。

当我们将稀疏向量与BM25结合,比如在构建搜索引擎时,先用稀疏向量表示文档集合,然后利用MB25算法对这些稀疏向量进行打分,从而高效地找出与用户查询相关的文档。

我们会去安装一个pinecone叫做pinecone-text的库,用于创建稀疏向量。

arduino 复制代码
!pip install pinecone-text

我们对数据进行BM25编码

ini 复制代码
#从pinecone_text文本工具库的稀疏模块中引入BM25Encoder 编码工具
from pinecone_text.sparse import BM25Encoder
# 实例化bm25实例
bm25 = BM25Encoder()
# 训练一下,是为了让 BM25Encoder 对 `metadata` 数据集中 'productDisplayName' 列中的文本数据进行学习,计算文档中每个词的 IDF(逆文档频率)和其他相关统计量。
bm25.fit(metadata['productDisplayName'])
metadata['productDisplayName'][0]
  • 对查询文本或文档进行编码
css 复制代码
# 对查询字符串进行编码
bm25.encode_queries(metadata['productDisplayName'][0])
# 对文档进行编码
bm25.encode_documents(metadata['productDisplayName'][0])

bm25 就是使用encode_queries和encode_documents两者结合,实现了从海量文档中快速找到与查询语句最匹配的内容。

使用CLIP创建密集向量

  • 先安装编码库
yaml 复制代码
# HuggingFace 文本编码的库
!pip install sentence-transformers

进行编码, 相比于bm25, 前者是稀疏向量,后者是密集向量

ini 复制代码
# 从HuggingFace的文本编码库SentenceTransformer库中引入sentence-transformers/clip-ViT-B-32  来自openai开源的clip
# 设备是否支持gpu计算
# 当下流行NLP框架
from torch import torch
device = 'cuda' if torch.cuda.is_available() else 'cpu'
model = SentenceTransformer('sentence-transformers/clip-ViT-B-32', 
# 是否调用了gpu? 
    device=device)
model
# 也对productDisplayName 列进行clip编码
dense_vec = model.encode([metadata['productDisplayName'][0]])
# 返回向量维度
dense_vec.shape

下载了sentence-transformers/clip-ViT-B-32模型,在实例化时,传入了device参数(cuda or not)。对productDisplayName列的第 一个值进行编码,并编出向量的维度为512。

实例化Pinecone

看我们在Pinecone里是怎么来存稀疏和密集两种向量的。

ini 复制代码
import torch
# 是否支持显卡
device = 'cuda' if torch.cuda.is_available() else 'cpu'
print(device)

INDEX_NAME = 'dl-ai'

pinecone = Pinecone(api_key='填入你自己的key')
# 如果存在删除
if INDEX_NAME in [index.name for index in pinecone.list_indexes()]:
  pinecone.delete_index(INDEX_NAME)
# 创建index 
pinecone.create_index(
  INDEX_NAME,
  # clip 密集向量是512维度的 
  dimension=512,
  # 相似度计算不是cosine 是dotproduct   点积运算, 
  # cosine 是两个向量的夹角, 不考虑向量的长度
  # dotproduct 在高维相似度中更适合,不仅考虑方向(夹角),也考虑长度
  metric="dotproduct",
  spec=ServerlessSpec(cloud='aws', region='us-west-2')
)
index = pinecone.Index(INDEX_NAME)

进行两种编码并存入pinecone

ini 复制代码
from tqdm.auto import tqd
# 每批encode处理100个
batch_size = 100
# 总计1000个
fashion_data_num = 1000
# tqdm进度条显示 第一个参数使用range创建了一个可迭代对象
# min 取fashion数据集与1000 之间的较小者,
#batch_size 每次处理base_size个
for i in tqdm(range(0, min(fashion_data_num,len(fashion)), batch_size)):
    # find end of batch   i会自增的 怕最后一页不到batch_size
    i_end = min(i+batch_size, len(fashion))
    # extract metadata batch 把相应部分拿出来
    meta_batch = metadata.iloc[i:i_end]
    # 将dataframe转换为字典,orient="records"意思是按每一行记录转成字典
    meta_dict = meta_batch.to_dict(orient="records")
    # concatinate all metadata field except for id and year to form a single string
    # meta_batch.columns.isin(['id', 'year'])的意思是meta_batch列表里取id  year这两列
    #~按位取反操作符  那么就是除id year 两列外其余列都要
    # join 将这些列用空格连起来
    # 这样所有的文本列都在一起了, 做embedding 包含这行的所有语义
    meta_batch = [" ".join(x) for x in meta_batch.loc[:, ~meta_batch.columns.isin(['id', 'year'])].values.tolist()]
    # extract image batch  将这一批的图像列拿出来
    img_batch = images[i:i_end]
    # create sparse BM25 vectors   对文本内容做稀疏向量编码
    sparse_embeds = bm25.encode_documents([text for text in meta_batch])
    # create dense vectors
    # clip 密集模型用于图片的编码
    dense_embeds = model.encode(img_batch).tolist()
    # create unique IDs
    # 列表推导式 将从i  到 i_end 的数字i 拼到一起 代表这段数据也是唯一的
    ids = [str(x) for x in range(i, i_end)]

    upserts = []
    # loop through the data and create dictionaries for uploading documents to pinecone index
    # 将ids,sparse_embeds, .... 等元素用zip打包成一个元组。
    for _id, sparse, dense, meta in zip(ids, sparse_embeds, dense_embeds, meta_dict):
        # 添加元素  
        upserts.append({
            'id': _id,
            # 这个之前的没有, sparse_values存的稀疏向量 
            'sparse_values': sparse,
            # values 值存的是图片编码
            'values': dense,
            # 媒体数据用的是meta_dict 本身的json 
            'metadata': meta
        })
    # upload the documents to the new hybrid index
    index.upsert(upserts)

# show index description after uploading the documents
index.describe_index_stats()

花了2:30秒, 一是数据量比较大,二是稀疏向量和密集向量两种编码,肯定更耗时。tqdm 帮我们打理好了进度信息展示。

  • 进行查询
ini 复制代码
# 我们想查找深蓝色法国连体男士牛仔裤
query = "dark blue french connection jeans for men"
# 对查询文本做稀疏编码
sparse = bm25.encode_queries(query)
# 对查询文本再做密集编码
dense = model.encode(query).tolist()
# 返回相似的14条
# pinecone index支持 稀疏、密集向量同时查询
result = index.query(
    top_k=14,
    vector=dense,
    sparse_vector=sparse,
    include_metadata=True
)
#  在输出中拿出images
imgs = [images[int(r["id"])] for r in result["matches"]]
imgs

封装图片函数并显示出来

ini 复制代码
#ipython的display模块中以html的方式显示  
from IPython.core.display import HTML
# 二进制流文件
from io import BytesIO
# 用于将图片转为base64
from base64 import b64encode

# function to display product images
def display_result(image_batch):
    figures = []
    for img in image_batch:
        # 二进制实例
        b = BytesIO()
        # 以png的格式存放
        img.save(b, format='png')
        figures.append(f'''
            <figure style="margin: 5px !important;">
              <img src="data:image/png;base64,{b64encode(b.getvalue()).decode('utf-8')}" style="width: 90px; height: 120px" >
            </figure>
        ''')
    return HTML(data=f'''
        <div style="display: flex; flex-flow: row wrap; text-align: center;">
        {''.join(figures)}
        </div>
    ''')

总结

  • 当我们在index.query()方法中除入传入vector外, 还传入了sparse_vector, 我们完成了一次混合向量查询
  • 图片由二进制表达,更适合密集向量

参考资料

相关推荐
workflower3 分钟前
AI+自动驾驶
人工智能·机器学习·自动驾驶
爱技术的小伙子13 分钟前
【ChatGPT】 让ChatGPT模拟客户服务对话与应答策略
人工智能·chatgpt
OptimaAI40 分钟前
【 LLM论文日更|检索增强:大型语言模型是强大的零样本检索器 】
人工智能·深度学习·语言模型·自然语言处理·nlp
谢眠1 小时前
机器学习day4-朴素贝叶斯分类和决策树
人工智能·机器学习
HelpHelp同学1 小时前
教育机构内部知识库:教学资源的集中管理与优化
人工智能·知识库软件·搭建知识库·知识管理工具
深度学习lover1 小时前
<项目代码>YOLOv8 番茄识别<目标检测>
人工智能·python·yolo·目标检测·计算机视觉·番茄识别
IT古董1 小时前
【机器学习】机器学习中用到的高等数学知识-1.线性代数 (Linear Algebra)
人工智能·python·线性代数·机器学习
飞腾开发者1 小时前
飞腾平台Arm NN软件栈安装使用指南
linux·运维·人工智能·机器学习·计算机视觉
Watermelo6172 小时前
通过MongoDB Atlas 实现语义搜索与 RAG——迈向AI的搜索机制
人工智能·深度学习·神经网络·mongodb·机器学习·自然语言处理·数据挖掘
AI算法-图哥2 小时前
pytorch量化训练
人工智能·pytorch·深度学习·文生图·模型压缩·量化