-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathsearch.xml
3790 lines (3683 loc) · 577 KB
/
search.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
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
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
<?xml version="1.0" encoding="utf-8"?>
<search>
<entry>
<title>hello-world</title>
<url>/2019/10/10/hello-world/</url>
<content><![CDATA[<link rel="stylesheet" class="aplayer-secondary-style-marker" href="/assets/css/APlayer.min.css"><script src="/assets/js/APlayer.min.js" class="aplayer-secondary-script-marker"></script><p><strong>Hello World!</strong></p>
<div class="toc">
<!-- toc -->
<ul>
<li><a href="#qi-yin">起因</a></li>
<li><a href="#xuan-xing">选型</a></li>
<li><a href="#chu-zhong">初衷</a></li>
<li><a href="#guan-yu-tou-tu">关于头图</a></li>
</ul>
<!-- tocstop -->
</div>
<h1><span id="qi-yin">起因</span><a href="#qi-yin" class="header-anchor"></a></h1><p>日常的阅读思考需要常常总结记录,偶尔回过头来再看看,尤其近两年论文阅读的越来越多,自己的理解难免浅薄,也需要有一个其他人有可能参与<br>并能一起讨论的地方。之前一直使用印象笔记,但是这个更”personal”,需要一个更”public”的方式。</p>
<h1><span id="xuan-xing">选型</span><a href="#xuan-xing" class="header-anchor"></a></h1><p><strong>私建博客</strong></p>
<p>最初是在阿里云上私建博客,而后因为种种原因,一年后宣告失败 - -!(其实主要是因为思考少,也懒的写~~)为了避免在一个地方摔两次跟头,所以pass</p>
<p><strong>知乎/csdn/简书</strong></p>
<p>由于对csdn实在没好感,pass,知乎上创建了一个专栏,结果需要审核,等了几天(当时在职时应该给自己加点”戏”的233333),最后排除上述答案后,选C。<br>其实以上选项都有一个共同问题:网站出问题了,你的内容就出问题了。</p>
<p><strong>GitPage</strong></p>
<p>简书也不给力,比知乎审查更让人受不了,整个九月都无法正常使用(让我少更多少啊,阻止我进步!!),最后还是需要一个更稳定的,维护成本低的,<br>不需要”审核”的方案,所以选了Hexo + GitPage(主要是这个主题我喜欢~),之前简书上的内容也会陆续迁移过来,简书就当作是临时草稿箱吧~<br>PS.原主题地址:<a href="https://github.com/Mrminfive/hexo-theme-skapp" target="_blank" rel="noopener">https://github.com/Mrminfive/hexo-theme-skapp</a></p>
<h1><span id="chu-zhong">初衷</span><a href="#chu-zhong" class="header-anchor"></a></h1><p>学习上,避免学习中只听不说,只看不写,只学不练的”浅层”学习,通过写的方式,督促自己多总结思考;生活上,记录日常的碎碎念,也记录下当时的一段时光。</p>
<h1><span id="guan-yu-tou-tu">关于头图</span><a href="#guan-yu-tou-tu" class="header-anchor"></a></h1><p>摄于奥森</p>
]]></content>
<categories>
<category>Life</category>
</categories>
</entry>
<entry>
<title>Python中的GIL</title>
<url>/2019/10/13/gil/</url>
<content><![CDATA[<link rel="stylesheet" class="aplayer-secondary-style-marker" href="/assets/css/APlayer.min.css"><script src="/assets/js/APlayer.min.js" class="aplayer-secondary-script-marker"></script><div class="toc">
<!-- toc -->
<ul>
<li><a href="#gil-shi-shi-me">GIL是什么</a></li>
<li><a href="#gil-ji-zhi">GIL机制</a></li>
<li><a href="#dai-lai-de-wen-ti">带来的问题</a></li>
<li><a href="#guan-yu-tou-tu">关于头图</a></li>
</ul>
<!-- tocstop -->
</div>
<h1><span id="gil-shi-shi-me">GIL是什么</span><a href="#gil-shi-shi-me" class="header-anchor"></a></h1><p>熟悉Python的人对GIL肯定都不陌生, 其全称是全局解释器锁(Global Interpreter Lock)。但是,很多人都误以为GIL是python的特性,所以,首先需要明确的一点是GIL并不是Python的特性,它是在实现Python解析器(CPython)时所引入的一个概念。而其他的实现版本如JPython就没有GIL。然而因为CPython是大部分环境下默认的Python执行环境。所以在很多人的概念里CPython就是Python,也就想当然的把GIL归结为Python语言的缺陷。所以这里要先明确一点:GIL并不是Python的特性,Python完全可以不依赖于GIL<br>那么CPython实现中的GIL又是什么呢?官方给出的解释:</p>
<blockquote>
<p>In CPython, the global interpreter lock, or GIL, is a mutex that protects access to Python objects, preventing multiple threads from executing Python bytecodes at once. This lock is necessary mainly because CPython’s memory management is not thread-safe. (However, since the GIL exists, other features have grown to depend on the guarantees that it enforces.)</p>
<p><a href="https://wiki.python.org/moin/GlobalInterpreterLock" target="_blank" rel="noopener">https://wiki.python.org/moin/GlobalInterpreterLock</a></p>
</blockquote>
<p>即原因是在CPython中的线程实际是操作系统的原生线程,而CPython的内存管理不是线程安全的,于是使用GIL这把大锁锁住其他线程,保证同一时刻只有一个线程可以解释执行字节码。</p>
<h1><span id="gil-ji-zhi">GIL机制</span><a href="#gil-ji-zhi" class="header-anchor"></a></h1><p>按照Python社区的想法,操作系统本身的线程调度已经非常成熟稳定了,没有必要自己搞一套。所以Python的线程就是C语言的一个pthread,并通过操作系统调度算法进行调度(例如linux是CFS)。为了让各个线程能够平均利用CPU时间,python会计算当前已执行的微代码数量ticks,达到一定阈值后就强制释放。而这时也会触发一次操作系统的线程调度(当然是否真正进行上下文切换由操作系统自主决定)。<br>Python释放GIL的时机之一:<br>有一个周期性计数的计数器,不断递减,保证Python线程可以在执行一段时间之后释放GIL<br>仔细分析就会发现问题,假如在解析执行字节码的过程中当前线程遇到了一个IO操作,却由于等待数据而被阻塞在该IO操作上,而从GIL的设计来看,GIL只能通过当前线程主动释放,其他线程才有可能获取。而当前<br>线程阻塞在IO操作上时,此时给其他线程运行的机会并没有什么问题,因为GIL只是用来同步线程执行字节码的,并非一般的互斥共享资源的互斥锁。在阻塞操作之前让出GIL,其他线程可以继续执行,而当前线程可以继续执行阻塞型的操作,当该阻塞型的操作完成之后,再次试图获取GIL,继续执行余下的字节码。<br>由此可以看出,Python释放GIL的第二个时机:<br>在IO操作等可能会引起阻塞的system call之前,可以暂时释放GIL,但在执行完毕后,必须重新获取GIL</p>
<h1><span id="dai-lai-de-wen-ti">带来的问题</span><a href="#dai-lai-de-wen-ti" class="header-anchor"></a></h1><p>1.CPU密集型代码(各种循环处理、计数等等),在这种情况下,ticks计数很快就会达到阈值,然后触发GIL的释放与再竞争(多个线程来回切换当然是需要消耗资源的)<br>2.线程的释放与获取是没有间隙的,单核下没什么问题,多核情况下,当当前master线程释放GIL后,其他核心上的线程被唤醒,而此时大部分情况下当前线程又获取了GIL,而被唤醒的线程只能浪费cpu时间而无法运行,直到达到切换时间,切换为待调度状态,这样会造成线程颠簸(thrashing),导致效率更低。<br><img src="/2019/10/13/gil/gil_war.png" alt="Multi GIL War"><br>因此,3开始做了一些优化,如将基于pcode调度改为分时调度;避免释放GIL的线程再次立即被调度等。</p>
<hr>
<p><a href="http://www.dabeaz.com/GIL/" target="_blank" rel="noopener">http://www.dabeaz.com/GIL/</a><br><a href="http://cyrusin.github.io/2016/04/27/python-gil-implementaion/" target="_blank" rel="noopener">http://cyrusin.github.io/2016/04/27/python-gil-implementaion/</a><br><a href="https://zhuanlan.zhihu.com/p/20953544" target="_blank" rel="noopener">https://zhuanlan.zhihu.com/p/20953544</a><br><a href="http://cenalulu.github.io/python/gil-in-python/" target="_blank" rel="noopener">http://cenalulu.github.io/python/gil-in-python/</a></p>
<h1><span id="guan-yu-tou-tu">关于头图</span><a href="#guan-yu-tou-tu" class="header-anchor"></a></h1><p>摄于世园会</p>
]]></content>
<categories>
<category>Programming</category>
</categories>
<tags>
<tag>Python</tag>
</tags>
</entry>
<entry>
<title>python中的多线程</title>
<url>/2019/10/13/multiThreads/</url>
<content><![CDATA[<link rel="stylesheet" class="aplayer-secondary-style-marker" href="/assets/css/APlayer.min.css"><script src="/assets/js/APlayer.min.js" class="aplayer-secondary-script-marker"></script><div class="toc">
<!-- toc -->
<!-- tocstop -->
</div>
<p> 最近在看Python的多线程,经常我们会听到老手说:“Python下多线程是鸡肋,推荐使用多进程!”,但是为什么这么说呢?<br>要知其然,更要知其所以然。所以有了下面的深入研究:<br>首先强调背景:<br><strong>1. GIL是什么?</strong><br> GIL的全称是Global Interpreter Lock(全局解释器锁),来源是python设计之初的考虑,为了数据安全所做的决定。</p>
<p><strong>2. 每个CPU在同一时间只能执行一个线程</strong><br> 在单核CPU下的多线程其实都只是并发,不是并行,并发和并行从宏观上来讲都是同时处理多路请求的概念。但并发和并行又有区别,并行是指两个或者多个事件在同一时刻发生;而并发是指两个或多个事件在同一时间间隔内发生。<br>在Python多线程下,每个线程的执行方式:<br> <strong><strong>获取GIL</strong></strong><br> <strong><strong>执行代码直到sleep或者是python虚拟机将其挂起。</strong></strong><br> <strong><strong>释放GIL</strong></strong></p>
<p> 可见,某个线程想要执行,必须先拿到GIL,我们可以把GIL看作是“通行证”,并且在一个python进程中,GIL只有一个。拿不到通行证的线程,就不允许进入CPU执行。<br>在Python2.x里,GIL的释放逻辑是当前线程遇见IO操作或者ticks计数达到100(ticks可以看作是Python自身的一个计数器,专门作用于GIL,每次释放后归零,这个计数可以通过 sys.setcheckinterval 来调整),进行释放。而每次释放GIL锁,线程进行锁竞争、切换线程,会消耗资源。并且由于GIL锁存在,python里一个进程永远只能同时执行一个线程(拿到GIL的线程才能执行),这就是为什么在多核CPU上,python的多线程效率并不高。那么是不是python的多线程就完全没用了呢?在这里我们进行分类讨论:</p>
<p> <strong>CPU密集型</strong><br> CPU密集型代码包括各种循环处理、计数等等,在这种情况下,由于计算工作多,ticks计数很快就会达到阈值,然后触发GIL的释放与再竞争(多个线程来回切换当然是需要消耗资源的),所以python下的多线程对CPU密集型代码并不友好。</p>
<p> <strong>IO密集型</strong><br> IO密集型代码包括文件处理、网络爬虫等,多线程能够有效提升效率(单线程下有IO操作会进行IO等待,造成不必要的时间浪费,而开启多线程能在线程A等待时,自动切换到线程B,可以不浪费CPU的资源,从而能提升程序执行效率)。所以python的多线程对IO密集型代码比较友好。<br>而在python3.x中,GIL不使用ticks计数,改为使用计时器(执行时间达到阈值后,当前线程释放GIL),这样对CPU密集型程序更加友好,但依然没有解决GIL导致的同一时间只能执行一个线程的问题,所以效率依然不尽如人意。<br>请注意:多核多线程比单核多线程更差,原因是单核下的多线程,每次释放GIL,唤醒的那个线程都能获取到GIL锁,所以能够无缝执行,但多核下,CPU0释放GIL后,其他CPU上的线程都会进行竞争,但GIL可能会马上又被CPU0拿到,导致其他几个CPU上被唤醒后的线程会醒着等待到切换时间后又进入待调度状态,这样会造成线程颠簸(thrashing),导致效率更低。<br>回到最开始的问题:经常我们会听到老手说:“python下想要充分利用多核CPU,就用多进程”,原因是什么呢?<br>原因是:每个进程有各自独立的GIL,互不干扰,这样就可以真正意义上的并行执行,所以在python中,多进程的执行效率优于多线程(仅仅针对多核CPU而言)。<br>所以在这里说结论:多核下,想做并行提升效率,比较通用的方法是使用多进程,能够有效提高执行效率</p>
<p><strong>关于头图</strong><br>摄于首开广场</p>
]]></content>
<categories>
<category>Programming</category>
</categories>
<tags>
<tag>Python</tag>
</tags>
</entry>
<entry>
<title>逻辑回归与最大熵模型</title>
<url>/2019/10/13/logistic/</url>
<content><![CDATA[<link rel="stylesheet" class="aplayer-secondary-style-marker" href="/assets/css/APlayer.min.css"><script src="/assets/js/APlayer.min.js" class="aplayer-secondary-script-marker"></script><p><strong>最大熵是概论模型学习的一个准则。</strong></p>
<div class="toc">
<!-- toc -->
<ul>
<li><a href="#luo-ji-hui-gui">逻辑回归</a><ul>
<li><a href="#luo-ji-hui-gui-fen-bu">逻辑回归分布:</a></li>
<li><a href="#er-xiang-luo-ji-hui-gui-mo-xing">二项逻辑回归模型:</a></li>
<li><a href="#luo-ji-hui-gui-mo-xing-te-dian">逻辑回归模型特点:</a></li>
</ul>
</li>
<li><a href="#zui-da-shang-mo-xing">最大熵模型</a><ul>
<li><a href="#shang">熵</a></li>
<li><a href="#zui-da-shang-mo-xing">最大熵模型:</a></li>
<li><a href="#mo-xing-xue-xi">模型学习:</a></li>
</ul>
</li>
<li><a href="#guan-yu-tou-tu">关于头图</a></li>
</ul>
<!-- tocstop -->
</div>
<h1><span id="luo-ji-hui-gui">逻辑回归</span><a href="#luo-ji-hui-gui" class="header-anchor"></a></h1><h2><span id="luo-ji-hui-gui-fen-bu">逻辑回归分布:</span><a href="#luo-ji-hui-gui-fen-bu" class="header-anchor"></a></h2><p>对于连续随机变量X,X服从逻辑回归分布是指X具有以下分布函数和密度函数:<br><img src="/2019/10/13/logistic/func.jpeg" alt="分布函数与密度函数"></p>
<h2><span id="er-xiang-luo-ji-hui-gui-mo-xing">二项逻辑回归模型:</span><a href="#er-xiang-luo-ji-hui-gui-mo-xing" class="header-anchor"></a></h2><p>二项逻辑回归是一种分类模型,由条件概论分布P(Y|X)表示,其中Y取值为0或1,其条件概论分布为:<br><img src="/2019/10/13/logistic/regression.jpeg" alt="二项逻辑回归条件概率"><br>对于给定的输入x,可以求出P(Y=1|x)和 P(Y=0|x),逻辑回归通过比较两个条件概率的大小,将x分到概率值大的类中。</p>
<h2><span id="luo-ji-hui-gui-mo-xing-te-dian">逻辑回归模型特点:</span><a href="#luo-ji-hui-gui-mo-xing-te-dian" class="header-anchor"></a></h2><p>一个事件的几率(odds)是指该事件发生与不发生的概率比值,如果事件发生的概率是p,则几率为p/(1-p),其对数几率或logit函数是logit(p) = log(p/1-p),对逻辑回归而言:<br> $log(P(Y=1|x)/(1-P(Y=1|x)) = wx$<br>即输出Y=1的对数几率是x的线性函数。</p>
<h1><span id="zui-da-shang-mo-xing">最大熵模型</span><a href="#zui-da-shang-mo-xing" class="header-anchor"></a></h1><h2><span id="shang">熵</span><a href="#shang" class="header-anchor"></a></h2><p>假设离散随机变量X的概率分布是P(X),其熵为:<br><img src="/2019/10/13/logistic/entropy.jpeg" alt="熵"><br>熵满足下列不等式:<br> $0<=H(P)<=log|X|$<br>其中|X|指X的取值个数,当且仅当X当分布是均匀分布时,右边等号成立,即均匀分布时,熵最大。<br>最大熵原理是概率模型学习的一个准则,其认为在学习概率模型时,熵最大的模型是最好的。直观地说,即在选择概率模型时,首先要满足已有的事实,在没有更多信息的情况下,那些不确定的部分是“等可能的”,“等可能”不容易操作,而熵则是一个可以优化的数值指标,通过最大化熵来表示等可能。<br>举例说明,当没有给任何多余信息时,我们猜测抛掷硬币时,每面朝上的概率都是0.5,仍骰子时,每面朝上的概率都是1/6,即没有额外信息时,认为都是等可能是“最合理”。假如有一枚骰子,扔出1和4的概率之和是1/2,则此时我们认为1和4朝上的概率是1/4,而其余四面朝上的概率是1/8,即首先要满足已知信息,没有额外信息时,均匀分布“最合理”。</p>
<h2><span id="zui-da-shang-mo-xing">最大熵模型:</span><a href="#zui-da-shang-mo-xing" class="header-anchor"></a></h2><p><img src="/2019/10/13/logistic/p_entropy.jpeg" alt="条件熵"><br>其中条件熵最大的模型称为最大熵模型</p>
<h2><span id="mo-xing-xue-xi">模型学习:</span><a href="#mo-xing-xue-xi" class="header-anchor"></a></h2><p>最大熵模型的学习可以形式化为约束最优化问题。通过引入拉格朗日乘子将约束最优化问题转化为无约束最优化的对偶问题。</p>
<p>PS:<br>西瓜书上认为对于任意单调可微函数g(),令<br> $(y) = wx + b$<br>这样的模型称为广义线性模型,其中函数g为联系函数。对于二分类任务,其输出为0或1,而线性模型的输出为实数域,需要一个函数将实数域的输出转化到0/1值,最理想的函数是单位阶跃函数,即大于0输出1,小于0输出0,等于0输出0.5,但是该函数不连续,不能用作g(),所以使用sigmoid函数替代。<br>逻辑回归有很多优点,如直接对分类可能性进行建模,无需事先假设数据分布,不仅预测出类别,而是得到近似概率预测。</p>
<p>统计学习方法中给出逻辑回归分布函数,通过引入最大熵模型,求解模型;西瓜书中通过广义线性模型,将sigmoid函数作为连续函数g给出罗辑回归模型,并说明罗辑回归不需要事先假设数据分布。两者都没有明确说明为什么是sigmoid函数,而答案在<a href="http://www.win-vector.com/dfiles/LogisticRegressionMaxEnt.pdf" target="_blank" rel="noopener">http://www.win-vector.com/dfiles/LogisticRegressionMaxEnt.pdf</a> 可以看到,感兴趣的可以读读。</p>
<h1><span id="guan-yu-tou-tu">关于头图</span><a href="#guan-yu-tou-tu" class="header-anchor"></a></h1><p>摄于奥森</p>
]]></content>
<categories>
<category>MachineLearning</category>
</categories>
<tags>
<tag>LR</tag>
</tags>
</entry>
<entry>
<title>神经网络语言模型(NNLM)</title>
<url>/2019/10/13/nnlm/</url>
<content><![CDATA[<link rel="stylesheet" class="aplayer-secondary-style-marker" href="/assets/css/APlayer.min.css"><script src="/assets/js/APlayer.min.js" class="aplayer-secondary-script-marker"></script><div class="toc">
<!-- toc -->
<ul>
<li><a href="#tong-ji-yu-yan-mo-xing">统计语言模型</a></li>
<li><a href="#nnlm">NNLM</a><ul>
<li><a href="#why-it-works">why it works?</a></li>
<li><a href="#bing-xing">并行</a></li>
<li><a href="#out-of-vocabulary-word">out-of-vocabulary word</a></li>
<li><a href="#hou-xu-gong-zuo">后续工作</a></li>
</ul>
</li>
<li><a href="#guan-yu-tou-tu">关于头图</a></li>
</ul>
<!-- tocstop -->
</div>
<h1><span id="tong-ji-yu-yan-mo-xing">统计语言模型</span><a href="#tong-ji-yu-yan-mo-xing" class="header-anchor"></a></h1><p>首先看一个例子:<br><strong>ztc/ 上下/ 齐/ 拼搏/ ,誓为/ 春战/ 做/ 贡献</strong><br>这句话呢通顺,意思明白,那如果换一下词的位置:<br><strong>上下/ 齐/ 拼搏/ ztc/ ,春站/ 做/ 贡献/ 誓为</strong><br>意思含糊了,但是大概意思还是能猜到,那如果在变换一下:<br><strong>拼搏/ 齐/ ztc/ 上下/ ,贡献/ 誓为/ 做/ 春战</strong><br>现在这句话已经不知所云了,如何判断这个由词序组成的序列是否符合文法、含义是否正确?</p>
<p><strong>统计语言模型:一个句子是否合理,就看他的可能性的大小,即他的概率大小。</strong></p>
<p>假设一个句子S,由一连串特定顺序的词$W_1, W_2,…W_T$ 组成,T是句子中词的个数,则S出现的概率$P(S) = P(w_1, w_2,…w_T)$<br>利用条件概率公式展开:<br>$P(w_1,w_2,..w_T) = P(w_1)*P(w_2|w_1)*P(w_3|w_1,w_2)*…*P(w_T|w_1,w_2,..w_{T-1})$<br>即:<br><img src="/2019/10/13/nnlm/p.png" alt><br>当语料中词典大小为100,000,句子平均长度为5时,需要学习的参数大概$100000 * 5 -1$ 个,为了降低计算复杂度,并考虑到词序列中离的更近的词通常在语义上也更相关,所以在计算时可以通过只使用前面<code>n-1</code>个词来近似计算,即<code>n-grams</code>:<br><img src="/2019/10/13/nnlm/ngram.png" alt></p>
<p>n-grams存在的问题:1.泛化时常常有训练语料中没有出现过的词序列;2.没有考虑词之间的相似性。</p>
<h1><span id="nnlm">NNLM</span><a href="#nnlm" class="header-anchor"></a></h1><p><img src="/2019/10/13/nnlm/arc.png" alt="Neural architecture"></p>
<ul>
<li>1.对词库里的每个词指定一个分布的词向量</li>
<li>2.定义联合概率(通过序列中词对应的词向量)</li>
<li>3.学习词向量和概率函数的参数</li>
</ul>
<h2><span id="why-it-works">why it works?</span><a href="#why-it-works" class="header-anchor"></a></h2><p>如果我们已知 “走” 和 “跑” 是相似词,那很容易通过 ”猫在屋里跑“ 推出 “猫在屋里走“,因为相似的词会有相似的词向量,而且概率函数是特征的平滑函数,所以特征的微小变化,只会对概率值产生一个很小的影响。即:1.相似词在特征空间距离更接近;2.概率函数是一个相对平滑的函数,对特征值的变化不是非常敏感。<br>所以训练语料中句子的出现不光增加了自身的概率,也增加了他与周围句子的概率(句子向量空间)<br>目标:$f(w_t ,··· ,w_{t−n+1}) = Pˆ(w_t |w_1,w_2,..w_{t-1} )<br>约束:</p>
<ul>
<li><ol>
<li>$∑ |V| i=1 f(i,wt−1,··· ,wt−n+1) = 1$ </li>
</ol>
</li>
<li>2.$f>0$</li>
</ul>
<p>通过得到的条件概率进行相乘,得到词序列的联合概率.<br>模型被分成二部分:<br>1.<strong>特征映射:通过映射矩阵 C∈R ∣V∣×m</strong><br>将输入的每个词映射为一个特征向量,C(i)∈Rm 表示词典中第 i 个词对应的特征向量,其中 m 表示特征向量的维度。<br>2.<strong>概率函数g</strong><br>通过context中词的词向量来映射下一个词的条件概率。g的输出是一个向量,其中第i个元素表示了字典中第i个词的概率。完整的模型表达如下:<br> $f(i,w_{t−1},··· ,w_{t−n+1}) = g(i,C(wt−1),··· ,C(wt−n+1))$<br>函数f由两个映射(g and c)组成,其中c由所有的上下文共享。<br>训练过程中的参数就由两个映射组成,设 g 对应参数为w,c映射的参数就是自身,则 θ=(c, w)<br>训练过程就是学习θ的最大似然:<br><img src="/2019/10/13/nnlm/l.ong" alt><br>其中R(θ) 是正则项。<br>模型中参数与字典大小V成线性关系,且与n(n-grams)成线性关系,不过可以通过共享结构降低参数数量,如延时神经网络或循环神经网络。<br>实验中,神经网络层只有一个隐层,有一个可选的词向量到输出的直连层,实际上就有两个隐层,一个共享的词向量C 层,该层没有激活函数,还有一个tanh激活函数的隐层;最后的输出层是一个softmax层,来保证所有结果的和为1:<br><img src="/2019/10/13/nnlm/pnew.png" alt></p>
<p>注意:第一层是没有非线性激活函数的,因为非线性激活函数会带来其他信息(联想神经网络中非线性激活函数),而正是这种直接的线性变换,才能让第一层的参数来作为词向量<br>用yi表示每个输出词的对数概率,则<br>$y = b+Wx+U tanh(d +Hx)$<br>其中x是词向量的拼接,$x = (c(wt-1),c(wt-2),c(wt-n+1))$</p>
<h2><span id="bing-xing">并行</span><a href="#bing-xing" class="header-anchor"></a></h2><p>参数与输入的窗口大小和字典的大小成线性,但是计算量却比n-grams 要大很多,首先n-grams中不需要每次都计算所有词的概率,只需要相关词频的线性组合,另外神经网络中主要瓶颈是输出层的激活计算。</p>
<h2><span id="out-of-vocabulary-word">out-of-vocabulary word</span><a href="#out-of-vocabulary-word" class="header-anchor"></a></h2><p>首先根据窗口上下文可能出现的词,进行加权求和初始化新词的词向量,然后将新词 j 加入字典,然后利用这部分数据集重新训练,进行retune.</p>
<h2><span id="hou-xu-gong-zuo">后续工作</span><a href="#hou-xu-gong-zuo" class="header-anchor"></a></h2><ul>
<li>1,分解网络到子网络,如使用词聚类,构建许多小的子网络可能更快更简单</li>
<li>2,用树结构来表达条件概率:神经网络作用在每一个节点上,每个节点代表根据上下问得到该词类的可能性,叶子节点代表词的可能性,这种结构可以将计算复杂度从|v| 降低到 log|v|</li>
<li>3,梯度传播时可以只在部分输出词上进行,如某些条件下最相似的(如三元模型)。如果用在语音识别,可以只计算听觉上相似的词。</li>
<li>4,引入先验知识,如语义信息和语法信息。通过在神经网络结构中共享更多的结构与参数,可以捕获长期的上下文信息,</li>
<li>5,如何解释神经网络得到的词向量</li>
<li>6,上述模型对每个单词分配一个在语义空间的点,所以无法解决一词多义问题。如何扩展当前模型,在语义空间中为词分配多个点来代表词的不同语义。</li>
</ul>
<p>作者提出的后续工作中,目前是很多人的研究方向,一些已经被证明有效。</p>
<ul>
<li>第一个,优化网络结构,提到了从数据方向,构建更多的子网络,还可以直接对网络结构本身进行优化,如word2vec,将神经网络层去掉;</li>
<li>第二个,由于计算瓶颈在计算output的概率(对每个词计算概率,需要softmax归一化),所以提出可以通过树结构,来避免直接对所有词进行计算,如 Hierarchical Softmax</li>
<li>第三个也是在计算输出时,只通过一部分词来进行梯度传播,如负采样</li>
<li>第四个是通过共享结构,来捕获更多上下文信息,如GPT,Bert</li>
<li>第五个是如何解释,也是目前很多人的研究方向</li>
<li>第六个是一次多义的解决方法,如ELMO</li>
</ul>
<hr>
<p>参考:<br><a href="http://www.iro.umontreal.ca/~vincentp/Publications/lm_jmlr.pdf" target="_blank" rel="noopener">http://www.iro.umontreal.ca/~vincentp/Publications/lm_jmlr.pdf</a></p>
<h1><span id="guan-yu-tou-tu">关于头图</span><a href="#guan-yu-tou-tu" class="header-anchor"></a></h1><p>摄于望京soho</p>
]]></content>
<categories>
<category>NLP</category>
</categories>
<tags>
<tag>Language Model</tag>
</tags>
</entry>
<entry>
<title>python中实现单例模式</title>
<url>/2019/10/13/singleton/</url>
<content><![CDATA[<link rel="stylesheet" class="aplayer-secondary-style-marker" href="/assets/css/APlayer.min.css"><script src="/assets/js/APlayer.min.js" class="aplayer-secondary-script-marker"></script><div class="toc">
<!-- toc -->
<!-- tocstop -->
</div>
<p>python中实现单例模式的方式大致有四种:<br>1.模块<br>2.改写类的<strong>new</strong>方法,控制实例生成<br>3.装饰器<br>4.元类</p>
<p>1.模块<br>python中的模块是天然的单例模式,并且是线程安全的,所有的模块只会在运行时加载一次。<br>所以,利用模块就可以实现一个线程安全的单例。如:<br><figure class="highlight python"><table><tr><td class="code"><pre><span class="line"><span class="comment"># my singleton.py</span></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">My_singleton</span><span class="params">(object)</span>:</span></span><br><span class="line"> <span class="function"><span class="keyword">def</span> <span class="title">foo</span><span class="params">(self)</span>:</span></span><br><span class="line"> <span class="keyword">pass</span></span><br><span class="line">my_singleton = My_singleton()</span><br></pre></td></tr></table></figure></p>
<ol start="2">
<li><strong>new</strong>方法<br>python中每个类在实例化对象时,首先会调用<strong>new</strong>方法创建一个实例,然后再调用<strong>init</strong>方法动态绑定其他属性.如:<figure class="highlight python"><table><tr><td class="code"><pre><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">Singleton</span><span class="params">(object)</span>:</span></span><br><span class="line"> _instance = <span class="literal">None</span></span><br><span class="line"> <span class="function"><span class="keyword">def</span> <span class="title">__new__</span><span class="params">(cls, *args, **kw)</span>:</span></span><br><span class="line"> <span class="keyword">if</span> <span class="keyword">not</span> cls._instance:</span><br><span class="line"> cls._instance = super(Singleton, cls).__new__(cls, *args, **kw) </span><br><span class="line"> <span class="keyword">return</span> cls._instance </span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">MyClass</span><span class="params">(Singleton)</span>:</span> </span><br><span class="line"> a = <span class="number">1</span></span><br></pre></td></tr></table></figure>
</li>
</ol>
<p>3.装饰器<br>装饰器可以动态的修改一个类或方法的表现,利用装饰器生成单例<br><figure class="highlight python"><table><tr><td class="code"><pre><span class="line"><span class="keyword">from</span> functools <span class="keyword">import</span> wraps</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">singleton</span><span class="params">(cls)</span>:</span></span><br><span class="line"> instances = {}</span><br><span class="line"></span><br><span class="line"><span class="meta"> @wraps(cls)</span></span><br><span class="line"> <span class="function"><span class="keyword">def</span> <span class="title">getinstance</span><span class="params">(*args, **kw)</span>:</span></span><br><span class="line"> <span class="keyword">if</span> cls <span class="keyword">not</span> <span class="keyword">in</span> instances:</span><br><span class="line"> instances[cls] = cls(*args, **kw)</span><br><span class="line"> <span class="keyword">return</span> instances[cls]</span><br><span class="line"></span><br><span class="line"> <span class="keyword">return</span> getinstance</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="meta">@singleton</span></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">MyClass</span><span class="params">(object)</span>:</span></span><br><span class="line"> a = <span class="number">1</span></span><br></pre></td></tr></table></figure></p>
<p>4.元类<br>元类可以控制其子类的类对象(<strong>new</strong>)及类实例对象(<strong>call</strong>)的创建。<br><figure class="highlight python"><table><tr><td class="code"><pre><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">Singleton</span><span class="params">(type)</span>:</span></span><br><span class="line"> _instances = {}</span><br><span class="line"></span><br><span class="line"> <span class="function"><span class="keyword">def</span> <span class="title">__call__</span><span class="params">(cls, *args, **kwargs)</span>:</span></span><br><span class="line"> <span class="keyword">if</span> cls <span class="keyword">not</span> <span class="keyword">in</span> cls._instances:</span><br><span class="line"> cls._instances[cls] = super(Singleton, cls).__call__(*args, **kwargs)</span><br><span class="line"> <span class="keyword">return</span> cls._instances[cls]</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="comment"># Python2</span></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">MyClass</span><span class="params">(object)</span>:</span></span><br><span class="line"> __metaclass__ = Singleton</span><br><span class="line"></span><br><span class="line"> <span class="comment"># Python3</span></span><br><span class="line"> <span class="comment"># class MyClass(metaclass=Singleton):</span></span><br><span class="line"> <span class="comment"># pass</span></span><br></pre></td></tr></table></figure></p>
<p>最著名的就是Django中的ORM,其model中就使用了元类来控制所有model的表现</p>
<p><strong>关于头图</strong></p>
<p>摄于内蒙古乌兰布统草原</p>
]]></content>
<categories>
<category>Programming</category>
</categories>
<tags>
<tag>Python</tag>
</tags>
</entry>
<entry>
<title>Bagging为什么能降低过拟合</title>
<url>/2019/10/16/bagging/</url>
<content><![CDATA[<link rel="stylesheet" class="aplayer-secondary-style-marker" href="/assets/css/APlayer.min.css"><script src="/assets/js/APlayer.min.js" class="aplayer-secondary-script-marker"></script><div class="toc">
<!-- toc -->
<ul>
<li><a href="#pian-chai-yu-fang-chai">偏差与方差</a></li>
<li><a href="#jiang-di-mo-xing-guo-ni-he">降低模型过拟合</a><ul>
<li><a href="#ji-cheng-mo-xing">集成模型</a></li>
<li><a href="#bagging">Bagging</a></li>
<li><a href="#sui-ji-sen-lin">随机森林</a></li>
</ul>
</li>
<li><a href="#guan-yu-tou-tu">关于头图</a></li>
</ul>
<!-- tocstop -->
</div>
<h1><span id="pian-chai-yu-fang-chai">偏差与方差</span><a href="#pian-chai-yu-fang-chai" class="header-anchor"></a></h1><p><strong>偏差 (bias)</strong><br><img src="/2019/10/16/bagging/fx.png" alt><br>即模型的期望预测与真实值之间的差异。</p>
<p><strong>方差 (variance)</strong><br><img src="/2019/10/156/bagging/var.png" alt><br>方差通常衡量模型对<strong>不同数据集</strong>的敏感程度,也可以认为是衡量模型的不稳定性。若方差大,则表示数据的微小变动就能导致学习出的模型产生较大差异,即对应的模型结构风险更高。</p>
<p>有了偏差和方差的定义,我们就能推导出模型的期望泛化误差:</p>
<p><img src="/2019/10/16/bagging/ed.png" alt><br>如果我们能在保持bias基本不变时,降低variance,则模型的期望泛化误差降低,从而降低模型过拟合风险。</p>
<h1><span id="jiang-di-mo-xing-guo-ni-he">降低模型过拟合</span><a href="#jiang-di-mo-xing-guo-ni-he" class="header-anchor"></a></h1><h2><span id="ji-cheng-mo-xing">集成模型</span><a href="#ji-cheng-mo-xing" class="header-anchor"></a></h2><p>假设我们现在有一个集成模型,其过程为从整体样本中进行采样,得到n份独立且与整体同分布的样本集,然后选择同样的模型进行训练,最后取平均。由于单个模型对应数据同分布,模型相同,则对应的bias和variance相同,而</p>
<p><img src="/2019/10/16/bagging/exi.png" alt></p>
<p>所以最终模型的bias与单模型的bias相同;另一方面,由于各个子模型独立,则</p>
<p><img src="/2019/10/16/bagging/varn.png" alt></p>
<p>此时可以显著降低模型的variance,根据模型泛化误差期望公式,此时的集成模型的期望泛化误差将小于单模型的期望泛化误差,从而降低了模型的过拟合。</p>
<h2><span id="bagging">Bagging</span><a href="#bagging" class="header-anchor"></a></h2><p>针对上述集成模型,当各个子模型相同时,</p>
<p><img src="/2019/10/16/bagging/varxi.png" alt><br>此时不会降低variance。</p>
<p>对应公式:设有n个随机变量,两两变量之间的相关性为𝜌,则方差为</p>
<p><img src="/2019/10/16/bagging/p.png" alt><br>Bagging对样本重采样,对每一重采样得到的子样本集训练一个模型,最后取平均。由于子样本集有相似性,同时也使用同种模型,则各个子模型有相似的bias和variance,由上面结论可知,此时的bias与单模型近似相同,所以bagging不能显著降低bias。(因此在选择模型时,需要选择bias小的模型)子模型介于相同与独立两个极端情况之间,所以对应variance会处于var(x) 与 var(x)/n之间,即通过降低上述公式中的第二项降低整体方差。<br>而根据模型期望泛化误差公式,由于方差的降低,也能带来最终模型的期望泛化误差的降低,从而降低过拟合。</p>
<h2><span id="sui-ji-sen-lin">随机森林</span><a href="#sui-ji-sen-lin" class="header-anchor"></a></h2><p>随机森林是一种常用的Bagging模型,其通过对样本进行有放回的采样,构造n个样本集,同时对特征列进行采样后进行模型训练,即同时降低上述公式中的两项,来降低方差,从而降低过拟合。</p>
<h1><span id="guan-yu-tou-tu">关于头图</span><a href="#guan-yu-tou-tu" class="header-anchor"></a></h1><p>摄于杭州青芝坞</p>
]]></content>
<categories>
<category>MachineLearning</category>
</categories>
<tags>
<tag>Bagging</tag>
</tags>
</entry>
<entry>
<title>Dropout--深度神经网络中的Bagging</title>
<url>/2019/10/17/dropout/</url>
<content><![CDATA[<link rel="stylesheet" class="aplayer-secondary-style-marker" href="/assets/css/APlayer.min.css"><script src="/assets/js/APlayer.min.js" class="aplayer-secondary-script-marker"></script><div class="toc">
<!-- toc -->
<ul>
<li><a href="#shen-du-shen-jing-wang-luo-guo-ni-he-wen-ti">深度神经网络过拟合问题</a></li>
<li><a href="#dropout">Dropout</a></li>
<li><a href="#bu-tong-dropout-fang-an-dui-bi-shi-yan">不同dropout方案对比实验</a></li>
<li><a href="#updating">updating…</a></li>
<li><a href="#guan-yu-tou-tu">关于头图</a></li>
</ul>
<!-- tocstop -->
</div>
<h1><span id="shen-du-shen-jing-wang-luo-guo-ni-he-wen-ti">深度神经网络过拟合问题</span><a href="#shen-du-shen-jing-wang-luo-guo-ni-he-wen-ti" class="header-anchor"></a></h1><p>深度神经网络由于其巨大的参数量,可以很方便的拟合非常复杂的非线性关系,同时,巨大的参数量也给模型带来了过拟合的问题。为了解决这个问题,也有人提出了早停和加入正则项等手段。而在传统机器学习中,除了这些手段,还有一种手段来解决这个问题,即bagging(参考我之前的文章<a href="https://xv44586.github.io/2019/10/16/bagging/">Bagging为什么能降低过拟合</a>),最典型的就是随机森林:对样本和特征进行抽样,训练多个模型,然后进行集成(投票/求平均)。那如何将这种思路引入到深度神经网络中呢?<br>首先,由于训练单个深度神经网络就已经非常耗时,通常样本量也不够多,不适合采样,所以像随机森林一样抽样训练多个模型的方案不可取。<br>剩下的思路就是用“一个”模型来模拟多个sub-model,那如何来模拟呢?</p>
<h1><span id="dropout">Dropout</span><a href="#dropout" class="header-anchor"></a></h1><p>对于深度神经网络,其最重要的部分就是其隐藏单元(神经元),对于一个有n个神经元的层,我们可以通过设置神经元是否激活,来模拟$2^n$ 种结构,即sub-model,而在evaluate阶段,我们将这些所有的sub-mdoel的结果进行求平均。那对于现在的结构,不可能对 $2^n$中结构都去做计算然后再求平均,一种近似的做法是用当前这“一个”模型来近似模拟:设置所有神经元都处于激活状态,同时,对每个神经元的输出乘以其激活概率keep_prob.这就是Dropout的背后思想。<br><img src="/2019/10/17/dropout/dropout.png" alt="Dropout"><br>简单总结Dropout的具体做法:训练阶段,在前向传播的时候,让某个神经元的激活值以一定的概率p停止工作;预测阶段,对每个神经元的输出乘以1-p (keep_prob)。<br>接下来思考一下,为什么这么做work:首先,训练阶段部分神经元失活,对应的结果是部分features不参与计算,本质上是对features进行采样;其次,从整个训练过程中看,每次训练(batch-data),都对应不同的失活神经元(sub-model),对应的每个sub-model在单个epoch内,都是在对样本进行无放回的抽样,本质上是在bagging。最后,在预测阶段,为什么可以用“一个”完整的模型来模拟sub-model的求平均过程呢?从sub-model的角度看,每个sub-model被training的概率为$(1-p)$, 而神经元对所有sub-model是共享的,唯一的区别是是否激活,所以归一到每个神经元上,单个神经元被training(激活)的概率为$(1-p)$,而sub-model的总数是$2^n$,每个神经元求平均的过程即$Out_i * (1-p) * 2^n/ 2^n = Out_i * (1 -p)$.<br>具体实现时,我们的目的是在训练阶段对神经元进行随机(概率p)失活,而在test和evaluate时,对神经元的输出乘以$(1-p)$,所以在实现时,可以采用一个trick:dropout时,对样本进行mask的同时,将其除以$(1-p)$,这样就可以一次计算完成所有逻辑,同时,把所有逻辑保留在整个层中。<br><strong>tf的实现:</strong><br><figure class="highlight python"><table><tr><td class="code"><pre><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">dropout</span><span class="params">(x, keep_prob, noise_shape=None, seed=None, name=None)</span>:</span> <span class="comment"># pylint: disable=invalid-name</span></span><br><span class="line"> <span class="string">"""Computes dropout.</span></span><br><span class="line"><span class="string"></span></span><br><span class="line"><span class="string"> With probability `keep_prob`, outputs the input element scaled up by</span></span><br><span class="line"><span class="string"> `1 / keep_prob`, otherwise outputs `0`. The scaling is so that the expected</span></span><br><span class="line"><span class="string"> sum is unchanged.</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="keyword">with</span> ops.name_scope(name, <span class="string">"dropout"</span>, [x]) <span class="keyword">as</span> name:</span><br><span class="line"> ...</span><br><span class="line"> <span class="comment"># Do nothing if we know keep_prob == 1</span></span><br><span class="line"> <span class="keyword">if</span> tensor_util.constant_value(keep_prob) == <span class="number">1</span>:</span><br><span class="line"> <span class="keyword">return</span> x</span><br><span class="line"></span><br><span class="line"> noise_shape = noise_shape <span class="keyword">if</span> noise_shape <span class="keyword">is</span> <span class="keyword">not</span> <span class="literal">None</span> <span class="keyword">else</span> array_ops.shape(x)</span><br><span class="line"> <span class="comment"># uniform [keep_prob, 1.0 + keep_prob)</span></span><br><span class="line"> random_tensor = keep_prob</span><br><span class="line"> random_tensor += random_ops.random_uniform(noise_shape,</span><br><span class="line"> seed=seed,</span><br><span class="line"> dtype=x.dtype)</span><br><span class="line"> <span class="comment"># 0. if [keep_prob, 1.0) and 1. if [1.0, 1.0 + keep_prob)</span></span><br><span class="line"> binary_tensor = math_ops.floor(random_tensor)</span><br><span class="line"> ret = math_ops.div(x, keep_prob) * binary_tensor</span><br><span class="line"> <span class="keyword">if</span> context.in_graph_mode():</span><br><span class="line"> ret.set_shape(x.get_shape())</span><br><span class="line"> <span class="keyword">return</span> ret</span><br></pre></td></tr></table></figure></p>
<h1><span id="bu-tong-dropout-fang-an-dui-bi-shi-yan">不同dropout方案对比实验</span><a href="#bu-tong-dropout-fang-an-dui-bi-shi-yan" class="header-anchor"></a></h1><p>如果在非dropout阶段不进行scaled会如何?<br>scaled<br><img src="/2019/10/17/dropout/scaled.png" alt="scaled.png"><br>without_scaled<br><img src="/2019/10/17/dropout/without-scaled.png" alt="without_scaled.png"><br>实验也说明非dropout阶段如果没有进行scaled(求平均),对应的loss会比train阶段高,同时acc也会降低。</p>
<p><strong>桥豆麻袋,到这里好像出现了一点问题:</strong><br>按照我们上面的思路,在test和evaluate阶段,我们从对sub-model求平均转化为对每个神经元的output进行scaled down,即 $activation(x * W ) * (1 - p)$, 而我们在实现时,只是对x进行scaled up操作,如果后面接的层的激活函数是线性的,这样处理没有什么问题,但是,后面的层不总是线性激活函数,那此时,$output = activation(x * (1-p) * W) != activation(x*W) * (1 - p)$,即我们得到的输出与我们想要的并不一样,按照以上的理解,我们对output进行scaled-down,验证一下两者的区别。<br>scaled_input<br><img src="/2019/10/17/dropout/scaled-input.png" alt="scaled_input.png"><br>scaled_output<br><img src="/2019/10/17/dropout/scaled-output.png" alt="scaled_output.png"></p>
<p>看上去对output进行scaled-down结果稍微好一点,但是并不显著,此时的激活函数是relu,而且是在倒数第二层,换个激活函数试试。<br>scaled_input_tanh<br><img src="/2019/10/17/dropout/scaled_input_tanh.png" alt="scaled_input_tanh.png"><br>scaled_output_tanh<br><img src="/2019/10/17/dropout/scaled_output_tanh.png" alt="scaled_output_tanh.png"><br>结果看上去对output进行scaled-down效果稍微差一些,但差距不大。<br>代码地址<a href="https://github.com/xv44586/Papers/tree/master/DeepLearning/Dropout" target="_blank" rel="noopener">https://github.com/xv44586/Papers/tree/master/DeepLearning/Dropout</a>,感兴趣的可以试试其他方式。<br>既然两种方式在结果上看,效果差不多,而对output进行scaled-down需要添加一个AfterDropLayer,逻辑会在不同的层中,而对inputs直接进行scaled-up,所有逻辑都保存在一个layer中,更清晰。<br><strong>But,Why?</strong>为什么两种方式的结果效果差异不大?真让人头秃啊!</p>
<p><strong>论文地址</strong><br><a href="http://www.jmlr.org/papers/volume15/srivastava14a/srivastava14a.pdf" target="_blank" rel="noopener">http://www.jmlr.org/papers/volume15/srivastava14a/srivastava14a.pdf</a></p>
<p>================================</p>
<h1><span id="updating">updating…</span><a href="#updating" class="header-anchor"></a></h1><p><img src="/2019/10/17/dropout/linear.png" alt="image.png"></p>
<p>上图是sigmoid函数在[-8,8]区间的图像,其中linear是由[-8,-4,-2,2,4,8]截断的直线,看图可以看出,sigmoid在区间内都非常的接近”linear”,梯度变化较大的部分只在几个拐点周围,大部分都是近似”linear”,所以也就解释了为什么两种方式有差异,但是差异并不大。</p>
<h1><span id="guan-yu-tou-tu">关于头图</span><a href="#guan-yu-tou-tu" class="header-anchor"></a></h1><p>大四竞赛作品截图</p>
]]></content>
<categories>
<category>MachineLearning</category>
</categories>
<tags>
<tag>Deep Learning</tag>
</tags>
</entry>
<entry>
<title>Glove模型</title>
<url>/2019/10/17/glove/</url>
<content><![CDATA[<link rel="stylesheet" class="aplayer-secondary-style-marker" href="/assets/css/APlayer.min.css"><script src="/assets/js/APlayer.min.js" class="aplayer-secondary-script-marker"></script><div class="toc">
<!-- toc -->
<ul>
<li><a href="#yi-zheng-ti-si-lu">一、整体思路</a></li>
<li><a href="#er-ji-ben-jia-she">二、基本假设</a></li>
<li><a href="#san-mo-xing">三、模型</a></li>
<li><a href="#si-dui-bi">四、对比</a></li>
<li><a href="#wu-si-kao">五、思考</a></li>
<li><a href="#zai-si-kao">再思考:</a></li>
<li><a href="#guan-yu-tou-tu">关于头图</a></li>
</ul>
<!-- tocstop -->
</div>
<h1><span id="yi-zheng-ti-si-lu">一、整体思路</span><a href="#yi-zheng-ti-si-lu" class="header-anchor"></a></h1><p>获取词向量基本上有两种思路:</p>
<ul>
<li>1.利用全局统计信息,进行矩阵分解(如LSA)来获取词向量,这样获得的词向量往往在词相似性任务上表现不好,表明这是一个次优的向量空间结构;</li>
<li>2.利用局部上下文窗口单独训练,但是统计信息作为有用的先验知识,没有很好的利用到。 </li>
</ul>
<p>Glove:结合两种训练方式,获取更好的词向量</p>
<h1><span id="er-ji-ben-jia-she">二、基本假设</span><a href="#er-ji-ben-jia-she" class="header-anchor"></a></h1><p>词的共现次数与其语义的相关性往往不是严格成比例,所以直接用共线性来表征词之间相关性效果不好,因此,作者通过引入第三个词,通过词之间的差异来刻画相关性。差异选择用两个词与同一个词的共现概率的次数来更好的判断词之间的相关性。比率:$ratio_{i,j,k}=\frac{Pi,k}{Pj,k} $<br>看下面这个例子:<br><img src="/2019/10/17/glove/table1.png" alt="image.png"><br>ice 与solid相关性高,而steam与solid相关性弱,对应比例大于1;ice与gas相关性弱,steam与gas相关性高,对应比例小于1,ice与steam都与water相关,对应比例约等于1,ice与steam与fashion都不相关,对应比例也是约等于1.<br>相关性的规律:<br><img src="/2019/10/17/glove/ratio.png" alt="image.png"></p>
<h1><span id="san-mo-xing">三、模型</span><a href="#san-mo-xing" class="header-anchor"></a></h1><p>模型的数学形式为:<br><img src="/2019/10/17/glove/1.png" alt><br>其中$w_i$, $w_j$ 与$w_k$分属不同的两个词向量空间(参考skipgram),对于F函数,我们希望他能够在向量空间内预测$p(P_{ik}/P_{jk})$这个比率,由于向量空间的线性结构,最自然的方式就是用向量的差,即:<br><img src="/2019/10/17/glove/2.png" alt><br>等式的右侧是一个标量,左侧F函数可以是一个复杂函数,而我们上面提到我们希望捕捉向量的线性结构,所以避免使用复杂函数,首先将参数做内积:<br><img src="/2019/10/17/glove/3.png" alt><br>在窗口滑动的过程中,中心词与上下文词的角色会相互转化,但是当词的位置互换后,其相关性应该是保持一致的,所以,F函数需要对<code>和</code>操作与<code>商</code>操作上同态(这里同态的意思是F函数在左右两侧应该是一致的,也就是 $F((w_i - w_j))= F(w_i) / F(w_j)$:<br><img src="/2019/10/17/glove/4.png" alt><br>其中:<br><img src="/2019/10/17/glove/5.png" alt></p>
<p>为了解决 上式4,F函数的形式就是exp(指数形式),最终求解后:<br><img src="/2019/10/17/glove/6.png" alt><br>上式中,等式左侧是对称的,即$W_i^T*W_j = W_j^T*W_i$, 而右侧是不对称的,即$log(p_{ij}) != log(P_{ji})$. 如果上式的右侧没有$log(x_i)$,则等式的左右就对称了,考虑到与$k$无关,所以把这一项并入到$i$的偏差项中,即:<br><img src="/2019/10/17/glove/7.png" alt><br>由于上式中有$log$,所以需要处理0值,同时,对于低频与高频的共线词都不能过度训练,于是,优化目标就变成了:<br><img src="/2019/10/17/glove/8.png" alt><br>其中,权重函数$f(x)$需要满足:</p>
<ul>
<li>1, f(0)=0</li>
<li>2, 非减以避免低频共现过度训练</li>
<li>3,抑制高频共现避免过度训练<br>最后采用的$f(x)$ 形式为:<br><img src="/2019/10/17/glove/9.png" alt><br>实验中他们采用的是xmax=100, a=3/4<br><img src="/2019/10/17/glove/scratch.png" alt="完整过程"></li>
</ul>
<h1><span id="si-dui-bi">四、对比</span><a href="#si-dui-bi" class="header-anchor"></a></h1><p>与局部窗口方式对比:<br><img src="/2019/10/17/glove/comp.png" alt="与local window对比"><br>优化目标使用不同的损失函数,并带有调和函数来降低高频词的影响。<br>语义相似性结果对比:<br><img src="/2019/10/17/glove/result.png" alt="不同模型对比"></p>
<h1><span id="wu-si-kao">五、思考</span><a href="#wu-si-kao" class="header-anchor"></a></h1><ul>
<li>1.相对与word2vec, Glove引入了词频统计信息,这是很重要的全局信息。</li>
<li>2.word2vec的训练次数与词频相关,Glove的训练中词频是loss的weight,高频低频词的overweight的情况更低。</li>
<li>3.将基于局部窗口的模型中,相同词进行合并,修改对应object:<img src="/2019/10/17/glove/13.png" alt><br>其中$H$为交叉熵,相对Glove的object:<br><img src="/2019/10/17/glove/16.png" alt><br>loss由交叉熵改为最小二乘,$X_i$改为$f(X_i)$函数进行调和。</li>
<li>4.Glove中的左右词向量也是两个不同的词向量空间,与word2vec一样,虽然Glove模型上看上去可以使用同一个词向量空间做,但是作者说是因为更好优化且模型更稳定,不同的时,最后的结果是左右词向量求和(虽然word2vec也可以这么做)</li>
</ul>
<p>Demo:<a href="https://github.com/xv44586/Papers/blob/master/NLP/WordVector/GloveDemo.ipynb" target="_blank" rel="noopener">https://github.com/xv44586/Papers/blob/master/NLP/WordVector/GloveDemo.ipynb</a></p>
<hr>
<h1><span id="zai-si-kao">再思考:</span><a href="#zai-si-kao" class="header-anchor"></a></h1><ul>
<li>1.通常我们都是根据模型来推导其对应的性质,而Glove是因为其应该具有的性质,来反推模型,这种方式也给人提供了一种新思路。</li>
<li>2.为什么两种模型都有两套词向量空间(中心词向量和上下文词向量)?虽然两个作者都说是因为更好优化且模型更稳定,那有没有更合理的理论上的解释呢?我的理解是:对于word2vec,模型直接对概率$p(w|context)$,如skipgram中,直接对$P(w_2|w_1)$进行建模,而$P(w_2|w_1$)与$P(w_1|w_2)$并不一定相等,所以需要针对词的位置区分,也就是需要两套不一样的词向量空间;而Glove中,如上文中公式(6)所示,模型右侧有一个与位置有关的参数,虽然通过引入两个bias可以一定程度上消除这个位置相关的参数,但是这个参数并不是均匀分布,所以仅通过bias不能完全解决这个问题,而引入两个不同的词向量空间,相当于是引入了位置信息,这样能更好的解决这个问题。其最本质的原因是在窗口滑动过程中,词位置变化的同时信息可能是不对称的,即以a为中心词的窗口中的b在以b为窗口时,a可能丢失。</li>
<li>3.对于上式8,存在一个比较严重的问题,模型为了消去位置相关参数,将其吸收进bias内,而这个bias的引入,就导致了一个严重的问题,即模型不适定。<br><img src="/2019/10/17/glove/ret.png" alt><br>即当你求得一组解后,你可以给这组解加上一个常数向量,其还是一组解。那这个问题就很严重了,你无法评估你得到的解是哪组解。如果加上的是非常大的常数向量,那这组词向量在很多度量上就失去了意义(如余弦距离)<h1><span id="guan-yu-tou-tu">关于头图</span><a href="#guan-yu-tou-tu" class="header-anchor"></a></h1>摄于北京某水库</li>
</ul>
]]></content>
<categories>
<category>NLP</category>
</categories>
<tags>
<tag>Glove</tag>
</tags>
</entry>
<entry>
<title>随机森林模型中是不是树越多越好</title>
<url>/2019/10/17/randomForest/</url>
<content><![CDATA[<link rel="stylesheet" class="aplayer-secondary-style-marker" href="/assets/css/APlayer.min.css"><script src="/assets/js/APlayer.min.js" class="aplayer-secondary-script-marker"></script><div class="toc">
<!-- toc -->
<ul>
<li><a href="#sui-ji-sen-lin-zhong-shu-yue-duo-yue-hao">随机森林中树越多越好?</a></li>
<li><a href="#guan-yu-tou-tu">关于头图</a></li>
</ul>
<!-- tocstop -->
</div>
<h1><span id="sui-ji-sen-lin-zhong-shu-yue-duo-yue-hao">随机森林中树越多越好?</span><a href="#sui-ji-sen-lin-zhong-shu-yue-duo-yue-hao" class="header-anchor"></a></h1><p>面试时被问到在随机森林的树是否数量越多越好?开始只考虑构建更多的树一来浪费资源,二来数量一定后模型的性能基本保持稳定,随着树的增加提升非常小。<br>回来后又想了想,随机森林中通过引入随机抽样和随机抽列,使模型对异常点有更好的鲁棒性,模型的泛化能力更强。如果是无限颗树,那其实会抵消随机性的引入,最后的模型会是一个过拟合的模型,其泛化性能也会降低。此外,噪音较大时,模型也会学习到更多噪音相关的信息,发生过拟合,降低泛化性能。</p>
<h1><span id="guan-yu-tou-tu">关于头图</span><a href="#guan-yu-tou-tu" class="header-anchor"></a></h1><p>摄于玉渊潭公园</p>
]]></content>
<categories>
<category>MachineLearning</category>
</categories>
<tags>
<tag>Random Forest</tag>
</tags>
</entry>
<entry>
<title>深入谈谈word2vec</title>
<url>/2019/10/21/deep-w2v/</url>
<content><![CDATA[<link rel="stylesheet" class="aplayer-secondary-style-marker" href="/assets/css/APlayer.min.css"><script src="/assets/js/APlayer.min.js" class="aplayer-secondary-script-marker"></script><div class="toc">
<!-- toc -->
<ul>
<li><a href="#nnlm-fu-za-du">NNLM复杂度</a></li>
<li><a href="#you-hua-fang-an">优化方案</a><ul>
<li><a href="#1-hierarchical-softmax">1.Hierarchical softmax</a></li>
<li><a href="#2-negative-sampling">2.negative sampling</a></li>
</ul>
</li>
<li><a href="#si-chong-xun-lian-fang-an">四种训练方案</a></li>
<li><a href="#si-kao">思考</a></li>
<li><a href="#hui-da">回答</a></li>
<li><a href="#guan-yu-tou-tu">关于头图</a></li>
</ul>
<!-- tocstop -->
</div>
<h1><span id="nnlm-fu-za-du">NNLM复杂度</span><a href="#nnlm-fu-za-du" class="header-anchor"></a></h1><p>原始的NNLM在训练词向量时非常耗时,尤其是大规模语料上,作者在论文后也提出了可能的优化方案,所以word2vec的关注点就是如果更加有效的在大规模语料上训练词向量。<br>每个训练样本的计算复杂度:<br>$$<br>Q = N * D + N * D * H + H * V<br>$$<br>其中V是词典大小,每个词编码为1-of-V,N是当前序列中当前词的前N个词,D是词向量大小,H是神经网络层中隐层神经元个数。<br>这个$Q$中的主要部分是最后的$H * V$ 部分,但通过一些优化方法可以降低(hs/ng), 所以此时的主要的复杂度来着 $N * D * H$, 所以作者直接将神经网络层去掉,来提高计算效率。<br>作者之前的工作发现成功的训练一个神经网络语言模型可以通过两步进行:1,首先通过一个简单模型训练词向量,2,然后在这之上训练N-gram NNLM。同时,增加当前词后续的词(下文信息)可以得到更好的结果。基于此,提出了两种结构的模型:<br><img src="/2019/10/21/deep-w2v/model.png" alt="CBOW vs Skip-gram"></p>
<p>1.CBOW:与NNLM类似,但是将网络层去掉,同时使用当前词的下文,即通过将上下文窗口内的词投影得到统一向量,然后预测当前词。此时模型内词的顺序不再影响投影结果,计算复杂度为:$Q = N * D + D * log(V)$<br>2.Skip-gram: 与CBOW类似,不过是通过当前此来预测上下文内的词,为了提高效率,在实际工程上对上下文内的词进行了采样,此时的计算复杂度为:$Q = C * (D + D * log(V))$</p>
<h1><span id="you-hua-fang-an">优化方案</span><a href="#you-hua-fang-an" class="header-anchor"></a></h1><p>之前提到原始NNLM中的主要计算复杂度是输出层,即 $H * V$,主要的优化思路是避免全量计算V的概率,作者实现了两种方案,即 Hierarchical Softmax 和negative sampling</p>
<h2><span id="1-hierarchical-softmax">1.Hierarchical softmax</span><a href="#1-hierarchical-softmax" class="header-anchor"></a></h2><p>通过词频构建霍夫曼树,然后将输出层用霍夫曼树替换,上一层结果与每个节点做二分类,判断属于词类,叶子节点为对应的词,判断属于该词的概率</p>
<p><img src="/2019/10/21/deep-w2v/hs.png" alt></p>
<p>特点:高频词的位置更靠近根节点,所需的计算进一步降低。但对于低频词,其对应位置远离根,对应路径长,所需计算量依然很大,效率不高</p>
<h2><span id="2-negative-sampling">2.negative sampling</span><a href="#2-negative-sampling" class="header-anchor"></a></h2><p>在输出层避免对全量字典进行判断,而通过先验知识来圈出最容易混淆的一部分,然后组成负样本(相对于当前词)。作者提出通过词频来归一化后的比例来组成一定比例的候选集,随机的在候选集选取一定数量的负样本(n << V)来组成负样本集,最后的softmax多分类层变成多个sigmod二分类层,来提高计算效率及词向量的质量。</p>
<h1><span id="si-chong-xun-lian-fang-an">四种训练方案</span><a href="#si-chong-xun-lian-fang-an" class="header-anchor"></a></h1><ul>
<li>1.基于hs的CBOW</li>
</ul>
<p><img src="/2019/10/21/deep-w2v/hs-cbow.png" alt><br>其中</p>
<p><img src="/2019/10/21/deep-w2v/xw.png" alt></p>
<p><img src="/2019/10/21/deep-w2v/pw.png" alt></p>
<p><img src="/2019/10/21/deep-w2v/gd.png" alt></p>
<p>对应的伪代码:</p>
<p><img src="/2019/10/21/deep-w2v/code.png" alt></p>
<ul>
<li>2.基于hs的Skip-gram<br><img src="/2019/10/21/deep-w2v/sk.png" alt></li>
</ul>
<p>对应的伪代码:<br><img src="/2019/10/21/deep-w2v/sk-code.png" alt></p>
<ul>
<li>3.基于ng的CBOW</li>
</ul>
<p><img src="/2019/10/21/deep-w2v/ng-cbow.png" alt><br><img src="/2019/10/21/deep-w2v/G.png" alt="image.png"></p>
<p>对应伪代码:</p>
<p><img src="/2019/10/21/deep-w2v/ng-code.png" alt></p>
<ul>
<li>4.基于hs的skip-gram<br><img src="/2019/10/21/deep-w2v/opt.png" alt></li>
</ul>
<p>G中表达式与基于hs的CBOW一样,只是在最外面多了一层求和,后面的过程与CBOW一样。</p>
<h1><span id="si-kao">思考</span><a href="#si-kao" class="header-anchor"></a></h1><ul>
<li>1.词向量的训练过程是一个fake task,我们的目标不是最后的语言模型,而是在这个过程中产生的feature vector,用一个real task 来训练是不是更好?</li>
<li>2.因为是个fake task,那我们如何评估这个task,又如何评估得到的词向量的质量?论文中使用了近似词对及线性平移的特性,有没有更好的方式?</li>
<li>3.词向量的“similarity”具体是什么含义?</li>
</ul>
<h1><span id="hui-da">回答</span><a href="#hui-da" class="header-anchor"></a></h1><ul>
<li>1.用real task来训练一般会得到更好的词向量,但一般下游任务都是在词向量之上构建,所以一般情况是训练一个词向量,然后作为embedding层的初始参数进行下游任务的训练。</li>
<li>2.除了相似词及线性平移性,其他情况下可以通过下游任务的效率来评估。</li>
<li>3.词向量的“similarity”跟通常意义的近义词或相似词有本质上的区别,词向量更多的含义是“同位词”,即上下文相近的词。换个角度,我们将模型的连接函数形式写出来:</li>
</ul>
<p><img src="/2019/10/21/deep-w2v/pwk.png" alt></p>
<p>上式中v分别对应左右两个词向量空间的词向量,由于模型是对称的,所以实际使用时左右两个词向量可以任选一个。</p>
<p>其中<img src="/2019/10/21/deep-w2v/pwi.png" alt></p>
<p>分母是归一化项,暂时忽略,最终最大化$P(w_k|w_i)$的同时,即让 Vwk与 Vwi的内积更大。即模型内隐式的用词向量的内积(方向)来表示词向量直接的距离远近(语义距离),所以可以利用词向量的cosine来寻找语义更接近的词。进一步的,左右两个词向量分属不同的向量空间,最小化两个词的语义距离被转化为最小化两个词在不同语义空间的距离,而不是在同一个向量空间,为什么这种方案可行?原文里提到是因为放在同一个向量空间(同一个矩阵),两个词向量正交在一起,不好优化,,分开放在两个向量空间更利于优化。两个词向量空间为什么可行?我认为主要是因为模型是对称的,虽然两个向量空间不同,但是可以认为是只是经过了旋转缩放,词在向量空间的相对位置没有发生改变(词向量之间的角度)。</p>
<p>优点:</p>
<ul>
<li>1.没有神经网络层,所以没有耗时的矩阵相乘,只保留了一个softmax层,计算效率高。</li>
<li>2.优化时使用的是随机梯度下降,罕见词不会主导优化目标<br>demo:<a href="https://github.com/xv44586/Papers/blob/master/NLP/WordVector/word2vecDemo.ipynb" target="_blank" rel="noopener">https://github.com/xv44586/Papers/blob/master/NLP/WordVector/word2vecDemo.ipynb</a></li>
</ul>
<p><strong>论文</strong><br><a href="https://arxiv.org/pdf/1301.3781.pdf" target="_blank" rel="noopener">https://arxiv.org/pdf/1301.3781.pdf</a><br><a href="https://arxiv.org/abs/1310.4546" target="_blank" rel="noopener">https://arxiv.org/abs/1310.4546</a></p>
<h1><span id="guan-yu-tou-tu">关于头图</span><a href="#guan-yu-tou-tu" class="header-anchor"></a></h1><p>摄于苏州</p>
]]></content>
<categories>
<category>NLP</category>
</categories>
<tags>
<tag>word2vec</tag>
</tags>
</entry>
<entry>
<title>词向量总结</title>
<url>/2019/10/22/w2v-summary/</url>
<content><![CDATA[<link rel="stylesheet" class="aplayer-secondary-style-marker" href="/assets/css/APlayer.min.css"><script src="/assets/js/APlayer.min.js" class="aplayer-secondary-script-marker"></script><div class="toc">
<!-- toc -->
<ul>
<li><a href="#yi-tong">异同</a><ul>
<li><a href="#xiang-tong-dian">相同点</a></li>
<li><a href="#bu-tong-dian">不同点</a></li>
</ul>
</li>
<li><a href="#xing-zhi">性质</a><ul>
<li><a href="#pmi-jiao-du">PMI角度</a></li>
<li><a href="#ke-jia-xing">可加性</a></li>
<li><a href="#mo-chang">模长</a></li>
<li><a href="#xiang-guan-xing">相关性</a></li>
</ul>
</li>
<li><a href="#ying-yong">应用</a><ul>
<li><a href="#liang-ge-ju-zi-de-xiang-guan-xing">两个句子的相关性</a></li>
<li><a href="#zhong-xin-ci-guan-jian-ci-ti-qu">中心词(关键词)提取</a></li>
<li><a href="#ju-xiang-liang">句向量</a></li>
</ul>
</li>
<li><a href="#guan-yu-tou-tu">关于头图</a></li>
</ul>
<!-- tocstop -->
</div>
<h1><span id="yi-tong">异同</span><a href="#yi-tong" class="header-anchor"></a></h1><p>本文主要讨论<code>Glove</code>和<code>word2vec</code>两种模型对应词向量。</p>
<h2><span id="xiang-tong-dian">相同点</span><a href="#xiang-tong-dian" class="header-anchor"></a></h2><ul>
<li>两种模型都是在对词对的<code>PMI</code>做分解,所以他们具有相同的性质(向量可加性,点积,余弦距离,模长等)。</li>
<li>模型的基本形式都是向量的点积,且都有两套词向量空间(单词向量空间与上下文词向量空间)。</li>
</ul>
<h2><span id="bu-tong-dian">不同点</span><a href="#bu-tong-dian" class="header-anchor"></a></h2><ul>
<li>1.通常我们都是根据模型来推导其对应性质,而Glove是通过其性质来反推模型,这种方式还是给人眼前一亮的。</li>
<li>2.除Glove以外的词向量模型都是对条件概率$P(w|context)$进行建模,如<code>word2vec</code>的<code>SkipGram</code>对$P(w_2|w_1)$进行建模,但是这个信息是有缺点的,首先,他不是一个严格对称的模型,即$P(w_2|w_1)$ 与 $P(w_1|w_2)$ 并不一定相等,所以,在建模时需要把上下文与中心词向量区分开,不能放到同一个向量空间;其次,这个概率是有界的、归一化的量,所以在模型里需要用softmax等对结果进行归一化,这个也会造成优化上的困难。而<code>Glove</code>可以看作是对$PMI$进行建模,而$PMI$是比概率更对称也更重要的一个量。</li>
<li>3.如Glove论文中所述,就整体目标函数而言,可以看作是两种模型采用了不同的损失函数,其基本形式是一致的。</li>
<li>4.两种模型都是词袋模型(一元模型)。</li>
<li>5.对应词向量内积含义不同:<br><img src="/2019/10/22/w2v-summary/dot.png" alt="内积含义"></li>
</ul>
<h1><span id="xing-zhi">性质</span><a href="#xing-zhi" class="header-anchor"></a></h1><h2><span id="pmi-jiao-du">PMI角度</span><a href="#pmi-jiao-du" class="header-anchor"></a></h2><p>上文说了两种模型都是对词对的PMI做分解,所以我们在解释其性质时直接从PMI的角度来解释。<br>首先来看一下PMI<br><img src="/2019/10/22/w2v-summary/pmi.png" alt="PMI"><br>对于任意两个词序列Q和A,其中$Q=(q1,q2…qk)$, $A = (a1, a2…al)$,我们模型都是采用的词袋模型,即满足朴素假设:每个特征之间相互独立。<br><img src="/2019/10/22/w2v-summary/pqa.png" alt><br>带入朴素假设<br><img src="/2019/10/22/w2v-summary/naive.png" alt><br>用贝叶斯公式变换<br><img src="/2019/10/22/w2v-summary/change.png" alt><br>再用一次朴素假设<br><img src="/2019/10/22/w2v-summary/naive-change.png" alt></p>
<p>最后得到:<br><img src="/2019/10/22/w2v-summary/pmiqa.png" alt><br>即在朴素假设下,两个序列的互信息等于两个序列中各个项的互信息的总和。</p>
<h2><span id="ke-jia-xing">可加性</span><a href="#ke-jia-xing" class="header-anchor"></a></h2><p>在Glove和word2vec中,两个词之间的相关性是通过对应词向量的内积来表达的,即对于词$W_i$, $W_j$, 其相关性等于$<V_i ,V_j>$, 带入上面,即:<br><img src="/2019/10/22/w2v-summary/pmiin.png" alt><br>即两个词序列的相关性可以通过将两个序列内的词向量求和后再进行点积计算。<br>如我们求两个句子的相关度时,可以先将句子内的词对应的词向量进行求和,然后再进行相似性计算。</p>
<h2><span id="mo-chang">模长</span><a href="#mo-chang" class="header-anchor"></a></h2><p>词向量w的模长正比与其内积<w,w>,即正比PMI(w,w),而在一个滑动窗口内,上下文中的词与中心词相等的概率极低,所以可以认为P(w,w) ~ P(w),推出<br><img src="/2019/10/22/w2v-summary/pww.png" alt><br>即,模长正比与词频的倒数,词频越高(停用词,虚词等),其对应的模长越短,这样就表面模长能在一定程度上代表词本身的重要性。<br>从模型学习的角度来看,词向量的内积等于其模长的乘积乘以余弦值,即<br><img src="/2019/10/22/w2v-summary/cos.png" alt><br>对于高频的几乎没有什么固定搭配的词,其所含语义也相对非常少,即这些词与其他任意词的互信息都非常低,约等于0,而为了让上式等于0,与其不停的调节两个向量的方向,不如让其中一个的模长像0靠近,这样经过多次迭代后,高频的语义少的词的模长就越来越短,逐渐接近0.<br>实验结果中,也能看到按模长排序后,前面的都是高频的语义含量极低的词。<br><img src="/2019/10/22/w2v-summary/sort.png" alt="模长排序结果"><br>可以看到,排在前面的都是高频的语义极少的词(’UNK’,’以及’,’三’,符号等)</p>
<h2><span id="xiang-guan-xing">相关性</span><a href="#xiang-guan-xing" class="header-anchor"></a></h2><p>两个词的互信息正比于词对应向量的内积,即两个词互信息越大,两个词成对出现的几率越高,其对应词向量的内积也就越大,因此,可以通过内积来对词的相关性进行排序。而上面也说了,模长代表了词的重要程度,如果我们不考虑词本身的重要程度,只考虑其词义,可以用向量范数将其归一化后在进行内积计算,这样更稳定.<br><img src="/2019/10/22/w2v-summary/cosij.png" alt><br>即词的相关性可以用词向量之间的余弦距离来计算,这样比只使用内积更稳定。<br>在统计上,互信息为0,则表面这两个词无关,对应到模型,即两个词的词向量的内积为0,而根据向量的知识,两个向量的内积为0,则表明两个向量相互垂直,即两个向量无关。两个词在统计上的无关正好对应其在词向量空间上的几何无关!</p>
<h1><span id="ying-yong">应用</span><a href="#ying-yong" class="header-anchor"></a></h1><h2><span id="liang-ge-ju-zi-de-xiang-guan-xing">两个句子的相关性</span><a href="#liang-ge-ju-zi-de-xiang-guan-xing" class="header-anchor"></a></h2><p>计算两个句子或短语之间的相关性时,我们可以借鉴上面PMI在朴素假设下的性质,将两个句子中的词向量进行求和,再计算两个结果向量之间的相关性,如点积或余弦。<br>而如果一个句子内的词向量的和与某一个词的词向量相关性非常高,可以认为这个句子与这个词表达了相同的语义,或者,在词向量空间内,词对应的向量在句子的聚类中心附近。</p>
<h2><span id="zhong-xin-ci-guan-jian-ci-ti-qu">中心词(关键词)提取</span><a href="#zhong-xin-ci-guan-jian-ci-ti-qu" class="header-anchor"></a></h2><p>所谓中心词(关键词),即能概况句子的意思,通过这些词,我就能大概猜到整个句子的整体内容。即这些词(相对句子内其他词)对整个句子的相关性更高。这样就能将问题转化成词与句子相关性排序问题。<br>通过语言模型的角度来看,语言模型本身就是一个通过上(下)文来预测下一个词概率的模型,即最大化$P(w1,w2,w3..|W)$.而关键词的含义是什么呢?用数学的方式表达就是:<br>对于$S=(w1,w2, w3..wk)$,求解$P(S|wk)$值最高的$wk$,其中$wk$属于$S$。这其实与语言模型的含义是一致的。<br>最终,将问题转化成词与句子之间的相关性排序问题,而上面提到求解两个句子相关性时,可以将句子对应词向量先求和再计算相关性,最后关键词提取就变成:<br>先将句子对应词向量求和,得到sen_vec,然后计算单个词与sen_vec的相关性,然后排序即可。<br><img src="/2019/10/22/w2v-summary/keywords.png" alt><br>可以看到结果还是相当不错的。</p>
<h2><span id="ju-xiang-liang">句向量</span><a href="#ju-xiang-liang" class="header-anchor"></a></h2><p>上面在做句子相关性时,都是为了将计算从$O(n^2)$降低到$O(n)$而将句子内的词向量进行求和,然后再计算。其实也就是用词向量的求和来得到句向量,来作为句子在相同词向量空间的语义。其实这是一种简单又快捷的得到句向量的方式,在很多任务中都可以尝试使用。</p>
<p><strong>refer</strong><br><a href="https://papers.nips.cc/paper/5477-neural-word-embedding-as-implicit-matrix-factorization.pdf" target="_blank" rel="noopener">neural-word-embedding-as-implicit-matrix-factorization</a><br><a href="https://aclweb.org/anthology/P17-1007" target="_blank" rel="noopener">https://aclweb.org/anthology/P17-1007</a></p>
<h1><span id="guan-yu-tou-tu">关于头图</span><a href="#guan-yu-tou-tu" class="header-anchor"></a></h1><p>摄于秦皇岛</p>
]]></content>
<categories>
<category>NLP</category>
</categories>
<tags>
<tag>Glove</tag>
<tag>word2vec</tag>
</tags>
</entry>
<entry>
<title>Word2Vec之skip-gram</title>
<url>/2019/10/13/skipgram/</url>
<content><![CDATA[<link rel="stylesheet" class="aplayer-secondary-style-marker" href="/assets/css/APlayer.min.css"><script src="/assets/js/APlayer.min.js" class="aplayer-secondary-script-marker"></script><div class="toc">
<!-- toc -->
<ul>
<li><a href="#word2vec">Word2Vec</a></li>
<li><a href="#fake-task">Fake Task</a></li>
<li><a href="#train">Train</a></li>
<li><a href="#shu-ru">输入</a></li>
<li><a href="#yin-ceng">隐层</a></li>
<li><a href="#shu-chu-ceng">输出层</a></li>
<li><a href="#guan-yu-tou-tu">关于头图</a></li>
</ul>
<!-- tocstop -->
</div>
<h1><span id="word2vec">Word2Vec</span><a href="#word2vec" class="header-anchor"></a></h1><p><img src="/2019/10/13/skipgram/w2v.jpeg" alt="Word2Vec"><br><code>Word2vec</code> 主要有两种形式,<code>CBOW</code> 和<code>Skip-gram</code>,其中<code>CBOW</code>是通过上下文context来预测当前位置词,<code>SKip-gram</code>则是通过当前词来预测上下文</p>
<h1><span id="fake-task">Fake Task</span><a href="#fake-task" class="header-anchor"></a></h1><p>word2vec 实际上分为两部分,1,建立模型,2,通过模型获取词的嵌入向量(隐层参数)。整个过程与自编码器的思想类似,即基于训练数据训练一个神经网络,模型训练好后,并不会用这个模型处理后续的任务,真正需要的是这个模型学到的参数,如隐层的权重矩阵,基于训练数据建模的过程叫“Fake Task”,意味着建模并不是我们最终的目的。</p>
<h1><span id="train">Train</span><a href="#train" class="header-anchor"></a></h1><p>如何训练我们的神经网络模型?假如我们有一个句子“ The dog barked at the mailman”:</p>
<ul>
<li>首先,我们选择句子中一个词作为我们的input word, 如 dog</li>
<li>然后,我们需要定义一个skip_window参数,来指定上下文的大小,即input word 一侧选取词的数量,假如skip_window=2,那将从dog出发向左右两个方向取最近的两个word,即(the, dog,barked,at),此时的$span = skip_window * 2 + 1 = 5$<br>另一个需要定义的参数是num_skips,即从上下文中选取多少个word来作为output word,这个参数应该小于等于$2 * skip_window$,即最多将所有上下文都作为output,但是不能重复。如设置num_skips = 2,此时从上下文选取2个词作为output,如(the, barked),最终我们将得到两组训练数据(dog, the) (dog, barked)</li>
</ul>
<p>神经网络将基于这些训练数据输出一个概率分布,这个概率分布代表着在输入数据下,词典中每个词是output的概率。如拿数据(dog, barked)来训练,则模型将会告诉我们每个单词是’barked’的概率大小。<br>模型的输出概率代表着词典中每个单词有多大可能性跟input word同时出现。举个栗子,如果我们向神经网络模型中输入一个单词“Soviet“,那么最终模型的输出概率中,像“Union”, ”Russia“这种相关词的概率将远高于像”watermelon“,”kangaroo“非相关词的概率。因为”Union“,”Russia“在文本中更大可能在”Soviet“的窗口中出现。<br>我们将通过给神经网络输入文本中成对的单词来训练它完成上面所说的概率计算。下面的图中给出了一些我们的训练样本的例子。我们选定句子“The quick brown fox jumps over lazy dog”,设定我们的窗口大小为2(window_size = 2),也就是说我们仅选输入词前后各两个词和输入词进行组合。下图中,蓝色代表input word,方框内代表位于窗口内的单词。</p>
<p><img src="/2019/10/13/skipgram/train.png" alt><br>模型将会从每队单词出现的次数中习得统计结果,模型可能会得到更多的(’soviet’, ‘union’)样本对,而(soviet, dog)这样的组合看到的很少。因此,当模型训练完成后,给定一个单词 soviet,输出结果中union 或者russia会比dog有更高的概率。</p>
<h1><span id="shu-ru">输入</span><a href="#shu-ru" class="header-anchor"></a></h1><p>常用做法是用训练文档构建词汇表,然后再对单词进行one-hot编码。编码后的向量,形如$dog = [0, 0, 1, 0, …0]$, 如果词汇表大小为10000, 那这个向量包含了10000的概率,即为当前词为输入的概率<br>下图是神经网络结构:</p>
<p><img src="/2019/10/13/skipgram/arc.png" alt><br>我们基于成对的单词来对神经网络进行训练, 训练样本是(input word, output word)这样的单词对,input word 和 output word都是one-hot编码的向量,最终的模型输出是一个概率分布。</p>
<h1><span id="yin-ceng">隐层</span><a href="#yin-ceng" class="header-anchor"></a></h1><p>如果我们想要用300个特征来表示一个词(即每个词是300维的向量),即隐层有300个神经元,隐层的权重为$10000 * 300$的矩阵,下图中的左右两个图代表了不同角度看隐层权重,左图中每列代表一个10000维的词向量与隐层单个神经元的连接权重,右图每行代表了一个单词的词向量。<br><img src="/2019/10/13/skipgram/weights.png" alt><br>我们最终的目标就是学习这个隐层权重矩阵。<br>输入被one-hot编码后,实际上只有一个位置不为0,所以这个向量相当稀疏,那如果我们将$1*10000$的向量与$10000*300$的矩阵相乘,相当消耗计算资源,为了高效计算,仅仅会选择矩阵中对应的向量中纬度为1的索引行<br><img src="/2019/10/13/skipgram/lookup.png" alt="lookup table"><br>即实际不会进行矩阵乘法计算,而是根据输入向量中不为0 的维度去索引。这样模型中的隐层权重矩阵便成了一个查找表(lookup table),输出就是输入单词的嵌入词向量</p>
<h1><span id="shu-chu-ceng">输出层</span><a href="#shu-chu-ceng" class="header-anchor"></a></h1><p>隐层的输出是一个1*300的向量,而输出层是一个softmax回归分类器,他的每个结点将会输出一个0-1之间的值(概率),而结点的概率之和为1.<br><img src="/2019/10/13/skipgram/softmax.png" alt><br>我们会发现Word2Vec模型是一个超级大的神经网络(权重矩阵规模非常大)。<br>举个栗子,我们拥有10000个单词的词汇表,我们如果想嵌入300维的词向量,那么我们的输入-隐层权重矩阵和隐层-输出层的权重矩阵都会有 10000 x 300 = 300万个权重,在如此庞大的神经网络中进行梯度下降是相当慢的。更糟糕的是,你需要大量的训练数据来调整这些权重并且避免过拟合。百万数量级的权重矩阵和亿万数量级的训练样本意味着训练这个模型将会是个灾难(太凶残了)。<br>Word2Vec的作者在它的第二篇论文中强调了这些问题,下面是作者在第二篇论文中的三个创新:</p>
<ul>
<li><ol>
<li>将常见的单词组合(word pairs)或者词组作为单个“words”来处理。</li>
</ol>
</li>
<li><ol start="2">
<li>对高频次单词进行抽样来减少训练样本的个数。</li>
</ol>
</li>
<li><ol start="3">
<li>对优化目标采用“negative sampling”方法,这样每个训练样本的训练只会更新一小部分的模型权重,从而降低计算负担。<br>事实证明,对常用词抽样并且对优化目标采用“negative sampling”不仅降低了训练过程中的计算负担,还提高了训练的词向量的质量。</li>
</ol>
</li>
</ul>
<p><strong>word pairs and phases</strong><br>一些单词组合的含义和拆开以后具有完全不同的意义,比如 New York,单独的New 和York无法表达这个词组的含义。因此,应该把New York作为一个单独的词组来生成其词向量。<br><strong>对高频词抽样</strong><br>对于高频词,如 the ,按上面的处理方式会有两个问题:</p>
<ul>
<li><ol>
<li>当我们得到成对的单词训练样本时,(dog, the)这样的样本并不会提供更多关于dog的语义信息,因为the 在每个单词的上下文几乎都会出现</li>
</ol>
</li>
<li><ol start="2">
<li>由于the 这样的高频词出现的概率很大,因此为们将会有大量的(the , 。。。)这样的训练样本,而这些样本的数量远远超过我们学习the这个单词所需的训练样本数。</li>
</ol>
</li>
</ul>
<p>如果直接删除掉这些高频词,会有两个问题:</p>
<ul>
<li>1.删除后,the这个单词永远也不会出现在我们的上下文窗口</li>
<li>2.训练样本会减少</li>
</ul>
<p>所以word2vec 采用抽样的方式来解决这种高频词问题。他的基本思想是:对于我们在训练原始文本中遇到的每一个单词,他们都有一定概率被我们从文本中删除掉,而这个被删除的概率与单词的频率有关。<br><img src="/2019/10/13/skipgram/del.png" alt><br>wi 是一个单词,$Z(w_i)$是这个单词在所有预料中出现的频次。$P(w_i)$是被保留的概率。</p>
<ul>
<li>当$Z(w_i) <= 0.0026$时,$P(w_i) = 1.0$。当单词在语料中出现的频率小于0.0026时,它是100%被保留的,这意味着只有那些在语料中出现频率超过0.26%的单词才会被采样。</li>
<li>当$Z(w_i) = 0.00746$ 时,$P(w_i) = 0.5$,意味着这一部分的单词有50%的概率被保留。</li>
<li>当$Z(w_i) = 1.0$ 时,$P(w_i) = 0.033$,意味着这部分单词以3.3%的概率被保留</li>
</ul>
<p><strong>负采样</strong><br>训练一个神经网络意味着要输入训练样本并且不断的调整神经元的权重,不断提高对目标的准确预测。而vocabulary的大小决定了skip-gram神经网络将拥有大规模的权重矩阵,所有的这些权重需要通过我们数以亿计的样本来训练调整,非常消耗计算资源,并且实际中会非常慢。<br>负采样解决了这个问题,不同于原本每个训练样本更新所有权重,负采样每次让一个训练样本仅仅更新一部分权重,减小计算量。<br>对于训练样本(fox,quick),都是经过one-hot编码的,当vocabulary的大小为10000时,我们期望输出对应的quick单词的那个神经元的输出是1,其余9999个都是0,这9999个输出为0的神经元所对应的单词称为negative word<br>隐层-输出层拥有$300 *10000$的权重,而负采样时,我们仅仅更新quick 和我们选择的其他5个negative word的结点对应的权重,共6个神经元,$300* 6 = 1800$ 个权重,相当于只计算了0.06%的权重,计算效率大大提高。<br><img src="/2019/10/13/skipgram/sample.png" alt><br>其中f(wi)代表每个单词出现的频次,p(wi)代表被选中的概率。<br>负采样的C语言实现非常的有趣。unigram table有一个包含了一亿个元素的数组,这个数组是由词汇表中每个单词的索引号填充的,并且这个数组中有重复,也就是说有些单词会出现多次。那么每个单词的索引在这个数组中出现的次数该如何决定呢,由公式$P(w_i) * table_size$,也就是说计算出的负采样概率*1亿=单词在表中出现的次数。<br>有了这张表以后,每次去我们进行负采样时,只需要在0-1亿范围内生成一个随机数,然后选择表中索引号为这个随机数的那个单词作为我们的negative word即可。一个单词的负采样概率越大,那么它在这个表中出现的次数就越多,它被选中的概率就越大。</p>
<h1><span id="guan-yu-tou-tu">关于头图</span><a href="#guan-yu-tou-tu" class="header-anchor"></a></h1><p>摄于内蒙古乌兰布统</p>
]]></content>
<categories>
<category>NLP</category>
</categories>
<tags>
<tag>word2vec</tag>
<tag>skipgram</tag>
</tags>
</entry>
<entry>
<title>Xgboost原理</title>
<url>/2019/10/14/xgb/</url>
<content><![CDATA[<link rel="stylesheet" class="aplayer-secondary-style-marker" href="/assets/css/APlayer.min.css"><script src="/assets/js/APlayer.min.js" class="aplayer-secondary-script-marker"></script><div class="toc">
<!-- toc -->
<ul>
<li><a href="#xu-lun">绪论</a></li>
<li><a href="#suan-fa-yuan-li">算法原理</a><ul>
<li><a href="#xue-xi-mu-biao">学习目标</a><ul>
<li><a href="#jie-dian-hua-fen">节点划分</a></li>
<li><a href="#suo-jian-yu-lie-cai-yang">缩减与列采样</a></li>
<li><a href="#xun-zhao-zui-jia-fen-ge-dian-suan-fa">寻找最佳分割点算法</a></li>
<li><a href="#dai-quan-chong-de-fen-wei-shu-lue-tu-weighted-quantile-sketch-suan-fa">带权重的分位数略图(weighted quantile sketch)算法</a></li>
<li><a href="#xi-shu-zi-gua-ying-fen-ge-ce-lue">稀疏自适应分割策略</a></li>
<li><a href="#xgboost-de-you-que-dian">XGBoost的优缺点</a></li>
</ul>
</li>
</ul>
</li>
<li><a href="#guan-yu-tou-tu">关于头图</a></li>
</ul>
<!-- tocstop -->
</div>
<h1><span id="xu-lun">绪论</span><a href="#xu-lun" class="header-anchor"></a></h1><p>在实际应用的机器学习方法里,<code>GradientTree Boosting (GBDT)</code>是一个在很多应用里都很出彩的技术。XGBoost是一套提升树可扩展的机器学习系统。2015年Kaggle发布的29个获胜方法里有17个用了XGBoost。在这些方案里,有8个仅用了XGBoost,另外的大多数用它结合了神经网络。对比来看,第二流行的方法,深度神经网络,只被用了11次。这个系统的成功性也被KDDCup2015所见证了,前十的队伍都用了XGBoost。此外,据胜出的队伍说,很少有别的集成学习方法效果能超过调好参的XGBoost。<br>主要创新点:</p>
<ul>
<li>设计和构建高度可扩展的端到端提升树系统。 </li>
<li>提出了一个理论上合理的加权分位数略图(weighted quantile sketch )来计算候选集。 </li>
<li>引入了一种新颖的稀疏感知算法用于并行树学习。 令缺失值有默认方向。</li>
<li>提出了一个有效的用于核外树形学习的缓存感知块结构。 用缓存加速寻找排序后被打乱的索引的列数据的过程。</li>
</ul>
<h1><span id="suan-fa-yuan-li">算法原理</span><a href="#suan-fa-yuan-li" class="header-anchor"></a></h1><h2><span id="xue-xi-mu-biao">学习目标</span><a href="#xue-xi-mu-biao" class="header-anchor"></a></h2><p>首先来看下我们是如何预测的:<br>XGBoost是一个树集成模型,他将K(树的个数)个树的结果进行求和,作为最终的预测值。即:<br><img src="/2019/10/14/xgb/target.png" alt><br>假设给定的样本集有n个样本,m个特征,则<br><img src="/2019/10/14/xgb/feature.png" alt><br>其中 xi 表示第i个样本,yi 表示第i个类别标签,回归树(CART树)的空间F为<br><img src="/2019/10/14/xgb/F.png" alt><br>其中q代表每棵树的结构,他将样本映射到对应的叶节点;T是对应树的叶节点个数;f(x)对应树的结构q和叶节点权重w。所以XGBoost的预测值是每棵树对应的叶节点的值的和。<br>我们的目标是学习这k个树,所以我们最小化下面这个带正则项的目标函数:<br><img src="/2019/10/14/xgb/target_n.png" alt><br><img src="/2019/10/14/xgb/text.png" alt><br>上式的第一项是损失误差,如MSE和logistic等,第二项是正则项,控制树的复杂度,防止过拟合。<br>式子2中目标函数的优化参数是模型(functions),不能使用传统的优化方法在欧氏空间优化,但是模型在训练时,是一种加法的方式(additive manner),所以在第t轮,我们将f(t)加入模型,最小化下面的目标函数:<br><img src="/2019/10/14/xgb/min.png" alt><br>训练时,新的一轮加入一个新的f函数,来最大化的降低目标函数,在第t轮,我们的目标函数为 :<br><img src="/2019/10/14/xgb/lt.png" alt><br>接下来我们将目标函数进行泰勒展开,取前三项,移除高阶小无穷小项,最后我们的目标函数转化为:<br><img src="/2019/10/14/xgb/lt_merge.png" alt><br>其中:<br><img src="/2019/10/14/xgb/gi.png" alt><br>接下来我们求解这个目标函数<br><img src="/2019/10/14/xgb/scratch.png" alt></p>
<p>最终我们将关于树模型的迭代转化为关于树的叶子节点的迭代,并求出最优的叶节点分数。将叶节点的最优值带入目标函数,最终目标函数的形式为:<br><img src="/2019/10/14/xgb/ltq.png" alt><br>上式可以作为得分函数用来测量树结构q的质量,他类似与决策树的不纯度得分,只是他通过更广泛的目标函数得到<br><img src="/2019/10/14/xgb/arc.png" alt><br>通过上式我们可以看到,当树结构确定时,树的结构得分只与其一阶倒数和二阶倒数有关,得分越小,说明结构越好。</p>
<h3><span id="jie-dian-hua-fen">节点划分</span><a href="#jie-dian-hua-fen" class="header-anchor"></a></h3><p>而通常情况下,我们无法枚举所有可能的树结构然后选取最优的,所以我们选择用一种贪婪算法来代替:我们从单个叶节点开始,迭代分裂来给树添加节点。节点切分后的损失函数:<br><img src="/2019/10/14/xgb/split.png" alt><br>上式用来评估切分后的损失函数,我们的目标是寻找一个特征及对应的值,使得切分后的loss reduction最大。γ除了控制树的复杂度,另一个作用是作为阈值,只有当分裂后的增益大于γ时,才选择分裂,起到了预剪枝的作用。</p>
<h3><span id="suo-jian-yu-lie-cai-yang">缩减与列采样</span><a href="#suo-jian-yu-lie-cai-yang" class="header-anchor"></a></h3><p>除了在目标函数中引入正则项,为了防止过拟合,XGBoost还引入了缩减(shrinkage)和列抽样(column subsampling),通过在每一步的boosting中引入缩减系数,降低每个树和叶子对结果的影响;列采样是借鉴随机森林中的思想,根据反馈,列采样有时甚至比行抽样效果更好,同时,通过列采样能加速计算。</p>
<h3><span id="xun-zhao-zui-jia-fen-ge-dian-suan-fa">寻找最佳分割点算法</span><a href="#xun-zhao-zui-jia-fen-ge-dian-suan-fa" class="header-anchor"></a></h3><p>树模型学习的一个关键问题是如何寻找最优分割点。第一种方法称为基本精确贪心算法(exact greedy algorithm):枚举所有特征的所有可能划分,寻找最优分割点。该算法要求为连续特征枚举所有可能的切分,这个对计算机要求很高,为了有效做到这一点,XGBoost首先对特征进行排序,然后顺序访问数据,累计loss reduction中的梯度统计量(式6)。<br><img src="/2019/10/14/xgb/max.png" alt><br>上述方法是一个非常精确的分割点算法,但是当数据无法完全加载进内存或分布式的情况下,该算法就不是特别有效了。为了支持这两种场景,提出了一种近似算法:根据特征分布的百分位数,提出n个候选切分点,然后将样本映射到对应的两个相邻的切分点组成的桶中,聚会统计值,通过聚会后的统计值及推荐分割点,计算最佳分割点。该算法有两种形式:全局近似和局部近似,其差别是全局近似是在生成一棵树之前,对各个特征计算其分位点并划分样本;局部近似是在每个节点进行分裂时采用近似算法。近似算法的流程:<br><img src="/2019/10/14/xgb/appr.png" alt></p>
<h3><span id="dai-quan-chong-de-fen-wei-shu-lue-tu-weighted-quantile-sketch-suan-fa">带权重的分位数略图(weighted quantile sketch)算法</span><a href="#dai-quan-chong-de-fen-wei-shu-lue-tu-weighted-quantile-sketch-suan-fa" class="header-anchor"></a></h3><p>在近似算法中重要的一步是寻找候选分割点,通常情况下,特征的百分位数使数据均匀的分布在数据上。现在我们定义一个数据集Dk = {(x1k, h1), (x2k, h2) … }代表样本的第k个特征及其对应的二阶梯度,现在我们定义一个函数rk:<br><img src="/2019/10/14/xgb/rk.png" alt><br>上式代表特征k小于特征z的样本比例,我们的目标是寻找候选分割点{sk1, sk2,…},使它满足:<br><img src="/2019/10/14/xgb/rksk.png" alt><br>其中e是候选因子,即切分的百分位数,所以最后有大约1/e个候选分割点。那为什么可以用二阶倒数h来代替权重呢?我们将目标函数变形为<br><img src="/2019/10/14/xgb/new_target.png" alt><br>上式可以看成是label是gi/hi,权重是hi的平方损失,这对于大数据下寻找划分点非常重要。在以往的分位法中,没有考虑权值,许多存在的近似方法中,或者通过排序或者通过启发式方法(没有理论保证)划分。文章的贡献是提供了理论保证的分布式加权分位法。<br>为什么要对目标函数进行类似的变形?思考一下我们划分分割点的目标是什么?是希望均匀的划分loss,而不同样本对loss的权重不同,不考虑权重直接按样本划分时,loss的分布是不均匀的,对应的分位点就会有偏差。<br>PS:文章中的近似变形感觉不太近似,更近似的变形可能是<img src="/2019/10/14/xgb/trans.png" alt><br>即label是-gi/hi的带权重平方损失。不知道文章内为啥是另一种形式</p>
<h3><span id="xi-shu-zi-gua-ying-fen-ge-ce-lue">稀疏自适应分割策略</span><a href="#xi-shu-zi-gua-ying-fen-ge-ce-lue" class="header-anchor"></a></h3><p>实际情况下避免不了数据稀疏,产生数据稀疏的原因主要有三个:1,数据缺失,2,统计上为0,3,one-hot编码。而适应稀疏数据非常重要。XGBoost提出的是在计算分割后的分数时,遇到缺失值,分别将缺失值带入左右两个分割节点,然后取最大值的方向为其默认方向。<br><img src="/2019/10/14/xgb/gain.png" alt><br>以上就是XGBoost所涉及的算法原理。</p>
<h3><span id="xgboost-de-you-que-dian">XGBoost的优缺点</span><a href="#xgboost-de-you-que-dian" class="header-anchor"></a></h3><p><strong>与GBDT对比</strong></p>
<ul>
<li>1.GBDT的基分类器只支持CART树,而XGBoost支持线性分类器,此时相当于带有L1和L2正则项的逻辑回归(分类问题)和线性回归(回归问题)。</li>
<li>2.GBDT在优化时只使用了一阶倒数,而XGBoost对目标函数进行二阶泰勒展开,此外,XGBoost支持自定义损失函数,只要损失函数二阶可导</li>
<li>3.XGBoost借鉴随机森林算法,支持列抽样和行抽样,这样即能降低过拟合风险,又能降低计算。</li>
<li>4.XGBoost在目标函数中引入了正则项,正则项包括叶节点的个数及叶节点的输出值的L2范数。通过约束树结构,降低模型方差,防止过拟合。</li>
<li>5.XGBoost对缺失值不敏感,能自动学习其分裂方向</li>
<li>6.XGBoost在每一步中引入缩减因子,降低单颗树对结果的影响,让后续模型有更大的优化空间,进一步防止过拟合。</li>
<li>7.XGBoost在训练之前,对数据预先进行排序并保存为block,后续迭代中重复使用,减少计算,同时在计算分割点时,可以并行计算</li>
<li>8.可并行的近似直方图算法,树结点在进行分裂时,需要计算每个节点的增益,若数据量较大,对所有节点的特征进行排序,遍历的得到最优分割点,这种贪心法异常耗时,这时引进近似直方图算法,用于生成高效的分割点,即用分裂后的某种值减去分裂前的某种值,获得增益,为了限制树的增长,引入阈值,当增益大于阈值时,进行分裂;</li>
</ul>
<p><strong>与LightGBM对比</strong></p>
<ul>
<li>1.XGBoost采用预排序,在迭代之前,对结点的特征做预排序,遍历选择最优分割点,数据量大时,贪心法耗时,LightGBM方法采用histogram算法,占用的内存低,数据分割的复杂度更低,但是不能找到最精确的数据分割点。同时,不精确的分割点可以认为是降低过拟合的一种手段。</li>
<li>2.LightGBM借鉴Adaboost的思想,对样本基于梯度采样,然后计算增益,降低了计算</li>
<li>3.LightGBM对列进行合并,降低了计算</li>
<li>4.XGBoost采样level-wise策略进行决策树的生成,同时分裂同一层的节点,采用多线程优化,不容易过拟合,但有些节点分裂增益非常小,没必要进行分割,这就带来了一些不必要的计算;LightGBM采样leaf-wise策略进行树的生成,每次都选择在当前叶子节点中增益最大的节点进行分裂,如此迭代,但是这样容易产生深度很深的树,产生过拟合,所以增加了最大深度的限制,来保证高效的同时防止过拟合。</li>
</ul>
<hr>
<p>参考:<a href="https://arxiv.org/pdf/1603.02754.pdf" target="_blank" rel="noopener">https://arxiv.org/pdf/1603.02754.pdf</a><br><a href="https://www.zhihu.com/question/41354392/answer/98658997" target="_blank" rel="noopener">https://www.zhihu.com/question/41354392/answer/98658997</a></p>
<h1><span id="guan-yu-tou-tu">关于头图</span><a href="#guan-yu-tou-tu" class="header-anchor"></a></h1><p>风格迁移模型效果图</p>
]]></content>
<categories>
<category>MachineLearning</category>
</categories>
<tags>
<tag>Xgboost</tag>
<tag>Boosting</tag>
</tags>
</entry>
<entry>
<title>文档查重之SimHash算法</title>
<url>/2019/10/26/simhash/</url>
<content><![CDATA[<link rel="stylesheet" class="aplayer-secondary-style-marker" href="/assets/css/APlayer.min.css"><script src="/assets/js/APlayer.min.js" class="aplayer-secondary-script-marker"></script><div class="toc">
<!-- toc -->
<ul>
<li><a href="#simhash">SimHash</a></li>
<li><a href="#guan-yu-tou-tu">关于头图</a></li>
</ul>
<!-- tocstop -->
</div>
<h1><span id="simhash">SimHash</span><a href="#simhash" class="header-anchor"></a></h1><p>不同网站间相互转载内容的情况非常常见,即使同一网站,不同的URL地址也可能对应相同内容,只是以不同的形式显示出来(不同的UI),而我们在爬取大量内容时,除了靠URL去重外,还需按文档内容排重<br>指纹可以判断人的身份,比如侦探把从犯罪现场采集的指纹与指纹库中的指纹做个对比,就能确定犯罪嫌疑人的身份。类似的,我们用一个文档的语义指纹来代表文档的语义,如采用一个二进制数组来代表。从而判断文档之间的相似性转化为判断两个语义指纹之间的相似性。<br>SimHash是Google在2007年发表的论文《Detecting Near-Duplicates for Web Crawling 》中提到的一种指纹生成算法或者叫指纹提取算法,被Google广泛应用在亿级的网页去重的Job中,作为locality sensitive hash(局部敏感哈希)的一种,其主要思想是降维,即将一篇若干数量的文本内容用一个长度较短的数组来表示,而这个数组与这篇文档的主要的特征所对应。如在没有犯罪嫌疑人的身份证和指纹时,一个人的特征有无数多个,而我们可以通过调查犯罪嫌疑人的姓名,性别,出生日期,身高,体重,当天穿的衣服,外貌等一些主要特征来甄别嫌疑人的身份。simhash也是将复杂的特征,降维来简化。<br>SimHash计算过程:<br><img src="/2019/10/26/simhash/sim.png" alt="simhash计算流程"></p>
<ul>
<li>1.对文档提取特征及特征对应的权重</li>
<li>2.对特征进行hash,生成对应的hash值</li>
<li>3.hash值加权:对特征hash值的每一位做循环处理:如果该位值为1,则用weight代替,否则,用-weight代替</li>
<li>4.求和:将特征hash加权后的结果,按位求和,然后将结果按位二值化:大于0则为1,否则为0,即得到最后的SimHash值。</li>
</ul>
<p>SimHash的计算依据是要比较的对象的特征,对于结构化的记录,可以按列提取特征;对于非结构化的文档,特征可以用全文提取topk关键词、标题、最长的几句话、每段的首句、尾句等。</p>
<figure class="highlight python"><table><tr><td class="code"><pre><span class="line"></span><br><span class="line"><span class="keyword">import</span> jieba</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> jieba.analyse <span class="keyword">as</span> analyse</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> numpy <span class="keyword">as</span> np</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">SimHash</span><span class="params">(object)</span>:</span></span><br><span class="line"></span><br><span class="line"> <span class="function"><span class="keyword">def</span> <span class="title">__init__</span><span class="params">(self, content, topK=<span class="number">50</span>)</span>:</span></span><br><span class="line"></span><br><span class="line"> self.topK = topK</span><br><span class="line"></span><br><span class="line"> self.simhash = self.getSimHash(content)</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="keyword">def</span> <span class="title">getSimHash</span><span class="params">(self, content)</span>:</span></span><br><span class="line"></span><br><span class="line"> seg = jieba.cut(content)</span><br><span class="line"></span><br><span class="line"><span class="comment"># jieba.analyse.set_stop_words('stopword.txt')</span></span><br><span class="line"></span><br><span class="line"> <span class="comment">#topk words and it's tf/idf</span></span><br><span class="line"></span><br><span class="line"> keyWords = jieba.analyse.extract_tags(</span><br><span class="line"></span><br><span class="line"> <span class="string">'|'</span>.join(seg), topK=self.topK, withWeight=<span class="literal">True</span>, allowPOS=())</span><br><span class="line"></span><br><span class="line"><span class="comment"># print(keyWords)</span></span><br><span class="line"></span><br><span class="line"> word_list = []</span><br><span class="line"></span><br><span class="line"> <span class="keyword">for</span> feature, weight <span class="keyword">in</span> keyWords:</span><br><span class="line"></span><br><span class="line"> feature = self.string_hash(feature)</span><br><span class="line"></span><br><span class="line"> temp = []</span><br><span class="line"></span><br><span class="line"> <span class="keyword">for</span> i <span class="keyword">in</span> feature:</span><br><span class="line"></span><br><span class="line"> <span class="keyword">if</span> i == <span class="string">'1'</span>:</span><br><span class="line"></span><br><span class="line"> temp.append(weight)</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"> temp.append(-weight)</span><br><span class="line"></span><br><span class="line"> word_list.append(temp)</span><br><span class="line"></span><br><span class="line"> hashSum = np.sum(np.array(word_list), axis=<span class="number">0</span>)</span><br><span class="line"></span><br><span class="line"> simhash = <span class="string">''</span></span><br><span class="line"></span><br><span class="line"> <span class="keyword">for</span> code <span class="keyword">in</span> hashSum:</span><br><span class="line"></span><br><span class="line"> <span class="keyword">if</span> code > <span class="number">0</span>:</span><br><span class="line"></span><br><span class="line"> simhash += <span class="string">'1'</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"> simhash += <span class="string">'0'</span></span><br><span class="line"></span><br><span class="line"> <span class="keyword">return</span> simhash</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="keyword">def</span> <span class="title">string_hash</span><span class="params">(self,source)</span>:</span></span><br><span class="line"></span><br><span class="line"> <span class="keyword">if</span> source == <span class="string">""</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 class="keyword">else</span>:</span><br><span class="line"></span><br><span class="line"> x = ord(source[<span class="number">0</span>]) << <span class="number">7</span></span><br><span class="line"></span><br><span class="line"> m = <span class="number">1000003</span></span><br><span class="line"></span><br><span class="line"> mask = <span class="number">2</span> ** <span class="number">128</span> - <span class="number">1</span></span><br><span class="line"></span><br><span class="line"> <span class="keyword">for</span> c <span class="keyword">in</span> source:</span><br><span class="line"></span><br><span class="line"> x = ((x * m) ^ ord(c)) & mask</span><br><span class="line"></span><br><span class="line"> x ^= len(source)</span><br><span class="line"></span><br><span class="line"> <span class="keyword">if</span> x == <span class="number">-1</span>:</span><br><span class="line"></span><br><span class="line"> x = <span class="number">-2</span></span><br><span class="line"></span><br><span class="line"> x = bin(x).replace(<span class="string">'0b'</span>, <span class="string">''</span>).zfill(<span class="number">64</span>)[<span class="number">-64</span>:]</span><br><span class="line"></span><br><span class="line"><span class="comment"># print(source,x)</span></span><br><span class="line"></span><br><span class="line"> <span class="keyword">return</span> x</span><br></pre></td></tr></table></figure>
<p><strong>海明距离</strong></p>
<p>得到文档的SimHash值后,我们还需要判断两个文档是否相似。对相同长度的数字序列,我们采用海明距离来衡量其相似性。海明距离是指两个码字对应比特位(数字序列对应位置)不同的比特位个数。如1011101和1001001的第三位和第五位有差别,所以对应的海明距离为2。<br>计算两个数的海明距离时,我们先把两个数按位异或(XOR),然后计算结果中1的个数,结果就是海明距离。</p>
<figure class="highlight python"><table><tr><td class="code"><pre><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">hamDis</span><span class="params">(l1, l2)</span>:</span></span><br><span class="line"></span><br><span class="line"> <span class="comment">#异或</span></span><br><span class="line"></span><br><span class="line"> lxor = int(l1,<span class="number">2</span>) ^ int(l2,<span class="number">2</span>)</span><br><span class="line"></span><br><span class="line"> c = <span class="number">0</span></span><br><span class="line"></span><br><span class="line"> <span class="comment">#计算异或结果1的个数</span></span><br><span class="line"></span><br><span class="line"> <span class="keyword">while</span>(lxor):</span><br><span class="line"></span><br><span class="line"> lxor &= lxor<span class="number">-1</span></span><br><span class="line"></span><br><span class="line"> c += <span class="number">1</span></span><br><span class="line"></span><br><span class="line"> <span class="keyword">return</span> c</span><br></pre></td></tr></table></figure>
<p>把文档转化成SimHash后,文档的排重就变成了海明距离计算问题:给出一个f位的语义指纹集合F和一个语义指纹fg,找出F中是否存在与fg只有k位差异的语义指纹。<br>当k值很小而要找的语义指纹集合中的元素不多时,可以用逐次探查法:先把所有和当前指纹差k位的指纹扩展出来,然后用折半查找法在排好序的指纹集合中查找;<br>如果是面对的是海量的数据,且动态的增加,逐次探查法的效率将越来越慢。当k值较小,如不大于3时,我们使用一种快速方法。首先,我们将64位分成4份,当k为3时,则有一份中两者相等。<br><img src="/2019/10/26/simhash/match.png" alt><br>所以我们在存储时,将数据扩展为4份,每份以其中16位为k,剩余的部分为v,查找时精确匹配这16位。<br><img src="/2019/10/26/simhash/search.png" alt><br>除此之外,对于一个已经排序的容量为$2^d$的f位指纹集合,由于指纹集合中有很多的位组合存在,所以高d位只有少量重复存在,所以在搜索时,也可以找出高d位与当前指纹相同的集合f‘,缩小查找份范围。</p>
<p>Simhash算法对长文本500字+比较适用,短文本可能偏差较大,最后使用海明距离,求相似,在google的论文给出的数据中,64位的签名,在海明距离为3的情况下,可认为两篇文档是相似的或者是重复的,当然这个值只是参考值,针对自己的应用可以自测取值。</p>
<h1><span id="guan-yu-tou-tu">关于头图</span><a href="#guan-yu-tou-tu" class="header-anchor"></a></h1><p>摄于河南老家冬雪</p>
]]></content>
<categories>
<category>NLP</category>
</categories>
<tags>
<tag>Words Distance</tag>
</tags>
</entry>
<entry>
<title>天猫双十一销售额相关思考</title>
<url>/2019/11/24/tm-1111/</url>
<content><![CDATA[<link rel="stylesheet" class="aplayer-secondary-style-marker" href="/assets/css/APlayer.min.css"><script src="/assets/js/APlayer.min.js" class="aplayer-secondary-script-marker"></script><div class="toc">
<!-- toc -->
<ul>
<li><a href="#qi-yin">起因</a></li>
<li><a href="#shi-yan">实验</a></li>
<li><a href="#shi-yan-xiao-jie">实验小结</a></li>
<li><a href="#shi-me-shi-guo-ni-he">什么是过拟合</a></li>
<li><a href="#tong-su-de-jie-shi">通俗的解释</a></li>
<li><a href="#zhi-shi-zu-zhou">知识诅咒</a></li>
<li><a href="#guan-yu-tou-tu">关于头图</a></li>
</ul>
<!-- tocstop -->
</div>
<h1><span id="qi-yin">起因</span><a href="#qi-yin" class="header-anchor"></a></h1><p>最近双十一,各大电商平台造势,宣传自己当天平台销售额,而有个网友爆料天猫销售额造假,因为自己在几个月前就已经成功预测了今年双十一的销售额,并给出了自己的模型参数(公式)。<br><a href="/2019/11/24/tm-1111/wb.jpeg">2019年11月12日网民认为尹立庆神推算的微博</a></p>
<h1><span id="shi-yan">实验</span><a href="#shi-yan" class="header-anchor"></a></h1><p>首先,我们按照楼主的思路做一个的实验。<br><figure class="highlight python"><table><tr><td class="code"><pre><span class="line"><span class="keyword">from</span> sklearn.preprocessing <span class="keyword">import</span> PolynomialFeatures</span><br><span class="line"><span class="keyword">from</span> sklearn.linear_model <span class="keyword">import</span> LinearRegression</span><br><span class="line"><span class="keyword">import</span> matplotlib.pyplot <span class="keyword">as</span> plt </span><br><span class="line"><span class="keyword">import</span> numpy <span class="keyword">as</span> np</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">tm_2009_2019 = [<span class="number">0.50</span>, <span class="number">9.36</span>, <span class="number">52.00</span>, <span class="number">191.00</span>, <span class="number">350.00</span>, <span class="number">571.00</span>, <span class="number">912.00</span>, <span class="number">1207.00</span>, <span class="number">1682.69</span>, <span class="number">2135.00</span>]</span><br><span class="line">year = list(range(<span class="number">2009</span>, <span class="number">2019</span>))</span><br><span class="line">year = list(map(<span class="keyword">lambda</span> x: x<span class="number">-2009</span>, year))</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">sse</span><span class="params">(y_pre, y_true)</span>:</span></span><br><span class="line"> se = (y_pre - y_true) ** <span class="number">2</span></span><br><span class="line"> <span class="keyword">return</span> se.sum()</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">ssr</span><span class="params">(y_pre, y_true)</span>:</span></span><br><span class="line"> sr = (y_pre - y_true.mean()) ** <span class="number">2</span></span><br><span class="line"> <span class="keyword">return</span> sr.sum()</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">R</span><span class="params">(y_pre, y_true)</span>:</span></span><br><span class="line"> se = sse(y_pre, y_true) </span><br><span class="line"> sr = ssr(y_pre, y_true)</span><br><span class="line"> <span class="keyword">return</span> sr/(se+sr)</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">build_model</span><span class="params">(x, y, degree=<span class="number">3</span>)</span>:</span></span><br><span class="line"> x = np.array(x).reshape(<span class="number">-1</span>,<span class="number">1</span>)</span><br><span class="line"> y = np.array(y).reshape(<span class="number">-1</span>,<span class="number">1</span>)</span><br><span class="line"> pf = PolynomialFeatures(degree=degree)</span><br><span class="line"> X = pf.fit_transform(x)</span><br><span class="line"> linear_reg = LinearRegression()</span><br><span class="line"> linear_reg.fit(X, y)</span><br><span class="line"> y_pre = linear_reg.predict(X)</span><br><span class="line"> print(<span class="string">'R is: '</span>, R(y_pre, y))</span><br><span class="line"> <span class="keyword">return</span> linear_reg, pf</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">predict</span><span class="params">(model, pf, test_x)</span>:</span></span><br><span class="line"> test_X = pf.transform(np.array(test_x).reshape(<span class="number">-1</span>,<span class="number">1</span>))</span><br><span class="line"> <span class="keyword">return</span> model.predict(test_X)</span><br></pre></td></tr></table></figure></p>
<p><img src="/2019/11/24/tm-1111/r0.png" alt="模型结果"><br>可以看到,此时的拟合优度R高达0.999,而预测今年的值为2689,确实也与今年的实际值2684基本吻合,所以网友认定自己成功的发现了天猫双十一的销售额”模型”。<br>接下来,我们做几个不一样的实验来看看其结果。<br><strong>实验1</strong><br>这次我们将数据修改一下:将第三年和倒数第三年的数据都减少10%,其他参数不变,看看模型效果。<br><img src="/2019/11/24/tm-1111/lower_9.png" alt="修改数据后模型结果"><br>拟合优度0.996,预测值为2642,与今年实际值也基本吻合。</p>
<p><strong>实验2</strong><br>这次我们不再使用全部数据,而只用2013-2018这六年的数据,其他不变,来看看我们的模型效果如何。<br><img src="/2019/11/24/tm-1111/r6.png" alt="六年数据模型结果"><br>拟合优度也高达0.997,而且预测值2672也与今年的实际值基本吻合。</p>
<p><strong>实验3</strong><br>这次我们不再使用三次,而换为二次线性方程来拟合,仍然使用原始的十年数据。<br><img src="/2019/11/24/tm-1111/degree_2.png" alt="修改模型参数后结果"><br>拟合优度0.999,预测值为2675,与今年的实际值也基本吻合。</p>
<h1><span id="shi-yan-xiao-jie">实验小结</span><a href="#shi-yan-xiao-jie" class="header-anchor"></a></h1><p>实验1与实验2说明,对样本做一定处理后,对模型最终的拟合影响不大,原因是在求解模型时,通常我们使用的loss是欧式距离(最小二乘估计),整体的<br>loss是对所有样本拟合loss的和,所以模型在拟合时会更”关注”值大的样本,而前几年的值与后几年相比差距一个数量级,所以不使用值很小的样本对模型<br>影响不大。而实际建模时,对于样本范围跨度很大的特征,通常我们都需要平滑而不是直接使用原始值。<br>实验3我们使用了一个参数更小的模型,$f(x) = A + Bx + Cx^2$, 而原始模型使用三次拟合,$F(x) = A + bx + Cx^2 + Dx^3$, 两个模型明显是<br>不同的,但是却都能拟合数据,这是因为这些模型都过拟合了,也就是对于一批离散点,总是能找到一个函数F,可以非常好的拟合他,这也是冯诺伊曼的那<br>个笑话:四个参数画大象,五个参数鼻子晃。</p>
<h1><span id="shi-me-shi-guo-ni-he">什么是过拟合</span><a href="#shi-me-shi-guo-ni-he" class="header-anchor"></a></h1><p>在这个问题上,一些网友说,这个模型肯定是有效的而不是过拟合,因为他”完美预测”了今年的真实值,模型如果只是单纯的在之前的数据上很好的拟合,而不能成功预测今年的真实值,这才是过拟合,现在模型成功预测了今年的值,所以现在的模型是有效的,不存在过拟合。<br>为了说明这个问题,我们来简单讨论一下,什么是过拟合,是不是成功预测了就不存在过拟合。<br>首先,我们来简单说明一下,一个模型的误差分为两部分,一部分是因为模型对训练数据拟合的不好带来的误差,这部分我们称为偏差;而由于抽样过程中,抽出的训练样本与整体样本分布不同引起的误差,我们称为方差。而当我们抽样时是有偏的,即抽样与整体样本分布不同,而我们的模型又对训练数据拟合的非常好,此时的模型我们就称之为过拟合,即他虽然对样本拟合的很好,但是由于样本与整体分布不同,导致模型在泛化时性能并不好,通常比在训练集上要差很多,因为毕竟模型拟合的是一个与整体不同分布的数据集,拟合的越好,泛化越差。<br>那当模型在新的样本上预测对时,是不是就不能说他过拟合呢?其实也不尽然,本质上过拟合只与你的整个过程有关,至于最终的模型在新样本上预测的准不准,并不能说明这个模型是否过拟合,毕竟瞎猫还能碰上死耗子,模型拟合的数据集虽然与整体分布不同,但是毕竟也是整体数据的一部分,预测准<br>一部分数据也正常,毕竟我们说过拟合的泛化性能低,但不是说他泛化低到全错。<br>所以对应天猫的这个案例,总的来说天猫的销售额是一个复杂的业务场景最终带来的结果,而仅仅用最终的销售额来进行模型,忽略各种业务策略的影响因素,显然与业务的真实分布相去甚远,不然也不用投入这么多人力物力,直接等着新的一年到来就好了,因为你不管怎么调整,最终的结果都是”模型”定好的。</p>
<h1><span id="tong-su-de-jie-shi">通俗的解释</span><a href="#tong-su-de-jie-shi" class="header-anchor"></a></h1><p>当我用上面这些论述来解释时,同事告诉我:”我不懂数学不懂机器学习,我就知道他的模型预测对了,都预测对了,你怎么能说他不准呢,还过拟合,过拟合不是预测不对才叫过拟合吗?” 这时我会换一个场景,”假如现在有个人,通过之前的十期双色球,预测对了这期对双色球,那你要不要用他的模型,重金投入买下一期的双色球呢?”</p>
<h1><span id="zhi-shi-zu-zhou">知识诅咒</span><a href="#zhi-shi-zu-zhou" class="header-anchor"></a></h1><p>最近学到了一个新的概念:知识的诅咒,其含义是当你知道了一件事后,你就无法想象自己是不知道这件事的。而造成这种现象的根本原因是信息的不对等。在我最初给人解释天猫销售额事件时,对面总是听不太懂我在说什么,我也搞不清楚为什么我说的这么简单直白,他会听不懂,当看到知识诅咒这个概念后,这个问题就有了答案了。</p>
<h1><span id="guan-yu-tou-tu">关于头图</span><a href="#guan-yu-tou-tu" class="header-anchor"></a></h1><p>摄于故宫</p>
]]></content>
<categories>
<category>Math</category>
</categories>
<tags>
<tag>Statistics</tag>
</tags>
</entry>
<entry>
<title>带约束的领域词挖掘</title>
<url>/2019/11/28/domain-words/</url>
<content><![CDATA[<link rel="stylesheet" class="aplayer-secondary-style-marker" href="/assets/css/APlayer.min.css"><script src="/assets/js/APlayer.min.js" class="aplayer-secondary-script-marker"></script><div class="toc">
<!-- toc -->
<ul>
<li><a href="#bei-jing">背景</a></li>
<li><a href="#fang-an">方案</a><ul>
<li><a href="#shi-yan">实验</a></li>
<li><a href="#shi-yan-jie-guo">实验结果</a></li>
</ul>
</li>
<li><a href="#zong-jie">总结</a><ul>
<li><a href="#you-dian">优点</a></li>
<li><a href="#que-dian">缺点</a></li>
<li><a href="#fen-xiang">分享</a></li>
</ul>
</li>
<li><a href="#guan-yu-tou-tu">关于头图</a></li>
</ul>
<!-- tocstop -->
</div>
<h1><span id="bei-jing">背景</span><a href="#bei-jing" class="header-anchor"></a></h1><p>分享一个最近做的项目方案,背景是当前的内容标签是最细粒度到一级大类,而产品希望出一些三级小类(当前最细分类)的标签,而且这些标签应该是有别于其他三级小类的。</p>
<h1><span id="fang-an">方案</span><a href="#fang-an" class="header-anchor"></a></h1><p>我将其看作是领域词挖掘任务,只是这个任务带有一些约束,及这些领域词是个性化的,与其他小类内的领域词是不同的。<br>所以,需要做的是抽词(新词发现)+领域词挖掘+个性化判断。<br>由于之前做过新词发现,主要参考<a href="http://www.matrix67.com/blog/archives/5044" target="_blank" rel="noopener">互联网时代的社会语言学:基于SNS的文本数据挖掘</a>,<br>对应的java实现<a href="https://github.com/xv44586/dict_build" target="_blank" rel="noopener">dict_build</a>。<br>除了无监督抽词外,matrix67的博文后面半部分也很有意思:通过对两份语料进行抽词,然后对词进行词频统计,通过对比词在两份语料内的词频差异,来发现“热词”。<br>回到现在的任务,个性化的词,必定在当前小类内相对于其他小类更“热”,我们可以将当前小类内的词与非当前小类进行对比,就能得到当前小类的“热词”,<br>有了热词,我们在判断这些“热词”是不是领域词,即可得到想要的结果。而实际上,由于一级大类之间的词已经有很大差异,所以,我们在抽当前小类“热词”时,不必对全量非当前小类语料进行统计,只需要对其一级大类内非当前小类语料进行统计对比,即可得到“热词”。<br>而对于领域词判断,由于我们已经有了一级大类的标签,我们可以利用这部分信息来进行领域词判断。<br>所以,最终的方案是先抽词,然后对词进行领域词判断,对词进行是否是“热词”判断,对领域词与热词进行求交运算,得到个性化的领域词。</p>
<h2><span id="shi-yan">实验</span><a href="#shi-yan" class="header-anchor"></a></h2><p>实际处理时,由于我们挖掘的“词”其实更像是一个短语,其长度也比词稍长,而“词”的组合上限是${CharCount}^{WordLength}$,而字符大概有12000+, 词平均长度假如是7,这个量也是相当大的。<br>所以抽词对内存要求很高,而且非常耗时(10h+).在思考如何优化抽词程序时,看到了<a href="https://kexue.fm/archives/6540" target="_blank" rel="noopener">分享一次专业领域词汇的无监督挖掘</a>,作者的思路与我的思路一样,不过不同的是,<br>作者在抽词时,并没有沿用matrix67的博文中根据凝合度与信息熵来进行是否成词判断,而且用信息熵是否低于某个阈值,来判断是否切开,即为两个词,这个思路还是非常巧妙的,这样,不光计算量减小了很多,而且不在需要设置超参数中ngram的值,<br>挖掘出的词也更符号“语义完整性”;而领域词判断,作者采用的是通过语料训练词向量,再由种子词来扩展领域词,也不失为一个好思路。</p>
<h2><span id="shi-yan-jie-guo">实验结果</span><a href="#shi-yan-jie-guo" class="header-anchor"></a></h2><p>从结果上看,效果还是很不错的。<br><img src="/2019/11/28/domain-words/words.png" alt="热词结果"><br>热词结果<br><img src="/2019/11/28/domain-words/keywords.png" alt="领域词结果"><br>领域词结果<br><img src="/2019/11/28/domain-words/result.png" alt="最终结果"><br>最终挖掘的个性化领域词</p>
<h1><span id="zong-jie">总结</span><a href="#zong-jie" class="header-anchor"></a></h1><h2><span id="you-dian">优点</span><a href="#you-dian" class="header-anchor"></a></h2><p>挖掘领域词任务中,首先是如何对”词”进行判断,然后才是”领域词”判断,所以影响最终结果好坏的主要因素也是”词”划分的质量与”领域词”判断的准确性。<br>上述方案中,通过用信息熵的最小阈值来判断相邻两个字是否属于同一个词内来进行分词,优点是速度快,能切分出高频的短语;<br>通过种子词在词向量空间来一层一层挖掘领域词,主要思路是近邻的近邻,虽然与你”直线距离”稍远,但通过你的近邻点跳转后”距离”近,这样来扩大召回,所以通过少量种子点即可召回出大量”近邻”词。</p>
<h2><span id="que-dian">缺点</span><a href="#que-dian" class="header-anchor"></a></h2><p>缺点也很明显,首先分词时,容易将常用搭配错误切分,如html容易切出 *h/tml,的xxx容易切分为一个词,所以需要写一些规则过滤掉明显错误切分的词;种子词在召回时,由于词向量用的是word2vec这种静态词向量,近邻容易跳出当前domain,如用”华为”做种子词,直接聚类出”小米”/“苹果”,而苹果的近邻就可能跳出手机品牌,<br>转向水果,如”西瓜”/“桃子”,所以需要控制扩散的层次深度。</p>
<h2><span id="fen-xiang">分享</span><a href="#fen-xiang" class="header-anchor"></a></h2><p>机器学习中最重要的一个思路就是寻找差异,找到合理的差异往往是解决问题的关键。本次分享了一个通过对比差异来进行领域词挖掘的例子,希望给做类似任务的同学一些思路。</p>
<h1><span id="guan-yu-tou-tu">关于头图</span><a href="#guan-yu-tou-tu" class="header-anchor"></a></h1><p>摄于奥森</p>
]]></content>
<categories>
<category>NLP</category>
</categories>
<tags>
<tag>新词发现</tag>
<tag>领域词挖掘</tag>
<tag>信息熵</tag>
</tags>
</entry>
<entry>
<title>有趣的概率统计题</title>
<url>/2019/11/30/statistics/</url>
<content><![CDATA[<link rel="stylesheet" class="aplayer-secondary-style-marker" href="/assets/css/APlayer.min.css"><script src="/assets/js/APlayer.min.js" class="aplayer-secondary-script-marker"></script><div class="toc">
<!-- toc -->
<ul>
<li><a href="#fen-xiang-yi-ge-you-qu-de-gai-lu-ti">分享一个有趣的概率题</a></li>
<li><a href="#guan-yu-tou-tu">关于头图</a></li>
</ul>
<!-- tocstop -->
</div>
<p>昨晚下雪了,开心😄!</p>
<h1><span id="fen-xiang-yi-ge-you-qu-de-gai-lu-ti">分享一个有趣的概率题</span><a href="#fen-xiang-yi-ge-you-qu-de-gai-lu-ti" class="header-anchor"></a></h1><p>一段线段上,任意取两不重合的点,将这条线段切分成三段,问,这三条线段组成三角形点概率是多少?</p>
<p>现在我们将问题转化一下,假设原始线段长度为1,即$(0,1)$表示原始线段,此时,随机在$(0,1)$范围内选两个点a,b,组成$(0,a)$,$(a,b)$,$(b,1)$三个线段,<br>此时原始问题等价于现在的三个线段组成三角形的概率。<br>现在我们来考虑一下,三条线段分别为$A$, $B$,$C$,其中$A >= B >= C$,则A的长度必定在$[1/3, 1)$,而要组成三角形,则需要 $B + C > A$,所以 B + C 的长度为$(1/2, 1)$,<br>而对应的A的长度也就在[1/3, 1/2)内,所以,最终能构成三角形的概率即为 $A_{triangle} / A_{all} = (1/2 - 1/3) / (1 - 1/3) = 1/4$</p>
<p>看似非常复杂的问题,从最基本的数学知识就能解答,还真是有趣! </p>
<h1><span id="guan-yu-tou-tu">关于头图</span><a href="#guan-yu-tou-tu" class="header-anchor"></a></h1><p>2019年的第一场雪,摄于望京地铁站</p>
]]></content>
<categories>
<category>Math</category>
</categories>
<tags>
<tag>Statistics</tag>
</tags>
</entry>
<entry>
<title>分享一个有趣的游戏</title>
<url>/2019/12/09/longton/</url>
<content><![CDATA[<link rel="stylesheet" class="aplayer-secondary-style-marker" href="/assets/css/APlayer.min.css"><script src="/assets/js/APlayer.min.js" class="aplayer-secondary-script-marker"></script><div class="toc">
<!-- toc -->
<ul>
<li><a href="#guan-yu-tou-tu">关于头图</a></li>
</ul>
<!-- tocstop -->
</div>
<p>今天第一次知道了一个有趣的游戏,Langton的蚂蚁,动手自己画了一个后,决定分享一下。这个游戏的有趣体现在两个方面:<br><em>1:</em> 这个游戏是个零玩家游戏,整个过程也十分的简单:有一个像围棋盘一样画满方格的画布,初始时,整个画布都是空白,<br>一只蚂蚁在画布的某一个地方,如果当前空格为白色,则将当前空格去反,然后左转90度并前进一格;如果当前空格为黑色,则将当前空格颜色去反,<br>然后右转90度并前进一格,如此往复。<br><em>2:</em> 最后的结果非常有意思,开始时,整个画布是复杂无规律的复杂图像,很难想象是这么简单的规则产生的,但当蚂蚁走了一万步以后,整个运动过程<br>进入了循环,而图像也开始变为有规律的图像。</p>
<p>开始的前一百步如上图,到一万步还有点时间,所以为直接跳过中间一万步,给出一万步后的一百步。感兴趣有耐心的可以跑下代码观察一下这个有点神奇的游戏。Have fun!</p>
<p><img src="/2019/12/09/longton/ant.gif" alt><br> <strong>前一百步</strong></p>
<p><img src="/2019/12/09/longton/ant_r.gif" alt><br> <strong>规律出现后的一百步</strong></p>
<p>Langlon的蚂蚁游戏代码</p>
<figure class="highlight python"><table><tr><td class="code"><pre><span class="line"><span class="comment"># -*- coding: utf-8 -*-</span></span><br><span class="line"><span class="comment"># @Date : 2019/12/9</span></span><br><span class="line"><span class="comment"># @Author : mingming.xu</span></span><br><span class="line"><span class="comment"># @File : test.py</span></span><br><span class="line"><span class="keyword">import</span> numpy <span class="keyword">as</span> np</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> matplotlib.pyplot <span class="keyword">as</span> plt</span><br><span class="line"><span class="keyword">from</span> matplotlib <span class="keyword">import</span> animation</span><br><span class="line"><span class="keyword">import</span> matplotlib.ticker <span class="keyword">as</span> plticker</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">Direction</span><span class="params">(object)</span>:</span></span><br><span class="line"> D = [<span class="string">'E'</span>, <span class="string">'N'</span>, <span class="string">'W'</span>, <span class="string">'S'</span>]</span><br><span class="line"></span><br><span class="line"> <span class="function"><span class="keyword">def</span> <span class="title">__init__</span><span class="params">(self, position, direct)</span>:</span></span><br><span class="line"> self.direct = direct</span><br><span class="line"> self.position = position</span><br><span class="line"></span><br><span class="line"> <span class="function"><span class="keyword">def</span> <span class="title">go_one_step</span><span class="params">(self, left=True)</span>:</span></span><br><span class="line"> lr = <span class="number">1</span> <span class="keyword">if</span> left <span class="keyword">else</span> <span class="number">-1</span></span><br><span class="line"> direct = self.D[(self.D.index(self.direct) + <span class="number">1</span> * lr) % len(self.D)]</span><br><span class="line"> next_position = {</span><br><span class="line"> <span class="string">'E'</span>: <span class="keyword">lambda</span> p: [p[<span class="number">0</span>], p[<span class="number">1</span>] + <span class="number">1</span> * lr],</span><br><span class="line"> <span class="string">'N'</span>: <span class="keyword">lambda</span> p: [p[<span class="number">0</span>] - <span class="number">1</span> * lr, p[<span class="number">1</span>]],</span><br><span class="line"> <span class="string">'W'</span>: <span class="keyword">lambda</span> p: [p[<span class="number">0</span>], p[<span class="number">1</span>] - <span class="number">1</span> * lr],</span><br><span class="line"> <span class="string">'S'</span>: <span class="keyword">lambda</span> p: [p[<span class="number">0</span>] + <span class="number">1</span> * lr, p[<span class="number">1</span>]]</span><br><span class="line"> }</span><br><span class="line"> position = next_position[self.direct](self.position)</span><br><span class="line"> <span class="keyword">return</span> Direction(position=position, direct=direct)</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">N = <span class="number">100</span> <span class="comment"># length of matrix</span></span><br><span class="line">lc, hc = <span class="number">1</span>, <span class="number">-1</span> <span class="comment"># color for negative and positive</span></span><br><span class="line"></span><br><span class="line"></span><br><span class="line">fig, ax = plt.subplots(figsize=(<span class="number">50</span>, <span class="number">50</span>))</span><br><span class="line">data = np.zeros((N, N)) + lc</span><br><span class="line">data[int(N/<span class="number">2</span>), int(N/<span class="number">2</span>)] *= <span class="number">-1</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># remove axis</span></span><br><span class="line">ax.get_yaxis().set_visible(<span class="literal">False</span>) <span class="comment">#不显示y轴</span></span><br><span class="line">ax.get_xaxis().set_visible(<span class="literal">False</span>) <span class="comment">#不显示x轴</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># add text</span></span><br><span class="line">time_template = <span class="string">'step = %d'</span></span><br><span class="line">time_text = plt.text(<span class="number">0.5</span>, N+<span class="number">0.1</span>, <span class="string">''</span>, fontdict={<span class="string">'size'</span>: <span class="number">20</span>, <span class="string">'color'</span>: <span class="string">'red'</span>})</span><br><span class="line"></span><br><span class="line"><span class="comment">#add grid</span></span><br><span class="line"><span class="comment"># myInterval = 1</span></span><br><span class="line"><span class="comment"># loc = plticker.MultipleLocator(base=myInterval)</span></span><br><span class="line"><span class="comment"># ax.xaxis.set_major_locator(loc)</span></span><br><span class="line"><span class="comment"># ax.yaxis.set_major_locator(loc)</span></span><br><span class="line"><span class="comment"># ax.grid(which='major', axis='both', linestyle='-')</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># imshow</span></span><br><span class="line">ln = plt.imshow(data, animated=<span class="literal">True</span>, cmap=<span class="string">'gray'</span>)</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">dct = Direction(position=[int(N / <span class="number">2</span>), int(N / <span class="number">2</span>)], direct=<span class="string">'N'</span>)</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">init</span><span class="params">()</span>:</span></span><br><span class="line"> ax.set_xlim(<span class="number">0</span>, N)</span><br><span class="line"> ax.set_ylim(<span class="number">0</span>, N+<span class="number">5</span>)</span><br><span class="line"> time_text.set_text(<span class="string">''</span>)</span><br><span class="line"> <span class="keyword">return</span> ln, time_text</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">move</span><span class="params">(dct, data)</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 dct:</span></span><br><span class="line"><span class="string"> :param mat:</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"> pos = dct.position</span><br><span class="line"> <span class="keyword">if</span> data[pos[<span class="number">0</span>], pos[<span class="number">1</span>]] > <span class="number">0</span>:</span><br><span class="line"> data[pos[<span class="number">0</span>], pos[<span class="number">1</span>]] *= <span class="number">-1</span></span><br><span class="line"> dct_ = dct.go_one_step(left=<span class="literal">True</span>)</span><br><span class="line"> <span class="keyword">else</span>:</span><br><span class="line"> data[pos[<span class="number">0</span>], pos[<span class="number">1</span>]] *= <span class="number">-1</span></span><br><span class="line"> dct_ = dct.go_one_step(left=<span class="literal">False</span>)</span><br><span class="line"></span><br><span class="line"> <span class="keyword">return</span> dct_, data</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">update</span><span class="params">(f)</span>:</span></span><br><span class="line"> <span class="keyword">global</span> dct</span><br><span class="line"> <span class="keyword">global</span> data</span><br><span class="line"> dct, data = move(dct, data)</span><br><span class="line"> ln.set_data(data)</span><br><span class="line"> time_text.set_text(time_template % f)</span><br><span class="line"> <span class="keyword">return</span> ln, time_text</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">data_gen</span><span class="params">()</span>:</span></span><br><span class="line"> frame = <span class="number">0</span></span><br><span class="line"> max_step = <span class="number">11000</span></span><br><span class="line"></span><br><span class="line"> <span class="keyword">while</span> frame < max_step:</span><br><span class="line"> frame += <span class="number">1</span></span><br><span class="line"> <span class="keyword">yield</span> frame</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">anim = animation.FuncAnimation(fig, update, frames=data_gen, interval=<span class="number">10</span>,</span><br><span class="line"> init_func=init, blit=<span class="literal">True</span>, repeat=<span class="literal">False</span>)</span><br><span class="line"></span><br><span class="line">anim.save(<span class="string">'ant.gif'</span>, writer=<span class="string">'imagemagick'</span>, fps=<span class="number">100</span>)</span><br><span class="line"></span><br><span class="line">plt.show()</span><br></pre></td></tr></table></figure>
<h1><span id="guan-yu-tou-tu">关于头图</span><a href="#guan-yu-tou-tu" class="header-anchor"></a></h1><p>游戏的前一百步</p>
]]></content>
<categories>
<category>Math</category>
</categories>
<tags>
<tag>Game</tag>
</tags>
</entry>
<entry>
<title>Backup</title>
<url>/2019/12/27/backup/</url>
<content><![CDATA[<link rel="stylesheet" class="aplayer-secondary-style-marker" href="/assets/css/APlayer.min.css"><script src="/assets/js/APlayer.min.js" class="aplayer-secondary-script-marker"></script><div class="toc">
<!-- toc -->
<ul>
<li><a href="#pei-zhi">配置</a><ul>
<li><a href="#cha-jian">插件</a></li>
</ul>
</li>
<li><a href="#zhu-ti">主题</a></li>
<li><a href="#markdown">MarkDown</a><ul>
<li><a href="#bu-ju">布局</a></li>
</ul>
</li>
<li><a href="#guan-yu-tou-tu">关于头图</a></li>
</ul>
<!-- tocstop -->
</div>
<h1><span id> </span><a href="#" class="header-anchor"></a></h1><p><strong>Just for me!<br>现在使用的博客虽然使用的是开源的,但是自己做了部分修改,加上一些常用语法一段时间不用后又需要重新查,所以在此记录一下当前博客常用的。 </strong> </p>
<h1><span id="pei-zhi">配置</span><a href="#pei-zhi" class="header-anchor"></a></h1><p>Hexo 部署文档: <a href="http://hexo.io/docs/deployment.html" target="_blank" rel="noopener">http://hexo.io/docs/deployment.html</a><br>Hexo _config.xml 的配置 <a href="https://gist.github.com/btfak/18938572f5df000ebe06fbd1872e4e39" target="_blank" rel="noopener">https://gist.github.com/btfak/18938572f5df000ebe06fbd1872e4e39</a></p>
<h2><span id="cha-jian">插件</span><a href="#cha-jian" class="header-anchor"></a></h2><ul>
<li>hexo-toc <a href="https://github.com/bubkoo/hexo-toc" target="_blank" rel="noopener">Insert a markdown TOC before posts be rendered</a><br>用来生产目录</li>
<li>hexo-renderer-marked + MathJax<br>整体顺序是先由renderer渲染,然后交给MathJax渲染Math相关,前者在遇见$ $后将escape _ 导致下标失效(_在renderer中认为是黑体,<br>所以产生这种冲突),所以修改了部分escape</li>
</ul>
<h1><span id="zhu-ti">主题</span><a href="#zhu-ti" class="header-anchor"></a></h1><ul>
<li>hexo-theme-skapp <a href="https://github.com/Mrminfive/hexo-theme-skapp" target="_blank" rel="noopener">https://github.com/Mrminfive/hexo-theme-skapp</a><ul>
<li>主要修改:</li>
</ul>
<ol>
<li>部分页面布局,包括footer和header</li>
<li>字体样式,包括部分元素样式,如<strong>code</strong></li>
</ol>
</li>
</ul>
<h1><span id="markdown">MarkDown</span><a href="#markdown" class="header-anchor"></a></h1><h2><span id="bu-ju">布局</span><a href="#bu-ju" class="header-anchor"></a></h2><ul>
<li><p>添加大纲<br>在正文最开始添加 </p>
<figure class="highlight"><table><tr><td class="code"><pre><span class="line"><!-- toc --></span><br></pre></td></tr></table></figure>
</li>
<li><p>标题<br><code> # </code> ~ <code>######</code>,<code>#</code>号的个数表示几级标题,即表示一级标题到六级标题</p>
</li>
<li><p>有序列表</p>
<figure class="highlight plain"><table><tr><td class="code"><pre><span class="line">1. **我是一级序列** </span><br><span class="line">2. **我是一级序列** </span><br><span class="line">3. **我是一级序列** </span><br><span class="line"> 1. *我是二级序列* </span><br><span class="line"> 1. *我是二级序列* </span><br><span class="line"> 1. *我是二级序列*</span><br></pre></td></tr></table></figure>
</li>
</ul>
<blockquote><ol>
<li><strong>我是一级序列</strong> </li>
<li><strong>我是一级序列</strong> </li>
<li><strong>我是一级序列</strong> <ol>
<li><em>我是二级序列</em> </li>
<li><em>我是二级序列</em> </li>
<li><em>我是二级序列</em> </li>
</ol>
</li>
</ol>
</blockquote>
<ul>
<li>无序列表</li>
</ul>
<figure class="highlight plain"><table><tr><td class="code"><pre><span class="line">* *列表展示 </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><br><span class="line">- -列表展示</span><br><span class="line"> - -列表展示</span><br><span class="line"> - -列表展示</span><br></pre></td></tr></table></figure>
<blockquote><ul>
<li>*列表展示<ul>
<li>*列表展示<ul>
<li>*列表展示</li>
</ul>
<ul>
<li>+列表展示</li>
</ul>
</li>
</ul>
<ul>
<li>+列表展示<ul>
<li>+列表展示</li>
</ul>
<ul>
<li>-列表展示</li>
</ul>
</li>
</ul>
<ul>
<li>-列表展示<ul>
<li>-列表展示</li>
</ul>
</li>
</ul>
</li>
</ul>
</blockquote>
<ul>
<li>表格</li>
</ul>
<figure class="highlight python"><table><tr><td class="code"><pre><span class="line">&nbsp; | l1 | l2 </span><br><span class="line"> ----- | --- | ---- </span><br><span class="line"> w0 | $e_{<span class="number">01</span>}$ | $e_{<span class="number">02</span>}$ </span><br><span class="line"> w1 | $e_{<span class="number">11</span>}$ | $e_{<span class="number">12</span>}$ </span><br><span class="line"> w2 | $e_{<span class="number">21</span>}$ | $e_{<span class="number">22</span>}$</span><br></pre></td></tr></table></figure>
<p>表头与正文用–来分割,列之间用|来分割。注:列名不能使用空格,如需要列名为空,需要使用 &nbsp;替换</p>
<blockquote><table>
<thead>
<tr>
<th> </th>
<th>l1</th>
<th>l2 </th>
</tr>
</thead>
<tbody>
<tr>
<td> w0</td>
<td>$e_{01}$</td>
<td>$e_{02}$ </td>
</tr>
<tr>
<td> w1</td>
<td>$e_{11}$</td>
<td>$e_{12}$ </td>
</tr>
<tr>
<td> w2</td>
<td>$e_{21}$</td>
<td>$e_{22}$ </td>
</tr>
</tbody>
</table>
</blockquote>
<ul>
<li><p>强调</p>
**加粗** __加粗__ _斜体_ ***加粗并斜体*** ~~删除线~~
<blockquote><p><strong>加粗</strong> <strong>加粗</strong> _斜体_ <strong><em>加粗并斜体</em></strong> ~~删除线~~</p>
</blockquote>
</li>
<li><p>高亮</p>
</li>
</ul>
<figure class="highlight plain"><table><tr><td class="code"><pre><span class="line">用<code> content </code> 来包裹想要高亮的元素</span><br></pre></td></tr></table></figure>
<blockquote><p>用<code> content </code> 来包裹想要高亮的元素</p>
</blockquote>
<ul>
<li><p>关闭MarkDown</p>
<blockquote><p>{% raw %} content {% endraw %}</p>
<p>这种方式将会忽略空格回车等,有些场景也会失效,此时可以用 <strong>代码块</strong> 代替</p>
</blockquote>
</li>
<li><p>引用</p>
<blockquote><p>{% blockquote 江泽民%}科技是第一生产力 {% endblockquote %} </p>
</blockquote>
<blockquote><p>科技是第一生产力</p>
<footer><strong>江泽民</strong></footer></blockquote>
</li>
<li><p>Math</p>
<blockquote><p>行内公式用 $包裹,多行时用$$包裹<br>使用的是MathJax,语法可参考博客<a href="https://blog.csdn.net/ethmery/article/details/50670297" target="_blank" rel="noopener">MathJax基本语法</a> 和官方文档(<a href="https://math.meta.stackexchange.com/questions/5020/mathjax-basic-tutorial-and-quick-reference" target="_blank" rel="noopener">https://math.meta.stackexchange.com/questions/5020/mathjax-basic-tutorial-and-quick-reference</a>)<br><code>调试参考<a href="https://www.quicklatex.com/" target="_blank" rel="noopener">quicklatex</a></code></p>
</blockquote>
</li>
</ul>
<figure class="highlight python"><table><tr><td class="code"><pre><span class="line">$</span><br><span class="line"> states = $$\begin{pmatrix}</span><br><span class="line"> e_{<span class="number">01</span>}&e_{<span class="number">01</span>}\\\\</span><br><span class="line"> e_{<span class="number">02</span>}&e_{<span class="number">02</span>}\\\\</span><br><span class="line"> \end{pmatrix}$$</span><br><span class="line">$</span><br></pre></td></tr></table></figure>
<blockquote><p>$<br>states = $$\begin{pmatrix}<br>e_{01}&e_{01}\\<br>e_{02}&e_{02}\\<br>\end{pmatrix}$$<br>$</p>
<p><strong>注:公式内\ 会被转义,需要用双\,尤其在矩阵中。</strong></p>
</blockquote>
<ul>
<li>代码<blockquote><p>使用三个<code>`</code>包裹,如果需要显示三个 <code>`</code>, 可以用四个。也可以用 {%codeblock%}</p>
</blockquote>
</li>
</ul>
<figure class="highlight python"><table><tr><td class="code"><pre><span class="line">```</span><br><span class="line"> <span class="function"><span class="keyword">def</span> <span class="title">do</span><span class="params">(self, job_func, *args, **kwargs)</span>:</span></span><br><span class="line"> print(<span class="string">'hello world'</span>) </span><br><span class="line"> ```</span><br></pre></td></tr></table></figure>
<figure class="highlight python"><table><tr><td class="code"><pre><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">do</span><span class="params">(self, job_func, *args, **kwargs)</span>:</span></span><br><span class="line"> print(<span class="string">'hello world'</span>) </span><br><span class="line"> </span><br></pre></td></tr></table></figure>