基於 Python 的簡單自然語言處理實踐

基於 Python 的簡單自然語言處理實踐 從屬於筆者的 程序猿的數據科學與機器學習實戰手冊。

基於 Python 的簡單自然語言處理

本文是對於基於 Python 進行簡單自然語言處理任務的介紹,本文的所有代碼放置在這裡。建議前置閱讀 Python 語法速覽與機器學習開發環境搭建,更多機器學習資料參考機器學習、深度學習與自然語言處理領域推薦的書籍列表以及面向程序猿的數據科學與機器學習知識體系及資料合集。

Twenty News Group 語料集處理

20 Newsgroup 數據集包含了約 20000 篇來自於不同的新聞組的文檔,最早由 Ken Lang 搜集整理。本部分包含了對於數據集的抓取、特徵提取、簡單分類器訓練、主題模型訓練等。本部分代碼包括主要的處理代碼封裝庫與基於 Notebook 的交互示範。我們首先需要進行數據抓取:

def fetch_data(self, subset=train, categories=None):n """return datan 執行數據抓取操作n Arguments:n subset -> string -- 抓取的目標集合 train / test / alln """n rand = np.random.mtrand.RandomState(8675309)n data = fetch_20newsgroups(subset=subset,n categories=categories,n shuffle=True,n random_state=rand)nn self.data[subset] = datan

然後在 Notebook 中交互查看數據格式:

# 實例化對象ntwp = TwentyNewsGroup()n# 抓取數據ntwp.fetch_data()ntwenty_train = twp.data[train]nprint("數據集結構", "->", twenty_train.keys())nprint("文檔數目", "->", len(twenty_train.data))nprint("目標分類", "->",[ twenty_train.target_names[t] for t in twenty_train.target[:10]])nn數據集結構 -> dict_keys([data, filenames, target_names, target, DESCR, description])n文檔數目 -> 11314n目標分類 -> [sci.space, comp.sys.mac.hardware, sci.electronics, comp.sys.mac.hardware, sci.space, rec.sport.hockey, talk.religion.misc, sci.med, talk.religion.misc, talk.politics.guns]n

接下來我們可以對語料集中的特徵進行提取:

# 進行特徵提取nn# 構建文檔-詞矩陣(Document-Term Matrix)nnfrom sklearn.feature_extraction.text import CountVectorizernncount_vect = CountVectorizer()nnX_train_counts = count_vect.fit_transform(twenty_train.data)nnprint("DTM 結構","->",X_train_counts.shape)nn# 查看某個詞在詞表中的下標nprint("詞對應下標","->", count_vect.vocabulary_.get(ualgorithm))nnDTM 結構 -> (11314, 130107)n詞對應下標 -> 27366n

為了將文檔用於進行分類任務,還需要使用 TF-IDF 等常見方法將其轉化為特徵向量:

# 構建文檔的 TF 特徵向量nfrom sklearn.feature_extraction.text import TfidfTransformernntf_transformer = TfidfTransformer(use_idf=False).fit(X_train_counts)nX_train_tf = tf_transformer.transform(X_train_counts)nnprint("某文檔 TF 特徵向量","->",X_train_tf)nn# 構建文檔的 TF-IDF 特徵向量nfrom sklearn.feature_extraction.text import TfidfTransformernntf_transformer = TfidfTransformer().fit(X_train_counts)nX_train_tfidf = tf_transformer.transform(X_train_counts)nnprint("某文檔 TF-IDF 特徵向量","->",X_train_tfidf)nn某文檔 TF 特徵向量 -> (0, 6447) 0.0380693493813n (0, 37842) 0.0380693493813n

我們可以將特徵提取、分類器訓練與預測封裝為單獨函數:

def extract_feature(self):n """n 從語料集中抽取文檔特徵n """nn # 獲取訓練數據的文檔-詞矩陣n self.train_dtm = self.count_vect.fit_transform(self.data[train].data)nn # 獲取文檔的 TF 特徵nn tf_transformer = TfidfTransformer(use_idf=False)nn self.train_tf = tf_transformer.transform(self.train_dtm)nn # 獲取文檔的 TF-IDF 特徵nn tfidf_transformer = TfidfTransformer().fit(self.train_dtm)nn self.train_tfidf = tf_transformer.transform(self.train_dtm)nn def train_classifier(self):n """n 從訓練集中訓練出分類器n """nn self.extract_feature();nn self.clf = MultinomialNB().fit(n self.train_tfidf, self.data[train].target)nn def predict(self, docs):n """n 從訓練集中訓練出分類器n """nn X_new_counts = self.count_vect.transform(docs)nn tfidf_transformer = TfidfTransformer().fit(X_new_counts)n n X_new_tfidf = tfidf_transformer.transform(X_new_counts)nn return self.clf.predict(X_new_tfidf)n

然後執行訓練並且進行預測與評價:

# 訓練分類器ntwp.train_classifier()nn# 執行預測ndocs_new = [God is love, OpenGL on the GPU is fast]npredicted = twp.predict(docs_new)nnfor doc, category in zip(docs_new, predicted):n print(%r => %s % (doc, twenty_train.target_names[category]))n n# 執行模型評測ntwp.fetch_data(subset=test)nnpredicted = twp.predict(twp.data[test].data)nnimport numpy as npnn# 誤差計算nn# 簡單誤差均值nnp.mean(predicted == twp.data[test].target) nn# Metricsnnfrom sklearn import metricsnnprint(metrics.classification_report(n twp.data[test].target, predicted,n target_names=twp.data[test].target_names))nn# Confusion Matrixnmetrics.confusion_matrix(twp.data[test].target, predicted)nnGod is love => soc.religion.christiannOpenGL on the GPU is fast => rec.autosn precision recall f1-score supportnn alt.atheism 0.79 0.50 0.61 319n ...n talk.religion.misc 1.00 0.08 0.15 251nn avg / total 0.82 0.79 0.77 7532nnOut[16]:narray([[158, 0, 1, 1, 0, 1, 0, 3, 7, 1, 2, 6, 1,n 8, 3, 114, 6, 7, 0, 0],n ...n [ 35, 3, 1, 0, 0, 0, 1, 4, 1, 1, 6, 3, 0,n 6, 5, 127, 30, 5, 2, 21]])n

我們也可以對文檔集進行主題提取:

# 進行主題提取nntwp.topics_by_lda()nnTopic 0 : stream s1 astronaut zoo laurentian maynard s2 gtoal pem fpunTopic 1 : 145 cx 0d bh sl 75u 6um m6 sy gldnTopic 2 : apartment wpi mars nazis monash palestine ottoman sas winner gerardnTopic 3 : livesey contest satellite tamu mathew orbital wpd marriage solntze popenTopic 4 : x11 contest lib font string contrib visual xterm ahl brakenTopic 5 : ax g9v b8f a86 1d9 pl 0t wm 34u giznTopic 6 : printf null char manes behanna senate handgun civilians homicides magpienTopic 7 : buf jpeg chi tor bos det que uwo pit blahnTopic 8 : oracle di t4 risc nist instruction msg postscript dma convexnTopic 9 : candida cray yeast viking dog venus bloom symptoms observatory robynTopic 10 : cx ck hz lk mv cramer adl optilink k8 uwnTopic 11 : ripem rsa sandvik w0 bosnia psuvm hudson utk defensive vealnTopic 12 : db espn sabbath br widgets liar davidian urartu sdpa coolingnTopic 13 : ripem dyer ucsu carleton adaptec tires chem alchemy lockheed rsanTopic 14 : ingr sv alomar jupiter borland het intergraph factory paradox captainnTopic 15 : militia palestinian cpr pts handheld sharks igc apc jake lehighnTopic 16 : alaska duke col russia uoknor aurora princeton nsmca gene stereonTopic 17 : uuencode msg helmet eos satan dseg homosexual ics gear pyronnTopic 18 : entries myers x11r4 radar remark cipher maine hamburg senior bontchevnTopic 19 : cubs ufl vitamin temple gsfc mccall astro bellcore uranium wesleyann

常見自然語言處理工具封裝

經過上面對於 20NewsGroup 語料集處理的介紹我們可以發現常見自然語言處理任務包括,數據獲取、數據預處理、數據特徵提取、分類模型訓練、主題模型或者詞向量等高級特徵提取等等。筆者還習慣用 python-fire 將類快速封裝為可通過命令行調用的工具,同時也支持外部模塊調用使用。本部分我們主要以中文語料集為例,譬如我們需要對中文維基百科數據進行分析,可以使用 gensim 中的維基百科處理類:

class Wiki(object):n """n 維基百科語料集處理n """n n def wiki2texts(self, wiki_data_path, wiki_texts_path=./wiki_texts.txt):n """n 將維基百科數據轉化為文本數據n Arguments:n wiki_data_path -- 維基壓縮文件地址n """n if not wiki_data_path:n print("請輸入 Wiki 壓縮文件路徑或者前往 https://dumps.wikimedia.org/zhwiki/ 下載")n exit()nn # 構建維基語料集n wiki_corpus = WikiCorpus(wiki_data_path, dictionary={})n texts_num = 0nn with open(wiki_text_path, w, encoding=utf-8) as output:n for text in wiki_corpus.get_texts():n output.write(b .join(text).decode(utf-8) + n)n texts_num += 1n if texts_num % 10000 == 0:n logging.info("已處理 %d 篇文章" % texts_num)nn print("處理完畢,請使用 OpenCC 轉化為簡體字")n

抓取完畢後,我們還需要用 OpenCC 轉化為簡體字。抓取完畢後我們可以使用結巴分詞對生成的文本文件進行分詞,代碼參考這裡,我們直接使用 python chinese_text_processor.py tokenize_file /output.txt 直接執行該任務並且生成輸出文件。獲取分詞之後的文件,我們可以將其轉化為簡單的詞袋錶示或者文檔-詞向量,詳細代碼參考這裡:

class CorpusProcessor:n """n 語料集處理n """nn def corpus2bow(self, tokenized_corpus=default_documents):n """returns (vocab,corpus_in_bow)n 將語料集轉化為 BOW 形式n Arguments:n tokenized_corpus -- 經過分詞的文檔列表n Return:n vocab -- {human: 0, ... minors: 11}n corpus_in_bow -- [[(0, 1), (1, 1), (2, 1)]...]n """n dictionary = corpora.Dictionary(tokenized_corpus)nn # 獲取詞表n vocab = dictionary.token2idnn # 獲取文檔的詞袋錶示n corpus_in_bow = [dictionary.doc2bow(text) for text in tokenized_corpus]nn return (vocab, corpus_in_bow)nn def corpus2dtm(self, tokenized_corpus=default_documents, min_df=10, max_df=100):n """returns (vocab, DTM)n 將語料集轉化為文檔-詞矩陣n - dtm -> matrix: 文檔-詞矩陣n I like hate databasesn D1 1 1 0 1n D2 1 0 1 1n """nn if type(tokenized_corpus[0]) is list:n documents = [" ".join(document) for document in tokenized_corpus]n else:n documents = tokenized_corpusnn if max_df == -1:n max_df = round(len(documents) / 2)nn # 構建語料集統計向量n vec = CountVectorizer(min_df=min_df,n max_df=max_df,n analyzer="word",n token_pattern="[S]+",n tokenizer=None,n preprocessor=None,n stop_words=Nonen )nn # 對於數據進行分析n DTM = vec.fit_transform(documents)nn # 獲取詞表n vocab = vec.get_feature_names()nn return (vocab, DTM)n

我們也可以對分詞之後的文檔進行主題模型或者詞向量提取,這裡使用分詞之後的文件就可以忽略中英文的差異:

def topics_by_lda(self, tokenized_corpus_path, num_topics=20, num_words=10, max_lines=10000, split="s+", max_df=100):n """n 讀入經過分詞的文件並且對其進行 LDA 訓練n Arguments:n tokenized_corpus_path -> string -- 經過分詞的語料集地址n num_topics -> integer -- 主題數目n num_words -> integer -- 主題詞數目n max_lines -> integer -- 每次讀入的最大行數n split -> string -- 文檔的詞之間的分隔符n max_df -> integer -- 避免常用詞,過濾超過該閾值的詞n """nn # 存放所有語料集信息n corpus = []nn with open(tokenized_corpus_path, r, encoding=utf-8) as tokenized_corpus:nn flag = 0nn for document in tokenized_corpus:nn # 判斷是否讀取了足夠的行數n if(flag > max_lines):n breaknn # 將讀取到的內容添加到語料集中n corpus.append(re.split(split, document))nn flag = flag + 1nn # 構建語料集的 BOW 表示n (vocab, DTM) = self.corpus2dtm(corpus, max_df=max_df)nn # 訓練 LDA 模型nn lda = LdaMulticore(n matutils.Sparse2Corpus(DTM, documents_columns=False),n num_topics=num_topics,n id2word=dict([(i, s) for i, s in enumerate(vocab)]),n workers=4n )nn # 列印並且返回主題數據n topics = lda.show_topics(n num_topics=num_topics,n num_words=num_words,n formatted=False,n log=False)nn for ti, topic in enumerate(topics):n print("Topic", ti, ":", " ".join(word[0] for word in topic[1]))n

該函數同樣可以使用命令行直接調用,傳入分詞之後的文件。我們也可以對其語料集建立詞向量,代碼參考這裡;如果對於詞向量基本使用尚不熟悉的同學可以參考基於 Gensim 的 Word2Vec 實踐:

def wv_train(self, tokenized_text_path, output_model_path=./wv_model.bin):n """n 對於文本進行詞向量訓練,並將輸出的詞向量保存n """nn sentences = word2vec.Text8Corpus(tokenized_text_path)nn # 進行模型訓練n model = word2vec.Word2Vec(sentences, size=250)nn # 保存模型n model.save(output_model_path)nn def wv_visualize(self, model_path, word=["中國", "航空"]):n """n 根據輸入的詞搜索鄰近詞然後可視化展示n 參數:n model_path: Word2Vec 模型地址n """nn # 載入模型n model = word2vec.Word2Vec.load(model_path)nn # 尋找出最相似的多個詞n words = [wp[0] for wp in model.most_similar(word, topn=20)]nn # 提取出詞對應的詞向量n wordsInVector = [model[word] for word in words]nn # 進行 PCA 降維n pca = PCA(n_components=2)n pca.fit(wordsInVector)n X = pca.transform(wordsInVector)nn # 繪製圖形n xs = X[:, 0]n ys = X[:, 1]nn plt.figure(figsize=(12, 8))n plt.scatter(xs, ys, marker=o)nn # 遍歷所有的詞添加點注釋n for i, w in enumerate(words):n plt.annotate(n w,n xy=(xs[i], ys[i]), xytext=(6, 6),n textcoords=offset points, ha=left, va=top,n **dict(fontsize=10)n )n plt.show()n

推薦閱讀:

TAG:机器学习 | 自然语言处理 | Python |