import pandas as pddf = pd . read_csv ( "winequality-red.csv" )
quality_mapping = {3: 0,4: 1,5: 2,6: 3,7: 4,8: 5
}
df.loc[:, "quality"] = df.quality.map(quality_mapping)
df = df.sample(frac=1).reset_index(drop=True)
df_train = df.head(1000)
df_test = df.tail(599)
现在,我们将在训练集上使⽤scikit-learn训练⼀个决策树模型。
from sklearn import tree
from sklearn import metricsclf = tree.DecisionTreeClassifier(max_depth=3)cols = ['fixed acidity','volatile acidity','citric acid','residual sugar','chlorides','free sulfur dioxide','total sulfur dioxide','density','pH','sulphates','alcohol']
clf.fit(df_train[cols], df_train.quality)
train_predictions = clf.predict(df_train[cols])test_predictions = clf.predict(df_test[cols])train_accuracy = metrics.accuracy_score(df_train.quality, train_predictions
)
test_accuracy = metrics.accuracy_score(df_test.quality, test_predictions
)
from sklearn import tree
from sklearn import metrics
import matplotlib
import matplotlib.pyplot as plt
import seaborn as sns# 设置 matplotlib 字体大小
matplotlib.rc('xtick', labelsize=20)
matplotlib.rc('ytick', labelsize=20)# 用于在 Jupyter Notebook 中显示图形
%matplotlib inline# 初始化训练集和测试集的准确率列表
train_accuracies = [0.5]
test_accuracies = [0.5]# 循环尝试不同的决策树深度
for depth in range(1, 25):clf = tree.DecisionTreeClassifier(max_depth=depth)# 特征列名cols = ['fixed acidity','volatile acidity','citric acid','residual sugar','chlorides','free sulfur dioxide','total sulfur dioxide','density','pH','sulphates','alcohol']# 使用训练集训练模型clf.fit(df_train[cols], df_train.quality)# 进行训练集和测试集的预测train_predictions = clf.predict(df_train[cols])test_predictions = clf.predict(df_test[cols])# 计算训练集和测试集的准确率train_accuracy = metrics.accuracy_score(df_train.quality, train_predictions)test_accuracy = metrics.accuracy_score(df_test.quality, test_predictions)# 将准确率添加到列表中train_accuracies.append(train_accuracy)test_accuracies.append(test_accuracy)# 绘制准确率随最大深度变化的图像
plt.figure(figsize=(10, 5))
sns.set_style("whitegrid")
plt.plot(train_accuracies, label="train accuracy")
plt.plot(test_accuracies, label="test accuracy")
plt.legend(loc="upper left", prop={'size': 15})
plt.xticks(range(0, 26, 5))
plt.xlabel("max_depth", size=20)
plt.ylabel("accuracy", size=20)
plt.show()
这将⽣成如图 2 所⽰的曲线图。
import pandas as pd
from sklearn import model_selectionif __name__ == "__main__":# 读取训练数据集df = pd.read_csv("train.csv")# 创建一个新列 "kfold" 并初始化为 -1df["kfold"] = -1# 随机打乱数据集df = df.sample(frac=1).reset_index(drop=True)# 初始化 KFold 分割器,将数据集分成 5 折kf = model_selection.KFold(n_splits=5)# 遍历每一折并为相应的样本分配折叠索引for fold, (trn_, val_) in enumerate(kf.split(X=df)):df.loc[val_, 'kfold'] = fold# 将带有折叠索引的数据集保存为 CSV 文件df.to_csv("train_folds.csv", index=False)
import pandas as pd
from sklearn import model_selectionif __name__ == "__main__":# 读取训练数据集df = pd.read_csv("train.csv")# 创建一个新列 "kfold" 并初始化为 -1df["kfold"] = -1# 随机打乱数据集df = df.sample(frac=1).reset_index(drop=True)# 提取目标变量列y = df.target.values# 初始化 StratifiedKFold 分割器,将数据集分成 5 折,并保持目标变量的分布相同kf = model_selection.StratifiedKFold(n_splits=5)# 遍历每一折并为相应的样本分配折叠索引for fold, (train_idx, val_idx) in enumerate(kf.split(X=df, y=y)):df.loc[val_idx, 'kfold'] = fold# 将带有折叠索引的数据集保存为 CSV 文件df.to_csv("train_folds.csv", index=False)
对于葡萄酒数据集,我们来看看标签的分布情况。
b = sns . countplot ( x = 'quality' , data = df )b . set_xlabel ( "quality" , fontsize = 20 )b . set_ylabel ( "count" , fontsize = 20 )
请注意,我们继续上⾯的代码。因此,我们已经转换了⽬标值。从图 6 中我们可以看出,质量偏差很⼤。有些类别有很多样本,有些则没有那么多。如果我们进⾏简单的k折交叉检验,那么每个折叠中的⽬标值分布都不会相同。因此,在这种情况下,我们选择分层 k 折交叉检验。
规则很简单,如果是标准分类问题,就盲⽬选择分层k折交叉检验。
但如果数据量很⼤,该怎么办呢?假设我们有 100万个样本。5倍交叉检验意味着在 800k 个样本 上进⾏训练,在 200k 个样本上进⾏验证。根据我们选择的算法,对于这样规模的数据集来说, 训练甚⾄验证都可能⾮常昂贵。在这种情况下,我们可以选择暂留交叉检验。
创建保持结果的过程与分层 k 折交叉检验相同。对于拥有 100 万个样本的数据集,我们可以创建 10 个折叠⽽不是 5 个,并保留其中⼀个折叠作为保留样本。这意味着,我们将有 10 万个样本被 保留下来,我们将始终在这个样本集上计算损失、准确率和其他指标,并在 90 万个样本上进训练。
在处理时间序列数据时,暂留交叉检验也⾮常常⽤。假设我们要解决的问题是预测⼀家商店 2020 年的销售额,⽽我们得到的是 2015-2019 年的所有数据。在这种情况下,你可以选择 2019 年的 所有数据作为保留数据,然后在 2015年⾄ 2018 年的所有数据上训练你的模型。
其中 是数据集中的样本数。该函数如图8所⽰。
import numpy as np
import pandas as pd
from sklearn import datasets
from sklearn import model_selectiondef create_folds(data):# 初始化新列 "kfold" 并将其全部赋值为 -1data["kfold"] = -1# 随机打乱数据集data = data.sample(frac=1).reset_index(drop=True)# 计算分箱数量num_bins = int(np.floor(1 + np.log2(len(data))))# 将目标变量分到对应的分箱中data.loc[:, "bins"] = pd.cut(data["target"], bins=num_bins, labels=False)# 初始化 StratifiedKFold 分割器,保持分箱后的分布相同kf = model_selection.StratifiedKFold(n_splits=5)# 遍历每一折并为相应的样本分配折叠索引for fold, (train_idx, val_idx) in enumerate(kf.split(X=data, y=data.bins.values)):data.loc[val_idx, 'kfold'] = fold# 删除创建的分箱列data = data.drop("bins", axis=1)return dataif __name__ == "__main__":# 生成模拟数据X, y = datasets.make_regression(n_samples=15000, n_features=100, n_targets=1)# 将模拟数据转换为 DataFrame 格式df = pd.DataFrame(X,columns=[f"f_{i}" for i in range(X.shape[1])])# 将目标变量添加到 DataFrame 中df.loc[:, "target"] = y# 使用 create_folds 函数创建带有折叠索引的数据集df = create_folds(df)