简历项目

2021-05-21 10:03:11 浏览数 (1)

项目一:电商广告推荐系统

  • 离线推荐
    • 对召回结果排序
    • 为每一个用户都进行召回并排序,把排好顺序的结果放到数据库中
    • 如果需要推荐结果的时候,直接到数据库中按照user_id查询,返回推荐结果
    • 优点:结构比较简单,推荐服务只需要不断计算,把结果保存到数据库中即可
    • 缺点 实时性差,如果数据1天不更新 1天之内推荐结果一样的,不能反映用户的实时兴趣
  • 实时推荐
    • 排序模型加载好
    • 召回阶段的结果缓存
    • 所有用户的特征缓存
    • 所有物品的特征缓存
    • 把推荐的服务暴露出去(django flask)需要推荐结果的服务 把用户id传递过来
      • 根据id找到召回结果
      • 根据id找到缓存的用户特征
      • 根据召回结果的物品Id 找到物品的特征
      • 用户特征 物品特征-》逻辑回归模型 就可以预测点击率
      • 所有召回的物品的点击率都预测并排序 推荐topN
      • 实时通过LR模型进行排序的好处:
        • 随时修改召回集
        • 随时调整用户的特征
        • 当用户需要推荐服务的时候,获取到最新的召回集合用户特征 得到最新的排序结果 更能体现出用户的实时兴趣

一、数据集

  • 原始样本骨架 raw_sample 淘宝网站中随机抽样了114万用户8天内的广告展示/点击日志(2600万条记录),构成原始的样本骨架。 字段说明如下:
    1. user_id:脱敏过的用户ID;
    2. adgroup_id:脱敏过的广告单元ID;
    3. time_stamp:时间戳;
    4. pid:资源位;
    5. noclk:为1代表没有点击;为0代表点击;
    6. clk:为0代表没有点击;为1代表点击;

    用前面7天的做训练样本(20170506-20170512),用第8天的做测试样本(20170513)

  • 广告基本信息表ad_feature 本数据集涵盖了raw_sample中全部广告的基本信息(约80万条目)。字段说明如下:
    1. adgroup_id:脱敏过的广告ID;
    2. cate_id:脱敏过的商品类目ID;
    3. campaign_id:脱敏过的广告计划ID;
    4. customer_id: 脱敏过的广告主ID;
    5. brand_id:脱敏过的品牌ID;
    6. price: 宝贝的价格

    其中一个广告ID对应一个商品(宝贝),一个宝贝属于一个类目,一个宝贝属于一个品牌。

  • 用户基本信息表user_profile 本数据集涵盖了raw_sample中全部用户的基本信息(约100多万用户)。字段说明如下:
    1. userid:脱敏过的用户ID;
    2. cms_segid:微群ID;
    3. cms_group_id:cms_group_id;
    4. final_gender_code:性别 1:男,2:女;
    5. age_level:年龄层次; 1234
    6. pvalue_level:消费档次,1:低档,2:中档,3:高档;
    7. shopping_level:购物深度,1:浅层用户,2:中度用户,3:深度用户
    8. occupation:是否大学生 ,1:是,0:否
    9. new_user_class_level:城市层级
  • 用户的行为日志behavior_log 本数据集涵盖了raw_sample中全部用户22天内的购物行为(共七亿条记录)。字段说明如下: user:脱敏过的用户ID; time_stamp:时间戳; btag:行为类型, 包括以下四种: ​ 类型 | 说明 ​ pv | 浏览 ​ cart | 加入购物车 ​ fav | 喜欢 ​ buy | 购买 cate_id:脱敏过的商品类目id; brand_id: 脱敏过的品牌id; 这里以user time_stamp为key,会有很多重复的记录;这是因为我们的不同的类型的行为数据是不同部门记录的,在打包到一起的时候,实际上会有小的偏差(即两个一样的time_stamp实际上是差异比较小的两个时间)

项目实现分析

主要包括

  • 一份广告点击的样本数据raw_sample.csv:体现的是用户对不同位置广告点击、没点击的情况
  • 一份广告基本信息数据ad_feature.csv:体现的是每个广告的类目(id)、品牌(id)、价格特征
  • 一份用户基本信息数据user_profile.csv:体现的是用户群组、性别、年龄、消费购物档次、所在城市级别等特征
  • 一份用户行为日志数据behavior_log.csv:体现用户对商品类目(id)、品牌(id)的浏览、加购物车、收藏、购买等信息

  1. 推荐业务处理主要流程: 召回 ===> 排序 ===> 过滤
    • 离线处理业务流
      • raw_sample.csv ==> 历史样本数据
      • ad_feature.csv ==> 广告特征数据
      • user_profile.csv ==> 用户特征数据
      • raw_sample.csv ad_feature.csv user_profile.csv ==> CTR点击率预测模型(逻辑回归模型)
      • behavior_log.csv ==> 评分数据 ==> user-cate/brand评分数据 ==> 协同过滤召回 ==>逻辑回归 排序top-N cate/brand ==> 关联广告
      • 协同过滤召回 ==> top-N cate/brand ==> 关联对应的广告完成召回 (为每个用户召回关联的广告)
    • 在线处理业务流
      • 数据处理部分:
        • 实时行为日志 ==> 实时特征 ==> 缓存
        • 实时行为日志 ==> 实时商品类别/品牌 ==> 实时广告召回集 ==> 缓存
      • 推荐任务部分:
        • CTR点击率预测模型(离线训练好的) 广告/用户特征(缓存)(离线召回了一些) 对应的召回集(缓存) ==> 点击率排序 ==> top-N 广告推荐结果
  2. 涉及技术:Flume、Kafka、Spark-stremingHDFS、Spark SQL、Spark ML、Redis
    • Flume:日志数据收集
    • Kafka:实时日志数据处理队列
    • HDFS:存储数据
    • Spark SQL:离线处理
    • Spark ML:模型训练
    • Redis:缓存

二、根据用户行为数据创建ALS模型并召回商品

用户行为数据拆分(pv,fav,cart,buy)

分批处理,chunksize=100

预处理behavior_log数据集

  • 创建spark session
  • 从hdfs中加载csv文件为DataFrame
  • 从hdfs加载数据为dataframe,并设置结构
代码语言:javascript复制
from pyspark.sql.types import StructType, StructField, StringType, IntegerType, LongType
# 构建结构对象
schema = StructType([
    StructField("userId", IntegerType()),
    StructField("timestamp", LongType()),
    StructField("btag", StringType()),
    StructField("cateId", IntegerType()),
    StructField("brandId", IntegerType())
])
# 从hdfs加载数据为dataframe,并设置结构
behavior_log_df = spark.read.csv("hdfs://localhost:8020/datasets/behavior_log.csv", header=True, schema=schema)
behavior_log_df.show()
behavior_log_df.count() 
  • 分析数据集字段的类型和格式

  1. 查看是否有空值
代码语言:javascript复制
print("判断数据是否有空值:", behavior_log_df.count(), behavior_log_df.dropna().count())
# 约7亿条目723268134 723268134
# 本数据集无空值条目,可放心处理

  1. 查看每列数据的类型
  2. 查看每列数据的类别情况
代码语言:javascript复制
print("查看userId的数据情况:", behavior_log_df.groupBy("userId").count().count())
# 约113w用户
print("查看btag的数据情况:", behavior_log_df.groupBy("btag").count().collect())    # collect会把计算结果全部加载到内存,谨慎使用
  • 统计每个用户对各个品牌的pv、fav、cart、buy数量并保存结果

pivot透视操作,把某列里的字段值转换成行并进行聚合运算(pyspark.sql.GroupedData.pivot)

代码语言:javascript复制
 # 统计每个用户对各类商品的pv、fav、cart、buy数量
cate_count_df = behavior_log_df.groupBy(behavior_log_df.userId, behavior_log_df.cateId).pivot("btag",["pv","fav","cart","buy"]).count()
cate_count_df.printSchema()    # 此时还没有开始计算

根据用户对类目偏好打分训练ALS模型

  • 根据您统计的次数 打分规则 ==> 偏好打分数据集 ==> ALS模型
代码语言:javascript复制
# spark ml的模型训练是基于内存的,如果数据过大,内存空间小,迭代次数过多的化,可能会造成内存溢出,报错
# 设置Checkpoint的话,会把所有数据落盘,这样如果异常退出,下次重启后,可以接着上次的训练节点继续运行
# 但该方法其实指标不治本,因为无法防止内存溢出,所以还是会报错
# 如果数据量大,应考虑的是增加内存、或限制迭代次数和训练数据量级等
spark.sparkContext.setCheckpointDir("hdfs://localhost:8020/checkPoint/")
#设置检查点,避免迭代训练的过程中 挂掉,训练几步缓存当前的参数,如果挂掉了可以从检查点加载缓存
  • 处理每一行数据
代码语言:javascript复制
def process_row(r):
    # 处理每一行数据:r表示row对象
    
    # 偏好评分规则:
	# m: 用户对应的行为次数
    # 该偏好权重比例,次数上限仅供参考,具体数值应根据产品业务场景权衡
	# pv: if m<=20: score=0.2*m; else score=4
	# fav: if m<=20: score=0.4*m; else score=8
	# cart: if m<=20: score=0.6*m; else score=12
	# buy: if m<=20: score=1*m; else score=20
    
    # 注意这里要全部设为浮点数,spark运算时对类型比较敏感,要保持数据类型都一致
	pv_count = r.pv if r.pv else 0.0
	fav_count = r.fav if r.fav else 0.0
	cart_count = r.cart if r.cart else 0.0
	buy_count = r.buy if r.buy else 0.0

	pv_score = 0.2*pv_count if pv_count<=20 else 4.0
	fav_score = 0.4*fav_count if fav_count<=20 else 8.0
	cart_score = 0.6*cart_count if cart_count<=20 else 12.0
	buy_score = 1.0*buy_count if buy_count<=20 else 20.0

	rating = pv_score   fav_score   cart_score   buy_score
	# 返回用户ID、分类ID、用户对分类的偏好打分
	return r.userId, r.cateId, rating
  • 返回一个PythonRDD类型
  • 用户对商品类别的打分数据cate_rating_df
  • 基于Spark的ALS隐因子模型进行CF评分预测

ALS模型 是一种基于模型的推荐算法,基于最小二乘法对稀疏矩阵进行分解,可以依照分解的两个矩阵,对新的用户和物品数据进行评估。分解的两个矩阵的隐因子,可以看做是用户或物品的隐含特征,例如可以是用户的性格、教育程度、爱好等。 参考:为什么Spark中只有ALS 高度易并行化的——它的每个子任务之间没有什么依赖关系 显式:

隐式:

在隐反馈模型中是没有评分的,所以在式子中rui被pui所取代,pui是偏好的表示,仅仅表示用户和物品之间有没有交互,而不表示评分高低或者喜好程度。比如用户和物品之间有交互就让pui等于1,没有就等于0。函数中还有一个cui的项,它用来表示用户偏爱某个商品的置信程度,比如交互次数多的权重就会增加。

  • 模型训练好后,调用方法进行使用,为每个用户推荐topn
代码语言:javascript复制
# model.recommendForAllUsers(N) 给所有用户推荐TOP-N个物品 召回
ret = model.recommendForAllUsers(3)
# 由于是给所有用户进行推荐,此处运算时间也较长
ret.show()
# 推荐结果存放在recommendations列中,
ret.select("recommendations").show()
  • 召回到redis中
代码语言:javascript复制
import redis
host = "192.168.19.8"
port = 6379    
# 召回到redis
def recall_cate_by_cf(partition):
    # 建立redis 连接池
    pool = redis.ConnectionPool(host=host, port=port)
    # 建立redis客户端
    client = redis.Redis(connection_pool=pool)
    for row in partition:
        client.hset("recall_cate", row.userId, [i.cateId for i in row.recommendations])
# 对每个分片的数据进行处理 #mapPartition Transformation map
# foreachPartition Action操作 foreachRDD
result.foreachPartition(recall_cate_by_cf)

# 注意:这里这是召回的是用户最感兴趣的n个类别
# 总的条目数,查看redis中总的条目数是否一致
result.count()

根据用户对品牌偏好打分训练ALS模型

模型存在HDFS上

代码语言:javascript复制
# 将模型进行存储
model.save("hdfs://localhost:9000/models/userBrandRatingModel.obj")
# 测试存储的模型
from pyspark.ml.recommendation import ALSModel
# 从hdfs加载模型
my_model = ALSModel.load("hdfs://localhost:9000/models/userBrandRatingModel.obj")
my_model
# model.recommendForAllUsers(N) 给用户推荐TOP-N个物品
my_model.recommendForAllUsers(3).first()

三. CTR预估数据准备

分析并预处理raw_sample数据集

  • 从HDFS中加载样本数据信息
  • 分析数据集字段的类型和格式

  • 查看是否有空值
  • 查看每列数据的类型
  • 查看每列数据的类别情况
  • 使用dataframe.withColumn更改df列数据结构;使用dataframe.withColumnRenamed更改列名称
  • 特征选取

只有广告展示位pid对比较重要,且数据不同数据之间的占比约为6:4,因此pid可以作为一个关键特征 nonclk和clk在这里是作为目标值,不做为特征

  • Spark中使用独热编码

热编码只能对字符串类型的列数据进行处理 StringIndexer对指定字符串列数据进行特征处理,如将性别数据“男”、“女”转化为0和1 OneHotEncoder对特征列数据,进行热编码,通常需结合StringIndexer一起使用 Pipeline让数据按顺序依次被处理,将前一次的处理结果作为下一次的输入

【引申】用Embedding解决特征过多的问题: 如果特征过多,用独热编码,将会造成大量稀疏向量。采用embedding转成低维稠密向量,类似于word2vec的词向量。 [Word2vec] 包括skip-gram(给出词预测上下文)和CBOW(给出上下文预测词)两种训练模式。Softmax层优化方法:①分层softmax:类似树形分类器,每个节点都可以是一个二分类器。常用词在顶部,类似哈夫曼树。②负采样:上下文词和目标词构成正样本;用相同的上下文词,再在字典找那个随机选一个词,标记为0. [embedding]生成方法:①矩阵分解和因子分解机②利用word2vec方法③node2vec伪交互行为方法

  • 训练样本和测试样本

本样本数据集共计8天数据 前七天为训练数据、最后一天为测试数据

  1. 以时间戳划分,用filter函数
代码语言:javascript复制
from datetime import datetime
datetime.fromtimestamp(1494691186)
print("该时间之前的数据为训练样本,该时间以后的数据为测试样本:", datetime.fromtimestamp(1494691186-24*60*60))

该时间之前的数据为训练样本,该时间以后的数据为测试样本: 2017-05-12 23:59:46
代码语言:javascript复制
# 训练样本:
train_sample = raw_sample_df.filter(raw_sample_df.timestamp<=(1494691186-24*60*60))
print("训练样本个数:")
print(train_sample.count())
# 测试样本
test_sample = raw_sample_df.filter(raw_sample_df.timestamp>(1494691186-24*60*60))
print("测试样本个数:")
print(test_sample.count())

训练样本个数: 23249291 测试样本个数: 3308670

分析并预处理ad_feature数据集(空值NULL->-1)

  • HDFS中加载广告基本信息
代码语言:javascript复制
# 注意:由于本数据集中存在NULL字样的数据,无法直接设置schema,只能先将NULL类型的数据处理掉,然后进行类型转换
# 替换掉NULL字符串,替换掉
df = df.replace("NULL", "-1")
  • 查看各项数据的特征
  • 特征选择

只选取price作为特征数据,因为价格本身是一个统计类型连续数值型数据,且能很好的体现广告的价值属性特征,通常也不需要做其他处理(离散化、归一化、标准化等),所以这里直接将当做特征数据来使用

分析并预处理user_profile数据集(null)——随机森林——困难

代码语言:javascript复制
# 注意:这里的null会直接被pyspark识别为None数据,也就是na数据,所以这里可以直接利用schema导入数据
  • 缺失值处理

  • 注意,一般情况下:
    • 缺失率低于10%:可直接进行相应的填充,如默认值、均值、算法拟合等等;
    • 高于10%:往往会考虑舍弃该特征
    • 特征处理,如1维转多维(也就是将缺失作为一个特征

    但根据我们的经验,我们的广告推荐其实和用户的消费水平、用户所在城市等级都有比较大的关联,因此在这里pvalue_level、new_user_class_level都是比较重要的特征,我们不考虑舍弃

  • 缺失值处理方案:
    • 填充方案:结合用户的其他特征值,利用随机森林算法进行预测;但产生了大量人为构建的数据,一定程度上增加了数据的噪音
    • 把变量映射到高维空间(把缺失值当做单独的一类处理):如pvalue_level的1维数据,转换成是否1、是否2、是否3、是否缺失的4维数据;这样保证了所有原始数据不变,同时能提高精确度,但这样会导致数据变得比较稀疏,如果样本量很小,反而会导致样本效果较差,因此也不能滥用
  • 利用随机森林对缺失值预测
代码语言:javascript复制
from pyspark.mllib.regression import LabeledPoint

# 剔除掉缺失值数据,将余下的数据作为训练数据
# user_profile_df.dropna(subset=["pvalue_level"]): 将pvalue_level中的空值所在行数据剔除后的数据,作为训练样本
train_data = user_profile_df.dropna(subset=["pvalue_level"]).rdd.map(
    lambda r:LabeledPoint(r.pvalue_level-1, [r.cms_segid, r.cms_group_id, r.final_gender_code, r.age_level, r.shopping_level, r.occupation])
)

# 筛选出缺失值条目,作为预测样本
pl_na_df = user_profile_df.na.fill(-1).where("pvalue_level=-1")
  • 与非缺失数据进行拼接,完成缺失值预测
代码语言:javascript复制
new_user_profile_df = user_profile_df.dropna(subset=["pvalue_level"]).unionAll(spark.createDataFrame(pdf, schema=schema))
new_user_profile_df.show()

# 注意:unionAll的使用,两个df的表结构必须完全一样

困难点: 利用随机森林对new_user_class_level的缺失值进行预测 可以发现由于这两个字段的缺失过多,所以预测出来的值已经大大失真,但如果缺失率在10%以下,这种方法是比较有效的一种 解决办法: 低维转高维方式 我们接下来采用将变量映射到高维空间的方法来处理数据,即将缺失项也当做一个单独的特征来对待,保证数据的原始性 由于该思想正好和热独编码实现方法一样,因此这里直接使用热独编码方式处理数据

代码语言:javascript复制
# 使用热独编码转换pvalue_level的一维数据为多维,其中缺失值单独作为一个特征值

# 需要先将缺失值全部替换为数值,与原有特征一起处理
from pyspark.sql.types import StringType
user_profile_df = user_profile_df.na.fill(-1)
user_profile_df.show()

# 热独编码时,必须先将待处理字段转为字符串类型才可处理
user_profile_df = user_profile_df.withColumn("pvalue_level", user_profile_df.pvalue_level.cast(StringType()))
    .withColumn("new_user_class_level", user_profile_df.new_user_class_level.cast(StringType()))
user_profile_df.printSchema()

# 对pvalue_level进行热独编码,求值
stringindexer = StringIndexer(inputCol='pvalue_level', outputCol='pl_onehot_feature')
encoder = OneHotEncoder(dropLast=False, inputCol='pl_onehot_feature', outputCol='pl_onehot_value')
pipeline = Pipeline(stages=[stringindexer, encoder])
pipeline_fit = pipeline.fit(user_profile_df)
user_profile_df2 = pipeline_fit.transform(user_profile_df)
# pl_onehot_value列的值为稀疏向量,存储热独编码的结果
user_profile_df2.printSchema()
user_profile_df2.show()
  • 用户特征合并 VectorAssembler将多个数值列按顺序汇总成一个向量列。
代码语言:javascript复制
from pyspark.ml.feature import VectorAssembler
feature_df = VectorAssembler().setInputCols(["age_level", "pl_onehot_value", "nucl_onehot_value"]).setOutputCol("features").transform(user_profile_df3)
feature_df.show()
  • 特征选取(9个)

  • cms_segid: 97
  • cms_group_id: 13
  • final_gender_code: 2
  • age_level: 7
  • shopping_level: 3
  • occupation: 2 -pvalue_level -new_user_class_level -price

四. LR实现CTR预估(12个特征)——改进

根据广告点击样本数据集(raw_sample)、广告基本特征数据集(ad_feature)、用户基本信息数据集(user_profile)构建出了一个完整的样本数据集,并按日期划分为了训练集(前七天)和测试集(最后一天),利用逻辑回归进行训练。

Dataframe数据合并:pyspark.sql.DataFrame.join

代码语言:javascript复制
# raw_sample_df和ad_feature_df合并条件
condition = [raw_sample_df.adgroupId==ad_feature_df.adgroupId]
_ = raw_sample_df.join(ad_feature_df, condition, 'outer')

# _和user_profile_df合并条件
condition2 = [_.userId==user_profile_df.userId]
datasets = _.join(user_profile_df, condition2, "outer")
# 查看datasets的结构
datasets.printSchema()
# 查看datasets条目数
print(datasets.count())

按probability升序排列数据,probability表示预测结果的概率 如果预测值是0,其概率是0.9248,那么反之可推出1的可能性就是1-0.9248=0.0752,即点击概率约为7.52% 因为前面提到广告的点击率一般都比较低,所以预测值通常都是0,因此通常需要反减得出点击的概率

改进:进一步提升训练精确度,将类别特征转为多维特征,提高特征空间的维度, 类别性特征都可以考虑进行热独编码,将单一变量变为多变量,相当于增加了相关特征的数量

五. 离线推荐数据缓存

这里主要是利用我们前面训练的ALS模型进行协同过滤召回

如果是离线,为每个用户召回排序的结果存在redis中,线上服务需求推荐时,在redis中调取出来; 优点:操作简单 缺点:不重新算的话 数据库中数据不变,实时性不好

如果是在线的话,获取到用户id,到数据库中找到用户特征,找到所有商品的特征,将用户特征和商品特征送入逻辑回归模型中计算点击率,做排序 若用户对于推荐的某物品 累计几次都没看 则将此物品从召回集中删除,实时的影响到召回结果 若用户近期对于某些物品点击的多,也可以实时的更新用户特征(例如消费档次。。。

六. 实时产生推荐结果

CTR预测模型 特征 ==> 预测结果 ==> TOP-N列表

七、推荐算法

协同过滤(召回)

1.基于用户的:为用户推荐和他兴趣相似的其他用户喜欢的物品 (1)首先根据用户对物品的打分情况,计算用户与其他用户的相似程度,找出最相似的n个用户 (2)根据这n个用户对此物品的评分情况以及用户相似性程度可以得出用户对物品的评分。如果评分较高,则推荐。 2.基于物品的:为用户推荐和他之前喜欢的物品相似的物品 (1)计算物品之间的相似度。 (2)根据物品的相似度和用户的历史行为给用户生成推荐列表。 3.计算两个向量之间的相似程度 (1)杰卡德相似系数:两个集合的交集占并集的比例 (2)余弦相似度:向量内积/向量2范数乘积 (3)皮尔逊相关系数:减平均值 4.应用场景 UserCF:适用于用户少、物品多、时效性较强的场合(新闻推荐) ItemCF: 适用于物品少、用户多、用户兴趣固定的场合。 5.缺陷: (1)泛化能力弱,热门物品具有很强的头部效应,容易跟大量物品产生相似,而尾部物品由于特征向量稀疏,导致很少被推荐;【矩阵分解技术,在协同过滤共现矩阵的基础上,使用更稠密的隐向量表示用户和物品,挖掘用户和物品的隐含兴趣和隐含特征,弥补协同过滤模型处理稀疏矩阵能力不足的问题。】 (2)仅利用了用户与物品的交互信息,没有利用到物品本身和用户本身的属性【以逻辑回归模型为核心的推荐模型,引用了更多的特征】

矩阵分解(召回)

  1. Funk-SVD/LFM:把求解两个矩阵的参数问题转换成一个最优化问题,利用均方误差损失函数,通过梯度下降法来降低损失。
  2. SVD :考虑用户的历史行为对用户评分预测的影响。

优点:由于隐向量的存在,使得任意的用户和物品之间都可以得到预测分值,而求解隐向量的过程其实是对评分矩阵进行全局拟合的过程,这个过程中考虑了所有的用户和评分,因此隐向量是利用全局信息生成的,有更强的泛化能力缺点:只用到了评分矩阵,没有考虑到用户特征、物品特征和上下文特征。【逻辑回归模型以及因子分解机模型可以解决。】

LR GBDT(排序)

FM、FFM

  1. FM:①原理:特征两两交叉,算权重w【缺点:稀疏;参数多】; 改进:用两个特征的隐向量拟合w矩阵 ②复杂度:直观上看复杂度是O(kn^2),但可优化到O(kn) ③优点:可以解决稀疏性问题,向量本身可能非常稀疏,但是将其投影到隐向量空间,变成低维稠密向量。 缺点:交叉特征比较浅层(仅限于2阶,再高不好实现),没有深层的含义,所以改进用deepFM(wide&deep架构)
  2. FFM: 用户属性与用户属性互动时,和用户与物品互动时的性质不同。引入特征域感知概念,对特征根据性质的不同进行分类,不同的分类就是不同的域。对于每个特征,针对不同的交叉域要学习不同的隐向量特征。 O(nfk),f是域的个数,O(kn^2)

Wide&deep

  1. 组合动机: 简单的模型,例如协同过滤、逻辑回归等,能够从历史数据中学习到高频共现的特征组合,但是泛化能力不足; 而像矩阵分解,embedding再加上深度学习网络,能够探索历史数据中未出现的特征组合,挖掘数据潜在的关联模式,但对于某些特定的场景(数据分布长尾,大部分query-item都没什么关系),会推荐过度泛化。结合在提高模型泛化能力的同时,兼顾模型的记忆性。
  2. wide: 广义线性模型,优化器:L1正则的FTRL算法,该算法想让wide部分变得更加稀疏,压缩模型权重及特征向量维度,使模型能够更好的实时服务。一般接收一些重要的交互特征,高维的稀疏离散特征。
  3. deep:Embedding MLP,大规模稀疏特征通过embedding转化为低维密集型特征,然后特征进行拼接输入到MLP(多层感知机:全连接层 激活层)中。接收的是一些连续特征。
  4. deep&cross:用cross network替换wide部分,来自动进行特征之间的交叉,并且网络的时间和空间复杂度都是线性的。

项目二: RNN步态认证

项目简介

项目简介:利用移动设备的内嵌传感器采集用户的步态信息,对当前用户进行步态认证。

  1. 首先通过移动设备上的传感器数据获取APP,分别获得用户在不同的步态场景(走路、跑步、上下楼梯)以及不同的设备位置下的加速度计、陀螺仪、磁力计数据。
  2. 对数据进行清洗(缺失值处理、过滤异常值、去噪),步态周期分割。为了减少移动设备方向对数据的干扰,将传感器数据进行坐标系的转换(设备坐标系转变到用户坐标系)。
  3. 用tensorflow训练LSTM和GRU步态认证模型(包括单层、双向、多层),采用Mini- batch训练法,使用Adam进行优化,采用Dropout技术防止过拟合。
  4. 绘制ROC曲线分析认证结果,认证EER为3.11%。

坐标系转换 用户坐标系:X轴指向上,Y轴为运动方向,Z轴为横向。 X轴:重力过滤 Y轴:PCA降维,找到方差最大的方向 Z轴:与X轴和Y轴垂直

LSTM、GRU

LSTM:遗忘门、输入门、输出门、还包括细胞状态

  1. 遗忘门:决定丢弃或保留哪些信息,将前一个隐藏状态的信息和当前输入的信息同时传递到sigmoid函数中去,输出值介于0-1之间,越接近0代表越应该丢弃,越接近1代表越应该保留。
  2. 输入门:输入门用于更新细胞状态,首先将前一层隐藏状态的信息和当前输入的信息传递到sigmoid函数中去,将值调整到0-1之间,来决定要更新哪些信息,0表示不重要,1表示重要。其次,还需要将前一层隐藏状态的信息和当前输入的信息传递到tanh函数中去,创造一个新的候选值向量,最后将sigmodi的输出值与tanh的输出值相乘。sigmoid的输出值将决定tanh的输出值中哪些信息是重要且需要保留下来的。
  3. 细胞状态:首先用前一层的细胞状态与遗忘门向量相乘,如果它乘以一个接近0的值,意味着在新的细胞状态中,这些信息是需要丢掉的。然后再将这个值与输入门的输出值相加,将神经网络发现的新信息更新到细胞中去。得到新的细胞状态。
  4. 输出门:用来确定下一个隐藏状态的值。首先,将前一个隐藏状态和当前输入传递到sigmoid函数中去,然后将新得到的细胞状态传递给tanh。最后将tanh的输出与sigmoid的输出相乘,以确定隐藏状态应携带的信息,然后将新的隐藏状态和新的细胞状态传递到下一个时间步长中。

GRU: 更新门(类似于LSTM的遗忘门和输入门)、重置门(控制需要保留多少之前的记忆),去除了细胞状态,使用隐藏状态进行信息的传递。

GRU z:更新门 r:重置门

梯度消失、梯度爆炸

  1. 原因:链式求导,对激活函数求导乘上权重值,如果这一部分大于1,随着层数的增多,梯度更新会以指数形式增加,就是梯度爆炸;如果这一部分小于1,随着层数增多,梯度更新会以指数形式衰减,发生梯度消失
  2. LSTM怎么解决的? LSTM是通过记忆和当前输入相加,使得之前的记忆会继续存在,不是受到乘法的影响而部分消失,所以不会衰减。
  3. 其他解决方法
  • 使用不同的激活函数:Relu大于0的部分导数为1,就不存在梯度消失爆炸问题了,每层网络都可以得到相同的更新速度。但是,它负数部分恒为0,会导致一些神经元无法激活(可以通过设置小学习率部分解决),leakrelu解决了它负数部分为0 的问题。 激活函数:sigmoid,tanh,relu,leaekrelu
  • Batchnorm: 如何解决梯度问题:通过批规范化,解决分布问题,将输出从饱和区拉到非饱和区。 原理在输入到激活函数之前,将特征进行归一化,需要用λ和β(平移参数和缩放参数)进行调整,保证每一次数据经过归一化后还保留之前学习来的特征,加快训练速度。测试的时候,用的是训练时候的平均值和方差。 为什么能加快训练?每一次参数迭代更新后,上一层网络的输出数据经过这一层网络计算后,数据的分布会发生变化,给下一层网络的学习带来困难,神经网络本来就是要学习数据的分布,如果说分布一直变的话,就很难学习。 作用:减弱前层参数与后层参数之间的联系,使得网络的每层都可以自己学习,独立于其他层,有助于加速网络的学习;另一方面,有正则化的效果,类似于dropout,给隐藏层添加噪音,因为计算的是mini-batch上的均值和方差,而不是整个数据集的,这使得后面的隐藏单元不过分依赖任何一个隐藏单元。
  • 梯度剪切、正则(针对梯度爆炸):剪切:设置一个阈值,当更新梯度时,如果梯度超过这个阈值,就将它强制限制在这个范围内,可以防止梯度爆炸。正则:通过正则化项,可以部分限制梯度爆炸的发生。

梯度优化算法

  1. Batch、mini-batch、随机梯度下降的区别是取决于用多少数据来计算目标函数的梯度 (1)batch: 获得全局最优解,对整个数据集计算梯度,计算慢 (2)SGD随机梯度:每次更新时对每个样本进行梯度更新,可能会跳到更好的局部最优解,但因此噪音较多,有严重振荡。 (3)Mini-bacth:结合上两个优点,减少参数更新的次数,达到更稳定收敛结果。 上述算法的缺点: 对所有参数更新时应用同样的learning rate,如果数据是稀疏的,更希望对出现频率低的特征进行大一点的更新。学习率可以随着更新的次数变小;而且,一开始参数刚学习时,离最优解比较远,需要一个较大的学习率,但后面需要小一点。对于非凸函数,容易陷入局部极小值,或鞍点。 凸函数:海森矩阵为半正定
  2. Adam,其他优化算法,相比较 (1)Adagrada: 根据历史梯度值自适应的调节学习率,缺点:分母(历史梯度平方和)会不断累积,学习率就会收缩最终变得非常小。

(2)RMSprop: 对AdaGrad的改进,按照衰减系数累积历史的梯度平方值

(3)Adam: RMSprop(过去梯度平方的指数衰减平均值) 动量项(过去梯度的指数衰减平均值)

动量项:可以使得梯度方向不变的维度上速度变快,梯度方向有所改变的维度上更新速度变慢,这样可以加快收敛并减小振荡。

dropout防止过拟合

  1. Dropout怎么防止过拟合,代码 原理:在训练过程中,对于神经网络单元,按照一定的概率将其暂时从网络中丢弃 为什么:相当于训练了多个模型,类似集成学习,减弱神经元节点间的联合适应性,增强泛化能力;另一个角度,相当于做了数据增强,每次随机的丢弃一些单元,相当于增加了样本。 测试阶段不使用dropout,因为在测试阶段进行预测时,不期望输出结果是随机的,否则预测会受到干扰。
  2. 过拟合是怎么造成的? 数据:数据在抽样时,可能并不能代表整体,甚至与整体有较大差异; 模型:模型过于复杂
  3. 其他防止过拟合的方法
  • 数据:数据增强 图像:缩放、翻转、裁剪、颜色变换等 SMOTE算法(人工合成数据):通过将k个最近的邻居合并形成新实例。 类别不均衡问题: 1.调整分类阈值; 2.选择合适的评估指标; 3.使用集成学习模型; 4.损失函数加权的方法:给少数类的权重更高,使得分错少数类的代价更高,从而才追求整体代价最小的目标下,学习器就会偏向于尽可能将少数类划分正确。
  • 模型:①简化:减少网络的层数,神经元个数;②训练时间:early stopping;③正则化;④模型集成的方法。 正则化:λ大,容易欠拟合 1.为什么可以防止过拟合: 拟合过程中倾向于让权值尽可能小,可以设想一下对于一个线性回归方程,若参数很大,数据偏移一点,就会对结果造成很大的影响;但参数足够小,不会对结果造成大的影响。 2.区别: l1是权重向量的绝对值,l2是平方和;l1可以使权重稀疏,来使权重平滑。 3.为什么l1容易得到稀疏解: ①角度一:l1相当于为参数定义了一个菱形的解空间,来相当于定义一个圆形,l1的棱角更容易与目标函数等高线碰撞,从而产生稀疏解。 ②角度二:对L1和L2正则化下的目标函数求导。?

评价指标

  1. ROC(AUC)曲线
  • 横坐标:FPR(假正率),预测为正但实际为负的样本占所有负例样本的比例; 纵坐标:TPR(真正率),预测为正且实际为正的样本占所有正例样本的比例。
  • 绘制:假设已经得到了所有样本的概率输出(属于正样本的概率),根据每个测试样本属于正样本的概率值从大到小排列,依次将这些概率值作为阈值,当测试样本属于正样本的概率大于或等于这个阈值时,认为是正样本,否则为负样本。每个阈值可以得到一组FPR,TPR。
  • AUC物理意义:模型将某个随机正类样本排列在某个随机负类样本之上的概率。
  • ROC特性:当测试集中的正负样本分布变化的时候,ROC曲线能够保持不变。而Precision-recall会变。 为什么可以避免样本不平衡问题?举例:总样本中,90%是正样本,10%是负样本。TPR只关注90%正样本中有多少是被真正覆盖的,而与那10%无关;FPR只关注10%负样本中有多少是被错误覆盖的,也与那90%无关。
  1. 准确率 对于类别不平衡情况,没有参考意义。
  2. precision、recall、f1-score Precision: 预测的正例中,实际为正例的比例 Recall:真实的正例中,实际被预测出来的比例,”宁可错杀一千,绝不放过一个 F1-score = 2×precision*recall/(precision recall) PR曲线:横轴:Recall,纵轴:Precision

机器学习基础

1. 逻辑回归

回归模型: 1 线性回归:自变量和因变量必须满足线性关系 2 套索回归:线性回归 L1正则,有助于特征选择 3 岭回归:线性回归 L2正则

LR 逻辑回归 分类 ①原理:假设数据服从伯努利分布(抛硬币),在线性回归的基础上加了一个sigmoid函数(非线性映射),通过极大似然函数的方法,运用梯度下降求解参数,达到将数据二分类的目的。 ②优点:简单、占内存小、便于并行。

  • 并行化:对目标函数梯度计算的并行化。由于目标函数的梯度向量计算中只需要进行向量间的点乘和相加,可以很容易将每个迭代过程拆分成相互独立的计算步骤,由不同的节点进行独立计算,然后归并计算结果。 最大似然估计:在已知分布产生的一些样本,⽽不知道具体参数的情况下根据样本值推断最有可能产生样本的参数值。

缺点:需要手动交叉特征;处理非线性问题麻烦,需离散化。 【为什么要特征交叉,特征切分:举例辛普森悖论:在某个条件下的两组数据,分别讨论时都会满足某种性质,可是一旦合并考虑,却可能导致相反的结论。】 为什么不用平方损失函数: ①若用,会发现梯度的更新速度和sigmoid函数本身的梯度相关,而sigmoid函数在它定义域内的梯度都不大于0.25,训练会非常慢 ②会使得损失函数不是凸优化的。

  • 为什么要将高度相关的特征去掉? ①可以使模型的可解释性更好。②提高训练速度。

似然函数:

损失函数:

梯度更新:

2. 决策树

决策树

  1. ID3——最大信息增益= 数据集的经验熵-某个特征对于数据集的经验条件熵 倾向于取值较多的特征,特征取值越多就意味着确定性更高,也就是条件熵越小,信息增益越大。 C4.5——最大信息增益比=最大信息增益/数据集关于某个特征的取值熵,对取值较多的特征进行惩罚 CART——最大基尼指数,是一颗二叉树,采用二元切割法,每一步将数据按特征A的取值切成两份。
  2. 三者差异: (1)ID3只能处理离散型变量,而C4.5和CART都可以处理连续变量 (2)ID3和C4.5只能用于分类任务,而CART可以用于分类和回归 (3)ID3对样本特征缺失值比较敏感,而C4.5和CART可以对缺失值进行不同方式的处理。

3. 支持向量机

支持向量机

  • 原理:最大化几何间隔,转为求解对偶问题(更容易求解;自然引入核函数,进而推广到非线性分类问题)。
  1. 找到使得两类样本最大间隔的分离超平面f(x)=wx b。
  2. 三种:①线性可分:硬间隔SVM;②近似线性可分:软间隔SVM;③线性不可分:核技巧。
  3. 核函数:将非线性问题转为线性问题。将输入空间的内积转为特征空间的内积。多项式核,高斯核。
  4. ①函数间隔:y(wx b)。|wx b|表示点x到超平面的远近,而wx b的符号与类标记y的符号是否一致能够表示分类是否正确。然而,成比例的改变w,b,超平面没有变,所以引出几何间隔。 ②几何间隔:函数间隔/w的二范数。表示实例点到超平面带符号的距离。
  5. 求最大化间隔,可以转换成一个对偶问题,对对偶问题求偏导,可以得到用参数α表示的w,为了求α可以用SMO算法。
  6. 合页损失函数:用于软间隔最大化,当样本点(x,y)被正确分类且函数间隔大于1时,损失是0,否则损失为1-函数间隔。
  7. SMO:基本思路:所有变量的解都满足此最优化问题的KKT条件。选择两个变量,固定其他变量,针对这两个变量构建一个二次规划问题。
  • 优点: 对异常值不敏感。计算的复杂性取决于支持向量的数目,而不是样本空间的维数,这在某种意义上避免了“维数灾难”。
  • 缺点:对大规模训练样本难以实施;解决多分类问题困难;对参数和核函数选择敏感

4. 集成学习

集成学习 ⽅差反映的是模型每⼀次输出结果与模型输出期望之间的误差,即模型的稳定性。

  1. Boosting:首先将训练集用初始权重训练出一个弱学习器,根据弱学习的学习误差率来更新训练样本的权重,使得之前学习误差率高的样本权重变高,在后面的弱学习器中得到更多的重视,然后基于调整权重后的训练集来训练下一个弱学习器,通过加法模型将弱学习器线性组合。
  2. Bagging:采用bootstraping的方法抽取n个训练样本,多次抽取,得到多个训练集。每个训练集得到一个模型,最后采用多数投票法得到分类结果。(回归问题,采用平均值)
  3. 区别: ①样本选择:bagging有放回的选取;boosting每一轮训练集不变,只是权重发生变化; ②bagging所有弱分类器权重一样;boosting对于分类误差小的分类器有更大的权重; ③bagging各弱分类器可以并行生成,boosting只能顺序。

5.随机森林

随机森林

  1. 生成过程: (1)从原始样本中有放回抽样的选取n个样本; (2)对n个样本选取,随机选取k个特征,用建立决策树的方法获得最佳分割点 (3)重复多次,建立多个决策树 (4)多数投票机制决定最终结果
  2. 为什么要有放回抽样(自助抽样)? ①如果不放回,每棵树的样本完全不同,基学习器之间的相似性小,模型偏差大; ②如果不抽样,使用全部样本训练,模型的方差大,泛化能力差。 ③可以产生一部分袋外样本,做袋外估计。
  • 优点:
  1. 它能够处理很高维度(feature很多)的数据,并且不用做特征选择
  2. 训练速度快,容易做成并行化方法
  3. 它能够给出哪些feature比较重要
  4. 处理高维数据,处理特征遗失数据,处理不平衡数据是随机森林的长处
  • 缺点:
  1. 噪音较大的分类或回归问题上会过拟合
  2. 不能很好的解决回归问题

6. GBDT

GBDT 原理:只能用回归树。每一颗树学的是之前所有树结论和的残差,用损失函数的负梯度来拟合本轮损失的近似值。无论是分类问题还是回归问题,都可通过其损失函数的负梯度拟合,区别仅在于损失函数不同导致的负梯度不同。

缺点:由于弱学习器之间的依赖关系,难以并行训练数据。

7. XGB、LGB

XGBoost 相较于GBDT的优点:

  1. 精度更高:GBDT只用到一阶泰勒, 而xgboost对损失函数进行了二阶泰勒展开, 一方面为了增加精度, 另一方面也为了能够自定义损失函数,二阶泰勒展开可以近似大量损失函数
  2. 灵活性更强:GBDT以CART作为基分类器,而Xgboost不仅支持CART,还支持线性分类器,另外,Xgboost支持自定义损失函数,只要损失函数有一二阶导数。
  3. 正则化:xgboost在目标函数中加入了正则,用于控制模型的复杂度。有助于降低模型方差,防止过拟合。正则项里包含了树的叶子节点个数,叶子节点权重的L2范式。
  4. Shrinkage(缩减):相当于学习速率。这个主要是为了削弱每棵树的影响,让后面有更大的学习空间,学习过程更加的平缓
  5. 列抽样:这个就是在建树的时候,不用遍历所有的特征了,可以进行抽样,一方面简化了计算,另一方面也有助于降低过拟合
  6. 缺失值处理:这个是xgboost的稀疏感知算法,加快了节点分裂的速度 并行化操作:块结构可以很好的支持并行计算

LightGBM 相较于xgboost

  1. 内存更小
  • XGBoost 使用预排序后需要记录特征值及其对应样本的统计值的索引,而 LightGBM 使用了直方图算法将特征值转变为 bin 值,且不需要记录特征到样本的索引,将空间复杂度从 O(2*#data) 降低为 O(#bin) ,极大的减少了内存消耗;
  • LightGBM采用了直方图算法将存储特征值转变为存储 bin 值,降低了内存消耗; LightGBM 在训练过程中采用互斥特征捆绑算法减少了特征数量,降低了内存消耗。
  1. 速度更快
  • LightGBM 采用了直方图算法将遍历样本转变为遍历直方图,极大的降低了时间复杂度;
  • LightGBM在训练过程中采用单边梯度算法过滤掉梯度小的样本,减少了大量的计算;
  • LightGBM 采用了基于 Leaf-wise算法的增长策略构建树,减少了很多不必要的计算量;
  • LightGBM采用优化后的特征并行、数据并行方法加速计算,当数据量非常大的时候还可以采用投票并行的策略;
  • LightGBM 对缓存也进行了优化,增加了 Cache hit 的命中率。

8. Adaboost

Adaboost

  1. 分类 ①初始化样本集权重 ②得到弱分类器 ③计算样本分类误差 ④计算弱分类器数 ⑤更新样本集的权重分布 ⑥构成最终的分类器
  2. 回归 ①初始化样本集权重 ②得到弱分类器 ③计算训练集上的最大误差 ④计算每个样本的相对误差 ⑤计算回归误差率 ⑥计算弱学习器的系数 ⑦更新样本集的权重分布 ⑧构成最终分类器

缺点:对异常样本敏感,异常样本在迭代中可能会获得较高的权重,影响最终学习器的预测准确性。

发布者:全栈程序员栈长,转载请注明出处:https://javaforall.cn/100172.html原文链接:

0 人点赞