信息流个性化推荐场景中依赖爬虫抓取的海量新闻库,这些新闻中不乏互相抄袭的新闻,这些内容相似的文章,会造成内容的同质化并加重数据库的存储负担,更糟糕的是降低了信息流内容的体验。所以需要一种准确高效的文本去重算法。而最朴素的做法就是将所有文本进行两两比较,简单易理解,最符合人类的直觉,这种做法对于少量文本来说,实现起来很方便,但是对于海量文本来说是行不通的,所以应在尽可能保证准确性的同时,降低算法的时间复杂度。事实上,传统比较两个文本相似性的方法,大多是将文本分词之后,转化为特征向量距离的度量,比如常见的欧氏距离、海明距离或者余弦角度等等。下面以余弦相似度和simhash算法为例做简单介绍。
余弦相似度的核心思想是计算两个向量的夹角余弦值来判断两个句子的相似度,以下面两个句子为例:
第一步分词:
句子A:我/喜欢/看/电视,不/喜欢/看/电影
句子B:我/不/喜欢/看/电视,也/不/喜欢/看/电影
第二步列出所有词:
我,喜欢,看,电视,电影,不,也
第三步计算词频:
句子A:我1,喜欢2,看2,电视1,电影1,不1,也0
句子B:我1,喜欢2,看2,电视1,电影1,不2,也1
第四步,写出词向量:
句子A:[1,2,2,1,1,1,0]
句子B:[1,2,2,1,1,2,1]
到这里就可以将两个句子的相似度转换为两个向量的相似度,我们可以把这两个句子想象为空间中的两条线段,都是从原点[0,0,0...]出发,指向不同的方向,两条线段形成一个夹角,如果夹角为0,意味着方向相同线段重合,如果夹角为90度意味着形成直角,完全不相似,因此我们可以通过夹角来判断相似度,夹角越小就代表越相似。
余弦相似度得到的结果较为精确,但当面对大量文本时,计算文本向量的时间复杂度很高,这可能会影响性能。
simHash是谷歌提出来的一套用于文本去重的算法,将文本映射为一个01串,并且保证相似文本哈希之后得到的01串也是相似的,只在少数几个位置上的0和1不一样。
为了表征原始文本的相似度,可以计算两个01串之间在多少个位置上不同,这便是汉明距离,用来表征simHash算法下两个文本之间的相似度,通常来说,越相似的文本,对应simHash映射得到的01串之间的汉明距离越小。
举例:t1=“直击儿科急诊现状忙碌不止 儿科接诊进行时 ”t2=“儿科急诊现状直击不停忙碌 儿科接诊进行时 ”;可以看到,上面这两个字符串虽然只有几个字不同,但是通过简单的Hash算法得到的hash值可能就完全不一样了,因而无法利用得到的hash值来表征原始文本的相似性。
然而通过simHash算法的映射后,得到的simHash值便是如下:
这两个文本生成的两个64位的01串只有标红的3个位置不同。
通常来说,用于相似文本检测中的汉明距离判断标准就是3,也就是说,当两个文本对应的simHash之间的汉明距离小于或等于3,则认为这两个文本为相似,如果是要去重的话,就只能留下其中一个。
下图为在各种汉明距离的情况下simhash算法的准确和召回率变化趋势,可以看到在汉明距离为3时能够达到较好的平衡:
相比计算余弦相似度,simhash算法可以快速计算文本的哈希值,而且能够在哈希值之间计算汉明距离,从而衡量文本的相似度。simhash算法的优点是它能够快速处理大量文本,并且可以识别并过滤掉文本中的噪声和重复内容。
1、分词,把需要判重的文本分词,形成去掉噪音词的单词序列并为每个词加上权重。我们假设权重分为5个级别(1~5)。比如:“ 美国“51区”雇员称内部有9架飞碟,曾看见灰色外星人 ” ==> 分词后为 “ 美国(4) 51区(5) 雇员(3) 称(1) 内部(2) 有(1) 9架(3) 飞碟(5) 曾(1) 看见(3) 灰色(4) 外星人(5)”,括号里的权重代表重要程度,数字越大越重要,这里我们采用ansj分词器,tf-idf的方式计算权重。生成一个词和对应权重的map。
public static List\<String\> splitWords(String str) {
List\<String\> splitWords = new ArrayList\<String\>(1000);
Result terms = ToAnalysis.parse(str, forest);
for (int i = 0; i \< terms.size(); i++) {
Term term = terms.get(i);
String word = term.getName();
if (!"".equals(word.trim()) && !stopWords.contains(word)) {
splitWords.add(word);
}
}
return splitWords;
}
public Map\<String, Double\> extract(String str) {
List\<String\> words = WordsSegment.splitWords(str);
// 计算词频tf
int initialCapacity = Math.*max*((int) Math.*ceil*(words.size() / 0.75) + 1, 16);
Map\<String, Double\> wordmap = new HashMap\<String, Double\>(initialCapacity);
for (String word : words) {
if (!wordmap.containsKey(word)) {
wordmap.put(word, 1.0);
} else {
wordmap.put(word, wordmap.get(word) + 1);
}
}
Iterator\<Entry\<String, Double\>\> it = wordmap.entrySet().iterator();
while (it.hasNext()) {
Entry\<String, Double\> item = (Entry\<String, Double\>) it.next();
String word = item.getKey();
if (stopWords.contains(word) \|\| word.length() \< 2) {
it.remove();
continue;
}
// 计算权重idf
if (idfMap.containsKey(word)) {
double idf = wordmap.get(word) \* idfMap.get(word);
wordmap.put(word, idf);
} else {
double idf = wordmap.get(word) \* idfAverage;
wordmap.put(word, idf);
}
}
return wordmap;
}
2、hash,通过hash算法把每个词变成hash值,比如“美国”通过hash算法计算为 100101,“51区”通过hash算法计算为 101011。这样我们的字符串就变成了一串串数字,还记得文章开头说过的吗,要把文章变为数字计算才能提高相似度计算性能,现在是降维过程进行时。
public static BigInteger fnv1aHash64(String str) {
BigInteger hash = FNV_64_INIT;
int len = str.length();
for (int i = 0; i \< len; i++) {
hash = hash.xor(BigInteger.valueOf(str.charAt(i)));
hash = hash.multiply(FNV_64_PRIME);
}
hash = hash.and(MASK_64);
return hash;
}
3、加权,通过2步骤的hash生成结果,需要按照单词的权重形成加权数字串,比如“美国”的hash值为“100101”,通过加权计算为“4 -4 -4 4 -4 4”;“51区”的hash值为“101011”,通过加权计算为 “ 5 -5 5 -5 5 5”。
4、合并,把上面各个单词算出来的序列值累加,变成只有一个序列串。比如 “美国”的 “4 -4 -4 4 -4 4”,“51区”的 “ 5 -5 5 -5 5 5”, 把每一位进行累加, “4+5 -4+-5 -4+5 4+-5 -4+5 4+5” ==》 “9 -9 1 -1 1 9”。这里作为示例只算了两个单词的,真实计算需要把所有单词的序列串累加。
5、降维,把4步算出来的 “9 -9 1 -1 1 9” 变成 0 1 串,形成我们最终的simhash签名。如果每一位大于0 记为 1,小于0 记为 0。最后算出结果为:“1 0 1 0 1 1”。
private void analysis(String content) {
Map\<String, Double\> wordInfos = wordExtractor.extract(content);
Map\<String, Double\> newwordInfo = valueUpSort(wordInfos);
wordInfos.entrySet().stream()
.sorted(Collections.reverseOrder(Map.Entry.comparingByValue()))
.forEachOrdered(x -\> newwordInfo.put(x.getKey(), x.getValue()));
double[] featureVector = new double[FNVHash.HASH_BITS];
Set\<String\> words = wordInfos.keySet();
for (String word : words) {
BigInteger wordhash = FNVHash.fnv1aHash64(word);
for (int i = 0; i \< FNVHash.HASH_BITS; i++) {
BigInteger bitmask = BigInteger.ONE.shiftLeft(FNVHash.HASH_BITS - i - 1);
if (wordhash.and(bitmask).signum() != 0) {
featureVector[i] += wordInfos.get(word);
} else {
featureVector[i] -= wordInfos.get(word);
}
}
}
BigInteger signature = BigInteger.ZERO;
StringBuffer hashBuffer = new StringBuffer();
for (int i = 0; i \< FNVHash.HASH_BITS; i++) {
if (featureVector[i] \>= 0) {
signature = signature.add(BigInteger.ONE.shiftLeft(FNVHash.HASH_BITS - i - 1));
hashBuffer.append("1");
} else {
hashBuffer.append("0");
}
}
this.hash = hashBuffer.toString();
this.signature = signature;
}
算法部分流程图如下:
通过这种特殊的局部敏感哈希算法看起来是解决了相似性对比的问题,但是,检索一条汉明距离小于给定阈值的simhash时间复杂度是O(n²) ,这在海量数据下使用的代价是昂贵的。
为了解决这个问题,可以采用空间换时间的思路,假定汉明距离<3时认为文档与给定文档相似;每一个simHash都从高位到低位均分成4段,每一段都是16位。
在建立倒排索引的过程中,这些截取出来的16位01串的片段,分别作为索引的key值,并将对应位置上具有这个片段的所有文本添加到这个索引的value域中。
直观上理解,首先有四个大桶,分别是1,2,3,4号(对应的是64位hash值中的第一、二、三、四段),在每一个大桶中,又分别有个小桶,这些小桶的编号从0000000000000000到1111111111111111.在建立索引时,每一个文本得到对应的simHash值后,分别去考察每一段(确定是1,2,3和4中的哪个大桶),再根据该段中的16位hash值,将文本放置到对应大桶中对应编号的小桶中。
索引建立好后,由于相似文本一定会存在于某一个16位hash值的桶中,因此针对这些分段的所有桶进行去重(可以并行做),便可以将文本集合中的所有相似文本去掉。
这样,检索速度最快为OO(1),最慢为O(n),远优于原本的O(n^2),缺点是空间膨胀到原来的n倍。通常n为4,是一个可以接受的膨胀倍率。
因此,我们把64位的01串分隔为4份,每份以key-list的结构存入redis中,当新的文章需要判断时,则分四段分别到索引中查找。
private void buildContenIndex(String docId, String simHash, String title, String url, String content_index_name, String eid, String oid) {
long storageTime = System.*currentTimeMillis*();
String simHashFragment1 = simHash.substring(0, 16);
String simHashFragment2 = simHash.substring(16, 32);
String simHashFragment3 = simHash.substring(32, 48);
String simHashFragment4 = simHash.substring(48, 64);
String redisKey1 = content_index_name + "_" + simHashFragment1;
String redisKey2 = content_index_name + "_" + simHashFragment2;
String redisKey3 = content_index_name + "_" + simHashFragment3;
String redisKey4 = content_index_name + "_" + simHashFragment4;
String value = docId + "\\001" + title + "\\001" + simHash + "\\001" + url + "\\001" + storageTime + "\\001" + eid;
NewRedisCrud.set2list(redisKey1, value, oid);
NewRedisCrud.set2list(redisKey2, value, oid);
NewRedisCrud.set2list(redisKey3, value, oid);
NewRedisCrud.set2list(redisKey4, value, oid);
}
内容去重有很多应用场景,simhash作为谷歌选来作为网页内容去重的一种算法,在海量数据去重的效率上有着明显的速度优势,相对传统文本相似性方法,simhash的降维解决了计算量庞大的问题,但对短文本的去重准确率上有较明显的欠缺,因此我们在了解业务的背景和需求后才能做出相对合理的选择。
本文由微信公众号搜狐技术产品原创,哈喽比特收录。
文章来源:https://mp.weixin.qq.com/s/ELUiPxvaRNBiNFwF-kxyeA
京东创始人刘强东和其妻子章泽天最近成为了互联网舆论关注的焦点。有关他们“移民美国”和在美国购买豪宅的传言在互联网上广泛传播。然而,京东官方通过微博发言人发布的消息澄清了这些传言,称这些言论纯属虚假信息和蓄意捏造。
日前,据博主“@超能数码君老周”爆料,国内三大运营商中国移动、中国电信和中国联通预计将集体采购百万台规模的华为Mate60系列手机。
据报道,荷兰半导体设备公司ASML正看到美国对华遏制政策的负面影响。阿斯麦(ASML)CEO彼得·温宁克在一档电视节目中分享了他对中国大陆问题以及该公司面临的出口管制和保护主义的看法。彼得曾在多个场合表达了他对出口管制以及中荷经济关系的担忧。
今年早些时候,抖音悄然上线了一款名为“青桃”的 App,Slogan 为“看见你的热爱”,根据应用介绍可知,“青桃”是一个属于年轻人的兴趣知识视频平台,由抖音官方出品的中长视频关联版本,整体风格有些类似B站。
日前,威马汽车首席数据官梅松林转发了一份“世界各国地区拥车率排行榜”,同时,他发文表示:中国汽车普及率低于非洲国家尼日利亚,每百户家庭仅17户有车。意大利世界排名第一,每十户中九户有车。
近日,一项新的研究发现,维生素 C 和 E 等抗氧化剂会激活一种机制,刺激癌症肿瘤中新血管的生长,帮助它们生长和扩散。
据媒体援引消息人士报道,苹果公司正在测试使用3D打印技术来生产其智能手表的钢质底盘。消息传出后,3D系统一度大涨超10%,不过截至周三收盘,该股涨幅回落至2%以内。
9月2日,坐拥千万粉丝的网红主播“秀才”账号被封禁,在社交媒体平台上引发热议。平台相关负责人表示,“秀才”账号违反平台相关规定,已封禁。据知情人士透露,秀才近期被举报存在违法行为,这可能是他被封禁的部分原因。据悉,“秀才”年龄39岁,是安徽省亳州市蒙城县人,抖音网红,粉丝数量超1200万。他曾被称为“中老年...
9月3日消息,亚马逊的一些股东,包括持有该公司股票的一家养老基金,日前对亚马逊、其创始人贝索斯和其董事会提起诉讼,指控他们在为 Project Kuiper 卫星星座项目购买发射服务时“违反了信义义务”。
据消息,为推广自家应用,苹果现推出了一个名为“Apps by Apple”的网站,展示了苹果为旗下产品(如 iPhone、iPad、Apple Watch、Mac 和 Apple TV)开发的各种应用程序。
特斯拉本周在美国大幅下调Model S和X售价,引发了该公司一些最坚定支持者的不满。知名特斯拉多头、未来基金(Future Fund)管理合伙人加里·布莱克发帖称,降价是一种“短期麻醉剂”,会让潜在客户等待进一步降价。
据外媒9月2日报道,荷兰半导体设备制造商阿斯麦称,尽管荷兰政府颁布的半导体设备出口管制新规9月正式生效,但该公司已获得在2023年底以前向中国运送受限制芯片制造机器的许可。
近日,根据美国证券交易委员会的文件显示,苹果卫星服务提供商 Globalstar 近期向马斯克旗下的 SpaceX 支付 6400 万美元(约 4.65 亿元人民币)。用于在 2023-2025 年期间,发射卫星,进一步扩展苹果 iPhone 系列的 SOS 卫星服务。
据报道,马斯克旗下社交平台𝕏(推特)日前调整了隐私政策,允许 𝕏 使用用户发布的信息来训练其人工智能(AI)模型。新的隐私政策将于 9 月 29 日生效。新政策规定,𝕏可能会使用所收集到的平台信息和公开可用的信息,来帮助训练 𝕏 的机器学习或人工智能模型。
9月2日,荣耀CEO赵明在采访中谈及华为手机回归时表示,替老同事们高兴,觉得手机行业,由于华为的回归,让竞争充满了更多的可能性和更多的魅力,对行业来说也是件好事。
《自然》30日发表的一篇论文报道了一个名为Swift的人工智能(AI)系统,该系统驾驶无人机的能力可在真实世界中一对一冠军赛里战胜人类对手。
近日,非营利组织纽约真菌学会(NYMS)发出警告,表示亚马逊为代表的电商平台上,充斥着各种AI生成的蘑菇觅食科普书籍,其中存在诸多错误。
社交媒体平台𝕏(原推特)新隐私政策提到:“在您同意的情况下,我们可能出于安全、安保和身份识别目的收集和使用您的生物识别信息。”
2023年德国柏林消费电子展上,各大企业都带来了最新的理念和产品,而高端化、本土化的中国产品正在不断吸引欧洲等国际市场的目光。
罗永浩日前在直播中吐槽苹果即将推出的 iPhone 新品,具体内容为:“以我对我‘子公司’的了解,我认为 iPhone 15 跟 iPhone 14 不会有什么区别的,除了序(列)号变了,这个‘不要脸’的东西,这个‘臭厨子’。