Showing posts with label python. Show all posts
Showing posts with label python. Show all posts

Thursday

Python code examples for implementing an ML ensemble using Redis

 

Important Setup Note: RedisAI and RedisML are no longer actively maintained as of 2025/2026. Therefore, Pattern B has evolved into standard deployment patterns using lightweight modern tools (like ONNX Runtime or FastAPI workers coupled directly with Redis), ensuring your pipeline remains production-grade.

Pattern A: Asynchronous Parallel Processing (Redis Streams)

Best for: Heavy models (Deep Learning, large Random Forests) that need separate workers or hardware (GPUs) to compute in parallel without locking your main API.

Architecture Flow

[Client Request] 
       │
       ▼
 ┌───────────┐       XADD       ┌───────────────┐
 │  FastAPI  │ ────────────────>│ Redis Stream  │──┐ (Model 1 Worker)
 │  Gateway  │                  │(ml:requests)  │──┼─> [Model 2 Worker]
 └───────────┘                  └───────────────┘──┘ (Model 3 Worker)
       │                                                   │
       │ (Polls responses via                              │ XADD
       │  unique Request ID)                               ▼
       │                        ┌───────────────┐  ┌───────────────┐
       └────────────────────────│ Redis Hash    │<─│ Ensemble      │
                                │(ml:results:ID)│  │ Aggregator    │
                                └───────────────┘  └───────────────┘

Python Implementation

You will need redis and scikit-learn for this pattern. Run pip install redis scikit-learn.

Python
import time
import json
import uuid
import threading
import redis
from sklearn.linear_model import LogisticRegression
import numpy as np

# Connect to Redis
r = redis.Redis(host='localhost', port=6379, decode_responses=True)

# 1. API GATEWAY FUNCTION (Pushes tasks, awaits final decision)
def mock_api_gateway(features):
    request_id = str(uuid.uuid4())
    payload = {"request_id": request_id, "features": json.dumps(features)}
    
    # Broadcast to the stream
    r.xadd("ml:requests", payload)
    print(f"[API] Dispatched request {request_id}")
    
    # Poll for the ensemble aggregator's final answer
    result_key = f"ml:results:{request_id}"
    while True:
        final_decision = r.hgetall(result_key)
        if final_decision:
            r.delete(result_key) # Clean up
            return final_decision
        time.sleep(0.01) # 10ms polling backoff

# 2. MODEL WORKERS (Simulating 2 different models processing concurrently)
def run_model_worker(model_name, weight_offset):
    # Dummy local model initialization
    model = LogisticRegression()
    
    while True:
        # Read from stream (simulating listening for real-time events)
        messages = r.xread({"ml:requests": "$"}, block=100)
        if not messages:
            continue
            
        for stream, msgs in messages:
            for msg_id, data in msgs:
                req_id = data["request_id"]
                features = json.loads(data["features"])
                
                # Mock prediction: calculate probability
                raw_prob = float(np.mean(features)) * weight_offset
                prob = min(max(raw_prob, 0.0), 1.0)
                
                # Write individual prediction back to a Redis Hash unique to this request
                r.hset(f"ml:preds:{req_id}", model_name, prob)

# 3. ENSEMBLE AGGREGATOR WORKER
def run_ensemble_aggregator():
    while True:
        # Scan for active prediction hashes
        for key in r.scan_iter("ml:preds:*"):
            req_id = key.split(":")[-1]
            predictions = r.hgetall(key)
            
            # Check if both expected model workers have responded
            if len(predictions) == 2:
                prob1 = float(predictions["model_alpha"])
                prob2 = float(predictions["model_beta"])
                
                # Ensemble Logic: Simple Average
                ensemble_prob = (prob1 + prob2) / 2
                decision = "Approved" if ensemble_prob > 0.5 else "Rejected"
                
                # Publish final outcome back to API
                r.hset(f"ml:results:{req_id}", mapping={
                    "decision": decision,
                    "probability": ensemble_prob
                })
                r.delete(key) # Clear intermediate predictions

# --- TEST EXECUTION LOOP ---
if __name__ == "__main__":
    # Flush old data for clean demo
    r.flushdb()
    
    # Start background threads for workers
    threading.Thread(target=run_model_worker, args=("model_alpha", 1.2), daemon=True).start()
    threading.Thread(target=run_model_worker, args=("model_beta", 0.8), daemon=True).start()
    threading.Thread(target=run_ensemble_aggregator, daemon=True).start()
    
    time.sleep(1) # Allow workers to boot
    
    # Simulate a client checking credit features
    mock_features = [0.4, 0.6, 0.55, 0.7] 
    output = mock_api_gateway(mock_features)
    print(f"[API] Final Ensemble Response: {output}")

Pattern B: Synchronous In-Memory Processing (Pipeline)

Best for: Light-to-medium models (like Scikit-Learn or XGBoost) where raw speed matters most. Instead of threading out across streams, your API queries a centralized Redis backend using a pipeline to get all required features at once, then locally runs predictions.

Architecture Flow

[Client Request] ──> [FastAPI Server]
                         │
                         ├─> Redis Pipeline (MGET Features) 
                         │       │
                         │       ▼
                         │   [Redis In-Memory Key Store]
                         │       │ (Returns user & context features <1ms)
                         ▼       ▼
                    Executes Model 1, 2, & 3 locally
                         │
                         ▼
                    Ensemble Aggregation ──> [Return Decision]

Python Implementation

Python
import redis
import json
import numpy as np

r = redis.Redis(host='localhost', port=6379, decode_responses=True)

# Mock some feature store data inside Redis
r.hset("features:user_99", mapping={"risk_score": "0.34", "account_age_days": "120"})
r.hset("features:merchant_44", mapping={"fraud_velocity": "0.05", "trust_rating": "0.91"})

def predict_ensemble_sync(user_id, merchant_id):
    # 1. Pipeline Feature Store Retrieval (Single network round-trip)
    pipe = r.pipeline()
    pipe.hgetall(f"features:{user_id}")
    pipe.hgetall(f"features:{merchant_44}")
    user_feats, merchant_feats = pipe.execute()
    
    # Combine feature vectors
    base_features = [
        float(user_feats["risk_score"]),
        float(user_feats["account_age_days"]) / 365.0, # normalized
        float(merchant_feats["fraud_velocity"]),
        float(merchant_feats["trust_rating"])
    ]
    
    # 2. Synchronous Local Ensemble Execution
    # Model 1: Linear rule
    pred_1 = 1.0 if base_features[0] > 0.5 else 0.0
    # Model 2: Statistical weight
    pred_2 = (base_features[3] * 0.7) + (1 - base_features[2]) * 0.3
    
    # 3. Aggregation (Ensemble Rule: Weighted Voting)
    final_score = (pred_1 * 0.4) + (pred_2 * 0.6)
    return "Flagged Fraud" if final_score < 0.5 else "Authorized"

# Test synchronous run
decision = predict_ensemble_sync("user_99", "merchant_44")
print(f"[Sync API] Ensemble Outcome: {decision}")

Pattern C: Caching Decisions (The Shield)

Regardless of whether you use Pattern A or B, you should shield your models from processing repetitive inputs using an exact signature cache lookup.

Python
import hashlib
import redis

r = redis.Redis(host='localhost', port=6379, decode_responses=True)

def get_ensemble_decision_with_cache(user_id, context_payload):
    # Generate a deterministic cache key based on inputs
    payload_string = json.dumps(context_payload, sort_keys=True)
    payload_hash = hashlib.sha256(payload_string.encode()).hexdigest()
    cache_key = f"cache:decision:{user_id}:{payload_hash}"
    
    # 1. Hit the cache layer
    cached_decision = r.get(cache_key)
    if cached_decision:
        print("[Cache] Match found! Zero model calculation needed.")
        return json.loads(cached_decision)
        
    # 2. Cache Miss: Run your actual ensemble pipeline (from Pattern A or B)
    print("[Cache] Miss. Running full ensemble inference...")
    computed_decision = {"status": "Approved", "confidence": 0.89}
    
    # 3. Save to cache with a 60-second Time-To-Live (TTL)
    r.setex(cache_key, 60, json.dumps(computed_decision))
    return computed_decision

# Test caching execution
payload = {"amount": 250.00, "location": "NY"}
print(get_ensemble_decision_with_cache("user_99", payload))
print(get_ensemble_decision_with_cache("user_99", payload)) # Secondary call hits cache

Building a Real-Time Feature Store with Redis This video outlines Redis' architectures, illustrating how it manages massive operational throughput for contextual data architectures in modern live-serving applications.

Wednesday

Maersk’s digital twin ecosystem

Maersk’s digital twin ecosystem integrates advanced algorithms, machine learning, AI, IoT sensor networks, and satellite connectivity for operational optimization, predictive analytics, and real-time decision-making; each component plays a specific technical role for their vessels and logistics platforms.

Algorithms and ML Techniques

Voyage Simulation Algorithms: Maersk’s digital twins simulate “ghost ship” voyages using data-driven algorithms that include time series analysis, regression models, and vessel hydrodynamics optimization; these help forecast fuel consumption, emissions, and routing efficiency before a voyage is booked.

Predictive Modeling: ML models (e.g. XGBoost, Random Forest, Neural Networks) are used to estimate future cargo demand, predict maintenance needs (predictive maintenance), detect anomalies (such as abnormal sensor readings), and optimize speed and course under varying weather and market conditions.metalab.

Prescriptive Analytics: Reinforcement learning and optimization algorithms help select the best speed and route for fuel and time savings, leveraging vessel, weather, and trade data.

Artificial Intelligence Capabilities

AI for Real-Time Operations: Computer vision, time-series forecasting, and optimization AIs continuously monitor sensor inputs, vessel status, and logistics data; these systems perform anomaly detection and health forecasting for engines, cargo holds, and supply networks.

Scenario Modeling: AI-enabled digital twins help planners run “what-if” scenarios: e.g., impact of supply chain disruptions, reroute simulations, and dynamic inventory management for resilience and risk mitigation.

Sustainability Optimization: AI models track and recommend operational changes to minimize emissions, optimize energy use, and schedule cargo transfers, enabling measurable carbon reduction and cost savings.

IoT Sensor and Edge Analytics

IoT Sensor Networks: Vessels are fitted with thousands of IoT sensors—engine, hull, weather, cargo, and environmental status—which stream real-time telemetry to Maersk’s analytics backend.

Edge Computing: Machine learning models run directly on vessels (“at the edge”), enabling instant analysis of fuel consumption, cargo condition, and climate impacts, often when vessels lack continuous shore connectivity.

AI-enabled Alerts: IoT analytics power automated risk detection and alerting—such as engine overheating, cargo temperature excursions, and hull damage—allowing prompt corrective action.

Satellite Connectivity and Communications

Global Satellite Networks: Maersk uses high-bandwidth Maritime satellite networks (Inmarsat) for fleet-wide, always-on connectivity for telemetry, cloud-based digital twin services, and crew welfare.

Cloud-Enabled Vessel Operations: Satellite links allow seamless data flows for real-time analytics, digital twin updates, and continuous crew and office communications, supporting both business processes and life aboard.

Remote Operations and Automation: Satellite connectivity unlocks “floating office” concepts a cloud-based applications, real-time fleet coordination, and even future autonomous vessel operations.

Example Data Pipeline Workflow

  • Technology

  • Operations

  • Role

  • Data collection

  • IoT sensors, AIS feedsMeasure fuel, speed, weather, cargo, ship location

  • Edge analytics Python ML models Forecast consumption, detect risks, local decisions

  • Cloud integration Satellite, cloud APIs

  • Sync vessel data, run digital twin simulations.

  • Predictive insights

  • ML/AI algorithms

  • Optimize routing, maintenance, emissions.

  • Prescriptive action

  • Optimization

  • AI Recommend speed/course, schedule, risk mitigations.

Maersk’s architecture blends data-driven AI, ML models, ubiquitous IoT analysis, and high-reliability satellite infrastructure to enable resilient, sustainable, and highly autonomous fleet and logistics management.

You can get more details here

Thursday

Simple FastAPI App with Docker and Minikube

 Let's start with the simplest one. Which we can develop and test in our local system or laptop, or Mac.

✅ Simple FastAPI App with Docker and Minikube (Kubernetes)


📁 Folder Structure

fastapi-k8s-demo/
├── app/
│   └── main.py
├── Dockerfile
├── requirements.txt
├── k8s/
│   ├── deployment.yaml
│   └── service.yaml

📄 app/main.py

from fastapi import FastAPI

app = FastAPI()

@app.get("/")
def read_root():
    return {"message": "Hello from FastAPI on Kubernetes!"}

📄 requirements.txt

fastapi
uvicorn

📄 Dockerfile

FROM python:3.11-slim

WORKDIR /app

COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt

COPY app/ .

CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8000"]

📄 k8s/deployment.yaml

apiVersion: apps/v1
kind: Deployment
metadata:
  name: fastapi-deployment
spec:
  replicas: 1
  selector:
    matchLabels:
      app: fastapi
  template:
    metadata:
      labels:
        app: fastapi
    spec:
      containers:
      - name: fastapi
        image: fastapi-demo:latest
        imagePullPolicy: IfNotPresent
        ports:
        - containerPort: 8000

📄 k8s/service.yaml

apiVersion: v1
kind: Service
metadata:
  name: fastapi-service
spec:
  type: NodePort
  selector:
    app: fastapi
  ports:
    - port: 8000
      targetPort: 8000
      nodePort: 30036

🧪 Run Locally with Minikube

# Start minikube
minikube start

# Build docker image inside minikube
eval $(minikube docker-env)
docker build -t fastapi-demo .

# Apply Kubernetes configs
kubectl apply -f k8s/deployment.yaml
kubectl apply -f k8s/service.yaml

# Access app in browser
minikube service fastapi-service

✅ FastAPI on Minikube with Ingress, Live Reload, and Persistent Volume


📁 Updated Structure

fastapi-k8s-demo/
├── app/
│   └── main.py
├── Dockerfile
├── requirements.txt
├── k8s/
│   ├── deployment.yaml
│   ├── service.yaml
│   ├── ingress.yaml
│   └── pv-claim.yaml

📄 Dockerfile (with live reload)

FROM python:3.11-slim

WORKDIR /app

COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt

COPY app/ .

CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8000", "--reload"]

📄 requirements.txt

fastapi
uvicorn[standard]

📄 k8s/deployment.yaml (with volume)

apiVersion: apps/v1
kind: Deployment
metadata:
  name: fastapi-deployment
spec:
  replicas: 1
  selector:
    matchLabels:
      app: fastapi
  template:
    metadata:
      labels:
        app: fastapi
    spec:
      containers:
        - name: fastapi
          image: fastapi-demo:latest
          imagePullPolicy: IfNotPresent
          ports:
            - containerPort: 8000
          volumeMounts:
            - mountPath: /app
              name: app-volume
      volumes:
        - name: app-volume
          persistentVolumeClaim:
            claimName: fastapi-pvc

📄 k8s/pv-claim.yaml

apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: fastapi-pvc
spec:
  accessModes:
    - ReadWriteOnce
  resources:
    requests:
      storage: 1Gi

📄 k8s/service.yaml

apiVersion: v1
kind: Service
metadata:
  name: fastapi-service
spec:
  selector:
    app: fastapi
  ports:
    - protocol: TCP
      port: 80
      targetPort: 8000

📄 k8s/ingress.yaml

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: fastapi-ingress
  annotations:
    nginx.ingress.kubernetes.io/rewrite-target: /
spec:
  rules:
    - host: fastapi.local
      http:
        paths:
          - path: /
            pathType: Prefix
            backend:
              service:
                name: fastapi-service
                port:
                  number: 80

🔧 Enable Ingress and Mount App

minikube start --addons=ingress

# Use Minikube's Docker
eval $(minikube docker-env)
docker build -t fastapi-demo .

# Apply all configs
kubectl apply -f k8s/pv-claim.yaml
kubectl apply -f k8s/deployment.yaml
kubectl apply -f k8s/service.yaml
kubectl apply -f k8s/ingress.yaml

# Add /etc/hosts entry (Linux/macOS)
echo "$(minikube ip) fastapi.local" | sudo tee -a /etc/hosts

# Open in browser
http://fastapi.local/

✅ FastAPI + PostgreSQL on Minikube with Secrets & ConfigMaps


📁 Project Structure

fastapi-k8s-demo/
├── app/
│   └── main.py
├── Dockerfile
├── requirements.txt
├── k8s/
│   ├── deployment.yaml
│   ├── service.yaml
│   ├── ingress.yaml
│   ├── postgres-deployment.yaml
│   ├── postgres-service.yaml
│   ├── secret.yaml
│   └── configmap.yaml

📄 requirements.txt

fastapi
uvicorn[standard]
psycopg2-binary

📄 app/main.py

import os
import psycopg2
from fastapi import FastAPI

app = FastAPI()

@app.get("/")
def root():
    return {"message": "Hello from FastAPI + Postgres!"}

@app.get("/db-check")
def db_check():
    conn = psycopg2.connect(
        host=os.getenv("DB_HOST"),
        dbname=os.getenv("DB_NAME"),
        user=os.getenv("DB_USER"),
        password=os.getenv("DB_PASSWORD"),
    )
    cur = conn.cursor()
    cur.execute("SELECT version();")
    version = cur.fetchone()
    conn.close()
    return {"postgres_version": version}

📄 Dockerfile

FROM python:3.11-slim
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY app/ .
CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8000"]

📄 k8s/postgres-deployment.yaml

apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: postgres-pvc
spec:
  accessModes:
    - ReadWriteOnce
  resources:
    requests:
      storage: 1Gi
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: postgres
spec:
  selector:
    matchLabels:
      app: postgres
  replicas: 1
  template:
    metadata:
      labels:
        app: postgres
    spec:
      containers:
        - name: postgres
          image: postgres:13
          env:
            - name: POSTGRES_DB
              valueFrom:
                configMapKeyRef:
                  name: pg-config
                  key: POSTGRES_DB
            - name: POSTGRES_USER
              valueFrom:
                secretKeyRef:
                  name: pg-secret
                  key: POSTGRES_USER
            - name: POSTGRES_PASSWORD
              valueFrom:
                secretKeyRef:
                  name: pg-secret
                  key: POSTGRES_PASSWORD
          ports:
            - containerPort: 5432
          volumeMounts:
            - mountPath: /var/lib/postgresql/data
              name: pgdata
      volumes:
        - name: pgdata
          persistentVolumeClaim:
            claimName: postgres-pvc

📄 k8s/postgres-service.yaml

apiVersion: v1
kind: Service
metadata:
  name: postgres
spec:
  ports:
    - port: 5432
  selector:
    app: postgres

📄 k8s/secret.yaml

apiVersion: v1
kind: Secret
metadata:
  name: pg-secret
type: Opaque
stringData:
  POSTGRES_USER: myuser
  POSTGRES_PASSWORD: mypassword

📄 k8s/configmap.yaml

apiVersion: v1
kind: ConfigMap
metadata:
  name: pg-config
data:
  POSTGRES_DB: mydb

📄 k8s/deployment.yaml (FastAPI updated)

apiVersion: apps/v1
kind: Deployment
metadata:
  name: fastapi-deployment
spec:
  replicas: 1
  selector:
    matchLabels:
      app: fastapi
  template:
    metadata:
      labels:
        app: fastapi
    spec:
      containers:
        - name: fastapi
          image: fastapi-demo:latest
          ports:
            - containerPort: 8000
          env:
            - name: DB_HOST
              value: postgres
            - name: DB_NAME
              valueFrom:
                configMapKeyRef:
                  name: pg-config
                  key: POSTGRES_DB
            - name: DB_USER
              valueFrom:
                secretKeyRef:
                  name: pg-secret
                  key: POSTGRES_USER
            - name: DB_PASSWORD
              valueFrom:
                secretKeyRef:
                  name: pg-secret
                  key: POSTGRES_PASSWORD

📄 k8s/service.yaml (FastAPI)

apiVersion: v1
kind: Service
metadata:
  name: fastapi-service
spec:
  selector:
    app: fastapi
  ports:
    - protocol: TCP
      port: 80
      targetPort: 8000

📄 k8s/ingress.yaml

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: fastapi-ingress
  annotations:
    nginx.ingress.kubernetes.io/rewrite-target: /
spec:
  rules:
    - host: fastapi.local
      http:
        paths:
          - path: /
            pathType: Prefix
            backend:
              service:
                name: fastapi-service
                port:
                  number: 80

🔧 Deployment Commands

minikube start --addons ingress
eval $(minikube docker-env)

# Build image
docker build -t fastapi-demo .

# Apply K8s resources
kubectl apply -f k8s/configmap.yaml
kubectl apply -f k8s/secret.yaml
kubectl apply -f k8s/postgres-deployment.yaml
kubectl apply -f k8s/postgres-service.yaml
kubectl apply -f k8s/deployment.yaml
kubectl apply -f k8s/service.yaml
kubectl apply -f k8s/ingress.yaml

# Add to /etc/hosts
echo "$(minikube ip) fastapi.local" | sudo tee -a /etc/hosts

# Open
http://fastapi.local/db-check

Hope now you have a solid understanding of how to develop a Python FastAPI application in Docker and deploy it in Kubernetes clusters.

The whole application code is here with different branches https://github.com/dhirajpatra/fastapi-k8s-demo


House Based Manufacturing Micro Clustering

                                 image generated by meta ai House-based manufacturing micro-clustering in China refers to the hyper-local, v...