摘要:
合集:AI案例-传统机器学习-交通运输业
赛题:大数据与计算智能大赛CCF-BDCI-2021-泛在感知数据关联融合计算
AI问题:关联匹配问题(Association Matching Problem)。是一个二分类问题,判断人员和设备特征码是否匹配。
数据集:采用随机生成方式产生,分别模拟前端点位对人员的采集数据以及这些点位对某硬件设备特征码的采集数据。
数据集发布方:锐安科技
数据集价值:挖掘海量前端点位模拟采集数据,采用合适的算法精准感知数据关联,并进行融合计算。
解决方案:采用两阶段匹配方法:先基于时间进行粗匹配,再基于统计特征使用机器学习XGBoost模型进行精匹配。
一、赛题描述
近年来随着物联网、移动通信、前端感知等技术的高速发展,针对人员、车辆、设备等数据的泛在感知(Ubiquitous Sensing)采集通过遍布环境的感知设备(如摄像头、雷达、电子标签等)实时采集人员、车辆轨迹、设备状态等数据。这些感知手段往往相互独立,难以在前端直接对人、车、物的数据进行有效的关联融合。但通过设置多个感知点位,经过一定时间的数据积累,可以在后台进行大数据的关联分析,计算得到人、车、物之间的关联关系,实现泛在感知数据的关联融合。关联计算的准确率和海量数据的计算性能,是实际工作中的主要难点。
赛题提供两种感知手段分别采集到的数据,需要通过统计学方法、数据挖掘、图计算等相关算法将两种数据进行关联融合,给出两种关键要素之间的对应关系。
二、数据集描述
1、数据集内容
提供两个样本数据集,分别是:
- 人员特征采集样本集:CCF2021_run_record_p_Train.csv、CCF2021_run_record_p_EvalA.csv
- 硬件设备特征码采集样本集:CCF2021_run_record_c_Train.csv、CCF2021_run_record_c_EvalA.csv
- 人员-硬件设备特征标注数据集,用来标注人员编号和特征码之间的关系:CCF2021_run_label_Train.csv
训练集和测试集A、测试集B均包含这3类数据,是这3类数据的集合。对应实际场景,训练集可以是甲地标注的数据,测试集A和测试集B则是乙地、丙地的采集数据。
比赛的任务是为测试数据找到对应的关联特征码。以A榜阶段举例,开放训练集的3个文件:人员特征采集样本集、硬件设备特征码采集样本集、人员-硬件设备特征标注数据集,用于训练模型。同时开放测试集的2个文件:人员特征采集样本集、硬件设备特征码采集样本集,用于建立关联、提取特征。
参赛者需要根据训练集3个文件训练模型,再对测试集提取的样本特征,为每一个测试集中的人员从测试集的硬件设备特征码中找到其对应的匹配度最高的硬件设备特征码,并将结果输出为模型推理结果文件。
2、人员特征
训练数据集和测试集文件分别为:CCF2021_run_record_p_Train.csv、CCF2021_run_record_p_EvalA.csv
人员特征采集样本集包含5个字段,字段之间采用“,”分割,首行为字段中文名,字段格式及含义如下表所示。代码中映射为:df_Face.columns=[‘DeviceID’,’Lon’,’Lat’,’Time’,’FaceLabel’]
字段中文名 | 类型 | 描述 |
---|---|---|
点位编号 | String | 采集点位的唯一标识,由“D”+两位数字编码组成。 |
经度 | Float | 点位位置的经度值,-180度~180度。 |
纬度 | Float | 点位位置的纬度值,-90度~90度。 |
发现时间 | Datetime | 点位对人员的捕获采集时间,格式为yyyy-mm-dd HH:MM:SS.SSSSSS。 |
人员编号 | String | 采集的人员照片经特征提取等处理后得到的人员唯一标识,由“P”+三位数字编码组成。 |
数据样例:
点位编号 | 经度 | 纬度 | 发现时间 | 人员编号 |
---|---|---|---|---|
D00 | 118.3765 | 31.2352 | 10:42.9 | P0099 |
D00 | 118.3765 | 31.2352 | 12:01.3 | P0000 |
D00 | 118.3765 | 31.2352 | 12:21.2 | P0031 |
D00 | 118.3765 | 31.2352 | 12:26.1 | P0038 |
D00 | 118.3765 | 31.2352 | 12:31.4 | P0070 |
3、硬件设备特征码
训练数据集和测试集文件分别为文件:CCF2021_run_record_c_Train.csv、CCF2021_run_record_c_EvalA.csv
硬件设备特征码采集样本集包含5个字段,字段之间采用“,”分割,首行为字段中文名,字段格式及含义如下表所示。代码中映射为: df_Imsi.columns=[‘DeviceID’,’Lon’,’Lat’,’Time’,’Code’]
字段中文名 | 类型 | 描述 |
---|---|---|
点位编号 | String | 采集点位的唯一标识,由“D”+两位数字编码组成。 |
经度 | Float | 点位位置的经度值,-180度~180度。 |
纬度 | Float | 点位位置的纬度值,-90度~90度。 |
发现时间 | Datetime | 点位对特征码的捕获采集时间,格式为yyyy-mm-dd HH:MM:SS.SSSSSS。 |
特征码 | String | 采集的特征码,由“C”+7位编码组成。 |
数据样例:
点位编号 | 经度 | 纬度 | 发现时间 | 特征码 |
---|---|---|---|---|
D00 | 118.3765 | 31.2352 | 10:01.5 | CsZmYCFO |
D00 | 118.3765 | 31.2352 | 10:06.2 | CUX4LKr0 |
D00 | 118.3765 | 31.2352 | 10:06.2 | CJAslGMR |
D00 | 118.3765 | 31.2352 | 10:06.2 | C8oM1ibW |
D00 | 118.3765 | 31.2352 | 10:06.2 | CKOpg0bP |
4、人员-硬件设备特征码映射
训练数据集文件:CCF2021_run_label_Train.csv
字段中文名 | 类型 | 描述 |
---|---|---|
人员编号 | String | 人员唯一标识,由“P”+三位数字编码组成。按照升序进行排序。 |
硬件设备特征码 | String | 标注的与每个人员匹配的1个硬件设备特征码,由“C”+7位编码组成。 |
文件内容示例
人员编号 | 特征码 |
---|---|
P000 | Cc3K1aZj |
P001 | C1ISw9rQ |
P002 | CvxUR5cC |
P003 | CzPiHsBF |
5、预测提交文件
最终提交文件CCF2021_run_pred_EvalA.csv,包括“人员编号”列和”特征码Top1“列。人员编号对应于CCF2021_run_record_p_EvalA.csv中的人员信息。特征码对应于CCF2021_run_record_c_EvalA.csv中的硬件设备特征码中概率最大一个。
数据集版权许可协议
BY-NC-SA 4.0
数据集发布方:锐安科技
https://creativecommons.org/licenses/by-nc-sa/4.0/deed.zh-hans
三、解决方案样例
1、工作原理
源码:run_baseline.py 为基线解决方案。
该方案解决的是一个时空关联匹配问题(Association Matching Problem),更具体地说,是一个二分类问题。
- 目标:判断”人员(FaceLabel)”和”设备特征码(Code)”是否属于同一个人(是否匹配)。
- 形式:对每一对 <FaceLabel, Code> 组合,预测它们是否关联(1=匹配,0=不匹配)。
- 输出:概率值(通过 model.predict_proba() 生成),最终选择概率最高的匹配对。
该解决方案本质上是一个两阶段匹配方法:先基于时间进行粗匹配,再基于统计特征使用机器学习模型进行精匹配。
2、主要特点
- 时间窗口匹配:首先基于时间差进行初步筛选,减少计算量
- 统计特征:使用简单的计数统计作为特征
- 处理类别不平衡:通过过采样和设置scale_pos_weight来处理正负样本不平衡
- 设备自适应阈值:不同设备使用不同的时间差阈值
3、机器学习框架
XGBoost(eXtreme Gradient Boosting)是一种高效、灵活且强大的机器学习算法,特别在结构化/表格数据的预测任务中表现出色。它属于梯度提升决策树(Gradient Boosting Decision Trees, GBDT)算法家族,但在性能和效率上进行了多项创新优化。
XGBoost核心特点是:
- 高性能
- 通过并行处理和优化算法实现极快的训练速度
- 支持分布式计算(Hadoop, Spark)
- 内存使用效率高
- 正则化
- 在目标函数中加入正则化项(L1/L2),有效防止过拟合
- 正则化控制模型复杂度,提高泛化能力
- 灵活性
- 支持多种损失函数(回归、分类、排序等)
- 可自定义优化目标和评估指标
- 缺失值处理
- 自动学习缺失值的最佳处理方式
- 无需额外预处理步骤
- 树结构优化
- 精确的贪心算法和近似算法寻找最佳分裂点
- 支持剪枝和交叉验证
4、运行环境
外部库名称 | 版本号 |
---|---|
python | 3.11.9 |
pandas | 2.1.4 |
numpy | 1.26.4 |
xgboost | 3.0.2 |
四、工作流程
工作流程:
- 加载并预处理训练数据
- 计算各设备的最佳匹配时间差阈值
- 基于时间差进行初步关联匹配
- 从匹配结果中提取统计特征
- 训练XGBoost分类器
- 评估模型性能
- 对评估数据重复2-4步
- 使用训练好的模型预测并输出结果
1. 数据准备阶段
数据加载
从CSV文件加载三种数据:
CCF2021_run_record_c_Train.csv
:特征码(IMSI)记录数据CCF2021_run_record_p_Train.csv
:人员记录数据CCF2021_run_label_Train.csv
:人员与特征码的对应标签数据
数据预处理
- 对时间数据进行转换:将字符串时间转换为datetime对象和时间戳
- 创建标签字典
dict_label
,将特征码映射到对应的人员编号
对于代码:
for tup in zip(df_label['人员编号'], df_label['特征码']):
dict_label[tup[1]] = tup[0]
- 使用
zip
函数将DataFrame中的两列(‘人员编号’和’特征码’)组合成元组 - 遍历这些元组
- 将每个元组中的第二个元素(特征码)作为字典的键
- 将第一个元素(人员编号)作为对应的值
- 这样就将DataFrame中的数据转换成了字典格式
path_imsi = './data/CCF2021_run_record_c_Train.csv'
path_face = './data/CCF2021_run_record_p_Train.csv'
df_Imsi=pd.read_csv(path_imsi,dtype=str)
df_Imsi.columns=['DeviceID','Lon','Lat','Time','Code']
df_Imsi['Time1']=pd.to_datetime(df_Imsi['Time'])
df_Imsi['TimeStamp']=[int(t.timestamp()) for t in df_Imsi['Time1']]
df_Face=pd.read_csv(path_face,dtype=str)
df_Face.columns=['DeviceID','Lon','Lat','Time','FaceLabel']
df_Face['Time1']=pd.to_datetime(df_Face['Time'])
df_Face['TimeStamp']=[int(t.timestamp()) for t in df_Face['Time1']]
path_label = './data/CCF2021_run_label_Train.csv'
df_label = pd.read_csv(path_label,dtype=str)
dict_label = {}
for tup in zip(df_label['人员编号'], df_label['特征码']):
dict_label[tup[1]] = tup[0]
2. 核心功能
时间差阈值计算 (getDeltaSeconds
)
- 计算每个设备(DeviceID)上人员和特征码匹配的最佳时间差阈值
- 使用两种统计方法确定阈值:
- 3倍标准差方法
- 箱线图四分位法
- 取两种方法结果中的较小值作为最终阈值
def getDeltaSeconds(df_Face, df_Imsi):
df_FaceCode = pd.merge_asof(df_Face, df_Imsi, on=["TimeStamp"], by=["FaceLabel", "DeviceID"], tolerance=maxDeltaT, direction="nearest")
df_FaceCode["TimeDelta"] = df_FaceCode.apply(lambda x:pd.Timedelta(seconds=0) if(pd.isna(x['Time1_x']) or pd.isna(x['Time1_y'])) else abs(x['Time1_x'] - x['Time1_y']), axis=1) #无法处理NaN
df_FaceCode['TimeDelta'] = df_FaceCode['TimeDelta'].fillna(pd.Timedelta(seconds=0))
df_FaceCode['TimeDeltaSeconds'] = df_FaceCode['TimeDelta'].map(lambda x: x.seconds)
df_min = df_FaceCode.groupby(["FaceLabel", "DeviceID"])["TimeDeltaSeconds"].min()
df_describe = df_min.groupby(["DeviceID"]).describe()
df_describe['edge1'] = df_describe['mean'] + 3 * df_describe['std'] #3倍标准差
df_describe['edge2'] = df_describe['75%'] + 1.5 * (df_describe['75%'] - df_describe['25%']) # 箱线图四分位确定
#print(df_describe)
deltaSeconds = df_describe.apply(lambda x: min(x['edge1'], x['edge2'], x['max']), axis=1)
return deltaSeconds
初步关联匹配 (get_MatchRecord
)
- 基于时间差阈值,将人员记录与特征码记录进行初步关联
- 对于每个人员记录,查找同一设备上时间差小于阈值的所有特征码记录
# 根据一定的特征时间初步关联图码数据-全关联
def get_MatchRecord(imsi,face, deltaSeconds):
match_record2=[]
for i in face.index:
deviceID=face['DeviceID'][i]
threshold2=deltaSeconds[deviceID]
f=face['FaceLabel'][i]
ts=face['TimeStamp'][i]
match_imsi2=imsi[(imsi['DeviceID']==deviceID) & (abs(imsi['TimeStamp']-ts)<threshold2)]['Code']
if len(match_imsi2)>0:
for code in match_imsi2:
match_record2.append([f,code,ts,deviceID])
df_Match2=pd.DataFrame(match_record2,columns=['FaceLabel','Code','TimeStamp','DeviceID'])
return df_Match2
特征工程 (genFeature
)
模型训练时候,生成三种统计特征:
countT
:每个人员FaceLabel
出现总次数countM
:每个特征码Code
出现总次数countTM
:每对(FaceLabel, Code)
的共现次数
模型训练的输出变量是 label,表示人员和设备编码关联是否正确匹配。label 的生成逻辑如下:
- 如果 Code 对应的真实 FaceLabel(通过 dict_label 映射)与当前记录的 FaceLabel 一致,则 label 为 1(匹配)。
- 否则,label 为 0(不匹配)。
函数label() 在 res[‘label’] = res.apply(label,axis=1) 中被调用:
#标记图码关联,生成训练数据的label
def label(row):
p = row['FaceLabel']
c = row['Code']
#if(c.endswith(p)):
if dict_label.get(c) == p:
return 1
else:
return 0
生成特征工程 genFeature()
# 基于图码预关联的数据,进行特征提取的准备工作
def genFeature(df_Match, df_Face, df_Imsi, deltaSeconds):
listDeviceID = list(deltaSeconds.keys())
listDeviceID.sort()
#图的总次数
df_T_temp3 = df_Face.groupby('FaceLabel').count().reset_index()[['FaceLabel','DeviceID']]
df_T_temp3.columns = ['FaceLabel','countT']
df_T_count = df_T_temp3['countT']
df_T_temp3['countT'] = df_T_count
df_T = df_T_temp3
#码的总次数
df_M_temp3 = df_Imsi.groupby('Code').count().reset_index()[['Code','DeviceID']]
df_M_temp3.columns = ['Code','countM']
df_M_count = df_M_temp3['countM']
df_M_temp3['countM'] = df_M_count
df_M = df_M_temp3
#图码关联的总次数
df_TM_temp3 = df_Match.groupby(['FaceLabel','Code']).count().reset_index()[['FaceLabel','Code', 'DeviceID']]
df_TM_temp3.columns = ['FaceLabel','Code','countTM']
df_TM_count = df_TM_temp3['countTM']
df_TM_temp3['countTM'] = df_TM_count
df_TM = df_TM_temp3
#汇总所有特征
res = df_TM.merge(df_T,on='FaceLabel',how='left').merge(df_M, on='Code', how='left')
return res
3. 模型训练
- 数据划分:按
FaceLabel
的哈希值划分训练集(70%)/测试集(30%) - 样本构建:
- 正样本:标签匹配的记录(
label=1
) - 负样本:按
countTM
加权采样(数量=正样本×100倍)
- 正样本:标签匹配的记录(
- 模型选择:
XGBClassifier
参数:scale_pos_weight=100
(解决正负样本不均衡)learning_rate=0.05
df = get_MatchRecord(df_Imsi,df_Face, deltaSeconds)
res = genFeature(df,df_Face,df_Imsi, deltaSeconds)
res['label'] = res.apply(label,axis=1) # 调用函数label()
res['testflag'] = res.apply(testflag, axis=1)
train = res[res['testflag'] == 0]
test = res[res['testflag'] == 1]
X = res[res.columns[2:-2]]
y = res['label']
X_train = train[train.columns[2:-2]] #First 2 Columns: FaceLabel Code Last 2 columns: label testflag
y_train = train['label']
model = XGBClassifier(scale_pos_weight=100, learning_rate=0.05, random_state=1000)
model.fit(X,y)
4. 评估与预测
- 训练集评估:
- 对每个
FaceLabel
选取概率最高的Code
作为预测值 - 计算准确率:
正确匹配数/总预测数
- 对每个
- 评估集预测:
- 复用训练好的模型
- 输出格式:
(人员编号, 特征码Top1)
- 保存为
CCF2021_run_pred_EvalA.csv
df = get_MatchRecord(df_Imsi,df_Face, deltaSeconds)
res = genFeature(df,df_Face,df_Imsi, deltaSeconds)
resX = res[res.columns[2:]]
probability = model.predict_proba(resX)[:,1]
res['probability'] = pd.Series(probability)
xgb_temp = res.groupby("FaceLabel").apply(lambda t: t[t.probability==t.probability.max()].iloc[0])
path_pred = "CCF2021_run_pred_EvalA.csv"
df_pred = pd.DataFrame(zip(xgb_temp['FaceLabel'], xgb_temp['Code']), columns=['人员编号', '特征码Top1']).sort_values(by=['人员编号'])
df_pred.to_csv(path_pred,index=False)
运行结果展示
运行 python run_baseline.py 过程输出如下:
xgboost计算的正确率为: 1251 1997 0.6264396594892339
最终提交文件CCF2021_run_pred_EvalA.csv,包括“人员编号”列和”特征码Top1“列。输出数据样例:
人员编号 | 特征码Top1 |
---|---|
P0000 | CbsZdD70 |
P0001 | Ct37PTcS |
P0002 | C9Yu7nHp |
P0003 | Cp3g58MG |
P0004 | C2HLXJ3m |
P0005 | C4EpecoJ |
… | … |
源码开源协议
GPL-v3
CCF2021 算法基准程序
@author: zhouwei