小叽导读:如何将单张图片由120k 压缩到了平均13k?阿里工程师做到了!并且将欧式距离计算平均耗时做到9微秒。今天,阿里巴巴技术专家萧冷将公开从初步尝试到优化的过程,希望对你有所帮助。背景在手机上用户随手拍一张衣服的照片,去找类似图片的需求比较明显,以图搜图项目目的就是满足用户的这部分需求。该项目初步预计5个类目,每个类目500万图片用于检索。经过特征提取,每张图片可以表示为30976维空间中的一个点,即可以用30976个float值表示,为了便于处理,我们将特征值乘以100000,在不损失float值精度的情况下,用int32_t存储图片特征。图片检索时需要计算query 图片和索引图片的欧式距离,图片之间计算欧式距离的耗时为50微秒,经过cpu指令集优化(sse),欧式距离计算耗时减少到13微秒。在压缩之前,所有图片的大小为 3T( 4 * 30k * 25000000),每台机器30G内存用于存储图片,需要100台机器存储全量图片。需要在计算欧式距离效率不降低的情况下,对图片进行压缩,大规模减少机器的占用。我们的目标是压缩到500G左右,即压缩之后每张图片20k左右,欧式距离计算耗时为15微秒左右。欧式距离计算要求耗时在微秒级别,已有的压缩方法,比如p4delta、valgrind压缩等在性能上不满足要求,需要我们根据数据特点自己定制压缩方法。成果 

  1. 目前的压缩方法单张图片由120k 压缩到了平均13k。

  2. 欧式距离计算平均耗时为9微秒。

这么靠谱的成果是如何做到的呢?初步尝试

  • bitmap的方法

观察数据特点,发现平均每张图片有7000个数为非0值,其他值都为 0。首先想到的是用bitmap表示30976个值在特定的位置是否是0。bitmap需要30976 / 8= 4k个字节。然后只存储非0值,需要7k * 4,压缩之后平均每张图片大小为32k。压缩代码大体如下:

 int bitmap_len = size / 8 + 8;   uint64_t *bitmap = (uint64_t*)(cmpr_buf);   int32_t *data   = (int32_t*)(cmpr_buf + bitmap_len);   for(unsigned int i=0;i       if(list[i] != 0) {           data[index++] = list[i];           bitmap[i/64] |= bit_mask_tab[i % 64];       }   }

但是在计算图片之间的欧式距离时,需要遍历30976次bitmap,并判断特定位的值知否为0,即将bitmap和掩码数组进行与操作,比较耗时,计算耗时在100微秒以上。计算两个压缩图片的欧式距离代码:

 for(i=0; i64; i++) {       for(int j=0; j<64; j++) {           a = 0;           b = 0;           if((bitmap1[i] & bit_mask_tab[j])) {               a = data1[index1++];           }           if((bitmap2[i] & bit_mask_tab[j])) {               b = data2[index2++];           }           olength += (a - b) * (a - b);       }   }
  • 采用offset的压缩方式

我们只保存非0数据的off_set和value,off_set最大值30975,需要用int16_t来保存,value的范围0~100万,需要int32_t来表示,采用该方法的话,每个图片占用空间为42k (7k * (2 + 4))。

  for(int i=0; i       if(list[i] != 0) {           index++;       }   }   *(int16_t*) cmpr_buf = index;   int16_t *p_off = (int16_t*)cmpr_buf + 1;   int32_t *p_data = (int32_t*)(((char *)cmpr_buf) + sizeof(int16_t) * index + sizeof(int16_t));   index = 0;   for(int i=0; i       if(list[i] != 0) {           p_off[index] = i;           p_data[index] = list[i];           index++;       }   }

计算两个压缩图片的欧式距离的时候,采用按照off_set归并的方法:

while(p_data1<end1 && p_data2 < end2){       if(*p_off1 < *p_off2) {           olength += *p_data1 * *p_data1;           p_data1++;           p_off1++;       } else if(*p_off1 > *p_off2) {           olength += *p_data2 * *p_data2;           p_data2++;           p_off2++;       } else {           olength += (*p_data1 - *p_data2) * (*p_data1 - *p_data2);           p_data1++;           p_data2++;           p_off1++;           p_off2++;       }   }

该方法进行欧式距离的耗时为55微秒,我们认为是if 条件比较耗时,于是尝试了用位掩码替代if的方式:

while(p_data1 < end1 && p_data2       a = ((*p_off1 - *p_off2) <= 0);       b = ((*p_off2 - *p_off1) <= 0);       tmp1 = -a & *p_data1;       tmp2 = -b & *p_data2;       p_off1 += a;       p_off2 += b;       p_data1 += a;       p_data2 += b;       tmp = tmp1 - tmp2;       olength += tmp * tmp;   }

该方式消除了if 条件判断,但是耗时为70微秒,性能反而下降了,耗时的原因是cpu的指令增多了。性能优化场景分析

  • 两个压缩图片计算 --> 一个压缩一个非压缩

目前的优化进入了一个瓶颈,如何才能提升性能到10微秒级别呢?我们回过头来重新考虑了一下应用场景,在线的场景是query图片和一系列图片计算距离,离线的场景是中心点图片和其他一系列图片计算距离也就是说都是一个图片和多个图片进行距离计算,这时一个图片不需要进行压缩,完全可以是非压缩的,即使该图片是压缩也可以先解压计算欧式距离实际上可以转化为一个非压缩图片和多个压缩图片计算欧式距离。对这样的情况,我们需要重新考虑提升效率的问题。

  • 确定采用off_set压缩方式

对于计算一个压缩和一个非压缩图片欧式距离的问题,比较bitmap的压缩方式和off_set的压缩方式,off_set的压缩方式有明显的优势对于bitmap方式,最耗时的地方仍然是访问30976次bitmap,然后做与操作,来获取解压后的值,遍历30976次bitmap耗时,至少50微秒以上但是off_set的方式保存了7000个非0数据的off_set,我们只需要遍历7000次非0 数组就可以计算出欧式距离。一个压缩一个非压缩

  • 做法

首先计算好非压缩图片的累加平方和,每次查询计算多次欧式距离,只计算一次累加平方和。遍历压缩图片数组,计算该值和另一张非压缩图片的对应off_set的差值的平方。对于压缩图片的为0的那些值来说,欧式距离只需要加上非压缩图片那些值的平方和。

  • 举例:

a.非压缩图片:[0 2 3 0 4 0 0 5 6 0 0] ,压缩图片:[0 0 0 6 6 6 0 0 ]b.事先计算好非压缩图片的特定位之前的所有值的平方和:sqrt[0 4 13 13 29 29 29 54 90 90 90]c.压缩的数组为 off[3 4 5], data[6 6 6 ]d.遍历off和data数组

  • olength += (6 - 0) * (6 - 0) olength += (sqrt[2] - sqrt[0])
  • olength += (6 - 4) * (6 - 4)olength += (sqrt[3] - sqrt[3])
  • olength += (6 - 0) * (6 - 0) olength += (sqrt[4] - sqrt[4])
  • 效率:20微秒

该方法只需要遍历7000次数组, 进行7000次相减 平方操作和 7000次访问sqrt 数组操作,大大简化了复杂度。

  • 代码如下:

data1为压缩数据,data2为非压缩数据:

 for(int i=0; i            olength += (data1[i] - data2[off1[i]]) * (data1[i] - data2[off1[i]]);            olength += sqrt[off[i] - 1]  - sqrt[off[i-1]];      }

平方差展开

  • 有没有更优化的方法呢?

欧式距离的计算公式为(a[1]-b[1])*(a[1]-b[1]) + (a[2]-b[2])*(a[2]-b[2]) + ... +(a[n]-b[n])*(a[n]-b[n)。经过展开可得a[1]*a[1] + a[2]*a[2] + ... +a[n]*a[n]+ b[1]*b[1] + b[2] *b[2] + ... b[n]*b[n] - 2*(a[1]*b[1] + a[2]*b[2] + ... + a[n]*b[n])。而a[1]*a[1] + ... a[n]*a[n]和b[1]*b[1] + ... + b[n]*b[n]可以在压缩的时候先计算并保存起来,因为压缩的耗时可以多一些。所以计算欧式距离只需要计算a[1]*b[1] + a[2]*b[2] + ... + a[n]*b[n]即可。

  • 举例,上面的例子:

a.非压缩图片:[0 2 3 0 4 0 0 5 6 0 0] ,压缩图片:[0 0 0 6 6 6 0 0 ]b.计算压缩图片和非压缩图片的平方和。sqrt1 :90 sqrt2:108c.每次查询 query 图片计算一次 平方和, 压缩图片的平方和在压缩的时候,计算好,并存储。d.遍历压缩数组

  • multi += 6 * 0
  • multi += 6 * 4
  • multi += 6 * 0

e.欧式距离 = 90 + 108 - 2 * multi

  • 效率 :11微秒

该方法只计算7000次相乘,效率相对于上面的方法提高了一倍。

  • 代码

   for(int i=0; iint64_t)p_data1[i] * (int64_t)p_data2[p_off1[i]];       }       olength += sqrt1 + sqrt2 - 2 * multi;

压缩比优化基本off_set压缩

  • 方法

基本的off_set压缩前面已经讲过。

  • 效果

每张图片压缩到42k。

  • 举例

图片 :[ 5 5 8 0 0 ... 0 7 6 3 0 0 0 0] ((8 和 7 之间500个0),其中208为平方和,6 为非0元素个数。off_set优化

  • 方法

基本的方法利用int16_t来保存非0数据的off_set,每个非0 value的off_set占用2个字节,如何减少off_set 的空间占用呢?很自然的我们想到了保存该数据的off_set与上一个非0数据的off_set的差值,这样的话,该值就会大大减小,一般的情况下会小于255,则我们可以用uint8_t来保存off_set的差值。但是如果off_set的差值大于255的话,该怎么办呢?我们多存了一些0,作为非0值来保存,如果两个非0值之间的off_set大于255,则第一个非0值的off_set+255个off_set的位置存一个作为非0值的0。如果0的off_set 与下一个非0的off_set 的差值小于255,则保存下一个非0值,否则再加一个非0值。经过统计,每个图片平均有2个off_set差值大于255的非0值,对性能基本没有影响。

  • 效果

采用该方法的话,off_set部分占用一个字节,则压缩之后,图片大小为:7k * (1+4) = 35k。

  • 举例:

图片 : [5 5 8 0 0 ... 0 7 6 3 0] (8 和 7 之间500个0)。如下图所示非0元素的个数变成了7,元素7和8之间由于off_set差值大于255,故多加了一个0,作为非0值,这样off_set的差值就小于255了,在计算欧式距离时没有影响。value优化

  • 方法

value用的是int32_t保存,经过统计,平均有6个值大于65535,则采用uint16_t来保存value的值,超过65535的值,放在压缩buff的尾部,用int32_t保存。

  • 效果

压缩之后每个图片大小为7k*(2+1) = 21k。

  • 举例

图片 :[5 5 8 0 0 0 0 7 67676 66666 0 0 0]。如下图,其中9024396695 为平方和;4 为 uint16_t的值的个数, 2为大于uint16_t的值的个数,前面的非0值采用uint16_t存储,占两个字节,后面的67676、66666由于大于65535,用int32_t类型存储,占用4个字节,平均每个图片大于65535的值的个数为6个,故大大减少了空间占用。去掉重复值还有没有继续压缩的可能呢?

  • 方法

经过分析数据,发现了图片中的相邻的非0数值,很多都是相同的,经过统计平均每张图片有2400个相邻的值不相同,有4300个相邻值是相同的,其中700个值是相邻的两个值相同,3600个值是相邻的三个值相同。例:0 1 1 1 0 0 3 3 3 2 2 0 0。如果将上面的1 1 1 和 3 3 3 和 2 2 归一,只保存 1、3、2和 该值的有几个,则可以进一步压缩空间将每个值有几个存在哪里呢?存在off_set 数组中, 现在的off_set用8个bit保存,最大保存255的off_set差值,将这8个off_set拆分,6个bit保存off_set差值,最大保存差值为63,剩下的2个bit保存该值有几个相同的值,可以保存的相同值的个数为:0 (不会用到)、1 、2 、3。经过统计,平均每条query中的off_set差值大于63的值的个数为250个,也就是需要额外保存200多个0值,对空间占用影响不是特别大。

  • 效果

通过该方法将图片压缩到13k,约等于 :2.4k * 3 + (1.2+0.3)k * 3 其中2.4k为 相邻不重复的值的个数,1.2k为相邻且重复值为3的值的个数, 0.3k为相邻且重复值为2 的值的个数,3 为 一字节的off_set + 2字节的value。off_set 差值大于63的和value值大于65535的对空间影响很小。计算欧式距离的耗时变为了35微秒,效率变慢的原因主要是int8_t字节 被拆成了6bit 和 2bit。

  • 举例

图片 : [0 1 1 1 0 0 3 3 3 2 2 0 0 ]其中38表示平方和、3表示有3个值,0表示有0个大于65535的值,(1:3)用一个uint8_t表示, 前6个bit保存1,表示off_set是1,后2个bit存3,表示相同的值有3个。(5:3) 和前面的类似,表示off_set差值是5,3个相同的值。(3:2)类似。后面的1 3 2为非0的值,与前面的方法不同的是不存重复的值。相邻相同的值,只存一个。去掉重复值优化上面的方法虽然压缩比高了,但是效率降低了,能不能将效率提高上来呢?

  • 方法

上面的方法,效率降低是因为off_set拆成了6个bit和2个bit,在计算欧式距离时,需要做 与 操作,增大了复杂度。能不能不拆分呢?我们在存储的时候将没有重复的值存在一起,两个重复的值存在一起,将重复3个重复的存在一起,这样在计算欧式距离的时候,遍历到3个重复的值的时候,只需要计算三次相乘的值就可以了,就不需要保存重复值的个数了。这样的方法off_set的差值发生了变化,不是非0值之间的off_set差值,而是没有重复值的off_set差值,和重复3个的值之间的off_set差值,这个off_set会增大,经过统计没有重复值之间的off_set差值大于255的为4个,两个重复值的off_set差值大于255的为25个,3个重复的off_set之间的差值大于255的为5个。2个重复值的之所以大于255的比较多,是因为2个重复的值比较少,300个值左右,故他们之间相隔比较远,即off_set差值比较大,所以我们将2个重复的值作为不重复的值考虑。

  • 效果

该方法将图片压缩为14k左右。(2.4k + 0.7k) * 3 + 1.3k * 3, 其中2.4k为不重复的值的总和,0.7k为重复数为2的值的总和,1.3k为重复数为3的值的个数之和,3 为off_set字节+value 字节,其他值大小对总空间影响忽略不计。性能:9微秒, 在11微秒的基础上减少了2微秒,因为重复数为3 的值时,不需要重复遍历数组。遍历的数组的次数不是7000,而是3000 + 1300 = 4300次。

  • 举例

图片 : [0 1 1 1 0 0 3 3 3 2 2 0 0 ]。其中38表示平方和、2表示有2不重复的值,后面的2表示有2个重复数为3 的值, 0表示有0个大于65535的值,9 和 1 表示不重复的值的off_set 差值, 2 和2 表示不重复的值,1和5表示重复数为3 的off_set差值,1 和3表示重复数为3 的值。各个方法的效果总结:「 更多干货,更多收获 」

多视图多标记算法SIMM

10亿节点异构网络中,GCN 如何应用?

当个不“佛系”的推荐系统,CTR 预估要做哪些?

关注机器智能把握未来可能

如何把照片压缩到20k一下_如何将图像压缩10倍?阿里工程师有个大胆的想法!...相关推荐

  1. 如何把照片压缩到20k一下_如何将照片压缩到20k_手机怎么把照片压缩到20k

    如何压缩图片到20k,PS是一款修图软件,今天小编就为大家介绍一下如何压缩图片到20k. 怎么将照片压缩到20k以下,在网上报名,上传电子版照片的要求通常是照片要这种情况可以使用word中的压缩图片 ...

  2. 如何把照片压缩到20k一下_如何把2寸彩照压缩到20k以下?

    最近一位好友,找我压缩电子证件照. 网络证件报名,要求的证件照大小是20k以下. 下面分享一下,如何用Photoshop,把2寸彩照,压缩到20k以下?证件照要求: 1.背景颜色:白色: 2.照片尺寸 ...

  3. 如何把照片压缩到20k一下_怎么样才能把大于20k的照片缩小到20k之内?分享解决问题的方法...

    在生活中,我们通常会在报名参加考试或者某活动时需要上传20k以内大小的证件照到某网站,而我们原本的照片远远大于20k,这时候,我们应该怎么办呢?怎么样才能把大于20k的照片缩小到20k之内呢?小编今天 ...

  4. 如何把照片压缩到20k一下_如何将一寸照片压缩到20k以内?

    小编发现,许多考试都采取网上报名的方式,且要求上传本人近期免冠照片(20k以下),相信很多同学们都有过图片尺寸过大,上传不的尴尬经历,而且缩小截图会变得模糊的情况. 为解决同学们不会压缩图片,上传模糊 ...

  5. 照片怎么压缩变小?如何把照片压缩到20K?

    现在使用纸质照片已经越来越少了,更多的时候是使用一些电子版的照片,在使用电子版照片时有一个问题大家应该都遇到过,那就是有的系统会限制图片大小,例如图片大小不能超过20K,这就非常麻烦了,因为现在一张照 ...

  6. 如何将证件照缩小到20k像素不变?怎么把照片压缩到20k?

    随着时代的发展,很多以前需要现场办理的东西现在都可以从网上进行自助办理了,极大地节约了大家的时间,但是在办理一些证件 的时候大家经常会遇到上传的照片大小在20k以内,如果手头上的照片比这个大要怎么办呢 ...

  7. 角距离恒星_恒星问卷调查的10倍机器学习生产率

    角距离恒星 With availability of massive data and computation, Machine Learning (ML) and other spheres of ...

  8. 如何把图片压缩到20k一下?怎么降低照片大小kb?

    很多时候我们都需要调整图片kb大小,比如将图片上传到网站时,网站限制大小是20K,那么超过这个大小的图片我们就可以通过压缩图片大小去将图片压缩到20k以内.下面给大家分享一个简单有效的图片压缩指定大小 ...

  9. 如何使用手机把照片压缩到100K?教你手机压缩方法

    如何使用手机来把照片压缩到100K大小呢?在我们日常的工作和学习中,有时会使用到照片来进行传输或者保存,如果照片的内存过大,会导致我们上传不了或者是非常占用内存.在身边没有电脑的情况下,我们如何使用手 ...

最新文章

  1. MCU多任务提高实时性
  2. Element 对象表示 XML 文档中的元素。
  3. 用Flink取代Spark Streaming!知乎实时数仓架构演进
  4. idea中怎么新建vue项目_项目中使用vue-awesome-swiper
  5. ferror,perror,cleaner
  6. [linux]单网卡绑定多个IP
  7. 拓端tecdat|R语言t检验和非正态性的鲁棒性
  8. LINUX下载编译jpeglib
  9. Cognos SDK 入门教程(一) - Hello Cognos
  10. 3dmax模型带材质导出obj格式文件的方法与步骤
  11. pr踩点插件beat edit安装教程
  12. win7禁用驱动签名验证_如何在64位Windows 8或10上禁用驱动程序签名验证(以便可以安装未签名的驱动程序)...
  13. linux中的.sh文件是什么
  14. js与朴php订单评价功能
  15. 数学基础task04 一元函数微分学的几何应用
  16. 天翼云linux远程密码不对,天翼云主机远程连接
  17. 快的买大黄蜂是福是祸?
  18. App Inventor 模拟器问题的解决
  19. 集中化运维管理——Puppet管理之路
  20. vue项目中videoPlayer 的 src 视频地址参数动态修改---方法

热门文章

  1. 2转单通道 python_机器学习用Python—Python集成工具包Anaconda安装步骤
  2. 1003 我要通过! (20 分) python版答案(全部代码都有注释)
  3. matlab关于噪声课设,基于matlab的有噪声的语音信号处理的课程设计.doc
  4. dell r220服务器配置oracle linux 阵列卡,如何在Dell服务器PERC5/6阵列卡配置RAID
  5. 基于服务器端保存用户的查询参数
  6. where field in
  7. DNS原理及其解析过程(转)
  8. JSP页面退出时清除会话Session
  9. 清理无用的CSS样式比较有用的几个工具
  10. 经典博文--各系列文章