NLP——自然語言處理(三)text2vec包
text2vec簡介
text2vec包是由Dmitriy Selivanov於2016年10月所寫的R包。此包主要是為文本分析和自然語言處理提供了一個簡單高效的API框架。由於其由C++所寫,同時許多部分(例如GloVe)都充分運用RcppParallel等包進行並行化操作,處理速度得到加速。並且採樣流處理器,可以不必把全部數據載入內存才進行分析,有效利用了內存,可以說該包是充分考慮了NLP處理數據量龐大的現實。 text2vec包也可以說是一個文本分析的生態系統,可以進行詞向量化操作(Vectorization)、Word2Vec的「升級版GloVe詞嵌入表達(與word2vec比較見下圖[8])、主題模型分析以及相似性度量四大方面,可以說非常的強大和實用。詳情可見[官網]( http://text2vec.org/index.html)現在就以官網給出的例子,分別來看看這個生態系統的使用吧!
一、詞向量操作——進行情感分析
1.1 基本步驟
在前一期已經對基礎文本分析做了一個簡單的小結,運用text2vec包進行文本分析也大同小易,主要分三步:
構建一個文檔-詞頻矩陣(document-term matrix,DTM)或者詞頻共現矩陣( term-co-occurrence matrix,TCM);
在DTM基礎上擬合模型,包括文本(情感)分類、主題模型、相似性度量等。並進行模型的調試和驗證;
最終在新的數據上運用擬合好的模型。
1.2 情感分析Demo
以text2vec包提供的影評數據為例,對5000條電影評論進行情感分析(評論正面VS.負面)。首先載入text2vec包,並運用data.table包進行數據讀取。
#install.packages("text2vec")library(text2vec)library(data.table)
數據準備
首先運用Setkey為數據設置唯一的「主鍵」,並劃分為訓練集和測試集。
data("movie_review") setDT(movie_review) setkey(movie_review, id) set.seed(2016L) all_ids = movie_review$id train_ids = sample(all_ids, 4000) test_ids = setdiff(all_ids, train_ids) train = movie_review[J(train_ids)] test = movie_review[J(test_ids)]
文檔向量化
文檔向量化是text2vec的主要步驟,創建詞表(vocabulary)前需要設置itoken分詞迭代器,然後用create_vocabulary創建詞表,形成語料文件,構建DTM矩陣。
prep_fun = tolower #代表詞語劃分到什麼程度tok_fun = word_tokenizer #步驟1.設置分詞迭代器it_train = itoken(train$review, preprocessor = prep_fun, tokenizer = tok_fun, ids = train$id, progressbar = FALSE)#步驟2.分詞#消除停用詞stop_words = c("i", "me", "my", "myself", "we", "our", "ours", "ourselves", "you", "your", "yours") #分詞函數vocab = create_vocabulary(it_train, stopwords = stop_words) #對低頻詞的修建pruned_vocab = prune_vocabulary(vocab, term_count_min = 10, #詞頻,低於10個都刪掉 doc_proportion_max = 0.5, doc_proportion_min = 0.001) #步驟3.設置形成語料文件vectorizer = vocab_vectorizer(pruned_vocab)#進行hash化,提效降內存#2-ngrams增加文字信息量#h_vectorizer = hash_vectorizer(hash_size = 2 ^ 14, ngram = c(1L, 2L)) #步驟4.構建DTM矩陣dtm_train = create_dtm(it_train, vectorizer)#=========================================#優化方法#標準化,加入懲罰項#dtm_train_l1_norm = normalize(dtm_train, "l1")#轉為TFIDF步驟#1.設置TFIDF編譯器#tfidf = TfIdf$new() #2.轉換成TFIDF格式#dtm_train_tfidf = fit_transform(dtm_train, tfidf)#dtm_test_tfidf = create_dtm(it_test, vectorizer) %>% # transform(tfidf)#或者寫為#dtm_test_tfidf = create_dtm(it_test, vectorizer) %>% # transform(tfidf)
基於Logistic的情感標註
運用glmnet包中的binomial函數族進行Logistic的情感標註。並設置alpha=1懲罰項進行L1懲罰。(不懂的同志出門左轉,在公眾號歷史文章「正則化及其R實現」查看)。
library(glmnet) NFOLDS = 4 glmnet_classifier = cv.glmnet(x = dtm_train, y = train[[sentiment]], family = binomial, # L1 penalty alpha = 1, # interested in the area under ROC curve type.measure = "auc", # 5-fold cross-validation nfolds = NFOLDS, # high value is less accurate, but has faster training thresh = 1e-3, # again lower number of iterations for faster training maxit = 1e3) plot(glmnet_classifier)
驗證集效果
最後驗證測試集效果,AUC為0.9185.除了以上的基礎分析外,還可以對數據進行標準化、TF-IDF或hash化來提高執行的效率和模型準確性。
it_test = test$review %>% prep_fun %>% tok_fun %>% itoken(ids = test$id, # turn off progressbar because it wont look nice in rmd progressbar = FALSE) dtm_test = create_dtm(it_test, vectorizer) preds = predict(glmnet_classifier, dtm_test, type = response)[,1] glmnet:::auc(test$sentiment, preds)
## [1] 0.9185611
二、Glove詞嵌入
在Tomas Mikolov等提出word2vec後,關於詞向量表示的文獻就層出不窮。斯坦福大學提出GloVe: [Global Vectors for Word Representation](http://nlp.stanford.edu/projects/glove/),主要是在詞語共現矩陣下因式分解。經過代碼優化GloVe性能提高了2-3倍,是通過單精度浮點運算[4]。 現在我們就通過text2vec實現一個word2vec。
讀取數據
給定一個語言規則的例子:「paris」之於 「france」 等於 「germany」 之於——,我們期望這個結果為「berlin」。我們以Wikipedia數據為語料進行demo。
text8_file = "./text8"if (!file.exists(text8_file)) { download.file("http://mattmahoney.net/dc/text8.zip", "./text8.zip") unzip ("./text8.zip", files = "text8", exdir = "./")}wiki = readLines(text8_file, n = 1, warn = FALSE)
創建辭彙表
通過首先tokens迭代,運用流處理API節省內存使用(運用text2vec操作raw數據的首要動作)。並設置prune_vocabulary參數進行清洗最小詞頻。最終留下71290個terms。創建共現矩陣tcm。
# Create iterator over tokenstokens <- space_tokenizer(wiki)# Create vocabulary. Terms will be unigrams (simple words).it = itoken(tokens, progressbar = FALSE)vocab <- create_vocabulary(it)vocab <- prune_vocabulary(vocab, term_count_min = 5L)# Use our filtered vocabularyvectorizer <- vocab_vectorizer(vocab, # dont vectorize input grow_dtm = FALSE, # use window of 5 for context words skip_grams_window = 5L)tcm <- create_tcm(it, vectorizer)
運用GloVe對TCM進行因子分解
text2vec使用GloVe演算法進行並行化隨機梯度下降,默認情況下將使用計算機的所有核並行運算,當然也可以指定threads。
#RcppParallel::setThreadOptions(numThreads = 4)glove = GlobalVectors$new(word_vectors_size = 50, vocabulary = vocab, x_max = 10)glove$fit(tcm, n_iter = 20)
注意:text2vec為S6類型對象,因此可以用fit或fit_transform進行S3操作。
#glove = GlobalVectors$new(word_vectors_size = 50, vocabulary = vocab, x_max = 10)# `glove` object will be modified by `fit()` call !fit(tcm, glove, n_iter = 20)#now we get the word vectors:word_vectors <- glove$get_word_vectors()
查找最近的詞向量:paris - france + germany
可以看到概率最大的詞向量為「berlin」。
berlin <- word_vectors["paris", , drop = FALSE] - word_vectors["france", , drop = FALSE] + word_vectors["germany", , drop = FALSE]cos_sim = sim2(x = word_vectors, y = berlin, method = "cosine", norm = "l2")head(sort(cos_sim[,1], decreasing = TRUE), 5)# berlin paris munich leipzig germany # 0.8015347 0.7623165 0.7013252 0.6616945 0.6540700
三、主題模型LDA(Latent Dirichlet Allocation)
構建dtm與前面步驟「詞向量操作」一致,後面運用LDA函數構建主題模型。
tokens = movie_review$review %>% tolower %>% word_tokenizer# turn off progressbar because it wont look nice in rmdit = itoken(tokens, ids = movie_review$id, progressbar = FALSE)v = create_vocabulary(it) %>% prune_vocabulary(term_count_min = 10, doc_proportion_max = 0.2)vectorizer = vocab_vectorizer(v)dtm = create_dtm(it, vectorizer, type = "lda_c")#前面步驟與詞向量操作一致,後面運用LDA函數構建主題模型lda_model = LDA$new(n_topics = 10, vocabulary = v, doc_topic_prior = 0.1, topic_word_prior = 0.01)doc_topic_distr = lda_model$fit_transform(dtm, n_iter = 1000, convergence_tol = 0.01, check_convergence_every_n = 10)
四、相似性度量
text2vec提供了2套函數集測量變數距離/相似性。他們是:
sim2(x, y, method):分別計算x*y個相似性;
psim2(x, x, method):平行地求數據的相似性,x個相似性;
dist2(x, y, method):跟sim2相反,分別計算x*y個距離;
4. pdist2(x, x, method),平行地求數據的距離,x個距離。
注意到的是,sim2與psim2一個是生成了x*y個數值,一個是生成了x個數值,區別顯而易見[5]。主要有4種距離的度量方法:Jaccard距離、Cosine距離、Euclidean距離和RWMD(Relaxed Word Mover』s Distance).
舉例
還是拿影評數據為例,計算文檔間的相似性。
library(stringr)library(text2vec)data("movie_review")# select 500 rows for faster running timesmovie_review = movie_review[1:500, ]prep_fun = function(x) { x %>% # make text lower case str_to_lower %>% # remove non-alphanumeric symbols str_replace_all("[^[:alnum:]]", " ") %>% # collapse multiple spaces str_replace_all("\s+", " ")}movie_review$review_clean = prep_fun(movie_review$review)
創建兩個文檔集,計算兩者相似性
doc_set_1 = movie_review[1:300, ]it1 = itoken(doc_set_1$review_clean, progressbar = FALSE)# specially take different number of docs in second setdoc_set_2 = movie_review[301:500, ]it2 = itoken(doc_set_2$review_clean, progressbar = FALSE)
由於需要在同一個向量空間比較文檔的相似性,因此需要定義一個相同的空間和項目文檔集。
it = itoken(movie_review$review_clean, progressbar = FALSE)v = create_vocabulary(it) %>% prune_vocabulary(doc_proportion_max = 0.1, term_count_min = 5)vectorizer = vocab_vectorizer(v)
4.1 Jaccard similarity
# they will be in the same space because we use same vectorizer# hash_vectorizer will also work finedtm1 = create_dtm(it1, vectorizer)dim(dtm1)dtm2 = create_dtm(it2, vectorizer)dim(dtm2)d1_d2_jac_sim = sim2(dtm1, dtm2, method = "jaccard", norm = "none")
4.2 Cosine similarity
d1_d2_cos_sim = sim2(dtm1, dtm2, method = "cosine", norm = "l2")
4.3 Euclidean distance
x = dtm_tfidf_lsa[1:300, ]y = dtm_tfidf_lsa[1:200, ]m1 = dist2(x, y, method = "euclidean")
4.4 RWMD
data("movie_review") tokens = movie_review$review %>% tolower %>% word_tokenizer v = create_vocabulary(itoken(tokens)) %>% prune_vocabulary(term_count_min = 5, doc_proportion_max = 0.5) corpus = create_corpus(itoken(tokens), vocab_vectorizer(v, skip_grams_window = 5)) dtm = get_dtm(corpus) tcm = get_tcm(corpus) glove_model = GloVe$new(word_vectors_size = 50, vocabulary = v, x_max = 10)wv = glove_model$fit(tcm, n_iter = 10) rwmd_model = RWMD(wv) rwmd_dist = dist2(dtm[1:10, ], dtm[1:100, ], method = rwmd_model, norm = none)
參考文獻
[1] Deep Learning 實戰之word2vec http://wenku.baidu.com/link?url=GIaePrya8VtcNJIFrC91LNqlekzsX07K8dA-kRppUITgF5eyofvvz2wgmZ32DTJKa3HY-78s0Gk64Z7GlQklXvI-UEdhkWj6IIzgU6Gmr7q
[2]斯坦福大學深度學習與自然語言處理第二講——詞向量
[3]R+NLP:text2vec包——New 文本分析生態系統 No.1(一,簡介)http://blog.csdn.net/sinat_26917383/article/details/53161863
[4]R+NLP︱text2vec包——BOW詞袋模型做監督式情感標註案例(二,情感標註)http://blog.csdn.net/sinat_26917383/article/details/53260117
[5]R+NLP︱text2vec包——四類文本挖掘相似性指標 RWMD、cosine、Jaccard 、Euclidean (三,相似距離)http://blog.csdn.net/sinat_26917383/article/details/53286009
[6]text2vec http://text2vec.org/glove.html
[7]GloVe: Global Vectors for Word Representationhttp://nlp.stanford.edu/projects/glove/
[8]GloVe vs word2vec revisitedhttp://dsnotes.com/post/glove-enwiki/
[9]text2vec package help
要想獲取分析代碼,可查看原文,進入本人的GitHubhttps://github.com/Alven8816查看下載,或通過本人郵箱yuwenhuajiayou@sina.cn與本人聯繫
」樂享數據「個人公眾號,不代表任何團體利益,亦無任何商業目的。任何形式的轉載、演繹必須經過公眾號聯繫原作者獲得授權,保留一切權力。歡迎關注「樂享數據」。
推薦閱讀: