Valeurs manquantes et factorisation de matrices

Réflexion autour des valeur manquantes et de la factorisation de matrice positive.

[2]:
%matplotlib inline

Matrice à coefficients aléatoires

On étudie la factorisation d’une matrice à coefficients tout à fait aléatoires qui suivent une loi uniforme sur l’intervalle [0,1]. Essayons sur une petite matrice :

[3]:
from numpy.random import rand

M = rand(3, 3)
M
[3]:
array([[ 0.05119593,  0.43722929,  0.9290821 ],
       [ 0.4588466 ,  0.14187813,  0.23762633],
       [ 0.9768084 ,  0.47674026,  0.79044526]])
[4]:
from sklearn.decomposition import NMF

mf = NMF(1)
mf.fit_transform(M)
[4]:
array([[ 0.67825803],
       [ 0.38030919],
       [ 1.02295362]])

La matrice précédente est la matrice W dans le produit WH, la matrice qui suit est H.

[5]:
mf.components_
[5]:
array([[ 0.73190904,  0.50765757,  0.92611883]])
[6]:
mf.reconstruction_err_ / (M.shape[0] * M.shape[1])
[6]:
0.07236890712696428

On recalcule l’erreur :

[7]:
d = M - mf.fit_transform(M) @ mf.components_
a = d.ravel()
e = a @ a.T
e**0.5 / (M.shape[0] * M.shape[1])
[7]:
0.072368907126964283
[8]:
e.ravel()
[8]:
array([ 0.42421796])

Et maintenant sur une grande et plus nécessairement carrée :

[9]:
M = rand(300, 10)
mf = NMF(1)
mf.fit_transform(M)
mf.reconstruction_err_ / (M.shape[0] * M.shape[1])
[9]:
0.004996164872801101

L’erreur est la même :

[10]:
errs = []
rangs = list(range(1, 11))
for k in rangs:
    mf = NMF(k)
    mf.fit_transform(M)
    e = mf.reconstruction_err_ / (M.shape[0] * M.shape[1])
    errs.append(e)
[11]:
import pandas

df = pandas.DataFrame(dict(rang=rangs, erreur=errs))
df.plot(x="rang", y="erreur")
[11]:
<matplotlib.axes._subplots.AxesSubplot at 0x199924d8668>
../../_images/notebooks_ml_valeurs_manquantes_mf_15_1.png

Matrice avec des vecteurs colonnes corrélés

Supposons maintenant que la matrice précédente M est de rang 3. Pour s’en assurer, on tire une matrice aléalatoire avec 3 vecteurs colonnes et on réplique des colonnes jusqu’à la dimension souhaitée.

[12]:
from numpy import hstack

M = rand(300, 3)
M = hstack([M, M, M, M[:, :1]])
M.shape
[12]:
(300, 10)
[13]:
errs = []
rangs = list(range(1, 11))
for k in rangs:
    mf = NMF(k)
    mf.fit_transform(M)
    e = mf.reconstruction_err_ / (M.shape[0] * M.shape[1])
    errs.append(e)
[14]:
import pandas

df = pandas.DataFrame(dict(rang=rangs, erreur=errs))
df.plot(x="rang", y="erreur")
[14]:
<matplotlib.axes._subplots.AxesSubplot at 0x199925d6630>
../../_images/notebooks_ml_valeurs_manquantes_mf_19_1.png

On essaye à nouveausur une matrice un peu plus petite.

[15]:
M = rand(3, 2)
M = hstack([M, M[:, :1]])
M
[15]:
array([[ 0.27190312,  0.6497563 ,  0.27190312],
       [ 0.44853292,  0.87097224,  0.44853292],
       [ 0.29424835,  0.65106952,  0.29424835]])
[16]:
mf = NMF(2)
mf.fit_transform(M)
[16]:
array([[ 0.61835197,  0.        ],
       [ 0.82887888,  0.29866219],
       [ 0.61960446,  0.07743224]])
[17]:
mf.components_
[17]:
array([[ 0.43972536,  1.05078419,  0.43972536],
       [ 0.28143493,  0.        ,  0.28143493]])

La dernière colonne est identique à la première.

Matrice identité

Et maintenant si la matrice M est la matrice identité, que se passe-t-il ?

[18]:
from numpy import identity

M = identity(3)
M
[18]:
array([[ 1.,  0.,  0.],
       [ 0.,  1.,  0.],
       [ 0.,  0.,  1.]])
[19]:
mf = NMF(1)
mf.fit_transform(M)
[19]:
array([[ 0.],
       [ 1.],
       [ 0.]])
[20]:
mf.components_
[20]:
array([[ 0.,  1.,  0.]])
[21]:
mf.reconstruction_err_**2
[21]:
2.0000000000000004

On essaye avec k=2.

[22]:
mf = NMF(2)
mf.fit_transform(M)
[22]:
array([[ 0.        ,  0.        ],
       [ 0.        ,  1.03940448],
       [ 0.95521772,  0.        ]])
[23]:
mf.components_
[23]:
array([[ 0.        ,  0.        ,  1.04688175],
       [ 0.        ,  0.96208937,  0.        ]])
[24]:
mf.reconstruction_err_**2
[24]:
1.0

Avec des vecteurs normés et indépendants (formant donc une base de l’espace vectoriel), l’algorithme aboutit à une matrice W égale au k premiers vecteurs et oublie les autres.

Matrice identité et représentation spatiale

Pour comprendre un peu mieux ce dernier exemple, il est utile de chercher d’autres solutions dont l’erreur est équivalente.

[25]:
def erreur_mf(M, W, H):
    d = M - W @ H
    a = d.ravel()
    e = a @ a.T
    e**0.5 / (M.shape[0] * M.shape[1])
    return e


M = identity(3)
mf = NMF(2)
W = mf.fit_transform(M)
H = mf.components_
erreur_mf(M, W, H)
[25]:
1.0
[26]:
W
[26]:
array([[ 0.        ,  0.        ],
       [ 0.9703523 ,  0.        ],
       [ 0.        ,  1.02721047]])
[27]:
H
[27]:
array([[ 0.        ,  1.03055354,  0.        ],
       [ 0.        ,  0.        ,  0.97351032]])
[28]:
W @ H
[28]:
array([[ 0.,  0.,  0.],
       [ 0.,  1.,  0.],
       [ 0.,  0.,  1.]])
[29]:
import matplotlib.pyplot as plt

fig = plt.figure()
ax = fig.add_subplot(111, projection="3d")
wh = W @ H
ax.scatter(M[:, 0], M[:, 1], M[:, 2], c="b", marker="o", s=20)
ax.scatter(wh[:, 0], wh[:, 1], wh[:, 2], c="r", marker="^")
[29]:
<mpl_toolkits.mplot3d.art3d.Path3DCollection at 0x19992d2a5c0>
../../_images/notebooks_ml_valeurs_manquantes_mf_40_1.png

Et si on pose maintenant :

[30]:
import numpy

W = numpy.array([[0.5, 0.5, 0], [0, 0, 1]]).T
H = numpy.array([[1, 1, 0], [0.0, 0.0, 1.0]])
W
[30]:
array([[ 0.5,  0. ],
       [ 0.5,  0. ],
       [ 0. ,  1. ]])
[31]:
H
[31]:
array([[ 1.,  1.,  0.],
       [ 0.,  0.,  1.]])
[32]:
W @ H
[32]:
array([[ 0.5,  0.5,  0. ],
       [ 0.5,  0.5,  0. ],
       [ 0. ,  0. ,  1. ]])
[33]:
erreur_mf(M, W, H)
[33]:
1.0
[34]:
fig = plt.figure()
ax = fig.add_subplot(111, projection="3d")
wh = W @ H
ax.scatter(M[:, 0], M[:, 1], M[:, 2], c="b", marker="o", s=20)
ax.scatter(wh[:, 0], wh[:, 1], wh[:, 2], c="r", marker="^")
[34]:
<mpl_toolkits.mplot3d.art3d.Path3DCollection at 0x19992a2e5f8>
../../_images/notebooks_ml_valeurs_manquantes_mf_46_1.png

On peut voir la matrice M comme un ensemble de n points dans un espace vectoriel. La matrice W est un ensemble de k < n points dans le même espace. La matrice WH, de rang k est une approximation de cet ensemble dans le même espace, c’est aussi n combinaisons linéaires de k points de façon à former n points les plus proches proches de n points de la matrice M.

[35]:


Notebook on github