ジオマーリン

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

 

Stanford Corenlp をPythonで使う1(インストール〜ダンプ)

Stanford Corenlp (http://stanfordnlp.github.io/CoreNLP/)は自然言語処理の汎用ツールで、品詞の特定、構文木・依存関係の決定、固有名詞解析、共参照の特定(例えば”This is my friend ,Asami.She is from U.S.”ならthis=my friend=she=Asamiの関係を特定)などをやってくれる。素人が使うツールとしては最強!であるがデフォルトで3GBのメモリをアロケートするので注意。

 

まず、pythonでラップする対象である元のjavaプログラムをダウンロード

curl -L -O http://nlp.stanford.edu/software/stanford-corenlp-full-2014-08-27.zip

アクセス制限のエラー出たら、sudoってください。

 

解凍(僕の場合は/usr/local/libが空いてるのでここにしておく)

unzip ./stanford-corenlp-full-2014-08-27.zip -d /usr/local/lib/

 

次にpipでラッパーをインストール

sudo pip3 install corenlp-python

 

ではもう使えます

import corenlp
parser = corenlp.StanfordCoreNLP()
json_data = parser.parse(解析する英文)

エラーが出る場合は一番下へ

 

結果について説明しておきます。出力はjsonなのでjson.loadsかevalして下さい。

かなりひどいネスト構造になっています。
大まかには全体データ > 文章データ(sentences) > 単語データ(words)のネストです。

最初は大きいディクショナリは全部の文章の解析結果でキーはcoref とsentencesです。
corefの値のリストには共参照がセットになっています。
(それぞれの共参照はだいたい最後の要素が最初にでてきた表現です)

sentencesの値はそれぞれの文ごとの解析結果です。
このリストの中はディクショナリが格納されていて、キーはtext,dependencies,wordsです。
さらにwordsの値にはそれぞれの単語のデータが入っています。

wordsのkeyは固有名詞解析のNamedEntityTag,原型のLemma,品詞のPartOfSpeechからなっています。

 

文章データにあるdependenciesは依存関係を表していますが、これは依存関係の表現を知らないと使いようがありません。このドキュメントに使い方が書いてあります。

http://nlp.stanford.edu/software/dependencies_manual.pdf

 

・エラーが出る場合

次のようなエラーが出ましたか?

corenlp.corenlp.ProcessError: 'CoreNLP process terminates abnormally while parsing'

だいたいメモリ不足か自然言語とは思えない文字が混入してるかどちらかですね。

後者の場合は除去して下さい。前者の場合は、Java仮想マシンに3GBもアロケートさせるのをやめましょう。つぎのファイルにアクセスして下さい

pythonなんとか/site-packages/corenlp/corenlp.py

ここで

class StanfordCoreNLP:

    (中略)

    def __init__(self, corenlp_path=DIRECTORY, memory="3g"

のところの3gを1gか2gにしましょう。これでパーサーがメモリ飲み放題のパーティーを開いて怒られるのを防ぎます。ただし、精度は下がるでしょうから、長い文章の共参照はあきらめましょう。

他にも/site-packages/corenlp/default.propertiesの

annotators = tokenize, ssplit, pos, lemma, ner, parse, dcorefを減らすと機能が減る代わりに負担も減ります。