-
Notifications
You must be signed in to change notification settings - Fork 0
/
atom.xml
485 lines (231 loc) · 257 KB
/
atom.xml
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
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
<title>WelCome!</title>
<subtitle>[object Object]</subtitle>
<link href="https://frankho-hwc.github.io/atom.xml" rel="self"/>
<link href="https://frankho-hwc.github.io/"/>
<updated>2023-06-19T15:18:11.678Z</updated>
<id>https://frankho-hwc.github.io/</id>
<author>
<name>Frank Ho</name>
</author>
<generator uri="https://hexo.io/">Hexo</generator>
<entry>
<title>用PPT画神经网络图</title>
<link href="https://frankho-hwc.github.io/2023/06/19/%E7%94%A8PPT%E7%94%BB%E7%A5%9E%E7%BB%8F%E7%BD%91%E7%BB%9C%E5%9B%BE/"/>
<id>https://frankho-hwc.github.io/2023/06/19/%E7%94%A8PPT%E7%94%BB%E7%A5%9E%E7%BB%8F%E7%BD%91%E7%BB%9C%E5%9B%BE/</id>
<published>2023-06-19T02:27:47.000Z</published>
<updated>2023-06-19T15:18:11.678Z</updated>
<content type="html"><![CDATA[<h1 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h1><p> 最近一直在忙着把毕业设计改成一篇会议投出去,其中需要画网络结构图,我在网上找了很多画图的方法,同时也问了老师,最后还是觉得PPT是最合适的画图工具。但是网上关于PPT画神经网络图的帖子和视频讲解较少,所以自己写一篇记录一下:</p><h1 id="网络block"><a href="#网络block" class="headerlink" title="网络block"></a>网络block</h1><p>首先是网络的block,具体步骤如下</p><ul><li><p>先新建一个白色的空白页</p></li><li><p>然后点击插入->形状->基本形状->立方体</p><p><img src="/2023/06/19/%E7%94%A8PPT%E7%94%BB%E7%A5%9E%E7%BB%8F%E7%BD%91%E7%BB%9C%E5%9B%BE/image-20230619230052233.png" alt="image-20230619230052233"></p></li><li><p>然后就是拖出来后,点击形状格式->设置形状格式->三维旋转,进而设置旋转角度</p><p><img src="/2023/06/19/%E7%94%A8PPT%E7%94%BB%E7%A5%9E%E7%BB%8F%E7%BD%91%E7%BB%9C%E5%9B%BE/image-20230619230214879.png" alt="image-20230619230214879"></p><p>我这里给出一个具体的值(因为这个值不好找):X 120 Y 50 Z 0 设置出来的效果如下图所示</p><p><img src="/2023/06/19/%E7%94%A8PPT%E7%94%BB%E7%A5%9E%E7%BB%8F%E7%BD%91%E7%BB%9C%E5%9B%BE/image-20230619230348159.png" alt="image-20230619230348159"></p></li><li><p>如果是要设置对称的另一半(例如U-Net),就进行这样的设置:X 60 Y 50 Z 0,效果如图所示:</p><p><img src="/2023/06/19/%E7%94%A8PPT%E7%94%BB%E7%A5%9E%E7%BB%8F%E7%BD%91%E7%BB%9C%E5%9B%BE/image-20230619230450037.png" alt="image-20230619230450037"></p></li><li><p>连接线则使用插入->形状->线条->任意多边形。选择好后可以自己进行连线,然后弄好后需要修改,则可以右键编辑顶点来操作</p></li><li><p>element-wise addition 和 element-wise multiplication 就从插入 -> 符号内进行寻找即可</p></li></ul><h1 id="图片"><a href="#图片" class="headerlink" title="图片"></a>图片</h1><p>插入图片作为网络input的话,则可以这样设置旋转角度 X 290 Y 25 Z 0</p><p>如果插入的有random crop需要弄的话,这个可以用ImageJ进行操作,这个另开一个来讲。</p><h1 id="导出网络图"><a href="#导出网络图" class="headerlink" title="导出网络图"></a>导出网络图</h1><p>从文件->另存为->选择.jpg,.png等格式进行保存,如果PPT页面太小,就从设计->设置页面大小设置</p><p><img src="/2023/06/19/%E7%94%A8PPT%E7%94%BB%E7%A5%9E%E7%BB%8F%E7%BD%91%E7%BB%9C%E5%9B%BE/image-20230619231131137.png" alt="image-20230619231131137"></p>]]></content>
<summary type="html"><h1 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h1><p> 最近一直在忙着把毕业设计改成一篇会议投出去,其中需要画网络结构图,我在网上找了很多画图的方法,同时也问了老师,最后还是觉得PPT是最合适的</summary>
<category term="深度学习" scheme="https://frankho-hwc.github.io/tags/%E6%B7%B1%E5%BA%A6%E5%AD%A6%E4%B9%A0/"/>
<category term="科研" scheme="https://frankho-hwc.github.io/tags/%E7%A7%91%E7%A0%94/"/>
</entry>
<entry>
<title>个人杂想(I)</title>
<link href="https://frankho-hwc.github.io/2023/06/13/%E4%B8%AA%E4%BA%BA%E6%9D%82%E6%83%B3-I/"/>
<id>https://frankho-hwc.github.io/2023/06/13/%E4%B8%AA%E4%BA%BA%E6%9D%82%E6%83%B3-I/</id>
<published>2023-06-13T03:45:29.000Z</published>
<updated>2023-06-13T05:16:07.416Z</updated>
<content type="html"><![CDATA[<p> 自从开始弄毕设有进展之后,我已经两个多月没有更新blog了。写blog是个好习惯,应该要好好保持。本来是要在四月底弄完公务员面试的时候就该写一篇的,由于太懒了加上毕设要截稿了就作罢了。现在就补上这一篇吧。我觉得这个阶段是最值得大写特写的,因为我觉得我心态的彻底转变也差不多就是从这个时候开始的吧。</p><p>首先是看到自己进面的消息,第一反应那自然是高兴,毕竟证明自己也不是特别差劲。然后就是开始准备材料回家面试了。回去的那天是4月10号,当时先回的崂山盖章,崂山的樱花谢得真快,真是感慨,我都要毕业了。 然后就跑去中山公园,那边樱花还是开的挺灿烂的。游人如织,繁花似锦,似乎这几年就没有发生过啥一般。由于时间关系,我草草地拍了几张就走人了,并未作过多停留。</p><p> 回家之后,我就开始准备交材料,拿面试通知书,一切都是顺其自然。万幸的是,当时还是有两个好兄弟在家里,就分别都找出来聊了聊天,聊了聊未来的打算。在小城市,能有好友跟你聊天也算是一种幸运了,毕竟小城市并不是年轻人的归宿吧。接下来就是准备面试的面试班了,这是改变我心态最关键的地方吧。</p><p>一开始,刚进这个培训班还挺累的。前两天就从早上到晚将面试题目类型,对我来说还是有点吃不消。然后从第三天开始就开始八个八个人组在一起搞“车轮战”,也就是两两对练。跟同组的一开始从不熟到慢慢熟络,我也慢慢敢开口说话了,说话跟不上脑子的毛病好一些了。里面有去年应届进面不中,包括我四个今年应届,一个跟组织部打交道的基层事业编和一个啥工作吧。聊着天来看我的分数还算很高了,还有其他的一些人也是一直考公才进面,突然感觉自己也就没那么菜了。由于是封闭式训练,我住在培训在的酒店。同房间那个老哥是一个快到考公年限的事业编,跟纪委打交道的。从他还有同组那位事业编我看到了其实体制内也不好做。第五天和第七天都有一次实战演练,总的来说发挥还算一般吧。这次培训班,算是体会到了什么叫上可陪玉皇大帝,下可陪卑田院乞儿了。至少能跟三教九流打交道,开阔开阔思路,发现自己也没那么差也是一种好事吧。</p><p>然后就是开始面试,面试的话是七点半之前要进入考点。七点半之后禁止进入。我那个候考室是二十四个人八个岗位,有一个人没来。碰巧的是,同组那位事业编老哥也在。人齐之后就开始抽签,先按照岗位面试顺序抽,然后抽岗位内部的面试顺序。不巧的是,我是第十八个面试,还挺靠后了。所以我就在考场蹭了顿午饭,也算是不亏了。吃完没多久就轮到我去面。面试之前先是考官把你叫出去站在外面等。那天下着雨,暮春的南方居然还能这么冷,站在外面等了十分钟属实把我冻得不轻。进去之后,按照流程就开始面试。四道题,第一道是:江西一方面毗邻长三角、珠三角等经济发达地区,可以承接这些发达省份的资源转移。另一方面,江西也存在人才不足、竞争激烈的情况。对此,请问你怎么看?第二道是:你是某单位的一名年轻干部,对工作十分敬业,并且尚未成家。领导总是让你加班,你觉得这样影响了你的个人生活,你心中有些不乐意的情绪。请问,你会如何与领导进行沟通?请现场模拟。第三道题是:外省兄弟单位要来你们单位考察交流,了解你们乡镇乡村旅游发展情况。领导安排你来负责此项工作,你会怎么处理接待工作?第四道题是:单位领导派你去社区帮助困难群众,你负责收集了群众的困难和问题,对于收集到的问题你接下来会怎么处理?我感觉我第二题答得不太行,其他的都还好。等完分数出来还挺低的,不过我听到前面一个没我高。所以我也就没搭理同岗那个来加微信。考完之后分数太低了,感觉不太行,就回家睡了一觉。第二天就回学校了。</p><p>回到学校,当天寝室四个就煮了个火锅吃,边吃边喝,好不快活。然后就是等成绩,不出意外是没了,第一名太高了,实在是没办法。虽然是这样,但是我觉得也就如此,反正我志不在此,也算是老天首肯了。 说了这么多废话流水账,其实我就是觉得,干啥最主要的还是心态,心态好就啥都好,也不要说害怕失败。有时候其实成功了又怎么样,失败了又怎么样。就跟这次考公一样,考上了就是我想要的生活吗,也不是。没考上我就是菜逼吗,也不是,我至少进面分确实很高,那个上岸的同岗还准备事业编,人家专门准备,我一个准备一个多月的说实话也不错了。所以还是要朝前看,万一考研二战也没了呢,这也是很正常的。还有就是别人进入下一个阶段,我没进入下一个阶段,就有种逃课的负罪感和茫然若失的感觉,其实也没必要。有时候就是不能绷得太紧,橡皮筋绷太紧都会断何况是人呢。所以说,志向上还是要高远,日常生活还是能躺就躺,当然该尽力还是尽力,只是不要一天到晚跟个清教徒一样哈人就是。其他的就没啥好说的了,写这个的时候也是离毕业还差两个星期,目前就先好好毕业吧。</p><p>写于2023年6月13日</p>]]></content>
<summary type="html"><p> 自从开始弄毕设有进展之后,我已经两个多月没有更新blog了。写blog是个好习惯,应该要好好保持。本来是要在四月底弄完公务员面试的时候就该写一篇的,由于太懒了加上毕设要截稿了就作罢了。现在就补上这一篇吧。我觉得这个阶段是最值得大写特写的,因为我觉得我心</summary>
<category term="-杂言" scheme="https://frankho-hwc.github.io/tags/%E6%9D%82%E8%A8%80/"/>
</entry>
<entry>
<title>一点感想</title>
<link href="https://frankho-hwc.github.io/2023/03/26/%E4%B8%80%E7%82%B9%E6%84%9F%E6%83%B3/"/>
<id>https://frankho-hwc.github.io/2023/03/26/%E4%B8%80%E7%82%B9%E6%84%9F%E6%83%B3/</id>
<published>2023-03-26T07:28:23.000Z</published>
<updated>2023-04-01T12:05:17.103Z</updated>
<content type="html"><![CDATA[<p> 转眼间三月就要过去了,离毕业也不过三个月了。这一个月来,我投了几十家公司,也就几家公司给了笔试。然后公务员成绩到现在也没有消息。毕设我本人又不想混,但是不混嘛又进展不顺。这段时间日子煎熬,到现在考研的也开始陆陆续续上岸了。大家都进入了下一个阶段,只有我在这前不着村后不着店,踟蹰不前。但是时间又过得很快,一天一下就过去了,觉得自己啥也没干。现在就两个字,心累。</p><p>我觉得自己并不菜,但是现实证明我现在能力确实不怎么样,也没有人来点拨,只有自己一个在这迷宫里走走停停。能力不足就算了,关键是我什么都想要,什么都抓不住。耐心也慢慢地消磨殆尽了,越来越觉得自己等不起,越来越觉得需要一次成功来给自己一颗定心丸。可就是等不到这个时刻。</p><p>上面是2023年3/26/15:28:23所写,现在这部分是3/26/21:35所写,搞笑的是,我的省考出分了,分数令人满意——135.64分。以我的薄见应该是进面了。其实我并不是很想当公务员,但是上天给了我这个机会,我还是要抓住的。如果上了公务员,我还是要考研,我还是要去追求我的梦想,而不是如井底之蛙拘于一地。最主要是不能沾沾自喜,不能因为这个而放弃梦想。</p><p>不过令人高兴的是,这个至少给了我一点信心,至少说明我还是没那么差的。所以,该是你的就是你的,不要着急,心态放平,一定能做到的。</p><p>————————————————————————-—————</p><p>再次更新于第二天晚,这次出面试名单了,我进面试了。但是下午浪潮的面试很糟糕,很简单的都答不上来,然后hr说没看到我的简历,不过这都无所谓了。先上公务员,然后再图考研上岸,上岸后就开始我的step2.</p><p>——————————————————————————————</p><p>04.01更新</p><p>这几天毕设有一些进展,加上保底工作给二面了,现在心情还算不错。</p>]]></content>
<summary type="html"><p> 转眼间三月就要过去了,离毕业也不过三个月了。这一个月来,我投了几十家公司,也就几家公司给了笔试。然后公务员成绩到现在也没有消息。毕设我本人又不想混,但是不混嘛又进展不顺。这段时间日子煎熬,到现在考研的也开始陆陆续续上岸了。大家都进入了下一个阶段,只有我在这前不着村后不着店</summary>
<category term="碎碎念" scheme="https://frankho-hwc.github.io/tags/%E7%A2%8E%E7%A2%8E%E5%BF%B5/"/>
</entry>
<entry>
<title>一些题目</title>
<link href="https://frankho-hwc.github.io/2023/03/26/%E4%B8%80%E4%BA%9B%E9%A2%98%E7%9B%AE/"/>
<id>https://frankho-hwc.github.io/2023/03/26/%E4%B8%80%E4%BA%9B%E9%A2%98%E7%9B%AE/</id>
<published>2023-03-26T02:18:11.000Z</published>
<updated>2023-03-26T02:24:03.167Z</updated>
<content type="html"><![CDATA[<h1 id="请问如下程序输出结果"><a href="#请问如下程序输出结果" class="headerlink" title="请问如下程序输出结果"></a>请问如下程序输出结果</h1><figure class="highlight c++"><table><tr><td class="code"><pre><span class="line"><span class="function"><span class="type">int</span> <span class="title">main</span><span class="params">(<span class="type">int</span> argc, <span class="type">char</span> * argv[])</span></span></span><br><span class="line"><span class="function"></span></span><br><span class="line"><span class="function"></span>{</span><br><span class="line"></span><br><span class="line"> <span class="type">int</span> a[] = { <span class="number">1</span>,<span class="number">2</span>,<span class="number">3</span>,<span class="number">4</span>,<span class="number">5</span> };</span><br><span class="line"></span><br><span class="line"> <span class="type">int</span>* ptr = (<span class="type">int</span>*)(&a + <span class="number">1</span>);</span><br><span class="line"></span><br><span class="line"> <span class="built_in">printf</span>(<span class="string">"%d,%d"</span>, *(a + <span class="number">1</span>), *(ptr - <span class="number">1</span>));</span><br><span class="line"></span><br><span class="line"> <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line"></span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>答案 2,5</p><p>int (*p) [5] = &a;<br>p是一个指针,它指向的是一个包含5个int元素的数组!!</p><p>那么执行p+1后,p的偏移量相当于 p + sizeof(int) * 5 !!</p><p>而程序中强制将指针p转换成一个int* 那么 p -1 其实就是 p - sizeof(int)<br>所以,p -1 指向了数组中得最后一个元素,也就是 5</p><p>[(44条消息) int a<a href="https://blog.csdn.net/weixin_46108954/article/details/105739127">5]={1,2,3,4,5}; int <em>p=(int</em>)(&a+1); printf(“%d”,*(p-1));_str[5] = (1,2,3,4,5)&a+1_匡夆的博客-CSDN博客</a></p><h1 id="在天龙手游里有一个空间系统,玩家发的每条动态都可以有很多条评论,当动态删除时候,其评论也会被随之删除。若用面向对象的方法进行设计,那么类“动态”和类“评论”之间的关系是:"><a href="#在天龙手游里有一个空间系统,玩家发的每条动态都可以有很多条评论,当动态删除时候,其评论也会被随之删除。若用面向对象的方法进行设计,那么类“动态”和类“评论”之间的关系是:" class="headerlink" title="在天龙手游里有一个空间系统,玩家发的每条动态都可以有很多条评论,当动态删除时候,其评论也会被随之删除。若用面向对象的方法进行设计,那么类“动态”和类“评论”之间的关系是:"></a>在天龙手游里有一个空间系统,玩家发的每条动态都可以有很多条评论,当动态删除时候,其评论也会被随之删除。若用面向对象的方法进行设计,那么类“动态”和类“评论”之间的关系是:</h1><p>是组合关系,即将评论变成动态的成员类进行组合</p><h1 id="针对寄存器变量,以下说法不正确的是:"><a href="#针对寄存器变量,以下说法不正确的是:" class="headerlink" title="针对寄存器变量,以下说法不正确的是:"></a>针对寄存器变量,以下说法不正确的是:</h1><ul><li>A 无法取得寄存器变量的地址</li><li>B 寄存器变量访问效率要比普通变量访问效率高</li><li>C 声明寄存器变量时有可能不成功</li><li>D 寄存器变量可以是全局变量</li></ul><p>答案是D,寄存器变量只能在寄存器中</p>]]></content>
<summary type="html"><h1 id="请问如下程序输出结果"><a href="#请问如下程序输出结果" class="headerlink" title="请问如下程序输出结果"></a>请问如下程序输出结果</h1><figure class="highlight c++"><table><tr></summary>
<category term="c++" scheme="https://frankho-hwc.github.io/tags/c/"/>
<category term="工作" scheme="https://frankho-hwc.github.io/tags/%E5%B7%A5%E4%BD%9C/"/>
</entry>
<entry>
<title>春招寄录</title>
<link href="https://frankho-hwc.github.io/2023/03/22/%E6%98%A5%E6%8B%9B%E5%AF%84%E5%BD%95/"/>
<id>https://frankho-hwc.github.io/2023/03/22/%E6%98%A5%E6%8B%9B%E5%AF%84%E5%BD%95/</id>
<published>2023-03-22T13:12:24.000Z</published>
<updated>2023-06-13T03:51:49.325Z</updated>
<content type="html"><![CDATA[<h1 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h1><p>这是本人春招记录帖子,各位可以吸取教训</p><h1 id="自我介绍"><a href="#自我介绍" class="headerlink" title="自我介绍"></a>自我介绍</h1><p>本人考研难民,无项目无实习,竞赛只有一些参与奖,绩点50%以内,可以说拉中之拉。以下就是我的春招经历</p><h1 id="优博讯"><a href="#优博讯" class="headerlink" title="优博讯"></a>优博讯</h1><p>这个是我随便投的一家公司,我以为他们家开发用的是c++。结果他们家hr打电话说他们是java客户端和底层硬件的。然后约了个时间进行hr面。</p><p>这是我第一次面试,虽然只是hr面。这次面试持续了15min不到。这个hr一开始还让我等了一会才开始面的。首先面试没让我自我介绍,我有点意外。她问了这么几个问题</p><ol><li>我的竞赛是否是个人赛</li><li>我的英语口语水平</li><li>然后问我是不是考研失利,是否准备调剂</li><li>看了下籍贯,然后问为什么去深圳发展(他们家就在深圳)?有没有亲戚朋友在深圳?</li><li>问了能不能接受出差</li><li>问了下学习成绩情况,说我成绩不是很好(废话,要不然早保研了,还在这跟你bb),为什么会成绩这么差,然后你的学习能力怎么样</li><li>我的校园经历</li></ol><p>最后她说完之后有对hr提问的环节</p><p>我提了两个问题</p><ol><li><p>薪资待遇</p><p>我提了16-18k。hr回答,不包吃住,但是有一定房补</p></li><li><p>晋升途径</p><p>公司重视校招生,有培养途径。技术岗只能晋升高级技术岗。然后可以有转岗,但是需要进行面试</p></li></ol><p>总的来说我并不是很喜欢这个公司,知乎评价也是拉中拉。幸好他们把我挂了,我也就不需要面这家的技术面了</p><h1 id="funplus"><a href="#funplus" class="headerlink" title="funplus"></a>funplus</h1><p>游戏公司,LPL冠军战队fpx的东家</p><p>笔试环节,后天(3/24)进行,写完复盘</p><h2 id="笔试-更新于3-x2F-24"><a href="#笔试-更新于3-x2F-24" class="headerlink" title="笔试(更新于3/24)"></a>笔试(更新于3/24)</h2><p>开摄像头,手机开小程序(不开摄像头)</p><p>具体题目是7道单选,7道多选,7道填空和一道编程</p><p>主要考的是计算机网络,操作系统,I/O复用,linux指令和数据库,还有一点点算法</p><p>计算机网络主要是这几个</p><ul><li>哪些协议是应用层协议</li><li>TCP断开连接是三次握手(其中一题的错误选项,这题就是选错误)</li><li>还有几个忘记了,想起来再补充</li></ul><p>I/O复用就考了一道,就是考epoll和select的一些性质</p><p>数据库考了一道索引和搜寻平均数的命令</p><p>linux是使用linux命令找出某个txt文件中“warning”的数量</p><p>操作系统考了一个造成死锁的必要条件</p><p>考了一个很简单的概率题</p><p>考了一个手写简单dp,爬楼梯</p><p>考了一个算sizeof的题</p><p>编程题很简单</p><p>题目是,给定一个数组和一个target,让你找出数组中第一个大于target的位置</p><h1 id="联想"><a href="#联想" class="headerlink" title="联想"></a>联想</h1><p>联想给的反馈很快,也是明后两天(3/25,3/26)笔试</p><p>笔试分两部分,行测和技术笔试。行测一般般,技术笔试有两道算法题,不太会。前面的笔试的题有进程锁,sizeof,三次握手。有点不太记得了。</p><h1 id="中信期货"><a href="#中信期货" class="headerlink" title="中信期货"></a>中信期货</h1><p>这家也给了笔试,也是明后天</p><p>这家的笔试就行测,所以不评价</p><h1 id="浪潮"><a href="#浪潮" class="headerlink" title="浪潮"></a>浪潮</h1><p>这家是我拿来保底的,当时随便投的,然后立马给笔试,全是python题。然后这两天给了面试,3/27面试,到时候面试一下就知道了</p><p>面完了,问的问题不算很难,hr面和技术面一块进行。但是我就是不会,而且简历他们没看到(我明明传了的)。问的问题有这几个:</p><p>自我介绍, 我没准备,很结巴</p><p>有什么项目</p><p>有什么校园经历</p><p>对工作地点有什么要求</p><p>技术就是</p><p>python <strong>new</strong> 和__init__的区别,我回答出来了</p><p>python的优势</p><p>python和java的区别</p><p>python字符串如何去掉空格</p><p>mysql的事务</p><p>你了解什么数据库</p><p>你用pandas干了什么,我说用来加上sklearn包做了波士顿房价预测</p><p>然后就这些,就叫我等消息。反正很多我是没答出来的,算了算了,看他情况吧,总会给通知,不行不就不去,行反正也就保底</p><p>——————————————更新于2023.04.01————————</p><p>浪潮在03.31给了2面,给的是12k,没说多少薪,然后就是工作地点是在济南。先保底吧</p><p>——————更新于2023.06.13————————————————</p><p>池子泡麻了,我绷不住了。</p><h1 id="byd"><a href="#byd" class="headerlink" title="byd"></a>byd</h1><p>到现在也没反应</p><p>因为要去学校宣讲的地方去看,他们系统才会有反应。</p><h1 id="小米"><a href="#小米" class="headerlink" title="小米"></a>小米</h1><p>更新于2023.06.13</p><p>其实在2023.05.30就挂了,当时在苏州玩,好久都没看邮箱了。</p><h1 id="oppo"><a href="#oppo" class="headerlink" title="oppo"></a>oppo</h1><p>更新于2023.04.12</p><p>oppo昨天才给的消息,给了三道代码题,选择题给了一些判断图像饱和度,图像色调,红绿色盲的题。还有一道udp,一道自旋锁,两道mysql题。</p><p>三道代码题,一道kmp,一道a+b,一道忘记了</p><p>方向跟我毕设很符合,但是感觉代码题不太行,进去了面技术可能也过不了,先看着吧。</p><h1 id="4399"><a href="#4399" class="headerlink" title="4399"></a>4399</h1><p>给了笔试,03.29笔试。三道代码题,两道了解个人情况的题。代码题还是稀烂。然后说之后给消息。</p><p>更新于2023.04.12</p><p>4399挂了,不出意外,因为确实做的稀烂。</p><h1 id="国遥新天地"><a href="#国遥新天地" class="headerlink" title="国遥新天地"></a>国遥新天地</h1><p>这个就是电话面,过了,武汉,开13k,c++二次开发。</p><p>我推了,没去,直接考研。</p><h1 id="总结"><a href="#总结" class="headerlink" title="总结"></a>总结</h1><p>找了好一会的工作,找来找去也就一个。反思一下,第一是当时无所适从,找工作方法有问题,投递渠道有问题;第二是我太焦虑了,天天看网络的消息,即使今年情况确实烂透了;第三是我找工作学习八股和项目的方式有问题。总之还是确定二战了,家庭支持,本人也有学术追求,那就再来一次吧。这一次,好好来。</p>]]></content>
<summary type="html"><h1 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h1><p>这是本人春招记录帖子,各位可以吸取教训</p>
<h1 id="自我介绍"><a href="#自我介绍" class="headerlin</summary>
<category term="工作" scheme="https://frankho-hwc.github.io/tags/%E5%B7%A5%E4%BD%9C/"/>
<category term="碎碎念" scheme="https://frankho-hwc.github.io/tags/%E7%A2%8E%E7%A2%8E%E5%BF%B5/"/>
</entry>
<entry>
<title>webserver面经</title>
<link href="https://frankho-hwc.github.io/2023/03/21/webserver%E9%9D%A2%E7%BB%8F/"/>
<id>https://frankho-hwc.github.io/2023/03/21/webserver%E9%9D%A2%E7%BB%8F/</id>
<published>2023-03-21T03:03:22.000Z</published>
<updated>2023-03-21T08:28:11.143Z</updated>
<category term="c++" scheme="https://frankho-hwc.github.io/tags/c/"/>
<category term="工作" scheme="https://frankho-hwc.github.io/tags/%E5%B7%A5%E4%BD%9C/"/>
</entry>
<entry>
<title>KL散度</title>
<link href="https://frankho-hwc.github.io/2023/03/21/KL%E6%95%A3%E5%BA%A6/"/>
<id>https://frankho-hwc.github.io/2023/03/21/KL%E6%95%A3%E5%BA%A6/</id>
<published>2023-03-21T02:48:22.000Z</published>
<updated>2023-03-22T13:38:39.074Z</updated>
<content type="html"><![CDATA[<h1 id="前置知识"><a href="#前置知识" class="headerlink" title="前置知识"></a>前置知识</h1><p>信息熵</p><p>什么是信息熵,信息熵就是编码信息平均需要的比特量。<br>$$<br>H(x) = -\sum_{i=1}^np(x_i)\log(p(x_i))<br>$$<br>其中$p(x_i)$表示随机事件$x_i$的概率</p><h1 id="KL散度-Kullback-Leibler"><a href="#KL散度-Kullback-Leibler" class="headerlink" title="KL散度(Kullback-Leibler)"></a>KL散度(Kullback-Leibler)</h1><p>KL散度,又称相对熵。可以用来描述两个概率分布P和Q的差异和相似性,用$D_{KL}(P||Q)$表示<br>$$<br>D_{KL}(P||Q) = \sum_{i = 1}^Np(x_i)(\log p(x_i) - \log q(x_i))<br>$$<br>或者写成如下形式<br>$$<br>D_{KL}(P||Q) = \sum_{i = 1}^Np(x_i)\log(\frac{p(x_i)}{q(x_i)})<br>$$<br>很显然,KL散度越小,说明概率$p$和$q$就越接近,那么估计的概率分布与真实的概率分布就越接近</p><p>KL 散度可以帮助我们选择最优的参数,比如$p(x)$ 是我们需要估计的一个未知的分布,我们无法直接得知 $p ( x )$ 的分布,不过我们可以建立一个分布 $q(x | \theta)$ 去估计 $p(x)$,为了确定参数 $\theta$,虽然我们无法得知$p(x)$ 的真实分布,但可以利用采样的方法,从$p(x)$中采样N个样本,构建如下的目标函数:<br>$$<br>D_{KL}(P||Q) = \sum_{i = 1}^N{\log p(x_i) - \log q(x_i| \theta)}<br>$$<br>我们要预估的参数是$\theta$,而前一项与$\theta$无关,所以只需要优化后一项,而后一项是什么,就是最大似然估计</p><h1 id="性质"><a href="#性质" class="headerlink" title="性质"></a>性质</h1><ul><li>KL散度具有非对称性,即$D_{KL}(P||Q) \neq D_{kL}(Q||P)$</li><li>$D_{KL}(P||Q) \geq 0$,当且仅当P==Q有等号成立</li></ul><h1 id="参考"><a href="#参考" class="headerlink" title="参考"></a>参考</h1><p><a href="https://zhuanlan.zhihu.com/p/100676922">Kullback-Leibler(KL)散度介绍 - 知乎 (zhihu.com)</a></p><p><a href="https://blog.csdn.net/matrix_space/article/details/80550561">(43条消息) 机器学习:Kullback-Leibler Divergence (KL 散度)_kl散度是什么_Matrix_11的博客-CSDN博客</a></p><p><a href="https://blog.csdn.net/Poyunji/article/details/123771660">(43条消息) 机器学习:KL散度详解_Re:coder的博客-CSDN博客</a></p>]]></content>
<summary type="html"><h1 id="前置知识"><a href="#前置知识" class="headerlink" title="前置知识"></a>前置知识</h1><p>信息熵</p>
<p>什么是信息熵,信息熵就是编码信息平均需要的比特量。<br>$$<br>H(x) &#x3D; -\su</summary>
<category term="计算机视觉" scheme="https://frankho-hwc.github.io/tags/%E8%AE%A1%E7%AE%97%E6%9C%BA%E8%A7%86%E8%A7%89/"/>
<category term="深度学习" scheme="https://frankho-hwc.github.io/tags/%E6%B7%B1%E5%BA%A6%E5%AD%A6%E4%B9%A0/"/>
</entry>
<entry>
<title>Webserver问题汇总</title>
<link href="https://frankho-hwc.github.io/2023/03/20/Webserver%E9%97%AE%E9%A2%98%E6%B1%87%E6%80%BB/"/>
<id>https://frankho-hwc.github.io/2023/03/20/Webserver%E9%97%AE%E9%A2%98%E6%B1%87%E6%80%BB/</id>
<published>2023-03-20T03:07:51.000Z</published>
<updated>2023-03-20T03:07:51.577Z</updated>
</entry>
<entry>
<title>I/O多路复用</title>
<link href="https://frankho-hwc.github.io/2023/03/13/I-O%E5%A4%9A%E8%B7%AF%E5%A4%8D%E7%94%A8/"/>
<id>https://frankho-hwc.github.io/2023/03/13/I-O%E5%A4%9A%E8%B7%AF%E5%A4%8D%E7%94%A8/</id>
<published>2023-03-13T02:50:09.000Z</published>
<updated>2023-03-13T09:39:24.457Z</updated>
<content type="html"><![CDATA[<h1 id="什么是I-x2F-O复用"><a href="#什么是I-x2F-O复用" class="headerlink" title="什么是I/O复用"></a>什么是I/O复用</h1><p>单线程或单进程同时监测若干个文件描述符是否可以执行IO操作的能力。</p><h1 id="I-x2F-O复用解决什么问题"><a href="#I-x2F-O复用解决什么问题" class="headerlink" title="I/O复用解决什么问题"></a>I/O复用解决什么问题</h1><p>I/O复用是为了解决</p><h1 id="I-x2F-O模型"><a href="#I-x2F-O模型" class="headerlink" title="I/O模型"></a>I/O模型</h1><h2 id="阻塞I-x2F-O"><a href="#阻塞I-x2F-O" class="headerlink" title="阻塞I/O"></a>阻塞I/O</h2><p>阻塞I/O意味着当我们发起一次IO操作后一直等待成功或失败之后才返回,在这期间程序不能做其它的事情。阻塞IO操作只能对单个文件描述符进行操作。</p><p><img src="/2023/03/13/I-O%E5%A4%9A%E8%B7%AF%E5%A4%8D%E7%94%A8/1102427-20190306142652603-1084445531.png" alt="阻塞式I/O模型"></p><h2 id="非阻塞I-x2F-O"><a href="#非阻塞I-x2F-O" class="headerlink" title="非阻塞I/O"></a>非阻塞I/O</h2><p>非阻塞I/O一般发生在一个for循环中,因为每次IO操作要么是成功的,要么是阻塞的。当发生阻塞的时候,会返回错误EWOULDBLOCK/EAGAIN,然后再根据需求进行下一次for循环操作。这是一种轮询的操作方式,会浪费很多CPU资源。</p><p><img src="/2023/03/13/I-O%E5%A4%9A%E8%B7%AF%E5%A4%8D%E7%94%A8/1102427-20190306142827583-1565779258.png" alt="非阻塞式I/O模型"></p><h2 id="I-x2F-O多路复用"><a href="#I-x2F-O多路复用" class="headerlink" title="I/O多路复用"></a>I/O多路复用</h2><p>IO多路复用在linux系统中可由select,poll,epoll函数完成。下面具体介绍这三种复用方式。</p><p><img src="/2023/03/13/I-O%E5%A4%9A%E8%B7%AF%E5%A4%8D%E7%94%A8/1102427-20190306143002103-1545280521.png" alt="I/O多路复用"></p><h2 id="信号驱动I-x2F-O"><a href="#信号驱动I-x2F-O" class="headerlink" title="信号驱动I/O"></a>信号驱动I/O</h2><p>开启套接字信号驱动I/O功能,通过 sigaction 系统调⽤安装⼀个信号处理函数,该系统函数⽴即返回,不阻塞;<br>数据报准备好后,内核为该进程产⽣⼀个 SIGIO 信号递交给进程;<br>可以在信号处理函数中调⽤ recvfrom 读取数据报,通知主循环数据已准备好待处理;<br>可以⽴即通知主循环,读取数据报。</p><p><img src="/2023/03/13/I-O%E5%A4%9A%E8%B7%AF%E5%A4%8D%E7%94%A8/1102427-20190306143015916-547171186.png" alt="信号驱动I/O"></p><p><strong>步骤</strong>:</p><ol><li>开启套接字信号驱动I/O功能,通过 sigaction 系统调⽤安装⼀个信号处理函数,该系统函数 ⽴即返回,不阻塞; </li><li>数据报准备好后,内核为该进程产⽣⼀个 SIGIO 信号递交给进程; 可以在信号处理函数中调⽤ recvfrom 读取数据报,通知主循环数据已准备好待处理; </li><li>可以⽴即通知主循环,读取数据报。</li></ol><h2 id="异步I-x2F-O"><a href="#异步I-x2F-O" class="headerlink" title="异步I/O"></a>异步I/O</h2><p>⽤户进程告知内核启动某个操作,并由内核在整个操作中完成后通知⽤户进程。</p><p>与信号驱动I/O的区别: </p><pre><code>1. 信号驱动I/O是由内核通知我们何时可以启动⼀个I/O操作 1. 异步I/O是由内核通知我们I/O操作何时完成</code></pre><p><img src="/2023/03/13/I-O%E5%A4%9A%E8%B7%AF%E5%A4%8D%E7%94%A8/1102427-20190306143038604-144299278.png" alt="异步I/O模型"></p><p><strong>步骤</strong>:</p><ol><li>⽤户进程调⽤aio_read函数,给内核传递描述符、缓冲区指针、缓冲区⼤⼩和⽂件 偏移,告诉内核整个操作完成时如何通知我们,然后就⽴刻去做其他事情; </li><li>当内核收到aio_read后,会⽴刻返回,然后内核开始等待数据准备,数据准备好以后,直接把数据拷贝到⽤户空间,然后再通知进程本次IO已经完成。</li></ol><h1 id="Select"><a href="#Select" class="headerlink" title="Select"></a>Select</h1><p>Linux内相关函数如下</p><figure class="highlight c++"><table><tr><td class="code"><pre><span class="line"><span class="comment">/* According to POSIX.1-2001, POSIX.1-2008 */</span></span><br><span class="line"> <span class="meta">#<span class="keyword">include</span> <span class="string"><sys/select.h></span></span></span><br><span class="line"></span><br><span class="line"> <span class="comment">/* According to earlier standards */</span></span><br><span class="line"> <span class="meta">#<span class="keyword">include</span> <span class="string"><sys/time.h></span></span></span><br><span class="line"> <span class="meta">#<span class="keyword">include</span> <span class="string"><sys/types.h></span></span></span><br><span class="line"> <span class="meta">#<span class="keyword">include</span> <span class="string"><unistd.h></span></span></span><br><span class="line"></span><br><span class="line"> <span class="function"><span class="type">int</span> <span class="title">select</span><span class="params">(<span class="type">int</span> nfds, fd_set *readfds, fd_set *writefds,</span></span></span><br><span class="line"><span class="params"><span class="function"> fd_set *exceptfds, <span class="keyword">struct</span> timeval *timeout)</span></span>;</span><br><span class="line"></span><br><span class="line"> <span class="function"><span class="type">int</span> <span class="title">pselect</span><span class="params">(<span class="type">int</span> nfds, fd_set *readfds, fd_set *writefds,</span></span></span><br><span class="line"><span class="params"><span class="function"> fd_set *exceptfds, <span class="type">const</span> <span class="keyword">struct</span> timespec *timeout,</span></span></span><br><span class="line"><span class="params"><span class="function"> <span class="type">const</span> <span class="type">sigset_t</span> *sigmask)</span></span>;</span><br><span class="line"></span><br><span class="line"> <span class="function"><span class="type">void</span> <span class="title">FD_CLR</span><span class="params">(<span class="type">int</span> fd, fd_set *set)</span></span>;</span><br><span class="line"> <span class="function"><span class="type">int</span> <span class="title">FD_ISSET</span><span class="params">(<span class="type">int</span> fd, fd_set *set)</span></span>;</span><br><span class="line"> <span class="function"><span class="type">void</span> <span class="title">FD_SET</span><span class="params">(<span class="type">int</span> fd, fd_set *set)</span></span>;</span><br><span class="line"> <span class="function"><span class="type">void</span> <span class="title">FD_ZERO</span><span class="params">(fd_set *set)</span></span>;</span><br></pre></td></tr></table></figure><h2 id="select-int-nfds-fd-set-readfds-fd-set-writefds"><a href="#select-int-nfds-fd-set-readfds-fd-set-writefds" class="headerlink" title="select(int nfds, fd_set *readfds, fd_set *writefds,"></a>select(int nfds, fd_set *readfds, fd_set *writefds,</h2><pre><code> fd_set *exceptfds, struct timeval *timeout)</code></pre><ul><li>nfds: 被监听的文件描述符总数</li><li>readfds 可读的文件描述符集合</li><li>writefds 可写的文件描述符集合</li><li>exceptfds 异常的文件描述符集合</li><li>timeout select函数超时时间</li></ul><h2 id="FD-CLR"><a href="#FD-CLR" class="headerlink" title="FD_CLR"></a>FD_CLR</h2><p>清除一个文件描述符</p><h2 id="FD-SET"><a href="#FD-SET" class="headerlink" title="FD_SET"></a>FD_SET</h2><p>将一个文件描述符放入组中</p><h2 id="FD-ISSET"><a href="#FD-ISSET" class="headerlink" title="FD_ISSET"></a>FD_ISSET</h2><p>判断一个文件描述符是否在组中</p><h2 id="FD-ZERO"><a href="#FD-ZERO" class="headerlink" title="FD_ZERO"></a>FD_ZERO</h2><p>用于清空文件描述符组</p><h2 id="具体示例"><a href="#具体示例" class="headerlink" title="具体示例"></a>具体示例</h2><figure class="highlight c++"><table><tr><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string"><stdio.h></span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string"><sys/time.h></span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string"><sys/types.h></span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string"><unistd.h></span></span></span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> TIMEOUT 5 <span class="comment">/* select timeout in seconds */</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> BUF_LEN 1024 <span class="comment">/* read buffer in bytes */</span></span></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">main</span> <span class="params">(<span class="type">void</span>)</span> </span>{</span><br><span class="line"> <span class="keyword">struct</span> <span class="title class_">timeval</span> tv;</span><br><span class="line"> fd_set readfds;</span><br><span class="line"> <span class="type">int</span> ret;</span><br><span class="line"> </span><br><span class="line"> <span class="comment">/* Wait on stdin for input. */</span></span><br><span class="line"> <span class="built_in">FD_ZERO</span>(&readfds);</span><br><span class="line"> <span class="built_in">FD_SET</span>(STDIN_FILENO, &readfds);</span><br><span class="line"> <span class="comment">/* Wait up to five seconds. */</span></span><br><span class="line"> tv.tv_sec = TIMEOUT;</span><br><span class="line"> tv.tv_usec = <span class="number">0</span>;</span><br><span class="line"> </span><br><span class="line"> <span class="comment">/* All right, now block! */</span></span><br><span class="line"> ret = <span class="built_in">select</span> (STDIN_FILENO + <span class="number">1</span>, &readfds,</span><br><span class="line"> <span class="literal">NULL</span>,</span><br><span class="line"> <span class="literal">NULL</span>, </span><br><span class="line"> &tv);</span><br><span class="line"> <span class="keyword">if</span> (ret == −<span class="number">1</span>) {</span><br><span class="line"> <span class="built_in">perror</span> (<span class="string">"select"</span>);</span><br><span class="line"> <span class="keyword">return</span> <span class="number">1</span>; </span><br><span class="line"> } <span class="keyword">else</span> <span class="keyword">if</span> (!ret) {</span><br><span class="line"> <span class="built_in">printf</span> (<span class="string">"%d seconds elapsed.\n"</span>, TIMEOUT);</span><br><span class="line"> <span class="keyword">return</span> <span class="number">0</span>; </span><br><span class="line"> }</span><br><span class="line"> <span class="comment">/*</span></span><br><span class="line"><span class="comment"> * Is our file descriptor ready to read?</span></span><br><span class="line"><span class="comment"> * (It must be, as it was the only fd that</span></span><br><span class="line"><span class="comment"> * we provided and the call returned</span></span><br><span class="line"><span class="comment"> * nonzero, but we will humor ourselves.)</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"> <span class="keyword">if</span> (<span class="built_in">FD_ISSET</span>(STDIN_FILENO, &readfds)) {</span><br><span class="line"> <span class="type">char</span> buf[BUF_LEN+<span class="number">1</span>];</span><br><span class="line"> <span class="type">int</span> len;</span><br><span class="line"> <span class="comment">/* guaranteed to not block */</span></span><br><span class="line"> len = <span class="built_in">read</span> (STDIN_FILENO, buf, BUF_LEN);</span><br><span class="line"> <span class="keyword">if</span> (len == −<span class="number">1</span>) {</span><br><span class="line"> <span class="built_in">perror</span> (<span class="string">"read"</span>);</span><br><span class="line"> <span class="keyword">return</span> <span class="number">1</span>; </span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">if</span> (len) {</span><br><span class="line"> buf[len] = <span class="string">'\0'</span>;</span><br><span class="line"> <span class="built_in">printf</span> (<span class="string">"read: %s\n"</span>, buf);</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">return</span> <span class="number">0</span>; </span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="built_in">fprintf</span> (stderr, <span class="string">"This should not happen!\n"</span>);</span><br><span class="line"> <span class="keyword">return</span> <span class="number">1</span>; </span><br><span class="line">}</span><br></pre></td></tr></table></figure><h1 id="poll"><a href="#poll" class="headerlink" title="poll"></a>poll</h1><p>Linux相关函数如下</p><figure class="highlight c++"><table><tr><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string"><poll.h></span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string"><signal.h></span></span></span><br><span class="line"> <span class="function"><span class="type">int</span> <span class="title">poll</span><span class="params">(<span class="keyword">struct</span> pollfd *fds, <span class="type">nfds_t</span> nfds, <span class="type">int</span> timeout)</span></span>;</span><br><span class="line"> <span class="function"><span class="type">int</span> <span class="title">ppoll</span><span class="params">(<span class="keyword">struct</span> pollfd *fds, <span class="type">nfds_t</span> nfds,</span></span></span><br><span class="line"><span class="params"><span class="function"> <span class="type">const</span> <span class="keyword">struct</span> timespec *tmo_p, <span class="type">const</span> <span class="type">sigset_t</span> *sigmask)</span></span>;</span><br><span class="line"></span><br><span class="line"> <span class="keyword">struct</span> <span class="title class_">pollfd</span> {</span><br><span class="line"> <span class="type">int</span> fd; <span class="comment">/* file descriptor */</span></span><br><span class="line"> <span class="type">short</span> events; <span class="comment">/* requested events to watch */</span></span><br><span class="line"> <span class="type">short</span> revents; <span class="comment">/* returned events witnessed */</span> <span class="comment">//即哪个事件是否就绪</span></span><br><span class="line"> };</span><br></pre></td></tr></table></figure><h2 id="poll-struct-pollfd-fds-nfds-t-nfds-int-timeout"><a href="#poll-struct-pollfd-fds-nfds-t-nfds-int-timeout" class="headerlink" title="poll(struct pollfd *fds, nfds_t nfds, int timeout);"></a>poll(struct pollfd *fds, nfds_t nfds, int timeout);</h2><ul><li>fdarray: 为传入的pollfd数组的首地址,该数组中的每一个元素为一个pollfd结构体对象,关联一个管理的描述符fd</li><li>nfds:nfds传入的值为fdarray数组的长度,表示管理的描述符个数。fdarray是一个变长数组,需要指定长度</li><li>timeout poll函数超时时间,有三种取值 无限等待(INFTIM),立即返回不阻塞(0),等待指定的超时时间(timeout)</li></ul><h1 id="poll与select总结"><a href="#poll与select总结" class="headerlink" title="poll与select总结"></a>poll与select总结</h1><table><thead><tr><th>维度</th><th align="left">select</th><th align="left">poll</th></tr></thead><tbody><tr><td>实现</td><td align="left">select底层实现是用<strong>bitmap</strong>进行实现的,一个描述符对应一位</td><td align="left">poll的底层是通过pollfd结构体来实现,管理的描述符通过pollfd数组来组织,一个描述符对应一个pollfd对象</td></tr><tr><td>用法不同</td><td align="left">select默认大小是FD_SETSIZE(1024),修改的话,需要修改配置参数同时重新编译内核来实现</td><td align="left"><strong>poll是采用变长数组</strong>管理的,因此理论上可以支持海量连接</td></tr></tbody></table><h1 id="Epoll"><a href="#Epoll" class="headerlink" title="Epoll"></a>Epoll</h1><h2 id="触发模型"><a href="#触发模型" class="headerlink" title="触发模型"></a>触发模型</h2><h3 id="水平触发-Level-Triggled-LT"><a href="#水平触发-Level-Triggled-LT" class="headerlink" title="水平触发 (Level- Triggled,LT)"></a>水平触发 (Level- Triggled,LT)</h3><ul><li>socket接收缓冲区不空,有数据可读,该事件一直触发</li><li>socket发送缓冲区不满,有数据可写,该事件一直触发</li><li>系统调用次数较多</li><li>编程难度较低,数据完整性由内核来保证</li></ul><h3 id="边缘触发-Edge-Triggled-ET"><a href="#边缘触发-Edge-Triggled-ET" class="headerlink" title="边缘触发(Edge-Triggled,ET)"></a>边缘触发(Edge-Triggled,ET)</h3><ul><li>socket的接收缓冲区状态发生变化时触发事件,即空的接收缓冲区刚接收到数据时触发事件</li><li>socket的发送缓冲区状态变化时触发写事件,即满的缓冲区刚空出空间时触发读事件</li><li>系统调用次数较少</li><li>编程难度较高,数据完整性由用户自己保证</li></ul><h2 id="epoll-create"><a href="#epoll-create" class="headerlink" title="epoll_create()"></a>epoll_create()</h2><h2 id="epoll-ctl"><a href="#epoll-ctl" class="headerlink" title="epoll_ctl()"></a>epoll_ctl()</h2><h2 id="epoll-wait"><a href="#epoll-wait" class="headerlink" title="epoll_wait()"></a>epoll_wait()</h2><h2 id="epoll常见宏"><a href="#epoll常见宏" class="headerlink" title="epoll常见宏"></a>epoll常见宏</h2><ul><li>EPOLLIN : 表示对应的文件描述符可以读(包括对端SOCKET正常关闭);</li><li>EPOLLOUT: 表示对应的文件描述符可以写;</li><li>EPOLLPRI: 表示对应的文件描述符有紧急的数据可读(这里应该表示有带外数据到来);</li><li>EPOLLERR: 表示对应的文件描述符发生错误;</li><li>EPOLLHUP: 表示对应的文件描述符被挂断;</li><li>EPOLLET: 将 EPOLL设为边缘触发(Edge Triggered)模式(默认为水平触发),这是相对于水平触发(Level Triggered)来说的。</li><li>EPOLLONESHOT: 只监听一次事件,当监听完这次事件之后,如果还需要继续监听这个socket的话,需要再次把这个socket加入到EPOLL队列里</li></ul><h1 id="参考"><a href="#参考" class="headerlink" title="参考"></a>参考</h1><p><a href="https://zhuanlan.zhihu.com/p/115220699">一文看懂IO多路复用 - 知乎 (zhihu.com)</a></p><p><a href="https://www.cnblogs.com/nr-zhang/p/10483011.html">IO复用(较详细) - zhang-san - 博客园 (cnblogs.com)</a></p><p><a href="https://zhuanlan.zhihu.com/p/93609693">深入理解 Epoll - 知乎 (zhihu.com)</a></p>]]></content>
<summary type="html"><h1 id="什么是I-x2F-O复用"><a href="#什么是I-x2F-O复用" class="headerlink" title="什么是I&#x2F;O复用"></a>什么是I&#x2F;O复用</h1><p>单线程或单进程同时监测若干个文件描述符是否可以执行IO操</summary>
<category term="c++" scheme="https://frankho-hwc.github.io/tags/c/"/>
<category term="工作" scheme="https://frankho-hwc.github.io/tags/%E5%B7%A5%E4%BD%9C/"/>
<category term="网络编程" scheme="https://frankho-hwc.github.io/tags/%E7%BD%91%E7%BB%9C%E7%BC%96%E7%A8%8B/"/>
</entry>
<entry>
<title>常见设计模式</title>
<link href="https://frankho-hwc.github.io/2023/03/11/%E5%B8%B8%E8%A7%81%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F/"/>
<id>https://frankho-hwc.github.io/2023/03/11/%E5%B8%B8%E8%A7%81%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F/</id>
<published>2023-03-11T08:55:52.000Z</published>
<updated>2023-03-19T06:55:48.033Z</updated>
<content type="html"><![CDATA[<h1 id="设计模式"><a href="#设计模式" class="headerlink" title="设计模式"></a>设计模式</h1><h1 id="设计模式分类"><a href="#设计模式分类" class="headerlink" title="设计模式分类"></a>设计模式分类</h1><p>设计模式分为三类: </p><h2 id="创造型模式:"><a href="#创造型模式:" class="headerlink" title="创造型模式:"></a>创造型模式:</h2><p>单例模式、⼯⼚模式、建造者模式、原型模式 </p><h2 id="结构型模式:"><a href="#结构型模式:" class="headerlink" title="结构型模式:"></a>结构型模式:</h2><p>适配器模式、桥接模式、外观模式、组合模式、装饰模式、享元模式、代理模式 </p><h2 id="⾏为型模式:"><a href="#⾏为型模式:" class="headerlink" title="⾏为型模式:"></a>⾏为型模式:</h2><p>责任链模式、命令模式、解释器模式、迭代器模式、中介者模式、备忘录模式、观察者模式、状态模式、策略模式、模板⽅法模式、访问者模式。</p><h1 id="单例模式"><a href="#单例模式" class="headerlink" title="单例模式"></a>单例模式</h1><h2 id="性质"><a href="#性质" class="headerlink" title="性质"></a>性质</h2><p>单例模式是指在整个系统生命周期内,保证一个类只能产生一个实例,确保该类的唯一性。</p><p>注意:</p><ul><li>1、单例类只能有一个实例。</li><li>2、单例类必须自己创建自己的唯一实例。</li><li>3、单例类必须给所有其他对象提供这一实例。</li></ul><p><strong>意图:</strong>保证一个类仅有一个实例,并提供一个访问它的全局访问点。</p><p><strong>主要解决:</strong>一个全局使用的类频繁地创建与销毁。</p><p><strong>何时使用:</strong>当您想控制实例数目,节省系统资源的时候。</p><p><strong>如何解决:</strong>判断系统是否已经有这个单例,如果有则返回,如果没有则创建。</p><p><strong>关键代码:</strong>构造函数是私有的。</p><p><strong>应用例子:</strong> </p><ol><li>一个班级只有一个班主任</li><li>有些设备管理器只能设计为单例模式,如一台电脑有两个打印机,但是输出的时候只能有一台打印机打印文件。</li></ol><h2 id="优点"><a href="#优点" class="headerlink" title="优点"></a>优点</h2><ol><li>在内存里只有一个实例,减少了内存的开销,尤其是频繁的创建和销毁实例(比如管理学院首页页面缓存)。</li><li>避免对资源的多重占用(比如写文件操作)。</li></ol><h2 id="缺点"><a href="#缺点" class="headerlink" title="缺点"></a>缺点</h2><p>没有接口,不能继承,与单一职责原则冲突,一个类应该只关心内部逻辑,而不关心外面怎么样来实例化。</p><h2 id="应用场景"><a href="#应用场景" class="headerlink" title="应用场景"></a>应用场景</h2><ul><li>1、要求生产唯一序列号。</li><li>2、WEB 中的计数器,不用每次刷新都在数据库里加一次,用单例先缓存起来。</li><li>3、创建的一个对象需要消耗的资源过多,比如 I/O 与数据库的连接等。</li></ul><h2 id="实现方式"><a href="#实现方式" class="headerlink" title="实现方式"></a>实现方式</h2><h3 id="饿汉式-线程安全"><a href="#饿汉式-线程安全" class="headerlink" title="饿汉式(线程安全)"></a>饿汉式(线程安全)</h3><p><strong>是否 Lazy 初始化:</strong>否</p><p><strong>是否多线程安全:</strong>是</p><p><strong>实现难度:</strong>易</p><p>饿汉,顾名思义就是饿了就饥不择食,所以单例类定义的时候就进行了实例化。</p><p>在最开始的时候静态对象就已经创建完成,设计⽅法是类中包含⼀个静态成员指针,该指针指向该类的⼀个对象, 提供⼀个公有的静态成员⽅法,返回该对象指针,为了使得对象唯⼀,构造函数设为私有。</p><figure class="highlight c++"><table><tr><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string"><algorithm></span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span><span class="string"><iostream></span></span></span><br><span class="line"><span class="keyword">using</span> <span class="keyword">namespace</span> std;</span><br><span class="line"><span class="keyword">class</span> <span class="title class_">SingleInstance</span> {</span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line"> <span class="function"><span class="type">static</span> SingleInstance* <span class="title">GetInstance</span><span class="params">()</span> </span>{</span><br><span class="line"> <span class="type">static</span> SingleInstance ins;</span><br><span class="line"> <span class="keyword">return</span> &ins;</span><br><span class="line"> }</span><br><span class="line"> ~<span class="built_in">SingleInstance</span>(){};</span><br><span class="line"><span class="keyword">private</span>:</span><br><span class="line"> <span class="comment">//涉及到创建对象的函数都设置为private</span></span><br><span class="line"> <span class="built_in">SingleInstance</span>() { std::cout<<<span class="string">"SingleInstance() 饿汉"</span><<std::endl; }</span><br><span class="line"> <span class="built_in">SingleInstance</span>(<span class="type">const</span> SingleInstance& other) {};</span><br><span class="line"> SingleInstance& <span class="keyword">operator</span>=(<span class="type">const</span> SingleInstance& other) {<span class="keyword">return</span> *<span class="keyword">this</span>;}</span><br><span class="line">};</span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">main</span><span class="params">()</span></span>{</span><br><span class="line"> <span class="comment">//因为不能创建对象所以通过静态成员函数的⽅法返回静态成员变量</span></span><br><span class="line"> SingleInstance* ins = SingleInstance::<span class="built_in">GetInstance</span>();</span><br><span class="line"> <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">}</span><br><span class="line"><span class="comment">//输出 SingleInstance() 饿汉</span></span><br></pre></td></tr></table></figure><h3 id="懒汉式-线程安全"><a href="#懒汉式-线程安全" class="headerlink" title="懒汉式(线程安全)"></a>懒汉式(线程安全)</h3><p><strong>是否 Lazy 初始化:</strong>是</p><p><strong>是否多线程安全:</strong>是</p><p><strong>实现难度:</strong>易</p><figure class="highlight c++"><table><tr><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">include</span><span class="string"><iostream></span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span><span class="string"><algorithm></span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string"><pthread.h></span></span></span><br><span class="line"><span class="keyword">using</span> <span class="keyword">namespace</span> std;</span><br><span class="line"><span class="keyword">class</span> <span class="title class_">SingleInstance</span></span><br><span class="line">{</span><br><span class="line"> <span class="keyword">public</span>:</span><br><span class="line"> <span class="function"><span class="type">static</span> SingleInstance* <span class="title">GetInstance</span><span class="params">()</span> </span>{</span><br><span class="line"> <span class="keyword">if</span> (ins == <span class="literal">nullptr</span>) {</span><br><span class="line"> <span class="built_in">pthread_mutex_lock</span>(&mutex);</span><br><span class="line"> <span class="keyword">if</span> (ins == <span class="literal">nullptr</span>) {</span><br><span class="line"> ins = <span class="keyword">new</span> <span class="built_in">SingleInstance</span>();</span><br><span class="line"> }</span><br><span class="line"> <span class="built_in">pthread_mutex_unlock</span>(&mutex);</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">return</span> ins;</span><br><span class="line"> }</span><br><span class="line"> ~<span class="built_in">SingleInstance</span>(){};</span><br><span class="line"> <span class="comment">//互斥锁</span></span><br><span class="line"> <span class="type">static</span> <span class="type">pthread_mutex_t</span> mutex;</span><br><span class="line"></span><br><span class="line"><span class="keyword">private</span>:</span><br><span class="line"> <span class="comment">//涉及到创建对象的函数都设置为private</span></span><br><span class="line"> <span class="built_in">SingleInstance</span>() { std::cout<<<span class="string">"SingleInstance() 懒汉"</span><<std::endl; }</span><br><span class="line"> <span class="built_in">SingleInstance</span>(<span class="type">const</span> SingleInstance& other) {};</span><br><span class="line"> SingleInstance& <span class="keyword">operator</span>=(<span class="type">const</span> SingleInstance& other) { <span class="keyword">return</span> *<span class="keyword">this</span>; }</span><br><span class="line"> <span class="comment">//静态成员</span></span><br><span class="line"> <span class="type">static</span> SingleInstance* ins;</span><br><span class="line"> </span><br><span class="line">};</span><br><span class="line"><span class="comment">//懒汉式 静态变量需要定义</span></span><br><span class="line">SingleInstance* SingleInstance::ins = <span class="literal">nullptr</span>;</span><br><span class="line"><span class="type">pthread_mutex_t</span> SingleInstance::mutex;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">main</span><span class="params">()</span></span>{</span><br><span class="line"> <span class="comment">//因为不能创建对象所以通过静态成员函数的⽅法返回静态成员变ᰁ</span></span><br><span class="line"> SingleInstance* ins = SingleInstance::<span class="built_in">GetInstance</span>();</span><br><span class="line"> <span class="keyword">delete</span> ins;</span><br><span class="line"> <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">}</span><br><span class="line"><span class="comment">//输出 SingleInstance() 懒汉</span></span><br></pre></td></tr></table></figure><h3 id="懒汉式-线程不安全"><a href="#懒汉式-线程不安全" class="headerlink" title="懒汉式(线程不安全)"></a>懒汉式(线程不安全)</h3><p><strong>是否 Lazy 初始化:</strong>是</p><p><strong>是否多线程安全:</strong>是</p><p><strong>实现难度:</strong>易</p><figure class="highlight c++"><table><tr><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">include</span><span class="string"><iostream></span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span><span class="string"><algorithm></span></span></span><br><span class="line"><span class="keyword">using</span> <span class="keyword">namespace</span> std;</span><br><span class="line"><span class="keyword">class</span> <span class="title class_">SingleInstance</span></span><br><span class="line">{</span><br><span class="line"><span class="keyword">private</span>:</span><br><span class="line"><span class="type">static</span> SingleInstance* instance;</span><br><span class="line"><span class="keyword">private</span>:</span><br><span class="line"><span class="built_in">SingleInstance</span>() {};</span><br><span class="line">~<span class="built_in">SingleInstance</span>() {};</span><br><span class="line"><span class="built_in">SingletInstance</span>(<span class="type">const</span> SingleInstance&);</span><br><span class="line">SingleInstance& <span class="keyword">operator</span>=(<span class="type">const</span> SingleInstance&);</span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line"><span class="function"><span class="type">static</span> SingleInstance* <span class="title">getInstance</span><span class="params">()</span> </span></span><br><span class="line"><span class="function"> </span>{</span><br><span class="line"><span class="keyword">if</span>(instance == <span class="literal">NULL</span>) </span><br><span class="line">instance = <span class="keyword">new</span> <span class="built_in">SingleInstance</span>();</span><br><span class="line"><span class="keyword">return</span> instance;</span><br><span class="line">}</span><br><span class="line">};</span><br><span class="line"></span><br><span class="line"><span class="comment">// init static member</span></span><br><span class="line">SingleInstance* SingleInstance::ins = <span class="literal">nullptr</span>;</span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">main</span><span class="params">()</span></span>{</span><br><span class="line"> <span class="comment">//因为不能创建对象所以通过静态成员函数的⽅法返回静态成员变ᰁ</span></span><br><span class="line"> SingleInstance* ins = SingleInstance::<span class="built_in">GetInstance</span>();</span><br><span class="line"> <span class="keyword">delete</span> ins;</span><br><span class="line"> <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h1 id="工厂模式"><a href="#工厂模式" class="headerlink" title="工厂模式"></a>工厂模式</h1><h3 id="简单工厂模式"><a href="#简单工厂模式" class="headerlink" title="简单工厂模式"></a>简单工厂模式</h3><p>就是建⽴⼀个⼯⼚类,对实现了同⼀接⼝的⼀些类进⾏实例的创建。简单⼯⼚模式的实质是由⼀个⼯⼚类根据传⼊ 的参数,动态决定应该创建哪⼀个产品类(这些产品类继承⾃⼀个⽗类或接⼝)的实例。</p><p>例如如下,有一家生产处理器核的厂家,它只有一个工厂,能够生产两种型号的处理器核。客户需要什么样的处理器核,一定要显式地告诉工厂。</p><figure class="highlight c++"><table><tr><td class="code"><pre><span class="line"><span class="keyword">enum</span> <span class="title class_">CTYPE</span> {COREA,COREB};</span><br><span class="line"><span class="keyword">class</span> <span class="title class_">SingleCore</span></span><br><span class="line">{</span><br><span class="line"> <span class="keyword">public</span>:</span><br><span class="line"> <span class="function"><span class="keyword">virtual</span> <span class="type">void</span> <span class="title">Show</span><span class="params">()</span> </span>= <span class="number">0</span>;</span><br><span class="line"> </span><br><span class="line">};</span><br><span class="line"><span class="comment">//内核a</span></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">SingleCoreA</span>: <span class="keyword">public</span> SingleCore</span><br><span class="line">{</span><br><span class="line"> <span class="keyword">public</span>:</span><br><span class="line"> <span class="function"><span class="type">void</span> <span class="title">Show</span><span class="params">()</span> </span>{cout<<<span class="string">"SingleCoreA"</span><<endl;}</span><br><span class="line"> </span><br><span class="line">};</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="comment">//内核b</span></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">SingleCoreB</span>: <span class="keyword">public</span> SingleCore</span><br><span class="line">{</span><br><span class="line"> <span class="keyword">public</span>:</span><br><span class="line"> <span class="function"><span class="type">void</span> <span class="title">Show</span><span class="params">()</span> </span>{cout<<<span class="string">"SingleCoreB"</span><<endl;}</span><br><span class="line"> </span><br><span class="line">};</span><br><span class="line"></span><br><span class="line"><span class="comment">//唯一的工厂,可以生产两种内核</span></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">Factory</span></span><br><span class="line">{</span><br><span class="line"> <span class="keyword">public</span>:</span><br><span class="line"> <span class="function">SingleCore* <span class="title">CreateSingleCore</span><span class="params">(<span class="keyword">enum</span> CTYPE ctype)</span></span></span><br><span class="line"><span class="function"> </span>{</span><br><span class="line"> <span class="keyword">if</span>(ctype == COREA)</span><br><span class="line"> { <span class="keyword">return</span> <span class="keyword">new</span> <span class="built_in">SingleCoreA</span>(); <span class="comment">// 生产核A</span></span><br><span class="line">}</span><br><span class="line"> <span class="keyword">else</span> <span class="keyword">if</span>(ctype == COREB)</span><br><span class="line"> {</span><br><span class="line"> <span class="keyword">return</span> <span class="keyword">new</span> <span class="built_in">SingleCoreB</span>();</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">else</span> </span><br><span class="line"> <span class="keyword">return</span> <span class="literal">NULL</span>;</span><br><span class="line"> }</span><br><span class="line">};</span><br></pre></td></tr></table></figure><p>⼯⼚模式⽬的就是代码解耦,如果我们不采⽤⼯⼚模式,如果要创建产品 A、B,通常做法采⽤⽤ switch…case语 句,那么想⼀想后期添加更多的产品进来,我们不是要添加更多的 switch…case 吗?这样就很麻烦,⽽且也不符 合设计模式中的<strong>开放封闭原则</strong>。</p><h3 id="工厂方法模式"><a href="#工厂方法模式" class="headerlink" title="工厂方法模式"></a>工厂方法模式</h3><p>所谓工厂方法模式,就是指定义一个用于创建对象的接口,让子类决定实例化哪一个类。Factory Method使一个类的实例化延迟动其子类</p><p>举例如下,这家专门生产处理器核的厂家赚了不少钱,于是决定再开设一个工厂专门用来生产B型号的单核,而原来的工厂专门用来生产A型号的单核。这时,客户要做的是找好工厂,比如要A型号的核,就找A工厂要,否则找B工厂要,不再需要告诉工厂具体要什么型号的处理器核了。</p><figure class="highlight c++"><table><tr><td class="code"><pre><span class="line"><span class="keyword">class</span> <span class="title class_">SingleCore</span></span><br><span class="line">{</span><br><span class="line"> <span class="keyword">public</span>:</span><br><span class="line"> <span class="function"><span class="keyword">virtual</span> <span class="type">void</span> <span class="title">Show</span><span class="params">()</span> </span>= <span class="number">0</span>;</span><br><span class="line">};</span><br><span class="line"></span><br><span class="line"><span class="comment">//内核a</span></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">SingleCoreA</span>: <span class="keyword">public</span> SingleCore</span><br><span class="line">{</span><br><span class="line"> <span class="keyword">public</span>:</span><br><span class="line"> <span class="function"><span class="type">void</span> <span class="title">Show</span><span class="params">()</span> </span>{cout<<<span class="string">"SingleCoreA"</span><<endl;}</span><br><span class="line"> </span><br><span class="line">};</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="comment">//内核b</span></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">SingleCoreB</span>: <span class="keyword">public</span> SingleCore</span><br><span class="line">{</span><br><span class="line"> <span class="keyword">public</span>:</span><br><span class="line"> <span class="function"><span class="type">void</span> <span class="title">Show</span><span class="params">()</span> </span>{cout<<<span class="string">"SingleCoreB"</span><<endl;}</span><br><span class="line"> </span><br><span class="line">};</span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">Factory</span></span><br><span class="line">{</span><br><span class="line"> <span class="keyword">public</span>:</span><br><span class="line"> <span class="function"><span class="keyword">virtual</span> SingleCore* <span class="title">CreateSingleCore</span><span class="params">()</span> </span>= <span class="number">0</span>;</span><br><span class="line">};</span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">FactoryA</span>: <span class="keyword">public</span> Factory</span><br><span class="line">{</span><br><span class="line"> <span class="keyword">public</span>:</span><br><span class="line"> <span class="function">SingleCoreA* <span class="title">CreateSingleCore</span><span class="params">()</span> </span>{ <span class="keyword">return</span> <span class="keyword">new</span> SingleCoreA;}</span><br><span class="line">};</span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">FactoryB</span>: <span class="keyword">public</span> Factory</span><br><span class="line">{</span><br><span class="line"> <span class="keyword">public</span>:</span><br><span class="line"> <span class="function">SingleCoreB* <span class="title">CreateSingleCore</span><span class="params">()</span> </span>{ <span class="keyword">return</span> <span class="keyword">new</span> SingleCoreB;}</span><br><span class="line">};</span><br></pre></td></tr></table></figure><p>优点:可扩展性好,符合了开闭原则,新增一种产品时,只需增加对应的产品类和对应的工厂子类即可</p><p>缺点:每增加一种产品,就需要增加一个对象的工厂,如果这家公司发展迅速,推出了很多新的处理器核,那么就要开设相应的新工厂。在c++实现中就要定义一个个的工厂子类</p><h3 id="抽象工厂模式"><a href="#抽象工厂模式" class="headerlink" title="抽象工厂模式"></a>抽象工厂模式</h3><p>举例: 这家公司的技术不断进步,不仅可以生产单核处理器,也能生产多核处理器。现在简单工厂模式和工厂方法模式都鞭长莫及。抽象工厂模式登场了。<strong>它的定义为提供一个创建一系列相关或者相互依赖对象的接口,而无需指定它们具体的类。</strong>具体这样应用,这家公司还是开设两家工厂,一个专门用来生产A型号的单核多核处理器,而另外一个工厂专门用来生产B型号的单核多核处理器,下面给出实现的代码:</p><figure class="highlight c++"><table><tr><td class="code"><pre><span class="line"><span class="comment">//抽象工厂模式</span></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">SingleCore</span></span><br><span class="line">{</span><br><span class="line"> <span class="keyword">public</span>:</span><br><span class="line"> <span class="function"><span class="keyword">virtual</span> <span class="type">void</span> <span class="title">show</span><span class="params">()</span></span>=<span class="number">0</span>;</span><br><span class="line">};</span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">SingleCoreA</span>: <span class="keyword">public</span> SingleCore</span><br><span class="line">{</span><br><span class="line"> <span class="keyword">public</span>:</span><br><span class="line"> <span class="function"><span class="type">void</span> <span class="title">show</span><span class="params">()</span></span>{ cout<<<span class="string">"Single Core A"</span><<endl;}</span><br><span class="line">};</span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">SingleCoreB</span>: <span class="keyword">public</span> SingleCore</span><br><span class="line">{</span><br><span class="line"> <span class="keyword">public</span>:</span><br><span class="line"> <span class="function"><span class="type">void</span> <span class="title">show</span><span class="params">()</span></span>{ cout<<<span class="string">"Single Core B"</span><<endl;}</span><br><span class="line">};</span><br><span class="line"></span><br><span class="line"><span class="comment">//多核</span></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">MultiCore</span></span><br><span class="line">{</span><br><span class="line"> <span class="keyword">public</span>:</span><br><span class="line"> <span class="function"><span class="keyword">virtual</span> <span class="type">void</span> <span class="title">show</span><span class="params">()</span></span>=<span class="number">0</span>;</span><br><span class="line">};</span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">MutilCoreA</span>: <span class="keyword">public</span> MultiCore</span><br><span class="line">{</span><br><span class="line"> <span class="keyword">public</span>:</span><br><span class="line"> <span class="function"><span class="type">void</span> <span class="title">show</span><span class="params">()</span></span>{cout<<<span class="string">"Multi Core A"</span><<endl;}</span><br><span class="line">};</span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">MutilCoreB</span>: <span class="keyword">public</span> MultiCore</span><br><span class="line">{</span><br><span class="line"> <span class="keyword">public</span>:</span><br><span class="line"> <span class="function"><span class="type">void</span> <span class="title">show</span><span class="params">()</span></span>{cout<<<span class="string">"Multi Core B"</span><<endl;}</span><br><span class="line">};</span><br><span class="line"></span><br><span class="line"><span class="comment">//工厂</span></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">CoreFactory</span></span><br><span class="line">{</span><br><span class="line"> <span class="keyword">public</span>:</span><br><span class="line"> <span class="function"><span class="keyword">virtual</span> SingleCore* <span class="title">CreatingSingleCore</span><span class="params">()</span> </span>= <span class="number">0</span>;</span><br><span class="line"> <span class="function"><span class="keyword">virtual</span> MultiCore* <span class="title">CreatingSingleCore</span><span class="params">()</span> </span>= <span class="number">0</span>;</span><br><span class="line"> </span><br><span class="line">};</span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">FactoryA</span>: <span class="keyword">public</span> CoreFactory</span><br><span class="line">{</span><br><span class="line"> <span class="keyword">public</span>:</span><br><span class="line"> <span class="function">SingleCore* <span class="title">CreatingSingleCore</span><span class="params">()</span> </span>{<span class="keyword">return</span> <span class="keyword">new</span> <span class="built_in">SingleCoreA</span>();}</span><br><span class="line"> <span class="function">MultiCore* <span class="title">CreatingSingleCore</span><span class="params">()</span> </span>{<span class="keyword">return</span> <span class="keyword">new</span> <span class="built_in">MultiCoreA</span>();}</span><br><span class="line">};</span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">FactoryB</span>: <span class="keyword">public</span> CoreFactory</span><br><span class="line">{</span><br><span class="line"> <span class="keyword">public</span>:</span><br><span class="line"> <span class="function">SingleCore* <span class="title">CreatingSingleCore</span><span class="params">()</span> </span>{<span class="keyword">return</span> <span class="keyword">new</span> <span class="built_in">SingleCoreB</span>();}</span><br><span class="line"> <span class="function">MultiCore* <span class="title">CreatingSingleCore</span><span class="params">()</span> </span>{<span class="keyword">return</span> <span class="keyword">new</span> <span class="built_in">MultiCoreB</span>();}</span><br><span class="line">};</span><br></pre></td></tr></table></figure><p><strong>优点</strong>:工厂抽象类创建了多个类型的产品,当有需求时,可以创建相关产品子类和子工厂类来获取。</p><p><strong>缺点</strong>:扩展新种类产品时困难,抽象工厂模式需要我们在工厂抽象类中提前确定了可能需要的儿产品种类,以满足不同型号的多种产品的需求。但是如果我们需要的产品种类并没有在工厂抽象类中提前确定,那我们就需要去修改工厂抽象类了,而一旦修改了工厂抽象类,那么所有的工厂子类也需要修改,这样显然扩展不方便。</p><h1 id="装饰器模式"><a href="#装饰器模式" class="headerlink" title="装饰器模式"></a>装饰器模式</h1><p>装饰器模式(Decorator Pattern)允许向⼀个现有的对象添加新的功能,同时⼜不改变其结构。 这种类型的设计模式属于结构型模式,它是作为现有的类的⼀个包装。 代码没有改变 Car 类的内部结构,还为其增加了新的功能,这就是装饰器模式的作⽤。</p><figure class="highlight c++"><table><tr><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string"><iostream></span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string"><list></span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string"><memory></span></span></span><br><span class="line"><span class="keyword">using</span> <span class="keyword">namespace</span> std;</span><br><span class="line"><span class="comment">//抽象构件类 Transform (变形⾦刚)</span></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">Transform</span>{</span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line"> <span class="function"><span class="keyword">virtual</span> <span class="type">void</span> <span class="title">move</span><span class="params">()</span> </span>= <span class="number">0</span>;</span><br><span class="line">};</span><br><span class="line"><span class="comment">//具体构件类Car</span></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">Car</span> : <span class="keyword">public</span> Transform{</span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line"> <span class="built_in">Car</span>(){</span><br><span class="line"> std::cout << <span class="string">"变形⾦刚是⼀辆⻋!"</span> << endl;</span><br><span class="line"> }</span><br><span class="line"> <span class="function"><span class="type">void</span> <span class="title">move</span><span class="params">()</span></span>{</span><br><span class="line"> std::cout << <span class="string">"在陆地上移动。"</span> << endl;</span><br><span class="line"> }</span><br><span class="line">};</span><br><span class="line"><span class="comment">//抽象装饰类</span></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">Changer</span> : <span class="keyword">public</span> Transform{</span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line"> <span class="built_in">Changer</span>(shared_ptr<Transform> transform){</span><br><span class="line"> <span class="keyword">this</span>->transform = transform;</span><br><span class="line"> }</span><br><span class="line"> <span class="function"><span class="type">void</span> <span class="title">move</span><span class="params">()</span></span>{</span><br><span class="line"> transform-><span class="built_in">move</span>();</span><br><span class="line"> }</span><br><span class="line"><span class="keyword">private</span>:</span><br><span class="line"> shared_ptr<Transform> transform;</span><br><span class="line">};</span><br><span class="line"><span class="comment">//具体装饰类Robot</span></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">Robot</span> : <span class="keyword">public</span> Changer{</span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line"> <span class="built_in">Robot</span>(shared_ptr<Transform> transform) : <span class="built_in">Changer</span>(transform){</span><br><span class="line"> std::cout << <span class="string">"变成机器⼈!"</span> << std::endl;</span><br><span class="line"> }</span><br><span class="line"> <span class="function"><span class="type">void</span> <span class="title">say</span><span class="params">()</span></span>{</span><br><span class="line"> std::cout << <span class="string">"说话!"</span> << std::endl;</span><br><span class="line"> }</span><br><span class="line">};</span><br><span class="line"><span class="comment">//具体装饰类AirPlane</span></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">Airplane</span> : <span class="keyword">public</span> Changer{</span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line"> <span class="built_in">Airplane</span>(shared_ptr<Transform> transform) : <span class="built_in">Changer</span>(transform){</span><br><span class="line"> std::cout << <span class="string">"变成⻜机!"</span> << std::endl;</span><br><span class="line"> }</span><br><span class="line"> <span class="function"><span class="type">void</span> <span class="title">say</span><span class="params">()</span></span>{</span><br><span class="line"> std::cout << <span class="string">"在天空⻜翔!"</span> << std::endl;</span><br><span class="line"> } </span><br><span class="line">};</span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">main</span><span class="params">(<span class="type">void</span>)</span></span>{</span><br><span class="line"> shared_ptr<Transform> camaro = <span class="built_in">make_shared</span><Car>();</span><br><span class="line"> camaro-><span class="built_in">move</span>();</span><br><span class="line"> std::cout << <span class="string">"--------------"</span> << endl;</span><br><span class="line"> shared_ptr<Robot> bumblebee = <span class="built_in">make_shared</span><Robot>(camaro);</span><br><span class="line"> bumblebee-><span class="built_in">move</span>();</span><br><span class="line"> bumblebee-><span class="built_in">say</span>();</span><br><span class="line"> <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h1 id="观察者模式"><a href="#观察者模式" class="headerlink" title="观察者模式"></a>观察者模式</h1><p>观察者模式:定义⼀种⼀(被观察类)对多(观察类)的关系,让多个观察对象同时监听⼀个被观察对象,被观察对象状态发⽣变化时,会通知所有的观察对象,使他们能够更新⾃⼰的状态。</p><p>观察者模式中存在两种⻆⾊: </p><p><strong>观察者</strong>:内部包含被观察者对象,当被观察者对象的状态发⽣变化时,更新⾃⼰的状态。(接收通知更新状态) </p><p><strong>被观察者</strong>:内部包含了所有观察者对象,当状态发⽣变化时通知所有的观察者更新⾃⼰的状态。(发送通知) </p><p><strong>应⽤场景</strong>: 当⼀个对象的改变需要同时改变其他对象,且不知道具体有多少对象有待改变时,应该考虑使⽤观察者模式; ⼀个抽象模型有两个⽅⾯,其中⼀⽅⾯依赖于另⼀⽅⾯,这时可以⽤观察者模式将这两者封装在独⽴的对象中使它 们各⾃独⽴地改变和复⽤。</p><p>实现如下</p><figure class="highlight c++"><table><tr><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">include</span><span class="string"><iostream></span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span><span class="string"><list></span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span><span class="string"><string></span></span></span><br><span class="line"><span class="keyword">using</span> <span class="keyword">namespace</span> std;</span><br><span class="line"><span class="keyword">class</span> <span class="title class_">Subject</span>;</span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">Observer</span></span><br><span class="line">{</span><br><span class="line"> <span class="keyword">protected</span>: </span><br><span class="line"> string name;</span><br><span class="line"> Subject *sub;</span><br><span class="line"> </span><br><span class="line"> <span class="keyword">public</span>:</span><br><span class="line"> <span class="built_in">Observer</span>(string name, Subject *sub)</span><br><span class="line"> {</span><br><span class="line"> <span class="keyword">this</span> -> name = name;</span><br><span class="line"> <span class="keyword">this</span> -> sub = sub;</span><br><span class="line"> }</span><br><span class="line"> <span class="function"><span class="keyword">virtual</span> <span class="type">void</span> <span class="title">update</span><span class="params">()</span> </span>= <span class="number">0</span>;</span><br><span class="line">};</span><br><span class="line"><span class="keyword">class</span> <span class="title class_">StockObserver</span> : <span class="keyword">public</span> Observer</span><br><span class="line">{</span><br><span class="line"> <span class="keyword">public</span>:</span><br><span class="line"> <span class="built_in">StockObserver</span>(string name ,Subject *sub): <span class="built_in">Observer</span>(name,sub){}</span><br><span class="line"> <span class="function"><span class="type">void</span> <span class="title">update</span><span class="params">()</span></span>;</span><br><span class="line">};</span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">NBAObserver</span> : <span class="keyword">public</span> Observer {</span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line"> <span class="built_in">NBAObserver</span>(string name, Subject *sub) : <span class="built_in">Observer</span>(name, sub){}</span><br><span class="line"> <span class="function"><span class="type">void</span> <span class="title">update</span><span class="params">()</span></span>;</span><br><span class="line">};</span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">Subject</span> {</span><br><span class="line"><span class="keyword">protected</span>:</span><br><span class="line"> std::list<Observer *> observers;</span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line"> string action; <span class="comment">//被观察者对象的状态</span></span><br><span class="line"> <span class="function"><span class="keyword">virtual</span> <span class="type">void</span> <span class="title">attach</span><span class="params">(Observer *)</span> </span>= <span class="number">0</span>;</span><br><span class="line"> <span class="function"><span class="keyword">virtual</span> <span class="type">void</span> <span class="title">detach</span><span class="params">(Observer *)</span> </span>= <span class="number">0</span>;</span><br><span class="line"> <span class="function"><span class="keyword">virtual</span> <span class="type">void</span> <span class="title">notify</span><span class="params">()</span> </span>= <span class="number">0</span>;</span><br><span class="line">};</span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">Secretary</span> : <span class="keyword">public</span> Subject {</span><br><span class="line"> <span class="function"><span class="type">void</span> <span class="title">attach</span><span class="params">(Observer *observer)</span> </span>{</span><br><span class="line"> observers.<span class="built_in">push_back</span>(observer);</span><br><span class="line"> }</span><br><span class="line"> <span class="function"><span class="type">void</span> <span class="title">detach</span><span class="params">(Observer *observer)</span> </span>{</span><br><span class="line"> list<Observer *>::iterator iter = observers.<span class="built_in">begin</span>();</span><br><span class="line"> <span class="keyword">while</span> (iter != observers.<span class="built_in">end</span>()) {</span><br><span class="line"> <span class="keyword">if</span> ((*iter) == observer) {</span><br><span class="line"> observers.<span class="built_in">erase</span>(iter);</span><br><span class="line"> <span class="keyword">return</span>;</span><br><span class="line"> }</span><br><span class="line"> ++iter;</span><br><span class="line"> } </span><br><span class="line"> }</span><br><span class="line"> <span class="function"><span class="type">void</span> <span class="title">notify</span><span class="params">()</span> </span>{</span><br><span class="line"> list<Observer *>::iterator iter = observers.<span class="built_in">begin</span>();</span><br><span class="line"> <span class="keyword">while</span> (iter != observers.<span class="built_in">end</span>()) {</span><br><span class="line"> (*iter)-><span class="built_in">update</span>();</span><br><span class="line"> ++iter;</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line">};</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">StockObserver::update</span><span class="params">()</span> </span>{</span><br><span class="line"> cout << name << <span class="string">" 收到消息:"</span> << sub->action << endl;</span><br><span class="line"> <span class="keyword">if</span> (sub->action == <span class="string">"老板来了!"</span>) {</span><br><span class="line"> cout << <span class="string">"我关上关闭股票,装做很认真工作的样子!"</span> << endl;</span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">NBAObserver::update</span><span class="params">()</span> </span>{</span><br><span class="line"> cout << name << <span class="string">" 收到消息:"</span> << sub->action << endl;</span><br><span class="line"> <span class="keyword">if</span> (sub->action == <span class="string">"老板来了!"</span>) {</span><br><span class="line"> cout << <span class="string">"我关上关闭 NBA,装做很认真工作的样子!"</span> << endl;</span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">main</span><span class="params">()</span></span></span><br><span class="line"><span class="function"></span>{</span><br><span class="line"> Subject *BOSS = <span class="keyword">new</span> <span class="built_in">Secretary</span>();</span><br><span class="line"> Observer *xa = <span class="keyword">new</span> <span class="built_in">NBAObserver</span>(<span class="string">"xa"</span>, BOSS);</span><br><span class="line"> Observer *xb = <span class="keyword">new</span> <span class="built_in">NBAObserver</span>(<span class="string">"xb"</span>, BOSS);</span><br><span class="line"> Observer *xc = <span class="keyword">new</span> <span class="built_in">StockObserver</span>(<span class="string">"xc"</span>, BOSS);</span><br><span class="line"> BOSS-><span class="built_in">attach</span>(xa);</span><br><span class="line"> BOSS-><span class="built_in">attach</span>(xb);</span><br><span class="line"> BOSS-><span class="built_in">attach</span>(xc);</span><br><span class="line"> BOSS->action = <span class="string">"去吃饭了!"</span>;</span><br><span class="line"> BOSS-><span class="built_in">notify</span>();</span><br><span class="line"> cout << endl;</span><br><span class="line"> BOSS->action = <span class="string">"老板来了!"</span>;</span><br><span class="line"> BOSS-><span class="built_in">notify</span>();</span><br><span class="line"> <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">}</span><br><span class="line"><span class="comment">//输出</span></span><br><span class="line"><span class="comment">//product A create! product B create!</span></span><br></pre></td></tr></table></figure>]]></content>
<summary type="html"><h1 id="设计模式"><a href="#设计模式" class="headerlink" title="设计模式"></a>设计模式</h1><h1 id="设计模式分类"><a href="#设计模式分类" class="headerlink" title="设计模式分</summary>
<category term="c++" scheme="https://frankho-hwc.github.io/tags/c/"/>
<category term="工作" scheme="https://frankho-hwc.github.io/tags/%E5%B7%A5%E4%BD%9C/"/>
</entry>
<entry>
<title>图像质量评价指标</title>
<link href="https://frankho-hwc.github.io/2023/03/09/%E5%9B%BE%E5%83%8F%E8%B4%A8%E9%87%8F%E8%AF%84%E4%BB%B7%E6%8C%87%E6%A0%87/"/>
<id>https://frankho-hwc.github.io/2023/03/09/%E5%9B%BE%E5%83%8F%E8%B4%A8%E9%87%8F%E8%AF%84%E4%BB%B7%E6%8C%87%E6%A0%87/</id>
<published>2023-03-09T14:17:14.000Z</published>
<updated>2023-06-06T13:46:13.167Z</updated>
<content type="html"><![CDATA[<h1 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h1><p>图像质量评估(Image Quality Assessment)对于计算机视觉任务内是很重要的一部分,根据是否是以人的主观观察还是客观标准可以分为主观评价指标和客观评价指标。其中图像质量客观评价可分为全参考(Full-Reference,FR),部分参考(Reduced-Reference,RR)和无参考(No-Reference,NR)三种类型。</p><h1 id="评价指标"><a href="#评价指标" class="headerlink" title="评价指标"></a>评价指标</h1><h2 id="全参考"><a href="#全参考" class="headerlink" title="全参考"></a>全参考</h2><h3 id="MAE"><a href="#MAE" class="headerlink" title="MAE"></a>MAE</h3><p>平均绝对误差,预测值与真实值的绝对误差的平均值<br>$$<br>MAE(y,\hat y) = \frac{1}{MN}\sum_{i=0}^{M}\sum_{j = 0}^{N}|x(i,j) - \hat x (i,j)|<br>$$</p><h3 id="MSE"><a href="#MSE" class="headerlink" title="MSE"></a>MSE</h3><p>均方误差(Mean Square Error),预测值与真实值的绝对平方误差的平均值,实际上就是两幅图像的所有的像素值的差的平方和,再求平均值,最终得到的是一个数值。MSE做损失函数可以抑制噪声<br>$$<br>MSE(y,\hat y) = \frac{1}{MN}\sum_{i=0}^{M}\sum_{j = 0}^{N}[x(i,j) - \hat x (i,j)]^2<br>$$</p><h3 id="SNR"><a href="#SNR" class="headerlink" title="SNR"></a>SNR</h3><p>给定一个给定一个大小为m×n的灰度图 I 和噪声图 K,已知其MSE,定义SNR如下<br>$$<br>SNR = 10 \cdot \lg [\frac{\sum_{i=0}^{M}\sum_{j = 0}^{N}x^2(i,j)}{MSE}] (db)<br>$$</p><h3 id="PSNR"><a href="#PSNR" class="headerlink" title="PSNR"></a>PSNR</h3><p>峰值信噪比(Peak Signal to Noise Ratio, PSNR)是一种评价图像质量的度量标准。因为PSNR值具有局限性,所以它只是衡量最大值信号和背景噪音之间的图像质量参考值。PSNR的单位为dB,其值越大,图像失真越少。一般来说,PSNR高于40dB说明图像质量几乎与原图一样好;在30-40dB之间通常表示图像质量的失真损失在可接受范围内;在20-30dB之间说明图像质量比较差;PSNR低于20dB说明图像失真严重。</p><p>给定一个给定一个大小为m×n的灰度图 I 和噪声图 K,已知其MSE,定义PSNR如下<br>$$<br>PSNR = 10 \cdot \log_{10}(\frac{MAX_I^2}{MSE}) (db)<br>$$<br>其中,$MAX_I$为图片可能的最大像素值,即B-bit的图像的$MAX_I$值为 $2^B-1$。一般地,针对 uint8 数据,最大像素值为255;针对浮点型数据,最大像素值为1。</p><p>有三种方法来计算彩色RGB图像的PSNR:</p><ol><li>分别计算 RGB 三个通道的 PSNR,然后取平均值;</li><li>或者计算RGB各个通道的均方差的均值,然后统一求PSNR;</li><li>或者把RGB转化为 YCbCr,然后只计算 Y(亮度)分量的PSNR。</li></ol><h3 id="SSIM"><a href="#SSIM" class="headerlink" title="SSIM"></a>SSIM</h3><p>结构相似性指数(structural similarity index,SSIM)是一种用于量化两幅图像间的结构相似性的指标。与L2损失函数不同,SSIM仿照人类的视觉系统(Human Visual System,HVS)实现了结构相似性的有关理论,对图像的局部结构变化的感知敏感。SSIM从亮度、对比度以及结构量化图像的属性,用均值估计亮度,方差估计对比度,协方差估计结构相似程度。SSIM值的范围为0至1,越大代表图像越相似。如果两张图片完全一样时,SSIM值为1。</p><p>给定x,y两张图片,两者之间的照明度(luminance)、对比度 (contrast) 和结构 (structure)分别如下公式所示:<br>$$<br>\begin{align}<br>l(x,y) =& \frac{2 \mu_x\mu_y + c_1}{\mu_x^2+\mu_y^2+c_1}\<br>c(x,y) =& \frac{2\sigma_x\sigma_y+c_2}{\sigma_x^2+\sigma_y^2+c_2}\<br>s(x,y) =& \frac{\sigma_{xy}+c_3}{\sigma_x\sigma_y+c_3}<br>\end{align}<br>$$<br>定义<br>$$<br>SSIM(x,y) = [l(x,y)^\alpha \cdot c(x,y)^\beta \cdot s(x,y)^\gamma]<br>$$<br>令$\alpha,\beta,\gamma$ 均为1,则SSIM表达式为<br>$$<br>SSIM(x,y) = \frac{(2\mu_x\mu_y + c_1)(2\sigma_{xy} + c_2)}{(\mu_x^2+\mu_y^2+c_1)(\sigma_x^2+\sigma_y^2+c_2)}<br>$$<br>其中, $\mu_x$ </p><p> 是x的平均值,$\sigma_x^2$ 是x的方差, $\mu_y$ 是y的平均值,$\sigma_y^2$ 是y的方差,$\sigma_{xy}$ 是x 和 y 的协方差, $c_1 = (k_1L)^2,c_2 = (k_2L)^2$ 是两个用于维持稳定的常数,避免出现除零的情况,L 为像素值的范围,表示B-bit的图像的L值为 $2^B-1$ 。一般地,针对 uint8 数据,最大像素值为255。针对浮点型数据,最大像素值为1。一般情况下,k1=0.01,k2=0.03。</p><h3 id="总结"><a href="#总结" class="headerlink" title="总结"></a>总结</h3><p>PSNR与MSE都是通过计算待评图像与参考图像之间像素误差的全局大小来衡量图像质量好坏的。PSNR值越大,表明待评图像与参考图像之间的失真较小,图像质量较好。而MSE的值越小,表明图像质量越好。这两种方法比较简单,且容易实现,在图像去噪等方面受到广泛应用。但这类算法是从图像像素值的全局统计出发,未考虑人眼的局部视觉因素,所以对于图像局部质量无从把握。</p><h3 id="IFC"><a href="#IFC" class="headerlink" title="IFC"></a>IFC</h3><p>信息保真度准则(IFC)</p><h3 id="VIF"><a href="#VIF" class="headerlink" title="VIF"></a>VIF</h3><p>算法介绍:<a href="https://live.ece.utexas.edu/research/Quality/VIF.htm">https://live.ece.utexas.edu/research/Quality/VIF.htm</a></p><p>视觉信息保真度(Visual Information Fidelity,VIF),是由德克萨斯大学奥斯汀分校的图像和视频工程实验室(<em>LIVE: Laboratory for Image and Video Engineering</em>)提出的算法。</p><p>VIF算法将图像/视频的质量评估问题看作一个信息保真的问题,并从信息通信和共享的角度来评估图像/视频的质量。</p><p>VIF认为:</p><ul><li>人眼所看到的图像是通过HVS过滤之后而提取到的信息,在这个过程中,人类视觉系统也是一个简单的失真通道</li><li>在经过HVS之前,失真图像只是比原始图像多经过了一个失真通道</li><li>利用信息论的理论,把人眼从失真图像中提取到的信息和人眼从原始图像中提取到的信息进行对比,从而得到符合人主观感知的图像质量</li></ul><p>整体VIF算法中包含三个部分:<code>Source Model</code>,<code>Distortion Model</code>,<code>HVS Model</code>,具体如下图所示:</p><p><img src="/2023/03/09/%E5%9B%BE%E5%83%8F%E8%B4%A8%E9%87%8F%E8%AF%84%E4%BB%B7%E6%8C%87%E6%A0%87/1.gif" alt="img"></p><p>具体内容可见[VIF质量评估方法简介 | 17哥 (wangwei1237.github.io)](<a href="https://wangwei1237.github.io/2021/03/05/intoduction-to-VIF/#:~:text=%E8%A7%86%E8%A7%89%E4%BF%A1%E6%81%AF%E4%BF%9D%E7%9C%9F%E5%BA%A6%EF%BC%88VIF%EF%BC%89%E6%98%AF%E5%9F%BA%E4%BA%8E">https://wangwei1237.github.io/2021/03/05/intoduction-to-VIF/#:~:text=视觉信息保真度(VIF)是基于</a> 自然场景统计( natural scene statistics ),和 人类视觉系统( human visual system ) 提取图像信息的一种全参考的图像质量评估指标,并且与人类对视觉质量的判断具有良好的相关性。),该文章较为详细地介绍了VIF算法如何进行图像质量评价的。</p><h2 id="部分参考"><a href="#部分参考" class="headerlink" title="部分参考"></a>部分参考</h2><h2 id="无参考"><a href="#无参考" class="headerlink" title="无参考"></a>无参考</h2><p>无参考方法也称为首评价方法,因为一般的理想图像很难获得,所以这种完全脱离了对理想参考图像依赖的质量评价方法应用较为广泛。无参考方法一般都是基于图像统计特性。</p><h3 id="均值"><a href="#均值" class="headerlink" title="均值"></a>均值</h3><p>标准差是指图像像素灰度值相对于均值的离散程度。如果标准差越大,表明图像中灰度级分别越分散,图像质量也就越好,其计算公式为:<br>$$<br>u= \frac{1}{MN}\sum_{i= 1}^{M}\sum_{j=1}^{N}F(i,j)<br>$$</p><h3 id="标准差"><a href="#标准差" class="headerlink" title="标准差"></a>标准差</h3><p>标准差是指图像像素灰度值相对于均值的离散程度。如果标准差越大,表明图像中灰度级分别越分散,图像质量也就越好,其计算公式为:<br>$$<br>std = \frac{1}{MN}\sum_{i= 1}^{M}\sum_{j=1}^{N}\sqrt{(F(i,j) - u)^2}<br>$$</p><h3 id="平均梯度"><a href="#平均梯度" class="headerlink" title="平均梯度"></a>平均梯度</h3><p>平均梯度:反应图像中细节反差和纹理变化,他在一定程度上反应图像的清晰度,公式如下<br>$$<br>\nabla G = \frac{1}{MN}\sum_{i= 1}^{M}\sum_{j=1}^{N}\sqrt{\Delta xF(i,j)^2+\Delta y F(i,j)^2}<br>$$</p><p>其中,$\Delta xF(i,j)^2$表示像素点(i,j)在x上的一阶差分,$\Delta yF(i,j)^2$表示该点在y方向上的一阶差分。</p><h3 id="熵"><a href="#熵" class="headerlink" title="熵"></a>熵</h3><p>指图像的平均信息量,从信息论的角度衡量图像中信息的多少,信息熵越大,说明图像包含的信息越多。假设图像中各个像素点的灰度值之间时相互独立的,图像的灰度分布为p={p1,p2,…..,pi,…….pn},其中Pi表示灰度值为i的像素的个数与总像素个数之比,而n为灰度级总数,则计算公式为<br>$$<br>H = -\sum_{i=0}^{255}p_i \logp_i<br>$$<br>其中,$p_i$是某个灰度在该图像中出现的概率,可由灰度直方图获得。</p><h3 id="NIQE-自然图像评估"><a href="#NIQE-自然图像评估" class="headerlink" title="NIQE(自然图像评估)"></a>NIQE(自然图像评估)</h3><p>论文地址:<a href="https://doi.org/10.1109/LSP.2012.2227726">https://doi.org/10.1109/LSP.2012.2227726</a></p><p>NIQE基于一组“质量感知”特征,并将其拟合到MVG模型中。质量感知特征源于一个简单但高度正则化的NSS模型。然后,将给定的测试图像的NIQE指标表示为从测试图像中提取的NSS特征的MVG模型与从自然图像语料中提取的质量感知特征的MVG模型之间的距离。整个过程由五步操作完成:</p><h4 id="空域NSS特征提取"><a href="#空域NSS特征提取" class="headerlink" title="空域NSS特征提取"></a>空域NSS特征提取</h4><p>一个经典的空域NSS模型:<br>$$<br>\hat I(i,j) = \frac{I(i,j) - \mu(i,j)}{\sigma(i,j) + 1} \tag{1}<br>$$<br>式中,$i \in {1,2,…,M},j \in {1,2,…,N }$为图像的空间坐标,$M$与$N$为图像的空间维度,且<br>$$<br>\begin{align}<br> \mu(i,j) &= \sum_{k = -k}^{K}\sum_{l = -L}^{L} w_{k,l}I(j+k, j+l) \tag {2} \<br> \sigma(i,j) &= \sqrt{\sum_{k = -k}^{K}\sum_{l = -L}^{L}w_{k,l}[I(i,j) - \mu(i,j)]^2} \tag 3<br>\end{align}<br>$$<br>在上述两个式子中,${w_{k,l}|k = -K,..,K;l = -L,…,L }$是一个从3个标准差(K=L=3)中采样并重新缩放到单位体积的二维循环对称高斯权函数</p><h4 id="图像块选取"><a href="#图像块选取" class="headerlink" title="图像块选取"></a>图像块选取</h4><p> 一旦图像的系数由(1)式计算出,整张图像会被分割成$P × P$ 的块。然后从每个块的系数中计算出特殊的NSS特征。方差(3)在之前的基于NSS的图片分析中常常被忽视。但是它在结构化图片信息上有丰富的内容。这些内容可以被用来量化局部图片的锐利度。(从美学上认为一幅图片越锐利它的成像效果会越好,平滑模糊代表一种视觉信息的潜在损失。)将$P × P$ 的图像块用$b = 1 , 2 , . . . , B$做标记,再用一种直接的方法计算每一块$b$平均局部偏移范围:<br>$$<br>\delta(b) = \sum\sum_{(i,j) \in patchb}\sigma (i,j) \tag 4<br>$$<br>式中,$\delta$表示局部活跃(锐利)度</p><p>在求得每块的锐利度后,超过门限$T$的$\delta$将会被选中(即$\delta > T$)。门限$T$由块的峰值锐利度$p$得到,在原文中p = 0.75,按照作者的观察p在一个小范围$[0.6,0.9]$内变化</p><h4 id="特征化图像块"><a href="#特征化图像块" class="headerlink" title="特征化图像块"></a>特征化图像块</h4><p> 先前以NSS为基础图像质量的研究表明广义高斯分布可以有效捕捉自然图像与失真图像公式(1)的联系。<br>以0为均值的广义高斯分布公式(GGD)为:<br>$$<br>f(x;\alpha,\beta) = \frac{\alpha}{2\beta \Gamma(1/\alpha)}exp(-(\frac{|x|}{\beta})) \tag 5<br>$$<br>式中的Gamma函数$\Gamma(\cdot)$ 为:<br>$$<br>\Gamma(a) = \int_{0}^{\infty} t^{\alpha - 1}e^{-t} dt (a > 0)\tag 6<br>$$<br>GGD的参数$(\alpha,\beta)$可以使用一种moment-matching的方法估计出</p><p>这样,我们用一个均值为0的对称广义高斯分布(AGGD)将领域系数的乘积很好的建模:<br>$$<br>\begin{align}<br>f(x;\gamma,\beta_l,\beta_r) =<br>\begin{cases}<br>\frac{\gamma}{\beta_l+\beta_{\gamma}\Gamma({\frac{1}{\gamma})}\exp(-(\frac{-x}{\beta_{l}})^\gamma)} & (\forall x \leq 0) \<br>\frac{\gamma}{\beta_l+\beta_{\gamma}\Gamma({\frac{1}{\gamma})}\exp(-(\frac{-x}{\beta_{r}})^\gamma)} & (\forall x \geq 0) \ \tag 7</p><p>\end{cases}<br>\end{align}<br>$$<br>AGGD的参数$({\gamma},{\beta}_l,{\beta}_r)$可以被“Mutiscale skewed heavy tailed model for texture analysis”这篇文章中的方法计算出。AGGD的均值也很有用:<br>$$<br>\eta = (\beta_r - \beta_l)\frac{\Gamma(\frac{2}{\gamma})}{\Gamma(\frac{1}{\gamma})} \tag 8<br>$$</p><h4 id="MVG模型"><a href="#MVG模型" class="headerlink" title="MVG模型"></a>MVG模型</h4><p> 通过将自然图像块与MVG模型密度函数拟合,可以得到一个简单的NSS特征模型,MVG模型密度函数为:<br>$$</p><p>$$</p><h4 id="NIQE指标"><a href="#NIQE指标" class="headerlink" title="NIQE指标"></a>NIQE指标</h4><p> 用NSS特征模型与提取自失真图像特征的MVG间的距离来表示失真图像的质量:<br>$$<br>D(V_1,V_2,\Sigma_1,\Sigma_2) = \sqrt{(v_1 - v_2)^T(\frac{\Sigma_1+\Sigma_2}{2})^2(v_1 - v_2)}<br>$$</p><p>其中$v_1,v_2,\Sigma_1,\Sigma_2$分别表示自然MVG模型与失真图像MVG模型的均值向量和协方差矩阵。</p><p>但是NIQE指标还是用的比较少,因为这个标准好像不太行。</p><h1 id="参考文章"><a href="#参考文章" class="headerlink" title="参考文章"></a>参考文章</h1><p><a href="https://zhuanlan.zhihu.com/p/309892873">有真实参照的图像质量的客观评估指标:SSIM、PSNR和LPIPS - 知乎 (zhihu.com)</a></p><p><a href="https://blog.csdn.net/qq_27825451/article/details/102954096">(41条消息) 图像质量评估各项指标(一)_图像的评价指标_LoveMIss-Y的博客-CSDN博客</a></p><p><a href="https://blog.csdn.net/qq_23304241/article/details/80953613">(41条消息) 图像质量评价概述(评估指标、传统检测方法)_图像主观评价标准有哪些_qq_23304241的博客-CSDN博客</a></p><p><a href="https://blog.csdn.net/bby1987/article/details/109373572">(41条消息) 图像质量评估指标:MSE,PSNR,SSIM_图像mse_拜阳的博客-CSDN博客</a>.</p><p>[VIF质量评估方法简介 | 17哥 (wangwei1237.github.io)](<a href="https://wangwei1237.github.io/2021/03/05/intoduction-to-VIF/#:~:text=%E8%A7%86%E8%A7%89%E4%BF%A1%E6%81%AF%E4%BF%9D%E7%9C%9F%E5%BA%A6%EF%BC%88VIF%EF%BC%89%E6%98%AF%E5%9F%BA%E4%BA%8E">https://wangwei1237.github.io/2021/03/05/intoduction-to-VIF/#:~:text=视觉信息保真度(VIF)是基于</a> 自然场景统计( natural scene statistics ),和 人类视觉系统( human visual system ) 提取图像信息的一种全参考的图像质量评估指标,并且与人类对视觉质量的判断具有良好的相关性。)</p><p><a href="https://blog.csdn.net/qq_30124657/article/details/114268329">(43条消息) 无参考图像评价指标NIQE——自然图像质量_凌川御雪的博客-CSDN博客</a></p>]]></content>
<summary type="html"><h1 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h1><p>图像质量评估(Image Quality Assessment)对于计算机视觉任务内是很重要的一部分,根据是否是以人的主观观察还是客观标准可</summary>
<category term="计算机视觉" scheme="https://frankho-hwc.github.io/tags/%E8%AE%A1%E7%AE%97%E6%9C%BA%E8%A7%86%E8%A7%89/"/>
<category term="低亮图像增强" scheme="https://frankho-hwc.github.io/tags/%E4%BD%8E%E4%BA%AE%E5%9B%BE%E5%83%8F%E5%A2%9E%E5%BC%BA/"/>
</entry>
<entry>
<title>c++八股文</title>
<link href="https://frankho-hwc.github.io/2023/03/09/c++%E5%85%AB%E8%82%A1%E6%96%87/"/>
<id>https://frankho-hwc.github.io/2023/03/09/c++%E5%85%AB%E8%82%A1%E6%96%87/</id>
<published>2023-03-09T08:50:01.000Z</published>
<updated>2023-03-24T05:24:18.807Z</updated>
<content type="html"><![CDATA[<h1 id="问题-1-c-中内存的分布情况"><a href="#问题-1-c-中内存的分布情况" class="headerlink" title="问题(1) c++中内存的分布情况"></a>问题(1) c++中内存的分布情况</h1><p>答: c++内存分为五个部分</p><ul><li>栈 由编译器管理分配和回收,存放非静态局部变量和函数参数以及返回值。栈是向下增长的。</li><li>堆 用于程序运行时的动态内存分配,堆是向上增长的</li><li>代码区 存放程序的二进制代码</li><li>全局/静态数据区:分为初始化和未初始化两个相邻区域,存放初始化和未初始化的全局变量和静态变量</li><li>常量区:存放常量,一般不允许修改。</li></ul><h1 id="问题-2-栈和堆的区别"><a href="#问题-2-栈和堆的区别" class="headerlink" title="问题(2) 栈和堆的区别"></a>问题(2) 栈和堆的区别</h1><ul><li><p>栈</p><p>由编译器进行管理,在需要的时候编译器自动分配空间,在不需要的时候编译器自动回收空间,一般是保存局部变量和函数参数及返回值。内存空间是连续的,在函数调用的时候,首先入栈的是下一条可执行指令的地址,然后是函数的各个参数。</p><p>⼤多数编译器中,参数是从右向左⼊栈(原因在于采⽤这种顺序,是为了让程序员在使⽤C/C++的“函数参数⻓度可变”这个特性时更⽅便。**(这也是为什么默认形参是从右往左定义)**。如果是从左向右压栈,第⼀个参数(即描述可变参数表各变ᰁ类型的那个参数)将被放在 栈底,由于可变参的函数第⼀步就需要解析可变参数表的各参数类型,即第⼀步就需要得到上述参数,因此,将它放在栈底是很不⽅便的。)本次函数调⽤结束时,局部变ᰁ先出栈,然后是参数,最后是栈顶指针最开始存放的地 址,程序由该点继续运⾏,不会产⽣碎⽚。</p><p>栈是由高地址向低地址扩展。</p></li><li><p>堆则是由程序员自主分配,使用new/delete或者malloc/free进行分配。如果不进行回收的话,会造成内存泄漏问题。堆的内存空间是不连续的。系统内有一个空闲链表,当有程序申请内存时,系统遍历空闲链表找到第⼀个⼤于等于申请 ⼤⼩的空间分配给程序,⼀般在分配程序的时候,也会空间头部写⼊内存⼤⼩,⽅便 delete 回收空间⼤⼩。如果有剩余的话,会将剩余内存插入空闲链表中,这样会产生内存碎片。</p><p>堆是低地址向高地址扩展,空间较大,比较灵活。</p></li></ul><h1 id="问题-3-C-如何申请空间"><a href="#问题-3-C-如何申请空间" class="headerlink" title="问题(3) C++如何申请空间"></a>问题(3) C++如何申请空间</h1><p>C++使用new运算符来为任意数据类型申请内存,new在申请内存的时候创造了一个对象。然后使用delete运算符来归还内存。同样地,可以使用malloc和free来进行申请和销毁内存,但是更建议使用new/free来申请/销毁内存。</p><h1 id="问题-4"><a href="#问题-4" class="headerlink" title="问题(4)"></a>问题(4)</h1><h1 id="问题-5-函数传递参数的几种方式"><a href="#问题-5-函数传递参数的几种方式" class="headerlink" title="问题(5) 函数传递参数的几种方式"></a>问题(5) 函数传递参数的几种方式</h1><ul><li>值传参 形参是实参的拷贝,对形参的改变不会影响实参</li><li>引用传参 </li><li>指针传参 是值传参的一种,不过形参接收的是实参的地址,对形参进行操作,等价于对实参的操作</li></ul><h1 id="问题-6-类中构造函数调用的顺序,和析构的顺序"><a href="#问题-6-类中构造函数调用的顺序,和析构的顺序" class="headerlink" title="问题(6) 类中构造函数调用的顺序,和析构的顺序"></a>问题(6) 类中构造函数调用的顺序,和析构的顺序</h1><p>当一个类D,有虚继承A,正常继承B,然后类中有类对象成员C时:</p><p><strong>先虚基类,再基类,再类成员函数,再本类</strong></p><p>(1)、构造顺序 A->B->C->D</p><p>(2)、析构顺序D->C->B->A</p><p>(3)、当有多个类成员对象时,其构造函数调用的顺序为类对象声明的顺序。</p><h1 id="问题-7-指针和引用的区别"><a href="#问题-7-指针和引用的区别" class="headerlink" title="问题(7) 指针和引用的区别"></a>问题(7) 指针和引用的区别</h1><p>指针是一个变量,存储的是变量的地址,指向的是一块内存空间。</p><p>引用是原变量的一个别名,跟原变量本质是一个东西。</p><figure class="highlight c++"><table><tr><td class="code"><pre><span class="line"><span class="type">int</span> a = <span class="number">996</span>;</span><br><span class="line"><span class="type">int</span> *p = &a; <span class="comment">// p是指针, &在此是求地址运算</span></span><br><span class="line"><span class="type">int</span> &r = a; <span class="comment">// r是引用, &在此起标识作用</span></span><br></pre></td></tr></table></figure><p>指针可以指向NULL,而引用不行。</p><p>指针可以有多级,而引用不可以</p><p>指针可以在定义的时候不初始化,而引用必须在定义的时候初始化。</p><p>指针初始化后可以再改变,而引用不行</p><p>指针自增指向的是内存的下一个单元,而引用自增就是变量加一</p><h1 id="问题-8-指针和数组的区别"><a href="#问题-8-指针和数组的区别" class="headerlink" title="问题(8) 指针和数组的区别"></a>问题(8) 指针和数组的区别</h1><p>数组是存储多个相同类型数据的集合,在内存上是连续存放的,数组的存储空间,不是在静态区就是在栈上。</p><p>指针很灵活,它可以指向任意类型的数据的地址。</p><p>传参时,数组会退化为一个指针。</p><h2 id="指针数组"><a href="#指针数组" class="headerlink" title="指针数组"></a>指针数组</h2><p>实际上是一个数组,数组的每一个元素都存储的是一个指针</p><figure class="highlight c++"><table><tr><td class="code"><pre><span class="line"><span class="type">int</span>* arr[<span class="number">8</span>];</span><br><span class="line"><span class="comment">//优先级问题:[]的优先级比*高</span></span><br><span class="line"><span class="comment">//说明arr是一个数组,而int*是数组里面的内容</span></span><br><span class="line"><span class="comment">//这句话的意思就是:arr是一个含有8和int*的数组</span></span><br></pre></td></tr></table></figure><h2 id="数组指针"><a href="#数组指针" class="headerlink" title="数组指针"></a>数组指针</h2><p>它实际上是一个指针,该指针指向一个数组。</p><figure class="highlight c++"><table><tr><td class="code"><pre><span class="line"><span class="built_in">int</span> (*arr)[<span class="number">8</span>];</span><br></pre></td></tr></table></figure><h1 id="问题-9-野指针和悬挂指针,并如何避免"><a href="#问题-9-野指针和悬挂指针,并如何避免" class="headerlink" title="问题(9) 野指针和悬挂指针,并如何避免"></a>问题(9) 野指针和悬挂指针,并如何避免</h1><h2 id="野指针"><a href="#野指针" class="headerlink" title="野指针"></a>野指针</h2><p>访问一个已销毁或者访问受限的内存区域的指针,野指针不能判断是否为NULL来避免。指针指向了一块随机的空间,不受程序控制。</p><h2 id="悬挂指针"><a href="#悬挂指针" class="headerlink" title="悬挂指针"></a>悬挂指针</h2><p>指针正常初始化,曾指向一个对象,该对象被销毁了,但是指针未制空,那么就成了悬空指针。</p><h2 id="野指针产生的原因"><a href="#野指针产生的原因" class="headerlink" title="野指针产生的原因"></a>野指针产生的原因</h2><ul><li><p>指针定义时未初始化</p><p>指针在被定义的时候,如果程序不对其进行初始化的话,它会随机指向一个区域,因为任意指针变量(出了static修饰的指针)它的默认值都是随机的。</p></li><li><p>被释放时没有置空</p><p>我们在用malloc()/new开辟空间的时候,要检查返回值是否为空,如果为空,则开辟失败;如果不为空,则指针指向的是开辟的内存空间的首地址。指针指向的内存空间在用free()和delete释放后,如果程序员没有对其进行置空或者其他赋值操作的话,就会成为一个野指针</p></li><li><p>指针操作超越变量作用域:不要返回指向栈内存的指针或者引用,因为栈内存在函数结束的时候会被释放。</p></li></ul><h2 id="野指针的危害"><a href="#野指针的危害" class="headerlink" title="野指针的危害"></a>野指针的危害</h2><h2 id="如何避免"><a href="#如何避免" class="headerlink" title="如何避免"></a>如何避免</h2><ul><li>在定义指针时进行初始化操作置NULL,指针指向的内存在free或delete时将指针置空</li><li>使用智能指针</li></ul><h1 id="问题-10-const修饰指针"><a href="#问题-10-const修饰指针" class="headerlink" title="问题(10) const修饰指针"></a>问题(10) const修饰指针</h1><p>const修饰指针创建了一个指针常量,但是const的位置会导致这个指针常量有不同的含义。</p><figure class="highlight c++"><table><tr><td class="code"><pre><span class="line"> <span class="type">const</span> <span class="type">int</span> *p1;<span class="comment">//const修饰的是int,表示p1指向的变量值不可改变,指针本身可以改变</span></span><br><span class="line"> <span class="comment">//p1指向的值为常量 </span></span><br><span class="line"><span class="type">int</span> * <span class="type">const</span> p2 = days;<span class="comment">//const修饰的是p2,表示p2这个指针本身是无法修改的,但是其指向的值是可以修改的</span></span><br></pre></td></tr></table></figure><h1 id="问题-11-static定义静态变量"><a href="#问题-11-static定义静态变量" class="headerlink" title="问题(11) static定义静态变量"></a>问题(11) static定义静态变量</h1><h2 id="局部变量"><a href="#局部变量" class="headerlink" title="局部变量"></a>局部变量</h2><p>静态局部变量在声明时未赋初值,编译器也会将其赋0值,且静态变量储存于进程的全局数据区,即使函数返回,它的值也会保持不变。</p><h2 id="全局变量"><a href="#全局变量" class="headerlink" title="全局变量"></a>全局变量</h2><p>全局变量定义在函数体外部,在全局数据区分配存储空间,且编译器会自动对其初始化。</p><p>普通全局变量对整个工程可见,其他文件可以使用extern外部声明后直接使用。也就是说其他文件不能再定义一个与其相同名字的变量了(否则编译器会认为它们是同一个变量)。</p><p>静态全局变量只对当前文件可见,其他文件不可见</p><figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">普通全局变量对整个工程可见,其他文件可以使用extern外部声明后直接使用。也就是说其他文件不能再定义一个与其相同名字的变量了(否则编译器会认为它们是同一个变量)。</span><br></pre></td></tr></table></figure><h2 id="函数"><a href="#函数" class="headerlink" title="函数"></a>函数</h2><p>函数的使用方式与全局变量类似,在函数的返回类型前加上static,就是静态函数。其特性如下:</p><ul><li>静态函数只能在声明它的文件中可见,其他文件不能引用该函数</li><li>不同的文件可以使用相同名字的静态函数,互不影响</li></ul><h2 id="面向对象的静态函数成员"><a href="#面向对象的静态函数成员" class="headerlink" title="面向对象的静态函数成员"></a>面向对象的静态函数成员</h2><p>在类内数据成员的声明前加上static关键字,该数据成员就是类内的静态数据成员。其特点如下:</p><ul><li>静态数据成员存储在全局数据区,静态数据成员在定义时分配存储空间,所以不能在类声明中定义</li><li>静态数据成员是类的成员,无论定义了多少个类的对象,静态数据成员的拷贝只有一个,且对该类的所有对象可见。也就是说任一对象都可以对静态数据成员进行操作。而对于非静态数据成员,每个对象都有自己的一份拷贝。</li><li>由于上面的原因,静态数据成员不属于任何对象,在没有类的实例时其作用域就可见,在没有任何对象时,就可以进行操作<br>和普通数据成员一样,静态数据成员也遵从public, protected, private访问规则</li><li>静态数据成员的初始化格式:<数据类型><类名>::<静态数据成员名>=<值></li><li>类的静态数据成员有两种访问方式:<类对象名>.<静态数据成员名> 或 <类类型名>::<静态数据成员名></li></ul><p>同全局变量相比,使用静态数据成员有两个优势:</p><ul><li>静态数据成员没有进入程序的全局名字空间,因此不存在与程序中其它全局名字冲突的可能性<br>可以实现信息隐藏。</li><li>静态数据成员可以是private成员,而全局变量不能</li></ul><h2 id="面向对象的静态成员函数"><a href="#面向对象的静态成员函数" class="headerlink" title="面向对象的静态成员函数"></a>面向对象的静态成员函数</h2><ul><li>静态成员函数没有this指针,它无法访问属于类对象的非静态数据成员,也无法访问非静态成员函数,它只能调用其余的静态成员函数</li><li>出现在类体外的函数定义不能指定关键字static</li><li>非静态成员函数可以任意地访问静态成员函数和静态数据成员</li></ul><p>总结就是静态成员函数只能访问静态成员函数,非静态则都可以。</p><h1 id="问题-12-const和volatile"><a href="#问题-12-const和volatile" class="headerlink" title="问题(12) const和volatile"></a>问题(12) const和volatile</h1><ul><li><p>const只在编译期有效,而运行期无效。</p><p>const在编译期保证在C的“源代码”里面,没有对其修饰的变量进行修改的地方(如有则报错,编译不通过),而运行期该变量的值是否被改变则不受const的限制。</p></li><li><p>volatile在编译期和运行期都有用</p></li></ul><p> 在编译期告诉编译器:请不要做自以为是的优化,这个变量的值可能会变掉;</p><p> 在运行期:每次用到该变量的值,都从内存中取该变量的值。</p><ul><li><p>const和volatile可以修饰同一个变量</p><p>表示一个变量在程序编译期不能被修改且不能被优化;在程序运行期,变量值可修改,但每次用到该变量的值都要从内存中读取,以防止意外错误。</p></li></ul><p>例子</p><figure class="highlight c++"><table><tr><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string"><stdio.h></span></span></span><br><span class="line"></span><br><span class="line"><span class="type">const</span> <span class="keyword">volatile</span> <span class="type">int</span> a = <span class="number">10</span>;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">main</span><span class="params">(<span class="type">void</span>)</span></span>{</span><br><span class="line"> <span class="comment">// a = 100; // 非法</span></span><br><span class="line"> <span class="type">int</span>* aPtr = &a; <span class="comment">//必须用指针先从内存取出来</span></span><br><span class="line"> *aPtr = <span class="number">100</span>;</span><br><span class="line"></span><br><span class="line"> <span class="built_in">printf</span>(<span class="string">"%d,%d"</span>,a,*aPtr);</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h1 id="问题-13-宏定义-define和常量const的区别"><a href="#问题-13-宏定义-define和常量const的区别" class="headerlink" title="问题(13) 宏定义#define和常量const的区别"></a>问题(13) 宏定义#define和常量const的区别</h1><ul><li><p>编译器的处理方式不同</p><p>define宏是在预处理阶段展开</p><p>const常量是在编译运行阶段使用</p></li><li><p>类型和安全检查不同</p><p>define宏没有类型,不做任何类型检查,仅仅是展开。</p><p>const常量有具体的类型,在编译阶段会执行类型检查。</p></li><li><p>存储方式不同</p><p>define宏仅仅是展开,有多少地方使用,就展开多少次,不会分配内存。(宏定义不分配内存,<strong>变量定义</strong>分配内存。)</p><p>const常量会在内存中分配(可以是堆中也可以是栈中)。</p></li><li><p>宏替换只作替换,不做计算,不做表达式求解;</p><p>宏预编译时就替换了,<strong>程序运行时,并不分配内存。</strong></p></li><li><p>const 可以节省内存空间</p><p>define给出的是立即数,会在内存中有多份拷贝。而const常量只是给出对应内存的地址,内存中只会有一份拷贝。</p></li></ul><h2 id="两者间的比较"><a href="#两者间的比较" class="headerlink" title="两者间的比较"></a>两者间的比较</h2><p>C++可以用两者来定义常量,但是const优点更多</p><ul><li>const常量有数据类型,而define没有。编译器可以对前者进行安全检查,而后者只是字符替换,并不会进行安全检查,并且字符替换可能会出现意想不到的错误</li><li>有些集成化工具能支持对const常量的调试,但是并不支持对define常量的调试</li></ul><h1 id="问题-14-C-中struct和class的区别"><a href="#问题-14-C-中struct和class的区别" class="headerlink" title="问题(14) C++中struct和class的区别"></a>问题(14) C++中struct和class的区别</h1><p>struct是一种数据类型,不能定义函数。在c++中,struct得到了扩展,<strong>可以包含成员函数,可以实现继承,可以实现多态。</strong></p><p>struct和class的区别</p><ol><li><p><strong>默认的继承访问权。class默认的是private,strcut默认的是public。</strong></p></li><li><p><strong>默认访问权限:struct作为数据结构的实现体,它默认的数据访问控制是public的,而class作为对象的实现体,它默认的成员变量访问控制是private的。</strong></p></li><li><p><strong>“class”这个关键字还用于定义模板参数,就像“typename”。但关建字“struct”不用于定义模板参数</strong></p></li><li><p><strong>class和struct在使用大括号{ }上的区别</strong><br><strong>关于使用大括号初始化</strong><br><strong>1.)class和struct如果定义了构造函数的话,都不能用大括号进行初始化</strong></p><p><strong>2.)如果没有定义构造函数,struct可以用大括号初始化。</strong></p><p><strong>3.)如果没有定义构造函数,且所有成员变量全是public的话,class可以用大括号初始化</strong></p></li></ol><h1 id="问题-15-C和C-的区别"><a href="#问题-15-C和C-的区别" class="headerlink" title="问题(15) C和C++的区别"></a>问题(15) C和C++的区别</h1><p>(1)、语法上:语法上C++的区别有头文件和命名空间的不同,C++允许我们自定义自己的空间,而C不可以;在关键字方面也有不同,如C++在动态内存管理上增加了new和delete,在指针上,C++增加了引用的概念,C++在关键字上,也还增加了auto类型等等。</p><p>(2)、函数上:C++支持函数重载,而C不支持,主要原因是C++函数的名字修饰和C不同,C++在函数名字修饰时,会把参数加在后面,如 int a(int b, char c) 会修饰成 _a_int_char,而C只会修饰成_a,所有C++中支持不同参数调用不同的函数,即支持函数重载; C++还有虚函数的概念,用以实现多态。</p><p>(3)、struct上:C的struct像是一个数据结构的集合,而C++的struct不仅成员函数,还有成员变量,同时还增加了访问权限的概念。</p><p>(4)、C是面向过程的语言,而C++是面向对象的语言,C++在C的基础上最大的变化就是增加了类的概念。</p><p>(5)、C++支持模板类来重用代码,提供更加强大的STL库</p><h1 id="问题-16-C-x2F-C-编译过程"><a href="#问题-16-C-x2F-C-编译过程" class="headerlink" title="问题(16) C/C++编译过程"></a>问题(16) C/C++编译过程</h1><p>预处理(展开宏和内联函数)->编译(编译成汇编语言)->汇编(编译成二进制文件)->链接(链接其他所包含的库文件)</p><h1 id="问题-17-智能指针有哪些,作用是什么"><a href="#问题-17-智能指针有哪些,作用是什么" class="headerlink" title="问题(17) 智能指针有哪些,作用是什么"></a>问题(17) 智能指针有哪些,作用是什么</h1><p>智能指针有shared_ptr,unique_ptr,weak_ptr</p><h2 id="shared-ptr"><a href="#shared-ptr" class="headerlink" title="shared_ptr"></a>shared_ptr</h2><h2 id="unique-ptr"><a href="#unique-ptr" class="headerlink" title="unique_ptr"></a>unique_ptr</h2><h2 id="weak-ptr"><a href="#weak-ptr" class="headerlink" title="weak_ptr"></a>weak_ptr</h2><h1 id="问题-18-C-的-STL-介绍(内存管理,allocator,函数,实现机理,多线程实-现等)"><a href="#问题-18-C-的-STL-介绍(内存管理,allocator,函数,实现机理,多线程实-现等)" class="headerlink" title="问题(18) C++ 的 STL 介绍(内存管理,allocator,函数,实现机理,多线程实 现等)"></a>问题(18) C++ 的 STL 介绍(内存管理,allocator,函数,实现机理,多线程实 现等)</h1><h1 id="问题-19-vector-使⽤的注意点及其原因,频繁对-vector-调⽤-push-back-性-能影响"><a href="#问题-19-vector-使⽤的注意点及其原因,频繁对-vector-调⽤-push-back-性-能影响" class="headerlink" title="问题(19) vector 使⽤的注意点及其原因,频繁对 vector 调⽤ push_back() 性 能影响"></a>问题(19) vector 使⽤的注意点及其原因,频繁对 vector 调⽤ push_back() 性 能影响</h1><h1 id="问题-20-map-和-set-有什么区别,分别⼜是怎么实现的?"><a href="#问题-20-map-和-set-有什么区别,分别⼜是怎么实现的?" class="headerlink" title="问题(20)map 和 set 有什么区别,分别⼜是怎么实现的?"></a>问题(20)map 和 set 有什么区别,分别⼜是怎么实现的?</h1><h1 id="问题-21-请你来说⼀说-STL-迭代器删除元素"><a href="#问题-21-请你来说⼀说-STL-迭代器删除元素" class="headerlink" title="问题(21)请你来说⼀说 STL 迭代器删除元素"></a>问题(21)请你来说⼀说 STL 迭代器删除元素</h1><h1 id="问题-22-请你来说⼀下-STL-中迭代器的作⽤,有指针为何还要迭代器"><a href="#问题-22-请你来说⼀下-STL-中迭代器的作⽤,有指针为何还要迭代器" class="headerlink" title="问题(22) 请你来说⼀下 STL 中迭代器的作⽤,有指针为何还要迭代器"></a>问题(22) 请你来说⼀下 STL 中迭代器的作⽤,有指针为何还要迭代器</h1><h1 id="问题-23-回答⼀下-STL-⾥-resize-和-reserve-的区别"><a href="#问题-23-回答⼀下-STL-⾥-resize-和-reserve-的区别" class="headerlink" title="问题(23) 回答⼀下 STL ⾥ resize 和 reserve 的区别"></a>问题(23) 回答⼀下 STL ⾥ resize 和 reserve 的区别</h1><h1 id="问题-24-C-强制类型转换有哪些"><a href="#问题-24-C-强制类型转换有哪些" class="headerlink" title="问题(24) C++强制类型转换有哪些"></a>问题(24) C++强制类型转换有哪些</h1><p>C++强制类型转换有四种</p><h2 id="static-cast"><a href="#static-cast" class="headerlink" title="static_cast"></a>static_cast</h2><p>在C++语言中static_cast用于数据类型的强制转换,强制将一种数据类型转换为另一种数据类型。例如将整型数据转换为浮点型数据,如</p><figure class="highlight c++"><table><tr><td class="code"><pre><span class="line"><span class="type">int</span> a = <span class="number">10</span>;</span><br><span class="line"><span class="type">int</span> b = <span class="number">3</span>;</span><br><span class="line"><span class="type">double</span> result = <span class="built_in">static_cast</span><<span class="type">double</span>>(a) / <span class="built_in">static_cast</span><<span class="type">double</span>>(b);</span><br></pre></td></tr></table></figure><p><strong>用法:static_cast <类型说明符> (变量或表达式)</strong></p><p><strong>主要做法</strong></p><ul><li>用于类层次结构中基类和派生类之间指针或引用的转换<br> 进行上行转换(把派生类的指针或引用转换成基类表示)是安全的<br> 进行下行转换(把基类的指针或引用转换为派生类表示),由于没有动态类型检查,所以是不安全的</li><li>用于基本数据类型之间的转换,转换安全又开发人员保证</li><li>把空指针转换成任何类型的指针</li><li>把任何类型的指针转换为void类型</li></ul><h2 id="const-cast"><a href="#const-cast" class="headerlink" title="const_cast"></a>const_cast</h2><p>我们知道,const关键字限制了变量的值不能被改变。</p><p>而const_cast正是强制去掉这种不能被修改的常数特性,<strong>但需要特别注意的是const_cast不是用于去除变量的常量性,而是去除指向常数对象的指针或引用的常量性,其去除常量性的对象必须为指针或引用。</strong></p><p><strong>用法:const_cast<type_id> (expression)</type_id></strong><br> 该运算符用来修改类型的const或volatile属性。除了const 或volatile修饰之外, type_id和expression的类型是一样的。<br> 常量指针被转化成非常量指针,并且仍然指向原来的对象;<br> 常量引用被转换成非常量引用,并且仍然指向原来的对象;常量对象被转换成非常量对象。</p><h2 id="reinterpret-cast"><a href="#reinterpret-cast" class="headerlink" title="reinterpret_cast"></a>reinterpret_cast</h2><p>reinterpret_cast主要是三个用途</p><p><strong>用法:reinterpret_cast<type_id> (expression)</type_id></strong><br> type-id必须是一个指针、引用、算术类型、函数指针或者成员指针。<br> 它可以把一个指针转换成一个整数,也可以把一个整数转换成一个指针(先把一个指针转换成一个整数,在把该整数转换成原类型的指针,还可以得到原先的指针值)。<br> <strong>在使用reinterpret_cast强制转换过程仅仅只是比特位的拷贝,因此在使用过程中需要特别谨慎!</strong></p><h3 id="改变指针或引用类型"><a href="#改变指针或引用类型" class="headerlink" title="改变指针或引用类型"></a>改变指针或引用类型</h3><figure class="highlight c++"><table><tr><td class="code"><pre><span class="line"><span class="type">int</span> *a = <span class="keyword">new</span> <span class="type">int</span>;</span><br><span class="line"><span class="type">double</span> *d = <span class="built_in">reinterpret_cast</span><<span class="type">double</span> *>(a);</span><br></pre></td></tr></table></figure><p>此处就是将一个整形指针转化为双精度浮点数指针</p><h3 id="将指针或引用转换成一个足够长度的整形"><a href="#将指针或引用转换成一个足够长度的整形" class="headerlink" title="将指针或引用转换成一个足够长度的整形"></a>将指针或引用转换成一个足够长度的整形</h3><h3 id="将整形转换为指针或引用类型"><a href="#将整形转换为指针或引用类型" class="headerlink" title="将整形转换为指针或引用类型"></a>将整形转换为指针或引用类型</h3><p>用户可以用reinterpret_cast做任何操作,但是要为自己的行为负责。</p><h2 id="dynamic-cast"><a href="#dynamic-cast" class="headerlink" title="dynamic_cast"></a>dynamic_cast</h2><p><strong>用法:dynamic_cast<type_id> (expression)</type_id></strong></p><p>dynamic_cast主要是用于解决向下转换不安全的问题。</p><p>对于“向下转型”有两种情况。</p><p>一种是基类指针所指对象是派生类类型的,这种转换是安全的;</p><p>另一种是基类指针所指对象为基类类型,在这种情况下dynamic_cast在运行时做检查,转换失败,返回结果为0;</p><figure class="highlight c++"><table><tr><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">"stdafx.h"</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span><span class="string"><iostream></span></span></span><br><span class="line"><span class="keyword">using</span> <span class="keyword">namespace</span> std;</span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">Base</span></span><br><span class="line">{</span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line"> <span class="built_in">Base</span>(){};</span><br><span class="line"> <span class="function"><span class="keyword">virtual</span> <span class="type">void</span> <span class="title">Show</span><span class="params">()</span></span>{cout<<<span class="string">"This is Base calss"</span>;}</span><br><span class="line">};</span><br><span class="line"><span class="keyword">class</span> <span class="title class_">Derived</span>:<span class="keyword">public</span> Base</span><br><span class="line">{</span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line"> <span class="built_in">Derived</span>(){};</span><br><span class="line"> <span class="function"><span class="type">void</span> <span class="title">Show</span><span class="params">()</span></span>{cout<<<span class="string">"This is Derived class"</span>;}</span><br><span class="line">};</span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">main</span><span class="params">()</span></span></span><br><span class="line"><span class="function"></span>{ </span><br><span class="line"> <span class="comment">//这是第一种情况</span></span><br><span class="line"> Base* base = <span class="keyword">new</span> Derived;</span><br><span class="line"> <span class="keyword">if</span>(Derived *der= <span class="built_in">dynamic_cast</span><Derived*>(base))</span><br><span class="line"> {</span><br><span class="line"> cout<<<span class="string">"第一种情况转换成功"</span><<endl;</span><br><span class="line"> der-><span class="built_in">Show</span>();</span><br><span class="line"> cout<<endl;</span><br><span class="line"> }</span><br><span class="line"> <span class="comment">//这是第二种情况</span></span><br><span class="line"> Base * base1 = <span class="keyword">new</span> Base;</span><br><span class="line"> <span class="keyword">if</span>(Derived *der1 = <span class="built_in">dynamic_cast</span><Derived*>(base1))</span><br><span class="line"> {</span><br><span class="line"> cout<<<span class="string">"第二种情况转换成功"</span><<endl;</span><br><span class="line"> der1-><span class="built_in">Show</span>();</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">else</span> </span><br><span class="line"> {</span><br><span class="line"> cout<<<span class="string">"第二种情况转换失败"</span><<endl;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="built_in">delete</span>(base);</span><br><span class="line"> <span class="built_in">delete</span>(base1);</span><br><span class="line"> <span class="built_in">system</span>(<span class="string">"pause"</span>);</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>与指针一样,引用的向下转型也可以分为两种情况,<strong>与指针不同的是,并不存在空引用,所以引用的dynamic_cast检测失败时会抛出一个bad_cast异常</strong></p><h1 id="问题-25-override-和-overload"><a href="#问题-25-override-和-overload" class="headerlink" title="问题(25) override 和 overload"></a>问题(25) override 和 overload</h1><h2 id="override"><a href="#override" class="headerlink" title="override"></a>override</h2><p>派⽣类中重新定义⽗类中除了函数体外完全相同的虚函数,注意被重写的函数不能是 static 的, ⼀定要是虚函数,且其他⼀定要完全相同。要注意,重写和被重写的函数是在不同的类当中的,重写函数的访问修饰符是可以不同的,尽管 virtual 中是 private 的,派⽣类中重写可以改为 public</p><h2 id="overload"><a href="#overload" class="headerlink" title="overload"></a>overload</h2><p>是指同⼀可访问区内被声明的⼏个具有不同参数列表的同名函数,依赖于 C++函数名字的修饰会将参数加在后⾯,可以是参数类型,个数,顺序的不同。根据参数列表决定调⽤哪个函数,重载不关⼼函数的返回类型。</p><p>重载看到是函数名,形参个数和形参类型</p><h1 id="问题-26-C-和-Java-区别(语⾔特性,垃圾回收,应⽤场景等)"><a href="#问题-26-C-和-Java-区别(语⾔特性,垃圾回收,应⽤场景等)" class="headerlink" title="问题(26) C++ 和 Java 区别(语⾔特性,垃圾回收,应⽤场景等)"></a>问题(26) C++ 和 Java 区别(语⾔特性,垃圾回收,应⽤场景等)</h1><h1 id="问题-27-说⼀下-C-⾥是怎么定义常量的?常量存放在内存的哪个位置?"><a href="#问题-27-说⼀下-C-⾥是怎么定义常量的?常量存放在内存的哪个位置?" class="headerlink" title="问题(27) 说⼀下 C++ ⾥是怎么定义常量的?常量存放在内存的哪个位置?"></a>问题(27) 说⼀下 C++ ⾥是怎么定义常量的?常量存放在内存的哪个位置?</h1><p>使用const关键字或者#define来定义常量</p><ul><li>对于局部常量,存放在栈区; </li><li>对于全局常量,编译期⼀般不分配内存,放在符号表中以提⾼访问效率; </li><li>字⾯值常量,⽐如字符串,放在常量区。</li></ul><h1 id="问题-28-介绍-C-所有的构造函数"><a href="#问题-28-介绍-C-所有的构造函数" class="headerlink" title="问题(28) 介绍 C++ 所有的构造函数"></a>问题(28) 介绍 C++ 所有的构造函数</h1><h2 id="按参数种类分"><a href="#按参数种类分" class="headerlink" title="按参数种类分"></a>按参数种类分</h2><h3 id="无参构造函数"><a href="#无参构造函数" class="headerlink" title="无参构造函数"></a>无参构造函数</h3><p>就是不带函数的构造函数</p><h3 id="有参构造函数"><a href="#有参构造函数" class="headerlink" title="有参构造函数"></a>有参构造函数</h3><p>就是带有参数的构造函数</p><h3 id="默认构造函数"><a href="#默认构造函数" class="headerlink" title="默认构造函数"></a>默认构造函数</h3><p>就是带有默认参数的构造函数,在实例化时若传入参数,则传入的参数值优先;若没有传入参数,则就使用指定的默认参数。</p><h2 id="按类型分"><a href="#按类型分" class="headerlink" title="按类型分"></a>按类型分</h2><h3 id="拷贝构造函数"><a href="#拷贝构造函数" class="headerlink" title="拷贝构造函数"></a>拷贝构造函数</h3><ul><li>一种特殊的构造函数,当对象之间复制时会自动调用拷贝构造函数。</li><li>若类中没有显示定义拷贝构造函数,则系统会自动生成默认拷贝构造函数。</li><li>使用场合:旧对象初始化新对象</li></ul><h3 id="自定义拷贝构造函数"><a href="#自定义拷贝构造函数" class="headerlink" title="自定义拷贝构造函数"></a>自定义拷贝构造函数</h3><figure class="highlight c++"><table><tr><td class="code"><pre><span class="line"><span class="comment">//假如已经定义了一个类Box,则通过以下方式定义拷贝构造函数:</span></span><br><span class="line"><span class="built_in">Box</span>(<span class="type">const</span> Box &p) </span><br><span class="line">{</span><br><span class="line">age= p.age;</span><br><span class="line">name= p.name;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">//调用拷贝构造函数</span></span><br><span class="line"><span class="function">Box <span class="title">b3</span><span class="params">(b2)</span></span>; <span class="comment">//传入参数就是一个对象b2</span></span><br><span class="line"></span><br></pre></td></tr></table></figure><p>在定义拷贝函数那个括号中:p是一个引用类型,括号内相当于Box p=b2,b2是已经实例化的一个对象,const加上&就是常量引用了。</p><p>因此,拷贝构造就是简单的拷贝值,因为它就是个常量引用,此处就是引用了对象b2。调用那句,就是说明通过Box类实例化一个对象b3,引用了对象b2的数据。</p><h3 id="匿名对象"><a href="#匿名对象" class="headerlink" title="匿名对象"></a>匿名对象</h3><p>简单来说,就是没有名字的对象,这个对象只能用一次,只在定义行起作用,一般情况是不会去用它的。</p><figure class="highlight c++"><table><tr><td class="code"><pre><span class="line"><span class="comment">//假如已经定义好了类Box,则可以有以下三种匿名对象的实例化:</span></span><br><span class="line"><span class="built_in">Box</span> (<span class="number">10</span>, <span class="string">"Chung"</span>); <span class="comment">//有参构造函数匿名对象</span></span><br><span class="line"><span class="built_in">Box</span> (); <span class="comment">//无参构造函数匿名对象</span></span><br><span class="line"></span><br><span class="line"><span class="comment">//有名对象调用(用以区别匿名对象)</span></span><br><span class="line"><span class="function">Box <span class="title">p</span><span class="params">(<span class="number">15</span>, <span class="string">"Hawk"</span>)</span></span>;</span><br><span class="line"></span><br></pre></td></tr></table></figure><h1 id="问题-29"><a href="#问题-29" class="headerlink" title="问题(29)"></a>问题(29)</h1><h1 id="问题-30-类的继承权限问题"><a href="#问题-30-类的继承权限问题" class="headerlink" title="问题(30) 类的继承权限问题"></a>问题(30) 类的继承权限问题</h1><h2 id="三种访问权限"><a href="#三种访问权限" class="headerlink" title="三种访问权限"></a>三种访问权限</h2><h3 id="public"><a href="#public" class="headerlink" title="public"></a>public</h3><p>该关键字修饰的成员表示是公有成员,表示该成员不仅可以在类内被访问,也可以在类外被访问,是类对外提供的可访问接口</p><h3 id="protected"><a href="#protected" class="headerlink" title="protected"></a>protected</h3><p>该关键字修饰的成员表示保护成员,该成员在类外是隐藏的,但是对于该类的派生类来说,相当于公有成员,在派生类中可以被访问。<strong>保护模式就是类外不能访问,但子类可以访问。</strong></p><h3 id="private"><a href="#private" class="headerlink" title="private"></a>private</h3><p>该关键字修饰的成员表示私有成员,在类内可以被访问,在类外是隐藏状态</p><h3 id="三种继承方式"><a href="#三种继承方式" class="headerlink" title="三种继承方式"></a>三种继承方式</h3><ul><li>若继承方式是public,基类成员在派生类中的访问权限保持不变,也就是说,基类中的成员访问权限,在派生类中仍然保持原来的访问权限;</li><li>若继承方式是private,基类所有成员在派生类中的访问权限都会变为私有(private)权限;</li><li>若继承方式是protected,基类的共有成员和保护成员在派生类中的访问权限都会变为保护(protected)权限,私有成员在派生类中的访问权限仍然是私有(private)权限。</li></ul><h1 id="问题-31-菱形继承问题"><a href="#问题-31-菱形继承问题" class="headerlink" title="问题(31) 菱形继承问题"></a>问题(31) 菱形继承问题</h1><h1 id="问题-32-C-中-lt-gt-和”-“引入头文件的区别"><a href="#问题-32-C-中-lt-gt-和”-“引入头文件的区别" class="headerlink" title="问题(32) C++中<>和” “引入头文件的区别"></a>问题(32) C++中<>和” “引入头文件的区别</h1><ul><li><> 表示从系统目录下开始查找,然后再搜索环境变量PATH中列出的目录,不搜索当前目录</li><li>“”表示从当前目录开始搜索,然后再是系统目录和环境变量PATH中列出的目录</li></ul><h1 id="问题-33-虚函数的作用:(virtual关键字修饰)"><a href="#问题-33-虚函数的作用:(virtual关键字修饰)" class="headerlink" title="问题(33) 虚函数的作用:(virtual关键字修饰)"></a>问题(33) 虚函数的作用:(virtual关键字修饰)</h1><p>虚函数的作用是为了实现多态而设置的</p><p>有例子如下:</p><figure class="highlight c++"><table><tr><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">include</span><span class="string"><iostream></span></span></span><br><span class="line"><span class="keyword">using</span> <span class="keyword">namespace</span> std;</span><br><span class="line"><span class="keyword">class</span> <span class="title class_">A</span></span><br><span class="line">{</span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line"> <span class="built_in">A</span>(){};</span><br><span class="line">~<span class="built_in">A</span>(){};</span><br><span class="line"><span class="function"><span class="keyword">virtual</span><span class="type">void</span> <span class="title">show</span><span class="params">()</span></span></span><br><span class="line"><span class="function"></span>{</span><br><span class="line">cout<<<span class="string">"Hello,I'm A."</span><<endl;</span><br><span class="line"> } </span><br><span class="line">};</span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">B</span> : <span class="keyword">public</span> A</span><br><span class="line">{<span class="keyword">public</span>:</span><br><span class="line"><span class="built_in">B</span>(){};</span><br><span class="line">~<span class="built_in">B</span>();</span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">show</span><span class="params">()</span></span></span><br><span class="line"><span class="function"></span>{</span><br><span class="line">cout<<<span class="string">"Hello,I'm B."</span><<endl;</span><br><span class="line">}</span><br><span class="line"> };</span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">main</span><span class="params">()</span></span></span><br><span class="line"><span class="function"></span>{</span><br><span class="line"> A* a = <span class="keyword">new</span> B;</span><br><span class="line"> a-><span class="built_in">show</span>();</span><br><span class="line"> <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line"> } </span><br></pre></td></tr></table></figure><p>如果不加virtual关键字,输出的是“Hello,I’m A”,加了的话则是”Hello, I’m B.”</p><p>虚函数意思是在基类中不实现该函数,而在派生类中来具体实现该函数,以此来实现多态。</p><p>纯虚函数定义 virtual void function()=0; 加一个=0即可。</p><p><strong>声明了纯虚函数的类是一个抽象类。所以,用户不能创建类的实例,只能创建它的派生类的实例。</strong></p><h1 id="问题-34-哪些函数不能是虚函数"><a href="#问题-34-哪些函数不能是虚函数" class="headerlink" title="问题(34) 哪些函数不能是虚函数"></a>问题(34) 哪些函数不能是虚函数</h1><ul><li>构造函数</li><li>内联成员函数</li><li>静态成员函数</li><li>普通函数(注意,不是普通成员函数)</li><li>友元函数</li></ul><p>总的来说,就是不能被继承的函数,不能被重写的函数,这两类是不能是虚函数的。</p><h1 id="问题-35-析构函数为什么要写成虚函数"><a href="#问题-35-析构函数为什么要写成虚函数" class="headerlink" title="问题(35) 析构函数为什么要写成虚函数"></a>问题(35) 析构函数为什么要写成虚函数</h1><p>我们知道,基类指针或者引用可以指向派生类对象。如果基类指针指向派生类对象,当我们需要删除该指针的时候,我们希望调用该派生类的析构函数,而派生类的析构函数又自动调用基类的析构函数,这样整个派生类的对象完全被释放。</p><p>但是,如果析构函数不被声明成虚函数,则编译器采用的绑定方式是静态绑定,在删除基类指针时,只会调用基类析构函数,而不调用派生类析构函数,这样就会导致基类指针指向的派生类对象析构不完全。</p><p>而析构函数是虚函数,则执行的是动态绑定,则能够解决该问题。</p><h1 id="问题-36-纯虚函数和抽象类"><a href="#问题-36-纯虚函数和抽象类" class="headerlink" title="问题(36) 纯虚函数和抽象类"></a>问题(36) 纯虚函数和抽象类</h1><p>在c++中,可以将虚函数声明为纯虚函数,声明如下</p><figure class="highlight c++"><table><tr><td class="code"><pre><span class="line"><span class="keyword">virtual</span> 返回值类型 函数名(函数参数) = <span class="number">0</span>;</span><br></pre></td></tr></table></figure><p>最后加上一个“=0”只是告诉编译器这是一个纯虚函数。</p><p>包含纯虚函数的类称为<strong>抽象类(Abstract Class)</strong>。之所以说它抽象,是因为它无法实例化,也就是无法创建对象。原因很明显,纯虚函数没有函数体,不是完整的函数,无法调用,也无法为其分配内存空间。</p><p>抽象类通常是作为基类,让派生类去实现纯虚函数。派生类必须实现纯虚函数才能被实例化。</p><p><strong>注意</strong></p><ol><li><p>一个纯虚函数就可以使类成为抽象基类,但是抽象基类中除了包含纯虚函数外,还可以包含其它的成员函数(虚函数或普通函数)和成员变量。</p></li><li><p>只有类中的虚函数才能被声明为纯虚函数,普通成员函数和顶层函数均不能声明为纯虚函数。如下例所示:</p><figure class="highlight c++"><table><tr><td class="code"><pre><span class="line"><span class="comment">//顶层函数不能被声明为纯虚函数</span></span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">fun</span><span class="params">()</span> </span>= <span class="number">0</span>; <span class="comment">//compile error</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">base</span>{</span><br><span class="line"><span class="keyword">public</span> :</span><br><span class="line"> <span class="comment">//普通成员函数不能被声明为纯虚函数</span></span><br><span class="line"> <span class="function"><span class="type">void</span> <span class="title">display</span><span class="params">()</span> </span>= <span class="number">0</span>; <span class="comment">//compile error</span></span><br><span class="line">};</span><br></pre></td></tr></table></figure></li></ol><h1 id="问题-37-浅拷贝和深拷贝"><a href="#问题-37-浅拷贝和深拷贝" class="headerlink" title="问题(37) 浅拷贝和深拷贝"></a>问题(37) 浅拷贝和深拷贝</h1><p><img src="/2023/03/09/c++%E5%85%AB%E8%82%A1%E6%96%87/v2-56b79a660af6fc485c49e4ff8d5de58d_720w.jpeg" alt="img"></p><p>如图所示,这就是浅拷贝与深拷贝的区别</p><h2 id="浅拷贝"><a href="#浅拷贝" class="headerlink" title="浅拷贝"></a>浅拷贝</h2><p>浅拷贝只会创造一个指向某个对象的指针,而不会复制对象本身。新旧对象还共享一块内存</p><ol><li>浅拷贝是按位拷贝对象,它会创建一个新对象,这个对象有着原始对象属性值的一份精确拷贝。</li><li>如果属性是基本类型,拷贝的就是基本类型的值;如果属性是内存地址(引用类型),拷贝的就是内存地址 ,因此如果其中一个对象改变了这个地址,就会影响到另一个对象。</li></ol><h2 id="深拷贝"><a href="#深拷贝" class="headerlink" title="深拷贝"></a>深拷贝</h2><p>深拷贝则是另外创造一个一模一样的新对象,新对象和原对象不共享内存,修改新对象不会改到原对象</p><ol><li>拷贝第一层级的对象属性或数组元素</li><li>递归拷贝所有层级的对象属性和数组元素</li><li>深拷贝会拷贝所有的属性,并拷贝属性指向的动态分配的内存。当对象和它所引用的对象一起拷贝时即发生深拷贝。深拷贝相比于浅拷贝速度较慢并且花销较大。</li></ol><h1 id="问题-38-调用拷贝构造函数的时机"><a href="#问题-38-调用拷贝构造函数的时机" class="headerlink" title="问题(38) 调用拷贝构造函数的时机"></a>问题(38) 调用拷贝构造函数的时机</h1><p>调用拷贝构造函数有三种情况</p><p>假设现在有这样的一个类</p><figure class="highlight c++"><table><tr><td class="code"><pre><span class="line"><span class="keyword">class</span> <span class="title class_">Complex</span> {</span><br><span class="line">pulic:</span><br><span class="line"><span class="type">double</span> real, image;</span><br><span class="line"><span class="built_in">Complex</span>(){}</span><br><span class="line"><span class="built_in">Complex</span>(<span class="type">const</span> Complex &c) {<span class="comment">//构造函数的参数一定是本类的引用</span></span><br><span class="line">real = c.real;</span><br><span class="line">imag = c.image;</span><br><span class="line">cout << <span class="string">"Copy Constructor called"</span>;</span><br><span class="line">}</span><br><span class="line">};</span><br><span class="line"></span><br></pre></td></tr></table></figure><h3 id="当用一个对象去初始化同类的另一个对象时"><a href="#当用一个对象去初始化同类的另一个对象时" class="headerlink" title="当用一个对象去初始化同类的另一个对象时"></a>当用一个对象去初始化同类的另一个对象时</h3><figure class="highlight c++"><table><tr><td class="code"><pre><span class="line">Complex c1;</span><br><span class="line"><span class="function">Complex <span class="title">c2</span><span class="params">(c1)</span></span>;</span><br><span class="line">Complex c2 = c1; <span class="comment">//初始化语句,非赋值函数</span></span><br></pre></td></tr></table></figure><h3 id="如果某函数有一个参数是类A的对象,那么该函数被调用时,类A的赋值构造函数将被调用"><a href="#如果某函数有一个参数是类A的对象,那么该函数被调用时,类A的赋值构造函数将被调用" class="headerlink" title="如果某函数有一个参数是类A的对象,那么该函数被调用时,类A的赋值构造函数将被调用"></a>如果某函数有一个参数是类A的对象,那么该函数被调用时,类A的赋值构造函数将被调用</h3><figure class="highlight c++"><table><tr><td class="code"><pre><span class="line"><span class="function"><span class="type">void</span> <span class="title">Func</span><span class="params">(Complex a1)</span></span>{}</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">main</span><span class="params">()</span> </span>{</span><br><span class="line">Complex a2;</span><br><span class="line"><span class="built_in">Func</span>(a2);</span><br><span class="line"><span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">}</span><br><span class="line"></span><br></pre></td></tr></table></figure><p>a1的值未必等于a2,具体看拷贝构造函数的内容(有没有赋值)。</p><h3 id="如果函数的返回值是类A的对象时,则函数返回时,A的拷贝构造函数将被调用"><a href="#如果函数的返回值是类A的对象时,则函数返回时,A的拷贝构造函数将被调用" class="headerlink" title="如果函数的返回值是类A的对象时,则函数返回时,A的拷贝构造函数将被调用"></a>如果函数的返回值是类A的对象时,则函数返回时,A的拷贝构造函数将被调用</h3><figure class="highlight c++"><table><tr><td class="code"><pre><span class="line"><span class="function">Complex <span class="title">Func</span><span class="params">()</span> </span>{</span><br><span class="line">Comeplex b;</span><br><span class="line">b.real = <span class="number">1</span>;</span><br><span class="line">b.image = <span class="number">1</span>;</span><br><span class="line"><span class="keyword">return</span> b;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">main</span><span class="params">()</span> </span>{</span><br><span class="line">cout << <span class="built_in">Func</span>().real << endl;</span><br><span class="line"><span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">}</span><br><span class="line"></span><br></pre></td></tr></table></figure><p>返回的值未必相等,取决于拷贝构造函数中是否进行了赋值操作。</p><p>总结起来就是三个地方,初始化另一个同类,传参,函数返回</p><h1 id="问题-39-拷贝构造函数为什么要是引用传递,而不能是值传递"><a href="#问题-39-拷贝构造函数为什么要是引用传递,而不能是值传递" class="headerlink" title="问题(39) 拷贝构造函数为什么要是引用传递,而不能是值传递"></a>问题(39) 拷贝构造函数为什么要是引用传递,而不能是值传递</h1><h1 id="问题-40-define和typedef的区别"><a href="#问题-40-define和typedef的区别" class="headerlink" title="问题(40) #define和typedef的区别"></a>问题(40) #define和typedef的区别</h1><ul><li><p>原理不同</p><p>#define是C语言中定义的用法,是预处理指令,在预处理时进行简单而机械的字符串替换,不做正确性检查,只有在编译已被展开的源程序才会发现可能的错误并报错</p><p>typedef是关键字,在编译时处理,有类型检查功能。它在自己的作用域内给一个已经存在的类型一个别名,但不能在一个函数定义里面使用typedef。用typedef定义数组、指针、结构等类型会带来很大的方便,不仅使程序书写简单,也使意义明确,增强可读性。</p></li><li><p>功能不同</p><p>typedef用来定义类型的别名,起到类型易于记忆的功能。另一个功能是定义机器无关的类型。如定义一个REAL的浮点类型,在目标机器上它可以获得最高的精度:typedef long double REAL, 在不支持long double的机器上,看起来是这样的,typedef double REAL,在不支持double的机器上,是这样的,typedef float REAL</p><p>#define不只是可以为类型取别名,还可以定义常量、变量、编译开关等。</p></li><li><p>作用域不同</p><p>#define没有作用域的限制,只要是之前预定义过的宏,在以后的程序中都可以使用,而typedef有自己的作用域。</p></li><li></li></ul><h1 id="问题-41-define和inline的区别"><a href="#问题-41-define和inline的区别" class="headerlink" title="问题(41) #define和inline的区别"></a>问题(41) #define和inline的区别</h1><h1 id="问题-42-explicit关键字"><a href="#问题-42-explicit关键字" class="headerlink" title="问题(42) explicit关键字"></a>问题(42) explicit关键字</h1><p>explicit,在英语中的意思很明显,明确的之意。在c++中可以阻止隐式转换的发生。</p><h1 id="问题-43-简单说⼀下函数指针"><a href="#问题-43-简单说⼀下函数指针" class="headerlink" title="问题(43) 简单说⼀下函数指针"></a>问题(43) 简单说⼀下函数指针</h1><h2 id="函数指针"><a href="#函数指针" class="headerlink" title="函数指针"></a>函数指针</h2><p>函数指针的形式:类型(*)( ),例如:int (*p)( ).它可以存放函数的地址,在平时的应用中也很常见。</p><h2 id="函数指针数组"><a href="#函数指针数组" class="headerlink" title="函数指针数组"></a>函数指针数组</h2><p>形式:例如int (<em>p[10])( );<br>因为p先和[ ]结合,说明p是数组,数组的内容是一个int (</em>)( )类型的指针<br>函数指针数组在转换表中应用广泛</p><h1 id="问题-44-volatile-和-extern-关键字"><a href="#问题-44-volatile-和-extern-关键字" class="headerlink" title="问题(44) volatile 和 extern 关键字"></a>问题(44) volatile 和 extern 关键字</h1><h1 id="问题-45-计算下⾯⼏个类的⼤⼩"><a href="#问题-45-计算下⾯⼏个类的⼤⼩" class="headerlink" title="问题(45) 计算下⾯⼏个类的⼤⼩"></a>问题(45) 计算下⾯⼏个类的⼤⼩</h1><figure class="highlight c++"><table><tr><td class="code"><pre><span class="line"><span class="keyword">class</span> <span class="title class_">A</span>{}; <span class="built_in">sizeof</span>(A) = <span class="number">1</span>; <span class="comment">//空类在实例化时得到⼀个独⼀⽆⼆的地址,所以为 1.</span></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">A</span>{<span class="function"><span class="keyword">virtual</span> <span class="title">Fun</span><span class="params">()</span></span>{} }; <span class="built_in">sizeof</span>(A) = <span class="number">4</span>(<span class="number">32b</span>it)/<span class="number">8</span>(<span class="number">64b</span>it) <span class="comment">//当 C++ 类中有虚函数的时候,会有⼀个指向虚函数表的指针(vptr)</span></span><br><span class="line"><span class="keyword">class</span> A{<span class="type">static</span> <span class="type">int</span> a; }; <span class="built_in">sizeof</span>(A) = <span class="number">1</span>;</span><br><span class="line"><span class="keyword">class</span> <span class="title class_">A</span>{<span class="type">int</span> a; }; <span class="built_in">sizeof</span>(A) = <span class="number">4</span>;</span><br><span class="line"><span class="keyword">class</span> <span class="title class_">A</span>{<span class="type">static</span> <span class="type">int</span> a; <span class="type">int</span> b; }; <span class="built_in">sizeof</span>(A) = <span class="number">4</span>;</span><br><span class="line"></span><br></pre></td></tr></table></figure><h1 id="问题-46-多态的实现"><a href="#问题-46-多态的实现" class="headerlink" title="问题(46) 多态的实现"></a>问题(46) 多态的实现</h1><h1 id="问题-47-静态绑定和动态绑定的介绍"><a href="#问题-47-静态绑定和动态绑定的介绍" class="headerlink" title="问题(47) 静态绑定和动态绑定的介绍"></a>问题(47) 静态绑定和动态绑定的介绍</h1><p><strong>静态绑定:在编译时刻,根据指针或引用变量的静态类型来决定成员函数属于哪一个类。</strong></p><p><strong>动态绑定:在运行时刻,根据指针或引用变量实际指向或引用的对象类型(动态类型)来确定成员函数属于哪一个类。</strong></p><h1 id="问题-49-结构体内存对齐方式和为什么要进⾏内存对⻬?"><a href="#问题-49-结构体内存对齐方式和为什么要进⾏内存对⻬?" class="headerlink" title="问题(49) 结构体内存对齐方式和为什么要进⾏内存对⻬?"></a>问题(49) 结构体内存对齐方式和为什么要进⾏内存对⻬?</h1><h1 id="问题-50-内存泄漏的定义,如何检测与避免?"><a href="#问题-50-内存泄漏的定义,如何检测与避免?" class="headerlink" title="问题(50) 内存泄漏的定义,如何检测与避免?"></a>问题(50) 内存泄漏的定义,如何检测与避免?</h1><p><strong>定义</strong>:就是申请一块空间,使用完毕后,没有释放掉,其表现方式是程序运行的时间越长,占的内存越多,最终用尽全部内存,使得整个程序崩溃掉。实际上就是,申请一块内存后,没有任何一个指针去指向这块内存,那么就说这个块内存已经泄漏。</p><h1 id="问题-51-说⼀下平衡⼆叉树、⾼度平衡⼆叉树(AVL)"><a href="#问题-51-说⼀下平衡⼆叉树、⾼度平衡⼆叉树(AVL)" class="headerlink" title="问题(51) 说⼀下平衡⼆叉树、⾼度平衡⼆叉树(AVL)"></a>问题(51) 说⼀下平衡⼆叉树、⾼度平衡⼆叉树(AVL)</h1><h1 id="问题-52-说⼀下红黑树(RB-tree)"><a href="#问题-52-说⼀下红黑树(RB-tree)" class="headerlink" title="问题(52) 说⼀下红黑树(RB-tree)"></a>问题(52) 说⼀下红黑树(RB-tree)</h1><h1 id="问题-53-说⼀下-fork,wait,exec-函数"><a href="#问题-53-说⼀下-fork,wait,exec-函数" class="headerlink" title="问题(53) 说⼀下 fork,wait,exec 函数"></a>问题(53) 说⼀下 fork,wait,exec 函数</h1><h2 id="fork"><a href="#fork" class="headerlink" title="fork"></a>fork</h2><ul><li>函数 pid_t fork(void) //pid_t 为int类型</li><li>pid_t getpid() //获取当前pid</li><li>pid_t getppid(); //获取当前进程的父进程 pid 值。</li></ul><p>fork用于创建一个进程,所创建的进程复制父进程的<strong>代码段/数据段/BSS段/堆/栈等所有用户空间信息</strong>;在内核中操作系统重新为其申请了一个PCB,并使用父进程的PCB进行初始化。</p><h2 id="wait"><a href="#wait" class="headerlink" title="wait"></a>wait</h2><h2 id="exec"><a href="#exec" class="headerlink" title="exec"></a>exec</h2><h1 id="问题-54-构造函数的执行算法?"><a href="#问题-54-构造函数的执行算法?" class="headerlink" title="问题(54) 构造函数的执行算法?"></a>问题(54) 构造函数的执行算法?</h1><h1 id="问题-55-⼿写实现智能指针类"><a href="#问题-55-⼿写实现智能指针类" class="headerlink" title="问题(55) ⼿写实现智能指针类"></a>问题(55) ⼿写实现智能指针类</h1><figure class="highlight c++"><table><tr><td class="code"><pre><span class="line"><span class="keyword">template</span><<span class="keyword">typename</span>> T</span><br><span class="line"><span class="keyword">class</span> <span class="title class_">shared_ptr</span></span><br><span class="line">{</span><br><span class="line"> <span class="keyword">private</span>:</span><br><span class="line"> <span class="type">size_t</span> * m_count; <span class="comment">//size_t 是任意类型数据的最大长度,是为了可移植性而生的</span></span><br><span class="line"> T* m_ptr; <span class="comment">// 具体指针</span></span><br><span class="line"> </span><br><span class="line"> <span class="keyword">public</span>:</span><br><span class="line"> <span class="built_in">shared_ptr</span>(): <span class="built_in">m_ptr</span>(<span class="literal">nullptr</span>),<span class="built_in">m_count</span>(<span class="keyword">new</span> <span class="type">size_t</span>){}</span><br><span class="line"> <span class="built_in">shared_ptr</span>(T* ptr):<span class="built_in">m_ptr</span>(ptr),<span class="built_in">m_count</span>(<span class="keyword">new</span> <span class="type">size_t</span>){m_count = <span class="number">1</span>;} </span><br><span class="line"> ~<span class="built_in">shared_ptr</span>()</span><br><span class="line"> {</span><br><span class="line"> --(*m_count);</span><br><span class="line"> <span class="keyword">if</span>(*m_count == <span class="number">0</span>)</span><br><span class="line"> { <span class="keyword">delete</span> m_ptr;</span><br><span class="line"> <span class="keyword">delete</span> m_count;</span><br><span class="line"> m_ptr = <span class="literal">nullptr</span>; <span class="comment">// 置空操作,防止野指针</span></span><br><span class="line"> m_count = <span class="literal">nullptr</span>; <span class="comment">//同上</span></span><br><span class="line">}</span><br><span class="line"> }</span><br><span class="line"> <span class="built_in">shared_ptr</span>(<span class="type">const</span> shared_ptr& ptr) <span class="comment">// </span></span><br><span class="line"> {</span><br><span class="line"> m_count = ptr.m_count;</span><br><span class="line"> m_ptr = ptr.m_ptr;</span><br><span class="line"> ++(*m_count);</span><br><span class="line"> }</span><br><span class="line"> <span class="comment">//拷⻉赋值运算</span></span><br><span class="line"> </span><br><span class="line"><span class="type">void</span> <span class="keyword">operator</span>=(<span class="type">const</span> shared_ptr& ptr) { <span class="built_in">shared_ptr</span>(std::<span class="built_in">move</span>(ptr)); }</span><br><span class="line"></span><br><span class="line"><span class="comment">//移动构造函数</span></span><br><span class="line"><span class="built_in">shared_ptr</span>(shared_ptr &&ptr):<span class="built_in">m_ptr</span>(ptr.m_ptr),<span class="built_in">m_count</span>(ptr.m_count){</span><br><span class="line"> ++(*m_count);</span><br><span class="line">}</span><br><span class="line"><span class="comment">//移动赋值运算</span></span><br><span class="line"><span class="type">void</span> <span class="keyword">operator</span>=(shared_ptr&& ptr)</span><br><span class="line">{</span><br><span class="line"> <span class="built_in">shared_ptr</span>(std::<span class="built_in">move</span>(ptr));</span><br><span class="line">}</span><br><span class="line"> </span><br><span class="line"><span class="comment">//解引用</span></span><br><span class="line"> T& <span class="keyword">operator</span>*(){ <span class="keyword">return</span> *m_ptr;}</span><br><span class="line"><span class="comment">// 箭头运算</span></span><br><span class="line"> T& <span class="keyword">operator</span>->() {<span class="keyword">return</span> ,m_ptr;}</span><br><span class="line"><span class="comment">//重载bool运算符</span></span><br><span class="line"> <span class="function"><span class="keyword">operator</span> <span class="title">bool</span><span class="params">()</span> </span>{<span class="keyword">return</span> m_ptr = <span class="literal">nullptr</span>;}</span><br><span class="line"><span class="function">T* <span class="title">get</span><span class="params">()</span></span>{<span class="keyword">return</span> m_ptr;}</span><br><span class="line"></span><br><span class="line"><span class="type">size_t</span> use_count{<span class="keyword">return</span> m_count;}</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">bool</span> <span class="title">unique</span><span class="params">()</span></span>{<span class="keyword">return</span> *m_count == <span class="number">1</span>;}</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">swap</span><span class="params">(shared_ptr& ptr)</span></span>{std::<span class="built_in">swap</span>(*<span class="keyword">this</span>,ptr);}</span><br><span class="line">};</span><br></pre></td></tr></table></figure><h1 id="问题-56-C-11特性"><a href="#问题-56-C-11特性" class="headerlink" title="问题(56) C++11特性"></a>问题(56) C++11特性</h1><ul><li>nullptr替代 NULL</li><li>引入了 auto 和 decltype 这两个关键字实现了类型推导</li><li>基于范围的 for 循环for(auto& i : res){}</li><li>类和结构体的中初始化列表</li><li>Lambda 表达式(匿名函数)</li><li>std::forward_list(单向链表)</li><li>右值引用和move语义</li></ul><h1 id="问题-57-c-的值类型和引用类型"><a href="#问题-57-c-的值类型和引用类型" class="headerlink" title="问题(57) c#的值类型和引用类型"></a>问题(57) c#的值类型和引用类型</h1><p>1、值类型和引用类型的主要区别在于: </p><p><strong>1)范围方面</strong><br> C#的值类型包括:结构体(数值类型、bool型、用户定义的结构体),枚举,可空类型。<br>C#的引用类型包括:数组,用户定义的类、接口、委托,object,字符串。</p><p> <strong>2)内存分配方面:</strong> </p><p> 数组的元素不管是引用类型还是值类型,都存储在托管堆上。</p><p> 引用类型在栈中存储一个引用,其实际的存储位置位于托管堆。简称引用类型部署在托管推上。而值类型总是分配在它声明的地方:作为字段时,跟随其所属的变量(实 例)存储;作为局部变量时,存储在栈上。(栈的内存是自动释放的,堆内存是.NET中会由GC来自动释放) </p><p> <strong>3)适用场合</strong> </p><p> 值类型在内存管理方面具有更好的效率,并且不支持多态,适合用做存储数据的载体;引用类型支持多态,适合用于定义应用程序的行为。 </p><ul><li> 引用类型可以派生出新的类型,而值类型不能,因为所有的值类型都是密封(seal)的; </li><li> 引用类型可以包含null值,值类型不能(可空类型功能允许将 null 赋给值类型,如 int? a = null; ); </li><li> 引用类型变量的赋值只复制对对象的引用,而不复制对象本身。而将一个值类型变量赋给另一个值类型变量时,将复制包含的值。</li></ul><h1 id="问题-58-进程的通信方式"><a href="#问题-58-进程的通信方式" class="headerlink" title="问题(58) 进程的通信方式"></a>问题(58) 进程的通信方式</h1><p>链接:<a href="https://www.nowcoder.com/questionTerminal/088f8c870a904fe5ae05e1f7d966087b">https://www.nowcoder.com/questionTerminal/088f8c870a904fe5ae05e1f7d966087b</a><br>来源:牛客网</p><p>进程通信方式: </p><p> (1)(无名)管道(Pipe):管道可用于具有亲缘关系进程间的通信,允许一个进程和另一个与它有共同祖先的进程之间进行通信。<br> (2)命名管道(named pipe):命名管道克服了管道没有名字的限制,因此,除具有管道所具有的功能外,它还允许无亲缘关系进程间的通信。命名管道在文件系统中有对应的文件名。命名管道通过命令mkfifo或系统调用mkfifo来创建。<br> (3)信号(Signal):信号是比较复杂的通信方式,用于通知接受进程有某种事件发生,除了用于进程间通信外,进程还可以发送信号给进程本身;linux除了支持Unix早期信号语义函数sigal外,还支持语义符合Posix.1标准的信号函数sigaction(实际上,该函数是基于BSD的,BSD为了实现可靠信号机制,又能够统一对外接口,用sigaction函数重新实现了signal函数)。<br> (4)消息(Message)队列:消息队列是消息的链接表,包括Posix消息队列system V消息队列。有足够权限的进程可以向队列中添加消息,被赋予读权限的进程则可以读走队列中的消息。消息队列克服了信号承载信息量少,管道只能承载无格式字节流以及缓冲区大小受限等缺<br> (5)共享内存:使得多个进程可以访问同一块内存空间,是最快的可用IPC形式。是针对其他通信机制运行效率较低而设计的。往往与其它通信机制,如信号量结合使用,来达到进程间的同步及互斥。<br> (6)内存映射(mapped memory):内存映射允许任何多个进程间通信,每一个使用该机制的进程通过把一个共享的文件映射到自己的进程地址空间来实现它。<br> (7)信号量(semaphore):主要作为进程间以及同一进程不同线程之间的同步手段。<br> (8)套接口(Socket):更为一般的进程间通信机制,可用于不同机器之间的进程间通信。起初是由Unix系统的BSD分支开发出来的,但现在一般可以移植到其它类Unix系统上:Linux和System V的变种都支持套接字。<br> (9)文件锁</p><h1 id="问题-59"><a href="#问题-59" class="headerlink" title="问题(59)"></a>问题(59)</h1>]]></content>
<summary type="html"><h1 id="问题-1-c-中内存的分布情况"><a href="#问题-1-c-中内存的分布情况" class="headerlink" title="问题(1) c++中内存的分布情况"></a>问题(1) c++中内存的分布情况</h1><p>答: c++内存分为五个部分</summary>
<category term="工作" scheme="https://frankho-hwc.github.io/tags/%E5%B7%A5%E4%BD%9C/"/>
<category term="C++" scheme="https://frankho-hwc.github.io/tags/C/"/>
</entry>
<entry>
<title>GAN讲解</title>
<link href="https://frankho-hwc.github.io/2023/03/09/GAN%E8%AE%B2%E8%A7%A3/"/>
<id>https://frankho-hwc.github.io/2023/03/09/GAN%E8%AE%B2%E8%A7%A3/</id>
<published>2023-03-09T08:49:11.000Z</published>
<updated>2023-03-12T09:44:05.355Z</updated>
<content type="html"><![CDATA[<h1 id="概述"><a href="#概述" class="headerlink" title="概述"></a>概述</h1><p>GAN包含有两个模型,一个是生成模型(generative model),一个是判别模型(discriminative model)。生成模型的任务是生成看起来自然真实的、和原始数据相似的实例。判别模型的任务是判断给定的实例看起来是自然真实的还是人为伪造的(真实实例来源于数据集,伪造实例来源于生成模型)。</p>]]></content>
<summary type="html"><h1 id="概述"><a href="#概述" class="headerlink" title="概述"></a>概述</h1><p>GAN包含有两个模型,一个是生成模型(generative model),一个是判别模型(discriminative model)。生成模</summary>
<category term="计算机视觉" scheme="https://frankho-hwc.github.io/tags/%E8%AE%A1%E7%AE%97%E6%9C%BA%E8%A7%86%E8%A7%89/"/>
<category term="深度学习" scheme="https://frankho-hwc.github.io/tags/%E6%B7%B1%E5%BA%A6%E5%AD%A6%E4%B9%A0/"/>
</entry>
<entry>
<title>resnet讲解</title>
<link href="https://frankho-hwc.github.io/2023/03/09/resnet%E8%AE%B2%E8%A7%A3/"/>
<id>https://frankho-hwc.github.io/2023/03/09/resnet%E8%AE%B2%E8%A7%A3/</id>
<published>2023-03-09T08:47:51.000Z</published>
<updated>2023-03-12T02:40:35.000Z</updated>
<content type="html"><![CDATA[<h1 id="论文链接"><a href="#论文链接" class="headerlink" title="论文链接"></a>论文链接</h1><p>论文地址 [<a href="https://arxiv.org/abs/1512.03385">1512.03385] Deep Residual Learning for Image Recognition (arxiv.org)</a></p><h1 id="引言"><a href="#引言" class="headerlink" title="引言"></a>引言</h1><h2 id="深度神经网络退化问题"><a href="#深度神经网络退化问题" class="headerlink" title="深度神经网络退化问题"></a>深度神经网络退化问题</h2><p>从经验来看,网络的深度对模型的性能至关重要,当增加网络层数后,网络可以进行更加复杂的特征模式的提取,所以当模型更深时理论上可以取得更好的结果,从下图也可以看出网络深度越深拟合程度就越好。但是随着深度越深,网络的性能就一定会更好吗?</p><p><img src="/2023/03/09/resnet%E8%AE%B2%E8%A7%A3/v2-606573bdaaa97de6b8b10fb00f76d29a_720w.png" alt="img"></p><p>实验发现,随着网络深度增加时,网络准确度出现饱和,甚至出现下降。这就是退化问题(degradation problem)。由下图可知,56层的网络比20层网络效果还要差。这不会是过拟合问题,因为56层网络的训练误差同样高。虽然我们知道深度神经网络存在梯度消失或者梯度爆炸的问题,但是存在的一些技术并没有解决这些问题。</p><p><img src="/2023/03/09/resnet%E8%AE%B2%E8%A7%A3/v2-dcf5688dad675cbe8fb8be243af5e1fd_720w.png" alt="20层与56层网络在CIFAR-10上的误差"></p><h1 id="残差学习"><a href="#残差学习" class="headerlink" title="残差学习"></a>残差学习</h1><p><strong>残差</strong>即观测值与估计值之间的差</p><p>假设我们要建立深层网络,当我们不断堆积新的层,但增加的层什么也不学习(极端情况),那么我们就仅仅复制浅层网络的特征,即新层是浅层的恒等映射(Identity mapping),这样深层网络的性能应该至少和浅层网络一样,那么退化问题就得到了解决。</p><p>对于一个堆积层结构(几层堆积而成)当输入为$x$ 时其学习到的特征记为 $H(x)$ ,现在我们希望其可以学习到残差 $F(x) = H(x) - x$ ,这样其实原始的学习特征是$F(x)+x $ 。之所以这样是因为残差学习相比原始特征直接学习更容易。当残差为0时,此时堆积层仅仅做了恒等映射,至少网络性能不会下降,实际上残差不会为0,这也会使得堆积层在输入特征基础上学习到新的特征,从而拥有更好的性能。残差学习的结构如图4所示。这有点类似与电路中的“短路”,所以是一种短路连接(shortcut connection)。如图所示。</p><p><img src="/2023/03/09/resnet%E8%AE%B2%E8%A7%A3/v2-252e6d9979a2a91c2d3033b9b73eb69f_720w.png" alt="img"></p><p>从直观上看,残差学习需要学习的内容较少,从数学角度来说,如下所示</p><p>首先残差单元可以表示为<br>$$<br>y_l = h(x_i) + F(x_i,W_l) \<br>x_{l + 1} = f(y_l)<br>$$<br>其中$x_l$和$x_{l+1}$ 分别表示的是第$l$个残差单元的输入和输出,注意每个残差单元一般包含多层结构。$F$是残差函数,表示学习到的残差,而 $h(x_l) = x_l$表示恒等映射, $f$是ReLU激活函数。基于上式,我们求得从浅层$l$到深层 $L$的学习特征为<br>$$<br>x_{L} = X_l + \sum_{i = l}^{L-1}F(x_i,W_i)<br>$$<br>利用链式规则,可以求得反向过程的梯度<br>$$<br>\frac{\delta loss}{\delta x_l} = \frac{\delta loss}{\delta x_L} \cdot \frac{\delta x_L}{\delta x_l} = \frac{\delta loss}{\delta x_L} \cdot (1 + \frac{\delta}{\delta x_l}\sum_{i = l}^{L-1}F(x_i,W_i))<br>$$<br>式子的第一个因子$\frac{\delta loss}{\delta x_l}$ 表示的损失函数到达$L$ 的梯度,小括号中的1表明短路机制可以无损地传播梯度,而另外一项残差梯度则需要经过带有weights的层,梯度不是直接传递过来的。残差梯度不会那么巧全为-1,而且就算其比较小,有1的存在也不会导致梯度消失。所以残差学习会更容易。要注意上面的推导并不是严格的证明。</p><h1 id="ResNet网络结构"><a href="#ResNet网络结构" class="headerlink" title="ResNet网络结构"></a>ResNet网络结构</h1><p>ResNet网络是参考了VGG19网络,在其基础上进行了修改,并通过短路机制加入了残差单元。如下图所示,变化主要体现在ResNet直接使用stride=2的卷积做下采样,并且用global average pool层替换了全连接层。ResNet的一个重要设计原则是:当feature map大小降低一半时,feature map的数量增加一倍,这保持了网络层的复杂度。从下图中可以看到,ResNet相比普通网络每两层间增加了短路机制,这就形成了残差学习,其中虚线表示feature map数量发生了改变。下图展示的34-layer的ResNet,还可以构建更深的网络如表1所示。从表中可以看到,对于18-layer和34-layer的ResNet,其进行的两层间的残差学习,当网络更深时,其进行的是三层间的残差学习,三层卷积核分别是1x1,3x3和1x1,一个值得注意的是隐含层的feature map数量是比较小的,并且是输出feature map数量的1/4。</p><p><img src="/2023/03/09/resnet%E8%AE%B2%E8%A7%A3/v2-7cb9c03871ab1faa7ca23199ac403bd9_720w.webp" alt="resnet structure"></p><p><img src="/2023/03/09/resnet%E8%AE%B2%E8%A7%A3/v2-1dfd4022d4be28392ff44c49d6b4ed94_720w.webp" alt="不同的resnet结构"></p><p>再来说一下resnet的block结构,ResNet block有两种,一种两层结构,一种是三层的bottleneck结构,即将两个3x3的卷积层替换为1x1 + 3x3 + 1x1,它通过1x1 conv来巧妙地缩减或扩张feature map维度,从而使得我们的3x3 conv的filters数目不受上一层输入的影响,它的输出也不会影响到下一层。中间3x3的卷积层首先在一个降维1x1卷积层下减少了计算,然后在另一个1x1的卷积层下做了还原。既保持了模型精度又减少了网络参数和计算量,节省了计算时间。</p><p><img src="/2023/03/09/resnet%E8%AE%B2%E8%A7%A3/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzM3NTQxMDk3,size_16,color_FFFFFF,t_70.png" alt="在这里插入图片描述"></p><p>注意:<br>对于短路连接,如果残差映射F(x)的维度与跳跃连接x的维度不同,那咱们是没有办法对它们两个进行相加操作的,必须对x进行升维操作,让他俩的维度相同时才能计算:</p><p>zero-padding全0填充增加维度:<br>此时一般要先做一个downsamp,可以采用stride=2的pooling,这样不会增加参数<br>采用新的映射(projection shortcut):<br>一般采用1x1的卷积,这样会增加参数,也会增加计算量。</p><h2 id="改进版的ResNet"><a href="#改进版的ResNet" class="headerlink" title="改进版的ResNet"></a>改进版的ResNet</h2><p>改进前后一个明显的变化是采用pre-activation,BN和ReLU都提前了。而且作者推荐短路连接采用恒等变换,这样保证短路连接不会有阻碍。</p><p><img src="/2023/03/09/resnet%E8%AE%B2%E8%A7%A3/cc9ccbd34fe3cf9b201fe520523a71f3.jpeg" alt="在这里插入图片描述"></p><h1 id="对比结果"><a href="#对比结果" class="headerlink" title="对比结果"></a>对比结果</h1><p>对比18-layer和34-layer的网络效果,如图所示。可以看到普通的网络出现退化现象,但是ResNet很好的解决了退化问题。</p><p><img src="/2023/03/09/resnet%E8%AE%B2%E8%A7%A3/v2-ac88d9e118e3a85922188daba84f7efd_720w.webp" alt="img"></p><p>最后展示一下ResNet网络与其他网络在ImageNet上的对比结果,如表所示。可以看到ResNet-152其误差降到了4.49%,当采用集成模型后,误差可以降到3.57%。</p><p><img src="/2023/03/09/resnet%E8%AE%B2%E8%A7%A3/v2-0a2c8a209a221817f91c1f1728327beb_720w.webp" alt="img"></p><h1 id="Resnet的pytorch实现"><a href="#Resnet的pytorch实现" class="headerlink" title="Resnet的pytorch实现"></a>Resnet的pytorch实现</h1><p>此处实现的是resnet50结构</p><figure class="highlight python"><table><tr><td class="code"><pre><span class="line"><span class="keyword">import</span> torch</span><br><span class="line"><span class="keyword">from</span> torch <span class="keyword">import</span> nn</span><br><span class="line"><span class="keyword">class</span> <span class="title class_">Bottleneck</span>(nn.Module):</span><br><span class="line"> <span class="comment">#每个stage维度中扩展的倍数</span></span><br><span class="line"> extention=<span class="number">4</span></span><br><span class="line"> <span class="keyword">def</span> <span class="title function_">__init__</span>(<span class="params">self,inplanes,planes,stride,downsample=<span class="literal">None</span></span>):</span><br><span class="line"> <span class="string">'''</span></span><br><span class="line"><span class="string"></span></span><br><span class="line"><span class="string"> :param inplanes: 输入block的之前的通道数</span></span><br><span class="line"><span class="string"> :param planes: 在block中间处理的时候的通道数</span></span><br><span class="line"><span class="string"> planes*self.extention:输出的维度</span></span><br><span class="line"><span class="string"> :param stride:</span></span><br><span class="line"><span class="string"> :param downsample:</span></span><br><span class="line"><span class="string"> '''</span></span><br><span class="line"> <span class="built_in">super</span>(Bottleneck, self).__init__()</span><br><span class="line"></span><br><span class="line"> self.conv1=nn.Conv2d(inplanes,planes,kernel_size=<span class="number">1</span>,stride=stride,bias=<span class="literal">False</span>)</span><br><span class="line"> self.bn1=nn.BatchNorm2d(planes)</span><br><span class="line"></span><br><span class="line"> self.conv2=nn.Conv2d(planes,planes,kernel_size=<span class="number">3</span>,stride=<span class="number">1</span>,padding=<span class="number">1</span>,bias=<span class="literal">False</span>)</span><br><span class="line"> self.bn2=nn.BatchNorm2d(planes)</span><br><span class="line"></span><br><span class="line"> self.conv3=nn.Conv2d(planes,planes*self.extention,kernel_size=<span class="number">1</span>,stride=<span class="number">1</span>,bias=<span class="literal">False</span>)</span><br><span class="line"> self.bn3=nn.BatchNorm2d(planes*self.extention)</span><br><span class="line"></span><br><span class="line"> self.relu=nn.ReLU(inplace=<span class="literal">True</span>)</span><br><span class="line"></span><br><span class="line"> <span class="comment">#判断残差有没有卷积</span></span><br><span class="line"> self.downsample=downsample</span><br><span class="line"> self.stride=stride</span><br><span class="line"></span><br><span class="line"> <span class="keyword">def</span> <span class="title function_">forward</span>(<span class="params">self,x</span>):</span><br><span class="line"> <span class="comment">#参差数据</span></span><br><span class="line"> residual=x</span><br><span class="line"></span><br><span class="line"> <span class="comment">#卷积操作</span></span><br><span class="line"> out=self.conv1(x)</span><br><span class="line"> out=self.bn1(out)</span><br><span class="line"> out=self.relu(out)</span><br><span class="line"></span><br><span class="line"> out=self.conv2(out)</span><br><span class="line"> out=self.bn2(out)</span><br><span class="line"> out=self.relu(out)</span><br><span class="line"></span><br><span class="line"> out=self.conv3(out)</span><br><span class="line"> out=self.bn3(out)</span><br><span class="line"> out=self.relu(out)</span><br><span class="line"></span><br><span class="line"> <span class="comment">#是否直连(如果Indentity blobk就是直连;如果Conv2 Block就需要对残差边就行卷积,改变通道数和size</span></span><br><span class="line"> <span class="keyword">if</span> self.downsample <span class="keyword">is</span> <span class="keyword">not</span> <span class="literal">None</span>:</span><br><span class="line"> residual=self.downsample(x)</span><br><span class="line"></span><br><span class="line"> <span class="comment">#将残差部分和卷积部分相加</span></span><br><span class="line"> out+=residual</span><br><span class="line"> out=self.relu(out)</span><br><span class="line"></span><br><span class="line"> <span class="keyword">return</span> out</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">ResNet</span>(nn.Module):</span><br><span class="line"> <span class="keyword">def</span> <span class="title function_">__init__</span>(<span class="params">self,block,layers,num_class</span>):</span><br><span class="line"> <span class="comment">#inplane=当前的fm的通道数</span></span><br><span class="line"> self.inplane=<span class="number">64</span></span><br><span class="line"> <span class="built_in">super</span>(ResNet, self).__init__()</span><br><span class="line"></span><br><span class="line"> <span class="comment">#参数</span></span><br><span class="line"> self.block=block</span><br><span class="line"> self.layers=layers</span><br><span class="line"></span><br><span class="line"> <span class="comment">#stem的网络层</span></span><br><span class="line"> self.conv1=nn.Conv2d(<span class="number">3</span>,self.inplane,kernel_size=<span class="number">7</span>,stride=<span class="number">2</span>,padding=<span class="number">3</span>,bias=<span class="literal">False</span>)</span><br><span class="line"> self.bn1=nn.BatchNorm2d(self.inplane)</span><br><span class="line"> self.relu=nn.ReLU()</span><br><span class="line"> self.maxpool=nn.MaxPool2d(kernel_size=<span class="number">3</span>,stride=<span class="number">2</span>,padding=<span class="number">1</span>)</span><br><span class="line"></span><br><span class="line"> <span class="comment">#64,128,256,512指的是扩大4倍之前的维度,即Identity Block中间的维度</span></span><br><span class="line"> self.stage1=self.make_layer(self.block,<span class="number">64</span>,layers[<span class="number">0</span>],stride=<span class="number">1</span>)</span><br><span class="line"> self.stage2=self.make_layer(self.block,<span class="number">128</span>,layers[<span class="number">1</span>],stride=<span class="number">2</span>)</span><br><span class="line"> self.stage3=self.make_layer(self.block,<span class="number">256</span>,layers[<span class="number">2</span>],stride=<span class="number">2</span>)</span><br><span class="line"> self.stage4=self.make_layer(self.block,<span class="number">512</span>,layers[<span class="number">3</span>],stride=<span class="number">2</span>)</span><br><span class="line"></span><br><span class="line"> <span class="comment">#后续的网络</span></span><br><span class="line"> self.avgpool=nn.AvgPool2d(<span class="number">7</span>)</span><br><span class="line"> self.fc=nn.Linear(<span class="number">512</span>*block.extention,num_class)</span><br><span class="line"></span><br><span class="line"> <span class="keyword">def</span> <span class="title function_">forward</span>(<span class="params">self,x</span>):</span><br><span class="line"> <span class="comment">#stem部分:conv+bn+maxpool</span></span><br><span class="line"> out=self.conv1(x)</span><br><span class="line"> out=self.bn1(out)</span><br><span class="line"> out=self.relu(out)</span><br><span class="line"> out=self.maxpool(out)</span><br><span class="line"></span><br><span class="line"> <span class="comment">#block部分</span></span><br><span class="line"> out=self.stage1(out)</span><br><span class="line"> out=self.stage2(out)</span><br><span class="line"> out=self.stage3(out)</span><br><span class="line"> out=self.stage4(out)</span><br><span class="line"></span><br><span class="line"> <span class="comment">#分类</span></span><br><span class="line"> out=self.avgpool(out)</span><br><span class="line"> out=torch.flatten(out,<span class="number">1</span>)</span><br><span class="line"> out=self.fc(out)</span><br><span class="line"></span><br><span class="line"> <span class="keyword">return</span> out</span><br><span class="line"></span><br><span class="line"> <span class="keyword">def</span> <span class="title function_">make_layer</span>(<span class="params">self,block,plane,block_num,stride=<span class="number">1</span></span>):</span><br><span class="line"> <span class="string">'''</span></span><br><span class="line"><span class="string"> :param block: block模板</span></span><br><span class="line"><span class="string"> :param plane: 每个模块中间运算的维度,一般等于输出维度/4</span></span><br><span class="line"><span class="string"> :param block_num: 重复次数</span></span><br><span class="line"><span class="string"> :param stride: 步长</span></span><br><span class="line"><span class="string"> :return:</span></span><br><span class="line"><span class="string"> '''</span></span><br><span class="line"> block_list=[]</span><br><span class="line"> <span class="comment">#先计算要不要加downsample</span></span><br><span class="line"> downsample=<span class="literal">None</span></span><br><span class="line"> <span class="keyword">if</span>(stride!=<span class="number">1</span> <span class="keyword">or</span> self.inplane!=plane*block.extention):</span><br><span class="line"> downsample=nn.Sequential(</span><br><span class="line"> nn.Conv2d(self.inplane,plane*block.extention,stride=stride,kernel_size=<span class="number">1</span>,bias=<span class="literal">False</span>),</span><br><span class="line"> nn.BatchNorm2d(plane*block.extention)</span><br><span class="line"> )</span><br><span class="line"></span><br><span class="line"> <span class="comment"># Conv Block输入和输出的维度(通道数和size)是不一样的,所以不能连续串联,他的作用是改变网络的维度</span></span><br><span class="line"> <span class="comment"># Identity Block 输入维度和输出(通道数和size)相同,可以直接串联,用于加深网络</span></span><br><span class="line"> <span class="comment">#Conv_block</span></span><br><span class="line"> conv_block=block(self.inplane,plane,stride=stride,downsample=downsample)</span><br><span class="line"> block_list.append(conv_block)</span><br><span class="line"> self.inplane=plane*block.extention</span><br><span class="line"></span><br><span class="line"> <span class="comment">#Identity Block</span></span><br><span class="line"> <span class="keyword">for</span> i <span class="keyword">in</span> <span class="built_in">range</span>(<span class="number">1</span>,block_num):</span><br><span class="line"> block_list.append(block(self.inplane,plane,stride=<span class="number">1</span>))</span><br><span class="line"></span><br><span class="line"> <span class="keyword">return</span> nn.Sequential(*block_list)</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"></span><br><span class="line"></span><br><span class="line">resnet=ResNet(Bottleneck,[<span class="number">3</span>,<span class="number">4</span>,<span class="number">6</span>,<span class="number">3</span>],<span class="number">1000</span>)</span><br><span class="line">x=torch.randn(<span class="number">64</span>,<span class="number">3</span>,<span class="number">224</span>,<span class="number">224</span>)</span><br><span class="line">X=resnet(x)</span><br><span class="line"><span class="built_in">print</span>(X.shape)</span><br><span class="line"></span><br><span class="line"></span><br></pre></td></tr></table></figure><h1 id="参考"><a href="#参考" class="headerlink" title="参考"></a>参考</h1><p><a href="https://zhuanlan.zhihu.com/p/31852747">你必须要知道CNN模型:ResNet - 知乎 (zhihu.com)</a></p><p><a href="https://blog.csdn.net/m0_54487331/article/details/112758795">https://blog.csdn.net/m0_54487331/article/details/112758795</a></p>]]></content>
<summary type="html"><h1 id="论文链接"><a href="#论文链接" class="headerlink" title="论文链接"></a>论文链接</h1><p>论文地址 [<a href="https://arxiv.org/abs/1512.03385">1512.03385] D</summary>
<category term="计算机视觉" scheme="https://frankho-hwc.github.io/tags/%E8%AE%A1%E7%AE%97%E6%9C%BA%E8%A7%86%E8%A7%89/"/>
<category term="深度学习" scheme="https://frankho-hwc.github.io/tags/%E6%B7%B1%E5%BA%A6%E5%AD%A6%E4%B9%A0/"/>
</entry>
<entry>
<title>Gram矩阵</title>
<link href="https://frankho-hwc.github.io/2023/03/09/Gram%E7%9F%A9%E9%98%B5/"/>
<id>https://frankho-hwc.github.io/2023/03/09/Gram%E7%9F%A9%E9%98%B5/</id>
<published>2023-03-09T08:47:04.000Z</published>
<updated>2023-03-09T14:04:56.893Z</updated>
<content type="html"><![CDATA[<h1 id="前置知识-向量的内积"><a href="#前置知识-向量的内积" class="headerlink" title="前置知识-向量的内积"></a>前置知识-向量的内积</h1><p>等之后补充,或者自行线性代数学习。</p><h1 id="什么是Gram矩阵"><a href="#什么是Gram矩阵" class="headerlink" title="什么是Gram矩阵"></a>什么是Gram矩阵</h1><h2 id="定义"><a href="#定义" class="headerlink" title="定义"></a>定义</h2><p>n维欧式空间中任意k个向量之间两两的内积所组成的矩阵,称为这k个向量的格拉姆矩阵(Gram matrix),很明显,这是一个对称矩阵。</p><p><img src="/2023/03/09/Gram%E7%9F%A9%E9%98%B5/v2-d9cbea9872a6accbc98df52e5d3b1599_720w.webp" alt="gram matrix"></p><p><img src="/2023/03/09/Gram%E7%9F%A9%E9%98%B5/v2-c31c8591818dcb5ea833b9a1b1253f29_720w.webp" alt="img"></p><p>也可以说,格拉姆矩阵$G = A^TA$,其中$A$为某个向量矩阵</p><h2 id="性质"><a href="#性质" class="headerlink" title="性质"></a>性质</h2><p>下面的都是列向量Gram矩阵</p><ol><li><p>Gram矩阵是对称矩阵<br> $$<br> G^T = (A^TA)^T = A^TA = G<br> $$</p></li><li><p>对于实矩阵<br>$$<br>r(A^TA) = r(A)<br>$$</p></li><li><p>若$A^TA = 0$,则$A = 0$</p></li><li><p>对于实矩阵 $A$, 则 $A^TA$是半正定矩阵<br>$$<br>x^TA^TAx = (A^Tx)^TAX \geq 0<br>$$</p></li><li><p>对于任意$n$阶实对称半正定矩阵$M$, 存在矩阵$A$使得$M =A^TA$成立.</p><p>因为矩阵$M$实对称, 所以$M$可以正交对角化, 即<br>$$<br>M = Q^T\Lambda Q<br>$$</p><p>又因为矩阵$M$半正定, 所以其特征值$\lambda_i \geq 0$, 所以可记<br>$$<br>\Lambda^{\frac{1}{2}} = diag(\sqrt{\lambda_1},…,\sqrt{\lambda_i})<br>$$<br>且$A =\Lambda^{\frac{1}{2}} Q^T$</p><p>则有<br>$$<br>M = Q\Lambda Q^T<br> =(\Lambda^{\frac{1}{2}}Q^T)^T\Lambda^{\frac{1}{2}}Q^T<br> =A^TA<br>$$</p></li><li><p>若$A = [\alpha_1 … \alpha_n]$列满秩, 则$A^TA$正定</p><p>由性质2知,$r(A^TA)=r(A) = n$</p><p>因为$Ax = 0$只有零解, 结合性质 (4), 对于非零$x \in \mathbb{R}^n$<br>$$<br>x^TA^TAx = (Ax)^TAx > 0<br>$$</p></li></ol><h1 id="应用"><a href="#应用" class="headerlink" title="应用"></a>应用</h1><p>在深度学习中,可以用于Style Transference。</p><p>这个以后再填坑</p><h1 id="参考"><a href="#参考" class="headerlink" title="参考"></a>参考</h1><p><a href="https://zhuanlan.zhihu.com/p/105470826">Gram 矩阵 - 知乎 (zhihu.com)</a></p><p><a href="https://zhuanlan.zhihu.com/p/187345192">格拉姆矩阵(Gram matrix)详细解读 - 知乎 (zhihu.com)</a></p>]]></content>
<summary type="html"><h1 id="前置知识-向量的内积"><a href="#前置知识-向量的内积" class="headerlink" title="前置知识-向量的内积"></a>前置知识-向量的内积</h1><p>等之后补充,或者自行线性代数学习。</p>
<h1 id="什么是Gram矩</summary>
<category term="深度学习" scheme="https://frankho-hwc.github.io/tags/%E6%B7%B1%E5%BA%A6%E5%AD%A6%E4%B9%A0/"/>
<category term="线性代数" scheme="https://frankho-hwc.github.io/tags/%E7%BA%BF%E6%80%A7%E4%BB%A3%E6%95%B0/"/>
</entry>
<entry>
<title>transformer讲解</title>
<link href="https://frankho-hwc.github.io/2023/03/09/transformer%E8%AE%B2%E8%A7%A3/"/>
<id>https://frankho-hwc.github.io/2023/03/09/transformer%E8%AE%B2%E8%A7%A3/</id>
<published>2023-03-09T08:46:48.000Z</published>
<updated>2023-03-19T13:34:28.048Z</updated>
<content type="html"><![CDATA[<h1 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h1><h2 id="论文地址"><a href="#论文地址" class="headerlink" title="论文地址"></a>论文地址</h2><p>[<a href="https://arxiv.org/abs/1706.03762">1706.03762] Attention Is All You Need (arxiv.org)</a></p><h2 id="介绍"><a href="#介绍" class="headerlink" title="介绍"></a>介绍</h2><p>Attention机制最早在视觉领域提出,2014年Google Mind发表了《Recurrent Models of Visual Attention》,使Attention机制流行起来,这篇论文采用了RNN模型,并加入了Attention机制来进行图像的分类。</p><h1 id="Attention机制"><a href="#Attention机制" class="headerlink" title="Attention机制"></a>Attention机制</h1>]]></content>
<summary type="html"><h1 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h1><h2 id="论文地址"><a href="#论文地址" class="headerlink" title="论文地址"></a>论文地址</h</summary>
<category term="计算机视觉" scheme="https://frankho-hwc.github.io/tags/%E8%AE%A1%E7%AE%97%E6%9C%BA%E8%A7%86%E8%A7%89/"/>
<category term="深度学习" scheme="https://frankho-hwc.github.io/tags/%E6%B7%B1%E5%BA%A6%E5%AD%A6%E4%B9%A0/"/>
<category term="自然语言处理" scheme="https://frankho-hwc.github.io/tags/%E8%87%AA%E7%84%B6%E8%AF%AD%E8%A8%80%E5%A4%84%E7%90%86/"/>
</entry>
<entry>
<title>Normalization串讲</title>
<link href="https://frankho-hwc.github.io/2023/03/08/Normalization%E4%B8%B2%E8%AE%B2/"/>
<id>https://frankho-hwc.github.io/2023/03/08/Normalization%E4%B8%B2%E8%AE%B2/</id>
<published>2023-03-08T13:23:40.000Z</published>
<updated>2023-03-09T11:26:27.585Z</updated>
<content type="html"><![CDATA[<h1 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h1><p>Normalization的目的就是使数据分布服从均值为0,方差为1的标准正态分布(高斯分布).其目的在于使神经元输入的数据是独立同分布的,这样可以使得网络更快收敛,而且又不会出现梯度消失的问题。</p><h1 id="Batch-Normalization"><a href="#Batch-Normalization" class="headerlink" title="Batch Normalization"></a>Batch Normalization</h1><h2 id="详解"><a href="#详解" class="headerlink" title="详解"></a>详解</h2><p>在深度学习中,网络一旦train起来,那么参数就要发生更新,除了输入层的数据外(因为输入层数据,我们已经人为的为每个样本归一化),后面网络每一层的输入数据分布是一直在发生变化的,因为在训练的时候,前面层训练参数的更新将导致后面层输入数据分布的变化。以网络第二层为例:网络的第二层输入,是由第一层的参数和input计算得到的,而第一层的参数在整个训练过程中一直在变化,因此必然会引起后面每一层输入数据分布的改变。我们把网络中间层在训练过程中,数据分布的改变称之为:<strong>“Internal Covariate Shift”</strong>。</p><p>出现ICS问题,就会导致每个神经元的输入数据不再是“独立同分布”了,则会导致</p><ol><li>上层参数需要不断适应新的输入数据分布,降低学习速度。</li><li>下层输入的变化可能趋向于变大或者变小,导致上层落入饱和区,使得学习过早停止。</li><li>每层的更新都会影响到其它层,因此每层的参数更新策略需要尽可能的谨慎。</li></ol><p>所以为了解决这个问题,就提出了批量归一化</p><p>具体做法如下</p><p><img src="/2023/03/08/Normalization%E4%B8%B2%E8%AE%B2/webp.webp" alt="img"></p><p>为什么最后规范化之后还要尺度变换和平移呢,这个操作是一个归一化的反操作,给予两个参数$\gamma$和$\beta$让神经网络自己去学习,让它自己琢磨normalization到底有没有起到优化作用。</p><h2 id="Batch-Normalization-后的效果"><a href="#Batch-Normalization-后的效果" class="headerlink" title="Batch Normalization 后的效果"></a>Batch Normalization 后的效果</h2><p><img src="/2023/03/08/Normalization%E4%B8%B2%E8%AE%B2/v2-95f654fdf99999db3fa7dab0bbfbc358_720w.webp" alt="img"></p><p>输入数据被归一到高斯分布区间,激活函数的敏感性更强了</p><p><img src="/2023/03/08/Normalization%E4%B8%B2%E8%AE%B2/v2-b31f7d863179f5f0b93d40c4fabbc31a_720w.webp" alt="img"></p><p>经过激活函数后的数据更加平滑,有利于之后的训练</p><h2 id="BatchNormalizaiton-图解"><a href="#BatchNormalizaiton-图解" class="headerlink" title="BatchNormalizaiton 图解"></a>BatchNormalizaiton 图解</h2><p><strong>1维</strong></p><p><img src="/2023/03/08/Normalization%E4%B8%B2%E8%AE%B2/62ae8f9dadb346308041b7ad909be734-16783450068039.png" alt="img"></p><p><strong>2维</strong></p><p><img src="/2023/03/08/Normalization%E4%B8%B2%E8%AE%B2/183a59ee09834831851930e0a580abcd.png" alt="img"></p><p><strong>3维</strong></p><p><img src="/2023/03/08/Normalization%E4%B8%B2%E8%AE%B2/0bff3dea63de40549f1e5abbc2f8cc8f.png" alt="img"></p><h2 id="加入BN后的效果"><a href="#加入BN后的效果" class="headerlink" title="加入BN后的效果"></a>加入BN后的效果</h2><ol><li>加快网络收敛速度</li><li>缓解梯度爆炸和防止梯度消失,由上图[Batch Normalization后的效果](##Batch Normalization 后的效果)</li><li>防止过拟合</li></ol><h1 id="Layer-Normalization"><a href="#Layer-Normalization" class="headerlink" title="Layer Normalization"></a>Layer Normalization</h1><h2 id="详解-1"><a href="#详解-1" class="headerlink" title="详解"></a>详解</h2><p>如果是遇到样本序列长度不同的时候,如RNN,transformer等,无法使用BN来进行归一化,如下图这种情况,如果使用BN的话,会出现batchsize过小的问题。同时样本数过少时,BN也不能发挥出它应该有的效果。</p><p><img src="/2023/03/08/Normalization%E4%B8%B2%E8%AE%B2/3ca9141cc2224748993520f6db520bf4.png" alt="img"></p><p>具体公式如下</p><p><img src="/2023/03/08/Normalization%E4%B8%B2%E8%AE%B2/image-20230309192458979.png" alt="image-20230309192458979"></p><p>LN中同层神经元的输入拥有相同的均值和方差,不同的输入样本有不同的均值和方差。</p><p>对于特征图</p><p><img src="/2023/03/08/Normalization%E4%B8%B2%E8%AE%B2/7000.png" alt="img"></p><p> ,LN 对每个样本的 C、H、W 维度上的数据求均值和标准差,保留 N 维度。其均值和标准差公式为:</p><p><img src="/2023/03/08/Normalization%E4%B8%B2%E8%AE%B2/7000-16783611681623.png" alt="img"></p><h2 id="与BN的对比"><a href="#与BN的对比" class="headerlink" title="与BN的对比"></a>与BN的对比</h2><p><img src="/2023/03/08/Normalization%E4%B8%B2%E8%AE%B2/v2-c039daa05cd9d5c3936c4513422690b0_720w.jpeg" alt="img"></p><p>如图所示,左边是LayerNormalizaiton,而右边是BatichNormalization。BN是按照batch来切的,batch中每一个样本的同一个维度来进行normalization。而LN则是对同一个样本的不同通道进行normalization。</p><h2 id="LN的效果"><a href="#LN的效果" class="headerlink" title="LN的效果"></a>LN的效果</h2><p>同样地,LN也能够很好地缓解ICS问题。</p><h1 id="Instance-Normalization"><a href="#Instance-Normalization" class="headerlink" title="Instance Normalization"></a>Instance Normalization</h1><h2 id="详解-2"><a href="#详解-2" class="headerlink" title="详解"></a>详解</h2><h2 id="BN-LN-IN三者对比"><a href="#BN-LN-IN三者对比" class="headerlink" title="BN,LN,IN三者对比"></a>BN,LN,IN三者对比</h2><p><img src="/2023/03/08/Normalization%E4%B8%B2%E8%AE%B2/v2-94c40b6f6f41e45f5d254906d70c10ee_720w.webp" alt="img"></p><p>如上图所示,最左边是LN,中间是BN,最右边是IN。</p><h1 id="Group-Normalization"><a href="#Group-Normalization" class="headerlink" title="Group Normalization"></a>Group Normalization</h1><h2 id="详解-3"><a href="#详解-3" class="headerlink" title="详解"></a>详解</h2><p>GN介于LN和IN之间,其首先将channel分为许多组(group),对每一组做归一化,及先将feature的维度由[N, C, H, W]reshape为[N, G,C//G , H, W],归一化的维度为[C//G , H, W]</p><h2 id="BN-LN-IN-GN四者对比"><a href="#BN-LN-IN-GN四者对比" class="headerlink" title="BN,LN,IN,GN四者对比"></a>BN,LN,IN,GN四者对比</h2><p><img src="/2023/03/08/Normalization%E4%B8%B2%E8%AE%B2/v2-fad3333df9a87c1c4f1db4b20557da6f_720w.webp" alt="img"></p><p>BatchNorm:batch方向做归一化,算N<em>H</em>W的均值<br>LayerNorm:channel方向做归一化,算C<em>H</em>W的均值<br>InstanceNorm:一个channel内做归一化,算H*W的均值<br>GroupNorm:将channel方向分group,然后每个group内做归一化,算(C//G)<em>H</em>W的均值</p><h1 id="参考"><a href="#参考" class="headerlink" title="参考"></a>参考</h1><p><a href="https://zhuanlan.zhihu.com/p/480425962">Internal Covariate Shift问题 - 知乎 (zhihu.com)</a></p><p><a href="https://zhuanlan.zhihu.com/p/24810318">什么是批标准化 (Batch Normalization) - 知乎 (zhihu.com)</a></p><p><a href="https://www.jianshu.com/p/a78470f521dd">内部协变量偏移(Internal Covariate Shift)和批归一化(Batch Normalization) - 简书 (jianshu.com)</a></p><p><a href="https://blog.csdn.net/Mike_honor/article/details/125915321">(38条消息) 标准化(Normalization)知识点总结_normalization操作汇总_CV技术指南的博客-CSDN博客</a></p><p><a href="https://zhuanlan.zhihu.com/p/54530247">模型优化之Layer Normalization - 知乎 (zhihu.com)</a></p><p><a href="https://zhuanlan.zhihu.com/p/492803886">Transformer中的归一化(五):Layer Norm的原理和实现 & 为什么Transformer要用LayerNorm - 知乎 (zhihu.com)</a></p><p><a href="https://zhuanlan.zhihu.com/p/56542480">模型优化之Instance Normalization - 知乎 (zhihu.com)</a></p><p><a href="https://zhuanlan.zhihu.com/p/35005794">全面解读Group Normalization-(吴育昕-何恺明 ) - 知乎 (zhihu.com)</a></p><p><a href="https://blog.csdn.net/duanshao/article/details/80055887">(38条消息) 组归一化(Group Normalization)的解释_技术修行的博客-CSDN博客</a></p><p><a href="https://zhuanlan.zhihu.com/p/74476637">深度学习之17——归一化(BN+LN+IN+GN) - 知乎 (zhihu.com)</a></p><p><a href="https://cloud.tencent.com/developer/article/1526775">https://cloud.tencent.com/developer/article/1526775</a></p>]]></content>
<summary type="html"><h1 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h1><p>Normalization的目的就是使数据分布服从均值为0,方差为1的标准正态分布(高斯分布).其目的在于使神经元输入的数据是独立同分布的,</summary>
<category term="计算机视觉" scheme="https://frankho-hwc.github.io/tags/%E8%AE%A1%E7%AE%97%E6%9C%BA%E8%A7%86%E8%A7%89/"/>
<category term="深度学习" scheme="https://frankho-hwc.github.io/tags/%E6%B7%B1%E5%BA%A6%E5%AD%A6%E4%B9%A0/"/>
</entry>
<entry>
<title>双线性插值卷积与反卷积</title>
<link href="https://frankho-hwc.github.io/2023/03/08/%E5%8F%8C%E7%BA%BF%E6%80%A7%E6%8F%92%E5%80%BC%E5%8D%B7%E7%A7%AF%E4%B8%8E%E5%8F%8D%E5%8D%B7%E7%A7%AF/"/>
<id>https://frankho-hwc.github.io/2023/03/08/%E5%8F%8C%E7%BA%BF%E6%80%A7%E6%8F%92%E5%80%BC%E5%8D%B7%E7%A7%AF%E4%B8%8E%E5%8F%8D%E5%8D%B7%E7%A7%AF/</id>
<published>2023-03-08T08:06:31.000Z</published>
<updated>2023-03-08T13:36:22.371Z</updated>
<content type="html"><![CDATA[<h1 id="反卷积"><a href="#反卷积" class="headerlink" title="反卷积"></a>反卷积</h1><p>在介绍反卷积之前,先介绍一下卷积</p><h2 id="卷积"><a href="#卷积" class="headerlink" title="卷积"></a>卷积</h2><p>计算机视觉里的卷积操作本质上就是数学分析里的卷积详情见下面链接</p><p>具体操作为</p><p><img src="/2023/03/08/%E5%8F%8C%E7%BA%BF%E6%80%A7%E6%8F%92%E5%80%BC%E5%8D%B7%E7%A7%AF%E4%B8%8E%E5%8F%8D%E5%8D%B7%E7%A7%AF/image-20230308191953527.png" alt="image-20230308191953527"></p><p>用矩阵法来理解为</p><p>假设输入图像为 4*4,元素矩阵为</p><p><img src="/2023/03/08/%E5%8F%8C%E7%BA%BF%E6%80%A7%E6%8F%92%E5%80%BC%E5%8D%B7%E7%A7%AF%E4%B8%8E%E5%8F%8D%E5%8D%B7%E7%A7%AF/image-20230308192113451.png" alt="image-20230308192113451"></p><p>卷积核大小为3*3</p><p><img src="/2023/03/08/%E5%8F%8C%E7%BA%BF%E6%80%A7%E6%8F%92%E5%80%BC%E5%8D%B7%E7%A7%AF%E4%B8%8E%E5%8F%8D%E5%8D%B7%E7%A7%AF/image-20230308192131123.png" alt="image-20230308192131123"></p><p>步长stride = 1, 填充padding = 0,按照卷积计算公式$output = \frac{i + 2p -k}{s} + 1$,则输出矩阵为2*2</p><p>将输入图像flatten成1维向量</p><p><img src="/2023/03/08/%E5%8F%8C%E7%BA%BF%E6%80%A7%E6%8F%92%E5%80%BC%E5%8D%B7%E7%A7%AF%E4%B8%8E%E5%8F%8D%E5%8D%B7%E7%A7%AF/image-20230308192245137.png" alt="image-20230308192245137"></p><p>同样地,将输出Flatten成1维向量</p><p><img src="/2023/03/08/%E5%8F%8C%E7%BA%BF%E6%80%A7%E6%8F%92%E5%80%BC%E5%8D%B7%E7%A7%AF%E4%B8%8E%E5%8F%8D%E5%8D%B7%E7%A7%AF/image-20230308192325607.png" alt="image-20230308192325607"></p><p>用矩阵运算来描述</p><p><img src="/2023/03/08/%E5%8F%8C%E7%BA%BF%E6%80%A7%E6%8F%92%E5%80%BC%E5%8D%B7%E7%A7%AF%E4%B8%8E%E5%8F%8D%E5%8D%B7%E7%A7%AF/image-20230308192336855.png" alt="image-20230308192336855"></p><p>推导可知稀疏矩阵$C$:</p><p><img src="/2023/03/08/%E5%8F%8C%E7%BA%BF%E6%80%A7%E6%8F%92%E5%80%BC%E5%8D%B7%E7%A7%AF%E4%B8%8E%E5%8F%8D%E5%8D%B7%E7%A7%AF/image-20230308192351512.png" alt="image-20230308192351512"></p><h2 id="反卷积-1"><a href="#反卷积-1" class="headerlink" title="反卷积"></a>反卷积</h2><p>反卷积,顾名思义就是卷积的反操作,但是对于同一个卷积核(因非其稀疏矩阵不是正交矩阵),<strong>结果转置操作之后并不能恢复到原始的数值,而仅仅保留原始的形状</strong></p><p>以矩阵运算来描述</p><p><img src="/2023/03/08/%E5%8F%8C%E7%BA%BF%E6%80%A7%E6%8F%92%E5%80%BC%E5%8D%B7%E7%A7%AF%E4%B8%8E%E5%8F%8D%E5%8D%B7%E7%A7%AF/v2-556e4849c9bf764fdbfb5ce84f0a204d_720w.webp" alt="img"></p><h2 id="棋盘效应"><a href="#棋盘效应" class="headerlink" title="棋盘效应"></a>棋盘效应</h2><p>在使用转置卷积时观察到一个棘手的现象(尤其是深色部分常出现)就是”<a href="https://www.zhihu.com/search?q=%E6%A3%8B%E7%9B%98%E6%A0%BC%E5%AD%90%E7%8A%B6%E4%BC%AA%E5%BD%B1&search_source=Entity&hybrid_search_source=Entity&hybrid_search_extra=%7B%22sourceType%22:%22answer%22,%22sourceId%22:1682194600%7D">棋盘格子状伪影</a>“,被命名为棋盘效应(Checkboard artifacts)。</p><p>棋盘效应是由于转置卷积的“不均匀重叠”(Uneven overlap)的结果。使图像中某个部位的颜色比其他部位更深。尤其是当卷积核(Kernel)的大小不能被步长(Stride)整除时,<a href="https://www.zhihu.com/search?q=%E5%8F%8D%E5%8D%B7%E7%A7%AF&search_source=Entity&hybrid_search_source=Entity&hybrid_search_extra=%7B%22sourceType%22:%22answer%22,%22sourceId%22:1682194600%7D">反卷积</a>就会不均匀重叠。虽然原则上网络可以通过训练调整权重来避免这种情况,但在实践中神经网络很难完全避免这种不均匀重叠。</p><p>在(a)中,步长为1,卷积核为$2*2$。如红色部分所展示,输入第一个像素映射到输出上第一个和第二个像素。而正如绿色部分,输入的第二个像素映射到输出上的第二个和第三个像素。则输出上的第二个像素从输入上的第一个和第二个像素接收信息。总而言之,输出中间部分的像素从输入中接收的信息存在重叠区域。在示例(b)中的卷积核大小增加到3时,输出所接收到的大多数信息的中心部分将收缩。但这并不是最大的问题,因为重叠仍然是均匀的。</p><p><img src="/2023/03/08/%E5%8F%8C%E7%BA%BF%E6%80%A7%E6%8F%92%E5%80%BC%E5%8D%B7%E7%A7%AF%E4%B8%8E%E5%8F%8D%E5%8D%B7%E7%A7%AF/v2-9a93cbac03830084b90574f017b6038b_720w.webp" alt="img"></p><p>如果将步幅改为2,在卷积核大小为2的示例中,输出上的所有像素从输入中接收相同数量的信息。由下图(a)可见,此时描以转置卷积的重叠。若将卷积核大小改为4(下图(b)),则均匀重叠区域将收缩,与此同时因为重叠是均匀的,故仍然为有效输出。但如果将卷积核大小改为3,步长为2(下图(c)),以及将卷积核大小改为5,步长为2(下图(d)),问题就出现了,对于这两种情况输出上的每个像素接收的信息量与相邻像素不同。在输出上找不到连续且均匀重叠区域。</p><p><img src="/2023/03/08/%E5%8F%8C%E7%BA%BF%E6%80%A7%E6%8F%92%E5%80%BC%E5%8D%B7%E7%A7%AF%E4%B8%8E%E5%8F%8D%E5%8D%B7%E7%A7%AF/v2-6288d1734ad00c718fc814e4c7bbc985_720w.webp" alt="img"></p><p> 在二维情况下棋盘效应更为严重,下图直观地展示了在二维空间内的棋盘效应。</p><p><img src="/2023/03/08/%E5%8F%8C%E7%BA%BF%E6%80%A7%E6%8F%92%E5%80%BC%E5%8D%B7%E7%A7%AF%E4%B8%8E%E5%8F%8D%E5%8D%B7%E7%A7%AF/v2-de1bb8f86193666e3b0a1539d273ab32_720w.webp" alt="img"></p><h3 id="如何避免"><a href="#如何避免" class="headerlink" title="如何避免"></a>如何避免</h3><p><strong>采取可以被步长整除的卷积核长度</strong><br>该方法较好地应对了棋盘效应问题,但仍然不够圆满,因为一旦我们的卷积核学习不均匀。</p><p><strong>线性插值</strong></p><h2 id="上采样"><a href="#上采样" class="headerlink" title="上采样"></a>上采样</h2><p>在应用在计算机视觉的深度学习领域,由于输入图像通过卷积神经网络(CNN)提取特征后,输出的尺寸往往会变小,而有时我们需要将图像恢复到原来的尺寸以便进行进一步的计算**(e.g.:图像的语义分割(segmentation))<strong>,这个采用扩大图像尺寸,实现图像由小分辨率到大分辨率的映射的操作,叫做</strong>上采样(Upsample)**</p><h2 id="下采样"><a href="#下采样" class="headerlink" title="下采样"></a>下采样</h2><p>下采样实际上就是缩小图像,主要目的是为了使得图像符合显示区域的大小,生成对应图像的缩略图。比如说在CNN中的池化层或卷积层就是下采样。不过卷积过程导致的图像变小是为了提取特征,而池化下采样是为了降低特征的维度。下采样层有两个作用:<br>一是减少计算量,防止过拟合;<br>二是增大感受野,使得后面的卷积核能够学到更加全局的信息。</p><h1 id="双线性插值"><a href="#双线性插值" class="headerlink" title="双线性插值"></a>双线性插值</h1><h2 id="双线性插值-Bilinear-Interpolation)"><a href="#双线性插值-Bilinear-Interpolation)" class="headerlink" title="双线性插值(Bilinear Interpolation)"></a>双线性插值(Bilinear Interpolation)</h2><p>在讲双线性插值卷积前,先讲单线性插值卷积</p><h3 id="单线性插值"><a href="#单线性插值" class="headerlink" title="单线性插值"></a>单线性插值</h3><p><img src="/2023/03/08/%E5%8F%8C%E7%BA%BF%E6%80%A7%E6%8F%92%E5%80%BC%E5%8D%B7%E7%A7%AF%E4%B8%8E%E5%8F%8D%E5%8D%B7%E7%A7%AF/image-20230308163812548.png" alt="image-20230308163812548"></p><p>如图所示,已知中P1点和P2点,坐标分别为(x1, y1)、(x2, y2),要计算 [x1, x2] 区间内某一位置 x 在直线上的y值</p><p>由两点确定一条直线可知<br>$$<br>\frac{y-y_1}{x - x_1} = \frac{y_2 - y_1}{x_2 - x_1}<br>$$<br>经过整理可得<br>$$<br>y = \frac{x_2 - x}{x_2 - x_1}y_1 - \frac{x - x_1}{x_2 - x_1} y_2<br>$$<br>首先看分子,分子可以看成x与x1和x2的距离作为权重,这也是很好理解的,P点与P1、P2点符合线性变化关系,所以P离P1近就更接近P1,反之则更接近P2。</p><p>现在再把公式中的分式看成一个整体,原式可以理解成y1与y2是加权系数,如何理解这个加权,要返回来思考一下,咱们先要明确一下根本的目的:咱们现在不是在求一个公式,而是在图像中根据2个点的像素值求未知点的像素值。这样一个公式是不满足咱们写代码的要求的。<br>现在根据实际的目的理解,就很好理解这个加权了,y1与y2分别代表原图像中的像素值,上面的公式可以写成如下形式:<br>$$<br>f(P) =\frac{x_2 - x}{x_2 - x_1}f(P_1) - \frac{x - x_1}{x_2 - x_1} f(P_2)<br>$$<br>其中,$P$,$P_1$,$P_2$分别代表了被插值的像素和插值两边的像素</p><h3 id="再看双线性插值"><a href="#再看双线性插值" class="headerlink" title="再看双线性插值"></a>再看双线性插值</h3><p>双线性插值是分别在两个方向计算了共3次单线性插值,如图所示,先在x方向求2次单线性插值,获得R1(x, y1)、R2(x, y2)两个临时点,再在y方向计算1次单线性插值得出P(x, y)(实际上调换2次轴的方向先y后x也是一样的结果)。<br><img src="/2023/03/08/%E5%8F%8C%E7%BA%BF%E6%80%A7%E6%8F%92%E5%80%BC%E5%8D%B7%E7%A7%AF%E4%B8%8E%E5%8F%8D%E5%8D%B7%E7%A7%AF/image-20230308164511503.png" alt="image-20230308164511503"></p><p>首先是插值出$R_1$和$R_2$<br>$$<br>f(R_1) =\frac{x_2 - x}{x_2 - x_1}f(Q_{11}) - \frac{x - x_1}{x_2 - x_1} f(Q_{21}) \<br>f(R_2) =\frac{x_2 - x}{x_2 - x_1}f(Q_{12}) - \frac{x - x_1}{x_2 - x_1} f(Q_{22})<br>$$<br>然后再通过$R_1$和$R_2$插值出P<br>$$<br>f(P) =\frac{y_2 - y}{y_2 - y_1}f(R_{1}) - \frac{y - y_1}{y_2 - y_1} f(R_{2})<br>$$<br>总结起来就是<br>$$<br>f(x,y) = \frac{f(Q_{11})}{(x_2-x_1)(y_2-y_1)}(x_2-x)(y_2-y) + \frac{f(Q_{21})}{(x_2-x_1)(y_2-y_1)}(x - x_1)(y_2 - y) + \frac{f(Q_{12})}{(x_2-x_1)(y_2-y_1)}(x_2 - x_1)(y_2 -y <em>1) + \frac{f(Q</em>{22})}{(x_2-x_1)(y_2-y_1)}(x - x1)(y - y2)<br>$$</p><h1 id="双线性插值与反卷积的关系"><a href="#双线性插值与反卷积的关系" class="headerlink" title="双线性插值与反卷积的关系"></a>双线性插值与反卷积的关系</h1><p>双线性插值与反卷积都可以用来实现上采样,而双线性插值可以通过反卷积实现。而插值方法不止一种,还有三线性插值等。插值方法的优点是可以避免棋盘效应。</p><h1 id="参考"><a href="#参考" class="headerlink" title="参考"></a>参考</h1><p><a href="https://zhuanlan.zhihu.com/p/48501100">反卷积(Transposed Convolution)详细推导 - 知乎 (zhihu.com)</a></p><p><a href="https://www.zhihu.com/question/22298352">知乎 (zhihu.com)</a></p><p><a href="https://www.zhihu.com/question/48279880/answer/1682194600">https://www.zhihu.com/question/48279880/answer/1682194600</a></p><p>对上述帖子致以诚挚感谢</p>]]></content>
<summary type="html"><h1 id="反卷积"><a href="#反卷积" class="headerlink" title="反卷积"></a>反卷积</h1><p>在介绍反卷积之前,先介绍一下卷积</p>
<h2 id="卷积"><a href="#卷积" class="headerlink" </summary>
<category term="计算机视觉" scheme="https://frankho-hwc.github.io/tags/%E8%AE%A1%E7%AE%97%E6%9C%BA%E8%A7%86%E8%A7%89/"/>
<category term="深度学习" scheme="https://frankho-hwc.github.io/tags/%E6%B7%B1%E5%BA%A6%E5%AD%A6%E4%B9%A0/"/>
</entry>
<entry>
<title>名词解释-ablation</title>
<link href="https://frankho-hwc.github.io/2023/03/08/%E5%90%8D%E8%AF%8D%E8%A7%A3%E9%87%8A-Ablation%20study/"/>
<id>https://frankho-hwc.github.io/2023/03/08/%E5%90%8D%E8%AF%8D%E8%A7%A3%E9%87%8A-Ablation%20study/</id>
<published>2023-03-08T07:56:36.000Z</published>
<updated>2023-03-08T08:53:04.267Z</updated>
<content type="html"><![CDATA[<h1 id="何为Ablation-study"><a href="#何为Ablation-study" class="headerlink" title="何为Ablation study"></a>何为Ablation study</h1><p><strong>Ablation study</strong>,意为消融实验,<strong>通常是指删除模型或算法的某些“功能”,并查看其如何影响性能。</strong></p><p>在论文中一般来说会提出多个创新方法,或者新型结构模块,或注意力模块等。这些东西在一起为模型的性能作出了贡献。然而为了了解每个部分单独能发挥的作用,常常会在论文中提出消融研究。</p><p>例如某论文提出了方法A,B,C。而该论文是基于某个baseline的改进。因此,在消融研究部分,会进行以下实验,baseline ,baseline+A,baseline+B, baseline+C, baseline+A+B+C等实验的各个评价指标有多少,从而得出每个部分所能发挥的作用有多大。</p><p>参考文章<a href="https://zhuanlan.zhihu.com/p/389091953">名词解释 | 论文中的Ablation study - 知乎 (zhihu.com)</a></p>]]></content>
<summary type="html"><h1 id="何为Ablation-study"><a href="#何为Ablation-study" class="headerlink" title="何为Ablation study"></a>何为Ablation study</h1><p><strong>Ablati</summary>
<category term="深度学习" scheme="https://frankho-hwc.github.io/tags/%E6%B7%B1%E5%BA%A6%E5%AD%A6%E4%B9%A0/"/>
</entry>
<entry>
<title>EnLightenGAN</title>
<link href="https://frankho-hwc.github.io/2023/03/04/EnLightenGAN/"/>
<id>https://frankho-hwc.github.io/2023/03/04/EnLightenGAN/</id>
<published>2023-03-04T12:03:15.000Z</published>
<updated>2023-04-01T12:59:46.599Z</updated>
<content type="html"><![CDATA[<h1 id="EnligtenGAN"><a href="#EnligtenGAN" class="headerlink" title="EnligtenGAN"></a>EnligtenGAN</h1><p>github:<a href="https://github.com/VITA-Group/EnlightenGAN">https://github.com/VITA-Group/EnlightenGAN</a></p><p>论文地址:[<a href="https://arxiv.org/abs/1906.06972">1906.06972] EnlightenGAN: Deep Light Enhancement without Paired Supervision (arxiv.org)</a></p><h1 id="论文讲解"><a href="#论文讲解" class="headerlink" title="论文讲解"></a>论文讲解</h1><h2 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h2><p>该论文是低亮图像增强领域第一篇无监督方法的模型,且是基于非配准(unpaired)图像的模型。即不需要一张low-light和一张对应normal-light图像配对的无监督模型。该模型对数据集要求较低,且数据集两种图片数量可以不需要相等。是一种与之前基于retinex理论的模型不同的模型。该模型是基于cyclegan模型的对抗网络,类似于风格迁移任务。</p><h2 id="生成器"><a href="#生成器" class="headerlink" title="生成器"></a>生成器</h2><p>该生成器是一个U-Net架构的网络。如图所示</p><p><img src="/2023/03/04/EnLightenGAN/image-20230401203204771.png" alt="image-20230401203204771"></p><p>首先是将需要增强的low-light与它的灰度图concat起来送入U-Net中,然后就是经过五个block。前四个block是由两个conv层组成。然后反卷积的过程中与灰度图下采样后的进行element-wise multiplitation,最后出来的与原来的灰度图做element-wise multiplitation,再与原来的图片相加就得到增强了的图片。为什么要乘以灰度图呢,这里作者有一个假设,就是我们如果要增强一张图片,那么较暗的区域一定要比较亮的区域增强更多。所以乘以灰度图可以做到一定的self-regularization(作者所说的)</p><h2 id="分类器"><a href="#分类器" class="headerlink" title="分类器"></a>分类器</h2><p>此处分类器有两个,一个全局的分类器和一个局部的分类器,结构如图所示。</p><p><img src="/2023/03/04/EnLightenGAN/image-20230401204008109.png" alt="image-20230401204008109"></p><p>可以看到,分类器输出的是true/false,表示判断这张图片是真实的normal-light图像还是增强后获得的normal-light图像。全局是对整张图片进行判断,局部的则是通过对原图片切5个patch进行判断。然后判断之后输出true/false再经过损失函数进行计算后进行反向传播。下面就开始将损失函数。</p><h2 id="损失函数"><a href="#损失函数" class="headerlink" title="损失函数"></a>损失函数</h2><h1 id="代码讲解"><a href="#代码讲解" class="headerlink" title="代码讲解"></a>代码讲解</h1><p>首先我们可以看到有scripts文件夹,我们运行的程序就在这个里面‘</p><p>通过分析代码我们可以知道实际运行的还是train.py和predict.py</p><p><img src="/2023/03/04/EnLightenGAN/image-20230306215659710.png" alt="image-20230306215659710"></p><p>然后我们可以发现有一个option文件夹,里面就是有附带的一些选项。</p><p>configs里有一个yaml文件,里面配置了enlightenGAN的超参数。</p><p>data文件夹里定义了dataloader,即如何将数据读取进去。</p><p>最关键的是models里的singlemodel和networks这两个py文件,其他文件我觉得应该是一些替代组件,应该是用来做消融实验的?</p><p>此处具体讲解的就是这两个文件</p><p>这个模型使用是一个U-Net架构的Generator和两个Discirminator,具体之后更新</p><h1 id="代码运行及一些问题"><a href="#代码运行及一些问题" class="headerlink" title="代码运行及一些问题"></a>代码运行及一些问题</h1><h2 id="运行步骤"><a href="#运行步骤" class="headerlink" title="运行步骤"></a>运行步骤</h2><h3 id="1-从github上git-clone源码"><a href="#1-从github上git-clone源码" class="headerlink" title="1. 从github上git clone源码"></a>1. 从github上git clone源码</h3><figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">git clone https://github.com/VITA-Group/EnlightenGAN</span><br></pre></td></tr></table></figure><h3 id="2-创建虚拟环境-也可以使用以前的环境-并安装依赖"><a href="#2-创建虚拟环境-也可以使用以前的环境-并安装依赖" class="headerlink" title="2.创建虚拟环境(也可以使用以前的环境),并安装依赖"></a>2.创建虚拟环境(也可以使用以前的环境),并安装依赖</h3><figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">conda activate enlighten</span><br><span class="line">pip install -r requirement.txt</span><br></pre></td></tr></table></figure><h3 id="3-创建文件夹-并将vgg预训练模型放入其中"><a href="#3-创建文件夹-并将vgg预训练模型放入其中" class="headerlink" title="3.创建文件夹,并将vgg预训练模型放入其中"></a>3.创建文件夹,并将vgg预训练模型放入其中</h3><figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">mkdir model</span><br></pre></td></tr></table></figure><p>模型地址:<a href="https://drive.google.com/file/d/1IfCeihmPqGWJ0KHmH-mTMi_pn3z3Zo-P/view?usp=sharing">https://drive.google.com/file/d/1IfCeihmPqGWJ0KHmH-mTMi_pn3z3Zo-P/view?usp=sharing</a></p><p>本人提供一个百度网盘 链接:<a href="https://pan.baidu.com/s/1qX97-7H3HCwllLwiBUo_Zg?pwd=ktnj">https://pan.baidu.com/s/1qX97-7H3HCwllLwiBUo_Zg?pwd=ktnj</a> code:ktnj </p><h3 id="4-训练"><a href="#4-训练" class="headerlink" title="4. 训练"></a>4. 训练</h3><h4 id="创建文件夹"><a href="#创建文件夹" class="headerlink" title="创建文件夹"></a>创建文件夹</h4><p> …/final_dataset/trainA and …/final_dataset/trainB(即final_dataset文件夹与项目文件夹同级的位置),将<a href="https://drive.google.com/drive/folders/1fwqz8-RnTfxgIIkebFG2Ej3jQFsYECh0?usp=sharing">图片</a>下载分别放入</p><p>如无法下载,此处有百度网盘</p><p>链接:<a href="https://pan.baidu.com/s/1MpSKs5HVs6alMjfzQtm3rA?pwd=vgiy">https://pan.baidu.com/s/1MpSKs5HVs6alMjfzQtm3rA?pwd=vgiy</a> code:vgiy </p><h4 id="可视化-可选"><a href="#可视化-可选" class="headerlink" title="可视化(可选)"></a>可视化(可选)</h4><figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">nohup python -m visdom.server -port=8097</span><br></pre></td></tr></table></figure><p>访问步骤</p><p>打开浏览器,输入<a href="http://localhost:8097/%EF%BC%88%E5%8F%AF%E4%BB%A5%E5%AE%9E%E6%97%B6%E8%A7%82%E7%9C%8B%E5%9B%BE%E7%89%87%E7%BB%93%E6%9E%9C%EF%BC%89">http://localhost:8097/(可以实时观看图片结果)</a><br>如果在另一台电脑上跑,输入地址:地址号:端口号<br>如:10.162.34.109:8097</p><h4 id="进行训练"><a href="#进行训练" class="headerlink" title="进行训练"></a>进行训练</h4><figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">python scripts/script.py --train</span><br><span class="line"></span><br></pre></td></tr></table></figure><p><strong>可能遇到问题</strong></p><p>如果你的集群的卡不是三张,请打开scripts文件夹下的script.py,将第37行 –gpu_ids 修改为你所拥有显卡数量的编号</p><p><img src="/2023/03/04/EnLightenGAN/image-20230304202357977.png" alt="image-20230304202357977"></p><p>我使用的是有两张3090的服务器,所以修改位0,1</p><p>否则将会出现这样的错误</p><figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">CustomDatasetDataLoader</span><br><span class="line">dataset [UnalignedDataset] was created</span><br><span class="line">#training images = 1016</span><br><span class="line">single</span><br><span class="line">Traceback (most recent call last):</span><br><span class="line"> File "train.py", line 19, in <module></span><br><span class="line"> model = create_model(opt)</span><br><span class="line"> File "/home/ubuntu/hwc/EnlightenGAN-master/models/models.py", line 36, in create_model</span><br><span class="line"> model.initialize(opt)</span><br><span class="line"> File "/home/ubuntu/hwc/EnlightenGAN-master/models/single_model.py", line 40, in initialize</span><br><span class="line"> self.vgg = networks.load_vgg16("./model", self.gpu_ids)</span><br><span class="line"> File "/home/ubuntu/hwc/EnlightenGAN-master/models/networks.py", line 1051, in load_vgg16</span><br><span class="line"> vgg = torch.nn.DataParallel(vgg, gpu_ids)</span><br><span class="line"> File "/home/ubuntu/anaconda3/envs/IAT/lib/python3.7/site-packages/torch/nn/parallel/data_parallel.py", line 142, in __init__</span><br><span class="line"> _check_balance(self.device_ids)</span><br><span class="line"> File "/home/ubuntu/anaconda3/envs/IAT/lib/python3.7/site-packages/torch/nn/parallel/data_parallel.py", line 23, in _check_balance</span><br><span class="line"> dev_props = _get_devices_properties(device_ids)</span><br><span class="line"> File "/home/ubuntu/anaconda3/envs/IAT/lib/python3.7/site-packages/torch/_utils.py", line 458, in _get_devices_properties</span><br><span class="line"> return [_get_device_attr(lambda m: m.get_device_properties(i)) for i in device_ids]</span><br><span class="line"> File "/home/ubuntu/anaconda3/envs/IAT/lib/python3.7/site-packages/torch/_utils.py", line 458, in <listcomp></span><br><span class="line"> return [_get_device_attr(lambda m: m.get_device_properties(i)) for i in device_ids]</span><br><span class="line"> File "/home/ubuntu/anaconda3/envs/IAT/lib/python3.7/site-packages/torch/_utils.py", line 441, in _get_device_attr</span><br><span class="line"> return get_member(torch.cuda)</span><br><span class="line"> File "/home/ubuntu/anaconda3/envs/IAT/lib/python3.7/site-packages/torch/_utils.py", line 458, in <lambda></span><br><span class="line"> return [_get_device_attr(lambda m: m.get_device_properties(i)) for i in device_ids]</span><br><span class="line"> File "/home/ubuntu/anaconda3/envs/IAT/lib/python3.7/site-packages/torch/cuda/__init__.py", line 299, in get_device_properties</span><br><span class="line"> raise AssertionError("Invalid device id")</span><br><span class="line">AssertionError: Invalid device id</span><br></pre></td></tr></table></figure><p>此外,还要修改一个地方</p><p><img src="/2023/03/04/EnLightenGAN/image-20230304203105671.png" alt="image-20230304203105671"></p><p>否则会报错,是因为YAML 5.1版本后弃用了yaml.load(file)这个用法,因为觉得很不安全,5.1版本之后就修改了需要指定Loader,通过默认加载器(FullLoader)禁止执行任意函数,该load函数也变得更加安全。</p><p>此外还有个报错</p><figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">model [SingleGANModel] was created</span><br><span class="line">Setting up a new session...</span><br><span class="line">create web directory ./checkpoints/enlightening/web...</span><br><span class="line">/home/ubuntu/anaconda3/envs/IAT/lib/python3.7/site-packages/torch/nn/functional.py:2952: UserWarning: nn.functional.upsample is deprecated. Use nn.functional.interpolate instead.</span><br><span class="line"> warnings.warn("nn.functional.upsample is deprecated. Use nn.functional.interpolate instead.")</span><br><span class="line">/home/ubuntu/anaconda3/envs/IAT/lib/python3.7/site-packages/torch/nn/functional.py:3063: UserWarning: Default upsampling behavior when mode=bilinear is changed to align_corners=False since 0.4.0. Please specify align_corners=True if the old behavior is desired. See the documentation of nn.Upsample for details.</span><br><span class="line"> "See the documentation of nn.Upsample for details.".format(mode))</span><br><span class="line">Traceback (most recent call last):</span><br><span class="line"> File "train.py", line 37, in <module></span><br><span class="line"> errors = model.get_current_errors(epoch)</span><br><span class="line"> File "/home/ubuntu/hwc/EnlightenGAN-master/models/single_model.py", line 413, in get_current_errors</span><br><span class="line"> D_A = self.loss_D_A.data[0]</span><br><span class="line">IndexError: invalid index of a 0-dim tensor. Use `tensor.item()` in Python or `tensor.item<T>()` in C++ to convert a 0-dim tensor to a number</span><br><span class="line"></span><br></pre></td></tr></table></figure><p>这个就是把single_model.py里所有的data[0]改成.item就行了</p><h3 id="5-评估"><a href="#5-评估" class="headerlink" title="5.评估"></a>5.评估</h3><h4 id="仅测试时:"><a href="#仅测试时:" class="headerlink" title="仅测试时:"></a>仅测试时:</h4><p>下载pretrained model,放入./checkpoints/enlightening中</p><h4 id="创建文件夹-1"><a href="#创建文件夹-1" class="headerlink" title="创建文件夹"></a>创建文件夹</h4><p>…/test_dataset/testA and …/test_dataset/testB(即test_dataset文件夹与项目文件夹同级的位置),将自己要测试的图片放入testA,在testB中至少存入一张随机图片</p><h4 id="运行测试代码"><a href="#运行测试代码" class="headerlink" title="运行测试代码"></a>运行测试代码</h4><figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">python scripts/script.py --predict</span><br></pre></td></tr></table></figure>]]></content>
<summary type="html"><h1 id="EnligtenGAN"><a href="#EnligtenGAN" class="headerlink" title="EnligtenGAN"></a>EnligtenGAN</h1><p>github:<a href="https://github.com</summary>
<category term="计算机视觉" scheme="https://frankho-hwc.github.io/tags/%E8%AE%A1%E7%AE%97%E6%9C%BA%E8%A7%86%E8%A7%89/"/>
<category term="深度学习" scheme="https://frankho-hwc.github.io/tags/%E6%B7%B1%E5%BA%A6%E5%AD%A6%E4%B9%A0/"/>
<category term="低亮图像增强" scheme="https://frankho-hwc.github.io/tags/%E4%BD%8E%E4%BA%AE%E5%9B%BE%E5%83%8F%E5%A2%9E%E5%BC%BA/"/>
</entry>
</feed>