机器学习特征处理详解与 tensorflow feature_column 接口实战
书接上文,在 模型手把手系列的 前两篇 文章 中,我们 已经 详细介绍 了 python、spark 和 java 生成TFrecord 和 六种方法构建读入batch样本 ,按照 常规 机器学习模型pipline 的 流程 来说,我们应该在 使用 dataset 构建好的 batch 数据上开始 分别 对 读入的各个特征进行处理 例如 特征数值化、取embeding等操作,然后 输入模型 的 过程 了,那么 本文 就从 这里开始 吧~
(资料图片)
因为本系列 开发的模型主要 使用的是 tensorflow,而 tensorflow 官方 有着 自己实现的特征处理接口 feature_column,非常好用 且 业界使用 的 非常广泛,这里 强烈 安利 下~ 。本文 这里 不欲对 feature_column 接口的 参数 做解释 ,而是 更侧重于在 每一步得到 的 数据形式 做一些 说明,方便 我们 灵活对 数据 输入 进行定制 和 debug.
feature_column 接口 本来是 Google为了 适配 tensorflow estimator这个 模型训练 的 高阶接口使用 的,但它 既然 能 方便处理特征,并且 特征处理 殊途同归,当然 我们也 可以将 feature_column 接口 配合 tensorflow keras开发模型使用, 亲测 也 非常好用 哦。这里要 重点推荐一下 estimator接口,使用 estimator 开发的 单机版模型可以直接适配分布式模型 训练,代码 无需怎么 改动,非常强大!!! 在本系列 后期 我们 也会写 几篇关于 使用 estimator 搭建 模型的文章,感兴趣 得 同学可以 关注下 后续的 文章 哦 ~
闲言少叙,下面 就让 我们 开始 本文的 机器学习 特征处理方法介绍吧~
在 深入浅出理解word2vec模型 (理论与源码分析) 文章中,我们说过 自从 2013年 embeding 诞生 以来 就被 业界的 深度学习模型进行了 深入而广泛 的 应用,特别是在 高维稀疏的sparse ID 类特征应用 特别 广泛。从 常规意义 上来 说,对于 推荐系统或广告算法系统,百分之80的 特征 均是以 高维稀疏 的 ID类 特征 的形式 出现的。所以 我们 开发 DNN 模型的时候,对于 高维稀疏 的 ID类特征 甚至是 用户历史 行为序列特征,我们 总是 会 先以某种 方式 去 取得 该 ID 的 embeding , 然后 进行 加减乘或则拼接、求attention等 花样的 骚操作。
在 企业级机器学习 Pipline - 特征feature处理 - part 1 文章中,我们 列出了 搜广推算法中 经常用到 的 一些特征的 设计方法 包括: 交叉特征、序列特征、实时特征等,错过 以前文章 的 同学,可以 戳进去 看看 哦 。从 上文中 我们 知道了 有哪些特征 可以用,但是 在 模型设计 中 真正的把 数据 以合适的格式 读进去 适配 模型设计的需要,则是 有着 道与术的 鸿沟。而 其实 在 模型开发 中,我们 大多数 时间 均是 花在了 模型 的 数据处理 上。
一般意义 上来说,对于 浮点数特征,我们 可以用 一些方式 (例如 log / xgboost ) 进行 分桶 离散化然后求 embeding 扔进 模型 里,或则 直接 读入 浮点数 将 它和 其它特征的 embeding 拼接后 传入网络 等。 但是 目前的 实验 来看,统计类的浮点数特征直接扔进模型效果提升总是不太明显。
而在 实际操作 中,对于 一些类别类型 的 category特征,我们 通常会 构建特有的 embeding matrix, 当然 我们 也可以 多个特征 共用一个 embeding matrix, 就像 后文 要介绍的 tf.feature_column.shared_embeddings
。我们 可以将 用户 刚下载的 Appid 和 用户 最近3天打开过 的 appid 用一个 embeding 去训练 也是 一种 不错的选择 ,其可以 有效缓解 因 数据稀疏 导致的 训练不充分的问题。
更细致 的说,要想 得到 某 ID 的 embeding, 我们通常 需要 根据索引 (ids)去 embeding matrix 中 查找(look_up),这里 我们 就 需要 先有这个 id,一般 这个id是数值 int型 的,而 我们 常规使用 的 高维稀疏 特征 大多 是 字符串 类型的sparse ID, 所以 常规情况下我们 需要 去对 每一列特征 对应的 去维护 一个索引id,id 和 该 特征 的 取值unique 个数 一一对应,一般 这个 过程 又称为 特征取值ID化。我们 取出 该特征 对应的ID 对应的 embeding , 将 高维sparse特征转化为低维的embedding,则可以 进行 模型 数据 的 语义计算了。
如 上文 所说 ,在 特征取值ID化这一步,如果 我们的 模型 中 特征的取值个数和 特征种类个数非常多 的话,我们 就需要 每天 去对 每列特征 的 旧的ID集合上 加上 新增的 特征取值 并 重新构建索引输入模型 中 训练,多个特征 均 需要 如此,对于 搜广推模型 上 动则上亿的 稀疏特征 来说,可以想象 这是一个 非常复杂 且 难以 持久维护 的 工程,而 早期的 很多大厂 的 dnn 模型均是如此处理的,可怕 ~
好在 tensorflow 中提供了 feature_column接口,它可以 支持 将 每个 特征hash后 快速得到 一定数值的 索引id, 该特征 空间大小 可以 自行定制。既然是 hash ,肯定 避免不了冲突,这里 我们 不在展开,自己根据 业务 调整 hash空间大小 即可。 类似的 特征ID化 工具,我知道 百度的 paddelpaddle 深度学习框架 中也有 类似的 hash函数 的 设计。
既然是说 feture_column,我们就不得不祭出这张图了。
从 上图中 我们 可以知道,feature column 处理特征 可以分为 Categorical Column和 Dense Column两大类,这其实 和 我们 前面介绍 的 特征总共 分成 dense和 sparse两类特征 是 一个意思。 当然,图中的 一些接口 在 某些场景 里 需要 组合使用,也和我们上文 介绍的 特征处理 流程 差不太多。而一列特征要想接入DNN模型,则需要 先转化 为 DenseColumn才可以。至于 如何组合 ,在下文 我们 会 进行 一些说明,但是从 数据取值类型和 我们 自己的 先验知识,推断 出来 某个位置 取值 应该是 什么 类型和 形状也是 不难 的~
当然 除了 图中 的 一些接口 之外,feature_column 还有一些 以 sequences开头的 处理序列特征的接口,我们的 文章中,一直 强调了 序列特征 的 重要性,因为 序列特征 的 出现 让我们 不再是 孤立的 看待 用户的行为,而是在 时间序列上 连续的 建模用户的 特性和偏好,用 历史的、发展的,普遍联系 的 眼光来 分析 用户,在 工业实践 中 具有 举足轻重 的意义。后面 我们 也会写 一些 介绍序列建模 的 文章,感兴趣 的 同学可以 持续关注 下哦。
这里 需要注意的一点是:feature_column 接口在tensorflow 1.x系列和 2.x系列 均有支持,但是也有一些 细微的 差别。对于普通特征,在 tensorflow 1.x 中,我们可以 通过 tf.feature_column.input_layer
处理 features 得到 dense feature,而 序列特征 可以使用 tf.feature_column.sequence_input_layer
。但是在 2.x 中,该 接口 均不在支持。在2.x 中 改为了 通过 tf.keras.layers.DenseFeatures
处理 features 得到 dense feature,序列 特征 可以 通过 tf.keras.experimental.SequenceFeatures
来 得到。而我们 下文的 代码 均是 基于 tensorflow 2.x 开发的。
其实要想对 tensorflow 的 使用 与 设计思想 进行 更深入 的了解,我们 可以 直接 去看 源码,源码 的 说明非常详细 ,并且 对 参数进行了 保姆级的 说明,还列举出了 使用的demo。 具体路径见下面这2个链接:
源码地址 tensorflow源码
接口介绍 feature_column接口介绍
好吧,文字部分 就 这些吧,代码才是硬通货,接下来 就让 我们 一起开始 接口 的 更进一步 的 代码介绍吧~
我们 这里 只挑一些常用 的 接口说明,主要涵盖 数值特征处理接口、类别特征hash化接口、序列特征和dataset结合使用方法、特征交叉、embedding共享等,而其他 的 类似接口 则 可以 常规类比推算 过去哈。
毕竟 万变不离其宗,掌握 数据的流程以及各阶段数据的形式比会用多少接口更加重要。我们 会在 介绍接口 的时候 说明 该接口 的 适用场景,注意 看旁白 哦~
@ 欢迎关注作者公众号 算法全栈之路import tensorflow as tfnumber = tf.feature_column.numeric_column("price")price_feature_dict = {"price": [[2.0],[3.0],[4.0]]}# 用这种数据解析方法来解析dict数据# 这里感觉更像是定义了一种数据解析方法 output = tf.keras.layers.DenseFeatures(number)(price_feature_dict)print(output)
从名字 我们 就可以 numeric_column
可以读入 数值类型 的特征 ,我们输入 统计类的浮点数特征或则 其他 不需要 分桶、且 也不需要 embeding 的 特征 可以 使用。
这里我们可以重点看一下:output 返回的就是一个 浮点数tensor, 维度 没有 改变。
我们使用 tf.feature_column.numeric_column
接口 定义了 处理 price 列 字段 的方法,这个方法 返回的值,我们可以通过 tf.keras.layers.DenseFeatures
接口( tensorflow 2.x 支持 ) 来查看。而 tf.keras.layers.DenseFeatures(number)
到 这里 整体( 包括((number)) )其实 就定义了 对 该列特征 的 处理方法, 后面括号 里的 (price_feature_dict)
是 这个方法 的 输入参数 。
这里我们 刻意把分行的 写法 合并在了一起,方便理解:注意 两个括号 的 连接,第一个括号 是 给方法用的,是 处理方法 的 一部分。第二个括号 才是 根据输入 得到 具体的值 ,并用 前面定义的方法 来 处理该输入值 。整体 来看 就是: 对输入特征的某个字段定义了一种什么处理方法。
因为 tensorflow 2.x 支持 eager模式,所以输出 变量取值 就和 python一样,直接 打印变量 就 OK。
这里 插入一个深坑, 可能 引起 bug 的 地方 就是: 我们 使用 简单 自定义数据 测试接口 和 使用 dataset 数据 测试的 时候,略有不同。注意看:代码里的 price_feature_dict
就是 我们 输入 的特征,这里要 注意 每一个元素都是被[]
包裹着的([2.0]
), 是一个数组,而我们上一篇文章 tensorflow 六种方法构建读入batch样本(含序列特征处理),踩坑经验值得收藏 介绍的 batch 数据里,每一列 特征 都是 仅仅 只有数值,不被 []
包裹,在 tensor 的 世界里也就是 维度上少了一维。
解决方法就是:在上一篇文章里介绍的 train_raw_dataset
后面 可以接入 这段代码 就可以在 dateset 上 测试代码 运行通过:
@ 欢迎关注作者公众号 算法全栈之路final_dataset = train_raw_dataset.apply(tf.data.experimental.ignore_errors()) .shuffle(2)..batch(BATCH_SIZE, drop_remainder=True).repeat(NUM_EPOCHS).prefetch(tf.data.experimental.AUTOTUNE)
上面是 一个插曲,仅仅 是 为了说明 批量跑模型的时候,数据格式 略有不同 而已。如果你 不用 dataset 读入 数据 来测试 这个接口 则不用关注。
上面 打印的 output 输出的 最后 返回数据 长这样:
@ 欢迎关注作者公众号 算法全栈之路import tensorflow as tfage_feature_dict = {"age": [[2.0],[3.0],[4.0]]}age_bucket = tf.feature_column.bucketized_column(tf.feature_column.numeric_column(key='age', shape=(1,),default_value=0,dtype=tf.dtypes.float32),boundaries=[20, 40, 50, 60])feature_layer = tf.keras.layers.DenseFeatures(age_bucket)output = feature_layer(age_feature_dict)print(output)# 返回的是onehot值,维度改变
这里的 bucketized_column
接口 很好理解,就是 根据 接口限定 的 边界(boundaries) 进行分桶。对于 浮点数类型 的 特征,我们 需要分桶的,这里 给定分桶边界就 可以 了。
注意 分桶 是 基于 数组比较的,所以 这个 接口 需要先将 输入数据 确定为 数值 才能 进行 比较分桶,所以 只能 和 2.1 介绍的 numeric_column一起 组合使用。 接口 一起组合 使用 在 feature_column
处理 接口 中是 非常常见 的。
这里 我们要 更详细 的 赘述一下: bucketized_column 返回的 是 和 boundaries 维度 大小 相同的 onehot 数组。数值 落在 哪个区间,则 那个维度 的 取值为 1,其他维度 为0 。
拿到了 onehot 之后,当然我们后边也可以在接入 embeding_column 得到 embeding之后, 通过 DenseFeatures
将 具体的 embeding 展示 出来。这里没有接入 embeding_column 而直接接了 DenseFeatures ,所以返回的是onehot
。
@ 欢迎关注作者公众号 算法全栈之路import tensorflow as tffeatures = {'video_id': tf.sparse.from_dense([[2, 85, 0, 0, 0],[33,78, 2, 73, 1]])}video_id = tf.feature_column.categorical_column_with_identity( key='video_id', num_buckets=100,default_value=0)# 说明 sparse tensor 可以直接传入categorical_column_with_identity # 后面直接接入 embedinig columns = [tf.feature_column.embedding_column(video_id, 9)]input_layer = tf.keras.layers.DenseFeatures(columns)dense_tensor = input_layer(features)print(dense_tensor)
categorical_column_with_identity
可以 返回 onehot 数据,我们使用 dd=tf.feature_column.indicator_column(video_id)
将 dd 塞入 DenseFeatures
中查看。这里是 序列数据,所以 返回的是 multI hot 形式的数据。 这个 接口 使用的 非常广泛, identity 属于 直接类型 , 无需 映射 ,直接 输入 类别。
这里我们可以看到 tf.sparse.from_dense
是将 输入的 dense 数据 转成了 sparse tensor 的 格式。 我们知道:sparse 和 dense 其实描述的是同一份数据,只是用的是不同的形式。
从 上面的 代码 我们 也可以 看出:sparse tensor 可以直接传入categorical_column_with_identity
。这就 非常强大 了,因为 我们在 很多时候用 tf.string_split()
返回的就是 sparse tensor 的格式,这样 我们 就可以 处理 变长字符串了。用 tf.string_split()
切割 字符串,然后 扔进categorical_column_with_identity
,后面 再 接入 embeding_column 拿到 embedinig ,这 数据不是 处理的 一气呵成,非常丝滑吗 ~
同时 sparse tensor 数据也可以直接接入 tf.keras.embeding哦,非常好用哦!!!
@ 欢迎关注作者公众号 算法全栈之路hash_word = tf.feature_column.embedding_column( tf.feature_column.categorical_column_with_hash_bucket(key='adid', hash_bucket_size=100, dtype=tf.dtypes.string),4)feature_dict = {"adid": ["20", "127", "51", "3"]}feature_layer = tf.keras.layers.DenseFeatures(hash_word)output = feature_layer(feature_dict)print(output)# 在这里将 embedding_column 换成 indicator_column 列将能看到返回的是 onthot
这个 接口 应该是 算法工程师们使用的 最多 的 接口 了, 顾名思义 ,将 类别id类 的 特征hash数值化。在这里 tf.feature_column.categorical_column_with_hash_bucket
返回的 是 onehot或则 sparse tensor。onehot 或则 sparse tensor 在 这里 并没有 严格的区分,均可以 打印 出来 查看 格式。本事例中,output 最后输出embeding 长这样:
这些 接口中 ,不带 sequences开头的接口,一般都是 使用 单列 特征 定长 的 使用,并且 大多数时候 一列特征都是一个取值。categorical_column_with_hash_bucket
这个接口 比较强大 的一个 功能 就是 他也可以处理 多个取值的 multi hot的类别特征,或则 称为 序列特征。 我们可以使用下面的代码来进行验证:
@ 欢迎关注作者公众号 算法全栈之路hash_word = tf.feature_column.indicator_column( tf.feature_column.categorical_column_with_hash_bucket(key='id', hash_bucket_size=10, dtype=tf.dtypes.string))feature_dict = {"id": [["20","21"],["127","128"] ,["51",'52'], ["3","4"]]}feature_layer = tf.keras.layers.DenseFeatures(hash_word)output = feature_layer(feature_dict)print(output)# 在这里将 indicator_column 换成embedding_column 列将能看到返回的是和 onehot 一样格式embeding .
这里,我们直接使用 indicator_column返回了 categorical_column_with_hash_bucket
处理多取值 list 返回的 multi hot ,长这样:
在这里将 indicator_column
换成 embedding_column
列 将 能看到 返回的 是 和 上面事例代码的单列一个特征的数据一样格式embeding 。
这里明明 id 里输入 了多个取值,也返回了 multihot , 为啥 最后 返回得 embeding 确是 和 onehot 维度一样呢? 原来是因为: embedding_column 默认对多个取值返回的 embeding 进行了combine, 默认的 combine 方式 是 mean.
@ 欢迎关注作者公众号 算法全栈之路import tensorflow as tffeatures = {'sex': tf.sparse.from_dense([["male"],["female"]])}sex_col = tf.feature_column.categorical_column_with_vocabulary_file( key='sex', vocabulary_file='./voc.txt', vocabulary_size=2, num_oov_buckets=5)sex_emb=tf.feature_column.embedding_column(sex_col, 4)columns = [sex_emb]input_layer = tf.keras.layers.DenseFeatures(columns)dense_tensor = input_layer(features)print(dense_tensor)
这里除了使用 hash 的方式 进行 特征取值id化之外,我们也可以使用 categorical_column_with_vocabulary_file
手动的 维护 一个 字典文件,达到 和 上文 最初 介绍的 手动维护 id索引的 古老做法类似 的 功能。在 voc.txt
字典 文件 中,我们 只要 每一行放入一个特征的原始取值即可,这个 接口 会 自动 将 原始特征 的 取值 映射 成 索引ID,非常强大 哦,在 某些场景 下,我们还是 使用 的 非常多 的。
当然,这里的 文件路径 不仅 可以是 单机版本 的 pc路径,也可以是 保存 在 大数据集群上的 hdfs路径哦 。
对于 feature_column众多接口中,以 *_with_vocabulary_file
结尾 的 接口, 均可以 使用 这里 说明 的 类似的 做法 进行操作,其他的接口 我就 不在赘述 了。
@ 欢迎关注作者公众号 算法全栈之路import tensorflow as tf# 定义特征列click_history_feature_col = tf.feature_column.sequence_categorical_column_with_hash_bucket('click_list', hash_bucket_size=100, dtype=tf.int64)click_history_embedding_col = tf.feature_column.embedding_column(click_history_feature_col, dimension=16)columns = [click_history_embedding_col]# 定义特征层 list_layer = tf.keras.experimental.SequenceFeatures(columns)max_len=5# 对于每个特征需要构建一个dictlist_dict = dict()list_dict["click_list"]=tf.keras.Input(shape=(max_len,), dtype=tf.int64,name="click_list")# dict里只有一个元素,然后可以 sequence_input, sequence_length=list_layer(list_dict)sequence_length_mask = tf.sequence_mask(sequence_length)print("sequence_input:",sequence_input.shape)print("sequence_length_mask:",sequence_length_mask)# reduce_mean 的时候,要注意考虑 batch_size 的维度为0,后面第一层括号的维度为1 embeding_mean = tf.reduce_mean(sequence_input,1)print("embeding_mean:",embeding_mean)#接一层全链接层 den = tf.keras.layers.Dense(10, activation="relu", name="dense1")(embeding_mean)model_outputs = tf.keras.layers.Dense(1, activation="sigmoid", name="final_sigmoid")(den)model = tf.keras.Model(inputs=[list_dict["click_list"]],outputs=model_outputs)# model.summary()model.compile(optimizer='adam',loss="binary_crossentropy",metrics=['accuracy'])model.fit(final_dataset, epochs=2)
顾名思义,这个接口 是以 sequence_categorical_column_*
开头的,就是 feature_column 提供的 众多 处理序列特征的 接口中 的一个。 序列特征 表示 特征的取值 是一个 list或则 数组。
上面的代码是一个 feature_column 和 tensorflow keras 结合使用进行 特征处理 和 模型开发 的 完美样例代码。对于这个 事例,我将 keras 的 数据读入 也接 进来了。
中间一个隐藏的深坑是 :使用 tf.keras.Input
和 input_layer
结合 将 特征数据 进行 固定形式 的 处理 的 时候,要求 input_layer
后面 跟着的 keras_input 数据 必须是一个 字典类型。按照上面 我 提供 的 demo 的 同样做法,字典里仅仅放入了一个字段,然后作为参数传递给 特征处理输入层 input_layer, 他大爷的,深坑啊!!!当初 花了老大时间解决 这个问题, 写到 这里 希望确实 可以 帮到 还在 困惑中 的 老哥,觉得有用 就帮忙 关注转发一下 吧~
demo 里 我们 直接接入了 上面所说的 final_dataset 的 dataset ,是一个 相对完整的工程实例。我们 通过 batch 数据来训练 模型,在dataset 的 click_list 列,我输入的是一个python 数组。
这里要 注意到是: click_list 我是padding 之后的,填充得最大长度是 5 , 是 定长的list.所以这里也是5 , 代码里是 tf.keras.Input(shape=(max_len,)
。
中间部分,我们使用了 tf.keras.experimental.SequenceFeatures
来 将 embeding_col 接入网络,取代了以前 tensorflow 1.x 系列 的 tf.feature_column.sequence_input_layer
,和 前面开篇 的 时候说 的 是一个意思。
在某些场景下,我们 也许有 多列的 field 的特征 需要 **共用一个 shared_embeding&& , feature_column接口 下的 shared_embeddings
可以帮助我们实现。
@ 欢迎关注作者公众号 算法全栈之路# tf.enable_eager_execution() # 在tensorflow 2.x 中需要关闭eagerimport tensorflow as tftf.compat.v1.disable_eager_execution()tf.compat.v1.reset_default_graph()# 特征数据features = { 'department': ['sport', 'good', 'drawing', 'gardening', 'travelling'], 'display': ['sport', 'yellow', 'light', 'sex', 'bad'],}# 特征列department_hash = tf.feature_column.categorical_column_with_hash_bucket('department', 10, dtype=tf.string)display_hash=tf.feature_column.categorical_column_with_hash_bucket('display', 10, dtype=tf.string)# print(department_hash)columns = [department_hash,display_hash]share_columns = tf.feature_column.shared_embeddings(columns, dimension=4,shared_embedding_collection_name="share_embeding")# 这里2个 ids 共同构建了一个 share embeding column, 查找的时候,使用公共的variable 查找值。share_input_layer = tf.keras.layers.DenseFeatures(share_columns)dense_tensor = share_input_layer(features)print(dense_tensor)
这里 需要 注意 的 tensorflow 2.x 使用 shared_embeddings
得话,需要 关闭 eager模式,源码里有说明,应该是 底层有冲突 吧。我们 可以使用 tf.compat.v1.disable_eager_execution()
方法 关闭eager 模式。
并且 需要注意 的一点 是:最后返回的 dense_tensor 得维度,在我们的例子中是:Tensor("dense_features/concat:0", shape=(5, 8), dtype=float32)。
对 组合 成 共享embeding 集合的每一个元素,均 返回一个embeding , 因为 department_hash 和 display_hash 在batch size 一致,这里均是5,而 8 则是因为 shared_embeddings 得 每一条 embeding 是 拼接了 2类 得2个 dim =4 的embeding . 用源码里的解释是:
返回 embeding 顺序 和 输入的 categorical_column 时候 顺序 致。
@ 欢迎关注作者公众号 算法全栈之路# 这里要求我们输入特征名称,而不能是categorical_column_with_hash_bucket,官方解释说是会增加冲突。cross_column = tf.feature_column.crossed_column(["department","display"], 100)cross_emb=tf.feature_column.embedding_column(cross_column, 4)# sparsetensor 直接接入 denseFeatures cross_input_layer = tf.keras.layers.DenseFeatures(cross_emb)dense_tensor = cross_input_layer(features)print(dense_tensor)
我们 知道 单列特征 仅仅 从 一个维度 刻画用户,而 交叉特征则是可以 从 交叉的 多列特征中 综合 刻画用户行为 ,例如 刻画情人节 这个日期 和 情趣内衣裤 的 购买记录 之间的 关系,是不是 更能描述 和 反映 某位美女 帅哥 对 某件衣服 的 购买意愿 呢。
在 搜广推算法的 实际 使用场景 中,我们 会 遇到 大量的交叉特征。对于 交叉特征列,我们 可以 输入原始单列特征 得到 embeding 之后,使用 embeding 相乘 或则 对位乘 或则 别的 什么做法 达到 综合 两个特征 建模的 目的,我们 也可以 在 离线使用 spark 进行简单 的 字符串拼接来达到 高维离散 特征 特征交叉的目的。本文这里 介绍 了 tensorflow 提供的 一种新的 解决 方案 。
feature_column 里 提供的 特征交叉接口 crossed_column
,看官方 介绍是 将 特征取值之间做了 笛卡尔积 之后在对 组合好的字符串进行hash操作,将交叉的操作放在了tensorlfow 自己的特征处理过程中,在大数据之后,模型之前。
注意,这个接口 返回的 是一个 交叉特征列类 (_CrossedColumn
), 后面 依然 需要 接 indicator 或则 embeding 层 输入 后面 的 模型。这个 接口 最后 底层 调用的 是 sparse_cross_hashed
这个 方法 做的 交叉操作,感兴趣 的可以 去 前面提供 的 源码地址 去 一层一层 点开 看看 哦。
本文到这里,本文共介绍了 7种 tensorflow feature_column
提供的 常用接口,中间 也穿插 介绍了 很多 特征处理 技巧 和 踩坑 经验,具有 很高的 参考价值 哦。如果 你 还有问题,欢迎 关注作者 的 公众号 留言 一起讨论 哦 ~
到这里,机器学习特征处理详解与 tensorflow feature_column 接口实战的 全文 就 写完 了。本文代码 每个模块 均 可以 独立 跑 成功, 中间序列特征 处理模块 是一个完整的 feature_column结合keras 开发模型的 优秀 式例,希望 可以 对 你 有 参考作用 ~
码字不易,觉得有收获就动动小手转载一下吧,你的支持是我写下去的最大动力 ~
更多更全更新内容,欢迎关注作者的公众号: 算法全栈之路
- END -
X 关闭
Copyright © 2015-2022 人人机械网版权所有 备案号:粤ICP备18023326号-36 联系邮箱:8557298@qq.com