å眮ããšãããæŠèŠ
ä»å¹Žã®EMNLP2017ã§ææ¡ãããŠããSCDV(Sparse Composite Document Vectors)ã«ã€ããŠãæ¥æ¬èªã®ã³ãŒãã¹(livedoor ãã¥ãŒã¹ã³ãŒãã¹)ã§æ€èšŒããŸããã
20171114远èš
fasttextããŒã¹ã®SCDVã«ããããã¹ãåé¡ç²ŸåºŠã远èšããŸããã
SCDVã®ã¢ãããŒã·ã§ã³

(https://dheeraj7596.github.io/SDV/)
ããæãã®ã©ã³ãã£ã³ã°ããŒãžãŸã§çšæããŠããŠããããªã£ãŠæããŸãããè«æã¯åœç¶ã®ããã«arxivã§å
¬éãããŠããŸãã倧æ£çŸ©ã
https://arxiv.org/pdf/1612.06778.pdf
HTMLã§èªã¿ããæ¹ã¯ãã¡ããhttps://www.arxiv-vanity.com/papers/1612.06778/
ãããèªãã§ããçæ§ã«ããã©ãã«ãããŠæç« ã®ãã¯ãã«ã欲ãããã§ãïŒåèªãããªããã§ãïŒãé¡ãããŸãïŒãªãã§ãããŸãããïŒããšããäŸé Œãæ¥ãããã©ããªæãã§å¯ŸåŠããŸãã§ããããã
doc2vecã§ããããããããšãdoc2vecã¯ç²ŸåºŠããããŸããããªãããåºæ¬ã«å¿ å®ã«TFIDFã§ãããããããããªã®ã§ãããäœãåŠç¿æžã¿Word2Vecã®ã¢ãã«ã䜿ã£ãŠãããæç« äžã«çŸããåèªã®ãã¯ãã«ãè¶³ãäžããŠæåŸã«å¹³åããããšã§ãã®æç« ã®ãã¯ãã«ãšããŠãããããªãã§ããããã
ãããšãããæãã«æã£ãŠè¡ã£ãŠããŸããŸããããSCDVã¯ãããªé¢šã«ãåèªãã¯ãã«ã®å¹³åã§ææžãã¯ãã«ãäœãéã®ç²ŸåºŠåäžãç®çãšããææ¡ææ³ã§ãã
SCDVã¯äœãããææ³ãªã®ã
ã§ã¯ãSCDVã¯ã©ã®ããã«ããŠãããå®çŸããã®ããšãããŸããšãword2vecã®ãã¯ãã«ç©ºéãããæãã«ä¿®æ£ããããšããã®æ¬è³ªã§ãã
ããå
·äœçã«èšããšãword2vecã®ãã¯ãã«ç©ºéãã¯ã©ã¹ã¿ãªã³ã°ããŠãååèªãã©ã®ãããã¯ã«å±ããŠããã®ãããããèæ
®ãããã¯ãã«ç©ºéã«ä¿®æ£ããŠãããããšèšãæãã§ããã¢ã«ãŽãªãºã èªäœã¯ãã¡ããã¡ãåçŽã§ã以äžã®éãã
- ããåèª$w_i$ã®ãã¯ãã«$wv_i$ãååŸ(word2vec)
- ããåèª$w_i$ã®IDFå€$idf(w_i)$ãèšç®ãã
- GMMã§å šåèªãã¯ãã«ã«ã€ããŠã¯ã©ã¹ã¿ãªã³ã°ãè¡ããããåèªãåã¯ã©ã¹ã¿$c_k$ã«å±ãã確ç$P(c_k|w_i)$ãååŸãã
- èªåœç©ºéã«ãããååèª$w_i$ã«ã€ããŠ5ãç¹°ãè¿ã
- åã¯ã©ã¹ã¿$c_k$ã«ã€ããŠã¯ã©ã¹ã¿ã®æ°ã ã6ãç¹°ãè¿ããã¯ã©ã¹ã¿ã®æ°ã ãèšç®ãããã7ãå®è¡ãã
- ã¯ã©ã¹ã¿ãèæ ®ããæ°ããªåèªãã¯ãã«$wcv_{ik}$ã$wv_i \times P(c_k|w_i)$ãšããŠç®åº
- IDFãèæ ®ããæ°ããªåèªãã¯ãã«$wtv_i$ã$idf(w_i)\times \oplus_k^Kãwcv_{ik}$ã§ç®åº($\oplus$ã¯concatenation)
- åããã¥ã¡ã³ã($nããN$)ã«ã€ããŠ9ã®æäœãç¹°ãè¿ã
- ããã¥ã¡ã³ã$D_n$ã«ã€ããŠãã¯ãã«ãåæåãã$D_n$ã«å«ãŸããåèª$w_i in D_n$ã«ã€ããŠãã¯ãã«ãè¶³ãåãããŠããå¹³åãã
- æåŸã«åŸãããææžãã¯ãã«$D_n$ãã¹ããŒã¹ã«ããŠ$SCDV_{D_n}$ãåŸã
7ãŸã§ã§ä¿®æ£ããåèªãã¯ãã«è¡šçŸãæã«å ¥ãã8,9,10ã§åããã¥ã¡ã³ãã®ãã¯ãã«ãæã«å ¥ããŸãã
tsneã§ææžãã¯ãã«ãå¯èŠåãããšãããªæãã«ãªããã ãããå·Šããã¬ãŒã³ã§å³ãSCDVã§ãã確ãã«è¯ããªã£ãŠããããã«èŠãããåã¯ã©ã¹ã¿ãã¯ã£ããããŠããããã«èŠããŸãã
ã§ãããã§ææžåé¡ã¿ã¹ã¯ããã£ãŠã¿ãããæ¢åã®ææžãã¯ãã«è¡šçŸããã粟床ãè¯ãã£ããããšãããããã®ã§ããã»ããšããããïŒ
ãŸããã®çåãæã¡ç Žãããã«ããã¡ããã¡ãããããå®éšããŠããã®ã§ãããããããŸããã£ãããšèšãæ°æã¡ã«ãªããã§ããã
åãã¯ãã«è¡šçŸææ³ã«ã€ããŠã§ããããŸãNTSGã¯Neural-Tensor-Skip-Gramãšãã(https://www.ijcai.org/Proceedings/15/Papers/185.pdf) çŸç¶ã®State-of-the-ArtãšãããŠããææ³ã®ããšã§ãã

skip-gramã¯åèªã®çµã¿åããããæèãäºæž¬ããããšã§ãã¯ãã«è¡šçŸãç²åŸããTWE(Topical Word Embedding)ãšããææ³ã§ã¯ããã«å ããŠãããã¯ãèæ
®ããŠã¿ãããšèšãææ³ã§ãã(https://github.com/largelymfs/topical_word_embeddings ããã¯2015ã®AAAI)ããã«å¯ŸããŠNTSGã§ã¯æŽã«ãããè¡åãtensor Factorizationã§æŽããŠããããšããæãã§ä¿®æ£ãããã®ã§ãã(ããŸãèªä¿¡ããªã)
ãã®äžã«æ¥ãŠããBowVãšããã®ã¯Bag of Words Vectorsã®ç¥ã§ãæ¬è«æå ±èè ã®äžã«ãå ¥ã£ãŠããVivekæ°ããã£ãŠããSCDVã®å身ãšãªãææ³ã§ãããã¡ããæ ¹å¹¹ã¯åãã§ããããœããã¯ã©ã¹ã¿ãªã³ã°ã§ã¯ãªãããŒãã¯ã©ã¹ã¿ãªã³ã°ã§ãäžã€IDFå€ãèããªãææ³ã§ããããŸãã¹ããŒã¹åãæœããªããããå®éã«ããã¹ãåé¡ããéã®ãã¬ãŒãã³ã°æéãèãããšãããªãã®æéãããã£ãŠããŸãããšããæ¹åãã€ã³ãããã£ãããã§ãã
åãæ¢ãããããã«è¿ãææ³ã ãèŠãŸãããããšã«ããSCDVã¯ãããããããŸã§ææ¡ãããŠããææ³ããªãåããŠããããã§ããfasttextãšãgloveãšããdoc2vecãšããšæ¯èŒããŠããªãã®ã¯äœã§ãªã®ïŒãšããæ°æã¡ã¯ãããŸãã...
ããŠãSCDVã®å©ç¹ãšããŠããäžã€äžãããããã€ã³ããšããŠãæè»œãªèšç®æéã§ããã ãã®ç²ŸåºŠãåŸããããšããã®ããããŸãã

æ¢åææ³ã§ããTWEãšæ¯èŒãããšèšç®æéã¯çŽäžåã®äžïŒïŒïŒ
æããSVCãªã®ã§ãããåŠç¿ããäºæž¬ã«é¢ããæéã¯TWEãšæ¯èŒããŠåå以äžïŒïŒïŒ
äžèº«ã®ã¢ã«ãŽãªãºã ã§ãã£ãŠããããšèªäœã¯ããããããã§ãããæéãšããŠããã ãæ¢åææ³ãšå·®ãåºãããšããéšåã ãèŠããçŽåŸã§ãããåæã«æ¢åææ³ãäžåã粟床ãåºããŠãããšããã®ãããããšããã ãªãšæããŸããçã«åŒ·ãææ³ã¯ãã€ã§ãã·ã³ãã«ãªã¢ãŒããã¯ãã£ããçãŸãããªããšã€ãã¥ãæããŸããã
æ¥æ¬èªã³ãŒãã¹ã§å®éš:livedoorãã¥ãŒã¹ã³ãŒãã¹ã«ã€ããŠ
ããŠãSCDVæ¬äœã®è©±ã¯ãããªãšãããšããŠãæ¥æ¬èªã®ã³ãŒãã¹ã§ãã£ãŠèŠãŠã©ããªãã®ããæ€èšŒããŠã¿ãŸããã
ä»åçšããã®ã¯åé ã«ã瀺ããããã«livedoorãã¥ãŒã¹ã³ãŒãã¹ã§ããäžèº«ã¯9åã®ãã¥ãŒã¹ãµã€ãããçŽ1000ä»¶ã®èšäºãåŒã£åŒµã£ãŠãããã®ã«ãªã£ãŠãããŸãã
SCDVããã³ãããŒã¯ã®äžã€ãšããŠãã20newsgroupãšããè±èªã³ãŒãã¹ãšæ§æã䌌ãŠããã®ã§ãæ€èšŒãšããç®çã§ã¯ã°ã£ã¡ãããšããã®ãã¡æ¥æ¬èªwikipediaãããã®å€§èŠæš¡ãªã³ãŒãã¹ã§ããã£ãŠèŠãªããŠã¯ãšããæãããããŸãã...
9åã®ãã¥ãŒã¹ãµã€ãã®å èš³ã¯æ¬¡ã®éããã ãããåç§°ããå 容ã¯ããããšæããŸãããããã¥ããã®ã¯PeachyãšSmaxãšãã§ãããããåè ã¯ãµããµã女åç³»ã®èšäºãåŸè ã¯ã¢ããªã²ãŒã ãªã©ã®èšäºãæ±ã£ãŠããã¡ãã£ã¢ã§ãã
| ã¯ã©ã¹ |
|---|
| ç¬å¥³éä¿¡ |
| ITã©ã€ããã㯠|
| å®¶é»ãã£ã³ãã« |
| livedoor home |
| movie-enter |
| Peachy |
| Smax |
| ã¹ããŒããŠã©ãã |
| ãããã¯ãã¥ãŒã¹ |
SCDVã®ã³ãŒãã¯Githubã§å ¬éãããŠãã(https://github.com/dheeraj7596/SCDV )ã»ãããã³ãããŒã¯ãšãªãããŒã¿ã»ããã«å¯Ÿããé©ç𿹿³ããã®ãŸãŸããã®ã§ãä»åã®ããŒã¿ã»ããã䜿ãã«ããã£ãŠã¯è³ç£ãã»ãšãã©ãã®ãŸãŸäœ¿ãããšãã§ããŸãããpython2ã ã£ãéšåãpython3ã«å¯Ÿå¿ãããã®ãã¡ãã£ãšæéã§ããã...
ãªããžããªå šäœã¯ãã¡ã: fufufukakaka/SCDV
python3ã«å¯Ÿå¿ãããŠ20newsgroupãå®è¡ããŠããã®ããã¡ã
livedoorãã¥ãŒã¹ã³ãŒãã¹ã§å®éšããŠããã®ããã¡ã
ããŒãããã¯ãéã«ãã£ãŠããŸã£ãã®ã§é©å®å¿ èŠãªãšããã¯ã³ãŒãã貌ã£ãŠãããªãã解説ããŸãã
ãŸãã¯word2vecãåŠç¿ããã+åèªãã¯ãã«ç©ºéãå¯èŠå
ãŸãã¯word2vecãåŠç¿ãããŠãããŸããlivedoorãã¥ãŒã¹ã³ãŒãã¹ã¯ããã¹ããã¡ã€ã«ã§æ£ãã°ã£ãŠããã®ã§ãããäžã€ã®ããŒã¿ãã¬ãŒã ã«ãããšããããã§ãã
import pandas as pd
import glob
import os
from tqdm import tqdm_notebook as tqdm
# preprocessing
dirlist = ["dokujo-tsushin","it-life-hack","kaden-channel","livedoor-homme",
"movie-enter","peachy","smax","sports-watch","topic-news"]
df = pd.DataFrame(columns=["class","news"])
for i in tqdm(dirlist):
path = "../japanese-dataset/livedoor-news-corpus/"+i+"/*.txt"
files = glob.glob(path)
files.pop()
for j in tqdm(files):
f = open(j)
data = f.read()
f.close()
t = pd.Series([i,"".join(data.split("\n")[3:])],index = df.columns)
df = df.append(t,ignore_index=True)
åºæ¥äžããããŒã¿ãã¬ãŒã ã¯ãããªæãã§ãã

## create word2vec
import logging
import numpy as np
from gensim.models import Word2Vec
import MeCab
import time
from sklearn.preprocessing import normalize
import sys
import re
start = time.time()
tokenizer = MeCab.Tagger("-Owakati")
sentences = []
print ("Parsing sentences from training set...")
# Loop over each news article.
for review in tqdm(df["news"]):
try:
# Split a review into parsed sentences.
result = tokenizer.parse(review).replace("\u3000","").replace("\n","")
result = re.sub(r'[0123456789ïŒïŒïŒïŒïŒïŒïŒïŒïŒïŒïŒïŒ ïŒïŒïŒ
ïŒ\-|\\ïŒ\âïŒïŒïŒ¿â Ãâ»âââ(ïŒãïŒ=)ïŒ*&^%$#@!~`){}âŠ\[\]\"\'\â:;<>?ïŒïŒïŒããã»,./ããããããâââ]+', "", result)
h = result.split(" ")
h = list(filter(("").__ne__, h))
sentences.append(h)
except:
continue
logging.basicConfig(format='%(asctime)s : %(levelname)s : %(message)s',\
level=logging.INFO)
num_features = 200 # Word vector dimensionality
min_word_count = 20 # Minimum word count
num_workers = 40 # Number of threads to run in parallel
context = 10 # Context window size
downsampling = 1e-3 # Downsample setting for frequent words
print ("Training Word2Vec model...")
# Train Word2Vec model.
model = Word2Vec(sentences, workers=num_workers, hs = 0, sg = 1, negative = 10, iter = 25,\
size=num_features, min_count = min_word_count, \
window = context, sample = downsampling, seed=1)
model_name = str(num_features) + "features_" + str(min_word_count) + "minwords_" + str(context) + "context_len2alldata"
model.init_sims(replace=True)
# Save Word2Vec model.
print ("Saving Word2Vec model...")
model.save("../japanese-dataset/livedoor-news-corpus/model/"+model_name)
endmodeltime = time.time()
print ("time : ", endmodeltime-start)
ããã§word2vecã®åŠç¿å®äºã§ããåºæ¥äžãã£ãword2vecã«å¯ŸããŠå¯èŠåããã£ãŠã¿ãŸããtSNEã§äºæ¬¡å ã«å§çž®ã
# plain word2vec t-SNE Visualization
word2vec_model=model
skip=0
limit=241
vocab = word2vec_model.wv.vocab
emb_tuple = tuple([word2vec_model[v] for v in vocab])
X = np.vstack(emb_tuple)
tsne_model = TSNE(n_components=2, random_state=0,verbose=2)
np.set_printoptions(suppress=True)
tsne_model.fit_transform(X)
plain_tsne = pd.DataFrame(tsne_model.embedding_[skip:limit, 0],columns = ["x"])
plain_tsne["y"] = pd.DataFrame(tsne_model.embedding_[skip:limit, 1])
plain_tsne["word"] = list(vocab)[skip:limit]
# plain_tsne["cluster"] = idx[skip:limit] # ã¯ã©ã¹ã¿ãèšç®ãçµãã£ãããšãªãããã§ã¯ã©ã¹ã¿ãä»äžã§ããŸã
# plain_tsne.plot.scatter(x="x",y="y",c="cluster",cmap="viridis",figsize=(8, 6),s=30)
plain_tsne.plot.scatter(x="x",y="y",figsize=(8, 6),s=30)
ååèªã®ã¯ã©ã¹ã¿ã§è²åããããšæ¬¡ã®ããã«ãªããŸããã
tsneããããåã«ã¯ã©ã¹ã¿ãªã³ã°ããŠããã®ããšå¯èŠåãããšãã€ããããªæãã«ãªãæ°ãããŸãã...
ããŸãããæãã«åãããŠãããšã¯èšããªãã§ããããã€ãã®ããšã§ããåèªããšã«å€ç«ããŠãããããã§ãªãã£ããããŠããŸãã
確çéã¿ä»ãåèªãã¯ãã«ãæ±ãã+åèªãã¯ãã«ç©ºéãå¯èŠå
ã§ã¯ãã¬ãŒã³ãªword2vecãå ã«ããŠSCDVãæ±ããŠãããŸãã
## create gwbowv
from sklearn.feature_extraction.text import TfidfVectorizer,HashingVectorizer
import pickle
from sklearn.mixture import GaussianMixture
from sklearn.preprocessing import LabelEncoder
from sklearn.model_selection import train_test_split
def cluster_GMM(num_clusters, word_vectors):
# Initalize a GMM object and use it for clustering.
clf = GaussianMixture(n_components=num_clusters,
covariance_type="tied", init_params='kmeans', max_iter=50)
# Get cluster assignments.
clf.fit(word_vectors)
idx = clf.predict(word_vectors)
print ("Clustering Done...", time.time()-start, "seconds")
# Get probabilities of cluster assignments.
idx_proba = clf.predict_proba(word_vectors)
# Dump cluster assignments and probability of cluster assignments.
pickle.dump(idx, open('../japanese-dataset/livedoor-news-corpus/model/gmm_latestclusmodel_len2alldata.pkl',"wb"))
print ("Cluster Assignments Saved...")
pickle.dump(idx_proba,open( '../japanese-dataset/livedoor-news-corpus/model/gmm_prob_latestclusmodel_len2alldata.pkl',"wb"))
print ("Probabilities of Cluster Assignments Saved...")
return (idx, idx_proba)
def read_GMM(idx_name, idx_proba_name):
# Loads cluster assignments and probability of cluster assignments.
idx = pickle.load(open('../japanese-dataset/livedoor-news-corpus/model/gmm_latestclusmodel_len2alldata.pkl',"rb"))
idx_proba = pickle.load(open( '../japanese-dataset/livedoor-news-corpus/model/gmm_prob_latestclusmodel_len2alldata.pkl',"rb"))
print ("Cluster Model Loaded...")
return (idx, idx_proba)
def get_probability_word_vectors(featurenames, word_centroid_map, num_clusters, word_idf_dict):
# This function computes probability word-cluster vectors
prob_wordvecs = {}
for word in word_centroid_map:
prob_wordvecs[word] = np.zeros( num_clusters * num_features, dtype="float32" )
for index in range(0, num_clusters):
try:
prob_wordvecs[word][index*num_features:(index+1)*num_features] = model[word] * word_centroid_prob_map[word][index] * word_idf_dict[word]
except:
continue
# prob_wordvecs_idf_len2alldata = {}
# i = 0
# for word in featurenames:
# i += 1
# if word in word_centroid_map:
# prob_wordvecs_idf_len2alldata[word] = {}
# for index in range(0, num_clusters):
# prob_wordvecs_idf_len2alldata[word][index] = model[word] * word_centroid_prob_map[word][index] * word_idf_dict[word]
# for word in prob_wordvecs_idf_len2alldata.keys():
# prob_wordvecs[word] = prob_wordvecs_idf_len2alldata[word][0]
# for index in prob_wordvecs_idf_len2alldata[word].keys():
# if index==0:
# continue
# prob_wordvecs[word] = np.concatenate((prob_wordvecs[word], prob_wordvecs_idf_len2alldata[word][index]))
return prob_wordvecs
def create_cluster_vector_and_gwbowv(prob_wordvecs, wordlist, word_centroid_map, word_centroid_prob_map, dimension, word_idf_dict, featurenames, num_centroids, train=False):
# This function computes SDV feature vectors.
bag_of_centroids = np.zeros( num_centroids * dimension, dtype="float32" )
global min_no
global max_no
for word in wordlist:
try:
temp = word_centroid_map[word]
except:
continue
bag_of_centroids += prob_wordvecs[word]
norm = np.sqrt(np.einsum('...i,...i', bag_of_centroids, bag_of_centroids))
if(norm!=0):
bag_of_centroids /= norm
# To make feature vector sparse, make note of minimum and maximum values.
if train:
min_no += min(bag_of_centroids)
max_no += max(bag_of_centroids)
return bag_of_centroids
䟿å©ãªé¢æ°çŸ€ãå®çŸ©ã
num_features = 200 # Word vector dimensionality
min_word_count = 20 # Minimum word count
num_workers = 40 # Number of threads to run in parallel
context = 10 # Context window size
downsampling = 1e-3 # Downsample setting for frequent words
model_name = str(num_features) + "features_" + str(min_word_count) + "minwords_" + str(context) + "context_len2alldata"
# Load the trained Word2Vec model.
model = Word2Vec.load("../japanese-dataset/livedoor-news-corpus/model/"+model_name)
# Get wordvectors for all words in vocabulary.
word_vectors = model.wv.syn0
# Load train data.
train,test = train_test_split(df,test_size=0.3,random_state=40)
all = df
# Set number of clusters.
num_clusters = 60
idx, idx_proba = cluster_GMM(num_clusters, word_vectors)
# Create a Word / Index dictionary, mapping each vocabulary word to
# a cluster number
word_centroid_map = dict(zip( model.wv.index2word, idx ))
# Create a Word / Probability of cluster assignment dictionary, mapping each vocabulary word to
# list of probabilities of cluster assignments.
word_centroid_prob_map = dict(zip( model.wv.index2word, idx_proba ))
ãããŸã§ã§ãååèªç©ºéã«å¯Ÿããã¯ã©ã¹ã¿ãªã³ã°ãå®äºããŸããword_centroid_mapã¯ååèªã®ã¯ã©ã¹ã¿ã瀺ãdictãword_centroid_prob_mapã¯ååèªã®ãœããã¯ã©ã¹ã¿ãªã³ã°ã«ããåã¯ã©ã¹ã¿ã«å±ãã確çã瀺ãdictã«ãªã£ãŠããŸãã
# Computing tf-idf values.
traindata = []
for review in all["news"]:
result = tokenizer.parse(review).replace("\u3000","").replace("\n","")
result = re.sub(r'[0123456789ïŒïŒïŒïŒïŒïŒïŒïŒïŒïŒïŒïŒ ïŒïŒïŒ
ïŒ\-|\\ïŒ\âïŒïŒïŒ¿â Ãâ»âââ(ïŒãïŒ=)ïŒ*&^%$#@!~`){}âŠ\[\]\"\'\â:;<>?ïŒïŒïŒããã»,./ããããããâââ]+', "", result)
h = result.split(" ")
h = filter(("").__ne__, h)
traindata.append(" ".join(h))
tfv = TfidfVectorizer(dtype=np.float32)
tfidfmatrix_traindata = tfv.fit_transform(traindata)
featurenames = tfv.get_feature_names()
idf = tfv._tfidf.idf_
# Creating a dictionary with word mapped to its idf value
print ("Creating word-idf dictionary for Training set...")
word_idf_dict = {}
for pair in zip(featurenames, idf):
word_idf_dict[pair[0]] = pair[1]
ããã§ã¯TFIDFå€ãèšç®ããŠããŸããæåŸã«ãããdictã«ãŸãšããŠããŸãã
# Pre-computing probability word-cluster vectors.
prob_wordvecs = get_probability_word_vectors(featurenames, word_centroid_map, num_clusters, word_idf_dict)
## 該åœãã颿°ãåæ²
def get_probability_word_vectors(featurenames, word_centroid_map, num_clusters, word_idf_dict):
# This function computes probability word-cluster vectors
prob_wordvecs = {}
for word in word_centroid_map:
prob_wordvecs[word] = np.zeros( num_clusters * num_features, dtype="float32" )
for index in range(0, num_clusters):
try:
prob_wordvecs[word][index*num_features:(index+1)*num_features] = model[word] * word_centroid_prob_map[word][index] * word_idf_dict[word]
except:
continue
return prob_wordvecs
ä»ãŸã§æ±ããŠæ¥ãååèªã®ã¯ã©ã¹ã¿ã確çã®dictãštfidfã®dictããããšword2vecã®ã¢ãã«ãçšããŠç¢ºçã§éã¿ä»ãããword2vecãæ±ããŠããŸãããã£ãŠããããšãšããŠã¯
prob_wordvecs[word][index*num_features:(index+1)*num_features] = model[word] * word_centroid_prob_map[word][index] * word_idf_dict[word]
ãçè§£ã§ãããããã°ããããããªãããªãšæããŸããã€ãŸããååèªãã¯ãã«ã®ãã®åèªã®ã¯ã©ã¹ã¿ããããŠåã¯ã©ã¹ã¿ã«å±ãã確çãIDFå€ãšäžç·ã«æãåãããŠããããã§ããããã«ãã£ãŠãã¬ãŒã³ãªword2vecã§ã¯200次å ã ã£ãåèªãã¯ãã«ã¯ã¯ã©ã¹ã¿æ°åã ãåã«ãªããŸãã60ã¯ã©ã¹ã¿ã ã£ãã12000次å ã
è«æã®è¶£æšãšããŠã¯ãã®ããšææžãã¯ãã«ãæ±ãããšããã«è¡ãã®ã§ããããã®åã«ãã¡ãã§ãåèªãã¯ãã«ç©ºéãå¯èŠåããŠã¿ãŸãããã³ãŒãã¯ã»ãšãã©åããªã®ã§çç¥ã
ãã¬ãŒã³ãªword2vecãšæ¯ã¹ããšã¯ã©ã¹ã¿ããã¡ããã¡ãã¯ã£ããããŠãã...ïŒ
ããã¯ç¢ºãã«ææžãã¯ãã«ã«ãæåŸ
ãæãŠãããªæãããšããããææžãã¯ãã«åæãããªããŠãã®ç¢ºçã§éã¿ä»ãããåèªãã¯ãã«ã ãã§ãããªãæçšãããªæãã
SCDVãæ±ãã
ããŠæåŸã«ææžã®ãã¯ãã«ãæ±ããå¿ èŠããããŸãã®ã§ããã£ãŠãããŸãã
# gwbowv is a matrix which contains normalised document vectors.
gwbowv = np.zeros( (train["news"].size, num_clusters*(num_features)), dtype="float32")
counter = 0
min_no = 0
max_no = 0
for review in train["news"]:
# Get the wordlist in each news article.
result = tokenizer.parse(review).replace("\u3000","").replace("\n","")
result = re.sub(r'[0123456789ïŒïŒïŒïŒïŒïŒïŒïŒïŒïŒïŒïŒ ïŒïŒïŒ
ïŒ\-|\\ïŒ\âïŒïŒïŒ¿â Ãâ»âââ(ïŒãïŒ=)ïŒ*&^%$#@!~`){}âŠ\[\]\"\'\â:;<>?ïŒïŒïŒããã»,./ããããããâââ]+', "", result)
h = result.split(" ")
h = filter(("").__ne__, h)
words = h
gwbowv[counter] = create_cluster_vector_and_gwbowv(prob_wordvecs, words, word_centroid_map, word_centroid_prob_map, num_features, word_idf_dict, featurenames, num_clusters, train=True)
counter+=1
if counter % 1000 == 0:
print ("Train News Covered : ",counter)
gwbowv_name = "SDV_" + str(num_clusters) + "cluster_" + str(num_features) + "feature_matrix_gmm_sparse.npy"
gwbowv_test = np.zeros( (test["news"].size, num_clusters*(num_features)), dtype="float32")
counter = 0
for review in test["news"]:
# Get the wordlist in each news article.
result = tokenizer.parse(review).replace("\u3000","").replace("\n","")
result = re.sub(r'[0123456789ïŒïŒïŒïŒïŒïŒïŒïŒïŒïŒïŒïŒ ïŒïŒïŒ
ïŒ\-|\\ïŒ\âïŒïŒïŒ¿â Ãâ»âââ(ïŒãïŒ=)ïŒ*&^%$#@!~`){}âŠ\[\]\"\'\â:;<>?ïŒïŒïŒããã»,./ããããããâââ]+', "", result)
h = result.split(" ")
h = filter(("").__ne__, h)
words = h
gwbowv_test[counter] = create_cluster_vector_and_gwbowv(prob_wordvecs, words, word_centroid_map, word_centroid_prob_map, num_features, word_idf_dict, featurenames, num_clusters)
counter+=1
if counter % 1000 == 0:
print ("Test News Covered : ",counter)
test_gwbowv_name = "TEST_SDV_" + str(num_clusters) + "cluster_" + str(num_features) + "feature_matrix_gmm_sparse.npy"
print ("Making sparse...")
# Set the threshold percentage for making it sparse.
percentage = 0.04
min_no = min_no*1.0/len(train["news"])
max_no = max_no*1.0/len(train["news"])
print ("Average min: ", min_no)
print ("Average max: ", max_no)
thres = (abs(max_no) + abs(min_no))/2
thres = thres*percentage
# Make values of matrices which are less than threshold to zero.
temp = abs(gwbowv) < thres
gwbowv[temp] = 0
temp = abs(gwbowv_test) < thres
gwbowv_test[temp] = 0
# saving gwbowv train and test matrices
np.save("../japanese-dataset/livedoor-news-corpus/model/"+gwbowv_name, gwbowv)
np.save("../japanese-dataset/livedoor-news-corpus/model/"+test_gwbowv_name, gwbowv_test)
ææžãã¯ãã«ã®å¯èŠå
ã§ã¯ãSCDVãæã«å ¥ã£ããšããã§ããã¬ãŒã³word2vecã§ã®ææžãã¯ãã«ãšãSCDVã§ã®å¯èŠåãæ¯èŒããŠã¿ãŸãããã
ãã¬ãŒã³word2vecã«ããææžãã¯ãã«å¯èŠå
ãŸãã¯ãã¬ãŒã³word2vecã®åèªãã¯ãã«å¹³åã§è¡šããææžãã¯ãã«ã®å¯èŠåçµæããã
import seaborn as sns
# document vector visualization
## plain word2vec
def plain_word2vec_document_vector(sentence,word2vec_model,num_features):
bag_of_centroids = np.zeros(num_features, dtype="float32")
for word in sentence:
try:
temp = word2vec_model[word]
except:
continue
bag_of_centroids += temp
bag_of_centroids = bag_of_centroids / len(sentence)
return bag_of_centroids
plainDocVec_all = {}
counter = 0
num_features = 200
for review in all["news"]:
# Get the wordlist in each news article.
result = tokenizer.parse(review).replace("\u3000","").replace("\n","")
result = re.sub(r'[0123456789ïŒïŒïŒïŒïŒïŒïŒïŒïŒïŒïŒïŒ ïŒïŒïŒ
ïŒ\-|\\ïŒ\âïŒïŒïŒ¿â Ãâ»âââ(ïŒãïŒ=)ïŒ*&^%$#@!~`){}âŠ\[\]\"\'\â:;<>?ïŒïŒïŒããã»,./ããããããâââ]+', "", result)
h = result.split(" ")
h = filter(("").__ne__, h)
words = list(h)
plainDocVec_all[counter] = plain_word2vec_document_vector(words,word2vec_model,num_features)
counter+=1
if counter % 1000 == 0:
print ("All News Covered : ",counter)
## visualize all document vector
emb_tuple = tuple([plainDocVec_all[v] for v in plainDocVec_all.keys()])
X = np.vstack(emb_tuple)
plain_w2v_doc= TSNE(n_components=2, random_state=0,verbose=2)
np.set_printoptions(suppress=True)
plain_w2v_doc.fit(X)
alldoc_plainw2v_tsne = pd.DataFrame(plain_w2v_doc.embedding_[:, 0],columns = ["x"])
alldoc_plainw2v_tsne["y"] = pd.DataFrame(plain_w2v_doc.embedding_[:, 1])
alldoc_plainw2v_tsne["class"] = list(all["class"])
sns.lmplot(data=alldoc_plainw2v_tsne,x="x",y="y",hue="class",fit_reg=False,size=8)
çŽ çŽã«seabornäœ¿ãæ¹ããã£ã±ãæ£åžå³ã§ã®è²åãããããããªãµãšæããããã§ã¯seabornã§æç»ããŠããŸãã
ã¯ã©ã¹ã¿ãšåãããŠã¿ããšããããŸã§æªãããªçµæã§ã¯ãªãã£ãœãã§ãããæŠããŸãšãŸã£ãŠãããšæããŸãããã¡ããã¡ããé£ã³å°çãªææžãã¯ãã«ãããããã§ãã
SCDVã«ããææžãã¯ãã«å¯èŠå
ã§ã¯SCDVã®çµæãèŠãŠã¿ãŸãããã
ããïŒãªãããã¬ãŒã³ãªword2vecã§äœã£ãææžãã¯ãã«ã®æ¹ãããŸãããããŠãããããª... ãããããã ãšäžç·ããã«ãªã£ãŠããããã«èŠããŸãã倧äžå€«ã§ãããã...
ãšäžç¬æã£ãã®ã§ãããããããã«ããŽãªããšã®ç¹æ§ãèããŠã¿ããš
- äžçªæ£ãã°ã£ãŠãããã³ã¯ã¯ãããã¯ãã¥ãŒã¹...åéãéå®ããªããã¬ã€ãã³ã°ãã¥ãŒã¹ãªã©ãäž»ãªã®ã§ãè²ã ãªã«ããŽãªã«åºããã®ã¯ééã£ãŠããªããšããããããããããã¹ã
- éè²ã®smaxãšæ©ã®ITã©ã€ãããã¯ãé»ç·ã®å®¶é»ãã£ã³ãã«ã¯æ··ããåã£ãŠãã...å 容ãèããã°æ··ãã£ãŠããŠãäžèªç¶ã§ã¯ãªã
- èµ€è²ã®ç¬å¥³éä¿¡ã玫è²ã®ã¹ããŒããŠã©ããã¯ãããªã«æ£ãã°ã£ãŠããªã...ãããªãšããæã
- éç·ã®peachyãç¬å¥³éä¿¡ã«è¿ãäœçœ®ã«ç§»å...ããçŽåŸã§ããäœçœ®ã«ãªã£ã
- ç·è²ã®livedoor-homeã忣...å 容çã«ã¯äžé話ã¿ãããªæããªã®ã§ãããã¯ãã¥ãŒã¹ãšè¿ãïŒ
- movie-enterãç¬å¥³éä¿¡ã«è¿ããšããã«ç§»åïŒ...è²çã«å·®åãåããã¥ããã§ãããããããç§»åããŠããŸããããã¯ã¡ãã£ãšããããªãã£ãã
ã¡ãªã¿ã«livedoor-homeã®äžæç®ã¯ãããªæãã§ã(ãªããèœãšããŠæ¥ãæç¹ã§livedoor-hommeãšããè¬ã®ã©ãã«ã ã£ãïŒããããªã«ãããã¯ã«ãŸãšãŸãã¯ãªãããã§ãããã
(20181115远èš: livedoor hommeã«ã€ã㊠http://news.livedoor.com/article/detail/4533799/)
20newsgroup datasetã¯ãã¥ãŒã¹ã®ã«ããŽãªããšããããšã§ã¯ã£ããåãããŠããããšãåæã«ãããããŒã¿ã»ããã ãšæãã®ã§ãããããã«å¯ŸããŠãã¡ãã®ã³ãŒãã¹ã¯ç°ãªãã¡ãã£ã¢ããã®èšäºååŸãšããããšãªã®ã§ããããååžã«ãªã£ãŠããŸãã®ã§ãããã
ããèãããšãããã¡ãããšãããã¯çã«åãããããŒã¿ã»ãããããªããšSCDVã£ãŠå¹æãåºãªãã®ã§ã¯ããšããæ°æã¡ãåºãŠæ¥ãŸã...ããŒããã©ããªãã§ããããããŸãæ€èšŒããŠã¿ãŸãããã
ããã¹ãåé¡ã®ç²ŸåºŠ
å è«æã§ã¯SVCã§ãããæéãããã£ãŠãããããªãã®ã§ãLightGBMã§ãããŸãããæéããããããªãã£ãã®ã§ãäœãèšå®ããŠããªãçŽ ã®LGBMClassifierã§ãã£ãŠããŸããå€ã¯ã©ã¹åé¡ãªã®ã§ãç®çãã©ã¡ãŒã¿ã ãæŽããŠãããŸããã
## test lgb
from sklearn.metrics import classification_report
import lightgbm as lgb
start = time.time()
clf = lgb.LGBMClassifier(objective="multiclass")
clf.fit(gwbowv, train["class"])
Y_true, Y_pred = test["class"], clf.predict(gwbowv_test)
print ("Report")
print (classification_report(Y_true, Y_pred, digits=6))
print ("Accuracy: ",clf.score(gwbowv_test,test["class"]))
print ("Time taken:", time.time() - start, "\n")
ããã«å¯ŸããŠããã¬ãŒã³ãªword2vecã®å¹³åãfasttextã§ãåèªã®ãã¯ãã«ãå¹³åãããã®ãææžãã¯ãã«ãšããŠããŸããdoc2vecã§ãææžãã¯ãã«ãååŸããåãã³ãŒãã§ç²ŸåºŠãæ±ããŠã¿ãŸãããæ¯èŒãã衚ããã¡ããå šãŠlightgbmã§ã®çµæã§ãããªããSCDVã®å ã³ãŒãã®æç¹ã§train,testãåããããŠããããããã¡ããåæ§ã«ããŒã«ãã¢ãŠãæ€èšŒæ³ã§ã®ç²ŸåºŠãšãªããŸããCVãããªããŠããããªããã
| method | accuracy | precision | recall | f1 |
|---|---|---|---|---|
| SCDV | 0.8742 | 0.8743 | 0.8742 | 0.8730 |
| fastext-average | 0.8588 | 0.8580 | 0.8588 | 0.8576 |
| doc2vec | 0.8398 | 0.8386 | 0.8398 | 0.8385 |
| word2vec-average | 0.8249 | 0.8221 | 0.8249 | 0.8217 |
ãããããŸãã§ããïŒæ£çŽãããŸã§å·®ãã€ããšã¯æã£ãŠããŸããã§ãããword2vecã®åèªãã¯ãã«å¹³åãšæ¯èŒãããšå®ã«5%ãå·®ãã€ããšãããããã¯ãªããªã倧ããçµæã§ã¯ãªãã§ããããã
ããããã°ãdoc2vecã¯paragraoh vectorãæèããææ³ã®ã¯ããªã®ã«ãfasttextã®åèªãã¯ãã«å¹³åã«è² ããŠããŸã£ãŠããŸãããããããã
sklearn.metrics.classification_reportã§åã¯ã©ã¹ããšã®æçžŸãèŠãŠã¿ãŸããããã§ã¯word2vec-averageãšSCDVãæ¯èŒããŸãã
SCDVã®åã¯ã©ã¹åé¡ç²ŸåºŠ
| class | precision | recall | f1-score | support |
|---|---|---|---|---|
| dokujo-tsushin | 0.7947 | 0.8486 | 0.8208 | 251 |
| it-life-hack | 0.8360 | 0.8427 | 0.8393 | 248 |
| kaden-channel | 0.8875 | 0.8554 | 0.8711 | 249 |
| livedoor-home | 0.8545 | 0.6438 | 0.7343 | 146 |
| movie-enter | 0.9179 | 0.9389 | 0.9283 | 262 |
| peachy | 0.7992 | 0.7992 | 0.7992 | 254 |
| smax | 0.9632 | 0.9886 | 0.9757 | 265 |
| sports-watch | 0.8770 | 0.9428 | 0.9087 | 280 |
| topic-news | 0.9233 | 0.8945 | 0.9087 | 256 |
| avg / total | 0.8743 | 0.8742 | 0.8730 | 2211 |
word2vec-averageã®åã¯ã©ã¹åé¡ç²ŸåºŠ
| class | precision | recall | f1-score | support |
|---|---|---|---|---|
| dokujo-tsushin | 0.8023 | 0.8247 | 0.8133 | 251 |
| it-life-hack | 0.8208 | 0.7943 | 0.8073 | 248 |
| kaden-channel | 0.8127 | 0.7670 | 0.7892 | 249 |
| livedoor-home | 0.72 | 0.4931 | 0.5853 | 146 |
| movie-enter | 0.8530 | 0.9083 | 0.8798 | 262 |
| peachy | 0.7568 | 0.7598 | 0.7583 | 254 |
| smax | 0.9202 | 0.9584 | 0.9390 | 265 |
| sports-watch | 0.8630 | 0.9 | 0.8811 | 280 |
| topic-news | 0.7971 | 0.8593 | 0.8270 | 256 |
| avg / total | 0.8221 | 0.8249 | 0.8217 | 2211 |
èå¯
ããããŠã¿ããšãf1ã§ã¯å®å šã«å§åã§ãããç¹ã«ãlivedoor-homeã®äžããæ¹ãåãŸãããå ã»ã©å¯èŠåãããã¬ãŒã³word2vecã«ããææžãã¯ãã«ç©ºéãèŠãŠã¿ããšã確ãã«livedoor-homeã¯äžç®æã«åºãŸã£ãŠã¯ããŸãããä»ã®ã¯ã©ã¹ã¿ãšã®è·é¢ãè¿ãåæ²¡ãæ°å³ã«ãèŠããŸãããããSCDVã«ãªããšãããããèŠããããã«ãªã£ãŠãããšè§£éã§ããããªæ°ãããŸãã
éã«äžããå¹ ãå°ãªãã£ãã¯ã©ã¹ãšããŠã¯ç¬å¥³éä¿¡ãã¹ããŒããŠã©ããã2%çšåºŠã®äžæã«ãšã©ãŸã£ãŠããŸããããããããã¯ããã¬ãŒã³word2vecã§æ§ç¯ããææžãã¯ãã«ã®æç¹ã§æ¢ã«ã¯ã©ã¹ã¿ãã¯ã£ããããŠããã®ã§ãSCDVã«ããæ©æµãåãã«ããã£ãã®ã§ã¯ãªãããšæšå¯ã§ããŸãã粟床ãäžãã£ãã®ã¯è¿ãã£ãã¯ã©ã¹ã¿ãšã®å·®ãããããã¯ã£ããããããšã«ãããã®ã§ã¯ãªãã§ããããã
ãŸãšããšæãã課é¡ãªã©
ãšããããã§ãéåžžã«åçŽãªã¢ã€ãã¢ã§ãæè»œã«é«ã粟床ãåŸãããSCDVã¯æ¥æ¬èªã³ãŒãã¹ã§ãåæ§ã«ç²ŸåºŠåäžãèŠèŸŒããããšãããããŸããã
ä»å詊ããã³ãŒãã¹ã¯åè¿°ããããã«ã¯ã£ãããšå 容ãåãããŠãããšã¯èšãé£ãéšåããã£ãã«ããããããã粟床åäžã確èªããããšãã§ããã®ã§ãšãŠãæºè¶³ã§ããæ¬¡ã¯wikipediaã³ãŒãã¹ã®äž»èŠã«ããŽãª(ç§åŠãåŠåãæŽå²ãªã©)ã®9ã¯ã©ã¹ã§ããã£ãŠã¿ããã§ããã
ãŸããSCDVãæ§æãã確çã§éã¿ä»ãããåèªãã¯ãã«èªäœãéåžžã«æçšããã§ãããä»åã¯ã³ãµã€ã³é¡äŒŒåºŠã§ããåèªã«å¯ŸããŠã©ã®ãããªåèªãè¿ã£ãŠããããã«å€åããã®ãã®æ€èšŒã¯ã§ããŸããã§ããããtSNEã§å¯èŠåããçµæãèŠãéããšãŠãæåŸ ãæãŠããã§ããwikipediaã§åŠç¿ãããã®ãã¯ãã«ããLSTMãªã©ãã£ãŒãç³»ã®ææ³ã®embeddingå±€ã«åã蟌ãããšã§ç²ŸåºŠåäžãæããããããªãããªãšã
SCDVãæ¹åãããšãããäœãã§ãããã§ãããã... æ¢åææ³ã®TWEã¯ãããã¯ãèæ
®ããããšããããšã§LDAã䜵çšããã¡ãœããã§ããããSCDVã¯TFIDFã§æãããããã¯ãã¯ãã«ãäžããŠããããšããè§£éã§ããã§ããã°ãTFIDFã§ã¯ãªãLDAã§ã¯ã©ããªããã ããïŒãšããçåãåºãŠããããã§ããã
ãŸãä»åæ€èšŒã®çµæã§fasttext-averageãšã®å·®ããããªã«ãªãã£ããšããããšããèžãŸãããšãword2vecã§ã¯ãªãfasttextãããŒã¹ã«SCDVãæ§ç¯ããæ¹ãè¯ããšæãããŸããæ¥æ¬èªã§ããã ã£ãããã§ããããè±èªãªãããã«ç²ŸåºŠãäžããããããªãã§ããããã
wikipediaã§åŠç¿ããããfasttextçã®SCDVã詊ããäž¡è ãpythonã®ã³ãŒãã«ãŸãšããŠgensimã®word2vecã¿ãããªã¡ãœãããçšæããŠäœ¿ããããã«ããããªã©ãä»åŸã®Todoã£ãœãããªãããã£ãŠãããŸã
20171113远èš: 次å ãäžãã£ãããšã§ç²ŸåºŠãåäžããã ãã§ã¯ããšããçåãåºãŠæ¥ãã®ã§æ¬¡å åæžããäžã§ã®æ¯èŒãèããã
20171114远èš: FasttextããŒã¹ã®SCDVããããŸãã
ãšããããã§ãfasttext200次å ã䜿ã£ãŠ60ã¯ã©ã¹ã¿ãšãããã©ã¡ãŒã¿ã§SCDVãæ§ç¯ããå¶åºŠãåºããŠã¿ãçµæããã¡ãã§ãã
| method | accuracy | precision | recall | f1 |
|---|---|---|---|---|
| SCDV with fasttext | 0.8854 | 0.8837 | 0.8854 | 0.8833 |
| SCDV | 0.8742 | 0.8743 | 0.8742 | 0.8730 |
| fastext-average | 0.8588 | 0.8580 | 0.8588 | 0.8576 |
| doc2vec | 0.8398 | 0.8386 | 0.8398 | 0.8385 |
| word2vec-average | 0.8249 | 0.8221 | 0.8249 | 0.8217 |
fasttextãã€ãã
ãšããããšã§ãè«æã®å®éšçµæä»¥äžã®çµæãåºãããã§ããããã®è«æã§æã倧äºãªã®ã¯ãã¯ãã«ç©ºéã«ãããã¯ã©ã¹ã¿ãªã³ã°ã®çµæãIDFå€ãšããäž¡è ãèæ ®ããããšã§ãã¯ãã«è¡šçŸãåäžãããããšãã§ãããïŒãšããæ çµã¿ã®éšåã ãšæãã®ã§ããªãã«ãã䟡å€ãé«ãè«æã ãšæããŸããitem2vecç³»ã®ææ³ã§ã¬ã³ã¡ã³ããªã©ãè¡ãªã£ãŠããå Žåã§ã¯fasttextã¯æå³ããªã(subwordã«è©²åœãããã®ããªã)ã®ã§ããã®ãšãã¯äœ¿ããŸããããæåããŒã¹ã§äœããããã ã£ããè¿·ããfasttextããŒã¹ã®SCDVã§ããã¹ããããªãã§ããããã











