NLP第7課:動手實戰(zhàn)基于 ML 的中文短文本聚類

文本聚類是將一個個文檔由原有的自然語言文字信息轉(zhuǎn)化成數(shù)學信息睦裳,以高維空間點的形式展現(xiàn)出來乓序,通過計算哪些點距離比較近,從而將那些點聚成一個簇裸准,簇的中心叫做簇心。一個好的聚類要保證簇內(nèi)點的距離盡量的近赔硫,但簇與簇之間的點要盡量的遠炒俱。

如下圖,以 K、M权悟、N 三個點分別為聚類的簇心砸王,將結(jié)果聚為三類,使得簇內(nèi)點的距離盡量的近峦阁,但簇與簇之間的點盡量的遠谦铃。

image

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

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

本文繼續(xù)沿用上篇文本分類中的語料來進行文本無監(jiān)督聚類操作拇派。

整個過程分為以下幾個步驟

  • 語料加載
  • 分詞
  • 去停用詞
  • 抽取詞向量特征
  • 實戰(zhàn) TF-IDF 的中文文本 K-means 聚類
  • 實戰(zhàn) word2Vec 的中文文本 K-means 聚類

下面開始項目實戰(zhàn)荷辕。

1. 首先進行語料加載,在這之前件豌,引入所需要的 Python 依賴包,并將全部語料和停用詞字典讀入內(nèi)存中控嗜。

第一步茧彤,引入依賴庫,有隨機數(shù)庫疆栏、jieba 分詞曾掂、pandas 庫等:

    import random
    import jieba
    import pandas as pd
    import numpy as np
    from sklearn.feature_extraction.text import TfidfTransformer
    from sklearn.feature_extraction.text import TfidfVectorizer
    import matplotlib.pyplot as plt
    from sklearn.decomposition import PCA
    from sklearn.cluster import KMeans
    import gensim
    from gensim.models import Word2Vec
    from sklearn.preprocessing import scale
    import multiprocessing

第二步,加載停用詞字典壁顶,停用詞詞典為 stopwords.txt 文件珠洗,可以根據(jù)場景自己在該文本里面添加要去除的詞(比如冠詞、人稱若专、數(shù)字等特定詞):

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

第三步许蓖,加載語料,語料是4個已經(jīng)分好類的 csv 文件调衰,直接用 pandas 加載即可膊爪,加載之后可以首先刪除 nan 行,并提取要分詞的 content 列轉(zhuǎn)換為 list 列表:

    #加載語料
    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=',')
    #刪除語料的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. 分詞和去停用詞嚎莉。

第一步米酬,定義分詞、去停用詞的函數(shù)趋箩,函數(shù)包含兩個參數(shù):content_lines 參數(shù)為語料列表赃额;sentences 參數(shù)為預先定義的 list,用來存儲分詞后的結(jié)果:

    #定義分詞函數(shù)preprocess_text
    #參數(shù)content_lines即為上面轉(zhuǎn)換的list
    #參數(shù)sentences是定義的空list叫确,用來儲存分詞后的數(shù)據(jù)

    def preprocess_text(content_lines, sentences):
        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)) #長度為1的字符
                segs = list(filter(lambda x:x not in stopwords, segs)) #去掉停用詞
                sentences.append(" ".join(segs))
            except Exception:
                print(line)
                continue 

第二步跳芳,調(diào)用函數(shù)、生成訓練數(shù)據(jù)启妹,根據(jù)我提供的司法語料數(shù)據(jù)筛严,分為報警人被老公打,報警人被老婆打,報警人被兒子打桨啃,報警人被女兒打车胡,具體如下:

    sentences = []
    preprocess_text(laogong, sentences)
    preprocess_text(laopo, sentences)
    preprocess_text(erzi, sentences)
    preprocess_text(nver, sentences)

第三步,將得到的數(shù)據(jù)集打散照瘾,生成更可靠的訓練集分布匈棘,避免同類數(shù)據(jù)分布不均勻:

 random.shuffle(sentences)

第四步,我們控制臺輸出前10條數(shù)據(jù)析命,觀察一下(因為上面進行了隨機打散主卫,你看到的前10條可能不一樣):

    for sentence in sentences[:10]:
        print(sentenc)

得到的結(jié)果聚類和分類是不同的,這里沒有標簽:

image

3. 抽取詞向量特征鹃愤。

抽取特征簇搅,將文本中的詞語轉(zhuǎn)換為詞頻矩陣,統(tǒng)計每個詞語的 tf-idf 權(quán)值软吐,獲得詞在對應文本中的 tf-idf 權(quán)重:

    #將文本中的詞語轉(zhuǎn)換為詞頻矩陣 矩陣元素a[i][j] 表示j詞在i類文本下的詞頻
    vectorizer = TfidfVectorizer(sublinear_tf=True, max_df=0.5)
    #統(tǒng)計每個詞語的tf-idf權(quán)值
    transformer = TfidfTransformer()
    # 第一個fit_transform是計算tf-idf 第二個fit_transform是將文本轉(zhuǎn)為詞頻矩陣
    tfidf = transformer.fit_transform(vectorizer.fit_transform(sentences))
    # 獲取詞袋模型中的所有詞語
    word = vectorizer.get_feature_names()
    # 將tf-idf矩陣抽取出來瘩将,元素w[i][j]表示j詞在i類文本中的tf-idf權(quán)重
    weight = tfidf.toarray()
    #查看特征大小
    print ('Features length: ' + str(len(word)))

  1. 實戰(zhàn) TF-IDF 的中文文本 K-means 聚類

第一步,使用 k-means++ 來初始化模型凹耙,當然也可以選擇隨機初始化姿现,即 init="random",然后通過 PCA 降維把上面的權(quán)重 weight 降到10維肖抱,進行聚類模型訓練:

    numClass=4 #聚類分幾簇
    clf = KMeans(n_clusters=numClass, max_iter=10000, init="k-means++", tol=1e-6)  #這里也可以選擇隨機初始化init="random"
    pca = PCA(n_components=10)  # 降維
    TnewData = pca.fit_transform(weight)  # 載入N維
    s = clf.fit(TnewData)

第二步备典,定義聚類結(jié)果可視化函數(shù) plot_cluster(result,newData,numClass),該函數(shù)包含3個參數(shù)意述,其中 result 表示聚類擬合的結(jié)果集提佣;newData 表示權(quán)重 weight 降維的結(jié)果,這里需要降維到2維欲险,即平面可視化镐依;numClass 表示聚類分為幾簇,繪制代碼第一部分繪制結(jié)果 newData天试,第二部分繪制聚類的中心點:

    def plot_cluster(result,newData,numClass):
        plt.figure(2)
        Lab = [[] for i in range(numClass)]
        index = 0
        for labi in result:
            Lab[labi].append(index)
            index += 1
        color = ['oy', 'ob', 'og', 'cs', 'ms', 'bs', 'ks', 'ys', 'yv', 'mv', 'bv', 'kv', 'gv', 'y^', 'm^', 'b^', 'k^',
                 'g^'] * 3 
        for i in range(numClass):
            x1 = []
            y1 = []
            for ind1 in newData[Lab[I]]:
                # print ind1
                try:
                    y1.append(ind1[1])
                    x1.append(ind1[0])
                except:
                    pass
            plt.plot(x1, y1, color[I])

        #繪制初始中心點
        x1 = []
        y1 = []
        for ind1 in clf.cluster_centers_:
            try:
                y1.append(ind1[1])
                x1.append(ind1[0])
            except:
                pass
        plt.plot(x1, y1, "rv") #繪制中心
        plt.show()

第三步槐壳,對數(shù)據(jù)降維到2維,然后獲得結(jié)果喜每,最后繪制聚類結(jié)果圖:

    pca = PCA(n_components=2)  # 輸出兩維
    newData = pca.fit_transform(weight)  # 載入N維
    result = list(clf.predict(TnewData))
    plot_cluster(result,newData,numClass)

第四步务唐,得到的聚類結(jié)果圖,4個中心點和4個簇带兜,我們看到結(jié)果還比較好枫笛,簇的邊界很清楚:

image

第五步,上面演示的可視化過程刚照,降維使用了 PCA刑巧,我們還可以試試 TSNE,兩者同為降維工具,主要區(qū)別在于啊楚,所在的包不同(也即機制和原理不同):

    from sklearn.decomposition import PCA
    from sklearn.manifold import TSNE

因為原理不同吠冤,導致 TSNE 保留下的屬性信息,更具代表性恭理,也即最能體現(xiàn)樣本間的差異拯辙,但是 TSNE 運行極慢,PCA 則相對較快颜价,下面看看 TSNE 運行的可視化結(jié)果:

    from sklearn.manifold import TSNE
    ts =TSNE(2)
    newData = ts.fit_transform(weight)
    result = list(clf.predict(TnewData))
    plot_cluster(result,newData,numClass)

得到的可視化結(jié)果涯保,為一個中心點,不同簇落在圍繞中心點的不同半徑之內(nèi)周伦,我們看到在這里結(jié)果并不是很好:

image

第六步夕春,為了更好的表達和獲取更具有代表性的信息,在展示(可視化)高維數(shù)據(jù)時专挪,更為一般的處理撇他,常常先用 PCA 進行降維,再使用 TSNE:

    from sklearn.manifold import TSNE
    newData = PCA(n_components=4).fit_transform(weight)  # 載入N維
    newData =TSNE(2).fit_transform(newData)
    result = list(clf.predict(TnewData))
    plot_cluster(result,newData,numClass)

得到的可視化結(jié)果狈蚤,不同簇落在圍繞中心點的不同半徑之內(nèi):

image

總結(jié)

上面通過真實小案例,對司法數(shù)據(jù)一步步實現(xiàn)中文短文本聚類划纽,從優(yōu)化和提高模型準確率來說脆侮,主要有兩方面可以嘗試:

  1. 特征向量的構(gòu)建,除了詞袋模型勇劣,可以考慮使用 word2vec 和 doc2vec 等靖避;
  2. 模型上可以采用基于密度的 DBSCAN、層次聚類等算法比默。
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末幻捏,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子命咐,更是在濱河造成了極大的恐慌篡九,老刑警劉巖,帶你破解...
    沈念sama閱讀 216,402評論 6 499
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件醋奠,死亡現(xiàn)場離奇詭異榛臼,居然都是意外死亡,警方通過查閱死者的電腦和手機窜司,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,377評論 3 392
  • 文/潘曉璐 我一進店門沛善,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人塞祈,你說我怎么就攤上這事金刁。” “怎么了?”我有些...
    開封第一講書人閱讀 162,483評論 0 353
  • 文/不壞的土叔 我叫張陵尤蛮,是天一觀的道長媳友。 經(jīng)常有香客問我,道長抵屿,這世上最難降的妖魔是什么庆锦? 我笑而不...
    開封第一講書人閱讀 58,165評論 1 292
  • 正文 為了忘掉前任,我火速辦了婚禮轧葛,結(jié)果婚禮上搂抒,老公的妹妹穿的比我還像新娘。我一直安慰自己尿扯,他們只是感情好求晶,可當我...
    茶點故事閱讀 67,176評論 6 388
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著衷笋,像睡著了一般芳杏。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上辟宗,一...
    開封第一講書人閱讀 51,146評論 1 297
  • 那天爵赵,我揣著相機與錄音,去河邊找鬼泊脐。 笑死空幻,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的容客。 我是一名探鬼主播秕铛,決...
    沈念sama閱讀 40,032評論 3 417
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼缩挑!你這毒婦竟也來了但两?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 38,896評論 0 274
  • 序言:老撾萬榮一對情侶失蹤供置,失蹤者是張志新(化名)和其女友劉穎谨湘,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體士袄,經(jīng)...
    沈念sama閱讀 45,311評論 1 310
  • 正文 獨居荒郊野嶺守林人離奇死亡悲关,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,536評論 2 332
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了娄柳。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片寓辱。...
    茶點故事閱讀 39,696評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖赤拒,靈堂內(nèi)的尸體忽然破棺而出秫筏,到底是詐尸還是另有隱情诱鞠,我是刑警寧澤,帶...
    沈念sama閱讀 35,413評論 5 343
  • 正文 年R本政府宣布这敬,位于F島的核電站航夺,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏崔涂。R本人自食惡果不足惜阳掐,卻給世界環(huán)境...
    茶點故事閱讀 41,008評論 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望冷蚂。 院中可真熱鬧缭保,春花似錦、人聲如沸蝙茶。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,659評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽隆夯。三九已至钳恕,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間蹄衷,已是汗流浹背忧额。 一陣腳步聲響...
    開封第一講書人閱讀 32,815評論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留愧口,地道東北人宙址。 一個月前我還...
    沈念sama閱讀 47,698評論 2 368
  • 正文 我出身青樓,卻偏偏與公主長得像调卑,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子大咱,可洞房花燭夜當晚...
    茶點故事閱讀 44,592評論 2 353

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

  • 文本聚類 將一個個文檔表示成高維空間點恬涧,通過計算哪些點距離比較近,聚成一個簇碴巾,簇的中心叫做簇心 一個好的聚類要...
    不會停的蝸牛閱讀 15,779評論 20 92
  • 前言 關(guān)鍵詞提取就是從文本里面把跟這篇文章意義最相關(guān)的一些詞語抽取出來溯捆。這個可以追溯到文獻檢索初期,關(guān)鍵詞是為了文...
    Element靜婷閱讀 1,332評論 0 1
  • 文本分類厦瓢,屬于有監(jiān)督學習中的一部分提揍,在很多場景下都有應用,下面通過小數(shù)據(jù)的實例煮仇,一步步完成中文短文本的分類實現(xiàn)劳跃。 ...
    Element靜婷閱讀 771評論 0 3
  • 混沌中的每一顆氧氣分子撩騷著我的每一個細胞,分不清白天還是黑夜浙垫,高高的土坡刨仑,傾斜而稀松郑诺,側(cè)身是一片黑乎乎的東西,夾...
    趙斯年0622閱讀 334評論 0 3
  • Skin Tight 情人節(jié)這天會人滿為患的地方很多杉武,“轉(zhuǎn)角”卻并不是其中一個辙诞。今晚駐店樂隊缺席,更顯冷清轻抱,由音箱...
    搞個事情閱讀 2,417評論 0 0