のんびりしているエンジニアの日記

ソフトウェアなどのエンジニア的な何かを書きます。

DenoisingAutoEncoderでアニメ顔の特徴を抽出してみた

Sponsored Links

皆さんこんにちは
お元気ですか。私は人生元気に仲良くフリーダムに生きています。

本日はDenoisingAutoEncoder(DAE)を使って実験してみたいと思います。

DeepLearningの重みは可視化できます。他のブログで掲載されている
可視化について殆ど、MNISTだったので、今回試しに、
アニメ顔の特徴を抽出してみました。

データセット

今回の実験ではanimeface-character-datasetを使います。
Tile化して表示させるのが面倒だったので、Finderのスクリーンショットで
お許し下さい。

※7/13追記:animeface-character-datasetはanimeface-character-datasetから手に入ります。

f:id:tereka:20150630222352p:plain

ソースコード

Chainerを使って記載しました。大部分のDAEのコードは
Implementation of stacked denoising autoencoder using BriCA1 and Chainer.を参考にしました。ありがとうございます。

AnimeFaceの読み込み

#coding:utf-8
import cv2
import glob
import numpy as np
class AnimeFaceDataset(object):
	def __init__(self,folder,reshape_size=28):
		self.data_list = self.image_read(folder,reshape_size)

	def image_read(self, folder,reshape_size):
		data_list = []
		for file in glob.glob(folder):
			image = cv2.resize(cv2.cvtColor(cv2.imread(file),cv2.COLOR_BGR2GRAY),(reshape_size,reshape_size))
			data_list.append(np.reshape(image,reshape_size * reshape_size) / 255.0)
		return np.array(data_list,dtype=np.float32)

	def getImages(self):
		return self.data_list

DenoisingAutoEncoder

#coding:utf-8
from chainer import Variable, FunctionSet, optimizers, cuda
import chainer.functions as F
import numpy as np

class DenoisingAutoEncoder(object):
	def __init__(self, input_size, hidden_size):
		self.model = FunctionSet(
				encode = F.Linear(input_size, hidden_size),
				decode = F.Linear(hidden_size, input_size)
			)
		self.use_cuda = False
		self.optimizer = optimizers.Adam()
		self.optimizer.setup(self.model.collect_parameters())

	def encode(self,x):
		return F.sigmoid(self.model.encode(x))

	def decode(self,x):
		return F.sigmoid(self.model.decode(x))

	def cost(self, x, loss_param):
		if self.use_cuda:
			x = cuda.to_gpu(x)
		lost_x = x * np.random.binomial(1, 1 - loss_param,len(x[0]))
		x_variable = Variable(lost_x)
		t_variable = Variable(x)

		y = self.encode(x_variable)
		z = self.decode(y)

		return F.mean_squared_error(z, t_variable)

	def train(self, x, loss_param):
		self.optimizer.zero_grads()
		loss = self.cost(x, loss_param)
		loss.backward()
		self.optimizer.update()
		return float(cuda.to_cpu(loss.data))

	def predict(self, x):
		x_variable = Variable(x)
		y_variable = self.model.encode(x_variable)
		return cuda.to_cpu(y_variable)

	def getWeight(self):
		return self.model.encode.W

実行

#coding:utf-8
import AnimeFaceDataset
import DenoisingAutoEncoder
import numpy as np
import Visualize
import cv2

reshape_size = 50

animeFaceDataset = AnimeFaceDataset.AnimeFaceDataset("./animeface-character-dataset/thumb/*/*.png",reshape_size=reshape_size)
data = animeFaceDataset.getImages()

batch_size = 64
N = len(data)
n_epoch = 70

visualize = Visualize.Visualizer()

for loss_param in [0.0,0.1,0.2,0.3,0.5]:
	dae = DenoisingAutoEncoder.DenoisingAutoEncoder(reshape_size * reshape_size,500)

	for epoch in range(1, n_epoch+1):
		print('epoch', epoch)
		if epoch == 1:
			tile = visualize.convert(dae.getWeight(), reshape_size, (20,20))
			cv2.imwrite("./training_result/weight_tile_t_%f_%d.png" % (loss_param,0), tile)

		perm = np.random.permutation(N)
		sum_loss = 0.0
		for i in xrange(0, N, batch_size):
			x_batch = data[i:i+batch_size]
			sum_loss = sum_loss + dae.train(x_batch, loss_param=loss_param)
		print sum_loss / N
		tile = visualize.convert(dae.getWeight(), reshape_size, (20,20))
		cv2.imwrite("./training_result/weight_tile_t_%f_%d.png" % (loss_param,epoch), tile)

Visualizer

こちらは、Theano tutrialを参考にして作成しました。

#coding:utf-8
import numpy as np
import cv2
class Visualizer(object):
	def __init__(self):
		pass

	def scale_to_unit_interval(self,ndar, eps=1e-8):
	    ndar = ndar.copy()
	    ndar -= ndar.min()
	    ndar *= 1.0 / (ndar.max() + eps)
	    return ndar

	def convert(self,weights,size,tile_size = (10,10)):
		tile = np.zeros((tile_size[0] * size, tile_size[1] * size),dtype='uint8')
		tile_images = []
		for weight in weights:
			tile_image = self.scale_to_unit_interval(weight)
			tile_image = np.reshape(tile_image,(size,size)) * 255
			tile_images.append(tile_image)

		cnt = 0
		for height_index in xrange(tile_size[0]):
			for width_index in xrange(tile_size[1]):
				height = height_index * size
				width = width_index * size
				tile[height:height+size,width:width+size] = tile_images[cnt]
				cnt = cnt + 1
		return tile

特徴量抽出結果

初期状態

f:id:tereka:20150630221531p:plain

欠損率0.0

f:id:tereka:20150630221546p:plain

欠損率0.1

f:id:tereka:20150630221558p:plain

欠損率0.3

f:id:tereka:20150630221612p:plain

欠損率0.5

f:id:tereka:20150630221713p:plain

誰が誰かというのはわかりませんが顔ということはわかりますね。
色々おもしろいことをやっている人は沢山いますので、様々な画像をかけてみると良いかもしれません。