如何使用带有 Ruby 的 Stanford CoreNLP java 库进行情绪分析?
How to use Stanford CoreNLP java library with Ruby for sentiment analysis?
我正在尝试在本地 MongoDB 实例中对 大量推文 进行 情感分析 Ruby Rails 4,Ruby 2.1.2 和 Mongoid ORM。
我在 Mashape.com 上使用了免费提供的 https://loudelement-free-natural-language-processing-service.p.mashape.com API,但是在快速推送了几百条推文后它开始超时 - 显然它不是这意味着要浏览数万条推文,这是可以理解的。
所以接下来我想 使用这里推荐的 Stanford CoreNLP 库:http://nlp.stanford.edu/sentiment/code.html
默认用法,除了使用Java 1.8代码中的库外,似乎是使用XML输入输出文件。对于我的用例,这很烦人,因为我有数以万计的短推文,而不是长文本文件。我想像使用方法一样使用 CoreNLP 并执行 tweets.each 类型的循环。
我想一种方法是用所有推文构建一个 XML 文件,然后从 Java 进程中取出一个并解析它并将其放回数据库,但是这对我来说很陌生,需要做很多工作。
所以,我很高兴在上面链接的网站上找到了一种从命令行到 运行 CoreNLP 并接受文本作为标准输入 的方法,这样我就没有' 必须开始摆弄文件系统,而是将文本作为参数提供。然而,与使用大声元素自由情绪分析相比,为每条推文单独启动 JVM 会增加巨大的开销 API.
现在,我写的代码又丑又慢,但它可以工作。不过,我想知道是否有更好的方法从 Ruby 中 运行 CoreNLP java 程序,而不必开始摆弄文件系统(创建临时文件并将它们作为参数)或编写 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
您至少可以通过使用 IO.popen
打开外部进程并与之通信来避免丑陋和危险的命令行内容,例如:
input_string = "
foo
bar
baz
"
output_string =
IO.popen("grep 'foo'", 'r+') do |pipe|
pipe.write(input_string)
pipe.close_write
pipe.read
end
puts "grep said #{output_string.strip} but not bar"
编辑:为避免在每个项目上重新加载 Java 程序的开销,您可以围绕 todo.each
循环打开管道并与这样的进程通信
inputs = ['a', 'b', 'c', 'd']
IO.popen('cat', 'r+') do |pipe|
inputs.each do |s|
pipe.write(s + "\n")
out = pipe.readline
puts "cat said '#{out.strip}'"
end
end
也就是说,如果 Java 程序支持这种行缓冲 "batch" 输入。不过,如果不是这样,修改它应该不是很难。
正如@Qualtagh 评论中所建议的,我决定使用JRuby。
我首先尝试使用Java使用MongoDB作为接口(直接从[=26=读取,用Java/CoreNLP分析并写回MongoDB),但是 MongoDB Java 驱动程序比我使用 Ruby 的 Mongoid ORM 更复杂,所以这就是为什么我觉得 JRuby 更复杂合适的。
为 Java 提供 REST 服务需要我先学习如何在 Java 中提供 REST 服务,这可能很容易,也可能不那么容易。我不想花时间弄明白。
所以我需要为 运行 我的代码做的代码是:
def analyze_tweet_with_corenlp_jruby
require 'java'
require 'vendor/CoreNLPTest2.jar' # I made this Java JAR with IntelliJ IDEA that includes both CoreNLP and my initialization class
analyzer = com.me.Analyzer.new # this is the Java class I made for running the CoreNLP analysis, it initializes the CoreNLP with the correct annotations etc.
result = analyzer.analyzeTweet(self.text) # self.text is where the text-to-be-analyzed resides
self.corenlp_sentiment = result # adds the result into this field in the MongoDB model
self.save!
return "#{result}: #{self.text}" # for debugging purposes
end
我正在尝试在本地 MongoDB 实例中对 大量推文 进行 情感分析 Ruby Rails 4,Ruby 2.1.2 和 Mongoid ORM。
我在 Mashape.com 上使用了免费提供的 https://loudelement-free-natural-language-processing-service.p.mashape.com API,但是在快速推送了几百条推文后它开始超时 - 显然它不是这意味着要浏览数万条推文,这是可以理解的。
所以接下来我想 使用这里推荐的 Stanford CoreNLP 库:http://nlp.stanford.edu/sentiment/code.html
默认用法,除了使用Java 1.8代码中的库外,似乎是使用XML输入输出文件。对于我的用例,这很烦人,因为我有数以万计的短推文,而不是长文本文件。我想像使用方法一样使用 CoreNLP 并执行 tweets.each 类型的循环。
我想一种方法是用所有推文构建一个 XML 文件,然后从 Java 进程中取出一个并解析它并将其放回数据库,但是这对我来说很陌生,需要做很多工作。
所以,我很高兴在上面链接的网站上找到了一种从命令行到 运行 CoreNLP 并接受文本作为标准输入 的方法,这样我就没有' 必须开始摆弄文件系统,而是将文本作为参数提供。然而,与使用大声元素自由情绪分析相比,为每条推文单独启动 JVM 会增加巨大的开销 API.
现在,我写的代码又丑又慢,但它可以工作。不过,我想知道是否有更好的方法从 Ruby 中 运行 CoreNLP java 程序,而不必开始摆弄文件系统(创建临时文件并将它们作为参数)或编写 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
您至少可以通过使用 IO.popen
打开外部进程并与之通信来避免丑陋和危险的命令行内容,例如:
input_string = "
foo
bar
baz
"
output_string =
IO.popen("grep 'foo'", 'r+') do |pipe|
pipe.write(input_string)
pipe.close_write
pipe.read
end
puts "grep said #{output_string.strip} but not bar"
编辑:为避免在每个项目上重新加载 Java 程序的开销,您可以围绕 todo.each
循环打开管道并与这样的进程通信
inputs = ['a', 'b', 'c', 'd']
IO.popen('cat', 'r+') do |pipe|
inputs.each do |s|
pipe.write(s + "\n")
out = pipe.readline
puts "cat said '#{out.strip}'"
end
end
也就是说,如果 Java 程序支持这种行缓冲 "batch" 输入。不过,如果不是这样,修改它应该不是很难。
正如@Qualtagh 评论中所建议的,我决定使用JRuby。
我首先尝试使用Java使用MongoDB作为接口(直接从[=26=读取,用Java/CoreNLP分析并写回MongoDB),但是 MongoDB Java 驱动程序比我使用 Ruby 的 Mongoid ORM 更复杂,所以这就是为什么我觉得 JRuby 更复杂合适的。
为 Java 提供 REST 服务需要我先学习如何在 Java 中提供 REST 服务,这可能很容易,也可能不那么容易。我不想花时间弄明白。
所以我需要为 运行 我的代码做的代码是:
def analyze_tweet_with_corenlp_jruby
require 'java'
require 'vendor/CoreNLPTest2.jar' # I made this Java JAR with IntelliJ IDEA that includes both CoreNLP and my initialization class
analyzer = com.me.Analyzer.new # this is the Java class I made for running the CoreNLP analysis, it initializes the CoreNLP with the correct annotations etc.
result = analyzer.analyzeTweet(self.text) # self.text is where the text-to-be-analyzed resides
self.corenlp_sentiment = result # adds the result into this field in the MongoDB model
self.save!
return "#{result}: #{self.text}" # for debugging purposes
end