<jupyter_start><jupyter_text>Classification de documents : prise en main des outilsLe but de ce TP est de classer des documents textuels... Dans un premier temps, nous allons vérifier le bon fonctionnement des outils sur des données jouets puis appliquer les concepts sur des données réelles. Conception de la chaine de traitementPour rappel, une chaine de traitement de documents classique est composée des étapes suivantes:1. Lecture des données et importation    - Dans le cadre de nos TP, nous faisons l'hypothèse que le corpus tient en mémoire... Si ce n'est pas le cas, il faut alors ajouter des structures de données avec des buffers (*data-reader*), bien plus complexes à mettre en place.    - Le plus grand piège concerne l'encodage des données. Dans le TP... Pas (ou peu) de problème. Dans la vraie vie: il faut faire attention à toujours maitriser les formats d'entrée et de sortie.1. Traitement des données brutes paramétrique. Chaque traitement doit être activable ou desactivable + paramétrable si besoin.    - Enlever les informations *inutiles* : chiffre, ponctuations, majuscules, etc...     **L'utilité dépend de l'application!**    - Segmenter en mots (=*Tokenization*)    - Elimination des stop-words    - Stemming/lemmatisation (racinisation)    - Byte-pair encoding pour trouver les mots composés (e.g. Sorbonne Université, Ville de Paris, Premier Ministre, etc...)1. Traitement des données numériques    - Normalisation *term-frequency* / binarisation    - Normalisation *inverse document frequency*    - Elimination des mots rares, des mots trop fréquents    - Construction de critère de séparabilité pour éliminer des mots etc...1. Apprentissage d'un classifieur    - Choix du type de classifieur    - Réglage des paramètres du classifieur (régularisation, etc...) Exploitation de la chaine de traitementOn appelle cette étape la réalisation d'une campagne d'expériences: c'est le point clé que nous voulons traviller en TAL cette année.1. Il est impossible de tester toutes les combinaisons par rapport aux propositions ci-dessus... Il faut donc en éliminer un certain nombre.    - En discutant avec les experts métiers    - En faisant des tests préliminaires1. Après ce premier filtrage, il faut:    - Choisir une évaluation fiable et pas trop lente (validation croisée, leave-one-out, split apprentissage/test simple)    - Lancer des expériences en grand        - = *grid-search*        - parallèliser sur plusieurs machines        - savoir lancer sur un serveur et se déconnecter1. Collecter et analyser les résultats InférenceL'inférence est ensuite très classique: la chaine de traitement optimale est apte à traiter de nouveaux documents Etape 1: charger les données<jupyter_code># !pip install ipython
# !pip install nbconvert
# !jupyter nbconvert --to python classifDoc_2021.ipynb
import numpy as np
import matplotlib.pyplot as plt

import codecs
import re
import os.path
# Chargement des données:
def load_pres(fname):
    alltxts = []
    alllabs = []
    s=codecs.open(fname, 'r','utf-8') # pour régler le codage
    while True:
        txt = s.readline()
        if(len(txt))<5:
            break
        lab = re.sub(r"<[0-9]*:[0-9]*:(.)>.*","\\1",txt)
        txt = re.sub(r"<[0-9]*:[0-9]*:.>(.*)","\\1",txt)
        if lab.count('M') >0:
            alllabs.append(-1)
        else: 
            alllabs.append(1)
        alltxts.append(txt)
    return alltxts,alllabs
fname = "data/corpus.tache1.learn.utf8"

alltxts, alllabs = load_pres(fname)
print(len(alltxts),len(alllabs))
print(alltxts[0])
print(alllabs[0])
print(alltxts[-1])
print(alllabs[-1])
# def load_movies(path2data): # 1 classe par répertoire
#     alltxts = [] # init vide
#     labs = []
#     cpt = 0
#     for cl in os.listdir(path2data): # parcours des fichiers d'un répertoire
#         for f in os.listdir(path2data+cl):
#             txt = open(path2data+cl+'/'+f).read()
#             alltxts.append(txt)
#             labs.append(cpt)
#         cpt+=1 # chg répertoire = cht classe
#     return alltxts,labs
# path = "movies1000/"

# alltxts,alllabs = load_movies(path)
# print(len(alltxts),len(alllabs))
# print(alltxts[0])
# print(alllabs[0])
# print(alltxts[-1])
# print(alllabs[-1])<jupyter_output><empty_output><jupyter_text>Transformation paramétrique du texteVous devez tester, par exemple, les cas suivants:- transformation en minuscule ou pas- suppression de la ponctuation- transformation des mots entièrement en majuscule en marqueurs spécifiques- suppression des chiffres ou pas- conservation d'une partie du texte seulement (seulement la première ligne = titre, seulement la dernière ligne = résumé, ...)- stemming- ...Vérifier systématiquement sur un exemple ou deux le bon fonctionnement des méthodes sur deux documents (au moins un de chaque classe).<jupyter_code>import string
import re
import unicodedata
import nltk
from nltk.corpus import stopwords
from nltk.stem import *
from nltk.stem.snowball import SnowballStemmer

def preprocessing(X):
    res = []
    for doc in X:
        punc = string.punctuation  # recupération de la ponctuation
        punc += '\n\r\t'
        doc = doc.translate(str.maketrans(punc, ' ' * len(punc)))
        doc = unicodedata.normalize('NFD', doc).encode('ascii', 'ignore').decode("utf-8")
        doc = doc.lower()
        doc = re.sub('[0-9]+', '', doc)
        res.append(doc)
    return np.array(res)

def formal(X):
    stemmer = SnowballStemmer(language='french')
#     nltk.download('stopwords')
    res = []
    stop = stopwords.words('french')
    for doc in X:
        new_doc = ""
        for w in doc.split():
            if w not in stop:
                new_doc += w + " "
        new_doc = [stemmer.stem(X) for X in new_doc.split()]
        new_doc = " ".join(new_doc)
        res.append(new_doc)
    return res
X = np.array(alltxts[:1000])
Y = np.array(alllabs[:1000])
        
print(X[0])
X_preprocess = preprocessing(X)
print(X_preprocess[0])
X_train = formal(X_preprocess)
print("\n",X_train[0])<jupyter_output>Quand je dis chers amis, il ne s'agit pas là d'une formule diplomatique, mais de l'expression de ce que je ressens.

 quand je dis chers amis  il ne s agit pas la d une formule diplomatique  mais de l expression de ce que je ressens  

 quand dis cher amis agit formul diplomat express ressen<jupyter_text>Extraction du vocabulaireExploration préliminaire des jeux de données.- Quelle est la taille d'origine du vocabulaire?- Que reste-t-il si on ne garde que les 100 mots les plus fréquents? [word cloud]- Quels sont les 100 mots dont la fréquence documentaire est la plus grande? [word cloud]- Quels sont les 100 mots les plus discriminants au sens de odds ratio? [word cloud]- Quelle est la distribution d'apparition des mots (Zipf)- Quels sont les 100 bigrammes/trigrammes les plus fréquents?<jupyter_code>from wordcloud import WordCloud
from sklearn.feature_extraction.text import CountVectorizer

print(X.shape)
print(Y.shape)

def flatten(A):
    rt = []
    for i in A:
        if isinstance(i,list): 
            rt.extend(flatten(i))
        else: 
            rt.append(i)
    return rt

words = "".join(flatten(X))
print(words[:200])
wordcloud = WordCloud(background_color='white', max_words=100).generate(words)
plt.imshow(wordcloud)
plt.axis("off")
plt.show()

# vectorizer = CountVectorizer()
# vector = vectorizer.fit_transform(X).toarray()
# names = vectorizer.get_feature_names()
# print(vector.shape)
# n, m = vector.shape
# vector = np.where(vector == 0, 0, 1)
# sums = vector.sum(axis=0)
# res = np.sort(sums)[sums.size-100:]
# print(res)
# words = "".join(flatten(X))
# wordcloud = WordCloud(background_color='white', max_words=100).generate(words)
# plt.imshow(wordcloud)
# plt.axis("off")
# plt.show()
words = "".join(flatten(X_preprocess[:1000]))
unique_words, count = np.unique(words.split(), return_counts=True)
# count = np.where(count > 100, 100, count)
count = np.log(count)
unique_words, count = np.unique(words.split(), return_counts=True)

count_sort_ind = np.argsort(count)
unique_words = unique_words[count_sort_ind]
count = np.sort(count)
print(count)
print(unique_words)

fig = plt.figure(figsize=(8,8))
ax = fig.add_axes([0,0,1,1])
ax.bar(np.arange(len(count)),np.log(count))
plt.title("Log du nombre d'apparition / mot")
plt.show()
unique_words, count = np.unique(words.split(), return_counts=True)

count_sort_ind = np.argsort(count)
unique_words = unique_words[count_sort_ind]
count = np.sort(count)
print(count)
print(unique_words)

fig = plt.figure(figsize=(10,10))
ax = fig.add_axes([0,0,1,1])
ax.bar(unique_words[len(count)-25:],count[len(count)-25:])
plt.title("Nombre d'apparition / mot les plus frequent")
plt.show()<jupyter_output>[   1    1    1 ...  713  829 1245]
['insiste' 'malabar' 'malentendus' ... 'et' 'la' 'de']<jupyter_text>Question qui devient de plus en plus intéressante avec les approches modernes:est-il possible d'extraire des tri-grammes de lettres pour représenter nos documents?Quelle performances attendrent? Quels sont les avantages et les inconvénients d'une telle approche?<jupyter_code># On peut le faire assez facilemment avec sklearn
# Cela donne plus de contexte a nos mots mais les probabilites de tri-grammes vont etre tres faible<jupyter_output><empty_output><jupyter_text>Modèles de Machine LearningAvant de lancer de grandes expériences, il faut se construire une base de travail solide en étudiant les questions suivantes:- Combien de temps ça prend d'apprendre un classifieur NB/SVM/RegLog sur ces données en fonction de la taille du vocabulaire?- La validation croisée est-elle nécessaire? Est ce qu'on obtient les mêmes résultats avec un simple *split*?- La validation croisée est-elle stable? A partir de combien de fold (travailler avec différentes graines aléatoires et faire des statistiques basiques)?<jupyter_code># Cela prend beaucoup de temps avec des donnees textuels car nous avons un grand nombre de variables
# Cela depend fortement de la taille du vocabulaire
# La validation est tres pratique car on train nos modeles sur toutes les donnes contrairement au split
# Ici on utlise le split car bien plus rapide, on pourra envisager la validation croisee quand on aura deja fait un choix au niveau des modeles<jupyter_output><empty_output><jupyter_text>Première campagne d'expériencesLes techniques sur lesquelles nous travaillons étant sujettes au sur-apprentissage: trouver le paramètre de régularisation dans la documentation et optimiser ce paramètre au sens de la métrique qui vous semble la plus appropriée (cf question précédente). - IL FAUT FAIRE PLEIN DE VARIABLE X_train1 X_train2 ... AVEC DES DONNES EN UTILISANT CountVectorizer() (ngram, preprocess, mindf, maxdf etc...)- COMPRENDRE LESQUELS SONT LES PLUS IMPORTANTS (mindf, maxdf)- PUIS APRES IL FAUT EQUILIBRER LES CLASSES- PUIS OPTIMISER LES MODELES- PUIS TESTER LES MODELES ET LES COMPARER- FAIRE DES COURBES PLT POUR MONTRER QU'ON A TROUVER LE MODEL OPTIMAL (mindf/test_acc)<jupyter_code>import sklearn.naive_bayes as nb
from sklearn import svm
from sklearn import linear_model as lin
from sklearn.model_selection import train_test_split
import string
import re
import unicodedata
import nltk
from nltk.corpus import stopwords
from nltk.stem.snowball import SnowballStemmer
from sklearn.feature_extraction.text import CountVectorizer
from sklearn.feature_extraction.text import TfidfVectorizer<jupyter_output><empty_output><jupyter_text>Fonction pour le traitement de donnees<jupyter_code>def preprocessing(X):
    res = []
    punc = string.punctuation  
    punc += '\n\r\t'
    for doc in X:    
        doc = doc.translate(str.maketrans(punc, ' ' * len(punc)))
        doc = unicodedata.normalize('NFD', doc).encode('ascii', 'ignore').decode("utf-8")
        doc = doc.lower()
        doc = re.sub('[0-9]+', '', doc)
        res.append(doc)
    return np.array(res)

def formal(X):
    stemmer = SnowballStemmer(language='french')
#     nltk.download('stopwords')
    res = []
    stop = stopwords.words('french')
    for doc in X:
        new_doc = ""
        for w in doc.split():
            if w not in stop:
                new_doc += w + " "
        new_doc = [stemmer.stem(X) for X in new_doc.split()]
        new_doc = " ".join(new_doc)
        res.append(new_doc)
    return res<jupyter_output><empty_output><jupyter_text>Importation des deux fichiers contenant les donnees<jupyter_code>fname = "data/corpus.tache1.learn.utf8"
alltxts_test, alllabs_test = load_pres(fname)
X = np.array(alltxts)
Y = np.array(alllabs)<jupyter_output><empty_output><jupyter_text>Fonctions permettant de faire notre selection de modele suivant les données<jupyter_code>model_names = ["SVC1", "SVC0.8", "SVC0.6", "NB", "LR"]

def train_models_and_get_acc(X_train, X_test, Y_train, Y_test, display):
    print("--> SVC reg=1.0 :")
    clf = svm.LinearSVC(class_weight="balanced", C=1.0)
    clf.fit(X_train, Y_train) 
    acc1 = np.round(clf.score(X_test, Y_test)*100, 2)
    print("Acc : ", acc1, " %")
    if display : display_infos(clf, X_test, Y_test)
    
    print("--> SVC reg=0.8 :")
    clf = svm.LinearSVC(class_weight="balanced", C=0.8)
    clf.fit(X_train, Y_train) 
    acc2 = np.round(clf.score(X_test, Y_test)*100, 2)
    print("Acc : ", acc2, " %")
    if display : display_infos(clf, X_test, Y_test)
    
    print("--> SVC reg=0.6 :")
    clf = svm.LinearSVC(class_weight="balanced", C=0.6)
    clf.fit(X_train, Y_train) 
    acc3 = np.round(clf.score(X_test, Y_test)*100, 2)
    print("Acc : ", acc3, " %")
    if display : display_infos(clf, X_test, Y_test)
    
    print("--> NB :")
    clf = nb.MultinomialNB()
    clf.fit(X_train, Y_train)
    acc4 = np.round(clf.score(X_test, Y_test)*100, 2)
    print("Acc : ", acc4, " %")
    if display : display_infos(clf, X_test, Y_test)
    
    print("--> LR :")
    clf = lin.LogisticRegression(class_weight="balanced")
    clf.fit(X_train, Y_train)
    acc5 = np.round(clf.score(X_test, Y_test)*100, 2)
    print("Acc : ", acc5, " %") 
    if display : display_infos(clf, X_test, Y_test)
    
    return [acc1, acc2, acc3, acc4, acc5]
def display_infos(clf, X_test, Y_test):
    # Check le nombre de predictions pour chaque label
    predictions = clf.predict(X_test)
    unique, counts = np.unique(predictions, return_counts=True)
    print("/!\ Prediction counts for label ", unique, " --> ", counts)
    # Check le nombre de predictions pour chaque label
    unique, counts = np.unique(Y_test, return_counts=True)
    print("/!\ Ground truth counts for label ", unique, " --> ", counts)
    # Check la precision du label en inferiorite
    acc = get_inf_acc(predictions, Y_test)
    print("/!\ Accuracy of inferior label :", acc, "%")
    # Check la precision du label en superiorite
    acc = get_sup_acc(predictions, Y_test)
    print("/!\ Accuracy of superior label :", acc, "%\n")
def get_inf_acc(predictions, Y_test):
    idx_inf = np.where(predictions==-1, True, False)
    tmp_pred = predictions[idx_inf]
    tmp_real = Y_test[idx_inf]
    cpt = 0
    for i in range(len(tmp_pred)):
        if tmp_pred[i] == tmp_real[i]:
            cpt += 1
    return np.round((cpt/len(tmp_pred))*100, 2)

def get_sup_acc(predictions, Y_test):
    idx_inf = np.where(predictions==1, True, False)
    tmp_pred = predictions[idx_inf]
    tmp_real = Y_test[idx_inf]
    cpt = 0
    for i in range(len(tmp_pred)):
        if tmp_pred[i] == tmp_real[i]:
            cpt += 1
    return np.round((cpt/len(tmp_pred))*100, 2)
def get_all_data_vectorized(X, X_test, Y, Y_test, vectorizer, transformer=None):
    X_vector = vectorizer.fit_transform(X)
    if transformer is not None:
        transformer = transformer.fit(X_vector)
        X_final = transformer.transform(X_vector)
    else:
        X_final = X_vector
    X_test_vector = vectorizer.transform(X_test)
    return X_final, X_test_vector, Y, Y_test
database_models_name = ["vanilla", "ngram22", "maxfeat500", "stopwords", "min2", "min3", "min4", "max0.9", "max0.8", "max0.7", "min3_max0.7", "ngram22_maxfeat500", "tfidfvectorizer"]
def build_database(X, X_test, Y, Y_test):
    database = []

    vectorizer = CountVectorizer()
    X_train_vector, Y_test_vector, Y_train, Y_test = get_all_data_vectorized(X, X_test, Y, Y_test, vectorizer)
    database.append([X_train_vector, Y_test_vector, Y_train, Y_test])
    
    vectorizer = CountVectorizer(ngram_range=(2, 2))
    X_train_vector, Y_test_vector, Y_train, Y_test = get_all_data_vectorized(X, X_test, Y, Y_test, vectorizer)
    database.append([X_train_vector, Y_test_vector, Y_train, Y_test])
    
    vectorizer = CountVectorizer(max_features=500)
    X_train_vector, Y_test_vector, Y_train, Y_test = get_all_data_vectorized(X, X_test, Y, Y_test, vectorizer)
    database.append([X_train_vector, Y_test_vector, Y_train, Y_test])
    
    stop = stopwords.words('french')
    vectorizer = CountVectorizer(stop_words=stop)
    X_train_vector, Y_test_vector, Y_train, Y_test = get_all_data_vectorized(X, X_test, Y, Y_test, vectorizer)
    database.append([X_train_vector, Y_test_vector, Y_train, Y_test])
    
    vectorizer = CountVectorizer(min_df=2)
    X_train_vector, Y_test_vector, Y_train, Y_test = get_all_data_vectorized(X, X_test, Y, Y_test, vectorizer)
    database.append([X_train_vector, Y_test_vector, Y_train, Y_test])
    vectorizer = CountVectorizer(min_df=3)
    X_train_vector, Y_test_vector, Y_train, Y_test = get_all_data_vectorized(X, X_test, Y, Y_test, vectorizer)
    database.append([X_train_vector, Y_test_vector, Y_train, Y_test])
    vectorizer = CountVectorizer(min_df=4)
    X_train_vector, Y_test_vector, Y_train, Y_test = get_all_data_vectorized(X, X_test, Y, Y_test, vectorizer)
    database.append([X_train_vector, Y_test_vector, Y_train, Y_test])
    
    vectorizer = CountVectorizer(max_df=0.9)
    X_train_vector, Y_test_vector, Y_train, Y_test = get_all_data_vectorized(X, X_test, Y, Y_test, vectorizer)
    database.append([X_train_vector, Y_test_vector, Y_train, Y_test])
    vectorizer = CountVectorizer(max_df=0.8)
    X_train_vector, Y_test_vector, Y_train, Y_test = get_all_data_vectorized(X, X_test, Y, Y_test, vectorizer)
    database.append([X_train_vector, Y_test_vector, Y_train, Y_test])
    vectorizer = CountVectorizer(max_df=0.7)
    X_train_vector, Y_test_vector, Y_train, Y_test = get_all_data_vectorized(X, X_test, Y, Y_test, vectorizer)
    database.append([X_train_vector, Y_test_vector, Y_train, Y_test])
    
    vectorizer = CountVectorizer(max_df=0.7, min_df=3)
    X_train_vector, Y_test_vector, Y_train, Y_test = get_all_data_vectorized(X, X_test, Y, Y_test, vectorizer)
    database.append([X_train_vector, Y_test_vector, Y_train, Y_test])

    vectorizer = CountVectorizer(ngram_range=(2, 2), max_features=500)
    X_train_vector, Y_test_vector, Y_train, Y_test = get_all_data_vectorized(X, X_test, Y, Y_test, vectorizer)
    database.append([X_train_vector, Y_test_vector, Y_train, Y_test])

    vectorizer = TfidfVectorizer()
    X_train_vector, Y_test_vector, Y_train, Y_test = get_all_data_vectorized(X, X_test, Y, Y_test, vectorizer)
    database.append([X_train_vector, Y_test_vector, Y_train, Y_test])
    
    return np.array(database)
def simulation(db, display=False):
    metrics = []
    for cpt, element in enumerate(db):
        print("---------------- Testing model : ", database_models_name[cpt], " ----------------")
        X_train, X_test, Y_train, Y_test = element
        metrics.append(train_models_and_get_acc(X_train, X_test, Y_train, Y_test, display))
    return metrics<jupyter_output><empty_output><jupyter_text>Model selection manuel<jupyter_code>import warnings
warnings.filterwarnings('ignore')
# X, X_test, Y, Y_test = train_test_split((X_Y), test_size=0.2)
# db = build_database(X, X_test, Y, Y_test)
# metrics = simulation(db, display=True)
# metrics = np.array(metrics)
# n_data, n_model = metrics.shape
# print("--> Simulation done on", n_data, "different data processing and", n_model, "different models")
# print("--> Metrics :\n", metrics)
# print("--> Best accuracy :", metrics.max(), "from data model : '", database_models_name[metrics.argmax()//n_model], "' and model :'", model_names[metrics.argmax()%n_model], "'")<jupyter_output><empty_output><jupyter_text>Model selection avec sklearn<jupyter_code>from sklearn import svm, datasets
from sklearn.model_selection import GridSearchCV
model_names = ["SVC", "NB", "LR"]

def train_models_and_get_acc_sklearn(X_train, X_test, Y_train, Y_test, display):
    print("--> SVC :")
    svc = svm.SVC()
    parameters = {'kernel':(['linear']), 
                  'C':[0.5, 1, 2, 5], 
                  'class_weight':(['balanced'])}
    clf = GridSearchCV(svc, parameters, cv=5, verbose=True, n_jobs=-1)
    best_clf = clf.fit(X_train, Y_train)
    acc1 = np.round(best_clf.score(X_test, Y_test)*100, 2)
    print("Acc : ", acc1, " %")
    if display : 
        display_infos(clf, X_test, Y_test)
        performance(best_clf)
    
    print("--> NB :")
    clf = nb.MultinomialNB()
    clf.fit(X_train, Y_train)
    acc2 = np.round(clf.score(X_test, Y_test)*100, 2)
    print("Acc : ", acc2, " %")
    if display : display_infos(clf, X_test, Y_test)
    
    print("--> LR :")
    lr = LogisticRegression()
    parameters = {'max_iter' : [2000],
                  'penalty' : ['l1', 'l2'],
                  'C' : np.logspace(-4, 4, 20),
                  'solver' : ['liblinear'],
                  'class_weight':(['balanced'])}
    clf = GridSearchCV(lr, parameters, cv=5, verbose=True, n_jobs=-1)
    best_clf = clf.fit(X_train,Y_train)
    acc3 = np.round(clf.score(X_test, Y_test)*100, 2)
    print("Acc : ", acc3, " %") 
    if display : 
        display_infos(clf, X_test, Y_test)
        performance(best_clf)
    
    return [acc1, acc2, acc3]
def simulation_sklearn(db, display=False):
    metrics = []
    for cpt, element in enumerate(db):
        print("---------------- Testing model : ", database_models_name[cpt], " ----------------")
        X_train, X_test, Y_train, Y_test = element
        metrics.append(train_models_and_get_acc_sklearn(X_train, X_test, Y_train, Y_test, display))
    return metrics
def get_performance(clf):
    print('Best Score: ' + str(clf.best_score_))
    print('Best Parameters: ' + str(clf.best_params_))
# db = build_database(X, X_test, Y, Y_test)
# metrics = simulation_sklearn(db, display=True)<jupyter_output><empty_output><jupyter_text>Model selection + data selection avec sklearn<jupyter_code>test_size = 10_000 # Taille des data pour le grid search (MAX = 57400)<jupyter_output><empty_output><jupyter_text>--> Selection de data<jupyter_code>from sklearn.pipeline import Pipeline
from sklearn.feature_extraction.text import TfidfTransformer

pipeline = Pipeline(
    [
        ("vect", CountVectorizer()),
        ("tfidf", TfidfTransformer()),
        ("clf", svm.SVC()),
    ]
)

parameters = {
    "vect__min_df": (0, 1, 2, 3),
    "vect__max_df": (0.5, 0.75, 1.0),
    'vect__max_features': (None, 1000, 2000, 3000),
    "vect__ngram_range": ((1, 1), (1, 2), (1,3)),
    'tfidf__use_idf': (True, False),
    'tfidf__norm': (None, 'l1', 'l2'),
    'clf__class_weight':(['balanced']),
    'clf__C': (np.arange(0, 1, 0.1)),
    'clf__kernel':(['linear'])
                         
    # "clf__max_iter": (20,)
    # "clf__alpha": (0.00001, 0.000001),
    # "clf__penalty": ("l2", "elasticnet"),
    # 'clf__max_iter': (10, 50, 80),
}
# grid_search = GridSearchCV(pipeline, parameters, n_jobs=8, verbose=3, cv=5)
X_select, Y_select = X[:test_size], Y[:test_size]
# from time import time

# t0 = time()
# grid_search.fit(list(X_select), list(Y_select))
# print("done in %0.3fs" % (time() - t0))<jupyter_output><empty_output><jupyter_text>- 4320 fits et n_jobs=8:    - Temps pour size=2_000  -> 159s     - Temps pour size=10_000 -> 11075s (184 minutes)    - Temps pour size=20_000 ->  --> Affichage meilleur paramètres pour la data<jupyter_code># best_parameters = grid_search.best_estimator_.get_params()
# for param_name in sorted(parameters.keys()):
#     print("--->%s: %r" % (param_name, best_parameters[param_name]))<jupyter_output><empty_output><jupyter_text>--> Creation du dataset optimal<jupyter_code>fname = "data/corpus.tache1.learn.utf8"
alltxts_test, alllabs_test = load_pres(fname)
X = np.array(alltxts)
Y = np.array(alllabs)
X, X_test, Y, Y_test = train_test_split(X, Y, test_size=0.2)
transformer = TfidfTransformer(use_idf=True, 
                               norm="l2")
vectorizer = CountVectorizer(max_df=0.75, 
                             min_df=3, 
                             max_features=None, 
                             ngram_range=(1,2)
                            )

X_train_vector, X_test_vector, Y_train, Y_test = get_all_data_vectorized(X, X_test, Y, Y_test, vectorizer, transformer)<jupyter_output><empty_output><jupyter_text>--> Verification perte trop grande de mots dans le vocabulaire<jupyter_code>print("Avec parametres : ", X_train_vector.shape[1])

vectorizer = CountVectorizer()
test, _, _, _ = get_all_data_vectorized(X, X_test, Y, Y_test, vectorizer)
print("Sans parametres :", test.shape[1])<jupyter_output>Avec parametres :  61164
Sans parametres : 26263<jupyter_text>--> Selection de model en partant d'un dataset optimal<jupyter_code># En fait le premier grid search le fait deja (je crois)
# X_select, Y_select = X_train_vector[:test_size], Y_train[:test_size]
# svc = svm.SVC()
# parameters = {'kernel':(['linear']), 
#               'C':np.arange(0, 1, 0.1), 
#               'class_weight':(['balanced'])}
# t0 = time()
# clf = GridSearchCV(svc, parameters, n_jobs=8, cv=5, verbose=3)
# best_clf = clf.fit(X_select, Y_select)
# print("done in %0.3fs" % (time() - t0))
# best_parameters = clf.best_estimator_.get_params()
# for param_name in sorted(parameters.keys()):
#     print("--->%s: %r" % (param_name, best_parameters[param_name]))<jupyter_output><empty_output><jupyter_text>- 50 fits, njobs=8:    - size=10_000 --> 88s  --> Entrainement final après avoir selectionner les data et le modele<jupyter_code># test_size = 10_000

# X_select, Y_select = X_train_vector[:test_size], Y_train[:test_size]
# optimal_model = svm.SVC(C=0.3, 
#               class_weight="balanced", 
#               kernel="linear")
# optimal_model.fit(X_select, Y_select)
# test_size = 10_000

# X_select, Y_select = X_test_vector[:test_size], Y_test[:test_size]
# acc = np.round(optimal_model.score(X_select, Y_select)*100, 2)
# print("Acc : ", acc, " %")
# display_infos(optimal_model, X_select, Y_select)
# Mauvais résultat car il faut faire tt l'entrainement après avoir balance le dataset.
# Si on ne fait pas ca dans ce sens, les gridsearch qui<jupyter_output><empty_output><jupyter_text>Equilibrage des donnéesUn problème reconnu comme dur dans la communauté est celui de l'équilibrage des classes (*balance* en anglais). Que faire si les données sont à 80, 90 ou 99% dans une des classes?Le problème est dur mais fréquent; les solutions sont multiples mais on peut isoler 3 grandes familles de solution.1. Ré-équilibrer le jeu de données: supprimer des données dans la classe majoritaire et/ou sur-échantilloner la classe minoritaire.   $\Rightarrow$ A vous de jouer pour cette technique1. Changer la formulation de la fonction de coût pour pénaliser plus les erreurs dans la classe minoritaire:soit une fonction $\Delta$ mesurant les écarts entre $f(x_i)$ et $y_i$ $$C = \sum_i  \alpha_i \Delta(f(x_i),y_i), \qquad \alpha_i = \left\{\begin{array}{ll}1 & \mbox{si } y_i \in \mbox{classe majoritaire}\\B>1 & \mbox{si } y_i \in \mbox{classe minoritaire}\\\end{array} \right.$$   $\Rightarrow$ Les SVM et d'autres approches sklearn possèdent des arguments pour régler $B$ ou $1/B$... Ces arguments sont utiles mais pas toujours suffisant.1. Courbe ROC et modification du biais. Une fois la fonction $\hat y = f(x)$ apprise, il est possible de la *bidouiller* a posteriori: si toutes les prédictions $\hat y$ sont dans une classe, on va introduire $b$ dans $\hat y = f(x) + b$ et le faire varier jusqu'à ce qu'un des points change de classe. On peut ensuite aller de plus en plus loin.Le calcul de l'ensemble des scores associés à cette approche mène directement à la courbe ROC.**Note:** certains classifieurs sont intrinsèquement plus résistante au problème d'équilibrage, c'est par exemple le cas des techniques de gradient boosting que vous verrez l'an prochain.<jupyter_code>import random

def naive_balancing(X, Y):
    label, count = np.unique(Y, return_counts=True)

    idx_pos = np.where(Y == 1, True, False)
    Y_pos = Y[idx_pos]
    Y_pos = Y_pos[:count.min()]

    idx_neg = np.where(Y == -1, True, False)
    Y_neg = Y[idx_neg]

    new_Y = np.concatenate((Y_pos, Y_neg))
    new_X = np.concatenate((X[:count.min()], X[idx_neg]))
    
    tmp = list(zip(new_X, new_Y))
    random.shuffle(tmp)
    new_X, new_Y = zip(*tmp)
    
    label, count = np.unique(new_Y, return_counts=True)
    print(label, count)

    return new_X, new_Y<jupyter_output><empty_output><jupyter_text>--> Selection de data et model avec gridsearch apres balancing<jupyter_code>fname = "data/corpus.tache1.learn.utf8"
alltxts, alllabs = load_pres(fname)
X = np.array(alltxts)
Y = np.array(alllabs)
new_X, new_Y = naive_balancing(X,Y)
X_train, X_test, Y_train, Y_test = train_test_split(X, Y, test_size=0.2) # Test avec new_X, new_Y
# import snowballstemmer

# print(len(X_train))
# stemmer = snowballstemmer.stemmer('french');
# for i in range(len(X_train)):
#     stemmer.stemWords(X_train[i].split()[j] for j in range(len(X_train[i].split())))
# print(np.array(X_train).shape)
# for i in range(len(X_test)):
#     stemmer.stemWords(X_test[i].split()[j] for j in range(len(X_test[i].split())))
train_size = len(X_train)
test_size = len(X_test)
search = False
print(train_size)
print(test_size)
from sklearn.pipeline import Pipeline
from sklearn.feature_extraction.text import TfidfTransformer
from time import time
from nltk.stem.snowball import SnowballStemmer

sw = stopwords.words('french')

if search :

    # Search with booleans
    pipeline1 = Pipeline(
        [
            ("vect", TfidfVectorizer()),
            ("clf", svm.SVC()),
        ]
    )
    parameters1 = {
        "vect__lowercase": (True, False),
        "vect__stop_words": (None, sw),
        "vect__strip_accents": (None, "ascii"),
        "vect__use_idf": (True, False),
        "vect__smooth_idf": (True, False),
        "vect__use_idf": (True, False),
        "vect__sublinear_tf": (True, False),
        
        "vect__min_df": (1,),
        "vect__max_df": (0.1,),
        "vect__ngram_range": [(1, 2),],
        'vect__max_features': (None,),

        "clf__C": (10,100),
        'clf__class_weight':(["balanced"]),
        'clf__kernel':(['linear']),               
        "clf__max_iter": (1000,)
    }
    
    # Search with numerical values
    pipeline2 = Pipeline(
        [
            ("vect", TfidfVectorizer()),
            ("clf", svm.SVC()),
        ]
    )
    parameters2 = {
        # "vect__lowercase": (False,),
        # "vect__stop_words": (None,),
        # "vect__strip_accents": ('ascii',),
        # "vect__use_idf": (True,),
        # "vect__smooth_idf": (False,),
        # "vect__sublinear_tf": (False,),
        
        "vect__min_df": (1,),
        "vect__max_df": (0.001,),
        "vect__ngram_range": [(1, 2),],
        'vect__max_features': (None,),
        
        "clf__C": (10000,),
        'clf__class_weight':(["balanced"]),
        'clf__kernel':(['linear']),               
        "clf__max_iter": (1000,)
    }
    
    # Search with everything
    pipeline3 = Pipeline(
        [
            ("vect", TfidfVectorizer()),
            ("clf", svm.SVC()),
        ]
    )
    parameters3 = {
        "vect__lowercase": (False,True),
        "vect__stop_words": (None,sw),
        "vect__strip_accents": (None, 'ascii'),
        # "vect__use_idf": (True,False),
        # "vect__smooth_idf": (False,True),
        # "vect__sublinear_tf": (False,True),
        
        "vect__min_df": (1, 3, 10, 25),
        "vect__max_df": (0.05,0.1,0.5,0.9),
        "vect__ngram_range": [(1, 1),(1, 2)],
        'vect__max_features': (None,),
        
        "clf__C": (200, 500),
        'clf__class_weight':(["balanced"]),
        'clf__kernel':(['linear']),               
        "clf__max_iter": (1000,)
    }

    grid_search_parameters = GridSearchCV(pipeline3, 
                                          parameters3, 
                                          scoring="f1", # test "rog_auc"
                                          n_jobs=8, 
                                          verbose=3, 
                                          cv=3,
                                          refit=True
                                          )
    
    t0 = time()
    grid_search_parameters.fit(X_train, Y_train) # Essayer de fit en utilisant les pipelines dans l'autre sens et comparer f1 score
    print("done in %0.3fs" % (time() - t0))<jupyter_output>Fitting 3 folds for each of 512 candidates, totalling 1536 fits
done in 5646.937s<jupyter_text>- size=10_000    - 900 fits, njob=8: 2568s (42 minutes)    - 135 fits, njob=8: 220s    - 720 fits, njob=8, scoring="f1", data=naive_balanced : 234s    - 720 fits, njob=8, scoring="f1", data=not_balanced : 287s- size=FULL    - 900 fits, njob=8, scoring="f1", data=not_balanced : 12_000s    - 540 fits, njob=8, scoring="f1", data=not_balanced : 2_631s    - 720 fits, njob=10, scoring="f1", data=not_balanced : 1_719s    - 1536 fits, njob=8, scoring="f1", data=not_balanced : ????s<jupyter_code>def display_model_scores(model, X_test, Y_test):
    best_parameters = model.get_params()
    for param_name in sorted(best_parameters.keys()):
        print("--->%s: %r" % (param_name, best_parameters[param_name]))
    grid_predictions = model.predict(X_test)
    print(confusion_matrix(Y_test, grid_predictions))
    print(classification_report(Y_test, grid_predictions))
from sklearn.metrics import confusion_matrix, classification_report, accuracy_score, f1_score, roc_auc_score

if search :
    # display_model_scores(grid_search_parameters, X_train, Y_train)
    display_model_scores(grid_search_parameters, X_test, Y_test)<jupyter_output><empty_output><jupyter_text>--> Construction dataset optimal<jupyter_code>def stemm(X):
    stemmer = SnowballStemmer("french")
    stem = stemmer.stem(X)
    return X
vectorizer = TfidfVectorizer(lowercase=False,
                             stop_words=None,
                             strip_accents=None,
                             use_idf=True,
                             smooth_idf=True,
                             sublinear_tf=False,
                             
                             min_df=1,
                             max_df=0.05,
                             ngram_range=(1,2),
                            )

# Decommenter pour stemming
# print(len(X_train))
# stemmer = snowballstemmer.stemmer('french');
# for i in range(len(X_train)):
#     stemmer.stemWords(X_train[i].split()[j] for j in range(len(X_train[i].split())))
# print(np.array(X_train).shape)

X_train_vector, X_test_vector, Y_train, Y_test = get_all_data_vectorized(X_train, X_test, Y_train, Y_test, vectorizer, transformer)
print(X_train_vector.shape)<jupyter_output>(45930, 307727)<jupyter_text>--> Test model selection avec donnees optimales<jupyter_code>if search:
    parameters = {
        'C': ([200]),
        'gamma': (["scale"]),
        'class_weight':(["balanced"]),
        'kernel':(['linear'])  
        # "max_iter": (1000,)
    }

    optimal_model = GridSearchCV(svm.SVC(),
                                 parameters, 
                                 scoring="f1", # test "rog_auc"
                                 n_jobs=8, 
                                 verbose=3, 
                                 cv=3,
                                 refit=True
                                 )

    t0 = time()
    optimal_model.fit(X_train_vector[:train_size], Y_train[:train_size])
    print("done in %0.3fs" % (time() - t0))
if search:
    # display_model_scores(optimal_model, X_train_vector, Y_train)
    display_model_scores(optimal_model, X_test_vector, Y_test)<jupyter_output>--->C: 200
--->break_ties: False
--->cache_size: 200
--->class_weight: 'balanced'
--->coef0: 0.0
--->decision_function_shape: 'ovr'
--->degree: 3
--->gamma: 'scale'
--->kernel: 'linear'
--->max_iter: -1
--->probability: False
--->random_state: None
--->shrinking: True
--->tol: 0.001
--->verbose: False
0.9465052388890548
[[1009  503]
 [ 650 9321]]
              precision    recall  f1-score   support

          -1       0.61      0.67      0.64      1512
           1       0.95      0.93      0.94      9971

    accuracy                           0.90     11483
   macro avg       0.78      0.80      0.79     11483
weighted avg       0.90      0.90      0.90     11483<jupyter_text>--> Entrainement avec model et data optimal sur le TRAIN entier sans max_iter<jupyter_code># fname = "data/corpus.tache1.learn.utf8"
# alltxts_train_final, alllabs_train_final = load_pres(fname)
# X_train_final = np.array(alltxts_train_final)
# Y_train_final = np.array(alllabs_train_final)

fname = "data/corpus.tache1.learn.utf8"
alltxts, alllabs = load_pres(fname)
X = np.array(alltxts)
Y = np.array(alllabs)
X_train_final, X_test, Y_train_final, Y_test = train_test_split(X, Y, test_size=0.2)
final_vectorizer = TfidfVectorizer(lowercase=False,
                             stop_words=None,
                             strip_accents=None,
                             use_idf=True,
                             smooth_idf=True,
                             sublinear_tf=False,
                             
                             min_df=1,
                             max_df=0.05,
                             ngram_range=(1,2),
                            )

X_final_vector = final_vectorizer.fit_transform(X_train_final)
X_test_vector = final_vectorizer.transform(X_test)
final_model = svm.SVC(C=200, 
                        gamma="scale",
                        class_weight="balanced", 
                        kernel="linear",
                        verbose=3,
                       )
print(X_final_vector.shape)
print(Y_train_final.shape)
from time import time
t0 = time()
final_model.fit(X_final_vector,  Y_train_final)
print("done in %0.3fs" % (time() - t0))
display_model_scores(final_model, X_final_vector, Y_train_final)
display_model_scores(final_model, X_test_vector, Y_test)<jupyter_output>--->C: 200
--->break_ties: False
--->cache_size: 200
--->class_weight: 'balanced'
--->coef0: 0.0
--->decision_function_shape: 'ovr'
--->degree: 3
--->gamma: 'scale'
--->kernel: 'linear'
--->max_iter: -1
--->probability: False
--->random_state: None
--->shrinking: True
--->tol: 0.001
--->verbose: 3
[[ 6005     0]
 [    6 39919]]
              precision    recall  f1-score   support

          -1       1.00      1.00      1.00      6005
           1       1.00      1.00      1.00     39925

    accuracy                           1.00     45930
   macro avg       1.00      1.00      1.00     45930
weighted avg       1.00      1.00      1.00     45930

--->C: 200
--->break_ties: False
--->cache_size: 200
--->class_weight: 'balanced'
--->coef0: 0.0
--->decision_function_shape: 'ovr'
--->degree: 3
--->gamma: 'scale'
--->kernel: 'linear'
--->max_iter: -1
--->probability: False
--->random_state: None
--->shrinking: True
--->tol: 0.001
--->verbose: 3
[[ 836  682]
 [ 345 9620]]
              pre[...]<jupyter_text>--> Calcul des predictions pour envoie<jupyter_code>fname = "data/corpus.tache1.test.utf8"
alltxts_test_final, alllabs_test_final = load_pres(fname)
X_test_final = np.array(alltxts_test)
Y_test_final = np.array(alllabs_test)
X_test_vector = final_vectorizer.transform(X_test_final)
final_pred = final_model.predict(X_test_vector)
print(final_pred.shape)
f = open("output/preds_line.txt", "a")
preds = ""
for i in range(len(final_pred)):
    preds += str(final_pred[i]) + "\n"
# print(preds)
f.write(preds)
f.close()<jupyter_output><empty_output>
{'fraction_non_alphanumeric': 0.12593055208593554, 'fraction_numerical': 0.023107669248063952, 'mean_word_length': 3.314150231706003, 'pattern_counts': {'":': 41, '<': 97, '<?xml version=': 0, '>': 175, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 26, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'chain_length': '25', 'dir': 'jupyter-structured-clean-dedup', 'id': '493947', 'license': 'MIT', 'n_tokens_mistral': 14722, 'n_tokens_neox': 14074, 'n_words': 3353, 'path': 'S2/RITAL/TAL/TME/TME1/.ipynb_checkpoints/BenchmarkClassif-checkpoint.ipynb', 'repo_name': 'hanzopgp/MasterArtificialIntelligencePTs', 'size': '40030'}