计算数组索引的平均值并将结果收集到新数组

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 可能会更好,尤其是因为它看起来精度和准确性对您尝试的任何事情都很重要。

正如其他人所提到的,您的代码很难阅读,尤其是可以使用更好的参数命名。