FastText情感分析和詞向量訓練實戰(zhàn)——Keras算法練習(1)

FastText是facebook開源的一個詞向量與文本分類工具 ,其最大的優(yōu)點就是快崭孤,同時不失精度售睹。 此算法有兩個主要應用場景:

  • 文本分類
  • 詞向量訓練

工業(yè)界碰到一些簡單分類問題時腿短,經(jīng)常采用這種簡單鸠补,快速的模型解決問題。

FastText原理簡介

FastText原理部分有3個突出的特點:

  • 模型簡單藐翎,其結構有點類似word2vector中的CBOW架構材蹬,如下圖所示。FastText將句子特征通過一層全連接層映射到向量空間后吝镣,直接將詞向量平均處理一下堤器,就去做預測。
    模型架構
  • 使用了n-gram的特征末贾,使得句子的表達更充分吼旧。筆者會在實戰(zhàn)中詳細介紹這部分的操作。
  • 使用 Huffman算法建立用于表征類別的樹形結構未舟。這部分可以加速運算,同時減緩一些樣本不均衡的問題掂为。

其中比較有意思的是裕膀,做完分類任務后,模型全連接層的權重可以用來做詞向量勇哗。而且由于使用了n-gram的特征昼扛,fasttext的詞向量可以很好的緩解Out of Vocabulary的問題。接下來筆者就用keras構建一個fasttext模型做一下情感分析的任務欲诺,同時拿出它的詞向量看一看抄谐。

FastText情感分析實戰(zhàn)

import numpy as np
np.random.seed(1335)  # for reproducibility
from keras.preprocessing import sequence
from keras.models import Sequential
from keras.layers import Dense
from keras.layers import Embedding
from keras.layers import GlobalAveragePooling1D

這里定義了兩個函數(shù)用于n-gram特征增廣,這里筆者是直接將這篇參考文章的代碼拷貝過來扰法,作者的注釋極其詳細蛹含。這里需要講解一下n-gram特征的含義:
如果原句是:今天的雪下個不停。

  • unigram(1-gram)的特征:["今天"塞颁,"的"浦箱,"雪"吸耿,"下","個"酷窥,"不停"]
  • bigram(2-gram) 的特征: ["今天的"咽安,"的雪","雪下"蓬推,"下個"妆棒,"個不停"]

所以大家發(fā)現(xiàn)沒,n-gram的意思將句子中連續(xù)的n個詞連起來組成一個單獨的詞沸伏。
如果使用unigram和bigram的特征糕珊,句子特征就會變成:
["今天","的"馋评,"雪"放接,"下","個"留特,"不停"纠脾,"今天的","的雪"蜕青,"雪下"苟蹈,"下個","個不停"]這么一長串右核。
這樣做可以豐富句子的特征慧脱,能夠更好的表示句子的語義。

def create_ngram_set(input_list, ngram_value=2):
    """
    Extract a set of n-grams from a list of integers.
    從一個整數(shù)列表中提取  n-gram 集合贺喝。
    >>> create_ngram_set([1, 4, 9, 4, 1, 4], ngram_value=2)
    {(4, 9), (4, 1), (1, 4), (9, 4)}
    >>> create_ngram_set([1, 4, 9, 4, 1, 4], ngram_value=3)
    [(1, 4, 9), (4, 9, 4), (9, 4, 1), (4, 1, 4)]
    """
    return set(zip(*[input_list[i:] for i in range(ngram_value)]))


def add_ngram(sequences, token_indice, ngram_range=2):
    """
    Augment the input list of list (sequences) by appending n-grams values.
    增廣輸入列表中的每個序列菱鸥,添加 n-gram 值
    Example: adding bi-gram
    >>> sequences = [[1, 3, 4, 5], [1, 3, 7, 9, 2]]
    >>> token_indice = {(1, 3): 1337, (9, 2): 42, (4, 5): 2017}
    >>> add_ngram(sequences, token_indice, ngram_range=2)
    [[1, 3, 4, 5, 1337, 2017], [1, 3, 7, 9, 2, 1337, 42]]
    Example: adding tri-gram
    >>> sequences = [[1, 3, 4, 5], [1, 3, 7, 9, 2]]
    >>> token_indice = {(1, 3): 1337, (9, 2): 42, (4, 5): 2017, (7, 9, 2): 2018}
    >>> add_ngram(sequences, token_indice, ngram_range=3)
    [[1, 3, 4, 5, 1337], [1, 3, 7, 9, 2, 1337, 2018]]
    """
    new_sequences = []
    for input_list in sequences:
        new_list = input_list[:]
        for i in range(len(new_list) - ngram_range + 1):
            for ngram_value in range(2, ngram_range + 1):
                ngram = tuple(new_list[i:i + ngram_value])
                if ngram in token_indice:
                    new_list.append(token_indice[ngram])
        new_sequences.append(new_list)

    return new_sequences

數(shù)據(jù)載入

筆者在之前的情感分析文章中介紹了這個數(shù)據(jù)集的數(shù)據(jù)格式,想詳細了解的同學可以去這篇文章查看數(shù)據(jù)詳情躏鱼。

def read_data(data_path):
    senlist = []
    labellist = []  
    with open(data_path, "r",encoding='gb2312',errors='ignore') as f:
         for data in  f.readlines():
                data = data.strip()
                sen = data.split("\t")[2] 
                label = data.split("\t")[3]
                if sen != "" and (label =="0" or label=="1" or label=="2" ) :
                    senlist.append(sen)
                    labellist.append(label) 
                else:
                    pass                    
    assert(len(senlist) == len(labellist))            
    return senlist ,labellist 

sentences,labels = read_data("data_train.csv")
char_set = set(word for sen in sentences for word in sen)
char_dic = {j:i+1 for i,j in enumerate(char_set)}
char_dic["unk"] = 0

n-gram特征增廣

這里筆者只使用了unigram和bigram的特征氮采,如果使用trigram的特征,特征數(shù)以及計算量將會猛增染苛,所以沒有好的硬件不要輕易嘗試3鹊漠,4-gram以上的特征。

max_features = len(char_dic)
sentences2id = [[char_dic.get(word) for word in sen] for sen in sentences]
ngram_range = 2
if ngram_range > 1:
    print('Adding {}-gram features'.format(ngram_range))
    # Create set of unique n-gram from the training set.
    ngram_set = set()
    for input_list in sentences2id:
        for i in range(2, ngram_range + 1):
            set_of_ngram = create_ngram_set(input_list, ngram_value=i)
            ngram_set.update(set_of_ngram)
    # Dictionary mapping n-gram token to a unique integer. 將 ngram token 映射到獨立整數(shù)的詞典
    # Integer values are greater than max_features in order
    # to avoid collision with existing features.
    # 整數(shù)大小比 max_features 要大茶行,按順序排列躯概,以避免與已存在的特征沖突
    start_index = max_features 
    token_indice = {v: k + start_index for k, v in enumerate(ngram_set)}
    
fea_dict = {**token_indice,**char_dic}
# 使用 n-gram 特征增廣 X_train 
sentences2id= add_ngram(sentences2id,fea_dict, ngram_range)

print('Average train sequence length: {}'.format(
        np.mean(list(map(len, sentences2id)), dtype=int)))

數(shù)據(jù)預處理

將句子特征padding成300維的向量,同時對label進行onehot編碼畔师。

import numpy as np
from keras.utils import np_utils
print('Pad sequences (samples x time)')
X_train = sequence.pad_sequences(sentences2id, maxlen=300)
labels = np_utils.to_categorical(labels)

定義模型

這里我們我們可以看到fasttext的一些影子了:

  • 使用了一個簡單的Embedding層(其實本質(zhì)上就是一個Dense層)娶靡,
  • 然后接一個GlobalAveragePooling1D層對句子中每個詞的輸出向量求平均得到句子向量,
  • 之后句子向量通過全連接層后看锉,得到的輸出和label計算損失值固蛾。

此模型的最后一部沒有嚴格的遵循fasttext结执。

print('Build model...')
model = Sequential()
#我們從一個有效的嵌入層(embedding layer)開始,它將我們的詞匯索引(vocab indices )映射到詞向量的維度上.
model.add(Embedding(len(fea_dict),
                    200,
                    input_length=300))
# 我們增加 GlobalAveragePooling1D, 這將平均計算文檔中所有詞匯的的詞嵌入
model.add(GlobalAveragePooling1D())
#我們投射到單個單位的輸出層上
model.add(Dense(3, activation='softmax'))
model.compile(loss='categorical_crossentropy',
              optimizer="adam",
              metrics=['accuracy'])
model.summary()

這下面是模型結構的的可視化輸出艾凯,我們可以看到献幔,只用了unigram和bigram的特征詞典的維度已經(jīng)到了5千多萬,如果用到trigram了特征趾诗,特征詞典的維度肯定過億蜡感。


train

模型訓練

從訓練速度上來看,2分多鐘一個epoch恃泪,同樣的數(shù)據(jù)郑兴,比之前筆者使用的BiLSTM的速度快了不少。

train

訓練副產(chǎn)物——詞向量

embedding_layer = model.get_layer("embedding_1")
emb_wight = embedding_layer.get_weights()[0]

我們可以通過上方兩行代碼就拿到fasttext的訓練副產(chǎn)物——詞向量贝乎。
其中Embedding層的weight的形式和下圖中間的 W矩陣一樣情连,每行對應著一個詞的詞向量。通過簡單的index索引就可以得到訓練好的詞向量览效。


embedding

下面是筆者索引"媽媽"這個詞的詞向量的代碼却舀。

def word2fea(word,char_dic):
    wordtuple = tuple(char_dic.get(i) for i in word)
    return wordtuple
mather = word2fea("媽媽",char_dic)
index = fea_dict.get(mather)
mama = emb_wight[index]

打印出來如下圖所示,"媽媽"被映射成了一個200維的詞向量锤灿。


vector of word

結語

fasttext一個如此簡單的模型卻極其好用挽拔,這也是工業(yè)界特別喜歡它的原因。所以在面對問題的時候不要一上來就構建一個特別復雜的模型但校,有時候簡單的模型也能很好解決的問題螃诅,一定要記住大道至簡。
參考:
https://kexue.fm/archives/4122
http://www.voidcn.com/article/p-alhbnusv-bon.html
https://github.com/facebookresearch/fastText

最后編輯于
?著作權歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末状囱,一起剝皮案震驚了整個濱河市术裸,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌亭枷,老刑警劉巖穗椅,帶你破解...
    沈念sama閱讀 211,561評論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異奶栖,居然都是意外死亡,警方通過查閱死者的電腦和手機门坷,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,218評論 3 385
  • 文/潘曉璐 我一進店門宣鄙,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人默蚌,你說我怎么就攤上這事冻晤。” “怎么了绸吸?”我有些...
    開封第一講書人閱讀 157,162評論 0 348
  • 文/不壞的土叔 我叫張陵鼻弧,是天一觀的道長设江。 經(jīng)常有香客問我,道長攘轩,這世上最難降的妖魔是什么叉存? 我笑而不...
    開封第一講書人閱讀 56,470評論 1 283
  • 正文 為了忘掉前任,我火速辦了婚禮度帮,結果婚禮上歼捏,老公的妹妹穿的比我還像新娘。我一直安慰自己笨篷,他們只是感情好瞳秽,可當我...
    茶點故事閱讀 65,550評論 6 385
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著率翅,像睡著了一般练俐。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上冕臭,一...
    開封第一講書人閱讀 49,806評論 1 290
  • 那天腺晾,我揣著相機與錄音,去河邊找鬼浴韭。 笑死丘喻,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的念颈。 我是一名探鬼主播泉粉,決...
    沈念sama閱讀 38,951評論 3 407
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼榴芳!你這毒婦竟也來了嗡靡?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 37,712評論 0 266
  • 序言:老撾萬榮一對情侶失蹤窟感,失蹤者是張志新(化名)和其女友劉穎讨彼,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體柿祈,經(jīng)...
    沈念sama閱讀 44,166評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡哈误,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,510評論 2 327
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了躏嚎。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片蜜自。...
    茶點故事閱讀 38,643評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖卢佣,靈堂內(nèi)的尸體忽然破棺而出重荠,到底是詐尸還是另有隱情,我是刑警寧澤虚茶,帶...
    沈念sama閱讀 34,306評論 4 330
  • 正文 年R本政府宣布戈鲁,位于F島的核電站仇参,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏婆殿。R本人自食惡果不足惜诈乒,卻給世界環(huán)境...
    茶點故事閱讀 39,930評論 3 313
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望鸣皂。 院中可真熱鬧抓谴,春花似錦、人聲如沸寞缝。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,745評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽荆陆。三九已至滩届,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間被啼,已是汗流浹背帜消。 一陣腳步聲響...
    開封第一講書人閱讀 31,983評論 1 266
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留浓体,地道東北人泡挺。 一個月前我還...
    沈念sama閱讀 46,351評論 2 360
  • 正文 我出身青樓,卻偏偏與公主長得像命浴,于是被迫代替她去往敵國和親娄猫。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 43,509評論 2 348

推薦閱讀更多精彩內(nèi)容