In this post I will present how it can be handled Linear Algebra in Python to implement Machine Learning algorithms. This will be important not only to represent the 'X' matrix and 'y' vector as the input and output, respectively, of the algorithms, but also to perform the vectorized implementation of the algorithms which turn out to present much more performance than the iterative alternatives.
For this we shall use the
numpy Python library, particularly, its version 1.6.1.
Both for the representation of matrices and vectors we shall use the
numpy array object which allow us to perform all needed operations. It follows a set of examples that cover all (I hope) the operations we may need to do perform in the implementation of Machine Learning algorithms in Python.
So firstly we must import the library:
>>> from numpy import *
By importing the library this way there is no need to use the prefix np when referring to a numpy object like array.
So then we create some matrices and a vector and print them to the screen:
>>> # Matrix M1 3x3 (3 rows, 3 columns)
>>> M1= array([[1,0,5],[2,5,3],[6,2,8]])
>>> print M1
[[1 0 5]
[2 5 3]
[6 2 8]]
>>> # Matrix M2 3x3 (3 rows, 3 columns)
>>> M2= array([[4,0,2],[2,5,8],[2,4,1]])
>>> print M2
[[4 0 2]
[2 5 8]
[2 4 1]]
>>> # Matrix M3 2x3 (2 rows, 3 colums)
>>> M3= array([[1,3,2],[4,0,1]])
>>> print M3
[[1 3 2]
[4 0 1]]
>>> # Matrix M4 3x2 (3 rows, 2 colums)
>>> M4= array([[1,3],[0,1],[5,2]])
>>> print M4
[[1 3]
[0 1]
[5 2]]
>>> # Vector 1x3 (3-dimensional vector)
>>> v= array([[2,5,4]])
>>> print v
[[2 5 4]]
>>> # Transpose of the vector to convert it from a horizontal to a vertical vector
>>> v= v.transpose()
>>> print v
[[2]
[5]
[4]]
Notice that in line 29 we define the vector as a matrix of 1 row (getting an horizontal vector). This is necessary because the transpose only works well in an matrix. If we defined v as we define u bellow (notice that bellow we have only one'[' instead of two '[[' we would not be able to apply the transpose and get a vertical vector):
>>> u= array([2,5,4])
>>> print u
[2 5 4]
>>> print u.transpose()
[2 5 4]
And how can we create a matrix or a vector initializing all elements to 0?
>>> # Matrix (3 rows, 4 column) of float 0's
>>> print zeros((3,4))
[[ 0. 0. 0. 0.]
[ 0. 0. 0. 0.]
[ 0. 0. 0. 0.]]
>>> # Vector (6 rows, 1 column) of float 0's
>>> print zeros((6,1))
[[ 0.]
[ 0.]
[ 0.]
[ 0.]
[ 0.]
[ 0.]]
And to 1?
>>> # Matrix (4 rows, 3 column) of float 1's
>>> print ones((4,3))
[[ 1. 1. 1.]
[ 1. 1. 1.]
[ 1. 1. 1.]
[ 1. 1. 1.]]
>>> # Vector (1 row, 6 columns) of float 1's
>>> print ones((1,6))
[[ 1. 1. 1. 1. 1. 1.]]
Now that we have some matrices and a vector let's perform some operations on them:
>>> # Size of the matrix
>>> print "size:", M1.shape, "nr. of rows:", M1.shape[0], "nr. of columns:", M1.shape[1]
size: (3, 3) nr. of rows: 3 nr. of columns: 3
>>> # Access cells of a matrix
>>> print "element in 1st row and 1st column of M1:", M1[0,0]
element in 1st row and 1st column of M1: 1
Now let's add two matrices which results in a new matrix where each element is the result of adding the correspondent elements in the original matrices. Both added matrices must have the same size (rows and columns):
>>> # Matrix addition
>>> print M1+M2
[[ 5 0 7]
[ 4 10 11]
[ 8 6 9]]
And now let's multiply a scalar and a matrix. The result is a matrix with the same dimension (rows and columns) where each cell is multiplied by the scalar:
>>> # Scalar multiplication
>>> print 3*M1
[[ 3 0 15]
[ 6 15 9]
[18 6 24]]
It follows the multiplication of a vector by a matrix. If we multiply A (nrOfRows-A, nrOfCols-A) and B (nrOfRows-B,nrOfCols-B), and without worring which is a vector and which is a matrix, the multiplication is only possible when, nrOfCols-A == nrOfRows-B. The result of the multiplication is a matrix with size (nrOfRows-A,nrOfCols-B)
>>> # Multiplication of a matrix with a vector
>>> print dot(M1,v)
[[22]
[41]
[54]]
To explain how the multiplication above works, see bellow:
$$\begin{bmatrix}
1&0&5\\
2&5&3\\
6&2&8
\end{bmatrix}*\begin{bmatrix}
2\\
5\\
4
\end{bmatrix}=\begin{bmatrix}
1*2+0*5+5*4\\
2*2+5*5+3*4\\
6*2+2*5+8*4
\end{bmatrix}=\begin{bmatrix}
22\\
41\\
54
\end{bmatrix}$$
It is also possible to multiply two vectors. What we get as a result depends on the dimensions of the multiplied vectors. If we multiply a 1 by 3 vector by a 3 by 1 vector we get a 1 by 1 matrix (which we can easily convert to a scalar) obtained by multiplying corresponding entries and then summing those products. If we multiply a 3 by 1 vector by a 1 by 3 vector we get a 3 by 3 matrix obtained by multiplying each cell in the first vector by each cell in the correspondent row of the second vector. Bellow we show two examples by multiplying a vector by its transpose.
>>> # Vector multiplication
>>> print dot(v.transpose(), v)
[[45]]
>>> print dot(v.transpose(), v)[0][0]
45
>>> print dot(v, v.transpose())
[[ 4 10 8]
[10 25 20]
[ 8 20 16]]
And now we will multiply two matrices. This is the general case of the above cases, and so, to multiply two matrices we need that the number of columns of the first matrix be equal to the number of rows of the second matrix. And as above the result shall be a matrix with size (nrOfRows-A,nrOfCols-B)
>>> # Matrix multiplication
>>> print dot(M3, M4)
[[11 10]
[ 9 14]]
To create a Identity matrix is also pretty straightforward:
>>> # Identity matrix of 3*3
>>> print eye(3)
[[ 1. 0. 0.]
[ 0. 1. 0.]
[ 0. 0. 1.]]
As it can be seen bellow, any matrix multiplied by the identity results in that same matrix. M1 must be a square matrix (same nr of rows and columns). If you remember from above M1 is a 3x3 square matrix:
>>>print "Checking identity: M1xI"
Checking identity: M1xI
>>>print dot(M1, eye(3))
[[ 1. 0. 5.]
[ 2. 5. 3.]
[ 6. 2. 8.]]
>>>print "Checking identity: IxM1"
Checking identity: IxM1
>>>print dot(eye(3), M1)
[[ 1. 0. 5.]
[ 2. 5. 3.]
[ 6. 2. 8.]]
Now let's see how to get the inverse of a matrix. By definition matrix*(inverse(matrix)) == (inverse(matrix))*matrix == identity. Also be aware that not all matrices have inverse... the rule of thumb is that matrices that are near to 0 may not have inverse.
>>> # Inverse of M1
>>> print linalg.inv(M1)
[[-0.35416667 -0.10416667 0.26041667]
[-0.02083333 0.22916667 -0.07291667]
[ 0.27083333 0.02083333 -0.05208333]]
>>> # Inverse of M1 multiplied by M1 - which results in the identity matrix
>>> print dot(linalg.inv(M1), M1)
[[ 1.00000000e+00 0.00000000e+00 0.00000000e+00]
[ 0.00000000e+00 1.00000000e+00 -1.11022302e-16]
[ 5.55111512e-17 1.38777878e-17 1.00000000e+00]]
>>> # M1 multiplied by inverse of M1 - which results in the identity matrix
>>> print dot(M1, linalg.inv(M1))
[[ 1.00000000e+00 0.00000000e+00 0.00000000e+00]
[ 0.00000000e+00 1.00000000e+00 -8.32667268e-17]
[ 0.00000000e+00 2.77555756e-17 1.00000000e+00]]
We have seen above for the case of a vector, but in any case, see in the following how you can get the transpose of a matrix.
>>> # Matrix transpose
>>> print M1.transpose()
[[1 2 6]
[0 5 2]
[5 3 8]]
>>> print M3.transpose()
[[1 4]
[3 0]
[2 1]]
And how can we get the maximum value present in a given matrix or vector and how can we do this by row or column of a matrix
>>> # Maximum value of all cells of a matrix
>>> print M1.max()
8
>>> print M2.max()
8
>>> print M3.max()
4
>>> print M4.max()
5
>>> # Maximum value of all cells of a vector
>>> print v.max()
5
>>> # Maximum value of each column of a matrix
>>> print M1.max(0)
[6 5 8]
>>> # Maximum value of each row of a matrix
>>> print M1.max(1)
[5 5 8]
Other task important to Machine Learning is to be able to concatenate. Bellow you can find some examples of it.
>>> # CONCATENATE rows of M1 with rows of M2
>>> print concatenate((M1,M2))
[[1 0 5]
[2 5 3]
[6 2 8]
[4 0 2]
[2 5 8]
[2 4 1]]
>>> # CONCATENATE columns of M1 with columns of M2
>>> print concatenate((M1,M2),1)
[[1 0 5 4 0 2]
[2 5 3 2 5 8]
[6 2 8 2 4 1]]
>>> # CONCATENATE a vector with a matrix
>>> print concatenate((v,M1),1)
[[2 1 0 5]
[5 2 5 3]
[4 6 2 8]]