ARTS-28(,Knowledge Distillation, 计组-函数调用-为什么会发生stackoverflow, 《小强升职记》总结)

Algorithm

Review

Knowledge Distillation

一般地,大模型往往是单个复杂网络或者是若干网络的集合,拥有良好的性能和泛化能力,而小模型因为网络规模较小,表达能力有限。 利用大模型学习到的知识去指导小模型训练,使得小模型具有与大模型相当的性能,但是参数数量大幅降低,从而可以实现模型压缩与加速,就是知识蒸馏与迁移学习在模型优化中的应用。

Hinton 等人最早在文章“Distilling the knowledge in a neural network”中提出了知识蒸馏这个概念,其核心思想是一旦复杂网络模型训练完成,便可以用另一种训练方法从复杂模型中提取出来更小的模型,因此知识蒸馏框架通常包含了一个大模型(被称为 teacher 模型),和一个小模型(被称为 student 模型)。

其框架示意图如下:
在这里插入图片描述
从上图中可以看出,包括一个 teacher model 和一个 student model,teacher model 需要预先训练好,使用的就是标准分类 softmax 损失,但是它的输出使用带温度参数 T 的 softmax 函数进行映射,如下:

在这里插入图片描述
当 T=1 时,就是 softmax 本身。当 T>1,称之为 soft softmax, T 越大,因为输入 zk 产生的概率 f(zk)差异就会越小。

之所以要这么做,其背后的思想是当训练好一个模型之后,模型为所有的误标签都分配了很小的概率。然而实际上对于不同的错误标签,其被分配的概率仍然可能存在数个量级的悬殊差距。这个差距,在 softmax 中直接就被忽略了,但这其实是一部分有用的信息。

训练的时候小模型有两个损失,一个是与真实标签的 softmax 损失,一个是与 teacher model 的蒸馏损失,定义为 KL 散度。

当 teacher model 和 student model 各自的预测概率为 pi, qi 时,其蒸馏损失部分梯度传播如下:
在这里插入图片描述
KL 损失如下:
在这里插入图片描述
可以看出形式非常的简单, 梯度为两者预测概率之差,这就是最简单的知识蒸馏框架。

参考实现:
https://github.com/TropComplique/knowledge-distillation-keras
https://github.com/DushyantaDhyani/kdtf
https://github.com/wentianli/knowledge_distillation_caffe

优化目标驱动的知识蒸馏框架

Hinton 等人提出的框架是在模型最后的预测端,让 student 模型学习到与teacher 模型的知识,这可以称之为直接使用优化目标进行驱动的框架,类似的还有 ProjectionNet
在这里插入图片描述
ProjectionNet同时训练一个大模型和一个小模型,两者的输入都是样本,其中大模型就是普通的 CNN 网络,而小模型会对输入首先进行特征投影。每一个投影矩阵 P 都对应了一个映射,由一个 d-bit 长的向量表示,其中每一个 bit 为 0 或者 1,这是一个更加稀疏的表达。特征用这种方法简化后自然就可以使用更加轻量的网络的结构进行训练。

那么怎么完成这个过程呢?文中使用的是 locality sensitivehashing(LSH)算法,这是一种聚类任务中常用的降维的算法。优化目标包含了 3 部分,分别是大模型的损失,投影损失,以及大模型和小模型的预测损失,全部使用交叉熵,各自定义如下:
在这里插入图片描述
基于优化目标驱动的方法其思想是非常直观,就是结果导向型,中间怎么实现的不关心。

实践
下面我们来对 Hinton 提出的知识蒸馏框架进行实践, 使用的是 Caffe 框架。

(1) 代码实现。
首先,我们需要增加知识蒸馏的相关层,即在 LayerParameter 中增加一个KnowledgeDistillationParameter:

message LayerParameter {
//next available layer-specific ID
optional KnowledgeDistillationParameter knowledge_distillation_param = 147;
}

它包含有一个参数,即温度:

message KnowledgeDistillationParameter {
optional float temperature = 1 [default = 1];
}

(2) 蒸馏层实现

接下来我们具体实现蒸馏层的整个功能,首先添加knowledge_distillation_layer.hpp,它继承损失层 LossLayer:

message LayerParameter {
template <typename Dtype>
class KnowledgeDistillationLayer : public LossLayer<Dtype> {
public:
	explicit KnowledgeDistillationLayer(const LayerParameter& param)
		: LossLayer<Dtype>(param) {}
virtual void LayerSetUp(const vector<Blob<Dtype>*>& bottom,
	const vector<Blob<Dtype>*>& top);
virtual void Reshape(const vector<Blob<Dtype>*>& bottom,
	const vector<Blob<Dtype>*>& top);
virtual inline const char* type() const { return "KnowledgeDistillation"; }
virtual inline int ExactNumBottomBlobs() const { return -1; }
virtual inline int MinBottomBlobs() const { return 2; }
virtual inline int MaxBottomBlobs() const { return 3; }
virtual inline int ExactNumTopBlobs() const { return 1; }

protected:
virtual void Forward_cpu(const vector<Blob<Dtype>*>& bottom,
	const vector<Blob<Dtype>*>& top);
virtual void Backward_cpu(const vector<Blob<Dtype>*>& top,
	const vector<bool>& propagate_down, const vector<Blob<Dtype>*>& bottom);
virtual Dtype get_normalizer(
	LossParameter_NormalizationMode normalization_mode, int valid_count);
	
shared_ptr<Layer<Dtype> > s_softmax_layer_;//内部的 Student SoftmaxLayer
shared_ptr<Layer<Dtype> > t_softmax_layer_;//内部的 Teacher SoftmaxLayer

// KnowledgeDistillationLayer 特有的参数
Dtype T; // 温度参数
Blob<Dtype> s_logit_; // Student 层预测结果
Blob<Dtype> t_logit_;// Teacher 层预测结果
Blob<Dtype> s_prob_;// Student 层预测概率
Blob<Dtype> t_prob_;// Teacher 层预测结果

/// 其他 Caffe 网络层的标准参数
vector<Blob<Dtype>*> s_softmax_bottom_vec_;
vector<Blob<Dtype>*> t_softmax_bottom_vec_;
vector<Blob<Dtype>*> s_softmax_top_vec_;
vector<Blob<Dtype>*> t_softmax_top_vec_;

bool has_ignore_label_;
int ignore_label_;
LossParameter_NormalizationMode normalization_;
int softmax_axis_, outer_num_, inner_num_;
};
}

对于蒸馏层来说,它使用 teacher 层的输出作为真实标签,所以损失不需要传播到 teacher 层, 只需与传播到 student 层。

(3) 蒸馏层使用
一个标准的知识蒸馏层,底层输入 student 和 teacher 的预测结果,高层是它们的散度, 如下:

message LayerParameter {
layer {
name: "KD"
type: "KnowledgeDistillation"
bottom: "student_logits" ##student 层预测
bottom: "teacher_logits" ##teacher 层预测
top: "KL_div" ##KL 散度
include { phase: TRAIN }
knowledge_distillation_param { temperature: 4 } #通常大于 4
loss_weight: 1
}

(4) 训练结果
我们使用 5 层的简单模型做学生模型, mobilenet 做教师模型, 在不同温度参数下在
GHIM 数据集的实验结果如下:
在这里插入图片描述
可以看出每一个蒸馏模型的性能都要优于学生模型, 这说明知识蒸馏确实起到了作用, 具体的模型文件请参考附件, 大家可以自行做更多的实验。

Tips

把被调用函数的指令直接插入在调用处的方法行不通。那我们就换一个思路,能不能把后面要跳回来执行的指令地址给记录下来呢?就像前面讲 PC 寄存器一样,我们可以专门设立一个“程序调用寄存器”,来存储接下来要跳转回来执行的指令地址。等到函数调用结束,从这个寄存器里取出地址,再跳转到这个记录的地址,继续执行就好了。但是在多层函数调用里,简单只记录一个地址也是不够的。我们在调用函数 A 之后,A 还可以调用函数 B,B 还能调用函数 C。这一层又一层的调用并没有数量上的限制。在所有函数调用返回之前,每一次调用的返回地址都要记录下来,但是我们 CPU 里的寄存器数量并不多。像我们一般使用的 Intel i7 CPU 只有 16 个 64 位寄存器,调用的层数一多就存不下了。

最终,计算机科学家们想到了一个比单独记录跳转回来的地址更完善的办法。我们在内存里面开辟一段空间,用栈这个后进先出(LIFO,Last In First Out)的数据结构。栈就像一个乒乓球桶,每次程序调用函数之前,我们都把调用返回后的地址写在一个乒乓球上,然后塞进这个球桶。这个操作其实就是我们常说的压栈。如果函数执行完了,我们就从球桶里取出最上面的那个乒乓球,很显然,这就是出栈。拿到出栈的乒乓球,找到上面的地址,把程序跳转过去,就返回到了函数调用后的下一条指令了。如果函数 A 在执行完成之前又调用了函数 B,那么在取出乒乓球之前,我们需要往球桶里塞一个乒乓球。而我们从球桶最上面拿乒乓球的时候,拿的也一定是最近一次的,也就是最下面一层的函数调用完成后的地址。乒乓球桶的底部,就是栈底,最上面的乒乓球所在的位置,就是栈顶。
在这里插入图片描述
在真实的程序里,压栈的不只有函数调用完成后的返回地址。比如函数 A 在调用 B 的时候,需要传输一些参数数据,这些参数数据在寄存器不够用的时候也会被压入栈中。整个函数 A 所占用的所有内存空间,就是函数 A 的栈帧(Stack Frame)。Frame 在中文里也有“相框”的意思,所以,每次到这里,我都有种感觉,整个函数 A 所需要的内存空间就像是被这么一个“相框”给框了起来,放在了栈里面。而实际的程序栈布局,顶和底与我们的乒乓球桶相比是倒过来的。底在最上面,顶在最下面,这样的布局是因为栈底的内存地址是在一开始就固定的。而一层层压栈之后,栈顶的内存地址是在逐渐变小而不是变大。

如何构造一个 stack overflow?
通过引入栈,我们可以看到,无论有多少层的函数调用,或者在函数 A 里调用函数 B,再在函数 B 里调用 A,这样的递归调用,我们都只需要通过维持 rbp 和 rsp,这两个维护栈顶所在地址的寄存器,就能管理好不同函数之间的跳转。不过,栈的大小也是有限的。如果函数调用层数太多,我们往栈里压入它存不下的内容,程序在执行的过程中就会遇到栈溢出的错误,这就是大名鼎鼎的“stack overflow”。

要构造一个栈溢出的错误并不困难,最简单的办法,就是我们上面说的 Infiinite Mirror Effect 的方式,让函数 A 调用自己,并且不设任何终止条件。这样一个无限递归的程序,在不断地压栈过程中,将整个栈空间填满,并最终遇上 stack overflow。


int a()
{
  return a();
}


int main()
{
  a();
  return 0;
}

除了无限递归,递归层数过深,在栈空间里面创建非常占内存的变量(比如一个巨大的数组),这些情况都很可能给你带来 stack overflow。相信你理解了栈在程序运行的过程里面是怎么回事,未来在遇到 stackoverflow 这个错误的时候,不会完全没有方向了。

除了依靠编译器的自动优化,你还可以在定义函数的地方,加上 inline 的关键字,来提示编译器对函数进行内联。内联带来的优化是,CPU 需要执行的指令数变少了,根据地址跳转的过程不需要了,压栈和出栈的过程也不用了。不过内联并不是没有代价,内联意味着,我们把可以复用的程序指令在调用它的地方完全展开了。如果一个函数在很多地方都被调用了,那么就会展开很多次,整个程序占用的空间就会变大了。

Share

时间管理第一层:记录每日时间开支,认识自己的时间黑洞。你会惊讶地发现,每天的无意义时间很可能在70%以上。

避开时间黑洞的小策略:

1、彻底关闭聊天工具;

2、关掉邮件的到达提醒功能;

3、保持办公环境的干净整洁,保持办公用品的触手可及;

4、给水杯里倒满水;

5、准备一张当天的任务清单,完成一项划掉一项;

6、选一张舒适的椅子;

7、尽量地隔离自己,开启免打扰模式;

8、解决最重要、最迫切的事情;

9、当觉得无法集中精力的时候就先休息一会儿(一般为20分钟左右);

10、设置任务的最后期限。

时间管理第二层:填写一周的时间日志,找出自己工作的高效时间,进行每日工作时段规划,用最高效的时段做最重要的事。如何判断什么是最重要的事?——确定自己的价值观。

【职业价值观的分类:利他主义、美感、智力刺激、成就感、独立性、社会地位、管理、经济报酬、社会交际、安全感、舒适、人际关系、变异性


时间管理第三层:四象限法。第一象限立即去做,第二象限有计划去做,第三象限交给别人去做,第四象限尽量别做。

【用“猴子法则”走出第三象限:把并非自己职责的事情甩给别人

【第二象限工作法:将项目分解成连续性的任务,每个任务有自己的目标、计划时间、负责人、是否达成的记录

【像巴菲特那样投资时间:
1、找到杰出的公司
2、少就是多
3、押大赌注于高概率事件上
4、要有耐心
5、不要担心短期价格波动

【脑袋里只装一件事——你的下一步行动(个人理解层次关系是这样:从第二象限里抽出一个项目,分解成任务,再分解成下一步行动)

【找到“下一步行动”:动词开头;内容清晰;描述结果;设定开始时间、周期和最后期限


时间管理绝招:衣柜整理法-收集、处理、组织、回顾、行动

【收集】
事情分为可以执行和不能执行两种,不能执行的任务有垃圾(一般在收集时就过滤掉)、将来某时/也许(写入专门分类)、参考资料(归档)

可以执行的任务分为:
1、2分钟之内可以搞定的事——立即去做
2、需要多个步骤搞定的项目——分解成任务-下一步行动
3、指派给别人完成的事——把猴子甩到别人身上
4、特定时间做的事——写在日程表里
5、代办事项(例如查看邮箱)——有空的时候搞定
关于工具:
1、收集篮:每日清空,用便利贴或者小记事本记录
2、将来某时/也许清单:用记事本单独记录
3、待办事项清单:记事本记录,随记随完成随删
4、项目清单:用A4大小的清单将项目进行分解
回顾任务的时间:每天第一眼和“每周回顾”

【每周回顾的内容】

1、回顾“将来某时/也许清单”
2、回顾“项目清单”
3、回顾“待办事项清单”
4、重新审视自己的目标:事业目标、财富目标、健康目标、其他目标等

【大卫·艾伦的“六个高度”】

原则:价值观、原则和目标
愿景:3-5年目标(我的目标是什么?哪些人已经达到了我的这个目标?他们是如何达到的?达到这个目标后我的工作和生活会是怎样的?)
目标:一年内的想到达到的阶段性的成果
责任范围:为了将工作上和生活中的角色扮演好,需要执行的任务
任务:所有需要一步以上完成的事情
下一步行动:最细枝末节的事件,逐一完成。(选择下一步行动的标准:重要性、环境/场合、时间、精力)


以道御术:个人成长与目标设定
【正确描述目标】
SMART原则(具体的、可测量的、可实现的、可实行的、时间线)

【目标分解】
1、写出目标
2、为什么要达成目标
3、我为什么可以达成此目标
4、哪些个人、团体、组织对我达成这个目标有帮助?
5、这个目标要在多长时间内完成?
大树分解法:适合长于1年,短于5年的中长期目标
树干:写下实现目标的具体步骤
树枝:在月目标计划表里写下打算如何完成计划步骤
树杈:将月目标计划中的内容细化到每一周
【提高执行力:找到最大的石头、写下来、拒绝第一次失败


关于习惯
培养习惯不用写入任务清单,最好的办法是将习惯列入别人的任务清单,即寻求同事、伴侣的支持,鼓励他们培养相同的习惯;
将工作和生活严格区分开;
已经养成的习惯不用写入清单

写下“我想要养成的好习惯”及“我想要改掉的坏习惯”
1、一次培养一个习惯(3个阶段:刚开始的头三天、三天后到一个月内、一个月以后)
2、和自己谈谈
(1)告诉潜意识这很重要
(2)不断重复愿景
可以写一张习惯培养卡片,内容:
正面:
1)我要培养————
2)培养这个习惯对我来说很重要,因为————
3)我一定能养成这个习惯,因为————
4)如果这个习惯培养成功了,我会————(自我奖励)
5)如果失败了,我会————(重新开始)
背面:
6)如果我有些想放弃:——————(列出自己激励的话)
3、循序渐进制定计划
(1)写下来
(2)循序渐进
(3)简单。只用打勾或者打叉即可
(4)明确、具体
(5)将可能遇到的困难也记下来,写出应对措施
4、将计划公之于众
5、执行计划


每日回顾三问:
1、我觉得今天这几件事处理得很出色:——
2、我觉得今天这几件事青还可以处理得更好:——
3、我觉得自己还可以在这些方面做得更好:——

与自己的沟通:
1、你觉得今天做了哪些能为人脉加分的事情?
2、你今天微笑了吗?
3、今天有没有控制好自己的情绪?
4、你今天虚度光阴了吗?
5、你今天的计划完成得怎么样?
6、今天有哪些收获?

用80%的时间思考,20%的时间做事;
做20%的事情,创造80%的产出;

搞定每日清单上的boss项

unbroken time:
1、做好一切准备工作(泡茶、身体保持舒适、取出记录工具、再想想有什么现在需要做的事情)
2、让自己的心完全静下来(将大脑里的东西剪切出来、尽量切断信息输入的干扰、固定时间段)
3、进行“头脑风暴”
4、一定要重新整理一下思路

unbroken time可以用来:规划、总结和回顾、策划、读书、写作、创意
认真地计划自己的将来
系统地考虑整个项目
从某个高度思考人生
高效地完成手头的任务

追求平衡工作和生活的人最重要的两点:把事做正确、做正确的事

处理突发事件的原则:
1、要事第一
2、委婉而坚决拒绝
3、正确下次机会

调整遭遇“突袭”的情绪:
1、立即放松
2、做好最坏的打算
3、默念“静心口诀”(例如:世界如此美妙,我却如此暴躁……)
4、要改变情绪,先改变自己(假装气定神闲……)
5、积极乐观

原则:
1、不要让突发事件影响情绪
2、重视“再次放入收集篮”的步骤
3、向周围人推荐自己使用的时间管理方法(与人方便,与己方便)

一位和尚问得道高僧,如何得成正果?高僧说,砍柴时只想砍柴,挑水时只想挑水。
看相关管理书也很多了,但把书读薄的最大体会就是一句话。有时从一本二十万字的书中你可能也只得到一句话,就足以受益人生了。
任正非述说自已能够事业做大的原因,指出小时候他的父母在做任何事的时候都专心致志,心有静气。在创业之前,任正非已经具备了创业所需要的所有东西,他等待的只是一个挑战或者说是“唤醒”。
为什么说他在创业之前就具备了创业所需的东西,这要从他的父母说起,任正非说他从来没看到过父亲着急。都说巧妇难为无米之炊,而任正非的母亲程远昭却使无米之炊也可为。她话不多,每做一件事都全心全意。任正非说母亲给他很强的启示:无事心不空,有事心不乱,大事心不畏,小事心不慢。这使得任正非性格中有一种独特的清静。在他看来,多话常常是一种干扰,干扰了那颗做大事需要的静心。
所以正如书中所举例子那样,一次只做一件事。
很多人恰恰相反,在砍柴时想着挑水,在挑水时想着砍柴,这样恰恰恰什么都做不好。心中如乱云飞渡,不从容自然什么也做不好。司马懿在起兵造反前夜,特地安排人去看司马师与司马昭二人当夜的反就,下人回报司马师在床上大睡,而昭翻来过去睡不着,当父亲的下断语:师可成大事。
我们平时最敬佩的就是那些每临大事有静气的人,为什么静?用德国人的话说叫活在当下,用作者的话来说叫一次只做一件事。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值