L1正则化在线性回归中失效问题排查求助
你代码里存在几个关键问题,导致L1正则化没有把冗余特征x4的权重压缩到接近0,逐一说明如下:
1. 数据归一化逻辑错误
在normalize_the_data函数中,将归一化后的数据写入DataFrame时,错误地取了张量的第一个元素:
normalized_df[column] = Xn.numpy()[0]
Xn是形状为(total_data, 1)的张量,Xn.numpy()[0]仅取了第一行数据,导致整个特征列的所有样本值都相同。这种情况下,模型无法从特征中学习有效信息,正则化自然无法发挥作用。
修改方式:将数据展平后赋值:
normalized_df[column] = Xn.numpy().flatten()
2. 测试数据归一化错误
normalize_the_test_data函数中,对测试数据调用了norm_l.adapt(training_df[column]),这会重新计算测试数据的均值和方差,覆盖训练集的归一化参数。正确的做法应该是直接使用训练阶段学到的归一化参数转换测试数据,不需要重新适配。
修改方式:移除norm_l.adapt调用,直接转换:
def normalize_the_test_data(training_df, norm_list): normalized_df = pd.DataFrame() for column in training_df.columns: print(f"Max, Min of {column} pre normalization: {np.max(training_df[column]):0.2f}, {np.min(training_df[column]):0.2f}") norm_l = norm_list[column] Xn = norm_l(training_df[column]) # 直接用训练好的归一化层转换 normalized_df[column] = Xn.numpy().flatten() print(f"Max, Min of {column} post normalization: {np.max(normalized_df[column]):0.2f}, {np.min(normalized_df[column]):0.2f}") return normalized_df
3. 正则化强度不足
Keras中使用kernel_regularizer='l1'时,默认的正则化系数(λ)为0.01,这个强度对于稀疏化冗余特征来说可能不够。需要调大正则化系数,才能让L1正则化的权重惩罚效果更明显。
修改方式:显式指定更大的正则化系数:
from tensorflow.keras.regularizers import L1 model.add(tf.keras.layers.Dense(units=1, input_shape=(training_df.shape[1] - 1,), kernel_regularizer=L1(0.1)))
可以根据实际情况调整λ值(比如0.05、0.1、0.2等),直到x4的权重趋近于0。
4. 训练轮数不足
L1正则化需要足够的迭代次数才能让冗余特征的权重逐渐稀疏化。你当前只训练了20个epochs,可能还没达到权重收敛到0的阶段。
修改方式:增加训练轮数,比如设置epochs=100甚至更多,同时可以加入早停机制防止过拟合:
early_stop = tf.keras.callbacks.EarlyStopping(monitor='val_loss', patience=5) history = model.fit(..., epochs=100, callbacks=[weight_callback, early_stop], ...)
5. 权重回调记录不完整
你的权重回调函数只记录了第一个特征的权重(model.get_weights()[0][0][0]),没有记录x4对应的权重(即model.get_weights()[0][3][0]),导致你无法观察到x4权重的变化情况,误以为正则化没生效。
修改方式:修改回调函数,记录所有特征的权重:
weight_callback = tf.keras.callbacks.LambdaCallback( on_epoch_end=lambda epoch, logs: weights_dict.update({ epoch: { "w1": model.get_weights()[0][0][0], "w2": model.get_weights()[0][1][0], "w3": model.get_weights()[0][2][0], "w4": model.get_weights()[0][3][0], "b": model.get_weights()[1][0] } }) )
训练完成后,可以打印各epoch的w4值,观察其是否逐渐趋近于0。
额外建议:检查标签生成逻辑
你代码中标签生成的公式是:
my_label[i] = ((2 + np.random.uniform(0.0, 0.1) ) * row[0]) + ((5 + np.random.uniform(0.0, 0.1) )* row[1]) + ((3 + np.random.uniform(0.0, 0.1) )* row[2]) + (50 + np.random.randint(low=1, high=3))
和你描述的y=2*x1+5*x2+3*x3+5有差异,常数项是50+随机数,不过这不会影响L1正则化的稀疏化效果,但如果是笔误可以修正。
内容的提问来源于stack exchange,提问作者Nitul

