线性回归在建模的时候,只考虑到了单个特征的影响。但是有些场景下,添加组合特征(交叉特征)会给模型带来非常好的效果。 线性回归
POLY2 算法(二阶多项式)在线性回归基础上添加组合特征 \( x_{i}x_{j}\),并为每个组合特征配备了一个学习参数 \( w_{ij}\),如下公式所示: POLY2
公式的第一部分就是简单的线性回归部分,第二部分则是 POLY2 算法增加的组合特征部分。需要注意的是第二部分的 ∑ 角标记分别为 i=1 和 j=i+1,为什么这么写?
这是因为组合特征 \(x_{i}x_{j}\) 和 \(x_{j}x_{i} \) 表示的组合特征含义是一样的,并且我们也不需要引入 \(x_{i}^2\) 这样的组合特征。如下图所示(我们只需要上三角,红色标注的组合特征):
至此,我们就完成了给线性回归增加二阶特征的目标,增强了模型的拟合能力。但上面的模型还存在一个问题,如果我们的数据存在较多的类别特征,一般我们都会通过 one hot 对其进行处理,这就使得数据集维度变大,并且出现大量 0 的特征,使得数据集变得稀疏。
本来 one hot 就使得原本数据集变得稀疏,现在通过 POLY2 产生二阶特征中也会存在很多的 0,导致数据集更加稀疏,我们知道特征过于稀疏就会导致很多的权重无法得到有效的学习,如下图所示: 原始数据集 one hot 编码数据集 增加组合特征
红色为增加的组合特征,我们会发现组合出来的特征出现了大量的 0,导致原本的稀疏的数据变得更加稀疏。并且,二阶组合特征 \( x_{i}x_{j}\) 大量等于 0 时,会导致对参数 \( w_{ij}\) 估计变得非常困难。
- FM 算法 {#title-0} ===================
Paper:https://www.csie.ntu.edu.tw/~b97053/paper/Rendle2010FM.pdf
针对 POLY2 算法的无法应对稀疏数据的缺点,FM 算法进行了改进,其公式表示如下:
FM 和 POLY2 变化的地方就是 \(w_{ij}\) 便成了 <\(w_i, w_j\)>,它表示向量 \(w_i\) 和 \(w_j\)的点积。这个变化简单理解的话,就是原本组合特征的参数是 \(w_{ij}\),现在变成两个向量 \(w_{i}\) 和 \(w_{j}\),要学习的参数更多了。
到这里的话,我就有两个问题:
\(w_{ij}\) 是如何变成 <\(w_i, w_j\)>?
将 \(w_{ij}\) 变为 <\(w_i, w_j\)> 是如何解决稀疏矩阵场景问题?
我们先理解第一个问题。在 POLY2 算法中,\(w_{ij}\) 的矩阵 W 表示如下:
当 k 足够大时,实对称半正定阵可以分解成 \(VV^T\) 的形式,即:
W 矩阵根据上面公式可以分解如下图所示(注意: k 为超参数,假设此处选择 k=3):
例如:参数 \(w_{12}\) 就可以用分解后的矩阵表示如下:
这里用到了两个矩阵,其实我们只用 3✖️4 矩阵就可以表示,如下图所示:
上图中,每一列叫做对应特征的隐向量,我们一共有 4 个特征,所以这里出现了 4 列的隐向量。两列隐向量相乘再相加得到的结果就对应了 FM 公式中的参数 <\(w_i, w_j\)>。至此,我们应该能够知道: \(w_{ij}\) 是通过矩阵分解转换为 <\(w_i, w_j\)> 形式的。
转换成向量点积形式,如何解决稀疏场景的问题?
我是这么理解的:POLY2 形式中 \(w_{ij}\) 是一个独立的特征,如果样本中大量出现该特征,则较为容易学习到其权重参数,但在稀疏场景下,这一点变得不能现实。通过将权重参数分解为两个隐向量形式之后,会发现每个组合特征参数的估计都有其他的特征隐向量的参与,是不是就可以说多个组合特征之间建立起某种关联,虽然由于稀疏导致无法直接估计到当前特征的参数,但是通过其他关联的特征,间接对当前特征的参数进行了估计。
- FFM 算法 {#title-1} ====================
FFM 算法 (Field-aware Factorization Machine) 是对 FM 算法的改进,其公式如下:
相对于 FM 算法的公式,变化如下:
从公式来看,仅仅是在隐向量中增加一个角标 \(f_{j}\) 和 \(f_{i}\),这表示啥意思呢?
特征 A 的隐向量可以理解为当 A 特征与其他特征组合时所表达的信息,如果特征 A 与特征 B 组合、特征 A 与特征 C 组合时,A 表达的信息都是一样的。在很多情况下,A 和不同的特征组合在一起应该允许表达不同的含义。这就像某个词在不同的上下文里含义是可以不同的。但是,在 FM 算法中,每个特征只对应一个隐向量,导致模型表达能力稍微差那么点意思。
如果特征 "国籍=中国" 和 "性别=女" 组合特征,则将其对应位置的隐向量点乘作为参数,如果 "国籍=中国" 和 "年龄" 组合特征,使用的隐向量都是一样的。
FFM 引入了 FIELD 的概念,使得特征对应多个隐向量,这就使得特征组合的表达能力得到很大的提升。如下图所示:
- 如果特征 "国籍=中国" 和 "性别=女" 组合特征,则将 "国籍=中国" 特征对应的隐向量集合中 "性别 FIELD" 的隐向量取出来,和 "性别=男" 特征对应的多个隐向量集合中 "国籍 FIELD" 的隐向量点乘作为参数。
- 如果特征 "国籍=中国" 和 "年龄" 组合特征,则将 "国籍=中国" 特征对应的隐向量集合中 "年龄" 的隐向量取出来,和 "年龄" 特征对应的多个隐向量集合中 "国籍=中国" 的隐向量点乘作为参数。
至此,已经能够大概理解 FFM 算法相对于 FM 算法做的改进之处了。最后,需要回顾一点,FM、FFM 算法都是针对稀疏场景的算法模型。
- FM API 使用 {#title-2} =======================
xlearn:https://xlearn-doc-cn.readthedocs.io/en/latest/index.html
libfm:http://www.libfm.org/
**xLearn 是一款高性能的,易用的,并且可扩展的机器学习算法库,你可以用它来解决大规模机器学习问题,尤其是大规模稀疏数据机器学习问题。**如果你是 liblinear、libfm、libffm 的用户,那么现在 xLearn 将会是你更好的选择,因为 xLearn 几乎囊括了这些系统的全部功能,并且具有更好的性能,易用性,以及可扩展性。
pip install xlearn
示例代码:
import xlearn as xl
from sklearn.datasets import load_breast_cancer
from sklearn.model_selection import train_test_split
from sklearn.metrics import accuracy_score
from sklearn.preprocessing import StandardScaler
if __name__ == '__main__':
# 加载鸢尾花数据集
data = load_breast_cancer()
print('数据量:', len(data.data))
# 鸢尾花数据集分割
x_train, x_test, y_train, y_test = \
train_test_split(data.data, data.target, test_size=0.2, stratify=data.target, random_state=0)
# 初始化 FM 模型
fm_model = xl.FMModel(task='binary', lr=0.02, k=20, epoch=200, opt='sgd', n_jobs=1)
# 模型开始训练
fm_model.fit(x_train, y_train)
# 训练集准确率
# x_train = scaler_model.fit_transform(x_test)
y_pred = fm_model.predict(x_train)
y_pred = [ 1 if pred > 0.5 else 0 for pred in y_pred]
print(accuracy_score(y_train, y_pred))
FMModel 在 scikit-learn 的 breast_cancer 数据集上训练效果不如 LRModel 效果。考虑的原因可能是是 FM 算法对稀疏数据效果更好,而我们的数据集则并不是稀疏的。