iT邦幫忙

2021 iThome 鐵人賽

DAY 3
0
AI & Data

AI Facial Expression Recognition: Data, Model, Application系列 第 3

[Day 03] 一聲探氣,索性來資料分析 (探索性資料分析)

前言

昨天我們介紹了FER2013表情資料集,今天要來讀取資料與做探索性資料分析。

  • Exploratory Data Analysis (EDA):
    意旨用統計手法分析資料集,取出一些統計值,如樣本數、平均數和四分位數。
    也可以用統計圖表畫出資料的趨勢,常見的統計圖有直方圖、折線圖。
    EDA可以幫助我們了解資料的特性,像是qqplot可以看出資料是否接近常態分布。
    常常可以幫我們檢驗資料是否符合統計分析方法的先前假設(hypothesis),
    像是變異數分析中(ANOVA),樣本中不同組別的依變項(y)必須符合變異數同質性,
    所做出來的分析結果才會是可信的。

事前準備

你應該要有一個fer2013.csv,與你的python程式碼放在同一個資料夾中。

1. 讀取套件

import pandas as pd
import numpy as np
import os
import matplotlib.pyplot as plt

2. 讀取資料

由於原始資料是長度為2304的字串,每個數字以空格隔開,
所以我們使用np.fromstring把字串轉成numpy array,
再用np.reshape轉成2維陣列,視為圖片的矩陣。

def prepare_data(data):
    """ Prepare data for modeling 
        input: data frame with labels and pixel data
        output: image and label array """

    image_array = np.zeros(shape=(len(data), 48, 48, 1))
    image_label = np.array(list(map(int, data['emotion'])))

    for i, row in enumerate(data.index):
        image = np.fromstring(data.loc[row, 'pixels'], dtype=int, sep=' ')
        image = np.reshape(image, (48, 48, 1))  # 灰階圖的channel數為1
        image_array[i] = image

    return image_array, image_label

2. 觀察資料範例

fer2013.csv只有3個欄位,分別是:

  1. emotions: 圖片的標籤(label),也是我們的表情類別(y)。
  2. pixels: 圖片的像素,值域為0 ~ 255。
  3. Usage: 該圖片用途,分成Training、PublicTest和PrivateTest,前者用來訓練,當初比賽最後的冠軍是以PrivateTest上的準確率為主,但是在有些論文則會看PublicTest。

資料範例


3. 切分訓練集、公開測試集和私人測試集

有些小夥伴會問:我們不是要做EDA嗎?為甚麼要分開資料集呢?
當然!不管是PublicTest還是PrivateTest,都是我們未知的資料,
實務上來說,我們不會獲得測試集的數據,更不可能獲得測試集的標籤。
所以我們要在假裝看不見測試集的情況下去理解資料。
通常是把訓練集再拆分一些出來成為驗證集,以驗證集代替測試集去檢驗模型。
但在這裡我們將公開測試集當作驗證集、私人測試集當測試集。

X_train, y_train = prepare_data(df_raw[df_raw['Usage'] == 'Training'])
X_val, y_val = prepare_data(df_raw[df_raw['Usage'] == 'PublicTest'])
X_test, y_test = prepare_data(df_raw[df_raw['Usage'] == 'PrivateTest'])

4. 一行指令比較各資料集大小

訓練、驗證和測試集的比例為8:1:1,非常經典~

df_raw['Usage'].value_counts()  # 8:1:1
# output:
# Training       28709
# PrivateTest     3589
# PublicTest      3589
# Name: Usage, dtype: int64

5. 觀察每一類的表情圖片

歷經了重重困難(大約15分鐘),
終於可以觀察到活生生的表情圖片了!

def plot_one_emotion(data, img_arrays, img_labels, label=0):
    fig, axs = plt.subplots(1, 7, figsize=(25, 12))
    fig.subplots_adjust(hspace=.2, wspace=.2)
    axs = axs.ravel()
    for i in range(7):
        idx = data[data['emotion'] == label].index[i]
        axs[i].imshow(img_arrays[idx][:, :, 0], cmap='gray')
        axs[i].set_title(emotions[img_labels[idx]])
        axs[i].set_xticklabels([])
        axs[i].set_yticklabels([])
        
emotions = {0: 'Angry', 1: 'Disgust', 2: 'Fear',
            3: 'Happy', 4: 'Sad', 5: 'Surprise', 6: 'Neutral'}
for label in emotions.keys():
    plot_one_emotion(df_train, X_train, y_train, label=label)

0
1
2
3
4
5
6


6. 觀察表情分布

def plot_distributions(img_labels_1, img_labels_2, title1='', title2=''):
    df_array1 = pd.DataFrame()
    df_array2 = pd.DataFrame()
    df_array1['emotion'] = img_labels_1
    df_array2['emotion'] = img_labels_2

    fig, axs = plt.subplots(1, 2, figsize=(12, 6), sharey=False)
    x = emotions.values()

    y = df_array1['emotion'].value_counts()
    keys_missed = list(set(emotions.keys()).difference(set(y.keys())))
    for key_missed in keys_missed:
        y[key_missed] = 0
    axs[0].bar(x, y.sort_index(), color='orange')
    axs[0].set_title(title1)
    axs[0].grid()

    y = df_array2['emotion'].value_counts()
    keys_missed = list(set(emotions.keys()).difference(set(y.keys())))
    for key_missed in keys_missed:
        y[key_missed] = 0
    axs[1].bar(x, y.sort_index())
    axs[1].set_title(title2)
    axs[1].grid()

    plt.show()
    
plot_distributions(
    y_train, y_val, title1='train labels', title2='val labels')

資料不平衡的問題苦惱著各個資料科學家,
今天我們發現disgust類別特別少,happy類別特別多。
可能是因為人類是一個友善的族群,
所以當時在蒐集照片時大多是笑臉吧!
表情分布


上一篇
[Day 02] 在表情資料尋找邂逅是否搞錯了甚麼 (Facial Expression Recognition)
下一篇
[Day 04] 眼前的黑不是黑,你說的白是什麼白?(直方圖均衡化)
系列文
AI Facial Expression Recognition: Data, Model, Application30

尚未有邦友留言

立即登入留言