|
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") |
|
|