28. シンプルなファインチューニング例#

  • やりたいこと

  • 方針

    • fastTextにより「Wikipedia(en)コーパスの一部」を用いて事前学習する。(異なるソースを元に言語モデルを構築する)

      • なお、ここではfastTextで学習するためにどのようにデータを要ししたら良いのかを確認しやすくするためにWikipediaコーパスから事前学習を行っている。しかし自前でWikipedia事前学習するぐらいなら、最初からFastTextで公開されている事前学習済みモデルをダウンロードして用いるほうが良い。

    • fastText学習済みモデルを用いて、20 newsgroups textの記事をベクトル化する。

    • 比較対象としてTF-IDFによるベクトル化も用意する。

    • 分類学習にはナイーブベイズ、SVM、NNを用いる。なお、ナイーブベイズは基本的にはカウント情報を想定しているため、fastTextベクトルには適用できないことからTF-IDFのみに適用する。

!date
2022年 3月 9日 水曜日 15時52分25秒 JST

28.1. 事前学習#

28.1.1. 環境構築#

  • fastTextモデルのためにgensimを利用。

#!pip install --upgrade gensim
!conda install -c conda-forge gensim
Collecting package metadata (current_repodata.json): done
Solving environment: failed with initial frozen solve. Retrying with flexible solve.
Collecting package metadata (repodata.json): done
Solving environment: failed with initial frozen solve. Retrying with flexible solve.

PackagesNotFoundError: The following packages are not available from current channels:

  - gensim

Current channels:

  - https://conda.anaconda.org/conda-forge/osx-arm64
  - https://conda.anaconda.org/conda-forge/noarch
  - https://repo.anaconda.com/pkgs/main/osx-arm64
  - https://repo.anaconda.com/pkgs/main/noarch
  - https://repo.anaconda.com/pkgs/r/osx-arm64
  - https://repo.anaconda.com/pkgs/r/noarch

To search for alternate channels that may provide the conda package you're
looking for, navigate to

    https://anaconda.org

and use the search bar at the top of the page.

28.1.2. データセットの準備#

  • 英語版Wikipediaのダンプデータをダウンロードし、これを事前学習用コーパスとして利用する。なお、全データを用いると圧縮状態で15GBを超えて待ち時間が長いため、ここでは小規模で提供されているものを指定している。

  • ダンプデータはbzcatで確認しているように、XML形式で書かれている。

file_url="https://dumps.wikimedia.org/enwiki/latest/enwiki-latest-pages-articles1.xml-p1p41242.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  245M  100  245M    0     0   856k      0  0:04:53  0:04:53 --:--:--  860kk    0     0   755k      0  0:05:32  0:00:10  0:05:22  871k0     0   798k      0  0:05:14  0:00:18  0:04:56  870k 0     0   821k      0  0:05:05  0:00:31  0:04:34  848k:01:22  0:03:34  867k55  0:02:07  0:02:48  862k4:55  0:02:11  0:02:44  859k0   852k      0  0:04:54  0:02:22  0:02:32  860k52k      0  0:04:54  0:02:31  0:02:23  864k    0  0:04:54  0:02:49  0:02:05  859k  853k0:01:26  863k2  178M    0     0   854k      0  0:04:53  0:03:33  0:01:20  864k     0  0:04:53  0:04:21  0:00:32  864k 855k      0  0:04:53  0:04:23  0:00:30  860k   0  0:04:53  0:04:42  0:00:11  857k
!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.38.0-wmf.23</generator>
    <case>first-letter</case>
    <namespaces>
      <namespace key="-2" case="first-letter">Media</namespace>
      <namespace key="-1" case="first-letter">Special</namespace>

bzcat: I/O or other error, bailing out.  Possible reason follows.
bzcat: Broken pipe
	Input file = enwiki-latest-pages-articles1.xml-p1p41242.bz2, output file = (stdout)

28.1.3. 環境構築2#

import multiprocessing
from gensim.corpora.wikicorpus import WikiCorpus
from gensim.models.fasttext import FastText as FT_gensim
---------------------------------------------------------------------------
ModuleNotFoundError                       Traceback (most recent call last)
/var/folders/nc/_3k6g05j2499x9n2cjtmhxl80000gn/T/ipykernel_33837/2467614308.py in <module>
      1 import multiprocessing
----> 2 from gensim.corpora.wikicorpus import WikiCorpus
      3 from gensim.models.fasttext import FastText as FT_gensim

ModuleNotFoundError: No module named 'gensim'

ダンプデータから本文抽出する様子。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

28.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

28.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"))

28.2. ファインチューニング#

fastTextによる事前学習を終えた。これを用いて本当にやりたい 20 news 分類学習に移る。

28.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

28.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

28.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

28.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))
!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
!date