TL;DR
numpy.matrix
est obsolète. Utilisez les opérateurs «numpy.ndarray» et «@».
Il y a quelque temps, quand j'ai écrit un article "Essayez la décomposition en valeurs singulières de la matrice daimyo", un ancien collègue a dit que "numpy.matrix` est obsolète. J'ai été surpris d'entendre cela [^ 1].
[^ 1]: Actuellement, il a été modifié pour que matrix
ne soit pas utilisé, mais il a été utilisé lors de son apparition.
Quand je l'ai recherché, il y avait une explication très détaillée dans StackOverflow, donc je vais l'expliquer en fonction de cela.
Il existe un processus appelé Décomposition en Valeur Singulière (SVD) en algèbre linéaire. La matrice m-par-n X est divisée en produits de la matrice unitaire m-par-m U, de la matrice diagonale m-par-n S et de la matrice unitaire n-par-n V. Faisons une matrice appropriée.
import numpy as np
from scipy import linalg
X = (np.arange(6)+1).reshape(2,3)
Maintenant X est une matrice 2x3 comme celle-ci:
[[1 2 3]
[4 5 6]]
Let's SVD ceci.
U, s, V = linalg.svd(X)
linalg.svd
renvoie troisndarray
s pour la décomposition de singularité. U correspond à m lignes et n colonnes, V correspond à n lignes et n colonnes. La matrice au milieu est une matrice diagonale avec m lignes et n colonnes, mais c'est un tableau unidimensionnel car il n'y a que des éléments diagonaux.
print(s) # => [9.508032 0.77286964]
Maintenant, la décomposition en valeurs singulières revient à la matrice d'origine en prenant le produit de la matrice dans l'ordre U, S, V. Cependant, comme la matrice diagonale au centre est un tableau unidimensionnel, il est nécessaire de la remodeler une fois en une matrice. Il existe une fonction appelée linalg.diagsvd
pour cela.
S = linalg.diagsvd(s, X.shape[0], X.shape[1])
Maintenant S est une matrice diagonale 2x3.
print(S)
# =>
# [[9.508032 0. 0. ]
# [0. 0.77286964 0. ]]
Après cela, si vous prenez le produit de trois matrices, il reviendra à la matrice d'origine, mais le produit *
of numpy.ndarray
sera le produit de chaque élément. Vous devez utiliser numpy.ndarray.dot
pour le produit matriciel, qui est un calcul écrasant.
print(U.dot(S.dot(V)))
# =>
# [[1. 2. 3.]
# [4. 5. 6.]]
C'est revenu à la normale, mais je veux utiliser des opérateurs infixes comme *
au lieu d'appels de méthode comme ʻU.dot (S.dot (V)) `. Pour cela, «numpy.matrix» a été préparé. Changeons la matrice diagonale «S» de «ndarray» en «matrice».
Z = np.matrix(S)
«S» est «numpy.ndarray», mais «Z» est «numpy.matrix».
type(S) # => numpy.ndarray
type(Z) # => numpy.matrix
numpy.matrix
devient un produit matriciel lorsque *
est utilisé comme opérateur d'infixe. Il fait également du produit «numpy.ndarray» et «numpy.matrix» un produit matriciel. De là, vous pouvez écrire quelque chose comme ʻU * Z * V`.
print(U * Z * V)
# =>
# [[1. 2. 3.]
# [4. 5. 6.]]
C'est pratique.
Comme vous pouvez le voir, «matrice» a été utilisé plutôt parce que c'était pratique, mais il y avait quelques problèmes. En particulier, il peut se comporter de manière non intuitive lorsqu'il est mélangé avec «ndarray». Permettez-moi de vous donner un exemple de la précédente OS.
Préparons une matrice ndarray
appropriée ʻarr et faisons un
mat qui en fait une
matrice`.
arr = np.zeros((3,4))
mat = np.matrix(arr)
«Ar» et «mat» représentent une matrice 3 par 4.
Tout d'abord, ajoutons les deux.
(arr+mat).shape # => (3, 4)
Il n'y a pas de problème car nous ajoutons des matrices de même forme. Ensuite, coupons-le. Ajoutons seulement la première ligne de chacun. Comme j'ai ajouté uniquement la première ligne de 3 lignes et 4 colonnes, je voudrais que ce soit une matrice à 1 ligne et 4 colonnes.
(arr[0,:] + mat[0,:]).shape # => (1, 4)
Ce n'est pas non plus un problème.
Ajoutons maintenant les colonnes au lieu des lignes. Ajoutons la première ligne de chacun. Devrait être 3 lignes et 1 colonne.
(arr[:,0] + mat[:,0]).shape # => (3, 3)
Il est devenu 3 lignes et 3 colonnes. Qu'est-il arrivé?
C'est parce que la forme de ʻarr [: .0] ʻis(3,)
, tandis que la forme de mat [:, 0] ʻest
(3,1)`, donc Diffusion Rules a été appliqué.
Créons en fait une matrice de (3,1) et une matrice de (3,) et ajoutons-les.
x = np.ones(3)
y = np.arange(3).reshape(3,1)
x.shape # => (3,)
y.shape # => (3, 1)
La situation est la même qu'avant. Ajoutons-le.
(x+y).shape # => (3, 3)
C'est (3, 3). Au fait, la valeur ressemble à ceci.
print(x)
# =>
# [1. 1. 1.]
print(y)
# =>
#[[0]
# [1]
# [2]]
print(x+y)
# =>
#[[1. 1. 1.]
# [2. 2. 2.]
# [3. 3. 3.]]
De plus, "*" de "matrice" est le produit de la matrice, mais "/" est la division pour chaque élément.
a = np.matrix([[2,4],[6,8]])
b = np.matrix(([2,2],[2,2]])
print(a)
# =>
# [[2 4]
# [6 8]]
print(b)
# =>
# [[2 2]
# [2 2]]
print(a*b)
# =>
# [[12 12]
# [28 28]]← Je comprends cela
print(a/b)
# =>
# [[1. 2.]
# [3. 4.]] ← !!!
Ceci est également peu intuitif.
@
Maintenant, la raison pour laquelle nous voulions utiliser «matrice» était que le produit matriciel de «ndarray» était difficile à utiliser dans la méthode, et nous voulions écrire le produit matriciel en notation neutre, comme «A * B». Cependant, depuis Python 3.5, l'opérateur infixe @
, qui représente le produit matriciel de ndarray
, a été introduit (PEP 465. )). Maintenant, la décomposition des valeurs singulières et le processus d'annulation peuvent être écrits comme suit.
import numpy as np
from scipy import linalg
X = (np.arange(6)+1).reshape(2,3)
print(X)
# =>
# [[1 2 3]
# [4 5 6]]
U, s, V = linalg.svd(X)
S = linalg.diagsvd(s, X.shape[0], X.shape[1])
print(U @ S @ V)
# =>
# [[1. 2. 3.]
# [4. 5. 6.]]
C'est facile. Si $ m <n $ en m lignes et n colonnes, tous les éléments de $ V $ ne sont pas utilisés, vous pouvez donc écrire comme suit en utilisant np.diag
au lieu de linalg.diagsvd
.
U, s, V = linalg.svd(X)
S = np.diag(s)
print(U @ S @ V[:2,:])
Même en effectuant une décomposition de valeur singulière et en effectuant une approximation de rang bas du rang r, il peut être écrit comme «np.diag» comme suit. Ce qui suit est un échantillon d'une matrice de 200 par 50 qui est singulièrement décomposée et de faible rang. Tout d'abord, créons une matrice X avec 200 lignes et 50 colonnes.
m = 200
n = 50
X = np.arange(m*n).reshape(m,n)
print(X)
X est une telle matrice.
[[ 0 1 2 ... 47 48 49]
[ 50 51 52 ... 97 98 99]
[ 100 101 102 ... 147 148 149]
...
[9850 9851 9852 ... 9897 9898 9899]
[9900 9901 9902 ... 9947 9948 9949]
[9950 9951 9952 ... 9997 9998 9999]]
Pour approximer cela avec le rang r = 10, procédez comme suit:
U, s, V = linalg.svd(X)
S = np.diag(s)
r = 10
Ur = U[:, :r]
Sr = S[:r, :r]
Vr = V[:r, :]
Y = Ur @ Sr @ Vr
print(np.array(Y, np.int))
Y est un nombre réel, mais il ressemble à ceci lorsqu'il est converti en entier.
[[ 0 0 1 ... 46 47 48]
[ 50 50 51 ... 97 98 98]
[ 100 101 102 ... 146 147 149]
...
[9850 9851 9851 ... 9897 9897 9899]
[9900 9901 9901 ... 9947 9947 9949]
[9950 9951 9952 ... 9996 9998 9999]]
Ça fait du bien.
Avec l'introduction de l'opérateur «@», qui représente le produit de la matrice dans la notation en incrustation de «numpy.ndarray», le plus grand avantage de l'utilisation de «numpy.matrix» a disparu. A partir de maintenant, utilisons numpy.ndarray
et @
.
Recommended Posts