Java 数据科学秘籍(三)
六、从文本数据中检索信息
在本章中,我们将介绍以下配方:
- 使用 Java 检测标记(单词)
- 使用 Java 检测句子
- 使用 OpenNLP 检测单词和句子
- 使用 Stanford CoreNLP 从标记中检索词条和词性并识别命名实体
- 使用 Java 8 用余弦相似度度量文本相似度
- 使用 Mallet 从文本文档中提取主题
- 使用 Mallet 分类文本文档
- 使用 Weka 对文本文档进行分类
简介
由于 web 数据的可用性,而且大部分都是文本格式,数据科学家现在处理最多的数据类型就是文本。可以从文档、文章、博客文章、社交媒体更新、新闻专线以及任何你能想到的地方检索到文本数据的许多方面。
许多基于 Java 的工具可供数据科学家从文本数据中检索信息。此外,还有一些工具可以完成各种数据科学任务。在本章中,我们将范围限制在几个数据科学任务上,如句子和单词等琐碎文本特征提取、使用机器学习的文档分类、主题提取和建模、从文档中提取关键字以及命名实体识别。
使用 Java 检测令牌(单词)
数据科学家需要使用文本数据完成的最常见任务之一是检测其中的标记。这个任务叫做标记化 *。*虽然“记号”可以指单词、符号、短语或任何其他有意义的文本单元,但在本章中,我们将把单词视为记号,因为单词是一个合理的文本单元。然而,单词标记的概念因人而异;有些人只需要单词,有些人希望在检测过程中省略符号,而有些人希望在单词中保留标点符号以获得更多的上下文信息。根据不同的需求,在这个配方中,我们将使用三种不同的技术,当应用于相同的字符串时,会产生三种不同的结果。这些技术将涉及字符串标记化、中断迭代器和正则表达式。您需要决定使用哪种技术。
请记住,我们只选择性地选择了三种方法,尽管还有许多其他的选择;它们是供你探索的。
准备就绪
- 转到https://docs . Oracle . com/javase/7/docs/API/Java/util/regex/pattern . html,浏览关于
Pattern类支持的正则表达式模式的文档。 - 进入https://docs . Oracle . com/javase/7/docs/API/Java/text/break iterator . html查看示例。这将让你对 break 迭代器的用法有所了解。
怎么做...
-
首先,我们将创建一个使用 Java 的
StringTokenzier类来检测令牌的方法。这个方法将获取输入的句子,并使用这个类对句子进行标记。最后,该方法将打印令牌:public void useTokenizer(String input){ -
通过将输入句子作为参数:
StringTokenizer tokenizer = new StringTokenizer(input);来调用
StringTokenizer构造函数 -
创建一个字符串对象来保存令牌:
String word =""; -
遍历分词器,获取每个单词,并在控制台上打印出来:
while(tokenizer.hasMoreTokens()){ word = tokenizer.nextToken(); System.out.println(word); } -
Close the method:
}这种方法的输出对于一个类似让我们来看看这个相对于 *的句子,他说,“*这些男生的分数真的那么好吗?**如下:
***"Let's get this vis-a-vis", he said, "these boys' marks are really that well?"*** -
*Second, we will create a method that will use Java's
BreakIteratorclass to iterate through each word in a text. You will see that the code is slightly more complex than the first method we have created in this recipe.该方法将获得输入句子作为其参数:
public void useBreakIterator(String input){ -
*然后,使用
BreakIterator类创建一个tokenizer:BreakIterator tokenizer = BreakIterator.getWordInstance(); -
*将
tokenizer应用于输入句子:tokenizer.setText(input); -
*获取
tokenizer:int start = tokenizer.first();的起始索引*
-
*使用 for 循环将每个令牌作为字符串,并在控制台上打印出来,如下所示:
```java
for (int end = tokenizer.next();
end != BreakIterator.DONE;
start = end, end = tokenizer.next()) {
System.out.println(input.substring(start,end));
}
```
11. *Close the method:
```java
}
```
这种方法的输出对于类似*“让我们来看看这个”的句子,他说,“这些男孩的分数真的那么好吗?*将如下:
```java
"
Let's
get
this
vis-a-vis
"
,
he
said
,
"
these
boys
'
marks
are
really
that
well
?
"
```
12. *最后,我们将创建一个使用正则表达式对输入文本进行标记的方法:
```java
public void useRegEx(String input){
```
13. *使用带有正则表达式的模式,该模式可以捕获标点符号、单个或多个连字符、引号、末尾的撇号等等。如果你需要一些特定的模式,只需在下面一行使用你自己的正则表达式:
```java
Pattern pattern = Pattern.compile("\\w[\\w-]+('\\w*)?");
```
14. *在pattern :
```java
Matcher matcher = pattern.matcher(input);
```
上施加一个`matcher`*
15. *使用matcher从输入文本中检索所有单词:
```java
while ( matcher.find() ) {
System.out.println(input.substring(matcher.start(),
matcher.end()));
}
```
16. 关闭方法:
*}*
这种方法对于类似Let ' s get this visa-vis,* 他说,这些男生的分数真的有那么好吗? 将如下:*
***Let's
get
this
vis-a-vis
he
said
these
boys'
marks
are
really
that
well***
该配方的完整代码如下:
*import java.text.BreakIterator;
import java.util.StringTokenizer;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class WordDetection {
public static void main(String[] args){
String input = ""Let's get this vis-a-vis", he said, "these boys'
marks are really that well?"";
WordDetection wordDetection = new WordDetection();
wordDetection.useTokenizer(input);
wordDetection.useBreakIterator(input);
wordDetection.useRegEx(input);
}
public void useTokenizer(String input){
System.out.println("Tokenizer");
StringTokenizer tokenizer = new StringTokenizer(input);
String word ="";
while(tokenizer.hasMoreTokens()){
word = tokenizer.nextToken();
System.out.println(word);
}
}
public void useBreakIterator(String input){
System.out.println("Break Iterator");
BreakIterator tokenizer = BreakIterator.getWordInstance();
tokenizer.setText(input);
int start = tokenizer.first();
for (int end = tokenizer.next();
end != BreakIterator.DONE;
start = end, end = tokenizer.next()) {
System.out.println(input.substring(start,end));
}
}
public void useRegEx(String input){
System.out.println("Regular Expression");
Pattern pattern = Pattern.compile("\\w[\\w-]+('\\w*)?");
Matcher matcher = pattern.matcher(input);
while ( matcher.find() ) {
System.out.println(input.substring(matcher.start(),
matcher.end()));
}
}
}*
使用 Java 检测句子
在这个食谱中,我们将看到如何检测句子,以便我们可以使用它们进行进一步的分析。对于数据科学家来说,句子是一个非常重要的文本单元,可以用来试验不同的路由练习,例如分类。为了从文本中检测句子,我们将使用 Java 的BreakIterator类。
准备就绪
进入https://docs . Oracle . com/javase/7/docs/API/Java/text/break iterator . html查看示例。这将让你对 break 迭代器的用法有所了解。
怎么做...
为了测试这个菜谱的代码,我们将使用两个句子,这两个句子可能会给许多基于正则表达式的解决方案造成混淆。用于测试的两个句子是:*我的名字是 Rushdi Shams。你可以在我的名字前用博士,因为我有博士学位。但是我有点不好意思用它。*有趣的是,我们会看到 Java 的BreakIterator类非常高效地处理它们。
创建一个将测试字符串作为参数的方法。
public void useSentenceIterator(String source){
-
创建一个
BreakIterator类的sentenceiterator对象:BreakIterator iterator = BreakIterator.getSentenceInstance(Locale.US); -
在测试管柱
iterator.setText(source);上使用
iterator -
将测试字符串的
start索引获取到一个整数变量:int start = iterator.first(); -
最后,遍历迭代器中的所有句子并打印出来。要循环遍历迭代器中的句子,您需要另一个名为
end的变量来指向句子的结尾索引:
for (int end = iterator.next(); end != BreakIterator.DONE;
start = end, end = iterator.next()) {
System.out.println(source.substring(start,end));
}
代码的输出如下所示:
My name is Rushdi Shams.
You can use Dr. before my name as I have a Ph.D. but I am a bit shy to use it.
食谱的完整代码如下:
import java.text.BreakIterator;
import java.util.Locale;
public class SentenceDetection {
public void useSentenceIterator(String source){
BreakIterator iterator =
BreakIterator.getSentenceInstance(Locale.US);
iterator.setText(source);
int start = iterator.first();
for (int end = iterator.next();
end != BreakIterator.DONE;
start = end, end = iterator.next()) {
System.out.println(source.substring(start,end));
}
}
public static void main(String[] args){
SentenceDetection detection = new SentenceDetection();
String test = "My name is Rushdi Shams. You can use Dr. before my
name as I have a Ph.D. but I am a bit shy to use it.";
detection.useSentenceIterator(test);
}
}
使用 OpenNLP 检测单词和句子
本章前面的两个方法使用遗留 Java 类和其中的方法来检测令牌(单词)和句子。在这个菜谱中,我们将把检测标记和句子这两个任务与 Apache 的一个名为 OpenNLP 的开源库结合起来。引入 OpenNLP 和这两个可以用传统方法很好完成的任务的原因是向数据科学家介绍一个非常方便的工具,并且在标准和经典语料库的几个信息检索任务中具有非常高的准确性。OpenNLP 的主页可以在opennlp.apache.org/找到。使用这个库进行标记化、句子分割、词性标注、命名实体识别、分块、解析和共同引用解析的一个有力论据是,您可以在文章或文档的语料库上训练自己的分类器。
准备就绪
- At the time of writing this book, the 1.6.0 version was the latest for OpenNLP and therefore you are encouraged to use this version. Download the 1.6.0 version of the library from opennlp.apache.org/download.ht…. Go to this webpage and download the binary zip files:
- After downloading the files, unzip them. In the distribution, you will find a directory named
lib. - 在
lib目录中,您会发现以下两个 Jar 文件:
从该目录中,将opennlp-tools-1.6.0.jar文件作为外部库添加到您需要为这个配方创建的 Eclipse 项目中:
对于这个菜谱,您将使用 OpenNLP 提供的预构建的令牌和句子检测模型。因此,您需要下载模型并将其保存在硬盘中。记住这些模型的位置,这样您就可以在代码中包含这些模型。
去opennlp.sourceforge.net/models-1.5/下载英语分词器和句子检测器模型。将它们保存在您的C:/驱动器中。现在,您可以编写一些代码了:
怎么做...
-
In this recipe, you will create a method that uses the
tokenizerand sentence detector models of OpenNLP to tokenize and fragment a source text into sentences. As parameters, you will send the following:- 将包含源文本的字符串。
- 模型的路径。
- 一个字符串,表示您是要对源文本进行标记,还是要将其分割成句子单元。对于前者,你将选择发送的是字,对于后者,选择将是句。
public void useOpenNlp(String sourceText, String modelPath, String choice) throws IOException{ -
首先,阅读将它们视为输入流的模型:
InputStream modelIn = null; modelIn = new FileInputStream(modelPath); -
然后,为选择句子创建一个 if 块,它将包含代码来检测源文本:
if(choice.equalsIgnoreCase("sentence")){的句子片段
-
从预先构建的模型中创建一个句子模型,然后关闭用于保存预先构建的模型的变量:
SentenceModel model = new SentenceModel(modelIn); modelIn.close(); -
使用该模型,创建一个句子检测器:
SentenceDetectorME sentenceDetector = new SentenceDetectorME(model); -
使用句子检测器检测源文本的句子。生成的句子将被视为一个字符串数组:
String sentences[] = sentenceDetector.sentDetect(sourceText); -
现在,在控制台上打印句子并关闭 if 块:
System.out.println("Sentences: "); for(String sentence:sentences){ System.out.println(sentence); } } -
接下来,创建一个 else if 块,它将保存用于对源文本进行标记化的代码:
else if(choice.equalsIgnoreCase("word")){ -
从预建模型创建一个
tokenizer模型,并关闭预建模型:TokenizerModel model = new TokenizerModel(modelIn); modelIn.close(); -
使用该模型,创建一个
tokenizer:
```java
Tokenizer tokenizer = new TokenizerME(model);
```
11. 使用tokenizer从源文本中提取单词。提取的令牌将被放入一个字符串数组:
```java
String tokens[] = tokenizer.tokenize(sourceText);
```
12. 最后,在控制台上打印令牌并关闭 else if 块:
```java
System.out.println("Words: ");
for(String token:tokens){
System.out.println(token);
}
}
```
13. 您将需要一个 else 块,以防来自用户的无效选择:
```java
else{
System.out.println("Error in choice");
modelIn.close();
return;
}
```
14. 关闭方法:
}
该配方的完整源代码如下:
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import opennlp.tools.sentdetect.SentenceDetectorME;
import opennlp.tools.sentdetect.SentenceModel;
import opennlp.tools.tokenize.Tokenizer;
import opennlp.tools.tokenize.TokenizerME;
import opennlp.tools.tokenize.TokenizerModel;
public class OpenNlpSenToken {
public static void main(String[] args){
OpenNlpSenToken openNlp = new OpenNlpSenToken();
try {
openNlp.useOpenNlp("My name is Rushdi Shams. "
+ "You can use Dr. before my name as I have a Ph.D. "
+ "but I am a bit shy to use it.", "C:/en-sent.bin",
"sentence");
openNlp.useOpenNlp(""Let's get this vis-a-vis", he said,
"these boys' marks are really that well?"", "C:/en-
token.bin", "word");
} catch (IOException e) {
}
}
public void useOpenNlp(String sourceText, String modelPath, String
choice) throws IOException{
InputStream modelIn = null;
modelIn = new FileInputStream(modelPath);
if(choice.equalsIgnoreCase("sentence")){
SentenceModel model = new SentenceModel(modelIn);
modelIn.close();
SentenceDetectorME sentenceDetector = new
SentenceDetectorME(model);
String sentences[] = sentenceDetector.sentDetect(sourceText);
System.out.println("Sentences: ");
for(String sentence:sentences){
System.out.println(sentence);
}
}
else if(choice.equalsIgnoreCase("word")){
TokenizerModel model = new TokenizerModel(modelIn);
modelIn.close();
Tokenizer tokenizer = new TokenizerME(model);
String tokens[] = tokenizer.tokenize(sourceText);
System.out.println("Words: ");
for(String token:tokens){
System.out.println(token);
}
}
else{
System.out.println("Error in choice");
modelIn.close();
return;
}
}
}
现在,您可以将这个源代码的输出与前两个菜谱进行比较,因为这两个菜谱使用了相同的源文本。
注意
对于 OpenNLP 库的其他用途,强烈建议本书的读者查看https://open NLP . Apache . org/documentation/1 . 6 . 0/manual/open NLP . html。
使用斯坦福 CoreNLP 从标记中检索词条、词性和识别命名实体
现在我们知道了如何从给定的文本中提取标记或单词,我们将看到如何从标记中获得不同类型的信息,如它们的词条、词性以及标记是否是命名实体。
把一个词的屈折形式组合在一起,使它们可以作为一个单一的文本单位来分析的过程。这类似于词干处理过程,区别在于词干处理在分组时不考虑上下文。因此,对文本数据分析来说,词汇化比词干化更有用,但需要更多的计算能力。
文章或文档中的标记的词性标签被广泛用作许多机器学习模型的特征,这些机器学习模型对数据科学家来说可能是有用的。
另一方面,命名实体对于新闻文章数据分析非常重要,并且对与商业公司相关的研究具有非常高的影响。
在这个菜谱中,我们将使用 Stanford CoreNLP 3.7.0 从文本中检索这些信息,这是编写本章时的最新版本。
准备就绪
- 去 stanfordnlp.github.io/CoreNLP/dow… 下载斯坦福 CoreNLP 3.7.0。
- The files that you have downloaded in step 1 are compressed. If you decompress them, you will find a directory structure as follows:
- 将图中所示的所有 jar 文件作为外部 Jar 文件包含到您现有的项目中,您就可以编写一些代码了:
怎么做...
-
创建一个类和一个
main()方法,用于保存这个菜谱的所有代码:public class Lemmatizer { public static void main(String[] args){ -
接下来,创建一个斯坦福 CoreNLP 管道。通过这条管道,你将为 CoreNLP 引擎提供许多属性值:
StanfordCoreNLP pipeline; -
创建一个
Properties对象并添加一些属性。在我们的例子中,我们将使用词性标注和词汇化来标记:Properties props = new Properties(); props.put("annotators", "tokenize, ssplit, pos, lemma, ner"); -
接下来,使用这些属性创建一个 CoreNLP 对象:
pipeline = new StanfordCoreNLP(props, false); -
创建一个需要为其生成词条的字符串:
String text = "Hamlet's mother, Queen Gertrude, says this famous line while watching The Mousetrap. " + "Gertrude is talking about the queen in the play. " + "She feels that the play-queen seems insincere because she repeats so dramatically that she'll never remarry due to her undying love of her husband."; -
接下来,用给定的文本
Annotation document = pipeline.process(text);创建一个
Annotation -
最后,对于每一个令牌,得到原令牌,得到该令牌的引理。您不需要获得原始令牌,但要看到单词形式和词条形式之间的区别,这可能很方便。使用您在上一步中创建的名为
document的Annotation变量:for(CoreMap sentence: document.get(SentencesAnnotation.class)) { for(CoreLabel token: sentence.get(TokensAnnotation.class)) { String word = token.get(TextAnnotation.class); String lemma = token.get(LemmaAnnotation.class); String pos = token.get(PartOfSpeechAnnotation.class); String ne = token.get(NamedEntityTagAnnotation.class); System.out.println(word + "-->" + lemma + "-->" + pos + "-->" + ne); } }对所有句子重复上述步骤
-
关闭方法和类:
}
}
代码的部分输出如下:
...
Queen-->Queen-->NNP-->PERSON
Gertrude-->Gertrude-->NNP-->PERSON
,-->,-->,-->O
says-->say-->VBZ-->O
this-->this-->DT-->O
famous-->famous-->JJ-->O
line-->line-->NN-->O
while-->while-->IN-->O
watching-->watch-->VBG-->O
The-->the-->DT-->O
Mousetrap-->mousetrap-->NN-->O
.-->.-->.-->O
Gertrude-->Gertrude-->NNP-->PERSON
is-->be-->VBZ-->O
talking-->talk-->VBG-->O
...
这个食谱的完整代码如下:
import edu.stanford.nlp.ling.CoreAnnotations.LemmaAnnotation;
import edu.stanford.nlp.ling.CoreAnnotations.NamedEntityTagAnnotation;
import edu.stanford.nlp.ling.CoreAnnotations.PartOfSpeechAnnotation;
import edu.stanford.nlp.ling.CoreAnnotations.SentencesAnnotation;
import edu.stanford.nlp.ling.CoreAnnotations.TextAnnotation;
import edu.stanford.nlp.ling.CoreAnnotations.TokensAnnotation;
import edu.stanford.nlp.ling.CoreLabel;
import edu.stanford.nlp.pipeline.Annotation;
import edu.stanford.nlp.pipeline.StanfordCoreNLP;
import edu.stanford.nlp.util.CoreMap;
import java.util.Properties;
public class Lemmatizer {
public static void main(String[] args){
StanfordCoreNLP pipeline;
Properties props = new Properties();
props.put("annotators", "tokenize, ssplit, pos, lemma, ner");
pipeline = new StanfordCoreNLP(props, false);
String text = "Hamlet's mother, Queen Gertrude, says this
famous line while watching The Mousetrap. "
+ "Gertrude is talking about the queen in the play. "
+ "She feels that the play-queen seems insincere because
she repeats so dramatically that she'll never remarry
due to her undying love of her husband.";
Annotation document = pipeline.process(text);
for(CoreMap sentence: document.get(SentencesAnnotation.class))
{
for(CoreLabel token: sentence.get(TokensAnnotation.class))
{
String word = token.get(TextAnnotation.class);
String lemma = token.get(LemmaAnnotation.class);
String pos = token.get(PartOfSpeechAnnotation.class);
String ne = token.get(NamedEntityTagAnnotation.class);
System.out.println(word + "-->" + lemma + "-->" + pos
+ "-->" + ne);
}
}
}
}
使用 Java 8 用余弦相似度度量文本相似度
数据科学家经常测量两个数据点之间的距离或相似性——有时用于分类或聚类,有时用于检测异常值,以及许多其他情况。当他们将文本作为数据点处理时,传统的距离或相似性度量就不能使用了。有许多标准的和经典的以及新兴的和新颖的相似性度量可用于比较两个或多个文本数据点。在这个食谱中,我们将使用一种叫做余弦相似度的方法来计算两个句子之间的距离。余弦相似度被认为是信息检索社区中事实上的标准,因此被广泛使用。在这个菜谱中,我们将使用这个度量来查找字符串格式的两个句子之间的相似性。
准备就绪
虽然读者可以从en.wikipedia.org/wiki/Cosine…那里得到测量的全面展望,但是让我们来看看使用公式的两个句子的算法:
- 首先,从两个字符串中提取单词。
- 对于相应字符串中的每个单词,计算它们的频率。这里的频率表示单词在每个句子中出现的次数。设 A 是来自第一个字符串的单词及其频率的向量,B 是来自第二个字符串的单词及其频率的向量。
- 通过删除重复项,找出每个字符串中唯一的单词。
- 找出两个字符串相交的单词列表。
- 余弦相似性公式的分子将是矢量 A 和 b 的点积。
- 公式的分母是矢量 A 和 b 的算术乘积。
注意
请注意,两个句子的余弦相似性得分将介于 1(表示完全相反)和 1(表示完全相同)之间,而 0 分表示去相关。
怎么做...
-
创建一个接受两个字符串参数的方法。这些字符串将与您的
calculateCosine相似:public double calculateCosine(String s1, String s2){ -
Use the power of regular expressions and Java 8's parallelization facility to tokenize the given strings. This gives you two streams of words in the tw O strings:
Stream<String> stream1 = Stream.of(s1.toLowerCase().split("\\W+")).parallel(); Stream<String> stream2 = Stream.of(s2.toLowerCase().split("\\W+")).parallel();tip
For tokenization, you can use any method in the first method in this chapter, but the method shown in this step is also convenient and short, and makes use of the powerful functions of regular expressions and Java 8.
-
获取每个单词在每个字符串中的出现频率。同样,您将使用 Java 8 来实现这一点。结果将是两个地图:
Map<String, Long> wordFreq1 = stream1 .collect(Collectors.groupingBy (String::toString,Collectors.counting())); Map<String, Long> wordFreq2 = stream2 .collect(Collectors.groupingBy (String::toString,Collectors.counting())); -
从每个句子的单词列表中,通过删除重复的单词,只保留唯一的单词。为此,您将使用在上一步中创建的地图创建两个集合:
Set<String> wordSet1 = wordFreq1.keySet(); Set<String> wordSet2 = wordFreq2.keySet(); -
因为您将在步骤 3 中计算两个图的点积,以用于余弦相似性度量的分子,所以您需要创建两个字符串共有的单词列表:
Set<String> intersection = new HashSet<String>(wordSet1); intersection.retainAll(wordSet2); -
接下来计算公式的分子,就是两个图的点积:
double numerator = 0; for (String common: intersection){ numerator += wordFreq1.get(common) * wordFreq2.get(common); } -
From this point on, you will be preparing to compute the denominator of the formula, which is the arithmetic product of the magnitudes of the two maps.
首先,创建变量来保存矢量的量值(在地图数据结构中):
double param1 = 0, param2 = 0; -
现在,计算你的第一个向量的大小:
for(String w1: wordSet1){ param1 += Math.pow(wordFreq1.get(w1), 2); } param1 = Math.sqrt(param1); -
接下来,计算第二个向量的大小:
for(String w2: wordSet2){ param2 += Math.pow(wordFreq2.get(w2), 2); } param2 = Math.sqrt(param2); -
现在你已经有了
denominator的所有参数,乘以幅度得到它:
```java
double denominator = param1 * param2;
```
11. 最后,将分子和分母放在适当的位置,计算两个字符串的余弦相似度。将分数返回给呼叫者。关闭方法:
```java
double cosineSimilarity = numerator/denominator;
return cosineSimilarity;
}
```
12. 此菜谱的完整代码如下:
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.Stream;
public class CosineSimilarity {
public double calculateCosine(String s1, String s2){
//tokenization in parallel with Java 8
Stream<String> stream1 =
Stream.of(s1.toLowerCase().split("\\W+")).parallel();
Stream<String> stream2 =
Stream.of(s2.toLowerCase().split("\\W+")).parallel();
//word frequency maps for two strings
Map<String, Long> wordFreq1 = stream1
.collect(Collectors.groupingBy
(String::toString,Collectors.counting()));
Map<String, Long> wordFreq2 = stream2
.collect(Collectors.groupingBy
(String::toString,Collectors.counting()));
//unique words for each string
Set<String> wordSet1 = wordFreq1.keySet();
Set<String> wordSet2 = wordFreq2.keySet();
//common words of two strings
Set<String> intersection = new HashSet<String>(wordSet1);
intersection.retainAll(wordSet2);
//numerator of cosine formula. s1.s2
double numerator = 0;
for (String common: intersection){
numerator += wordFreq1.get(common) * wordFreq2.get(common);
}
//denominator of cosine formula has two parameters
double param1 = 0, param2 = 0;
//sqrt (sum of squared of s1 word frequencies)
for(String w1: wordSet1){
param1 += Math.pow(wordFreq1.get(w1), 2);
}
param1 = Math.sqrt(param1);
//sqrt (sum of squared of s2 word frequencies)
for(String w2: wordSet2){
param2 += Math.pow(wordFreq2.get(w2), 2);
}
param2 = Math.sqrt(param2);
//denominator of cosine formula. sqrt(sum(s1^2)) X
sqrt(sum(s2^2))
double denominator = param1 * param2;
//cosine measure
double cosineSimilarity = numerator/denominator;
return cosineSimilarity;
}//end method to calculate cosine similarity of two strings
public static void main(String[] args){
CosineSimilarity cos = new CosineSimilarity();
System.out.println(cos.calculateCosine("To be, or not to be: that
is the question.", "Frailty, thy name is woman!"));
System.out.println(cos.calculateCosine("The lady doth protest too
much, methinks.", "Frailty, thy name is woman!"));
}
}
如果运行该代码,您会发现以下输出:
0.11952286093343936
0.0
输出意味着句子之间的余弦相似度*是,还是不是:这是个问题。和脆弱,你的名字是女人!*大约是0.11;在我看来,这位女士抗议得太多了。脆弱,你的名字是女人!是0.0。
Tip
在这个方法中,您没有从字符串中删除停用词。为了获得无偏差的结果,最好从两个文本单元中删除停用词。
使用 Mallet 从文本文档中提取主题
如今,随着文本格式的文档数量不断增加,任何数据科学家的一项重要任务都是获得大量带有摘要、概要或抽象主题列表的文章的概览,这不是因为这样可以节省通读文章的时间,而是为了进行聚类、分类、语义相关度测量、情感分析等。
在机器学习和自然语言处理领域,主题建模是指使用统计模型从文本文章中检索抽象主题或关键词。在这个食谱中,我们将使用一个复杂的基于 Java 的机器学习和自然语言处理库,名为 Mallet,是语言工具包机器学习的首字母缩写(见mallet.cs.umass.edu/)。Mallet 在学术界和工业中广泛应用于以下方面:
- 文件分类,
- 聚类,
- 主题建模,以及
- 信息提取。
然而,这本书的范围仅限于主题建模和文档分类。在这个菜谱中,我们将介绍如何使用 Mallet 提取主题,而下一个菜谱将重点关注使用 Mallet 和监督机器学习对文本文档进行分类。
注意
请注意,您将只使用命令提示符来使用该工具,并且您将不会参与该配方和下一个配方的编码。这是因为 Mallet 更容易与命令提示符一起使用。希望使用 Java API 的感兴趣的读者可以去 mallet.cs.umass.edu/api/阅读 Mallet 丰富的 API 文档。
准备就绪
- First, you will be installing Mallet. We will be providing installation instructions only for Windows operating systems in this recipe. Go to mallet.cs.umass.edu/download.ph… and download Mallet. At the time of writing this book, version 2.0.8 was the latest version and therefore you are encouraged to download it (preferably the zip file):
- Unzip Mallet into your
C:/ directory. YourC:/drive will have a directory namedC:\mallet-2.0.8RC2or similar: - Inside the directory, the files and folders will look something as the following screenshot shows. The actual runnable file is in the bin folder and there are some sample datasets in the sample-data folder:
- Go to
Control Panel\System and Security\Systemin your Windows PC. Click on Advanced system settings. - Now, you will see a system property window. Click on the Environment Variables... button:
- This will open a window for setting up environment variables. Click on New for system variables:
- 在变量名文本框中,输入
MALLET_HOME。并在变量值文本框中,给出路径C:\mallet-2.0.8RC2。点击确定关闭窗口。 - 要查看 mallet 是否已正确安装,请打开命令提示符窗口,转到 Mallet 目录的 bin 文件夹,并键入 Mallet。您应该会看到所有可以在屏幕上使用的 Mallet 2.0 命令:
现在,你可以使用木槌了。在任何时候,如果您对命令或参数不确定,您可以使用 Mallet append-help 中的 Mallet 2.0 命令。这将列出特定 Mallet 2.0 命令的所有可能的命令和参数选项:
怎么做...
-
The Mallet distribution folder in your
C:/drive has a directory named sample-data. This directory contains another directory named web. Inside web, you will find two more directories-the directory named en contains a few text files that are text versions of a few English web articles, and the directory named de contains a few text files that are text versions of a few German web articles. The en directory can be seen as our dataset or corpus for this recipe and you will be extracting topics from these web articles. If you have your own set of documents for which you need to extract topics, just imagine the following tasks that you are going to do by simply replacing the en directory with the directory where your documents reside.为此,首先将文本文件转换成一个单独的文件,这个文件是 Mallet 类型的,是二进制的,不可读的。从命令行,转到
C:\mallet-2.0.8RC2/bin并键入以下命令:mallet import-dir --input C:\mallet-2.0.8RC2\sample- data\web\en --output c:/web.en.mallet --keep-sequence -- remove-stopwords该命令在您的
C:/驱动器中创建一个 Mallet 文件,名为 web.en.mallet,方法是保留 en 目录中列出的数据文件的原始序列,从标准英语词典中删除停用词。如果您希望模型在建模过程中考虑文本的二元模型,请将该命令替换为:
注意
mallet import-dir --input C:\mallet-2.0.8RC2\sample-data\web\en --output c:/web.en.mallet --keep-sequence-bigrams --remove-stopwords -
Type in the following command to run Mallet's topic modelling routing with default settings on the
we.en.malletfile:mallet train-topics --input c:/web.en.mallet该命令将在命令提示符下生成如下信息:
让我们检查输出。Mallet 主题建模输出的第二行包含一行:
1 5 zinta role hindi actress film indian acting top-grossing naa award female filmfare earned films drama written areas evening protection april如果你是一个印地语电影迷,那么你会立刻明白这个话题是关于女演员普丽缇·泽塔的印地语电影。为了确认,您可以查看在
C:\mallet-2.0.8RC2\sample-data\web\en目录中名为zinta.txt的文件。输出中的 1 表示段落编号(编号从 0 开始),5 是主题的狄利克雷参数(可以看做是主题的权重)。由于我们没有设置它,这个数字将是输出中所有段落的默认值。
注意
MALLET 包含了主题建模和提取的随机性元素,因此每次程序运行时,即使是在相同的数据集上,关键字列表看起来也会不同。因此,如果您的输出与本食谱中概述的不同,不要认为有什么地方出错了。
这一步中的命令太普通了,没有使用 Mallet 中任何精彩的参数,并且在控制台上显示结果。
-
Next, we will be applying topic modelling on the same data but with more options and we will output the topics to an external file so that we can further use them. On your command prompt, type in the following:
mallet train-topics --input c:/web.en.mallet --num-topics 20-- num-top-words 20 --optimize-interval 10 --xml-topic-phrase- report C:/web.en.xml该命令表示我们将使用
c:/web.en.mallet文件作为输入,为数据生成最多 20 个主题,打印前 20 个主题,并在c:/web.en.xml文件中输出结果。--optimize-区间用于通过打开超参数优化来生成更好的主题模型,最终通过将一些主题优先于其他主题来允许模型更好地拟合数据。运行该命令后,您将看到在您的
C:/驱动器中,生成了一个名为web.en.xml的 XML 文件。如果您打开该文件,您将看到类似以下内容: -
There are some other options in Mallet that you can use when you use topic modelling. One of the important options is the alpha parameter, Which is known as the smoothing parameter for the topic distribution. Try the following command:
mallet train-topics --input c:/web.en.mallet --num-topics 20-- num-top-words 20 --optimize-interval 10 --alpha 2.5 --xml- topic-phrase-report C:/web.en.xmlTip
Set . The rule of thumb for the alpha value is 50/T, where t is the number of topics you select with the-num-topics [NUMBER] option. So, if you generate 20 themes, you should set the value of alpha to 50/20 = 2.5.
If-random-seed is not set to generate a theme model for the document, randomness will be applied, and a slightly/completely different xml file will be generated using the theme every time.
-
Mallet 还以不同的格式生成输出,以多种不同的方式帮助分析主题。在命令行中键入以下命令:
mallet train-topics --input c:/web.en.mallet --num-topics 20--
num-top-words 20 --optimize-interval 10 --output-state
C:\web.en.gz --output-topic-keys C:\web.en.keys.txt --
output-doc-topics c:/web.en.composition.txt
该命令将在您的C:/驱动器中生成三个新文件。
C:\web.en.gzcontains a file where every word in your corpus and the topic it belongs to. A partial look of the file can be as follows:C:\web.en.keys.txt包含我们已经在步骤 2 中在控制台上看到的数据,即主题编号、权重和每个主题的热门关键词。C:/web.en.composition.txt包含您导入的每个原始文本文件中每个主题的百分比细分。以下是该文件的部分外观。可以使用任何电子表格应用程序(如 Microsoft Excel)打开该文件。
在大多数情况下,这些是您将用来从文章集中提取主题的关键命令。在这个食谱中遵循的步骤是从文本集合中提取主题。如果您有一篇需要提取主题的文章,请将这篇文章放在一个目录中,并将其视为单个文档的语料库。
在我们完成菜谱之前,让我们来看看 Mallet 可以使用的主题建模算法:
- 皱胃向左移
- 并行 LDA
- DMR·艾达
- 分级 LDA
- 标签 LDA
- 多语种主题模型
- 分层弹球分配模型
- 加权主题模型
- 具有集成短语发现的 LDA
- 使用具有负采样的跳跃图的单词嵌入(word2vec)
使用木槌对文本文档进行分类
本章的最后两个方法是经典的机器学习分类问题——使用语言建模对文档进行分类。在这个菜谱中,我们将使用 Mallet 及其命令行界面来训练一个模型,并将该模型应用于看不见的测试数据。
木槌分类取决于三个步骤:
- 将您的培训文档转换成 Mallet 的本地格式。
- 根据培训文档培训您的模型。
- 应用该模型对看不见的测试文档进行分类。
当提到需要将训练文档转换成 Mallet 的原生格式时,其技术含义是将文档转换成特征向量。您不需要从您的培训或测试文档中提取任何特性,因为 Mallet 会处理这些。您可以在物理上分离培训和测试数据,或者您可以有一个文档的平面列表,并从命令行选项中分割培训和测试部分。
让我们考虑一个简单的设置:您有纯文本文件中的文本数据,每个文档一个文件。不需要识别文档的开始或结束。文件将被组织在目录中,其中具有相同类别标签的所有文档将被包含在一个目录中。例如,如果您的文本文件有两个类,spam 和 ham,那么您需要创建两个目录——一个包含所有的 spam 文档,另一个包含所有的 ham 文档。
准备就绪
- Mallet 的安装已经在前面的名为使用 Mallet 从文本文档中提取主题的菜谱中详细介绍过了,因此我们将避免重复。
- 打开网页浏览器,粘贴以下网址:http://www . cs . CMU . edu/AFS/cs/project/theo-11/www/naive-Bayes/20 _ news groups . tar . gz。这将下载一个文件夹,其中包含分类在 20 个不同目录中的新闻文章。将它保存在 Mallet 安装目录中:
怎么做...
-
打开一个命令提示窗口,进入
Mallet安装文件夹的 bin 文件夹。 -
Write the following command while you are inside the bin folder:
mallet import-dir --input C:\mallet-2.0.8RC2\20_newsgroups\*-- preserve-case --remove-stopwords --binary-features --gram- sizes 1 --output C:\20_newsgroup.classification.mallet该命令将获取
C:\mallet-2.0.8RC2\20_newsgroups文件夹中的所有文档,删除其中的停用词,保留文档中单词的实际大小写,并创建 gram 大小为 1 的二进制特征。Mallet 从文档中输出的本地文件格式将被保存为C:\20_newsgroup.classification.mallet. -
Next, create a Maximum Entropy classifier from the data using the following command. The command takes the output of the previous step as input, creates a Naïve Bayes classifier from the binary features with 1-grams and outputs the classifier as
C:\20_newsgroup.classification.classifier:mallet train-classifier --trainer NaiveBayes --input C:\20_newsgroup.classification.mallet --output-classifier C:\20_newsgroup.classification.classifier除了朴素贝叶斯,Mallet 还支持许多其他算法。以下是完整的列表:
- adaboost 算法
- 制袋材料
- 扬
- C45 决策树
- 合奏教练
- 最大熵分类器(多项式逻辑回归)
- 朴素贝叶斯
- 秩最大熵分类器
- 后验正则化辅助模型
-
Besides training on the full dataset, you can also provide a portion of data to be used as training data and the rest as test data; and based on the test data's actual class labels, you can see the classifier's prediction performance.
在 bin 文件夹中编写以下命令:
mallet train-classifier --trainer NaiveBayes --input C:\20_newsgroup.classification.mallet --training-portion 0.9该命令随机选择 90%的数据,并在这些数据上训练朴素贝叶斯分类器。最后,通过看不到它们的实际标签,将分类器应用于剩余的 10%数据;它们的实际类别仅在分类器评估期间被考虑。
该命令为您提供了 20 个类别的分类器的总体准确性,以及每个类别的精确度、召回率和准确性,以及标准误差。
-
您也可以多次运行培训和测试;每次训练和测试集将被随机选择。例如,如果您想在 90%的数据上训练您的分类器,并在剩下的 10%的数据上用随机分割测试分类器 10 次,使用下面的命令:
mallet train-classifier --trainer NaiveBayes --input C:\20_newsgroup.classification.mallet --training-portion 0.9-- num-trials 10 -
You can also do cross-validation using Mallet where you can specify number of folds to be created during cross validation. For instance, if you want to do a 10-fold cross validation, use the following command:
mallet train-classifier --trainer NaiveBayes --input C:\20_newsgroup.classification.mallet --cross-validation 10该命令将为您提供 10 次试验中每一次的单独结果,每次都包含原始数据的新测试部分以及 10 次试验的平均结果。Mallet 还给出了一个混淆矩阵,这对数据科学家更好地理解他们的模型真的很重要。
-
Mallet 允许您比较从不同算法开发的多个分类器的性能。例如,下面的命令将给出使用朴素贝叶斯和使用 10 重交叉验证的最大熵的两个分类器的比较:
mallet train-classifier --trainer MaxEnt --trainer NaiveBayes- -input C:\20_newsgroup.classification.mallet --cross- validation 10 -
If you want to use your saved classifier on a set of unseen test documents (which is not our case as we have used the entire directory for training in step 2), you can use the following command:
mallet classify-dir --input <directory containing unseen test data> --output - --classifier C:\20_newsgroup.classification.classifier这个命令将会在控制台上显示你未看到的测试文档的预测类。还可以使用以下命令将预测保存在制表符分隔的值文件中:
mallet classify-dir --input <directory containing unseen test data> --output <Your output file> --classifier C:\20_newsgroup.classification.classifier -
最后,还可以在单个看不见的测试文档上使用一个保存的分类器。为此,请使用以下命令:
mallet classify-file --input <unseen test data file path> --
output - --classifier
C:\20_newsgroup.classification.classifier
这个命令将会在控制台上显示出你未看到的测试文档的预测类。还可以使用以下命令将预测保存在制表符分隔的值文件中:
mallet classify-file --input <unseen test data file path> --
output <Your output file> --classifier C:\20_ne
wsgroup.classification.classifier
使用 Weka 对文本文档进行分类
我们在第 4 章、从数据中学习-第 1 部分中使用 Weka 对非文本格式的数据点进行分类。Weka 也是使用机器学习模型对文本文档进行分类的非常有用的工具。在这个菜谱中,我们将演示如何使用 Weka 3 来开发文档分类模型。
准备就绪
- 要下载 Weka,请前往www.cs.waikato.ac.nz/ml/weka/dow…,你会找到 Windows、Mac 和其他操作系统(如 Linux)的下载选项。仔细阅读选项并下载合适的版本。在撰写本书期间,3.9.0 是开发人员的最新版本,由于作者已经在他的 64 位 Windows 机器上安装了 1.8 版本的 JVM,他选择了来下载一个用于 64 位 Windows 的自解压可执行文件,无需 Java VM。
- 下载完成后,双击可执行文件并按照屏幕上的说明进行操作。你需要安装 Weka 的完整版本。
- 安装完成后,不要运行该软件。相反,转到安装它的目录,找到 Weka 的 Java 归档文件(weka.jar)。将这个文件作为外部库添加到 Eclipse 项目中。
- 将在该配方中使用的示例文档文件将保存在目录中。每个目录包含相似类别的文档。要下载示例文档,请打开 web 浏览器,复制并粘贴以下 URL:https://WEKA . wikispaces . com/file/view/text _ example . zip/82917283/text _ example . zip。这将提示您保存文件(如果您的浏览器配置为询问您保存文件的位置)。将文件保存在您的
C:/驱动器上。解压缩文件,您将看到如下所示的目录结构:
每个目录包含一些属于特定类的 html 文件。这些类别具有标签 class1、class2 和 class3。
现在,您已经为使用 Weka 对这些文档进行分类做好了准备。
怎么做...
-
创建一个类和一个
main()方法来存放你所有的代码。main 方法会抛出异常:public class WekaClassification { public static void main(String[] args) throws Exception { -
创建一个加载器,通过设置父目录到加载器的路径来加载所有类目录的父目录:
TextDirectoryLoader loader = new TextDirectoryLoader(); loader.setDirectory(new File("C:/text_example")); -
从加载的 html 文件创建实例:
Instances data = loader.getDataSet(); -
从数据字符串中创建单词向量。为此,首先创建一个过滤器,将字符串转换为单词向量,然后为过滤器设置上一步的原始数据:
StringToWordVector filter = new StringToWordVector(); filter.setInputFormat(data); -
为了完成字符串到单词的向量转换,使用这个过滤器从数据中创建实例:
Instances dataFiltered = Filter.useFilter(data, filter); -
从这个单词向量生成一个朴素贝叶斯分类器:
NaiveBayes nb = new NaiveBayes(); nb.buildClassifier(dataFiltered); -
此时,你也可以考虑看看你的模型是什么样子的。为此,在控制台上打印您的模型:
System.out.println("\n\nClassifier model:\n\n" + nb); -
A partial output on your screen will look like the following:
-
要使用 k-fold 交叉验证评估模型,请编写以下代码:
Evaluation eval = null;
eval = new Evaluation(dataFiltered);
eval.crossValidateModel(nb, dataFiltered, 5, new Random(1));
System.out.println(eval.toSummaryString());
这将在控制台上打印分类器评估:
Correctly Classified Instances 1 14.2857 %
Incorrectly Classified Instances 6 85.7143 %
Kappa statistic -0.5
Mean absolute error 0.5714
Root mean squared error 0.7559
Relative absolute error 126.3158 %
Root relative squared error 153.7844 %
Total Number of Instances 7
请注意,我们使用了五重交叉验证,而不是标准的 10 重交叉验证,因为文档的数量少于 10(确切地说,是 7)。
食谱的完整代码如下:
import weka.core.*;
import weka.core.converters.*;
import weka.classifiers.Evaluation;
import weka.classifiers.bayes.NaiveBayes;
import weka.filters.*;
import weka.filters.unsupervised.attribute.*;
import java.io.*;
import java.util.Random;
public class WekaClassification {
public static void main(String[] args) throws Exception {
TextDirectoryLoader loader = new TextDirectoryLoader();
loader.setDirectory(new File("C:/text_example"));
Instances data = loader.getDataSet();
StringToWordVector filter = new StringToWordVector();
filter.setInputFormat(data);
Instances dataFiltered = Filter.useFilter(data, filter);
NaiveBayes nb = new NaiveBayes();
nb.buildClassifier(dataFiltered);
System.out.println("\n\nClassifier model:\n\n" + nb);
Evaluation eval = null;
eval = new Evaluation(dataFiltered);
eval.crossValidateModel(nb, dataFiltered, 5, new Random(1));
System.out.println(eval.toSummaryString());
}
}
七、处理大数据
在本章中,我们将介绍以下配方:
- 使用 Apache Mahout 训练在线逻辑回归模型
- 使用 Apache Mahout 应用在线逻辑回归模型
- 用 Apache Spark 解决简单的文本挖掘问题
- 基于 MLib 的 KMeans 聚类算法
- 使用 MLib 创建线性回归模型
- 使用 MLib 通过随机森林模型对数据点进行分类
简介
在这一章中,你会看到大数据框架中用到的三个关键技术,对数据科学家来说极其有用:Apache Mahout、Apache Spark,以及其名为 MLib 的机器学习库。
我们将从 Apache Mahout 开始这一章,Apache Mahout 是一个可伸缩的或分布式的机器学习平台,用于分类、回归、聚类和协作过滤任务。Mahout 最初是一个机器学习工作台,只在 Hadoop MapReduce 上工作,但最终选择了 Apache Spark 作为其平台。
Apache Spark 是一个在大数据处理中引入并行化的框架,与 MapReduce 相似,因为它也跨集群分布数据。但 Spark 和 MapReduce 之间的一个关键区别是,前者试图尽可能地将内容保存在内存中,而后者则不断地从磁盘中读写。所以 Spark 比 MapReduce 快很多。我们将看到,作为一名数据科学家,您如何使用 Spark 来完成简单的文本挖掘相关任务,例如计算空行或获取大型文件中单词的频率。使用 Spark 的另一个原因是,它不仅可以与 Java 一起使用,还可以与 Python、Scala 等其他流行语言一起使用;对于 MapReduce,通常的选择是 Java。
MLib 是来自 Apache Spark 的可扩展机器学习库,它实现了多种分类、回归、聚类、协作过滤和特征选择算法。它基本上坐在 Spark 上,利用它的速度来解决机器学习问题。在本章中,你将看到如何使用这个库来解决分类、回归和聚类问题。
注意
在本书中,我们已经使用了 0.9 版本的 Mahout,但是有兴趣的读者可以在这里看看 Mahout 0.10.x 和 MLib 的区别:http://www . weatheringthroughtechdays . com/2015/04/Mahout-010 x-first-Mahout-release-as . html。
使用 Apache Mahout 训练在线逻辑回归模型
在这个菜谱中,我们将使用 Apache Mahout 来训练一个使用 Apache Mahout Java 库的在线逻辑回归模型。
准备就绪
-
In Eclipse, create a new Maven project. The author had Eclipse Mars set up. To do so, go to File. Then select New and Other...:
-
Then, expand Maven from the wizard and select Maven Project. Click on Next until you reach the window where Eclipse prompts you to provide an Artifact Id. Type in
mahoutas Artifact Id, and the grayed out Finish button will become visible. Click on Finish. This will create a Maven project for you named mahout: -
Double-click on
pom.xmlfrom your Eclipse Package Explorer to edit: -
点击
pom.xml选项卡。现在你会在屏幕上看到pom.xml文件。将下面几行放到您的<dependencies>...</dependencies>标签内的pom.xml中,并保存它。这将自动下载依赖 JAR 文件到您的项目:<dependency> <groupId>org.apache.mahout</groupId> <artifactId>mahout-core</artifactId> <version>0.9</version> </dependency> <dependency> <groupId>org.apache.mahout</groupId> <artifactId>mahout-examples</artifactId> <version>0.9</version> </dependency> <dependency> <groupId>org.apache.mahout</groupId> <artifactId>mahout-math</artifactId> <version>0.9</version> </dependency> -
Create a package named
chap7.science.datain your project undersrc/main/java directory: -
在 Eclipse 中右键单击项目名称,选择新建,然后选择文件夹。您将创建两个文件夹。第一个文件夹将包含您将为其创建模型的输入数据集,其名称将为
data。第二个文件夹将被命名为model,您将在其中保存您的模型。现在输入data作为文件夹名,点击完成。重复这个步骤,创建一个名为model的文件夹。 -
用以下数据在
data文件夹中创建一个名为weather.numeric.csv的 CSV 文件:outlook,temperature,humidity,windy,play sunny,85,85,FALSE,no sunny,80,90,TRUE,no overcast,83,86,FALSE,yes rainy,70,96,FALSE,yes rainy,68,80,FALSE,yes rainy,65,70,TRUE,no overcast,64,65,TRUE,yes sunny,72,95,FALSE,no sunny,69,70,FALSE,yes rainy,75,80,FALSE,yes sunny,75,70,TRUE,yes overcast,72,90,TRUE,yes overcast,81,75,FALSE,yes rainy,71,91,TRUE,no -
现在您已经准备好编码了。
怎么做...
-
在您刚刚创建的包中,创建一个名为
OnlineLogisticRegressionTrain.java的 Java 类。双击类文件,写下您的代码。创建一个名为OnlineLogisticRegressionTrain:public class OnlineLogisticRegressionTrain {的类
-
开始编写你的
main方法:public static void main(String[] args) throws IOException { -
创建两个
String变量来包含输入数据文件路径和您将要构建和保存的模型文件的路径:String inputFile = "data/weather.numeric.csv"; String outputFile = "model/model"; -
现在创建一个包含数据文件特性的列表:
List<String> features =Arrays.asList("outlook", "temperature", "humidity", "windy", "play"); -
此步骤列出数据文件的所有特征名称,并按照它们在数据文件中出现的顺序排列。
-
接下来,定义每个特征的类型。特征类型
w表示名义特征,特征类型n表示数字特征类型:List<String> featureType = Arrays.asList("w", "n", "n", "w", "w"); -
现在是时候设置分类器的参数了。在此步骤中,您将创建一个参数变量,并将一些值设置为参数。您将设置目标变量或类变量(在我们的例子中是
"play")。如果您看一下数据,您会发现类变量“play”最多取两个值——是或否。因此,您会将最大目标类别设置为2。接下来,您将设置非类特征的数量(在我们的例子中是4)。接下来的三个参数取决于算法。在这个方法中,您将不会使用任何偏差来生成分类器,您将使用一个平衡学习率0.5。最后,您需要使用类型映射方法设置特性及其类型:LogisticModelParameters params = new LogisticModelParameters(); params.setTargetVariable("play"); params.setMaxTargetCategories(2); params.setNumFeatures(4); params.setUseBias(false); params.setTypeMap(features,featureType); params.setLearningRate(0.5); -
您将使用
10 passes创建分类器。这个数字是任意的,你可以选择你凭经验找到的任何数字:int passes = 10; -
创建在线线性回归分类器:
OnlineLogisticRegression olr; -
创建一个变量从 CSV 文件中读取数据,并开始创建回归模型:
```java
CsvRecordFactory csv = params.getCsvRecordFactory();
olr = params.createRegression();
```
11. 接下来,您将创建一个for循环来遍历每个10 passes :
```java
for (int pass = 0; pass < passes; pass++) {
```
12. 开始读取数据文件:
```java
BufferedReader in = new BufferedReader(new
FileReader(inputFile));
```
13. 获取数据文件的文件头,它由特性的名称组成:
```java
csv.firstLine(in.readLine());
```
14. 读取数据行:
```java
String row = in.readLine();
```
15. 现在循环遍历不是null :
```java
while (row != null) {
```
的每一行
16. 现在对于每一行(或数据行),显示数据点,并创建一个输入向量:
```java
System.out.println(row);
Vector input = new
RandomAccessSparseVector(params.getNumFeatures());
```
17. 获取行
```java
int targetValue = csv.processLine(row, input);
```
的`targetValue`
18. Train具有该数据点的模型:
```java
olr.train(targetValue, input);
```
19. 阅读下一个row :
```java
row = in.readLine();
```
20. 闭环:
```java
}
```
21. 关闭阅读器阅读输入数据文件:
```java
in.close();
```
22. 关闭循环以遍历路径:
```java
}
```
23. 最后,将output模型保存到 Eclipse 项目
```java
OutputStream modelOutput = new FileOutputStream(outputFile);
try {
params.saveTo(modelOutput);
} finally {
modelOutput.close();
}
```
的`model`目录下名为`model`的文件中
24. 关闭main方法和类:
```java
}
}
```
25. 如果您运行代码,您将在控制台上看到输入数据文件的数据行作为您的输出,并且在学习的模型中,它将保存在您的 Eclipse 项目的模型目录中。
食谱的完整代码如下:
package chap7.science.data;
import java.io.BufferedReader;
import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.IOException;
import java.io.OutputStream;
import java.util.Arrays;
import java.util.List;
import org.apache.mahout.classifier.sgd.CsvRecordFactory;
import org.apache.mahout.classifier.sgd.LogisticModelParameters;
import org.apache.mahout.classifier.sgd.OnlineLogisticRegression;
import org.apache.mahout.math.RandomAccessSparseVector;
import org.apache.mahout.math.Vector;
public class OnlineLogisticRegressionTrain {
public static void main(String[] args) throws IOException {
String inputFile = "data/weather.numeric.csv";
String outputFile = "model/model";
List<String> features =Arrays.asList("outlook", "temperature",
"humidity", "windy", "play");
List<String> featureType = Arrays.asList("w", "n", "n", "w",
"w");
LogisticModelParameters params = new LogisticModelParameters();
params.setTargetVariable("play");
params.setMaxTargetCategories(2);
params.setNumFeatures(4);
params.setUseBias(false);
params.setTypeMap(features,featureType);
params.setLearningRate(0.5);
int passes = 10;
OnlineLogisticRegression olr;
CsvRecordFactory csv = params.getCsvRecordFactory();
olr = params.createRegression();
for (int pass = 0; pass < passes; pass++) {
BufferedReader in = new BufferedReader(new
FileReader(inputFile));
csv.firstLine(in.readLine());
String row = in.readLine();
while (row != null) {
System.out.println(row);
Vector input = new
RandomAccessSparseVector(params.getNumFeatures());
int targetValue = csv.processLine(row, input);
olr.train(targetValue, input);
row = in.readLine();
}
in.close();
}
OutputStream modelOutput = new FileOutputStream(outputFile);
try {
params.saveTo(modelOutput);
} finally {
modelOutput.close();
}
}
}
使用 Apache Mahout 应用在线逻辑回归模型
在这个菜谱中,我们将演示如何使用 Apache Mahout 对看不见的、未标记的测试数据应用在线逻辑回归模型。请注意,这个配方与上一个配方非常相似,需要您使用训练数据来建立模型。这个要求在前面的配方中已经演示过了。
准备就绪
-
完成前面的配方后,转到您创建的项目文件夹,进入您在最后一个配方中创建的名为
model的目录。您应该会在那里看到一个model文件。 -
接下来,创建一个测试文件。转到您在上一个配方的项目文件夹中创建的
data文件夹。用以下数据创建一个名为weather.numeric.test.csv的测试文件:outlook,temperature,humidity,windy,play overcast,90,80,TRUE,yes overcast,95,88,FALSE,yes rainy,67,78,TRUE,no rainy,90,97,FALSE,no sunny,50,67,FALSE,yes sunny,67,75,TRUE,no -
在名为 mahout 的 Eclipse 项目中,您应该会在
src/main/java folder中看到名为chap7.science.data的包。这个包是在前一个配方中创建的。在这个包中创建一个名为OnlineLogisticRegressionTest.java的 Java 类。双击要编辑的 Java 类文件。
怎么做...
-
创建
class:public class OnlineLogisticRegressionTest { -
声明几个类变量。首先,创建两个变量来保存您的测试文件
data和model的路径(您在上一个菜谱中创建的):private static String inputFile = "data/weather.numeric.test.csv"; private static String modelFile = "model/model"; -
开始创建你的
main方法:public static void main(String[] args) throws Exception { -
创建一个类类型 AUC 的变量,因为您将计算您的分类器的曲线下面积 ( AUC )作为性能指标:
Auc auc = new Auc(); -
接下来,从
model文件中读取并加载在线逻辑回归算法的参数:LogisticModelParameters params = LogisticModelParameters.loadFrom(new File(modelFile)); -
创建一个变量来读取测试数据文件:
CsvRecordFactory csv = params.getCsvRecordFactory(); -
创建一个
onlinelogisticregression分类器:OnlineLogisticRegression olr = params.createRegression(); -
现在读取测试数据文件:
InputStream in = new FileInputStream(new File(inputFile)); BufferedReader reader = new BufferedReader(new InputStreamReader(in, Charsets.UTF_8)); -
测试数据文件的第一行是文件头或特性列表。因此,您将从分类中忽略这一行,并读取下一行(或行或数据点):
String line = reader.readLine(); csv.firstLine(line); line = reader.readLine(); -
您可能希望在控制台上显示分类结果。为此创建一个
PrintWriter变量:
```java
PrintWriter output=new PrintWriter(new
OutputStreamWriter(System.out, Charsets.UTF_8), true);
```
11. 您将打印预测类、model's output和log likelihood。创建标题并在控制台上打印:
```java
output.println(""class","model-output","log-likelihood"");
```
12. 现在遍历每一个不为空的行:
```java
while (line != null) {
```
13. 为您的测试数据:
```java
Vector vector = new
SequentialAccessSparseVector(params.getNumFeatures());
```
创建特性`vector`
14. 创建一个变量来保存每行/数据点的实际classvalue:
```java
int classValue = csv.processLine(line, vector);
```
15. 对测试数据点进行分类,从分类器
```java
double score = olr.classifyScalarNoLink(vector);
```
中获取`score`
16. 在控制台上打印以下内容-classValue、score和log likelihood :
```java
output.printf(Locale.ENGLISH, "%d,%.3f,%.6f%n", classValue,
score, olr.logLikelihood(classValue, vector));
```
17. 将score和classvalue添加到AUC变量:
```java
auc.add(classValue, score);
```
18. 阅读下一行并关闭循环:
```java
line = reader.readLine();
}
```
19. 关闭reader :
```java
reader.close();
```
20. 现在让我们打印您的分类的输出。首先,打印AUC :
```java
output.printf(Locale.ENGLISH, "AUC = %.2f%n", auc.auc());
```
21. 接下来,你将打印出你的分类中的困惑。为此制造混乱matrix。由于训练/测试数据有两类,你会有一个 2x2 混淆matrix :
```java
Matrix matrix = auc.confusion();
output.printf(Locale.ENGLISH, "confusion: [[%.1f, %.1f], [%.1f,
%.1f]]%n", matrix.get(0, 0), matrix.get(1, 0), matrix.get(0,
1), matrix.get(1, 1));
```
22. 保存matrix中的熵值。您不需要为此创建一个新的matrix变量,但是如果您愿意,您可以这样做:
```java
matrix = auc.entropy();
output.printf(Locale.ENGLISH, "entropy: [[%.1f, %.1f], [%.1f,
%.1f]]%n", matrix.get(0, 0), matrix.get(1, 0), matrix.get(0,
1), matrix.get(1, 1));
```
23. 关闭main方法和类:
}
}
食谱的完整代码如下:
package chap7.science.data;
import com.google.common.base.Charsets;
import org.apache.mahout.math.Matrix;
import org.apache.mahout.math.SequentialAccessSparseVector;
import org.apache.mahout.math.Vector;
import org.apache.mahout.classifier.evaluation.Auc;
import org.apache.mahout.classifier.sgd.CsvRecordFactory;
import org.apache.mahout.classifier.sgd.LogisticModelParameters;
import org.apache.mahout.classifier.sgd.OnlineLogisticRegression;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.util.Locale;
public class OnlineLogisticRegressionTest {
private static String inputFile = "data/weather.numeric.test.csv";
private static String modelFile = "model/model";
public static void main(String[] args) throws Exception {
Auc auc = new Auc();
LogisticModelParameters params =
LogisticModelParameters.loadFrom(new File(modelFile));
CsvRecordFactory csv = params.getCsvRecordFactory();
OnlineLogisticRegression olr = params.createRegression();
InputStream in = new FileInputStream(new File(inputFile));
BufferedReader reader = new BufferedReader(new
InputStreamReader(in, Charsets.UTF_8));
String line = reader.readLine();
csv.firstLine(line);
line = reader.readLine();
PrintWriter output=new PrintWriter(new
OutputStreamWriter(System.out, Charsets.UTF_8), true);
output.println(""class","model-output","log-likelihood"");
while (line != null) {
Vector vector = new
SequentialAccessSparseVector(params.getNumFeatures());
int classValue = csv.processLine(line, vector);
double score = olr.classifyScalarNoLink(vector);
output.printf(Locale.ENGLISH, "%d,%.3f,%.6f%n", classValue,
score, olr.logLikelihood(classValue, vector));
auc.add(classValue, score);
line = reader.readLine();
}
reader.close();
output.printf(Locale.ENGLISH, "AUC = %.2f%n", auc.auc());
Matrix matrix = auc.confusion();
output.printf(Locale.ENGLISH, "confusion: [[%.1f, %.1f], [%.1f,
%.1f]]%n", matrix.get(0, 0), matrix.get(1, 0), matrix.get(0,
1), matrix.get(1, 1));
matrix = auc.entropy();
output.printf(Locale.ENGLISH, "entropy: [[%.1f, %.1f], [%.1f,
%.1f]]%n", matrix.get(0, 0), matrix.get(1, 0), matrix.get(0,
1), matrix.get(1, 1));
}
}
如果运行该代码,输出将如下所示:
"class","model-output","log-likelihood"
1,119.133,0.000000
1,123.028,0.000000
0,15.888,-15.887942
0,63.213,-100.000000
1,-6.692,-6.693089
0,24.286,-24.286465
AUC = 0.67
confusion: [[0.0, 1.0], [3.0, 2.0]]
entropy: [[NaN, NaN], [0.0, -9.2]]
使用 Apache Spark 解决简单的文本挖掘问题
根据 Apache Spark 网站,Spark 在内存中运行程序的速度比 Hadoop MapReduce 快 100 倍,在磁盘上快 10 倍。一般来说,Apache Spark 是一个开源的集群计算框架。它的处理引擎提供了良好的速度和易用性,并为数据科学家提供了复杂的分析。
在这个菜谱中,我们将演示如何使用 Apache Spark 来解决非常简单的数据问题。当然,数据问题仅仅是虚拟的问题,而不是真实世界的问题,但是这可以作为一个起点,让您直观地理解 Apache Spark 在大规模使用时的用法。
准备就绪
-
In Eclipse, create a new Maven project. The author had Eclipse Mars set up. To do so, go to File. Then select New and Other...:
-
Expand Maven from the wizard and select Maven Project. Click on Next until you reach the window where Eclipse prompts you to provide an Artifact Id. Type in
mlibas the Artifact Id, and the grayed-out Finish button will become visible. Click on Finish. This will create a Maven project for you namedmlib: -
Double-click on
pom.xmlfrom your Eclipse Package Explorer to edit: -
点击
pom.xml选项卡。现在你会在屏幕上看到pom.xml文件。将下面几行放到您的<dependencies>...</dependencies>标签内的pom.xml中,并保存它。这将自动下载依赖 JAR 文件到您的项目:<dependency> <groupId>org.apache.spark</groupId> <artifactId>spark-mllib_2.10</artifactId> <version>1.3.1</version> </dependency> -
Create a package named
com.data.big.mlibin your project undersrc/main/java directory: -
在 Eclipse 中右键单击项目名称,选择 New ,然后选择 Folder。您将创建一个名为
data的文件夹,用于存放该配方的输入数据文件。 -
You will be using the literature of William Shakespeare in text format. Open a browser and put the link norvig.com/ngrams/. This will open a page named Natural Language Corpus Data: Beautiful Data. In the Files for Download section, you will find a .txt file named
shakespeare. Download this file anywhere in your system: -
在您创建的包中,创建一个名为
SparkTest的 Java 类文件。双击开始在其中编写代码。
怎么做...
-
创建您的类:
public class SparkTest { -
开始编写你的
main方法:public static void main( String[] args ){ -
首先,获取输入数据文件的路径。这是您下载的莎士比亚文学文件,保存在项目的 data 文件夹中:
String inputFile = "data/shakespeare.txt"; -
火花属性用于控制应用程序设置,并为每个应用程序单独配置。设置这些属性的一种方法是使用传递给 SparkContext 的
SparkConf。SparkConf允许您配置一些常用属性:SparkConf configuration = new SparkConf().setMaster("local[4]").setAppName("My App"); JavaSparkContext sparkContext = new JavaSparkContext(configuration); -
注意,如果我们使用
local[2],它将实现最小的并行性。上述语法使应用程序能够运行四个线程。 -
JavaRDD 是一个分布式对象集合。创建一个 RDD 对象。该方法中该对象的主要用途是收集
shakespeare.txt文件中的空行:JavaRDD<String> rdd = sparkContext.textFile(inputFile).cache();提示
如果我们使用
local[*],火花将使用系统的所有核心 -
统计输入数据文件中空行的行数:
long emptyLines = rdd.filter(new Function<String,Boolean>(){ private static final long serialVersionUID = 1L; public Boolean call(String s){ return s.length() == 0; } }).count(); -
在控制台上打印文件
System.out.println("Empty Lines: " + emptyLines);中
emptylines的编号 -
Next, create the following code snippet to retrieve the word frequencies from the input data file:
JavaPairRDD<String, Integer> wordCounts = rdd .flatMap(s -> Arrays.asList(s.toLowerCase().split(" "))) .mapToPair(word -> new Tuple2<>(word, 1)) .reduceByKey((a, b) -> a + b);Note
One of the reasons for choosing Apache Spark instead of MapReduce is that it requires less code to achieve the same thing. For example, the lines of code in this step retrieve words and their frequencies from a text document. The same effect can be achieved by using more than 100 lines of MapReduce code, as shown below: Hadoop.apache.org/docs/r1.2.1… .
-
使用
wordCountsRDD,您可以收集单词和它们的频率作为地图,然后迭代地图并打印单词-频率对:
```java
Map<String, Integer> wordMap = wordCounts.collectAsMap();
for (Entry<String, Integer> entry : wordMap.entrySet()) {
System.out.println("Word = " + entry.getKey() + ", Frequency
= " + entry.getValue());
}
```
11. 关闭您创建的sparkContext:
```java
sparkContext.close();
```
12. 关闭main方法和类:
}
}
食谱的完整代码如下:
package com.data.big.mlib;
import java.util.Arrays;
import java.util.Map;
import java.util.Map.Entry;
import org.apache.spark.SparkConf;
import org.apache.spark.api.java.JavaPairRDD;
import org.apache.spark.api.java.JavaRDD;
import org.apache.spark.api.java.JavaSparkContext;
import org.apache.spark.api.java.function.Function;
import scala.Tuple2;
public class SparkTest {
public static void main( String[] args ){
String inputFile = "data/shakespeare.txt";
SparkConf configuration = new
SparkConf().setMaster("local[4]").setAppName("My App");
JavaSparkContext sparkContext = new
JavaSparkContext(configuration);
JavaRDD<String> rdd = sparkContext.textFile(inputFile).cache();
long emptyLines = rdd.filter(new Function<String,Boolean>(){
private static final long serialVersionUID = 1L;
public Boolean call(String s){
return s.length() == 0;
}
}).count();
System.out.println("Empty Lines: " + emptyLines);
JavaPairRDD<String, Integer> wordCounts = rdd
.flatMap(s -> Arrays.asList(s.toLowerCase().split(" ")))
.mapToPair(word -> new Tuple2<>(word, 1))
.reduceByKey((a, b) -> a + b);
Map<String, Integer> wordMap = wordCounts.collectAsMap();
for (Entry<String, Integer> entry : wordMap.entrySet()) {
System.out.println("Word = " + entry.getKey() + ", Frequency
= " + entry.getValue());
}
sparkContext.close();
}
}
如果运行该代码,部分输出将如下所示:
Empty Lines: 35941
......................................................................................................
Word = augustus, Frequency = 4
Word = bucklers, Frequency = 3
Word = guilty, Frequency = 66
Word = thunder'st, Frequency = 1
Word = hermia's, Frequency = 7
Word = sink, Frequency = 37
Word = burn, Frequency = 76
Word = relapse, Frequency = 2
Word = boar, Frequency = 16
Word = cop'd, Frequency = 2
......................................................................................................
注意
能鼓励用户使用 Apache Spark 而不是 MapReduce 的好文章可以在这里找到:https://www . mapr . com/blog/5-minute-guide-understanding-significance-Apache-Spark。
使用带 MLib 的 KMeans 算法进行聚类
在本食谱中,我们将演示如何使用带有 MLib 的 KMeans 算法对没有标签的数据点进行聚类。正如本章介绍中所讨论的,MLib 是 Apache Spark 的机器学习组件,是 Apache Mahout 的一个有竞争力(甚至更好)的替代方案。
准备就绪
- 您将使用您在前一个菜谱中创建的 Maven 项目(用 Apache Spark 解决简单的文本挖掘问题)。如果您还没有这样做,请遵循该配方的准备好部分中的步骤 1-6。
- 进入https://github . com/Apache/spark/blob/master/data/ml lib/k means _ data . txt,下载数据并另存为
km-data.txt在您按照步骤 1 中的说明创建的项目的数据文件夹中。或者,您可以在项目的 data 文件夹中创建一个名为km-data.txt的文本文件,并从上述 URL 复制粘贴数据。 - 在您创建的包中,创建一个名为
KMeansClusteringMlib.java的 Java 类文件。双击开始在其中编写代码。
现在,您已经准备好进行一些编码了。
怎么做...
-
创建一个名为
KMeansClusteringMlib:public class KMeansClusteringMlib {的类
-
开始编写你的
main方法:public static void main( String[] args ){ -
创建一个 Spark 配置,并使用该配置创建一个 Spark 上下文。注意,如果我们使用
local[2],它将实现最小的并行性。以下语法使应用程序能够运行四个线程:SparkConf configuration = new SparkConf().setMaster("local[4]").setAppName("K-means Clustering"); JavaSparkContext sparkContext = new JavaSparkContext(configuration); -
现在您将加载并解析您的输入数据:
String path = "data/km-data.txt"; -
JavaRDD是对象的分布式集合。创建一个 RDD 对象来读取数据文件:JavaRDD<String> data = sparkContext.textFile(path); -
现在,您需要从前面的 RDD 中读取数据值,这些值由空格分隔。将这些数据值解析并读取到另一个 RDD:
JavaRDD<Vector> parsedData = data.map( new Function<String, Vector>() { private static final long serialVersionUID = 1L; public Vector call(String s) { String[] sarray = s.split(" "); double[] values = new double[sarray.length]; for (int i = 0; i < sarray.length; i++) values[i] = Double.parseDouble(sarray[i]); return Vectors.dense(values); } } ); parsedData.cache(); -
现在为 KMeans 聚类算法定义几个参数。我们将只使用两个聚类来分离数据点,最多迭代 10 次。连同解析的数据一起,使用参数值创建一个集群器:
int numClusters = 2; int iterations = 10; KMeansModel clusters = KMeans.train(parsedData.rdd(), numClusters, iterations); -
计算聚类器集合内的误差平方和:
double sse = clusters.computeCost(parsedData.rdd()); System.out.println("Sum of Squared Errors within set = " + sse); -
最后,关闭
sparkContext、main方法和类:
sparkContext.close();
}
}
食谱的完整代码如下:
package com.data.big.mlib;
import org.apache.spark.api.java.*;
import org.apache.spark.api.java.function.Function;
import org.apache.spark.mllib.clustering.KMeans;
import org.apache.spark.mllib.clustering.KMeansModel;
import org.apache.spark.mllib.linalg.Vector;
import org.apache.spark.mllib.linalg.Vectors;
import org.apache.spark.SparkConf;
public class KMeansClusteringMlib {
public static void main( String[] args ){
SparkConf configuration = new
SparkConf().setMaster("local[4]").setAppName("K-means
Clustering");
JavaSparkContext sparkContext = new
JavaSparkContext(configuration);
// Load and parse data
String path = "data/km-data.txt";
JavaRDD<String> data = sparkContext.textFile(path);
JavaRDD<Vector> parsedData = data.map(
new Function<String, Vector>() {
private static final long serialVersionUID = 1L;
public Vector call(String s) {
String[] sarray = s.split(" ");
double[] values = new double[sarray.length];
for (int i = 0; i < sarray.length; i++)
values[i] = Double.parseDouble(sarray[i]);
return Vectors.dense(values);
}
}
);
parsedData.cache();
// Cluster the data into two classes using KMeans
int numClusters = 2;
int iterations = 10;
KMeansModel clusters = KMeans.train(parsedData.rdd(),
numClusters, iterations);
// Evaluate clustering by computing Within Set Sum of Squared
Errors
double sse = clusters.computeCost(parsedData.rdd());
System.out.println("Sum of Squared Errors within set = " + sse);
sparkContext.close();
}
}
如果运行该代码,输出将如下所示:
Sum of Squared Errors within set = 0.11999999999994547
使用 MLib 创建线性回归模型
在本菜谱中,您将了解如何使用线性回归模型来构建 MLib 模型。
准备就绪
- 您将使用您在名为的菜谱中创建的 Maven 项目,通过 Apache Spark 解决简单的文本挖掘问题。如果您还没有这样做,那么请按照该食谱的准备部分中的步骤 1-6 进行操作。
- 转到https://github . com/Apache/spark/blob/master/data/ml lib/ridge-data/lpsa . data,下载数据,并另存为按照步骤 1 中的说明创建的项目的数据文件夹中的
lr-data.txt。或者,您可以在项目的 data 文件夹中创建一个名为lr-data.txt的文本文件,并从上述 URL 复制粘贴数据。 - 在您创建的包中,创建一个名为
LinearRegressionMlib.java的 Java 类文件。双击开始在其中编写代码。
现在,您已经准备好进行一些编码了。
怎么做...
-
创建一个名为
LinearRegressionMlib:public class LinearRegressionMlib {的类
-
开始编写你的
main方法:public static void main(String[] args) { -
创建一个 Spark 配置,并使用该配置创建一个 Spark 上下文。注意,如果我们使用
local[2],它将实现最小的并行性。以下语法使应用程序能够运行四个线程:SparkConf configuration = new SparkConf().setMaster("local[4]").setAppName("Linear Regression"); JavaSparkContext sparkContext = new JavaSparkContext(configuration); -
现在您将加载并解析您的输入数据:
String inputData = "data/lr-data.txt"; -
JavaRDD是对象的分布式集合。创建一个 RDD 对象来读取数据文件:JavaRDD<String> data = sparkContext.textFile(inputData); -
现在,您需要从上述 RDD 中读取数据值。输入数据由逗号分隔的两部分组成。在第二部分中,这些功能由空格分隔。标记点是输入数据中每一行的第一部分。解析这些数据值并将其读取到另一个 RDD。用特征创建特征向量。将特征向量与标记点放在一起:
JavaRDD<LabeledPoint> parsedData = data.map( new Function<String, LabeledPoint>() { private static final long serialVersionUID = 1L; public LabeledPoint call(String line) { String[] parts = line.split(","); String[] features = parts[1].split(" "); double[] featureVector = new double[features.length]; for (int i = 0; i < features.length - 1; i++){ featureVector[i] = Double.parseDouble(features[i]); } return new LabeledPoint(Double.parseDouble(parts[0]), Vectors.dense(featureVector)); } } ); parsedData.cache(); -
接下来,您将使用 10 次迭代来构建线性回归模型。使用特征向量、标记点和关于迭代次数的信息创建模型:
int iterations = 10; final LinearRegressionModel model = LinearRegressionWithSGD.train(JavaRDD.toRDD(parsedData), iterations); -
然后,您将使用该模型获得预测,并将它们放入另一个名为 predictions 的 RDD 变量中。该模型将根据给定的要素集预测一个值,并返回预测值和实际标注。请注意,此时您将获得的预测是针对您的训练集中的数据点的预测(
lr-data.txt)。Tuple2 包含回归预测值和实际值:JavaRDD<Tuple2<Double, Double>> predictions = parsedData.map( new Function<LabeledPoint, Tuple2<Double, Double>>() { private static final long serialVersionUID = 1L; public Tuple2<Double, Double> call(LabeledPoint point) { double prediction = model.predict(point.features()); return new Tuple2<Double, Double>(prediction, point.label()); } } ); -
最后,计算训练数据的线性回归模型的均方误差。对于每个数据点,误差是模型预测值与数据集中提到的实际值之差的平方。最后,平均每个数据点的误差:
double mse = new JavaDoubleRDD(predictions.map( new Function<Tuple2<Double, Double>, Object>() { private static final long serialVersionUID = 1L; public Object call(Tuple2<Double, Double> pair) { return Math.pow(pair._1() - pair._2(), 2.0); } } ).rdd()).mean(); System.out.println("training Mean Squared Error = " + mse); -
最后,关闭
sparkContext、main方法和类:
sparkContext.close();
}
}
食谱的完整代码将是:
package com.data.big.mlib;
import scala.Tuple2;
import org.apache.spark.api.java.*;
import org.apache.spark.api.java.function.Function;
import org.apache.spark.mllib.linalg.Vectors;
import org.apache.spark.mllib.regression.LabeledPoint;
import org.apache.spark.mllib.regression.LinearRegressionModel;
import org.apache.spark.mllib.regression.LinearRegressionWithSGD;
import org.apache.spark.SparkConf;
public class LinearRegressionMlib {
public static void main(String[] args) {
SparkConf configuration = new
SparkConf().setMaster("local[4]").setAppName("Linear
Regression");
JavaSparkContext sparkContext = new
JavaSparkContext(configuration);
// Load and parse the data
String inputData = "data/lr-data.txt";
JavaRDD<String> data = sparkContext.textFile(inputData);
JavaRDD<LabeledPoint> parsedData = data.map(
new Function<String, LabeledPoint>() {
private static final long serialVersionUID = 1L;
public LabeledPoint call(String line) {
String[] parts = line.split(",");
String[] features = parts[1].split(" ");
double[] featureVector = new
double[features.length];
for (int i = 0; i < features.length - 1; i++){
featureVector[i] =
Double.parseDouble(features[i]);
}
return new LabeledPoint(Double.parseDouble(parts[0]),
Vectors.dense(featureVector));
}
}
);
parsedData.cache();
// Building the model
int iterations = 10;
final LinearRegressionModel model =
LinearRegressionWithSGD.train(JavaRDD.toRDD(parsedData),
iterations);
// Evaluate model on training examples and compute training
error
JavaRDD<Tuple2<Double, Double>> predictions = parsedData.map(
new Function<LabeledPoint, Tuple2<Double, Double>>() {
private static final long serialVersionUID = 1L;
public Tuple2<Double, Double> call(LabeledPoint point) {
double prediction = model.predict(point.features());
return new Tuple2<Double, Double>(prediction,
point.label());
}
}
);
double mse = new JavaDoubleRDD(predictions.map(
new Function<Tuple2<Double, Double>, Object>() {
private static final long serialVersionUID = 1L;
public Object call(Tuple2<Double, Double> pair) {
return Math.pow(pair._1() - pair._2(), 2.0);
}
}
).rdd()).mean();
System.out.println("training Mean Squared Error = " + mse);
sparkContext.close();
}
}
运行该代码时,其输出如下:
training Mean Squared Error = 6.487093790021849
利用 MLib 对随机森林模型的数据点进行分类
在这个菜谱中,我们将演示如何使用 MLib 的随机森林算法来分类数据点。
准备就绪
- 您将使用您在名为的菜谱中创建的 Maven 项目,通过 Apache Spark 解决简单的文本挖掘问题。如果您还没有这样做,那么请按照该食谱的准备部分中的步骤 1-6 进行操作。
- 转到https://github . com/Apache/spark/blob/master/data/ml lib/sample _ binary _ class ification _ data . txt,下载数据,并另存为
rf-data.txt在您按照步骤 1 中的说明创建的项目的数据文件夹中。或者,您可以在项目的 data 文件夹中创建一个名为rf-data.txt的文本文件,并从上述 URL 复制粘贴数据。 - 在您创建的包中,创建一个名为
RandomForestMlib.java的 Java 类文件。双击开始在其中编写代码。
怎么做...
-
创建一个名为
RandomForestMlib:public class RandomForestMlib {的类
-
开始编写你的
main方法。public static void main(String args[]){ -
创建一个 Spark 配置,并使用该配置创建一个 Spark 上下文。注意,如果我们使用
local[2],它将实现最小的并行性。以下语法使应用程序能够运行四个线程:SparkConf configuration = new SparkConf().setMaster("local[4]").setAppName("Random Forest"); JavaSparkContext sparkContext = new JavaSparkContext(configuration); -
现在您将加载并解析您的输入数据:
String input = "data/rf-data.txt"; -
通过将输入文件加载为 LibSVM 文件并将其放入 RDD 来读取数据。
JavaRDD<LabeledPoint> data = MLUtils.loadLibSVMFile(sparkContext.sc(), input).toJavaRDD(); -
您将使用 70%的数据来训练模型,30%的数据作为模型的测试数据。数据的选择将是随机的。
JavaRDD<LabeledPoint>[] dataSplits = data.randomSplit(new double[]{0.7, 0.3}); JavaRDD<LabeledPoint> trainingData = dataSplits[0]; JavaRDD<LabeledPoint> testData = dataSplits[1]; -
现在,您将配置一些参数来设置随机森林,以便根据训练数据生成模型。您需要定义数据点可以拥有的数量类别。您还需要为名义要素创建地图。您可以定义森林中的树木数量。如果您不知道选择什么作为分类器的特征子集选择过程,您可以选择
"auto"。其余四个参数是森林结构所必需的。Integer classes = 2; HashMap<Integer, Integer> nominalFeatures = new HashMap<Integer, nteger>(); Integer trees = 3; String featureSubsetProcess = "auto"; String impurity = "gini"; Integer maxDepth = 3; Integer maxBins = 20; Integer seed = 12345; -
使用这些参数,创建一个
RandomForest分类器。final RandomForestModel rf = RandomForest.trainClassifier(trainingData, classes, nominalFeatures, trees, featureSubsetProcess, impurity, maxDepth, maxBins, seed); -
下一步,使用该模型预测给定特征向量的数据点的类别标签。
Tuple2<Double,Double>包含每个数据点的预测值和实际类别值:JavaPairRDD<Double, Double> label = testData.mapToPair(new PairFunction<LabeledPoint, Double, Double>() { private static final long serialVersionUID = 1L; public Tuple2<Double, Double> call(LabeledPoint p) { return new Tuple2<Double, Double> (rf.predict(p.features()), p.label()); } }); -
最后,计算预测的误差。您只需计算预测值与实际值不匹配的次数,然后通过除以测试实例的总数得到平均值:
```java
Double error =
1.0 * label.filter(new Function<Tuple2<Double, Double>,
Boolean>() {
private static final long serialVersionUID = 1L;
public Boolean call(Tuple2<Double, Double> pl) {
return !pl._1().equals(pl._2());
}
}).count() / testData.count();
```
11. 在控制台上打印出测试错误。您可能还想看看从训练数据中学习到的实际的RandomForest模型:
```java
System.out.println("Test Error: " + error);
System.out.println("Learned classification forest model:\n" +
rf.toDebugString());
```
12. 关闭sparkContext、main方法和类:
sparkContext.close();
}
}
食谱的完整代码如下:
package com.data.big.mlib;
import scala.Tuple2;
import java.util.HashMap;
import org.apache.spark.SparkConf;
import org.apache.spark.api.java.JavaPairRDD;
import org.apache.spark.api.java.JavaRDD;
import org.apache.spark.api.java.JavaSparkContext;
import org.apache.spark.api.java.function.Function;
import org.apache.spark.api.java.function.PairFunction;
import org.apache.spark.mllib.regression.LabeledPoint;
import org.apache.spark.mllib.tree.RandomForest;
import org.apache.spark.mllib.tree.model.RandomForestModel;
import org.apache.spark.mllib.util.MLUtils;
public class RandomForestMlib {
public static void main(String args[]){
SparkConf configuration = new
SparkConf().setMaster("local[4]").setAppName("Random Forest");
JavaSparkContext sparkContext = new
JavaSparkContext(configuration);
// Load and parse the data file.
String input = "data/rf-data.txt";
JavaRDD<LabeledPoint> data =
MLUtils.loadLibSVMFile(sparkContext.sc(), input).toJavaRDD();
// Split the data into training and test sets (30% held out for
testing)
JavaRDD<LabeledPoint>[] dataSplits = data.randomSplit(new
double[]{0.7, 0.3});
JavaRDD<LabeledPoint> trainingData = dataSplits[0];
JavaRDD<LabeledPoint> testData = dataSplits[1];
// Train a RandomForest model.
Integer classes = 2;
HashMap<Integer, Integer> nominalFeatures = new HashMap<Integer,
Integer>();// Empty categoricalFeaturesInfo indicates all
features are continuous.
Integer trees = 3; // Use more in practice.
String featureSubsetProcess = "auto"; // Let the algorithm
choose.
String impurity = "gini";
Integer maxDepth = 3;
Integer maxBins = 20;
Integer seed = 12345;
final RandomForestModel rf =
RandomForest.trainClassifier(trainingData, classes,
nominalFeatures, trees, featureSubsetProcess, impurity,
maxDepth, maxBins, seed);
// Evaluate model on test instances and compute test error
JavaPairRDD<Double, Double> label =
testData.mapToPair(new PairFunction<LabeledPoint, Double,
Double>() {
private static final long serialVersionUID = 1L;
public Tuple2<Double, Double> call(LabeledPoint p) {
return new Tuple2<Double, Double>
(rf.predict(p.features()), p.label());
}
});
Double error =
1.0 * label.filter(new Function<Tuple2<Double, Double>,
Boolean>() {
private static final long serialVersionUID = 1L;
public Boolean call(Tuple2<Double, Double> pl) {
return !pl._1().equals(pl._2());
}
}).count() / testData.count();
System.out.println("Test Error: " + error);
System.out.println("Learned classification forest model:\n" +
rf.toDebugString());
sparkContext.close();
}
}
如果运行该代码,输出将如下所示:
Test Error: 0.034482758620689655
Learned classification forest model:
TreeEnsembleModel classifier with 3 trees
Tree 0:
If (feature 427 <= 0.0)
If (feature 407 <= 0.0)
Predict: 0.0
Else (feature 407 > 0.0)
Predict: 1.0
Else (feature 427 > 0.0)
Predict: 0.0
Tree 1:
If (feature 405 <= 0.0)
If (feature 624 <= 253.0)
Predict: 0.0
Else (feature 624 > 253.0)
If (feature 650 <= 0.0)
Predict: 0.0
Else (feature 650 > 0.0)
Predict: 1.0
Else (feature 405 > 0.0)
If (feature 435 <= 0.0)
If (feature 541 <= 0.0)
Predict: 1.0
Else (feature 541 > 0.0)
Predict: 0.0
Else (feature 435 > 0.0)
Predict: 1.0
Tree 2:
If (feature 271 <= 72.0)
If (feature 323 <= 0.0)
Predict: 0.0
Else (feature 323 > 0.0)
Predict: 1.0
Else (feature 271 > 72.0)
If (feature 414 <= 0.0)
If (feature 159 <= 124.0)
Predict: 0.0
Else (feature 159 > 124.0)
Predict: 1.0
Else (feature 414 > 0.0)
Predict: 0.0