发布于: Oct 18, 2022

先决条件

在本演练中,我们需要一个拥有适当 Amazon Web Services 身份与访问管理(IAM)权限的 Amazon Web Services 账户,用以启动 Amazon Web Services CloudFormation 模板。

这里,我们使用 CloudFormation 栈以部署解决方案。此栈将创建所有必要资源,具体包括:

  • 一个 Amazon SageMaker notebook 实例,用于在 Jupyter notebook 中运行 Python 代码。
  • 与 notebook 实例相关联的 IAM 角色。
  • 一个 Amazon ES 域,用于将句子嵌入向量存储在 KNN 索引内以供检索。
  • 两个 S3 存储桶:一个用于存储源服饰图像,另一个用于托管静态网站。

在此步骤中,我们应该在notebook的开头使用 NLU based Item Search 标题。请遵循 notebook 中的步骤并按顺序运行各个单元。
您可以使用 sentence-transformers 句子转换器中的预训练BERT模型(distilbert-base-nli-stsb-mean-tokens),并将其托管在 Amazon SageMaker PyTorch 模型服务器端点之上,借此生成定长的句子嵌入。嵌入内容将被保存在 CloudFormation 栈所创建的 Amazon ES 域内。关于更多详细信息,请参阅 notebook 中的各 markdown 单元。
按 notebook 一步操作,直至 Deploying a full-stack NLU search application 单元。
此 notebook 中包含多个重要的单元,我们将引导您完成其中所对应的操作。
首先从 Feidegger 处下载多模语料库数据集,其中包含时尚图片与德语描述。具体参见以下代码:

## 数据准备

import os 
import shutil
import json
import tqdm
import urllib.request
from tqdm import notebook
from multiprocessing import cpu_count
from tqdm.contrib.concurrent import process_map

images_path = 'data/feidegger/fashion'
filename = 'metadata.json'

my_bucket = s3_resource.Bucket(bucket)

if not os.path.isdir(images_path):
    os.makedirs(images_path)

def download_metadata(url):
    if not os.path.exists(filename):
        urllib.request.urlretrieve(url, filename)
        
#将metadata.json下载至本地notebook
download_metadata('https://raw.githubusercontent.com/zalandoresearch/feidegger/master/data/FEIDEGGER_release_1.1.json')

def generate_image_list(filename):
    metadata = open(filename,'r')
    data = json.load(metadata)
    url_lst = []
    for i in range(len(data)):
        url_lst.append(data[i]['url'])
    return url_lst


def download_image(url):
    urllib.request.urlretrieve(url, images_path + '/' + url.split("/")[-1])
                    
#生成图片列表           
url_lst = generate_image_list(filename)     

workers = 2 * cpu_count()

#将图片下载至本地磁盘
process_map(download_image, url_lst, max_workers=workers)
Upload the dataset to Amazon S3:
#将数据集上传至S3

files_to_upload = []
dirName = 'data'
for path, subdirs, files in os.walk('./' + dirName):
    path = path.replace("\\","/")
    directory_name = path.replace('./',"")
    for file in files:
        files_to_upload.append({
            "filename": os.path.join(path, file),
            "key": directory_name+'/'+file
        })
        

def upload_to_s3(file):
        my_bucket.upload_file(file['filename'], file['key'])
        
#将图片上传至s3
process_map(upload_to_s3, files_to_upload, max_workers=workers)

此数据集中包含德语产品描述,因此我们需要使用 Amazon Translate 将各德语句子翻译为英语:

with open(filename) as json_file:
    data = json.load(json_file)

#定义翻译器函数
def translate_txt(data):
    results = {}
    results['filename'] = f's3://{bucket}/data/feidegger/fashion/' + data['url'].split("/")[-1]
    results['descriptions'] = []
    translate = boto3.client(service_name='translate', use_ssl=True)
    for i in data['descriptions']:
        result = translate.translate_text(Text=str(i), 
            SourceLanguageCode="de", TargetLanguageCode="en")
        results['descriptions'].append(result['TranslatedText'])
    return results

将句子转换器模型保存至 notebook 实例:

!pip install sentence-transformers

#将模型保存至由sagemaker托管的磁盘当中
from sentence_transformers import models, SentenceTransformer
saved_model_dir = 'transformer'
if not os.path.isdir(saved_model_dir):
    os.makedirs(saved_model_dir)

model = SentenceTransformer('distilbert-base-nli-stsb-mean-tokens')
model.save(saved_model_dir)

使用以下代码,将模型工件(model.tar.gz)上传至 Amazon S3:

#将模型压缩为.gz格式
import tarfile
export_dir = 'transformer'
with tarfile.open('model.tar.gz', mode='w:gz') as archive:
    archive.add(export_dir, recursive=True)

#将模型上传至S3

inputs = sagemaker_session.upload_data(path='model.tar.gz', key_prefix='model')
inputs

使用 Amazon SageMaker Python SDK 将模型部署至 Amazon SageMaker PyTorch 模型服务器当中。具体参见以下代码:

from sagemaker.pytorch import PyTorch, PyTorchModel
from sagemaker.predictor import RealTimePredictor
from sagemaker import get_execution_role

class StringPredictor(RealTimePredictor):
    def __init__(self, endpoint_name, sagemaker_session):
        super(StringPredictor, self).__init__(endpoint_name, sagemaker_session, content_type='text/plain')

pytorch_model = PyTorchModel(model_data = inputs, 
                             role=role, 
                             entry_point ='inference.py',
                             source_dir = './code', 
                             framework_version = '1.3.1',
                             predictor_cls=StringPredictor)

predictor = pytorch_model.deploy(instance_type='ml.m5.large', initial_instance_count=3)

使用以下代码定义余弦相似度 Amazon ES KNN索引映射(要定义余弦相似度 KNN 索引映射,您需要使用 Amazon ES 7.7 或者更高版本):

#KNN索引映射
knn_index = {
    "settings": {
        "index.knn": True,
        "index.knn.space_type": "cosinesimil",
        "analysis": {
          "analyzer": {
            "default": {
              "type": "standard",
              "stopwords": "_english_"
            }
          }
        }
    },
    "mappings": {
        "properties": {
           "zalando_nlu_vector": {
                "type": "knn_vector",
                "dimension": 768
            } 
        }
    }
}

每种产品都对应 5 条视觉描述,我们需要将5条描述结合起来以获得一个固定长度的句子嵌入。具体请参见以下代码:

def concat_desc(results):
    obj = {
        'filename': results['filename'],
    }
    obj['descriptions'] = ' '.join(results['descriptions'])
    return obj

concat_results = map(concat_desc, results)
concat_results = list(concat_results)
concat_results[0]

使用以下代码,将句子嵌入与关联的 Amazon S3 图像 URI 导入至 Amazon ES KNN 索引当中。您还可以全文翻译描述内容,以便之后在 Elasticsearch 当中比较 KNN 搜索与标准匹配文本查询之间的差异。

# 定义一项函数,将对应于各S3 URI的特征向量导入至Elasticsearch KNN索引
# 此过程大约需要10分钟。

def es_import(concat_result):
    vector = json.loads(predictor.predict(concat_result['descriptions']))
    es.index(index='idx_zalando',
             body={"zalando_nlu_vector": vector,
                   "image": concat_result['filename'],
                   "description": concat_result['descriptions']}
            )
        
workers = 8 * cpu_count()
    
process_map(es_import, concat_results, max_workers=workers)

在拥有一个能够正常工作的 Amazon Sagemaker 端点之后,我们可以在 Amazon ES 上提取文本特征与 KNN 索引,接下来即可构建一款真实的全栈机器学习驱动型 Web 应用程序。这里我们使用 Amazon Web Services SAM 模板通过 API Gateway 与 Lambda 部署无服务器 REST API。REST API 接受新的搜索字符串、生成嵌入,并将相似的相关项返回至客户端。接下来,您可以将与 REST API 交互的前端网站上传至 Amazon S3。前端代码使用Amplify 与您的 REST API 相集成。

  • 在以下单元中,预填充一个 CloudFormation 模板,此模板将为全栈应用程序创建必要的资源,例如 Lambda 与 API Gateway:
s3_resource.Object(bucket, 'backend/template.yaml').upload_file('./backend/template.yaml', ExtraArgs={'ACL':'public-read'})


sam_template_url = f'https://{bucket}.s3.amazonaws.com/backend/template.yaml'

# 生成CloudFormation快速创建链接

print("Click the URL below to create the backend API for NLU search:\n")
print((
    'https://console.aws.amazon.com/cloudformation/home?region=us-east-1#/stacks/create/review'
    f'?templateURL={sam_template_url}'
    '&stackName=nlu-search-api'
    f'&param_BucketName={outputs["s3BucketTraining"]}'
    f'&param_DomainName={outputs["esDomainName"]}'
    f'&param_ElasticSearchURL={outputs["esHostName"]}'
    f'&param_SagemakerEndpoint={predictor.endpoint}'
))

以下截屏所示为输出结果:预生成的 CloudFormation 模板链接。

  • 选择此链接。

您将跳转至 Quick create stack 页面。

  • 选中复选框以确认 IAM 资源,具有自定义名称的 IAM 资源并创建CAPABILITY_AUTO_EXPAND。
  • 选择 Create stack

栈创建完成之后,您将看到状态转换为 CREATE_COMPLETE。您可以在 Resources 选项卡上查看 CloudFormation 模板创建的所有资源。

  • 创建栈后,继续遍历各单元。

以下单元表示您的全栈应用程序(包括前端与后端代码)已成功部署:

print('Click the URL below:\n')
print(outputs['S3BucketSecureURL'] + '/index.html')

以下截屏所示为 URL 输出。

  • 选择此链接。

您将跳转至应用程序页面,可以在这里提供您自己的搜索文本,以使用 KNN 方法及常规的全文本搜索方法查找产品。

  • 完成 KNN 搜索应用程序的测试与实验之后,请运行 notebook 末尾的最后两个单元:
# 删除端点
predictor.delete_endpoint()

# 清空S3内容
training_bucket_resource = s3_resource.Bucket(bucket)
training_bucket_resource.objects.all().delete()

hosting_bucket_resource = s3_resource.Bucket(outputs['s3BucketHostingBucketName'])
hosting_bucket_resource.objects.all().delete()
这些单元将终止您的Amazon SageMaker端点并清空S3存储桶,为资源清理做好准备。

要删除其余 Amazon Web Services 资源,请前往 Amazon Web Services CloudFormation 控制台并删除 nlu-search-api 与 nlu-search 栈。

在本文中,我们共同了解了如何使用 Amazon SageMaker 与Amazon ES KNN索引功能创建基于KNN的搜索应用程序。我们使用了句子转换器Python库内的预训练BERT模型。您也可以使用自己的数据集对BERT模型进行调优。关于更多详细信息,请参阅使用 Amazon Elastic Inference 在 Amazon SageMaker 上微调并部署 PyTOrch BERT 模型。
本文建议您使用GPU实例处理大多数深度学习项目。在大部分情况下,在 GPU 实例上训练新模型的速度要比 CPU 实例快。如果使用多个 GPU 实例,或者在多个 GPU 实例之间使用分布式训练,则可实现亚线性资源扩展。但是,我们在此用例中使用了 CPU 实例,因此您可以在 Amazon Web Services Free Tier 下完成演练。

相关文章