首页
小游戏
壁纸
留言
视频
友链
关于
Search
1
上海市第八人民医院核酸检测攻略(时间+预约+报告)-上海
299 阅读
2
上海烟花销售点一览表2022-上海
241 阅读
3
新款的 Thinkbook 16+ 值不值得买?-知乎热搜
219 阅读
4
如何看待网传小米 MIUI 13 内置国家反诈中心 APP?-知乎热搜
214 阅读
5
窦唯到底厉害在哪里?-知乎热搜
192 阅读
免费代理IP
免费翻墙节点
文章聚合
掘金
知乎
IT之家
本地宝
观察者网
金山词霸
搜韵网
新华网
其他
登录
/
注册
Search
标签搜索
知乎热搜
IT之家热榜
广州
深圳
北京
观察者网头条
前端
上海
后端
知乎日报
Android
iOS
人工智能
阅读
工具资源
杭州
诗词日历
每日一句
郑州
设计
看啥
累计撰写
129,720
篇文章
累计收到
46
条评论
首页
栏目
免费代理IP
免费翻墙节点
文章聚合
掘金
知乎
IT之家
本地宝
观察者网
金山词霸
搜韵网
新华网
其他
页面
小游戏
壁纸
留言
视频
友链
关于
搜索到
1348
篇与
的结果
2022-10-20
什么是梯度消失?为什么会存在梯度消失问题?-掘金
首先,你需要了解什么是BP神经网络,最简单的一个BP网络模型如下图所示,由输入层、隐藏层、输出层组成。对于BP神经网络,我们之前也说过,它的基本思想就是:学习过程由信号的正向传播和误差的反向传播两个过程组成。从上图BP网络模型中,我们可以看出,在正向传播时,BP网络将样本的特征从输入层进行输入,信号经过各个隐藏层逐层处理后,最后从输出层传出。然后再进行误差的反向传播,这部分图中不可见,它的计算过程是:计算网络的实际输出与期望输出之间的误差,然后将误差信号从最后一层逐层反传,从而获得各个层的误差学习信号,再根据误差学习信号来修正各个层神经元的权值,以此优化网络减小实际输出与期望输出之间的误差。而这个误差就是我们常说的损失函数(代价函数);修正权值优化网络的过程,我们也可以称之为损失函数最小化的过程;将误差逐层反转以此来修正权重w的过程,我们可以称之为梯度下降法。梯度在了解梯度下降法之前,我先来说一下什么是梯度。梯度的本意是一个向量(矢量),表示某一函数在该点处的方向导数沿着该方向取得最大值,即函数在该点处沿着该方向(此梯度的方向)变化最快,变化率最大(为该梯度的模)。梯度定义如下:对于 $f(x_0,...,x_i,...,x_n)$ 上的某一点来说存在很多个 方向导数,梯度的方向是函数 $f(x_0,...,x_i,...x_n)$ 在某一点上增长最快的方向,梯度的模则是该点上 方向导数 的最大值 ,梯度的模等于注意: 梯度是一个向量,即有方向有大小; 梯度的方向是最大 方向导数 的方向; 梯度的值是最大 方向导数 的值 方向导数定义如下:导数和偏导数,都是沿坐标轴正方向讨论函数的变化率。那么当我们讨论函数沿任意方向的变化率时,也就引出了方向导数的定义,即:某一点在某一趋近方向上的导数值。换言之就是,我们不仅要知道函数在坐标轴正方向上的变化率(即偏导数),而且还要设法求得函数在其他特定方向上的变化率,而方向导数就是函数在其他特定方向上的变化率。梯度下降法梯度下降法定义:在变量空间的某一点处,函数沿梯度方向具有最大的变化率,那么在优化代价函数的时候,就可以沿着负梯度方向去减小代价函数的值。代价函数的值越小,说明模型的预测值越接近真实标签的值,模型训练的越好。而代价函数中的预测值 y 是跟神经网络中的参数 w 和 b 相关的,所以我们使用 梯度下降法 来不断优化w和b的值。那么下面我通过一个例子具体讲解梯度下降法是怎么不断优化网络的。这里我先假设一个简单的情况,假设神经网络中只有一个参数w,并且 w 初始值是 -3 ,我们使用梯度下降法来优化 w 的取值,以使得loss值不断减小,那么 参数w和代价函数loss的关系,并且 w=-3 时的梯度 如下图所示。从图中,我们可以看出,当 w 为 -3 时,w 所处位置的梯度是一个负数,但是梯度下降法在优化代价函数的时候,是沿着负梯度方向的去减小代价函数的值,所以负梯度是一个正数,那么 w 的值会变大。梯度下降法的优化公式如下:𝑤 = 𝑤 − 𝜂(𝜕𝑓/𝜕𝑤)其中,学习率 𝜂 一般是一个 大于0 的数,𝜕𝑓/𝜕𝑤 为负数,那么就可以判断出 w 的值会变大。变大的数值 跟学习率大小𝜂有关,也跟 函数f 在 w 处的梯度大小有关。假设 w 变大移动到了 w=2 的位置,我们需要再次计算 w=2 时的梯度,如下图所示当 w=2 时,w 所处位置的梯度是一个正数,所以负梯度是一个负数,那么w的值会变小,从上面公式也可以推断,这里不再过多赘述。我们还可以发现此时loss值会比上一次的loss值小。通过这个例子,我们可以发现不管 w 处于什么位置,当 w 向着负梯度的方向进行移动时,实际上就是向着可以使loss 值减小的方向进行移动。这就有点类似于一个小球在山坡上面,它总是往坡底的方向进行移动,只不过它每一次是移动一步,这个步子的大小会受到学习率和所处位置梯度的大小所影响。梯度消失首先我先给出梯度消失的定义:在梯度下降法中, 随着算法反向的反馈, 梯度会越来越小,最终归零没有变化,但此时并没有收敛到比较好的解,这就是梯度消失的问题。下面我们通过权值调整的公式具体体会一下上面这句话的意思,权值调整公式如下:其中,$ΔW^h$ 表示第h层权值矩阵w的变化,𝜂表示学习率,𝜹_h 表示第h层的学习信号,它的改变会引起权值矩阵的改变。1)输出层的学习信号公式为:其中,T 表示数据的标签值(已知的),表示模型的预测值(正向传播计算得到), 表示激活函数的导数,表示输出层信号的汇总;2)除了输出层外,剩下的网络层的学习信号的公式都为:可以看出,第 h 层的学习信号,跟它下一层 h+1层的学习信号、权值矩阵的转置 以及 相关。那么我们就可有以下结论,激活函数的导数会影响学习信号𝛿 的值(由学习信号公式得出),而学习信号𝛿 的值会影响权值调整Δ𝑊 的值(由权值调整公式得出)。所以如果激活函数的值越大,Δ𝑊的值就越大;如果激活函数的值越小,Δ𝑊的值就越小。现在我们再来看下之前讲解过的三种 S型激活函数 的导数以及其图像。(1)sigmoid函数的导数公式:导数图像为:(2)tanh函数的导数公式:导数图像为:(3)softsign 函数的导数公式:导数图像为:总结:基于这三个 S型激活函数的导数图像,我们可以发现,当 x 取值较大或较小时,这三种激活函数的导数很快就趋向于0,那么这就会使得学习信号接近于0,从而使得权值 Δ𝑊调整 接近于0。Δ𝑊接近于0 那就意味着该层的参数不会发生改变,不能进行优化。参数不能优化,那整个网络就不能再进行学习了,这也就造成了梯度消失(Vanishing Gradient)问题。以上文章来自[掘金]-[欣xy]本程序使用github开源项目RSSHub提取聚合!
2022年10月20日
0 阅读
0 评论
0 点赞
2022-10-20
转转推荐场景EE题解决思路-掘金
1 引言推荐系统的目标主要包含两个方面:Exploitation 和 Exploration在Exploitation中最重要的是 Relevance ( 相关性 ) 的计算,其根本思想是根据用户浏览、观看和收藏的内容等用户行为数据推测该用户可能采取的行动。常见的推荐算法大多是基于针对该目标的优化而展开的。 然而用户行为数据在现实中很可能过少、不足以全面地体现用户的兴趣。这一现象在冷启动等场景中很常见。此时推荐系统还有责任挖掘用户尚未表现出的兴趣,并且避免由于现有行为数据过少而导致推送内容相似性过高的情况。这就需要引入Exploration。2 Exploitation排序环节中相关性探索目前主要以debias为主。2.1 bias引起模型bias的原因很多,主要包括: selection bias用户普遍倾向于自己喜欢或者讨厌的item进行评价,比如豆瓣评分。 exposure bias(sample selection bias)用户只能看到曝光的item并产生交互,但数据中没交互的item不代表用户不喜欢,可能是没曝光,这一点不好区分。 1)推荐系统决定了展现哪些items给用户 2)用户主动找到搜索找到感兴趣的项目 3)用户自身的背景。朋友、地理位置等 4)热门的items更容易被用户看到 conformity bias用户行为会受到他人影响,我们观测到的并不一定是用户真实偏好。 用户偏向于和集体喜好一致,用户看到大众统计数据之前和之后,行为分布有很大不同。 position bias用户在不同位置上的交互倾向和点击偏好不同。用户趋向于选择排位靠前的 items,因此实际发生交互的 item 并不一定相关性很强。许多场景也倾向于将盈利高而用户兴趣较低的item放在前面吸引用户点击,以提升相关item的交互行为,如某度。 2.2解决方案 1)特征输入以position bias为例,在训练时将 position 当作一个特征进行输入,在预测时以一个默认值进行输入,即假设所有item出现在同一位置预测点击率,对比用户偏好。 2)bias Tower单独设置一个shallow tower(Youtube Recsys19)来预测偏置,输入的特征是一些与偏置相关的特征。在最后的 sigmoid 前,将shallow tower的输出结果加到logit中,线上预估时位置偏差特征取值为missing。 3)贪婪算法在预测时将每个item在所有位置都预测一次,再通过贪婪算法寻求最优组合(Deep Position-wise Interaction Network,SIGIR 2021) 3 Exploration此部分通常会被归为重排序阶段,目前以解决多样性为主。行列式点过程DPP算法(Fast Greedy MAP Inference for Determinantal Point Process to Improve Recommendation Diversity,NIPS2018)认为如果两个商品的相似性过高,用户可能点击一个之后对另一个的点击需求就会下降。通过构建矩阵来计算每一个子集的行列式值,该值可以理解为用户对推荐列表的满意程度,受到相关性和多样性两个因素的影响。矩阵可以表示为 $$ L_Y = egin{pmatrix} L_{ii} & L_{ij} L_{ji} & L_{jj} end{pmatrix} $$ 矩阵元素构建如下:$ce{L_{ii} = q_i^2}$$ce{L_{ij} = {alpha} * q_i * q_j * expleft(-frac{D_{ij}}{2sigma^2} ight)}$其中$ce{q_i}$为相关性指标,$ce{D_{ij}}$为多样性指标。$ce{q_i approx p(y_i=1|feature of item i)}$$ce{D_{ij} = distance(item i, item j)in[0, +infty)}$$ce{alpha、sigma}$为超参,当$ce{alpha}$处于0到1之间且$ce{alpha}$变小时,相当于我们整个行列式值被缩小,所以多样性变好;相反,$ce{alpha}$大于1且变大时,多样性变差;alpha=1时为标准高斯径向基函数。该问题的求解明显是个NP-hard问题,因此可用贪婪算法进行求解。同时由于$ce{L_Y}$是半正定矩阵,因此可以通过矩阵分解得:$ce{L_Y=VV^T}$其中$ce{V}$是下三角矩阵。通过以上优化,每次逐步增加一个item,进而获得最终的推荐列表,使得整体求解复杂度从$ce{O(y^3)}$成功降到了$ce{O(y)}$,但实际计算中需保证$ce{L_Y}$矩阵的半正定,论文中给出的方法是若L的特征值为负值,则将该值替换为0。具体求解过程如下:盈利场景则会综合考虑每个item的盈利信息,具体可参考转转商业化OCPC产品护航之路4 总结bias类型很多,但是并不是所有的bias都需要去除,例如电商场景下的流行度这种本身就会影响用户点击的bias,但是这种分析方法能够引导我们更好的进行特征挖掘和目标优化;多样性表面上看会降低推荐列表的相关性,但从实验结果上来看,反倒会对业务指标有促进作用,说明用户对多样性还是有很大的需求。> 转转研发中心及业界小伙伴们的技术学习交流平台,定期分享一线的实战经验及业界前沿的技术话题。 > 关注公众号「转转技术」(综合性)、「大转转FE」(专注于FE)、「转转QA」(专注于QA),更多干货实践,欢迎交流分享~以上文章来自[掘金]-[转转技术团队]本程序使用github开源项目RSSHub提取聚合!
2022年10月20日
0 阅读
0 评论
0 点赞
2022-10-19
CLIP打通文本图像壁垒,为AI图像生成打下基础-掘金
预训练可以看一下(1),预训练阶段使用的是对比学习,我们可以看到是把一组文本和一组图片丢进了一个孪生网络。使用自然语言处理的结果作为监督信息公提供给图像进行学习。图片进入图片编码器获得对应的图片特征$I_i$,文本进入文本编码器获得对应的文本特征$T_j$,之后二者组成矩阵,矩阵对角线上的特征是图文对应的信息,矩阵其他位置的特征是图文不对应的信息,这样我们就可以将对应的信息作为正样本,不对应的信息作为负样本,从而进行对比学习。在这样的设置之下,假设我们一个batch size大小设置为n,那我们就能获得$n$个正样本和$n^2-n$个负样本。这样就不需要图片标签,使用对比学习进行预训练了。但是对比学习需要大量的数据,为此OpenAI也是下了大功夫,收集了4亿个text-image对,并做的极好的数据清晰,才能保证模型最终获得如此优秀的效果(主要原因之一)。分类器因为使用对比学习,所以CLIP是没有分类头的,所以作者得想个办法让它能去做分类。在这里就用到了NLP中的一个方法——“prompt template”。看看图中的(2),用左边不同的词汇(多少类别就是多少词汇)去替换中间方框中的那个句子A photo if a _____ .,把一个单词变为一个句子。之后使用编码器对其进行编码获得特征。其实在这里不是必须将其变为一个句子,单个的单词也是可以进行的,但是会和训练阶段产生较大的差异,所以作者在这里还是麻烦了一点,将其都变为句子。这里怎么变为句子还是有说法的,具体可以看一下这篇文章:然后看图中的(3),在预测阶段,我们拿到一张图之后将其丢进图片编码器,获得一个特征,之后我们使用余弦相似度计算我们获得的句子特征和图片特征,取得最终的分类结果。补充一下,在CLIP,模型训练完之后因为是zero-shot的,所以完全可以摆脱categorical label的限制,(2)中的列表我们可以替换为任意的单词,完全不要纠结于ImageNet的1000类还是3000类,图片也可以是任意出处的图,不拘泥于某一个数据集。总结: 因为其独特的训练方式,所以CLIP将图片和文本语义做到了强关联,每个图片都有非常好的语义特征,所以迁移效果非常好。凭借这一点,CLIP在ImageNet数据集上打败了ResNet101等专门在ImageNet上训练的模型。因为和自然语言处理的结合,CLIP训练出来的模型可以在图片域变化非常大的情况下也获得很好的效果。Learning Transferable Visual Models From Natural Language Supervision少说点在之前的一些模型都需要一些预定义的类,比如cifar-10有10个类,提前定义好的类别会影响模型的泛化性,因为之前的类别中没有设置到这些内容,所以遇到一些新数据可能会出现无法处理等问题。所以这个作者们想做一个泛化能力强的模型,大家就考虑能不能借助自然语言处理,从文本中获得一些监督信号,这样的话监督信号就很丰富了,你只要能用语言描述,我就可以将你作为文本信息标签,将其放到视觉模型中。所以作者就去爬了4亿的数据,使用图片文本多模态做了一个对比学习的预训练。为了验证模型的有效性,作者去30个数据集上进行了多个任务的测试,在不需要任何数据集专门训练的情况下,能和之前的一个完全用有监督方法训练出来的模型取得相同的效果甚至吊打人家。作者在论文里稍微提了一下之前的工作,但是效果不行,总结原因就是他们没有OpenAI的 钞能力。方法概述CLIP方法的核心就是借助自然语言,之前的NLP的transformer和自监督训练兴起之前,很难将其应用到图像这边,但是现在NLP发展起来了,可以使用自监督学习获得上下文语义,使用海量的无标签数据获得又大又好用的模型了。为什么CLIP要用自然语言的监督信号训练视觉模型? 不需要再去标注数据了。在ImageNet上是128万图片及其1000类,但是使用自然语言,就不需要这么麻烦了,你只需要去网上爬到对应的文本数据对。并且现在图片对应的不再是标签,而是变成了文本,这样模型的输入输出更加灵活。 训练过程将图片文本绑定到一起,模型学到的是多模态的特征,更有利于模型的泛化。 …… CV方向的模型都很大,训练起来都很费钱费时间。举个夸张的例子,ResNeXt101-32x46d需要训练33个TPUv3年,如果你用GPU的话可能要算好几个世纪了。之前模型的数据集都没有CLIP大,虽然OpenAI的钞能力是出了名的,但是作者也不敢想象要烧多少亿才能把模型训练起来。所以作者们进行了一系列的尝试,比如图片用CNN,文本用transformer,做一个预测任务。但是要知道预测任务的可能性太多了,比如给我们一张图,做这个图的caption。我们看一下下图,可以得到很多结果,比如: 图上是杨戬。 图上是LolitaAnn的老公。 图中是一个男人的侧脸。 这个男人看起来很悲伤 …… 这种开放式的预测答案实在是太多了。但是如果使用对比学习的训练方法,我们仅需要判断图文能不能搭配起来即可。这样任务就被简化了很多,约束也放宽了很多。看一下下图,作者将预测型的目标函数转化为对比学习的目标函数之后,训练速度一下子提高了很多。绿色的线是CLIP最终选择的对比学习训练方式,比中间橙色的线快了四倍。中间橙色的线是使用词袋模型,将内容都抽取为特征进行匹配,比蓝色的线快了三倍,蓝色的线是原来的预测型的训练方式。最终为了保证训练效率,选择了对比学习。因为OpenAI的这个数据集太大了,所以训练过程中根本不用担心出现过拟合的问题,也不需要做什么数据增强,用的唯一一个数据增强就是一个随机裁剪(random square crop)。因为模型实在是太大了,调参实在是太困难了,所以进行对比学习的时候将对比学习中很重要的一个t temperature parameter作为一个可学习的标量,所以这个参数在模型训练过程中直接就被优化了,不需要再调参了。此外他们使用的图像和文本的编码器都不需要预训练,并且最后做projection的时候没有使用非线性的投射层,使用的是线性的投射层。补充:看过之前我写的对比学习系列的应该知道,在SimCLR工作中因为加了一个非线性的projection之后获得了10个点的提升,但是CLIP的作者在工作过程中发现,多模态中使用线性还是非线性影响并不是很大,对此作者还进行了一个 猜测 ,这种非线性的projection应该只能适用于纯图片的单模态学习。伪代码# image_encoder - ResNet or Vision Transformer # text_encoder - CBOW or Text Transformer # I[n, h, w, c] - minibatch of aligned images # T[n, l] - minibatch of aligned texts # W_i[d_i, d_e] - learned proj of image to embed # W_t[d_t, d_e] - learned proj of text to embed # t - learned temperature parameter # extract feature representations of each modality I_f = image_encoder(I) #[n, d_i] T_f = text_encoder(T) #[n, d_t] # joint multimodal embedding [n, d_e] I_e = l2_normalize(np.dot(I_f, W_i), axis=1) T_e = l2_normalize(np.dot(T_f, W_t), axis=1) # scaled pairwise cosine similarities [n, n] logits = np.dot(I_e, T_e.T) * np.exp(t) # symmetric loss function labels = np.arange(n) loss_i = cross_entropy_loss(logits, labels, axis=0) loss_t = cross_entropy_loss(logits, labels, axis=1) loss = (loss_i + loss_t)/2 伪代码我就不讲了,具体看安安的这篇文章: CLIP模型伪代码详细解析不是我的小号!训练We train a series of 5 ResNets and 3 Vision Transformers. For the ResNets we train a ResNet-50, a ResNet-101, and then 3 more which follow EfficientNet-style model scaling and use approximately 4x, 16x, and 64x the compute of a ResNet-50 . They are denoted as RN50x4, RN50x16, and RN50x64 respectively. For the Vision Transformers we train a ViT-B/32, a ViT-B/16, and a ViT-L/14. We train all models for 32 epochs. We use the Adam optimizer with decoupled weight decay regularization applied to all weights that are not gains or biases, and decay the learning rate using a cosine schedule.训练过程作者是训练了5个ResNet网络和3个Vision Transformer模型,每个模型都训练了32个epoch。使用的是Adam优化器。Initial hyperparameters were set using a combination of grid searches, random search, and manual tuning on the baseline ResNet50 model when trained for 1 epoch. Hyperparameters were then adapted heuristically for larger models due to computational constraints.对于所有的超参数,作者也是做了一些简单的grid search和random search进行手动调整,为了调的快一点,用的都是标准的ResNet50去做的,并且就跑了一个epoch,就突出一个懒得调。更大模型直接摆烂根本不调的。The learnable temperature parameter t was initialized to the equivalent of 0.07 from and clipped to prevent scaling the logits by more than 100 which we found necessary to prevent training instability. We use a very large minibatch size of 32,768.训练的batch size直接拉满,到32768,我只跑过8192的,再次感叹一下OpenAI的钞能力!!!Mixed-precision was used to accelerate training and save memory. To save additional memory, gradient checkpointing, half-precision Adam statistics, and half-precision stochastically rounded text encoder weights were used. The calculation of embedding similarities was also sharded with individual GPUs computing only the subset of the pairwise similarities necessary for their local batch of embeddings.此外作者还用了这么多加速和省内存的tricks,通过这么多的努力才把CLIP训练起来。一番烧钱操作之后,作者最终选定了ViT-L/14并对其进行微调。然后CLIP就定下用这个了,之后我们就确定以及肯定提到图片编码器用的就是ViT-L/
[email protected]
。模型局限性有的人会提出质疑,你这玩意儿效果好,是不是因为你四亿对数据集太大了,把人家其他数据集都涵盖了,所以你的结果才能这么好看?为了防止这样的问题,作者对数据集进行了去重等一系列操作,最后的结论就是 “我的模型就是好用”。吹了半天它多厉害之后我们讲一下模型的局限性。作者说CLIP在很多数据集都能和简单的baseline打成平手的,比如ResNet50,但是要知道ResNet只是baseline,不是SOTA,CLIP目前在很多任务上是打不过SOTA的,所以是一个比较均衡的强大,但是能力不是很拔尖。CLIP的如果继续扩大模型和数据集去追赶SOTA的话,作者预估的是还要扩大1000倍,但是即使对于OpenAI这样的钞能力公司来说也是跑不起的(其他人更别做梦了),所以想靠着扩大模型扩大数据集来改进模型,几乎不太可能。(OpenAI:面子我给各位了,各位不要不识好歹。)另一个缺点是,CLIP在某些数据集上效果也就一般般,比如在细分类上zero-shot就不行,并且CLIP没办法做一些很抽象的东西,也没办法做一些异常检测之类的艰难任务。第三个局限性是虽然泛化能力做的很好,但是如果在做图像推理过程中遇到真正的OOD是无法处理的,并且作者说很尴尬的一个事情是在MNIST中准确率只达到88%,作者找了一下原因,发现是四亿对图片居然没有和MNIST相像的图片,因为MNIST是简单的12345的数字识别嘛,他们的数据集里居然无!!!第四个局限性是虽然可以做分类任务,并且你可以自定义分类的种类和列表,但是还是只能在你给定的类别里做选择。相比而言,直接去生成图像标题,让模型去做生成,这样会更灵活。这一点看出作者还是想秉承OpenAI一贯的工作去做预测任务,但是受限于计算资源没办法去实现,之后作者可能会考虑怎么合并一下对比学习的目标函数和生成式任务的目标函数,将二者的优势结合到一起。第五个局限性是对数据的利用不是很高效,需要大量的数据集去投喂。你想32个epochs,每轮都是4亿图,这就是跑了128亿的图啊!!!DataLoader好辛苦啊……如果能优化数据用量就好了。如果能在这里改进就好了。然后是下有数据的局限性。虽然一直都在说zero-shot,但是为了和其他模型去打一打,不停地在目标数据集上进行优化,不停用27个数据集去做测试,这样就在无形中引入了偏见,也就是说这个模型更偏向于打败这27个数据集。 我摊牌了。然后是他们的数据集是去往上爬的,数据清晰做不好的话可能会带有一些bias,会引发一些伦理问题。很多很复杂的任务和概念,有的你用语言也是无法描述的,如果在下游任务中如果能给一些prompt会更好,但是CLIP做的目标就是zero-shot,你给他一些数据反而效果会变差,所以这也是很奇怪一点。以上文章来自[掘金]-[LolitaAnn在掘金]本程序使用github开源项目RSSHub提取聚合!
2022年10月19日
0 阅读
0 评论
0 点赞
2022-10-19
使用Mask R-CNN模型实现人体关键节点标注-掘金
持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第5天,点击查看活动详情本文分享自华为云社区《使用Mask R-CNN模型实现人体关键节点标注》,作者: 运气男孩。前言ModelArts 是面向开发者的一站式 AI 开发平台,为机器学习与深度学习提供海量数据预处理及交互式智能标注、大规模分布式训练、自动化模型生成,及端-边-云模型按需部署能力,帮助用户快速创建和部署模型,管理全周期 AI 工作流。背景Mask R-CNN是一个灵活开放的框架,可以在这个基础框架的基础上进行扩展,以完成更多的人工智能任务。在本案例中,我们将展示如何对基础的Mask R-CNN进行扩展,完成人体关键节点标注的任务。Mask R-CNN整体架构,它的3个主要网络: backbone网络,用于生成特征图 RPN网络,用于生成实例的位置、分类、分割(mask)信息 head网络,对位置、分类和分割(mask)信息进行训练 在head网络中,有分类、位置框和分割(mask)信息的3个分支,我们可以对head网络进行扩展,加入一个人体关键节点keypoint分支。并对其进行训练,使得我们的模型具备关键节点分析的能力。那么我们的模型结构将如下图所示:head网络中,红色的keypionts分支为新加入的人体关键节点分支MaskRCNN模型的解析可以参考此文章 。本案例的运行环境是 TensorFlow 1.8.0 。keypoints分支在RPN中,我们生成Proposal后,当检测到Proposal的分类为"Person"时,对每个部位的关键点生成一个one-hot掩码,训练的目标最终是得到一个56*56的二值掩码,当中只有一个像素被标记为关键点,其余像素均为背景。对于每一个关键点的位置,进行最小化平均交叉熵损失检测,K个关键点是被独立处理的。人体姿态检测中,人本身可以作为一个目标实例进行分类检测。但是,采取了one-hot编码以后,就可以扩展到coco数据集中被标注的17个人体关键点(例如:左眼、右耳),同时也能够处理非连续型数值特征。COCO数据集中,对人体中17个关键点进行了标注,包括:鼻子,左眼,右眼,左耳,右耳,左肩,右肩,左肘,右肘,左手腕,右手腕,左膝盖,右膝盖,左脚踝,右脚踝,左小腿,右小腿,如下图所示:基础环境准备在使用 ModelArts 进行 AI 开发前,需先完成以下基础操作哦(如有已完成部分,请忽略),主要分为4步(注册–>实名认证–>服务授权–>领代金券): 使用手机号注册华为云账号:点击注册 点此去完成实名认证,账号类型选"个人",个人认证类型推荐使用"扫码认证"。 点此进入 ModelArts 控制台数据管理页面,上方会提示访问授权,点击【服务授权】按钮,按下图顺序操作: 进入 ModelArts 控制台首页,如下图,点击页面上的"彩蛋",领取新手福利代金券!后续步骤可能会产生资源消耗费用,请务必领取。 以上操作,也提供了详细的视频教程,点此查看:ModelArts环境配置在ModelArts中训练Mask R-CNN keypoints模型准备数据和源代码第一步:准备数据集和预训练模型下载完成后,显示如下压缩包解压后,得到data目录,其结构如下:data/ ├── mask_rcnn_coco.h5 ├── annotations │ ├── person_keypoints_train2014.json │ ├── ***.json ├── train2014 │ ├── COCO_train2014_***.jpg └── val2014 ├── COCO_val2014_***.jpg 其中data/mask_rcnn_coco_humanpose.h5为预训练模型,annotations、train2014和val2014为我们提前准备好的最小数据集,包含了500张图片的标注信息。第二步:准备源代码第三步:安装依赖pycocotools我们使用COCO数据集,需要安装工具库pycocotools程序初始化第一步:导入相关的库,定义全局变量第二步:生成配置项我们定义Config类的子类MyTrainConfig,指定相关的参数,较为关键的参数有: __NAME__: Config的唯一名称 __NUM_CLASSIS__: 分类的数量,我们只生成圆形,正方形和三角形,再加上背景,因此一共是4个分类 __IMAGE_MIN_DIM和IMAGE_MAX_DIM__: 图片的最大和最小尺寸,我们生成固定的128x128的图片,因此都设置为128 __TRAIN_ROIS_PER_IMAGE__: 每张图片上训练的RoI个数 __STEPS_PER_EPOCH和VALIDATION_STEPS__: 训练和验证时,每轮的step数量,减少step的数量可以加速训练,但是检测精度降低 第三步:创建数据集对象我们使用封装好的CocoDataset类,生成训练集和验证集。创建模型用"training"模式创建模型对象,并加载预训练模型运行完成后输出下面训练模型Keras中的模型可以按照制定的层进行构建,在模型的train方法中,我们可以通过layers参数来指定特定的层进行训练。layers参数有以下几种预设值: heads:只训练head网络中的分类、mask和bbox回归 all: 所有的layer 3+: 训练ResNet Stage3和后续Stage 4+: 训练ResNet Stage4和后续Stage 5+: 训练ResNet Stage5和后续Stage 此外,layers参数还支持正则表达式,按照匹配规则指定layer,可以调用model.keras_model.summary()查看各个层的名称,然后按照需要指定要训练的层。我们针对不同的layer进行训练,首先,训练head网络中的4个分支:输出结果:然后训练ResNet Stage4和后续Stage最后,对所有layer进行优化,并将训练的模型保存到本地输出结果:使用模型检测图片物体第一步:创建"Inference"模式的模型对象,并加载我们训练好的模型文件第二步:从验证数据集中随机选出一张图片,显式Ground Truth信息输出结果,识别图片如下:第三步:使用模型对图片进行预测,并显示结果最终识别结果:总结使用Mask R-CNN模型实现人体关键节点标注,在head网络中,有分类、位置框和分割(mask)信息的3个分支,我们可以对head网络进行扩展,加入一个人体关键节点keypoint分支。并对其进行训练,使得我们的模型具备关键节点分析的能力。对人体中17个关键点进行了标注,包括:鼻子,左眼,右眼,左耳,右耳,左肩,右肩,左肘,右肘,左手腕,右手腕,左膝盖,右膝盖,左脚踝,右脚踝,左小腿,右小腿,并且取得了不错的效果。点击关注,第一时间了解华为云新鲜技术~以上文章来自[掘金]-[华为云开发者联盟]本程序使用github开源项目RSSHub提取聚合!
2022年10月19日
0 阅读
0 评论
0 点赞
2022-10-19
CV攻城狮入门VIT(vision transformer)之旅——VIT代码实战篇-掘金
在上一篇,我们已经介绍了VIT的原理,是不是发现还挺简单的呢!对VIT原理不清楚的请点击☞☞☞了解详细。🌿🌿🌿那么这篇我将带大家一起来看看VIT的代码,主要为大家介绍VIT模型的搭建过程,也会简要的说说训练过程。 这篇VIT的模型是用于物体分类的,我们选择的例子是花的五分类问题。关于花的分类,我之前也有详细的介绍,是用卷积神经网络实现的,不清楚可以点击下列链接了解详情:基于pytorch搭建AlexNet神经网络用于花类识别 🍁🍁🍁基于pytorch搭建VGGNet神经网络用于花类识别 🍁🍁🍁基于pytorch搭建GoogleNet神经网络用于花类识别 🍁🍁🍁基于pytorch搭建ResNet神经网络用于花类识别 🍁🍁🍁 代码部分依旧参考的是B站霹雳吧啦Wz 的视频 ,强烈推荐大家观看喔,你一定会收获满满!!!🌾🌾🌾如果你看视频中有什么不理解的,可以来这篇文章寻找寻找答案喔。🌼🌼🌼 代码点击☞☞☞获取。🥝🥝🥝 VIT模型构建 这部分我以VIT-Base模型为例为大家讲解,此模型的相关参数如下: Model Patch size Layers Hidden Size MLP size Heads Params VIT-Base 16*16 12 768 3072 12 86M 在上代码之前,我们有必要了解整个VIT模型的结构。关于这点我在上一篇VIT原理详解篇已经为大家介绍过,但上篇模型结构上的一些细节,像Droupout层,Encoder结构等等都是没有体现的,这些只有阅读源码才知道。下面给出整个VIT-Base模型的详细结构,如下图所示: 图片来自于霹雳吧啦Wz 的博客 我们的代码是完全按照上图结构搭建的,但在解读代码之前我觉得很有必要再向大家强调一件事——你看我上文推荐的视频或看我的代码解读都只起到一个辅助的作用,你很难说光靠看就能把这些理解透彻。我当时看视频的时候甚至很难完整的看完一遍,更多的还是靠自己一步一步的调试来看每个操作后维度的变换。 我猜测可能有些同学还不是很清楚怎么在vit_model.py进行调试,其实很简单,只需要创建一个全1的tensor来模拟图片,将其当作输入输入网络即可,即可在vit_model.py文件末尾加上下列代码:if __name__ == '__main__': input = torch.ones(1, 3, 224, 224) # 1为batch_size (3 224 224)即表示输入图片尺寸 print(input.shape) model = vit_base_patch16_224_in21k() #使用VIT_Base模型,在imageNet21k上进行预训练 output = model(input) print(output.shape) 那么下面我们就一步步的对代码进行解读,首先我们先对输入进行Patch_embedding操作,这部分我在理论详解篇有详细的介绍过,其就是采用一个卷积核大小为16*16,步长为16的卷积和一个展平操作实现的,相关代码如下:class PatchEmbed(nn.Module): """ 2D Image to Patch Embedding """ def __init__(self, img_size=224, patch_size=16, in_c=3, embed_dim=768, norm_layer=None): super().__init__() img_size = (img_size, img_size) patch_size = (patch_size, patch_size) self.img_size = img_size self.patch_size = patch_size self.grid_size = (img_size[0] // patch_size[0], img_size[1] // patch_size[1]) self.num_patches = self.grid_size[0] * self.grid_size[1] self.proj = nn.Conv2d(in_c, embed_dim, kernel_size=patch_size, stride=patch_size) self.norm = norm_layer(embed_dim) if norm_layer else nn.Identity() def forward(self, x): B, C, H, W = x.shape assert H == self.img_size[0] and W == self.img_size[1], f"Input image size ({H}*{W}) doesn't match model ({self.img_size[0]}*{self.img_size[1]})." # flatten: [B, C, H, W] -> [B, C, HW] # transpose: [B, C, HW] -> [B, HW, C] x = self.proj(x).flatten(2).transpose(1, 2) x = self.norm(x) return x 其实我觉得我再怎么解释这个代码的效果都不会很好,你只要在这里打上一个断点,这个过程就一目了然了。所以这篇文章可能就更倾向于让大家熟悉一下整个模型搭建的过程,具体细节大家可自行调试!!!🌻🌻🌻 这步结束后,你会发现现在x的维度为(1,196,768)。其中1为batch_size数目,我们之前将其设为1。 接着我们会将此时的x和Class token拼接,相关代码如下:# 定义一个可学习的Class token self.cls_token = nn.Parameter(torch.zeros(1, 1, embed_dim)) # 第一个1为batch_size embed_dim=768 cls_token = self.cls_token.expand(x.shape[0], -1, -1) # 保证cls_token的batch维度和x一致 if self.dist_token is None: x = torch.cat((cls_token, x), dim=1) # [B, 197, 768] self.dist_token为None,会执行这句 else: x = torch.cat((cls_token, self.dist_token.expand(x.shape[0], -1, -1), x), dim=1) 同样可以来看看拼接后的维度,如下图: 继续进行下一步——位置编码。位置编码是和上步得到的x进行相加的操作,相关代码如下: # 定义一个可学习的位置编码 self.pos_embed = nn.Parameter(torch.zeros(1, num_patches + self.num_tokens, embed_dim)) #这个维度为(1,197,768) x = x + self.pos_embed 经过位置编码输入的维度并不会发生变换,如下: 位置编码过后,还会经过一个Dropout层,这并不会改变输入维度,相信大家对这个就很熟悉了,就不过多介绍了。 到这里,我们的输入维度为(1,197,768)。接下来就要被送入encoder模块了。首先做了一个Layer Normalization归一化操作,接着会送入Multi-Head Attention部分,然后进行Droppath操作并做一个残差链接。这部分的代码如下:class Block(nn.Module): def __init__(self, dim, num_heads, mlp_ratio=4., qkv_bias=False, qk_scale=None, drop_ratio=0., attn_drop_ratio=0., drop_path_ratio=0., act_layer=nn.GELU, norm_layer=nn.LayerNorm): super(Block, self).__init__() self.norm1 = norm_layer(dim) self.attn = Attention(dim, num_heads=num_heads, qkv_bias=qkv_bias, qk_scale=qk_scale, attn_drop_ratio=attn_drop_ratio, proj_drop_ratio=drop_ratio) # NOTE: drop path for stochastic depth, we shall see if this is better than dropout here self.drop_path = DropPath(drop_path_ratio) if drop_path_ratio > 0. else nn.Identity() self.norm2 = norm_layer(dim) mlp_hidden_dim = int(dim * mlp_ratio) self.mlp = Mlp(in_features=dim, hidden_features=mlp_hidden_dim, act_layer=act_layer, drop=drop_ratio) def forward(self, x): x = x + self.drop_path(self.attn(self.norm1(x))) #🌰🌰🌰上文描述的在这喔🌰🌰🌰 x = x + self.drop_path(self.mlp(self.norm2(x))) #这是encode结构的后半部分 return x 相信你对Layer Normalization已经有相关了解了,不清楚的可以看我对Transfomer讲解的文章,里面有关于此部分的解释,这里不再重复叙述。但是你对Multi-Head Attention是如何实现的可能还存在诸多疑惑,此部代码如下:class Attention(nn.Module): def __init__(self, dim, # 输入token的dim num_heads=8, qkv_bias=False, qk_scale=None, attn_drop_ratio=0., proj_drop_ratio=0.): super(Attention, self).__init__() self.num_heads = num_heads head_dim = dim // num_heads self.scale = qk_scale or head_dim ** -0.5 self.qkv = nn.Linear(dim, dim * 3, bias=qkv_bias) self.attn_drop = nn.Dropout(attn_drop_ratio) self.proj = nn.Linear(dim, dim) self.proj_drop = nn.Dropout(proj_drop_ratio) def forward(self, x): # [batch_size, num_patches + 1, total_embed_dim] B, N, C = x.shape # qkv(): -> [batch_size, num_patches + 1, 3 * total_embed_dim] # reshape: -> [batch_size, num_patches + 1, 3, num_heads, embed_dim_per_head] # permute: -> [3, batch_size, num_heads, num_patches + 1, embed_dim_per_head] qkv = self.qkv(x).reshape(B, N, 3, self.num_heads, C // self.num_heads).permute(2, 0, 3, 1, 4) # [batch_size, num_heads, num_patches + 1, embed_dim_per_head] q, k, v = qkv[0], qkv[1], qkv[2] # make torchscript happy (cannot use tensor as tuple) # transpose: -> [batch_size, num_heads, embed_dim_per_head, num_patches + 1] # @: multiply -> [batch_size, num_heads, num_patches + 1, num_patches + 1] attn = (q @ k.transpose(-2, -1)) * self.scale attn = attn.softmax(dim=-1) attn = self.attn_drop(attn) # @: multiply -> [batch_size, num_heads, num_patches + 1, embed_dim_per_head] # transpose: -> [batch_size, num_patches + 1, num_heads, embed_dim_per_head] # reshape: -> [batch_size, num_patches + 1, total_embed_dim] x = (attn @ v).transpose(1, 2).reshape(B, N, C) x = self.proj(x) x = self.proj_drop(x) return x 光看确实难以发现其中的很多细节,那就尽情的调试吧!!!🌼🌼🌼这部分也不会改变x的尺寸,如下: Multi-Head Attention后还有个Droppath层,其和Dropout类似,但说实话我也没了解过,就当成是一个固定的模块使用了。感兴趣的可以查阅资料。如果有很多人不了解或者我后期会经常用到这个函数的话,我也会出一期Dropout和Droppath区别的教程。这里就靠大家自己啦!!!🍤🍤🍤 下一步同样是一个Layer Normalization层,接着是MLP Block,最后是一个Droppath加一个残差链接。这一部分还值得说的就是这个MLP Bolck了,但其实也非常简单,主要就是两个全连接层,相关代码如下:class Mlp(nn.Module): """ MLP as used in Vision Transformer, MLP-Mixer and related networks """ def __init__(self, in_features, hidden_features=None, out_features=None, act_layer=nn.GELU, drop=0.): super().__init__() out_features = out_features or in_features hidden_features = hidden_features or in_features self.fc1 = nn.Linear(in_features, hidden_features) self.act = act_layer() self.fc2 = nn.Linear(hidden_features, out_features) self.drop = nn.Dropout(drop) def forward(self, x): x = self.fc1(x) x = self.act(x) x = self.drop(x) x = self.fc2(x) x = self.drop(x) return x 需要提醒大家的是上述代码的hidden_features其实就是一开始模型参数中MLP size,即3072。 这样一个encoder Block就介绍完了,接着只需要重复这个Block 12次即可。这部分相关代码如下:self.blocks = nn.Sequential(*[ Block(dim=embed_dim, num_heads=num_heads, mlp_ratio=mlp_ratio, qkv_bias=qkv_bias, qk_scale=qk_scale, drop_ratio=drop_ratio, attn_drop_ratio=attn_drop_ratio, drop_path_ratio=dpr[i], norm_layer=norm_layer, act_layer=act_layer) for i in range(depth) ]) x = self.blocks(x) 注意输入输出这个encoder Block前后,x的维度同样没有发生变化,仍为(1,197,768)。接着会进行Layer Normalization操作。然后要通过切片的方式提取出Class Token,代码如下:if self.dist_token is None: return self.pre_logits(x[:, 0]) #self.dist_token=None 执行此句 else: return x[:, 0], x[:, 1] 你会发现上述代码中会存在一个pre_logits()函数,这个函数其实就是一个全连接层加上一个Tanh激活函数,如下:# Representation layer if representation_size and not distilled: self.has_logits = True self.num_features = representation_size self.pre_logits = nn.Sequential(OrderedDict([ ("fc", nn.Linear(embed_dim, representation_size)), ("act", nn.Tanh()) ])) else: self.has_logits = False self.pre_logits = nn.Identity() 可以发现,这部分不是总存在的。当representation_size=None时,此部分只是一个恒等映射,即什么都不做。关于representation_size何时取何值,我这里做一个简要的说明。当我们的预训练数据集是ImageNet时,representation_size=None,即此时什么都不做;当预训练数据集为ImageNet-21k时,representation_size是一个特定的值,至于是多少是不定的,这和是Base、Large或Huge模型有关,我们这里以Base模型为例,representation_size=768。 经过pre_logits后,还有最后一个全连接层用于最终的分类。相关代码如下:self.head = nn.Linear(self.num_features, num_classes) if num_classes > 0 else nn.Identity() x = self.head(x) 到这里,VIT模型的搭建就全部介绍完啦,看到这里的话,为自己鼓个掌吧👏👏👏 VIT 训练脚本 VIT训练部分和之前我用神经网络搭建的花类识别训练脚本基本是一样的,不清楚的可以先去看看之前的文章。这里我给大家讲讲怎么进行训练。其实你需要修改的地方只有两处,第一是数据集的路径,在代码中设置默认路径如下: parser.add_argument('--data-path', type=str, default="/data/flower_photos") 我们只需要将"/data/flower_photos"修改成我们对应的数据集路径即可。需要注意的是这里路径要指定到flower_photos文件夹,否则检测不到图片,这里和之前讲的还是有点差别的。还有一处你需要修改的地方为预训练权重的位置,代码中默认路径如下:# 预训练权重路径,如果不想载入就设置为空字符 parser.add_argument('--weights', type=str, default='./vit_base_patch16_224_in21k.pth', help='initial weights path') 我们需要将'./vit_base_patch16_224_in21k.pth'换成自己下载预训练权重的地址。需要注意的时这里的预训练权重需要和你创建模型时选择的模型是一样的,即你选择了VIT_Base模型并在ImageNet21k上做预训练,你就要使用./vit_base_patch16_224_in21k.pth的预训练权重。 最后我们训练的权重会保存在当前文件夹下的weights文件夹下,没有这个文件夹会创建一个新的,相关代码如下:torch.save(model.state_dict(), "./weights/model-{}.pth".format(epoch)) VIT分类任务实验结果 这里我们来看看花的五分类训练结果:不使用预训练模型训练10轮:不使用预训练权重训练50轮:使用预训练权重训练10轮: 通过上面的三个实验你可以发现,VIT模型不使用预训练权重进行训练的话效果是非常差的,我们用ResNet网络不使用预训练权重训练50轮大概能达到0.79左右的准确率,而ViT只能达到0.561;但是使用了预训练模型的ResNet达到了0.915,而VIT高达0.971,效果是非常不错的。所以VIT是非常依赖预训练的,且预训练数据集越大,效果往往越好。🥂🥂🥂 最后我们来看看预测部分,下图为检测郁金香的概率: 小结 到这里,VIT代码实战篇就介绍完了。同时CV攻城狮入门VIT(vision transformer)之旅的三篇文章到这里也就告一个段落了,希望大家能够有所收获吧!!!🌾🌾🌾 这里预告一下,后期我打算出Swin Transformer的教程,这个模型才是目前真正霸榜的存在,敬请期待吧!!!🥗🥗🥗 如若文章对你有所帮助,那就🛴🛴🛴 以上文章来自[掘金]-[秃头小苏]本程序使用github开源项目RSSHub提取聚合!
2022年10月19日
0 阅读
0 评论
0 点赞
2022-10-18
为什么ReLU激活函数独受BP神经网络'恩宠'?-掘金
sigmoid函数也被称之为 逻辑函数(logical function),函数公式为sigmoid函数图像如下:从函数图像中我们可以看出 函数的取值范围是 0 - 1 之间 当 x 趋向于 -∞ 的时候函数值趋向于 0; 当 x 趋向于 +∞ 的时候函数值趋向于 1。 (2)tanh 函数tanh函数也被称之为 双曲正切函数,函数公式为tanh函数图像如下:从函数图像中我们可以看出 函数的取值范围是 -1 - 1 之间 当 x 趋向于 -∞ 的时候函数值趋向于 -1; 当 x 趋向于+∞ 的时候函数值趋向于 1。 (3)softsign 函数函数公式为:softsign函数图像如下:从函数图像中我们可以看出 函数的取值范围是 -1 - 1 之间 当 x 趋向于 -∞ 的时候函数值趋向于 -1; 当 x 趋向于 +∞ 的时候函数值趋向于 1。 (4)ReLU 函数ReLU(The Rectified Linear Unit) 函数的公式为: $$ f(x) = max(0 , x) $$ReLU函数图像如下:从函数图像中我们看出,当 x < 0 时,y = 0; 当 x > 0 时,y = x。为什么常用ReLU激活函数?从上面的四种函数图像中,我们可以很明显的看出 sigmoid、tanh、softsign激活函数都是S型函数,形状相似,只不过sigmoid 函数取值范围是 0-1 之间,tanh函数 和 softsign函数 取值范围是 -1 - 1 之间。而ReLU激活函数,首先它整体是非线性的,但是内部对于所有的正值(x > 0),ReLU是线性的(identity),对于所有负值(x < 0),ReLU是零。那么这就意味着:使用ReLU激活函数,不会涉及到特别复杂的数学运算,因此计算开销相比于另外三种激活函数较小,所以使用ReLU激活函数可以花费更少的时间进行训练或预测,收敛的更快。ReLU函数的 阶段性线性特征 意味着当 x 变大时,斜率不会平稳或“饱和” ,那么这就不会出现另外三种激活函数具有的梯度消失或梯度爆炸问题(对于梯度消失或梯度爆炸,我后续会在专门的文章进行讲解)总结以上,就是BP神经网络经常使用ReLU作为激活函数的原因,计算简单、收敛快、无梯度消失问题,后续大家在使用BP神经网络进行训练时,可以直接使用ReLU函数进行训练。以上文章来自[掘金]-[欣xy]本程序使用github开源项目RSSHub提取聚合!
2022年10月18日
0 阅读
0 评论
0 点赞
2022-10-18
两个视角给你解读 熵、交叉熵、KL散度-掘金
熵用于计算一个离散随机变量的信息量。对于一个概率分布$X$,$X$的熵就是它的不确定性。 用大白话来说,假设你预测一个东西,有时候结果会出乎意料,熵就表示出乎意料的程度。熵越大你越不容易预测对,事情就越容易出乎意料。离散型概率分布$X$的熵定义为自信息的平均值: $$ H(X)=E_{p(x)}[I(x)]=-sum_{x} p(x) log p(x) $$注意: 熵的单位可以是比特(bits)也可以是奈特(nats)。二者区别在于前者是用$log_2$计算,后者是用$log_e$计算。我们这里是用$log_2$计算。举个栗子算一下熵。两个城市明天的天气状况如下: 现在有两个事件: A市明天的天气状况 B市明天的天气状况 $H(A)=-0.8 imes log 0.8-0.15 imes log 0.15-0.05 imes log 0.05=0.884$$H(B)=-0.4 imes log 0.4-0.3 imes log 0.3-0.3 imes log 0.3=1.571$可以看到B的熵比A大,因此B城市的天气具有更大的不确定性。交叉熵 Cross-Entropy交叉熵用于度量两个概率分布间的差异性信息。 再用大白话说一下,比如你认为一件事有六成概率能成功,实际上你去做的时候你又八成概率能成功。这时候结果出乎意料的程度就是交叉熵。交叉熵的数学定义:$$ H(A, B)=-Sigma_{i} P_{A}left(x_{i} ight) log left(P_{B}left(x_{i} ight) ight) $$举个栗子算一下交叉熵。改了一下表头。 现在还是有两个事件: $P$实际A城市明天的天气状况 $Q$你以为的A城市的天气状况 $H(P,Q)=-0.8 imes log0.4-0.15 imes log0.3 - 0.05 imes log 0.3 = 1.405$KL散度 Kullback-Leibler divergenceKL散度又称相对熵、信息增益,相对于交叉熵来说,是从另一个角度计算两个分布的差异程度。相对于分布X,分布Y有多大的不同?这个不同的程度就是KL散度。注意,KL散度是不对称的,也就是说X关于Y的KL散度 不等于 Y关于X的KL散度。若 $A$ 和 $B$ 为定义在同一概率空间的两个概率测度,定义 $A$ 相对于 $B$ 的相对熵为 $$ D(A | B)=sum_{x} P_A(x) log frac{P_A(x)}{P_B(x)} $$举个栗子算一下KL散度。还是用这个例子: 现在还是有两个事件: $P$实际A城市明天的天气状况 $Q$你以为的A城市的天气状况 $D(P |Q) = 0.8 imes log(0.8 div0.4) + 0.15 imes log(0.15 div 0.3) + 0.05 imes log(0.0.5div 0.3) =0.521$熵、KL散度和交叉熵的关系我们从上边三个例子中可以看到: A城市明天实际天气状况的熵$H(A)=0.884$ A城市明天实际天气状况和你预测的天气状况的交叉熵为$H(P,Q)=1.405$ A城市明天实际天气状况和你预测的天气状况的KL散度为$D(P |Q) =0.521$ 然后我们可以发现:$0.884+0.521=1.405$这里可以引出一个结论 $$ 熵 + KL散度 = 交叉熵 $$从编码的角度解释注意:下边这个举的例子是能整除的情况下,不能整除的情况下是算不出来的。能整除的例子假设我们现在有一条消息皮皮卡皮,皮卡丘。让我们对这条消息统计一下: 字 皮 卡 丘 , 数量 4 2 1 1 比例 $frac{4}{8}$ $frac{2}{8}$ $frac{1}{8}$ $frac{1}{8}$ 画个哈夫曼树: 字 皮 卡 丘 , 数量 4 2 1 1 比例 $frac{4}{8}$ $frac{2}{8}$ $frac{1}{8}$ $frac{1}{8}$ 哈夫曼编码 0 11 100 101 编码长度 1 2 3 3 最短编码平均长度:$frac{4}{8} imes 1+frac{2}{8} imes 2+frac{1}{8} imes 3+frac{1}{8} imes 3=1.75$上述编码的熵:$-frac{4}{8} imes log frac{4}{8}-frac{2}{8} imes log frac{2}{8}-frac{1}{8} imes log frac{1}{8}-frac{1}{8} imes log frac{1}{8}=1.75$从编码角度看,一串编码的熵等于它的最短编码平均长度。 字 皮 卡 丘 , 数量 4 2 1 1 比例 $frac{4}{8}$ $frac{2}{8}$ $frac{1}{8}$ $frac{1}{8}$ 哈夫曼编码 0 11 100 101 错误的哈夫曼编码 11 0 100 101 如果你编码时候写错了现在的平均编码长度是:$frac{4}{8} imes 2+frac{2}{8} imes 1+frac{1}{8} imes 3+frac{1}{8} imes 3=2$此时交叉熵为:$-frac{4}{8} imes log frac{2}{8}-frac{2}{8} imes log frac{4}{8}-frac{1}{8} imes log frac{1}{8}-frac{1}{8} imes log frac{1}{8}=2$使用错误的编码时候,编码平均长度就是交叉熵。而KL散度呢?$frac{4}{8} imes log(frac{4}{8}divfrac{2}{8})+frac{2}{8} imes log (frac{2}{8} div frac{4}{8})+frac{1}{8} imes log (frac{1}{8} div frac{1}{8})+frac{1}{8} imes log (frac{1}{8} div frac{1}{8})=0.25$KL散度就是错误编码平均长度和正确编码平均长度的差异。不能整除的例子注意:你看,不能整除的情况下是算不出来的。假设我们现在有一条消息皮卡皮卡,皮卡皮,皮卡丘。让我们对这条消息统计一下: 字 皮 卡 丘 , 数量 5 4 1 2 比例 $frac{5}{12}$ $frac{4}{12}$ $frac{1}{12}$ $frac{2}{12}$ 画个哈夫曼树: 字 皮 卡 , 丘 数量 5 4 2 1 比例 $frac{5}{12}$ $frac{4}{12}$ $frac{2}{12}$ $frac{1}{12}$ 哈夫曼编码 0 11 101 100 编码长度 1 2 3 3 最短编码平均长度:$frac{5}{12} imes 1 +frac{4}{12} imes 2+frac{2}{12} imes 3+frac{1}{12} imes 3 = 1.83$上述编码的熵:$-frac{5}{12} imes logfrac{5}{12} -frac{4}{12} imes logfrac{4}{12}-frac{2}{12} imes logfrac{2}{12}-frac{1}{12} imes logfrac{1}{12} = 1.78$后边不算了。可以看到不能整除情况下因为一些误差是不相等的。以上文章来自[掘金]-[LolitaAnn在掘金]本程序使用github开源项目RSSHub提取聚合!
2022年10月18日
3 阅读
0 评论
0 点赞
2022-10-18
如何在 20 天内损坏一个模型? 一个生产环境模型分析教程(Evidently)-掘金
持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第18天,点击查看活动详情假设您训练了一个预测模型,并将其发布到生产环境。现在,您依靠它来做出业务决策。您必须维护、重新训练并密切关注您的模型。它会出现什么问题,以及如何跟踪?让我们来看一个例子。这是一个关于我们如何训练模型、模拟生产环境使用并分析其逐渐退化的故事。任务:自行车需求预测在本教程中,我们将处理需求预测问题。数据集。 我们采用了关于自行车共享需求的 Kaggle 数据集。 我们的目标是预测每小时的自行车租赁量。 为此,我们有一些关于季节、天气和星期几的数据。模型。我们使用从一月份开始的四个星期的数据训练了一个随机森林模型。让我们想象一下,在实践中,我们刚刚开始数据收集,这就是所有可用的数据。训练模型的性能看起来可以接受,所以我们决定试一试。反馈。我们假设我们只在每周结束时才了解ground truth(实际需求)。这是现实世界机器学习中的一个现实假设。集成和更新不同的数据源并不总是那么简单。即使在实际事件发生之后!也许日常使用数据存储在本地,每周只发送和合并一次到数据库中。当您为不同的未来一段时间生成预测时,可能会出现类似的延迟。如果你预测未来一周,那么这个范围将成为您的等待时间。模型检查。由于每周仅提供一次实际数据,因此我们决定每次都运行常规模型分析。没有实时监控。相反,我们安排了一项工作,生成一份标准的每周报表供数据科学家查看。如何分析模型性能?为了在生产中分析我们的模型,我们将使用 Evidently。 它是一个开源工具,可生成有关模型性能的交互式预构建报表。为了运行它,我们将行为数据准备为 Pandas DataFrame。它应该包括: 模型应用日志:模型中的特征和相应的预测; ground truth数据:作为我们“target”的每小时租用自行车的实际数量。 您可以使用此Jupyter notebook 示例按照我们的步骤操作。让我们先看看我们创建的模型的性能。 一旦我们训练了一个模型,我们就会获取我们的训练数据集和预测并将其指定为“Reference”数据。 请继续关注:这也将有助于我们稍后参考这些数据。reference = raw_data.loc['2011-01-01 00:00:00':'2011-01-28 23:00:00'] 我们可以直接从 DataFrame 中选择这个时间段,因为它有 datetime 作为索引。我们还映射列以显示工具是什么并执行正确的分析:target = 'count' prediction = 'prediction' numerical_features = ['temp', 'atemp', 'humidity', 'windspeed', 'hour', 'weekday'] categorical_features = ['season', 'holiday', 'workingday'] 默认情况下,Evidently 使用索引作为绘图中的 x 轴。 在这种情况下,它是日期时间,所以我们不显式添加任何内容。 否则,我们必须在列映射中指定它。接下来,我们调用回归模型的相应报告。regression_perfomance_dashboard = Dashboard(tabs=[RegressionPerformanceTab]) regression_perfomance_dashboard.calculate(reference, None, column_mapping=column_mapping) 并直接在 Jupyter notebook中显示结果。regression_perfomance_dashboard.show() 我们还将其保存为 .html 文件,以便轻松共享。regression_performance_dashboard.save('regression_performance_at_training.html') 我们可以看到模型质量很好,因为我们只训练了四个星期的数据!更多好消息:误差是对称的并且分布在零附近。 没有明显的低估或高估。我们将继续将训练中模型表现的数据集作为我们的"reference"。 它让我们很好地感受到了我们在生产使用中可以从我们的模型中获得的质量。 因此,我们可以将未来的表现与这个基准进行对比。进入自然环境:生产环境的第 1 周在生产环境中观察模型有简单的目标。我们想检测是否有问题。我们还想诊断根本原因并快速了解如何解决它。也许,模型退化得太快了,我们需要更频繁地重新训练它?或许,误差太高,需要对模型进行适配并重建?哪些新模式正在出现?在我们的例子中,我们首先检查模型在训练数据之外的表现如何。我们继续使用Jupyter notebook 示例。出于演示目的,我们在一个批次中生成了未来几周的所有预测。实际上,我们会在数据进入时按顺序运行模型。要选择分析的时间段,我们将在 DataFrame 中指明行。让我们首先将第一周的表现与我们在训练中看到的进行比较。前 28 天是我们的参考数据集;接下来的7个是生产数据集。regression_performance_dashboard.calculate( reference, production.loc['2011-01-29 00:00:00':'2011-02-07 23:00:00'], column_mapping=column_mapping ) 报表出来了!我们可以快速将生产性能与我们的参考性能进行比较。预计会出现一些衰退,但总体上看起来并没有那么糟糕。误差略有增加并且倾向于低估。让我们检查一下我们的目标是否有任何统计变化。 为此,我们将生成目标漂移报表。我们从 Evidently 选项卡调用报告。 在回归模型中,我们的目标是数值,所以我们选择一个匹配的报表:target_drift_dashboard = Dashboard(tabs=[NumTargetDriftTab]) target_drift_dashboard.calculate( reference, production.loc['2011-01-29 00:00:00':'2011-02-07 23:00:00'], column_mapping=column_mapping) 我们可以看到,实际租用自行车数量的分布仍然非常相似。 更准确地说,相似性假设没有被拒绝。 未检测到漂移。我们预测的分布也没有太大变化。尽管如此,一个合理的决定是通过包含新一周的数据来更新您的模型。 这样,模型可以继续学习,我们可能可以改善错误。为了演示,我们将继续看看事情真的出了问题进展得有多快。继续下周!第 2 周:未能保持良好状态再一次,我们将新的一周与参考数据集进行对比。regression_perfomance_dashboard.calculate( reference, production.loc['2011-02-07 00:00:00':'2011-02-14 23:00:00'], column_mapping=column_mapping) 乍一看,第二周的模型表现差别不大。MAE 几乎保持不变。 但是,低估的倾向继续增加。 看来这个错误不是随机的! 平均而言,我们低估了十辆自行车。要了解更多信息,我们将转到图表。 我们可以看到该模型很好地捕捉到了整体的每日趋势。 所以它学到了一些有用的东西! 但是,在高峰时段,实际需求往往高于预期。在误差分布图中,我们可以看到它是如何变得“更宽”的,因为我们有更多具有高误差的预测。 向左的移动也是可见的。 在某些极端情况下,我们有 -80 到 40 辆自行车之间的错误,这是以前看不到的。让我们也检查一下我们的目标漂移。target_drift_dashboard.calculate( reference, production.loc['2011-02-07 00:00:00':'2011-02-14 23:00:00'], column_mapping=column_mapping) 事情变得有趣了!我们可以看到目标分布现在不同了:相似性假设被拒绝了。 从字面上看,人们正在租用更多的自行车。 这与我们的训练时期在统计上是不同的。但是,我们的预测分布未能保持! 这是模型退化的一个明显例子。 世界上发生了一些新的事情,但它错过了模式。进一步调查很诱人。数据中有什么可以解释这种变化吗?如果有一些新信号,重新训练可能会帮助模型保持良好状态。在目标漂移报告中,有一个部分可以帮助我们探索特征与目标(或模型预测)之间的关系。浏览各个特征时,我们可以检查是否发现任何新模式。 我们知道预测没有改变,所以我们只关注与目标的关系。例如,随着租用自行车数量的相应增加,似乎正在向更高的温度(以摄氏度测量)转变。这对模型是新的!也许,它会在重新训练中采用这些模式。 但就目前而言,我们只是继续下周而没有任何更新。第 3 周:当情况变得越来越糟糕时好吧,现在情况看起来确实很糟糕。 在第 3 周,我们面临质量大幅下降。绝对误差和百分比误差均显着增加。如果我们查看这些图,模型预测明显分散。 我们还面临模型无法预测的具有高需求量的新数据段。但即使在目标值的已知范围内,模型现在也会出错。 自从训练以来,情况确实发生了变化。我们可以看到模型不能很好地推断。 预测的需求量保持在相同的已知范围内,而实际值正在达到峰值。如果我们放大特定日期,我们可能会认为该错误在一天中的特定(活跃的)时间更高。 我们从晚上 10 点到早上 6 点做得很好!但是,我们在解释这种模式时应该小心。这也可能是由于其他一些相关因素,例如:在这些相同时间的温度更暖和。Evidently 生成更多图表来显示错误。 在当前情况下,这些是描绘同一个故事的不同方式。 我们有一个很大的错误,而且它明显倾向于低估。在过去的几周中,这些相同模型质量问题的早期迹象已经显现。 随着变化的积累,它们被放大了。回归性能报告还生成了一组洞悉,以深入了解表现不佳的段(segments)。 目标是探索特定的特征范围是否可以解释错误。在我们的示例中,我们特别想了解模型低估了目标函数的段(segments)。误差偏差表提供了更多细节。我们按“Range%”字段对其进行排序。如果特定特征的值在模型低估或高估的组中存在显着差异,则该特征将排名靠前。在我们的例子中,我们可以看到极端误差取决于“temp”(温度)和“atemp”(感觉温度)特征。在训练中,情况并非如此。 我们在不同的温度下有各种各样的错误,没有一致的模式。在这个快速分析之后,我们对模型性能及其弱点有了更具体的了解。 该模型面临着新的、异常高的需求量。 考虑到它是如何训练的,它往往会低估它。 最重要的是,这些错误根本不是随机的。 至少,它们与我们观察到的温度有关。 温度越高,低估的幅度越大。它提出了模型以前无法学习的与天气相关的新模式。 日子越来越暖和了,模型变得离群了。如果我们运行目标漂移报表,我们还将看到特征和目标之间的线性相关性的相关变化。 温度和湿度脱颖而出。在这一点上,该模型似乎无用。这是一个生动的例子,说明在有限数据集上训练的模型如何无法捕捉季节性模式。我们应该尽快重新训练,并经常这样做,直到我们学会所有的模式。 如果我们不习惯频繁的再训练,我们可能会选择更适合时间序列或推理更好的算法。在它崩溃之前:数据和预测漂移在实践中,一旦我们获得了ground truth,我们确实可以快速纠正方向。 如果我们在第一周后重新训练模型,它可能不会那么戏剧性地结束。但是,如果我们没有可用的ground truth怎么办? 我们能提前捕捉到这种退化吗?在这种情况下,我们可以分析数据漂移。 我们不需要实际值来计算误差。 相反,我们的目标是查看输入数据是否发生了变化。再一次,让我们将生产的第一周数据与训练中的数据进行比较。当然,我们可以查看我们所有的特征。 但我们也可以得出结论,分类特征(如“季节”、“假期”和“工作日”)不太可能变化。让我们只看数字特征!我们指定这些特征,以便该工具应用正确的统计测试。 在这种情况下,它将是 Kolmogorov-Smirnov 测试。column_mapping = {} column_mapping['numerical_features'] = numerical_features 然后,我们调用所选时间段的漂移报表:data_drift_dashboard = Dashboard(tabs=[DataDriftTab]) data_drift_dashboard.calculate( reference, production.loc['2011-01-29 00:00:00':'2011-02-07 23:00:00'], column_mapping=column_mapping) 一旦我们显示报表,它就会返回一个答案。 我们可以在第一周看到特征分布的统计变化。让我们放大我们平常的疑点——温度(temperature)。该报表为我们提供了关于特征分布如何随时间演变的两个展示。 我们可以注意到观察到的温度是如何一天天变高的。这些值明显偏离了我们在训练中看到的绿色走廊(与平均值的一个标准偏差)。 从稳定的增长来看,我们可以怀疑有上升的趋势。在这个图表中,我们还看到:天气越来越暖和了。 这不是我们的模型所习惯的!正如我们之前检查的那样,我们在第一周后没有检测到模型预测中的漂移。鉴于我们的模型不擅长推断,我们不应该真的期望它。这种预测漂移可能仍然会发生,并会发出诸如输入数据损坏之类的信号。在其他情况下,如果我们有一个更敏感的模型,我们会观察到它。尽管如此,仅数据漂移本身就可以提供出色的早期监控,以检测变化并对其做出反应。结语频繁的再训练是解决生产模型维护问题的一种方法。监控和可观察性增加了一层以确保模型质量。为什么我们应该将它包含在我们的工作流程中? 它加快了调试速度。每当您的模型失败时,您都需要确定根本原因。预先构建的仪表板使其更快。 它详细显示了性能。如果在交叉验证中只依赖聚合模型的性能,它可能会掩盖重要的模式。您的模型可能会在特定段上静默失败并需要重建。 它有助于改进模型。您可以探索模型在何处以及如何出错。它有助于确定最佳模型架构、重新训练计划、生成特征工程的想法。或者,向您的主题专家提出正确的问题。 它让你积极主动。数据输入的变化可能是模型质量的最重要指标。我们希望在问题导致模型失败之前发现这些问题。 原文链接:How to break a model in 20 days. A tutorial on production model analytics以上文章来自[掘金]-[吃果冻不吐果冻皮]本程序使用github开源项目RSSHub提取聚合!
2022年10月18日
1 阅读
0 评论
0 点赞
2022-10-17
详解核方法-背景介绍【白板推导系列笔记】-掘金
持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第15天,点击查看活动详情核方法相关的概念有三个Kernel Method(从思想角度)、Kernel Trick(从计算角度)、Kernel Function核方法可以用于非线性带来的高维转换(从模型角度),对偶表示带来内积(从优化角度)有时分类数据是完全不可分的,例如异或问题,即数据集为$$left{((0,0),0),((1,1),0),((1,0),1),((0,1),1) ight}$$显然异或问题中的数据不是线性可分的,但我们可以将数据映射到高位空间来实现线性可分,因此我们需要寻找一个非线性的$phi(x)$将低维空间的数据$x$映射到成高维空间的数据$z$,从而实现新的数据集$left{(z,y) ight}$线性可分Cover Theonem:高维比低维更易线性可分$phi(x)$可以是$$x=(x_{1},x_{2})overset{phi(x)}{ ightarrow }z=(x_{1},x_{2},(x_{1}-x_{2})^{2})$$显然在新的空间中,新数据可以实现线性可分在硬间隔SVM中我们将求解问题转化为凸优化问题$$left{egin{aligned}&mathop{ ext{min }}limits_{omega,b} frac{1}{2}omega^{T}omega&s.t.y_{i}(omega^{T}x_{i}+b)geq 1,i=1,2,cdots,Nend{aligned} ight.$$进而转化为其对偶问题$$left{egin{aligned}&mathop{ ext{min }}limits_{lambda} frac{1}{2}sumlimits_{i=1}^{N}sumlimits_{j=1}^{N}lambda_{i}lambda_{j}y_{i}y_{j}x_{i}x_{j}-sumlimits_{i=1}^{N}lambda_{i}&s.t.lambda_{i}geq 0,sumlimits_{i=1}^{N}lambda_{i}y_{i}=0end{aligned} ight.$$如果我们把这里的原数据映射到高维空间实现线性可分,则问题转化为$$left{egin{aligned}&mathop{ ext{min }}limits_{lambda} frac{1}{2}sumlimits_{i=1}^{N}sumlimits_{j=1}^{N}lambda_{i}lambda_{j}y_{i}y_{j}phi(x_{i})^{T}phi(x_{j})-sumlimits_{i=1}^{N}lambda_{i}&s.t.lambda_{i}geq 0,sumlimits_{i=1}^{N}lambda_{i}y_{i}=0end{aligned} ight.$$然而,如果我们将$x$代入$phi(x)$,然后计算点积$phi(x_{i})^{T}phi(x_{j})$,这个计算量是很大的,因此我们引出核函数核函数的定义为$$egin{gathered}forall x,x' in X,exists phi:x mapsto zs.t.K(x,x')=phi^{T}(x)phi(x)=left
2022年10月17日
0 阅读
0 评论
0 点赞
2022-10-17
OpenCV用指针扫描图像-掘金
在大多数图像处理任务中,我们需要扫描图像的所有像素才能执行计算,由于需要访问大量像素,我们必须以高效的方法进行扫描。本节我们将介绍如何使用指针实现高效扫描图像的方法。我们通过完成减少图像中的颜色数量这一任务来说明图像扫描过程。用指针扫描图像彩色图像由三通道像素组成,这些通道中的每一个都对应于红色、绿色和蓝色三种基色之一的强度值。由于这些像素值都是 8 位无符号字符,因此颜色总数为 256 x 256 x 256,超过 1600 万种颜色。因此,为了降低分析的复杂性,减少图像中颜色的数量通常是有效的。实现此目标的一种方法是将 RGB 空间细分为大小相等的立方体。例如,如果我们将每个维度中的颜色数量减少为原来的 1/8,那么可以得到共 32 x 32 x 32 种颜色。此时,原始图像中的每种颜色都会在新的颜色空间中分配一个新的颜色值,该值等于原始颜色值所属的立方体中心的值。因此,基本的色彩量化(色彩量化即为减少图像中颜色数量的过程)算法很简单。如果 N 是缩减因子,则对于图像中的每个像素和该像素的每个通道,将值除以 N (使用整数除法,舍弃余数);然后,将结果乘以 N,此时获得的值与输入像素值之间的差值为 N 的倍数,然后,只需添加 N/2 即可获得 N 的两个相邻倍数间的中心位置。如果对每个 8 位通道值重复此过程,将获得共 256/N x 256/N x 256/N 个可能的颜色值。1. 减色函数的签名如下,函数需要提供图像和每个通道的缩减因子 div 作为参数:void colorReduce(cv::Mat image, int div=64); 此函数使用原地处理,即输入图像的像素值被函数修改。2. 只需创建一个遍历所有像素值的双循环即可完成处理。第一个循环扫描每一行,获取行图像数据的指针:for (int j=0; j
2022年10月17日
0 阅读
0 评论
0 点赞
1
2
...
135