TensorFlow实践|TensorFlow实现人脸表情识别

╰+攻爆jí腚メ 2022-09-29 14:49 367阅读 0赞

运行程序输入:

  1. import input_emotion_data
  2. data = input_emotion_data.read_data(input_dir = 'emotion')
  3. import cnnemotion
  4. cnnemotion.run_training(data=data, n_training_data=data.train.images.shape[0],
  5. is_CAENN=False, batch_size=170, n_training_epochs = 10, n_cae_training_epochs=5)

input_emotion_data.py

  1. def read_data(input_dir, cut64x64=False, test_rate = 0.2, one_hot=True):
  2. temp_dir = path.join(input_dir, 'temp')

os.path.join()

os.path.join()是在拼接路径时使用的,上句返回路径”emotion/temp”

在运行语句”import input_emotion_data”后,在emotion文件夹里生成temp文件夹,但temp里面是空的。

在运行语句“data = input_emotion_data.read_data(input_dir = ‘emotion’)”后,temp文件变成如下:

Center

并且输出了一些one-hot数组和内容是浮点数的数组。

它们究竟是什么?我猜是labels和images~不过images长得形状有点奇怪,要好好研究下它为什么长那样。

Center 1

  1. # 如果序列化文件存在,则直接将其数据反序列化读入data中,然后函数返回data即可。
  2. filename = "Emotion_pic_cut64x64_%s_rate_%.1f_onehot_%s.pickle" % (str(cut64x64), test_rate, str(one_hot))
  3. if path.exists(path.join(temp_dir, filename)):
  4. with open(path.join(temp_dir, filename), 'rb') as f_pickle:
  5. data = pickle.load(f_pickle)
  6. print("type(data)")
  7. print(type(data))
  8. return data
  9. # 否则,继续运行read_data()函数下面的其他语句
  10. pic_with_anger_dir = path.join(temp_dir, 'anger')
  11. pic_with_happiness_dir = path.join(temp_dir, 'happiness')
  12. pic_with_sadness_dir = path.join(temp_dir, 'sadness')
  13. pic_with_calm_dir = path.join(temp_dir, 'calm')
  14. if not path.exists(temp_dir):
  15. os.mkdir(temp_dir)

序列化

在程序运行的过程中,所有的变量都是在内存中,但是一旦程序结束,变量所占用的内存就被操作系统全部回收。

我们把变量从内存中变成可存储或传输的过程称之为序列化,在Python中叫pickling。

序列化之后,就可以把序列化后的内容写入磁盘,或者通过网络传输到别的机器上。

反过来,把变量内容从序列化的对象重新读到内存里称之为反序列化,即unpickling。

pickle.load(file):注解:从file中读取一个字符串,并将它重构为原来的python对象。

  1. # unzip
  2. zipfile.ZipFile(path.join(input_dir, 'anger.zip')).extractall(path=temp_dir)
  3. zipfile.ZipFile(path.join(input_dir, 'happiness.zip')).extractall(path=temp_dir)
  4. zipfile.ZipFile(path.join(input_dir, 'sadness.zip')).extractall(path=temp_dir)
  5. zipfile.ZipFile(path.join(input_dir, 'calm.zip')).extractall(path=temp_dir)
  6. # get all pictures filename
  7. pic_with_anger_filenames = os.listdir(pic_with_anger_dir)
  8. pic_with_happiness_filenames = os.listdir(pic_with_happiness_dir)
  9. pic_with_sadness_filenames = os.listdir(pic_with_sadness_dir)
  10. pic_with_calm_filenames = os.listdir(pic_with_calm_dir)

listdir()

os.listdir() 方法用于返回指定的文件夹包含的文件或文件夹的名字的列表。这个列表以字母顺序。 它不包括 ‘.’ 和’..’ 即使它在文件夹中。

只支持在 Unix, Windows 下使用。

方法格式如下:os.listdir(path)

  1. for pic_filename in pic_with_anger_filenames: #anger的数据预处理
  2. img = Image.open(path.join(pic_with_anger_dir, pic_filename))
  3. img = np.asarray(img, dtype = np.float32)
  4. print("anger_img in line 60")
  5. print(img)
  6. print(img.shape[0],img.shape[1])
  7. if cut64x64 == True:
  8. img = img[7:71, 7:71, 0] # 原图大小为79*79,现只居中取其64*64,而且图像是灰度图像,三个通道的值均相同,因此只取其中一个通道即可
  9. img = img.reshape(img.shape[0], img.shape[1], 1) / 256.
  10. print("anger_img in line 66")
  11. print(img)
  12. print(img.shape[0],img.shape[1],img.shape[2])
  13. pic_with_anger.append(img)
  14. pic_with_anger = np.stack(pic_with_anger, axis = 0)

输出:

Center 2

可以看出来,经过np.asarray()的图像是一个64*64的二维数组,且其中每个元素在0到255之间;

而经过img.reshape()的二维数组变成了一个64*64*1的三维数组,且其中每个元素都小于1。

numpy.asarray()

#

  1. >>> a =[[1,2],[1,0]]
  2. >>> a = numpy.asarray(a)
  3. >>> a
  4. array([[1, 2],
  5. [1, 0]])
  6. >>> numpy.asarray(a,'f')
  7. array([[ 1., 2.],
  8. [ 1., 0.]], dtype=float32)

Image.open()

Center 3

灰度图像

在计算机领域中,这类图像通常显示为从最暗黑色到最亮的白色的灰度,尽管理论上这个采样可以任何颜色的不同深浅,甚至可以是不同亮度上的不同颜色。

用于显示的灰度图像通常用每个采样像素8位的非线性尺度来保存,这样可以有256级灰度。这种精度刚刚能够避免可见的条带失真,并且非常易于编程。

np.stack()

看上去是合并数组的。

  1. >>> arrays = [np.random.randn(3, 4) for _ in range(10)]
  2. >>> np.stack(arrays, axis=0).shape
  3. (10, 3, 4)
  4. >>>
  5. >>> np.stack(arrays, axis=1).shape
  6. (3, 10, 4)
  7. >>>
  8. >>> np.stack(arrays, axis=2).shape
  9. (3, 4, 10)
  10. >>>
  11. >>> a = np.array([1, 2, 3])
  12. >>> b = np.array([2, 3, 4])
  13. >>> np.stack((a, b))
  14. array([[1, 2, 3],
  15. [2, 3, 4]])
  16. >>>
  17. >>> np.stack((a, b), axis=-1)
  18. array([[1, 2],
  19. [2, 3],
  20. [3, 4]])

cnnemotion.py

神经网络中对epoch和batch的理解:

一次epoch=所有训练数据forward+backward后更新参数的过程。
一次iteration=[batch size]个训练数据forward+backward后更新参数过程。

  1. for i in range(data.train.images.shape[0] // batch_size):

‘//’表示地板除,//除法不管操作数为何种数值类型,总是会舍去小数部分,返回数字序列中比真正的商小的最接近的数字。

utils.py

  1. class DataSet(object):
  2. """create same API with input_data.DataSet, but not change the data format
  3. """
  4. def __init__(self, images, labels):
  5. self._num_examples = images.shape[0]
  6. self._images = images
  7. self._labels = labels
  8. self._epochs_completed = 0
  9. self._index_in_epoch = 0
  10. # Shuffle the data
  11. perm = np.arange(self._num_examples)
  12. np.random.shuffle(perm)
  13. self._images = self._images[perm]
  14. self._labels = self._labels[perm]
  15. @property
  16. def images(self):
  17. return self._images
  18. @property
  19. def labels(self):
  20. return self._labels
  21. @property
  22. def num_examples(self):
  23. return self._num_examples
  24. @property
  25. def epochs_completed(self):
  26. return self._epochs_completed
  27. def next_batch(self, batch_size):
  28. start = self._index_in_epoch
  29. self._index_in_epoch += batch_size
  30. if self._index_in_epoch > self._num_examples:
  31. # Finished epoch
  32. self._epochs_completed += 1
  33. # Shuffle the data
  34. perm = np.arange(self._num_examples)
  35. np.random.shuffle(perm)
  36. self._images = self._images[perm]
  37. self._labels = self._labels[perm]
  38. # Start next epoch
  39. start = 0
  40. self._index_in_epoch = batch_size
  41. assert batch_size <= self._num_examples
  42. end = self._index_in_epoch
  43. return self._images[start:end], self._labels[start:end]

np.arange()

np.random.shuffle()

  1. >>> import numpy as np
  2. >>> perm = np.arange(20) # 返回一个序列,可以被当做向量使用
  3. >>> perm
  4. array([ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16,
  5. 17, 18, 19])
  6. >>> np.random.shuffle(perm) # 用于将列表中的元素打乱
  7. >>> perm
  8. array([11, 0, 9, 4, 16, 7, 1, 14, 12, 18, 8, 15, 19, 5, 13, 3, 2,
  9. 17, 10, 6])

错误

  1. 1.AttributeError: module 'tensorflow' has no attribute 'global_variables_initializer'

将tf.global_variables_initializer()改成tf.initialize_all_variables()就好啦~

(不是说initialize_all_variables已被弃用,在2017-03-02之后删除,使用tf.global_variables_initializer代替吗???)

表情数据库

  • JAFFE数据库:数据库是由10个人的7种正面表情(6种基本表情+1种自然表情)组成的213幅灰度图像,图像是以大小为256*256的8位灰度级存储的,格式为.tiff型,平均每个人每种表情有2到4张。图片命名中包含表情标注。表情包括HAP(高兴) SAD(悲伤) SUR(惊讶) ANG(生气) DIS(厌恶) FEA(恐惧) NE(自然)。
  • CK+数据库:这个数据库包括123个subjects, 593 个 image sequence, 每个image sequence的最后一张 Frame 都有action units 的label, 而在这593个image sequence中,有327个sequence 有 emotion的 label。图像是以大小为480*480的8位灰度级存储的。这个数据库是人脸表情识别中比较流行的一个数据库,很多文章都会用到这个数据做测试。表情包括Disgust 3(厌恶59)Happy 5(高兴69)Surprise 7(惊讶83)Fear 4(恐惧25)Angry 1(生气45)Contempt 2(蔑视18)Sadness 6(忧伤28) Neutral 0(自然)。

问题

  • 如何确定需要几层网络?几个卷积层?几个dropout?几个全连接层?有什么参考依据吗?
  • 如何确定每层需要多少个特征图?有参考依据吗?

目前进度

2017年3月16日版本:

  • 基本模型建立起来了,只有197个训练数据,batch取120,epoch取50,发现准确率在50%左右,但在测试集上准确率只有30%左右。
  • 只有四类表情:生气(45幅),平静(112幅),开心(69幅),忧伤(28幅)。

  • 存在问题:训练数据量太小;泛化率太差!

2017年3月20日结果:

  • 将jaffe数据库处理成只包含人脸的96*96大小图像
  • 将ck+数据库处理成只包含人脸的64*64大小图像

2017年3月22日结果:

  • 解决了一个warning,通过在这里f1 = f1_score(y_true, y_pred, average=”weighted”)添加average属性。
  • 加入了dropout操作,不过正不正确不晓得啦~

2017年3月23日结果:

  • 对数据集进行旋转操作

目前结果

2017年3月20日结果:

1.来自CK+数据集(生气(45幅),平静(112幅),开心(69幅),忧伤(28幅)),训练集共211幅图像,分四类。

Center 4

2.来自CK+和jaffe数据集(生气(75幅),平静(142幅),开心(100幅),忧伤(59幅)),训练集共302幅图像,分四类。

Center 5

对于今天跑出来的结果,我很不明白。

为什么在第一种情况下大约10个epoch参数就不再更新了?并且前面正确率变化幅度那样的大?

为什么在第二种情况下爱经过了120个epoch参数还是没有平稳?

怎样选择epoch,minibatch?

现在应该从哪些方面改进?有什么基准吗?

需要对CK+库里的图像做光照补偿吗?

图像预处理需要做到什么程度?

UFLDL图像预处理

3月20号抛出模型以后,就一筹莫展,不知道该怎么评估自己的模型。

紧接着就去翻看视频资料、图书资料,去查找评估模型的方法。

  1. Epoch 119, loss=0.00, AUC=1.00, accuracy=1.0000, precision=1.00, recall=1.00, f1=1.00 (3.021 sec)
  2. test set: AUC=0.85, accuracy=0.6351, precision=0.70, recall=0.64, f1=0.66

听说训练集上正确率达到100%,很可能是模型过拟合了。所以接下来打算再仔细研读代码,增加dropout试试。

2017年3月22日结果:

1.修改batch_size为50(原来为120),正确率提高啦~

  1. Epoch 49, loss=0.03, AUC=1.00, accuracy=0.9967, precision=1.00, recall=1.00, f1=1.00 (2.407 sec)
  2. test set: AUC=0.86, accuracy=0.7027, precision=0.72, recall=0.70, f1=0.71

Center 6

2.加入dropout操作,训练的时候0.5

Center 7

Center 8

使用dropout了以后,每次运行结果竟然会差这么多!!运行了4次,其中1,3次像第一张图,2,4次像第二张图。

哈哈哈,为什么呀???

参考文献

  1. 浅析Python中的序列化存储的方法
  2. Python图像处理库:PIL中Image,ImageDraw等基本模块介绍
  3. Python numpy函数hstack() vstack() stack() dstack() vsplit() concatenate()
  4. JAFFE数据库

发表评论

表情:
评论列表 (有 0 条评论,367人围观)

还没有评论,来说两句吧...

相关阅读

    相关 TensorFlow 人脸识别

    TensorFlow 人脸识别 以下资料来源于极客时间学习资料 人脸识别问题概述 **人脸识别概述**   人脸识别,特指利用分析比较人脸视觉特征信息进行...