手把手教你用 TensorFlow 實現文本分類(上)

2021-01-08 雷鋒網

雷鋒網按:本文作者張慶恆,原文載於作者個人博客,雷鋒網已獲授權。

由於需要學習語音識別,期間接觸了深度學習的算法。利用空閒時間,想用神經網絡做一個文本分類的應用, 目的是從頭到尾完成一次機器學習的應用,學習模型的優化方法,同時學會使用主流的深度學習框架(這裡選擇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, 這裡用 {class_id}{text_type}{text_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 {class_id}_type_{text_id}.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):


        if os.path.exists(data_type+".txt"):

            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_path = os.path.join(root, fp)

                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("\n", " ")+"\n")


        print "split word for %s file end." % data_type

        return

函數傳入參數為數據集目錄路徑,以及數據集類型(train or test)。結果文件保存形如train.txt,後續的操作在該輸出文件基礎之上。輸出文件格式為:<class_{data_type}_id> < words >

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("\n")] = 1


        # remove tmp file if exists

        if os.path.exists(file_path+".tmp"):

            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 + "\n")


        # 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

        return

代碼中經過簡單的按行讀文本,然後搜索停用詞典,如果文本中的詞彙在詞典中,則跳過,否則保存。這裡每行對應數據集中的一個文本。

  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("\n".join(main_words) + "\n")

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.nn.softmax(tf.matmul(x, W) + b)


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):

    batch_xs, batch_ys = data_sets.train.next_batch(100)

    train_step.run({x: batch_xs, y_: batch_ys})


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

#-*-coding:utf-8-*-


import tensorflow as tf

from datasets import datasets


data_sets = datasets()

data_sets.read_test_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.nn.softmax(tf.matmul(x, W) + b)

y_ = tf.placeholder(tf.float32, [None, 10])


saver = tf.train.Saver()

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({x: data_sets.test.text, y_: data_sets.test.label}))

  小結

直接通過上面過程訓練模型,得到的準確率大概為65%,雖然比10%高出許多,仍然屬於比較低的準確率。在後面一篇文章重點對上面的過程進行改進,提高預測的準確性。

此外,值得一提的是,一開始,直接參考tensorflow官網給的例子進行訓練會出現準確率為0的現象,觀察TensorBord,發現權重和偏重一直不更新,列印W和b發現值為Nan,最後找到問題所在:

使用交叉熵作為cost function,由於文本矩陣為嚴重稀疏矩陣,導致出現y_ tf.log(y)結果為0log0的現象。導致訓練參數為Nan,給預測值加一個極小的值,防止與測試為0。

雷鋒網(公眾號:雷鋒網)相關閱讀:

手把手教你用 TensorFlow 實現文本分類(下)

手把手教你如何用 TensorFlow 實現基於 DNN 的文本分類


雷鋒網版權文章,未經授權禁止轉載。詳情見轉載須知。

相關焦點

  • 手把手教你如何用TensorFlow實現基於DNN的文本分類
    至於算法和函數內部的實現機制,可以等了解整個流程之後,在實踐中進行更深入的學習和掌握。那麼問題來了,既然作為初學者不需要掌握算法細節,但實現模型的過程中又必須用到相關算法,怎麼辦呢?答案是藉助於網際網路上已經實現好的函數庫,例如 TensorFlow。在本文中,我們將利用 TensorFlow 實現一個基於深度神經網絡(DNN)的文本分類模型,希望對各位初學者有所幫助。
  • 手把手教你用 TensorFlow 實現卷積神經網絡(附代碼)
    標準的數位相機有紅、綠、藍三個通道(Channels),每一種顏色的像素值在0-255之間,構成三個堆疊的二維矩陣;灰度圖像則只有一個通道,可以用一個二維矩陣來表示。第二步中,這個小的神經網絡對這77張大小相同的小圖片都進行同樣的計算,也稱權重共享(SharedWeights)。
  • 手把手教你安裝Tensorflow GPU版本
    tensorflow-gpu+電腦win10+gtx1050ti顯卡配置心得       這裡建議使用CUDA8.0的版本,因為小編之前下載安裝了一個CUDA9.0的版本,好像是不能用的哦。之後打開Anaconda Prompt      檢查目前有哪些版本的python可以安裝:conda search --full-name python       創建一個名為tensorflow-gpu的anaconda環境,配置python3.5,之所以選擇3.5的,是因為看的一些博主也是用3.5的,其實也可以用3.6的,因為有對應的tensorflow的版本,小編也是之後才知道的
  • 教程| 如何用TensorFlow在安卓設備上實現深度學習推斷
    她在 Insight 工作的時候,在安卓系統上用 TensorFlow 部署了一個 WaveNet 模型。本文詳細介紹了部署和實現過程。對於個人和公司來說,存在許多狀況是更希望在本地設備上做深度學習推斷的:想像一下當你在旅行途中沒有可靠的網際網路連結時,或是要處理傳輸數據到雲服務的隱私問題和延遲問題時。
  • 如何在AMD的GPU上運行TensorFlow?
    ROCm 即 Radeon 開放生態系統 (Radeon Open Ecosystem),是我們在 Linux 上進行 GPU 計算的開源軟體基礎。而 TensorFlow 實現則使用了 MIOpen,這是一個適用於深度學習的高度優化 GPU 例程庫。 AMD 提供了一個預構建的 whl 軟體包,安裝過程很簡單,類似於安裝 Linux 通用 TensorFlow。
  • Tensorflow(入門)——Mnist分類問題
    最近開始向深度學習發展,學習深度學習自然要學習Tensorflow,而且它的中文社區非常活躍,課程強烈推薦龍龍老師的課。要學習深度學習,先從分類問題開始入門吧。在Tensorflow社區中,有個入門級的問題,就是Mnist數據集的分類問題。
  • TensorFlow 實現流行的機器學習算法的教程匯集
    使用一個預訓練的 VGG 網絡並將其約束到你自己的數據上,以便實現快速訓練: https://github.com/tflearn/tflearn/blob/master/examples/images/vgg_network_finetuning.pyRNN Pixels。
  • 手把手教你用TensorFlow實現看圖說話|教程+代碼
    O’reilly出版社和TensorFlow團隊聯合發布了一份教程,詳細介紹了如何在Google的Show and Tell模型基礎上,用Flickr30k數據集來訓練一個圖像描述生成器。模型的創建、訓練和測試都基於TensorFlow。
  • 用Tensorflow和FastAPI構建圖像分類API
    這個類有一些有用的參數,比如我們可以傳遞swaggerui的標題和描述。from fastapi import FastAPIapp = FastAPI(title='Hello world')我們定義一個函數並用@app.get. 這意味著我們的API/index支持GET方法。
  • TFX 最新博文:如何使用 TensorFlow 生態系統實現快速高效的 BERT...
    通過與 TensorFlow 團隊合作,並使用他們的最新成果,Concur 實驗室最終實現了一致、簡單、快速的 BERT 模型推斷,可以在幾毫秒內實現從原始文本的分類預測。那麼,各種 TensorFlow 庫和組件如何幫助研究人員達到這個裡程碑?
  • 圖像分類任務中,Tensorflow 與 Keras 到底哪個更厲害?
    讓我們看看這個問題在圖像分類的實際應用中的答案。 在此之前,先介紹Keras和Tensorflow這兩個術語,幫助你在10分鐘內構建強大的圖像分類器。   Tensorflow: Tensorflow是開發深度學習模型最常用的庫。它是有史以來最好的庫,在日常實驗中被許多極客選擇。
  • TensorFlow 2.3 新功能概覽
    也有新的改善用戶體驗的實用程序 ,僅用幾行代碼即可輕鬆地從磁碟圖像或文本文件目錄創建 tf.data.Dataset 。tf.datahttps://tensorflow.您只需一個函數調用,便可從磁碟上的圖像或文本文件目錄創建 tf.data.Dataset。
  • TensorFlow中RNN實現的正確打開方式
    在代碼實現上,RNNCell只是一個抽象類,我們用的時候都是用的它的兩個子類BasicRNNCell和BasicLSTMCell。顧名思義,前者是RNN的基礎類,後者是LSTM的基礎類。如果我們處理的是分類問題,那麼我們還需要對new_h添加單獨的Softmax層才能得到最後的分類概率輸出。還是建議大家親自看一下源碼實現(地址:http://t.cn/RNJsJoH)來搞明白其中的細節。
  • MobileNet教程:用TensorFlow搭建在手機上運行的圖像分類器
    MobileNet框架怎麼用?Coastline Automation創始人Matt Harvey最近在Medium上發布了一份教程,教你用MobileNet來識別道路。Coastline是一家用深度學習來監測行車情況、防止車禍的公司。
  • 宣布Swift for TensorFlow已在GitHub上開源
    >https://github.com/tensorflow/swiftSwift for TensorFlow 為 TensorFlow 提供了一種新的編程模型,將 TensorFlow 計算圖與 Eager Execution 的靈活性和表達能力結合在了一起,同時還注重提高整個軟體架構每一層的可用性。
  • 未探索的TensorFlow庫
    但是,這在桌面上不太好用,因為它是為ARM neon優化的。這篇問題對此進行了解釋(https://github.com/tensorflow/tensorflow/issues/35380),否則我們需要進一步優化模型。模型優化工具箱可以幫助我們完成這些任務。根據其主頁,它可以用於:減少雲和邊緣設備(如行動裝置、物聯網)的延遲和推理成本。
  • 手把手教你用TensorFlow搭建圖像分類器|乾貨
    它讓用戶一步步利用基本的計算機知識,通過在平臺上搭建一個簡單的應用,以省去那些又難懂、又沒必要的過程。不過到目前為止,我還沒看到一個同樣有大眾化用戶群體的深度學習平臺。為此,我向大家展示了如何在不會電腦編程的情況下,用TensorFlow搭建一個屬於自己圖片分類器。以下是具體步驟(相關Youtube視頻請點擊):這裡第一步:安裝Docker為什麼需要Docker呢?
  • 如何用Tensorflow對象檢測進行像素級分類
    有幾種算法實現了實例分割,但Tensorflow對象檢測API所使用的算法是掩碼RCNN。  Mask RCNN  讓我們先從比較簡單的地方說起。  第二階段,在本質上是Faster RCNN,從每個候選框中提取出使用RoIPool的特性,並進行分類和邊界框的回歸。兩個階段使用的特性可以共享,以獲得更快的推理。  Mask R-CNN在概念上很簡單:Faster RCNN對每個候選對象有兩個輸出,一個類標籤和一個邊界框偏移。為此,我們添加了輸出對象掩碼的第三個分支——這是一個二進位掩碼,表示對象在邊界框中的像素。
  • 如何用一個Python示例入門TensorFlow?
    在本文中,我們將在 Windows10 上使用 Python,因此會提及這個平臺上的安裝過程。TensorFlow 只支持 Python 3.5 和 3.6,所以請確保你的系統中安裝了其中的一個版本。對於其他的作業系統和語言,你可以查看官方的安裝指南。我們需要了解的另一件事是系統的硬體配置。
  • TensorFlow推出新接口,簡化 ML移動端開發流程
    Task Library 支持主流的機器學習任務,包括圖像分類與分割、目標檢測和自然語言處理。模型接口針對每個任務進行過專門設計,可實現最佳性能和易用性——現在,只需 5 行代碼就可以在受支持任務的預訓練和自定義模型上執行推理!