以广告点击预估问题为例,假设原始数据有语言和类型两种 离散特征。为了提高拟合能力,语言和类型可以组成二阶特征。
上面是在百面机器学习上看到的一个小问题,尝试一下。
这里我采用特征交叉的方法来处理。将语言和类型两个特征的取值进行两两组合,
假设现在样本中语言特征取值为"English",类型特征取值为"video",我将其组成一个新的二阶特征"English_video"。
实现思路:
先将语言和类型特征进行编码,例如使用独热编码或二进制编码将其转换为数值特征
然后对这些编码后的特征进行两两交叉,得到新的二阶特征。
最终得到一个特征向量,其中包含原始特征和二阶特征。
代码尝试:
import numpy as np
# 原始特征
language = ["English", "Chinese", "French"]
type = ["video", "article", "audio"]
# 对特征进行编码
language_encoded = np.eye(len(language))
type_encoded = np.eye(len(type))
# 特征交叉,得到新的二阶特征 将二阶特征存储在一个大小为 (len(language) * len(type), len(language) + len(type)) 的二维矩阵中
feature_cross = np.zeros((len(language) * len(type), len(language) + len(type)))
for i in range(len(language)):
for j in range(len(type)):
feature_cross[i*len(type)+j] = np.concatenate((language_encoded[i], type_encoded[j]))
# 打印特征交叉后的结果
print(feature_cross)
在这里我也使用到了np.concatenate()函数:
这个函数将多个数组沿着指定的轴进行拼接。其中,axis
参数用于指定拼接的轴,默认为 0。当 axis=0
时,表示将多个数组在纵向方向进行拼接,即增加行数;当 axis=1
时,表示将多个数组在横向方向进行拼接,即增加列数。
输出一下结果可以看到:
[[1. 0. 0. 1. 0. 0.]
[1. 0. 0. 0. 1. 0.]
[1. 0. 0. 0. 0. 1.]
[0. 1. 0. 1. 0. 0.]
[0. 1. 0. 0. 1. 0.]
[0. 1. 0. 0. 0. 1.]
[0. 0. 1. 1. 0. 0.]
[0. 0. 1. 0. 1. 0.]
[0. 0. 1. 0. 0. 1.]]
这就得到了一个由原始特征语言和类型组成的二阶特征数组。
但是,这还存在一个问题,如果原始特征分别对应 m个和 n 个,那么二阶特征的数量将会是 m*n,二阶交叉特征的数量会随着原始特征数量的增加而呈指数级增长,导致模型的复杂度和训练难度也会随之增加。
我们需要在这个基础上引入降维操作。比如我们可以使用特征哈希(Feature Hashing),主成分分析(PCA)、奇异值分解(SVD)、独立成分分析(ICA)等。
这里使用特征哈希举例:
特征哈希将原始特征映射到一个固定大小的哈希空间中,通过哈希函数的随机映射将原始特征映射到相应的二阶特征上。
它将原始特征值看作哈希表中的键,然后将其哈希到一个固定大小的桶(bucket)中。哈希函数可以是任意的映射函数,可以是简单的模运算,也可以是更复杂的哈希函数。通过将原始特征哈希到桶中,我们可以将原始特征的维度降低到一个固定的值。
这种方法的优点是:
可以在不显式地计算二阶特征交叉的情况下,实现特征的组合和降维,
从而避免了二阶特征数量过大导致的维度爆炸问题。
缺点是:
存在哈希冲突。不同的原始特征可能被哈希到同一个桶中,导致信息损失。
代码实现:(使用sklearn
中的FeatureHasher
类来实现)
from sklearn.feature_extraction import FeatureHasher
# 原始特征
language = ["English", "Chinese", "French"]
type = ["video", "article", "audio"]
# 创建 FeatureHasher 对象
n_features = 100 # 哈希表大小
hasher = FeatureHasher(n_features=n_features, input_type='string')
# 将原始特征哈希到固定大小的向量中
X = [{"language": lang, "type": t} for lang in language for t in type]
# 使用transform()方法将原始特征映射到哈希表中,得到哈希后的向量。
X_hashed = hasher.transform(X)
# 输出哈希后的向量
print(X_hashed.toarray())
转换成一个普通的二维数组,输出:
[[ 0. -1. -1. ... -1. -1. 1.]
[ 0. -1. 0. ... 0. -1. 1.]
[ 0. -1. 1. ... -1. -1. 1.]
...
[ 0. 1. 0. ... -1. -1. 1.]
[ 0. 1. -1. ... 0. -1. 1.]
[ 0. 1. 1. ... 1. -1. 1.]]
得到一个哈希后的稀疏矩阵,其大小为 (len(language)*len(type), n_features)
每一行对应原始特征中的一个组合,每一列对应哈希表中的一个桶。
如果哈希后的特征值落入了某个桶中,那么该桶所在的列的值就为 1,否则为 0。
哈希函数是随机的,因此在同一个哈希表中,相同的特征值可能被映射到不同的桶中,从而得到不同的哈希特征向量。
以上。