BasicAI 工作流标注绩效实现方案

BasicAI SaaS 的工作流标注是为了满足专业的标注团队对大量数据进行标注,既然是团队工作,那么就需要监管团队成员的工作进度和质量,也就是工作绩效管理。由于工作绩效需要细到标注对象(Object)的粒度,并且还需要按工作阶段和时间周期统计,导致计算逻辑非常复杂,并且计算量也很大。工作绩效还用于给标注人员结算工资,因此其准确性要求也很高。

BasicAI 工作流标注绩效实现方案

BasicAI 是全球首个开源多模态训练数据平台,通过提供 AI 赋能的软件工具、数万项目提炼的本体中心和丰富的数据治理特性,来加速多模态训练数据的处理效率,进而提高 AI 工程师的建模效率。

需求及限制

相比于数据流只适合于算法工程师少量数据标注,工作流是为了满足专业的标注团队对大量数据进行标注,同时还需要对标注进度和质量进行监管。标注团队管理员创建 Task 来对某个数据集中的全部或部分数据按指定的要求进行标注,并从团队中分配一些标注人员来完成这些工作。整个标注过程包含标注(Annotate)、审核(Review)、验收(Accept)等多个 Stage(阶段),其中审核可以有多个,每个标注人员可以参与其中的一个或多个阶段,验收由甲方客户来完成。如果审核和验收不通过,可以打回到前面的阶段。绩效分为完成绩效和提交绩效两类,完成绩效用于结算(给标注人员算钱),只需在某个 Data 验收通过后计算其相关绩效即可,而提交绩效用于监管过程,包括进度和质量。

  1. Task 完成绩效不分 Stage,Worker 完成绩效、Task 提交绩效和 Worker 提交绩效分 Stage(不汇总 ALL Stage);
  2. Task 完成绩效、Worker 完成绩效、Task 提交绩效和 Worker 完成绩效都按 Tool 和 Class 细分;
  3. 完成绩效可以选择任意时间段来汇总查看,提交绩效不行,因为单个 Data 可能在多个时间点产生提交绩效,汇总时需要去重,无法通过简单的累加来实现;
  4. Edit 和 Review Object 在每个 Stage 只算作最后一个人的绩效;
  5. 重置结果时需清空 Data 绩效事件和提交绩效,只有打回到第一个 Stage 时才允许清空结果;
  6. 导入的初始结果算作 Import Object 绩效;

库表设计

设计思路

产品功能上要求能够按 Task、Data Type 和 Scenario Type,以及任意时间段来汇总绩效,并且部分绩效指标还需要以天、周、月等周期来汇总,这么复杂的查询条件使得难以通过预计算来提前算出各种条件组合的查询结果,只能存储较细粒度的绩效并在查询时实时聚合来实现。考虑到数据是以 Data 为粒度流转,并且各个 Data 之间的绩效不会互相影响,可以直接累加,因此以 Data 作为绩效存储粒度是合适的。对于完成绩效,Data 的完成时间决定了其绩效所属时间点,因此可以根据完成时间来判断某个 Data 的绩效是否属于某个时间段或者周期。对于提交绩效,反应的是过程绩效,由于单个 Data 的提交绩效会分布在多个时间点(提交时间),且不能把多个时间点的提交绩效简单累加来得到某个时间段或周期的提交绩效(比如同一天内 Edit 和 Review 同一个 Object 只能算一个),因此只能预先按预设周期汇总好单个 Data 的提交绩效(需要在周期内去重)。查询的时候可以按预设周期查看提交绩效,但不能选择任意时间段来查看。

完成绩效

task_completed_performance (Task 完成绩效)

字段
类型
描述
备注
id
long
ID

team_id
long
Team ID

task_id
long
Task ID

data_id
long
Data ID

data_type
string
Data 类型

scene_id
long
Scene ID,Null 表示非连续帧

completed_at
datetime
完成时间

is_data_valid
bool
Data 是否有效

submit_count
int
提交次数

redo_count
int
重做次数,包括 Reject 和 Reassign

reset_count

int
重置次数,Reject 和 Reassign 时选择了重置结果

is_correct
bool
标注是否准确

create_object_total_count
int
创建对象总数

create_object_error_count
int
创建对象错误数

  1. Task Data 通过率 = Task Data 数量 * Task Stage 数量 / 提交次数;
  2. Reject Data 数量和 Reset Data 数量分别依据 redo_count 和 reset_count 是否大于 0 来计算;

task_completed_performance_by_tool (按 Tool 细分的 Task 完成绩效)

字段
类型
描述
备注
id
long
ID

team_id
long
Team ID

task_id
long
Task ID

data_id
long
Data ID

data_type
string
Data 类型

is_data_valid
bool
Data 是否有效

completed_at
datetime
完成时间

tool_type
string
Tool 类型

create_object_count
int
创建对象的数量

edit_object_count
int
编辑对象的数量

delete_object_count
int
删除对象的数量

review_object_count
int
审核对象的数量

accept_object_count
int
接收对象的数量

task_completed_performance_by_class (按 Class 细分的 Task 完成绩效)

字段
类型
描述
备注
id
long
ID

team_id
long
Team ID

task_id
long
Task ID

data_id
long
Data ID

data_type
string
Data 类型

is_data_valid
bool
Data 是否有效

completed_at
datetime
完成时间

class_id
long
Class ID

create_object_count
int
创建对象的数量

edit_object_count
int
编辑对象的数量

delete_object_count
int
删除对象的数量

review_object_count
int
审核对象的数量

accept_object_count
int
接收对象的数量

task_worker_completed_performance (Worker 完成绩效)

字段
类型
描述
备注
id
long
ID

team_id
long
Team ID

task_id
long
Task ID

stage_id
long
Stage ID,不能为 Null
-100 表示 Need Acceptance Stage
data_id
long
Data ID

data_type
string
Data 类型

scene_id
long
Scene ID,Null 表示非连续帧

worker_id
long
Worker ID

is_data_valid
bool
Data 是否有效

is_final_worker
bool
是否为最后工作者,决定了完成 Data 的数量

completed_at
datetime
完成时间

working_duration
int
工作时长,秒

submit_count
int
提交次数

redo_count
int
重做次数,包括 Reject 和 Reassign

reset_count

int

重置次数,Reject 和 Reassign 时选择了重置结果

create_object_total_count
int
创建对象总数

create_object_error_count
int
创建对象错误数

edit_object_count
int
编辑对象的数量

delete_object_count
int
删除对象的数量

review_object_total_count
int
审核对象总数

review_object_error_count
int
审核对象错误数

accept_object_count
int
接收对象的数量

  1. Worker Stage 通过率 = 1 - Stage 重做次数 / Stage 提交次数;

task_worker_completed_performance_by_tool (按 Tool 细分的 Worker 完成绩效)

字段
类型
描述
备注
id
long
ID

team_id
long
Team ID

task_id
long
Task ID

stage_id
long
Stage ID,不能为 Null
-100 表示 Need Acceptance Stage
data_id
long
Data ID

data_type
string
Data 类型

worker_id
long
Worker ID

is_data_valid
bool
Data 是否有效

completed_at
datetime
完成时间

tool_type
string
Tool 类型

create_object_count
int
创建对象的数量

edit_object_count
int
编辑对象的数量

delete_object_count
int
删除对象的数量

review_object_count
int
审核对象的数量

accept_object_count
int
接收对象的数量

task_worker_completed_performance_by_class (按 Class 细分的 Worker 完成绩效)

字段
类型
描述
备注
id
long
ID

team_id
long
Team ID

task_id
long
Task ID

stage_id
long
Stage ID,不能为 Null
-100 表示 Need Acceptance Stage
data_id
long
Data ID

data_type
string
Data 类型

worker_id
long
Worker ID

is_data_valid
bool
Data 是否有效

completed_at
datetime
完成时间

class_id
long
Class ID

create_object_count
int
创建对象的数量

edit_object_count
int
编辑对象的数量

delete_object_count
int
删除对象的数量

review_object_count
int
审核对象的数量

accept_object_count
int
接收对象的数量

提交绩效

task_submitted_performance (Task 提交绩效)

字段
类型
描述
备注
id
long
ID

team_id
long
Team ID

task_id
long
Task ID

stage_id
long
Stage ID,不能为 Null
-100 表示 Need Acceptance Stage
data_id
long
Data ID

data_type
string
Data 类型

scene_id
long
Scene ID,Null 表示非连续帧

submit_at
datetime
提交时间

submit_count
int
提交次数

redo_count
int
重做次数,包括 Reject 和 Reassign

reset_count

int
重置次数,Reject 和 Reassign 时选择了重置结果

  1. Stage Data 通过率 = Stage Data 数量 / Stage 提交次数;
  2. All Stage Data 通过率 = All Stage Data 记录数 / All Stage 提交次数;
  3. All Stage 的可通过聚合多个 Stage 来得到;

task_submitted_performance_by_tool (按 Tool 细分的 Task 提交绩效)

字段
类型
描述
备注
id
long
ID

team_id
long
Team ID

task_id
long
Task ID

stage_id
long
Stage ID,不能为 Null
-100 表示 Need Acceptance Stage
data_id
long
Data ID

data_type
string
Data 类型

period_type

string

统计周期力度,day、week、month or all

period_index
int
统计周期序号,比如 20221114

tool_type
string
Tool 类型

create_object_count
int
创建对象的数量

edit_object_count
int
编辑对象的数量

delete_object_count
int
删除对象的数量

review_object_count
int
审核对象的数量

accept_object_count
int
接收对象的数量

task_submitted_performance_by_class (按 Class 细分的 Task 提交绩效)

字段
类型
描述
备注
id
long
ID

team_id
long
Team ID

task_id
long
Task ID

stage_id
long
Stage ID,不能为 Null
-100 表示 Need Acceptance Stage
data_id
long
Data ID

data_type
string
Data 类型

period_type
string
统计周期粒度,day、week、month or all

period_index
int
统计周期序号,比如 20221114

class_id
long
Class ID

create_object_count
int
创建对象的数量

edit_object_count
int
编辑对象的数量

delete_object_count
int
删除对象的数量

review_object_count
int
审核对象的数量

accept_object_count
int
接收对象的数量

worker_submitted_performance (Worker 提交绩效)

字段
类型
描述
备注
id
long
ID

team_id
long
Team ID

task_id
long
Task ID

stage_id
long
Stage ID,不能为 Null
-100 表示 Need Acceptance Stage
data_id
long
Data ID

data_type
string
Data 类型

scene_id
long
Scene ID,Null 表示非连续帧

worker_id
long
Worker ID

period_type

string
统计周期粒度,day、week、month or all


period_index
int
统计周期序号,比如 20221114

is_final_worker

bool
是否为当前周期内的最后工作者,决定了完成 Data 的数量

working_duration
int
工作时长,秒

submit_count
int
提交次数

redo_count
int
重做次数,包括 Reject 和 Reassign

reset_count

int

重置次数,Reject 和 Reassign 时选择了重置结果

create_object_total_count
int
创建对象总数

edit_object_count
int
编辑对象的数量

delete_object_count
int
删除对象的数量

review_object_total_count
int
审核对象总数

accept_object_count
int
接收对象的数量

  1. Worker Stage 通过率 = 1 - Stage 重做次数 / Stage 提交次数;

worker_submitted_performance_by_tool (按 Tool 细分的 Worker 提交绩效)

字段
类型
描述
备注
id
long
ID

team_id
long
Team ID

task_id
long
Task ID

stage_id
long
Stage ID,不能为 Null
-100 表示 Need Acceptance Stage
data_id
long
Data ID

data_type
string
Data 类型

worker_id
long
Worker ID

period_type

string

统计周期力度,day、week、month or all

period_index
int
统计周期序号,比如 20221114

tool_type
string
Tool 类型

create_object_count
int
创建对象的数量

edit_object_count
int
编辑对象的数量

delete_object_count
int
删除对象的数量

review_object_count
int
审核对象的数量

accept_object_count
int
接收对象的数量

worker_submitted_performance_by_class (按 Class 细分的 Worker 提交绩效)

字段
类型
描述
备注
id
long
ID

team_id
long
Team ID

task_id
long
Task ID

stage_id
long
Stage ID,不能为 Null
-100 表示 Need Acceptance Stage
data_id
long
Data ID

data_type
string
Data 类型

worker_id
long
Worker ID

period_type
string
统计周期粒度,day、week、month or all

period_index
int
统计周期序号,比如 20221114

class_id
long
Class ID

create_object_count
int
创建对象的数量

edit_object_count
int
编辑对象的数量

delete_object_count
int
删除对象的数量

review_object_count
int
审核对象的数量

accept_object_count
int
接收对象的数量

绩效事件

task_performance_event (Task 绩效事件)

字段
类型
描述
备注
id
long
ID

team_id
long
Team ID

task_id
long
Task ID

data_id
long
Data ID

events
json
事件列表,单个绩效事件数据结构参考下方描述

绩效事件数据结构

属性
类型
描述
备注
type
string
类型

workerId
long
Worker ID

stageId
long
Stage ID,Null 表示 Accept 阶段

payload
json
内容,依具体事件类型而定

time
datetime
事件发生时间

绩效事件

为了减少前后端沟通协作成本,绩效事件都在后端产生,但需要前端配合来采集一些数据,比如工作时长、Object 修改时间等。对于 Object 相关事件,考虑到自动保存,如果每次保存时都产生一系列的创建、编辑和删除事件,那么事件数量会急剧增加,因此最好是只在提交的时候产生这些事件。后端可以在标注结果表里增加一个草稿字段,保存时修改草稿字段,提交时对比草稿和正式字段的内容来得到 Object 的差异并写入相关事件,然后使用草稿内容覆盖正式内容并清空草稿。为了方便后端知道 Object 是否有修改,前端需要维护一个 Object 的版本号(从 1 开始,每次有修改就 +1),否则后端得一一去比对 Object 的所有属性。工作时长也有类似情况,自动保存时也会产生大量工作时长事件,为了减少事件数量,可以在标注结果表里维护一个自上次提交以来的累计工作时长,每次保存时只需要更新该字段,不产生事件,提交时使用当前的累计工作时长产生一个事件并重置工作时长。Reject 和 Reassign 时需要把标注结果表里的草稿内容转为正式并记录相关事件,工作时长也类似。

导入 Object

从其它来源导入 Object,首次提交结果时产生,包含初始化数据里的所有 Object,无论其是否被编辑或删除。

{
    "type": "IMPORT_OBJECT",
    "workerId": 1,
    "stageId": 1,
    "payload": {
        "objects": [
            {
                "id": "1",
                "toolType": "3D_OBJECT",
                "classId": 1
            },
            {
                "id": "2",
                "toolType": "2D_BOX",
                "classId": 2
            }
        ]
    },
    "time": "2022-11-11 15:40:00"
}

创建 Object

{
    "type": "CREATE_OBJECT",
    "workerId": 1,
    "stageId": 1,
    "payload": {
        "objects": [
            {
                "id": "1",
                "toolType": "3D_OBJECT",
                "classId": 1
            },
            {
                "id": "2",
                "toolType": "2D_BOX",
                "classId": 2
            }
        ]
    },
    "time": "2022-11-11 15:40:00"
}

编辑 Object

{
    "type": "EDIT_OBJECT",
    "workerId": 1,
    "stageId": 1,
    "payload": {
        "objects": [
            {
                "id": "1",
                "toolType": "3D_OBJECT",
                "classId": 1
            }
        ]
    },
    "time": "2022-11-11 15:40:00"
}

删除 Object

{
    "type": "DELETE_OBJECT",
    "workerId": 1,
    "stageId": 1,
    "payload": {
        "objects": [
            {
                "id": "2",
                "toolType": "2D_BOX",
                "classId": 2
            }
        ]
    },
    "time": "2022-11-11 15:40:00"
}

审核 Object

需要记录所有被审核的 Objects,审核时如果有创建、修改和删除 Objects 操作,需单独记录相应事件。

{
    "type": "REVIEW_OBJECT",
    "workerId": 1,
    "stageId": 1,
    "payload": {
        "objects": [
            {
                "id": "1",
                "toolType": "3D_OBJECT",
                "classId": 1
            }
        ]
    },
    "time": "2022-11-11 15:40:00"
}

接受 Object

{
    "type": "ACCEPT_OBJECT",
    "workerId": 1,
    "stageId": null,
    "payload": {
        "objects": [
            {
                "id": "1",
                "toolType": "3D_OBJECT",
                "classId": 1
            }
        ]
    },
    "time": "2022-11-11 15:40:00"
}

错误 Object

Accept 时记录。

{
    "type": "ERROR_OBJECT",
    "workerId": 1,
    "stageId": null,
    "payload": {
        "objects": [
            {
                "id": "1"
            }
        ]
     }
    "time": "2022-11-11 15:40:00"
}

提交 Data

{
    "type": "SUBMIT_DATA",
    "workerId": 1,
    "stageId": 1,
    "payload": null,
    "time": "2022-11-11 15:40:00"
}

拒绝 Data

{
    "type": "REJECT_DATA",
    "workerId": 1,
    "stageId": 2,
    "payload": {
        // Stage 回退路径,顺序
        "stageBackPath": [1],
        "isReset": false,
        "currentWorker": 2,
        "isChangeWorker": false
    },
    "time": "2022-11-11 15:40:00"
}

重分配 Data

{
    "type": "REASSIGN_DATA",
    "workerId": 1,
    "stageId": 2,
    "payload": {
        // Stage 回退路径,顺序
        "stageBackPath": [1],
        "isReset": false,
        "currentWorker": 2,
        "toWorker": 3
    },
    "time": "2022-11-11 15:40:00"
}

工作时长

前端每次保存(Save)时上报自上次保存以来的工作时长(注意中间可能有暂停),后端累计到下一次提交的工作时长里,然后在提交(Submit)时后端使用累计的工作时长生成本次提交的工作时长事件并重置累计工作时长。

{
    "type": "WORKING_DURATION",
    "workerId": 1,
    "stageId": 1,
    "payload": {
        // 工作时长,单位秒
        "duration": 3600
    },
    "time": "2022-11-11 15:40:00"
}

计算流程

批量计算

流式计算