שלב 3: הכנת הנתונים

כדי שנוכל להזין את הנתונים שלנו במודל, צריך להמיר אותם לפורמט שהמודל יכול להבין.

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

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

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

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

  2. ונקובר: הגדירו תכונה מספרית טובה לאופי הטקסטים האלה.

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

וקטורים מסוג N [אפשרות א']

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

בוקטור n גרם, הטקסט מיוצג כאוסף של n גרם ייחודי: קבוצות של n אסימונים סמוכים (בדרך כלל מילים). חשוב על הטקסט The mouse ran up the clock. כאן, המילה unigrams (n = 1) היא ['the', 'mouse', 'ran', 'up', 'clock'], המילה Bigrams (n = 2) הם ['the mouse', 'mouse ran', 'ran up', 'up the', 'the clock'], וכן הלאה.

יצירת אסימונים

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

ניצחון

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

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-gram, בדרך כלל אנחנו מבצעים וקטורים באמצעות אחת מהאפשרויות הבאות.

קידוד לוהט: כל טקסט לדוגמה מיוצג וקטור המציין את נוכחותו או את קיומו של אסימון בטקסט.

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

קידוד הספירה: כל טקסט לדוגמה מיוצג וקטור המציין את ספירת האסימון בטקסט. שימו לב שהאלמנט התואם לרכיב ה&

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

קידוד ff-idf: הבעיה בשתי הגישות שלמעלה היא שמילים נפוצות שמתרחשות בתדירות דומה בכל המסמכים (כלומר, מילים שאינן ייחודיות במיוחד לדגימות הטקסט במערך הנתונים) לא יינקטו סנקציות. לדוגמה, מילים כמו "a" יופיעו לעתים קרובות בכל הטקסטים. לכן ספירת אסימונים גבוהה יותר של המילה "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-גרם. עם זאת, חשוב לזכור שהפעולה הזאת תופסת יותר זיכרון (בשביל שהיא משתמשת בייצוג של נקודות צפות) ומקדישה יותר זמן לחישוב, במיוחד כשמדובר במערכי נתונים גדולים (במקרים מסוימים, התהליך עשוי להימשך זמן רב יותר).

בחירת תכונה

כשאנחנו ממירים את כל הטקסטים במערך נתונים לאסימוני uni+bigram, אנחנו עשויים לקבל עשרות אלפי אסימונים. לא כל אסימונים/תכונות אלה תורמים לחיזוי תוויות. כך אנחנו יכולים לשחרר אסימונים מסוימים, למשל אלה שקורים לעיתים רחוקות במערך הנתונים. אנחנו יכולים גם למדוד את חשיבות התכונה (עד כמה כל אסימון תורם לחיזויים של תוויות), ולכלול רק את האסימונים האינפורמטיביים ביותר.

יש פונקציות סטטיסטיות רבות שמשתמשות בתכונות ובתוויות המתאימות ויוצרות ציון של חשיבות התכונה. שתי פונקציות נפוצות הן f_classif ו-chi2. מהניסויים שערכנו עולה כי שתי הפונקציות יוצרות ביצועים טובים באופן שווה.

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

K עליון לעומת דיוק

איור 6: תכונות K מובילות לעומת דיוק. מערכי נתונים ברמת דיוק של כ-20 אלף תכונות מובילות.

נורמליזציה

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

הקוד הבא משלב את כל השלבים שלמעלה:

  • משייכים דוגמאות טקסט למילה אחת + ביגרם,
  • וצריך לבצע קידוד באמצעות 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 גרם, אנחנו משמיטים מידע רב לגבי סדר המילים והדקדוק (לכל היותר נצליח לשמור פרטי הזמנה חלקיים כאשר n > 1). התהליך הזה נקרא 'גישה למילים'. משתמשים בייצוג הזה בשילוב עם מודלים שלא מביאים בחשבון את הסדר, כמו רגרסיה לוגיסטית, תפיסות רב-שכבתיות, מכונות להגברה הדרגתית ומכונות תמיכה וקטוריות.

רצף וקטורים [Option B]

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

עבור דוגמאות טקסט מסוימות, סדר המילים הוא קריטי למשמעות הטקסט. לדוגמה, המשפטים "פעם שנאתי את הנסיעה היומית שלי. האופניים החדשים שלי שינו לחלוטין את המשמעות הזו לאחר קריאתם. מודלים כמו CNN/RNN יכולים להסיק את המשמעות של סדר המילים בדגימה. במודלים האלה אנחנו מייצגים את הטקסט כרצף של אסימונים, ששומרים על הסדר.

יצירת אסימונים

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

ניצחון

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

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 , n שבו הגודל של אוצר המילים הוא 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 התכונות הנפוצות ביותר מספיק בדרך כלל. הדבר נכון גם לגבי מודלים של ngram (ראו איור 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 כדי לייצג אותם. ברשת, הרשת תשתמש בוקטורים לוהטים כדי לייצג את הערכים האלה (כדי להימנע מהסקת קשר שגוי בין תוויות). הייצוג הזה תלוי בפונקציית אובדן הנתונים ובפונקציית ההפעלה האחרונה שאנחנו משתמשים בה ברשת נוירונים. הסבר על הנושאים האלה מופיע בקטע הבא.