Régression polynomiale avec Scikit-learn

Un des outils les plus répandus en python pour effectuer des régressions est le module numpy.polynomial.polynomial.

Mais si nous voulons jouer sur les coefficients avec les méthodes ridge ou lasso par exemple, pourquoi ne pas travailler directement avec Scikit-learn ?

Je vais vous montrer dans ce petit tutoriel comment procéder à une régression polynomiale à l’aide de scikit-learn simplement. Et dans un deuxième temps, vous pourrez jouer avec les paramètres L1 et L2 des régressions Lasso et Ridge à l’aide de graphes interactifs, réalisés avec le module “Bokeh

Régression polynomiale

Prenons des données simples, par exemple une fonction log bruitée :

x = np.arange(1,50,.5)
y = np.random.normal(0,0.22,len(x))+(np.log(x))

La méthode “classique” pour précéder à une régression polynomiale consiste à créer un tableau dont chaque colonne va correspondre à un degré polynomial. Puis procéder à une régression pour chaque colonne. Par exemple, comme ci-dessous :

for i in range(2,16):
    colname = "x_%d"%i #New columname
    data[colname] = data["x"]**i

Une méthode plus rapide consiste à utiliser le module sklearn.preprocessing.PolynomialFeatures qui va générer automatiquement les données nécessaires. Ensuite, toujours avec scikit-learn, il suffit de créer et fit le modèle de régression:

def polyfit(d):
    polyn = PolynomialFeatures(degree=d)
    x_ = polyn.fit_transform(x) #x_ contient les degrés et produits croisés
    #Une fois qu'on a préparé les degrés on peut faire la régression
    clf = linear_model.LinearRegression()
    clf.fit(x_,y)
    y1 = clf.predict(x_)
    return y1

Pour générer des régressions pour différents degrés polynomiaux et les rajouter à un DataFrame :

for i in np.arange(1,d,1):
    colname = "Pow_%s"%i
    pp = polyfit(i)
    data_fit[colname] = pp

Il ne reste plus qu’à tracer ceci. Cliquez sur l’image ci-dessous pour visualiser la régression pour différentes valeurs de d (le degré de polynôme).

Au delà d’un certain degré on voit rapidement un overfit de la fonction, qui se traduit par des coefficients dont les valeurs augmentent très fortement. Nous pouvons appeler “Ridge” ou “Lasso” à la rescousse. En quelques mots, rappelons nous que Ridge est plutôt utilisé pour réduire l’overfit (n’annule pas les coefficients) et LASSO est plutôt utilisé pour sélectionner les paramètres.

Réduction des coefficients par la méthode “Ridge”

Commençons par “Ridge” qui permet de réduire les coefficients sans les annuler, grâce à une régularisation de type L2. Pour un degré Je vais prendre quelques valeurs de alpha, calculer les coefficients

alphas = [1-e12, 1e-10,1e-9,1e-8,1e-7,1e-6]
i=0
params = pd.DataFrame()

for alph in alphas:
    i+=1
    clf_Ridge = Ridge(alpha=alph, normalize=True,max_iter=1e5)
    A = clf_Ridge.fit(x_,y)
    colname = "x_%d"%i #New columname
    YY = clf_Ridge.predict(x_)
    data[colname] = YY

Sélection par la méthode “Lasso”

Cette méthode est couramment utilisée pour effectuer une sélection de variables: plus le coefficient est élevé et moins il restera de variables dans le modèle.

Cliquez sur l’image pour jouer avec la valeur du coefficient du lasso (de 1e-15 à 5e-2)

 

Contrairement au cas précédent, lorsque la valeur de L2 est trop importante, le modèle est tellement simplifié qu’il ne reste plus qu’un seul paramètre, et la régression devient simplement linéaire.

C’est tout pour ce tutoriel. Pour des explications plus détaillées sur les méthodes Ridge et Lasso, je vous suggère cet article bien détaillé sur Analytics Vidhya ou bien entendu la documentation de scikit learn.

N’hésitez pas à commenter cet article !

Bonus : le code utilisé pour créer un graphe interaction avec Bokeh

D3.js, n’étant pas très copain avec wordpress j’ai utilisé Bokeh pour ces graphes. C’est une librairie très efficace qui permet d’obtenir de bons résultats sans nécessiter des connaissances en javascript trop poussées. Par rapport à D3.js, on perd l’interactivité avec les données (les graphes ne sont pas mis à jour en temps réel) et surtout le côté “responsive design”.

Ci-dessous le code avec quelques commentaires

''' *** PLOT DE LA REGRESSION LASSO *** '''
output_file('graph_lasso.html')
tools = 'pan'

#Création du dictionnaire contenant les noms des colonnes et indice pour association avec slider
data_plot = {}

for i in range(1,len(alphas_lasso),1):
    colname = str(alphas_lasso[i])
    data_plot.update({str(i): data_lasso[colname]})

initial_power = '1' #La première colonne à tracer dans data_lasso

# Wrap the data in two ColumnDataSources
source_visible = ColumnDataSource(data=dict(
x=x, y=data_plot[initial_power]))
source_available = ColumnDataSource(data=data_plot)
source_scatter = ColumnDataSource(data=dict(x=x, y=y_init))
    
# Define plot elements

plot = Figure(plot_width=400, plot_height=400)
plot.line('x', 'y', source=source_visible, line_width=3, line_alpha=0.6)
plot.circle('x','y', source=source_scatter, fill_color="red") 

L2_slider = Slider(start=1, end=12, value=1, step=1, title="L2 slider")
    
L2_slider.callback = CustomJS(args=dict(source_visible=source_visible,
              source_available=source_available), code="""
        var selected_function = cb_obj.get('value');
        // Get the data from the data sources
        var data_visible = source_visible.get('data');
        var data_available = source_available.get('data');
        // Change y-axis data according to the selected value
        data_visible.y = data_available[selected_function];
        // Update the plot
        source_visible.trigger('change');
    """)


widgets = WidgetBox(L2_slider)
   
l = row(plot, L2_slider) #l = layout : sur une row on met le plot et le slider à droite
#l = row(slider())
script,div = components(plot)
#html = file_html(l, CDN, "my plot") #Pour exporter un fichier html en standalone
show(l)

 

2 thoughts on “Régression polynomiale avec Scikit-learn”

  1. Bonjour,

    Merci pour ce super article.

    J’essaie de paramétrer une régression polynomiale avec un jeu de donnée issu d’un CSV. Je galère un peu avec Bokeh pour l’affichage de la régression avec les dataframe. Le fit et predict se déroulent bien avec le dataframe. Mais malheureusement ça bloque au niveau de Bokeh.

    Avez vous déjà rencontré l’erreur suivante avec Bokeh : RuntimeError: Columns need to be 1D (x is not)

    Je me permets de paster mon code :

    from sklearn.preprocessing import PolynomialFeatures
    from sklearn import linear_model

    import numpy as np
    import pandas as pd

    from bokeh.io import show, output_notebook
    from bokeh.plotting import figure, curdoc
    from bokeh.client import push_session

    df = pd.read_csv(‘data/BOE-XUDLERD.csv’, header=0)
    df[‘Date’] = pd.to_datetime(df[‘Date’], format=”%Y-%m-%d”)
    df[‘Date’] = df[‘Date’].astype(‘datetime64[ns]’).astype(int)

    # hack
    X = df[‘Date’]
    y = df[‘Value’]

    X = np.array(df[‘Date’]).reshape((len(df[‘Date’]), 1))

    poly = PolynomialFeatures(degree=2)
    y = poly.fit_transform(X)

    # Instantiate
    lg = linear_model.LinearRegression()

    # Fit
    lg.fit(X, y)

    y = lg.predict(X)

    p = figure(title=”Test”, plot_height=400)

    # add the lines
    #p.scatter(df[‘Date’].index, y)
    #p.line(df[‘Date’].index, y_hat, color=”#A6CEE3″, legend=’Regression’)

    # ERREUR À LA LIGNE SUIVANTE :
    p.line(X, y, color=”#A6CEE3″, legend=’Regression’)
    p.line(df[‘Date’], df[‘Value’], color=”#B2DF8A”, legend=’True’)

    document = curdoc()
    document.add_root(p)

    # to use along with “bokeh serve”
    if __name__ == “__main__”:
    print(“\npress ctrl-C to exit”)
    session = push_session(document)
    session.show()
    session.loop_until_closed()

    1. Bonjour,
      Désolé pour la réponse tardive, j’ai été assez pris ces derniers temps. Problème résolu ?
      A première vue je ne vois pas trop ce qui cloche dans ce code…

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.