The newest chapter addresses a problem that often bedevils nonparametric machine learning models.Read More
Making an art collection browsable by voice
The Art Museum skill uses Alexa Conversations, an AI-driven dialogue management tool.Read More
Translate and analyze text using SQL functions with Amazon Athena, Amazon Translate, and Amazon Comprehend
You have Amazon Simple Storage Service (Amazon S3) buckets full of files containing incoming customer chats, product reviews, and social media feeds, in many languages. Your task is to identify the products that people are talking about, determine if they’re expressing happy thoughts or sad thoughts, translate their comments into a single common language, and create copies of the data for your business analysts with this new information added to each record. Additionally, you need to remove any personally identifiable information (PII), such as names, addresses, and credit card numbers.
You already know how to use Amazon Athena to transform data in Amazon S3 using simple SQL commands and the built-in functions in Athena. Now you can also use Athena to translate and analyze text fields, thanks to Amazon Translate, Amazon Comprehend, and the power of Athena User Defined Functions (UDFs).
Athena is an interactive query service that makes it easy to analyze data stored in Amazon S3 using SQL. Amazon Comprehend is a Natural Language Processing (NLP) service that makes it easy to uncover insights from text. Amazon Translate is a neural machine translation service that delivers fast, high-quality, affordable, and customizable language translation. In this post, I show you how you can now use them together to perform the following actions:
- Detect the dominant language of a text field
- Detect the prevailing sentiment expressed—positive, negative, neither, or both
- Detect or redact entities (such as items, places, or quantities)
- Detect or redact PII
- Translate text from one language to another
This post accomplishes the following goals:
- Show you how to quickly set up the text analytics functions in your own AWS account (it’s fast and easy!)
- Briefly explain how the functions work
- Discuss performance and cost
- Provide a tutorial where we do some text analytics on Amazon product reviews
- Describe all the available functions
We include a list of all the available functions at the end of the post; the following code shows a few example queries and results:
USING FUNCTION detect_sentiment(text_col VARCHAR, lang VARCHAR) RETURNS VARCHAR TYPE LAMBDA_INVOKE WITH (lambda_name = 'textanalytics-udf')
SELECT detect_sentiment('I am very happy', 'en') as sentiment
sentiment
POSITIVE
USING FUNCTION detect_pii_entities(text_col VARCHAR, lang VARCHAR) RETURNS VARCHAR TYPE LAMBDA_INVOKE WITH (lambda_name = 'textanalytics-udf')
SELECT detect_pii_entities('I am Bob, I live in Herndon VA, and I love cars', 'en') as pii
pii
[["NAME","Bob"],["ADDRESS","Herndon VA"]]
USING FUNCTION redact_pii_entities(text_col VARCHAR, lang VARCHAR, type VARCHAR) RETURNS VARCHAR TYPE LAMBDA_INVOKE WITH (lambda_name = 'textanalytics-udf')
SELECT redact_pii_entities('I am Bob, I live in Herndon VA, and I love cars', 'en', 'NAME,ADDRESS') as pii_redacted
pii_redacted
I am [NAME], I live in [ADDRESS], and I love cars
USING FUNCTION translate_text(text_col VARCHAR, sourcelang VARCHAR, targetlang VARCHAR, terminologyname VARCHAR) RETURNS VARCHAR TYPE LAMBDA_INVOKE WITH (lambda_name = 'textanalytics-udf')
SELECT translate_text('It is a beautiful day in the neighborhood', 'auto', 'fr', NULL) as translated_text
translated_text
C'est une belle journée dans le quartier
Install the text analytics UDF
An Athena UDF uses AWS Lambda to implement the function capability. I discuss more details later in this post, but you don’t need to understand the inner workings to use the text analytics UDF, so let’s get started.
Install the prebuilt Lambda function with the following steps:
- Navigate to the TextAnalyticsUDFHandler application in the AWS Serverless Application Repository.
- In the Application settings section, keep the settings at their defaults.
- Select I acknowledge that this app creates custom IAM roles.
- Choose Deploy.
And that’s it! Now you have a new Lambda function called textanalytics-udf
. You’re ready to try some text analytics queries in Athena.
If you prefer to build and deploy from the source code instead, see the directions at the end of the GitHub repository README.
Run your first text analytics query
If you’re new to Athena, you may want to review the Getting Started guide.
As of this writing, the Athena UDF feature is still in preview. To enable it, create an Athena workgroup named AmazonAthenaPreviewFunctionality
and run all the UDF queries from that workgroup.
Enter the following query into the SQL editor:
USING FUNCTION detect_sentiment(text_col VARCHAR, lang VARCHAR) RETURNS VARCHAR TYPE LAMBDA_INVOKE WITH (lambda_name = 'textanalytics-udf')
SELECT detect_sentiment('I am very happy', 'en') as sentiment
You get a simple POSITIVE
result. Now try again, varying the input text—try something less positive to see how the returned sentiment value changes.
To get the sentiment along with confidence scores for each potential sentiment value, use the following query instead:
USING FUNCTION detect_sentiment_all(text_col VARCHAR, lang VARCHAR) RETURNS VARCHAR TYPE LAMBDA_INVOKE WITH (lambda_name = 'textanalytics-udf')
SELECT detect_sentiment_all('I am very happy', 'en') as sentiment
Now you get a JSON string containing the sentiment and all the sentiment scores:
{"sentiment":"POSITIVE","sentimentScore":{"positive":0.999519,"negative":7.407639E-5,"neutral":2.7478999E-4,"mixed":1.3210243E-4}}
You can use the built-in JSON extraction functions in Athena on this result to extract the fields for further analysis.
How the UDF works
For more information about the Athena UDF framework, see Querying with User Defined Functions.
The Java class TextAnalyticsUDFHandler implements our UDF Lambda function handler. Each text analytics function has a corresponding public method in this class.
Athena invokes our UDF Lambda function with batches of input records. The TextAnalyticsUDFHandler
subdivides these batches into smaller batches of up to 25 rows to take advantage of the Amazon Comprehend synchronous multi-document batch APIs where they are available (for example, for detecting language, entities, and sentiment). When there is no synchronous multi-document API available (such as for DetectPiiEntity
and TranslateText
), we use the single-document API instead.
Amazon Comprehend API service quotas provide guardrails to limit your cost exposure from unintentional high usage (we discuss this more in the following section). By default, the multi-document batch APIs process up to 250 records per second, and the single-document APIs process up to 20 records per second. Our UDFs use exponential back off and retry to throttle the request rate to stay within these limits. You can request increases to the transactions per second quota for APIs using the Quota Request Template on the AWS Management Console.
Amazon Comprehend and Amazon Translate each enforce a maximum input string length of 5,000 utf-8 bytes. Text fields that are longer than 5,000 utf-8 bytes are truncated to 5,000 bytes for language and sentiment detection, and split on sentence boundaries into multiple text blocks of under 5,000 bytes for translation and entity or PII detection and redaction. The results are then combined.
Optimizing cost
In addition to Athena query costs, the text analytics UDF incurs usage costs from Lambda and Amazon Comprehend and Amazon Translate. The amount you pay is a factor of the total number of records and characters that you process with the UDF. For more information, see AWS Lambda pricing, Amazon Comprehend pricing, and Amazon Translate pricing.
To minimize the costs, I recommend that you avoid processing the same records multiple times. Instead, materialize the results of the text analytics UDF by using CREATE TABLE AS SELECT (CTAS) queries to capture the results in a separate table that you can then cost-effectively query as often as needed without incurring additional UDF charges. Process newly arriving records incrementally using INSERT INTO…SELECT queries to analyze and enrich only the new records and add them to the target table.
Avoid calling the text analytics functions needlessly on records that you will subsequently discard. Write your queries to filter the dataset first using temporary tables, views, or nested queries, and then apply the text analytics functions to the resulting filtered records.
Always assess the potential cost before you run text analytics queries on tables with vary large numbers of records.
In this section, we provide two example cost assessments.
Example 1: Analyze the language and sentiment of tweets
Let’s assume you have 10,000 tweet records, with average length 100 characters per tweet. Your SQL query detects the dominant language and sentiment for each tweet. You’re in your second year of service (the Free Tier no longer applies). The cost details are as follows:
- Size of each tweet = 100 characters
- Number of units (100 character) per record (minimum is 3 units) = 3
- Total Units: 10,000 (records) x 3 (units per record) x 2 (Amazon Comprehend requests per record) = 60,000
- Price per unit = $0.0001
- Total cost for Amazon Comprehend = [number of units] x [cost per unit] = 60,000 x $0.0001 = $6.00
Example 2: Translate tweets
Let’s assume that 2,000 of your tweets aren’t in your local language, so you run a second SQL query to translate them. The cost details are as follows:
- Size of each tweet = 100 characters
- Total characters: 2,000 (records) * 100 (characters per record) x 1 (Translate requests per record) = 200,000
- Price per character = $0.000015
- Total cost for Amazon Translate = [number of characters] x [cost per character] = 200,000 x $0.000015 = $3.00
Analyze insights from customer reviews
It’s time to put our new text analytics queries to use.
For a tutorial on getting actionable insights from customer reviews, see Tutorial: Analyzing Insights from Customer Reviews with Amazon Comprehend. This post provides an alternate approach to the same challenge: using SQL queries powered by Athena and Amazon Comprehend.
The tutorial takes approximately 10 minutes to complete, and costs up to $6 for Amazon Comprehend—there is no cost if you’re eligible for the Free Tier.
Create a new database in Athena
Run the following query in the Athena query editor:
CREATE DATABASE IF NOT EXISTS comprehendresults;
When connecting your data source, choose your new database.
Create a source table containing customer review data
We use the Amazon Customer Reviews Dataset, conveniently hosted for public access in Amazon S3.
- Run the following query in the Athena query editor:
CREATE EXTERNAL TABLE amazon_reviews_parquet( marketplace string, customer_id string, review_id string, product_id string, product_parent string, product_title string, star_rating int, helpful_votes int, total_votes int, vine string, verified_purchase string, review_headline string, review_body string, review_date bigint, year int) PARTITIONED BY (product_category string) ROW FORMAT SERDE 'org.apache.hadoop.hive.ql.io.parquet.serde.ParquetHiveSerDe' STORED AS INPUTFORMAT 'org.apache.hadoop.hive.ql.io.parquet.MapredParquetInputFormat' OUTPUTFORMAT 'org.apache.hadoop.hive.ql.io.parquet.MapredParquetOutputFormat' LOCATION 's3://amazon-reviews-pds/parquet/'
- Under Tables, find the new table amazon_reviews_parquet.
- From the options menu, choose Load partitions.
- Preview the new table,
amazon_reviews_parquet
.
- Run the following query to assess the average review length:
SELECT AVG(LENGTH(review_body)) AS average_review_length FROM amazon_reviews_parquet
The average review length is around 365 characters. This equates to 4 Amazon Comprehend units per record (1 unit = 100 characters).
Detect the language for each review
To detect the language of each review, run the following query in the Athena query editor—it takes just over 1 minute to run and costs $2:
CREATE TABLE amazon_reviews_with_language WITH (format='parquet') AS
USING FUNCTION detect_dominant_language(col1 VARCHAR) RETURNS VARCHAR TYPE LAMBDA_INVOKE WITH (lambda_name = 'textanalytics-udf')
SELECT *, detect_dominant_language(review_body) AS language
FROM amazon_reviews_parquet
LIMIT 5000
This query creates a new table, amazon_reviews_with_language
, with one new column added: language
. The LIMIT clause limits the number of records to 5,000.
Cost is calculated as: 5,000 (records) x 4 (units per record) x 1 (requests per record) x $0.0001 (Amazon Comprehend price per unit) = $2.
Run the following query to see the detected language codes, with the corresponding count of reviews for each language:
SELECT language, count(*) AS count FROM amazon_reviews_with_language GROUP BY language ORDER BY count DESC
Detect sentiment and entities for each review
To detect sentiment, run the following query in the Athena query editor—it uses two text analytics functions, takes around 1 minute to run, and costs $4:
CREATE TABLE amazon_reviews_with_text_analysis WITH (format='parquet') AS
USING
FUNCTION detect_sentiment_all(col1 VARCHAR, lang VARCHAR) RETURNS VARCHAR TYPE LAMBDA_INVOKE WITH (lambda_name = 'textanalytics-udf'),
FUNCTION detect_entities_all(col1 VARCHAR, lang VARCHAR) RETURNS VARCHAR TYPE LAMBDA_INVOKE WITH (lambda_name = 'textanalytics-udf')
SELECT *,
detect_sentiment_all(review_body, language) AS sentiment,
detect_entities_all(review_body, language) AS entities
FROM amazon_reviews_with_language
WHERE language IN ('ar', 'hi', 'ko', 'zh-TW', 'ja', 'zh', 'de', 'pt', 'en', 'it', 'fr', 'es')
This query creates a new table, amazon_reviews_with_text_analysis
, with two additional columns added: sentiment
and entities
. The WHERE clause restricts the result set to the list of languages supported by Amazon Comprehend sentiment and entity detection.
Cost is calculated as: 5,000 (records) x 4 (units per record) x 2 (requests per record) x $0.0001 (Amazon Comprehend price per unit) = $4.
Preview the new table and inspect some of the values for the new sentiment
and entities
columns. They contain JSON strings with nested structures and fields.
The following screenshot shows the sentiment
column details.
The following screenshot shows the entities column details.
Next, we use the JSON functions in Athena to prepare these columns for analysis.
Prepare sentiment for analysis
Run the following SQL query to create a new table containing sentiment and sentiment scores expanded into separate columns:
CREATE TABLE sentiment_results_final WITH (format='parquet') AS
SELECT
review_date, year, product_title, star_rating, language,
CAST(JSON_EXTRACT(sentiment,'$.sentiment') AS VARCHAR) AS sentiment,
CAST(JSON_EXTRACT(sentiment,'$.sentimentScore.positive') AS DOUBLE ) AS positive_score,
CAST(JSON_EXTRACT(sentiment,'$.sentimentScore.negative') AS DOUBLE ) AS negative_score,
CAST(JSON_EXTRACT(sentiment,'$.sentimentScore.neutral') AS DOUBLE ) AS neutral_score,
CAST(JSON_EXTRACT(sentiment,'$.sentimentScore.mixed') AS DOUBLE ) AS mixed_score,
review_headline, review_body
FROM amazon_reviews_with_text_analysis
Preview the new sentiment_results_final
table (see the following screenshot). Does the sentiment generally align with the text of the review_body
field? How does it correlate with the star_rating
? If you spot any dubious sentiment assignments, check the confidence scores to see if the sentiment was assigned with a low confidence.
Prepare entities for analysis
Run the following SQL query to create a new table containing detected entities unnested into separate rows (inner subquery), with each field in a separate column (outer query):
CREATE TABLE entities_results_final WITH (format='parquet') AS
SELECT
review_date, year, product_title, star_rating, language,
CAST(JSON_EXTRACT(entity_element, '$.text') AS VARCHAR ) AS entity,
CAST(JSON_EXTRACT(entity_element, '$.type') AS VARCHAR ) AS category,
CAST(JSON_EXTRACT(entity_element, '$.score') AS DOUBLE ) AS score,
CAST(JSON_EXTRACT(entity_element, '$.beginOffset') AS INTEGER ) AS beginoffset,
CAST(JSON_EXTRACT(entity_element, '$.endOffset') AS INTEGER ) AS endoffset,
review_headline, review_body
FROM
(
SELECT *
FROM
(
SELECT *,
CAST(JSON_PARSE(entities) AS ARRAY(json)) AS entities_array
FROM amazon_reviews_with_text_analysis
)
CROSS JOIN UNNEST(entities_array) AS t(entity_element)
)
Preview the contents of the new table, entities_results_final
(see the following screenshot).
Visualize in Amazon QuickSight (optional)
As an optional step, you can visualize your results with Amazon QuickSight. For instructions, see Step 5: Visualizing Amazon Comprehend Output in Amazon QuickSight.
You can use the new word cloud visual type for entities, instead of tree map. In the word cloud chart menu, select Hide “other” categories.
You now have a dashboard with sentiment and entities visualizations that looks similar to the following screenshot.
Troubleshooting
If your query fails, check the Amazon CloudWatch metrics and logs generated by the UDF Lambda function.
- On the Lambda console, find the
textanalytics-udf
function. - Choose Monitoring.
You can view the CloudWatch metrics showing how often the function ran, how long it runs for, how often it failed, and more.
- Choose View logs in CloudWatch to open the function log streams for additional troubleshooting insights.
For more information about viewing CloudWatch metrics via Lambda, see Using the Lambda console.
Additional use cases
There are many use cases for SQL text analytics functions. In addition to the example shown in this post, consider the following:
- Simplify ETL pipelines by using incremental SQL queries to enrich text data with sentiment and entities, such as streaming social media streams ingested by Amazon Kinesis Data Firehose
- Use SQL queries to explore sentiment and entities in your customer support texts, emails, and support cases
- Prepare research-ready datasets by redacting PII from customer or patient interactions
- Standardize many languages to a single common language
You may have additional use cases for these functions, or additional capabilities you want to see added, such as the following:
- SQL functions to call custom entity recognition and custom classification models in Amazon Comprehend
- SQL functions for de-identification—extending the entity and PII redaction functions to replace entities with alternate unique identifiers
Additionally, the implementation is open source, which means that you can clone the repo, modify and extend the functions as you see fit, and (hopefully) send us pull requests so we can merge your improvements back into the project and make it better for everyone.
Cleaning up
After you complete this tutorial, you might want to clean up any AWS resources you no longer want to use. Active AWS resources can continue to incur charges in your account.
- In Athena, run the following query to drop the database and all the tables:
DROP DATABASE comprehendresults CASCADE
- In AWS CloudFormation, delete the stack
serverlessrepo-TextAnalyticsUDFHandler
. - Cancel your QuickSight subscription.
Conclusion
I have shown you how to install the sample text analytics UDF Lambda function for Athena, so that you can use simple SQL queries to translate text using Amazon Translate, generate insights from text using Amazon Comprehend, and redact sensitive information. I hope you find this useful, and share examples of how you can use it to simplify your architectures and implement new capabilities for your business.
Please share your thoughts with us in the comments section, or in the issues section of the project’s GitHub repository.
Appendix: Available function reference
This section summarizes the functions currently provided. The README file provides additional details.
Detect language
This function uses the Amazon Comprehend BatchDetectDominantLanguage API to identify the dominant language based on the first 5,000 bytes of input text.
The following code returns a language code, such as fr for French or en for English:
USING FUNCTION detect_dominant_language(text_col VARCHAR) RETURNS VARCHAR TYPE LAMBDA_INVOKE WITH (lambda_name = 'textanalytics-udf')
SELECT detect_dominant_language('il fait beau à Orlando') as language
The following code returns a JSON formatted array of language codes and corresponding confidence scores:
USING FUNCTION detect_dominant_language_all(text_col VARCHAR) RETURNS VARCHAR TYPE LAMBDA_INVOKE WITH (lambda_name = 'textanalytics-udf')
SELECT detect_dominant_language_all('il fait beau à Orlando') as language_all
Detect sentiment
This function uses the Amazon Comprehend BatchDetectSentiment API to identify the sentiment based on the first 5,000 bytes of input text.
The following code returns a sentiment as POSITIVE, NEGATIVE, NEUTRAL, or MIXED:
USING FUNCTION detect_sentiment(text_col VARCHAR, lang VARCHAR) RETURNS VARCHAR TYPE LAMBDA_INVOKE WITH (lambda_name = 'textanalytics-udf')
SELECT detect_sentiment('Joe is very happy', 'en') as sentiment
The following code returns a JSON formatted object containing detected sentiment and confidence scores for each sentiment value:
USING FUNCTION detect_sentiment_all(text_col VARCHAR, lang VARCHAR) RETURNS VARCHAR TYPE LAMBDA_INVOKE WITH (lambda_name = 'textanalytics-udf')
SELECT detect_sentiment_all('Joe is very happy', 'en') as sentiment_all
Detect entities
This function uses the Amazon Comprehend DetectEntities API to identify PII. Input text longer than 5,000 bytes results in multiple Amazon Comprehend API calls.
The following code returns a JSON formatted object containing an array of entity types and values:
USING FUNCTION detect_entities(text_col VARCHAR, lang VARCHAR) RETURNS VARCHAR TYPE LAMBDA_INVOKE WITH (lambda_name = 'textanalytics-udf')
SELECT detect_entities('His name is Joe, he lives in Richmond VA, he bought an Amazon Echo Show on January 5th, and he loves it', 'en') as entities
The following code returns a JSON formatted object containing an array of PII entity types, with their values, scores, and character offsets:
USING FUNCTION detect_entities_all(text_col VARCHAR, lang VARCHAR) RETURNS VARCHAR TYPE LAMBDA_INVOKE WITH (lambda_name = 'textanalytics-udf')
SELECT detect_entities_all('His name is Joe, he lives in Richmond VA, he bought an Amazon Echo Show on January 5th, and he loves it', 'en') as entities_all
Redact entities
This function replaces entity values for the specified entity types with “[ENTITY_TYPE]
”. Input text longer than 5,000 bytes results in multiple Amazon Comprehend API calls. See the following code:
USING FUNCTION redact_entities(text_col VARCHAR, lang VARCHAR, types VARCHAR) RETURNS VARCHAR TYPE LAMBDA_INVOKE WITH (lambda_name = 'textanalytics-udf')
SELECT redact_entities('His name is Joe, he lives in Richmond VA, he bought an Amazon Echo Show on January 5th, and he loves it', 'en', 'ALL') as entities_redacted
The command returns a redacted version on the input string. Specify one or more entity types to redact by providing a comma-separated list of valid types
in the types string parameter, or ALL
to redact all types.
Detect PII
This function uses the DetectPiiEntities API to identify PII. Input text longer than 5,000 bytes results in multiple Amazon Comprehend API calls.
The following code returns a JSON formatted object containing an array of PII entity types and values:
USING FUNCTION detect_pii_entities(text_col VARCHAR, lang VARCHAR) RETURNS VARCHAR TYPE LAMBDA_INVOKE WITH (lambda_name = 'textanalytics-udf')
SELECT detect_pii_entities('His name is Joe, his username is joe123 and he lives in Richmond VA', 'en') as pii
The following code returns a JSON formatted object containing an array of PII entity types, with their scores and character offsets:
USING FUNCTION detect_pii_entities_all(text_col VARCHAR, lang VARCHAR) RETURNS VARCHAR TYPE LAMBDA_INVOKE WITH (lambda_name = 'textanalytics-udf')
SELECT detect_pii_entities_all('His name is Joe, his username is joe123 and he lives in Richmond VA', 'en') as pii_all
Redact PII
This function replaces the PII values for the specified PII entity types with “[PII_ENTITY_TYPE]
”. Input text longer than 5,000 bytes results in multiple Amazon Comprehend API calls. See the following code:
USING FUNCTION redact_pii_entities(text_col VARCHAR, lang VARCHAR, types VARCHAR) RETURNS VARCHAR TYPE LAMBDA_INVOKE WITH (lambda_name = 'textanalytics-udf')
SELECT redact_pii_entities('His name is Joe, his username is joe123 and he lives in Richmond VA', 'en', 'ALL') as pii_redacted
The function returns a redacted version on the input string. Specify one or more PII entity types to redact by providing a comma-separated list of valid types in the type
string parameter, or ALL
to redact all type.
Translate text
This function translates text from the source language to target language. Input text longer than 5,000 bytes results in multiple Amazon Translate API calls. See the following code:
USING FUNCTION translate_text(text_col VARCHAR, sourcelang VARCHAR, targetlang VARCHAR, customterminologyname VARCHAR) RETURNS VARCHAR TYPE LAMBDA_INVOKE WITH (lambda_name = 'textanalytics-udf')
SELECT translate_text('It is a beautiful day in the neighborhood', 'auto', 'fr', NULL) as translated_text
The function returns the translated string. Optionally, auto-detect the source language (use auto as the language code, which uses Amazon Comprehend), and optionally specify a custom terminology (otherwise use NULL
for customTerminologyName
).
About the Author
Bob Strahan is a Principal Solutions Architect in the AWS Language AI Services team.
The intersection of design and science
How a team of designers, scientists, developers, and engineers worked together to create a truly unique device in Echo Show 10.Read More
Setting up Amazon Personalize with AWS Glue
Data can be used in a variety of ways to satisfy the needs of different business units, such as marketing, sales, or product. In this post, we focus on using data to create personalized recommendations to improve end-user engagement. Most ecommerce applications consume a huge amount of customer data that can be used to provide personalized recommendations; however, that data may not be cleaned or in the right format to provide those valuable insights.
The goal of this post is to demonstrate how to use AWS Glue to extract, transform, and load your JSON data into a cleaned CSV format. We then show you how to run a recommendation engine powered by Amazon Personalize on your user interaction data to provide a tailored experience for your customers. The resulting output from Amazon Personalize is recommendations you can generate from an API.
A common use case is an ecommerce platform that collects user-item interaction data and suggests similar products or products that a customer may like. By the end of this post, you will be able to take your uncleaned JSON data and generate personalized recommendations based off of products each user has interacted with, creating a better experience for your end-users. For the purposes of this post, refer to this user-item-interaction dataset to build this solution.
The resources of this solution may incur a cost on your AWS account. For pricing information, see AWS Glue Pricing and Amazon Personalize Pricing.
The following diagram illustrates our solution architecture.
Prerequisites
For this post, you need the following:
- An Amazon Simple Storage Service (Amazon S3) bucket containing the user-item-interaction dataset.
For instructions on creating a bucket, see Step 1: Create your first S3 bucket. Make sure to attach the Amazon Personalize access policy.
- An AWS Identity and Access Management (IAM) role for AWS Glue with the
AmazonS3FullAccess
andAmazonPersonalizeFullAccess
These are very permissive policies; in practice it’s best to use least privilege and only give access where it’s needed. For instructions on creating a role, see Step 2: Create an IAM Role for AWS Glue.
Crawling your data with AWS Glue
We use AWS Glue to crawl through the JSON file to determine the schema of your data and create a metadata table in your AWS Glue Data Catalog. The Data Catalog contains references to data that is used as sources and targets of your ETL jobs in AWS Glue. AWS Glue is a serverless data preparation service that makes it easy to extract, clean, enrich, normalize, and load data. It helps prepare your data for analysis or machine learning (ML). In this section, we go through how to get your JSON data ready for Amazon Personalize, which requires a CSV file.
Your data can have different columns that you may not necessarily want or need to run through Amazon Personalize. In this post, we use the user-item-interaction.json
file and clean that data using AWS Glue to only include the columns user_id
, item_id
, and timestamp
, while also transforming it into CSV format. You can use a crawler to access your data store, extract metadata, and create table definitions in the Data Catalog. It automatically discovers new data and extracts schema definitions. This can help you gain a better understanding of your data and what you want to include while training your model.
The user-item-interaction
JSON data is an array of records. The crawler treats the data as one object: just an array. We create a custom classifier to create a schema that is based on each record in the JSON array. You can skip this step if your data isn’t an array of records.
- On the AWS Glue console, under Crawlers, choose Classifiers.
- Choose Add classifier.
- For Classifier name¸ enter
json_classifier
. - For Classifier type, select JSON.
- For JSON path, enter
$[*].
- Choose Create.
- On the Crawlers page, choose Add crawler.
- For Crawler name, enter
json_crawler
. - For Custom classifiers, add the classifier you created.
- Choose Next.
- For Crawler source type, choose Data stores.
- Leave everything else as default and choose Next.
- For Choose a data store, enter the Amazon S3 path to your JSON data file.
- Choose Next.
- Skip the section Add another data store.
- In the Choose an IAM role section, select Choose an existing IAM role.
- For IAM role, choose the role that you created earlier (
AWSGlueServiceRole-xxx
). - Choose Next.
- Leave the frequency as Run on Demand.
- On the Output page, choose Add database.
- For Database name, enter
json_data
. - Choose Finish.
- Choose Run it now.
You can also run your crawler by going to the Crawlers page, selecting your crawler, and choosing Run crawler.
Using AWS Glue to convert your files from CSV to JSON
After your crawler finishes running, go to the Tables page on the AWS Glue console. Navigate to the table your crawler created. Here you can see the schema of your data. Make note of the fields you want to use with your Amazon Personalize data. For this post, we want to keep the user_id
, item_id
, and timestamp columns for Amazon Personalize.
At this point, you have set up your database. Amazon Personalize requires CSV files, so you have to transform the data from JSON format into three cleaned CSV files that include only the data you need in Amazon Personalize. The following table shows examples of the three CSV files you can include in Amazon Personalize. It’s important to note that interactions data is required, whereas user and item data metadata is optional.
Dataset Type | Required Fields | Reserved Keywords |
Users |
1 metadata field |
|
Items |
1 metadata field |
CREATION_TIMESTAMP (long) |
Interactions |
|
|
It’s also important to make sure that you have at least 1,000 unique combined historical and event interactions in order to train the model. For more information about quotas, see Quotas in Amazon Personalize.
To save the data as a CSV, you need to run an AWS Glue job on the data. A job is the business logic that performs the ETL work in AWS Glue. The job changes the format from JSON into CSV. For more information about data formatting, see Formatting Your Input Data.
- On the AWS Glue Dashboard, choose AWS Glue Studio.
AWS Glue Studio is an easy-to-use graphical interface for creating, running, and monitoring AWS Glue ETL jobs.
- Choose Create and manage jobs.
- Select Source and target added to the graph.
- For Source, choose S3.
- For Target, choose S3.
- Choose Create.
- Choose the data source S3 bucket.
- On the Data source properties – S3 tab, add the database and table we created earlier.
- On the Transform tab, select the boxes to drop
user_login
andlocation
.
In this post, we don’t use any additional metadata to run our personalization algorithm.
- Choose the data target S3 bucket.
- On the Data target properties – S3 tab, for Format, choose CSV.
- For S3 Target location, enter the S3 path for your target.
For this post, we use the same bucket we used for the JSON file.
- On the Job details page, for Name, enter a name for your job (for this post,
json_to_csv
). - For IAM Role, choose the role you created earlier.
You should also have included the AmazonS3FullAccess
policy earlier.
- Leave the rest of the fields at their default settings.
- Choose Save.
- Choose Run.
It may take a few minutes for the job to run.
In your Amazon S3 bucket, you should now see the CSV file that you use in the next section.
Setting up Amazon Personalize
At this point, you have your data formatted in a file type that Amazon Personalize can use. Amazon Personalize is a fully managed service that uses ML and over 20 years of recommendation experience at Amazon.com to enable you to improve end-user engagement by powering real-time personalized product and content recommendations, and targeted marketing promotions. In this section, we go through how to create an Amazon Personalize solution to use your data to create personalized experiences.
- On the Amazon Personalize console, under New dataset groups, choose Get started.
- Enter the name for your dataset group.
A dataset group contains the datasets, solutions, and event ingestion API.
- Enter a dataset name, and enter in the schema details based on your data.
For this dataset, we use the following schema. You can change the schema according to the values in your dataset.
{
"type": "record",
"name": "Interactions",
"namespace": "com.amazonaws.personalize.schema",
"fields": [
{
"name": "USER_ID",
"type": "string"
},
{
"name": "ITEM_ID",
"type": "string"
},
{
"name": "TIMESTAMP",
"type": "long"
}
],
"version": "1.0"
}
- Choose Next.
- Enter your dataset import job name to import data from Amazon S3.
Make sure that your IAM service role has access to Amazon S3 and Amazon Personalize, and that your bucket has the correct bucket policy.
- Enter the path to your data (the Amazon S3 bucket from the previous section).
- On the Dashboard page for your dataset groups, under Upload datasets, import the
user-item-interactions
data (user data and item data are optional but can enhance the solution).
We include an example item.csv
file in the GitHub repo. The following screenshot shows an example of the item data.
- Under Create solutions, for Solutions training, choose Start.
A solution is a trained model of the data you provided with the algorithm, or recipe, that you select.
- For Solution name, enter
aws-user-personalization
. - Choose Next.
- Review and choose Finish.
- On the dashboard, under Launch campaigns, for Campaign creation, choose Start.
A campaign allows your application to get recommendations from your solution version.
- For Campaign name, enter a name.
- Choose the solution you created.
- Choose Create campaign.
You have now successfully used the data from your data lake and created a recommendation model that can be used to get various recommendations. With this dataset, you can get personalized recommendations for houseware products based off the user’s interactions with other products in the dataset.
Using Amazon Personalize to get your recommendations
To test your solution, go to the campaign you created. In the Test campaign results section, under User ID, enter an ID to get recommendations for. A list of IDs shows up, along with a relative score. The item IDs correlate with specific products recommended.
The following screenshot shows a search for user ID 1
. They have been recommended item ID 59
, which correlates to a wooden picture frame. The score listed next to the item gives you the predicted relevance of each item to your user.
To learn more about Amazon Personalize scores, see Introducing recommendation scores in Amazon Personalize.
To generate recommendations, you can call the GetRecommendations
or GetPersonalizedRanking
API using the AWS Command Line Interface (AWS CLI) or a language-specific SDK. With Amazon Personalize, your recommendations can change as the user clicks on the items for more real-time use cases. For more information, see Getting Real-Time Recommendations.
Conclusion
AWS offers a wide range of AI/ML and analytics services that you can use to gain insights and guide better business decisions. In this post, you used a JSON dataset that included additional columns of data, and cleaned and transformed that data using AWS Glue. In addition, you built a custom model using Amazon Personalize to provide recommendations for your customers.
To learn more about Amazon Personalize, see the developer guide. Try this solution out and let us know if you have any questions in the comments.
About the Authors
Zoish Pithawala is a Startup Solutions Architect at Amazon Web Services based out of San Francisco. She primarily works with startup customers to help them build secure and scalable solutions on AWS.
Sam Tran is a Startup Solutions Architect at Amazon Web Services based out of Seattle. He focuses on helping his customers create well-architected solutions on AWS.
Amazon Rekognition Custom Labels Community Showcase
In our Community Showcase, Amazon Web Services (AWS) highlights projects created by AWS Heroes and AWS Community Builders.
We worked with AWS Machine Learning (ML) Heroes and AWS ML Community Builders to bring to life projects and use cases that detect custom objects with Amazon Rekognition Custom Labels.
The AWS ML community is a vibrant group of developers, data scientists, researchers, and business decision-makers that dive deep into artificial intelligence and ML concepts, contribute with real-world experiences, and collaborate on building projects together.
Amazon Rekognition is a fully managed computer vision service that allows developers to analyze images and videos for a variety of use cases, including face identification and verification, media intelligence, custom industrial automation, and workplace safety.
Detecting custom objects and scenes can be hard, and training and improving a computer vision model with growing data makes the problem more complex. Amazon Rekognition Custom Labels allows you to detect custom labeled objects and scenes with zero Jupyter notebook experience. For example, you can identify logos in streaming media, simplify preventative maintenance, and scale supply chain inventory management. ML practitioners, data scientists, and developers with no previous ML experience benefit by moving their models to production faster, while Amazon Rekognition Custom Labels takes care of the heavy lifting of model development.
In this post, we highlight a few externally published getting started guides and tutorials from AWS ML Heroes and AWS ML Community Builders that applied Amazon Rekognition to a wide variety of use cases, from at-home projects like a fridge inventory checker to an enterprise-level HVAC filter cleanliness detector.
AWS ML Heroes and AWS ML Community Builders
Classify LEGO bricks with Amazon Rekognition Custom Labels by Mike Chambers. In this video, Mike walks you through this fun use case to use Amazon Rekognition Custom Labels to detect 250 different LEGO bricks.
Training models using Satellite imagery on Amazon Rekognition Custom Labels by Rustem Feyzkhanov (with code samples). Satellite imagery is becoming a more and more important source of insights with the advent of accessible satellite data from sources such as the Sentinel-2 on Open Data on AWS. In this guide, Rustem shows how you can find agricultural fields with Amazon Rekognition Custom Labels.
Detecting insights from X-ray data with Amazon Rekognition Custom Labels by Olalekan Elesin (with code samples). Learn how to detect anomalies quickly and with low cost and resource investment with Amazon Rekognition Custom Labels.
Building Natural Flower Classifier using Amazon Rekognition Custom Labels by Juv Chan (with code samples). Building a computer vision model from scratch can be daunting task. In this step-by-step guide, you learn how to build a natural flower classifier using the Oxford Flower 102 dataset and Amazon Rekognition Custom Labels.
What’s in my Fridge by Chris Miller and Siaterlis Konstantinos. How many times have you gone to the grocery store and forgot your list, or weren’t sure if you needed to buy milk, beer, or something else? Learn how AWS ML Community members Chris Miller and Siaterlis Konstantinos used Amazon Rekognition Custom Labels and AWS DeepLens to build a fridge inventory checker to let AI do the heavy lifting on your grocery list.
Clean or dirty HVAC? Using Amazon SageMaker and Amazon Rekognition Custom Labels to automate detection by Luca Bianchi. How can you manage 1–3,000 cleanliness checks with zero ML experience or data scientist on staff? Learn how to detect clean and dirty HVACs using Amazon Rekognition Custom Labels and Amazon SageMaker from AWS ML Hero Luca Bianchi.
Conclusion
Getting started with Amazon Rekognition Custom Labels is simple. Learn more with the getting started guide and example use cases.
Whether you’re just getting started with ML, already an expert, or something in between, there is always something to learn. Choose from community-created and ML-focused blogs, videos, eLearning guides, and much more from the AWS ML community.
Are you interested in contributing to the community? Apply to the AWS Community Builders program.
The content and opinions in the preceding linked posts are those of the third-party authors and AWS is not responsible for the content or accuracy of those posts.
About the Author
Cameron Peron is Senior Marketing Manager for AWS Amazon Rekognition and the AWS AI/ML community. He evangelizes how AI/ML innovation solves complex challenges facing community, enterprise, and startups alike. Out of the office, he enjoys staying active with kettlebell-sport, spending time with his family and friends, and is an avid fan of Euro-league basketball.
Using container images to run TensorFlow models in AWS Lambda
TensorFlow is an open-source machine learning (ML) library widely used to develop neural networks and ML models. Those models are usually trained on multiple GPU instances to speed up training, resulting in expensive training time and model sizes up to a few gigabytes. After they’re trained, these models are deployed in production to produce inferences. They can be synchronous, asynchronous, or batch-based workloads. Those endpoints need to be highly scalable and resilient in order to process from zero to millions of requests. This is where AWS Lambda can be a compelling compute service for scalable, cost-effective, and reliable synchronous and asynchronous ML inferencing. Lambda offers benefits such as automatic scaling, reduced operational overhead, and pay-per-inference billing.
This post shows you how to use any TensorFlow model with Lambda for scalable inferences in production with up to 10 GB of memory. This allows us to use ML models in Lambda functions up to a few gigabytes. For this post, we use TensorFlow-Keras pre-trained ResNet50 for image classification.
Overview of solution
Lambda is a serverless compute service that lets you run code without provisioning or managing servers. Lambda automatically scales your application by running code in response to every event, allowing event-driven architectures and solutions. The code runs in parallel and processes each event individually, scaling with the size of the workload, from a few requests per day to hundreds of thousands of workloads. The following diagram illustrates the architecture of our solution.
You can package your code and dependencies as a container image using tools such as the Docker CLI. The maximum container size is 10 GB. After the model for inference is Dockerized, you can upload the image to Amazon Elastic Container Registry (Amazon ECR). You can then create the Lambda function from the container imaged stored in Amazon ECR.
Prerequisites
For this walkthrough, you should have the following prerequisites:
- An AWS account
- The AWS Command Line Interface (AWS CLI) installed and configured to interact with AWS services locally
- The Docker CLI
Implementing the solution
We use a pre-trained model from the TensorFlow Hub for image classification. When an image is uploaded to an Amazon Simple Storage Service (Amazon S3) bucket, a Lambda function is invoked to detect the image and print it to the Amazon CloudWatch logs. The following diagram illustrates this workflow.
To implement the solution, complete the following steps:
- On your local machine, create a folder with the name
lambda-tensorflow-example
. - Create a
requirements.txt
file in that directory. - Add all the needed libraries for your ML model. For this post, we use TensorFlow 2.4.
- Create an
app.py
script that contains the code for the Lambda function. - Create a Dockerfile in the same directory.
The following text is an example of the requirements.txt file to run TensorFlow code for our use case:
# List all python libraries for the lambda
tensorflow==2.4.0
tensorflow_hub==0.11
The Python code is placed in app.py. The inference function in app.py needs to follow a specific structure to be invoked by the Lambda runtime. For more information about handlers for Lambda, see AWS Lambda function handler in Python. See the following code:
import json
import boto3
import numpy as np
import PIL.Image as Image
import tensorflow as tf
import tensorflow_hub as hub
IMAGE_WIDTH = 224
IMAGE_HEIGHT = 224
IMAGE_SHAPE = (IMAGE_WIDTH, IMAGE_HEIGHT)
model = tf.keras.Sequential([hub.KerasLayer("model/")])
model.build([None, IMAGE_WIDTH, IMAGE_HEIGHT, 3])
imagenet_labels= np.array(open('model/ImageNetLabels.txt').read().splitlines())
s3 = boto3.resource('s3')
def lambda_handler(event, context):
bucket_name = event['Records'][0]['s3']['bucket']['name']
key = event['Records'][0]['s3']['object']['key']
img = readImageFromBucket(key, bucket_name).resize(IMAGE_SHAPE)
img = np.array(img)/255.0
prediction = model.predict(img[np.newaxis, ...])
predicted_class = imagenet_labels[np.argmax(prediction[0], axis=-1)]
print('ImageName: {0}, Prediction: {1}'.format(key, predicted_class))
def readImageFromBucket(key, bucket_name):
bucket = s3.Bucket(bucket_name)
object = bucket.Object(key)
response = object.get()
return Image.open(response['Body'])
The following Dockerfile for Python 3.8 uses the AWS provided open-source base images that can be used to create container images. The base images are preloaded with language runtimes and other components required to run a container image on Lambda.
# Pull the base image with python 3.8 as a runtime for your Lambda
FROM public.ecr.aws/lambda/python:3.8
# Install OS packages for Pillow-SIMD
RUN yum -y install tar gzip zlib freetype-devel
gcc
ghostscript
lcms2-devel
libffi-devel
libimagequant-devel
libjpeg-devel
libraqm-devel
libtiff-devel
libwebp-devel
make
openjpeg2-devel
rh-python36
rh-python36-python-virtualenv
sudo
tcl-devel
tk-devel
tkinter
which
xorg-x11-server-Xvfb
zlib-devel
&& yum clean all
# Copy the earlier created requirements.txt file to the container
COPY requirements.txt ./
# Install the python requirements from requirements.txt
RUN python3.8 -m pip install -r requirements.txt
# Replace Pillow with Pillow-SIMD to take advantage of AVX2
RUN pip uninstall -y pillow && CC="cc -mavx2" pip install -U --force-reinstall pillow-simd
# Copy the earlier created app.py file to the container
COPY app.py ./
# Download ResNet50 and store it in a directory
RUN mkdir model
RUN curl -L https://tfhub.dev/google/imagenet/resnet_v1_50/classification/4?tf-hub-format=compressed -o ./model/resnet.tar.gz
RUN tar -xf model/resnet.tar.gz -C model/
RUN rm -r model/resnet.tar.gz
# Download ImageNet labels
RUN curl https://storage.googleapis.com/download.tensorflow.org/data/ImageNetLabels.txt -o ./model/ImageNetLabels.txt
# Set the CMD to your handler
CMD ["app.lambda_handler"]
Your folder structure should look like the following screenshot.
You can build and push the container image to Amazon ECR with the following bash commands. Replace the <AWS_ACCOUNT_ID> with your own AWS account ID and also specify a <REGION>.
# Build the docker image
docker build -t lambda-tensorflow-example .
# Create a ECR repository
aws ecr create-repository --repository-name lambda-tensorflow-example --image-scanning-configuration scanOnPush=true --region <REGION>
# Tag the image to match the repository name
docker tag lambda-tensorflow-example:latest <AWS_ACCOUNT_ID>.dkr.ecr.<REGION>.amazonaws.com/lambda-tensorflow-example:latest
# Register docker to ECR
aws ecr get-login-password --region <REGION> | docker login --username AWS --password-stdin <AWS_ACCOUNT_ID>.dkr.ecr.<REGION>.amazonaws.com
# Push the image to ECR
docker push <AWS_ACCOUNT_ID>.dkr.ecr.<REGION>.amazonaws.com/lambda-tensorflow-example:latest
If you want to test your model inference locally, the base images for Lambda include a Runtime Interface Emulator (RIE) that allows you to also locally test your Lambda function packaged as a container image to speed up the development cycles.
Creating an S3 bucket
As a next step, we create an S3 bucket to store the images used to predict the image class.
- On the Amazon S3 console, choose Create bucket.
- Give the S3 bucket a name, such as
tensorflow-images-for-inference-<Random_String>
and replace the <Random_String> with a random value. - Choose Create bucket.
Creating the Lambda function with the TensorFlow code
To create your Lambda function, complete the following steps:
- On the Lambda console, choose Functions.
- Choose Create function.
- Select Container image.
- For Function name, enter a name, such as
tensorflow-endpoint
. - For Container image URI, enter the earlier created
lambda-tensorflow-example
repository.
- Choose Browse images to choose the latest image.
- Click Create function to initialize the creation of it.
- To improve the Lambda runtime, increase the function memory to at least 6 GB and timeout to 5 minutes in the Basic settings.
For more information about function memory and timeout settings, see New for AWS Lambda – Functions with Up to 10 GB of Memory and 6 vCPUs.
Connecting the S3 bucket to your Lambda function
After the successful creation of the Lambda function, we need to add a trigger to it so that whenever a file is uploaded to the S3 bucket, the function is invoked.
- On the Lambda console, choose your function.
- Choose Add trigger.
- Choose S3.
- For Bucket, choose the bucket you created earlier.
After the trigger is added, you need to allow the Lambda function to connect to the S3 bucket by setting the appropriate AWS Identity and Access Management (IAM) rights for its execution role.
- On the Permissions tab for your function, choose the IAM role.
- Choose Attach policies.
- Search for
AmazonS3ReadOnlyAccess
and attach it to the IAM role.
Now you have configured all the necessary services to test your function. Upload a JPG image to the created S3 bucket by opening the bucket in the AWS management console and clicking Upload. After a few seconds, you can see the result of the prediction in the CloudWatch logs. As a follow-up step, you could store the predictions in an Amazon DynamoDB table.
After uploading a JPG picture to the S3 bucket we will get the predicted image class as a result printed to CloudWatch. The Lambda function will be triggered by EventBridge and pull the image from the bucket. As an example, we are going to use the picture of this parrot to get predicted by our inference endpoint.
In the CloudWatch logs the predicted class is printed. Indeed, the model predicts the correct class for the picture (macaw):
Performance
In order to achieve optimal performance, you can try various levels of memory setting (which linearly changes the assigned vCPU, to learn more, read this AWS News Blog). In the case of our deployed model, we realize most performance gains at about 3GB – 4GB (~2vCPUs) setting and gains beyond that are relatively low. Different models see different level of performance improvement by increased amount of CPU so it is best to determine this experimentally for your own model. Additionally, it is highly recommended that you compile your source code to take advantage of Advanced Vector Extensions 2 (AVX2) on Lambda that further increases the performance by allowing vCPUs to run higher number of integer and floating-point operations per clock cycle.
Conclusion
Container image support for Lambda allows you to customize your function even more, opening up a lot of new use cases for serverless ML. You can bring your custom models and deploy them on Lambda using up to 10 GB for the container image size. For smaller models that don’t need much computing power, you can perform online training and inference purely in Lambda. When the model size increases, cold start issues become more and more important and need to be mitigated. There is also no restriction on the framework or language with container images; other ML frameworks such as PyTorch, Apache MXNet, XGBoost, or Scikit-learn can be used as well!
If you do require GPU for your inference, you can consider using containers services such as Amazon Elastic Container Service (Amazon ECS), Kubernetes, or deploy the model to an Amazon SageMaker endpoint.
About the Author
Jan Bauer is a Cloud Application Developer at AWS Professional Services. His interests are serverless computing, machine learning, and everything that involves cloud computing.
Process documents containing handwritten tabular content using Amazon Textract and Amazon A2I
Even in this digital age where more and more companies are moving to the cloud and using machine learning (ML) or technology to improve business processes, we still see a vast number of companies reach out and ask about processing documents, especially documents with handwriting. We see employment forms, time cards, and financial applications with tables and forms that contain handwriting in addition to printed information. To complicate things, each document can be in various formats, and each institution within any given industry may have several different formats. Organizations are looking for a simple solution that can process complex documents with varying formats, including tables, forms, and tabular data.
Extracting data from these documents, especially when you have a combination of printed and handwritten text, is error-prone, time-consuming, expensive, and not scalable. Text embedded in tables and forms adds to the extraction and processing complexity. Amazon Textract is an AWS AI service that automatically extracts printed text, handwriting, and other data from scanned documents that goes beyond simple optical character recognition (OCR) to identify, understand, and extract data from forms and tables.
After the data is extracted, the postprocessing step in a document management workflow involves reviewing the entries and making changes as required by downstream processing applications. Amazon Augmented AI (Amazon A2I) makes it easy to configure a human review into your ML workflow. This allows you to automatically have a human step to review your ML pipeline if the results fall below a specified confidence threshold, set up review and auditing workflows, and modify the prediction results as needed.
In this post, we show how you can use the Amazon Textract Handwritten feature to extract tabular data from documents and have a human review loop using the Amazon A2I custom task type to make sure that the predictions are highly accurate. We store the results in Amazon DynamoDB, which is a key-value and document database that delivers single-digit millisecond performance at any scale, making the data available for downstream processing.
We walk you through the following steps using a Jupyter notebook:
- Use Amazon Textract to retrieve tabular data from the document and inspect the response.
- Set up an Amazon A2I human loop to review and modify the Amazon Textract response.
- Evaluating the Amazon A2I response and storing it in DynamoDB for downstream processing.
Prerequisites
Before getting started, let’s configure the walkthrough Jupyter notebook using an AWS CloudFormation template and then create an Amazon A2I private workforce, which is needed in the notebook to set up the custom Amazon A2I workflow.
Setting up the Jupyter notebook
We deploy a CloudFormation template that performs much of the initial setup work for you, such as creating an AWS Identity and Access Management (IAM) role for Amazon SageMaker, creating a SageMaker notebook instance, and cloning the GitHub repo into the notebook instance.
- Choose Launch Stack to configure the notebook in the US East (N. Virginia) Region:
- Don’t make any changes to stack name or parameters.
- In the Capabilities section, select I acknowledge that AWS CloudFormation might create IAM resources.
- Choose Create stack.
The following screenshot of the stack details page shows the status of the stack as
CREATE_IN_PROGRESS
. It can take up to 20 minutes for the status to change to CREATE_COMPLETE
.
- On the SageMaker console, choose Notebook Instances.
- Choose Open Jupyter for the
TextractA2INotebook
notebook you created. - Open
textract-hand-written-a2i-forms.ipynb
and follow along there.
Setting up an Amazon A2I private workforce
For this post, you create a private work team and add only one user (you) to it. For instructions, see Create a Private Workforce (Amazon SageMaker Console). When the user (you) accepts the invitation, you have to add yourself to the workforce. For instructions, see the Add a Worker to a Work Team section in Manage a Workforce (Amazon SageMaker Console).
After you create a labeling workforce, copy the workforce ARN and enter it in the notebook cell to set up a private review workforce:
WORKTEAM_ARN=
"<your workteam ARN>"
In the following sections, we walk you through the steps to use this notebook.
Retrieving tabular data from the document and inspecting the response
In this section, we go through the following steps using the walkthrough notebook:
- Review the sample data, which has both printed and handwritten content.
- Set up the helper functions to parse the Amazon Textract response.
- Inspect and analyze the Amazon Textract response.
Reviewing the sample data
Review the sample data by running the following notebook cell:
# Document
documentName = "test_handwritten_document.png"
display(Image(filename=documentName))
We use the following sample document, which has both printed and handwritten content in tables.
Use the Amazon Textract Parser Library to process the response
We will now import the Amazon Textract Response Parser library to parse and extract what we need from Amazon Textract’s response. There are two main functions here. One, we will extract the form data (key-value pairs) part of the header section of the document. Two, we will parse the table and cells to create a csv file containing the tabular data. In this notebook, we will use Amazon Textract’s Sync API for document extraction, AnalyzeDocument. This accepts image files (png or jpeg) as an input.
client = boto3.client(
service_name='textract',
region_name= 'us-east-1',
endpoint_url='https://textract.us-east-1.amazonaws.com',
)
with open(documentName, 'rb') as file:
img_test = file.read()
bytes_test = bytearray(img_test)
print('Image loaded', documentName)
# process using image bytes
response = client.analyze_document(Document={'Bytes': bytes_test}, FeatureTypes=['TABLES','FORMS'])
You can use the Amazon Textract Response Parser library to easily parse JSON returned by Amazon Textract. The library parses JSON and provides programming language specific constructs to work with different parts of the document. For more details, please refer to the Amazon Textract Parser Library
from trp import Document
# Parse JSON response from Textract
doc = Document(response)
# Iterate over elements in the document
for page in doc.pages:
# Print lines and words
for line in page.lines:
print("Line: {}".format(line.text))
for word in line.words:
print("Word: {}".format(word.text))
# Print tables
for table in page.tables:
for r, row in enumerate(table.rows):
for c, cell in enumerate(row.cells):
print("Table[{}][{}] = {}".format(r, c, cell.text))
# Print fields
for field in page.form.fields:
print("Field: Key: {}, Value: {}".format(field.key.text, field.value.text))
Now that we have the contents we need from the document image, let’s create a csv file to store it and also use it for setting up the Amazon A2I human loop for review and modification as needed.
# Lets get the form data into a csv file
with open('test_handwritten_form.csv', 'w', newline='') as csvfile:
formwriter = csv.writer(csvfile, delimiter=',',
quoting=csv.QUOTE_MINIMAL)
for field in page.form.fields:
formwriter.writerow([field.key.text+" "+field.value.text])
# Lets get the table data into a csv file
with open('test_handwritten_tab.csv', 'w', newline='') as csvfile:
tabwriter = csv.writer(csvfile, delimiter=',')
for r, row in enumerate(table.rows):
csvrow = []
for c, cell in enumerate(row.cells):
if cell.text:
csvrow.append(cell.text.rstrip())
#csvrow += '{}'.format(cell.text.rstrip())+","
tabwriter.writerow(csvrow)
Alternatively, if you would like to modify this notebook to use a PDF file or for batch processing of documents, use the StartDocumentAnalysis API. StartDocumentAnalysis
returns a job identifier (JobId
) that you use to get the results of the operation. When text analysis is finished, Amazon Textract publishes a completion status to the Amazon Simple Notification Service (Amazon SNS) topic that you specify in NotificationChannel
. To get the results of the text analysis operation, first check that the status value published to the Amazon SNS topic is SUCCEEDED. If so, call GetDocumentAnalysis
, and pass the job identifier (JobId
) from the initial call to StartDocumentAnalysis
.
Inspecting and analyzing the Amazon Textract response
We now load the form line items into a Pandas DataFrame and clean it up to ensure we have the relevant columns and rows that downstream applications need. We then send it to Amazon A2I for human review.
Run the following notebook cell to inspect and analyze the key-value data from the Amazon Textract response:
# Load the csv file contents into a dataframe, strip out extra spaces, use comma as delimiter
df_form = pd.read_csv('test_handwritten_form.csv', header=None, quoting=csv.QUOTE_MINIMAL, sep=',')
# Rename column
df_form = df_form.rename(columns={df_form.columns[0]: 'FormHeader'})
# display the dataframe
df_form
The following screenshot shows our output.
Run the following notebook cell to inspect and analyze the tabular data from the Amazon Textract response:
# Load the csv file contents into a dataframe, strip out extra spaces, use comma as delimiter
df_tab = pd.read_csv('test_handwritten_tab.csv', header=1, quoting=csv.QUOTE_MINIMAL, sep=',')
# display the dataframe
df_tab.head()
The following screenshot shows our output.
We can see that Amazon Textract detected both printed and handwritten content from the tabular data.
Setting up an Amazon A2I human loop
Amazon A2I supports two built-in task types: Amazon Textract key-value pair extraction and Amazon Rekognition image moderation, and a custom task type that you can use to integrate a human review loop into any ML workflow. You can use a custom task type to integrate Amazon A2I with other AWS services like Amazon Comprehend, Amazon Transcribe, and Amazon Translate, as well as your own custom ML workflows. To learn more, see Use Cases and Examples using Amazon A2I.
In this section, we show how to use the Amazon A2I custom task type to integrate with Amazon Textract tables and key-value pairs through the walkthrough notebook for low-confidence detection scores from Amazon Textract responses. It includes the following steps:
- Create a human task UI.
- Create a workflow definition.
- Send predictions to Amazon A2I human loops.
- Sign in to the worker portal and annotate or verify the Amazon Textract results.
Creating a human task UI
You can create a task UI for your workers by creating a worker task template. A worker task template is an HTML file that you use to display your input data and instructions to help workers complete your task. If you’re creating a human review workflow for a custom task type, you must create a custom worker task template using HTML code. For more information, see Create Custom Worker Task Template.
For this post, we created a custom UI HTML template to render Amazon Textract tables and key-value pairs in the notebook. You can find the template tables-keyvalue-sample.liquid.html in our GitHub repo and customize it for your specific document use case.
This template is used whenever a human loop is required. We have over 70 pre-built UIs available on GitHub. Optionally, you can create this workflow definition on the Amazon A2I console. For instructions, see Create a Human Review Workflow.
After you create this custom template using HTML, you must use this template to generate an Amazon A2I human task UI Amazon Resource Name (ARN). This ARN has the following format: arn:aws:sagemaker
:<aws-region>:<aws-account-number>:human-task-u
i/<template-name>. This ARN is associated with a worker task template resource that you can use in one or more human review workflows (flow definitions). Generate a human task UI ARN using a worker task template by using the CreateHumanTaskUi API operation by running the following notebook cell:
def create_task_ui():
'''
Creates a Human Task UI resource.
Returns:
struct: HumanTaskUiArn
'''
response = sagemaker_client.create_human_task_ui(
HumanTaskUiName=taskUIName,
UiTemplate={'Content': template})
return response
# Create task UI
humanTaskUiResponse = create_task_ui()
humanTaskUiArn = humanTaskUiResponse['HumanTaskUiArn']
print(humanTaskUiArn)
The preceding code gives you an ARN as output, which we use in setting up flow definitions in the next step:
arn:aws:sagemaker:us-east-1:<aws-account-nr>:human-task-ui/ui-hw-invoice-2021-02-10-16-27-23
Creating the workflow definition
In this section, we create a flow definition. Flow definitions allow us to specify the following:
- The workforce that your tasks are sent to
- The instructions that your workforce receives (worker task template)
- Where your output data is stored
For this post, we use the API in the following code:
create_workflow_definition_response = sagemaker_client.create_flow_definition(
FlowDefinitionName= flowDefinitionName,
RoleArn= role,
HumanLoopConfig= {
"WorkteamArn": WORKTEAM_ARN,
"HumanTaskUiArn": humanTaskUiArn,
"TaskCount": 1,
"TaskDescription": "Review the table contents and correct values as indicated",
"TaskTitle": "Employment History Review"
},
OutputConfig={
"S3OutputPath" : OUTPUT_PATH
}
)
flowDefinitionArn = create_workflow_definition_response['FlowDefinitionArn'] # let's save this ARN for future use
Optionally, you can create this workflow definition on the Amazon A2I console. For instructions, see Create a Human Review Workflow.
Sending predictions to Amazon A2I human loops
We create an item list from the Pandas DataFrame where we have the Amazon Textract output saved. Run the following notebook cell to create a list of items to be sent for review:
NUM_TO_REVIEW = len(df_tab) # number of line items to review
dfstart = df_tab['Start Date'].to_list()
dfend = df_tab['End Date'].to_list()
dfemp = df_tab['Employer Name'].to_list()
dfpos = df_tab['Position Held'].to_list()
dfres = df_tab['Reason for leaving'].to_list()
item_list = [{'row': "{}".format(x), 'startdate': dfstart[x], 'enddate': dfend[x], 'empname': dfemp[x], 'posheld': dfpos[x], 'resleave': dfres[x]} for x in range(NUM_TO_REVIEW)]
item_list
You get an output of all the rows and columns received from Amazon Textract:
[{'row': '0',
'startdate': '1/15/2009 ',
'enddate': '6/30/2011 ',
'empname': 'Any Company ',
'posheld': 'Assistant baker ',
'resleave': 'relocated '},
{'row': '1',
'startdate': '7/1/2011 ',
'enddate': '8/10/2013 ',
'empname': 'Example Corp. ',
'posheld': 'Baker ',
'resleave': 'better opp. '},
{'row': '2',
'startdate': '8/15/2013 ',
'enddate': 'Present ',
'empname': 'AnyCompany ',
'posheld': 'head baker ',
'resleave': 'N/A current '}]
Run the following notebook cell to get a list of key-value pairs:
dforighdr = df_form['FormHeader'].to_list()
hdr_list = [{'hdrrow': "{}".format(x), 'orighdr': dforighdr[x]} for x in range(len(df_form))]
hdr_list
Run the following code to create a JSON response for the Amazon A2I loop by combining the key-value and table list from the preceding cells:
ip_content = {"Header": hdr_list,
'Pairs': item_list,
'image1': s3_img_url
}
Start the human loop by running the following notebook cell:
# Activate human loops
import json
humanLoopName = str(uuid.uuid4())
start_loop_response = a2i.start_human_loop(
HumanLoopName=humanLoopName,
FlowDefinitionArn=flowDefinitionArn,
HumanLoopInput={
"InputContent": json.dumps(ip_content)
}
)
Check the status of human loop with the following code:
completed_human_loops = []
resp = a2i.describe_human_loop(HumanLoopName=humanLoopName)
print(f'HumanLoop Name: {humanLoopName}')
print(f'HumanLoop Status: {resp["HumanLoopStatus"]}')
print(f'HumanLoop Output Destination: {resp["HumanLoopOutput"]}')
print('n')
if resp["HumanLoopStatus"] == "Completed":
completed_human_loops.append(resp)
You get the following output, which shows the status of the human loop and the output destination S3 bucket:
HumanLoop Name: f69bb14e-3acd-4301-81c0-e272b3c77df0
HumanLoop Status: InProgress
HumanLoop Output Destination: {'OutputS3Uri': 's3://sagemaker-us-east-1-<aws-account-nr>/textract-a2i-handwritten/a2i-results/fd-hw-forms-2021-01-11-16-54-31/2021/01/11/16/58/13/f69bb14e-3acd-4301-81c0-e272b3c77df0/output.json'}
Annotating the results via the worker portal
Run the steps in the notebook to check the status of the human loop. You can use the accompanying SageMaker Jupyter notebook to follow the steps in this post.
- Run the following notebook cell to get a login link to navigate to the private workforce portal:
workteamName = WORKTEAM_ARN[WORKTEAM_ARN.rfind('/') + 1:] print("Navigate to the private worker portal and do the tasks. Make sure you've invited yourself to your workteam!") print('https://' + sagemaker_client.describe_workteam(WorkteamName=workteamName)['Workteam']['SubDomain'])
- Choose the login link to the private worker portal.
- Select the human review job.
- Choose Start working.
You’re redirected to the Amazon A2I console, where you find the original document displayed, your key-value pair, the text responses detected from Amazon Textract, and your table’s responses.
Scroll down to find the correction form for key-value pairs and text, where you can verify the results and compare the Amazon Textract response to the original document. You will also find the UI to modify the tabular handwritten and printed content.
You can modify each cell based on the original image response and reenter correct values and submit your response. The labeling workflow is complete when you submit your responses.
Evaluating the results
When the labeling work is complete, your results should be available in the S3 output path specified in the human review workflow definition. The human answers are returned and saved in the JSON file. Run the notebook cell to get the results from Amazon S3:
import re
import pprint
pp = pprint.PrettyPrinter(indent=4)
for resp in completed_human_loops:
splitted_string = re.split('s3://' + 'a2i-experiments' + '/', resp['HumanLoopOutput']['OutputS3Uri'])
output_bucket_key = splitted_string[1]
response = s3.get_object(Bucket='a2i-experiments', Key=output_bucket_key)
content = response["Body"].read()
json_output = json.loads(content)
pp.pprint(json_output)
print('n')
The following code shows a snippet of the Amazon A2I annotation output JSON file:
{ 'flowDefinitionArn': 'arn:aws:sagemaker:us-east-1:<aws-account-nr>:flow-definition/fd-hw-invoice-2021-02-22-23-07-53',
'humanAnswers': [ { 'acceptanceTime': '2021-02-22T23:08:38.875Z',
'answerContent': { 'TrueHdr3': 'Full Name: Jane '
'Smith',
'predicted1': 'relocated',
'predicted2': 'better opp.',
'predicted3': 'N/A, current',
'predictedhdr1': 'Phone '
'Number: '
'555-0100',
'predictedhdr2': 'Mailing '
'Address: '
'same as '
'above',
'predictedhdr3': 'Full Name: '
'Jane Doe',
'predictedhdr4': 'Home '
'Address: '
'123 Any '
'Street, Any '
'Town. USA',
'rating1': { 'agree': True,
'disagree': False},
'rating2': { 'agree': True,
'disagree': False},
'rating3': { 'agree': False,
'disagree': True},
'rating4': { 'agree': True,
'disagree': False},
'ratingline1': { 'agree': True,
'disagree': False},
'ratingline2': { 'agree': True,
'disagree': False},
'ratingline3': { 'agree': True,
'disagree': False}}
Storing the Amazon A2I annotated results in DynamoDB
We now store the form with the updated contents in a DynamoDB table so downstream applications can use it. To automate the process, simply set up an AWS Lambda trigger with DynamoDB to automatically extract and send information to your API endpoints or applications. For more information, see DynamoDB Streams and AWS Lambda Triggers.
To store your results, complete the following steps:
- Get the human answers for the key-values and text into a DataFrame by running the following notebook cell:
#updated array values to be strings for dataframe assignment for i in json_output['humanAnswers']: x = i['answerContent'] for j in range(0, len(df_form)): df_form.at[j, 'TrueHeader'] = str(x.get('TrueHdr'+str(j+1))) df_form.at[j, 'Comments'] = str(x.get('Comments'+str(j+1))) df_form = df_form.where(df_form.notnull(), None)
- Get the human-reviewed answers for tabular data into a DataFrame by running the following cell:
#updated array values to be strings for dataframe assignment for i in json_output['humanAnswers']: x = i['answerContent'] for j in range(0, len(df_tab)): df_tab.at[j, 'TrueStartDate'] = str(x.get('TrueStartDate'+str(j+1))) df_tab.at[j, 'TrueEndDate'] = str(x.get('TrueEndDate'+str(j+1))) df_tab.at[j, 'TrueEmpName'] = str(x.get('TrueEmpName'+str(j+1))) df_tab.at[j, 'TruePosHeld'] = str(x.get('TruePosHeld'+str(j+1))) df_tab.at[j, 'TrueResLeave'] = str(x.get('TrueResLeave'+str(j+1))) df_tab.at[j, 'ChangeComments'] = str(x.get('Change Reason'+str(j+1))) df_tab = df_tab.where(df_tab.notnull(), None)You will get below output:
- Combine the DataFrames into one DataFrame to save in the DynamoDB table:
# Join both the dataframes to prep for insert into DynamoDB df_doc = df_form.join(df_tab, how='outer') df_doc = df_doc.where(df_doc.notnull(), None) df_doc
Creating the DynamoDB table
Create your DynamoDB table with the following code:
# Get the service resource.
dynamodb = boto3.resource('dynamodb')
tablename = "emp_history-"+str(uuid.uuid4())
# Create the DynamoDB table.
table = dynamodb.create_table(
TableName=tablename,
KeySchema=[
{
'AttributeName': 'line_nr',
'KeyType': 'HASH'
}
],
AttributeDefinitions=[
{
'AttributeName': 'line_nr',
'AttributeType': 'N'
},
],
ProvisionedThroughput={
'ReadCapacityUnits': 5,
'WriteCapacityUnits': 5
}
)
# Wait until the table exists.
table.meta.client.get_waiter('table_exists').wait(TableName=tablename)
# Print out some data about the table.
print("Table successfully created. Item count is: " + str(table.item_count))
You get the following output:
Table successfully created. Item count is: 0
Uploading the contents of the DataFrame to a DynamoDB table
Upload the contents of your DataFrame to your DynamoDB table with the following code:
Note: When adding contents from multiple documents in your DynamoDB table, please ensure you add a document number as an attribute to differentiate between documents. In the example below we just use the index as the line_nr because we are working with a single document.
for idx, row in df_doc.iterrows():
table.put_item(
Item={
'line_nr': idx,
'orig_hdr': str(row['FormHeader']) ,
'true_hdr': str(row['TrueHeader']),
'comments': str(row['Comments']),
'start_date': str(row['Start Date ']),
'end_date': str(row['End Date ']),
'emp_name': str(row['Employer Name ']),
'position_held': str(row['Position Held ']),
'reason_for_leaving': str(row['Reason for leaving']),
'true_start_date': str(row['TrueStartDate']),
'true_end_date': str(row['TrueEndDate']),
'true_emp_name': str(row['TrueEmpName']),
'true_position_held': str(row['TruePosHeld']),
'true_reason_for_leaving': str(row['TrueResLeave']),
'change_comments': str(row['ChangeComments'])
}
)
To check if the items were updated, run the following code to retrieve the DynamoDB table value:
response = table.get_item(
Key={
'line_nr': 2
}
)
item = response['Item']
print(item)
Alternatively, you can check the table on the DynamoDB console, as in the following screenshot.
Conclusion
This post demonstrated how easy it is to use services in the AI layer of the AWS AI/ML stack, such as Amazon Textract and Amazon A2I, to read and process tabular data from handwritten forms, and store them in a DynamoDB table for downstream applications to use. You can also send the augmented form data from Amazon A2I to an S3 bucket to be consumed by your AWS analytics applications.
For video presentations, sample Jupyter notebooks, or more information about use cases like document processing, content moderation, sentiment analysis, text translation, and more, see Amazon Augmented AI Resources. If this post helps you or inspires you to solve a problem, we would love to hear about it! The code for this solution is available on the GitHub repo for you to use and extend. Contributions are always welcome!
About the Authors
Prem Ranga is an Enterprise Solutions Architect based out of Atlanta, GA. He is part of the Machine Learning Technical Field Community and loves working with customers on their ML and AI journey. Prem is passionate about robotics, is an autonomous vehicles researcher, and also built the Alexa-controlled Beer Pours in Houston and other locations.
Mona Mona is an AI/ML Specialist Solutions Architect based out of Arlington, VA. She works with the World Wide Public Sector team and helps customers adopt machine learning on a large scale. She is passionate about NLP and ML explainability areas in AI/ML.
Sriharsha M S is an AI/ML specialist solution architect in the Strategic Specialist team at Amazon Web Services. He works with strategic AWS customers who are taking advantage of AI/ML to solve complex business problems. He provides technical guidance and design advice to implement AI/ML applications at scale. His expertise spans application architecture, big data, analytics, and machine learning.
Amazon Scholar Alla Sheffer uses computer graphics to drive improvements in garment sizing and fitting
Complex algorithms promise to fundamentally change a craft that still relies almost entirely on handwork.Read More
Talkdesk and AWS: What AI and speech-to-text mean for the future of contact centers and a better customer experience
This is a guest post authored by Ben Rigby, the VP, Global Head of Product & Engineering, Artificial Intelligence and Machine Learning at Talkdesk. Talkdesk broadens contact center machine learning capabilities with AWS Contact Center Intelligence.
At Talkdesk, we’re driven to reduce friction in the customer journey. Whether that’s surfacing relevant content to agents while they’re on a call, automatically summarizing after call work, or discovering an emerging product issue that’s causing trouble, the goal is to make the customer journey more effortless. The key to reduce this friction is automatic and accurate transcription of 100% of contact center calls.
Although the core job of the contact center hasn’t changed for decades (deliver great service to customers), AI helps us do it better. It plays a central role in the delivery of these experiences. AI helps sift through massive amounts of data, connects the dots, and surfaces relevant information at the right time. In 2020, Talkdesk launched a suite of AI-focused products tailor-made for the contact center and focused on operational efficiency.
Now, we’re teaming with AWS Contact Center Intelligence (AWS CCI) solutions. AWS CCI offers a combination of services, available through the AWS Partner Network (APN), that amplify AI solutions and support AI integration in contact centers. Talkdesk joined the AWS Partner Network in October 2020.
Speech-to-text integration
For the first integration, Talkdesk now offers speech-to-text service through Amazon Transcribe. This integration represents an expansion of Talkdesk’s speech-to-text offering. It allows Talkdesk to expand to over 30 languages and accents for Talkdesk Speech Analytics and Talkdesk QM Assist products. It also expands coverage for live transcription by 11 accents and languages for Talkdesk Agent Assist.
In 2021, Talkdesk will expose all the Amazon Transcribe and Amazon Transcribe Medical features to its clients through an easy-to-use, non-technical interface. This will allow business users to customize speech-to-text using custom vocabularies, custom language models, automatic content redaction, and unwanted word filters. In addition, Talkdesk healthcare and life sciences clients can take advantage of the powerful speech recognition engine in Amazon Transcribe Medical, which supports thousands of medical and sub-specialty terms.
Expanded services
Beyond Amazon Transcribe, we also plan to make other AWS CCI services available for Talkdesk customers. That means better translation, enterprise search, chatbots, business intelligence, and language comprehension for Talkdesk customers. This fusion of technologies is a step beyond what’s in the contact center market right now. The signup process is simple: current Talkdesk customers just need to reach out to their customer success manager to get started. In the future, signing up for these features will be as easy as clicking a button in the Talkdesk AppConnect Store. Stay tuned.
Summary
We’re looking to the future of integrated speech-to-text technology, with high-quality transcription for agents to understand customers in real time and for use in performance and training management. This new integration gives Talkdesk clients a competitive edge—and a chance to transform the contact center experience into something great.
Learn more about the Talkdesk/AWS CCI partnership and how to use AI to transform your contact center by reaching out to your customer success manager.
The content and opinions in this post are those of the third-party author and AWS is not responsible for the content or accuracy of this post.
About the Author
Ben Rigby is the VP, Global Head of Product & Engineering, Artificial Intelligence and Machine Learning at Talkdesk. He oversees strategy and execution of products that use machine learning techniques to increase operational efficiency in the contact center. This product suite spans automation (Virtual Agent, Agent Assist, AI Training Suite), analytics (Speech Analytics), knowledge management (Guide), and security (Guardian). Prior to Talkdesk, Rigby was the Head of AI at Directly, where he led the development and deployment of customer service automation for clients like Samsung, Microsoft, Airbnb, and LinkedIn. Rigby graduated Phi Beta Kappa from Stanford University with Honors and Distinction.