©作者 | 昀昊
单位 | 大连理工大学
研究方向 | 智能风控
背景
最近学习了一个模型 LassoNet初次看到,根据名称猜测是一个 Lasso+Net 组成的神经网络结构。
Lasso 是传统的回归模型基础上添加了一个 L1 范数,也称之为 L1 正则化。Lasso 给简单线性回归加了 L1 正则化,可以将不重要变量的系数收缩到 0 ,从而实现特征选择。Lasso 由于其完备的理论支持和快速的效率,在学术界和工业界十分常用。但由于其表示的是线性模型,模型复杂度有限。
因此,考虑如何将 Lasso 与神经网络结构进行结合。
传统神经网络大多是是“黑箱”模型,输入层与输出层间有许多隐含层,其中包含了成千上万个参数。网络训练过程中会进行特征提取,因此很难表示每个特征与输出结果之间的对应关系。而特征选择,就是要通过表示特征与输出结果之间的对应关系,进而找到每个特征的“贡献程度”。
不论是基于机器学习模型的线性模型 Lasso、树模型(rf、GBDT、XGBoost 等)的分裂增益、线性支持向量机或逻辑回归(LinearSVM、Logit)的系数 coefficient。还是基于统计量的 mRMR、ReliefF 等,要么是得到每一个特征的 feature importance,要么是通过模型训练过程,来评估特征对模型最终表现(metrics)的影响。归根结底是得到了特征->输出之间的对应关系。关系成立,则特征选择就有可能。
假如有一个模型结构,既能保证特征在输入模型训练过程中像 Lasso 一样自动“稀疏”,将系数压缩至 0,又能保留神经网络的特征提取能力,兼顾一定的模型复杂度,使得模型的特征提取能力更强。
1.1 LassoNet模型结构
因此,LassoNet 模型就是满足了以上两个条件而诞生的模型,模型的结构如下
▲ LassoNet模型结构
1.2 LassoNet模型特点
▲ LassoNet模型的跳跃连接
结构决定功能。LassoNet 模型之所以能够将特征“稀疏”,亦或是有特征选择能力,是因为其借鉴了残差网络(ResNet)的跳跃连接机制(Skip Connection)。思路是:该模型在任意一种神经网络前(DNN、CNN 均可)额外“添加”一个输入层,将“添加”的这个输入层直接跨过神经网络,与输出层神经元相连接,形成一个“桥梁”(上图橙色箭头)。通过该结构,可以直接评估输入特征和输出之间的关系。
在网络训练时,对这个“桥梁”,损失函数会施加一个数学上的“惩罚”,让算法在训练时移除对结果没有贡献的神经元节点(图中浅灰色的节点)。将节点移除后,该“特征”也相应地被移除了。进而达到了特征选择的目的。
公式(2)表示 LassoNet 的目标函数和约束条件。目标函数是一个 Loss+L1 正则的表示,与 Lasso 相同。约束条件表示在前馈神经网络上,每一个特征在每一个神经元上的权重参数 W 需小于等于该特征在线性模型上的参数的 M 倍。其中 M 是一个超参。M 越大,模型越倾向加强非线性,成为一个前馈神经网络,M 越小,越加强线性,当 M 位 0 时变为 Lasso。
使用LassoNet进行特征选择
2.1 LassoNet如何使用
LassoNet已开源:
https://github.com/lasso-net/lassonet
LassoNet官网:
https://lassonet.ml/
安装使用 pip 安装即可。
pip
 install lassonet

2.2 使用LassoNet选特征(分类问题)
导入相关包
import
 pandas 
as
 pd

import
 numpy 
as
 np

import
 matplotlib.pyplot 
as
 plt

from
 sklearn.model_selection 
import
 train_test_split

from
 sklearn.ensemble 
import
 RandomForestClassifier,GradientBoostingClassifier

from
 sklearn.linear_model 
import
 LogisticRegression

from
 sklearn.svm 
import
 SVC

from
 sklearn.datasets 
import
 make_classification

from
 sklearn.metrics 
import
 *

from
 lassonet 
import
 LassoNetClassifier

from
 lightgbm 
import
 LGBMClassifier

加载示例数据集
# 加载示例数据集
X,y = make_classification(n_samples=
5000
,n_features=
30
,n_informative=
3
,n_redundant=
27
,random_state=
2022
)


# 划分训练集,验证集,测试集
X_train, X_test, y_train, y_test = train_test_split(X,y,test_size=
0.15
)

X_train,X_valid,y_train,y_valid = train_test_split(X_train,y_train,test_size=
0.2
)


print(X_train.shape,X_valid.shape,X_test.shape)


# 示例特征(指标)名字
feature_names = [
f'fea{i}'for
 i 
in
 range(
1
,X.shape[
1
] + 
1
)]
实例化 LassoNet 模型(预设超参数 M=20,lambda 模型可自动设置)
通过变量选择和参数更新同时进行,模型训练的同时得到整个的解路径(solution path)进而得到不同的特征组合。可以通过不同特征组合在不同 path 上的表现,反推一个目标特征组合。
# 加载模型
model = LassoNetClassifier(M=20, verbose=True,)

path = model.path(X_train, y_train)



n_selected = []

auc = []

lambda_ = []

feature_selected = []


# 模型训练过程
for save in path:

    model.load(save.state_dict)

    y_pred = model.predict(X_valid)

    y_score = model.predict_proba(X_valid)[:,1]

# 选取的特征个数
    n_selected.append(save.selected.sum())

    feature_selected.append(np.array(feature_names)[save.selected.numpy()])

#print(np.array(feature_names)[save.selected.numpy()])
#print(accuracy_score(y_test, y_pred))
    auc.append(roc_auc_score(y_valid, y_score))

    lambda_.append(save.lambda_)


# 汇总表示
df = pd.DataFrame({'feature_selected':feature_selected,'auc':auc})

df['n_feature'] = df['feature_selected'].apply(lambda x:len(x))

df_sort = df.query(
"n_feature != 30"
).sort_values(by=['auc','n_feature'],ascending=[False,True])

df_sort

▲ 不同的解路径(solution path)对应的特征数量与 AUC 表现
得到选择出的特征
# 选择的特征为
feature_selected = df_sort.head(1)['feature_selected'].values[0]


# 特征选择后的数据
X_train_s = pd.DataFrame(X_train,columns=feature_names)[feature_selected]

X_test_s = pd.DataFrame(X_test,columns=feature_names)[feature_selected]


print(feature_selected.__len__())


# result
28

2.3 模型效果测试
使用 Random Forest、SVM、GBDT、LightGBM 来测试。
RF
# 模型效果测试
rf_clf = RandomForestClassifier()

rf_clf.fit(X_train,y_train)

y_pred = rf_clf.predict(X_test)

confusion_matrix(y_test,y_pred)


# result
array([[329,  22],

       [ 18, 381]])


rf_clf = RandomForestClassifier()

rf_clf.fit(X_train_s,y_train)

y_pred = rf_clf.predict(X_test_s)

confusion_matrix(y_test,y_pred)


# result
array([[327,  24],

       [ 17, 382]])

SVM
svm_clf = SVC()

svm_clf.fit(X_train,y_train)

y_pred = svm_clf.predict(X_test)

confusion_matrix(y_test,y_pred)


# result
array([[326,  25],

       [ 17, 382]])


svm_clf = SVC()

svm_clf.fit(X_train_s,y_train)

y_pred = svm_clf.predict(X_test_s)

confusion_matrix(y_test,y_pred)

# result
array([[327,  24],

       [ 17, 382]])

GBDT
gbdt = GradientBoostingClassifier()

gbdt.fit(X_train,y_train)

y_pred = gbdt.predict(X_test)

confusion_matrix(y_test,y_pred)


# result
array([[331,  20],

       [ 14, 385]])


gbdt = GradientBoostingClassifier()

gbdt.fit(X_train_s,y_train)

y_pred = gbdt.predict(X_test_s)

confusion_matrix(y_test,y_pred)


# result
array([[332,  19],

       [ 15, 384]])

LightGBM
lgb = LGBMClassifier()

lgb.fit(X_train,y_train)

y_pred = lgb.predict(X_test)

confusion_matrix(y_test,y_pred)


# result
array([[330,  21],

       [ 18, 381]])


lgb = LGBMClassifier()

lgb.fit(X_train_s,y_train)

y_pred = lgb.predict(X_test_s)

confusion_matrix(y_test,y_pred)


# result
array([[329,  22],

       [ 17, 382]])

结论
3.1 主要结论
通过 LassoNet 选特征,具有较强的稳定性。在示例数据中 30 个特征选择了 28 个特征,且模型表现没有受到影响,部分模型效果甚至有提升。
3.2 展望
实际生产或者研究中,可根据实际业务需求来保留需要的特征数。
例如实际生产数据集的特征数量过大,可以通过特征 filter+LassoNet 的方法实现更大的降维效果。
或者在可接受范围内采取牺牲少许精度的情况下,达到 Number of feature 和 performance 的权衡。
最后 M 参数可以通过交叉验证来寻优,进而得到最佳的模型表现。
参考文献
[1] 机器学习基础:用 Lasso 做特征选择:
https://blog.csdn.net/jpld/article/details/125591292?utm_medium=distribute.pc_relevant.none-task-blog-2~default~baidujs_utm_term~default-0-125591292-blog-124765520.pc_relevant_3mothn_strategy_and_data_recovery&spm=1001.2101.3001.4242.1&utm_relevant_index=3
[2] Hansen:LassoNet:说得清的神经网络:
https://zhuanlan.zhihu.com/p/584717736
[3] 益者三友:LassoNet: 新老手艺的结合:
https://zhuanlan.zhihu.com/p/339540893
[4] https://github.com/lasso-net/lassonet/blob/master/examples/boston_housing_group.py:
[5] LassoNet: A Neural Network with Feature Sparsity:
https://jmlr.org/papers/v22/20-848.html
更多阅读
#投 稿 通 道#
 让你的文字被更多人看到 
如何才能让更多的优质内容以更短路径到达读者群体,缩短读者寻找优质内容的成本呢?答案就是:你不认识的人。
总有一些你不认识的人,知道你想知道的东西。PaperWeekly 或许可以成为一座桥梁,促使不同背景、不同方向的学者和学术灵感相互碰撞,迸发出更多的可能性。 
PaperWeekly 鼓励高校实验室或个人,在我们的平台上分享各类优质内容,可以是最新论文解读,也可以是学术热点剖析科研心得竞赛经验讲解等。我们的目的只有一个,让知识真正流动起来。
📝 稿件基本要求:
• 文章确系个人原创作品,未曾在公开渠道发表,如为其他平台已发表或待发表的文章,请明确标注 
• 稿件建议以 markdown 格式撰写,文中配图以附件形式发送,要求图片清晰,无版权问题
• PaperWeekly 尊重原作者署名权,并将为每篇被采纳的原创首发稿件,提供业内具有竞争力稿酬,具体依据文章阅读量和文章质量阶梯制结算
📬 投稿通道:
• 投稿邮箱:[email protected] 
• 来稿请备注即时联系方式(微信),以便我们在稿件选用的第一时间联系作者
• 您也可以直接添加小编微信(pwbot02)快速投稿,备注:姓名-投稿
△长按添加PaperWeekly小编
🔍
现在,在「知乎」也能找到我们了
进入知乎首页搜索「PaperWeekly」
点击「关注」订阅我们的专栏吧
·
·
继续阅读
阅读原文