番外篇-USG用户购物性别标签(详细思路可以去看RFM篇)

概述

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,以上,大佬勿喷)

  • 31
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值