الخطوة 3: إعداد بياناتك

قبل تقديم بياناتنا إلى نموذج، يجب تحويلها إلى تنسيق يمكن أن يفهمه النموذج.

أولاً، قد تكون نماذج البيانات التي جمعناها بترتيب معيّن. لا نريد أن تؤثر أي معلومات مرتبطة بترتيب النماذج في العلاقة بين النصوص والتصنيفات. على سبيل المثال، إذا تم ترتيب مجموعة البيانات حسب الفئة، ثم تم تقسيمها إلى مجموعات للتدريب/التحقّق، لن تمثّل هذه المجموعات التوزيع العام للبيانات.

تتمثّل إحدى أفضل الممارسات البسيطة في ضمان عدم تأثُّر النموذج بترتيب البيانات وترتيب البيانات دائمًا قبل تنفيذ أي إجراء آخر. في حال تم تقسيم بياناتك إلى مجموعات تدريب والتحقق من الصحة، تأكد من تحويل بيانات التحقق بالطريقة نفسها التي يتم بها تحويل بيانات التدريب. إذا لم تكن لديك مجموعات تدريب والتحقّق منفصلة، يمكنك تقسيم العيّنات بعد إجراء ترتيب عشوائي، فمن الطبيعي استخدام 80% من العيّنات للتدريب و20% للتحقق من الصحة.

ثانيًا، تأخذ خوارزميات تعلُّم الآلة الأرقام كإدخالات. وهذا يعني أننا سنحتاج إلى تحويل النصوص إلى متّجهات رقمية. وهناك خطوتان لتنفيذ هذه العملية:

  1. الترميز: قسِّم النصوص إلى كلمات أو نصوص فرعية أصغر، ما يسمح بتعميم العلاقة بين النصوص والتصنيفات. يحدّد هذا الإعداد "مفردات" مجموعة البيانات (مجموعة من الرموز المميّزة الفريدة الموجودة في البيانات).

  2. الموجّه: حدّد مقياسًا عدديًا جيدًا لوصف هذه النصوص.

لنتعرّف على كيفية تنفيذ هاتين الخطوتين لكلّ من متّجهَي n-gram ومتّجهيّات التسلسلات، بالإضافة إلى كيفية تحسين تمثيلات المتّجهَين باستخدام تقنيات اختيار الميزات والتسوية.

متّجهات غرامية [الخيار (أ)]

في الفقرات اللاحقة، سنتعرّف على كيفية إجراء الترميز وتحويل الصوت إلى نماذج n-gram. وسنتناول أيضًا الطرق التي يمكننا من خلالها تحسين تمثيل البيانات الجدولية باستخدام أساليب اختيار الميزات والتسوية.

في المتّجه بتنسيق n-gram، يتم تمثيل النص كمجموعة من رموز n-gram الدقيقة: مجموعات من الرموز المميزة n المجاورة (عادةً، الكلمات). ننصحك باستخدام النص The mouse ran up the clock. هنا، كلمة unigram (n = 1) هي ['the', 'mouse', 'ran', 'up', 'clock']، والكلمة كبيرة (n = 2) هي ['the mouse', 'mouse ran', 'ran up', 'up the', 'the clock']، وهكذا.

إنشاء الرموز المميّزة

لقد تبيّن لنا أن استخدام ترميز الكلمات يونيغرام + Bigrams يوفّر دقة جيدة مع تقليل وقت الحوسبة.

الموجّه

وبعد تقسيم عيّنات النصوص لدينا إلى غرامات n، علينا تحويل هذه الغرامات إلى متّجهات رقمية يمكنها معالجة نماذج تعلُّم الآلة لدينا. يوضّح المثال التالي الفهارس المحدَّدة للترميز unigram وbigrams التي تم إنشاؤها للنصَين.

Texts: 'The mouse ran up the clock' and 'The mouse ran down'
Index assigned for every token: {'the': 7, 'mouse': 2, 'ran': 4, 'up': 10,
  'clock': 0, 'the mouse': 9, 'mouse ran': 3, 'ran up': 6, 'up the': 11, 'the
clock': 8, 'down': 1, 'ran down': 5}

بعد أن يتم تحديد فهارس البيانات إلى n-g، يتم عادةً تحديد الرسم البياني باستخدام أحد الخيارات التالية.

الترميز الساخن: يتم تمثيل كل نموذج نص كمتّجه يشير إلى وجود رمز مميز في النص أو عدم وجوده.

'The mouse ran up the clock' = [1, 0, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1]

ترميز العدد: يتم تمثيل كل نموذج نص كمتّجه يشير إلى عدد الرموز المميّزة في النص. تجدُر الإشارة إلى أنّ العنصر المقابل للرمز يونيكود 'the' (الغامق أدناه) يتم تمثيله الآن على أنه 2 لأن كلمة "the" تظهر مرّتين في النص.

'The mouse ran up the clock' = [1, 0, 1, 1, 1, 0, 1, 2, 1, 1, 1, 1]

ترميز Tf-idf: تتمثّل المشكلة في النهجَين المذكورَين أعلاه في أنّه لا يتم فرض عقوبات على الكلمات الشائعة التي تقع في تسلسلات متشابهة في جميع المستندات (أي الكلمات التي ليست فريدة من نوعها في العيّنات النصية في مجموعة البيانات). على سبيل المثال، عادةً ما تظهر كلمات مثل "أ" في جميع النصوص. وبالتالي، فإنّ إضافة رمز مميّز أعلى لـ "the" مقارنةً بالكلمات الأخرى الأكثر فائدةً لا يُعدّ مفيدًا للغاية.

'The mouse ran up the clock' = [0.33, 0, 0.23, 0.23, 0.23, 0, 0.33, 0.47, 0.33,
0.23, 0.33, 0.33] (See Scikit-learn TfidfTransformer)

هناك العديد من التمثيلات الموجّهة الأخرى، ولكن الثلاثة الأكثر استخدامًا هي الأكثر استخدامًا.

وقد لاحظنا أن ترميز tf-idf أفضل بكثير من النوعَين الآخرَين من حيث الدقة (في المتوسط: من 0.25 إلى 15%)، ونقترح استخدام هذه الطريقة لتحديد اتجاه n-gram. ومع ذلك، ضَع في اعتبارك أنّها تشغّل مساحة أكبر من الذاكرة (لأنّها تستخدم تمثيل النقاط العائمة) وتستغرق وقتًا أطول لاحتسابها، خاصةً لمجموعات البيانات الكبيرة (التي يمكن أن تستغرق ضعف تلك المدة في بعض الحالات).

اختيار الميزة

عندما نحوّل كل النصوص في مجموعة بيانات إلى رمز مميّز واحد لكلمة uni+bigram، يمكن أن ينتهي الحال إلى عشرات الآلاف من الرموز المميّزة. لا تساهم جميع هذه الرموز/الميزات في توقّع التصنيف. لذلك يمكننا إسقاط رموز مميّزة معيّنة، على سبيل المثال الرمز المميّز الذي نادرًا ما يحدث في مجموعة البيانات. ويمكننا أيضًا قياس أهمية الميزات (مدى مساهمة كل رمز مميّز في توقّعات التصنيفات)، وتضمين الرموز المميّزة الأكثر أهمية فقط.

هناك العديد من الدوال الإحصائية التي تعتمد على الميزات والتصنيفات المقابلة والحصول على نتيجة أهمية الميزة. هناك دالتان شائعتان الاستخدام f_classif و chi2. وتُظهر تجاربنا أنّ هاتَي الميزتَين يؤديان الأداء نفسه بالتساوي.

الأهم من ذلك هو أننا لاحظنا أن الدقة تصل إلى 20000 ميزة تقريبًا في العديد من مجموعات البيانات (راجع الشكل 6). تؤدي إضافة المزيد من الميزات بعد هذا الحدّ إلى المساهمة القليل جدًا وفي بعض الأحيان تؤدي إلى المتلاءم وتؤدي إلى خفض مستوى الأداء.

أعلى دقة مقارنةً بالدقة

الشكل 6: أهم ميزات K مقابل الدقة على مستوى مجموعات البيانات، تظهر هضبة الدقة في حوالي 20 ألف ميزة.

تسوية

تعمل عملية التسوية على تحويل جميع قيم الميزات/العينات إلى قيم صغيرة ومشابهة. يعمل هذا على تبسيط اهتمامات النزهات المتدرجة في خوارزميات التعلُّم. واستنادًا إلى ما تبيّن لنا، لا يبدو أن التسوية عند المعالجة المُسبَقة للبيانات تضيف الكثير من المشاكل في تصنيف النص، لذلك ننصح بتخطّي هذه الخطوة.

يجمع الرمز التالي كل الخطوات المذكورة أعلاه:

  • تحويل عيّنات النصوص إلى رموز مميّزة باستخدام كلمة uni+bigram
  • المتجه باستخدام ترميز tf-idf
  • اختَر فقط أهم 20,000 ميزة من متّجه الرموز المميّزة عن طريق تجاهل الرموز المميزة التي تظهر أقل من مرتين واستخدام f_classif لاحتساب مدى أهمية الميزة.
from sklearn.feature_extraction.text import TfidfVectorizer
from sklearn.feature_selection import SelectKBest
from sklearn.feature_selection import f_classif

# Vectorization parameters
# Range (inclusive) of n-gram sizes for tokenizing text.
NGRAM_RANGE = (1, 2)

# Limit on the number of features. We use the top 20K features.
TOP_K = 20000

# Whether text should be split into word or character n-grams.
# One of 'word', 'char'.
TOKEN_MODE = 'word'

# Minimum document/corpus frequency below which a token will be discarded.
MIN_DOCUMENT_FREQUENCY = 2

def ngram_vectorize(train_texts, train_labels, val_texts):
    """Vectorizes texts as n-gram vectors.

    1 text = 1 tf-idf vector the length of vocabulary of unigrams + bigrams.

    # Arguments
        train_texts: list, training text strings.
        train_labels: np.ndarray, training labels.
        val_texts: list, validation text strings.

    # Returns
        x_train, x_val: vectorized training and validation texts
    """
    # Create keyword arguments to pass to the 'tf-idf' vectorizer.
    kwargs = {
            'ngram_range': NGRAM_RANGE,  # Use 1-grams + 2-grams.
            'dtype': 'int32',
            'strip_accents': 'unicode',
            'decode_error': 'replace',
            'analyzer': TOKEN_MODE,  # Split text into word tokens.
            'min_df': MIN_DOCUMENT_FREQUENCY,
    }
    vectorizer = TfidfVectorizer(**kwargs)

    # Learn vocabulary from training texts and vectorize training texts.
    x_train = vectorizer.fit_transform(train_texts)

    # Vectorize validation texts.
    x_val = vectorizer.transform(val_texts)

    # Select top 'k' of the vectorized features.
    selector = SelectKBest(f_classif, k=min(TOP_K, x_train.shape[1]))
    selector.fit(x_train, train_labels)
    x_train = selector.transform(x_train).astype('float32')
    x_val = selector.transform(x_val).astype('float32')
    return x_train, x_val

وباستخدام تمثيل متّجه n-g، نتجاهل الكثير من المعلومات حول ترتيب الكلمات والقواعد النحوية (على أفضل نحو، يمكننا الاحتفاظ ببعض معلومات الترتيب الجزئي عند استخدام n > 1). ويُطلق على هذه العملية اسم نهج الكلام. يُستخدم هذا التمثيل مع النماذج التي لا تأخذ في الاعتبار الطلب، مثل الانحدار اللوجستي، والانطباعات المتعددة الطبقات، وأدوات تحسين التدرّج، وأجهزة المتّجه.

متّجهات التسلسل [الخيار ب]

في الفقرات اللاحقة، سنتعرّف على كيفية إجراء ترميز وتوجيه لنماذج التسلسل. وسنتناول أيضًا كيف يمكننا تحسين تمثيل التسلسل باستخدام أساليب اختيار الميزات وتسويتها.

بالنسبة إلى بعض النماذج النصية، يكون ترتيب الكلمات مهمًا بالنسبة إلى معنى النص. على سبيل المثال، في الجملة، "اعتدت على كراهتي أثناء التنقل. لا يمكن فهم دراجتي الجديدة تمامًا إلا عند قراءتها بالترتيب. ويمكن أن تستنتج النماذج، مثل CNNs/RNNs، معنى ترتيب الكلمات في عيّنة. بالنسبة إلى هذه النماذج، نعرض النص كسلسلة من الرموز المميّزة، مع الحفاظ على الترتيب.

إنشاء الرموز المميّزة

يمكن تمثيل النص كتسلسل من الأحرف أو تسلسل للكلمات. لقد تبيّن لنا أنّ استخدام التمثيل على مستوى الكلمة يوفّر أداءً أفضل من الرموز المميّزة للأحرف. وهذا أيضًا هو المعيار العام المتّبع في المجال. ومن المنطقي استخدام الرموز المميزة للأحرف فقط إذا كانت النصوص تحتوي على الكثير من الأخطاء الإملائية، وهذا ليس طبيعيًا.

الموجّه

بعد تحويل العيّنات النصية إلى تسلسلات من الكلمات، علينا تحويل هذه التسلسلات إلى متّجهات رقمية. يوضّح المثال التالي الفهارس المخصّصة للأحرف المستقلة التي تم إنشاؤها لنصّين، ثم يعرض تسلسل الفهارس المميّزة التي يتم تحويل النص الأول إليها.

Texts: 'The mouse ran up the clock' and 'The mouse ran down'
Index assigned for every token: {'clock': 5, 'ran': 3, 'up': 4, 'down': 6, 'the': 1, 'mouse': 2}.
NOTE: 'the' occurs most frequently, so the index value of 1 is assigned to it.
Some libraries reserve index 0 for unknown tokens, as is the case here.
Sequence of token indexes: 'The mouse ran up the clock' = [1, 2, 3, 4, 1, 5]

هناك خياران متاحان لتنظيم تسلسلات الرموز المميّزة:

الترميز الساخن: يتم تمثيل التسلسلات باستخدام متّجهات الكلمات في المسافة البُعدية حيث n = حجم المفردات. ويُعتبر هذا التمثيل مناسبًا عند ترميز الرموز كأحرف، وبالتالي تكون المصطلحات صغيرة. عند ترميز الكلمات ككلمات، سيكون للمفردات عادةً عشرات الآلاف من الرموز المميّزة، ما يجعل متّجهات متفرِّغة قليلة جدًا وغير فعّالة. مثال:

'The mouse ran up the clock' = [
  [0, 1, 0, 0, 0, 0, 0],
  [0, 0, 1, 0, 0, 0, 0],
  [0, 0, 0, 1, 0, 0, 0],
  [0, 0, 0, 0, 1, 0, 0],
  [0, 1, 0, 0, 0, 0, 0],
  [0, 0, 0, 0, 0, 1, 0]
]

تضمينات الكلمات: تشير الكلمات إلى معانٍ مرتبطة بها. ونتيجةً لذلك، يمكننا تمثّل الرموز المميزة للكلمات في مساحة متّجه مكتظة (بضعة مئات الأرقام الحقيقية)، حيث يشير الموقع الجغرافي والمسافة بين الكلمات إلى مدى تشابهها دلاليًا (اطّلع على الشكل 7). ويُعرف هذا التمثيل باسم تضمينات الكلمات.

تضمينات الكلمات

الشكل 7: تضمينات الكلمات

غالبًا ما تحتوي نماذج التسلسل على طبقة تضمين مثل الطبقة الأولى. تتعلّم هذه الطبقة تحويل تسلسلات فهرس الكلمات إلى متّجهات تضمين الكلمات أثناء عملية التدريب، بحيث يتم ربط كل فهرس كلمات بالمتّجه الكثيف للقيم الفعلية التي تمثّل موقع هذه الكلمة في المساحة الدلالية (اطّلع على الشكل 8).

طبقة التضمين

الشكل 8: طبقة التضمين

اختيار الميزة

ليست كل الكلمات في بياناتنا تساهم في توقّعات التصنيفات. يمكننا تحسين عملية التعلّم من خلال تجاهل الكلمات النادرة أو غير الملائمة من مفرداتنا. في الواقع، نلاحظ أن استخدام أكثر 20,000 ميزة بشكل كافٍ يكفي عمومًا. ينطبق هذا الأمر أيضًا على نماذج n-gram (اطّلِع على الشكل 6).

لنضع كل الخطوات المذكورة أعلاه في اتجاه التسلسل معًا. يؤدي الرمز التالي المهام التالية:

  • تحويل النصوص إلى كلمات رمزية
  • تنشئ مفردات باستخدام أهم 20,000 رمز مميّز.
  • تحويل الرموز المميّزة إلى متّجهة تسلسلية
  • ضبط التسلسلات على طول تسلسل ثابت
from tensorflow.python.keras.preprocessing import sequence
from tensorflow.python.keras.preprocessing import text

# Vectorization parameters
# Limit on the number of features. We use the top 20K features.
TOP_K = 20000

# Limit on the length of text sequences. Sequences longer than this
# will be truncated.
MAX_SEQUENCE_LENGTH = 500

def sequence_vectorize(train_texts, val_texts):
    """Vectorizes texts as sequence vectors.

    1 text = 1 sequence vector with fixed length.

    # Arguments
        train_texts: list, training text strings.
        val_texts: list, validation text strings.

    # Returns
        x_train, x_val, word_index: vectorized training and validation
            texts and word index dictionary.
    """
    # Create vocabulary with training texts.
    tokenizer = text.Tokenizer(num_words=TOP_K)
    tokenizer.fit_on_texts(train_texts)

    # Vectorize training and validation texts.
    x_train = tokenizer.texts_to_sequences(train_texts)
    x_val = tokenizer.texts_to_sequences(val_texts)

    # Get max sequence length.
    max_length = len(max(x_train, key=len))
    if max_length > MAX_SEQUENCE_LENGTH:
        max_length = MAX_SEQUENCE_LENGTH

    # Fix sequence length to max value. Sequences shorter than the length are
    # padded in the beginning and sequences longer are truncated
    # at the beginning.
    x_train = sequence.pad_sequences(x_train, maxlen=max_length)
    x_val = sequence.pad_sequences(x_val, maxlen=max_length)
    return x_train, x_val, tokenizer.word_index

اتجاه التصنيف

ولاحظنا كيفية تحويل نموذج بيانات النص إلى متّجهات رقمية. يجب تطبيق عملية مماثلة على التصنيفات. يمكننا ببساطة تحويل التصنيفات إلى قيم في النطاق [0, num_classes - 1]. على سبيل المثال، في حال وجود 3 فئات، يمكننا استخدام القيم 0 و1 و2 لتمثيلها. داخليًا، ستستخدم الشبكة متّجهات واحد لتمثيل هذه القيم (لتجنب استنتاج علاقة غير صحيحة بين التصنيفات). ويعتمد هذا التمثيل البصري على دالة الخسارة ووظيفة تفعيل الطبقة الأخيرة التي نستخدمها في الشبكة العصبونية. سنتعرّف على المزيد من المعلومات عن ذلك في القسم التالي.