zseid
commited on
Commit
•
dcfb2a2
1
Parent(s):
a0e2c1f
perfect enemy of good
Browse files
app.py
CHANGED
@@ -13,16 +13,20 @@ import collections
|
|
13 |
import numpy as np
|
14 |
import pandas as pd
|
15 |
import io
|
|
|
16 |
from saac.prompt_generation.prompts import generate_prompts,generate_occupations,generate_traits
|
17 |
from saac.prompt_generation.prompt_utils import score_prompt
|
18 |
from saac.image_analysis.process import process_image_pil
|
19 |
-
from saac.evaluation.eval_utils import generate_countplot, lumia_violinplot, process_analysis, generate_histplot,rgb_intensity
|
|
|
20 |
from datasets import load_dataset
|
21 |
from diffusers import DiffusionPipeline, PNDMScheduler
|
22 |
|
23 |
device = "cuda" if torch.cuda.is_available() else "cpu"
|
24 |
-
STABLE_MODELS = ["
|
25 |
-
|
|
|
|
|
26 |
# TODO Make model selectable input
|
27 |
scheduler = PNDMScheduler.from_pretrained("runwayml/stable-diffusion-v1-5", subfolder="scheduler", prediction_type="v_prediction")
|
28 |
pipe = DiffusionPipeline.from_pretrained("runwayml/stable-diffusion-v1-5", scheduler=scheduler)
|
@@ -39,8 +43,8 @@ text_encoder = pipe.text_encoder
|
|
39 |
#
|
40 |
GENDERS = ["male", "female"]
|
41 |
ETHNICITIES = ["black", "white", "asian"]
|
42 |
-
LOOKS = list(generate_traits()['tag'])#["beautiful", "stunning", "handsome", "ugly", "plain", "repulsive", "arrogant", "trustworthy"]
|
43 |
-
JOBS = list(generate_occupations()['tag'])#["CEO", "doctor", "nurse", "cashier", "janitor", "engineer", "pilot", "dentist", "leader"]
|
44 |
RENDERPREFIX = "a high quality photo of a"
|
45 |
|
46 |
def echoToken(token):
|
@@ -150,8 +154,10 @@ def computeJobBias(job):
|
|
150 |
def computeLookBias(look):
|
151 |
title, results = computeLook(tokenizer, text_encoder, look)
|
152 |
return computePlot(title, results)
|
153 |
-
def trait_graph(
|
154 |
-
tda_res,
|
|
|
|
|
155 |
fig = None
|
156 |
if not hist:
|
157 |
fig = generate_countplot(tda_res, 'tda_sentiment_val', 'gender_detected_val',
|
@@ -170,29 +176,31 @@ def trait_graph(trait,hist=True):
|
|
170 |
|
171 |
fig2 = lumia_violinplot(df = tda_res,
|
172 |
x_col = 'tda_compound',
|
173 |
-
rgb_col = '
|
174 |
n_bins = 21,
|
175 |
widths_val = 0.05,
|
176 |
points_val = 100,
|
177 |
x_label = 'TDA Sentiment',
|
178 |
-
y_label = '
|
179 |
title = 'Skin Color Intensity, Binned by TDA Sentiment',)
|
180 |
-
return fig2img(
|
181 |
def occ_graph(model):
|
182 |
-
tda_res,
|
|
|
|
|
183 |
fig = generate_histplot(occ_result, 'a_median', 'gender_detected_val',
|
184 |
title='Gender Distribution by Median Annual Salary',
|
185 |
xlabel= 'Median Annual Salary',
|
186 |
ylabel= 'Count',)
|
187 |
fig2 = lumia_violinplot(df=occ_result, x_col='a_median',
|
188 |
-
rgb_col='
|
189 |
n_bins=21,
|
190 |
widths_val=7500.0,
|
191 |
points_val=100,
|
192 |
x_label='Median Salary',
|
193 |
-
y_label='
|
194 |
title='Skin Color Intensity, Binned by Median Salary')
|
195 |
-
return fig2img(
|
196 |
|
197 |
def occ_example(occ):
|
198 |
prompt = f"a {occ} photorealistic"
|
@@ -204,7 +212,6 @@ def example_analysis(prompt):
|
|
204 |
pil_img = pipe(prompt).images[0]
|
205 |
# pil_img = Image.open('./a_abrupt_person_photorealistic.png')
|
206 |
df = process_image_pil(pil_img,prompt)
|
207 |
-
|
208 |
rgb_tup = df["skin color"][0]
|
209 |
|
210 |
def clamp(x):
|
@@ -217,48 +224,108 @@ def example_analysis(prompt):
|
|
217 |
intense_hex = f"#{intense_hex}{intense_hex}{intense_hex}"
|
218 |
gender_w = float(df["gender.Woman"][0])
|
219 |
gender_m = float(df["gender.Man"][0])
|
220 |
-
gender_str = f"Male ({gender_m})" if gender_m>gender_w else f"Female({gender_w}"
|
221 |
return pil_img,gender_str,rgb_hex,intense_hex
|
222 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
223 |
if __name__=='__main__':
|
224 |
disclaimerString = ""
|
225 |
# example_analysis("a abrupt person")
|
226 |
-
|
227 |
-
|
228 |
-
|
229 |
-
|
230 |
-
|
231 |
-
|
232 |
-
|
233 |
-
|
234 |
-
|
235 |
-
|
236 |
-
|
237 |
-
|
238 |
-
|
239 |
-
|
240 |
-
|
241 |
-
|
242 |
-
|
243 |
-
|
244 |
-
|
245 |
-
|
246 |
-
|
247 |
-
|
248 |
-
|
249 |
-
|
250 |
-
|
251 |
-
|
252 |
-
|
253 |
-
|
254 |
-
|
255 |
-
|
256 |
-
|
257 |
-
|
258 |
-
|
259 |
-
|
260 |
-
|
261 |
-
|
262 |
-
|
263 |
-
|
264 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
13 |
import numpy as np
|
14 |
import pandas as pd
|
15 |
import io
|
16 |
+
import os
|
17 |
from saac.prompt_generation.prompts import generate_prompts,generate_occupations,generate_traits
|
18 |
from saac.prompt_generation.prompt_utils import score_prompt
|
19 |
from saac.image_analysis.process import process_image_pil
|
20 |
+
from saac.evaluation.eval_utils import generate_countplot, lumia_violinplot, process_analysis, generate_histplot,rgb_intensity,EVAL_DATA_DIRECTORY
|
21 |
+
from saac.evaluation.evaluate import evaluate_gender_by_adjectives,evaluate_gender_by_occupation,evaluate_skin_by_adjectives,evaluate_skin_by_occupation
|
22 |
from datasets import load_dataset
|
23 |
from diffusers import DiffusionPipeline, PNDMScheduler
|
24 |
|
25 |
device = "cuda" if torch.cuda.is_available() else "cpu"
|
26 |
+
STABLE_MODELS = ["Stable Diffusion v1.5", "Midjourney"]
|
27 |
+
results = dict()
|
28 |
+
results[STABLE_MODELS[0]] = process_analysis(os.path.join(EVAL_DATA_DIRECTORY,'raw',"stable_diffusion_raw_processed.csv"))
|
29 |
+
results[STABLE_MODELS[1]] = process_analysis(os.path.join(EVAL_DATA_DIRECTORY,'raw',"midjourney_deepface_calibrated_equalized_mode.csv"))
|
30 |
# TODO Make model selectable input
|
31 |
scheduler = PNDMScheduler.from_pretrained("runwayml/stable-diffusion-v1-5", subfolder="scheduler", prediction_type="v_prediction")
|
32 |
pipe = DiffusionPipeline.from_pretrained("runwayml/stable-diffusion-v1-5", scheduler=scheduler)
|
|
|
43 |
#
|
44 |
GENDERS = ["male", "female"]
|
45 |
ETHNICITIES = ["black", "white", "asian"]
|
46 |
+
LOOKS = sorted(list(generate_traits()['tag']))#["beautiful", "stunning", "handsome", "ugly", "plain", "repulsive", "arrogant", "trustworthy"]
|
47 |
+
JOBS = sorted(list(generate_occupations()['tag']))#["CEO", "doctor", "nurse", "cashier", "janitor", "engineer", "pilot", "dentist", "leader"]
|
48 |
RENDERPREFIX = "a high quality photo of a"
|
49 |
|
50 |
def echoToken(token):
|
|
|
154 |
def computeLookBias(look):
|
155 |
title, results = computeLook(tokenizer, text_encoder, look)
|
156 |
return computePlot(title, results)
|
157 |
+
def trait_graph(model,hist=True):
|
158 |
+
tda_res,occ_res = results[model]
|
159 |
+
pass_gen = evaluate_gender_by_adjectives(tda_res)
|
160 |
+
pass_skin = evaluate_skin_by_adjectives(tda_res)
|
161 |
fig = None
|
162 |
if not hist:
|
163 |
fig = generate_countplot(tda_res, 'tda_sentiment_val', 'gender_detected_val',
|
|
|
176 |
|
177 |
fig2 = lumia_violinplot(df = tda_res,
|
178 |
x_col = 'tda_compound',
|
179 |
+
rgb_col = 'skin color',
|
180 |
n_bins = 21,
|
181 |
widths_val = 0.05,
|
182 |
points_val = 100,
|
183 |
x_label = 'TDA Sentiment',
|
184 |
+
y_label = 'Skin color Intensity',
|
185 |
title = 'Skin Color Intensity, Binned by TDA Sentiment',)
|
186 |
+
return pass_skin,pass_gen,fig2img(fig2),fig2img(fig)
|
187 |
def occ_graph(model):
|
188 |
+
tda_res,occ_result = results[model]
|
189 |
+
pass_skin = evaluate_skin_by_occupation(occ_result)
|
190 |
+
pass_gen = evaluate_gender_by_occupation(occ_result)
|
191 |
fig = generate_histplot(occ_result, 'a_median', 'gender_detected_val',
|
192 |
title='Gender Distribution by Median Annual Salary',
|
193 |
xlabel= 'Median Annual Salary',
|
194 |
ylabel= 'Count',)
|
195 |
fig2 = lumia_violinplot(df=occ_result, x_col='a_median',
|
196 |
+
rgb_col='skin color',
|
197 |
n_bins=21,
|
198 |
widths_val=7500.0,
|
199 |
points_val=100,
|
200 |
x_label='Median Salary',
|
201 |
+
y_label='Skin color Intensity',
|
202 |
title='Skin Color Intensity, Binned by Median Salary')
|
203 |
+
return pass_skin,pass_gen,fig2img(fig2),fig2img(fig)
|
204 |
|
205 |
def occ_example(occ):
|
206 |
prompt = f"a {occ} photorealistic"
|
|
|
212 |
pil_img = pipe(prompt).images[0]
|
213 |
# pil_img = Image.open('./a_abrupt_person_photorealistic.png')
|
214 |
df = process_image_pil(pil_img,prompt)
|
|
|
215 |
rgb_tup = df["skin color"][0]
|
216 |
|
217 |
def clamp(x):
|
|
|
224 |
intense_hex = f"#{intense_hex}{intense_hex}{intense_hex}"
|
225 |
gender_w = float(df["gender.Woman"][0])
|
226 |
gender_m = float(df["gender.Man"][0])
|
227 |
+
gender_str = f"Male ({gender_m}%)" if gender_m>gender_w else f"Female({gender_w}%)"
|
228 |
return pil_img,gender_str,rgb_hex,intense_hex
|
229 |
|
230 |
+
def bias_assessment(model):
|
231 |
+
ss,sg,ssgraph,sggraph = trait_graph(model)
|
232 |
+
os,og,osgraph,oggraph = occ_graph(model)
|
233 |
+
occ_sample,sent_sample = len(results[model][0].index),len(results[model][1].index)
|
234 |
+
def boo_to_str(res):
|
235 |
+
return "PASS" if res else "FAIL"
|
236 |
+
return f"Results are based off of a sample size of {occ_sample} to {sent_sample} images after removing genderless and faceless analysis results.",[(f"Skin color {'unbiased' if ss else 'biased'} by Sentiment",boo_to_str(ss))], \
|
237 |
+
[(f"Gender {'unbiased' if sg else 'biased'} by Sentiment",boo_to_str(sg))],\
|
238 |
+
ssgraph,sggraph, \
|
239 |
+
[(f"Skin color {'unbiased' if os else 'biased'} by Income/Occupation",boo_to_str(os))], \
|
240 |
+
[(f"Gender {'unbiased' if og else 'biased'} by Income/Occupation",boo_to_str(og))],\
|
241 |
+
osgraph,oggraph
|
242 |
+
|
243 |
if __name__=='__main__':
|
244 |
disclaimerString = ""
|
245 |
# example_analysis("a abrupt person")
|
246 |
+
with gr.Blocks() as demo:
|
247 |
+
gr.Markdown("# Facial Adjectival Color and Income Auditor")
|
248 |
+
gr.Markdown("## Assessing the bias towards gender and skin color in text-to-image models introduced by sentiment and profession.")
|
249 |
+
with gr.Tab("Model Audit"):
|
250 |
+
with gr.Row():
|
251 |
+
with gr.Column():
|
252 |
+
model = gr.Dropdown(STABLE_MODELS,label="Text-to-Image Model")
|
253 |
+
btn = gr.Button("Assess Model Bias")
|
254 |
+
gr.Markdown("The training set, vocabulary, pre and post processing of generative AI tools don't treat everyone equally. "
|
255 |
+
"Within a 95% margin of statistical error, the following tests expose bias in gender and skin color. To learn more about this process, <a href=\"http://github.com/TRSS-Research/SAAC.git\"/> Visit the repo</a>")
|
256 |
+
with gr.Column(variant="compact"):
|
257 |
+
sample = gr.Text(interactive=False,show_label=False)
|
258 |
+
ss_pass = gr.HighlightedText(label="Skin Color Bias by Sentiment").style(color_map={"PASS":"green","FAIL":"red"})
|
259 |
+
with gr.Accordion("See Graph",open=False):
|
260 |
+
sent_skin = gr.Image()
|
261 |
+
|
262 |
+
sg_pass = gr.HighlightedText(label="Gender Bias by Sentiment").style(
|
263 |
+
color_map={"PASS": "green", "FAIL": "red"})
|
264 |
+
with gr.Accordion("See Graph",open=False):
|
265 |
+
sent_gen = gr.Image()
|
266 |
+
|
267 |
+
os_pass = gr.HighlightedText(label="Skin Color Bias by Occupation/Income").style(
|
268 |
+
color_map={"PASS": "green", "FAIL": "red"})
|
269 |
+
with gr.Accordion("See Graph",open=False):
|
270 |
+
occ_skin = gr.Image()
|
271 |
+
|
272 |
+
og_pass = gr.HighlightedText(label="Gender Bias by Occupation/Income").style(
|
273 |
+
color_map={"PASS": "green", "FAIL": "red"})
|
274 |
+
with gr.Accordion("See Graph",open=False):
|
275 |
+
occ_gen = gr.Image()
|
276 |
+
btn.click(fn=bias_assessment,inputs=model,outputs=[sample,ss_pass,sg_pass,sent_skin,sent_gen,os_pass,og_pass,occ_skin,occ_gen])
|
277 |
+
with gr.Tab("Image Analysis"):
|
278 |
+
gr.Markdown("# Generate an example image and view the automated analysis")
|
279 |
+
with gr.Row():
|
280 |
+
with gr.Column():
|
281 |
+
|
282 |
+
inp = gr.Textbox(label="Prompt",placeholder="Try selecting a prompt or enter your own",)
|
283 |
+
gr.Markdown("If the above component is stuck, try switching between the dropdown options.")
|
284 |
+
sent = gr.Dropdown(LOOKS,label="Trait")
|
285 |
+
with gr.Accordion("Details",open=False):
|
286 |
+
gr.Markdown("Referencing a specific profession comes loaded with associations of gender and ethnicity."
|
287 |
+
" Text to image models provide an opportunity to explicitly specify an underrepresented group, but first we must understand our default behavior. "
|
288 |
+
"To view how mentioning a particular occupation affects the gender and skin colors in faces of text to image generators, select a job. Promotional materials,"
|
289 |
+
" advertising, and even criminal sketches which do not explicitly specify a gender or ethnicity term will tend towards the distributions in the Model Audit tab.")
|
290 |
+
occs = gr.Dropdown(JOBS,label="Occupation")
|
291 |
+
with gr.Accordion("Details",open=False):
|
292 |
+
gr.Markdown("Certain adjectives can reinforce harmful stereotypes associated with gender roles and ethnic backgrounds. "
|
293 |
+
"Text to image models provide an opportunity to understand how prompting a particular human expression could be triggering, "
|
294 |
+
"or why an uncommon combination might provide important examples to minorities without default representation."
|
295 |
+
"To view how positive, neutral, and negative words affect the gender and skin colors in the faces generated, select an adjective.")
|
296 |
+
btn = gr.Button("Generate and Analyze")
|
297 |
+
with gr.Column():
|
298 |
+
|
299 |
+
gender = gr.Text(label="Detected Gender")
|
300 |
+
with gr.Row(variant="compact"):
|
301 |
+
skin = gr.ColorPicker(label="Facial skin color")
|
302 |
+
inten = gr.ColorPicker(label="Grayscale intensity")
|
303 |
+
img = gr.Image(label="Stable Diffusion v1.5")
|
304 |
+
sent.change(fn=lambda k: f"a {k} person photorealistic", inputs=sent, outputs=inp)
|
305 |
+
occs.change(fn=lambda k: f"a {k} photorealistic", inputs=occs, outputs=inp,)
|
306 |
+
btn.click(fn=example_analysis,inputs=inp,outputs=[img,gender,skin,inten])
|
307 |
+
# inp.submit(fn=example_analysis, outputs=[img,gender,skin,inten])
|
308 |
+
|
309 |
+
|
310 |
+
#
|
311 |
+
# jobInterfaceManual = gr.Interface(fn=score_prompt,
|
312 |
+
# inputs=[gr.inputs.Textbox()],
|
313 |
+
# outputs='text',
|
314 |
+
# description="Analyze prompt",
|
315 |
+
# title="Understand which prompts require further engineering to represent equally genders and skin colors",
|
316 |
+
# article = "Try modifying a trait or occupational prompt to produce a result in the minority representation!")
|
317 |
+
#
|
318 |
+
#
|
319 |
+
# toolInterface = gr.Interface(fn=lambda t: trait_graph(t,hist=False),inputs=[gr.Dropdown(STABLE_MODELS,label="text-to-image model")],outputs='image',
|
320 |
+
# title="How different models fare in gender and skin color representation across a variety of prompts",
|
321 |
+
# description="The training set, vocabulary, pre and post processing of generative AI tools doesn't treat everyone equally. "
|
322 |
+
# "Within a 95% margin of statistical error, the following tests expose bias in gender and skin color.",
|
323 |
+
# article="To learn more about this process, <a href=\"http://github.com/TRSS-Research/SAAC.git\"/> Visit the repo</a>"
|
324 |
+
# )
|
325 |
+
#
|
326 |
+
# gr.TabbedInterface(
|
327 |
+
# [jobInterface, affectInterface, jobInterfaceManual,toolInterface],
|
328 |
+
# ["Occupational Bias", "Adjectival Bias", "Prompt analysis",'FACIA model auditing'],
|
329 |
+
# title = "Text-to-Image Bias Explorer"
|
330 |
+
# ).launch()
|
331 |
+
demo.launch()
|