次の方法で共有


テストの実行

多群最適化

James McCaffrey

コード サンプルのダウンロード

多群最適化 (MSO: Multi-Swarm Optimization) は、解決が困難または不可能に思える数値問題の解を見積もるための技術です。MSO は、粒子群最適化の変形です (この話題については、私の記事 (msdn.microsoft.com/magazine/hh335067) を参照してください)。通常の粒子群最適化は、鳥の群れや魚の群雄などに見られる集団行動をモデル化します。MSO は、単一の群れではなく複数の群れに分かれた粒子のシミュレーションを使用することで粒子群最適化を拡張しています。

MSO はさまざまな機械学習シナリオに適用できます。たとえば、人工ニューラル ネットワークの重みやバイアス値を見積もるシナリオ、集団分類や集団予測における弱学習器の重みを見積もるシナリオなどに適用できます。MSO はメタヒューリスティックです。つまりその実態は、特定の最適化問題を解くための特定のアルゴリズムの構築に使用できる、設計原則とガイドラインの集合です。

多群最適化を理解する近道は、図 1のデモ プログラムと図 2のグラフを調べることです。デモ プログラムでは、次のような Rastrigin 関数と呼ばれる標準ベンチマークの数値最適化問題の解を見積もります。デモ プログラムの目的は、関数の値が最小になるような x0 と x1 の値を見つけることです。


図 1 多群最適化のデモ


図 2 Rastrigin 関数

この関数には x0 = 0.0 かつ x1 = 0.0 のときに f = 0.0 という既知の解があるので、このシナリオでは必ずしも MSO を使用する必要はありません。図 2 のグラフは、Rastrigin 関数を示しています。この関数には偽の解を表す山と谷が多数ありますが、グローバルな最小値はグラフ中央部の 1 か所だけです。Rastrigin 関数の真の解の近似値であってもそのものではない多数の解は、最適化アルゴリズムによる処理の難易度を引き上げるために故意に設定したものです。

図 1 のデモ プログラムでは、まず MSO アルゴリズムの入力パラメーターを設定しています。3 つの群れにはそれぞれ 4 個の粒子が含まれています。ほとんどの数値最適化問題では、値が存在する可能性がある範囲をなんらかの方法で制限します。今回は、最初から検索領域を -100.0 ~ +100.0 の x 値に制限しています。

デモ プログラムでは、12 個の粒子をそれぞれランダムな (x0, x1) の値に初期化します。値の各ペアは、解の候補としても解釈可能な粒子の位置を表しています。それぞれの位置における Rastrigin 関数の値は、関数の値の最適化を目的としていることを示すために、位置のコストと呼ばれています。初期化直後の最適な粒子 (コストが最小の粒子) は、0 から始まるインデックスで表すと群れ 2 の粒子 0 になります。この粒子の位置は [-40.57, 28.54] で、関連付けられたコストは 2498.93 です。

多群最適化は反復プロセスです。デモ プログラムでは、任意の最大ループ回数として 150 を設定しています。続いて、MSO で 12 個の粒子のうちいずれかで検出された最適解を追跡しながら、より良い解を探ります。150 回反復した結果見つかった最適解は、x0 = -0.0003、x1 = 0.0004 で f = 0.000043 でした。これは、実際の解に非常に近い値ですが、そのものではありません。maxLoop 変数を 500 に設定すればデモ プログラムでも実際の解を検出できますが、重要な点は、ほとんどの現実的な問題のシナリオでは MSO によって最適解が検出されたかどうかわからないことです。

今回は、中級以上のプログラミング スキルがあることを前提にしていますが、多群最適化については何も知らなくても問題ありません。デモ プログラムは C# でコーディングしましたが、他の言語にリファクタリングする場合もそれほど手間はかからないでしょう。コードのサイズを小さくして中心となるアイデアを明確にするために、デモ コードでは通常のエラー チェックの大半を省略しています。デモは非常に長いのですべてをこのコラム内に掲載することはできませんが、archive.msdn.microsoft.com/mag201309TestRun(英語) で完全なソースコードを入手できます。

プログラムの全体構造

いくつか小さな変更を加え、WriteLine ステートメントのほとんどを省略したデモ プログラムの全体構造を図 3に示します。

図 3 多群デモ プログラムの構造

using System; 
namespace MultiSwarm 
{ 
  class MultiSwarmProgram 
  { 
    static void Main(string[] args) 
    { 
      try 
      { 
        Console.WriteLine( 
          "\nBegin Multiple Particle Swarm optimization demo\n"); 
        int dim = 2; 
        double minX = -100.0; 
        double maxX = 100.0; 
        int numParticles = 4; // Particles in each swarm 
        int numSwarms = 3; // Swarms in multi-swarm 
        Multiswarm ms = new Multiswarm(numSwarms, numParticles,  
          dim, minX, maxX); 
        Console.WriteLine("\nInitial multiswarm:"); 
        Console.WriteLine(ms.ToString()); 
        int maxLoop = 150; 
        ms.Solve(maxLoop); 
        Console.WriteLine("\nFinal multiswarm:"); 
        Console.WriteLine(ms.ToString()); 
        Console.WriteLine("\nBest solution found = " + 
          ms.bestMultiCost.ToString("F6")); 
        Console.Write("at x0 = " + ms.bestMultiPos[0].ToString("F4")); 
        Console.WriteLine(", x1 = "  
          + ms.bestMultiPos[1].ToString("F4")); 
        Console.WriteLine("\nEnd demo\n"); 
        Console.ReadLine(); 
      } 
      catch (Exception ex) 
      { 
        Console.WriteLine(ex.Message); 
        Console.ReadLine(); 
      } 
    } 
    public static double Cost(double[] position) { . . } 
  } // Program 
  public class Particle { . . } 
  public class Swarm { . . } 
  public class Multiswarm { . . } 
} // ns

デモ プログラムの作成には、Visual Studio 2012 を使用しました。デモには大きな依存関係はなく、任意のバージョンの Visual Studio で動作します。ここでは、C# コンソール アプリケーション テンプレートを選択し、MultiSwarm という名前を付けました。Visual Studio にコード テンプレートが読み込まれたら、ソリューション エクスプローラー ウィンドウで Program.cs ファイルの名前を MultiSwarmProgram.cs に変えました。これにより、プログラムのクラス名も Visual Studio によって自動的に変更されました。ソース コードの冒頭では、System 名前空間の参照だけ残し、不要な名前空間参照をすべて削除しました。

デモ プログラムでは、パブリック スコープの Cost メソッドを定義しています。これが Rastrigin 関数です。

public static double Cost(double[] position) 
{ 
  double result = 0.0; 
  for (int i = 0; i < position.Length; ++i) { 
    double xi = position[i]; 
    result += (xi * xi) - (10 * Math.Cos(2 * Math.PI * xi)) + 10; 
  } 
  return result; 
}

Cost メソッドは、粒子の位置を表す配列パラメーターを 1 つ受け取ります。これは解となる可能性がある値です。ほとんどの機械学習のシナリオでは、最小化しようとしている Cost 関数はなんらかのエラーを表し、トレーニング データ ソースなどの追加パラメーターが必要です。通常、Cost 関数は MSO のパフォーマンスのボトルネックになるので、できる限り効率が上がるようにコーディングする必要があります。

Multiswarm クラスは、MSO アルゴリズムをカプセル化しています。Particle クラスと Swarm クラスは、Multiswarm クラスのヘルパー クラスです。クラス定義を入れ子にできる言語では、Multiswarm クラス内に Particle クラスと Swarm クラスを定義するという設計方法もあります。

デモ プログラムでは、まず 5 つの入力パラメーターを設定します。

int dim = 2; 
double minX = -100.0; 
double maxX = 100.0; 
int numParticles = 4; 
int numSwarms = 3;

dim 変数は、解こうとしている問題の次元数を表します。Rastrigin 関数は x0 と x1 を受け取るので、dim の値を 2 に設定します。多群最適化は、どの次元数の問題にも適しています。minX 変数と maxX 変数は、検索対象を一定範囲内の値に絞り込みます。minX 変数と maxX 変数の値は、問題によって異なります。numParticles 変数は、それぞれの群れに含まれる粒子の数を定義します。numSwarms 変数は、群れの総数を定義します。一般には、numParticles 変数と numSwarms 変数の値が大きいほど解の精度が向上しますが、パフォーマンスは低下します。

続いて、最初の多群オブジェクトのインスタンスを作成し、MSO の解決アルゴリズムを呼び出します。

Multiswarm ms = new Multiswarm(numSwarms, numParticles,  
  dim, minX, maxX); 
Console.WriteLine("\nInitial multiswarm:"); 
Console.WriteLine(ms.ToString()); 
int maxLoop = 150; 
ms.Solve(maxLoop);

このデモでは、粒子の集合を "群れ" と呼び、群れの集合を "多群" と呼んでいます。研究文献によっては、このような命名方法の代わりに粒子の集合を "部分的な群れ" と呼び、部分的な群れの集合を "群れ" と呼ぶことがあります。Multiswarm オブジェクトのインスタンス作成には、先ほど定義した入力パラメーターを使用します。maxLoop 変数には、解決アルゴリズムのメイン ループを反復する最大回数を格納します。一般には、maxLoop 変数の値が大きいほど解の精度が向上しますが、パフォーマンスは低下します。

Solve メソッドは、Rastrigin 関数の最適解を繰り返し検索します。Cost 関数を Multiswarm オブジェクトの外部で定義していることに注意してください。ほとんどの場合、MSO のコードはソフトウェア システムに組み込み、クラス ライブラリ DLL としては実装しません。MSO コードをクラス ライブラリ DLL にすると、Cost 関数を Multiswarm オブジェクトに (たとえばデリゲートを使用して) 渡したり、インターフェイス設計アプローチでプログラミング コントラクトを定義したりする必要が生じます。

Solve メソッドの実行が完了したら、Multiswarm オブジェクトの最終状態を表示します。また、多群のすべての群れに含まれるすべての粒子で検出された解のうちの最適解も表示します。

Console.WriteLine("\nFinal multiswarm:"); 
Console.WriteLine(ms.ToString()); 
Console.WriteLine("\nBest solution found = " + 
 ms.bestMultiCost.ToString("F6")); 
Console.Write("at x0 = " + ms.bestMultiPos[0].ToString("F4")); 
Console.WriteLine(", x1 = " + ms.bestMultiPos[1].ToString("F4"));

粒子

Particle クラスには、以下の 6 つのメンバーがあります。

static Random ran = new Random(0); 
public double[] position; 
public double[] velocity; 
public double cost; 
public double[] bestPartPos; 
public double bestPartCost;

ここではわかりやすくするためにパブリック スコープを使用していますが、get プロパティ メソッドや set プロパティ メソッドと共にプライベート スコープを使用してもかまいません。Random オブジェクトは、Particle オブジェクトをランダムな位置に初期化するためにコンストラクターで使用します。position という配列は、粒子の位置を表します。velocity 配列は、粒子のスピードと方向を表します。たとえば、ある粒子の位置が [12.0, 24.0] で速度が [5.0, 0.0] だとします。これは、次の時間増分の間に粒子が x0 の次元方向に 5.0 単位、x1 の次元方向に 0.0 単位移動することを表します。粒子が移動すると、新しい位置は [17.0, 24.0] になります。

cost 変数には、現在位置での Cost 関数の値を格納します。bestPartCost 変数にはその粒子で見つかった最適 (最小) コストの値を格納し、bestPartPos 変数には既知の最適コストが見つかった位置を格納します。

Particle コンストラクターの定義を図 4 に示します。

図 4 Particle コンストラクター

public Particle(int dim, double minX, double maxX) 
{ 
  position = new double[dim]; 
  velocity = new double[dim]; 
  bestPartPos = new double[dim]; 
  for (int i = 0; i < dim; ++i) { 
    position[i] = (maxX - minX) * ran.NextDouble() + minX; 
    velocity[i] = (maxX - minX) * ran.NextDouble() + minX; 
  } 
  cost = MultiSwarmProgram.Cost(position); 
  bestPartCost = cost; 
  Array.Copy(position, bestPartPos, dim); 
}

コンストラクターは、問題の次元数に基づいて、position 配列、velocity 配列、および bestPartPos 配列のための領域を割り当てます。position 配列と velocity 配列の各セルには、minX ~ maxX のランダム値を割り当てます。初期の位置を計算で求め、粒子の既知の最適位置と最適コストを初期位置と初期コストに設定します。

重要な代替方法として、ランダムではない初期の位置を各粒子に割り当てる手法があります。MSO に関する一部の研究文献では、さまざまな群れの粒子を検索範囲のさまざまな領域に割り当てる方がランダムに割り当てる手法よりも優れているとされています。たとえば、2 つの群れに粒子が 10 個ずつ含まれている場合、最初の群れの 10 個の粒子を x 値が -100.0 ~ 0.0 の位置に割り当て、もう 1 つの群れの 10 個の粒子を x が 0.0 ~ +100.0 の位置に割り当てることができます。ただし、私はこれらの研究結果に完全に納得しているわけではなく、これまでの実践経験では粒子をランダムな位置に割り当てる単純な手法で問題ありませんでした。

群れ

群れは粒子の集合です。Swarm クラスには、以下の 3 つのメンバーがあります。

public Particle[] particles; 
public double[] bestSwarmPos; 
public double bestSwarmCost;

MSO の命名方法は少しややこしいことがあります。今回は Swarm クラスで Particle オブジェクトの配列に "particles" という名前を付けていますが、"swarm" という名前を付けることもできます。bestSwarmCost メンバー変数には、アルゴリズムの実行中にその群れのすべての粒子で検出された中での最適 (最小) コストの値を格納します。bestSwarmPos 配列には、群れのメンバーの最適コストが見つかった位置を格納します。

Swarm コンストラクターを図 5に示します。

図 5 Swarm コンストラクター

public Swarm(int numParticles, int dim, double minX, double maxX) 
{ 
  bestSwarmCost = double.MaxValue; 
  bestSwarmPos = new double[dim]; 
  particles = new Particle[numParticles]; 
  for (int i = 0; i < particles.Length; ++i) { 
    particles[i] = new Particle(dim, minX, maxX); 
    if (particles[i].cost < bestSwarmCost) { 
      bestSwarmCost = particles[i].cost; 
      Array.Copy(particles[i].position, bestSwarmPos, dim); 
    } 
  } 
}

Swarm コンストラクターは、メモリ領域を割り当ててから、numParticles で指定した回数分 Particle コンストラクターを呼び出して、ランダムな位置に粒子を生成します。粒子を作成するたびに、その粒子に群れのすべての粒子の中での最適コストが見つかるかどうか確認します。

Multiswarm クラス

多群は群れの集合です。最上位の Multiswarm クラスには、以下の 7 つのメンバーがあります。

public Swarm[] swarms; 
public double[] bestMultiPos; 
public double bestMultiCost; 
public int dim; 
public double minX; 
public double maxX; 
static Random ran = new Random(0);

swarms 配列には各 Swarm オブジェクトを格納し、各 Swarm オブジェクトは Particle オブジェクトの集合です。したがって、swarms[2].particles[3].position[0] は、群れ 2 における粒子 3 の x0 値を表します。

bestMultiCost メンバー変数には、アルゴリズムの実行中にすべての群れのすべての粒子によって検出された中での最適コストを格納します。bestMultiPos 配列には、グローバルな最適コストが見つかった場所に関連する位置を格納します。dim、minX、および maxX の各メンバー変数には、利便性のために値を格納しています。この結果、パラメーターとして渡さなくても、メンバー変数の値をクラス メソッドで使用できます。

Particle クラスには、初期のランダムな位置を生成する ran という Random オブジェクトがあることを思い出してください。Multiswarm クラスには、別の Random オブジェクトがあります。こちらは、MSO アルゴリズムで問題解決の処理中に擬似的なランダム動作を挿入するために使用します。

Multiswarm コンストラクターを図 6に示します。

図 6 Multiswarm コンストラクター

public Multiswarm(int numSwarms, int numParticles, int dim, 
  double minX, double maxX) 
{ 
  swarms = new Swarm[numSwarms]; 
  bestMultiPos = new double[dim]; 
  bestMultiCost = double.MaxValue; 
  this.dim = dim; 
  this.minX = minX; 
  this.maxX = maxX; 
  for (int i = 0; i < numSwarms; ++i) 
  { 
    swarms[i] = new Swarm(numParticles, dim, minX, maxX); 
    if (swarms[i].bestSwarmCost < bestMultiCost) 
    { 
      bestMultiCost = swarms[i].bestSwarmCost; 
      Array.Copy(swarms[i].bestSwarmPos, bestMultiPos, dim); 
    } 
  } 
}

配列を割り当てて入力パラメーターの値を保存したら、Multiswarm コンストラクターは numSwarms で指定した回数だけ Swarm コンストラクターを呼び出します。群れを作成するたびに、その群れのすべての粒子での最適コストがグローバルな最適コストかどうかを確認します。グローバルな最適コストの場合は、そのコストとコストに関連する位置をそれぞれ bestMultiCost と bestMultiPos に格納します。

MSO アルゴリズム

MSO アルゴリズムを非常に大まかな擬似コードで表すと、次のようになります。

loop maxLoop times
  for each swarm
    for each particle
      compute new velocity
      use velocity to update position
      check if a new best cost has been found
    end for
  end for
end loop

MSO での重要な演算は、粒子の新しい速度を求める計算です。特定の粒子の新しい速度に影響を及ぼすのは、現在速度、現在位置、粒子の既知の最適位置、同じ群れに含まれるすべての粒子の中での既知の最適位置、およびすべての群れに含まれるすべての粒子の中での既知の最適位置です。数学表現では、新しい速度は次のようになります。

v(t+1) = w * v(t) +
         (c1 * r1) * (p(t) - x(t)) +
         (c2 * r2) * (s(t) - x(t)) +
         (c3 * r3) * (m(t) - x(t))

新しい速度を計算すると、粒子の新しい位置は次のようになります。

x(t+1) = x(t) + v(t+1)

少し我慢してお付き合いください。この計算は、見かけよりもはるかに単純です。項 v(t+1) は、時間 t+1 における速度、つまり新しい速度を意味します。項 v(t) は現在速度です。項 x(t) は現在位置です。この計算式の x と v は、単一の値ではなく [12.0, 25.0] といったベクトルであることに注意してください。

項 p(t) は、粒子の既知の最適位置です。項 s(t) は、その粒子と同じ群れに含まれるすべての粒子の中での最適位置です。項 m(t) は、すべての群れに含まれるすべての粒子の中での最適位置です。

項 w は、慣性質量と呼ばれる定数です。項 c1、項 c2、および項 c3 は、新しい速度の各成分を最大限に変更する定数です。項 r1、項 r2、および項 r3 は、0 ~ 1 のランダム値で、速度の更新に毎回ランダムな効果を適用します。

新しい速度の計算については、例を見ていただくのが最もわかりやすいでしょう。たとえば、粒子の現在位置が [12.0, 24.0] で現在速度が [-1.0, -3.0] だとします。また、粒子の既知の最適位置を [8.0, 10.0]、群れに含まれるすべての粒子の中での既知の最適位置を [7.0, 9.0]、およびすべての群れに含まれるすべての粒子の中での既知の最適位置を [5.0, 6.0] とします。さらに、定数 w の値を 0.7、定数 c1 と定数 c2 の値を 1.4、定数 c3 の値を 0.4 とします。最後に、ランダム値 r1、r2 および r3 をすべて 0.2 とします。

粒子の新しい速度を図 7に示します。

図 7 粒子の新しい速度の計算

v(t+1) = 0.7         * [-1.0, -3.0] +
         (1.4 * 0.2) * [8.0, 10.0] - [12.0, 24.0] +
         (1.4 * 0.2) * [7.0, 9.0] - [12.0, 24.0] +
         (0.4 * 0.2) * [5.0, 6.0] - [12.0, 24.0]
       = 0.7 * [-1.0, -3.0] +
         0.3 * [-4.0, -14.0] +
         0.3 * [-5.0, -15.0] +
         0.1 * [-7.0, -18.0]
       = [-0.7, -2.1] +
         [-1.2, -4.2] +
         [-1.5, -4.5] +
         [-0.7, -1.8]
       = [-4.1, -12.6]

したがって、図 7のとおり計算すると、この粒子の新しい位置は次のようになります。

x(t+1) = [12.0, 24.0] + [-4.1, -12.6]
       = [7.9, 11.4]

最適解を Rastrigin 関数と同じ [0.0, 0.0] とすると、元の位置よりも最適解に近い、新しい位置に粒子が移動したことに注目してください。

v(t+1) の慣性項は、粒子を現在の方向へ移動し続けます。項 p(t) は、粒子をその粒子でこれまでに検出された中で既知の最適位置に向けて移動します。項 s(t) は、同じ群れに含まれるすべての粒子によって検出された位置の中で最適な位置に向けて粒子を移動します。項 m(t) は、すべての群れに含まれるすべての粒子によって検出された位置の中で最適な位置に向けて粒子を移動します。

定数 c1、c2、および c3 は、それぞれ認識質量、社会的質量、およびグローバル質量とも呼ばれます。これらの定数は、r1、r2、および r3 のランダムな値と慣性質量 w と共に、各項が粒子の動きに与える影響の大きさを決定します。通常の粒子群最適化に関する研究では、w、c1、および c2 の妥当な値はそれぞれ 0.729、1.49445、および 1.49445 とされています。MSO における定数 c3 についてはほとんど研究されていませんが、私個人が普段使用している値は 0.3645 (慣性質量の半分) で、この値はこれまでの実践経験では問題なく機能しています。

削除と移動

基本的な MSO アルゴリズムを変更する優れた方法は、いくつかあります。たとえば、ときどきランダムに選択した粒子を基本的に削除して、新しい粒子を作成する方法があります。デモ プログラムでは、この削除と作成による変更を使用しています。Solve メソッドの最初の数行を図 8に示します。

図 8 Solve メソッドの冒頭

public void Solve(int maxLoop) 
{ 
  // Assign values to ct, w, c1, c2, c3 
  double death = 0.005; ; // Prob of death 
  while (ct < maxLoop) 
  { 
    ++ct; 
    for (int i = 0; i < swarms.Length; ++i) { 
      for (int j = 0; j < swarms[i].particles.Length; ++j) { 
        double p = ran.NextDouble(); 
        if (p < death) 
          swarms[i].particles[j] = new Particle(dim, minX, maxX); 
        for (int k = 0; k < dim; ++k) { 
...

このメソッドでは、0 ~ 1 のランダム値を生成して、p に格納します。ランダム値が 0.005 未満の場合は、Particle コンストラクターを呼び出して現在の粒子のインスタンスを再作成します。これは、現在の粒子を事実上削除して、新しい粒子をランダムな場所に作成する処理です。

別の MSO 手法は、定期的に異なる群れの 2 つの粒子を選んで交換することで、移動をモデル化する方法です。ある粒子を現在の群れに事実上移入し、別の粒子を群れの外に移します。デモ プログラムにはこの手法も含まれています。この手法の重要なメソッドは、次のとおりです。

private void Immigration(int i, int j) 
{ 
  // Swap particle j in swarm i 
  // with a random particle in a random swarm 
  int otheri = ran.Next(0, swarms.Length); 
  int otherj = ran.Next(0, swarms[0].particles.Length); 
  Particle tmp = swarms[i].particles[j]; 
  swarms[i].particles[j] = swarms[otheri].particles[otherj]; 
  swarms[otheri].particles[otherj] = tmp; 
}

この手法は単純なため、粒子が意図せず交換されるという望ましくない現象が発生するおそれがあります。移動機能は、Solve メソッド内で次のように呼び出します。

double immigrate = 0.005; 
... 
double q = ran.NextDouble(); 
if (q < immigrate) 
  Immigration(i, j);

まとめ

この記事の説明と付属のコード ダウンロードでは、多群最適化を使ってみるための確かな基盤を紹介しました。通常の粒子群最適化と比べ、多群最適化は少し複雑なだけで高品質な結果を得られますが、処理に時間がかかります。MSO に関する私の経験では、MSO が通常の粒子群最適化よりも適しているのは、解決が困難に思える最適化問題 (ローカルの最小解に関する問題) の解決についてです。

MSO は汎用数値最適化メタヒューリスティックであり、通常はもっと大きな機械学習シナリオの一部として、なんらかのエラー関数の値を最小化する重みのセットを見つけるために使用します。たとえば、MSO を使用すると、一連のトレーニング データに関する分類エラーを最小限に抑えることでニューラル ネットワークの重みやバイアス値の最適なセットを見つけることができます。MSO には、進化的最適化アルゴリズム (別名、実数値遺伝アルゴリズム)、細菌の採餌最適化、アメーバ手法最適化など、さまざまな代替手法があります。

汎用数値最適化に関するほとんどの代替手法に比べ、今回紹介した MSO は実装が簡単でカスタマイズが容易です。いくつかの代替手法に比べた MSO のデメリットは、MSO に限定されないパラメーター (慣性質量定数、認識質量/社会的質量/グローバル質量など) の値の選択方法について、概してほとんどガイドラインがないことです。しかし、私は MSO が大好きで、これまでソフトウェア システムで数値最適化問題を解く必要が発生したときには、MSO が非常に役立ってきました。

Dr. James McCaffrey は、ワシントン州レドモンドにあるマイクロソフト本社に勤務しています。これまでに、Internet Explorer、MSN サーチなどの複数のマイクロソフト製品にも携わってきました。彼は、『.NET Test Automation Recipes』(Apress、2006 年) の著者でもあり、jammc@microsoft.com(英語のみ) から連絡できます。

この記事のレビューに協力してくれた技術スタッフの Kirk Olynyk (マイクロソフト) に心より感謝いたします。