当前位置: 首页 > 后端技术 > Node.js

图片查图系统工程实践

时间:2023-04-03 18:54:43 Node.js

地图查图系统工程实践之前写过一个概述:地图系统查图概述。图像搜索系统需要解决的主要问题是:提取图像特征向量(用特征向量表示一幅图像)对应的特征向量相似度计算的工程实践(寻找内容相似的图像),具体为:卷积神经网络网络CNN提取图像特征向量搜索引擎MilvusCNN使用卷积神经网络CNN提取图像特征是主流方案,具体模型可以使用VGG16,技术实现采用Keras+TensorFlow。参考Keras官方例子:fromkeras.applications.vgg16importVGG16fromkeras.preprocessingimportimagefromkeras.applications.vgg16importpreprocess_inputimportnumpyasnpmodel=VGG16(weights='imagenet',include_top=False)img_path='elephant.jpg'img_path=图像,imgload。target_size=(224,224))x=image.img_to_array(img)x=np.expand_dims(x,axis=0)x=preprocess_input(x)features=model.predict(x)这里提取的特征就是特征向量.1.归一化为了方便后续操作,我们经常对特征进行归一化:fromnumpyimportlinalgasLNorm_feat=feat[0]/LA.norm(feat[0])后续实际使用的也是归一化Normalizednorm_feat。2.图片说明这里加载的图片使用的是keras.preprocessing的image.load_img方法,即:fromkeras.preprocessingimportimageimg_path='elephant.jpg'img=image.load_img(img_path,target_size=(224,224))实际上它是Keras调用的TensorFlow方法。详见TensorFlow官方文档,最终的图像对象其实是一个PILImage实例(TensorFlow使用的PIL)。3、字节转换在实际项目中,图片内容往往是通过网络传输的,所以相比从路径加载图片,我们更倾向于直接将字节数据转换为图片对象,即PILImage:importiofromPILimportImage#img_bytes:图片内容bytesimg=Image.open(io.BytesIO(img_bytes))img=img.convert('RGB')img=img.resize((224,224),Image.NEAREST)上面的imgand上面的image.load_img得到的结果是一样的。这里需要注意的是:必须进行RGB转换,必须进行resize(load_img方法的第二个参数也是resize)4.黑边处理有时候图片会有比较多的黑边部分(比如截图),而这些黑边的部分是没有实用价值的,会造成比较大的干扰,所以去黑边也是一个常用的操作。所谓黑边,本质上就是一行或一列的像素点都是(0,0,0)(RGB图像)。去除黑边就是找到这些行或列,然后删除它们。它实际上是一个numpy的3-D矩阵操作。去除水平黑边示例:#-*-coding:utf-8-*-importnumpyasnpfromkeras.preprocessingimportimagedefRemoveBlackEdge(img):"""去除图像的水平黑边Args:img:PILimageinstanceReturns:PIL图像实例"""width=img.widthimg=image.img_to_array(img)img_without_black=img[~np.all(img==np.zeros((1,width,3),np.uint8),axis=(1,2))]img=image.array_to_img(img_without_black)returnimgCNN提取图像特征以及图像的其他相关处理先写这么多,我们来看看向量搜索引擎。向量搜索引擎Milvus仅仅有图像的特征向量是不够的。我们还需要对这些特征向量进行动态管理(增、删、改),并计算向量的相似度,返回最近范围内的向量数据。开源矢量搜索引擎Milvus很好地完成了这些工作。下面将介绍具体做法和注意事项。一、对CPU的要求如果要使用Milvus,首先要要求你的CPU支持avx2指令集。如何查看你的CPU支持哪些指令集?对于Linux系统,输入命令cat/proc/cpuinfo|grep标志,你会看到以下内容:noplxtopologynonstop_tsccpuidaperfmperfpnipclmulqdqdtes64monitords_cplvmxsmxesttm2ssse3sdbgfmacx16xtprpdcmpciddcasse4_1sse4_2x2apicmovbepopcntaesxsaveavxf16crdrandlahf_lmabmcpuid_faultepbinvpcid_singleptiintel_ppintpr_shadowvnmiflexpriorityeptvpidept_adfsgsbasetsc_adjustbmi1avx2smepbmi2ermsinvpcidcqmxsaveoptcqm_llccqm_occup_llcdthermidaaratplnptsflags您的CPU支持所有这些。只是想看看是否支持特定的指令集,比如avx2,加个grep过滤一下:cat/proc/cpuinfo|grep标志|grepavx2如果执行结果没有内容输出,说明不支持这个指令集,你只能更换一台符合要求的机器。2.容量规划在设计系统时,容量规划是首先要考虑的。我们需要存储多少数据,这些数据需要多少内存和多少磁盘空间?快速计算,上面的特征向量每一维都是float32的数据类型,一个float32需要占用4个字节,那么一个512维的向量需要2KB,以此类推:一千个512维的向量需要2MB,一个百万个512维向量需要2GB。一个1000万个512维向量需要20GB。一个1亿个512维向量需要200GB。十亿个512维向量需要2TB。内存容量。这里推荐大家使用官方的大小计算工具:milvustools其实我们的内存可能没有那么大(内存不够也没关系,milvus会自动刷盘),而在除了这些原始的向量数据之外,还会有一些其他的数据比如日志等其他存储是我们需要考虑的。3.系统配置关于系统配置,官方文档有比较详细的描述:Milvusserverconfiguration如何设置系统配置项来为生产环境配置Milvus4.数据库设计collection&partition在Milvus中,数据会按照collection和partition进行划分partition:collection就是我们理解的表。partition是集合的分区,也就是某个表里面的分区。partition的底层实现其实和collection是一样的,只是前者属于后者。但是,有了分区,数据的组织变得更加灵活。我们也可以在集合中指定特定的分区进行查询,从而达到更高的查询性能,更多细节可以参考分区表的详细说明。我们可以使用多少个集合和分区?由于collection和partition等基本信息属于元数据,而milvus内部的元数据管理需要使用SQLite(milvus内部集成)或者MySQL(需要外接)其中之一,如果使用默认的SQLite来管理元数据,当集合和分区数太大,性能损失会很严重,所以集合和分区总数不要超过50000(0.8.0以后版本会限制在4096),如果需要设置更大号,建议使用外部MySQL方式。Milvus的集合和分区内部支持的数据结构非常简单,只支持ID+vector。也就是说,该表只有两列,一列是ID,一列是矢量数据。注意:ID目前只支持整数类型。我们需要确保ID在集合级别是唯一的,而不是分区。条件过滤我们在使用一些传统数据库的时候,往往可以指定字段进行条件过滤,但是Milvus并没有直接支持这个功能。但是,我们可以通过集合和分区的设计来实现简单的条件过滤。比如我们有很多Image数据,但是这些图片数据显然是属于特定用户的,那么我们就可以按照用户来划分分区,这样在查询的时候,以用户作为过滤条件实际上就是指定分区。结构化数据与向量的映射由于milvus只支持ID+向量的数据结构,在实际业务中,我们最终需要的往往是具有业务意义的结构化数据,即我们需要通过向量vectors来查找结构化数据,所以我们需要使用ID来维护结构化数据与向量的映射关系:结构化数据ID<-->映射表<-->MilvusID索引类型选择,请参考如下文档:索引类型如何选择索引类型5、搜索结果处理Milvus的搜索结果是一个ID+距离的集合:ID:集合中的ID。distance:0~1的距离值,表示相似程度,越小越相似。过滤ID为-1的数据当数据集太小时,搜索结果中可能会包含ID为-1的数据,我们需要自己过滤掉。翻页向量的搜索比较特殊。查询结果按相似度排序,从最相似的中选出topK个数据(topK由用户在需要查找时指定)。Milvus的搜索不支持翻页。如果我们要在业务中实现这个功能,就只能自己搞定了。比如我要每页10条数据,只显示第3页的数据,那么我们就需要去取topK=30条数据,然后只返回最后10条。业务相似度阈值两张图片的特征向量之间的距离取值范围为0到1。在业务上有时候我们需要判断两张图片是否相似。这时候我们就需要自己设定一个距离阈值。当距离小于阈值时,可以判断为相似,当大于阈值时,可以判断为不相似。这个也需要根据具体的业务来处理。结语本文用图片搜索系统介绍了工程实践中比较常见的内容,最后强烈推荐Milvus。