Я пытаюсь выполнить анализ настроений для большого набора твитов в локальном экземпляре MongoDB с Ruby on Rails 4, Ruby 2.1.2 и Mongoid ORM.
Я использовал бесплатно доступный https://loudelement-free-natural-language-processing-service.p.mashape.com API на Mashape.com, однако время ожидания истекает после быстрой отправки нескольких сотен твитов — очевидно, что оно не предназначено для обработки десятков тысячи твитов и это понятно.
Затем я решил использовать библиотеку Stanford CoreNLP, представленную здесь: http://nlp.stanford.edu/sentiment/code.html
Использование по умолчанию, в дополнение к использованию библиотеки в коде Java 1.8, по-видимому, заключается в использовании входных и выходных файлов XML. В моем случае это раздражает, учитывая, что у меня десятки тысяч коротких твитов, а не длинных текстовых файлов. Я хотел бы использовать CoreNLP как метод и выполнять цикл типа tweets.each.
Я предполагаю, что одним из способов было бы создать файл XML со всеми твитами, а затем получить один из процесса Java, проанализировать его и вернуть обратно в БД, но это кажется мне чуждым и потребует много работы.
Итак, я был счастлив найти на сайте, указанном выше, способ запустить CoreNLP из командной строки и принять текст как стандартный ввод, чтобы мне не пришлось возиться с файловой системой, а скорее передать текст в качестве параметра. Однако запуск JVM отдельно для каждого твита добавляет огромные накладные расходы по сравнению с использованием API анализа тональности без Loudelement.
Код, который я написал, уродлив и медленен, но он работает. Тем не менее, мне интересно, есть ли лучший способ запустить java-программу CoreNLP из Ruby без необходимости возиться с файловой системой (создавать временные файлы и указывать их в качестве параметров) или писать код Java?
Вот код, который я использую:
def self.mass_analyze_w_corenlp # batch run the method in multiple Ruby processes
todo = Tweet.all.exists(corenlp_sentiment: false).limit(5000).sort(follow_ratio: -1) # start with the "least spammy" tweets based on follow ratio
counter = 0
todo.each do |tweet|
counter = counter+1
fork {tweet.analyze_sentiment_w_corenlp} # run the analysis in a separate Ruby process
if counter >= 5 # when five concurrent processes are running, wait until they finish to preserve memory
Process.waitall
counter = 0
end
end
end
def analyze_sentiment_w_corenlp # run the sentiment analysis for each tweet object
text_to_be_analyzed = self.text.gsub("'"){" "}.gsub('"'){' '} # fetch the text field of DB item strip quotes that confuse the command line
start = "echo '"
finish = "' | java -cp 'vendor/corenlp/*' -mx250m edu.stanford.nlp.sentiment.SentimentPipeline -stdin"
command_string = start+text_to_be_analyzed+finish # assemble the command for the command line usage below
output =`#{command_string}` # run the CoreNLP on the command line, equivalent to system('...')
to_db = output.gsub(/\s+/, "").downcase # since CoreNLP uses indentation, remove unnecessary whitespace
# output is in the format of "neutral, "positive", "negative" and so on
puts "Sentiment analysis successful, sentiment is: #{to_db} for tweet #{text_to_be_analyzed}."
self.corenlp_sentiment = to_db # insert result as a field to the object
self.save! # sentiment analysis done!
end