2019年8月6日 星期二

使用Keras和OpenCV進行面部表情識別

0=Angry, 1=Disgust, 2=Fear, 3=Happy, 4=Sad, 5=Surprise, 6=Neutral
In [84]:
# display some images for every different expression
import numpy as np
import seaborn as sns
from keras.preprocessing.image import load_img, img_to_array
import matplotlib.pyplot as plt
import os
import pandas as pd
import sys
import cv2
from keras import models
from keras.utils import np_utils
from keras.callbacks import ModelCheckpoint, EarlyStopping

下載圖片

將下載的csv檔案轉換成圖片並分類

In [85]:
#訓練需要的圖片和label為csv形式,需要用以下程式碼將csv的內容轉變為圖片和label
data = np.genfromtxt('face_emition/fer2013/fer2013.csv',delimiter=',',dtype=None)
#第0欄為label,第1欄為image的string形式
labels = data[1:,0].astype(np.int32)
image_buffer = data[1:,1]
#把string形式的image變成uint8的格式,最後轉變為numpy array
images = np.array([np.fromstring(image, np.uint8, sep=' ') for image in image_buffer])
#usage為訓練或測試資料的標記
usage = data[1:,2]
#將labels, images, usages以zip的形式打包成一個個的tuple
dataset = zip(labels, images, usage)
#label的數字轉文字的翻譯
label_names = ['Angry', 'Disgust', 'Fear', 'Happy', 'Sad', 'Surprise', 'Neutral']
emotion_dict = dict((j,i) for i,j in enumerate(label_names))
inverse_emotion_dict = dict((i,j) for i,j in enumerate(label_names))
output_path = os.getcwd()
df=dataset
#以下for迴圈把image array儲存成.jpg的格式並且分別儲存在training/test下,且又依據不同的label分開存放
for i, d in enumerate(dataset):
    path = os.path.join(output_path, 'face_emition/data')
    usage_path = os.path.join(path, eval(str(d[-1]).lstrip('b')))
    img = d[1].reshape((48,48))
    label_path = os.path.join(usage_path, label_names[d[0]])
    if not os.path.isdir(usage_path):
        os.mkdir(usage_path)
    img_name = '%08d.jpg' % i
    img_path = os.path.join(label_path, img_name)
    if not os.path.isdir(label_path):
        os.mkdir(label_path)
    cv2.imwrite(img_path, img)

/Users/laihunglai/anaconda3/envs/py35/lib/python3.5/site-packages/ipykernel_launcher.py:2: VisibleDeprecationWarning: Reading unicode strings without specifying the encoding argument is deprecated. Set the encoding, use None for the system default.
  

將圖片從讀入資料架中讀入,label為資料夾名稱

In [86]:
output_path = os.getcwd()
path = os.path.join(output_path, 'face_emition/data')
data_list = os.listdir(path)
img_data=[]
emotion_data=[]
for i in data_list:
    file_path = os.path.join(path, i)
    file_list = os.listdir(file_path)
    for j in file_list:
        file_n_path = os.path.join(file_path, j)
        file_n_list = os.listdir(file_n_path)
        for k in file_n_list:
            figure_path = os.path.join(file_n_path,k)
            img = cv2.imread(figure_path)
            img_data.append(img)
            emotion_data.append(emotion_dict[j])

In [88]:
#顯示讀入幾筆訓練資料
print('total number of data:', len(img_data))
#將訓練資料轉成numpy array
emotion_data = np.array(emotion_data)
img_data = np.array(img_data)
print('shape of data:', img_data.shape)

total number of data: 35887
shape of data: (35887, 48, 48, 3)


In [89]:
#將data分成訓練和測試資料
mask = np.random.rand(len(emotion_data)) #隨機產生一個1D mask用來隨機篩選traning和test資料
trainX = img_data[mask>0.8]
trainY = emotion_data[mask>0.8]
testX = img_data[mask<0.2]
testY = emotion_data[mask<0.2]
#normalizetraning set的pixel到[0,1]
trainX_normalize=trainX/225
testX_normalize=testX/225

In [90]:
#label的資料預處理:利用One-hot enconding將0~6的數字轉換成7個0或1的組合,對應到7個神經元
trainY_OneHot=np_utils.to_categorical(trainY)
testY_OneHot=np_utils.to_categorical(testY)

In [91]:
from keras.models import Sequential
from keras.layers import Dense, Conv2D,MaxPooling2D,Dropout,Flatten
#建立Sequential模型
model=Sequential()
model.add(Conv2D(filters=8,kernel_size=(3,3),padding='same',input_shape=(48,48,3),activation='relu'))
model.add(MaxPooling2D(pool_size=(2,2)))
model.add(Conv2D(filters=16,kernel_size=(3,3),padding='same',activation='relu'))
model.add(MaxPooling2D(pool_size=(2,2)))
model.add(Dropout(0.25))
model.add(Flatten())
model.add(Dense(32,activation='relu'))
model.add(Dropout(0.5))
model.add(Dense(7,activation='softmax'))
model.summary()

_________________________________________________________________
Layer (type)                 Output Shape Param #   
=================================================================
conv2d_5 (Conv2D)            (None, 48, 48, 8) 224       
_________________________________________________________________
max_pooling2d_5 (MaxPooling2 (None, 24, 24, 8)         0         
_________________________________________________________________
conv2d_6 (Conv2D)            (None, 24, 24, 16) 1168      
_________________________________________________________________
max_pooling2d_6 (MaxPooling2 (None, 12, 12, 16)        0         
_________________________________________________________________
dropout_5 (Dropout)          (None, 12, 12, 16) 0         
_________________________________________________________________
flatten_3 (Flatten)          (None, 2304) 0         
_________________________________________________________________
dense_5 (Dense)              (None, 32) 73760     
_________________________________________________________________
dropout_6 (Dropout)          (None, 32) 0         
_________________________________________________________________
dense_6 (Dense)              (None, 7) 231       
=================================================================
Total params: 75,383
Trainable params: 75,383
Non-trainable params: 0
_________________________________________________________________


In [71]:
model.compile(loss='categorical_crossentropy',optimizer='adam',metrics=['accuracy'])
#進行訓練
es = EarlyStopping(monitor='val_loss', mode='min', verbose=0, patience=5)
mc = ModelCheckpoint('emotion.h5', monitor='val_loss', mode='min', verbose=2, save_best_only=True)
train_history=model.fit(x=trainX_normalize,y=trainY_OneHot, validation_split=0.2, epochs=50, batch_size=256, verbose=2, callbacks=[es, mc])

Train on 5716 samples, validate on 1430 samples
Epoch 1/50
 - 111s - loss: 1.8174 - acc: 0.2474 - val_loss: 2.3562 - val_acc: 0.1147


Epoch 00001: val_loss improved from inf to 2.35617, saving model to emotion.h5
Epoch 2/50
 - 85s - loss: 1.7631 - acc: 0.2724 - val_loss: 2.3993 - val_acc: 0.1147


Epoch 00002: val_loss did not improve from 2.35617
Epoch 3/50
 - 83s - loss: 1.7386 - acc: 0.2720 - val_loss: 2.4787 - val_acc: 0.1147


Epoch 00003: val_loss did not improve from 2.35617
Epoch 4/50
 - 91s - loss: 1.7025 - acc: 0.2953 - val_loss: 2.4697 - val_acc: 0.1175


Epoch 00004: val_loss did not improve from 2.35617
Epoch 5/50
 - 91s - loss: 1.6702 - acc: 0.3104 - val_loss: 2.5253 - val_acc: 0.1455


Epoch 00005: val_loss did not improve from 2.35617
Epoch 6/50
 - 99s - loss: 1.6370 - acc: 0.3188 - val_loss: 2.3106 - val_acc: 0.1580


Epoch 00006: val_loss improved from 2.35617 to 2.31061, saving model to emotion.h5
Epoch 7/50
 - 86s - loss: 1.6132 - acc: 0.3401 - val_loss: 2.6427 - val_acc: 0.1469


Epoch 00007: val_loss did not improve from 2.31061
Epoch 8/50
 - 83s - loss: 1.5911 - acc: 0.3606 - val_loss: 2.6162 - val_acc: 0.1671


Epoch 00008: val_loss did not improve from 2.31061
Epoch 9/50
 - 84s - loss: 1.5665 - acc: 0.3690 - val_loss: 2.5161 - val_acc: 0.1748


Epoch 00009: val_loss did not improve from 2.31061
Epoch 10/50
 - 71s - loss: 1.5497 - acc: 0.3796 - val_loss: 2.5572 - val_acc: 0.1797


Epoch 00010: val_loss did not improve from 2.31061
Epoch 11/50
 - 75s - loss: 1.5366 - acc: 0.3907 - val_loss: 2.6521 - val_acc: 0.1727


Epoch 00011: val_loss did not improve from 2.31061


在 Keras 中若要儲存與載入訓練好的模型或參數,可以使用其內建模型儲存與載入功能,將模型儲存於 HDF5(.h5) 或 JSON(.json) 檔案中,以下是 Keras 儲存模型的操作方式。
上面訓練結束自動儲存的emotion.h5內含模型和權重參數下次要使用時載入的指令如下
In [76]:
# 從 HDF5 檔案中載入模型
model = models.load_model('emotion.h5')
model.summary()

_________________________________________________________________
Layer (type)                 Output Shape Param #   
=================================================================
conv2d_3 (Conv2D)            (None, 48, 48, 8) 224       
_________________________________________________________________
max_pooling2d_3 (MaxPooling2 (None, 24, 24, 8)         0         
_________________________________________________________________
conv2d_4 (Conv2D)            (None, 24, 24, 16) 1168      
_________________________________________________________________
max_pooling2d_4 (MaxPooling2 (None, 12, 12, 16)        0         
_________________________________________________________________
dropout_3 (Dropout)          (None, 12, 12, 16) 0         
_________________________________________________________________
flatten_2 (Flatten)          (None, 2304) 0         
_________________________________________________________________
dense_3 (Dense)              (None, 32) 73760     
_________________________________________________________________
dropout_4 (Dropout)          (None, 32) 0         
_________________________________________________________________
dense_4 (Dense)              (None, 7) 231       
=================================================================
Total params: 75,383
Trainable params: 75,383
Non-trainable params: 0
_________________________________________________________________


以下為其他權重和模型儲存方式,這個案例沒有使用

如果只要將模型儲存起來,不儲存其中的參數,可以使用 to_json 或 to_yaml 將模型轉為 JSON 或 YAML 的文字資料,在自己儲存至檔案中:
In [73]:
# 將模型匯出至 JSON(不含參數)
json_emotion = model.to_json()
# 從 JSON 資料重建模型
model = models.model_from_json(json_emotion)
model.summary()

_________________________________________________________________
Layer (type)                 Output Shape Param #   
=================================================================
conv2d_3 (Conv2D)            (None, 48, 48, 8) 224       
_________________________________________________________________
max_pooling2d_3 (MaxPooling2 (None, 24, 24, 8)         0         
_________________________________________________________________
conv2d_4 (Conv2D)            (None, 24, 24, 16) 1168      
_________________________________________________________________
max_pooling2d_4 (MaxPooling2 (None, 12, 12, 16)        0         
_________________________________________________________________
dropout_3 (Dropout)          (None, 12, 12, 16) 0         
_________________________________________________________________
flatten_2 (Flatten)          (None, 2304) 0         
_________________________________________________________________
dense_3 (Dense)              (None, 32) 73760     
_________________________________________________________________
dropout_4 (Dropout)          (None, 32) 0         
_________________________________________________________________
dense_4 (Dense)              (None, 7) 231       
=================================================================
Total params: 75,383
Trainable params: 75,383
Non-trainable params: 0
_________________________________________________________________


若只想要儲存模型的參數(也就是 weights),不包含模型本身,可以使用 save_weights:
In [74]:
# 將參數儲存至 HDF5 檔案(不含模型)
model.save_weights('emotion_weights.h5')
# 從 HDF5 檔案載入參數(不含模型)
model.load_weights('emotion_weights.h5')

若要將儲存的參數載入至不同的模型中使用(模型不同,但有相同網路層,例如 fine-tuning 或 transfer-learning),可以加上 by_name 參數:
In [75]:
# 載入參數至不同的模型中使用
model.load_weights('emotion_weights.h5', by_name = True)

In [77]:
#視覺化訓練過程
import matplotlib.pyplot as plt
def show_train_history(train_history,train,validation):
    plt.plot(train_history.history[train])
    plt.plot(train_history.history[validation])
    plt.title('Train History')
    plt.ylabel(train)
    plt.xlabel('Epoch')
    plt.legend(['train','validation'],loc='upper left')
    plt.show()
show_train_history(train_history,'acc','val_acc')
#若訓練(train)的準確度一直增加而驗證(validation)的準確度沒有一直增加則可能是overfit
#畫出loss誤差執行結果
show_train_history(train_history,'loss','val_loss')
#測試model的準確度
score=model.evaluate(testX_normalize,testY_OneHot)
print('accuracy=',score[1])
#進行預測
prediction=model.predict_classes(testX_normalize)

7263/7263 [==============================] - 50s 7ms/step
accuracy= 0.3224562852815641


In [78]:
import matplotlib.pyplot as plt
def plot_images_labels_prediction(images,labels,prediction,idx,num=10):
    #images: 影像; labels: 答案; prediction: 預測結果; idx: 開始顯示的資料; num: 要顯示的資料筆數
    fig=plt.gcf()
    fig.set_size_inches(12,14)
    if num>25:num=25
    for i in range(0,num):
        ax=plt.subplot(5,5,1+i)
        ax.imshow(images[idx])
        title='label='+str(inverse_emotion_dict[labels[idx]])
        if len(prediction)>0:
            title+=",predict="+str(inverse_emotion_dict[prediction[idx]])
        ax.set_title(title,fontsize=8)
        ax.set_xticks([]);ax.set_yticks([])
        idx+=1
    plt.show()

In [79]:
#顯示前10筆預測結果, 顯示的200筆到209筆共20筆data
plot_images_labels_prediction(testX,testY,prediction,200,100)
#用pandas crosstab建立混淆矩陣
import pandas
import seaborn as sns
heat = pd.crosstab(testY, prediction,rownames=['label'],colnames=['prediction'])
sns.heatmap(heat)

Out[79]:
<matplotlib.axes._subplots.AxesSubplot at 0x1a7969c9b0>


對視訊鏡頭的影像進行表情辨識




import cv2
filename = '../../people.png'
face_cascade =cv2.CascadeClassifier('../../cascades/haarcascade_frontalface_default.xml')
img = cv2.imread(filename)
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
faces = face_cascade.detectMultiScale(gray, 1.3, 5)
for (x,y,w,h) in faces:
    img = cv2.rectangle(img,(x,y),(x+w,y+h),(255,0,0),2)
cv2.namedWindow('human Detected!!')
cv2.imshow('human Detected!!', img)
cv2.imwrite('./human.jpg', img)
cv2.waitKey(0)

In [ ]:
import cv2
from keras import models
import numpy as np
# 從 HDF5 檔案中載入模型
model = models.load_model('../../../emotion.h5')
label_names = ['Angry', 'Disgust', 'Fear', 'Happy', 'Sad', 'Surprise', 'Neutral']
inverse_emotion_dict = dict((i,j) for i,j in enumerate(label_names))


face_cascade =cv2.CascadeClassifier('../../cascades/haarcascade_frontalface_default.xml')
eye_cascade = cv2.CascadeClassifier('../../cascades/haarcascade_eye.xml')
camera = cv2.VideoCapture(1)
while (True):
    ret, frame = camera.read()
    gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
    color=frame
    faces = face_cascade.detectMultiScale(gray, 1.3, 5)
    for (x,y,w,h) in faces:
        img = cv2.rectangle(frame,(x,y),(x+w,y+h),(255,0,0),2)
        roi_gray = gray[y:y+h, x:x+w]
        roi_color = color[y:y+h, x:x+w]
        face_img=cv2.resize(roi_color,(48,48))
        face_img=face_img/255
        prediction=model.predict_classes(face_img[np.newaxis,:])
        emotion= [inverse_emotion_dict[i] for i in prediction]
        cv2.putText(img,emotion[0],(x,y-20),cv2.FONT_HERSHEY_SIMPLEX,1,255,2)
        eyes = eye_cascade.detectMultiScale(roi_gray, 1.03, 5, 0,(40,40))#最小搜尋眼睛的pixel size: 40*40 pixels
        for (ex,ey,ew,eh) in eyes:
            cv2.rectangle(img,(x+ex,y+ey),(x+ex+ew,y+ey+eh),(0,255,0),2)
    cv2.imshow("camera", frame)
    if cv2.waitKey(1000 // 12) & 0xff == ord("q"):
        break
camera.release()
cv2.destroyAllWindows()

/Users/laihunglai/anaconda3/envs/py35/lib/python3.5/site-packages/h5py/__init__.py:36: FutureWarning: Conversion of the second argument of issubdtype from `float` to `np.floating` is deprecated. In future, it will be treated as `np.float64 == np.dtype(float).type`.
  from ._conv import register_converters as _register_converters
Using TensorFlow backend.



三倍槓桿和一倍槓桿的長期定期定額報酬率分析

  以下是中國,美國股票債卷的三倍槓桿和一倍槓桿ETF分析.可以發現,三倍槓桿在下跌時期的跌幅遠比一倍槓桿的多 .且從時間軸來看,三倍槓桿由於下跌力道較強,因此會把之前的漲幅都吃掉,所以對於長期上身的市場,例如美國科技股,由於上升時間遠比下跌時間長,所以持有TQQQ的長期回報率會...