计算数组索引的平均值并将结果收集到新数组
calculating averages across array indices and collecting results to a new array
我正在尝试 运行 averaged perceptron
它采用每次迭代期间创建的所有权重向量,然后,例如,对于第一个索引,它将采用所有权重的平均值在收敛之前的所有迭代中与索引 1 关联,并使用此平均值作为最终权重结果的输出值,即这成为最终输出数组的索引 1 的值。
所以如果权重是:
迭代 1:[5,10,20]
迭代 2:[3,4,6]
迭代 3:[2,9,12]
结果数组:[3.333,7.666,12.666]
我试图编写代码来执行此操作,但我得到的是:
weights:
[4.356800000000003, 0.5011, 0.502, 0.2078, 0.8042,
weights:
[7.956800000000005, 0.5011, 0.402, 0.1078, 0.8042,
average:
[7.956800000000005, 0.5011, 0.402, 0.1078, 0.8042,
它没有取平均值,只是复制了最后一个权重。这是什么原因?!似乎我正确指定了平均计算但显然没有。我哪里出错了?
代码如下所示:
public static void perceptron( Table< int[] , String , Integer > train_freq_count_against_globo_dict,
Table< int[] , String , Integer > test_freq_count_against_globo_dict,
Set<String> GLOBO_DICT )
{
//store weights to be averaged.
Map<Integer,double[]> cached_weights = new HashMap<Integer,double[]>();
int globo_dict_size = GLOBO_DICT.size();
int number_of_files__train = train_freq_count_against_globo_dict.size();
double[] weights = new double[ globo_dict_size + 1 ];//one for bias
for (int i = 0; i < weights.length; i++)
{
weights[i] = randomNumber(0,1);
}
double[][] feature_matrix__train = new double[ number_of_files__train ][ globo_dict_size ];
int[] outputs__train = new int [ number_of_files__train ];
int z = 0;
for ( Cell< int[] , String , Integer > cell: train_freq_count_against_globo_dict.cellSet() )
{
int[] container_of_feature_vector = cell.getRowKey();
for (int q = 0; q < globo_dict_size; q++)
{
feature_matrix__train[z][q] = container_of_feature_vector[q];
}
outputs__train[z] = String.valueOf( cell.getColumnKey() ).equals(LABEL) ? 1 : 0;
z++;
}
//LEARNING WEIGHTS
double localError, globalError;
int p, iteration, output;
iteration = 0;
do
{
iteration++;
globalError = 0;
//loop through all instances (complete one epoch)
for (p = 0; p < number_of_files__train; p++)
{
// calculate predicted class
output = calculateOutput( theta, weights, feature_matrix__train, p, globo_dict_size );
// difference between predicted and actual class values
localError = outputs__train[p] - output;
//update weights and bias
for (int i = 0; i < globo_dict_size; i++)
{
weights[i] += ( LEARNING_RATE * localError * feature_matrix__train[p][i] );
}
weights[ globo_dict_size ] += ( LEARNING_RATE * localError );
//summation of squared error (error value for all instances)
globalError += (localError*localError);
}
System.out.println("weights: ");
System.out.println(Arrays.toString(weights));
System.out.println();
//store weights for averaging
cached_weights.put( iteration , weights );
}
while(globalError != 0 && iteration<=MAX_ITER);
//compute averages
double[] sums = new double[ globo_dict_size + 1 ];
double[] averages = new double[ globo_dict_size + 1 ];
for (Entry<Integer, double[]> entry : cached_weights.entrySet())
{
double[] value = entry.getValue();
for(int pos=0; pos < globo_dict_size + 1; pos++)
{
sums[ pos ] += value[ pos ];
}
}
for(int pos=0; pos < globo_dict_size + 1; pos++)
{
averages[ pos ] = sums[ pos ] / cached_weights.size();
}
}
我认为问题出在 do while
循环中,您在该循环中用数据填充了 Map 以便稍后进行平均。在循环结束时,您将数组 weights
添加到地图中。但实际上每次迭代都是同一个数组。
因此每次迭代都会覆盖现有 weights
数组的值。该映射包含 x 个键,但它们都引用了与值相同的数组。
这就是为什么最后,当您尝试计算平均值时,您总是得到 "last" 数组的值...因为地图的所有条目都引用同一个数组,因此引用平均值都一样。
您需要做的是为每次迭代在循环内创建一个新数组。看到新数组的值取决于最后一个数组的值,您应该像这样复制现有值(感谢@Soana 的输入):
weights = Arrays.copyOf(weights, weights.length);
然后计算新数组的新值。
希望你明白我的意思。 :-)
将您的要求设为:
so if the weights are:
iteration 1: [5,10,20] iteration 2: [3,4,6] iteration 3: [2,9,12]
resulting array: [3.333,7.666,12.666]
您可以试试下面的方法,它接受任意数量的数组:
private double[] averageArray(int[]... arrays) throws Exception
{
// take the first array as starting point
int[] sums = arrays[0].clone();
for (int i = 1; i < arrays.length; i++)
{
// how can we take average if the inputs are different lengths!
if (arrays[i].length != sums.length)
{
throw new Exception("Input arrays are of differing dimensions!");
}
// add this array to our running sum
for (int j = 0; j < sums.length; j++)
{
sums[j] += arrays[i][j];
}
}
double[] averages = new double[sums.length];
for (int k = 0; k < sums.length; k++)
{
// cast to double to avoid integer division
averages[k] = (double) sums[k] / arrays.length;
}
return averages;
}
这通过了表达您要求的以下单元测试:
@Test
public void averageTest() throws Exception
{
int[] a = { 5, 10, 20 };
int[] b = { 3, 4, 6 };
int[] c = { 2, 9, 12 };
Assert.assertArrayEquals(new double[] { 10.0 / 3, 23.0 / 3, 38.0 / 3 }, averageArray(a, b, c), 0);
}
根据您的代码,此 returns 是双精度 [],但使用 BigDecimals 可能会更好,尤其是因为它看起来精度和准确性对您尝试的任何事情都很重要。
正如其他人所提到的,您的代码很难阅读,尤其是可以使用更好的参数命名。
我正在尝试 运行 averaged perceptron
它采用每次迭代期间创建的所有权重向量,然后,例如,对于第一个索引,它将采用所有权重的平均值在收敛之前的所有迭代中与索引 1 关联,并使用此平均值作为最终权重结果的输出值,即这成为最终输出数组的索引 1 的值。
所以如果权重是:
迭代 1:[5,10,20]
迭代 2:[3,4,6]
迭代 3:[2,9,12]
结果数组:[3.333,7.666,12.666]
我试图编写代码来执行此操作,但我得到的是:
weights:
[4.356800000000003, 0.5011, 0.502, 0.2078, 0.8042,
weights:
[7.956800000000005, 0.5011, 0.402, 0.1078, 0.8042,
average:
[7.956800000000005, 0.5011, 0.402, 0.1078, 0.8042,
它没有取平均值,只是复制了最后一个权重。这是什么原因?!似乎我正确指定了平均计算但显然没有。我哪里出错了?
代码如下所示:
public static void perceptron( Table< int[] , String , Integer > train_freq_count_against_globo_dict,
Table< int[] , String , Integer > test_freq_count_against_globo_dict,
Set<String> GLOBO_DICT )
{
//store weights to be averaged.
Map<Integer,double[]> cached_weights = new HashMap<Integer,double[]>();
int globo_dict_size = GLOBO_DICT.size();
int number_of_files__train = train_freq_count_against_globo_dict.size();
double[] weights = new double[ globo_dict_size + 1 ];//one for bias
for (int i = 0; i < weights.length; i++)
{
weights[i] = randomNumber(0,1);
}
double[][] feature_matrix__train = new double[ number_of_files__train ][ globo_dict_size ];
int[] outputs__train = new int [ number_of_files__train ];
int z = 0;
for ( Cell< int[] , String , Integer > cell: train_freq_count_against_globo_dict.cellSet() )
{
int[] container_of_feature_vector = cell.getRowKey();
for (int q = 0; q < globo_dict_size; q++)
{
feature_matrix__train[z][q] = container_of_feature_vector[q];
}
outputs__train[z] = String.valueOf( cell.getColumnKey() ).equals(LABEL) ? 1 : 0;
z++;
}
//LEARNING WEIGHTS
double localError, globalError;
int p, iteration, output;
iteration = 0;
do
{
iteration++;
globalError = 0;
//loop through all instances (complete one epoch)
for (p = 0; p < number_of_files__train; p++)
{
// calculate predicted class
output = calculateOutput( theta, weights, feature_matrix__train, p, globo_dict_size );
// difference between predicted and actual class values
localError = outputs__train[p] - output;
//update weights and bias
for (int i = 0; i < globo_dict_size; i++)
{
weights[i] += ( LEARNING_RATE * localError * feature_matrix__train[p][i] );
}
weights[ globo_dict_size ] += ( LEARNING_RATE * localError );
//summation of squared error (error value for all instances)
globalError += (localError*localError);
}
System.out.println("weights: ");
System.out.println(Arrays.toString(weights));
System.out.println();
//store weights for averaging
cached_weights.put( iteration , weights );
}
while(globalError != 0 && iteration<=MAX_ITER);
//compute averages
double[] sums = new double[ globo_dict_size + 1 ];
double[] averages = new double[ globo_dict_size + 1 ];
for (Entry<Integer, double[]> entry : cached_weights.entrySet())
{
double[] value = entry.getValue();
for(int pos=0; pos < globo_dict_size + 1; pos++)
{
sums[ pos ] += value[ pos ];
}
}
for(int pos=0; pos < globo_dict_size + 1; pos++)
{
averages[ pos ] = sums[ pos ] / cached_weights.size();
}
}
我认为问题出在 do while
循环中,您在该循环中用数据填充了 Map 以便稍后进行平均。在循环结束时,您将数组 weights
添加到地图中。但实际上每次迭代都是同一个数组。
因此每次迭代都会覆盖现有 weights
数组的值。该映射包含 x 个键,但它们都引用了与值相同的数组。
这就是为什么最后,当您尝试计算平均值时,您总是得到 "last" 数组的值...因为地图的所有条目都引用同一个数组,因此引用平均值都一样。
您需要做的是为每次迭代在循环内创建一个新数组。看到新数组的值取决于最后一个数组的值,您应该像这样复制现有值(感谢@Soana 的输入):
weights = Arrays.copyOf(weights, weights.length);
然后计算新数组的新值。
希望你明白我的意思。 :-)
将您的要求设为:
so if the weights are:
iteration 1: [5,10,20] iteration 2: [3,4,6] iteration 3: [2,9,12]
resulting array: [3.333,7.666,12.666]
您可以试试下面的方法,它接受任意数量的数组:
private double[] averageArray(int[]... arrays) throws Exception
{
// take the first array as starting point
int[] sums = arrays[0].clone();
for (int i = 1; i < arrays.length; i++)
{
// how can we take average if the inputs are different lengths!
if (arrays[i].length != sums.length)
{
throw new Exception("Input arrays are of differing dimensions!");
}
// add this array to our running sum
for (int j = 0; j < sums.length; j++)
{
sums[j] += arrays[i][j];
}
}
double[] averages = new double[sums.length];
for (int k = 0; k < sums.length; k++)
{
// cast to double to avoid integer division
averages[k] = (double) sums[k] / arrays.length;
}
return averages;
}
这通过了表达您要求的以下单元测试:
@Test
public void averageTest() throws Exception
{
int[] a = { 5, 10, 20 };
int[] b = { 3, 4, 6 };
int[] c = { 2, 9, 12 };
Assert.assertArrayEquals(new double[] { 10.0 / 3, 23.0 / 3, 38.0 / 3 }, averageArray(a, b, c), 0);
}
根据您的代码,此 returns 是双精度 [],但使用 BigDecimals 可能会更好,尤其是因为它看起来精度和准确性对您尝试的任何事情都很重要。
正如其他人所提到的,您的代码很难阅读,尤其是可以使用更好的参数命名。