Hadoop 的 MapReduce-KNN - 运行 来自一个数据文件的多个测试用例
MapReduce-KNN for Hadoop - run multiple test cases from one data file
背景: [跳至下一节了解确切问题]
我目前在大学里作为一个小项目在 Hadoop 上工作(不是强制性项目,我这样做是因为我想做)。
我的计划是在其中一个实验室(Master + 4 Slaves)中使用 5 台 PC 在大型数据集上 运行 KNN 算法找出 运行ning 时间等.
我知道我可以在 Internet 上找到基本代码,我确实找到了它 (https://github.com/matt-hicks/MapReduce-KNN)。 运行 对于单个测试用例来说没问题,但我拥有的是一个非常大的测试用例,有数百个测试用例。因此,我需要为每个测试用例重复相同的代码。
问题:
tl;dr:我有一个 KNN 程序,一次只接受一个测试用例,但我想让它迭代,以便它可以处理多个测试用例。
我的解决方案:
我对此不是很有经验,根据我所知道的基础知识,我决定将变量和映射制作成变量数组和映射数组。
所以这个:
public static class KnnMapper extends Mapper<Object, Text, NullWritable, DoubleString>
{
DoubleString distanceAndModel = new DoubleString();
TreeMap<Double, String> KnnMap = new TreeMap<Double, String>();
// Declaring some variables which will be used throughout the mapper
int K;
double normalisedSAge;
double normalisedSIncome;
String sStatus;
String sGender;
double normalisedSChildren;
变成了这个:
DoubleString distanceAndModel = new DoubleString();
TreeMap<Double, String>[] KnnMap = new TreeMap<Double, String>[1000];
// Declaring some variables which will be used throughout the mapper
int[] K = new int[1000];
double[] normalisedSAge = new double[1000];
double[] normalisedSIncome = new double[1000];
String[] sStatus = new String[1000];
String[] sGender = new String[1000];
double[] normalisedSChildren = new double[1000];
int n = 0;
还有这个:
protected void setup(Context context) throws IOException, InterruptedException
{
if (context.getCacheFiles() != null && context.getCacheFiles().length > 0)
{
// Read parameter file using alias established in main()
String knnParams = FileUtils.readFileToString(new File("./knnParamFile"));
StringTokenizer st = new StringTokenizer(knnParams, ",");
// Using the variables declared earlier, values are assigned to K and to the test dataset, S.
// These values will remain unchanged throughout the mapper
K = Integer.parseInt(st.nextToken());
normalisedSAge = normalisedDouble(st.nextToken(), minAge, maxAge);
normalisedSIncome = normalisedDouble(st.nextToken(), minIncome, maxIncome);
sStatus = st.nextToken();
sGender = st.nextToken();
normalisedSChildren = normalisedDouble(st.nextToken(), minChildren, maxChildren);
}
}
变成了这个:
protected void setup(Context context) throws IOException, InterruptedException
{
if (context.getCacheFiles() != null && context.getCacheFiles().length > 0)
{
// Read parameter file using alias established in main()
String knnParams = FileUtils.readFileToString(new File("./knnParamFile"));
//Splitting input File if we hit a newline character or return carriage i.e., Windown Return Key as input
StringTokenizer lineSt = new StringTokenizer(knnParams, "\n\r");
//Running a loop to tokennize each line of inputs or test cases
while(lineSt.hasMoreTokens()){
String nextLine = lineSt.nextToken(); //Converting current line to a string
StringTokenizer st = new StringTokenizer(nextLine, ","); // Tokenizing the current string or singular data
// Using the variables declared earlier, values are assigned to K and to the test dataset, S.
// These values will remain unchanged throughout the mapper
K[n] = Integer.parseInt(st.nextToken());
normalisedSAge[n] = normalisedDouble(st.nextToken(), minAge, maxAge);
normalisedSIncome[n] = normalisedDouble(st.nextToken(), minIncome, maxIncome);
sStatus[n] = st.nextToken();
sGender[n] = st.nextToken();
normalisedSChildren[n] = normalisedDouble(st.nextToken(), minChildren, maxChildren);
n++;
}}
}
减速机等class也是如此。
虽然这是我第一次使用 TreeMaps。我以前研究过和使用过树,但不是地图或 TreeMaps。
我仍然尝试制作它和数组结果是错误的:
/home/hduser/Desktop/knn/KnnPattern.java:81: error: generic array creation TreeMap[] KnnMap = new TreeMap[1000];
^
/home/hduser/Desktop/knn/KnnPattern.java:198: error: incompatible
types: double[] cannot be converted to double
normalisedRChildren, normalisedSAge, normalisedSIncome, sStatus, sGender, normalisedSChildren);
^
/home/hduser/Desktop/knn/KnnPattern.java:238: error: generic array
creation TreeMap[] KnnMap = new TreeMap[1000];
^
/home/hduser/Desktop/knn/KnnPattern.java:283: error: bad operand types
for binary operator '>'
if (KnnMap[num].size() > K)
^ first type: int second type: int[]
现在,我想也许如果我尝试使用 TreeMaps 的链接列表,它可能会起作用。
但是,到目前为止,我基本上在 Uni 中使用 C/C++ 和 Python。 OOP 在这里似乎让人们的生活更轻松,但我不是 100% 确定如何使用它。
我的问题:
是否可以制作 TreeMap 的链接列表?
是否有链接列表替代:
TreeMap<Double, String>[] KnnMap = new TreeMap<Double, String>[1000];
我解决问题的方法是否正确?使代码迭代应该有助于迭代所有测试用例,对吗?
我会尝试错误,尝试让它从那里开始工作。但这是我几天以来一直坚持的事情。
如果之前有人问过这个问题,但我找不到任何东西,所以我不得不写一个问题,我深表歉意。
如果您认为之前已经回答过此问题,请分享任何相关答案的 link。
谢谢!
并且,附带说明:在使用 TreeMaps 时我应该记住的其他任何事情,特别是 linked 的 TreeMaps 列表。
关于错误信息
/home/hduser/Desktop/knn/KnnPattern.java:81: error: generic array creation TreeMap[] KnnMap = new TreeMap[1000]; ^
和
/home/hduser/Desktop/knn/KnnPattern.java:238: error: generic array creation TreeMap[] KnnMap = new TreeMap[1000]; ^
发生这些错误是因为您试图从 Java 不支持的通用组件类型创建实例,因为通用类型在运行时丢失。一种解决方法(如果您确实需要数组)是创建 List
个 TreeMap
对象,然后将其转换为数组:
// TreeMap<Double, String>[] KnnMap = new TreeMap<Double, String>[1000];
List<TreeMap<Double, String>> KnnMapList = new LinkedList<>();
TreeMap<Double, String>[] KnnMap = (TreeMap<Double, String>[]) KnnMapList.toArray();
有关详细信息,请参阅 this 问题。
/home/hduser/Desktop/knn/KnnPattern.java:198: error: incompatible types: double[] cannot be converted to double normalisedRChildren, normalisedSAge, normalisedSIncome, sStatus, sGender, normalisedSChildren); ^
通过查看 GitHub 的源代码,我意识到您可能没有修改方法 KnnMapper#map(Object, Text, Context)
:
中的以下方法调用
double tDist = totalSquaredDistance(normalisedRAge, normalisedRIncome, rStatus, rGender,
normalisedRChildren, normalisedSAge, normalisedSIncome, sStatus, sGender, normalisedSChildren);
应该是
double tDist = totalSquaredDistance(normalisedRAge, normalisedRIncome, rStatus, rGender,
normalisedRChildren, normalisedSAge[n], normalisedSIncome[n], sStatus[n], sGender[n], normalisedSChildren[n]);
但我想这些修改不会给你想要的功能,因为 KnnMapper#map(Object, Text, Context)
只在每个 key/value 对中调用一次,如 here 所述,你可能想调用它 n -次。
具体问题
为了防止进一步的麻烦,我建议你保持 GitHub class 的上层代码不变,只修改 KnnPattern#main(String[])
方法以便它调用作业n 次,如 this 答案中所述。
编辑:示例
这是一种改进的KnnPattern#main(String[])
方法,它逐行读取您的数据文件,创建一个以当前行为内容的临时文件,并以该临时文件作为缓存文件启动一个作业。
(假设您至少使用 Java 7)
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.FileReader;
import java.io.FileWriter;
...
public class KnnPattern
{
...
public static void main(String[] args) throws Exception {
// Create configuration
Configuration conf = new Configuration();
if (args.length != 3) {
System.err.println("Usage: KnnPattern <in> <out> <parameter file>");
System.exit(2);
}
try (final BufferedReader br = new BufferedReader(new FileReader(args[2]))) {
int n = 1;
String line;
while ((line = br.readLine()) != null) {
// create temporary file with content of current line
final File tmpDataFile = File.createTempFile("hadoop-test-", null);
try (BufferedWriter tmpDataWriter = new BufferedWriter(new FileWriter(tmpDataFile))) {
tmpDataWriter.write(line);
tmpDataWriter.flush();
}
// Create job
Job job = Job.getInstance(conf, "Find K-Nearest Neighbour #" + n);
job.setJarByClass(KnnPattern.class);
// Set the third parameter when running the job to be the parameter file and give it an alias
job.addCacheFile(new URI(tmpDataFile.getAbsolutePath() + "#knnParamFile")); // Parameter file containing test data
// Setup MapReduce job
job.setMapperClass(KnnMapper.class);
job.setReducerClass(KnnReducer.class);
job.setNumReduceTasks(1); // Only one reducer in this design
// Specify key / value
job.setMapOutputKeyClass(NullWritable.class);
job.setMapOutputValueClass(DoubleString.class);
job.setOutputKeyClass(NullWritable.class);
job.setOutputValueClass(Text.class);
// Input (the data file) and Output (the resulting classification)
FileInputFormat.addInputPath(job, new Path(args[0]));
FileOutputFormat.setOutputPath(job, new Path(args[1] + "_" + n));
// Execute job
final boolean jobSucceeded = job.waitForCompletion(true);
// clean up
tmpDataFile.delete();
if (!jobSucceeded) {
// return error status if job failed
System.exit(1);
}
++n;
}
}
}
}
背景: [跳至下一节了解确切问题]
我目前在大学里作为一个小项目在 Hadoop 上工作(不是强制性项目,我这样做是因为我想做)。
我的计划是在其中一个实验室(Master + 4 Slaves)中使用 5 台 PC 在大型数据集上 运行 KNN 算法找出 运行ning 时间等.
我知道我可以在 Internet 上找到基本代码,我确实找到了它 (https://github.com/matt-hicks/MapReduce-KNN)。 运行 对于单个测试用例来说没问题,但我拥有的是一个非常大的测试用例,有数百个测试用例。因此,我需要为每个测试用例重复相同的代码。
问题:
tl;dr:我有一个 KNN 程序,一次只接受一个测试用例,但我想让它迭代,以便它可以处理多个测试用例。
我的解决方案:
我对此不是很有经验,根据我所知道的基础知识,我决定将变量和映射制作成变量数组和映射数组。
所以这个:
public static class KnnMapper extends Mapper<Object, Text, NullWritable, DoubleString>
{
DoubleString distanceAndModel = new DoubleString();
TreeMap<Double, String> KnnMap = new TreeMap<Double, String>();
// Declaring some variables which will be used throughout the mapper
int K;
double normalisedSAge;
double normalisedSIncome;
String sStatus;
String sGender;
double normalisedSChildren;
变成了这个:
DoubleString distanceAndModel = new DoubleString();
TreeMap<Double, String>[] KnnMap = new TreeMap<Double, String>[1000];
// Declaring some variables which will be used throughout the mapper
int[] K = new int[1000];
double[] normalisedSAge = new double[1000];
double[] normalisedSIncome = new double[1000];
String[] sStatus = new String[1000];
String[] sGender = new String[1000];
double[] normalisedSChildren = new double[1000];
int n = 0;
还有这个:
protected void setup(Context context) throws IOException, InterruptedException
{
if (context.getCacheFiles() != null && context.getCacheFiles().length > 0)
{
// Read parameter file using alias established in main()
String knnParams = FileUtils.readFileToString(new File("./knnParamFile"));
StringTokenizer st = new StringTokenizer(knnParams, ",");
// Using the variables declared earlier, values are assigned to K and to the test dataset, S.
// These values will remain unchanged throughout the mapper
K = Integer.parseInt(st.nextToken());
normalisedSAge = normalisedDouble(st.nextToken(), minAge, maxAge);
normalisedSIncome = normalisedDouble(st.nextToken(), minIncome, maxIncome);
sStatus = st.nextToken();
sGender = st.nextToken();
normalisedSChildren = normalisedDouble(st.nextToken(), minChildren, maxChildren);
}
}
变成了这个:
protected void setup(Context context) throws IOException, InterruptedException
{
if (context.getCacheFiles() != null && context.getCacheFiles().length > 0)
{
// Read parameter file using alias established in main()
String knnParams = FileUtils.readFileToString(new File("./knnParamFile"));
//Splitting input File if we hit a newline character or return carriage i.e., Windown Return Key as input
StringTokenizer lineSt = new StringTokenizer(knnParams, "\n\r");
//Running a loop to tokennize each line of inputs or test cases
while(lineSt.hasMoreTokens()){
String nextLine = lineSt.nextToken(); //Converting current line to a string
StringTokenizer st = new StringTokenizer(nextLine, ","); // Tokenizing the current string or singular data
// Using the variables declared earlier, values are assigned to K and to the test dataset, S.
// These values will remain unchanged throughout the mapper
K[n] = Integer.parseInt(st.nextToken());
normalisedSAge[n] = normalisedDouble(st.nextToken(), minAge, maxAge);
normalisedSIncome[n] = normalisedDouble(st.nextToken(), minIncome, maxIncome);
sStatus[n] = st.nextToken();
sGender[n] = st.nextToken();
normalisedSChildren[n] = normalisedDouble(st.nextToken(), minChildren, maxChildren);
n++;
}}
}
减速机等class也是如此。
虽然这是我第一次使用 TreeMaps。我以前研究过和使用过树,但不是地图或 TreeMaps。 我仍然尝试制作它和数组结果是错误的:
/home/hduser/Desktop/knn/KnnPattern.java:81: error: generic array creation TreeMap[] KnnMap = new TreeMap[1000]; ^
/home/hduser/Desktop/knn/KnnPattern.java:198: error: incompatible types: double[] cannot be converted to double normalisedRChildren, normalisedSAge, normalisedSIncome, sStatus, sGender, normalisedSChildren); ^
/home/hduser/Desktop/knn/KnnPattern.java:238: error: generic array creation TreeMap[] KnnMap = new TreeMap[1000]; ^
/home/hduser/Desktop/knn/KnnPattern.java:283: error: bad operand types for binary operator '>' if (KnnMap[num].size() > K) ^ first type: int second type: int[]
现在,我想也许如果我尝试使用 TreeMaps 的链接列表,它可能会起作用。
但是,到目前为止,我基本上在 Uni 中使用 C/C++ 和 Python。 OOP 在这里似乎让人们的生活更轻松,但我不是 100% 确定如何使用它。
我的问题:
是否可以制作 TreeMap 的链接列表?
是否有链接列表替代:
TreeMap<Double, String>[] KnnMap = new TreeMap<Double, String>[1000];
我解决问题的方法是否正确?使代码迭代应该有助于迭代所有测试用例,对吗?
我会尝试错误,尝试让它从那里开始工作。但这是我几天以来一直坚持的事情。
如果之前有人问过这个问题,但我找不到任何东西,所以我不得不写一个问题,我深表歉意。 如果您认为之前已经回答过此问题,请分享任何相关答案的 link。
谢谢! 并且,附带说明:在使用 TreeMaps 时我应该记住的其他任何事情,特别是 linked 的 TreeMaps 列表。
关于错误信息
/home/hduser/Desktop/knn/KnnPattern.java:81: error: generic array creation TreeMap[] KnnMap = new TreeMap[1000]; ^
和
/home/hduser/Desktop/knn/KnnPattern.java:238: error: generic array creation TreeMap[] KnnMap = new TreeMap[1000]; ^
发生这些错误是因为您试图从 Java 不支持的通用组件类型创建实例,因为通用类型在运行时丢失。一种解决方法(如果您确实需要数组)是创建 List
个 TreeMap
对象,然后将其转换为数组:
// TreeMap<Double, String>[] KnnMap = new TreeMap<Double, String>[1000];
List<TreeMap<Double, String>> KnnMapList = new LinkedList<>();
TreeMap<Double, String>[] KnnMap = (TreeMap<Double, String>[]) KnnMapList.toArray();
有关详细信息,请参阅 this 问题。
/home/hduser/Desktop/knn/KnnPattern.java:198: error: incompatible types: double[] cannot be converted to double normalisedRChildren, normalisedSAge, normalisedSIncome, sStatus, sGender, normalisedSChildren); ^
通过查看 GitHub 的源代码,我意识到您可能没有修改方法 KnnMapper#map(Object, Text, Context)
:
double tDist = totalSquaredDistance(normalisedRAge, normalisedRIncome, rStatus, rGender,
normalisedRChildren, normalisedSAge, normalisedSIncome, sStatus, sGender, normalisedSChildren);
应该是
double tDist = totalSquaredDistance(normalisedRAge, normalisedRIncome, rStatus, rGender,
normalisedRChildren, normalisedSAge[n], normalisedSIncome[n], sStatus[n], sGender[n], normalisedSChildren[n]);
但我想这些修改不会给你想要的功能,因为 KnnMapper#map(Object, Text, Context)
只在每个 key/value 对中调用一次,如 here 所述,你可能想调用它 n -次。
具体问题
为了防止进一步的麻烦,我建议你保持 GitHub class 的上层代码不变,只修改 KnnPattern#main(String[])
方法以便它调用作业n 次,如 this 答案中所述。
编辑:示例
这是一种改进的KnnPattern#main(String[])
方法,它逐行读取您的数据文件,创建一个以当前行为内容的临时文件,并以该临时文件作为缓存文件启动一个作业。
(假设您至少使用 Java 7)
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.FileReader;
import java.io.FileWriter;
...
public class KnnPattern
{
...
public static void main(String[] args) throws Exception {
// Create configuration
Configuration conf = new Configuration();
if (args.length != 3) {
System.err.println("Usage: KnnPattern <in> <out> <parameter file>");
System.exit(2);
}
try (final BufferedReader br = new BufferedReader(new FileReader(args[2]))) {
int n = 1;
String line;
while ((line = br.readLine()) != null) {
// create temporary file with content of current line
final File tmpDataFile = File.createTempFile("hadoop-test-", null);
try (BufferedWriter tmpDataWriter = new BufferedWriter(new FileWriter(tmpDataFile))) {
tmpDataWriter.write(line);
tmpDataWriter.flush();
}
// Create job
Job job = Job.getInstance(conf, "Find K-Nearest Neighbour #" + n);
job.setJarByClass(KnnPattern.class);
// Set the third parameter when running the job to be the parameter file and give it an alias
job.addCacheFile(new URI(tmpDataFile.getAbsolutePath() + "#knnParamFile")); // Parameter file containing test data
// Setup MapReduce job
job.setMapperClass(KnnMapper.class);
job.setReducerClass(KnnReducer.class);
job.setNumReduceTasks(1); // Only one reducer in this design
// Specify key / value
job.setMapOutputKeyClass(NullWritable.class);
job.setMapOutputValueClass(DoubleString.class);
job.setOutputKeyClass(NullWritable.class);
job.setOutputValueClass(Text.class);
// Input (the data file) and Output (the resulting classification)
FileInputFormat.addInputPath(job, new Path(args[0]));
FileOutputFormat.setOutputPath(job, new Path(args[1] + "_" + n));
// Execute job
final boolean jobSucceeded = job.waitForCompletion(true);
// clean up
tmpDataFile.delete();
if (!jobSucceeded) {
// return error status if job failed
System.exit(1);
}
++n;
}
}
}
}