阿里云天池《“AI Earth”人工智能创新挑战赛》萌新体验

本文记录了一位参赛者首次参加阿里天池大赛的经历,挑战时间序列预测问题——ENSO指数预测。面对数据质量问题、内存限制和模型选择的困难,作者尝试了多种数据处理策略和模型,包括CNN。通过分析CMIP和SODA数据,进行增量学习,最终实现模型训练。尽管遇到诸多bug,但作者决心克服并期待未来进步。
摘要由CSDN通过智能技术生成

前言

一直都想参加一次阿里天池比赛,看到有公众号提了这次比赛的baseline,果断报名。0基础,开始了第一次数据科学比赛的尝试~

这次比赛遇到了各种各样的问题,几度想放弃,但是不忍心排200多名。来都来了,冲完B榜吧,也算是表明自己第一次参加天池大赛的态度,不能让母校的名字排在最后。

题目

https://tianchi.aliyun.com/competition/entrance/531871/introduction
在这里插入图片描述
发生在热带太平洋上的厄尔尼诺-南方涛动(ENSO)现象是地球上最强、最显著的年际气候信号。通过大气或海洋遥相关过程,经常会引发洪涝、干旱、高温、雪灾等极端事件,对全球的天气、气候以及粮食产量具有重要的影响。准确预测ENSO,是提高东亚和全球气候预测水平和防灾减灾的关键。

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

在这里插入图片描述

这题是一个典型的时间序列预测问题,看起来没那么难,但是认真一想还是有很多难点的,比如数据质量,内存限制,模型,都是可以改进的,下面是我的baseline。

数据分析

拿到数据,我都是一股脑往模型里扔,然后开始炼丹,炼着炼着,分数从-6变为-10、-13、-15、-22…最后都到NaN了,平时老师也总是说,不要一上来就上模型,先把数据分析清楚了,于是打算再花几个小时进行数据分析。

CMIP数据分析

CMIP是模拟数据,网友UCDuan说:

CMIP是气候模型的数据
你可以理解为不同情况下(不同的初始场和不同的气候模型)“地球”的真实情况,只是符合大致的‘物理规律’,但不是地球真实的历史情况。所以用CMIP就是想挖掘这个‘物理规律’。

这里我打算拿CMIP数据训练,SODA数据验证。

数据统计

以sst数据为例
在这里插入图片描述

在这里插入图片描述

趋势图

在这里插入图片描述
在这里插入图片描述
可以看出数据是经过处理的,每隔12个月,连续三组数据值是一样的
再加上下个特征,发现他们也是相似的
在这里插入图片描述
另外有些特征数据缺失,需要进行平均填充
在这里插入图片描述
方法如下:

tr_features2 = np.concatenate([sst_CMIP_shaped,t300_CMIP_shaped,va_CMIP_shaped,ua_CMIP_shaped],axis=-1)
tr_features2[np.isnan(tr_features2)] = np.mean(tr_features2)#对NaN值进行均值填充

SODA数据分析

代码

baseline来自这篇文章:https://mp.weixin.qq.com/s/63LPCHNo4zOA_UGDAc2xUQ
这篇文章完成了数据组织、处理、MLP模型构建、本地测试、上传代码,但是有很多问题,我进行了初步修正:

  1. 由于内存限制,CMIP数据只加载1000年==》全部
  2. 前面数据转换为csv文件,后面没有用到==》后面数据加载来源于csv文件
  3. 模型使用MLP==》模型改为3维CNN

代码链接:https://github.com/guhang987/-AI-Earth-
代码解释:

  1. ./ENSO/data文件夹
    存放原始数据与转换后的数据

在这里插入图片描述

  1. data_prepare.py
    将比赛提供的nc文件转换为csv文件
  2. train.ipynb
    训练模型
  3. main.py
    模型打包提交

训练

conda环境:

  • python 3.8
  • tensorflow 2.20

首先进行数据格式转换,提取数据前24个月,以及标签后12个月的数据。

soda_label = pd.read_csv('./ENSO/data/df_SODA_label.csv')['label']
sst_SODA = pd.read_csv('./ENSO/data/df_sst_SODA.csv')
t300_SODA = pd.read_csv('./ENSO/data/df_t300_SODA.csv')
ua_SODA = pd.read_csv('./ENSO/data/df_ua_SODA.csv')
va_SODA = pd.read_csv('./ENSO/data/df_va_SODA.csv')

sst_SODA = np.array(sst_SODA).reshape(100,36,24,72,1)[:,:12,:,:,:]
t300_SODA = np.array(t300_SODA).reshape(100,36,24,72,1)[:,:12,:,:,:]
va_SODA = np.array(va_SODA).reshape(100,36,24,72,1)[:,:12,:,:,:]
ua_SODA = np.array(ua_SODA).reshape(100,36,24,72,1)[:,:12,:,:,:]

然后利用concat方法拼接这4个维度:


tr_features = np.concatenate([sst_SODA,t300_SODA,va_SODA,ua_SODA],axis=-1)
tr_features.shape
### 训练标签,取后24个
tr_labels = np.array(soda_label).reshape(-1,36)[:,12:]
tr_labels.shape

得到(100, 12, 24, 72, 4)的数据以及(100, 24)的标签。按8:2分割训练集与验证集。
下一步构建CNN模型:

_________________________________________________________________
Layer (type)                 Output Shape              Param #   
=================================================================
input_1 (InputLayer)         (None, 12, 24, 72, 4)     0         
_________________________________________________________________
conv3d (Conv3D)              (None, 9, 9, 32, 64)      671808    
_________________________________________________________________
max_pooling3d (MaxPooling3D) (None, 4, 4, 16, 64)      0         
_________________________________________________________________
conv3d_1 (Conv3D)            (None, 1, 1, 1, 128)      2097280   
_________________________________________________________________
flatten (Flatten)            (None, 128)               0         
_________________________________________________________________
dense (Dense)                (None, 64)                8256      
_________________________________________________________________
dropout (Dropout)            (None, 64)                0         
_________________________________________________________________
dense_1 (Dense)              (None, 32)                2080      
_________________________________________________________________
dropout_1 (Dropout)          (None, 32)                0         
_________________________________________________________________
dense_2 (Dense)              (None, 24)                792       
=================================================================
Total params: 2,780,216
Trainable params: 2,780,216
Non-trainable params: 0

相关训练参数:

history        = model_cnn.fit(tr_fea,tr_label,
                    validation_data=(val_fea, val_label),
                    batch_size=4096, epochs=2000,
                    callbacks=[plateau, checkpoint, early_stopping],
                    verbose=1)

之前是拼接SODA和CMIP数据(2000年),后来拼接SODA和CMIP数据(4368年)发现内存爆了,怎么改都不行。于是尝试多次model.fit方法分别输入SODA CMIP数据,中间用del 和gc.collect来释放内存。

#增量学习

for round_ in range(4):
    step=1000
    leng = step*36
    inde = list(range(step*round_*36+1))
    if(round_==4):
        leng = 600*36
    gc.collect()
    sst_CMIP = pd.read_csv('./ENSO/data/df_sst_CMIP.csv',header=None,skiprows=inde,nrows = leng)
    t300_CMIP = pd.read_csv('./ENSO/data/df_t300_CMIP.csv',header=None,skiprows=inde,nrows = leng)
    ua_CMIP = pd.read_csv('./ENSO/data/df_ua_CMIP.csv',header=None,skiprows=inde,nrows = leng)
    va_CMIP = pd.read_csv('./ENSO/data/df_va_CMIP.csv',header=None,skiprows=inde,nrows = leng)
    cmip_label = pd.read_csv('./ENSO/data/df_CMIP_label.csv',header=None,skiprows=inde,nrows = leng)[1]

    sst_CMIP.drop(sst_CMIP.columns[0],axis=1,inplace=True)
    t300_CMIP.drop(t300_CMIP.columns[0],axis=1,inplace=True)
    va_CMIP.drop(va_CMIP.columns[0],axis=1,inplace=True)
    ua_CMIP.drop(ua_CMIP.columns[0],axis=1,inplace=True)
    
    sst_CMIP_shaped = np.array(sst_CMIP).reshape(-1,36,24,72,1)[:,:12,:,:,:]
    t300_CMIP_shaped = np.array(t300_CMIP).reshape(-1,36,24,72,1)[:,:12,:,:,:]
    va_CMIP_shaped = np.array(va_CMIP).reshape(-1,36,24,72,1)[:,:12,:,:,:]
    ua_CMIP_shaped = np.array(ua_CMIP).reshape(-1,36,24,72,1)[:,:12,:,:,:]
    del va_CMIP
    del t300_CMIP
    del ua_CMIP
    del sst_CMIP

    gc.collect()
    tr_features2 = np.concatenate([sst_CMIP_shaped,t300_CMIP_shaped,va_CMIP_shaped,ua_CMIP_shaped],axis=-1)
    tr_features2[np.isnan(tr_features2)] = np.mean(tr_features2)#对NaN值进行均值填充
    del va_CMIP_shaped
    del t300_CMIP_shaped
    del ua_CMIP_shaped
    del sst_CMIP_shaped

    gc.collect()
    tr_labels2 = np.array(cmip_label).reshape(-1,36)[:,12:]
    model_cnn     = build_model()
    if round_ != 0:
        #加载上一轮模型
        
        model_cnn.load_weights('./model/model-%d.h5'%(round_-1))
    #存储本轮模型
    model_weights = "./model/model-%d.h5"%round_
    #保存最佳模型
    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)
    model_cnn.fit(tr_features2,tr_labels2,
                    validation_data=(val_f,val_l),
                    batch_size=32, epochs=20000,
                    callbacks=[plateau, checkpoint, early_stopping],
                    verbose=2)
    prediction = model_cnn.predict(tr_features)
    #from   sklearn.metrics import mean_squared_error
    sc = score(y_true = tr_labels, y_preds = prediction)
    print("%d轮完成,分数%f"%(round_,sc))
    #3轮完成,分数-17.867135

总的来说,数据处理目前有三种思路:

  1. 只拿100年的SODA数据训练
  2. 拿4645年的CMIP数据训练,SODA数据作为验证集
  3. CMIP与SODA数据融合训练

模型的话,也可以用RNN来做,只不过提供的数据已经组织好了,用CNN就直接可以输入。于是我就拿了一个玩具CNN来做,可惜对CNN没啥理解,参数都是自己调的,不知道哪些超参最好,希望以后能搞明白。

我始终没搞清楚什么是气象中的模拟数据、真实历史数据,以上三种方法都尝试过,可惜各种奇怪的bug和每天几次的提交机会,让我无法验证🤯,下次比赛一定要在A轮做好系统的总结。

另外,在服务器训练过程中,经常用到的命令:

  • nvidia-smi:查看显卡信息
  • os.environ["CUDA_VISIBLE_DEVICES"] = "2":设置显卡
  • os.getpid()查看进程id
  • top -d 2 -p 44429 -o ruser=WIDE-RUSER-COLUMN查看某进程
  • free --giga 查看剩余内存
  • echo 1 > /proc/sys/vm/overcommit_memory 设置内存覆盖模式
  • firewall-cmd --zone=public --list-ports 查看端口
  • firewall-cmd --zone=public --add-port=8894/tcp --permanent 开端口
  • firewall-cmd --reload 重启防火墙
    -gpasswd -a $USER docker 将当前用户添加到docker组

BUG

各种各样的bug,主要是数据集太大,内存不够的原因

  • OOM:减少batchsize
  • MemoryError: Unable to allocate 8.61 GiB for an array with shape (4645, 36, 24, 72, 4) and data type float64 读文件时加限制,不全部读取:sst_CMIP = pd.read_csv('./ENSO/data/df_sst_CMIP.csv',nrows = leng )
  • ValueError: Data cardinality is ambiguous: x sizes: 20 y sizes: 500 Please provide data which shares the same first dimension.检查数据输入格式
  • tensorflow.python.framework.errors_impl.UnknownError: Failed to get convolution algorithm. This is probably because cuDNN failed to initialize, so try looking to see if a warning log message was printed above. [[node model/conv3d/Conv3D (defined at train_soda.py:134) ]] [Op:__inference_train_function_902] Function call stack: train_function 之前可以,突然不行,怀疑是内存不够了

提交

第一次用docker,在Windows、linux、macOS上都踩了一遍坑,希望以后会好一点,这里详细记录下如果用docker提交模型。

  1. 阿里云官网免费申请镜像容器

在这里插入图片描述
2. 设置镜像密码
3. 在自己的环境中,准备好如下文件夹或代码:
model文件夹存储自己训练完的模型,
result存储打包提交的结果,
main.py包含了模型加载、测试数据读取、模型预测、预测结果打包等一系列流程。
run.sh是一行脚本,比如python main.py
在这里插入图片描述
4. docker login
5. docker build -t gogogo:3.4 . 这里gogogo是你的镜像名字,后面跟的是版本号。第一次运行时,会自动加载Dockerfile里面的镜像,从服务器拉取
6. docker images查看目前的镜像id
在这里插入图片描述
7. docker tag [imageID] [remote]
8. docker push
9. 在比赛提交页面,配置路径,提交
在这里插入图片描述
10. 查看排名
羞耻排名,只要提交结果,有分数就能进B轮 🎉 🎉 🎉

成绩

A轮成绩:晋级赛A榜:213 /-15.04
B轮成绩:

太羞耻了!!!😓

感悟

我 太 菜 了, 折腾几天,分数越来越多,bug越来越多。但是还是想坚持下去

杀不死你的bug让你更强大🙂

评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值