2020科大讯飞比赛Rank5解决方案

数据集是一种温度预测的数据集
人多力量大(@ocean,@fish,@羞涩中略带豪放,@小傻鱼,排名不分先后)我们使用xgb, lgb, cat, sgd, svrg, ridge, lstm, prophet, net,gcn等多模型进行研究,并进行了相关实验结果。简单介绍一下:xgb, lgb, cat都是树模型。sgd是随机梯度下降,svrg是随机方差减小的梯度下降算法,ridge是岭回归。lstm和prophet是时序模型,net是神经网络, gcn是图神经网络做的模型。当然在进行上述模型训练之前,我们进行了相关的特征特征工程,并针对不同的模型做不同的特征。

虽然我们使用很多模型,以及模型融合的方式来得到最终的结果,但是在所有模型中,sgd得到了一个最优的性能(训练快,训练时间上是完全胜过树模型的)。为了让大家对结果的复现以及方便审查我们的思路,我们对其它模型不做详细说明。

注:这里只公布最佳模型,等有时间继续接着写,其他模型会链接到队友博客。

一、数据处理

我们将初赛和复赛的训练集以及初赛的测试集进行合并,合并得到的结果是trainmerge.csv。初赛的训练集和测试集的shape(25497,5), 测试集的shape(406,5). data.iloc[:25903,:]可以在trainmerge.csv中得到初赛的数据集,其次为测试时的数据。

二、时间穿越问题

  1. 考虑到时间穿越问题,代码中的所有空缺值填充方式,我们都使用了下述方式
    data.fillna(method=‘ffill’)
  2. 对于给定两小时间隔的数据,但是提交需要按照分钟进行提交,那么我们进行了如下方式的特征填充:
    由于不能用当前时刻的特征预测当前时刻的温度,所以我们在时间轴上总体做数据平移操作,时间从1月31开始,依据初赛训练集和复赛训练集进行特征填充。依据trainmerge.csv进行特征工程,得到1月31日开始的数据,并且依次往后得到整个测试集的长度,目标从1月31日开始预测未来2月1日开始的数据集,最终的测试集表现为如下形式(数据为test_all.csv):
    2020科大讯飞比赛Rank5解决方案_第1张图片

三、最优化模型

虽然我们使用很多模型,以及模型融合的方式来得到最终的结果,但是在所有模型中,sgd得到了一个最优的性能。为了让大家对结果的复现以及方便审查我们的思路,我们对其它模型不做详细说明。

  1. 使用pd.read_csv()函数读取训练数据、测试数据以及提交的样例数据,样例数据用于提供每分钟的时间戳。
#初赛训练集和测试集+复赛训练集的合并
train_df = pd.read_csv('./data/trainmerge.csv')
#复赛测试集,也是我们需要上交的数据
test_df = pd.read_csv('./data/test.csv')
#任何一个复赛提交样例,目的是为了得到time
sub1 = pd.read_csv('./example.csv')
  1. 查看训练数据的temperature是否存在缺失值,对缺失值数据进行删除(.notnull()函数),对训练集和测试集中的缺失值用上一个非缺失值进行填充(.fillna(method=’ffill’)函数)。
train_df = train_df[train_df['temperature'].notnull()]
train_df = train_df.fillna(method='ffill')
test_df = test_df.fillna(method='ffill')
  1. 对训练集和测试集的列名进行重构。
train_df.columns = ['time', 'year', 'month', 'day', 'hour', 'min', 'sec', 'outdoorTemp', 'outdoorHum', 'outdoorAtmo', 'indoorHum', 'indoorAtmo', 'temperature']
test_df.columns = ['time', 'year', 'month', 'day', 'hour', 'min', 'sec', 'outdoorTemp', 'outdoorHum', 'outdoorAtmo', 'indoorHum', 'indoorAtmo']
  1. 使用自写的utils.deal_outliers函数对训练集中outdoorAtmo特征进行异常值处理,将异常值设置为nan值,使用上一个非空值对nan值填充。
strain_df['outdoorAtmo'] = utils.deal_outliers(train_df,'outdoorAtmo')
train_df = train_df.fillna(method='ffill')
test_df = test_df.fillna(method='ffill')

utils中的函数如下:

def deal_outliers(data, col_name):
    data = data[col_name]
    if col_name=='outdoorAtmo':
        data[data < 955] = np.nan
        data[data > 1000] = np.nan
    if col_name == 'indoorAtmo':
        data[data < 600] = np.nan
    return data
  1. 根据提交样例中的时间戳建立一个DataFrame类型的表格
sub = pd.DataFrame(sub1['time'])
  1. 测试集特征的填充,在时间穿越中已经给出,我们可以依据复赛的训练集做相关特征。这一块我们直接给出了填充的数据集,时间是从1月31日开始的(见模块二时间穿越问题的描述)。
  2. 随机选取训练集的一部分数据填充在测试集中增加噪声,提升模型的鲁棒性,也是为了避免穿越问题。合并训练集和测试集进行接下来的特征工程步骤。
a = pd.DataFrame()
a = train_df1.iloc[12728:, 7:12].reset_index()
a.pop('index')
test_df2.fillna(0, inplace=True)
section_a = 20
test_df2['outdoorTemp'].iloc[0:section_a] = a['outdoorTemp'].map(lambda name:name)
test_df2['outdoorHum'].iloc[0:section_a] = a['outdoorHum'].map(lambda name:name)
test_df2['outdoorAtmo'].iloc[0:section_a] = a['outdoorAtmo'].map(lambda name:name)
test_df2['indoorHum'].iloc[0:section_a] = a['indoorHum'].map(lambda name:name)
test_df2['indoorAtmo'].iloc[0:section_a] = a['indoorAtmo'].map(lambda name:name)
  1. 进行特征工程
    (1)基本聚合特征构建
    对五个基本特征(室外温度、室外湿度、室外气压、室内湿度、室内气压)构建聚合特征
mmr = lambda x: x.mean() - x.median()
mmd = lambda x: x.mean() / x.median()
group_feats = []
for f in tqdm(['outdoorTemp', 'outdoorHum', 'outdoorAtmo', 'indoorHum', 'indoorAtmo']):
    a=data_df.groupby(['month', 'day', 'hour'])
    data_df['MDH_{}_medi'.format(f)] = data_df.groupby(['month', 'day', 'hour '])[f].transform('median')
    data_df['MDH_{}_mean'.format(f)] = data_df.groupby(['month', 'day', 'hour'])[f].transform('mean')
    data_df['MDH_{}_max'.format(f)] = data_df.groupby(['month', 'day', 'hour'])[f].transform('max')
    data_df['MDH_{}_min'.format(f)] = data_df.groupby(['month', 'day', 'hour'])[f].transform('min')
    data_df['MDH_{}_std'.format(f)] = data_df.groupby(['month', 'day', 'hour'])[f].transform('std')
    data_df['MDH_{}_mmr'.format(f)] = data_df.groupby(['month', 'day', 'hour'])[f].transform(mmr)
    data_df['MDH_{}_mmd'.format(f)] = data_df.groupby(['month', 'day', 'hour'])[f].transform(mmd)

    data_df['MDH_{}_medimd'.format(f)] = data_df.groupby(['month', 'day']) [f]. tra nsfo rm('msedian')
    data_df['MDH_{}_meanmd'.format(f)] = data_df.groupby(['month','day'])[f].transform('mean')
    data_df['MDH_{}_maxmd'.format(f)] = data_df.groupby(['month', 'day'])[f].transform('max')
    data_df['MDH_{}_minmd'.format(f)] = data_df.groupby(['month', 'day'])[f].transform('min')
    data_df['MDH_{}_stdmd'.format(f)] = data_df.groupby(['month', 'day'])[f].transform('std')
    group_feats.append('MDH_{}_medi'.format(f))
    group_feats.append('MDH_{}_mean'.format(f))

(2)离散化特征
对五个基本特征(室外温度、室外湿度、室外气压、室内湿度、室内气压)构建离散化特征

for f in ['outdoorTemp', 'outdoorHum', 'outdoorAtmo', 'indoorHum', 'indoorAtmo']:
    data_df[f + '_20_bin'] = pd.cut(data_df[f], 20, duplicates='drop').apply(lambda x: x.left).astype(int)
    group_feats.append('{}_20_bin'.format(f))

(3)基本交叉特征

for f1 in tqdm(['outdoorTemp', 'outdoorHum', 'outdoorAtmo', 'indoorHum', 'indoorAtmo'] + group_feats):
    for f2 in ['outdoorTemp', 'outdoorHum', 'outdoorAtmo', 'indoorHum', 'indoorAtmo'] + group_feats:
        if f1 != f2:
            colname = '{}_{}_ratio'.format(f1, f2)
            data_df[colname] = data_df[f1].values / data_df[f2].values
            colname1 = '{}_{}_ratio1'.format(f1, f2)
            data_df[colname1] = data_df[f1].values - data_df[f2].values
  1. 对构建完特征的数据中的潜在nan值进行一个填充,使用上一个非缺失值进行填充(.fillna(method=’ffill’)函数)
data_df = data_df.fillna(method='ffill')
  1. 定义模型函数,将训练集拆分为训练集和验证集,80%的训练数据用于训练,20%用于验证。并设置sgd模型的参数。输出验证集的mse得分。
def single_model(clf, train_x, train_y, test_x, clf_name, class_num=1):
    nums = int(train_x.shape[0] * 0.80)
    print('MinMaxScaler...')
    for col in features:
        ss = MinMaxScaler()
        ss.fit(np.vstack([train_x[[col]].values, test_x[[col]].values]))
        train_x[col] = ss.transform(train_x[[col]].values).flatten()
        test_x[col] = ss.transform(test_x[[col]].values).flatten()
        train_x[col] = train_x[col].fillna(0)
        test_x[col] = test_x[col].fillna(0)
    trn_x, trn_y, val_x, val_y = train_x[:nums], train_y[:nums], train_x[nums:], train_y[nums:]
    if clf_name == "sgd":
        params = {
            'loss': 'squared_loss',
            'penalty': 'l2',
            'alpha': 0.00001,
            'random_state': 2020,
        }
        model = SGDRegressor(**params)
        model.fit(trn_x, trn_y)
        val_pred = model.predict(val_x)
        test_pred = model.predict(test_x)
    print("%s_mse_score:" % clf_name, mean_squared_error(val_y, val_pred))
    return val_pred, test_pred

11.去掉对训练无用的time(时间)、sec(秒)、temperature(结果温度)列。

drop_columns = ["time",  "sec", "temperature"]
features = train_df[:1].drop(drop_columns, axis=1).columns
  1. 因为时间顺序合并了初赛和复赛的数据集,防止验证集的百分之20只包括2020年的数据,为了所有年份的数据均能参与模型训练,我们对训练集的数据进行的打乱。
train_df = shuffle(train_df, random_state=2020)
  1. 构建训练模型的训练特征x_train和测试时的测试特征x_test和训练的标签y_train。观察训练集temperature与室外温度特征存在近似的线性关系,所以我们的标签使用的是temperature与室外温度的比值放缩标签范围。
y_train = train_df['temperature'].values / train_df['outdoorTemp'].values
  1. 调用sgd模型进行预测,输出结果。

四、软件包版本问题

pandas : '1.0.5'
sklearn: '0.22'
numpy: '1.16.0'
tqdm: '4.32.2'
utils: ' 源程序中已提供'
上述包装使用pip install 然后版本对应参数上述已经给出

五、注意事项与相关代码说明

官方要求我们直接使用官方数据集,考虑到数据集代码需要注释,所以做几点说明。
对于数据:

#使用初赛训练集,初赛测试集和复赛的训练集的合并
train_df = pd.read_csv('./data/trainmerge.csv')
#复赛的测试集
test_df = pd.read_csv('./data/test.csv')
#任何带有复赛提交样例时间的文件
sub1 = pd.read_csv('./example.csv')

注意:数据集读入使用相对路径,官方可以进行这个路径的修改,然后对初赛训练,初赛测试以及复赛训练的数据做拼接
对于数据:

test_df2 = pd.read_csv("./data/test_all.csv")

test_all.csv文件在代码文件中也已经给出,这个特征进行了时间上的平移操作,由于数据量较少,以及对训练集特征分析对应插值有很多手动操作,所以不具有任何代码说明。

你可能感兴趣的:(人工智能,机器学习,深度学习,神经网络,lstm)