2つの特徴的分散表現を測る


用途

特定の文章群に対して文章分類を行う際,SVM(SupportVectorMachine)+FeatureSelectionを用いると,ただ分類ができるだけではなく,その分類に寄与する素性を順位付けすることができる.(なので,ノイズのない状態で実験できるので精度が上がる.次元圧縮にも役立つ)
その素性が分散表現でどう表現されるのか調べるのがこのプログラム.

使用するデータ

*SVM+FSで抽出されたテキストファイル
*word2vecで作成した日本語Wikipedia全記事のモデル

SVM+FS.txt
# 3.22/w.o
  1  0.2144     142     146 1:株式会社           -0.2309       9      21 1:レストランチェーン
  2  0.1981     108     119 1:を                     -0.1946       0       3 1:Tughril                
  3  0.1959     503     588 1:経営                  -0.1763      64     382 1:(\"                  
  4  0.1821      33      34 2:、                     -0.1759      70      86 1:)",                  
  5  0.1743     407     488 1:企業                  -0.1737       1      15 1:Grill                     

今回準備したデータは,SVMから出力されるオリジナルではなく,そこから作成したものです.省略してtop5のみあげています.

プログラム

ALL.py
# -*- coding:utf-8 -*-
from janome.tokenizer import Tokenizer
from gensim.models import word2vec
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
from sklearn.decomposition import PCA
from matplotlib import rcParams
from matplotlib.backends.backend_pdf import PdfPages
import glob
import torch.nn.functional as F
import torch
from scipy.cluster.hierarchy import linkage, dendrogram, fcluster

t = Tokenizer()
plt.rcParams["font.size"] = 18
rcParams['font.family'] = 'sans-serif'
rcParams['font.sans-serif'] = ['Hiragino Maru Gothic Pro', 'Yu Gothic', 'Meirio', 'Takao', 'IPAexGothic', 'IPAPGothic', 'VL PGothic', 'Noto Sans CJK JP']

model = word2vec.Word2Vec.load("./wiki_model.sg1")

reject = [')",','(','),','、','。',"'",',(',', ','・','。",','\\")','(\\"','\n']

def words_append(wordsp,wordsn):
    with open("./SVM+FS.txt") as fs:
        fslist0 = fs.readlines()
        fslist = []
        for m in fslist0:
            m = m.replace("\n","")
            fslist.append(m)
        del fslist0
        positive = []
        negative = []
        for m in fslist:
            line = m.split(" ")
            line2 = [a for a in line if a != '']
            try:
                positive.append(line2[4])
                negative.append(line2[8])
            except:
                pass
        del line,line2
        for i,m in enumerate(positive):
            mm = m.split(":")
            if not mm[1] in reject:
                mm[1] = mm[1].replace("\n","")
                tokens = t.tokenize(mm[1])
                for token in tokens:
                    # 品詞を取り出し
                    partOfSpeech = token.part_of_speech.split(',')[0]
                    if partOfSpeech == "名詞":
                        wordsp.append([mm[1],"r"])
                        break
        for i,m in enumerate(negative):
            mm = m.split(":")
            if not mm[1] in reject:
                mm[1] = mm[1].replace("\n","")
                tokens = t.tokenize(mm[1])
                for token in tokens:
                    # 品詞を取り出し
                    partOfSpeech = token.part_of_speech.split(',')[0]
                    if partOfSpeech == "名詞":
                        wordsn.append([mm[1],"b"])
                        break

def plotM(wordsp,wordsn):
    labels = []
    print(len(wordsp),len(wordsn))
    length = len(wordsp)
    datap = []

    j = 0
    while j <= length:
        try:
            datap.append(model[wordsp[j][0]])

        except:
            try:
                print(wordsp[j])
                del wordsp[j]
            except:
                pass
        j += 1
    # print(f'{"datap is"} {datap}')
    length = len(wordsn)
    datan = []

    j = 0
    while j <= length:
        try:
            datan.append(model[wordsn[j][0]])

        except:
            try:
                del wordsn[j]
            except:
                pass
        j += 1

    if len(wordsp) > len(wordsn):
        print("TypeA")
        for i in range((len(wordsp) - len(wordsn))):
            wordsp.pop()
    elif len(wordsn) > len(wordsp):
        print("TypeB")
        for i in range((len(wordsn) - len(wordsp))):
            wordsn.pop()

    print(len(wordsp),len(wordsn))
    length = len(wordsp)
    datap = []

    j = 0
    while j <= length:
        try:
            datap.append(model[wordsp[j][0]])
            x = wordsp[j][0]
            x = x + "+"
            labels.append(x)
        except:
            pass
        j += 1
    # print(f'{"datap is"} {datap}')
    length = len(wordsn)
    datan = []

    j = 0
    while j <= length:
        try:
            datan.append(model[wordsn[j][0]])
            x = wordsn[j][0]
            x = x + "-"
            labels.append(x)
        except:
            pass
        j += 1

    pca = PCA(n_components=2)
    pca.fit(datap)
    pca.fit(datan)
    data_pcap= pca.transform(datap)
    data_pcan= pca.transform(datan)

    pdata = torch.from_numpy(data_pcap)
    ndata = torch.from_numpy(data_pcan)
    out = F.kl_div(pdata, ndata)  
    print(out)


    #----
    A = data_pcap
    B = data_pcan
    AB = len(A)+len(B)
    #print(A)
    #data = A + B
    #print(len(data))
    print(len(labels))
    data = np.zeros(shape=(AB,2))
    count = 0
    for i,m in enumerate(A):
        data[i] = m
        count+=1
    for i,m in enumerate(B,start = count):
        #print(i)
        data[i] = m
    #print(data)
    print(f'{len(A)},{len(B)},{len(data)}')

    # 階層型クラスタリングの実施
    # ウォード法 x ユークリッド距離
    linkage_result = linkage(data, method='ward', metric='euclidean')

    # クラスタ分けするしきい値を決める
    threshold = 0.7 * np.max(linkage_result[:, 2])

    # 階層型クラスタリングの可視化
    plt.figure(num=None, figsize=(16, 9), dpi=200, facecolor='w', edgecolor='k')
    dendrogram(linkage_result, labels=labels, color_threshold=threshold)
    #dendrogram(linkage_result, labels=labels)
    plt.show()

    # クラスタリング結果の値を取得
    clustered = fcluster(linkage_result, threshold, criterion='distance')

    # クラスタリング結果を比較
    print(clustered)

    #------

    # length_data = len(data_pca)

#     i = 0
#     while i < length_data:
#         #点プロット
#         plt.plot(data_pca[i][0], data_pca[i][1], ms=5.0, zorder=2, marker="x", color=words[i][1])

#         #文字プロット
#         plt.annotate(words[i][0], (data_pca[i][0], data_pca[i][1]), size=7)

#         i += 1
#         title = "r料理店とb茶店"
#         plt.title(title, fontsize=18)
#     plt.savefig("./"+str(title)+".png")
#     # plt.show()

words = []
wordsp = []
wordsn = []
words_append(wordsp,wordsn)
plotM(wordsp,wordsn)

付け足し付け足しで作ったプログラムで,しかもセンスないのでゴミプログラムと言われるかもしれませんが,参考になれば嬉しいです.

KL-divはPyTorchで計算しています.
階層的クラスタリングはscipyでやっています.

結果を少し

料理店と茶屋という類義語(分類語彙表では)を用い,その単語を含む記事をWikipediaから抽出,SVM+FSをおこなった結果FS:200で分類精度が最高になった.それでも分類精度は0.664.

そこで抽出したFS正負top100単語の名詞のみの分散表現をwikipedia全記事から作成したモデルから抽出.PCAで2次元まで落とし,散布図を作成する.(ここはプログラムではコメントアウトされている)

赤と青の分散表現群は近いところで散布しているので,KL-divも0.0019となっている.
しかし.2つの群が混じり合い1つのクラスタを形成している部分もある(右端).
これを抽出するのが,階層的クラスタリングである.

これを見ると各クラスタを抽出することができる.

すいません,やっぱりこの記事みてわかる人は変態だと思います.
説明のグダリ具合がヤバめ.貴重な時間を使って読んでくださった方,ありがとうございます.

コメントありましたら,お待ちしてます.

参考文献