最小描述长度MDL实战用Python超越传统模型选择方法在机器学习的世界里模型选择一直是个令人头疼的问题。我们常常陷入这样的困境模型太简单预测效果不佳模型太复杂又容易过拟合。传统方法如AIC赤池信息准则和BIC贝叶斯信息准则虽然广为人知但它们并非在所有场景下都是最优解。今天我们要介绍一种基于信息论的强大替代方案——最小描述长度Minimum Description LengthMDL原则并通过Python实战演示如何将其应用于实际模型选择问题。1. 为什么需要MDL信息论视角下的模型选择模型选择的本质是在模型复杂度和拟合优度之间寻找平衡点。AIC和BIC通过引入惩罚项来避免过拟合但它们都有各自的局限性AIC倾向于选择更复杂的模型尤其在大样本情况下BIC虽然对复杂度惩罚更重但理论基础依赖于样本量趋于无穷大的假设MDL原则则从信息论的角度提供了一个更通用的框架。其核心思想是最好的模型是能够以最短的编码长度描述数据。这包括两部分描述模型本身所需的编码长度L(h)用该模型描述数据所需的编码长度L(D|h))# MDL基本公式的Python表示 import numpy as np def mdl(model_complexity, data_fit): 计算最小描述长度 参数: model_complexity -- 模型复杂度项负对数先验 data_fit -- 数据拟合项负对数似然 返回: MDL值 return model_complexity data_fitMDL与AIC/BIC的关键区别在于准则复杂度惩罚理论基础适用场景AIC2k渐近理论预测准确性优先BICk*log(n)贝叶斯模型识别优先MDL可变信息论通用编码最优2. 实战准备环境配置与数据加载在开始编码前我们需要准备好Python环境和示例数据集。这里我们使用经典的UCI波士顿房价数据集作为演示。# 环境准备 import numpy as np import pandas as pd import matplotlib.pyplot as plt from sklearn.datasets import load_boston from sklearn.model_selection import train_test_split from sklearn.preprocessing import PolynomialFeatures from sklearn.linear_model import LinearRegression from sklearn.metrics import mean_squared_error # 加载数据 boston load_boston() X pd.DataFrame(boston.data, columnsboston.feature_names) y boston.target # 选择最具代表性的特征为了演示简化 X X[[LSTAT, RM]] # 低收入比例和房间数 X_train, X_test, y_train, y_test train_test_split(X, y, test_size0.2, random_state42)为了展示MDL在不同复杂度模型下的表现我们将创建一系列多项式回归模型# 生成不同阶数的多项式特征 degrees range(1, 8) # 从线性到7次多项式 models {} for degree in degrees: # 生成多项式特征 poly PolynomialFeatures(degreedegree) X_poly_train poly.fit_transform(X_train) X_poly_test poly.transform(X_test) # 训练模型 model LinearRegression() model.fit(X_poly_train, y_train) # 存储模型和转换后的数据 models[degree] { model: model, poly: poly, X_poly_train: X_poly_train, X_poly_test: X_poly_test }3. 实现MDL计算从理论到代码MDL的具体实现需要考虑两个关键部分模型描述长度和数据描述长度。对于线性回归模型我们可以采用以下方法计算3.1 模型描述长度L(h))模型描述长度反映了编码模型参数所需的信息量。对于线性回归我们可以使用参数的负对数先验def compute_model_length(model, sigma_prior1.0): 计算线性回归模型的描述长度 参数: model -- 训练好的线性回归模型 sigma_prior -- 参数先验分布的标准差 返回: 模型描述长度负对数先验 # 获取模型参数不包括截距 params model.coef_ # 计算负对数高斯先验 log_prior -np.sum(np.log(1/(np.sqrt(2*np.pi)*sigma_prior)) - (params**2)/(2*sigma_prior**2)) return log_prior3.2 数据描述长度L(D|h))数据描述长度反映了在给定模型下编码数据所需的信息量。对于回归问题我们可以使用负对数似然def compute_data_length(model, X, y, sigma_noise1.0): 计算数据描述长度负对数似然 参数: model -- 训练好的模型 X -- 特征矩阵 y -- 真实值 sigma_noise -- 噪声标准差假设 返回: 数据描述长度 # 获取预测值 y_pred model.predict(X) # 计算残差平方和 rss np.sum((y - y_pred)**2) # 计算负对数似然高斯噪声假设 n len(y) log_likelihood -n/2 * np.log(2*np.pi*sigma_noise**2) - rss/(2*sigma_noise**2) return -log_likelihood3.3 综合MDL计算将两部分结合起来我们得到完整的MDL计算函数def compute_mdl(model, X, y, sigma_prior1.0, sigma_noise1.0): 计算完整的MDL值 参数: model -- 训练好的模型 X -- 特征矩阵 y -- 真实值 sigma_prior -- 参数先验标准差 sigma_noise -- 噪声标准差 返回: MDL值 L_h compute_model_length(model, sigma_prior) L_Dh compute_data_length(model, X, y, sigma_noise) return L_h L_Dh4. 模型比较MDL vs AIC/BIC现在我们可以比较不同多项式阶数下各准则的表现# 计算各模型的评价指标 results [] for degree, m in models.items(): # 获取模型和数据 model m[model] X_poly_train m[X_poly_train] X_poly_test m[X_poly_test] # 计算训练集和测试集误差 train_mse mean_squared_error(y_train, model.predict(X_poly_train)) test_mse mean_squared_error(y_test, model.predict(X_poly_test)) # 计算各种信息准则 n X_poly_train.shape[0] k X_poly_train.shape[1] # AIC计算 aic n * np.log(train_mse) 2 * k # BIC计算 bic n * np.log(train_mse) k * np.log(n) # MDL计算 mdl compute_mdl(model, X_poly_train, y_train) results.append({ degree: degree, train_mse: train_mse, test_mse: test_mse, aic: aic, bic: bic, mdl: mdl }) # 转换为DataFrame方便分析 results_df pd.DataFrame(results).set_index(degree)让我们可视化这些结果# 绘制各准则随模型复杂度的变化 plt.figure(figsize(12, 6)) # 标准化各指标以便比较 normalized_df results_df.apply(lambda x: (x - x.min()) / (x.max() - x.min())) # 绘制曲线 plt.plot(normalized_df.index, normalized_df[aic], o-, labelAIC) plt.plot(normalized_df.index, normalized_df[bic], s-, labelBIC) plt.plot(normalized_df.index, normalized_df[mdl], d-, labelMDL) # 标记最小值 min_aic normalized_df[aic].idxmin() min_bic normalized_df[bic].idxmin() min_mdl normalized_df[mdl].idxmin() plt.axvline(min_aic, colorblue, linestyle--, alpha0.3) plt.axvline(min_bic, colororange, linestyle--, alpha0.3) plt.axvline(min_mdl, colorgreen, linestyle--, alpha0.3) plt.xlabel(多项式阶数) plt.ylabel(标准化值) plt.title(不同模型选择准则的比较) plt.legend() plt.grid(True) plt.show()从结果中我们通常会发现AIC倾向于选择较高阶的模型欠惩罚BIC比AIC更保守但可能仍不够MDL通常选择最简洁的模型能有效防止过拟合5. 高级话题调整MDL的超参数MDL计算中的sigma_prior和sigma_noise是重要的超参数它们会影响MDL的表现5.1 噪声标准差(sigma_noise)的影响噪声标准差决定了我们对数据噪声的假设。在实践中我们可以使用交叉验证估计从残差中估计作为超参数调整# 从训练残差估计噪声标准差 def estimate_noise(model, X, y): y_pred model.predict(X) residuals y - y_pred return np.std(residuals) # 为每个模型估计噪声水平 for degree, m in models.items(): sigma_noise estimate_noise(m[model], m[X_poly_train], y_train) print(fDegree {degree}: estimated noise {sigma_noise:.2f})5.2 先验标准差(sigma_prior)的选择先验标准差反映了我们对参数大小的预期。选择策略包括基于领域知识使用分层贝叶斯方法估计通过交叉验证选择# 尝试不同的先验标准差 sigma_priors [0.1, 0.5, 1.0, 2.0, 5.0] for sigma_prior in sigma_priors: mdl_values [] for degree, m in models.items(): mdl_val compute_mdl(m[model], m[X_poly_train], y_train, sigma_priorsigma_prior) mdl_values.append(mdl_val) # 找出最优模型 best_degree np.argmin(mdl_values) 1 print(fSigma_prior{sigma_prior}: best degree{best_degree})5.3 贝叶斯信息准则(BIC)与MDL的关系在某些条件下BIC可以看作是MDL的一种特例。具体来说当使用平坦先验时当样本量n足够大时当模型参数满足特定条件时然而MDL提供了更灵活的框架可以融入更复杂的先验知识处理非参数模型适应不同的编码方案6. 实际应用建议与注意事项在真实项目中使用MDL时有几个实用建议特征标准化确保所有特征在相似尺度上使参数先验更有意义from sklearn.preprocessing import StandardScaler scaler StandardScaler() X_train_scaled scaler.fit_transform(X_train) X_test_scaled scaler.transform(X_test)模型复杂度计算对于非参数模型如决策树需要更复杂的编码方案def tree_complexity(tree): 计算决策树的编码长度 n_nodes tree.tree_.node_count n_leaves (tree.tree_.children_left -1).sum() # 简单近似每个节点需要一定比特数编码 return n_nodes * 2 n_leaves * 4混合模型对于包含不同类型参数的模型可以采用分层编码def mixed_model_mdl(model): # 线性部分 linear_part compute_model_length(model.linear_part) # 树部分 tree_part tree_complexity(model.tree_part) return linear_part tree_part交叉验证虽然MDL提供了理论框架但实践中的噪声估计仍需验证from sklearn.model_selection import cross_val_score cv_scores cross_val_score(model, X, y, scoringneg_mean_squared_error, cv5) estimated_noise np.sqrt(-cv_scores.mean())可视化诊断始终检查模型残差是否符合假设plt.scatter(y_pred, y_pred - y_test) plt.xlabel(预测值) plt.ylabel(残差) plt.axhline(0, colorred) plt.title(残差图)7. 超越线性模型MDL在其他模型中的应用MDL原则不仅适用于线性模型还可以扩展到各种机器学习模型7.1 决策树与MDL决策树的MDL实现需要考虑树结构的编码from sklearn.tree import DecisionTreeRegressor def compute_tree_mdl(tree, X, y): # 编码树结构 n_nodes tree.tree_.node_count n_leaves (tree.tree_.children_left -1).sum() structure_length n_nodes * 2 # 每个节点约2比特 # 编码叶节点值 leaf_values tree.tree_.value[(tree.tree_.children_left -1), 0, 0] leaves_length np.sum(np.log(1 np.abs(leaf_values))) # 数据拟合项 y_pred tree.predict(X) rss np.sum((y - y_pred)**2) data_length len(y)/2 * np.log(rss/len(y)) if rss 0 else 0 return structure_length leaves_length data_length7.2 神经网络中的MDL神经网络的MDL计算更具挑战性可以考虑权重量化后的编码长度网络结构的描述长度激活模式的压缩表示import torch import torch.nn as nn def compute_nn_mdl(model, X, y, bits_per_weight4): # 计算权重编码长度 total_bits 0 for param in model.parameters(): n_weights param.numel() total_bits n_weights * bits_per_weight # 转换为nats (1 nat ≈ 1.44 bits) model_length total_bits / 1.44 # 数据拟合项 with torch.no_grad(): y_pred model(torch.FloatTensor(X)) loss nn.MSELoss()(y_pred, torch.FloatTensor(y).view(-1,1)) data_length len(y)/2 * torch.log(loss).item() return model_length data_length7.3 集成模型的MDL考虑对于随机森林或梯度提升树等集成方法MDL计算需要包括基学习器的编码集成策略的描述组合规则的表示from sklearn.ensemble import RandomForestRegressor def compute_rf_mdl(forest, X, y): # 编码每棵树 trees_length sum(compute_tree_mdl(tree, X, y) for tree in forest.estimators_) # 编码集成方法简单近似 ensemble_length np.log(len(forest.estimators_)) # 数据拟合项 y_pred forest.predict(X) rss np.sum((y - y_pred)**2) data_length len(y)/2 * np.log(rss/len(y)) if rss 0 else 0 return trees_length ensemble_length data_length8. MDL在特征选择中的应用MDL原则也可以指导特征选择过程。基本思路是比较包含该特征时的总描述长度不包含该特征时的总描述长度def mdl_feature_selection(X, y, max_featuresNone): 基于MDL的前向特征选择 参数: X -- 特征DataFrame y -- 目标变量 max_features -- 可选的最大特征数 返回: 选中的特征列表 selected [] remaining list(X.columns) if max_features is None: max_features len(remaining) while len(selected) max_features and remaining: best_mdl float(inf) best_feature None for feature in remaining: # 尝试添加当前特征 trial_features selected [feature] X_trial X[trial_features] # 训练简单线性模型 model LinearRegression().fit(X_trial, y) # 计算MDL current_mdl compute_mdl(model, X_trial, y) if current_mdl best_mdl: best_mdl current_mdl best_feature feature if best_feature is not None: selected.append(best_feature) remaining.remove(best_feature) else: break return selected这种方法特别适合高维数据可以避免传统方法如基于p值的一些问题。
别再只盯着AIC/BIC了!用Python实战最小描述长度MDL做模型选择(附代码)
发布时间:2026/7/1 17:48:52
最小描述长度MDL实战用Python超越传统模型选择方法在机器学习的世界里模型选择一直是个令人头疼的问题。我们常常陷入这样的困境模型太简单预测效果不佳模型太复杂又容易过拟合。传统方法如AIC赤池信息准则和BIC贝叶斯信息准则虽然广为人知但它们并非在所有场景下都是最优解。今天我们要介绍一种基于信息论的强大替代方案——最小描述长度Minimum Description LengthMDL原则并通过Python实战演示如何将其应用于实际模型选择问题。1. 为什么需要MDL信息论视角下的模型选择模型选择的本质是在模型复杂度和拟合优度之间寻找平衡点。AIC和BIC通过引入惩罚项来避免过拟合但它们都有各自的局限性AIC倾向于选择更复杂的模型尤其在大样本情况下BIC虽然对复杂度惩罚更重但理论基础依赖于样本量趋于无穷大的假设MDL原则则从信息论的角度提供了一个更通用的框架。其核心思想是最好的模型是能够以最短的编码长度描述数据。这包括两部分描述模型本身所需的编码长度L(h)用该模型描述数据所需的编码长度L(D|h))# MDL基本公式的Python表示 import numpy as np def mdl(model_complexity, data_fit): 计算最小描述长度 参数: model_complexity -- 模型复杂度项负对数先验 data_fit -- 数据拟合项负对数似然 返回: MDL值 return model_complexity data_fitMDL与AIC/BIC的关键区别在于准则复杂度惩罚理论基础适用场景AIC2k渐近理论预测准确性优先BICk*log(n)贝叶斯模型识别优先MDL可变信息论通用编码最优2. 实战准备环境配置与数据加载在开始编码前我们需要准备好Python环境和示例数据集。这里我们使用经典的UCI波士顿房价数据集作为演示。# 环境准备 import numpy as np import pandas as pd import matplotlib.pyplot as plt from sklearn.datasets import load_boston from sklearn.model_selection import train_test_split from sklearn.preprocessing import PolynomialFeatures from sklearn.linear_model import LinearRegression from sklearn.metrics import mean_squared_error # 加载数据 boston load_boston() X pd.DataFrame(boston.data, columnsboston.feature_names) y boston.target # 选择最具代表性的特征为了演示简化 X X[[LSTAT, RM]] # 低收入比例和房间数 X_train, X_test, y_train, y_test train_test_split(X, y, test_size0.2, random_state42)为了展示MDL在不同复杂度模型下的表现我们将创建一系列多项式回归模型# 生成不同阶数的多项式特征 degrees range(1, 8) # 从线性到7次多项式 models {} for degree in degrees: # 生成多项式特征 poly PolynomialFeatures(degreedegree) X_poly_train poly.fit_transform(X_train) X_poly_test poly.transform(X_test) # 训练模型 model LinearRegression() model.fit(X_poly_train, y_train) # 存储模型和转换后的数据 models[degree] { model: model, poly: poly, X_poly_train: X_poly_train, X_poly_test: X_poly_test }3. 实现MDL计算从理论到代码MDL的具体实现需要考虑两个关键部分模型描述长度和数据描述长度。对于线性回归模型我们可以采用以下方法计算3.1 模型描述长度L(h))模型描述长度反映了编码模型参数所需的信息量。对于线性回归我们可以使用参数的负对数先验def compute_model_length(model, sigma_prior1.0): 计算线性回归模型的描述长度 参数: model -- 训练好的线性回归模型 sigma_prior -- 参数先验分布的标准差 返回: 模型描述长度负对数先验 # 获取模型参数不包括截距 params model.coef_ # 计算负对数高斯先验 log_prior -np.sum(np.log(1/(np.sqrt(2*np.pi)*sigma_prior)) - (params**2)/(2*sigma_prior**2)) return log_prior3.2 数据描述长度L(D|h))数据描述长度反映了在给定模型下编码数据所需的信息量。对于回归问题我们可以使用负对数似然def compute_data_length(model, X, y, sigma_noise1.0): 计算数据描述长度负对数似然 参数: model -- 训练好的模型 X -- 特征矩阵 y -- 真实值 sigma_noise -- 噪声标准差假设 返回: 数据描述长度 # 获取预测值 y_pred model.predict(X) # 计算残差平方和 rss np.sum((y - y_pred)**2) # 计算负对数似然高斯噪声假设 n len(y) log_likelihood -n/2 * np.log(2*np.pi*sigma_noise**2) - rss/(2*sigma_noise**2) return -log_likelihood3.3 综合MDL计算将两部分结合起来我们得到完整的MDL计算函数def compute_mdl(model, X, y, sigma_prior1.0, sigma_noise1.0): 计算完整的MDL值 参数: model -- 训练好的模型 X -- 特征矩阵 y -- 真实值 sigma_prior -- 参数先验标准差 sigma_noise -- 噪声标准差 返回: MDL值 L_h compute_model_length(model, sigma_prior) L_Dh compute_data_length(model, X, y, sigma_noise) return L_h L_Dh4. 模型比较MDL vs AIC/BIC现在我们可以比较不同多项式阶数下各准则的表现# 计算各模型的评价指标 results [] for degree, m in models.items(): # 获取模型和数据 model m[model] X_poly_train m[X_poly_train] X_poly_test m[X_poly_test] # 计算训练集和测试集误差 train_mse mean_squared_error(y_train, model.predict(X_poly_train)) test_mse mean_squared_error(y_test, model.predict(X_poly_test)) # 计算各种信息准则 n X_poly_train.shape[0] k X_poly_train.shape[1] # AIC计算 aic n * np.log(train_mse) 2 * k # BIC计算 bic n * np.log(train_mse) k * np.log(n) # MDL计算 mdl compute_mdl(model, X_poly_train, y_train) results.append({ degree: degree, train_mse: train_mse, test_mse: test_mse, aic: aic, bic: bic, mdl: mdl }) # 转换为DataFrame方便分析 results_df pd.DataFrame(results).set_index(degree)让我们可视化这些结果# 绘制各准则随模型复杂度的变化 plt.figure(figsize(12, 6)) # 标准化各指标以便比较 normalized_df results_df.apply(lambda x: (x - x.min()) / (x.max() - x.min())) # 绘制曲线 plt.plot(normalized_df.index, normalized_df[aic], o-, labelAIC) plt.plot(normalized_df.index, normalized_df[bic], s-, labelBIC) plt.plot(normalized_df.index, normalized_df[mdl], d-, labelMDL) # 标记最小值 min_aic normalized_df[aic].idxmin() min_bic normalized_df[bic].idxmin() min_mdl normalized_df[mdl].idxmin() plt.axvline(min_aic, colorblue, linestyle--, alpha0.3) plt.axvline(min_bic, colororange, linestyle--, alpha0.3) plt.axvline(min_mdl, colorgreen, linestyle--, alpha0.3) plt.xlabel(多项式阶数) plt.ylabel(标准化值) plt.title(不同模型选择准则的比较) plt.legend() plt.grid(True) plt.show()从结果中我们通常会发现AIC倾向于选择较高阶的模型欠惩罚BIC比AIC更保守但可能仍不够MDL通常选择最简洁的模型能有效防止过拟合5. 高级话题调整MDL的超参数MDL计算中的sigma_prior和sigma_noise是重要的超参数它们会影响MDL的表现5.1 噪声标准差(sigma_noise)的影响噪声标准差决定了我们对数据噪声的假设。在实践中我们可以使用交叉验证估计从残差中估计作为超参数调整# 从训练残差估计噪声标准差 def estimate_noise(model, X, y): y_pred model.predict(X) residuals y - y_pred return np.std(residuals) # 为每个模型估计噪声水平 for degree, m in models.items(): sigma_noise estimate_noise(m[model], m[X_poly_train], y_train) print(fDegree {degree}: estimated noise {sigma_noise:.2f})5.2 先验标准差(sigma_prior)的选择先验标准差反映了我们对参数大小的预期。选择策略包括基于领域知识使用分层贝叶斯方法估计通过交叉验证选择# 尝试不同的先验标准差 sigma_priors [0.1, 0.5, 1.0, 2.0, 5.0] for sigma_prior in sigma_priors: mdl_values [] for degree, m in models.items(): mdl_val compute_mdl(m[model], m[X_poly_train], y_train, sigma_priorsigma_prior) mdl_values.append(mdl_val) # 找出最优模型 best_degree np.argmin(mdl_values) 1 print(fSigma_prior{sigma_prior}: best degree{best_degree})5.3 贝叶斯信息准则(BIC)与MDL的关系在某些条件下BIC可以看作是MDL的一种特例。具体来说当使用平坦先验时当样本量n足够大时当模型参数满足特定条件时然而MDL提供了更灵活的框架可以融入更复杂的先验知识处理非参数模型适应不同的编码方案6. 实际应用建议与注意事项在真实项目中使用MDL时有几个实用建议特征标准化确保所有特征在相似尺度上使参数先验更有意义from sklearn.preprocessing import StandardScaler scaler StandardScaler() X_train_scaled scaler.fit_transform(X_train) X_test_scaled scaler.transform(X_test)模型复杂度计算对于非参数模型如决策树需要更复杂的编码方案def tree_complexity(tree): 计算决策树的编码长度 n_nodes tree.tree_.node_count n_leaves (tree.tree_.children_left -1).sum() # 简单近似每个节点需要一定比特数编码 return n_nodes * 2 n_leaves * 4混合模型对于包含不同类型参数的模型可以采用分层编码def mixed_model_mdl(model): # 线性部分 linear_part compute_model_length(model.linear_part) # 树部分 tree_part tree_complexity(model.tree_part) return linear_part tree_part交叉验证虽然MDL提供了理论框架但实践中的噪声估计仍需验证from sklearn.model_selection import cross_val_score cv_scores cross_val_score(model, X, y, scoringneg_mean_squared_error, cv5) estimated_noise np.sqrt(-cv_scores.mean())可视化诊断始终检查模型残差是否符合假设plt.scatter(y_pred, y_pred - y_test) plt.xlabel(预测值) plt.ylabel(残差) plt.axhline(0, colorred) plt.title(残差图)7. 超越线性模型MDL在其他模型中的应用MDL原则不仅适用于线性模型还可以扩展到各种机器学习模型7.1 决策树与MDL决策树的MDL实现需要考虑树结构的编码from sklearn.tree import DecisionTreeRegressor def compute_tree_mdl(tree, X, y): # 编码树结构 n_nodes tree.tree_.node_count n_leaves (tree.tree_.children_left -1).sum() structure_length n_nodes * 2 # 每个节点约2比特 # 编码叶节点值 leaf_values tree.tree_.value[(tree.tree_.children_left -1), 0, 0] leaves_length np.sum(np.log(1 np.abs(leaf_values))) # 数据拟合项 y_pred tree.predict(X) rss np.sum((y - y_pred)**2) data_length len(y)/2 * np.log(rss/len(y)) if rss 0 else 0 return structure_length leaves_length data_length7.2 神经网络中的MDL神经网络的MDL计算更具挑战性可以考虑权重量化后的编码长度网络结构的描述长度激活模式的压缩表示import torch import torch.nn as nn def compute_nn_mdl(model, X, y, bits_per_weight4): # 计算权重编码长度 total_bits 0 for param in model.parameters(): n_weights param.numel() total_bits n_weights * bits_per_weight # 转换为nats (1 nat ≈ 1.44 bits) model_length total_bits / 1.44 # 数据拟合项 with torch.no_grad(): y_pred model(torch.FloatTensor(X)) loss nn.MSELoss()(y_pred, torch.FloatTensor(y).view(-1,1)) data_length len(y)/2 * torch.log(loss).item() return model_length data_length7.3 集成模型的MDL考虑对于随机森林或梯度提升树等集成方法MDL计算需要包括基学习器的编码集成策略的描述组合规则的表示from sklearn.ensemble import RandomForestRegressor def compute_rf_mdl(forest, X, y): # 编码每棵树 trees_length sum(compute_tree_mdl(tree, X, y) for tree in forest.estimators_) # 编码集成方法简单近似 ensemble_length np.log(len(forest.estimators_)) # 数据拟合项 y_pred forest.predict(X) rss np.sum((y - y_pred)**2) data_length len(y)/2 * np.log(rss/len(y)) if rss 0 else 0 return trees_length ensemble_length data_length8. MDL在特征选择中的应用MDL原则也可以指导特征选择过程。基本思路是比较包含该特征时的总描述长度不包含该特征时的总描述长度def mdl_feature_selection(X, y, max_featuresNone): 基于MDL的前向特征选择 参数: X -- 特征DataFrame y -- 目标变量 max_features -- 可选的最大特征数 返回: 选中的特征列表 selected [] remaining list(X.columns) if max_features is None: max_features len(remaining) while len(selected) max_features and remaining: best_mdl float(inf) best_feature None for feature in remaining: # 尝试添加当前特征 trial_features selected [feature] X_trial X[trial_features] # 训练简单线性模型 model LinearRegression().fit(X_trial, y) # 计算MDL current_mdl compute_mdl(model, X_trial, y) if current_mdl best_mdl: best_mdl current_mdl best_feature feature if best_feature is not None: selected.append(best_feature) remaining.remove(best_feature) else: break return selected这种方法特别适合高维数据可以避免传统方法如基于p值的一些问题。