今天就跟大家聊聊有關(guān)Tensorflow中怎么實(shí)現(xiàn)CNN文本分類(lèi),可能很多人都不太了解,為了讓大家更加了解,小編給大家總結(jié)了以下內(nèi)容,希望大家根據(jù)這篇文章可以有所收獲。
成都創(chuàng)新互聯(lián)公司專(zhuān)注于霍林郭勒企業(yè)網(wǎng)站建設(shè),成都響應(yīng)式網(wǎng)站建設(shè)公司,商城網(wǎng)站建設(shè)?;袅止站W(wǎng)站建設(shè)公司,為霍林郭勒等地區(qū)提供建站服務(wù)。全流程按需求定制開(kāi)發(fā),專(zhuān)業(yè)設(shè)計(jì),全程項(xiàng)目跟蹤,成都創(chuàng)新互聯(lián)公司專(zhuān)業(yè)和態(tài)度為您提供的服務(wù)
我們將在這篇文章中使用的數(shù)據(jù)集是 Movie Review data from Rotten Tomatoes,也是原始文獻(xiàn)中使用的數(shù)據(jù)集之一。 數(shù)據(jù)集包含10,662個(gè)示例評(píng)論句子,正負(fù)向各占一半。 數(shù)據(jù)集的大小約為20k。 請(qǐng)注意,由于這個(gè)數(shù)據(jù)集很小,我們很可能會(huì)使用強(qiáng)大的模型。 此外,數(shù)據(jù)集不附帶拆分的訓(xùn)練/測(cè)試集,因此我們只需將10%的數(shù)據(jù)用作 dev set。 原始文獻(xiàn)展示了對(duì)數(shù)據(jù)進(jìn)行10倍交叉驗(yàn)證的結(jié)果。
這里不討論數(shù)據(jù)預(yù)處理代碼,代碼可以在 Github 上獲得,并執(zhí)行以下操作:
從原始數(shù)據(jù)文件中加載正負(fù)向情感的句子。
使用與原始文獻(xiàn)相同的代碼清理文本數(shù)據(jù)。
將每個(gè)句子加到最大句子長(zhǎng)度(59)。我們向所有其他句子添加特殊的操作,使其成為59個(gè)字。填充句子相同的長(zhǎng)度是有用的,因?yàn)檫@樣就允許我們有效地批量我們的數(shù)據(jù),因?yàn)榕幚碇械拿總€(gè)示例必須具有相同的長(zhǎng)度。
構(gòu)建詞匯索引,并將每個(gè)單詞映射到0到18,765之間的整數(shù)(詞庫(kù)大?。?。 每個(gè)句子都成為一個(gè)整數(shù)向量。
原始文獻(xiàn)的網(wǎng)絡(luò)結(jié)構(gòu)如下圖:
第一層將單詞嵌入到低維向量中。 下一層使用多個(gè)過(guò)濾器大小對(duì)嵌入的字矢量執(zhí)行卷積。 例如,一次滑過(guò)3,4或5個(gè)字。 接下來(lái),我們將卷積層的max_pooling結(jié)果作為一個(gè)長(zhǎng)的特征向量,添加dropout正則,并使用softmax層對(duì)結(jié)果進(jìn)行分類(lèi)。
因?yàn)檫@是是一篇教學(xué)性質(zhì)的博客,所以對(duì)于原始文獻(xiàn)的模型進(jìn)行一下簡(jiǎn)化:
我們不會(huì)對(duì)我們的詞嵌入使用預(yù)先訓(xùn)練的word2vec向量。 相反,我們從頭開(kāi)始學(xué)習(xí)嵌入。
我們不會(huì)對(duì)權(quán)重向量執(zhí)行L2規(guī)范約束。 《A Sensitivity Analysis of (and Practitioners’ Guide to) Convolutional Neural Networks for Sentence Classification》這篇文章中發(fā)現(xiàn)約束對(duì)最終結(jié)果幾乎沒(méi)有影響。(關(guān)注公眾號(hào)輸入cnn獲取)
原始實(shí)驗(yàn)用兩個(gè)輸入數(shù)據(jù)通道 - 靜態(tài)和非靜態(tài)字矢量。 我們只使用一個(gè)通道。
將這些擴(kuò)展代碼添加到這里是比較簡(jiǎn)單的(幾十行代碼)。 看看帖子結(jié)尾的練習(xí)。
為了允許各種超參數(shù)配置,我們將代碼放入TextCNN類(lèi)中,在init函數(shù)中生成模型圖。
為了實(shí)例化類(lèi),我們傳遞以下參數(shù):
sequence_length- 句子的長(zhǎng)度。注意:我們將所有句子填充到相同的長(zhǎng)度(我們的數(shù)據(jù)集為59)。
num_classes- 輸出層中的類(lèi)數(shù),在我們的例子中為(消極,積極)。
vocab_size- 我們的詞匯量的大小。 這需要定義我們的嵌入層的大小,它將具有[vocabulary_size,embedding_size]的形狀。
embedding_size- 嵌入的維度。
filter_sizes- 我們想要卷積過(guò)濾器覆蓋的字?jǐn)?shù)。 我們將為此處指定的每個(gè)大小設(shè)置num_filters。 例如,[3,4,5]意味著我們將有一個(gè)過(guò)濾器,分別滑過(guò)3,4和5個(gè)字,總共有3 * num_filters個(gè)過(guò)濾器。
num_filters- 每個(gè)過(guò)濾器大小的過(guò)濾器數(shù)量(見(jiàn)上文)。
首先定義網(wǎng)絡(luò)的輸入數(shù)據(jù)
tf.placeholder創(chuàng)建一個(gè)占位符變量,當(dāng)我們?cè)谟?xùn)練集或測(cè)試時(shí)間執(zhí)行它時(shí),我們將其饋送到網(wǎng)絡(luò)。 第二個(gè)參數(shù)是輸入張量的形狀:None意味著該維度的長(zhǎng)度可以是任何東西。 在我們的情況下,第一個(gè)維度是批量大小,并且使用“None”允許網(wǎng)絡(luò)處理任意大小的批次。
將神經(jīng)元保留在丟失層中的概率也是網(wǎng)絡(luò)的輸入,因?yàn)槲覀儍H在訓(xùn)練期間使用dropout。 我們?cè)谠u(píng)估模型時(shí)禁用它(稍后再說(shuō))。
我們定義的第一層是嵌入層,它將詞匯詞索引映射到低維向量表示中。 它本質(zhì)上是一個(gè)從數(shù)據(jù)中學(xué)習(xí)的lookup table。
我們?cè)谶@里使用了幾個(gè)功能:
tf.device(“/ cpu:0”)強(qiáng)制在CPU上執(zhí)行操作。 默認(rèn)情況下,TensorFlow將嘗試將操作放在GPU上(如果有的話(huà))可用,但是嵌入式實(shí)現(xiàn)當(dāng)前沒(méi)有GPU支持,并且如果放置在GPU上會(huì)引發(fā)錯(cuò)誤。
tf.name_scope創(chuàng)建一個(gè)名稱(chēng)范圍,名稱(chēng)為“embedding”。 范圍將所有操作添加到名為“嵌入”的頂級(jí)節(jié)點(diǎn)中,以便在TensorBoard中可視化網(wǎng)絡(luò)時(shí)獲得良好的層次結(jié)構(gòu)。
W是我們?cè)谟?xùn)練中學(xué)習(xí)的嵌入矩陣。 我們使用隨機(jī)均勻分布來(lái)初始化它。 tf.nn.embedding_lookup創(chuàng)建實(shí)際的嵌入操作。 嵌入操作的結(jié)果是形狀為[None,sequence_length,embedding_size]的三維張量。
TensorFlow的卷積轉(zhuǎn)換操作具有對(duì)應(yīng)于批次,寬度,高度和通道的尺寸的4維張量。 我們嵌入的結(jié)果不包含通道尺寸,所以我們手動(dòng)添加,留下一層shape為[None,sequence_length,embedding_size,1]。
現(xiàn)在我們已經(jīng)準(zhǔn)備好構(gòu)建卷積層,然后再進(jìn)行max-pooling。 注意:我們使用不同大小的filter。 因?yàn)槊總€(gè)卷積產(chǎn)生不同形狀的張量,我們需要迭代它們,為它們中的每一個(gè)創(chuàng)建一個(gè)層,然后將結(jié)果合并成一個(gè)大特征向量。
這里,W是我們的濾波器矩陣,h是將非線(xiàn)性應(yīng)用于卷積輸出的結(jié)果。 每個(gè)過(guò)濾器在整個(gè)嵌入中滑動(dòng),但是它涵蓋的字?jǐn)?shù)有所不同。 “VALID”填充意味著我們?cè)跊](méi)有填充邊緣的情況下將過(guò)濾器滑過(guò)我們的句子,執(zhí)行給我們輸出形狀[1,sequence_length - filter_size + 1,1,1]的窄卷積。 在特定過(guò)濾器大小的輸出上執(zhí)行最大值池將留下一張張量的形狀[batch_size,1,num_filters]。 這本質(zhì)上是一個(gè)特征向量,其中最后一個(gè)維度對(duì)應(yīng)于我們的特征。 一旦我們從每個(gè)過(guò)濾器大小得到所有的匯總輸出張量,我們將它們組合成一個(gè)長(zhǎng)形特征向量[batch_size,num_filters_total]。 在tf.reshape中使用-1可以告訴TensorFlow在可能的情況下平坦化維度。
Dropout可能是卷積神經(jīng)網(wǎng)絡(luò)正則最流行的方法。Dropout背后的想法很簡(jiǎn)單。Dropout層隨機(jī)地“禁用”其神經(jīng)元的一部分。 這可以防止神經(jīng)元共同適應(yīng)(co-adapting),并迫使他們學(xué)習(xí)個(gè)別有用的功能。 我們保持啟用的神經(jīng)元的分?jǐn)?shù)由我們網(wǎng)絡(luò)的dropout_keep_prob輸入定義。 在訓(xùn)練過(guò)程中,我們將其設(shè)置為0.5,在評(píng)估過(guò)程中設(shè)置為1(禁用Dropout)。
使用max-pooling(with dropout )的特征向量,我們可以通過(guò)執(zhí)行矩陣乘法并選擇具有最高分?jǐn)?shù)的類(lèi)來(lái)生成預(yù)測(cè)。 我們還可以應(yīng)用softmax函數(shù)將原始分?jǐn)?shù)轉(zhuǎn)換為歸一化概率,但這不會(huì)改變我們的最終預(yù)測(cè)。
這里,tf.nn.xw_plus_b是執(zhí)行Wx + b矩陣乘法的便利包裝器。
使用分?jǐn)?shù)我們可以定義損失函數(shù)。 損失是對(duì)我們網(wǎng)絡(luò)錯(cuò)誤的衡量,我們的目標(biāo)是將其最小化。分類(lèi)問(wèn)題的標(biāo)準(zhǔn)損失函數(shù)是交叉熵?fù)p失 cross-entropy loss。
這里,tf.nn.softmax_cross_entropy_with_logits是一個(gè)方便的函數(shù),計(jì)算每個(gè)類(lèi)的交叉熵?fù)p失,給定我們的分?jǐn)?shù)和正確的輸入標(biāo)簽。 然后求損失的平均值。 我們也可以使用總和,但這比較難以比較不同批量大小和訓(xùn)練/測(cè)試集數(shù)據(jù)的損失。
我們還為精度定義一個(gè)表達(dá)式,這是在訓(xùn)練和測(cè)試期間跟蹤的有用數(shù)值。
TensorFlow可以看到其結(jié)構(gòu)圖如下:
在我們?yōu)榫W(wǎng)絡(luò)定義訓(xùn)練程序之前,我們需要了解一些關(guān)于TensorFlow如何使用Sessions和Graphs的基礎(chǔ)知識(shí)。如果您已經(jīng)熟悉這些概念,請(qǐng)隨時(shí)跳過(guò)本節(jié)。
在TensorFlow中, Session是正在執(zhí)行g(shù)raph 操作的環(huán)境,它包含有關(guān)變量和隊(duì)列的狀態(tài)。每個(gè) Session都在單個(gè)graph上運(yùn)行。如果在創(chuàng)建變量和操作時(shí)未明確使用 Session,則使用TensorFlow創(chuàng)建的當(dāng)前默認(rèn) Session。您可以通過(guò)在session.as_default()塊中執(zhí)行命令來(lái)更改默認(rèn) Session(見(jiàn)下文)。
Graph包含操作和張量。您可以在程序中使用多個(gè)Graph,但大多數(shù)程序只需要一個(gè)Graph。您可以在多個(gè) Session中使用相同的Graph,但在一個(gè) Session中不能使用多Graph。 TensorFlow始終創(chuàng)建一個(gè)默認(rèn)Graph,但您也可以手動(dòng)創(chuàng)建一個(gè)Graph,并將其設(shè)置為新的默認(rèn)Graph,如下圖所示。顯式創(chuàng)建 Session和Graph可確保在不再需要資源時(shí)正確釋放資源。
當(dāng)優(yōu)選設(shè)備不存在時(shí),allow_soft_placement設(shè)置允許TensorFlow回退到具有特定操作的設(shè)備上。 例如,如果我們的代碼在GPU上放置一個(gè)操作,并且我們?cè)跊](méi)有GPU的機(jī)器上運(yùn)行代碼,則不使用allow_soft_placement將導(dǎo)致錯(cuò)誤。 如果設(shè)置了log_device_placement,TensorFlow會(huì)登錄那些設(shè)備(CPU或GPU)進(jìn)行操作。 這對(duì)調(diào)試非常有用。 標(biāo)記是我們程序的命令行參數(shù)。
當(dāng)我們實(shí)例化我們的TextCNN模型時(shí),所有定義的變量和操作將被放置在上面創(chuàng)建的默認(rèn)圖和會(huì)話(huà)中。
接下來(lái),我們定義如何優(yōu)化網(wǎng)絡(luò)的損失函數(shù)。 TensorFlow有幾個(gè)內(nèi)置優(yōu)化器。 我們正在使用Adam優(yōu)化器。
在這里,train_op這里是一個(gè)新創(chuàng)建的操作,我們可以運(yùn)行它們來(lái)對(duì)我們的參數(shù)執(zhí)行更新。 train_op的每次執(zhí)行都是一個(gè)訓(xùn)練步驟。 TensorFlow自動(dòng)計(jì)算哪些變量是“可訓(xùn)練的”并計(jì)算它們的梯度。 通過(guò)定義一個(gè)global_step變量并將其傳遞給優(yōu)化器,讓TensorFlow對(duì)訓(xùn)練步驟進(jìn)行計(jì)數(shù)。 每次執(zhí)行train_op時(shí),global step 將自動(dòng)遞增1。
TensorFlow有一個(gè)概述(summaries),可以在訓(xùn)練和評(píng)估過(guò)程中跟蹤和查看各種數(shù)值。 例如,您可能希望跟蹤您的損失和準(zhǔn)確性隨時(shí)間的變化。您還可以跟蹤更復(fù)雜的數(shù)值,例如圖層激活的直方圖。 summaries是序列化對(duì)象,并使用SummaryWriter寫(xiě)入磁盤(pán)。
在這里,我們分別跟蹤培訓(xùn)和評(píng)估的總結(jié)。 在我們的情況下,這些數(shù)值是相同的,但是您可能只有在訓(xùn)練過(guò)程中跟蹤的數(shù)值(如參數(shù)更新值)。 tf.merge_summary是將多個(gè)摘要操作合并到可以執(zhí)行的單個(gè)操作中的便利函數(shù)。
通常使用TensorFlow的另一個(gè)功能是checkpointing- 保存模型的參數(shù)以便稍后恢復(fù)。Checkpoints 可用于在以后的時(shí)間繼續(xù)訓(xùn)練,或使用 early stopping選擇最佳參數(shù)設(shè)置。 使用Saver對(duì)象創(chuàng)建 Checkpoints。
在訓(xùn)練模型之前,我們還需要在圖中初始化變量。
global_variables_initializer函數(shù)是一個(gè)方便函數(shù),它運(yùn)行我們?yōu)樽兞慷x的所有初始值。也可以手動(dòng)調(diào)用變量的初始化程序。 如果希望使用預(yù)先訓(xùn)練的值初始化嵌入,這很有用。
現(xiàn)在我們來(lái)定義一個(gè)訓(xùn)練步驟的函數(shù),評(píng)估一批數(shù)據(jù)上的模型并更新模型參數(shù)。
feed_dict包含我們傳遞到我們網(wǎng)絡(luò)的占位符節(jié)點(diǎn)的數(shù)據(jù)。您必須為所有占位符節(jié)點(diǎn)提供值,否則TensorFlow將拋出錯(cuò)誤。使用輸入數(shù)據(jù)的另一種方法是使用隊(duì)列,但這超出了這篇文章的范圍。
接下來(lái),我們使用session.run執(zhí)行我們的train_op,它返回我們要求它進(jìn)行評(píng)估的所有操作的值。請(qǐng)注意,train_op什么都不返回,它只是更新我們網(wǎng)絡(luò)的參數(shù)。最后,我們打印當(dāng)前培訓(xùn)批次的丟失和準(zhǔn)確性,并將摘要保存到磁盤(pán)。請(qǐng)注意,如果批量太小,訓(xùn)練批次的損失和準(zhǔn)確性可能會(huì)在批次間顯著變化。而且因?yàn)槲覀兪褂胐ropout,您的訓(xùn)練指標(biāo)可能開(kāi)始比您的評(píng)估指標(biāo)更糟。
我們寫(xiě)一個(gè)類(lèi)似的函數(shù)來(lái)評(píng)估任意數(shù)據(jù)集的丟失和準(zhǔn)確性,例如驗(yàn)證集或整個(gè)訓(xùn)練集。本質(zhì)上這個(gè)功能與上述相同,但沒(méi)有訓(xùn)練操作。它也禁用退出。
最后,準(zhǔn)備編寫(xiě)訓(xùn)練循環(huán)。 迭代數(shù)據(jù)的批次,調(diào)用每個(gè)批次的train_step函數(shù),偶爾評(píng)估和檢查我們的模型:
這里,batch_iter是一個(gè)批處理數(shù)據(jù)的幫助函數(shù),而tf.train.global_step是返回global_step值的便利函數(shù)。
我們的訓(xùn)練腳本將summaries寫(xiě)入輸出目錄,并將TensorBoard指向該目錄,我們可以將圖和我們創(chuàng)建的summaries可視化。
有幾件事情脫穎而出:
我們的訓(xùn)練指標(biāo)并不平滑,因?yàn)槲覀兪褂眯∨俊?如果我們使用較大的批次(或在整個(gè)訓(xùn)練集上評(píng)估),我們會(huì)得到一個(gè)更平滑的藍(lán)線(xiàn)。
因?yàn)闇y(cè)試者的準(zhǔn)確性顯著低于訓(xùn)練準(zhǔn)確度,我們的網(wǎng)絡(luò)在訓(xùn)練數(shù)據(jù)似乎過(guò)擬合了,這表明我們需要更多的數(shù)據(jù)(MR數(shù)據(jù)集非常小),更強(qiáng)的正則化或更少的模型參數(shù)。 例如,我嘗試在最后一層為重量添加額外的L2正則,并且能夠?qū)?zhǔn)確度提高到76%,接近于原始文獻(xiàn)。
因?yàn)槭褂昧薲ropout,訓(xùn)練損失和準(zhǔn)確性開(kāi)始大大低于測(cè)試指標(biāo)。
您可以使用代碼進(jìn)行操作,并嘗試使用各種參數(shù)配置運(yùn)行模型。 Github提供了代碼和說(shuō)明。
以下是一些的練習(xí),可以提高模型的性能:
使用預(yù)先訓(xùn)練的word2vec向量初始化嵌入。 為了能夠起作用,您需要使用300維嵌入,并用預(yù)先訓(xùn)練的值初始化它們。
限制最后一層權(quán)重向量的L2范數(shù),就像原始文獻(xiàn)一樣。 您可以通過(guò)定義一個(gè)新的操作,在每次訓(xùn)練步驟之后更新權(quán)重值。
將L2正規(guī)化添加到網(wǎng)絡(luò)以防止過(guò)擬合,同時(shí)也提高dropout比率。 (Github上的代碼已經(jīng)包括L2正則化,但默認(rèn)情況下禁用)
添加權(quán)重更新和圖層操作的直方圖summaries,并在TensorBoard中進(jìn)行可視化。
看完上述內(nèi)容,你們對(duì)Tensorflow中怎么實(shí)現(xiàn)CNN文本分類(lèi)有進(jìn)一步的了解嗎?如果還想了解更多知識(shí)或者相關(guān)內(nèi)容,請(qǐng)關(guān)注創(chuàng)新互聯(lián)行業(yè)資訊頻道,感謝大家的支持。