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

                ThinkChat2.0新版上線,更智能更精彩,支持會話、畫圖、視頻、閱讀、搜索等,送10W Token,即刻開啟你的AI之旅 廣告
                # 6\. 學習分類文本 模式識別是自然語言處理的一個核心部分。以-ed 結尾的詞往往是過去時態動詞([5.](./ch05.html#chap-tag))。頻繁使用 will 是新聞文本的暗示([3](./ch03.html#chap-words))。這些可觀察到的模式——詞的結構和詞頻——恰好與特定方面的含義關聯,如時態和主題。但我們怎么知道從哪里開始尋找,形式的哪一方面關聯含義的哪一方面? 本章的目的是要回答下列問題: 1. 我們怎樣才能識別語言數據中能明顯用于對其分類的特征? 2. 我們怎樣才能構建語言模型,用于自動執行語言處理任務? 3. 從這些模型中我們可以學到哪些關于語言的知識? 一路上,我們將研究一些重要的機器學習技術,包括決策樹、樸素貝葉斯分類器和最大熵分類。我們會掩蓋這些技術的數學和統計的基礎,集中關注如何以及何時使用它們(更多的技術背景知識見進一步閱讀一節)。在看這些方法之前,我們首先需要知道這個主題的范圍十分廣泛。 ## 1 有監督分類 分類是為給定的輸入選擇正確的類標簽的任務。在基本的分類任務中,每個輸入被認為是與所有其它輸入隔離的,并且標簽集是預先定義的。這里是分類任務的一些例子: * 判斷一封電子郵件是否是垃圾郵件。 * 從一個固定的主題領域列表中,如“體育”、“技術”和“政治”,決定新聞報道的主題是什么。 * 決定詞 bank 給定的出現是用來指河的坡岸、一個金融機構、向一邊傾斜的動作還是在金融機構里的存儲行為。 基本的分類任務有許多有趣的變種。例如,在多類分類中,每個實例可以分配多個標簽;在開放性分類中,標簽集是事先沒有定義的;在序列分類中,一個輸入列表作為一個整體分類。 一個分類稱為有監督的,如果它的建立基于訓練語料的每個輸入包含正確標簽。有監督分類使用的框架圖如[1.1](./ch06.html#fig-supervised-classification)所示。 ![Images/supervised-classification.png](https://img.kancloud.cn/0d/1b/0d1b4e11f6f8a06c0b5802c78ece54d2_913x471.jpg) 圖 1.1:有監督分類。(a)在訓練過程中,特征提取器用來將每一個輸入值轉換為特征集。這些特征集捕捉每個輸入中應被用于對其分類的基本信息,我們將在下一節中討論它。特征集與標簽的配對被送入機器學習算法,生成模型。(b)在預測過程中,相同的特征提取器被用來將未見過的輸入轉換為特征集。之后,這些特征集被送入模型產生預測標簽。 在本節的其余部分,我們將著眼于分類器如何能夠解決各種各樣的任務。我們討論的目的不是要范圍全面,而是給出在文本分類器的幫助下執行的任務的一個代表性的例子。 ## 1.1 性別鑒定 在[4](./ch02.html#sec-lexical-resources)中,我們看到,男性和女性的名字有一些鮮明的特點。以 a,e 和 i 結尾的很可能是女性,而以 k,o,r,s 和 t 結尾的很可能是男性。讓我們建立一個分類器更精確地模擬這些差異。 創建一個分類器的第一步是決定輸入的什么樣的特征是相關的,以及如何為那些特征編碼。在這個例子中,我們一開始只是尋找一個給定的名稱的最后一個字母。以下特征提取器函數建立一個字典,包含有關給定名稱的相關信息: ```py >>> def gender_features(word): ... return {'last_letter': word[-1]} >>> gender_features('Shrek') {'last_letter': 'k'} ``` 這個函數返回的字典被稱為特征集,映射特征名稱到它們的值。特征名稱是區分大小寫的字符串,通常提供一個簡短的人可讀的特征描述,例如本例中的`'last_letter'`。特征值是簡單類型的值,如布爾、數字和字符串。 注意 大多數分類方法要求特征使用簡單的類型進行編碼,如布爾類型、數字和字符串。但要注意僅僅因為一個特征是簡單類型,并不一定意味著該特征的值易于表達或計算。的確,它可以用非常復雜的和有信息量的值作為特征,如第 2 個有監督分類器的輸出。 現在,我們已經定義了一個特征提取器,我們需要準備一個例子和對應類標簽的列表。 ```py >>> from nltk.corpus import names >>> labeled_names = ([(name, 'male') for name in names.words('male.txt')] + ... [(name, 'female') for name in names.words('female.txt')]) >>> import random >>> random.shuffle(labeled_names) ``` 接下來,我們使用特征提取器處理`names`數據,并劃分特征集的結果鏈表為一個訓練集和一個測試集。訓練集用于訓練一個新的“樸素貝葉斯”分類器。 ```py >>> featuresets = [(gender_features(n), gender) for (n, gender) in labeled_names] >>> train_set, test_set = featuresets[500:], featuresets[:500] >>> classifier = nltk.NaiveBayesClassifier.train(train_set) ``` 在本章的后面,我們將學習更多關于樸素貝葉斯分類器的內容。現在,讓我們只是在上面測試一些沒有出現在訓練數據中的名字: ```py >>> classifier.classify(gender_features('Neo')) 'male' >>> classifier.classify(gender_features('Trinity')) 'female' ``` 請看 _《黑客帝國》_ 中這些角色的名字被正確分類。盡管這部科幻電影的背景是在 2199 年,但它仍然符合我們有關名字和性別的預期。我們可以在大數據量的未見過的數據上系統地評估這個分類器: ```py >>> print(nltk.classify.accuracy(classifier, test_set)) 0.77 ``` 最后,我們可以檢查分類器,確定哪些特征對于區分名字的性別是最有效的: ```py >>> classifier.show_most_informative_features(5) Most Informative Features last_letter = 'a' female : male = 33.2 : 1.0 last_letter = 'k' male : female = 32.6 : 1.0 last_letter = 'p' male : female = 19.7 : 1.0 last_letter = 'v' male : female = 18.6 : 1.0 last_letter = 'f' male : female = 17.3 : 1.0 ``` 此列表顯示訓練集中以 a 結尾的名字中女性是男性的 38 倍,而以 k 結尾名字中男性是女性的 31 倍。這些比率稱為似然比,可以用于比較不同特征-結果關系。 注意 **輪到你來:**修改`gender_features()`函數,為分類器提供名稱的長度、它的第一個字母以及任何其他看起來可能有用的特征。用這些新特征重新訓練分類器,并測試其準確性。 在處理大型語料庫時,構建一個包含每一個實例的特征的單獨的列表會使用大量的內存。在這些情況下,使用函數`nltk.classify.apply_features`,返回一個行為像一個列表而不會在內存存儲所有特征集的對象: ```py >>> from nltk.classify import apply_features >>> train_set = apply_features(gender_features, labeled_names[500:]) >>> test_set = apply_features(gender_features, labeled_names[:500]) ``` ## 1.2 選擇正確的特征 選擇相關的特征,并決定如何為一個學習方法編碼它們,這對學習方法提取一個好的模型可以產生巨大的影響。建立一個分類器的很多有趣的工作之一是找出哪些特征可能是相關的,以及我們如何能夠表示它們。雖然使用相當簡單而明顯的特征集往往可以得到像樣的性能,但是使用精心構建的基于對當前任務的透徹理解的特征,通常會顯著提高收益。 典型地,特征提取通過反復試驗和錯誤的過程建立的,由哪些信息是與問題相關的直覺指引的。它通常以“廚房水槽”的方法開始,包括你能想到的所有特征,然后檢查哪些特征是實際有用的。我們在[1.2](./ch06.html#code-gender-features-overfitting)中對名字性別特征采取這種做法。 ```py def gender_features2(name): features = {} features["first_letter"] = name[0].lower() features["last_letter"] = name[-1].lower() for letter in 'abcdefghijklmnopqrstuvwxyz': features["count({})".format(letter)] = name.lower().count(letter) features["has({})".format(letter)] = (letter in name.lower()) return features ``` 然而,你要用于一個給定的學習算法的特征的數目是有限的——如果你提供太多的特征,那么該算法將高度依賴你的訓練數據的特性,而一般化到新的例子的效果不會很好。這個問題被稱為過擬合,當運作在小訓練集上時尤其會有問題。例如,如果我們使用[1.2](./ch06.html#code-gender-features-overfitting)中所示的特征提取器訓練樸素貝葉斯分類器,將會過擬合這個相對較小的訓練集,造成這個系統的精度比只考慮每個名字最后一個字母的分類器的精度低約 1%: ```py >>> featuresets = [(gender_features2(n), gender) for (n, gender) in labeled_names] >>> train_set, test_set = featuresets[500:], featuresets[:500] >>> classifier = nltk.NaiveBayesClassifier.train(train_set) >>> print(nltk.classify.accuracy(classifier, test_set)) 0.768 ``` 一旦初始特征集被選定,完善特征集的一個非常有成效的方法是錯誤分析。首先,我們選擇一個開發集,包含用于創建模型的語料數據。然后將這種開發集分為訓練集和開發測試集。 ```py >>> train_names = labeled_names[1500:] >>> devtest_names = labeled_names[500:1500] >>> test_names = labeled_names[:500] ``` 訓練集用于訓練模型,開發測試集用于進行錯誤分析。測試集用于系統的最終評估。由于下面討論的原因,我們將一個單獨的開發測試集用于錯誤分析而不是使用測試集是很重要的。在[1.3](./ch06.html#fig-corpus-org)中顯示了將語料數據劃分成不同的子集。 ![Images/corpus-org.png](https://img.kancloud.cn/df/7c/df7c6a21bc75a078f7b28b1cc7b9ab7f_638x332.jpg) 圖 1.3:用于訓練有監督分類器的語料數據組織圖。語料數據分為兩類:開發集和測試集。開發集通常被進一步分為訓練集和開發測試集。 已經將語料分為適當的數據集,我們使用訓練集訓練一個模型[![[1]](https://img.kancloud.cn/97/aa/97aa34f1d446f0c464068d0711295a9a_15x15.jpg)](./ch06.html#err-analysis-train),然后在開發測試集上運行[![[2]](https://img.kancloud.cn/c7/9c/c79c435fbd088cae010ca89430cd9f0c_15x15.jpg)](./ch06.html#err-analysis-run)。 ```py >>> train_set = [(gender_features(n), gender) for (n, gender) in train_names] >>> devtest_set = [(gender_features(n), gender) for (n, gender) in devtest_names] >>> test_set = [(gender_features(n), gender) for (n, gender) in test_names] >>> classifier = nltk.NaiveBayesClassifier.train(train_set) ![[1]](https://img.kancloud.cn/97/aa/97aa34f1d446f0c464068d0711295a9a_15x15.jpg) >>> print(nltk.classify.accuracy(classifier, devtest_set)) ![[2]](https://img.kancloud.cn/c7/9c/c79c435fbd088cae010ca89430cd9f0c_15x15.jpg) 0.75 ``` 使用開發測試集,我們可以生成一個分類器預測名字性別時的錯誤列表: ```py >>> errors = [] >>> for (name, tag) in devtest_names: ... guess = classifier.classify(gender_features(name)) ... if guess != tag: ... errors.append( (tag, guess, name) ) ``` 然后,可以檢查個別錯誤案例,在那里該模型預測了錯誤的標簽,嘗試確定什么額外信息將使其能夠作出正確的決定(或者現有的哪部分信息導致其做出錯誤的決定)。然后可以相應的調整特征集。我們已經建立的名字分類器在開發測試語料上產生約 100 個錯誤: ```py >>> for (tag, guess, name) in sorted(errors): ... print('correct={:<8} guess={:<8s} name={:<30}'.format(tag, guess, name)) correct=female guess=male name=Abigail ... correct=female guess=male name=Cindelyn ... correct=female guess=male name=Katheryn correct=female guess=male name=Kathryn ... correct=male guess=female name=Aldrich ... correct=male guess=female name=Mitch ... correct=male guess=female name=Rich ... ``` 瀏覽這個錯誤列表,它明確指出一些多個字母的后綴可以指示名字性別。例如,yn 結尾的名字顯示以女性為主,盡管事實上,n 結尾的名字往往是男性;以 ch 結尾的名字通常是男性,盡管以 h 結尾的名字傾向于是女性。因此,調整我們的特征提取器包括兩個字母后綴的特征: ```py >>> def gender_features(word): ... return {'suffix1': word[-1:], ... 'suffix2': word[-2:]} ``` 使用新的特征提取器重建分類器,我們看到測試數據集上的性能提高了近 3 個百分點(從 76.5%到 78.2%): ```py >>> train_set = [(gender_features(n), gender) for (n, gender) in train_names] >>> devtest_set = [(gender_features(n), gender) for (n, gender) in devtest_names] >>> classifier = nltk.NaiveBayesClassifier.train(train_set) >>> print(nltk.classify.accuracy(classifier, devtest_set)) 0.782 ``` 這個錯誤分析過程可以不斷重復,檢查存在于由新改進的分類器產生的錯誤中的模式。每一次錯誤分析過程被重復,我們應該選擇一個不同的開發測試/訓練分割,以確保該分類器不會開始反映開發測試集的特質。 但是,一旦我們已經使用了開發測試集幫助我們開發模型,關于這個模型在新數據會表現多好,我們將不能再相信它會給我們一個準確地結果。因此,保持測試集分離、未使用過,直到我們的模型開發完畢是很重要的。在這一點上,我們可以使用測試集評估模型在新的輸入值上執行的有多好。 ## 1.3 文檔分類 在[1](./ch02.html#sec-extracting-text-from-corpora)中,我們看到了語料庫的幾個例子,那里文檔已經按類別標記。使用這些語料庫,我們可以建立分類器,自動給新文檔添加適當的類別標簽。首先,我們構造一個標記了相應類別的文檔清單。對于這個例子,我們選擇電影評論語料庫,將每個評論歸類為正面或負面。 ```py >>> from nltk.corpus import movie_reviews >>> documents = [(list(movie_reviews.words(fileid)), category) ... for category in movie_reviews.categories() ... for fileid in movie_reviews.fileids(category)] >>> random.shuffle(documents) ``` 接下來,我們為文檔定義一個特征提取器,這樣分類器就會知道哪些方面的數據應注意([1.4](./ch06.html#code-document-classify-fd))。對于文檔主題識別,我們可以為每個詞定義一個特性表示該文檔是否包含這個詞。為了限制分類器需要處理的特征的數目,我們一開始構建一個整個語料庫中前 2000 個最頻繁詞的列表[![[1]](https://img.kancloud.cn/97/aa/97aa34f1d446f0c464068d0711295a9a_15x15.jpg)](./ch06.html#document-classify-all-words)。然后,定義一個特征提取器[![[2]](https://img.kancloud.cn/c7/9c/c79c435fbd088cae010ca89430cd9f0c_15x15.jpg)](./ch06.html#document-classify-extractor),簡單地檢查這些詞是否在一個給定的文檔中。 ```py all_words = nltk.FreqDist(w.lower() for w in movie_reviews.words()) word_features = list(all_words)[:2000] ![[1]](https://img.kancloud.cn/97/aa/97aa34f1d446f0c464068d0711295a9a_15x15.jpg) def document_features(document): ![[2]](https://img.kancloud.cn/c7/9c/c79c435fbd088cae010ca89430cd9f0c_15x15.jpg) document_words = set(document) ![[3]](https://img.kancloud.cn/69/fc/69fcb1188781ff9f726d82da7988a139_15x15.jpg) features = {} for word in word_features: features['contains({})'.format(word)] = (word in document_words) return features ``` 注意 在[![[3]](https://img.kancloud.cn/69/fc/69fcb1188781ff9f726d82da7988a139_15x15.jpg)](./ch06.html#document-classify-set)中我們計算文檔的所有詞的集合,而不僅僅檢查是否`word in document`,因為檢查一個詞是否在一個集合中出現比檢查它是否在一個列表中出現要快的多([4.7](./ch04.html#sec-algorithm-design))。 現在,我們已經定義了我們的特征提取器,可以用它來訓練一個分類器,為新的電影評論加標簽([1.5](./ch06.html#code-document-classify-use))。為了檢查產生的分類器可靠性如何,我們在測試集上計算其準確性[![[1]](https://img.kancloud.cn/97/aa/97aa34f1d446f0c464068d0711295a9a_15x15.jpg)](./ch06.html#document-classify-test)。再一次的,我們可以使用`show_most_informative_features()`來找出哪些特征是分類器發現最有信息量的[![[2]](https://img.kancloud.cn/c7/9c/c79c435fbd088cae010ca89430cd9f0c_15x15.jpg)](./ch06.html#document-classify-smif)。 ```py featuresets = [(document_features(d), c) for (d,c) in documents] train_set, test_set = featuresets[100:], featuresets[:100] classifier = nltk.NaiveBayesClassifier.train(train_set) ``` 顯然在這個語料庫中,提到 Seagal 的評論中負面的是正面的大約 8 倍,而提到 Damon 的評論中正面的是負面的大約 6 倍。 ## 1.4 詞性標注 第[5.](./ch05.html#chap-tag)中,我們建立了一個正則表達式標注器,通過查找詞內部的組成,為詞選擇詞性標記。然而,這個正則表達式標注器是手工制作的。作為替代,我們可以訓練一個分類器來算出哪個后綴最有信息量。首先,讓我們找出最常見的后綴: ```py >>> from nltk.corpus import brown >>> suffix_fdist = nltk.FreqDist() >>> for word in brown.words(): ... word = word.lower() ... suffix_fdist[word[-1:]] += 1 ... suffix_fdist[word[-2:]] += 1 ... suffix_fdist[word[-3:]] += 1 ``` ```py >>> common_suffixes = [suffix for (suffix, count) in suffix_fdist.most_common(100)] >>> print(common_suffixes) ['e', ',', '.', 's', 'd', 't', 'he', 'n', 'a', 'of', 'the', 'y', 'r', 'to', 'in', 'f', 'o', 'ed', 'nd', 'is', 'on', 'l', 'g', 'and', 'ng', 'er', 'as', 'ing', 'h', 'at', 'es', 'or', 're', 'it', '``', 'an', "''", 'm', ';', 'i', 'ly', 'ion', ...] ``` 接下來,我們將定義一個特征提取器函數,檢查給定的單詞的這些后綴: ```py >>> def pos_features(word): ... features = {} ... for suffix in common_suffixes: ... features['endswith({})'.format(suffix)] = word.lower().endswith(suffix) ... return features ``` 特征提取函數的行為就像有色眼鏡一樣,強調我們的數據中的某些屬性(顏色),并使其無法看到其他屬性。分類器在決定如何標記輸入時,將完全依賴它們強調的屬性。在這種情況下,分類器將只基于一個給定的詞擁有(如果有)哪個常見后綴的信息來做決定。 現在,我們已經定義了我們的特征提取器,可以用它來訓練一個新的“決策樹”的分類器(將在[4](./ch06.html#sec-decision-trees)討論): ```py >>> tagged_words = brown.tagged_words(categories='news') >>> featuresets = [(pos_features(n), g) for (n,g) in tagged_words] ``` ```py >>> size = int(len(featuresets) * 0.1) >>> train_set, test_set = featuresets[size:], featuresets[:size] ``` ```py >>> classifier = nltk.DecisionTreeClassifier.train(train_set) >>> nltk.classify.accuracy(classifier, test_set) 0.62705121829935351 ``` ```py >>> classifier.classify(pos_features('cats')) 'NNS' ``` 決策樹模型的一個很好的性質是它們往往很容易解釋——我們甚至可以指示 NLTK 將它們以偽代碼形式輸出: ```py >>> print(classifier.pseudocode(depth=4)) if endswith(,) == True: return ',' if endswith(,) == False: if endswith(the) == True: return 'AT' if endswith(the) == False: if endswith(s) == True: if endswith(is) == True: return 'BEZ' if endswith(is) == False: return 'VBZ' if endswith(s) == False: if endswith(.) == True: return '.' if endswith(.) == False: return 'NN' ``` 在這里,我們可以看到分類器一開始檢查一個詞是否以逗號結尾——如果是,它會得到一個特別的標記`","`。接下來,分類器檢查詞是否以`"the"`尾,這種情況它幾乎肯定是一個限定詞。這個“后綴”被決策樹早早使用是因為詞"the"太常見。分類器繼續檢查詞是否以"s"結尾。如果是,那么它極有可能得到動詞標記`VBZ`(除非它是這個詞"is",它有特殊標記`BEZ`),如果不是,那么它往往是名詞(除非它是標點符號“.”)。實際的分類器包含這里顯示的 if-then 語句下面進一步的嵌套,參數`depth=4` 只顯示決策樹的頂端部分。 ## 1.5 探索上下文語境 通過增加特征提取函數,我們可以修改這個詞性標注器來利用各種詞內部的其他特征,例如詞長、它所包含的音節數或者它的前綴。然而,只要特征提取器僅僅看著目標詞,我們就沒法添加依賴詞出現的 _ 上下文語境 _ 特征。然而上下文語境特征往往提供關于正確標記的強大線索——例如,標注詞"fly",如果知道它前面的詞是“a”將使我們能夠確定它是一個名詞,而不是一個動詞。 為了采取基于詞的上下文的特征,我們必須修改以前為我們的特征提取器定義的模式。不是只傳遞已標注的詞,我們將傳遞整個(未標注的)句子,以及目標詞的索引。[1.6](./ch06.html#code-suffix-pos-tag)演示了這種方法,使用依賴上下文的特征提取器定義一個詞性標記分類器。 ```py def pos_features(sentence, i): ![[1]](https://img.kancloud.cn/97/aa/97aa34f1d446f0c464068d0711295a9a_15x15.jpg) features = {"suffix(1)": sentence[i][-1:], "suffix(2)": sentence[i][-2:], "suffix(3)": sentence[i][-3:]} if i == 0: features["prev-word"] = "<START>" else: features["prev-word"] = sentence[i-1] return features ``` 很顯然,利用上下文特征提高了我們的詞性標注器的準確性。例如,分類器學到一個詞如果緊跟在詞"large"或"gubernatorial"后面,極可能是名詞。然而,它無法學到,一個詞如果它跟在形容詞后面可能是名詞,這樣更一般的,因為它沒有獲得前面這個詞的詞性標記。在一般情況下,簡單的分類器總是將每一個輸入與所有其他輸入獨立對待。在許多情況下,這非常有道理。例如,關于名字是否傾向于男性或女性的決定可以通過具體分析來做出。然而,有很多情況,如詞性標注,我們感興趣的是解決彼此密切相關的分類問題。 ## 1.6 序列分類 為了捕捉相關的分類任務之間的依賴關系,我們可以使用聯合分類器模型,收集有關輸入,選擇適當的標簽。在詞性標注的例子中,各種不同的序列分類器模型可以被用來為一個給定的句子中的所有的詞共同選擇詞性標簽。 一種序列分類器策略,稱為連續分類或貪婪序列分類,是為第一個輸入找到最有可能的類標簽,然后使用這個問題的答案幫助找到下一個輸入的最佳的標簽。這個過程可以不斷重復直到所有的輸入都被貼上標簽。這是[5](./ch05.html#sec-n-gram-tagging)中的二元標注器采用的方法,它一開始為句子的第一個詞選擇詞性標記,然后為每個隨后的詞選擇標記,基于詞本身和前面詞的預測的標記。 在[1.7](./ch06.html#code-consecutive-pos-tagger)中演示了這一策略。首先,我們必須擴展我們的特征提取函數使其具有參數`history`,它提供一個我們到目前為止已經為句子預測的標記的列表[![[1]](https://img.kancloud.cn/97/aa/97aa34f1d446f0c464068d0711295a9a_15x15.jpg)](./ch06.html#consec-pos-tag-features)。`history`中的每個標記對應`sentence`中的一個詞。但是請注意,`history`將只包含我們已經歸類的詞的標記,也就是目標詞左側的詞。因此,雖然是有可能查看目標詞右邊的詞的某些特征,但查看那些詞的標記是不可能的(因為我們還未產生它們)。 已經定義了特征提取器,我們可以繼續建立我們的序列分類器[![[2]](https://img.kancloud.cn/c7/9c/c79c435fbd088cae010ca89430cd9f0c_15x15.jpg)](./ch06.html#consec-pos-tagger)。在訓練中,我們使用已標注的標記為征提取器提供適當的歷史信息,但標注新的句子時,我們基于標注器本身的輸出產生歷史信息。 ```py def pos_features(sentence, i, history): ![[1]](https://img.kancloud.cn/97/aa/97aa34f1d446f0c464068d0711295a9a_15x15.jpg) features = {"suffix(1)": sentence[i][-1:], "suffix(2)": sentence[i][-2:], "suffix(3)": sentence[i][-3:]} if i == 0: features["prev-word"] = "<START>" features["prev-tag"] = "<START>" else: features["prev-word"] = sentence[i-1] features["prev-tag"] = history[i-1] return features class ConsecutivePosTagger(nltk.TaggerI): ![[2]](https://img.kancloud.cn/c7/9c/c79c435fbd088cae010ca89430cd9f0c_15x15.jpg) def __init__(self, train_sents): train_set = [] for tagged_sent in train_sents: untagged_sent = nltk.tag.untag(tagged_sent) history = [] for i, (word, tag) in enumerate(tagged_sent): featureset = pos_features(untagged_sent, i, history) train_set.append( (featureset, tag) ) history.append(tag) self.classifier = nltk.NaiveBayesClassifier.train(train_set) def tag(self, sentence): history = [] for i, word in enumerate(sentence): featureset = pos_features(sentence, i, history) tag = self.classifier.classify(featureset) history.append(tag) return zip(sentence, history) ``` ## 1.7 其他序列分類方法 這種方法的一個缺點是我們的決定一旦做出無法更改。例如,如果我們決定將一個詞標注為名詞,但后來發現的證據表明應該是一個動詞,就沒有辦法回去修復我們的錯誤。這個問題的一個解決方案是采取轉型策略。轉型聯合分類的工作原理是為輸入的標簽創建一個初始值,然后反復提煉那個值,嘗試修復相關輸入之間的不一致。Brill 標注器,[(1)](./ch05.html#sec-transformation-based-tagging)中描述的,是這種策略的一個很好的例子。 另一種方案是為詞性標記所有可能的序列打分,選擇總得分最高的序列。隱馬爾可夫模型就采取這種方法。隱馬爾可夫模型類似于連續分類器,它不光看輸入也看已預測標記的歷史。然而,不是簡單地找出一個給定的詞的單個最好的標簽,而是為標記產生一個概率分布。然后將這些概率結合起來計算標記序列的概率得分,最高概率的標記序列會被選中。不幸的是,可能的標簽序列的數量相當大。給定 30 個標簽的標記集,有大約 600 萬億(30&lt;sup&gt;10&lt;/sup&gt;)種方式來標記一個 10 個詞的句子。為了避免單獨考慮所有這些可能的序列,隱馬爾可夫模型要求特征提取器只看最近的標記(或最近的 n 個標記,其中 n 是相當小的)。由于這種限制,它可以使用動態規劃([4.7](./ch04.html#sec-algorithm-design)),有效地找出最有可能的標記序列。特別是,對每個連續的詞索引 i,每個可能的當前及以前的標記都被計算得分。這種同樣基礎的方法被兩個更先進的模型所采用,它們被稱為最大熵馬爾可夫模型和線性鏈條件隨機場模型;但為標記序列打分用的是不同的算法。 ## 2 有監督分類的更多例子 ## 2.1 句子分割 句子分割可以看作是一個標點符號的分類任務:每當我們遇到一個可能會結束一個句子的符號,如句號或問號,我們必須決定它是否終止了當前句子。 第一步是獲得一些已被分割成句子的數據,將它轉換成一種適合提取特征的形式: ```py >>> sents = nltk.corpus.treebank_raw.sents() >>> tokens = [] >>> boundaries = set() >>> offset = 0 >>> for sent in sents: ... tokens.extend(sent) ... offset += len(sent) ... boundaries.add(offset-1) ``` 在這里,`tokens`是單獨句子標識符的合并列表,`boundaries`是一個包含所有句子邊界詞符索引的集合。下一步,我們需要指定用于決定標點是否表示句子邊界的數據特征: ```py >>> def punct_features(tokens, i): ... return {'next-word-capitalized': tokens[i+1][0].isupper(), ... 'prev-word': tokens[i-1].lower(), ... 'punct': tokens[i], ... 'prev-word-is-one-char': len(tokens[i-1]) == 1} ``` 基于這一特征提取器,我們可以通過選擇所有的標點符號創建一個加標簽的特征集的列表,然后標注它們是否是邊界標識符: ```py >>> featuresets = [(punct_features(tokens, i), (i in boundaries)) ... for i in range(1, len(tokens)-1) ... if tokens[i] in '.?!'] ``` 使用這些特征集,我們可以訓練和評估一個標點符號分類器: ```py >>> size = int(len(featuresets) * 0.1) >>> train_set, test_set = featuresets[size:], featuresets[:size] >>> classifier = nltk.NaiveBayesClassifier.train(train_set) >>> nltk.classify.accuracy(classifier, test_set) 0.936026936026936 ``` 使用這種分類器進行斷句,我們只需檢查每個標點符號,看它是否是作為一個邊界標識符;在邊界標識符處分割詞列表。在[2.1](./ch06.html#code-classification-based-segmenter)中的清單顯示了如何可以做到這一點。 ```py def segment_sentences(words): start = 0 sents = [] for i, word in enumerate(words): if word in '.?!' and classifier.classify(punct_features(words, i)) == True: sents.append(words[start:i+1]) start = i+1 if start < len(words): sents.append(words[start:]) return sents ``` ## 2.2 識別對話行為類型 處理對話時,將對話看作說話者執行的 _ 行為 _ 是很有用的。對于表述行為的陳述句這種解釋是最直白的,例如"I forgive you"或"I bet you can't climb that hill"。但是問候、問題、回答、斷言和說明都可以被認為是基于語言的行為類型。識別對話中言語下的對話行為是理解談話的重要的第一步。 NPS 聊天語料庫,在[1](./ch02.html#sec-extracting-text-from-corpora)中的展示過,包括超過 10,000 個來自即時消息會話的帖子。這些帖子都已經被貼上 15 種對話行為類型中的一種標簽,例如“陳述”,“情感”,“yn 問題”和“Continuer”。因此,我們可以利用這些數據建立一個分類器,識別新的即時消息帖子的對話行為類型。第一步是提取基本的消息數據。我們將調用`xml_posts()`來得到一個數據結構,表示每個帖子的 XML 注釋: ```py >>> posts = nltk.corpus.nps_chat.xml_posts()[:10000] ``` 下一步,我們將定義一個簡單的特征提取器,檢查帖子包含什么詞: ```py >>> def dialogue_act_features(post): ... features = {} ... for word in nltk.word_tokenize(post): ... features['contains({})'.format(word.lower())] = True ... return features ``` 最后,我們通過為每個帖子提取特征(使用`post.get('class')`獲得一個帖子的對話行為類型)構造訓練和測試數據,并創建一個新的分類器: ```py >>> featuresets = [(dialogue_act_features(post.text), post.get('class')) ... for post in posts] >>> size = int(len(featuresets) * 0.1) >>> train_set, test_set = featuresets[size:], featuresets[:size] >>> classifier = nltk.NaiveBayesClassifier.train(train_set) >>> print(nltk.classify.accuracy(classifier, test_set)) 0.67 ``` ## 2.3 識別文字蘊含 識別文字蘊含(RTE)是判斷文本 _T_ 的一個給定片段是否蘊含著另一個叫做“假設”的文本(已經在[5](./ch01.html#sec-automatic-natural-language-understanding)討論過)。迄今為止,已經有 4 個 RTE 挑戰賽,在那里共享的開發和測試數據會提供給參賽隊伍。這里是挑戰賽 3 開發數據集中的文本/假設對的兩個例子。標簽 _True_ 表示蘊含成立,_False_ 表示蘊含不成立。 > Challenge 3, Pair 34 (True) > > &gt; **T**: Parviz Davudi was representing Iran at a meeting of the Shanghai Co-operation Organisation (SCO), the fledgling association that binds Russia, China and four former Soviet republics of central Asia together to fight terrorism. > &gt; > &gt; **H**: China is a member of SCO. > > Challenge 3, Pair 81 (False) > > &gt; **T**: According to NC Articles of Organization, the members of LLC company are H. Nelson Beavers, III, H. Chester Beavers and Jennie Beavers Stewart. > &gt; > &gt; **H**: Jennie Beavers Stewart is a share-holder of Carolina Analytical Laboratory. 應當強調,文字和假設之間的關系并不一定是邏輯蘊涵,而是一個人是否會得出結論:文本提供了合理的證據證明假設是真實的。 我們可以把 RTE 當作一個分類任務,嘗試為每一對預測 _True_/_False_ 標簽。雖然這項任務的成功做法似乎看上去涉及語法分析、語義和現實世界的知識的組合,RTE 的許多早期的嘗試使用粗淺的分析基于文字和假設之間的在詞級別的相似性取得了相當不錯的結果。在理想情況下,我們希望如果有一個蘊涵那么假設所表示的所有信息也應該在文本中表示。相反,如果假設中有的資料文本中沒有,那么就沒有蘊涵。 在我們的 RTE 特征探測器([2.2](./ch06.html#code-rte-features))中,我們讓詞(即詞類型)作為信息的代理,我們的特征計數詞重疊的程度和假設中有而文本中沒有的詞的程度(由`hyp_extra()`方法獲取)。不是所有的詞都是同樣重要的——命名實體,如人、組織和地方的名稱,可能會更為重要,這促使我們分別為`word`s 和`ne`s(命名實體)提取不同的信息。此外,一些高頻虛詞作為“停用詞”被過濾掉。 ```py def rte_features(rtepair): extractor = nltk.RTEFeatureExtractor(rtepair) features = {} features['word_overlap'] = len(extractor.overlap('word')) features['word_hyp_extra'] = len(extractor.hyp_extra('word')) features['ne_overlap'] = len(extractor.overlap('ne')) features['ne_hyp_extra'] = len(extractor.hyp_extra('ne')) return features ``` 為了說明這些特征的內容,我們檢查前面顯示的文本/假設對 34 的一些屬性: ```py >>> rtepair = nltk.corpus.rte.pairs(['rte3_dev.xml'])[33] >>> extractor = nltk.RTEFeatureExtractor(rtepair) >>> print(extractor.text_words) {'Russia', 'Organisation', 'Shanghai', 'Asia', 'four', 'at', 'operation', 'SCO', ...} >>> print(extractor.hyp_words) {'member', 'SCO', 'China'} >>> print(extractor.overlap('word')) set() >>> print(extractor.overlap('ne')) {'SCO', 'China'} >>> print(extractor.hyp_extra('word')) {'member'} ``` 這些特征表明假設中所有重要的詞都包含在文本中,因此有一些證據支持標記這個為 _True_。 `nltk.classify.rte_classify`模塊使用這些方法在合并的 RTE 測試數據上取得了剛剛超過 58%的準確率。這個數字并不是很令人印象深刻的,還需要大量的努力,更多的語言學處理,才能達到更好的結果。 ## 2.4 擴展到大型數據集 Python 提供了一個良好的環境進行基本的文本處理和特征提取。然而,它處理機器學習方法需要的密集數值計算不能夠如 C 語言那樣的低級語言那么快。因此,如果你嘗試在大型數據集使用純 Python 的機器學習實現(如`nltk.NaiveBayesClassifier`),你可能會發現學習算法會花費大量的時間和內存。 如果你打算用大量訓練數據或大量特征來訓練分類器,我們建議你探索 NLTK 與外部機器學習包的接口。只要這些軟件包已安裝,NLTK 可以透明地調用它們(通過系統調用)來訓練分類模型,明顯比純 Python 的分類實現快。請看 NLTK 網站上推薦的 NLTK 支持的機器學習包列表。 ## 3 評估 為了決定一個分類模型是否準確地捕捉了模式,我們必須評估該模型。評估的結果對于決定模型是多么值得信賴以及我們如何使用它是非常重要。評估也可以是一個有效的工具,用于指導我們在未來改進模型。 ## 3.1 測試集 大多數評估技術為模型計算一個得分,通過比較它在測試集(或評估集)中為輸入生成的標簽與那些輸入的正確標簽。該測試集通常與訓練集具有相同的格式。然而,測試集與訓練語料不同是非常重要的:如果我們簡單地重復使用訓練集作為測試集,那么一個只記住了它的輸入而沒有學會如何推廣到新的例子的模型會得到誤導人的高分。 建立測試集時,往往是一個可用于測試的和可用于訓練的數據量之間的權衡。對于有少量平衡的標簽和一個多樣化的測試集的分類任務,只要 100 個評估實例就可以進行有意義的評估。但是,如果一個分類任務有大量的標簽或包括非常罕見的標簽,那么選擇的測試集的大小就要保證出現次數最少的標簽至少出現 50 次。此外,如果測試集包含許多密切相關的實例——例如來自一個單獨文檔中的實例——那么測試集的大小應增加,以確保這種多樣性的缺乏不會扭曲評估結果。當有大量已標注數據可用時,只使用整體數據的 10%進行評估常常會在安全方面犯錯。 選擇測試集時另一個需要考慮的是測試集中實例與開發集中的實例的相似程度。這兩個數據集越相似,我們對將評估結果推廣到其他數據集的信心就越小。例如,考慮詞性標注任務。在一種極端情況,我們可以通過從一個反映單一的文體(如新聞)的數據源隨機分配句子,創建訓練集和測試集: ```py >>> import random >>> from nltk.corpus import brown >>> tagged_sents = list(brown.tagged_sents(categories='news')) >>> random.shuffle(tagged_sents) >>> size = int(len(tagged_sents) * 0.1) >>> train_set, test_set = tagged_sents[size:], tagged_sents[:size] ``` 在這種情況下,我們的測試集和訓練集將是 _ 非常 _ 相似的。訓練集和測試集均取自同一文體,所以我們不能相信評估結果可以推廣到其他文體。更糟糕的是,因為調用`random.shuffle()`,測試集中包含來自訓練使用過的相同的文檔的句子。如果文檔中有相容的模式(也就是說,如果一個給定的詞與特定詞性標記一起出現特別頻繁),那么這種差異將體現在開發集和測試集。一個稍好的做法是確保訓練集和測試集來自不同的文件: ```py >>> file_ids = brown.fileids(categories='news') >>> size = int(len(file_ids) * 0.1) >>> train_set = brown.tagged_sents(file_ids[size:]) >>> test_set = brown.tagged_sents(file_ids[:size]) ``` 如果我們要執行更令人信服的評估,可以從與訓練集中文檔聯系更少的文檔中獲取測試集: ```py >>> train_set = brown.tagged_sents(categories='news') >>> test_set = brown.tagged_sents(categories='fiction') ``` 如果我們在此測試集上建立了一個性能很好的分類器,那么我們完全可以相信它有能力很好的泛化到用于訓練它的數據以外的數據。 ## 3.2 準確度 用于評估一個分類最簡單的度量是準確度,測量測試集上分類器正確標注的輸入的比例。例如,一個名字性別分類器,在包含 80 個名字的測試集上預測正確的名字有 60 個,它有 60/80 = 75%的準確度。`nltk.classify.accuracy()`函數會在給定的測試集上計算分類器模型的準確度: ```py >>> classifier = nltk.NaiveBayesClassifier.train(train_set) >>> print('Accuracy: {:4.2f}'.format(nltk.classify.accuracy(classifier, test_set))) 0.75 ``` 解釋一個分類器的準確性得分,考慮測試集中單個類標簽的頻率是很重要的。例如,考慮一個決定詞 bank 每次出現的正確的詞意的分類器。如果我們在金融新聞文本上評估分類器,那么我們可能會發現,`金融機構`的意思 20 個里面出現了 19 次。在這種情況下,95%的準確度也難以給人留下深刻印象,因為我們可以實現一個模型,它總是返回`金融機構`的意義。然而,如果我們在一個更加平衡的語料庫上評估分類器,那里的最頻繁的詞意只占 40%,那么 95%的準確度得分將是一個更加積極的結果。(在[2](./ch11.html#sec-life-cycle-of-a-corpus)測量標注一致性程度時也會有類似的問題。) ## 3.3 精確度和召回率 另一個準確度分數可能會產生誤導的實例是在“搜索”任務中,如信息檢索,我們試圖找出與特定任務有關的文檔。由于不相關的文檔的數量遠遠多于相關文檔的數量,一個將每一個文檔都標記為無關的模型的準確度分數將非常接近 100%。 ![Images/precision-recall.png](https://img.kancloud.cn/7e/d5/7ed576354586ccfa4e01c7d8b397ab6d_2092x1134.jpg) 圖 3.1:真與假的陽性和陰性 因此,對搜索任務使用不同的測量集是很常見的,基于[3.1](./ch06.html#fig-precision-recall)所示的四個類別的每一個中的項目的數量: * 真陽性是相關項目中我們正確識別為相關的。 * I 型錯誤)是不相關項目中我們錯誤識別為相關的。 * II 型錯誤)是相關項目中我們錯誤識別為不相關的。 給定這四個數字,我們可以定義以下指標: * F-度量值(或 F-Score),組合精確度和召回率為一個單獨的得分,被定義為精確度和召回率的調和平均數(2 × _Precision_ × _Recall_) / (_Precision_ + _Recall_)。 ## 3.4 混淆矩陣 當處理有 3 個或更多的標簽的分類任務時,基于模型錯誤類型細分模型的錯誤是有信息量的。一個混淆矩陣是一個表,其中每個 cells [i,j]表示正確的標簽 i 被預測為標簽 j 的次數。因此,對角線項目(即 cells [|ii|](./ch06.html#id17))表示正確預測的標簽,非對角線項目表示錯誤。在下面的例子中,我們為[4](./ch05.html#sec-automatic-tagging)中開發的一元標注器生成一個混淆矩陣: ```py >>> def tag_list(tagged_sents): ... return [tag for sent in tagged_sents for (word, tag) in sent] >>> def apply_tagger(tagger, corpus): ... return [tagger.tag(nltk.tag.untag(sent)) for sent in corpus] >>> gold = tag_list(brown.tagged_sents(categories='editorial')) >>> test = tag_list(apply_tagger(t2, brown.tagged_sents(categories='editorial'))) >>> cm = nltk.ConfusionMatrix(gold, test) >>> print(cm.pretty_format(sort_by_count=True, show_percents=True, truncate=9)) | N | | N I A J N V N | | N N T J . S , B P | ----+----------------------------------------------------------------+ NN | <11.8%> 0.0% . 0.2% . 0.0% . 0.3% 0.0% | IN | 0.0% <9.0%> . . . 0.0% . . . | AT | . . <8.6%> . . . . . . | JJ | 1.7% . . <3.9%> . . . 0.0% 0.0% | . | . . . . <4.8%> . . . . | NNS | 1.5% . . . . <3.2%> . . 0.0% | , | . . . . . . <4.4%> . . | VB | 0.9% . . 0.0% . . . <2.4%> . | NP | 1.0% . . 0.0% . . . . <1.8%>| ----+----------------------------------------------------------------+ (row = reference; col = test) ``` 這個混淆矩陣顯示常見的錯誤,包括`NN`替換為了`JJ`(1.6%的詞),`NN`替換為了`NNS`(1.5%的詞)。注意點(`.`)表示值為 0 的單元格,對角線項目——對應正確的分類——用尖括號標記。.. XXX explain use of "reference" in the legend above. ## 3.5 交叉驗證 為了評估我們的模型,我們必須為測試集保留一部分已標注的數據。正如我們已經提到,如果測試集是太小了,我們的評價可能不準確。然而,測試集設置較大通常意味著訓練集設置較小,如果已標注數據的數量有限,這樣設置對性能會產生重大影響。 這個問題的解決方案之一是在不同的測試集上執行多個評估,然后組合這些評估的得分,這種技術被稱為交叉驗證。特別是,我們將原始語料細分為 N 個子集稱為折疊。對于每一個這些的折疊,我們使用 _ 除 _ 這個折疊中的數據外其他所有數據訓練模型,然后在這個折疊上測試模型。即使個別的折疊可能是太小了而不能在其上給出準確的評價分數,綜合評估得分是基于大量的數據,因此是相當可靠的。 第二,同樣重要的,采用交叉驗證的優勢是,它可以讓我們研究不同的訓練集上性能變化有多大。如果我們從所有 N 個訓練集得到非常相似的分數,然后我們可以相當有信心,得分是準確的。另一方面,如果 N 個訓練集上分數很大不同,那么,我們應該對評估得分的準確性持懷疑態度。 ## 4 決策樹 接下來的三節中,我們將仔細看看可用于自動生成分類模型的三種機器學習方法:決策樹、樸素貝葉斯分類器和最大熵分類器。正如我們所看到的,可以把這些學習方法看作黑盒子,直接訓練模式,使用它們進行預測而不需要理解它們是如何工作的。但是,仔細看看這些學習方法如何基于一個訓練集上的數據選擇模型,會學到很多。了解這些方法可以幫助指導我們選擇相應的特征,尤其是我們關于那些特征如何編碼的決定。理解生成的模型可以讓我們更好的提取信息,哪些特征對有信息量,那些特征之間如何相互關聯。 決策樹是一個簡單的為輸入值選擇標簽的流程圖。這個流程圖由檢查特征值的決策節點和分配標簽的葉節點組成。為輸入值選擇標簽,我們以流程圖的初始決策節點開始,稱為其根節點。此節點包含一個條件,檢查輸入值的特征之一,基于該特征的值選擇一個分支。沿著這個描述我們輸入值的分支,我們到達了一個新的決策節點,有一個關于輸入值的特征的新的條件。我們繼續沿著每個節點的條件選擇的分支,直到到達葉節點,它為輸入值提供了一個標簽。[4.1](./ch06.html#fig-decision-tree)顯示名字性別任務的決策樹模型的例子。 ![Images/decision-tree.png](https://img.kancloud.cn/3a/b0/3ab0343f96c1e7df3d5fadb1a5867e8a_395x186.jpg) 圖 4.1:名字性別任務的決策樹模型。請注意樹圖按照習慣畫出“顛倒的”,根在上面,葉子在下面。 一旦我們有了一個決策樹,就可以直接用它來分配標簽給新的輸入值。不那么直接的是我們如何能夠建立一個模擬給定的訓練集的決策樹。但在此之前,我們看一下建立決策樹的學習算法,思考一個簡單的任務:為語料庫選擇最好的“決策樹樁”。決策樹樁是只有一個節點的決策樹,基于一個特征決定如何為輸入分類。每個可能的特征值一個葉子,為特征有那個值的輸入指定類標簽。要建立決策樹樁,我們首先必須決定哪些特征應該使用。最簡單的方法是為每個可能的特征建立一個決策樹樁,看哪一個在訓練數據上得到最高的準確度,也有其他的替代方案,我們將在下面討論。一旦我們選擇了一個特征,就可以通過分配一個標簽給每個葉子,基于在訓練集中所選的例子的最頻繁的標簽,建立決策樹樁(即選擇特征具有那個值的例子)。 給出了選擇決策樹樁的算法,生長出較大的決策樹的算法就很簡單了。首先,我們選擇分類任務的整體最佳的決策樹樁。然后,我們在訓練集上檢查每個葉子的準確度。沒有達到足夠的準確度的葉片被新的決策樹樁替換,新決策樹樁是在根據到葉子的路徑選擇的訓練語料的子集上訓練的。例如,我們可以使[4.1](./ch06.html#fig-decision-tree)中的決策樹生長,通過替換最左邊的葉子為新的決策樹樁,這個新的決策樹樁是在名字不以"k"開始或以一個元音或"l"結尾的訓練集的子集上訓練的。 ## 4.1 熵和信息增益 正如之前提到的,有幾種方法來為決策樹樁確定最有信息量的特征。一種流行的替代方法,被稱為信息增益,當我們用給定的特征分割輸入值時,衡量它們變得更有序的程度。要衡量原始輸入值集合如何無序,我們計算它們的標簽的墑,如果輸入值的標簽非常不同,墑就高;如果輸入值的標簽都相同,墑就低。特別地,熵被定義為每個標簽的概率乘以那個標簽的 log 概率的總和: ```py import math def entropy(labels): freqdist = nltk.FreqDist(labels) probs = [freqdist.freq(l) for l in freqdist] return -sum(p * math.log(p,2) for p in probs) ``` 一旦我們已經計算了原始輸入值的標簽集的墑,就可以判斷應用了決策樹樁之后標簽會變得多么有序。為了這樣做,我們計算每個決策樹樁的葉子的熵,利用這些葉子熵值的平均值(加權每片葉子的樣本數量)。信息增益等于原來的熵減去這個新的減少的熵。信息增益越高,將輸入值分為相關組的決策樹樁就越好,于是我們可以通過選擇具有最高信息增益的決策樹樁來建立決策樹。 決策樹的另一個考慮因素是效率。前面描述的選擇決策樹樁的簡單算法必須為每一個可能的特征構建候選決策樹樁,并且這個過程必須在構造決策樹的每個節點上不斷重復。已經開發了一些算法通過存儲和重用先前評估的例子的信息減少訓練時間。 決策樹有一些有用的性質。首先,它們簡單明了,容易理解。決策樹頂部附近尤其如此,這通常使學習算法可以找到非常有用的特征。決策樹特別適合有很多層次的分類區別的情況。例如,決策樹可以非常有效地捕捉進化樹。 然而,決策樹也有一些缺點。一個問題是,由于決策樹的每個分支會劃分訓練數據,在訓練樹的低節點,可用的訓練數據量可能會變得非常小。因此,這些較低的決策節點可能 過擬合訓練集,學習模式反映訓練集的特質而不是問題底層顯著的語言學模式。對這個問題的一個解決方案是當訓練數據量變得太小時停止分裂節點。另一種方案是長出一個完整的決策樹,但隨后剪去在開發測試集上不能提高性能的決策節點。 決策樹的第二個問題是,它們強迫特征按照一個特定的順序進行檢查,即使特征可能是相對獨立的。例如,按主題分類文檔(如體育、汽車或謀殺之謎)時,特征如`hasword(football)`極可能表示一個特定標簽,無論其他的特征值是什么。由于決策樹頂部附近的空間有限,大部分這些特征將需要在樹中的許多不同的分支中重復。因為越往樹的下方,分支的數量成指數倍增長,重復量可能非常大。 一個相關的問題是決策樹不善于利用對正確的標簽具有較弱預測能力的特征。由于這些特征的影響相對較小,它們往往出現在決策樹非常低的地方。決策樹學習的時間遠遠不夠用到這些特征,也不能留下足夠的訓練數據來可靠地確定它們應該有什么樣的影響。如果我們能夠在整個訓練集中看看這些特征的影響,那么我們也許能夠做出一些關于它們是如何影響標簽的選擇的結論。 決策樹需要按一個特定的順序檢查特征的事實,限制了它們的利用相對獨立的特征的能力。我們下面將討論的樸素貝葉斯分類方法克服了這一限制,允許所有特征“并行”的起作用。 ## 5 樸素貝葉斯分類器 在樸素貝葉斯分類器中,每個特征都得到發言權,來確定哪個標簽應該被分配到一個給定的輸入值。為一個輸入值選擇標簽,樸素貝葉斯分類器以計算每個標簽的先驗概率開始,它由在訓練集上檢查每個標簽的頻率來確定。之后,每個特征的貢獻與它的先驗概率組合,得到每個標簽的似然估計。似然估計最高的標簽會分配給輸入值。[5.1](./ch06.html#fig-naive-bayes-triangle)說明了這一過程。 ![Images/naive-bayes-triangle.png](https://img.kancloud.cn/18/06/1806fe8cdd65241279d260dfea230d0f_1234x792.jpg) 圖 5.1:使用樸素貝葉斯分類器為文檔選擇主題的程序的抽象圖解。在訓練語料中,大多數文檔是有關汽車的,所以分類器從接近“汽車”的標簽的點上開始。但它會考慮每個特征的影響。在這個例子中,輸入文檔中包含的詞"dark",它是謀殺之謎的一個不太強的指標,也包含詞"football",它是體育文檔的一個有力指標。每個特征都作出了貢獻之后,分類器檢查哪個標簽最接近,并將該標簽分配給輸入。 個別特征對整體決策作出自己的貢獻,通過“投票反對”那些不經常出現的特征的標簽。特別是,每個標簽的似然得分由于與輸入值具有此特征的標簽的概率相乘而減小。例如,如果詞 run 在 12%的體育文檔中出現,在 10%的謀殺之謎的文檔中出現,在 2%的汽車文檔中出現,那么體育標簽的似然得分將被乘以 0.12,謀殺之謎標簽將被乘以 0.1,汽車標簽將被乘以 0.02。整體效果是略高于體育標簽的得分的謀殺之謎標簽的得分會減少,而汽車標簽相對于其他兩個標簽會顯著減少。這個過程如[5.2](./ch06.html#fig-naive-bayes-bargraph)和[5.3](./ch06.html#fig-naive-bayes-graph)所示。 ![Images/naive_bayes_bargraph.png](https://img.kancloud.cn/62/44/62446f6ae5f037f3ed089aa43ff631f6_2200x1225.jpg) 圖 5.2:計算樸素貝葉斯的標簽似然得分。樸素貝葉斯以計算每個標簽的先驗概率開始,基于每個標簽出現在訓練數據中的頻率。然后每個特征都用于估計每個標簽的似然估計,通過用輸入值中有那個特征的標簽的概率乘以它。似然得分結果可以認為是從具有給定的標簽和特征集的訓練集中隨機選取的值的概率的估計,假設所有特征概率是獨立的。 ## 5.1 底層的概率模型 理解樸素貝葉斯分類器的另一種方式是它為輸入選擇最有可能的標簽,基于下面的假設:每個輸入值是通過首先為那個輸入值選擇一個類標簽,然后產生每個特征的方式產生的,每個特征與其他特征完全獨立。當然,這種假設是不現實的,特征往往高度依賴彼此。我們將在本節結尾回過來討論這個假設的一些后果。這簡化的假設,稱為樸素貝葉斯假設(或獨立性假設),使得它更容易組合不同特征的貢獻,因為我們不必擔心它們相互影響。 ![Images/naive_bayes_graph.png](https://img.kancloud.cn/af/ae/afaebab7ad043e20291979afe0a610fb_888x546.jpg) 圖 5.3:一個貝葉斯網絡圖演示樸素貝葉斯分類器假定的生成過程。要生成一個標記的輸入,模型先為輸入選擇標簽,然后基于該標簽生成每個輸入的特征。對給定標簽,每個特征被認為是完全獨立于所有其他特征的。 基于這個假設,我們可以計算表達式 P(label|features),給定一個特別的特征集一個輸入具有特定標簽的概率。要為一個新的輸入選擇標簽,我們可以簡單地選擇使 P(l|features)最大的標簽 l。 一開始,我們注意到 P(label|features)等于具有特定標簽 _ 和 _ 特定特征集的輸入的概率除以具有特定特征集的輸入的概率: ```py >>> from nltk.corpus import senseval >>> instances = senseval.instances('hard.pos') >>> size = int(len(instances) * 0.1) >>> train_set, test_set = instances[size:], instances[:size] ``` 使用這個數據集,建立一個分類器,預測一個給定的實例的正確的詞意標簽。關于使用 Senseval 2 語料庫返回的實例對象的信息請參閱`http://nltk.org/howto`上的語料庫 HOWTO。 * ? 使用本章討論過的電影評論文檔分類器,產生對分類器最有信息量的 30 個特征的列表。你能解釋為什么這些特定特征具有信息量嗎?你能在它們中找到什么驚人的發現嗎? * ? 選擇一個本章所描述的分類任務,如名字性別檢測、文檔分類、詞性標注或對話行為分類。使用相同的訓練和測試數據,相同的特征提取器,建立該任務的三個分類器::決策樹、樸素貝葉斯分類器和最大熵分類器。比較你所選任務上這三個分類器的準確性。你如何看待如果你使用了不同的特征提取器,你的結果可能會不同? * ? 同義詞 strong 和 powerful 的模式不同(嘗試將它們與 chip 和 sales 結合)。哪些特征與這種區別有關?建立一個分類器,預測每個詞何時該被使用。 * ? 對話行為分類器為每個帖子分配標簽,不考慮帖子的上下文背景。然而,對話行為是高度依賴上下文的,一些對話行序列可能比別的更相近。例如,ynQuestion 對話行為更容易被一個`yanswer`回答而不是以一個`問候`來回答。利用這一事實,建立一個連續的分類器,為對話行為加標簽。一定要考慮哪些特征可能是有用的。參見[1.7](./ch06.html#code-consecutive-pos-tagger)詞性標記的連續分類器的代碼,獲得一些想法。 * ? 詞特征在處理文本分類中是非常有用的,因為在一個文檔中出現的詞對于其語義內容是什么具有強烈的指示作用。然而,很多詞很少出現,一些在文檔中的最有信息量的詞可能永遠不會出現在我們的訓練數據中。一種解決方法是使用一個詞典,它描述了詞之間的不同。使用 WordNet 詞典,加強本章介紹的電影評論文檔分類器,使用概括一個文檔中出現的詞的特征,使之更容易匹配在訓練數據中發現的詞。 * ★ PP 附件語料庫是描述介詞短語附著決策的語料庫。語料庫中的每個實例被編碼為`PPAttachment`對象: ```py &gt;&gt;&gt; from nltk.corpus import ppattach &gt;&gt;&gt; ppattach.attachments('training') [PPAttachment(sent='0', verb='join', noun1='board', prep='as', noun2='director', attachment='V'), PPAttachment(sent='1', verb='is', noun1='chairman', prep='of', noun2='N.V.', attachment='N'), ...] &gt;&gt;&gt; inst = ppattach.attachments('training')[1] &gt;&gt;&gt; (inst.noun1, inst.prep, inst.noun2) ('chairman', 'of', 'N.V.') ``` 選擇`inst.attachment`為`N`的唯一實例: ```py &gt;&gt;&gt; nattach = [inst for inst in ppattach.attachments('training') ... if inst.attachment == 'N'] ``` 使用此子語料庫,建立一個分類器,嘗試預測哪些介詞是用來連接一對給定的名詞。例如,給定的名詞對"team"和"researchers",分類器應該預測出介詞"of"。更多的使用 PP 附件語料庫的信息,參閱`http://nltk.org/howto`上的語料庫 HOWTO。 * ★ 假設你想自動生成一個場景的散文描述,每個實體已經有了一個唯一描述此實體的詞,例如 the jar,只是想決定在有關的各項目中是否使用 in 或 on,例如 the book is in the cupboard 對比 the book is on the shelf。通過查找語料數據探討這個問題;編寫需要的程序。 | (13) | | &#124; a. &#124; &#124; in the car _versus_ on the train &#124; &#124; b. &#124; &#124; in town _versus_ on campus &#124; &#124; c. &#124; &#124; in the picture _versus_ on the screen &#124; &#124; d. &#124; &#124; in Macbeth _versus_ on Letterman &#124; | 關于本文檔... 針對 NLTK 3.0 作出更新。本章來自于 _Natural Language Processing with Python_,[Steven Bird](http://estive.net/), [Ewan Klein](http://homepages.inf.ed.ac.uk/ewan/) 和[Edward Loper](http://ed.loper.org/),Copyright ? 2014 作者所有。本章依據 _Creative Commons Attribution-Noncommercial-No Derivative Works 3.0 United States License_ [[http://creativecommons.org/licenses/by-nc-nd/3.0/us/](http://creativecommons.org/licenses/by-nc-nd/3.0/us/)] 條款,與 _ 自然語言工具包 _ [`http://nltk.org/`] 3.0 版一起發行。 本文檔構建于星期三 2015 年 7 月 1 日 12:30:05 AEST ## Docutils System Messages System Message: ERROR/3 (`ch06.rst2`, line 1264); _[backlink](./ch06.html#id18)_ Undefined substitution referenced: "ii".
                  <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>

                              哎呀哎呀视频在线观看