原来为了简单方便,我的小网站文章页面的相关内容推荐是从数据库中随机抽取数据填入一个列表,所以根本没有关联,也没有办法引导用户访问推荐内容。算法选择如何实现相似内容的推荐?因为小网站还是运行在虚拟主机上(是的,他们连自己完全可控的服务器都没有),所以想的办法不多,条件也仅限于只能用PHP+MySql。所以我想到的办法就是用Tags来匹配相似的文章进行推荐。如果两篇文章的TAGS比较相似,比如:A篇文章的TAGS是:[A,B,C,D,E]B篇文章的TAGS是:[A,D,E,F,G]文章C的TAGS是:[C,H,I,J,K]通过我们的眼睛,我们很容易发现文章B与文章A更相似,因为它们有相同的三个关键字:[A,D,E],如何使用计算机判断它们的相似度。这里我们使用jaccard相似度最基本的应用来计算它们的相似度。jaccard相似度给定两个集合A,B,Jaccard系数定义为A和B的交集的大小与A和B的交集的大小B的并集的大小之比,定义如下:文章A和文章B的交集为[A,D,E],大小为3,并集为[A,B,C,D,E,F,G],大小为7,3/7=0.4285...而文章A和文章C的交集为[C],大小为1,并集为[A,B,C,D,E,H,I,J,K],大小为9、1/9=0.11111...这样可以得出文章A和B比文章A和C更相似。通过这个算法,计算机可以判断两篇文章的相似度。具体推荐思路给定一篇文章,获取该文章的关键词TAGS,然后利用上述算法比较数据库中所有文章的相似度,得到最相似的N篇文章进行推荐。实现过程中第一个TAGS是通过TF-IDF算法获取文章的TAGS提取文章中的高频词,选取N个作为TAGS。对于中文文章,也存在中文分词的问题,因为是虚拟主机。这一步我用python(为什么用Python,jieba分词,真香)在本地写了一个程序,完成所有文章的分词,词频统计,生成TAGS,写回服务器的数据库。由于本文是写一个推荐算法,分词和建立TAGS的部分就不详细展开了,不同的系统有不同的建立TAGS的方式。第二个TAGS的存储创建了两张表,用于存储TAGStags,用于存储所有标签的名称+--------+------------+-------+-----+----------+------+|领域|类型|空|键|默认|额外|+--------+-----------+------+-----+--------+-------+|标签|文字|是||空||计数|大整数(20)|是||空|||标记|整数(11)|否|优先级|0||+------+------------+------+-----+--------+-------+tag_map建立tag和article的关系。+------------+------------+-----+-----+--------+-------+|领域|类型|空|键|默认|额外的|+------------+------------+------+-----+--------+------+|编号|大整数(20)|否|优先级|0|||文章编号|大整数(20)|是||空|||标记|整数(11)|是||空||+------------+------------+------+-----+--------+------+tag_map中存储的数据类似于:+----+-----------+--------+|编号|文章编号|tagid|+----+------------+------+|1|776|589|2|776|471||3|776|1455||4|776|1287||5|776|52||6|777|1386||7|777|588||8|777|109||9|777|第603章10|777|1299|+----+------------+--------+其实在做类似推荐的时候,只需要使用tag_map表就可以了,因为有tagid和标签名一一对应。具体编码1.获取所有文章对应的TAGIDmysql>selectarticleid,GROUP_CONCAT(tagid)astagsfromtag_mapGROUPBYarticleid;+------------+--------------------------+|文章编号|标签|+------------+--------------------------+|12|1178,1067,49,693,1227||13|196,2004,2071,927,131||14|1945,713,1711,2024,49||15|,9,1,1180||16|1182,1924,2200,181,1938||17|46,492,414,424,620||18|415,499,153,567,674||19|第1149章21|1953,1961,1534,2038,1393|+------------+-------------------------+通过上面的SQL,可以一次性查询到使用过的文章,对应的标签都是PHP的,我们可以把标签变成一个数组。publicfunctiongetAllGroupByArticleId(){//缓存查询数据,因为这是全表数据,文章不更新是不会变的,也就是说每次推荐都需要从数据库中获取一次数据,肯定会影响性能,所以这样做缓存。if($cache=CacheHelper::getCache()){返回$cache;}$query_result=$this->query('selectarticleid,GROUP_CONCAT(tagid)astagsfromtag_mapGROUPBYarticleid');$结果=[];foreach($query_resultas$key=>$value){//以articleid为key,value为id下所有tagID的数组。$result[$value['articleid']]=explode(",",$value['tags']);}CacheHelper::setCache($result,86400);返回$结果;}有了这个返回结果,就好办了,接下来就是应用jaccard相似度算法了,具体看代码吧。/***[根据指定文章返回相似文章推荐]*@param$articleid指定文章ID*@param$top返回推荐条数*@returnArray推荐条目数组*/functiongetArticleRecommend($articleid,$top=5){if($cache=CacheHelper::getCache()){返回$cache;}试试{$articleid=intval($articleid);$m=newTagMapModel();$all_tags=$m->getAllGroupByArticleId();//调用上面的函数返回所有文章的标签$founded=$all_tags[$articleid];//因为上面包含了所有文章,所以必须包含当前文章。unset($all_tags[$articleid]);//从数组中删除当前文章,否则你和你自己的相似度一定是最高的。$jaccard_arr=[];//用于存储相似度foreach($all_tagsas$key=>$value){$intersect=array_intersect($founded,$value);//计算交集$union=array_unique(array_merge($found,$value));//计算并集$jaccard_arr[$key]=(float)(count($intersect)/count($union));}arsort($jaccard_arr);//按相似度排序,最相似的排在最前面$jaccard_keys=array_keys($jaccard_arr);//由于数组的key是文章id,这里取出key即可$top);//得到前N篇推荐//这里我们得到了与N篇文章最相似的ID。接下来的工作就是通过这些ID从数据库中查询相关信息。$articleModels=new\Api\Model\ArticleModel();$recommendArticles=$articleModels->getRecommendByTag($jaccard_keys);CacheHelper::setCache($recommendArticles,604800);//缓存7天return$recommendArticles;}catch(\Exception$e){thrownew\Exception("获取推荐文章时出错");}}虽然简单,就几行代码,效果还是可以的是的,推荐的文章有一定的相似度,一定会带来更好的用户体验。例如,您可以查看https://www.wx2share.com/Arti...
