PythonTMTable of Contents文本挖掘概述?什么是文本挖掘?文本挖掘的基本流程和任務(wù)?文本挖掘的基本思路?原始語(yǔ)料數(shù)據(jù)化時(shí)需要考慮的工作?Python文本挖掘的正確打開(kāi)姿勢(shì)?磨刀不誤砍柴工:工具準(zhǔn)備?Python的常見(jiàn)IDE簡(jiǎn)介?Anaconda的安裝與配置?Jupyter Notebook的基本操作?NLTK包的安裝?什么是NLTK?一個(gè)完整的?然語(yǔ)?處理框架
NLTK的主要模塊?
如何安裝NLTK?Anaconda中已經(jīng)默認(rèn)安裝
In [ ]: # 學(xué)員交流微信群:請(qǐng)加拉群專(zhuān)用微信號(hào) bigdatastar # 文彤老師微信公眾號(hào):統(tǒng)計(jì)之星import nltknltk.download() In [ ]: nltk.__version__ In [ ]: # 布朗語(yǔ)料庫(kù)示例from nltk.corpus import brownbrown.categories() In [ ]: len(brown.sents()) In [ ]: len(brown.words()) 常見(jiàn)的語(yǔ)料庫(kù)格式?外部文件 除直接網(wǎng)絡(luò)抓取并加工的情況外,原始文檔由于內(nèi)容較多,往往會(huì)首先以單個(gè)/多個(gè)文本文件的形式保存在外部,然后讀入程序 list 結(jié)構(gòu)靈活松散,有利于對(duì)原始語(yǔ)料進(jìn)行處理,也可以隨時(shí)增刪成員
list of list 語(yǔ)料完成分詞后的常見(jiàn)形式,每個(gè)文檔成為詞條構(gòu)成的list,而這些list又是原文檔list的成員
DataFrame 使用詞袋模型進(jìn)行后續(xù)數(shù)據(jù)分析時(shí)常見(jiàn)格式,行/列代表語(yǔ)料index,相應(yīng)的列/行代表詞條,或者需要加以記錄的文檔屬性,如作者,原始超鏈接,發(fā)表日期等 詞條/文檔對(duì)應(yīng)時(shí),單元格記錄相應(yīng)的詞條出現(xiàn)頻率,或者相應(yīng)的概率/分值 Doc2Term矩陣 Term2Doc矩陣 可以和原始語(yǔ)料的外部文件/list配合使用 對(duì)于單個(gè)文檔,也可以建立DataFrame,用行/列代表一個(gè)句子/段落/章節(jié)。 準(zhǔn)備《射雕》語(yǔ)料庫(kù)?為使用Python還不熟練的學(xué)員提供一個(gè)基于Pandas的通用操作框架。 讀入為數(shù)據(jù)框?In [ ]: import pandas as pd# 有的環(huán)境配置下read_table出錯(cuò),因此改用read_csvraw = pd.read_csv("金庸-射雕英雄傳txt精校版.txt", names = ['txt'], sep ='aaa', encoding ="GBK")print(len(raw))raw加入章節(jié)標(biāo)識(shí)?In [ ]: # 章節(jié)判斷用變量預(yù)處理def m_head(tmpstr): return tmpstr[:1]def m_mid(tmpstr): return tmpstr.find("回 ")# 注意:下面的raw.txt指的是raw數(shù)據(jù)框中的txt變量列,對(duì)pandas不熟悉的學(xué)員請(qǐng)復(fù)習(xí)相關(guān)知識(shí)raw['head'] = raw.txt.apply(m_head)raw['mid'] = raw.txt.apply(m_mid)raw['len'] = raw.txt.apply(len)# raw['chap'] = 0raw.head(50)In [ ]: # 章節(jié)判斷chapnum = 0for i in range(len(raw)): if raw['head'][i] == "第" and raw['mid'][i] > 0 and raw['len'][i] < 30 : chapnum += 1 if chapnum >= 40 and raw['txt'][i] == "附錄一:成吉思汗家族" : chapnum = 0 raw.loc[i, 'chap'] = chapnum # 刪除臨時(shí)變量,這里必須刪除,否則后續(xù)sum函數(shù)處會(huì)出錯(cuò)del raw['head']del raw['mid']del raw['len']raw.head(50) 提取出所需章節(jié)?In [ ]: raw[raw.chap == 1].head() In [ ]: from matplotlib import pyplot as plt%matplotlib inlineraw.txt.agg(len).plot.box() In [ ]: rawgrp = raw.groupby('chap')chapter = rawgrp.agg(sum) # 只有字符串列的情況下,sum函數(shù)自動(dòng)轉(zhuǎn)為合并字符串,對(duì)pandas不熟悉的學(xué)員請(qǐng)復(fù)習(xí)相關(guān)知識(shí)chapter = chapter[chapter.index != 0]chapter.txt[1]實(shí)戰(zhàn):準(zhǔn)備工具與素材?請(qǐng)自行完成分析用Anaconda環(huán)境的安裝和配置。 請(qǐng)自行熟悉Jupyter notebook環(huán)境的操作。 自行提取《射雕》任意一回的文字,并完成如下操作:
說(shuō)明: 最后一題主要涉及到Pandas的操作,對(duì)該模塊不熟悉的學(xué)員可直接繼續(xù)后續(xù)課程的學(xué)習(xí),這部分知識(shí)的欠缺并不會(huì)影響對(duì)文本挖掘課程本身的學(xué)習(xí)。當(dāng)然,能懂得相應(yīng)的知識(shí)是最好的。 In [ ]: import jiebatmpstr = "郭靖和哀牢山三十六劍。"res = jieba.cut(tmpstr) # 精確模式print(res) # 是一個(gè)可迭代的 generator,可以使用 for 循環(huán)來(lái)遍歷結(jié)果,本質(zhì)上類(lèi)似list In [ ]: print(' '.join(res))In [ ]: res = jieba.cut(tmpstr)list(word for word in res) # 演示generator的用法 In [ ]: print(jieba.lcut(tmpstr)) # 結(jié)果直接輸出為list In [ ]: print('/'.join(jieba.cut(tmpstr, cut_all = True))) # 全模式In [ ]: # 搜索引擎模式,還有jieba.lcut_for_search可用print('/'.join(jieba.cut_for_search(tmpstr)))In [ ]: # 動(dòng)態(tài)修改詞典jieba.add_word("哀牢山三十六劍")'/'.join(jieba.cut(tmpstr))In [ ]: jieba.del_word("哀牢山三十六劍")'/'.join(jieba.cut(tmpstr))使用自定義詞典?load_userdict(file_name) file_name:文件類(lèi)對(duì)象或自定義詞典的路徑 詞典基本格式 一個(gè)詞占一行:詞、詞頻(可省略)、詞性(可省略),用空格隔開(kāi) 詞典文件必須為 UTF-8 編碼 必要時(shí)可以使用Uedit32進(jìn)行文件編碼轉(zhuǎn)換
In [ ]: dict = '金庸小說(shuō)詞庫(kù).txt'jieba.load_userdict(dict) # dict為自定義詞典的路徑'/'.join(jieba.cut(tmpstr)) 使用搜狗細(xì)胞詞庫(kù)?https://pinyin.sogou.com/dict/ 按照詞庫(kù)分類(lèi)或者關(guān)鍵詞搜索方式,查找并下載所需詞庫(kù) 使用轉(zhuǎn)換工具,將其轉(zhuǎn)換為txt格式 深藍(lán)詞庫(kù)轉(zhuǎn)換 奧創(chuàng)詞庫(kù)轉(zhuǎn)換 在程序中導(dǎo)入相應(yīng)詞庫(kù) In [ ]: newlist = [ w for w in jieba.cut(tmpstr) if w not in ['和', '。'] ] print(newlist) In [ ]: import pandas as pdtmpdf = pd.read_csv('停用詞.txt', names = ['w'], sep = 'aaa', encoding = 'utf-8')tmpdf.head()In [ ]: # 熟悉Python的可以直接使用 open('stopWord.txt').readlines()獲取停用詞list,效率更高[ w for w in jieba.cut(tmpstr) if w not in list(tmpdf.w) ]用extract_tags函數(shù)去除停用詞?方法特點(diǎn): 根據(jù)TF-IDF算法將特征詞提取出來(lái),在提取之前去掉停用詞 可以人工指定停用詞字典 jieba.analyse.set_stop_words() In [ ]: # 使用預(yù)先準(zhǔn)備的停用詞表import jieba.analyse as anaana.set_stop_words('停用詞.txt')jieba.lcut(tmpstr) # 讀入的停用詞列表對(duì)分詞結(jié)果無(wú)效In [ ]: ana.extract_tags(tmpstr, topK = 20) # 使用TF-IDF算法提取關(guān)鍵詞,并同時(shí)去掉停用詞 詞性標(biāo)注?import jieba.posseg posseg.cut():給出附加詞性的分詞結(jié)果 詞性標(biāo)注采用和 ICTCLAS 兼容的標(biāo)記法 后續(xù)可基于詞性做進(jìn)一步處理,如只提取出名詞,動(dòng)詞等 In [ ]: import jieba.posseg as psgtmpres = psg.cut(tmpstr) # 附加詞性的分詞結(jié)果print(tmpres)for item in tmpres: print(item.word, item.flag) In [ ]: psg.lcut(tmpstr) # 直接輸出為list,成員為pair 分詞的NLTK實(shí)現(xiàn)?NLTK只能識(shí)別用空格作為詞條分割方式,因此不能直接用于中文文本的分詞。 一般的做法是先用jieba分詞,然后轉(zhuǎn)換為空格分隔的連續(xù)文本,再轉(zhuǎn)入NLTK框架使用。 rawtext = '周伯通笑道:“你懂了嗎?...” txt = ' '.join(jieba.cut(rawtext)) # "周伯通 笑 道 :..." toke = nltk.word_tokenize(txt) # ['周伯通', '笑', '道', ':'...] 實(shí)戰(zhàn):《射雕》一書(shū)分詞?選取第一回的文字,應(yīng)用搜狗的細(xì)胞詞庫(kù)和停用詞表,清理出干凈的分詞結(jié)果。 選取第一回中最長(zhǎng)的1個(gè)段落,比較不使用詞庫(kù)、不使用停用詞表前后的分詞結(jié)果。 熟悉搜狗細(xì)胞詞庫(kù)網(wǎng)站中的資源,思考哪些詞庫(kù)可能是自己需要的,下載相應(yīng)的資源并進(jìn)行格式轉(zhuǎn)換。 In [ ]: import jieba#分詞word_list = jieba.lcut(chapter.txt[1])word_list[:10] 構(gòu)建完list之后,也可以自行編寫(xiě)詞頻統(tǒng)計(jì)程序,框架如下:
使用Pandas統(tǒng)計(jì)?In [ ]: word_list = jieba.lcut(" ".join(raw.txt))In [ ]: word_list[:10] In [ ]: import pandas as pddf = pd.DataFrame(word_list, columns = ['word'])df.head(30) In [ ]: result = df.groupby(['word']).size()print(type(result))freqlist = result.sort_values(ascending=False)freqlist[:20] In [ ]: freqlist[freqlist.index == '道'] In [ ]: freqlist[freqlist.index == '黃蓉道'] In [ ]: jieba.add_word('道', freq = 50000)使用NLTK統(tǒng)計(jì)?NLTK生成的結(jié)果為頻數(shù)字典,在和某些程序包對(duì)接時(shí)比較有用 In [ ]: import nltk# 分詞等預(yù)處理工作# 這里可以根據(jù)需要做任何的preprocessing: stopwords, lemma, stemming, etc.word_list[:10] In [ ]: fdist = nltk.FreqDist(word_list) # 生成完整的詞條頻數(shù)字典fdist In [ ]: # 帶上某個(gè)單詞, 可以看到它在整個(gè)文章中出現(xiàn)的次數(shù)fdist['顏烈'] In [ ]: fdist.keys() # 列出詞條列表 In [ ]: fdist.tabulate(10) In [ ]: fdist.most_common(5) 詞云概述?wordcloud包的安裝?安裝?警告:wordcloud的安裝有可能非常順利,也有可能非常痛苦,完全是拼人品的一件事情。。。 方法1:pip install wordcloud 安裝后很可能不能用,直接成功的話(huà),您的人品實(shí)在是爆棚 方法2:python setup.py install 在github.com/amueller/word_cloud下載安裝包 方法3:下載第三方編譯好的whl文件進(jìn)行安裝 https://www.lfd./~gohlke/pythonlibs/#wordcloud Visual C++ build tools支持 提示:Microsoft Visual C++ 14.0 is required. 需要:Visual C++ 2015 Build Tools 文件:visualcppbuildtools_full.exe ImportError: DLL load failed: 找不到指定的模塊。 pip uninstall pillow,然后重新安裝pillow包 或者uninstall pillow之后使用上面的方法2安裝,會(huì)自動(dòng)安裝相關(guān)的支持包 中文字體支持?.WordCloud(font_path='simhei.ttf') 需要帶路徑寫(xiě)完整字體文件名 注意Win10的字體文件后綴可能不一樣 繪制詞云?WordCloud的基本語(yǔ)法?class wordcloud.WordCloud( 常用功能: font_path : 在圖形中使用的字體,默認(rèn)使用系統(tǒng)字體 width / height = 200 : 圖形的寬度/高度 max_words = 200 : 需要繪制的最多詞條數(shù) stopwords = None : 停用詞列表,不指定時(shí)會(huì)使用系統(tǒng)默認(rèn)停用詞列表 字體設(shè)定: min_font_size = 4 / max_font_size = None : 字符大小范圍 font_step = 1 : 字號(hào)增加的步長(zhǎng) relative_scaling = .5: 詞條頻數(shù)比例和字號(hào)大小比例的換算關(guān)系,默認(rèn)為50% prefer_horizontal = 0.90 : 圖中詞條水平顯示的比例 顏色設(shè)定: background_color = ”black” : 圖形背景色 mode = ”RGB”: 圖形顏色編碼,如果指定為"RGBA"且背景色為None時(shí),背景色為透明 color_func = None : 生成新顏色的函數(shù),使用matplotlib的colormap 背景掩模: mask = None : 詞云使用的背景圖(遮罩) ) 用原始文本直接分詞并繪制?cloudobj = WordCloud().generate(text) generate實(shí)際上是generate_from_text的別名 文本需要用空格/標(biāo)點(diǎn)符號(hào)分隔單詞,否則不能正確分詞 In [ ]: import wordcloudmyfont = r'C:\Windows\Fonts\simkai.ttf'text = 'this is shanghai, 郭靖, 和, 哀牢山 三十六劍'cloudobj = wordcloud.WordCloud(font_path = myfont).generate(text) print(cloudobj) In [ ]: import matplotlib.pyplot as pltplt.imshow(cloudobj)plt.axis("off")plt.show()In [ ]: # 更改詞云參數(shù)設(shè)定cloudobj = wordcloud.WordCloud(font_path = myfont,
width = 360, height = 180, mode = "RGBA", background_color = None).generate(text) plt.imshow(cloudobj)plt.axis("off")plt.show()保存詞云?wordcloud.to_file(保存文件的路徑與名稱(chēng)) 該命令保存的是高精度圖形 In [ ]: cloudobj.to_file("詞云.png")# wordcloud.WordCloud(font_path = myfont).generate(text).to_file(r"詞云.png")生成射雕第一章的詞云?In [ ]: import pandas as pdimport jiebastoplist = list(pd.read_csv('停用詞.txt', names = ['w'], sep = 'aaa',
encoding = 'utf-8', engine='python').w)def m_cut(intxt): return [ w for w in jieba.cut(intxt) if w not in stoplist ]In [ ]: cloudobj = wordcloud.WordCloud(font_path = myfont,
width = 1200, height = 800, mode = "RGBA", background_color = None, stopwords = stoplist).generate(' '.join(jieba.lcut(chapter.txt[1]))) plt.imshow(cloudobj)plt.axis("off")plt.show()In [ ]: cloudobj.to_file("詞云2.png")基于分詞頻數(shù)繪制?generate()的實(shí)際操作 調(diào)用分詞函數(shù)process_text() 調(diào)用基于頻數(shù)的繪制函數(shù)fit_words() fit_words(dict) 實(shí)際上是generate_from_frequencies的別名 Dict: 由詞條和頻數(shù)構(gòu)成的字典 In [ ]: #基于分詞頻數(shù)繪制詞云txt_freq = {'張三':100,'李四':90,'王二麻子':50}cloudobj = wordcloud.WordCloud(font_path = myfont).fit_words(txt_freq)plt.imshow(cloudobj)plt.axis("off")plt.show()用頻數(shù)生成射雕第一章的詞云?In [ ]: import nltkfrom nltk import FreqDisttokens = m_cut(chapter.txt[1])fdist = FreqDist(tokens) # 生成完整的詞條頻數(shù)字典type(fdist) In [ ]: cloudobj = wordcloud.WordCloud(font_path = myfont).fit_words(fdist)plt.imshow(cloudobj)plt.axis("off")plt.show()詞云的美化?各種詳細(xì)的操作和設(shè)定可以參考官網(wǎng)的案例: https://amueller./word_cloud/ 設(shè)置背景圖片?Mask / 掩模 / 遮罩 用于控制詞云的整體形狀 指定mask后,設(shè)置的寬高值將被忽略,遮罩形狀被指定圖形的形狀取代。除全白的部分仍然保留外,其余部分會(huì)用于繪制詞云。因此背景圖片的畫(huà)布一定要設(shè)置為白色(#FFFFFF) 字的大小,布局和顏色也會(huì)基于Mask生成 必要時(shí)需要調(diào)整顏色以增強(qiáng)可視效果 基本調(diào)用方式 from scipy.misc import imread mask = imread(背景圖片名稱(chēng)) In [ ]: from imageio import imreaddef m_cut(intxt): return [ w for w in jieba.cut(intxt) if w not in stoplist and len(w) > 1 ] cloudobj = wordcloud.WordCloud(font_path = myfont,
mask = imread("射雕背景1.png"),
mode = "RGBA", background_color = None ).generate(' '.join(m_cut(chapter.txt[1]))) plt.imshow(cloudobj)plt.axis("off")plt.show()指定圖片色系?讀取指定圖片的色系設(shè)定 imgarray = np.array(imread(imgfilepath)) 獲取圖片顏色 bimgColors = wordcloud.ImageColorGenerator(imgarray) 重置詞云顏色 cloudobj.recolor(color_func=bimgColors) # 利用已有詞云對(duì)象直接重繪顏色,輸出速度要比全部重繪快的多 In [ ]: import numpy as npimgobj = imread("射雕背景2.png")image_colors = wordcloud.ImageColorGenerator(np.array(imgobj))cloudobj.recolor(color_func=image_colors)plt.imshow(cloudobj)plt.axis("off")plt.show()指定單詞組顏色?理想的狀況應(yīng)該是分組比較詞頻,在兩組中都高頻的詞條在圖形中相互抵消。 Python目前只能實(shí)現(xiàn)詞條分組上色。 color_to_words = { '#00ff00': ['顏烈', '武官', '金兵', '小人'], 'red': ['包惜弱', '郭嘯天', '楊鐵心', '丘處機(jī)'] } '#00ff00'為綠色的代碼 default_color = 'grey' # 其余單詞的默認(rèn)顏色 cloudobj.recolor() In [ ]: # 官網(wǎng)提供的顏色分組類(lèi)代碼,略有修改from wordcloud import get_single_color_funcclass GroupedColorFunc(object): def __init__(self, color_to_words, default_color): self.color_func_to_words = [ (get_single_color_func(color), set(words)) for (color, words) in color_to_words.items()] self.default_color_func = get_single_color_func(default_color) def get_color_func(self, word): """Returns a single_color_func associated with the word""" try: color_func = next( color_func for (color_func, words) in self.color_func_to_words if word in words) except StopIteration: color_func = self.default_color_func return color_func def __call__(self, word, **kwargs): return self.get_color_func(word)(word, **kwargs)####### 指定分組色系color_to_words = { '#00ff00': ['顏烈', '武官', '金兵', '官兵'], 'red': ['包惜弱', '郭嘯天', '楊鐵心', '丘處機(jī)']}default_color = 'grey' # 指定其他詞條的顏色grouped_color_func = GroupedColorFunc(color_to_words, default_color)cloudobj.recolor(color_func=grouped_color_func)plt.imshow(cloudobj)plt.axis("off")plt.show()實(shí)戰(zhàn):優(yōu)化射雕詞云?嘗試進(jìn)一步清理分詞結(jié)果,并且只保留所有的名稱(chēng)(人名、地名)。 提示:可以使用詞性標(biāo)注功能,只保留名詞和未知詞性的詞。 可以考慮對(duì)自定義詞典做優(yōu)化,通過(guò)強(qiáng)行調(diào)整權(quán)重等方法改善分詞效果。 將所有的人名按照藍(lán)色系,地名按照紅色系進(jìn)行詞云繪制。 自行制作兩個(gè)純色圖片,分別為綠色和藍(lán)色,然后將其分別指定為繪圖所用的色系,觀察圖形效果。 嘗試使用不同的背景圖片作為掩模,思考怎樣的圖片才能使得繪圖效果最佳。 文檔信息的向量化?詞袋模型?詞袋模型的gensim實(shí)現(xiàn)?gensim的安裝?pip install genism 安裝完成后如果使用word2vec時(shí)報(bào)錯(cuò),建議去gensim官網(wǎng)下載MS windows install的exe程序進(jìn)行安裝:https://pypi./pypi/gensim 建立字典?Dictionary類(lèi)用于建立word<->id映射關(guān)系,把所有單詞取一個(gè)set(),并對(duì)set中每個(gè)單詞分配一個(gè)Id號(hào)的map class gensim.corpora.dictionary.Dictionary( documents=None : 若干個(gè)被拆成單詞集合的文檔的集合,一般以list in list形式出現(xiàn) prune_at=2000000 : 字典中的最大詞條容量 ) In [ ]: from gensim.corpora import Dictionarytexts = [['human', 'interface', 'computer']]dct = Dictionary(texts) # fit dictionarydct.num_nnz Dictionary類(lèi)的屬性?token2id dict of (str, int) – token -> tokenId. id2token dict of (int, str) – Reverse mapping for token2id, initialized in lazy manner to save memory. dfs dict of (int, int) – Document frequencies: token_id -> in how many documents contain this token. num_docs int – Number of documents processed. num_pos int – Total number of corpus positions (number of processed words). num_nnz int – Total number of non-zeroes in the BOW matrix. In [ ]: # 向字典增加詞條dct.add_documents([["cat", "say", "meow"], ["dog"]]) dct.token2id 轉(zhuǎn)換為BOW稀疏向量?dct.doc2bow( # 轉(zhuǎn)換為BOW格式:list of (token_id, token_count) document : 用于轉(zhuǎn)換的詞條list allow_update = False : 是否直接更新所用字典 return_missing = False : 是否返回新出現(xiàn)的(不在字典中的)詞 ) 輸出結(jié)果 [(0, 2), (1, 2)],表明在文檔中id為0,1的詞匯各出現(xiàn)了2次,至于其他詞匯則沒(méi)有出現(xiàn) return_missing = True時(shí),輸出list of (int, int), dict of (str, int) In [ ]: dct.doc2bow(["this", "is", "cat", "not", "a", "dog"]) In [ ]: dct.doc2bow(["this", "is", "cat", "not", "a", "dog"], return_missing = True) 轉(zhuǎn)換為BOW長(zhǎng)向量?可考慮的思路: 從稀疏格式自行轉(zhuǎn)換。 直接生成文檔-詞條矩陣。 doc2idx( # 轉(zhuǎn)換為list of token_id document : 用于轉(zhuǎn)換的詞條list unknown_word_index = -1 : 為不在字典中的詞條準(zhǔn)備的代碼 輸出結(jié)果 按照輸入list的順序列出所出現(xiàn)的各詞條ID In [ ]: dct.doc2idx(["this", "is", "a", "dog", "not", "cat"]) In [ ]: chapter.head() In [ ]: # 設(shè)定分詞及清理停用詞函數(shù)# 熟悉Python的可以使用 open('stopWord.txt').readlines() 獲取停用詞list,效率更高stoplist = list(pd.read_csv('停用詞.txt', names = ['w'], sep = 'aaa',
encoding = 'utf-8', engine='python').w)import jieba def m_cut(intxt): return [ w for w in jieba.cut(intxt)
if w not in stoplist and len(w) > 1 ]In [ ]: # 設(shè)定數(shù)據(jù)框轉(zhuǎn)換函數(shù)def m_appdf(chapnum): tmpdf = pd.DataFrame(m_cut(chapter.txt[chapnum + 1]), columns = ['word']) tmpdf['chap'] = chapter.index[chapnum] # 也可以直接 = chapnum + 1 return tmpdf In [ ]: # 全部讀入并轉(zhuǎn)換為數(shù)據(jù)框df0 = pd.DataFrame(columns = ['word', 'chap']) # 初始化結(jié)果數(shù)據(jù)框for i in range(len(chapter)): df0 = df0.append(m_appdf(i))df0.tail() In [ ]: # 輸出為序列格式df0.groupby(['word', 'chap']).agg('size').tail(10)In [ ]: # 直接輸出為數(shù)據(jù)框t2d = pd.crosstab(df0.word, df0.chap)len(t2d) In [ ]: t2d.head() In [ ]: # 計(jì)算各詞條的總出現(xiàn)頻次,準(zhǔn)備進(jìn)行低頻詞刪減totnum = t2d.agg(func = 'sum', axis=1)totnum In [ ]: t2dclean = t2d.iloc[list(totnum >= 10)]t2dclean.T 用sklearn庫(kù)實(shí)現(xiàn)?CountVectorizer類(lèi)的基本用法?文本信息在向量化之前很難直接納入建模分析,考慮到這一問(wèn)題,專(zhuān)門(mén)用于數(shù)據(jù)挖掘的sklearn庫(kù)提供了一個(gè)從文本信息到數(shù)據(jù)挖掘模型之間的橋梁,即CountVectorizer類(lèi),通過(guò)這一類(lèi)中的功能,可以很容易地實(shí)現(xiàn)文檔信息的向量化。 class sklearn.feature_extraction.text.CountVectorizer( input = 'content' : {'filename', 'file', 'content'}
filename為所需讀入的文件列表, file則為具體的文件名稱(chēng)。
encoding='utf-8' : 文檔編碼
stop_words = None : 停用詞列表,當(dāng)analyzer == 'word'時(shí)才生效
min_df / max_df : float in range [0.0, 1.0] or int, default = 1 / 1.0
詞頻絕對(duì)值/比例的閾值,在此范圍之外的將被剔除
小數(shù)格式說(shuō)明提供的是百分比,如0.05指的就是5%的閾值) CountVectorizer.build_analyzer() 返回文本預(yù)處理和分詞的可調(diào)用函數(shù) In [ ]: from sklearn.feature_extraction.text import CountVectorizercountvec = CountVectorizer(min_df = 2) # 在兩個(gè)以上文檔中出現(xiàn)的才保留analyze = countvec.build_analyzer()analyze('郭靖 和 哀牢山 三十六 劍 。')CountVectorizer.fit_transform(raw_documents) 對(duì)文檔進(jìn)行學(xué)習(xí)(處理),返回term-document matrix 等價(jià)于先調(diào)用fit函數(shù),然后再調(diào)用transform函數(shù),但是效率更高 In [ ]: countvec.fit(['郭靖 和 黃蓉 哀牢山 三十六 劍 。', '黃蓉 和 郭靖 郭靖']) In [ ]: countvec.get_feature_names() # 詞匯列表,實(shí)際上就是獲取每個(gè)列對(duì)應(yīng)的詞條 In [ ]: countvec.vocabulary_ # 詞條字典 In [ ]: x = countvec.transform(['郭靖 和 黃蓉 哀牢山 三十六 劍 。', '黃蓉 和 郭靖 郭靖'])type(x) In [ ]: x.todense() # 將稀疏矩陣直接轉(zhuǎn)換為標(biāo)準(zhǔn)格式矩陣 In [ ]: countvec.fit_transform(['郭靖 和 哀牢山 三十六 劍 。', '黃蓉 和 郭靖 郭靖']) # 一次搞定 使用sklearn生成射雕的章節(jié)d2m矩陣?將章節(jié)文檔數(shù)據(jù)框處理為空格分隔詞條的文本格式 使用fit_transform函數(shù)生成bow稀疏矩陣 轉(zhuǎn)換為標(biāo)準(zhǔn)格式的d2m矩陣 In [ ]: rawchap = [ " ".join(m_cut(w)) for w in chapter.txt.iloc[:5]] rawchap[0] In [ ]: from sklearn.feature_extraction.text import CountVectorizercountvec = CountVectorizer(min_df = 5) # 在5個(gè)以上章節(jié)中出現(xiàn)的才保留res = countvec.fit_transform(rawchap)res In [ ]: res.todense() In [ ]: countvec.get_feature_names() 實(shí)戰(zhàn):生成詞向量?嘗試編制以下程序: 以段為單位依次讀入射雕第一章的內(nèi)容。 為每一段分別生成bow稀疏向量。 生成稀疏向量的同時(shí)動(dòng)態(tài)更新字典。 請(qǐng)自行編制bow稀疏向量和標(biāo)準(zhǔn)長(zhǎng)向量互相轉(zhuǎn)換的程序。 請(qǐng)自行思考基于BOW的分析模型和基于分布式表示向量的模型在文本挖掘中的適用范圍和優(yōu)缺點(diǎn)。 在文檔詞條矩陣中可以看到許多類(lèi)似“黃蓉道”、“黃蓉說(shuō)”之類(lèi)的詞條,請(qǐng)思考對(duì)此有哪些處理辦法。 關(guān)鍵詞提取?關(guān)鍵詞提取的基本思路?TF-IDF 算法?TF-IDF的具體實(shí)現(xiàn)?jieba, NLTK, sklearn, gensim等程序包都可以實(shí)現(xiàn)TF-IDF的計(jì)算。除算法細(xì)節(jié)上會(huì)有差異外,更多的是數(shù)據(jù)輸入/輸出格式上的不同。 jieba?輸出結(jié)果會(huì)自動(dòng)按照TF-IDF值降序排列,并且直接給出的是詞條而不是字典ID,便于閱讀使用。 可在計(jì)算TF-IDF時(shí)直接完成分詞,并使用停用詞表和自定義詞庫(kù),非常方便。 有默認(rèn)的IDF語(yǔ)料庫(kù),可以不訓(xùn)練模型,直接進(jìn)行計(jì)算。 以單個(gè)文本為單位進(jìn)行分析。 jieba.analyse.extract_tags( sentence 為待提取的文本 topK = 20 : 返回幾個(gè) TF/IDF 權(quán)重最大的關(guān)鍵詞 withWeight = False : 是否一并返回關(guān)鍵詞權(quán)重值 allowPOS = () : 僅包括指定詞性的詞,默認(rèn)值為空,即不篩選 ) jieba.analyse.set_idf_path(file_name) 關(guān)鍵詞提取時(shí)使用自定義逆向文件頻率(IDF)語(yǔ)料庫(kù)
jieba.analyse.set_stop_words(file_name) 關(guān)鍵詞提取時(shí)使用自定義停止詞(Stop Words)語(yǔ)料庫(kù) jieba.analyse.TFIDF(idf_path = None) 新建 TFIDF模型實(shí)例 idf_path : 讀取已有的TFIDF頻率文件(即已有模型) 使用該實(shí)例提取關(guān)鍵詞:TFIDF實(shí)例.extract_tags() In [ ]: import jiebaimport jieba.analyse# 注意:函數(shù)是在使用默認(rèn)的TFIDF模型進(jìn)行分析!jieba.analyse.extract_tags(chapter.txt[1]) In [ ]: jieba.analyse.extract_tags(chapter.txt[1], withWeight = True) # 要求返回權(quán)重值 In [ ]: # 應(yīng)用自定義詞典改善分詞效果jieba.load_userdict('金庸小說(shuō)詞庫(kù).txt') # dict為自定義詞典的路徑# 在TFIDF計(jì)算中直接應(yīng)用停用詞表jieba.analyse.set_stop_words('停用詞.txt')TFres = jieba.analyse.extract_tags(chapter.txt[1], withWeight = True)TFres[:10]In [ ]: # 使用自定義TF-IDF頻率文件jieba.analyse.set_idf_path("idf.txt.big")TFres1 = jieba.analyse.extract_tags(chapter.txt[1], withWeight = True)TFres1[:10]sklearn?輸出格式為矩陣,直接為后續(xù)的sklearn建模服務(wù)。 需要先使用背景語(yǔ)料庫(kù)進(jìn)行模型訓(xùn)練。 結(jié)果中給出的是字典ID而不是具體詞條,直接閱讀結(jié)果比較困難。 class sklearn.feature_extraction.text.TfidfTransformer() 發(fā)現(xiàn)參數(shù)基本上都不用動(dòng),所以這里就不介紹了... In [ ]: from sklearn.feature_extraction.text import TfidfTransformertxtlist = [ " ".join(m_cut(w)) for w in chapter.txt.iloc[:5]] vectorizer = CountVectorizer() X = vectorizer.fit_transform(txtlist) # 將文本中的詞語(yǔ)轉(zhuǎn)換為詞頻矩陣 transformer = TfidfTransformer() tfidf = transformer.fit_transform(X) #基于詞頻矩陣X計(jì)算TF-IDF值 tfidf In [ ]: tfidf.toarray() # 轉(zhuǎn)換為數(shù)組 In [ ]: tfidf.todense() # 轉(zhuǎn)換為矩陣 In [ ]: tfidf.todense().shape In [ ]: print("字典長(zhǎng)度:", len(vectorizer.vocabulary_))vectorizer.vocabulary_gensim?輸出格式為list,目的也是為后續(xù)的建模分析服務(wù)。 需要先使用背景語(yǔ)料庫(kù)進(jìn)行模型訓(xùn)練。 結(jié)果中給出的是字典ID而不是具體詞條,直接閱讀結(jié)果比較困難。 gensim也提供了sklearn的API接口:sklearn_api.tfidf,可以在sklearn中直接使用。 In [ ]: # 文檔分詞及預(yù)處理 chaplist = [m_cut(w) for w in chapter.txt.iloc[:5]]chaplist In [ ]: from gensim import corpora, models # 生成文檔對(duì)應(yīng)的字典和bow稀疏向量dictionary = corpora.Dictionary(chaplist) corpus = [dictionary.doc2bow(text) for text in chaplist] # 仍為list in list corpus In [ ]: tfidf_model = models.TfidfModel(corpus) # 建立TF-IDF模型 corpus_tfidf = tfidf_model[corpus] # 對(duì)所需文檔計(jì)算TF-IDF結(jié)果corpus_tfidf In [ ]: corpus_tfidf[3] # 列出所需文檔的TF-IDF計(jì)算結(jié)果 In [ ]: dictionary.token2id # 列出字典內(nèi)容 In [ ]: jieba.analyse.textrank(chapter.txt[1], topK=20, withWeight = True) 實(shí)戰(zhàn)練習(xí)?請(qǐng)使用《射雕》全文計(jì)算出jieba分詞的IDF語(yǔ)料庫(kù),然后使用該語(yǔ)料庫(kù)重新對(duì)第一章計(jì)算關(guān)鍵詞。比較這樣的分析結(jié)果和以前有何不同。 請(qǐng)自行編制將jieba分詞的TF-IDF結(jié)果轉(zhuǎn)換為文檔-詞條矩陣格式的程序。 請(qǐng)自行思考本章提供的三種TF-IDF實(shí)現(xiàn)方式的使用場(chǎng)景是什么。 抽取文檔主題?主題模型的基本概念?sklearn實(shí)現(xiàn)?在scikit-learn中,LDA主題模型的類(lèi)被放置在sklearn.decomposition.LatentDirichletAllocation類(lèi)中,其算法實(shí)現(xiàn)主要基于變分推斷EM算法,而沒(méi)有使用基于Gibbs采樣的MCMC算法實(shí)現(xiàn)。 注意由于LDA是基于詞頻統(tǒng)計(jì)的,因此理論上一般不宜用TF-IDF來(lái)做文檔特征,但并非不能?chē)L試。實(shí)際分析中也確實(shí)會(huì)見(jiàn)到此類(lèi)操作。 class sklearn.decomposition.LatentDirichletAllocation( n_components = None : 隱含主題數(shù)K,需要設(shè)置的最重要參數(shù)。 K的設(shè)定范圍和具體的研究背景有關(guān)。 K越大,需要的文檔樣本越多。 doc_topic_prior = None : 文檔主題先驗(yàn)Dirichlet分布的參數(shù)α,未設(shè)定則用1/K。 topic_word_prior = None : 主題詞先驗(yàn)Dirichlet分布的參數(shù)η,未設(shè)定則用1/K。 learning_method = 'online' : 即LDA的求解算法。'batch' | 'online' batch: 變分推斷EM算法,會(huì)將將訓(xùn)練樣本分批用于更新主題詞分布,新版默認(rèn)算法。 樣本量不大只是用來(lái)學(xué)習(xí)的話(huà)用batch比較好,這樣可以少很多參數(shù)要調(diào)。 需注意n_components(K), doc_topic_prior(α), topic_word_prior(η) online: 在線(xiàn)變分推斷EM算法,大樣本時(shí)首選。 需進(jìn)一步注意learning_decay, learning_offset, total_samples和batch_size等參數(shù)。 僅在online算法時(shí)需要設(shè)定的參數(shù) learning_decay = 0.7 :控制"online"算法的學(xué)習(xí)率,一般不用修改。 取值最好在(0.5, 1.0],以保證"online"算法漸進(jìn)的收斂。 learning_offset = 10. :用來(lái)減小前面訓(xùn)練樣本批次對(duì)最終模型的影響。 取值要大于1。 total_samples = 1e6 : 分步訓(xùn)練時(shí)每一批文檔樣本的數(shù)量。 使用partial_fit進(jìn)行模型擬合時(shí)才需要此參數(shù)。 batch_size = 128 : 每次EM算法迭代時(shí)使用的文檔樣本的數(shù)量。 ) 將語(yǔ)料庫(kù)轉(zhuǎn)換為所需矩陣?除直接使用分詞清理后文本進(jìn)行轉(zhuǎn)換外,也可以先計(jì)算關(guān)鍵詞的TF-IDF值,然后使用關(guān)鍵詞矩陣進(jìn)行后續(xù)分析。 In [ ]: # 設(shè)定分詞及清理停用詞函數(shù)# 熟悉Python的可以使用 open('stopWord.txt').readlines() 獲取停用詞list,效率更高stoplist = list(pd.read_csv('停用詞.txt', names = ['w'], sep = 'aaa',
encoding = 'utf-8', engine='python').w)import jieba def m_cut(intxt): return [ w for w in jieba.cut(intxt)
if w not in stoplist and len(w) > 1 ]In [ ]: # 生成分詞清理后章節(jié)文本cleanchap = [ " ".join(m_cut(w)) for w in chapter.txt] In [ ]: # 將文本中的詞語(yǔ)轉(zhuǎn)換為詞頻矩陣 from sklearn.feature_extraction.text import CountVectorizercountvec = CountVectorizer(min_df = 5) wordmtx = countvec.fit_transform(cleanchap) wordmtx In [ ]: #基于詞頻矩陣X計(jì)算TF-IDF值 from sklearn.feature_extraction.text import TfidfTransformertransformer = TfidfTransformer() tfidf = transformer.fit_transform(wordmtx) tfidf In [ ]: # 設(shè)定LDA模型from sklearn.decomposition import LatentDirichletAllocationn_topics = 10ldamodel = LatentDirichletAllocation(n_components = n_topics) In [ ]: # 擬合LDA模型,注意這里使用的是原始wordmtx矩陣ldamodel.fit(wordmtx) In [ ]: # 擬合后模型的實(shí)質(zhì)print(ldamodel.components_.shape)ldamodel.components_[:2] In [ ]: # 主題詞打印函數(shù)def print_top_words(model, feature_names, n_top_words): for topic_idx, topic in enumerate(model.components_): print("Topic #%d:" % topic_idx) print(" ".join([feature_names[i]
for i in topic.argsort()[:-n_top_words - 1:-1]])) print()In [ ]: n_top_words = 12tf_feature_names = countvec.get_feature_names()print_top_words(ldamodel, tf_feature_names, n_top_words) gensim實(shí)現(xiàn)?class gensim.models.ldamodel.LdaModel( corpus = None : 用于訓(xùn)練模型的語(yǔ)料 num_topics = 100 : 準(zhǔn)備提取的主題數(shù)量 id2word = None : 所使用的詞條字典,便于結(jié)果閱讀 passes = 1 :模型遍歷語(yǔ)料庫(kù)的次數(shù),次數(shù)越多模型越精確,但是也更花時(shí)間 ) 用新出現(xiàn)的語(yǔ)料更新模型 ldamodel.update(other_corpus) gensim也提供了sklearn的API接口:sklearn_api.ldamodel,可以在sklearn中直接使用。 In [ ]: # 設(shè)定分詞及清理停用詞函數(shù)# 熟悉Python的可以使用 open('stopWord.txt').readlines()獲取停用詞list,效率更高stoplist = list(pd.read_csv('停用詞.txt', names = ['w'], sep = 'aaa',
encoding = 'utf-8', engine='python').w)import jieba def m_cut(intxt): return [ w for w in jieba.cut(intxt)
if w not in stoplist and len(w) > 1 ]In [ ]: # 文檔預(yù)處理,提取主題詞 chaplist = [m_cut(w) for w in chapter.txt] In [ ]: # 生成文檔對(duì)應(yīng)的字典和bow稀疏向量from gensim import corpora, models dictionary = corpora.Dictionary(chaplist) corpus = [dictionary.doc2bow(text) for text in chaplist] # 仍為list in list tfidf_model = models.TfidfModel(corpus) # 建立TF-IDF模型 corpus_tfidf = tfidf_model[corpus] # 對(duì)所需文檔計(jì)算TF-IDF結(jié)果corpus_tfidf In [ ]: from gensim.models.ldamodel import LdaModel# 列出所消耗的時(shí)間備查%time ldamodel1 = LdaModel(corpus, id2word = dictionary, \ num_topics = 10, passes = 2) 列出最重要的前若干個(gè)主題?print_topics(num_topics=20, num_words=10) In [ ]: ldamodel1.print_topics() In [ ]: # 計(jì)算各語(yǔ)料的LDA模型值corpus_lda = ldamodel1[corpus_tfidf] # 此處應(yīng)當(dāng)使用和模型訓(xùn)練時(shí)相同類(lèi)型的矩陣for doc in corpus_lda: print(doc) In [ ]: ldamodel1.get_topics() In [ ]: # 檢索和文本內(nèi)容最接近的主題query = chapter.txt[1] # 檢索和第1章最接近的主題query_bow = dictionary.doc2bow(m_cut(query)) # 頻數(shù)向量query_tfidf = tfidf_model[query_bow] # TF-IDF向量print("轉(zhuǎn)換后:", query_tfidf[:10])ldamodel1.get_document_topics(query_bow) # 需要輸入和文檔對(duì)應(yīng)的bow向量In [ ]: # 檢索和文本內(nèi)容最接近的主題ldamodel1[query_tfidf] 結(jié)果的圖形化呈現(xiàn)?文檔主題在呈現(xiàn)時(shí)需要解決的需求: 每個(gè)主題的含義是什么? 每個(gè)主題的重要性如何?是否是重要的主題? 主題直接的聯(lián)系是怎樣的? pyLDAvis包引入自R,可以用交互式圖形的方式呈現(xiàn)主題模型的分析結(jié)果。 同時(shí)支持sklearn和gensim包。 在許多系統(tǒng)配置下都會(huì)出現(xiàn)兼容問(wèn)題。 安裝時(shí)會(huì)先從最高版本的包進(jìn)行下載,然后根據(jù)兼容性報(bào)錯(cuò)依次降級(jí),直至找到適合的包為止(這都什么奇葩操作) pip install pyLDAvis pyLDAvis的結(jié)果呈現(xiàn)方式: 左側(cè):各個(gè)主題模型在模型空間中的相互關(guān)系和重要性。 空間定位使用MDS方式實(shí)現(xiàn)。 圓圈大小則代表該主題的流行程度(頻數(shù)意義上的重要性)。 右側(cè):列出和當(dāng)前選中主題頻數(shù)關(guān)聯(lián)最強(qiáng)的詞條。 Lambda參數(shù)的調(diào)節(jié)方式: 1 : 重要性完全由詞條的頻數(shù)高低來(lái)決定 0:重要性完全由詞條提升程度來(lái)決定 lift值:詞條在某主題下的出現(xiàn)頻度/詞條在整個(gè)文檔中的出現(xiàn)頻度
class pyLDAvis.sklearn.prepare( lda_model : 用sklearn基于dtm訓(xùn)練而來(lái)的Latent Dirichlet Allocation model dtm : 用于訓(xùn)練lda_model的Document-term matrix vectorizer :將raw documents轉(zhuǎn)換為dtm時(shí)使用的vectorizer ) # 返回值:用于可視化的數(shù)據(jù)結(jié)構(gòu) pyLDAvis.gensim.prepare()函數(shù)的參數(shù)設(shè)定與上面完全相同 In [ ]: # 對(duì)sklearn的LDA結(jié)果作呈現(xiàn)import pyLDAvisimport pyLDAvis.sklearnpyLDAvis.enable_notebook() In [ ]: pyLDAvis.sklearn.prepare(ldamodel, wordmtx, countvec) In [ ]: # 對(duì)gensim的LDA結(jié)果作呈現(xiàn)import pyLDAvis.gensimpyLDAvis.enable_notebook() In [ ]: pyLDAvis.gensim.prepare(ldamodel1, corpus, dictionary) In [ ]: pyLDAvis.disable_notebook() # 關(guān)閉notebook支持后,可以看到背后所生成的數(shù)據(jù) 實(shí)戰(zhàn)練習(xí)?在其余參數(shù)全部固定不變的情況下,嘗試分別用清理前矩陣、清理后原始矩陣、TF-IDF矩陣進(jìn)行LDA模型擬合,比較分析結(jié)果。 在gensim擬合LDA時(shí),分別將passes參數(shù)設(shè)置為1、5、10、50、100等,觀察結(jié)果變化的情況,思考如何對(duì)該參數(shù)做最優(yōu)設(shè)定。 請(qǐng)嘗試對(duì)模型進(jìn)行優(yōu)化,得到對(duì)本案例較好的分析結(jié)果。 提示:使用gensim進(jìn)行擬合更容易一些。 文檔相似度?基本概念?詞條相似度:word2vec?詞袋模型不考慮詞條之間的相關(guān)性,因此無(wú)法用于計(jì)算詞條相似度。 分布式表達(dá)會(huì)考慮詞條的上下文關(guān)聯(lián),因此能夠提取出詞條上下文中的相關(guān)性信息,而詞條之間的相似度就可以直接利用此類(lèi)信息加以計(jì)算。 目前主要使用gensim實(shí)現(xiàn)相應(yīng)的算法。 gensim也提供了sklearn的API接口:sklearn_api.w2vmodel,可以在sklearn中直接使用。 設(shè)置word2vec模型?class gensim.models.word2vec.Word2Vec( sentences = None : 類(lèi)似list of list的格式,對(duì)于特別大的文本,盡量考慮流式處理 vector_size = 100 : 詞條向量的維度,數(shù)據(jù)量充足時(shí),300/500的效果會(huì)更好 老版本中該參數(shù)為size window = 5 : 上下文窗口大小 workers = 3 : 同時(shí)運(yùn)行的線(xiàn)程數(shù),多核系統(tǒng)可明顯加速計(jì)算 其余細(xì)節(jié)參數(shù)設(shè)定: min_count = 5 : 低頻詞過(guò)濾閾值,低于該詞頻的不納入模型 max_vocab_size = None : 每1千萬(wàn)詞條需要1G內(nèi)存,必要時(shí)設(shè)定該參數(shù)以節(jié)約內(nèi)存 sample=0.001 : 負(fù)例采樣的比例設(shè)定 negative=5 : 一般為5-20,設(shè)為0時(shí)不進(jìn)行負(fù)例采樣 iter = 5 : 模型在語(yǔ)料庫(kù)上的迭代次數(shù),該參數(shù)將被取消 與神經(jīng)網(wǎng)絡(luò)模型有關(guān)的參數(shù)設(shè)定: seed=1, alpha=0.025, min_alpha=0.0001, sg=0, hs=0 ) In [ ]: chapter.head() In [ ]: # 分詞和預(yù)處理,生成list of list格式import jiebachapter['cut'] = chapter.txt.apply(jieba.lcut)chapter.head() In [ ]: # 初始化word2vec模型和詞表from gensim.models.word2vec import Word2Vecn_dim = 300 # 指定向量維度,大樣本量時(shí)300~500較好w2vmodel = Word2Vec(vector_size = n_dim, min_count = 10)w2vmodel.build_vocab(chapter.cut) # 生成詞表w2vmodel 對(duì)word2vec模型進(jìn)行訓(xùn)練?word2vecmodel.train( sentences : iterable of iterables格式,對(duì)于特別大量的文本,盡量考慮流式處理 total_examples = None : 句子總數(shù),int,可直接使用model.corpus_count指定 total_words = None : 句中詞條總數(shù),int,該參數(shù)和total_examples至少要指定一個(gè) epochs = None : 模型迭代次數(shù),需要指定 其他帶默認(rèn)值的參數(shù)設(shè)定: start_alpha=None, end_alpha=None, word_count=0, queue_factor=2, report_delay=1.0, compute_loss=False, callbacks=() ) In [ ]: # 在評(píng)論訓(xùn)練集上建模(大數(shù)據(jù)集時(shí)可能會(huì)花費(fèi)幾分鐘)# 本例消耗內(nèi)存較少%time w2vmodel.train(chapter.cut, \ total_examples = w2vmodel.corpus_count, epochs = 10) In [ ]: # 訓(xùn)練完畢的模型實(shí)質(zhì)print(w2vmodel.wv["郭靖"].shape)w2vmodel.wv["郭靖"] In [ ]: w2vmodel.wv.most_similar("郭靖")In [ ]: w2vmodel.wv.most_similar("黃蓉", topn = 20)In [ ]: w2vmodel.wv.most_similar("黃蓉道")In [ ]: # 尋找對(duì)應(yīng)關(guān)系w2vmodel.wv.most_similar(['郭靖', '小紅馬'], ['黃藥師'], topn = 5) In [ ]: w2vmodel.wv.most_similar(positive=['郭靖', '黃蓉'], negative=['楊康'], topn=10) In [ ]: # 計(jì)算兩個(gè)詞的相似度/相關(guān)程度print(w2vmodel.wv.similarity("郭靖", "黃蓉"))print(w2vmodel.wv.similarity("郭靖", "楊康"))print(w2vmodel.wv.similarity("郭靖", "楊鐵心"))In [ ]: # 尋找不合群的詞w2vmodel.wv.doesnt_match("小紅馬 黃藥師 魯有腳".split())In [ ]: w2vmodel.wv.doesnt_match("楊鐵心 黃藥師 黃蓉 洪七公".split())文檔相似度?基于詞袋模型計(jì)算?sklearn實(shí)現(xiàn)?sklearn.metrics.pairwise.pairwise_distances( X : 用于計(jì)算距離的數(shù)組 [n_samples_a, n_samples_a] if metric == 'precomputed' [n_samples_a, n_features] otherwise Y = None : 用于計(jì)算距離的第二數(shù)組,當(dāng)metric != 'precomputed'時(shí)可用 metric = 'euclidean' : 空間距離計(jì)算方式 scikit-learn原生支持 : ['cityblock', 'cosine', 'euclidean', 'l1', 'l2', 'manhattan'],可直接使用稀疏矩陣格式 來(lái)自scipy.spatial.distance : ['braycurtis', 'canberra', 'chebyshev', 'correlation', 'dice', 'hamming', 'jaccard', 'kulsinski', 'mahalanobis', 'matching', 'minkowski', 'rogerstanimoto', 'russellrao', 'seuclidean', 'sokalmichener', 'sokalsneath', 'sqeuclidean', 'yule'] 不支持稀疏矩陣格式 n_jobs = 1 : 用于計(jì)算的線(xiàn)程數(shù),為-1時(shí),所有CPU內(nèi)核都用于計(jì)算 ) In [ ]: cleanchap = [ " ".join(m_cut(w)) for w in chapter.txt.iloc[:5]] from sklearn.feature_extraction.text import CountVectorizercountvec = CountVectorizer() resmtx = countvec.fit_transform(cleanchap)resmtx In [ ]: from sklearn.metrics.pairwise import pairwise_distancespairwise_distances(resmtx, metric = 'cosine') In [ ]: pairwise_distances(resmtx) # 默認(rèn)值為euclidean In [ ]: # 使用TF-IDF矩陣進(jìn)行相似度計(jì)算pairwise_distances(tfidf[:5], metric = 'cosine') gensim實(shí)現(xiàn)?In [ ]: from gensim import similaritiessimmtx = similarities.MatrixSimilarity(corpus)simmtx In [ ]: # 檢索和第1章內(nèi)容最相似(所屬主題相同)的章節(jié)simmtx = similarities.MatrixSimilarity(corpus) # 使用的矩陣種類(lèi)需要和擬合模型時(shí)相同simmtx In [ ]: simmtx.index[:2] In [ ]: # 使用gensim的LDA擬合結(jié)果進(jìn)行演示query = chapter.txt[1] query_bow = dictionary.doc2bow(m_cut(query))lda_vec = ldamodel1[query_bow] # 轉(zhuǎn)換為lda模型下的向量sims = simmtx[lda_vec] # 進(jìn)行矩陣內(nèi)向量和所提供向量的余弦相似度查詢(xún)sims = sorted(enumerate(sims), key=lambda item: -item[1])sims doc2vec?word2vec用來(lái)計(jì)算詞條相似度非常合適。 較短的文檔如果希望計(jì)算文本相似度,可以將各自?xún)?nèi)部的word2vec向量分別進(jìn)行平均,用平均后的向量作為文本向量,從而用于計(jì)算相似度。 但是對(duì)于長(zhǎng)文檔,這種平均的方式顯然過(guò)于粗糙。 doc2vec是word2vec的拓展,它可以直接獲得sentences/paragraphs/documents的向量表達(dá),從而可以進(jìn)一步通過(guò)計(jì)算距離來(lái)得到sentences/paragraphs/documents之間的相似性。 模型概況 分析目的:獲得文檔的一個(gè)固定長(zhǎng)度的向量表達(dá)。 數(shù)據(jù):多個(gè)文檔,以及它們的標(biāo)簽,一般可以用標(biāo)題作為標(biāo)簽。 影響模型準(zhǔn)確率的因素:語(yǔ)料的大小,文檔的數(shù)量,越多越高;文檔的相似性,越相似越好。 In [ ]: import jieba import gensimfrom gensim.models import doc2vecdef m_doc(doclist): reslist = [] for i, doc in enumerate(doclist): reslist.append(doc2vec.TaggedDocument(jieba.lcut(doc), [i])) return reslistcorp = m_doc(chapter.txt) In [ ]: corp[:2] In [ ]: d2vmodel = gensim.models.Doc2Vec(vector_size = 300, window = 20, min_count = 5)%time d2vmodel.build_vocab(corp) In [ ]: # The vocab attribute was removed from KeyedVector in Gensim 4.0.0.d2vmodel.wv.key_to_index In [ ]: # 將新文本轉(zhuǎn)換為相應(yīng)維度空間下的向量newvec = d2vmodel.infer_vector(jieba.lcut(chapter.txt[1])) In [ ]: d2vmodel.docvecs.most_similar([newvec], topn = 10) 文檔聚類(lèi)?在得到文檔相似度的計(jì)算結(jié)果后,文檔聚類(lèi)問(wèn)題在本質(zhì)上已經(jīng)和普通的聚類(lèi)分析沒(méi)有區(qū)別。 注意:最常用的Kmeans使用的是平方歐氏距離,這在文本聚類(lèi)中很可能無(wú)法得到最佳結(jié)果。 算法的速度和效果同樣重要。 In [ ]: # 為章節(jié)增加名稱(chēng)標(biāo)簽chapter.index = [raw.txt[raw.chap == i].iloc[0] for i in chapter.index]chapter.head() In [ ]: import jiebacuttxt = lambda x: " ".join(m_cut(x)) cleanchap = chapter.txt.apply(cuttxt) cleanchap[:2] In [ ]: # 計(jì)算TF-IDF矩陣from sklearn.feature_extraction.text import TfidfTransformervectorizer = CountVectorizer() wordmtx = vectorizer.fit_transform(cleanchap) # 將文本中的詞語(yǔ)轉(zhuǎn)換為詞頻矩陣 transformer = TfidfTransformer() tfidf = transformer.fit_transform(wordmtx) #基于詞頻矩陣計(jì)算TF-IDF值 tfidf In [ ]: # 進(jìn)行聚類(lèi)分析from sklearn.cluster import KMeans clf = KMeans(n_clusters = 5) s = clf.fit(tfidf) print(s) clf.cluster_centers_ In [ ]: clf.cluster_centers_.shape In [ ]: clf.labels_ In [ ]: chapter['clsres'] = clf.labels_chapter.head() In [ ]: chapter.sort_values('clsres').clsresIn [ ]: chapgrp = chapter.groupby('clsres')chapcls = chapgrp.agg(sum) # 只有字符串列的情況下,sum函數(shù)自動(dòng)轉(zhuǎn)為合并字符串cuttxt = lambda x: " ".join(m_cut(x)) chapclsres = chapcls.txt.apply(cuttxt) chapclsresIn [ ]: # 列出關(guān)鍵詞以刻畫(huà)類(lèi)別特征import jieba.analyse as anaana.set_stop_words('停用詞.txt')for item in chapclsres: print(ana.extract_tags(item, topK = 10))實(shí)戰(zhàn)練習(xí)?在計(jì)算詞條相似度時(shí)進(jìn)行停用詞清理,然后再進(jìn)行擬合,思考為什么會(huì)有這樣的結(jié)果出現(xiàn)。 在基于詞袋模型,使用原始詞頻計(jì)算文本余弦相似度時(shí),比較清理停用詞前后的結(jié)果。 In [ ]: # 從原始語(yǔ)料df中提取出所需的前兩章段落raw12 = raw[raw.chap.isin([1,2])]raw12ana = raw12.iloc[list(raw12.txt.apply(len) > 50), :] # 只使用超過(guò)50字的段落raw12ana.reset_index(drop = True, inplace = True)print(len(raw12ana))raw12ana.head() In [ ]: # 分詞和預(yù)處理import jiebacuttxt = lambda x: " ".join(jieba.lcut(x)) # 這里不做任何清理工作,以保留情感詞raw12ana["cleantxt"] = raw12ana.txt.apply(cuttxt) raw12ana.head() In [ ]: from sklearn.feature_extraction.text import CountVectorizercountvec = CountVectorizer() wordmtx = countvec.fit_transform(raw12ana.cleantxt)wordmtx 劃分訓(xùn)練集和測(cè)試集?In [ ]: # 作用:將數(shù)據(jù)集劃分為 訓(xùn)練集和測(cè)試集from sklearn.model_selection import train_test_splitx_train, x_test, y_train, y_test = train_test_split(wordmtx, raw12ana.chap, test_size = 0.3, random_state = 111) 擬合樸素貝葉斯模型?In [ ]: from sklearn import naive_bayesNBmodel = naive_bayes.MultinomialNB() In [ ]: # 擬合模型NBmodel.fit(x_train, y_train) In [ ]: # 進(jìn)行驗(yàn)證集預(yù)測(cè)x_test In [ ]: NBmodel.predict(x_test) 模型評(píng)估?In [ ]: # 預(yù)測(cè)準(zhǔn)確率(給模型打分)print('訓(xùn)練集:', NBmodel.score(x_train, y_train),
',驗(yàn)證集:', NBmodel.score(x_test, y_test))In [ ]: from sklearn.metrics import classification_reportprint(classification_report(y_test, NBmodel.predict(x_test))) 使用Logistic回歸模型進(jìn)行分類(lèi)?In [ ]: from sklearn.linear_model import LogisticRegressionlogitmodel = LogisticRegression() # 定義Logistic回歸模型 In [ ]: # 擬合模型logitmodel.fit(x_train, y_train)print(classification_report(y_test, logitmodel.predict(x_test))) 模型預(yù)測(cè)?將需要預(yù)測(cè)的文本轉(zhuǎn)換為和建模時(shí)格式完全對(duì)應(yīng)的d2m矩陣格式,隨后即可進(jìn)行預(yù)測(cè)。 In [ ]: countvec.vocabulary_ In [ ]: string = "楊鐵心和包惜弱收養(yǎng)穆念慈"words = " ".join(jieba.lcut(string))words_vecs = countvec.transform([words]) # 數(shù)據(jù)需要轉(zhuǎn)換為可迭代的list格式words_vecs In [ ]: NBmodel.predict(words_vecs) In [ ]: # 使用Pandas的命令進(jìn)行轉(zhuǎn)換freqlist.to_dict() In [ ]: df0.groupby(['word']).agg('size').tail(10).to_dict()訓(xùn)練用數(shù)據(jù)集的格式?訓(xùn)練用數(shù)據(jù)集為list of list格式,每個(gè)成員為list[語(yǔ)料字典, 結(jié)果變量]
構(gòu)建模型?考慮到過(guò)擬合問(wèn)題,此處需要先拆分好訓(xùn)練集和測(cè)試集 model = NaiveBayesClassifier.train(training_data) In [ ]: # 這里直接以章節(jié)為一個(gè)單元進(jìn)行分析,以簡(jiǎn)化程序結(jié)構(gòu)import nltkfrom nltk import FreqDist# 生成完整的詞條頻數(shù)字典,這部分也可以用遍歷方式實(shí)現(xiàn)fdist1 = FreqDist(m_cut(chapter.txt[1])) fdist2 = FreqDist(m_cut(chapter.txt[2])) fdist3 = FreqDist(m_cut(chapter.txt[3])) fdist1 In [ ]: from nltk.classify import NaiveBayesClassifiertraining_data = [ [fdist1, 'chap1'], [fdist2, 'chap2'], [fdist3, 'chap3'] ] In [ ]: # 訓(xùn)練分類(lèi)模型NLTKmodel = NaiveBayesClassifier.train(training_data) In [ ]: print(NLTKmodel.classify(FreqDist(m_cut("楊鐵心收養(yǎng)穆念慈"))))print(NLTKmodel.classify(FreqDist(m_cut("錢(qián)塘江 日日夜夜 包惜弱 顏烈 使出楊家槍"))))模型擬合效果的考察?In [ ]: nltk.classify.accuracy(NLTKmodel, training_data) # 準(zhǔn)確度評(píng)價(jià) In [ ]: NLTKmodel.show_most_informative_features(5)#得到似然比,檢測(cè)對(duì)于哪些特征有用 實(shí)戰(zhàn)作業(yè)?對(duì)射雕的前兩個(gè)章節(jié)提取關(guān)鍵字,然后使用關(guān)鍵字而不是原始文本進(jìn)行文檔分類(lèi),比較這樣兩種方式的分類(lèi)效果有何變化。 減少用于訓(xùn)練的樣本量,考察使用樸素貝葉斯算法或者其他標(biāo)準(zhǔn)分類(lèi)算法時(shí),模型效果的變化趨勢(shì)。 提示:對(duì)編程比較熟悉的學(xué)員可以自行編制循環(huán)程序,自動(dòng)完成樣本量和模型效果的曲線(xiàn) 自行實(shí)現(xiàn)基于NLTK的按段落為單位進(jìn)行章節(jié)分類(lèi)的程序。 自行下載金庸或者古龍的另一本武俠小說(shuō),構(gòu)建任一文本段落在該小說(shuō)和射雕之間的分類(lèi)模型。 In [ ]: # 讀入原始數(shù)據(jù)集import pandas as pddfpos = pd.read_excel("購(gòu)物評(píng)論.xlsx", sheet_name = "正向", header=None)dfpos['y'] = 1dfneg = pd.read_excel("購(gòu)物評(píng)論.xlsx", sheet_name = "負(fù)向", header=None)dfneg['y'] = 0df0 = dfpos.append(dfneg, ignore_index = True)df0.head()In [ ]: # 分詞和預(yù)處理import jiebacuttxt = lambda x: " ".join(jieba.lcut(x)) # 這里不做任何清理工作,以保留情感詞df0["cleantxt"] = df0[0].apply(cuttxt) df0.head() In [ ]: from sklearn.feature_extraction.text import CountVectorizercountvec = CountVectorizer(min_df = 5) # 出現(xiàn)5次以上的才納入wordmtx = countvec.fit_transform(df0.cleantxt)wordmtx In [ ]: # 按照7:3的比例生成訓(xùn)練集和測(cè)試集from sklearn.model_selection import train_test_splitx_train, x_test, y_train, y_test = train_test_split( wordmtx, df0.y, test_size=0.3) # 這里可以直接使用稀疏矩陣格式x_train[0] In [ ]: # 使用SVM進(jìn)行建模from sklearn.svm import SVCclf=SVC(kernel = 'rbf', verbose = True)clf.fit(x_train, y_train) # 內(nèi)存占用可能較高clf.score(x_train, y_train) In [ ]: # 對(duì)模型效果進(jìn)行評(píng)估from sklearn.metrics import classification_reportprint(classification_report(y_test, clf.predict(x_test))) In [ ]: clf.predict(countvec.transform([df0.cleantxt[0]]))[0] In [ ]: # 模型預(yù)測(cè)import jiebadef m_pred(string, countvec, model) : words = " ".join(jieba.lcut(string)) words_vecs = countvec.transform([words]) # 數(shù)據(jù)需要轉(zhuǎn)換為可迭代格式 result = model.predict(words_vecs) if int(result[0]) == 1: print(string, ":正向") else: print(string, ":負(fù)向") comment = "外觀美觀,速度也不錯(cuò)。上面一排觸摸鍵挺實(shí)用。應(yīng)該對(duì)得起這個(gè)價(jià)格。當(dāng)然再降點(diǎn)大家肯定也不反對(duì)。風(fēng)扇噪音也不大。"m_pred(comment, countvec, clf) In [ ]: comment = "作為女兒6.1的禮物。雖然晚到了幾天。等拿到的時(shí)候,女兒愛(ài)不釋手,上洗手間也看,告知不好。竟以學(xué)習(xí)毛主席來(lái)反駁我。我反對(duì)了幾句,還說(shuō)我對(duì)主席不敬。暈。上周末,告訴我她把火鞋和風(fēng)鞋拿到學(xué)校,好多同學(xué)羨慕她。呵呵,我也看了其中的人鴉,只可惜沒(méi)有看完就在老公的催促下睡了。說(shuō)了這么多,歸納為一句:這套書(shū)買(mǎi)的值。"m_pred(comment, countvec, clf) 基于分布式表達(dá)的分析?和詞袋模型相比,分布式表達(dá)主要是改變了文本信息的提取方式。 目前主要使用gensim實(shí)現(xiàn)相應(yīng)的算法。 注意:由于矩陣不再是頻數(shù)值,因此不能使用樸素貝葉斯算法來(lái)進(jìn)行擬合。 In [ ]: # 讀入原始數(shù)據(jù)集,和上面完全相同import pandas as pddfpos = pd.read_excel("購(gòu)物評(píng)論.xlsx", sheet_name = "正向", header=None)dfpos['y'] = 1dfneg = pd.read_excel("購(gòu)物評(píng)論.xlsx", sheet_name = "負(fù)向", header=None)dfneg['y'] = 0df0 = dfpos.append(dfneg, ignore_index = True)df0.head()In [ ]: # 分詞和預(yù)處理,生成list of list格式import jiebadf0['cut'] = df0[0].apply(jieba.lcut)df0.head() In [ ]: # 按照7:3的比例生成訓(xùn)練集和測(cè)試集from sklearn.model_selection import train_test_splitx_train, x_test, y_train, y_test = train_test_split( df0.cut, df0.y, test_size=0.3)x_train[:2] 設(shè)置word2vec模型?In [ ]: # 初始化word2vec模型和詞表from gensim.models.word2vec import Word2Vecn_dim = 300 # 指定向量維度,大樣本量時(shí)300~500較好w2vmodel = Word2Vec(vector_size = n_dim, min_count = 10)w2vmodel.build_vocab(x_train) # 生成詞表 In [ ]: # 在評(píng)論訓(xùn)練集上建模(大數(shù)據(jù)集時(shí)可能會(huì)花費(fèi)幾分鐘)# 本例消耗內(nèi)存較少%time w2vmodel.train(x_train, \ total_examples = w2vmodel.corpus_count, epochs = 10) In [ ]: # 情感詞向量間的相似度w2vmodel.wv.most_similar("不錯(cuò)")In [ ]: w2vmodel.wv.most_similar("失望")生成整句向量用于情感分值預(yù)測(cè)?對(duì)購(gòu)物評(píng)論、微博等短文本而言,一般是將所有詞向量的平均值作為分類(lèi)算法的輸入值。 In [ ]: # 生成整句所對(duì)應(yīng)的所有詞條的詞向量矩陣pd.DataFrame([w2vmodel.wv[w] for w in df0.cut[0] if w in w2vmodel.wv]).head() In [ ]: # 用各個(gè)詞向量直接平均的方式生成整句對(duì)應(yīng)的向量def m_avgvec(words, w2vmodel): return pd.DataFrame([w2vmodel.wv[w]
for w in words if w in w2vmodel.wv]).agg("mean")In [ ]: # 生成建模用矩陣,耗時(shí)較長(zhǎng)%time train_vecs = pd.DataFrame([m_avgvec(s, w2vmodel) for s in x_train])train_vecs.head() 情感分析模型擬合?In [ ]: # 用轉(zhuǎn)換后的矩陣擬合SVM模型from sklearn.svm import SVCclf2 = SVC(kernel = 'rbf', verbose = True)clf2.fit(train_vecs, y_train) # 占用內(nèi)存小于1Gclf2.score(train_vecs, y_train) In [ ]: from sklearn.metrics import classification_reportprint(classification_report(y_train, clf2.predict(train_vecs))) # 此處未用驗(yàn)證集 In [ ]: # 保存訓(xùn)練完畢的模型以便今后使用# sklearn在0.23版之后已移除joblib,需直接安裝joblib包并import joblibimport joblib # joblib.dump(modelname, 'filename')# modelname = joblib.load('filename')In [ ]: # 模型預(yù)測(cè)import jiebadef m_pred(string, model): words = jieba.lcut(string) words_vecs = pd.DataFrame(m_avgvec(words, w2vmodel)).T result = model.predict(words_vecs) if int(result[0]) == 1: print(string, ":正向") else: print(string, ":負(fù)向") comment = "作為女兒6.1的禮物。雖然晚到了幾天。等拿到的時(shí)候,女兒愛(ài)不釋手,上洗手間也看,告知不好。竟以學(xué)習(xí)毛主席來(lái)反駁我。我反對(duì)了幾句,還說(shuō)我對(duì)主席不敬。暈。上周末,告訴我她把火鞋和風(fēng)鞋拿到學(xué)校,好多同學(xué)羨慕她。呵呵,我也看了其中的人鴉,只可惜沒(méi)有看完就在老公的催促下睡了。說(shuō)了這么多,歸納為一句:這套書(shū)買(mǎi)的值。"m_pred(comment, clf2) 實(shí)戰(zhàn)作業(yè)?自行完成基于情感詞典的分析程序,比較該方法與其他方法的預(yù)測(cè)準(zhǔn)確度。 提示:可使用《知網(wǎng)》情感詞語(yǔ)集作為詞典。 嘗試使用關(guān)鍵詞進(jìn)行基于詞袋模型的情感分析,評(píng)估效果的改進(jìn)情況。 在基于分布式表達(dá)的模型中,進(jìn)行去除停用詞等清理工作,比較前后模型效果的改變情況。 在本章所用數(shù)據(jù)中,各抽取1千條正向、負(fù)向評(píng)論,重新擬合基于詞袋的和基于分布式表達(dá)的模型,比較前兩種模型效果的改變情況。 In [ ]: chapter.txt[1] In [ ]: def cut_sentence(intxt):
delimiters = frozenset('。???')
buf = []
for ch in intxt:
buf.append(ch)
if delimiters.__contains__(ch):
yield ''.join(buf)
buf = []
if buf:
yield ''.join(buf)In [ ]: sentdf = pd.DataFrame(cut_sentence(chapter.txt[1]))sentdf In [ ]: # 去除過(guò)短的句子,避免摘要出現(xiàn)無(wú)意義的內(nèi)容sentdf['txtlen'] = sentdf[0].apply(len)sentdf.head() In [ ]: sentlist = sentdf[0][sentdf.txtlen > 20]print(len(sentlist))sentlist In [ ]: from sklearn.feature_extraction.text import CountVectorizerfrom sklearn.feature_extraction.text import TfidfTransformertxtlist = [ " ".join(jieba.lcut(w)) for w in sentlist]vectorizer = CountVectorizer() X = vectorizer.fit_transform(txtlist) # 將文本中的詞語(yǔ)轉(zhuǎn)換為詞頻矩陣 In [ ]: tfidf_matrix = TfidfTransformer().fit_transform(X) In [ ]: # 利用nx包實(shí)現(xiàn)pagerank算法import networkx as nx similarity = nx.from_scipy_sparse_matrix(tfidf_matrix * tfidf_matrix.T) scores = nx.pagerank(similarity) In [ ]: scores In [ ]: tops = sorted(scores.items(), key = lambda x: x[1], reverse = True) In [ ]: tops[:3] In [ ]: print(sentlist.iloc[tops[0][0]])print(sentlist.iloc[tops[1][0]])sentlist.iloc[tops[2][0]] In [ ]: topn = 5topsent = sorted(tops[:topn])abstract = ''for item in topsent: abstract = abstract + sentlist.iloc[item[0]] + "......"abstract[:-6] 實(shí)戰(zhàn)作業(yè)?請(qǐng)自行嘗試完成利用TextRank、TF-IDF等指標(biāo)來(lái)抽取句子并生成摘要的程序。 請(qǐng)嘗試使用段落而不是句子來(lái)生成摘要。 提示:對(duì)于字?jǐn)?shù)較長(zhǎng)的段落,可以考慮進(jìn)一步在其中提取關(guān)鍵句來(lái)代替整段用于摘要。 思考自動(dòng)摘要和抽取文檔主題的分析操作有什么異同之處。 自動(dòng)寫(xiě)作?自動(dòng)寫(xiě)作的基本原理?應(yīng)用場(chǎng)景?RNN的基本原理?LSTM的基本原理?用LSTM實(shí)現(xiàn)英文寫(xiě)作?英文文本內(nèi)容的預(yù)測(cè)可以縮減至預(yù)測(cè)下一個(gè)字符是什么,相應(yīng)的模型比較簡(jiǎn)單,因此可以用來(lái)演示LSTM的基本用法。 數(shù)據(jù)來(lái)源:古登堡計(jì)劃網(wǎng)站下載txt平文本 https://www./wiki/Category:Bookshelf 樣本數(shù)據(jù):r&j.txt 注意:目前Keras只支持到python3.6版,在高于3.6版的環(huán)境下可能無(wú)法運(yùn)行。 文本預(yù)處理?In [ ]: # 載入所需工具包import numpy as npimport pandas as pdfrom keras.models import Sequentialfrom keras.layers import Densefrom keras.layers import Dropoutfrom keras.layers import LSTMfrom keras.callbacks import ModelCheckpointfrom keras.utils import np_utils In [ ]: rawtxt = pd.read_csv("r&j.txt", sep = 'aaaaa',
names = ['txt'], engine = 'python')print(rawtxt.head())rawtxt.txt[1]In [ ]: # 處理大小寫(xiě),末尾增加空格def m_perproc(tmpstr): return (tmpstr + " ").lower()rawtxt.txt = rawtxt.txt.apply(m_perproc)rawtxt.txt[1] In [ ]: raw_txt = rawtxt.txt.agg("sum")raw_txtIn [ ]: # 將字符轉(zhuǎn)換為數(shù)值代碼以便處理chars = sorted(list(set(raw_txt))) # 生成字符listchar_to_int = dict((c, i) for i, c in enumerate(chars)) # 字符-數(shù)值對(duì)應(yīng)字典int_to_char = dict((i, c) for i, c in enumerate(chars)) # 數(shù)值-字符對(duì)應(yīng)字典chars 構(gòu)造訓(xùn)練測(cè)試集?In [ ]: seq_length = 100x = []; y = []for i in range(0, len(raw_txt) - seq_length): given = raw_txt[i:i + seq_length] # 將前seq_length個(gè)字符作為預(yù)測(cè)用變量 predict = raw_txt[i + seq_length] # 將當(dāng)前字符作為因變量 x.append([char_to_int[char] for char in given]) y.append(char_to_int[predict]) In [ ]: x[:3] In [ ]: y[:3] 將文本的數(shù)值表達(dá)轉(zhuǎn)換為L(zhǎng)STM需要的數(shù)組格式:[樣本數(shù),時(shí)間步伐,特征] In [ ]: n_patterns = len(x)n_vocab = len(chars)# 把x變成LSTM需要的格式,reshape最后的1表示每個(gè)數(shù)值均為單獨(dú)一個(gè)向量(代表一個(gè)字母輸入)x = np.reshape(x, (n_patterns, seq_length, 1)) x = x / float(n_vocab) # 轉(zhuǎn)換為0-1之間的數(shù)值以方便計(jì)算x[0] In [ ]: # 將因變量的類(lèi)型正確指定為類(lèi)別y = np_utils.to_categorical(y) y[0] 建立LSTM模型?In [ ]: model = Sequential() # LSTM層指定為128個(gè)神經(jīng)元model.add(LSTM(128, input_shape = (x.shape[1], x.shape[2]))) model.add(Dropout(0.2)) # 拋棄20%的結(jié)果,防止過(guò)擬合model.add(Dense(y.shape[1], activation = 'softmax')) # 使用標(biāo)準(zhǔn)的NN作為內(nèi)核# 指定損失函數(shù)model.compile(loss = 'categorical_crossentropy', optimizer = 'adam') In [ ]: # batch_size為分批量將數(shù)據(jù)用于訓(xùn)練,以減小計(jì)算資源的需求# epochs次數(shù)越多,模型訓(xùn)練效果越好,但所需時(shí)間也線(xiàn)性增加model.fit(x, y, epochs = 2, batch_size = 64) 進(jìn)行文本預(yù)測(cè)?In [ ]: def predict_next(input_array): # 進(jìn)行下一個(gè)字符的預(yù)測(cè) x = np.reshape([0 for i in range(seq_length - len(input_array))] + input_array, (1, seq_length, 1)) # 生成預(yù)測(cè)用的x序列 x = x / float(n_vocab) y = model.predict(x) return ydef string_to_index(raw_input): # 將輸入的字符轉(zhuǎn)換為索引值 res = [] for c in raw_input[(len(raw_input) - seq_length):]: res.append(char_to_int[c]) return resdef y_to_char(y): # 將預(yù)測(cè)結(jié)果由索引值轉(zhuǎn)換回字符 largest_index = y.argmax() # 取最大數(shù)值對(duì)應(yīng)的索引值 c = int_to_char[largest_index] return c In [ ]: def generate_article(init, rounds = 50): # 按照指定的字符長(zhǎng)度進(jìn)行預(yù)測(cè) in_string = init.lower() for i in range(rounds): n = y_to_char(predict_next(string_to_index(in_string))) in_string += n # 將預(yù)測(cè)到的新字符合并,用于下一步預(yù)測(cè) return in_string In [ ]: # 進(jìn)行字母預(yù)測(cè)init = 'We produce about two million dollars for each hour we work. The fifty hours is one conservative estimate for how long'article = generate_article(init)article 將LSTM與word2vec結(jié)合實(shí)現(xiàn)中文自動(dòng)寫(xiě)作?字母級(jí)別的預(yù)測(cè)由于無(wú)法利用單詞的字母組合信息,同時(shí)字母也并非最小語(yǔ)義單位,因此基于字母的預(yù)測(cè)模型其效果顯然會(huì)比較差。 如果要進(jìn)行單詞級(jí)別的預(yù)測(cè),則必須要考慮單詞的龐大數(shù)量所導(dǎo)致的稀疏向量問(wèn)題。 word2vec可以對(duì)稀疏向量中的有效信息進(jìn)行濃縮,從而使得單詞級(jí)別預(yù)測(cè)模型的計(jì)算量變得可行。 即便如此,在絕大多數(shù)情況下,這種自動(dòng)寫(xiě)作的計(jì)算量也是普通PC難以承受的。 文本預(yù)處理?In [ ]: # 載入所需工具包import jiebafrom gensim.models.word2vec import Word2Vecimport numpy as npimport pandas as pdfrom keras.models import Sequentialfrom keras.layers import Densefrom keras.layers import Dropoutfrom keras.layers import LSTMfrom keras.callbacks import ModelCheckpointfrom keras.utils import np_utils In [ ]: dict = '金庸小說(shuō)詞庫(kù).txt'jieba.load_userdict(dict) # dict為自定義詞典的路徑# 以整句或者整段為基本單位進(jìn)行分析顯然更為合適corpus = [jieba.lcut(item) for item in raw.txt]corpus[:3] 將文本轉(zhuǎn)換為word2vec向量,此處長(zhǎng)度越長(zhǎng),則后續(xù)所需的訓(xùn)練時(shí)間也越長(zhǎng)。 In [ ]: # 此處完全可以使用外部語(yǔ)料庫(kù)進(jìn)行更全面的訓(xùn)練w2v_model = Word2Vec(corpus, vector_size = 100, window = 5, min_count = 5, workers = 4) In [ ]: w2v_model.wv['郭嘯天'] In [ ]: # 將數(shù)據(jù)還原為一個(gè)長(zhǎng)listraw_input = [item for sublist in corpus for item in sublist]print(len(raw_input))raw_input[:10] In [ ]: # 列出模型中納入的詞條vocab = w2v_model.wv.index_to_keyvocab In [ ]: # min_count = 5參數(shù)會(huì)過(guò)濾掉低頻詞,因此需要在文本中同步清除這些低頻詞text_stream = []for word in raw_input: if word in vocab: text_stream.append(word)print(len(text_stream))text_stream[:10] 構(gòu)造訓(xùn)練測(cè)試集?In [ ]: seq_length = 10 # 取前面10個(gè)單詞用于預(yù)測(cè)x = []; y = []for i in range(0, len(text_stream) - seq_length): given = text_stream[i : i + seq_length] predict = text_stream[i + seq_length] x.append(np.array([w2v_model.wv[word] for word in given])) y.append(w2v_model.wv[predict]) In [ ]: len(x) In [ ]: x[0][0] In [ ]: y[0] 隨后將w2v格式的數(shù)值表達(dá)轉(zhuǎn)換為L(zhǎng)STM需要的格式:[樣本數(shù),時(shí)間步伐,特征] In [ ]: x = np.reshape(x, (-1, seq_length, 100)) # 每一個(gè)詞條,對(duì)應(yīng)一個(gè)word2vec向量y = np.reshape(y, (-1, 100)) 建立LSTM模型?In [ ]: model = Sequential()model.add(LSTM(128, input_shape = (seq_length, 100)))model.add(Dropout(0.2))model.add(Dense(100, activation = 'sigmoid'))model.compile(loss = 'mse', optimizer = 'adam') In [ ]: model.fit(x, y, epochs = 5, batch_size = 64) In [ ]: model.summary() In [ ]: model.save_weights('LSTM.h5') # 文件類(lèi)型是HDF5In [ ]: model.load_weights('LSTM.h5')In [ ]: model.fit(x, y, epochs = 10) # 按照指定的數(shù)據(jù)和參數(shù)繼續(xù)訓(xùn)練模型 持續(xù)訓(xùn)練找到優(yōu)化模型?In [ ]: from keras.callbacks import ModelCheckpointcheckpointer = ModelCheckpoint(filepath = 'LSTM_best.hdf5', monitor = 'val_loss', save_best_only = True, verbose = 1) In [ ]: model.compile(loss = 'mse', optimizer = 'adam') In [ ]: model.fit(x, y, epochs = 50, validation_data = (x, y), callbacks = [checkpointer]) 進(jìn)行文本預(yù)測(cè)?In [ ]: def predict_next(input_array): x = np.reshape(input_array, (-1, seq_length, 100)) y = model.predict(x) return ydef string_to_index(raw_input): input_stream = [] for word in jieba.lcut(raw_input): if word in vocab: input_stream.append(word) res = [] for word in input_stream[(len(input_stream) - seq_length):]: res.append(w2v_model.wv[word]) return resdef y_to_word(y): word = w2v_model.wv.most_similar(positive = y, topn = 1) return word In [ ]: def generate_article(init, rounds = 50): in_string = init.lower() for i in range(rounds): n = y_to_word(predict_next(string_to_index(in_string))) in_string += n[0][0] return in_string In [ ]: init = '郭嘯天、楊鐵心越聽(tīng)越怒。郭嘯天道:“靖康年間徽欽二帝被金兵擄去這件大恥,我們'article = generate_article(init)print(article) 實(shí)戰(zhàn)作業(yè)?有GPU計(jì)算條件的,請(qǐng)嘗試安裝和配置TensorFlow的GPU版本。 嘗試對(duì)原始文本進(jìn)行縮減,只篩選出包含郭靖、黃蓉的段落進(jìn)行訓(xùn)練,然后進(jìn)行郭靖、黃蓉之間對(duì)話(huà)的文本自動(dòng)寫(xiě)作。 在其余參數(shù)基本保持不變的情況下,將按段落進(jìn)行訓(xùn)練修改為按照整句進(jìn)行訓(xùn)練,比較兩者的效果。 |
|
|
來(lái)自: oipnfpte40pbqo > 《文件夾1》