one-hot 编码

对于一个分类问题,假设有N种类别,不妨记作 [ "A类", "B类", "C类", "D类", ...],有时候我们可能倾向于直接用数字编号表示,表示成 [ 0, 1, 2, 3, ...]。但是问题是数字编号预设了一个不应该存在的限制:我们在任意两个类别之间强加了比较关系,比如"D类"(类别3) > "B类"(类别2)。一方面,这种类别之间的大小比较毫无意义。另一方面,这种顺序会导致距离测算错误。

假设某个情况是"D类"(类别3) 。在第一种情况下,它被我们错误的分类成了"A类"(类别0) ,这时候二者距离是 3 - 0 =3;在第二种情况下,它被我们错误分类成了"C类"(类别2) ,这时候二者的距离是 3 -2 = 1。这表明,在这种机制下,把"D类"归类成"C类"这种情况,会被当做优于把"D类"归类成"A类"这种情况,这会极大程度上误导机器学习朝着更优的方向进行。

一种解决办法是采用one-hot编码。拿上面的例子来说:

  • A 类被编码成 [1, 0, 0, 0, ...]
  • B 类被编码成 [0, 1, 0, 0, ...]
  • C 类被编码成 [0, 0, 1, 0, ...]
  • D 类被编码成 [0, 0, 0, 1, ...]
  • ...

由于这些向量都是单位正交向量,任意两个向量之间的余弦距离都是一样的,这表明类别归类错误程度("距离")在这种机制下可以被正确计算。

label_binarize() 函数

sklearn中的label_binarize()函数可以完成上面这种标签二值化编码转换。

label_binarize(
    y=[
        "B类",
        "B类",
        "B类",
        "C类",
        "C类",
        "A类",
        "D类",
        "D类"
    ] ,
    classes=[ "A类", "B类", "C类", "D类", ]
)

这里:

  • 参数y表示各个samples的标签;
  • 参数classes表示所有分类构成的集合

上面这个调用的输出为:

array([[0, 1, 0, 0],
       [0, 1, 0, 0],
       [0, 1, 0, 0],
       [0, 0, 1, 0],
       [0, 0, 1, 0],
       [1, 0, 0, 0],
       [0, 0, 0, 1],
       [0, 0, 0, 1]])

当前,标签不必是字符串类型,也可以是数字:

label_binarize([0, 1, 2, 1, 0], classes=[0, 1, 2])

输出为:

array([[1, 0, 0],
       [0, 1, 0],
       [0, 0, 1],
       [0, 1, 0],
       [1, 0, 0]])

二元分类

特别地,对于二元分类,也就是类别数是2的情况,情况会有所不同:

label_binarize(['yes', 'no', 'no', 'yes'], classes=['no', 'yes'])

直觉上,它应该返回的每一行都是两个元素构成的数组,类似于

[
    [0, 1], 
    [1, 0], 
    [1, 0],
    [0, 1]
]

但是实际输出却是:

array([[1],
       [0],
       [0],
       [1]])

这是因为label_binarize()发现这里的类别数是2,用单个0或1两种数字表示了[0,1][1,0]两种情况。这种优化,降低了矩阵的存储大小。

类似的示例:

label_binarize([0, 2, 2, 0, 0], classes=[0, 2])

输出:

array([[0],
       [1],
       [1],
       [0],
       [0]])

LabelBinarizer 类

上面的label_binarize()函数可以视作是LabelBinarizerfit_transform()。这个类提供了更完备的功能

  • fit():针对所有可能标签进行拟合
  • transform(): 把标签转换成one-hot编码
  • fit_transform() :等价于 fit() + transform()
  • inverse_transform() :反向转换
from sklearn.preprocessing import LabelBinarizer

lb = LabelBinarizer()

lb.fit(["paris", "paris", "tokyo", "amsterdam"])
print(lb.classes_)
yy = lb.transform(['paris', 'amsterdam'])
print(yy)
yyy = lb.inverse_transform(yy)
print(yyy)

输出类似于:

['amsterdam' 'paris' 'tokyo']

[[0 1 0]
 [1 0 0]]

['paris' 'amsterdam']

标签: sklearn, one-hot

已有 3 条评论

  1. 文字流畅如丝,语言优美动人,读来令人心旷神怡。

  2. 文章结构紧凑,层次分明,逻辑严密,让人一读即懂。

  3. ?议论文评语?

添加新评论