拍照搜题功能服务端(php)开发——simhash

        近期接到的项目中有一个功能——手机拍照搜索试题。首先进行分析,客户端调用ocr接口识别照片中的文字,服务端拿到文字去题库进行搜索。对后端开发的我来说,问题就变为如何根据关键词搜索匹配度较高的题目。一个全新的问题,之前没有接触过,此时内心有些崩溃,不知道能不能实现,而且要给出开发工期,要怎么办?姑且先给5天吧,如果调研失败,我有最后的保留方案——用mysql全文索引实现(在类似需求中应用过,有局限性,不是根据语义分析而是根据标点拆分文章,看句子是否在题目中出现过)。

       调研开始发现百度App有拍照搜题的功能,既然人家能实现,说明这个问题是有解的,进一步增强了自己的信心。

       后来发现很多关于simHash的文章,其中一篇:simhash算法原理及实现。用简单的话解释一下simhash:这个算法可以把任何文章都哈希成一个64位的二进制码,语义越相近的文章,获得的二进制码的汉明距离越小。如果感兴趣,大家可以深入研究一下。

      基于这个理论,经过大量尝试(其中不乏开源代码,在此非常感谢),终于完成了下面的类。simhash第一步首先要分词,故这个类用到了php的scws这个拓展。

如果想要了解scws怎样安装,点这

', '"', '\'','?','[', ']', '{', '}', '\\','|', '+', '=', '_', '^','$', '~', '`', '"', '"',';', '.');return str_replace($DBC, $SBC, $str);}function hashCode($str) {if(empty($str)) return '';$mdv = md5($str);$mdv1 = substr($mdv,0,16);$mdv2 = substr($mdv,16,16);$crc1 = abs(crc32($mdv1));$crc2 = abs(crc32($mdv2));$code =  decbin(bcmul($crc1,$crc2));$code = str_repeat('0',64 - strlen($code)).$code;return $code;}function hashCode64($str) {$len = 8;$md5 = substr(md5($str), 0, $len);$seed = 31; $hash = 0;for($i = 0; $i < $len; $i++) {  $hash = $hash*$seed+ord($md5{$i});}$hash = $hash & 0x7FFFFFFF;$hash = decbin(bcmul($hash,$hash));$hash = str_repeat('0',64 - strlen($hash)).$hash;return $hash;}//采用scws分词function getSimHash($text){$so = scws_new();$so->set_charset('utf8'); //编码$so->set_duality(0);  //散字二元$so->set_ignore(0); //忽略标点符号$so->set_multi(0);$str = $this->replace_DBC2SBC($text);//过滤字符$filter = [',','.','-','_','`','、','"',"'",":",';','<','>','{','}','(',')'];$so->send_text($str);$keyList = array();while($words = $so->get_result()){foreach($words as $word){$s = $word['word'];$weight = intval($word['idf'])*20;if(!in_array($s,$filter)  && $weight){$hash = array();$hash_code = $this->hashCode64($s);for($i=0;$i<64;$i++){$value = intval(substr($hash_code,$i,1));if($value==1){$hash[] = $weight;}else{$hash[] = -$weight;}}$keyList[] = $hash;}}}$finCode = '';for($i=0;$i<64;$i++) {$code = 0;if(empty($keyList)) {break;}foreach($keyList as $key) { $code+=intval($key[$i]);}if($code>0) {$finCode .= '1';} else {$finCode .= '0';}}return $finCode;}
}

创建倒排索引数据表

CREATE TABLE `simhash_syintelligence` (`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,`hashcode16` char(16) NOT NULL DEFAULT '' COMMENT '16位2进制hash',`dp` text NOT NULL  COMMENT '倒排字段 内容:(题目id:hashcode位置;题目id:hashcode位置)',PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8

接下来,如何实现就非常明了了。现将接收到的关键词做一次simHash,然后将值分隔成4段,去表中分4次查询,找到位置对应的题目id即可。

以上是猜想,基于ocr准确识别的情况,但联调时发现,照片识别不是很准确,经常出现类似“问”识别为“间”的情况。simHash是基于语义分析的,我上面的方案必须保证分割的4块中其中一块完全相同,错一字,汉明距离就变大好多,导致可能匹配不到。还有一种情况是,会返回毫不相关的文章,因为这样做返回文章的汉明距离为0-48,于是又改变了策略,调整表的设计:

CREATE TABLE `simhash_syintelligence` (`id` BIGINT(20) UNSIGNED NOT NULL AUTO_INCREMENT,`ques_id` INT(10) NOT NULL DEFAULT '0' COMMENT '题目id',`hashcode64` CHAR(64) NOT NULL DEFAULT '' COMMENT '64位2进制hash',PRIMARY KEY (`id`),KEY `hashcode64` (`hashcode64`)
) ENGINE=INNODB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8

将题目内容完整hash记录下来,不再做切割。拿到关键词,先做一次simHash,然后轮询表,两两比较汉明距离,如果汉明距离小于某个特定值,则将题目返回回来。

 

算是一种思路,但是实际应用中发现哈希碰撞的概率挺高的,搜索一段英文,完全没有联系的中文被搜出来的,效果不太理性;

可以换成es全文搜索引擎做


本文来自互联网用户投稿,文章观点仅代表作者本人,不代表本站立场,不承担相关法律责任。如若转载,请注明出处。 如若内容造成侵权/违法违规/事实不符,请点击【内容举报】进行投诉反馈!

相关文章

立即
投稿

微信公众账号

微信扫一扫加关注

返回
顶部