数据分析业务场景 | 用户画像

时间:2022-12-12 07:53:20

一.概况

定义

是根据用户的一系列行为和意识过程建立起来的多维度标签;是根据用户人口学特征,网络浏览内容,网络社交活动和消费行为等信息而抽象出的一个标签化的用户模型;首要任务:根据业务需求整理和数据情况分析建立相对应的标签体系

内容

并不完全固定,根据行业和产品的不同所关注的特征也有所不同。对于大部分互联网公司,用户画像都会包含人口属性和行为特征。人口属性主要指用户的年龄,性别,所在的省份和城市,教育程度,婚姻情况,生育情况,工作所在的行业和职业等;行为特征主要包含活跃度,忠诚度等指标

目的

必须从业务场景出发,解决实际的业务问题:获取新用户 ;提升用户体验 ;挽回流失用户

根据用户画像的信息做产品设计,必须要清楚知道用户长什么样,有什么行为特征和属性,这样才能为用户设计产品或开展营销活动

目标

通过分析用户行为,最终为每个用户打上标签以及该标签的权重。

标签:表现了内容,用户对该内容有兴趣,偏好,需求等

权重:表现了指数,用户的兴趣,偏好指数,也可能表现用户的需求度,概率

作用

精准营销:精准直邮,短信,app消息推送,个性化广告等

用户研究:指导产品优化,甚至做到产品功能的私人订制等

个性服务:个性化推荐,个性化搜索等

业务决策:排名统计,地域分析,行业趋势,竞品分析等

意义

完善产品运营,提升用户体验:业务运营监控,迅速定位服务群体,优化用户体验 

对外服务,提升盈利:分析产品潜在用户,精细化营销,数据服务

用户统计

数据挖掘,构建智能推荐系统

对服务或产品进行私人订制

业务经营分析以及竞争分析,影响企业发展战略

应用

风险控制:包括个人及企业级信用评分,欺诈识别

个性化推荐:根据每个人的不同喜好推荐与之相关的内容

精细化运营:包括产品优化,市场和渠道分析,漏斗分析。提升用户体验还有广告投放,数据交易,行为预测等

常用算法

常见的分类算法和聚类算法都会用到,比如svm,lr分类,k-means聚类等

核心工作

利用存储在服务器上的海量日志和数据库里的大量数据进行分析和挖掘,给用户贴标签,而标签是能表示用户某一维度特征的标识

难点

数据源,业务结合,动态更新

商业价值

和它支撑的应用密切相关,比如应用于营销,可以提升广告效果和流量变现的效率

做好用户画像的前提

优质的数据源

统一设备ID

数据源补充

标签体系

计算能力:数据挖掘需要大量的矩阵和迭代计算

技术积累:涉及分类,聚类,神经网络等算法,以及算法模型的构建和优化,需要相当的技术积累和时间验证

二.实施流程

数据分析业务场景 | 用户画像

1.特征工程

就是提取APP特征,事件特征,浏览内容特征等

对非结构化数据来说,通常要经历 "分词" ,"过滤","特征提取" 三个步骤

2.数据建模

常见的模型:朴素贝叶斯,逻辑回归,SVM,神经网络等

在模型优化过程中,调参优化是非常重要的一步,在调参优化过程中,我们通常会遇到过拟合,样本不均等情况

三.源数据分析

用户数据分为两类:静态信息数据来源和动态信息数据来源

静态信息数据来源

顾名思义就是用户不会轻易改变的数据,比如社会属性,生活习惯等

用户填写的个人资料,或者由此通过一定的算法,计算出来的数据

如果有不确定的,可以建立模型来判断

动态信息数据来源

用户不断变化的行为信息,一般取决于用户对产品的行为反馈

动态数据都可以通过该产品/网页的数据统计记录下来,只要产品数据埋点足够完善,需要哪些数据就直接从数据库拉取就可以

用户行为产生的数据:注册,浏览,点击,购买,签收,评价,收藏等

用户比较重要的行为数据:浏览商品,收藏商品,加入购物车,关注商品

四.标签构建策略

标签是表达人的基本属性,行为倾向,兴趣爱好等某一维度的数据标识,是一种相关性很强的关键字,可以简洁地描述和分类人群

标签的定义来源于业务目标,基于不同的行业,不同的应用场景,同样的标签名称可能代表了不同的含义,也决定了不同的模型设计和数据处理方式

特点

语义化:使人方便理解每个标签的含义;每个标签只表示一种含义

短文本:无需过多进行文本分析处理工作;方便机器提取标准化信息

1.特征属性

用户的特征属性可以是事实的,也可以是抽象的;可以是自然属性,比如性别,年龄,星座等,可以是社会属性,比如职业,社交,出生地等;还可以是财富状况,比如是否高收入人群,是否有豪车豪宅等固定资产,这些属性可以清楚地描绘一个用户的画像特征

数据分析业务场景 | 用户画像

2.分类

基础类标签和个性化标签

这些标签可以有重复,但是都是通过不同的角度去定义和刻画一个用户,来满足不同的业务营销需求

①通常的标签分类

人口学属性:性别,年龄,学历,收入等

兴趣偏好:爱玩篮球,德州扑克,打电竞等

消费偏好:线上/线下的一些消费行为等

位置信息:wifi定位,常驻城市,商圈等

设备属性:品牌,机型,操作系统等

②从打标签的方式来看,一般可分为三种类型

统计类标签:是最基础也是最常见的标签类型,该类标签构成了用户画像的基础

规则类标签:基于用户行为及确定的规则产生,由运营人员和数据人员共同协商确定

机器学习挖掘类标签:通过数据挖掘产生,应用在对用户的某些属性或某些行为进行预测判断

在项目工程实践中,一般统计类和规则类标签即可以满足应用需求,开发中占有较大比例。机器学习挖掘类标签多用于预测场景,如判断用户性别是男是女,判断用户购买商品偏好,判断用户流失意向等。一般机器学习标签开发周期长,耗费开发成本较大,因此其开发所占比例较小

3.构建流程

数据分析业务场景 | 用户画像

①战略解读:首先要明确用户画像平台的战略意义,平台建设目标和效果预期,进而有针对性地开展实施工作

②业务分析:从全局整体上理解全产业业务,在理解业务的基础上深入了解数据仓库的数据体系,并且确定识别唯一用户的方案

③特征分解:以用户,产品,交易,社交等数据为中心,对用户产生的数据分解和列举。根据相关性原则,选取和战略目的以及业务场景相关的数据维度,避免产生过多无用数据干扰分析过程。确定数据的分类级别以及每个类别中的具体指标

④标签建模:对用户画像进行数据建模,结合公司实际需求,针对具体的业务场景,基于用户的特征分解数据,借助于数据挖掘方法,发掘用户的当前社会特征以及预测用户未来的社会特征,最终形成符合用户在社会活动中真实特征的标签体系

⑤画像应用:针对不同角色人员的需求(如战略,市场,销售,运营,研发等),设计各角色人员在用户画像工具中的使用功能/操作流程

4.技术构架 数据分析业务场景 | 用户画像

 5.实施方法论

数据分析业务场景 | 用户画像

6.用户特征提取步骤

①用户建模:指确定提取的用户特征维度和需要使用到的数据源

②数据收集:通过数据收集工具把需要使用的数据统一存放到hadoop集群

③数据清理:此过程通常位于hadoop集群,也有可能与数据收集同时进行,这一步的主要工作是把收集到各种来源,杂乱无章的数据进行字段提取,得到关注的目标特征

④模型训练:有些特征可能无法直接从数据清理得到,比如用户感兴趣的内容或用户的消费水平,那么可以通过收集到的已知特征进行学习和预测

⑤属性预测:利用训练得到的模型和用户的已知特征,预测用户的未知特征

⑥数据合并:把用户通过各种数据源提取的特征进行合并,并给出一定的可信度

⑦数据分发:对于合并后的结果数据,分发到精准营销,个性化推荐,CRM等各个平台,提供数据支持

五.案例:消费者人群画像

1. 背景

随着社会信用体系建设的深入推进,社会信用标准建设飞速发展,相关的标准相继发布,社会信用标准体系有望快速推进。传统的信用评分主要以客户消费能力等少数维度来衡量,难以全面,客观,及时地反映客户的信用,运营商信用智能评分体系的建立能够完善社会信用体系

2. 探索数据

这里把测试集和训练集都融合在一起做特征分析,原因是有可能存在出现在测试集而不在训练集中的特征值

#读取数据集
train = pd.read_csv("...")
test = pd.read_csv("...")
data = pd.concat([train,test],axis = 0,ignore_index=True)

#查看数据大概情况
data.shape
data.info()

columnList = list(data)

for col in columnList:
  print(data[col].value_counts())
  print(' 不同值个数 = {},最大值 = {},最小值 = {}'.format(len(data[col].value_counts()),data[col].max(),data[col].min()))

data.describe()

#观察训练/测试集数据同分布状况
data[data['信用分'].isnull()].describe()
data[data['信用分'].notnull()].describe()

'''特征探索
1. 按照特征顺序
2. 按照连续,离散,非结构化特征顺序
3. 基于业务场景来对关联特征进行分析
4. 对特征本身进行业务角度解读,提取出关键信息
5. 充分利用外部信息,让特征具有实际场景意义'''

3. 数据预处理

def data_process(data):
  user_fea = ['last_payment_amount','average_consumption_value','all_fee','balance']
  log_features = ['shopping_app_usage','financial_app_usage','video_app_usage']
  transform_value_feature = ['user_network_age','age','number_people_circle','average_number_appearance','shopping_app_usage','express_app_usage','financial_app_usage','video_app_usage','aircraft_app_usage','train_app_usage','tourism_app_usage']  

有些特征明显有异常大和异常小的值,分别用1%和99%替换超过上下限的值

for col in transform_value_feature + user_fea:
  ulimit = np.percentile(train[col].values,99.9)
  llimit = np.percentile(train[col].values,0.1)
  train.loc[train[col] > ulimit,'col'] = ulimit
  train.loc[train[col] < llimit,'col'] = llimit

有几个特征上下界相差较大,做平滑处理,使数据分布更加符合高斯分布,减小数据偏度,缩小数据范围,一定程度上缓和波动较大的数据对后续预测带来的影响,使数据更加稳定

for col in log_features:
  data[col] = data[col].map(lambda x: np.log(x))

4. 特征工程

def get_count(df,column,feature):
  df['idx'] = range(len(df))
  temp = df.groupby(column)['userr_id'].agg([(feature,'count')]).reset_index()
  df = df.merge(temp)
  df = df.sort_values('idx').drop('idx',axis=1).reset_index(drop=True)
  return df

data['age'] = data['age'].apply(lambda x:np.nan if (x > 100) | (x == 0) else x)
data['all_fee-average_consumption_value'] = data['all_fee'] - data['average_consumption_value']
data = get_count(data,'all_fee','count_all_fee')
...
data['user_network_age_diff'] = data.apply(lambda x: x.user_network_age % 12,axis=1)

构建训练集和测试集

train = data[:train.shape[0]]
test = data[train.shape[0]:]

5. 特征选择:方差选择法

删除方差低于某一阈值的特征(因为它们可能包含很少信息),选择一个方差高于给定阈值的特征子集

方差阈值是手动选择的,所以必须自己判断选择一个好的阈值,如果没有太大把握就把阈值设置得小一点。不同数据尺度或者单位的数据可能对应的方差尺度也不一样,在设置阈值的时候要慎重

from sklearn.feature_selection import VarianceThreshold
#threshold传入参数格式为:最小容忍比例 * (1 - 最小容忍比例)
#这里的容忍比例就是当离散样本中最多的那一类数量占全体数量的上限
#0.8*(1-0.8):对所有变量中最大比例样本对应的比例大于等于80%的变量予以剔除
#初始化低方差特征选择模型
sel = VarianceThreshold(threshold=0.8*(1-0.8))
sel.fit_transform(data)

6. 构建模型

在结构化比赛中,机器学习常用的模型有lightgbm,xgboost,catboost等模型,算法速度快且能够容纳缺失值,stacking通过结合多个各有所长的子学习器,提升了整体的预测能力。一个基本的理论假设是,不同的子模型在不同数据上有不同的表达能力,我们可以结合它们擅长的部分,得到一个在各方面都很准确的模型

调参过程:以XGBoost为例

调参思路:逐个或逐类参数调整,避免所有参数一起调整导致模型复杂度过高。因此,调参过程分多步进行,每次调整相同类型的参数。

模型调参过程会输出每个组合的平均得分值,根据GridSearchCV参数指定使用roc_auc为评分指标,进行cv=5的5折交叉验证,返回的平均得分即为5折交叉验证auc值的平均值,调用调参函数进行调参,根据设定会打印调参信息并画图辅助判断

'''
sklearn提供了调参利器:sklearn.model_selection.GridSearchCV,对估计器的指定参数值进行穷举搜索
estimator:学习器
param_grid:待调整的参数,字典形式
scoring:评估标准('roc_auc','accuracy','f1'等)
cv:确定交叉验证分割策略,默认5折交叉验证,也可使用分层k折,重复k折等
'''
class sklearn.model_selection.GridSearchCV(estimator,param_grid,scoring=None,cv=None)

设置初始参数值,并选择较高的学习率,设置在0.05~0.3,这样可以减少迭代用时,获得最佳学习器个数(用xgboost.cv函数获取)

'''
params:dict,学习器的参数
dtrain:DMatrix,训练集,DMatrix是xgboost使用的一种内部数据结构,它对内存效率和训练速度进行了优化
num_boost_round:int,学习器的个数
folds,metrics:同gridsearchcv的cv参数,scoring参数
early_stopping_rounds:int,停止迭代轮数,比如等于50,则表示如果精度在某一轮后五十轮仍未提升,则停止迭代
'''
xgboost.cv(params,dtrain,num_boost_round=10,nfold=3,stratified=False,folds=None,early_stopping_rounds=None)

然后对max_depth,min_child_weight,gamma,subsample,colsample_bytree这些参数进行调整

先调整max_depth和min_child_weight,这两个参数对输出结果影响很大,首先将它们设置为较大的数,然后通过迭代的方式不断修正,缩小范围:GridSearchCV

然后基于上面确定好的最优值来调整gamma值

max_depth:[3,5,6,7,9,12,15,17,25]

min_child_weight:[1,3,5,7]

gamma:[0.005~0.1,0.3,0.5,0.7,0.9,1]

subsample:[0.6,0.7,0.8,0.9,1]

colsample_bytree:[0.6,0.7,0.8,0.9,1]

调整正则化参数lambda,alpha

用的比较少,主要是为了减少模型复杂度和提高运行速度,适当减少过拟合

alpha:[0,0.01~0.1,1]

lambda:[0,0.1,0.5,1]

降低学习率,较多学习器,使用xgboost.cv获取最佳学习器个数,继续调整参数

eta:[0.01,0.015,0.025,0.05,0.1]

xgb_param = {'eta':0.01,
             'max_depth':6,
             'subsample':0.8,
             'colsample_bytree':0.7,
             'objective':'reg:linear',
             'eval_metric':'rmse',
             'silent':True,
             'nthread':4,
             'n_estimators':20000,
             'gamma':0.2,
             'min_child_weight':25,
             'num_threads':8,
             'alpha':0.18,
             'lambda':0.23,
             'colsample_bylevel':0.8,
             }     

gsearch = GridSearchCV(xgb_param,param_grid=parameters,scoring='accuracy',cv=3)
gsearch.fit(train_x,train_y)
best_parameters = gsearch.best_estimator_.get_params()
for param_name in sorted(parameters.keys()):
  print("\t%s:%r" % (param_name,best_parameters[param_name]))

''' 
eta:学习率,可以缩减每一步的权重值,使得模型更加健壮,典型值一般设置为0.01~0.2
max_depth:值越大,树越复杂,可以用来控制过拟合,典型值是3~10
subsample:采样率,训练实例的子样本比率,将其设置为0.5意味着xgboost将在种植树木之前随机抽样一般的训练数据。这将防止过度安装。每一次提升迭代中都会进行一次子采样
colsample_bytree:构造每棵树时,列采样率,一般是特征采样率;用于列的子采样的参数,用来控制每棵树随机采样的列数的占比,有利于满足多样性要求,避免过拟合
objective:指定学习目标是分类,多分类还是回归
eval_metric:评估方法,主要用来验证数据,根据一个学习目标会默认分配一个评估指标
silent:设置模型是否有logo打印
gamma:在树的叶子节点上进行进一步分区所需的最小损失下降的大小,一般来说需要根据损失函数来调整,gamma越大,算法越保守
min_child_weight:如果树分区步骤导致叶节点的实例权重之和小于min_child_weight,构建过程将放弃进一步的分区,最小子权重越大,模型越保守;定义了一个子集所有观察值的最小权重和,可以用来减少过拟合,但是过高的值也会导致欠拟合,因此可以通过cv来调整
alpha:L1关于权重的正则化项,主要是用在数据维度很高的情况下,可以提高运行速度,增加该值将使模型更加保守
lambda:关于权重的L2正则化项,用的很少,增加该值将使模型更加保守
learning_rate:一般这时候要调小学习率来测试,学习率越小训练速度越慢,模型可靠性越高,但并非越小越好 
colsample_bylevel:每执行一次分裂,列采样率,一般很少用'''

7. 交叉验证

首先将全部样本划分成k个大小相等的样本子集

依次遍历这k个子集,每次把当前子集作为验证集,其余所有样本作为训练集,进行模型的训练和评估

将上一步对可能的 k 种选择重复进行,这样就训练了k个模型,每个模型都在相应的测试集上计算测试误差,得到了k个测试误差

最后把k次测试误差的平均值得到一个交叉验证误差作为最终的评估指标

folds_1 = Kfold(n_split=5,shuffle=True,random_state=2022)
oof_xgb = np.zeros(len(train))
prediction_xgb = np.zeros(len(test))

for fold_,(trn_idx,val_idx) in enumerate(fold_1.split(X_train,y_train):
  trn_data = xgb.DMatrix(X_train[trn_idx],y_train[trn_idx])
  val_data = xgb.DMatrix(X_train[val_idx],y_train[val_idx])
  watchlist = [(trn_data,'train'),(val_data,'valid')]
  clf = xgb.train(dtrain=trn_data,num_boost_round=20000,evals=watchlist,early_stopping_round=200,verbose_eval=100,params=xgb_params)
  oof_xgb[val_idx] = clf.predict(xgb.DMatrix(X_train[val)idx]),ntree_limit=clf.best_ntree_limit)
  predictions_xgb += clf.predict(xgb.DMatrix(X_test),ntree_limit=clf.best_ntree_limit)/fold_1.n_split

8. 模型融合:stacking

两层堆叠,在不同模型预测结果的基础上再加一层模型,进行再训练,从而得到模型最终的预测

stacking结合了多个不同种类的学习器进行训练,能够有效地降低方差,结合了多层进行训练,从而降低偏差

stacking算法分为2层,第一层是用不同的算法形成n个弱分类器,同时产生一个与原数据集大小相同的新数据集,利用这个新数据集和一个新算法构成第二层的分类器

注意点

第一层基模型最好是强模型,而第二层的基模型可以放一个简单的分类器,防止过拟合

第一层基模型的个数不能太小,因为一层模型个数等于第二层分类器的特征维度,当然这个值也不是越多越好

第一层的基模型必须准而不同,如果有一个性能很差的模型出现在第一层,将会严重影响整个模型融合的效果

有时这样对于如果训练集和测试集分布不那么一致的情况下是有一点问题的,其问题在于用初始模型训练的标签再利用真实标签进行再训练,会导致一定的模型过拟合,这样或许模型在测试集上的泛化能力或效果会有一定的下降

那么,如何降低再训练的过拟合性呢?

次级模型尽量选择简单的线性模型

利用 k 折交叉验证

stacking配合GridSearchCV实现超参数的调整,如果想要更好地搜索参数,可以使用贝叶斯搜索参数或者随机参数搜索法对参数进行搜索

train_stack = np.vstack([oof_lgb,oof_lgb1,oof_xgb,oof_cat]).transpose()
test_stack = np.vstack([predictions_lgb,predictions_lgb1,predictions_xgb,predictions_cat]).transpose()
#StratifiedKFold用法类似Kfold,但它是分层采样,确保训练集,测试集中各类别样本的比例与原始数据相同
folds_stack = StratifiedKFold(n_splits=10,shuffle=True,random_state=8888)
oof_stack = np.zeros(train_stack.shape[0])
predictions = np.zeros(test_stack.shape[0])

#对于每个模型,使用交叉验证的方法来训练初级学习器,并且得到次级训练集
for fold_,(trn_idx,val_idx) in enumerate(folds_stack.split(train_stack,y_train)):
  trn_data,trn_y = train_stack[trn_idx],y_train[trn_idx]
  val_data,val_y = train_stack[val_idx],y_train[val_idx]

  stacking = HuberRegressor(epsilon=1.03,alpha=1e-5)

  #使用次级训练集来训练次级学习器
  #在fit方法中,我们已经将训练出来的初级学习器和次级学习器保存下来了 
  stacking.fit(trn_data,trn_y)
  oof_stack[val_idx] = stacking.predict(val_data)
  #predict的时候只需要用这些学习器构造次级预测数据集并且进行预测就可以了
  predictions += stacking.predict(test_stack) / folds_stack.n_splits

print("stacking MAE score:{:<8.8f}".format(mean_absolute_error(oof_stack,y_train)))
print("stacking CV score:{:<8.8f}".format(1/(mean_absolute_error(oof_stack,y_train)+1)))

result['score'] = predictions