<jupyter_start><jupyter_text>Mock Lambda FunctionThis next notebook simulates a lambda function calling the NTM model deployed in the previous notebook.  In reality, the deployed model might be a small component in a large application or pipeline.  In this example, the lambda function takes a plain text comment as an input and returns topic probabilities. The test comment is below.  We'll wrap the comment in a lambda event (i.e. json).<jupyter_code>test_comment = "The focus on raising the age of gun buyers to 21 is not random: Individuals ages 18 to 20 commit homicides using guns at a rate that’s four times higher than the rate for those over 21, according to Everytown for Gun Safety, using F.B.I. and census data.\"<br/><br/>No, it may not be RANDOM, but this statement is highly DECEPTIVE. The overwhelming majority of homicides are committed with illegal handguns. Those who possess them are not purchasing them at retail stores using credit cards and submitting to NICS background checks. They are most often young, frequently gang members, and associated with the drug trade.<br/>In virtually all cases, statistics from Everytown can be shown to be distortions of legitimate FBI, DoJ, and census data."

test_event = {'comment': test_comment}
test_context = None<jupyter_output><empty_output><jupyter_text>The mock lambda function requires the vocabulary created by the Glue job.  Let's assume the vocabulary file is packaged with lambda function and accessable on the local file system.<jupyter_code>import boto3

s3 = boto3.resource('s3')
obj = s3.Object('sagemaker-us-east-1-228889150161','data/nyt-features/vocab.json')
obj.download_file('vocab.json')<jupyter_output><empty_output><jupyter_text>Amazon lambda functions can access os environment variable.  Let's make the Sagemaker Endpoint a configurable option.<jupyter_code>import os 
os.environ['ENDPOINT'] = 'ntm-nyt'<jupyter_output><empty_output><jupyter_text>Lambda FunctionThe is an example script that could be deployed as a lambda function.  The function:1. Loads a vocabuarly as a dictionary2. Tokenizes the comments3. Coverts the tokens to indexes 4. Counts the words (i.e. creates the bag-of-words)5. Represents the bag-of-words as a sparse vector6. Call the NTM endpoint and gets the response.> Note: This function needs to tokenize the sentence exactly like the data prep.  It might make sense to create a tokenization micro-service that is use by this function and the data prep.<jupyter_code>import json 
import re 
from collections import Counter
import boto3
import io
import os


def load_vocab():
    with open('vocab.json', 'r') as json_file:
        return json.load(json_file)

def mock_function(event, context):
    vocab = load_vocab()
    tokens = re.split(r'\W+', event['comment'])
    
    lowercase = map(lambda token: token.lower(), tokens)
    vocab_words = filter(lambda token: token in vocab, lowercase)
    idx = map(lambda token: vocab[token], vocab_words)
    counts = Counter(idx)
    
    #https://docs.aws.amazon.com/sagemaker/latest/dg/cdf-inference.html
    keys = sorted(counts.keys())
    values = list(map(lambda key: counts[key], keys))
    features = { "keys": keys, "shape": [len(vocab)],"values": values }
    
    ntm_input = {"instances":[{"data": {"features": features}}]}
    req_body = json.dumps(ntm_input)
    print(req_body)
    
    client = boto3.client('sagemaker-runtime')
    resp = client.invoke_endpoint(
        EndpointName=os.environ['ENDPOINT'],
        Body=req_body.encode('utf-8'),
        ContentType='application/json',
        Accept='application/json'
    )
        
    predictions = json.load(resp["Body"])
    return predictions<jupyter_output><empty_output><jupyter_text>Call the Mock FunctionThe call to SageMaker is in a json format and looks like this...```{    "instances":         [            {"data":                 {                "features":                     {                    "keys": [words],                     "shape": [5000],                     "values": [counts]                     }                 }             }         ]    }```<jupyter_code>response = mock_function(test_event, test_context)<jupyter_output>{"instances": [{"data": {"features": {"keys": [26, 27, 77, 80, 102, 158, 238, 243, 246, 317, 323, 410, 468, 471, 550, 687, 738, 786, 798, 846, 869, 912, 961, 1058, 1070, 1081, 1128, 1200, 1243, 1270, 1363, 1851, 1852, 2137, 2173, 2295, 2527, 2559, 2638, 2765, 2828, 3137, 3449, 3690, 4185, 4417, 4599, 4854], "shape": [5000], "values": [1, 2, 1, 1, 1, 1, 1, 1, 2, 1, 3, 1, 1, 1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1]}}}]}<jupyter_text>The response is an array of topic probabilities.   The most likely topic is the gun topic.<jupyter_code>print(response)
import numpy as np
weigths = response['predictions'][0]['topic_weights']
w = np.asarray(weigths)
np.argmax(w, axis=0)
import sagemaker
sagemaker_session = sagemaker.Session()
sagemaker_session.delete_endpoint('ntm-nyt')<jupyter_output><empty_output>
{'fraction_non_alphanumeric': 0.10906571654790181, 'fraction_numerical': 0.0510688836104513, 'mean_word_length': 3.8123809523809524, 'pattern_counts': {'":': 18, '<': 27, '<?xml version=': 0, '>': 28, 'https://': 1, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 8, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'chain_length': '6', 'dir': 'jupyter-structured-clean-dedup', 'id': '330093', 'license': 'Apache-2.0', 'n_tokens_mistral': 1786, 'n_tokens_neox': 1551, 'n_words': 571, 'path': '4. Mock Lambda Function.ipynb', 'repo_name': 'jshudzina/sagemaker-tutorial', 'size': '5052'}