在nlp的數據預處理中,我們通常需要根據原始數據集做出如題目所示的三種結構。但是新手(我自己)常常會感到混亂,因此特意整理一下
1.詞庫
詞庫是最先需要處理出的數據形式,即將原數據集按空格分詞或者使用分詞的包如jieba等,將原始文章分割成一個個詞語所表示的list,一般是一維或者二維的,二維詞庫往往是以行為第一維。
比如下面我們對ptb數據集進行處理產生對應的詞庫
with open('ptb/ptb.train.txt') as f:
raw_txt = f.read()
sentences = [line.split() for line in raw_txt.split('\n')]
或者在一些情況下,我們只需要統計出現過的詞匯,使用set結構體進行處理即可
wordSet = set([word for word in line for line in sentence])
2.詞典
在詞典中我們主要做的工作是,統計詞頻,按照頻率進行排序,排序主要是為了讓頻率高的詞有較小的編號,若某些單詞出現的次數低於某個界限值,如10,我們通常將它轉化為特殊詞元如'
#構建詞表
class Vocab:
def __init__(self, tokens=None, min_freq=0, reserved_tokens=None):
if tokens is None:
tokens = []
if reserved_tokens is None:
reserved_tokens = []
#將二維token轉化為一維列表
if(isinstance(tokens[0], list)):
tokens = [token for line in tokens for token in line]
counter = collections.Counter(tokens)
self._token_freqs = sorted(counter.items(), key=lambda x:x[1],reverse = True)#降序
self.idx_to_token = ['<unk>'] + reserved_tokens
#先對特定的token進行編號
self.token_to_idx = {token : idx for idx, token in enumerate(self.idx_to_token)}
#接着對詞語token進行編號
for token, freq in self._token_freqs:
if freq < min_freq:
break
if token not in self.token_to_idx:
self.idx_to_token.append(token)
self.token_to_idx[token] = len(self.idx_to_token) - 1
def __len__(self):
return len(self.idx_to_token)
def __getitem__(self, tokens):
#如果tokens不是列表或元組,就直接查詢token,若不存在返回unk,此函數可以直接數組形態訪問獲得id
if not isinstance(tokens, (list, tuple)):
return self.token_to_idx.get(tokens, self.unk)
return [self.__getitem__(token) for token in tokens]
#裝飾器,使得可以不帶括號的訪問函數
@property
def unk(self):
return 0
@property
def token_freqs(self):
return self._token_freqs
3.語料庫corpus
在詞典生成完成后,我們就可以將詞庫中的一個個單詞轉化為對應的標號,比如原文是['I','LIKE', 'YOU']就可以轉化為19, 90, 127,我們使用語料庫來進行訓練。值得注意的是,在生成語料庫之前,由於某些高頻詞或者停用詞的存在(停用詞通常也是高頻詞),提前使用停用詞表(github上有資源)將停用詞刪掉可以有效幫助訓練,因為停用詞沒有意義;或者使用下采樣方法,將高頻率詞按一定概率刪除,頻率越高,則被刪除概率越高,通常使用如下的概率公式:
即單詞\(w_i\)被刪除的概率。其中t是一個常數,實驗取1e-4,\(f(w_i)\)是該單詞詞頻,詞頻大於t時,才有可能被刪除。代碼:
# 如果在下采樣期間保留詞元,則返回True
def keep(token):
return(random.uniform(0, 1) <
math.sqrt(1e-4 / counter[token] * num_tokens))
最后正式轉化語料庫就很簡單了
corpus = [vocab[line] for line in sentences]
還是需要通過多次的實踐編寫代碼才能熟練。
代碼都是參考李沐老師的DIVE INTO DEEP LEARNING.