| | import streamlit as st |
| | import pandas as pd |
| | import logging |
| | from deeploy import Client, CreateEvaluation |
| | from utils import ( |
| | get_request_body, |
| | get_fake_certainty, |
| | get_model_url, |
| | get_random_suspicious_transaction, |
| | ) |
| | from utils import ( |
| | get_explainability_texts, |
| | get_explainability_values, |
| | send_evaluation, |
| | get_comment_explanation, |
| | ) |
| | from utils import COL_NAMES, feature_texts |
| | from utils import ( |
| | create_data_input_table, |
| | create_table, |
| | ChangeButtonColour, |
| | get_weights, |
| | modify_datapoint, |
| | ) |
| |
|
| | logging.basicConfig(level=logging.INFO) |
| |
|
| | st.set_page_config(layout="wide") |
| |
|
| | st.title("Smart AML:tm:") |
| | st.divider() |
| |
|
| | |
| | data = pd.read_pickle("data/preprocessed_data.pkl") |
| |
|
| | |
| | if "predict_button_clicked" not in st.session_state: |
| | st.session_state.predict_button_clicked = False |
| |
|
| | if "submitted_disabled" not in st.session_state: |
| | st.session_state.submitted_disabled = False |
| |
|
| | if "disabled" not in st.session_state: |
| | st.session_state.disabled = False |
| |
|
| | if "no_button_text" not in st.session_state: |
| | st.session_state.no_button_text = ( |
| | "I don't think this transaction is money laundering because..." |
| | ) |
| |
|
| | if "yes_button_text" not in st.session_state: |
| | st.session_state.yes_button_text = "" |
| |
|
| | if "yes_button_clicked" not in st.session_state: |
| | st.session_state.yes_button_clicked = False |
| |
|
| |
|
| | |
| | |
| | def get_input_no_button(): |
| | st.session_state.no_button_text = comment.replace( |
| | st.session_state.no_button_text, st.session_state.no_comment |
| | ) |
| | st.session_state.evaluation_input["comment"] = st.session_state.no_button_text |
| |
|
| |
|
| | |
| | def get_input_yes_button(): |
| | st.session_state.yes_button_text = comment.replace( |
| | st.session_state.yes_button_text, st.session_state.yes_comment |
| | ) |
| | st.session_state.evaluation_input["comment"] = st.session_state.yes_button_text |
| |
|
| |
|
| | |
| | def disabled(): |
| | st.session_state.disabled = True |
| |
|
| |
|
| | |
| | def rerun(): |
| | st.session_state.predict_button_clicked = True |
| | st.session_state.submitted_disabled = False |
| | st.session_state.no_button_text = ( |
| | "I don't think this transaction is money laundering because..." |
| | ) |
| |
|
| |
|
| | |
| | def submitted_disabled(): |
| | st.session_state.submitted_disabled = True |
| |
|
| |
|
| | |
| | st.markdown( |
| | """ |
| | <style> |
| | [data-testid=stSidebar] { |
| | background-color: #E0E0E0; ##E5E6EA |
| | } |
| | </style> |
| | """, |
| | unsafe_allow_html=True, |
| | ) |
| |
|
| | with st.sidebar: |
| | |
| | st.image("deeploy_logo.png", width=270) |
| | |
| | host = st.text_input("Host (changing is optional)", "app.deeploy.ml") |
| | model_url, workspace_id, deployment_id = get_model_url() |
| | deployment_token = st.text_input("Deeploy API token", "my-secret-token") |
| | if deployment_token == "my-secret-token": |
| | |
| | st.warning("Please enter Deeploy API token.") |
| | else: |
| | st.button( |
| | "Get suspicious transaction", |
| | key="predict_button", |
| | help="Click to get a suspicious transaction", |
| | use_container_width=True, |
| | on_click=disabled, |
| | disabled=st.session_state.disabled, |
| | ) |
| | ChangeButtonColour("Get suspicious transaction", "#FFFFFF", "#00052D") |
| |
|
| | |
| | client_options = { |
| | "host": host, |
| | "deployment_token": deployment_token, |
| | "workspace_id": workspace_id, |
| | } |
| | client = Client(**client_options) |
| |
|
| | |
| | |
| | if "predict_button" not in st.session_state: |
| | st.session_state.predict_button = False |
| |
|
| | if st.session_state.predict_button: |
| | st.session_state.predict_button_clicked = True |
| |
|
| | if "got_explanation" not in st.session_state: |
| | st.session_state.got_explanation = False |
| |
|
| | |
| | if st.session_state.predict_button_clicked: |
| | try: |
| | with st.spinner("Loading..."): |
| | datapoint_pd = get_random_suspicious_transaction(data) |
| | request_body = get_request_body(datapoint_pd) |
| | |
| | exp = client.explain(request_body=request_body, deployment_id=deployment_id) |
| | st.session_state.shap_values = exp["explanations"][0]["shap_values"] |
| | st.session_state.request_log_id = exp["requestLogId"] |
| | st.session_state.prediction_log_id = exp["predictionLogIds"][0] |
| | st.session_state.datapoint_pd = datapoint_pd |
| | certainty = get_fake_certainty() |
| | st.session_state.certainty = certainty |
| | st.session_state.got_explanation = True |
| | st.session_state.predict_button_clicked = False |
| | except Exception as e: |
| | logging.error(e) |
| | st.error( |
| | "Failed to retrieve the prediction or explanation." |
| | + "Check whether you are using the right model URL and Token. " |
| | + "Contact Deeploy if the problem persists." |
| | ) |
| |
|
| | |
| | if not st.session_state.got_explanation: |
| | st.info( |
| | "Fill in left hand side and click on button to observe a potential fraudulent transaction" |
| | ) |
| |
|
| | |
| | if st.session_state.got_explanation: |
| | shap_values = st.session_state.shap_values |
| | request_log_id = st.session_state.request_log_id |
| | prediction_log_id = st.session_state.prediction_log_id |
| | datapoint_pd = st.session_state.datapoint_pd |
| | certainty = st.session_state.certainty |
| | datapoint = modify_datapoint(datapoint_pd) |
| |
|
| | |
| | col1, col2 = st.columns(2) |
| |
|
| | |
| | with col1: |
| | create_data_input_table(datapoint, COL_NAMES) |
| |
|
| | |
| | with col2: |
| | st.subheader("AML Model Hit") |
| | st.metric(label="Model Certainty", value=certainty, delta="threshold: 75%") |
| | explainability_texts, sorted_indices = get_explainability_texts( |
| | shap_values, feature_texts |
| | ) |
| | weights = get_weights(shap_values, sorted_indices) |
| | explainability_values = get_explainability_values(sorted_indices, datapoint) |
| | create_table( |
| | explainability_texts, |
| | explainability_values, |
| | weights, |
| | "Important Suspicious Factors", |
| | ) |
| |
|
| | st.subheader("") |
| |
|
| | |
| | if "eval_selected" not in st.session_state: |
| | st.session_state["eval_selected"] = False |
| |
|
| | |
| | col3, col4 = st.columns(2) |
| |
|
| | |
| | with col3: |
| | |
| | eval1 = st.empty() |
| | eval1.button( |
| | "Send to FIU", |
| | key="yes_button", |
| | use_container_width=True, |
| | disabled=st.session_state.submitted_disabled, |
| | ) |
| | ChangeButtonColour("Send to FIU", "#FFFFFF", "#4C506C") |
| | st.session_state.yes_button_clicked = False |
| |
|
| | if st.session_state.yes_button: |
| | st.session_state.eval_selected = True |
| | st.session_state.evaluation_input = {"agree": True} |
| |
|
| | |
| | with col4: |
| | |
| | eval2 = st.empty() |
| | eval2.button( |
| | "Not money laundering", |
| | key="no_button", |
| | use_container_width=True, |
| | disabled=st.session_state.submitted_disabled, |
| | ) |
| | ChangeButtonColour("Not money laundering", "#FFFFFF", "#4C506C") |
| | st.session_state.no_button_clicked = False |
| | if st.session_state.no_button: |
| | st.session_state.no_button_clicked = True |
| | if st.session_state.no_button_clicked: |
| | st.session_state.eval_selected = True |
| | st.session_state.evaluation_input = { |
| | "agree": False, |
| | "desired_output": {"predictions": [1]}, |
| | } |
| |
|
| | |
| | success = False |
| | if st.session_state.eval_selected: |
| | |
| | |
| | if st.session_state.yes_button: |
| | st.session_state.yes_button_clicked = True |
| | yes_button = True |
| | explanation = get_comment_explanation( |
| | certainty, explainability_texts, explainability_values |
| | ) |
| | st.session_state.yes_button_text = explanation |
| | comment = st.text_area( |
| | "Reason for evaluation:", |
| | st.session_state.yes_button_text, |
| | key="yes_comment", |
| | on_change=get_input_yes_button, |
| | ) |
| | st.session_state.evaluation_input[ |
| | "comment" |
| | ] = st.session_state.yes_button_text |
| |
|
| | |
| | |
| | if st.session_state.no_button: |
| | comment = st.text_area( |
| | "Reason for evaluation:", |
| | st.session_state.no_button_text, |
| | key="no_comment", |
| | on_change=get_input_no_button, |
| | ) |
| | st.session_state.evaluation_input[ |
| | "comment" |
| | ] = st.session_state.no_button_text |
| | |
| | eval3 = st.empty() |
| | eval3.button( |
| | "Submit", |
| | key="submit_button", |
| | use_container_width=True, |
| | on_click=submitted_disabled, |
| | disabled=st.session_state.submitted_disabled, |
| | ) |
| | ChangeButtonColour("Submit", "#FFFFFF", "#00052D") |
| |
|
| | |
| | if st.session_state.submit_button: |
| | st.session_state.eval_selected = False |
| | success = send_evaluation( |
| | client, |
| | deployment_id, |
| | request_log_id, |
| | prediction_log_id, |
| | st.session_state.evaluation_input, |
| | ) |
| | |
| | |
| | if success: |
| | st.session_state.eval_selected = False |
| | st.session_state.submitted = True |
| | eval1.empty() |
| | eval2.empty() |
| | eval3.empty() |
| | st.success("Feedback submitted successfully") |
| | st.button("Next", key="next", use_container_width=True, on_click=rerun) |
| | ChangeButtonColour("Next", "#FFFFFF", "#00052D") |
| |
|