<jupyter_start><jupyter_text>TFIDF (Word, BPE, Weighted Embeddings)  Before You Start  Libraries<jupyter_code># install the essential libraries that are not available on google colab
!pip install youtokentome
! pip install gdown
# fasttext
!git clone https://github.com/facebookresearch/fastText.git 
!cd fastText && pip install .
# import libraries 
import os
import numpy as np
import codecs
import re
from sklearn.feature_extraction.text import TfidfVectorizer
from sklearn.metrics.pairwise import cosine_similarity
from tqdm import tqdm
import youtokentome as yttm
import fasttext.util
import fasttext
import yaml
# create the utils folder and upload evaluation.ipynb and preprocess.ipynb
# ! mkdir utils
%run "./utils/evaluation.ipynb"
%run "./utils/preprocess.ipynb"<jupyter_output><empty_output><jupyter_text>Get The Skip-gram Fasttext Model<jupyter_code># https://github.com/taesiri/PersianWordVectors
SKIPGRAM_MODEL_FILE_ID = '1wPnMG9_GNUVdSgbznQziQc5nMWI3QKNz'
# Let's explore the SKIPGRAM Model

!gdown --id $SKIPGRAM_MODEL_FILE_ID 
# loading the Model
model_skipgram = fasttext.load_model('farsi-dedup-skipgram.bin')

# official persian fasttext
# !wget https://dl.fbaipublicfiles.com/fasttext/vectors-crawl/cc.fa.300.bin.gz
# !gunzip cc.fa.300.bin.gz
# model_skipgram = fasttext.load_model('cc.fa.300.bin')<jupyter_output><empty_output><jupyter_text>Download Data<jupyter_code>! wget https://raw.githubusercontent.com/language-ml/2-LM-embedding-projects/main/problem3/evaluation_IR.yml -P ./data
! wget https://github.com/language-ml/2-LM-embedding-projects/raw/main/problem3/doc_collection.zip -P ./data
! unzip ./data/doc_collection.zip -d ./data
! wget https://raw.githubusercontent.com/SajjadMb/Masnavi_NLP_Toolkit/main/stopwords.txt -P ./data<jupyter_output><empty_output><jupyter_text>Preprocess  Read data set path of data in `PATH` variable and ground truth in `YAML_PATH` variable<jupyter_code>PATH = './data/IR_dataset/'
YAML_PATH = './data/evaluation_IR.yml/'

PATH = PATH.rstrip('/')
YAML_PATH = YAML_PATH.rstrip('/')<jupyter_output><empty_output><jupyter_text>store txt files into a list named `doc`<jupyter_code>docs = []
for index in tqdm(range(0, 3258)):
    with open(f"{PATH}/{index}.txt", 'r', encoding='utf8') as file_reader:
        docs.append(file_reader.read())<jupyter_output>100%|██████████| 3258/3258 [00:00<00:00, 18626.94it/s]<jupyter_text>show example of data<jupyter_code>for doc in docs[:3]:
    print (doc[:500])
    print('-' * 50)<jupyter_output>برخی از هواداران مصدق یا اعضای جبهه ملی که در زمان نخست‌وزیری مصدق، از جبهه ملی یا از هیئت‌وزیران کنار گذاشته شده یا کنار رفتند، پس از جدایی از مصدق، به انتقاد از کارنامه وی پرداختند و حتی برای سرنگونی‌اش تلاش کردند. برخی از این افراد عبارت‌اند از:
* فضل‌الله زاهدی (نخست‌وزیر کودتا)
* علی امینی
* حسین مکی، (که در آغاز سرباز فداکار وطن نامیده شد ولی در پایان به دلیل مخالفت با مصدق از سوی هواداران جبهه ملی سرباز خطاکار وطن خطاب می‌شد)
* مظفر بقایی (به دلیل اتهام مشارکت در قتل سرتیپ افشار طوس و سپس
--------------------------------------------------
جبهه ملی ایران که به اختصار جبهه ملی نیز خوانده می‌شود سازمان سیاسی ملی‌گرای سکولار، دموکرات و جمهوری‌خواه فعال در ایران است، که در حال حاضر تحت رهبری دکتر موسویان فعالیت می‌کند.

جبهه ملی ایران در سال ۱۳۲۸ توسط سیاستمدارانی از قبیل محمد مصدق، حسین فاطمی و کریم سنجابی تأسیس شد و به پیش نهاد حسین فاطمی، ملی شدن صنعت نفت ایران را مطرح کرد. دکتر مصدق در کتاب خاطرات خود می‌گوید: «پیشنهاد ملی‌شدن صنعت نفت در سراسر کشور ابتکار شادروان دکتر حسین فاطمی[...]<jupyter_text>Data clean<jupyter_code># cleaned data
docs = [clean_text(doc, sentence=False, only_persian=True) for doc in tqdm(docs)]
for doc in docs[:3]:
    print (doc[:500])
    print('-' * 50)<jupyter_output>100%|██████████| 3258/3258 [00:03<00:00, 1085.68it/s]<jupyter_text>BPE<jupyter_code># Set path of bpe model and corpus that will be created by train cell
BPE_PATH = './bpe/'
BPE_PATH = BPE_PATH.rstrip('/')
os.makedirs(BPE_PATH, exist_ok=True)
train_data_path = BPE_PATH + "/train_data.txt"
model_path = BPE_PATH + "/bpe.model"<jupyter_output><empty_output><jupyter_text>Train<jupyter_code># Generating corpus file with training data
pretrained_bpe_texts = ' '.join(docs)

with open(train_data_path, "w") as w_file:
    w_file.write(pretrained_bpe_texts)

# Training model
yttm.BPE.train(data=train_data_path, vocab_size=5000, model=model_path, unk_id=1, bos_id=2, eos_id=3)<jupyter_output><empty_output><jupyter_text>Load and Test<jupyter_code># Loading model
bpe = yttm.BPE(model=model_path)

# first document as a test 
test_text = docs[0]

# Tokenization test
print(bpe.encode(test_text, output_type=yttm.OutputType.SUBWORD))<jupyter_output>['▁برخی', '▁از', '▁هواداران', '▁مصدق', '▁یا', '▁اعضای', '▁جبهه', '▁ملی', '▁که', '▁در', '▁زمان', '▁نخست', '▁وزیری', '▁مصدق', '▁از', '▁جبهه', '▁ملی', '▁یا', '▁از', '▁هییت', '▁وزیر', 'ان', '▁کنار', '▁گذاشته', '▁شده', '▁یا', '▁کنار', '▁رفتند', '▁پس', '▁از', '▁جدایی', '▁از', '▁مصدق', '▁به', '▁انتقاد', '▁از', '▁کار', 'نامه', '▁وی', '▁پرداختند', '▁و', '▁حتی', '▁برای', '▁سرنگ', 'ونی', '▁اش', '▁تلاش', '▁کردند', '▁برخی', '▁از', '▁این', '▁افراد', '▁عبارت', '▁اند', '▁از', '▁فضل', '▁الله', '▁ز', 'اه', 'دی', '▁نخست', '▁وزیر', '▁کودتا', '▁علی', '▁ام', 'ینی', '▁حسین', '▁م', 'کی', '▁که', '▁در', '▁آغاز', '▁سرباز', '▁ف', 'دا', 'کار', '▁و', 'طن', '▁نامیده', '▁شد', '▁ولی', '▁در', '▁پایان', '▁به', '▁دلیل', '▁مخالفت', '▁با', '▁مصدق', '▁از', '▁سوی', '▁هواداران', '▁جبهه', '▁ملی', '▁سرباز', '▁خط', 'اک', 'ار', '▁و', 'طن', '▁خطاب', '▁می', '▁شد', '▁مظ', 'فر', '▁ب', 'قایی', '▁به', '▁دلیل', '▁اتهام', '▁مشارکت', '▁در', '▁قتل', '▁سر', 'تی', 'پ', '▁افشار', '▁ط', 'وس', '▁و', '▁سپس', '▁اتهام', '▁شرکت', '▁در', '▁کودتای', [...]<jupyter_text>TFIDF TOP K Relevant Documents<jupyter_code># TFIDF Class: a class to generate TFIDF for your docs and get top k relvant documents
class tfidf:
    
    def __init__(self, docs, ngram_range, bpe_model=None):
        self._min_df = 1
        self._max_df=0.5
        self._max_features=10000
        self._docs = docs
        self._bpe = bpe_model
        self._ngram_range = ngram_range
        if self._bpe:
            print("we are using bpe")
            self._model_tfidf = TfidfVectorizer(analyzer="word", min_df=self._min_df, max_df=self._max_df, max_features=self._max_features, ngram_range=self._ngram_range, tokenizer= lambda x: self.bpe_tokenizer(x))
        else:
            self._model_tfidf = TfidfVectorizer(analyzer="word", min_df=self._min_df, max_df=self._max_df, max_features=self._max_features, ngram_range=self._ngram_range)

        self._matrix = self._model_tfidf.fit_transform(docs)
        self._feature_names = self._model_tfidf.get_feature_names_out()
        #Load skip-gram embedding vector for each TF-IDF term
        self._tfidf_emb_vecs = np.vstack([model_skipgram.get_word_vector(word) for word in self._feature_names])

        #To get a TF-IDF weighted skip-gram vector summary of each document, 
        #we just need to matrix multiply docs_vecs with tfidf_emb_vecs
        self._docs_emb = np.dot(self._matrix.toarray(), self._tfidf_emb_vecs)


    def bpe_tokenizer(self, text):
        tokens = self._bpe.encode(text, output_type=yttm.OutputType.SUBWORD)
        return tokens 
        

    def tfidf_top_k(self, query, k=2):
        
        query_tfidf = self._model_tfidf.transform([query])

        # Stores cosine similarity scores
        doc_scores = []

        # Compute the cosine similarity scores
        for doc in self._matrix:
            doc_scores.append(cosine_similarity(query_tfidf, doc)[0][0])

        # Sort list of doc_scores and return the top k indices of highest scores
        sorted_scores = sorted(enumerate(doc_scores), key=lambda ind_score: ind_score[1], reverse=True)
        if k!=-1:
            top_doc_indices = [ind for ind, score in sorted_scores[:k]]
        else:
            top_doc_indices=sorted_scores
        return top_doc_indices



    def tfidf_weighted_top_k(self, query, k=2):

        query_tfidf = self._model_tfidf.transform([query])
        query_emb = np.dot(query_tfidf.toarray(), self._tfidf_emb_vecs)
        # Stores cosine similarity scores
        doc_scores = []

        # Compute the cosine similarity scores
        for doc in self._docs_emb:
            doc_scores.append(cosine_similarity(query_emb, doc.reshape(1, -1))[0][0])

        # Sort list of doc_scores and return the top k indices of highest scores
        sorted_scores = sorted(enumerate(doc_scores), key=lambda ind_score: ind_score[1], reverse=True)
        if k!=-1:
            top_doc_indices = [ind for ind, score in sorted_scores[:k]]
        else:
            top_doc_indices = sorted_scores
        return top_doc_indices
query = "آدولف هیتلر شکست و مرگ"<jupyter_output><empty_output><jupyter_text>Word Level: ngram (1,1)<jupyter_code>tf_obj_word_1_1 = tfidf(docs=docs, ngram_range=(1,1), bpe_model=None)
# show 1000 features among 10000 selected features by tfidf 
print(tf_obj_word_1_1._feature_names[:1000])
tf_obj_word_1_1.tfidf_top_k(query, 20)<jupyter_output>['آب' 'آباد' 'آبادان' 'آبادانی' 'آباده' 'آبادی' 'آبان' 'آبجو' 'آبخاز'
 'آبخوان' 'آبراه' 'آبراهام' 'آبراهه' 'آبشار' 'آبشارهای' 'آبله' 'آبکش'
 'آبگیری' 'آبی' 'آبیاری' 'آتش' 'آتشکده' 'آتلانتیک' 'آتن' 'آتواتوکا' 'آتی'
 'آث' 'آثار' 'آثارش' 'آثاری' 'آجر' 'آخر' 'آخری' 'آخرین' 'آداب' 'آدام'
 'آدامز' 'آدرس' 'آدلاید' 'آدم' 'آدمی' 'آدنوزین' 'آدهمار' 'آدولف' 'آدونیا'
 'آذر' 'آذربایجان' 'آذربایجانی' 'آذرماه' 'آذری' 'آذوقه' 'آر' 'آرا'
 'آراسته' 'آرام' 'آرامش' 'آرامگاه' 'آرامی' 'آرانی' 'آرای' 'آرایش' 'آرایه'
 'آرایی' 'آرتمیسینین' 'آرتور' 'آرد' 'آرزو' 'آرزوهای' 'آرزوی' 'آرسنال'
 'آرش' 'آرشیو' 'آرم' 'آرمان' 'آرواره' 'آرژانتین' 'آرژانتینی' 'آرکانزاس'
 'آریامهر' 'آریانا' 'آریایی' 'آزاد' 'آزادانه' 'آزادخان' 'آزادسازی'
 'آزادگان' 'آزادی' 'آزار' 'آزمایش' 'آزمایشگاه' 'آزمایشگاهی' 'آزمایشی'
 'آزمایی' 'آزمون' 'آسا' 'آسان' 'آسانسورهای' 'آسانی' 'آستارا' 'آستانه'
 'آسفالت' 'آسمان' 'آسمانی' 'آسوده' 'آسیا' 'آسیاب' 'آسیای' 'آسیایی' 'آسیب'
 'آسیبی' 'آش' 'آشامیدنی' 'آشتی' 'آشتیانی' 'آشفته' 'آشفتگی' 'آشنا' 'آشنایی'
 'آ[...]<jupyter_text>Word Level: ngram (1,2)<jupyter_code>tf_obj_word_1_2 = tfidf(docs=docs, ngram_range=(1,2), bpe_model=None)
tf_obj_word_1_2.tfidf_top_k(query, 20)<jupyter_output><empty_output><jupyter_text>BPE: ngram (1,1)<jupyter_code>tf_obj_bpe_1_1 = tfidf(docs=docs, ngram_range=(1,1), bpe_model=bpe)
# show 1000 features among 10000 selected features by tfidf 
print(tf_obj_bpe_1_1._feature_names[:1000])
tf_obj_bpe_1_1.tfidf_top_k(query, 20)<jupyter_output>we are using bpe
['آ' 'آب' 'آباد' 'آمد' 'آن' 'آور' 'آوری' 'اب' 'ابان' 'ابت' 'ابتی' 'ابر'
 'ابری' 'ابع' 'ابق' 'ابل' 'ابله' 'ابه' 'ابی' 'اتر' 'اتری' 'اترین' 'اتل'
 'اتور' 'اتی' 'اتیک' 'اج' 'اجه' 'اح' 'احب' 'احت' 'احی' 'اخ' 'اخت' 'اخته'
 'اخر' 'اد' 'ادا' 'ادار' 'ادت' 'ادث' 'ادر' 'ادش' 'ادل' 'ادمی' 'اده' 'ادها'
 'ادهای' 'ادو' 'ادگی' 'ادی' 'ادیان' 'ادیه' 'ار' 'ارا' 'ارات' 'اران' 'ارت'
 'ارتی' 'ارد' 'اردی' 'ارستان' 'ارف' 'ارن' 'اره' 'ارها' 'ارهایی' 'ارک'
 'اری' 'اریان' 'از' 'ازد' 'ازده' 'ازدهم' 'ازم' 'ازند' 'ازه' 'ازی' 'اس'
 'اسا' 'اسب' 'است' 'استه' 'اسر' 'اسط' 'اسطه' 'اسم' 'اسپ' 'اسی' 'اسیون'
 'اش' 'اشا' 'اشت' 'اشته' 'اشد' 'اشی' 'اص' 'اصر' 'اصره' 'اصل' 'اصله' 'اض'
 'اضی' 'اط' 'اطر' 'اطع' 'اظ' 'اع' 'اعت' 'اعده' 'اعی' 'اعیل' 'اف' 'افت'
 'افظ' 'افع' 'افق' 'افه' 'اق' 'اقد' 'اقض' 'اقی' 'ال' 'الات' 'الب' 'الت'
 'التی' 'الح' 'الد' 'الدین' 'الع' 'العمل' 'الله' 'الم' 'اله' 'الی' 'الیا'
 'الیست' 'الیستی' 'الیسم' 'ام' 'اما' 'امبر' 'امت' 'امح' 'امحمد' 'امد'
 'امع' 'امل' 'امن' 'امه' 'امون' 'امی' 'امین' [...]<jupyter_text>BPE: ngram (1,2)<jupyter_code>tf_obj_bpe_1_2 = tfidf(docs=docs, ngram_range=(1,2), bpe_model=bpe)
tf_obj_bpe_1_2.tfidf_top_k(query, 20)<jupyter_output>we are using bpe<jupyter_text>TFIDF Weighted<jupyter_code>tf_obj_weighted = tfidf(docs=docs, ngram_range=(1,2), bpe_model=None)
tf_obj_weighted.tfidf_weighted_top_k(query, 20)<jupyter_output><empty_output><jupyter_text>Evaluation<jupyter_code># Create all models information as a dcit
models = {'Word Level: ngram (1,1)': {'model': tf_obj_word_1_1, 'MRR': 0, 'P@K': 0, 'MAP': 0},
          'Word Level: ngram (1,2)': {'model': tf_obj_word_1_2, 'MRR': 0, 'P@K': 0, 'MAP': 0},
          'BPE: ngram (1,1)': {'model': tf_obj_bpe_1_1, 'MRR': 0, 'P@K': 0, 'MAP': 0},
          'BPE: ngram (1,2)': {'model': tf_obj_bpe_1_2, 'MRR': 0, 'P@K': 0, 'MAP': 0},
          'TFIDF Weighted': {'model': tf_obj_weighted, 'MRR': 0, 'P@K': 0, 'MAP': 0}
         }
query_data = {}
with open(YAML_PATH, "r") as stream:
    try:
        query_data = dict(yaml.safe_load(stream))
    except yaml.YAMLError as exc:
        print(exc)

queries = list(query_data.keys())
TOP_K_FROM_MODELS = 100
# compute MMR metric for all models and queries
def MMR_TFIDf(models, queries, query_data):
    relevent_match_list = []
    for model in models.keys():
        relevent_match_list = []
        for query_i in tqdm(queries):
            if model == 'TFIDF Weighted':
                top_results_indices_i = models[model]['model'].tfidf_weighted_top_k(query_i, TOP_K_FROM_MODELS)
            else:
                top_results_indices_i = models[model]['model'].tfidf_top_k(query_i, TOP_K_FROM_MODELS)
            boolian_match = (np.array(top_results_indices_i) == query_data[query_i]['relevant'][0]).astype(int)
            relevent_match_list.append(list(boolian_match))
        models[model]['MRR'] = mean_reciprocal_rank(relevent_match_list)
    return models


# compute P@K metric for all models and queries
def PATK_TFIDF(models, queries, query_data, k=10):
    patk = []

    for model in models.keys():
        patk = []
        for query_i in tqdm(queries):
            if model == 'TFIDF Weighted':
                top_results_indices_i = models[model]['model'].tfidf_weighted_top_k(query_i, TOP_K_FROM_MODELS)
            else:
                top_results_indices_i = models[model]['model'].tfidf_top_k(query_i, TOP_K_FROM_MODELS)
            boolian_match = np.isin(top_results_indices_i, (query_data[query_i]['relevant'] + query_data[query_i]['similar_high'])).astype(int)
            patk.append(precision_at_k(boolian_match, k))
        models[model]['P@K'] = sum(patk)/len(patk)
    return models


# compute MAP metric for all models and queries
def MAP_TFIDF(models, queries, query_data):
    map_i = []

    for model in models.keys():
        map_i = []
        for query_i in tqdm(queries):
            if model == 'TFIDF Weighted':
                top_results_indices_i = models[model]['model'].tfidf_weighted_top_k(query_i, TOP_K_FROM_MODELS)
            else:
                top_results_indices_i = models[model]['model'].tfidf_top_k(query_i, TOP_K_FROM_MODELS)
            boolian_match = np.isin(top_results_indices_i, (query_data[query_i]['relevant'] + query_data[query_i]['similar_high'])).astype(int)
            map_i.append(list(boolian_match))

        models[model]['MAP'] = mean_average_precision(map_i)
    return models


models = MMR_TFIDf(models, queries, query_data)
models = PATK_TFIDF(models, queries, query_data, k=10)
models = MAP_TFIDF(models, queries, query_data)
models<jupyter_output><empty_output>
{'fraction_non_alphanumeric': 0.1624975117775861, 'fraction_numerical': 0.015327450069670227, 'mean_word_length': 4.460869565217391, 'pattern_counts': {'":': 0, '<': 64, '<?xml version=': 0, '>': 62, 'https://': 6, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 1, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'chain_length': '17', 'dir': 'jupyter-structured-clean-dedup', 'id': '404673', 'license': 'MIT', 'n_tokens_mistral': 7678, 'n_tokens_neox': 7238, 'n_words': 1359, 'path': 'tfidf.ipynb', 'repo_name': 'SajjadMb/persian-semantic-search', 'size': '15071'}