9. シンプルなファインチューニング例¶
やりたいこと
20 newsgroups text datasetを分類タスクとして学習したい。
方針
fastTextにより「Wikipedia(en)コーパスの一部」を用いて事前学習する。(異なるソースを元に言語モデルを構築する)
なお、ここではfastTextで学習するためにどのようにデータを要ししたら良いのかを確認しやすくするためにWikipediaコーパスから事前学習を行っている。しかし自前でWikipedia事前学習するぐらいなら、最初からFastTextで公開されている事前学習済みモデルをダウンロードして用いるほうが良い。
fastText学習済みモデルを用いて、20 newsgroups textの記事をベクトル化する。
比較対象としてTF-IDFによるベクトル化も用意する。
分類学習にはナイーブベイズ、SVM、NNを用いる。なお、ナイーブベイズは基本的にはカウント情報を想定しているため、fastTextベクトルには適用できないことからTF-IDFのみに適用する。
!date
Wed Jun 2 10:09:51 UTC 2021
9.1. 事前学習¶
9.1.1. 環境構築¶
fastTextモデルのためにgensimを利用。
!pip install --upgrade gensim
Collecting gensim
?25l Downloading https://files.pythonhosted.org/packages/44/52/f1417772965652d4ca6f901515debcd9d6c5430969e8c02ee7737e6de61c/gensim-4.0.1-cp37-cp37m-manylinux1_x86_64.whl (23.9MB)
|████████████████████████████████| 23.9MB 159kB/s
?25hRequirement already satisfied, skipping upgrade: scipy>=0.18.1 in /usr/local/lib/python3.7/dist-packages (from gensim) (1.4.1)
Requirement already satisfied, skipping upgrade: numpy>=1.11.3 in /usr/local/lib/python3.7/dist-packages (from gensim) (1.19.5)
Requirement already satisfied, skipping upgrade: smart-open>=1.8.1 in /usr/local/lib/python3.7/dist-packages (from gensim) (5.0.0)
Installing collected packages: gensim
Found existing installation: gensim 3.6.0
Uninstalling gensim-3.6.0:
Successfully uninstalled gensim-3.6.0
Successfully installed gensim-4.0.1
9.1.2. データセットの準備¶
英語版Wikipediaのダンプデータをダウンロードし、これを事前学習用コーパスとして利用する。なお、全データを用いると圧縮状態で15GBを超えて待ち時間が長いため、ここでは小規模で提供されているものを指定している。
ダンプデータはbzcatで確認しているように、XML形式で書かれている。
#!curl -O https://dumps.wikimedia.org/enwiki/latest/enwiki-latest-pages-articles.xml.bz2
!curl -O https://dumps.wikimedia.org/enwiki/latest/enwiki-latest-pages-articles1.xml-p1p41242.bz2
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
100 237M 100 237M 0 0 4668k 0 0:00:52 0:00:52 --:--:-- 4760k
!bzcat enwiki-latest-pages-articles1.xml-p1p41242.bz2 | head
<mediawiki xmlns="http://www.mediawiki.org/xml/export-0.10/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.mediawiki.org/xml/export-0.10/ http://www.mediawiki.org/xml/export-0.10.xsd" version="0.10" xml:lang="en">
<siteinfo>
<sitename>Wikipedia</sitename>
<dbname>enwiki</dbname>
<base>https://en.wikipedia.org/wiki/Main_Page</base>
<generator>MediaWiki 1.37.0-wmf.5</generator>
<case>first-letter</case>
<namespaces>
<namespace key="-2" case="first-letter">Media</namespace>
<namespace key="-1" case="first-letter">Special</namespace>
9.1.3. 環境構築2¶
multiprocessingは、実行環境におけるCPU数(コア数)を確認するために利用。
gensim.corpora.wikicorpusは、Wikipediaのダンプデータから本文データのみを抽出するために利用。
gensim.models.fasttextは、FastTextモデル。
import multiprocessing
from gensim.corpora.wikicorpus import WikiCorpus
from gensim.models.fasttext import FastText as FT_gensim
/usr/local/lib/python3.7/dist-packages/gensim/similarities/__init__.py:15: UserWarning: The gensim.similarities.levenshtein submodule is disabled, because the optional Levenshtein package <https://pypi.org/project/python-Levenshtein/> is unavailable. Install Levenhstein (e.g. `pip install python-Levenshtein`) to suppress this warning.
warnings.warn(msg)
ダンプデータから本文抽出する様子。sentencesにlatestの全本文があり、文書数は15025件。1件目の文書に含まれる単語数は50単語。
!date
!curl -O https://dumps.wikimedia.org/enwiki/latest/enwiki-latest-pages-articles11.xml-p6899367p7054859.bz2
wikipedia_data = "./enwiki-latest-pages-articles11.xml-p6899367p7054859.bz2"
# expand and extarct
print("get texts from {}".format(wikipedia_data))
wiki = WikiCorpus(wikipedia_data, dictionary={})
sentences = list(wiki.get_texts())
# 出力確認
print(len(sentences))
print(len(sentences[0]))
print(sentences[0][0:5])
!date
Wed Jun 2 10:10:53 UTC 2021
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
100 45.4M 100 45.4M 0 0 4338k 0 0:00:10 0:00:10 --:--:-- 4689k
get texts from ./enwiki-latest-pages-articles11.xml-p6899367p7054859.bz2
15025
50
['waterhouse', 'swamp', 'rat', 'scapteromys', 'tumidus']
Wed Jun 2 10:12:36 UTC 2021
9.1.4. fastTextによる事前学習¶
build_vocab() により、まずボキャブラリ(単語一覧)を作成する。
その後、コーパスとそれに対する基本情報、エポック数を指定して学習する。
!date
# faxtText
ft_model = FT_gensim(vector_size=200, window=10, min_count=10, workers=max(1, multiprocessing.cpu_count() - 1))
# build the vocabulary
print("building vocab...")
ft_model.build_vocab(sentences)
# train the model
print("training model...")
ft_model.train(
sentences,
epochs = ft_model.epochs,
total_examples = ft_model.corpus_count,
total_words = ft_model.corpus_total_words
)
print("training done.")
!date
Wed Jun 2 10:12:36 UTC 2021
building vocab...
training model...
training done.
Wed Jun 2 10:24:45 UTC 2021
9.1.5. 事前学習により得られたモデルの確認¶
単語でも文章でもベクトル化できる。
“hoge” は元々の文書には存在しない(False)が、ベクトル化できている。(サブワードによる未知語対応)
ベクトル化できたため、類似単語も確認可能。
# 動作確認
print(ft_model.wv['artificial'].shape)
print(ft_model.wv['artificial'][:5])
print(ft_model.wv["more like funchuck,Gave this"][:5])
print("===========")
print("hoge" in ft_model.wv.key_to_index)
print(ft_model.wv["hoge"][:5])
print("===========")
print(ft_model.wv.most_similar("computer"))
print(ft_model.wv.most_similar("programming"))
print(ft_model.wv.most_similar("apple"))
(200,)
[-0.6973012 -0.26773152 0.46666357 -0.25891605 1.3251537 ]
[-0.17531493 -0.01367316 -0.04481015 -0.02450226 0.121394 ]
===========
False
[ 0.26534227 1.1729028 0.04152503 -2.065968 0.08280751]
===========
[('compute', 0.9225346446037292), ('computers', 0.874762773513794), ('computing', 0.8344529867172241), ('compulsory', 0.8319492936134338), ('computable', 0.7980512380599976), ('computerized', 0.794980525970459), ('compulsive', 0.7739043235778809), ('computed', 0.7671758532524109), ('computational', 0.762458324432373), ('computability', 0.7551568746566772)]
[('programme', 0.9124019145965576), ('programmes', 0.9014513492584229), ('programmer', 0.8962664604187012), ('programmers', 0.8907719254493713), ('programmed', 0.8655120134353638), ('programmable', 0.8621218204498291), ('program', 0.8598142266273499), ('programs', 0.8576140999794006), ('programmatic', 0.8526832461357117), ('prog', 0.8027951121330261)]
[('appleby', 0.9099407196044922), ('apples', 0.7911590933799744), ('applause', 0.7831224799156189), ('downloadable', 0.78069007396698), ('applicable', 0.7683167457580566), ('appleseed', 0.7469122409820557), ('app', 0.7379465699195862), ('appleyard', 0.7336268424987793), ('apply', 0.7324667572975159), ('pineapple', 0.7182422876358032)]
9.2. ファインチューニング¶
fastTextによる事前学習を終えた。これを用いて本当にやりたい 20 news 分類学習に移る。
9.2.1. データセットを用意¶
20 newsのデータセットを用意。
# fine-tuneing stage.
# デーセットの用意
# こちらも時間かかるので、変換したデータセットを指定した場所に保存。
# 既に保存済みデータセットの利用にも対応。
from sklearn.datasets import fetch_20newsgroups
#categories = ['alt.atheism', 'sci.space']
categories = ['comp.os.ms-windows.misc', 'comp.sys.mac.hardware', 'misc.forsale']
newsgroups_train = fetch_20newsgroups(subset='train', categories=categories)
train_text = newsgroups_train.data
train_label = newsgroups_train.target
newsgroups_test = fetch_20newsgroups(subset='test', categories=categories)
test_text = newsgroups_test.data
test_label = newsgroups_test.target
Downloading 20news dataset. This may take a few minutes.
Downloading dataset from https://ndownloader.figshare.com/files/5975967 (14 MB)
9.2.2. 事前学習モデルによるベクトル化¶
!date
# 事前学習したfastTextにより、文章をベクトルに変換
def sentence2vector(sentences, model):
vectors = []
for sent in sentences:
vectors.append(model.wv[sent])
return vectors
ft_train_vectors = sentence2vector(train_text, ft_model)
ft_test_vectors = sentence2vector(test_text, ft_model)
!date
Wed Jun 2 10:24:56 UTC 2021
Wed Jun 2 10:25:28 UTC 2021
9.2.3. 分類学習モデルによる学習(fastText版)¶
!date
#from sklearn.naive_bayes import MultinomialNB
from sklearn import svm
from sklearn.neural_network import MLPClassifier
#clf1 = MultinomialNB()
clf2 = svm.SVC(gamma='scale')
clf3 = MLPClassifier(max_iter=500)
clfs = {"SVM":clf2, "NN":clf3}
ft_scores = []
for name, clf in clfs.items():
clf.fit(ft_train_vectors, train_label)
score = clf.score(ft_test_vectors, test_label)
ft_scores.append(score)
print("ft_score = {} by {}".format(score,name))
!date
Wed Jun 2 10:25:28 UTC 2021
ft_score = 0.7681779298545766 by SVM
ft_score = 0.7895637296834902 by NN
Wed Jun 2 10:25:44 UTC 2021
/usr/local/lib/python3.7/dist-packages/sklearn/neural_network/_multilayer_perceptron.py:571: ConvergenceWarning: Stochastic Optimizer: Maximum iterations (500) reached and the optimization hasn't converged yet.
% self.max_iter, ConvergenceWarning)
9.2.4. 分類学習(TF-IDF版)¶
# 比較対象の、事前学習なし実験。
# BoW + TFIDFによるベクトル生成
from sklearn.feature_extraction.text import TfidfVectorizer
vectorizer = TfidfVectorizer()
tfidf_train_vectors = vectorizer.fit_transform(newsgroups_train.data)
print("train_vectors.shape=", tfidf_train_vectors.shape)
print("len(train_label)=",len(train_label))
tfidf_test_vectors = vectorizer.transform(newsgroups_test.data)
print("test_vectors.shape=", tfidf_test_vectors.shape)
print("len(test_label)=",len(test_label))
train_vectors.shape= (1754, 54317)
len(train_label)= 1754
test_vectors.shape= (1169, 54317)
len(test_label)= 1169
!date
from sklearn.naive_bayes import MultinomialNB
from sklearn import svm
from sklearn.neural_network import MLPClassifier
clf1 = MultinomialNB()
clf2 = svm.SVC(gamma='scale')
clf3 = MLPClassifier(max_iter=500)
clfs = {"NB":clf1, "SVM":clf2, "NN":clf3}
tfidf_scores = []
for name, clf in clfs.items():
clf.fit(tfidf_train_vectors, train_label)
score = clf.score(tfidf_test_vectors, test_label)
tfidf_scores.append(score)
print("tfidf_scores = {} by {}".format(score,name))
!date
Wed Jun 2 10:25:45 UTC 2021
tfidf_scores = 0.9024807527801539 by NB
tfidf_scores = 0.9230111206159111 by SVM
tfidf_scores = 0.9084687767322498 by NN
Wed Jun 2 10:27:23 UTC 2021
!date
Wed Jun 2 10:27:23 UTC 2021