Commit b5eaad2a authored by Guillaume Garrigos's avatar Guillaume Garrigos
Browse files

retire artefact vieille version

parent 9b7a1955
%% Cell type:markdown id: tags:
<p hidden>Here are some Latex definitions</p>
$\newcommand{\R}{\mathbb{R}}$
$\newcommand{\N}{\mathcal{N}}$
$\newcommand{\RR}{\mathbb{R}}$
$\newcommand{\E}{\mathbb{E}}$
$\newcommand{\Mm}{\mathcal{M}}$
%% Cell type:markdown id: tags:
# TP 2 : Modélisation d'un problème de régression linéaire
Avant de commencer le TP, exécutez la commande ci-dessous afin de lancer une session de mybinder.org sans interruption.
%% Cell type:code id: tags:
``` python
# N'exécuter qu'une seule fois
import mybinder
mybinder.start_session()
```
%% Cell type:markdown id: tags:
# Un problème pratique : détection de la qualité d'un vin
On s'intéresse au problème suivant : vous voulez être de capable de prédire si un vin va plaire aux consommateurs.
| Choisir un bon vin : facile ?|
| --- |
|![](images/vin_choix.png)|
Pour cela, vous allez vous baser sur des propriétés **chimiques** du vin, qui sont des mesures objectives de son contenu.
Par exemple l'acidité du vin, sa teneur en sulfites, son taux d'alcoolémie, etc.
Vous avez donc constitué une *base de donnée* (un gros tableur donc), dans laquelle :
- d'un coté vous avez fait vos mesures sur une liste de vins, ce qui vous donne une dizaines de valeurs que vous enregistrez quelque part;
- de l'autre vous avez demandé à des consommateurs de donner une note au vin (disons entre 0 et 10).
A partir de tout cela vous voulez construire une fonction de *prédiction* qui, étant donné les propriétés chimiques d'un nouveau vin qui arrive sur le marché (et pour lequel vous n'avez pas encore de note), renvoie une note prédite pour le vin concerné.
Par exemple, on peut imaginer que si la plupart des gens n'aiment pas les vins acides, un nouveau vin ayant une acidité élevée aura une note prédite faible.
Toute ressemblance avec une [application existante](https://en.wikipedia.org/wiki/Vivino) serait totalement fortuite.
# Introduction : Le problème de régression
Le problème décrit ci-dessus est un cas particulier de nombreux problèmes que l'on rencontre en économie, physique, biologie ou en intelligence artificielle : on possède un jeu de donnèes expérimentales à partir duquel on cherche à **extrapoler** une loi de comportement que l'on va appliquer à de futures données réelles.
Typiquement, ce jeu de données se présente sous la forme de couples $(a_1,b_1),\cdots,(a_m,b_m) \in \R^p \times \R$
et l'on cherche une Relation de la forme $b=R(a)$ reliant ces couples. Grosso modo, si on veut savoir ce "qu'est" $a$, ou "combien vaut" $a$, on le donne à la fonction de prédiction $R$ qui nous fournit une réponse.
On veut donc trouver $R:\R^p \rightarrow \R$ telle que
\begin{equation}\label{Probleme}
(\forall i\in\{1,\cdots,m\}) \quad R(a_i) \simeq b_i.\tag{P}
\end{equation}
Voici quelques exemples de tels problèmes:
- chaque $a_i$ est une image, et chaque $b_i$ est un nombre entier qui correspond à une catégorie d'image. Par exemple 1=chat, 2=chien, 3=avion.
Dans ce cas, un fois que l'on a trouvé $R$, on possède une fonction qui est capable de décrire le contenu d'une image.
- chaque $a_i$ est un vecteur contenant des coordonnées dans l'espace et le temps, et chaque $b_i$ est un nombre réel correspondant à la température mesurée en l'endroit/moment indiqué par $a_i$.
Dans ce cas, un fois que l'on a trouvé $R$, on possède une fonction qui est capable de prédire la température qu'il fait/fera.
- chaque $a_i$ est un vecteur dont la dizaine de coordonnées correspond aux valeurs de mesures chimiques d'un vin $i$, et $b_i$ est la note moyenne attribuée par des testeurs/utilisateurs.
Dans ce TP nous allons voir quelques stratégies pour arriver à résoudre ce problème \eqref{Probleme}, puis l'appliquer au problème du vin.
%% Cell type:markdown id: tags:
# I. Un problème de moindres carrés
Etant donné un jeu de données $(a_1,b_1),\cdots,(a_m,b_m) \in \R^p \times \R$, on cherche donc une fonction $R : \mathbb{R}^p \rightarrow \R$ telle que $R(a_i) \simeq b_i$ pour tout $i$.
Ce problème est mal posé, car l'ensemble de toutes les fonctions de $\R^p \to \R$ est **trop grand**, ce qui veut dire qu'il y a potentiellement une infinité de solutions.
| Le problème du choix de $R$ |
| --- |
| ![](images/many_choices.png) |
| Laquelle des fonctions bleue ou orange représente le mieux le jeu de données (en rouge)? Dans l'absolu il n'y a objectivement pas de bonne réponse à cette question... En moins que vous n'en sachiez plus à propos du problème. |
On va donc restreindre notre recherche à un sous-ensemble de fonctions plus simples: on fait ici **l'hypothèse de modélisation** que la fonction $R$ que l'on recherche est *affine* :
- d'une part ce sera facile à implémenter
- d'autre part l'espace des fonctions affines est relativement "petit", donc il y a des chances que l'on aie une solution unique à notre problème.
| Oui mais si ça se trouve c'est une hypothèse un peu abusive? |
| --- |
| On s'en inquiétera plus tard, lorsqu'on testera notre solution sur nos données, quitte à changer d'hypothèse par la suite. |
%% Cell type:code id: tags:
``` python
import numpy as np
from numpy import linalg as la
import matplotlib.pyplot as plt
```
%% Cell type:markdown id: tags:
Quelques commandes qui vous seront utiles
| | |
|-|-|
|`np.array([1, 2, 3])`| Définit un vecteur 1D |
| `np.array([[1, 2, 3], [4, 5, 6]])` | Définit une matrice 2D dans $\mathcal{M}_{2,3}(\mathbb{R})$ |
| `x[2]` | Renvoie le 3e coefficient du vecteur 1D `x` |
| `A.T` | Transpose la matrice `A` |
| `A@x` ou `A@B` | Calcule le produit matriciel entre `A` et `x` (ou `A` et `B`) |
| `x + 2` | Ajoute 2 à tous les coefficients du vecteur `x` |
| `np.trace(A)` | Calcule la trace de `A` |
| `la.det(A)` | Calcule le déterminant de `A` |
| `la.eig(A)[0]` | Renvoie les valeurs propres de la matrice symétrique `A` |
%% Cell type:markdown id: tags:
**I.1)** On définit ci dessous un (petit) jeu de données $a=(a_1,\cdots, a_m)$, $b=(b_1,\cdots,b_m)$, avec $m=4$. On pourra visualiser ces points dans le plan avec la fonction `plt.scatter`
%% Cell type:code id: tags:
``` python
a = np.array([0.2, 0.7, 2, 3.5])
b = np.array([-14.5, -9, 0, 5])
```
%% Cell type:code id: tags:
``` python
plt.scatter(a,b)
```
%% Cell type:markdown id: tags:
**I.2)** Essayez de trouver une "bonne" fonction affine approximant ces points. Autrement dit, essayez de trouver de bons coefficients $\alpha \in \mathbb{R}$, $\beta \in \mathbb{R}$, tels que la fonction affine $R(x) = \alpha x + \beta$ vérifie $R(a_i) \simeq b_i$.
Vous pourrez essayer de prendre les coefficients de votre choix, visualiser si la droite est "bonne" ou pas, et éventuellement ajuster vos coefficients. On n'attend pas un résultat parfait.
%% Cell type:code id: tags:
``` python
# choisissez les paramètres de la droite affine
alpha = 0
beta = 0
```
%% Cell type:code id: tags:
``` python
abscisses = np.arange(0, 4, 0.1) # intervalle [0,4[ par pas de 0.1
ordonnees = alpha*abscisses + beta
plt.scatter(a, b)
plt.plot(abscisses, ordonnees, color='orange')
```
%% Cell type:markdown id: tags:
**I.3)** Choisir $(\alpha,\beta)$ à la main est assez laborieux! En plus, il n'est pas clair comment déterminer quelle est la meilleure droite! On va maintenant essayer de **modéliser** le problème et le transformer en un **problème d'optimisation** à résoudre:
1. Tout d'abord, on veut $R(a_i) \simeq b_i$. Donc cela veut dire que l'on veut que la distance $\vert R(a_i) - b_i \vert$ soit la plus petite possible.
2. Ensuite, on veut que ceci soit vrai pour *tous* les couples $(a_i,b_i)$. Une façon de dire ça est de dire qu'on veut que la *somme* $\sum\limits_{i=1}^m \vert R(a_i) - b_i \vert$ soit la plus petite possible.
3. De plus, cette quantité ne dépend que du choix de la fonction $R$, que l'on suppose affine, càd de la forme $R(a) = \alpha a + \beta$. Donc on cherche un couple $(\alpha,\beta) \in \mathbb{R}\times \mathbb{R}$ qui minimise :
$$ \text{minimiser}_{(\alpha,\beta) \in \mathbb{R}\times \mathbb{R}} \ \sum\limits_{i=1}^m \vert \alpha a_i + \beta - b_i \vert$$
4. On voit qu'on a un problème d'optimisation! Par contre la fonction n'a pas l'air différentiable en $\alpha$ et $\beta$ à cause des valeurs absolues; on peut contourner ce problème en considérant des distances au carré :
\begin{equation}\label{E:MC}
\text{minimiser}_{(\alpha,\beta) \in \mathbb{R}\times \mathbb{R}} \ \sum\limits_{i=1}^m \vert \alpha a_i + \beta - b_i \vert^2 \tag{MC}
\end{equation}
Le problème d'optimisation qu'on vient d'obtenir est appelé **problème des moindres carrés**.
**I.4)** Vérifier que le problème des moindres carrés $\eqref{E:MC}$ peut s'écrire de manière équivalente en
\begin{equation*}
\text{minimiser}_{x \in \mathbb{R}^2} \ f(x) = \Vert \Phi x - y \Vert^2,
\end{equation*}
$\
\text{ avec }
\Phi = \begin{pmatrix}
a_1 & 1 \\
\vdots & \vdots \\
a_m & 1
\end{pmatrix}, \
x = \begin{pmatrix}
\alpha \\
\beta
\end{pmatrix}, \
y = \begin{pmatrix}
b_1 \\
\vdots \\
b_m
\end{pmatrix}.$
%% Cell type:markdown id: tags:
**I.5)** Définissez $\Phi$ (`Phi`) et $y$ en fonction des données de la première question. Pensez à bien respecter l'orientation de ces matrices!
%% Cell type:code id: tags:
``` python
```
%% Cell type:markdown id: tags:
**I.6)** Vérifiez que la matrice $\Phi^\top \Phi$ est définie positive. Il y a plein de façons de faire ça!
%% Cell type:code id: tags:
``` python
```
%% Cell type:code id: tags:
``` python
```
%% Cell type:markdown id: tags:
**I.7)** Déduisez-en, en utilisant le cours, que le minimiseur $x=(\alpha, \beta)^\top$ de $f(x) = \Vert \Phi x - y \Vert^2$ que l'on cherche est solution du système linéaire $\Phi^\top \Phi x = \Phi^\top y$.
Trouvez ce vecteur en utilisant la fonction `la.solve(A,b)` qui résoud les équations $Ax=b$.
%% Cell type:code id: tags:
``` python
```
%% Cell type:markdown id: tags:
**I.8)** Maintenant que vous avez obtenu un $x = (\alpha,\beta)^\top$ optimal, visualisez ce que cela donne, à l'instar de la question I.2).
%% Cell type:code id: tags:
``` python
```
%% Cell type:code id: tags:
``` python
```
%% Cell type:markdown id: tags:
## II. Moindres carrés
%% Cell type:markdown id: tags:
On a vu que trouver un polynome qui passe par les points des données ne marche pas, notamment parce que les poynomes de grand degré oscillent trop. Que peut-on alors faire?
1. On va relaxer la condition $R(a_i)=b_i$, et on va seulement demander à ce que $R(a_i) \simeq b_i$, pour être plus flexibles. Pour cela, on va essayer de faire en sorte que $\sum_i (R(a_i) - b_i)^2$ soit minimal. On parle alors de résoudre un problème de *moindres carrés*.
2. Vu qu'on ne cherche plus a passer par les points, on va s'autoriser à travailler avec des polynomes de plus petit degré, qui oscillent moins. En fait, on va carrément demander à ce que $R$ soit un polynome de degré $1$. C'est pour cela qu'on parle parfois de *régression linéaire*.
On va donc chercher à résoudre le problème d'optimisation:
$$ \text{minimiser } f(x) = \frac{1}{2} \sum\limits_{i=1}^m (\langle \phi(a_i), x \rangle - b_i)^2. $$
%% Cell type:markdown id: tags:
## II. Détection de la qualité d'un vin : acquisition et exploration des données
%% Cell type:code id: tags:
``` python
import pandas as pd
```
%% Cell type:markdown id: tags:
Les données viticoles qui nous intéressent sont toutes contenues dans un tableur (un fichier au format `.csv`). On peut importer ce tableur directement dans Python, avec la commande suivante:
%% Cell type:code id: tags:
``` python
dataset = pd.read_csv('data/winequality.csv')
```
%% Cell type:markdown id: tags:
Ici `dataset` n'est rien d'autre qu'une grosse matrice : chacune de ses lignes correspond à une bouteille de vin, et chacune de ses colonnes correspond à un nombre correspondant à une propriété chimique ou a une note.
Je vous **déconseille** d'essayer d'afficher l'intégralité de la matrice ici, car elle est trop grosse :
%% Cell type:code id: tags:
``` python
dataset.shape # affiche les dimensions de la matrice
```
%% Cell type:markdown id: tags:
Ok, mais tout ceci ne nous dit pas ce qu'il y a dans cette matrice !?
Pour en avoir un aperçu, vous pouvez utiliser `dataset.head()` qui vous donne un aperçu de ses premières lignes:
%% Cell type:code id: tags:
``` python
dataset.head()
```
%% Cell type:markdown id: tags:
Vous voyez donc qu'on a affaire à un tableur, ou chaque ligne correspond à un vin (on n'a pas de nom ici mais seulement un numéro, de 0 à 1599), et chaque colonne à une propriété chimique (il y en a 11) ou sa note (12e colonne).
Afin d'avoir un aprerçu du reste des données, on peut également appeler `dataset.describe()` pour avoir un résumé des données. Plus précisément, cela nous renvoie pour chaque propriété chimique la moyenne (*mean*), l'écart-type (*std*), etc parmi tous les vins.
%% Cell type:code id: tags:
``` python
dataset.describe()
```
%% Cell type:markdown id: tags:
Vous pouvez par exemple voir que les notes vont de 3 à 8, avec une note moyenne de 5.63 et une déviation standard de 0.80.
%% Cell type:markdown id: tags:
**III.2)** Si on résume ce qui se passe, chaque ligne $i$ correspond à un vin, et on peut en extraire un vecteur $a_i \in \mathbb{R}^{11}$ contenant ses 11 propriétés chimiques et un nombre $b_i$ correspondant à sa note.
Donc on peut extraire des données une sous-matrice et un vecteur colonne:
$$
A=\begin{pmatrix}
& a_1^\top & \\
& \vdots & \\
& a_m^\top &
\end{pmatrix}, \ \
b = \begin{pmatrix}
b_1 \\
\vdots \\
b_m
\end{pmatrix}
$$
Essayez de comprendre ce que fait le code ci-dessous:
%% Cell type:code id: tags:
``` python
A_full = dataset[['acidité stable', 'acidité volatile', 'acide citrique', 'sucre résiduel', 'chlorides', 'dioxide de soufre libre', 'dioxide de soufre total', 'densité', 'pH', 'sulfates','alcool']].values
b_full = dataset['note'].values
```
%% Cell type:markdown id: tags:
Afin de simuler correctement ce qui se passerait en réalité, nous allons diviser en deux le jeu de données : nous allons en garder une grosse partie (80%), sur laquelle nous allons faire une régression linéaire ; puis nous allons garder le reste de coté (20%), qui va jouer le rôle d'un nouveau vin arrivant sur le marché, et nous permettre de **tester** nos résultats.
Etant donné que l'on a déjà les notes pour ces vins, on pourra alors vérifier que notre solution marche bien (ou pas).
%% Cell type:code id: tags:
``` python
from sklearn.model_selection import train_test_split
A, A_test, b, b_test = train_test_split(A_full, b_full, test_size=0.2, random_state=0)
```
%% Cell type:markdown id: tags:
# III. Application des moindres carrés aux données viticoles
%% Cell type:markdown id: tags:
Comme au **I.** on cherche ici à trouver une fonction *affine* $R : \mathbb{R}^{p} \longrightarrow \mathbb{R}$ (avec $p=11$, telle que $R(a_i) \simeq b_i$.
Autrement dit, on s'attend à ce que la note attribuée à un vin peut en fait s'exprimer comme une combinaison affine des valeurs de ses propriétés chimiques.
Une telle fonction affine de $\mathbb{R}^p$ dans $\mathbb{R}$ s'écrit forcément sous la forme $$R(x) = \langle \alpha, x \rangle + \beta,$$ où $\alpha = (\alpha_1, \dots, \alpha_p) \in \mathbb{R}^p$ et $\beta \in \mathbb{R}$.
Donc il nous faut trouver les meilleurs $\alpha \in \mathbb{R}^P$ et $\beta \in \mathbb{R}$.
De la même manière qu'au **I.** on va procéder à une approche de moindres carrés : on veut minimiser
\begin{equation}\label{E:MC2}
\text{minimiser}_{(\alpha,\beta) \in \mathbb{R}^p\times \mathbb{R}} \ \sum\limits_{i=1}^m \vert \langle\alpha, a_i \rangle + \beta - b_i \vert^2 \tag{$MC_{vin}$}
\end{equation}
Quelques commandes qui vous seront utiles:
| | |
|-|-|
| `np.ones(3)`, ou `np.zeros(3)` | Définit un vecteur 1D rempli de 1, ou 0|
| `np.ones((2,3))`, ou `np.zeros((2,3))` | Définit une matrice 2D dans $\mathcal{M}_{2,3}(\mathbb{R})$ remplie de 1, ou 0 |
| `A.shape` | Renvoie les dimensions de la matrice `A` |
| `A[2,0]` | Renvoie le coefficient de la 3e ligne, 1ère colonne de la matrice `A` |
| `A[2,:]` (ou `A[:,2]` ) | Renvoie la 3e ligne (ou colonne) de la matrice `A` (comme vecteur 1D) |
| `A[:, :11]` | Renvoie les 11 premières colonnes de la matrice `A` |
| `np.abs(x)` | Applique la valeur absolue à tous les coefficients du vecteur `x` |
| `np.mean(x)` ou `np.std(x)` | Renvoie la moyenne (ou écart-type) des coefficients du vecteur `x` |
**III.1)** Vérifier que le problème des moindres carrés $\eqref{E:MC2}$ peut s'écrire de manière équivalente en
\begin{equation*}
\text{minimiser}_{x \in \mathbb{R}^2} \ f(x) = \Vert \Phi x - y \Vert^2,
\end{equation*}
$\
\text{ avec }
\Phi = \begin{pmatrix}
& & & 1 \\
& A & & \vdots \\
& & & 1
\end{pmatrix} \in \mathcal{M}_{m,p+1}, \
x = \begin{pmatrix}
\alpha \\
\beta
\end{pmatrix} \in \mathbb{R}^{p+1}, \
y = b = \begin{pmatrix}
b_1 \\
\vdots \\
b_m
\end{pmatrix} \in \mathbb{R}^m.$
%% Cell type:markdown id: tags:
**III.2)** Définissez $\Phi$ (`Phi`) et $y$ en fonction de $A$ et $b$. Cela demande de savoir comment définir une matrice avec des sous-matrices. Savez-vous comment faire? Si non, c'est le moment de chercher l'information!
%% Cell type:code id: tags:
``` python
```
%% Cell type:code id: tags:
``` python
```
%% Cell type:code id: tags:
``` python
```
%% Cell type:code id: tags:
``` python
```
%% Cell type:code id: tags:
``` python
```
%% Cell type:code id: tags:
``` python
```
%% Cell type:markdown id: tags:
**III.3)** Vérifiez que la matrice $\Phi^\top \Phi$ est définie positive. (Question bonus : Pensez-vous que vous auriez pu le prouver avec un argument mathématique?)
%% Cell type:code id: tags:
``` python
```
%% Cell type:markdown id: tags:
<details>
<summary>Cliquez <b>ici</b> pour afficher la réponse à la question bonus</summary>
Une telle matrice est toujours positive, donc elle est définie positive ssi Phi est injective.
Cela équivaut à ce que ses colonnes soient une famille indépendante.
Si elles étaient liées, cela voudrait dire qu'une des quantités chimiques peut s'exprimer comme combiniaison linéaire des autres.
Si c'était le cas ce ne serait pas dramatique, il suffirait de s'en débarasser car l'information serait redondante.
</details>
%% Cell type:markdown id: tags:
**III.4)** Déduisez-en, en utilisant le cours, que le minimiseur $x=(\alpha, \beta)^\top \in \mathbb{R}^{p+1}$ de $f(x) = \Vert \Phi x - y \Vert^2$ que l'on cherche est solution du système linéaire $\Phi^\top \Phi x = \Phi^\top y$.
Trouvez ce vecteur en utilisant la fonction `la.solve(A,b)` qui résoud les équations $Ax=b$.
Coupez ce vecteur solution en deux : un vecteur $\alpha \in \mathbb{R}^{11}$, et une constante $\beta \in \mathbb{R}$.
%% Cell type:code id: tags:
``` python
```
%% Cell type:code id: tags:
``` python
```
%% Cell type:markdown id: tags:
**III.5)** Regardez les valeurs de `alpha` que vous avez trouvé. Chaque valeur correspond au "poids", à "l'importance" associée à une certaine quatité chimique mesurée. Par exemple le troisième coefficient correspond à l'acide citrique, et le dernier au taux d'alcoolémie. Quels ont l'air d'être les critères les plus importants?
%% Cell type:code id: tags:
``` python
```
%% Cell type:markdown id: tags:
**III.6)** On dispose maintenant d'une fonction $R$ définie par $R(x) = \langle \alpha , x \rangle + \beta$.
Avec la solution $(\alpha, \beta)$ que vous venez de trouver, prenez un vin au hasard parmi les données de **test** (cf. question III.2), et vérifiez que $R(a_i) \simeq b_i$.
<details>
<summary>Bloqué(e)? Cliquez <b>ici</b> pour afficher de l'aide</summary>
* Un vin = une ligne de la matrice <br>
* Un produit scalaire se calcule en faisant un produit ligne fois vecteur. Assurez-vous de manipuler des vecteurs qui ont les bonnes dimensions! Cela se vérifie avec la commande 'shape' <br>
* La transposée s'obtient avec ".T"
</details>
%% Cell type:code id: tags:
``` python
```
%% Cell type:markdown id: tags:
**III.7)** Définir un vecteur `b_prediction` qui renvoie les notes prédites par $R$ pour tous les vins du jeu de données de test. Interdiction de faire une boucle for!
%% Cell type:code id: tags:
``` python
```
%% Cell type:markdown id: tags:
On peut alors essayer de comparer `b_prediction` au vrai `b_test`. Que pensez-vous du résultat?
%% Cell type:code id: tags:
``` python
comparaison = pd.DataFrame({'Réalité': b_test, 'Prédiction': b_prediction}) # on compare
comparaison_partiel = comparaison.head(25) # on prend les 25 premiers vins
comparaison_partiel.plot(kind='bar',figsize=(10,8)) # on affiche la comparaison
```
%% Cell type:markdown id: tags:
**III.8)** On peut essayer de quantifier l'erreur commise par notre prédiction : pour cela on va calculer l'erreur commise *en moyenne* pour chaque vin : $\frac{1}{m} \sum_i \vert R(a_i) - y_i \vert$. Considérez-vous que c'est un bon chiffre? Et quid de l'écart-type?
%% Cell type:code id: tags:
``` python
```
%% Cell type:code id: tags:
``` python
```
%% Cell type:markdown id: tags:
A votre avis est-ce que notre modèle linéaire est pertinent?
%% Cell type:markdown id: tags:
### Conclusion:
Ce TP était un peu une excuse pour faire de la science des données, et, de manière cachée, de la modélisation statistique. Si cela vous a intéressé, ou a minima rendu curieux, sachez que ce sont des sujets développés en profondeur dans nos masters en science des données: un double master généraliste [Mathématiques-Informatique Data Science](http://master.math.univ-paris-diderot.fr/parcours/data/), et un Master plus orienté vers la [finance](https://masterfinance.math.univ-paris-diderot.fr/).
%% Cell type:markdown id: tags:
### Sources :
- Données : le Wine Quality Dataset, récolté lors d'une étude de comparaison entre les vins rouges et blancs/verts portuguais http://www3.dsi.uminho.pt/pcortez/wine/
- Idée de l'exercice : [https://towardsdatascience.com/a-beginners-guide-to-linear-regression-in-python-with-scikit-learn-83a8f7ae2b4f](https://web.archive.org/web/20190605170834if_/https://towardsdatascience.com/a-beginners-guide-to-linear-regression-in-python-with-scikit-learn-83a8f7ae2b4f) par [Nagesh Singh Chauhan](https://twitter.com/nschauhan00?lang=fr)
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment