[一番下からの深走り2]05Word Embedding


  • 自然言語:私たちが普段使っている言語
  • 自然言語処理(NLP;Natural Language Processing):コンピュータに私たちの言語を理解させるための領域
  • 話は文字で構成され、文字は単語で構成されている.
  • したがって,NLPにおける単語の意味を理解することが重要である.

    語義表現


    シソラス

  • 人の人力を動員し、同義語、有義語群によって
  • を分類する.
  • NLPは、「親と子」、「全体と部分」など、より詳細な関係を定義します.
  • 単語間の関係図で表すと、単語間の関係(単語ネットワーク)
  • を学習することができる.
  • car = auto , automobile , machine , motocar
  • object ∋\ni∋ motor vehicle ∋\ni∋ car , go-kart , truck
  • Word Net:有名なシソラス
  • 問題
  • 時代の変化に対応し難い
  • 人高
  • 単語のわずかな違いは
  • を表現できない.

    統計ベースのメソッド

  • 馬群(Corpus):大量のテキストデータ→rarr→人が書いた文章を利用して、人の自然言語に対する知識を十分に体現している.(ex)ウィキペディア、グーグルニュース、大文豪の作品(シェークスピア、ナスメソセッチなど)の話前処理:小文字の変換、句点分割の空白基準の考慮、ディックシャナリー表記など    →rarr→正規表現
  • が使いやすい
    def preprocess(text):
        text = text.lower()
        text = text.replace('.', ' .')
        text = text.split(' ')
        
        word_to_id = {}
        id_to_word = {}
        for word in words:
            if word not in word_to_id:
                new_id = len(word_to_id)
                word_to_id[word] = new_id
                id_to_word[new_id] = word
                
        corpus = np.array([word_to_id[w] for w in words])
        
        return corpus, word_to_id, id_to_word
    
    text = 'You say goodbye and I say hello.'
    corpus, word_to_id, id_to_word = preprocess(text)
  • 単語の分散表現:単語を正確な意味ベクトル
  • として表す.
  • 分布仮定:単語自体に意味がなく、脈絡形成意味(周囲の単語によって形成される)

  • 同時にマトリクスが発生します:周囲の単語が何回現れるかを数えます
    yousaygoodbyeandihello.you0100000say1010110goodbye0101000and0010100i0101000hello0100001.0000010
  •       →rarr→実際に関連がなくても多くの理由が出てきて、高い相関性を示しています
            →rarr→単語の相関計算ベクトル間の類似度
               ex)ベクトルの内積、ユークリッド距離、コサイン類似度
    cosine_similarity(x,y)=x1y1+⋯+xnynx12+x22+⋯+yn2y12+y22+⋯+yn2cosine\_similarity(x,y) =\cfrac{x_1y_1 +\dots + x_ny_n}{\sqrt{x_1^2+x_2^2+\dots+y_n^2}\sqrt{y_1^2+y_2^2+\dots+y_n^2}}cosine_similarity(x,y)=x12​+x22​+⋯+yn2​​y12​+y22​+⋯+yn2​​x1​y1​+⋯+xn​yn​​
    指す方向がどれほど似ているか(-1~1)
    # 수동으로 만들기
    C = np.array([
        [0,1,0,0,0,0,0],
        [1,0,1,0,1,1,0],
        [0,1,0,1,0,0,0],
        [0,0,1,0,1,0,0],
        [0,1,0,1,0,0,0],
        [0,1,0,0,0,0,1],
        [0,0,0,0,0,1,0]
        ], dtype=np.int32)
        
    # 자동으로 만드는 함수
    def create_co_matrix(corpus, vocab_size, window_size=1):
        corpus_size = len(corpus)
        co_matrix = np.zeros((vocab_size, vocab_size), dtype=np.int32)
        
        for idx, word_id in enumerate(corpus):
            for i in range(1, window_size+1):
                left_idx = idx-i
                right_idx = idx+i
                
                if left_idx >= 0:
                    left_word_id = corpus[left_idx]
                    co_matrix[word_id, left_word_id] += 1
                if right_idx < corpus_size:
                    right_word_id = corpus[right_idx]
                    co_matrix[word_id, right_word_id] += 1
        return co_matrix
                    
  • 相互情報量(PMI)
  • thea等の語は、頻度が高いために強い相関を有するトラブルシューティング
  • である.
    PMI(x,y)=ログバックアップP(x,y)P(x,y)P(y)=log 2 C(x,y)NC(x)N=log 2 C(x,y)NC(x)NC(x)C(x):xの出現回数PMI(x,y)=log 2{cfrac2{p(x,y)}{p(x,y))(x(x(x(x)))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))\\\\\cxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx出場回数PMI(x,y)=log 2 P(x)P(y)P(x,y)=log 2 NC(x)NC(x,y)=log 2 C(x)C(x,y)NC(x):xの出場回数
    同時に発生する
  • 回数が0の場合、
  • "log 2{0}=-Infin,RRlog 2=∞"量相互情報量(PPMI)を用いる.
    ∴PPMI(x,y)=max(0,PMI(x,y))\therefore PPMI(x,y) = max(0, PMI(x,y))∴PPMI(x,y)=max(0,PMI(x,y))
    def ppmi(C, verbose=True, eps=1e-8):
        M = np.zeros_like(C, dtype=np.float32)
        N = np.sum(C)
        S = np.sum(C, axis=0)
        total = C.shape[0] * C.shape[1]
        cnt = 0
        
        for i in range(C.shape[0]):
            for j in range(C.shape[1]):
            pmi = np.log2(C[i,j]*N / (S[j]*S[i]) + eps)
            M[i,j] = max(0, pmi)
            
            if verbose:
                cnt += 1
                if cnt % (total//100) == 0:
                    print('%.1f%% 완료'%(100*cnt/total))
        return M
        
    text = 'You say goodbye and I say hello.'
    corpus, word_to_id, id_to_word = preprocess(text)
    vocab_size = len(word_to_id)
    C = create_co_matrix(corpus, vocab_size)
    W = ppmi(C)
    24172語団の語彙量が大きいほどベクトルの次元数が高くなるので、ベクトル降次元法を用いる-重要な情報を最大限に保持し、ベクトルの次元数を1減少させる.SVD(特接値分解):任意の3つの行列の積に分解する.
    X=U⋅S⋅VTX = U\cdot S\cdot V^TX=U⋅S⋅VT
    U, S, V = np.linalg.svd(W)
    U[:,:2] # 희소벡터 W를 밀집벡터 U로 변환한 후 2차원으로 감소
          2.Truncated SVD:対角要素(S)の上位ビットのみを抽出して次元を縮小する
              →rarr→急げ!
    from sklearn.utils.extmath import radomized_svd
    
    U, S, V = randomized_svd(W, n_components=wordvec_size,
                              n_iter=5, random_state=None)
  • ですが、現在の業界では馬の数が多いため、1つのレベルに縮小するのは適切ではありません.
  • 推論に基づく方法


  • ある単語の文脈における位置を推測する問題を繰り返し解くことで出現パターンを学習する.
  • の脈絡の情報を入力と、単語毎の出現確率出力
  • が出力される.
  • 正しい推測を学び、単語の分散表現
  • を得る.

  • 目標は簡潔な表現であるため,隠蔽層ニューロン数は入力層ニューロン数に比べて少なくなければならない.

  • 言語の違いによって、分散した表現も違います.

  • WinW In}WinもWoutW Out}Woutも単語の意味を含んでいますが、代表的なのはWinW In}Winのみの使用です

  • 以上の方法はWord 2 VectorのCBOW方法

  • Skip-gramがCBOWを逆転(中心語で脈絡を予測)
    L=1TΣt=1T(log⁡P(wt−1∣Wt+log⁡P(wt+1∣Wt))∵P(wt−1,wt+1∣wt)=P(wt−1∣wt)P(wt+1∣wt)L =\cfrac{1}{T}\Sigma_{t=1}^{T} (\log{P(w_{t-1}|W_t}+\log{P(w_{t+1}|W_t)})\\\because P(w_{t-1},w_{t+1}|w_t)= P(w_{t-1}|w_t) P(w_{t+1}|w_t)L=T1​Σt=1T​(logP(wt−1​∣Wt​+logP(wt+1​∣Wt​))∵P(wt−1​,wt+1​∣wt​)=P(wt−1​∣wt​)P(wt+1​∣wt​)

  • 性能はSkip-Gram、学習速度はCBOW
  • class SimpleCBOW:
        def __init__(self, vocab_size, hidden_size):
            V, H = vocab_size, hidden_size
            
            # 가중치 초기화
            W_in = 0.01 * np.random.randn(V,H).astype('f')
            W_out = 0.01 * np.random.randn(V,H).astype('f')
            
            # 계층 생성
            self.in_layer0 = MatMul(W_in)
            self.in_layer1 = MatMul(W_in)
            self.out_layer = MatMul(W_out)
            self.loss_layer = SoftmaxWithLoss()
            
            # 모든 가중치와 기울기를 리스트에 모은다.
            layers = [self.in_layer0, self.in_layer1, self.out_layer]
            self.params, self.grads = [], []
            for layer in layers:
                self.params += layer.params
                self.grads += layer.grads
                
            # 인스턴스 변수에 단어의 분산 표현을 저장한다.
            self.word_vecs = W_in
        def forward(self, contexts, target):
            h0 = self.in_layer0.forward(contexts[:,0])
            h1 = self.in_layer1.forward(contexts[:,1])
            h = (h0+h1)*0.5
            score = self.out_layer.forward(h)
            loss = self.loss_layer.forward(score, target)
            return loss
        def backward(self, dout=1):
            ds = self.loss_layer.backward(dout)
            da = self.out_layer.backward(ds)
            da += 0.5
            self.in_layer1.backward(da)
            self.in_layer0.backward(da)
            return None

    推論に基づく方法の速度を改善する


  • 非表示レイヤを計算する場合、入力レイヤニューロンの重み付けとインデックスは求められません.One Hot Vectorに乗算するため、同じ2479182の行ベクトルが抽出されます.
  • インデックスを使用する逆伝播で重複する問題が発生した場合、dhdhの各行の値はdwdwdwの各行の合計
  • となる.
  • の出力層を計算する場合、行列乗算を行うのではなく、インデックス
  • を行う.
  • 多分類問題の代わりにバイナリ分類問題を用いることにより,速度改善源を目標語Yes/No問題(Softmax→rarr→Sigmoid)
  • に変換する.
  • 負サンプリング:新しい損失関数に基づいてワード出現回数ごとに確率分布を求め、単語をサンプリングした後、損失はいずれもデフォルト確率分布の0.75平方(出現確率の低い単語を考慮)
  • である.

    統計に基づく方法と推論に基づく方法

  • 統計に基づく方法は1回の学習(バッチ学習)であり、推論に基づく方法は一部1回に複数回見、学習(ミニバッチ学習)
  • 統計に基づく方法は単語の類似性符号化であり、推論に基づく方法はこれまでの単語の類似性+学習複雑関係
  • である.
  • 新しい単語が出現すると,統計的基礎技法は最初から再学習され,推論的基礎技法はこれまでの重み付け値を初期値として
  • を再学習する.
    →rarr→それでも推論に基づく方法は優秀ではなく、優劣を区別できない.→rarr→skip-gramはSVDをコンカレントマトリクスに適用し、同じ→rarr→Glove:推論に基づくテクニック+統計に基づくテクニック(統計を損失関数に反映)
    リファレンス
    底からの深さ学習2(Saito Goki)