RomanticQq

保持对生活的热爱,唯有生活不可被辜负

0%

AlexNet

AlexNet——验证了深度卷积神经网络的高效性

主体贡献

  1. 提出了一种卷积层加全连接层的卷积神经网络结构;
  2. 首次使用ReLU函数做为神经网络的激活函数;
  3. 首次提出Dropout正则化来控制过拟合 ;
  4. 使用加入动量的小批量梯度下降算法加速了训练过程的收敛;
  5. 使用数据增强策略极大地抑制了训练过程的过拟合;
  6. 利用了GPU的并行计算能力,加速了网络的训练与推断;

结构

NORM:批量相应归一化层;(现在基本不用了,因为它的性能在更深的网络里不太明显,计算量又有点大)

Max POOL:池化层;

AlexNet是8层神经网络结构,池化层和归一化层在计算神经网络的层数时不参与计算;

卷积层

第一个卷积层的输入是经过去均值操作 后的数据,即原始向量—均值向量,再输入到神经网络里面;

去均值的作用?

我们在分类任务中,均值向量是没有实际意义的,在向量比较时,绝对值是没有意义的,比较的相对值。去均值是为了保留自己的相对值,这样也会更加有利于计算。

池化层

3个池化层的卷积核大小3 * 3,步长为2;

这里是重叠池化,重叠有利于对抗过拟合,但是一般还是采用2*2的卷积核,步长为2;

池化层的作用:降低特征图尺寸,对抗轻微的目标偏移带来的影响;

轻微的目标偏移:比如在一个卷积核中最大值为8,最大池化后是8;但当这个8跑到这个卷积核中别的位置时,池化后的结果还是为8;因此能够对抗轻微的目标偏移带来的影响。

局部相应归一化层

目前基本不用了;

后来的研究表明: 更深的网络中该层 对分类性能的提升 效果并不明显,且 会增加计算量与存 储空间。

作用
  • 对局部神经元的活动创建竞争机制; 、
  • 响应比较大的值变得相对更大;
  • 抑制其他反馈较小的神经元;
  • 增强模型的泛化能力;

全连接层

MAX POOL3的输出:特征响应图组,大小为6 6 256;

把MAX POOL3的输出打平成9216(6 6 256)维的向量,交给全连接层;

重要说明

  • 用于提取图像特征的卷积层以及用于分类的全连接层是同时学习的;
  • 卷积层与全连接层在学习过程中会相互影响、相互促进;

卷积层和全连接层是一起训练的,被训练的梯度是可以回传的;

重要技巧

  • Dropout策略防止过拟合;
  • 使用加入动量的随机梯度下降算法,加速收敛;
  • 验证集损失不下降时,手动降低10倍的学习率;
  • 采用样本增强策略增加训练样本数量,防止过拟合;
  • 集成多个模型,进一步提高精度(同时训练多个模型);

AlexNet卷积层在做什么

把AlexNet卷积层看作是一个卷积层,输入227 227 3,输出6 6 256,相当于有256个卷积核,获得256个特征响应图,把256个特征响应图拉成向量;

特征响应图某点处较亮,说明该亮处含有该卷积核的特征;

ZFNet

主体结构

主要改进

  • 将第一个卷积层的卷积核大小改为了7×7;(不要一次卷积就丢掉细颗粒度的东西)
  • 将第一、第二个卷积层的卷积步长都设置为2;(缓慢给图像降维)
  • 增加了第三、第四个卷积层的卷积核个数;(第三层、第四层有语义信息了,增加了神经元即增加模板信息,仅仅靠现有的模板数不足以存储信息)

AlexNet与ZFNet的对比

VGG

结构

VGG的输入

VGG的输入与AlexNet和ZFNet是不同的,它是先把所有的R、G、B的均值求出来,然后再进行去均值的操作。

VGG16 VS VGG19

思考

问题1:小卷积核有哪些优势?

多个小尺寸卷积核串联可以得到与大尺寸卷积核相同的感受野;

使用小卷积核串联构建的网络深度更深、非线性更强、参数也更少。

注:2个3 3卷积核的感受野相当于1个5 5卷积核的感受野;这两方式虽然感受野相同,但是最后获取的结果可能是不同的。

问题2::为什么VGG网络前四段里,每经过一次池化操作,卷积核个数就增加一倍?
  1. 池化操作可以减小特征图尺寸,降低显存占用;

  2. 增加卷积核个数有助于学习更多的结构特征,但会增加网络参数数量以及内存消耗;

  3. 一减一增的设计平衡了识别精度与存储、计算开销;

    最终还是提升了性能

问题3:为什么卷积核个数增加到512后就不再增加了?
  1. 第一个全连接层含102M参数,占总参数个数的74%;
  2. 这一层的参数个数是特征图的尺寸与个数的乘积;
  3. 参数过多容易过拟合,且不易被训练;

注:第一层全连接层的输入为7 7 512维的向量,则这一层需要的参数为102M(7 7 512 * 4096)参数;

若继续增加卷积核个数,那么第一层全连接层参数会更多,如为1024个卷积核,那么第一层全连接层参数将为200M,那样模型将会很难被训练;因此不是不想增加,而是增加不了。

GoogleNet

GoogleNet的结构

GoogleNet的创新点

  • 提出了一种Inception结构,它能保留输入信号中的更多特征信息;
  • 去掉了AlexNet的前两个全连接层,并采用了平均池化,这一设计使得 GoogLeNet只有500万参数,比AlexNet少了12倍;
  • 在网络的中部引入了辅助分类器,克服了训练过程中的梯度消失问题;

从AlexNet等一些经典的神经网络中可知参数极大多数来源于与最后一层卷积层相连接的第一层全连接层,当在GoogleNet中最后一层卷积层后采用平均池化的方法,大大降低了第一层全连接层的参数;

串联结构(如VGG)存在的问题

后面的卷积层只能处理 前层输出的特征图;前层丢失重要信息,后层无法找回。

解决方案:

—每一层尽量多的保留输入信号中的信息。

Inception模块和Inception V1模块

1 * 1 conv:把原来的信息进行压缩整理;

3 3 conv:提取3 3的局部特征;

5 5 conv:提取5 5的局部特征;

1 * 1 max pooling:提取扩张以后的信息;(max pooling相当于最大化抑制,max pooling对周围信息进行了扩展,比如一片区域内最大值是9,那么经过max pooling后,9的周围就会都变成9,对9这个信号进行加强)

为了保证输出的特征图大小相同,分别对1 1,3 3,5 * 5这3个卷积层进行边缘进行0,1,2的零填充;

Inception模块 VS Inception V1模块

Inception V1模块增加了3个1 * 1的卷积层,使特征图的深度极大的降低,较少了参数个数和计算量,提高了效率;

1 * 1的卷积核不改变H和W,但是可以降低通道深度;

GoogleNet的总结

输入

输入为去均值后224 * 224的图像,去均值采用的方法是R、G、B;

平均池化

与全连接层相连时,采用的池化是平均池化;

辅助分类器损失

问题

平均池化向量化与直接展开向量化有什么区别?

平移不变性:图像中的目标,不管被移动到图片的哪个位置,得到的结果应该是相同的,这就是卷积神经网络的平移不变性。

利用1x1卷积进行压缩会损失信息吗?

因为一个图像经过卷积核后,只有满足卷积核特征的地方值才会很大,其余基本上为0;又因为一个图像上满足某一特征的地方往往又很少,因此得到的特征向量图是十分稀疏的,经过压缩往往是压缩了大量的0,因此非线性压缩通常不会损失信息。

ResNet

问题引入

通过前面四种网络的学习,我们几乎可以想到越深层次的网络的学习效果越好;但是其实并不是这样的,当网络较深时,就会出现正反向信息流动不顺畅的问题,ResNet的出现解决了这个问题。

ResNet的贡献

ResNet残差模块

  • 通过公式我们可以看出,即使F(x)不起作用,H(x)还是x的值,从而可以知道F(x)即使不起作用也不会造成负面影响;
  • 原图+细节=锐化;可以理解为F(x)为提取的细节,使x得到了锐化即H(x),当H(x)作为输入时,即可以提取原图中的特征,也可以在上一层提取的细节上进行增强,因此这个前向传播信号也是不容易衰减的;
  • 反向传播时,即使F(x)内梯度为0,x梯度恒为1,也能够使梯度进行回传,保证了反向梯度的通畅;
  • F(x)=H(x)-X,即残差=输出-输入;输出和输入的差异叫做残差,也因此叫残差模型;

第一个Conv 1 * 1的作用:降低通道数,使能够减少参数和计算量;

第二个Conv 1 * 1的作用:增加通道数,使能够和X相加;

ResNet结构

问题

为什么残差网络性能那么好?

普通网络当某一层失去作用时,可能就会瘫痪,而残差网络不用这样;

残差网络会自动适应选择某些层进行工作;

总结

  • 介绍了5种经典的卷积神经网络AlexNet、ZFNet、VGG、GoogLeNet和ResNet;
  • 残差网络和Inception V4是公认的推广性能最好的两个分类模型;
  • 特殊应用环境下的模型:面向有限存储资源的SqueezeNet以及面向有限计算资源的 MobileNet和ShuffleNet;

全连接神经网络的瓶颈

把图像拉成拉成一维的向量作为输入层;

隐层神经元的参数个数为前一层神经元个数+1;

在全连接神经网络中,当图像较大时,参数就会非常多,因此会出现过拟合和计算困难的情况,而且也不容易学习出有效的东西,此时卷积神经网络就出现了;

全连接神经网络对一些表示成向量形式特别是简洁的向量形式特别有效,比如适合处理小的图像,或则把一些处理过的数据获得的简洁结果作为输入;

卷积神经网络

卷积神经网络层次结构

卷积神经网络分为卷积层、激活层、池化层和全连接层;

卷积神经网络的工作流程

图像输入——>卷积核组——>获得结果——>全连接神经网络——>分类

基于卷积核组的图像表示

卷积神经网络的卷积核和纹理的原理基本一致;

如果特征响应图某一点较亮,说明原图像这一点存在该特征;

卷积网络中的卷积核

  • 不仅具有宽和高,还具有深度,常写成如下形式: 宽度 x 高度 x 深度
  • 卷积核参数不仅包括核中存储的权值,还包括一个偏置值

设计卷积核

只需要设计卷积核的W,H和卷积核个数,因为卷积核深度由前一层的卷积核个数决定;

卷积网络中的卷积操作

特征响应图中每个位置上的值反映了图像上对应位置是否存在卷积核所记录的基元结构信息;

浅层次的卷积核只是记录边缘,深层次的卷积核记录可能就是一个模板;

卷积步长

边界填充

特征响应图组尺寸计算

池化操作

池化的作用

对每一个特征响应图独立进行,降低特征响应图组中每个特 征响应图的宽度和高度,减少后续卷积层的参数的数量,降低计算资源耗 费,进而控制过拟合。

  • 降低计算资源消耗;
  • 扩大感受野,即卷积核变大,方差变大;
池化操作

对特征响应图某个区域进行池化就是在该区域上指定一个值来 代表整个区域。

常见的池化操作
  • 最大池化——使用区域内的最大值来代表这个区域;
  • 平均池化——采用区域内所有值的均值作为代表。

最大池化可以理解成这几个位置都表现出了某一特征,只是表现强度和位置不一样,把最大强度的选出来;

池化层的超参数

池化窗口和池化步长

池化示例

池化操作不用填充边;

卷积神经网络如何与全连接神经网络相连

  • 用均值:把每一个特征响应图展成一个向量,即r1,r2….(r表示向量)
  • 用均值:求每个特征响应图的均值,即r1,r2….(r表示均值)

损失函数&优化算法

  • 损失函数:交叉熵损失
  • 优化算法:SGD、带动量的SGD以及ADAM

图像增强

存在的问题

过拟合的原因是学习样本太少,导致无法训练出能够泛化到新数 据的模型。

数据增强

是从现有的训练样本中生成更多的训练数据,其方法是利用多种能 够生成可信图像的随机变换来增加样本。

数据增强的目标

模型在训练时不会两次查看完全相同的图像。这让模型能够 观察到数据的更多内容,从而具有更好的泛化能力。

样本增强

翻转

随机缩放&抠图

色彩抖动

其他

补充材料1:卷积与图像去噪

图像去噪与卷积

噪声图像

噪声的一般表达:这个点的像素比其他点的像素值都大,而且可能还大好多;

图像去噪

可以使用加权求和的方法达到去噪的目的。

边界填充

如果不考虑边界填充,那么卷出来至少少一圈;

填充的层数与模板的大小有关;

一般采取0填充;

除了0填充,还有拉伸填充(将四周边缘像素拉伸)和镜像填充;

卷积平移

原图

向左右平移

平均卷积核

平滑

锐化

原图-平滑=边缘图;

原图+边缘图=锐化;

高斯卷积核

平均卷积核存在的问题

卷积后的图像产生了一些水平和竖直方向的条状(振铃);

解决方法

根据邻域像素与中心的远近程度分配权重,即高斯卷积核;

高斯卷积核

高斯卷积核的性质就是使中心位置权重大边缘位置权重小;

高斯卷积核的作用:去除图像中的高频成分(因为高斯卷积核也是一个平均操作,把高的地方给磨皮,即去除高频成分,另外也可以理解让低频的像素通过,即又称低通滤波器);

高斯卷积核是一个线性卷积核;

高斯卷积核各个位置的权值的求法:以中心位置为(0,0),把x,y代入求解,则(0,0)左边为(-1,0)代入求解,以此类推;

对权重值进行归一化处理:不做归一化会出现两个问题(若总和小于1,比如相加之和为0.1,在一个白色图像上,卷完就成25.5,对原图像进行了衰减;若大于1,那么像素值就有可能大于255)

高斯卷积核参数

根据高斯函数的计算公式,在(0,0)处方差越大,G(0,0)的值越小即权重越小,受周围像素的影响较大,即平滑能力强;同理方差越小,平滑能力越弱;

当相同方差时,G(0,0)处的值,大窗口进行归一化后值小,小窗口值大;

G(0,0)越大,平滑能力越强;

图像经过1,2两个卷积核卷积后,和图像经过1,2两个卷积核的标准差平方和再开方后的结果是相同的;可以推广到多个;

一个二维的高斯卷积核可以分解为两个一维的高斯卷积核。

图像噪声与中值滤波器

图像噪声
  • 椒盐噪声: 黑色像素和白色像 素随机出现;
  • 脉冲噪声: 白色像素随机出现;
  • 高斯噪声: 噪声强度变化服从高斯分布(正态分布)
高斯去噪

高斯卷积核可以去除高斯噪声,并且噪声方差越大,卷积核也应越大;

椒盐噪声和脉冲噪声不能使用高斯卷积核达到去噪的效果;

中值滤波器

它是一个空的3 3或5 5…的模板套在数据集上,然后把所有像素值进行排序,找到中位数,用中位数去替换模板中间位置的数。
好处:它的值一定是图像上某一点的值;

补充材料2:卷积和边缘提取

边缘提取

边缘

图像中亮度明显而急剧变化的点;

为什么要研究边缘
  • 编码图像中的语义和图像信息
  • 想对于像素表示,边缘表示显得更加紧凑
边缘的种类

表面法向不连续:两个面交界的地方会形成边;

深度不连续:不是真实存在的,比如拍照时照不到形成的边;

表面颜色不连续:物品上的细节,例如字等;

光照不连续:阴影;

边缘检测

图像中亮度明显而急剧变化的地方称为边缘,从而可知边缘处相邻处像素相差较大,即一阶导数的绝对值较大;

图像求导

可以根据图像求导找出图像的边缘,边缘处的导数最大;

在计算图像求导时,ε=1,则经过化简可得,某一像素点处的导数等于它右边的像素减去它自己的像素;

并且可以通过卷积核实现对x,y的求导;

图像梯度

梯度指向灰度变化最快的方向;

信号方向与梯度方向垂直,所以通过梯度方向还可以知道信号大概的方向;

图像梯度通常由暗的地方指向亮的地方;

梯度的值越大,表明是边缘的可能性就越大;

高斯一阶偏导卷积核

噪声的影响

对于含有噪声的图像是不能直接求导求边缘的,需要先平滑,如果直接求导将很难判断边缘;

注:这的噪声一般都是指高斯噪声;

平滑去噪再求导

image-20211001103924027

先用高斯卷积核去噪以后再对去噪后的图像进行求导;

高斯一阶偏导

高斯一阶偏导是先对高斯卷积核求偏导,因为卷积操作满足交换律和结合律;

对高斯卷积核求导后的结果称为高斯一阶偏导;

高斯一阶偏导卷积核

高斯一阶偏导卷积核只有一个参数,就是方差;

方差越小,获取的图像边缘越清晰;方差越大,获取的图像边缘越粗糙;

方差小关注细颗粒度,方差大关注粗颗粒度;

高斯核 VS 高斯一阶偏导核

高斯一阶偏导卷结合总和是0是可以证明的,它表名了在一个没有像素变化图像的图像上,梯度恒为0;

边缘目标检测

非极大值抑制

利用高斯偏导卷积核求出的图像可能会出现较粗的边,这是由于某处它附近的梯度值都很大的缘故,此时需要用到非极大值抑制;

p点的梯度强度需要与它正梯度方向和反梯度方向的点去进行比较,如果比这两个梯度都强保留,否则删去;

Canny边缘检测器

用一个高门限把高阈值留下来,然后这些低阈值就会产生很多边,把那些跟高阈值有连接的边留下来;

补充材料3:纹理表示

纹理

纹理可以区分不同物品,它表示了一些材质或则属性;

image-20211001163609151

纹理分为规则纹理和随机纹理;

基于卷积核组的纹理表示方法

  • 利用卷积核组提取图像中的纹理基;

  • 利用基元的统计信息来 表示图像中的纹理;

卷积核组

卷积核组是有多个卷积核组成的,每个卷积核表示着不同的特征;

比如:第一个卷积核检测水平方向的边,最后一个卷积核检测圆形或斑状物体;

基于卷积核组的图像表示

把每个卷积核得到的特征响应图打平组合成一个向量;

ri的个数等于卷积核组中卷积核的个数;

纹理分类任务

  • 忽略基元位置;

  • 关注出现了哪种基元对应的纹理以及基元出现的频率;

基于卷积核组的图像表示

该方式采用的每个响应特征的均值,可以减少计算量;

小游戏

可见在纹理A中第四个颜色最白表明含有第四个卷积核的特征,即垂直的线条,因为为图像2;

卷积核组设计

设计重点:

  • 卷积核类型(边缘、条形以及点状)
  • 卷积核尺度(3-6个尺度)
  • 卷积核方向(6个角度)

卷积核尺度,大尺度提取粗粒度的边,小尺度提取细粒度的边;

总结
  • 设计卷积核组;
  • 利用卷积核组对图像进行卷积操作获得对应的特征响应图组;
  • 利用特征响应图的某种统计信息来表示图像中的纹理。

图像——>卷积核模板——>特征响应图——>ri特征向量的均值

  1. 目录错乱

    几级标题必须按顺序,如果三级标题下直接五级标题就会出问题。

0.像素表示

如(r1,g1,b1,r2,g2,b2…rn,gn,bn)这样

1.多层感知机

全连接神经网络

max()叫激活函数。

激活函数不能去掉,去掉就退化成线性分类器。

全连接神经网络的权值

w1可以看做模板,它可以有比类别数更多的分类,就是一个类别可以有多个模板。

比如10中类别的动物,w1可以有50中模板甚至更多,即同一类动物可以有多个模板,把与x得到的结果与w2融合,找到最大的,得出结果。

线性分类器的w行数和类别数相同,而全连接神经网络中只需最外层w的行数和类别数相同。

全连接神经网络与线性不可分

线性分类器可用于分离线性可分的,但是线性分类器不能把线性不可分的分开,此时就需要有全连接神经网络这样的模型。

全连接神经网络绘制与命名

每个x相互独立为单独的输入。

2.激活函数

激活函数

常用的激活函数

sigmoid函数的范围为(0,1);

tanh函数的范围是(-1,1);而且是中心对称的;

网络结构设计

  1. 用不用隐层,用一个还是用几个隐层?(深度设计)

  2. 每隐层设置多少个神经元比较合适?(宽度设计)

    没有确定的答案。

    依据分类任务的难易程度来调整神经网络模型的复杂程度。分 类任务越难,我们设计的神经网络结构就应该越深、越宽。但是, 需要注意的是对训练集分类精度最高的全连接神经网络模型,在 真实场景下识别性能未必是最好的(过拟合) 。

全连接神经网络小结

3.softmax与交叉熵

softmax函数

softmax函数是把分类的结果得分转换成各种结果的概率。

交叉熵

在此处交叉熵的意义在于衡量两个分布的不相似度。

计算交叉熵之前要用softmax方法做归一化处理。

相对熵中的p和q是不能交换的,p和q之间不能交距离,只能叫散度,因为二者不满足可交换性。

熵表示的信息量的大小。

比如:中国和巴西踢足球,巴西赢的概率为1,平的概率为0,输的概率为0,这种事情没有信息量,确定性极强,熵为0;

当每种概率都是1/n时,此时熵是最大的,因为结果是不确定的。

通过熵,交叉熵,相对熵的公式可以看出:交叉熵=熵+相对熵

又因为真实分布的熵为0,此时交叉熵等于相对熵。

因为相对熵又称为KL散度,表示的是两个随机分布之间的差异,且交叉熵等于相对熵,所以可用交叉熵来表示两个随机分布之间的差异。

4.对比多类支撑向量机损失

交叉熵损失 vs 多类支撑向量机损失

很明显,计算出来的交叉熵损失和多类支撑向量机损失的值是不同的。

采用交叉熵损失训练的结果是,某一项分数高时,还要尽可能去压低其余项的分数;

采用多类支撑向量机损失训练的结果是,某一项分数高时,只比其余项高出1即可。

在使用多类支撑向量机损失时,可能会出现loss值几乎没有变但是分类精度却提高了,比如:

【0.35,0.33,0.32】和【0.33,0.35,0.32】,两则计算出的loss值差距很小,但是分类的结果却完全不同。

5.计算图与反向传播

计算图

计算图是一种有向图,它用来表达输入、输出以及中 间变量之间的计算关系,图中的每个节点对应着一种数学运算。

当多层神经网络时,写出求导后的表达式是不容易的,引入计算图可以实现复杂函数的求导问题。

通过计算图计算某处的值,要知道三点:

  1. 上图中1处的值,即z=x+y处的z值是多少;
  2. 上图中2处求导的表达式,即z^2求导的表达式
  3. 上图中3处的导数的值;

反向传播

总结:

  1. 通过正向计算出各处的值;
  2. 能反向计算是因为有了链式法则;
  3. 反向传播最后一个导数为1,因为f对f求导还是1;
  4. 所求处的梯度=上游梯度*局部梯度;
  5. 上游梯度是指所求处右侧的梯度;局部梯度为所求处右侧圆圈式子里的梯度;
  6. 局部梯度的式子若为+1,即为x+1,若为*-1,即为-x,以此类推;

存在的问题:因为是相乘操作,当梯度都特别小时,乘着乘着就可能出现梯度消失的情况;

计算图的颗粒度

计算图的颗粒度:每一个门可以是一个很复杂的函数,也可以是简单的加减乘除;函数越复杂代表颗粒度越大;

简单函数的好处是都可以按照基源去操作,坏处是要严格按照链路一步一步去计算;

复杂的函数颗粒度较大,但计算效率较高;

计算图中常见的门单元

  • 加法门:左边每个分支的值都等于右边的值;
  • 乘法门:两数互换,再相乘;
  • 拷贝门:左边等于右边的和;
  • max门:把右边的值赋给左边最大的,其余为0;

6.再看激活函数

梯度消失

通过观察sigmoid函数和tanh函数的图像可知,当x超出一定值后梯度为0,由于链式法则是相乘的就会出现梯度消失的情况。

为了解决这一问题就出现了Leakly ReLU函数

Leakly ReLU函数在x>0的情况下梯度恒为1,在x<0的情况下恒为0.01;效果较好;

梯度爆炸

梯度爆炸会导致错过良好的w值;

通过限制步长的方法来解决梯度爆炸;

激活函数选择总结

尽量选择ReLU函数或者Leakly ReLU函数,相对于Sigmoid/tanh, ReLU函数或者Leakly ReLU函数会让梯度流更加顺畅,训练过程收敛 得更快。

由于Sigmoid/tanh函数的特性,一般情况下不会在隐层中使用,但可能会用在输出层,把结果控制在某一个范围中。

7.动量法与自适应梯度(梯度算法的改进)

梯度下降算法存在的问题

当存在多个w时,传统梯度下降算法会在陡峭处w会来回震荡,而平坦处的w优化缓慢;

仅仅增大步长并不能加快算法收敛的速度,因为w=w-步长*某点处梯度,增加步长会使陡峭的地方震荡幅度增大(因为陡峭处的梯度值的绝对值较大),平坦的地方进展仍然缓慢(因为平坦处的梯度值的绝对值较小);

动量法

动量是有方向的,梯度也是有方向的;

在陡峭的地方,梯度方向是不断变化的,当把一定的梯度累加在一起可能是在某一个方向上一个很小的数;在平坦的地方,梯度的方向是一定的,虽然它是一个很小的数,但是累加起来就是一个较大的数,从而实现梯度较快更新的目的;

动量法累加梯度信息是图中3处,并不是2处,2处是小批量随机梯度下降算法的累加;

动量系数u控制历史信息是不是要被衰减;

若u=0时就退化成梯度下降算法;若u=1时就会不断的加大历史的影响,即v=v+g,即使梯度下降算法走到平坦的区域(g=0)时,v仍然不等于0,那么权值会仍然更新下去,但若v=0.9时,v=0.9v,一定次数后v的值将会非常小。

u的值一般使用0.9;

因为鞍点和局部最小值点的导数都为0,又因为w=w-步长*导数,则w更新不动了,此时通常就认为收敛了,但这是不对的;

加入动量以后,即使局部梯度为0,即g=0,但是v=uv+g不为0,并不会马上停止更新,此时就有可能冲出局部最小点或鞍点 ,找到更优的解。

自适应梯度与RMSProp

自适应梯度算法就是在陡峭的地方用小步长,在平坦的地方用大步长。

通过计算梯度平方和找出震荡方向和平坦方向,在4中表达式可以看出较大的r学习率较小,较小的r学习率较大,从而达到减少震荡更快达到谷底的目的。

存在问题:因为r一直在累加,当加到很大时,学习率就会趋近于0,失去调节作用;

RMSProp解决了AdaGrad存在的问题,因为r前面存在ρ,当r加到一定的次数,前面的r就会衰减变得非常小,进而保证r的值永远不会很大。

ρ=0时,仅仅考虑当前梯度的强度;

ρ=1时,r=r,又r初始为0,显示不可能;

ρ一般取0.999;

保留的次数越多,p的值就应该越大,例如:保留一百个数p的值应为0.99(1-1/100=0.99),可以此类推(只是大概而已);

ADAM

ADAM是动量法和自适应梯度下降算法的结合;

图中3处体现了动量法,图中4处体现了自适应梯度下降算法,图中5处解决了程序初期的冷启动问题;

图中5处解决程序刚开始运行时的冷启动问题:当第一次执行程序,u=0.9,v=0,t=1时:图中3处v=0.1g,若此时去更新梯度则实际步长是非常小的,更新较慢,在图中5处进行修正偏差,u=0.9,t=1此时修正后的v=g,从而解决了程序刚开始执行时梯度更新较慢的问题,随着t的增大,u的t次方趋近于0,从而修正偏差逐渐失去作用,不会对开始一段时间后的更新产生影响;r的更新同理。

t代表程序第t次执行;

一般情况下,u=0.9,ρ=0.999,这两个是经验值,可以直接用。

总结

动量法:采用累加梯度的思想,降低陡峭处w的变化幅度,增强平坦处w的变化幅度;

自适用梯度和RMSProp:自适应梯度算法的思想是减小陡峭处的步长,增大平坦处的步长,从而提高优化效率,但是存在问题(因为r一直在累加增大,当r太大时,就会对步长失去调节作用),为解决这个问题就出现了RMSProp算法,它通过给r设置衰减系数的方法,从而防止r的值不会变的很大。

ADAM:ADAM算法是动量法和自适用梯度算法的结合,又加入了修正偏差,解决了冷启动问题(程序刚开始执行时,梯度更新较慢)。

在一般情况下使用ADAM算法,但是它没有动量法+小批量梯度下降算法手动调优的效果好。ADAM算法在大部分任务下可以速度很快。

在实际使用过程中,有可能先用ADAM算法初始学习一下,学不动了用动量法+SGD手动调;也有可能先用动量法+SGD先学习一下,然后用ADAM做后面的加速。

8.权值初始化

全零初始化

全零初始化会使不同的神经元有相同的输出,不能进行正向传播和反向传播;

随机权值初始化

如采用较小的随机正态分布采样,通过双曲正切激活函数的图像可知,激活后几乎所有值趋近于0,在经过激活函数所有值就为0,在后面的隐层不能进行正向传播;

如采用较大的随机正态分布采样,通过双曲正切激活函数的图像可知,激活后几乎所有值趋近于1和-1,此时局部神经元梯度为0,不能进行反向传播;

Xavier初试化

(此处公式推导的原理还不明白)

N为输入的神经元个数;

HE初始化

权值初始化小结

9.批归一化

批归一化就是把输入先调整成0均值1方差的样子,然后再调整成与输入具有相同的分布。

批归一化在非线性激活之前。

γ和β是参数,是通过训练得到的;

经过减均值,除方差后,数据会集中在均值两侧,此时梯度较好,几乎不会出现为0的情况;

批归一化的作用是使数据集中在激活函数的中间位置,使梯度不会出现为0的情况;

10.欠拟合、过拟合与Dropout

过拟合

过拟合——是指学习时选择的模型所包含的参数过多,以至于出现这一模 型对已知数据预测的很好,但对未知数据预测得很差的现象。这种情况下 模型可能只是记住了训练集数据,而不是学习到了数据特征。

一般当出现在验证集上误差增大时即为训练最优状态。

调节模型大小

调节模型的宽度和深度;

正则化

L2正则损失对于大数值的权值向量进行严厉惩罚,鼓励更加分散的权重向量, 使模型倾向于使用所有输入特征做决策,此时的模型泛化性能好;

使分界面变得平滑,不那么复杂;

随机失活

随机失活鼓励权值分散,因为当某些较大的权值被失活时就可能造成神经网络的瘫痪。

解释三:Dropout可以看作模型的集成,因为随机失活,每个随机失活剩余的模型就不同(则可以看作多个模型的集成),可能会出现不同的分类结果,可以找到分类结果出现最多的那次(相当于投票)。

欠拟合

欠拟合——模型描述能力太弱,以至于不能很好地学习到数据中的规律。 产生欠拟合的原因通常是模型过于简单。

11.模型正则与超参数优化

神经网络中的超参数

  • 网络结构——隐层神经元个数,网络层数,非线性单元选择等;

  • 优化相关——学习率、dropout比率、正则项强度等;

学习率设置

参数优化方法

超参数的标尺空间

图像表示

将图像转换成向量的方法有很多,最直接的方法是将图像矩阵转换成向量
图像类型

  • 二进制图像(非黑即白)
  • 灰度图像(一个像素由一个bit表示,取值范围0~255;图像的像素取值范围都是0~255,最黑是0,最白是255)
  • 彩色图像(每个像素点有3个值,即有3个bit,分别记录R、G、B)

线性分类器

  • 线性分类器是一种线性映射,将输入的图像特征映射为类别分数
  • 有n类图像就有n个w,b,即每类图像都有自己的w,b
  • 把图片放入得到n个分数,分数最大的即为结果
    11111

为什么从线性分类器开始

  • 形状简单,易于理解
  • 通过层级结构(神经网络)或则高维映射(支持向量机)可以形成功能强大的非线性模型

线性分类器的权值

把w放入一个32 32 3的矩阵中,把其中的值规划到0~255之间,生成的图片和目标图片很相似;w记录了该类别信息的平均值,当物体和w越像的时候,他们之间的点乘值就是一个很大的值。

线性分类器的分界面

  • 分数等于0的面试决策面
  • 在分界面的正方向,离分界面越远正确率越高(或把x代入wx+b中,得分越高正确率越高)

当判断分类器的分类效果时,需要损失函数来帮忙

损失函数

损失函数搭建了模型性能与模型参数之间的桥梁,指导模型参数优化。

  • 损失函数是一个函数,用于度量给定分类器的预测值与真实值的不一致程度,其输出通常是一个非负实数
  • 其输出的非负实数值可以作为反馈信号来对分类器参数进行调整,以降低当前示例对应的损失值,提升分类器的分类效果
  • 损失为平均损失

多类支持向量机损失


用n个w,b对同一样本进行预测得到n个数,把wi,bi预测出的syi除外,拿syi与其他预测值比较,若syi比其他测试值大1,则损失函数为0,否则通过上图公式进行计算;
1为限定的边界值,一般取1;

问题抢答

  1. 最小是0;最大可能是正无穷;
  2. 损失L可能是n-1,即分类数-1;因为当w,b非常接近于0时,Li的值为n-1,有n个n-1,则L近似为n-1;
  3. Li的值会+1,对训练结果没有影响;
  4. 可以代替,因为求和是平均n的倍数,损失大的还是大,损失小的还是小;
  5. 使用平方会可能会对训练结果产生影响;

正则项和超参数

问题:假设存在一个w使损失函数为0,这个w是唯一的吗?


这个时候就引入了正则项。
正则项


正则项的三个作用

  • 让解唯一
  • 让模型有了偏好
  • 抵抗过拟合(防止只在训练集在学习的太好)
    常用的正则项损失函数

    Elastic net(L1+L2)是弹性网络正则,即L1和L2组合起来的。
    超参数
    是在训练之前自己设置的,而不是在训练过程中学习优化得到的。

什么是参数优

参数优化
参数优化是利用损失函数的输出值作为反馈信号来调整分类器参数,以提升分类器对训练样本的预测能力。
优化目标
损失函数L是一个与参数w有关的函数,优化的目标就是找到损失函数L达到最优的那组w。

梯度下降算法

梯度下降算法


梯度下降算法的下降过程如L,w坐标图所示,我们可以理解任意一个w都对应一个L,我们从模型上的任意一点开始,若它在单调递减区间,那么它在这一点的梯度方向是向左上的,且导数为负数,因为我们要向负梯度方向移动,即向右下方向,此时随着w的增加L值减小;同理当L在单调增区间时,梯度的值为正,则L随着w的不断减少而减少。

更新权值的计算公式:新权值 = 老权值 - 学习率(步长) * 权值的梯度;

梯度计算方法的比较

数值梯度:近似,慢,易写

解析梯度:精确,快,易错

数值梯度的作用:求梯度时一般是用的是解析梯度,而数值梯度主要用于解析梯度正确性的校验(梯度检查);若两者的计算结果非常接近,则说明在这部分代码从应该没有问题。

采用梯度下降法,是把所有的样本计算一次才更新一次w,计算量较大,且效率较低。

随机梯度下降法

方法1:有一百万个样本,算完了一百万个样本更新一次w,更新一百万次w;

方法2:有一百万个样本,每个样本更新一次,更新一百万次w;

那么方法2肯定比方法1好,起码速度上比较快,精确上也接近;

因此,在样本N很大时,权值的梯度计算量很大,下降很慢,改进方法就是采用随机梯度下降法,因为每次只采用有一个样本,梯度更新会更快。

但是就会出现问题:如果有些样本是噪声,那梯度方向会不会沿着反方向走?

随机梯度下降法是每次随机挑选一个样本去更新w,虽然单个样本可能使L向增大的方向走,但大部分样本会让我们整体是一个往下走的方式。

小批量梯度下降算法

epoch:1个epoch表示用过了一个训练集中的所有样本(并不是所有样本都用过了,而是所用的样本总数达到了N);

100次迭代每次用100个样本和1000次迭代每次用10个样本,这两则对样本的利用率是一样的;

梯度下降算法总结

数据集划分

训练集用于对参数的训练,当模型中有超参数时,通过验证集把超参数给选择出来,并通过测试集去测试模型的泛化能力;当对参数和超参数确定的过程中,测试集对模型是不可见的。

当数据太少,那么验证集所包含的样本就很少,从而无法统计上代表的数据

K折验证和重复的K折验证

举例:在区分男女的模型中,拿10%的数据作为验证集,且这部分数据都是女生;模型1对男女的区别的正确率都是90%,模型2对男生的正确区分率是0%,对女生的正确区分率是100%;那么结果就会表现出模型2是优秀的,但实际上并不是。

K折验证

重复的K折验证

K就是分的块数,常用5折和10折;当大量数据时,一般取5%作为验证集,则不再做交叉验证。

数据预处理

去均值后的数据:去均值就是把数据归到中心来,训练数据时看的是相对差值而不是绝对差值,保证数据不会受到数据范围的影响。

归一化后的数据:保证数据不会受到量纲的影响。

去相关:让数据独立出来;能尽量减少某些在维度上没有变化的数据,这些数据就不考虑了;

什么是图像分类任务,它有哪些应用场合

图像分类任务:图像分类任务是计算机视觉中的核心任务,其目的是根据图像信息中所反映的不同特征,把不同类别的图像区分开来。
应用场合

  • 图像分类:从已知的类别标签中为给定的输入图片选定一个类别标签。
  • 图像识别:知道该物体是什么;
  • 图像搜索:在互联网上搜到该图片的信息(在街上拍个照片去搜索,就知道这个图片在什么位置)
  • 目标检测
  • 图像分割
  • 图像生成

图像分类任务有哪些难点

跨越语义鸿沟,建立像素到语义的映射

  • 视角(不同角度看到同一物体是不一样的)
  • 光照(不同的光照照射到同一物体上反应出来是不一样的)
  • 尺寸(从不同的距离看物体大小是不一样的)
  • 遮挡
  • 形变(比如同一只猫趴着和站着是不一样的)
  • 背景杂波(在背景和物体有相似性,背景对识别进行了干扰)
  • 类内形变(同一类物体具有不同的形状,比如各种各样的凳子)
  • 运动模糊(一个像素记录了多个像素的值)
  • 类别繁多

基于规则的编码方式是否可行

硬编码:把人的理解写成代码,通过硬编码的方法识别物体是一件很困难的事情;

什么是数据驱动的图像分类范式

  • 数据集构建(很多猫、狗等的图片和类别标签)
  • 分类器设计和学习(从数据中学习)
  • 分类器决策

图像表示

  • 像素表示(RGB)
  • 全局特征表示(如:GIST,从图像中抽出频率特征,适用于大场景)
  • 局部特征表示(适应于遮挡,用几个区块特征,即使某个区块被遮挡,也可以用别的区块下结论)

分类器

  • 近邻分类器
  • 贝叶斯分类器
  • 线性分类器
  • 支持向量机分类器
  • 神经网络分类器
  • 随机森林
  • Adaboost

损失函数

  • 0-1损失
  • 多类支持向量机损失
  • 交叉熵损失
  • L1损失
  • L2损失

优化方法

一阶和二阶方法都是迭代优化法。

  • 一阶优化方法
    • 随机下降法
    • 随机梯度下降
    • 小批量随机梯度下降
  • 二阶方法
    • 牛顿法
    • BFGS
    • L-BFGS

训练过程

  • 数据集划分(训练集、测试集和验证集)
  • 数据预处理
  • 数据增强(旋转、裁剪)
  • 过拟合和欠拟合(过拟合:在数据集上表现很好,在实际中效果很差;欠拟合:怎么训练都不行,模型太简单而事情太复杂,怎么训练都搞不定)
    • 减少算法复杂度
      • 使用权重正则项
      • 使用droput正则化
  • 超参数调整(超参数:确定的参数,比如神经元层数,个数;参数:训练过程中确定的数)
  • 模型集成(通过运用多个模型,使系统性能更好)

常用的分类任务评价指标是什么

正确率 = 分对的样本数/全部样本数
错误率 = 1 - 正确率
Top1指标与Top5指标

  • top1:分类结果中第一个正确的才是正确的;
  • top5:分类结果中前五个有一个正确的才是正确的;

  1. 两个数组相加

    1
    2
    3
    4
    ya = np.array([1,2,3])
    b = np.array([1,2,3])
    c = a + b
    print(c)
  2. 生成np数组

    1
    2
    3
    a = [1,2,3]
    b = np.array(a)
    print(b)
  3. 生成矩阵

    1
    2
    3
    4
    5
    6
    7
    # 生成10*10 元素全为0的矩阵
    array_one = np.zeros([10,10])
    print(array_one)
    # 生成10*10 元素全为1的矩阵
    array_one = np.ones([10,10])
    print(array_one)
    # 只能生成0,1其余数字error
  4. 生成数组

    1
    2
    3
    # 生成一个0~10,每次递增2的数组,不包括10
    a = np.arange(0,10,2)
    print(a)
  5. 随机数组的创建

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    # 该函数可以保证生成的随机数具有可预测性,seed里的值相同,生成的随机数相同
    np.random.seed(10)
    # 均匀分布
    # 生成一个范围的整数
    a = np.random.randint(0,100)
    # 生成一个10*10且范围在0~1内的数
    b = np.random.rand(10,10)
    # 生成一个范围内的数
    c = np.random.uniform(0,100)
    # 正态分布

  1. 创建舞台
    1
    let stage = new createjs.Stage();//()中为画布的id,与画布进行绑定
  2. 创建容器
    1
    2
    3
    let gameView = new createjs.Container();
    stage.addChild(gameView);//可将容器放在舞台中,创建的元素放在容器中
    stage > container > 单个元素//三代之间的关系,可通过addChild()方法进行追加元素
  3. 创建图形
    1
    2
    3
    4
    let shape = new createjs.Shape();//创建一个图形
    shape.graphics.beginFill(color)drawRect(x,y,w,h);//画一个矩形
    shape.graphics.beginFill(color)drawRoundRect(x,y,w,h,radius);//画圆角矩形
    shape.graphics.beginFill(color)drawCircle(x,y,r);//画圆
  4. 创建图片
    1
    let img = new createjs.Bitmap();//括号中为图片路径
  5. 创建文字
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    let text = new createjs.Text(text,font,color);//创建文字
    //属性
    text.lineWidth //设置行宽
    text.lineHeight //设置行高
    text.textAlign //设置对齐方式
    注:
    当设置左对齐的时候,text.x的值为文字的起始点横坐标;
    当设置右对齐的时候,text.x的值为文字的结束点横坐标;
    当设置居中对齐的时候,text.x的值为文字的中间点横坐标;
    当设置lineWidth会出现中文不换行问题见19
  6. 常见属性和方法
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    demo.x //设置起始位置横坐标
    demo.y //设置起始位置纵坐标
    demo.alpha //设置透明度
    demo.x //设置起始位置横坐标
    demo.y //设置起始位置纵坐标
    demo.scaleX //水平变换
    demo.scaleY //垂直变换
    demo.scale //整体变换
    demo.alpha //设置透明度
    demo.mask //设置蒙版
    demo.name //设置名称
    demo.visible //设置元素是否显示
    demo.removeAllChildren(); //移除所有子元素
    parent.addChild(child); //添加子元素
    parent.setChildIndex(child,parent.getNumChildren()-num);//设置层级
    注:
    1. 当alpha为0时不能触发事件;
    2. mask显示的内容为和蒙版重合的部分,为蒙版不追加入img的父元素中;
  7. 雪碧图
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    let data = {
    images: [url],//图片路径
    frames: {width:w,height:h,count:count},//w,h是一张精灵图的实际宽高,count的图片数量
    animations: {
    run: {
    frames:[0,3],//显示哪几张图片,0,3为0~3四张
    speed:0.5 //图片运动速度
    },
    }
    };
    let spriteSheet = new createjs.SpriteSheet(data);
    let animation = new createjs.Sprite(spriteSheet,"run");
    nimation.set({x:x,y:y,scaleX:scaleX,scaleY:scaleY});
  8. 创建动画
    1
    2
    createjs.Tween.get(元素).to({属性:值},时间);//异步执行
    createjs.Tween.get(元素).to({属性:值},时间).call(function(){});//function()和动画同步执行,会在动画结束后执行
  9. 资源预加载
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    $.when(filePreload()).then(function(){});
    //预加载完成后在执行then后面的函数
    function filePreload(){
    //deferred对象的含义就是"延迟"到未来某个点再执行。
    let deferred = new $.Deferred();
    //preload可以方便我们预先加载相关资源
    let queue = new createjs.LoadQueue(false);
    //如果载入声音,必须先注册createjs.Sound
    queue.installPlugin(createjs.Sound);
    //将需要预加载的资源放在manifest数组中
    //url:资源的路径
    //id:给资源重新定义的名字,调用改资源时使用
    let manifest = [
    {id:id,src:url"}
    ];
    //预加载过程中执行的函数
    //当调用某一资源时使用bitmapList[id].clone(),且bitmapList需为全局变量
    queue.on("fileload", function (event) {
    if (event.item.type == "image") {
    bitmapList[event.item.id] = new createjs.Bitmap(event.result);
    }
    }
    }, this);
    //预加载完成后执行的函数
    queue.on("complete", function (event) {
    deferred.resolve();
    }, this);
    queue.loadManifest(manifest);
    return deferred.promise();
    }
  10. 设置旋转(竖屏转成横屏)
    1
    2
    3
    4
    5
    6
    7
    stage.rotation = 90;
    stage.x = screenWidth;
    stage.y = 0;
    let temp = screenWidth;
    screenWidth = screenHeight;
    screenHeight = temp;
    注:等号右侧为竖屏宽高,左侧为横屏宽高
  11. 当使用高清图片时若图片还是不清晰,可适当扩大画布的倍数;
  12. 当使用onload方法且需要传参时,避免异步的影响
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    //如加载图片时,图片没有加载好就获取图片属性会报错
    //若在for循环中,可能计数i会产生影响
    let img = new Image();
    img.src = url;
    img.index = 1;
    img.onload = function(){
    console.log(img.index);
    }
    //可推广
    let obj = {};
    obj.x = 1;
    obg.onload = function(){
    console.log(obj.x);
    }
  13. 动态修改shape图形的参数
    1
    shape.graphics.command.属性 = 值;
  14. 实现container的滚动
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    //主函数
    if(createjs.Touch.isSupported() == true){
    createjs.Touch.enable(stage)
    }
    //设置滚动条件
    let localPos,y;
    gameView.addEventListener('mousedown',function(evt){
    //滑动开始时的位置
    let stageX = evt.stageX;
    let stageY = evt.stageY;
    //将gameView对象从舞台(全局)坐标转换为显示对象的(本地)坐标
    localPos = stage.globalToLocal(stageX,stageY);
    //获取gameview对象的坐标
    y = gameView.y;
    })
    gameView.addEventListener('pressmove',function(evt){
    //gameview.y = 滑动结束时滑动点处的y-滑动开始时滑动点处的y+滑动开始时gameview的y
    let move_y = evt.stageY - localPos.y + y;
    //通过滑动区域y坐标的大小限制滑动区域
    if(move_y >= -(滑动区域.y-显示区域.y) && move_y <= 0){
    gameView.y = move_y;
    }
    })
  15. 全局只需要在入口函数设置tick刷新舞台即可;
  16. 适配不同的手机屏幕可通过长宽比例进行适配;
  17. js合并两个数组
    1
    arr1 = arr1.concat(arr2);
  18. 获取屏幕的宽高
    1
    2
    let screenWidth = window.innerWidth;
    let screenHeight = window.innerHeight;
  19. EaselJS的Text中文不会自动换行的问题
    修改easeljs的p._drawText方法为
    点击
  20. 解决createjs的点击图片跨域问题

    1
    2
    3
    4
    //在图片上面盖上一个形状,使图片不触发单击时间
    var hitArea = new createjs.Shape();
    hitArea.graphics.beginFill("#000").drawRect(0,0,92,92);//这里是图片大小
    bitmap.hitArea = hitArea;

当用createjs做手机游戏开发,通常会遇到游戏横屏的问题,此时难以开发,下面通过一些代码去解决这一问题。

当开发时,可以通过谷歌浏览器打开手机横屏调试界面,进行页面的布局,并在布局前通过下面代码或得手机屏幕的宽高,然后进行适配:

1
2
3
4
5
6
7
8
9
10
//测试
// var gameScale = screenWidth/375;
// gameView.rotation = 90;
// gameView.x = screenWidth;
// gameView.y = 0;
// screenWidth = window.innerHeight;
// screenHeight = window.innerWidth;
//开发
screenWidth = window.innerWidth;
screenHeight = window.innerHeight;

当测试时,可以通过旋转屏幕的方式查看手机横屏后的效果:

1
2
3
4
5
6
7
8
9
10
//测试
var gameScale = screenWidth/375;
gameView.rotation = 90;
gameView.x = screenWidth;
gameView.y = 0;
screenWidth = window.innerHeight;
screenHeight = window.innerWidth;
//开发
//screenWidth = window.innerWidth;
//screenHeight = window.innerHeight;