在模型建立中,通常有两个数据集:训练集(train)和测试集(test)。训练集用来训练模型;测试集是完全不参与训练的数据,仅仅用来观测测试效果的数据。
一般情况下,训练的结果对于训练集的拟合程度通常还是挺好的,但是在测试集总的表现却可能不行。比如下面的例子:
所以,训练的模型需要找出数据之间‘真正’的关系,避免‘过拟合’的情况发生。
交叉验证:就是在训练集中选一部分样本用于测试模型。
保留一部分的训练集数据作为验证集/评估集,对训练集生成的参数进行测试,相对客观的判断这些参数对训练集之外的数据的符合程度。
只从可用的数据集中保留一个数据点,并根据其余数据训练模型。此过程对每个数据点进行迭代,比如有n个数据点,就要重复交叉验证n次。例如下图,一共10个数据,就交叉验证十次
优点:
缺点:
LOOCC是保留一个数据点,同样的你也可以保留P个数据点作为验证集,这种方法叫LPOCV(Leave P Out Cross Validation)
R
score = list()
LOOCV_function = function(x,label){
for(i in 1:nrow(x)){
training = x[-i,]
model = #... train model on training
validation = x[i,]
pred = predict(model, validation[,setdiff(names(validation),label)])
score[[i]] = rmse(pred, validation[[label]]) # score/error of ith fold
}
return(unlist(score)) # returns a vector
}
交叉验证的步骤如下:
优点: 简单方便。直接将训练集按比例拆分成训练集和验证集,比如50:50。
缺点: 没有充分利用数据, 结果具有偶然性。如果按50:50分,会损失掉另外50%的数据信息,因为我们没有利用着50%的数据来训练模型。
Python 使用train_test_split
from sklearn.model_selection import train_test_split
train, validation = train_test_split(data, test_size=0.50, random_state = 5)
用train_test_split
,划分成train 和test 两部分,其中train用来训练模型,test用来评估模型,模型通过fit方法从train数据集中学习,调用score方法在test集上进行评估。
from sklearn.model_selection import train_test_split
from sklearn.linear_model import LogisticRegression
#已经导入数据
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=20, shuffle=True)
# Logistic Regression
model = LogisticRegression()
model.fit(X_train, y_train)
prediction = model.predict(X_test)
print('The accuracy of the Logistic Regression is: {0}'.format(metrics.accuracy_score(prediction,y_test)))
R
set.seed(101) # Set Seed so that same sample can be reproduced in future also
# Now Selecting 50% of data as sample from total 'n' rows of the data
sample <- sample.int(n = nrow(data), size = floor(.50*nrow(data)), replace = F)
train <- data[sample, ]
test <- data[-sample, ]
针对上面通过train_test_split划分,从而进行模型评估方式存在的弊端,提出Cross Validation 交叉验证。
Cross Validation:简言之,就是进行多次train_test_split划分;每次划分时,在不同的数据集上进行训练、测试评估,从而得出一个评价结果;如果是5折交叉验证,意思就是在原始数据集上,进行5次划分,每次划分进行一次训练、评估,最后得到5次划分后的评估结果,一般在这几次评估结果上取平均得到最后的 评分。k-fold cross-validation ,其中,k一般取5或10。
优点:
缺点:
如何确定K值?
Python 使用cross_val_score
或者KFold
cross_val_score
直接将整个交叉验证过程连接起来。
from sklearn.model_selection import cross_val_score
model = LogisticRegression()
scores = cross_val_score(model,X, y,cv=3) #cv:默认是3折交叉验证,可以修改cv=5,变成5折交叉验证。
print("Cross validation scores:{}".format(scores))
print("Mean cross validation score:{:2f}".format(scores.mean()))
KFold
可以显示具体的划分情况。
from sklearn.model_selection import KFold
kf = KFold(n_splits=5, random_state=None) # 5折
#显示具体划分情况
for train_index, test_index in kf.split(X):
print("Train:", train_index, "Validation:",test_index)
X_train, X_test = X[train_index], X[test_index]
y_train, y_test = y[train_index], y[test_index]
i = 1
for train_index, test_index in kf.split(X, y):
print('\n{} of kfold {}'.format(i,kf.n_splits))
X_train, X_test = X[train_index], X[test_index]
y_train, y_test = y[train_index], y[test_index]
model = LogisticRegression(random_state=1)
model.fit(X_train, y_train)
pred_test = model.predict(X_test)
score = metrics.accuracy_score(y_test, pred_test)
print('accuracy_score', score)
i += 1
#pred_test = model.predict(X_test)
pred = model.predict_proba(X_test)[:, 1]
R code
library(caret)
data(iris)
# Define train control for k fold cross validation
train_control <- trainControl(method="cv", number=10)
# Fit Naive Bayes Model
model <- train(Species~., data=iris, trControl=train_control, method="nb")
# Summarise Results
print(model)
分层是重新将数据排列组合,使得每一折都能比较好地代表整体。
比如下面这个例子:在一个二分类问题上,原始数据一共有两类(F和M),F:M的数据量比例大概是 1:3;划分了5折,每一折中F和M的比例都保持和原数据一致(1:3)。
这样就避免了随机划分可能产生的的情况,像是一折全是F,其他3折都是M。
下图是标准交叉验证和分层交叉验证的区别:
标准交叉验证(即K折交叉验证):直接将数据分成几折;
分层交叉验证:先将数据分类(class1,2,3),然后在每个类别中划分三折。
Python 使用cross_val_score
和StratifiedKFold
from sklearn.model_selection import StratifiedKFold
skf = StratifiedKFold(n_splits=5,shuffle=False,random_state=0)
# X is the feature set and y is the target
for train_index, test_index in skf.split(X,y):
print("Train:", train_index, "Validation:", test_index)
X_train, X_test = X[train_index], X[test_index]
y_train, y_test = y[train_index], y[test_index]
model = LogisticRegression()
scores = cross_val_score(model,X,y,cv=skf)
print("straitified cross validation scores:{}".format(scores))
print("Mean score of straitified cross validation:{:.2f}".format(scores.mean()))
** R code**
library(caret)
# Folds are created on the basis of target variable
folds <- createFolds(factor(data$target), k = 10, list = FALSE)
如果训练集不能很好地代表整个样本总体,分层交叉验证就没有意义了。这时候,可以使用重复交叉验证。
Python:RepeatedKFold
重复K折交叉验证
kf = RepeatedKFold(n_splits=5, n_repeats=2, random_state=None) #默认是5折
for train_index, test_index in kf.split(X):
print("Train:", train_index, "Validation:",test_index)
X_train, X_test = X[train_index], X[test_index]
y_train, y_test = y[train_index], y[test_index]
i = 1
for train_index, test_index in kf.split(X, y):
print('\n{} of kfold {}'.format(i,i))
X_train, X_test = X[train_index], X[test_index]
y_train, y_test = y[train_index], y[test_index]
model = LogisticRegression(random_state=1)
model.fit(X_train, y_train)
pred_test = model.predict(X_test)
score = metrics.accuracy_score(y_test, pred_test)
print('accuracy_score', score)
i += 1
#pred_test = model.predict(X_test)
pred = model.predict_proba(X_test)[:, 1]
在处理实际数据集时,经常会出现测试集和训练集截然不同的情况。因此,可能导致交叉验证结果不一致
在这种情况下,可以使用对抗验证法:总体思路是根据特征分布创建一个分类模型,以检查训练集和测试集之间的相似程度。
步骤:
优点: 使验证策略在训练集和测试集高度不同的情况下更加可靠。
缺点: 一旦测试集的分布发生变化,验证集可能不再适合评估模型。
#1. 将目标变量删除
train.drop(['target'], axis = 1, inplace = True)
#2. 创建新的目标变量:训练集为1;测试集为0
train['is_train'] = 1
test['is_train'] = 0
#3. 合并训练集和测试集
df = pd.concat([train, test], axis = 0)
#4. 使用新变量训练分类模型,并预测概率
y = df['is_train']; df.drop('is_train', axis = 1, inplace = True)
# Xgboost parameters
xgb_params = {
'learning_rate': 0.05,
'max_depth': 4,
'subsample': 0.9,
'colsample_bytree': 0.9,
'objective': 'binary:logistic',
'silent': 1,
'n_estimators':100,
'gamma':1,
'min_child_weight':4}
clf = xgb.XGBClassifier(**xgb_params, seed = 10)
probs = clf.predict_proba(x1)[:,1]
#5. 使用步骤4中计算的概率对序列集进行排序,并将前n%个样本/行作为验证集(n%是您希望保留在验证集中的序列集的分数)
new_df = pd.DataFrame({
'id':train.id, 'probs':probs})
new_df = new_df.sort_values(by = 'probs', ascending=False) # 30% validation set
val_set_ids = new_df.iloc[1:np.int(new_df.shape[0]*0.3),1]
#val_set_ids将为提供列训练集的ID,这些ID将构成与测试集最相似的验证集。
对于时间序列的数据集,不能像上述方法一样随机地划分验证集。为了解决时间序列的预测问题,可以尝试时间序列交叉验证:采用正向链接的策略,即按照时间顺序划分每一折的数据集。
假设我们有一个时间序列,表示在n年内消费者对某一产品的年需求量。
我们逐步选择新的训练集和测试集。我们从一个最小的训练集开始(这个训练集具有拟合模型所需的最少观测数)逐步地,每次都会更换训练集和测试集。在大多数情况下,不必一个个点向前移动,可以设置一次跨5个点/10个点。在回归问题中,可以使用以下代码执行交叉验证。
pythonTimeSeriesSplit
from sklearn.model_selection import TimeSeriesSplit
X = np.array([[1, 2], [3, 4], [1, 2], [3, 4]])
y = np.array([1, 2, 3, 4])
tscv = TimeSeriesSplit(n_splits=3)
for train_index, test_index in tscv.split(X):
print("Train:", train_index, "Validation:", val_index)
X_train, X_test = X[train_index], X[val_index]
y_train, y_test = y[train_index], y[val_index]
TRAIN: [0] TEST: [1]
TRAIN: [0 1] TEST: [2]
TRAIN: [0 1 2] TEST: [3]
R code
library(fpp)
library(forecast)
e <- tsCV(ts, Arima(x, order=c(2,0,0), h=1) #CV for arima model
sqrt(mean(e^2, na.rm=TRUE)) # RMSE
#h =1意味着我们只接受1步超前预报的误差。
#(h=4)4步前进误差如下图所示。如果想评估多步预测的模型,可以使用此选项。
参考链接:https://www.analyticsvidhya.com/blog/2018/05/improve-model-performance-cross-validation-in-python-r/
其他网上找到的有关文章:
关于时间序列的:https://zhuanlan.zhihu.com/p/99674163
关于对抗验证的:https://zhuanlan.zhihu.com/p/137580733