?機器學習代碼心得之迭代器和流水處理
很多機器學習程序涉及從外存的數據讀取以及預處理。常見的例子比如深度的神經網絡,或者是基于外存計算的一些算法如VW還有我很早之前寫過的SVDFeature。在這類問題中,一個常見的優化是采用一個單獨的線程來進行數據的預讀或者預處理,而用另外一個線程進行計算。
現在有這樣一個問題:假設我們可以從硬盤文本格式,jpg以及各種格式來進行讀入,而讀入的實現多種多樣,并且包含一些可能的預處理。但是我們自己又不想每次寫各種多線程代碼,怎么樣才可以抽象出一個通用的模塊解決這一個問題。
這里提供了一個解決方案,迭代器。迭代器(iterator)是設計模式中最常見的模式之一,它特別適用于在線更新的機器學習算法。一般常見的迭代器的接口如下
DataIterator {void Reset(); bool Next(); DataBatch GetBatch();
};</pre>
我們通過Next移動迭代器,GetBatch拿到當前的數據塊,Reset恢復迭代器到數據起始位置。一般一個常見的在線更新算法可以拿到一個迭代器,并且做如下操作
while (iter.Next()) {Update(iter.GetBatch());
}</pre>
對于不同的數據,我們只需要實現各個格式的迭代器即可。這樣對于機器學習的部分不需要關心數據的讀入邏輯,就可以進行統一的計算。迭代器的抽象一大優勢是可嵌套,一個迭代器的實現可以以另外一個迭代器作為輸入。比如我們在讀入圖片的時候要對圖片進行隨機旋轉,那我們可以設計一個隨機旋轉迭代器,每一次Next的時候從輸入迭代器拿到一個圖片,進行隨機旋轉,返回給使用者。大概的代碼如下:
RandomProcIter : publicDataIterator {DataIterator base; DataBatch out; bool Next() { if (!base.Next()) return false; out = rotate(iter.GetBatch()); } DataBatch GetData() { return out; }
} </pre>
這樣的好處是我們可以把預處理代碼和讀入代碼分開,對于不同格式的數據讀入我們都可以嵌一個處理迭代器來進行處理。但是現在的問題是,如何支持采用一個獨立的線程來做預讀。答案很簡單,設計一個線程緩沖迭代器(thread buffer iterator)。
想法如下,設計一個迭代器以其它任意迭代器作為輸入,在這個迭代器內部開啟一個獨立的線程,來主動地把輸入迭代器的數據讀入到內部的一個緩沖里面去。在caller調用Next和GetData的時候,只要直接返回緩沖里面已有的結果就好了。
這樣設計的好處是所有的讀入接口只是迭代器,并沒有多線程的痕跡,而我們也只需要實現一次線程緩沖迭代器的代碼。整個讀入像是一個迭代器的chain,我們可以任意地引入線程緩沖迭代器讓一個單獨的線程來做前面這部分數據的預讀,比如一個可能的迭代器鏈可以是:
JPegIterator->ThreadBuffer->ResizeRotationCrop->ThreadBuffer
這樣的話有一個獨立的線程去預讀取jpeg圖片,采用另外一個線程來進行圖片的旋轉等預處理。比起專門寫多線程代碼來的更加靈活地多。這類處理方式一般稱為流水線(pipeline)
值得一提的是這類思想來源于數據庫領域,在一般數據庫的query execution就是用迭代器的嵌套組合來實現的。
來自:http://weibo.com/p/1001603795714256832384