(課程)基於Spark的機器學習經驗

starzhou發表於2017-05-06

(課程)基於Spark的機器學習經驗
祝威廉 2016-09-07 16:48:36 瀏覽837 評論0

機器學習 spark

摘要: 這篇內容基於我去年的一些感悟寫的,但是今年才在Stuq 的微信群做的分享。從技術角度而言,對Spark的掌握和使用還是顯得很手生的。但是今天一位做資料分析相關的朋友說,受這篇內容影響,他接受了Spark-Shell作為資料分析的工具,簡單幾個命令,輕鬆處理幾千萬行資料。於是我就重新整理了下這篇文章。

Hi,大家好!我是祝威廉,本來微博也想叫祝威廉的,可惜被人佔了,於是改名叫·祝威廉二世。然後總感覺哪裡不對。目前在樂視雲資料部門裡從事實時計算,資料平臺、搜尋和推薦等多個方向。曾從事基礎框架,搜尋研發四年,大資料平臺架構、推薦三年多,個人時間現專注於叢集自動化部署,服務管理,資源自動化排程等方向。
今天會和大家分享三個主題。 不過限於時間,第三個只是會簡單提及下, 等未來有機會可以更詳細的分享。
  1. 如何基於Spark做機器學習(Spark-Shell其實也算的上即席查詢了)
  2. 基於Spark做新詞發現(依託Spark的強大計算能力)
  3. 基於Spark做智慧問答(Spark上的演算法支援)
其中這些內容在我之前寫的一篇描述工作經歷的文章 我的工作都有提及到,當然,可能不如今天分享的這麼詳細。

如何基於spark做機器學習

Spark發展到1.5版本,算是全平臺了,實時批計算,批處理,演算法庫,SQL,hadoop能做的,基本他都能做,而且做的比Hadoop好。
當然,這裡我要提及的是,Spark依然是Hadoop生態圈的一員,他替換的也僅僅是MR的計算模型而已。資源排程依賴於Yarn,儲存則依賴於HDFS,是hadoop生態圈的一顆新星(其實算是老星啦)。
我之前寫文章說,Spark-Shell 是個偉大的創新,加上牛逼的Scala語言,寫spark程式就和寫普通的shell指令碼(或者類似python程式)一樣容易。問題是,原來的shell,python只能在單機工作,現在你寫的每一行程式碼,都被放到了一個幾百臺,幾千臺的規模上去做了。
以前的統計/機器學習依賴於資料抽樣,抽樣從統計的角度來看,如果足夠隨機,其實可以很精準的反應全集的結果,但事實上往往很難做好隨機,所以通常做出來也會很不準。現在大資料解決了這個問題,但不是通過優化抽樣的隨機來解決,而是通過全量資料來解決。
要解決全量的就需要有強大的處理能力,spark首先具備強大的處理能力,其次SparkShell帶來了傳說中的即席查詢。做演算法的工程師,以前經常是在小資料集上跑個單機,然後看效果不錯,一到全量上,就歇菜了,和單機效果很不一樣。雖然是小資料,但是在你的筆記本上跑你幾個小時,也是很正常的。
但是有了spark後,不一樣了,尤其是有了spark-shell。 我後面的兩個例子,都完全是在spark-shell上寫就的。邊寫程式碼,邊執行,邊看結果。我研究的CSDN幾百萬博文的時候,就是直接拿全量資料跑,直接看效果。spark 抽樣也很方便,一個sample函式,你想要多少就多少。
幾十個G的博文資料,count一下 也就十幾秒,cache了之後 幾秒就count完了。所以說,如果docker顛覆了部署,那麼spark-shell 也應該顛覆演算法工程師的日常工作。我現在會和一個百度的朋友比,哇,最近我們spark叢集記憶體有9個T了,對方鄙視的看我一眼:百T的飄過.....
目前Spark已經提供的演算法,我用的最多的是貝葉斯,word2vec,線性迴歸等。所以這裡,作為演算法工程師,或者分析師,一定要學會用spark-shell。

基於Spark做新詞發現

新詞發現是一個非常有意思的領域,用途非常多。譬如可以構建垂直領域詞庫,自動發現新熱門詞彙。詞庫的重要性我不用強調了。基於Spark強大的計算能力,我直接對200萬+的博文進行了分析,得到大概八萬詞,包含中文、英文、中英文混合詞。通過凝固度、自由度、詞頻、idf以及重合子串(比如 c1c2c3..cN c2c3..cN-1 這種形態的,我們認為是重合子串,如果詞頻一樣,則都過濾掉,否則留詞頻高的)五個維度進行閾值設定和過濾。事實上,中間結果可以到幾百億,一個不小心就可以把Spark跑死,但是也在這個過程中慢慢對Spark有了更深的理解。 最終效果還是不錯的,現在它已經作為我們的基礎詞庫了。
基本上就是用spark計算出詞的五個屬性: 凝固度、自由度、詞頻、idf以及重合子串。演算法自然是參考論文的,凝固度、自由度的概念來源於這裡(http://www.matrix67.com/blog/archives/5044) 重合子串能修正一類的問題,但我感觸比較深的是,通常某篇論文只會在一個視角去focus 某件事情,所以你需要參考多篇,從不同角度去理解這件事情的解決方式,最後通過實驗綜合,得到一個更好解決方案。我參考了兩篇論文,比如凝固度,自由度是出自一篇論文,而重合子串則來自另外一篇論文,然後自己觀察實際資料,新增了很多規則,才得到最後的結果。
一說到演算法,大概很多人心裡就是想著,恩,我把資料轉化為演算法需要的格式,然後丟給現成的演算法跑,跑著就出結果,或者出模型,然後反覆嘗試,直到得到你認為能接受的或者最優的結果。我一開始也是這麼想的,可是如果你真的做這件事情,就發現完全不是那樣子啊,需要注意的細節太多了。
新詞發現沒有現成的工具包,所以完全自己寫了。第一步,你要獲取語料。這容易,基於現有的平臺,我從我們資源中心挑出了200萬篇文章id,然後根據id到資料閘道器獲取title,body欄位。這個基於現有的平臺,也就一個SQL + 幾行Scala程式碼就搞定的事情。
SQL 其實就是用Hive 生成一個200萬博文id列表。Scala程式碼也就幾行。因為我們的新詞發現是沒有詞典的,需要列舉所有組合,然後通過一定的規則判定這是不是一個詞。比如 ‘我是天才’,就這四個字, 組合有,‘我是’,‘我是天’,‘我是天才’,‘是天’,‘是天才’,‘天才’ 。你想想,200萬篇文章,這種組合得多誇張,問題是你還要接著給這些組合做計算呢。這個演算法可沒告訴你怎麼處理的,你只能自己去想辦法。看到了,真正你做演算法的過程中,不只是實現,你需要面對的問題特別多,我是怎麼做的呢?
  1. 將所有html標籤替換成空格。
  2. 通過小空格將一個大文字切分成無數小文字塊。
  3. 我們認為一個詞的長度最長不能超過5個字。
  4. 對每個小文字塊再抽取出中文,中英文,英文。
  5. 將一些特殊字元,類似“!¥……()+{}【】的呀啊阿哎吧和與兮呃唄咚咦喏啐喔唷嗬嗯噯你們我他她,這是由於” 這些不可能成詞的字元先去掉。處理的過程中,你可能需要寫中文,英文,中英文的抽取方法。
通過上面的五個處理,你計算規模會小非常多。如果不這樣處理,估計再大記憶體都能讓你歇菜。接著就是按論文裡的規則做計算了,比如算詞的凝固度,算重合子串。這裡面還會遇到很多效能,或者記憶體的坑,比如Spark裡的groupByKey,reduceByKey。 我一開始省事,用了groupByKey,歇菜了,記憶體直接爆了,為啥,你要去研究groupByKey到底是怎麼實現的,一個詞出現幾十萬次,幾百萬次都很正常啊,groupByKey受不了這種情況。所以你得用reduceByKey。
在spark 1.5裡,已經支援動態調整worker數目了。我之前做這個的時候,會開的比較大,如果叢集規模比較小,可能會影響別人,而且用完要趕緊釋放,但釋放了重新再起,也還是很麻煩的,現在好很多了。
很好,實現了演算法後得到了結果,可人家沒告訴你,他貼出來的結果都是好看的,那是因為他是按頻次排的,但如果你拉到最後看,結果就不太好看了。這個時候你就需要觀察資料了,然後提出新的規則,比如最後得到的中文詞結果,我用了一些簡單規則過濾下,都是哪些呢?凡是詞裡面包含‘或’的,或者'就'的或者上面羅列的,我都認為這個詞是沒有意義的,經過這個簡單規則一過濾,效果好非常多,很多沒什麼意義的生活詞,或者不成詞的詞就被去掉了。中文,英文,中英文混合,我都加了很多這種規則,最終才過濾出了八萬計算機詞彙。
我在做上面的方案時,基本上就是在spark-shell中完成的。其實有點像ngram,就是對所有字串做所有列舉,只是會限制最終成詞的長度。我這裡中文是最長五個字,英文是四個字,中英文一塊的 是五個字,接著要算出每個詞左右連線字。具體的演算法大家可以參考http://www.matrix67.com/blog/archives/5044 這篇文章。而且如果有spark環境的,也可以嘗試自己實現一把。
重合子串,是這個演算法的一個比較大的問題,比如 c1c2c3...cN c2c3...cN-1,因為是從統計的方案做的,c1c2c3…cN c2c3...cN-1 他們兩算出來的分數可能就是一樣的,所以如果我們發現他們的分值或者出現頻率是一樣的,就可以直接排除掉了。

基於Spark做智慧問答

其實我做的智慧問答算不上智慧問答,但是內部一開始這麼叫的,所以也就這麼順帶叫下來了。 其實做的事情非常簡單:
比較兩個標題的相似度
如果我們能知道兩個句子說的其實是一件事情,那麼就能打通各產品的互通鴻溝了。之前試水的專案是打通問答到部落格的通道。具體效果大家可以看看CSDN的問答產品,裡面的機器人,背後用的演算法就是這套。當使用者問一個問題,機器人就會到部落格裡去找有沒有這個問題的答案,或者有沒有可以做參考的。 比較神奇的是,之前有個在問答活躍的人也特別喜歡貼部落格連結作為回答,我們對比了機器人和他的結果,發現機器人和他貼的差不多。
對於擁有內容的網站來說,這個技術還是非常重要的,比如CSDN,有論壇,部落格,資訊,雜誌等等,都是內容的載體。使用者在問答頻道里問的一個問題,其實在部落格,在論壇早就已經有答案了。具體做法是透過word2vec解決一意多詞的問題。接著將詞轉換為句子向量。這樣任何一個問題都可以轉換為一個向量。同理任何一篇博文的標題也可以轉化為一個向量。
word2vec,採用的資料來源,我是用的搜尋引擎的資料。大部分內容類的網站,他的PV應該有相當一部分來自搜尋引擎,其實搜尋引擎對這些網站來說,就是一個大的寶藏。因為搜尋的query串,都是使用者遇到的問題,然後指向到解決這些問題的內容上。內容上,所以我直接拿使用者的query作為word2vec的語料,得到一些常用的提問詞,每個詞用一個50維度的向量表示。當然,我們不可能真的讓一個問題和幾百萬內容直接做比較,一個簡單有效的方式是,先通過搜尋引擎去搜,然後將搜尋得到top100結果做向量計算得到新的得分。 基本相似度大於0.9 的可以算作答案。大於0.7的就可以作為參考答案了。站內搜尋服務應該是標配了,所以對大部分網站應該不是問題。
對了,這裡有個問題是:word2vec計算出來的是用一個稠密的定長向量表示詞,我的做法是直接把一個句子的裡的詞的向量按位做加法,重新得到一個新的向量作為句子的向量。當然,這種方式也是有缺陷,也就是句子越長,資訊損耗越大。但是做這種標題性質的相似度,效果出奇的好,那種句子裡很多詞彙不相同的,它都能算出他們很相似來,這是因為word2vec可以算出不同詞彙之間關係。
好了,具體的內容就分享到這裡。

總結

下面是我的幾個觀點:
  1. 作為資料分析師,演算法工程師,請好好利用spark-shell。 Spark社群為了滿足資料分析師,演算法工程師,其實也做了非常多的工作,包括Python, R語言的支援。15年社群努力做的DataFrame其實就是從R裡借鑑過來的,也方便R資料科學家方便的遷移過來。我覺得大家都應該與時俱進,不要只玩單機了。
  2. 機器學習平臺的構建,可以參考我這篇文章從內容/使用者畫像到如何做演算法研發裡面有我對平臺方面一些看法。

課程Q&A

Q: 如何從0開始系統學習spark,最後轉行?
A: 學會scala就行,scala是一門具有學院派氣息的語言,你可以把它寫的像python,ruby那樣,也可以寫的想java那樣方方正正,也可以學習python,spark支援python但是可能有些功能用不了,用了一天的時間把Scala的官方教程看了,基本就能上手了。
Q:建議不做RAID的原因是什麼?A: 比如我例子提到的預設HDFS的所有資料都會存三份,可以保證資料位於不同的伺服器上,不同的磁碟上,所以無需RAID。
Q:很多沒什麼意義的生活詞,或者不成詞的詞,這些詞是怎樣得到的?也是分析出來的? A: 因為用的都是統計的一些方式,所以肯定會有很多無意義的詞彙,假設我們現在得到的詞彙幾何是
A,接著我去爬了一些新聞和生活的類的部落格,然後用程式去跑一遍得到一批詞彙B,然後A-B 就能得到一拼更純正的計算機詞彙。
Q:記憶體要調到多大才能不會爆掉?是不是有什麼比例?
A: 你不管調到多大,如果用的不好 也都有可能,groupByKey這個會有很大的記憶體問題,他形成的結構式 key-> value1,value2,value3…...valuen,這種是非常消耗儲存空間的額,大家使用spark的時候,序列化最好使用kyro,效能確實好太多,一個worker 會同時配置可以使用的記憶體和cpu,這個時候一定要搭配好。比如你允許work使用5個cpu,那記憶體最好能配到10G,如果記憶體過小,你的cpu會大量浪費在GC上,我一般是單個worker 12G記憶體 ,可使用4核。
Q:直接把一個句子的裡的詞的向量按位做加法,這是如何加?能舉個例子不?
A:比如 考慮一個三維向量: A[1,3,5] B[1,3,7],現在有個句子 是AB兩個片語成,則對應的向量為A+B=[2,6,12]
Q:還有中文分詞是用的什麼方法?可否分享程式碼不啊?
A:這裡是無監督分詞,所以不用中文分詞,按維度疊加,才能保證都是相同長度的向量,而且中文分詞這塊,我推薦我一個同事的 ansj分詞,還是做的不錯的。
Q:一些分詞方法具有新詞發現的功能,比如crf,樓主是比較過效果麼?而且我記得matrix67這個演算法複雜度還是很高的?
A:matrix67 這個演算法複雜度還是非常高的,你實際操作就會發現計算量,記憶體使用量都很大,crf等據我所知,還都是需要依賴詞表的,matrix67的這個方式,完全不需要任何先驗的東西。
Q:為什麼一個詞要用50維度表示? 這能舉個例子不? 這裡不太明白。
A:理論上維度越長越好,我當時是隨意試了一個值。發現效果其實已經可以了,這是一個可以調整的值,比如你可以分別生成50,150,300維度的,然後試試那個效果好。

相關文章