天然語言處理和圖像處理不一樣,做爲人類抽象出來的高級表達形式,它和圖像、聲音不一樣,圖像和聲音十分直覺,好比圖像的像素的顏色表達能夠直接量化成數字輸入到神經網絡中,固然若是是通過壓縮的格式jpeg等必須還要通過一個解碼的過程才能變成像素的高階矩陣的形式,而天然語言則不一樣,天然語言和數字之間沒有那麼直接的相關關係,也就不是那麼容易做爲特徵輸入到神經網絡中去了,因此,用神經網絡處理天然語言,不可避免的在數據預處理方面更加繁瑣,也更加細緻!天然語言處理的另一個不一樣之處在於語言之間的相關關係,舉一個最簡單的例子,在作智能助理機器人的時候,一句「我將在上午十點到達北京」和「我將在上午十點離開北京」 若是你只考慮每一個獨立的詞彙的話,那麼「北京」究竟是做爲始發地仍是目的地是不得而知的,也就是說你必須得聯繫上下文才可以更好的理解整個句子的意思!這也是天然語言的特別之處!固然針對語言的這種特性,也有相應的用於處理的網絡——RNN(recurrent neural network)循環神經網絡!編程
做爲分類到Tensorflow編程實戰裏的一篇博客,固然以解釋代碼爲主,具體的理論部分這裏不過多解釋,開門見山,本次處理的數據集是PTB數據集,它是目前語言模型學習中使用的最爲普遍的數據集,PTB數據集的下載地址是: http://www.fit.vutbr.cz/~imikolov/rnnlm/simple-examples.tgz 網絡
將數據集解壓以後將會獲得好幾個文件夾,重點看/data文件夾下的三個文件,ptb.test.txt, ptb.train.txt, ptb.valid.txt 這三個數據文件已經通過了預處理,相鄰的單詞之間用空格隔開,以ptb.train.txt爲例,看一下它裏面的具體內容:ide
no it was n't black monday but while the new york stock exchange did n't fall apart friday as the dow jones industrial average plunged N points most of it in the final hour it barely managed to stay this side of chaos 學習
some circuit breakers installed after the october N crash failed their first test traders say unable to cool the selling panic in both stocks and futures
ui
the N stock specialist firms on the big board floor this
數據集中包含了9998個詞彙,加上稀有詞語的特殊符號 <unk>和語句的結束標記,一共是10000個! 爲了可以將數據輸入到神經網絡,咱們首先須要作的就是這10000個詞彙按照其出現的頻率進行編號,造成詞彙表,而後按照詞彙表的對應關係,將train/test/valid數據集分別轉換成編號的形式! 編碼
按照單詞出現的頻率進行編號的代碼以下:spa
1 import codecs 2 import collections 3 from operator import itemgetter 4 5 RAW_DATA = "C:\\Users\\Yang\\Desktop\\nlp\\ptb.train.txt" #數據的輸入路徑/ 6 VOCAB_OUTPUT ="ptb.vocab" #詞彙表的輸出路徑 7 8 counter = collections.Counter() #counter 顧名思義是一個計數器 9 10 with codecs.open(RAW_DATA,"r","utf-8") as f: 以read的方式,utf-8編碼的形式打開上述路徑 11 for line in f: #讀行 12 for word in line.strip().split(): #以空格做爲劃分,將文件裏面的每個詞彙都切開! strip()默認用於去掉收尾的空格和換行符 13 counter[word] += 1 #統計單詞出現的次數 14 15 sorted_word_to_cnt = sorted(counter.items(),key=itemgetter(1),reverse=True) 按照itemgetter(1)屬性進行排序,應該就是按照單詞出現的次數排序,例如:and:981 16 sorted_words = [x[0] for x in sorted_word_to_cnt] #之因此取x[0]是由於格式是 the:356 這種形式,x[0]就是爲了將單詞取出來 排好序的word 17 18 sorted_words = ["<eos>"] + sorted_words #將句子的結束符添加到排好序的list中 19 #由於PTB數據中已經將低頻詞彙替換成了<"unk">因此下面這步不必 <"eos">是句子結束符 <"sos">是句子開始符 <"unk">是句子的低頻詞彙 20 #sorted_words = ["<unk>","<sos>","<eos>"] + sorted_words 21 #if len(sorted_words) >10000: 22 #sorted_words = sorted_words[:10000] 標紅的這三句不必出現! 23 24 with codecs.open(VOCAB_OUTPUT,"w",'utf-8') as file_output : 25 26 for word in sorted_words: 27 file_output.write(word + "\n") #將排序好的詞彙再寫回輸出的文件目錄裏面,那麼就算完成了詞彙對應表的構建
寫完了詞彙表的構建代碼以後,咱們還須要一個將ptb.train/test/valid.txt文件轉換成對應的編號的過程! 每個詞彙對應的編號就是其在詞彙表裏面對應的行號! 注意到以前每個word的輸出後面都跟着"\n"3d
下面就來實現將單詞轉換成對應的編號的部分的代碼:code
1 import codecs 2 import sys 3 4 RAW_DATA = 'C:\\Users\\Yang\\Desktop\\nlp\\data\\ptb.valid.txt' #這裏是待轉換的文件的目錄/相應的改爲ptb.test.txt/ptb.train.txt能夠用於其餘的轉換 5 VOCAB = 'ptb.vocab' #詞彙表的目錄 6 OUTPUT_DATA = 'ptb.valid' 用於輸出的目錄 7 8 9 with codecs.open(VOCAB,'r','utf-8') as f_vocab: #首先就是將詞彙表以read和utf-8編碼的方式打開 10 vocab = [w.strip() for w in f_vocab.readlines()] #我怎麼感受這裏只是讀到了一行而後進行收尾空格換行符去掉的處理??? 11 #哦,我明白了 由於VOCAB中詞彙的存儲格式就是一個單詞佔一行,因此出來的就是一個個單詞而不須要.split() 12 word_to_id = {k:v for (k,v) in zip(vocab,range(len(vocab)))} #轉換成了單詞序號的字典形式 這裏是完成了詞彙和其對應編號的對應! 13 14 #將詞彙轉換成了對應的行號序號 15 def get_id(word): 16 return word_to_id[word] if word in word_to_id else word_to_id["<unk>"] #若是是在詞彙表裏面的就將它轉換成對應的編號,不然的話就轉換成unk對應的編號 17 18 fin = codecs.open(RAW_DATA,"r","utf-8") #打開待轉換文件 19 fout = codecs.open(OUTPUT_DATA,"w",'utf-8') #輸出文件目錄 20 21 for line in fin: 22 words = line.strip().split() + ["<eos>"] #打開的文件讀取每一行而後首尾去除換行符和空格以後對空格進行切分,在末尾加上eos! 23 out_line = ' '.join([str(get_id(w)) for w in words]) +'\n' 對其的行進行轉換 24 fout.write(out_line) 而後寫入到對應的輸出文件裏面 25 26 fin.close() 27 fout.close()
咱們來看一下運行完這個代碼以後咱們的ptb.train.txt變成了什麼樣子:
9994 9996 9974 9999 9972 9978 9981 9993 9977 9973 9985 9998 9992 9971 9997 9990 9995 9970 9989 9987 9988 9975 9980 9986 0
沒錯,所有變成了詞彙表對應的行號編號的形式!
進行完上述兩步處理以後,咱們已經完成了數據預處理部分的一半了,接下來咱們須要考慮的問題就是,處理後的編碼數據不可能直接輸入到網絡裏面去,網絡接受的通常都是許多batch,可是在將數據打包成batch
的時候,又有一個十分嚴峻的問題須要咱們考慮,那就是到底以多長的長度來打包數據呢? 每條句子的長度是不同的,該如何打包呢? 有兩種比較常見的作法,一種是根據batch_size的大小,選取裏面最長的句子爲
參考,剩下的padding成統一的長度!長度統一以後就能夠用於batch了!
而PTB數據是一整段一整段文本,若是一句一句的進行切分的話,會破壞掉數據之間的上下文的關聯,而語言模型爲了利用上下文信息,必須將前面的信息傳遞到後面的句子中去,因此PTB一般採用的是另一種Batch的方法!
該方法將長序列切割成固定長度的子序列,而後循環神經網絡處理完這個子序列以後將最後一個隱藏層的結果複製到下一個序列中做爲初始值,這樣在前向計算的過程當中效果就等同於一次性的讀取完了所有的文檔!
可是若是這樣作的話會面臨另一個問題,整個數據流都是串行流動的,這對於能夠並行計算的tensorflow來講簡直太浪費了,因此,兼顧兩者的作法是,根據batch_size的大小將整個文檔劃分紅batch_size部分!而後讓batch_size的每個位置負責一部分數據,若是處理不完,繼續橫向移動到下一個batch中去,也就是說,對於batch來說數據之間橫向纔是連貫的,縱向實際上是互不相干的!這樣既可以保證數據之間的上下文關係,也可以保證tensorflow能夠並行處理數據!
畫一個示意圖的話,大概是下面這樣: