前文
在《利用Hadoop實現超大矩陣相乘之我見(一)》中我們所介紹的方法有着“計算過程中文件占用存儲空間大”這個缺陷,本文中我們着重解決這個問題。
矩陣相乘計算思想
傳統的矩陣相乘方法為行、列相乘的方式,即利用左矩陣的一行乘以右矩陣的一列。不過該方法針對稀疏矩陣相乘,會造成過多的無效計算,降低計算效率。為了解決這個問題,本發明采用列、行相乘計算方式,即利用左矩陣的一列中的元素與右矩陣對應行中的所有元素依次相乘,該方法有效避免了稀疏矩陣相乘過程中產生的無效計算。具體計算過程示意圖如圖1所示。
圖1 列、行矩陣相乘計算示意圖
數據預處理
為了便於Map-Reduce模型對矩陣元素進行處理,所有的矩陣元素都存儲在文本文件中,一行記錄代表一個矩陣元素,針對稀疏矩陣,0元素不納入輸入文本。如圖2所示。
圖2 輸入的矩陣元素
圖3 預處理后的矩陣元素
我們對圖2進行舉例說明,假如一行記錄為:。則其代表左矩陣第一行第二列的元素值為
。
在一個Map過程中,我們對每一行輸入數據進行預處理,若一行記錄代表左矩陣元素,則提取列號作為Key值,剩余信息組成Value值;若一行記錄代表右矩陣元素,則提取行號作為Key值,剩余信息組成Value值,如圖3所示。之所以這樣做,是為了下一步在Reduce過程中能夠按照Key值統計左矩陣第Key列極其對應右矩陣第Key行中的元素。
統計與分段
當矩陣規模大到一定程度時,內存可能會碰到加載不了左矩陣的一列或右矩陣的一行元素的問題。為了提高矩陣相乘運算的可擴展性,本發明提出了對左矩陣元素按列進行分段,對右矩陣元素按行進行分段的方法,這樣,單個計算節點就可以加載左矩陣的一段與右矩陣的一段至內存進行相乘運算,突破了內存的限制。分段相乘示意圖如圖4所示。
圖4 矩陣分段相乘示意圖
接下來我們結合圖4來說明Reduce階段如何來完成統計與分段工作。Reduce階段首先將所有Key相同的Value集合在一起,形成一個Value-List。若Key為k,那么Value-List則代表了左矩陣第k列與右矩陣第k行的所有元素,這些元素時混合在一起的。在Reduce階段,我們第一輪遍歷Value-List,獲得左矩陣第k列的元素個數為Mk,右矩陣第k行的元素個數為Nk。接下來我們通過第二輪遍歷對左矩陣第k列、右矩陣第k行的元素進行分段操作,假設每個分段包含w個元素,則左矩陣第k列被分為段,右矩陣被分為段。
圖5 分布式緩存存儲矩陣分段信息
本發明將L矩陣中第k列中第i個分段表示成如下格式:
代表該分段在接下來的過程中總共需要
個拷貝,element_list表示該分段中的元素集合。
同理,R矩陣中第k行中第j個分段表示成如下格式:
為了便於后續Map-Reduce過程的處理,我們將每一個分段都存儲在磁盤文件中,文件中的一樣代表一個分段。同時,我們將兩個矩陣中的具體分段信息存儲在分布式緩存中,有利於解決后續步驟中不同節點間的通信與數據查詢問題。具體存儲格式如圖5所示。
圖5中代表矩陣L中第1列的元素個數為M1,每個分段的元素個數為w,所以對該列的分段數目為
;同理
表示矩陣R中第1行的元素個數為N1,每個分段的元素個數為w,所以對該行的分段數目為
。
拷貝任務分發——Map迭代算法
l Map迭代算法
如圖4所示,我們需要將兩個矩陣中的分段一一對應相乘。我們做如下舉例:由於矩陣L中的第k列中的第i段需要與矩陣R中第k行中的所有段依次相乘,所以需要將L中第k列第i段的內容拷貝份;同理,R中第k行中的每一個分段需要拷貝
份。當然,拷貝工作是通過Map-Reduce來完成的,現在的問題是,若兩個矩陣中每一個分段需要拷貝的數量都很大,則一個Map對每行記錄都需要執行好多遍拷貝工作,大大延長了Map執行的時間,同時,可能使得很多計算節點沒有參與運算。
圖6 Map迭代拷貝任務分發算法
為了解決上述問題,本發明提出了“Map迭代拷貝任務分發算法”來對每條記錄(每個分段)的拷貝任務進行分發,這樣有效的控制每個節點對每個分段的拷貝數量,同時更有效的使得更多的節點參與拷貝運算工作。
為了便於每條數據知道自己需要拷貝的分數,我們對公式(1)、(2)進行簡單的修改:
式(3)中代表該記錄(分段)需要拷貝份,拷貝標識號為1至
;同理解釋式(4)。
這里,我們結合圖7進行舉例說明,假設…1#10000…是式(3)或式(4)的縮寫形式,代表一條記錄需要拷貝10000份,同時假設所有分段需拷貝的份數都是10000,那么初始時,將有N個節點參與拷貝工作。為了使得更多的計算節點參與拷貝工作,我們設計了此Map迭代拷貝任務分發算法。假設分發擴展率為10,則經過一次迭代后,文件大小擴大了約10倍,則大約有10*N個計算節點將參與拷貝工作,依次類推,三次迭代后,約有1000*N個計算節點參與拷貝工作,當有1000*N個節點參與拷貝工作時,每條記錄被拷貝的最大份數為10,如圖7所示。
l 迭代次數控制
在現實大矩陣相乘中,由於大部分情況下矩陣都為稀疏矩陣,那么每行每列包含的元素個數就不一樣,所以每個分段需拷貝的份數都不確定。這樣我們就需要計算Map迭代過程的迭代次數,依次來控制Map迭代的過程。在此,我們利用圖5所示存儲在分布式緩存中的各個分段信息來得到最大分段數目,同時結合分發擴展率n,利用公式(5)來計算Map迭代的次數,依次來控制Map迭代過程。
最后計算模塊
完成記錄的拷貝工作后,我們還需要兩輪Map-Reduce過程完成矩陣的運算。
l 第一輪Map-Reduce——分段拷貝與對應
在此輪中,我們首先在Map階段完成分發到的拷貝任務,若圖(7)中的….2191#2200…格式符合式(3),則其原本的形態為:
在Map中執行拷貝工作后記錄樣式為:
k−i−2191 element_list
k−i−2192 element_list
......
k−i−2199 element_list
k−i−2200 element_list
若圖(7)中的….2191#2200…格式符合式(3),則其原本的形態為:
在Map中執行拷貝工作后記錄樣式為:
k−2191-j element_list
k−2192-j element_list
......
k−2199-j element_list
k−2200-j element_list
經過此輪Map階段,每個key(拷貝后每條記錄的前半部分)對應兩個Value,也就是L矩陣中的一個段與R矩陣中的一個段,同一個key的兩個Value將在該輪的Reduce階段進行匯合,匯合后如下所示:
代表L矩陣第k列第i個分段,
代表R矩陣第k行第j個分段,然后在下一輪Map-Reduce進行如圖(4)所示的兩個段的相乘工作。
l 第二輪Map-Reduce——相乘並匯總
Map階段對每條記錄進行相乘運算,即將L中每個元素依次與R中每個元素相乘,若element_list_(L,k,i)中某個元素Li,k與element_list_(R,k,j)中某個元素Rk,j相乘,則結果記錄成“i-j value”格式。 然后每個Map結束后執行combine操作,combine操作與該輪Reduce操作一樣,執行相同key的value相加,便得到了最終的矩陣運算結果。
推薦一個自己業余時間開發的網盤搜索引擎,360盤搜(www.360panso.com)
某研究開發中心雲計算組 小周