1. 案例介绍

  • 使用到的中间件:Apache Flink + Analytics Zoo + proxima
  • 开发语言:python
  • 数据:通过天池平台下载小型的训练集,选手自行将训练集划分成部分训练集和测试集,并在本地训练和调试算法,生成预测结果。
    在评测机器上,会给定四份数据集,分别是用于训练的历史行动数据集标签数据集,用于测试打分的确诊病例数据集实时行动数据集
    在历史行动数据集,确诊病例数据集和实时行动数据集中,不同的数据都可能代表同一个类(人)。历史行动数据集包括有确诊病例和非确诊病例,确诊病例数据集里只有确诊病例,并且每个确诊病例代表的人都曾经在历史行动数据集中都出现过。
  • 目标:1)对确诊病例数据集中的每个确诊病例,从历史行动数据集中找出该确诊病例代表的类(人)所对应的所有记录,并输出每条记录对应的face_id。2)对实时行动数据集的每一条数据,判断该条数据的类别(人),输出该条数据的device_id和类别。

需要修改的地方主要包括:
1)tianchi_main.py:flink算子编排
2)tianchi_executor.py、python_job_excutor.py:filink算子的具体内容。前面是在线预测的stream任务,后面的是离线训练的batch任务。
3)proxima_executor.py:建立index。话说proxima不是自带很多向量压缩的方法么?
4)python_job_executor.py:自定义了一个自编码器建立index。有必要这么麻烦么……

除了flatL2直接用原向量暴力计算距离外,其他所有的方法都需要先训练建立索引,然后再执行搜索。
如何边搜索边建立索引,貌似给的demo没有涉及(话说这才很关键啊)。

2. 案例分析

2.1 目标分析

本质上是一个reid问题。历史行动数据集和标签数据集合起来用,得到如下字典:pk->[feature1, feature2,…],[device_id_1,device_id_2,…],[face_id_1,face_id_2,…],为了后面方便,考虑加上每个摄像头之下的feature和跨摄像头下的feature。
两个问题分别对应两个数据:

  • 第一个是确诊病例数据,没有摄像头信息,数据为feature,从底库中找到所有的历史记录(找到pk,再输出[face_id])
  • 第二个是实时行动数据,有摄像头信息,数据为(device_id,feature),找到pk

2.2 运行样例解析

所有的文件都在tianchi_ai_flow下。在~/.bash_profile中添加如下:

PATH=$PATH:$HOME/bin:/root/kafka_2.11-2.3.0/bin
export JAVA_HOME=/usr/java/jdk1.8.0_171
export FLINK_HOME=/root/flink-1.11.1
export ENV_HOME=/root/***/tianchi
export TASK_ID=1
export PATH

然后执行source ~/.bash_profile,然后依次打开三个窗口,执行:

python $ENV_HOME/tianchi_ai_flow/ai_flow_master.py
python $ENV_HOME/tianchi_ai_flow/kafka-source.py
python $ENV_HOME/tianchi_ai_flow/package/python_codes/tianchi_main.py

2.2.1 启动并行推理引擎

封装为AI flow了。配置文件如下:

python flink wordcount代码 flink python案例_数据


启动如图:

python flink wordcount代码 flink python案例_数据集_02


随即同目录下出现aiflow.db文件,数据库中有如下表:

python flink wordcount代码 flink python案例_python_03

2.2.2 配置数据源

数据源见source.yaml,如下:

python flink wordcount代码 flink python案例_python_04


这里注册了两个topic,一个是read一个是write,而数据源如下:

python flink wordcount代码 flink python案例_python_05


下面是模拟生产数据的代码,从csv文件读取数据后,每隔0.1秒以key:value的形式一行行发布到bootstrap server上。

python flink wordcount代码 flink python案例_数据集_06

数据源使用kafka,一般来说会使用KafkaProducer,使用send方法发送信息到一个bootstrap_servers,并且可以注册多个topic,
使用python kafka-source.py启动服务。

2.2.3 启动主流程

配置文件在project.yaml中,里面仅仅指定了服务器:

python flink wordcount代码 flink python案例_数据集_07


第一步,读取数据,如下。这里要将下面提到的csv文件都放到data_set目录下。

python flink wordcount代码 flink python案例_数据集_08


第二步,将kafka的数据流等注册进并行推理引擎:

python flink wordcount代码 flink python案例_数据_09

2.2.4 训练索引

首先三个是离线训练任务

job_0读取csv数据,并使用trainAutoEncoder()方法对512维特征向量进行编码压缩(变为128维)。

job_1使用AI_flow对特征向量进行分布式预测(这里其实是在分布式做向量压缩)

job_2将预测结果合并

python flink wordcount代码 flink python案例_数据集_10


python flink wordcount代码 flink python案例_数据_11

2.2.5 预测新的数据

接下来开始是对数据流进行预测了,顾名思义,四个任务分别是:对历史数据进行特征向量压缩、对新数据建立index、查找sick的类别、在线预测。最后两个任务是本次案例的两个目标,所以有write_result节点。

python flink wordcount代码 flink python案例_数据集_12


python flink wordcount代码 flink python案例_python_13

3. 核心代码

3.1 训练索引

使用自编码器模型,代码使用的是keras架构,训练好的模型保存在model目录下。

python flink wordcount代码 flink python案例_python_14

python flink wordcount代码 flink python案例_数据_15

3.2 分布式推理

python flink wordcount代码 flink python案例_数据_16


已经封装在AI_flow里面了,调用af.cluster_serving即可。

python flink wordcount代码 flink python案例_数据_17

3.3 流式算子

算子的编写工作全都放在了tianchi_executor.py中,我们举个例子看看:

python flink wordcount代码 flink python案例_数据_18


这段代码是注册了一个udf算子predict2,虽然是输入一个、输出一个,但还是要包装成输入list,输出list。下面这几个是sink算子,可以参考示例代码:

python flink wordcount代码 flink python案例_数据_19