队名:Turing
成员:
- 中南大学硕士:罗涛,李楠,王艺霏,李迎港
- 华南理工大学硕士:陈暄群
名次:决赛第四,一等奖
本次大赛的赛题是一个九分类问题,需要构建一个城市区域功能分类模型,对于给定的区域,根据区域的遥感图像和用户到访数据,预测区域功能类别。 数据集的每一个样本都包含了两种数据,一种是该区域的遥感图像数据,另一种是该区域的多条用户到访数据。用户到访数据包含了用户ID与到访时间。
竞赛官网:https://dianshi.baidu.com/competition/30/rule
解决方案的详细介绍: https://zhuanlan.zhihu.com/p/82410926
决赛答辩PPT: https://pan.baidu.com/s/1zx5o_O0KVP-q8fadXI6ZOw 提取码:zp3p
竞赛故事: https://blog.csdn.net/destiny19960207/article/details/100693799
lightgbm>=2.2.3
torch>=1.1.0
torchvision>=0.3.0
pandas>=0.23.0
imgaug>=0.2.9
- 规则(rule文件夹)
- CNN(CNN文件夹)
- LGB基模型(LGB文件夹)
- 模型集成(model ensemble文件夹)
由于用户的行为总是具有自身的独特性,所以用户ID携带了丰富的信息。通过分析训练集和测试集的ID特征,我们发现训练集和测试集的用户重叠率极高,测试集的5千万用户中有4300万都在训练集中出现过,重叠率超过80%,因此我们设计了规则对用户ID进行挖掘。
规则分为两种:基于时间的规则、基于可信用户的规则。
所有历史用户都有投票权,票数等于其所在训练集对应类别的区域出现的时长。
基于时间的规则,对于单个测试样本中的用户集合,将其与训练集的用户集合求交集,得到的交集我们称为历史用户集合。对于每一个历史用户,我们可以分别求得他在训练集中9个区域所待时长,将每个历史用户在9个区域对应的时长相加,时长最多的那个类别就是预测类别。(这里的时长根据粗细粒度划分,可以是天数、每天的平均小时数、周末的天数、小时数,得到4个不同的时长规则)
在基于可信用户的规则中,我们定义了严格高可信用户和广义高可信用户两种用户。
若用户在训练集中只去过类别C的地区,则该用户为严格高可信用户,并将该用户的选票设为C。
若用户在训练集中某个类别C区域出现的天数超过其出现总天数的50%,则该用户为广义高可信用户,将该用户的选票设为C。
通过定义可信用户,去掉了部分用户,留下的用户是对分类更有利。获取可信用户的选票后,使用可信用户投票的方式对测试样本进行分类。只有可信用户拥有投票权,每个可信用户只能给其“选票”所在类别投上一票。
通过定义可信用户,去掉了部分用户,留下的用户是对分类更有利。获取可信用户的选票后,使用可信用户投票的方式对测试样本进行分类。
- (1) 基于总时长的规则 rule/totaltime/rule.ipynb
遍历训练集的每个用户的时长,再分类别 统计测试样本中老用户的时长,哪个类别的老用户时长大,就分为哪类
分两步:- 第一步:对训练集进行遍历的代码
遍历训练集的原始visit txt文件,分9个类别记录每个用户的总时长。输出9个txt,每个文件对应一个类别,每个文件中两列记录用户id和时长。对于每个类别,依据时长对用户排序,删除时长低的80% 或 90% 的用户,再合并同一类别中的相同用户,时长相加 - 第二步:测试代码
将第一步输出的txt文件读取,得到训练集的用户时长,存入9个字典中,每个字典对应一个类别,字典的key是用户id,字典的值是该用户的时长,(使用字典可以明显加速,因为对于上亿的用户量进行用户查找是很耗时的)。对于每个测试样本,根据老用户的时长总和,可以得到:该样本内的老用户在训练集中每个类别的时长。代码最终会得到一个npy文件:id2num_test.npy,其维度为[样本数量,9],二维矩阵,数值越大,我们就倾向于将样本分类为这一类。
- 第一步:对训练集进行遍历的代码
- (2) 基于天数的规则(剔除了训练集中天数少的90%用户) rule/day/rule.ipynb
- (3) 基于平均时长的规则 rule/hour/rule.ipynb
- (4) 基于严格高可信用户的规则 rule/cxq/rule.py
先从训练集中找出严格高可信用户,并设置选票。再对单个测试样本,利用严格高可信用户的选票对类别进行投票,投票得到的向量(长度为类别数量9)用于后续集成学习。 - (5) 基于广义高可信用户的规则 rule/cxq2/rule.py
- (6) 基于天数的规则 rule/lyg_day/rule.ipynb
- (7) 基于周末的规则 rule/lyg_holiday/rule.ipynb
将40万样本随机划分为35万训练集样本和5万验证集样本。
预处理:
CNN/model1/data_txt/cleaned.py 从35万训练集样本中除去了约1300张黑图样本
CNN/model1/data_txt/resample_txt.py 针对类别不均衡问题,进行重采样,采样之后训练集样本大约94万
CNN/model1/Dataloader/MultiModal_BDXJTU2019.py 利用imgaug,在训练时做了数据增强
CNN双分支网络结构:
将visit处理成[7,26,24]的三维矩阵,经过一个DPN网络,编码为向量。将图片经过一个卷积网络,编码为向量。将两个向量拼接,实现了visit和图片的特征融合,再接一个全连接层,进行分类。
visit的尺寸太小,不能使用深层的预训练CNN(因为太多的池化层导致feature map尺寸太小,信息丢失),visit分支网络使用随机初始化的DPN26,图片的卷积网络使用不同的预训练CNN,得到以下不同的网络模型:
1) se_resnext101_32x4d
CNN/model1/train_model1.py 训练代码
CNN/model1/test_model1.py 测试代码
CNN/model1/val.py 验证代码
2) se_resnext50_32x4d
CNN/model2/train_model2.py 训练代码
CNN/model2/test_model2.py 测试代码
CNN/model2/val.py 验证代码
3) se_resnet50
CNN/model3/train_model3.py 训练代码
CNN/model3/test_model3.py 测试代码
CNN/model3/val.py 验证代码
4) resnet101
CNN/0.71_resnet101/train.py 训练代码
CNN/0.71_resnet101/test.py 测试代码
CNN/0.71_resnet101/val.py 验证代码
5) resnet50 5折交叉
CNN/0.71_resnet50_5fold/train.py 训练代码
CNN/0.71_resnet50_5fold/test.py 测试代码
CNN/0.71_resnet50_5fold/val.py 验证代码
6) resnet50
CNN/0.71/train.py 训练代码
CNN/0.71/test.py 测试代码
CNN/0.71/val.py 验证代码
6) se_resnet50 十折交叉,后续集成时只用了其中8折的模型
CNN/A/train.py 训练代码
CNN/A/test.py 测试代码
CNN/A/val.py 验证代码
7) se_resnext50_32x4d 十折交叉,后续集成时只用了其中1折的模型
CNN/B/train.py 训练代码
CNN/B/test.py 测试代码
CNN/B/val.py 验证代码
8) se_resnext101_32x4d 十折交叉,后续集成时只用了其中8折的模型
CNN/C/train.py 训练代码
CNN/C/test.py 测试代码
CNN/C/val.py 验证代码
9) squeezenet1_0
CNN/squeezenet1_0/train.py 训练代码
CNN/squeezenet1_0/test.py 测试代码
CNN/squeezenet1_0/val.py 验证代码
LGB/lgb.py
遍历visit的txt文件,统计每个样本的24小时的人数。对于每个样本,都有24维的特征,将24维特征作为lightgbm的输入,进行分类。本部分对集成起的作用不大。
model ensemble/ensemble.ipynb lightgbm集成代码
7个规则、28个卷积网络(同一种网络的K折交叉算作不同模型)、1个LGB,一共36个模型。
集成的步骤:
1. lgb的训练:对于每个验证集样本,将36个单模型分别对其预测,得到36个模型的输出,再将36个模型的输出进行归一化(每个模型输出的9个数单独归一化,模型间互不干扰),并拼接成324的一维向量,将长度为324的特征作为lightgbm的输入,将样本的真实标注作为lightgbm的输出,训练一个lightgbm。
2. lgb的测试:对于每个测试集样本,将36个单模型分别对其预测,得到36个模型的输出,再将36个模型的输出进行归一化(每个模型输出的9个数单独归一化,模型间互不干扰),并拼接成324的一维向量,将长度为324的特征作为lightgbm的输入,利用1中训练好的lgb进行测试,得到每个测试样本的预测类别。
https://github.com/czczup/UrbanRegionFunctionClassification
https://github.com/ZHOUXINWEN/2019Baidu-XJTU_URFC
https://github.com/xjtuwxliang/URFC2019_Preliminary?tdsourcetag=s_pcqq_aiomsg
有几个准确率较低的弱模型,但是去掉这些弱模型均会导致集成后的准确率略微降低,因此我们在最终集成时保留了这些相关性低的弱模型。
1)每个CNN模型在验证集的准确率
图像分支网络结构 | 是否多折交叉 | 代码文件夹 | Acc |
---|---|---|---|
se_resnext101_32x4d | CNN/model1 | 0.6838 | |
se_resnext50_32x4d | CNN/model2 | 0.67364 | |
se_resnet50 | CNN/model3 | 0.67008 | |
resnet101 | CNN/0.71_resnet101 | 0.69878 | |
resnet50 | 5折的第1折 | CNN/0.71_resnet50_5fold | 0.6614 |
resnet50 | 5折的第2折 | CNN/0.71_resnet50_5fold | 0.71152 |
resnet50 | 5折的第3折 | CNN/0.71_resnet50_5fold | 0.7133 |
resnet50 | 5折的第4折 | CNN/0.71_resnet50_5fold | 0.70272 |
resnet50 | 5折的第5折 | CNN/0.71_resnet50_5fold | 0.72154 |
resnet50 | CNN/0.71 | 0.6733 | |
se_resnet50 | 10折的第1折 | CNN/A | 0.71006 |
se_resnet50 | 10折的第2折 | CNN/A | 0.6971 |
se_resnet50 | 10折的第3折 | CNN/A | 0.7095 |
se_resnet50 | 10折的第4折 | CNN/A | 0.68812 |
se_resnet50 | 10折的第5折 | CNN/A | 0.67054 |
se_resnet50 | 10折的第6折 | CNN/A | 0.67014 |
se_resnet50 | 10折的第7折 | CNN/A | 0.66088 |
se_resnet50 | 10折的第8折 | CNN/A | 0.66436 |
se_resnext50_32x4d | 10折的第2折 | CNN/B | 0.70258 |
se_resnext101_32x4d | 10折的第10折 | CNN/C | 0.7069 |
se_resnext101_32x4d | 10折的第1折 | CNN/C | 0.70774 |
se_resnext101_32x4d | 10折的第2折 | CNN/C | 0.67052 |
se_resnext101_32x4d | 10折的第5折 | CNN/C | 0.70472 |
se_resnext101_32x4d | 10折的第6折 | CNN/C | 0.70628 |
se_resnext101_32x4d | 10折的第7折 | CNN/C | 0.67204 |
se_resnext101_32x4d | 10折的第8折 | CNN/C | 0.675 |
se_resnext101_32x4d | 10折的第9折 | CNN/C | 0.70526 |
squeezenet1_0 | CNN/squeezenet1_0 | 0.59964 |
2)每个规则在验证集的准确率
规则 | 代码文件夹 | Acc | |
---|---|---|---|
基于可信用户的规则 | 基于严格高可信用户的规则 | rule/cxq | 0.7669 |
基于可信用户的规则 | 基于广义高可信用户的规则 | rule/cxq2 | 0.77696 |
基于时间的规则 | 基于天数的规则(剔除90%时长短的用户) | rule/day | 0.71704 |
基于时间的规则 | 基于平均时长的规则 | rule/hour | 0.7062 |
基于时间的规则 | 基于天数的规则 | rule/lyg_day | 0.7182 |
基于时间的规则 | 基于周末的规则 | rule/lyg_holiday | 0.68716 |
基于时间的规则 | 基于总时长的规则 | rule/totaltime | 0.68572 |
3)LGB基模型的验证集准确率
LGB基模型 | Acc |
---|---|
LGB | 0.6181 |