fasttext算法原理及使用

1.  FastText原理

fastText是一種簡單高效的文本表徵方法,性能與深度學習比肩。fastText的核心思想就是:將整篇文檔的詞及n-gram向量疊加平均得到文檔向量,然後使用文檔向量做softmax多分類。這中間涉及到兩個技巧:字符級n-gram特徵的引入以及分層Softmax分類。主要功能在於:

  • 文本分類:有監督學習
  • 詞向量表徵:無監督學習

1.1 模型框架(Model architecture)

fastText的結構與word2vec的CBOW模型架構相似(fastText的開源工具不僅可以文本分類,還可以訓練詞向量,與word2vec相似)。word2vec有兩種模型:skip-gram 模型和CBOW模型。兩者的區別概括區別是:skip-gram,用當前詞來預測上下文;CBOW,用上下文來預測當前詞。

CBOW及fastText的模型框架對比如下: 

CBOW模型框架

  • 輸入層:由目標詞彙y的上下文單詞\left \{ x_{1} ,...,x_{c}\right \}組成,x_{i}是被onehot編碼過的V維向量,V是詞彙量大小;輸入層通過V\ast N維的權重矩陣W連接到隱含層;
  • 隱含層:N維向量h,隱含層通過N\ast V維的權重矩陣W^{'}連接到輸出層;
  • 輸出層:是被onehot編碼過的目標詞y(中間詞);爲提高計算效率,CBOW輸出層採用分層softmax;

fastText模型框架

  • 輸入層:embedding後多個單詞及其n-gram特徵,這些特徵用來表示單個文檔;
  • 隱含層:對多個詞向量的疊加平均
  • 輸出層:是文檔對應的類別標籤;採用分層softmax;

1.2 層次softmax(Hierarchical softmax)

1.2.1 標準softmax函數

softmax函數又稱爲歸一化指數函數,常在神經網絡輸出層充當激活函數,它是二分類函數sigmoid在多分類上的推廣,目的是將多分類的結果以概率的形式展現出來。下圖爲softma x的計算過程:

softmax的作用:

  • 將預測結果轉化爲非負數;
  • 各種預測結果概率之和等於1;

1.2.2 層次softmax函數

標準的softmax中,計算一個類別的softmax概率時,需要對所有的類別概率做歸一化,這在類別數量很大時會很耗時;分層softmax(Hierarchical Softmax)的目的就是提高計算效率,方法是構造霍夫曼樹來代替標準softmax,只需計算一條路徑上的所有節點的概率值,無需在意其它的節點。通過分層softmax可以將複雜度從N降低到logN

霍夫曼樹:

給定n個權值作爲n個葉子結點,構造一棵二叉樹,若帶權路徑長度達到最小,稱這樣的二叉樹爲最優二叉樹,也稱爲霍夫曼樹(Huffman Tree)。

如左圖所示兩棵二叉樹,葉子結點爲A、B、C、D,對應權值分別爲7、5、2、4。樹的帶權路徑長度規定爲所有葉子結點的帶權路徑長度之和,記爲WPL。

葉子結點爲A、B、C、D,對應權值分別爲7、5、2、4。

  • 左樹的WPL = 7 * 2 + 5 * 2 + 2 * 2 + 4 * 2 = 36
  • 右樹的WPL = 7 * 1 + 5 * 2 + 2 * 3 + 4 * 3 = 35

由ABCD構成葉子結點的二叉樹形態有許多種,但是WPL最小的樹只有右樹所示的形態。則右樹爲一棵霍夫曼樹。

下圖是一個層次softmax:

樹的結構是根據類別標記的頻數構造的霍夫曼樹。K個不同的類標組成所有的葉子節點,從根節點到某個葉子節點經過的節點和邊形成一條路徑,路徑長度爲L(y_{i})。需要計算目標詞y_{i}的概率,這個概率的具體含義,是指從根結點開始隨機走,走到目標詞y_{i}的概率,非葉子結點處需要分別知道往左走和往右走的概率。例如到達非葉子節點n的時候往左邊走和往右邊走的概率分別是:

p(n, left)=\sigma (\theta _{n} ^{T}\cdot X)

p(n, right)=1-\sigma (\theta _{n} ^{T}\cdot X) = \sigma (-\theta _{n} ^{T})

圖中標記的路徑是從根節點到葉子節點y_{2}的路徑,路徑長度L(y_{2})=4,節點y_{2}的概率可以表示爲:

P(y_{2}) \\&= P(n(y_{2},1),left)\cdotP(n(y_{2},2),left)\cdotP(n(y_{3},1),right) \\ &= \sigma (\theta _{n(y_{2},1)}^{T}X)\cdot \sigma (\theta _{n(y_{2},2)}^{T}X)\cdot \sigma (-\theta _{n(y_{2},3)}^{T}X)

從根節點走到葉子節點y_{2} ,實際上是在做了3次二分類的邏輯迴歸;通過分層的softmax,計算複雜度從|K|降低到log|K|

1.3 N-gram特徵(N-gram features)

原始文本是一個單詞序列,一般的詞袋錶示中沒有任何序列,它只記錄每個單詞在文本中出現的次數。因此 fastText 還加入了 N-gram 特徵,基本思想是將文本內容按照字節順序進行大小爲N的滑動窗口操作,最終形成長度爲N的字節片段序列。n-gram可以是字粒度,也可以是詞粒度的。n-gram產生的特徵只是作爲文本特徵的候選集,後面可能會採用信息熵、卡方統計、IDF等文本特徵選擇方式篩選出比較重要特徵。

bigram特徵示例:我來到北京旅遊

  • 字粒度我來 來到 到北 北京 京旅 旅遊
  • 詞粒度:我/來到 來到/北京 北京/旅遊

n-gram有如下優點

  • 保持詞序信息:n-gram可以讓模型學習到局部單詞順序的部分信息;
  • 處理低頻詞:字符級別的n-gram,即使這個單詞出現的次數很少,但是組成單詞的字符和其他單詞有共享的部分,可以優化生成的單詞向量;
  • 處理未出現過的詞:字符級n-gram,即使單詞沒有出現在訓練語料庫中,仍然可以從字符級n-gram中構造單詞的詞向量;

 

2.fastText文本分類實踐

fasttext官網:https://fasttext.cc/   
中文社區:http://fasttext.apachecn.org/#/doc/zh/support

fastText 支持的不同用例:

The commands supported by fasttext are:

  supervised              訓練一個監督分類器
  quantize                量化模型以減少內存使用量
  test                    評估一個監督分類器
  predict                 預測最有可能的標籤
  predict-prob            用概率預測最可能的標籤
  skipgram                訓練一個 skipgram 模型
  cbow                    訓練一個 cbow 模型
  print-word-vectors      給定一個訓練好的模型,打印出所有的單詞向量
  print-sentence-vectors  給定一個訓練好的模型,打印出所有的句子向量
  nn                      查詢最近鄰居
  analogies               查找所有同類詞

fasttext.supervised 參數如下

input_file                 訓練文件路徑(必須)
output                     輸出文件路徑(必須)
label_prefix               標籤前綴 default __label__
lr                         學習率 default 0.1
lr_update_rate             學習率更新速率 default 100
dim                        詞向量維度 default 100
ws                         上下文窗口大小 default 5
epoch                      epochs 數量 default 5
min_count                  最低詞頻 default 5
word_ngrams                n-gram 設置 default 1
loss                       損失函數 {ns,hs,softmax} default softmax
minn                       最小字符長度 default 0
maxn                       最大字符長度 default 0
thread                     線程數量 default 12
t                          採樣閾值 default 0.0001
silent                     禁用 c++ 擴展日誌輸出 default 1
encoding                   指定 input_file 編碼 default utf-8
pretrained_vectors         指定使用已有的詞向量 .vec 文件 default None

先貼出其他博客不錯的代碼,後面給出實例:

# -*- coding:utf-8 -*-
import pandas as pd
import random
import fasttext
import jieba
from sklearn.model_selection import train_test_split

cate_dic = {'technology': 1, 'car': 2, 'entertainment': 3, 'military': 4, 'sports': 5}
"""
函數說明:加載數據
"""
def loadData():


    #利用pandas把數據讀進來
    df_technology = pd.read_csv("./data/technology_news.csv",encoding ="utf-8")
    df_technology=df_technology.dropna()    #去空行處理

    df_car = pd.read_csv("./data/car_news.csv",encoding ="utf-8")
    df_car=df_car.dropna()

    df_entertainment = pd.read_csv("./data/entertainment_news.csv",encoding ="utf-8")
    df_entertainment=df_entertainment.dropna()

    df_military = pd.read_csv("./data/military_news.csv",encoding ="utf-8")
    df_military=df_military.dropna()

    df_sports = pd.read_csv("./data/sports_news.csv",encoding ="utf-8")
    df_sports=df_sports.dropna()

    technology=df_technology.content.values.tolist()[1000:21000]
    car=df_car.content.values.tolist()[1000:21000]
    entertainment=df_entertainment.content.values.tolist()[:20000]
    military=df_military.content.values.tolist()[:20000]
    sports=df_sports.content.values.tolist()[:20000]

    return technology,car,entertainment,military,sports

"""
函數說明:停用詞
參數說明:
    datapath:停用詞路徑
返回值:
    stopwords:停用詞
"""
def getStopWords(datapath):
    stopwords=pd.read_csv(datapath,index_col=False,quoting=3,sep="\t",names=['stopword'], encoding='utf-8')
    stopwords=stopwords["stopword"].values
    return stopwords

"""
函數說明:去停用詞
參數:
    content_line:文本數據
    sentences:存儲的數據
    category:文本類別
"""
def preprocess_text(content_line,sentences,category,stopwords):
    for line in content_line:
        try:
            segs=jieba.lcut(line)    #利用結巴分詞進行中文分詞
            segs=filter(lambda x:len(x)>1,segs)    #去掉長度小於1的詞
            segs=filter(lambda x:x not in stopwords,segs)    #去掉停用詞
            sentences.append("__lable__"+str(category)+" , "+" ".join(segs))    #把當前的文本和對應的類別拼接起來,組合成fasttext的文本格式
        except Exception as e:
            print (line)
            continue

"""
函數說明:把處理好的寫入到文件中,備用
參數說明:

"""
def writeData(sentences,fileName):
    print("writing data to fasttext format...")
    out=open(fileName,'w')
    for sentence in sentences:
        out.write(sentence.encode('utf8')+"\n")
    print("done!")

"""
函數說明:數據處理
"""
def preprocessData(stopwords,saveDataFile):
    technology,car,entertainment,military,sports=loadData()    

    #去停用詞,生成數據集
    sentences=[]
    preprocess_text(technology,sentences,cate_dic["technology"],stopwords)
    preprocess_text(car,sentences,cate_dic["car"],stopwords)
    preprocess_text(entertainment,sentences,cate_dic["entertainment"],stopwords)
    preprocess_text(military,sentences,cate_dic["military"],stopwords)
    preprocess_text(sports,sentences,cate_dic["sports"],stopwords)

    random.shuffle(sentences)    #做亂序處理,使得同類別的樣本不至於扎堆

    writeData(sentences,saveDataFile)

if __name__=="__main__":
    stopwordsFile=r"./data/stopwords.txt"
    stopwords=getStopWords(stopwordsFile)
    saveDataFile=r'train_data.txt'
    preprocessData(stopwords,saveDataFile)
    #fasttext.supervised():有監督的學習
    classifier=fasttext.supervised(saveDataFile,'classifier.model',lable_prefix='__lable__')
    result = classifier.test(saveDataFile)
    print("P@1:",result.precision)    #準確率
    print("R@2:",result.recall)    #召回率
    print("Number of examples:",result.nexamples)    #預測錯的例子

    #實際預測
    lable_to_cate={1:'technology'.1:'car',3:'entertainment',4:'military',5:'sports'}

    texts=['中新網 日電 2018 預賽 亞洲區 強賽 中國隊 韓國隊 較量 比賽 上半場 分鐘 主場 作戰 中國隊 率先 打破 場上 僵局 利用 角球 機會 大寶 前點 攻門 得手 中國隊 領先']
    lables=classifier.predict(texts)
    print(lables)
    print(lable_to_cate[int(lables[0][0])])

    #還可以得到類別+概率
    lables=classifier.predict_proba(texts)
    print(lables)

    #還可以得到前k個類別
    lables=classifier.predict(texts,k=3)
    print(lables)

    #還可以得到前k個類別+概率
    lables=classifier.predict_proba(texts,k=3)
    print(lables)

 

 

 

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