# CNN手勢識別
# 1.問題描述
使用CNN,識別如下三種不同的手勢:

提供的數據集:
共提供五個數據集,其中Set1、Set2、Set3為訓練集,Set4和Set5位測試集。
每個數據集中包含0000-0008九個文件夾,0000-0002,0003-0005,0006-0008分別為手勢一、手勢二和手勢三。每個文件夾中有0000-0019共二十張32x32像素的灰階圖。
# 2.数据预处理
## ①定義得到訓練集和測試集所有圖片的path和label的function
首先要得到数据,通过读取五個數據集內的圖片,先統一獲取到測試集和訓練集,稍後再將訓練集和測試集分開。
```python=
# 得到训练集和测试集的所有图片path和label
import cv2
import numpy as np
import os
def enumerate_files(base_path = 'C:/Users/NOTEBOOK/DeepLearningNote/All_gray_1_32_32'):
filenames,labels = [],[]
for file1 in os.listdir(base_path):#得到根目录的下一级目录,内有“Set1”...
for file2 in os.listdir(base_path+'/'+file1):#得到set1到set5的0000-0008
for file3 in os.listdir(base_path+'/'+file1+'/'+file2):#得到所有的0000-0019
for file4 in os.listdir(base_path+'/'+file1+'/'+file2+'/'+file3):#得到所有的图片
filenames += [base_path+'/'+file1+'/'+file2+'/'+file3+'/'+file4]
#将三种手势分别对应0,1,2
if(file2 in ['0000','0001','0002']):
labels += [0]
if(file2 in ['0003','0004','0005']):
labels += [1]
if(file2 in ['0006','0007','0008']):
labels += [2]
return filenames,labels
```
## ②定義將圖片轉換為神經網絡可以處理的array的function
需要注意的是,在我的這種將圖片轉換為array的時候,`img_to_array`方法產生的結果是一個三通道的彩色圖片結果,由於我們數據為灰階圖,所以三個通道的數值會是一樣的,在此我只保留RGB通道的index=0的通道。
```python=
#将enumerate_files方法获取到得图片的path,转换为array
from keras.preprocessing.image import img_to_array, load_img
def read_images(images):
imgs = []
for image in images:
img = load_img(image)
img = img_to_array(img)
img = img[:,:,0]/255.0#由于img_to_array方法得到的通道数是3,是彩色图,所以最后会有三个通道,我们只取三个中的一个(三个颜色的通道内容相同)
imgs.append(img)
return imgs
```
## ③利用前面兩個function,得到images和labels,並且劃分為train_data,test_data
原始的數據集中,訓練集為Set1、Set2、Set3,測試集為Set4和Set5,所以我們按照順序得到的所有圖片前540張為訓練集,540之後為測試集。
```python=
#使用enumerate_files(),read_images()将图片转换为array,以及每张图片对应的label标签
from keras.utils import to_categorical
filenames,labels = enumerate_files()
images = read_images(filenames)
images = np.array(images)#这个时候,images.shape = 900,32,32
images = images.reshape((900,32,32,1))
labels = to_categorical(labels)
train_images = images[:540]
train_labels = labels[:540]
test_images = images[540:]
test_labels = labels[540:]
print(test_labels.shape)
```
# 3.建立模型
因為要做兩組判斷,一組用數據增強,一組不用數據增強,在此我們使用一個function來構造model
```python=
#建立構造model的function
#建立模型的function
from keras import models,layers
def createModel():
model = models.Sequential()
model.add(layers.Conv2D(32,(2,2),activation='relu',input_shape=(32,32,1)))
model.add(layers.MaxPooling2D((2,2)))
model.add(layers.Conv2D(64,(2,2),activation='relu'))
model.add(layers.MaxPooling2D((2,2)))
model.add(layers.Conv2D(128,(2,2),activation='relu'))
model.add(layers.MaxPooling2D((2,2)))
model.add(layers.Conv2D(128,(2,2),activation='relu'))
model.add(layers.MaxPooling2D((2,2)))
model.add(layers.Flatten())
# model.add(layers.Dropout(0.3))
model.add(layers.Dense(512,activation='relu'))
model.add(layers.Dense(3,activation='softmax'))
model.compile(optimizer='rmsprop',
loss='categorical_crossentropy',
metrics=['accuracy'])
return model
```
# 4.訓練模型
## ①打亂數據集順序
由於原始數據集是存在一定的規律順序,先是手勢1,然後手勢2,然後手勢3,所以在訓練模型之前,我們要先將train_data的順序打亂
```python=
# 简单分类之前要先打乱一下
shuffle_index= np.arange(train_images.shape[0])
np.random.shuffle(shuffle_index)
train_images = train_images[shuffle_index]
train_labels = train_labels[shuffle_index]
print(shuffle_index)
```
## ②劃分出驗證集
得到了打亂的訓練集後,再劃分出驗證集validation_data,在本測試中,選取了validation的長度為100
```python=
#划分出validation集和train集
validation_images = train_images[:100]
validation_labels = train_labels[:100]
images = train_images[100:]
labels = train_labels[100:]
```
## ③不使用數據增強來訓練模型
```python=
model = createModel()
history = model.fit(images,labels,epochs=40,batch_size=32,validation_data=(validation_images,validation_labels))
```
## ④使用數據增強來訓練模型
```python=
#使用数据增强
from keras.preprocessing.image import ImageDataGenerator,img_to_array
datagen = ImageDataGenerator(
rotation_range=40,
width_shift_range=0.2,
height_shift_range=0.2,
shear_range=0.2,
zoom_range=0.2,
horizontal_flip=True,
fill_mode='nearest')
model1 = createModel()
history1 = model1.fit_generator(datagen.flow(images,labels,batch_size=32),
steps_per_epoch=len(train_images)/32,epochs = 40,validation_data=(validation_images,validation_labels))
```
# 5.打印訓練過程
“<font color='Blue'>藍色</font>”為<font color='Blue'>不使用數據增強</font>的訓練過程圖
“<font color='Red'>紅色</font>”為<font color="Red">使用數據增強</font>的訓練過程圖
```python=
import matplotlib.pyplot as plt
acc = histroy.history["accuracy"]
loss = histroy.history['loss']
epochs = range(1,len(acc)+1)
val_acc = histroy.history["val_accuracy"]
val_loss = histroy.history["val_loss"]
plt.plot(epochs,acc,'bo',label="Training acc")
plt.plot(epochs,val_acc,'b',label="Validation acc")
plt.legend()
plt.show()
```

```python=
plt.plot(epochs,loss,'bo',label="Training loss")
plt.plot(epochs,val_loss,'b',label="Validation loss")
plt.legend()
plt.show()
```

```python=
import matplotlib.pyplot as plt
acc = history1.history["accuracy"]
loss = history1.history['loss']
epochs = range(1,len(acc)+1)
val_acc = history1.history["val_accuracy"]
val_loss = history1.history["val_loss"]
plt.plot(epochs,acc,'ro',label="Training acc")
plt.plot(epochs,val_acc,'r',label="Validation acc")
plt.legend()
plt.show()
```

```python=
plt.plot(epochs,loss,'ro',label="Training loss")
plt.plot(epochs,val_loss,'r',label="Validation loss")
plt.legend()
plt.show()
```

# 6.評估模型
<font color="Blue">不使用數據增強</font>
```python=
loss_and_metries = model.evaluate(test_images,test_labels)
print(loss_and_metries)
```
評估結果如下:
```
360/360 [==============================] - 0s 260us/step
[0.09706378592074745, 0.9638888835906982]
```
<font color="Red">使用數據增強</font>
```python=
result = model1.evaluate(test_images,test_labels)
print(result)
```
評估結果如下:
```
360/360 [==============================] - 0s 255us/step
[0.11562680713379653, 0.9638888835906982]
```
兩個方法相差較少,可能以為這三個手勢的識別較為簡單,不使用數據增強也能得到較好的結果
# 7.總結
此神經網絡為第一次使用keras實踐,在過程中出現很多小問題,如:
1.最初忘記了打亂一下訓練集(由於原始數據是有規律的,也導致後續劃分的validation有問題),所以出現validation_acc和validation_loss的圖像震蕩幅度很大,acc和loss在0和1之間來回震蕩。
2.得到了灰階圖的張量化表示後,要注意再增加一個維度,使符合神經網絡的輸入規格