1. 程式人生 > >Python 機器學習——線性代數和矩陣運算 從matlab遷移到python

Python 機器學習——線性代數和矩陣運算 從matlab遷移到python

                     
 

誠然,沒有一門語言能夠撼動matlab的矩陣或科學計算在學術圈的地位,因其簡潔的語法(matrix是其基本資料型別),因其矩陣運算的便捷,因其術業有專攻(matlab:為科學計算而生),因其名字matlab:matrix laboratory,所在的公司名mathworks:math works。我在寫過一些matlab和python的程式碼之後,油然發過一句感慨“沒有一門語言能比matlab還更具數學感”。然而,因為numpypandasmatplotlib 等一眾優秀的開發者(不排除從matlab陣營溜出來的)貢獻的一眾優秀的開源的庫,讓python具備了和matlab一樣的功能,為工程而生的python從此因為有了數學家的參與就相當初的matlab一樣,也學術起來,工程學術通吃。

本文試圖回答的問題包括:

  • 為什麼矩陣運算要從matlab遷移到python?
  • 如何進行遷移,其中會涉及哪些基本程式設計理念的差異?
  • 遷移的過程中需要注意哪些細節?
 

python矩陣運算,更準確地說,是numpy矩陣運算,為了更為方便地使用numpy庫,如下文使用的那樣,我們需要匯入numpy庫並重命名為np

import numpy as np
  • 1

零、程式設計理念的對比

0.1 程式設計正規化

matlab是面向過程的程式設計方式,而python既支援面向過程又支援面向物件,是一種多正規化(multi paradigms)的程式語言。因此,不難理解python程式語言中廣泛存在的以下的兩種等價實現方式:

np.dot
(X, w)    # 呼叫全域性函式,面向過程的程式設計方式X.dot(w)        # 呼叫物件的成員函式,面向物件的程式設計方式
  • 1
  • 2

0.2 matlab從1開始計數,python從0開始

0.2.1 對矩陣而言:

r = size(A, 1);         % 表示的是行數c = size(A, 2);         % 表示的是列數i = 0:size(A, 1) - 1;A(i, :)             % error: 矩陣的下標索引必須是正整數型別(>=1)或邏輯型別(true/false)
  • 1
  • 2
  • 3
  • 4

0.2.2 對python而言:

A.shape[0]      # 行數, axis = 0
A.shape[1]      # 列數,   axis = 1# 等價的(或者叫面向過程的)表達方式np.shape(A)[0]np.shape(A)[1]# 可從0開始索引矩陣的行r = A.shape[0]A[0:r, :]
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

0.3 matlab矩陣索引用的是小括號,python是中括號

小括號對於一種支援面向物件、支援運算子過載的語言來說,具有特別的意義,過載括號運算子是仿函式實現的命門。如果瞭解c++的運算子過載機制,一個類如果過載了括號運算子,便可稱作仿函式,把一個類當做函式來使用。恰好,python也支援運算子過載。

class Prob(object):    def __init__(self, lhs):        self.lhs = lhs    def __call__(self, rhs):        return self.lhs * rhs    # def __call__(self, lhs, rhs)    #   return lhs * rhsif __name__ == '__main__':    p = Prob(2)     # 呼叫的是類建構函式,也即__init__    print(p(5))     # 呼叫的是例項的括號運算子,也即__call__
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

0.4 切片的端點值

matlab中的切片(也即:表示式),是包含兩個端點值的,也即是一個閉區間

1:5     % 1, 2, 3, 4, 5
  • 1

而python中的切片是一個左閉右開的區間:

A[0:2, :]   # 索引的是第零行和第一行,而不包括第二行
  • 1

0.5 維度的順序

% matlab>> zeros(2, 3, 4)ans(:,:,1) =   0   0   0   0   0   0ans(:,:,2) =   0   0   0   0   0   0ans(:,:,3) =   0   0   0   0   0   0ans(:,:,4) =   0   0   0   0   0   0
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
# python>>> np.zeros((2, 3, 4))array([[[ 0.,  0.,  0.,  0.],        [ 0.,  0.,  0.,  0.],        [ 0.,  0.,  0.,  0.]],       [[ 0.,  0.,  0.,  0.],        [ 0.,  0.,  0.,  0.],        [ 0.,  0.,  0.,  0.]]])
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

所以如果一幅256×256d維增廣到1+d維的情況(每個輸入的第一維值為1):

N = 100, d = 5X = np.random.randn(N, d)x0 = np.ones((X.shape[0], 1))X = np.concatenate((x0, X), axis=1)
  • 1
  • 2
  • 3
  • 4

1.9 向量(矩陣)層疊

1.9.1 matlab/octave

a = [1, 2, 3]b = [4, 5, 6]c = [a', b']c = [a; b]
  • 1
  • 2
  • 3
  • 4

1.9.2 python

a = np.array([1, 2, 3])b = np.array([4, 5, 6])np.c_[a, b]                 # c_: column    array([[1, 4],       [2, 5],       [3, 6]])np.r_[a, b]                 # r_: row    array([1, 2, 3, 4, 5, 6])
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

二、 幾種特殊矩陣

2.1 隨機矩陣(均勻分佈)

2.1.1 matlab/octave

rand(3, 2)
  • 1

2.1.2 python

np.random.rand(3, 2)
  • 1
 

無論是matlab中的rand()函式,還是numpy.random中的rand()函式,生成的隨機數都是服從[0-1]的均勻分佈(uniformed distributed),如何生成任意區間的隨機數呢?例[2, 5]區間上的隨機數 2*rand()+3

2.2 0矩陣, 全一矩陣,單位矩陣

 

符合大小的0矩陣,常常用以申請空間,初始化矩陣, 預先分配記憶體,提高執行的速度

2.2.1 matlab/octave

zeros(3, 2)ones(3, 2)eye(3)
  • 1
  • 2
  • 3

2.2.2 python

np.zeros((3, 2))np.ones((3, 2))np.eye(3)
  • 1
  • 2
  • 3

2.3 對角矩陣

2.3.1 matlab/octave

a = [1, 2, 3]diag(a)diag(diag(a)    % [1, 2, 3]
  • 1
  • 2
  • 3

2.3.2 python

a = np.array([1, 2, 3])np.diag(a)np.diag(np.diag(a))     % array([1, 2, 3])
  • 1
  • 2
  • 3

三、 矩陣運算

3.1 矩陣與標量運算

3.1.1 matlab/octave

A = [1, 2, 3; 4, 5, 6; 7, 8, 9]A*2A+2A-2A/2         % 最後統統轉換為A中的每一個元素(element-wise)同標量的運算
  • 1
  • 2
  • 3
  • 4
  • 5

3.1.2 python

A = np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9]])A*2A/2A-2A+2
  • 1
  • 2
  • 3
  • 4
  • 5

3.2 矩陣與矩陣運算

 

無論是matlab中的乘法運算(*),還是numpy中的np.dot()運算,本質上執行的都是矩陣的乘法運算,都需要滿足矩陣A的列數等於矩陣B的行數。

3.2.1 matlab/octave

A = [1, 2, 3; 4, 5, 6]  % 2*3B = [1, 2; 3, 4; 5, 6]  % 3*2A*B
  • 1
  • 2
  • 3

3.2.2 python

A = np.array([[1, 2, 3], [4, 5, 6]])B = np.array([[1, 2], [3, 4], [5, 6]])A.dot(B)    # np.dot(A, B)
  • 1
  • 2
  • 3

3.3 矩陣與向量運算

 

矩陣與向量的乘積,是矩陣與矩陣乘積的特例

3.4 按位矩陣與矩陣運算

3.4.1 matlab/octave

A.*AA.+AA.-AA./AA.^A
  • 1
  • 2
  • 3
  • 4
  • 5

3.4.2 python

A*AA+AA-AA/A
  • 1
  • 2
  • 3
  • 4
  • 5

3.5 矩陣元素的冪乘

3.5.1 matlab/octave

A.^2
  • 1

3.5.2 python

np.power(A, 2)
  • 1

3.6 矩陣的冪乘

 

方陣才有冪乘運算

3.6.1 matlab/octave

A^2
  • 1

3.6.2 python

np.linalg.matrix_power(A, 2)
  • 1

3.7 矩陣轉置

3.7.1 matlab/octave

A'
  • 1

3.7.2 python

A.T
  • 1

3.8 矩陣行列式

3.8.1 matlab/octave

det(A)
  • 1

3.8.2 python

np.linalg.det(A)
  • 1

3.9 矩陣求逆

3.9.1 matlab/octave

inv(A)
  • 1

3.9.10 python

A_inv = np.linalg.inv(A)assert(np.dot(A, A_inv).all() == (np.eye(2)).all()) 
  • 1
  • 2

3.10 計算矩陣協方差矩陣

 

協方差矩陣刻畫的是屬性間的關係,標準協方差矩陣的求法:

   
   

協方差(i,j)=(第i列所有元素-第i列均值)*(第j列所有元素-第j列均值)/(樣本數-1)(分母減去1是為了實現無偏估計)

 

3.10.1 matlab/octave

>> x1 = [4.0000 4.2000 3.9000 4.3000 4.1000]’>> x2 = [2.0000 2.1000 2.0000 2.1000 2.2000]'>> x3 = [0.60000 0.59000 0.58000 0.62000 0.63000]’>> cov( [x1,x2,x3] )ans =   2.5000e-02   7.5000e-03   1.7500e-03   7.5000e-03   7.0000e-03   1.3500e-03   1.7500e-03   1.3500e-03   4.3000e-04
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

3.10.1 python

>> x1 = np.array([ 4, 4.2, 3.9, 4.3, 4.1])>> x2 = np.array([ 2, 2.1, 2, 2.1, 2.2])>> x3 = np.array([ 0.6, 0.59, 0.58, 0.62, 0.63])>> np.cov([x1, x2, x3])array([[ 0.025  ,  0.0075 ,  0.00175],       [ 0.0075 ,  0.007  ,  0.00135],       [ 0.00175,  0.00135,  0.00043]])
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

3.11 計算特徵值和特徵向量

3.11.1 matlab/octave

[eig_vec, eig_val] = eig(A)
  • 1

3.11.2 python

eig_val, eig_vec = np.linalg.eig(A)
  • 1

3.12 生成高斯分佈的資料集

3.12.1 matlab/octave

mu = [0, 0]cov = [2, 0; 0, 2]X = mvnrnd(mu, cov, 1000)
  • 1
  • 2
  • 3

3.12.2 python

mu = np.array([0, 0])cov = np.array([[2, 0], [0, 2]])X = np.random.multivariate_normal(mu, cov, 100)
  • 1
  • 2
  • 3

四、 numpy中的matrix與array對比

雖然numpy也存在matrix型別,且matrix語法更類似與matlab中的矩陣運算,而numpy中的陣列,進行矩陣運算時,和matlab中的用法存在較大的差異,比如matlab中的*表示矩陣相乘,numpy中的陣列卻表示按位相乘。但絕大多數人仍然推薦numpy中的陣列,因為numpy中的絕大多數函式的返回型別都是numpy.ndarray(),如果堅持使用numpy中的matrix型別的話,需要進行繁瑣的型別轉換(np.mat(A))。