Lorsqu'il s'agit de tenseurs, je suis souvent confus quant à "ça? Quel pied est où?" Donc, sur la base de l 'Approximation de bas rang des images par HOSVD, procédons aux étapes en vérifiant chacune d'elles.
Ci-dessous, en supposant une formation pratique, j'écrirai le processus en détail persistant.
Ci-dessous, pour faciliter l'affichage, je l'ai écrit en mode interactif Python, mais en réalité, il est plus pratique de le faire avec ipython.
Tout d'abord, importons les bibliothèques requises.
>>> import numpy as np
>>> from scipy import linalg
>>> from PIL import Image
Ensuite, chargez une image appropriée. J'avais juste une image de taille raisonnable sous la main, alors utilisons-la.
C'est le cheval spirituel Itanum 2.
Lisons ceci et vérifions la taille.
>>> img = Image.open("uma.jpg ")
>>> img.size
(480, 340)
Il s'agit de données d'image de 480X340 pixels. Pour plus tard, déposez la hauteur et la largeur dans des variables.
>>> w, h = img.size
Maintenant, convertissons les données d'image en un tableau Numpy.
>>> X = np.asarray(img)
>>> X.shape
(340, 480, 3)
Notez que l'ordre est "hauteur, largeur, 3 (RVB)".
Je vais faire du SVD à partir de maintenant, mais je dois amener le pied pour faire du SVD à l'extrême droite et transformer le tenseur du 3ème étage en matrice. Utilisez transpose pour échanger les jambes.
Vérifiez à nouveau la dimension de X.
>>> X.shape
(340, 480, 3)
C'est maintenant "hauteur, largeur, couleur", mais je veux d'abord écraser mes jambes sur la largeur, donc je dois commander "hauteur, couleur, largeur". Puisque l'ordre de "hauteur, largeur, couleur" est (0,1,2), l'ordre cible est (0,2,1).
>>> X.transpose(0,2,1).shape
(340, 3, 480)
L'ordre a changé. Ceci est en outre combiné dans une matrice avec les deux jambes gauches.
>>> X.transpose(0,2,1).reshape(h*3,w).shape
(1020, 480)
Vous avez créé avec succès une matrice avec des dimensions (hauteur et couleur, largeur). Sauvegardons ceci sous X1.
>>> X1 = X.transpose(0,2,1).reshape(h*3,w)
De même, je souhaite créer une matrice avec une dimension de hauteur à l'extrême droite. Regardez à nouveau la dimension X.
>>> X.shape
(340, 480, 3)
Je veux faire ceci (480, 3, 340) [^ rgb]. À cette fin, l'ordre doit être (1,2,0).
>>> X.transpose(1,2,0).shape
(480, 3, 340)
De même, mettez les deux jambes de gauche ensemble dans une file d'attente et enregistrez-la dans X2.
>>> X2 = X.transpose(1,2,0).reshape(w*3,h)
>>> X2.shape
(1440, 340)
La dimension du pied droit est la hauteur. À ce stade, le SVD est prêt.
Tout d'abord, SVD X1. Vérifions chaque taille plus en détail.
>>> U, s, A1 = linalg.svd(X1)
>>> U.shape
(1020, 1020)
>>> s.shape
(480,)
>>> A1.shape
(480, 480)
Maintenant, compressez les informations de A1 à 5%. À l'origine, il s'agissait de 480 lignes, seules les 24 premières lignes sont donc récupérées. Appelons cela a1.
>>> a1 = A1[:24, :]
>>> a1.shape
(24, 480)
De même, SVD X2 et récupère uniquement les 17 premières lignes (5% de 340) de A2.
>>> U, s, A2 = linalg.svd(X2)
>>> A2.shape
(340, 340)
>>> a2 = A2[:17,:]
>>> a2.shape
(17, 340)
En prenant l'inversion de a1 et a2 obtenue ici, il devient une matrice de compression qui écrase la largeur et la hauteur.
>>> a1.T.shape
(480, 24)
>>> a2.T.shape
(340, 17)
On voit que les matrices sont écrasées en 480 → 24 et 340 → 17, respectivement. Si cela est pris comme un produit intérieur avec le pied approprié de X, un tenseur de noyau est obtenu.
Tout d'abord, multipliez X par a1.T. Vous pouvez dire quel pied et quel pied écraser en regardant la forme.
>>> X.shape
(340, 480, 3)
>>> a1.T.shape
(480, 24)
Avec le plus à gauche comme 0e, écrasez la 1ère jambe de X et la 0e jambe de a1.T. Remplacez-le par X3.
>>> X3 = np.tensordot(X, a1.T, (1,0))
>>> X3.shape
(340, 3, 24)
Il a été compressé avec succès de 480 à 24. Ce qu'il faut noter ici, c'est l'ordre des jambes après le point tenseur. Jusqu'à ce que vous vous y habituiez, vous devriez vérifier la forme de quel pied du nouveau tenseur à chaque fois.
Ensuite, appliquez a2.T sur le pied qui représente la hauteur de X3 et compressez-le. Découvrez lequel doit être écrasé à nouveau.
>>> X3.shape
(340, 3, 24)
>>> a2.T.shape
(340, 17)
Si tous les pieds sont de taille différente, il est pratique de savoir immédiatement que vous devez écraser la même dimension. Dans ce cas, vous pouvez écraser les nombres 0, donc
>>> x = np.tensordot(X3,a2.T,(0,0))
>>> x.shape
(3, 24, 17)
Vous avez réussi à obtenir un noyau tenseur x avec une dimension réduite de X.
Enfin, X a été compressé en une matrice de compression / restauration de largeur a1, une matrice de compression / restauration de hauteur a2 et un tenseur de noyau x. Examinons ce taux de compression.
>>> float(x.size + a1.size + a2.size)/X.size
0.03783496732026144
Nous avons donc pu le compresser à environ 3,8% des données d'origine.
Maintenant, j'ai le tenseur de noyau x et les matrices de reconstruction de largeur et de hauteur a1 et a2. Si vous multipliez le tenseur du noyau par a1 et a2, vous obtiendrez le tenseur approximatif Y du tenseur d'origine, alors faisons-le.
Vérifiez d'abord les dimensions.
>>> x.shape
(3, 24, 17)
>>> a1.shape
(24, 480)
Parce qu'il suffit d'écraser le 1er de x et le 0e de a1
>>> x2 = np.tensordot(x,a1,(1,0))
>>> x2.shape
(3, 17, 480)
La largeur a été restaurée de 24 à 480. De même, soit x3 la hauteur restaurée.
>>> x3 = np.tensordot(x2,a2,(1,0))
>>> x3.shape
(3, 480, 340)
Cependant, l'ordre des jambes est différent tel qu'il est. Jetez un nouveau coup d'œil à X.
>>> X.shape
(340, 480, 3)
Si vous le comparez à X, vous pouvez voir que les jambes doivent être de l'ordre de (2,1,0).
>>> Y = x3.transpose(2,1,0)
>>> Y.shape
(340, 480, 3)
Il s'agit du tenseur restauré à partir du tenseur du noyau x à l'aide des matrices de restauration a1 et a2. Maintenant que nous avons les mêmes dimensions que les données d'origine, nous pouvons créer un objet Image. Cependant, il est nécessaire de lui faire un type uint8 avant de l'insérer dans ʻImage.fromarray`.
>>> img2 = Image.fromarray(np.uint8(Y))
>>> img2.save("approximated.jpg ")
L'image restaurée créée de cette manière ressemble à ceci.
Eh bien, je pense que j'ai pu le restaurer à partir des données compressées à 3,8% sans utiliser les informations selon lesquelles "l'original est une image".
J'ai essayé de compresser et de restaurer l'image en utilisant HOSVD (Higher Order Singular Value Decomposition). Utiliser IPython est très pratique car il fonctionne comme la complétion d'onglets. Après avoir écrasé les jambes du tenseur, il est facile de ne pas savoir quel nombre est quoi, mais je pense que les erreurs / confusion peuvent être réduites en travaillant tout en vérifiant les dimensions de chaque forme.
Python est pratique, n'est-ce pas ... [^ ruby].
[^ rgb]: Ici, la raison pour laquelle la hauteur et la couleur des jambes ne sont pas échangées de l'ordre de (3 480 340) est que les données de pied résumées sont alignées avec (R, G, B, R, G, B ...). Je te veux. De l'ordre de (3 480 340), les données seront disposées dans l'ordre de (RRR ..., GGG ...., BBB ...) différemment de celui de X1.
[^ ruby]: Pour le moment, ma langue préférée est Ruby, mais la richesse de la bibliothèque semble être que Python a une longue journée ...
Recommended Posts