赛题名:室内用户运动时序数据分类
赛道:训练赛道
背景:随着数据量的不断积累,海量时序信息的处理需求日益凸显。作为时间序列数据分析中的重要任务之一,时间序列分类应用广泛且多样。时间序列分类旨在赋予序列某个离散标记。传统特征提取算法使用时间序列中的统计信息作为分类的依据。近年来,基于深度学习的时序分类取得了较大进展。基于端到端的特征提取方式,深度学习可以避免繁琐的人工特征设计。如何对时间序列中进行有效的分类,从繁芜丛杂的数据集中将具有某种特定形态的序列归属到同一个集合,对于学术研究及工业应用具有重要意义。
任务:基于上述实际需求以及深度学习的进展,本次训练赛旨在构建通用的时间序列分类算法。通过本赛题建立准确的时间序列分类模型,希望大家探索更为鲁棒的时序特征表述方法。
比赛链接:https://www.datafountain.cn/competitions/484
数据整理自网上公开数据集UCI(已脱敏),数据集涵盖2类不同时间序列,该类数据集广泛应用于时序分类的业务场景。
文件类别 | 文件名 | 文件内容 |
---|---|---|
训练集 | train.csv | 训练数据集标签文件,标签CLASS |
测试集 | test.csv | 测试数据集标签文件,无标签 |
字段说明 | 字段说明.xlsx | 训练集/测试集XXX个字段的具体说明 |
提交样例 | Ssample_submission.csv | 仅有两个字段ID\CLASS |
baseline讲解见前面的博客,这里在baseline的基础上加上svm的参数搜索。(其实加不加感觉差不多)
import pandas as pd
import numpy as np
from sklearn.model_selection import StratifiedKFold, KFold
from sklearn.svm import SVR
from sklearn.model_selection import GridSearchCV
train = pd.read_csv('train.csv')
test = pd.read_csv('test.csv')
#分离数据集
X_train_c = train.drop(['ID','CLASS'], axis=1).values
y_train_c = train['CLASS'].values
X_test_c = test.drop(['ID'], axis=1).values
nfold = 5
kf = KFold(n_splits=nfold, shuffle=True, random_state=2020)
prediction1 = np.zeros((len(X_test_c), ))
i = 0
for train_index, valid_index in kf.split(X_train_c, y_train_c):
print("\nFold {}".format(i + 1))
X_train, label_train = X_train_c[train_index],y_train_c[train_index]
X_valid, label_valid = X_train_c[valid_index],y_train_c[valid_index]
clf = GridSearchCV(SVR(), param_grid={
"kernel": ("linear", 'rbf', 'sigmoid'), "C": np.logspace(-3, 3, 7), "gamma": np.logspace(-3, 3, 7)})
#clf=SVR(kernel='rbf',C=1,gamma='scale')
clf.fit(X_train,label_train)
x1 = clf.predict(X_valid)
y1 = clf.predict(X_test_c)
prediction1 += ((y1)) / nfold
i += 1
#result1 = np.round(prediction1)
result1 = prediction1
为了为后续模型融合做准备,这里保留原始的预测结果result1,不进行四舍五入
当我们对时序数据进行特征提取时,常常用到的方法包括滑动平均、指数平滑等等,tsfresh库可以为我们提供几十或者几百个特征,包括时序能量等等,操作非常方便。下面是对本赛题进行特征提取的步骤和代码:
1.import包和数据读取
import pandas
import numpy as np
from sklearn.model_selection import cross_val_score
from sklearn.preprocessing import LabelEncoder
from sklearn.model_selection import StratifiedKFold
from sklearn.preprocessing import StandardScaler
from sklearn.pipeline import Pipeline
#数据加载
dataframe = pandas.read_csv("train.csv")
dataframe_test = pandas.read_csv("test.csv")
X = dataframe.iloc[:,1:241].astype(float)
Y = dataframe.iloc[:,241]
X_test = dataframe_test.iloc[:,1:241].astype(float)
2.对数据进行重构
对数据重构是为了方便之后特征的提取,格式可以从官方文档查看,id代表第几个训练样本,time是每一个训练样本的不同时间点,time_series是相应id的训练样本在time点的值。代码如下:
data_new = list()
for i in range(len(X)):
data_new.append(X.loc[i])
data_new = np.array(data_new).reshape(-1,1)
time_id = np.tile(np.array([i for i in range(0,240)]) , len(X)).reshape(-1,1)
id_index = np.array([i for i in range(0,210)]).repeat(240).reshape(-1,1)
data_format = pandas.DataFrame(np.concatenate([id_index,time_id,data_new],axis=1))
data_format.columns = ['id','time','time_series']
3.从训练数据里提取特征
下面利用extract_features和select_features从训练数据中提取和筛选数据
from tsfresh import extract_features
extracted_features = extract_features(data_format, column_id="id", column_sort="time")
#特征筛选
from tsfresh import select_features
from tsfresh.utilities.dataframe_functions import impute
impute(extracted_features)
features_filtered = select_features(extracted_features, Y)
from tsfresh import feature_extraction
kind_to_fc_parameters = feature_extraction.settings.from_columns(features_filtered)
4.对测试数据进行重构并且提取相同的特征
下面对测试数据进行处理,先按训练数据同样的方式构造数据,然后提取与训练数据相同的特征
#测试数据重构
data_new = list()
for i in range(len(X_test)):
data_new.append(X_test.loc[i])
data_new = np.array(data_new).reshape(-1,1)
time_id = np.tile(np.array([i for i in range(0,240)]) , len(X_test)).reshape(-1,1)
id_index = np.array([i for i in range(0,104)]).repeat(240).reshape(-1,1)
data_format_test = pandas.DataFrame(np.concatenate([id_index,time_id,data_new],axis=1))
data_format_test.columns = ['id','time','time_series']
features_filtered_test = extract_features(data_format_test, column_id="id", column_sort="time",
kind_to_fc_parameters =kind_to_fc_parameters)
features_filtered_test = features_filtered_test[features_filtered.columns]
可以通过下面的代码查看相应的特征:
features_filtered_test.info()
本文提取了67个特征,相比于原本的200多个点,已经大大降维了,下面展示了提取的所有特征
可以看出有些特征的名称是不符合lgb处理时的特征命名规范的,因此我们需要在后面训练lgb之前修改特征名称:
new_col = ['fea%s'%i for i in range(67)]
print(new_col)
features_filtered_test.columns = new_col
features_filtered.columns = new_col
下面根据上节提取的特征,使用lgb模型进行预测、
定义10折交叉验证和lgb参数等
import lightgbm as lgb
from sklearn.metrics import roc_auc_score
num_folds = 10
folds = StratifiedKFold(n_splits=num_folds, shuffle=True, random_state=2020)
test_result = np.zeros(len(features_filtered_test))
auc_score = 0
params = {
'num_leaves': int(16),
'objective': 'regression',
'max_depth': int(4),
'min_data_in_leaf': int(5),
'min_sum_hessian_in_leaf': int(0),
'learning_rate': 0.18,
'boosting': 'gbdt',
'feature_fraction': 0.8,
'bagging_freq': int(2),
'bagging_fraction': 1,
'bagging_seed': 8,
'lambda_l1': 0.01,
'lambda_l2': 0.01,
'metric': 'auc', ##评价函数选择
"random_state": 2020, #随机数种子,可以防止每次运行的结果不一致
}
模型的训练和预测
for fold_, (trn_idx, val_idx) in enumerate(folds.split(features_filtered, Y)):
print("Fold: ", fold_ + 1)
X_train, y_train = features_filtered.iloc[trn_idx], Y.iloc[trn_idx]
X_valid, y_valid = features_filtered.iloc[val_idx], Y.iloc[val_idx]
trn_data = lgb.Dataset(X_train, y_train)
val_data = lgb.Dataset(X_valid, y_valid, reference=trn_data)
clf = lgb.train(params,
trn_data,
10000,
valid_sets=val_data,
verbose_eval=50,
early_stopping_rounds=50)
y_pred = clf.predict(X_valid, num_iteration=clf.best_iteration)
auc = roc_auc_score(y_valid, y_pred)
print(auc)
auc_score += auc
preds = clf.predict(features_filtered_test, num_iteration=clf.best_iteration)
test_result += preds
预测结果保存在test_result中,输出auc检查
auc_score = auc_score / folds.n_splits
print("AUC score: ", auc_score)
test_result = test_result / folds.n_splits
Y_test = np.round(test_result)
另外,博主还尝试用svm和提取的特征进行分类,但是效果没有lgb理想。
使用上述的特征提取+lgb单模型预测,预测结果提交的得分是0.92+,比baseline中的0.83左右要高。
为什么进行模型融合能提高准确率?因为我们分别使用了svm分类和lgb回归,它们一个是结构风险最小化的模型,一个是经验风险最小化的模型,将两者融合是有助于准确率的提升的。这里模型的融合有一些技巧,本方案的思路很简单:
1)如果两个模型同时预测为正类或者负类则输出相应的预测结果(正类或者负类);
2)如果两个模型一个预测正类一个预测负类,那么看它们预测结果和0.5的差值,谁的差值大则以谁的预测结果为准,不过因为svm的准确率要低一些,因此在此基础上还设计了一个阈值(代码中使用的是0.1)
具体的过程见下面的代码:
ans = [0 for i in range(104)]
for i in range(104):
if result1[i]>0.5 and test_result[i]>0.5:
ans[i] = int(1)
if result1[i]<0.5 and test_result[i]<0.5:
ans[i] = int(0)
if result1[i]>0.5 and test_result[i]<0.5:
d1 = result1[i]-0.5
d2 = 0.5 - test_result[i]
if d1>d2+0.1:
ans[i] = int(1)
else:
ans[i] = int(0)
if result1[i]<0.5 and test_result[i]>0.5:
d1 = 0.5 - result1[i]
d2 = test_result[i] - 0.5
if d2+0.1>d1:
ans[i] = int(1)
else:
ans[i] = int(0)
最终的预测结果保存在ans中
还有很多模型融合的方法,这里不一一列举了,简单的用结果进行加权也可以做到上述的效果
将预测结果ans按提交格式写入csv文件
id_ = range(210,314)
df = pd.DataFrame({
'ID':id_,'CLASS':ans})
df.to_csv("baseline22.csv",index=False)