本稿ではfastTextを使ってサクッと単語の分散表現を獲得する方法について見ていきます。前日の記事と少しリンクするお話に出来れば良いなあと思って書きました。
fastTextとは
fastTextはFacebookが発表した単語の分散表現(単語を数値で表現したもの)を獲得する手法です。基となっているのはお馴染みWord2Vec(CBOW / skip-gram)です。Word2Vecについては今更も今更なので説明は不要でしょう。
論文:Enriching Word Vectors with Subword Information
Word2VecとfastTextの違いは、ベクトルの取り方です。サブワードという仕組みを入れることによって、活用形等の近い単語同士を汲み取っています。
Word2Vecではgoとgoesは全く別の単語でした。しかしfastTextではこれを考慮し、同じ構成要素としてgoとgoesに意味を持たせます。当然、未知語にも強くなりますね!
詳細は論文や次の資料をお勧めします。
FastTextで遊ぼう
今回の目的は「PythonとFastTextを使ってサクッと単語の分散表現で遊ぶ」ことです。
環境構築
え?Pythonの導入すらサクッと出来ない?ではGoogle Colaboratoryを使いましょう。(以下colab)
colabはGoogleアカウントさえあれば誰でも簡単にPythonやGPUが使える最強の無料環境です。
サクッとfastText
fastTextを使うには学習モデルが必要になります。これは自分でデータを収集して、学習させても良いのですが、ここでは学習済みモデルを使わせてもらいます。
学習済みモデル:fastTextの学習済みモデルを公開しました - Qiita
モデルはそのままGoogleドライブの中に放りこんでください。せっかくなのでcolabの使い方説明も含めて、ドライブから解凍してしまいましょう!
colabでドライブの中を触るにはマウントが必要です。これは以下のコードを実行すればよいのですが、「ドライブをマウント」ボタンを押せば同じコードが出てくるはずなのでそれを実行してください。
from google.colab import drive
drive.mount('/content/drive')
あとはURLにアクセスして、アカウントにログイン、するとauthorization codeが出てくると思うのでそれをcolabでコピペするだけです。簡単。
あとは先程のモデルを解凍するだけ!
%cd /content/drive/My Drive/data
!unzip vector_neologd.zip
pathはドライブに上げた学習済みモデルの場所を指定してくださいね。
それでは類似する言葉を探してみましょう。
import gensim
model = gensim.models.KeyedVectors.load_word2vec_format('model.vec', binary=False)
model.most_similar(positive=['自然言語処理'])
結果。
[('自然言語理解', 0.7600098848342896),
('自然言語', 0.7503659725189209),
('計算言語学', 0.7258570194244385),
('自動プログラミング', 0.6848069429397583),
('テキストマイニング', 0.6811494827270508),
('コンピュータ言語', 0.6618390083312988),
('メタプログラミング', 0.658093273639679),
('Webプログラミング', 0.6488876342773438),
('形態素解析', 0.6479052901268005),
('コーパス言語学', 0.6465639472007751)]
色々遊ぶと意外と楽しい。
model.most_similar(positive=['友人'], negative=['友情'])
[('知人', 0.4586910605430603),
('自宅', 0.35488438606262207),
('知り合い', 0.329221248626709),
('入り浸り', 0.3212822675704956),
('親戚', 0.31865695118904114),
('知り合っ', 0.3158203959465027),
('宅', 0.31503963470458984),
('誘わ', 0.302945077419281),
('繁く', 0.30250048637390137),
('同僚', 0.29792869091033936)]
サクッと?fastText
あまりにサクッとし過ぎたので、続いてボキャブラリーを制限して同じことをしてみます。こんなやり方もあるよというオプションです。
今回扱うのは日本語のテキストですが、日本語は英語と違い各単語がスペース等で分割されていないので、初めに前処理を行わなければなりません(分かち書き)。
尚、実装は書籍「つくりながら学ぶ! PyTorchによる発展ディープラーニングを参考にしています。
ここではjanomeを使います。
!pip install janome
では分かち書きをしましょう。
from janome.tokenizer import Tokenizer
j_t = Tokenizer()
def tokenizer_janome(text):
return [tok for tok in j_t.tokenize(text, wakati=True)]
janomeだけで分かち書きがサクッと出来ますが、加えて簡単な前処理をしましょう。
import re
import unicodedata
import string
def format_text(text):
text = unicodedata.normalize("NFKC", text)
table = str.maketrans("", "", string.punctuation + "「」、。・")
text = text.translate(table)
return text
def preprocessing(text):
"""
前処理をする関数
"""
text.translate(str.maketrans({chr(0xFF01 + i): chr(0x21 + i) for i in range(94)})) # 全角→半角
text = text.lower() # 大文字→小文字
text = re.sub('\r', '', text)
text = re.sub('\n', '', text)
text = re.sub(' ', '', text)
text = re.sub(' ', '', text)
text = re.sub(r'[0-9 0-9]', '', text) # 数字の除去
text = re.sub(r'[!-/:-@[-`{-~]', '', text) # 半角記号の除去
text = re.sub(r'/[!-/:-@[-`{-~、-〜”’・]', '', text) # 全角記号の除去
text = format_text(text) # 全角記号の除去
return text
def tokenizer_with_preprocessing(text):
text = preprocessing(text)
text = tokenizer_janome(text)
return text
コメントに書いた通りですが、いくつかの前処理を行っています。もちろんタスクや用途によって前処理で行うべきことは変わるはずです。
では試しに適当な文章を入れてみましょう。
text = '今回は fastTextを 使って 分散表現を獲得したい!!!?'
print(tokenizer_with_preprocessing(text))
結果。
['今回', 'は', 'fasttext', 'を', '使っ', 'て', '分散', '表現', 'を', '獲得', 'し', 'たい']
上手くいっているようですね。
続いてtorchtextを使って諸々を楽にします。torchtextについてはtorchtextで簡単にDeepな自然言語処理 - Qiitaが詳しいです。
import torchtext
max_length = 25
TEXT = torchtext.data.Field(sequential=True, tokenize=tokenizer_with_preprocessing,
use_vocab=True, lower=True, include_lengths=True,
batch_first=True, fix_length=max_length)
LABEL = torchtext.data.Field(sequential=False, use_vocab=False)
train_ds, val_ds, test_ds = torchtext.data.TabularDataset.splits(
path='./tsv/', train='train.tsv',
validation='val.tsv', test='test.tsv', format='tsv',
fields=[('Text', TEXT), ('Label', LABEL)])
train.tsv / test.tsv / val.tsvを用意しました。これは本稿の「fastTextとは」の文章を適当に3分割してtsvファイルとしました。これも上記のリンクが詳しいのでそちらをお勧めします。
from torchtext.vocab import Vectors
vectors = Vectors(name='model.vec')
# ベクトル化したバージョンのボキャブラリーを作成します
TEXT.build_vocab(train_ds, vectors=vectors, min_freq=1)
# ボキャブラリーのベクトルを確認します
print(TEXT.vocab.vectors.shape) # 52個の単語が300次元のベクトルで表現されている
TEXT.vocab.vectors
# ボキャブラリーの単語の順番を確認します
TEXT.vocab.stoi
これで準備が出来たのでボキャブラリーの各単語の類似度を計算してみます。「単語」に3つの単語が類似するか見てみましょう。
import torch.nn.functional as F
tensor_calc = TEXT.vocab.vectors[TEXT.vocab.stoi['単語']]
# コサイン類似度
print("論文", F.cosine_similarity(tensor_calc, TEXT.vocab.vectors[TEXT.vocab.stoi['論文']], dim=0))
print("ワード", F.cosine_similarity(tensor_calc, TEXT.vocab.vectors[TEXT.vocab.stoi['ワード']], dim=0))
print("ベクトル", F.cosine_similarity(tensor_calc, TEXT.vocab.vectors[TEXT.vocab.stoi['ベクトル']], dim=0))
結果。
論文 tensor(0.3089)
ワード tensor(0.3704)
ベクトル tensor(0.3265)
もっと詳しくやりたい人は
fastTextについてはWord2Vecほどではありませんが、色んな記事がありますので、特におすすめなもの(参考文献)を紹介して締めます。
fastTextで文書分類
追記箇所。以前は色んな記事を紹介しましたが、最近投稿された以下の記事が最もおすすめ。タイトルにWatsonとの比較とありますが、Google colabで動作するfastTextのコードがあるのでポチポチするだけ。学習データの形式についての説明も丁寧ですし、記事とコードをにらめっこすると良いと思います。
fastText以外の単語埋め込み
もっと最近の単語埋め込みを知りたい方はELMoとかBERTでググって貰えれば沢山出ると思います。当然ですが最近のモデルになるほど難しくなります。
次回は?
@takashi1029さんです!