在這個筆記本中,我們將訓練文字分類器來識別可能被視為有害或有害的書面內容,並應用 MinDiff 來補救一些公平性問題。在我們的工作流程中,我們將
- 評估基準模型在包含敏感群體參考資料的文字上的效能。
- 透過使用 MinDiff 進行訓練,改善任何表現不佳群體的效能。
- 評估新模型在我們選擇的指標上的效能。
我們的目的是示範 MinDiff 技術的用法,並採用非常精簡的工作流程,而不是闡述機器學習中處理公平性的原則性方法。因此,我們的評估將僅關注一個敏感類別和單一指標。我們也不會處理資料集中的潛在缺點,也不會調整我們的設定。在生產環境中,您會希望嚴謹地處理這些問題。如需評估公平性的更多資訊,請參閱本指南。
設定
我們先安裝 Fairness Indicators 和 TensorFlow Model Remediation。
安裝
匯入所有必要元件,包括 MinDiff 和用於評估的 Fairness Indicators。
匯入
2024-04-27 09:16:08.097342: E external/local_xla/xla/stream_executor/cuda/cuda_dnn.cc:9261] Unable to register cuDNN factory: Attempting to register factory for plugin cuDNN when one has already been registered 2024-04-27 09:16:08.097391: E external/local_xla/xla/stream_executor/cuda/cuda_fft.cc:607] Unable to register cuFFT factory: Attempting to register factory for plugin cuFFT when one has already been registered 2024-04-27 09:16:08.098819: E external/local_xla/xla/stream_executor/cuda/cuda_blas.cc:1515] Unable to register cuBLAS factory: Attempting to register factory for plugin cuBLAS when one has already been registered
我們使用公用程式函式下載預先處理的資料,並準備標籤以符合模型的輸出形狀。該函式也會將資料下載為 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 345699197/345699197 [==============================] - 4s 0us/step Downloading data from https://storage.googleapis.com/civil_comments_dataset/validate_df_processed.csv 229970098/229970098 [==============================] - 2s 0us/step Downloading data from https://storage.googleapis.com/civil_comments_dataset/validate_tf_processed.tfrecord 324941336/324941336 [==============================] - 4s 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)
我們儲存模型,以便使用 Fairness Indicators 進行評估。
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: /tmpfs/tmp/saved_models6z9x_9qz/model_export_baseline/assets INFO:tensorflow:Assets written to: /tmpfs/tmp/saved_models6z9x_9qz/model_export_baseline/assets
接下來,我們執行 Fairness Indicators。提醒您,我們將僅針對參考一個類別 (宗教團體) 的評論執行切片評估。在生產環境中,我們建議採取周全的方法來判斷要跨哪些類別和指標進行評估。
為了計算模型效能,公用程式函式針對指標、切片和分類器閾值做出了一些方便的選擇。
# 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.15.1) 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.9/site-packages/tensorflow_model_analysis/writers/metrics_plots_and_validations_writer.py:112: 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.9/site-packages/tensorflow_model_analysis/writers/metrics_plots_and_validations_writer.py:112: 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': …
讓我們看看評估結果。試著選取閾值為 0.450 的指標「偽陽率」(FPR)。我們可以發現,對於某些宗教團體而言,模型的效能不如其他宗教團體,顯示出更高的 FPR。請注意,某些群體的信賴區間較寬,因為它們的範例太少。這使得我們難以確定這些切片的效能是否存在顯著差異。我們可能需要收集更多範例來解決此問題。但是,我們可以嘗試對我們確信表現不佳的兩個群體應用 MinDiff。
我們選擇專注於 FPR,因為較高的 FPR 表示參考這些身分群體的評論比其他評論更可能被錯誤地標記為有害。這可能會導致參與宗教對話的使用者產生不公平的結果,但請注意,其他指標的差異可能會導致其他類型的傷害。
定義並訓練 MinDiff 模型
現在,我們將嘗試改善表現不佳的宗教群體的 FPR。我們將嘗試使用 MinDiff 來做到這一點,這是一種補救技術,旨在透過懲罰訓練期間效能的差異來平衡資料切片的錯誤率。當我們應用 MinDiff 時,模型效能可能會在其他切片上稍微降低。因此,我們使用 MinDiff 的目標將是
- 改善表現不佳群體的效能
- 限制其他群體和整體效能的降低
準備您的資料
若要使用 MinDiff,我們需要建立兩個額外的資料分割
- 針對參考少數群體的非有害範例的分割:在我們的案例中,這將包括參考我們表現不佳的身分條款的評論。我們不包含某些群體,因為範例太少,導致較高的不確定性以及寬廣的信賴區間範圍。
- 針對參考多數群體的非有害範例的分割。
擁有足夠的範例屬於表現不佳的類別非常重要。根據您的模型架構、資料分配和 MinDiff 設定,所需的資料量可能會差異很大。在過去的應用中,我們看到 MinDiff 在每個資料分割中包含 5,000 個範例時效果良好。
在我們的案例中,少數分割中的群體範例數量分別為 9,688 和 3,906。請注意資料集中的類別不平衡;實際上,這可能會引起關注,但我們不會在本筆記本中尋求解決這些問題,因為我們的意圖只是示範 MinDiff。
我們僅為這些群體選取負面範例,以便 MinDiff 可以最佳化以正確取得這些範例。如果我們主要關心偽陽率的差異,那麼劃分出地面實況負面範例集似乎有悖常理,但請記住,偽陽性預測是一個地面實況負面範例,它被錯誤地分類為陽性,而這正是我們試圖解決的問題。
建立 MinDiff DataFrame
# 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 DataFrame 轉換為 Tensorflow Dataset 以作為 MinDiff 輸入。請注意,與 Pandas DataFrame 的 Keras 模型 API 不同,使用 Dataset 表示我們需要將模型的輸入功能和標籤一起提供在一個 Dataset 中。在這裡,我們提供 'comment_text'
作為輸入功能,並重新塑造標籤以符合模型的預期輸出。
我們也在這個階段對 Dataset 進行批次處理,因為 MinDiff 需要批次處理的 Dataset。請注意,我們調整批次大小選取的方式與基準模型的調整方式相同,同時考慮訓練速度和硬體考量,並與模型效能保持平衡。在這裡,我們為所有三個資料集選擇了相同的批次大小,但這不是必要條件,儘管讓兩個 MinDiff 批次大小相等是一種良好做法。
建立 MinDiff Dataset
# 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 進行訓練,只需採用原始模型並將其封裝在具有對應 loss
和 loss_weight
的 MinDiffModel 中即可。我們使用 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: /tmpfs/tmp/saved_modelsfzu9o9iw/model_export_min_diff/assets INFO:tensorflow:Assets written to: /tmpfs/tmp/saved_modelsfzu9o9iw/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.15.1) 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 已獲得改善。我們表現最差的群體與多數群體之間的差距已從 0.024 改善至 0.006。鑑於我們觀察到的改善以及多數群體持續強勁的效能,我們已實現我們的兩個目標。根據產品的不同,可能需要進一步改善,但這種方法已使我們的模型更接近為所有使用者公平地執行。