概述
USG用户购物性别标签模型,通过用户购买的产品,确定用户的性别,常常使用算法为:逻辑回归、线性支持向量机、朴素贝叶斯、决策树和树集成学习算法。
【购物性别定义】对于用户精确营销来说十分重要:
系统会依据商品的名称、商品的颜色和商品的类别等,判断购买者的性别。
引入
首先需要了解一下,如何通过大数据来确定用户的真实性别。
经常谈论到的用户精细化经营到底是什么?
简单来说,就是将网站的每一个用户都进行标签化,制作一个属于用户自己的网络身份证。然后,运营人员会通过身份证来确定进行活动投放的人群,圈定人群范围,更为精确的用户培养与管理。
当然,身份证最基本的信息就是姓名、年龄和性别,与现实不同的是,互联网上你无法一眼看出来用户的真实性别,单凭用户所填写的资料也无法断定其真实性,还需要进一步的确认和评估。
确定性别这件事非常重要,举个栗子,比如店铺想要推荐新品的Bra,如果粗糙随意的全部投放到人群或者投放到不准确性别的人群中,不仅效果极差,而且会造成不必要的骚扰,后果可想而知。
再举个栗子
U_1001 product_01 male
U_1001 product_02 female
U_1001 product_03 male
U_1001 product_04 female
U_1001 product_05 male
U_1001 product_06 male
基于用户购物物品,打上商品购买性别以后,进行计算,最终确定用户购物性别
统计购物商品个数
Total = 6
统计购物中男性商品的个数
MaleTotal = 4
占比 maleRate = 4 / 6 = 0.6666
统计购物中女性商品的个数
FemaleTotal = 2
占比 femaleRate = 2 / 6 = 0.3333
判断男性商品占比和女性商品占比
if(maleRate >= 0.6)时,USG=male
Else If(femaleRate >= 0.6)时,USH=female
Else USG = 未知
上述分明已经计算出用户购物性别标签USG,为什么还有算法构建模型进行预测呢?
因为依据上述计算数据,构建分类算法模型以后,商品的种类会一直增加,颜色会一直变化,时代一直在变,我们不可能实时认为的去更新经验和数据,所以不如直接使用算法模型对用户进行预测,不需要按照规则/经验进行分类操作(这样真的很笨)。
用户画像数据
用户平时在电商平台上的购物行为、浏览行为、搜索行为以及订单购买情况都会被记录在案,探查其消费能力和兴趣等。数据归类以后,一般来说可以通过三类数据对用户进行划分和定义。
在拿到数据以后,会将每个环节进行拆解,落实到具体的行动策略上,大体根据以下流程进行模型的预估:
模型构建的流程:
解读流程
建立模型,然后采用对比组,将模型在小范围内投放使用,对比对照组(自然流量效果)就可以知道这个模型的优劣,然后再次进行训练,再次进行测试,再次进行对比……一次次执行,获取最佳模型
理解建模过程
虽然能够通过用户的行为、购买和兴趣等数据,了解用户的基本信息,但是仍然还是不清楚如何建模,该用什么语言去建模对吧?
购物性别的区分使用的时机器学习分类算法模型,但是算法也分有很多类别,包含逻辑回归、线性支持向量机、朴素贝叶斯模型以及决策树算法,这里我们主要讨论的时决策树算法;语言方面:使用大数据Spark MLlib机器学习库,Java、Scala和Python三种语言都支持,极其方便。
算法为什么选择决策树捏,因为决策树的有点比较多,追要是其变量处理灵活,不要求相互独立。可以处理大维度的数据,不需要预先对模型的特征有所了解。对于表达复杂的非线性模式和特征的相互关系,模型相对容易理解和解释,所以决定用决策树进行尝试。
说了那么多,到底什么是决策树?简单来说决策树就是:通过训练数据来构建一颗用于分类的树,从而对位置数据进行高效分类。
举个栗子
构建决策树的步骤为:
①起始阶段,所有历史数据作为一个主节点;
②选择某个属性,测试条件用于分割节点,以择偶标准模型为例,把长相作为首届点
③将颜值节点分割,以帅和丑作为条件,导致的结果作为其子节点,如分割成签收和是否公务员;
④对子节点,如签收和是否公务员,继续进行2、3步骤,知道节点满足停止分割的条件。
以上步骤中,能够得出一个结论,在构建决策树的过程中,最重要的是如何找到最好的分割点。
决策树值得注意的问题是过拟合问题,整个算法必须解决【如何停止分割】和【如何选择分割】两个关键问题。最简单的做法就是设定树的深度:MAXDept或枝叶的最少样本量MaxBin。
【关于决策树详细介绍可以去看我的前面那篇知识点补充:决策树基础知识补充 以及 决策树】
购物性定义
在建模前期,首先要考虑的事情就是先确定指标以及对样本的定义。购物性别指的是什么?通过哪些数据来确定购物性别,样本的准确性,如何验证数据的可信度等。
一般来说,用户填写的资料的可信度不高,对用户的性别数据需持怀疑态度,所以需要其他数据进行辅助证明其性别。订单数据能够真实反映用户的购买心态,预测购买行为,并且能够通过购买商品的所属类别去判断用户的购买倾向,最后得到性别特征类目。
根据数据结果,最终确认了购物性别的定义,分为:
购物性别【男】:N月购买的男性特征类目子下单数 > N月购买的女性特征类目子下单数
购物性别【女】:N月购买的女性特征类目子下单数 > N月购买的男性特征类目子下单数
购物性别【未知】:未下单男女特征类目
其中,N需要具体根据业务场景来定。
建模数据准备
一般来说,不同模型的训练其实大体雷同。从技术上来讲,各家算法基本类似,大多使用Spark MLlib,不同点是:所运算的模型针对的场景不一样。
数据集的划分,这种方式可以更好的根据数据的规模,提高模型的准确性
在全部的样本中,取80%的数据用于训练模型
在全部的样本中,取20%的数据用于数据测试
模型效果分析
根据各类参数的评估结果,以及人工经验选定的模型参数,建立模型。值得注意的是,决策树的深度不要过深,以防止过拟合的问题:
这张图就表示了对角线是是预测的数据,非对角线是是误差数据,从文字上理解就是:对角线上的数据和最右边的数据越相近越好;非对角线上的数据越接近0越好。
行业内当前采用数据挖掘、机器学习和推荐系统中的评测指标-准确率、召回率,准确率是应用最为广泛的数据指标,也很清晰易懂,以男性为例:
准确率=命中的男性用户数量/所有预测男性数量。一般来说,准确率可以评估模型的质量,他是很直观的数据评价,但并不是说准确率越高算法就越好。
召回率=命中的男性用户数量/所有男性数量,反映了被正确判定的正例的比重,
模型建立完成后,需要根据模型的结果与预期进行对比,进行调优。
代码实现
import cn.itcast.tags.models.{AbstractModel, ModelType}
import cn.itcast.tags.tools.TagTools
import org.apache.spark.broadcast.Broadcast
import org.apache.spark.ml.classification.{DecisionTreeClassificationModel, DecisionTreeClassifier}
import org.apache.spark.ml.evaluation.MulticlassClassificationEvaluator
import org.apache.spark.ml.feature.{VectorAssembler, VectorIndexer, VectorIndexerModel}
import org.apache.spark.sql.expressions.UserDefinedFunction
import org.apache.spark.sql.{Column, DataFrame, SparkSession}
import org.apache.spark.sql.functions._
/**
* 挖掘类型标签模型开发:用户购物性别标签模型
*/
class UsgModel extends AbstractModel("用户购物性别USG", ModelType.ML){
/*
378 用户购物性别
379 男 0
380 女 1
381 中性 -1
*/
override def doTag(businessDF: DataFrame, tagDF: DataFrame): DataFrame = {
val session: SparkSession = businessDF.sparkSession
import session.implicits._
/*
root
|-- cordersn: string (nullable = true)
|-- ogcolor: string (nullable = true)
|-- producttype: string (nullable = true)
*/
//businessDF.printSchema()
//businessDF.show(10, truncate = false)
//tagDF.printSchema()
/*
+---+----+----+-----+
|id |name|rule|level|
+---+----+----+-----+
|379|男 |0 |5 |
|380|女 |1 |5 |
|381|中性 |-1 |5 |
+---+----+----+-----+
*/
//tagDF.filter($"level".equalTo(5)).show(10, truncate = false)
// 1. 获取订单表数据tbl_tag_orders,与订单商品表数据关联获取会员ID
val ordersDF: DataFrame = spark.read
.format("hbase")
.option("zkHosts", "bigdata-cdh01.itcast.cn")
.option("zkPort", "2181")
.option("hbaseTable", "tbl_tag_orders")
.option("family", "detail")
.option("selectFields", "memberid,ordersn")
.load()
//ordersDF.printSchema()
//ordersDF.show(10, truncate = false)
// 2. 加载维度表数据:tbl_dim_colors(颜色)、tbl_dim_products(产品)
// 2.1 加载颜色维度表数据
val colorsDF: DataFrame = {
spark.read
.format("jdbc")
.option("driver", "com.mysql.jdbc.Driver")
.option("url",
"jdbc:mysql://bigdata-cdh01.itcast.cn:3306/?useUnicode=true&characterEncoding=UTF-8&serverTimezone=UTC")
.option("dbtable", "profile_tags.tbl_dim_colors")
.option("user", "root")
.option("password", "123456")
.load()
}
/*
root
|-- id: integer (nullable = false)
|-- color_name: string (nullable = true)
*/
//colorsDF.printSchema()
//colorsDF.show(30, truncate = false)
// 2.2. 构建颜色WHEN语句
val colorColumn: Column = {
// 声明变量
var colorCol: Column = null
colorsDF
.as[(Int, String)].rdd
.collectAsMap()
.foreach{case (colorId, colorName) =>
if(null == colorCol){
colorCol = when($"ogcolor".equalTo(colorName), colorId)
}else{
colorCol = colorCol.when($"ogcolor".equalTo(colorName), colorId)
}
}
colorCol = colorCol.otherwise(0).as("color")
// 返回
colorCol
}
// 2.3 加载商品维度表数据
val productsDF: DataFrame = {
spark.read
.format("jdbc")
.option("driver", "com.mysql.jdbc.Driver")
.option("url",
"jdbc:mysql://bigdata-cdh01.itcast.cn:3306/?useUnicode=true&characterEncoding=UTF-8&serverTimezone=UTC")
.option("dbtable", "profile_tags.tbl_dim_products")
.option("user", "root")
.option("password", "123456")
.load()
}
/*
root
|-- id: integer (nullable = false)
|-- product_name: string (nullable = true)
*/
//productsDF.printSchema()
///productsDF.show(30, truncate = false)
// 2.4. 构建商品类别WHEN语句
var productColumn: Column = {
// 声明变量
var productCol: Column = null
productsDF
.as[(Int, String)].rdd
.collectAsMap()
.foreach{case (productId, productName) =>
if(null == productCol){
productCol = when($"producttype".equalTo(productName), productId)
}else{
productCol = productCol.when($"producttype".equalTo(productName), productId)
}
}
productCol = productCol.otherwise(0).as("product")
// 返回
productCol
}
// 2.5. 根据运营规则标注的部分数据
val labelColumn: Column = {
when($"ogcolor".equalTo("樱花粉")
.or($"ogcolor".equalTo("白色"))
.or($"ogcolor".equalTo("香槟色"))
.or($"ogcolor".equalTo("香槟金"))
.or($"productType".equalTo("料理机"))
.or($"productType".equalTo("挂烫机"))
.or($"productType".equalTo("吸尘器/除螨仪")), 1) //女
.otherwise(0)//男
.alias("label")//决策树预测label
}
// 3. 关联订单数据,颜色维度和商品类别维度数据
val goodsDF: DataFrame = businessDF
// 关联订单数据:
.join(ordersDF, businessDF("cordersn") === ordersDF("ordersn"))
// 选择所需字段,使用when判断函数
.select(
$"memberid".as("userId"), //
colorColumn, // 颜色ColorColumn
productColumn, // 产品类别ProductColumn
// 依据规则标注商品性别
labelColumn
)
//goodsDF.printSchema()
//goodsDF.show(100, truncate = false)
// TODO: 直接使用标注数据,给用户打标签
val predictionDF: DataFrame = goodsDF.select($"userId", $"label".as("prediction"))
// 4. 按照用户ID分组,统计每个用户购物男性或女性商品个数及占比
val genderDF: DataFrame = predictionDF
.groupBy($"userId")
.agg(
count($"userId").as("total"), // 某个用户购物商品总数
// 判断label为0时,表示为男性商品,设置为1,使用sum函数累加
sum(
when($"prediction".equalTo(0), 1).otherwise(0)
).as("maleTotal"),
// 判断label为1时,表示为女性商品,设置为1,使用sum函数累加
sum(
when($"prediction".equalTo(1), 1).otherwise(0)
).as("femaleTotal")
)
/*
root
|-- userId: string (nullable = true)
|-- total: long (nullable = false)
|-- maleTotal: long (nullable = true)
|-- femaleTotal: long (nullable = true)
*/
//genderDF.printSchema()
//genderDF.show(10, truncate = false)
// 5. 计算标签
// 5.1 获取属性标签:tagRule和tagName
val rulesMap: Map[String, String] = TagTools.convertMap(tagDF)
val rulesMapBroadcast: Broadcast[Map[String, String]] = session.sparkContext.broadcast(rulesMap)
// 5.2 自定义UDF函数,计算占比,确定标签值
// 对每个用户,分别计算男性商品和女性商品占比,当占比大于等于0.6时,确定购物性别
val gender_tag_udf: UserDefinedFunction = udf(
(total: Long, maleTotal: Long, femaleTotal: Long) => {
// 计算占比
val maleRate: Double = maleTotal / total.toDouble
val femaleRate: Double = femaleTotal / total.toDouble
if(maleRate >= 0.6){ // usg = 男性
rulesMapBroadcast.value("0")
}else if(femaleRate >= 0.6){ // usg =女性
rulesMapBroadcast.value("1")
}else{ // usg = 中性
rulesMapBroadcast.value("-1")
}
}
)
// 5.3 获取画像标签数据
val modelDF: DataFrame = genderDF
.select(
$"userId", //
gender_tag_udf($"total", $"maleTotal", $"femaleTotal").as("usg")
)
//modelDF.printSchema()
//modelDF.show(100, truncate = false)
// 返回画像标签数据
modelDF
}
}
object UsgModel {
def main(args: Array[String]): Unit = {
val tagModel = new UsgModel()
tagModel.executeModel(378L)
}
}
(叠甲:大部分资料来源于黑马程序员,这里只是做一些自己的认识、思路和理解,主要是为了分享经验,如果大家有不理解的部分可以私信我,也可以移步【黑马程序员_大数据实战之用户画像企业级项目】https://www.bilibili.com/video/BV1Mp4y1x7y7?p=201&vd_source=07930632bf702f026b5f12259522cb42,以上,大佬勿喷)