介紹
大家好!很高興又能在這兒和大家分享自然語言處理相關的知識了。在上一篇發布于自然語言處理:初識自然語言處理-CSDN博客為大家初步介紹了自然語言處理的基本概念。而這次,我將進一步深入這個領域,和大家聊聊自然語言處理中一個關鍵的基礎環節:文本規范化。好了,我們直接進入正題。
文本規范化
概念
自然語言處理中的文本規范化,是指對原始文本進行一系列處理操作,使其具有統一、標準的格式和表達形式,以提高后續自然語言處理任務(如文本分類、信息檢索、機器翻譯等)的準確性和效率。
常見的文本規范化操作和任務包含以下內容:
- 大小寫轉換:將文本中的所有字母統一為大寫或小寫形式。
- 去除標點符號:移除文本中的標點符號,如句號、逗號、問號、感嘆號等。因為標點符號通常對文本的語義理解貢獻較小,且可能干擾文本處理算法,所以將其去除。
- 分詞:將連續的文本按照一定規則切分成單個的詞或詞組。對于英文,詞與詞之間通常由空格分隔,但也存在一些特殊情況(如縮寫、復合詞等)需要特殊處理;對于中文,由于詞與詞之間沒有明顯的分隔符,分詞難度較大,需要借助專門的分詞算法和工具。
- 詞形還原:將單詞的不同形式(如動詞的時態、名詞的單復數等)還原為其基本形式(原形)。
- 去除停用詞:停用詞是指在文本中頻繁出現但對文本語義表達貢獻較小的詞,如英文中的“the”“and”“is”等,中文中的“的”“了”“在”等。去除停用詞可以減少文本中的冗余信息,突出關鍵詞,提高處理效率。
- 數字歸一化:將文本中的數字進行統一表示。
- 特殊字符處理:處理文本中的特殊字符,如HTML標簽、表情符號、特殊符號等。
分詞
分詞是自然語言處理(NLP)中的一項基礎且關鍵的任務,指的是將連續的自然語言文本按照一定的規則或算法切分成一個個獨立的詞或詞組的過程。不同語言的分詞存在差異,例如:
- 中文:漢語中詞與詞之間沒有明顯的分隔標記(不像英語單詞間有空格),所以分詞相對復雜。例如,“我喜歡自然語言處理”,需要借助特定的分詞算法和工具,將其切分為 “我 / 喜歡 / 自然語言 / 處理”。常用的中文分詞方法有基于詞典的方法、基于統計的方法和基于深度學習的方法等。
- 英文:雖然英文單詞之間以空格作為天然的分隔,但也存在一些特殊情況需要處理,如縮寫(“it's”需處理為“it”和“'s”)、復合詞(“mother-in-law”)、連字符連接的詞(“self-esteem”)等。有時也需要對英文文本進行進一步的詞形還原等操作,以便更好地進行后續的自然語言處理任務。
分詞作為詞性標注、句法分析、語義理解、文本分類、信息檢索、機器翻譯等眾多自然語言處理任務的前置步驟,其準確性對后續任務的質量和效率影響重大,它通過精確劃分文本,為后續對文本的深入分析和處理提供基礎。
空格與標點符號的分詞
在印歐語系里,英語是具有代表性的語言。在這一語系的大多數語言中,普遍采用空格字符來劃分單詞。所以,一種極為簡便的分詞方法便是依據空格來進行分詞:
空格分詞
我們使用Python字符串對象的一個內置方法split(),其功能是將字符串按照指定的分隔符進行分割,然后返回一個由分割后的子字符串組成的列表。
完整代碼
# 定義一個名為NLPTextNormalization的類,用于自然語言處理中的文本規范化操作
class NLPTextNormalization:# 類的構造方法,目前為空,不執行任何初始化操作def __init__(self):# pass 語句占位,不進行實際操作pass# 定義一個實例方法text_normalization用于文本規范化處理def text_normalization(self):# 定義原始句子,用于后續分詞操作original_sentence = ("I'm currently researching the field of Natural Language Processing, and it's really ""fascinating.")# 使用空格作為分隔符對原始句子進行分詞,結果存儲在segmented_sentence中segmented_sentence = original_sentence.split(' ')# 打印原始句子print(f'原句:{original_sentence}')# 打印分詞結果print(f"分詞結果:{segmented_sentence}")# 程序入口判斷,如果當前腳本作為主程序運行
if __name__ == "__main__":# 創建NLPTextNormalization類的一個實例nlp_text_normalization = NLPTextNormalization()# 調用實例的text_normalization方法進行文本規范化處理nlp_text_normalization.text_normalization()
運行結果
原句:I'm currently researching the field of Natural Language Processing,and it's really fascinating.
分詞結果:["I'm", 'currently', 'researching', 'the', 'field', 'of', 'Natural', 'Language', 'Processing,and', "it's", 'really', 'fascinating.']進程已結束,退出代碼為 0
從運行結果我們發現,這種最為簡易的基于空格進行分詞的方式,沒辦法把單詞和緊隨其后的標點符號分離開來。倘若標點符號對于后續的任務(比如文本分類工作)而言并非關鍵要素,那么可以先將這些標點符號予以剔除,而后再開展更為深入的分詞操作。那么這時候,我們需要引入正則表達式的功能。
正則分詞
我們借助Python的re模塊實現字符串替換,用正則表達式模式?(r'\.|\,', '')?用于匹配字符串里的句號(.)和逗號(,),并講將匹配到的句號和逗號替換為空字符串。
完整代碼
# 導入Python的re模塊,該模塊提供了正則表達式操作的功能
import re# 定義一個名為NLPTextNormalization的類,用于自然語言處理中的文本規范化操作
class NLPTextNormalization:# 類的構造方法,目前為空,不執行任何初始化操作def __init__(self):# pass 語句占位,不進行實際操作pass定義一個實例方法text_normalization用于文本規范化處理def text_normalization(self):# 定義原始句子,用于后續分詞操作original_sentence = ("I'm currently researching the field of Natural Language Processing, and it's really ""fascinating.")# 打印原始句子,方便查看初始文本內容print(f'原句:{original_sentence}')# 使用正則表達式替換,去除句子中的句號(.)和逗號(,)sentence = re.sub(r'\.|\,', '', original_sentence)# 基于空格對處理后的句子進行分詞,將其拆分為單詞列表segmented_sentence = sentence.split(' ')# 打印分詞結果,展示分詞后的單詞列表print(f"分詞結果:{segmented_sentence}")# 程序入口判斷,如果當前腳本作為主程序運行
if __name__ == "__main__":# 創建NLPTextNormalization類的一個實例nlp_text_normalization = NLPTextNormalization()# 調用實例的text_normalization方法進行文本規范化處理nlp_text_normalization.text_normalization()
運行結果
原句:I'm currently researching the field of Natural Language Processing, and it's really fascinating.
分詞結果:["I'm", 'currently', 'researching', 'the', 'field', 'of', 'Natural', 'Language', 'Processing', 'and', "it's", 'really', 'fascinating']進程已結束,退出代碼為 0
正則表達式的分詞
正則表達式能夠借助一個單獨的字符串(一般被叫做 “模式”,也就是pattern),用該字符串去對相應文本里所有符合特定規則的字符串進行描述和匹配操作。 我們同樣能夠運用正則表達式來達成基于空格的分詞處理。
空格分詞
我們使用正則表達式模式 (r"\w+") 。其主要功能是從文本中找出由一個或多個連續的字母、數字或下劃線組成的字符串。
完整代碼
# 導入Python的re模塊,該模塊提供了正則表達式操作的功能
import re# 定義一個名為NLPTextNormalization的類,用于自然語言處理中的文本規范化操作
class NLPTextNormalization:# 類的構造方法,目前為空,不執行任何初始化操作def __init__(self):# pass 語句占位,不進行實際操作pass# 定義一個實例方法text_normalization用于文本規范化處理def text_normalization(self):# 定義原始句子,用于后續使用正則表達式進行分詞處理original_sentence = ("Did you pay €6.99 on researchgate.net for access to that full-text paper? ""No, I got it through our university library! It's really convenient...")# 定義正則表達式模式,\w+表示匹配一個或多個字母、數字或下劃線pattern = r"\w+"# 使用re模塊的findall方法,根據模式從原始句子中找出所有匹配項并打印結果print(re.findall(pattern, original_sentence))# 程序入口判斷,如果當前腳本作為主程序運行
if __name__ == "__main__":# 創建NLPTextNormalization類的一個實例nlp_text_normalization = NLPTextNormalization()# 調用實例的text_normalization方法進行文本規范化處理nlp_text_normalization.text_normalization()
運行結果
['Did', 'you', 'pay', '6', '99', 'on', 'researchgate', 'net', 'for', 'access', 'to', 'that', 'full', 'text', 'paper', 'No', 'I', 'got', 'it', 'through', 'our', 'university', 'library', 'It', 's', 'really', 'convenient']進程已結束,退出代碼為 0
含標點分詞
我們可以定義一個正則表達式模式(r"\w+|\S\w*"),\w+匹配一個或多個字母、數字或下劃線,\S\w*匹配以非空白字符開頭,后接零個或多個字母、數字或下劃線的字符串。這樣文本中的單詞、標點符號以及帶有特殊符號的話題標簽都被成功提取出來。
完整代碼
# 導入Python的re模塊,該模塊提供了正則表達式操作的功能
import re# 定義一個名為NLPTextNormalization的類,用于自然語言處理中的文本規范化操作
class NLPTextNormalization:# 類的構造方法,目前為空,不執行任何初始化操作def __init__(self):# pass 語句占位,不進行實際操作pass# 定義一個實例方法text_normalization用于文本規范化處理def text_normalization(self):# 定義原始句子,用于后續使用正則表達式進行分詞處理original_sentence = ("Did you pay €6.99 on researchgate.net for access to that full-text paper? ""No, I got it through our university library! It's really convenient...")# 定義正則表達式模式,\w+匹配一個或多個字母、數字或下劃線,\S\w*匹配以非空白字符開頭,后接零個或多個字母、數字或下劃線的字符串pattern = r"\w+|\S\w*"# 使用re模塊的findall方法,根據模式從原始句子中找出所有匹配項并打印結果print(re.findall(pattern, original_sentence))# 程序入口判斷,如果當前腳本作為主程序運行
if __name__ == "__main__":# 創建NLPTextNormalization類的一個實例nlp_text_normalization = NLPTextNormalization()# 調用實例的text_normalization方法進行文本規范化處理nlp_text_normalization.text_normalization()
運行結果
['Did', 'you', 'pay', '€6', '.99', 'on', 'researchgate', '.net', 'for', 'access', 'to', 'that', 'full', '-text', 'paper', '?', 'No', ',', 'I', 'got', 'it', 'through', 'our', 'university', 'library', '!', 'It', "'s", 'really', 'convenient', '.', '.', '.']進程已結束,退出代碼為 0
匹配連字符詞
我們定義一個正則表達式模式(r"\w+(?:[-']\w+)*"),其作用是匹配文本中的單詞,既可以匹配普通單詞(由一個或多個字母、數字或下劃線組成),也能匹配包含連字符 - 或單引號 ' 的復合單詞。該模式先匹配一個或多個連續的字母、數字或下劃線,之后可選擇零次或多次匹配以連字符或單引號開頭,后接一個或多個字母、數字或下劃線的部分。
完整代碼
# 導入Python的re模塊,該模塊提供了正則表達式操作的功能
import re# 定義一個名為NLPTextNormalization的類,用于自然語言處理中的文本規范化操作
class NLPTextNormalization:# 類的構造方法,目前為空,不執行任何初始化操作def __init__(self):# pass 語句占位,不進行實際操作pass# 定義一個實例方法text_normalization用于文本規范化處理def text_normalization(self):# 定義原始句子,用于后續使用正則表達式進行分詞處理original_sentence = ("Did you pay €6.99 on researchgate.net for access to that full-text paper? ""No, I got it through our university library! It's really convenient...")# 定義正則表達式模式,用于匹配普通單詞及包含連字符或單引號的復合單詞pattern = r"\w+(?:[-']\w+)*"# 使用re模塊的findall方法,根據模式從原始句子中找出所有匹配項并打印結果print(re.findall(pattern, original_sentence))# 程序入口判斷,如果當前腳本作為主程序運行
if __name__ == "__main__":# 創建NLPTextNormalization類的一個實例nlp_text_normalization = NLPTextNormalization()# 調用實例的text_normalization方法進行文本規范化處理nlp_text_normalization.text_normalization()
運行結果
['Did', 'you', 'pay', '6', '99', 'on', 'researchgate', 'net', 'for', 'access', 'to', 'that', 'full-text', 'paper', 'No', 'I', 'got', 'it', 'through', 'our', 'university', 'library', "It's", 'really', 'convenient']進程已結束,退出代碼為 0
匹配連字符詞和標點
大家看到這,我相信大家肯定會想到:如果把匹配含標點詞和匹配連字符詞的模式放到一起,是不是既可以匹配到標點又可以匹配到連字符詞?嗯,是這樣的。我們用代碼來演示下。
完整代碼
# 導入Python的re模塊,該模塊提供了正則表達式操作的功能
import re# 定義一個名為NLPTextNormalization的類,用于自然語言處理中的文本規范化操作
class NLPTextNormalization:# 類的構造方法,目前為空,不執行任何初始化操作def __init__(self):# pass 語句占位,不進行實際操作pass# 定義一個實例方法text_normalization用于文本規范化處理def text_normalization(self):# 定義原始句子,用于后續使用正則表達式進行分詞處理original_sentence = ("Did you pay €6.99 on researchgate.net for access to that full-text paper? ""No, I got it through our university library! It's really convenient...")# 定義正則表達式模式,可匹配普通單詞、含連字符或單引號的復合單詞以及以非空白字符開頭的字符串pattern = r"\w+(?:[-']\w+)*|\S\w*"# 使用re模塊的findall方法,根據模式從原始句子中找出所有匹配項并打印結果print(re.findall(pattern, original_sentence))# 程序入口判斷,如果當前腳本作為主程序運行
if __name__ == "__main__":# 創建NLPTextNormalization類的一個實例nlp_text_normalization = NLPTextNormalization()# 調用實例的text_normalization方法進行文本規范化處理nlp_text_normalization.text_normalization()
運行結果
['Did', 'you', 'pay', '€6', '.99', 'on', 'researchgate', '.net', 'for', 'access', 'to', 'that', 'full-text', 'paper', '?', 'No', ',', 'I', 'got', 'it', 'through', 'our', 'university', 'library', '!', "It's", 'really', 'convenient', '.', '.', '.']進程已結束,退出代碼為 0
匹配簡寫和網址
在英文簡寫和網址里,我們經常會用到 “.”,例如上述代碼里的researchgate.net,它和英文里的句號是同一個符號,那么我們怎么來區分它到底是英文中的句號,還是網址里的域名點呢?
正則表達式模式(r"(?:\w+\.)+\w+(?:\.)*")用于匹配特定格式的字符串,常適用于英文縮寫詞、網址等場景。
我們定義一個新的正則模式?r"(?:\w+\.)+\w+(?:\.)*",然后再與(r"\w+(?:[-']\w+)*|\S\w*")連接起來形成一個新的正則表達式模式。
完整代碼
# 導入Python的re模塊,該模塊提供了正則表達式操作的功能
import re# 定義一個名為NLPTextNormalization的類,用于自然語言處理中的文本規范化操作
class NLPTextNormalization:# 類的構造方法,目前為空,不執行任何初始化操作def __init__(self):# pass 語句占位,不進行實際操作pass# 定義一個實例方法text_normalization用于文本規范化處理def text_normalization(self):# 定義原始句子,用于后續使用正則表達式進行分詞處理original_sentence = ("Did you pay €6.99 on researchgate.net for access to that full-text paper? ""No, I got it through our university library! It's really convenient...")# 定義正則表達式模式,可匹配普通單詞、含連字符或單引號的復合單詞以及以非空白字符開頭的字符串pattern = r"\w+(?:[-']\w+)*|\S\w*"# 定義新的正則表達式模式,用于匹配類似英文簡寫和網址的字符串web_pattern = r"(?:\w+\.)+\w+(?:\.)*"# 將新的正則表達式模式和原模式通過邏輯或連接,更新為新的匹配模式pattern = web_pattern + r"|" + pattern# 導入re模塊,使用re.findall方法根據模式從原始句子中找出所有匹配項并打印結果print(re.findall(pattern, original_sentence))# 程序入口判斷,如果當前腳本作為主程序運行
if __name__ == "__main__":# 創建NLPTextNormalization類的一個實例nlp_text_normalization = NLPTextNormalization()# 調用實例的text_normalization方法進行文本規范化處理nlp_text_normalization.text_normalization()
運行結果
['Did', 'you', 'pay', '€6', '.99', 'on', 'researchgate.net', 'for', 'access', 'to', 'that', 'full-text', 'paper', '?', 'No', ',', 'I', 'got', 'it', 'through', 'our', 'university', 'library', '!', "It's", 'really', 'convenient', '.', '.', '.']進程已結束,退出代碼為 0
匹配價格符號
在全球眾多的語言體系里,當表達貨幣金額或者百分比數值時,貨幣符號(比如美元符號 “$”、歐元符號 “€” 等)以及百分比符號 “%”,往往是直接和數字緊密連接在一起的。若要在文本中找出符合這種直接相連形式的內容,就可以借助一個特定的正則表達式來實現,該正則表達式模式為:r"\$?\d+(?:\.\d+)?%?"
完整代碼
# 導入Python的re模塊,該模塊提供了正則表達式操作的功能
import re# 定義一個名為NLPTextNormalization的類,用于自然語言處理中的文本規范化操作
class NLPTextNormalization:# 類的構造方法,目前為空,不執行任何初始化操作def __init__(self):# pass 語句占位,不進行實際操作pass# 定義一個實例方法text_normalization用于文本規范化處理def text_normalization(self):# 定義原始句子,用于后續使用正則表達式進行分詞處理original_sentence = ("Did you pay €6.99 on researchgate.net for access to that full-text paper? ""No, I got it through our university library! It's really convenient...")# 定義正則表達式模式,可匹配普通單詞、含連字符或單引號的復合單詞以及以非空白字符開頭的字符串pattern = r"\w+(?:[-']\w+)*|\S\w*"# 定義用于匹配網址的正則表達式模式,可匹配如example.com等形式的網址web_pattern = r"(?:\w+\.)+\w+(?:\.)*"# 定義用于匹配價格符號相關內容的正則表達式模式,可匹配如$12.34、12%等形式price_symbol_pattern = r"\$?\d+(?:\.\d+)?%?"# 將價格符號模式、網址模式和之前的模式通過邏輯或連接,形成新的匹配模式pattern = price_symbol_pattern + r"|" + web_pattern + r"|" + pattern# 使用re.findall方法根據最終模式從原始句子中找出所有匹配項并打印結果print(re.findall(pattern, original_sentence))# 程序入口判斷,如果當前腳本作為主程序運行
if __name__ == "__main__":# 創建NLPTextNormalization類的一個實例nlp_text_normalization = NLPTextNormalization()# 調用實例的text_normalization方法進行文本規范化處理nlp_text_normalization.text_normalization()
運行結果
['Did', 'you', 'pay', '€6', '.99', 'on', 'researchgate.net', 'for', 'access', 'to', 'that', 'full-text', 'paper', '?', 'No', ',', 'I', 'got', 'it', 'through', 'our', 'university', 'library', '!', "It's", 'really', 'convenient', '.', '.', '.']進程已結束,退出代碼為 0
匹配省略號
省略號在文本里有著自身獨特的表意功能,能夠傳達出諸如意猶未盡、停頓、沉默等特定的含義。正因為它具備這樣不可忽視的語義價值,所以在對文本進行分詞處理時,我們需要將其完整地保留下來,不能遺漏或忽略。而為了能夠在文本中準確地找到并識別出省略號,以便實現對它的保留操作,我們可以運用一個專門設計的正則表達式來進行匹配,具體的正則表達式模式為:r"\.\.\."
完整代碼
# 導入Python的re模塊,該模塊提供了正則表達式操作的功能
import re# 定義一個名為NLPTextNormalization的類,用于自然語言處理中的文本規范化操作
class NLPTextNormalization:# 類的構造方法,目前為空,不執行任何初始化操作def __init__(self):# pass 語句占位,不進行實際操作pass# 定義一個實例方法text_normalization用于文本規范化處理def text_normalization(self):# 定義原始句子,用于后續使用正則表達式進行分詞處理original_sentence = ("Did you pay €6.99 on researchgate.net for access to that full-text paper? ""No, I got it through our university library! It's really convenient...")# 定義正則表達式模式,可匹配普通單詞、含連字符或單引號的復合單詞以及以非空白字符開頭的字符串pattern = r"\w+(?:[-']\w+)*|\S\w*"# 定義用于匹配網址的正則表達式模式,可匹配如example.com等形式的網址web_pattern = r"(?:\w+\.)+\w+(?:\.)*"# 定義用于匹配價格符號相關內容的正則表達式模式,可匹配如$12.34、12%等形式price_symbol_pattern = r"\$?\d+(?:\.\d+)?%?"# 定義用于匹配省略號的正則表達式模式,可精準匹配文本中的省略號ellipsis_pattern = r"\.\.\."# 將省略號模式、價格符號模式、網址模式和原模式通過邏輯或連接,形成新的匹配模式pattern = ellipsis_pattern + r"|" + price_symbol_pattern + r"|" + web_pattern + r"|" + pattern# 使用re.findall方法根據最終模式從原始句子中找出所有匹配項并打印結果print(re.findall(pattern, original_sentence))# 程序入口判斷,如果當前腳本作為主程序運行
if __name__ == "__main__":# 創建NLPTextNormalization類的一個實例nlp_text_normalization = NLPTextNormalization()# 調用實例的text_normalization方法進行文本規范化處理nlp_text_normalization.text_normalization()
運行結果
['Did', 'you', 'pay', '€6', '.99', 'on', 'researchgate.net', 'for', 'access', 'to', 'that', 'full-text', 'paper', '?', 'No', ',', 'I', 'got', 'it', 'through', 'our', 'university', 'library', '!', "It's", 'really', 'convenient', '...']進程已結束,退出代碼為 0
NLTK
NLTK(Natural Language Toolkit)即自然語言工具包,是一個廣泛用于自然語言處理(NLP)的 Python庫。
它提供了大量的工具和資源,涵蓋了文本處理的各個方面,如分詞(將文本分割成單詞或句子)、詞性標注(為每個單詞標注詞性,如名詞、動詞、形容詞等)、命名實體識別(識別文本中的人名、地名、組織機構名等實體)、詞干提取(將單詞還原為詞干形式,如“running”還原為“run”)、詞形還原(將單詞還原為字典中的標準形式,如“went”還原為“go”)等。
我們可以使用NLTK庫來實現上面的功能。在使用NLTK庫之前,我們需要先安裝該庫,安裝命令為:
pip install nltk
完整代碼
# 從nltk.tokenize模塊中導入regexp_tokenize函數,用于根據正則表達式模式進行分詞
from nltk.tokenize import regexp_tokenize# 定義一個名為NLPTextNormalization的類,用于自然語言處理中的文本規范化操作
class NLPTextNormalization:# 類的構造方法,目前為空,不執行任何初始化操作def __init__(self):# pass 語句占位,不進行實際操作pass# 定義一個實例方法text_normalization用于文本規范化處理def text_normalization(self):# 定義原始句子,用于后續使用正則表達式進行分詞處理original_sentence = ("Did you pay €6.99 on researchgate.net for access to that full-text paper? ""No, I got it through our university library! It's really convenient...")# 定義正則表達式模式,可匹配普通單詞、含連字符或單引號的復合單詞以及以非空白字符開頭的字符串pattern = r"\w+(?:[-']\w+)*|\S\w*"# 定義用于匹配網址的正則表達式模式,可匹配如example.com等形式的網址web_pattern = r"(?:\w+\.)+\w+(?:\.)*"# 定義用于匹配價格符號相關內容的正則表達式模式,可匹配如$12.34、12%等形式price_symbol_pattern = r"\$?\d+(?:\.\d+)?%?"# 定義用于匹配省略號的正則表達式模式,可精準匹配文本中的省略號ellipsis_pattern = r"\.\.\."# 將省略號模式、價格符號模式、網址模式和原模式通過邏輯或連接,形成新的匹配模式pattern = ellipsis_pattern + r"|" + price_symbol_pattern + r"|" + web_pattern + r"|" + pattern# 使用regexp_tokenize函數,依據指定的正則表達式模式對句子進行分詞,并將結果存儲在tokens變量中tokens = regexp_tokenize(original_sentence, pattern)# 打印分詞后的結果print(tokens)# 程序入口判斷,如果當前腳本作為主程序運行
if __name__ == "__main__":# 創建NLPTextNormalization類的一個實例nlp_text_normalization = NLPTextNormalization()# 調用實例的text_normalization方法進行文本規范化處理nlp_text_normalization.text_normalization()
運行結果
['Did', 'you', 'pay', '€6', '.99', 'on', 'researchgate.net', 'for', 'access', 'to', 'that', 'full-text', 'paper', '?', 'No', ',', 'I', 'got', 'it', 'through', 'our', 'university', 'library', '!', "It's", 'really', 'convenient', '...']進程已結束,退出代碼為 0
基于BPE的詞元學習器
BPE(Byte Pair Encoding,字節對編碼是)一種數據壓縮算法,后來被廣泛應用于自然語言處理領域的詞元(subword)學習,用于將單詞拆分成更小的、有意義的單元(詞元)。
BPE的核心思想是從字符級別開始,逐步合并高頻的字符對(或字節對),直到達到預設的詞表大小或滿足一定的停止條件。具體來說,首先將每個單詞表示為字符序列(例如,“h?i?g?h?e r”),然后統計數據集中所有相鄰字符對的出現頻率,將頻率最高的字符對合并成一個新的單元(比如“h?i?g?h?e r”中“e”和“r”常見,合并為“er”,得到“h?i?g?h er”)。重復這個過程,不斷合并高頻字符對,最終形成一個包含各種詞元的詞表。
創建詞表
我們用代碼的方式來演示一下。首先我們根據語料庫來創建初始的詞表:
完整代碼
# 定義一個名為NLPTextNormalization的類,用于自然語言處理中的文本規范化操作
class NLPTextNormalization:# 類的構造方法,目前為空,不執行任何初始化操作def __init__(self):# pass 語句占位,不進行實際操作pass# 定義一個實例方法text_normalization用于文本規范化處理def text_normalization(self):# 定義待處理的文本語料,包含多個城市相關詞匯input_text_corpus = "shang shang shang shang shang shanghai shanghai weihai weihai weihai weihai weihai weihai haikou haikou haikou kou kou"# 將文本語料按空格分割成單詞列表segmented_words = input_text_corpus.split(' ')# 構建基于字符的初始詞表# 把文本語料中的所有字符放入集合,利用集合去重特性char_based_vocab = set(input_text_corpus)# 從字符集合中移除空格字符char_based_vocab.remove(' ')# 向字符集合中添加特殊字符'_'用于標記單詞結束char_based_vocab.add('_')# 將字符集合轉換為列表并排序,得到初始的字符詞表sorted_char_vocab = sorted(list(char_based_vocab))# 根據語料構建詞表# 初始化一個空字典,用于存儲每個單詞的拆分信息和出現次數word_info_dict = {}# 遍歷分割后的單詞列表for single_word in segmented_words:# 為每個單詞添加結束符'_'形成新的鍵word_key = single_word + '_'# 檢查該鍵是否不在存儲單詞信息的字典中if word_key not in word_info_dict:# 若不在,初始化該單詞的信息,包含字符拆分列表和初始計數0word_info_dict[word_key] = {"split": list(word_key), "count": 0}# 增加該單詞的出現次數計數word_info_dict[word_key]['count'] += 1# 打印語料信息提示print(f"語料:")# 遍歷存儲單詞信息的字典for word_key in word_info_dict:# 打印該單詞的出現次數和字符拆分情況print(word_info_dict[word_key]['count'], word_info_dict[word_key]['split'])# 打印初始構建好的字符詞表print(f"詞表:{sorted_char_vocab}")# 程序入口判斷,如果當前腳本作為主程序運行
if __name__ == "__main__":# 創建NLPTextNormalization類的一個實例nlp_text_normalization = NLPTextNormalization()# 調用實例的text_normalization方法進行文本規范化處理nlp_text_normalization.text_normalization()
運行結果
語料:
2 ['a', 'p', 'p', 'l', 'e', '_']
2 ['b', 'a', 'n', 'a', 'n', 'a', '_']
2 ['o', 'r', 'a', 'n', 'g', 'e', '_']
2 ['s', 'h', 'a', 'n', 'g', 'h', 'a', 'i', '_']
3 ['g', 'u', 'a', 'n', 'g', 'z', 'h', 'o', 'u', '_']
2 ['c', 'h', 'e', 'n', 'g', 'd', 'u', '_']
詞匯表:['_', 'a', 'b', 'c', 'd', 'e', 'g', 'h', 'i', 'l', 'n', 'o', 'p', 'r', 's', 'u', 'z']進程已結束,退出代碼為 0
通過上面的完整代碼和運行結果我們看到,我們對預設的文本語料(包含城市名稱)進行處理。首先將語料按空格分詞,然后構建基于字符的詞匯表,去除空格并添加特殊字符 _ 表示詞尾,同時對詞匯表進行排序。接著,統計語料中每個詞語的出現次數,將詞語拆分為字符列表并存儲相關信息。最后,代碼打印出語料中各詞語的出現次數與字符拆分情況,以及構建好的詞匯表,為后續自然語言處理任務提供基礎的語料分析和詞匯信息。
循環納入
其次,我們采用循環的手段讓詞元學習器循序漸進地將新的符號進行組合,而后把這些組合所得納入到詞表當中。
完整代碼
# 定義一個名為NLPTextNormalization的類,用于自然語言處理中的文本規范化操作
class NLPTextNormalization:# 類的構造方法,目前為空,不執行任何初始化操作def __init__(self):# pass 語句占位,不進行實際操作pass# 定義一個實例方法text_normalization用于文本規范化處理def text_normalization(self):# 定義待處理的文本語料,包含多個城市相關詞匯input_text_corpus = ("shang shang shang shang shang shanghai shanghai weihai weihai weihai weihai weihai ""weihai haikou haikou haikou kou kou")# 將文本語料按空格分割成單詞列表segmented_words = input_text_corpus.split(' ')# 構建基于字符的初始詞表# 把文本語料中的所有字符放入集合,利用集合去重特性char_based_vocab = set(input_text_corpus)# 從字符集合中移除空格字符char_based_vocab.remove(' ')# 向字符集合中添加特殊字符'_'用于標記單詞結束char_based_vocab.add('_')# 將字符集合轉換為列表并排序,得到初始的字符詞表sorted_char_vocab = sorted(list(char_based_vocab))# 根據語料構建詞表# 初始化一個空字典,用于存儲每個單詞的拆分信息和出現次數word_info_dict = {}# 遍歷分割后的單詞列表for single_word in segmented_words:# 為每個單詞添加結束符'_'形成新的鍵word_key = single_word + '_'# 檢查該鍵是否不在存儲單詞信息的字典中if word_key not in word_info_dict:# 若不在,初始化該單詞的信息,包含字符拆分列表和初始計數0word_info_dict[word_key] = {"split": list(word_key), "count": 0}# 增加該單詞的出現次數計數word_info_dict[word_key]['count'] += 1# 打印語料信息提示print(f"語料:")# 遍歷存儲單詞信息的字典for word_key in word_info_dict:# 打印該單詞的出現次數和字符拆分情況print(word_info_dict[word_key]['count'], word_info_dict[word_key]['split'])# 打印初始構建好的字符詞表print(f"詞表:{sorted_char_vocab}")# 進行9次循環操作,逐步更新詞表for iteration in range(9):# 設定最大打印步驟數,若想輸出所有步驟結果可將其改為999max_print_steps = 3# 判斷是否滿足打印當前循環信息的條件if iteration < max_print_steps or iteration == 8:# 打印當前循環的序號print(f"第{iteration + 1}次循環")# 初始化一個空字典,用于統計符號組合的出現次數symbol_combination_count = {}# 遍歷存儲單詞信息的字典for word_key in word_info_dict:# 獲取該單詞的字符拆分列表char_splits = word_info_dict[word_key]['split']# 遍歷該單詞的字符拆分列表,除最后一個字符for i in range(len(char_splits) - 1):# 組合相鄰兩個字符形成新的符號組合current_combination = char_splits[i] + char_splits[i + 1]# 檢查該符號組合是否不在統計字典中if current_combination not in symbol_combination_count:# 若不在,初始化該符號組合的計數為0symbol_combination_count[current_combination] = 0# 增加該符號組合的計數symbol_combination_count[current_combination] += word_info_dict[word_key]['count']# 將符號組合及其計數按計數降序排序,存儲為元組列表sorted_combination_list = [(k, v) for k, v insorted(symbol_combination_count.items(), key=lambda item: item[1], reverse=True)]# 判斷是否滿足打印符號組合信息的條件if iteration < max_print_steps or iteration == 8:# 打印當前最常出現的前 5 個符號組合print(f"當前最常出現的前5個符號組合:{sorted_combination_list[:5]}")# 取出最常出現的符號組合作為本次要合并的符號merge_symbol = sorted_combination_list[0][0]# 判斷是否滿足打印合并符號信息的條件if iteration < max_print_steps or iteration == 8:# 打印本次循環要合并的符號組合print(f"本次循環組合的符號為:{merge_symbol}")# 遍歷存儲單詞信息的字典for word_key in word_info_dict:# 檢查該單詞是否包含要合并的符號組合if merge_symbol in word_key:# 初始化一個空列表,用于存儲更新后的字符拆分updated_splits = []# 獲取該單詞的字符拆分列表char_splits = word_info_dict[word_key]['split']# 初始化索引為 0i = 0# 遍歷該單詞的字符拆分列表while i < len(char_splits):# 判斷是否到達字符拆分列表的最后一個字符if i + 1 >= len(char_splits):# 若到達,將最后一個字符添加到更新后的列表updated_splits.append(char_splits[i])# 索引加 1i += 1# 繼續下一次循環continue# 判斷相鄰兩個字符是否組成要合并的符號組合if merge_symbol == char_splits[i] + char_splits[i + 1]:# 若組成,將合并后的符號組合添加到更新后的列表updated_splits.append(merge_symbol)# 索引加 2i += 2else:# 若不組成,將當前字符添加到更新后的列表updated_splits.append(char_splits[i])# 索引加 1i += 1# 更新該單詞的字符拆分信息word_info_dict[word_key]['split'] = updated_splits# 將本次合并的符號組合添加到字符詞表中sorted_char_vocab.append(merge_symbol)# 判斷是否滿足打印循環后信息的條件if iteration < max_print_steps or iteration == 8:# 打印空行用于分隔信息print()# 打印循環后語料信息提示print(f"循環后的語料為:")# 遍歷存儲單詞信息的字典for word_key in word_info_dict:# 打印該單詞的出現次數和更新后的字符拆分情況print(word_info_dict[word_key]['count'], word_info_dict[word_key]['split'])# 打印更新后的字符詞表print(f"詞表:{sorted_char_vocab}")# 打印空行用于分隔信息print()# 打印分隔線print('***********************************')# 程序入口判斷,如果當前腳本作為主程序運行
if __name__ == "__main__":# 創建NLPTextNormalization類的一個實例nlp_text_normalization = NLPTextNormalization()# 調用實例的text_normalization方法進行文本規范化處理nlp_text_normalization.text_normalization()
運行結果
語料:
5 ['s', 'h', 'a', 'n', 'g', '_']
2 ['s', 'h', 'a', 'n', 'g', 'h', 'a', 'i', '_']
6 ['w', 'e', 'i', 'h', 'a', 'i', '_']
3 ['h', 'a', 'i', 'k', 'o', 'u', '_']
2 ['k', 'o', 'u', '_']
詞表:['_', 'a', 'e', 'g', 'h', 'i', 'k', 'n', 'o', 's', 'u', 'w']
第1次循環
當前最常出現的前5個符號組合:[('ha', 18), ('ai', 11), ('i_', 8), ('sh', 7), ('an', 7)]
本次循環組合的符號為:ha循環后的語料為:
5 ['s', 'ha', 'n', 'g', '_']
2 ['s', 'ha', 'n', 'g', 'ha', 'i', '_']
6 ['w', 'e', 'i', 'ha', 'i', '_']
3 ['ha', 'i', 'k', 'o', 'u', '_']
2 ['k', 'o', 'u', '_']
詞表:['_', 'a', 'e', 'g', 'h', 'i', 'k', 'n', 'o', 's', 'u', 'w', 'ha']***********************************
第2次循環
當前最常出現的前5個符號組合:[('hai', 11), ('i_', 8), ('sha', 7), ('han', 7), ('ng', 7)]
本次循環組合的符號為:hai循環后的語料為:
5 ['s', 'ha', 'n', 'g', '_']
2 ['s', 'ha', 'n', 'g', 'hai', '_']
6 ['w', 'e', 'i', 'hai', '_']
3 ['hai', 'k', 'o', 'u', '_']
2 ['k', 'o', 'u', '_']
詞表:['_', 'a', 'e', 'g', 'h', 'i', 'k', 'n', 'o', 's', 'u', 'w', 'ha', 'hai']***********************************
第3次循環
當前最常出現的前5個符號組合:[('hai_', 8), ('sha', 7), ('han', 7), ('ng', 7), ('we', 6)]
本次循環組合的符號為:hai_循環后的語料為:
5 ['s', 'ha', 'n', 'g', '_']
2 ['s', 'ha', 'n', 'g', 'hai_']
6 ['w', 'e', 'i', 'hai_']
3 ['hai', 'k', 'o', 'u', '_']
2 ['k', 'o', 'u', '_']
詞表:['_', 'a', 'e', 'g', 'h', 'i', 'k', 'n', 'o', 's', 'u', 'w', 'ha', 'hai', 'hai_']***********************************
第9次循環
當前最常出現的前5個符號組合:[('weihai_', 6), ('shang_', 5), ('ko', 5), ('ou', 5), ('u_', 5)]
本次循環組合的符號為:weihai_循環后的語料為:
5 ['shang', '_']
2 ['shang', 'hai_']
6 ['weihai_']
3 ['hai', 'k', 'o', 'u', '_']
2 ['k', 'o', 'u', '_']
詞表:['_', 'a', 'e', 'g', 'h', 'i', 'k', 'n', 'o', 's', 'u', 'w', 'ha', 'hai', 'hai_', 'sha', 'shan', 'shang', 'we', 'wei', 'weihai_']***********************************進程已結束,退出代碼為 0
上述代碼中我們進行9次循環操作,每次循環統計相鄰字符組合成新符號的出現次數,找出最常出現的符號組合并將其作為要合并的符號,更新包含該符號組合的單詞的字符拆分信息,同時將合并后的符號添加到詞表中,并打印循環中的符號組合信息、合并的符號以及循環后的語料和詞表情況。?
基于BPE的詞元分詞器
在獲取到通過學習所生成的詞表后,若給定一個全新的句子,可借助BPE詞元分詞器,依據詞表中各個符號的學習順序,以貪心策略對字符進行組合操作。舉例來講,當輸入的句子為“shanghai weihai”時,按照上述例子所生成的詞表,首先會將字符 “h” 、“a”和“i” 組合成為 “hai”,接著依次組合 “ha”,“sha”…… 直至完成分詞。
組合操作
接下來,我們用代碼來演示下功能。
完整代碼
# 定義一個名為NLPTextNormalization的類,用于自然語言處理中的文本規范化操作
class NLPTextNormalization:# 類的構造方法,目前為空,不執行任何初始化操作def __init__(self):# pass 語句占位,不進行實際操作pass# 定義一個實例方法text_normalization用于文本規范化處理def text_normalization(self):# 定義待處理的文本語料,包含多個城市相關詞匯input_text_corpus = "shang shang shang shang shang shanghai shanghai weihai weihai weihai weihai weihai weihai haikou haikou haikou kou kou"# 將文本語料按空格分割成單詞列表segmented_words = input_text_corpus.split(' ')# 構建基于字符的初始詞表# 把文本語料中的所有字符放入集合,利用集合去重特性char_based_vocab = set(input_text_corpus)# 從字符集合中移除空格字符char_based_vocab.remove(' ')# 向字符集合中添加特殊字符'_'用于標記單詞結束char_based_vocab.add('_')# 將字符集合轉換為列表并排序,得到初始的字符詞表sorted_char_vocab = sorted(list(char_based_vocab))# 根據語料構建詞表# 初始化一個空字典,用于存儲每個單詞的拆分信息和出現次數word_info_dict = {}# 遍歷分割后的單詞列表for single_word in segmented_words:# 為每個單詞添加結束符'_'形成新的鍵word_key = single_word + '_'# 檢查該鍵是否不在存儲單詞信息的字典中if word_key not in word_info_dict:# 若不在,初始化該單詞的信息,包含字符拆分列表和初始計數0word_info_dict[word_key] = {"split": list(word_key), "count": 0}# 增加該單詞的出現次數計數word_info_dict[word_key]['count'] += 1# 打印語料信息提示print(f"語料:")# 遍歷存儲單詞信息的字典for word_key in word_info_dict:# 打印該單詞的出現次數和字符拆分情況print(word_info_dict[word_key]['count'], word_info_dict[word_key]['split'])# 打印初始構建好的字符詞表print(f"詞表:{sorted_char_vocab}")# 進行9次循環操作,逐步更新詞表for iteration in range(9):# 設定最大打印步驟數,若想輸出所有步驟結果可將其改為999max_print_steps = 3# 判斷是否滿足打印當前循環信息的條件if iteration < max_print_steps or iteration == 8:# 打印當前循環的序號print(f"第{iteration + 1}次循環")# 初始化一個空字典,用于統計符號組合的出現次數symbol_combination_count = {}# 遍歷存儲單詞信息的字典for word_key in word_info_dict:# 獲取該單詞的字符拆分列表char_splits = word_info_dict[word_key]['split']# 遍歷該單詞的字符拆分列表,除最后一個字符for i in range(len(char_splits) - 1):# 組合相鄰兩個字符形成新的符號組合current_combination = char_splits[i] + char_splits[i + 1]# 檢查該符號組合是否不在統計字典中if current_combination not in symbol_combination_count:# 若不在,初始化該符號組合的計數為0symbol_combination_count[current_combination] = 0# 增加該符號組合的計數symbol_combination_count[current_combination] += word_info_dict[word_key]['count']# 將符號組合及其計數按計數降序排序,存儲為元組列表sorted_combination_list = [(k, v) for k, v insorted(symbol_combination_count.items(), key=lambda item: item[1], reverse=True)]# 判斷是否滿足打印符號組合信息的條件if iteration < max_print_steps or iteration == 8:# 打印當前最常出現的前 5 個符號組合print(f"當前最常出現的前5個符號組合:{sorted_combination_list[:5]}")# 取出最常出現的符號組合作為本次要合并的符號merge_symbol = sorted_combination_list[0][0]# 判斷是否滿足打印合并符號信息的條件if iteration < max_print_steps or iteration == 8:# 打印本次循環要合并的符號組合print(f"本次循環組合的符號為:{merge_symbol}")# 遍歷存儲單詞信息的字典for word_key in word_info_dict:# 檢查該單詞是否包含要合并的符號組合if merge_symbol in word_key:# 初始化一個空列表,用于存儲更新后的字符拆分updated_splits = []# 獲取該單詞的字符拆分列表char_splits = word_info_dict[word_key]['split']# 初始化索引為 0i = 0# 遍歷該單詞的字符拆分列表while i < len(char_splits):# 判斷是否到達字符拆分列表的最后一個字符if i + 1 >= len(char_splits):# 若到達,將最后一個字符添加到更新后的列表updated_splits.append(char_splits[i])# 索引加 1i += 1# 繼續下一次循環continue# 判斷相鄰兩個字符是否組成要合并的符號組合if merge_symbol == char_splits[i] + char_splits[i + 1]:# 若組成,將合并后的符號組合添加到更新后的列表updated_splits.append(merge_symbol)# 索引加 2i += 2else:# 若不組成,將當前字符添加到更新后的列表updated_splits.append(char_splits[i])# 索引加 1i += 1# 更新該單詞的字符拆分信息word_info_dict[word_key]['split'] = updated_splits# 將本次合并的符號組合添加到字符詞表中sorted_char_vocab.append(merge_symbol)# 判斷是否滿足打印循環后信息的條件if iteration < max_print_steps or iteration == 8:# 打印空行用于分隔信息print()# 打印循環后語料信息提示print(f"循環后的語料為:")# 遍歷存儲單詞信息的字典for word_key in word_info_dict:# 打印該單詞的出現次數和更新后的字符拆分情況print(word_info_dict[word_key]['count'], word_info_dict[word_key]['split'])# 打印更新后的字符詞表print(f"詞表:{sorted_char_vocab}")# 打印空行用于分隔信息print()# 打印分隔線print('***********************************')# 生成有序的詞表,鍵為符號,值為其在詞表中的索引ordered_vocab = {key: index for index, key in enumerate(sorted_char_vocab)}# 定義待分詞的輸入語句input_sentence = "shanghai weihai"# 打印輸入語句print(f"輸入語句:{input_sentence}")# 將輸入語句按空格分割成單詞列表input_words = input_sentence.split(' ')# 初始化一個空列表,用于存儲分詞結果tokenized_result = []# 遍歷輸入的單詞列表for single_word in input_words:# 為輸入的單詞添加結束符'_'形成新的鍵word_key = single_word + '_'# 將該單詞轉換為字符列表char_splits = list(word_key)# 用于在沒有更新的時候跳出循環的標志,初始設為1表示需要更新update_flag = 1# 當更新標志為1時,繼續循環進行更新操作while update_flag:# 先將更新標志置為0,表示假設本次無更新update_flag = 0# 初始化一個空字典,用于統計符號組合及其在有序詞表中的索引combination_index_dict = {}# 遍歷該單詞的字符拆分列表,除最后一個字符for i in range(len(char_splits) - 1):# 組合相鄰兩個字符形成新的符號組合current_combination = char_splits[i] + char_splits[i + 1]# 檢查該符號組合是否不在有序詞表中if current_combination not in ordered_vocab:# 若不在,跳過本次循環continue# 檢查該符號組合是否不在統計字典中if current_combination not in combination_index_dict:# 若不在,將該符號組合及其在有序詞表中的索引添加到統計字典combination_index_dict[current_combination] = ordered_vocab[current_combination]# 將更新標志置為1,表示有更新update_flag = 1# 判斷是否沒有更新if not update_flag:# 若沒有更新,跳出循環continue# 將符號組合及其索引按索引升序排序,存儲為元組列表sorted_combination_index = [(k, v) for k, v insorted(combination_index_dict.items(), key=lambda item: item[1])]# 取出索引最小(優先級最高)的符號組合top_combination = sorted_combination_index[0][0]# 初始化一個空列表,用于存儲更新后的字符拆分new_char_splits = []# 初始化索引為0i = 0# 遍歷該單詞的字符拆分列表while i < len(char_splits):# 判斷是否到達字符拆分列表的最后一個字符if i + 1 >= len(char_splits):# 若到達,將最后一個字符添加到更新后的列表new_char_splits.append(char_splits[i])# 索引加1i += 1# 繼續下一次循環continue# 判斷相鄰兩個字符是否組成優先級最高的符號組合if top_combination == char_splits[i] + char_splits[i + 1]:# 若組成,將該符號組合添加到更新后的列表new_char_splits.append(top_combination)# 索引加 2i += 2else:# 若不組成,將當前字符添加到更新后的列表new_char_splits.append(char_splits[i])# 索引加 1i += 1# 更新該單詞的字符拆分列表char_splits = new_char_splits# 將更新后的字符拆分列表添加到分詞結果列表tokenized_result += char_splits# 打印最終的分詞結果print(f"分詞結果:{tokenized_result}")# 程序入口判斷,如果當前腳本作為主程序運行
if __name__ == "__main__":# 創建NLPTextNormalization類的一個實例nlp_text_normalization = NLPTextNormalization()# 調用實例的text_normalization方法進行文本規范化處理nlp_text_normalization.text_normalization()
運行結果
語料:
5 ['s', 'h', 'a', 'n', 'g', '_']
2 ['s', 'h', 'a', 'n', 'g', 'h', 'a', 'i', '_']
6 ['w', 'e', 'i', 'h', 'a', 'i', '_']
3 ['h', 'a', 'i', 'k', 'o', 'u', '_']
2 ['k', 'o', 'u', '_']
詞表:['_', 'a', 'e', 'g', 'h', 'i', 'k', 'n', 'o', 's', 'u', 'w']
第1次循環
當前最常出現的前5個符號組合:[('ha', 18), ('ai', 11), ('i_', 8), ('sh', 7), ('an', 7)]
本次循環組合的符號為:ha循環后的語料為:
5 ['s', 'ha', 'n', 'g', '_']
2 ['s', 'ha', 'n', 'g', 'ha', 'i', '_']
6 ['w', 'e', 'i', 'ha', 'i', '_']
3 ['ha', 'i', 'k', 'o', 'u', '_']
2 ['k', 'o', 'u', '_']
詞表:['_', 'a', 'e', 'g', 'h', 'i', 'k', 'n', 'o', 's', 'u', 'w', 'ha']***********************************
第2次循環
當前最常出現的前5個符號組合:[('hai', 11), ('i_', 8), ('sha', 7), ('han', 7), ('ng', 7)]
本次循環組合的符號為:hai循環后的語料為:
5 ['s', 'ha', 'n', 'g', '_']
2 ['s', 'ha', 'n', 'g', 'hai', '_']
6 ['w', 'e', 'i', 'hai', '_']
3 ['hai', 'k', 'o', 'u', '_']
2 ['k', 'o', 'u', '_']
詞表:['_', 'a', 'e', 'g', 'h', 'i', 'k', 'n', 'o', 's', 'u', 'w', 'ha', 'hai']***********************************
第3次循環
當前最常出現的前5個符號組合:[('hai_', 8), ('sha', 7), ('han', 7), ('ng', 7), ('we', 6)]
本次循環組合的符號為:hai_循環后的語料為:
5 ['s', 'ha', 'n', 'g', '_']
2 ['s', 'ha', 'n', 'g', 'hai_']
6 ['w', 'e', 'i', 'hai_']
3 ['hai', 'k', 'o', 'u', '_']
2 ['k', 'o', 'u', '_']
詞表:['_', 'a', 'e', 'g', 'h', 'i', 'k', 'n', 'o', 's', 'u', 'w', 'ha', 'hai', 'hai_']***********************************
第9次循環
當前最常出現的前5個符號組合:[('weihai_', 6), ('shang_', 5), ('ko', 5), ('ou', 5), ('u_', 5)]
本次循環組合的符號為:weihai_循環后的語料為:
5 ['shang', '_']
2 ['shang', 'hai_']
6 ['weihai_']
3 ['hai', 'k', 'o', 'u', '_']
2 ['k', 'o', 'u', '_']
詞表:['_', 'a', 'e', 'g', 'h', 'i', 'k', 'n', 'o', 's', 'u', 'w', 'ha', 'hai', 'hai_', 'sha', 'shan', 'shang', 'we', 'wei', 'weihai_']***********************************
輸入語句:shanghai weihai
分詞結果:['shang', 'hai_', 'weihai_']進程已結束,退出代碼為 0
通過上述,我們看到,利用前期通過BPE算法構建并更新的詞表對新輸入句子進行分詞的功能。首先,將排序好的字符詞表轉換為有序詞表ordered_vocab,其鍵為符號,值為該符號在詞表中的索引。
接著,定義待分詞的輸入語句 “shanghai weihai” 并打印,隨后將其按空格拆分為單詞列表。之后初始化一個空列表用于存儲最終分詞結果。對每個單詞,添加結束符?“_”?并轉為字符列表,借助update_flag循環嘗試將相鄰字符組合成詞表中的符號組合。
在遍歷字符拆分列表時,若組合在詞表中且未統計過,就記錄該組合及其索引并標記有更新。對統計的組合按索引升序排序,取索引最小的組合更新字符拆分列表,直至無法組合。最后將更新后的字符拆分列表添加到結果列表,并打印最終的分詞結果。?
詞規范化
自然語言處理中的詞規范化,是對文本中的詞匯進行一系列處理,以達到統一、標準的表示形式,從而提高后續自然語言處理任務的效率和準確性。
在原始文本中,同一個詞可能有多種不同的書寫形式,如大小寫差異(“Apple”和“apple”)、單復數形式(“book”和“books”)、詞形變化(“run”和 “running”,“ran”)等,還可能存在拼寫錯誤或不規范的表達。詞規范化就是要處理這些情況,通過特定的算法和規則,將這些不同形式的詞映射到一個標準形式。例如:
- 進行大小寫轉換:把所有單詞統一為大寫或小寫。
- 進行詞干提取:去除詞的詞綴,將單詞還原為詞干形式(如“running”還原為“run”)
- 進行詞形還原:根據詞性等信息將單詞轉換為其詞典中的基本形式(如“am”,“is”,“are”還原為“be”);
- 糾正拼寫錯誤:將錯誤拼寫的單詞修正為正確形式。
大小寫轉換
在Python中,字符串類型提供了一些方法來進行大小寫轉換,常見的方法如下:
lower方法
lower()方法將字符串中的所有大寫字母轉換為小寫字母,其他字符保持不變。
完整代碼
# 定義一個名為NLPTextNormalization的類,用于自然語言處理中的文本規范化操作
class NLPTextNormalization:# 類的構造方法,目前為空,不執行任何初始化操作def __init__(self):# pass 語句占位,不進行實際操作passdef text_normalization(self):# 定義原始句子,內容是關于正在研究自然語言處理領域以及對其的感受original_sentence = ("I'm currently researching the field of Natural Language Processing, and it's really ""fascinating.")# 將原始句子中的所有大寫字母轉換為小寫字母lower_original_sentence = original_sentence.lower()# 打印轉換后的句子print(lower_original_sentence)# 程序入口判斷,如果當前腳本作為主程序運行
if __name__ == "__main__":# 創建NLPTextNormalization類的一個實例nlp_text_normalization = NLPTextNormalization()# 調用實例的text_normalization方法進行文本規范化處理nlp_text_normalization.text_normalization()
運行結果
i'm currently researching the field of natural language processing, and it's really fascinating.進程已結束,退出代碼為 0
我們看到,lower_original_sentence變量中的所有大寫字母全部變成了小寫字母。
upper方法
upper()把字符串中的所有小寫字母轉換為大寫字母,其余字符不受影響。
完整代碼
# 定義一個名為NLPTextNormalization的類,用于自然語言處理中的文本規范化操作
class NLPTextNormalization:# 類的構造方法,目前為空,不執行任何初始化操作def __init__(self):# pass 語句占位,不進行實際操作pass# 定義一個實例方法text_normalization用于文本規范化處理def text_normalization(self):# 定義原始句子,內容是關于正在研究自然語言處理領域以及對其的感受original_sentence = ("I'm currently researching the field of Natural Language Processing, and it's really ""fascinating.")# 將原始句子中的所有小寫字母轉換為大寫字母lower_original_sentence = original_sentence.upper()# 打印轉換后的句子print(lower_original_sentence)# 程序入口判斷,如果當前腳本作為主程序運行
if __name__ == "__main__":# 創建NLPTextNormalization類的一個實例nlp_text_normalization = NLPTextNormalization()# 調用實例的text_normalization方法進行文本規范化處理nlp_text_normalization.text_normalization()
運行結果
I'M CURRENTLY RESEARCHING THE FIELD OF NATURAL LANGUAGE PROCESSING, AND IT'S REALLY FASCINATING.進程已結束,退出代碼為 0
我們看到,lower_original_sentence變量中的所有大寫字母全部變成了小寫字母(Python中還又一些其他的方法,例如:capitalize()方法,title()方法,swapcase()方法。這些方法請大家自己去做測試,看看結果會是什么樣的)。
經過詞規范化處理后,文本中的詞匯形式更加統一,能夠減少詞匯的多樣性和歧義性,使計算機在進行文本分析、信息檢索、機器翻譯、情感分析等自然語言處理任務時,能夠更準確地理解和處理文本,提高處理結果的質量和一致性。
詞目還原
在自然語言處理中,詞目還原是一項重要的基礎操作,旨在將單詞恢復到其在詞典中呈現的基本形式,即詞元。這一過程并非簡單地去除詞綴,而是綜合考量單詞的詞性(諸如名詞、動詞、形容詞等)以及上下文語境來精準確定其詞元。
比如在英語里,“studies”(動詞“study”的第三人稱單數形式)、“studying”(現在分詞形式)、“studied”(過去式和過去分詞形式),經過詞目還原后,都會回歸到“study”這一基本形式;“geese”(“goose”的復數形式)會被還原為“goose”。
匹配還原
在人類學習各類語言的進程里,能夠借助詞典去探尋單詞的原始形態;與之相仿,計算機也能夠憑借構建專門的詞典,以此來達成詞目還原的操作。我們用代碼來演示一下:
完整代碼
# 定義一個名為NLPTextNormalization的類,用于自然語言處理中的文本規范化操作
class NLPTextNormalization:# 類的構造方法,目前為空,不執行任何初始化操作def __init__(self):# pass 語句占位,不進行實際操作pass# 定義一個實例方法text_normalization用于文本規范化處理def text_normalization(self):# 構建詞元映射字典,記錄單詞及其對應的詞元word_to_lemma_mapping = {'studied': 'study', 'learned': 'learn', 'achieved': 'achieve', 'passed': 'pass', 'tests': 'test', 'goals': 'goal', 'skills': 'skill', 'improved': 'improve', 'discovered': 'discover'}# 定義需要進行處理的原始句子original_sentence = ("He studied hard for his tests and learned a lot of new skills. He achieved all his ""goals, passed the exams, and improved his grades. He also discovered a new passion ""for science.")# 把原始句子按照空格拆分成單個單詞,形成列表single_word_list = original_sentence.split(' ')# 打印詞目還原操作之前的單詞列表print(f'詞目還原前:{single_word_list}')# 初始化一個空列表,用來存放經過詞目還原后的單詞lemma_reduced_word_list = []# 對拆分后的每個單詞進行遍歷for single_word in single_word_list:# 檢查當前遍歷到的單詞是否存在于詞元映射字典中if single_word in word_to_lemma_mapping:# 若存在,將該單詞對應的詞元添加到存放還原后單詞的列表中lemma_reduced_word_list.append(word_to_lemma_mapping[single_word])else:# 若不存在,直接將該單詞添加到存放還原后單詞的列表中lemma_reduced_word_list.append(single_word)# 打印經過詞目還原操作之后的單詞列表print(f'詞目還原后:{lemma_reduced_word_list}')# 程序入口判斷,如果當前腳本作為主程序運行
if __name__ == "__main__":# 創建NLPTextNormalization類的一個實例nlp_text_normalization = NLPTextNormalization()# 調用實例的text_normalization方法進行文本規范化處理nlp_text_normalization.text_normalization()
運行結果
詞目還原前:['He', 'studied', 'hard', 'for', 'his', 'tests', 'and', 'learned', 'a', 'lot', 'of', 'new', 'skills.', 'He', 'achieved', 'all', 'his', 'goals,', 'passed', 'the', 'exams,', 'and', 'improved', 'his', 'grades.', 'He', 'also', 'discovered', 'a', 'new', 'passion', 'for', 'science.']
詞目還原后:['He', 'study', 'hard', 'for', 'his', 'test', 'and', 'learn', 'a', 'lot', 'of', 'new', 'skills.', 'He', 'achieve', 'all', 'his', 'goals,', 'pass', 'the', 'exams,', 'and', 'improve', 'his', 'grades.', 'He', 'also', 'discover', 'a', 'new', 'passion', 'for', 'science.']進程已結束,退出代碼為 0
NLTK還原
此外,我們還能夠借助自然語言處理工具包NLTK中所內置的詞典資源,以此來實現詞目還原的操作。我們用代碼來演示下:
完整代碼
# 導入自然語言處理工具包nltk
import nltk
# 從nltk的tokenize模塊導入word_tokenize函數,用于將文本分詞
from nltk.tokenize import word_tokenize
# 從nltk的stem模塊導入WordNetLemmatizer類,用于詞形還原
from nltk.stem import WordNetLemmatizer
# 從nltk的corpus模塊導入wordnet,提供詞法信息
from nltk.corpus import wordnet# 定義一個名為NLPTextNormalization的類,用于自然語言處理中的文本規范化操作
class NLPTextNormalization:# 類的構造方法,目前為空,不執行任何初始化操作def __init__(self):# pass語句占位,不進行實際操作pass# 定義一個實例方法text_normalization用于文本規范化處理def text_normalization(self):# 下載分詞所需的punkt_tab資源包,quiet=True表示安靜模式,不顯示下載詳細信息nltk.download('punkt_tab', quiet=True)# 下載wordnet資源包,用于詞形還原等操作,quiet=True表示安靜模式nltk.download('wordnet', quiet=True)# 創建一個WordNetLemmatizer類的實例,用于后續的詞形還原操作word_lemmatizer = WordNetLemmatizer()# 定義一個待進行詞目還原處理的原始句子original_sentence = ("He studied hard for his tests and learned a lot of new skills. He achieved all his ""goals, passed the exams, and improved his grades. He also discovered a new passion ""for science.")# 使用word_tokenize函數對原始句子進行分詞,得到單詞列表tokenized_word_list = word_tokenize(original_sentence)# 打印詞目還原操作前的單詞列表print(f'詞目還原前:{tokenized_word_list}')# 初始化一個空列表,用于存儲詞目還原后的單詞lemmatized_result_list = []# 遍歷分詞后得到的單詞列表中的每個單詞for single_word in tokenized_word_list:# 對當前單詞按動詞詞性進行詞目還原,并添加到結果列表中lemmatized_result_list.append(word_lemmatizer.lemmatize(single_word, wordnet.VERB))# 打印詞目還原操作后的單詞列表print(f'詞目還原后:{lemmatized_result_list}')# 程序入口判斷,如果當前腳本作為主程序運行
if __name__ == "__main__":# 創建NLPTextNormalization類的一個實例nlp_text_normalization = NLPTextNormalization()# 調用實例的text_normalization方法進行文本規范化處理nlp_text_normalization.text_normalization()
運行結果
詞目還原前:['He', 'studied', 'hard', 'for', 'his', 'tests', 'and', 'learned', 'a', 'lot', 'of', 'new', 'skills', '.', 'He', 'achieved', 'all', 'his', 'goals', ',', 'passed', 'the', 'exams', ',', 'and', 'improved', 'his', 'grades', '.', 'He', 'also', 'discovered', 'a', 'new', 'passion', 'for', 'science', '.']
詞目還原后:['He', 'study', 'hard', 'for', 'his', 'test', 'and', 'learn', 'a', 'lot', 'of', 'new', 'skills', '.', 'He', 'achieve', 'all', 'his', 'goals', ',', 'pass', 'the', 'exams', ',', 'and', 'improve', 'his', 'grade', '.', 'He', 'also', 'discover', 'a', 'new', 'passion', 'for', 'science', '.']進程已結束,退出代碼為 0
綜上,我們看到,在自然語言處理應用中,詞目還原是不可或缺的。它能夠提高特征的質量和穩定性,減少因詞匯形式差異帶來的噪聲干擾,使模型能夠更專注于語義信息,進而提升模型的性能和泛化能力,讓自然語言處理系統在各種任務中表現得更加出色。?
分句
在自然語言處理中,分句(Sentence Segmentation)是指將一段連續的文本分割成一個個獨立的句子,它是自然語言處理的基礎任務之一,在文本處理流程中起著關鍵的前置作用。具體來說,分句有以下特點和作用:
- 基于標點符號:在許多語言中,最直觀的分句依據是標點符號,如句號(.)、問號(?)、感嘆號(!)等。
- 考慮上下文和語言規則:為了更準確地分句,自然語言處理系統需要考慮上下文信息和語言的語法、語義規則。
- 跨語言差異:不同的語言在標點符號的使用和句子結構上存在差異,這使得分句在不同語言中面臨不同的挑戰。
- 應用場景廣泛:分句是許多自然語言處理任務的基礎,如文本摘要、機器翻譯、信息檢索、情感分析等。
實現分句的方法通常包括基于規則的方法(利用標點符號和語言規則)、基于統計的方法(通過機器學習算法從大量標注數據中學習分句模式)以及兩者結合的方法。我們用代碼來演示一下:
完整代碼
# 從NLTK庫導入regexp_tokenize函數,用于基于正則表達式對文本進行靈活分詞
from nltk import regexp_tokenize# 定義一個名為NLPTextNormalization的類,用于自然語言處理中的文本規范化操作
class NLPTextNormalization:# 類的構造方法,目前為空,不執行任何初始化操作def __init__(self):# pass語句占位,不進行實際操作pass# 定義一個實例方法text_normalization用于文本規范化處理def text_normalization(self):sentence_spliter = set([".", "?", '!', '...'])# 定義原始句子,用于后續使用正則表達式進行分詞處理original_sentence = ("Did you pay €6.99 on researchgate.net for access to that full-text paper? ""No, I got it through our university library! It's really convenient...")# 定義正則表達式模式,可匹配普通單詞、含連字符或單引號的復合單詞以及以非空白字符開頭的字符串pattern = r"\w+(?:[-']\w+)*|\S\w*"# 定義用于匹配網址的正則表達式模式,可匹配如example.com等形式的網址web_pattern = r"(?:\w+\.)+\w+(?:\.)*"# 定義用于匹配價格符號相關內容的正則表達式模式,可匹配如$12.34、12%等形式price_symbol_pattern = r"\$?\d+(?:\.\d+)?%?"# 定義用于匹配省略號的正則表達式模式,可精準匹配文本中的省略號ellipsis_pattern = r"\.\.\."# 將省略號模式、價格符號模式、網址模式和原模式通過邏輯或連接,形成新的匹配模式pattern = ellipsis_pattern + r"|" + price_symbol_pattern + r"|" + web_pattern + r"|" + pattern# 使用regexp_tokenize函數,依據指定的正則表達式模式對句子進行分詞,并將結果存儲在tokens變量中tokens = regexp_tokenize(original_sentence, pattern)# 初始化存儲分句結果的列表segmented_sentence_list = []# 初始化存儲每個句子起始索引的列表,初始值為0sentence_start_index_list = [0]# 遍歷分詞后的標記列表,獲取標記索引和標記內容for token_index, single_token in enumerate(tokens):# 判斷當前標記是否為句子分隔符if single_token in sentence_spliter:# 若為分隔符,將從當前句子起始索引到當前標記的所有標記作為一個句子添加到結果列表segmented_sentence_list.append(tokens[sentence_start_index_list[-1]:token_index + 1])# 更新下一個句子的起始索引sentence_start_index_list.append(token_index + 1)# 檢查是否還有剩余標記未組成句子if sentence_start_index_list[-1] != len(tokens):# 若有剩余標記,將其作為一個句子添加到結果列表segmented_sentence_list.append(tokens[sentence_start_index_list[-1]:])# 打印分句結果提示信息print(f"分句結果:")# 遍歷分句結果列表for segmented_sentence in segmented_sentence_list:# 打印每個分句的內容print(segmented_sentence)# 程序入口判斷,如果當前腳本作為主程序運行
if __name__ == "__main__":# 創建NLPTextNormalization類的一個實例nlp_text_normalization = NLPTextNormalization()# 調用實例的text_normalization方法進行文本規范化處理nlp_text_normalization.text_normalization()
運行結果
分句結果:
['Did', 'you', 'pay', '€6', '.99', 'on', 'researchgate.net', 'for', 'access', 'to', 'that', 'full-text', 'paper', '?']
['No', ',', 'I', 'got', 'it', 'through', 'our', 'university', 'library', '!']
["It's", 'really', 'convenient', '...']進程已結束,退出代碼為 0
從上我們看到,分句具有多方面的重要意義。從文本理解角度看,將連續的文本分割成一個個獨立的句子,能夠幫助計算機更清晰地把握文本的結構和語義層次。對于文本生成任務,如機器翻譯、文本摘要等,分句是基礎且關鍵的步驟。
總之,分句作為自然語言處理的基礎操作,為后續的各種任務提供了必要的前提和支持,對提升自然語言處理系統的整體效果具有不可忽視的作用。?
結束
好了。以上就是本次分享的全部內容了。大家是否理解并掌握了自然語言處理中的文本規范化。本規范化作為自然語言處理的基礎環節,對提升語義理解、信息檢索等任務的效果至關重要。希望大家能在實際操作中多加運用,不斷深化對這一概念的理解和掌握。
我相信大家對文本規范化已有了一定的認識。博主希望大家通過本次分享的知識,將其應用到實際項目中,通過實踐來鞏固知識。期待大家在后續的學習和工作中,能夠熟練運用文本規范化的技巧,為自然語言處理任務的順利開展奠定堅實基礎。
那么本次分享就到這里了。最后,博主還是那句話:請大家多去大膽的嘗試和使用,成功總是在不斷的失敗中試驗出來的,敢于嘗試就已經成功了一半。如果大家對博主分享的內容感興趣或有幫助,請點贊和關注。大家的點贊和關注是博主持續分享的動力🤭,博主也希望讓更多的人學習到新的知識。