手搓多模态-06 数据预处理
前情回顾

我们目前实现了视觉模型的编码器部分,然而,我们所做的是把一张图片编码嵌入成了许多个上下文相关的嵌入向量,然而我们期望的是一张图片用一个向量来表示,从而与文字的向量做点积形成相似度(参考手搓多模态-01 对比学习的优化)。所以我们需要加一个Linear Projection来将多个嵌入向量投影成一个嵌入向量,但这些可以之后再做,在数据准备阶段我们还有一些数据预处理的工作没有做,本文将着重描述如何进行数据预处理。
数据预处理
我们新建了一个文件,名叫paligemma_processer.py.

首先我们创建了一个类,名叫processer,在processer中我们将实现数据预处理的功能。
所做的预处理主要包括
- 图像的放缩和标准化
- 对输入的图像-文本对解析形成token向量
- 为图像的token创建占位符,这表示在Gemma模型的输入中,有一部分输入向量是来自视觉编码器,那这部分向量暂时就用一个临时的image_token来表示,方便后面替换
- 对于图像的输入,我们需要将输入转换为形如[Batch_size,Channels,Height,Width]的输入,并转换为torch.tensor类型方便pytorch处理。
这部分的代码主要如下:
class PaligemmaProcessor: IMAGE_TOKEN = "<image>"def __init__(
self,
tokenizer,
num_image_tokens,
image_size ):super().__init__()
self.image_seq_lenth = num_image_tokens
self.image_size = image_size token_to_add = {"addtional_special_tokens": [self.IMAGE_TOKEN]} ##标识图像占位符
tokenizer.add_special_tokens(token_to_add)
EXTRA_TOKENS = [f"<loc:{i:04d}>" for i in range(1024)] ##用于目标检测
EXTRA_TOKENS += [f"<seg:<{i:04d}>" for i in range(1024)] ##用于语义分割 tokenizer.add_tokens(EXTRA_TOKENS)
self.image_token_id = tokenizer.convert_tokens_to_ids(self.IMAGE_TOKEN) ##图像占位符的id tokenizer.add_bos_token = False ##去掉默认的bos和eos
tokenizer.add_eos_token = False self.tokenizer = tokenizerdef __call__(
self,
text: List[str],
images: List[Image.Image],
padding: str = "longest",
truncation: bool = True):assert len(text) == 1 and len(images) == 1, "Only support one text and one image for now." pixel_values = process_images(
images,
size=(self.image_size, self.image_size),
resample= Image.Resampling.BICUBIC,
rescale_factor=1.0 / 255.0,
image_mean = IMAGENET_STANDARD_MEAN,
image_std = IMAGENET_STANDARD_STD) pixel_values = np.stack(pixel_values,axis=0)
pixel_values = torch.tensor(pixel_values) ##转换为torch.tensor##通过add_image_token_to_prompt函数,将图像占位符添加到文本中,形成一个输入prompt
input_string = [
add_image_token_to_prompt(
prefix_prompt = prompt,
bos_token = self.tokenizer.bos_token,
image_seq_len = self.image_seq_lenth,
image_token = self.IMAGE_TOKEN)for prompt in text]## 将string类型的prompt转换为嵌入向量,其实这里是根据string中的词元划分成一个token_id的向量## 比如输入的string是"<image><image><bos>hello,world<sep>" 然后tokenizer将其分割成[<image>,<image>,<bos>,hell,oworld,<sep>],这里假设hell和oworld都是token,并查找每个token的id,可能得到[0,0,1,11,15,2]作为嵌入向量
inputs = self.tokenizer(
input_string,
padding=padding,
truncation=truncation,
return_tensors="pt")## 返回待编码的图像以及嵌入好的文本向量
return_data = {"pixel_values": pixel_values, **inputs}return return_data
首先我们为tokenizer 添加了一些额外的特殊token,比如用于目标检测的特殊位置token,以及语义分割用的特殊token,但是我们暂时不会用到它们。
图像处理
在数据预处理部分,我们首先通过一个预处理函数来处理图像,这里面就包括了图像的缩放,归一化和标准化。
pixel_values = process_images(
images,
size=(self.image_size, self.image_size),
resample= Image.Resampling.BICUBIC,
rescale_factor=1.0 / 255.0,
image_mean = IMAGENET_STANDARD_MEAN,
image_std = IMAGENET_STANDARD_STD)
该函数的实现如下:
def process_images(
images: List[Image.Image],
size: Tuple[int, int] = None,
resample: Image.Resampling = None,
rescale_factor: float = None,
image_mean: Optional[Union[float,List[float]]] = None,
image_std: Optional[Union[float,List[float]]] = None
) -> List[np.ndarray]:
height, width = size
images = [
resize(image =image, size = (height,width), resample=resample) if image.size != size else image for image in images]
images = [np.array(image) for image in images]
images = [
rescale(image = image, factor = rescale_factor) for image in images] ## 归一化缩放
images = [
normalize(image = image, mean = image_mean, std = image_std) for image in images] ## 标准化## image : [Height,Width,Channel] --> [Channel,Height,Width]
images = [image.transpose(2,0,1) for image in images]return images
首先我们需要对图像进行缩放,那么这里会涉及到一个重采样函数,表示你用什么方法对图像进行缩放采样,不同的方法采样出来的效果也不一样,这里我们用Image.Resampling.BICUBIC方法,关于这个方法的详细信息请参考其他博客。
缩放操作是在将0-255的像素值缩放到0-1,防止之后模型嵌入时的数值不稳定。
随后的标准化其实就像Batch Normalization和Layer Normalization一样,对图像的像素分布标准化为正态分布,所以需要大量图像的RGB通道各自的均值和方差,故使用image_net的统计结果。
然后把图像三个维度的顺序改变一下。
其中resize,rescale,normalize函数依次如下:
IMAGENET_STANDARD_MEAN = [0.485, 0.456, 0.406]
IMAGENET_STANDARD_STD = [0.229, 0.224, 0.225]def resize(
image: Image.Image,
size: Tuple[int, int],
resample: Image.Resampling = None,
reducing_gap: Optional[int] = None
):
height,width = size
image.resize(size=(width,height),resample=resample,reducing_gap=reducing_gap)return imagedef rescale(
image: np.ndarray,
factor: float,
dtype: np.dtype = np.float32
):
image = image * factor
image = image.astype(dtype)return imagedef normalize(
image: np.ndarray,
mean: Optional[Union[float,List[float]]] = None,
std: Optional[Union[float,List[float]]] = None
) -> np.ndarray:if mean is None:
mean = IMAGENET_STANDARD_MEANif std is None:
std = IMAGENET_STANDARD_STD
mean = np.array(mean)
std = np.array(std)
image = (image - mean) / stdreturn image
然后我们还需要将输出的图像信息转换为torch.tensor,这里的转换代码如下:
pixel_values = np.stack(pixel_values,axis=0)
pixel_values = torch.tensor(pixel_values) ##转换为torch.tensor
首先用stack方法把一个numpy tensor list转换为一个大tensor,然后再把numpy的tensor转换为torch的tensor.
token 处理
token的处理主要在这一部分
##通过add_image_token_to_prompt函数,将图像占位符添加到文本中,形成一个输入prompt
input_string = [add_image_token_to_prompt(
prefix_prompt = prompt,
bos_token = self.tokenizer.bos_token,
image_seq_len = self.image_seq_lenth,
image_token = self.IMAGE_TOKEN)for prompt in text]## 将string类型的prompt转换为嵌入向量,其实这里是根据string中的词元划分成一个token_id的向量## 比如输入的string是"<image><image><bos>hello,world<sep>" 然后tokenizer将其分割成[<image>,<image>,<bos>,hell,oworld,<sep>],这里假设hell和oworld都是token,并查找每个token的id,可能得到[0,0,1,11,15,2]作为嵌入向量
inputs = self.tokenizer(
input_string,
padding=padding,
truncation=truncation,
return_tensors="pt")## 返回待编码的图像以及嵌入好的文本向量
return_data = {"pixel_values": pixel_values, **inputs}
首先我们会构造输入的string文本,这个构造主要依照论文的内容:

论文构造的输入首先是image token占位符,然后是一个bos表示文本信息的开始,然后是text input,然后是[sep],而论文用'\n'表示[sep]:

于是我们的add_image_token_to_prompt如下所示
def add_image_token_to_prompt(
prefix_prompt: str,
bos_token: str,
image_seq_len: int,
image_token: str
):##论文使用换行符当做[sep]token,但是可能会出现\n与前面的token重叠导致语义信息丢失。 return f"{image_token * image_seq_len}{bos_token}{prefix_prompt}\n"
但这里可能会出现一个问题,即论文使用换行符当做[sep]token,但是可能会出现\n与前面的token重叠导致语义信息丢失。但这里我们暂时不管这个问题。
随后tokenizer的调用实际上会把输入的string根据词汇表拆分成一个个token对应的id,而我们预选添加了image_token的占位符,也就是说我们构建的string_input可能类似于"<image><image><bos>hello,world<sep>",而tokenizer会根据词汇表划分这些token为一个list[<image>,<image>,<bos>,"hell,","oworld",<sep>],然后再通过查表的方式将这些token转换为数值id,比如查到我们加入的<image>对应的id是0,<bos>是1, <sep>是2,"hell,"是11,"oworld"是15,(注意,这里假设词汇表的"hell,"是一个token,"oworld"也是一个token)那么输出可能形如[0,0,1,11,15,2]。
## 将string类型的prompt转换为嵌入向量,其实这里是根据string中的词元划分成一个token_id的向量
## 比如输入的string是"<image><image><bos>hello,world<sep>" 然后tokenizer将其分割成[<image>,<image>,<bos>,hell,oworld,<sep>],这里假设hell和oworld都是token,并查找每个token的id,可能得到[0,0,1,11,15,2]作为嵌入向量
inputs = self.tokenizer(
input_string,
padding=padding,
truncation=truncation,
return_tensors="pt"
)
## 返回待编码的图像以及嵌入好的文本向量
return_data = {"pixel_values": pixel_values, **inputs}
return return_data
相关文章:
手搓多模态-06 数据预处理
前情回顾 我们目前实现了视觉模型的编码器部分,然而,我们所做的是把一张图片编码嵌入成了许多个上下文相关的嵌入向量,然而我们期望的是一张图片用一个向量来表示,从而与文字的向量做点积形成相似度(参考手搓多模态-01…...
HCIP【路由过滤技术(详解)】
目录 1 简介 2 路由过滤方法 3 路由过滤工具 3.1 静默接口 3.2 ACL 3.3 地址前缀列表 3.4 filter-policy 3.4.1 filter-policy过滤接收路由(以RIP为例) 3.4.2 filter-policy过滤接收路由(以OSPF为例) 1 简介 路由过滤技术…...
【Kafka基础】topics命令行操作大全:高级命令解析(2)
1 强制删除主题 /export/home/kafka_zk/kafka_2.13-2.7.1/bin/kafka-topics.sh --delete \--zookeeper 192.168.10.33:2181 \--topic mytopic \--if-exists 参数说明: --zookeeper:直接连接Zookeeper删除(旧版本方式)--if-exists&…...
【AI插件开发】Notepad++ AI插件开发实践(代码篇):从Dock窗口集成到功能菜单实现
一、引言 上篇文章已经在Notepad的插件开发中集成了选中即问AI的功能,这一篇文章将在此基础上进一步集成,支持AI对话窗口以及常见的代码功能菜单: 显示AI的Dock窗口,可以用自然语言向 AI 提问或要求执行任务选中代码后使用&…...
Vue3在ZKmall开源商城前端的应用实践与技术创新
ZKmall开源商城作为一款企业级电商解决方案,其前端架构基于Vue3实现了高效、灵活的开发模式,结合响应式设计、组件化开发与全链路性能优化,为多端协同和复杂业务场景提供了先进的技术支持。以下从技术架构、核心特性、性能优化等维度解析Vue3…...
SpringAI+MCP协议 实战
文章目录 前言快速实战Spring AISpring AI 集成 MCP 协议Spring Mcp Client 示例Spring Mcp Server 示例 前言 尽管Python最近成为了编程语言的首选,但是Java在人工智能领域的地位同样不可撼动,得益于强大的Spring框架。随着人工智能技术的快速发展&…...
[数据结构]图krusakl算法实现
目录 Kruskal算法 Kruskal算法 我们要在连通图中去找生成树 连通图:在无向图中,若从顶点v1到顶点v2有路径,则称顶点v1与顶点v2是连通的。如果图中任意一对顶点都是连通的,则称此图为连通图。 生成树:一个连通图的最小…...
SQL122 删除索引
alter table examination_info drop index uniq_idx_exam_id; alter table examination_info drop index full_idx_tag; 描述 请删除examination_info表上的唯一索引uniq_idx_exam_id和全文索引full_idx_tag。 后台会通过 SHOW INDEX FROM examination_info 来对比输出结果。…...
QEMU学习之路(5)— 从0到1构建Linux系统镜像
QEMU学习之路(5)— 从0到1构建Linux系统镜像 一、前言 参考:从内核到可启动镜像:0到1构建你的极简Linux系统 二、linux源码获取 安装编译依赖 sudo apt install -y build-essential libncurses-dev flex bison libssl-dev li…...
node ---- 解决错误【Error: error:0308010C:digital envelope routines::unsupported】
1. 报错 在 Node.js 18.18.0 的版本中,遇到以下错误: this[kHandle] new _Hash(algorithm, xofLen);^ Error: error:0308010C:digital envelope routines::unsupported这个错误通常发生在运行项目或构建时,尤其是在使用 Webpack、Vite 或其他…...
蓝桥杯——走迷宫问题(BFS)
这是一个经典的BFS算法 1. BFS算法保证最短路径 核心机制:广度优先搜索按层遍历所有可能的路径,首次到达终点的路径长度即为最短步数。这是BFS的核心优势。队列的作用:通过队列按先进先出的顺序处理节点,确保每一步探索的都是当…...
详解 Redis repl_backlog_buffer(如何判断增量同步)
一、repl_backlog_buffer 复制积压缓冲区(Replication Backlog Buffer) 是一个环形内存区域(Ring Buffer),用于临时保存主节点最近写入的写命令,以支持从节点断线重连后的增量同步。 1.1 三个复制偏移量 …...
服务器虚拟化技术深度解析:医药流通行业IT架构优化指南
一、服务器虚拟化的定义与原理 (一)技术定义:从物理到虚拟的资源重构 服务器虚拟化是通过软件层(Hypervisor)将物理服务器的CPU、内存、存储、网络等硬件资源抽象为逻辑资源池,分割成多个相互隔离的虚拟机…...
使用PyTorch实现ResNet:从残差块到完整模型训练
ResNet(残差网络)是深度学习中的经典模型,通过引入残差连接解决了深层网络训练中的梯度消失问题。本文将从残差块的定义开始,逐步实现一个ResNet模型,并在Fashion MNIST数据集上进行训练和测试。 1. 残差块(…...
Scala相关知识学习总结5
1、多维数组 定义: val arr Array.ofDim[Double](3,4) 表示二维数组中有三个一维数组,每个一维数组有四个元素。 2、列表 List 不可变 List:默认不可变,可创建有序且可重复的列表,可使用:从右向左增加数据…...
Day1:前端项目uni-app壁纸实战
uni-app官网下载HBuilder。 uni-app快速上手 | uni-app官网 点击HBuilder 安装 新建项目 工具——插件安装 安装uni-app(vue3) 我们先来准备一下: 先在wallpaper下新建目录 我已经建过了 同样,再在common下建images和style目录&…...
光谱相机的光谱数据采集原理
光谱相机的光谱数据采集原理基于分光技术和光电信号转换,通过将入射光按波长分解并记录各波段的强度信息,最终生成包含空间和光谱维度的数据立方体。以下是详细原理分解: 1. 分光技术:将复合光分解为单色光 光谱相机…...
《算法笔记》10.3小节——图算法专题->图的遍历 问题 A: 第一题
题目描述 该题的目的是要你统计图的连通分支数。 输入 每个输入文件包含若干行,每行两个整数i,j,表示节点i和j之间存在一条边。 输出 输出每个图的联通分支数。 样例输入 1 4 4 3 5 5样例输出 2 分析: 由于题目没给出范围࿰…...
python中的{}
注意,如果要创建空集合,只能使用 set() 函数实现。因为直接使用一对 {},Python 解释器会将其视为一个空字典。 Python中集合set和字典dict的用法区别_python创建set变量和dict区别-CSDN博客...
宏碁笔记本电脑擎7PRO搭载的 NVIDIA RTX 5080 显卡安装pytorch
宏碁笔记本电脑擎7PRO搭载的 NVIDIA RTX 5080 显卡是一款高性能移动 GPU,基于 NVIDIA 最新的 Blackwell 架构设计,通过修正架构(Blackwell)、显存类型与带宽(GDDR7、960GB/s)、Tensor Core 与 RT Core 全面…...
html+css+js 实现一个贪吃蛇小游戏
目录 游戏简介 游戏功能与特点 如何玩转贪吃蛇 游戏设计与实现 HTML结构 JavaScript核心实现 代码结构: 效果 关于“其他游戏” 游戏简介 贪吃蛇是一款经典的单人小游戏,玩家通过控制蛇的移动,吃掉食物来增加长度,避免撞…...
淘宝按图搜索商品(拍立淘)API接口解析
以下是关于淘宝按图搜索商品(拍立淘)API的深度解析指南,结合官方文档和开发者经验整理,包含调用方法、参数详解、返回结果解析及常见问题处理: 一、API核心接口说明 1. 接口名称 官方接口:taobao.image.…...
Python爬虫生成CSV文件的完整流程
引言 在当今数据驱动的时代,网络爬虫已成为获取互联网数据的重要工具。Python凭借其丰富的库生态系统和简洁的语法,成为了爬虫开发的首选语言。本文将详细介绍使用Python爬虫从网页抓取数据并生成CSV文件的完整流程,包括环境准备、网页请求、…...
21.OpenCV获取图像轮廓信息
OpenCV获取图像轮廓信息 在计算机视觉领域,识别和分析图像中的对象形状是一项基本任务。OpenCV 库提供了一个强大的工具——轮廓检测(Contour Detection),它能够帮助我们精确地定位对象的边界。这篇博文将带你入门 OpenCV 的轮廓…...
医学图像分割效率大幅提升!U-Net架构升级,助力精度提升5%!
在医学图像分割领域,U-Net模型及其变体的创新应用正在带来显著的性能提升和效率优化。最新研究显示,通过引入结构化状态空间模型(SSM)和轻量级LSTM(xLSTM)等技术,VMAXL-UNet模型在多个医学图像数…...
智能设备运行监控系统
在工业 4.0 与智能制造浪潮下,设备运行效率与稳定性成为企业竞争力的核心要素。然而,传统设备管理模式面临数据采集分散、状态分析滞后、维护成本高昂等痛点。为破解这些难题,设备运行监控系统应运而生,通过融合智能传感、5G 通信…...
详细分析单例模式
目录 1.单例模式的定义 2.单例模式的实现方式 1.饿汉模式 2.懒汉模式 (1)线程不安全的问题怎么解决? (2)直接对整个getInstance方法代码块加锁吗? (3)那对if语句加锁不就行了吗…...
Windwos的DNS解析命令nslookup
nslookup 解析dns的命令 有两种使用方式,交互式&命令行方式。 交互式 C:\Users\Administrator>nslookup 默认服务器: UnKnown Address: fe80::52f7:edff:fe28:35de> www.baidu.com 服务器: UnKnown Address: fe80::52f7:edff:fe28:35de非权威应答:…...
服务器报错:xxx/libc.so.6: version `GLIBC_2.32‘ not found
/lib/x86_64-linux-gnu/libc.so.6: version GLIBC_2.32 not found (required by ./aima-sim-app-main) 解决思路 根据错误信息,您的应用程序 aima-sim-app-main 和 libmujoco.so.3.1.6 库依赖于较新的 GNU C Library (glibc) 版本(如 GLIBC_2.32, GLIBC…...
Flutter之页面布局一
目录: 1、页面布局一2、无状态组件StatelessWidget和有状态组件StatefulWidget2.1、无状态组件示例2.2、有状态组件示例2.3、在 widget 之间共享状态1、使用 widget 构造函数2、使用 InheritedWidget3、使用回调 3、布局小组件3.1、布置单个 Widget3.2、容器3.3、垂…...
