手把手教你用 TensorFlow 實現文本分類
雷鋒網按:本文作者張慶恆,原文載於作者個人博客,雷鋒網(公眾號:雷鋒網)已獲授權。
由於需要學習語音識別,期間接觸了深度學習的演算法。利用空閑時間,想用神經網路做一個文本分類的應用, 目的是從頭到尾完成一次機器學習的應用,學習模型的優化方法,同時學會使用主流的深度學習框架(這裡選擇tensorflow)。
文章分為兩部分,本文僅實現流程,用簡單的softmax回歸對文本進行分類,後面一篇文章再從流程的各個方面對模型進行優化,達到比較好的效果。收集數據
該部分不是這裡的重點,數據從各大新聞網站爬取新聞文本,分十類保存到本地,包括科技、生活、體育、娛樂等。文本分別保存到training_set和testing_set目錄下,如:
$ tree -L 1 training_set/
training_set/
├── 10_hel
├── 1_ent
├── 2_fin
├── 3_spo
├── 4_tec
├── 5_mil
├── 6_soc
├── 7_lif
├── 8_cul
└── 9_car
文本以text_id.txt的格式保存在不同類的目錄下(如text_1234.txt)。本例保存了共113673個訓練文本和等數量的測試文本(暫時按1:1的比例)。
預處理文本step0
為方便後面處理,預處理文本首先要分別針對訓練文本和測試文本生成唯一的文本ID, 這裡用.txt來標示唯一文本,class_id為類的id,這裡為1-10;text_type為數據類型包括train和test;text_id為類文件夾下的文本id,實現函數:
def get_unique_id(self, data_dir):
"""
get flie unique id famate as _type_.txt.
data_dir is the full path of file
e.g ./training_set/4_tec/4_tec_text/text_2001.txt
where "training" is type, "4" is file class, and "2001" is text id.
modify this function to adapt your data dir fomate
dir_list = data_dir.split("/")
class_id = dir_list[2].split("_")[0]
text_id = dir_list[4].split(".")[0]
type_id = dir_list[1].split("_")[0]
return class_id + "_" + type_id + "_" + text_id
step1: 分詞
通俗來講,文本分類的主要思想,是構建各類文本的漢語詞典,通過對文本進行分析,觀察文本中哪類辭彙比較多,由此判斷文本所屬類別。因此,文本分類需要對文本進行分詞操作,可以選擇的分詞工具很多,這裡選擇Python編寫的jieba開源庫對文本進行分詞,並以行為單位,將文本保存到輸出文件,該部分實現比較簡單:
def splitwords(self, data_dir, data_type):
os.remove(data_type+".txt")
list_dirs = os.walk(data_dir)
for root, _, files in list_dirs:
print root
# get all files under data_dir
for fp in files:
file_id = self.get_unique_id(file_path)
#split words for f, save in file ./data_type.txt
with nested(open(file_path), open(data_type+".txt", "a+")) as (f1, f2):
data = f1.read()
#print data
seg_list = jieba.cut(data, cut_all=False)
f2.write(file_id + " " + " ".join(seg_list).replace("", " ")+"")
print "split word for %s file end." % data_type
return
函數傳入參數為數據集目錄路徑,以及數據集類型(train or test)。結果文件保存形如train.txt,後續的操作在該輸出文件基礎之上。輸出文件格式為:
step2: 去除停用詞
這部分主要刪去文本中的停用詞,停用詞包括一些對於文本分類無用,而且出經常出現的辭彙或符號,如「因此」、「關於」、「嘿嘿」、標點符號等。去除停用詞需根據停用詞典,去除上面經過分詞操作的文本中的停用詞。停用詞典可以根據自己需要生成或在網路上獲得,這裡後面源碼鏈接中會給出使用的停用詞詞典。
def rm_stopwords(self, file_path, word_dict):
#read stop word dict and save in stop_dict
stop_dict = {}
with open(word_dict) as d:
for word in d:
stop_dict[word.strip("")] = 1
# remove tmp file if exists
os.remove(file_path+".tmp")
print "now remove stop words in %s." % file_path
# read source file and rm stop word for each line.
with nested(open(file_path), open(file_path+".tmp", "a+")) as (f1, f2):
for line in f1:
tmp_list = [] # save words not in stop dict
words = line.split()
for word in words[1:]:
if word not in stop_dict:
tmp_list.append(word)
words_without_stop = " ".join(tmp_list)
f2.write(words[0] + " " + words_without_stop + "")
# overwrite origin file with file been removed stop words
shutil.move(file_path+".tmp", file_path)
print "stop words in %s has been removed." % file_path
代碼中經過簡單的按行讀文本,然後搜索停用詞典,如果文本中的辭彙在詞典中,則跳過,否則保存。這裡每行對應數據集中的一個文本。
step3: 生成詞典
上面提到文本分類需要得到能表徵各類文本的漢語詞典,這部分的主要思路是實現tf_idf演算法自動提取關鍵詞,根據詞頻(TF)和逆文檔頻率(IDF)來衡量辭彙在文章中的重要程度。這裡詞頻的計算採用公式:
由於是衡量某類文本的關鍵詞,公式中的「文章」為某類所有文本的總和。逆文檔頻率計算採用公式:
上面的文檔總數為train數據集所有文本的數目。tf-idf為兩個指標的乘積,計算各類文本中所有辭彙的tf-idf,由小到大排序,默認取前500個辭彙作為該類的關鍵詞保存到詞典。最終生成大小為5000的詞典。簡潔考慮,該部分的關鍵代碼(gen_dict方法中):
for k, text_info in class_dict.items():
#print "class %s has %d words" % (k, text_info.file_num)
# get tf in words of class k
for w in text_info.wordmap:
text_info.tf_idf(w, word_in_files[w], text_num)
main_words = []
with open(save_path, "a+") as f:
main_words = text_info.get_mainwords()
print "class %s : main words num: %d" % (k, len(main_words))
f.write("".join(main_words) + "")
class_dict是類id到該類文本信息(text_info)的字典,text_info.wordmap保存了該類文本的所有不重複的辭彙,text_info.tf_idf方法計算該類文本某詞的tf-idf,輸入參數為辭彙,辭彙在整個語料庫出現的文本數和語料庫的文本數。text_info.get_mainwords方法得到該類本前500個關鍵詞。完整的定義與實現參考源碼。
step4: 生成詞袋
該部分實現向量化文本,利用生成的詞典,以行為單位將去停用詞後的文本轉換為向量,這裡向量為5000維。如果文本出現詞典中的某辭彙,則文本向量對應詞典中該辭彙的位置的計數累加。最終生成文件,行數為文本數,列為5000。此外生成對應的label文件,行數為文本數,對應於文本向量文件行,列為1,對應某文本的類別(1-10)。該部分代碼比較簡單,實現在gen_wordbag方法中。
到此完成了文本的預處理,接下來針對不同分類演算法,將有不同的處理,這裡參考tensotflow處理MNIST數據集,讀取預處理後的文本到系統,進行線性回歸。
讀取訓練數據
該部分主要包括兩部分,一是從磁碟讀取向量化後的文本保存到numpy數組,將數據和類別分別存儲,數據保存為二維(text_line_num, 5000)的數組,text_line_num為數據集的文本數,5000為詞典的維度,也是後面模型輸入參數的個數。類別保存為標籤向量(label_line_num, 1),label_line_num,同樣為數據集的大小。
為方便處理,將類別10的標籤保存為0,並對label進行「one_hot」處理,這部分解釋可參考上個tensotflow鏈接。該部分在datasets類中實現。需要注意的是這裡train部分數據最為cv(cross validation)數據,這裡暫時不會用到。此外,由於數據較多,為節省內存,提高整體運算速度,分別讀取train數據集和test數據集。dataset類中保存不同類型的數據集,並實現next_batch方法,獲取指定數目的數據。
訓練數據
該部分利用softmax回歸對數據進行訓練,對於tensorflow的使用這裡不作介紹。完整代碼如下:
#!/usr/bin/python
#-*-coding:utf-8-*-
import tensorflow as tf
from datasets import datasets
data_sets = datasets()
data_sets.read_train_data(".", True)
sess = tf.InteractiveSession()
x = tf.placeholder(tf.float32, [None, 5000])
W = tf.Variable(tf.zeros([5000, 10]))
b = tf.Variable(tf.zeros([10]))
y_ = tf.placeholder(tf.float32, [None, 10])
cross_entropy = -tf.reduce_sum(y_ * tf.log(y + 1e-10))
train_step = tf.train.GradientDescentOptimizer(0.01).minimize(cross_entropy)
#training
tf.global_variables_initializer().run()
saver = tf.train.Saver()
for i in range(1000):
train_step.run()
print W.eval()
print b.eval()
path = saver.save(sess, "./model2/model.md")
代碼中:
● x : 對於輸入數據,None佔位符標示輸入樣本的數量,5000為單個樣本的輸入維度,對應字典維度。
● W :權重矩陣,行為輸入維度,列為輸出維度,這裡為類別的數目10。
● b : 偏重為10對應輸出的維度
● y : 定義訓練輸出結果,使用softmax作為激勵函數,tf.matmul(x, W) + b為輸入參數,tf.matmul為矩陣乘。
● y_ : 真實樣本的類別,從數據集讀入,None佔位符標示輸入樣本的數量,10為輸出的維度。
● cross_entropy: 交叉熵,衡量真實值與預測值的偏差程度,訓練過程中目的是最小化該值。
訓練對cross_entropy進行梯度下降演算法更新參數,學習率為0.01。迭代1000次,每次使用100個訓練集。最後保存訓練的模型到指定目錄。
測試模型
這部分主要讀取上面保存的模型參數,對測試數據集進行預測,並列印準確率。
!/usr/bin/python
data_sets.read_test_data(".", True)
saver.restore(sess, "./model2/model.md")
# test
correct_prediction = tf.equal(tf.argmax(y, 1), tf.argmax(y_, 1))
acc = tf.reduce_mean(tf.cast(correct_prediction, tf.float32))
print(acc.eval())
小結
直接通過上面過程訓練模型,得到的準確率大概為65%,雖然比10%高出許多,仍然屬於比較低的準確率。在後面一篇文章重點對上面的過程進行改進,提高預測的準確性。
此外,值得一提的是,一開始,直接參考tensorflow官網給的例子進行訓練會出現準確率為0的現象,觀察TensorBord,發現權重和偏重一直不更新,列印W和b發現值為Nan,最後找到問題所在:
使用交叉熵作為cost function,由於文本矩陣為嚴重稀疏矩陣,導致出現y_ tf.log(y)結果為0log0的現象。導致訓練參數為Nan,給預測值加一個極小的值,防止與測試為0。
雷鋒網
「12小時零基礎入門深度學習班」開課啦!
想要挑戰AlphaGo卻不懂深度學習?
雷鋒網AI慕課學院專門打造了《12小時零基礎入門深度學習》,fastai中文社區最活躍的四位貢獻者親授,矽谷教學模式帶你實操9個深度學習項目!即使零編程基礎,也能在這裡找到適合你的學習路徑!
課程鏈接:http://www.leiphone.com/special/mooc/01.html
雷鋒網版權文章,未經授權禁止轉載。詳情見轉載須知。


※專訪小米林斌:第100家小米之家開業,今年總銷售額突破1000億不困難
※UI設計師需要具備什麼素質
※Material Design 控制項之 Toolbar 非完全解析
※努比亞真·旗艦Z17,配備三星快閃記憶體反超S8
※震驚!iOS 系統居然自帶懸浮窗口調試工具
TAG:推酷 |
※用TensorFlow Estimator實現文本分類
※如何使用Tokenvator和Windows Tokens實現提權
※用TensorFlow實現物體檢測的像素級分類
※用 TensorFlow 實現物體檢測的像素級分類
※谷歌正式開源 Hinton 膠囊理論代碼,即刻用 TensorFlow 實現吧
※基於Tensorflow實現DeepFM
※用Pytorch 實現的 Capsule Network
※使用Tensorflow Object Detection API實現對象檢測
※基於TensorFlow的變分自編碼器實現
※Android如何實現帶有粘性頭部的ScrollView
※如何使用Cron Jobs實現Linux提權
※用Python 實現的機器人演算法示例集合——PythonRobotics
※Ether Universe項目評測:第四代跨鏈技術實現「異鏈」Tokens直接交換
※Machine Learning(一):基於 TensorFlow 實現寵物血統智能識別
※分散式框架spring-session實現session一致性使用問題
※用TensorFlow 實現的模型集合
※Pytorch實現Logistic回歸二分類
※SAP Cloud for Customer Extensibility的設計與實現
※德國女性已經實現了自我?Deutsche Frauen verwirklichen sich selbst……
※在Spark 上實現 TensorFlow 的庫——Sparkflow