一、基本概念
自然語言處理也就是Natural Language Processing,簡稱NLP。NLP就是人工只能和語言學領域的一個分支,涉及到計算機與人類語言之間的相互作用。
主要目標是讓計算機能夠理解、解釋和生成人類語言的數據。
1 自然語言處理的基本介紹
NLP包括但不限于語言理解、語言生成、機器翻譯、情感分析、語音識別和語音合成。同時在中文環境,但需要考慮中文的特殊性,如中文分詞、中文語法和語義分析。
1.1 使用NLP的原因
自然語言處理就是在機器語言和人類語言之間溝通的橋梁,以實現人機交流的目的。機器也有自己的語言就是數字信息,NLP就是讓機器學會處理人類的語言。
2 NLP的應用方向
2.1 自然語言理解
典型的自然語言理解:
情感分析:對給定的文本輸入,在給定的選項范圍內分析文本的情緒是正面還是負面。
文本分類:對于給定文本輸入,在給定的選項范圍內對文本進行二分類或多分類。
信息檢索:搜索引擎依托于多種技術,如網絡爬蟲技術、檢索排序技術、網頁處理技術等等,為信息檢索用戶提供快速、高相關性的信息服務。例如百度、Google。
抽取式閱讀理解:對于給定的文本輸入,用文本中的內容回答問題。
語義匹配:對給定的兩個文本輸入,判斷是否相似。
自然語言推理:對給定的兩個文本輸入,判斷是蘊涵、矛盾還是無關。
命名實體識別:對給定的文本輸入,返回含有命名實體及其對應標簽的映射,例如{'糖尿病':'疾病'}。
文本摘要:對給定的文本輸入,用文本中的內容對文本進行摘要。
2.2 自然語言轉換
自然語言轉換(NLT)任務包括但不限于:
機器翻譯:將一種自然語言轉換為另一種自然語言,包括從源語言到目標語言的文本或語音轉換。機器學習模型目前常用seq2seq模型來實現,由Encoder和Decoder組成。
非抽取式閱讀理解:接受給定文本的輸入,能夠理解自然語言問題,并回答問題。
文本風格轉換:將文本從一種風格轉換為另一種風格,如將正式文本轉換為非正式文本。
語音識別:將人類的語音轉換為文本,用于語音指令、口述文本、會議記錄等。比如微信的語音轉文字。
意圖改寫:對給定的文本輸入,將原始文本中的意圖或核心信息重新表述,以不同的詞匯和句式表達相同的意思,同時保持原意的準確性和完整性。
2.3 自然語言生成
自然語言生成(NLG)任務:
文本生成:根據給定的上下文或提示,自動生成文本,如自動寫作、詩歌創作、故事生成等。
語音合成:將文本轉換為聽起來自然的語音,用于有聲書、導航系統、虛擬助手等。
聊天機器人:能夠與人類實現多輪對話的聊天助手。
文本到知識:從文本中提取知識,構建知識圖譜或語義網絡。
語義解析:將自然語言表達轉換為形式化的邏輯表示,用于命令解析、查詢理解等。
2.4 同步序列到序列
特點:輸入序列和輸出序列是嚴格對齊的,即在處理一個輸入序列的元素時,模型會即時產生相應的輸出序列的元素。
典型應用:通常應用在那些輸入和輸出之間存在一對一對齊關系的任務中。例如,逐詞對齊、詞性標注(POS tagging)、命名實體識別(NER)。
優點:處理速度快,因為輸入和輸出同步生成,不需要等待整個輸入處理完畢。
缺點:要求輸入和輸出嚴格對齊,不適合處理輸入輸出之間存在較復雜映射關系的任務。
2. 5異步序列到序列
特點:輸入序列和輸出序列之間不需要嚴格對齊,模型可以在處理完整個輸入序列后,再開始生成輸出序列。大多數的Seq2Seq模型都是異步的。
典型應用:用于需要對整個輸入序列進行全局上下文理解后,才能生成輸出的任務。例如,機器翻譯(句子級別)、文本摘要生成、問答系統等。經典的Seq2Seq模型,如基于RNN或Transformer的模型,通常都屬于異步類型。
優點:模型可以利用輸入序列的全局上下文信息進行更準確的輸出生成。
缺點:生成速度較慢,因為需要等待整個輸入序列處理完畢后才開始生成輸出。
3 NLP基礎概率
(1)詞表/詞庫(Vocabulary):文本數據集中出現的所有單詞的集合。
(2)語料庫(Corpus):用于NLP任務的文本數據集合,可以是大規模的書籍、文章、網頁等。
(3)詞嵌入(Word Embedding):將單詞映射到低維連續向量空間的技術,用于捕捉單詞的語義和語法信息。
(4)停用詞(Stop Words):在文本處理中被忽略的常見單詞,如"a"、"the"、"is"等,它們通常對文本的意義貢獻較小。
(5)分詞(Tokenization):將文本分割成一個個單詞或標記的過程,為后續處理提供基本的單位。
(6) 詞頻(Term Frequency):在給定文檔中,某個單詞出現的次數。
(7)逆文檔頻率(Inverse Document Frequency):用于衡量一個單詞在整個語料庫中的重要性,是將詞頻取倒數并取 對數的值。
(8) TF-IDF(Term Frequency-Inverse Document Frequency):一種常用的文本特征表示方法,綜合考慮了詞頻和逆文檔頻率。
(9) 詞袋模型(Bag of Words):將文本表示為一個單詞的集合,忽略了單詞的順序和語法結構。
(10)N-gram:連續的N個單詞構成的序列,用于捕捉文本中的局部特征和上下文信息。
(11)序列:指的是一個按順序排列的元素集合。這些元素可以是字符、單詞、句子,甚至更抽象的結構。序列的每個元素都有特定的順序和位置,這意味著它們不能隨意重排,否則會影響其意義或功能。
序列的常見類型
字符序列:
一個字符串就是一個字符序列,每個字符按順序排列。"hello"
是一個由h
、e
、l
、l
、o
組成的字符序列。單詞序列:
一句話可以看作是一個單詞序列,每個單詞按照一定順序排列。"I love NLP"
是一個由I
、love
、NLP
組成的單詞序列。時序數據:
在時間序列中,元素是按時間順序排列的,常用于預測問題。股票價格數據可以看作是隨時間變化的數值序列。語音序列:
在語音處理任務中,語音信號可以被分解為按時間順序排列的幀序列(特征向量序列)。其他序列:
序列還可以表示一些更抽象的結構,比如DNA序列(由堿基組成的序列)、事件序列等。
4 NLP的基本流程
中文特殊性在文本預處理環節。
1、中文文本沒有像英文單詞的空格,則不像英文一樣直接用簡單的空格和標點符號完成分詞,一般需要用分詞算法完成分詞。
2、中文的編碼不是utf-8,是Unicode,則在預處理的時候,有編碼處理的問題。
3、中文NLP的基本流程由語料獲取、語料預處理、文本向量化、模型構建、模型訓練和模型評價。
(1)語料獲取
文本語料的獲取一般有以下幾種方法:
(1)利用已經建好的數據集或第三方語料庫。
(2)獲取網絡數據。
(3)與第三方合作獲取數據。
(2)語料預處理
獲取語料后還需要對語料進行預處理。
(1)去除數據中非文本內容
獲取的文本數據可能中存在很多無用的內容,如爬取的一些HTML代碼、CSS標簽和不需要的標點符號等,這些都需要分步驟去除。
少量非文本內容可以直接用 Python的正則表達式刪除;
復雜的非文本內容可以通過 Python的 Beautiful Soup庫去除。
(2)中文分詞
常用的中文分詞軟件:如jieba、FoolNLTK、HanLP、THULAC、NLPIR、LTP等。
其中jieba是使用 Python語言編寫的。
(3)詞性標注
詞性標注指給詞語打上詞類標簽,如名詞、動詞、形容詞等。
(4)去停用詞
停用詞就是句子中沒必要存在的詞,去掉停用詞后對理解整個句子的語義沒有影響。
中文文本中存在大量的虛詞、代詞或者沒有特定含義的動詞、名詞,在文本分析的時候需要去掉。
(3)文本向量化(特征工程)
文本數據經過預處理去除數據中非文本內容、中文分詞、詞性標注和去停用詞后,但此時還是無法直接將文本用于任務計算,需要通過某些處理手段,預先將文本轉化為特征向量。
一般可以調用一些模型來對文本進行處理,常用的模型有詞袋模型(Bag of Words Model)、獨熱表示、TF-IDF 表示、n元語法(n-gram)模型和 Word2Vec模型等。
(4)模型構建
文本向量化后,根據文本分析的需求選擇合適的模型進行模型構建,同類模型也需要多準備幾個備選用于效果對比。
過于復雜的模型往往不是最優的選擇,模型的復雜度與模型訓練時間呈正相關,模型復雜度越高,模型訓練時間往往也越長,但結果的精度可能與簡單的模型相差無幾。
NLP中使用的模型包括機器學習模型和深度學習模型兩種。
常用的機器學習模型有SVM、Naive Bayes、決策樹、K-means 等;
常用的深度學習模型有TextCNN、RNN、LSTM、GRM、Seq2Seq、transformer等。
(5)模型訓練
訓練時可先使用小批量數據進行試驗,這樣可以避免直接使用大批量數據訓練導致訓練時間過長等問題。
在模型訓練的過程中要注意兩個問題:
在訓練集上表現很好,但在測試集上表現很差的過擬合問題;
模型不能很好地擬合數據的欠擬合問題,同時還要避免出現梯度消失和梯度爆炸問題。
僅訓練一次的模型往往無法達到理想的精度與效果,還需要進行模型調優迭代,提升模型的性能。
模型調優往往是一個復雜、冗長且枯燥的過程,需要多次對模型的參數做出修正;調優的同時需要權衡模型的精度與泛用性,在提高模型精度的同時還需要避免過擬合。
當一個模型隨著時間的推移,在新的數據集中的評價不斷下降時,就意味著這個模型無法適應新的數據的變化,此時模型需要進行重新訓練。
(6)模型評價
模型的評價指標指主要有準確率(Accuracy)、精確率(Precision)、召回率、F1值、ROC曲線、AUC線等。
如分類模型常用的評價方法有準確率、精確率AUC曲線等。同一種評價方法也往往適用于多種類型的模型。
二、NLP中的特征工程
特征是數據中抽取出來的對結果預測有用的信息。
因為文本是一種非結構化數據,機器學習模型無法直接處理,則必須通過特征工程啦提取有用信息。
在自然語言處理中,特征工程是指將文本數據轉換成適合機器學習模型使用的數值表示過程。通過特征工程能讓機器學習到文本數據中的一些特征,比如詞性、語法、相似度。
1 詞向量的引入
詞向量也稱詞嵌入,這些向量能夠體現詞語之間的語義關系,是對詞語義或含義的數值向量表示,包括字面意義和隱含意義。可以捕捉到詞的內涵,將這些含義結合在一起構成一個稠密的浮點數向量,這個稠密向量支持查詢和邏輯推理。
詞嵌入實際上是單個詞在預定義的向量空間中被表示為實數向量,每個單詞都映射到一個向量。
在一個文本中包含“貓”“狗”“愛情”等若干單詞,而這若干單詞映射到向量空間中:
“貓”對應的向量為(0.1 0.2 0.3)
“狗”對應的向量為(0.2 0.2 0.4)
“愛情”對應的映射為(-0.4 -0.5 -0.2)
這個映射的過程就叫做詞嵌入。
對于機器而言,三個詞都是用0,1表示成二進制的字符串而已,無法對其進行計算。而通過詞嵌入這種方式將單詞轉變為詞向量,機器便可對單詞進行計算,通過計算不同詞向量之間夾角余弦值cosine而得出單詞之間的相似性。
2. 傳統NLP中的特征工程
2.1 獨熱編碼 one - hot
獨熱編碼(One-Hot Encodinga)用于將離散的類別型數據轉換為數值型表示,以便輸入到機器學習模型中。特點是將每個類別表示為一個向量,在該向量中,只有一個元素為1,其余元素全部為0。
One-Hot Encoding 的工作原理
假設你有一個包含以下類別的分類任務:
紅色(red)、綠色(green)、藍色(blue)
要將這些類別轉換為 One-Hot 編碼,為每個類別創建一個獨特的二進制向量:
紅色:
[1, 0, 0]
綠色:
[0, 1, 0]
藍色:
[0, 0, 1]
則輸入數據是“紅色”,在使用 One-Hot 編碼后,它將被表示為 [1, 0, 0]
。
在NLP當中
Time flies like an arrow.
Fruit flies like a banana.
構成詞庫{time, fruit, flies, like, a, an, arrow, banana}
banana的one-hot表示就是:[0,0,0,0,0,0,0,1]
"like a banana” 的one-hot表示就是:[0,0,0,1,1,0,0,1]
2.2 詞頻-逆文檔頻率(TF-IDF)
(1)詞頻
在計算詞頻(TF)時,分母是文檔中的總詞數,而不考慮每個詞是否重復。這意味著無論詞是否重復,分母始終是文檔中所有詞的數量總和。
例如,the在總次數為1000的文章中出現的總次數為20,則TF("the",d)=20/1000=0.02
短語,句子或者文檔的詞頻表示就是其組成單詞‘one-hot’表示向量的總和。
“Fruit flies like time flies a fruit” ,DF表示為:[1,2,2,1,1,0,0,0],總詞數:(7)TF表示為:[0.14,0.29,0.29,0.14,0.14,0,0,0](1/7,2/7,2/7,1/7,1/7,0,0,0)
(2)逆文檔頻率(Inverse Document Frequency, IDF)
逆文檔頻率用來衡量一個詞在整個文檔集(語料庫)中的重要性,是降低那些在很多文檔中頻繁出現的詞的權重。
D
表示文檔集合,t
是要計算的詞。+1
是為了避免分母為 0 的情況。
例如,the在總篇數為1000篇的文章庫中,在10篇文章中出現,則
3)TF-IDF 計算
最終,TF-IDF 是將 TF 和 IDF 相乘得出的結果,公式如下:
通過這個方法,一個詞在特定文檔中出現的頻率越高(TF高),并且在整個語料庫中出現得越少(IDF高),它的 TF-IDF 值就越高。
可以使模型更加關注那些在某篇文檔中特別重要但不常見的詞。
則"the":TF-IDF("the",d,D)=*0.02
2.3 n-grams
特征工程中的一種技術。
通過將文本中的連續 n 個詞(或字符)組合起來,形成一個短語來捕捉文本中的局部上下文信息。
unigram 只關心詞的獨立出現頻率,而 bigram 和 trigram 能捕捉到詞之間的順序關系。bigram 中幾個詞同時出現,這種信息在建模中會比詞獨立的出現頻率更有價值。
假設句子為 "I love NLP and machine learning":
1-gram(Unigram): ["I", "love", "NLP", "and", "machine", "learning"]
2-grams(Bigram): ["I love", "love NLP", "NLP and", "and machine", "machine learning"]
3-grams(Trigram): ["I love NLP", "love NLP and", "NLP and machine", "and machine learning"]
將 n-grams 與 TF-IDF 相結合是文本特征工程中非常常見的做法,它不僅能夠捕捉詞與詞之間的局部關系,還能通過 TF-IDF 來衡量這些短語在整個語料庫中的重要性。
結合 n-grams 與 TF-IDF 的步驟:
生成 n-grams:首先從文本中生成 n-grams,通常使用
CountVectorizer
或類似的工具生成。計算詞頻 (TF):統計每個 n-gram 在文本中出現的頻率。
計算逆文檔頻率 (IDF):計算 n-gram 在所有文檔中出現的頻率,稀有的 n-grams 會得到較高的權重,而常見的 n-grams 權重較低。
計算 TF-IDF:將每個 n-gram 的 TF 和 IDF 相乘,得到 TF-IDF 權重,表示該 n-gram 對特定文本的重要性。
tip:當使用 2-grams 時,I love
和 love NLP
被看作是兩個單獨的特征,總共有兩個特征(總特征數 = 2)。
但是傳統的NLP特征工程缺點:
詞典多長,向量就多長,一般詞典都非常大,所以計算量巨大;
如果一句話十個詞,就至少向量里面有9990個0,既冗余又沒有意義;
同時還存在語義鴻溝。
我們可以使用稠密編碼,也就是特征嵌入。
3. 深度學習中NLP的特征輸入
使用分布式單詞表示技術,也稱詞嵌入表示。
通過查看所使用的單詞的周圍單詞(即上下文)來學習單詞表示。這種表示方式將詞表示為一個粘稠的序列,在保留詞上下文信息同時,避免維度過大導致的計算困難。
3.1 稠密編碼(特征嵌入)
稠密編碼在機器學習和深度學習中,通常指的是將離散或高維稀疏數據轉化為低維的連續、密集向量表示。
稠密向量表示不再以one-hot中的一維來表示各個特征,是把每個核心特征(詞,詞性,位置等)都嵌入到d維空間中,并用空間中的一個向量表示。
空間維度d遠小于每個特征的樣本數(40000的詞表,100/200維向量)。
嵌入的向量(每個核心特征的向量表示)作為網絡參數與神經網絡中的其他參數一起被訓練。
特征嵌入,也是詞嵌入,是稠密編碼的一種表現形式。
目的是將離散的類別、對象或其他類型的特征映射到一個連續的向量空間。通過這種方式,嵌入后的向量可以捕捉不同特征之間的語義關系,并且便于在后續的機器學習模型中使用。
特點:
低維度:相比稀疏表示(如獨熱編碼),稠密編碼的維度更低,能夠減少計算和存儲成本。
語義相似性:嵌入向量之間的距離(如歐氏距離或余弦相似度)可以表示這些對象之間的語義相似性。
可微學習:嵌入表示通常通過神經網絡進行學習,并且通過反向傳播算法進行優化。
3.2 詞嵌入算法
3.2.1 Embedding Layer
Embedding Layer是與特定自然語言處理上的神經網絡模型聯合學習的單詞嵌入。該嵌入方法將清理好的文本中的單詞進行one hot編碼(獨熱編碼),向量空間的大小或維度被指定為模型的一部分,例如50、100或200維。
向量以小的隨機數進行初始化。Embedding Layer用于神經網絡的前端,并采用反向傳播算法進行監督。
目標是希望神經網絡發現如下這樣的規律:已知一句話的前幾個字,預測下一個字是什么,于是有了NNLM 語言模型搭建的網絡結構圖:
詞嵌入層的使用
詞嵌入層首先會根據輸入的詞的數量構建一個詞向量矩陣。
例如:? 100 個詞,每個詞希望轉換成 128 維度的向量,那么構建的矩陣形狀即為: 100*128,輸入的每個詞都對應了一個該矩陣中的一個向量。
在 PyTorch可以使用 nn.Embedding 詞嵌入層來實現輸入詞的向量化。
先將語料進行分詞,構建詞與索引的映射,我們可以把這個映射叫做詞表,詞表中每個詞都對應了一個唯一的索引。
然后使用 nn.Embedding 構建詞嵌入矩陣,詞索引對應的向量即為該詞對應的數值化后的向量表示。
例如,我們的文本數據為: "北京冬奧的進度條已經過半,不少外國運動員在完成自己的比賽后踏上歸途。",接下來,我們看下如何使用詞嵌入層將其進行轉換為向量表示,步驟如下:
首先,將文本進行分詞;
然后,根據詞構建詞表;
最后,使用嵌入層將文本轉換為向量表示。
nn.Embedding 對象構建時,最主要有兩個參數:
num_embeddings 表示詞的數量
embedding_dim 表示用多少維的向量來表示每個詞
nn.Embedding(num_embeddings=10, embedding_dim=4)
接下來,我們就實現下剛才的需求:
補充jieba庫:
jieba.cut
:
需要分詞的字符串;
cut_all(True、False)用來控制是否采用全模式;
HMM 參數用來控制是否使用 HMM 模型。
jieba.cut_for_search
?:
需要分詞的字符串;
是否使用 HMM 模型。
jieba.cut
?以及?jieba.cut_for_search:
?返回的結構都是一個可迭代的 generator,可以使用 for 循環來獲得分詞后得到的每一個詞語(unicode)。
jieba.lcut
、jieba.lcut_for_search:
分詞后將詞存儲為list,
直接返回。
jieba.Tokenizer(dictionary=DEFAULT_DICT)
?:
新建自定義分詞器,可用于同時使用不同詞典。jieba.dt
?為默認分詞器,所有全局分詞相關函數都是該分詞器的映射。
代碼實例:
import torch
import torch.nn as nn
import jieba
text='人面不知何處去,桃花依舊笑春風。'"""對文本進行精確模式分詞后以列表形式返回"""
words=jieba.lcut(text)
print(words)'''初始化兩個空字典,通過索引找詞語、通過詞語找索引'''
index_to_word={}
word_to_index={}'''set()去重,list()轉換,用漢語獲取不重復的詞語列表'''
unique_words=list(set(words))'''遍歷去重后的詞語列表(unique_words),同時獲取每個詞語的索引和詞語本身'''
for idx,word in enumerate(unique_words):'''建立索引到詞語的映射,例如0:‘人’ '''index_to_word[idx]=word'''建立詞語到索引的映射,例如‘人’:0 '''word_to_index[word]=idx'''nn.Embedding(),創建一個詞嵌入層,以詞的數量,每個詞以4維向量表示,若有10個詞,則每行是一個詞的初始隨機向量'''embed = nn.Embedding(num_embeddings=len(index_to_word), embedding_dim=4)'''遍歷分詞結果,對原始文本的每個詞進行處理'''
for word in words:'''獲取詞索引,從word_to_index字典中查詢該詞對應的數字索引'''idx=word_to_index[word]'''轉換為張量,torch.tensor(idx)將索引轉換為pytorch整數張量,必需格式''''''embed()執行矩陣查找操作,等價于embed。weight[idx],返回該索引對應的4維向量'''word_vec=embed(torch.tensor(idx))'''%3s:將詞語格式化為3字符寬度(對齊輸出)'''print('%3s\t'%word,word_vec)
結果:
['人面', '不知', '何處', '去', ',', '桃花', '依舊', '笑', '春風', '。']
人面?? ? tensor([-0.7112, -0.9587, -0.1539, -1.4211], grad_fn=<EmbeddingBackward0>)
不知?? ? tensor([-0.9177, -2.1300, -1.5096, -0.5236], grad_fn=<EmbeddingBackward0>)
何處?? ? tensor([ 0.9228, -1.7725, ?0.5607, -0.4327], grad_fn=<EmbeddingBackward0>)
去?? ? tensor([-1.0147, -1.8442, ?1.8837, -0.4600], grad_fn=<EmbeddingBackward0>)
,?? ? tensor([-0.2569, -0.6907, ?0.9694, ?0.7406], grad_fn=<EmbeddingBackward0>)
桃花?? ? tensor([ 1.2791, -0.7683, -0.9927, -0.2771], grad_fn=<EmbeddingBackward0>)
依舊?? ? tensor([-0.9680, ?0.3950, ?0.4738, ?0.2836], grad_fn=<EmbeddingBackward0>)
笑?? ? tensor([ 0.0740, ?1.4216, -1.6399, ?0.7748], grad_fn=<EmbeddingBackward0>)
春風?? ? tensor([ 1.2677, -0.1979, -0.4704, -1.1983], grad_fn=<EmbeddingBackward0>)
。?? ? tensor([-1.3815, ?1.1088, -0.4722, ?0.4201], grad_fn=<EmbeddingBackward0>)
Embedding類是一個大小為(num_embedding,embedding_dim)的矩陣,每一行是某個詞匯的嵌入向量。通過索引可以從這個矩陣中提取對應詞匯的向量表示,因為 nn.Embedding
在內部通過索引直接查找矩陣中的行。