LSAとSVDの2種類のマトリックス分解


SVDとLSAについて
まずSVDとLSAとは何でしょうか.SVDのフルネームはsingular value decompositionです.通称の奇異値分解です.SVDの使い道はたくさんあります.例えば、PCA(主成分分析)、グラフィック圧縮、LSAを作ることができます.LSAは何ですか.LSAのフルネームはLatent semantic analysisです.中国語の意味は意味を隠す分析です.LSAはtopic modelの一種です.LSAに対する直感的な認識は文章に語があることであり,語は異なるテーマによって生成される.例えば,一つの文章には語計算機が含まれ,もう一つの文章には語計算機が含まれているが,一般的なベクトル空間から見ると,この2つの文章は関連していないが,LSAから見ると,この2つの単語は同じテーマに属しているので,2つの文章も関連している.
フィーチャー値フィーチャーベクトル
SVDについては,特徴値と特徴ベクトルが先に説明される必要がある.具体的な内容はwikiで見ることができますが、ここでは簡単な紹介をします.方程式Mに対して
M∗v=λ∗v
vはベクトルで、λ数ですvはMの特徴ベクトルと呼ばれていますλMの特徴値であり,Mを特徴分解して得ることができる.
M=Q∗Λ∗Q−1
ここでQは特徴ベクトルからなる行列,∧は対角行列であり,対角線上の要素が特徴値である.特徴の幾何学的理解はマトリクスMが実際には線形変換であり、線形変換がベクトルに与える影響は2種類あり、回転と引張りであり、特徴ベクトルはこのような線形変換の下で方向が変わらないベクトルであるが、長さは相応の引張りを行い、特徴値は引張りの程度である.
別の角度から言えば,特徴値が比較的大きいいくつかの項をとると,元の行列に近似した.
M≈Q1..k∗Λ1..k∗Q−11..k
これにより,より少ない要素で元の行列を近似的に表すことができるが,特徴分解の制限は比較的多く,例えば行列が方程式でなければならないことが要求される
奇異値分解
wikiはいいものですが、よく知りたければ、wikiを見に行くことをお勧めします.奇異値分解は行列をこのような形式に変えた.
M=U∗Σ∗VT
内Σ依然として対角行列であり、UとVは直交行列直交行列であり、U∗UT=Iである.
行列が線形変換であるという考え方に戻ります
Mを用いて空間内の1組の基を作用させると,図のように別の基を得ることができる.では、最初のベースのセットを回してみましょう.
このようにして,Mの変換を経て,ある直交基のセットから別の直交基のセットに変換した.つまり次のようです.
つまり我々は
M∗v1=σ1∗u1
M∗v2=σ2∗u2
任意のベクトルxに対して
x=v1∗(vT1∗x)+v2∗(vT2∗x)
そして私たちは
M∗x=M∗v1∗(vT1∗x)+M∗v2∗(vT2∗x)
M∗x=σ1∗u1∗(vT1∗x)+σ2∗u2∗(vT2∗x)
M=σ1∗u1∗vT1+σ2∗u2∗vT2
M=U∗Σ∗VT
はい、特徴値と特徴ベクトルに似たものを得ました.SVDが分解したのはMの線形変換の下で、直交基変換は直交基で、奇異値は引張りの程度です.実はSVDと特徴値と特徴ベクトルの関係はまだ大きい.
M∗MT=U∗Σ∗VT∗V∗ΣT∗UT
M∗MT=U∗Σ2∗UT
すなわちSVDはM∗MTとMT∗Mの特徴ベクトルを求める.同様にこのSVD分解という形式を得た後,我々は彼を利用して元のデータを次元ダウン操作することができる.
ここでは,RBG行列をそれぞれSVD,左上隅を原図,その他の順に最大100個,50個,20個,10個,5個の奇異値を取った近似画像を行った.
# -*- coding: utf-8 -*-
 
from scipy import linalg, dot
from PIL import Image
 
def main(num=5):
    im = Image.open('ai.jpg')
    pix = im.load()
    ma = [[], [], []]
    for x in xrange(im.size[0]):
        for i in xrange(3):
            ma[i].append([])
        for y in xrange(im.size[1]):
            for i in xrange(3):
                ma[i][-1].append(pix[x, y][i])
    for i in xrange(3):
        u, s, v = linalg.svd(ma[i])
        u = u[:, :num]
        v = v[:num, :]
        s = s[:num]
        ma[i] = dot(dot(u, linalg.diagsvd(s, num, num)), v)
    for x in xrange(im.size[0]):
        for y in xrange(im.size[1]):
            ret = []
            for i in xrange(3):
                tmp = int(ma[i][x][y])
                if tmp < 0:
                    tmp = 0
                if tmp > 255:
                    tmp = 255
                ret.append(tmp)
            pix[x, y] = tuple(ret)
    im.show()
    im.save('test.jpg')
 
if __name__ == '__main__':
    main()

行列を正規化してからSVDがPCAの形になると,分散最大化や誤差最小化で求めることができ,具体的にはPCA関連のものを見ることができる.だからscturtleと直接SVDの意味を議論したが、結局結論は出なかった.の
暗黙的意味解析
最後の暗黙的意味解析について述べると,まずテキストと語の行列を構築し,すなわち行列にとって各ベクトルは1つの文章を表し,各ベクトルには単語の出現回数である(より良いのは単語のtf/idf値であり,tf/idfは後述せず,具体的にはwikiを見ることができる).ではSVD分解後、次元を下げる行列が得られ、次のようになります.
つまり、私たちは100000の文章を持っていて、全部で500000の単語を持っていて、私たちは最大の100の単語を残して次元を下げることができて、そこで今私たちはこのように理解することができて、私たちは100のテーマを残して、その中でUは文章の対応するテーマの分布で、Vはテーマの対応する言葉の分布で、このようにして、私たちは騒音を減らすことができます.また,このように計算する文章間の相関性もより合理的であり,関連する単語を集約することができる.コードは次のとおりです.
# -*- coding: utf-8 -*-
 
import os
import re
import heapq
import codecs
from math import log
from scipy import linalg
 
import unigram_good_turing as seg
 
seg.init()
 
def tfidf(docs):
    doclen = len(docs)+1.0
    for doc in docs:
        wordtotal = sum(doc.values())+0.0
        for word in doc:
            tf = doc[word]/wordtotal
            idf = log(doclen/(sum([word in tmp for tmp in docs])+1))
            doc[word] = tf*idf
    return docs
 
def solve(data):
    re_zh, re_other = re.compile(ur"([\u4E00-\u9FA5]+)"), re.compile(ur"[^a-zA-Z0-9+#
]") blocks = re_zh.split(data) for item in blocks: if re_zh.match(item): for i in seg.solve(item): yield i else: tmp = re_other.split(item) for x in tmp: if x != '': pass def show(dic, p): p = heapq.nlargest(10, enumerate(p), key=lambda x:x[1]) print ' '.join(map(lambda x:dic[x[0]], p)) def main(): names = os.listdir('text') dic = {} cnt = 0 ma = [] for name in names: data = codecs.open('text/'+name, 'r', 'utf-8').read() doc = {} for word in solve(data): if not word in dic: dic[word] = cnt cnt += 1 tmp = dic[word] if tmp not in doc: doc[tmp] = 0 doc[tmp] += 1 ma.append(doc) ma = tfidf(ma) ret = [] for item in ma: tmp = [] for i in xrange(cnt): if i in item: tmp.append(item[i]) else: tmp.append(0) ret.append(tmp) u, s, v = linalg.svd(ret) for i in xrange(10): show(dict(zip(dic.values(), dic.keys())), list(v[i])) if __name__ == '__main__': main()