กรณีศึกษาการแก้ไขแบบจำลอง

ในสมุดบันทึกนี้ เราจะฝึกตัวแยกประเภทข้อความเพื่อระบุเนื้อหาที่เป็นลายลักษณ์อักษรที่อาจถือว่าเป็นพิษหรือเป็นอันตราย และใช้ MinDiff เพื่อแก้ไขข้อกังวลด้านความเป็นธรรมบางประการ ในขั้นตอนการทำงานของเรา เราจะ:

  1. ประเมินประสิทธิภาพของโมเดลพื้นฐานของเราในข้อความที่มีการอ้างอิงถึงกลุ่มที่มีความละเอียดอ่อน
  2. ปรับปรุงประสิทธิภาพในกลุ่มที่มีประสิทธิภาพต่ำโดยการฝึกอบรมกับ MinDiff
  3. ประเมินประสิทธิภาพของโมเดลใหม่กับเมตริกที่เราเลือก

จุดประสงค์ของเราคือเพื่อสาธิตการใช้เทคนิค MinDiff กับขั้นตอนการทำงานที่น้อยที่สุด ไม่ใช่เพื่อวางแนวทางหลักเพื่อความเป็นธรรมในการเรียนรู้ของเครื่อง ด้วยเหตุนี้ การประเมินของเราจะเน้นที่หมวดหมู่ที่ละเอียดอ่อนเพียงหมวดหมู่เดียวและตัวชี้วัดเดียวเท่านั้น เราไม่แก้ไขข้อบกพร่องที่อาจเกิดขึ้นในชุดข้อมูล หรือปรับแต่งการกำหนดค่าของเรา ในสภาพแวดล้อมการผลิต คุณจะต้องเข้าหาสิ่งเหล่านี้อย่างเข้มงวด สำหรับข้อมูลเพิ่มเติมเกี่ยวกับการประเมินผลเพื่อความเป็นธรรมให้ดูที่ คำแนะนำนี้

ติดตั้ง

เราเริ่มต้นด้วยการติดตั้ง Fairness Indicators และ TensorFlow Model Remediation

การติดตั้ง

นำเข้าส่วนประกอบที่จำเป็นทั้งหมด รวมถึง MinDiff และตัวบ่งชี้ความเป็นธรรมเพื่อการประเมิน

นำเข้า

เราใช้ฟังก์ชันยูทิลิตี้เพื่อดาวน์โหลดข้อมูลที่ประมวลผลล่วงหน้าและเตรียมฉลากให้ตรงกับรูปร่างเอาต์พุตของโมเดล ฟังก์ชันนี้ยังดาวน์โหลดข้อมูลเป็น 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 สามารถเพิ่มประสิทธิภาพในการทำให้ตัวอย่างเหล่านี้ถูกต้อง มันอาจจะดูเหมือน counterintuitive ที่จะตัดออกชุดตัวอย่างเชิงลบความจริงพื้นดินถ้าเรากำลังกังวลเกี่ยวเนื่องกับความแตกต่างในอัตราบวกปลอม แต่จำไว้ว่าการคาดการณ์ในเชิงบวกเท็จเป็นตัวอย่างจริงพื้นดินเชิงลบที่จัดไม่ถูกต้องเป็นบวกซึ่งเป็นปัญหาที่เรา กำลังพยายามที่จะอยู่

สร้าง 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 สำหรับอินพุต MinDiff โปรดทราบว่าไม่เหมือนกับ Keras model API สำหรับ 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': …

การตรวจสอบผลลัพธ์เหล่านี้ คุณอาจสังเกตเห็นว่า FPR สำหรับกลุ่มเป้าหมายของเรามีการปรับปรุง ช่องว่างระหว่างกลุ่มที่มีประสิทธิภาพต่ำที่สุดของเรากับกลุ่มส่วนใหญ่ดีขึ้นจาก .024 เป็น .006 จากการปรับปรุงที่เราสังเกตเห็นและผลงานที่แข็งแกร่งอย่างต่อเนื่องสำหรับกลุ่มคนส่วนใหญ่ เราจึงบรรลุเป้าหมายทั้งสองของเรา ทั้งนี้ขึ้นอยู่กับผลิตภัณฑ์ การปรับปรุงเพิ่มเติมอาจมีความจำเป็น แต่วิธีการนี้ทำให้โมเดลของเราเข้าใกล้การทำงานอย่างเท่าเทียมกันสำหรับผู้ใช้ทั้งหมดอีกขั้นหนึ่ง