spark2

simhash

simhash

分词→hash→加权→合并→降维

两个simhash对应二进制(01串)取值不同的数量称为这两个simhash的海明距离(异或运算)

分表存储策略:

将一个64位的simhash签名拆分成4个16位的二进制码

编码

import org.apache.spark.sql.SparkSession
import org.apache.hadoop.conf.Configuration
import org.apache.hadoop.fs.{FileSystem, Path}

class Strawberry extends Serializable{
  
    var argsMap: Map[String,String] = _
    
    def run(args:Array[String]):Unit={
        
        //参数初始化
        arsMap = initArgs(args) //通过args(0)传参
        
        val ss = SparkSession.builder().enableHiveSupport().getOrCreate()
        
        try{
            val sparkContext=ss.sparkContext
            val outHdfs = org.apache.hadoop.fs.FileSystem.get(
                new java.net.URI("hdfs://apple"),
                new org.apache.hadoop.conf.Configuration())
            val blackWordSet:Set[String] = Set.empty[String]
            
            val blackWordFilePath=arsMap("blackWordFilePath")//比如hdfs://apple/blackWord.txt
            if(outHdfs.exists(new Path(blackWordFilePaht))){//判断hdfs文件是否存在
                //获取hdfs文件的最后修改时间
                val modifyTime=outHdfs.getFileStatus(new Path(blackWordFilePath)).getModificationTime
            }
            
            
            
            
        }finally{
            if(null != ss){
                ss.stop()
            }
        }
        
        
    }
    
    //传入的参数通过一个map统一管理
    def initArgs(args:Array[String]):Map[String,String]={
    	val argsMap=Map(
    	"appName" -> args(0),
    	"checkPoint" -> args(1),
   		"topics" -> args(2),
        "blackWordFilePath" -> args(3)
    	)
	}
    
    
}


import org.apache.hadoop.conf.Configuration
import org.apache.hadoop.fs.{FileSystem, Path}
/**
* 热加载:判断文件最后修改时间是否有变化
*/
val fs = FileSystem.get(new Configuration())
val inputPath="/apple.txt"
val input_files = fs.listStatus(new Path(inputPath))
var last_modification_time  = input_files.map(m => m.getModificationTime.toString).toList

val fs2 = FileSystem.get(new Configuration())
val input_files2 = fs.listStatus(new Path(input_files))

if (input_files2.map(m => m.getModificationTime.toString).toList != last_modification_time){
            //file update,do something
            last_modification_time = input_files2.map(m => m.getModificationTime.toString).toList// update ModificationTime
          }
class MyApple extends Serializable{
    var args: Array[String] = _
    private val DATA_INPUT_ARRAY=Array(
        ("name","jack")
        ,("age","12")
        ,("address","china")
    )
    
    def run(args:Array[String]):Unit ={
        val ss=SparkSession.builder().enableHiveSupport().getOrCreate()
        
        try{
            
            
            
            
            
            
        }finally{
            if(null !=ss){
                ss.stop()
            }
        }
    }
}

object MyApple{
    def main(args:Array[String]):Unit ={
        val myApple=new MyApple()
        myApple.run(args)
    }
}

编码优化

//对于很大的元组,可以使用case class或者 用数组定义字段

val ss = SparkSession.builder().enableHiveSupport().getOrCreate()
val sc = ss.sparkContext
val rdd1=sc.parallelize(List(("id01","jack",12,Set("piano","soccer")
                              ,Map("beijing"->"china","newyork"->"usa"))))

case class Person(id:String,name:String,age:Int
                  ,hobby:Set[String],address:Map[String,String])
//case class
val rdd2=rdd1.map(x => Person(x._1,x._2,x._3,x._4,x._5))
rdd2.map(p=>{
   p.id
   p.hobby
})

val PERSON_ARRAY=Array("id","name","age","hobby","address")
//数组按顺序定义字段,用indexof取位置,避免出现大量的x(0),x(1),难以理解
val rdd3=rdd1.map(x=>Array(x._1,x._2,x._3,x._4,x._5))
rdd3.map(x=>{
      x(PERSON_ARRAY.indexOf("id")) //等价x(0)
      x(PERSON_ARRAY.indexOf("hobby")) //等价x(3)
    })


//常用方法

//字符串 转 Long    
def stringToLongEmptyZero(ss: String): Long = {
        try {
            if (ss.isEmpty) {
                0L
            } else {
                ss.toLong
            }
        } catch {
            case e: Exception => 0L
        }
    }
// break 跳出循环

import scala.util.control.Breaks

val loop = new Breaks
loop.breakable{
    
    for(x <- aArr){
        if(flag==true){
            loop.break
        }else{
            //do something
        }    
    }
    
}


系统结构

冷启动用户:冷启动召回策略

不活跃重新活跃(非冷启动用户):短时兴趣和长时兴趣;但短时兴趣已经衰减很弱

长时兴趣召回策略:
兴趣模型相对固定
更新召回列表两个动机:①用户刷新频繁,召回列表的内容很快被推送完毕
②从召回列表生成已有比较长的时间,需要重新生成召回列表来体现新增内容

热搜名称+频道+热搜热度值+来源+获取时间

平均阅读速度: 900字/分钟=15字/秒
标准时长=总字数/平均阅读速度 +图片数*5
平均阅读时长=总阅读时长/点击数

文章质量分: (1-标题党异常分+exp(-用户投诉反馈次数和) +平均阅读时长/标准时长+CTR)/4

内容标签:

通过TF-IDF生成原始关键词和关键词对应tf-idf值,
用TextRank算法计算关键词和权重,对TF-IDF值进行加权

内容多样性: 偏好分频道存,在推荐策略中推荐多个频道,主题多样化,频道间要岔开

相似用户标签的召回策略:

根据聚类的用户分群,寻找一个用户相类似的用户群的标签,映射到文章,做召回推荐

基于用户协同的召回策略:

对一批流式用户做协同过滤生成召回推荐-根据与用户A特征最相似的N个用户,根据其他用户的兴趣来给用户A做推荐生成召回列表

热点惩罚:

热门推荐中,用户对某一类热点总是不点(或点击少到一定值)

兴趣试探与行为反馈存储:

Bandit类算法,对用户兴趣进行试探,快速反馈,得到用户超短期兴趣偏好

#############

########

这部分用的标签指关键词

非冷启动用户8个召回策略:

兴趣召回:

用户兴趣模型中取不超过3个兴趣标签,根据标签从内容倒排抽取30篇;

进行已推过滤,再每个标签匹配分数取top10

列表之间去重

对每个标签保留的文章进行ctr打分

兴趣扩展:

用户兴趣模型中抽取2个二级类别,根据二级类别标签从内容倒排抽取文章30篇;

进行已推过滤,再每个标签匹配分数取top10

列表之间去重

对每个标签保留的文章进行ctr打分

兴趣探索:

从用户兴趣模型之外的二级类别做探索,选定两个二级类别;

根据二级类别从内容倒排抽取文章,每个二级类别20篇;

进行已推过滤,按标签-文章分数取top10;

列表之间去重

ctr打分

兴趣协同:

根据用户所属群体,读取群体标签;

从协同标签集合中抽取至多1个协同标签;每个标签重内容倒排抽取20篇文章

进行已推过滤,按标签-文章分数取top10;

列表之间去重

ctr打分

地域召回:

根据用户请求LocalCode提取整个地域候选列表,进行已推过滤,按发布时间取top10;

列表间去重;

对文章ctr打分;

热点召回:

提取整个热门候选列表,进行已推过滤,按发布时间取top10;

列表间去重;

对文章ctr打分;

相似召回:

针对上一刷有点击/收藏/转发/分享行为的每篇文章,从相似内容列表按相似度随机抽取相似文章,各取30篇;

已推过滤,每个行为按相似度取top10;

列表之间进行去重;

对每个列表进行ctr打分;

默认召回:

兜底策略,如果其他召回无法凑齐10篇文章,则用默认召回补齐;

提取整个默认侯选池;

对默认召回 侯选池进行已推过滤后提取不超过30篇文章;

列表之间去重;

对保留文章进行ctr打分

默认侯选池是线下预先 生成的长度为1000的多样化文章集合

长时画像: 用户id, 标签|分值

生成布隆过滤器:

1.根据文章的发布时间从redis读取相应的bitmap

2.采用k个独立的hash算子处理NewsId生成k个偏移值

3.将bitmap总k个偏移值对应的bit位设置为1,结果写入redis

4.bitmap按周存储,发布时间一周的已推文章存储在一起;单独一个任务将超期(比如5周)的bitmap删除

已推过滤:

1.根据文章的发布时间读取redis相应的bitmap

2.采用k个独立的hash算子处理NewsId生成k个偏移值

3.检查bitmap中k个偏移值对应的bit位设置是否全为1,是则将文章从召回列表中删除

日历定期类事件的推荐召回

事件 分数 起始时间 终止时间
圣诞 3 2020/12/25 2020/12/25
春节 10 2020/2/3 2020/2/10

标题与内容相关度:

利用Word2vec计算文章标题与内容相似度, 长文章采取文章摘要

用户画像标签体系

标签是某一种用户特征的符号表示

标签级别:

事实标签:通过对原始数据库的数据进行统计分析而来的

模型标签:以事实标签为基础,通过构建实际标签与业务问题之间的模型,进行模型分析得到

预测标签:在模型的基础上做预测

标签体系结构

1610936103617

读书笔记 |《推荐系统实践》- 个性化推荐系统总结

https://www.jianshu.com/p/319e4933c5ba

报错总结

当topic中,开始推数据的时候或者数据因为时间消失时,消费若没数据消失的速度快,则会报错:

image

原因: org.apache.kafka.clients.consumer.offsetOutofRangeException:Offsets out of range with no configured reset policy for partition:{xxx}
当我们的任务开始的时候,如果之前消费过某个topic,那么这个topic会在zk上设置offset,我们一般会去获取这个offset来继续从上次结束的地方继续消费,但是kafka定时清理日志的功能,比如定时一天一清理,那么如果你的offset是前天消费的offset,那么这个时候你再去消费,自然而然的你的offset肯定已经不在有效范围内,所以就报OffsetOutOfRangeException了

解决方法就是判断一下zk中的offset是否小于topic最小的offset,如果小于的话,就把最小的offset设置到zk中

处理方法:
停掉任务,手动设置偏移量 image

image 当前有两个方法 1) 重新用一个新的groupid 然后 offset设置为latest 2) 用上面的命令 将已有的groupid 强制设置offset为latest 然后直接启动就ok

image 这个可以查出来 开始的 和结束的offset 然后强制设置下

但由于我们要消费全部日志,则可查看最小offset,将current offset设置成最小offset:

查看最小offset:

image

设置成最小offset:

image

待验证:是否无需最开始的那些操作,直接修改current offset为最小offset,即可实现kafka消息的重新消费。

推荐系统

基尼系数:

马太效应: 强者越强,弱者更弱

主流的推荐算法都有马太效应

新颖度: 喜欢周星驰,推荐了他一部早期鲜为人知的电影

惊喜度: 推荐了一部和周星驰没有关系的电影,你觉得很不错,这是惊喜度

预测准确度度量一个推荐系统或者推荐算法预测用户行为的能力

网站在提供推荐服务时,一般是给用户一个个性化的推荐列表,这种推荐叫做TopN推荐。 TopN推荐的预测准确率一般通过准确率(precision)/召回率(recall)度量

二.利用用户行为数据

基于用户行为分析的推荐算法是个性化推荐系统的重要算法,学术界一般将这种类型的算法称之为协同过滤算法.顾名思义,协同过滤就是指用户可以齐心协力,通过不断地和网站互动,使自己的推荐列表能够不断过滤掉自己不敢兴趣的物品,从而越来越满足自己的需求.

显性反馈行为

隐性反馈行为

1611629067815

1611629096490

隐语义模型(latent factor model)

基于图的随机有组算法(random walk on graph)

基于领域的方法(neighborhood-based) :

获得最广泛应用,主要分为两大类:

	基于用户的协同过滤算法

	基于物品的系统过滤算法

基于用户的协同过滤算法:

1.找到和目标用户兴趣相似的用户集合

2.找到集合中得用户喜欢的,且目标用户没有听说过的物品推荐给目标用户

关键是计算两个用户的兴趣相似度: 余弦相似度

1611630001891

对两两用户都计算相似度,时间复杂度高,用户基数大则非常耗时

物品-用户的倒排表,只对同时属于倒排表中k个物品的两个用户做计算

每个用户选出K个和他兴趣最相似的用户,然后推荐那K个用户感兴趣的物品

准确率和召回率: 选择合适K提高推荐的精度

流行度: K越大,推荐的就越热门,因为参考的人越多,结果就越趋近于全局热门物品

覆盖率: K越大覆盖率越低,因为k越大流行度越高,所以对长尾物品推荐越少

基于物品的协同过滤算法

计算物品之间的相似度

根据物品的相似度和用户的历史行为给用户生成推荐列表

用户—物品倒排表

物品相似度归一化:

增加推荐的准确度,提高推荐的覆盖率和多样性

UserCF的推荐结果着重于反映和用户兴趣相似的小群体的热点

ItemCF 的推荐结果着重于维系用户的历史兴趣

在新闻网站中,用户的兴趣不是特别细化,绝大多数用户都喜欢看热门的新闻.即使是个性 化,也是比较粗粒度的,比如体育,娱乐;个性化新闻推荐更加强调抓住 新闻热点,热门程度和时效性是个性化新闻推荐的重点,而个性化相对于这两点略显次要。因 此,UserCF可以给用户推荐和他有相似爱好的一群其他用户今天都在看的新闻,这样在抓住热 点和时效性的同时,保证了一定程度的个性化

用户没有过行为的物品叫做负样本

负样本采样:

对每个用户,要保证负样本的平衡(数目相似)

负样本采样时,选取哪些很热门,而用户却没有行为的物品

一般认为,很热门而用户却没有行为更加代表用户对这个物品不感兴趣。因为对于冷门的物
品,用户可能是压根没在网站中发现这个物品,所以谈不上是否感兴趣

冷启动问题:

标签:

一种无层次化结构的用来描述信息的关键词

标签应用分为两种:

让作者或专家给物品打标签;

让普通用户给物品打标签,UGC(User Generated Content)

排名模块:

1.新颖性:

给用户推荐他们不知道的长尾中的物品	

2.多样性:

推荐结果中覆盖多种类型

3.时间多样性:

保证推荐系统的实时性,在用户新行为实时调整推荐结果以满足用户最近的需求

如果用户没有新行为,今天推荐结果列表中,把昨天或者更长时间看到的推荐结果降权

回顾总结

posted @ 2021-02-05 14:08  等木鱼的猫  阅读(127)  评论(0编辑  收藏  举报