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

ניתן להזין וקטורים שנוצרו לאלגוריתם למידת המכונה שלך.
נתחיל בדוגמה להבנה על ידי לקיחת כמה משפטים ויצירת וקטורים עבורם.
שקול את שני המשפטים להלן.
1. "John likes to watch movies. Mary likes movies too."
2. "John also likes to watch football games."
ניתן לייצג את שני המשפטים הללו גם באוסף של מילים.
1. ['John', 'likes', 'to', 'watch', 'movies.', 'Mary', 'likes', 'movies', 'too.']
2. ['John', 'also', 'likes', 'to', 'watch', 'football', 'games']
יתר על כן, עבור כל משפט, הסר מספר מופעים של המילה והשתמש בספירת המילים כדי לייצג זאת.
1. {"John":1,"likes":2,"to":1,"watch":1,"movies":2,"Mary":1,"too":1}
2. {"John":1,"also":1,"likes":1,"to":1,"watch":1,"football":1, "games":1}
בהנחה שמשפטים אלה הם חלק ממסמך, להלן תדירות המילה המשולבת לכל המסמך שלנו. שני המשפטים נלקחים בחשבון.
{"John":2,"likes":3,"to":2,"watch":2,"movies":2,"Mary":1,"too":1, "also":1,"football":1,"games":1}
אוצר המילים הנ"ל מכל המילים במסמך, עם ספירת המילים בהתאמה, ישמש ליצירת הווקטורים לכל אחד מהמשפטים.
אורך הווקטור תמיד יהיה שווה לגודל אוצר המילים. במקרה זה אורך הווקטור הוא 11.
על מנת לייצג את המשפטים המקוריים שלנו בווקטור, כל וקטור מאותחל עם כל האפסים - [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
לאחר מכן איטרציה והשוואה עם כל מילה באוצר המילים שלנו, והגדלת הערך הווקטורי אם למשפט יש את המילה הזו.
John likes to watch movies. Mary likes movies too.[1, 2, 1, 1, 2, 1, 1, 0, 0, 0]
John also likes to watch football games.[1, 1, 1, 1, 0, 0, 0, 1, 1, 1]
לדוגמא, במשפט 1 המילה likes
מופיעה במיקום שני ומופיעה פעמיים. אז האלמנט השני של הווקטור שלנו למשפט 1 יהיה 2: [1, 2, 1, 1, 2, 1, 1, 0, 0, 0]
הווקטור תמיד פרופורציונלי לגודל אוצר המילים שלנו.
מסמך גדול שבו אוצר המילים שנוצר הוא עצום עלול לגרום לווקטור עם הרבה 0 ערכים. זה נקרא וקטור דליל .וקטורים דלילים דורשים יותר זיכרון ומשאבים חישוביים בעת דוגמנות. המספר העצום של מיקומים או ממדים יכול להפוך את תהליך הדוגמנות למאתגר מאוד עבור אלגוריתמים מסורתיים.
קידוד אלגוריתם ה- BOW שלנו
הכניסה לקוד שלנו תהיה משפטים מרובים והפלט יהיה הווקטורים.
מערך הקלט הוא זה:
["Joe waited for the train", "The train was late", "Mary and Samantha took the bus",
"I looked for Mary and Samantha at the bus station",
"Mary and Samantha arrived at the bus station early but waited until noon for the bus"]
שלב 1: אסימון משפט
נתחיל בהסרת מילות עצירה מהמשפטים.
מילות עצירה הן מילים שאינן מכילות מספיק משמעות לשימוש ללא האלגוריתם שלנו. לא נרצה שמילים אלה יתפסו מקום במאגר המידע שלנו, או שייקחו זמן עיבוד יקר. לשם כך, אנו יכולים להסיר אותם בקלות על ידי אחסון רשימת מילים שאתה רואה כמילות עצירה.
טוקניזציה היא פעולה של פירוק רצף של מיתרים לחתיכות כגון מילים, מילות מפתח, ביטויים, סמלים ואלמנטים אחרים הנקראים אסימונים . אסימונים יכולים להיות מילים בודדות, ביטויים או אפילו משפטים שלמים. בתהליך האסימון, תווים מסוימים כמו סימני פיסוק מושלכים.
def word_extraction(sentence): ignore = ['a', "the", "is"] words = re.sub("[^\w]", " ", sentence).split() cleaned_text = [w.lower() for w in words if w not in ignore] return cleaned_text
ליישום חזק יותר של מילות עצירה , תוכלו להשתמש בספריית nythk של python . יש לו סט של מילים שהוגדרו מראש לכל שפה. הנה דוגמה:
import nltkfrom nltk.corpus import stopwords set(stopwords.words('english'))
שלב 2: החל טוקניזציה על כל המשפטים
def tokenize(sentences): words = [] for sentence in sentences: w = word_extraction(sentence) words.extend(w) words = sorted(list(set(words))) return words
השיטה מחרגת את כל המשפטים ומוסיפה את המילה שחולצה למערך.
התפוקה של שיטה זו תהיה:
['and', 'arrived', 'at', 'bus', 'but', 'early', 'for', 'i', 'joe', 'late', 'looked', 'mary', 'noon', 'samantha', 'station', 'the', 'took', 'train', 'until', 'waited', 'was']
שלב 3: בנה אוצר מילים וצור וקטורים
השתמש בשיטות שהוגדרו בשלבים 1 ו -2 כדי ליצור את אוצר המילים של המסמך ולחלץ את המילים מהמשפטים.
def generate_bow(allsentences): vocab = tokenize(allsentences) print("Word List for Document \n{0} \n".format(vocab));
for sentence in allsentences: words = word_extraction(sentence) bag_vector = numpy.zeros(len(vocab)) for w in words: for i,word in enumerate(vocab): if word == w: bag_vector[i] += 1 print("{0}\n{1}\n".format(sentence,numpy.array(bag_vector)))
הנה הקלט והביצוע המוגדרים של הקוד שלנו:
allsentences = ["Joe waited for the train train", "The train was late", "Mary and Samantha took the bus",
"I looked for Mary and Samantha at the bus station",
"Mary and Samantha arrived at the bus station early but waited until noon for the bus"]
generate_bow(allsentences)
וקטורי הפלט של כל אחד מהמשפטים הם:
Output:
Joe waited for the train train[0. 0. 0. 0. 0. 0. 1. 0. 1. 0. 0. 0. 0. 0. 0. 0. 0. 2. 0. 1. 0.]
The train was late[0. 0. 0. 0. 0. 0. 0. 0. 0. 1. 0. 0. 0. 0. 0. 1. 0. 1. 0. 0. 1.]
Mary and Samantha took the bus[1. 0. 0. 1. 0. 0. 0. 0. 0. 0. 0. 1. 0. 1. 0. 0. 1. 0. 0. 0. 0.]
I looked for Mary and Samantha at the bus station[1. 0. 1. 1. 0. 0. 1. 1. 0. 0. 1. 1. 0. 1. 1. 0. 0. 0. 0. 0. 0.]
Mary and Samantha arrived at the bus station early but waited until noon for the bus[1. 1. 1. 2. 1. 1. 1. 0. 0. 0. 0. 1. 1. 1. 1. 0. 0. 0. 1. 1. 0.]
As you can see, each sentence was compared with our word list generated in Step 1. Based on the comparison, the vector element value may be incremented. These vectors can be used in ML algorithms for document classification and predictions.
We wrote our code and generated vectors, but now let’s understand bag of words a bit more.
Insights into bag of words
The BOW model only considers if a known word occurs in a document or not. It does not care about meaning, context, and order in which they appear.
This gives the insight that similar documents will have word counts similar to each other. In other words, the more similar the words in two documents, the more similar the documents can be.
Limitations of BOW
- Semantic meaning: the basic BOW approach does not consider the meaning of the word in the document. It completely ignores the context in which it’s used. The same word can be used in multiple places based on the context or nearby words.
- Vector size: For a large document, the vector size can be huge resulting in a lot of computation and time. You may need to ignore words based on relevance to your use case.
This was a small introduction to the BOW method. The code showed how it works at a low level. There is much more to understand about BOW. For example, instead of splitting our sentence in a single word (1-gram), you can split in the pair of two words (bi-gram or 2-gram). At times, bi-gram representation seems to be much better than using 1-gram. These can often be represented using N-gram notation. I have listed some research papers in the resources section for more in-depth knowledge.
You do not have to code BOW whenever you need it. It is already part of many available frameworks like CountVectorizer in sci-kit learn.
Our previous code can be replaced with:
from sklearn.feature_extraction.text import CountVectorizervectorizer = CountVectorizer()X = vectorizer.fit_transform(allsentences)print(X.toarray())
It’s always good to understand how the libraries in frameworks work, and understand the methods behind them. The better you understand the concepts, the better use you can make of frameworks.
Thanks for reading the article. The code shown is available on my GitHub.
You can follow me on Medium, Twitter, and LinkedIn, For any questions, you can reach out to me on email (praveend806 [at] gmail [dot] com).
Resources to read more on bag of words
- Wikipedia-BOW
- Understanding Bag-of-Words Model: A Statistical Framework
- Semantics-Preserving Bag-of-Words Models and Applications