単語の意味をベクトルで表現する手法を提供するword2vecですが、これをC#で実装したものがgitHubに上がっています。
元々のC言語のソースと見比べてみましたが、もうほとんど同じですね。そのままソース持ってきて、C#の文法に沿って微修正したような印象を受けました。
#ただし、ちょっと不具合と思われる箇所が...
不具合に1か所気づいたのでメモしておきます。
Word2Vec.cs中のコンストラクタの部分です。ここにシグモイド関数の計算結果をテーブルに保持させるロジックがあります。
for (var i = 0; i < ExpTableSize; i++)
{
_expTable[i] = (float)Math.Exp((i / ExpTableSize * 2 - 1) * MaxExp); // Precompute the exp() table
_expTable[i] = _expTable[i] / (_expTable[i] + 1); // Precompute f(x) = x / (x + 1)
}
恐らくシグモイド関数の計算時間を節約するために、あらかじめ計算結果をテーブル化させておいているんでしょうね。
問題は、このロジック中の変数 i なのですが、VisualStudioのデバッガで確認したところ、int型として扱われています。なので、Exp()関数の引数中の計算式「i / ExpTableSize * 2」は、常に0になってしまいます。ですので、_expTable[]にはシグモイド関数の計算結果は入りません。一定の値(0.00247262325)が全配列要素に入るだけです。
なので、以下のように修正する必要があると思います。
for (var i = 0; i < ExpTableSize; i++)
{
_expTable[i] = (float)Math.Exp(((float)i / ExpTableSize * 2 - 1) * MaxExp); // Precompute the exp() table
_expTable[i] = _expTable[i] / (_expTable[i] + 1); // Precompute f(x) = x / (x + 1)
}
変更前:Math.Exp((i / ExpTableSize * 2 - 1) * MaxExp);
変更後:Math.Exp(((float)i / ExpTableSize * 2 - 1) * MaxExp);