1. 题目概述

2026年Kaggle Playground Series -Season 6 Episode 4
竞赛地址:https://www.kaggle.com/competitions/playground-series-s6e4/overview
目标:预测灌溉需求
评价:提交内容的评估基于预测类别与观察目标之间的平衡准确性
本文章代码已开源
github:https://github.com/Gxinglan/Kaggle-Predicting-Irrigation-Need
kaggle:https://www.kaggle.com/code/starxinglan/predicting-irrigation-need-2-3

1.1 提交要求

对于测试集中的每个目标,你必须预测一个类别标签(低、中、高)。文件应包含一个头部,格式如下:id,Irrigation_Need

id Irrigation_Need
630000 Low
630001 High
630002 Low

1.2 训练集 train.csv

1.2.1 训练集数据结构

该数据集包含多维度的农业环境与作物生长特征。字段说明及部分样本示例:

字段名称 字段说明 示例数据
id 样本唯一标识 0, 1, 2 …
Soil_Type 土壤类型 Loamy, Clay, Sandy
Soil_pH 土壤酸碱度 4.92, 7.08, 5.69
Soil_Moisture 土壤湿度 (%) 32.58, 56.61, 27.71
Organic_Carbon 有机碳含量 1.01, 0.44, 0.81
Electrical_Conductivity 电导率 3.05, 2.00, 2.83
Temperature_C 温度 (°C) 15.01, 22.92, 26.97
Humidity 湿度 (%) 50.61, 67.86, 92.22
Rainfall_mm 降雨量 (mm) 725.99, 985.66, 2201.70
Sunlight_Hours 日照时长 (小时) 5.90, 6.98, 6.05
Wind_Speed_kmh 风速 (km/h) 16.79, 3.39, 3.85
Crop_Type 作物类型 Sugarcane, Wheat, Rice
Crop_Growth_Stage 作物生长阶段 Sowing, Vegetative, Reproductive
Season 季节 Zaid, Kharif, Rabi
Irrigation_Type 灌溉类型 Drip, Rainfed, Sprinkler
Water_Source 水源 Rainwater, River, Reservoir
Field_Area_hectare 地块面积 (公顷) 0.82, 5.27, 8.24
Mulching_Used 是否使用覆盖物 Yes, No
Previous_Irrigation_mm 上次灌溉量 (mm) 112.16, 47.16, 110.38
Region 区域 East, South, North
Irrigation_Need 灌溉需求标签 (目标值) Low, Medium, High

1.2.2 部分样本数据实例

id Soil_Type Soil_pH Soil_Moisture Organic_Carbon Electrical_Conductivity Temperature_C Humidity Rainfall_mm Sunlight_Hours Wind_Speed_kmh Crop_Type Crop_Growth_Stage Season Irrigation_Type Water_Source Field_Area_hectare Mulching_Used Previous_Irrigation_mm Region Irrigation_Need
0 Loamy 4.92 32.58 1.01 3.05 15.01 50.61 725.99 5.90 16.79 Sugarcane Sowing Zaid Drip Rainwater 0.82 No 112.16 East Low
1 Clay 7.08 56.61 0.44 2.00 22.92 67.86 985.66 6.98 3.39 Wheat Vegetative Kharif Rainfed River 5.27 Yes 47.16 South Low
2 Clay 5.69 27.71 0.81 2.83 26.97 92.22 2201.70 6.05 3.85 Rice Vegetative Kharif Sprinkler Reservoir 8.24 Yes 110.38 North Low

1.3 测试集 test.csv

测试集特征结构与训练集基本一致,不含标签 Irrigation_Need。我们需要做的就是对Irrigation_Need进行预测。

1.3.1 部分样本数据实例

id Soil_Type Soil_pH Soil_Moisture Organic_Carbon Electrical_Conductivity Temperature_C Humidity Rainfall_mm Sunlight_Hours Wind_Speed_kmh Crop_Type Crop_Growth_Stage Season Irrigation_Type Water_Source Field_Area_hectare Mulching_Used Previous_Irrigation_mm Region
630000 Silt 6.36 26.19 0.59 2.81 17.83 30.24 1533.38 5.4 3 Maize Sowing Rabi Canal River 13.59 Yes 47.48 West
630001 Clay 5.87 9.88 1.18 3.26 21.18 78.07 576.05 7.22 15.88 Cotton Sowing Rabi Drip Reservoir 6.12 Yes 56.43 South
630002 Sandy 6.22 26.55 0.96 0.85 26.87 60.35 545.3 9.43 2.63 Wheat Sowing Kharif Sprinkler Reservoir 3.11 Yes 20 East

2. 解题代码讲解

本方案采用CatBoostClassifier模型,结合农业领域知识构建有效特征,使用7 折分层交叉验证保证训练稳定,通过类别权重处理样本不均衡问题,最终对多折模型的预测概率进行融合平均,得到稳健的预测结果并生成提交文件。
采用7折目前跑出来的结果是最好的,以下是在各折下跑的分数结果。
在5折时分数跑到了0.96805
在6折时分数跑到了0.96820
在7折时分数跑到了0.96907
在8折时分数跑到了0.96749
在10折时分数跑到了0.96728

2.1 CatBoost

CatBoost是俄罗斯的搜索巨头Yandex在2017年开源的机器学习库,是Boosting族算法的一种。CatBoost和XGBoost、LightGBM并称为GBDT的三大主流神器,都是在GBDT算法框架下的一种改进实现。其中CatBoostClassifier 是CatBoost库里专门做分类任务的模型。
其有三个主要特点:

  • 自动处理类别特征:无需繁琐的特征编码,直接原始数据
  • 有序目标统计:有效防止目标泄露(Target Leakage)
  • 对称决策树:训练更快更不容易拟合

因此,CatBoost 尤其适用于包含大量类别特征、数值与类别特征存在复杂交互、需要快速搭建高质量基线模型的表格数据场景。

2.1.1 有序目标统计:比 One-Hot 更智能的类别特征处理策略

在类别型特征的处理中,传统方法存在固有局限:独热编码(One-Hot Encoding)易引发维度灾难,导致特征空间高维稀疏;标签编码(Label Encoding)则会人为引入虚假的顺序关系,破坏特征的语义独立性。CatBoost 通过有序目标统计(Ordered Target Statistics)机制,解决了这一矛盾。

核心原理

有序目标统计的核心逻辑基于时序化的目标均值编码,具体流程如下:

  1. 样本随机排序:训练前对全体样本进行随机重排,构建时序化的样本序列。
  2. 历史信息编码:对于第 i i i 个样本的类别特征值,仅使用前 i − 1 i-1 i1 个样本中相同类别的目标值均值作为编码结果。
  3. 先验平滑处理:引入先验概率对编码结果进行平滑,缓解小样本类别统计不稳定的问题。

其优势在于优势

  • 避免了未来信息的泄漏
  • 保留了类别特征的统计特性

2.2.2 对称树: 决策树中的标准化结构

与 XGBoost 等传统框架采用的非对称决策树不同,CatBoost 使用对称树(Oblivious Trees) 结构。该结构的核心特点是,树的每一层所有节点均使用相同的特征与阈值进行分裂,结构规整且统一。
这种结构有以下优点:

  • 训练速度快:可并行计算所有叶节点的分裂
  • 内存效率高:只需要存储每层的分裂特征和阈值
  • 正则化结果好:限制了模型的复杂度

但对称树也有其局限性,当特征交互非常复杂时,可能不如传统决策树灵活。

CatBoost的主要算法原理可以参考以下两篇论文:

  • Anna Veronika Dorogush, Andrey Gulin, Gleb Gusev, Nikita Kazeev, Liudmila Ostroumova Prokhorenkova, Aleksandr Vorobev “Fighting biases with dynamic boosting”. arXiv:1706.09516, 2017
  • Anna Veronika Dorogush, Vasily Ershov, Andrey Gulin “CatBoost: gradient boosting with categorical features support”. Workshop on ML Systems at NIPS 2017

2.2 代码

2.2.1 导入库

import os
import pandas as pd
import numpy as np
from sklearn.model_selection import StratifiedKFold
from sklearn.metrics import f1_score, classification_report
from sklearn.utils.class_weight import compute_class_weight
from catboost import CatBoostClassifier
  • import os:导入操作系统工具,用于文件路径管理与文件读写。
  • import pandas as pd:数据处理核心库,用于读取 CSV、表格数据操作。
  • import numpy as np:数值计算库,用于数组运算、概率融合等。
  • StratifiedKFold:分层 K 折交叉验证,保证每一折数据的类别分布与原数据一致。
  • f1_score, classification_report:模型评估指标,用于计算 F1 分数与输出分类报告。
  • compute_class_weight:计算类别平衡权重,解决样本分布不均衡问题。
  • CatBoostClassifier:CatBoost 分类模型。

2.2.2 设置文件路径与运行模式

# 配置路径
TRAIN_PATH = "./train.csv"
TEST_PATH = "./test.csv"
SUBMISSION_OUTPUT_PATH = "./submission_final.csv"

# 训练模式:GPU or CPU
TRAINING_MODE = "GPU"
  • TRAIN_PATH:训练集文件路径,指定代码读取训练数据的位置。
  • TEST_PATH:测试集文件路径。
  • SUBMISSION_OUTPUT_PATH:最终提交文件保存路径。
  • TRAINING_MODE:训练模式,GPU或者CPU。

2.2.3 读取数据集与测试集数据

# 读取数据
train = pd.read_csv(TRAIN_PATH)
test = pd.read_csv(TEST_PATH)

# 保存测试集 ID,用于最后提交
submission = test[["id"]].copy()
  • pd.read_csv(TRAIN_PATH):从指定路径读取训练集数据,生成 DataFrame 数据结构。
  • pd.read_csv(TEST_PATH):读取测试集数据,用于后续预测。
  • submission = test[["id"]].copy():单独复制测试集的 id 列,用于最终生成提交文件。

2.2.4 标签处理

# 标签映射
label_map = {"Low": 0, "Medium": 1, "High": 2}
train["Irrigation_Need"] = train["Irrigation_Need"].map(label_map)

# 划分特征和标签
X = train.drop(["id", "Irrigation_Need"], axis=1)
y = train["Irrigation_Need"]
X_test = test.drop(["id"], axis=1)
  • label_map:将标签文本(Low/Medium/High)映射为模型可处理的数字(0/1/2)。
  • map():批量替换训练集标签为数值形式。
  • X:训练集特征,剔除 id 与标签列。
  • y:训练集标签,即需要预测的灌溉需求等级。
  • X_test:测试集特征,仅剔除 id 列。

2.2.5 特征工程

# 构造关键特征
def create_features(df):
    # 1. 参考作物蒸散量
    df["ET0"] = df["Temperature_C"] * df["Wind_Speed_kmh"] * df["Sunlight_Hours"] / df["Humidity"]
    # 2. 水平衡:降水 + 上次灌溉 - 蒸发
    df["Water_Balance"] = df["Rainfall_mm"] + df["Previous_Irrigation_mm"] - df["ET0"]
    # 3. 水分胁迫指数
    df["Moisture_Stress"] = df["Temperature_C"] * (100 - df["Humidity"]) / df["Soil_Moisture"]
    # 4. 组合特征
    df["Crop_Growth"] = df["Crop_Type"] + "_" + df["Crop_Growth_Stage"]
    df["Region_Season"] = df["Region"] + "_" + df["Season"]
    return df

# 对训练集和测试集都应用特征工程
X = create_features(X)
X_test = create_features(X_test)
  • create_features(df):自定义特征工程函数,用于批量构造新特征。
  • df["ET0"]:构造蒸散量特征,反映水分蒸发强度。
    公式:温度 × 风速 × 日照时长 / 湿度
  • df["Water_Balance"]:构造水平衡特征,衡量土壤水分盈亏状态。
    公式:降雨量 + 上次灌溉量 − 蒸散量
  • df["Moisture_Stress"]:构造水分胁迫指数,表征作物缺水压力。
    公式:温度 × (100−湿度) / 土壤湿度
  • df["Crop_Growth"]:作物类型与生长阶段交叉组合特征。
  • df["Region_Season"]:地区与季节交叉组合特征。
  • X = create_features(X):对训练集执行特征工程。
  • X_test = create_features(X_test):对测试集执行相同特征工程。

2.2.5 识别并处理类别特征

# 类别型特征列名
cat_features = [
    "Soil_Type", "Crop_Type", "Crop_Growth_Stage", "Season",
    "Irrigation_Type", "Water_Source", "Mulching_Used", "Region",
    "Crop_Growth", "Region_Season"
]

# 确保类别特征是字符串格式
for col in cat_features:
    X[col] = X[col].astype(str)
    X_test[col] = X_test[col].astype(str)
  • cat_features:定义所有类别型特征列表,供 CatBoost 自动识别处理。
  • for col in cat_features:遍历所有类别特征,统一转换数据类型。
  • X[col] = X[col].astype(str):将训练集类别特征转为字符串格式。
  • X_test[col] = X_test[col].astype(str):将测试集类别特征转为字符串格式。

2.2.7 计算类别权重

# 计算类别权重
weights = compute_class_weight(
    class_weight="balanced", classes=np.unique(y), y=y
)
class_weights = dict(zip(np.unique(y), weights))
  • compute_class_weight():根据样本分布自动计算类别平衡权重。
  • class_weight="balanced":启用平衡模式,自动修正样本分布不均问题。
  • np.unique(y):获取数据集中所有不重复的标签类别。
  • class_weights:存储类别与对应权重的字典,供模型训练时使用。

2.2.8 设置7折分层交叉验证

# 7折交叉验证
skf = StratifiedKFold(n_splits=7, shuffle=True, random_state=42)
test_pred_probs = []
  • StratifiedKFold:分层 K 折交叉验证,保持每折数据类别分布一致。
  • n_splits=8:将数据集划分为 7 份,7折交叉验证。
  • shuffle=True:训练前打乱数据顺序,提升模型泛化能力。
  • random_state=42:固定随机种子,保证实验可复现。
  • test_pred_probs:空列表,用于存储每一折模型的预测概率。

2.2.9 训练模型

for fold, (train_idx, val_idx) in enumerate(skf.split(X, y)):
    print(f"\n========== Fold {fold + 1} ==========")

    # 划分训练集与验证集
    X_train, X_val = X.iloc[train_idx], X.iloc[val_idx]
    y_train, y_val = y.iloc[train_idx], y.iloc[val_idx]

    # 定义 CatBoost 模型
    model = CatBoostClassifier(
        iterations=1000,
        learning_rate=0.1,
        depth=6,
        eval_metric="TotalF1",
        early_stopping_rounds=200,
        class_weights=class_weights,
        task_type=TRAINING_MODE,
        random_state=42,
        verbose=100
    )

    # 训练模型
    model.fit(
        X_train, y_train,
        eval_set=(X_val, y_val),
        cat_features=cat_features,
        use_best_model=True
    )

    # 验证集评估
    val_pred = model.predict(X_val)
    f1 = f1_score(y_val, val_pred, average="macro")
    print(f"Fold {fold+1} F1 Score: {f1:.4f}")
    print(classification_report(y_val, val_pred))

    # 保存测试集预测概率
    test_pred_probs.append(model.predict_proba(X_test))
  • for fold, (train_idx, val_idx) in enumerate(skf.split(X, y)):按折循环,拆分训练集与验证集索引。
  • X_train, X_val:按索引划分训练集与验证集特征。
  • y_train, y_val:按索引划分训练集与验证集标签。
  • CatBoostClassifier():初始化 CatBoost 分类模型。
  • iterations=1000:最大迭代次数(树的数量)。
  • learning_rate=0.1:模型学习率。
  • depth=6:决策树最大深度。
  • eval_metric="TotalF1":以 F1 分数作为评估指标。
  • early_stopping_rounds=200:早停策略,防止过拟合。
  • class_weights=class_weights:传入类别平衡权重。
  • task_type=TRAINING_MODE:指定使用 CPU / GPU 训练。
  • random_state=42:固定随机种子,保证可复现。
  • verbose=100:每 100 轮输出一次训练日志。
  • model.fit():在训练集上训练,在验证集上验证。
  • cat_features=cat_features:指定类别特征,让模型自动处理。
  • use_best_model=True:使用最优迭代轮数的模型。
  • model.predict():对验证集进行预测。
  • f1_score():计算验证集宏观 F1 分数。
  • classification_report():输出完整分类评估报告。
  • model.predict_proba():对测试集预测概率。
  • test_pred_probs.append():将每折预测概率存入列表。

2.2.10 多模型预测结果融合

# 对 7 个模型的预测概率求平均
avg_pred_probs = np.mean(test_pred_probs, axis=0)
# 取概率最大的类别作为最终预测
predictions = np.argmax(avg_pred_probs, axis=1)
  • np.mean(test_pred_probs, axis=0):对8折模型的预测概率按列求平均,提升结果稳定性。
  • np.argmax(avg_pred_probs, axis=1):取每行概率最大的值对应的类别,生成最终预测标签。
  • avg_pred_probs:存储多模型融合后的平均预测概率。
  • predictions:存储最终的类别预测结果。

2.2.11 生成提交文件

# 把数字转回标签
label_map_inv = {0: "Low", 1: "Medium", 2: "High"}
submission["Irrigation_Need"] = predictions
submission["Irrigation_Need"] = submission["Irrigation_Need"].map(label_map_inv)

# 保存文件
submission.to_csv(SUBMISSION_OUTPUT_PATH, index=False)
print(f"\n提交文件已保存至:{SUBMISSION_OUTPUT_PATH}")
  • label_map_inv:将预测的数字标签映射回原始文本标签。
  • submission["Irrigation_Need"]:将预测结果存入提交文件。
  • map(label_map_inv):批量转换数字为文本标签。
  • submission.to_csv():将结果保存为CSV格式提交文件。
  • index=False:不保存行索引,符合竞赛提交格式要求。
  • print():输出文件保存路径,方便查看结果。

注:水平有限,若存在错误或可优化之处,欢迎各位批评指正。

Logo

AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。

更多推荐