tensorflowとKerasを使ってTextClassificationをしたい

– Neural network model

> We have to multiply each input node by a weight w and add a bias b.
> It is generally common to use a rectified linear unit (ReLU) for hidden layers, a sigmoid function for the output layer in a binary classification problem, or a softmax function for the output layer of multi-class classification problems.

### Keras
– Keras is a deep learning and neural networks API by Francois Chollet
$ pip3 install keras

kerasを使うにはbackgroundにtensorflowが動いていないといけないので、amazon linux2にtensorflowをインストールします。
$ pip3 install tensorflow
$ python3 -c “import tensorflow as tf; print(tf.reduce_sum(tf.random.normal([1000, 1000])))”
tf.Tensor(-784.01, shape=(), dtype=float32)
上手くインストールできたようです。

from keras.models import Sequential
from keras import layers

// 省略
input_dim = X_train.shape[1]

model = Sequential()
model.add(layers.Dense(10, input_dim=input_dim, activation='relu'))
model.add(layers.Dense(1, activation='sigmoid'))
model.add(layers.Dense(10, input_dim=input_dim, activation='relu'))
model.add(layers.Dense(1, activation='sigmoid'))

model.compile(loss='binary_crossentropy',
				optimizer='adam',
				metrics=['accuracy'])
print(model.summary())

$ python3 split.py
_________________________________________________________________
Layer (type) Output Shape Param #
=================================================================
dense (Dense) (None, 10) 17150
_________________________________________________________________
dense_1 (Dense) (None, 1) 11
=================================================================
Total params: 17,161
Trainable params: 17,161
Non-trainable params: 0
_________________________________________________________________
None

### batch size

history = model.fit(X_train, y_train,
					epochs=100,
					verbose=False,
					validation_data=(X_test, y_test)
					batch_size=10)

### evaluate accuracy

loss, accuracy = model.evaluate(X_train, y_train, verbose=False)
print("Training Accuracy: {:.4f}".format(accuracy))
loss, accuracy = model.evaluate(X_test, y_test, verbose=False)
print("Training Accuracy: {:.4f}".format(accuracy))

$ python3 split.py
Training Accuracy: 1.0000
Training Accuracy: 0.8040

### matplotlib
$ pip3 install matplotlib

import matplotlib.pyplot as plt

// 省略
def plot_history(history):
    acc = history.history['accuracy']
    val_acc = history.history['val_accuracy']
    loss = history.history['loss']
    val_loss = history.history['val_loss']
    x = range(1, len(acc) + 1)

    plt.figure(figsize=(12, 5))
    plt.subplot(1, 2, 1)
    plt.plot(x, acc, 'b', label='Training acc')
    plt.plot(x, val_acc, 'r', label='Validation acc')
    plt.title('Training and validation accuracy')
    plt.legend()
    plt.subplot(1, 2, 2)
    plt.plot(x, loss, 'b', label='Training loss')
    plt.plot(x, val_loss, 'r', label='Validation loss')
    plt.title('Training and validation loss')
    plt.savefig("img.png")

plot_history(history)

おおお、なんか凄え

Python・Mecab・ディープラーニングを用いたテキストのジャンル分類

$ pip3 install numpy
$ pip3 install scikit-learn
$ pip3 install pandas

scikit-learnはpythonで使用できる機械学習ライブラリ
ニュースコーパスは livedoorニュースコーパス を使用します。
livedoor ニュースコーパス

$ ls
text
$ cd text
$ ls
CHANGES.txt dokujo-tsushin livedoor-homme smax
README.txt it-life-hack movie-enter sports-watch
app.py kaden-channel peachy topic-news

app.py

import os, os.path
import csv

f = open('corpus.csv', 'w')
csv_writer = csv.writer(f, quotechar="'")
files = os.listdir('./')

datas = []
for filename in files:
	if os.path.isfile(filename):
		continue

	category = filename
	for file in os.listdir('./'+filename):
		path = './'+filename+'/'+file
		r = open(path, 'r')
		line_a = r.readlines()

		text = ''
		for line in line_a[2:]:
			text += line.strip()
		r.close()

		datas.append()
		print(text)
csv_writer.writerows(datas)
f.close()

$python3 app.py

以下のようなCSVファイルが生成されます。

### NNの仕組み
– 全てのテキストを形態素に分解し、全ての単語を把握した後、全ての単語に対してテキストに出現するかと言うベクトルを作成する。
– その後、ニューラルネットワークに対し、このベクトルを入力とし、出力をカテゴリとして学習を行う

nlp_tasks.py

# -*- coding: utf-8 -*-
#! /usr/bin/python3
import MeCab
from sklearn.feature_extraction.text import CountVectorizer

def _split_to_words(text):
	tagger = MeCab.Tagger('-O wakati')
	try:
		res = tagger.parse(text.strip())
	except:
		return[]
	return res

def get_vector_by_text_list(_items):
	count_vect = CountVectorizer(analyzer=_split_to_words)
	box = count_vect.fit_transform(_items)
	X = box.todense()
	return [X,count_vect]

models というフォルダを作成する

main.py

# -*- coding: utf-8 -*-
#! /usr/bin/python3
import pandas as pd
import numpy as np
from sklearn.preprocessing import LabelEncoder
from sklearn.externals import joblib
import os.path
import nlp_tasks

from sklearn.neural_network import MLPClassifier # アルゴリズムのmlp

def train():
	classifier = MyMLPClassifier()
classifier.train('corpus.csv')

def predict():
	classifier = MyMLPClassifier()
	classifier.load_model()
	result = classifier.predict(u"競馬の3歳馬日本一を決めるG1レース、「第88回日本ダービー」が行われ、4番人気のシャフリヤールが優勝しました。")
	print(result)

class MyMLPClassifier():
	model = None
	model_name = "mlp"

	def load_model(self):
		if os.path.exists(self.get_model_path())==False:
			raise Exception('no model file found!')
		self.model = joblib.load(self.get_model_path())
		self.classes = joblib.load(self.get_model_path('class')).tolist()
		self.vectorizer = joblib.load(self.get_model_path('vect'))
		self.le = joblib.load(self.get_model_path('le'))

	def get_model_path(self, type='model'):
		return 'models/'+self.model_name+"_"+type+".pkl"

	def get_vector(self,text)
		return self.vectorizer.transform()


	def train(self, csvfile):
		df = pd.read_csv(cavfile,names=('text','category'))
		X, vectorizer = nlp_tasks.get_vector_by_text_list(df["text"])

		le = LabelEncoder()
		le.fit(df['category'])
		Y = le.transform(df['category'])

		model = MLPClassifier(max_iter=300, hidden_layer_sizes=(100,),verbose=10,)
		model.fit(X, Y)

		# save models
		joblib.dump(model, self.get_model_path())
		joblib.dump(le.classes_, self.get_model_path("class"))
		joblib.dump(vectorizer, self.get_model_path("vect"))
		joblib.dump(le, self.get_model_path("le"))

		self.model = model
		self.classes = le.classes_.tolist()
		self.vectorizer = vectorizer

	def predict(self, query):
		X = self.vectorizer.transform([query])
		key = self.model.predict(X)
		return self.classes[key[0]]

if __name__ == '__main__':
	train()
	#predict()

$ ls
corpus.csv main.py models nlp_tasks.py text

$ python3 main.py
Iteration 1, loss = 4.24610757
Iteration 2, loss = 2.08110509
Iteration 3, loss = 1.70032200
Iteration 4, loss = 1.49572560
// 省略
Iteration 136, loss = 0.00257828
Training loss did not improve more than tol=0.000100 for 10 consecutive epochs. Stopping.

if __name__ == '__main__':
    # train()
    predict()

$ python3 main.py
sports-watch

まじかーーーーーーーーーーーーーーーーーー
Sugeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee

誤差逆伝播法

順伝播では、入力値から重みを掛けて、中間層へと計算をして出力を導いた。
逆伝播では、出力値と正解の誤差を出し、最後の層、前の層と計算して重みを調整する

誤差逆伝播法
損失関数を2乗誤差の式として定義し、中間層の活性化関数を標準シグモイド関数として定義する

勾配降下法

二乗誤差を出力層、中間層、入力層に置き換えていくと、
E = 1/2 ||t – y||^2
E = 1/2 ||σ2(a1W2 + b2) – y||^2

E = 1/2 ||σ2(σ1(a0W1 + b1)*W2 + b2) – y||^2
E = 1/2 ||σ2(σ1(x0W1 + b1)*W2 + b2) – y||^2

勾配降下法は、関数のグラフを斜面に見立てて、関数の傾きを調べながら関数の値を小さくするような方向に少しずつ降りていくことで、関数の最小値を近位的に求める方法

df(x)/dx = limΔx→0 Δf(x)/Δx = {limh→0 f(x + Δx) – f(x)} / Δx
Δxが非常に小さい値であれば、
Δf(x) = f(x + Δx) – f(x)

Δx = -η df(x)/dx
xnew = xold -η df(x)/dx

DeepLearningでは、勾配降下法の確率的勾配降下法(SGD)などを利用する
訓練データからデータをN個抜き出し、N枚を学習させて計算された損失関数から、勾配降下法を用いてN枚ごとに重みを更新する。この枚数をバッチサイズという
訓練データを使いまわす回数をエポック数と呼ぶ

損失関数

重みやバイアスは損失関数が最小になるように調整される

損失関数の例: 二乗誤差
E = 1/2*|t – y|^2
※t=正解ラベル、y=NN出力
→ 二乗誤差を最小化するため、微分計算する

損失関数の例: クロスエントロピー
E = -Σt*log[e]y
※t=正解ラベル、y=NN出力
正解ラベルが0か1の値のみ持つ場合、正解ラベル1に対応する出力の自然対数を計算する
y=1, E=0

順伝播

入力値(0層目): x01 x02 x03 ※α:層の数、β:ノードの数
出力値(出力層):a21 a22 a23
wαβγ ※α:次の層の数、β:α層のノードの数(β番目)、γ:α-1層のノードの数(バイアスbの場合は記載なし)

中間層の計算
x11 = w111*a01 + w112a02 + w13a03 + b11
x12 = w121*a01 + w122a02 + w23a03 + b12
バイアスはパラメータとして入る
計算は行列を用いると
x1(中間層) = W1a0(入力層) + b1(バイアス) に置き換えられる

中間層の出力 a11, a12
a11 = σ1(x11)
a12 = σ1(x12)
σに活性化関数を選択し、x11 = 0 だった場合は、中間層の出力はa11 = σ1(0) = 0.5となる。シグモイド関数を挟むことで、出力は0 or 1 ではなくなる。

x2, w2, a1, b1を行列として出力層を表現すると
x2 = W2a1 + b2

最後の出力層では、softmax関数で非線形変換される。
softmax関数で確率に変換される。
yi = exp(xi)/ (exp(x1) + exp(x2) + … exp(xn)) (1≦i≦n)

DNN

入力値: x
重み: w
バイアス: b
活性化: σ

wx + b -> σ(wx + b) = 出力α

MNISTの場合、入力層は784ノード
つまり、画像認識の場合、入力層がピクセル数というイメージだ。
出力層では確率を表しており、確率の高い出力結果をoutputする
例えば、MNISTのように数字が0~9だと10通りだが、自動運転のように外景全てを認識させるとなると、無数に広がる
-> ということは1000万画素の画像認識の場合はノードは1000万?
-> 信号、人、建物、のように出力を絞れば可能か?
-> 人間の顔の場合は、目、鼻など特徴抽出ができるが、ルールがない場合は、頻度の問題が常に生じる??

活性化関数σにはシグモイド関数ReLu関数がよく用いられる
シグモイド関数: 1/(1+exp(-x))
ReLu関数: max(0,x)
-> 線形分離不可能なデータを分離するのに有効

tensorflowのインストール

TensorFlowをインストールします。
https://www.tensorflow.org/install?hl=ja

osは、ubuntu, macos, windows, raspbian とある。
何故か、python使ってる人は、centosではなく、ubuntuで構築してますね。何故だろう。centosはRedhat系ですが、ubuntuはDebianです。
centosだけでなく、ubuntuにも慣れておかないといけないですな。
あああああああああああああ、やることがガンガン増えますね。

さて、tensoflowですが、pipで入れられるようです。少し重いようで、時間がかかります。
[vagrant@localhost python]$ pip install tensorflow
Successfully installed absl-py-0.8.1 astor-0.8.0 gast-0.3.2 google-pasta-0.1.7 grpcio-1.24.1 h5py-2.10.0 keras-applications-1.0.8 keras-preprocessing-1.1.0 markdown-3.1.1 setuptools-41.4.0 tensorboard-1.14.0 tensorflow-1.14.0 tensorflow-estimator-1.14.0 termcolor-1.1.0 werkzeug-0.16.0 wheel-0.33.6 wrapt-1.11.2

kerasのモジュールも入っていますね。

公式を見ると、preview build for cpu, gpuと記載があります。
Or preview build for CPU/GPU (unstable)
pip install tf-nightly

Chainerを始めよう2

chainerでlinkはimportの際にLとする

import chainer.links as L

活性化関数はF

import chainer.functions as F

linkやfunctionをまとめて管理するのがchain

class MyNeuralNet(chainer.Chain):
from chainer import Chain, optimizers, Variable
import chainer.functions as F 
import chainer.links as L 

import numpy as np 

from sklearn import datasets, model_selection 

import seaborn as sns 
import matplotlib.pyplot as plt 

class MLP(Chain):
	def __init__(self, n_hid=100, n_out=10):
		super().__init__()
		<b>
		with self.init_scope():
			self.l1 = L.Liner(None, n_hid)
			self.l2 = L.Linear(n_hid, n_out)

	def __call__(self, x):
		hid = F.relu(self.l1(x))
		return self.l2(hid)</b>

iris = datasets.load_iris()

train_data, test_data, train_label, test_label = model_selection.train_test_split(iris.data.astype(np.float32),iris.target)

model = MLP(20, 3)

optimizer = optimizers.SGD()

optimizer.setup(model)

train_data_variable = Variable(train_data.astype(np.float32))
train_label_variable = Variable(train_label.astype(np.int32))

loss_log = []

for epoch in range(200):
	model.cleargrads()
	prod_label = model(train_data_variable)
	loss = F.softmax_cross_entropy(prod_label, train_label_variable)
	loss.backward()
	optimizer.update()

	loss_log.append(loss.data)

plt.plot(loss_log)

ん?
全結合層、ランプ関数の使い方、隠れ層の扱い、フォワードプロパゲーションなどがいまいち理解できん。。

損失関数を計算してから、パラメータを更新?

全結合層は、特徴量の取り出し(畳み込みフィルタ、ReLU、プーリング層)後に、特徴量に基づく分類を行う

ランプ関数は、f(x)= max(0,x)
一変数の実関数であり、独立変数とその絶対値の平均として容易に求められる?

隠れ層は、入力層、出力層の間

Softmax関数:多くの次元からなる入力のうち、自分の値が他の値たちに比べて一番目立っているならば、その値が11に近づく関数

フォワードプロパゲーション:レイヤーを進める方向に計算

あれ、NNでいう学習とは、パラメータの更新をforeachなどで繰り返し処理するってこと??

Chainerを始めよう1

Chainerとは
-> Deep learningのモデル、実行コードを直観的に記述できるpythonのフレームワーク
-> 使用することで、ニューラルネットワークやDeep learningの理解も深まる!?

ニューラルネットワークとは、脳のニューロンのネットワークをコンピュータ上で再現したモデル

ニューラルネットワークは、入力層(l-1層)で受け取り、隠れ層(l層)を通り、出力層(l+1層)から答えを返す

正しい答えと、NNの予想した答えとの誤差を損失関数と呼ぶ。損失関数の値が小さくなるよう値を変えていくことで、NNを学習させる


NNではWeight(結合重み)とBiasという二つのパラメータを使って、目的の値を出力する関数に近いものを作る
ニューロン同士のつながりの強さがweight
ニューロン・weightの出力に足すパラメーターbias
この関数をChainerではLinkと呼ぶ

ではchainerをインストールしましょう。

[vagrant@localhost python]$ pip install chainer
Collecting chainer
Downloading https://files.pythonhosted.org/packages/dc/bf/b561706d4e86f055841c7a5a414b5e5423bea230faa92abf5422c82cf995/chainer-6.4.0.tar.gz (876kB)
100% |████████████████████████████████| 880kB 641kB/s
Requirement already satisfied: setuptools in /home/vagrant/.pyenv/versions/3.5.2/lib/python3.5/site-packages (from chainer) (20.10.1)
Collecting typing<=3.6.6 (from chainer) Downloading https://files.pythonhosted.org/packages/4a/bd/eee1157fc2d8514970b345d69cb9975dcd1e42cd7e61146ed841f6e68309/typing-3.6.6-py3-none-any.whl Collecting typing_extensions<=3.6.6 (from chainer) Downloading https://files.pythonhosted.org/packages/62/4f/392a1fa2873e646f5990eb6f956e662d8a235ab474450c72487745f67276/typing_extensions-3.6.6-py3-none-any.whl Collecting filelock (from chainer) Downloading https://files.pythonhosted.org/packages/93/83/71a2ee6158bb9f39a90c0dea1637f81d5eef866e188e1971a1b1ab01a35a/filelock-3.0.12-py3-none-any.whl Requirement already satisfied: numpy>=1.9.0 in /home/vagrant/.pyenv/versions/3.5.2/lib/python3.5/site-packages (from chainer) (1.15.0)
Collecting protobuf<3.8.0rc1,>=3.0.0 (from chainer)
Downloading https://files.pythonhosted.org/packages/81/59/c7b0815a78fd641141f24a6ece878293eae6bf1fce40632a6ab9672346aa/protobuf-3.7.1-cp35-cp35m-manylinux1_x86_64.whl (1.2MB)
100% |████████████████████████████████| 1.2MB 1.2MB/s
Requirement already satisfied: six>=1.9.0 in /home/vagrant/.pyenv/versions/3.5.2/lib/python3.5/site-packages (from chainer) (1.11.0)
Installing collected packages: typing, typing-extensions, filelock, protobuf, chainer
Running setup.py install for chainer … done
Successfully installed chainer-6.4.0 filelock-3.0.12 protobuf-3.7.1 typing-3.6.6 typing-extensions-3.6.6

はや、数秒ですね。
[vagrant@localhost python]$ pip list
Package Version
—————– ———
awscli 1.16.198
beautifulsoup4 4.6.1
botocore 1.12.188
certifi 2018.4.16
chainer 6.4.0

import chainer

print(chainer.print_runtime_info())

Platform: Linux-2.6.32-754.14.2.el6.x86_64-x86_64-with-centos-6.10-Final
Chainer: 6.4.0
NumPy: 1.15.0
CuPy: Not Available
iDeep: Not Available
None