פיתוח מודל לאנליזת סנטימנט באמצעות למידת מכונה ו-TensorFlow

מחבר:
בתאריך:

אנליזת סנטימנט (sentiment analysis) משתמשת במחשבים כדי ללמוד את דעת הקהל מטקסטים שהציבור מפרסם ברשתות חברתיות ובאזור תגובות הקהל בעיתונים. ניתן לבצע את האנליזה באמצעות למידת מכונה מטקסטים בגישה של "עיבוד שפה טבעית" (Natural Language Processing, NLP).

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

אנליזת סנטימנט באמצעות למידת מכונה TensorFlow

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

בשלב ההכנה, נמיר את הטקסטים לוקטורים מספריים כי TensorFlow לא יודע לעבוד ישירות עם טקסטים. ההמרה תכלול:

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

לצורך אימון המודל נשתמש ברשת נאורונית הכוללת 3 סוגי שכבות:

  • ראשית, שכבת embedding אשר דוחסת את הטוקנים לוקטורים בעלי משמעות בהבנת כוונת הכותב.
  • שנית, שכבות של יחידות חוזרות recurrent units מכיוון שסדר המילים הוא חשוב, וכאשר הסדר חשוב משתמשים ב-recurrent units.
  • השכבה האחרונה מסוג dense. בתוכה יסווגו הטקסטים לחיוביים ושליליים.

להורדת קוד הפרויקט שאותו נפתח במדריך

את המדריך פתחתי על סביבת Colab הזמינה לכולם מחשבון הגוגל. בשביל להאיץ את תהליך הלמידה השתמשתי ב- GPU.

 

ייבוא הספריות

נייבא את numpy ו-pandas שיקלו על העבודה עם מערכים מספריים.

import numpy as np
import pandas as pd

TensorFlow 2 היא הספרייה המשמשת ללמידת מכונה. במדריך זה אני משתמש בגרסה העדכנית ביותר נכון לתאריך עריכת המדריך.

התקנת הספרייה:

!pip install tensorflow==2.2.0

נייבא:

import tensorflow as tf
from tensorflow import keras

נוודא:

print(tf.__version__)
2.2.0

 

ייבוא מסד הנתונים

את קובץ ה-zip שהורדתי מ- Kaggle IMDB dataset העליתי לסביבת Colab.

פתחתי תיקייה database שאליה העברתי את מסד הנתונים.

# make a directory to accommodate the dataset
!mkdir -p database
!unzip imdb-review-dataset.zip -d database

נייבא את מסד הנתונים ל-pandas.

df = pd.read_csv('./database/imdb_master.csv',encoding='latin-1')

 

נסקור את מסד הנתונים head, tail, sample:

df.head()

כמה דוגמאות יש לנו?

df.shape # (100000,5)

10,000 דוגמאות.

נשאר עם העמודות הרלוונטיות.

# get rid of the unnecessary columns
df1 = df[['type','review','label']]

אילו קטגוריות יש לנו בסט הנתונים?

# which categories do we have?
df1.label.unique()
array(['neg', 'pos', 'unsup'], dtype=object)

כמה דוגמאות יש לנו מכל קטגוריה?

# the frequency of each category
df1.label.value_counts()
unsup 50000
pos 25000
neg 25000
Name: label, dtype: int64

 

הכנת הנתונים ללמידת מכונה

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

# use only the labeled data
labeled_df = df1.loc[df1.label != 'unsup']
# frequency of each category
labeled_df.label.value_counts()
neg 25000
pos 25000
Name: label, dtype: int64

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

labeled_df['sentiment'] = np.where((labeled_df['label']=='pos'),1,0)

נבדוק:

labeled_df.sample(5)

5 sample reviews from the IMDB dataset after labeling with with numeric values

מעניין לקרוא את אחת הביקורות.

# read a single review
labeled_df.loc[0]['review']
"Once again Mr. Costner has dragged out a movie for far longer than necessary. Aside from the terrific sea rescue sequences, of which there are very few I just did not care about any of the characters. Most of us have ghosts in the closet, and Costner's character are realized early on, and then forgotten until much later, by which time I did not care. The character we should really care about is a very cocky, overconfident Ashton Kutcher. The problem is he comes off as kid who thinks he's better than anyone else around him and shows no signs of a cluttered closet. His only obstacle appears to be winning over Costner. Finally when we are well past the half way point of this stinker, Costner tells us all about Kutcher's ghosts. We are told why Kutcher is driven to be the best with no prior inkling or foreshadowing. No magic here, it was all I could do to keep from turning it off an hour in."

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

 

הפרדה לסט אימון ומבחן

נפריד לסט אימון ומבחן:

# separate into train and test datasets
train_set = labeled_df.loc[labeled_df.type == 'train']
test_set = labeled_df.loc[labeled_df.type == 'test']

 

טוקניזציה

מחשבים צריכים מספרים כדי לעשות למידת מכונה אז במקום לאמן את המודל ישירות על טקסטים ממסד הנתונים נמיר את המילים האינדיבידואליות מהם מורכבים הטקסטים לטוקנים (tokens) שבהם לכל מילה ייחודית בטקסט יוקצה אינדקס מספרי.

במקום לעבוד עם כל המילים במסד הנתונים, נעבוד רק עם 10,000 המילים הנפוצות ביותר שמהם TensorFlow ייצר 10,000 טוקנים.

# Tokenize - computers need numbers instead of words in order to work
# here I limit the number of tokens to the 10,000
# most used words in the dataset
# oov_token - represents the words in the non-training samples that are not
# in the training corpus
from tensorflow.keras.preprocessing.text import Tokenizer num_words = 10000 tokenizer = Tokenizer(num_words=num_words, oov_token='<OOV>')

נסקור את מילון הטוקנים שיצרנו.

# explore the dictionary of tokens
dictionary = tokenizer.word_index
{'the': 1,
 'and': 2,
 'a': 3,
 'of': 4,
 'to': 5,
 'is': 6,
 'br': 7,
 'in': 8,
 'it': 9,
 'i': 10,
 'this': 11,
 'that': 12,
 'was': 13,
 'as': 14,
 'for': 15,
 'with': 16,
 'movie': 17,
 'but': 18,
 'film': 19,
 'on': 20,
 'not': 21,
 'you': 22,
 'are': 23,
 'his': 24,
 'have': 25,
 'be': 26,
 'one': 27,
 'he': 28,
 'all': 29,
 'at': 30,
 'by': 31,
 'an': 32,
 'they': 33,
 'so': 34,
 'who': 35,
 'from': 36,
 'like': 37,
 'or': 38,
 'just': 39,
 'her': 40,
…}

המילה the היא הנפוצה ביותר במסד הנתונים ולכן קיבלה את האינדקס 1, ובמקום ה-17 המילה film.

וגם הפוך. מילון שבו המפתחות הם האינדקסים:

tokenizer.index_word

 

נעשה טוקניזציה בפועל של סט הנתונים המשמש לאימון train_set.

# tokenize - use the tokenizer to convert all the texts in the training set to list of tokens
x_train_tokens = tokenizer.texts_to_sequences(train_set['review'])

נציץ בדוגמה אחת של טקסט שעבר טוקניזציה.

# a single review after tokenization
np.array(x_train_tokens[0])
array([ 64, 4, 3, 128, 35, 45, 7159, 1395, 15, 3, 4968,
 537, 41, 16, 3, 615, 129, 12, 6, 3, 1322, 472,
 4, 1877, 202, 3, 6407, 309, 6, 661, 82, 32, 2018,
 1125, 2750, 31, 1, 947, 4, 44, 5452, 480, 9, 2842,
 1877, 1, 223, 55, 16, 54, 825, 1353, 853, 231, 9,
 39, 96, 122, 1499, 57, 143, 36, 1, 1006, 142, 26,
 661, 122, 1, 411, 58, 94, 2231, 308, 765, 5, 3,
 873, 20, 3, 1891, 652, 44, 126, 71, 22, 233, 101,
 16, 47, 49, 633, 31, 731, 78, 731, 406, 3207, 2,
 8497, 67, 26, 107, 3189])

הפונקציה הבאה תאפשר לנו להמיר את הטוקנים חזרה לטקסט:

# Nice, but what is in the text.
# The following function converts the tokens back to the original text.
def tokens_to_text(dict, tokens):
  words = []
  for token in tokens:
    words.append(list(dict.keys())[list(dict.values()).index(token)])

  return ' '.join(words)

נבדוק את הפונקציה:

# Let's test it
tokens_to_text(dictionary, np.array(x_test_tokens[0]))
"once again mr costner has dragged out a movie for far longer than necessary aside from the terrific sea rescue sequences of which there are very few i just did not care about any of the characters most of us have ghosts in the closet and character are realized early on and then forgotten until much later by which time i did not care the character we should really care about is a very cocky the problem is he comes off as kid who thinks he's better than anyone else around him and shows no signs of a closet his only appears to be winning over costner finally when we are well past the half way point of this stinker costner tells us all about ghosts we are told why is driven to be the best with no prior or no magic here it was all i could do to keep from turning it off an hour in"

כפי שאפשר לראות, הטוקניזציה ניקתה את הטקסט מסימני פיסוק והפכה את כל האותיות לקטנות.

נעשה טוקניזציה של סט הנתונים test_set.

# tokenize the test set
x_test_tokens = tokenizer.texts_to_sequences(test_set['review'])

 

ריפוד וקיצוץ

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

# pad the sequences so they all have the same length when fed into the neural net
# first, determine the maximum length from the average plus 2 standard 
# deviations of all the vector lengths. 
# to allow the inclusion of the full length of 95% of the vectors.
import math

lengths = [len(item) for item in x_train_tokens + x_test_tokens]
arr = np.array(lengths)
mean = np.mean(arr, axis=0)
stdev = np.std(arr, axis=0)
max_len = math.floor(mean + 2 * stdev)
max_len

אורך הוקטור 583 ע"פ ממוצע ו-2 סטיות תקן של אורכי כל הוקטורים. מה שיספיק כדי לכלול את האורך המלא של 95% מהוקטורים.

את הריפוד והקיצוץ של הווקטורים לאורך הרצוי יבצע למעננו TensorFlow:

# pad the sequences with the 'pre' parameter so the padding zeroes will be at the beginning
# because the last part of each sequence convey extra meaning for the recurrent units of the GRU
# if a text is longer than the max_len it will be truncated at the beginning

from tensorflow.keras.preprocessing.sequence import pad_sequences

x_train = pad_sequences(x_train_tokens, maxlen=max_len, 
                                    padding='pre', truncating='pre')

הפרמטר pre גורם להוספת האפסים המרפדים בתחילת הוקטור ולא בסופו כי המשקל של היחידות האחרונות הוא גבוה יותר כשעובדים עם recurrent units בשל אפקט הגרדיאנטים הנעלמים vanishing gradients שמשכיח את האפקט של תחילת הוקטור עד שמגיעים לסופו.

נרפד גם את סט המבחן.

x_test = pad_sequences(x_test_tokens, maxlen=max_len, padding='pre', truncating='pre')

הפרדת עמודת המטרה

y_train = np.array(train_set.sentiment)
y_test = np.array(test_set.sentiment)

 

בניית ואימון הרשת הנוירונית

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

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

לאחר שכבת ה-Embedding אנו משתמשים בשכבות Recurrent Neural Network בארכיטקטורת GRU. אנו משתמשים ב-RNN מאחר וסדר המילים חשוב וכאשר אנו מנסים לפתח מודל עבור נתונים שבהם יש משמעות לסדר אנו משתמשים בRNN - זה יכול להיות בארכיטקטורת GRU או LSTM. אני מעדיף את GRU על LSTM כי המבנה של היחידה החוזרת הוא פשוט ואלגנטי.

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

# define the model
from tensorflow.keras.models import Sequential
from tensorflow.keras.layers import Embedding, GRU, Dense

# set the model
# 128 output 
model = Sequential()

# Embedding layer - captures relationships between words
# in our case, it allows us to find if a text is more positive or more negative 
# from the sequence of words

# the number of words in the number of inputs 
# the number of outputs is 128 because that's what I found in the documentation
model.add(Embedding(num_words, 128, input_length=max_len))

# 3 GRU layers - recurrent neural network because it takes the sequence of data
# into account when it's learning
# with fewer nodes in the consecutive layers
# to allow the later layers to see less details and more of the whole picture
# and so to be able to generalize - the whole mark of a meaningful learning process
# the first 2 GRU layers need to return a sequence of outputs
# to feed to the next GRU layer
model.add(GRU(units=32, return_sequences=True))
model.add(GRU(units=16, return_sequences=True))

# the 3rd GRU layer need to feed its output to the dense layer
# so we don't return a sequence
model.add(GRU(units=8, return_sequences=False))

# dense layer calculates a value between 0 and 1 as the output
model.add(Dense(1, activation='sigmoid'))

אני משתמש פה ב-3 שכבות RNN שבכל אחת יש פחות יחידות מאשר בקודמת כדי לאפשר לשכבות המתקדמות לראות פחות פרטים ויותר מהתמונה הגדולה וכך להכליל - שזו המהות של תהליך למידה משמעותי.

השכבה האחרונה ברשת היא שכבת dense אשר מסווגת את הטקסטים לאחת משתי קטגוריות 1 או 0 - רגש חיובי או שלילי.

נקמפל את המודל עם פרמטרים שמתאימים לסיווג בינארי.

# compile the model
# 2 classes hence the loss function is 'binary_crossentropy'
model.compile(optimizer='adam', loss='binary_crossentropy', metrics=['acc'])

נציג את סיכום המודל:

# summarize the model
print(model.summary())

נאמן את המודל:

# set the conditions for early stopping
from tensorflow.keras.callbacks import EarlyStopping
es = EarlyStopping(patience = 2, monitor='val_loss')

# fit
model.fit(x_train, y_train,
          batch_size=32,
          epochs=100,
          validation_data=(x_test, y_test), 
          callbacks=[es])
Epoch 1/100
782/782 [==============================] - 69s 89ms/step - loss: 0.4332 - acc: 0.8021 - val_loss: 0.3377 - val_acc: 0.8639
Epoch 2/100
782/782 [==============================] - 68s 88ms/step - loss: 0.2530 - acc: 0.9046 - val_loss: 0.3069 - val_acc: 0.8749
Epoch 3/100
782/782 [==============================] - 69s 88ms/step - loss: 0.1702 - acc: 0.9400 - val_loss: 0.3349 - val_acc: 0.8625
Epoch 4/100
782/782 [==============================] - 68s 88ms/step - loss: 0.1153 - acc: 0.9632 - val_loss: 0.3762 - val_acc: 0.8754

 

הערכת המודל

אחרי שאימנו את המודל נרצה לדעת עד כמה הוא אפקטיבי. המדדים loss ו-accuracy יסייעו לנו להעריך את המודל.

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

loss, accuracy = model.evaluate(x_test, y_test, verbose=1)
print('Accuracy: %0.2f' % (accuracy*100))
782/782 [==============================] - 18s 23ms/step - loss: 0.3762 - acc: 0.8754
Accuracy: 87.54

87% דיוק אחרי 4 מחזורים בלבד. יפה מאוד!

מעניין לבדוק האם המודל שוגה יותר בסיווג ביקורות חיוביות או שליליות לשם כך נשתמש ב-confusion matrix.

ראשית, נשתמש במודל כדי למצוא את הסנטימנט של קבוצת הביקורת.

predictions = model.predict(x_test)
predictions
array([[0.08226028],
  [0.05003896],
  [0.03048536],
  ...,
  [0.5752455 ],
  [0.9702544 ],
  [0.9014092 ]], dtype=float32)

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

# predicted class indices
y_pred = np.where((predictions > 0.5),1,0)

y_pred
array([[0],
       [0],
       [0],
       ...,
       [1],
       [1],
       [1]])

נציג את ה-confusion מטריקס:

from sklearn.metrics import classification_report, confusion_matrix
print('Confusion Matrix')
cf_matrix = confusion_matrix(y_test, y_pred)
print(cf_matrix)

confusion matrix for sentiment analysis with keras

אותי צורת ההצגה הזו קצת מבלבלת. הקוד הבא עזר לי להבין מי נגד מי:

true_pos = 0
true_neg = 0
false_pos = 0
false_neg = 0
for cnt,itm in enumerate(y_test):
  pred = y_pred[cnt]
  if itm == pred and itm == 1:
    true_pos = true_pos +1
  elif itm == pred and itm == 0:
    true_neg = true_neg +1
  elif itm != pred and itm == 0:
    false_pos = false_pos +1
  elif itm != pred and itm == 1:
    false_neg = false_neg +1
    
print('true_pos: ', true_pos)
print('true_neg: ', true_neg)
print('false_pos: ', false_pos)
print('false_neg: ', false_neg)
true_pos:  10911
true_neg:  10973
false_pos:  1527
false_neg:  1589

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

כשמציגים את העבודה ברבים כדאי להעזר בקוד הבא שמציג את ה-confusion matrix כמפת חום:

import matplotlib.pyplot as plt 
import seaborn as sns

ax= plt.subplot()
sns.heatmap(cf_matrix, annot=True, ax = ax, fmt='d', cmap='Blues', cbar=False)

# labels, title and ticks
ax.set_title('Confusion Matrix')
ax.set_xlabel('Predicted labels')
ax.set_ylabel('True labels')
ax.xaxis.set_ticklabels(['Negative', 'Positive']) 
ax.yaxis.set_ticklabels(['Negative', 'Positive'])

confusion matrix as seaborn heatmap

עוד דרך להסתכל על מידת הדיוק של הסיווג היא באמצעות classification report:

print('Classification Report')
target_names = ['Negative', 'Positive']
print(classification_report(y_test, y_pred, target_names=target_names))

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

Classification Report
              precision    recall  f1-score   support

    Negative       0.87      0.88      0.88     12500
    Positive       0.88      0.87      0.88     12500

    accuracy                           0.88     25000
   macro avg       0.88      0.88      0.88     25000
weighted avg       0.88      0.88      0.88     25000

נראה דוגמה אחת שהמודל שגה בסיווג שלה.

# see the misclassified samples
incorrect = (y_pred.reshape(25000,) != test_set['sentiment'])
incorrect_texts = test_set['review'][incorrect]
incorrect_texts
4        Brass pictures (movies is not a fitting word f...
11       First of all, I would like to say that I am a ...
16       At the bottom end of the apocalypse movie scal...
17       Earth has been destroyed in a nuclear holocaus...
26       Everything everyone has said already pretty mu...
                               ...                        
24972    When i first saw the movie being advertised i ...
24977    This is not a movie that I would typically wat...
24983    THE GREATEST GAME EVER PLAYED (TGGEP, 2005) is...
24992    As you know "The Greatest Game Ever Played" is...
24997    From the start of "The Edge Of Love", the view...
Name: review, Length: 3116, dtype: object

נשלוף את אחת הדוגמאות:

incorrect_texts[16]
"At the bottom end of the apocalypse movie scale is this piece of pish called 'The Final Executioner'.. at least where I come from. A bloke is trained by an ex-cop to seek vengeance on those that killed his woman and friends in cold blood.. and that's about it. Lots of fake explosions and repetitive shootings ensue. Has one of the weirdest array of costumes I've seen in a film for a while, and a massive fortress which is apparently only run by 7 people. GREAT job on the dubbing too guys(!) Best moment: when our hero loses a swordfight and is about to be skewered through the neck, he just gets out his gun and BANG! Why not do that earlier? It's a mystery. As is why anyone would want to sit through this in the first place. I'm still puzzling over that one myself now.. 2/10"
test_set.loc[16]
type                                                      test
review       At the bottom end of the apocalypse movie scal...
label                                                      neg
sentiment                                                    0
Name: 16, dtype: object

ומה המודל שלנו חזה?

y_pred[16]
array([1])

אמנם הביקורת מסווגת כשלילית במסד הנתונים IMDB אולם המודל שפיתחנו מסווג את הביקורת דווקא כחיובית כנראה בגלל האחוז הגבוה של מילים חיוביות.

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

reviews1 =[
   'Bad movie, worst actors, and really bad director. this assembly of misfits is better off the film industry for good',
   'Hillarious, brilliant, kudos, two thumbs up. one of the greatest movies you can see this year',
   'god is a dj and the masses are his loyal servants',
   'I dont want to waste your time but this disaster movie is probably one of the worst movies to ever miss despite the lack of quality and the horrible production. who knows it might even become this generations rockies horror show so you actually want to watch it'
]
tokenized = tokenizer.texts_to_sequences(reviews1)
padded = pad_sequences(tokenized, maxlen=max_len, padding='pre', truncating='pre')
model.predict(padded)

והרי התחזית:

array([[0.00665382],
       [0.9849937 ],
       [0.52168566],
       [0.01111782]], dtype=float32)

טקסט 1 השלילי מאוד וגם טקסט 2 החיובי מאוד סווגו נכונה על ידי המערכת. טקסט 3 בעל האופי הניטרלי אכן קיבל ציון בינוני. אבל טקסט 4 שמשתמש בהרבה מילים "שליליות" כדי להמריץ אנשים לצפות בסרט פולחן מצליח להטעות את המודל שמסווג אותו כשלילי.

 

סיכום

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

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

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

לכל המדריכים בסדרה על למידת מכונה

 

אהבתם? לא אהבתם? דרגו!

0 הצבעות, ממוצע 0 מתוך 5 כוכבים

 

 

הוסף תגובה חדשה

 

= 5 + 8