特征工程#

本页面中的代码仅供参考,部署模式存在安全问题,请 不要 作为生产代码使用。

推荐使用 jupyter 运行本教程。

Secretflow 提供了多种工具来分析数据集,以提高机器学习结果的质量。

初始化#

初始化Secretflow,并创建alice/bob两个计算参与方兼数据提供方。

使用前请先了解Secretflow的数据集设定 DataFrame

[13]:
import secretflow as sf

# In case you have a running secretflow runtime already.
sf.shutdown()

sf.init(['alice', 'bob'], address='local')
alice = sf.PYU('alice')
bob = sf.PYU('bob')

spu = sf.SPU(sf.utils.testing.cluster_def(['alice', 'bob']))

准备Demo数据#

使用一组线性拟合数据作为示例。

[14]:
from secretflow.utils.simulation.datasets import load_linear

vdf = load_linear(parts={alice: (1, 4), bob: (18, 22)})

label_data = vdf["y"]
vdf = vdf.drop(columns="y")

print(f"orig ds in alice:\n {sf.reveal(vdf.partitions[alice].data)}")
print(f"orig ds in bob:\n {sf.reveal(vdf.partitions[bob].data)}")
orig ds in alice:
             x1        x2        x3
0    -0.514226  0.730010 -0.730391
1    -0.725537  0.482244 -0.823223
2     0.608353 -0.071102 -0.775098
3    -0.686642  0.160470  0.914477
4    -0.198111  0.212909  0.950474
...        ...       ...       ...
9995 -0.367246 -0.296454  0.558596
9996  0.010913  0.629268 -0.384093
9997 -0.238097  0.904069 -0.344859
9998  0.453686 -0.375173  0.899238
9999 -0.776015 -0.772112  0.012110

[10000 rows x 3 columns]
orig ds in bob:
            x18       x19       x20
0     0.810261  0.048303  0.937679
1     0.312728  0.526637  0.589773
2     0.039087 -0.753417  0.516735
3    -0.855979  0.250944  0.979465
4    -0.238805  0.243109 -0.121446
...        ...       ...       ...
9995 -0.847253  0.069960  0.786748
9996 -0.502486 -0.076290 -0.604832
9997 -0.424209  0.434947  0.998955
9998  0.914291 -0.473056  0.616257
9999 -0.602927 -0.021368  0.885519

[10000 rows x 3 columns]

皮尔逊积矩相关系数#

皮尔逊积矩相关系数可以用来探查两个变量X/Y之间的线性相关性的强度。

两个变量之间的皮尔逊积矩相关系数定义为两个变量的协方差除以其标准差的乘积:

\[\rho_{X,Y}=\frac{cov(X, Y)}{\sigma_X \sigma_Y}=\frac{(X-\mu_X)(Y-\mu_Y)}{\sigma_X \sigma_Y}\]
\[\mu_X= \mathbb{E}(X), \sigma_X^2=\mathbb{E}[(X-\mathbb{E}(X))^2]=\mathbb{E}(X^2)-\mathbb{E}^2(X)\]

数据集中样本(特征)的皮尔逊积矩相关系数,通常用小写字母 r 表示:

\[r=\frac{\sum^n_{i=1}(X_i-\bar{X})(Y_i-\bar{Y})}{\sqrt{\sum^n_{i=1}(X_i-\bar{X})^2} \sqrt{\sum^n_{i=1}(Y_i-\bar{Y})^2}}\]

其值介于-1与1之间。 \(r>0\) 对应两者正相关,反之为负相关; \(|r|\) 越大,相关程度越大。

SSVertPearsonR#

SecretFlow 的 SSVertPearsonR 模块可以用于探查垂直划分数据集的皮尔逊积矩相关系数,计算过程使用秘密分享协议保护。

SSVertPearsonR会首先标准化数据集,这样一来所有特征的均值为0方差为1,可以将计算简化为:

\[r=\frac{1}{n-1}X^TX\]
[15]:
from secretflow.stats import SSVertPearsonR

v_pearsonr = SSVertPearsonR(spu)
corr = v_pearsonr.pearsonr(vdf)
print(f"corr: \n {corr}")
corr:
 [[ 1.0001000e+00  2.4262429e-03 -5.3907027e-03 -7.7360502e-04
  -7.4419454e-03 -1.0678579e-02]
 [ 2.4262429e-03  1.0001000e+00 -5.4155029e-03 -2.0672032e-03
   4.3277428e-03  3.7399144e-03]
 [-5.3907027e-03 -5.4155029e-03  1.0001000e+00  6.2504560e-03
  -3.7937090e-03  7.3285382e-03]
 [-7.7360502e-04 -2.0672032e-03  6.2504560e-03  1.0001000e+00
  -1.0416691e-02  1.0044558e-02]
 [-7.4419454e-03  4.3277428e-03 -3.7937090e-03 -1.0416691e-02
   1.0001000e+00 -1.3045982e-02]
 [-1.0678579e-02  3.7399144e-03  7.3285382e-03  1.0044558e-02
  -1.3045982e-02  1.0001000e+00]]

方差扩大因子#

方差扩大因子 (VIF) 用于探查变量之间的多重共线性。在一个线性统计模型中,一个系数的方差扩大因子等于多元模型中该系数的方差与只有一个变量的模型中该系数的方差的商,简单来说,就是解释变量(特征)之间存在多重共线性时的方差与不存在多重共线性时的方差之比。

SSVertVIF#

SecretFlow 的 SSVertVIF 模块可以用于探查垂直划分数据集的方差扩大因子,计算过程使用秘密分享协议保护。

第j个特征的vif值为:

\[VIF_j = (X^TX)^{-1}_{jj}(n-1)var(X_j)\]

注意:

在秘密分享协议下计算矩阵逆开销非常大,所以这里使用牛顿迭代进行近似。

当输入数据集中存在完全线性相关时,XTX矩阵不满秩,XTX矩阵逆的解析解不存在。

statsmodels在完全线性相关列上计算的VIF为INF,意为无限大的相关性。而我们提供的模块会输出一个很大的值(>>1000),也能正确的表示这些列之间存在很强的相关性。

对于常量列,statsmodels的结果是NAN,我们的模块依然是很大的值(>>1000),表明这个列在建模前需要被剔除。

所以本模块的结果虽然无法和statemodels这类明文计算的结果完全一致,但依然能正确的反映出特征间的相关性。

[16]:
from secretflow.stats import SSVertVIF

v_vif = SSVertVIF(spu)
vif = v_vif.vif(vdf)
print(f"vif: \n{vif}")
vif:
[0.9999169 0.9997679 0.9999169 0.9999169 1.0000659 1.0002149]

线性模型/广义线性模型系数显著检验#

线性/逻辑回归变量显著性检验用于判断特征(解释变量)是否显著,即自变量是否能有效预测因变量的变化,从而判断对应的解释变量是否应被包含在模型中。

线性回归系数显著检验#

对线性回归 :math:`y=Xω`(X包含常数项),使用t检验来对回归项系数检验其值是否为零。

其中:

\[\hat{ω}=(X^T X)^{-1} X^T y=ω+(X^T X)^{-1} X^T ε\]
\[E(\hat{ω})=ω\]
\[Var(\hat{ω} )=σ^2 (X^T X)^{-1}\]

在最小二乘法5条假设都成立的情况下,统计量:

\[ \begin{align}\begin{aligned}t_j=\frac{\hat{ω}_j- ω_j}{s.e.(ω_j )}=\frac{\hat{ω}_j - ω_j}{\sqrt{\hat{σ}^2 (X^T X)_{jj}^{-1}}} \sim t_{n-k}\\其中,n为样本量,k为特征数\end{aligned}\end{align} \]

检验的原假设和备择假设为:

\[\begin{split} \begin{aligned} & H_0:ω_j=0 (j=1,2,⋯,k) \\ & H_1:ω_j≠0 \end{aligned}\end{split}\]

将检验的原假设带入 \(t_j\)

\[t_j=\frac{\hat{ω}_j}{s.e.(ω_j )}=\frac{\hat{ω}_j}{\sqrt{\hat{σ}^2 (X^T X)_{jj}^{-1}}} \sim t_{n-k}\]

逻辑回归系数显著检验#

对于逻辑回归

\[\begin{split} P(y=1|x)=π(x)=1/(1+e^{-ωx} ) \\ P(y=0|x)=1-π(x)=1/(1+e^{ωx} )\end{split}\]

检验的原假设和备择假设为:

\[\begin{split} \begin{aligned} & H_0:ω_j=0 (j=1,2,⋯,k) \\ & H_1:ω_j≠0 \end{aligned}\end{split}\]

Wald test \(Wald=(\hat{ω}_k/SE(\hat{ω}_k ) )^2\) 符合自由度为1的卡方分布。

其中 \(SE(\hat{ω}_k )\)\(ω_k\) 的标准误差, 为方差协方差矩阵的对角元素的平方根:

\[SE(\hat{ω}_k )={Cov(\hat{ω}_{kk})}^{1/2}\]

模型参数的方差和协方差矩阵,为对数似然函数的Hessian矩阵的逆在 \(\hat{ω}\) 处的值:

\[Cov(\hat{ω}) =H^{-1}=\frac{∂^2 l(ω)}{∂ω^2}|_{\hat{ω}}\]

其中:

\[H_{kr}=\frac{∂^2l(ω)}{∂ω_k ∂ω_r}=∑_{i=1}^mx_{ik}π(x_i)[π(x_i )-1]x_{ir}\]

Hessian矩阵表示为 \(H=X^T A X\), A矩阵为:

\[\begin{split} \begin{aligned} & A_{ii} = π(x_{i})[π(x_{i}) - 1] \\ & A_{ij} = 0 , i≠j \end{aligned}\end{split}\]

可得:

\[\begin{split} \begin{aligned} Wald & = (\hat{ω}_k/SE(\hat{ω}_k ) )^2 \\ & = \hat{ω}_k^2 /Cov(\hat{ω}_{kk}) \\ & = \hat{ω}_k^2 / H^{-1}_{kk} \\ & = \hat{ω}_k^2 / (X^T \Lambda X )^{-1}_{kk} \end{aligned}\end{split}\]

SSPValue#

SecretFlow 的 SSPValue 模块可以用于探查模型的P-Value,计算过程使用秘密分享协议保护。

线性回归模型:

  • 计算预测残差 \(\hat{ε}=Xω - y\)

  • 计算 \(\hat{σ}^2=\hat{ε}^T\hat{ε} /(n-k)\)

  • 通过牛顿迭代计算 \((X^T X)^{-1}\)

  • \(t^2= ω^2 / \hat{σ}^2(X^T X)_{jj}^{-1}\)

  • \(p =2 * (1 - t_{n-k}.cdf(|t|))\)

逻辑回归模型:

  • 计算 \(H=X^TAX\)

  • 通过牛顿迭代计算 \(H^{-1}\)

  • 计算 \(z^2=ω^2/H^{-1}_{kk}\)

  • \(p = 2 * (1 - norm.cdf(|z|))\)

[19]:
from secretflow.stats import SSPValue
from secretflow.ml.linear.ss_sgd import SSRegression

# first, get a LR model
model = SSRegression(spu)
model.fit(
    vdf,
    label_data,
    3,      # epochs
    0.3,    # Learning rate
    64,     # batch_size
    't1',   # sig_type
    'logistic',  # reg_type
    'l2',   # penalty
    0.5,    # l2_norm
)
spu_model = model.save_model()

sspv = SSPValue(spu)
pvalues = sspv.pvalues(vdf, label_data, spu_model)
print(f"logistic pvalue: \n{pvalues}")
logistic pvalue:
[9.46532264e-08 0.00000000e+00 0.00000000e+00 0.00000000e+00
 8.21281902e-07 0.00000000e+00 0.00000000e+00]