一种乘法和边缘化概率表的算法

An algorithm for multiplying and marginalizing probability tables

我正在 Javascript 中实现一个特定的贝叶斯网络库。 由于我在网上找不到任何其他可用的库,因此我不得不从头开始,包括乘以和边缘化 table 的概率(或势)。

marginalization中,将要边缘化的变量那一列去掉。 然后对公共行求和。 例如,c 将从 table phi(a,b,c) 中边缘化。 其中,相对于c的列被取出,共同的行ab相加,即, (0,0)(1,1) 总和分别为 0.3 和 0.1。

在乘法中,我们在公共变量(列)中寻找相等的值。 然后我们将匹配的行相乘。 例如,tables phi(a,b)phi(b,c) 将相乘。 公共列是b。 其中 b 在两个 table 中都是 0(具有所有可能的组合),我们将值相乘。

我的第一次尝试是以 Map 方式实现 tables,其中每一行都是一个 key,概率值是 。 但这似乎不是一个好主意,因为要实现乘法我们需要知道变量的label。因此,我稍微修改了代码以便从函数参数中获取标签,但它不起作用,因为行与标签没有直接关系。 所以,我把这段代码放在一边,现在我正在寻找新的想法。

拜托,有人想过如何实现这些示例吗?

以下内容似乎起到了边缘化的作用。它创建一个 table,其中包含具有成员和值的行。 table 中的每一行必须具有相同数量的成员。

它还没有实现乘法(真正的工作必须在某个时候完成......)。请注意,在 ECMAScript 中,0.1 + 0.2 = 0.30000000000000004 因此您需要实施某种舍入来解决此问题并保持准确性。

/**
 * A table has rows, all rows within a table have the same number of members
 * Each row as 1 to n members plus a value
 * e.g. a = 0, b = 0, c = 0, value = 0.1
 *      a = 0, b = 0, c = 1, value = 0.2
 *      a = 1, b = 0, c = 0, value = 0.1
 *      a = 1, b = 1, c = 0, value = 0.1
 *      a = 1, b = 1, c = 1, value = 0.1
 *
 * Marginalisation addition takes the values of matching values and sums them,
 * 
 * e.g. 
 *      marginalisation of {a, b} where a=0 and b=0:
 *      matching rows are 0 and 1, so add 0.1 + 0.2 => 0.3
 *
 *      marginalisation of {a, b} where a=1 and b=1:
 *      matching rows are 3 and 4, so add 0.1 + 0.1 => 0.2
 *
 * @param {number} numberOfValues - number of values in each row. So for 3 members
 *                                  plus value then numberOfValues is 4
 */
function BayTable (numberOfValues) {

  // Array containing rows of values
  this.rows = [];

  // Number of values in a row, so for [a, b, c, value] memberCount is 4
  this.memberCount = numberOfValues;
}

/**
 * @param {number} memberValue[, memberValue, ...], rowValue
 *
 * e.g. addRow(0, 0, 0, 0.1)
 */
BayTable.prototype.addRow = function() {

  if (arguments.length != this.memberCount) return; // or throw error

  var row = [];

  for (var i=0, iLen=arguments.length; i<iLen; i++) {
    row.push(arguments[i]);
  }
  this.rows.push(row);
}

/**
 * marginalise finds matching rows and adds their values,
 * so marginalise(0,0) finds rows where a=0 and b=0 ignoring
 * any other member in the row and
 * sums the value for the rows
 */
BayTable.prototype.marginalise = function() {

  // Guard agains too many arguments
  if (arguments.length > this.memberCount - 2) return; // or throw error

  var total = 0;
  var row, match;

  // For each row
  for (var i=0, iLen=this.rows.length; i<iLen; i++) {
    row = this.rows[i];
    match = true

    // Check values against arguments until a missmatch
    for (var j=0, jLen=arguments.length; j<jLen && match; j++) {
      match = row[j] === arguments[j];
    }

    // If no missmatch, add row value
    if (match) total += row[row.length - 1];
  }
  return total;
}

var x = new BayTable(4);
x.addRow(0, 0, 0, 0.1);
x.addRow(0, 0, 1, 0.2);
x.addRow(1, 0, 0, 0.1);
x.addRow(1, 1, 0, 0.1);
x.addRow(1, 1, 1, 0.1);

console.log(x.marginalise(0, 0)); // 0.30000000000000004
console.log(x.marginalise(1, 0)); // 0.1
console.log(x.marginalise(1, 1)); // 0.2

这是对象版本,它使用了几个 ES5 方法:

/**
 * A table has rows, all rows within a table have the same number of members
 * Each row as 1 to n members plus a value
 * e.g. a = 0, b = 0, c = 0, value = 0.1
 *      a = 0, b = 0, c = 1, value = 0.2
 *      a = 1, b = 0, c = 0, value = 0.1
 *      a = 1, b = 1, c = 0, value = 0.1
 *      a = 1, b = 1, c = 1, value = 0.1
 *
 * Marginalisation addition takes the values of matching values and sums them,
 * 
 * e.g. 
 *      marginalisation of {a, b} where a=0 and b=0:
 *      matching rows are 0 and 1, so add 0.1 + 0.2 => 0.3
 *
 *      marginalisation of {a, b} where a=1 and b=1:
 *      matching rows are 3 and 4, so add 0.1 + 0.1 => 0.2
 *
 * @param {number} numberOfValues - number of values in each row. So for 3 members plus value
 *                                  then numberOfValues is 4
 */
function BayTable (numberOfValues) {

  // Array containing rows of values
  this.rows = [];

  // Number of values in a row, so for [a, b, c, value] memberCount is 4
  this.memberCount = numberOfValues;
}

/**
 * @param {Object} row - {label:value[, label:value, ...], 'value':value}
 *
 * e.g. addRow({a:0, b:0, c:0, value:0.1})
 */
BayTable.prototype.addRow = function(row) {
  this.rows.push(row);
}

/**
 * marginalise finds matching rows and adds their values,
 * so marginalise({a:0, b:0}) finds rows where a=0 and b=0 ignoring
 * any other member in the row and sums the values of matched rows
 */
BayTable.prototype.marginalise = function(obj) {

  var keys = Object.keys(obj);

  // For each row
  return this.rows.reduce(function(total, row) {

    // If all key/values match, accumlate value
    if (keys.every(function(key){return obj[key] === row[key]}))
      total += row.value;
    return total;
  }, 0);

/*
  // Less obscure version, same number of lines of code
  var total = 0;
  var keys = Object.keys(obj);

  // For each row
  this.rows.forEach(function(row) {

    // If key/values match, add row value to total
    if (keys.every(function(key){return obj[key] === row[key]}))
      total += row.value;
  });
  return total;
*/
}


var x = new BayTable(4);

x.addRow({a:0, b:0, c:0, value:0.1});
x.addRow({a:0, b:0, c:1, value:0.2});
x.addRow({a:1, b:0, c:0, value:0.1});
x.addRow({a:1, b:1, c:0, value:0.1});
x.addRow({a:1, b:1, c:1, value:0.1});

console.log(x.marginalise({a:0, b:0})); // 0.30000000000000004
console.log(x.marginalise({a:1, b:0})); // 0.1
console.log(x.marginalise({a:1, b:1})); // 0.2
console.log(x.marginalise({a:1, c:1})); // 0.1
console.log(x.marginalise({b:0, c:0})); // 0.2