|
"""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_applicant, |
|
pre_process_encrypt_send_bank, |
|
pre_process_encrypt_send_credit_bureau, |
|
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> |
|
""" |
|
) |
|
|
|
with gr.Accordion("What is credit scoring for card approval?", open=False): |
|
gr.Markdown( |
|
""" |
|
It is a complex process that involves several entities: the applicant, the bank, the |
|
credit bureau, and the credit scoring agency. When you apply for a credit card, you |
|
provide personal and financial information to the bank. This might include your income, |
|
employment status, and existing debts. The bank uses this information to assess your |
|
creditworthiness. To do this, they often turn to credit bureaus and credit scoring |
|
agencies. |
|
- Credit bureaus collect and maintain data on consumers' credit and payment |
|
histories. This data includes your past and current debts, payment history, and the |
|
length of your credit history. |
|
- Credit scoring agencies use algorithms to analyze |
|
the data from credit bureaus and generate a credit score. This score is a numerical |
|
representation of your creditworthiness. |
|
- The bank uses your credit score, along with |
|
the information you provided, to make a decision on your credit card application. A |
|
higher credit score generally increases your chances of being approved and may result |
|
in better terms (like a lower interest rate). |
|
""" |
|
) |
|
|
|
with gr.Accordion("Why is it critical to add a new privacy layer to this process?", open=False): |
|
gr.Markdown( |
|
""" |
|
The data involved is highly sensitive. It includes personal details like your Social |
|
Security number, income, and credit history. There's significant sharing of data |
|
between different entities. Your information is not just with the bank, but also with |
|
credit bureaus and scoring agencies. The more entities that have access to your data, |
|
the greater the risk of a data breach. This can lead to identity theft and financial |
|
fraud. There's also the issue of data accuracy. Mistakes in credit reports can lead to |
|
unjustly low credit scores, affecting your ability to get credit. |
|
""" |
|
) |
|
|
|
with gr.Accordion( |
|
"Why is Fully Homomorphic Encryption (FHE) a solution for better credit scoring?", |
|
open=False, |
|
): |
|
gr.Markdown( |
|
""" |
|
Fully Homomorphic Encryption (FHE) is seen as an ideal solution for enhancing privacy |
|
and accuracy in credit scoring processes involving multiple parties like applicants, |
|
banks, credit bureaus, and credit scoring agencies. It allows data to be encrypted and |
|
processed without ever needing to decrypt it. This means that sensitive data can be |
|
shared and analyzed without exposing the actual information to any of the parties or |
|
the server processing it. In the context of credit scoring, this would enable a more |
|
thorough and accurate assessment of a person's creditworthiness. Data from various |
|
sources can be combined and analyzed to make a more informed decision, yet each party's |
|
data remains confidential. As a result, the risk of data leaks or breaches is |
|
significantly minimized, addressing major privacy concerns. |
|
|
|
To summarize, FHE provides a means to make more accurate credit eligibility decisions |
|
while maintaining strict data privacy, offering a sophisticated solution to the delicate |
|
balance between data utility and confidentiality. |
|
""" |
|
) |
|
|
|
gr.Markdown( |
|
""" |
|
<p align="center"> |
|
<img src="file/images/banner.png"> |
|
</p> |
|
""" |
|
) |
|
|
|
gr.Markdown("## Step 1: Generate the keys.") |
|
gr.Markdown("<hr />") |
|
gr.Markdown("<span style='color:grey'>Applicant, Bank and Credit bureau setup</span>") |
|
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) |
|
|
|
|
|
keygen_button.click( |
|
keygen_send, |
|
outputs=[client_id, evaluation_key, keygen_button], |
|
) |
|
|
|
gr.Markdown("## Step 2: Fill in some information.") |
|
gr.Markdown("<hr />") |
|
gr.Markdown("<span style='color:grey'>Applicant, Bank and Credit bureau setup</span>") |
|
gr.Markdown( |
|
""" |
|
Select the information that corresponds to the profile you want to evaluate. Three sources |
|
of information are represented in this model: |
|
- the applicant's personal information in order to evaluate his/her credit card eligibility; |
|
- the applicant bank account history, which provides any type of information on the |
|
applicant's banking information relevant to the decision (here, we consider duration of |
|
account); |
|
- and credit bureau 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("### Step 2.1 - Applicant information π§βπ»") |
|
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.Row(): |
|
with gr.Column(scale=2): |
|
encrypt_button_applicant = gr.Button("Encrypt the inputs and send to server.") |
|
|
|
encrypted_input_applicant = gr.Textbox( |
|
label="Encrypted input representation:", max_lines=2, interactive=False |
|
) |
|
|
|
gr.Markdown("<hr />") |
|
with gr.Column(): |
|
gr.Markdown("### Step 2.2 - Bank information π¦") |
|
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.Row(): |
|
with gr.Column(scale=2): |
|
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 |
|
) |
|
|
|
gr.Markdown("<hr />") |
|
with gr.Column(): |
|
gr.Markdown("### Step 2.3 - Credit bureau information π’") |
|
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.Row(): |
|
with gr.Column(scale=2): |
|
encrypt_button_credit_bureau = gr.Button("Encrypt the inputs and send to server.") |
|
|
|
encrypted_input_credit_bureau = gr.Textbox( |
|
label="Encrypted input representation:", max_lines=2, interactive=False |
|
) |
|
|
|
|
|
|
|
encrypt_button_applicant.click( |
|
pre_process_encrypt_send_applicant, |
|
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_applicant, encrypt_button_applicant], |
|
) |
|
|
|
|
|
|
|
encrypt_button_bank.click( |
|
pre_process_encrypt_send_bank, |
|
inputs=[client_id, account_age], |
|
outputs=[encrypted_input_bank, encrypt_button_bank], |
|
) |
|
|
|
|
|
|
|
encrypt_button_credit_bureau.click( |
|
pre_process_encrypt_send_credit_bureau, |
|
inputs=[client_id, years_employed, employed], |
|
outputs=[encrypted_input_credit_bureau, encrypt_button_credit_bureau], |
|
) |
|
|
|
gr.Markdown("## Step 3: Run the FHE evaluation.") |
|
gr.Markdown("<hr />") |
|
gr.Markdown("<span style='color:grey'>Server Side</span>") |
|
gr.Markdown( |
|
""" |
|
Once the server receives the encrypted inputs, it can compute the prediction without ever |
|
needing to decrypt any value. |
|
|
|
This server employs a [Decision Tree](https://scikit-learn.org/stable/modules/generated/sklearn.tree.DecisionTreeClassifier.html) |
|
classifier model that has been trained on a synthetic data-set. |
|
""" |
|
) |
|
|
|
execute_fhe_button = gr.Button("Run the FHE evaluation.") |
|
fhe_execution_time = gr.Textbox( |
|
label="Total FHE execution time (in seconds):", max_lines=1, interactive=False |
|
) |
|
|
|
|
|
execute_fhe_button.click(run_fhe, inputs=[client_id], outputs=[fhe_execution_time, execute_fhe_button]) |
|
|
|
gr.Markdown("## Step 4: Receive the encrypted output from the server and decrypt.") |
|
gr.Markdown("<hr />") |
|
gr.Markdown("<span style='color:grey'>Applicant, Bank and Credit bureau decryption</span>") |
|
gr.Markdown( |
|
""" |
|
Once the server completed the inference, the encrypted output is returned to the applicant. |
|
|
|
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( |
|
""" |
|
The first value displayed below is a shortened byte representation of the actual encrypted |
|
output. |
|
The applicant 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 |
|
) |
|
|
|
|
|
get_output_button.click( |
|
get_output_and_decrypt, |
|
inputs=[client_id], |
|
outputs=[prediction_output, encrypted_output_representation, get_output_button], |
|
) |
|
|
|
gr.Markdown("## Step 5: Explain the prediction (only if your credit card is likely to be denied).") |
|
gr.Markdown("<hr />") |
|
gr.Markdown( |
|
""" |
|
In case the credit card is likely to be denied, the applicant 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 |
|
) |
|
|
|
|
|
explain_button.click( |
|
explain_encrypt_run_decrypt, |
|
inputs=[client_id, prediction_output, years_employed, employed], |
|
outputs=[explain_prediction, explain_button], |
|
) |
|
|
|
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) |
|
|