|
|
|
|
|
|
|
|
|
|
|
下面代码中stratify参数会保证测试集中标签y的各个值至少存在一个吗?
```
from sklearn.model_selection import train_test_split
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=test_size, random_state=random_state, stratify=y)
```
尽量但不保证
`stratify=y` 的作用是按 **原始标签的比例** 把样本分到训练集和测试集,
但它 **不承诺** 每个标签在测试集里至少出现一次。
是否“至少有一个”取决于两个因素:
1. 该标签在总体里的 **出现次数**(频数)。
2. 测试集的 **绝对大小**(`test_size` 乘以总样本量)。
举例说明:
- 总体 1000 条样本,某罕见标签只占 5 条(0.5%)。
若 `test_size=0.2`,则测试集理论容量 200 条,该标签期望只有 1 条。
一旦随机划分时这 5 条里有一条被分到训练集,测试集就可能 **0 条**,出现空类。
此时 `StratifiedShuffleSplit` 会抛错:`ValueError: The least populated class in y has only X members, which is less than n_splits=1.`
但 `train_test_split` 默认用的是 `StratifiedShuffleSplit(n_splits=1)`,在 **1 次划分** 场景下 **不会提前检查**,于是直接给出空类,**不保证至少一个**。
- 如果该标签在总体里有 10 条以上,且 `test_size` 不是极小,那么通常每个类在测试集都会至少分到 1 条,但这只是“大概率”,而非“硬保证”。
结论:
`stratify=y` **不会** 强行保证测试集里每个标签都至少出现一次;
它只保证 **比例一致**,当某类样本太少时,测试集仍可能出现空类。
如果你需要“每类至少一条”的硬性约束,必须自己额外检查或采用分层采样后手动补样。
|
|
from sklearn.model_selection import StratifiedKFold
import numpy as np
# 示例数据
X = np.array([[1, 2], [3, 4], [5, 6], [7, 8], [9, 10], [11, 12]])
y = np.array([0, 0, 0, 1, 1, 1]) # 3个类别0,3个类别1
# 创建 StratifiedKFold 对象
skf = StratifiedKFold(n_splits=3, shuffle=True, random_state=42)
# 进行交叉验证划分
for fold, (train_index, test_index) in enumerate(skf.split(X, y)):
print(f"Fold {fold + 1}:")
print(f" 训练集索引: {train_index}")
print(f" 测试集索引: {test_index}")
print(f" 训练集标签分布: {y[train_index]}")
print(f" 测试集标签分布: {y[test_index]}")
print("-" * 40)
Fold 1:
训练集索引: [0 2 3 5]
测试集索引: [1 4]
训练集标签分布: [0 0 1 1]
测试集标签分布: [0 1]
Fold 2:
训练集索引: [0 1 4 5]
测试集索引: [2 3]
训练集标签分布: [0 0 1 1]
测试集标签分布: [0 1]
Fold 3:
训练集索引: [1 2 3 4]
测试集索引: [0 5]
训练集标签分布: [0 0 1 1]
测试集标签分布: [0 1]
|
|
from sklearn.model_selection import KFold
# 普通 KFold
kf = KFold(n_splits=3, shuffle=True, random_state=42)
print("普通 KFold 结果:")
for fold, (train_index, test_index) in enumerate(kf.split(X, y)):
print(f"Fold {fold + 1}: 测试集标签 {y[test_index]}")
print("\nStratifiedKFold 结果:")
# StratifiedKFold
skf = StratifiedKFold(n_splits=3, shuffle=True, random_state=42)
for fold, (train_index, test_index) in enumerate(skf.split(X, y)):
print(f"Fold {fold + 1}: 测试集标签 {y[test_index]}")
普通 KFold 结果:
Fold 1: 测试集标签 [0 1]
Fold 2: 测试集标签 [0 1]
Fold 3: 测试集标签 [0 1] # 可能不平衡
StratifiedKFold 结果:
Fold 1: 测试集标签 [0 1] # 始终保持平衡
Fold 2: 测试集标签 [0 1]
Fold 3: 测试集标签 [0 1]
|
|
from collections import Counter
# 创建不平衡数据集
X = np.random.randn(100, 5)
y = np.array([0] * 90 + [1] * 10) # 90个类别0,10个类别1
print("原始数据分布:", Counter(y))
skf = StratifiedKFold(n_splits=5, shuffle=True, random_state=42)
for fold, (train_index, test_index) in enumerate(skf.split(X, y)):
train_dist = Counter(y[train_index])
test_dist = Counter(y[test_index])
print(f"Fold {fold + 1}:")
print(f" 训练集 - 类别0: {train_dist[0]}, 类别1: {train_dist[1]}")
print(f" 测试集 - 类别0: {test_dist[0]}, 类别1: {test_dist[1]}")
print(f" 训练集比例 - 类别1: {train_dist[1]/len(train_index):.2%}")
print(f" 测试集比例 - 类别1: {test_dist[1]/len(test_index):.2%}")
print("-" * 50)
|
|
from sklearn.ensemble import RandomForestClassifier
from sklearn.metrics import accuracy_score
from sklearn.datasets import make_classification
# 创建示例数据集
X, y = make_classification(n_samples=1000, n_classes=3,
weights=[0.7, 0.2, 0.1], random_state=42)
# 使用 StratifiedKFold 进行交叉验证
skf = StratifiedKFold(n_splits=5, shuffle=True, random_state=42)
scores = []
for fold, (train_index, test_index) in enumerate(skf.split(X, y)):
X_train, X_test = X[train_index], X[test_index]
y_train, y_test = y[train_index], y[test_index]
# 训练模型
model = RandomForestClassifier(random_state=42)
model.fit(X_train, y_train)
# 预测并评估
y_pred = model.predict(X_test)
accuracy = accuracy_score(y_test, y_pred)
scores.append(accuracy)
print(f"Fold {fold + 1} - 准确率: {accuracy:.4f}")
print(f"\n平均准确率: {np.mean(scores):.4f} (±{np.std(scores):.4f})")
主要优势
保持类别比例:每个折叠中各类别比例与原始数据一致
减少偏差:避免某些折叠中缺失重要类别
更可靠的评估:对于不平衡数据,评估结果更加稳定可靠
适合小数据集:确保每个类别在训练和测试中都有代表性样本
适用场景
分类问题,特别是多分类问题
不平衡数据集
需要稳定评估模型性能的场景
小样本数据集
StratifiedKFold 是处理分类问题时首选的交叉验证方法,能够提供更加可靠和稳定的性能评估。
|