こんにちは、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),"...")
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)))
いいね! 予想通り、最も大きな違いがある言葉の中に、
bad 、
great 、
loveなどの用語があり
ます 。 しかし、ここにも
映画のような一般的な言葉があります。 頻繁に起こる言葉では、わずかなパーセンテージの差でさえ、高い絶対差が生じることが起こりました。 この省略をなくすために、差を周波数の合計で割ることにより差を正規化します。 結果のメトリックは
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)))
差係数のインデックスが最も高い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)
このモデルをテストサンプルで使用し、
AUC値
0.81584を取得します。
おわりに
この作業は、テキストデータに基づいて予測モデルを作成するための可能なオプションの1つです。 モデルの品質を改善するオプションの1つは、ドキュメント-用語マトリックスから使用される用語の数を増やすことですが、この方法では、使用されるマシンリソースを大幅に増やす必要があります。 また、単語の頻度ではなく、単語の意味と単語間の関係を参照して、はるかに良い結果を得ることができます。 これを行うには、
word2vecモデルを使用します。 さらに、研究の大きな分野は、ドキュメントの文脈における用語の検討です