-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathindex.xml
More file actions
839 lines (620 loc) · 39.2 KB
/
Copy pathindex.xml
File metadata and controls
839 lines (620 loc) · 39.2 KB
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
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
<?xml version="1.0" encoding="utf-8" standalone="yes" ?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
<channel>
<title>Chanrom - Logging life</title>
<link>http://chanrom.github.io/</link>
<description>Recent content on Chanrom - Logging life</description>
<generator>Hugo -- gohugo.io</generator>
<language>en</language>
<lastBuildDate>Sun, 20 Aug 2017 21:38:52 +0800</lastBuildDate>
<atom:link href="http://chanrom.github.io/index.xml" rel="self" type="application/rss+xml" />
<item>
<title>About</title>
<link>http://chanrom.github.io/about/</link>
<pubDate>Sun, 20 Aug 2017 21:38:52 +0800</pubDate>
<guid>http://chanrom.github.io/about/</guid>
<description><p>Hugo is a static site engine written in Go.</p>
<p>It makes use of a variety of open source projects including:</p>
<ul>
<li><a href="https://github.com/spf13/cobra">Cobra</a></li>
<li><a href="https://github.com/spf13/viper">Viper</a></li>
<li><a href="https://github.com/spf13/jWalterWeatherman">J Walter Weatherman</a></li>
<li><a href="https://github.com/spf13/cast">Cast</a></li>
</ul>
<p>Learn more and contribute on <a href="https://github.com/gohugoio">GitHub</a>.</p>
</description>
</item>
<item>
<title>Emacs</title>
<link>http://chanrom.github.io/post/mimic/emacs/</link>
<pubDate>Sun, 19 Aug 2018 00:00:00 +0000</pubDate>
<guid>http://chanrom.github.io/post/mimic/emacs/</guid>
<description>
<h1 id="部分参考如下网站">部分参考如下网站</h1>
<p><a href="https://www.cnblogs.com/kunyuanjushi/p/5951404.html">Emacs常用快捷键</a></p>
<h1 id="基本命令">基本命令</h1>
<pre><code>C-x C-f 打开/新建文件
C-x C-s 保存当前缓冲区
C-x C-w 当前缓冲区另存为
C-x b 切换Buffer
C-x C-b 显示Buffer列表
C-x k 关闭当前Buffer
C-x C-v 关闭当前Buffer并打开新文件
C-x C-c 关闭Emacs
C-c C-z 终止shell中的进程
</code></pre>
<h1 id="窗口命令">窗口命令</h1>
<pre><code>C-x 2 水平分割窗格
C-x 3 垂直分割窗格
C-x 0 关闭当前窗口
C-x o 切换窗口
C-x 1 关闭其他窗口
C-x 5 2 新建窗口
C-x 5 f 新窗口中打开文件
</code></pre>
<h1 id="光标移动命令">光标移动命令</h1>
<pre><code>C-x 2 水平分割窗格
C-x 3 垂直分割窗格
C-x 0 关闭当前窗口
C-x o 切换窗口
C-x 1 关闭其他窗口
C-x 5 2 新建窗口
C-x 5 f 新窗口中打开文件
</code></pre>
<h1 id="编辑命令">编辑命令</h1>
<pre><code>C-v 向前翻页
M-v 向后翻页
M-r 将光标移动到屏幕中间那行
C-a 移到行首
M-a 移到句首,从行首到句首之间可能有空格
C-e 移到行尾
M-e 移到句尾
M-{ 向上移动一段
M-} 向下移动一段
C-right 向前移动一个单词
C-left 向后移动一个单词
C-up 向前移动一段
C-down 向后移动一段
M-&lt; 移到整个文本开头
M-&gt; 移到整个文本末尾
M-x goto-line 移动到某一行
C-l 重绘屏幕,效果就是当前编辑行移动窗口中央
C-Space 设置开始标记
C-@ 设置开始标记(C-space可能被操作系统拦截)
M-w 复制标记区内容
C-y 帖粘
M-u 使光标处的单词大写
M-l 使光标处的单词小写
M-c 使光标处单词首字母大写
C-k 删除一行
</code></pre>
<h1 id="搜索-替换命令">搜索/替换命令</h1>
<pre><code>C-s 向下搜索
C-r 向上搜索
M-x replace-string 替换(替换光标之后的内容)
M-x replace-regexp 正则替换(替换光标之后的内容)
</code></pre>
<h1 id="帮助">帮助</h1>
<pre><code>C-h ? 查看帮助信息
C-h f 查看一个函数
C-h v 查看一个变量
C-h k 查看一个键绑定 (C-h c 也是查看键绑定,但是信息较简略)
C-h C-f 查看一个函数的info,非常有用
C-h i 看Info
</code></pre>
<h1 id="其他">其他</h1>
<pre><code>C-M-\ 对选中区域,按照某种格式(比如C程序)进行格式化
C-x h 全部选中
M-! 执行外部shell命令
M-x shell 模拟shell的buffer
M-x term 模拟terminal, C-c k 关闭terminal
C-x C-q 修改buffer的只读属性
</code></pre>
</description>
</item>
<item>
<title>NLP一些经典算法</title>
<link>http://chanrom.github.io/post/nlp/basic/</link>
<pubDate>Thu, 17 May 2018 00:00:00 +0000</pubDate>
<guid>http://chanrom.github.io/post/nlp/basic/</guid>
<description>
<h1 id="tf-idf">TF-IDF</h1>
<h2 id="简介">简介</h2>
<p><code>TF-IDF</code>,指的是词频-逆文档频率(<code>Term Frequency-Inverse Document Frequency</code>),一种用于文档检索的加权计算方法,常用于文档的向量表示。<code>TF-IDF</code>可表示某个词在当前文档的重要程度,具体地,词的重要性和它在文档中出现的次数成正比,但是和它在整个语料库出现的次数成反比。通俗的说就是,一个词语在一篇文档中出现的次数越多而在所有文档出现的次数越少,就越能代表这个文档。</p>
<h2 id="定义">定义</h2>
<p><code>TF-IDF</code>的定义如下:
$$
TFIDF_{i,j} = \frac{N_{i,j}}{N_{\cdot j}} \log (\frac{D}{D_i})
$$
其中\( N_{i,j} \) 表示词\( i \) 出现在文档\( j \)的次数;</p>
<p>\( N_{\cdot, j} \) 表示文档\( j \)一共有多少词;</p>
<p>\( D \) 表示文档的总数;</p>
<p>\( D_{i} \) 表示有多少文档出现过词\( i \)。</p>
<p>注意,以上提到的词一般先经过了无用词(<code>stopwords</code>)的过滤。</p>
<h1 id="lsa">LSA</h1>
<h2 id="简介-1">简介</h2>
<p><code>LSA</code>(潜在语义分析)目的是解决通过搜索词(<code>search words</code>)定位出相关的文档的问题。<code>LSA</code>的基本原理是将文档和单词都映射到同一个语义空间(<code>concept space</code>)中,并在该空间进行对比分析。</p>
<h2 id="定义-1">定义</h2>
<p>首先需要构建词-文档的矩阵 \(A\),\(A\) 中的每个元素 \(A_{i j}\) 表示搜索词 \(w_i\) 在文档 \(d_j\) 出现的次数(一般又转换为<code>TF-IDF</code>并作平滑处理)。</p>
<p>接着对 \(A\) 做奇异值分解:
$$
A = U S V^T
$$
\(U\) 为一个 \(R^{t \times n}\) 的正交矩阵,每行代表一个词的表示,其含义为该词在语义空间的每个维度上中重要程度;</p>
<p>\(V\) 为一个 \(R^{d \times n}\) 的正交矩阵,每行代表一个文档的表示,其含义为该文档在语义空间的每个维度上中重要程度;</p>
<p>\(S\) 为一个 \(R^{n \times n}\) 的奇异矩阵,每个元素刻画了词和文档映射到语义空间后二者的相关度。</p>
<h2 id="优缺点">优缺点</h2>
<p>优点:</p>
<ul>
<li>低维的向量表示可以缓解多词一义的情况(一词多义应该也可以有一点的作用,只是很有限)</li>
<li>降维可以去除部分噪音,特征的表示更加鲁棒</li>
</ul>
<p>缺点:</p>
<ul>
<li>貌似因为一个词就是一个表示,不能很好的处理一词多义的情况</li>
<li>SVD分解的复杂度很高</li>
<li>物理解释偏弱</li>
</ul>
<h1 id="plsa">pLSA</h1>
<h2 id="介绍">介绍</h2>
<p>pLSA是在LSA的基础上提出的,一般就可以称之为主题模型。pLSA在模型中引入了一个隐变量:主题变量z,进一步刻画了文档生成的过程。在介绍pLSA之前,先定义基本的符号:</p>
<ul>
<li>\( w \) 表示词,\( V\) 表示所有单词的个数,也就是词表大小(在语料库中统计,固定值)</li>
<li>\( z\) 表示主题,\( K\) 代表主题的个数(预先设定,固定值)</li>
<li>\( D = (d_1, d_2, &hellip;, d_M)\),其中 \( M\) 代表语料库中文档数目</li>
<li>\( d = (w_1, w_2, &hellip;, w_N)\),其中 \( N\) 代表该文档内的词的数目</li>
</ul>
<p>pLSA定义了语料库的生成过程(如图所示):</p>
<ul>
<li>对于每一篇文档 \( d \)
<ul>
<li>对于该文档的每个位置</li>
<li>选择一个主题 \( z \)</li>
<li>根据主题选择词 \( w\)</li>
</ul></li>
</ul>
<p align="center">
<img src="http://chanrom.github.io/pLSA.jpeg">
</p>
<h2 id="定义-2">定义</h2>
<p>在形式化定义如上的生成过程之前,我们先定义一些变量:</p>
<ul>
<li>\( p(d_i) \) 表示语料库中某篇文档被选中的概率</li>
<li>\( p(w_j | d_i)\) 表示给定文档 \( d_i\) 时,词 \( w_j\) 出现的概率</li>
<li>\( p(z_k | d_i)\) 表示给定文档 \( d_i\) 时,主题 \( z_k\) 出现的概率</li>
<li>\( p(w_j | z_k)\) 表示主题 \( z_k\) 时,词 \( w_j\) 出现的概率</li>
</ul>
<p>重写以上的生成过程:</p>
<ol>
<li>按照概率 \( p(d) \) 选择一篇文档 \( d_i \) (一般计为等概率的)</li>
<li>按照文档-主题分布 \( p(z | d_i)\) 选择某个主题 \( z_k \)</li>
<li>按照主题-词分布 \( p(w | z_k)\) 选择某个词 \( w_j \)</li>
<li>根据 \( M \) 和 \( N \) 重复以上过程</li>
</ol>
<h2 id="求解">求解</h2>
<p>上一小节我们描述了pLSA的生成过程。通常来说我们拿到的仅仅只有语料库,我们更希望获得的是模型潜在的主题信息,这样就可以使用主题信息去刻画文本和词的关系。由以上公式的定义可知,\( p(w_j | d_i)\) 是已知的(参看TF-IDF的计算方法),我们可以根据大量的已知的文档-词的信息推出文档-主题以及主题-词的信息:</p>
<p>$$
p(w_j| d_i) = \sum_{k=1}^K p(w_j|z_k) p(z_k|d_i)
$$</p>
<p>因此某文档中某个词的生成概率为:</p>
<p>$$
p(w_j, d_i) = p(d_i) p(w_j| d_i)<br />
= p(d_i) \sum_{k=1}^K p(w_j|z_k) p(z_k|d_i)
$$
\(p(d_i)\)可以事先计算出来,而 \( p(w_j|z_k)\) 和 \( p(z_k|d_i)\) 是未知的。因此为了得到主题的信息,必须估计 \( p(w_j|z_k)\) 和 \( p(z_k|d_i)\) 的参数。因为模型含有隐变量,考虑使用EM算法。</p>
<h2 id="em参数估计">EM参数估计</h2>
<p>在介绍EM参数估计之前,我们先列出模型的参数并一一解释。</p>
<ul>
<li>假定 \( p(w_j|z_k)\) 服从多项式分布,该分布的参数 \( \phi_k \) 表示的是主题 \( z_k \) 在词表(一共\( V \) 个词)上面的一个“分布”,即每个词 \( w_j \) 在该参数里都对应一个元素 \( \phi_{k, j} \),代表了 \( w_j \) 出现在主题 \( z_k \) 上的概率:</li>
</ul>
<p>$$
p(w_j|z_k)= \phi_{k, j} ,\sum_{j = 1}^V \phi_{k, j} = 1
$$</p>
<ul>
<li>假定 \( p(z_k|d_i)\) 服从多项式分布,该分布的参数 \( \theta_i \) 表示的是文本 \( d_i \) 在主题(一共 \( K \) 个主题上面的一个“分布”,即每个主题 \( z_k \) 在该参数里都对应一个元素 \( \theta_{i, k} \),代表了主题 \( z_k \) 出现在文档 \( d_i \) 上的概率:</li>
</ul>
<p>$$
p(z_k|d_i)= \theta_{i, k} ,\sum_{k = 1}^K \theta_{i, k} = 1
$$</p>
<p>因此我们要最终求解的两个参数矩阵:</p>
<p>$$
\Phi = [\phi_1, &hellip;, \phi_K]
$$</p>
<p>$$
\Theta = [\theta_1, .., \theta_M]
$$</p>
<p>由于词和词的生成是独立的,文档和文档的生成也是独立的,因此整个语料库的生成词的概率为:</p>
<p>$$
p(W | D) = \prod_{i=1}^M \prod_{j=1}^N p(w_j, d_i) ^{n(d_i, w_j)}
$$
其中,\( {n(d_i, w_j)} \) 表示词 \( w_j \) 在文档 \( d_i \) 出现的次数。于是我们可以得到对数似然函数:</p>
<p>$$
l(\Phi, \Theta) = \sum_{i=1}^M \sum_{j=1}^n n(d_i, w_j) \log p(w_j, d_i)
$$
$$
~~~~~~~~~~~~~~ = \sum_{i=1}^M \sum_{j=1}^n n(d_i, w_j) \log p(w_j, d_i)
$$</p>
<p><a href="https://blog.csdn.net/pipisorry/article/details/42560693">继续请参看</a></p>
<p><a href="https://people.cs.pitt.edu/~milos/courses/cs3750/lectures/class11.pdf">2</a></p>
<p><a href="http://vdisk.weibo.com/s/bjfcErv7QQc">3</a></p>
</description>
</item>
<item>
<title>参数估计</title>
<link>http://chanrom.github.io/post/nlp/estimation/</link>
<pubDate>Thu, 17 May 2018 00:00:00 +0000</pubDate>
<guid>http://chanrom.github.io/post/nlp/estimation/</guid>
<description>
<h1 id="参数估计方法">参数估计方法</h1>
<h2 id="极大似然估计">极大似然估计</h2>
<h2 id="最大后验估计">最大后验估计</h2>
<h2 id="贝叶斯估计">贝叶斯估计</h2>
</description>
</item>
<item>
<title>Backtracking</title>
<link>http://chanrom.github.io/post/leetcode/backtracking/</link>
<pubDate>Fri, 11 May 2018 00:00:00 +0000</pubDate>
<guid>http://chanrom.github.io/post/leetcode/backtracking/</guid>
<description>
<h1 id="回溯">回溯</h1>
<p>看题目看的我头晕,这种嵌套的实在是烧脑子啊。
<a href="https://leetcode.com/problems/combination-sum/discuss/16502/A-general-approach-to-backtracking-questions-in-Java-Subsets-Permutations-Combination-Sum-Palindrome-Partitioning">回溯统一解法</a>)
<a href="https://segmentfault.com/a/1190000006121957">回溯全集</a></p>
</description>
</item>
<item>
<title>Batch Normalization</title>
<link>http://chanrom.github.io/post/dnn/batchnorm/</link>
<pubDate>Thu, 10 May 2018 00:00:00 +0000</pubDate>
<guid>http://chanrom.github.io/post/dnn/batchnorm/</guid>
<description>
<h1 id="介绍">介绍</h1>
<p><code>Batch Normalization</code>是很多深层网络会使用的一种训练<code>trick</code>,这篇博客主要想从谷歌的原始论文和一些大牛的博客出发,重新阐述一下这个训练深层网络的“大杀器”。</p>
<h1 id="the-insights">The Insights</h1>
<p>我们知道深度学习在图像、语言和文本等领域都取得了很大的进步。在这当中,随机梯度下降(<code>SGD</code>)作为一种有效的训练深层网络的方法而被很多人所熟知。在<code>SGD</code>的基础之上的一些改进优化算法,如<code>Adam</code>、<code>Adagrad</code>和<code>NAdam</code>等通常都能将深层网络训练到最佳的性能。</p>
<p>举个例子,假设我们要使用<code>SGD</code>来优化一个含有参数 \( \Theta \) 的网络:
$$
\Theta = argmin_{\Theta} \frac{1}{N} \sum_{i=1}^N l(x_i, \Theta),
$$
每个<code>step</code>使用梯度下降来更新一次 \( \Theta \),最后得到最优的值。每个<code>step</code>可以使用整个数据集来计算一次梯度,也可以使用一个样本来计算一次梯度,但这两种方法都有各自的缺点,所以一般使用的是批处理(<code>mini-batch</code>)随机梯度下降。<strong>其优点在于</strong>:1) \( m \) 个样本的梯度近似于整个数据集的梯度,相对来说梯度比较稳定,并且随着\( m \) 的增大,这种稳定性更好;2) 相比于利用单个样本去计算梯度,批处理的方式更高效。数学上看,<code>mini-batch</code>的方式每次使用 \( m \) 个样本计算一次梯度,其梯度为:
$$
\frac{1}{m} \frac{\partial l(x_i, \Theta)}{\partial \Theta}.
$$</p>
<p>虽然随机梯度下降的方法简单高效,使得训练很深的网络也不在话下,但是想要使得深层网络获得更佳的性能却仍然不是一件容易的事情。实际上,想要深层网络训练的好,模型超参数、学习速率和模型参数的初始化方法的选择都至关重要。这些因素在网络变得更深的时候显得格外重要,因为底层网络参数一丁点的改变都将影响高层网络的输入。</p>
<p>在领域适应(<code>domain adaptation</code>)的研究当中,一个本质的问题就是某个学习系统的输入分布不是固定不变的(如训练样本的分布和测试样本的分布不一致会导致分类器的性能降低),这种情况称之为<code>covariate shift</code>。<code>Covariate shift</code>导致学习系统需要有额外的能力去适应输入分布的变化。结合之前提到的深层网络的训练问题,如果我们把一个深层网络的每一层全部分开来看,<code>covariate shift</code>的问题同样存在。假设某个网络有这样一个计算过程:
$$
l = F_2(F_1(u, \Theta_1), \Theta_2),
$$
\( F \) 指任意的函数变换。当我们学习参数\( \Theta_2\) 的时候,可以将 \( F_2 \) 的输入看做是 \( v = F_1(u, \Theta_1) \):
$$
l = F_2(v, \Theta).
$$
这样,一次梯度下降可以用如下计算方式:
$$
\Theta_2 \leftarrow \Theta_2 - \frac{\alpha}{m} \sum_{i = 1}^m \frac{\partial F_2(v_i, \Theta_2)}{\partial \Theta_2}
$$
所以从上面的角度看待深层网络的训练时我们发现,如果输入 \( v \) 的分布是固定不变的,那么训练含有参数 \(\Theta_2 \) 的高层网络就会变得更加容易,也能训练的更好:在训练的过程中,\(\Theta_2 \) 不需要调整自己去“补偿”因为输入分布变化所带来的不利影响。</p>
<p>前面我们了解到深度网络某一层的输入保持固定不变的分布形式对该层的训练是非常有利的,实际上输入保持固定不变的分布还能为除该层之外的子网络带来益处。考虑深层网络的某一层使用的是<code>sigmoid</code>激活函数:
$$
z = g(W v + b),
$$
其中 \( v \) 是该层的输入,其他为参数。\(g \) 是<code>sigmoid</code>函数。随着 \(|x|\) 的增大,\( g^\prime(x) \) 都是趋近于0的。这就意味着如果\( x = W v + b \) 中某一维度的绝对值如果不能比较小的话,能够传给输入 \( v \) 的梯度就会<code>vanish</code>,进而训练就会变得很慢。但是呢,因为深层网络层数很多,这往往是不可避免的:在不断训练的过程中,梯度要么很大要么很小,所以到头来 \(x \) 还是慢慢进入到非线性函数的<code>saturated regime</code>,使得几乎没有梯度能够往底层的网络传播(<code>ReLU</code>)的设计就是为了缓解非线性激活函数梯度消失的这个问题)。那如果我们使得输入始终稳定为一个较稳定的分布,那么是不是有可能在训练的过程中降低激活函数陷入<code>saturated regime</code>的可能性,使得训练加快呢?</p>
<p>基于以上两点<code>insights</code>,一种可以消除深层网络里<code>Internal Covatiate Shift</code>的机制被提出,那就是<code>Batch Normalization</code>。<code>Batch normalization</code>不仅可以加速网络的训练,而且降低了梯度对量级很小的网络参数以及参数初始化的敏感度。有了<code>Batch normalization</code>之后,我们可以为网络设定一个更高的学习速率。</p>
<h1 id="internal-covariate-shift">Internal Covariate Shift</h1>
</description>
</item>
<item>
<title>Leetcode总结</title>
<link>http://chanrom.github.io/post/leetcode/leetcode/</link>
<pubDate>Sat, 28 Apr 2018 00:00:00 +0000</pubDate>
<guid>http://chanrom.github.io/post/leetcode/leetcode/</guid>
<description>
<h1 id="leetcode">Leetcode</h1>
<p>一直知道自己的算法能力偏弱,但是却没有时间针对性地练习过相关的能力。趁着自己有想找一份暑期实习的计划下开始刷Leetcode,刚开始时,即使是easy的题目也解的比较费力,在刷到100题的时候,解easy题的速度明显快了很多。但是在面对medium难度的题依然要花很长的时间才可以AC。本着融会贯通、不停进步的思想,我将在这个博客记录、总结一下遇到的题目,以便自己以及有兴趣一起学习的同学参考。</p>
<p>目前网络上的Leetcode解题思路不计其数,我们可以随手搜到。但是我想只有在自己脑中形成一个知识体系,才可能在所遇到的新问题得到突破。因此这篇博客会尽量地分门别类地总结类似的题目。</p>
<h1 id="二叉树的遍历">二叉树的遍历</h1>
<p>二叉树的遍历需要熟悉:前序遍历、中序遍历、后序遍历以及层次遍历,包括递归的实现和循环的实现。以下分别给出各个算法的Python实现。</p>
<h2 id="前序遍历-递归">前序遍历-递归</h2>
<p>递归的实现很简单:</p>
<pre><code class="language-python"> def preorderTraversal(self, root):
res = []
def pre(root):
if not root:
return
res.append(root.val)
pre(root.left)
pre(root.right)
pre(root)
return res
</code></pre>
<h2 id="前序遍历-循环">前序遍历-循环</h2>
<p>相比递归实现,循环实现具有更高的效率,其实现需要借助栈来实现:</p>
<pre><code class="language-python"> def preorderTraversal(self, root):
stack = []
res = []
while (root or len(stack) != 0): # 注意这个终止条件
if root:
res.append(root.val)
stack.append(root)
root = root.left
else:
root = stack[len(stack) - 1]
stack.pop()
root = root.right
return res
</code></pre>
<h2 id="中序遍历-递归">中序遍历-递归</h2>
<p>基本同上:</p>
<pre><code class="language-python"> def inorderTraversal(self, root):
res = []
def pre(root):
if not root:
return
pre(root.left)
res.append(root.val)
pre(root.right)
pre(root)
return res
</code></pre>
<h2 id="中序遍历-循环">中序遍历-循环</h2>
<p>中序遍历的循环实现和前序遍历很类似,无非就是(出栈)打印的时机不一样。在代码里的体现就是<code>res.append(root.val)</code>:</p>
<pre><code class="language-python"> def inorderTraversal(self, root):
stack = []
res = []
while (root or len(stack) != 0): # 注意这个终止条件
if root:
stack.append(root)
root = root.left
else:
root = stack[len(stack) - 1]
res.append(root.val)
stack.pop()
root = root.right
return res
</code></pre>
<h2 id="后序遍历-递归">后序遍历-递归</h2>
<p>递归很简单,直接给出代码:</p>
<pre><code class="language-python"> def postorderTraversal(self, root):
res = []
def pre(root):
if not root:
return
pre(root.left)
pre(root.right)
res.append(root.val)
pre(root)
return res
</code></pre>
<h2 id="后序遍历-循环">后序遍历-循环</h2>
<p>相比前两个而言,后序遍历会比较复杂一点,因为在后序遍历中,必须最后打印根节点的值,也就是说必须先打印左子树和右子树的值。在具体的实现当中,我们可以使用一个<code>last</code>指针,表示在上一时刻访问多的节点。当某个节点没有左右子节点或者右子节点已经访问过了(此时左子节点肯定也访问过了),这个节点的值才可以被打印出来。以下代码:</p>
<pre><code class="language-python"> def postorderTraversal(self, root):
stack = []
res = []
last = None
while (root or len(stack) != 0): # 注意这个终止条件
while root: # 一直往左下搜索,使节点入栈
stack.append(root)
root = root.left
root = stack[len(stack) - 1] # 开始分析栈内节点
if (root.right and root.right != last): # 如果当前节点有右子节点并且没被访问过,又要开始对右子树的访问
root = root.right
else: # 不是以上情况的时候就可以输出当前根节点了
res.append(root.val)
stack.pop()
last = root # 记录
root = None # 保证下一次处理栈顶元素
return res
</code></pre>
<h2 id="层序遍历-循环">层序遍历-循环</h2>
<p>第一种层序遍历要求的打印顺序是:根节点先打印叶子节点最后打印的。思路是每遍历完当前层所有的节点的时候,将当前层内节点拥有的子节点装入列表,以备下一步再开始之前的步骤:</p>
<pre><code class="language-python"> def levelOrder(self, root):
if not root:
return []
ans, level = [], [root]
while level:
ans.append([node.val for node in level])
temp = []
for node in level:
temp.extend([node.left, node.right])
level = [leaf for leaf in temp if leaf]
return ans
</code></pre>
<h2 id="层序遍历2-循环">层序遍历2-循环</h2>
<p>第一种层序遍历要求的打印顺序是和之前相反,但这个在Python中不是大问题,直接一条命令就可以<code>res[::-1]</code></p>
<h1 id="字符串翻转">字符串翻转</h1>
<p>字符串的翻转涉及到比较多的题目,比如,从最简单的字符串翻转的概念题到涉及回文串的题。总结一下这类题目对“启后”——在将来的面试中解决新的题目——还是很有帮助的。这里介绍几个这样的题目。</p>
<h2 id="翻转字符串">翻转字符串</h2>
<p>把一个字符串最左侧的若干字符截取下来,保持其顺序,放到原字符串截断后留下的子串前面,这个称之为翻转字符串。举个例子,如果<code>A=&quot;abcde&quot;</code>,那么<code>B=&quot;deabc&quot;</code>是<code>A</code>的一个翻转。如果判断一个字符串是不是另外一字符串的翻转呢?解题的思路就是,如果<code>B</code>是<code>A</code>的一个翻转字符串的话,<code>A</code>必须要在<code>(B + B)[1:-1]</code>中能找到,去掉最后和最前的字符是为了保证<code>A=&quot;aaaa&quot;</code>,<code>B=&quot;aa&quot;</code>的情况是不符合要求的。代码如下:</p>
<pre><code class="language-python"> def rotateString(self, A, B):
if len(A) == 0 and len(B) == 0:
return True
if len(A) == 0:
return False
s = (B + B)[1:-1]
if A not in s:
return False
else:
return True
</code></pre>
<h1 id="排列组合">排列组合</h1>
<p>我这里的排列组合其实指的很宽泛,只要是涉及到计算若干个字符的排列组合、有效排列组合等等都可以归为这一类。把这些题目放到一起,以便总结。</p>
<h2 id="括号生成">括号生成</h2>
<p>给出 n 代表生成括号的对数,请你写出一个函数,使其能够生成所有可能的并且有效的括号组合。例如,给出 n = 3,生成结果为:</p>
<pre><code>[
&quot;((()))&quot;,
&quot;(()())&quot;,
&quot;(())()&quot;,
&quot;()(())&quot;,
&quot;()()()&quot;
]
</code></pre>
<p>我自己想到的方法是通过变换字符串中的<code>)(</code>这种组合。举例来说,串<code>()()()</code>的第一个<code>)(</code>变换成<code>()</code>则可以变成一个有效的括号串。</p>
<pre><code class="language-python"> def generateParenthesis(self, N):
valid_str = set(['()'*n])
stack = ['()'*n]
while len(stack) != 0:
top = stack[len(stack) - 1]
stack.pop()
top = [c for c in top]
for i in xrange(1, len(top) - 1):
if top[i] == ')' and top[i + 1] == '(':
tmp = top[:]
tmp[i], tmp[i + 1] = tmp[i + 1], tmp[i]
if ''.join(tmp) not in valid_str:
valid_str.add(''.join(tmp))
stack.append(''.join(tmp))
return list(valid_str)
</code></pre>
<p>网上很多解法,通常是利用递归进行广度搜索:</p>
<pre><code class="language-python"> def generateParenthesis(self, N):
ans = []
def backtrack(S = '', left = 0, right = 0):
if len(S) == 2 * N:
ans.append(S)
return
if left &lt; N:
backtrack(S+'(', left+1, right)
if right &lt; left:
backtrack(S+')', left, right+1)
backtrack()
return ans
</code></pre>
<h1 id="两个指针">两个指针</h1>
<p>使用两个指针可以解决不少的问题,特别是有关数组的题目上,这里从易到难介绍几道题目(从二分搜索到复杂一点的二分搜索33,34)。</p>
<h1 id="链表问题">链表问题</h1>
<p>链表问题好像在面试中考察的不多,但是相关的题目却比较有技巧性,以下简单的做一些分析。</p>
<h2 id="两个链表的第一个公共节点">两个链表的第一个公共节点</h2>
<p>如果说可以借助其他数据结构的话,这道题目比较好解,比如借助栈来存储两个链表的元素,然后再从栈顶开始一一比对。如果不能借助其他数据结构呢(即空间复杂度为<code>O(1)</code>)?退一步考虑,如果两个链表有交点,那必要条件是从两个链表的末尾开始就相同,然后再往前的某个地方分开。因此,如果我们“对齐”两个链表的长度,从前往后扫描,如果在结束之前有相同节点,说明找到了第一个交点。</p>
<pre><code class="language-python"> def getIntersectionNode(self, headA, headB):
if not headA or not headB:
return None
def count(p):
m = 0
while p:
m += 1
p = p.next
return m
ma = count(headA)
mb = count(headB)
if ma &lt; mb:
ma, mb = mb, ma
headA, headB = headB, headA
n = ma - mb
while n &gt; 0:
headA = headA.next
n = n - 1
while mb &gt; 0:
if headA == headB:
return headA
mb -= 1
headA = headA.next
headB = headB.next
return None
</code></pre>
<h2 id="反转链表">反转链表</h2>
<p>有两种方法,非递归和递归的方法。</p>
<pre><code class="language-python"> def reverseList(self, head):
# 递归
if not head:
return
if not head.next:
return head
root = self.reverseList(head.next)
head.next.next = head
head.next = None
return root
# 以下为非递归
# cur = head
# prev = None
# while cur:
# after = cur.next
# cur.next = prev
# prev = cur
# cur = after
# return prev
</code></pre>
<h2 id="链表中倒数第k个节点">链表中倒数第K个节点</h2>
<p>使用两个指针,第一个指针先走K个节点,然后第二个指针才开始和第一个指针一起走。当第一个指针到达链表尾端,第二个指针指向的节点就是倒数第K个节点。</p>
<h2 id="判断一个单向链表是否形成了环形结构">判断一个单向链表是否形成了环形结构。</h2>
<p>也是定义两个指针,一个快指针一次走一步,一个慢指针一次走两步,如果快指针追赶到了慢指针说明有环,若快指针变成NULL则无环。</p>
<h1 id="快速排序">快速排序</h1>
<p>快速排序是很基本的一种排序算法,在面试中经常被问道,而且很多题目也是基于快速排序的一个变形。这里给出一个基本的实现:
网上很多解法,通常是利用递归进行广度搜索:</p>
<pre><code class="language-python"> def partition(a, l, r):
flag = a[r]
j = l - 1
for i in range(l, r):
if a[i] &lt;= flag:
j += 1
a[i], a[j] = a[j], a[i]
a[j + 1], a[r] = a[r], a[j + 1]
return j + 1
def Quicksort(a, l, r):
if l &lt; r:
q = partition(a, l, r)
Quicksort(a, l, q - 1)
Quicksort(a, q + 1, r)
</code></pre>
</description>
</item>
<item>
<title>Markdown</title>
<link>http://chanrom.github.io/post/mimic/markdown/</link>
<pubDate>Sat, 28 Apr 2018 00:00:00 +0000</pubDate>
<guid>http://chanrom.github.io/post/mimic/markdown/</guid>
<description>
<h1 id="参考网站">参考网站</h1>
<p><a href="https://segmentfault.com/markdown">Markdown 编辑器语法指南</a></p>
<h1 id="代码高亮">代码高亮</h1>
<pre><code class="language-python">python sdf
</code></pre>
<h1 id="标题">标题</h1>
<pre><code class="language-markdown"># 标题
</code></pre>
<h1 id="标题1">标题1</h1>
<pre><code class="language-markdown">标题1
======
</code></pre>
<h2 id="标题2">标题2</h2>
<pre><code class="language-markdown">标题2
-----
</code></pre>
<h2 id="大标题">大标题</h2>
<pre><code class="language-markdown">## 大标题 ##
</code></pre>
<h3 id="小标题">小标题</h3>
<pre><code class="language-markdown">### 小标题 ###
</code></pre>
<h1 id="粗体">粗体</h1>
<p><em>斜体文本</em> <em>斜体文本</em>
<strong>粗体文本</strong> <strong>粗体文本</strong>
<strong><em>粗斜体文本</em></strong> <strong><em>粗斜体文本</em></strong></p>
<pre><code class="language-markdown">*斜体文本* _斜体文本_
**粗体文本** __粗体文本__
***粗斜体文本*** ___粗斜体文本___
</code></pre>
</description>
</item>
<item>
<title>使用Github搭建Hugo博客站</title>
<link>http://chanrom.github.io/post/mimic/how-to/</link>
<pubDate>Wed, 18 Apr 2018 00:00:00 +0000</pubDate>
<guid>http://chanrom.github.io/post/mimic/how-to/</guid>
<description>
<h1 id="为什么要搭建个人博客">为什么要搭建个人博客</h1>
<p>我们在网络上获取别人的经验和知识的时候,通常呈现出来的都是一篇篇页面精致、内容专业的博客,令人耳目一新。有自己的博客,可以分享自己对一些事情、或者专业论文的看法,提高自己的知名度,使得我们在无穷无尽的因特网上有一个向世界展现自己的途径。</p>
<h1 id="准备工作">准备工作</h1>
<p>使用Github+Hugo建立个人博客之前,我们可能具备以下一些基础知识和工作:</p>
<ul>
<li>Git命令</li>
<li>Github账号</li>
</ul>
<h1 id="参考">参考</h1>
<p>目前主要参考的建站博客是<a href="https://keysaim.github.io/post/blog/deploy-hugo-blog-in-github.io/">keysaim</a>,完全按照这个博客的做法就可以搭建成功。</p>
<h1 id="结语">结语</h1>
<p>大家有兴趣可以自己尝试做一个个人博客!</p>
</description>
</item>
</channel>
</rss>