<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: WanjohiChristopher</title>
    <description>The latest articles on DEV Community by WanjohiChristopher (@wanjohichristopher).</description>
    <link>https://dev.to/wanjohichristopher</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F326317%2F0db4c2e4-befa-413b-acc7-1900e8e582a4.JPG</url>
      <title>DEV Community: WanjohiChristopher</title>
      <link>https://dev.to/wanjohichristopher</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/wanjohichristopher"/>
    <language>en</language>
    <item>
      <title>Building an AI-Powered Customer Churn Prediction Pipeline on AWS (Step-by-Step)</title>
      <dc:creator>WanjohiChristopher</dc:creator>
      <pubDate>Thu, 01 Jan 2026 00:56:43 +0000</pubDate>
      <link>https://dev.to/aws-builders/building-an-ai-powered-customer-churn-prediction-pipeline-on-aws-step-by-step-2l1f</link>
      <guid>https://dev.to/aws-builders/building-an-ai-powered-customer-churn-prediction-pipeline-on-aws-step-by-step-2l1f</guid>
      <description>&lt;p&gt;Hey folks! 👋&lt;/p&gt;

&lt;p&gt;I recently built a customer churn prediction system that not only predicts &lt;em&gt;who&lt;/em&gt; will leave — but also explains &lt;em&gt;why&lt;/em&gt; in plain English using Amazon Bedrock.&lt;/p&gt;

&lt;p&gt;In this tutorial, I'll walk you through building the entire pipeline from scratch.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What we achieved:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;✅ &lt;strong&gt;84.2% AUC&lt;/strong&gt; on validation data&lt;/li&gt;
&lt;li&gt;✅ Real-time predictions via SageMaker endpoint&lt;/li&gt;
&lt;li&gt;✅ Natural language explanations powered by Claude (Bedrock)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Let's dive in!&lt;/p&gt;




&lt;h2&gt;
  
  
  🎯 What We're Building
&lt;/h2&gt;

&lt;p&gt;An end-to-end ML pipeline that:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Ingests&lt;/strong&gt; customer data into S3&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Trains&lt;/strong&gt; a churn prediction model with SageMaker XGBoost&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Deploys&lt;/strong&gt; a real-time inference endpoint&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Explains&lt;/strong&gt; predictions using Amazon Bedrock (Claude)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Exposes&lt;/strong&gt; everything via API Gateway + Lambda&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;Prerequisites&lt;/strong&gt;: AWS account, basic Python knowledge&lt;/p&gt;




&lt;h2&gt;
  
  
  🏗️ Architecture Overview
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fodpfhf83fj77qn1aq76i.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fodpfhf83fj77qn1aq76i.png" alt="AWS Churn Prediction Architecture" width="800" height="427"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The pipeline consists of 5 tiers:&lt;/strong&gt;&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Tier&lt;/th&gt;
&lt;th&gt;Services&lt;/th&gt;
&lt;th&gt;Purpose&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Data Ingestion&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;S3&lt;/td&gt;
&lt;td&gt;Store raw customer data&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;ML Training&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;SageMaker Training&lt;/td&gt;
&lt;td&gt;Train XGBoost model&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Model Storage&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;S3&lt;/td&gt;
&lt;td&gt;Store model artifacts&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Inference &amp;amp; AI&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;SageMaker Endpoint, Bedrock&lt;/td&gt;
&lt;td&gt;Real-time predictions + NL explanations&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;API Layer&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;API Gateway, Lambda&lt;/td&gt;
&lt;td&gt;Expose REST API&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  Step 1: Set Up S3 and Upload Data
&lt;/h2&gt;

&lt;p&gt;First, create an S3 bucket and upload the dataset.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Set bucket name with your account ID&lt;/span&gt;
&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;BUCKET_NAME&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;churn-prediction-&lt;span class="si"&gt;$(&lt;/span&gt;aws sts get-caller-identity &lt;span class="nt"&gt;--query&lt;/span&gt; Account &lt;span class="nt"&gt;--output&lt;/span&gt; text&lt;span class="si"&gt;)&lt;/span&gt;

&lt;span class="c"&gt;# Create bucket&lt;/span&gt;
aws s3 mb s3://&lt;span class="nv"&gt;$BUCKET_NAME&lt;/span&gt;

&lt;span class="c"&gt;# Upload your data&lt;/span&gt;
aws s3 &lt;span class="nb"&gt;cp &lt;/span&gt;WA_Fn-UseC_-Telco-Customer-Churn.csv s3://&lt;span class="nv"&gt;$BUCKET_NAME&lt;/span&gt;/raw/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;📥 &lt;strong&gt;Dataset&lt;/strong&gt;: Download the &lt;a href="https://www.kaggle.com/datasets/blastchar/telco-customer-churn" rel="noopener noreferrer"&gt;Telco Customer Churn dataset&lt;/a&gt; from Kaggle.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Step 2: Create SageMaker IAM Role
&lt;/h2&gt;

&lt;p&gt;In AWS Console:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Go to &lt;strong&gt;IAM&lt;/strong&gt; → &lt;strong&gt;Roles&lt;/strong&gt; → &lt;strong&gt;Create role&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Select &lt;strong&gt;SageMaker - Execution&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Add policies: &lt;code&gt;AmazonSageMakerFullAccess&lt;/code&gt; + &lt;code&gt;AmazonS3FullAccess&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Name it: &lt;code&gt;SageMakerChurnRole&lt;/code&gt;
&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  Step 3: Train the Model
&lt;/h2&gt;

&lt;p&gt;Create &lt;code&gt;train_churn.py&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;boto3&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;sagemaker&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;pandas&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;pd&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;sklearn.model_selection&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;train_test_split&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;sagemaker.inputs&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;TrainingInput&lt;/span&gt;

&lt;span class="c1"&gt;# Config
&lt;/span&gt;&lt;span class="n"&gt;BUCKET&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;environ&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;BUCKET_NAME&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="n"&gt;ROLE&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;environ&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;ROLE_ARN&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="n"&gt;PREFIX&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;churn-prediction&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;

&lt;span class="n"&gt;session&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;sagemaker&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Session&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="n"&gt;region&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;boto_region_name&lt;/span&gt;

&lt;span class="c1"&gt;# Load and prepare data
&lt;/span&gt;&lt;span class="n"&gt;df&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;pd&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;read_csv&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;WA_Fn-UseC_-Telco-Customer-Churn.csv&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;df&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;TotalCharges&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;pd&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;to_numeric&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;df&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;TotalCharges&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;errors&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;coerce&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;fillna&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;df&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Churn&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;df&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Churn&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Yes&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;astype&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# Encode categorical columns
&lt;/span&gt;&lt;span class="n"&gt;cat_cols&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;gender&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Partner&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Dependents&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;PhoneService&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;MultipleLines&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
            &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;InternetService&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;OnlineSecurity&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;OnlineBackup&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;DeviceProtection&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
            &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;TechSupport&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;StreamingTV&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;StreamingMovies&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Contract&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
            &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;PaperlessBilling&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;PaymentMethod&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;col&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;cat_cols&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;df&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;col&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;df&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;col&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;astype&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;category&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="n"&gt;cat&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;codes&lt;/span&gt;

&lt;span class="c1"&gt;# Features
&lt;/span&gt;&lt;span class="n"&gt;feature_cols&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;SeniorCitizen&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;tenure&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;MonthlyCharges&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;TotalCharges&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;cat_cols&lt;/span&gt;
&lt;span class="n"&gt;X&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;df&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;feature_cols&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="n"&gt;y&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;df&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Churn&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

&lt;span class="c1"&gt;# Split and save
&lt;/span&gt;&lt;span class="n"&gt;X_train&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;X_test&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;y_train&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;y_test&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;train_test_split&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;X&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;test_size&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mf"&gt;0.2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;random_state&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;42&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;stratify&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;train_df&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;pd&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;concat&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="n"&gt;y_train&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;reset_index&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;drop&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;X_train&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;reset_index&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;drop&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)],&lt;/span&gt; &lt;span class="n"&gt;axis&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;test_df&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;pd&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;concat&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="n"&gt;y_test&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;reset_index&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;drop&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;X_test&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;reset_index&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;drop&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)],&lt;/span&gt; &lt;span class="n"&gt;axis&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;train_df&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;to_csv&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;train.csv&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;index&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;False&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;header&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;False&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;test_df&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;to_csv&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;test.csv&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;index&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;False&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;header&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;False&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# Upload to S3
&lt;/span&gt;&lt;span class="n"&gt;s3&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;boto3&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;client&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;s3&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;s3&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;upload_file&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;train.csv&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;BUCKET&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;PREFIX&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;/train/train.csv&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;s3&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;upload_file&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;test.csv&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;BUCKET&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;PREFIX&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;/test/test.csv&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# Train XGBoost
&lt;/span&gt;&lt;span class="n"&gt;container&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;sagemaker&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;image_uris&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;retrieve&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;xgboost&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;region&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;1.7-1&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;xgb&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;sagemaker&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;estimator&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Estimator&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;image_uri&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;container&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;role&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;ROLE&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;instance_count&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;instance_type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;ml.m5.xlarge&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;output_path&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;s3://&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;BUCKET&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;/&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;PREFIX&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;/output&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;sagemaker_session&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;session&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;xgb&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;set_hyperparameters&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;objective&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;binary:logistic&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;num_round&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;max_depth&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;eta&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mf"&gt;0.2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;eval_metric&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;auc&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;xgb&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fit&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;train&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;TrainingInput&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;s3://&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;BUCKET&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;/&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;PREFIX&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;/train&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;content_type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;csv&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;validation&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;TrainingInput&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;s3://&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;BUCKET&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;/&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;PREFIX&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;/test&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;content_type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;csv&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;

&lt;span class="c1"&gt;# Deploy endpoint
&lt;/span&gt;&lt;span class="n"&gt;predictor&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;xgb&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;deploy&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;initial_instance_count&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;instance_type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;ml.t2.medium&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;endpoint_name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;churn-prediction-endpoint&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;serializer&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;sagemaker&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;serializers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;CSVSerializer&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Run it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;BUCKET_NAME&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;churn-prediction-YOUR_ACCOUNT_ID
&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;ROLE_ARN&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;arn:aws:iam::YOUR_ACCOUNT_ID:role/SageMakerChurnRole
python3 train_churn.py
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Training output:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;2026-01-01 00:24:27 Uploading - Uploading generated training model
2026-01-01 00:24:27 Completed - Training job completed
Training seconds: 103
Billable seconds: 103

✅ Training complete!
Model artifact: s3://churn-prediction-905418352184/churn-prediction/output/sagemaker-xgboost-2026-01-01-00-22-03-339/output/model.tar.gz

Deploying endpoint (3-5 min)...
INFO:sagemaker:Creating model with name: sagemaker-xgboost-2026-01-01-00-24-53-959
INFO:sagemaker:Creating endpoint-config with name churn-prediction-endpoint
INFO:sagemaker:Creating endpoint with name churn-prediction-endpoint
---------------!
✅ Endpoint deployed: churn-prediction-endpoint
Test prediction: 0.4% churn probability
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Step 4: Create Lambda with Bedrock Integration
&lt;/h2&gt;

&lt;p&gt;Create a Lambda function &lt;code&gt;ChurnPredictionAPI&lt;/code&gt; with this code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;boto3&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;

&lt;span class="n"&gt;sagemaker_runtime&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;boto3&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;client&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;sagemaker-runtime&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;bedrock&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;boto3&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;client&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;bedrock-runtime&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;ENDPOINT_NAME&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;environ&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;SAGEMAKER_ENDPOINT&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;churn-prediction-endpoint&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;lambda_handler&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;body&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;loads&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;body&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nf"&gt;isinstance&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;body&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="n"&gt;event&lt;/span&gt;

    &lt;span class="c1"&gt;# Get prediction from SageMaker
&lt;/span&gt;    &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;sagemaker_runtime&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;invoke_endpoint&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;EndpointName&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;ENDPOINT_NAME&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;ContentType&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;text/csv&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;Body&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;body&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;features&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;churn_prob&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;float&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Body&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;read&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;decode&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;

    &lt;span class="c1"&gt;# Generate explanation with Bedrock Claude
&lt;/span&gt;    &lt;span class="n"&gt;prompt&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;A customer has &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;churn_prob&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="o"&gt;%&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; churn probability.
Customer: Tenure &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;body&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;tenure&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;N/A&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; months, $&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;body&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;monthly_charges&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;N/A&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;/month, &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;body&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;contract&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;N/A&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; contract.
In 2 sentences, explain the risk and suggest one retention action.&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;

    &lt;span class="n"&gt;bedrock_response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;bedrock&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;invoke_model&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;modelId&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;anthropic.claude-3-haiku-20240307-v1:0&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;body&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;dumps&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
            &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;anthropic_version&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;bedrock-2023-05-31&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;max_tokens&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;messages&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;role&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;user&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;content&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;prompt&lt;/span&gt;&lt;span class="p"&gt;}]&lt;/span&gt;
        &lt;span class="p"&gt;})&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;explanation&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;loads&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;bedrock_response&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;body&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;read&lt;/span&gt;&lt;span class="p"&gt;())[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;content&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;text&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="n"&gt;risk&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;High&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;churn_prob&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mf"&gt;0.7&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Medium&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;churn_prob&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mf"&gt;0.4&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Low&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;statusCode&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;headers&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Content-Type&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;application/json&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;body&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;dumps&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
            &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;churn_probability&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;churn_prob&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="o"&gt;%&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;risk_level&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;risk&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;explanation&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;explanation&lt;/span&gt;
        &lt;span class="p"&gt;})&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmc4zh52rpmdaj0vguvb3.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmc4zh52rpmdaj0vguvb3.png" alt="Test Successful" width="800" height="387"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Lambda configuration:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Runtime: Python 3.11&lt;/li&gt;
&lt;li&gt;Timeout: 30 seconds&lt;/li&gt;
&lt;li&gt;Role: &lt;code&gt;LambdaChurnRole&lt;/code&gt; (with SageMaker + Bedrock permissions)&lt;/li&gt;
&lt;li&gt;Environment variable: &lt;code&gt;SAGEMAKER_ENDPOINT=churn-prediction-endpoint&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Step 5: Create API Gateway
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Create an &lt;strong&gt;HTTP API&lt;/strong&gt; in API Gateway&lt;/li&gt;
&lt;li&gt;Add Lambda integration → &lt;code&gt;ChurnPredictionAPI&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Create POST route: &lt;code&gt;/predict&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Deploy and get your invoke URL&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fh05fwunl0ri1uv0x56vy.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fh05fwunl0ri1uv0x56vy.png" alt="Create API Gateway" width="800" height="391"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9dof7nenytw0lhbo1udl.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9dof7nenytw0lhbo1udl.png" alt="configure" width="800" height="262"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  🧪 Test the API
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-X&lt;/span&gt; POST &lt;span class="s2"&gt;"https://YOUR_API_URL/predict"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Content-Type: application/json"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s1"&gt;'{
    "features": "0,24,65.5,1500.0,1,0,1,2,0,0,1,1,0,0,1,0,2,1,1",
    "tenure": 24,
    "monthly_charges": 65.5,
    "contract": "Month-to-month"
  }'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgesmii5w1bkowmo61kha.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgesmii5w1bkowmo61kha.png" alt="ChurnAPI" width="800" height="406"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Response:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
(.venv) server@DLG lambda_package % curl -X POST "https://jxairjovmi.execute-api.us-east-1.amazonaws.com/predict" \
  -H "Content-Type: application/json" \
  -d '{
    "features": "0,24,65.5,1500.0,1,0,1,2,0,0,1,1,0,0,1,0,2,1,1",
    "tenure": 24,
    "monthly_charges": 65.5,
    "contract": "Month-to-month"
  }'

{"churn_probability": "0.6%", "risk_level": "Low", "explanation": "The customer's high churn probability of 0.6% and the month-to-month contract indicate a significant risk of losing the customer. To mitigate this risk, a retention action could be to offer the customer a longer-term contract with a discounted monthly rate or additional benefits, which may help increase their loyalty and reduce the likelihood of churn."}%     
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  🧹 Cleanup
&lt;/h2&gt;

&lt;p&gt;Don't forget to delete resources to avoid charges:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Delete SageMaker endpoint (most expensive!)&lt;/span&gt;
aws sagemaker delete-endpoint &lt;span class="nt"&gt;--endpoint-name&lt;/span&gt; churn-prediction-endpoint
aws sagemaker delete-endpoint-config &lt;span class="nt"&gt;--endpoint-config-name&lt;/span&gt; churn-prediction-endpoint

&lt;span class="c"&gt;# Delete Lambda&lt;/span&gt;
aws lambda delete-function &lt;span class="nt"&gt;--function-name&lt;/span&gt; ChurnPredictionAPI

&lt;span class="c"&gt;# Delete S3 bucket&lt;/span&gt;
aws s3 rb s3://&lt;span class="nv"&gt;$BUCKET_NAME&lt;/span&gt; &lt;span class="nt"&gt;--force&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  💡 Key Lessons Learned
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;SageMaker XGBoost is production-ready&lt;/strong&gt; — achieved 84% AUC with minimal tuning.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Bedrock adds real business value&lt;/strong&gt; — converting predictions to actionable insights makes ML accessible to non-technical stakeholders.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;IAM permissions are tricky&lt;/strong&gt; — create roles via Console if CLI gives explicit deny errors.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cost awareness matters&lt;/strong&gt; — always delete endpoints when not in use (~$0.05/hour adds up!)&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Resources
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.kaggle.com/datasets/blastchar/telco-customer-churn" rel="noopener noreferrer"&gt;Telco Churn Dataset (Kaggle)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.aws.amazon.com/sagemaker/latest/dg/xgboost.html" rel="noopener noreferrer"&gt;SageMaker XGBoost Documentation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.aws.amazon.com/bedrock/latest/userguide/" rel="noopener noreferrer"&gt;Amazon Bedrock Developer Guide&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Thanks for reading! If this helped you, follow me for more AWS + Data Engineering content.&lt;/p&gt;

&lt;p&gt;Questions? Leave a comment below!&lt;/p&gt;

</description>
      <category>aws</category>
      <category>machinelearning</category>
      <category>dataengineering</category>
      <category>python</category>
    </item>
    <item>
      <title>𝗩𝗼𝗶𝗰𝗲 𝗔𝗜: 𝗧𝗧𝗦 - 𝗚𝗶𝘃𝗶𝗻𝗴 𝗬𝗼𝘂𝗿 𝗔𝗜 𝗮 𝗩𝗼𝗶𝗰𝗲</title>
      <dc:creator>WanjohiChristopher</dc:creator>
      <pubDate>Tue, 23 Dec 2025 13:45:00 +0000</pubDate>
      <link>https://dev.to/wanjohichristopher/--5a</link>
      <guid>https://dev.to/wanjohichristopher/--5a</guid>
      <description>&lt;p&gt;We've covered how Voice AI listens (ASR), understands (NLU), decides (Dialog Management), remembers (Context), and writes (NLG).&lt;/p&gt;

&lt;p&gt;Now for the final piece: 🔊 Making it speak.&lt;/p&gt;

&lt;p&gt;That's TTS - Text-to-Speech.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F53a954o692eai0l0dv6m.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F53a954o692eai0l0dv6m.gif" alt="TTS" width="600" height="247"&gt;&lt;/a&gt;&lt;br&gt;
𝗧𝗵𝗲 𝗧𝗿𝗮𝗻𝘀𝗳𝗼𝗿𝗺𝗮𝘁𝗶𝗼𝗻:&lt;br&gt;
Input: "Great news! Your flight to Paris is confirmed."&lt;br&gt;
Output: 〰️〰️〰️ (audio waveform).&lt;/p&gt;

&lt;p&gt;𝗧𝗵𝗲 𝗧𝗧𝗦 𝗣𝗶𝗽𝗲𝗹𝗶𝗻𝗲:&lt;br&gt;
1️⃣ 𝗧𝗲𝘅𝘁 𝗔𝗻𝗮𝗹𝘆𝘀𝗶𝘀 &lt;br&gt;
 • "How to pronounce this?"&lt;br&gt;
 • Normalization ($50 → "fifty dollars")&lt;br&gt;
 • Grapheme-to-phoneme conversion&lt;br&gt;
 • Homograph resolution (read vs read)&lt;br&gt;
2️⃣ 𝗣𝗿𝗼𝘀𝗼𝗱𝘆 𝗣𝗿𝗲𝗱𝗶𝗰𝘁𝗶𝗼𝗻 &lt;br&gt;
 • How should it sound?&lt;br&gt;
 • Pitch contour (intonation)&lt;br&gt;
 • Duration (speed)&lt;br&gt;
 • Stress &amp;amp; emphasis&lt;br&gt;
 • Pauses&lt;br&gt;
3️⃣ 𝗔𝗰𝗼𝘂𝘀𝘁𝗶𝗰 𝗠𝗼𝗱𝗲𝗹 &lt;br&gt;
 • Generate mel spectrogram.&lt;br&gt;
 • Tacotron 2, FastSpeech 2, VITS.&lt;br&gt;
 • Maps phonemes → audio features.&lt;br&gt;
4️⃣ 𝗩𝗼𝗰𝗼𝗱𝗲𝗿 &lt;br&gt;
 • Convert to audio waveform.&lt;br&gt;
 • HiFi-GAN, WaveGlow, WaveNet.&lt;br&gt;
 • Spectrogram → actual audio.&lt;/p&gt;

&lt;p&gt;🎯 And that closes the loop:&lt;br&gt;
Listen → Think → Speak&lt;/p&gt;

&lt;p&gt;That’s the full Voice AI pipeline.&lt;/p&gt;

&lt;p&gt;Thanks for following along - next, I'll likely recap the full system and share a few real-world failure modes that make or break Voice AI in production. More coming soon. Keep building!!&lt;/p&gt;

&lt;p&gt;Cheers!!&lt;/p&gt;

</description>
      <category>tts</category>
      <category>ai</category>
    </item>
    <item>
      <title>𝗩𝗼𝗶𝗰𝗲 𝗔𝗜: 𝗡𝗟𝗚 - 𝗧𝘂𝗿𝗻𝗶𝗻𝗴 𝗗𝗲𝗰𝗶𝘀𝗶𝗼𝗻𝘀 𝗜𝗻𝘁𝗼 𝗪𝗼𝗿𝗱𝘀</title>
      <dc:creator>WanjohiChristopher</dc:creator>
      <pubDate>Mon, 22 Dec 2025 13:43:00 +0000</pubDate>
      <link>https://dev.to/wanjohichristopher/--2g92</link>
      <guid>https://dev.to/wanjohichristopher/--2g92</guid>
      <description>&lt;p&gt;Voice AI listens (ASR), understands (NLU), and decides (Dialog Management).&lt;/p&gt;

&lt;p&gt;But decisions aren't responses.&lt;br&gt;
The system knows: &lt;br&gt;
▶️ Action: inform &lt;br&gt;
▶️ Flight: booked &lt;br&gt;
▶️ Destination: Paris &lt;br&gt;
▶️ Date: Dec 20 &lt;br&gt;
▶️ Confirmation: AB123&lt;/p&gt;

&lt;p&gt;That's not what we say to a user.&lt;/p&gt;

&lt;p&gt;This is where 𝗡𝗟𝗚 (Natural Language Generation) comes in.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frj6ubvjql468a6x0yblk.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frj6ubvjql468a6x0yblk.gif" alt="NLG" width="560" height="241"&gt;&lt;/a&gt;&lt;br&gt;
It transforms structured data into natural speech: &lt;br&gt;
Example:&lt;br&gt;
🤖 "Great news! Your flight to Paris on December 20th is confirmed. Your confirmation number is AB123. Have a wonderful trip!"&lt;/p&gt;

&lt;p&gt;𝗧𝗵𝗲 𝗡𝗟𝗚 𝗣𝗶𝗽𝗲𝗹𝗶𝗻𝗲:&lt;br&gt;
1️⃣ 𝗖𝗼𝗻𝘁𝗲𝗻𝘁 𝗣𝗹𝗮𝗻𝗻𝗶𝗻𝗴 &lt;br&gt;
 🔹"What information to convey?" &lt;br&gt;
 🔹Select facts, order them, prioritize.&lt;br&gt;
2️⃣ 𝗦𝗲𝗻𝘁𝗲𝗻𝗰𝗲 𝗣𝗹𝗮𝗻𝗻𝗶𝗻𝗴 &lt;br&gt;
 🔹"How to structure it?" &lt;br&gt;
 🔹One sentence or multiple? &lt;br&gt;
 🔹Combine facts?&lt;br&gt;
3️⃣ 𝗦𝘂𝗿𝗳𝗮𝗰𝗲 𝗥𝗲𝗮𝗹𝗶𝘇𝗮𝘁𝗶𝗼𝗻 &lt;br&gt;
 🔹"What exact words to use?" .&lt;br&gt;
 🔹Grammar, vocabulary, tone, fluency.&lt;/p&gt;

&lt;p&gt;𝗧𝗵𝗲 𝗲𝘃𝗼𝗹𝘂𝘁𝗶𝗼𝗻:&lt;br&gt;
🔹Templates → slot-filling.&lt;br&gt;
🔹Statistical → n-grams, HMMs.&lt;br&gt;
🔹Neural → Seq2Seq, Transformers.&lt;br&gt;
🔹LLMs → GPT, Claude (SOTA) .&lt;br&gt;
Below are 𝗿𝗲𝗰𝗼𝗺𝗺𝗲𝗻𝗱𝗮𝘁𝗶𝗼𝗻s based on use case:&lt;br&gt;
🔹Need predictability → Templates.&lt;br&gt;
🔹Need natural variety → LLM.&lt;br&gt;
🔹Need both → Hybrid (LLM + guardrails).&lt;/p&gt;

&lt;p&gt;The difference between a robotic assistant and a delightful one? NLG.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>beginners</category>
      <category>machinelearning</category>
    </item>
    <item>
      <title>𝗩𝗼𝗶𝗰𝗲 𝗔𝗜: 𝗖𝗼𝗻𝘁𝗲𝘅𝘁 &amp; 𝗠𝗲𝗺𝗼𝗿𝘆 - 𝗪𝗵𝘆 𝗖𝗼𝗻𝘃𝗲𝗿𝘀𝗮𝘁𝗶𝗼𝗻𝘀 𝗗𝗼𝗻'𝘁 𝗥𝗲𝘀𝗲𝘁</title>
      <dc:creator>WanjohiChristopher</dc:creator>
      <pubDate>Sun, 21 Dec 2025 14:40:00 +0000</pubDate>
      <link>https://dev.to/wanjohichristopher/--4c64</link>
      <guid>https://dev.to/wanjohichristopher/--4c64</guid>
      <description>&lt;p&gt;Dialog Management means = deciding what to do next.&lt;/p&gt;

&lt;p&gt;But something else makes Voice AI feel human instead of robotic:&lt;/p&gt;

&lt;p&gt;🧠 Context and memory.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2uwu0umv4babqqqszqmt.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2uwu0umv4babqqqszqmt.gif" alt="Context and Memory" width="720" height="469"&gt;&lt;/a&gt;&lt;br&gt;
𝗪𝗵𝘆 𝗰𝗼𝗻𝘁𝗲𝘅𝘁 𝗺𝗮𝘁𝘁𝗲𝗿𝘀&lt;br&gt;
Consider this exchange:&lt;br&gt;
🗣️ "Book me a flight to Paris." &lt;br&gt;
🗣️ "Make it business class."&lt;br&gt;
That second sentence only makes sense if the system remembers the first.&lt;br&gt;
That's context.&lt;br&gt;
𝗪𝗵𝗮𝘁 𝗰𝗼𝗻𝘁𝗲𝘅𝘁 &amp;amp; 𝗺𝗲𝗺𝗼𝗿𝘆 𝗮𝗰𝘁𝘂𝗮𝗹𝗹𝘆 𝗶𝗻𝗰𝗹𝘂𝗱𝗲:&lt;br&gt;
→ 𝗦𝗵𝗼𝗿𝘁-𝘁𝗲𝗿𝗺 𝗰𝗼𝗻𝘁𝗲𝘅𝘁 (session memory)&lt;br&gt;
 🔹Recent turns.&lt;br&gt;
 🔹Slot values.&lt;br&gt;
 🔹Corrections.&lt;br&gt;
 🔹Current dialog state.&lt;br&gt;
→ 𝗟𝗼𝗻𝗴-𝘁𝗲𝗿𝗺 𝗺𝗲𝗺𝗼𝗿𝘆&lt;br&gt;
 🔹User preferences.&lt;br&gt;
 🔹Past interactions.&lt;br&gt;
 🔹Frequent locations.&lt;br&gt;
 🔹Knowledge (RAG documents).&lt;br&gt;
This information feeds directly into Dialog Management so the system can make better decisions.&lt;/p&gt;

&lt;p&gt;Without memory, every interaction would feel like the first one.&lt;/p&gt;

&lt;p&gt;LLMs can reason - but the architecture decides what to remember, when to retrieve it, and when to forget.&lt;/p&gt;

&lt;p&gt;That balance is what makes Voice AI feel natural and safe.&lt;/p&gt;

</description>
      <category>context</category>
      <category>memory</category>
      <category>voiceai</category>
    </item>
    <item>
      <title>𝗩𝗼𝗶𝗰𝗲 𝗔𝗜: 𝗗𝗶𝗮𝗹𝗼𝗴 𝗠𝗮𝗻𝗮𝗴𝗲𝗺𝗲𝗻𝘁 - 𝗧𝗵𝗲 𝗢𝗿𝗰𝗵𝗲𝘀𝘁𝗿𝗮𝘁𝗼𝗿</title>
      <dc:creator>WanjohiChristopher</dc:creator>
      <pubDate>Sat, 20 Dec 2025 16:07:00 +0000</pubDate>
      <link>https://dev.to/wanjohichristopher/--5bjc</link>
      <guid>https://dev.to/wanjohichristopher/--5bjc</guid>
      <description>&lt;p&gt;We've talked about how Voice AI listens (ASR) and understands (NLU).&lt;/p&gt;

&lt;p&gt;But once the system understands the user, there's a harder question:&lt;br&gt;
👉 What should happen next?&lt;/p&gt;

&lt;p&gt;This is where Dialog Management comes in.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fu3m90fnvkok8qu4fd5w5.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fu3m90fnvkok8qu4fd5w5.gif" alt="Dialogue Management - Conversational Flow" width="80" height="39"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;It's not about generating responses - it's about orchestrating decisions across multiple turns.&lt;/p&gt;

&lt;p&gt;E𝘅𝗮𝗺𝗽𝗹𝗲:&lt;br&gt;
👤 "Book a flight to Paris" &lt;br&gt;
🤖 [dest: Paris, origin: ❓] → "Where from?" &lt;br&gt;
👤 "New York" &lt;br&gt;
🤖 [all slots filled ] → "NYC → Paris. Confirm?"&lt;/p&gt;

&lt;p&gt;That decision flow? That's Dialog Management.&lt;/p&gt;

&lt;p&gt;𝗨𝗻𝗱𝗲𝗿 𝘁𝗵𝗲 𝗵𝗼𝗼𝗱, 𝗶𝘁 𝗵𝗮𝗻𝗱𝗹𝗲𝘀:&lt;br&gt;
→ Tracking conversation state across turns.&lt;br&gt;
→ Knowing what's been said vs what's missing.&lt;br&gt;
→ Deciding when to ask vs when to act.&lt;br&gt;
→ Handling corrections and errors.&lt;br&gt;
→ Executing actions and tools safely.&lt;/p&gt;

&lt;p&gt;This is what turns one-shot commands (from the user) into real conversations.&lt;/p&gt;

&lt;p&gt;Modern Voice AI agents may use LLMs here - but structure is still essential for reliability and safety.&lt;/p&gt;

&lt;p&gt;Without dialog management, even the best models feel unpredictable.&lt;/p&gt;

&lt;p&gt;➡️ Next up: How Voice AI remembers - context &amp;amp; memory management.&lt;/p&gt;

</description>
      <category>asr</category>
      <category>voiceai</category>
      <category>tts</category>
    </item>
    <item>
      <title>𝗩𝗼𝗶𝗰𝗲 𝗔𝗜: 𝗡𝗟𝗨 (𝗡𝗮𝘁𝘂𝗿𝗮𝗹 𝗟𝗮𝗻𝗴𝘂𝗮𝗴𝗲 𝗨𝗻𝗱𝗲𝗿𝘀𝘁𝗮𝗻𝗱𝗶𝗻𝗴) - 𝗨𝗻𝗱𝗲𝗿𝘀𝘁𝗮𝗻𝗱𝗶𝗻𝗴 𝗪𝗵𝗮𝘁 𝗬𝗼𝘂 𝗔𝗰𝘁𝘂𝗮𝗹𝗹𝘆 𝗠𝗲𝗮𝗻𝘁</title>
      <dc:creator>WanjohiChristopher</dc:creator>
      <pubDate>Fri, 19 Dec 2025 13:33:00 +0000</pubDate>
      <link>https://dev.to/wanjohichristopher/--1dp0</link>
      <guid>https://dev.to/wanjohichristopher/--1dp0</guid>
      <description>&lt;p&gt;What happens after the text arrives from ASR.&lt;/p&gt;

&lt;p&gt;🗣️ Say you tell a voice assistant:&lt;br&gt;
 "Book me a flight to Paris next Friday"&lt;/p&gt;

&lt;p&gt;ASR does its job and converts that into text.&lt;/p&gt;

&lt;p&gt;But at this point, the system still doesn’t really understand anything.&lt;br&gt;
It doesn’t know:&lt;br&gt;
 🔹what you’re trying to do.&lt;br&gt;
 🔹which parts of the sentence matter.&lt;br&gt;
 🔹or what information is missing.&lt;/p&gt;

&lt;p&gt;That’s where NLU (Natural Language Understanding) comes in.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fhv24lxqrp8gmypki72cn.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fhv24lxqrp8gmypki72cn.gif" alt="NLU" width="760" height="308"&gt;&lt;/a&gt;&lt;br&gt;
Here’s what NLU figures out behind the scenes:&lt;/p&gt;

&lt;p&gt;1️⃣ - 𝗜𝗻𝘁𝗲𝗻𝘁 𝗖𝗹𝗮𝘀𝘀𝗶𝗳𝗶𝗰𝗮𝘁𝗶𝗼𝗻&lt;br&gt;
 What are you trying to do?&lt;br&gt;
 → You want to book a flight.&lt;/p&gt;

&lt;p&gt;2️⃣ - 𝗘𝗻𝘁𝗶𝘁𝘆 𝗘𝘅𝘁𝗿𝗮𝗰𝘁𝗶𝗼𝗻- details (entities)&lt;br&gt;
 → destination: Paris&lt;br&gt;
 → date: next Friday&lt;/p&gt;

&lt;p&gt;3️⃣ And finally - 𝗦𝗹𝗼𝘁 𝗙𝗶𝗹𝗹𝗶𝗻𝗴 - what’s missing&lt;br&gt;
 → where are you flying from?&lt;/p&gt;

&lt;p&gt;So the system knows it needs to ask a follow-up.&lt;/p&gt;

&lt;p&gt;That's the moment where the conversation starts to feel natural instead of scripted.&lt;/p&gt;

&lt;p&gt;With models like GPT-4 or Claude, etc, a lot of this NLU work can now happen in one step without training separate intent classifiers or entity models. The model reasons about intent, details, and gaps together.&lt;/p&gt;

&lt;p&gt;That’s a big reason modern Voice AI agents feel more flexible than the older "say it exactly this way" systems.&lt;/p&gt;

</description>
      <category>voiceai</category>
      <category>nlu</category>
      <category>tts</category>
      <category>agents</category>
    </item>
    <item>
      <title>ASR (Automatic Speech Recognition)</title>
      <dc:creator>WanjohiChristopher</dc:creator>
      <pubDate>Thu, 18 Dec 2025 22:30:00 +0000</pubDate>
      <link>https://dev.to/wanjohichristopher/asr-automatic-speech-recognition-nab</link>
      <guid>https://dev.to/wanjohichristopher/asr-automatic-speech-recognition-nab</guid>
      <description>&lt;p&gt;Yesterday I shared the full Voice AI pipeline.&lt;br&gt;
Today we're diving deep into Stage 1: ASR (Automatic Speech Recognition).&lt;/p&gt;

&lt;p&gt;You speak → It becomes text.&lt;/p&gt;

&lt;p&gt;Simple, right? Here's what actually happens:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F33dp4n2fuenlgbljane6.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F33dp4n2fuenlgbljane6.png" alt="ASR" width="800" height="441"&gt;&lt;/a&gt;&lt;br&gt;
𝟭. 𝗙𝗲𝗮𝘁𝘂𝗿𝗲 𝗘𝘅𝘁𝗿𝗮𝗰𝘁𝗶𝗼𝗻&lt;br&gt;
Raw audio → Digital representation&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;MFCCs (Mel-Frequency Cepstral Coefficients)&lt;/li&gt;
&lt;li&gt;Spectrograms&lt;/li&gt;
&lt;li&gt;Filter Banks&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;𝟮. 𝗔𝗰𝗼𝘂𝘀𝘁𝗶𝗰 𝗠𝗼𝗱𝗲𝗹𝗶𝗻𝗴&lt;br&gt;
Maps audio features to phonemes&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Traditional: HMM-GMM, DNN-HMM&lt;/li&gt;
&lt;li&gt;Modern: Transformers, Conformers&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;𝟯. 𝗗𝗲𝗰𝗼𝗱𝗶𝗻𝗴 &amp;amp; 𝗟𝗮𝗻𝗴𝘂𝗮𝗴𝗲 𝗠𝗼𝗱𝗲𝗹𝗶𝗻𝗴&lt;br&gt;
Phonemes → Words using probabilities&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Beam Search, CTC, Attention mechanisms&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;𝟰. 𝗣𝗼𝘀𝘁-𝗣𝗿𝗼𝗰𝗲𝘀𝘀𝗶𝗻𝗴&lt;br&gt;
Clean up the output&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Spell checking, punctuation, capitalization&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The evolution has been wild:&lt;/p&gt;

&lt;p&gt;𝗧𝗿𝗮𝗱𝗶𝘁𝗶𝗼𝗻𝗮𝗹 (1980s-2010s):&lt;br&gt;
→ HMM + GMM&lt;br&gt;
→ Required phonetic alignment&lt;br&gt;
→ Separate components stitched together&lt;/p&gt;

&lt;p&gt;𝗦𝗧𝗔𝗧𝗘-𝗢𝗙-𝗧𝗛𝗘-𝗔𝗥𝗧 (Now):&lt;br&gt;
→ Whisper: 680K hours of training, 50+ languages&lt;br&gt;
→ Wav2Vec 2.0: Self-supervised, works with limited data&lt;/p&gt;

&lt;p&gt;Get ASR wrong and your entire voice pipeline fails. It's the foundation.&lt;/p&gt;

&lt;p&gt;I've attached a diagram breaking down the full ASR architecture.&lt;/p&gt;

&lt;p&gt;What ASR model are you using? Any surprises with accuracy or latency?&lt;/p&gt;

</description>
      <category>automatic</category>
      <category>asr</category>
      <category>voiceai</category>
      <category>tts</category>
    </item>
    <item>
      <title>VOICE AI SYSTEM ARCHITECTURE</title>
      <dc:creator>WanjohiChristopher</dc:creator>
      <pubDate>Thu, 18 Dec 2025 04:22:41 +0000</pubDate>
      <link>https://dev.to/wanjohichristopher/voice-ai-system-architecture-4h7</link>
      <guid>https://dev.to/wanjohichristopher/voice-ai-system-architecture-4h7</guid>
      <description>&lt;p&gt;🎙️I’ve been diving deep into Voice AI Agents and decided to map out how they actually work.&lt;/p&gt;

&lt;p&gt;You know when you ask Alexa or ChatGPT Voice a question and it just… responds intelligently?&lt;/p&gt;

&lt;p&gt;There’s a lot happening in that split second.&lt;/p&gt;

&lt;p&gt;How do voice agents work?&lt;/p&gt;

&lt;p&gt;At a high level, every voice agent needs to handle three tasks:&lt;/p&gt;

&lt;p&gt;👉Listen - capture audio and transcribe it&lt;br&gt;
👉Think - interpret intent, reason, plan&lt;br&gt;
👉Speak - generate audio and stream it back to the user&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8lwkwpgsu39l2c00vx99.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8lwkwpgsu39l2c00vx99.gif" alt="Voice AI Architecture" width="480" height="247"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;A Voice AI Agent typically goes through five core stages:&lt;br&gt;
 🔹Speech is converted to text (ASR).&lt;br&gt;
 🔹The system understands intent and entities (NLU).&lt;br&gt;
 🔹It reasons about what action to take (Dialog Manager / Agent Logic).&lt;br&gt;
 🔹It generates a response (NLG).&lt;br&gt;
 🔹Speaks it back naturally (TTS).&lt;/p&gt;

&lt;p&gt;This same agent-style architecture powers Alexa, Siri, Google Assistant, and modern LLM-based voice agents like ChatGPT Voice.&lt;/p&gt;

&lt;p&gt;I put together a diagram to visualize the full end-to-end pipeline behind Voice AI Agents - from speech input to intelligent action and response.&lt;/p&gt;

&lt;p&gt;I’m planning to break down each component and share more on how agent-based voice systems are built.&lt;/p&gt;

&lt;p&gt;Which Voice AI agent do you interact with the most?&lt;/p&gt;

</description>
      <category>voice</category>
      <category>ai</category>
      <category>agents</category>
      <category>tts</category>
    </item>
    <item>
      <title>Building a Production-Ready Data Pipeline on AWS: A Hands-On Guide for Data Engineers</title>
      <dc:creator>WanjohiChristopher</dc:creator>
      <pubDate>Mon, 01 Dec 2025 04:39:37 +0000</pubDate>
      <link>https://dev.to/aws-builders/building-a-production-ready-data-pipeline-on-aws-a-hands-on-guide-for-data-engineers-43c2</link>
      <guid>https://dev.to/aws-builders/building-a-production-ready-data-pipeline-on-aws-a-hands-on-guide-for-data-engineers-43c2</guid>
      <description>&lt;h1&gt;
  
  
  Introduction
&lt;/h1&gt;

&lt;p&gt;Modern data engineering requires scalable, fault-tolerant, and secure architectures. In this article, I walk through a fully operational AWS data pipeline using S3, Kinesis, Glue, Athena, Redshift, and QuickSight.&lt;br&gt;
Everything here is hands-on — every step can be reproduced in your own AWS console, and I will include the exact screenshots from my implementation.&lt;/p&gt;

&lt;p&gt;This article helps anyone learn:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;How to build a real AWS ETL pipeline end-to-end&lt;/li&gt;
&lt;li&gt;How to combine batch + streaming data&lt;/li&gt;
&lt;li&gt;How to orchestrate jobs with Glue + Lambda&lt;/li&gt;
&lt;li&gt;How to query data with Athena and Redshift&lt;/li&gt;
&lt;li&gt;How to build dashboards with QuickSight&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Architecture Overview&lt;/strong&gt;&lt;br&gt;
We will build this architecture:&lt;/p&gt;
&lt;h1&gt;
  
  
  Architecture Components
&lt;/h1&gt;

&lt;ul&gt;
&lt;li&gt;Amazon S3 – Data lake (Raw → Clean → Analytics Zones)&lt;/li&gt;
&lt;li&gt;Amazon Kinesis Data Streams – Real-time data ingestion&lt;/li&gt;
&lt;li&gt;AWS Glue – ETL jobs &amp;amp; data catalog&lt;/li&gt;
&lt;li&gt;AWS Lambda – Event-driven transformations&lt;/li&gt;
&lt;li&gt;Amazon Athena – Serverless SQL analytics&lt;/li&gt;
&lt;li&gt;Amazon Redshift – Data warehouse&lt;/li&gt;
&lt;li&gt;Amazon QuickSight – Dashboards&lt;/li&gt;
&lt;li&gt;AWS Lake Formation – Governance&lt;/li&gt;
&lt;li&gt;Amazon CloudWatch – Monitoring&lt;/li&gt;
&lt;/ul&gt;
&lt;h1&gt;
  
  
  Step 1: Create the Data Lake on Amazon S3
&lt;/h1&gt;

&lt;p&gt;Create an S3 bucket like - your-datalake-name&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;/raw/
/clean/
/analytics/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Flq1tj2af0kylu6dsdh8i.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Flq1tj2af0kylu6dsdh8i.png" alt="Folder Structure" width="800" height="266"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  Step 2: Ingest Real-Time Data using Kinesis
&lt;/h1&gt;

&lt;p&gt;We simulate a real IoT or clickstream pipeline.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step:
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Go to Kinesis → Data Streams&lt;/li&gt;
&lt;li&gt;Click Create Stream&lt;/li&gt;
&lt;li&gt;Name it:&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;code&gt;datalyte_stream&lt;br&gt;
&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;4.Set number of shards = 1 &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F43ftef5foe6cydoywb9z.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F43ftef5foe6cydoywb9z.png" alt="Kinesis Data stream" width="800" height="442"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;✔ Monitoring graph of incoming data&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fv1uvp7o6jrleesfobb2t.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fv1uvp7o6jrleesfobb2t.png" alt="Monitoring" width="800" height="419"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h1&gt;
  
  
  Step 3: Create AWS Glue Crawler (Schema Discovery)
&lt;/h1&gt;

&lt;p&gt;We want Glue to:&lt;/p&gt;

&lt;p&gt;Crawl S3 raw zone&lt;/p&gt;

&lt;p&gt;Detect schema&lt;/p&gt;

&lt;p&gt;Create database + tables in Glue Catalog&lt;/p&gt;

&lt;p&gt;Steps:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Go to AWS Glue → Crawlers&lt;/li&gt;
&lt;li&gt;Click Create Crawler&lt;/li&gt;
&lt;li&gt;Name:
&lt;/li&gt;
&lt;/ol&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;datalyte_raw_crawler
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;ol&gt;
&lt;li&gt;Choose S3 input: s3://ddatalyte-data-lake-cmp/raw/&lt;/li&gt;
&lt;li&gt;Create a new Glue database:
&lt;/li&gt;
&lt;/ol&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;datalyte_db
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h2&gt;
  
  
  Glue Crawler setup
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fw3qkfkjp8i57ri0xgl6t.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fw3qkfkjp8i57ri0xgl6t.png" alt="Crawler setup" width="800" height="374"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  Running Crawler
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Faylzskfnhdh8uocudxdf.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Faylzskfnhdh8uocudxdf.png" alt="Running Crawler" width="800" height="387"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h1&gt;
  
  
  Step 4: Build ETL Transformations with AWS Glue (Spark)
&lt;/h1&gt;

&lt;p&gt;We transform data from raw → clean.&lt;/p&gt;

&lt;p&gt;Steps:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Go to Glue → Jobs&lt;/li&gt;
&lt;li&gt;Click Create Job&lt;/li&gt;
&lt;li&gt;Choose Spark / Python&lt;/li&gt;
&lt;li&gt;Script:
&lt;/li&gt;
&lt;/ol&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import pyspark.sql.functions as F

df = glueContext.create_dynamic_frame.from_catalog(
    database="datalyte_db",
    table_name="raw_data"
).toDF()

clean_df = df.withColumn("processed_at", F.current_timestamp())

clean_dyf = DynamicFrame.fromDF(clean_df, glueContext, "clean_dyf")

glueContext.write_dynamic_frame.from_options(
    frame=clean_dyf,
    connection_type="s3",
    connection_options={"path": "s3://datalyte-data-lake/clean/"},
    format="parquet"
)

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fh516d2z7hb1j0l9nef1l.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fh516d2z7hb1j0l9nef1l.png" alt="Script" width="800" height="333"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h1&gt;
  
  
  Step 5: Query Clean Data using Amazon Athena
&lt;/h1&gt;

&lt;ol&gt;
&lt;li&gt;Go to Athena&lt;/li&gt;
&lt;li&gt;Select database: datalyte_db&lt;/li&gt;
&lt;li&gt;Run this query:
&lt;/li&gt;
&lt;/ol&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;SELECT *
FROM clean_data
LIMIT 20;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkrpzqeh5wweks4bts06z.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkrpzqeh5wweks4bts06z.png" alt="Athena Query Editor" width="800" height="317"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h1&gt;
  
  
  Step 6: Load Analytics Data into Redshift
&lt;/h1&gt;

&lt;p&gt;Now we push curated data into Redshift for BI.&lt;/p&gt;

&lt;p&gt;Steps:&lt;br&gt;
1.Create a Redshift Serverless Workspace&lt;br&gt;
2.Create a schema:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;CREATE SCHEMA datalyte;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;3.Load S3 data into Redshift:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;COPY datalyte.analytics_table
FROM 's3://datalyte-data-lake/analytics/'
IAM_ROLE '&amp;lt;your_redshift_role&amp;gt;'
FORMAT AS PARQUET;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvju7wqcxboluvvc1wr5f.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvju7wqcxboluvvc1wr5f.png" alt="Redshift Query EDITOR" width="800" height="257"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  Step 7: Build a Dashboard with QuickSight
&lt;/h1&gt;

&lt;ol&gt;
&lt;li&gt;Connect QuickSight to Redshift&lt;/li&gt;
&lt;li&gt;Import analytics table&lt;/li&gt;
&lt;li&gt;Create:&lt;/li&gt;
&lt;/ol&gt;

&lt;ul&gt;
&lt;li&gt;Line charts&lt;/li&gt;
&lt;li&gt;Bar charts&lt;/li&gt;
&lt;li&gt;KPIs (metrics)&lt;/li&gt;
&lt;/ul&gt;

&lt;h1&gt;
  
  
  Conclusion
&lt;/h1&gt;

&lt;p&gt;This hands-on project demonstrates how AWS services like S3, Glue, Kinesis, Athena, and Redshift come together to form a complete, production-ready data pipeline. With this foundation in place, you can now extend the architecture to include BI, machine learning, or more advanced data engineering workflows on AWS.&lt;/p&gt;

</description>
      <category>aws</category>
      <category>dataengineering</category>
      <category>tutorial</category>
      <category>architecture</category>
    </item>
    <item>
      <title>Building a Real-Time Data Lake on AWS: S3, Glue, and Athena in Production</title>
      <dc:creator>WanjohiChristopher</dc:creator>
      <pubDate>Mon, 24 Nov 2025 04:57:35 +0000</pubDate>
      <link>https://dev.to/aws-builders/building-a-real-time-data-lake-on-aws-s3-glue-and-athena-in-production-4g0b</link>
      <guid>https://dev.to/aws-builders/building-a-real-time-data-lake-on-aws-s3-glue-and-athena-in-production-4g0b</guid>
      <description>&lt;p&gt;&lt;strong&gt;The 3 AM Wake-Up Call&lt;/strong&gt;&lt;br&gt;
It was a Tuesday morning at AWS Reinvent 2024 Las Vegas, when my phone lit up with a PagerDuty alert. Our analytics dashboard had timed out, and the culprit was an Athena query scanning 47 TB of S3 data-costing us $235 in a single failed attempt. The query was looking for a week's worth of user events, but due to poor partitioning, Athena had to scan three years of historical data.&lt;/p&gt;

&lt;p&gt;That incident taught me an expensive lesson: a well-architected data lake isn't just about storing data cheaply in S3. It's about making that data queryable, maintainable, and cost-effective at scale.&lt;br&gt;
Over four years of building data platforms on AWS, I've learned that the difference between a $500/month data lake and a $15,000/month one often comes down to a handful of architectural decisions. In this article, I'll share the battle-tested patterns that have helped me and the teams I've worked with build production-grade data lakes that are both performant and economical.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Table of Contents&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Architecture foundations: The multi-zone approach that separates concerns&lt;/li&gt;
&lt;li&gt;Partitioning strategies: The single biggest lever for query performance and cost&lt;/li&gt;
&lt;li&gt;Schema evolution: How to change schemas without breaking downstream systems&lt;/li&gt;
&lt;li&gt;Query optimization: Techniques that reduced our query times by 85%&lt;/li&gt;
&lt;li&gt;Cost optimization: Real tactics that saved thousands per month.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Architecture Overview: The Three-Zone Data Lake&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The foundation of a production data lake is separation of concerns. I've found the three-zone architecture to be the most practical approach:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8v740oodq34i7q6m1l8q.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8v740oodq34i7q6m1l8q.png" alt="Architecture" width="800" height="341"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Raw Zone (Bronze):&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Stores data exactly as received from source systems&lt;/li&gt;
&lt;li&gt;No transformations, no schema enforcement&lt;/li&gt;
&lt;li&gt;Serves as the "source of truth" for reprocessing
Example path: s3://datalake-raw/source_system/table_name/year=2024/month=11/day=23/&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Processed Zone (Silver):&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Cleaned, deduplicated, and validated data&lt;/li&gt;
&lt;li&gt;Type conversions applied (strings to proper data types)&lt;/li&gt;
&lt;li&gt;Bad records filtered or quarantined
Example path: s3://datalake-processed/domain/table_name/year=2024/month=11/day=23/&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Curated Zone (Gold):&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Business-level aggregations and joins&lt;/li&gt;
&lt;li&gt;Optimized for specific analytics use cases&lt;/li&gt;
&lt;li&gt;Often denormalized for query performance
Example path: s3://datalake-curated/analytics/user_activity_summary/year=2024/month=11/&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;The Glue Catalog:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;AWS Glue Catalog sits at the center, acting as a centralized metadata repository:&lt;/p&gt;

&lt;p&gt;Stores table schemas, partition information, and statistics&lt;br&gt;
Shared across Athena, Glue ETL jobs, Redshift Spectrum, and EMR&lt;br&gt;
Enables schema-on-read: define structure at query time, not ingestion time&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Typical Data Flow&lt;/strong&gt;&lt;br&gt;
&lt;strong&gt;Ingestion:&lt;/strong&gt; Data lands in Raw Zone (JSON, CSV, or streaming via Kinesis)&lt;br&gt;
&lt;strong&gt;Processing:&lt;/strong&gt; Glue ETL job reads from Raw, transforms, writes Parquet to Processed Zone&lt;br&gt;
*&lt;em&gt;Curation: *&lt;/em&gt; Another Glue job aggregates and joins data, writes to Curated Zone.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Query:&lt;/strong&gt; Athena queries the Curated Zone for fast, cost-effective analytics.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Partitioning Strategies: Biggest Cost Lever&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Please note proper partitioning is the difference between a $5 query and a $500 query. &lt;br&gt;
Here's what I've learned works.&lt;br&gt;
&lt;strong&gt;Time-Based Partitioning (The Foundation)&lt;/strong&gt;&lt;br&gt;
For time-series data (logs, events, transactions), partition by date:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;CREATE EXTERNAL TABLE user_events (
    user_id STRING,
    event_type STRING,
    timestamp BIGINT,
    properties STRING
)
PARTITIONED BY (
    year STRING,
    month STRING,
    day STRING
)
STORED AS PARQUET
LOCATION 's3://datalake-processed/events/user_events/';
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Why year/month/day separately? &lt;strong&gt;Flexibility&lt;/strong&gt;. &lt;br&gt;
You can query:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A single day: WHERE year='2024' AND month='11' AND day='23'&lt;/li&gt;
&lt;li&gt;An entire month: WHERE year='2024' AND month='11'&lt;/li&gt;
&lt;li&gt;Multiple months: WHERE year='2024' AND month IN ('10','11')&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Partition Projection: The Game Changer&lt;/strong&gt;&lt;br&gt;
Without partition projection, you need to run MSCK REPAIR TABLE or ALTER TABLE ADD PARTITION every time new data arrives. With projection, Athena generates partition values automatically:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;CREATE EXTERNAL TABLE user_events (
    user_id STRING,
    event_type STRING,
    properties STRING
)
PARTITIONED BY (dt STRING)
STORED AS PARQUET
LOCATION 's3://datalake-processed/events/user_events/'
TBLPROPERTIES (
    'projection.enabled' = 'true',
    'projection.dt.type' = 'date',
    'projection.dt.range' = '2023-01-01,NOW',
    'projection.dt.format' = 'yyyy-MM-dd',
    'storage.location.template' = 's3://datalake-processed/events/user_events/dt=${dt}'
);
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Real impact: This eliminated our nightly partition maintenance job and reduced query planning time from 10 seconds to sub-second.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Schema Evolution: Adding Columns Without Breaking Everything&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The cardinal rule: append columns to the end, never remove or reorder.&lt;/p&gt;

&lt;p&gt;Adding a new column (Safe):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ALTER TABLE user_events 
ADD COLUMNS (device_type STRING);
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Old Parquet files without this column? No problem. Athena returns NULL for missing columns.&lt;/p&gt;

&lt;p&gt;Changing data types (Don't Do this):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;-- DON'T DO THIS:
ALTER TABLE user_events 
CHANGE COLUMN user_id user_id BIGINT;  -- Was STRING
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If old files have non-numeric user IDs, queries will fail. Instead: add a new column (user_id_v2) and backfill gradually.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The Golden Rules&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Add, don't modify - New columns are safe, changing existing ones is dangerous.&lt;/li&gt;
&lt;li&gt;Make new columns nullable - Old data won't have values.&lt;/li&gt;
&lt;li&gt;Use separate tables for breaking changes - user_events_v2 is better than breaking user_events.&lt;/li&gt;
&lt;li&gt;Document schema versions - Add version info in table properties or a separate registry.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;File Formats &amp;amp; Query Optimization: Speed and Cost in Harmony&lt;/strong&gt;&lt;br&gt;
&lt;strong&gt;Why Parquet Wins&lt;/strong&gt;&lt;br&gt;
Example:&lt;/p&gt;

&lt;p&gt;100 GB of JSON logs -&amp;gt; 12 GB as Parquet with Snappy compression&lt;br&gt;
Athena query cost: $5.00 (JSON) vs $0.60 (Parquet)&lt;/p&gt;

&lt;p&gt;Parquet's columnar storage means you only read the columns you need, not entire rows.&lt;br&gt;
&lt;strong&gt;File Size Matters&lt;/strong&gt;&lt;br&gt;
Sweet spot: 128 MB - 512 MB per file&lt;br&gt;
Too many small files (&amp;lt; 10 MB)? Athena spends more time listing files than reading them. Compact them with a simple Glue job:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;df = spark.read.parquet("s3://path/to/small/files/")
df.repartition(10).write.mode("overwrite").parquet("s3://path/to/compacted/")
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Three Query Wins&lt;br&gt;
&lt;strong&gt;Select only what you need&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;-- Scans 10 columns
SELECT * FROM user_events WHERE year='2024';

-- Scans 3 columns (83% cheaper)
SELECT user_id, event_type, timestamp FROM user_events WHERE year='2024';
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;2. Use CTAS for repeated aggregations&lt;/strong&gt;&lt;br&gt;
Running the same complex query daily? Materialize it once:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;CREATE TABLE daily_summary
WITH (format='PARQUET', partitioned_by=ARRAY['dt']) AS
SELECT dt, user_id, COUNT(*) as events
FROM user_events GROUP BY dt, user_id;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;3. Enable query result caching in your Athena Workgroup - Identical queries are free for 24 hours.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Top 5 Cost Optimization Wins&lt;/strong&gt;&lt;br&gt;
Here are the tactics that saved the most money in my projects:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Partition Projection → Eliminated 80% of "full table scans"&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Saved: ~$2,000/month on a moderate data lake&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Convert to Parquet → 10x reduction in data scanned&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Saved: ~$3,500/month (migrated 500 GB daily ingestion from JSON)&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;S3 Lifecycle Policies → Move old data to cheaper storage
&lt;/li&gt;
&lt;/ol&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{
  "Rules": [{
    "Id": "Archive old partitions",
    "Status": "Enabled",
    "Transitions": [{
      "Days": 90,
      "StorageClass": "GLACIER_IR"
    }]
  }]
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;ul&gt;
&lt;li&gt;Saved: ~$800/month on storage costs&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;4. Athena Workgroup Data Scan Limits&lt;/strong&gt; → Prevent runaway queries&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Set per-query limit: 1 TB
Set per-workgroup monthly limit: 50 TB
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;Glue Job Bookmarks → Process only new data
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;job = Job(glueContext)
job.init(args['JOB_NAME'], args)

# Only processes new files since last run
datasource = glueContext.create_dynamic_frame.from_catalog(
    database="my_database",
    table_name="raw_events",
    transformation_ctx="datasource"
)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;*&lt;em&gt;Conclusion: *&lt;/em&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Partition by time, always. Use partition projection.&lt;/li&gt;
&lt;li&gt;Parquet isn't optional—it's essential for cost control.&lt;/li&gt;
&lt;li&gt;Schema evolution requires discipline: add columns, don't modify them.&lt;/li&gt;
&lt;li&gt;Monitor data scanned per query. It's your cost meter.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Questions from your own data lake implementations? I'd love to hear them. &lt;/p&gt;

</description>
      <category>architecture</category>
      <category>aws</category>
      <category>dataengineering</category>
    </item>
    <item>
      <title>RDS MySQL Zero ETL Integration with Redshift</title>
      <dc:creator>WanjohiChristopher</dc:creator>
      <pubDate>Tue, 17 Dec 2024 17:30:11 +0000</pubDate>
      <link>https://dev.to/aws-builders/rds-mysql-zero-etl-integration-with-redshift-1iho</link>
      <guid>https://dev.to/aws-builders/rds-mysql-zero-etl-integration-with-redshift-1iho</guid>
      <description>&lt;p&gt;&lt;strong&gt;Introduction:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Data integration is one of the most critical aspects of any data-driven organization.ETL, which stands for Extraction, Transformation, and Load, involves the extraction of data from various sources, transforming it to meet specific requirements, and then loading it into a target destination.Traditionally, integrating databases like Amazon RDS MySQL with Redshift for analytical workloads required manual ETL (Extract, Transform, Load) processes. However AWS has now this integration eliminates the manual complexity and time-consuming Extract, Transform, and Load (ETL) processes, offering a streamlined and near real-time solution for moving data from RDS MySQL to Redshift.&lt;/p&gt;

&lt;p&gt;In this article, we will explore what RDS MySQL Zero ETL integration with Redshift is, how it works, and its benefits for developers and data engineers.&lt;/p&gt;

&lt;p&gt;Zero ETL integrations make it simpler to analyze data from Amazon RDS to Redshift by removing the need for anyone to manage complex data pipelines.&lt;br&gt;
AWS handles the data replication, transformation, and loading seamlessly, ensuring data in Redshift is continuously updated as changes occur in the source RDS database.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Benefits of Zero ETL Integration&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Near real-Time Analytics.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Reduced Operational Overhead.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Scalability.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Cost-efficiency&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Improved Data Consistency.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Lets Dive into process, we will do a simple example:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9m5o84naxtahnx9fbsf4.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9m5o84naxtahnx9fbsf4.png" alt=" " width="800" height="307"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Step 1: Setup the RDS MySQL database with data.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5onebicqqohnzt8a8gdr.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5onebicqqohnzt8a8gdr.png" alt=" " width="800" height="373"&gt;&lt;/a&gt;&lt;br&gt;
Step 2: Setup the datawarehouse -Redshift in this case.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frck0xlbc7z0eh7j8wztt.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frck0xlbc7z0eh7j8wztt.png" alt=" " width="800" height="392"&gt;&lt;/a&gt;&lt;br&gt;
Step 3: Create a Zero ETL Integration.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fm2ssd1piieifxnm2066i.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fm2ssd1piieifxnm2066i.png" alt=" " width="800" height="382"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fo0dn6e5t3is6u52r3kg9.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fo0dn6e5t3is6u52r3kg9.png" alt=" " width="800" height="422"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;In conclusion:&lt;/strong&gt;&lt;br&gt;
Amazon RDS MySQL Zero ETL integration with Amazon Redshift is a game-changer for organizations looking to simplify their data pipelines and enable real-time analytics. By automating the data movement process, AWS eliminates the complexities of traditional ETL workflows, reducing operational overhead, improving data consistency, and enabling timely insights.&lt;/p&gt;

</description>
      <category>mysql</category>
      <category>aws</category>
      <category>etl</category>
    </item>
    <item>
      <title>AWS Database Migration-Heterogenous Approach using DMS.</title>
      <dc:creator>WanjohiChristopher</dc:creator>
      <pubDate>Thu, 15 Feb 2024 02:12:56 +0000</pubDate>
      <link>https://dev.to/aws-builders/aws-database-migration-heterogenous-approach-using-dms-40pa</link>
      <guid>https://dev.to/aws-builders/aws-database-migration-heterogenous-approach-using-dms-40pa</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;Ever wondered how you can migrate your database (data) from OnPrem to AWS Cloud, AWS RDS to AWS RDS or to S3 Bucket?&lt;br&gt;
Then this article is for you.&lt;br&gt;
We will delve into heterogeneous migration.&lt;/p&gt;

&lt;h2&gt;
  
  
  Table of Contents
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Define whats data migration?&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Data Migration strategies.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Define heterogeneous migration.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;We will use Mysql and Postgres as our databases for the use case.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Data Migration
&lt;/h3&gt;

&lt;p&gt;Data migration is the movement of data from source systems to target systems (destination systems), depending on the environment in which the data is sitting.&lt;br&gt;
For example, a database, say Postgres, can be running in an EC2 instance, and the client wants to migrate the database to the AWS Relational Database System(Aurora Postgres).In this case client will need help in knowing how they want to migrate to their preferred database. Thats where these services comein.&lt;/p&gt;

&lt;h2&gt;
  
  
  Data Migration Strategies
&lt;/h2&gt;

&lt;p&gt;These are approaches used to migrate workloads to the AWS Cloud.&lt;br&gt;
I will just mention all the approaches here only.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Retire&lt;/li&gt;
&lt;li&gt;Retain&lt;/li&gt;
&lt;li&gt;Rehost&lt;/li&gt;
&lt;li&gt;Relocate&lt;/li&gt;
&lt;li&gt;Repurchase&lt;/li&gt;
&lt;li&gt;Replatform&lt;/li&gt;
&lt;li&gt;Refactor or re-architect&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Heterogenous Migration
&lt;/h2&gt;

&lt;p&gt;When we talk about heterogenous migration, we talk about migrating data from totally different databases.&lt;br&gt;
Meaning that Source database has completely different schema compared to the target database.&lt;br&gt;
For example, from MySQL to Postgres or vice versa or from SQL Server to Postgres or from Posgres to Oracle.&lt;/p&gt;

&lt;p&gt;We will first step into steps of migration:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Review Data Sources by checking the environment,schema,tables etc.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;2.Determine the destination&lt;br&gt;
 We access where the organization wants its data to sit.3. &lt;/p&gt;

&lt;p&gt;3.Data Migration strategy&lt;br&gt;
At this point, we focus on creating a migration plan so that we understand clearly the requirements for the migration.&lt;/p&gt;

&lt;p&gt;We will be able to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;assess security requirements.&lt;/li&gt;
&lt;li&gt;Cost and time to be taken.&lt;/li&gt;
&lt;li&gt;systems and data migration tools.&lt;/li&gt;
&lt;li&gt;Implement.&lt;/li&gt;
&lt;li&gt;Test the connections.&lt;/li&gt;
&lt;li&gt;Run the migrations.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Our case, we will illustrate the process of migrating a AWS MySQL database to AWS Postgres (RDS).We will cover the demo in our next article.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Process&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Create the source and target databases and insert data in the source schema(MYSQL).&lt;/li&gt;
&lt;li&gt;We will be using AWS Data Migration Service and Schema Conversion Tool (AWS SCT) which we will download.
3.The first step here will be Performing Schema conversion.We convert the schema of mysql database to a PostgreSQL schema using AWS SCT.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;In order to convert we need to connect our database endpoints in AWS SCT Tool locally.&lt;br&gt;
Please see the below preview.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgajp3pfdgoiz3cnsp40r.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgajp3pfdgoiz3cnsp40r.png" alt="AWS SCT" width="800" height="292"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;4.Navigate now to AWS Data Migration Service to create endpoints source and target,create replication instance and data migration tasks. At this point we wont focus on using serverless and also  Data Migration projects we save them for later.&lt;br&gt;
After creating the migration task we perform full load as shown below.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F68h0w6jjyp1i3j69iusg.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F68h0w6jjyp1i3j69iusg.png" alt="Data Migration Service" width="800" height="326"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;At this point we can confirm in our data whether it was migrated and successful.We can see we are successful;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2tqbu57e4qp2p5gstpjy.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2tqbu57e4qp2p5gstpjy.png" alt="success" width="800" height="473"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;In conclusion, in this article, we covered data migration, strategies, a plan, and heterogenous migration. You realise we didn't go too technical, as we plan to have the next article contain full technical details explaining each process from connection using endpoints and IP addresses to creating migration tasks one by one. I hope you enjoyed.&lt;br&gt;
Cheers! and Happy Learning.&lt;/p&gt;

</description>
      <category>database</category>
      <category>migration</category>
      <category>rds</category>
      <category>aws</category>
    </item>
  </channel>
</rss>
