基於Spark Tensorflow使用CNN處理NLP嘗試
作者:祝威廉
前言
關於CNN如何和NLP結合,其實是被這篇文章指導入門的 。 我覺得使用CNN去處理一些NLP的分類問題,是非常不錯的。
主要好處有:
CNN能自動抽取出一些高級特徵,減少了特徵工程的時間
使用WordEmbedding技術將辭彙表達為向量後,可以很方便的將文本表示為類似圖片的2D向量
神經網路表達能力強
缺點的話,就是目前我還沒想到如何把一些非文本類的因子給融合進去。就是有時候我是希望能夠做些特徵工程,抽取出一些因子,然後加入到模型裡面的。
數據預處理階段
先簡單解釋下流程,首先是對所有文本先分詞,我們採用Ansj分詞工具,然後通過Spark 的Word2vec 來訓練得到詞向量。Zepplin是一個很好的工具,方便演算法工程師做預處理,我們給力的運維同學還把tensorflow也集成進了zepplin,方便我們使用。
比如要做分詞也很簡答,
%sparkimport org.ansj.splitWord.analysis.NlpAnalysisimport scala.collection.JavaConversions._import scala.collection.mutableimport org.apache.spark.sql.functions.udf//註冊一個分詞函數spark.udf.register("parse",(co:String)=>{ val parseMethod = Class.forName("org.ansj.splitWord.analysis.NlpAnalysis").getMethod("parse", classOf[String]) val tmp = parseMethod.invoke(null, co) val terms = tmp.getClass.getMethod("getTerms").invoke(tmp).asInstanceOf[java.util.List[Any]] terms.map(f => f.asInstanceOf[ ].getName).toArray })//獲取數據spark.sql("select parse(content[1]) as keywords,id from table where size(content) > 2").write.options(Map("format"->"csv")).save("/tmp/words_anlysis") spark.read.csv("/tmp/words_anlysis").show(10)
這樣就得到了所有分好詞的文本。
接著使用word2vec來訓練:
值得注意的是,這些都是在zepplin完成的,你也可以寫個spark程序來完成。
使用CNN卷積做分類
詳細Tensorflow的代碼我已經貼到gist上了: nlp-cnn.py 。我Python也才剛學沒一會,寫的時候也是不斷的到google里去問,為了能夠先run起來,我把訓練數據全部載入到內存。最好還是應該採用部分預載入的方式,或者使用tensorflow queue的機制來喂數據,否則數據量大了,內存就不夠用了。
關於CNN現在文章已經多的不要不要了,大家先參考其他文章學個大概,我這裡主要介紹一些我在實際操作中遇到的一些問題。
首先定義輸入輸出:
VOCAB_SIZE = 100NUM_CLASSES = 2SEQUENCE_LENGTH = 59input_x = tf.placeholder(tf.float32, [None, SEQUENCE_LENGTH, VOCAB_SIZE, 1], name="input_x")input_y = tf.placeholder(tf.float32, [None, NUM_CLASSES], name="input_y")
我們以輸入為例,我們需要構建的一個數組應該是這樣的:
[//某次迭代需要的文檔數量 [ //某個文檔的長度 SEQUENCE_LENGTH [ // 詞向量的長度 VOCAB_SIZE [//輸入通道,為1], .... ], .... ] ]
數組的第一層對應 None,也就是說不確定最外層數組的大小。第二層的大小對應SEQUENCE_LENGTH,也就文檔的詞長度,第三層對應詞向量,也就是100,最後一層對應輸入通道,圖片是RGB 那麼就是3通道,我們這裡是1。 所以構建數據格式也是我一開始疑惑的一個地方,如何構建一個適合 CNN輸入的數據格式。
第二個問題就是卷積網路的構建,我們分析下具體的代碼,裡面還要做一些計算:
首先,VOCAB_WINDOW, width 定了了你的卷積框的大小。其次,size_in其實指的是我們前面說的文本矩陣的通道數,這裡是1,如果是彩圖那麼是3,如果是灰度圖是1。我們也可以通過不同的方式對同一端文本構建新的矩陣,那麼就可以設置為多個通道。
size_out 則是你任意指定的,主要定義你想捕捉到多少個特徵,一個特徵對應一個卷積後的二維向量。形象上說,就是我掃描原來的那張二維向量多少次,我這裡第一次卷積操作設置為64,第二個卷積操作設置為128,也就是我第一次掃描輸入的圖片64次,得到64個新的圖,第二次又對新的圖(這64個新圖會被第二次掃描器看成一張新圖),掃描128次,得到128個新圖。
b 為啥是一個size_out大小的一維張量呢?我們說CNN會闡述共享,就是一次卷積操作,也就是把圖片掃描一遍,會共用一組參數。
下面一段代碼設計到了很多數字,這些數字都是計算出來的。
我們知道,初始輸入是 59X100,這個是原始的文本矩陣大小。 經過了兩次卷積,兩次max-pool,那麼最後把卷積輸出的向量張開成一維的的大小後是51*128 的長度。那麼這個值是怎麼計算出來的。
卷積和池化的stride 都是1,然後第一次卷積的框大小是: 3X100。 因為padding採用了VALID, 計算卷積後的向量大小是通過如下公式完成的:
? 若padding=VALID,output_size = (input_size - filter_size + stride) / stride; ? 若padding=SAME,output_size = (input_size + stride - 1) / stride;
所以第一次卷積後高變成了 (59-3+1)/1 = 57 ,寬變成了1。 接著再進行一次大小為 3X1的池化操作,按相同的公式計算,變成了 55X1。 接著再進行一次卷積,一次池化,變成了 51X1。 所以每個通道都是一個51維的向量。第二次卷積,我們一共有128個通道,也就是經過前面的運算,我們一共產出了 128個51X1的向量,然後把這些向量都拼接起來,長度就是 51 * 128了。
fc層就是一個全連接的網路了,沒啥可說的。這裡還有一個問題,有時候我們希望能夠把最後產生的那51個128維的向量給提取出來,因為這些向量是CNN對某個內容做完分析後抽取出來的特徵。一開始我沒啥思路,後面想想,其實tensorflow里conv不過是一個op,是op就代表是可以運行的,所以通過如下代碼,就可以把每輪迭代的向量輸出了:
[train_accuracy, s, train_conv_out] = sess.run([accurate, summ, conv_out], feed_dict=) print(train_conv_out)
總結


TAG:36大數據 |
※Django Channel處理Websocket鏈接
※Servlet Cookie 處理
※Google愛上Intel+AMD合體處理器:Chromebook要用它
※CEVA和mPerpetuo合作為CEVA視覺處理器提供Halide語言支持
※Magic Leap One頭顯採用NVIDIA TX2處理器
※下一代伺服器處理器Cascade Lake-SP將支持3DXPoint DIMM
※原Movidius CEO Remi El-Ouazzane:深度了解終端視覺處理器VPU
※Pytorch 中如何處理 RNN 輸入變長序列 padding
※Universal Laser Systems(R)的ULTRA平台系列擴展了材料處理的激光器系統組合
※Python數據處理實戰——使用Scikit-Learn進行多類文本分類
※Jdk 動態代理異常處理分析,UndeclaredThrowableException
※流式處理:使用 Apache Kafka的Streams API 實現 Rabobank 的實時財務告警
※三款照片處理軟體橫評:Lightroom、CaptureOne、AfterShot
※Intel Ice Lake平台Xeon處理器細節曝光
※LifeSignals推出與3M和意法半導體聯合開發的Life Signal?系列處理器
※Oculus Santa Cruz或採用高通Snapdragon 845處理器
※Docker、TensorFlow目標檢測API和OpenCV實現目標檢測和視頻處理
※Intel宣布退役Kaby Lake-X處理器
※JSP Cookie 處理
※ZUUL 處理 gerrit patch-set 的流程