Въведение в Bag of Words и как да го кодирате в Python за NLP

Bag of Words (BOW) е метод за извличане на характеристики от текстови документи. Тези функции могат да се използват за обучение на алгоритми за машинно обучение. Той създава речник на всички уникални думи, срещащи се във всички документи в учебния комплект.

По-просто казано, това е колекция от думи, които представляват изречение с брой на думите и най-вече пренебрегвайки реда, в който се появяват.

BOW е подход, широко използван с:

  1. Обработка на естествен език
  2. Извличане на информация от документи
  3. Класификации на документи

На високо ниво това включва следните стъпки.

Генерираните вектори могат да бъдат въведени във вашия алгоритъм за машинно обучение.

Нека започнем с пример, който да разберем, като вземем някои изречения и генерираме вектори за тях.

Помислете за следните две изречения.

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: Токенизиране на изречение

Ще започнем, като премахнем думите за спиране от изреченията.

Stopwords са думи, които не съдържат достатъчно значение, за да бъдат използвани без нашия алгоритъм. Не бихме искали тези думи да заемат място в нашата база данни или да отнемат ценно време за обработка. За това можем лесно да ги премахнем, като съхраним списък с думи, които смятате за стоп думи.

Токенизацията е акт на разбиване на поредица от низове на парчета като думи, ключови думи, фрази, символи и други елементи, наречени символи . Жетоните могат да бъдат отделни думи, фрази или дори цели изречения. В процеса на токенизация някои знаци като пунктуационни знаци се изхвърлят.

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

За по-надеждна реализация на стоп думи можете да използвате библиотеката на python nltk . Той има набор от предварително дефинирани думи на език. Ето пример:

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

  1. 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.
  2. 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

  1. Wikipedia-BOW
  2. Understanding Bag-of-Words Model: A Statistical Framework
  3. Semantics-Preserving Bag-of-Words Models and Applications