ジオマーリン

geomerlin.com に関することを中心に。難しいことをもっと難しく書くブログ。

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