تکنیک Bag of Words در پردازش زبان طبیعی | آموزش کامل همراه با پروژهی عملی spam detection
تکنیک Bag of Words (BOW) یکی از مهمترین و پرکاربردترین روشها در استخراج ویژگیهای متنی در حوزهی پردازش زبان طبیعی (NLP) است. یکی از چالشهای اصلی در دادههای متنی، تفاوت اندازهی بردار ویژگی بین نمونه ها است که فرآیند آموزش مدلهای یادگیری ماشین را با چالش روبرو میکند. روش BOW با تبدیل متن به یک بردار ویژگی یکسان و قابل پردازش، این مشکل را برطرف میکند و پایهی بسیاری از مدلهای طبقه بندی متون است. برای مثال در تحلیل احساس افراد از روی کامنتها، یا تشخص موضوع یک متن، یا تشخیص پیامهای هرز میتوان از این تکنیک استفاده کرد. در این پست ابتدا با مفهوم BOW، نحوهی ساخت بردار ویژگی و کاربردهای آن در sentiment analysis و طبقه بندی متون آشنا میشوید. سپس یک پروژهی عملی تشخیص پیامکهای Spam رو به صورت گام به گام پیاده سازی خواهیم کرد. در پایان نیز یک تمرین چالشی در زمینهی تحلیل احساسات افراد در مورد فیلمها ارائه شده تا بتوانید مهارتهای خود در پروژه های واقعی NLP محک بزنید.
صورت مسئله و چالش در طبقه بندی کامنتها با NLP
فرض کنید در یک شرکت تولید کننده محصولات بهداشتی به عنوان مهندس هوش مصنوعی (AI Engineer) مشغول به کار هستید. این شرکت به تازگی یک محصول جدید وارد بازار کرده و برای آن یک کمپین تبلیغاتی در شبکه های اجتماعی راهاندازی شده است. کاربران در زیر این پستها کامنتهای متفاوتی نوشته اند.
حال از شما میخواهند سیستمی طراحی کنید که بتواند این کامنتها را طبقه بندی کند (sentiment analysis). یعنی مدل شما باید تشخیص دهد:
- احساس کاربر مثبت است؟ (positive)
- احساس کاربر منفی است؟ (negative)
- یا اینکه کامنت کاملا نا مرتبط است؟ (irrelevant)
بنابراین سیستم باید کامنتها را دقیقاً در سه گروه دستهبندی کند: positive-negative-irrelevant
نمونه کامنتها
- “I absolutely love this new hand sanitizer! It smells amazing and doesn’t dry out my skin.” (positive)
- “Does anyone know where I can buy this in Canada?” (irrelevant)
- “My cousin works at this company!” (irrelevant)
- “Finally a product that keeps my hands soft even after multiple washes. Totally worth it!” (positive)
- “The smell is way too strong. It gives me a headache.” (negative)
- “I don’t see any difference after using it. Total waste of money.” (negative)
- “The bottle leaks easily, very poor design.” (negative)
- “This is the best soap I’ve used so far. The scent lasts all day!” (positive)
- “I tried it last week and my skin feels so fresh and clean. Great job on this one!” (positive)
- “I saw their commercial during a football game.” (irrelevant)
- “The packaging looks premium and the quality matches it. I’m definitely buying again.” (positive)
- “This reminds me of another product I used years ago.” (irrelevant)
- “This product made my hands really dry after just one use. Not happy.” (negative)
- “I expected something better for the price. It’s just like any regular soap.” (negative)
- “It irritated my skin badly. Definitely won’t buy again.” (negative)
- “I think the brand logo looks really cool.” (irrelevant)
چرا باید کامنتها را به بردار عددی تبدیل کنیم؟
به این کامنت نگاه کنید:
“This is the best soap I’ve used so far. The scent lasts all day!” –> (positive)
این جمله از چندین کلمه تشکیل شده است. اما میدانیم که الگوریتمهای یادگیری ماشین روی رشته کار نمیکنند! آنها فقط اعداد را پردازش میکنند.
پس ما باید این متن را تبدیل کنیم به:
- یک بردار ویژگی (Feature Vector) شامل مقادیر عددی
- یا چندین بردار (اگر مدل ما از نوع دنبالهای باشد- مثال شبکه های بازگشتی RNNs)
اینجاست که بحث اصلی مطرح میشود:
برای هر کلمه در کامنت یک بردار ویژگی بسازیم یا برای کل کامنت (برای همه کلمات) فقط یک بردار ویژگی؟
پاسخ دقیقاً وابسته است به نوع مدلی که انتخاب میکنید:
- اگر از مدلهای کلاسیک استفاده کنیم، مثل MLP, SVM, LDA, Decision Tree, random forest و غیره. در این حالت، هر کامنت فقط باید یک بردار ویژگی داشته باشد. و تمام کامنتها/نمونه ها نیز باید بردار ویژگی ای با طول یکسان داشته باشند.
- اگر از شبکه های بازگشتی مثل RNN / LSTM / GRU استفاده کنیم، (که در پستهای بعدی توضیح داده خواهند شد)، در اینصورت، باید به ازای هر کلمه یک بردار ویژگی بسازیم. یعنی برای هر نمونه/کامنت چندین بردار ویژگی خواهیم داشت و این همان چیزی است که RNNها دوست دارند: داده دنبالهای/sequence
چالش اصلی در sentiment analysis: ساخت بردار ویژگی با اندازه یکسان
فرض کنید یک lookup table داریم که برای هر کلمه یک مقدار عددی یا یک بردارعددی (مثلا برای هر کلمه یک بردار ویژگی با اندازه 1*100) تعریف شده است.
رویکرد اول: ساخت lookup table و اختصاص یک مقدار برای هر کلمه
اگر بخواهید از ساده ترین روش استفاده کنید، ابتدا یک lookup table یا همان دایره لغات میسازید و برای هر کلمه، یک مقدار عددی اختصاص میدهید، و سپس برای هرکامنت که شامل تعداد کلمه هست همانند زیر بردار ویژگی میسازید.
- کلمات موجود در کامنت را پیدا میکنید
- مقدار عددی هر کلمه را از جدول ورمیدارید
- آنها را کنار هم می چینید و یک بردار ویژگی می سازید
اما مشکل چیه؟ کامنتها طول یکسانی نخواهند داشت!!! چرا که هر کامنت شامل تعداد کلمات متفاوتی است.پس بردار ویژگی آنها نیز طول متفاوت پیدا میکنند و ما عملا نمتیوانیم از مدلهایی مثل MLP و SVM برای این مسئله استفاده کنیم!
ایدهی استفاده از Padding
از طرفی اگه از Padding برای حل این مسئله استفاده کنیم، یعنی یک اندازه خاص مثلا 50000 در نظر بگیریم، هر کامنتی که بردار ویژگی اش کمتر از 50000 بود، برای جبران اون اندازه کوچک، یک تعدادی صفر به بردار بچسبانیم تا اندازه اش برابر با 50000 شود. و اونهایی که بزرگتر هستند رو کراپ کنیم. یعنی تا 50000 مقدار ورداریم و مابقی رو بریزیم بیرون!!
به عبارت ساده تر میتوان گفت:
- یک اندازه ثابت (مثلاً 50,000) انتخاب میکنیم
- اگر بردار ویژگی کامنت کوتاه تر بود، چندین صفر اضافه میکنیم
- اگر طولانی تر بود، آن را کراپ میکنیم
اما این راه حل خودش مشکلات جدی دارد!!
- صفرهای اضافه شده میتوانند مدل را دچار خطا و سوگیری کنند.
- کراپ کردن کامنتهای طولانی ممکن است باعث حذف کلمات مهم و احساسی شود.
- انتخاب اندازه مناسب دشوار خواهد بود
جدا از sparse شدن بردار ویژگی (اضافه شدن صفرهای زیاد برای کامنتهای کوتاه)، بردار ویژگی بزرگ باعث افزایش تصادعدی تعداد پارامترهای شبکه عصبی و افزایش احتمال overfitting شبکه را به همراه خواهد داشت.
بنابراین این رویکرد برای MLP و مدلهای کلاسیک مناسب نیست.
نتیجهگیری این بخش، ما برای استفاده از روشهایی مانند MLP به روشی نیاز داریم که:
- از یک کامنت با طول متغیر، یک بردار ویژگی با اندازه ثابت بسازد، بدون اینکه اطلاعات مهم از بین برود
و این دقیقاً جایی است که تکنیک Bag of Words (BOW) وارد میشود. در بخش بعد، به صورت عملی توضیح خواهیم داد که چگونه BOW این مشکل را حل میکند و چگونه میتوان از آن برای طبقه بندی احساسات و تشخیص کامنتهای اسپم استفاده کرد. کد پایتون هم اینجا و هم در گیت هاب بارگزاری میشود.
راهحل اصلی: استفاده از تکنیک Bag Of Words (BOW)
پس از بررسی چالشهای طول متغیر کامنتها، به یک راه حل بسیار کاربردی یعنی تکنیک Bag of words میرسیم.
- این روش با یک ترفند ساده اما هوشمندانه ثابت میکند که بدون حذف هیچ اطلاعاتی از متن میتوان برای تمام کامنتها یک بردار ویژگی با اندازه ثابت ساخت.
- از طرفی این تکنیک بردار ویژگی با حجمی بسیار کمتر نسبت به رویکردهایی مثل Paddingمیسازد که مناسب برای مدلهای کلاسیک مثل MLP ،SVM است
- از همه مهمتر یک تکنیک قابل درک هست و پیاده سازی بسیار سریع و آسانی دارد.
البته BOW محدودیتهایی دارد که بعداً توضیح میدهیم و در پستهای بعدی روشهای پیشرفته تر مثل TF-IDF و Word Embedding و RNNها را معرفی خواهیم کرد.
مراحل اجرای تکنیک Bag of Words (BOW)
گام اول: ساخت vocabulary یا همان دایره لغت/واژگان
در این مرحله تمام کامنتها بررسی شده و تمام کلمات منحصر به فرد (حداقل یکبار ظاهر شدهاند) استخراج میشوند.
در نهایت یک جدول یا lookup table خواهیم داشت که هر کلمه در آن همانند تصاویر زیر یک آدرس عددی دارد.

اگر فرض کنیم تعداد لغات منحصر به فرد 5000 است، با اضافه کردن کلمه unknown برای کلمات ناشناخته، طول دایره لغات ما 5001 میشود. با این حساب، هر کامنت باید تبدیل شود به یک بردار ویژگی با طول دقیقاً 5001.
گام دوم توکن کردن کامنتها (tokenization)
کامنت زیر را در نظر بگیرید:
“This is the best soap I have used so far. The scent last’s all day!”
پس ازtokenization و تمیزسازی کامنت به شکل زیر خواهد بود.
Tokens=
[‘this’, ‘is’, ‘the’, ‘best’, ‘soap’, ‘i’, ‘have’, ‘used’, ‘so’, ‘far’, ‘the’, ‘scent’, ‘lasts’, ‘all’, ‘day’ ]
تمام کلمات:
- کوچک سازی شدهاند
- از علائم نگارشی جدا شدهاند
- به یک لیست از توکنها تبدیل شدهاند
گام سوم: ساخت بردار اولیه (تمام صفر)
برای هر کامنت، یک بردار ویژگی تمام صفر به اندازه طول vocabulary ساخته میشود.
مثلاً:

این بردار قالب اصلی بردار ویژگی است.
گام چهارم: شمارش تکرار کلمات در کامنت
حال تعداد دفعات تکرار هر کلمه (در vocabulary) در کامنت شمارش میشود و در آدرس متناظر در بردار قرار میگیرد. قاعدتا هر کامنتی از تعدادی کلمه تشکیل شده است که کنار هم احساس فرد در مورد موضوع مورد نظر رو بیان میکند. از طرفی ما تعدادی کلمه در vocabulary داریم. میایییم میشماریم که هر کدام از کلمات در vocabulary چندین بار در کامنت استفاده شده اند. از طرفی اگر کلمه ای در کامنت باشد که جز لغات vocabulary نیست، اونارو جز unknownها در نظر میگیریم و تعداد اونهارو در آدرس مربوط به unknown درvocabulary قرار میدهیم.
نمونه:
Tokens=
[‘this’, ‘is’, ‘the’, ‘best’, ‘soap’, ‘i’, ‘have’, ‘used’, ‘so’, ‘far’, ‘the’, ‘scent’, ‘lasts’, ‘all’, ‘day’ ]
نتیجه بردار ویژگی:

- best : یکبار
- soap : یکبار
- have : یکبار
- the : دو بار
- day : یکبار
- unknown : سه بار. یعنی سه کلمهای که در vocab نیستند.
بردار ویژگی نهایی همانند زیر خواهد بود که همان بردار ویژگی نهایی برای کامنت خواهد بود.
این همان بردار ویژگی نهایی کامنت است.
پروژه عملی: پیادهسازی Spam Detection با تکنیک BOW
در این پروژه با دادههای متنی واقعی، مدل MLP را آموزش میدهیم تا پیامهای spam و ham را تشخیص دهد.

دسترسی به کدهای پروژه به صورت یکجا در GitHub
مرحله 0: وارد کردن کتابخانه های مورد نیاز
Python
import pandas as pd from torch import nn,optim import torch from sklearn import metrics from matplotlib import pyplot as plt from sklearn import preprocessing as prep from torch.utils.data import TensorDataset,DataLoader,Dataset import pandas as pd from sklearn.preprocessing import LabelEncoder from sklearn.model_selection import train_test_split import numpy as np import re
مرحله 1: خواندن دادهها در پایتون
- بارگذاری فایل CSV
- جدا کردن متن و لیبل
Python
df = pd.read_csv('spam.csv', encoding='ISO-8859-1') df=df.drop(['Unnamed: 2', 'Unnamed: 3', 'Unnamed: 4'], axis=1) df=df.rename(columns={"v1":'label_num',"v2":'text'}) le= LabelEncoder() df['label_num']= le.fit_transform(df['label_num']) df.head()
مرحله 2: Tokenize کردن متنها
- تمیزسازی
- کوچکسازی
- حذف stopwords (اختیاری)
Python
from def tokenize(text): tokens=[] for line in text: line_cleaned= re.sub(r'[^A-Za-z0-9]+', ' ', line) tokens.append(line_cleaned.lower().split()) return tokens doc= df['text'].astype(str).tolist() doc_tokens= tokenize(doc) print(doc[1],'\n',doc_tokens[1]) output: Ok lar... Joking wif u oni... ['ok', 'lar', 'joking', 'wif', 'u', 'oni']
مرحله 3: ساخت Vocabulary
- استخراج تمام کلمات منحصر به فرد
- اختصاص index به هر کلمه
Python
def create_vocabulary(doc_tokens,vocab_size=5000):
word_freq = {}
for tokens in doc_tokens:
for token in tokens:
if token in word_freq and token.isalpha():
word_freq[token] += 1
else:
word_freq[token] = 1
sorted_words = sorted(word_freq.items(), key=lambda x: x[1], reverse=True)
most_frequent_words = [word for word, freq in sorted_words[:vocab_size]]
vocab=most_frequent_words
word2idx= {w: i+1 for i, w in enumerate(vocab)}
word2idx['<UNK>']=0
vocab_size+=1</pre >
return vocab,word2idx,vocab_size</pre >
vocab,word2idx,vocab_size= create_vocabulary(doc_tokens,vocab_size=5000)
print(vocab_size)
output:
5001
مرحله 4: ساخت بردار ویژگی با تکنیک BOW
- بردار صفر
- شمارش تکرار
- ساخت بردار ویژگی برای هر کامنت
Python
def vectorize_bow(tokens, vocab2idx, vocab_size): feature= np.zeros((len(tokens), vocab_size), dtype=np.float64) for i, token in enumerate(tokens): for j, word in enumerate(token): if word in vocab2idx: feature[i,vocab2idx[word]]+=1 else: feature[i,vocab2idx['<UNK>']]+=1 return feature
مرحله 5: تقسیم داده به سه بخش آموزش، ارزیابی و تست
Python
Xtrain, Xtest, y_train, y_test = train_test_split(doc_tokens,df["label_num"].values,test_size=0.2,random_state=42) Xtrain, Xvalid, y_train, y_valid = train_test_split(Xtrain,y_train, test_size=0.15,random_state=42) print(len(Xtrain), len(Xvalid), len(Xtest)) print(np.unique(y_train, return_counts=True)) output: 3788 669 1115 (array([0, 1]), array([3274, 514], dtype=int64))
مرحله 6: ساخت Dataset اختصاصی برای لود کردن داده در پایتورچ
- سه متد جادویی init, len , get_item را تعریف میکنیم.
- هدف این متد این هست که هربار یک نمونه از داده به همراه لیبل از داده اصلی خوانده شده و سپس به تنسور تبدیل شوند.
- بعدا این dataset رو به dataloader میدهیم تا یک حلقه دور آن بزند و batching را انجام دهد.
Python
class CustomDataset(Dataset):
def __init__(self,comments, labels,vocab_size,word2idx):
self.tokenized_comments=comments
self.y= labels
self.vocab_size= vocab_size
self.word2idx= word2idx
def __len__(self):
return len(self.tokenized_comments)
def __getitem__(self, idx):
tokens= self.tokenized_comments[idx]
x= vectorize_bow([tokens], self.word2idx, self.vocab_size)
y= self.y[idx]
x= torch.tensor(x, dtype=torch.float32).squeeze(0)
y= torch.tensor(y, dtype=torch.long)
return x,y
ds_train= CustomDataset(Xtrain, y_train,vocab_size,word2idx)
ds_valid= CustomDataset(Xvalid, y_valid,vocab_size,word2idx)
ds_test= CustomDataset(Xtest, y_test,vocab_size,word2idx)
مرحله 7: ساخت dataloader برای داده ها
Python
train_loader = DataLoader(ds_train, batch_size=128, shuffle=True) valid_loader = DataLoader(ds_valid, batch_size=128, shuffle=False) test_loader = DataLoader(ds_test, batch_size=128, shuffle=False)
مرحله 8: طراحی شبکه عصبی MLP در PyTorch
- یک hidden layer کافی است
- ورودی مدل = طول واژگان
Python
class MLP(nn.Module): def __init__(self, input_size, hidden_size, num_classes): super(MLP, self).__init__() self.fc1 = nn.Linear(input_size, hidden_size) self.relu = nn.ReLU() self.fc2 = nn.Linear(hidden_size, num_classes) def forward(self, x): out = self.fc1(x) out = self.relu(out) out = self.fc2(out) return out
مرحله 9: تعریف Loss و Optimizer
- CrossEntropyLoss
- Adam optimizer
Python
model = MLP(input_size=vocab_size,hidden_size=32, num_classes=2) criterion = nn.CrossEntropyLoss() optimizer = optim.Adam(model.parameters(), lr=0.001)
مرحله 10: حلقه آموزش مدل
-
- batch training
- محاسبه accuracy
Python
num_epochs = 10
train_losses = []
valid_losses = []
for epoch in range(num_epochs):
model.train()
epoch_loss = 0.0
num_epochs = 10
train_losses = []
valid_losses = []
for epoch in range(num_epochs):
model.train()
epoch_loss = 0.0
correct_train = 0
total_train = 0
for batch_X, batch_y in train_loader:
optimizer.zero_grad()
outputs = model(batch_X)
loss = criterion(outputs, batch_y)
loss.backward()
optimizer.step()
epoch_loss += loss.item() * batch_X.size(0)
_, predicted = torch.max(outputs.data, 1)
correct_train += (predicted == batch_y).sum().item()
total_train += batch_y.size(0)
epoch_loss /= len(train_loader.dataset)
train_losses.append(epoch_loss)
train_acc = correct_train / total_train
# Validation
model.eval()
valid_loss = 0.0
correct_valid = 0
total_valid = 0
with torch.no_grad():
for batch_X, batch_y in valid_loader:
outputs = model(batch_X)
loss = criterion(outputs, batch_y)
valid_loss += loss.item() * batch_X.size(0)
_, predicted = torch.max(outputs.data, 1)
correct_valid += (predicted == batch_y).sum().item()
total_valid += batch_y.size(0)
valid_loss /= len(valid_loader.dataset)
valid_losses.append(valid_loss)
valid_acc = correct_valid / total_valid
print(f"Epoch [{epoch+1}/{num_epochs}], "
f"Train Loss: {epoch_loss:.4f}, Train Acc: {train_acc:.4f}, "
f"Valid Loss: {valid_loss:.4f}, Valid Acc: {valid_acc:.4f}")
مرحله 11: تست مدل آموختهشده
Python
model.eval()
y_pred=[]
y_true=[]
with torch.no_grad():
for batch_X, batch_y in test_loader:
outputs = model(batch_X)
# outputs_p= torch.softmax(outputs,dim=1)
_, predicted = torch.max(outputs.data, 1)
y_pred.extend(predicted.numpy())
y_true.extend(batch_y.numpy())
accuracy = metrics.accuracy_score(y_true, y_pred)*100
precision = metrics.precision_score(y_true, y_pred)*100
recall = metrics.recall_score(y_true, y_pred)*100
f1 = metrics.f1_score(y_true, y_pred)*100
print(f'Accuracy: {accuracy:.4f}%')
print(f'Percision: {precision:.4f}%')
print(f'Recall: {recall:.4f}%')
print(f'F1-score: {f1:.4f}%')
cm = metrics.confusion_matrix(y_true, y_pred, normalize='true')
# display normalized confusion matrix with %
disp = metrics.ConfusionMatrixDisplay(confusion_matrix=cm,
display_labels=le.classes_)
# normalized confusion matrix
disp = metrics.ConfusionMatrixDisplay(confusion_matrix=cm,
display_labels=le.classes_)
disp.plot(cmap=plt.cm.Blues, values_format='.2%')
plt.show()
Accuracy: 98.0269%
Percision: 97.7612%
Recall: 87.3333%
F1-score: 92.2535%
دسترسی به کل کد و پروژه به صورت یکجا در GitHub
پروژه: تحلیل احساسات مخاطبان فیلم (Sentiment Analysis)
در پروژه sentiment analysis ابتدا با روش Bag of words برای هر نمونه از نقدهای فیلم (review ) یک بردار ویژگی بسازید، سپس با استفاده از شبکه عصبی MLP نمونه ها را به دو گروه positive و negative دسته بندی کنید.

تحلیل احساسات در نقدهای فیلمهای IMDb
تحلیل احساسات یک تکنیک پردازش زبان طبیعی هست که برای تعیین لحن احساسات در جملات استفاده می شود. در این پایگاه داده 50000 نقد بر فیلمها قرار داده شده است، که هر کدام لیبل مثبت یا منفی گرفته اند.
- Size: 50,000 reviews
- Label Type: Binary (positive = 1, negative = 0)
مشکل روش Bag of Words در پردازش زبان طبیعی
با وجود مزایایی که تکنیک BOW دارد، اما محدودیتهای اساسی نیز دارد:
- ترتیب کلمات را حذف میکند
- اندازه بردار ویژگی بسیار بزرگ میشود
- بردار ویژگی Sparse خواهد شد، در نتیجه رمزگشایی بردارهای برای شبکه های عصبی بسیار سخت خواهد بود.
- نسبت به کلمات کمکاربرد بسیار حساس است
- معنی کلمات و ارتباط معنایی دیده نمیشود
بعدا سایر رویکردها برای حل این مسئله را بررسی خواهیم کرد.
چرا باید از Bag of Words به سمت RNN رفت؟
چون:
- ترتیب کلمات مهم است
- معنا در وابستگی زمانی قرار دارد
- کلمات هم معنی و هم خانواده باید قابل تشخیص باشند
- مدل باید مفهوم جمله را بفهمد، نه فقط بشمارد
اینجاست که شبکههای عصبی بازگشتی (RNN) راه حل مدرن هستند.
در پستهای آینده این مباحث را کامل پوشش میدهیم.
کاربرد BOW در پردازش تصویر
Bag of Words فقط مخصوص متن نیست. در پردازش تصویر نیز زمانی که ویژگیها اندازه ثابت ندارند از همین ایده استفاده میشود.
تکنیک bag of words لزوما فقط برای مباحث پردازش زبان طبیعی نیست. از این رویکرد در سایر حوزه، زمانی که نمونه های مشاهده، بردار ویژگی یکسانی ندارند، میتوان استفاده کرد.
برای مثال وقتی از روش SIFT در پردازش تصاویر، برای استخراج ویژگی استفاده میشود، به ازای هر تصویر این الگوریتم، بسته به محتوای تصویر، تعداد نقاط کلیدی مختلفی پیدا میکند. هر کدام از نقاط کلیدی یک بردار ویژگی (معمولا 1*128) هستند. حال اگر بخواهیم برای هر تصویر یک بردار ویژگی از روی این نقاط کلیدی بسازیم، به مشکل خواهیم خورد.
برای مثال در یک تصویر 100 نقطه کلیدی ممکن است پیدا شود، که اینها رو بخواهیم کنار هم بگذاریم میشه: 1*12800
و اگر برای یک تصویر دیگه 50 تا نقطه کلیدی پیدا شود، سایز بردار ویژگی نهایی تصویر 6400 خواهد بود.
در چنین مواردی، برای حل مشکل از تکنیک BOW استفاده میکنند تا همه تصاویر بردار ویژگی با سایز یکسانی داشته باشند.
به این طریق که یک سری نقاط کلیدی پایه از روی چندین تصویر آموزشی پیدا میکنند، مثلا 100 تا در نظر میگیریند. سپس با کمک آنها برای هر تصویر یک بردار ویژگی با سایز 1*100 میسازند.
روال هم به این شکل هست که ابتدا با کمک SIFT برای هر تصویر (چه آموزشی و چه تست) نقاط کلیدی پیدا میشود. سپس این نقاط کلیدی با 100 نقطه کلیدی پایه مقایسه میشوند تا متوجه شویم که چندتا از نقاط کلیدی تصویر، شبیه به این 100 نقطه کلیدی هستند. اینطوری یک بردار ویژگی هستوگرام ساخته میشود.
یعنی به عبارتی همانند مسئله LLM، اینجا هم میخواهیم بدانیم که هر تصویر ورودی شامل چه نوع کلمات vocabulary، هست، اما اینجا به جای کلمات، نقاط کلیدی پایه وجود دارد.
دیدگاه ها