基于openpose数据的用户姿势识别
继续上一篇的github项目,继续将他修改成基于openpose数据集的网络结构。
1. 神经网络复习
为了方便读懂神经网络的框架代码和后续的网络按需修改,在这里简单复习一下分类和回归问题,然后把自己对深度学习网络的简单理解记录下来。
用简单的单变量线性回归做例子:
将房子的大小作为输入特征x来盘预测房子的价格y,h代表学习算法的解决方案或函数。
一种可能的表达式就是:
我们需要优化θ0和θ1这两个参数使得训练之后的模型能输入与实际相差最小的预测值。
我们通过设置一个代价函数(COST FUNCTION)来进行参数优化,同时也就是我们所说的损失函数(LOSS FUNCTION),目的是使代价函数最小。
一般我们使用梯度下降的方法来求这个函数的最小值,起始我们随即选择一组参数,然后寻找下一个能让代价函数值下降子多的参数,持续直到我们找到一个局部最小值。因为没有尝试完所有的参数组合,所以不能确定我们得到的局部最小值是全局最小值,选择不同的其实参数可能会得到不同的局部最小值。
其中的a是学习率(learning rate),决定我们的优化速度,每一次我们都让所有参数减去学习速率乘以代价函数的导数。
在梯度下降的过程中,我们需要同时去更新所有的参数,就是说我们需要求代价函数对每个参数的偏导数,然后再去更新参数。
直观的理解就是, 我们计算出来的偏导数都是代价函数在这个点上的切线斜率,在左边和在右边的时候分别对应了负号和正号,所以都可以让代价函数朝着局部最小值移动。
在逻辑回归和线性回归这两种简单的机器学习算法中,我们一般先确定自己需要使用的拟合函数形式,然后再通过梯度下降的方法去优化局部最优参数,使得我们的代价函数最小。
对逻辑回归而言实际虽然叫回归实际是一个二分类问题,我们需要确定边界函数的形式,利用梯度下降的方法去优化。
在涉及多个类别的逻辑回归问题时候,我们需要将他细分成多个二分类问题进行求解。
在学习过程中,有可能会出现过拟合的问题,导致模型的预测效果很差,这个时候我们一般可以选择丢弃一些不能帮助我们正常预测的特征,手动选择保留哪些特征。
或者使用正则化,保留所有的特征,但是减少参数的大小。
比普通机器学习更厉害的算法就是涉及到深度学习。
包括输入层,中间的隐藏层和最后的输出层。
第一层输入层属于是我们的原始数据,图片中的例子相当于是一个我们一个完整数据集中的一个实例传入神经网络时候的情况,实际运用的时候我们需要将整个数据集按照batch_size参数分成几块分别传进神经网络进行计算。
第二层开始每个激活单元是经过θ映射之后得到的。
θj带白哦从第j层映射到第j+1层时候的权重矩阵,形状上行数等于我们下一层全连接层的神经元个数,列数等于该层的激活单元个数加一(加一是因为我们会为每层添加一个偏置单元,一般就是等于1,相当于就是线性回归中的截距,这里将他融入到了矩阵运算中)。
这里加入一点自己的理解,因为刚开始接触的时候非常不理解当我们输入的数据集的一个实例的维度大于二维(比如图片矩阵)的时候,神经网络是如何操作最后使用softmax激活函数得到每个类别的概率的(因为softmax函数在调用的时候会针对narray数据中的最低维度的数据进进行softmax,如果是高维数据没办法根据此判断哪个类别的概率最大)。
现在会发现:其实所谓的batch_size,只是告诉神经网络,我们在计算了这么多个数据实例之后,才作为第一个epoch的结束去更新我们的权重,对应的简单数学理解,我引用一部分:
Batch size
即,首先选择n个样本组成一个batch,然后将batch丢进神经网络,得到输出结果。再将输出结果与样本label丢给loss函数算出本轮的loss,而后就可以愉快的跑BP算法了(从后往前逐层计算参数之于loss的导数)。最后将每个参数的导数配合步长参数来进行参数更新。这就是训练过程的一次迭代。
由此,最直观的超参数就是batch的大小——我们可以一次性将整个数据集喂给神经网络,让神经网络利用全部样本来计算迭代时的梯度(即传统的梯度下降法),也可以一次只喂一个样本(即随机梯度下降法,也称在线梯度下降法),也可以取个折中的方案,即每次喂一部分样本让其完成本轮迭代(即batch梯度下降法)。数学基础不太好的初学者可能在这里犯迷糊——一次性喂500个样本并迭代一次,跟一次喂1个样本迭代500次相比,有区别吗?
其实这两个做法就相当于:
第一种: total = 旧参下计算更新值1+旧参下计算更新值2+…+旧参下计算更新值500 ; 新参数 = 旧参数 + total
第二种: 新参数1 = 旧参数 + 旧参数下计算更新值1; 新参数2 = 新参数1 + 新参数1下计算更新值1; 新参数3 = 新参数2 +
新参数2下计算更新值1; … 新参数500 = 新参数500 + 新参数500下计算更新值1;也就是说,第一种是将参数一次性更新500个样本的量,第二种是迭代的更新500次参数。
原文链接:
https://blog.csdn.net/Dontla/article/details/104373682
在这个基础上,其实我们每次投喂给神经网络的数据还是仅仅是一个实例,只不过我们更新参数的时间变成了计算了batch size个实例之后而一,同时更新的方法变成了我们上面提到的算法。所以,我们始终能保证最后映射到输出层传入softmax里面的是一个一维的向量。至于在一个实例是多维的情况下,我们会在model里面添加flatten,将其展开成一维张量之后,再传入全连接层,这也是为什么CNN中传入图片之后经过卷积之后都会展开向量再传入全连接层。
经过层层传播,也就是我们所说的前向传播之后,最终得到我们需要的结果。在分类问题上,我们得到的可能是几个激活单元,每个激活单元使用softmax激活函数得到概率,最终概率最大的就是我们的输出类别。对回归问题而言,我们得到的是一个连续的输出变量,即一个激活单元。
FP中的部分记号:
可以把中间的激活单元看成是更加高级的特征值,因为是梯度下降的,所以a是变化的,并且变得越来越厉害,所以这些高级的特征值远比x次方利害,能更好的预测新数据。
在反向传播中,我们需要计算代价函数的偏导数,
我们先计算最后一层的误差,然后将误差一层一层的向前传到,直到倒数第二层,因为第一层是输入变量,不存在误差。我们假设有一个四层的网络:
第四层的误差是激活单元的预测ak(4)和我们的实际值yk之间的误差:
如果不进行正则化,我们在完成所有误差计算之后就可以开始计算代价函数的偏导数。
相当于就是说,代价函数针对每一层之间,每个权重变量的偏导,等于这一层的对应下表的激活单元乘上下一层的输出值和实际值的误差(当然这个误差也可以说是从从最后一层传导回来的)。在完成计算之后最终会张开成一个行数和列数与原本的权重矩阵对应的偏导矩阵,然后用这个矩阵乘上我们的学习率就是我们需要更新的下一个权重矩阵。从而完成反向传播。
在这里提一下为什么偏导数会等于上面的形式,可以利用链式求导法则简单理解:
这里的b就是我们的偏置矩阵,他的形状应该是体现在当我们将整个数据集喂给神经网络的时候,行数等于我们的数据集的实例个数,列数等于我们下一层的激活单元个数。
W相当于我们的权重矩阵θ。链式法则张开之后,zl对θ求偏导其实就是该层的输入xl,也就是上一层是输出al-1,而代价函数对z求偏导就是我们通过反向传播计算得到的误差值。
参考的笔记来源如下:
https://github.com/Mikoto10032/DeepLearning/blob/master/books/%5BML-Coursera%5D%5B2014%5D%5BAndrew%20Ng%5D/%5B2014%5D%E6%9C%BA%E5%99%A8%E5%AD%A6%E4%B9%A0%E4%B8%AA%E4%BA%BA%E7%AC%94%E8%AE%B0%E5%AE%8C%E6%95%B4%E7%89%88v5.1.pdf
几种我们常用的激活函数:
第一种:sigmoid函数
第二种:双曲正切函数,tanh函数
第三种:ReLU函数
第四中:softmax函数
2. 数据集处理和代码修改
参考的github项目,我们需要对原始提取出来的框架做数据处理,里面有三种数据处理的方法函数。
第一种是将所有坐标转化成关于头部坐标的x,y坐标。
第二种是将tf-pose输出的18个关节位置转化成8个关节角度,分别是左右肩膀,左右手肘,左右髋和左右膝盖。
第三种是将所有坐标转化成关于骨架保卫框的坐标。
在正式开始之前,我们需要知道tf-pose的18个坐标点和openpose 默认使用的body25 model输出的25个座标点区别:
tf-pose:
18点模型
对应位置:
// {0, “Nose”},
// {1, “Neck”},
// {2, “RShoulder”},
// {3, “RElbow”},
// {4, “RWrist”},
// {5, “LShoulder”},
// {6, “LElbow”},
// {7, “LWrist”},
// {8, “RHip”},
// {9, “RKnee”},
// {10, “RAnkle”},
// {11, “LHip”},
// {12, “LKnee”},
// {13, “LAnkle”},
// {14, “REye”},
// {15, “LEye”},
// {16, “REar”},
// {17, “LEar”}
对应位置:
{0, “Nose”},
{1, “Neck”},
{2, “RShoulder”},
{3, “RElbow”},
{4, “RWrist”},
{5, “LShoulder”},
{6, “LElbow”},
{7, “LWrist”},
{8, “MidHip”},
{9, “RHip”},
{10, “RKnee”},
{11, “RAnkle”},
{12, “LHip”},
{13, “LKnee”},
{14, “LAnkle”},
{15, “REye”},
{16, “LEye”},
{17, “REar”},
{18, “LEar”},
{19, “LBigToe”},
{20, “LSmallToe”},
{21, “LHeel”},
{22, “RBigToe”},
{23, “RSmallToe”},
{24, “RHeel”},
对比之后我们可以发现25坐标模型会比18坐标模型多了一个midhip的坐标,对于下半身来说还会多几个坐标但是因为我们暂时只需要用到上半身的坐标来做姿势识别,所以我们可以先将下半身的坐标点抛弃。
在data_preprocessing.py下面进行数据处理。因为在git上的测试下是第三种数据处理的效果最好,所以我们直接从第三种方法入手验证。
后续待更新。