Java实现LSH(Locality Sensitive Hash )

  在对大批量数据进行图像处理的时候,比如说我提取SIFT特征,数据集为10W张图片,一个SIFT特征点是128维,一张图片提取出500个特征点,这样我们在处理的时候就是对5000万个128维的数据进行处理,这样处理所需要的耗时太长了,不符合实际生产的需要。我们需要用一种方法降低运算量,比如说降维。

  看了一些论文,提到的较多的方法是LSH(Locality Sensitive Hash),就是局部敏感哈希。我们利用LSH方法在5000万个特征点中筛选出极少量的我们需要的特征点,在对这些极少量的数据进行计算,就可以得到我们想要的结果啦。

  1 package com.demo.lsh;
  2
  3 import com.demo.config.Constant;
  4 import com.demo.dao.FeatureDao;
  5 import com.demo.dao.FeatureTableDao;
  6 import com.demo.dao.HashTableDao;
  7 import com.demo.entity.HashTable;
  8 import com.demo.utils.MD5Util;
  9 import com.demo.utils.MathUtil;
 10 import org.opencv.core.Mat;
 11 import org.springframework.util.StringUtils;
 12
 13 import java.io.*;
 14 import java.security.MessageDigest;
 15 import java.security.NoSuchAlgorithmException;
 16 import java.util.*;
 17
 18 public class LSH {
 19     //维度大小,例如对于sift特征来说就是128
 20     private int dimention = Constant.DIMENTION;
 21     //所需向量中元素可能的上限,譬如对于RGB来说,就是255
 22     private int max = Constant.MAX;
 23     //哈希表的数量,用于更大程度地削减false positive
 24     private int hashCount = Constant.HASHCOUNT;
 25     //LSH随机选取的采样位数,该值越小,则近似查找能力越大,但相应的false positive也越大;若该值等于size,则为由近似查找退化为精确匹配
 26     private int bitCount = Constant.BITCOUNT;
 27     //转化为01字符串之后的位数,等于max乘以dimensions
 28     private int size = dimention * max;
 29     //LSH哈希族,保存了随机采样点的INDEX
 30     private int[][] hashFamily;
 31     private HashTableDao hashTableDao;
 32     /**
 33      * 构造函数
 34      */
 35     public LSH(HashTableDao hashTableDao) {
 36         this.hashTableDao = hashTableDao;
 37         dimention = Constant.DIMENTION;
 38         max = Constant.MAX;
 39         hashCount = Constant.HASHCOUNT;
 40         bitCount = Constant.BITCOUNT;
 41         size = dimention * max;
 42         hashFamily = new int[hashCount][bitCount];
 43         generataHashFamily();
 44     }
 45
 46     /**
 47      * 生成随机的投影点 ,在程序第一次执行时生成。投影点可以理解为后面去数组的索引值
 48      */
 49     private void generataHashFamily() {
 50         if (new File("/home/fanxuan/data/1.txt").exists()) {
 51             try {
 52                 InputStream in = new FileInputStream("/home/fanxuan/data/1.txt");
 53                 ObjectInputStream oin = new ObjectInputStream(in);
 54                 hashFamily = (int[][]) (oin.readObject());
 55             } catch (IOException e) {
 56                 e.printStackTrace();
 57             } catch (ClassNotFoundException e) {
 58                 e.printStackTrace();
 59             }
 60         }else {
 61             Random rd = new Random();
 62             for (int i = 0; i < hashCount; i++) {
 63                 for (int j = 0; j < bitCount; j++) {
 64                     hashFamily[i][j] = rd.nextInt(size);
 65                 }
 66             }
 67             try {
 68                 OutputStream out = new FileOutputStream("/home/fanxuan/data/1.txt");
 69                 ObjectOutputStream oout = new ObjectOutputStream(out);
 70                 oout.writeObject(hashFamily);
 71             } catch (FileNotFoundException e) {
 72                 e.printStackTrace();
 73             } catch (IOException e) {
 74                 e.printStackTrace();
 75             }
 76         }
 77     }
 78
 79     //将向量转化为二进制字符串,比如元素的最大范围255,则元素65就被转化为65个1以及190个0
 80     private int[] unAray(int[] data) {
 81         int unArayData[] = new int[size];
 82         for (int i = 0; i < data.length; i++) {
 83             for (int j = 0; j < data[i]; j++) {
 84                 unArayData[i * max + j] = 1;
 85             }
 86         }
 87         return unArayData;
 88     }
 89
 90     /**
 91      * 将向量映射为LSH中的key
 92      */
 93     private String generateHashKey(int[] list, int hashNum) {
 94         StringBuilder sb = new StringBuilder();
 95         int[] tempData = unAray(list);
 96         int[] hashedData = new int[bitCount];
 97         //首先将向量转为二进制字符串
 98         for (int i = 0; i < bitCount; i++) {
 99             hashedData[i] = tempData[hashFamily[hashNum][i]];
100             sb.append(hashedData[i]);
101         }
102         //再用常规hash函数比如MD5对key进行压缩
103         MessageDigest messageDigest = null;
104         try{
105             messageDigest = MessageDigest.getInstance("MD5");
106         }catch (NoSuchAlgorithmException e) {
107
108         }
109         byte[] binary = sb.toString().getBytes();
110         byte[] hash = messageDigest.digest(binary);
111         String hashV = MD5Util.bufferToHex(hash);
112         return hashV;
113     }
114
115     /**
116      * 将Sift特征点转换为Hash存表
117      */
118     public void generateHashMap(String id, int[] vercotr, int featureId) {
119         for (int j = 0; j < hashCount; j++) {
120             String key = generateHashKey(vercotr, j);
121             HashTable hashTableUpdateOrAdd = new HashTable();
122             HashTable hashTable = hashTableDao.findHashTableByBucketId(key);
123             if (hashTable != null) {
124                 String featureIdValue = hashTable.getFeatureId() + "," + featureId;
125                 hashTableUpdateOrAdd.setFeatureId(featureIdValue);
126                 hashTableUpdateOrAdd.setBucketId(key);
127                 hashTableDao.updateHashTableFeatureId(hashTableUpdateOrAdd);
128             } else {
129                 hashTableUpdateOrAdd.setBucketId(key);
130                 hashTableUpdateOrAdd.setFeatureId(String.valueOf(featureId));
131                 hashTableDao.insertHashTable(hashTableUpdateOrAdd);
132             }
133         }
134     }
135
136     // 查询与输入向量最接近(海明空间)的向量
137     public List<String> queryList(int[] data) {
138         List<String> result = new ArrayList<>();
139         for (int j = 0; j < hashCount; j++) {
140             String key = generateHashKey(data, j);
141             result.add(key);
142             HashTable hashTable = hashTableDao.findHashTableByBucketId(key);
143             if (!StringUtils.isEmpty(hashTable.getFeatureId())) {
144                 String[] str = hashTable.getFeatureId().split(",");
145                 for (String string : str) {
146                     result.add(string);
147                 }
148             }
149         }
150         return result;
151     }
152
153 }

  

 1 package com.demo.config;
 2
 3 public class Constant {
 4     //维度大小,例如对于sift特征来说就是128
 5     public static final int DIMENTION = 128;
 6     //所需向量中元素可能的上限,譬如对于RGB来说,就是255
 7     public static final int MAX = 255;
 8     //哈希表的数量,用于更大程度地削减false positive
 9     public static final int HASHCOUNT = 12;
10     //LSH随机选取的采样位数,该值越小,则近似查找能力越大,但相应的false positive也越大;若该值等于size,则为由近似查找退化为精确匹配
11     public static final int BITCOUNT = 32;
12 }

  简单的介绍下代码,构造函数LSH()用来建立LSH对象,hashTableDao为数据表操作对象,不多说;因为局部敏感哈希依赖与一套随机数,每次产生的结果都不一致,所以我们需要在程序第一次运行的时候将随机数生成并固定下来,我采用的方法是存放在本地磁盘中,也可以存放在数据库中。generateHashMap()方法为数据训练函数,int[] vercotr为特征向量,其他两个参数为我需要的标志位。queryList()方法是筛选方法。

  感谢http://grunt1223.iteye.com/blog/944894的文章。

原文地址:https://www.cnblogs.com/fx-blog/p/8227988.html

时间: 2024-11-08 23:49:25

Java实现LSH(Locality Sensitive Hash )的相关文章

Locality Sensitive Hash 局部敏感哈希

Locality Sensitive Hash是一种常见的用于处理高维向量的索引办法.与其它基于Tree的数据结构,诸如KD-Tree.SR-Tree相比,它较好地克服了Curse of Dimension,能够将KNN的时间复杂度缩减到sub-linear.LSH多被用于文本.多媒体(图像.音频)的相似性判断.请看下图: 参考上图,如果我们要返回距离中心为r的点,LSH会返回给我们范围更远.更多的点,也就是说,LSH返回的结果会带有一定的false positive.我们或许需要使用linea

局部敏感哈希(Locality Sensitive Hashing)

比较不同的文章.图片啊什么的是否相似,如果一对一的比较,数据量大的话,以O(n2)的时间复杂度来看,计算量相当惊人.所以如果是找相同就好了,直接扔到一个hashmap中即可.这样就是O(n)的复杂度了.不过相同的字符串一定会得到相同的hash,而不同的字符串,哪怕只有一点点不同,也极可能得到完全不同hash.很自然的想到,要是相似的object能够得到相似的hash就好了.局部敏感哈希就是这样的hash,实现了相似的object的hash也是相似的. 定义相似 要找相似,首先是要定义什么事相似.

转: memcached Java客户端spymemcached的一致性Hash算法

转自:http://colobu.com/2015/04/13/consistent-hash-algorithm-in-java-memcached-client/ memcached Java客户端spymemcached的一致性Hash算法 最近看到两篇文章,一个是江南白衣的陌生但默默一统江湖的MurmurHash,另外一篇是张洋的一致性哈希算法及其在分布式系统中的应用.虽然我在项目中使用memcached的java客户端spymemcached好几年了,但是对它的一致性哈希算法的细节从来

大话Java中的哈希(hash)结构(一)

o( ̄▽ ̄)d 小伙伴们在上网或者搞程序设计的时候,总是会听到关于“哈希(hash)”的一些东西.比如哈希算法.哈希表等等的名词,那么什么是hash呢? 一.相关概念 1.hash算法:一类特殊的算法(注意哦,hash算法并不是某个固定的算法,而是一类特殊功能算法的统称). 2.哈希表(hash table).哈希映射(hash map).哈希集合(hash set):一种基于hash算法的数据结构. 3.哈希函数:在hash算法中的核心函数. 4.map:译为“映射”,是一种从键(key)到值

学习 Local Sensitive Hash

1. 最近邻法的应用 1.1 Jaccard 相似集 如何定义相似:即相关属性交集的大小,越大则越相似.我们给相似一个数学上的定义:Jaccard 相似集. 集合 \(S\) 与集合 \(T\) 的 Jaccard 集合被定义为 \(|S \cap T|/|S \cup T|\),即它们交集与并集的大小之比.我们简单地定义为\(SIM(S,T)\) ? 1.2 文件的相似度 大范围内搜索内容相似的文本是相似度分析的一个非常重要的应用: 查找剽窃,即相似度查询 网站针对不同主机的镜像页面维护 查找

java包装类的比较、hash和CollectionUtils交集原理探究

转载请注明出处:http://blog.csdn.net/gklifg/article/details/45914169 1.连等(==)比较的适用与不适用场景 场景1: <pre name="code" class="java">public void testJava(){ Long longA = new Long(4l); Long longB = (longA -2)*2; System.out.println("longA=&quo

java 日志脱敏框架 sensitive,优雅的打印脱敏日志

问题 为了保证用户的信息安全,敏感信息需要脱敏. 项目开发过程中,每次处理敏感信息的日志问题感觉很麻烦,大部分都是用工具类单独处理,不利于以后统一管理,很不优雅. 于是,就写了一个基于 java 注解的日志脱敏工具. github sensitive 项目介绍 日志脱敏是常见的安全需求.普通的基于工具类方法的方式,对代码的入侵性太强.编写起来又特别麻烦. 本项目提供基于注解的方式,并且内置了常见的脱敏方式,便于开发. 用户也可以基于自己的实际需要,自定义注解. 变更日志 日志脱敏 为了金融交易的

LSH算法原理

原文链接--http://www.jiahenglu.net/NSFC/LSH.html LSH(Location Sensitive Hash),即位置敏感哈希函数. 与一般哈希函数不同的是位置敏感性,也就是散列前的类似点经过哈希之后,也可以在一定程度上类似,而且具有一定的概率保证. 形式化定义: 对于随意q,p属于S,若从集合S到U的函数族H={h1,h2...hn}对距离函数D(,),如欧式距离.曼哈顿距离等等,满足条件: 则称D(,)是位置敏感的. 例如以下图.空间上的点经位置敏感哈希函

海量文件查重SimHash和Minhash

SimHash 事实上,传统比较两个文本相似性的方法,大多是将文本分词之后,转化为特征向量距离的度量,比如常见的欧氏距离.海明距离或者余弦角度等等.两两比较固然能很好地适应,但这种方法的一个最大的缺点就是,无法将其扩展到海量数据.例如,试想像Google那种收录了数以几十亿互联网信息的大型搜索引擎,每天都会通过爬虫的方式为自己的索引库新增的数百万网页,如果待收录每一条数据都去和网页库里面的每条记录算一下余弦角度,其计算量是相当恐怖的. 我们考虑采用为每一个web文档通过hash的方式生成一个指纹