Kaggle:テキストの調性を決定する

こんにちは、Habr!







#{初心者向けのデータサイエンス}



私の名前はグレブ・モロゾフです。以前の記事ですでにおなじみです。 人気のある需要によって、私はMLClass.ruの教育プロジェクトへの参加の経験を説明し続けます (ところで、まだ時間がありません-まだ利用できる間に資料ダウンロードすることをお勧めします)。



データ



作品のデータは、Kaggleウェブサイトで開催されたBag of Wordsコンテストの一部として提供され、IMBDウェブサイトからの25,000件のレビューのトレーニングサンプルであり、それぞれがネガティブ/ポジティブのいずれかのクラスに割り当てられました。 タスクは、テストセットの各レビューがどのクラスに適用されるかを予測することです。



library(magrittr) library(tm) require(plyr) require(dplyr) library(ggplot2) library(randomForest)
      
      





データをRAMにロードします。



 data_train <- read.delim("labeledTrainData.tsv",header = TRUE, sep = "\t", quote = "", stringsAsFactors = F)
      
      





結果のテーブルは、id、sentiment、およびreviewの3つの列で構成されます。 作業の対象となるのは最後の列です。 レビュー自体が何であるかを見てみましょう。 (レビューは十分に長いため、最初の700文字のみを提供します)



 paste(substr(data_train[1,3],1,700),"...") ## [1] "\"With all this stuff going down at the moment with MJ i've started listening to his music, watching the odd documentary here and there, watched The Wiz and watched Moonwalker again. Maybe i just want to get a certain insight into this guy who i thought was really cool in the eighties just to maybe make up my mind whether he is guilty or innocent. Moonwalker is part biography, part feature film which i remember going to see at the cinema when it was originally released. Some of it has subtle messages about MJ's feeling towards the press and also the obvious message of drugs are bad m'kay.<br /><br />Visually impressive but of course this is all about Michael Jackson so unless you remotely lik ..."
      
      





HTMLタグ形式のゴミがテキストに存在することがわかります。



言葉の袋



Bag of WordsまたはBag of Wordsは、ワードプロセッシングでよく使用されるモデルです。これは、処理されたテキストに含まれる順序付けられていない一連のワードです。 多くの場合、モデルは、行が単一のテキストに対応し、列がそれに含まれる単語であるマトリックスの形式で表示されます。 交差点のセルは、対応するドキュメント内の単語の出現回数です。 このモデルは、言葉の人間の言語をコンピューターに優しい数字の言語に翻訳するという点で便利です。



データ処理



データ処理には、tmパッケージの機能を使用します。 次のコードブロックでは、次のアクションが実行されます。







 train_corpus <- data_train$review %>% VectorSource(.)%>% Corpus(.) %>% tm_map(., tolower) %>% tm_map(., PlainTextDocument) %>% tm_map(., removePunctuation) %>% tm_map(., removeWords, c("movie", stopwords("english"))) %>% tm_map(., stemDocument)
      
      





次に、周波数行列を作成します。



 frequencies <- DocumentTermMatrix(train_corpus) frequencies ## <<DocumentTermMatrix (documents: 25000, terms: 92244)>> ## Non-/sparse entries: 2387851/2303712149 ## Sparsity : 100% ## Maximal term length: 64 ## Weighting : term frequency (tf)
      
      





マトリックスには90,000を超える用語が含まれています。 それに基づくモデルには90,000の機能が含まれます! 減らす必要があります。このため、レビューではめったに見られない単語がたくさんあるという事実を使用しています。 放電されます(用語スパース)。 (トレーニングセットに25,000個のオブジェクトがあるモデルがRAMに収まるように)大幅に削減し、少なくとも5%のレビューで見つかった単語のみを残すことにしました。



 sparse <- removeSparseTerms(frequencies, 0.95) sparse ## <<DocumentTermMatrix (documents: 25000, terms: 373)>> ## Non-/sparse entries: 1046871/8278129 ## Sparsity : 89% ## Maximal term length: 10 ## Weighting : term frequency (tf)
      
      





その結果、373個の用語がマトリックスに残りました。 マトリックスをデータフレームに変換し、ターゲット属性を持つ列を追加します。



 reviewSparse = as.data.frame(as.matrix(sparse)) vocab <- names(reviewSparse) reviewSparse$sentiment <- data_train$sentiment %>% as.factor(.) %>% revalue(., c("0"="neg", "1" = "pos")) row.names(reviewSparse) <- NULL
      
      





次に、受信したデータでランダムフォレストモデルをトレーニングします。 RAMの制限により、100本のツリーを使用しています。



 model_rf <- randomForest(sentiment ~ ., data = reviewSparse, ntree = 100)
      
      





訓練されたモデルを使用して、テストデータの予測を作成します。



 data_test <- read.delim("testData.tsv", header = TRUE, sep = "\t", quote = "", stringsAsFactors = F) test_corpus <- data_test$review %>% VectorSource(.)%>% Corpus(.) %>% tm_map(., tolower) %>% tm_map(., PlainTextDocument) %>% tm_map(., removePunctuation) %>% tm_map(., removeWords, c("movie", stopwords("english"))) %>% tm_map(., stemDocument) test_frequencies <- DocumentTermMatrix(test_corpus,control=list(dictionary = vocab)) reviewSparse_test <- as.data.frame(as.matrix(test_frequencies)) row.names(reviewSparse_test) <- NULL sentiment_test <- predict(model_rf, newdata = reviewSparse_test) pred_test <- as.data.frame(cbind(data_test$id, sentiment_test)) colnames(pred_test) <- c("id", "sentiment") pred_test$sentiment %<>% revalue(., c("1"="0", "2" = "1")) write.csv(pred_test, file="Submission.csv", quote=FALSE, row.names=FALSE)
      
      





Kaggle Webサイトでダウンロードして評価した後、モデルは0.73184のAUC統計に基づいた推定値を受け取りました。



反対側から問題にアプローチしてみましょう。 頻度行列をコンパイルして切り捨てるとき、最も頻繁に発生する単語を残しますが、映画レビューでよく見られるがレビューの雰囲気を反映していない多くの単語を残している可能性が高いです。 たとえば、映画、映画などの言葉。 しかし、なぜなら レビューの印象的な気分を備えたトレーニングサンプルがあり、否定的なレビューと肯定的なレビューで頻度が大幅に異なる単語を区別できます。



まず、否定的なレビューの頻度マトリックスを作成します。



 freq_neg <- data_train %>% filter(sentiment == 0) %>% select(review) %>% VectorSource(.)%>% Corpus(.) %>% tm_map(., tolower) %>% tm_map(., PlainTextDocument) %>% tm_map(., removePunctuation) %>% tm_map(., removeNumbers) %>% tm_map(., removeWords, c(stopwords("english"))) %>% tm_map(., stemDocument) %>% DocumentTermMatrix(.) %>% removeSparseTerms(., 0.999) %>% as.matrix(.) freq_df_neg <- colSums(freq_neg) freq_df_neg <- data.frame(word = names(freq_df_neg), freq = freq_df_neg) rownames(freq_df_neg) <- NULL head(arrange(freq_df_neg, desc(freq))) ## word freq ## 1 movi 27800 ## 2 film 21900 ## 3 one 12959 ## 4 like 12001 ## 5 just 10539 ## 6 make 7846
      
      





そして肯定的なレビューのために。



 freq_pos <- data_train %>% filter(sentiment == 1) %>% select(review) %>% VectorSource(.)%>% Corpus(.) %>% tm_map(., tolower) %>% tm_map(., PlainTextDocument) %>% tm_map(., removePunctuation) %>% tm_map(., removeNumbers) %>% tm_map(., removeWords, c(stopwords("english"))) %>% tm_map(., stemDocument) %>% DocumentTermMatrix(.) %>% removeSparseTerms(., 0.999) %>% as.matrix(.) freq_df_pos <- colSums(freq_pos) freq_df_pos <- data.frame(word = names(freq_df_pos), freq = freq_df_pos) rownames(freq_df_pos) <- NULL head(arrange(freq_df_pos, desc(freq))) ## word freq ## 1 film 24398 ## 2 movi 21796 ## 3 one 13706 ## 4 like 10138 ## 5 time 7889 ## 6 good 7508
      
      





結果のテーブルを組み合わせて、周波数間の差を計算します。



 freq_all <- merge(freq_df_neg, freq_df_pos, by = "word", all = T) freq_all$freq.x[is.na(freq_all$freq.x)] <- 0 freq_all$freq.y[is.na(freq_all$freq.y)] <- 0 freq_all$diff <- abs(freq_all$freq.x - freq_all$freq.y) head(arrange(freq_all, desc(diff))) ## word freq.x freq.y diff ## 1 movi 27800 21796 6004 ## 2 bad 7660 1931 5729 ## 3 great 2692 6459 3767 ## 4 just 10539 7109 3430 ## 5 love 2767 5988 3221 ## 6 even 7707 5056 2651
      
      





いいね! 予想通り、最も大きな違いがある言葉の中に、 badgreatloveなどの用語があります 。 しかし、ここにも映画のような一般的な言葉があります。 頻繁に起こる言葉では、わずかなパーセンテージの差でさえ、高い絶対差が生じることが起こりました。 この省略をなくすために、差を周波数の合計で割ることにより差を正規化します。 結果のメトリックは0〜1の間隔にあり、その値が高いほど、この値は肯定的レビューと否定的レビューの違いを判断する上でより重要になります。 しかし、1つのクラスのレビューでしか見られず、同時にその頻度が少ない単語をどうすればよいでしょうか? それらの重要性を減らすには、分母に係数を追加します。



 freq_all$diff_norm <- abs(freq_all$freq.x - freq_all$freq.y)/ (freq_all$freq.x +freq_all$freq.y + 300) head(arrange(freq_all, desc(diff_norm))) ## word freq.x freq.y diff diff_norm ## 1 worst 2436 246 2190 0.7344064 ## 2 wast 1996 192 1804 0.7250804 ## 3 horribl 1189 194 995 0.5912062 ## 4 stupid 1525 293 1232 0.5816808 ## 5 bad 7660 1931 5729 0.5792134 ## 6 wors 1183 207 976 0.5775148
      
      





差係数のインデックスが最も高い500ワードを選択します。



 freq_word <- arrange(freq_all, desc(diff_norm)) %>% select(word) %>% slice(1:500)
      
      





結果の辞書を使用して周波数行列を作成し、その上でランダムフォレストモデルをトレーニングします。



 vocab <- as.character(freq_word$word) frequencies = DocumentTermMatrix(train_corpus,control=list(dictionary = vocab)) reviewSparse_train <- as.data.frame(as.matrix(frequencies)) row.names(reviewSparse_train) <- NULL reviewSparse_train$sentiment <- data_train$sentiment %>% as.factor(.) %>% revalue(., c("0"="neg", "1" = "pos")) model_rf <- randomForest(sentiment ~ ., data = reviewSparse_train, ntree = 100)
      
      





Kaggleウェブサイトでダウンロードして評価した後、モデルは0.83120のAUC統計に基づく推定値を受け取りました。 サインを操作した後、統計が10%改善されました!



TF-IDF



文書の用語マトリックスを作成するとき、単語の重要性の指標として、レビューで単語の頻度を使用しました。 tmパッケージには、 tf-idfと呼ばれる別のメジャーを使用する機能があります。 TF-IDF (英語版TF-用語頻度、IDF-逆文書頻度 )は、文書またはコーパスのコレクションの一部である文書のコンテキストで単語の重要性を評価するために使用される統計メトリックです。 単語の重みは、ドキュメントでのこの単語の使用量に比例し、コレクションの他のドキュメントでの単語の使用頻度に反比例します。



tf-idfを使用して、最高のメトリックを持つ500個の用語の辞書を作成します。 この辞書が単語の重要性を最も密接に反映するために、レビューの雰囲気がマークされていない追加のトレーニングセットを使用します。 結果の辞書に基づいて、ドキュメント用語マトリックスを作成し、モデルをトレーニングします。



 data_train_un <- read.delim("unlabeledTrainData.tsv",header = TRUE, sep = "\t", quote = "", stringsAsFactors = F) train_review <- c(data_train$review, data_train_un$review) train_corpus <- train_review %>% VectorSource(.)%>% Corpus(.) %>% tm_map(., tolower) %>% tm_map(., PlainTextDocument) %>% tm_map(., removePunctuation) %>% tm_map(., removeNumbers) %>% tm_map(., removeWords, c(stopwords("english"))) %>% tm_map(., stemDocument) tdm <- TermDocumentMatrix(train_corpus, control = list(weighting = function(x) weightTfIdf(x, normalize = F))) library(slam) freq <- rollup(tdm, 2,FUN = sum) freq <- as.matrix(freq) freq_df <- data.frame(word = row.names(freq), tfidf = freq) names(freq_df) <- c("word", "tf_idf") row.names(freq_df) <- NULL freq_df %<>% arrange(desc(tf_idf)) vocab <- as.character(freq_df$word)[1:500] train_corpus <- data_train$review %>% VectorSource(.)%>% Corpus(.) %>% tm_map(., tolower) %>% tm_map(., PlainTextDocument) %>% tm_map(., removePunctuation) %>% tm_map(., removeNumbers) %>% tm_map(., removeWords, c(stopwords("english"))) %>% tm_map(., stemDocument) frequencies = DocumentTermMatrix(train_corpus,control=list(dictionary = vocab, weighting = function(x) weightTfIdf(x, normalize = F) )) reviewSparse_train <- as.data.frame(as.matrix(frequencies)) rm(data_train_un, tdm, dtm, train_review) reviewSparse_train <- as.data.frame(as.matrix(frequencies)) row.names(reviewSparse_train) <- NULL colnames(reviewSparse_train) = make.names(colnames(reviewSparse_train)) reviewSparse_train$sentiment <- data_train$sentiment %>% as.factor(.) %>% revalue(., c("0"="neg", "1" = "pos")) rm(data_train, train_corpus, freq, freq_df) model_rf <- randomForest(sentiment ~ ., data = reviewSparse_train, ntree = 100)
      
      







このモデルをテストサンプルで使用し、 AUC0.81584を取得します。



おわりに



この作業は、テキストデータに基づいて予測モデルを作成するための可能なオプションの1つです。 モデルの品質を改善するオプションの1つは、ドキュメント-用語マトリックスから使用される用語の数を増やすことですが、この方法では、使用されるマシンリソースを大幅に増やす必要があります。 また、単語の頻度ではなく、単語の意味と単語間の関係を参照して、はるかに良い結果を得ることができます。 これを行うには、 word2vecモデルを使用します。 さらに、研究の大きな分野は、ドキュメントの文脈における用語の検討です



All Articles