読者です 読者をやめる 読者になる 読者になる

ジオマーリン

仕様を公開してアドバイスを受けながら 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