Spaces:
Running
on
CPU Upgrade
Running
on
CPU Upgrade
"""A gradio app for credit card approval prediction using FHE.""" | |
import subprocess | |
import time | |
import gradio as gr | |
from settings import ( | |
REPO_DIR, | |
ACCOUNT_MIN_MAX, | |
CHILDREN_MIN_MAX, | |
INCOME_MIN_MAX, | |
AGE_MIN_MAX, | |
FAMILY_MIN_MAX, | |
INCOME_TYPES, | |
OCCUPATION_TYPES, | |
HOUSING_TYPES, | |
EDUCATION_TYPES, | |
FAMILY_STATUS, | |
YEARS_EMPLOYED_BINS, | |
INCOME_VALUE, | |
AGE_VALUE, | |
) | |
from backend import ( | |
keygen_send, | |
pre_process_encrypt_send_user, | |
pre_process_encrypt_send_bank, | |
pre_process_encrypt_send_cs_agency, | |
run_fhe, | |
get_output_and_decrypt, | |
explain_encrypt_run_decrypt, | |
) | |
subprocess.Popen(["uvicorn", "server:app"], cwd=REPO_DIR) | |
time.sleep(3) | |
demo = gr.Blocks() | |
print("Starting the demo...") | |
with demo: | |
gr.Markdown( | |
""" | |
<p align="center"> | |
<img width=200 src="file/images/logos/zama.jpg"> | |
</p> | |
<h1 align="center">Encrypted Credit Card Approval Prediction Using Fully Homomorphic Encryption</h1> | |
<p align="center"> | |
<a href="https://github.com/zama-ai/concrete-ml"> <img style="vertical-align: middle; display:inline-block; margin-right: 3px;" width=15 src="file/images/logos/github.png">Concrete-ML</a> | |
— | |
<a href="https://docs.zama.ai/concrete-ml"> <img style="vertical-align: middle; display:inline-block; margin-right: 3px;" width=15 src="file/images/logos/documentation.png">Documentation</a> | |
— | |
<a href="https://zama.ai/community"> <img style="vertical-align: middle; display:inline-block; margin-right: 3px;" width=15 src="file/images/logos/community.png">Community</a> | |
— | |
<a href="https://twitter.com/zama_fhe"> <img style="vertical-align: middle; display:inline-block; margin-right: 3px;" width=15 src="file/images/logos/x.png">@zama_fhe</a> | |
</p> | |
<p align="center"> | |
<img src="file/images/banner.png" width="70%" height="70%"> | |
</p> | |
""" | |
) | |
gr.Markdown("# Client, Bank and Credit Scoring Agency setup") | |
gr.Markdown("## Step 1: Generate the keys.") | |
gr.Markdown( | |
""" | |
- The private key is generated jointly by the entities that collaborate to compute the | |
credit score. It is used to encrypt and decrypt the data and shall never be shared with | |
any other party. | |
- The evaluation key is a public key that the server needs to process encrypted data. It is | |
therefore transmitted to the server for further processing as well. | |
""" | |
) | |
keygen_button = gr.Button("Generate the keys and send evaluation key to the server.") | |
evaluation_key = gr.Textbox( | |
label="Evaluation key representation:", max_lines=2, interactive=False | |
) | |
client_id = gr.Textbox(label="", max_lines=2, interactive=False, visible=False) | |
# Button generate the keys | |
keygen_button.click( | |
keygen_send, | |
outputs=[client_id, evaluation_key, keygen_button], | |
) | |
gr.Markdown("## Step 2: Fill in some information.") | |
gr.Markdown( | |
""" | |
Select the information that corresponds to the profile you want to evaluate. Three sources | |
of information are represented in this model: | |
- a user's personal information in order to evaluate his/her credit card eligibility; | |
- the user’s bank account history, which provides any type of information on the user's | |
banking information relevant to the decision (here, we consider duration of account); | |
- and credit scoring agency information, which represents any other information (here, | |
employment history) that could provide additional insight relevant to the decision. | |
Please always encrypt and send the values (through the buttons on the right) once updated | |
before running the FHE inference. | |
""" | |
) | |
with gr.Row(): | |
with gr.Column(): | |
gr.Markdown("### User") | |
bool_inputs = gr.CheckboxGroup( | |
["Car", "Property", "Mobile phone"], | |
label="Which of the following do you actively hold or own?" | |
) | |
num_children = gr.Slider( | |
**CHILDREN_MIN_MAX, | |
step=1, | |
label="Number of children", | |
info="How many children do you have ?" | |
) | |
household_size = gr.Slider( | |
**FAMILY_MIN_MAX, | |
step=1, | |
label="Household size", | |
info="How many members does your household have ?" | |
) | |
total_income = gr.Slider( | |
**INCOME_MIN_MAX, | |
value=INCOME_VALUE, | |
label="Income", | |
info="What's you total yearly income (in euros) ?" | |
) | |
age = gr.Slider( | |
**AGE_MIN_MAX, | |
value=AGE_VALUE, | |
step=1, | |
label="Age", | |
info="How old are you ?" | |
) | |
with gr.Column(): | |
income_type = gr.Dropdown( | |
choices=INCOME_TYPES, | |
value=INCOME_TYPES[0], | |
label="Income type", | |
info="What is your main type of income ?" | |
) | |
education_type = gr.Dropdown( | |
choices=EDUCATION_TYPES, | |
value=EDUCATION_TYPES[0], | |
label="Education", | |
info="What is your education background ?" | |
) | |
family_status = gr.Dropdown( | |
choices=FAMILY_STATUS, | |
value=FAMILY_STATUS[0], | |
label="Family", | |
info="What is your family status ?" | |
) | |
occupation_type = gr.Dropdown( | |
choices=OCCUPATION_TYPES, | |
value=OCCUPATION_TYPES[0], | |
label="Occupation", | |
info="What is your main occupation ?" | |
) | |
housing_type = gr.Dropdown( | |
choices=HOUSING_TYPES, | |
value=HOUSING_TYPES[0], | |
label="Housing", | |
info="In what type of housing do you live ?" | |
) | |
with gr.Column(): | |
encrypt_button_user = gr.Button("Encrypt the inputs and send to server.") | |
encrypted_input_user = gr.Textbox( | |
label="Encrypted input representation:", max_lines=2, interactive=False | |
) | |
with gr.Row(): | |
with gr.Column(scale=2): | |
gr.Markdown("### Bank ") | |
account_age = gr.Slider( | |
**ACCOUNT_MIN_MAX, | |
step=1, | |
label="Account age (months)", | |
info="How long have this person had this bank account (in months) ?" | |
) | |
with gr.Column(): | |
encrypt_button_bank = gr.Button("Encrypt the inputs and send to server.") | |
encrypted_input_bank = gr.Textbox( | |
label="Encrypted input representation:", max_lines=2, interactive=False | |
) | |
with gr.Row(): | |
with gr.Column(scale=2): | |
gr.Markdown("### Credit Scoring Agency ") | |
employed = gr.Radio(["Yes", "No"], label="Is the person employed ?", value="Yes") | |
years_employed = gr.Dropdown( | |
choices=YEARS_EMPLOYED_BINS, | |
value=YEARS_EMPLOYED_BINS[0], | |
label="Years of employment", | |
info="How long have this person been employed (in years) ?" | |
) | |
with gr.Column(): | |
encrypt_button_cs_agency = gr.Button("Encrypt the inputs and send to server.") | |
encrypted_input_cs_agency = gr.Textbox( | |
label="Encrypted input representation:", max_lines=2, interactive=False | |
) | |
# Button to pre-process, generate the key, encrypt and send the user inputs from the client | |
# side to the server | |
encrypt_button_user.click( | |
pre_process_encrypt_send_user, | |
inputs=[client_id, bool_inputs, num_children, household_size, total_income, age, \ | |
income_type, education_type, family_status, occupation_type, housing_type], | |
outputs=[encrypted_input_user], | |
) | |
# Button to pre-process, generate the key, encrypt and send the bank inputs from the client | |
# side to the server | |
encrypt_button_bank.click( | |
pre_process_encrypt_send_bank, | |
inputs=[client_id, account_age], | |
outputs=[encrypted_input_bank], | |
) | |
# Button to pre-process, generate the key, encrypt and send the credit scoring agency inputs from the | |
# client side to the server | |
encrypt_button_cs_agency.click( | |
pre_process_encrypt_send_cs_agency, | |
inputs=[client_id, years_employed, employed], | |
outputs=[encrypted_input_cs_agency], | |
) | |
gr.Markdown("# Server side") | |
gr.Markdown( | |
""" | |
Once the server receives the encrypted inputs, it can compute the prediction without ever | |
needing to decrypt any value. | |
This server employs an [XGBoost](https://github.com/dmlc/xgboost) classifier model that has | |
been trained on a synthetic data-set. | |
""" | |
) | |
gr.Markdown("## Step 4: Run FHE execution.") | |
execute_fhe_button = gr.Button("Run FHE execution.") | |
fhe_execution_time = gr.Textbox( | |
label="Total FHE execution time (in seconds):", max_lines=1, interactive=False | |
) | |
# Button to send the encodings to the server using post method | |
execute_fhe_button.click(run_fhe, inputs=[client_id], outputs=[fhe_execution_time]) | |
gr.Markdown("# Client, Bank and Credit Scoring Agency decryption") | |
gr.Markdown( | |
""" | |
Once the server completed the inference, the encrypted output is returned to the user. | |
The three entities that provide the information to compute the credit score are the only | |
ones that can decrypt the result. They take part in a decryption protocol that allows to | |
only decrypt the full result when all three parties decrypt their share of the result. | |
""" | |
) | |
gr.Markdown("## Step 5: Receive the encrypted output from the server and decrypt.") | |
gr.Markdown( | |
""" | |
The first value displayed below is a shortened byte representation of the actual encrypted | |
output. | |
The user is then able to decrypt the value using its private key. | |
""" | |
) | |
get_output_button = gr.Button("Receive the encrypted output from the server.") | |
encrypted_output_representation = gr.Textbox( | |
label="Encrypted output representation: ", max_lines=2, interactive=False | |
) | |
prediction_output = gr.Textbox( | |
label="Prediction", max_lines=1, interactive=False | |
) | |
# Button to send the encodings to the server using post method | |
get_output_button.click( | |
get_output_and_decrypt, | |
inputs=[client_id], | |
outputs=[prediction_output, encrypted_output_representation], | |
) | |
gr.Markdown("## Step 6 (optional): Explain the prediction.") | |
gr.Markdown( | |
""" | |
In case the credit card is likely to be denied, the user can ask for how many years of | |
employment would most likely be required in order to increase the chance of getting a | |
credit card approval. | |
All of the above steps are combined into a single button for simplicity. The following | |
button therefore encrypts the same inputs (except the years of employment, which varies) | |
from all three parties, runs the new prediction in FHE and decrypts the output. | |
In case the following states to try a new "Years of employment" input, one can simply | |
update the value in Step 2 and directly run Step 6 once more. | |
""" | |
) | |
explain_button = gr.Button( | |
"Encrypt the inputs, compute in FHE and decrypt the output." | |
) | |
explain_prediction = gr.Textbox( | |
label="Additional years of employed required.", interactive=False | |
) | |
# Button to explain the prediction | |
explain_button.click( | |
explain_encrypt_run_decrypt, | |
inputs=[client_id, prediction_output, years_employed, employed], | |
outputs=[explain_prediction], | |
) | |
gr.Markdown( | |
"The app was built with [Concrete-ML](https://github.com/zama-ai/concrete-ml), a " | |
"Privacy-Preserving Machine Learning (PPML) open-source set of tools by [Zama](https://zama.ai/). " | |
"Try it yourself and don't forget to star on Github ⭐." | |
) | |
demo.launch(share=False) | |