chainerで文書分類(ニュースをカテゴリ分け) 訓練後を中心
chainerはディープラーニングもできるNNライブラリ
ディープラーニングということは入力と出力のサンプル(教師データ)を用意すれば分類問題は絶対自動化できる。
ただ、入力と出力の型が違うとコードも違ってくるから、「でぃーぷらーにんぐをやれ!」と一言では済ませられない。
chainerはトレーニング→実用なのだが、実用の部分がドキュメント・ブログともに少ないので、訓練後を丁寧にやっていきたい。トレーニングまでは
Chainerでフィードフォワードニューラルネットワークを実装して文書分類する - Qiita
を参照してほしい。
まず最低限のchainer用語を紹介
・l1,l2,l3 →入力から第一層目に変換、一層目から二層目、二層目から三層目へ変換する関数
・optimizer →最適化のためのクラス
・model→学習済みニューラルネットワーク
ここでこの記事の結論を先に言うことにする
l3の出力をnp.argmaxすれば分類を出力できます。
では前述のありがたい記事にしたがって書いていく。
想定する教師データ: [[文字リスト],番号] ×n行 例:[[[この、学校、バカ、ばっかり],0],[[けど、俺、は、すごい],1]]
【Chainer】畳み込みニューラルネットワークによる文書分類 - Qiita をコピペして動かしますが、2点を変更
①インポートライブラリとload_data
# coding: utf-8
import numpy as np
from sklearn.cross_validation import train_test_split
from collections import defaultdict
import six
import sys
import json
import chainer
import chainer.links as L
from chainer import optimizers, cuda, serializers
import chainer.functions as F
import argparse
from gensim import corpora, matutils
import pickle
def load_data():
source = []
target = []
document_list = [] #各行に一文書. 文書内の要素は単語
with open("teacher.json", "r") as fi:
dataset = json.load(fi)
for l in dataset:
#ラベルと単語列を分ける
label = int(l[1]) #ラベル
target.append(label)
document_list.append(l[0]) #単語分割して文書リストに追加
#単語辞書を作成
for document in document_list:
tmp = dictionary.doc2bow(document) #文書をBoW表現
vec = list(matutils.corpus2dense([tmp], num_terms=len(dictionary)).T[0])
source.append(vec)
dataset = {}
dataset['target'] = np.array(target)
dataset['source'] = np.array(source)
print("vocab size:", len(dictionary.items()))
return dataset, dictionary
load_dataを少々変更。
この関数は教師データをchainerにぶち込める形に変換する。
②データ保存コードを変更。pickleのほうが何かと便利で使いやすい
#modelとoptimizerを保存
print('save the model')
#serializers.save_npz('pn_classifier_ffnn.model', model)
#print('save the optimizer')
#serializers.save_npz('pn_classifier_ffnn.state', optimizer)
with open('classifier_ffnn3.pickle',"wb") as fi:
pickle.dump(model,fi)
with open('dictionary3.pickle',"wb") as fi:
pickle.dump(dictionary,fi)
ではトレーニングを終えた後のことについて
別ファイルで次のコードを書きます。
import chainer
import chainer.functions as F
from gensim import corpora, matutils
import pickle
####################
#ファイル読み込み
####################
with open("classifier_ffnn3.pickle", "rb") as fi:
Model = pickle.load(fi)
with open("dictionary3.pickle","rb") as fi:
Dictionary = pickle.load(fi)
#NNによる分類関数
def fwd(model,dictionary,x):
x = dictionary.doc2bow(x) #文書をBoW表現
vec = list(matutils.corpus2dense([x], num_terms=len(dictionary)).T[0])
x = np.array([vec]).astype(np.float32)
x =chainer.Variable(x)
h1 = F.sigmoid(model.l1(x))
h2 = model.l2(h1)
h3 = model.l3(h2)
return np.argmax(h3.data)#F.softmax(h2)
####################
#関数用意
####################
def label_news(word_list):
return fwd(Model,Dictionary,word_list)
pickleで保存したNNとディクショナリを復活。
fwd関数で文字リストx(["ムズい","試験","タルい"]など)を最後の引数で受け取り、教師と同じように分類して返す。ラッパーがlabel_news関数になっている。
結論はl3の出力をnp.argmaxすれば分類できます。
このコードは http://geomerlin.com のニュース分類に使っています。
//訓練データ作るのにネカフェで修行僧になってました:D