百度飛槳(3)—— 手勢識別

前言

本節學習卷積神經網絡,然後使用經典的LENET-5神經網絡訓練手勢識別。
教程地址:視屏地址

課程筆記

深度學習模型:
建模——>損失函數——>參數學習

全連接神經網絡的不足:

  • 模型不夠靈活,輸入圖片尺寸變換時網絡需要修改
  • 模型參數太多

卷積神經網絡特點:

  • 局部連接
  • 權值共享
  • 降採樣

LENET-5網絡模型在這裏插入圖片描述

本節主要根據這張圖設計LENET5模型並編寫paddle代碼。

首先看下卷積函數的定義源碼:

    def __init__(self,
                 num_channels,
                 num_filters,
                 filter_size,
                 stride=1,
                 padding=0,
                 dilation=1,
                 groups=None,
                 param_attr=None,
                 bias_attr=None,
                 use_cudnn=True,
                 act=None,
                 dtype='float32'):
  • num_channels:通道數
  • num_filters:卷積核數
  • filter_size:卷積核大小,卷積核一般是正方形的所以就一個邊長參數
  • stride:卷積步長

卷積步長參照下圖,藍色方格每次向右移動兩列或向下移動兩行表示步長爲2:
在這裏插入圖片描述

分析LENET-5網絡的input層

Yann LeCun教授的輸入是灰度的圖片單通道的,大小32 × 32。所以他的輸入是32 × 32 × 1,但是我們用的手勢數據是彩色的三通道的,所以我們需要做一點修改並且把輸入的圖片resize成32 × 32 × 3,resize代碼:

images = np.array([x[0].reshape(3, 32, 32) for x in data], np.float32)

分析LENET-5網絡的C1層

在這裏插入圖片描述

  • 卷積核大小:5 * 5
  • 卷積核數目:6
  • 卷積步長:1
  • 輸入通道數: 3

這裏要注意的就是卷積核數目,一個卷積核會對應圖片的一種特徵,n個卷積核就能得出n個特徵,n個輸出的特徵就是下一層的通道個數。
這裏我們說了採用的是彩色圖片所以我們的input是32 * 32 * 3的,也就是C1層的輸入是3通道的,C1層卷積核個數是6個,卷積核尺寸是5,步長是1所以得出paddle源碼:

self.c1 = Conv2D(3, 6, 5, 1)

分析LENET-5網絡的S2層池化層

在這裏插入圖片描述

  • 池化大小:2 × 2
  • 池化步長:2

paddle源碼:

self.s2 = Pool2D(pool_size=2, pool_type='max', pool_stride=2)

分析LENET-5網絡的C3層

在這裏插入圖片描述

  • 卷積核大小:5 * 5
  • 卷積核數目:16
  • 卷積步長:1
  • 輸入通道數:6

paddle源碼:

self.c3 = Conv2D(6, 16, 5, 1)

分析LENET-5網絡的S4層池化層

在這裏插入圖片描述

  • 池化大小:2 × 2
  • 池化步長:2

paddle源碼:

self.s4 = Pool2D(pool_size=2, pool_type='max', pool_stride=2)

分析LENET-5網絡的C5層

在這裏插入圖片描述

  • 卷積核大小:5 * 5
  • 卷積核數目:120
  • 卷積步長:1
  • 輸入通道數:16

paddle源碼:

self.c5 = Conv2D(16, 120, 5, 1)

分析LENET-5網絡的F6層

在這裏插入圖片描述

  • 全連接層輸入:120
  • 全連接層輸出:84

paddle源碼:

self.f6 = Linear(120, 84, act='relu')

分析LENET-5網絡的OUTPUT層

在這裏插入圖片描述

  • 全連接層輸入:84
  • 全連接層輸出:0

paddle源碼:

self.f7 = Linear(84, 10, act='softmax')

前向傳播模型構建

    def forward(self, input):
        print("input shape : " + str(input.shape))
        x = self.c1(input)
        print("C1 : " + str(x.shape))
        x = self.s2(x)
        print("S2 : " + str(x.shape))
        x = self.c3(x)
        print("C3 : " + str(x.shape))
        x = self.s4(x)
        print("S4 : " + str(x.shape))
        x = self.c5(x)
        print("C5 : " + str(x.shape))
        x = fluid.layers.reshape(x, shape=[-1, 120])
        x = self.f6(x)
        y = self.f7(x)
        return y

重點在這裏:

x = fluid.layers.reshape(x, shape=[-1, 120])

在卷積層和全連接層間有一個過度,C5層的輸出是120 × 1 × 1的,相當於是一維的,所以這裏做了一個reshape,reshape中的-1參數表示設置爲一維,120就是輸入的通道數。

訓練結果

在這裏插入圖片描述

識別結果

在這裏插入圖片描述

完整訓練代碼

gestureTrain.py代碼:

import os
import time
import random
import numpy as np
from PIL import Image
import matplotlib.pyplot as plt
import paddle
import paddle.fluid as fluid
import paddle.fluid.layers as layers
from multiprocessing import cpu_count
from paddle.fluid.dygraph import Pool2D,Conv2D
from paddle.fluid.dygraph import Linear


def makeListFile(data_path):
    # 生成圖像列表
    #data_path = '/home/xmy/PycharmProjects/test/paddle/data/gesture'
    # 返回指定的文件夾包含的文件或文件夾的名字的列表。
    character_folders = os.listdir(data_path)
    # 刪除存在的train_data.list和test_data.list
    if (os.path.exists('./train_data.list')):
        os.remove('./train_data.list')
    if (os.path.exists('./test_data.list')):
        os.remove('./test_data.list')

    # 遍歷所有的folder
    for character_folder in character_folders:
        # 寫入train_data
        with open('./train_data.list', 'a') as f_train:
            with open('./test_data.list', 'a') as f_test:
                # 遍歷目錄下的所有圖片文件
                character_imgs = os.listdir(os.path.join(data_path, character_folder))
                # 用count圖片分類
                count = 0
                for img in character_imgs:
                    # 這裏是9:1的設置爲訓練集和測試集
                    if count % 10 == 0:
                        f_test.write(os.path.join(data_path, character_folder, img) + '\t' + character_folder + '\n')
                    else:
                        f_train.write(os.path.join(data_path, character_folder, img) + '\t' + character_folder + '\n')
                    count += 1
    print('列表已生成')


# 對圖片進行預處理
def data_mapper(sample):
    img, label = sample
    img = Image.open(img)
    img = img.resize((32, 32), Image.ANTIALIAS)
    img = np.array(img).astype('float32')
    # 將讀出來的rgb,rgb,rgb......轉換成rrr......ggg......bbb
    img = img.transpose((2, 0, 1))
    # 對圖片歸一化
    img = img / 255.0
    return img, label


def data_reader(data_list_path):
    def reader():
        with open(data_list_path, 'r') as f:
            lines = f.readlines()
            for line in lines:
                img, label = line.split('\t')
                yield img, int(label)

    return paddle.reader.xmap_readers(data_mapper, reader, cpu_count(), 512)



#定義DNN網絡
# class MyDNN(fluid.dygraph.Layer):
#     def __init__(self):
#         super(MyDNN,self).__init__()
#         self.hidden1 = Linear(100,100,act='tanh')
#         self.hidden2 = Linear(100,100,act='tanh')
#         self.hidden3 = Linear(100,100,act='tanh')
#         # 10是輸出10類,3 × 100 × 100是做了個拉伸但是這樣會跟hidden3的輸出不匹配所以在前向傳播的時候做了reshape
#         self.hidden4 = Linear(3*100*100,10,act='softmax')
#     def forward(self,input):
#         x = self.hidden1(input)
#         x = self.hidden2(x)
#         x = self.hidden3(x)
#         # 拉伸變換。這裏搞不明白爲什麼要拉伸成3 × 100 × 100
#         x = fluid.layers.reshape(x,shape=[-1,3*100*100])
#         y = self.hidden4(x)
#         return y

# 定義網絡
class MyLeNet(fluid.dygraph.Layer):
    def __init__(self):
        super(MyLeNet, self).__init__()
        self.c1 = Conv2D(3, 6, 5, 1)
        self.s2 = Pool2D(pool_size=2, pool_type='max', pool_stride=2)
        self.c3 = Conv2D(6, 16, 5, 1)
        self.s4 = Pool2D(pool_size=2, pool_type='max', pool_stride=2)
        self.c5 = Conv2D(16, 120, 5, 1)
        self.f6 = Linear(120, 84, act='relu')
        self.f7 = Linear(84, 10, act='softmax')


    def forward(self, input):
        print("input shape : " + str(input.shape))
        x = self.c1(input)
        print("C1 : " + str(x.shape))
        x = self.s2(x)
        print("S2 : " + str(x.shape))
        x = self.c3(x)
        print("C3 : " + str(x.shape))
        x = self.s4(x)
        print("S4 : " + str(x.shape))
        x = self.c5(x)
        print("C5 : " + str(x.shape))
        x = fluid.layers.reshape(x, shape=[-1, 120])
        # print(x.shape)
        x = self.f6(x)
        y = self.f7(x)
        return y



if __name__ == '__main__':
    data_path = '/home/xmy/PycharmProjects/test/paddle/data/gesture'
    makeListFile(data_path)

    # 用於訓練的數據提供器,buf_size越大越亂序
    train_reader = paddle.batch(reader=paddle.reader.shuffle(reader=data_reader('./train_data.list'), buf_size=256),
                                batch_size=32)
    # 用於測試的數據提供器
    test_reader = paddle.batch(reader=data_reader('./test_data.list'), batch_size=32)

    # 用動態圖進行訓練
    with fluid.dygraph.guard():
        model = MyLeNet()  # 模型實例化
        model.train()  # 訓練模式
        opt = fluid.optimizer.SGDOptimizer(learning_rate=0.01,
                                           parameter_list=model.parameters())  # 優化器選用SGD隨機梯度下降,學習率爲0.001.

        epochs_num = 50  # 迭代次數

        for pass_num in range(epochs_num):

            for batch_id, data in enumerate(train_reader()):
                # 將圖片大小處理成3 * 32 × 32的爲了與lenet相同
                images = np.array([x[0].reshape(3, 32, 32) for x in data], np.float32)

                labels = np.array([x[1] for x in data]).astype('int64')
                # 給labels升維度
                labels = labels[:, np.newaxis]
                # print(images.shape)
                image = fluid.dygraph.to_variable(images)
                label = fluid.dygraph.to_variable(labels)
                predict = model(image)  # 預測
                # print(predict)
                loss = fluid.layers.cross_entropy(predict, label)
                avg_loss = fluid.layers.mean(loss)  # 獲取loss值

                acc = fluid.layers.accuracy(predict, label)  # 計算精度

                if batch_id != 0 and batch_id % 50 == 0:
                    print("train_pass:{},batch_id:{},train_loss:{},train_acc:{}".format(pass_num, batch_id,
                                                                                        avg_loss.numpy(), acc.numpy()))

                avg_loss.backward()
                opt.minimize(avg_loss)
                model.clear_gradients()

        fluid.save_dygraph(model.state_dict(), 'MyLeNet')  # 保存模型

    # 模型校驗
    with fluid.dygraph.guard():
        accs = []
        model_dict, _ = fluid.load_dygraph('MyLeNet')
        model = MyLeNet()
        model.load_dict(model_dict)  # 加載模型參數
        model.eval()  # 訓練模式
        for batch_id, data in enumerate(test_reader()):  # 測試集
            images = np.array([x[0].reshape(3, 32, 32) for x in data], np.float32)
            labels = np.array([x[1] for x in data]).astype('int64')
            labels = labels[:, np.newaxis]

            image = fluid.dygraph.to_variable(images)
            label = fluid.dygraph.to_variable(labels)

            predict = model(image)
            acc = fluid.layers.accuracy(predict, label)
            accs.append(acc.numpy()[0])
            avg_acc = np.mean(accs)
        print(avg_acc)

完整模型使用代碼

gestureRecongnition.py

import numpy as np
from PIL import Image
import paddle.fluid as fluid
import cv2
from gestureTrain import MyLeNet

#讀取預測圖像,進行預測
def load_image(path):
    img = Image.open(path)
    img = img.resize((32, 32), Image.ANTIALIAS)
    img = np.array(img).astype('float32')
    img = img.transpose((2, 0, 1))
    img = img/255.0
    print(img.shape)
    return img

#構建預測動態圖過程
with fluid.dygraph.guard():
    infer_path = '/home/xmy/PycharmProjects/test/paddle/proj1_gestureRecongnize/手勢.JPG'
    model=MyLeNet()#模型實例化
    model_dict,_=fluid.load_dygraph('/home/xmy/PycharmProjects/test/paddle/proj1_gestureRecongnize/MyLeNet')
    model.load_dict(model_dict)#加載模型參數
    model.eval()#評估模式
    infer_img = load_image(infer_path)
    infer_img=np.array(infer_img).astype('float32')
    infer_img=infer_img[np.newaxis,:, : ,:]
    infer_img = fluid.dygraph.to_variable(infer_img)
    result=model(infer_img)
    cv2.imshow("手勢",cv2.imread(infer_path))
    print(np.argmax(result.numpy()))
    cv2.waitKey(1000)

完整工程和數據集下載

csdn下載鏈接

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