<jupyter_start><jupyter_text>TME2 :  AppariementL'appariement est l'action qui consiste à rassembler par paires des choses qui sont naturellement compatibles.<jupyter_code>import import_ipynb
import tme1Indexation as tm
import math
import numpy as np<jupyter_output>importing Jupyter notebook from tme1Indexation.ipynb<jupyter_text>Exercice 1 – Exercice de compréhension : modèle de RI simpleOn considère la collection de documents (cisi.txt et cacm.txt) et la liste des stopwords (TextRepresenter.py) du TME1. L’objectif dans cet exercice est d’estimer le score des documents pour la requˆete "home sales top"  question 1) Quels index faut-il interroger pour avoir un calcule du score pertinent? On utilise l'index inversé , car il nous donne accès aux informations dont on a besoin pour calculer le score plus rapidement. question 1.2) Ecrire le code qui permet de calculer le score des documents à partir du modèle booléen ScoreBoolean retourne les documents qui contiennent tous les termes de la requête.Ca veut dire que j'ai considéré  que les temes de la requête sont relié par des AND entre eux.Remarque:si j'écris q="t1 AND (t2 OR NOT T3)", si ça c'est ma requête alors je dois retourner les documents  qui contiennent t1 et ( t2 ou t3).Du coup ça dépend de la syntaxe de la requête. intersection  retourne la liste des documents qui contiennent tous les mots de la requete.<jupyter_code>def intersection(indexReverse,s):
    L=[]
    for c,v in s.items():
        L.append(indexReverse[c])
    d=L[0]
    M=[]
    for c,v in d.items():
        M.append(c)
    for c in M:
        b=False
        for D in L:
            if (c not in D):
                b=True
        if(b):
            M.remove(c)
    return M
    
         
    
def ScoreBoolean(q,T):
    I=tm.IndexerSimple()
    index,indexReverse=I.indexation(T)
    
    PS=tm.tr.PorterStemmer()
    s=PS.getTextRepresentation(q)
    return intersection(indexReverse,s)<jupyter_output><empty_output><jupyter_text>On récupère la liste des documents qui contiennent tous les mots de la requête<jupyter_code>q="home sales top"
T="cisi/cisi.txt"
#print(ScoreBoolean(q,T))<jupyter_output><empty_output><jupyter_text>question 1.3) On calcule le score des documents à partir du modèle vectoriel (produit cart´esien) dans le cas d’une pond´eration tf Modèle Vectoriel= Vector Space Model (VSM).On représente les documents et les requêtes sous forme de vecteurs dans l’espace vectoriel engendré par tous les termes de la collection de documents:T  (un terme = une dimension).Document : dj= (w1j, w2j, …, wMj) Requête : q= (w1q, w2q, …, wMq) wij: poids du terme ti dans le document dj : tf*idf On parle aussi de Modèle sac de mots:La représentation vectorielle ne tient pas compte de l’ordre des mots.« Un garçon manque une pomme » est représenté par le même vecteur que « une pomme mange un garçon » c’est ce que l’on appelle « Sac de mots » (Bag of words)   Une collection de n documents et M termes distincts peut être représentée sous forme de matrice ( ligne : M document , colonnes: M terme de la collection)  La requête est également représentée par un vecteur<jupyter_code>#I=tm.IndexerSimple()
#index,indexReverse=I.indexation("cisi/cisi.txt")
#les termes de l'index (les termes normalisé présents dans la collection)
"""
T=[]
for c,v in indexReverse.items():
    T.append(c)
#La liste de tous les Documents la collection
D=[]
for c,v in index.items():
    D.append(c)

#Construction de la matrice de vecteurs de document
M={}

for d in D:
    di={} #vecteur associé à chaque document
    r=I.getTfIDFsForDoc(d)
    for w in T:
        if (w in r):
            di[w]=r[w] 
        else:
            di[w]=0
    M[d]=di
"""       
#print(M["2"])<jupyter_output><empty_output><jupyter_text>Construction du vecteur requete. Je normalise les termes de la reqête parceque les termes de l'indexation ont tous été normalisé<jupyter_code>"""
q="home sales top"
PS=tm.tr.PorterStemmer()
s=PS.getTextRepresentation(q)
Q={}
for w in T:
    if (w in s):
        Q[w]=s[w] 
    else:
        Q[w]=0
"""    
#print(Q)<jupyter_output><empty_output><jupyter_text>La pertinence est traduite en une similarité vectorielle:un document est d'autant plus pertinent à une requête que le vecteur associé est similaire à celui de la requête. Le degré de correspondance de R(d,q):Produit scalaire des deux vecteurs:R(di,qk) = somme des produit des poids des termes de la reqête et ceux du document di , pour i=1,....,n<jupyter_code>"""
Scores={}
for c,v in M.items():
    p=0
    for w in T:
        p=p+(v[w]*Q[w])
    Scores[c]=p
"""   
#print(Scores)<jupyter_output><empty_output><jupyter_text>Exercice 2 – Projet ”Moteur de Recherche” : Etape Appariemen 1) Représentation pondérée des documents et de la requêteUne classe générique est une classe qui peut être réutilisée pour des objets de différents types<jupyter_code>class Weighter:
    
    def __init__(self,T):
        self.I=tm.IndexerSimple()
        self.index,self.indexReverse=self.I.indexation(T)
        self.mots=[c for c,v in self.indexReverse.items()] #récupère tous les mots de la collection
        self.docs=[c for c,v in self.index.items()] #répère la liste de tous les id des documents de la collection
     
     #getWeightsForDoc retourne les poids des termes pour un document dont l’identiﬁant est idDoc
        
    def getWeightsForDoc(self,idDoc):
        raise NotImplementedError
    
    
    #getWeightsForStem retourne les poids du terme stem pour tous les documents qui le contiennent
    
    def  getWeightsForStem(self,stem):
        raise NotImplementedError
        
    
    #getWeightsForQuery retourne les poids des termes de la requˆete
    
    def  getWeightsForQuery(self,query):
        raise NotImplementedError<jupyter_output><empty_output><jupyter_text>w t,d = tf t,d et w t,q = 1 si t ∈ q, O sinon;<jupyter_code>class Weighter1(Weighter):
    
    def __init__(self,T):
        self.I=tm.IndexerSimple()
        self.index,self.indexReverse=self.I.indexation(T)
        self.mots=[c for c,v in self.indexReverse.items()] #récupère tous les mots de la collection
        self.docs=[c for c,v in self.index.items()] #répère la liste de tous les id des documents de la collection
     
    def getWeightsForDoc(self,idDoc):
        d=self.index[idDoc]    
        for m in self.mots:   #tous les termes de la collection qui ne sont pas dans ce doc ont un poid de 0
            if(m not in d):
                d[m]=0
        return d
    
    def  getWeightsForStem(self,stem):
        return self.indexReverse[stem]
   
    def  getWeightsForQuery(self,query):
        PS=tm.tr.PorterStemmer()
        s=PS.getTextRepresentation(query)
        q={}
        for m in self.mots:
            if(m in s):
                q[m]=1
            else:
                q[m]=0
        return q
   
#w=Weighter1("cisi/cisi.txt")<jupyter_output><empty_output><jupyter_text>Test:on affiche le poid de tous les terme (normalisé) de la collection pour le document dont l'id est 1<jupyter_code>#print(w.getWeightsForDoc("1"))<jupyter_output><empty_output><jupyter_text>on affiche le poid du mot "present"  "seulement" pour les documents qui le contient<jupyter_code>#print(w.getWeightsForStem("present"))<jupyter_output><empty_output><jupyter_text>on affiche pour chaque terme de la collection le poid de ce terme pour la reqête<jupyter_code>#print(w.getWeightsForQuery("home saled top"))<jupyter_output><empty_output><jupyter_text>w t,d = tf t,d et w t,q = tf t,q<jupyter_code>class Weighter2(Weighter):
    
    def __init__(self,T):
        self.I=tm.IndexerSimple()
        self.index,self.indexReverse=self.I.indexation(T)
        self.mots=[c for c,v in self.indexReverse.items()] #récupère tous les mots de la collection
        self.docs=[c for c,v in self.index.items()] #répère la liste de tous les id des documents de la collection
     
    def getWeightsForDoc(self,idDoc):
        d=self.index[idDoc]    
        for m in self.mots:   #tous les termes de la collection qui ne sont pas dans ce doc ont un poid de 0
            if(m not in d):
                d[m]=0
        return d
    
    def  getWeightsForStem(self,stem):
        return self.indexReverse[stem]
   
    def  getWeightsForQuery(self,query):
        PS=tm.tr.PorterStemmer()
        s=PS.getTextRepresentation(query)
        q={}
        for m in self.mots:
            if(m in s):
                q[m]=s[m]   #il y a que cette ligne qui a changé
            else:
                q[m]=0
        return q<jupyter_output><empty_output><jupyter_text>w t,d = tf t,d et w t,q = idf t si t ∈ q, 0 sinon;<jupyter_code>class Weighter3(Weighter):
    
    def __init__(self,T):
        self.I=tm.IndexerSimple()
        self.index,self.indexReverse=self.I.indexation(T)
        self.mots=[c for c,v in self.indexReverse.items()] #récupère tous les mots de la collection
        self.docs=[c for c,v in self.index.items()] #répère la liste de tous les id des documents de la collection
     
    def getWeightsForDoc(self,idDoc):
        d=self.index[idDoc]    
        for m in self.mots:   #tous les termes de la collection qui ne sont pas dans ce doc ont un poid de 0
            if(m not in d):
                d[m]=0
        return d
    
    def  getWeightsForStem(self,stem):
        return self.indexReverse[stem]
   
    def  getWeightsForQuery(self,query):
        PS=tm.tr.PorterStemmer()
        s=PS.getTextRepresentation(query)
        q={}
        N=len(self.docs)
        for m in self.mots:
            if(m in s):
                idf=math.log( (1+N) / (1+len(self.indexReverse[m])) )
                q[m]=idf 
            else:
                q[m]=0
        return q
#w=Weighter3("cisi/cisi.txt")
#print(w.getWeightsForQuery("home saled top"))<jupyter_output><empty_output><jupyter_text>w t,d = 1 + log(tf t,d) si t ∈ d, 0 sinon; et w t,q = idf t si t ∈ q, 0 sinon<jupyter_code>class Weighter4(Weighter):
    
    def __init__(self,T):
        self.I=tm.IndexerSimple()
        self.index,self.indexReverse=self.I.indexation(T)
        self.mots=[c for c,v in self.indexReverse.items()] #récupère tous les mots de la collection
        self.docs=[c for c,v in self.index.items()] #répère la liste de tous les id des documents de la collection
     
    def getWeightsForDoc(self,idDoc):
        d=self.index[idDoc]    
        di={}
        for m in self.mots:   #tous lew=Weighter3("cisi/cisi.txt")s termes de la collection qui ne sont pas dans ce doc ont un poid de 0
            if(m in d):
                di[m]=1+math.log(d[m])
            else:
                di[m]=0
        return di
    
    def  getWeightsForStem(self,stem):
        r=self.indexReverse[stem]
        d={}
        for c,v in r.items():
            d[c]=1+math.log(v)
        return d
   
    def  getWeightsForQuery(self,query):
        PS=tm.tr.PorterStemmer()
        s=PS.getTextRepresentation(query)
        q={}
        N=len(self.docs)
        for m in self.mots:
            if(m in s):
                idf=math.log( (1+N) / (1+len(self.indexReverse[m])) )
                q[m]=idf 
            else:
                q[m]=0
        return q
#w=Weighter4("cisi/cisi.txt")
#print(w.getWeightsForDoc("1")) 
#print(w.getWeightsForStem("present"))<jupyter_output><empty_output><jupyter_text>wt,d = (1 + log(tf t,d))x idf t si t ∈ d, 0 sinon; et wt,q = (1 + log(tf t,q))x idf t si t ∈ q, 0.<jupyter_code>class Weighter5(Weighter):
    
    def __init__(self,T):
        self.I=tm.IndexerSimple()
        self.index,self.indexReverse=self.I.indexation(T)
        self.mots=[c for c,v in self.indexReverse.items()] #récupère tous les mots de la collection
        self.docs=[c for c,v in self.index.items()] #répère la liste de tous les id des documents de la collection
     
    def getWeightsForDoc(self,idDoc):
        d=self.index[idDoc]    
        di={}
        N=len(self.docs)
        for m in self.mots:   #tous lew=Weighter3("cisi/cisi.txt")s termes de la collection qui ne sont pas dans ce doc ont un poid de 0
            if(m in d):
                idf=math.log( (1+N) / (1+len(self.indexReverse[m])) )
                di[m]=(1+math.log(d[m]))*idf
            else:
                di[m]=0
        return di
    
    def  getWeightsForStem(self,stem):
        r=self.indexReverse[stem]
        d={}
        N=len(self.docs)
        idf=math.log( (1+N) / (1+len(r)) )
        for c,v in r.items():           
            d[c]=(1+math.log(v))*idf
        return d
   
    def  getWeightsForQuery(self,query):
        PS=tm.tr.PorterStemmer()
        s=PS.getTextRepresentation(query)
        q={}
        N=len(self.docs)
        for m in self.mots:
            if(m in s):
                idf=math.log( (1+N) / (1+len(self.indexReverse[m])) )
                q[m]=(1+math.log(s[m]))*idf 
            else:
                q[m]=0
        return q<jupyter_output><empty_output><jupyter_text>2) Modèles de RI<jupyter_code>class IRModel:
    
    def __init__(self,T):
        self.I=tm.IndexerSimple()
        self.index,self.indexReverse=I.indexation(T)
        self.mots=[c for c,v in self.indexReverse.items()] #récupère tous les mots de la collection
        self.docs=[c for c,v in self.index.items()] #répère la liste de tous les id des documents de la collection
     
    def  getScores(self,query):   # retourne les scores des documents pour une requête
        raise NotImplementedError
        
    def getRanking(self,query): # retourne une liste de couples (document-score) ordonn´ee par score d´ecroissante
        scores=self.getScores(query)
        return sorted(scores.items(), key=lambda t: t[1],reverse=True)<jupyter_output><empty_output><jupyter_text>question 2.2) Déﬁnir une classe Vectoriel qui hérite de IRModel. Elle comporte en paramètres un Weighter déﬁni dans l’´etape précédente ainsi qu’un booléen normalized permettant de déﬁnir la fonction de score (produit scalaire si faux et score cosinus si vrai). ( les normes des vecteurs des documents ne doivent pas être calculés à chaque nouvelle requête ).<jupyter_code>class Vectoriel(IRModel):
    
    def __init__(self,weighter, normalized):
        self.weighter=weighter
        self. normalized= normalized
        self.nd={}
        self.M={}
        
        for d in self.weighter.docs:
            self.M[d]=self.weighter.getWeightsForDoc(d) # pour chaque document de la collection , on récupère la liste des poids de chaque termes de la collection
   
        for c,v in self.M.items():
                d2=[]
                for ca,va in v.items():
                    d2.append(va*va)
                self.nd[c]=math.sqrt(sum(d2))  #on calcule la norme de chaque document

    def getScores(self,query):
        Scores={}
        q=self.weighter.getWeightsForQuery(query)
        for c,v in self.M.items():
                p=0              
                for w in self.weighter.mots:
                    p=p+(v[w]*q[w])
                Scores[c]=p        
                
        if(self.normalized):
            q2=[]
            for c,v in q.items():
                q2.append(v*v)
                
            nq=math.sqrt(sum(q2))
            Scores2={}
            
            for c,v in Scores.items():
                Scores2[c]=v/(self.nd[c]*nq)  
            Scores=Scores2
            
        return Scores
            
#V=Vectoriel(w,False)
#S=V.getScores("home saled top")
#print(S)
#V=Vectoriel(w,True)
#S=V.getScores("home saled top")
#print(S)
#print(V.getRanking("home saled top"))<jupyter_output><empty_output><jupyter_text>question 2.3) Déﬁnir les classes ModeleLangue et Okapi permettant de calculer les scores pour respectivement le modèle de langue (lissage Jelinek-Mercer) et Okapi-BM25  Modèle de langue :Un modèle de langue est défini par son vocabulaire (mots simples, séquence de mots).Chaque mot (m)/séquence de mots(m1m2..mn) a une probabilité d’être généré(e).Le but est de calculer --> P(s|M):    -s une observation (séquence de mots/texte) quelconque     -Probabilité d’observer s dans le modèle (la langue) MDéfinir la taille des séquences générées par le modèle ?     --> Séquence de 1 mot, 2 mots, 3 mots, …  Estimer le modèle à probabilité de chaque séquence générée ? Calculer la probabilité d’une observation (un texte) quelconque? On choisit : Séquence d’un mot --> modèle unigram .  soit s une observation (un texte) de n mots s=m1 m2…mn .Unigram – (M génère des séquences de 1 mot) . P(S|M)= P(m1m2....mn)= produit des P(mi|M) , pour i=1,....,n.UNigram : P(mi | M) = tf (mi) /nombre total de mots dans M.:Problème des fréquences nulles (zéro) :    -Si un événement (un mot de la séquence) n’apparait pas dans le modèle, le modèle lui assigne une probabilité  0     -Solution : assigner des probabilités différentes de zéro aux événements (mots) absentsLissage par interpolation :    - Les méthodes de « discounting »  traitent les mots qui n’apparaissent pas dans le corpus de la même manière. Or, il y a des mots qui peuvent être plus fréquents que d’autres     -Solution – Interpoler le modèle en utilisant d’autres sources d’évidence (par exemple la collection de documents) Interpolation  (Jelinek-Mercer) :    -RSV(Q,d)= Produit ( (1-lambda) * p(t|Mc) + lambda*p(t|Md)), pourtous les termes t de la requête.    P(t|Mc)=p(t)=frequence du terme t dans la collection / somme des fréquence de tous les termes de la collection    -(λ=0.8 pour les requêtes courtes et 0.2 pour les requêtes longues)<jupyter_code>class ModeleLangue(IRModel):
    
    def __init__(self,weighter,l):
        self.weighter=weighter        
        L=[sum(v.values()) for c,v in self.weighter.indexReverse.items()]
        self.total=sum(L)
        self.l=l
        
    def ptD(self,t,d):
        s=sum(d.values())
        if(t in d):
            return d[t]/s
        else:
            return 0
        
    def ptC(self,t):      
        if(t in self.weighter.indexReverse):    
            return sum(self.weighter.indexReverse[t].values()) / self.total
        else:
            return 0
        
    def getScores(self,query):
        PS=tm.tr.PorterStemmer()
        s=PS.getTextRepresentation(query)
        Scores={}
        for d in self.weighter.docs:
            p=1
            for m,v in s.items():
                p=p*( (1-self.l) * self.ptC(m) + self.l*self.ptD(m,self.weighter.index[d]))
            Scores[d]=p
        return Scores
#M=ModeleLangue(w,0.8)
#print(M.getScores("home saled top"))
#print(M.getRanking("home saled top"))<jupyter_output><empty_output><jupyter_text>Okapi-BM25<jupyter_code>class Okapi(IRModel):
    
    def __init__(self,weighter,k,b):
        self.weighter=weighter  
        self.k=k
        self.b=b

        
    def getScores(self,query):
        Scores={}
        PS=tm.tr.PorterStemmer()
        s=PS.getTextRepresentation(query)
        avgdl=0
        L=[]
        
        for c,v in self.weighter.index.items():
            t=sum(v.values())
            L.append(t)
        avgdl=(sum(L)/len(self.weighter.index))
        
        for d in self.weighter.docs:
            t=self.weighter.index[d]
            D=sum(t.values())
            for c,v in s.items():
                sc=0
                #ATTENTION : SI LE MOT DE LA REQUETE NE FIGURE PAS DANS L'INDEXINVERSE 
                if(c not in self.weighter.indexReverse ):
                    n=0
                else:
                    n=self.weighter.indexReverse[c]
                idf=math.log( (1+len(self.weighter.docs)) / (1+len(n)))
        
                if(c in t):
                    tf=t[c]
                else:
                    tf=0
                if( (tf + self.k * (1-self.b+self.b*(D/avgdl))) != 0 ):
                    sc=sc + idf * ( ( tf*(self.k+1) ) / ( tf + self.k * (1-self.b+self.b*(D/avgdl)) ) )
            Scores[d]=sc
        return Scores
#M=Okapi(w,1.2,0.75)
#print(M.getRanking("home saled top"))<jupyter_output><empty_output><jupyter_text>3 Bonus - Très fortement conseillés question 2.4)<jupyter_code>Q=["convention depth  week profil","object issu assign", "inter feeder result " , 
   "emphasi length demonstrat organ" , "group receipt connect relev", "run fundament assign librari",
  "vindic howel  baxter data", "mechan timeli physical negoti", "mari historiograph desper demonstrat",
  "home saled top"]<jupyter_output><empty_output><jupyter_text>On sépare l’ensemble des requêtes en deux ensembles (train/test)<jupyter_code>train= Q[:int(len(Q)/2)]
test= Q[int(len(Q)/2):]
#print(train)
#print(test)<jupyter_output><empty_output><jupyter_text>On déﬁnit une grille de valeurs à tester (de 0 à 1 par pas de 0.1). On expérimente chaque combinaison possible pour chaque modèle sur le jeu de données train Rappel : il y a 4 classe weighter différentes , chacune a sa façon de calculer le poid d'un terme pourla requête et le poid d'un terme pour le document  Test sur l'ensemble "train"  Test Modele LangueDans le modèle de langue il y a un seul paramètre qui varie (c'est le lambda). (Voir en haut à quoi correspond ce lambda)<jupyter_code>#w=Weighter1("cisi/cisi.txt")<jupyter_output><empty_output><jupyter_text>Pour chaque valeur de lambda , on cré un modele de langue parametré avec ce lambda. Ensuite pour ce modèle de langue , on parcourt chaque requête dans l'ensemble train , et on calcule pour chacune de ces reqête le score associé à chaque document de la collection "cisi.txt"<jupyter_code>"""
G={}
for i in np.arange(0,1.1,0.1):
    ML=ModeleLangue(w,i)
    d={}
    for c in train:
        scores=ML.getRanking(c)
        d[c]=scores
    G[i]=d
"""       
#print(G[0.1])<jupyter_output><empty_output><jupyter_text>Observation : pour chaque valeur de lambda , on obtient pour chaque requête un ensemble de scores de documents différent  Test Modele VectorielSur ce modèle il n'y a pas de paramètres.Mais il peut être intéressant de changer notre weighter , car chaque weighter a sa manière de calculer le vecteur de poid des documents et de la requête<jupyter_code>"""
w1=Weighter1("cisi/cisi.txt")
w2=Weighter2("cisi/cisi.txt")
w3=Weighter3("cisi/cisi.txt")
w4=Weighter4("cisi/cisi.txt")
w5=Weighter5("cisi/cisi.txt")

W=[w1,w2,w3,w4,w5]
"""
"""
Gw={}
i=1
for w in W:
    V=Vectoriel(w,False)  # normalized = false  , on a utilisé la similarité produit scalaire
    d={}
    for c in train:
        scores=V.getRanking(c)
        d[c]=scores
    Gw[str(i)]=d
    i+=1
"""
#print(Gw['4'])
#print(Gw['5'])<jupyter_output><empty_output><jupyter_text>Observation : pour des weighter différents , sur chaque modele Vectoriel construit , on remarque que l'ordre des documents classé selon leur score,  obtenue sur chaque reqête , est à peu près le même  Test Modele Okapi Pour chaque valeur de k et b , on cré un modele Okapi parametré avec ce k et ce b. Ensuite pour ce modèle Okapi , on parcourt chaque requête dans l'ensemble train , et on calcule pour chacune de ces reqête le score associé à chaque document de la collection "cisi.txt"<jupyter_code>"""
G={}
for k in np.arange(0,1.1,0.1):
    for b in np.arange(0,1.1,0.1):
        Mo=Okapi(w,k,b)
        d={}
        for c in train:
            scores=Mo.getRanking(c)
            d[c]=scores
        G[(k,b)]=d
"""       
#print(G[(0.0,0.0)])
#print(G[(1.0,0.8)])<jupyter_output><empty_output><jupyter_text>On remarque que pour des valeurs de paramètres différents sur k et b , on obtient des scores de documents différents  sur chacune des requêtes, mais l'ordre de pertinence des documents sur chacune des reqêtes n'a pas l'air de changer on pourra tester dans le TME 3 la combinaison qui obtient la meilleure valeur de métrique (MAP, Pr´ecision, ...)on applique ces valeurs sur le jeu de test  Test sur l'ensemble "test"  Test Model Langue<jupyter_code>"""
G={}
for i in np.arange(0,1.1,0.1):
    ML=ModeleLangue(w,i)
    d={}
    for c in test:
        scores=ML.getRanking(c)
        d[c]=scores
    G[i]=d
"""       
#print(G[0.8])
#print(G[0.9])<jupyter_output><empty_output><jupyter_text>Observation : pour des valeurs de lambda différentes , on obtient à peu près le même ordre score de pertinence.C'est la valeur associé à chacun des scores qui change , mais cela n'a pas l'air de modifier l'ordre de pertience des documents sur chaque requête.  Test Model Okapi<jupyter_code>"""
G={}
for k in np.arange(0,1.1,0.1):
    for b in np.arange(0,1.1,0.1):
        Mo=Okapi(w,k,b)
        d={}
        for c in test:
            scores=Mo.getRanking(c)
            d[c]=scores
        G[(k,b)]=d
"""        
#print(G[(0.5,0.2)])
#print(G[(0.9,0.4)])<jupyter_output><empty_output>
StarcoderdataJupyterStructuredCleanDedup