Host a mxnet model aws lambda#
GitHub this is part 1 of my series on SageMaker. I start by hosting a mxnet nn model in AWS Lambda and serving it via API Gateway.
- pros: simple, least management, low cost
- cons: cold start as the ecr image size about 500MB
- mxnet mnist dataset
- build and train a simple mxnet nn model locally
- upload the model (params) to a s3 bucket
- deploy the model into lambda and apigaw
Lambda processing code#
it create a model and loads pre-trained params from a s3 bucket, then it perform prediction on an image passed by image url from the client request via apigw.
# s3 clients3 = boto3.resource("s3")# download model paramss3.meta.client.download_file(Bucket=os.environ["BUCKET_NAME"],Key=os.environ["MODEL_PATH"],Filename="/tmp/model_params",)# create a nn modeldef create_model():"""create a nn model"""net = mx.gluon.nn.Sequential()net.add(nn.Flatten(),nn.Dense(128, activation="relu"),nn.Dense(64, activation="relu"),nn.Dense(10, activation=None),)return net# read image from filedef read_image(image_url):"""read image from s3 and transform to mxnet format"""#file_name = "/tmp/image.png"# get image from s3# TODO: read into buffer - not saving files3.meta.client.download_file(Bucket=os.environ["BUCKET_NAME"],Key=image_url,Filename=file_name)# mxnet read imageimage = mx.image.imread(filename=file_name)# transform to mxnet formatimage = mx.image.imresize(image, 28, 28)image = image.transpose((2, 0, 1))image = image.astype(dtype="float32")# transform image to mx formatreturn image
then the lambda handler
def handler(event, context):"""lambda handler"""print(event)# create a modelnet = create_model()# load params into modelnet.load_parameters(filename="/tmp/model_params")# parse requestimage_url = event["queryStringParameters"]["image_url"]# read the image from s3image = read_image(image_url)# predictpred = net(image)[0]pred = pred.asnumpy()pred_dict = dict(zip(np.arange(10), pred))pred_dict = {k: v for k, v in sorted(pred_dict.items(), key=lambda item: item[1])}# predictionreturn {"statusCode": 200,"headers": {"Access-Control-Allow-Origin": "*","Access-Control-Allow-Headers": "Content-Type","Access-Control-Allow-Methods": "OPTIONS,GET",},"body": json.dumps({"pred": f"{pred_dict}"}),}
Infrastructure deployment#
lambda ecr
const func = new cdk.aws_lambda.Function(this, 'mxnetLambda', {functionName: 'mxnetLambda',code: cdk.aws_lambda.Code.fromAssetImage(path.join(__dirname, './../../mxnet-lambda')),runtime: cdk.aws_lambda.Runtime.FROM_IMAGE,handler: cdk.aws_lambda.Handler.FROM_IMAGE,memorySize: 2048,timeout: Duration.seconds(300),environment: {BUCKET_NAME: config.BUCKET_NAME,MODEL_PATH: config.MODEL_PATH},initialPolicy: [new cdk.aws_iam.PolicyStatement({effect: Effect.ALLOW,resources: ['*'],actions: ['s3:*']})]})
Dockerfile
FROM public.ecr.aws/lambda/python:3.7# create code dir inside containerRUN mkdir ${LAMBDA_TASK_ROOT}/source# copy code to containerCOPY "requirements.txt" ${LAMBDA_TASK_ROOT}/source# copy handler function to containerCOPY ./index.py ${LAMBDA_TASK_ROOT}# install dependencies for running time environmentRUN pip3 install -r ./source/requirements.txt --use-feature=2020-resolver --target "${LAMBDA_TASK_ROOT}"# set the CMD to your handlerCMD [ "index.handler" ]
requirements, should be careful with dependencies for mxnet on the lambda.
mxnet==1.6.0numpy==1.19.5certifi==2020.6.20idna==2.10graphviz==0.8.4chardet==3.0.4
lambda apigw integration, alternatively can use lambda proxy mode. The important thing is client will request with an image url, and apigw should pass this image url to the lambda.
resource.addMethod('GET',new cdk.aws_apigateway.LambdaIntegration(func, {proxy: true,allowTestInvoke: false,credentialsRole: role}))
Tsx load json#
modify tsconfig.js to load config.json
"resolveJsonModule": true,"esModuleInterop": true,