实验介绍

数据采用Criteo Display Ads。这个数据一共11G,有13个integer features,26个categorical features。

Spark

由于数据比较大,且只在一个txt文件,处理前用split -l 400000 train.txt 对数据进行切分。

连续型数据利用log进行变换,因为从实时训练的角度上来判断,一般的标准化方式,如Z-Score和最大最小标准化中用到的值都跟某一批数据的整体统计结果有关,换一批数据后标准化就程度就不一样了。

而对于离散型分类数据,一般企业应该都会有类别表而不需要自己从数据中获取(这样能节省计算时间,而且流处理下只能针对特定批量或者时间段出现的数据进行数字编码,所以对超出该批量和时间的新类别就无法进行编码了)。虽然如此,如果在离线情况且真的需要自己从数据中提取类别并进行编码,比如现在这种情况,最直接的方法是使用ML模块的StringIndexer。这个工具方面使用,但是对于数据类别过多或者需要进行编码的列数量较多时容易出现OOM。通过StringIndexer的源码可以知道,它的实现是先利用rdd的countByValue得出特定列的统计map,然后出现频率最大的编码为0,第二的为1,如此类推。另外,它会copy这个map,而StringIndexer本身并没有提供删除这个map的方法,所以如果出现上述数据类别过多或者需要进行编码的列数量较多便会积累大量的map。而刚好这份数据有26种类别数据,且某些类别的种类居然能有三百多万种,所以只能另辟蹊径。下面的方法效仿StringIndexer的部分实现来达到目的,而且运行效率比之前有了很大的提升。当然,由于某些类别出现的频率很低,也可以采取一些cutoff措施,比如该用countByValue,只保留前n个类别,或者保留频率在某个数值以上的类别。

下面实现考虑cutoff,出现次数少于万分之一的类别统一归类为UNK。

  1. val spark = SparkSession
  2. .builder()
  3. .master("local[*]")
  4. // 这里的driver.memory和memory.fraction只做展示,实际使用中要在driver启动前设置才有效。即如果在idea中想增大driver的大小,这需要在VM option中设置堆大小。另外,local模式下设置提高driver大小即可,因为executor也是在同一个JVM进程中。
  5. .config("spark.driver.memory", 5G)
  6. .config("spark.sql.shuffle.partitions", 12)
  7. .config("spark.default.parallelism", 12)
  8. .config("spark.memory.fraction", 0.75)
  9. .getOrCreate()
  10. import org.apache.spark.sql.functions._
  11. val path = ""
  12. // 数据源是txt文件,但可以通过csv来推断格式
  13. val df = spark.read
  14. .option("header", false)
  15. .option("delimiter", "\t")
  16. .option("inferSchema", true)
  17. .format("csv")
  18. .load(path + "..")
  19. // 如果内存够大,先把它全部加载到内存,减少IO
  20. df.persist(StorageLevel.MEMORY_AND_DISK_SER)
  21. val dataSize = df.count()
  22. val cutoff = dataSize * 0.0001
  23. val numCols = (1 until 14).map(i => s"_c$i").toArray
  24. var df1 = df
  25. numCols.foreach(column => {
  26. df1 = df1.withColumn(column, when(col(column).isNull, 0).otherwise(log(col(column) + 10)))
  27. })
  28. val catCols = (14 until 40).map(i => s"_c$i")
  29. var df2 = df1
  30. // 所有cat列统一编码
  31. // 使用java的map,通常java的集合比scala的更效率,而且java的hashmap能够初始化大小
  32. val inderMap: util.HashMap[String, util.HashMap[String, Int]] = new util.HashMap(catCols.length)
  33. var i = 0
  34. for (column <- catCols) {
  35. val uniqueElem = df2.select(column)
  36. .groupBy(column)
  37. .agg(count(column))
  38. .filter(col(s"count($column)") >= cutoff)
  39. .select(column)
  40. .map(_.getAs[String](0))
  41. .collect()
  42. val len = uniqueElem.length
  43. var index = 0
  44. val freqMap = new util.HashMap[String, Int](len)
  45. while (index < len){
  46. freqMap.put(uniqueElem(index), i)
  47. index += 1
  48. i += 1
  49. }
  50. freqMap.put("UNK", i)
  51. i += 1
  52. inderMap.put(column, freqMap)
  53. }
  54. val bcMap = spark.sparkContext.broadcast(inderMap)
  55. for (column <- catCols) {
  56. val Indexer = udf { elem: String =>
  57. val curMap = bcMap.value.get(column)
  58. if (elem == null || !curMap.containsKey(elem)) curMap.get("UNK")
  59. else curMap.get(elem)
  60. }
  61. df2 = df2.withColumn(column + "_e", Indexer(col(column))).drop(column)
  62. }
  63. // 如需要划分训练集和测试集
  64. val Array(train, test) = df2.randomSplit(Array(0.9, 0.1))
  65. // parquet输出
  66. df2.write
  67. .mode("overwrite")
  68. .save(path + "res/")
  69. // txt输出
  70. df2.map(x => x.mkString(","))
  71. .write
  72. .mode("overwrite")
  73. .format("text")
  74. .save(path + "res/txt")
  75. // 后面tensorflow需要
  76. print("The total dimension of all categorical features is " + i) // 14670

Tensorflow

下面代码大致介绍深度学习的整体流程,生产环境的代码需要做一定的修改,可以参考“https://github.com/yangxudong/deeplearning/tree/master/DCN” 和 “https://github.com/lambdaji/tf_repos/tree/master/deep_ctr” 两个GitHub的实现。

大致流程:定义数据输入函数input_fn,然后开始规划模型和它的训练和测试operation,最后是执行阶段的代码。

input function

  1. def input_fn(filenames, batch_size=32):
  2. def _parse_line(line):
  3. fields = tf.decode_csv(line,FIELD_DEFAULTS)
  4. label = fields[0]
  5. num_features = fields[1:14]
  6. cat_features = fields[14:]
  7. return num_features, cat_features, label
  8. num_features, cat_features, label = tf.data.TextLineDataset(filenames)\
  9. .repeat(2)\
  10. .prefetch(1024)\
  11. .batch(batch_size)\
  12. .map(_parse_line, num_parallel_calls=2)\
  13. .make_one_shot_iterator()\
  14. .get_next()
  15. return num_features, cat_features, label

数据和模型的变量

  1. # 构建一些input需要用到的参数。
  2. NUM_COLUMNS = ["c%d" % i for i in range(14)]
  3. CAT_COLUMNS = ["c%d" % i for i in range(14,40)]
  4. FIELD_DEFAULTS = []
  5. for i in range(14):
  6. FIELD_DEFAULTS.append([0.0])
  7. for i in range(14,40):
  8. FIELD_DEFAULTS.append([0])
  9. filenames = []
  10. for i in range(24):
  11. ...
  12. # 本地调试
  13. num_col = 13
  14. cat_col = 26
  15. cat_size = 14670
  16. embedding_size = 12
  17. cross_layers = 3
  18. deep_layers = [200,100,33]
  19. label_size = 1
  20. learning_rate = 0.0005

DCN模型

  1. # DCN模型的构建,这里利用LOW Level API,实际上按照custom Estimator的流程会更好。
  2. with tf.name_scope("DCN_model"):
  3. he_init = tf.variance_scaling_initializer()
  4. with tf.name_scope("Embedding_layer"):
  5. x1, x2, label = input_fn(filenames,32)
  6. Embed_W = tf.get_variable(name='embed_w', shape=[cat_size, embedding_size],
  7. initializer=he_init) # TC * E
  8. embeddings = tf.nn.embedding_lookup(Embed_W, x2) # ? * C * E
  9. oned_embed = tf.reshape(embeddings, shape=[-1, cat_col * embedding_size]) # ? * (C * E)
  10. embed_layer_res = tf.concat([x1, oned_embed], 1) # ? * (N + C * E)
  11. with tf.name_scope("Cross_Network"):
  12. x0 = embed_layer_res
  13. cross_x = embed_layer_res
  14. for level in range(cross_layers):
  15. Cross_W = tf.get_variable(name='cross_w%s' % level, shape=[num_col + cat_col * embedding_size, 1],
  16. initializer=he_init) # (N + C * E) * 1
  17. Cross_B = tf.get_variable(name='cross_b%s' % level, shape=[1,num_col + cat_col * embedding_size],
  18. initializer=he_init) # (N + C * E) * 1
  19. xtw = tf.matmul(cross_x, Cross_W) # ? * 1
  20. cross_x = x0 * xtw + cross_x + Cross_B # (N + C * E) * 1
  21. with tf.name_scope("Deep_Network"):
  22. deep_x = embed_layer_res
  23. for neurons in deep_layers:
  24. deep_x = tf.layers.dense(inputs=deep_x, units=neurons, name='deep_%s' % neurons,
  25. activation=tf.nn.selu, kernel_initializer=he_init)
  26. with tf.variable_scope("Output-layer"):
  27. x_stack = tf.concat([cross_x, deep_x], 1) # ? * ((N + C * E) + deep_layers[-1])
  28. logits = tf.layers.dense(inputs=x_stack, units=label_size, name="outputs")
  29. z = tf.reshape(logits, shape=[-1])
  30. pred = tf.sigmoid(z)

训练和评估指标

  1. with tf.name_scope("loss"):
  2. xentropy = tf.nn.sigmoid_cross_entropy_with_logits(labels=label, logits=z)
  3. loss = tf.reduce_mean(xentropy, name="loss")
  4. loss_summary = tf.summary.scalar('log_loss', loss)
  5. with tf.name_scope("train"):
  6. optimizer = tf.train.AdamOptimizer(learning_rate=learning_rate, beta1=0.9, beta2=0.999, epsilon=1e-8)
  7. training_op = optimizer.minimize(loss)
  8. with tf.name_scope("eval"):
  9. acc, upacc = tf.metrics.accuracy(label, tf.math.round(pred))
  10. auc, upauc = tf.metrics.auc(label, pred)
  11. acc_summary = tf.summary.scalar('accuracy', upacc)
  12. auc_summary = tf.summary.scalar('auc', upauc)

TensorBroad相关设置,optional

  1. from datetime import datetime
  2. def log_dir(prefix=""):
  3. now = datetime.utcnow().strftime("%Y%m%d%H%M%S")
  4. root_logdir = "tf_logs"
  5. if prefix:
  6. prefix += "-"
  7. name = prefix + "run-" + now
  8. return "{}/{}/".format(root_logdir, name)
  9. logdir = log_dir("my_dcn")
  10. file_writer = tf.summary.FileWriter(logdir, tf.get_default_graph())

执行阶段

  1. # 包含checkpoint、early stop。同样,这里利用LOW Level API,实际上按照custom Estimator的流程会更好。
  2. n_epochs = 2
  3. data_size = 12000000
  4. batch_size = 64
  5. n_batches = int(np.ceil(data_size / batch_size))
  6. checkpoint_path = ".../model/my_dcn_model.ckpt"
  7. checkpoint_epoch_path = checkpoint_path + ".epoch"
  8. final_model_path = "./my_deep_mnist_model"
  9. best_auc = np.infty
  10. epochs_without_progress = 0
  11. max_epochs_without_progress = 20
  12. saver = tf.train.Saver()
  13. gb_init = tf.global_variables_initializer()
  14. lc_init = tf.local_variables_initializer()
  15. with tf.Session() as sess:
  16. if os.path.isfile(checkpoint_epoch_path):
  17. with open(checkpoint_epoch_path, "rb") as f:
  18. start_epoch = int(f.read())
  19. print("Training was interrupted. Continuing at epoch", start_epoch)
  20. saver.restore(sess, checkpoint_path)
  21. else:
  22. start_epoch = 0
  23. sess.run([gb_init,lc_init])
  24. for epoch in range(start_epoch, n_epochs):
  25. for batch_index in range(n_batches):
  26. # 每2000批数据测试一遍
  27. if batch_index % 2000 != 0:
  28. sess.run(training_op)
  29. else:
  30. loss_tr, loss_summary_str, up1, up2, acc_summary_str, auc_summary_str = sess.run([loss, loss_summary, upacc, upauc, acc_summary, auc_summary])
  31. print("Epoch:", epoch, ",Batch_index:", batch_index,
  32. "\tLoss: {:.5f}".format(loss_tr),
  33. "\tACC: ", up1,
  34. "\tAUC", up2)
  35. file_writer.add_summary(acc_summary_str, batch_index)
  36. file_writer.add_summary(auc_summary_str, batch_index)
  37. file_writer.add_summary(loss_summary_str, batch_index)
  38. if batch_index % 5000 == 0:
  39. saver.save(sess, checkpoint_path)
  40. with open(checkpoint_epoch_path, "wb") as f:
  41. f.write(b"%d" % (epoch + 1))
  42. if up2 < best_auc:
  43. saver.save(sess, final_model_path)
  44. best_auc = up2
  45. else:
  46. epochs_without_progress += 1
  47. if epochs_without_progress > max_epochs_without_progress:
  48. print("Early stopping")
  49. break

参考:

玩转企业级Deep&Cross Network模型你只差一步

基于Spark和Tensorflow构建DCN模型进行CTR预测的更多相关文章

  1. Spark学习笔记——构建分类模型

    Spark中常见的三种分类模型:线性模型.决策树和朴素贝叶斯模型. 线性模型,简单而且相对容易扩展到非常大的数据集:线性模型又可以分成:1.逻辑回归:2.线性支持向量机 决策树是一个强大的非线性技术, ...

  2. tensorflow构建CNN模型时的常用接口函数

    (1)tf.nn.max_pool()函数 解释: tf.nn.max_pool(value, ksize, strides, padding, data_format='NHWC', name=No ...

  3. 基于spark邮件自动分类

    代码放在github上:click me 一.数据说明 数据集为英文语料集,一共包含20种类别的邮件,除了类别soc.religion.christian的邮件数为997以外每个类别的邮件数都是100 ...

  4. 客户流失?来看看大厂如何基于spark+机器学习构建千万数据规模上的用户留存模型 ⛵

    作者:韩信子@ShowMeAI 大数据技术 ◉ 技能提升系列:https://www.showmeai.tech/tutorials/84 行业名企应用系列:https://www.showmeai. ...

  5. 基于Spark ALS构建商品推荐引擎

    基于Spark ALS构建商品推荐引擎   一般来讲,推荐引擎试图对用户与某类物品之间的联系建模,其想法是预测人们可能喜好的物品并通过探索物品之间的联系来辅助这个过程,让用户能更快速.更准确的获得所需 ...

  6. FaceRank-人脸打分基于 TensorFlow 的 CNN 模型

    FaceRank-人脸打分基于 TensorFlow 的 CNN 模型 隐私 因为隐私问题,训练图片集并不提供,稍微可能会放一些卡通图片. 数据集 130张 128*128 张网络图片,图片名: 1- ...

  7. 基于Spark的GBDT + LR模型实现

    目录 基于Spark的GBDT + LR模型实现 数据预处理部分 GBDT模型部分(省略调参部分) GBDT与LR混合部分 基于Spark的GBDT + LR模型实现 测试数据来源http://arc ...

  8. PLUTO平台是由美林数据技术股份有限公司下属西安交大美林数据挖掘研究中心自主研发的一款基于云计算技术架构的数据挖掘产品,产品设计严格遵循国际数据挖掘标准CRISP-DM(跨行业数据挖掘过程标准),具备完备的数据准备、模型构建、模型评估、模型管理、海量数据处理和高纬数据可视化分析能力。

    http://www.meritdata.com.cn/article/90 PLUTO平台是由美林数据技术股份有限公司下属西安交大美林数据挖掘研究中心自主研发的一款基于云计算技术架构的数据挖掘产品, ...

  9. 在R中使用Keras和TensorFlow构建深度学习模型

    一.以TensorFlow为后端的Keras框架安装 #首先在ubuntu16.04中运行以下代码 sudo apt-get install libcurl4-openssl-dev libssl-d ...

随机推荐

  1. 77-CCI,Commodity Channel Index,商品通道指标.(2015.7.1)

    CCI,Commodity Channel Index 商品通道指标 Channel Index,商品通道指标.(2015.7.1)" title="77-CCI,Commodit ...

  2. Python使用selenium实现网页用户名 密码 验证码自动登录功能

    一.安装selenium 二.下载谷歌浏览器驱动 1.去http://chromedriver.storage.googleapis.com/index.html下载chromedriver.exe( ...

  3. 张小龙最新内部演讲:KPI 是副产品,警惕复杂流程

    张小龙最新内部演讲:KPI 是副产品,警惕复杂流程 各位 WXG(微信事业群)的同事们,大家早上好!又到我们一年一度的领导力大会. 大家都看到,我们微信团队膨胀还是比较快的,有 1500 多人了.对此 ...

  4. B. Mr. Kitayuta's Colorful Graph,二维并查集,一个简单变形就可以水过了~~

    B. Mr. Kitayuta's Colorful Graph ->  Link  <- 题目链接在上面,题目比较长,就不贴出来了,不过这是道很好的题,很多方法都可以做,真心邀请去A了这 ...

  5. ORACLE RAC with NFS install

    第一步:建立第一台节点机1).拷贝一台虚拟机做第一个节点,增加host-only的网卡 kudzu工具识别网卡(两都host-only是可以的).然后以下步骤修改相应的配置.etc/hosts内容如下 ...

  6. Oracle锁表数据查询及解决方法

    首先:查询数据那些表被锁定1. SELECT l.session_id sid, s.serial#, l.locked_mode,l.oracle_username, l.os_user_name, ...

  7. 整体二分--BZOJ1901: Zju2112 Dynamic Rankings

    n<=10000个数有m<=10000个操作,1.询问一个区间的第k小的数:2.单点修改. 带修主席树. 整体二分. 整体二分的必要条件: #include<string.h> ...

  8. vim fulerformat的设置

    在vim中设置选项,有注释很容易明白: set laststatus=1 "2总显示最后一个窗口的状态行,1窗口多于一个时显示最后一个窗口的状态行,0不显示最后一个窗口的状态行 fulerf ...

  9. intent使用Serializable传递对象

    package com.pingyijinren.test; import android.content.Intent; import android.support.v7.app.AppCompa ...

  10. [bzoj3238][Ahoi2013]差异_后缀数组_单调栈

    差异 bzoj-3238 Ahoi-2013 题目大意:求任意两个后缀之间的$LCP$的和. 注释:$1\le length \le 5\cdot 10^5$. 想法: 两个后缀之间的$LCP$和显然 ...