作者:杰少,南京大学硕士

本文基于 2021 “AI Earth”人工智能创新挑战赛-AI助力精准气象和海洋预测,梳理了时间序列赛事的实践和分析过程,提供了完整baseline方案。

时间序列(或称动态数列)是指将同一统计指标的数值按其发生的时间先后顺序排列而成的数列。时间序列分析的主要目的是根据已有的历史数据对未来进行预测。

一、赛题背景

赛题简介

比赛地址:https://tianchi.aliyun.com/competition/entrance/531871/information(复制粘贴或文末读原文

本次赛题是一个时间序列预测问题。基于历史气候观测和模式模拟数据,利用T时刻过去12个月(包含T时刻)的时空序列(气象因子),构建预测ENSO的深度学习模型,预测未来1-24个月的Nino3.4指数,如下图所示:

背景数据描述

1. 数据简介

本次比赛使用的数据包括CMIP5/6模式的历史模拟数据和美国SODA模式重建的近100多年历史观测同化数据。每个样本包含以下气象及时空变量:海表温度异常(SST),热含量异常(T300),纬向风异常(Ua),经向风异常(Va),数据维度为(year,month,lat,lon)。对于训练数据提供对应月份的Nino3.4 index标签数据。

2. 训练数据标签说明

标签数据为Nino3.4 SST异常指数,数据维度为(year,month)。

CMIP(SODA)_train.nc对应的标签数据当前时刻Nino3.4 SST异常指数的三个月滑动平均值,因此数据维度与维度介绍同训练数据一致。

注:三个月滑动平均值为当前月与未来两个月的平均值。

3. 测试数据说明

测试用的初始场(输入)数据为国际多个海洋资料同化结果提供的随机抽取的n段12个时间序列,数据格式采用NPY格式保存,维度为(12,lat,lon, 4),12为t时刻及过去11个时刻,4为预测因子,并按照SST,T300,Ua,Va的顺序存放。

测试集文件序列的命名规则:test_编号_起始月份_终止月份.npy,如test_00001_01_12_.npy。

评估指标

评分细则说明:根据所提供的n个测试数据,对模型进行测试,得到n组未来1-24个月的序列选取对应预测时效的n个数据与标签值进行计算相关系数和均方根误差,如下图所示。并计算得分。计算公式为:

其中,

而:

二、线下数据转换

将数据转化为我们所熟悉的形式,每个人的风格不一样,此处可以作为如何将nc文件转化为csv等文件

## 工具包导入&数据读取
### 工具包导入'''
安装工具
# !pip install netCDF4
'''
import pandas as pd
import numpy  as np
import tensorflow as tf
from tensorflow.keras.optimizers import Adam
import matplotlib.pyplot as plt
import scipy
from netCDF4 import Dataset
import netCDF4 as nc
import gc
%matplotlib inline

1. 数据读取

1.1 SODA_label处理

  1. 标签含义
标签数据为Nino3.4 SST异常指数,数据维度为(year,month)。
CMIP(SODA)_train.nc对应的标签数据当前时刻Nino3.4 SST异常指数的三个月滑动平均值,因此数据维度与维度介绍同训练数据一致
注:三个月滑动平均值为当前月与未来两个月的平均值。
  1. 将标签转化为我们熟悉的pandas形式
label_path       = './data/SODA_label.nc'
label_trans_path = './data/'
nc_label         = Dataset(label_path,'r')years            = np.array(nc_label['year'][:])
months           = np.array(nc_label['month'][:])year_month_index = []
vs               = []
for i,year in enumerate(years):for j,month in enumerate(months):year_month_index.append('year_{}_month_{}'.format(year,month))vs.append(np.array(nc_label['nino'][i,j]))df_SODA_label               = pd.DataFrame({'year_month':year_month_index})
df_SODA_label['year_month'] = year_month_index
df_SODA_label['label']      = vsdf_SODA_label.to_csv(label_trans_path + 'df_SODA_label.csv',index = None)
df_label.head()

2. 数据格式转化

2.1 SODA_train处理

SODA_train.nc中[0,0:36,:,:]为第1-第3年逐月的历史观测数据;SODA_train.nc中[1,0:36,:,:]为第2-第4年逐月的历史观测数据;
…,
SODA_train.nc中[99,0:36,:,:]为第100-102年逐月的历史观测数据。
SODA_path        = './data/SODA_train.nc'
nc_SODA          = Dataset(SODA_path,'r')

自定义抽取对应数据&转化为df的形式;

index为年月; columns为lat和lon的组合

def trans_df(df, vals, lats, lons, years, months):'''(100, 36, 24, 72) -- year, month,lat,lon ''' for j,lat_ in enumerate(lats):for i,lon_ in enumerate(lons):c = 'lat_lon_{}_{}'.format(int(lat_),int(lon_))  v = []for y in range(len(years)):for m in range(len(months)): v.append(vals[y,m,j,i])df[c] = vreturn df
year_month_index = []years              = np.array(nc_SODA['year'][:])
months             = np.array(nc_SODA['month'][:])
lats             = np.array(nc_SODA['lat'][:])
lons             = np.array(nc_SODA['lon'][:])for year in years:for month in months:year_month_index.append('year_{}_month_{}'.format(year,month))df_sst  = pd.DataFrame({'year_month':year_month_index})
df_t300 = pd.DataFrame({'year_month':year_month_index})
df_ua   = pd.DataFrame({'year_month':year_month_index})
df_va   = pd.DataFrame({'year_month':year_month_index})
%%time
df_sst = trans_df(df = df_sst, vals = np.array(nc_SODA['sst'][:]), lats = lats, lons = lons, years = years, months = months)
df_t300 = trans_df(df = df_t300, vals = np.array(nc_SODA['t300'][:]), lats = lats, lons = lons, years = years, months = months)
df_ua   = trans_df(df = df_ua, vals = np.array(nc_SODA['ua'][:]), lats = lats, lons = lons, years = years, months = months)
df_va   = trans_df(df = df_va, vals = np.array(nc_SODA['va'][:]), lats = lats, lons = lons, years = years, months = months)
label_trans_path = './data/'
df_sst.to_csv(label_trans_path  + 'df_sst_SODA.csv',index = None)
df_t300.to_csv(label_trans_path + 'df_t300_SODA.csv',index = None)
df_ua.to_csv(label_trans_path   + 'df_ua_SODA.csv',index = None)
df_va.to_csv(label_trans_path   + 'df_va_SODA.csv',index = None)

2.2 CMIP_label处理

label_path       = './data/CMIP_label.nc'
label_trans_path = './data/'
nc_label         = Dataset(label_path,'r')years            = np.array(nc_label['year'][:])
months           = np.array(nc_label['month'][:])year_month_index = []
vs               = []
for i,year in enumerate(years):for j,month in enumerate(months):year_month_index.append('year_{}_month_{}'.format(year,month))vs.append(np.array(nc_label['nino'][i,j]))df_CMIP_label               = pd.DataFrame({'year_month':year_month_index})
df_CMIP_label['year_month'] = year_month_index
df_CMIP_label['label']      = vsdf_CMIP_label.to_csv(label_trans_path + 'df_CMIP_label.csv',index = None)
df_CMIP_label.head()

2.3 CMIP_train处理

CMIP_train.nc中[0,0:36,:,:]为CMIP6第一个模式提供的第1-第3年逐月的历史模拟数据;
…,
CMIP_train.nc中[150,0:36,:,:]为CMIP6第一个模式提供的第151-第153年逐月的历史模拟数据;CMIP_train.nc中[151,0:36,:,:]为CMIP6第二个模式提供的第1-第3年逐月的历史模拟数据;
…,
CMIP_train.nc中[2265,0:36,:,:]为CMIP5第一个模式提供的第1-第3年逐月的历史模拟数据;
…,
CMIP_train.nc中[2405,0:36,:,:]为CMIP5第二个模式提供的第1-第3年逐月的历史模拟数据;
…,
CMIP_train.nc中[4644,0:36,:,:]为CMIP5第17个模式提供的第140-第142年逐月的历史模拟数据。其中每个样本第三、第四维度分别代表经纬度(南纬55度北纬60度,东经0360度),所有数据的经纬度范围相同。
CMIP_path       = './data/CMIP_train.nc'
CMIP_trans_path = './data'
nc_CMIP  = Dataset(CMIP_path,'r')
nc_CMIP.variables.keys()# dict_keys(['sst', 't300', 'ua', 'va', 'year', 'month', 'lat', 'lon'])
nc_CMIP['t300'][:].shape# (4645, 36, 24, 72)
year_month_index = []years              = np.array(nc_CMIP['year'][:])
months             = np.array(nc_CMIP['month'][:])
lats               = np.array(nc_CMIP['lat'][:])
lons               = np.array(nc_CMIP['lon'][:])last_thre_years = 1000
for year in years:'''数据的原因,我们'''if year >= 4645 - last_thre_years:for month in months:year_month_index.append('year_{}_month_{}'.format(year,month))df_CMIP_sst  = pd.DataFrame({'year_month':year_month_index})
df_CMIP_t300 = pd.DataFrame({'year_month':year_month_index})
df_CMIP_ua   = pd.DataFrame({'year_month':year_month_index})
df_CMIP_va   = pd.DataFrame({'year_month':year_month_index})
  • 因为内存限制,我们暂时取最后1000个year的数据

def trans_thre_df(df, vals, lats, lons, years, months, last_thre_years = 1000):'''(4645, 36, 24, 72) -- year, month,lat,lon ''' for j,lat_ in (enumerate(lats)):
#         print(j)for i,lon_ in enumerate(lons):c = 'lat_lon_{}_{}'.format(int(lat_),int(lon_))  v = []for y_,y in enumerate(years):'''数据的原因,我们'''if y >= 4645 - last_thre_years:for m_,m in  enumerate(months): v.append(vals[y_,m_,j,i])df[c] = vreturn df
%%time
df_CMIP_sst  = trans_thre_df(df = df_CMIP_sst,  vals   = np.array(nc_CMIP['sst'][:]),  lats = lats, lons = lons, years = years, months = months)
df_CMIP_sst.to_csv(CMIP_trans_path + 'df_CMIP_sst.csv',index = None)
del df_CMIP_sst
gc.collect()df_CMIP_t300 = trans_thre_df(df = df_CMIP_t300, vals   = np.array(nc_CMIP['t300'][:]), lats = lats, lons = lons, years = years, months = months)
df_CMIP_t300.to_csv(CMIP_trans_path + 'df_CMIP_t300.csv',index = None)
del df_CMIP_t300
gc.collect()df_CMIP_ua   = trans_thre_df(df = df_CMIP_ua,   vals   = np.array(nc_CMIP['ua'][:]),   lats = lats, lons = lons, years = years, months = months)
df_CMIP_ua.to_csv(CMIP_trans_path + 'df_CMIP_ua.csv',index = None)
del df_CMIP_ua
gc.collect()df_CMIP_va   = trans_thre_df(df = df_CMIP_va,   vals   = np.array(nc_CMIP['va'][:]),   lats = lats, lons = lons, years = years, months = months)
df_CMIP_va.to_csv(CMIP_trans_path + 'df_CMIP_va.csv',index = None)
del df_CMIP_va
gc.collect()# (36036, 1729)

数据建模

工具包导入&数据读取

1. 工具包导入

import pandas as pd
import numpy  as np
import tensorflow as tf
from tensorflow.keras.optimizers import Adam
import matplotlib.pyplot as plt
import scipy
import joblib
from netCDF4 import Dataset
import netCDF4 as nc
from tensorflow.keras.callbacks import LearningRateScheduler, Callback
import tensorflow.keras.backend as K
from tensorflow.keras.layers import *
from tensorflow.keras.models import *
from tensorflow.keras.optimizers import *
from tensorflow.keras.callbacks import *
from tensorflow.keras.layers import Input
import gc
%matplotlib inline

2. 数据读取

SODA_label处理

  1. 标签
标签数据为Nino3.4 SST异常指数,数据维度为(year,month)。
CMIP(SODA)_train.nc对应的标签数据当前时刻Nino3.4 SST异常指数的三个月滑动平均值,因此数据维度与维度介绍同训练数据一致
注:三个月滑动平均值为当前月与未来两个月的平均值。
label_path       = './data/SODA_label.nc'
nc_label         = Dataset(label_path,'r')
tr_nc_labels     = nc_label['nino'][:]

2. 原始特征数据读取

SODA_path        = './data/SODA_train.nc'
nc_SODA          = Dataset(SODA_path,'r') nc_sst           = np.array(nc_SODA['sst'][:])
nc_t300          = np.array(nc_SODA['t300'][:])
nc_ua            = np.array(nc_SODA['ua'][:])
nc_va            = np.array(nc_SODA['va'][:])

模型构建

1. 神经网络框架

def RMSE(y_true, y_pred):return tf.sqrt(tf.reduce_mean(tf.square(y_true - y_pred)))def RMSE_fn(y_true, y_pred):return np.sqrt(np.mean(np.power(np.array(y_true, float).reshape(-1, 1) - np.array(y_pred, float).reshape(-1, 1), 2)))def build_model():  inp    = Input(shape=(12,24,72,4))  x_4    = Dense(1, activation='relu')(inp)   x_3    = Dense(1, activation='relu')(tf.reshape(x_4,[-1,12,24,72]))x_2    = Dense(1, activation='relu')(tf.reshape(x_3,[-1,12,24]))x_1    = Dense(1, activation='relu')(tf.reshape(x_2,[-1,12]))x = Dense(64, activation='relu')(x_1)  x = Dropout(0.25)(x) x = Dense(32, activation='relu')(x)   x = Dropout(0.25)(x)  output = Dense(24, activation='linear')(x)   model  = Model(inputs=inp, outputs=output)adam = tf.optimizers.Adam(lr=1e-3,beta_1=0.99,beta_2 = 0.99) model.compile(optimizer=adam, loss=RMSE)return model

2. 训练集验证集划分

### 训练特征,保证和训练集一致
tr_features = np.concatenate([nc_sst[:,:12,:,:].reshape(-1,12,24,72,1),nc_t300[:,:12,:,:].reshape(-1,12,24,72,1),\nc_ua[:,:12,:,:].reshape(-1,12,24,72,1),nc_va[:,:12,:,:].reshape(-1,12,24,72,1)],axis=-1)### 训练标签,取后24个
tr_labels = tr_nc_labels[:,12:] ### 训练集验证集划分
tr_len     = int(tr_features.shape[0] * 0.8)
tr_fea     = tr_features[:tr_len,:].copy()
tr_label   = tr_labels[:tr_len,:].copy()val_fea     = tr_features[tr_len:,:].copy()
val_label   = tr_labels[tr_len:,:].copy()

3. 模型训练

#### 构建模型
model_mlp     = build_model()
#### 模型存储的位置
model_weights = './model_baseline/model_mlp_baseline.h5'checkpoint = ModelCheckpoint(model_weights, monitor='val_loss', verbose=0, save_best_only=True, mode='min',save_weights_only=True)plateau        = ReduceLROnPlateau(monitor='val_loss', factor=0.5, patience=5, verbose=1, min_delta=1e-4, mode='min')
early_stopping = EarlyStopping(monitor="val_loss", patience=20)
history        = model_mlp.fit(tr_fea, tr_label,validation_data=(val_fea, val_label),batch_size=4096, epochs=200,callbacks=[plateau, checkpoint, early_stopping],verbose=2)

4. 模型预测

prediction = model_mlp.predict(val_fea)

5. Metrics

from   sklearn.metrics import mean_squared_error
def rmse(y_true, y_preds):return np.sqrt(mean_squared_error(y_pred = y_preds, y_true = y_true))def score(y_true, y_preds):accskill_score = 0rmse_scores    = 0a = [1.5] * 4 + [2] * 7 + [3] * 7 + [4] * 6y_true_mean = np.mean(y_true,axis=0) y_pred_mean = np.mean(y_preds,axis=0)
#     print(y_true_mean.shape, y_pred_mean.shape)for i in range(24): fenzi = np.sum((y_true[:,i] -  y_true_mean[i]) *(y_preds[:,i] -  y_pred_mean[i]) ) fenmu = np.sqrt(np.sum((y_true[:,i] -  y_true_mean[i])**2) * np.sum((y_preds[:,i] -  y_pred_mean[i])**2) ) cor_i = fenzi / fenmuaccskill_score += a[i] * np.log(i+1) * cor_irmse_score   = rmse(y_true[:,i], y_preds[:,i])
#         print(cor_i,  2 / 3.0 * a[i] * np.log(i+1) * cor_i - rmse_score)rmse_scores += rmse_score return  2 / 3.0 * accskill_score - rmse_scores
print('score', score(y_true = val_label, y_preds = prediction))

三、模型预测

在上面的部分,我们已经训练好了模型,接下来就是提交模型并在线上进行预测,这块可以分为三步:

  • 导入模型;

  • 读取测试数据并且进行预测;

  • 生成提交所需的版本;

模型导入

import tensorflow as tf
import tensorflow.keras.backend as K
from tensorflow.keras.layers import *
from tensorflow.keras.models import *
from tensorflow.keras.optimizers import *
from tensorflow.keras.callbacks import *
from tensorflow.keras.layers import Input
import numpy as np
import os
import zipfiledef RMSE(y_true, y_pred):return tf.sqrt(tf.reduce_mean(tf.square(y_true - y_pred)))def build_model():  inp    = Input(shape=(12,24,72,4))  x_4    = Dense(1, activation='relu')(inp)   x_3    = Dense(1, activation='relu')(tf.reshape(x_4,[-1,12,24,72]))x_2    = Dense(1, activation='relu')(tf.reshape(x_3,[-1,12,24]))x_1    = Dense(1, activation='relu')(tf.reshape(x_2,[-1,12]))x = Dense(64, activation='relu')(x_1)  x = Dropout(0.25)(x) x = Dense(32, activation='relu')(x)   x = Dropout(0.25)(x)  output = Dense(24, activation='linear')(x)   model  = Model(inputs=inp, outputs=output)adam = tf.optimizers.Adam(lr=1e-3,beta_1=0.99,beta_2 = 0.99) model.compile(optimizer=adam, loss=RMSE)return model model = build_model()
model.load_weights('./user_data/model_data/model_mlp_baseline.h5')

模型预测

test_path = './tcdata/enso_round1_test_20210201/'### 1. 测试数据读取
files = os.listdir(test_path)
test_feas_dict = {}
for file in files:test_feas_dict[file] = np.load(test_path + file)### 2. 结果预测
test_predicts_dict = {}
for file_name,val in test_feas_dict.items():test_predicts_dict[file_name] = model.predict(val).reshape(-1,)
#     test_predicts_dict[file_name] = model.predict(val.reshape([-1,12])[0,:])### 3.存储预测结果
for file_name,val in test_predicts_dict.items(): np.save('./result/' + file_name,val)

预测结果打包

#打包目录为zip文件(未压缩)
def make_zip(source_dir='./result/', output_filename = 'result.zip'):zipf = zipfile.ZipFile(output_filename, 'w')pre_len = len(os.path.dirname(source_dir))source_dirs = os.walk(source_dir)print(source_dirs)for parent, dirnames, filenames in source_dirs:print(parent, dirnames)for filename in filenames:if '.npy' not in filename:continuepathfile = os.path.join(parent, filename)arcname = pathfile[pre_len:].strip(os.path.sep)   #相对路径zipf.write(pathfile, arcname)zipf.close()
make_zip()

四、提升方向

  • 模型角度:我们只使用了简单的MLP模型进行建模,可以考虑使用其它的更加fancy的模型进行尝试;

  • 数据层面:构建一些特征或者对数据进行一些数据变换等;

  • 针对损失函数设计各种trick的提升技巧;

往期精彩回顾适合初学者入门人工智能的路线及资料下载机器学习及深度学习笔记等资料打印机器学习在线手册深度学习笔记专辑《统计学习方法》的代码复现专辑
AI基础下载机器学习的数学基础专辑
本站qq群704220115,加入微信群请扫码:

【数据竞赛】从0梳理1场时间序列赛事!相关推荐

  1. 从0梳理1场时间序列赛事!

    ↑↑↑关注后"星标"Datawhale 每日干货 & 每月组队学习,不错过 Datawhale干货 作者:杰少,南京大学硕士 本文基于 2021 "AI Eart ...

  2. 【数据竞赛】从0梳理1场数据挖掘赛事!

    作者:王茂霖,华中科技大学,Datawhale成员 摘要:数据竞赛对于大家理论实践和增加履历帮助比较大,但许多读者反馈不知道如何入门,本文以河北高校数据挖掘邀请赛为背景,完整梳理了从环境准备.数据读取 ...

  3. 从0梳理1场NLP赛事!

    ↑↑↑关注后"星标"Datawhale 每日干货 & 每月组队学习,不错过 Datawhale干货 作者:徐美兰-lan,华南理工大学 摘要:这是从0实践竞赛第3篇,数据竞 ...

  4. 从0梳理1场数据挖掘赛事!

    ↑↑↑关注后"星标"Datawhale 每日干货 & 每月组队学习,不错过 Datawhale干货 作者:王茂霖,华中科技大学,Datawhale成员 摘要:数据竞赛对于大 ...

  5. 【NLP】从0梳理1场NLP赛事!

    作者:徐美兰-lan,华南理工大学 摘要:这是从0实践竞赛第3篇,数据竞赛对理论实践和增加履历有益,为了让初学者也能入门,本文以全球人工智能技术大赛预热赛-中文预训练模型赛事为背景,梳理了nlp赛事的 ...

  6. 从0梳理1场CV大赛(Top 3%)!

    ↑↑↑关注后"星标"Datawhale 每日干货 & 每月组队学习,不错过 Datawhale干货 作者:阿水,北京航空航天大学 ,Datawhale成员 摘要:数据竞赛对 ...

  7. 【数据竞赛】从0梳理1场CV缺陷检测赛事!

    作者:江保祥,厦门大学 一.布匹缺陷检测比赛分析 1. 赛题背景 去年的广东工业大赛已入选到全球人工智能技术大赛热身赛,大赛聚焦布匹疵点智能检测,要求选手研究开发高效可靠的计算机视觉算法,提升布匹疵点 ...

  8. 从0梳理1场CV缺陷检测赛事!

    ↑↑↑关注后"星标"Datawhale 每日干货 & 每月组队学习,不错过 Datawhale干货 作者:江保祥,厦门大学 一.布匹缺陷检测比赛分析 1. 赛题背景 去年的 ...

  9. kaggle点赞最多的 泰坦尼克号数据竞赛模型融合方法(附代码)

    听说很多大佬都是从kaggle上获取的知识, 加工整理成一套属于自己的竞赛体系 今年7月份我开始参加大数据竞赛, 现在差不多有10场比赛了, 都是结构化比赛. 小的比赛还能进Top名次, 大点的比赛就 ...

最新文章

  1. 父类的静态方法能否被子类重写?
  2. Android系统主题总结和使用
  3. Python Django 一对一多表设计数据保存
  4. 何时才有Email发布功能
  5. mysql myisam 锁机制_MySQL--MyISAM之锁机制
  6. Python PIL(图像处理库)使用方法
  7. NSMutableString可变字符串
  8. Rust LeetCode 练习:929 Unique Email Addresses
  9. 书摘 - 吴军.浪潮之巅
  10. 在线文档有哪些技术难点
  11. 7-35 混合类型数据格式化输入 (5 分)
  12. foreign key
  13. xp无法访问文件共享服务器,XP不能访问Windows7共享文件之解决办法
  14. 等额本息计算 java 代码
  15. #《神奇动物:邓布利多之谜》
  16. python与php8,后端php和python学哪个
  17. 职称计算机代码表,全国职称计算机考试科目及科目代码
  18. 名编辑电子杂志大师教程 | 设置电子杂志的高宽比例
  19. python数据框提取子集_pandas 数据子集的获取
  20. 反编译 轻松调频 Android APP 下载“飞鱼秀”录音

热门文章

  1. 选中条目android spinner的使用
  2. DotNetNuke(DNN)皮肤制作--如何居中内容
  3. sgu 207 Robbers
  4. 机器学习基础6--集群模型和算法
  5. C#应用视频教程3.1 USB工业相机测试
  6. UploadHandleServlet
  7. [ An Ac a Day ^_^ ] CodeForces 468A 24 Game 构造
  8. 《入门经典》——6.24
  9. smokeping部署安装
  10. 通过padding-bottom或者padding-top实现等比缩放响应式图片