一、冷启动原理与项目实战
1 冷启动原理与技术原理
推荐系统需要根据用户的历史行为和兴趣预测用户未来的行为和兴趣,因此大量的用户行为数据就称为推荐系统的重要组成部分和先决条件。很多在开始阶段就希望有个性化推荐应用的网站来说,在没有大量用户数据的情况下设计个性化推荐系统并且让用户对推荐结果满意从而愿意使用推荐系统,就是冷启动问题。本质:商品或用户多、但行为历史数据或特征历史数据少。
1.1 冷启动问题
用户不确定性需求是客观存在的,在当今信息爆炸的时代,用户的不确定性需求更加明显,而推荐作为一种解决用户不确定性需求的有效手段在互联网产品中会越来越重要, 特别是随着短视频、新闻等应用的崛起,推荐的重要性被更多人认可。很多产品将推荐业务放到最核心的位置(如首页),或者是整个产品的核心。因此,新用户必须要面对冷启动这个问题。
新用户、新标的物是持续产生的,对互联网产品来说是常态,是无法避免的,所以冷启动问题会伴随整个产品的生命周期。既然很多产品将推荐放到这么好的位置, 而推荐作为一种有效提升用户体验的工具,在新用户留存中一定要起到非常关键的作用,如果推荐系统不能很好的为新用户推荐好的内容,新用户可能会流失。所以如果不解决冷启动问题,你的新用户一直会得不到好的推荐体验,极有可能会流失掉。
新用户的留存对一个公司来说非常关键, 服务不好新用户,并让用户留下来,你的用户增长将会停滞不前。对于互联网公司来说, 用户是公司赖以生存的基础,是利润的核心来源。因为互联网经济是建立在规模用户基础上的,只有用户足够多,你的产品才会有变现的价值。
同时,只有你的产品有很好的用户增长曲线, 投资人才会相信未来用户大规模增长的可能, 才能看得到产品未来的变现价值,才会愿意在前期投资你的产品。
-
用户冷启动:用户冷启动主要解决如何给新用户做个性化推荐的问题。 当新用户到来时,没有他的行为数据,所以无法根据他的历史行为预测其兴趣,从而无法借此给他做个性化推荐。
-
物品冷启动:物品冷启动主要解决如何将新的物品推荐给可能对它感兴趣的用户这一问题。
-
系统冷启动:系统冷启动主要解决如何在一个新开发的网站上(没有用户,也没有用户行为,只有一些物品的信息)设计个性化推荐系统,从而在网站刚发布时就让用户体验到个性化推荐服务这一问题。
1.2 冷启动解决思路
我们先概述一下解决冷启动的一般思路:
-
提供非个性化的推荐(用户冷启动)
-
利用用户注册时提供的信息(用户冷启动、系统冷启动)
-
基于内容做推荐(用户冷启动、系统冷启动)
-
利用标的物的metadata信息做推荐(标的物冷启动)
-
采用快速试探策略(用户冷启动、标的物冷启动)
-
采用兴趣迁移策略(用户冷启动、系统冷启动)
-
采用基于关系传递的策略(标的物冷启动)
下面针对用户冷启动与物品冷启动做一个详细的介绍。
-
用户冷启动:
- 关联第三方平台,获取特征
- 利用注册信息
- 在注册时,收集用户喜好
-
物品冷启动:
- 管理员标签
- 内容特征
2 基于注册信息的冷启动推荐
2.1 数据集
Book-Crossings是由Cai-Nicolas Ziegler编写的图书评分数据集。 它包含90000个用户的270000本书的110万个评分。评分范围从1到10,包括显式和隐式的评分。Book-Crossings数据集是最不密集的数据集之一,也是具有明确评分的最不密集的数据集。
2.2 用户冷启动
-
提供非个性推荐
- 利用先验数据做推荐,人都是有喜新厌旧倾向的,推荐新的东西肯定能抓住用户的眼球。
- 给用户提供多样化的选择,将内容按照标签分几大类,每大类选择一个推荐给新用户,这样总有一个是用户喜欢的。
-
利用新用户在注册时提供的信息
- 利用人口统计学数据,很多产品在新用户注册时是需要用户填写一些信息的, 这些用户注册时填的信息就可以作为为用户提供推荐的指导。
- 利用社交关系,社交推荐最大的好处是用户基本不会反感推荐的标的物
- 利用用户填写的兴趣点,用户在注册时提供你的兴趣点,有了这些兴趣点就可以为你推荐你喜欢的内容
-
基于内容做推荐
- 当用户只有很少的行为记录时,这时还无法给用户做很精准的推荐。这时可以采用基于内容的推荐算法,基于内容的推荐算法只要用户有少量行为就可以给用户推荐。
-
采用快速试探策略
- 随机或者按照非个性化推荐的策略给用户推荐,基于用户的点击反馈快速发现用户的兴趣点,从而在短时间内挖掘出用户的兴趣。
-
采用兴趣迁移策略
- 鉴了迁移学习的思路,在基于主产品拓展新产品形态的情况下,特别适合新产品做冷启动。
2.3 基于新用户注册提供的信息
2.3.1 分析数据
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
# 1 分析用户表
# 读取数据
df_user = pd.read_csv('./data/BX-Users.csv',sep=';')
df_user.head()
# 修改列名
df_user.columns = ['user_id','location','age']
df_user.head()
# 分析location
location_value_counts = df_user['location'].value_counts()
location_value_counts.count()
# 平均每个地区,存在的用户个数
location_value_counts.sum() / location_value_counts.count()
# 把location变成国家
df_user['location'] = df_user['location'].apply(lambda location:location.rsplit(',',maxsplit=1)[-1].strip())
len(df_user['location'].unique())
location_value_counts = df_user['location'].value_counts()
plt.plot(
range(len(location_value_counts)),
location_value_counts,
)
plt.show()
plt.pie(
location_value_counts,
labels = location_value_counts.index
)
plt.show()
#### 分析 年龄
df_user[
df_user['age'].isnull()
].head()
df_user[
df_user['age'].isnull()
].count()
df_user.info()
df_user_age_notnull = df_user[~df_user['age'].isnull()]
df_user_age_notnull.head()
age_value_counts_sort_by_age = df_user_age_notnull['age'].value_counts().sort_index()
plt.plot(
age_value_counts_sort_by_age.index,
age_value_counts_sort_by_age
)
plt.show()
def divide_age(age):
"""
:return:
Nan,0-6,>100 --> 0 年龄填写有误
7 - 12 --> 1 少儿
13 - 17 --> 2 青少年
18 - 45 --> 3 青年
46 - 69 --> 4 中年
70 - 100 --> 5 老年
"""
if np.isnan(age) or (age <= 6) or (age > 100):
return 0
if age >= 7 and age <= 12:
return 1
if age >= 13 and age <= 17:
return 2
if age >= 18 and age <= 45:
return 3
if age >= 46 and age <= 69:
return 4
if age >= 70 and age <= 100:
return 5
df_user['age'] = df_user['age'].apply(divide_age)
df_user.head()
# 分析年龄段
plt.pie(
df_user['age'].value_counts().sort_index(),
labels = ['undefined','shaoer','qingshaonian','qingnian','zhongnian','laonian']
)
plt.show()
# 分析打分表
# 读取数据
df_rating = pd.read_csv('./data/BX-Book-Ratings.csv',sep=';')
df_rating.head()
df_rating.columns = ['user_id','item_id','rating']
df_rating.head()
df_rating.info()
# 删除有误数据
df_rating = df_rating.dropna()
df_rating.info()
# 用户活跃度
user_value_counts = df_rating['user_id'].value_counts()
plt.plot(
range(len(user_value_counts)),
user_value_counts,
)
plt.show()
# 商品流行度分析
item_value_counts = df_rating['item_id'].value_counts()
plt.plot(
range(len(item_value_counts)),
item_value_counts,
)
plt.show()
df_rating.info()
df_rating['rating'] = df_rating['rating'].astype(int)
df_rating.info()
# 合并用户表与打分表
df_user.head()
df_rating.head()
df_data = pd.merge(
df_user,
df_rating,
on='user_id',
how='inner',
)
df_data.head()
df_data.info()
df_data['age'] = df_data['age'].astype(int)
df_data.info()
# 保存表
import _pickle as cPickle
cPickle.dump(
df_data,
open('./data/df_data.pkl','wb')
)
object1 = cPickle.load(
open('./data/df_data.pkl','rb')
)
object1.head()
2.3.2 划分数据集
import pandas as pd
import _pickle as cPickle
# 读取文件
df_data = cPickle.load(
open('./data/df_data.pkl','rb')
)
df_data.head()
df_data.info()
# 划分数据集 70%训练集 30%测试集
import random
random.seed(123)
text_index_s = random.sample(
df_data.index.tolist(),
int(len(df_data) * 0.3)
)
text_index_s
df_data_text = df_data.loc[
text_index_s
]
df_data_text.head()
df_data_text.info()
df_data_training = df_data