在回归,分类,聚类等机器学习算法中,各个特征之间的距离(相似度)计算是非常重要的,然而常用的距离计算都是在欧式空间内计算,例如计算余弦相似性。但是在欧式空间内计算相似性要求数据是连续的,有序的。在很多机器学习的任务中,数据都是离散的,例如星期一,星期二,···,星期天,人的性别有男女,祖国有中国,美国,法国等。这些特征值并不是连续的,而是离散的,无序的。
如果要作为机器学习算法的输入,通常我们需要对其进行特征数字化。什么是特征数字化呢?例如:
性别特征:["男","女"]
祖国特征:["中国","美国,"法国"]
运动特征:["足球","篮球","羽毛球","乒乓球"]
怎么将上诉特征数字化呢?有个人他的特征是 [“男”,“中国”,“乒乓球”],怎么表示他呢?
独热编码即 One-Hot Encoding,又称一位有效编码,其方法是使用N位状态寄存器来对N个状态进行编码,每个状态都由他独立的寄存器位,并且在任意时候,其中只有一位有效。one-hot向量将类别变量转换为机器学习算法易于利用的一种形式的过程,这个向量的表示为一项属性的特征向量,也就是同一时间只有一个激活点(不为0),这个向量只有一个特征是不为0的,其他都是0,特别稀疏。
例1:
我们有四个样本,每个样本有三个特征,如图:
特征1 | 特征2 | 特征3 | |
---|---|---|---|
样本1 | 1 | 4 | 3 |
样本2 | 2 | 3 | 2 |
样本3 | 1 | 2 | 2 |
样本4 | 2 | 1 | 1 |
上诉样本特征1有两种可能的取值,若代表性别,比如1代表男性2代表女性,特征2有4种,可以代表另一种特征,同样的特征3也可以有他的含义。
独热编码保证每个样本中的单个特征只有1位数字为1,其余全部为0,编码后表示为:
特征1 | 特征2 | 特征3 | |
---|---|---|---|
样本1 | 01 | 1000 | 100 |
样本2 | 10 | 0100 | 010 |
样本3 | 01 | 0010 | 010 |
样本4 | 10 | 0001 | 001 |
对每个特征都使用独热编码表示,特征有2种取值就用两位表示,4种取值就用4位表示
对于前言中的例子,可以将特征与具体的特征对应:
import numpy as np
samples = ['I like playing basketball', 'I played football yesterday morning']
token_index = {}
for sample in samples:
for word in sample.split():
if word not in token_index:
token_index[word] = len(token_index)+1
max_length = 10
results = np.zeros(shape=(len(samples),
max_length,
max(token_index.values())+1))
for i, sample in enumerate(samples):
for j, word in list(enumerate(sample.split()))[:max_length]:
print(j)
index = token_index.get(word)
results[i, j, index] = 1
print(results)
通过sklearn的OneHotEncoder()来得到独热编码,但是只适用于数值型的数据。OneHotEncoder()的 feature_indices_ 可以知道哪几列对应哪个原来的特征。
使用 numpy.hstack() 将多次结果拼接起来得到变换后的结果
问题:不能直接编码字符串类型数据(LabelEncoder() + OneHotEncoder() 可实现,但需数据格式转换)
from sklearn import preprocessing
enc = OneHotEncoder()
enc.fit([[0, 0, 3], [1, 1, 0], [0, 2, 1],[1, 0, 2]])
print("enc.n_values_ is:",enc.n_values_)
print("enc.feature_indices_ is:",enc.feature_indices_)
print(enc.transform([[0, 1, 1]]).toarray())
print(enc.transform([[1, 1, 1]]).toarray())
print(enc.transform([[1, 2, 1]]).toarray())
输出的结果:
enc.n_values_ is: [2 3 4]
enc.feature_indices_ is: [0 2 5 9] #特征坐标
[[1. 0. 0. 1. 0. 0. 1. 0. 0.]]
[[0. 1. 0. 1. 0. 0. 1. 0. 0.]]
enc.n_values_ is :每个特征值的特征数目,第一个特征数目是2,第二个特征数目是3,第三个特征数目是4。
enc.feature_indices_ is :表明每个特征在one-hot向量中的坐标范围,0-2 是第一个特征,2-5就是第二个特征,5-9是第三个特征。
后面三个就是把特征值转换为 one-hot编码,我们可以对比结果看看one-hot差别。
from keras.preprocessing.text import Tokenizer
samples = ['I like playing basketball', 'I played football yesterday morning']
tokenizer = Tokenizer(num_words=1000)
tokenizer.fit_on_texts(samples)
sequences = tokenizer.texts_to_sequences(samples)
one_hot_results = tokenizer.text_to_matrix(samples, mode="binary")
word_index = tokenizer.word_index
print('Found %s unique tokens.' % len(word_index))
官方文档:
tf.one_hot(
indices,
depth,
on_value=None,
off_value=None,
axis=None,
dtype=None,
name=None
)
Returns a one-hot tensor(返回一个one_hot张量).
The locations represented by indices in indices take value on_value, while all other locations take value off_value.
(由indices指定的位置将被on_value填充, 其他位置被off_value填充).
on_value and off_value must have matching data types. If dtype is also provided, they must be the same data type as specified by dtype.
(on_value和off_value必须具有相同的数据类型).
If on_value is not provided, it will default to the value 1 with type dtype.
If off_value is not provided, it will default to the value 0 with type dtype.
If the input indices is rank N, the output will have rank N+1. The new axis is created at dimension axis (default: the new axis is appended at the end).
(如果indices是N维张量,那么函数输出将是N+1维张量,默认在最后一维添加新的维度).
If indices is a scalar the output shape will be a vector of length depth.
(如果indices是一个标量, 函数输出将是一个长度为depth的向量)
If indices is a vector of length features, the output shape will be:
features x depth if axis == -1.
(如果indices是一个长度为features的向量,则默认输出一个features*depth形状的张量)
depth x features if axis == 0.
(如果indices是一个长度为features的向量,axis=0,则输出一个depth*features形状的张量)
If indices is a matrix (batch) with shape [batch, features], the output shape will be:
batch x features x depth if axis == -1
(如果indices是一个形状为[batch, features]的矩阵,axis=-1(默认),则输出一个batch * features * depth形状的张量)
batch x depth x features if axis == 1
(如果indices是一个形状为[batch, features]的矩阵,axis=1,则输出一个batch * depth * features形状的张量)
depth x batch x features if axis == 0
(如果indices是一个形状为[batch, features]的矩阵,axis=0,则输出一个depth * batch * features形状的张量)
实现:
indices = [0, 1, 2] #输入数据(是个向量)需要编码的索引是[0,1,2]
depth = 3
tf.one_hot(indices, depth) # output: [3 x 3]
# [[1., 0., 0.],
# [0., 1., 0.],
# [0., 0., 1.]]
indices = [0, 2, -1, 1] #输入数据(是个向量)的需要编码的索引是[0,2,-1,1]
depth = 3
tf.one_hot(indices, depth,
on_value=5.0, off_value=0.0,
axis=-1) # output: [4 x 3]
# [[5.0, 0.0, 0.0], # one_hot(0) 对位置0处的数据进行one_hot编码
# [0.0, 0.0, 5.0], # one_hot(2) 对位置2处的数据进行one_hot编码
# [0.0, 0.0, 0.0], # one_hot(-1) 对位置-1处的数据进行one_hot编码
# [0.0, 5.0, 0.0]] # one_hot(1) 对位置1处的数据进行one_hot编码
indices = [[0, 2], [1, -1]] #输入数据是个矩阵
depth = 3
tf.one_hot(indices, depth,
on_value=1.0, off_value=0.0,
axis=-1) # output: [2 x 2 x 3]
# [[[1.0, 0.0, 0.0], # one_hot(0) 对位置(0,0)处的数据进行one_hot编码
# [0.0, 0.0, 1.0]], # one_hot(2) 对位置(0,2)处的数据进行one_hot编码
# [[0.0, 1.0, 0.0], # one_hot(1) 对位置(1,1)处的数据进行one_hot编码
# [0.0, 0.0, 0.0]]] # one_hot(-1) 对位置(1,-1)处的数据进行one_hot编码
独热表示以往在NLP中很流行,但是随着TF-IDF以及词向量的出现,已经渐渐变得不再适用了,主要的缺点: