KFold 交叉验证无法修复过度拟合
KFold Cross Validation does not fix overfitting
我将 X
和 y
中的特征分开,然后在使用 k 折交叉验证拆分后预处理我的火车测试数据。之后,我将训练数据拟合到我的随机森林回归模型并计算置信度分数。为什么要在拆分后进行预处理?因为人们告诉我这样做更正确,而且为了我的模型性能,我一直坚持这一原则。
这是我第一次使用 KFold 交叉验证,因为我的模型得分过高,我认为我可以通过交叉验证来修复它。我仍然对如何使用它感到困惑,我已经阅读了文档和一些文章,但我并没有真正明白我是如何真正将它暗示到我的模型中的,但我还是尝试了,但我的模型仍然过拟合。使用火车测试拆分或交叉验证导致我的模型分数仍然是 0.999,我不知道我的错误是什么,因为我是使用这种方法的新手,但我想也许我做错了,所以它没有修复过度拟合。请告诉我我的代码有什么问题以及如何解决这个问题
import pandas as pd
import seaborn as sns
import numpy as np
import matplotlib.pyplot as plt
from sklearn.impute import SimpleImputer
from sklearn.model_selection import train_test_split
from sklearn.model_selection import KFold
from sklearn.preprocessing import LabelEncoder
from sklearn.ensemble import RandomForestRegressor
import scipy.stats as ss
avo_sales = pd.read_csv('avocados.csv')
avo_sales.rename(columns = {'4046':'small PLU sold',
'4225':'large PLU sold',
'4770':'xlarge PLU sold'},
inplace= True)
avo_sales.columns = avo_sales.columns.str.replace(' ','')
x = np.array(avo_sales.drop(['TotalBags','Unnamed:0','year','region','Date'],1))
y = np.array(avo_sales.TotalBags)
# X_train, X_test, y_train, y_test = train_test_split(x, y, test_size=0.2)
kf = KFold(n_splits=10)
for train_index, test_index in kf.split(x):
X_train, X_test, y_train, y_test = x[train_index], x[test_index], y[train_index], y[test_index]
impC = SimpleImputer(strategy='most_frequent')
X_train[:,8] = impC.fit_transform(X_train[:,8].reshape(-1,1)).ravel()
X_test[:,8] = impC.transform(X_test[:,8].reshape(-1,1)).ravel()
imp = SimpleImputer(strategy='median')
X_train[:,1:8] = imp.fit_transform(X_train[:,1:8])
X_test[:,1:8] = imp.transform(X_test[:,1:8])
le = LabelEncoder()
X_train[:,8] = le.fit_transform(X_train[:,8])
X_test[:,8] = le.transform(X_test[:,8])
rfr = RandomForestRegressor()
rfr.fit(X_train, y_train)
confidence = rfr.score(X_test, y_test)
print(confidence)
你过度拟合的原因是,在所有训练样本都被正确分类之前,基于树的非正则化模型会调整数据。例如,请参见这张图片:
如您所见,这并不能很好地概括。如果您不指定对树进行正则化的参数,该模型将无法很好地拟合测试数据,因为它基本上只会学习训练数据中的噪音。 sklearn
中有很多正则化树的方法,你可以找到它们here。例如:
- max_features
- min_samples_leaf
- max_depth
通过适当的正则化,您可以获得一个可以很好地泛化到测试数据的模型。以正则化模型为例:
要规范化您的模型,请像这样实例化 RandomForestRegressor()
模块:
rfr = RandomForestRegressor(max_features=0.5, min_samples_leaf=4, max_depth=6)
这些参数值是任意的,您可以自行选择最适合您的数据的值。您可以使用特定领域的知识来选择这些值,或者使用 GridSearchCV
或 RandomizedSearchCV
等超参数调整搜索。
除此之外,估算均值和中位数可能会给您的数据带来很多噪音。除非你别无选择,否则我会反对它。
虽然@NicolasGervais 的回答深入说明了您的特定模型过度拟合的原因,但我认为在原始问题中存在关于交叉验证的概念误解;你似乎认为:
Cross-validation is a method that improves the performance of a machine learning model.
但不是这种情况。
交叉验证是一种用于估计给定模型在未见数据上的性能的方法。它本身并不能提高准确性。
换句话说,各自的分数可以告诉你你的模型是否过度拟合训练数据,但简单地应用交叉验证并不能使你的模型更好。
示例:
让我们看一个有 10 个点的数据集,并用一条线穿过它:
import numpy as np
import matplotlib.pyplot as plt
from sklearn.linear_model import LinearRegression
X = np.random.randint(0,10,10)
Y = np.random.randint(0,10,10)
fig = plt.figure(figsize=(1,10))
def line(x, slope, intercept):
return slope * x + intercept
for i in range(5):
# note that this is not technically 5-fold cross-validation
# because I allow the same datapoint to go into the test set
# several times. For illustrative purposes it is fine imho.
test_indices = np.random.choice(np.arange(10),2)
train_indices = list(set(range(10))-set(test_indices))
# get train and test sets
X_train, Y_train = X[train_indices], Y[train_indices]
X_test, Y_test = X[test_indices], Y[test_indices]
# training set has one feature and multiple entries
# so, reshape(-1,1)
X_train, Y_train, X_test, Y_test = X_train.reshape(-1,1), Y_train.reshape(-1,1), X_test.reshape(-1,1), Y_test.reshape(-1,1)
# fit and evaluate linear regression
reg = LinearRegression().fit(X_train, Y_train)
score_train = reg.score(X_train, Y_train)
score_test = reg.score(X_test, Y_test)
# extract coefficients from model:
slope, intercept = reg.coef_[0], reg.intercept_[0]
print(score_test)
# show train and test sets
plt.subplot(5,1,i+1)
plt.scatter(X_train, Y_train, c='k')
plt.scatter(X_test, Y_test, c='r')
# draw regression line
plt.plot(np.arange(10), line(np.arange(10), slope, intercept))
plt.ylim(0,10)
plt.xlim(0,10)
plt.title('train: {:.2f} test: {:.2f}'.format(score_train, score_test))
可以看到训练集和测试集的分数相差很大。您还可以看到估计的参数随着训练和测试集的变化 变化很大 。
这根本不会使您的线性模型变得更好。
但现在你知道它有多糟糕了:)
我将 X
和 y
中的特征分开,然后在使用 k 折交叉验证拆分后预处理我的火车测试数据。之后,我将训练数据拟合到我的随机森林回归模型并计算置信度分数。为什么要在拆分后进行预处理?因为人们告诉我这样做更正确,而且为了我的模型性能,我一直坚持这一原则。
这是我第一次使用 KFold 交叉验证,因为我的模型得分过高,我认为我可以通过交叉验证来修复它。我仍然对如何使用它感到困惑,我已经阅读了文档和一些文章,但我并没有真正明白我是如何真正将它暗示到我的模型中的,但我还是尝试了,但我的模型仍然过拟合。使用火车测试拆分或交叉验证导致我的模型分数仍然是 0.999,我不知道我的错误是什么,因为我是使用这种方法的新手,但我想也许我做错了,所以它没有修复过度拟合。请告诉我我的代码有什么问题以及如何解决这个问题
import pandas as pd
import seaborn as sns
import numpy as np
import matplotlib.pyplot as plt
from sklearn.impute import SimpleImputer
from sklearn.model_selection import train_test_split
from sklearn.model_selection import KFold
from sklearn.preprocessing import LabelEncoder
from sklearn.ensemble import RandomForestRegressor
import scipy.stats as ss
avo_sales = pd.read_csv('avocados.csv')
avo_sales.rename(columns = {'4046':'small PLU sold',
'4225':'large PLU sold',
'4770':'xlarge PLU sold'},
inplace= True)
avo_sales.columns = avo_sales.columns.str.replace(' ','')
x = np.array(avo_sales.drop(['TotalBags','Unnamed:0','year','region','Date'],1))
y = np.array(avo_sales.TotalBags)
# X_train, X_test, y_train, y_test = train_test_split(x, y, test_size=0.2)
kf = KFold(n_splits=10)
for train_index, test_index in kf.split(x):
X_train, X_test, y_train, y_test = x[train_index], x[test_index], y[train_index], y[test_index]
impC = SimpleImputer(strategy='most_frequent')
X_train[:,8] = impC.fit_transform(X_train[:,8].reshape(-1,1)).ravel()
X_test[:,8] = impC.transform(X_test[:,8].reshape(-1,1)).ravel()
imp = SimpleImputer(strategy='median')
X_train[:,1:8] = imp.fit_transform(X_train[:,1:8])
X_test[:,1:8] = imp.transform(X_test[:,1:8])
le = LabelEncoder()
X_train[:,8] = le.fit_transform(X_train[:,8])
X_test[:,8] = le.transform(X_test[:,8])
rfr = RandomForestRegressor()
rfr.fit(X_train, y_train)
confidence = rfr.score(X_test, y_test)
print(confidence)
你过度拟合的原因是,在所有训练样本都被正确分类之前,基于树的非正则化模型会调整数据。例如,请参见这张图片:
如您所见,这并不能很好地概括。如果您不指定对树进行正则化的参数,该模型将无法很好地拟合测试数据,因为它基本上只会学习训练数据中的噪音。 sklearn
中有很多正则化树的方法,你可以找到它们here。例如:
- max_features
- min_samples_leaf
- max_depth
通过适当的正则化,您可以获得一个可以很好地泛化到测试数据的模型。以正则化模型为例:
要规范化您的模型,请像这样实例化 RandomForestRegressor()
模块:
rfr = RandomForestRegressor(max_features=0.5, min_samples_leaf=4, max_depth=6)
这些参数值是任意的,您可以自行选择最适合您的数据的值。您可以使用特定领域的知识来选择这些值,或者使用 GridSearchCV
或 RandomizedSearchCV
等超参数调整搜索。
除此之外,估算均值和中位数可能会给您的数据带来很多噪音。除非你别无选择,否则我会反对它。
虽然@NicolasGervais 的回答深入说明了您的特定模型过度拟合的原因,但我认为在原始问题中存在关于交叉验证的概念误解;你似乎认为:
Cross-validation is a method that improves the performance of a machine learning model.
但不是这种情况。
交叉验证是一种用于估计给定模型在未见数据上的性能的方法。它本身并不能提高准确性。 换句话说,各自的分数可以告诉你你的模型是否过度拟合训练数据,但简单地应用交叉验证并不能使你的模型更好。
示例: 让我们看一个有 10 个点的数据集,并用一条线穿过它:
import numpy as np
import matplotlib.pyplot as plt
from sklearn.linear_model import LinearRegression
X = np.random.randint(0,10,10)
Y = np.random.randint(0,10,10)
fig = plt.figure(figsize=(1,10))
def line(x, slope, intercept):
return slope * x + intercept
for i in range(5):
# note that this is not technically 5-fold cross-validation
# because I allow the same datapoint to go into the test set
# several times. For illustrative purposes it is fine imho.
test_indices = np.random.choice(np.arange(10),2)
train_indices = list(set(range(10))-set(test_indices))
# get train and test sets
X_train, Y_train = X[train_indices], Y[train_indices]
X_test, Y_test = X[test_indices], Y[test_indices]
# training set has one feature and multiple entries
# so, reshape(-1,1)
X_train, Y_train, X_test, Y_test = X_train.reshape(-1,1), Y_train.reshape(-1,1), X_test.reshape(-1,1), Y_test.reshape(-1,1)
# fit and evaluate linear regression
reg = LinearRegression().fit(X_train, Y_train)
score_train = reg.score(X_train, Y_train)
score_test = reg.score(X_test, Y_test)
# extract coefficients from model:
slope, intercept = reg.coef_[0], reg.intercept_[0]
print(score_test)
# show train and test sets
plt.subplot(5,1,i+1)
plt.scatter(X_train, Y_train, c='k')
plt.scatter(X_test, Y_test, c='r')
# draw regression line
plt.plot(np.arange(10), line(np.arange(10), slope, intercept))
plt.ylim(0,10)
plt.xlim(0,10)
plt.title('train: {:.2f} test: {:.2f}'.format(score_train, score_test))
可以看到训练集和测试集的分数相差很大。您还可以看到估计的参数随着训练和测试集的变化 变化很大 。
这根本不会使您的线性模型变得更好。 但现在你知道它有多糟糕了:)