写在前面
去年九月份(应该是上一届比赛结束的时候吧),学校(那时)研一的学长简单讲了这个比赛,但是觉得机器学习很高大上很牛的感觉,听说了这个比赛当时就决定冲一冲,拿不拿奖是其次了,多少能学点东西吧哈哈哈哈~
当时是根据学长的建议入门的,边看吴恩达老师的机器学习入门边稍微看了点廖雪峰老师的python就开始做学生成绩预测以及泰坦尼克生存预测了(进程和线程那块没看,还是偷懒了)。
刚开始学还是很痛苦的,那时候疫情在家,身边也找不到一起入门学习打比赛的同学,就自己一个人硬啃。对着视频一行行敲代码,然后运行调试,查一个个函数的用法,写注释…
整个学习加上比赛的过程大概是二月到六月吧,完成了四个项目(完整的处理数据和预测,当然不是最优的),学生成绩预测、泰坦尼克生存预测、华为大数据挑战赛热身赛、华为大数据挑战赛正式赛。准备在这里记录一下自己的学习历程,要是忘了我也太亏了(没拿到奖总不能到最后还把学的那点皮毛给忘了)哈哈哈哈。
先大概理一理解题的步骤:
- 用合适的方法从给出的数据里提取需要的特征
- 根据需要进行特征工程
- 划分训练集和测试集
- 尝试用不同的算法进行训练,调参
- 如有必要,最后得到的结果进行融合
- 得到预测结果
学生成绩预测这个项目是跟着视频做的,附链接康奈尔小鲜肉带你用notebook学数据分析,算是根据这个视频入门juypter notebook了哈哈,内容比较简单下文就不赘述了。
泰坦尼克号生存预测是跟着大树先生的博客做的,真的好强,写得很详细,附链接Kaggle_Titanic生存预测 – 详细流程吐血梳理
从整个比赛过程来看,我在模型调参方面做得十分不到位,因为属于速成机器学习,在算法调参等等,比如更换激活函数,更换损失函数这块感觉无从下手。比赛时更多的在特征工程方向努力。
1.用合适的方法从给出的数据里提取需要的数据(数据清洗等)
- 热身赛
赛题:
热身赛题——交通流量预测
随着电子信息和移动通信技术高速发展和不断融合,人工智能在各个领域都相继取得了巨大的突破,城市智能体也应运而生,而城市交通又是城市智能体的核心。交通流量数据既是城市交通中的基础数据,又是反应交通状况的重要指标之一,准确预测交通流量对城市交通具有重大意义。本题以交通流量预测为目标,邀请各个队伍以历史交通流量数据建立对应的算法模型,预测目标流量数据,通过预测值和真实值之间的对比得到预测准确率,以此来评估各队伍所提交的预测算法。
一、赛题说明
本次比赛任务是利用历史数据并结合地图信息,预测五和张衡交叉路口未来一周周一(2019年2月11日)和周四(2019年2月14日)两天的5:00-21:00通过wuhe_zhangheng路口4个方向的车流量总和。
要求模型输出格式如下:
{“data”:{“resp_data”:{“wuhe_zhangheng”:[1,4,5,6,4…]}}}
从5:00开始每5min的预测数据,第一个数据为5:00-5:05的流量值,最后一个数据为20:55-21:00。两天的数据按时间先后放在一起,总共有384个数据。
小提示:如果不考虑天气、周边活动、节假日等因素,预测结果可能不准确哦。
二、数据说明
本次比赛提供4周(2019.1.12 – 2019.2.8)深圳龙岗区坂田街道交通流量历史数据。
车流数据格式如下:
其中,time为上述格式时间字符串,cross为路口名,direction为车流起始方向,leftFlow是左转车流,straightFlow是直行车流。
说明:
(1) 十字路口包含四个方向车流数据,此处未全部列出。
(2) 路口名称分别为:五和路、张衡路、稼先路、隆平路、冲之大道。可以通过但不限于百度地图等地图软件获取地图路网信息。
(3) 因为右转车流不受信号灯控制,因此未做统计。
三、评分标准
第一部分(分类问题)
分类问题评价标准:预测的评价还是通过每一个5min预测车流和真实通过车流对比,看看趋势是否一致(比如10月19日的5:00到5:05的真实车流是4,10月20日的5:00到5:05的真实车流为5,那么只要车流预测值大于4,就得100分,最后得分为所有得分求加权平均(权重为该时间段所在小时的车流量占16小时总车流的比重))。
第二部分(回归问题)
回归问题评价标准:预测的评价还是通过每一个5min 预测车流和真实通过车流通过grade公式计算最后得分,加权细则与第一部分相同:
其中wi为权重,xj为真实车流数据,xj拔为预测车流数据,ε为e-9。
最后将两部分分数做归一化处理,第一部分占比40%,第二部分占比60%。
以上是本次热身赛的赛题说明。
赛题要求预测五和张衡交叉路口周一(2019年2月11日)和周四(2019年2月14日)两天的5:00-21:00通过wuhe_zhangheng路口4个方向的车流量总和,然后主办方是给出了一共六个路口1.12-2.08的数据,但是五和张衡路口13号和15号的数据是确实的,只有0:00-3:05的数据,而14号的诗句是完全缺失的。
总的来说这次热身赛,就是利用这六个路口四个星期的数据来过 拟合2019.2.11(年后开工第一天)和2.14(情人节)这两天五和张衡路口左转和直行的车流量之和。
热身赛前期我并没有清洗数据的想法,但是与榜单前几的大佬沟通之后了解到了他们的处理方法。他们基本都是通过已提交的result的得分来找到接近2.10和2.14这两天实际车流量的验证集,在线下通过验证集得到分数较高的模型再提交。这样确实节省了很多提交次数,而像我这样的萌新则是在前期浪费了许多提交次数在一些对结果影响不大的修改上(这就是大佬吗,我哭了)。某大佬还曾经对我明示,挑出训练集中年前趋势类似的日期训练,其他路口不管,数据不全的日期不管(1.13、1.14、1.15),年后数据不管,调休日期不管。可惜这份“嗟来之食”我也没有参透o(╥﹏╥)o
简单梳理一下我清洗数据的思路:
- 1.需要预测的时间段是5:00-21:00,我按照了三种方式筛选数据:不做筛选处理;只选择5:00-21:00的数据;从五点前一段时间作为起点,九点后一段长度相同的时间作为终点。
- 2.去除1.13、1.14、1.15的数据
- 3.去除年后数据
- 4.去除年前一周的数据(因为这时候车流量已经很少了而我推测年后开工那一周车流量应该是比较大的,取作训练集反而干扰太大了)
- 5.筛选趋势异常的日期
- 6.删除调休的日期
可能是我清洗不到位或者方法有问题吧,以上我排列组合了N次都没有太好的效果,分数大概在60出头吧。
2.根据需要进行特征工程
- 1.特征这块,我一部分参考了baseline里对时间戳的处理,将weekday和timeindex作为特征,sum作为label
df_dt = to_datetime(raw_data.loc[:, "time"], format="%Y/%m/%d %H:%M:%S")
all_data = pd.DataFrame({
"weekday": df_dt.dt.weekday/6.0,
"timeindex": (df_dt.dt.hour * 60 + df_dt.dt.minute)/(24*60.0),
"sum":raw_data["sum"]]})
- 2.因为给的数据中有leftFlow和straightFlow,考虑过分别用预测leftFlow和straightFlow然后求和作为result。但是就很遗憾,由于是第一次使用华为云的modelArts,这个平台是这样的:首先用juypternotebook写训练模型的代码并运行然后得到模型文件和配置文件,然后编写推理代码,也就是对测试集数据的预处理。
如果想要实现上文的想法,需要在juypternotebook中使用两个模型训练(不可能实现,需要导出两个模型文件),并且在推理代码中需要编写对训练得到的两个label求和的代码(不可能实现,因为推理代码只做测试集数据的预处理)。或者以上可能有大佬可以通过各种改配置文件实现,但当时我只能舍弃这个想法了。
事实证明是我一根筋了,其实当时我完全可以把训练集下载下来,在自己的电脑上跑,最后提交一个result.txt文件就完事了。。。(一万只草泥马奔腾而过)
就当吸取个教训吧,有些事情可能真就差那么一点点,如果我有上帝视角o(╥﹏╥)o - 3.除夕前后车流量在各个路口都有明显区别,可以作为一个特征考虑
- 4.在画出五和张衡路口除夕前三周每天的车流量情况后我发现,在不同周次的同一天越靠近除夕车流量明显减少了,但是这个attribute我一直找不到好的方法去描述,应该是蛮有效的一个特征,但是那时候已经临近热身赛尾声,没有足够的提交次数验证我的想法了(没错我没有在线下找到合适的验证集)
但是据群里大佬们分享的经验,好像确实不用做特征工程,基本都是两三个特征。这题的关键是如何更好的构造数据集去拟合4.11和2.14的车流量,嗯对,过拟合就完事了。
但是时间序列这块我完全是门外汉,至少这次热身赛是来不及学了o(╥﹏╥)o
3.模型融合
热身赛我没有进行模型融合,因为在线上跑模型,当时在推理代码这块卡住了(还是很不方便啊对于初学者),所以没有试过效果怎么样。
4.划分训练集和验证集
我裂开了,我突然想到了为什么以上操作都没有什么效果,应该是我划分训练集和验证集的方式有问题。我一直都是直接使用train_test_split()来对整个训练集划分的。
既然是为了过拟合,针对这道题我觉得应该选定一个或者两个接近的日期作为验证集俩训练模型。对!就是这样,为什么我现在才想明白o(╥﹏╥)o
重心全在特征工程了o(╥﹏╥)o
可是这个破 题还真不用什么狗屁特征工程o(╥﹏╥)o
5.尝试用不同的算法进行训练,调参
算法这块我一直用的是GBR算法
后期向大佬建议我使用LGB,但是ModelArts上的JuyPterNoteBook用不了LGB…我裂开,又吃了不知道可以线下跑模型的亏o(╥﹏╥)o
虽然好像也提不了多少分
调参使用的是model_selection.GridSearchCV(),应该是我损失函数的选择有问题,效果也不明显。
6.得到预测结果
最后就是用训练好的模型predict测试集得到结果了。
贴一下最高得分(66.2228)代码
# read data of one day and one direction
def read_file(path, filename):
calfile = os.path.join(path, filename)#路径拼接
original = pd.read_csv(calfile, header=None)
data = pd.DataFrame(columns=["time","sum"])
data["time"] = original[0]
data["sum"]=original[3]+original[4]
return data
# read data of one day
def read_data_day(path, date,chuxihou):
#day_data = pd.DataFrame(columns=["time","cross","direction" ,"sum"])
day_data = pd.DataFrame(columns=["time","sum"])
day_data_= pd.DataFrame(columns=["time","sum"])
caldir = os.path.join(path, date)
i=1
# read data of one day
for f in os.listdir(caldir):
if (re.match(r'wuhe_zhangheng.*\.csv', f)) and i==1:
day_data = day_data.append(read_file(caldir, f), ignore_index=True)
i=0
else:
day_data_=day_data_.append(read_file(caldir, f), ignore_index=True)
day_data["sum"]=day_data_["sum"]+day_data["sum"]
if chuxihou==1:
day_data["chuxihou"]=1
else:
day_data["chuxihou"]=0
return day_data
def train_model(local_data_X,local_data_Y):
X_train, X_test, y_train, y_test = train_test_split(local_data_X, local_data_Y, test_size=0.2, random_state=42)
print("X_train shape is: " + str(X_train.shape))
print("X_test shape is: " + str(X_test.shape))
gbr=GradientBoostingRegressor(**best_params)
gbr.fit(local_data_X, local_data_Y)
joblib.dump(gbr, LOCAL_MODEL_PATH)
#y_predict = gbr.predict(X_test)
#mse = mean_squared_error(y_test, y_predict)
#print("MSE: %.4f" % mse)
def create_config():
schema_model=json.loads('{"model_algorithm":"gbtree_classification","model_type":"Scikit-learn","runtime":"python3.6","metrics":{},"apis":[{"procotol":"http","url":"/","method":"post","request":{"Content-type":"applicaton/json","data":{"type":"object","properties":{"req_data":{"type":"array","items":[{"type":"string"}]}}}},"response":{"Content-type":"applicaton/json","data":{"type":"object","properties":{"resp_data":{"type":"array","items":[{"type":"number"}]}}}}}]}',object_pairs_hook=OrderedDict)
schema_model['model_algorithm'] = "gbtree_regression"
schema_model['model_type'] = "Scikit_Learn"
with open(LOCAL_CONFIG_PATH, 'w') as f:
json.dump(schema_model, f)
if __name__ == "__main__":
# copy data from obs to local
mox.file.copy_parallel(OBS_DATA_PATH, LOCAL_DATA_PATH)
raw_data = pd.DataFrame(columns=["time","chuxihou","sum"])
for day in os.listdir(LOCAL_DATA_PATH):
if day in['02-03','02-02']:
continue
if day in ['02-04','02-05','02--06','02-07','02-08'] :
chuxihou=1
raw_data = raw_data.append(read_data_day(LOCAL_DATA_PATH, day,chuxihou))#https://blog.csdn.net/tz_zs/article/details/81238085
else:
chuxihou=0
raw_data = raw_data.append(read_data_day(LOCAL_DATA_PATH, day,chuxihou))
# encode time in raw data to weekday and timeindex(the n minutes of the day)
df_dt = to_datetime(raw_data.loc[:, "time"], format="%Y/%m/%d %H:%M:%S")
all_data = pd.DataFrame({
"weekday": df_dt.dt.weekday/6.0,
"timeindex": (df_dt.dt.hour * 60 + df_dt.dt.minute)/(24*60.0),
"sum":raw_data["sum"],"chuxihou":raw_data["chuxihou"]})
all_data.dropna(subset=['timeindex'],inplace=True)
all_data["sum"]=all_data["sum"].astype(int)
# read and preprocess data
local_data_X= all_data.drop(['sum'],axis=1)
local_data_Y=all_data['sum']
train_model(local_data_X,local_data_Y)
create_config()
mox.file.copy(LOCAL_MODEL_PATH, OBS_MODEL_PATH)
mox.file.copy(LOCAL_CONFIG_PATH, OBS_CONFIG_PATH)
print("Model training has been completed!")
好吧其实没有什么技术含量。。。