【深度學習】 HW3:Image Sentiment Classification

一、題目描述

題目描述在這裏:https://github.com/maplezzz/NTU_ML2017_Hung-yi-Lee_HW/tree/master/HW3

這個git項目裏不包括train.csv,所以我一開始在jupyter notebook裏運行data_analysis.ipynb的時候很奇怪爲什麼沒有文件卻可以寫入並打印出結果,然後自己重新運行了一遍就報錯了:

FileNotFoundError: [Errno 2] File b'data/train.csv' does not exist: b'data/train.csv'

這篇博客中可以找到train.csv:https://www.cnblogs.com/HL-space/p/10888556.html

下載過程是漫長的(辣雞百度雲,哎),中間我還下了一本網絡小說《有匪》哈哈。

第一行是index(這個在後面的數據處理需要注意),label描述表情屬於哪一類((0)生氣,(1)厭惡,(2)恐懼,(3)高興,(4)難過,(5)驚訝和(6)中立(即面無表情,無法歸爲前六類)),feature由48*48=2304個人臉圖像的像素值表示。

二、數據預處理

1、分割數據

導入fer2013.csv:

import pandas as pd
import os
infile = "fer2013.csv"
output_dir = "data\\"
rawData = pd.read_csv(infile,header=0)

#修改列名,不加inplace不會修改
rawData.rename(columns = {'emotion':'label','pixels':'feature'},inplace=True)

#統計訓練集、驗證集、測試集中的數據個數
num_train = 0
num_valid = 0
for index, row in rawData.iterrows():
    if row["Usage"] == 'Training':
        num_train += 1
    elif row["Usage"] == 'PublicTest':
        num_valid +=1
num_valid += num_train

#去掉Usage列
Data = rawData.drop(["Usage"], axis=1)

#劃分訓練集、驗證集和測試集
train = Data[:num_train]#左閉右開區間
valid = Data[num_train:num_valid]
test = Data[num_valid:]

if not os.path.exists(output_dir):
    os.mkdir(output_dir)
    
#\t表示製表符,index=False表示不添加索引
train.to_csv(os.path.join(output_dir+'train.csv'), index=False)
valid.to_csv(os.path.join(output_dir+'valid.csv'), index=False)
test.to_csv(os.path.join(output_dir+'test.csv'), index=False)

 2、testData

統計各個表情的樣本數,默認按從大到小排列:

import pandas as pd
import numpy as np
from matplotlib import pyplot as plt

name = ['angry','disgust', 'fear', 'happy', 'sad', 'surprise', 'neutral']

train_filename = "data/train.csv" 

train_df = pd.read_csv(train_filename)

train_df

train_df['label'].value_counts()

def plot_images(images, cls_true, cls_pred=None):
    assert len(images) == len(cls_true) == 9
    
    # Create figure with 3x3 sub-plots.創建子圖
    fig, axes = plt.subplots(3, 3)#axes子圖軸域
    fig.subplots_adjust(hspace=0.3, wspace=0.3)#調整邊距和子圖的間距

    for i, ax in enumerate(axes.flat):#flat方法是將整個numpy對象轉換成了1維對象
        # Get the i'th image and reshape the array.
        #image = images[i].reshape(img_shape)
        
        
        # Ensure the noisy pixel-values are between 0 and 1.
        #image = np.clip(image, 0.0, 1.0)

        # Plot image.
        ax.imshow(images[i],
                  cmap = 'gray',#顏色圖譜(colormap), 默認繪製爲RGB(A)顏色空間。
                  interpolation='nearest')#最近鄰差值

        # Show true and predicted classes.
        if cls_pred is None:#還沒有預測結果
            xlabel = "True: {0}".format(name[cls_true[i]])#字符串格式化
        else:
            xlabel = "True: {0}, Pred: {1}".format(name[cls_true[i]], name[cls_pred[i]])

        # Show the classes as the label on the x-axis.
        ax.set_xlabel(xlabel)#把xlabel在圖中標註出來
        
        # Remove ticks from the plot.
        ax.set_xticks([])#設置座標軸刻度
        ax.set_yticks([])
    
    # Ensure the plot is shown correctly with multiple plots
    # in a single Notebook cell.
    plt.show()

class clean_data(object):
    def __init__(self, filename):
        self._train_df = pd.read_csv(filename)#讀文件
        self._distribution = self._train_df['label'].value_counts()#統計各類表情數目
        #.map將函數作用在序列的每個元素上,然後創建由函數返回值組成的列表
        #.split通過指定分隔符對字符串進行切片
        #將feature(字符串)分割爲浮點型數值列表
        self._train_df['feature'] = self._train_df['feature'].map(lambda x : np.array(list(map(float, x.split()))))
        #獲取feature(image像素點)的size
        self._image_size = self._train_df.feature[0].size
        #獲取圖片大小
        self._image_shape = (int(np.sqrt(self._image_size)), int(np.sqrt(self._image_size)))
        #獲取樣本數目
        self._dataNum = self._train_df.size
        
        #feature矩陣
        self._feature = np.array(self._train_df.feature.map(lambda x: x.reshape(self._image_shape)).values.tolist())
        self._label = self._train_df.label.values
        self._labelNum = self._train_df['label'].unique().size

    #負責裝飾一個對象函數,讓某生成對應的setter和getter函數,調用的時候,直接可以使用對象名.函數名這種類似於屬性的調用方法來執行函數
    @property
    def distribution(self):
        return self._distribution
    
    @property
    def image_size(self):
        return self._image_size
    
    @property
    def image_shape(self):
        return self._image_shape
    
    @property
    def dataNum(self):
        return self._dataNum
    
    @property
    def feature(self):
        return self._feature
    
    @property
    def label(self):
        return self._label

    @property
    def labelNum(self):
        return self._labelNum
    
data = clean_data('data/train.csv')


data.labelNum

a = pd.read_csv('data/train.csv')['label']

#index() 函數用於從列表中找出某個值第一個匹配項的索引位置。
sample_images = [list(a[a == x].index)[0:9] for x in range(data.labelNum)]

for x in sample_images:
    plot_images(data.feature[x], data.label[x])

sample_images

其中用到的一些數據:

a是image的label(表情)列表:

sample_images是從每一類中挑選出的前九張image(的標號):

data.feature方法可以將大小爲2304的數據繪製成48*48的圖像:

df = pd.DataFrame([[1, 1, 1, 1], [2, 2, 2, 2], [3, 3, 3, 3]])
s = df.sum(axis=1)
s1 = s[:, np.newaxis]

二、CNN&DNN

1、訓練網絡

很奇怪tensorBoard報錯說文件路徑不存在,提示說好像可以換成summaryfile,我也不會修改,於是就把生成tensorboard註釋掉了。

tensorflow.python.framework.errors_impl.NotFoundError: Failed to create a directory: logs/1570693883
from utility import clean_data, plot_training_history

import pandas as pd
import numpy as np
from time import time
from matplotlib import pyplot as plt

import tensorflow as tf
from tensorflow.python.keras.models import Sequential
from tensorflow.python.keras.layers import InputLayer, Input
from tensorflow.python.keras.layers import Reshape, MaxPooling2D
from tensorflow.python.keras.layers import Conv2D, Dense, Flatten, Dropout, Activation
from tensorflow.python.keras.layers.normalization import BatchNormalization
from tensorflow.python.keras.callbacks import TensorBoard
from tensorflow.python.keras.models import Model

from sklearn.utils.class_weight import compute_class_weight


name = ['angry','disgust', 'fear', 'happy', 'sad', 'surprise', 'neutral']

train_data = clean_data('data//train.csv')
test_data = clean_data('data//test.csv', False)

train = train_data.feature.reshape((-1, 48, 48, 1))/255
train_x = train[:-2000]
train_label = train_data.label[:-2000]
train_onehot = train_data.onehot[:-2000]
test_x = train[-2000:]
test_label = train_data.label[-2000:]
test_onehot = train_data.onehot[-2000:]


class_weight = compute_class_weight(class_weight='balanced',
                                    classes=np.unique(train_data.label),
                                    y=train_data.label)


#CNN model

inputs = Input(shape=(48,48,1))
'''
# First convolutional layer with ReLU-activation and max-pooling.
net = Conv2D(kernel_size=5, strides=1, filters=64, padding='same',
             activation='relu', name='layer_conv1')(inputs)
net = MaxPooling2D(pool_size=2, strides=2)(net)
net = BatchNormalization(axis = -1)(net)
net = Dropout(0.25)(net)

# Second convolutional layer with ReLU-activation and max-pooling.
net = Conv2D(kernel_size=5, strides=1, filters=128, padding='same',
             activation='relu', name='layer_conv2')(net)
net = MaxPooling2D(pool_size=2, strides=2)(net)
net = BatchNormalization(axis = -1)(net)
net = Dropout(0.25)(net)

# Third convolutional layer with ReLU-activation and max-pooling.
net = Conv2D(kernel_size=5, strides=1, filters=256, padding='same',
             activation='relu', name='layer_conv3')(net)
net = MaxPooling2D(pool_size=2, strides=2)(net)
net = BatchNormalization(axis = -1)(net)
net = Dropout(0.5)(net)

# Flatten the output of the conv-layer from 4-dim to 2-dim.
net = Flatten()(net)

# First fully-connected / dense layer with ReLU-activation.
net = Dense(128)(net)
net = BatchNormalization(axis = -1)(net)
net = Activation('relu')(net)

# Last fully-connected / dense layer with softmax-activation
# so it can be used for classification.
net = Dense(7)(net)
net = BatchNormalization(axis = -1)(net)
net = Activation('softmax')(net)
# Output of the Neural Network.
outputs = net

model = Model(inputs=inputs, outputs=outputs)
tensorboard = TensorBoard(log_dir='logs/{}'.format(time()))
model.compile(optimizer='Adam',
               loss='categorical_crossentropy',
               metrics=['accuracy'])

y = model.fit(x=train_x,
           y=train_onehot,
           validation_data=(test_x, test_onehot),
           class_weight=class_weight,
           epochs=100, batch_size=64,
           callbacks=[tensorboard]
             )

plot_training_history(y)
#
model.save('cnn.h5')

#DNN model
'''
inputs = Input(shape=(48,48,1))

dnn = Flatten()(inputs)

dnn = Dense(512)(dnn)
dnn = BatchNormalization(axis = -1)(dnn)
dnn = Activation('relu')(dnn)
dnn = Dropout(0.25)(dnn)

dnn = Dense(1024)(dnn)
dnn = BatchNormalization(axis = -1)(dnn)
dnn = Activation('relu')(dnn)
dnn = Dropout(0.5)(dnn)

dnn = Dense(512)(dnn)
dnn = BatchNormalization(axis = -1)(dnn)
dnn = Activation('relu')(dnn)
dnn = Dropout(0.5)(dnn)

dnn = Dense(7)(dnn)
dnn = BatchNormalization(axis = -1)(dnn)
dnn = Activation('softmax')(dnn)

outputs = dnn

#writer=tf.summary.FileWriter("logs",tf.get_default_graph())
#writer.close()

model2 = Model(inputs=inputs, outputs=outputs)
#tensorboard = TensorBoard(log_dir="logs/{}".format(time()))
model2.compile(optimizer='Adam',
               loss='categorical_crossentropy',
               metrics=['accuracy'])

d = model2.fit(x=train_x,
           y=train_onehot,
           validation_data=(test_x, test_onehot),
           class_weight=class_weight,
           epochs=100, batch_size=64,
           #callbacks=[tensorboard]
             )

plot_training_history(d)
#model2.save('dnn.h5')

 

比較

在可訓練參數數目相差不多的情況下, dnn每一個epoch的訓練時間約爲4s, 遠快於cnn的19s, 但是在同樣做了BN以及droupout的情況下, cnn的訓練效果遠好於dnn。

在驗證集爲數據集最後2000筆的情況下, cnn在驗證集的正確率達到了60%, 而dnn只有40%, 證明了課上的理論, cnn能夠更高效的利用每一個參數。

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章