本文認識文本預處理以及它的作用、以及文本預處理有哪些主要環節。文本預處理是指在將文本數據用于深度學習模型訓練之前,對文本數據進行一系列的清洗、轉換和處理操作,以消除非結構化文本的噪聲和不必要的信息,并將文本數據轉化為適合模型訓練的格式。總的來說,文本預處理的目的是將原始的非結構化文本數據轉化為結構化的數值數據,從而提高模型的性能和準確度。
一、 文本處理的基本方法
假設我們要對以下句子進行分詞:“今天的天氣真好呀”。采用正向最大匹配算法,我們可以按照以下步驟進行分詞:
- 確定最大分詞長度。假設我們設定最大分詞長度為3。
- 從左往右掃描句子,取出前3個字符“今天的”。
- 查找詞庫,發現“今天的”不是一個詞,因此縮小分詞長度,取出前2個字符“今天”。
- 查找詞庫,發現“今天”是一個單字詞,因此將“今天”作為一個詞。
- 繼續掃描剩下的字符,取出前3個字符“的天氣”。
- 查找詞庫,發現“的天氣”不是一個詞,因此縮小分詞長度,取出前2個字符“的天”。
- 查找詞庫,發現“的天”不是一個詞,因此縮小分詞長度,取出前1個字符“的”。
- 查找詞庫,發現“的”是一個詞,因此將“的”作為一個詞。
最終的分詞結果是:“今天/的/天氣/真好/呀”。可以看到,這個過程會依賴一個詞典,但是詞典具有一定的缺點,尤其對于一些專有名詞,比如“央視新聞客戶端”,央視新聞是一個詞,不能分成兩個詞網絡名詞,比如你耗子尾汁,會被分割成耗子,尾汁歧義分割,比如羽毛球拍賣完了,會被分割成,羽毛球,拍賣,完,了
1.1 Jieba 分詞
Jieba提供了三種分詞模式:
● 精確模式:試圖將句子最精確地切開,適合文本分析。
● 全模式:把句子中所有可以成詞的詞語都掃描出來,速度非常快,但是不能解決歧義。
● 搜索引擎模式:在精確模式的基礎上,對長詞再次切分,提高召回率,適合用于搜索引擎分詞。
安裝:pip install jieba
import jieba
sent = '南京市長江大橋'
seg_list = jieba.cut(sent, cut_all=True)
print('全模式:', '/ '.join(seg_list))
seg_list = jieba.cut(sent, cut_all=False)
print('精確模式:', '/ '.join(seg_list))
seg_list = jieba.cut(sent)
print('默認精確模式:', '/ '.join(seg_list))
seg_list = jieba.cut_for_search(sent)
print('搜索引擎模式', '/ '.join(seg_list))
輸出結果:
全模式: 南京/ 南京市/ 京市/ 市長/ 長江/ 長江大橋/ 大橋
精確模式: 南京市/ 長江大橋
默認精確模式: 南京市/ 長江大橋
搜索引擎模式 南京/ 京市/ 南京市/ 長江/ 大橋/ 長江大橋
jieba支持用戶自定義詞典開發者可以指定自己自定義的詞典,以便包含 jieba 詞庫里沒有的詞。雖然 jieba 有新詞識別能力,但是自行添加新詞可以保證更高的正確率。比如我們自定義如下的user_dict.txt
創新辦 3 i
云計算 5 n
一個詞占一行;每一行分三部分:詞語、詞頻(可省略)、詞性(可省略),用空格隔開,順序不可顛倒。
import jieba
sent = '北上資金凈流入200億,同比增長15%,云計算板塊漲的最多'
#先導入詞典
jieba.load_userdict("./user_dict.txt")
seg_list = jieba.cut(sent)
print('精確模式:', '/ '.join(seg_list))
不加詞典分詞: 北上/ 資金/ 凈流入/ 200/ 億/ ,/ 同比/ 增長/ 15%/ ,/ 云/ 計算/ 板塊/ 漲/ 的/ 最/ 多
加上自定義詞典分詞: 北上/ 資金/ 凈流入/ 200/ 億/ ,/ 同比/ 增長/ 15%/ ,/ 云計算/ 板塊/ 漲/ 的/ 最/ 多.
1.2 pyltp 分詞
pyltp 是 LTP 的 Python 封裝,提供了分詞,詞性標注,命名實體識別,依存句法分析,語義角色標注的功能。首先下載源碼,從源代碼編譯pyltp,python環境需要先裝cmake。官網:http://ltp.ai/
$ git clone https://github.com/HIT-SCIR/pyltp
$ git submodule init
$ git submodule update
$ python setup.py install
當setup.py 執行成功之后,能進行import pyltp,說明就成功了。然后需要到http://ltp.ai/download.html 下載模型。
其中分詞用到的cws.model模型
from pyltp import Segmentor
if __name__ == '__main__':
segmentor = Segmentor("./ltp_data_v3.4.0/cws.model")
world_list = segmentor.segment("奧巴馬與克林頓昨晚在白宮發表了演說")
print(world_list)
輸出:['奧巴馬', '與', '克林頓', '昨晚', '在', '白宮', '發表', '了', '演說']
除了pyltp之外,還有hanlp分詞,在此不一一列舉。
1.3 pyltp 命名實體識別和語義角色標注
命名實體識別 (Named Entity Recognition, NER) 是在句子的詞序列中定位并識別人名、地名、機構名等實體的任務。想要實現命名實體識別需要知道一個句子中的分詞和這些詞對應的詞性標注。
LTP 采用 BIESO標注體系。B 表示實體開始詞,I表示實體中間詞,E表示實體結束詞,S表示單獨成實體,O表示不構成命名實體。LTP 提供的命名實體類型為:人名(Nh)、地名(Ns)、機構名(Ni)。B、I、E、S位置標簽和實體類型標簽之間用一個橫線 - 相連;O標簽后沒有類型標簽。
if __name__ == '__main__':
# 第一步分詞
segmentor = Segmentor(MODEL_PATH + "/cws.model")
world_list = segmentor.segment("奧巴馬與克林頓昨晚在白宮發表了演說")
print(world_list)
# 第二步,詞性標注
postagger = Postagger(MODEL_PATH + "/pos.model")
postag_list = list(postagger.postag(world_list))
print(postag_list)
# 第三步,命名實體識別
recognizer = NamedEntityRecognizer(MODEL_PATH + "/ner.model")
ner = recognizer.recognize(world_list, postag_list)
print("ner,", ner)
# 第四步,句法依存分析
parser = Parser(MODEL_PATH + "/parser.model")
arcs = parser.parse(world_list, postag_list) # 建立依存句法分析樹
# 第五步,語義角色標注
labeller = SementicRoleLabeller(MODEL_PATH + "/pisrl.model")
roles = labeller.label(world_list, postag_list, arcs)
print("roles", roles)
# 釋放模型
recognizer.release()
segmentor.release()
postagger.release()
parser.release()
labeller.release()
輸出:
['奧巴馬', '與', '克林頓', '昨晚', '在', '白宮', '發表', '了', '演說']
['nh', 'p', 'nh', 'nt', 'p', 'n', 'v', 'u', 'v']
ner, ['S-Nh', 'O', 'S-Nh', 'O', 'O', 'O', 'O', 'O', 'O']
roles [(6, [('A0', (0, 2)), ('TMP', (3, 3)), ('LOC', (4, 5)), ('A1', (8, 8))])]
給定的句子是:"奧巴馬與克林頓昨晚在白宮發表了演說。"根據語義角色標注(SRL)任務的目標,我們需要分析句子的謂詞論元結構,也就是回答"誰對誰做了什么"這樣的語義問題。
首先,我們需要確定謂詞(predicate),即動作或狀態的核心詞。在這個句子中,謂詞是"上漲"。
然后,我們需要確定論元(argument),即與謂詞相關的詞語或短語。綜上所述,根據給定的句子,可以將謂詞論元結構分析如下:
謂詞(predicate):發表
論元(argument):
施事者 (Agent):奧巴馬與克林
受事者 (Patient):演說
在何時(when):昨晚
在何地 (where) :白宮
其中(6, [('A0', (0, 2)的含義是,謂詞的索引是6(發表),A0是受試者,索引是從0到2,那么施事者是('奧巴馬', '與', '克林頓')。A1是受試者,索引是8(演說),那么組成一句話為:奧巴馬與克林頓發表演說。TMP代表時間,索引是3(昨晚),LOC是地點,索引是4-5(在白宮)。
二、文本詞向量表示方法
什么是詞向量?文本詞向量的表示是指在文本詞向量模型中,每個詞被映射到的一個固定長度的向量。比如”孫悟空“被映射成向量[2.22,1222,3.444,0.9999],常見的文本詞向量模型有Word2Vec、GloVe、FastText等。在這些模型中,每個詞都會被映射到一個固定長度的向量,通常是幾十或幾百維。
實現詞向量有2大挑戰。1、如何把詞轉換成向量?比如,孫悟空,白骨精,豬八戒是3個離散變量,如何把每個離散變量轉換成一個向量表示2、如何讓向量具有語義信息?比如,孫悟空,白骨精,豬八戒3個離散變量,孫悟空把豬八戒是師兄弟關系,更相近,我們改如何讓詞向量具備這樣的語義相似關系?下面我介紹兩種詞向量表示方法,one-hot與word2vec
2.1 獨熱編碼(one-hot encoding)
獨熱編碼是將每個詞表示成具有n個元素的向量,這個詞向量中只有一個元素是1,其他的元素都是0,不同的詞元素的為0的位置不同,其中n的大小是整個詞料中不同詞匯的總數。
以下是一個使用Python實現獨熱編碼的示例:給定一句話“在nlp任務中,首先需要考慮詞如何在計算機中表示”,首先分詞,然后進行one-hot 編碼。
import numpy as np
import jieba
# 定義一個函數來進行獨熱編碼
def one_hot_encode(word, vocab):
# 創建一個全零的向量
one_hot = np.zeros(len(vocab))
# 獲取單詞在詞匯表中的位置
index = vocab.index(word)
# 將對應位置設為1
one_hot[index] = 1
return one_hot
if __name__ == '__main__':
sent = '在nlp任務中,首先需要考慮詞如何在計算機中表示'
vocab = jieba.lcut(sent)
print('分詞結果', '/ '.join(vocab))
for word in vocab:
one_hot = one_hot_encode(word, vocab)
print(one_hot)
輸出:
分詞結果 在/ nlp/ 任務/ 中/ ,/ 首先/ 需要/ 考慮/ 詞/ 如何/ 在/ 計算機/ 中/ 表示
[1. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.]
[0. 1. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.]
[0. 0. 1. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.]
[0. 0. 0. 1. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.]
[0. 0. 0. 0. 1. 0. 0. 0. 0. 0. 0. 0. 0. 0.]
[0. 0. 0. 0. 0. 1. 0. 0. 0. 0. 0. 0. 0. 0.]
[0. 0. 0. 0. 0. 0. 1. 0. 0. 0. 0. 0. 0. 0.]
[0. 0. 0. 0. 0. 0. 0. 1. 0. 0. 0. 0. 0. 0.]
[0. 0. 0. 0. 0. 0. 0. 0. 1. 0. 0. 0. 0. 0.]
[0. 0. 0. 0. 0. 0. 0. 0. 0. 1. 0. 0. 0. 0.]
[1. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.]
[0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 1. 0. 0.]
[0. 0. 0. 1. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.]
[0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 1.]
可以看到,獨熱編碼將每一個詞轉換為了一個長度為12的向量,向量中只有對應位置的元素為1,其他元素均為0,這個位置表示單詞在詞匯表中的位置。
在舉一個例子,假設我們有四個樣本(行),每個樣本有三個特征(列),如圖:
特征1,代表性別,取值有2個,分別是【男,女】,男用1表示,女用2表示,特征二代表愛好,取值有4個,分別是【籃球,足球,乒乓球,羽毛球】分別用數字1,2,3,4表示,特征3是 愛好程度,取值有3個,分別是【不喜歡,喜歡,特別喜歡】,分別用數字1,2,3表示
將它換成獨熱編碼后,應該如下:
onehot編碼的優勢:
● 操作簡單,容易理解。
劣勢:
● 第一:維度災難,有多少個詞,我們的矩陣就要擴大多少維度就得是多少,造成每個向量的長度過大,占據大量內存。
● 第二:無法計算詞語之間的相似度
正因為one-hot編碼時明顯的劣勢,這種編碼的方式被應用的地方越來越少,取而代之的是word2vec和word embedding.
2.2 word2vec
word2vec是一種用于將詞語表示為連續向量的技術,它能夠將語義相似的詞語映射到向量空間中的相鄰位置。word2vec模型通常分為兩種:跳字模型(Skip-gram)和連續詞袋模型(Continuous Bag of Words,CBOW),其中Skip-gram模型更為常用。
Skip-gram模型的基本思想是通過給定中心詞來預測周圍的上下文詞;而CBOW模型則是通過給定周圍的上下文詞來預測中心詞。這兩種模型都會生成一個詞嵌入矩陣,其中每個詞都被表示為一個向量。
比如:在/ nlp/ 任務/ 中/ ,/ 首先/ 需要/ 考慮/ 詞/ 如何/ 在/ 計算機/ 中/ 表示
怎么確定哪些詞是上下文詞呢?需要指定一個窗口大小。圖中”首先“是中心詞,窗口大小是2,窗口范圍內是上下文詞,窗口范圍外的是非上下文詞,其中CBOW算法是通過上下文詞預測中心詞,skip-gram算法是通過中心詞預測上下文詞。
下面是一個使用Python和gensim庫進行word2vec模型訓練和使用的示例代碼:
import re
import numpy as np
import jieba
import gensim
from gensim.models import word2vec
from gensim.models import Word2Vec
import os
PATH = "./data/xi_you_ji.txt"
def read_crops():
file = open(PATH, encoding="utf-8")
lines = []
for line in file:
line_word_list = jieba.lcut(line)
current_word_list = []
for word in line_word_list:
# 定義正則表達式
pattern = re.compile('[^\w\s]')
# 過濾所有標點符號
text = pattern.sub('', word)
if text == "\n" or text == ' ':
continue
if len(text) > 0:
current_word_list.append(text)
if len(current_word_list) > 0:
lines.append(current_word_list)
print(lines[0:10])
return lines
def train(lines):
print("開始訓練模型")
model = Word2Vec(lines, vector_size=40, window=5, min_count=3, epochs=40, negative=10)
print("孫悟空的詞向量是:\n", model.wv.get_vector("悟空"))
print("孫悟空相關性最高的前10個詞語是:")
word_list = model.wv.most_similar("孫悟空", topn=10)
for item in word_list:
print(item)
# 保存model
model.save('word2vec_model') # 保存模型
def load_model():
model = None
if os.path.exists("word2vec_model"):
print("模型存在,直接加載模型")
model = word2vec.Word2Vec.load('word2vec_model') # 加載模型
else:
print("模型不存在")
return model
if __name__ == '__main__':
# 讀取語料
lines = read_crops()
model = load_model()
if model == None:
train(lines)
else:
word_list = model.wv.most_similar("孫悟空", topn=10)
for item in word_list:
print(item)
其中,/data/xi_you_ji.txt是從網絡上下載的西游記第一章內容。
輸出:
看到孫悟空相關詞的預測還是可以的,說明模型效果不錯。
2.13 詞嵌入(word embedding)
有四個單詞:“貓”、“狗”、“魚”、“跑”。首先,用數字表示這些單詞:- 貓:1- 狗:2- 魚:3 - 跑:4。如果我們只是用數字表示,那么計算機只能知道它們是不同的單詞,無法確定“貓”和“狗”更接近,還是“魚”和“跑”更接近,因為他們之間的數值都相差1。現在我們來用 word embedding 方法來表示這些單詞,詞向量可能是:
- 貓:[1, 0,0,0]
- 狗:[0, 1,0,0]
- 魚:[0, 0,1,0]
- 跑:[0, 0,0,1]
“貓”和“狗”這兩個向量非常接近,因為它們都屬于動物,“魚”和“跑”這兩個向量則相距很遠,因為它們不論屬性還是含義都相差很遠。接下來看看word2vec。所以詞嵌入(Word Embedding)是將詞語映射到一個高維向量空間的技術。在自然語言處理(NLP)任務中,詞嵌入可以將文本中的詞語轉化為計算機可以理解和處理的向量表示。
import torch
import jieba
from torch.utils.tensorboard import SummaryWriter
if __name__ == '__main__':
sent = '眾猴拍手稱揚道:“好水!好水!原來此處遠通山腳之下直接大海之波。”又道:“那一個有本事的鉆進去尋個源頭出來不傷身體者我等即拜他為王。”連呼了三聲忽見叢雜中跳出一名石猴應聲高叫道:“我進去!我進去!”好猴!也是他:'
seg_list = jieba.lcut(sent)
print(seg_list)
writer = SummaryWriter()
embedded = torch.randn(len(seg_list), 50)
writer.add_embedding(embedded, metadata=seg_list)
writer.close()
運行成功之后,在當前文件夾下會有runs目錄,然后啟動tensorboard。
游覽器訪問http://0.0.0.0:7777/#projector