本文讨论的是特征工程中常用的数据预处理技术之一,即一热编码及其在TensorFlow中的应用。
一热编码是处理机器学习模型时经常使用的术语,特别是在数据预处理阶段。它是用于准备分类数据的方法之一。
目录
- 分类变量
- 一热编码
- 在TensorFlow模型中实现单热编码(tf.one_hot)。
分类变量
分类变量或名义变量可以有两个或更多的类别,但与顺序变量不同的是,没有对类别进行排序或排名,或者说,我们不能以已知或明确的方式对它们进行排序。
举例来说
- 一个球的颜色可以分为 "红"、"绿"、"蓝 "或 "黄 "等类别。
- 狗的品种可以分为 "德国牧羊犬"、"小猎犬"、"
西伯利亚雪橇 "或 "柴犬
"等类别。
一热编码
它是什么?
一热编码将分类变量替换为二进制变量(或者更具体地说,是向量),其值为0或1。基本上,我们要表明某个特定类别的某个项目是否存在。这是通过为数据中的每个条目创建新的行和代表其类别的列来实现的。它们的存在或不存在都用1和0来标记。
让我们考虑一个例子:
在这里,我们的数据中有5个条目,所以我们有5个二进制变量和3列类别标签。如前所述,这种编码并不假定类别的任何排序。因此,如果在给定的分类数据中没有明确的排序,那么这种编码就能很好地发挥作用。
为什么需要单热编码?
我们不能将这些变量直接插入我们的模型中,因为大多数机器学习模型要求所有的变量,包括输入和输出都是数字形式的。
但令人惊讶的是,决策树和它的衍生物,包括回归树和随机森林这样的树群,对未处理的分类变量是稳健的。我们可以从它的观点来理解这一点,即树中的每个节点在其变量可以取的每个值上都有一个子节点,因此把所有变量都当作分类变量。
在进一步讨论之前,我们可能会有一个问题,为什么标签编码或序数编码不是简单的足够?
顺序编码的问题是,它假定数值越高,类别越好。假设我们的类别中有另一种颜色,它将被赋予一个更高的值,即4。因此,分类值会随着独特条目的数量按比例增加。因此,较高的数字被赋予较高的权重(重要性)。
例如,如果 "红色 "是1,"黄色 "是2,"绿色 "是3,我们的模型计算整个类别的平均值,它将做为1+3/2=2,模型将推断它是 "红色 "和 "绿色 "的平均值是 "黄色"。这最终会导致我们的模型挑出完全错误的共同关系或模式。
有一点需要注意的是,如果分类变量占了大量的值,比如说15个不同的值,那么一热编码的表现就不理想。
在TensorFlow模型中实现One-Hot编码(tf.one_hot)
tf.one_hot
操作
我们首先要创建一个神经网络层,它将使用这个tf.one_hot
操作,以便将一热编码作为数据预处理与实际的训练模型包括在内
tf.one_hot(
indices, depth, on_value=None, off_value=None, axis=None, dtype=None, name=None
)
这些是可以传递的参数:
指数:一个索引的输入张量。
深度:一个标量,定义一热维度的深度(类别数量)。
on_value:一个标量,定义当indices[j]=i时,要在输出中填充的值。(默认:1)
off_value:一个标量,定义当index[j] !=i时要在输出中填充的值。(默认:0)
axis:要填充的轴(默认:-1,一个新的最里面的轴)。
dtype:输出张量的数据类型。
name: 操作的可选名称
tf.one_hot
接受一个类别索引和深度的列表,这基本上是唯一类别的数量,也是结果张量中的列的数量。
作为一个例子,我们在输入指数的情况下尝试tf.one_hot
操作
import numpy as np
import tensorflow as tf
indices = [0,1,1,2,3,0,3]
cat_count = 4 #depth or the number of categories
input = tf.one_hot(indices, cat_count) #apply one-hot encoding
print(input.numpy())
>>>
[[1. 0. 0. 0.] <- binary variable 0
[0. 1. 0. 0.] <- binary variable 1
[0. 1. 0. 0.] <- binary variable 2
[0. 0. 1. 0.] <- binary variable 3
[0. 0. 0. 1.] <- binary variable 4
[1. 0. 0. 0.] <- binary variable 5
[0. 0. 0. 1.]] <- binary variable 6
这个操作的输出是一个二进制张量的列表,其中有一热编码的值。
正如我们所看到的,我们不能直接将输入作为一个类别的字符串,因为它只接受指数,这是很不方便的。所以,为此我们必须创建一个自定义层,以一致的方式将字符串转换成整数索引,即一个特定的类别应该总是得到相同的索引。
我们将使用文本矢量化的技术来做这种转换。我们必须在keras
的层模块的帮助下引入一个新的TextVectorization
层,该层将对文本序列进行标记。output_sequence_length=1
的设置是因为我们要为我们传递的每个类别分配一个整数索引。使用adapt()
方法,我们将在数据集上进行拟合。
import pandas as pd
from tensorflow.keras import layers
df = pd.DataFrame(data=[['red'],['green'],['green'],['blue'],['yellow'],['red'],['yellow']])
text_vectorization = layers.experimental.preprocessing.TextVectorization(output_sequence_length=1)
text_vectorization.adapt(df.values)
#print the index of color 'Red'
text_vectorization.call([['red']])
拟合完成后,会创建一个内部词汇表,存储在单词和索引之间创建的映射。你可以使用get_vocabulary()
方法查看这个词汇表。
print(text_vectorization.get_vocabulary())
这就是我们如何手动使用文本矢量化方法将类别字符串转换为整数索引。
此外,作为奖励,还可以创建一个One Hot Encoder Layer类,只要有需要,就可以用来即时创建One Hot Encoding layer的实例。
它接受一个分类输入,然后One Hot会对其进行编码。上述类的例子还提供了一个选项,可以将配置导出为JSON文件,因此它可以部署或重新加载到内存中。
通过OpenGenus的这篇文章,你一定对TensorFlow(tf.one_hot)中的One Hot编码有了深刻的认识。