<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          【NLP】基于深度學(xué)習(xí)的文本分類應(yīng)用

          共 10923字,需瀏覽 22分鐘

           ·

          2020-08-14 18:36

          作者:羅美君,算法工程師,Datawhale優(yōu)秀學(xué)習(xí)者

          基于機器學(xué)習(xí)的文本分類中,我們介紹了幾種常見的文本表示方法:One-hot、Bags of Words、N-gram、TF-IDF。這些方法存在兩個共同的問題:一是轉(zhuǎn)換得到的向量維度很高,需要較長的訓(xùn)練實踐;二是沒有考慮到單詞與單詞之間的關(guān)系,只是進行了統(tǒng)計。
          與上述表示方法不同,深度學(xué)習(xí)也可以用于文本表示,并可以將其映射到一個低維空間。fastText是Facebook2016年提出的文本分類工具,是一種高效的淺層網(wǎng)絡(luò)。今天我們就嘗試使用fastText模型進行文本分類。

          1. 數(shù)據(jù)及背景

          https://tianchi.aliyun.com/competition/entrance/531810/information(阿里天池-零基礎(chǔ)入門NLP賽事)

          2. fastText模型剖析

          2.1 概念

          FastText是一種典型的深度學(xué)習(xí)詞向量的表示方法,它的核心思想是將整篇文檔的詞及n-gram向量疊加平均得到文檔向量,然后使用文檔向量做softmax多分類。這中間涉及到兩個技巧:字符級n-gram特征的引入以及分層Softmax分類。

          2.2 模型框架

          fastText模型架構(gòu)和word2vec的CBOW模型架構(gòu)非常相似。下面是fastText模型架構(gòu)圖:

          注意:此架構(gòu)圖沒有展示詞向量的訓(xùn)練過程。可以看到,和CBOW一樣,fastText模型也只有三層:輸入層、隱含層、輸出層(Hierarchical Softmax),輸入都是多個經(jīng)向量表示的單詞,輸出都是一個特定的target,隱含層都是對多個詞向量的疊加平均。

          不同的是,CBOW的輸入是目標(biāo)單詞的上下文,fastText的輸入是多個單詞及其n-gram特征,這些特征用來表示單個文檔;CBOW的輸入單詞被onehot編碼過,fastText的輸入特征是被embedding過;CBOW的輸出是目標(biāo)詞匯,fastText的輸出是文檔對應(yīng)的類標(biāo)。

          值得注意的是,fastText在輸入時,將單詞的字符級別的n-gram向量作為額外的特征;在輸出時,fastText采用了分層Softmax,大大降低了模型訓(xùn)練時間。

          2.3 字符級別的n-gram

          word2vec把語料庫中的每個單詞當(dāng)成原子的,它會為每個單詞生成一個向量。這忽略了單詞內(nèi)部的形態(tài)特征,比如:"apple" 和"apples","達(dá)觀數(shù)據(jù)"和"達(dá)觀",這兩個例子中,兩個單詞都有較多公共字符,即它們的內(nèi)部形態(tài)類似,但是在傳統(tǒng)的word2vec中,這種單詞內(nèi)部形態(tài)信息因為它們被轉(zhuǎn)換成不同的id丟失了。

          為了克服這個問題,fastText使用了字符級別的n-grams來表示一個單詞。對于單詞"apple",假設(shè)n的取值為3,則它的trigram有:

          ""

          其中,<表示前綴,>表示后綴。于是,我們可以用這些trigram來表示"apple"這個單詞,進一步,我們可以用這5個trigram的向量疊加來表示"apple"的詞向量。

          這帶來兩點好處:

          • 對于低頻詞生成的詞向量效果會更好。因為它們的n-gram可以和其它詞共享。

          • 對于訓(xùn)練詞庫之外的單詞,仍然可以構(gòu)建它們的詞向量。我們可以疊加它們的字符級n-gram向量。

          2.4 分層softmax

          fastText的結(jié)構(gòu):
          1. 文本分詞后排成列做輸入。
          2. lookup table變成想要的隱層維數(shù)。
          3. 隱層后接huffman Tree。這個tree就是分層softmax減少計算量的精髓。

          3. 簡單實現(xiàn)fastText

          為了簡化任務(wù):
          1. 訓(xùn)練詞向量時,我們使用正常的word2vec方法,而真實的fastText還附加了字符級別的n-gram作為特征輸入;
          2. 我們的輸出層使用簡單的softmax分類,而真實的fastText使用的是Hierarchical Softmax。

          首先定義幾個常量:

          • ????VOCAB_SIZE = 2000

          • ????EMBEDDING_DIM =100

          • ????MAX_WORDS = 500

          • ????CLASS_NUM = 5

          • ??? VOCAB_SIZE表示詞匯表大小,這里簡單設(shè)置為2000;

          EMBEDDING_DIM表示經(jīng)過embedding層輸出,每個詞被分布式表示的向量的維度,這里設(shè)置為100。比如對于“達(dá)觀”這個詞,會被一個長度為100的類似于[ 0.97860014, 5.93589592, 0.22342691, -3.83102846, -0.23053935, …]的實值向量來表示;

          MAX_WORDS表示一篇文檔最多使用的詞個數(shù),因為文檔可能長短不一(即詞數(shù)不同),為了能feed到一個固定維度的神經(jīng)網(wǎng)絡(luò),我們需要設(shè)置一個最大詞數(shù),對于詞數(shù)少于這個閾值的文檔,我們需要用“未知詞”去填充。比如可以設(shè)置詞匯表中索引為0的詞為“未知詞”,用0去填充少于閾值的部分;

          CLASS_NUM表示類別數(shù),多分類問題,這里簡單設(shè)置為5。

          模型搭建遵循以下步驟:

          1. 添加輸入層(embedding層)。Embedding層的輸入是一批文檔,每個文檔由一個詞匯索引序列構(gòu)成。例如:[10, 30, 80, 1000] 可能表示“我 昨天 來到 達(dá)觀數(shù)據(jù)”這個短文本,其中“我”、“昨天”、“來到”、“達(dá)觀數(shù)據(jù)”在詞匯表中的索引分別是10、30、80、1000;Embedding層將每個單詞映射成EMBEDDING_DIM維的向量。于是:input_shape=(BATCH_SIZE, MAX_WORDS), output_shape=(BATCH_SIZE,MAX_WORDS, EMBEDDING_DIM);

          2. 添加隱含層(投影層)。投影層對一個文檔中所有單詞的向量進行疊加平均。keras提供的GlobalAveragePooling1D類可以幫我們實現(xiàn)這個功能。這層的input_shape是Embedding層的output_shape,這層的output_shape=( BATCH_SIZE, EMBEDDING_DIM);

          3. 添加輸出層(softmax層)。真實的fastText這層是Hierarchical Softmax,因為keras原生并沒有支持Hierarchical Softmax,所以這里用Softmax代替。這層指定了CLASS_NUM,對于一篇文檔,輸出層會產(chǎn)生CLASS_NUM個概率值,分別表示此文檔屬于當(dāng)前類的可能性。這層的output_shape=(BATCH_SIZE, CLASS_NUM)

          4. 指定損失函數(shù)、優(yōu)化器類型、評價指標(biāo),編譯模型。損失函數(shù)我們設(shè)置為categorical_crossentropy,它就是我們上面所說的softmax回歸的損失函數(shù);優(yōu)化器我們設(shè)置為SGD,表示隨機梯度下降優(yōu)化器;評價指標(biāo)選擇accuracy,表示精度。

          用訓(xùn)練數(shù)據(jù)feed模型時,你需要:

          1. 將文檔分好詞,構(gòu)建詞匯表。詞匯表中每個詞用一個整數(shù)(索引)來代替,并預(yù)留“未知詞”索引,假設(shè)為0;
          2. 對類標(biāo)進行onehot化。假設(shè)我們文本數(shù)據(jù)總共有3個類別,對應(yīng)的類標(biāo)分別是1、2、3,那么這三個類標(biāo)對應(yīng)的onehot向量分別是[1, 0,0]、[0, 1, 0]、[0, 0, 1];
          3. 對一批文本,將每個文本轉(zhuǎn)化為詞索引序列,每個類標(biāo)轉(zhuǎn)化為onehot向量。就像之前的例子,“我 昨天 來到 達(dá)觀數(shù)據(jù)”可能被轉(zhuǎn)化為[10, 30, 80, 1000];它屬于類別1,它的類標(biāo)就是[1, 0, 0]。由于我們設(shè)置了MAX_WORDS=500,這個短文本向量后面就需要補496個0,即[10, 30, 80, 1000, 0, 0, 0, …, 0]。因此,batch_xs的 維度為( BATCH_SIZE,MAX_WORDS),batch_ys的維度為(BATCH_SIZE, CLASS_NUM)。

          代碼如下:

          # coding: utf-8from __future__ import unicode_literals
          from keras.models import Sequentialfrom keras.layers import Embeddingfrom keras.layers import GlobalAveragePooling1Dfrom keras.layers import Dense
          VOCAB_SIZE = 2000EMBEDDING_DIM = 100MAX_WORDS = 500CLASS_NUM = 5

          def build_fastText(): model = Sequential() # 將詞匯數(shù)VOCAB_SIZE映射為EMBEDDING_DIM維 model.add(Embedding(VOCAB_SIZE, EMBEDDING_DIM, input_length=MAX_WORDS)) # 平均文檔中所有詞的embedding model.add(GlobalAveragePooling1D()) # softmax分類 model.add(Dense(CLASS_NUM, activation='softmax')) # 定義損失函數(shù)、優(yōu)化器、分類度量指標(biāo) model.compile(loss='categorical_crossentropy', optimizer='SGD', metrics=['accuracy']) return model
          if __name__ == '__main__': model = build_fastText() print(model.summary())


          4. 使用fastText文本分類

          4.1 加載庫

          import timeimport numpy as npimport fasttextimport pandas as pd
          from sklearn.metrics import f1_scorefrom sklearn.utils import shufflefrom sklearn.model_selection import StratifiedKFold

          4.2 fastText分類

          主要超參數(shù):
          • lr: 學(xué)習(xí)率

          • dim: 詞向量的維度

          • epoch: 每輪的個數(shù)

          • wordNgrams: 詞的n-gram,一般設(shè)置為2或3

          • loss: 損失函數(shù) ns(negative sampling, 負(fù)采樣)、hs(hierarchical softmax, 分層softmax)、softmax、ova(One-VS-ALL)

          def fasttext_model(nrows, train_num, lr=1.0, wordNgrams=2, minCount=1, epoch=25, loss='hs', dim=100):    start_time = time.time()
          # 轉(zhuǎn)換為FastText需要的格式 train_df = pd.read_csv('../input/train_set.csv', sep='\t', nrows=nrows)
          # shuffle train_df = shuffle(train_df, random_state=666)
          train_df['label_ft'] = '__label__' + train_df['label'].astype('str') train_df[['text', 'label_ft']].iloc[:train_num].to_csv('../input/fastText_train.csv', index=None, header=None, sep='\t')
          model = fasttext.train_supervised('../input/fastText_train.csv', lr=lr, wordNgrams=wordNgrams, verbose=2, minCount=minCount, epoch=epoch, loss=loss, dim=dim)
          train_pred = [model.predict(x)[0][0].split('__')[-1] for x in train_df.iloc[:train_num]['text']] print('Train f1_score:', f1_score(train_df['label'].values[:train_num].astype(str), train_pred, average='macro')) val_pred = [model.predict(x)[0][0].split('__')[-1] for x in train_df.iloc[train_num:]['text']] print('Val f1_score:', f1_score(train_df['label'].values[train_num:].astype(str), val_pred, average='macro')) train_time = time.time() print('Train time: {:.2f}s'.format(train_time - start_time))
          # 預(yù)測并保存 test_df = pd.read_csv('../input/test_a.csv')
          test_pred = [model.predict(x)[0][0].split('__')[-1] for x in test_df['text']] test_pred = pd.DataFrame(test_pred, columns=['label']) test_pred.to_csv('../input/test_fastText_ridgeclassifier.csv', index=False) print('Test predict saved.') end_time = time.time() print('Predict time:{:.2f}s'.format(end_time - train_time))

          if __name__ == '__main__': nrows = 200000 train_num = int(nrows * 0.7) lr=0.01 wordNgrams=2 minCount=1 epoch=25 loss='hs'
          fasttext_model(nrows, train_num)

          結(jié)果:

          Train f1_score: 0.998663548149514Val f1_score: 0.911468448971427Train time: 257.32sTest predict saved.Predict time:13.40s

          4.3 K折交叉驗證

          在使用FastText中,有一些模型的參數(shù)需要選擇,這些參數(shù)會在一定程度上影響模型的精度,那么如何選擇這些參數(shù)呢?有兩種方式:

          • 通過閱讀文檔,要弄清楚這些參數(shù)的含義,哪些參數(shù)會增加模型的復(fù)雜度;
          • 通過在驗證集上進行驗證模型精度,找到模型是否過擬合或欠擬合。

          這里我們采用第二種方法,用K折交叉驗證的思想進行參數(shù)調(diào)節(jié)。注意:每折的劃分必須保證標(biāo)簽的分布與整個數(shù)據(jù)集的分布一致。

          models = []scores = []pred_list = []
          # K折交叉驗證skf = StratifiedKFold(n_splits=n_splits, shuffle=True, random_state=666)for train_index, test_index in skf.split(train_df['text'], train_df['label_ft']):
          train_df[['text', 'label_ft']].iloc[train_index].to_csv('../input/fastText_train.csv', index=None, header=None, sep='\t')
          model = fasttext.train_supervised('../input/fastText_train.csv', lr=lr, wordNgrams=wordNgrams, verbose=2, minCount=minCount, epoch=epoch, loss=loss) models.append(model)
          val_pred = [model.predict(x)[0][0].split('__')[-1] for x in train_df.iloc[test_index]['text']] score = f1_score(train_df['label'].values[test_index].astype(str), val_pred, average='macro') print('score', score) scores.append(score)
          print('mean score: ', np.mean(scores))train_time = time.time()print('Train time: {:.2f}s'.format(train_time - start_time))

          所有代碼

          def fasttext_kfold_model(nrows, train_num, n_splits, lr=1.0, wordNgrams=2, minCount=1, epoch=25, loss='hs', dim=100):    start_time = time.time()
          # 轉(zhuǎn)換為FastText需要的格式 train_df = pd.read_csv('../input/train_set.csv', sep='\t', nrows=nrows)
          # shuffle train_df = shuffle(train_df, random_state=666)
          train_df['label_ft'] = '__label__' + train_df['label'].astype('str')
          models = [] train_scores = [] val_scores = []
          # K折交叉驗證 skf = StratifiedKFold(n_splits=n_splits, shuffle=True, random_state=666) for train_index, test_index in skf.split(train_df['text'], train_df['label_ft']): train_df[['text', 'label_ft']].iloc[train_index].to_csv('../input/fastText_train.csv', index=None, header=None, sep='\t')
          model = fasttext.train_supervised('../input/fastText_train.csv', lr=lr, wordNgrams=wordNgrams, verbose=2, minCount=minCount, epoch=epoch, loss=loss) models.append(model)
          train_pred = [model.predict(x)[0][0].split('__')[-1] for x in train_df.iloc[train_index]['text']] train_score = f1_score(train_df['label'].values[train_index].astype(str), train_pred, average='macro') # print('Train length: ', len(train_pred)) print('Train score: ', train_score) train_scores.append(train_score)
          val_pred = [model.predict(x)[0][0].split('__')[-1] for x in train_df.iloc[test_index]['text']] val_score = f1_score(train_df['label'].values[test_index].astype(str), val_pred, average='macro') # print('Val length: ', len(val_pred)) print('Val score', val_score) val_scores.append(val_score)
          print('mean train score: ', np.mean(train_scores)) print('mean val score: ', np.mean(val_scores)) train_time = time.time() print('Train time: {:.2f}s'.format(train_time - start_time))
          return models
          def fasttext_kfold_predict(models, n_splits):
          pred_list = []
          start_time = time.time() # 預(yù)測并保存 test_df = pd.read_csv('../input/test_a.csv')
          # 消耗時間較長 for model in models: test_pred = [model.predict(x)[0][0].split('__')[-1] for x in test_df['text']] pred_list.append(test_pred)
          test_pred_label = pd.DataFrame(pred_list).T.apply(lambda row: np.argmax(np.bincount([row[i] for i in range(n_splits)])), axis=1) test_pred_label.columns='label'
          test_pred_label.to_csv('../input/test_fastText_ridgeclassifier.csv', index=False) print('Test predict saved.') end_time = time.time() print('Predict time:{:.2f}s'.format(end_time - start_time))

          if __name__ == '__main__': nrows = 200000 train_num = int(nrows * 0.7) n_splits = 3 lr=0.1 wordNgrams=2 minCount=1 epoch=25 loss='hs' dim=200
          """ Train score: 0.9635013320936988 Val score 0.9086640111428032 Train score: 0.9623510782430645 Val score 0.9094998879044359 Train score: 0.9628121318772955 Val score 0.9096191534698315 mean train score: 0.9628881807380196 mean val score: 0.9092610175056901 Train time: 740.60s """
          models = fasttext_kfold_model(nrows, train_num, n_splits, lr=lr, wordNgrams=wordNgrams, minCount=minCount, epoch=epoch, loss=loss, dim=dim)????fasttext_kfold_predict(models,?n_splits=n_splits)

          K折交叉驗證能增加模型的穩(wěn)定性,尤其時間有限,驗證的結(jié)果僅達(dá)0.909,有時間的朋友可以調(diào)整超參數(shù),獲得更高的準(zhǔn)確率。



          往期精彩回顧





          獲取一折本站知識星球優(yōu)惠券,復(fù)制鏈接直接打開:

          https://t.zsxq.com/662nyZF

          本站qq群1003271085。

          加入微信群請掃碼進群(如果是博士或者準(zhǔn)備讀博士請說明):

          瀏覽 44
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  干美女视频 | 美女操BAV | 日韩极品毛片 | 最新亚洲无码在线视频 | 久要靠逼视频 |