تصنيف النص باستخدام BERT

عرض على TensorFlow.org تشغيل في Google Colab عرض على جيثب تحميل دفتر انظر نموذج TF Hub

يحتوي هذا البرنامج التعليمي على رمز كامل لضبط BERT لإجراء تحليل المشاعر على مجموعة بيانات لمراجعات أفلام IMDB ذات النص العادي. بالإضافة إلى تدريب النموذج ، ستتعلم كيفية المعالجة المسبقة للنص بتنسيق مناسب.

في هذا دفتر الملاحظات ، سوف:

  • قم بتحميل مجموعة بيانات IMDB
  • قم بتحميل نموذج BERT من TensorFlow Hub
  • قم ببناء النموذج الخاص بك عن طريق الجمع بين BERT والمصنف
  • قم بتدريب نموذجك الخاص ، وقم بضبط BERT كجزء من ذلك
  • احفظ النموذج الخاص بك واستخدمه لتصنيف الجمل

إذا كنت جديدًا في العمل مع مجموعة بيانات IMDB ، فيرجى الاطلاع على تصنيف النص الأساسي لمزيد من التفاصيل.

حول بيرت

نجحت BERT وبنيات تشفير Transformer الأخرى بشكل كبير في مجموعة متنوعة من المهام في معالجة اللغة الطبيعية (معالجة اللغة الطبيعية). يحسب تمثيلات فضاء المتجهات للغة الطبيعية المناسبة للاستخدام في نماذج التعلم العميق. تستخدم عائلة نماذج BERT بنية تشفير Transformer لمعالجة كل رمز مميز لنص الإدخال في السياق الكامل لجميع الرموز قبل وبعد ، ومن هنا جاء الاسم: تمثيلات التشفير ثنائية الاتجاه من المحولات.

عادةً ما يتم تدريب نماذج BERT مسبقًا على مجموعة كبيرة من النصوص ، ثم يتم ضبطها لمهام محددة.

يثبت

# A dependency of the preprocessing for BERT inputs
pip install -q -U tensorflow-text

ستستخدم مُحسِّن AdamW من Tensorflow / Models .

pip install -q tf-models-official
import os
import shutil

import tensorflow as tf
import tensorflow_hub as hub
import tensorflow_text as text
from official.nlp import optimization  # to create AdamW optimizer

import matplotlib.pyplot as plt

tf.get_logger().setLevel('ERROR')

تحليل المشاعر

يقوم هذا الكمبيوتر الدفتري بتدريب نموذج تحليل المشاعر لتصنيف مراجعات الأفلام على أنها إيجابية أو سلبية ، بناءً على نص المراجعة.

ستستخدم مجموعة بيانات استعراض الأفلام الكبيرة التي تحتوي على نص 50000 مراجعة فيلم من قاعدة بيانات الأفلام على الإنترنت .

قم بتنزيل مجموعة بيانات IMDB

لنقم بتنزيل مجموعة البيانات واستخراجها ، ثم نستكشف بنية الدليل.

url = 'https://ai.stanford.edu/~amaas/data/sentiment/aclImdb_v1.tar.gz'

dataset = tf.keras.utils.get_file('aclImdb_v1.tar.gz', url,
                                  untar=True, cache_dir='.',
                                  cache_subdir='')

dataset_dir = os.path.join(os.path.dirname(dataset), 'aclImdb')

train_dir = os.path.join(dataset_dir, 'train')

# remove unused folders to make it easier to load the data
remove_dir = os.path.join(train_dir, 'unsup')
shutil.rmtree(remove_dir)
Downloading data from https://ai.stanford.edu/~amaas/data/sentiment/aclImdb_v1.tar.gz
84131840/84125825 [==============================] - 7s 0us/step

بعد ذلك ، text_dataset_from_directory الأداة المساعدة text_dataset_from_directory لإنشاء ملفtf.data.Dataset .

تم بالفعل تقسيم مجموعة بيانات IMDB إلى تدريب واختبار ، لكنها تفتقر إلى مجموعة التحقق من الصحة. لنقم بإنشاء مجموعة تحقق باستخدام تقسيم 80:20 لبيانات التدريب باستخدام وسيطة validation_split أدناه.

AUTOTUNE = tf.data.AUTOTUNE
batch_size = 32
seed = 42

raw_train_ds = tf.keras.preprocessing.text_dataset_from_directory(
    'aclImdb/train',
    batch_size=batch_size,
    validation_split=0.2,
    subset='training',
    seed=seed)

class_names = raw_train_ds.class_names
train_ds = raw_train_ds.cache().prefetch(buffer_size=AUTOTUNE)

val_ds = tf.keras.preprocessing.text_dataset_from_directory(
    'aclImdb/train',
    batch_size=batch_size,
    validation_split=0.2,
    subset='validation',
    seed=seed)

val_ds = val_ds.cache().prefetch(buffer_size=AUTOTUNE)

test_ds = tf.keras.preprocessing.text_dataset_from_directory(
    'aclImdb/test',
    batch_size=batch_size)

test_ds = test_ds.cache().prefetch(buffer_size=AUTOTUNE)
Found 25000 files belonging to 2 classes.
Using 20000 files for training.
Found 25000 files belonging to 2 classes.
Using 5000 files for validation.
Found 25000 files belonging to 2 classes.

دعونا نلقي نظرة على بعض المراجعات.

for text_batch, label_batch in train_ds.take(1):
  for i in range(3):
    print(f'Review: {text_batch.numpy()[i]}')
    label = label_batch.numpy()[i]
    print(f'Label : {label} ({class_names[label]})')
02ade17900

نماذج التحميل من TensorFlow Hub

هنا يمكنك اختيار طراز BERT الذي ستقوم بتحميله من TensorFlow Hub وضبطه. هناك العديد من طرز BERT المتاحة.

  • BERT-Base و Uncased وسبعة نماذج أخرى بأوزان مدربة أصدرها مؤلفو BERT الأصليون.
  • تمتلك BERTs الصغيرة نفس البنية العامة ولكن عددًا أقل و / أو كتلًا أصغر من المحولات ، مما يتيح لك استكشاف المفاضلات بين السرعة والحجم والجودة.
  • ALBERT : أربعة أحجام مختلفة من "A Lite BERT" تقلل من حجم النموذج (ولكن ليس وقت الحساب) من خلال مشاركة المعلمات بين الطبقات.
  • خبراء BERT : ثمانية نماذج تحتوي جميعها على بنية قاعدة BERT ولكنها توفر خيارًا بين مجالات ما قبل التدريب المختلفة ، لتتماشى بشكل أوثق مع المهمة المستهدفة.
  • تمتلك Electra نفس بنية BERT (بثلاثة أحجام مختلفة) ، ولكن يتم تدريبها مسبقًا كمميز في إعداد يشبه شبكة الخصومة التوليدية (GAN).
  • يحتوي BERT مع Talking-Heads Attention و GELU [ قاعدة كبيرة ] على تحسينين في جوهر بنية المحولات.

يحتوي توثيق النموذج على TensorFlow Hub على مزيد من التفاصيل والمراجع إلى الأدبيات البحثية. اتبع الروابط أعلاه ، أو انقر فوق tfhub.dev URL المطبوع بعد تنفيذ الخلية التالية.

الاقتراح هو أن تبدأ بـ Small BERT (مع عدد أقل من المعلمات) لأنها أسرع في ضبطها. إذا كنت تحب نموذجًا صغيرًا ولكن بدقة أعلى ، فقد يكون ALBERT هو خيارك التالي. إذا كنت تريد دقة أفضل ، فاختر أحد أحجام BERT الكلاسيكية أو تحسيناتها الحديثة مثل Electra أو Talking Heads أو BERT Expert.

بصرف النظر عن الطرز المتوفرة أدناه ، هناك إصدارات متعددة من الطرز أكبر ويمكن أن تحقق دقة أفضل ، لكنها أكبر من أن يتم ضبطها بدقة على وحدة معالجة رسومات واحدة. ستكون قادرًا على القيام بذلك في مهام Solve GLUE باستخدام BERT على colab TPU .

سترى في الكود أدناه أن تبديل tfhub.dev URL يكفي لتجربة أي من هذه النماذج ، لأن جميع الاختلافات بينها مغلفة في SavedModels من TF Hub.

اختر نموذج BERT لضبطه

BERT model selected           : https://tfhub.dev/tensorflow/small_bert/bert_en_uncased_L-4_H-512_A-8/1
Preprocess model auto-selected: https://tfhub.dev/tensorflow/bert_en_uncased_preprocess/3

نموذج المعالجة المسبقة

يجب تحويل مدخلات النص إلى معرفات رمزية رقمية وترتيبها في العديد من Tensors قبل إدخالها في BERT. يوفر TensorFlow Hub نموذج معالجة مسبقة مطابقًا لكل من نماذج BERT التي تمت مناقشتها أعلاه ، والتي تنفذ هذا التحول باستخدام TF ops من مكتبة TF.text. ليس من الضروري تشغيل كود Python النقي خارج نموذج TensorFlow الخاص بك إلى نص المعالجة المسبقة.

يجب أن يكون نموذج المعالجة المسبقة هو النموذج المشار إليه في وثائق نموذج BERT ، والذي يمكنك قراءته على عنوان URL المطبوع أعلاه. بالنسبة لنماذج BERT من القائمة المنسدلة أعلاه ، يتم تحديد نموذج المعالجة المسبقة تلقائيًا.

bert_preprocess_model = hub.KerasLayer(tfhub_handle_preprocess)

لنجرب نموذج المعالجة المسبقة في بعض النصوص ونرى الإخراج:

text_test = ['this is such an amazing movie!']
text_preprocessed = bert_preprocess_model(text_test)

print(f'Keys       : {list(text_preprocessed.keys())}')
print(f'Shape      : {text_preprocessed["input_word_ids"].shape}')
print(f'Word Ids   : {text_preprocessed["input_word_ids"][0, :12]}')
print(f'Input Mask : {text_preprocessed["input_mask"][0, :12]}')
print(f'Type Ids   : {text_preprocessed["input_type_ids"][0, :12]}')
Keys       : ['input_type_ids', 'input_mask', 'input_word_ids']
Shape      : (1, 128)
Word Ids   : [ 101 2023 2003 2107 2019 6429 3185  999  102    0    0    0]
Input Mask : [1 1 1 1 1 1 1 1 1 0 0 0]
Type Ids   : [0 0 0 0 0 0 0 0 0 0 0 0]

كما ترى ، لديك الآن 3 مخرجات من المعالجة المسبقة التي input_words_id نموذج BERT ( input_words_id ، و input_mask ، و input_type_ids ).

بعض النقاط المهمة الأخرى:

  • يتم قطع الإدخال إلى 128 رمزًا مميزًا. يمكن تخصيص عدد الرموز المميزة ، ويمكنك الاطلاع على مزيد من التفاصيل حول مهام Solve GLUE باستخدام BERT على colab TPU .
  • إن input_type_ids لها قيمة واحدة فقط (0) لأن هذا هو إدخال جملة واحدة. لإدخال جملة متعددة ، سيكون لها رقم واحد لكل إدخال.

نظرًا لأن هذا المعالج النصي هو نموذج TensorFlow ، فيمكن تضمينه في النموذج الخاص بك مباشرةً.

استخدام نموذج بيرت

قبل وضع BERT في النموذج الخاص بك ، دعنا نلقي نظرة على مخرجاته. سوف تقوم بتحميله من TF Hub وترى القيم التي تم إرجاعها.

bert_model = hub.KerasLayer(tfhub_handle_encoder)
bert_results = bert_model(text_preprocessed)

print(f'Loaded BERT: {tfhub_handle_encoder}')
print(f'Pooled Outputs Shape:{bert_results["pooled_output"].shape}')
print(f'Pooled Outputs Values:{bert_results["pooled_output"][0, :12]}')
print(f'Sequence Outputs Shape:{bert_results["sequence_output"].shape}')
print(f'Sequence Outputs Values:{bert_results["sequence_output"][0, :12]}')
Loaded BERT: https://tfhub.dev/tensorflow/small_bert/bert_en_uncased_L-4_H-512_A-8/1
Pooled Outputs Shape:(1, 512)
Pooled Outputs Values:[ 0.76262873  0.99280983 -0.1861186   0.36673835  0.15233682  0.65504444
  0.9681154  -0.9486272   0.00216158 -0.9877732   0.0684272  -0.9763061 ]
Sequence Outputs Shape:(1, 128, 512)
Sequence Outputs Values:[[-0.28946388  0.3432126   0.33231565 ...  0.21300787  0.7102078
  -0.05771166]
 [-0.28742015  0.31981024 -0.2301858  ...  0.58455074 -0.21329722
   0.7269209 ]
 [-0.66157013  0.6887685  -0.87432927 ...  0.10877253 -0.26173282
   0.47855264]
 ...
 [-0.2256118  -0.28925604 -0.07064401 ...  0.4756601   0.8327715
   0.40025353]
 [-0.29824278 -0.27473143 -0.05450511 ...  0.48849759  1.0955356
   0.18163344]
 [-0.44378197  0.00930723  0.07223766 ...  0.1729009   1.1833246
   0.07897988]]

تعيد نماذج BERT خريطة بثلاثة مفاتيح مهمة: pooled_output ، و sequence_output ، و encoder_outputs :

  • يمثل pooled_output كل تسلسل إدخال ككل. الشكل هو [batch_size, H] . يمكنك التفكير في هذا على أنه تضمين لمراجعة الفيلم بالكامل.
  • sequence_output يمثل كل رمز إدخال في السياق. الشكل هو [batch_size, seq_length, H] . يمكنك التفكير في هذا على أنه تضمين سياقي لكل رمز مميز في مراجعة الفيلم.
  • encoder_outputs هي عمليات التنشيط الوسيطة لكتل ​​المحولات L outputs["encoder_outputs"][i] هي [batch_size, seq_length, 1024] الشكل [batch_size, seq_length, 1024] مع مخرجات كتلة المحول i ، لـ 0 <= i < L . القيمة الأخيرة من القائمة تساوي sequence_output .

من أجل الضبط الدقيق ، pooled_output مصفوفة pooled_output .

حدد نموذجك

ستنشئ نموذجًا بسيطًا دقيقًا للغاية ، مع نموذج المعالجة المسبقة ، ونموذج BERT المحدد ، وطبقة Dense وطبقة Dropout.

def build_classifier_model():
  text_input = tf.keras.layers.Input(shape=(), dtype=tf.string, name='text')
  preprocessing_layer = hub.KerasLayer(tfhub_handle_preprocess, name='preprocessing')
  encoder_inputs = preprocessing_layer(text_input)
  encoder = hub.KerasLayer(tfhub_handle_encoder, trainable=True, name='BERT_encoder')
  outputs = encoder(encoder_inputs)
  net = outputs['pooled_output']
  net = tf.keras.layers.Dropout(0.1)(net)
  net = tf.keras.layers.Dense(1, activation=None, name='classifier')(net)
  return tf.keras.Model(text_input, net)

دعنا نتحقق من أن النموذج يعمل مع إخراج نموذج المعالجة المسبقة.

classifier_model = build_classifier_model()
bert_raw_result = classifier_model(tf.constant(text_test))
print(tf.sigmoid(bert_raw_result))
tf.Tensor([[0.50131935]], shape=(1, 1), dtype=float32)

الناتج لا معنى له ، بالطبع ، لأن النموذج لم يتم تدريبه بعد.

دعنا نلقي نظرة على هيكل النموذج.

tf.keras.utils.plot_model(classifier_model)

بي إن جي

تدريب نموذجي

لديك الآن كل القطع لتدريب نموذج ، بما في ذلك وحدة المعالجة المسبقة ، ومشفّر BERT ، والبيانات ، والمصنف.

فقدان وظيفة

منذ هذه مشكلة تصنيف الثنائية ونموذج إخراج احتمال (طبقة وحدة واحدة)، عليك استخدام losses.BinaryCrossentropy فقدان الوظيفة.

loss = tf.keras.losses.BinaryCrossentropy(from_logits=True)
metrics = tf.metrics.BinaryAccuracy()

محسن

من أجل الضبط الدقيق ، دعنا نستخدم نفس المُحسِّن الذي تم تدريب BERT عليه في الأصل: "اللحظات التكيفية" (آدم). يقلل هذا المُحسِّن من فقدان التنبؤ ويقوم بالتنظيم من خلال تسوس الوزن (بدون استخدام اللحظات) ، والذي يُعرف أيضًا باسم AdamW .

بالنسبة لمعدل التعلم ( init_lr ) ، ستستخدم نفس الجدول الزمني مثل تدريب BERT المسبق: التحلل الخطي لمعدل التعلم الأولي النظري ، مسبوقًا بمرحلة إحماء خطية على أول 10٪ من خطوات التدريب ( num_warmup_steps ). تماشياً مع ورقة BERT ، يكون معدل التعلم الأولي أصغر للضبط الدقيق (أفضل من 5e-5 ، 3e-5 ، 2e-5).

epochs = 5
steps_per_epoch = tf.data.experimental.cardinality(train_ds).numpy()
num_train_steps = steps_per_epoch * epochs
num_warmup_steps = int(0.1*num_train_steps)

init_lr = 3e-5
optimizer = optimization.create_optimizer(init_lr=init_lr,
                                          num_train_steps=num_train_steps,
                                          num_warmup_steps=num_warmup_steps,
                                          optimizer_type='adamw')

تحميل نموذج BERT والتدريب

باستخدام classifier_model الذي قمت بإنشائه مسبقًا ، يمكنك تجميع النموذج مع الخسارة والقياس والمحسن.

classifier_model.compile(optimizer=optimizer,
                         loss=loss,
                         metrics=metrics)
print(f'Training model with {tfhub_handle_encoder}')
history = classifier_model.fit(x=train_ds,
                               validation_data=val_ds,
                               epochs=epochs)
Training model with https://tfhub.dev/tensorflow/small_bert/bert_en_uncased_L-4_H-512_A-8/1
Epoch 1/5
625/625 [==============================] - 83s 124ms/step - loss: 0.4881 - binary_accuracy: 0.7403 - val_loss: 0.3917 - val_binary_accuracy: 0.8340
Epoch 2/5
625/625 [==============================] - 77s 124ms/step - loss: 0.3296 - binary_accuracy: 0.8518 - val_loss: 0.3714 - val_binary_accuracy: 0.8450
Epoch 3/5
625/625 [==============================] - 78s 124ms/step - loss: 0.2530 - binary_accuracy: 0.8939 - val_loss: 0.4036 - val_binary_accuracy: 0.8486
Epoch 4/5
625/625 [==============================] - 78s 124ms/step - loss: 0.1968 - binary_accuracy: 0.9226 - val_loss: 0.4468 - val_binary_accuracy: 0.8502
Epoch 5/5
625/625 [==============================] - 78s 124ms/step - loss: 0.1604 - binary_accuracy: 0.9392 - val_loss: 0.4716 - val_binary_accuracy: 0.8498

قم بتقييم النموذج

دعونا نرى كيف يعمل النموذج. سيتم إرجاع قيمتين. الخسارة (رقم يمثل الخطأ ، والقيم الأقل أفضل) ، والدقة.

loss, accuracy = classifier_model.evaluate(test_ds)

print(f'Loss: {loss}')
print(f'Accuracy: {accuracy}')
782/782 [==============================] - 53s 67ms/step - loss: 0.4476 - binary_accuracy: 0.8554
Loss: 0.44761356711387634
Accuracy: 0.8554400205612183

ارسم الدقة والخسارة بمرور الوقت

استنادًا إلى كائن History إرجاعه بواسطة model.fit() . يمكنك رسم مخطط خسارة التدريب والتحقق من الصحة للمقارنة ، بالإضافة إلى دقة التدريب والتحقق من الصحة:

history_dict = history.history
print(history_dict.keys())

acc = history_dict['binary_accuracy']
val_acc = history_dict['val_binary_accuracy']
loss = history_dict['loss']
val_loss = history_dict['val_loss']

epochs = range(1, len(acc) + 1)
fig = plt.figure(figsize=(10, 6))
fig.tight_layout()

plt.subplot(2, 1, 1)
# "bo" is for "blue dot"
plt.plot(epochs, loss, 'r', label='Training loss')
# b is for "solid blue line"
plt.plot(epochs, val_loss, 'b', label='Validation loss')
plt.title('Training and validation loss')
# plt.xlabel('Epochs')
plt.ylabel('Loss')
plt.legend()

plt.subplot(2, 1, 2)
plt.plot(epochs, acc, 'r', label='Training acc')
plt.plot(epochs, val_acc, 'b', label='Validation acc')
plt.title('Training and validation accuracy')
plt.xlabel('Epochs')
plt.ylabel('Accuracy')
plt.legend(loc='lower right')
dict_keys(['loss', 'binary_accuracy', 'val_loss', 'val_binary_accuracy'])
<matplotlib.legend.Legend at 0x7f542fffc590>

بي إن جي

في هذه المؤامرة ، تمثل الخطوط الحمراء خسارة التدريب ودقته ، والخطوط الزرقاء هي فقدان التحقق من الصحة والدقة.

تصدير للاستدلال

أنت الآن تقوم فقط بحفظ النموذج الذي تم ضبطه لاستخدامه لاحقًا.

dataset_name = 'imdb'
saved_model_path = './{}_bert'.format(dataset_name.replace('/', '_'))

classifier_model.save(saved_model_path, include_optimizer=False)
WARNING:absl:Found untraced functions such as restored_function_body, restored_function_body, restored_function_body, restored_function_body, restored_function_body while saving (showing 5 of 310). These functions will not be directly callable after loading.

دعنا نعيد تحميل النموذج ، حتى تتمكن من تجربته جنبًا إلى جنب مع النموذج الذي لا يزال في الذاكرة.

reloaded_model = tf.saved_model.load(saved_model_path)

هنا يمكنك اختبار النموذج الخاص بك على أي جملة تريدها ، فقط أضف إلى متغير الأمثلة أدناه.

def print_my_examples(inputs, results):
  result_for_printing = \
    [f'input: {inputs[i]:<30} : score: {results[i][0]:.6f}'
                         for i in range(len(inputs))]
  print(*result_for_printing, sep='\n')
  print()


examples = [
    'this is such an amazing movie!',  # this is the same sentence tried earlier
    'The movie was great!',
    'The movie was meh.',
    'The movie was okish.',
    'The movie was terrible...'
]

reloaded_results = tf.sigmoid(reloaded_model(tf.constant(examples)))
original_results = tf.sigmoid(classifier_model(tf.constant(examples)))

print('Results from the saved model:')
print_my_examples(examples, reloaded_results)
print('Results from the model in memory:')
print_my_examples(examples, original_results)
Results from the saved model:
input: this is such an amazing movie! : score: 0.998905
input: The movie was great!           : score: 0.994330
input: The movie was meh.             : score: 0.968163
input: The movie was okish.           : score: 0.069656
input: The movie was terrible...      : score: 0.000776

Results from the model in memory:
input: this is such an amazing movie! : score: 0.998905
input: The movie was great!           : score: 0.994330
input: The movie was meh.             : score: 0.968163
input: The movie was okish.           : score: 0.069656
input: The movie was terrible...      : score: 0.000776

إذا كنت تريد استخدام النموذج الخاص بك على خدمة TF ، فتذكر أنه سيتصل بـ SavedModel الخاص بك من خلال أحد التوقيعات المسماة. في Python ، يمكنك اختبارها على النحو التالي:

serving_results = reloaded_model \
            .signatures['serving_default'](tf.constant(examples))

serving_results = tf.sigmoid(serving_results['classifier'])

print_my_examples(examples, serving_results)
input: this is such an amazing movie! : score: 0.998905
input: The movie was great!           : score: 0.994330
input: The movie was meh.             : score: 0.968163
input: The movie was okish.           : score: 0.069656
input: The movie was terrible...      : score: 0.000776

الخطوات التالية

كخطوة تالية ، يمكنك تجربة حل مهام GLUE باستخدام BERT في برنامج تعليمي لـ TPU ، والذي يعمل على TPU ويوضح لك كيفية العمل مع مدخلات متعددة.