<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                ??碼云GVP開源項目 12k star Uniapp+ElementUI 功能強大 支持多語言、二開方便! 廣告
                # 十、使用詞嵌入從頭開始訓練 LSTM 到目前為止,我們已經看到了深度學習在結構化數據,圖像數據甚至時間序列數據中的應用示例。 似乎唯一正確的方法是繼續進行**自然語言處理**(**NLP**)作為下一步。 機器學習和人類語言之間的聯系非常有趣。 深度學習已像計算機視覺一樣,以指數方式加快了該領域的發展速度。 讓我們從 NLP 的簡要概述開始,并在本章中將要完成的一些任務開始。 我們還將在本章中介紹以下主題: * 自然語言處理入門 * 向量化文本 * 詞嵌入 * Keras 嵌入層 * 用于自然語言處理的一維 CNN * 文檔分類的案例研究 # 自然語言處理入門 NLP 領域廣闊而復雜。 從技術上講,人類語言與計算機科學之間的任何交互都可能屬于此類。 不過,為了便于討論,我將 NLP 限于分析,理解,有時生成人類語言。 從計算機科學的起源開始,我們就對 NLP 著迷,因為它是通向強大人工智能的門戶。 1950 年,艾倫·圖靈(Alan Turing)提出了圖靈測試,其中涉及一臺計算機,它很好地模仿了一個人,使其與另一個人無法區分,以此作為機器智能的度量標準。 從那時起,我們一直在尋找幫助機器理解人類語言的聰明方法。 在此過程中,我們開發了語音到文本的轉錄,人類語言之間的自動翻譯,文檔的自動匯總,主題建模,命名實體標識以及各種其他用例。 隨著我們對 NLP 的了解不斷增長,我們發現 AI 應用在日常生活中變得越來越普遍。 聊天機器人作為客戶服務應用已變得司空見慣,最近,它們已成為我們的個人數字助理。 在撰寫本文時,我可以要求 Alexa 在我的購物清單中添加一些內容或演奏一些流暢的爵士樂。 自然語言處理以一種非常有趣和強大的方式將人類連接到計算機。 在本章中,我將專注于理解人類語言,然后使用這種理解進行分類。 我實際上將進行兩個分類案例研究,一個涉及語義分析,另一個涉及文檔分類。 這兩個案例研究為深度學習的應用提供了巨大的機會,而且它們確實非常相似。 # 語義分析 **語義分析**從技術上講是對語言含義的分析,但是通常當我們說語義分析時,我們是在談論理解作者的感受。 語義分類器通常試圖將某些話語分類為積極,消極,快樂,悲傷,中立等。 諷刺是我最喜歡的語言之一,這使這成為一個具有挑戰性的問題。 人類語言中有許多微妙的模式,這些對于計算機學習來說是非常具有挑戰性的。 但是挑戰并不意味著沒有可能。 只要有一個好的數據集,這個任務就很有可能實現。 要成功解決此類問題,需要一個好的數據集。 雖然我們當然可以在整個互聯網上找到大量的人類對話,但其中大多數沒有標簽。 查找帶標簽的病例更具挑戰性。 解決此問題的早期嘗試是收集包含表情符號的 Twitter 數據。 如果一條推文中包含:),則認為該推文是肯定的。 這成為 Jimmy Lin 和 Alek Kolcz 在 Twitter 上的[大規模機器學習中引用的知名表情符號技巧。](https://www.semanticscholar.org/paper/Large-scale-machine-learning-at-twitter-Lin-Kolcz/d192c32acab207b89fb11df88ef79c6ce5a69411) 這種類型的分類器的大多數業務應用都是二元的,我們嘗試在其中預測客戶是否滿意。 但是,那當然不是對這種語言模型的限制。 只要我們有用于此類事物的標簽,我們就可以為其他音調建模。 我們甚至可能嘗試衡量某人的聲音或語言中的焦慮或困擾; 但是,解決音頻輸入超出了本章的范圍。 進一步挖掘數據的嘗試包括使用與正面和負面電影評論相關的語言以及與在線購物產品評論相關的語言。 這些都是很好的方法。 但是,在使用這些類型的數據源對來自不同域的文本進行分類時,應格外小心。 您可能會想到,電影評論或在線購買中使用的語言可能與 IT 幫助臺客戶支持電話中使用的語言完全不同。 當然,我們當然可以對情緒進行更多的分類。 在下一節中,我們將討論文檔分類的更一般的應用。 # 文檔分類 **文檔分類**與情感分析密切相關。 在這兩種情況下,我們都使用文本將文檔分類。 實際上,這只是改變的原因。 文檔分類就是根據文檔的類型對文檔進行分類。 世界上最明顯,最常見的文檔分類系統是垃圾郵件過濾器,但它還有許多其他用途。 我最喜歡的文檔分類用途之一是解決“聯邦主義者論文”的原始作者的辯論。 亞歷山大·漢密爾頓(Alexander Hamilton),詹姆斯·麥迪遜(James Madison)和約翰·杰伊(John Jay)在 1787 年和 1788 年以化名 Publius 出版了 85 篇文章,支持批準美國憲法。 后來,漢密爾頓提供了一份清單,詳細列出了每篇論文的作者在 1804 年與亞倫·伯爾(Aaron Burr)進行致命的對決之前。麥迪遜(Madison)在 1818 年提供了自己的清單,這在作者身份上引起了爭執,此后學者一直在努力解決。 雖然大多數人都同意有爭議的作品是麥迪遜的作品,但是關于兩者之間的合作仍存在一些理論。 將這 12 個有爭議的文檔歸類為 Madison 還是 Hamilton,已經成為許多數據科學博客的不二之選。 正式而言,Glenn Fung 的論文[《有爭議的聯邦主義者論文:通過凹面最小化進行 SVM 特征選擇》](http://pages.cs.wisc.edu/~gfung/federalist.pdf) 涵蓋了相當嚴格的主題。 文檔分類的最后一個示例可能是圍繞了解文檔的內容并規定操作。 想象一下一個分類器,它可能會讀取有關法律案件的一些信息,例如請愿/投訴和傳票,然后向被告提出建議。 然后,我們的假想系統可能會說:*鑒于我在其他類似情況下的經驗,您可能想解決*。 情感分析和文檔分類是基于計算機理解自然語言的能力的強大技術。 但是,當然,這引出了一個問題,我們如何教計算機閱讀? # 向量化文本 機器學習模型(包括深度神經網絡)吸收數字信息并產生數字輸出。 自然語言處理的挑戰自然就變成了將單詞轉換成數字。 我們可以通過多種方式將單詞轉換為數字。 所有這些方法都滿足相同的目標,即將某些單詞序列轉換為數字向量。 有些方法比其他方法更好,因為有時進行轉換時,翻譯中可能會失去一些含義。 # NLP 術語 讓我們從定義一些通用術語開始,以便消除它們使用可能引起的任何歧義。 我知道,由于您可以閱讀,因此您可能會對這些術語有所了解。 如果這看起來很古怪,我深表歉意,但是我保證,這將立即與我們接下來討論的模型有關: * **詞**:我們將使用的大多數系統的原子元素。 盡管確實存在某些字符級模型,但我們今天不再討論它們。 * **句子**:表達陳述,問題等的單詞集合。 * **文檔**:文檔是句子的集合。 它可能是一個句子,或更可能是多個句子。 * **語料庫**:文檔的集合。 # 詞袋模型 **詞袋**(**BoW**)模型是 NLP 模型,實際上忽略了句子結構和單詞放置。 在“單詞袋”模型中,我們將每個文檔視為單詞袋。 很容易想到這一點。 每個文檔都是一個包含大量單詞的容器。 我們忽略句子,結構以及哪個詞排在前或后。 我們對文檔中包含“非常”,“很好”和“不好”這兩個詞的事實感到關注,但是我們并不真正在意“好”而不是“壞”。 詞袋模型很簡單,需要相對較少的數據,并且考慮到該模型的樸素性,其運行效果非常好。 注意,這里使用模型表示表示。 我并不是在特定意義上指深度學習模型或機器學習模型。 相反,在這種情況下,模型是表示文本的一種方式。 給定一個由一組單詞組成的文檔,則需要定義一種策略來將單詞轉換為數字。 稍后我們將介紹幾種策略,但首先我們需要簡要討論詞干,詞形化和停用詞。 # 詞干,詞根去除和停用詞 **詞干**和**詞根去除**是兩種不同但非常相似的技術,它們試圖將每個單詞還原為基本形式,從而簡化了語言模型。 例如,如果要阻止貓的各種形式,我們將在此示例中進行轉換: ```py cat, cats, cat's, cats' -> cat ``` 限制詞法化和詞干化之間的差異成為我們進行此轉換的方式。 提取是通過算法完成的。 當應用于同一個單詞的多種形式時,提取的根在大多數情況下應相同。 這個概念可以與詞條反義化形成對比,詞條反義化使用具有已知基礎的詞匯表并考慮如何使用該詞。 詞干處理通常比詞條化處理快得多。 Porter 提取器在很多情況下都可以很好地工作,因此您可以將其作為提取的第一個安全選擇。 停用詞是在該語言中非常常見的詞,但幾乎沒有語義。 典范示例是`the`一詞。 我在上一句話中只使用了 3 次,但實際上只保留了一次意思。 通常,我們會刪除停用詞,以使輸入內容更加稀疏。 大部分 BoW 模型都受益于詞干,詞根化和刪除停用詞。 有時,我們很快將要討論的詞嵌入模型也可以從詞干提取或詞義化中受益。 詞嵌入模型很少會受益于停用詞的刪除。 # 計數和 TF-IDF 向量化 計數向量化和**詞頻逆文檔頻率**(**TF-IDF**)是兩種策略,將詞袋轉換成適合機器學習算法輸入的特征向量。 計數向量化采用我們的一組單詞,并創建一個向量,其中每個元素代表語料庫詞匯中的一個單詞。 自然,一組文檔中唯一單詞的數量可能會很大,并且許多文檔可能不包含語料庫中存在的單詞的任何實例。 在這種情況下,使用稀疏矩陣表示這些類型的字向量通常是非常明智的。 當一個單詞出現一次或多次時,計數向量化器將簡單地對該單詞出現在文檔中的次數進行計數,然后將該計數放置在代表該單詞的位置。 使用計數向量化器,整個語料庫可以表示為二維矩陣,其中每一行是一個文檔,每一列是一個單詞,然后每個元素就是該單詞在文檔中的計數。 在繼續之前,讓我們先看一個簡單的例子。 想象一個具有兩個文檔的語料庫: ```py docA = "the cat sat on my face" docB = "the dog sat on my bed" ``` 語料庫詞匯為: ```py {'bed', 'cat', 'dog', 'face', 'my', 'on', 'sat', 'the'} ``` 因此,如果我們要為該語料庫創建一個計數嵌入,它將看起來像這樣: | | `bed` | `cat` | `dog` | `face` | `my` | `on` | `sat` | `the` | | --- | --- | --- | --- | --- | --- | --- | --- | --- | | **文件 0** | 0 | 1 | 0 | 1 | 1 | 1 | 1 | 1 | | **文件 1** | 1 | 0 | 1 | 0 | 1 | 1 | 1 | 1 | 這就是計數向量化。 這是我們工具箱中最簡單的向量化技術。 計數向量化的問題在于我們使用了很多根本沒有太多意義的單詞。 實際上,英語中最常用的單詞(`the`)占我們所講單詞的 7%,是第二個最受歡迎的單詞(`of`)出現頻率的兩倍。 語言中單詞的分布是冪律分布,[這是稱為 Zipf 定律的基礎](https://en.wikipedia.org/wiki/Zipf%27s_law)。 如果我們從計數中構造文檔矩陣,那么最終得到的數字將包含很多信息,除非我們的目標是查看誰最經常使用`the`。 更好的策略是根據單詞在文檔中的相對重要性對單詞進行加權。 為此,我們可以使用 TF-IDF。 一個單詞的 TF-IDF 分數是: ![](https://img.kancloud.cn/77/ca/77ca98db0f43c5ebac57c22d8e3ac648_1840x210.png) 在此公式中: ![](https://img.kancloud.cn/92/f0/92f059ef335648768ec71b002287b191_7340x220.png) 這個公式: ![](https://img.kancloud.cn/30/f9/30f96a10981f3d1e77171c3644904991_6880x220.png) 如果我們要為同一語料庫計算 TF-IDF 矩陣,它將看起來像這樣: | | `bed` | `cat` | `dog` | `face` | `my` | `on` | `sat` | `the` | | --- | --- | --- | --- | --- | --- | --- | --- | --- | | **文件 0** | 0 | 0.116 | 0 | 0.116 | 0 | 0 | 0 | 0 | | **文件 1** | 0.116 | 0 | 0.116 | 0 | 0 | 0 | 0 | 0 | 您可能會注意到,通過對單詞頻率乘以逆文檔頻率進行加權,我們取消了所有文檔中出現的單詞,從而放大了不同的單詞。 文件 0 全部關于貓和臉,而文件 1 全部關于狗和床。 這正是我們對許多分類器所要的。 # 詞嵌入 詞袋模型具有一些不理想的屬性,值得注意的是。 我們之前研究過的詞袋模型的第一個問題是它們沒有考慮單詞的上下文。 他們并沒有真正考慮文檔中單詞之間存在的關系。 第二個相關問題是向量空間中單詞的分配有些隨意。 可能無法捕獲有關語料庫詞匯中兩個單詞之間的關系的信息。 例如,雖然鱷魚和鱷魚都是相似的具有許多特征的生物,但已經學會處理鱷魚的單詞的模型幾乎無法利用鱷魚學到的知識(爬行動物學家討厭郵件) 。 最后,由于語料庫的詞匯量可能很大,并且可能不會出現在所有文檔中,因此 BoW 模型往往會產生非常稀疏的向量。 單詞嵌入模型通過為每個單詞學習一個向量來解決這些問題,其中每個語義相似的單詞都映射到(嵌入)附近的點。 另外,與 BoW 模型相比,我們將在更小的向量空間中表示整個詞匯表。 這提供了降維效果,并為我們提供了一個更小,更密集的向量,該向量可以捕獲單詞的語義值。 詞嵌入模型在現實文檔分類問題和語義分析問題中通常比詞袋模型具有很大的提升,因為這種能力可以保留詞相對于語料庫中其他詞的語義值。 # 一個簡單的例子 如果您不熟悉單詞嵌入,那么您現在可能會感到有些迷茫。 掛在那兒,它很快就會變得清晰起來。 讓我們嘗試一個具體的例子。 使用流行的單詞嵌入模型`word2vec`,我們可以從單詞`cat`開始,找到它的 384 元素向量,如以下輸出代碼所示: ```py array([ 5.81600726e-01, 3.07168198e+00, 3.73339128e+00, 2.83814788e-01, 2.79787600e-01, 2.29124355e+00, -2.14855480e+00, -1.22236431e+00, 2.20581269e+00, 1.81546474e+00, 2.06929898e+00, -2.71712840e-01,... ``` 我縮短了輸出,但您明白了。 此模型中的每個單詞都將轉換為 384 個元素的向量。 可以對這些向量進行比較,以評估數據集中單詞的語義相似性。 現在我們有了貓的向量,我將計算狗和蜥蜴的詞向量。 我建議貓比蜥蜴更像狗。 我應該能夠測量貓向量和狗向量之間的距離,然后測量貓向量和蜥蜴向量之間的距離。 盡管有許多方法可以測量向量之間的距離,但余弦相似度可能是單詞向量最常用的方法。 在下表中,我們正在比較貓與狗和蜥蜴的余弦相似度: | | `dog` | `lizard` | | --- | --- | --- | | `cat` | 0.74 | 0.63 | 不出所料,在我們的向量空間中,貓的含義比蜥蜴更接近狗。 # 通過預測學習單詞嵌入 單詞嵌入是通過使用專門為該任務構建的神經網絡來計算的。 我將在這里介紹該網絡的概述。 一旦計算了某些語料庫的詞嵌入,它們便可以輕松地重用于其他應用,因此使該技術成為遷移學習的候選者,類似于我們在第 8 章“使用預先訓練的 CNN 的遷移學習”中介紹的技術。 當我們完成了對該詞嵌入網絡的訓練后,我們網絡中單個隱藏層的權重將成為我們詞嵌入的查找表。 對于詞匯表中的每個單詞,我們將學習該單詞的向量。 該隱藏層將包含比輸入空間少的神經元,從而迫使網絡學習輸入層中存在的信息的壓縮形式。 這種架構非常類似于自編碼器。 但是,該技術圍繞著一項任務,該任務幫助網絡學習向量空間中每個單詞的語義值。 我們將用來訓練嵌入網絡的任務是預測某些目標詞出現在距訓練詞距離窗口內的概率。 例如,如果`koala`是我們的輸入詞,而`marsupials`是我們的目標詞,則我們想知道這兩個詞彼此靠近的可能性。 此任務的輸入層將是詞匯表中每個單詞的一個熱編碼向量。 輸出層將是相同大小的`softmax`層,如下圖所示: ![](https://img.kancloud.cn/86/f8/86f8eb914096c5fc89b796f9000f30dc_499x536.png) 該網絡導致隱藏層的形狀為權重矩陣`[詞匯 x 神經元]`。 例如,如果我們的語料庫中有 20,000 個唯一單詞,而隱藏層中有 300 個神經元,那么我們的隱藏層權重矩陣將為`20,000 x 300`。將這些權重保存到磁盤后,我們將擁有一個 300 元素向量,可用于代表每個詞。 然后,在訓練其他模型時,可以使用這些向量表示單詞。 當然,除此以外,還有更多的訓練詞嵌入網絡的方法,而我故意過分簡化了快速參考樣式。 如果您想了解更多信息,我建議您先閱讀 Mikolov 等人的[《單詞和短語的分布式表示及其組成》](https://papers.nips.cc/paper/5021-distributed-representations-of-words-and-phrases-and-their-compositionality.pdf)。 本文介紹了一種流行的創建單詞嵌入的方法,稱為`word2vec`。 # 通過計數學習單詞嵌入 學習單詞嵌入的另一種方法是通過計數。 [用于詞表示的**全局向量**或 **GloVe** 是 Pennington 等人創建的算法](https://nlp.stanford.edu/projects/glove/)。 GloVe 通過創建單詞共現的非常大的矩陣來工作。 對于某些語料庫,這實際上是兩個單詞彼此相鄰出現的次數的計數。 該算法的作者根據單詞的接近程度來加權此計數,以使彼此接近的單詞對每個計數的貢獻更大。 一旦創建了這個共現矩陣,它將分解為一個較小的空間,從而生成一個單詞 x 特征較大的矩陣。 有趣的是,`word2vec`和 GloVe 的結果非常相似,可以互換使用。 由 60 億個單詞的數據集預先構建的 GloVe 向量由斯坦福大學分發,是單詞向量的常用來源。 本章稍后將使用 GloVe 向量。 # 從文本到文檔 如果您一直在仔細閱讀,您可能會注意到我尚未消除的鴻溝。 詞嵌入模型為每個詞創建一個向量。 相比之下,BoW 模型為每個文檔創建一個向量。 那么,我們如何使用詞嵌入模型進行文檔分類呢? 一種幼稚的方法可能是獲取文檔中所有單詞的向量并計算均值。 我們可能將此值解釋為文檔的平均語義值。 在實踐中,通常使用此解決方案,并且可以產生良好的結果。 但是,它并不總是優于 BoW 嵌入模型。 考慮短語`dog bites man`和`man bites dog`。 希望您會同意我的觀點,這是兩個截然不同的陳述。 但是,如果我們對它們的詞向量進行平均,它們將具有相同的值。 這使我們提出了一些其他策略,可以用來設計文檔中的特征,例如使用每個向量的均值,最大值和最小值。 Le 和 Mikolov 在[《句子和文檔的分布式表示》](https://arxiv.org/abs/1405.4053)中提出了一種從單詞到文檔的更好的想法。 基于`word2vec`的思想,本文將段落標識符添加到我們描述的用于學習單詞向量的神經網絡的輸入中。 使用文本中的單詞以及文檔 ID 可以使網絡學習將可變長度文檔嵌入向量空間中。 該技術稱為 **doc2vec**,它可以很好地用作主題建模以及為模型創建輸入特征的技術。 最后,許多深度學習框架都包含了嵌入層的概念。 嵌入層使您可以了解嵌入空間,這是網絡正在執行的總體任務的一部分。 使用深度神經網絡時,嵌入層可能是向量化文本的最佳選擇。 接下來讓我們看一下嵌入層。 # Keras 嵌入層 **Keras 嵌入層**允許我們學習輸入詞的向量空間表示,就像我們在訓練模型時在`word2vec`中所做的那樣。 使用函數式 API,Keras 嵌入層始終是網絡中的第二層,緊隨輸入層之后。 嵌入層需要以下三個參數: * `input_dim`:語料庫的詞匯量。 * `output_dim`:我們要學習的向量空間的大小。 這將對應于`word2vec`隱藏層中神經元的數量。 * `input_length`:我們將在每次觀察中使用的文字數量。 在下面的示例中,我們將根據需要發送的最長文本使用固定大小,并將較小的文檔填充為 0。 嵌入層將為每個輸入文檔輸出 2D 矩陣,該矩陣包含`input_length`指定的每個單詞的一個向量。 例如,我們可能有一個如下所示的嵌入層: ```py Embedding(input_dim=10000, output_dim=128, input_length=10) ``` 在這種情況下,該層的輸出將是形狀為`10 x 128`的 2D 矩陣,其中每個文檔的 10 個單詞將具有與之關聯的 128 元素向量。 這樣的單詞序列可以作為 LSTM 的出色輸入。 LSTM 層可以緊隨嵌入層。 就像上一章一樣,我們可以將嵌入層中的這 10 行視為 LSTM 的順序輸入。 在本章的第一個示例中,我將使用 LSTM,因此,如果您在未閱讀第 9 章“從頭開始訓練 RNN”的情況下,則請花一點時間重新了解 LSTM 的操作,可以在此處找到。 如果我們想將嵌入層直接連接到密集層,則需要對其進行展平,但您可能不想這樣做。 如果您有序列文本,通常使用 LSTM 是更好的選擇。 我們還有另外一個有趣的選擇。 # 用于自然語言處理的一維 CNN 回顧第 7 章,“從頭開始訓練 CNN”時,我們使用了卷積在圖像區域上滑動窗口以學習復雜的視覺特征。 這使我們能夠學習重要的局部視覺特征,而不管這些特征在圖片中的位置,然后隨著我們的網絡越來越深入,逐步地學習越來越復雜的特征。 我們通常在 2D 或 3D 圖像上使用`3 x 3`或`5 x 5`過濾器。 如果您對卷積層及其工作原理的理解感到生疏,則可能需要閱讀第 7 章“從頭開始訓練 CNN”。 事實證明,我們可以對一系列單詞使用相同的策略。 在這里,我們的 2D 矩陣是嵌入層的輸出。 每行代表一個單詞,并且該行中的所有元素都是其單詞向量。 繼續前面的示例,我們將有一個 10 x 128 的向量,其中連續有 10 個單詞,每個單詞都由 128 個元素的向量空間表示。 我們當然可以在這些單詞上滑動過濾器。 卷積過濾器的大小針對 NLP 問題而改變。 當我們構建網絡來解決 NLP 問題時,我們的過濾器將與單詞向量一樣寬。 過濾器的高度可以變化,通常在 2 到 5 之間。高度為 5 表示我們一次要在五個字上滑動過濾器。 事實證明,對于許多 NLP 問題,CNN 可以很好地運行,并且比 LSTM 快得多。 很難就何時使用 RNN/LSTM 和何時使用 CNN 給出確切的規則。 通常,如果您的問題需要狀態,或者從很遠的序列中學習到一些東西,那么使用 LSTM 可能會更好。 如果您的問題需要檢測描述文本的特定單詞集或文檔的語義感覺,那么 CNN 可能會更快甚至更好地解決您的問題。 # 文檔分類的案例研究 由于我已經提出了兩種可行的文檔分類方法,因此本章將包含兩個單獨的文檔分類示例。 兩者都將使用嵌入層。 一個將使用 LSTM,另一個將使用 CNN。 我們還將比較學習嵌入層與從其他人的權重開始采用遷移學習方法之間的表現。 這兩個示例的代碼都可以在本書的 Git 存儲庫中的`Chapter10`文件夾中找到。 某些數據和 GloVe 向量將需要分別下載。 有關說明,請參見代碼中的注釋。 # Keras 嵌入層和 LSTM 的情感分析 本章的第一個案例研究將演示情緒分析。 在此示例中,我們將應用本章中學到的大多數內容。 我們將使用從**互聯網電影數據庫**(**IMDB**)內置于 Keras 中的數據集。 該數據集包含 25,000 條電影評論,每條評論均按情感標記。 正面評論標記為 1,負面評論標記為 0。此數據集中的每個單詞均已替換為標識該單詞的整數。 每個評論都被編碼為單詞索引序列。 我們的目標是僅使用評論中的文字將電影評論分為正面評論或負面評論。 # 準備數據 因為我們使用的是內置數據集,所以 Keras 會處理大量的日常工作,這些工作涉及標記,詞干,停用詞以及將詞標記轉換為數字標記的工作。 `keras.datasets.imbd`將為我們提供一個列表列表,每個列表包含一個長度可變的整數序列,這些整數表示審閱中的單詞。 我們將使用以下代碼定義數據: ```py def load_data(vocab_size): data = dict() data["vocab_size"] = vocab_size (data["X_train"], data["y_train"]), (data["X_test"], data["y_test"]) = imdb.load_data(num_words=vocab_size) return data ``` 我們可以通過調用`load_data`并為詞匯表選擇最大大小來加載數據。 在此示例中,我將使用 20,000 個單詞作為詞匯量。 如果需要手動執行此操作,以使示例代碼可以解決您自己的問題,則可以使用`keras.preprocessing.text.Tokenizer`類,我們將在下一個示例中介紹該類。 我們將使用以下代碼加載數據: ```py data = load_data(20000) ``` 下一步,我希望這些序列中的每個序列都具有相同的長度,并且我需要此列表列表為 2D 矩陣,其中每個評論是一行,每列是一個單詞。 為了使每個列表大小相同,我將用 0 填充較短的序列。 我們稍后將使用的 LSTM 將學習忽略那些 0,這對于我們當然非常方便。 這種填充操作相當普遍,因此已內置在 Keras 中。 我們可以通過以下代碼使用`keras.preprocessing.sequence.pad_sequences`完成此操作: ```py def pad_sequences(data): data["X_train"] = sequence.pad_sequences(data["X_train"]) data["sequence_length"] = data["X_train"].shape[1] data["X_test"] = sequence.pad_sequences(data["X_test"], maxlen=data["sequence_length"]) return data ``` 調用此函數會將列表列表轉換為等長序列,并方便地將列表列表轉換為 2D 矩陣,如下所示: ```py data = pad_sequences(data) ``` # 輸入和嵌入層架構 在上一章中,我們使用時間序列中的一組滯后訓練了 LSTM。 在這里,我們的滯后實際上是序列中的單詞。 我們將使用這些詞來預測審閱者的情緒。 為了從單詞序列到考慮這些單詞的語義值的輸入向量,我們可以使用嵌入層。 使用 Keras 函數式 API,嵌入層始終是網絡中輸入層之后的第二層。 讓我們看一下這兩層如何結合在一起: ```py input = Input(shape=(sequence_length,), name="Input") embedding = Embedding(input_dim=vocab_size, output_dim=embedding_dim, input_length=sequence_length, name="embedding")(input) ``` 我們的輸入層需要知道序列長度,該長度與輸入矩陣中的列數相對應。 嵌入層將使用輸入層。 但是,它需要知道整體語料庫詞匯量,我們將這些詞嵌入到的向量空間的大小以及序列長度。 我們定義的詞匯量為 20,000 個單詞,數據的序列長度為 2,494,并且指定的嵌入維數為 100。 將所有這些放在一起,嵌入層將從每個文件的 20,000 個輸入熱向量到每個文檔的`2,494 x 100` 2D 矩陣,從而為序列中的每個單詞嵌入向量空間。 隨著模型的學習,嵌入層將不斷學習。 很酷吧? # LSTM 層 我將在這里只使用一個 LSTM 層,只有 10 個神經元,如以下代碼所示: ```py lstm1 = LSTM(10, activation='tanh', return_sequences=False, dropout=0.2, recurrent_dropout=0.2, name='lstm1')(embedding) ``` 為什么要使用這么小的 LSTM 層? 就像您將要看到的那樣,該模型將因過擬合而陷入困境。 甚至只有 10 個 LSTM 單元也能很好地學習訓練數據。 解決此問題的方法可能是添加數據,但實際上不能添加數據,因此保持網絡結構簡單是一個好主意。 這導致我們使用丟棄法。 我將在這一層同時使用丟棄法和經常性丟棄。 我們還沒有談論經常性丟棄的問題,所以讓我們現在解決它。 以這種方式應用于 LSTM 層的常規過濾器將隨機掩蓋 LSTM 的輸入。 循環丟棄會隨機打開和關閉 LSTM 單元/神經元中*展開的*單元之間的內存。 與往常一樣,丟棄是一個超參數,您需要搜索最佳值。 因為我們的輸入是基于文檔的,并且因為沒有任何上下文,所以我們需要記住在文檔之間,這是使用無狀態 LSTM 的絕佳時機。 # 輸出層 在此示例中,我們預測了二元目標。 和以前一樣,我們可以使用具有單個 Sigmoid 神經元的密集層來完成此二分類任務: ```py output = Dense(1, activation='sigmoid', name='sigmoid')(lstm1) ``` # 放在一起 現在,我們了解了組成部分,現在來看整個網絡。 該網絡顯示在以下代碼中,以供您參考: ```py def build_network(vocab_size, embedding_dim, sequence_length): input = Input(shape=(sequence_length,), name="Input") embedding = Embedding(input_dim=vocab_size, output_dim=embedding_dim, input_length=sequence_length, name="embedding")(input) lstm1 = LSTM(10, activation='tanh', return_sequences=False, dropout=0.2, recurrent_dropout=0.2, name='lstm1')(embedding) output = Dense(1, activation='sigmoid', name='sigmoid')(lstm1) model = Model(inputs=input, outputs=output) model.compile(optimizer='adam', loss='binary_crossentropy', metrics=['accuracy']) return model ``` 與其他二分類任務一樣,我們可以使用二元交叉熵。 請注意,因為我們正在將 LSTM 層連接到密集層,所以我們需要將`return_sequences`設置為`False`,正如我們在第 9 章,“從頭訓練”中討論的那樣。 為了使這部分代碼可重用,我們使詞匯量,嵌入維數和序列長度可配置。 如果要搜索超參數,則還可能希望參數化`dropout`,`recurrent_dropout`和 LSTM 神經元的數量。 # 訓練網絡 現在,我的情緒分析網絡已經建立,現在該進行訓練了: ```py data = load_data(20000) data = pad_sequences(data) model = build_network(vocab_size=data["vocab_size"], embedding_dim=100, sequence_length=data["sequence_length"]) callbacks = create_callbacks("sentiment") model.fit(x=data["X_train"], y=data["y_train"], batch_size=32, epochs=10, validation_data=(data["X_test"], data["y_test"]), callbacks=callbacks) ``` 像這樣將我所有的訓練參數和數據保存在一個字典中,實際上只是一個樣式問題,而與函數無關。 您可能希望單獨處理所有事情。 我喜歡對所有內容使用字典,因為它使我無法來回傳遞大量參數。 由于我們使用的是無狀態 LSTM,因此我們將在每個批次中重置單元存儲器。 我的信念是,我們可以在不損失任何罰款的情況下重置文檔之間的單元狀態,因此批量大小實際上與表現有關。 我在這里使用了 32 個觀察批,但是只要 GPU 內存允許,128 個觀察批會產生相似的結果,并且表現會有所提高。 # 表現 從下面的屏幕截圖中,讓我們看一下我們的網絡運行情況。 檢查這些圖時,請密切注意`y`軸上的刻度。 雖然揮桿動作看起來很戲劇性,但幅度并不大: ![](https://img.kancloud.cn/a7/91/a791ca82f8a8e59202f795065831467c_344x688.png) 這里首先要注意的是,在第 1 階段,網絡正在做的相當不錯。 此后,它迅速開始過擬合。 總體而言,我認為我們的結果相當不錯。 在第 1 階段,我們會在驗證集上正確預測約 86% 的時間的情緒。 盡管此案例研究涵蓋了本章到目前為止已討論的許多主題,但讓我們再來看一個可以在嵌入層使用預訓練的單詞向量與我們學習的單詞向量進行比較的地方。 # 有和沒有 GloVe 的文檔分類 在此示例中,我們將使用一個比較著名的文本分類問題,稱為 [**news20**](http://www.cs.cmu.edu/afs/cs.cmu.edu/project/theo-20/www/data/news20.html)。 在此問題中,我們獲得了 19,997 個文檔,每個文檔都屬于一個新聞組。 我們的目標是使用帖子的文本來預測該文本所屬的新聞組。對于我們中間的千禧一代,新聞組是 **Reddit** 的先驅(但可能更接近偉大的 -Reddit 的曾祖父)。 這些新聞組涵蓋的主題差異很大,包括政治,宗教和操作系統等主題,您應避免在禮貌的公司中討論所有這些主題。 這些職位相當長,語料庫中有 174,074 個獨特的單詞。 這次,我將構建模型的兩個版本。 在第一個版本中,我們將使用嵌入層,并且將學習嵌入空間,就像在前面的示例中一樣。 在第二個版本中,我將使用 GloVe 向量作為嵌入層的權重。 然后,我將花一些時間比較和對比這兩種方法。 最后,在此示例中,我們將使用一維 CNN 代替 LSTM。 # 準備數據 當使用這樣的文本文檔時,可能需要很多平凡的代碼才能使您到達想要的位置。 我將這個示例作為解決問題的一種方式。 一旦了解了這里發生的事情,就可以在將來的問題中重用其中的大部分內容并縮短開發時間,因此值得考慮。 以下函數將進入 20 個新聞組文本所在的頂級目錄。 在該目錄中,將有 20 個單獨的目錄,每個目錄都有文件。 每個文件都是新聞組帖子: ```py def load_data(text_data_dir, vocab_size, sequence_length, validation_split=0.2): data = dict() data["vocab_size"] = vocab_size data["sequence_length"] = sequence_length # second, prepare text samples and their labels print('Processing text dataset') texts = [] # list of text samples labels_index = {} # dictionary mapping label name to numeric id labels = [] # list of label ids for name in sorted(os.listdir(text_data_dir)): path = os.path.join(text_data_dir, name) if os.path.isdir(path): label_id = len(labels_index) labels_index[name] = label_id for fname in sorted(os.listdir(path)): if fname.isdigit(): fpath = os.path.join(path, fname) if sys.version_info < (3,): f = open(fpath) else: f = open(fpath, encoding='latin-1') t = f.read() i = t.find('\n\n') # skip header if 0 < i: t = t[i:] texts.append(t) f.close() labels.append(label_id) print('Found %s texts.' % len(texts)) data["texts"] = texts data["labels"] = labels return data ``` 對于每個目錄,我們將使用目錄名稱并將其添加到將其映射為數字的字典中。 這個數字將成為我們想要預測的值,我們的標簽。 我們將把標簽列表保留在`data["labels"]`中。 同樣,對于文本,我們將打開每個文件,僅解析相關文本,而忽略有關誰在信息中張貼的垃圾郵件。 然后,我們將文本存儲在`data["texts"]`中。 順便說一句,刪除標頭中標識新聞組的部分非常重要。 那是作弊! 最后,我們剩下一個文本列表和一個相應的標簽列表。 但是,此時,這些文本都是字符串。 我們需要做的下一件事是將這些字符串拆分為單詞標記,將這些標記轉換為數字標記,并填充序列,以使它們具有相同的長度。 這幾乎是我們在前面的示例中所做的; 但是,在我們之前的示例中,數據已預先加標記。 我將使用此函數來完成任務,如以下代碼所示: ```py def tokenize_text(data): tokenizer = Tokenizer(num_words=data["vocab_size"]) tokenizer.fit_on_texts(data["texts"]) data["tokenizer"] = tokenizer sequences = tokenizer.texts_to_sequences(data["texts"]) word_index = tokenizer.word_index print('Found %s unique tokens.' % len(word_index)) data["X"] = pad_sequences(sequences, maxlen=data["sequence_length"]) data["y"] = to_categorical(np.asarray(data["labels"])) print('Shape of data tensor:', data["X"].shape) print('Shape of label tensor:', data["y"].shape) # texts and labels aren't needed anymore data.pop("texts", None) data.pop("labels", None) return data ``` 在這里,我們獲取該文本列表,并使用`keras.preprocessing.text.Tokenizer`將其標記化。 之后,我們將它們填充為相等的長度。 最后,我們將數字標簽轉換為`one_hot`格式,就像 Keras 在其他多分類問題中一樣。 我們幾乎完成了數據處理。 但是,最后,我們需要獲取文本和標簽,然后將數據隨機分成訓練,驗證和測試集,如以下代碼所示。 我沒有太多數據需要處理,因此我將在此處選擇`test`和`val`。 如果樣本太小,可能無法很好地理解實際模型的表現,因此在執行此操作時要格外小心: ```py def train_val_test_split(data): data["X_train"], X_test_val, data["y_train"], y_test_val = train_test_split(data["X"], data["y"], test_size=0.2, random_state=42) data["X_val"], data["X_test"], data["y_val"], data["y_test"] = train_test_split(X_test_val, y_test_val, test_size=0.25, random_state=42) return data ``` # 加載預訓練的單詞向量 正如我剛才提到的,我將使用 Keras 嵌入層。 對于模型的第二個版本,我們將使用本章前面介紹的 GloVe 字向量來初始化嵌入層的權重。 為此,我們將需要從磁盤加載這些權重,并將它們放入合適的 2D 矩陣中,該層可用作權重。 我們將在這里介紹該操作。 下載 GloVe 向量時,您會發現在將下載文件解壓縮到的目錄中有幾個文本文件。每個文件都對應一組單獨的尺寸。 但是,在所有情況下,這些載體都是使用包含 60 億個唯一單詞的相同通用語料庫開發的(因此標題為`GloVe.6B`)。 我將演示如何使用`glove.6B.100d.txt`文件。 在`glove.6B.100d.txt`中,每行都是單個單詞向量。 在該行上,您將找到該單詞和與其相關聯的 100 維向量。 單詞和向量的元素存儲為文本,并用空格分隔。 為了使這些數據進入可用狀態,我們將從磁盤加載開始。 然后,我們將線分為第一部分,單詞和向量的元素。 完成此操作后,我們將向量轉換為數組。 最后,我們將單詞作為該值的鍵將數組作為值存儲在字典中。 以下代碼說明了此過程: ```py def load_word_vectors(glove_dir): print('Indexing word vectors.') embeddings_index = {} f = open(os.path.join(glove_dir, 'glove.6B.100d.txt'), encoding='utf8') for line in f: values = line.split() word = values[0] coefs = np.asarray(values[1:], dtype='float32') embeddings_index[word] = coefs f.close() print('Found %s word vectors.' % len(embeddings_index)) return embeddings_index ``` 運行此命令后,我們將有一個名為`embeddings_index`的字典,其中包含 GloVe 單詞作為鍵,其向量作為值。 Keras 嵌入層需要 2D 矩陣作為輸入,但是不需要字典,因此我們需要使用以下代碼將字典操縱為矩陣: ```py def embedding_index_to_matrix(embeddings_index, vocab_size, embedding_dim, word_index): print('Preparing embedding matrix.') # prepare embedding matrix num_words = min(vocab_size, len(word_index)) embedding_matrix = np.zeros((num_words, embedding_dim)) for word, i in word_index.items(): if i >= vocab_size: continue embedding_vector = embeddings_index.get(word) if embedding_vector is not None: # words not found in embedding index will be all-zeros. embedding_matrix[i] = embedding_vector return embedding_matrix ``` 我知道所有這些煩惱似乎都是可怕的,但確實如此,但是 GloVe 的作者在如何分配這些單詞向量方面非常有心。 他們希望使使用任何一種編程語言的任何人都可以使用這些向量,為此,文本格式將受到人們的贊賞。 此外,如果您是一名實踐中的數據科學家,您將習慣于此! 現在,我們將向量表示為 2D 矩陣,現在可以在 Keras 嵌入層中使用它們了。 我們的準備工作已經完成,所以現在讓我們建立網絡。 # 輸入和嵌入層架構 我們在這里格式化 API 的方式與前面的示例稍有不同。 這種略有不同的結構將使在嵌入層中使用預訓練向量更加容易。 我們將在以下各節中討論這些結構性更改。 # 沒有 GloVe 向量 讓我們演示沒有先訓練詞向量的`embedding`層的代碼。 此代碼應與上一個示例中的代碼幾乎相同: ```py sequence_input = Input(shape=(sequence_length,), dtype='int32') embedding_layer = Embedding(input_dim=vocab_size, output_dim=embedding_dim, input_length=sequence_length, name="embedding")(sequence_input) ``` # 帶有 GloVe 向量 現在,將其與包含以 2D 矩陣編碼的預先訓練的 GloVe 向量的代碼進行比較: ```py sequence_input = Input(shape=(sequence_length,), dtype='int32') embedding_layer = Embedding(input_dim=vocab_size, output_dim=embedding_dim, weights=[embedding_matrix], input_length=sequence_length, trainable=False, name="embedding")(sequence_input) ``` 在大多數情況下,此代碼看起來是等效的。 有兩個主要區別: * 我們初始化層權重以包含在我們與`weights=[embedding_matrix]`組裝的 GloVe 矩陣中。 * 我們還將層設置為`trainable=False`。 這將阻止我們更新權重。 您可能希望以與微調權重相似的方式微調權重,該方式類似于我們在第 8 章“使用預訓練的 CNN”進行的遷移學習中構建的 CNN,但是在大多數情況下, 不必要或沒有幫助。 # 卷積層 對于一維卷積,層可以使用`keras.layers.Conv1D`。 我們將需要使用`MaxPooling1D`層以及`Conv1D`層,如以下代碼所示: ```py x = Conv1D(128, 5, activation='relu')(embedding_layer) x = MaxPooling1D(5)(x) x = Conv1D(128, 5, activation='relu')(x) x = MaxPooling1D(5)(x) x = Conv1D(128, 5, activation='relu')(x) x = GlobalMaxPooling1D()(x) ``` 對于`Conv1D`層,第一個整數參數是單元數,第二個是過濾器大小。 我們的過濾器只有一維,因此命名為 1D 卷積。 上例中的窗口大小為 5。 我正在使用的`MaxPooling1D`層也將使用 5 的窗口大小。相同的規則適用于一維實現中的池化層。 在最后一個卷積層之后,我們應用`GlobalMaxPooling1D`層。 該層是最大池化的特殊實現,它將獲取最后一個`Conv1D`層(一個`[batch x 35 x 128]`張量)的輸出,并跨時間步長將其合并到`[batch x 128]`。 這通常是在 NLP 網絡中完成的,其目的類似于在基于圖像的卷積網絡中使用`Flatten()`層。 該層充當卷積層和密集層之間的橋梁。 # 輸出層 此示例中的輸出層看起來像其他任何多分類。 我在輸出層之前也包括了一個密集層,如以下代碼所示: ```py x = Dense(128, activation='relu')(x) preds = Dense(20, activation='softmax')(x) ``` # 放在一起 和以前一樣,我們將在此處顯示整個神經網絡結構。 請注意,此結構適用于包含 GloVe 向量的模型版本: ```py def build_model(vocab_size, embedding_dim, sequence_length, embedding_matrix): sequence_input = Input(shape=(sequence_length,), dtype='int32') embedding_layer = Embedding(input_dim=vocab_size, output_dim=embedding_dim, weights=[embedding_matrix], input_length=sequence_length, trainable=False, name="embedding")(sequence_input) x = Conv1D(128, 5, activation='relu')(embedding_layer) x = MaxPooling1D(5)(x) x = Conv1D(128, 5, activation='relu')(x) x = MaxPooling1D(5)(x) x = Conv1D(128, 5, activation='relu')(x) x = GlobalMaxPooling1D()(x) x = Dense(128, activation='relu')(x) preds = Dense(20, activation='softmax')(x) model = Model(sequence_input, preds) model.compile(loss='categorical_crossentropy', optimizer='adam', metrics=['accuracy']) return model ``` 我在這里再次使用`adam`,`categorical_crossentropy`和`accuracy`。 盡管本章介紹了許多新主題,但希望能看到保持不變的感覺會有些安慰。 # 訓練 將所有代碼放在一起,只需幾行就可以完成訓練,如以下代碼所示: ```py glove_dir = os.path.join(BASE_DIR, 'glove.6B') text_data_dir = os.path.join(BASE_DIR, '20_newsgroup') embeddings_index = load_word_vectors(glove_dir) data = load_data(text_data_dir, vocab_size=20000, sequence_length=1000) data = tokenize_text(data) data = train_val_test_split(data) data["embedding_dim"] = 100 data["embedding_matrix"] = embedding_index_to_matrix(embeddings_index=embeddings_index, vocab_size=data["vocab_size"], embedding_dim=data["embedding_dim"], word_index=data["tokenizer"].word_index) callbacks = create_callbacks("newsgroups-pretrained") model = build_model(vocab_size=data["vocab_size"], embedding_dim=data['embedding_dim'], sequence_length=data['sequence_length'], embedding_matrix=data['embedding_matrix']) model.fit(data["X_train"], data["y_train"], batch_size=128, epochs=10, validation_data=(data["X_val"], data["y_val"]), callbacks=callbacks) ``` 請注意,我們只訓練 10 個周期,因此將這個問題的損失降到最低不會花很長時間。 # 表現 而我們在這里處于關鍵時刻。 讓我們看看我的表現如何。 更重要的是,讓我們將 GloVe 向量與該問題的學習向量進行比較。 以下屏幕截圖中的橙色線對應于學習的嵌入層,藍色線對應于 GloVe 向量: ![](https://img.kancloud.cn/2c/9f/2c9fb428dc1e897e5313a0aced5a4c18_339x604.png) GloVe 預先訓練的網絡不僅學習得更快,而且在每個周期都表現得更好。 總體而言,這些網絡似乎在學習文檔分類任務方面做得很好。 大約在第五個周期之后,它們都開始過擬合。 但是,GloVe 模型比沒有使用 GloVe 訓練的網絡更能防止過擬合。 通常,我建議盡可能在任何地方使用遷移學習。 圖片和文字都是如此。 如果通過這些示例與我一起工作,我建議您對 LSTM 嘗試同樣的問題。 我認為使用 LSTM 時,您會發現該問題更加難以解決,并且難以解決過擬合問題。 # 總結 在本章中,我們以一般形式以及在情感分析的特定情況下研究了文檔分類。 在此過程中,我們涵蓋了很多 NLP 主題,包括 Word 袋模型,向量空間模型以及每個模型的相對優點。 我們還研究了使用 LSTM 和 1D 卷積進行文本分析。 最后,我們訓練了兩個單獨的文檔分類器,并通過實際示例應用了我們討論的所有內容。 在下一章中,我們將討論一個非常酷的自然語言模型,該模型將允許我們實際生成單詞,稱為**序列到序列模型**。
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看