AR_v2 / app.py
PD03's picture
Update app.py
e01bb8b verified
import gradio as gr
import numpy as np
import pandas as pd
import tensorflow as tf
from sklearn.metrics import accuracy_score, precision_score, recall_score, f1_score
from sklearn.model_selection import train_test_split
import plotly.graph_objects as go
import plotly.express as px
from datetime import datetime, timedelta
import random
import time
class SAPARPredictor:
def __init__(self):
self.model = None
self.training_history = None
self.is_trained = False
def generate_synthetic_data(self, n_samples=1000):
"""Generate synthetic SAP AR data"""
np.random.seed(42) # For reproducibility
customers = ['CUST001', 'CUST002', 'CUST003', 'CUST004', 'CUST005', 'CUST006', 'CUST007', 'CUST008']
data = []
for i in range(n_samples):
invoice_amount = np.random.uniform(1000, 51000)
customer_code = np.random.choice(customers)
days_overdue = np.random.randint(0, 120)
previous_delays = np.random.randint(0, 5)
credit_score = np.random.uniform(0, 100)
industry_risk = np.random.uniform(0, 1)
seasonality = np.sin((i % 365) * 2 * np.pi / 365)
# Create correlation between features and payment probability
payment_prob = 0.7
payment_prob -= min(days_overdue / 100, 0.4)
payment_prob -= min(previous_delays / 10, 0.3)
payment_prob += (credit_score - 50) / 200
payment_prob -= industry_risk * 0.2
payment_prob += seasonality * 0.1
payment_prob = max(0.05, min(0.95, payment_prob))
paid_on_time = 1 if np.random.random() < payment_prob else 0
data.append({
'invoice_amount': invoice_amount / 50000, # Normalize
'days_overdue': days_overdue / 120, # Normalize
'previous_delays': previous_delays / 5, # Normalize
'credit_score': credit_score / 100, # Already normalized
'industry_risk': industry_risk,
'seasonality': (seasonality + 1) / 2, # Normalize to 0-1
'paid_on_time': paid_on_time
})
return pd.DataFrame(data)
def train_model(self, progress=gr.Progress()):
"""Train the ML model with progress tracking"""
try:
progress(0, desc="🔄 Generating synthetic data...")
# Generate training data
df = self.generate_synthetic_data(1000)
time.sleep(0.5) # Simulate data generation time
progress(0.2, desc="📊 Preparing features and labels...")
# Prepare features and labels
feature_columns = ['invoice_amount', 'days_overdue', 'previous_delays',
'credit_score', 'industry_risk', 'seasonality']
X = df[feature_columns].values
y = df['paid_on_time'].values
# Split data
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)
progress(0.3, desc="🧠 Building neural network...")
# Create model
self.model = tf.keras.Sequential([
tf.keras.layers.Dense(32, activation='relu', input_shape=(6,)),
tf.keras.layers.Dropout(0.2),
tf.keras.layers.Dense(16, activation='relu'),
tf.keras.layers.Dropout(0.2),
tf.keras.layers.Dense(1, activation='sigmoid')
])
self.model.compile(
optimizer=tf.keras.optimizers.Adam(0.001),
loss='binary_crossentropy',
metrics=['accuracy']
)
progress(0.4, desc="🎯 Training model (50 epochs)...")
# Train model
history = self.model.fit(
X_train, y_train,
epochs=50,
batch_size=32,
validation_split=0.2,
verbose=0
)
progress(0.8, desc="📈 Evaluating model performance...")
# Make predictions on test set
y_pred_proba = self.model.predict(X_test, verbose=0)
y_pred = (y_pred_proba > 0.5).astype(int)
# Calculate metrics
accuracy = accuracy_score(y_test, y_pred)
precision = precision_score(y_test, y_pred)
recall = recall_score(y_test, y_pred)
f1 = f1_score(y_test, y_pred)
self.training_history = history.history
self.is_trained = True
progress(1.0, desc="✅ Training completed successfully!")
# Create training visualization
fig = go.Figure()
epochs = list(range(1, len(history.history['accuracy']) + 1))
fig.add_trace(go.Scatter(
x=epochs,
y=history.history['accuracy'],
mode='lines+markers',
name='Training Accuracy',
line=dict(color='#007bff', width=4),
marker=dict(size=8)
))
fig.add_trace(go.Scatter(
x=epochs,
y=history.history['val_accuracy'],
mode='lines+markers',
name='Validation Accuracy',
line=dict(color='#28a745', width=4),
marker=dict(size=8)
))
fig.update_layout(
title={
'text': '📊 Model Training Progress',
'x': 0.5,
'font': {'size': 20}
},
xaxis_title='Epoch',
yaxis_title='Accuracy',
template='plotly_white',
#height=450,
hovermode='x unified',
legend=dict(
yanchor="bottom",
y=0.02,
xanchor="right",
x=0.98
)
)
# Create metrics cards HTML
metrics_html = f"""
<div style="display: grid; grid-template-columns: repeat(2, 1fr); gap: 15px; margin: 20px 0;">
<div style="background: linear-gradient(135deg, #007bff, #0056b3); color: white; padding: 20px; border-radius: 15px; text-align: center; box-shadow: 0 4px 15px rgba(0,123,255,0.3);">
<div style="font-size: 2.5rem; font-weight: bold; margin-bottom: 5px;">{accuracy:.1%}</div>
<div style="font-size: 1.1rem;">🎯 Accuracy</div>
</div>
<div style="background: linear-gradient(135deg, #28a745, #20c997); color: white; padding: 20px; border-radius: 15px; text-align: center; box-shadow: 0 4px 15px rgba(40,167,69,0.3);">
<div style="font-size: 2.5rem; font-weight: bold; margin-bottom: 5px;">{precision:.1%}</div>
<div style="font-size: 1.1rem;">🎯 Precision</div>
</div>
<div style="background: linear-gradient(135deg, #ffc107, #fd7e14); color: white; padding: 20px; border-radius: 15px; text-align: center; box-shadow: 0 4px 15px rgba(255,193,7,0.3);">
<div style="font-size: 2.5rem; font-weight: bold; margin-bottom: 5px;">{recall:.1%}</div>
<div style="font-size: 1.1rem;">📊 Recall</div>
</div>
<div style="background: linear-gradient(135deg, #17a2b8, #138496); color: white; padding: 20px; border-radius: 15px; text-align: center; box-shadow: 0 4px 15px rgba(23,162,184,0.3);">
<div style="font-size: 2.5rem; font-weight: bold; margin-bottom: 5px;">{f1:.1%}</div>
<div style="font-size: 1.1rem">⚖️ F1 Score</div>
</div>
</div>
<div style="background: #d4edda; border: 1px solid #c3e6cb; color: #155724; padding: 15px; border-radius: 10px; margin-top: 15px; text-align: center;">
<strong>✅ Model trained successfully on 1,000 synthetic SAP AR records!</strong><br>
<em>The model is now ready to make predictions on unpaid invoices.</em>
</div>
"""
return fig, metrics_html, gr.update(interactive=True, variant="primary")
except Exception as e:
error_html = f"""
<div style="background: #f8d7da; border: 1px solid #f5c6cb; color: #721c24; padding: 15px; border-radius: 10px; text-align: center;">
<strong>❌ Training failed:</strong> {str(e)}
</div>
"""
return None, error_html, gr.update(interactive=False)
def generate_unpaid_invoices(self):
"""Generate sample unpaid invoices for prediction"""
customers = ['SAP-CUST001', 'SAP-CUST002', 'SAP-CUST003', 'SAP-CUST004', 'SAP-CUST005']
invoices = []
for i in range(12):
invoice_id = f"INV-{datetime.now().strftime('%Y%m%d')}-{i:03d}"
customer = random.choice(customers)
amount = random.randint(5000, 50000)
days_overdue = random.randint(0, 90)
previous_delays = random.randint(0, 4)
credit_score = random.randint(40, 100)
invoices.append({
'Invoice ID': invoice_id,
'Customer': customer,
'Amount ($)': amount,
'Days Overdue': days_overdue,
'Previous Delays': previous_delays,
'Credit Score': credit_score,
'Industry Risk': round(random.random(), 3),
'Seasonality': round(random.random(), 3)
})
return pd.DataFrame(invoices)
def make_predictions(self):
"""Make predictions on unpaid invoices"""
if not self.is_trained:
error_msg = """
<div style="background: #f8d7da; border: 1px solid #f5c6cb; color: #721c24; padding: 15px; border-radius: 10px; text-align: center;">
<strong>❌ Please train the model first!</strong><br>
<em>Go to the Model Training tab and click "Train ML Model"</em>
</div>
"""
return None, error_msg, None
try:
# Generate unpaid invoices
df = self.generate_unpaid_invoices()
# Prepare features for prediction
features = []
for _, row in df.iterrows():
features.append([
row['Amount ($)'] / 50000, # Normalize
row['Days Overdue'] / 120, # Normalize
row['Previous Delays'] / 5, # Normalize
row['Credit Score'] / 100, # Normalize
row['Industry Risk'],
row['Seasonality']
])
# Make predictions
predictions = self.model.predict(np.array(features), verbose=0)
# Create results dataframe with better formatting
results_df = df.copy()
prob_values = [p[0] for p in predictions]
# Add prediction columns
results_df['Payment Probability'] = [f"{p:.1%}" for p in prob_values]
results_df['Prediction'] = ['✅ Will Pay' if p > 0.5 else '❌ Risk of Default' for p in prob_values]
results_df['Risk Level'] = ['🟢 Low Risk' if p > 0.7 else '🟡 Medium Risk' if p > 0.4 else '🔴 High Risk' for p in prob_values]
# Format amount column
results_df['Amount ($)'] = results_df['Amount ($)'].apply(lambda x: f"${x:,}")
# Reorder columns for better display
column_order = ['Invoice ID', 'Customer', 'Amount ($)', 'Days Overdue', 'Credit Score',
'Payment Probability', 'Prediction', 'Risk Level']
results_df = results_df[column_order]
# Create probability distribution chart
fig = go.Figure()
# Create histogram
fig.add_trace(go.Histogram(
x=prob_values,
nbinsx=15,
marker_color='rgba(0, 123, 255, 0.7)',
marker_line_color='rgba(0, 123, 255, 1)',
marker_line_width=2,
name='Payment Probability'
))
# Add vertical lines for risk thresholds
fig.add_vline(x=0.4, line_dash="dash", line_color="orange",
annotation_text="Medium Risk Threshold")
fig.add_vline(x=0.7, line_dash="dash", line_color="green",
annotation_text="Low Risk Threshold")
fig.update_layout(
title={
'text': '📊 Distribution of Payment Probabilities',
'x': 0.5,
'font': {'size': 18}
},
xaxis_title='Payment Probability',
yaxis_title='Number of Invoices',
template='plotly_white',
#height=400,
showlegend=False
)
# Count predictions by category
will_pay = sum(1 for p in prob_values if p > 0.5)
risk_default = len(prob_values) - will_pay
high_risk = sum(1 for p in prob_values if p <= 0.4)
success_msg = f"""
<div style="background: #d4edda; border: 1px solid #c3e6cb; color: #155724; padding: 20px; border-radius: 10px; margin: 15px 0;">
<div style="text-align: center; margin-bottom: 15px;">
<strong style="font-size: 1.2rem;">🔮 Prediction Results Generated Successfully!</strong>
</div>
<div style="display: grid; grid-template-columns: repeat(3, 1fr); gap: 15px; text-align: center;">
<div style="background: rgba(40, 167, 69, 0.1); padding: 15px; border-radius: 8px; border: 2px solid #28a745;">
<div style="font-size: 2rem; font-weight: bold; color: #28a745;">{will_pay}</div>
<div style="font-weight: bold;">✅ Will Pay</div>
</div>
<div style="background: rgba(220, 53, 69, 0.1); padding: 15px; border-radius: 8px; border: 2px solid #dc3545;">
<div style="font-size: 2rem; font-weight: bold; color: #dc3545;">{risk_default}</div>
<div style="font-weight: bold;">❌ Risk of Default</div>
</div>
<div style="background: rgba(255, 193, 7, 0.1); padding: 15px; border-radius: 8px; border: 2px solid #ffc107;">
<div style="font-size: 2rem; font-weight: bold; color: #856404;">{high_risk}</div>
<div style="font-weight: bold;">🔴 High Risk</div>
</div>
</div>
</div>
"""
return results_df, success_msg, fig
except Exception as e:
error_msg = f"""
<div style="background: #f8d7da; border: 1px solid #f5c6cb; color: #721c24; padding: 15px; border-radius: 10px; text-align: center;">
<strong>❌ Prediction failed:</strong> {str(e)}
</div>
"""
return None, error_msg, None
# Initialize the predictor
predictor = SAPARPredictor()
# Create Gradio interface with improved layout
with gr.Blocks(
theme=gr.themes.Soft(
primary_hue="blue",
secondary_hue="green",
neutral_hue="slate"
),
title="SAP AR ML Prediction Demo",
css="""
.gradio-container {
max-width: 1400px !important;
margin: 0 auto !important;
}
.main-header {
text-align: center;
margin-bottom: 2rem;
padding: 2rem;
background: linear-gradient(135deg, #1e3c72 0%, #2a5298 100%);
border-radius: 15px;
color: white;
margin-bottom: 30px;
box-shadow: 0 8px 32px rgba(0,0,0,0.2);
}
.tab-nav {
margin-bottom: 20px;
}
"""
) as demo:
gr.HTML("""
<div class="main-header">
<h1 style="font-size: 2.5rem; margin-bottom: 15px; text-shadow: 2px 2px 4px rgba(0,0,0,0.5); color: white; font-weight: bold;">
🏢 SAP Account Receivable ML Prediction Demo
</h1>
<p style="font-size: 1.2rem; color: rgba(255,255,255,0.9); margin: 0; text-shadow: 1px 1px 2px rgba(0,0,0,0.3);">
Machine Learning-powered invoice payment prediction system using TensorFlow
</p>
</div>
""")
with gr.Tabs() as tabs:
with gr.Tab("🎯 Model Training", id=0):
with gr.Row():
with gr.Column(scale=2):
gr.Markdown("""
### 🚀 Train Your ML Model
This will create a neural network trained on **1,000 synthetic SAP AR records** to predict invoice payment likelihood.
The model analyzes multiple factors including invoice amount, days overdue, customer credit score, and payment history.
""")
train_btn = gr.Button(
"🚀 Train ML Model",
variant="primary",
size="lg",
scale=1
)
with gr.Column(scale=1):
gr.Markdown("""
### 📋 Model Features
- Invoice Amount
- Days Overdue
- Previous Delays
- Credit Score
- Industry Risk
- Seasonality
""")
metrics_display = gr.HTML()
with gr.Row():
training_plot = gr.Plot(label="📈 Training Progress")
with gr.Row():
predict_btn = gr.Button(
"🔮 Generate Predictions",
variant="secondary",
interactive=False,
size="lg"
)
with gr.Tab("📊 Invoice Predictions", id=1):
gr.Markdown("""
### 🔮 Real-time Payment Predictions
View ML-powered predictions for unpaid invoices with probability scores and risk assessments.
""")
prediction_status = gr.HTML()
# Changed layout to stack vertically instead of side by side
with gr.Column():
predictions_df = gr.Dataframe(
label="📋 Invoice Predictions",
interactive=False,
wrap=True,
# height=400
)
probability_plot = gr.Plot(label="📊 Probability Distribution")
# Event handlers
train_btn.click(
fn=predictor.train_model,
outputs=[training_plot, metrics_display, predict_btn],
show_progress=True
)
predict_btn.click(
fn=predictor.make_predictions,
outputs=[predictions_df, prediction_status, probability_plot]
)
# Launch the app
if __name__ == "__main__":
demo.launch(share=True)