LoginSignup
171
149

More than 5 years have passed since last update.

word2vecのソースを読んでみた

Last updated at Posted at 2016-07-13

word2vec

word2vecC#Word2Vec.NetCVisualStudio使

word2vecC-BOWSkip-gram2Skip-gramNegative Sampling2word2vecNegative Sampling



word2vec

Word2Vec 
http://tkengo.github.io/blog/2016/05/09/understand-how-to-learn-word2vec/

1


word2vec

word2vec
  = {0.8, -0.01, 0.45, 0.23, ..... ,0.34, -0.56}
  = {0.76, 0.71, -0.92, -0.86, ...., 0.45, 0.22}
200200


11



http://www.not-enough.org/abe/manual/cg-math-ad06/vector2.html

2


2


word2vec
            


word2vec5 "window" 
周辺の単語.png

21



p(w_o|w_i) = \frac{\exp({v_{w_o}}^T・v_{w_i})}{\sum_{w_v\in V} \exp({v_{w_v}}^T・v_{w_i})}     (式1)

$w_i$$w_o$$w_i$$v_{w_i}$$w_o$$v_{w_o}$$w_i$$w_o$

$exp(x)$$e^x$${v_{w_o}}^Tv_{w_i}$2$v_{w_o}$$v_{w_i}$$V$word2vec1 select distinct


http://mathtrain.jp/softmax

22


1

使

23Negative Sampling


word2vec2
 Negative Sampling
 
Negative Sampling

window

$w_{i}$$w_{o}$$w_{o}$$w_{i}$$p(w_o|w_i)$$w_{o}$$1-p(w_o|w_i)$

$p(w_o|w_i)$01使
関連する単語である確率 : p(w_o|w_i) = \sigma({v_{w_o}}^T・v_{w_i}) =  \frac{1}{1+exp(-{v_{w_o}}^T・v_{w_i})}
関連しない単語である確率  : 1-p(w_o|w_i)

この2つの式から、以下のような関数を考えます。

E = -\log p(w_o|w_i)\prod_{w_v\in V_{neg}}(1-p(w_v|w_i)) (式2)

$V_{neg}$window





525

3


word2vec

31


word2vec111
VocubWord.cs
internal struct VocubWord : IComparable<VocubWord>
{
    public long Cn { get; set; }        // 文書データ中でその単語が出てきた回数
    public string Word { get; set; }    // その単語の言葉(例えば「晩御飯」)
    public char[] Code { get; set; }    // 階層的ソフトマックスで使うハフマン木構造を表す値
    public int CodeLen { get; set; }    // Code[]. Point[]の終端インデックス番号
    public int[] Point { get; set; }    // ハフマン木の分岐点のインデックス番号

    public int CompareTo(VocubWord other)
    {
        return (int)(this.Cn - other.Cn);
    }
}

5Negative Sampling"Cn""Word"3Negative Sampling使

1word2vec

単語のデータ構造

Word2Vec.cs
private uint GetWordHash(string word)
{
    int a;
    ulong hash = 0;
    for (a = 0; a < word.Length; a++) hash = hash*257 + word[a];
    hash = hash%VocabHashSize;
    return (uint) hash;
}

 _vocabHash[]  _vocab[]  VocubWord VocabWord _vocab[] 

 GetWordHash() "25687" _vocabHash[] VocabWord _vocab[] _vocab[25687] VocabWord

32


 _syn0[]
単語ベクトルの管理
 "25687"  200 _syn0[25687 * 200 + 0]  _syn0[25687 * 200 + 199]

33


Negative Sampling _table[] 
Word2Vec.cs
private void InitUnigramTable()
{
    int a;
    double trainWordsPow = 0;
    double power = 0.75;
    _table = new int[TableSize];
    for (a = 0; a < _vocabSize; a++) trainWordsPow += Math.Pow(_vocab[a].Cn, power); // (1)
    int i = 0;
    var d1 = Math.Pow(_vocab[i].Cn, power)/trainWordsPow; // (2)
    for (a = 0; a < TableSize; a++)
    {
        _table[a] = i;
        if (a/(double) TableSize > d1)
        {
            i++;
            d1 += Math.Pow(_vocab[i].Cn, power)/trainWordsPow; // (3)
        }
        if (i >= _vocabSize) i = _vocabSize - 1;
    }
}

$U(w)$VocabWord Cn
P(w) = \frac{U(w)^{3/4}}{\sum_{v=1}^{W}U(w_{v})^{3/4}}

(1)(2)a(2)d1(3) table[] 

_vocab[]_vocab[].Cntable[]

ノイズ分布
_vocabHash[] 

3/4
https://www.oreilly.co.jp/books/9784873116839/

34


 TrainModelThreadStart() 

未学習単語のランダム選択
_sampleExcel

キャプチャ5.PNG





35


sen[] sen[] 

 sen[] 2skip-gram2csentencePosition

学習単語の取り出し
cfora1

 lastWord word  3.2  l1l2

 l1 = lastWord * <>
 l2 = word * <>

Word2Vec.csSkip-gram & Negative Sampling
Word2Vec.cs
// NEGATIVE SAMPLING
if (_negative > 0)
{
    for (d = 0; d < _negative + 1; d++)
    {
        if (d == 0)
        {
            // for文の1回目のループでは、sentencePositionで示された単語を学習対象に選ぶ。
            target = word; // word = sen[sentencePosition]。
            label = 1;
        }
        else
        {
            // for文の2回目のループでは、関連性のない単語をランダムに選ぶ
            nextRandom = nextRandom * 25214903917 + 11;
            target = _table[(nextRandom >> 16) % TableSize];

            if (target == 0)
                target = (long)(nextRandom % (ulong)(_vocabSize - 1) + 1);

            if (target == word)
                continue;

            label = 0;
        }

        // "l1"が基準となる単語
        // "l2"はd=0の時には、周辺の単語。d>0の時には、ランダムに選ばれた関連性の無い単語。
        // _layer1Sizeは単語ベクトルの次元数(デフォルト値 = 200)

        l2 = target * _layer1Size;
        f = 0;

        for (c = 0; c < _layer1Size; c++)
            // 2つの単語ベクトルの内積を計算。
            f += _syn0[c + l1] * _syn1Neg[c + l2];

        // 内積の値がMaxExpの絶対値より大きい場合、シグモイド関数の定義により、1 or 0で計算する。
        if (f > MaxExp)
            g = (label - 1) * _alpha;
        else if (f < MaxExp * -1)
            g = (label - 0) * _alpha;
        else
            // 内積の値がMaxExpの絶対値以内だった場合、シグモイド関数の計算結果を収めたテーブルから値を取り出し、計算する。
            g = (label - _expTable[(int)((f + MaxExp) * (ExpTableSize / MaxExp / 2))]) * _alpha;  // (A)

        for (c = 0; c < _layer1Size; c++)
            neu1e[c] += g * _syn1Neg[c + l2];    // (B)

        for (c = 0; c < _layer1Size; c++)
            _syn1Neg[c + l2] += g * _syn0[c + l1];  // (B)'
    }
}

// Learn weights input -> hidden ... (C)
for (c = 0; c < _layer1Size; c++)
    _syn0[c + l1] += neu1e[c];

ソース中のコメントにも書きましたが、forループによる繰り返し処理により、window幅内の関連性のある単語のピックアップと、3.3章で説明した配列から選ばれた関連性のない単語のピックアップが行われています。

変数 f に2つの単語ベクトルの内積が計算され、その値を元に、損失関数の値が計算されます(ソース中のコメント(A))。損失関数の式は、最初に紹介したこちらのページに書かれている以下の式になります。

\sigma({v}^T・v_{w_i}) -t

$v$ は、window幅から選ばれた単語のベクトル、もしくはランダムに選ばれた無関係の単語のベクトルです。$t$ の値は、関連する単語のときには 1 、無関係の単語の時には 0 です。ソースコードを見ると、この $t$ に当たる変数が label であることが分かります。そして、この損失関数に -1 を掛けたものを足しているのが (B) (B)'の部分です。勾配降下法で出てくる「損失関数の値を減算していく処理」を、このように実装しています。

ソース中の(C)の部分は、こちらのページに説明されている隠れ層の更新処理です。このページに書かれている以下の数式を算出しているのが、ソース中の(B)の行です。

\sum_{v\in{w_{O}}\cup N_{Neg}}(\sigma({v_{v}}^T・v_{w_i}) -t)v_{v}

以上の処理を読み込んだ各単語に対して繰り返し実行していきます。

参考資料

けんごのお屋敷「Word2Vec のニューラルネットワーク学習過程を理解する」
http://tkengo.github.io/blog/2016/05/09/understand-how-to-learn-word2vec/

ディープラーニングチュートリアル 応用編:言葉の『意味』表現〜word2vec〜
https://adtech.cyberagent.io/pr/?p=1489

O'Reilly Japan 「word2vecによる自然言語処理」
https://www.oreilly.co.jp/books/9784873116839/

Distributed Representations of Words and Phrases and their Compositionality
http://papers.nips.cc/paper/5021-distributed-representations-of-words-and-phrases-and-their-compositionality.pdf

171
149
2

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up

171
149