本赛题基于位置数据对海上目标进行智能识别和作业行为分析,要求选手通过分析渔船北斗设备位置数据,得出该船的生产作业行为,具体判断出是拖网作业、围网作业还是流刺网作业。初赛将提供11000条(其中7000条训练数据、2000条testA、2000条testB)渔船轨迹北斗数据。
初赛提供11000条渔船北斗数据,数据包含脱敏后的渔船ID、经纬度坐标、上报时间、速度、航向信息,由于真实场景下海上环境复杂,经常出现信号丢失,设备故障等原因导致的上报坐标错误、上报数据丢失、甚至有些设备疯狂上报等。
「数据示例:」
渔船ID | x | y | 速度 | 方向 | time | type |
---|---|---|---|---|---|---|
1102 | 6283649.656204367 | 5284013.963699763 | 3 | 12.1 | 0921 09:00 | 围网 |
渔船ID:渔船的唯一识别,结果文件以此ID为标示
x: 渔船在平面坐标系的x轴坐标
y: 渔船在平面坐标系的y轴坐标
速度:渔船当前时刻航速,单位节
方向:渔船当前时刻航首向,单位度
time:数据上报时刻,单位月日 时:分
type:渔船label,作业类型
原始数据经过脱敏处理,渔船信息被隐去,坐标等信息精度和位置被转换偏移。选手可通过学习围网、刺网、拖网等专业知识辅助大赛数据处理。
提交结果与实际渔船作业类型结果进行对比,以3种类别的各自F1值取平均做为评价指标,结果越大越好,具体计算公式如下:
其中P为某类别的准确率,R为某类别的召回率,评测程序f1函数为sklearn.metrics.f1_score,average='macro'。
Baseline Auhtor | 阿水 |
---|---|
Baseline Address | Baseline |
Baseline Star | 38 |
Baseline Score | 0.62 |
阿水的Baseline是本赛题比较有代表性的方案,代码风格简介,对于初学者比较友好。同时 「数据->特征->算法」 的框架方便特征工程的快速迭代。
import os, sys, glob
import numpy as np
import pandas as pd
import time
import datetime
from joblib import Parallel, delayed
from sklearn.metrics import f1_score, log_loss, classification_report
from sklearn.model_selection import StratifiedKFold
import lightgbm as lgb
%pylab inline
def read_feat(path, test_mode=False):
df = pd.read_csv(path)
df = df.iloc[::-1]
if test_mode:
df_feat = [df['渔船ID'].iloc[0], df['type'].iloc[0]]
df = df.drop(['type'], axis=1)
else:
df_feat = [df['渔船ID'].iloc[0]]
df['time'] = df['time'].apply(lambda x: datetime.datetime.strptime(x, "%m%d %H:%M:%S"))
df_diff = df.diff(1).iloc[1:]
df_diff['time_seconds'] = df_diff['time'].dt.total_seconds()
df_diff['dis'] = np.sqrt(df_diff['x']**2 + df_diff['y']**2)
df_feat.append(df['time'].dt.day.nunique())
df_feat.append(df['time'].dt.hour.min())
df_feat.append(df['time'].dt.hour.max())
df_feat.append(df['time'].dt.hour.value_counts().index[0])
# 此处省略 N 段代码
return df_feat
由于本赛题每个ID都独立成一个csv文件,可以看到阿水把利用文件名传参的方式对每个ID进行独立的特征处理。
train_feat = Parallel(n_jobs=10)(delayed(read_feat)(path, True)
for path in glob.glob('../input/hy_round1_train_20200102/*')[:])
train_feat = pd.DataFrame(train_feat)
test_feat = Parallel(n_jobs=10)(delayed(read_feat)(path, False)
for path in glob.glob('../input/hy_round1_testA_20200102/*')[:])
test_feat = pd.DataFrame(test_feat)
test_feat = test_feat.sort_values(by=0)
train_feat[1] = train_feat[1].map({'围网':0,'刺网':1,'拖网':2})
这里利用Parallel进行了并行化处理,提升了计算效率。
from sklearn.model_selection import StratifiedKFold
from sklearn.metrics import f1_score
n_fold = 10
skf = StratifiedKFold(n_splits = n_fold, shuffle = True)
eval_fun = f1_score
def run_oof(clf, X_train, y_train, X_test, kf):
# 此处省略N行
return preds_train, preds_test
params = {# 此处省略N行}
train_pred, test_pred = run_oof(lgb.LGBMClassifier(**params),
train_feat.iloc[:, 2:].values,
train_feat.iloc[:, 1].values,
test_feat.iloc[:, 1:].values,
skf)
笔者在此开源的基础上,只修改了部分特征工程,线上可以有 「0.8913」 的分数,同时并行化也使得线下迭代验证效率得到了保障。
Baseline Auhtor | 蔡君洋jioooo~ |
---|---|
Baseline Address | Baseline |
Baseline Star | 18 |
Baseline Score | 0.8729 |
相对于Baseline 1,该Baseline的亮点在于做了一些EDA工作,对于理解题目很有帮助。
对不同标签的的(x,y)位置可视化我们可以发现,这基本就是沿海的轮廓。
同时,不同标签在坐标上的区分度还是较为明显的。
该Baseline对Xgboost,LightGBM,CatBoost模型做了融合,同时做了加权融合。
test_end_score | xgboost | lightgbm | catboost | weight_xgboost | weight_lightgbm | weight_catboost |
---|---|---|---|---|---|---|
0 | 0.931042 | 0.930866 | 0.917411 | 0.877637 | 0.5 | 0.2 |
1 | 0.914347 | 0.901870 | 0.884444 | 0.879513 | 0.6 | 0.3 |
2 | 0.927533 | 0.907810 | 0.923235 | 0.888009 | 0.2 | 0.5 |
3 | 0.912230 | 0.891184 | 0.877702 | 0.894567 | 0.1 | 0.2 |
4 | 0.904197 | 0.894321 | 0.895069 | 0.899206 | 0.2 | 0.7 |
5 | 0.929959 | 0.918331 | 0.894536 | 0.902057 | 0.7 | 0.2 |
6 | 0.930583 | 0.915332 | 0.930583 | 0.905171 | 0.0 | 1.0 |
7 | 0.904270 | 0.903738 | 0.885791 | 0.890281 | 0.4 | 0.2 |
8 | 0.931706 | 0.926309 | 0.915993 | 0.918455 | 0.4 | 0.3 |
9 | 0.918609 | 0.909511 | 0.918609 | 0.885122 | 0.0 | 1.0 |
10 | 0.906875 | 0.890628 | 0.867425 | 0.865150 | 0.7 | 0.0 |
11 | 0.889217 | 0.878888 | 0.867252 | 0.866126 | 0.3 | 0.3 |
12 | 0.919569 | 0.907833 | 0.884178 | 0.893573 | 0.5 | 0.0 |
13 | 0.904401 | 0.895870 | 0.901480 | 0.888129 | 0.0 | 0.9 |
14 | 0.908233 | 0.896810 | 0.885806 | 0.856757 | 0.4 | 0.5 |
15 | 0.919484 | 0.900211 | 0.872883 | 0.911664 | 0.2 | 0.0 |
16 | 0.905847 | 0.883122 | 0.893712 | 0.879878 | 0.2 | 0.2 |
17 | 0.902063 | 0.899060 | 0.896116 | 0.884067 | 0.2 | 0.4 |
18 | 0.938963 | 0.933309 | 0.914183 | 0.895051 | 0.7 | 0.0 |
19 | 0.914048 | 0.913054 | 0.882581 | 0.876354 | 0.4 | 0.2 |
Baseline Auhtor | wbbhcb |
---|---|
Baseline Address | Baseline |
Baseline Star | 21 |
Baseline Score | 0.5 |
相对于Baseline 1和2,该Baseline的亮点在于提供了一个NN版的思路,虽然该思路的做法,用图片的形式进行分类,损失了很多特征信息,但是依旧不妨碍作为一个Baseline思路的存在。
def train_epoch(model, optimizer, criterion, train_dataloader, epoch, lr, best_f1, batch_size):
model.train()
f1_meter, loss_meter, it_count = 0, 0, 0
tq = tqdm.tqdm(total=len(train_dataloader)*batch_size)
tq.set_description('folds: %d, epoch %d, lr %.4f, best_f:%.4f' % (fold_+1, epoch, lr, best_f1))
for i, (inputs, target) in enumerate(train_dataloader):
inputs = inputs.to(device)
target = target.to(device)
# zero the parameter gradients
optimizer.zero_grad()
# forward
output = model(inputs)
output = F.softmax(output, dim=1)
loss = criterion(output, target)
loss.backward()
optimizer.step()
loss_meter += loss.item()
it_count += 1
f1 = calc_f1(target, output)
f1_meter += f1
tq.update(batch_size)
tq.set_postfix(loss="%.4f f1:%.3f" % (loss.item(), f1))
tq.close()
return loss_meter / it_count, f1_meter / it_count
DOTA数据科学从业者
国内某知名互联网企业算法工程师
曾获:DiggSci亚军,JDATA季军,国内竞赛N次Top10
历史精品文章推荐
1、知否?知否?一文看懂深度文本分类之DPCNN原理与代码
2、CCL“中国法研杯”相似案例匹配评测竞赛 - TOP队伍攻略分享
3、推荐|机器学习入门方法和资料合集
个人微信:加时请注明 (昵称+公司/学校+方向)