torchtext的簡單教程

torchtext的使用

目錄

1.引言

  這兩天看了一些torchtext的東西, 其實torchtext的教程並不是很多,當時想着使用torchtext的原因就是, 其中提供了一個BucketIterator的桶排序迭代器,通過這個輸出的批數據中,每批文本長度基本都是一致的,當時就感覺這個似乎可以提升模型的性能,畢竟每次訓練的數據的長度都差不多,不會像以前一樣像狗牙一樣參差不齊,看着揪心了。

  但是實際使用起來, 其實發現torchtext並不是那麼好用,而且實際實驗結果表明,隨機抽取文本和按文本長度排序之後再去抽取文本,模型的性能似乎都是一樣的,我在CNN和LSTM上面都做了實驗,發現沒啥提升。至於爲啥沒用預訓練模型做實驗,主要是發現torchtext的限制太多了,而預訓練模型都有自己的tokenizer方式,靈活性比較高,導致這個模塊使用起來特別的彆扭。最主要的還是因爲torchtext封裝的太厲害了,而它的官方文檔說實話,寫的也不是特別清楚,有些地方用的就有點糊里糊塗,還得靠做實驗來看看到底是啥情況,有些操作感覺還是沒有自己動手寫感覺踏實。

參考鏈接
CSDN上面的一個torchtext的簡單介紹

Kaggle上面的一個torchtext+LSTM的示例

github上面關於torchtext+HuggingFace 的使用討論

本文代碼的github地址

2.torchtext簡介

  因爲也沒研究的特別深,所以這裏介紹的就是平時用的一些方法,而torchtext本身是有很多其他的用途的,例如它裏面提供了很多nlp方面的數據集,可以直接加載使用,也提供了不少訓練好的詞向量之類的,這一點和torchvisio是一樣的(但是限於國內的一些網絡,這些功能一般好像都是處於荒廢的狀態)。

  一般我們常用的torchtext主要是3大部分,分別是FieldDatasetIteration三大部分。其中Dataset是對數據進行一些處理操作等,這點和torchvisio還是比較像的,但是這裏的Dataset其實能做的操作並不是很多,因爲它的很多任務都被Field所承擔了;至於Iteration,這個和torchvisio模塊中的DataLoader很類似,但是Iteration提供了很多NLP裏面需要的功能,例如對每個batch的數據進行batch內排序,設置排序的關鍵字等。

3.代碼講解

  這裏使用一個基於LSTM的情感分析模型進行講解torchtext的簡單使用

3.1 Field

  一般來說,第一步是首先設定好Field,Field是對數據格式的一種定義,可以看到官方提供的Field參數如下所示:

~Field.sequential – 輸入的數據是否是序列型的,如果不是,將不使用tokenzie對數據進行處理

~Field.use_vocab – 是否使用Vocab對象,也就是使用輸入的詞向量,這裏以後會講到,如果不使用,那麼輸入Field的對象一定是數字類型的。

~Field.init_token – 給example數據的開頭加一個token,感覺類似<CLS>標籤,example之後會將

~Field.eos_token – 給example數據加一個結束token

~Field.fix_length – 設定序列的長度,不夠的進行填充

~Field.dtype – 表示輸入的example數據的類型

~Field.preprocessing – 將example數據在tokenize之後,但在轉換成數值之前的管道設置,這個我沒有用過,所以不確定具體怎麼用

~Field.postprocessing – 將example數據在轉換成數值之後,但在變成tensor數據之前的管道設置. 管道將每個batch的數據當成一個list進行處理

~Field.lower – 是否將輸入的文本變成小寫

~Field.tokenize – 設置一個tokenize分詞器給Field用,這裏也有內置的一些分詞器可以用

~Field.tokenizer_language – 分詞器tokenize的語言,這裏是針對SpaCy的

~Field.include_lengths – 是否在返回文本序列的時候返回文本的長度,這裏是對LSTM的變長輸入設置非常好用

~Field.batch_first – 輸出的數據的維度中batch的大小放到前面

~Field.pad_token – 用於填充文本的關鍵字,默認是<pad>

~Field.unk_token – 用於填充不在詞彙表中的關鍵字,默認是<unk>

~Field.pad_first – 是否將填充放到文本最前面

~Field.truncate_first – 是否從文本開始的地方將文本截斷

~Field.stop_words – 停止詞的設置

~Field.is_target – 沒看明白乾啥用的

  可以看到,Field的功能還是非常多的,畢竟這個是用來對輸入的文本進行一些數據的預處理,首先進行初始化Field,如下所示:

def tokenize(x): return jieba.lcut(x)
sentence_field = Field(sequential=True, tokenize=tokenize,
                        lower=False, batch_first=True, include_lengths=True)
label_field = Field(sequential=False, use_vocab=False)

  然後Field的代碼就這麼多。

3.2 Dataset

  這裏是Dataset的代碼介紹,這裏我們需要做的一般是繼承torchtext.data.Dataset類,然後重寫自己的Dataset,不過torchtext提供了一些內置的Dataset,如果處理的數據不是特別複雜,直接使用官方內置的一些Dataset可以滿足要求,那麼直接使用官方的就行了。不過一般都要自己定製一下吧,畢竟很多時候數據的輸入都要進行一些修改,官方的不一定能滿足要求。

  寫Dataset的時候,最主要的其實是一個Example和Field的結合,可以看下面的代碼:

from torchtext.data import Dataset, Example

class SentenceDataset(Dataset):

    def __init__(self, data_path, sentence_field, label_field):

        fields = [('sentence', sentence_field), ('label', label_field)]
        examples = []

        with open(data_path, 'r') as f_json:
            file_content = json.load(f_json)
            self.sentence_list = []
            self.label_list = []
            for data in file_content:
                self.sentence_list.append(data['sentence'])
                self.label_list.append(data['label'])
        
        for index in trange(len(self.sentence_list)):
            examples.append(Example.fromlist([self.sentence_list[index], self.label_list[index]], fields))

        super().__init__(examples, fields)

    @staticmethod
    def sort_key(input):
        return len(input.sentence)

  可以看到,這裏將輸入的文本,標籤和Field進行綁定,也就是告訴Field它要具體處理哪些東西,然後最後還要使用super().__init__(examples, fields)來調用一下父類的初始化方法。這裏還有一個def sort_key(input):方法,這個方法是幫助後面的Iteration進行數據排序用的關鍵字,其實在Iteration中可以直接設置用於排序的關鍵字,但是因爲在前面的Field裏面使用了include_lengths關鍵字,好像導致後面的 Iteration直接指定關鍵字無法進行正常的排序,然後在Dataset裏面直接指定關鍵字,Iteration就可以直接進行正常的排序。這裏不排除是我代碼寫的有問題,但是使用上面代碼的那種方法可以正常排序是經過實驗驗證的。

  對於上面的example和Field進行綁定的時候,因爲我這裏使用的訓練數據和測試數據都是有標籤的,所以標籤那個位置直接就寫上了,但是一般測試數據都是沒有標籤,如果是沒有標籤的,將上面的代碼改成下面這樣就行了:

examples.append(Example.fromlist([self.sentence_list[index], None], fields)) # 沒有標籤就使用None來代替

  當然這裏的Dataset還有其他的一些功能,例如split方法等,這些大家可以去看官方的API文檔。

3.4 使用Field構建詞向量表

  在使用LSTM等一些網絡的時候,我們喜歡使用詞向量對網絡的Embedding層進行初始化,而Field中的build_vocab提供了這些處理操作。首先我們需要將詞向量讀取進來,在一個txt文本中保存如下格式的詞向量:

公司 0.3919137716293335 0.4011327922344208 ...
公園 0.17394110560417175 0.10003302991390228 ...
公佈 0.24726712703704834 0.06743448227643967 ...
公正 0.1161544919013977 0.07093961536884308 ...
公道 0.44119203090667725 0.21420961618423462 ...

  首先需要注意的是,詞向量表中只包含詞語的詞向量,不包含等關鍵字的詞向量,這部分的詞向量可以在build_vocab進行一定的設置,首先看build_vocab的參數(實際是torchtext.vocab.Vocab的參數,但是這裏是經過build_vocab處理之後將參數傳入到torchtext.vocab.Vocab):

counter – 這裏用來計算輸入的數據的頻率的,其實沒太看明白英文翻譯,不過這裏對應build_vocab輸入的是Dataset,經過build_vocab處理之後傳遞給torchtext.vocab.Vocab

max_size – 詞向量表的最大大小

min_freq – 參與轉換成詞向量的最小詞頻率,不滿足這個詞頻率的直接就是<unk>了

specials – 需要添加到詞向量表中的一些特殊字符,默認的包含['<unk'>,'<pad>']兩種,也是因爲這個參數,所以我們的txt文件中的詞向量不需要包含這兩個特殊字符。

vectors – 用於加載的預訓練好的詞向量

unk_init (callback) – 用於初始化未知詞彙的詞向量,默認是0

vectors_cache – 存放緩存的目錄,這個不一定在這裏設定,在Vectors類中設置也行

specials_first – 改變特徵字符在詞彙表中的位置,是放在最前面還是放在最後面

  不過在build_vocab詞向量之前,我們需要先將詞向量加載進來,這裏的操作就是使用torchtext.vocab.Vectors,主要包含以下參數:

name – 這裏實際應該是保存詞向量文件的位置

cache – 用於存放緩存的目錄

url – url for download if vectors not found in cache

unk_init (callback) – 初始化未知詞的詞向量

max_vectors (int) – 用於設置詞向量的大小,API文檔中說,一般保存詞向量的文件中,是按照詞向量的頻率大小從上大小進行排序,然後存儲到文件中,所以放棄一些低頻率的詞向量,對性能可能沒影響,但是還可以節省內存

  上面的參數介紹完了,就可以來看代碼了,代碼其實並不怎麼複雜:

cache = 'data/vector_cache'
if not os.path.exists(cache):
    os.mkdir(cache)
vectors = Vectors(name=vector_path, cache=cache)
sentence_field.build_vocab(train_dataset, min_freq=min_freq, vectors=vectors)

3.3 Iteration

  最後是Iteration方面的介紹,這部分官方提供了三個Iteration,當然也可以自定義,但是目前看官方提供的Iteration就可以滿足大部分情況,所以這裏就沒有進行自定義的Iteration。

train_iterator = BucketIterator(train_dataset, batch_size=batch_size,
                                device='cuda',
                                sort_within_batch=True, shuffle=True)
test_iterator = Iterator(test_dataset, batch_size=batch_size,
                        device='cuda', train=False,
                        shuffle=False, sort=False, sort_within_batch=True)

  這裏使用了BucketIteratorIterator,因爲BucketIterator可以自動的選擇長度類似的文本組成一個batch,所以用於訓練數據,而測試數據一般而言不想進行排序或者其他的操作,就使用了Iterator,這裏就不對Iterator的一些參數進行介紹了,一些重要的常用的基本就是上面列出來的那些了。

4. 總結

  torchtext模塊對於傳統的一些模型,例如CNN,LSTM等,使用起來還是比較方便的,特別是一些常用的操作,一些常用的數據集等等,torchtext都是包含的,但是對於目前的預訓練模型,大家可以去網上找一下資料,比如github上面關於torchtext+HuggingFace 的使用討論,其實受限於torchtext本身的一些規則太多,導致很多操作都被隱層了起來,一些想要自定義的功能卻不怎麼方便去自定義,所以感覺對於預訓練模型,或者一些其他的,例如多模態方面的模型,使用起來並不怎麼方便,估計也是因爲這個原因,導致一些相關教程和討論比較少。然後我基於CNN和LSTM,寫了一個torchtext的代碼,所以這裏貼出來github地址本文代碼的github地址

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章