מחקר מקרה לתיקון מודל

במחברת זו, נאמן מסווג טקסט לזהות תוכן כתוב שעלול להיחשב רעיל או מזיק, ונחיל את MinDiff כדי לתקן כמה חששות מהוגנות. בתהליך העבודה שלנו, אנו:

  1. הערך את הביצועים של מודל הבסיס שלנו בטקסט המכיל הפניות לקבוצות רגישות.
  2. שפר את הביצועים בכל קבוצה עם ביצועים נמוכים על ידי אימון עם MinDiff.
  3. הערך את הביצועים של המודל החדש לפי המדד שבחרת.

המטרה שלנו היא להדגים את השימוש בטכניקת MinDiff עם זרימת עבודה מינימלית ביותר, לא להגדיר גישה עקרונית להוגנות בלמידת מכונה. לפיכך, ההערכה שלנו תתמקד רק בקטגוריה רגישה אחת ובמדד בודד. אנחנו גם לא מתייחסים לחסרונות פוטנציאליים במערך הנתונים, ולא מכוונים את התצורות שלנו. במסגרת הפקה, תרצה לגשת לכל אחד מהם בקפדנות. לקבלת מידע נוסף על הערכה לשנת הגינות, לראות את המדריך הזה .

להכין

אנו מתחילים בהתקנת אינדיקטורים להגינות ותיקון מודל TensorFlow.

מתקין

ייבא את כל הרכיבים הדרושים, כולל MinDiff ו- Fairness Indicators להערכה.

יבוא

אנו משתמשים בפונקציית עזר כדי להוריד את הנתונים המעובדים מראש ולהכין את התוויות כך שיתאימו לצורת הפלט של הדגם. הפונקציה גם מורידה את הנתונים כ-TFRecords כדי להפוך את ההערכה מאוחרת יותר למהירה יותר. לחלופין, אתה יכול להמיר את Pandas DataFrame ל-TFRecords עם כל פונקציית המרת כלי עזר זמינה.

# We use a helper utility to preprocessed data for convenience and speed.
data_train, data_validate, validate_tfrecord_file, labels_train, labels_validate = min_diff_keras_utils.download_and_process_civil_comments_data()
Downloading data from https://storage.googleapis.com/civil_comments_dataset/train_df_processed.csv
345702400/345699197 [==============================] - 8s 0us/step
Downloading data from https://storage.googleapis.com/civil_comments_dataset/validate_df_processed.csv
229974016/229970098 [==============================] - 5s 0us/step
Downloading data from https://storage.googleapis.com/civil_comments_dataset/validate_tf_processed.tfrecord
324943872/324941336 [==============================] - 9s 0us/step

אנו מגדירים כמה קבועים שימושיים. אנחנו נכשיר את המודל על 'comment_text' התכונה, עם תווית היעד שלנו כמו 'toxicity' . שים לב שגודל האצווה כאן נבחר באופן שרירותי, אבל בהגדרת ייצור תצטרך לכוון אותו לביצועים הטובים ביותר.

TEXT_FEATURE = 'comment_text'
LABEL = 'toxicity'
BATCH_SIZE = 512

הגדר זרעים אקראיים. (שים לב שזה לא מייצב את התוצאות באופן מלא.)

זרעים

הגדירו והכשירו את מודל הבסיס

כדי להפחית את זמן הריצה, אנו משתמשים במודל מיומן מראש כברירת מחדל. זהו מודל רציף פשוט של Keras עם שכבות הטבעה ראשוניות וקונבולוציה, המפיק תחזית רעילות. אם אתה מעדיף, תוכל לשנות זאת ולהתאמן מאפס באמצעות פונקציית השירות שלנו ליצירת המודל. (שים לב שמכיוון שסביר להניח שהסביבה שלך שונה משלנו, תצטרך להתאים אישית את ספי הכוונון וההערכה.)

use_pretrained_model = True

if use_pretrained_model:
  URL = 'https://storage.googleapis.com/civil_comments_model/baseline_model.zip'
  BASE_PATH = tempfile.mkdtemp()
  ZIP_PATH = os.path.join(BASE_PATH, 'baseline_model.zip')
  MODEL_PATH = os.path.join(BASE_PATH, 'tmp/baseline_model')

  r = requests.get(URL, allow_redirects=True)
  open(ZIP_PATH, 'wb').write(r.content)

  with zipfile.ZipFile(ZIP_PATH, 'r') as zip_ref:
    zip_ref.extractall(BASE_PATH)
  baseline_model = tf.keras.models.load_model(
      MODEL_PATH, custom_objects={'KerasLayer' : hub.KerasLayer})
else:
  optimizer = tf.keras.optimizers.Adam(learning_rate=0.001)
  loss = tf.keras.losses.BinaryCrossentropy()

  baseline_model = min_diff_keras_utils.create_keras_sequential_model()

  baseline_model.compile(optimizer=optimizer, loss=loss, metrics=['accuracy'])

  baseline_model.fit(x=data_train[TEXT_FEATURE],
                     y=labels_train,
                     batch_size=BATCH_SIZE,
                     epochs=20)

אנחנו חוסכים את המודל כדי להעריך באמצעות אינדיקטורים גינות .

base_dir = tempfile.mkdtemp(prefix='saved_models')
baseline_model_location = os.path.join(base_dir, 'model_export_baseline')
baseline_model.save(baseline_model_location, save_format='tf')
INFO:tensorflow:Assets written to: /tmp/saved_models867b8d74/model_export_baseline/assets
INFO:tensorflow:Assets written to: /tmp/saved_models867b8d74/model_export_baseline/assets

בשלב הבא אנו מפעילים אינדיקטורים להוגנות. כתזכורת, אנחנו פשוט הולכים לבצע הערכה פרוס להערות התייחסות קטגוריה אחת, קבוצות דתיות. בסביבת ייצור, אנו ממליצים לנקוט בגישה מתחשבת כדי לקבוע אילו קטגוריות ומדדים להעריך.

כדי לחשב את ביצועי המודל, פונקציית השירות עושה כמה אפשרויות נוחות עבור מדדים, פרוסות וספי סיווג.

# We use a helper utility to hide the evaluation logic for readability.
base_dir = tempfile.mkdtemp(prefix='eval')
eval_dir = os.path.join(base_dir, 'tfma_eval_result')
eval_result = fi_util.get_eval_results(
    baseline_model_location, eval_dir, validate_tfrecord_file)
WARNING:absl:Tensorflow version (2.5.0) found. Note that TFMA support for TF 2.0 is currently in beta
WARNING:apache_beam.runners.interactive.interactive_environment:Dependencies required for Interactive Beam PCollection visualization are not available, please use: `pip install apache-beam[interactive]` to install necessary dependencies to enable all data visualization features.
WARNING:apache_beam.io.tfrecordio:Couldn't find python-snappy so the implementation of _TFRecordUtil._masked_crc32c is not as fast as it could be.
WARNING:tensorflow:From /tmpfs/src/tf_docs_env/lib/python3.7/site-packages/tensorflow_model_analysis/writers/metrics_plots_and_validations_writer.py:113: tf_record_iterator (from tensorflow.python.lib.io.tf_record) is deprecated and will be removed in a future version.
Instructions for updating:
Use eager execution and: 
`tf.data.TFRecordDataset(path)`
WARNING:tensorflow:From /tmpfs/src/tf_docs_env/lib/python3.7/site-packages/tensorflow_model_analysis/writers/metrics_plots_and_validations_writer.py:113: tf_record_iterator (from tensorflow.python.lib.io.tf_record) is deprecated and will be removed in a future version.
Instructions for updating:
Use eager execution and: 
`tf.data.TFRecordDataset(path)`

עיבוד תוצאות הערכה

widget_view.render_fairness_indicator(eval_result)
FairnessIndicatorViewer(slicingMetrics=[{'sliceValue': 'Overall', 'slice': 'Overall', 'metrics': {'accuracy': …

בואו נסתכל על תוצאות ההערכה. נסה לבחור את שיעור חיובי כוזב (FPR) המטרי עם סף 0.450. אנו יכולים לראות שהמודל אינו מתפקד טוב עבור קבוצות דתיות מסוימות כמו עבור אחרות, ומציג FPR גבוה בהרבה. שימו לב למרווחי הסמך הרחבים בקבוצות מסוימות מכיוון שיש להן מעט מדי דוגמאות. זה מקשה לומר בוודאות שיש הבדל משמעותי בביצועים עבור הפרוסות הללו. אולי נרצה לאסוף דוגמאות נוספות כדי לטפל בבעיה זו. עם זאת, אנו יכולים לנסות ליישם את MinDiff עבור שתי הקבוצות שאנו בטוחים שהביצועים שלהן לא טובים.

בחרנו להתמקד ב-FPR, מכיוון ש-FPR גבוה יותר פירושו שלהערות המתייחסות לקבוצות זהות אלו יש סיכוי גבוה יותר להיות מסומן באופן שגוי כרעיל מאשר תגובות אחרות. זה עלול להוביל לתוצאות לא שוויוניות עבור משתמשים העוסקים בדיאלוג על דת, אך שימו לב שפערים במדדים אחרים יכולים להוביל לסוגים אחרים של נזק.

הגדר ואמן את מודל MinDiff

כעת, ננסה לשפר את ה-FPR עבור קבוצות דתיות עם ביצועים נמוכים. אנחנו ננסה לעשות זאת באמצעות MinDiff , טכניקת תיקון המבקשת לאזן שיעורי שגיאה פני פרוסות של נתון על ידי מעניש פערים בביצועים במהלך אימון. כאשר אנו מיישמים MinDiff, ביצועי הדגם עשויים לרדת מעט על פרוסות אחרות. ככזה, היעדים שלנו עם MinDiff יהיו:

  • ביצועים משופרים עבור קבוצות עם ביצועים נמוכים
  • השפלה מוגבלת עבור קבוצות אחרות וביצועים כלליים

הכן את הנתונים שלך

כדי להשתמש ב-MinDiff, אנו יוצרים שני פיצולי נתונים נוספים:

  • פיצול לדוגמאות שאינן רעילות המתייחסות לקבוצות מיעוט: במקרה שלנו, זה יכלול הערות עם הפניות למונחי הזהות החסרים שלנו. אנחנו לא כוללים חלק מהקבוצות כי יש מעט מדי דוגמאות, מה שמוביל לאי ודאות גבוהה יותר עם טווחי רווחי סמך רחבים.
  • פיצול לדוגמאות לא רעילות המתייחסות לקבוצת הרוב.

חשוב שיהיו מספיק דוגמאות השייכות לשיעורים עם ביצועים נמוכים. בהתבסס על ארכיטקטורת המודל, הפצת הנתונים ותצורת MinDiff, כמות הנתונים הדרושה יכולה להשתנות באופן משמעותי. ביישומים קודמים, ראינו את MinDiff עובד היטב עם 5,000 דוגמאות בכל פיצול נתונים.

במקרה שלנו, לקבוצות בפיצולי המיעוט יש כמויות לדוגמה של 9,688 ו-3,906. שימו לב לחוסר האיזון במחלקה במערך הנתונים; בפועל, זה יכול להוות סיבה לדאגה, אבל לא ננסה להתייחס אליהם במחברת זו מכיוון שהכוונה שלנו היא רק להדגים את MinDiff.

אנו בוחרים רק דוגמאות שליליות עבור קבוצות אלה, כדי ש-MinDiff תוכל לבצע אופטימיזציה כדי לקבל את הדוגמאות הללו בצורה נכונה. זה אולי נראה מנוגד לסלול את סטים של דוגמאות שליליות אמת קרקע אם אנחנו מודאגים בעיקר עם פערי שיעור חיובי כוזב, אך יש לזכור כי תחזית חיובית כוזבת היא דוגמא קרקע אמת שלילית זה סווג בטעות כדואר חיובי, שהיא הסוגיה שאנו מנסים להתייחס.

צור MinDiff DataFrames

# Create masks for the sensitive and nonsensitive groups
minority_mask = data_train.religion.apply(
    lambda x: any(religion in x for religion in ('jewish', 'muslim')))
majority_mask = data_train.religion.apply(lambda x: x == "['christian']")

# Select nontoxic examples, so MinDiff will be able to reduce sensitive FP rate.
true_negative_mask = data_train['toxicity'] == 0

data_train_main = copy.copy(data_train)
data_train_sensitive = data_train[minority_mask & true_negative_mask]
data_train_nonsensitive = data_train[majority_mask & true_negative_mask]

אנחנו גם צריכים להמיר את Pandas DataFrames שלנו ל- Tensorflow Datasets עבור קלט MinDiff. שים לב שבניגוד ל-API של מודל Keras עבור Pandas DataFrames, שימוש במערכים נתונים אומר שעלינו לספק את תכונות הקלט והתוויות של המודל יחד במערך נתונים אחד. כאן אנו מספקים את 'comment_text' כמאפיין קלט ולעצב מחדש את התווית כדי להתאים את התפוקה הצפויה של הדגם.

אנו מאגדים את מערך הנתונים גם בשלב זה, מכיוון ש-MinDiff דורש מערכי נתונים מצטברים. שים לב שאנו מכוונים את בחירת גודל האצווה באותו אופן שהוא מכוון למודל הבסיסי, תוך התחשבות בשיקולי מהירות האימון וחומרה תוך איזון עם ביצועי הדגם. כאן בחרנו באותו גודל אצווה עבור כל שלושת מערכי הנתונים, אך זו אינה דרישה, אם כי נוהג טוב ששני גדלי האצווה של MinDiff יהיו שווים.

צור מערכי נתונים של MinDiff

# Convert the pandas DataFrames to Datasets.
dataset_train_main = tf.data.Dataset.from_tensor_slices(
    (data_train_main['comment_text'].values, 
     data_train_main.pop(LABEL).values.reshape(-1,1) * 1.0)).batch(BATCH_SIZE)
dataset_train_sensitive = tf.data.Dataset.from_tensor_slices(
    (data_train_sensitive['comment_text'].values, 
     data_train_sensitive.pop(LABEL).values.reshape(-1,1) * 1.0)).batch(BATCH_SIZE)
dataset_train_nonsensitive = tf.data.Dataset.from_tensor_slices(
    (data_train_nonsensitive['comment_text'].values, 
     data_train_nonsensitive.pop(LABEL).values.reshape(-1,1) * 1.0)).batch(BATCH_SIZE)

אימון והערכת המודל

להתאמן עם MinDiff, פשוט לקחת את המודל המקורי לעטוף אותו MinDiffModel עם מקביל loss ו loss_weight . אנחנו משתמשים 1.5 כברירת המחדל loss_weight , אבל זה פרמטר שצריך מכוונים עבור המקרה שלכם, שכן הוא תלוי בדרישות מודל המוצר שלך. אתה יכול להתנסות בשינוי הערך כדי לראות כיצד הוא משפיע על המודל, שים לב שהגדלתו מקרבת את הביצועים של קבוצות המיעוט והרוב, אך עשויה לבוא עם פשרות בולטות יותר.

לאחר מכן אנו מרכיבים את המודל באופן רגיל (באמצעות אובדן רגיל שאינו MinDiff) ומתאים לאימון.

רכבת MinDiffModel

use_pretrained_model = True

base_dir = tempfile.mkdtemp(prefix='saved_models')
min_diff_model_location = os.path.join(base_dir, 'model_export_min_diff')

if use_pretrained_model:
  BASE_MIN_DIFF_PATH = tempfile.mkdtemp()
  MIN_DIFF_URL = 'https://storage.googleapis.com/civil_comments_model/min_diff_model.zip'
  ZIP_PATH = os.path.join(BASE_PATH, 'min_diff_model.zip')
  MIN_DIFF_MODEL_PATH = os.path.join(BASE_MIN_DIFF_PATH, 'tmp/min_diff_model')
  DIRPATH = '/tmp/min_diff_model'

  r = requests.get(MIN_DIFF_URL, allow_redirects=True)
  open(ZIP_PATH, 'wb').write(r.content)

  with zipfile.ZipFile(ZIP_PATH, 'r') as zip_ref:
    zip_ref.extractall(BASE_MIN_DIFF_PATH)
  min_diff_model = tf.keras.models.load_model(
      MIN_DIFF_MODEL_PATH, custom_objects={'KerasLayer' : hub.KerasLayer})

  min_diff_model.save(min_diff_model_location, save_format='tf')

else:
  min_diff_weight = 1.5

  # Create the dataset that will be passed to the MinDiffModel during training.
  dataset = md.keras.utils.input_utils.pack_min_diff_data(
      dataset_train_main, dataset_train_sensitive, dataset_train_nonsensitive)

  # Create the original model.
  original_model = min_diff_keras_utils.create_keras_sequential_model()

  # Wrap the original model in a MinDiffModel, passing in one of the MinDiff
  # losses and using the set loss_weight.
  min_diff_loss = md.losses.MMDLoss()
  min_diff_model = md.keras.MinDiffModel(original_model,
                                         min_diff_loss,
                                         min_diff_weight)

  # Compile the model normally after wrapping the original model.  Note that
  # this means we use the baseline's model's loss here.
  optimizer = tf.keras.optimizers.Adam(learning_rate=0.001)
  loss = tf.keras.losses.BinaryCrossentropy()
  min_diff_model.compile(optimizer=optimizer, loss=loss, metrics=['accuracy'])

  min_diff_model.fit(dataset, epochs=20)

  min_diff_model.save_original_model(min_diff_model_location, save_format='tf')
INFO:tensorflow:Assets written to: /tmp/saved_modelsb3zkcos_/model_export_min_diff/assets
INFO:tensorflow:Assets written to: /tmp/saved_modelsb3zkcos_/model_export_min_diff/assets

לאחר מכן אנו מעריכים את התוצאות.

min_diff_eval_subdir = os.path.join(base_dir, 'tfma_eval_result')
min_diff_eval_result = fi_util.get_eval_results(
    min_diff_model_location,
    min_diff_eval_subdir,
    validate_tfrecord_file,
    slice_selection='religion')
WARNING:absl:Tensorflow version (2.5.0) found. Note that TFMA support for TF 2.0 is currently in beta

כדי להבטיח שאנו מעריכים מודל חדש בצורה נכונה, עלינו לבחור סף באותו אופן שבו היינו מבצעים את המודל הבסיסי. במסגרת ייצור, המשמעות היא להבטיח שמדדי הערכה עומדים בתקני ההשקה. במקרה שלנו, נבחר את הסף שמביא ל-FPR כולל דומה למודל הבסיס. סף זה עשוי להיות שונה מזה שבחרת עבור מודל הבסיס. נסה לבחור שיעור חיובי שגוי עם סף 0.400. (שים לב שלתת הקבוצות עם דוגמאות בכמות נמוכה מאוד יש מרווחי טווח סמך רחב מאוד ואין להן תוצאות צפויות).

widget_view.render_fairness_indicator(min_diff_eval_result)
FairnessIndicatorViewer(slicingMetrics=[{'sliceValue': 'Overall', 'slice': 'Overall', 'metrics': {'accuracy': …

לאחר סקירת תוצאות אלו, ייתכן שתבחין כי ה-FPRs עבור קבוצות היעד שלנו השתפרו. הפער בין קבוצת הביצועים הנמוכים ביותר שלנו לקבוצת הרוב השתפר מ-0.024 ל-0.006. בהתחשב בשיפורים שצפינו ובהמשך הביצועים החזקים של קבוצת הרוב, השגנו את שתי המטרות שלנו. בהתאם למוצר, ייתכן שיהיה צורך בשיפורים נוספים, אך גישה זו קירבה את המודל שלנו צעד אחד יותר לביצועים הוגנים עבור כל המשתמשים.