Spaces:
Running
on
L40S
Running
on
L40S
import streamlit as st | |
st.set_page_config(layout="wide") | |
import streamlit_authenticator as stauth | |
from uuid import uuid4 | |
import model_comparison as MCOMP | |
import model_loading as MLOAD | |
import model_inferencing as MINFER | |
import user_evaluation_variables | |
from pathlib import Path | |
import tab_manager | |
import yaml | |
from yaml.loader import SafeLoader | |
from PIL import Image | |
import os | |
from huggingface_hub import CommitScheduler, login, snapshot_download | |
login(token=os.environ.get("HF_TOKEN"), write_permission=True, read_permission=True) | |
AUTHENTICATOR = None | |
TBYB_LOGO = Image.open('./assets/TBYB_logo_light.png') | |
USER_LOGGED_IN = False | |
USER_DATABASE_DIR = Path("data") | |
USER_DATABASE_DIR.mkdir(parents=True, exist_ok=True) | |
USER_DATABASE_PATH = USER_DATABASE_DIR / f"user_database.yaml" | |
USER_DATABASE_UPDATE_SCHEDULER = CommitScheduler( | |
repo_id="try-before-you-bias-data", | |
repo_type="dataset", | |
folder_path=USER_DATABASE_DIR, | |
path_in_repo="data", | |
every=3, | |
) | |
def create_new_user(authenticator, users): | |
try: | |
if authenticator.register_user('Register user', preauthorization=False): | |
st.success('User registered successfully') | |
except Exception as e: | |
st.error(e) | |
with USER_DATABASE_UPDATE_SCHEDULER.lock: | |
with USER_DATABASE_PATH.open('w') as file: | |
yaml.dump(users, file, default_flow_style=False) | |
def forgot_password(authenticator, users): | |
try: | |
username_of_forgotten_password, email_of_forgotten_password, new_random_password = authenticator.forgot_password( | |
'Forgot password') | |
if username_of_forgotten_password: | |
st.success('New password to be sent securely') | |
# Random password should be transferred to user securely | |
except Exception as e: | |
st.error(e) | |
with USER_DATABASE_UPDATE_SCHEDULER.lock: | |
with open(USER_DATABASE_PATH, 'w') as file: | |
yaml.dump(users, file, default_flow_style=False) | |
def update_account_details(authenticator, users): | |
if st.session_state["authentication_status"]: | |
try: | |
if authenticator.update_user_details(st.session_state["username"], 'Update user details'): | |
st.success('Entries updated successfully') | |
except Exception as e: | |
st.error(e) | |
with USER_DATABASE_UPDATE_SCHEDULER.lock: | |
with open(USER_DATABASE_PATH, 'w') as file: | |
yaml.dump(users, file, default_flow_style=False) | |
def reset_password(authenticator, users): | |
if st.session_state["authentication_status"]: | |
try: | |
if authenticator.reset_password(st.session_state["username"], 'Reset password'): | |
st.success('Password modified successfully') | |
except Exception as e: | |
st.error(e) | |
with USER_DATABASE_UPDATE_SCHEDULER.lock: | |
with open(USER_DATABASE_PATH, 'w') as file: | |
yaml.dump(users, file, default_flow_style=False) | |
def user_login_create(): | |
global AUTHENTICATOR | |
global TBYB_LOGO | |
global USER_LOGGED_IN | |
users = None | |
snapshot_download(repo_id='JVice/try-before-you-bias-data',repo_type='dataset',local_dir='user_data') | |
newPath = 'user_data/data/user_database.yaml' | |
with open(newPath) as file: | |
users = yaml.load(file, Loader=SafeLoader) | |
AUTHENTICATOR = stauth.Authenticate( | |
users['credentials'], | |
users['cookie']['name'], | |
users['cookie']['key'], | |
users['cookie']['expiry_days'], | |
users['preauthorized'] | |
) | |
with st.sidebar: | |
st.image(TBYB_LOGO, width=70) | |
loginTab, registerTab, detailsTab = st.tabs(["Log in", "Register", "Account details"]) | |
with loginTab: | |
name, authentication_status, username = AUTHENTICATOR.login('Login', 'main') | |
if authentication_status: | |
AUTHENTICATOR.logout('Logout', 'main') | |
st.write(f'Welcome *{name}*') | |
user_evaluation_variables.USERNAME = username | |
USER_LOGGED_IN = True | |
elif authentication_status == False: | |
st.error('Username/password is incorrect') | |
forgot_password(AUTHENTICATOR, users) | |
elif authentication_status == None: | |
st.warning('Please enter your username and password') | |
forgot_password(AUTHENTICATOR, users) | |
if not authentication_status: | |
with registerTab: | |
create_new_user(AUTHENTICATOR, users) | |
else: | |
with detailsTab: | |
st.write('**Username:** ', username) | |
st.write('**Name:** ', name) | |
st.write('**Email:** ', users['credentials']['usernames'][username]['email']) | |
# update_account_details(AUTHENTICATOR, users) | |
reset_password(AUTHENTICATOR, users) | |
return USER_LOGGED_IN | |
def setup_page_banner(): | |
global USER_LOGGED_IN | |
# for tab in [tab1, tab2, tab3, tab4, tab5]: | |
c1,c2,c3,c4,c5,c6,c7,c8,c9 = st.columns(9) | |
with c5: | |
st.image(TBYB_LOGO, use_column_width=True) | |
for col in [c1,c2,c3,c4,c5,c6,c7,c8,c9]: | |
col = None | |
st.title('Try Before You Bias (TBYB)') | |
st.write('*A Quantitative T2I Bias Evaluation Tool*') | |
def setup_how_to(): | |
expander = st.expander("How to Use") | |
expander.write("1. Login to your TBYB Account using the bar on the right\n" | |
"2. Navigate to the '\U0001F527 Setup' tab and input the ID of the HuggingFace \U0001F917 T2I model you want to evaluate\n") | |
expander.image(Image.open('./assets/HF_MODEL_ID_EXAMPLE.png')) | |
expander.write("3. Test your chosen model by generating an image using an input prompt e.g.: 'A corgi with some cool sunglasses'\n") | |
expander.image(Image.open('./assets/lykon_corgi.png')) | |
expander.write("4. Navigate to the '\U0001F30E General Eval.' or '\U0001F3AF Task-Oriented Eval.' tabs " | |
" to evaluate your model once it has been loaded\n" | |
"5. Once you have generated some evaluation images, head over to the '\U0001F4C1 Generated Images' tab to have a look at them\n" | |
"6. To check out your evaluations or all of the TBYB Community evaluations, head over to the '\U0001F4CA Model Comparison' tab\n" | |
"7. For more information about the evaluation process, see our paper at --PAPER HYPERLINK-- or navigate to the " | |
" '\U0001F4F0 Additional Information' tab for a TL;DR.\n" | |
"8. For any questions or to report any bugs/issues. Please contact jordan.vice@uwa.edu.au.\n") | |
def setup_additional_information_tab(tab): | |
with tab: | |
st.header("1. Quantifying Bias in Text-to-Image (T2I) Generative Models") | |
st.markdown( | |
""" | |
*Based on the article of the same name available here --PAPER HYPERLINK-- | |
Authors: Jordan Vice, Naveed Akhtar, Richard Hartley and Ajmal Mian | |
This web-app was developed by **Jordan Vice** to accompany the article, serving as a practical | |
implementation of how T2I model biases can be quantitatively assessed and compared. Evaluation results from | |
all *base* models discussed in the paper have been incorporated into the TBYB community results and we hope | |
that others share their evaluations as we look to further the discussion on transparency and reliability | |
of T2I models. | |
""") | |
st.header('2. A (very) Brief Summary') | |
st.image(Image.open('./assets/TBYB_flowchart.png')) | |
st.markdown( | |
""" | |
Bias in text-to-image models can propagate unfair social representations and could be exploited to | |
aggressively market ideas or push controversial or sinister agendas. Existing T2I model bias evaluation | |
methods focused on social biases. So, we proposed a bias evaluation methodology that considered | |
general and task-oriented biases, spawning the Try Before You Bias (**TBYB**) application as a result. | |
""" | |
) | |
st.markdown( | |
""" | |
We proposed three novel metrics to quantify T2I model biases: | |
1. Distribution Bias - $B_D$ | |
2. Jaccard Hallucination - $H_J$ | |
3. Generative Miss Rate - $M_G$ | |
Open the appropriate drop-down menu to understand the logic and inspiration behind metric. | |
""" | |
) | |
c1,c2,c3 = st.columns(3) | |
with c1: | |
with st.expander("Distribution Bias - $B_D$"): | |
st.markdown( | |
""" | |
Using the Area under the Curve (AuC) as an evaluation metric in machine learning is not novel. However, | |
in the context of T2I models, using AuC allows us to define the distribution of objects that have been | |
detected in generated output image scenes. | |
So, everytime an object is detected in a scene, we update a dictionary (which is available for | |
download after running an evaluation). After evaluating a full set of images, you can use this | |
information to determine what objects appear more frequently than others. | |
After all images are evaluated, we sort the objects in descending order and normalize the data. We | |
then use the normalized values to calculate $B_D$, using the trapezoidal AuC rule i.e.: | |
$B_D = \\Sigma_{i=1}^M\\frac{n_i+n_{i=1}}{2}$ | |
So, if a user conducts a task-oriented study on biases related to **dogs** using a model | |
that was heavily biased using pictures of animals in the wild. You might find that after running | |
evaluations, the most common objects detected were trees and grass - even if these objects weren't | |
specified in the prompt. This would result in a very low $B_D$ in comparison to a model that for | |
example was trained on images of dogs and animals in various different scenarios $\\rightarrow$ | |
which would result in a *higher* $B_D$ in comparison. | |
""" | |
) | |
with c2: | |
with st.expander("Jaccard Hallucination - $H_J$"): | |
st.markdown( | |
""" | |
Hallucination is a very common phenomena that is discussed in relation to generative AI, particularly | |
in relation to some of the most popular large language models. Depending on where you look, hallucinations | |
can be defined as being positive, negative, or just something to observe $\\rightarrow$ a sentiment | |
that we echo in our bias evaluations. | |
Now, how does hallucination tie into bias? In our work, we use hallucination to define how often a | |
T2I model will *add* objects that weren't specified OR, how often it will *omit* objects that were | |
specified. This indicates that there could be an innate shift in bias in the model, causing it to | |
add or omit certain objects. | |
Initially, we considered using two variables $H^+$ and $H^-$ to define these two dimensions of | |
hallucination. Then, we considered the Jaccard similarity coefficient, which | |
measures the similarity *and* diversity of two sets of objects/samples - defining this as | |
Jaccard Hallucination - $H_J$. | |
Simply put, we define the set of objects detected in the input prompt and then detect the objects in | |
the corresponding output image. Then, we determine the intersect over union. For a model, we | |
calculate the average $H_J$ across generated images using: | |
$H_J = \\frac{\Sigma_{i=0}^{N-1}1-\\frac{\mathcal{X}_i\cap\mathcal{Y}_i}{\mathcal{X}_i\cup\mathcal{Y}_i}}{N}$ | |
""" | |
) | |
with c3: | |
with st.expander("Generative Miss Rate - $M_G$"): | |
st.markdown( | |
""" | |
Whenever fairness and trust are discussed in the context of machine learning and AI systems, | |
performance is always highlighted as a key metric - regardless of the downstream task. So, in terms | |
of evaluating bias, we thought that it would be important to see if there was a correlation | |
between bias and performance (as we predicted). And while the other metrics do evaluate biases | |
in terms of misalignment, they do not consider the relationship between bias and performance. | |
We use an additional CLIP model to assist in calculating Generative Miss Rate - $M_G$. Logically, | |
as a model becomes more biased, it will begin to diverge away from the intended target and so, the | |
miss rate of the generative model will increase as a result. This was a major consideration when | |
designing this metric. | |
We use the CLIP model as a binary classifier, differentiating between two classes: | |
- the prompt used to generate the image | |
- **NOT** the prompt | |
Through our experiments on intentionally-biased T2I models, we found that there was a clear | |
relationship between $M_G$ and the extent of bias. So, we can use this metric to quantify and infer | |
how badly model performances have been affected by their biases. | |
""" | |
) | |
st.header('3. TBYB Constraints') | |
st.markdown( | |
""" | |
While we have attempted to design a comprehensive, automated bias evaluation tool. We must acknowledge that | |
in its infancy, TBYB has some constraints: | |
- We have not checked the validity of *every* single T2I model and model type on HuggingFace so we cannot | |
promise that all T2I models will work - if you run into any issues that you think should be possible, feel | |
free to reach out! | |
- Currently, a model_index.json file is required to load models and use them with TBYB, we will look to | |
address other models in future works | |
- TBYB only works on T2I models hosted on HuggingFace, other model repositories are not currently supported | |
- Adaptor models are currently not supported, we will look to add evaluation functionalities of these | |
models in the future. | |
- Download, generation, inference and evaluation times are all hardware dependent. | |
Keep in mind that these constraints may be removed or added to any time. | |
""") | |
st.header('4. Misuse, Malicious Use, and Out-of-Scope Use') | |
st.markdown( | |
""" | |
Given this application is used for the assessment of T2I biases and relies on | |
pre-trained models available on HuggingFace, we are not responsible for any content generated | |
by public-facing models that have been used to generate images using this application. | |
TBYB is proposed as an auxiliary tool to assess model biases and thus, if a chosen model is found to output | |
insensitive, disturbing, distressing or offensive images that propagate harmful stereotypes or | |
representations of marginalised groups, please address your concerns to the model providers. | |
However, given the TBYB tool is designed for bias quantification and is driven by transparency, it would be | |
beneficial to the TBYB community to share evaluations of biased T2I models! | |
We share no association with HuggingFace \U0001F917, we only use their services as a model repository, | |
given their growth in popularity in the computer science community recently. | |
For further questions/queries or if you want to simply strike a conversation, | |
please reach out to Jordan Vice at: jordan.vice@uwa.edu.au""") | |
setup_page_banner() | |
setup_how_to() | |
if user_login_create(): | |
tab1, tab2, tab3, tab4, tab5, tab6 = st.tabs(["\U0001F527 Setup", "\U0001F30E General Eval.", "\U0001F3AF Task-Oriented Eval.", | |
"\U0001F4CA Model Comparison", "\U0001F4C1 Generated Images", "\U0001F4F0 Additional Information"]) | |
setup_additional_information_tab(tab6) | |
# PLASTER THE LOGO EVERYWHERE | |
tab2.subheader("General Bias Evaluation") | |
tab2.write("Waiting for \U0001F527 Setup to be complete...") | |
tab3.subheader("Task-Oriented Bias Evaluation") | |
tab3.write("Waiting for \U0001F527 Setup to be complete...") | |
tab4.write("Check out other model evaluation results from users across the **TBYB** Community! \U0001F30E ") | |
tab4.write("You can also just compare your own model evaluations by clicking the '*Personal Evaluation*' buttons") | |
MCOMP.initialise_page(tab4) | |
tab5.subheader("Generated Images from General and Task-Oriented Bias Evaluations") | |
tab5.write("Waiting for \U0001F527 Setup to be complete...") | |
with tab1: | |
with st.form("model_definition_form", clear_on_submit=True): | |
modelID = st.text_input('Input the HuggingFace \U0001F917 T2I model_id for the model you ' | |
'want to analyse e.g.: "runwayml/stable-diffusion-v1-5"') | |
submitted1 = st.form_submit_button("Submit") | |
if modelID: | |
with st.spinner('Checking if ' + modelID + ' is valid and downloading it (if required)'): | |
modelLoaded = MLOAD.check_if_model_exists(modelID) | |
if modelLoaded is not None: | |
# st.write("Located " + modelID + " model_index.json file") | |
st.write("Located " + modelID) | |
modelType = MLOAD.get_model_info(modelLoaded) | |
if modelType is not None: | |
st.write("Model is of Type: ", modelType) | |
if submitted1: | |
MINFER.TargetModel = MLOAD.import_model(modelID, modelType) | |
if MINFER.TargetModel is not None: | |
st.write("Text-to-image pipeline looks like this:") | |
st.write(MINFER.TargetModel) | |
user_evaluation_variables.MODEL = modelID | |
user_evaluation_variables.MODEL_TYPE = modelType | |
else: | |
st.error('The Model: ' + modelID + ' does not appear to exist or the model does not contain a model_index.json file.' | |
' Please check that that HuggingFace repo ID is valid.' | |
' For more help, please see the "How to Use" Tab above.', icon="🚨") | |
if modelID: | |
with st.form("example_image_gen_form", clear_on_submit=True): | |
testPrompt = st.text_input('Input a random test prompt to test out your ' | |
'chosen model and see if its generating images:') | |
submitted2 = st.form_submit_button("Submit") | |
if testPrompt and submitted2: | |
with st.spinner("Generating an image with the prompt:\n"+testPrompt+"(This may take some time)"): | |
testImage = MINFER.generate_test_image(MINFER.TargetModel, testPrompt) | |
st.image(testImage, caption='Model: ' + modelID + ' Prompt: ' + testPrompt) | |
st.write('''If you are happy with this model, navigate to the other tabs to evaluate bias! | |
Otherwise, feel free to load up a different model and run it again''') | |
if MINFER.TargetModel is not None: | |
tab_manager.completed_setup([tab2, tab3, tab4, tab5], modelID) | |
else: | |
MCOMP.databaseDF = None | |
user_evaluation_variables.reset_variables('general') | |
user_evaluation_variables.reset_variables('task-oriented') | |
st.write('') | |
st.warning('Log in or register your email to get started! ', icon="⚠️") |