Day02 手势识别

# 查看当前挂载的数据集目录, 该目录下的变更重启环境后会自动还原

!ls /home/aistudio/data

# 查看工作区文件, 该目录下的变更将会持久保存. 请及时清理不必要的文件, 避免加载过慢.

!ls /home/aistudio/work

实践任务:手势识别

主要步骤

1.准备数据

2.配置网络

3.训练网络

4.模型评估

5.模型预测

!cd /home/aistudio/data/data23668 && unzip -qo Dataset.zip #手势识别数据集下载地址,下载后进行解压
!cd /home/aistudio/data/data23668/Dataset.zip && !rm -f */.DS_Store # 删除无关文件 

#解压指令:!cd 压缩包所在文件夹 && unzip -qo 名称.zip
#导入所需的包
import os
import numpy as np
from PIL import Image
import paddle
import paddle.fluid as fluid
import paddle.fluid.layers as layers #Fluid.layers包含了配置网络所需的丰富API
from multiprocessing import cpu_count  #查看当前机器cpu核心数量
from paddle.fluid.dygraph import Pool2D,Conv2D  #飞桨卷积算子对应的API是paddle.fluid.dygraph.nn.Conv2D,池化算子是...
from paddle.fluid.dygraph import Linear  #全连接层

注:

Conv2D

该函数用于二维卷积,有以下参数 - num_channels 输入通道数 - num_filters 滤波器个数 - filter_size 卷积核大小 - stride 步长大小 - padding 填充大小 - dilation 膨胀系数 - groups 分组数 - act 激活函数形式,比如act='relu',表示卷积操作后接一个relu激活,卷积核也称为滤波器

from paddle.fluid.dygraph import Conv2D,
conv = Conv2D(input_channel, output_channel, filter_size=3, stride=1)

BatchNorm

这是批量归一化层,paddle中bn层的使用与其他框架不太一样,它需要提供前面tensor的通道数 有以下常用的参数 - num_channels 需要归一化的Tensor通道数目,不一致会报错 - act 应用激活函数,跟前面一样,通常都是在BN后接激活函数,这样写比较方便 - momentum 用于指数移动平均估算均值和方差,默认值是0.9

from paddle.fluid.dygraph import BatchNorm
bn1 = BatchNorm(input_channel, act='relu')

Pool2D

这是二维池化层 其参数有以下 - pool_size 池化核大小 - pool_type 池化方式,有'avg', 'max',分别对应平均池化,最大池化 - pool_stride 池化步长大小 - pool_padding 池化填充大小 - global_pooling 是否全局池化,当为True,则忽略掉pool_size, pool_padding这两个参数,直接做一个全局池化

from paddle.fluid.dygraph import Pool2D
# 大小为3的池化层
pool = Pool2D(pool_size=3,  pool_type='avg', pool_stride=1, global_pooling=False)
# 全局平均池化
pool2 = Pool2D(pool_type='avg', global_pooling=True)

准备数据 网上公开的手势图像数据集:

包含0-9共9种数字手势,共2073张手势图片

图片为3*100*100,RGB格式文件

本次实验中,取其中的10%作为测试集,90%作为训练集

遍历图片,根据文件夹名称,生成label

按1:9比例划分测试集和训练集,生成train_list 和 test_list

# 生成图像列表
data_path = '/home/aistudio/data/data23668/Dataset'
character_folders = os.listdir(data_path) #os.listdir() 方法用于返回指定的文件夹包含的文件或文件夹的名字的列表。


# print(character_folders)
if(os.path.exists('./train_data.txt')):  #判断当前路径下文件是否存在
    os.remove('./train_data.txt')  #删除指定路径下的文件
if(os.path.exists('./test_data.txt')):
    os.remove('./test_data.txt')
    
for character_folder in character_folders:
    
    with open('./train_data.txt', 'a') as f_train:  #以追加方式打开文件
        with open('./test_data.txt', 'a') as f_test:
            if character_folder == '.DS_Store':
                continue
            character_imgs = os.listdir(os.path.join(data_path,character_folder))
            count = 0 
            for img in character_imgs:
                if img == '.DS_Store':
                    continue
                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('列表已生成')

注:

os.path.join()函数:连接两个或更多的路径名组件

1.如果各组件名首字母不包含’/’,则函数会自动加上

2.第一个以”/”开头的参数开始拼接,之前的参数全部丢弃,当有多个时,从最后一个开始

3.如果最后一个组件为空,则生成的路径以一个’/’分隔符结尾
 

data_mapper(): 读取图片,对图片进行归一化处理,返回图片和 标签。

data_reader(): 按照train_list和test_list批量化读取图片。

train_reader(): 用于训练的数据提供器,乱序、按批次提供数据

test_reader():用于测试的数据提供器

# 定义data_reader
def data_mapper(sample):
    img, label = sample
    img = Image.open(img) 从路径中读取图片并返回一个PIL对象
    img = img.resize((100, 100), Image.ANTIALIAS) #图片是3*100*100
    img = np.array(img).astype('float32')
    img = img.transpose((2, 0, 1))     #读出来的图像是rgb,rgb,rbg..., 转置为 rrr...,ggg...,bbb...(分开颜色通道)
    img = img/255.0  #归一化到[0,1]
    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') #split() 方法可以实现将一个字符串
#按照指定的分隔符切分成多个子串,这些子串会被保存到列表中(不包含分隔符),作为方法的返回值反馈回来。
                yield img, int(label) #生成器 返回图像地址和标签
    return paddle.reader.xmap_readers(data_mapper, reader, cpu_count(), 512)# 512是buffed_size

注:

  1. 调用一次np.transpose函数,即np.transpose(npimg,(1,2,0)),将npimg的数据格式由(channels,imagesize,imagesize)转化为(imagesize,imagesize,channels)。
  2. 写data_reader函数def data_loader(*):
    def data_loader(*):
    	...
        def reader(*):
      	...
        return reader
    

    通过使用飞桨提供的API paddle.reader.xmap_readers可以开启多线程读取数据。

# 用于训练的数据提供器
train_reader = paddle.batch(reader=paddle.reader.shuffle(reader=data_reader('./train_data.txt'), buf_size=256), batch_size=32)
# 用于测试的数据提供器
test_reader = paddle.batch(reader=data_reader('./test_data.txt'), batch_size=32) 

#定义DNN网络
class MyDNN(fluid.dygraph.Layer):
    def __init__(self):
        super(MyDNN,self).__init__() #继承父类的_init_()方法
        self.hidden1 = Linear(100,100,act='relu')
        self.hidden2 = Linear(100,100,act='relu')
        self.hidden3 = Linear(100,100,act='relu')
        self.hidden4 = Linear(3*100*100,10,act='softmax')
    def forward(self,input):
        # print(input.shape)
        x = self.hidden1(input)
        # print(x.shape)
        x = self.hidden2(x)
        # print(x.shape)
        x = self.hidden3(x)
        x = fluid.layers.reshape(x, shape=[-1,3*100*100])
        y = self.hidden4(x)
        # print(y.shape)
        return y
  • paddle.fluid.layers.reshape(x, shape, actual_shape=None, act=None, inplace=False, name=None) 该OP在保持输入 x 数据不变的情况下,改变 x 的形状。-1 表示这个维度的值是从x的元素总数和剩余维度推断出来的。因此,有且只有一个维度可以被设置为-1。
#用动态图进行训练
with fluid.dygraph.guard(): #通过with语句创建一个dygraph运行的context,执行context代码。
    model=MyDNN() #模型实例化
    model.train() #训练模式
    opt=fluid.optimizer.SGDOptimizer(learning_rate=0.01, parameter_list=model.parameters())#优化器选用SGD随机梯度下降,学习率为0.001.

    epochs_num=1 #迭代次数
    
    for pass_num in range(epochs_num):
        
        for batch_id,data in enumerate(train_reader()):
            images=np.array([x[0].reshape(3,100,100) for x in data],np.float32)
            labels = np.array([x[1] for x in data]).astype('int64')
            labels = labels[:, np.newaxis] 添加一个维度
            
            #将Numpy转换为DyGraph接收的输入.该函数实现从numpy.ndarray对象创建一个Variable类型的对象。
            image=fluid.dygraph.to_variable(images)
            label=fluid.dygraph.to_variable(labels)
            predict=model(image)#训练

            loss=fluid.layers.cross_entropy(predict,label)
            avg_loss=fluid.layers.mean(loss) #获取avg_loss值
            
            acc=fluid.layers.accuracy(predict,label)#计算精度
            
            if batch_id!=0 and batch_id%15==0:
                print("train_pass:{},batch_id:{},train_loss:{},train_acc:{}".format(pass_num,batch_id,avg_loss.numpy(),acc.numpy()))
            
            avg_loss.backward()      #使用backward()方法可以执行反向网络
            opt.minimize(avg_loss)   #调用定义的优化器对象的minimize方法进行参数更新
            model.clear_gradients()  #每一轮参数更新完成后我们调用clear_gradients()来重置梯度,以保证下一轮的正确性
            
    fluid.save_dygraph(model.state_dict(),'MyDNN')#保存模型
 

 

注:

  1. class paddle.fluid.optimizer.SGDOptimizer(learning_rate, parameter_list=None, regularization=None, name=None)    parameter_list (list, 可选) - 指定优化器需要优化的参数。在动态图模式下必须提供该参数;在静态图模式下默认值为None,这时所有的参数都将被优化。 
  2. paddle.fluid.dygraph.save_dygraph(state_dict, model_path)  ,(1)该接口会根据 state_dict 的内容,自动给 model_path 添加 .pdparams 或者 .pdopt 后缀, 生成 model_path + ".pdparms" 或者 model_path + ".pdopt" 文件。(2)state_dict 是通过 Layer 的 state_dict() 方法得到的。

#模型评估
with fluid.dygraph.guard():
    accs = []
    model_dict, _ = fluid.load_dygraph('MyDNN')
    model = MyDNN()
    model.load_dict(model_dict) #加载模型参数
    model.eval() #评估模式
    for batch_id,data in enumerate(test_reader()):#测试集
        images=np.array([x[0].reshape(3,100,100) 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)

注: 

  1. paddle.fluid.dygraph.load_dygraph(model_path) 该接口会同时加载 model_path + ".pdparams" 和 model_path + ".pdopt" 中的内容。参数或优化器的dict
  2. 返回: 两个 dict ,即从文件中恢复的参数 dict 和优化器 dict。para_dict: 从文件中恢复的参数 dict,opti_dict: 从文件中恢复的优化器 dict

#读取预测图像,进行预测
def load_image(path):
    img = Image.open(path)
    img = img.resize((100, 100), 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 = 'work/手势.JPG'
    model=MyDNN()#模型实例化
    model_dict,_=fluid.load_dygraph('MyDNN')   #fluid.load_dygraph(model_path)
    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)  #forward方法会被自动调用
    display(Image.open(infer_path))
    print(np.argmax(result.numpy()))  #np.argmax()取出数组中元素最大值对应的索引

 

 

 

 

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值