参考にさせて頂いたページ
qiita.com
2018年2月現在SCDVに関して日本語で書かれたページはここしか見つからなかった。
SCDVを勉強したいが元論文は英語で書かれていてもちろん読めるわけがない。
Githubのスクリプトを眺めてもなにがなんだかわからない。
そういった素人の自分が実際に動かしながらSCDVを理解しようと試みた。
そのため、Pythonスクリプトはほぼそのまま使用させて頂いた。
- 環境
- 下準備
- パッケージのインストール
- MeCabの導入
- テキストファイルの準備
- テキストファイルからデータフレームを作成
- 単語ベクトルの作成(word2vec)
- 単語ベクトルのクラスタリング
- TF-IDFを計算(必要なのはIDFのみ)
- 新しい単語ベクトルを求める
下準備
- pip3のインストール
sudo apt-get install python3-pip
- pip3のアップデート
sudo pip3 install --upgrade pip
- Jupyter Notebookのインストール(必須ではない)
sudo pip3 install jupyter
普通にJupyter Notebookが使用できることに驚いた。
パッケージのインストール
「pandas」を入れると「numpy」も同時に入る
sudo pip3 install pandas sudo pip3 install scipy sudo pip3 install gensim sudo pip3 install scikit-learn
MeCabの導入
sudo apt-get install mecab sudo apt-get install libmecab-dev sudo apt-get install mecab-ipadic sudo apt-get install mecab-ipadic-utf8 sudo pip3 install mecab-python3
テキストファイルの準備
wget http://www.rondhuit.com/download/ldcc-20140209.tar.gz tar axvf ldcc-20140209.tar.gz
「text」フォルダが作成され、その中には9個のフォルダが存在する。
フォルダを一つずつあけて、「LICENSE.txt」を削除していく。
(もちろん削除する前に中身はきちんと読みましょう)
テキストファイルからデータフレームを作成
import pandas as pd import glob import pickle 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 dirlist: path = "./text/"+i+"/*.txt" files = glob.glob(path) for j in 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) pickle.dump(df,open("textdata","wb"))
>>> type(df) <class 'pandas.core.frame.DataFrame'>
>>> df.shape (7367, 2)
7367行のデータフレームを「textdata」という名前で保存した。
単語ベクトルの作成(word2vec)
あらかじめ「model」というフォルダを作成しておく。
import logging from gensim.models import Word2Vec import MeCab import re import pickle df = pickle.load(open("textdata","rb")) tokenizer = MeCab.Tagger("-Owakati") sentences = [] print ("Parsing sentences from training set...") # Loop over each news article. for review in df["news"]: try: # Split a review into parsed sentences. result = tokenizer.parse(review).replace("\u3000","").replace("\n","") result = re.sub(r'[01234567890123456789!@#$%^&\-|\\*\“()_■×※⇒—●(:〜+=)/*&^%$#@!~`){}…\[\]\"\'\”:;<>?<>?、。・,./『』【】「」→←○]+', "", 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) print ("Training Word2Vec model...") # Train Word2Vec model. model = Word2Vec(sentences, workers=40, hs = 0, sg = 1, negative = 10, iter = 25,\ size=200, min_count = 20, \ window = 10, sample = 1e-3, seed=1) model_name = "word2vec" model.init_sims(replace=True) print ("Saving Word2Vec model...") # Save Word2Vec model. model.save("./model/"+model_name)
>>> type(model) <class 'gensim.models.word2vec.Word2Vec'> >>> type(model.wv.index2word) <class 'list'> >>> model.wv.index2word[0:10] ['の', 'に', 'を', 'が', 'は', 'て', 'で', 'た', 'と', 'し'] >>> len(model.wv.index2word) 11856 >>> type(model.wv.vectors) <class 'numpy.ndarray'> >>> model.wv.vectors.shape (11856, 200)
「model」フォルダに「word2vec」という名前で保存した。
単語ベクトルのクラスタリング
import pickle from gensim.models import Word2Vec from sklearn.mixture import GaussianMixture model = Word2Vec.load("./model/word2vec") word_vectors = model.wv.vectors clf = GaussianMixture(n_components=60, covariance_type="tied", init_params='kmeans', max_iter=50) clf.fit(word_vectors) idx_proba = clf.predict_proba(word_vectors) idx_proba_dict = dict(zip( model.wv.index2word, idx_proba )) pickle.dump(idx_proba_dict, open("./model/idx_proba_dict.pkl", "wb")) print ("Clustering Done...") #idx = clf.predict(word_vectors) #idx_dict = dict(zip( model.wv.index2word, idx )) #pickle.dump(idx_dict, open("./model/idx_dict.pkl", "wb"))
「idx」はそれぞれの単語がどのクラスタに属するかを表した配列
「idx_proba」はそれぞれの単語が各クラスタに属する確率
>>> type(idx) <class 'numpy.ndarray'> >>> idx.shape (11856,) >>> type(idx_proba) <class 'numpy.ndarray'> >>> idx_proba.shape (11856, 60)
>>> sum(idx_proba[0,]) 1.0 >>> sum(idx_proba[100,]) 1.0000000000000042 >>> sum(idx_proba[200,]) 0.99999999999999056 >>> sum(idx_proba[300,]) 1.0000000000000282
>>> idx[100:110,] array([56, 11, 9, 11, 48, 56, 57, 56, 56, 56]) >>> idx_proba[100:110,].argmax(axis=1) array([56, 11, 9, 11, 48, 56, 57, 56, 56, 56])
「idx_proba」を単語に紐づけした辞書型「idx_proba_dict」 として保存した。
「idx」はこの先使用しない。
TF-IDFを計算(必要なのはIDFのみ)
import pickle import numpy as np import MeCab import re from sklearn.feature_extraction.text import TfidfVectorizer,HashingVectorizer df = pickle.load(open("textdata","rb")) tokenizer = MeCab.Tagger("-Owakati") traindata = [] for review in df["news"]: result = tokenizer.parse(review).replace("\u3000","").replace("\n","") result = re.sub(r'[01234567890123456789!@#$%^&\-|\\*\“()_■×※⇒—●(:〜+=)/*&^%$#@!~`){}…\[\]\"\'\”:;<>?<>?、。・,./『』【】「」→←○]+', "", 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_ word_idf_dict = dict(zip(featurenames, idf)) pickle.dump(word_idf_dict, open("./model/word_idf_dict.pkl", "wb"))
単語に紐づけした辞書型「word_idf_dict」として保存した。
ここでの計算式は「log( (文章の総数+1) / (単語を含む文書数+1) ) + 1」で計算されている(自然対数)。
新しい単語ベクトルを求める
import numpy as np import pickle from gensim.models import Word2Vec model = Word2Vec.load("./model/word2vec") idx_proba_dict = pickle.load(open("./model/idx_proba_dict.pkl","rb")) word_idf_dict = pickle.load(open("./model/word_idf_dict.pkl","rb")) prob_wordvecs = {} for word in idx_proba_dict: prob_wordvecs[word] = np.zeros( 60 * 200, dtype="float32" ) for index in range(0, 60): try: prob_wordvecs[word][index*200:(index+1)*200] = model[word] * idx_proba_dict[word][index] * word_idf_dict[word] except: continue pickle.dump(prob_wordvecs,open("./model/prob_wordvecs.pkl","wb"))
>>> type(prob_wordvecs) <class 'dict'> >>> len(prob_wordvecs) 11856 >>> prob_wordvecs["明日"].shape (12000,)
単語ベクトルが12000次元になっていることがわかる。