MaxCompute Mars開發指南

Mars 算法實踐

人臉識別
Mars 是一個基於矩陣的統一分佈式計算框架 ,而且 Mars 已經在 GitHub 中開源。當你看完 Mars 的介紹可能會問它能做什麼,這幾乎取決於你想做什麼,因爲 Mars 作爲底層運算庫,實現了 numpy 70% 的常用接口。這篇文章將會介紹如何使用 Mars 完成你想做的事情。

奇異值分解 (SVD)
在處理紛繁的數據時,作爲數據處理者,首先想到的就是降維,SVD 就是其中一種比較常見的降維方法,在 numpy.linalg 模塊中就有 svd 方法,當我們有20000個100維的數據需要處理,調用 SVD 接口:

In [1]: import numpy as np
In [2]: a = np.random.rand(20000, 100)
In [3]: %time U, s, V = np.linalg.svd(a)
CPU times: user 4min 3s, sys: 10.2 s, total: 4min 13s
Wall time: 1min 18s
可以看到即使 Numpy 使用了 mkl 加速,也需要1分多鐘的運行時間,當數據量更大時,單機的內存已經無法處理。
Mars 也實現了 SVD ,但是它比 Numpy 有更快的速度,因爲利用矩陣分塊計算的算法,能夠並行計算:

In [1]: import mars.tensor as mt
In [2]: a = mt.random.rand(20000, 100, chunk_size=100)
In [3]: %time U, s, V = mt.linalg.svd(a).execute()
CPU times: user 5.42 s, sys: 1.49 s, total: 6.91 s
Wall time: 1.87 s
可以看到在相同數據量情況下,Mars 有幾十倍速度的提升,僅僅需要1秒多鍾就可以解決20000數據量的降維問題。想象一下淘寶用戶數據做矩陣分解時,分佈式的矩陣運算就顯現出其價值。

主成分分析 (PCA)
提到降維,主成分分析也是一種重要的手段。PCA 會選取包含信息量最多的方向對數據進行投影,其投影方向可以從最大化方差或者最小化投影誤差兩個角度理解。也就是通過低維表徵的向量和特徵向量矩陣,可以基本重構出所對應的原始高維向量。其最主要的公式如下所示:
maxμj1nn∑i(xiμj−¯¯¯x)T(xiμj−¯¯¯x)=μjTCμjmaxμj⁡1n∑in(xiμj−x¯)T(xiμj−x¯)=μjTCμj
xixi爲每個樣本的數據,μjμj爲新的投影方向,我們的目標就是使得投影方差最大化,從而找到主特徵。上面式子中的矩陣CC在數學中可以用協方差矩陣表示,當然首先要對輸入的樣本做中心化調整。我們可以用隨機產生的數組看一下 Numpy 是如何實現 PCA 降維操作:

import numpy as np
a = np.random.randint(0, 256, size=(10000, 100))
a_mean = a.mean(axis=1, keepdims=True)
a_new = a - a_mean
cov_a = (a_new.dot(a_new.T)) / (a.shape[1] - 1)
#利用SVD求協方差矩陣前20個特徵值
U, s, V = np.linalg.svd(cov_a)
V = V.T
vecs = V[:, :20]
#用低緯度的特徵向量表示原數據
a_transformed = a.dot(vecs)
由於隨機產生的數據本身就沒有太強的特徵,所以在100維數據中象徵性的取出前20維,一般可以用特徵值的比例取總和的前99%之類的數值。
再看一下 Mars 是如何實現的:

import mars.tensor as mt
a = mt.random.randint(0, 256, size=(10000, 100))
a_mean = a.mean(axis=1, keepdims=True)
a_new = a - a_mean
cov_a = (a_new.dot(a_new.T)) / (a.shape[1] - 1)
#利用SVD求協方差矩陣前20個特徵值
U, s, V = mt.linalg.svd(cov_a)
V = V.T
vecs = V[:, :20]
#用低緯度的特徵向量表示原數據
a_transformed = a.dot(vecs).execute()
可以看到除了 import 的不同,再者就是對最後需要數據的變量調用 execute 方法,甚至在未來我們做完 eager 模式後, execute 都可以省去,以前用 Numpy 寫的算法可以幾乎無縫轉化成多進程以及分佈式的程序,再也不用自己手動去寫MapReduce。

人臉識別
當 Mars 實現了基礎算法時,便可以使用到實際的算法場景中。PCA最著名的應用就是人臉特徵提取以及人臉識別,單個人臉圖片的維度很大,分類器很難處理,早起比較知名的人臉識別 Eigenface 算法就是採用PCA算法。本文以一個簡單的人臉識別程序作爲例子,看看 Mars 是如何實現該算法的。
本文的人臉數據庫用的是ORL face database,有40個不同的人共400張人臉圖片,每張圖片爲 92112 像素的灰度圖片。這裏選取每組圖片的第一張人臉圖片作爲測試圖片,其餘九張圖片作爲訓練集。
首先利用 python 的 OpenCV 的庫將所有圖片讀取成一個大矩陣,也就是 36010304大小的矩陣,每一行是每個人臉的灰度值,一共有360張訓練樣本。利用 PCA 訓練數據,data_mat 就是輸入的矩陣,k 是需要保留的維度。

import mars.tensor as mt
from mars.session import new_session
session = new_session()
def cov(x):
x_new = x - x.mean(axis=1, keepdims=True)
return x_new.dot(x_new.T) / (x_new.shape[1] - 1)
def pca_compress(data_mat, k):
data_mean = mt.mean(data_mat, axis=0, keepdims=True)
data_new = data_mat - data_mean
cov_data = cov(data_new)
U, s, V = mt.linalg.svd(cov_data)
V = V.T
vecs = V[:, :k]
data_transformed = vecs.T.dot(data_new)
return session.run(data_transformed, data_mean, vecs)
由於後續做預測識別,所以除了轉化成低維度的數據,還需要返回平均值以及低維度空間向量。可以看到中間過程平均臉的樣子,前幾年比較火的各地的平均臉就可以通過這種方式獲取,當然這裏的維度以及樣本比較少,大概只能看出個人臉的樣子。
MaxCompute Mars開發指南

其實 data_transformed 中保存的特徵臉按照像素排列之後也能看出特徵臉的形狀。圖中有15個特徵臉,足以用來做一個人臉分類器。
MaxCompute Mars開發指南

另外在函數 PCA 中用了 session.run 這個函數,這是由於三個需要返回的結果並不是相互獨立的,目前的延遲執行模式下提交三次運算會增加運算量,同一次提交則不會,當然立即執行模式以及運算過的部分圖的剪枝工作我們也在進行中。
當訓練完成之後,就可以利用降維後的數據做人臉識別了。將之前非訓練樣本的圖片輸入,轉化成降維後的維度表示,在這裏我們就用簡單的歐式距離判斷與之前訓練樣本中每個人臉數據的差距,距離最小的就是識別出的人臉,當然也可以設置某個閾值,最小值超過閾值的判斷爲識別失敗。最終在這個數據集下跑出來的準確率爲 92.5%,意味着一個簡單的人臉識別算法搭建完成。

計算歐氏距離

def compare(vec1, vec2):
distance = mt.dot(vec1, vec2) / (mt.linalg.norm(vec1) * mt.linalg.norm(vec2))
return distance.execute()

未來
上文展示瞭如何利用 Mars 一步一步地完成人臉識別小算法的過程,可以看到 Mars 類 Numpy 的接口對算法開發人員十分友好,算法規模超出單機能力時,不再需要關注如果擴展到分佈式環境,Mars 幫你處理背後所有的並行邏輯。
當然,Mars 還有很多可以改進的地方,比如在 PCA 中對協方差矩陣的分解,可以用特徵值、特徵向量計算,計算量會遠小於 SVD 方法,不過目前線性代數模塊還沒有實現計算特徵向量的方法,這些特性我們會一步步完善,包括 SciPy 裏各種上層算法接口的實現。大家有需求的可以在 GitHub 上提 issue 或者幫助我們共建 Mars。
Mars 作爲一個剛剛開源的項目,十分歡迎提出其他任何想法與建議,我們需要大家的加入,讓 Mars 越來越好。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章