予定にありませんでしたがpart5.5でございます。
くそミスのせいでこんなことに。
どうせ5.5として書くならもう一回コードをモデルから張り直して、整理します。
-- 目次 --
まずは2値分類してみる。
ここから主要キャラの分類
- part5 主要キャラで分類問題(未知データに適用するよ編)
- part5.5 主要キャラで分類問題(改良編) <--- イマココ
- part6 主要キャラで分類問題(GPU使ったよ編)
- part7 新規データをFasterRCNNを使って分類させる(むしろ全てはこれのため)
今まで、コードに関しての説明はぼちぼちしてきたのでまずはコード載せていきます。
モデル
#coding:utf-8 import os import chainer from chainer import optimizers import chainer.functions as F import chainer.links as L import chainer.serializers as S import numpy as np #chainerからChainを継承してクラスを作成します。 class clf_bake(chainer.Chain): def __init__(self): super(clf_bake, self).__init__( conv1 = F.Convolution2D(3, 16, 5, pad=2), conv2 = F.Convolution2D(16, 32, 5, pad=2), l3 = F.Linear(6272, 256), l4 = F.Linear(256, 10) #ここが10クラスの分類に変わっています。 ) def clear(self): self.loss = None self.accuracy = None def forward(self, X_data, y_data, train=True): self.clear() X_data = chainer.Variable(np.asarray(X_data), volatile=not train) y_data = chainer.Variable(np.asarray(y_data), volatile=not train) h = F.max_pooling_2d(F.relu(self.conv1(X_data)), ksize = 5, stride = 2, pad =2) h = F.max_pooling_2d(F.relu(self.conv2(h)), ksize = 5, stride = 2, pad =2) h = F.dropout(F.relu(self.l3(h)), train=train) y = self.l4(h) return F.softmax_cross_entropy(y, y_data), F.accuracy(y, y_data)
続いて学習&テストコード
#coding: utf-8 #必要なライブラリのインポート import cv2 import os import six import datetime import chainer from chainer import optimizers import chainer.functions as F import chainer.links as L import chainer.serializers as S from clf_bake_model import clf_bake import numpy as np #その1 ------データセット作成------ #フォルダは整数で名前が付いています。 def getDataSet(): #リストの作成 X_train = [] X_test = [] y_train = [] y_test = [] for i in range(0,10): path = "/Users/path/to/dir" if i == 0: #othersは15000枚用意します。テスト用には3000枚 cutNum = 22000 cutNum2 = 18000 else: #主要キャラたちは1800枚ずつ。テスト用には300枚 cutNum = 4000 cutNum2 = 3600 imgList = os.listdir(path+str(i)) imgNum = len(imgList) for j in range(cutNum): imgSrc = cv2.imread(path+str(i)+"/"+imgList[j]) #imreadはゴミを吸い込んでも、エラーで止まらずNoneを返してくれます。 #ですので読み込み結果がNoneでしたらスキップしてもらいます。 if imgSrc is None:continue if j < cutNum2: X_train.append(imgSrc) y_train.append(i) else: X_test.append(imgSrc) y_test.append(i) return X_train,y_train,X_test,y_test #その3 ---------学習させる------- def train(): #上で作った関数でデータセットを用意します。 X_train,y_train,X_test,y_test = getDataSet() #このままだとchainerで読み込んでもらえないので、array型にします。 X_train = np.array(X_train).astype(np.float32).reshape((len(X_train),3, 50, 50)) / 255 y_train = np.array(y_train).astype(np.int32) X_test = np.array(X_test).astype(np.float32).reshape((len(X_test),3, 50, 50)) / 255 y_test = np.array(y_test).astype(np.int32) # 諸々の初期設定 model = clf_bake() optimizer = optimizers.Adam() optimizer.setup(model) epochNum = 30 batchNum = 50 epoch = 1 # 学習とテスト while epoch <= epochNum: print("epoch: {}".format(epoch)) print(datetime.datetime.now()) trainImgNum = len(y_train) testImgNum = len(y_test) #---学習--- sumAcr = 0 sumLoss = 0 perm = np.random.permutation(trainImgNum) for i in six.moves.range(0, trainImgNum, batchNum): #ランダムにbatchNumの数だけ抽出する X_batch = X_train[perm[i:i+batchNum]] y_batch = y_train[perm[i:i+batchNum]] optimizer.zero_grads() loss, acc = model.forward(X_batch, y_batch) loss.backward() optimizer.update() sumLoss += float(loss.data) * len(y_batch) sumAcr += float(acc.data) * len(y_batch) print('train mean loss={}, accuracy={}'.format(sumLoss / trainImgNum, sumAcr / trainImgNum)) #---テスト--- sumAcr = 0 sumLoss = 0 for i in six.moves.range(0, testImgNum, batchNum): X_batch = X_test[i:i+batchNum] y_batch = y_test[i:i+batchNum] loss, acc = model.forward(X_batch, y_batch, train=False) sumLoss += float(loss.data) * len(y_batch) sumAcr += float(acc.data) * len(y_batch) print('test mean loss={}, accuracy={}'.format( sumLoss / testImgNum, sumAcr / testImgNum)) epoch += 1 #モデルの保存 S.save_hdf5('model'+str(epoch+1), model) train()
最後に未知データに対する予想をするコード
# -*- coding: utf-8 -*- #!/usr/bin/env python import sys import numpy as np import six import cv2 import os import chainer from chainer import computational_graph as c import chainer.functions as F import chainer.serializers as S from chainer import optimizers from clf_bake_model import clf_bake #モデルの読み込み model = clf_bake() S.load_hdf5('./4layersModel/model26', model) #model = pickle.load(open('model30','rb')) #キャラクターの名前 #今回は色だけで分けているので使っていませんが一応 chara_name = ['unknown', "koyomi","hitagi","tsubasa","suruga","nadeko","karen","tsukihi","shinobu","mayoi"] #伝播の設定 def forward(x_data): x = chainer.Variable(x_data, volatile=False) h = F.max_pooling_2d(F.relu(model.conv1(x)), ksize = 5, stride = 2, pad =2) h = F.max_pooling_2d(F.relu(model.conv2(h)), ksize = 5, stride = 2, pad =2) h = F.dropout(F.relu(model.l3(h)), train=False) y = model.l4(h) return y #顔検出関数 def detect(image, cascade_file = "/Users/path/to/lbpcascade_animeface.xml"): if not os.path.isfile(cascade_file): raise RuntimeError("%s: not found" % cascade_file) cascade = cv2.CascadeClassifier(cascade_file) gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY) gray = cv2.equalizeHist(gray) faces = cascade.detectMultiScale(gray, scaleFactor = 1.1, minNeighbors = 1, minSize = (20, 20)) print(faces) return faces #検出された顔を識別する関数 def recognition(image, faces): face_images = [] for (x, y, w, h) in faces: dst = image[y:y+h, x:x+w] dst = cv2.resize(dst, (50, 50)) face_images.append(dst) face_images = np.array(face_images).astype(np.float32).reshape((len(face_images),3, 50, 50)) / 255 #face_images = cuda.to_gpu(face_images) return forward(face_images) , image #識別結果を描画する関数 def draw_result(image, faces, result): count = 0 for (x, y, w, h) in faces: classNum = 0 result_data = result.data[count] classNum = result_data.argmax() recognized_class = chara_name[result_data.argmax()] if classNum == 0: cv2.rectangle(image, (x, y), (x+w, y+h), (255,255,51), 3) elif classNum == 1: cv2.rectangle(image, (x, y), (x+w, y+h), (0,0,0), 3) elif classNum == 2: cv2.rectangle(image, (x, y), (x+w, y+h), (255,0,255), 3) elif classNum == 3: cv2.rectangle(image, (x, y), (x+w, y+h), (255,255,255), 3) elif classNum == 4: cv2.rectangle(image, (x, y), (x+w, y+h), (255,0,0), 3) elif classNum == 5: cv2.rectangle(image, (x, y), (x+w, y+h), (153,51,255), 3) elif classNum == 6: cv2.rectangle(image, (x, y), (x+w, y+h), (0,0,255), 3) elif classNum == 7: cv2.rectangle(image, (x, y), (x+w, y+h), (0,255,0), 3) elif classNum == 8: cv2.rectangle(image, (x, y), (x+w, y+h), (0,255,255), 3) elif classNum == 9: cv2.rectangle(image, (x, y), (x+w, y+h), (0,204,255), 3) count+=1 return image #ファイル読み込み img = cv2.imread("test.jpg") faces = detect(img) result, image = recognition(img, faces) image = draw_result(image, faces, result) cv2.imwrite('out.png',image)
適用結果!
上の一連のコードで学習した結果がこちら。
epoch: 1 2016-03-23 16:07:03.075410 train mean loss=1.0787541097, accuracy=0.62730466466 test mean loss=1.65130365632, accuracy=0.463928382222 epoch: 2 2016-03-23 16:38:50.100446 train mean loss=0.699703022108, accuracy=0.761089168048 test mean loss=1.11648284097, accuracy=0.637704062016 epoch: 3 2016-03-23 17:10:51.170715 train mean loss=0.54584181995, accuracy=0.813959945566 test mean loss=1.25304294185, accuracy=0.637177467181 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ epoch: 28 2016-03-24 06:28:45.132229 train mean loss=0.144246906905, accuracy=0.950086330421 test mean loss=1.49791400061, accuracy=0.736835175373 epoch: 29 2016-03-24 06:59:53.761350 train mean loss=0.149735492156, accuracy=0.949510785031 test mean loss=1.41343511337, accuracy=0.754081094199 epoch: 30 2016-03-24 07:31:01.595651 train mean loss=0.141215701596, accuracy=0.951158035467 test mean loss=1.25496952082, accuracy=0.767509214906
はい。明らかに前回より良くなっていますね。そりゃそうですよ。
まだ収束仕切ってない感がありますが、model30を使ってテストしてみます!
え?時間がえぐいことになってるって?いいのいいの。CPUの限界ですわ。
未知データに適用するよ!
負例が水色
暦が黒、ひたぎが紫、翼が白、真宵がオレンジ、駿河が青、撫子がピンク、忍が黄色、火憐が赤、月火が緑
になってます。
ん〜翼がダメだこれ。
多分三つ編みverとショートカットverを同じところにぶっ込んでるのがまずいんだろうなあ。
あとやっぱりカスケードが拾ってくる精度が微妙です。
fasterじゃなくてまずはsliding windowから試すべきかな?
まあとりあえず一件落着ってことで。
改良したらまた更新します。
-- 目次 --
まずは2値分類してみる。
ここから主要キャラの分類
- part5 主要キャラで分類問題(未知データに適用するよ編)
- part5.5 主要キャラで分類問題(改良編) <--- イマココ
- part6 主要キャラで分類問題(GPU使ったよ編)
- part7 新規データをFasterRCNNを使って分類させる(むしろ全てはこれのため)