少量のテキストデータとSVMを使って、自然文章の分類器を作ったゾ!


はじめに

勝手なイメージですが、自然言語処理の機械学習と言えば、世間的にはLSTMやトピックモデルなどが挙げられると思います。LSTMやトピックモデルは自然文章のデータが大量にあることで、効果を得られるものです。しかし、ビジネス領域において自然文章のデータを大量に集めることは大変難しいです。
それならば、少量のデータで機械学習できれば、幸せになれる人がいるんじゃないかと考えました。
なので、今回は、少量データで機械学習をして、学習できるのか見たいと思います。

今回のやること

以下の技術を用いて、分類器を作ります。

今回の流れ

① twitterからテキストデータの取得

「機械学習」、「自然言語処理」、「データサイエンティスト」の文字列を含むツイートを100件ずつ取得する。

② テキストデータへのラベル付け

以下のようなルールでラベル付け。(テキストデータ => ラベル)

  • 「機械学習」で検索したテキストデータ => 機械学習
  • 「自然言語処理」で検索したテキストデータ => 自然言語処理
  • 「データサイエンティスト」で検索したテキストデータ => データサイエンティスト
③ tfidfモデル、tfidfベクトルを作成

取得したデータから名詞、形容詞の単語を抽出し、単語リストを生成。
単語リストからtfidfモデル、tfidfベクトルを作成。

④ tfidfベクトルとsvmを用いて、分類器を作成

tfidfベクトルとラベルを基にsvmで学習。

ソースコード

① twitterからテキストデータの取得

データ取得は、以下の記事を参考にしてみてください。
https://qiita.com/teri/items/e10a3be38113b31c0c9a

② テキストデータへのラベル付け

ラベル付けは、以下の記事を参考にしてみてください。
https://qiita.com/kazuki_hayakawa/items/18b7017da9a6f73eba77

③ tfidfモデルを作成

名詞、形容詞の単語のみを取得


def wakati_word_list_create(sentences:list, get_word_class:list) -> list:
    #複数文から、指定の品詞(GET_WORD_CLASS)を抽出した単語リスト
    sentences_word_list = []

    for sentence in sentences:
        #一文から、指定の品詞(GET_WORD_CLASS)を抽出した単語リスト
        one_sentence_word_str = ''
        #形態素解析
        node = mecab.parseToNode(sentence)

        while node:

            #語幹
            word = node.feature.split(",")[-3]
            #品詞
            word_class = node.feature.split(",")[0]  
            #(指定の品詞(GET_WORD_CLASS)である) and (語幹が*のもの(つまり未知語))場合は、単語リストに追加
            if word_class in get_word_class and word != '*': 
                one_sentence_word_str = one_sentence_word_str + " " + word

            node = node.next
        sentences_word_list.append(one_sentence_word_str)
    return sentences_word_list

#twitterデータから、指定品詞の分かち分割形式の単語リストを生成
wakati_word_list = nlp_util.wakati_word_list_create(search_tweet_list, GET_WORD_CLASS)

tfidfモデル、tfidfベクトルを作成

#tfidfベクトル化
from sklearn.feature_extraction.text import TfidfVectorizer

tfidf = TfidfVectorizer(use_idf=True, token_pattern=u'(?u)\\b\\w+\\b')
#tfidfモデルの作成
tfidf_model = tfidf.fit(wakati_word_list)
#tfidfベクトルに変換
vectors = tfidf_model.transform(wakati_word_list).toarray()
④ tfidfベクトルとsvmを用いて、分類器を作成

準備したデータを学習データ、テストデータに振り分ける。
今回は学習データを7割、テストデータを3割に振り分ける。

from sklearn.model_selection import train_test_split
from sklearn.preprocessing import StandardScaler 

input_train, input_test, output_train, output_test = train_test_split(vectors, targets, test_size=0.3, random_state=None )

sc = StandardScaler()
sc.fit(input_train)
input_train_std = sc.transform(input_train)
input_test_std = sc.transform(input_test)

tfidfベクトルとsvmで学習。

from sklearn.svm import SVC
# 学習インスタンス生成
svc_model = SVC(kernel='linear', random_state=None)

# 学習
svc_model.fit(input_train_std, output_train)

学習データで精度確認

from sklearn.metrics import accuracy_score

#traning dataのaccuracy
pred_train = svc_model.predict(input_train_std)
accuracy_train = accuracy_score(output_train, pred_train)
print('traning data accuracy: %.2f' % accuracy_train)

実行結果は以下。
traning data accuracy: 1.00

テストデータで精度確認

#test dataのaccuracy
pred_test = svc_model.predict(input_test_std)
accuracy_test = accuracy_score(output_test, pred_test)
print('test data accuracy: %.2f' % accuracy_test)

実行結果は以下。
test data accuracy: 0.93

少し遊んでみました。

適当な文章を入力して、ちゃんと分類されるか試してみました。
input_textsに格納されている文章が、「自然言語処理」、「機械学習」、「データサイエンティスト」のいずれかに分類されます。

#正解ラベル{1:機械学習、2:データサイエンティスト, 3:自然言語処理}
input_texts = [
                "自然言語処理の技術発展スピードはすごいな~",
                "データサイエンティストってどんな職種なんやろかー",
                "機械学習用のデータを集めるのって大変やなー",
                "自然言語処理系のデータサイエンティストって楽しそう!!",
                "ネガポジ分析について",
                "言葉ネットワークとは?",
              ]
wakati_input_texts = nlp_util.wakati_word_list_create(input_texts, GET_WORD_CLASS)
tfidf_vectors = tfidf_model.transform(wakati_input_texts).toarray()
predict = svc_model.predict(tfidf_vectors)

print("{: <30} : {: <20}".format("予測する文章", "予測結果"))
print("====================================================================================")
for pre, text in zip(predict, input_texts):
    print("{: <30} : {: <20}".format(text, LABEL_MAP[pre]))

実行結果は以下。

ネガポジ分析、言葉ネットワークが機械学習に分類されるのは、よくないっすね・・・。

考察

ネガポジ分析、言葉ネットワークは、個人的には、自然言語処理に分類してほしかったんですが、機械学習に分類されちゃいました。。。
今回は、tfidfベクトルを使ったため、学習に用いた単語以外の単語を入力されると、分類が難しいんだと思います。なので、これを解消するためには、word2vecなどの分散表現を使うことで、より色んな単語に対応させ精度が高まるかと思います。
次回は、word2vecを使った分類器を作って、精度比較をしてみますね!!

参考にさせて頂いた記事です。