使用的模型为tabnet,因此需要安装相应的模块 !pip install pytorch-tabnet
TabNet 是由 Google Research 提出的一个深度学习模型,旨在高效处理表格数据(Tabular Data)。TabNet 的设计目标是为表格数据提供一种新的、更高效、更易解释的处理方法。与传统的机器学习模型(如决策树、随机森林、XGBoost 等)和神经网络(如 MLP)相比,TabNet 在处理表格数据时具有显著优势,尤其是在复杂性和可解释性方面。
TabNet 的架构基于深度神经网络(DNN)和增强的自注意力机制,主要包括以下几个部分:
TabNet 的核心机制是通过 注意力机制 来选择重要的特征。这种注意力机制允许模型动态选择每个决策步骤中最重要的特征,从而进行有效的信息提取。
TabNet 在决策时引入了类似决策树的概念,它通过多个阶段的选择和调整来做出最终的预测。每个决策阶段都会依据当前阶段的目标选择不同的特征子集进行处理。
TabNet 通过稀疏激活机制提高了模型效率。稀疏激活是指只有少部分特征在每次计算中被激活,这样减少了计算量并提高了效率。
TabNet 采用掩码与路由机制(Masking and Routing),它通过学习如何将输入数据映射到一个更小的子集,以便进行后续的预测。 通过这种方式,TabNet 能够更高效地处理复杂数据并减少计算资源的消耗。
适用于表格数据:TabNet 特别适用于处理表格数据,而不像一些深度学习模型(如 CNN 或 RNN)那样主要面向图像或文本数据。 可解释性:通过其自注意力机制,TabNet 提供了比传统深度学习模型(如标准的多层感知机)更高的可解释性。 高效性:TabNet 在计算上相对较为高效,尤其在处理稀疏数据时能有效提升速度。 性能:在多个表格数据集上,TabNet 在准确性上经常超过传统的模型,如 XGBoost 和 LightGBM,尤其在特征选择和高维数据的处理上具有优势。
此挑战的目标是根据各种因素预测保险费。
本次比赛的数据集(训练和测试)是根据保险费预测数据集训练的深度学习模型生成的。特征分布与原始分布接近,但不完全相同。请随意使用原始数据集作为本次比赛的一部分,既可以探索差异,也可以看看将原始数据集纳入训练是否可以提高模型性能。 train.csv - 训练数据集;Premium Amount是目标变量 test.csv - 测试数据集;
数据的探索
import numpy as np
import pandas as pd
train_data = pd.read_csv('/kaggle/input/playground-series-s4e12/train.csv')
test_data = pd.read_csv('/kaggle/input/playground-series-s4e12/test.csv')
train_data.shape, test_data.shape
这里需要对数据集的数量,缺失值情况等进行查看,不多赘述这块的知识。
numeric_columns = ['Age', 'Annual Income', 'Number of Dependents', 'Health Score', 'Previous Claims', 'Vehicle Age', 'Credit Score', 'Insurance Duration']
train_data[numeric_columns] = train_data[numeric_columns].fillna(train_data[numeric_columns].mean())
categorical_columns = ['Marital Status', 'Occupation', 'Customer Feedback']
train_data[categorical_columns] = train_data[categorical_columns].fillna('unknown')
numeric_columns = ['Age', 'Annual Income', 'Number of Dependents', 'Health Score', 'Previous Claims', 'Vehicle Age', 'Credit Score', 'Insurance Duration']
test_data[numeric_columns] = test_data[numeric_columns].fillna(test_data[numeric_columns].mean())
categorical_columns = ['Marital Status', 'Occupation', 'Customer Feedback']
test_data[categorical_columns] = test_data[categorical_columns].fillna('unknown')
以上是对缺失值进行的数据,test_data和train_data都是要进行同样操作的处理,要保证test_data和train_data的数据维度一致,这样构建的模型在预测test_data时能够正常预测。
# 将 'Policy Start Date' 列转换为 datetime 类型
train_data['Policy Start Date'] = pd.to_datetime(train_data['Policy Start Date'])
# 提取年、月、日、时、分、秒
train_data['Year'] = train_data['Policy Start Date'].dt.year
train_data['Month'] = train_data['Policy Start Date'].dt.month
train_data['Day'] = train_data['Policy Start Date'].dt.day
train_data['Hour'] = train_data['Policy Start Date'].dt.hour
train_data['Minute'] = train_data['Policy Start Date'].dt.minute
train_data['Second'] = train_data['Policy Start Date'].dt.second
== 这里是对时间字段进行的操作,要转换为数值类型。test_data也要进行同样的操作。==
# 遍历所有object类型的字段,查看这些字段的unique()值
for column in train_data.select_dtypes(include=['object']).columns:
unique_values = train_data[column].unique()
print(f"Unique values in '{column}': {unique_values}")
查看object类型字段的值,以确定后面的编码方式。
# 创建教育水平的映射字典
education_mapping = {
'High School': 1,
"Bachelor's": 2,
"Master's": 3,
'PhD': 4
}
policy_type = {
'Basic':1,
'Comprehensive':2,
'Premium':3
}
customer = {
'unknown':0,
'Poor':1,
'Average':2,
'Good':3
}
exercise = {
'Rarely':1,
'Monthly':2,
'Weekly':3,
'Daily':4
}
# 使用映射字典进行序列编码
train_data['Education Level'] = train_data['Education Level'].map(education_mapping)
train_data['Policy Type'] = train_data['Policy Type'].map(policy_type)
train_data['Customer Feedback'] = train_data['Customer Feedback'].map(customer)
train_data['Exercise Frequency'] = train_data['Exercise Frequency'].map(exercise)
test_data['Education Level'] = test_data['Education Level'].map(education_mapping)
test_data['Policy Type'] = test_data['Policy Type'].map(policy_type)
test_data['Customer Feedback'] = test_data['Customer Feedback'].map(customer)
test_data['Exercise Frequency'] = test_data['Exercise Frequency'].map(exercise)
from sklearn.preprocessing import OneHotEncoder
import pandas as pd
# 需要处理的列
columns_to_encode = [
'Gender',
'Marital Status',
'Occupation',
'Location',
'Smoking Status',
'Property Type'
]
# 初始化one-hot编码器
encoder = OneHotEncoder(drop='first', sparse_output=False)
# 拆分编码器拟合和转换的步奏
train_encoded = encoder.fit_transform(train_data[columns_to_encode])
test_encoded = encoder.transform(test_data[columns_to_encode])
# 将编码后的数据转换为DataFrame,并与原始数据拼接
train_encoded_df = pd.DataFrame(train_encoded, columns=encoder.get_feature_names_out(columns_to_encode))
test_encoded_df = pd.DataFrame(test_encoded, columns=encoder.get_feature_names_out(columns_to_encode))
# 删除原始编码列,并在数据集中合并新编码的列
train_data_encoded = train_data.drop(columns=columns_to_encode).reset_index(drop=True)
test_data_encoded = test_data.drop(columns=columns_to_encode).reset_index(drop=True)
train_data_encoded = pd.concat([train_data_encoded, train_encoded_df], axis=1)
test_data_encoded = pd.concat([test_data_encoded, test_encoded_df], axis=1)
# 查看结果
train_data_encoded.shape, test_data_encoded.shape
以上就是编码的操作,对于有顺序的分类变量进行了映射,也可以使用序列编码,对于没有顺序的分类变量采用独热编码。
# 划分好目标变量和特征
x = train_data_encoded.drop(['id', 'Premium Amount','Policy Start Date'], axis=1)
x_test_encoded = test_data_encoded.drop(['id', 'Policy Start Date'], axis=1).values
y = train_data_encoded['Premium Amount']
from sklearn.model_selection import train_test_split
from sklearn.metrics import mean_squared_error
import torch
from pytorch_tabnet.tab_model import TabNetRegressor
# 检查 GPU 的数量
device = 'cuda' if torch.cuda.is_available() else 'cpu'
num_gpus = torch.cuda.device_count()
print(f"Using {num_gpus} GPUs on device: {device}")
# 数据准备:确保 X 和 y 是 NumPy 数组
X = train_data_encoded.drop(['id', 'Premium Amount', 'Policy Start Date'], axis=1).values
y = train_data_encoded['Premium Amount'].values
# 将 y 转换为二维数组
y = y.reshape(-1, 1)
# 划分训练集和测试集
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)
# 数据归一化(标准化)
from sklearn.preprocessing import StandardScaler
scaler_X = StandardScaler()
scaler_y = StandardScaler()
X_train = scaler_X.fit_transform(X_train)
X_test = scaler_X.transform(X_test)
y_train = scaler_y.fit_transform(y_train)
y_test = scaler_y.transform(y_test)
# 初始化 TabNet 模型
model = TabNetRegressor(device_name=device)
# 混合精度训练支持
scaler = torch.cuda.amp.GradScaler() if device == 'cuda' else None
# 多 GPU 分片数据
if num_gpus > 1:
print(f"Using {num_gpus} GPUs with manual data partitioning...")
# 将数据分成 num_gpus 份
split_size = len(X_train) // num_gpus
X_train_splits = [X_train[i * split_size:(i + 1) * split_size] for i in range(num_gpus)]
y_train_splits = [y_train[i * split_size:(i + 1) * split_size] for i in range(num_gpus)]
# 逐 GPU 分片训练
for i, (X_split, y_split) in enumerate(zip(X_train_splits, y_train_splits)):
print(f"Training on GPU {i}...")
current_device = f'cuda:{i}'
model = TabNetRegressor(device_name=current_device)
model.fit(
X_split, y_split,
eval_set=[(X_test, y_test)],
patience=20, # 提高 early stopping 容忍度
max_epochs=200, # 增加最大训练轮数
batch_size=8192, # 增大 batch size
virtual_batch_size=1024, # 降低虚拟 batch size,减少显存占用
num_workers=8, # 增加 worker 数
drop_last=True
)
else:
print("Training on a single GPU...")
# 单 GPU 训练
model.fit(
X_train, y_train,
eval_set=[(X_test, y_test)],
patience=20, # 提高 early stopping 容忍度
max_epochs=200, # 增加最大训练轮数
batch_size=8192, # 增大 batch size
virtual_batch_size=1024, # 降低虚拟 batch size,减少显存占用
num_workers=8, # 增加 worker 数
drop_last=True
)
# 在测试集上预测
print("Model evaluation...")
y_pred = model.predict(X_test)
# 反归一化预测结果
y_pred = scaler_y.inverse_transform(y_pred)
y_test = scaler_y.inverse_transform(y_test)
# 评估模型性能
mse = mean_squared_error(y_test, y_pred)
print(f"Test MSE: {mse}")
# 准备真实数据的特征
print("Preparing prediction on real data...")
real_X = x_test_encoded.values
real_X = scaler_X.transform(real_X) # 归一化特征数据
# 预测真实数据
real_y_pred = model.predict(real_X)
# 反归一化真实预测
real_y_pred = scaler_y.inverse_transform(real_y_pred)
# 获取真实数据的 id
id = test_data['id'].values
# 确保形状匹配
real_y_pred = real_y_pred.flatten()
if len(id) != len(real_y_pred):
raise ValueError(f"Length mismatch: id ({len(id)}) and predictions ({len(real_y_pred)})")
# 创建结果 DataFrame
result_df = pd.DataFrame({
'id': id,
'Premium Amount': real_y_pred
})
# 保存结果为 CSV 文件
result_df.to_csv('predicted_results.csv', index=False)
print("Prediction results saved to 'predicted_results.csv'")
# 显示结果
print(result_df.head())
本次竞赛的数据集训练数据有120w,因此考虑要使用Gpu进行训练,其运行速度更快。
以上就是损失图像,可以看到效果还行。 batch_size和virtual_batch_size的含义
batch_size 是每次处理的数据量。 virtual_batch_size 通过分批次累积梯度来模拟更大的 batch_size,帮助节省显存。
处理表格数据的神经网络比较少,除了上述这个,其实卷积神经网络也是可以处理时间序列数据,只需将卷积核进行改变即可。