

本文属于机器翻译版本。若本译文内容与英语原文存在差异，则一律以英文原文为准。

# 使用注释前和注释后 Lambda 函数
<a name="sms-custom-templates-step3-lambda-requirements"></a>

使用这些主题可以了解发送到注释前和注释后 Lambda 函数的请求语法，以及 Ground Truth 在自定义标注工作流程中使用的所需响应语法。

**Topics**
+ [注释前 Lambda](#sms-custom-templates-step3-prelambda)
+ [注释后 Lambda](#sms-custom-templates-step3-postlambda)

## 注释前 Lambda
<a name="sms-custom-templates-step3-prelambda"></a>

在将标注任务发送给工作人员之前，可以可选的调用注释前 Lambda 函数。

Ground Truth 向您的 Lambda 函数发送一个 JSON 格式的请求，以提供有关标注作业和数据对象的详细信息。

以下是 2 个 JSON 格式请求示例。

------
#### [ Data object identified with "source-ref" ]

```
{
    "version": "2018-10-16",
    "labelingJobArn": <labelingJobArn>
    "dataObject" : {
        "source-ref": <s3Uri>
    }
}
```

------
#### [ Data object identified with "source" ]

```
{
    "version": "2018-10-16",
    "labelingJobArn": <labelingJobArn>
    "dataObject" : {
        "source": <string>
    }
}
```

------

 以下列表包含注释前请求模式。每个参数的说明如下。
+ `version`（字符串）：这是 Ground Truth 内部使用的版本号。
+ `labelingJobArn`（字符串）：这是标注作业的 Amazon 资源名称 (ARN)。在使用 Ground Truth API 操作（如 `DescribeLabelingJob`）时，此 ARN 可用于引用标注作业。
+ `dataObject`（JSON 对象）：该键包含单一 JSON 行，可以来自您的输入清单文件，也可以来自 Amazon SNS。清单中的 JSON 行对象的大小最多可达 100 千字节，并且包含各种数据。对于非常基本的图像注释作业，`dataObject` JSON 可能只包含一个 `source-ref` 键，用于标识要注释的图像。如果数据对象（例如，一行文本）直接包含在输入清单文件中，则用 `source` 标识数据对象。如果您创建验证或调整作业，则此行可能包含来自前一个标注作业的标签数据和元数据。

以下标签示例显示了注释前请求的示例。这些示例请求中的每个参数都在选项卡式表下面进行了说明。

------
#### [ Data object identified with "source-ref" ]

```
{
    "version": "2018-10-16",
    "labelingJobArn": "arn:aws:sagemaker:us-west-2:111122223333:labeling-job/<labeling_job_name>"
    "dataObject" : {
        "source-ref": "s3://input-data-bucket/data-object-file-name"
    }
}
```

------
#### [ Data object identified with "source" ]

```
{
    "version": "2018-10-16",
    "labelingJobArn": "arn:aws:sagemaker:<aws_region>:111122223333:labeling-job/<labeling_job_name>"
    "dataObject" : {
        "source": "Sue purchased 10 shares of the stock on April 10th, 2020"
    }
}
```

------

作为返回对象，Ground Truth 需要如下格式的响应：

**Example 预期的返回数据**  

```
{
    "taskInput": <json object>,
    "isHumanAnnotationRequired": <boolean> # Optional
}
```

在前面的示例中，`<json object>` 需要包含自定义工作人员任务模板所需的*所有*数据。如果您正在执行一个边界框任务，其中指令始终保持不变，那么可能只是图像文件的 HTTP(S) 或 Amazon S3 资源。如果执行的是情绪分析任务，并且不同的对象可能有不同的选择，那么是作为字符串的对象引用和作为字符串数组的选项。

**`isHumanAnnotationRequired` 的含义**  
该值是可选的，因为它默认为 `true`。显式设置该值的主要使用案例是当您想要将此数据对象从人工标注范围中排除时。

如果清单中的对象多种多样，有些需要人工注释，有些则不需要，那么可以在每个数据对象中包含 `isHumanAnnotationRequired` 值。您可以在注释前 Lambda 中添加逻辑，以动态确定对象是否需要注释，并相应地设置该布尔值。

### 注释前 Lambda 函数的示例
<a name="sms-custom-templates-step3-prelambda-example"></a>

下面的基本注释前 Lambda 函数从初始请求访问 `dataObject` 中的 JSON 对象，并在 `taskInput` 参数中返回该对象。

```
import json

def lambda_handler(event, context):
    return {
        "taskInput":  event['dataObject']
    }
```

假设输入清单文件使用 `"source-ref"` 来标识数据对象，那么在与此注释前 Lambda 相同的标注作业中使用的工作人员任务模板必须包含类似下面的 Liquid 元素，以摄取 `dataObject`：

```
{{ task.input.source-ref | grant_read_access }}
```

如果输入清单文件使用 `source` 来标识数据对象，那么工作任务模板可以通过以下方式摄取 `dataObject`：

```
{{ task.input.source }}
```

下面的注释前 Lambda 示例包含一个逻辑，用于识别 `dataObject` 中使用的键，并在 Lambda 的返回语句中使用 `taskObject` 指向该数据对象。

```
import json

def lambda_handler(event, context):

    # Event received
    print("Received event: " + json.dumps(event, indent=2))

    # Get source if specified
    source = event['dataObject']['source'] if "source" in event['dataObject'] else None

    # Get source-ref if specified
    source_ref = event['dataObject']['source-ref'] if "source-ref" in event['dataObject'] else None

    # if source field present, take that otherwise take source-ref
    task_object = source if source is not None else source_ref

    # Build response object
    output = {
        "taskInput": {
            "taskObject": task_object
        },
        "humanAnnotationRequired": "true"
    }

    print(output)
    # If neither source nor source-ref specified, mark the annotation failed
    if task_object is None:
        print(" Failed to pre-process {} !".format(event["labelingJobArn"]))
        output["humanAnnotationRequired"] = "false"

    return output
```

## 注释后 Lambda
<a name="sms-custom-templates-step3-postlambda"></a>

当所有工作人员都注释了数据对象或达到 [https://docs.aws.amazon.com/sagemaker/latest/APIReference/API_HumanLoopConfig.html#SageMaker-Type-HumanLoopConfig-TaskAvailabilityLifetimeInSeconds](https://docs.aws.amazon.com/sagemaker/latest/APIReference/API_HumanLoopConfig.html#SageMaker-Type-HumanLoopConfig-TaskAvailabilityLifetimeInSeconds)（以先到者为准）时，Ground Truth 将这些注释发送到您的注释后 Lambda。此 Lambda 通常用于[注释整合](sms-annotation-consolidation.md)。

**注意**  
要查看合并后 Lambda 函数的示例，[请参阅-recipe 存储](https://github.com/aws-samples/aws-sagemaker-ground-truth-recipe/blob/master/aws_sagemaker_ground_truth_sample_lambda/annotation_consolidation_lambda.py)库[aws-sagemaker-ground-truth中的](https://github.com/aws-samples/aws-sagemaker-ground-truth-recipe) GitHub annotation\$1consolidation\$1lambda.py。

以下代码块包含注释后请求模式。下面的项目符号列表中描述了每个参数。

```
{
    "version": "2018-10-16",
    "labelingJobArn": <string>,
    "labelCategories": [<string>],
    "labelAttributeName": <string>,
    "roleArn" : <string>,
    "payload": {
        "s3Uri": <string>
    }
 }
```
+ `version`（字符串）：Ground Truth 内部使用的版本号。
+ `labelingJobArn`（字符串）：标注作业的 Amazon 资源名称 (ARN)。在使用 Ground Truth API 操作（如 `DescribeLabelingJob`）时，此 ARN 可用于引用标注作业。
+ `labelCategories`（字符串列表）：包括在控制台中指定或包含在标签类别配置文件中的标签类别和其他属性。
+ `labelAttributeName`（字符串）：标注作业的名称或创建标注作业时指定的标签属性名称。
+ `roleArn`（字符串）：您在创建标注作业时指定的 IAM 执行角色的 Amazon 资源名称 (ARN)。
+ `payload`（JSON 对象）：包含 `s3Uri` 键的 JSON，该键标识该数据对象的注释数据在 Amazon S3 中的位置。下面的第二个代码块显示了此注释文件的示例。

以下代码块包含注释后请求的示例。此示例请求中的每个参数都在代码块下面进行了说明。

**Example 注释后 Lambda 请求**  

```
{
    "version": "2018-10-16",
    "labelingJobArn": "arn:aws:sagemaker:us-west-2:111122223333:labeling-job/labeling-job-name",
    "labelCategories": ["Ex Category1","Ex Category2", "Ex Category3"],
    "labelAttributeName": "labeling-job-attribute-name",
    "roleArn" : "arn:aws:iam::111122223333:role/role-name",
    "payload": {
        "s3Uri": "s3://amzn-s3-demo-bucket/annotations.json"
    }
 }
```

**注意**  
如果没有工作人员处理数据对象并且已经达到 `TaskAvailabilityLifetimeInSeconds`，则数据对象被标记为失败，并且不包括在注释后 Lambda 调用中。

下面的代码块包含负载模式。这是由注释后 Lambda 请求 `payload` JSON 对象中的 `s3Uri` 参数指示的文件。例如，如果前面的代码块是注释后 Lambda 请求，则下面的注释文件位于 `s3://amzn-s3-demo-bucket/annotations.json`。

下面的项目符号列表中描述了每个参数。

**Example 注释文件**  

```
[
    {
        "datasetObjectId": <string>,
        "dataObject": {
            "s3Uri": <string>,
            "content": <string>
        },
        "annotations": [{
            "workerId": <string>,
            "annotationData": {
                "content": <string>,
                "s3Uri": <string>
            }
       }]
    }
]
```
+ `datasetObjectId`（字符串）：标识 Ground Truth 为发送到标注作业的每个数据对象分配的唯一 ID。
+ `dataObject`（JSON 对象）：标注的数据对象。如果数据对象包含在输入清单文件中并使用 `source` 键（例如，字符串）进行标识，则 `dataObject` 包括一个用于标识数据对象的 `content` 键。否则，数据对象的位置（例如，链接或 S3 URI）用 `s3Uri` 标识。
+ `annotations`（JSON 对象列表）：此列表包含单一 JSON 对象，用于表示工作人员为该 `dataObject` 提交的每个注释。单一 JSON 对象包含唯一的 `workerId`，可用于标识提交该注释的工作人员。`annotationData` 键包含下列值之一：
  + `content`（字符串）：包含注释数据。
  + `s3Uri`（字符串）：包含一个 S3 URI，用于标识注释数据的位置。

下表包含了您可以在不同类型注释的负载中找到的内容示例。

------
#### [ Named Entity Recognition Payload ]

```
[
    {
      "datasetObjectId": "1",
      "dataObject": {
        "content": "Sift 3 cups of flour into the bowl."
      },
      "annotations": [
        {
          "workerId": "private.us-west-2.ef7294f850a3d9d1",
          "annotationData": {
            "content": "{\"crowd-entity-annotation\":{\"entities\":[{\"endOffset\":4,\"label\":\"verb\",\"startOffset\":0},{\"endOffset\":6,\"label\":\"number\",\"startOffset\":5},{\"endOffset\":20,\"label\":\"object\",\"startOffset\":15},{\"endOffset\":34,\"label\":\"object\",\"startOffset\":30}]}}"
          }
        }
      ]
    }
]
```

------
#### [ Semantic Segmentation Payload ]

```
[
    {
      "datasetObjectId": "2",
      "dataObject": {
        "s3Uri": "s3://amzn-s3-demo-bucket/gt-input-data/images/bird3.jpg"
      },
      "annotations": [
        {
          "workerId": "private.us-west-2.ab1234c5678a919d0",
          "annotationData": {
            "content": "{\"crowd-semantic-segmentation\":{\"inputImageProperties\":{\"height\":2000,\"width\":3020},\"labelMappings\":{\"Bird\":{\"color\":\"#2ca02c\"}},\"labeledImage\":{\"pngImageData\":\"iVBOR...\"}}}"
          }
        }
      ]
    }
  ]
```

------
#### [ Bounding Box Payload ]

```
[
    {
      "datasetObjectId": "0",
      "dataObject": {
        "s3Uri": "s3://amzn-s3-demo-bucket/gt-input-data/images/bird1.jpg"
      },
      "annotations": [
        {
          "workerId": "private.us-west-2.ab1234c5678a919d0",
          "annotationData": {
            "content": "{\"boundingBox\":{\"boundingBoxes\":[{\"height\":2052,\"label\":\"Bird\",\"left\":583,\"top\":302,\"width\":1375}],\"inputImageProperties\":{\"height\":2497,\"width\":3745}}}"
          }
        }
      ]
    }
 ]
```

------

注释后 Lambda 函数可能包含类似下面的逻辑，以循环访问请求中包含的所有注释。有关完整示例，请参阅 [aws-sagemaker-ground-truth-](https://github.com/aws-samples/aws-sagemaker-ground-truth-recipe) recipe GitHub 存储库[中的 annotation\$1consolidation\$1lambda.py](https://github.com/aws-samples/aws-sagemaker-ground-truth-recipe/blob/master/aws_sagemaker_ground_truth_sample_lambda/annotation_consolidation_lambda.py)。在此 GitHub示例中，您必须添加自己的注释合并逻辑。

```
for i in range(len(annotations)):
    worker_id = annotations[i]["workerId"]
    annotation_content = annotations[i]['annotationData'].get('content')
    annotation_s3_uri = annotations[i]['annotationData'].get('s3uri')
    annotation = annotation_content if annotation_s3_uri is None else s3_client.get_object_from_s3(
        annotation_s3_uri)
    annotation_from_single_worker = json.loads(annotation)

    print("{} Received Annotations from worker [{}] is [{}]"
            .format(log_prefix, worker_id, annotation_from_single_worker))
```

**提示**  
对数据运行合并算法时，可以使用 AWS 数据库服务来存储结果，也可以将处理后的结果传回 Ground Truth。您返回给 Ground Truth 的数据会以合并注释清单的形式存储在 S3 存储桶中，该存储桶是在标注作业配置过程中指定用于输出的。

作为返回对象，Ground Truth 需要如下格式的响应：

**Example 预期的返回数据**  

```
[
   {        
        "datasetObjectId": <string>,
        "consolidatedAnnotation": {
            "content": {
                "<labelattributename>": {
                    # ... label content
                }
            }
        }
    },
   {        
        "datasetObjectId": <string>,
        "consolidatedAnnotation": {
            "content": {
                "<labelattributename>": {
                    # ... label content
                }
            }
        }
    }
    .
    .
    .
]
```
此时，除了 `datasetObjectId` 之外，您发送到 S3 存储桶的所有数据都在 `content` 对象中。

在 `content` 中返回注释时，会在作业的输出清单中生成一个条目，如下所示：

**Example 输出清单中的标签格式**  

```
{  "source-ref"/"source" : "<s3uri or content>", 
   "<labelAttributeName>": {
        # ... label content from you
    },   
   "<labelAttributeName>-metadata": { # This will be added by Ground Truth
        "job_name": <labelingJobName>,
        "type": "groundTruth/custom",
        "human-annotated": "yes", 
        "creation_date": <date> # Timestamp of when received from Post-labeling Lambda
    }
}
```

由于自定义模板及其所收集数据的潜在复杂性，Ground Truth 不会对数据进行进一步处理。