NLP第6課:動(dòng)手實(shí)戰(zhàn)基于 ML 的中文短文本分類

文本分類梳杏,屬于有監(jiān)督學(xué)習(xí)中的一部分棺聊,在很多場(chǎng)景下都有應(yīng)用伞租,下面通過小數(shù)據(jù)的實(shí)例,一步步完成中文短文本的分類實(shí)現(xiàn)限佩。

image

開發(fā)環(huán)境葵诈,我們選擇

  1. Mac 系統(tǒng)
  2. Python 3.6
  3. Jupyter Notebook

本文使用的數(shù)據(jù)是我曾經(jīng)做過的一份司法數(shù)據(jù),需求是對(duì)每一條輸入數(shù)據(jù)祟同,判斷事情的主體是誰(shuí)作喘,比如報(bào)警人被老公打,報(bào)警人被老婆打晕城,報(bào)警人被兒子打泞坦,報(bào)警人被女兒打等來進(jìn)行文本有監(jiān)督的分類操作。

整個(gè)過程分為以下幾個(gè)步驟

  • 語(yǔ)料加載
  • 分詞
  • 去停用詞
  • 抽取詞向量特征
  • 分別進(jìn)行算法建模和模型訓(xùn)練
  • 評(píng)估砖顷、計(jì)算 AUC 值
  • 模型對(duì)比

基本流程如下圖所示

image

下面開始項(xiàng)目實(shí)戰(zhàn)贰锁。

1. 首先進(jìn)行語(yǔ)料加載赃梧,在這之前,引入所需要的 Python 依賴包豌熄,并將全部語(yǔ)料和停用詞字典讀入內(nèi)存中授嘀。

第一步,引入依賴庫(kù)房轿,有隨機(jī)數(shù)庫(kù)粤攒、jieba 分詞所森、pandas 庫(kù)等:

    import random
    import jieba
    import pandas as pd

第二步囱持,加載停用詞字典,停用詞詞典為 stopwords.txt 文件焕济,可以根據(jù)場(chǎng)景自己在該文本里面添加要去除的詞(比如冠詞纷妆、人稱、數(shù)字等特定詞):

    #加載停用詞
    stopwords=pd.read_csv('stopwords.txt',index_col=False,quoting=3,sep="\t",names=['stopword'], encoding='utf-8')
stopwords=stopwords['stopword'].values

第三步晴弃,加載語(yǔ)料掩幢,語(yǔ)料是4個(gè)已經(jīng)分好類的 csv 文件,直接用 pandas 加載即可上鞠,加載之后可以首先刪除 nan 行际邻,并提取要分詞的 content 列轉(zhuǎn)換為 list 列表:

    #加載語(yǔ)料
    laogong_df = pd.read_csv('beilaogongda.csv', encoding='utf-8', sep=',')
    laopo_df = pd.read_csv('beilaogongda.csv', encoding='utf-8', sep=',')
    erzi_df = pd.read_csv('beierzida.csv', encoding='utf-8', sep=',')
    nver_df = pd.read_csv('beinverda.csv', encoding='utf-8', sep=',')
    #刪除語(yǔ)料的nan行
    laogong_df.dropna(inplace=True)
    laopo_df.dropna(inplace=True)
    erzi_df.dropna(inplace=True)
    nver_df.dropna(inplace=True)
    #轉(zhuǎn)換
    laogong = laogong_df.segment.values.tolist()
    laopo = laopo_df.segment.values.tolist()
    erzi = erzi_df.segment.values.tolist()
    nver = nver_df.segment.values.tolist()

  1. 分詞和去停用詞。

第一步芍阎,定義分詞世曾、去停用詞和批量打標(biāo)簽的函數(shù),函數(shù)包含3個(gè)參數(shù):content_lines 參數(shù)為語(yǔ)料列表谴咸;sentences 參數(shù)為預(yù)先定義的 list轮听,用來存儲(chǔ)分詞并打標(biāo)簽后的結(jié)果;category 參數(shù)為標(biāo)簽 :

    #定義分詞和打標(biāo)簽函數(shù)preprocess_text
    #參數(shù)content_lines即為上面轉(zhuǎn)換的list
    #參數(shù)sentences是定義的空l(shuí)ist岭佳,用來儲(chǔ)存打標(biāo)簽之后的數(shù)據(jù)
    #參數(shù)category 是類型標(biāo)簽
    def preprocess_text(content_lines, sentences, category):
        for line in content_lines:
            try:
                segs=jieba.lcut(line)
                segs = [v for v in segs if not str(v).isdigit()]#去數(shù)字
                segs = list(filter(lambda x:x.strip(), segs))   #去左右空格
                segs = list(filter(lambda x:len(x)>1, segs)) #長(zhǎng)度為1的字符
                segs = list(filter(lambda x:x not in stopwords, segs)) #去掉停用詞
                sentences.append((" ".join(segs), category))# 打標(biāo)簽
            except Exception:
                print(line)
                continue

第二步血巍,調(diào)用函數(shù)、生成訓(xùn)練數(shù)據(jù)珊随,根據(jù)我提供的司法語(yǔ)料數(shù)據(jù)述寡,分為報(bào)警人被老公打,報(bào)警人被老婆打叶洞,報(bào)警人被兒子打鲫凶,報(bào)警人被女兒打,標(biāo)簽分別為0京办、1掀序、2、3惭婿,具體如下:

    sentences = []
    preprocess_text(laogong, sentences,0)
    preprocess_text(laopo, sentences, 1)
    preprocess_text(erzi, sentences, 2)
    preprocess_text(nver, sentences, 3)

第三步不恭,將得到的數(shù)據(jù)集打散叶雹,生成更可靠的訓(xùn)練集分布,避免同類數(shù)據(jù)分布不均勻:

random.shuffle(sentences)

第四步换吧,我們?cè)诳刂婆_(tái)輸出前10條數(shù)據(jù)折晦,觀察一下:

    for sentence in sentences[:10]:
        print(sentence[0], sentence[1])  #下標(biāo)0是詞列表,1是標(biāo)簽

得到的結(jié)果如圖所示:

image

3. 抽取詞向量特征沾瓦。

第一步满着,抽取特征,我們定義文本抽取詞袋模型特征:

    from sklearn.feature_extraction.text import CountVectorizer
    vec = CountVectorizer(
        analyzer='word', # tokenise by character ngrams
        max_features=4000,  # keep the most common 1000 ngrams
    )

第二步贯莺,把語(yǔ)料數(shù)據(jù)切分风喇,用 sk-learn 對(duì)數(shù)據(jù)切分,分成訓(xùn)練集和測(cè)試集:

    from sklearn.model_selection import train_test_split
    x, y = zip(*sentences)
    x_train, x_test, y_train, y_test = train_test_split(x, y, random_state=1256)

第三步缕探,把訓(xùn)練數(shù)據(jù)轉(zhuǎn)換為詞袋模型:

vec.fit(x_train)

  1. 分別進(jìn)行算法建模和模型訓(xùn)練魂莫。

定義樸素貝葉斯模型,然后對(duì)訓(xùn)練集進(jìn)行模型訓(xùn)練爹耗,直接使用 sklearn 中的 MultinomialNB:

    from sklearn.naive_bayes import MultinomialNB
    classifier = MultinomialNB()
    classifier.fit(vec.transform(x_train), y_train)

  1. 評(píng)估耙考、計(jì)算 AUC 值。

第一步潭兽,上面步驟1-4完成了從語(yǔ)料到模型的訓(xùn)練,訓(xùn)練之后山卦,我們要用測(cè)試集來計(jì)算 AUC 值:

 print(classifier.score(vec.transform(x_test), y_test))

得到的結(jié)果評(píng)分為:0.647331786543鞋邑。

第二步,進(jìn)行測(cè)試集的預(yù)測(cè):

  pre = classifier.predict(vec.transform(x_test))

  1. 模型對(duì)比怒坯。

整個(gè)模型從語(yǔ)料到訓(xùn)練評(píng)估步驟1-5就完成了炫狱,接下來我們來看看,改變特征向量模型和訓(xùn)練模型對(duì)結(jié)果有什么變化剔猿。

(1)改變特征向量模型

下面可以把特征做得更強(qiáng)一點(diǎn)视译,嘗試加入抽取 2-gram 和 3-gram 的統(tǒng)計(jì)特征,把詞庫(kù)的量放大一點(diǎn)归敬。

    from sklearn.feature_extraction.text import CountVectorizer
    vec = CountVectorizer(
        analyzer='word', # tokenise by character ngrams
        ngram_range=(1,4),  # use ngrams of size 1 and 2
        max_features=20000,  # keep the most common 1000 ngrams
    )
    vec.fit(x_train)
    #用樸素貝葉斯算法進(jìn)行模型訓(xùn)練
    classifier = MultinomialNB()
    classifier.fit(vec.transform(x_train), y_train)
    #對(duì)結(jié)果進(jìn)行評(píng)分
    print(classifier.score(vec.transform(x_test), y_test))

得到的結(jié)果評(píng)分為:0.649651972158酷含,確實(shí)有一點(diǎn)提高,但是不太明顯汪茧。

(2)改變訓(xùn)練模型

使用 SVM 訓(xùn)練:

    from sklearn.svm import SVC
    svm = SVC(kernel='linear')
    svm.fit(vec.transform(x_train), y_train)
    print(svm.score(vec.transform(x_test), y_test))

使用決策樹椅亚、隨機(jī)森林、XGBoost舱污、神經(jīng)網(wǎng)絡(luò)等等:

    import xgboost as xgb  
    from sklearn.model_selection import StratifiedKFold  
    import numpy as np
    # xgb矩陣賦值  
    xgb_train = xgb.DMatrix(vec.transform(x_train), label=y_train)  
    xgb_test = xgb.DMatrix(vec.transform(x_test)) 

在 XGBoost 中呀舔,下面主要是調(diào)參指標(biāo),可以根據(jù)參數(shù)進(jìn)行調(diào)參:

    params = {  
            'booster': 'gbtree',     #使用gbtree
            'objective': 'multi:softmax',  # 多分類的問題扩灯、  
            # 'objective': 'multi:softprob',   # 多分類概率  
            #'objective': 'binary:logistic',  #二分類
            'eval_metric': 'merror',   #logloss
            'num_class': 4,  # 類別數(shù)媚赖,與 multisoftmax 并用  
            'gamma': 0.1,  # 用于控制是否后剪枝的參數(shù),越大越保守霜瘪,一般0.1、0.2這樣子惧磺。  
            'max_depth': 8,  # 構(gòu)建樹的深度颖对,越大越容易過擬合  
            'alpha': 0,   # L1正則化系數(shù)  
            'lambda': 10,  # 控制模型復(fù)雜度的權(quán)重值的L2正則化項(xiàng)參數(shù),參數(shù)越大磨隘,模型越不容易過擬合缤底。  
            'subsample': 0.7,  # 隨機(jī)采樣訓(xùn)練樣本  
            'colsample_bytree': 0.5,  # 生成樹時(shí)進(jìn)行的列采樣  
            'min_child_weight': 3,  
            # 這個(gè)參數(shù)默認(rèn)是 1,是每個(gè)葉子里面 h 的和至少是多少番捂,對(duì)正負(fù)樣本不均衡時(shí)的 0-1 分類而言  
            # 假設(shè) h 在 0.01 附近个唧,min_child_weight 為 1 葉子節(jié)點(diǎn)中最少需要包含 100 個(gè)樣本。  
            'silent': 0,  # 設(shè)置成1則沒有運(yùn)行信息輸出白嘁,最好是設(shè)置為0\.  
            'eta': 0.03,  # 如同學(xué)習(xí)率  
            'seed': 1000,  
            'nthread': -1,  # cpu 線程數(shù)  
            'missing': 1 
        }  

總結(jié)

上面通過真實(shí)司法數(shù)據(jù)坑鱼,一步步實(shí)現(xiàn)中文短文本分類的方法膘流,整個(gè)示例代碼可以當(dāng)做模板來用絮缅,從優(yōu)化和提高模型準(zhǔn)確率來說,主要有兩方面可以嘗試:

特征向量的構(gòu)建呼股,除了詞袋模型耕魄,可以考慮使用 word2vec 和 doc2vec 等;
模型上可以選擇有監(jiān)督的分類算法彭谁、集成學(xué)習(xí)以及神經(jīng)網(wǎng)絡(luò)等吸奴。

參考:http://www.reibang.com/p/3c8591a12bd6

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市缠局,隨后出現(xiàn)的幾起案子则奥,更是在濱河造成了極大的恐慌,老刑警劉巖狭园,帶你破解...
    沈念sama閱讀 219,490評(píng)論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件读处,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡唱矛,警方通過查閱死者的電腦和手機(jī)罚舱,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,581評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來绎谦,“玉大人管闷,你說我怎么就攤上這事∏猿Γ” “怎么了包个?”我有些...
    開封第一講書人閱讀 165,830評(píng)論 0 356
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)冤留。 經(jīng)常有香客問我碧囊,道長(zhǎng)恃锉,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,957評(píng)論 1 295
  • 正文 為了忘掉前任呕臂,我火速辦了婚禮破托,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘歧蒋。我一直安慰自己土砂,他們只是感情好赏胚,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,974評(píng)論 6 393
  • 文/花漫 我一把揭開白布昵骤。 她就那樣靜靜地躺著,像睡著了一般罩阵。 火紅的嫁衣襯著肌膚如雪阐虚。 梳的紋絲不亂的頭發(fā)上序臂,一...
    開封第一講書人閱讀 51,754評(píng)論 1 307
  • 那天,我揣著相機(jī)與錄音实束,去河邊找鬼奥秆。 笑死,一個(gè)胖子當(dāng)著我的面吹牛咸灿,可吹牛的內(nèi)容都是我干的构订。 我是一名探鬼主播,決...
    沈念sama閱讀 40,464評(píng)論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼避矢,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼悼瘾!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起审胸,我...
    開封第一講書人閱讀 39,357評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤亥宿,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后砂沛,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體烫扼,經(jīng)...
    沈念sama閱讀 45,847評(píng)論 1 317
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,995評(píng)論 3 338
  • 正文 我和宋清朗相戀三年尺上,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了材蛛。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,137評(píng)論 1 351
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡怎抛,死狀恐怖卑吭,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情马绝,我是刑警寧澤豆赏,帶...
    沈念sama閱讀 35,819評(píng)論 5 346
  • 正文 年R本政府宣布,位于F島的核電站,受9級(jí)特大地震影響掷邦,放射性物質(zhì)發(fā)生泄漏白胀。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,482評(píng)論 3 331
  • 文/蒙蒙 一抚岗、第九天 我趴在偏房一處隱蔽的房頂上張望或杠。 院中可真熱鬧,春花似錦宣蔚、人聲如沸向抢。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,023評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)挟鸠。三九已至,卻和暖如春亩冬,著一層夾襖步出監(jiān)牢的瞬間艘希,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,149評(píng)論 1 272
  • 我被黑心中介騙來泰國(guó)打工硅急, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留覆享,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,409評(píng)論 3 373
  • 正文 我出身青樓铜秆,卻偏偏與公主長(zhǎng)得像淹真,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子连茧,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,086評(píng)論 2 355

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