Kuroyagi飼育日誌

学んだことの備忘録

深層学習の学習 【その6】

前回はようやくMNISTの判別と答え合わせまできました。




kuroyagi.hatenablog.com



今回は、隠れ層がどうなっているの可視化するのを目的とします。






ちなみにもともと参考にしているのは【記事1】です。



【記事1】
qiita.com
 
 
 
そして、これまでに作って動いている【ソースコード1】を示しておきます。中間層のユニット層が100と間違っていたので1000に訂正しました。


ソースコード1】

import matplotlib.pyplot as plt
import numpy as np
from sklearn.datasets import fetch_mldata
import chainer
from chainer import cuda, Variable, optimizers, Chain
import chainer.functions  as F
import chainer.links as L
import sys

mnist = fetch_mldata("MNIST original", data_home=".")

# 学習用データを N個、検証用データを残りの個数と設定
batchsize = 100
N = 60000

# 学習の繰り返し回数
n_epoch   = 1

x_all = mnist['data'].astype(np.float32) / 255
y_all = mnist['target'].astype(np.int32)
x_train, x_test = np.split(x_all, [N])
y_train, y_test = np.split(y_all, [N])
N_test = y_test.size

class MLP(Chain):
    def __init__(self):
        super(MLP, self).__init__(
        l1=L.Linear(784, 1000),
        l2=L.Linear(1000, 100),
        l3=L.Linear(100, 10),
        )

    def __call__(self, x):
        h1 = F.dropout(F.relu(self.l1(x)))
        h2 = F.dropout(F.relu(self.l2(h1)))
        y = self.l3(h2)
        return y

class Classifier(Chain):
    def __init__(self, predictor):
        super(Classifier, self).__init__()
        with self.init_scope():
            self.predictor = predictor

    def __call__(self, x, t):
        y = self.predictor(x)
        loss = F.softmax_cross_entropy(y, t)
        accuracy = F.accuracy(y, t)
        report({'loss': loss, 'accuracy': accuracy}, self)
        return loss

model = L.Classifier(MLP())
optimizer = optimizers.Adam()
optimizer.setup(model)



train_loss = np.array([])
train_acc = np.array([])
test_loss = np.array([])
test_acc  = np.array([])

l1_W = np.array( [] )
l2_W = np.array( [] )
l3_W = np.array( [] )

for epoch in range(1, n_epoch+1):
    print('epoch %d' % epoch)
    indexes = np.random.permutation(N)

    # learning loop
    sum_loss = 0
    sum_acc = 0
    for i in range(0, N, batchsize):
        x = Variable(x_train[indexes[i : i + batchsize]])
        t = Variable(y_train[indexes[i : i + batchsize]])

        model.zerograds()
        loss = model(x, t)
        loss.backward()
        optimizer.update()

        train_loss = np.append(train_loss, model.loss.data)
        train_acc = np.append(train_acc, model.accuracy.data)

        sum_loss += float(cuda.to_cpu(model.loss.data)) * batchsize
        sum_acc += float(cuda.to_cpu(model.accuracy.data)) * batchsize
    # 訓練データの誤差と、正解精度を表示
    print('train mean loss={}, accuracy={}'.format(sum_loss / N, sum_acc / N))

    # test loop
    sum_loss = 0
    sum_acc = 0
    for i in range(0, N_test, batchsize):
        x = Variable(x_test[i : i + batchsize])
        t = Variable(y_test[i : i + batchsize])

        loss = model(x, t)

        test_loss = np.append(test_loss, model.loss.data)
        test_acc = np.append(test_acc, model.accuracy.data)

        sum_loss += float(cuda.to_cpu(model.loss.data)) * batchsize
        sum_acc += float(cuda.to_cpu(model.accuracy.data)) * batchsize
        # テストデータでの誤差と、正解精度を表示
    print('test  mean loss={}, accuracy={}'.format(sum_loss / N_test, sum_acc / N_test))

    # 学習したパラメーターを保存
    np.append(l1_W, model.predictor.l1.W)
    np.append(l2_W, model.predictor.l2.W)
    np.append(l3_W, model.predictor.l3.W)

print(model.predictor.l1.W.shape)

# 精度と誤差をグラフ描画
nfig = 1
fig = plt.figure(nfig, figsize=(8,6))
ax1 = plt.subplot(2,2,1)
plt.plot(range(len(train_acc)), train_acc, color='tomato', linewidth=0.5)
plt.ylim([0.7, 1.0])
plt.title("Accuracy of digit recognition.")
ax1.patch.set_facecolor('lightgray')
plt.legend(["train_acc","train_acc"],loc=4)
ax1.grid(color='white')
plt.draw()

ax2 = plt.subplot(2,2,2)
plt.plot(range(len(test_acc)), test_acc, color='blue', linewidth=0.5)
plt.ylim([0.7, 1.0])
plt.title("Accuracy of digit recognition.")
ax2.patch.set_facecolor('lightgray')
plt.legend(["test_acc","test_acc"],loc=4)
ax2.grid(color='white')
plt.draw()

ax3 = plt.subplot(2,2,3)
plt.plot(range(len(train_loss)), train_loss, color='tomato', linewidth=0.5)
plt.ylim([0, 1.0])
plt.title("Accuracy of digit recognition.")
ax3.patch.set_facecolor('lightgray')
plt.legend(["train_loss","train_loss"],loc=2)
ax3.grid(color='white')
plt.draw()

ax4 = plt.subplot(2,2,4)
plt.plot(range(len(test_loss)), test_loss, color='blue', linewidth=0.5)
plt.ylim([0, 1.0])
plt.title("Loss of digit recognition of test.")
ax4.patch.set_facecolor('lightgray')
plt.legend(["loss_acc","loss_acc"],loc=2)
ax4.grid(color='white')
plt.savefig('Learn_Test.png')

# Result
# 手書き数字データを描画する関数
plt.style.use('fivethirtyeight')
def draw_digit(data, n, ans, recog):
    size = 28
    # plt.subplot(10,10,n)
    plt.subplot()

    X, Y = np.meshgrid(range(size),range(size))
    Z = data.reshape(size,size)   # convert from vector to 28x28 matrix
    Z = Z[::-1,:]             # flip vertical
    plt.xlim(0,27)
    plt.ylim(0,27)
    plt.title("ans=%d, recog=%d"%(ans,recog), size=15)
    plt.pcolor(X, Y, Z)
    plt.gray()
    plt.tick_params(labelbottom="off")
    plt.tick_params(labelleft="off")


cnt = 0
for idx in np.random.permutation(N)[:10]:
    cnt += 1
    #
    nfig += 1
    plt.figure(nfig)
    #
    x = x_train[idx].astype(np.float32)
    pred = model.predictor(Variable(np.array(x.reshape((1, 784)), dtype=np.float32))).data
    draw_digit(x_train[idx], cnt, y_train[idx], pred.argmax(axis=1)[0])
    plt.draw()
    plt.savefig('Confirmation_%03.f'%cnt+'.png')


def draw_digit2(data, n, i):
    size = 28
    plt.subplot(10, 10, n)
    Z = data.reshape(size, size)
    Z = Z[::-1, :]
    plt.xlim(0, 27)
    plt.ylim(0, 27)
    plt.pcolor(Z)
    plt.title("%d"%i, size=9)
    plt.gray()
    plt.tick_params(labelbottom="off")
    plt.tick_params(labelleft="off")

nfig += 1
plt.figure(nfig, figsize=(10,10))
cnt = 1




【記事1】を参考にやろうとするのですが、そもそもl1_Wの学習結果を参照出来ない問題が発生します。それはなんとかなりそうなんですが、そもそも元である学習結果をl1_Wに値を格納するところでこけているようです。



ということで、そのあたりについて調査します。



まず、【記事1】の下記【ソースコード2】ではmodelにl1という属性がないために動きません。



ソースコード2】

plt.style.use('fivethirtyeight')
def draw_digit3(data, n, ans, recog):
    size = 28
    plt.subplot(10, 10, n)
    Z = data.reshape(size,size)   # convert from vector to 28x28 matrix
    Z = Z[::-1,:]             # flip vertical
    plt.xlim(0,27)
    plt.ylim(0,27)
    plt.pcolor(Z)
    plt.title("ans=%d, recog=%d"%(ans,recog), size=8)
    plt.gray()
    plt.tick_params(labelbottom="off")
    plt.tick_params(labelleft="off")


plt.figure(figsize=(15,15))

cnt = 0
for idx in np.random.permutation(N)[:100]:

    xxx = x_train[idx].astype(np.float32)
    h1 = F.dropout(F.relu(model.l1(Variable(xxx.reshape(1,784)))),  train=False)
    h2 = F.dropout(F.relu(model.l2(h1)), train=False)
    y  = model.l3(h2)
    cnt+=1
    draw_digit3(x_train[idx], cnt, y_train[idx], np.argmax(y.data))
plt.show




したがってどうにかclassifierで定義したmodel内のl1にアクセスする方法がないかと調べたところ、明示的にではないですが、それっぽいソースコードを【記事2】で発見しました。一部抜粋したものを【ソースコード3】に示します。



【記事2】
ksksksks2.hatenadiary.jp



ソースコード3】

    x = chainer.Variable(data, volatile=False)
    h0 = model.predictor.embed(x)
    h1 = model.predictor.l1(h0)
    h2 = model.predictor.l2(h1)
    y = model.predictor.l3(h2)




predictorは作ったソースコードでも定義していたので、predictorから隠れ層のパラメータにアクセスできるのでしょうか。試しにpredictor.l1などを使ってみるとエラーも出ずに使えました。したがって、とりあえず【記事1】を私のソースコード内で使うには以下の置換を行います。

model.l1 => model.predictor.l1

これで上手くいくかと思いきや、次はl1_Wへの学習結果格納が失敗しているのは依然直らぬままです。ということで、保存したいものはなんなのさ?というところを確認しておきます。保存したいものは以下の通り。


model.predictor.l1.W

 
 
 
どんなものが出力されているのか以下の方法で確認すると…


print(model.predictor.l1.W)




結果は以下の通り。


(1000, 784)

 
 
 
どこかで見覚えのある数字ですね。Linearで定義したユニット層の数字です。サイズ784の配列が1000個格納されています。



これを配列に追記していくと3次元になると思うのですが、学習の記録として784×1000をepoch毎に保存するのもありだとは思いますが、今の“学習結果”を保存するという意味ではepoch終了時の状態だけを保存すれば良いと思います。



したがって、やるべき処理は784×1000の二次元配列をl1_Wに格納するだけで良いかと思います。



ちなみに、その観点のもと元のソースコードを見てみるとl1_Wのインデントがおかしいです。これだと各epoch毎に784×1000の学習過程を追加していくことになるので、epoch=20であれば784×1000×20の3次元配列にしないと,後でepochを分割するためのreshapeが必要になります。



あとはまだmatrixからデータを取り出す際の.data部分がおぼろげな認識でそこでも間違えてました。それらも含めて直したのが【ソースコード4】になります。



ソースコード4】

import matplotlib.pyplot as plt
import numpy as np
from sklearn.datasets import fetch_mldata
import chainer
from chainer import cuda, Variable, optimizers, Chain
import chainer.functions  as F
import chainer.links as L
import sys

mnist = fetch_mldata("MNIST original", data_home=".")

# 学習用データを N個、検証用データを残りの個数と設定
batchsize = 100
N = 60000

# 学習の繰り返し回数
n_epoch   = 1

x_all = mnist['data'].astype(np.float32) / 255
y_all = mnist['target'].astype(np.int32)
x_train, x_test = np.split(x_all, [N])
y_train, y_test = np.split(y_all, [N])
N_test = y_test.size

class MLP(Chain):
    def __init__(self):
        super(MLP, self).__init__(
        l1=L.Linear(784, 1000),
        l2=L.Linear(1000, 100),
        l3=L.Linear(100, 10),
        )

    def __call__(self, x):
        h1 = F.dropout(F.relu(self.l1(x)))
        h2 = F.dropout(F.relu(self.l2(h1)))
        y = self.l3(h2)
        return y

class Classifier(Chain):
    def __init__(self, predictor):
        super(Classifier, self).__init__()
        with self.init_scope():
            self.predictor = predictor

    def __call__(self, x, t):
        y = self.predictor(x)
        loss = F.softmax_cross_entropy(y, t)
        accuracy = F.accuracy(y, t)
        report({'loss': loss, 'accuracy': accuracy}, self)
        return loss

model = L.Classifier(MLP())
optimizer = optimizers.Adam()
optimizer.setup(model)



train_loss = np.array([])
train_acc = np.array([])
test_loss = np.array([])
test_acc  = np.array([])

l1_W = np.array( [] )
l2_W = np.array( [] )
l3_W = np.array( [] )

for epoch in range(1, n_epoch+1):
    print('epoch %d' % epoch)
    indexes = np.random.permutation(N)

    # learning loop
    sum_loss = 0
    sum_acc = 0
    for i in range(0, N, batchsize):
        x = Variable(x_train[indexes[i : i + batchsize]])
        t = Variable(y_train[indexes[i : i + batchsize]])

        model.zerograds()
        loss = model(x, t)
        loss.backward()
        optimizer.update()

        train_loss = np.append(train_loss, model.loss.data)
        train_acc = np.append(train_acc, model.accuracy.data)

        sum_loss += float(cuda.to_cpu(model.loss.data)) * batchsize
        sum_acc += float(cuda.to_cpu(model.accuracy.data)) * batchsize
    # 訓練データの誤差と、正解精度を表示
    print('train mean loss={}, accuracy={}'.format(sum_loss / N, sum_acc / N))

    # test loop
    sum_loss = 0
    sum_acc = 0
    for i in range(0, N_test, batchsize):
        x = Variable(x_test[i : i + batchsize])
        t = Variable(y_test[i : i + batchsize])

        loss = model(x, t)

        test_loss = np.append(test_loss, model.loss.data)
        test_acc = np.append(test_acc, model.accuracy.data)

        sum_loss += float(cuda.to_cpu(model.loss.data)) * batchsize
        sum_acc += float(cuda.to_cpu(model.accuracy.data)) * batchsize
        # テストデータでの誤差と、正解精度を表示
    print('test  mean loss={}, accuracy={}'.format(sum_loss / N_test, sum_acc / N_test))

# 学習結果を保存
l1_W = model.predictor.l1.W
l2_W = model.predictor.l2.W
l3_W = model.predictor.l3.W


# 精度と誤差をグラフ描画
nfig = 1
fig = plt.figure(nfig, figsize=(8,6))
ax1 = plt.subplot(2,2,1)
plt.plot(range(len(train_acc)), train_acc, color='tomato', linewidth=0.5)
plt.ylim([0.7, 1.0])
plt.title("Accuracy of digit recognition.")
ax1.patch.set_facecolor('lightgray')
plt.legend(["train_acc","train_acc"],loc=4)
ax1.grid(color='white')
plt.draw()

ax2 = plt.subplot(2,2,2)
plt.plot(range(len(test_acc)), test_acc, color='blue', linewidth=0.5)
plt.ylim([0.7, 1.0])
plt.title("Accuracy of digit recognition.")
ax2.patch.set_facecolor('lightgray')
plt.legend(["test_acc","test_acc"],loc=4)
ax2.grid(color='white')
plt.draw()

ax3 = plt.subplot(2,2,3)
plt.plot(range(len(train_loss)), train_loss, color='tomato', linewidth=0.5)
plt.ylim([0, 1.0])
plt.title("Accuracy of digit recognition.")
ax3.patch.set_facecolor('lightgray')
plt.legend(["train_loss","train_loss"],loc=2)
ax3.grid(color='white')
plt.draw()

ax4 = plt.subplot(2,2,4)
plt.plot(range(len(test_loss)), test_loss, color='blue', linewidth=0.5)
plt.ylim([0, 1.0])
plt.title("Loss of digit recognition of test.")
ax4.patch.set_facecolor('lightgray')
plt.legend(["loss_acc","loss_acc"],loc=2)
ax4.grid(color='white')
plt.savefig('Learn_Test.png')

# Result
# 手書き数字データを描画する関数
plt.style.use('fivethirtyeight')
def draw_digit(data, n, ans, recog):
    size = 28
    # plt.subplot(10,10,n)
    plt.subplot()

    X, Y = np.meshgrid(range(size),range(size))
    Z = data.reshape(size,size)   # convert from vector to 28x28 matrix
    Z = Z[::-1,:]             # flip vertical
    plt.xlim(0,27)
    plt.ylim(0,27)
    plt.title("ans=%d, recog=%d"%(ans,recog), size=15)
    plt.pcolor(X, Y, Z)
    plt.gray()
    plt.tick_params(labelbottom="off")
    plt.tick_params(labelleft="off")

cnt = 0
for idx in np.random.permutation(N)[:10]:
    cnt += 1
    #
    nfig += 1
    plt.figure(nfig)
    #
    x = x_train[idx].astype(np.float32)
    pred = model.predictor(Variable(np.array(x.reshape((1, 784)), dtype=np.float32))).data
    draw_digit(x_train[idx], cnt, y_train[idx], pred.argmax(axis=1)[0])
    plt.savefig('Confirmation_%03.f'%cnt+'.png')

def draw_digit2(data, n, i):
    size = 28
    plt.subplot(10, 10, n)
    Z = data.reshape(size, size)
    Z = Z[::-1, :]
    Z = Z.data
    X, Y = np.meshgrid(range(28),range(28))
    plt.xlim(0, 27)
    plt.ylim(0, 27)
    plt.pcolor(X, Y, Z)
    plt.title("%d"%i, size=9)
    plt.gray()
    plt.tick_params(labelbottom="off")
    plt.tick_params(labelleft="off")

nfig += 1
plt.figure(nfig, figsize=(10,10))
cnt = 1

for i in np.random.permutation(1000)[:100]:
    draw_digit2(l1_W[i], cnt, i)
    cnt += 1
plt.savefig('Hidden_layer_1_randam.png')

plt.show()




ソースコード4】で得られた隠れ層の学習結果は以下の通りです。



【画像1】
f:id:cocosuzu:20171105174533p:plain



ようやくここまで出来ました。Pythonの基本がまだまだなところからくるミスも多かったので、この後はラズパイなどとからめてPythonの練習を強化したいところです。



また、深層学習は依然としてまだまだ入り口です。ここまでの内容からどのように発展させていくか一度考えてみます。多分まだ理解できていないのですがCNNとかまでまだ至っていないと思うので、専門書を何冊か読んで今の自分がどの位置まで進んでいるのか確認します。



それと並行して、やはり0から進めていくよりも、良い教材で勉強していくのが一番効率が良いと思いますので良い参考記事を探したいと思います。相変わらずカタツムリ学習スピードをどうにかせねば…



今のところある程度できるようになったらやりたいアイデアは以下の通りです。



(1) 28×28の画像を分類する応用
(2) 生産データの解析用に複数個のパラメータを含む各製品をOK/NGで分類する学習の実装
(3) 物判別の教育実装
(4) 物の位置を認識して追いかけるマシーンの作製




以下過去記事のリストです。後でエッセンスだけまとめたいと思います。


深層学習の学習【その1】[追記] - Kuroyagi飼育日誌
深層学習の学習【その2】[追記] - Kuroyagi飼育日誌
深層学習の学習 【その3】[追記] - Kuroyagi飼育日誌
深層学習の学習 【その4】 - Kuroyagi飼育日誌
深層学習の学習 [その4.5] - Kuroyagi飼育日誌
深層学習の学習 [その5] - Kuroyagi飼育日誌