Universal Sentence Encoderは、文をベクトル化する手法です。

Googleの研究者達が開発したもので、2018年にTensorflow Hubで公開されました。
多言語に対応しているところが特徴で、日本語と英語で同じ意味なら、ほぼ同じベクトルに変換してくれます。
また、文中の単語の意味や語順を考慮した文章ベクトルを、ニューラルネットワークによるend-to-endな学習で獲得できるので、意味を解釈したベクトルに変換可能です。

関連記事:Python(gensim)と日本語Word2Vecで単語ベクトル可視化

Universal Sentence Encoderの使い方

tensorflow_hubにある学習済みモデルを使うと、とても簡単にテキストデータを512次元の文章ベクトルに変換できます。

text = ["今日はいい天気だな"]

import tensorflow_hub as hub import numpy as np import tensorflow_text #テキストを文章ベクトルに変換 embed = hub.load("https://tfhub.dev/google/universal-sentence-encoder-multilingual/3") embeddings = embed(text) print(embeddings)

日本語を文章ベクトルに変換できました。非常に簡単です。

Universal Sentence Encoderの文書分類モデル

Universal Sentence Encoderで分類モデルを作成します。

テキストデータの準備

株式会社ロンウイット様のライブドアニュースのデータセットを利用します。

前処理は、以下の記事を参考にさせて頂きました。
参考記事:【実装解説】日本語版BERTでlivedoorニュース分類:Google Colaboratoryで(PyTorch)

Universal Sentence Encoderで文書ベクトル化

TensorFlow HubにあるUSEの学習済みモデルの中で、universal-sentence-encoder-multilingualを利用しました。large版より軽くて、少量データでもうまくいきやすいので便利です。

なお、データ件数が多いとUSEは少し時間がかかるので、データセットを200件に絞り込みました。

import tensorflow as tf
import tensorflow_hub as hub
import tensorflow_text

import numpy as np
import pandas as pd

df = pd.read_csv('livedoor_data_all.csv', encoding='utf-8')
df = df.iloc[850:1150,:]
text_df = df.iloc[:,1].values
y = df.iloc[:,2]

text_list = text_df.tolist()

# 学習済みモデルの読み込み
#embed = hub.load("https://tfhub.dev/google/universal-sentence-encoder-multilingual-large/3") 
embed = hub.load("https://tfhub.dev/google/universal-sentence-encoder-multilingual/3")

embeddings = embed(text_list)

# numpy配列に変換
embeddings =embeddings.numpy()

なお、tensorflow_textのimportでエラーが起きたときは、tensorflowとのバージョンがずれているケースが多いです。バージョン合わせるとエラーが解消されます。

ランダムフォレストで分類モデル作成

ランダムフォレストで分類モデルを作成します。
from sklearn.ensemble import RandomForestClassifier
from sklearn.model_selection import cross_validate

# ランダムフォレストのパラメータ
clf = RandomForestClassifier(n_estimators=10)

## Cross-Validation
scoring = {"a": "accuracy",
           "p": "precision_macro",
           "r": "recall_macro",
           "f":"f1_macro"}

## Cross-Validation
score = cross_validate(clf, embeddings, y, scoring=scoring,cv=2)
print('accuracy:', score['test_a'].mean())
print('precision:', score['test_p'].mean())
print('recall:', score['test_r'].mean())
print('f1:', score['test_f'].mean())

精度は以下になりました。なかなか良いですね。
accuracy: 0.9633333333333334
precision: 0.9810344827586207
recall: 0.7386363636363636
f1: 0.8132490116135409

実験:Bag-of-Wordsとの分類精度比較

USEの性能を確認するため、Bag-of-Wordsと分類精度を比較してみます。

形態素解析

MeCabで形態素解析します。文を単語に分割し、「名詞、形容詞、動詞」のみを抽出します。
import numpy as np
import pandas as pd
import MeCab
from gensim import corpora, matutils

df = pd.read_csv('livedoor_data_all.csv', encoding='utf-8')
df = df.iloc[850:1150,:]
text_df = df.iloc[:,1].values
y = df.iloc[:,2]

# ChaSen 互換形式
mecab = MeCab.Tagger("-Ochasen")

#形態素解析
def ChaSen(text):   
    word_l = []   
    res = mecab.parse(text) #構文解析
    words = res.split('\n')[:-2] # 最後にEOSが入るためそれ以外を取得 
    
    for word in words:
        pos = word.split('\t') # タブで各行を切り分け
        if '名詞'or'形容詞'or'動詞' in pos[3]: #品詞がある4番目にアクセス
            word_l.append(pos[2]) # 原型を取得
    return word_l

# 単語に分割したリスト作成
word_list = []
for text in text_df:
    b = ChaSen(text)
    word_list.append(b)
print(word_list)

Bag-of-Wordsの文書ベクトル作成

gensimでBag-of-Words形式に変換します。
#gensimでbowに変換
dictionary = corpora.Dictionary(word_list)
n_word = len(dictionary)

x = []
for i in word_list:
    bow_id = dictionary.doc2bow(i)
    bow = matutils.corpus2dense([bow_id], n_word).T[0]
    x.append(bow)

x = np.array(x, 'f')
print(x.shape)

x_pd = pd.DataFrame(x)

TF-IDF変換

TF-IDFに変換します。
# TF-IDF
from sklearn.feature_extraction import text tfidf = text.TfidfTransformer(norm=None) #帰ってくるのはスパースマトリックス res = tfidf.fit_transform(x_pd) x_pd_TFIDF = pd.DataFrame(res.toarray(), columns=x_pd.columns)

主成分分析

主成分分析で2次元に次元削減します。
# PCA
from sklearn import decomposition
pca = decomposition.PCA(n_components=100)  
x_2 = pca.fit_transform(x_pd_TFIDF)
x_2 = pd.DataFrame(x_2)

ランダムフォレストで分類モデル作成

ランダムフォレストで分類モデルを作成します。
from sklearn.ensemble import RandomForestClassifier
from sklearn.model_selection import cross_validate


# ランダムフォレストのパラメータ
clf = RandomForestClassifier(n_estimators=10)

scoring = {"a": "accuracy",
           "p": "precision_macro",
           "r": "recall_macro",
           "f":"f1_macro"}

## Cross-Validation
score = cross_validate(clf, x_2, y, scoring=scoring,cv=3)
print('accuracy:', score['test_a'].mean())
print('precision:', score['test_p'].mean())
print('recall:', score['test_r'].mean())
print('f1:', score['test_f'].mean())
精度は以下になりました。
accuracy: 0.9533333333333333
precision: 0.8626577604726101
recall: 0.7107014848950333
f1: 0.7570604649807056

やはりbag-of-wordsは、USEよりやや下回る精度でした。

関連記事:自然言語処理の分散表現(Word2Vec,fastText)の課題