-
Notifications
You must be signed in to change notification settings - Fork 0
/
search.xml
1220 lines (1220 loc) · 510 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><![CDATA[数据预处理]]></title>
<url>%2F2019%2F10%2F30%2F%E4%BA%BA%E5%B7%A5%E6%99%BA%E8%83%BD%2F%E7%89%B9%E5%BE%81%E5%B7%A5%E7%A8%8B%2F%E6%95%B0%E6%8D%AE%E9%A2%84%E5%A4%84%E7%90%86%2F</url>
<content type="text"><![CDATA[预处理 归一化 列:preprocessing.MinMaxScaler preprocessing.StandardScaler行:preprocessing.Normalizer 分类与编码 preprocessing.OrdinalEncoder()preprocessing.OneHotEncoder() 数据离散化 多项式化 PolynomialFeatures(degree=2,include_bias=False) 缺失值处理 归一化12345678910111213from sklearn.preprocessing import MinMaxScaler,StandardScaler,Normalizer# MinMaxScaler() 列特征iris_train=MinMaxScaler().fit_transform(iris_train)iris_test=MinMaxScaler().fit_transform(iris_test)# StandardScaler() 列特征iris_train=StandardScaler().fit_transform(iris_train)iris_test=StandardScaler().fit_transform(iris_test)# Normalizer() 行特征iris_train=Normalizer().fit_transform(iris_train)iris_test=Normalizer().fit_transform(iris_test) 列特征主要在线性模型中进行 名称 公式 说明 StandardScaler 标准化 $x’=\frac{x-\bar{x}}{\sigma}$ $\bar{x}$为每一列特征的均值$\sigma$为每一列特征的标准差 MinMaxScaler 归一化 $x’=\frac{x-Min}{Max-Min}$ $Min$:每一列特征的最小值$Max$:每一列特征的最大值 为什么树形模型不需要进行归一化、标准化因为树形模型的损失函数与特征尺度无关,所以树节点进行分裂的时候以及计算叶节点得分的时候同样与特征尺度无关。 分裂点 叶节点得分 ID3-分类 遍历特征,使$g(D,A)=H(D)-H(D A)$最大损失函数只与样本量有关,与特征的尺度无关 即分类结果A C4.5-分类 遍历特征,使$g_{R}(D,A)= \frac {g(D,A)}{H_A(D)}$最大损失函数只与样本量有关,与特征的尺度无关 即分类结果 CART-分类 遍历特征,使$Gini(D)-Gini(D,A)$最大损失函数只与样本量有关,与特征的尺度无关 即分类结果 CART-回归 使用CART回归树拟合残差分裂点使用CART规则,与特征尺度无关 使$L=(y_i-T(x))^2$最小选取均值,与特征尺度无关 GDBT-分类 使用CART回归树拟合残差分裂点使用CART规则,与特征尺度无关 使$- \sum_{k=1}^K y_klog \; p_k(x)$最小与特征尺度无关 GBDT-回归 $L=(y_i-f(x_i))^2$分裂点使得损失函数最小,与特征尺度无关 使$L=(y_i-f(x_i))^2$最小选取残差的均值,与特征尺度无关 xgboost $Gain=\frac 1 2 [\frac {G_L^2}{H_L + \lambda} + \frac {G_R^2}{H_R + \lambda} - \frac {(G_L + G_R)^2}{H_L + H_R + \lambda}] - \gamma$使增益最大,与特征尺度无关 $w_j^* = - \frac {G_j} {H_j + \lambda}$与特征尺度无关 决策树与随机森林随机森林是从训练集中,不停的抽取子样本集,利用子样本集训练决策树的集成学习。所以归一化是否对随机森林有影响,只需要考察决策树。决策树的分支方式如下 信息增益 (与左右节点中样本的数量有关) g(D,A)=H(D)-H(D|A)设训练数据集为$D,|D|$表示其样本容量,即样本个数。设有$K$个类$C_k,k=1,2,…,K$,$|C_k|$为属于类$C_k$的样本个数,。设特征$A$有$n$个不同的取值${a_1,a_2,…,a_n}$,根据特征$A$的取值将$D$划分为$n$个子集$D_1,D_2,…,D_n,|D_i|$为$D_i$的样本个数,$\sum_{i=1}^{n}|D_i|=|D|$。记子集$D_i$中属于类$C_k$的样本的集合为$D_{ik}$,即$D_{ik}=D_i⋂C_k$,$|D_{ik}|$为$D_{ik}$的样本个数。 $H(D)=-\sum_{k=1}^{K}\frac{|C_k|}{|D|}log_2\frac{|C_k|}{|D|}$ $H(D|A)=-\sum_{i=1}^{n}\frac{|D_i|}{|D|}H(D_i)=-\sum_{i=1}^{n}\frac{|D_i|}{|D|}\sum_{k=1}^{K}\frac{|D_{ik}|}{|D_i|}log_2\frac{|D_{ik}|}{|D_i|}$发现信息增益只与不同结点的样本量有关 ,$H(D)$与根结点的总样本量以及不同类别的样本量有关;$H(D|A)$与子结点的总样本量以及不同类别的样本量有关。 信息增益比 (与左右节点中样本的数量有关) 设特征$A$有$n$个不同的取值${a_1,a_2,…,a_n}$,根据特征$A$的取值将$D$划分为$n$个子集$D_1,D_2,…,D_n,|D_i|$为$D_i$的样本个数。则 \begin{align*} g_{R}(D,A)&=\frac{g(D,A)}{H_A(D)}\\ H_A(D)&=-\sum_{i=1}^{n}\frac{|D_i|}{|D|}log_2\frac{|D_{i}|}{|D|} \end{align*}发现信息增益只与不同结点的样本量有关 ,$G(D|A)$与结点的总样本量以及不同类别的样本量有关;$H_A(D)$与父结点的样本量与子结点的样本量有关。 CART-基尼指数 (与左右节点中样本的数量有关) Gain=Gini(D)-Gini(D,A) $Gini(D)$ 假设有 $K$ 个类,样本点属于第$k$类的概率为$p_k$,则概率分布的基尼指数定义为$$\begin{align*} Gini(p)&=\sum_{k=1}^{K}p_k(1-p_k)=1-\sum_{k=1}^{K}p_k^2\\ Gini(D)&=1-\sum_{k=1}^{K}\left ( \frac{|c_k|}{D} \right )^2 \end{align*} **只与每一个类别中样本的数量有关** - $Gini(D,A)$ 如果样本集合 $D$ 根据特征 $A$ 是否取某一可能值 $a$ 被分割成 $D_1$ 和 $D_2$ 两部分,则在特征 $A$ 的条件下,集合 $D$ 的基尼指数定义为Gini(D,A)=\frac{|D_1|}{D}Gini(D_1)+\frac{|D_2|}{D}Gini(D_2)$$ **只与每一个类别中样本的数量有关** CART-回归 L=(y_i-T(x))^2 选择分裂点使所有两个树节点中的损失函数$L$最小 选择得分使得损失函数$L$最小,得分是$y$的均值 全程与特征的尺度无关 GBDT 结点分裂。计算残差就是负梯度$r_{m,i}$,根据残差拟合一颗回归树$CART$(回归树的损失函数为平方差损失函数)得到树的结构,由此可知分裂点的选择就是CART回归树的分裂方法,与特征尺度全程无关。 r_{m,i} = -\left[ \frac{\partial L(y_i,f(x_i))}{\partial f(x_i)} \right]_{f(x)=f_{m-1}(x)} 得分。计算得分使损失函数最小,与特征尺度无关 c_{m,j} = \arg \min_c \sum_{x_i\in R_{m,j}} L(y_i, f_{m-1}(x_i)+c)=\underset{x_i\in R_{m,j}}{ave}r_{m,i} ,~ j=1,2,...,Jxgboost$j$为叶子结点的序号,$T$ 为叶子结点的总数 ;$i$ 为样本的序号,$n$ 为样本的总数;$w_q(x_i)$是求取$x_i$权值的对应函数;$\sum_{i \in I_j} g_i$ 为同一叶子结点的 $g_i$ 的和;;$\sum_{=1}^T \sum_{i \in I_j} = \sum_{i=1}^n$。其中 g_i=\frac{\partial l(y_i,\hat{y_i}^{(t-1)})}{\partial \hat{y_i}^{(t-1)}} \quad h_i=\frac{\partial ^2l(y_i,\hat{y_i}^{(t-1)})}{\partial ^2\hat{y_i}^{(t-1)}} \\ G_j = \sum_{i \in I_j} g_i \quad H_j = \sum_{i \in I_j} h_i 结点分裂:取增益的最大值,发现增益与特征尺度无关。$G_R,G_L$都与特征尺度无关,所以结点的分裂与特征尺度无关 Gain=\frac 1 2 [\frac {G_L^2}{H_L + \lambda} + \frac {G_R^2}{H_R + \lambda} - \frac {(G_L + G_R)^2}{H_L + H_R + \lambda}] - \gamma 结点的权重:结点的得分与特征尺度无关 w_j^* = - \frac {G_j} {H_j + \lambda} 归一化、标准化对线性模型的影响 归一化只是对输入空间进行缩放,并不影响模型的全局最优解以及局部最优解。 假设输入特征分别为$x_1$、$x_2$,进行标准化之后,使用回归模型进行训练 x^{'}_1=\frac{x_1-\mu_1}{\sigma_1} \;\;\;\;\;\;\;\;\; x^{'}_2=\frac{x_2-\mu_2}{\sigma_2} \\ y=w_1x_1+w_2x_2+b \;\;\;\;\;\;\;\;\; y^{'}=w_1^{'}x_1^{'}+w_2^{'}x_2^{'}+b^{'} \\ \begin{align*} y^{'} &=w_1^{'}x_1^{'}+w_2^{'}x_2^{'}+b^{'} \\ &= w_1^{'}\frac{x_1-\mu_1}{\sigma_1}+w_2^{'}\frac{x_2-\mu_2}{\sigma_2}+b^{'} \\ &= \frac{w_1^{'}}{\sigma_1}x_1+\frac{w_2^{'}}{\sigma_2}x_2 -\frac{w_1^{'}\mu_1}{\sigma_1}-\frac{w_2^{'}-\mu_2}{\sigma_2}+b^{'} \end{align*}由上式可知任何特征经过缩放之后,都可以通过$w,b$变换成之前的模样。所以归一化只是对输入空间进行缩放,并不影响模型的全局最优解以及局部最优解。 在训练过程中,由于学习率以及初始点,不会根据输入空间的进行等比例缩放。因此很可能会导致模型最终的局部最优解不同。 例如初始点都选择为$(1,1)$,那么归一化之后的初始点相对于原始空间将发生变化 在梯度下降过程中可以解决不恰当的学习率、以及初始点导致梯度爆炸等情况。 因为在特征较多的时候,需要根据特征的不同尺度,选择不同的学习率与初始点,不然很容易发生梯度爆炸的问题。例如,学习率选择为0.01,对于有的特征来讲学习率太小,需要很长的迭代才会拟合;对于有的特征来讲,学习率太大了,会发生梯度爆炸。此时需要对不同的特征设置不同的学习率。进行归一化之后,特征的失度变得一致,梯度也将变得一致,不再需要对不同的特征设置不同的学习率。 注意:模型的收敛速度与初始点、学习率的选取都有关。大多数情况下,归一化会加快模型的收敛速度;但是某些时候导致收敛速度变慢。 行特征Normalizer行特征进行归一化,使行特征的和为1。主要用于TF-IDF等特征。 norm参数 说明 l1 样本各个行特征值除以各个行特征值的绝对值之和 l2 样本各个行特征值除以各个行特征值的平方之和 max 样本各个行特征值除以样本中行特征值最大的值 1234567891011>>> from sklearn.preprocessing import Normalizer>>> X = [[4, 1, 2, 2],... [1, 3, 9, 3],... [5, 7, 5, 1]]>>> transformer = Normalizer().fit(X) # fit does nothing.>>> transformerNormalizer(copy=True, norm='l2')>>> transformer.transform(X)array([[0.8, 0.2, 0.4, 0.4], #4^2/(4^2+1+2^2+2^2)=0.8 [0.1, 0.3, 0.9, 0.3], [0.5, 0.7, 0.5, 0.1]]) 分类&编码123456789101112131415# 序号编码from sklearn import preprocessingenc = preprocessing.OrdinalEncoder()X = [['male', 'from US', 'uses Safari'], ['female', 'from Europe', 'uses Firefox']]enc.fit(X) # fit来学习编码temp=enc.transform([['female', 'from US', 'uses Safari']]) # 进行编码# 独热编码from sklearn import preprocessingenc = preprocessing.OneHotEncoder()X = [['male', 'from US', 'uses Safari'], ['female', 'from Europe', 'uses Firefox']]enc.fit(X) # fit来学习编码temp=enc.transform([['female', 'from US', 'uses Safari']]).toarray() # 进行编码# 二进制编码 面对分类特征,需要对特征进行编码,不同的编码方式对模型有不同的影响。下面分别介绍几种同编码方式对线性模型与树形模型的影响。例如对于A、B、O型血,其编码方式如下所示: 序号编码 独热编码 二进制编码 编码 1 A2 B3 C 1 0 0 A0 1 0 B0 0 1 C 1 0 0 A0 1 0 B1 1 0 C 编码特点 共同使用一个变量,类别特征具有大小关系 不具有大小关系,每一个变量对应着一个类别 不同类别之间具有某种联系,使得类别无法通过单一变量进行区分 线性模型 不同类别产生了大小关系,使得特征对模型的贡献度呈单调关系 不同类别分配不同的权重,使得每一个类别都有不同的贡献度。是线性模型最好的编码方式 例如类别C为1 1 0,是类别A与类别B的和,对模型贡献度也是两者之和。但是在实际应用中,类别C与类别A、B没有任何关系 多分枝树模型 无影响。一次分裂,就能够将,不同类别放入不同的分支。是多分枝树最好的编码 方式 一次分支能够筛选出一类样本,如果想要筛选出所有样本,需要增加树的深度。本例中,树的深度起码为3。此时与二叉树没有区别。 一次分支能够筛选出部分样本,例如,对第一个分支进行分类,能够识别出A、C与B,其中类别A与类别C在一起;如果想要筛选出所有样本,需要增加树的深度。此时与二叉树没有区别。 二分支树模型 一次分支,无法识别出所有类别或一种类别,其中部分类别绑定在一起。如果想要识别所有类别需要增加树的深度 一次分支,只能识别出来一种类别。如果想要识别所有类别需要增加树的深度。 一次分支,无法识别出所有类别,例如,对第一个分支进行分类,能够识别出A、C与B,其中类别A与类别C在一起。如果想要识别所有类别需要增加树的深度。 注意:多分支树包括ID3、C4.5;二分支树包括CART、GBDT、XGBoost。但是目前sklearn里面的所有树模型都是二分支树模型 离散化连续特征离散化的优势: 离散特征的增加和减少都很容易,易于模型的快速迭代; 稀疏向量内积乘法运算速度快,计算结果方便存储,容易扩展; 逻辑回归属于广义线性模型,表达能力受限;单变量离散化为N个后,每个变量有单独的权重,相当于为模型引入了非线性,能够提升模型表达能力,加大拟合;例如使用独热编码,此时函数为非线性函数。 离散化后可以进行特征交叉(多项式化),由M+N个变量变为M*N个变量,进一步引入非线性,提升表达能力; 离散化后的特征对异常数据有很强的鲁棒性:比如一个特征是年龄>30是1,否则0。如果特征没有离散化,一个异常数据“年龄300岁”会给模型造成很大的干扰。 特征离散化后,模型会更稳定,比如如果对用户年龄离散化,20-30作为一个区间,不会因为一个用户年龄长了一岁就变成一个完全不同的人。当然处于区间相邻处的样本会刚好相反,所以怎么划分区间是门学问。 李沐曾经说过:模型是使用离散特征还是连续特征,其实是一个“海量离散特征+简单模型” 同 “少量连续特征+复杂模型”的权衡。既可以离散化用线性模型,也可以用连续特征加深度学习。就看是喜欢折腾特征还是折腾模型了。通常来说,前者容易,而且可以n个人一起并行做,有成功经验;后者目前看很赞,能走多远还须拭目以待。 多项式化&高维组合特征特征多项式化,能够有效的将不同特征组合起来,弥补了线性模型的不足;对于树形模型来说,模型会使用组合特征,当树的深度足够时,可以表达特征多项化的效果。4个特征$(x_1,x_2,x_3,x_4)$,度为2的多项式转换公式如下: (x_{1}^{'},x_{2}^{'},x_{3}^{'},x_{4}^{'},\cdots ,x_{15}^{'})=\\(1,x_{1},x_{2},x_{3},x_{4},x_{1}^{2},x_{1}*x_{2},x_{1}*x_{3},x_{1}*x_{4},x_{2}^2,x_{2}*x_{3},x_{2}*x_{4},x_{3}^2,x_{3}*x_{4},x_{4}^2)1234567891011'''多项式变换'''from sklearn.preprocessing import PolynomialFeaturesimport pandas as pddata = pd.DataFrame({'Integer':[0,1,2,1],'Categorical':[4,5,6,7]})#多项式转换#参数degree为度,默认值为2.include_bias常数项poly = PolynomialFeatures(degree=2,include_bias=False)temp = poly.fit_transform(data) 对于树形模型来说,模型会使用组合特征,当树的深度足够时,可以表达特征多项化的效果。例如$feature1\times feature2>=500$是$class2$,$feaute1\times feature2<500$是$class1$树模型额表达方式如下所示 graph TD A[feature1]-->|||>=10|D[feture1 ...] A-->|>=50|E[feature2] E-->||>=10|G[class2] 缺失值处理删除数据12345# 删除数据表中含有空值的行df.dropna(how='any',axis=0)# 删除缺失值列df.dropna(how='any',axis=1) 总览 1. 牺牲了大量的数据,通过减少历史数据换取完整的信息,这样可能丢失了很多隐藏的重要信息; 2. 当缺失数据比例较大时,特别是缺失数据非随机分布时,直接删除可能会导致数据发生偏离,比如原本的正态分布变为非正太; 删除样本 删除样本以后,可能造成模型性能下降。当数据量过大时,只能通过实验评估删除数据对模型的影响,难以从理论上进行评估,原因有以下两个:1. 删除之后样本,发生偏移,正负样本比例发生变化2. 特征空间发生变化,删除样本之后,导致样本的某些特征分布发生变化 删除特征 删除样本以后,可能造成模型性能下降。当特征过多时,只能通过实验评估删除特征对模型的影响,难以从理论上进行评估,原因如下:1. 通过计算正负样本在特征中的显著性变化或者IV值变化,可以判断单一特征对模型的影响。所以线性模型可以使用此种方法进行特征选择2. 但是如果当特征与其他特征组合起来,共同影响模型,此时便难以判断特征的重要性 判断组合特征对于模型的影响。如下所示,性别中存在部分缺失样本,但是可以通过已知性别与年龄,共同判断类别: graph TD A[年龄 范围0到100]-->||男1岁1个,共50个|C[class1] B-->|女1岁1个,共50个|D[class2] B-->|不详|H[男] A-->|>50|E[性别] E-->|男1岁1个,共50个|F[class2] E-->|女1岁1个,共50个|G[class1] E-->|不详|J[女] 如果只有一项特征是无法进行判断的。例如年龄或者只有性别,此时样本呈现无偏分布。所以删除特征难以进行判断,因此根据特征值删除特征需要谨慎。 缺失值填充 操作方法 说明 填充均值、中位数或众数 效果一般,因为等于人为增加了噪声 用其他变量做预测模型来算出缺失变量 效果比方法1略好。有一个根本缺陷:如果其他变量和缺失变量无关,则预测的结果无意义;如果预测结果相当准确,则又说明这个变量是没必要加入建模的 123456789101112# 使用sklearn填充缺失值from sklearn.preprocessing import Imputerimp=Imputer(missing_values='NaN',strategy='mean',axis=0) #median中位数 most_frequent众数imp.fit(x)imp.transform(x)# 使用pandas填充data_train.fillna(0) #填充0data_train.fillna(data_train.mean()) # 将所有行用各自的均值填充 data_train.fillna(data_train.mean()['browse_his':'card_num']) # 也可以指定某些行进行填充 data_train.fillna(method='pad') # 用前一个数据代替NaN:method='pad'data_train.fillna(method='bfill') # 与pad相反,bfill表示用后一个数据代替NaN 12 模型对缺失值的处理 12345678910111213141516171819202122232425262728293031# 定义工资改变特征缺失值处理函数,将有变化设为Yes,缺失设为No def set_salary_change(df): df.loc[(df.salary_change.notnull()),'salary_change']="Yes" df.loc[(df.salary_change.isnull()),'salary_change']="No" return dfdata_train=set_salary_change(data_train)# 定义browse_his缺失值预测填充函数defset_missing_browse_his(df): # 把已有的数值型特征取出来输入到RandomForestRegressor中 process_df=df[[browse_his' , 'gender', 'job', 'edu', 'marriage', 'family_type']] # 乘客分成已知该特征和未知该特征两部分 known=process_df[process_df.browse_his.notnull()].as_matrix() unknown=process_df[process_df.browse_his.isnull()].as_matrix() # X为特征属性值 X=known[:,1:] # y为结果标签值 y=known[:,0] # fit到RandomForestRegressor之中 rfr=RandomForestRegressor(random_state=0,n_estimators=2000,n_jobs=-1) rfr.fit(X,y) # 用得到的模型进行未知特征值预测 predicted=rfr.predict(unknown[:,1::]) # 用得到的预测结果填补原缺失数据 df.loc[(df.browse_his.isnull()),'browse_his']=predicted return df,rfrdata_train,rfr=set_missing_brows 数据填补对缺失值的插补大体可分为两种:替换缺失值,拟合缺失值,虚拟变量。替换是通过数据中非缺失数据的相似性来填补,其核心思想是发现相同群体的共同特征,拟合是通过其他特征建模来填补,虚拟变量是衍生的新变量代替缺失值。 替换缺失值 均值插补: 对于定类数据:使用 众数(mode)填补,比如一个学校的男生和女生的数量,男生500人,女生50人,那么对于其余的缺失值我们会用人数较多的男生来填补。 对于定量(定比)数据:使用平均数(mean)或中位数(median)填补,比如一个班级学生的身高特征,对于一些同学缺失的身高值就可以使用全班同学身高的平均值或中位数来填补。一般如果特征分布为正太分布时,使用平均值效果比较好,而当分布由于异常值存在而不是正太分布的情况下,使用中位数效果比较好。 注:此方法虽然简单,但是不够精准,可能会引入噪声,或者会改变特征原有的分布。 下图左为填补前的特征分布,图右为填补后的分布,明显发生了畸变。因此,如果缺失值是随机性的,那么用平均值比较适合保证无偏,否则会改变原分布。 1234Python中的使用:#使用price均值对NA进行填充df['price'].fillna(df['price'].mean())df['price'].fillna(df['price'].median()) 存在缺失值时的算法选择主流的机器学习模型千千万,很难一概而论。但有一些经验法则(rule of thumb)供参考: 树模型对于缺失值的敏感度较低,大部分时候可以在数据有缺失时使用。 涉及到距离度量(distance measurement)时,如计算两个点之间的距离,缺失数据就变得比较重要。因为涉及到“距离”这个概念,那么缺失值处理不当就会导致效果很差,如K近邻算法(KNN)和支持向量机(SVM)。 线性模型的代价函数(loss function)往往涉及到距离(distance)的计算,计算预测值和真实值之间的差别,这容易导致对缺失值敏感。 神经网络的鲁棒性强,对于缺失数据不是非常敏感,但一般没有那么多数据可供使用。 贝叶斯模型对于缺失数据也比较稳定,数据量很小的时候首推贝叶斯模型。 总结来看,对于有缺失值的数据在经过缺失值处理后: 数据量很小,用朴素贝叶斯 数据量适中或者较大,用树模型,优先 xgboost 数据量较大,也可以用神经网络 避免使用距离度量相关的模型,如KNN和SVM 三、处理缺失值的算法(一)决策树模型怎么处理异常值? 随机森林(Random Forests)的提出者Leo Breiman提出两种解决缺失值的方法: 方法1(快速简单但效果差):把数值型变量(numerical variables)中的缺失值用其所对应的类别中(class)的中位数(median)替换。把类别型变量(categorical variables)缺失的部分用所对应类别中出现最多的数值替代(most frequent non-missing value) 方法2(耗时费力但效果好):虽然依然是使用中位数和出现次数最多的数来进行替换,方法2引入了权重。即对需要替换的数据先和其他数据做相似度测量(proximity measurement),在补全缺失点是相似的点的数据会有更高的权重W。 (二)xgboost怎么处理缺失值? xgboost模型能够处理缺失值,也就是说模型允许缺失值存在。 论文中关于缺失值的处理: 将其看与稀疏矩阵的处理看作一样。在寻找split point的时候,不会对该特征为missing的样本进行遍历统计,只对该列特征值为non-missing的样本上对应的特征值进行遍历,通过这个技巧来减少了为稀疏离散特征寻找split point的时间开销。 在逻辑实现上,为了保证完备性,会分别处理将missing该特征值的样本分配到左叶子结点和右叶子结点的两种情形,计算增益后选择增益大的方向进行分裂即可。可以为缺失值或者指定的值指定分支的默认方向,这能大大提升算法的效率。如果在训练中没有缺失值而在预测中出现缺失,那么会自动将缺失值的划分方向放到右子树。]]></content>
<categories>
<category>人工智能</category>
<category>特征工程</category>
</categories>
<tags>
<tag>特征工程</tag>
</tags>
</entry>
<entry>
<title><![CDATA[距离]]></title>
<url>%2F2019%2F10%2F29%2F%E6%95%B0%E5%AD%A6%E5%9F%BA%E7%A1%80%2F%E9%AB%98%E6%95%B0%2F%E8%B7%9D%E7%A6%BB%2F</url>
<content type="text"><![CDATA[距离公理距离必须满足以下条件。设$X$是任一非空集,对$X$中任意两点$x,y$有一实数$d(x,y)$与之对应且满足: 正定性:非负性、唯一性。$d(x,y) \geq 0$ ,且 $d(x,y) = 0$当且仅当$x=y$ 对称性:$d(x,y) =d(y,x)$ 三角不等式:$d(x,y) \leq d(x,z)+d(y,z)$,意两边之和大于、等于第三边。]]></content>
<categories>
<category>数学基础</category>
<category>高数</category>
</categories>
<tags>
<tag>高数</tag>
</tags>
</entry>
<entry>
<title><![CDATA[AB测]]></title>
<url>%2F2019%2F10%2F28%2F%E4%BA%BA%E5%B7%A5%E6%99%BA%E8%83%BD%2F%E8%AF%84%E4%BC%B0%2FAB%E6%B5%8B%2F</url>
<content type="text"><![CDATA[应用场景A / B测试,即你设计的页面有两个版本(A和B),A为现行的设计, B是新的设计。比较这两个版本之间你所关心的数据(转化率,业绩,跳出率等) 。A/B测试的目的就是尝试了解新的设计是否在统计上显著地改变了转化率。 流程 对用户进行分组,分为A组与B组。A组是对照组,B组是在A组的基础上有一些改变的实验组。 分组的重点是正交分流,也就是2组的人群分布一致。在实际操作过程中,经常会出现多组实验同时进行,为了避免实验相互干扰,保证每组实验的独立性;就应该保证每个实验过程中,实验组与对照组的人群分布一致,即正交分流。 例如淘宝App对用户登陆页面与用户注册页面都进行了修改(用户登陆页面—>跳转注册页面)。在对注册页面进行AB测试的过程中,需要保证AB两组中用户登陆页面的人群分布一致;如果A组中的用户80%来自登陆页面修改之后的用户,B组用户20%来自登陆页面修改之后的用户,这样很难确定是用户登陆页面还是注册页面对AB两组产生了影响。 使用显著性检验,观察A组与B组是否发生了在统计上发生了显著性的变化。从而判断B组的改变是否有效。这就是数学原理的关键所在。 数学原理分流引擎AB测副作用探索性难题多臂赌博机]]></content>
<categories>
<category>人工智能</category>
<category>评估</category>
</categories>
<tags>
<tag>模型评估</tag>
<tag>显著性检验</tag>
</tags>
</entry>
<entry>
<title><![CDATA[terminal]]></title>
<url>%2F2019%2F10%2F27%2Flinux%2Fterminal%2F</url>
<content type="text"><![CDATA[windows terminal添加到右键 检查常量 123echo %USERPROFILE%echo %LOCALAPPDATA% 创建文件夹用来存放图标与注册表 创建文件夹terminal 1mkdir "%USERPROFILE%\AppData\Local\Terminal" 存放图标 将terminal的图标存放在mkdir "%USERPROFILE%\AppData\Local\Terminal"中 添加注册表 创建 addwt.reg文件,在文件中写入以下内容,并使用管理员身份运行。 12345678Windows Registry Editor Version 5.00[HKEY_CLASSES_ROOT\Directory\Background\shell\wt]@="Windows Terminal here""Icon"="%USERPROFILE%\\AppData\\Local\\Terminal\\terminal.ico"[HKEY_CLASSES_ROOT\Directory\Background\shell\wt\command]@="C:\\Users\\[你的电脑用户名!你的电脑用户名!]\\AppData\\Local\\Microsoft\\WindowsApps\\wt.exe" 配置123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452{ "$schema": "https://aka.ms/terminal-profiles-schema", "globals" : { "alwaysShowTabs" : true, "copyOnSelect" : false, "defaultProfile" : "{c6eaf9f4-32a7-5fdc-b5cf-066e8a4b1e40}", //默认终端 "initialCols" : 120, "initialRows" : 30, "keybindings" : [ { "command" : "closePane", "keys" : [ "ctrl+shift+w" ] }, { "command" : "copy", "keys" : [ "ctrl+shift+c" ] }, { "command" : "duplicateTab", "keys" : [ "ctrl+shift+d" ] }, { "command" : "newTab", "keys" : [ "ctrl+shift+t" ] }, { "command" : "newTabProfile0", "keys" : [ "ctrl+shift+1" ] }, { "command" : "newTabProfile1", "keys" : [ "ctrl+shift+2" ] }, { "command" : "newTabProfile2", "keys" : [ "ctrl+shift+3" ] }, { "command" : "newTabProfile3", "keys" : [ "ctrl+shift+4" ] }, { "command" : "newTabProfile4", "keys" : [ "ctrl+shift+5" ] }, { "command" : "newTabProfile5", "keys" : [ "ctrl+shift+6" ] }, { "command" : "newTabProfile6", "keys" : [ "ctrl+shift+7" ] }, { "command" : "newTabProfile7", "keys" : [ "ctrl+shift+8" ] }, { "command" : "newTabProfile8", "keys" : [ "ctrl+shift+9" ] }, { "command" : "nextTab", "keys" : [ "ctrl+tab" ] }, { "command" : "openNewTabDropdown", "keys" : [ "ctrl+shift+space" ] }, { "command" : "openSettings", "keys" : [ "ctrl+," ] }, { "command" : "paste", "keys" : [ "ctrl+shift+v" ] }, { "command" : "prevTab", "keys" : [ "ctrl+shift+tab" ] }, { "command" : "scrollDown", "keys" : [ "ctrl+shift+down" ] }, { "command" : "scrollDownPage", "keys" : [ "ctrl+shift+pgdn" ] }, { "command" : "scrollUp", "keys" : [ "ctrl+shift+up" ] }, { "command" : "scrollUpPage", "keys" : [ "ctrl+shift+pgup" ] }, { "command" : "switchToTab0", "keys" : [ "ctrl+alt+1" ] }, { "command" : "switchToTab1", "keys" : [ "ctrl+alt+2" ] }, { "command" : "switchToTab2", "keys" : [ "ctrl+alt+3" ] }, { "command" : "switchToTab3", "keys" : [ "ctrl+alt+4" ] }, { "command" : "switchToTab4", "keys" : [ "ctrl+alt+5" ] }, { "command" : "switchToTab5", "keys" : [ "ctrl+alt+6" ] }, { "command" : "switchToTab6", "keys" : [ "ctrl+alt+7" ] }, { "command" : "switchToTab7", "keys" : [ "ctrl+alt+8" ] }, { "command" : "switchToTab8", "keys" : [ "ctrl+alt+9" ] } ], "requestedTheme" : "system", "showTabsInTitlebar" : true, "showTerminalTitleInTitlebar" : true, "wordDelimiters" : " ./\\()\"'-:,.;<>~!@#$%^&*|+=[]{}~?\u2502" }, "profiles" : [ { "acrylicOpacity" : 0.75, // 亚克力效果透明度 // "background" : "#123456", // 背景颜色 "backgroundImage" : "C://terminalpicture//powershell.png", // 背景图片地址,路径中不能有中文 "backgroundImageOpacity" : 0.3, // 透明度 "backgroundImageStretchMode" : "uniformToFill", "closeOnExit" : true, "colorScheme" : "Campbell", // 配色方案 "commandline" : "powershell.exe", // 打开终端的命令 "cursorColor" : "#FFFFFF", // 游标的颜色 "cursorShape" : "bar", "fontFace" : "Consolas", "fontSize" : 10, // 字体大小 "guid" : "{61c54bbd-c2c6-5271-96e7-009a87ff44bf}", "historySize" : 9001, "icon" : "ms-appx:///ProfileIcons/{61c54bbd-c2c6-5271-96e7-009a87ff44bf}.png", // 图标地址 "name" : "Windows PowerShell", // 终端的名称 "padding" : "0, 0, 0, 0", "snapOnInput" : true, "startingDirectory" : "./", "useAcrylic" : false // 使用亚克力效果 }, { "acrylicOpacity" : 0.75, // "background" : "#0C0C0C", // 背景颜色 "backgroundImage" : "C://terminalpicture//cmd.jpg", // 背景图片地址 "backgroundImageOpacity" : 0.9, // 透明度 "backgroundImageStretchMode" : "uniformToFill", "closeOnExit" : true, "colorScheme" : "Campbell", "commandline" : "cmd.exe", "cursorColor" : "#FFFFFF", "cursorShape" : "bar", "fontFace" : "Consolas", "fontSize" : 10, "guid" : "{0caa0dad-35be-5f56-a8ff-afceeeaa6101}", "historySize" : 9001, "icon" : "ms-appx:///ProfileIcons/{0caa0dad-35be-5f56-a8ff-afceeeaa6101}.png", "name" : "cmd", "padding" : "0, 0, 0, 0", "snapOnInput" : true, "startingDirectory" : "./", "useAcrylic" : false }, { "acrylicOpacity" : 0.59999999999999998, "closeOnExit" : false, "colorScheme" : "Vintage", "commandline" : "Azure", "connectionType" : "{d9fcfdfa-a479-412c-83b7-c5640e61cd62}", "cursorColor" : "#FFFFFF", "cursorShape" : "bar", "fontFace" : "Consolas", "fontSize" : 10, "guid" : "{b453ae62-4e3d-5e58-b989-0a998ec441b8}", "historySize" : 9001, "icon" : "ms-appx:///ProfileIcons/{b453ae62-4e3d-5e58-b989-0a998ec441b8}.png", "name" : "Azure Cloud Shell", "padding" : "0, 0, 0, 0", "snapOnInput" : true, "startingDirectory" : "./", "useAcrylic" : true }, { "acrylicOpacity" : 0.5, "background" : "#0C0C0C", // 背景颜色 "backgroundImage" : "C://terminalpicture//ubuntu.jpg", // 背景图片地址 "backgroundImageOpacity" : 0.3, // 透明度 "backgroundImageStretchMode" : "uniformToFill", "closeOnExit" : true, "colorScheme" : "Campbell", "commandline" : "wsl.exe -d Ubuntu-18.04", "cursorColor" : "#FFFFFF", "cursorShape" : "bar", "fontFace" : "Consolas", "fontSize" : 10, "guid" : "{c6eaf9f4-32a7-5fdc-b5cf-066e8a4b1e40}", "historySize" : 9001, "icon" : "ms-appx:///ProfileIcons/{9acb9455-ca41-5af7-950f-6bca1bc9722f}.png", "name" : "Ubuntu-18.04", "padding" : "0, 0, 0, 0", "snapOnInput" : true, "startingDirectory" : "./", "useAcrylic" : false } ], "schemes" : [ { "background" : "#0C0C0C", "black" : "#0C0C0C", "blue" : "#0037DA", "brightBlack" : "#767676", "brightBlue" : "#3B78FF", "brightCyan" : "#61D6D6", "brightGreen" : "#16C60C", "brightPurple" : "#B4009E", "brightRed" : "#E74856", "brightWhite" : "#F2F2F2", "brightYellow" : "#F9F1A5", "cyan" : "#3A96DD", "foreground" : "#CCCCCC", "green" : "#13A10E", "name" : "Campbell", "purple" : "#881798", "red" : "#C50F1F", "white" : "#CCCCCC", "yellow" : "#C19C00" }, { "background" : "#000000", "black" : "#000000", "blue" : "#000080", "brightBlack" : "#808080", "brightBlue" : "#0000FF", "brightCyan" : "#00FFFF", "brightGreen" : "#00FF00", "brightPurple" : "#FF00FF", "brightRed" : "#FF0000", "brightWhite" : "#FFFFFF", "brightYellow" : "#FFFF00", "cyan" : "#008080", "foreground" : "#C0C0C0", "green" : "#008000", "name" : "Vintage", "purple" : "#800080", "red" : "#800000", "white" : "#C0C0C0", "yellow" : "#808000" }, { "background" : "#282C34", "black" : "#282C34", "blue" : "#61AFEF", "brightBlack" : "#5A6374", "brightBlue" : "#61AFEF", "brightCyan" : "#56B6C2", "brightGreen" : "#98C379", "brightPurple" : "#C678DD", "brightRed" : "#E06C75", "brightWhite" : "#DCDFE4", "brightYellow" : "#E5C07B", "cyan" : "#56B6C2", "foreground" : "#DCDFE4", "green" : "#98C379", "name" : "One Half Dark", "purple" : "#C678DD", "red" : "#E06C75", "white" : "#DCDFE4", "yellow" : "#E5C07B" }, { "background" : "#FAFAFA", "black" : "#383A42", "blue" : "#0184BC", "brightBlack" : "#4F525D", "brightBlue" : "#61AFEF", "brightCyan" : "#56B5C1", "brightGreen" : "#98C379", "brightPurple" : "#C577DD", "brightRed" : "#DF6C75", "brightWhite" : "#FFFFFF", "brightYellow" : "#E4C07A", "cyan" : "#0997B3", "foreground" : "#383A42", "green" : "#50A14F", "name" : "One Half Light", "purple" : "#A626A4", "red" : "#E45649", "white" : "#FAFAFA", "yellow" : "#C18301" }, { "background" : "#002B36", "black" : "#073642", "blue" : "#268BD2", "brightBlack" : "#002B36", "brightBlue" : "#839496", "brightCyan" : "#93A1A1", "brightGreen" : "#586E75", "brightPurple" : "#6C71C4", "brightRed" : "#CB4B16", "brightWhite" : "#FDF6E3", "brightYellow" : "#657B83", "cyan" : "#2AA198", "foreground" : "#839496", "green" : "#859900", "name" : "Solarized Dark", "purple" : "#D33682", "red" : "#DC322F", "white" : "#EEE8D5", "yellow" : "#B58900" }, { "background" : "#FDF6E3", "black" : "#073642", "blue" : "#268BD2", "brightBlack" : "#002B36", "brightBlue" : "#839496", "brightCyan" : "#93A1A1", "brightGreen" : "#586E75", "brightPurple" : "#6C71C4", "brightRed" : "#CB4B16", "brightWhite" : "#FDF6E3", "brightYellow" : "#657B83", "cyan" : "#2AA198", "foreground" : "#657B83", "green" : "#859900", "name" : "Solarized Light", "purple" : "#D33682", "red" : "#DC322F", "white" : "#EEE8D5", "yellow" : "#B58900" } ]}]]></content>
<categories>
<category>linux</category>
<category>wsl</category>
</categories>
<tags>
<tag>wsl</tag>
<tag>terminal</tag>
</tags>
</entry>
<entry>
<title><![CDATA[anaconda与conda]]></title>
<url>%2F2019%2F10%2F25%2F%E7%BC%96%E7%A8%8B%E8%AF%AD%E8%A8%80%2Fpython%E8%AF%AD%E8%A8%80%2Fanaconda%E4%B8%8Econda%2F</url>
<content type="text"><![CDATA[conda环境管理12345conda info --envs #查看环境conda create -n my_env_name python=3.6 #创建环境conda remove -n my_env_name --all #删除环境source activate my_env_name #激活环境source deactivate my_env_name #关闭环境]]></content>
<categories>
<category>编程语言</category>
<category>python</category>
</categories>
<tags>
<tag>环境搭建</tag>
<tag>python</tag>
</tags>
</entry>
<entry>
<title><![CDATA[linux软件安装原理]]></title>
<url>%2F2019%2F09%2F26%2Flinux%2Flinux%E8%BD%AF%E4%BB%B6%E5%AE%89%E8%A3%85%E5%8E%9F%E7%90%86%2F</url>
<content type="text"><![CDATA[在linux下所有的东西都是以“文件”的形式表示的,那些可以运行的程序有2种, 脚本文件,脚本文件由解释程序执行,一般有shell脚本、perl脚本、python脚本等等。 二进制文件,也就是经过编译器编译、联接形成的只有0和1组成的文件(计算机只运行0和1组成的程序),c、java等程序都是这种程序。 因此,只要某个程序所需要的全部“文件”都存在于正确的位置上,那么这个程序就可以运行。(这个现象在windows下是不全部适用的,如果你复制某个缺少的文件到windows的系统目录,并不一定能使你需要的程序运行起来。)对程序而言,还有一些是以纯文本形式存在的配置文件,用户可以通过定制配置文件来控制程序的运行结果等等。 安装方式 说明 源码包-》需要编译成二进制文件 优点:开源,如果有足够的能力,可以修改源代码;   可以自由选择所需的功能;   软件是编译安装,所以更加适合自己的系统,更加稳定、效率更高;   卸载方便缺点:安装过程步骤较多,尤其安装较大的软件集合时(如LAMP环境搭建),容易出现拼写错误;   编译过程时间较长,安装比二进制安装时间长;   因为是编译安装,安装过程中一旦报错新手很难解决; 二进制包(RPM包—>Red Hat、.deb包—>Debian) 优点:包管理系统简单,只通过几个命令就可以实现包的安装、升级、查询和卸载   安装速度比源码包快缺点:经过编译,不再可以看到源代码;   功能选择不如源码包灵活;   依赖性 yum、apt-get在线安装 可以方便的解决RPM安装依赖文件,一条命令就可以帮用户从网上(本地也可以)找到安装包进行安装。 脚本安装包 所谓的脚本安装包如:lnmp/lamp LNMP一键安装包,就是把复杂的软件包安装过程写成了程序脚本,初学者可以执行脚本实现一键安装。但实际安装的还是源码包和二进制包 源码包安装安装位置 源码保存位置:/usr/local/src 软件安装位置:/usr/local 安装步骤环境准备 由于源码都是c语言写的,所以要先安装c语言编译器:gcc 确保gcc编译器安装成功 1gcc -v # 是否能打印你使用gcc版本信息 下载源码包,解压 123http://mirror.bit.edu.cn/apache/httpd/ #下载apache2tar -zvxf httpd-2.2.31.tar.gz #解压apache2cp .. /usr/local/src #将源代码保存到/usr/local/src目录 安装 在源码包目录,执行./configure,配置安装程序 执行./configure命令,该命令用于软件配置与检查(基本上每个源码包都会有该命令,即使个别的没有该命令,也会提供相关替代命令) 定义需要的功能选项; 检测系统环境是否符合安装要求; 把第一项中定义好的功能选项和第二项中检测系统环境的信息都写入Makefile文件,用于后续的编辑。(后续的【make】和【make install】命令都会依赖该文件) 执行命令./configure --prefix=/usr/local/apache2,该命令用于指定安装位置为:/usr/local/apache2(其中的apache2目录不需要提前创建,make install命令执行时会自动创建)。命令执行后,会在当前目录生成Makefile文件。在用源码包进行安装时,最好指定路径,因为如果指定路径只要删除/usr/local/XXX就可以了;如果不指定路径,此时程序的文件将保存在/usr/local/bin、/use/local/etc等文件夹中,卸载较麻烦 执行make depend命令检查依赖库 执行make编译源码 执行make install命令,安装程序,此时会创建/usr/local/apache2目录 RPM命令管理(包依赖很难解决) rpm 只能安装已经下载到本地机器上的rpm 包。RPM包命名规则如下: 123#软件包名-软件版本-软件发布的次数-适合的`Linux`平台-适合的硬件平台-包扩展名httpd-2.2.15-15.el6.centsos.1.i686.rpm#httpd软件包名-2.2.15软件版本-15发布的次数-el6.centos适合的Linux平台-i686适应的硬件平台-rpm包扩展名,el6是redhat的企业版 RPM 包默认安装位置 目录 说明 /etc/ 配置文件安装目录 /usr/bin/ 可执行的命令安装目录 /usr/lib/ 程序所使用的函数库保存位置 /usr/share/doc/ 基本的软件使用手册保存位置 /usr/share/man/ 帮助文件保存位置 yum在线安装将所有软件包放到官方服务器上,当进行yum在线安装时,可以自动解决依赖性问题。(rpm缺点:安装过程中,rpm包依赖性太强)。 命令 说明 yun list 查询所有可用软件包列表 yum search 关键字 —搜索服务器上所有和关键字相关的包 yum -y install -y 自动回答yes yum -y update 升级 yum -y remove 卸载 脚本安装安装路径说明/usr和/usr/local目录 在传统的unix系统中,/usr通常只包含系统发行时自带的程序,而/usr/local则是本地系统管理员用来自由添加程序的目录。(这样可能在升级新版系统或新distribution时无须重新安装全部程序. ) 对于Linux发行版,如 RedHat, Debian 等等,一个可能的规定是:/usr目录只能由发行版的软件包管理工具负责管理,而对/usr/local却没有这样做。正是因为采用这种方式,软件包管理工具的数据库才能知道在/usr目录内的每一个文件。 /bin,/sbin,/usr/sbin,/usr/bin /sbin 一般是指超级用户指令。主要放置一些系统管理的必备命令例如:cfdisk、dhcpcd、dump、e2fsck、fdisk、halt、ifconfig、ifup、 ifdown、init、insmod、lilo、lsmod、mke2fs、modprobe、quotacheck、reboot、rmmod、 runlevel、shutdown等。 /bin 超级用户和一般的用户都可以使用。。bin为binary的简写主要放置一些普通命令例如:cat、cp、chmod df、dmesg、gzip、kill、ls、mkdir、more、mount、rm、su、tar等。 /usr/bin 是你在后期安装的一些软件的运行脚本。主要放置一些应用软件工具的必备命令例如c++、g++、gcc、chdrv、diff、dig、du、eject、elm、free、gnome、 gzip、htpasswd、kfm、ktop、last、less、locale、m4、make、man、mcopy、ncftp、 newaliases、nslookup passwd、quota、smb、wget等。 /usr/sbin 放置一些用户安装的系统管理的命令例如:dhcpd、httpd、imap、in.*d、inetd、lpd、named、netconfig、nmbd、samba、sendmail、squid、swap、tcpd、tcpdump等。 opt目录/opt这个目录是一些大型软件的安装目录,或者是一些服务程序的安装目录,当不使用时,直接删除目录就可以了]]></content>
<categories>
<category>linux</category>
<category>环境</category>
</categories>
<tags>
<tag>操作系统</tag>
</tags>
</entry>
<entry>
<title><![CDATA[身体部位]]></title>
<url>%2F2019%2F09%2F21%2F%E9%A3%8E%E8%BD%BB%E4%BA%91%E6%B7%A1%2F%E8%BA%AB%E4%BD%93%E9%83%A8%E4%BD%8D%2F</url>
<content type="text"><![CDATA[肩部]]></content>
</entry>
<entry>
<title><![CDATA[t-SNE]]></title>
<url>%2F2019%2F09%2F19%2F%E4%BA%BA%E5%B7%A5%E6%99%BA%E8%83%BD%2F%E7%89%B9%E5%BE%81%E5%B7%A5%E7%A8%8B%2Ft-SNE%2F</url>
<content type="text"><![CDATA[t-SNE 是一种非线性降维算法,非常适用于高维数据降维到2维或者3维,进行可视化。 http://www.datakit.cn/blog/2017/02/05/t_sne_full.html]]></content>
<categories>
<category>人工智能</category>
<category>特征工程</category>
</categories>
<tags>
<tag>特征工程</tag>
<tag>降维</tag>
</tags>
</entry>
<entry>
<title><![CDATA[pytest]]></title>
<url>%2F2019%2F09%2F18%2F%E7%BC%96%E7%A8%8B%E8%AF%AD%E8%A8%80%2Fpython%E8%AF%AD%E8%A8%80%2Fpytest%2F</url>
<content type="text"><![CDATA[目录结构应用12345├── feature ├── in_fc.py #开发文件 └── test #测试目录 ├── __init__.py #子目录需要添加 └── test_in_fc.py #测试 in_fc.py文件12345def one(): return 1def Plus_One(x): return x+1 __init__.py空 test_in_fc.py123456789#!/usr/bin/env python3 -m pytest# coding=utf-8import in_fcdef test_one(): assert in_fc.one() == 1def test_Plus_One(): assert in_fc.Plus_One(1) == 2 或 123456789#!/usr/bin/env python3 -m pytest# coding=utf-8import in_fcclass Test_in_fc: def test_one(self): assert in_fc.one() == 1 def test_Plus_One(self): assert in_fc.Plus_One(1) == 2 运行1/usr/bin/env python3 -m pytest 文件]]></content>
<categories>
<category>编程语言</category>
<category>python</category>
</categories>
<tags>
<tag>python</tag>
<tag>测试框架</tag>
</tags>
</entry>
<entry>
<title><![CDATA[ssh]]></title>
<url>%2F2019%2F09%2F03%2Flinux%2Fssh%2F</url>
<content type="text"><![CDATA[生成ssh密钥对1ssh-keygen -t rsa -C 'xxx@xxx.com' 按3次回车(不使用密码)生成下图。密钥对在~/.ssh文件夹中,将id_rsa.pub(公钥)提交到github或者gitlab中 安装ssh12sudo apt-get install openssh-client #客户端 类似于xshell,登陆服务器sudo apt-get install openssh-server #服务端 用于服务器,让其他客户端远程登陆 远程登陆服务器shell脚本远程自动登陆服务器目前远程登陆服务器一般需要1. 登陆跳板机;2. 登陆服务器。 ssh跳板机->输入静态密码+token码 在跳板机ssh服务器->输入密码 解决方法1sudo apt-get install expect #安装expect 在expect中写入,不同公司的登陆流程不一样,需要修改登陆流程。但是登陆模板如图所示。其中set是设置变量;expect是设置返回的字符串;send是发送的字符串。 123456789101112131415161718192021222324252627282930313233343536#!/usr/bin/expectset salt [lindex $argv 0] #跳板机的token码set username 跳板机地址set password 跳板机静态密码set serverIP 服务器IP地址set server 服务器地址set serverpass 服务器密码spawn ssh "$username"expect { "*yes/no*" { #如果遇到yes/no输入yes send "yes\n"; exp_continue; } "*Password:" { #如果遇到Password send "$password $salt\n"; #静态密码+' '+token密码 exp_continue; } "Please input server keyword" { #输入服务器IP地址 send $serverIP; exp_continue; } "Select account" { #选择账户 send "powerop\r"; exp_continue; } "server" { #从跳板机ssh服务器 send "ssh $server\r"; exp_continue; } "*password:" { #输入服务器密码 send "$serverpass\r"; exp_continue; }}interact]]></content>
<categories>
<category>linux</category>
<category>远程登陆</category>
</categories>
<tags>
<tag>git</tag>
<tag>分布式</tag>
<tag>远程登陆</tag>
<tag>spark</tag>
</tags>
</entry>
<entry>
<title><![CDATA[git原理]]></title>
<url>%2F2019%2F09%2F03%2Flinux%2Fgit%2Fgit%E5%8E%9F%E7%90%86%2F</url>
<content type="text"><![CDATA[简介 Git 和其它版本控制系统(包括 Subversion 和近似工具)的主要差别在于 Git 对待数据的方法。 大部分版本控制系统,例如(CVS、Subversion、Perforce、Bazaar 等等),采用增量文件系统,存储每个文件版本的差异; Git 更像是把数据看作是对小型文件系统的一组快照。 每次你提交更新,它主要对当时的全部文件制作一个快照并保存这个快照的索引。 为了高效,如果文件没有修改,Git 不再重新存储该文件,而是只保留一个链接指向之前存储的文件;如果文件发生修改,Git 将存储该文件全部内容。 增量文件系统 快照文件系统 存储原理目录结构1sudo apt-get install tree #安装tree命令 1234567891011121314151617181920212223242526272829303132333435363738394041424344$ tree .git.git├── COMMIT_EDITMSG #本地最后一个提交的信息├── ORIG_HEAD #当前远程库的HEAD├── HEAD #本地库的当前HEAD ├── config #git配置信息,包括用户名,email,remote repository的地址,本地branch和remote branch 的follow关系├── description #该git库的描述信息,文件仅供 GitWeb 程序使用。├── index #文件存储着暂存区的内容信息├── hooks #钩子程序,可以被用于在执行git命令时自动执行一些特定操作,例如加入changeid│ ├── applypatch-msg.sample│ ├── commit-msg.sample│ ├── fsmonitor-watchman.sample│ ├── post-update.sample│ ├── pre-applypatch.sample│ ├── pre-commit.sample│ ├── pre-push.sample│ ├── pre-rebase.sample│ ├── pre-receive.sample│ ├── prepare-commit-msg.sample│ └── update.sample├── info #info目录下的exclude文件包含项目全局忽略匹配模式,与.gitignore文件互补。│ └── exclude├── logs │ ├── HEAD│ └── refs│ ├── heads #本地仓库对应分支所有操作│ │ └── hexo│ └── remotes #远程仓库对应分支所有操作│ └── origin│ ├── HEAD│ ├── hexo├── objects #目录存储着Git数据库的所有内容,包括三类对象commit,tree和blob│ ├── e8│ │ └── 4551234f24b6da002d962a26c2495ea16a425f│ ├── info│ └── pack└── refs ├── heads #本地库各个分支对应的当前指针 │ └── master ├── remotes #远程库各个分支对应的当前指针 │ └── origin │ ├── HEAD #远程仓库当前分支 │ └── master #远程库master分支对应的指针 └── tags #本地库tag的当前指针 object对象object对象包括Blob对象、Tree对象与Commit对象,对象的所有索引全部采用哈希值进行索引。 Blob对象:二进制数据块存储文件的内容,如果两个文件在同一个版本仓库中,那么它们将会共享同一个blob对象,例如文件夹e8中的文件4551234f24b6da002d962a26c2495ea16a425f其中存储的便是文件内容。 Tree对象:存储目录结构,指向其他tree对象或blob对象。 Commit对象:用于存储版本信息,tree存储tree SHA1签名;parent存储上一个版本的commit SHA1签名;author,作者的信息;committer实际提交者的信息 git指针主要介绍refs/heads、refs/tags、refs/remotes、HEAD、ORIG_HEAD等 HEAD:本地库的HEAD,保存refs/中的文件。例如refs/heads/master ORIG_HEAD:当进行一些有风险的操作的时候,如reset、merge或者rebase,Git会将HEAD原来所指向commit对象的sha-1值存放于ORIG_HEAD文件中。也就是说ORIG_HEAD可以让我们找到进行最近一次危险操作之前的HEAD位置 可以使用 git reset --hard ORIG_HEAD回退到上一次reset之前。 refs/heads:存储各个分支信息,保存有commit SHA1签名 refs/tags:存储各个tag信息,保存有commit SHA1签名 refs/remotes/repertoryName/HEAD:远程库的当前分支 refs/remotes/repertoryName/remoteBranchName:远程仓库对应分支最后一次commit HEAD里面的内容是当前的ref,而当前ref的内容是commit SHA1, commit 对象内容是tree SHA1, tree对象的内容是文件夹/文件信息,而blob对象存储着文件的具体内容。 命令git add将文件添加进暂存区,将文件保存为blob对象存储在object目录下,但并不会创建tree对象与commit对象,而是更新index文件(即暂存区)。 git commit生成tree对象与commit对象,存储版本信息。tree对象用于记录目录信息,将暂存区(index文件)转换为tree对象;commit对象用于记录版本信息,指向tree对象。移动指针,并更新refs\heads、refs\tags里的commit SHA1签名,与当前HEAD的信息。 本地分支 命令 注释 备注 branch 创建分支 \ref\heads创建分支 check out 切换分支 将HEAD指针指向另一个本地分支。 merge 合并分支 将出现分叉提交的分支整合在一起时 分支合并 远程分支 命令 注释 备注 git clone 克隆 远程下载 git fetch 获取 将远程内容下拉到本地版本库 远程仓库,并且有一些提交记录 。拉取下来 其他人提交了一些修改至远程仓库 你在本地分支上继续开发 你进行拉取你本地没有的数据 变基合并 创建了一个特性分支 server,为服务端添加了一些功能,提交了 C3 和 C4。 然后从 C3 上创建了特性分支 client,为客户端添加了一些功能,提交了 C8 和 C9。 最后,你回到 server 分支,又提交了 C10。 假设你希望将 client 中的修改合并到主分支并发布,但暂时并不想合并 server 中的修改 1$ git rebase --onto master server client 进行合并 client 分支 12$ git checkout master$ git merge client 进行合并 server 分支 1$ git rebase master server]]></content>
<categories>
<category>linux</category>
<category>版本管理</category>
</categories>
<tags>
<tag>git</tag>
<tag>版本管理</tag>
</tags>
</entry>
<entry>
<title><![CDATA[git使用]]></title>
<url>%2F2019%2F08%2F28%2Flinux%2Fgit%2Fgit%E4%BD%BF%E7%94%A8%2F</url>
<content type="text"><![CDATA[Git 是一个开源的分布式版本控制系统,用于敏捷高效地处理任何或小或大的项目。知名的git管理平台有github 本地git简介git分为工作区、暂存器和版本库。 工作区:就是你在电脑里能看到的目录。 暂存区:英文叫stage, 或index。一般存放在 “.git目录下” 下的index文件(.git/index)中,所以我们把暂存区有时也叫作索引(index)。 版本库:工作区有一个隐藏目录.git,这个不算工作区,而是Git的版本库。图中”HEAD” 实际是指向 master 分支的一个”游标”。所以图示的命令中出现 HEAD 的地方可以用 master 来替换。 object:Git 的对象库,实际位于 “.git/objects” 目录下,里面包含了创建的各种对象及内容。 命令说明 基本操作 git init 创建版本库 git add <文件> 将文件从工作区添加到暂存区 git commit -m“<说明>” 将文件从暂存区添加到版本库 git checkout <文件> 将文件从暂存区添加到工作区 git rm --cached <文件> 将暂存区中的文件删除 git reset head 将文件从版本库添加到暂存区 git checkout head <文件> 将文件从版本库添加到工作区 版本管理 git log 查看版本信息 ——查找commit id--graph:以图的形式展现信息 git reflog 查看历史操作信息——查找commit id以防因为版本回滚导致git log中的信息丢失 git reset --hard HEAD^git reset --hard HEAD^^git reset --HEAD~100git reset --hard 1094 # 回退上一个版本# 回退上两个版本# 回退上100个版本# 回退到指定版本,后面的1094a是commit的版本号 标签 git tag -a v0.1 -m "version 0.1 released" 1094adb -a指定标签名,-m指定说明文字 git tag v0.1 commit id 默认标签是打在最新提交的commit id如果添加commit id就会打在指定的commit id上 git tag -d v0.1 删除标签 git push origin v1.0git push origin --tags 推送标签到远程一次性推送全部标签 git taggit show <标签名> 查看标签查看标签的说明文字 保存工作区与缓存区 git stash 将工作区与暂存区的内容保存起来 git stash list 查看保存的工作区与暂存区 git stash apply 恢复制定的stash,但是stash内容并不删除。git stash apply stash@{0} git stash drop 删除制定的stash。git stash drop stash@{0} git stash pop 恢复并删除最新的工作区与暂存区 分支管理 git branch 查看分支 git branch <分支名> 新建分支,并且新分支有老分支的内容 git checkout <分支名> 切换分支 git branch -d <分支名> 删除分支 git merge <分支名>git merge --no-ff <分支名> 合并分支到当前分支下如果有冲突修改冲突,使用git add与git commit 提交 其他 git status 查看状态 git diffgit diff [--options] <commit> <commit> [--] [<path>...] git diff:比较工作区与暂存区 git diff --cached:比较暂存区与最新本地版本库 git diff --cached <commit-id>:比较暂存区与版本库指定commit-id的差异 git diff HEAD:比较工作区与最新本地版本库 git diff <commit-id>:比较工作区与版本库指定commit-id的差异 git diff <commit-id><commit-id>:比较版本库两个commit-id之间的差异 git diff <分支><分支>:比较两个分支 git rm 删除文件 .gitignore添加到.gitignore中的文件,不会被添加到暂存区,因此不会被保存到版本库中,进而不会存在版本管理。在不同文件夹下建立.gitignore可以忽略不同的文件。 1234567├── .vscode ├── feture │ ├── dev.py│ └── test #测试文件夹│ │ ├── test_dev.py #本地仓库对应分支所有操作│ └── .gitignore #写入test/ 可以忽略同级目录下test的文件夹 ├── .gitignore #写入.vscode/ 可以忽略同级目录下.vscode的文件夹 远程 命令 说明 git remote 查看当前配置有哪些远程仓库-v:执行时加上 -v 参数,你还可以看到每个别名的实际链接地址。 git remote add 连接远程版本库git remote add [shortname] [url]shortname:远程版本库的别名url:远程版本库的链接 git remote rm [别名] 删除远程仓库 git push 本地库内容推送到远程库中 git push [alias] [branch]:推送成为 [alias] 远程仓库上的 [branch] 分支 git clone 克隆远程版本库 git pull 拉取远程分支更新到本地仓库,再与本地指定分支合并。git pull <远程主机名> <远程分支名>:<本地分支名> git fetch 拉取远程分支更新到本地仓库 git merge origin/master 把远程下载下来的代码合并到本地仓库,远程的和本地的合并 git ls-files查看版本库目录 git指针问题 文件.git/index实际上就是一个包含文件索引的目录树,像是一个虚拟的工作区。在这个虚拟工作区的目录树中,记录了文件名、文件的状态信息(时间戳、文件长度等)。文件的内容并不存储其中,而是保存在Git对象库.git/objects目录中,文件索引建立了文件和对象库中对象实体之间的对应。下面这个图展示了工作区、版本库中的暂存区和版本库之间的关系。 工作区、版本库、暂存区原理图 在这个图中,可以看到部分Git命令是如何影响工作区和暂存区(stage,亦称index)的。下面就对这些命令进行简要的说明,而要彻底揭开这些命令的面纱要在接下来的几个章节。 图中左侧为工作区,右侧为版本库。在版本库中标记为index的区域是暂存区(stage,亦称index),标记为master的是master分支所代表的目录树。 图中可以看出此时HEAD实际是指向master分支的一个“游标”。所以图示的命令中出现HEAD的地方可以用master来替换。 图中的objects标识的区域为Git的对象库,实际位于.git/objects目录下,会在后面的章节重点介绍。 当对工作区修改(或新增)的文件执行git add命令时,暂存区的目录树被更新,同时工作区修改(或新增)的文件内容被写入到对象库中的一个新的对象中,而该对象的ID被记录在暂存区的文件索引中。 当执行提交操作(git commit)时,暂存区的目录树写到版本库(对象库)中,master分支会做相应的更新。即master最新指向的目录树就是提交时原暂存区的目录树。 当执行git reset HEAD命令时,暂存区的目录树会被重写,被master分支指向的目录树所替换,但是工作区不受影响。 当执行git rm –cached 命令时,会直接从暂存区删除文件,工作区则不做出改变。 当执行git checkout .或者git checkout – 命令时,会用暂存区全部或指定的文件替换工作区的文件。这个操作很危险,会清除工作区中未添加到暂存区的改动。 当执行git checkout HEAD .或者git checkout HEAD 命令时,会用HEAD指向的master分支中的全部或者部分文件替换暂存区和以及工作区中的文件。这个命令也是极具危险性的,因为不但会清除工作区中未提交的改动,也会清除暂存区中未提交的改动。]]></content>
<categories>
<category>linux</category>
<category>版本管理</category>
</categories>
<tags>
<tag>git</tag>
<tag>版本管理</tag>
</tags>
</entry>
<entry>
<title><![CDATA[PSI]]></title>
<url>%2F2019%2F08%2F26%2F%E6%9C%BA%E5%99%A8%E5%AD%A6%E4%B9%A0%2FPSI.md%2F</url>
<content type="text"><![CDATA[简介由于模型是以特定时期的样本所开发的,此模型是否适用于开发样本之外的族群,必须经过稳定性测试才能得知。稳定度指标(population stability index ,PSI)可衡量测试样本及模型开发样本评分的的分布差异,为最常见的模型稳定度评估指针。其实PSI表示的就是按分数分档后,针对不同样本,或者不同时间的样本,population分布是否有变化,就是看各个分数区间内人数占总人数的占比是否有显著变化。 PSI<0.1 0.1~0.25 PSI>0.25 稳定性很高 一般 稳定性差 计算 PSI=\sum_i^n {(Ac-Ex)} \times {ln (Ac/Ex)}其中:$Ac$是实际占比;$Ex$是期望占比 计算PSI将数据集划分为base与test两个数据集。 模型 将基准数据集base的score划分为10个区间,将测试数据集test的score按照相同的边界值划分为10个区间 分别统计各个分区基准score的个数$B_i$、预测score的个数$T_i$ 分别统计各个分区基准score的个数比例$P_B$、预测score的个数比例$T_B$ 计算 $PSI=\sum {\left(P_B(i)-P_T(i) \right)} \times {ln(P_B(i)/P_T(i))}$ 特征 将基准数据集base的特征划分为10个区间,将测试数据集test的特征按照相同的边界值划分为10个区间 分别统计各个分区基准feature的个数$B_i$、预测feature的个数$T_i$ 分别统计各个分区基准feature的个数比例$P_B$、预测feature的个数比例$T_B$ 计算 $PSI=\sum {(P_B(i)-P_T(i))} \times {ln(P_B(i)/P_T(i))}$]]></content>
<categories>
<category>人工智能</category>
<category>特征工程</category>
</categories>
<tags>
<tag>特征选择</tag>
<tag>特征工程</tag>
</tags>
</entry>
<entry>
<title><![CDATA[博客]]></title>
<url>%2F2019%2F08%2F23%2F%E5%8D%9A%E5%AE%A2%2F</url>
<content type="text"><![CDATA[使用WSL即windows的ubuntu子系统搭建博客 安装node与npm12345sudo apt-get install nodejssudo apt-get install npmnode -v #测试是否安装完成npm -v #测试是否安装完成 安装流程图插件1npm install hexo-filter-mermaid-diagrams #安装流程图插件 在themes/next/layout/_partials/footer.swig加入 12345678{% if (theme.mermaid.enable) %} <script src='https://unpkg.com/mermaid@{{ theme.mermaid.version }}/dist/mermaid.min.js'></script> <script> if (window.mermaid) { mermaid.initialize({theme: 'forest'}); } </script>{% endif %}]]></content>
<categories>
<category>博客</category>
</categories>
<tags>
<tag>环境搭建</tag>
<tag>WSL</tag>
</tags>
</entry>
<entry>
<title><![CDATA[健身动作]]></title>
<url>%2F2019%2F08%2F22%2F%E9%A3%8E%E8%BD%BB%E4%BA%91%E6%B7%A1%2F%E5%81%A5%E8%BA%AB%E5%8A%A8%E4%BD%9C%2F</url>
<content type="text"><![CDATA[部位 动作 胸部 上胸肌:上斜卧推中胸肌:平板卧推下胸肌:双杠臂屈伸 卧推卧推细节 上合适的杠铃片并用杠铃夹固定住。但当一个人训练时,可以不使用杠铃夹,这样杠铃倾斜的时候,杠铃片会掉落,从而保证不受伤。 杠铃与板凳位于正中间从而确保在卧推的过程中,杠铃两边的力量平衡 杠铃位于眼睛这个上方 握距(大臂与躯干程75°):确保杠铃在最低点,胳膊与躯干的角度在75°左右。注意千万不能在90°,因为此时肩关节达到最大活动限度,当杠铃到达最低点的时候,很容易造成肩关节损伤。。 握法:不要空握,以免杠铃滑落。手腕不要弯,保证手腕垂直,用你的大小鱼际顶住杠铃,从而使手臂承受杠铃的重量,避免手腕受伤。正确的姿势,大拇指缠绕的方法。 两脚:用力踩地面,臀部用力,感觉力量从脚向上传递,。(腿部驱动技术) 手臂外旋:保证手臂与躯干的夹角,夹角太大会损伤肩膀,配合大拇指缠绕的方式 下降点:胸部,刺激胸肌并保证小臂与地面垂直 最高点:锁骨,刺激胸肌并保证小臂与地面垂直 最重要的是肩胛骨后缩下压 卧推致命错误 卧推时双脚离地 尝试大重量卧推的时候没有人保护或不借用四方架 卧推下架和归架的移动速度过快 手腕疼:正确的办法是全握,手腕立直,中立位的方式推杠铃,用你的大小鱼际顶住杠铃。同时使用大拇指缠绕的方式 过度的外展手臂 杠铃运动轨迹 卧推时没有挺胸 臀部脱离卧推椅 疼痛 部位 原因 肩膀痛 保持肩胛骨后缩下压注意控制大臂外展幅度 手肘疼 伸肘肌群和屈肘肌群的力量失衡。保证从侧面看时前臂始终垂直于地面热身不足 手腕疼 保持手腕中立,用你的大小鱼际顶住杠铃 上斜卧推 与平板卧推不同 1. 卧推凳30°,这个角度最适合上胸肌发力2.下落点锁骨上方—》直上直下 与平板卧推相同 1. 脚像平卧一样提供动力,脚要加紧平板。为上身提供功力2. 臀部坐实不能离开凳子,(与平板卧推一样是力量举的规格)3. 挺胸收紧肩胛骨,以免肩损伤4. 手臂与躯干的夹角不能太大,以免肩损伤 ——-》握距5. 手臂与地面垂直,以免肘关节损伤6. 手臂外旋,以免肘关节损伤7. 手腕保持中立,使用大小鱼际承受重量 是否起桥,感受胸肌的发力 臂屈伸 准备动作 双手紧握双杠,锁住手腕,和小臂保持在一条直线上,避免手腕、肘部受伤。 挺胸,肩胛骨后收下压,身体前倾。以免肩关节受伤 手肘弯曲,不要锁住手肘或肘超伸,避免肘关节受伤 下降动作 缓慢下降,下降到手臂与水平面平行,感受胸肌的拉伸。下降太多,肩关节容易受伤;下降太少胸肌得不到锻炼。避免手肘外展 在最高点,手肘不要锁住,避免肘关节受伤;挺胸,肩胛骨后收下压,身体前倾。以免肩关节受;锁住手腕,和小臂保持在一条直线上,避免手腕、肘部受伤。 肩部疼痛的原因肩关节是一种球窝关节,这种关节十分灵活而且活动幅度大,但是缺乏稳定性。虽有关节囊及韧带保护,但关节囊薄而松弛,而韧带少而弱。因此肩关节脱位和劳损是比较普遍的现象。 为了避免肩关节损伤,应该保持肩关节受力平衡,如果受力不平衡,肱骨(大臂)就不能保持在关节窝的良好位置,它就会滑动,从而造成软组织受到磨损以及挤压,让你感到肩膀疼。因此在做这个动作时,应该注意以下几点: 充分的热身,增加关节滑液,保护关节软骨免受机械性损伤。 动作要循序渐进,不要逞强。注意动作的标准性 下降到大臂与水平面平行,因为下降的越多肩部受伤的可能性越大 避免圆肩含胸,保持挺胸,肩胛骨后收下压,否则会造成肩夹挤的现象(手举过头部会疼痛痛)。同时含胸会导致肩胛骨无法自由移动,如果肩胛骨没有正常活动,肩关节会承受巨大压力 身体前倾,这样不仅有利于锻炼胸肌,而且有利于保护肩部 练习时,手腕要锁住不要弯曲,和小臂保持在一条直线上,这样可以避免手腕、肘部受伤 双杠的间距不宜太宽,这样会增加肩部剪切力的力矩,同时还会肱骨头陷进关节窝的部分更少,让肩关节受力不稳定。一般建议双杠间距与肩同宽,手肘贴着身体两侧保持稳定 避免手肘外展,以免肩部和腕部的受伤 避免手肘超伸]]></content>
</entry>
<entry>
<title><![CDATA[linux子系统安装spark]]></title>
<url>%2F2019%2F08%2F22%2F%E5%A4%A7%E6%95%B0%E6%8D%AE%E6%A1%86%E6%9E%B6%2Fspark%2Flinux%E5%AD%90%E7%B3%BB%E7%BB%9F%E5%AE%89%E8%A3%85spark%2F</url>
<content type="text"><![CDATA[123JDK:1.8 Spark-2.2.0Hadoop Release:2.7.4 一般将文件安装在/opt目录下,/opt目录用来存放第三方的应用程序与文件。 安装java 下载Linux环境下的jdk1.8,请去(官网)中下载jdk的安装文件; 解压安装包 1tar -zxvf jdk-8u131-linux-x64.tar.gz 将jdk安装在usr/java当中 12mkdir /opt/java #创建/usr/java文件夹mv /home/cmfchina/jdk1.8.0_131 /opt/java #将jdk1.8.0_131移动到/opt/java文件夹 修改环境变量,使用vim /etc/profile并在末尾添加如下内容,并使用source /etc/profile让命令profile 12345export JAVA_HOME=/opt/java/jdk1.8.0_131export JRE_HOME=${JAVA_HOME}/jreexport CLASSPATH=.:${JAVA_HOME}/lib:${JRE_HOME}/lib:$CLASSPATHexport JAVA_PATH=${JAVA_HOME}/bin:${JRE_HOME}/binexport PATH=$PATH:${JAVA_PATH} 测试是否安装成功 使用javac命令,不会出现command not found错误 使用java -version,出现版本为java version "1.8.0_131" echo $PATH,看看自己刚刚设置的的环境变量配置是否都正确 安装scala 从官网:http://www.scala-lang.org/ 下载Scala 解压压缩包 1tar -zxvf scala-2.12.2.tgz 将scala安装在opt/scala`当中 12mkdir /opt/scala mv scala-2.12.2 /usr/scala 配置Scala环境变量 12export SCALA_HOME=/opt/scala/scala-2.12.2export PATH=$PATH:${SCALA_HOME}/bin 在该文件的PATH变量中增加下面的内容{SCALA_HOME}/bin 运行环境变量 1source /etc/profile 测试Scala是否安装成功 1scala -version ssh免密登陆 安装ssh,win10的ubuntu子系统使默认安装好的 12sudo apt-get install openssh-clientsudo apt-get install openssh-server 启动、重启ssh服务 12sudo service ssh startsudo service ssh restart 可能报错 123Could not load host key: /etc/ssh/ssh_host_rsa_keyCould not load host key: /etc/ssh/ssh_host_ecdsa_keyCould not load host key: /etc/ssh/ssh_host_ed25519_key 解决方法 123ssh-keygen -t rsa -f /etc/ssh/ssh_host_rsa_keyssh-keygen -t ecdsa -f /etc/ssh/ssh_host_ecdsa_keyssh-keygen -t ed25519 -f /etc/ssh/ssh_host_ed25519_key 配置ssh端口。ssh端口默认是22号端口,修改/etc/ssh/sshd_config 1234Port 22 #如果和win10的端口号冲突,可以改为其他的UsePrivilegeSeparation noPermitRootLogin no #如果你确实要用root方式登录的话设为yesPasswordAuthentication yes 配置免密登陆 123ssh-keygen -t rsa -P '' -f ~/.ssh/id_rsa 生成密钥ssh-copy-id -i ~/.ssh/id_rsa.pub <username>@localhost 将公钥发送至Ubuntu ,其中<username> 为你的用户名ssh localhost 查看是否不需要密码登录 安装hadoop 下载hadoop。官网。清华 解压配置环境变量 1234sudo tar -zvxf hadoop-2.7.7.tar.gz #解压缩sudo mv hadoop-2.7.7 /opt/hadoop #复制文件sudo vim /etc/profile #配置全局变量sudo source /etc/profile #生效 设置JAVA_HOME路径。修改etc/hadoop/hadoop-env.sh文件中的JAVA_HOME变量,添加export JAVA_HOME=/mnt/d/usr/local/jdk 修改etc/hadoop/core-site.xml文件。(注意文件夹的权限) 123456789101112<configuration> <!--指定NamNode通信地址--> <property> <name>fs.defaultFS</name> <value>hdfs://localhost:9000</value> </property> <!--指定Hadoop运行时产生文件的存储路径--> <property> <name>hadoop.tmp.dir</name> <value>file:/opt/hadoop/tmp</value> </property></configuration> 修改etc/hadoop/hdfs-site.xml文件 12345678910111213141516171819<configuration> <property> <name>dfs.name.dir</name> <value>file:/opt/hadoop/hdfs/name</value> <description>namenode上存储hdfs名字空间元数据 </description> </property> <property> <name>dfs.data.dir</name> <value>file:/opt/hadoop/hdfs/data</value> <description>datanode上数据块的物理存储位置</description> </property> <!-- 设置hdfs副本数量 --> <property> <name>dfs.replication</name> <value>1</value> </property></configuration> 编辑mapred-site.xml配置文件,配置mapreducer框架运行在yarn上: 键入如下命令复制mapred-site.xml配置文件:cp mapred-site.xml.template mapred-site.xml mapred-site.xml配置文件内容如下: 1234567<configuration><!-- 通知框架MR使用YARN --> <property> <name>mapreduce.framework.name</name> <value>yarn</value> </property></configuration> 编辑yarn配置yarn-site.xml,添加如下内容: 1234567<configuration><!--reducer取数据的方式是mapreduce_shuffle--> <property> <name>yarn.nodemanager.aux-services</name> <value>mapreduce_shuffle</value> </property></configuration> 建立文件夹 graph LR A[hadoop]-->B[tmp] A-->C[hdfs] C-->D[data] C-->E[name] 格式化hdfs;启动hdfs和yarn 12hdfs namenode -formatstart-all.sh 检测是否成功 12345678jps5074 SecondaryNameNode2003 NodeManager8008 Jps6505 NameNode6700 DataNode1663 ResourceManager 安装Spark 我们到Spark官网进行下载:http://spark.apache.org/ ,我们选择带有Hadoop版本的Spark,如图所示: 解压Spark,将spark安装进/opt/spark.同上 配置spark 12cp spark/conf/slaves.template ./spark/conf/slavescp spark/conf/spark-env.sh.template ./spark/conf/spark-env.sh 修改spark-env.sh 12345678export SCALA_HOME=/opt/scala/scala-2.12.2export JAVA_HOME=/opt/java/jdk1.8.0_131export HADOOP_HOME=/opt/hadoop/hadoop-2.7.7export HADOOP_CONF_DIR=${HADOOP_HOME}/etc/hadoopexport SPARK_MASTER_IP=localhostexport SPARK_LOCAL_IP=localhostexport SPAPK_LOCAL_DIRS=/opt/spark/spark-2.4.3-bin-hadoop2.7export SPARK_DRIVER_MEMORY=1G 启动spark-shell]]></content>
<categories>
<category>大数据平台</category>
<category>spark</category>
</categories>
<tags>
<tag>环境搭建</tag>
<tag>WSL</tag>
<tag>分布式</tag>
<tag>spark</tag>
</tags>
</entry>
<entry>
<title><![CDATA[数据分箱]]></title>
<url>%2F2019%2F08%2F21%2F%E4%BA%BA%E5%B7%A5%E6%99%BA%E8%83%BD%2F%E7%89%B9%E5%BE%81%E5%B7%A5%E7%A8%8B%2F%E6%95%B0%E6%8D%AE%E5%88%86%E7%AE%B1%2F</url>
<content type="text"><![CDATA[分箱方法是一种简单常用的预处理方法。所谓“分箱”,实际上就是按照属性值划分的子区间(箱子),把待处理的数据(某列属性值)按照一定的规则放进子区间中。在采用分箱技术时,需要确定的两个主要问题就是:如何分箱以及如何对每个箱子中的数据进行平滑处理。 方法 说明 无监督分箱 等宽分箱 将变量的取值范围分为k个等宽的区间,每个区间当作一个分箱。即每个箱的区间范围是一个常量,称为箱子宽度。 等频分箱 把观测值按照从小到大的顺序排列,根据观测的个数等分为k部分,每部分当作一个分箱。比如说 N=10 ,每个区间应该包含大约10%的实例。 自定义分箱 用户可以根据需要自定义区间 聚类分箱 有监督分箱 Best-KS分箱 卡方分箱 有效的特征,不同箱体之间应该具有不同的类分布。卡方分箱就是自底向上,合并类分布相似的相邻箱体,即合并卡方值较小的箱体 最小熵分箱 分箱后达到最小熵。使得总体信息的不确定性降到最低 分箱的重要性 离散特征便于维护,易于模型的迭代 离散化后,特征离散化后,模型会更稳定,比如如果对用户年龄离散化,20-30作为一个区间,不会因为一个用户年龄长了一岁就变成一个完全不同的人。 卡方分箱对于精确的离散化,不同箱体内的类分布应该不同。例如按照年龄判断人群是否喜欢打游戏,将年龄划分为年轻人与老年人;如果年轻人有80%的人喜欢打游戏,老年人有20%的喜欢打游戏,所以按照年龄判断人群是否喜欢打游戏是有意义的,如果一个人是年轻人,则他有80%的可能性喜欢打游戏;如果年轻人与老年人都有80%的可能性喜欢打游戏,所以按照年龄判断人群是否喜欢打游戏是没有意义的,因为无论年龄多大他都有80%的可能性喜欢打游戏。 有效的特征,应该是不同箱体之间具有不同的类分布;卡方分箱就是自底向上,合并类分布相似的相邻箱体。如果两个相邻的区间具有非常类似的类分布,则这两个区间可以合并;否则,它们应当保持分开。而低卡方值表明它们具有相似的类分布。 计算步骤 初始化 卡方分箱就是自底向上,合并类分布相似的相邻箱体 构建最初的离散化,即把每一个单独的值视为一个箱体。这样做的目的就是想从每个单独的个体开始逐渐合并。 合并:自底向上的合并,直到满足停止条件。 计算所有相邻分箱的卡方值:也就是说如果有1,2,3,4个分箱,那么就需要绑定相邻的两个分箱,共三组:12,23,34。然后分别计算三个绑定组的卡方值。 从计算的卡方值中找出最小的一个,并把这两个分箱合并:比如,23是卡方值最小的一个,那么就将2和3合并,本轮计算中分箱就变为了1,23,4。 停止条件: 卡方停止的阈值: 分箱数目的限制: 卡方检验$χ^2$检验是以$χ^2$分布为基础的一种假设检验方法,主要用于分类变量之间的独立性检验。基本思想是根据样本数据推断总体分布与期望分布是否有显著性差异,或者推断两个分类变量是否相关或者独立。一般可以设原假设$H_0$为 :观察频数与期望频数没有差异,或者两个变量相互独立不相关。 χ^2 = \sum \frac{(A-E)^2}{A}其中$A$为观察水平的频数,$E$为期望的频数。当样本比较多的时候$χ^2$服从$k-1个自由度的卡方分布。 计算实例 实际(观察) 类别1 类别2 合计 分箱1 $A_{11}$ $A_{12}$ $R_1$ 分箱2 $A_{21}$ $A_{22}$ $R_2$ 合计 $C_1$ $C_2$ $N$ 期望频数 类别1 类别2 分箱1 $E_{11} = R_1 \times \frac{C_1}{N}$ $E_{12} = R_1 \times \frac{C_2}{N}$ 分箱2 $E_{21} = R_2 \times \frac{C_1}{N}$ $E_{22} = R_2 \times \frac{C_2}{N}$ \begin{align} χ^2 &=\sum_{i=1}^{m}\sum_{j=1}^{k} \frac{(A_{ij}-E_{12})^2}{E_{ij}} \\ &=\left [ \frac{(A_{11}-E_{11})^2}{E_{11}} + \frac{(A_{12}-E_{12})^2} {E_{12}}\right ]+\left [ \frac{(A_{21}-E_{21})^2}{E_{21}} + \frac{(A_{22}-E_{22})^2} {E_{22}}\right ] \end{align}如果计算结果是所有卡方值中最小的,说明:这组中两个分箱具有最相似的类分布,因此把它们合并。 最小熵分箱分箱之后,总体信息的不确定性降到最低 。使得分箱后达到最小熵(minimumentropy) 假设样本的label可取为$1,…,k$。令$p_{ij}$表示第$i$个分箱内label取值为$j$的观测的比例,那么第$i$个分箱的熵值为$\sum_{i=1}^k -p_{ij}×log(p_{ij})$。如果第$i$个分箱内样本各类别label的比例相等,即$p_{11}=p_{12}=p_{1J}=1/J$,那么第$i$个分箱的熵值达到最大值;如果第$i$个分箱内因变量只有一种取值,即某个$p_{ij}$等于1而其他类别的比例等于0,那么第$i$个分箱的熵值达到最小值。 令$r_i$表示第$i$个分箱的观测数占所有观测数的比例;那么总熵值为$\sum_{i=0}^k\sum_{j=0}^J(-p_{ij}×logp_{ij})$。需要使总熵值达到最小,也就是使分箱能够最大限度地区分因变量的各类别。 注意:熵的定义是$p \times log \; p$ 最小熵分箱与信息增益的联系信息增益信息熵表示的是不确定度。均匀分布时,不确定度最大,此时熵就最大。当选择某个特征对数据集进行分类时,分类后的数据集信息熵会比分类前的小,其差值表示为信息增益。信息增益可以衡量某个特征对分类结果的影响大小。信息增益=信息熵-条件熵;换句话说,信息增益代表了在一个条件下,信息复杂度(不确定性)减少的程度 g(D,A)=H(D)-H(D|A) $g(D,A)$:特征 $A$ 对训练数据集 $D$ 的信息增益 $H(D)$ :定义为集合 $D$ 的信息熵 $H(D|A)$:特征 $A$ 给定条件下 $D$ 的条件熵 计算过程设训练数据集为 $D$,$|D|$ 表示其样本容量,即样本个数。设有 $K$ 个类 $C_k,k=1,2,…,K$ ,$|C_k|$ 为属于类 $C_k$ 的样本个数。设特征 $A$ 有 $n$ 个不同的取值 ${a_1,a_2,…,a_n}$ ,根据特征 $A$ 的取值将 $D$ 划分为$n$个子集 $D_1,D_2,…,D_n$,$|D_i|$ 为 $D_i$ 的样本个数,$\sum_{i=1}^{n}|D_i|=|D|$。记子集 $D_i$ 中属于类 $C_k$ 的样本的集合为 $D_{ik}$,即 $D_{ik}=D_i⋂C_k$,$|D_{ik}|$ 为 $D_{ik}$ 的样本个数。于是信息增益的算法如下:输入:训练数据集$D$和特征$A$;输出:特征$A$对训练数据集$D$的信息增益$g(D,A)$。 计算数据集$D$的信息熵$H(D)$ H(D)=-\sum_{k=1}^{K}\frac{|C_k|}{|D|}log_2\frac{|C_k|}{|D|} 计算特征$A$对数据集$D$的经验条件熵$H(D|A)$ H(D|A)=-\sum_{i=1}^{n}\frac{|D_i|}{|D|}H(D_i)=-\sum_{i=1}^{n}\frac{|D_i|}{|D|}\sum_{k=1}^{K}\frac{|D_{ik}|}{|D_i|}log_2\frac{|D_{ik}|}{|D_i|} 总结 最小熵分箱计算的是$\sum_{j=0}^J\sum_{i=0}^k(-p_{ij}×logp_{ij})$ 信息增益计算的是$g(D,A)=H(D)-H(D|A)=H(D)-(-\sum_{i=1}^{n}\frac{|D_i|}{|D|}\sum_{k=1}^{K}\frac{|D_{ik}|}{|D_i|}log_2\frac{|D_{ik}|}{|D_i|})=H(D)+(\sum_{j=1}^{J}\frac{|D_i|}{|D|}\sum_{k=1}^{K}(-p_{ij}×logp_{ij}))$ 由于$H(D)$是定值,所以最小熵分箱与信息增益只差系数$\frac{|D_i|}{|D|}$ 信息增益常用于决策树]]></content>
<categories>
<category>人工智能</category>
<category>特征工程</category>
</categories>
<tags>
<tag>特征选择</tag>
<tag>特征工程</tag>
<tag>分箱</tag>
</tags>
</entry>
<entry>
<title><![CDATA[机器学习]]></title>
<url>%2F2019%2F08%2F20%2F%E4%BA%BA%E5%B7%A5%E6%99%BA%E8%83%BD%2F%E6%9C%BA%E5%99%A8%E5%AD%A6%E4%B9%A0%2F</url>
<content type="text"><![CDATA[特征工程特征选择 特征的预测能力:iv值 特征的鲁棒性(稳定性):PSI值 特征在业务上的可解释性 特征生成的难度(可行性) 特征的相关性]]></content>
<categories>
<category>人工智能</category>
</categories>
<tags>
<tag>目录</tag>
</tags>
</entry>
<entry>
<title><![CDATA[IV值]]></title>
<url>%2F2019%2F08%2F20%2F%E6%9C%BA%E5%99%A8%E5%AD%A6%E4%B9%A0%2FIV%E5%80%BC.md%2F</url>
<content type="text"><![CDATA[$IV$的全称是Information Value,中文意思是信息价值,或者信息量。在特征选择的过程中,可以定量的描述模型的预测能力。每一个特征对应一个$IV$值。 IV值 IV<0.02 0.02$\leqslant$IV<0.1 0.1$\leqslant$IV<0.3 IV$\geqslant$0.3 预测能力 不具有预测能力 预测能力很弱 中等程度预测能力 预测能力很强 IV值的直观理解对$IV$值的直观理解:我们假设在一个分类问题中,label为:$Y_1$,$Y_2$;特征为$C_1,C_2,C_3,……,C_n$。 特征 label $C_1,C_2,C_3,……,C_n$ $Y_1$,$Y_2$ 对于一个样本$A$,要判断A属于$Y_1$还是$Y_2$,我们是需要一定的信息的,假设这个信息总量是$I$,而这些所需要的信息,就蕴含在所有的特征中,那么,对于其中的一个特征$C_i$来说,其蕴含的信息越多,那么它对于判断$A$属于$Y_1$还是$Y_2$的贡献就越大,$C_i$的信息价值就越大,$C_i$的$IV$就越大。 计算方法 计算每一个特征的$WOE_i$要对一个特征进行$WOE$编码,需要首先把这个变量进行分组处理(也叫离散化、分箱等等)。分组后,对于第$i$组,$WOE$的计算公式如下: WOE_i = ln(\frac{py_i}{pn_i})=ln(\frac{y_i/y_T}{n_i/n_T})WOE实际上是“当前分组中响应客户占所有响应客户的比例”和“当前分组中没有响应的客户占所有没有响应的客户的比例”的差异。WOE越大,这种差异越大,这个分组里的样本响应的可能性就越大,WOE越小,差异越小,这个分组里的样本响应的可能性就越小。 计算每一个特征的$VI$值 \begin{aligned} IV_i &= (py_i-pn_i)\times WOE_i =(py_i-pn_i)\times ln(\frac{py_i}{pn_i})= (\frac{y_i}{y_T}-\frac{n_i}{n_T}) \times ln(\frac{y_i/y_T}{n_i/n_T}) \\ IV &= \sum_{i}^n IV_i \end{aligned} $y_T$:样本中好的用户总数 $n_T$:样本中坏的用户总数 $y_i$:第$i$组中好的用户数 $n_i$:第$i$组中坏的用户数 计算实例假设我们需要构建一个预测模型,预测公司客户对我们的这项营销活动响应的可能性有多大。假设我们已经从公司客户列表中随机抽取了100000个客户进行了营销活动测试,收集了这些客户的响应结果,作为我们的建模数据集,其中响应的客户有10000个。 样本总量 响应样本量 未响应样本量 100000 十万 10000 1万 90000 9万 我们以其中的一个特征“最近一次购买金额”为例计算$WOE$和$IV$值 最近一次购买金额 响应 未响应 合计 响应比例 WOE IV <100元 2500 47500 50000 5% -0.74721 0.20756 [100,200) 3000 27000 30000 10% 0 0 [200,500) 3000 12000 15000 20% 0.81093 0.135155 $\geqslant$500 1500 3500 5000 30% 1.349927 0.149992 合计 10000 90000 100000 10% 0 0.492706 <100元:$IV_1=(\frac {2500}{10000}-\frac{47500}{90000})\times WOE_1=(\frac {2500}{10000}-\frac{47500}{90000})\times ln(\frac{2500/10000}{47500/90000})=0.20756$ [100,200) :$IV_2=(\frac {3000}{10000}-\frac{27000}{90000})\times WOE_2=(\frac {3000}{10000}-\frac{27000}{90000})\times ln(\frac{3000/10000}{27000/90000})=0$ [200,500) :$IV_3=(\frac {3000}{10000}-\frac{12000}{90000})\times WOE_2=(\frac {3000}{10000}-\frac{12000}{90000})\times ln(\frac{3000/10000}{12000/90000})=0.135155$ $\geqslant$500:$IV_4=(\frac {1500}{10000}-\frac{3500}{90000})\times WOE_1=(\frac {1500}{10000}-\frac{3500}{90000})\times ln(\frac{1500/10000}{3500/90000})=0.149992$ IV=\sum_i^n IV_i= IV_1+IV_2+IV_3+IV_4 = 0.492706WOE的理解WOE实际上是“当前分组中响应客户占所有响应客户的比例”和“当前分组中没有响应的客户占所有没有响应的客户的比例”的差异。WOE越大,这种差异越大,这个分组里的样本响应的可能性就越大,WOE越小,差异越小,这个分组里的样本响应的可能性就越小。 当前分组中,响应的比例越大,WOE值越大; 当前分组WOE的正负,由当前分组响应和未响应的比例,与样本整体响应和未响应的比例的大小关系决定,当前分组的比例小于样本整体比例时,WOE为负,当前分组的比例大于整体比例时,WOE为正,当前分组的比例和整体比例相等时,WOE为0。 WOE的取值范围是全体实数。 为什么用IV而不是直接用WOE从上面的内容来看,特征各分组的$WOE$和$IV$都隐含着这个分组对目标变量的预测能力这样的意义。那我们为什么不直接用$WOE$相加或者绝对值相加作为衡量一个变量整体预测能力的指标呢? 当我们衡量一个变量的预测能力时,我们所使用的指标值不应该是负数,否则,说一个变量的预测能力的指标是$-2.3$,听起来很别扭。从这个角度讲,乘以$(py_i-pn_i)$这个系数,保证了变量每个分组的结果都是非负数,你可以验证一下,当一个分组的$WOE$是正数时,$(py_i-pn_i)$也是正数,当一个分组的$WOE$是负数时,$(py_i-pn_i)$也是负数,而当一个分组的$WOE=0$时,$(py_i-pn_i)$也是0。 当然,上面的原因不是最主要的,因为其实我们上面提到的$WOE=\sum_i^n |WOE_i|$这个指标也可以完全避免负数的出现。 乘以$(py_i-pn_i)$后,体现出了变量当前分组中个体的数量占整体个体数量的比例,对变量预测能力的影响。怎么理解这句话呢?我们还是举个例子。 假设我们上面所说的营销响应模型中,还有一个特征A(是否为公司VIP客户),其取值只有两个0,1,数据如下: A(是否为公司VIP) 响应 未响应 合计 响应比例 WOE IV 1 90 10 100 90% 4.3944492 0.0390618 0 9910 89990 99900 10% -0.00893 $7.937^{-5}$ 合计 10000 90000 100000 10% 4.4033788 0.0391411 我们从上表可以看出,当特征A(是否为公司VIP)取值1时,其响应比例达到了90%,非常的高,但是我们能否说特征的预测能力非常强呢?不能。为什么呢?原因就在于,A取1时,响应比例虽然很高,但这个分组的客户数太少了,占的比例太低了。虽然,如果一个客户在A这个特征上取1,那他有90%的响应可能性,但是一个客户特征A取1的可能性本身就非常的低。所以,对于样本整体来说,特征的预测能力并没有那么强。 我们分别看一下变量各分组和整体的$WOE$,$IV$。从这个表我们可以看到,变量取1时,响应比达到90%,对应的$WOE$很高,但对应的$IV$却很低,原因就在于$IV$在$WO$E的前面乘以了一个系数$(py_i-pn_i)$,而这个系数很好的考虑了这个分组中样本占整体样本的比例,比例越低,这个分组对变量整体预测能力的贡献越低。相反,如果直接用$WOE$的绝对值加和,会得到一个很高的指标,这是不合理的。 IV的极端情况以及处理方式IV依赖WOE,并且IV是一个很好的衡量自变量对目标变量影响程度的指标。但是,使用过程中应该注意一个问题:特征的任何分组中,不应该出现响应数=0或非响应数=0的情况。 原因很简单,当变量一个分组中,响应数=0时,此时对应的$IV_i$为$-∞$。$WOE_i=ln(\frac{0/n_i}{y_T/n_T})=-∞$ 而当变量一个分组中,没有响应的数量 = 0时,此时的$IV_i$为$+∞$。$WOE_i=ln(\frac{y_i/0}{y_T/n_T})=+∞$ 那么,遇到响应比例为0或者100%的情况,我们应该怎么做呢?建议如下: 如果可能,直接把这个分组做成一个规则,作为模型的前置条件或补充条件; 重新对变量进行离散化或分组,使每个分组的响应比例都不为0且不为100%,尤其是当一个分组个体数很小时(比如小于100个),强烈建议这样做,因为本身把一个分组个体数弄得很小就不是太合理。 如果上面两种方法都无法使用,建议人工把该分组的响应数和非响应的数量进行一定的调整。如果响应数原本为0,可以人工调整响应数为1,如果非响应数原本为0,可以人工调整非响应数为1. 随机森林特征重要性Identifying churn drivers with Random Forests决策树类算法的特点之一就是有良好的模型解释性。我们可以分析出得到相应结果的数据原因,也可以得到哪些特征比较重要。下面来回顾一下得到这些的主要方法: 1,平均不纯度减少(MDI):表示每个特征对误差的平均减少程度。《统计学习要素》的作者非常简洁的解释了这种方法:“在每一棵树的每一个分裂中,分裂准则的改进是对分裂变量的重要度量,并分别在森林中的所有树上为每个变量累积。”让我们详细说明一下这段话的意思。如我们所知,决策树根据一些规则,将结点分裂为两个子结点。每次分裂都是针对一个可以使误差最小化的特征。误差的计算可以使均方误差,基尼纯度,信息增益,或者其他一些根据需要设置的指标。我们总结了所有树上,这个特定变量得到的所有分割使误差减少的情况。在sk-learn包中,每次分裂带来的提升效果,是由到达节点的样本数加权得到的,然后对特征的重要性进行归一化处理。值得注意的是,这种方法往往高估了具有许多类别的特性的重要性。这里描述了一种纠正MDI偏置的替代方法。 2,平均精确率减少(MDA):打乱每个特征的特征值顺序,并且度量顺序变动对模型的精确率的影响。这种巧妙的方法利用袋外数据来计算重要性。OOB数据是训练集的一部分,但不用于训练这种特殊的树。用OOB数据计算出基本误差,然后对每个特征,随机打乱顺序。实际上,这就像用相同的分布使用随机数据替换变量一样,并忽视树对该特性的已有知识。对于不重要的特征来说,打乱顺序对模型的精确率影响不会太大,但是对于重要的特征来说,打乱顺序就会降低模型的精确率。 3,Boruta:重复删除比最佳特征差的特征。主要思想就是检查比随机噪声重要的特征。首先我们要建立影子变量将所有特征混合。这就像在“减少平均精度”中描述的变量打乱一样,但这个方法是同时对所有变量进行操作。我们将影子特征加入到原有特征中,然后用随机森林进行训练。使用上述介绍的MDA或者MDI方法,我们可以看到哪个原始变量比影子变量重要。如果不相关的特征较少,则重要性度量更精确。因此,上述过程重复到预定义的次数,或者直到达到最小特征计数为止。这个算法从最不相关的特征开始删除,因此我们可以用删除顺序作为特征重要性排序。Boruta是一个“相关”的特征选择算法。这与通过确定最佳预测精度得到的最小数据集方法有细微的区别。正如该方法的作者所说的那样:“这个算法尝试找到所有对预测结果有用的特征,而不是找到一个使误差最小的特征集合。”]]></content>
<categories>
<category>人工智能</category>
<category>特征工程</category>
</categories>
<tags>
<tag>特征选择</tag>
<tag>特征工程</tag>
</tags>
</entry>
<entry>
<title><![CDATA[pyspark-DataFrame]]></title>
<url>%2F2019%2F08%2F19%2F%E5%A4%A7%E6%95%B0%E6%8D%AE%E6%A1%86%E6%9E%B6%2Fspark%2Fpyspark-DataFrame%2F</url>
<content type="text"><![CDATA[命令 说明 df.show(n,truncate) n - 要显示的行数。truncate - 如果设置为True,则默认截断超过20个字符的字符串。 如果设置为大于1的数字,则截断长字符串以截断长度并将其右对齐。 df.count() 返回DataFrame的行数 df.select(col) 投影一组表达式并返回一个新的DataFrame。col:列名,或者包含列名的列表 df.filter(condition) 使用给定的条件过滤行。where()是filter()的别名。 df.fillna(value) 空值填充 df.columns 返回DataFrame的列名,是一个list df.toPandas 转换为pandas的dataframe df.describe() 计算数字和字符串列的统计信息。这包括count,mean,stddev,min和max。 pyspark.sql.functions1from pyspark.sql.functions import * 命令 说明 isnan stddev 计算方差]]></content>
<categories>
<category>大数据平台</category>
<category>spark</category>
</categories>
<tags>
<tag>分布式</tag>
<tag>spark</tag>
<tag>python</tag>
</tags>
</entry>
<entry>
<title><![CDATA[MapReduce]]></title>
<url>%2F2019%2F08%2F16%2Fhadoop%2FMapReduce.md%2F</url>
<content type="text"><![CDATA[Map/Reduce 是一个分布式运算程序的编程框架,Map/Reduce 核心功能是将用户编写的业务逻辑代码和自带默认组件整合成一个完整的分布式运算程序,并发运行在一个 hadoop 集群上。每个Map/Reduce任务都被初始化为一个Job,每个Job又可以分为两种阶段:map阶段和reduce阶段。这两个阶段分别用两个函数表示,即map函数和reduce函数。一个Map/Reduce作业的输入和输出类型如下所示: (input) <k1, v1> -> map-> <k2, v2> -> Shuff-> <k2, list[v2]> -> reduce -> <k3, v3> (output) 简单在于其编程模型只包含map和reduce两个过程,map的主要输入是一对<key , value>值,经过map计算后输出一对<key , value>值;然后将相同key合并,形成<key , value集合>;再将这个<key , value集合>输入reduce,经过计算输出零个或多个<key , value>对。 原理 系统中有两类主要的进程节点:master(单点),worker(多个)。其中,worker根据不同的计算任务,又分为map worker(对应上图中的Map phase)、reduce worker(对应上图中的Reduce phase)。 master是系统的中心节点,负责为worker节点分配计算任务,同时监控worker节点的状态。如果某个worker计算太慢,或者宕机,master会将该worker进程负责的计算任务转移到其他进程。 worker包括Map与Reduce 首先将文件分割成<key,value>的键值对。Map通过 RecordReader 读取Input的<key,value>对,map根据用户自定义的任务,运行完毕后,输出另外一系列<key,value>, Shuffle 阶段需要从所有map主机上把相同的key 的 key value对组合在一起,(也就是这里省去的Combine阶段)。 Partitioner组件会把 key放进一个 hash函数里,然后得到结果。如果两个 key 的哈希值 一样,他们的 <key,value>对就被放到同一个 reduce 函数里。我们也把分配到同一个 reduce函数里的<key,value>对叫做一个reduce partition。 reduce() 函数以 key 及对应的 value 列表作为输入,按照用户自己的程序逻辑,经合并 key 相同的 value 值后,产 生另外一系列 <key,value> 对作为最终输出写入HDFS。 应用]]></content>
<categories>
<category>大数据平台</category>
<category>Hadoop</category>
</categories>
<tags>
<tag>分布式</tag>
<tag>Hadoop</tag>
</tags>
</entry>
<entry>
<title><![CDATA[HDFS]]></title>
<url>%2F2019%2F08%2F16%2Fhadoop%2FHDFS.md%2F</url>
<content type="text"><![CDATA[HDFS框架 主从结构: 主节点,只有一个 : namenode 从节点,有很多个 :datanodes namenode负责: 接收用户请求操作 维护文件信息系统的目录结构 管理文件与block之间的关系,block与datanade之间的关系注意:namenode归根结底要放在磁盘上的以保证数据的持久性,但是为了提高效率,一般在hadoop运行时时放在内存中的 datanode负责: 1. 存储文件 2. 文件被分成`block`存储字磁盘上 3. 为保证数据安全,文件会有多个副本 HDFS shell命令命令格式: bin/hadoop fs <args> URI URI:scheme://authority/path—HDFS文件系统 file://authority/path—本地文件系统 命令 说明 cat 使用方法:hadoop fs -cat URI [URI …]将路径指定文件的内容输出到stdout,成功返回0,失败返回-1。eg:hadoop fs -cat hdfs://host1:port1/file1 hdfs://host2:port2/file2  hadoop fs -cat file:///file3 /user/hadoop/file4 chgrp 使用方法:hadoop fs -chgrp [-R] GROUP URI [URI …] -R:递归改变文件的目录结构改变文件所属的组。 chmod 使用方法:hadoop fs -chmod [-R] <MODE[,MODE]... OCTALMODE> URI [URI …]      -R:递归改变文件的目录结构改变文件的权限。 chown 使用方法:hadoop fs -chown [-R] [OWNER][:[GROUP]] URI [URI ]     -R:递归改变文件的目录结构改变文件的拥有者。 copyFromLocal 使用方法:hadoop fs -copyFromLocal <localsrc> URI除了限定源路径是一个本地文件外,和put命令相似 copyToLocal 使用方法:hadoop fs -copyToLocal [-ignorecrc] [-crc] URI <localdst>除了限定目标路径是一个本地文件外,和get命令类似。 cp 使用方法:hadoop fs -cp URI [URI …] <dest>将文件从源路径复制到目标路径。这个命令允许有多个源路径,此时目标路径必须是一个目录。成功返回0,失败返回-1。eg:hadoop fs -cp /user/hadoop/file1 /user/hadoop/file2 /user/hadoop/dir du 使用方法:hadoop fs -du URI [URI …]显示目录中所有文件的大小,或者当只指定一个文件时,显示此文件的大小。成功返回0,失败返回-1。eg:hadoop fs -du /user/hadoop/dir1 /user/hadoop/file1 hdfs://host:port/user/hadoop/dir1 dus 使用方法:hadoop fs -dus <args>显示文件的大小。 expunge 使用方法:hadoop fs -expunge清空回收站。 get 使用方法:hadoop fs -get [-ignorecrc] [-crc] <src> <localdst>复制文件到本地文件系统。可用-ignorecrc选项复制CRC校验失败的文件。使用-crc选项复制文件以及CRC信息。eg:hadoop fs -get /user/hadoop/file localfile  hadoop fs -get hdfs://host:port/user/hadoop/file localfile getmerge 使用方法:hadoop fs -getmerge <src> <localdst> [addnl]接受一个源目录和一个目标文件作为输入,并且将源目录中所有的文件连接成本地目标文件。addnl是可选的,用于指定在每个文件结尾添加一个换行符。 ls 使用方法:hadoop fs -ls <args>如果是文件,则按照如下格式返回文件信息:文件名 <副本数> 文件大小 修改日期 修改时间 权限 用户ID 组ID如果是目录,则返回它直接子文件的一个列表,就像在Unix中一样。目录返回列表的信息如下目录名 <dir> 修改日期 修改时间 权限 用户ID 组ID成功返回0,失败返回-1。 lsr ls命令的递归版本。类似于Unix中的ls -R。 mkdir 使用方法:hadoop fs -mkdir <paths>接受路径指定的uri作为参数,创建这些目录。其行为类似于Unix的mkdir -p,它会创建路径中的各级父目录。成功返回0,失败返回-1。eg:hadoop fs -mkdir /user/hadoop/dir1 /user/hadoop/dir2 movefromLocal 使用方法:dfs -moveFromLocal <src> <dst> mv 使用方法:hadoop fs -mv URI [URI …] <dest>将文件从源路径移动到目标路径。这个命令允许有多个源路径,此时目标路径必须是一个目录。不允许在不同的文件系统间移动文件。eg:hadoop fs -mv /user/hadoop/file1 /user/hadoop/file2 put 使用方法:hadoop fs -put <localsrc> ... <dst>从本地文件系统中复制单个或多个源路径到目标文件系统。也支持从标准输入中读取输入写入目标文件系统。成功返回0,失败返回-1。eg:hadoop fs -put localfile /user/hadoop/hadoopfile rm 使用方法:hadoop fs -rm URI [URI …]删除指定的文件。只删除非空目录和文件。请参考rmr命令了解递归删除。成功返回0,失败返回-1。eg:hadoop fs -rm hdfs://host:port/file /user/hadoop/emptydir rmr 使用方法:hadoop fs -rmr URI [URI …]delete的递归版本。hadoop fs -rmr /user/hadoop/dir setrep 使用方法:hadoop fs -setrep [-R] <path>改变一个文件的副本系数。-R选项用于递归改变目录下所有文件的副本系数。成功返回0,失败返回-1。hadoop fs -setrep -w 3 -R /user/hadoop/dir1 stat 使用方法:hadoop fs -stat URI [URI …]返回指定路径的统计信息。hadoop fs -stat path tail 使用方法:hadoop fs -tail [-f] URI将文件尾部1K字节的内容输出到stdout。支持-f选项,行为和Unix中一致。成功返回0,失败返回-1。 test 使用方法:hadoop fs -test -[ezd] URI     -e 检查文件是否存在。如果存在则返回0。     -z 检查文件是否是0字节。如果是则返回0。      -d如果路径是个目录,则返回1,否则返回0。 text 使用方法:hadoop fs -text <src>将源文件输出为文本格式。允许的格式是zip和TextRecordInputStream。 touchz 使用方法:hadoop fs -touchz URI [URI …]创建一个0字节的空文件。成功返回0,失败返回-1。]]></content>
<categories>
<category>大数据平台</category>
<category>Hadoop</category>
</categories>
<tags>
<tag>分布式</tag>
<tag>文件管理系统</tag>
<tag>Hadoop</tag>
</tags>
</entry>
<entry>
<title><![CDATA[python]]></title>
<url>%2F2019%2F08%2F15%2F%E7%BC%96%E7%A8%8B%E8%AF%AD%E8%A8%80%2Fpython%E8%AF%AD%E8%A8%80%2Fpython%2F</url>
<content type="text"><![CDATA[条件语句1print('True') if condition else print('False') 基本 命令 说明 max(num,key=func) 按照func返回num中的最大值 sys getsizeof() osenviron()函数 os.environ[‘USER’] 当前使用用户 os.environ[‘SHELL’] 使用shell的类型 os.environ[‘LAN’] 使用的语言 numpy 命令 说明 random.randint(start,ene,size) start与end:取值范围size:数组的容量 np.logical_and(x1, x2, *args, **kwargs) 逻辑与 np.logical_or(x1, x2, *args, **kwargs) 逻辑或 np.logical_not(x, *args, **kwargs) 逻辑非 pandasDataframe 命令 说明 df.iloc[num] 指定行。df.iloc[0]指定第一行 df(df.b.isin([5,13])) 删除b列包含5、13的列 df['add_column']=1 df添加列add_column df.drop() 删除行或者列labels:删除的行或者列名axis:行->0;列->1index :直接指定要删除的行columns:直接指定要删除的列inplace=False:默认该删除操作不改变原数据,而是返回一个执行删除操作后的新dataframe;inplace=True:则会直接在原数据上进行删除操作,删除后无法返回。 df.dropna() 删除空行axis:0-行操作(默认),1-列操作 how:any-只要有空值就删除(默认),all-全部为空值才删除 inplace:False-返回新的数据集(默认),True-在愿数据集上操作 df.rename() df.rename(columns={'A':'a', 'B':'b', 'C':'c'}, inplace = True)将列名修改为`[‘a’,’b’,’c’] df.columns= ['a','b','c'] 将列名修改为['a','b','c'] df.reset_index(列名,inplace) 索引转化为列列名:可以有,可以没有。多索引时,可以指定索引inplace:替换原DataFrame df.set_index(列名) 列转化为索引 df.T DataFrame转置 df.merge() 类似于数据库的join,返回DataFrame df['列名'].tolist() 将列转换为list matplotlibhttps://serverpoolauth.ops.ctripcorp.com sklearn特征工程1from sklearn.preprocessing import * 命令 说明 KBinsDiscretizer - 对数据进行分箱(离散化) ,不能处理空值- 返回np.array数据- 参数 n_bins:分箱的数量,默认值是5,也可以是列表,指定各个特征的分箱数量,例如,[feature1_bins,feature2_bins,…] n_bins:分箱的数量,默认值是5,也可以是列表,指定各个特征的分箱数量,例如,[feature1_bins,feature2_bins,…] encode:编码方式,{‘onehot’, ‘onehot-dense’, ‘ordinal’}, (default=’onehot’) onehot:以onehot方式编码,返回稀疏矩阵 onehot-dense:以onehot方式编码,返回密集矩阵 ordinal:以ordinal方式编码,返回分箱的序号 strategy:定义分箱宽度的策略,{‘uniform’, ‘quantile’, ‘kmeans’}, (default=’quantile’) uniform:每个分箱等宽 quantile:每个分箱中拥有相同数量的数据点 kmeans:每个箱中的值具有与1D k均值簇最近的中心 其他断言 assert]]></content>
<categories>
<category>编程语言</category>
<category>python</category>
</categories>
<tags>
<tag>python</tag>
</tags>
</entry>
<entry>
<title><![CDATA[hadoop]]></title>
<url>%2F2019%2F08%2F14%2F%E5%A4%A7%E6%95%B0%E6%8D%AE%E6%A1%86%E6%9E%B6%2Fhadoop%2Fhadoop%2F</url>
<content type="text"><![CDATA[Apache Hadoop软件库是一个框架,允许使用简单的编程模型跨计算机集群分布式处理大型数据集。它旨在从单个服务器扩展到数千台计算机,每台计算机都提供本地计算和存储。该库本身不是依靠硬件来提供高可用性,而是设计用于检测和处理应用层的故障,从而在计算机集群之上提供高可用性服务,每个计算机都可能容易出现故障。Hadoop的命令众多,本文重点介绍HDFS与MapReduce hadoop.tgn 类别 名称 说明 模块 Hadoop Common 支持其他Hadoop模块的常用实用程序。 Hadoop HDFS 一种分布式文件系统,可提供对应用程序数据的高吞吐量访问 Hadoop YARN 作业调度和集群资源管理的框架。 Hadoop MapReduce 基于YARN的系统,用于并行处理大型数据集 Hadoop Ozone Hadoop的对象存储 Hadoop Submarine Hadoop的机器学习引擎。 数据库 Apache Hive 是基于Hadoop的一个数据仓库工具,可以将结构化的数据文件映射为一张数据库表,通过类SQL语句快速实现简单的MapReduce统计,不必开发专门的MapReduce应用,十分适合数据仓库的统计分析。 Apache Pig 是一个基于Hadoop的大规模数据分析工具,它提供的SQL-LIKE语言叫Pig Latin,该语言的编译器会把类SQL的数据分析请求转换为一系列经过优化处理的MapReduce运算 Apache HBase 是一个高可靠性、高性能、面向列、可伸缩的分布式存储系统,利用HBase技术可在廉价PC Server上搭建起大规模结构化存储集群 Apache Cassandra 是一套开源分布式NoSQL数据库系统。它最初由Facebook开发,用于储存简单格式数据,集Google BigTable的数据模型与Amazon Dynamo的完全分布式的架构于一身 Apache HCatalog 是基于Hadoop的数据表和存储管理,实现中央的元数据和模式管理,跨越Hadoop和RDBMS,利用Pig和Hive提供关系视图。 Apache Avro 是一个数据序列化系统,设计用于支持数据密集型,大批量数据交换的应用。Avro是新的数据序列化格式与传输工具,将逐步取代Hadoop原有的IPC机制 Apache Sqoop 是一个用来将Hadoop和关系型数据库中的数据相互转移的工具,可以将一个关系型数据库(MySQL ,Oracle ,Postgres等)中的数据导进到Hadoop的HDFS中,也可以将HDFS的数据导进到关系型数据库中。 Apache Chukwa 是一个开源的用于监控大型分布式系统的数据收集系统,它可以将各种各样类型的数据收集成适合 Hadoop 处理的文件保存在 HDFS 中供 Hadoop 进行各种 MapReduce 操作。 计算与模型 Apache Mahout 是基于Hadoop的机器学习和数据挖掘的一个分布式框架。Mahout用MapReduce实现了部分数据挖掘算法,解决了并行挖掘的问题。 Apache Hama 是一个基于HDFS的BSP(Bulk Synchronous Parallel)并行计算框架, Hama可用于包括图、矩阵和网络算法在内的大规模、大数据计算。 Apache Giraph 是一个可伸缩的分布式迭代图处理系统, 基于Hadoop平台,灵感来自 BSP (bulk synchronous parallel) 和 Google 的 Pregel。 框架 Apache Flume 是一个分布的、可靠的、高可用的海量日志聚合的系统,可用于日志数据收集,日志数据处理,日志数据传输。 Cloudera Hue 是一个基于WEB的监控和管理系统,实现对HDFS,MapReduce/YARN, HBase, Hive, Pig的web化操作和管理。 Apache Ambari 是一种基于Web的工具,支持Hadoop集群的供应、管理和监控 Apache Zookeeper 是一个为分布式应用所设计的分布的、开源的协调服务,它主要是用来解决分布式应用中经常遇到的一些数据管理问题,简化分布式应用协调及其管理的难度,提供高性能的分布式服务 Apache Bigtop 是一个对Hadoop及其周边生态进行打包,分发和测试的工具。 Apache Oozie 是一个工作流引擎服务器, 用于管理和协调运行在Hadoop平台上(HDFS、Pig和MapReduce)的任务。 辅助 Apache Crunch 是基于Google的FlumeJava库编写的Java库,用于创建MapReduce程序。与Hive,Pig类似,Crunch提供了用于实现如连接数据、执行聚合和排序记录等常见任务的模式库 HDFSHadoop分布式文件系统(HDFS)被设计成适合运行在通用硬件(commodity hardware)上的分布式文件系统。它和现有的分布式文件系统有很多共同点。但同时,它和其他的分布式文件系统的区别也是很明显的。HDFS是一个高度容错性的系统,适合部署在廉价的机器上。HDFS能提供高吞吐量的数据访问,非常适合大规模数据集上的应用。HDFS放宽了一部分POSIX约束,来实现流式读取文件系统数据的目的。HDFS在最开始是作为Apache Nutch搜索引擎项目的基础架构而开发的。HDFS是Apache Hadoop Core项目的一部分。HDFS使用指南 Map/ReduceMap/Reduce 是一个分布式运算程序的编程框架,Map/Reduce 核心功能是将用户编写的业务逻辑代码和自带默认组件整合成一个完整的分布式运算程序,并发运行在一个 hadoop 集群上。每个Map/Reduce任务都被初始化为一个Job,每个Job又可以分为两种阶段:map阶段和reduce阶段。这两个阶段分别用两个函数表示,即map函数和reduce函数。一个Map/Reduce作业的输入和输出类型如下所示: (input) <k1, v1> -> map -> <k2, v2> -> combine -> <k2, v2> -> reduce -> <k3, v3> (output) Hive有了MapReduce之后,程序员发现,MapReduce的程序写起来真麻烦。他们希望简化这个过程。这就好比你有了汇编语言,虽然你几乎什么都能干了,但是你还是觉得繁琐。你希望有个更高层更抽象的语言层来描述算法和数据处理流程。于是就有了Pig和Hive。Pig是接近脚本方式去描述MapReduce,Hive则用的是SQL。它们把脚本和SQL语言翻译成MapReduce程序,丢给计算引擎去计算,而你就从繁琐的MapReduce程序中解脱出来,用更简单更直观的语言去写程序了。]]></content>
<categories>
<category>大数据平台</category>
<category>Hadoop</category>
</categories>
<tags>
<tag>分布式</tag>
<tag>Hadoop</tag>
</tags>
</entry>
<entry>
<title><![CDATA[SQL-spark]]></title>
<url>%2F2019%2F08%2F13%2Fspark%2FSQL-spark.md%2F</url>
<content type="text"><![CDATA[getting startedSparkSessionSpark 中所有功能的入口是 SparkSession 类。要创建一个基本的SparkSession 对象, 只需要使用 SparkSession.builder(): 12345678// scalaimport org.apache.spark.sql.SparkSessionval spark = SparkSession .builder() .appName("Spark SQL basic example") .config("spark.some.config.option", "some-value") .getOrCreate() 12345678// javaimport org.apache.spark.sql.SparkSession;SparkSession spark = SparkSession .builder() .appName("Java Spark SQL basic example") .config("spark.some.config.option", "some-value") .getOrCreate(); 12345678# pythonfrom pyspark.sql import SparkSessionspark = SparkSession \ .builder \ .appName("Python Spark SQL basic example") \ .config("spark.some.config.option", "some-value") \ .getOrCreate() 创建 DataFrame应用程序可以使用 SparkSession从一个现有的 RDD,Hive表或 Spark 数据源创建 DataFrame。下面基于一个 JSON 文件的内容创建一个 DataFrame: 1234567val df = spark.read.json("examples/src/main/resources/people.json") //scalaimport org.apache.spark.sql.Dataset;import org.apache.spark.sql.Row;Dataset<Row> df = spark.read().json("examples/src/main/resources/people.json"); //javadf = spark.read.json("examples/src/main/resources/people.json") //python DataFrame 操作DataFrame为 Scala,Java, Python 以及 R 语言中的结构化数据操作提供了一种领域特定语言。 scala java python df.printSchema() df.printSchema(); df.printSchema() df.select("name") df.select("name").show(); df.select("name").show(); df.select($"name", $"age" + 1).show() df.select(col("name"), col("age").plus(1)).show(); df.select(df['name'], df['age'] + 1).show() df.filter($"age" > 21).show() df.filter(col("age").gt(21)).show(); df.filter(df['age'] > 21).show() df.groupBy("age").count().show() df.groupBy("age").count().show() df.groupBy("age").count().show() 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647/************************** scala*************************/// This import is needed to use the $-notationimport spark.implicits._// Print the schema in a tree formatdf.printSchema()// root// |-- age: long (nullable = true)// |-- name: string (nullable = true)// Select only the "name" columndf.select("name").show()// +-------+// | name|// +-------+// |Michael|// | Andy|// | Justin|// +-------+// Select everybody, but increment the age by 1df.select($"name", $"age" + 1).show()// +-------+---------+// | name|(age + 1)|// +-------+---------+// |Michael| null|// | Andy| 31|// | Justin| 20|// +-------+---------+// Select people older than 21df.filter($"age" > 21).show()// +---+----+// |age|name|// +---+----+// | 30|Andy|// +---+----+// Count people by agedf.groupBy("age").count().show()// +----+-----+// | age|count|// +----+-----+// | 19| 1|// |null| 1|// | 30| 1|// +----+-----+ 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748/************************** java*************************/// col("...") is preferable to df.col("...")import static org.apache.spark.sql.functions.col;// Print the schema in a tree formatdf.printSchema();// root// |-- age: long (nullable = true)// |-- name: string (nullable = true)// Select only the "name" columndf.select("name").show();// +-------+// | name|// +-------+// |Michael|// | Andy|// | Justin|// +-------+// Select everybody, but increment the age by 1df.select(col("name"), col("age").plus(1)).show();// +-------+---------+// | name|(age + 1)|// +-------+---------+// |Michael| null|// | Andy| 31|// | Justin| 20|// +-------+---------+// Select people older than 21df.filter(col("age").gt(21)).show();// +---+----+// |age|name|// +---+----+// | 30|Andy|// +---+----+// Count people by agedf.groupBy("age").count().show();// +----+-----+// | age|count|// +----+-----+// | 19| 1|// |null| 1|// | 30| 1|// +----+-----+ 12345678910111213141516171819202122232425262728293031323334353637383940414243444546##################### python #################### spark, df are from the previous example# Print the schema in a tree formatdf.printSchema()# root# |-- age: long (nullable = true)# |-- name: string (nullable = true)# Select only the "name" columndf.select("name").show()# +-------+# | name|# +-------+# |Michael|# | Andy|# | Justin|# +-------+# Select everybody, but increment the age by 1df.select(df['name'], df['age'] + 1).show()# +-------+---------+# | name|(age + 1)|# +-------+---------+# |Michael| null|# | Andy| 31|# | Justin| 20|# +-------+---------+# Select people older than 21df.filter(df['age'] > 21).show()# +---+----+# |age|name|# +---+----+# | 30|Andy|# +---+----+# Count people by agedf.groupBy("age").count().show()# +----+-----+# | age|count|# +----+-----+# | 19| 1|# |null| 1|# | 30| 1|# +----+-----+ SQL查询通过SQL访问数据库,返回一个DataFrame的类型 123456/*************************** scala *********************************/// Register the DataFrame as a SQL temporary viewdf.createOrReplaceTempView("people")val sqlDF = spark.sql("SELECT * FROM people")sqlDF.show() 12345678/*************************** java *********************************/import org.apache.spark.sql.Dataset;import org.apache.spark.sql.Row;// Register the DataFrame as a SQL temporary viewdf.createOrReplaceTempView("people");Dataset<Row> sqlDF = spark.sql("SELECT * FROM people");sqlDF.show(); 123456############################# python ################################ Register the DataFrame as a SQL temporary viewdf.createOrReplaceTempView("people")sqlDF = spark.sql("SELECT * FROM people")sqlDF.show() 全局临时视图Spark SQL中的临时视图是会话范围的,如果创建它的会话终止,它将消失。如果您希望拥有一个在所有会话之间共享的临时视图并保持活动状态,直到Spark应用程序终止,您可以创建一个全局临时视图。 1234567891011121314/********************************* scala *************************************/// Register the DataFrame as a global temporary viewdf.createGlobalTempView("people")// Global temporary view is tied to a system preserved database `global_temp`spark.sql("SELECT * FROM global_temp.people").show()// +----+-------+// | age| name|// +----+-------+// |null|Michael|// | 30| Andy|// | 19| Justin|// +----+-------+ 1234567891011121314/********************************* java *************************************/// Register the DataFrame as a global temporary viewdf.createGlobalTempView("people");// Global temporary view is tied to a system preserved database `global_temp`spark.sql("SELECT * FROM global_temp.people").show();// +----+-------+// | age| name|// +----+-------+// |null|Michael|// | 30| Andy|// | 19| Justin|// +----+-------+ 1234567891011121314############################### python ################################# Register the DataFrame as a global temporary viewdf.createGlobalTempView("people")# Global temporary view is tied to a system preserved database `global_temp`spark.sql("SELECT * FROM global_temp.people").show()# +----+-------+# | age| name|# +----+-------+# |null|Michael|# | 30| Andy|# | 19| Justin|# +----+-------+ 创建datasetDataset是特定域对象中的强类型集合,它可以使用函数或者相关操作并行地进行转换等操作。每个Dataset都有一个称为DataFrame的非类型化的视图,这个视图是行的数据集。上面的定义看起来和RDD的定义类似,DataSet和RDD主要的区别是:DataSet是特定域的对象集合;然而RDD是任何对象的集合。DataSet的API总是强类型的;而且可以利用这些模式进行优化,然而RDD却不行。DataFrame是特殊的Dataset,它在编译时不会对模式进行检测。在未来版本的Spark,Dataset将会替代RDD成为我们开发编程使用的API(注意,RDD并不是会被取消,而是会作为底层的API提供给用户使用)。 1234567891011121314151617181920212223242526272829// scala// Note: Case classes in Scala 2.10 can support only up to 22 fields. To work around this limit,// you can use custom classes that implement the Product interfacecase class Person(name: String, age: Long)// Encoders are created for case classesval caseClassDS = Seq(Person("Andy", 32)).toDS()caseClassDS.show()// +----+---+// |name|age|// +----+---+// |Andy| 32|// +----+---+// Encoders for most common types are automatically provided by importing spark.implicits._val primitiveDS = Seq(1, 2, 3).toDS()primitiveDS.map(_ + 1).collect() // Returns: Array(2, 3, 4)// DataFrames can be converted to a Dataset by providing a class. Mapping will be done by nameval path = "examples/src/main/resources/people.json"val peopleDS = spark.read.json(path).as[Person]peopleDS.show()// +----+-------+// | age| name|// +----+-------+// |null|Michael|// | 30| Andy|// | 19| Justin|// +----+-------+ 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970// javaimport java.util.Arrays;import java.util.Collections;import java.io.Serializable;import org.apache.spark.api.java.function.MapFunction;import org.apache.spark.sql.Dataset;import org.apache.spark.sql.Row;import org.apache.spark.sql.Encoder;import org.apache.spark.sql.Encoders;public static class Person implements Serializable { private String name; private int age; public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; }}// Create an instance of a Bean classPerson person = new Person();person.setName("Andy");person.setAge(32);// Encoders are created for Java beansEncoder<Person> personEncoder = Encoders.bean(Person.class);Dataset<Person> javaBeanDS = spark.createDataset( Collections.singletonList(person), personEncoder);javaBeanDS.show();// +---+----+// |age|name|// +---+----+// | 32|Andy|// +---+----+// Encoders for most common types are provided in class EncodersEncoder<Integer> integerEncoder = Encoders.INT();Dataset<Integer> primitiveDS = spark.createDataset(Arrays.asList(1, 2, 3), integerEncoder);Dataset<Integer> transformedDS = primitiveDS.map( (MapFunction<Integer, Integer>) value -> value + 1, integerEncoder);transformedDS.collect(); // Returns [2, 3, 4]// DataFrames can be converted to a Dataset by providing a class. Mapping based on nameString path = "examples/src/main/resources/people.json";Dataset<Person> peopleDS = spark.read().json(path).as(personEncoder);peopleDS.show();// +----+-------+// | age| name|// +----+-------+// |null|Michael|// | 30| Andy|// | 19| Justin|// +----+-------+ 与RDD互相转换Spark SQL 支持两种不同的方法将RDDs 转换成 Datasets. ?????????????????????????????????????????? 聚合函数DataFrame可以使用聚合函数,例如count()、max()等 自定义聚合函数使用UserDefinedAggregateFunction a自定义聚合函数。 12]]></content>
<categories>
<category>大数据平台</category>
<category>spark</category>
</categories>
<tags>
<tag>分布式</tag>
<tag>spark</tag>
<tag>数据库</tag>
</tags>
</entry>
<entry>
<title><![CDATA[hive]]></title>
<url>%2F2019%2F08%2F13%2FHDFS%2Fhive.md%2F</url>
<content type="text"><![CDATA[查看表结构 命令 说明 show tables like '*name*' hive模糊搜索表 desc formatted table_namedesc table_name 查看表结构信息 show partitions table_name 查看分区信息 select table_coulm from table_name where partition_name = '2014-02-25'; 根据分区查询数据 dfs -ls /user/hive/warehouse/table02; 查看hdfs文件信息 dfs -tail /user/hive/warehouse/table02; 显示hdfs文件的一行信息 创建表格 命令 说明 create table as 选中表格创建表格 create table ascreate创建部分可以指定表的存储格式等属性,比如指定新表的行列分隔符,存储格式等。 1234567CREATE TABLE temp_copy ROW FORMAT DELIMITED FIELDS TERMINATED BY '\t' LINES TERMINATED BY '\n'; STORED AS textfileASSELECT * FROM temp WHERE start_date IS NOT NULL; hive中用CTAS创建表,所创建的表统一都是非分区表,不管源表是否是分区表。所以对于分区表的创建使用create table ..as一定要注意分区功能的丢失。当然创建表以后可以添加分区,成为分区表。 如果使用create table as select *创建表时源表是分区表,则新建的表会多字段,具体多的字段个数和名称就是源表分区的个数和名称。当然如果select选择的是指定的列则不会有这种问题。 如果源表的存储格式不是textfile。则使用CTAS创建的表存储格式会变成默认的格式textfile。比如这里源表是RCFILE。而新建的表则是textfile。当然可以在使用create table ....as创建表时指定存储格式和解析格式,甚至是列的名称等属性。具体参考博客:hive使用create as创建表指定存储格式等属性 使用CTAS方式创建的表不能是外部表。 使用CTAS创建的表不能分桶表。对于每一个表或者是分区,Hive可以进一步组织成桶,也就是说桶是更为细粒度的数据范围划分。]]></content>
<categories>
<category>大数据平台</category>
<category>Hadoop</category>
</categories>
<tags>
<tag>分布式</tag>
<tag>Hadoop</tag>
<tag>数据库</tag>
</tags>
</entry>
<entry>
<title><![CDATA[RDD-Spark]]></title>
<url>%2F2019%2F08%2F12%2FsparkL%2FRDD-Spark.md%2F</url>
<content type="text"><![CDATA[Actions 命令 说明 reduce(func) 通过函数func聚集数据集中的所有元素。func函数接受2个参数,返回一个值。这个函数必须是关联性的,确保可以被正确的并发执行 collect() 在Driver的程序中,以数组的形式,返回数据集的所有元素。这通常会在使用filter或者其它操作后,返回一个足够小的数据子集再使用,直接将整个RDD集Collect返回,很可能会让Driver程序OOM count() 统计RDD中元素的个数 take(n) 取RDD中的前n个元素。注意,这个操作目前并非在多个节点上,并行执行,而是Driver程序所在机器,单机计算所有的元素(Gateway的内存压力会增大,需要谨慎使用) first() 返回数据集的第一个元素(类似于take(1) ) reduce(func) 按照指定规则聚合RDD中的元素 countByValue() 统计出RDD中每个元素的个数 countByKey() 统计出KV格式的RDD中相同的K的个数 foreach(func) 以元素为单位,遍历RDD,运行func函数。 foreachPartition(func) 以分区为单位,遍历RDD,运行func函数。 saveAsTextFile(path) 将数据集的元素,以textfile的形式,保存到本地文件系统,hdfs或者任何其它hadoop支持的文件系统。Spark将会调用每个元素的toString方法,并将它转换为文件中的一行文本 saveAsSequenceFile(path) 将数据集的元素,以sequencefile的格式,保存到指定的目录下,本地系统,hdfs或者任何其它hadoop支持的文件系统。RDD的元素必须由key-value对组成,并都实现了Hadoop的Writable接口,或隐式可以转换为Writable(Spark包括了基本类型的转换,例如Int,Double,String等等) 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364//collect 把运行结果拉回到Driver端val rdd = sc.makeRDD(Array( (5,"Tom"),(10,"Jed"),(3,"Tony"),(2,"Jack")))val resultRDD = rdd.sortByKey()val list = resultRDD.collect()list.foreach(println)val rdd = sc.makeRDD(Array( (5,"Tom"),(10,"Jed"),(3,"Tony"),(2,"Jack")))val resultRDD = rdd.sortByKey()val list = resultRDD.collect()list.foreach(println)//take(n):取RDD中的前n个元素val rdd = sc.makeRDD(Array("hello","hello","hello","world"))rdd.take(2).foreach(println)//first 相当于take(1)val rdd = sc.makeRDD(Array("hello","hello","hello","world"))println(rdd.first)// count 统计RDD中元素的个数val rdd = sc.makeRDD(Array("hello","hello","hello","world"))val num = rdd.count()// reduce 按照指定规则聚合RDD中的元素val numArr = Array(1,2,3,4,5)val rdd = sc.parallelize(numArr)val sum = rdd.reduce(_+_)println(sum)//countByValue 统计出RDD中每个元素的个数val rdd = sc.parallelize(Array( "Tom","Jed","Tom", "Tom","Jed","Jed", "Tom","Tony","Jed"))val result = rdd.countByValue();result.foreach(println)// countByKey 统计出KV格式的RDD中相同的K的个数val rdd = sc.parallelize(Array( ("销售部","Tom"), ("销售部","Jack"),("销售部","Bob"),("销售部","Terry"), ("后勤部","Jack"),("后勤部","Selina"),("后勤部","Hebe"), ("人力部","Ella"),("人力部","Harry"), ("开发部","Allen")))val result = rdd.countByKey();result.foreach(println)// foreach 遍历RDD中的元素val rdd = sc.makeRDD(Array("hello","hello","hello","world"))rdd.foreach(println)//foreachPartition 以分区为单位遍历RDDval rdd = sc.parallelize(1 to 6, 2)rdd.foreachPartition(x => { println("data from a partition:") while(x.hasNext) { println(x.next()) }}) Transformations 命令 说明 filter(func) 返回一个新的数据集,由经过func函数后返回值为true的原元素组成 map(func) 返回一个新的分布式数据集,由每个原元素经过func函数转换后组成 flatMap(func) 类似于map,但是每一个输入元素,会被映射为0到多个输出元素(因此,func函数的返回值是一个Seq,而不是单一元素) sample(withReplacement, frac, seed) 随机抽样withReplacement:是否是放回式抽样frac::抽样的比例seed:随机种子 reduceByKey(func, [numTasks]) 在一个(K,V)对的数据集上使用,返回一个(K,V)对的数据集,key相同的值,都被使用指定的reduce函数聚合到一起。和groupbykey类似,任务的个数是可以通过第二个可选参数来配置的。 groupByKey([numTasks]) 在一个由(K,V)对组成的数据集上调用,返回一个(K,Seq[V])对的数据集。注意:默认情况下,使用8个并行任务进行分组,你可以传入numTask可选参数,根据数据量设置不同数目的Task sortByKey(func, [numTasks]) 按key值进行排序 union(otherDataset) 返回一个新的数据集,由原数据集和参数联合而成 join(otherDataset, [numTasks]) 在类型为(K,V)和(K,W)类型的数据集上调用,返回一个(K,(V,W))对,每个key中的所有元素都在一起的数据集 cartesian(otherDataset) 笛卡尔积。但在数据集T和U上调用时,返回一个(T,U) 对的数据集,所有元素交互进行笛卡尔积。 map(func) 以元素为单位,遍历RDD,运行func函数。 mapPartitions(func) 以分区为单位,遍历RDD,运行func函数。 mapPartitionsWithIndex 与mapPartitions基本相同,只是在处理函数的参数是一个二元元组,元组的第一个元素是当前处理的分区的index,元组的第二个元素是当前处理的分区元素组成的Iterator coalesce(n,shuffle) 改变RDD的分区数n:新的分区数目shuffle:false:不产生shuffle;true:产生shuffle,如果重分区的数量大于原来的分区数量,必须设置为true,否则分区数不变 repartition(n) 改变RDD分区数。repartition(int n) = coalesce(int n, true) partitionBy 通过自定义分区器改变RDD分区数 randomSplit(Array) 根据传入的 Array中每个元素的权重将rdd拆分成Array.size个RDD拆分后的RDD中元素数量由权重来决定, groupWith(otherDataset, [numTasks]) 在类型为(K,V)和(K,W)类型的数据集上调用,返回一个数据集,组成元素为(K, Seq[V], Seq[W]) Tuples。这个操作在其它框架,称为CoGroup map和flatMap filter、sample、sortByKey、sortBy12345678910111213141516171819202122232425262728293031323334//filter:过滤val rdd = sc.makeRDD(Array("hello","hello","hello","world"))rdd.filter(!_.contains("hello")).foreach(println)/********** map和flatMap***************map: 输入一条记录,输出一条记录*flatmap:输入一条记录,输出多头记录*************************************///sample :随机抽样val rdd = sc.makeRDD(Array( "hello1","hello2","hello3","hello4","hello5","hello6", "world1","world2","world3","world4"))rdd.sample(false, 0.3).foreach(println)//sortByKey:按key进行排序val rdd = sc.makeRDD(Array( (5,"Tom"),(10,"Jed"),(3,"Tony"),(2,"Jack")))rdd.sortByKey().foreach(println)//sortBy:自定义排序规则object SortByOperator { def main(args: Array[String]): Unit = { val conf = new SparkConf().setAppName("TestSortBy").setMaster("local") val sc = new SparkContext(conf) val arr = Array( Tuple3(190,100,"Jed"), Tuple3(100,202,"Tom"), Tuple3(90,111,"Tony") ) val rdd = sc.parallelize(arr) rdd.sortBy(_._1).foreach(println) //按照190、100、90排序 groupByKey和reduceByKey distinct:去掉重复数据 1234567891011val rdd = sc.makeRDD(Array( "hello", "hello", "hello", "world"))val distinctRDD = rdd .map {(_,1)} .reduceByKey(_+_) .map(_._1)distinctRDD.foreach {println} //等价于:rdd.distinct().foreach {println} map和mapPartitions1234567891011121314151617181920212223242526val arr = Array("Tom","Bob","Tony","Jerry")//把4条数据分到两个分区中val rdd = sc.parallelize(arr,2) /* * 模拟把RDD中的元素写入数据库的过程 */rdd.map(x => { println("创建数据库连接...") println("写入数据库...") println("关闭数据库连接...") println()}).count()结果:创建数据库连接...写入数据库...关闭数据库连接...创建数据库连接...写入数据库...关闭数据库连接...创建数据库连接...写入数据库...关闭数据库连接...创建数据库连接...写入数据库...关闭数据库连接... 1234567891011121314151617181920/* * 将RDD中的数据写入到数据库中,绝大部分使用mapPartitions算子来实现 */rdd.mapPartitions(x => { println("创建数据库") val list = new ListBuffer[String]() while(x.hasNext){ //写入数据库 list += x.next()+":写入数据库" } //执行SQL语句 批量插入 list.iterator})foreach(println)结果:创建数据库Tom:写入数据库Bob:写入数据库 创建数据库Tony:写入数据库Jerry:写入数据库 mapPartitionsWithIndex1234567891011121314151617val dataArr = Array("Tom01","Tom02","Tom03" ,"Tom04","Tom05","Tom06" ,"Tom07","Tom08","Tom09" ,"Tom10","Tom11","Tom12")val rdd = sc.parallelize(dataArr, 3);val result = rdd.mapPartitionsWithIndex((index,x) => { val list = ListBuffer[String]() while (x.hasNext) { list += "partition:"+ index + " content:" + x.next } list.iterator})println("分区数量:" + result.partitions.size)val resultArr = result.collect()for(x <- resultArr){ println(x)} coalesce:改变RDD的分区数123456789101112131415161718192021/* * false:不产生shuffle * true:产生shuffle * 如果重分区的数量大于原来的分区数量,必须设置为true,否则分区数不变 * 增加分区会把原来的分区中的数据随机分配给设置的分区个数 */val coalesceRdd = result.coalesce(6,true) val results = coalesceRdd.mapPartitionsWithIndex((index,x) => { val list = ListBuffer[String]() while (x.hasNext) { list += "partition:"+ index + " content:[" + x.next + "]" } list.iterator}) println("分区数量:" + results.partitions.size)val resultArr = results.collect()for(x <- resultArr){ println(x)} randomSplit:拆分RDD1234567/** * randomSplit: * 根据传入的 Array中每个元素的权重将rdd拆分成Array.size个RDD * 拆分后的RDD中元素数量由权重来决定,数据量不大时不一定准确 */val rdd = sc.parallelize(1 to 10)rdd.randomSplit(Array(0.1,0.2,0.3,0.4)).foreach(x => {println(x.count)}) zip]]></content>
<categories>
<category>大数据平台</category>
<category>spark</category>
</categories>
<tags>
<tag>分布式</tag>
<tag>spark</tag>
</tags>
</entry>
<entry>
<title><![CDATA[spark]]></title>
<url>%2F2019%2F08%2F09%2F%E5%A4%A7%E6%95%B0%E6%8D%AE%E6%A1%86%E6%9E%B6%2Fspark%2Fspark%2F</url>
<content type="text"><![CDATA[spark简介Spark是开发通用的大数据处理框架。Spark应用程序可以使用R语言、Java、Scala和Python进行编写,极少使用R语言编写Spark程序,Java和Scala语言编写的Spark程序的执行效率是相同的,但Java语言写的代码量多,Scala简洁优雅,但可读性不如Java,Python语言编写的Spark程序的执行效率不如Java和Scala。 运行模式Spark有4中运行模式: 名称 说明 local 适用于测试 standalone 并非是单节点,而是使用spark自带的资源调度框架 yarn 最流行的方式,使用yarn集群调度资源 mesos 国外使用的多 RDDSpark主要特点是提供了一个集群的分布式内存抽象,以支持需要工作集的应用。这个抽象就是RDD RDD是弹性分布式数据集,是Spark中最基本的数据抽象,任何数据在Spark中都被表示为RDD。从编程的角度来看,RDD可以简单看成是一个数组。和普通数组的区别是,RDD中的数据是分区存储的,这样不同分区的数据就可以分布在不同的机器上,同时可以被并行处理。因此,Spark应用程序所做的无非是把需要处理的数据转换为RDD,然后对RDD进行一系列的变换和操作从而得到结果。 graph TD A[加载数据集]--> B[使用transformations算子对RDD进行操作] B-->C[使用actions算子触发执行] Transformation 命令 说明 filter(func) 返回一个新的数据集,由经过func函数后返回值为true的原元素组成 map(func) 返回一个新的分布式数据集,由每个原元素经过func函数转换后组成 flatMap(func) 类似于map,但是每一个输入元素,会被映射为0到多个输出元素(因此,func函数的返回值是一个Seq,而不是单一元素) ample(withReplacement, frac, seed) 根据给定的随机种子seed,随机抽样出数量为frac的数据 union(otherDataset) 返回一个新的数据集,由原数据集和参数联合而成 groupByKey([numTasks]) 在一个由(K,V)对组成的数据集上调用,返回一个(K,Seq[V])对的数据集。注意:默认情况下,使用8个并行任务进行分组,你可以传入numTask可选参数,根据数据量设置不同数目的Task reduceByKey(func, [numTasks]) 在一个(K,V)对的数据集上使用,返回一个(K,V)对的数据集,key相同的值,都被使用指定的reduce函数聚合到一起。和groupbykey类似,任务的个数是可以通过第二个可选参数来配置的。 join(otherDataset, [numTasks]) 在类型为(K,V)和(K,W)类型的数据集上调用,返回一个(K,(V,W))对,每个key中的所有元素都在一起的数据集 groupWith(otherDataset, [numTasks]) 在类型为(K,V)和(K,W)类型的数据集上调用,返回一个数据集,组成元素为(K, Seq[V], Seq[W]) Tuples。这个操作在其它框架,称为CoGroup cartesian(otherDataset) 笛卡尔积。但在数据集T和U上调用时,返回一个(T,U)对的数据集,所有元素交互进行笛卡尔积。 map(func) 返回一个新的分布式数据集,由每个原元素经过func函数转换后组成 Actions 命令 说明 reduce(func) 通过函数func聚集数据集中的所有元素。func函数接受2个参数,返回一个值。这个函数必须是关联性的,确保可以被正确的并发执行 collect() 在Driver的程序中,以数组的形式,返回数据集的所有元素。这通常会在使用filter或者其它操作后,返回一个足够小的数据子集再使用,直接将整个RDD集Collect返回,很可能会让Driver程序OOM count() 统计RDD中元素的个数 take(n) 取RDD中的前n个元素。注意,这个操作目前并非在多个节点上,并行执行,而是Driver程序所在机器,单机计算所有的元素(Gateway的内存压力会增大,需要谨慎使用) first() 返回数据集的第一个元素(类似于take(1) ) reduce(func) 按照指定规则聚合RDD中的元素 countByValue() 统计出RDD中每个元素的个数 countByKey() 统计出KV格式的RDD中相同的K的个数 foreach(func) 以元素为单位,遍历RDD,运行func函数。 foreachPartition(func) 以分区为单位,遍历RDD,运行func函数。 saveAsTextFile(path) 将数据集的元素,以textfile的形式,保存到本地文件系统,hdfs或者任何其它hadoop支持的文件系统。Spark将会调用每个元素的toString方法,并将它转换为文件中的一行文本 saveAsSequenceFile(path) 将数据集的元素,以sequencefile的格式,保存到指定的目录下,本地系统,hdfs或者任何其它hadoop支持的文件系统。RDD的元素必须由key-value对组成,并都实现了Hadoop的Writable接口,或隐式可以转换为Writable(Spark包括了基本类型的转换,例如Int,Double,String等等) MLlib与ML spark.mllib包含基于RDD的原始算法API。Spark MLlib 历史比较长,在1.0 以前的版本即已经包含了,提供的算法实现都是基于原始的 RDD。 spark.ml则提供了基于DataFrames 高层次的API,可以用来构建机器学习工作流(PipeLine)。ML Pipeline 弥补了原始 MLlib 库的不足,向用户提供了一个基于 DataFrame 的机器学习工作流式 API 套件。 使用 ML Pipeline API可以很方便的把数据处理,特征转换,正则化,以及多个机器学习算法联合起来,构建一个单一完整的机器学习流水线。这种方式给我们提供了更灵活的方法,更符合机器学习过程的特点,也更容易从其他语言迁移。Spark官方推荐使用spark.ml。如果新的算法能够适用于机器学习管道的概念,就应该将其放到spark.ml包中,如:特征提取器和转换器。开发者需要注意的是,从Spark2.0开始,基于RDD的API进入维护模式(即不增加任何新的特性),并预期于3.0版本的时候被移除出`MLlib`。 SQL、DataFrame和DataSet附录RDD与DataFrame、DataSetDataFrame = SchemaRDD = RDD<ROW> 从图虫颜色来区分,DataFrame是列式存储。当要取Age这一列时,RDD必须先取出person再取Age,而DataFrame可以直接取Age这一列]]></content>
<categories>
<category>大数据平台</category>
<category>spark</category>
</categories>
<tags>
<tag>分布式</tag>
<tag>spark</tag>
</tags>
</entry>
<entry>
<title><![CDATA[评估模型]]></title>
<url>%2F2019%2F08%2F07%2F%E4%BA%BA%E5%B7%A5%E6%99%BA%E8%83%BD%2F%E8%AF%84%E4%BC%B0%2F%E8%AF%84%E4%BC%B0%E6%A8%A1%E5%9E%8B%2F</url>
<content type="text"><![CDATA[真实类别 预测为正例 预测为反例 正例 TP(真正例) FN(假反例) 反例 FP(假正例) TN(真反例) 1234# 混淆矩阵from sklearn.metrics import confusion_matrixconfusion = confusion_matrix(y_test,y_prob) 准确率、错误率、召回率、查准率 名称 公式 说明 准确率($Accuracy$) $\frac{TP+TN}{TP+TN+FP+FN}$ 预测正确的比例 错误率($Error$) $\frac{FP+FN}{TP+TN+FP+FN}$ 预测错误的比例 召回率/查全率($Recall$) $\frac {TP}{TP+FN}$ 真正例被预测正确的比例。有多少好瓜被选出来 精确率/查准率($Precision$) $\frac{TP}{TP+FP}$ 预测为正例中正确的比例。预测为的好瓜的类别中有多少好瓜 123'''准确率、召回率、f分数'''from sklearn.metrics import classification_reportprint(classification_report(y_test,y_prob,target_names=['not nine','nine'])) P-R曲线与平衡点一般来说, 查准率高的时候,召回率偏低;召回率高的时候,查准率往往降低。以西瓜问题为例:查准率指的是所有预测为好瓜的西瓜中,真正的好瓜所占的比例;召回率指的是真正的好瓜中,有多少好瓜被挑出来了。如果所有西瓜都预测为好瓜则召回率为1,但是查准率就会降低因为所有坏瓜也预测为了好瓜。 12345678910111213'''准确率与召回率曲线'''from sklearn.metrics import precision_recall_curve#y_prob[:,1]是一个2维数组,1表示第1个类别precision,recall,threshold=precision_recall_curve(y_test,y_prob[:,1]) import numpy as npimport matplotlib.pyplot as pltclose_zero = np.argmin(np.abs(threshold)) # 找到最接近0的阈值plt.plot(recall[close_zero],precision[close_zero],'o',markersize=10,label='threshold zero',fillstyle = 'none',c = 'k',mew = 2)plt.plot(recall,precision,label='precision recall curve')plt.xlabel('Recall')plt.ylabel('Precision')plt.show() P-R曲线以查准率P为纵轴,召回率R为横轴作图,就得到了查准率-召回率曲线,称为“P-R曲线”。 。对于一个排序模型来说,其P-R曲 线上的一个点代表着,在某一阈值下,模型将大于该阈值的结果判定为正样本, 小于该阈值的结果判定为负样本,此时返回结果对应的召回率和精确率。 整条P-R 曲线是通过将阈值从高到低移动而生成的,左边的阈值较高,右边的阈值较低。 y轴-查准率(精确率)P = \frac {TP} {TP+FP} \\ x轴-查全率(召回率)R = \frac {TP} {TP+FN}在比较两个模型的性能时,如果一个学习器的P-R曲线被另外一个学习器P-R曲线包住,那么后者的性能优于前者(此时后者的查全率跟查准率都高于前者),如上图,A的性能优于C,B的性能也优于C;如果两个学习器的曲线有交叉点(如A和B),那么难以断定两个学习器性能的优劣,只能在具体的查准率或者查全率条件下进行比较。 举例计算假设有4个样本,样本的标签分别为 $0,1,0,1$;模型的预测概率分别为$0.1,0.35,0.4,0.8$; label 0 1 0 1 pred 0.1 0.35 0.4 0.8 阈值 0.1 0.35 0.4 0.8 那么在阈值分别取$0.1, 0.35, 0.4, 0.8$的时候。分别判断出每个pred是TP/FP/TN/FP中的哪个,进而得出当前阈值下的R和P,也就是(Recall, Precision)这一P-R曲线图上的点;对于所有阈值都计算相应的(Recall, Precision),则得到完整的ROC曲线上的几个关键点。 阈值 样本1 样本2 样本3 样本4 $precision=\frac{TP}{TP+FP}$ $recall=\frac {TP}{TP+FN}$ 0.1 FP TP FP TP 0.5 $\frac{2}{2+0}=1$ 0.35 TN TP FP TP $\frac{2}{3}$ $\frac{2}{2+0}= 1 $ 0.4 TN FN FP TP 0.5 $\frac{1}{1+1}=0.5$ 0.8 TN FN TN TP 1 $\frac {1} {1+1}=0.5$ 平衡点(BEP-》Break-Even Point)如果两个学习器的P-R曲线有交叉点(如A和B),那么通过平衡点或者F1度量断定两个学习器性能的优劣平衡点(Break-Even Point,简称BEP)指得是“P=R”时候的取值,如上图,C的的BEP是0.64,。基于BEP的比较,可以认为学习器A优于B。 F1指数与$F_\beta$指数正如上文所述,Precision和Recall指标有时是此消彼长的,即精准率高了,召回率就下降,在一些场景下要兼顾精准率和召回率,最常见的方法就是F1指数与$F_\beta$指数。 F1指数F1指数实际为查准率和召回率的调和平均数。例如小明跑步10公里,前面5公里用了1小时,后面5公里用了2小时,其平均速度为$\frac{10}{v}=(\frac {5}{1}+ \frac {5}{2})$ \frac 1 {F1} =\frac 1 2 (\frac 1{P}+\frac 1 R)$F_\beta$指数对于特定的应用场景,查准率和查全率的重视度是不一样的。例如在追捕逃犯时,希望尽可能的不遗漏逃犯,因此查全率较为重要;而在搜索引擎的搜索过程中,希望给用户精准的搜索信息,因此查准率更为重要。F1度量的更一般形式——$F_β$,修改了路程 \frac{1}{F_\beta }=\frac{1}{1+\beta ^2}(\frac{1}{P}+\frac{\beta ^2}{R})\\$β=R/P$ ,其中$β$值越大召回率越重要,$β$值越小查准率越重要。$β$值为两者重要性的比例 G-mean指数G-mean是正例的召回率与负例的召回率的综合指标。与F1的比较:在数据平衡度不是很大的情况下,F1和G-mean可以作为较好的评测指标,但是当训练样本很不平衡时,G-mean更好。 G - mean = \sqrt{\frac {TP}{TP+FN}\times \frac {TN}{TN+FP}} = \sqrt {查全率(召回率)真正例 \times 查全率(召回率)真负例}ROC曲线与AUC1234567891011121314151617'''ROC与AUC'''from sklearn.metrics import roc_curveimport matplotlib.pyplot as pltfpr,tpr,thresholds = roc_curve(y_test ,y_prob[:,1] )plt.plot(fpr,tpr,label = 'ROC Curve')plt.xlabel('FPR')plt.ylabel('TPR(recall)')# ROC曲线import numpy as npclose_zero = np.argmin(np.abs(thresholds)) # 0的位置plt.plot(fpr[close_zero],tpr[close_zero],'o',markersize = 10,label = 'threshold zero',fillstyle = 'none',c = 'k' ,mew = 2)plt.legend(loc = 4)# AUCfrom sklearn.metrics import roc_auc_scoreruc_auc = roc_auc_score(y_test ,y_prob[:,1]) ROC曲线真正例率指的是真实正例中有多少被预测为正例,假正例率指的是真实反例中有多少被预测为正例。可以看出TPR和Recall的形式是一样的,就是查全率了,FPR就是1-负例的召回率。如果所有西瓜都预测为好瓜则查全率为1,但是假正例率也会升高因为所有坏瓜也预测为了好瓜。 y轴-真正例率 TPR = \frac {TP}{TP+FN}=正例的召回率=\frac{预测为正且的正样本数}{实际为正的样本数}\\ x轴-假正例率 FPR = \frac {FP}{TN+FP}=1-\frac{TN}{TN+FP}=1-负例的召回率=\frac{预测为正的负样本数}{实际的负样本数} 以真正例率$TPR $为纵轴,假正例率$FPR$为横轴作图,就得到了“ROC曲线”。 。对于一个排序模型来说,其ROC曲线上的一个点代表着,在某一阈值下,模型将大于该阈值的结果判定为正样本, 小于该阈值的结果判定为负样本,此时返回结果对应的真正例率和假正例率。 整条ROC曲线是通过将阈值从高到低移动而生成的,左边的阈值较高,右边的阈值较低。 举例计算假设有4个样本,样本的标签分别为 $0,1,0,1$;模型的预测概率分别为$0.1,0.35,0.4,0.8$; label 0 1 0 1 pred 0.1 0.35 0.4 0.8 阈值 0.1 0.35 0.4 0.8 那么在阈值分别取$0.1, 0.35, 0.4, 0.8$的时候。分别判断出每个pred是TP/FP/TN/FP中的哪个,进而得出当前阈值下的TPR和FPR,也就是(FPR, TPR)这一ROC曲线图上的点;对于所有阈值都计算相应的(FPR, TPR),则得到完整的ROC曲线上的几个关键点。 阈值 样本1 样本2 样本3 样本4 $TPR=\frac {TP}{TP+FN}$ $FPR\frac {FP}{TN+FP}$ 0.1 FP TP FP TP $\frac{2}{2+0}=1$ $\frac{2}{0+2}=1$ 0.35 TN TP FP TP $\frac{2}{2+0}= 1 $ $\frac{1}{1+1}=0.5$ 0.4 TN FN FP TP $\frac{1}{1+1}=0.5$ $\frac{1}{1+1}=0.5 $ 0.8 TN FN TN TP $\frac {1} {1+1}=0.5$ 0 AUC指数AUC:ROC曲线的线下面积。与P-R曲线类似,如果学习器A的ROC曲线被学习器B完全“包住”,那么后者性能优于前者(相同的假正例率,其真正例率更高)。但是当两个曲线有交点时,则需要比较曲线下的面积AUC(Area Under ROC Curve)。 k-s指标K-S(Kolmogorov-Smirnov)统计量越大,表示模型能够将正、负客户区分开的程度越大。KS值的取值范围是[0,1] 。与ROC曲线相似 12345678910111213'''ks'''from sklearn.metrics import roc_curveimport matplotlib.pyplot as pltfpr,tpr,thresholds = roc_curve(y_test ,y_prob[:,1]) #计算fpr与tprthresholds=np.flipud(thresholds) #thresholds逆序plt.plot(thresholds,fpr,label = 'fpr')plt.plot(thresholds,tpr,label = 'tpr')plt.plot(thresholds,tpr-fpr,label = 'ks')plt.xlabel('threshold')plt.ylabel('Gradual ratio') #累计占比plt.xlim((0,1))plt.show()ks=max(tpr-fpr) ks曲线 TPR = TP/(TP+FN)=正例的召回率=\frac{预测为正且的正样本数}{实际为正的样本数} \\ FPR = FP/(FP+TN)=1-\frac{TN}{TN+FP}=1-负例的召回率=\frac{预测为正的负样本数}{实际的负样本数}洛伦兹线:两条线,其横轴是模型评分的阈值,纵轴是$TPR$(真正类率)与$FPR$(假正类率)的值 K-S曲线:洛伦兹线中两条线的差值($TPR-FPR$),值范围[0,1] 。 我们用一个风控模型预测一个人是好人的概率,图中黑线的洛伦兹线($TPR$与$FPR$)、红线的ks曲线。横坐标是模型评分的阈值,当阈值为0时,所有的用户被预测为坏用户,$TPR=FPR=0$;当阈值为1时,所有用户被预测为好用户,$TPR=FPR=1$。 这两条曲线之间的差值($TPR-FPR$),就是K-S曲线。如图所示,给定一个通过率20%(拒绝率80%),则该模型可以挑出来60%的好人,同时漏进来8%的坏人(92%的坏人都被拒绝掉了)。那么K-S曲线在这个通过率上的值,就是60%-8%=0.52。 K-S统计量K-S曲线中的最大值被称为K-S统计量。其取值在0到1之间。如果是随机抽样,好人的洛伦兹曲线跟坏人的是重合的,K-S统计量为0。而最理想的风控模型,好人和坏人完全分开,K-S统计量的值为1。 多类别评估123'''准确率、召回率、f分数'''from sklearn.metrics import classification_reportprint(classification_report(y_test,y_prob) 12345678 precision recall f1-score support 0 1.00 0.67 0.80 187169 #类别0的评估 1 0.01 0.66 0.02 1144 #类别1的评估 micro avg 0.67 0.67 0.67 188313 #micro平均值 macro avg 0.50 0.67 0.41 188313 #macro平均值weighted avg 0.99 0.67 0.80 188313 #weighted平均值 举例计算 Label 1 2 3 2 3 3 1 2 2 Prediction 2 2 1 2 1 3 2 3 2 micro avg TP:分类正确的样本。如绿色所示 FN=FP:分类错误的样本。如红色所示 precision \;\;\; P=\frac{TP}{TP+FP}=\frac{4}{4+5}=0.4444\\ recall\;\;\;\;R=\frac{TP}{TP+FN}=\frac{4}{4+5}=0.4444\\ F1=2\times \frac{\frac{4}{9} \times \frac{4}{9}}{\frac{4}{9} + \frac{4}{9}}=0.44444macro avg TP FP FN precision recall F1 class 1 0 2 2 0 0 0 class 2 3 2 1 $\frac{3}{5}=0.6$ $\frac{3}{4}=0.75$ $\frac{2}{3}$ class 3 1 1 2 $\frac{1}{2}$ $\frac{1}{3}$ $\frac{2}{5}$ precision:$(0+\frac{3}{5}+\frac{1}{2})/3=0.36667$ recall:$(0+\frac{3}{4}+\frac{1}{3})/3=0.3611111$ F1:$(0+\frac{2}{3}+\frac{2}{5})/3=0.3555556$ weighted avg加入类别的权重。3类样本的权重分别如下所示 class1 class2 class3 2/9 4/9 3/9 precision:$\frac{2}{9} \times 0+\frac{4}{9} \times \frac{3}{5}+ \frac{3}{9} \times \frac{1}{2}=0.4333333$ recall:$\frac{2}{9} \times 0+\frac{4}{9} \times \frac{3}{4}+ \frac{3}{9} \times \frac{1}{3}=0.444444444$ F1:$\frac{2}{9} \times 0+\frac{4}{9} \times \frac{2}{3}+ \frac{3}{9} \times \frac{2}{5}=0.429630$ 综合说明 micro avg 受不平衡样本影响较小。precision=recall=f1-score macro avg 受不平衡样本影响较小。几个类别的平均数 weighted avg 受不平衡样本影响较大。倾向于较大的类别 不同类别的概率分布图判断模型的分类性能,分的开不开。 12345678910111213141516171819def class_porb(true_label,guess_label) """ 用户正负比例的概率分布图 Args: true_label: 测试样本真实标签序列 guess_label: 测试样本预测标签序列 returns: None """ plt.hist(guess_label[true_label == 1], bins=50, color='blue', weights=np.ones_like(guess_label[true_label == 1]) / len(guess_label[true_label == 1]), label='Bad users') plt.hist(guess_label[true_label == 0], bins=50, color='green', alpha=0.8, weights=np.ones_like(guess_label[true_label == 0]) / len(guess_label[true_label == 0]), label='Good users') plt.grid() plt.xlabel(u'预测概率') plt.ylabel(u'用户占比') plt.title(u'正负类别概率分布') plt.legend(loc='best') plt.show() 回归评估指标 均方误差(Mean Squared Error,MSE) MSE=\frac{1}{m} \sum_{i=1}^{m}(f_i-y_i)^2 均方根误差 (Root Mean Squard Error,RMSE) RMSE=\sqrt{\frac{1}{m} \sum_{i=1}^{m}(f_i-y_i)^2}它的意义在于开个根号后,误差的结果就与数据是一个级别的,可以更好地来描述数据。标准误差对一组测量中的特大或特小误差反映非常敏感,所以,标准误差能够很好地反映出测量的精密度。这正是标准误差在工程测量中广泛被采用的原因。 平均绝对误差 (Mean Absolute Error,MAE) MAE=\frac{1}{m}\sum_{i=1}^{m}|f_i-y_i| R-squared R^2=1-\frac{\sum_{i=1}^m(f_i-y_i)^2}{\sum_{i=1}^m(\bar y_i-y_i)^2}=1-\frac{\frac{1}{m}\sum_{i=1}^m(f_i-y_i)^2}{\frac{1}{m}\sum_{i=1}^m(\bar y_i-y_i)^2}=1-\frac{MSE(f,y)}{Var(y)} 总结下面的简单总结,主要是对比各个参数,并说明不平衡数据集对参数的影响。 真实类别 预测为正例 预测为反例 正例 TP(真正例) FN(假反例) 反例 FP(假正例) TN(真反例) 名称 公式 说明 测试集为不平衡数据集 准确率($Accuracy$) $\frac{TP+TN}{TP+TN+FP+FN}$ 预测正确的比例 影响较大,因为包含两个不平衡样本的数据 错误率($Error$) $\frac{FP+FN}{TP+TN+FP+FN}$ 预测错误的比例 影响较大,因为包含两个不平衡样本的数据 召回率/查全率($Recall$) $\frac {TP}{TP+FN}$ 实际为正样本中有多少被预测出来 无影响,$TP、FN$全部来自实际正例数据集 精确率/查准率($Precision$) $\frac{TP}{TP+FP}$ 预测为正样本中有多少是真实的正样本 影响较大,因为包含两个不平衡样本的数据 PR曲线 横坐标为recall纵坐标为precision 左侧的阈值大,右侧的阈值小 影响较大,因为纵坐标包含两个不平衡样本的数据 F1指数 $\frac{1}{F1}=\frac{1}{P}+\frac{1}{R}$ 因为recall与precision存在对立关系,为了综合评估recall与precision。所以提出F1指数 影响较大,因为precision包含两个不平衡样本的数据 $F_\beta$指数 $\frac{1}{F_\beta }=\frac{1}{1+\beta ^2}(\frac{1}{P}+\frac{\beta ^2}{R})$ 通过调整$\beta$的比例,调整$F_\beta$的侧重关系 影响较大,因为precision包含两个不平衡样本的数据 G-mean $\sqrt{\frac {TP}{TP+FN}\times \frac {TN}{TN+FP}} $ $\sqrt {(召回率)正例 \times (召回率)负例}$越高越好;为了解决样本不平衡问题 无影响,因为召回率没有使用不平衡数据相比 ROC曲线 横坐标为,$\frac {FP}{TN+FP}=1-负例的召回率$纵坐标为,$\frac {TP}{TP+FN}=正例的召回率$ 左侧的阈值大,右侧的阈值小 无影响,因为召回率没有使用不平衡数据相比 AUC ROC的面积 越大越好 无影响,因为召回率没有使用不平衡数据相比 KS 横坐标阈值纵坐标为正例的召回率以及(1-负例的召回率)之间的差值 目的选定阈值,使得正例的召回率与1-负例的召回率差值最大。与ROC曲线具有一定的相似性 无影响,因为召回率没有使用不平衡数据相比]]></content>
<categories>
<category>人工智能</category>
<category>评估</category>
</categories>
<tags>
<tag>模型评估</tag>
</tags>
</entry>
<entry>
<title><![CDATA[linux子系统-win10]]></title>
<url>%2F2019%2F08%2F05%2Flinux%2Flinux%E5%AD%90%E7%B3%BB%E7%BB%9F-win10%2F</url>
<content type="text"><![CDATA[Windows10安装Ubuntu子系统一、开启开发模式“设置 - 更新和安全 - 针对开发人员”设置页面,选中“开发人员模式”。 二、启用或关闭Windows功能 三、安装 Linux 子系统打开 Windows 应用市场,输入 linux 搜索,选择你自己想要的系统版本 四、zshzsh(Z Shell)是一个功能强大的交互式shell脚本命令解释器。它不仅支持bash,还提供一些强大和高效的功能,例如更好的自动补全和纠错。貌似很好用 12345sudo apt-get install zsh #安装oh-my-zsh首先需要安装zshzsh --version #查看版本号sh -c "$(curl -fsSL https://raw.githubusercontent.com/robbyrussell/oh-my-zsh/master/tools/install.sh)" #使用curl安装oh-my-zshsh -c "$(wget https://raw.githubusercontent.com/robbyrussell/oh-my-zsh/master/tools/install.sh -O -)" #使用wget安装oh-my-zsh 在vim ~/.bash_profile中启用zsh,编辑文件,在末尾添加: 12exec zshsource .zshrc zsh插件zsh插件可以分为自带插件与第三方插件。 官方插件: 将插件填进~/.oh-my-zsh/plugins 第三方插件:将插件填进~/.oh-my-zsh/custom/plugins 然后在 ~/.zshrc 配置文件中的 plugins 变量中添加对应插件的名称即可,运行source ~/.zshrc z:自动跳转 colored-man-pages:手册页高亮 zsh-autosuggestion:自动补全,可能导致乱码 zsh-syntax-highlighting:命令高亮 incr:代码提示 。将zsh(自动补全版本 、非自动补全版本)放进~/.oh-my-zsh/custom/plugins/incr/incr-0.2.zsh;在~/.zshrc文件末尾加入source ~/.oh-my-zsh/custom/plugins/incr/incr*.zsh export LC_ALL=en_US.UTF-8 export LANG=en_US.UTF-8 环境变量在使用vscode的wsl开发环境的时候。vscode的终端只会运行~/.zshrc文件,不会运行/etc/profile文件;因此在/etc/profile文件中的环境变量不会被导入。为了解决这个问题需要在~/.zshrc文件中启用/etc/profile文件。在~/zshrc文件中添加 注意: 12source /etc/profile #必须在上面source $ZSH/oh-my-zsh.sh 五、安装配置Cmder命令行终端访问cmder.net,下载mini版本即可 修改启动参数 修改启动参数填入bash -cur_console:p,这样我们直接打开cmder后就可以进入到linux系统了。这种方法一般vim的方向键不可以使用。 或者使用下面的方法对cmder进行修改。 将cmder添加进win10的右键菜单 右键点击我的电脑—属性—高级系统设置—环境变量—系统变量,点击新建,变量名设置为:CMDER_HOME,变量值直接粘贴刚才拷贝的地址, 然后点击系统变量中的path,点击新建,输入%CMDER_HOME% 使用管理员权限打开cmder终端运行Cmder.exe /REGISTER ALL 文件夹互相访问win10访问ubuntu1C:\Users\xxx\AppData\Local\Packages\CanonicalGroupLimited.UbuntuonWindows_79rhkp1fndgsc\LocalState\rootfs\home\xxx ubuntu访问win101/mnt/c/Users # 进入win10的c盘的Users 可以建立一个win10的快捷方式1ln -s /mnt/c/Users/xxx ~/win10 建立链接]]></content>
<categories>
<category>linux</category>
<category>wsl</category>
</categories>
<tags>
<tag>WSL</tag>
<tag>操作系统</tag>
</tags>
</entry>
<entry>
<title><![CDATA[DML-SQL]]></title>
<url>%2F2019%2F07%2F31%2F%2F%E7%BC%96%E7%A8%8B%E8%AF%AD%E8%A8%80%2Fsql%E8%AF%AD%E8%A8%80%2FSQL%2FDML-SQL.md%2F</url>
<content type="text"><![CDATA[命令 作用 insert 插入语句insert into:追加数据insert overwrite:覆盖数据 UPDATE 更新语句 DELETE 删除语句 增、删、改123456789101112131415-- 插入语句Insert Into Persons (LastName, Address) VALUES ('Wilson', 'Champs-Elysees')Insert Into St (S#, Sname, avgScore) Select S#, Sname, Avg(Score) From Student, SC Where Student.S# = SC.S# Group by Student.S# ;Insert overwrite St (S#, Sname, avgScore) Select S#, Sname, Avg(Score) From Student, SC Where Student.S# = SC.S# Group by Student.S# ;-- 更新语句-UPDATE 修改某一行数据UPDATE Person SET Address = 'Zhongshan 23', City = 'Nanjing' WHERE LastName = 'Wilson'-- 删除语句-DELETEDELETE FROM Person WHERE LastName = 'Wilson' --删除行名为Wilson 查询语句12345select expr,列名 as 列的别名,function(列名) as 列的别名from 表名 as 表的别名where 条件(对每一个元组进行过滤)group by 列名(分组) having 对每一组进行过滤Order by 列名 [asc|desc] 命令 关键字 说明 select expr 常量 when case select when st.score>90 then 1 when st.score<60 then 0 else 0 end label function count 求个数 sum 求和 avg 求平均 max 求最大 min 求最小 first 返回列中第一个记录的值 last 返回列中最后一个记录的值 UCASE 把字段的值转换为大写 LCASE 把字段的值转换为小写 MID 从文本字段中提取字符SELECT MID(column_name,start[,length]) FROM table_name;column_name:必需。要提取字符的字段。Start:必需。规定开始位置(起始值是 1)。Length:可选。要返回的字符数。如果省略,则 MID() 函数返回剩余文本。 LEN 函数返回文本字段中值的长度 ROUND 函数用于把数值字段舍入为指定的小数位数。SELECT ROUND(column_name,decimals) FROM table_name;column_name 必需。要舍入的字段。decimals 必需。规定要返回的小数位数。 NOW 函数返回当前系统的日期和时间 FORMAT FORMAT 函数用于对字段的显示进行格式化。SELECT ProductName, UnitPrice, FORMAT(Now(),'YYYY-MM-DD') as PerDateFROM Products from 原理 从表格中选取数据,采用笛卡尔积 join连接 join oninner join on INNER JOIN 与 JOIN 是相同的不保留空值 Left join on 保留左表中所有的数据,右表中不能匹配的数据使用空值填充 Right join on 保留右表中所有的数据,左表中不能匹配的数据使用空值填充 Full join on 保留两个表中所有的数据,,两个表中不能匹配的数据使用空值填充 where 运算符 =、<>、>、<、>=、<= 函数 between BETWEEN ... AND 会选取介于两个值之间的数据范围。这些值可以是数值、文本或者日期 like "%" : 可以匹配0个或者多个字符 “ _ ” :只可以匹配一个字符 “\” : 转义字符,用于去掉一些特殊字符 ,使其变为普通字符。比如用"\%"可以去匹配字符%,而“”是去匹配字符“_”. [charlist]:字符列中的任何单一字符 [^charlist]或者[!charlist]:不在字符列中的任何单一字符 in = exists 是否存在 group by 列名 分组计算 having 条件 函数 count、sum、avg、max、min、first、last、UCASE、LCASE、MID、LEN、ROUND、NOW、FORMAT 运算符 =、<>、>、<、>=、<=;like;in = exists 关系代数操作1234567891011121314-- 并:求学过002号课的同学或学过003号课的同学学号Select S# From SC Where C# = ‘002’UNION [ALL] --使用ALL命令之后会保留所有重复的元组Select S# From SC Where C# = ‘003’;-- 交:求既学过002号课,又学过003号课的同学学号Select S# From SC Where C# = ‘002’INTERSECTSelect S# From SC Where C# = ‘003’;-- 差:假定所有学生都有选课,求没学过002号课程的学生学号Select DISTINCT S# From SCEXCEPTSelect S# From SC Where C# = ‘002’; 排序1order by columns asc(desc) #升序或者降序 分组排序123456select subject,score,rank() over(partition by name order by score) rank,dense_rank() over(partition by name order by score) dense_rank,row_number() over(partition by name order by score) row_number,rank() over(order by score) rank2from score_table --对score_table中的数据按照subject分组对score进行排序 dense_rank、rank与row_number的区别 subject score rank dense_rank row_number rank2 数学 67 1 1 1 1 数学 77 2 2 2 2 数学 78 3 3 3 3 数学 88 4 4 4 4 数学 99 5 5 5 5 语文 60 1 1 1 6 语文 70 2 2 2 6 语文 80 3 3 3 8 语文 80 3 3 4 9 语文 90 5 4 5 10]]></content>
<categories>
<category>编程语言</category>
<category>SQL</category>
</categories>
<tags>
<tag>数据库</tag>
<tag>sql</tag>
</tags>
</entry>
<entry>
<title><![CDATA[DCL-SQL]]></title>
<url>%2F2019%2F07%2F31%2F%2F%E7%BC%96%E7%A8%8B%E8%AF%AD%E8%A8%80%2Fsql%E8%AF%AD%E8%A8%80%2FSQL%2F%2FDCL-SQL.md%2F</url>
<content type="text"><![CDATA[用户对不同的数据对象有不同的存取权限 不同的用户对同一对象也有不同的权限 用户还可将其拥有的存取权限转授给其他用户]]></content>
<categories>
<category>编程语言</category>
<category>SQL</category>
</categories>
<tags>
<tag>数据库</tag>
<tag>sql</tag>
</tags>
</entry>
<entry>
<title><![CDATA[DDL-SQL]]></title>
<url>%2F2019%2F07%2F31%2F%2F%E7%BC%96%E7%A8%8B%E8%AF%AD%E8%A8%80%2Fsql%E8%AF%AD%E8%A8%80%2FSQL%2FDDL-SQL.md%2F</url>
<content type="text"><![CDATA[数据库12345678-- 创建数据库Create database my_db; -- 删除数据库Drop database my_db;-- 指定数据库use my_db;-- 关闭数据库close my_db; 表123456-- 创建表格CREATE TABLE table_name-- 修改表格alter table tablename -- 删除表格DROP TABLE Persons 创建表格12create table 表名 as select 列名 from 表名 12345678CREATE TABLE Persons( P_Id int NOT NULL PRIMARY KEY CHECK (P_Id>0), --不为空,主键,删选条件:P_Id>0 LastName varchar(255) NOT NULL, --不为空 FirstName varchar(255), Address varchar(255), City varchar(255) DEFAULT 'Sandnes' --默认值为'Sandnes'); 1234567CREATE TABLE table_name( 列名1 数据类型(size) 约束条件, 列名2 数据类型(size) 约束条件, 列名3 数据类型(size) 约束条件, ....); 参数 说明 列名 规定表中列的名称 数据类型 例如varchar、integer、decimal、date。不同的数据库有不同的数据类型 约束条件 Primary key 主键约束。每个表只能创建一个主键约束 Unique 唯一性约束(即候选键)。每个表可以有多个唯一性约束,但只能有一个Primary key。 Not null 非空约束。是指该列允许不允许有空值出现,如选择了Not null表明该列不允许有空值出现。 CHECK (search_cond) 约束用于限制列中的值的范围,search_cond为条件。如果对单个列定义 CHECK 约束,那么该列只允许特定的值;如果对一个表定义 CHECK 约束,那么此约束会在特定的列中对值进行限制。 FOREIGN KEY 一个表中的 FOREIGN KEY指向另一个表中的PRIMARY KEY DEFAULT 如果没有规定其他的值,那么会将默认值添加到所有的新记录。 AUTO_INCREMENT 每次插入时,自动生成主键的值 修改表格1234alter table tablename - add 增加新列 - drop 删除列或者完整性约束条件 - modify 修改列定义 12345678-- 在表 "Person" 中添加一个名为 "Birthday" 的新列。ALTER TABLE Person ADD Birthday date;-- 删除 "Person" 表中的 "Birthday" 列:ALTER TABLE Person DROP COLUMN Birthday;-- 删除 "Person" 表中 "P_Id" 的主键:ALTER TABLE Person DROP PRIMARY KEY(P_Id);-- 改变 "Person" 表中 "Birthday" 列的数据类型。ALTER TABLE Person Modify Birthday year; 删除表格1DROP TABLE table_name -- 删除表 视图表中存储的是实际数据,而视图中保存的是从表中取出数据所使用的SELECT语句。一般来说你可以用update,insert,delete等sql语句修改表中的数据,而对视图只能进行select操作。但是也存在可更新的视图,对于这类视图的update,insert和delete等操作最终会作用于与其相关的表中数据。 创建视图**定义视图时不能使用 ORDER BY 子句** 12345CREATE VIEW view_name ASSELECT column_name(s)FROM table_nameWHERE condition[with check option] -- 指明当对视图进行insert,update,delete时,要检查进行insert/update/delete的元组是否满足视图定义中子查询中定义的条件表达式 12345CREATE VIEW CUSTOMERS_VIEW ASSELECT name, ageFROM CUSTOMERSWHERE age IS NOT NULLWITH CHECK OPTION; --这里 WITH CHECK OPTION 使得视图拒绝任何 AGE 字段为 NULL 的条目,因为视图的定义中,AGE 字段不能为空。对视图不管修改前还是修改后都必须遵从此规定。 使用视图1Select * From view_name 视图的更新(增、删、改)一般来说你可以用update,insert,delete等sql语句修改表中的数据,而对视图只能进行select操作。但是也存在可更新的视图,对于这类视图的update,insert和delete等操作最终会作用于与其相关的表中数据。 对于可更新的视图,在视图中的行和基表中的行之间必须具有一对一的关系。还有一些特定的其他结构,这类结构会使得视图不可更新。如果视图包含下述结构中的任何一种,那么它就是不可更新的: 分类 说明 非一对一 DISTINCT关键字、GROUP BY子句、UNION运算符、位于选择列表中的子查询 未知 聚合函数、ORDER BY子句、HAVING子句、FROM子句中包含多个表、WHERE子句中的子查询,引用FROM子句中的表 其他 SELECT语句中引用了不可更新视图ALGORITHM 选项指定为TEMPTABLE(使用临时表总会使视图成为不可更新的) 插入数据12INSERT INTO CUSTOMERS_VIEW VALUES('李牧',50); 修改数据1UPDATE CUSTOMERS_VIEW SET age = 58 where name = '李牧'; 删除数据1DELETE FROM CUSTOMERS_VIEW WHERE name = '李牧'; 删除视图1Drop View view_name]]></content>
<categories>
<category>编程语言</category>
<category>SQL</category>
</categories>
<tags>
<tag>数据库</tag>
<tag>sql</tag>
</tags>
</entry>
<entry>
<title><![CDATA[SQL]]></title>
<url>%2F2019%2F07%2F31%2F%E7%BC%96%E7%A8%8B%E8%AF%AD%E8%A8%80%2Fsql%E8%AF%AD%E8%A8%80%2FSQL%2F</url>
<content type="text"><![CDATA[名词解释 名称 说明 候选码(超级码) 关系中的一个属性组,其值能唯一标识一个元组,若从该属性组中去掉任何一个属性,它就不具有这一性质了,这样的属性组称为候选码。 主码 当有多个候选码时,可以选定一个作为主码。 主属性 包含在任何一个候选码中的属性被称为主属性。 全码 All-key关系模型的所有属性组组成该关系模式的候选码,称为全码。即所有属性当作一个码。若关系中只有一个候选码,且这个候选码中包含全部属性,则该候选码为全码。 外码 关系R中的一个属性组,它不是R的候选码,但它与另一个关系S的候选码相对应,则称为这个属性组为R的外码或外键。s 实体完整性 关系主码中的属性值不能为空值。 关系运算 运算符 说明 集合 ∪ 并-Union 关系R和关系S具有相同的属性,且相应的属性来自同一个值域`Select * from R Union Select * from S;``Select * from R Except Select * from S;``Select * from R Intersect Select * from S;` - 差-Except ∩ 交-Except × 笛卡尔积 R: n个属性, k1个元组;S: m个属性, k2个元组R×S:(n+m) 列元组的集合行: k1×k2个元组`Select * from R,S;` 关系 σ 选择 从关系R中选取符合条件的元组,`SELECT R.学号,R.课程名, R.分数 from R WHERE 分数>85` π 投影 选取属性,选取的记过会删除重复的元组`SELECT 品名,数量 FROM R;` 连接 θ连接 从两个关系的笛卡尔积中选取属性间满足一定条件的元组 自然连接 从两个关系的笛卡尔积中选取相同属性分量相等的元组 外连接 如果把悬浮元组也保存在结果关系中,而在其他属性,上填空值(Null),就叫做外连接 ÷ 除 给定关系R (X, Y) 和S (Y, Z), 其中X, Y, Z为属性组。R中的Y与S中的Y出自相同的域集。R与S的除运算得到一个新的关系P(X)。其中P(x)与S(Y)组成的元组都在R(X,Y)中`SELECT DISTINCT R.X FROM R R1WHERE NOT EXISTS ( SELECT S.Y FROM S WHERE NOT EXISTS ( SELECT * FROM R R2 where R1.X=R2.X and R2.Y=S.Y ))` 数据定义语言-DDL 命令 描述 CREATE 创建新的表、视图或者其他数据库中的对象 ALTER 修改现存数据库对象,比如一张表 DROP 删除表、视图或者数据库中的其他对象 数据操纵语言-DML 命令 描述 INSERT 创建一条新记录 UPDATE 修改记录 DELETE 删除记录 SELECT 从一张或者多张表中检索特定的数据 数据控制语言-DCL 命令 描述 GRANT 赋予用户特权 REVOKE 收回赋予用户的特权]]></content>
<categories>
<category>编程语言</category>
<category>SQL</category>
</categories>
<tags>
<tag>目录</tag>
<tag>数据库</tag>
<tag>sql</tag>
</tags>
</entry>
<entry>
<title><![CDATA[vim]]></title>
<url>%2F2019%2F07%2F31%2Flinux%2Fvim%2F</url>
<content type="text"><![CDATA[命令模式 功能 描述 常用命令 i、a、o 切换到输入模式,以输入字符x 删除当前光标所在处的字符。: 切换到底线命令模式,以在最底一行输入命令。HOME(0) END($) 移动光标到行首/行尾 搜索/替换 删除 nx n 为数字,连续向后删除 n 个字符。举例来说,我要连续删除 10 个字符, 『10x』。dd 删除游标所在的那一整行(常用)ndd n 为数字。删除光标所在的向下 n 行,例如 20dd 则是删除 20 行 (常用)d1G 删除光标所在到第一行的所有数据dG 删除光标所在到最后一行的所有数据d$ 删除游标所在处,到该行的最后一个字符d0 那个是数字的 0 ,删除游标所在处,到该行的最前面一个字符 复制 yy 复制游标所在的那一行(常用)nyy n 为数字。复制光标所在的向下 n 行,例如 20yy 则是复制 20 行(常用)y1G 复制游标所在行到第一行的所有数据yG 复制游标所在行到最后一行的所有数据y0 复制光标所在的那个字符到该行行首的所有数据 粘贴 p, P p 为将已复制的数据在光标下一行贴上,J 将光标所在行与下一行的数据结合成同一行c 重复删除多个数据,例如向下删除 10 行,[ 10cj ]c 重复删除多个数据,例如向下删除 10 行,[ 10cj ]u 复原前一个动作。(常用) 搜索与替换 命令 说明 /word 向光标之下寻找一个名称为 word 的字符串。例如要在档案内搜寻 vbird 这个字符串,就输入 /vbird 即可! (常用) ?word 向光标之上寻找一个字符串名称为 word 的字符串。 n 这个 n 是英文按键。代表重复前一个搜寻的动作。举例来说, 如果刚刚我们执行 /vbird 去向下搜寻 vbird 这个字符串,则按下 n 后,会向下继续搜寻下一个名称为 vbird 的字符串。如果是执行 ?vbird 的话,那么按下 n 则会向上继续搜寻名称为 vbird 的字符串! N 这个 N 是英文按键。与 n 刚好相反,为『反向』进行前一个搜寻动作。 例如 /vbird 后,按下 N 则表示『向上』搜寻 vbird 。 n1,n2s/word1/word2/g n1 与 n2 为数字。在第 n1 与 n2 行之间寻找 word1 这个字符串,并将该字符串取代为 word2 !举例来说,在 100 到 200 行之间搜寻 vbird 并取代为 VBIRD 则:『:100,200s/vbird/VBIRD/g』。 输入模式 命令 说明 字符按键以及Shift组合 输入字符 ENTER 回车键,换行 BACK SPACE 退格键,删除光标前一个字符 DEL 删除键,删除光标后一个字符 方向键 在文本中移动光标 HOME/END 移动光标到行首/行尾 Page Up/Page Down 上/下翻页 Insert 切换光标为输入/替换模式,光标将变成竖线/下划线 ESC 退出输入模式,切换到命令模式 底线命令模式 命令 说明 q 退出程序 w 保存文件 :q! 若曾修改过档案,又不想储存,使用 ! 为强制离开不储存档案。 :w [filename] 将编辑的数据储存成另一个档案(类似另存新档) :r [filename] 在编辑的数据中,读入另一个档案的数据。亦即将 『filename』 这个档案内容加到游标所在行后面 :n1,n2 w [filename] 将 n1 到 n2 的内容储存成 filename 这个档案。 :! command 暂时离开 vi 到指令行模式下执行 command 的显示结果!例如『:! ls /home』即可在 vi 当中察看 /home 底下以 ls 输出的档案信息! 行号 :set nu 显示行号:set nonu 不显示行号]]></content>
<categories>
<category>linux</category>
<category>环境</category>
</categories>
<tags>
<tag>操作系统</tag>
<tag>编辑器</tag>
</tags>
</entry>
<entry>
<title><![CDATA[关系运算-SQL]]></title>
<url>%2F2019%2F07%2F31%2F%2F%E7%BC%96%E7%A8%8B%E8%AF%AD%E8%A8%80%2Fsql%E8%AF%AD%E8%A8%80%2FSQL%2F%E5%85%B3%E7%B3%BB%E8%BF%90%E7%AE%97-SQL.md%2F</url>
<content type="text"><![CDATA[集合运算 并 1Select * from R UNION Select * from S; 差 1Select * from R Except Select * from S; 交 1Select * from R Intersect Select * from S 笛卡尔积 1Select * from R,S 关系运算选择1SELECT R.学号,R.课程名,R.分数 from R WHERE 分数>85 投影1SELECT 课程名 FROM R; 连接从关系R和关系S的广义笛卡儿积中选取满足给定条件的元组组成新的关系称为R和S的连接 自然连接给定一个关系R和关系S,R与S的等值连接运算结果也是一个关系,记作 $R⋈S$ ,它由关系R和关系S的笛卡尔积中,选取R与S的笛卡尔积中选取相同属性组B上值相等的元组组成。 1SELECT * from R natual join S $\theta$连接给定一个关系R 和关系S,R 与S 的 $θ$ 连接运算结果也是一个关系,记作$R\underset{A\theta B}{\bowtie} S$,它由关系R和关系S的笛卡尔积中,选取R中属性A与S中属性E之间满足 $θ$ 条件的元组组成。 1234SELECT * from R joinSELECT * from Son R.B<S.H 等值连接给定一个关系R和关系S,R与S的等值连接运算结果也是一个关系,记作$R\underset{A= B}{\bowtie} S$,它由关系R和关系S的笛卡尔积中,选取R中属性A与S中属性B上值相等的元组组成。 1234SELECT * from R joinSELECT * from Son R.B=S.H 外连接两个关系R与S进行连接时,如果关系R(或S)中的元组在S(或R)中找不到相匹配的元组,则为了避免该元组信息丢失,从而将该元组与S(或R)中假定存在的全为空值的元组形成连接,放置在结果关系中,这种连接称之为外连接(Outer Join) 123select * from R left outer join S;select * from R right outer join S;select * from R full outer join S; 除给定关系R (X, Y) 和S (Y, Z), 其中X, Y, Z为属性组。R中的Y与S中的Y出自相同的域集。R与S的除运算得到一个新的关系P(X)。其中P(X)与S(Y)组成的元组都在R(X,Y)中。 P(X)=R(X,Y) \div S(Y,Z)12345678910SELECT DISTINCT R.X FROM R R1WHERE NOT EXISTS ( SELECT S.Y FROM S WHERE NOT EXISTS ( SELECT * FROM R R2 where R1.X=R2.X and R2.Y=S.Y )) 习题 求至少选择了C001和C003两门课程的学生学号 12345678select distinct sno from sc Awhere not exists( select * from course B where cno in ('C002','C003') and not exists ( select * from sc C where A.sno=C.sno and B.cno=C.cno ))]]></content>
<categories>
<category>编程语言</category>
<category>SQL</category>
</categories>
<tags>
<tag>数据库</tag>
<tag>sql</tag>
</tags>
</entry>
<entry>
<title><![CDATA[linux命令]]></title>
<url>%2F2019%2F07%2F30%2Flinux%2Flinux%E5%91%BD%E4%BB%A4%2F</url>
<content type="text"><![CDATA[文件基本属性文件属性说明对于文件来说,它都有一个特定的所有者,也就是对该文件具有所有权的用户。同时,在Linux系统中,用户是按组分类的,一个用户属于一个或多个组。文件所有者以外的用户又可以分为文件所有者的同组用户和其他用户。因此,Linux系统按文件所有者、文件所有者同组用户和其他用户来规定了不同的文件访问权限。 查看文件属性1ls -l 输出 12dr-xr-xr-x 2 root root 4096 Dec 14 2012 bindr-xr-xr-x 4 root root 4096 Apr 19 2012 boot 第一个字符 属主 属组 其他用户 d 目录- 文件l 链接文档(link file)b 装置文件里面的可供储存的接口设备(可随机存取装置)c 装置文件里面的串行端口设备,例如键盘、鼠标(一次性读取装置)。 r 读w 写x 执行 r 读w 写x 执行 r 读w 写x 执行 更改文件属性 功能 方法 更改文件属组 chgrp [-R] 属组名 文件名-R:递归更改文件属组,就是在更改某个目录文件的属组时,如果加上-R的参数,那么该目录下的所有文件的属组都会更改。 更改文件属主 chown [–R] 属主名 文件名 `chown bin install.log` chown [-R] 属主名:属组名 文件名 `chown root:root install.log` 更改文件属性 Linux文件的基本权限就有九个,分别是owner/group/others三种身份各有自己的read/write/execute权限。 第一种方式 chmod [-R] xyz 文件或目录-R : 进行递归(recursive)的持续变更,亦即连同次目录下的所有文件都会变更xyz : 就是刚刚提到的数字类型的权限属性` chmod 777 test` 第二种方式 u 属主g 同组o 其他a 所有 +(加入)-(除去)=(设定) rwx 文件或目录 `chmod u=rwx,g=rx,o=r test` 文件与目录管理Linux的目录结构为树状结构,最顶级的目录为根目录 /。其他目录通过挂载可以将它们添加到树中,通过解除挂载可以移除它们。 命令 说明 ls 列出目录a: 全部的文件,连同隐藏文件(开头为.的文件)-d :仅列出目录本身,而不是列出目录内的文件数据(常用)-l :长数据串列出,包含文件的属性与权限等等数据;(常用) cd 切换目录# cd ~ # 表示回到自己的家目录,亦即是 /root 这个目录 pwd 显示目前的目录-P :显示出确实的路径,而非使用连结 (link) 路径。 mkdir 创建一个新的目录-m :配置文件的权限喔!直接配置,不需要看默认权限 (umask) 的脸色~-p :帮助你直接将所需要的目录(包含上一级目录)递归创建起来! rmdir 删除一个空的目录-p :连同上一级『空的』目录也一起删除 cp 复制文件或目录-a:相当於 -pdr 的意思,至於 pdr 请参考下列说明;(常用)-d:若来源档为连结档的属性(link file),则复制连结档属性而非文件本身;-f:为强制(force)的意思,若目标文件已经存在且无法开启,则移除后再尝试一次;-i:若目标档(destination)已经存在时,在覆盖时会先询问动作的进行(常用)-l:进行硬式连结(hard link)的连结档创建,而非复制文件本身;-p:连同文件的属性一起复制过去,而非使用默认属性(备份常用);-r:递归持续复制,用於目录的复制行为;(常用)-s:复制成为符号连结档 (symbolic link),亦即『捷径』文件;-u:若 destination 比 source 旧才升级 destination ! rm 移除文件或目录-f :就是 force 的意思,忽略不存在的文件,不会出现警告信息;-i :互动模式,在删除前会询问使用者是否动作-r :递归删除啊!最常用在目录的删除了!这是非常危险的选项!!! mv 移动文件与目录,或修改文件与目录的名称-f :force 强制的意思,如果目标文件已经存在,不会询问而直接覆盖;-i :若目标文件 (destination) 已经存在时,就会询问是否覆盖!-u :若目标文件已经存在,且 source 比较新,才会升级 (update) 用户与用户组用户管理 命令 说明 useradd 添加新的用户账号 useradd -s /bin/sh -g group –G adm,root gemuseradd 选项 用户名-c comment 指定一段注释性描述。-d 目录 指定用户主目录,如果此目录不存在,则同时使用-m选项,可以创建主目录。-g 用户组 指定用户所属的用户组。-G 用户组,用户组 指定用户所属的附加组。-s Shell文件 指定用户的登录Shell。-u 用户号 指定用户的用户号,如果同时有-o选项,则可以重复使用其他用户的标识号。 userdel 删除帐号 userdel -r sam-r:把用户的主目录一起删除 usermod 修改帐号 usermod -s /bin/ksh -d /home/z –g developer samusermod 选项 用户名包括-c, -d, -m, -g, -G, -s, -u以及-o等,这些选项的意义与useradd命令中的选项一样 passwd 用户账号刚创建时没有口令,但是被系统锁定,无法使用,必须为其指定口令后才可以使用,即使是指定空口令。passwd 选项 用户名-l 锁定口令,即禁用账号。-u 口令解锁。-d 使账号无口令。-f 强迫用户下次登录时修改口令。 用户组管理每个用户都有一个用户组,系统可以对一个用户组中的所有用户进行集中管理。用户组的管理涉及用户组的添加、删除和修改。组的增加、删除和修改实际上就是对/etc/group文件的更新 命令 方法 groupadd 增加一个新的用户组-g GID 指定新用户组的组标识号(GID)。-o 一般与-g选项同时使用,表示新用户组的GID可以与系统已有用户组的GID相同。 groupdel 删除一个已有的用户组 groupmod 修改用户组的属性-g GID 为用户组指定新的组标识号。-o 与-g选项同时使用,用户组的新GID可以与系统已有用户组的GID相同。-n新用户组 将用户组的名字改为新名字 newgrp 如果一个用户同时属于多个用户组,那么用户可以在用户组之间切换,以便具有其他用户组的权限。root``` |1234567891011121314151617## 与用户账号有关的系统文件完成用户管理的工作有许多种方法,但是每一种方法实际上都是对有关的系统文件进行修改。这些文件包括/etc/passwd, /etc/shadow, /etc/group等。### 伪用户这些用户在/etc/passwd文件中也占有一条记录,但是不能登录,因为它们的登录Shell为空。它们的存在主要是方便系统管理,满足相应的系统进程对文件属主的要求。常见的伪用户如下所示:```shell伪 用 户 含 义 bin 拥有可执行的用户命令文件 sys 拥有系统文件 adm 拥有帐户文件 uucp UUCP使用 lp lp或lpd子系统使用 nobody NFS使用 用户管理文件-/etc/passwd12用户名:口令:用户标识号:组标识号:注释性描述:主目录:登录Shellroot:x:0:0:Superuser:/: 字段 说明 口令 虽然这个字段存放的只是用户口令的加密串,不是明文,但是由于/etc/passwd文件对所有用户都可读,所以这仍是一个安全隐患。因此,现在许多Linux 系统(如SVR4)都使用了shadow技术,把真正的加密后的用户口令字存放到/etc/shadow文件中,而在/etc/passwd文件的口令字段中只存放一个特殊的字符,例如“x”或者“*”。 用户标识号 系统内部用它来标识用户,一般情况下它与用户名是一一对应的。如果几个用户名对应的用户标识号是一样的,系统内部将把它们视为同一个用户,但是它们可以有不同的口令、不同的主目录以及不同的登录Shell等 组标识号 记录的是用户所属的用户组。它对应着/etc/group文件中的一条记录。 注释性描述 记录着用户的一些个人情况。例如用户的真实姓名、电话、地址等, 主目录 用户的起始工作目录 登陆shell 用户登录后,要启动一个进程,负责将用户的操作传给内核,这个进程是用户登录到系统后运行的命令解释器或某个特定的程序,即Shell 用户管理文件-/etc/shadow由于/etc/passwd文件是所有用户都可读的,如果用户的密码太简单或规律比较明显的话,一台普通的计算机就能够很容易地将它破解,因此对安全性要求较高的Linux系统都把加密后的口令字分离出来,单独存放在一个文件中,这个文件是/etc/shadow文件。有超级用户才拥有该文件读权限,这就保证了用户密码的安全性。 1登录名:加密口令:最后一次修改时间:最小时间间隔:最大时间间隔:警告时间:不活动时间:失效时间:标志 root:Dnakfw28zf38w:8764:0:168:7::: 字段 说明 登录名 与/etc/passwd文件中的登录名相一致的用户账号 口令 存放的是加密后的用户口令字,长度为13个字符。如果为空,则对应用户没有口令,登录时不需要口令;如果含有不属于集合 { ./0-9A-Za-z }中的字符,则对应的用户不能登录。 最后一次修改时间 从某个时刻起,到用户最后一次修改口令时的天数。时间起点对不同的系统可能不一样。例如在SCO Linux 中,这个时间起点是1970年1月1日。 最小时间间隔 是两次修改口令之间所需的最小天数。 最大时间间隔 口令保持有效的最大天数。 警告时间 从系统开始警告用户到用户密码正式失效之间的天数。 不活动时间 用户没有登录活动但账号仍能保持有效的最大天数。 失效时间 给出的是一个绝对的天数,如果使用了这个字段,那么就给出相应账号的生存期。期满后,该账号就不再是一个合法的账号,也就不能再用来登录了。 用户组管理文件-/etc/group每个用户都属于某个用户组;一个组中可以有多个用户,一个用户也可以属于不同的组。当一个用户同时是多个组中的成员时,在/etc/passwd文件中记录的是用户所属的主组,也就是登录时所属的默认组,而其他组称为附加组。用户要访问属于附加组的文件时,必须首先使用newgrp命令使自己成为所要访问的组中的成员。 12组名:口令:组标识号:组内用户列表root::0:root 字段 说明 组名 用户组的名称,由字母或数字构成。与/etc/passwd中的登录名一样,组名不应重复。 口令存 用户组加密后的口令字。一般Linux系统的用户组都没有口令,即这个字段一般为空,或者是*。 组标识号 与用户标识号类似,也是一个整数,被系统内部用来标识组。 组内用户列表 属于这个组的所有用户的列表/b],不同用户之间用逗号(,)分隔。这个用户组可能是用户的主组,也可能是附加组。 添加批量用户 先编辑一个文本用户文件。 每一列按照/etc/passwd密码文件的格式书写,要注意每个用户的用户名、UID、宿主目录都不可以相同,其中密码栏可以留做空白或输入x号。一个范例文件user.txt内容如下: 123456user001::600:100:user:/home/user001:/bin/bashuser002::601:100:user:/home/user002:/bin/bashuser003::602:100:user:/home/user003:/bin/bashuser004::603:100:user:/home/user004:/bin/bashuser005::604:100:user:/home/user005:/bin/bashuser006::605:100:user:/home/user006:/bin/bash 以root身份执行命令 /usr/sbin/newusers,从刚创建的用户文件user.txt中导入数据,创建用户 1newusers < user.txt 执行命令/usr/sbin/pwunconv 将 /etc/shadow 产生的 shadow 密码解码,然后回写到 /etc/passwd 中,并将/etc/shadow的shadow密码栏删掉。这是为了方便下一步的密码转换工作,即先取消 shadow password 功能。 1pwunconv 编辑每个用户的密码对照文件。 123456user001:密码user002:密码user003:密码user004:密码user005:密码user006:密码 以root身份执行命令 /usr/sbin/chpasswd 创建用户密码,chpasswd 会将经过 /usr/bin/passwd 命令编码过的密码写入 /etc/passwd 的密码栏。 1chpasswd < passwd.txt 确定密码经编码写入/etc/passwd的密码栏后。 执行命令 /usr/sbin/pwconv 将密码编码为 shadow password,并将结果写入 /etc/shadow。 1pwconv yum 命令-包管理 yum( Yellow dog Updater, Modified)是一个在Fedora和RedHat以及SUSE中的Shell前端软件包管理器。 基於RPM包管理,能够从指定的服务器自动下载RPM包并且安装,可以自动处理依赖性关系,并且一次安装所有依赖的软体包,无须繁琐地一次次下载、安装。 yum提供了查找、安装、删除某一个、一组甚至全部软件包的命令,而且命令简洁而又好记。 1yum [options] [command] [package ...] 参数 说明 options -h 帮助 -y 当安装过程提示选择全部为"yes" -q 不显示安装的过程 command check-update 列出所有可更新的软件清单命令 update 更新所有软件命令 update<package_name> 仅更新指定的软件命令 install <package_name> 仅安装指定的软件命令 list 列出所有可安裝的软件清单命令 remove <package_name> 删除软件包命令 search <keyword> 查找软件包命令 clean packages 清除缓存目录下的软件包 clean headers 清除缓存目录下的 headers clean oldheaders 清除缓存目录下旧的 headers clean/clean all 清除缓存目录下的软件包及旧的headers package 操作的对象 命令大全 1、文件管理 cat chattr chgrp chmod chown cksum cmp diff diffstat file find git gitview indent cut ln less locate lsattr mattrib mc mdel mdir mktemp more mmove mread mren mtools mtoolstest mv od paste patch rcp rm slocate split tee tmpwatch touch umask which cp whereis mcopy mshowfat rhmask scp awk read updatedb 2、文档编辑 col colrm comm csplit ed egrep ex fgrep fmt fold grep ispell jed joe join look mtype pico rgrep sed sort spell tr expr uniq wc let 3、文件传输 lprm lpr lpq lpd bye ftp uuto uupick uucp uucico tftp ncftp ftpshut ftpwho ftpcount 4、磁盘管理 cd df dirs du edquota eject mcd mdeltree mdu mkdir mlabel mmd mrd mzip pwd quota mount mmount rmdir rmt stat tree umount ls quotacheck quotaoff lndir repquota quotaon 5、磁盘维护 badblocks cfdisk dd e2fsck ext2ed fsck fsck.minix fsconf fdformat hdparm mformat mkbootdisk mkdosfs mke2fs mkfs.ext2 mkfs.msdos mkinitrd mkisofs mkswap mpartition swapon symlinks sync mbadblocks mkfs.minix fsck.ext2 fdisk losetup mkfs sfdisk swapoff 6、网络通讯 apachectl arpwatch dip getty mingetty uux telnet uulog uustat ppp-off netconfig nc httpd ifconfig minicom mesg dnsconf wall netstat ping pppstats samba setserial talk traceroute tty newaliases uuname netconf write statserial efax pppsetup tcpdump ytalk cu smbd testparm smbclient shapecfg 7、系统管理 adduser chfn useradd date exit finger fwhios sleep suspend groupdel groupmod halt kill last lastb login logname logout ps nice procinfo top pstree reboot rlogin rsh sliplogin screen shutdown rwho sudo gitps swatch tload logrotate uname chsh userconf userdel usermod vlock who whoami whois newgrp renice su skill w id free 8、系统设置 reset clear alias dircolors aumix bind chroot clock crontab declare depmod dmesg enable eval export pwunconv grpconv rpm insmod kbdconfig lilo liloconfig lsmod minfo set modprobe ntsysv mouseconfig passwd pwconv rdate resize rmmod grpunconv modinfo time setup sndconfig setenv setconsole timeconfig ulimit unset chkconfig apmd hwclock mkkickstart fbset unalias SVGATextMode 9、备份压缩 ar bunzip2 bzip2 bzip2recover gunzip unarj compress cpio dump uuencode gzexe gzip lha restore tar uudecode unzip zip zipinfo 10、设备管理 setleds loadkeys rdev dumpkeys MAKEDEV 其他解压rar1sudo apt-get install rar unrar]]></content>
<categories>
<category>linux</category>
<category>命令</category>
</categories>
<tags>
<tag>linux命令</tag>
<tag>操作系统</tag>
</tags>
</entry>
<entry>
<title><![CDATA[shell]]></title>
<url>%2F2019%2F07%2F30%2Flinux%2Fshell%2F</url>
<content type="text"><![CDATA[Shell 是一个用 C 语言编写的程序,它是用户使用 Linux 的桥梁。Shell 既是一种命令语言,又是一种程序设计语言。 运行脚本运行 Shell 脚本有两种方法:1、作为可执行程序;2、作为解释器参数 1、作为可执行程序12chmod +x ./test.sh #使脚本具有执行权限./test.sh #执行脚本 2、作为解释器参数12/bin/sh test.sh/bin/php test.php 变量变量名 命名只能使用英文字母,数字和下划线,首个字符不能以数字开头。 中间不能有空格,可以使用下划线(_)。 不能使用标点符号。 不能使用bash里的关键字(可用help命令查看保留关键字)。 使用变量12your_name="qinjx"echo ${your_name} 只读变量使用 readonly 命令可以将变量定义为只读变量,只读变量的值不能被改变。 123#!/bin/bashmyUrl="http://www.google.com"readonly myUrl 删除变量使用 unset 命令可以删除变量,变量被删除后不能再次使用。unset 命令不能删除只读变量。 1unset variable_name 变量类型 1) 局部变量 局部变量在脚本或命令中定义,仅在当前shell实例中有效,其他shell启动的程序不能访问局部变量。 2) 环境变量 所有的程序,包括shell启动的程序,都能访问环境变量,有些程序需要环境变量来保证其正常运行。必要的时候shell脚本也可以定义环境变量。 3) shell变量 shell变量是由shell程序设置的特殊变量。shell变量中有一部分是环境变量,有一部分是局部变量,这些变量保证了shell的正常运行 Shell 字符串 符号 说明 ‘-单引号 单引号里的任何字符都会原样输出,单引号字符串中的变量是无效的; “-双引号 双引号里可以有变量,双引号里可以添加转义字符 拼接字符123456789your_name="runoob"# 使用双引号拼接greeting="hello, "$your_name" !"greeting_1="hello, ${your_name} !"echo $greeting $greeting_1# 使用单引号拼接greeting_2='hello, '$your_name' !'greeting_3='hello, ${your_name} !'echo $greeting_2 $greeting_3 输出 12hello, runoob ! hello, runoob !hello, runoob ! hello, ${your_name} ! 获取字符串长度12string="abcd"echo ${#string} #输出 4 提取子字符串12string="runoob is a great site"echo ${string:1:4} # 输出 unoo 查找子字符串12string="runoob is a great site"echo `expr index "$string" io` # 输出 4 数组bash支持一维数组(不支持多维数组),并且没有限定数组的大小。数组元素的下标由 0 开始编号。获取数组中的元素要利用下标,下标可以是整数或算术表达式,其值应大于或等于 0。 123456789array_name=(value0 value1 value2 value3) #定义数组#### 读取数据valuen=${array_name[n]} #读取单个元素echo ${array_name[@]} #读取所有元素#### 数组长度length=${#array_name[@]} #取得数组所有元素的个数lengthn=${#array_name[n]} #取得数组单个元素的长度 传递参数我们可以在执行 Shell 脚本时,向脚本传递参数,脚本内获取参数的格式为:$n。n 代表一个数字,1 为执行脚本的第一个参数,2 为执行脚本的第二个参数,以此类推 参数处理 说明 $n n代表一个数字,1 为执行脚本的第一个参数,2 为执行脚本的第二个参数,以此类推 $# 传递到脚本的参数个数 $* 以一个单字符串显示所有向脚本传递的参数。 如”$*“用「”」括起来的情况、以”$1 $2 … $n”的形式输出所有参数。 $@ 与$*相同,但是使用时加引号,并在引号中返回每个参数。 如”$@”用「”」括起来的情况、以”$1” “$2” … “$n” 的形式输出所有参数。 $$ 脚本运行的当前进程ID号 $! 后台运行的最后一个进程的ID号 $- 显示Shell使用的当前选项,与set命令功能相同。 $? 显示最后命令的退出状态。0表示没有错误,其他任何值表明有错误。 注意:$* 与 $@ 区别: 相同点:都是引用所有参数。 不同点:只有在双引号中体现出来。假设在脚本运行时写了三个参数 1、2、3,,则 “ * “ 等价于 “1 2 3”(传递了一个参数),而 “@” 等价于 “1” “2” “3”(传递了三个参数)。 12345678910111213#!/bin/bash# author:菜鸟教程# url:www.runoob.comecho "-- \$* 演示 ---"for i in "$*"; do echo $idoneecho "-- \$@ 演示 ---"for i in "$@"; do echo $idone 输出 12345678$ chmod +x test.sh $ ./test.sh 1 2 3-- $* 演示 ---1 2 3-- $@ 演示 ---123 默认参数12#当没有参数的时候,输入ccidMinMaxData;当有参数的时候,输入指定的参数echo table_name ${1-ccidMinMaxData} 运算符 运算符 说明 举例 % 取余 expr $b % $a 结果为 0。 = 赋值 a=$b 将把变量 b 的值赋给 a。 == 相等。用于比较两个数字,相同则返回 true。 [$a == $b] 返回 false。 != 不相等。用于比较两个数字,不相同则返回 true。 [$a != $b ]返回 true。 -eq 检测两个数是否相等,相等返回 true。 [$a -eq $b] 返回 false。 -ne 检测两个数是否不相等,不相等返回 true。 [$a -ne $b] 返回 true。 -gt 检测左边的数是否大于右边的,如果是,则返回 true。 [$a -gt $b] 返回 false。 -lt 检测左边的数是否小于右边的,如果是,则返回 true。 [$a -lt $b] 返回 true。 -ge 检测左边的数是否大于等于右边的,如果是,则返回 true。 [$a -ge $b] 返回 false。 -le 检测左边的数是否小于等于右边的,如果是,则返回 true。 [$a -le $b] 返回 true。 ! 非运算,表达式为 true 则返回 false,否则返回 true。 [!false] 返回 true。 -o 或运算,有一个表达式为 true 则返回 true。 [$a -lt 20 -o $b -gt 100] 返回 true。 -a 与运算,两个表达式都为 true 才返回 true。 [$a -lt 20 -a $b -gt 100]返回 false。 && 逻辑的 AND [[$a -lt 100 && $b -gt 100]] 返回 false || 逻辑的 OR [[\$a -lt 100 || $b -gt 100 ]]返回 true 123456789a=10b=20if [[ $a -lt 100 && $b -gt 100 ]]then echo "返回 true"else echo "返回 false"fi 字符串运算符 运算符 说明 举例 = 检测两个字符串是否相等,相等返回 true。 [ $a = $b ] 返回 false。 != 检测两个字符串是否相等,不相等返回 true。 [ 、$a != $b ] 返回 true。 -z 检测字符串长度是否为0,为0返回 true。 [ -z $a ] 返回 false。 -n 检测字符串长度是否为0,不为0返回 true。 [ -n “$a” ] 返回 true。 $ 检测字符串是否为空,不为空返回 true。 [ $a ] 返回 true。 文件测试运算符 操作符 说明 举例 -b file 检测文件是否是块设备文件,如果是,则返回 true。 [ -b $file ] 返回 false。 -c file 检测文件是否是字符设备文件,如果是,则返回 true。 [ -c $file ] 返回 false。 -d file 检测文件是否是目录,如果是,则返回 true。 [ -d $file ] 返回 false。 -f file 检测文件是否是普通文件(既不是目录,也不是设备文件),如果是,则返回 true。 [ -f $file ] 返回 true。 -g file 检测文件是否设置了 SGID 位,如果是,则返回 true。 [ -g $file ] 返回 false。 -k file 检测文件是否设置了粘着位(Sticky Bit),如果是,则返回 true。 [ -k $file ] 返回 false。 -p file 检测文件是否是有名管道,如果是,则返回 true。 [ -p $file ] 返回 false。 -u file 检测文件是否设置了 SUID 位,如果是,则返回 true。 [ -u $file ] 返回 false。 -r file 检测文件是否可读,如果是,则返回 true。 [ -r $file ] 返回 true。 -w file 检测文件是否可写,如果是,则返回 true。 [ -w $file ] 返回 true。 -x file 检测文件是否可执行,如果是,则返回 true。 [ -x $file ] 返回 true。 -s file 检测文件是否为空(文件大小是否大于0),不为空返回 true。 [ -s $file ] 返回 true。 -e file 检测文件(包括目录)是否存在,如果是,则返回 true。 [ -e $file ] 返回 true。 echo命令 说明 输入 输出 显示普通字符串 echo “It is a test” echo It is a test 显示转义字符 echo “\”It is a test\”” “It is a test” 显示变量 echo “$name It is a test” ok It is a test 显示换行 echo -e “OK! \n” # -e 开启转义echo “It is a test” OK!OK! It is a test 显示不换行 echo -e “OK! \c” # -e 开启转义 \c 不换行echo “It is a test” OK! It is a test 显示结果定向至文件 echo “It is a test” > myfile 原样输出字符串-单引号 echo ‘$name\\”‘ $name\” 显示命令执行结果 echo `date` Thu Jul 24 10:08:46 CST 2014 printf1234printf "%-10s %-8s %-4s\n" 姓名 性别 体重kg printf "%-10s %-8s %-4.2f\n" 郭靖 男 66.1234 printf "%-10s %-8s %-4.2f\n" 杨过 男 48.6543 printf "%-10s %-8s %-4.2f\n" 郭芙 女 47.9876 %-10s 指一个宽度为10个字符(-表示左对齐,没有则表示右对齐),任何字符都会被显示在10个字符宽的字符内,如果不足则自动以空格填充,超过也会将内容全部显示出来。 %-4.2f 指格式化为小数,其中.2指保留2位小数。 转义字符 序列 说明 \a 警告字符,通常为ASCII的BEL字符 \b 后退 \c 抑制(不显示)输出结果中任何结尾的换行字符(只在%b格式指示符控制下的参数字符串中有效),而且,任何留在参数里的字符、任何接下来的参数以及任何留在格式字符串中的字符,都被忽略 \f 换页(formfeed) \n 换行 \r 回车(Carriage return) \t 水平制表符 \v 垂直制表符 \\ 一个字面上的反斜杠字符 \ddd 表示1到3位数八进制值的字符。仅在格式字符串中有效 \0ddd 表示1到3位的八进制值字符 流程控制if else1234567if conditionthen command1 command2 ... commandN fi if else-if else123456789if condition1then command1elif condition2 then command2else commandNfi for循环1234567for var in item1 item2 ... itemNdo command1 command2 ... commandNdone while 语句1234while conditiondo commanddone until 循环until 循环执行一系列命令直至条件为 true 时停止。until 循环与 while 循环在处理方式上刚好相反。 1234until conditiondo commanddone case语句1234567891011121314case 值 in模式1) command1 command2 ... commandN ;;模式2) command1 command2 ... commandN ;;esac break跳出所有的循环 continue跳出当前循环 函数1234567891011121314#!/bin/bash# author:菜鸟教程# url:www.runoob.comfunWithParam(){ echo "第一个参数为 $1 !" echo "第二个参数为 $2 !" echo "第十个参数为 $10 !" echo "第十个参数为 ${10} !" echo "第十一个参数为 ${11} !" echo "参数总数有 $# 个!" echo "作为一个字符串输出所有参数 $* !"}funWithParam 1 2 3 4 5 6 7 8 9 34 73 输出结果: 1234567第一个参数为 1 !第二个参数为 2 !第十个参数为 10 !第十个参数为 34 !第十一个参数为 73 !参数总数有 11 个!作为一个字符串输出所有参数 1 2 3 4 5 6 7 8 9 34 73 ! 参数处理 说明 $# 传递到脚本的参数个数 $* 以一个单字符串显示所有向脚本传递的参数 $$ 脚本运行的当前进程ID号 $! 后台运行的最后一个进程的ID号 $@ 与$*相同,但是使用时加引号,并在引号中返回每个参数。 $- 显示Shell使用的当前选项,与set命令功能相同。 $? 显示最后命令的退出状态。0表示没有错误,其他任何值表明有错误。 Shell 输入/输出重定向 命令 说明 command > file 将输出重定向到 file。任何file内的已经存在的内容将被新内容替代 command < file 将输入重定向到 file。本来需要从键盘获取输入的命令会转移到文件读取内容。 command >> file 将输出以追加的方式重定向到 file。 n > file 将文件描述符为 n 的文件重定向到 file。 n >> file 将文件描述符为 n 的文件以追加的方式重定向到 file。将新内容添加在文件末尾 n >& m 将输出文件 m 和 n 合并。 n <& m 将输入文件 m 和 n 合并。 << tag 将开始标记 tag 和结束标记 tag 之间的内容作为输入。 1234567# 将 菜鸟教程:www.runoob.com保存到users文件echo "菜鸟教程:www.runoob.com" >> users# 将 当前目录保存到users文件pwd >> users# 将 users文件中的内容保存进$line变量read $line <users 重定向深入讲解 标准输入文件(stdin):stdin的文件描述符为0,Unix程序默认从stdin读取数据。 标准输出文件(stdout):stdout 的文件描述符为1,Unix程序默认向stdout输出数据。 标准错误文件(stderr):stderr的文件描述符为2,Unix程序会向stderr流中写入错误信息。 1command >> file 2>&1 #将 stdout 和 stderr 合并后重定向到 file Shell 文件包含和其他语言一样,Shell 也可以包含外部脚本。这样可以很方便的封装一些公用的代码作为一个独立的文件。 12345. filename # 注意点号(.)和文件名中间有一空格或source filename 日期 命令 说明 date +%Y%m%d$(date +%Y%m%d) 获取今天日期 date -d -2day +%Y%m%d 获取前天日期(2天前) date -d 2day +%Y%m%d 获取后天日期(2天后) 命令 说明 命令 说明 %H 小时(00..23) %I 小时(01..12) %k 小时(0..23) %l 小时(1..12) %M 分(00..59) %p 显示出AM或PM %r 时间(hh:mm:ss AM或PM),12小时 %s 从1970年1月1日00:00:00到目前经历的秒数 %S 秒(00..59) %T 时间(24小时制)(hh:mm:ss) %X 显示时间的格式(%H:%M:%S) %Z 时区 日期域 %a 星期几的简称( Sun..Sat) %A 星期几的全称( Sunday..Saturday) %b 月的简称(Jan..Dec) %B 月的全称(January..December) %c 日期和时间( Mon Nov 8 14:12:46 CST 1999) %d 一个月的第几天(01..31) %D 日期(mm/dd/yy) %h 和%b选项相同 %j 一年的第几天(001..366) %m 月(01..12) %w 一个星期的第几天(0代表星期天) %W 一年的第几个星期(00..53,星期一为第一天) %x 显示日期的格式(mm/dd/yy) %y 年的最后两个数字( 1999则是99) %Y 年(例如:1970,1996等) 命令大全]]></content>
<categories>
<category>linux</category>
<category>命令</category>
</categories>
<tags>
<tag>linux命令</tag>
<tag>操作系统</tag>
</tags>
</entry>
<entry>
<title><![CDATA[矩阵求导]]></title>
<url>%2F2018%2F09%2F23%2F%E6%95%B0%E5%AD%A6%E5%9F%BA%E7%A1%80%2F%E7%BA%BF%E6%80%A7%E4%BB%A3%E6%95%B0%2F%E7%9F%A9%E9%98%B5%E6%B1%82%E5%AF%BC%2F</url>
<content type="text"></content>
<categories>
<category>数学基础</category>
<category>线性代数</category>
</categories>
<tags>
<tag>线性代数</tag>
<tag>矩阵</tag>
</tags>
</entry>
<entry>
<title><![CDATA[C++STL]]></title>
<url>%2F2018%2F08%2F30%2F%E7%BC%96%E7%A8%8B%E8%AF%AD%E8%A8%80%2Fc%E8%AF%AD%E8%A8%80%2FC-STL%2F</url>
<content type="text"><![CDATA[组件 描述 容器(Containers) 容器是用来管理某一类对象的集合。C++ 提供了各种不同类型的容器,比如 deque、list、vector、map 等。 算法(Algorithms) 算法作用于容器。它们提供了执行各种操作的方式,包括对容器内容执行初始化、排序、搜索和转换等操作。 迭代器(iterators) 迭代器用于遍历对象集合的元素。这些集合可能是容器,也可能是容器的子集。 容器 容器 特征 内存结构 可随机存取 元素搜寻速度 头文件 vector 在序列尾部进行插入和删除,访问和修改元素的时间复杂度为O(1),但插入和删除的时间复杂度与到末尾的距离成正比。 单端数组 可以 慢 <vector> list 对任意元素的访问与两端的距离成正比,但对某个位置的插入和删除花费为常数时间,即O(1) 双向链表 否 非常慢 <list> deque 与vector基本相同,唯一不同的是,在序列头部插入和删除的时间复杂度也是O(1) 双端数组 可以 慢 <deque> set 由节点组成的红黑树,具有快速查找的功能 二叉树 否 快 <set> multiset 可以支持重复元素,同样具有快速查找能力 二叉树 否 快 <set> map 由{键,值}对组成的集合,同样具有快速查找能力 二叉树 对key而言可以 对key而言快 <map> multimap 一个键可以对应于多个值,同样具有快速查找能力 二叉树 否 对key而言快 <map> 算法算法是用来操作容器中数据的模板函数,它抽象了对数据结构的操作行为。要使用STL中定义的算法,应首先引入<algorithm>头文件。例如STL中的sort()函数可以对容器中的数据进行排序,可以使用find()函数来搜索容器中的某个元素。这里的算法可以与C#中泛型方法进行对比来理解。 迭代器STL实现要点是将容器和算法分开,使两者彼此独立。迭代器使两个联系起来,迭代器提供访问容器中的方法。迭代器实质上是一种智能指针,它重载了->和*操作符。事实上,C++指针也是一种迭代器。在C#中同样有迭代器的概念,具体参考MSDN.aspx),不同的是,在C++ 中迭代器分为五类,这五类分别为: 输入迭代器(Input Iterator)——提供对数据的只读访问; 输出迭代器(Output Iterator)——提供对数据的只写访问; 前推迭代器(Forward Iterator)——提供对数据的读写操作,并能向前推进的迭代器; 双向迭代器(Bidirectional Iterator)——提供对数据的读写操作,并能向前和向后操作; 随机访问迭代器(Random Access Iterator)——提供对数据的读写操作,并能在数据中随机移动。 函数对象函数对象,又称为仿函数,STL中的函数对象就是重载了运算符()的模板类的对象,因为该类对象的调用方式类似与函数的调用方式,所以称为函数对象. 适配器适配器是用来修改其他组件接口,与设计模式中的适配器模的达到的效果是一样的。STL中定义了3种形式的适配器:容器适配器、迭代器适配和函数适配器 容器适配器——包括栈(stack)、队列(queue)和优先队列(priority_queue),容器适配器是对基本容器类型进行进一步的封装,从而转换为新的接口类型。 迭代器适配器——对STL中基本迭代器的功能进行扩展,该类适配器包括反向迭代器、插入迭代器和流迭代器。 函数适配器——通过转换或修改来扩展其他函数对象的功能。该类适配器有否定器、绑定器和函数指针适配器。函数对象适配器的作用就是使函数转化为函数对象,或将多参数的函数对象转换为少参数的函数对象,如STL中bind2nd()就是绑定器。 空间配置器当容器中保存的是用户自定义类型数据时,有的数据类型结构简单,占用的空间很小,而有的数据类型结构复杂,占用的内存空间较大;并且有的应用程序需要频繁地进行数据的插入删除操作,这样就需要对内存空间进行频繁地申请和释放工作,然而对内存的频繁操作,会产生严重的性能问题,为了解决这个问题,STL中提供了两个空间配置器,一个是简单空间配置器,仅仅对C运行库中malloc和free进行了简单的封装操作,另一个是“基于内存池的控件配置器”,即容器在每次申请内存的时候,内存池会基于一定的策略,向操作系统申请交大的内存空间,从而避免每次都向OS申请内存。STL中的空间配置器就是负责内存的分配和释放的工作]]></content>
<categories>
<category>编程语言</category>
<category>C++</category>
</categories>
<tags>
<tag>c语言</tag>
<tag>STL</tag>
</tags>
</entry>
<entry>
<title><![CDATA[C++多态]]></title>
<url>%2F2018%2F08%2F30%2F%E7%BC%96%E7%A8%8B%E8%AF%AD%E8%A8%80%2Fc%E8%AF%AD%E8%A8%80%2FC-%E5%A4%9A%E6%80%81%2F</url>
<content type="text"><![CDATA[虚函数虚函数 是在基类中使用关键字 virtual 声明的函数。在派生类中重新定义基类中定义的虚函数时,会告诉编译器不要静态链接到该函数。 我们想要的是在程序中任意点可以根据所调用的对象类型来选择调用的函数,这种操作被称为动态链接,或后期绑定。 多态父类调用子类的函数需要用到虚函数 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455#include <iostream>using namespace std;class Shape {protected: int width, height;public: Shape(int a = 0, int b = 0) { width = a; height = b; } virtual int area() { cout << "Parent class area :" << endl; return 0; }};class Rectangle : public Shape{public: Rectangle(int a = 0, int b = 0) :Shape(a, b) { } int area() { cout << "Rectangle class area :" << endl; return (width * height); }};class Triangle : public Shape{public: Triangle(int a = 0, int b = 0) :Shape(a, b) { } int area() { cout << "Triangle class area :" << endl; return (width * height / 2); }};// 程序的主函数int main(){ Shape *shape; Rectangle rec(10, 7); Triangle tri(10, 5); // 存储矩形的地址 shape = &rec; // 调用矩形的求面积函数 area shape->area(); // 存储三角形的地址 shape = &tri; // 调用三角形的求面积函数 area shape->area(); return 0;}]]></content>
<categories>
<category>编程语言</category>
<category>C++</category>
</categories>
<tags>
<tag>c语言</tag>
<tag>多态</tag>
<tag>虚函数</tag>
</tags>
</entry>
<entry>
<title><![CDATA[双循环链表]]></title>
<url>%2F2018%2F08%2F28%2F%E6%95%B0%E6%8D%AE%E7%BB%93%E6%9E%84%2F%E5%8F%8C%E5%BE%AA%E7%8E%AF%E9%93%BE%E8%A1%A8%2F</url>
<content type="text"></content>
<categories>
<category>数据结构</category>
</categories>
<tags>
<tag>链表</tag>
<tag>数据结构</tag>
<tag>c语言</tag>
</tags>
</entry>
<entry>
<title><![CDATA[静态链表]]></title>
<url>%2F2018%2F08%2F28%2F%E6%95%B0%E6%8D%AE%E7%BB%93%E6%9E%84%2F%E9%9D%99%E6%80%81%E9%93%BE%E8%A1%A8%2F</url>
<content type="text"></content>
<categories>
<category>数据结构</category>
</categories>
<tags>
<tag>链表</tag>
<tag>数据结构</tag>
<tag>c语言</tag>
</tags>
</entry>
<entry>
<title><![CDATA[LDA线性判别分析]]></title>
<url>%2F2018%2F08%2F13%2F%E4%BA%BA%E5%B7%A5%E6%99%BA%E8%83%BD%2F%E7%89%B9%E5%BE%81%E5%B7%A5%E7%A8%8B%2FLDA%E7%BA%BF%E6%80%A7%E5%88%A4%E5%88%AB%E5%88%86%E6%9E%90%2F</url>
<content type="text"><![CDATA[欲使同类样本的投影点尽可能接近,可以让同类样本的投影点的协方差尽可能小;而欲使异类样本的投影点尽可能远离,可以让异类样本的类中心之间的距离尽可能大 LDA思想LDA是一种监督学习的降维技术,也就是说它的数据集的每个样本是有类别输出的。这点和PCA不同。PCA是不考虑样本类别输出的无监督降维技术。LDA的思想可以用一句话概括,就是“投影后类内方差最小,类间方差最大”。什么意思呢? 我们要将数据在低维度上进行投影,投影后希望每一种类别数据的投影点尽可能的接近,而不同类别的数据的类别中心之间的距离尽可能的大。可能还是有点抽象,我们先看看最简单的情况。假设我们有两类数据 分别为红色和蓝色,如下图所示,这些数据特征是二维的,我们希望将这些数据投影到一维的一条直线,让每一种类别数据的投影点尽可能的接近,而红色和蓝色数据中心之间的距离尽可能的大。 同类的数据点尽可能的接近(within class) 不同类的数据点尽可能的分开(between class) 上图中国提供了两种投影方式,哪一种能更好的满足我们的标准呢?从直观上可以看出,右图要比左图的投影效果好,因为右图的黑色数据和蓝色数据各个较为集中,且类别之间的距离明显。左图则在边界处数据混杂。以上就是LDA的主要思想了,当然在实际应用中,我们的数据是多个类别的,我们的原始数据一般也是超过二维的,投影后的也一般不是直线,而是一个低维的超平面。 算法流程输入:数据集$D=\{(x_1,y_1), (x_2,y_2), …,((x_m,y_m))\}$,其中任意样本$x_i$为$n$维向量,$y_i \in \{C_1,C_2,…,C_k\}$,降维到的维度$d$。输出:降维后的样本集$D′$ 计算类内散度矩阵$S_w$$X_j(j=1,2…k)$为第$j$类样本的集合;$\mu_j(j=1,2…k)$为第$j$类样本的均值向量 S_w = \sum\limits_{j=1}^{k}S_{wj} = \sum\limits_{j=1}^{k}\sum\limits_{x \in X_j}(x-\mu_j)(x-\mu_j)^T 计算类间散度矩阵$S_b$$N_j(j=1,2…k)$为第$j$类样本的个数;$μ$为所有样本均值向量。 S_b = \sum\limits_{j=1}^{k}N_j(\mu_j-\mu)(\mu_j-\mu)^T 计算矩阵$S^{−1}wS_b$ 计算$S^{−1}wS_b$的最大的$d$个特征值和对应的$d$个特征向量($w_1,w_2,…w_d)$,得到投影矩阵 对样本集中的每一个样本特征$x_i$,转化为新的样本$z_i=W^Tx_i$ 得到输出样本集$D’=\{(z_1,y_1), (z_2,y_2), …,((z_m,y_m))\}$ 推导欲使同类样本的投影点尽可能接近,可以让同类样本的投影点的协方差尽可能小;而欲使异类样本的投影点尽可能远离,可以让异类样本的类中心之间的距离尽可能大 二分类假设我们的数据集$D=\{(x_1,y_1), (x_2,y_2), …,((x_m,y_m))\}$,其中任意样本$x_i$为$n$维向量,$y_i \in \{0,1\}$。我们定义$N_j(j=0,1)$为第$j$类样本的个数,$X_j(j=0,1)$为第$j$类样本的集合,而$\mu_j(j=0,1)$为第j类样本的均值向量,定义$\Sigma_j(j=0,1)$为第$j$类样本的协方差矩阵(严格说是缺少分母部分的协方差矩阵)。 $\mu_j $的表达式为: \mu _j = \frac{1}{N_j}\sum\limits_{x \in X_j}x\;\;(j=0,1)$Σ_j$的表达式为: \Sigma_j = \sum\limits_{x \in X_j}(x-\mu_j)(x-\mu_j)^T\;\;(j=0,1)由于是两类数据,因此我们只需要将数据投影到一条直线上即可。假设我们的投影直线是向量$w$,则对任意一个样本本$x_i$,它在直线$w$的投影为$w^Tx_i$,对于我们的两个类别的中心点$μ_0,μ_1$,在直线$w$的投影为$w^Tμ_0$和$w^Tμ_1$。由于LDA需要让不同类别的数据的类别中心之间的距离尽可能的大,也就是我们要最大化$||w^Tμ_0−w^Tμ_1||_2^2$,同时我们希望同一种类别数据的投影点尽可能的接近,也就是要同类样本投影点的协方差$w^TΣ_0w$和$w^TΣ_1w$尽可能的小,即最小化$w^TΣ_0w+w^TΣ_1w$。投影之后的协方差$WX(WX)^T$则$WXX^TW^T=WΣW^T$综上所述,我们的优化目标为: \underbrace{arg\;max}_w\;\;J(w) = \frac{||w^T\mu_0-w^T\mu_1||_2^2}{w^T\Sigma_0w+w^T\Sigma_1w} = \frac{w^T(\mu_0-\mu_1)(\mu_0-\mu_1)^Tw}{w^T(\Sigma_0+\Sigma_1)w}我们一般定义类内散度矩阵 $S_w$ 为: $S_w = \Sigma_0 + \Sigma_1 = \sum \limits_{x \in X_0}(x-\mu_0)(x-\mu_0)^T + \sum \limits_{x \in X_1}(x-\mu_1)(x-\mu_1)^T$ 同时定义类间散度矩阵$S_b$为:$S_b = (\mu_0-\mu_1)(\mu_0-\mu_1)^T$ 这样我们的优化目标重写为: \underbrace{arg\;max}_w\;\;J(w) = \frac{w^TS_bw}{w^TS_ww}因为分子分母都是关于$w$的二次型,若$w$是一个解,则对任意常数$α$,$αw$也是一个解。因此解与w的长度无关,只与其方向有关。不妨令$w^TS_ww=1$,则最优化目标等价于 \begin{eqnarray} &\min_w& -w^TS_bw\\ &s.t.& w^TS_ww=1 \end{eqnarray}引入拉格朗日乘子,上式等价于 \begin{eqnarray} &\min& c(w)=-w^TS_bw+\lambda(w^TS_ww-1) \end{eqnarray}求导得 \frac{dc}{dw}=-2S_bw+2\lambda S_ww令其等于0,得 S_bw=\lambda S_ww如果$S_w$可逆,等式两边同乘$S_w^{-1}$有 S_w^{-1}S_bw=\lambda w可喜的发现$w$就是矩阵 $S_w^{-1}S_b$ 的特征向量,因此求解问题转化成求矩阵特征值问题上了,首先求出 $S_w^{-1}S_b$ 的特征值,然后取前 $K$ 个特征向量按列组成 w 矩阵即可 多分类假设我们的数据集$D=\{(x_1,y_1), (x_2,y_2), …,((x_m,y_m))\}$其中任意样本$x_i$为$n$维向量,$y_i \in \{C_1,C_2,…,C_k\}$。我们定义$N_j(j=1,2…k)$为第$j$类样本的个数,$X_j(j=1,2…k)$为第$j$类样本的集合,而$\mu_j(j=1,2…k)$为第$j$类样本的均值向量,定义$\Sigma_j(j=1,2…k)$为第$j$类样本的协方差矩阵。在二类LDA里面定义的公式可以很容易的类推到多类LDA。 由于我们是多类向低维投影,则此时投影到的低维空间就不是一条直线,而是一个超平面了。假设我们投影到的低维空间的维度为$d$,对应的基向量为$(w_1,w_2,…w_d)$,基向量组成的矩阵为$W$, 它是一个$n×d$的矩阵。 此时我们的优化目标应该可以变成为:\frac{W^TS_bW}{W^TS_wW} 其中类间散度$S_b = \sum\limits_{j=1}^{k}N_j(\mu_j-\mu)(\mu_j-\mu)^T$,$μ$为所有样本均值向量。 类内散度$S_w = \sum\limits_{j=1}^{k}S_{wj} = \sum\limits_{j=1}^{k}\sum\limits_{x \in X_j}(x-\mu_j)(x-\mu_j)^T$ 由于现在分子分母都是矩阵,要将矩阵变成实数,可以取矩阵的行列式或者矩阵的迹。其中,矩阵的行列式等于矩阵特征值之积,矩阵的迹等于矩阵特征值之和。所以优化目标可以转化为: 常见的一个LDA多类优化目标函数定义为: \max_W \frac{tr(W^TS_bW)}{tr(W^TS_wW)}\\ or\\ \max_W \frac{|W^TS_bW|}{|W^TS_wW|}可以通过如下广义特征值求解: S_bW=\lambda S_wW $W$的解则是$S_w^{-1}S_b$的$N-1$个最大广义特征值所对应的特征向量按列组成的矩阵。]]></content>
<categories>
<category>人工智能</category>
<category>特征工程</category>
</categories>
<tags>
<tag>特征工程</tag>
<tag>降维</tag>
</tags>
</entry>
<entry>
<title><![CDATA[xgboost]]></title>
<url>%2F2018%2F08%2F13%2F%E4%BA%BA%E5%B7%A5%E6%99%BA%E8%83%BD%2F%E6%9C%BA%E5%99%A8%E5%AD%A6%E4%B9%A0%2Fxgboost%2F</url>
<content type="text"><![CDATA[简介下图就是CART树和一堆CART树的示例,用来判断一个人是否会喜欢计算机游戏 算法 模型我们希望训练出 $K$ 颗树,将它们集成起来从而预测我们的$y$。我们可以用以下公式表示: \hat{y}=\sum_{k=1}^Kf_k(x_i)\\ f(x)=w_{q(x)}在这里,我们用一个函数 $f_k(x)$ 来表示一颗决策树,那个函数 $f$ 可以理解为将样本$x$映射到树的某个叶子结点中,树中的每个叶子结点都会对应着一个权重$w$。如图,这就是提升树的一个例子,这里一共有两颗树,意味着我们有两个函数 $f_1,f_2,K=2$ ,然后将样本分别放到我们的两颗树中,就可以计算出两个值,把它加起来就是我们要预测的$y$ 目标函数$K$ 表示有 $K$ 棵树,$f_k$ 相当于第 $k$ 棵。因此我们的目标函数可以写成 Obj=\sum_il(\hat{y_i},y)+\sum_k\Omega(f_k)\\ where\ \Omega(f)=\gamma T+\frac{1}{2}\lambda||w||^2其中 $l$ 是可导且凸的损失函数,用来衡量 $\hat{y}$ 与 $y$ 的相近程度,第二项 $Ω$ 则是正则项,它包含了两个部分,第一个是 $γT$,这里的 $T$ 表示叶子结点的数量,$γ$ 是超参,也就是说如果 $γ$ 越大,那么我们的叶子结点数量就会越小。另外一部分则是L2正则项,通过对叶子结点的权重进行惩罚,使得不会存在权重过大的叶子结点防止过拟合。$w$ 就表示叶子结点的权重。 目标函数优化=》梯度提升假设第 $t$ 轮的预测值为$y^{(t)}$ ,第 $t$ 颗回归树为 $f_t(x)$。则模型迭代如下: \begin{align}\hat y_i^{(0)} &= 0 \\ y_i^{(0)} & = f_1(x_i)= \hat y_i^{(0)}+f_1(x_i) \\ y_i^{(2)}&=f_1(x_i)+f_2(x_i)= \hat y_i^{(1)}+f_2(x_i) \\ &\cdots \\ y_i^{(t)}&=\sum_{k=1}^tf_k(x_i)= \hat y_i^{(t-1)}+f_t(x_i) \end{align}但是对于上面这么一个目标函数,我们是很难进行优化的,于是我们将它变换一下,我们通过每一步增加一个基分类器 $f_t(x)$ ,贪婪地去优化这个目标函数,使得每次增加 $f_t(x)$,都使得loss变小。如此一来,我们就得到了一个可以用于评价当前分类器 $f_t(x)$ 性能的一个评价函数: \begin{align*} Obj^{(t)}&=\sum_{i=1}^nl(y_i,\hat{y_i}^{(t)})+ \sum_{i=1}^t\Omega(f_t) \\ &=\sum_{i=1}^nl(y_i,\hat{y_i}^{(t-1)}+f_t(x_i))+\Omega(f_t) + constant \end{align*}选取一个 $f_t(x)$来使得我们的目标函数尽量最大地降低。$constant$就是前 $t-1$ 棵树的复杂度 泰勒展开 因为 $l(y_i,\hat y_i^{(t-1)})$ 是常数字所以最优化可以化简为下式子 \begin{align*} Obj^{(t)} &=\sum_{i=1}^n [ g_if_t(x_i)+\frac 1 2 h_if_t^2(x_i)] + \Omega (f_t) \\ &=\sum_{i=1}^n [ g_iw_q(x_i)+\frac 1 2 h_iw_q^2(x_i)] + \gamma T + \lambda \frac{1}{2}\sum _{j=1}^Tw_j^2\\ &= \sum_{j=1}^T [( \sum_{i \in I_j} g_i)w_j+\frac 1 2(\sum_{i \in I_j} h_i + \lambda)w_j^2] + \gamma T \\ &= \sum_{j=1}^T [G_j w_j + \frac 1 2 (H_j + \lambda) w_j^2] + \gamma T \end{align*}$j$为叶子结点的序号,$T$ 为叶子结点的总数 ;$i$ 为样本的序号,$n$ 为样本的总数;$w_q(x_i)$是求取$x_i$权值的对应函数;$\sum_{i \in I_j} g_i$ 为同一叶子结点 $g_i$ 的和;$\sum_{i \in I_j} g_i w_j$ 为同一结点的 $g_i w_j$ ;$\sum_{j=1}^T \sum_{i \in I_j} = \sum_{i=1}^n$。其中 g_i=\frac{\partial l(y_i,\hat{y_i}^{(t-1)})}{\partial \hat{y_i}^{(t-1)}} \quad h_i=\frac{\partial ^2l(y_i,\hat{y_i}^{(t-1)})}{\partial ^2\hat{y_i}^{(t-1)}} \;\;\;\;\;梯度 \\ G_j = \sum_{i \in I_j} g_i \quad H_j = \sum_{i \in I_j} h_i求取基模型$f_t(x)$-叶子结点权值$w$$f_t(x_i)$ 是什么?它其实就是 $f_t$ 的某个叶子结点的值 $w$ 。之前我们提到过,叶子结点的值是可以作为模型的参数 Obj^{(t)} = \sum_{j=1}^T [G_j w_j + \frac 1 2 (H_j + \lambda) w_j^2] + \gamma T令$\frac{\partial Obj^{(t)}}{\partial w}=0$ 得到 w_j^* = - \frac {G_j} {H_j + \lambda}带入上式得到 Obj^{(t)} = - \frac 1 2 \sum_{j=1}^T \frac {G_j^2} {H_j + \lambda} + \gamma T计算得分=》评价分裂点 \begin{align} Obj_{split} &= - \frac 1 2 [\frac {G_L^2}{H_L + \lambda} + \frac {G_R^2}{H_R + \lambda}] + \gamma T_{split} \\ Obj_{noSplit} &= - \frac 1 2 \frac {(G_L + G_R)^2}{H_L + H_R + \lambda} + \gamma T_{noSplit} \\ Gain &= Obj_{noSplit} - Obj_{split} \\ &= \frac 1 2 [\frac {G_L^2}{H_L + \lambda} + \frac {G_R^2}{H_R + \lambda} - \frac {(G_L + G_R)^2}{H_L + H_R + \lambda}] - \gamma(T_{split} - T_{nosplit}) \\ &=\frac 1 2 [\frac {G_L^2}{H_L + \lambda} + \frac {G_R^2}{H_R + \lambda} - \frac {(G_L + G_R)^2}{H_L + H_R + \lambda}] - \gamma \end{align}因为是二分类,二叉树所以$T_{split} - T_{nosplit} = 1$,$Gain$越大越好 寻找最优分裂点论文中给出了两种分裂结点的方法,贪心算法遍历所有分割点进行划分挑选增益最大的切分点。近似算法:对于数据量大的情况下进行近似算法 贪心法:直观的方法是枚举所有的树结构,并根据上面数structure score来打分,找出最优的那棵树加入模型中,再不断重复。但暴力枚举根本不可行,所以类似于一般决策树的构建,XGBoost也是采用贪心算法,每一次尝试去对已有的叶子加入一个分割。对于一个具体的分割方案,增益计算如下: Gain=\frac 1 2 [\frac {G_L^2}{H_L + \lambda} + \frac {G_R^2}{H_R + \lambda} - \frac {(G_L + G_R)^2}{H_L + H_R + \lambda}] - \gamma对于每次扩展,我们还是要枚举所有可能的分割方案,如何高效地枚举所有的分割呢?我假设我们要枚举所有 $x < a$ 这样的条件,对于某个特定的分割 $a$ 我们要计算 $a$ 左边和右边的导数和。对于所有的 $a$,首先根据需要划分的那列特征值排序,然后从左到右的扫描就可以枚举出所有分割的梯度和$G_L$和$G_R$,再用上面的公式计算每个分割方案的分数就可以了。观察这个目标函数,大家会发现第二个值得注意的事情就是引入分割不一定会使得情况变好,因为我们有一个引入新叶子的惩罚项。优化这个目标对应了树的剪枝, 当引入的分割带来的增益小于一个阀值的时候,我们可以剪掉这个分割。大家可以发现,当我们正式地推导目标的时候,像计算分数和剪枝这样的策略都会自然地出现,而不再是一种因为heuristic(启发式)而进行的操作了。 算法说明上面是针对一个特征,如果有m个特征,需要对所有参数都采取一样的操作,然后找到最好的那个特征所对应的划分。 近似算法XGBoost使用exact greedy算法来寻找分割点建树,但是当数据量非常大难以被全部加载进内存时或者分布式环境下时,exact greedy算法将不再合适。因此作者提出近似算法来寻找分割点。近似算法的大致流程见下面的算法。该算法会首先根据特征分布的百分位数 (percentiles of feature distribution),提出候选划分点 (candidate splitting points)。接着,该算法将连续型特征映射到由这些候选点划分的分桶(buckets) 中,聚合统计信息,基于该聚合统计找到在 proposal 间的最优解。 Global:学习每棵树前,提出候选切分点; Local:每次分裂前,重新提出候选切分点; 第一个for循环做的工作:对特征 $K$ 根据该特征分布的分位数找到切割点的候选集合 $S_k = \{s_{k1}, s_{k2}, … ,s_{kl} \}$;这样做的目的是提取出部分的切分点不用遍历所有的切分点。其中获取某个特征K的候选切割点的方式叫proposal。主要有两种proposal方式:global proposal和local proposal。 第二个for循环的工作:将每个特征的取值映射到由这些该特征对应的候选点集划分的分桶(buckets)区间 $\{s_{k,v}≥x_{jk}>s_{k,v−1}\}$ 中,对每个桶(区间)内的样本统计值 $G,H$ 进行累加统计,最后在这些累计的统计量上寻找最佳分裂点。这样做的主要目的是获取每个特征的候选分割点的 $G,H$ 量。 近似算法举例 总结目标函数与叶子结点权值 Obj= - \frac 1 2 \sum_{j=1}^T \frac {G_j^2} {H_j + \lambda} + \gamma T \\ w_j^* = - \frac {G_j} {H_j + \lambda}其中 g_i=\frac{\partial l(y_i,\hat{y_i}^{(t-1)})}{\partial \hat{y_i}^{(t-1)}} \quad h_i=\frac{\partial ^2l(y_i,\hat{y_i}^{(t-1)})}{\partial ^2\hat{y_i}^{(t-1)}} \\ G_j = \sum_{i \in I_j} g_i \quad H_j = \sum_{i \in I_j} h_i打分函数示例Obj代表了当我们指定一个树的结构的时候,我们在目标上面最多减少多少。我们可以把它叫做结构分数(structure score) xgboost调参通用参数 参数 说明 booster [default=gbtree] 有两中模型可以选择gbtree和gblinear。gbtree使用基于树的模型进行提升计算,gblinear使用线性模型进行提升计算 silent [default=0] 取0时表示打印出运行时信息,取1时不打印运行时信息 Booster参数 参数 说明 n_estimators 弱学习器的个数。越多越容易过拟合 max_depth[默认6] 树的最大深度。越深越容易过拟合 learning_rate[默认0.3] 学习率。通过减少每一步的权重,可以提高模型的鲁棒性(找对最优值)。典型值为0.01-0.2。 min_child_weight[默认1] 孩子节点中最小的样本权重和。和GBM的 min_child_leaf 参数类似,但不完全一样。XGBoost的这个参数是最小样本权重的和,而GBM参数是最小样本总数。这个参数用于避免过拟合。当它的值较大时,可以避免模型学习到局部的特殊样本。但是如果这个值过高,会导致欠拟合。这个参数需要使用CV来调整。 max_leaf_nodes 树上最大的节点或叶子的数量。与树的深度正相关,树越深叶子节点越多 gamma[默认0] 在节点分裂时,只有分裂后损失函数的值下降了,才会分裂这个节点。Gamma指定了节点分裂所需的最小损失函数下降值。越小越容易过拟合 max_delta_step[默认0] 这参数限制每棵树权重改变的最大步长。如果这个参数的值为0,那就意味着没有约束。如果它被赋予了某个正值,那么它会让这个算法更加保守。 subsample[默认1] 和GBM中的subsample参数一模一样。这个参数控制对于每棵树,随机采样的比例。如果设置为0.5则意味着XGBoost将随机的从整个样本集合中抽取出50%的子样本建立树模型,这能够防止过拟合。 colsample_bytree[默认1] 构建子树时,对特征的采样比例。和GBM里面的max_features参数类似。 colsample_bylevel[默认1] 找划分点时,对特征的采样比例。用来控制树的每一级的每一次分裂,对列数的采样的占比。 reg_lambda[默认1] 权重的L2正则化项 reg_alpha[默认1] 权重的L1正则化项 学习目标参数 参数 说明 objective[默认reg:linear] 这个参数定义需要被最小化的损失函数。最常用的值有:binary:logistic、multi:softmax 、multi:softprob eval_metric[默认值取决于objective参数的取值] rmse 均方根误差:$\sqrt \frac{\sum_{i=1}^N \epsilon^2}{N}$mae 平均绝对误差$$logloss 负对数似然函数值error 二分类错误率(阈值为0.5)merror 多分类错误率mlogloss 多分类logloss损失函数auc 曲线下面积 https://www.biaodianfu.com/xgboost.html xgboost与GBDT的区别 Xgboost是GB算法的高效实现,xgboost中的基学习器除了可以是CART(gbtree)也可以是线性分类器(gblinear)。 传统GBDT以CART作为基分类器,xgboost还支持线性分类器,这个时候xgboost相当于带L1和L2正则化项的逻辑斯蒂回归(分类问题)或者线性回归(回归问题)。 传统GBDT在优化时只用到一阶导数信息,xgboost则对代价函数进行了二阶泰勒展开,同时用到了一阶和二阶导数。顺便提一下,xgboost工具支持自定义代价函数,只要函数可一阶和二阶求导。 xgboost在代价函数里加入了正则项,用于控制模型的复杂度。正则项里包含了树的叶子结点个数、每个叶子结点上输出的score的L2模的平方和 列抽样(column subsampling)。xgboost借鉴了随机森林的做法,支持列抽样,不仅能降低过拟合,还能减少计算,这也是xgboost异于传统gbdt的一个特性。 支持并行化处理。xgboost的并行是在特征粒度上的,在训练之前,预先对特征进行了排序,然后保存为block结构,后面的迭代中重复地使用这个结构,大大减小计算量。在进行结点的分裂时,需要计算每个特征的增益,最终选增益最大的那个特征去做分裂,那么各个特征的增益计算就可以开多线程进行,即在不同的特征属性上采用多线程并行方式寻找最佳分割点。 可以处理稀疏、缺失数据(结点分裂算法能自动利用特征的稀疏性),可以学习出它的分裂方向,加快稀疏计算速度。]]></content>
<categories>
<category>人工智能</category>
<category>机器学习</category>
</categories>
<tags>
<tag>模型</tag>
<tag>树模型</tag>
<tag>集成学习</tag>
</tags>
</entry>
<entry>
<title><![CDATA[GBDT]]></title>
<url>%2F2017%2F10%2F20%2F%E4%BA%BA%E5%B7%A5%E6%99%BA%E8%83%BD%2F%E6%9C%BA%E5%99%A8%E5%AD%A6%E4%B9%A0%2FGBDT%2F</url>
<content type="text"><![CDATA[简介GBDT也是集成学习Boosting家族的成员,由梯度提升方法与回归树结合而成。分类|损失函数回归|$(y-\hat y)^2$分类|$p_K log_2 \; p_K$ 回归树回归树生成算法 提升树提升树可以表示为以下形式:这里我们约定 $T(x;Θ_m)$ 表示第 $m$ 棵决策树;$Θ_m$表示决策树的参数;$M$ 为树的个数。强分类器 $f_M(x)$ 可以由多个弱分类器 $T(x;Θ_m)$ 线性相加而成 f_M (x)=\sum_{m=1}^MT(x;Θ_m )提升树的前向分步算法。第$m$步的模型可以写成 f_m (x)=f_{m-1} (x)+ T(x;Θ_m )然后得到损失函数 L(f_m (x),y)=L(f_{m-1} (x)+ T(x;Θ_m ),y)迭代的目的是构建 $T(x;Θ_m)$,使得本轮损失 $L(f_m(x),y)$ 最小。思想其实并不复杂,但是问题也很明显,对于不同的任务会有不同的损失函数,当损失函数是平方损失和指数损失函数时,每一步的优化还是简单的。但是对于一般损失函数而言,每一步的优化并不容易 提升树算法 梯度提升树采用泰勒展开式将上式中的残差展开, 泰勒公式一元函数在点$x_k$处的泰勒展开式为: f(x) = f(x_k)+(x-x_k)f'(x_k)+\frac{1}{2!}(x-x_k)^2f''(x_k)+o^n拟合残差的近似梯度提升思想正是为了解决上面的问题。它的主要思想是先求$h_m$,再求$β_m$。观察式子 \sum _{i=1}^N = L(y_i,f_{m-1}(x_i)+\beta h_m(x_i))我们要最小化的式子由N部分相加而成,如果能够最小化每一部分,自然也就最小化了整个式子。考察其中任一部分,并将其进行泰勒一阶展开 L(y_i,f_{m-1}(x_i)+\beta h_m(x_i))=L(y_i,f_{m-1}(x_i))+\beta h_m(x_i)\frac{\partial L(y_i,f_{m-1}(x_i))}{\partial f_{m-1}(x_i)} \\ L(y_i,f_{m-1}(x_i)+\beta h_m(x_i))-L(y_i,f_{m-1}(x_i))=\beta h_m(x_i)\frac{\partial L(y_i,f_{m-1}(x_i))}{\partial f_{m-1}(x_i)}由于需要 L(y_i,f_{m-1}(x_i)+\beta h_m(x_i))-L(y_i,f_{m-1}(x_i))]]></content>
<categories>
<category>人工智能</category>
<category>机器学习</category>
</categories>
<tags>
<tag>模型</tag>
<tag>树模型</tag>
<tag>集成学习</tag>
</tags>
</entry>
<entry>
<title><![CDATA[SVM支持向量机]]></title>
<url>%2F2017%2F10%2F18%2F%E4%BA%BA%E5%B7%A5%E6%99%BA%E8%83%BD%2F%E6%9C%BA%E5%99%A8%E5%AD%A6%E4%B9%A0%2FSVM%E6%94%AF%E6%8C%81%E5%90%91%E9%87%8F%E6%9C%BA%2F</url>
<content type="text"><![CDATA[简介支持向量机(support vector machines,SVM)是一种二类分类模型。它的基本模型是定义在特征空间上的间隔最大的线性分类器,间隔最大使它有别于感知机。构建一个超平面将数据点分开,使所有数据距离超平面的距离最大。其中红色与蓝色的数据点成为支持向量 知识体系结构 学习一个线性的分类器,即线性可分支持向量机,又称为硬间隔支持向量机; 当训练数据近似线性可分时,通过软间隔最大化,也学习一个线性的分类器,即线性支持向量机,又称为软间隔支持向量机; 当训练数据线性不可分时,通过使用核技巧及软间隔最大化,学习非线性支持向量机。 算法模型输入:是线性可分的 $m$ 个样本${(x_1,y_1), (x_2,y_2), …, (x_m,y_m),}$,其中 $x$ 为 $n$ 维特征向量。$y$为二元输出,值为1,或者-1.输出:是分离超平面的参数和$w^{\ast }和b^{\ast }$和分类决策函数。 \begin{align*} &min\;\; \frac{1}{2}||w||_2^2\\ s.t\;\;\;\; & y_i(w\cdot x_i + b) \geq 1 \;\;(i =1,2,...n) \end{align*}求得最优解,$w^{\ast },b^{\ast }$ 推导w^*\cdot x+b^*=0分类决策函数为 f(x)=sign(w^*\cdot x+b^*)其他点$(x_0,y_0)$平面$w\cdot x_i + b=0$的距离 \frac{|w\cdot x_i + b|}{||w||_2^2}因为$y=\pm 1$所以 \frac{y(w\cdot x_i + b)}{||w||_2^2}假设$y(w\cdot x_i + b)\geqslant 1$则 \begin{align*} &min\;\; \frac{1}{2}||w||_2^2\\ s.t\;\;\;\; & y_i(w\cdot x_i + b) \geq 1 \;\;(i =1,2,...n) \end{align*}线性可分支持向量机与硬间隔最大化概念 线性可分支持量机的定义给定线性可分训练数据集,通过间隔最大化或等价地求解相应的凸二次规划问题学习得到的分离超平面为w\cdot x + b = 0以及相应的分类决策函数f(x)=sign(w\cdot x + b )线性可分如下图所示: 支持向量距离超平面最近的数据点,如下图所示的圆圈处的数据点。 硬间隔\left\{\begin{matrix} w\cdot x_{i}+b\geqslant +1 &y_i=+1\\ w\cdot x_{i}+b\leqslant -1 &y_i=-1 \end{matrix}\right.硬间隔:两个异类支持向量到超平面的距离之和如下所示\gamma =\frac{2}{\left \| w \right \|} 数学模型SVM的模型是让所有点到超平面的距离大于一定的距离,也就是所有的分类点要在各自类别的支持向量两边。用数学式子表示为: \begin{align*} &min\;\; \frac{1}{2}||w||_2^2\\ s.t\;\;\;\; & y_i(w\cdot x_i + b) \geq 1 \;\;(i =1,2,...n) \end{align*} 推导略 怎么得到支持向量根据KKT条件中的对偶互补条件$α_i∗(y_i(w\cdot x_i+b)−1)=0$,如果$α_i>0$则有$y_i(w\cdot x_i+b)=1 $即点在支持向量上,否则如果$α_i=0$则有$y_i(w\cdot x_i+b)≥1$,即样本在支持向量上或者已经被正确分类。 算法输入:线性可分训练集$T={(x_1,y_1), (x_2,y_2), …, (x_m,y_m),}$,其中$x$为n维特征向量。$y$为二元输出,值为1,或者-1.输出:是分离超平面的参数和$w^{\ast }和b^{\ast }$和分类决策函数。 构造约束优化问题\begin{align*} &\underbrace{min}_{\alpha} \;\; \frac{1}{2}\sum\limits_{i=1}^{m}\sum\limits_{j=1}^{m}\alpha_i\alpha_jy_iy_j(x_i \cdot x_j) - \sum\limits_{i=1}^{m} \alpha_i\\ s.t\;\;\;\; & \sum\limits_{i=1}^{m}\alpha_iy_i = 0\\ &\alpha_i \geq 0 \; i=1,2,...m \end{align*} 利用SMO算法求得求出上式最小时对应的$α$向量的值$α^∗$向量. 计算:w^{*} = \sum\limits_{i=1}^{m}\alpha_i^{*}y_ix_i求b则稍微麻烦一点。注意到,对于任意支持向量$(x_s, y_s)$,都有y_s(w\cdot x_s+b) = y_s(\sum\limits_{i=1}^{S}\alpha_iy_ix_ix_s+b) = 1假设我们有S个支持向量,则对应我们求出S个$b^{\ast }$,理论上这些$b^{\ast }$都可以作为最终的结果, 但是我们一般采用一种更健壮的办法,即求出所有支持向量所对应的$b_s^{\ast }$,然后将其平均值作为最后的结果。 线性支持向量机与软间隔最大化概念 线性支持向量机训练数据集不是线性可分的。通常情况是,训练数据中有一些特异点(outlier),将这些特异点除去后,剩下大部分的样本点组成的集合是线性可分的。这时需要修改硬间隔最大化,使其成为软间隔最大化。 本来如果我们不考虑异常点,SVM的超平面应该是下图中的红色线所示,但是由于有一个蓝色的异常点,导致我们学习到的超平面是下图中的粗虚线所示,这样会严重影响我们的分类模型预测效果。 软间隔SVM对训练集里面的每个样本$(x_i,y_i)$引入了一个松弛变量$ξ_i≥0$,使函数间隔加上松弛变量大于等于1,也就是说:y_i(w\cdot x_i +b) \geq 1- \xi_i 数学模型对每个松弛变量$ξ_i$,支付一个代价$ξ_i$,这个就得到了我们的软间隔最大化的SVM学习条件。目标函数由原来的 \begin{align*} &min\;\; \frac{1}{2}||w||^2 +C\sum\limits_{i=1}^{m}\xi_i \\ s.t. \; \; \;\; &y_i(w\cdot x_i + b) \geq 1 - \xi_i \;\;(i =1,2,...m)\\ &\xi_i \geq 0 \;\;(i =1,2,...m) \end{align*}C>0称为惩罚参数,一般由应用问题决定,C值大时对误分类的惩罚增大,C值小时对误分类的惩罚减小。最小化目标函数包含两层含义:使$\frac{1}{2}||w||^2$尽量小即间隔尽量大,同时使误分类点的个数尽量小,C是调和二者的系数。 支持向量 根据软间隔最大化时KKT条件中的对偶互补条件$α_i∗(y_i(w\cdot x_i+b)−1+ξ_i^∗)=0$我们有 如果$α=0$,那么$y_i(w\cdot x_i+b)−1≥0$,即样本在支持向量上或者已经被正确分类。如图中所有远离支持向量的点。 如果$0≤α≤C$,那么$ξ_i=0,y_i(w\cdot x_i+b)−1=0$,即点在支持向量上。如图中在虚线支持向量上的点。 如果$α=C$,说明这是一个可能比较异常的点,需要检查此时$\xi_i$ 如果$0≤ξ_i≤1$,那么点被正确分类,但是却在超平面和自己类别的支持向量之间。如图中的样本2和4. 如果$ξ_i=1$,那么点在分离超平面上,无法被正确分类。 如果$ξ_i>1$,那么点在超平面的另一侧,也就是说,这个点不能被正常分类。如图中的样本1和3 算法输入:线性可分的$m$个样本$(x_1,y_1),(x_2,y_2),…,(x_m,y_m)$,,其中$x$为$n$维特征向量。$y$为二元输出,值为1,或者-1.输出:是分离超平面的参数和$w^{\ast }$和$b^{\ast }$和分类决策函数。 选择一个惩罚系数C>0, 构造约束优化问题 \begin{align*} &\underbrace{min}_{\alpha} \;\; \frac{1}{2}\sum\limits_{i=1}^{m}\sum\limits_{j=1}^{m}\alpha_i\alpha_jy_iy_j(x_i \cdot x_j) - \sum\limits_{i=1}^{m} \alpha_i\\ s.t\;\;\;\; & \sum\limits_{i=1}^{m}\alpha_iy_i = 0\\ &0\leqslant \alpha_i \leqslant C \; \;\;i=1,2,...m \end{align*} 用SMO算法求出上式最小时对应的$α$向量的值$α^∗$向量. 计算$w^{} = \sum\limits_{i=1}^{m}\alpha_i^{}y_ix_i$ 找出所有的S个支持向量,即满足$0<α_s<C$对应的样本$(x_s,y_s)$,通过 $y_s(\sum\limits_{i=1}^{S}\alpha_iy_ix_i^Tx_s+b) = 1$,计算出每个支持向量$(x_s,y_s)$对应的$b_s^{\ast }$,计算出这些$b_s^{\ast } = y_s - \sum\limits_{i=1}^{S}\alpha_iy_ix_i^Tx_s$. 所有的$b_s^∗$对应的平均值即为最终的$b^{\ast } = \frac{1}{S}\sum\limits_{i=1}^{S}b_s^{\ast }$ 这样最终的分类超平面为:$w^∗\cdot x+b^∗=0$,最终的分类决策函数为: f(x) = sign(w^{*} \cdot x + b^{*}) 合页损失函数线性支持向量机还有另外一种解释如下: \underbrace{ min}_{w, b}[1-y_i(w \cdot x + b)]_{+} + \lambda ||w||_2^2其中$L(y(w \bullet x + b)) = [1-y_i(w \bullet x + b)]_{+}$称为合页损失函数(hinge loss function),下标+表示为: [z]_{+}= \begin{cases} z & {z >0}\\ 0& {z\leq 0} \end{cases}也就是说,如果点被正确分类,且函数间隔大于1,损失是0,否则损失是$1−y(w∙x+b)$,如下图中的绿线。我们在下图还可以看出其他各种模型损失和函数间隔的关系:对于0-1损失函数,如果正确分类,损失是0,误分类损失1, 如下图黑线,可见0-1损失函数是不可导的。对于感知机模型,感知机的损失函数是$[-y_i(w \cdot x + b)]_{+}$,这样当样本被正确分类时,损失是0,误分类时,损失是$-y_i(w \cdot x + b)$,如下图紫线。对于逻辑回归之类和最大熵模型对应的对数损失,损失函数是$log[1+exp(−y(w\cdot x+b))]$, 如下图红线所示。 非线性支持向量机与核函数概念 非线性分类问题非线性分类问题是指通过利用非线性模型才能很好地进行分类的问题。如下图所示,无法用直线(线性模型)将正负实例正确分开,但可以用一条椭圆曲线(非线性模型)将它们正确分开。用线性分类方法求解非线性分类问题分为两步:首先使用一个变换将原空间的数据映射到新空间;然后在新空间里用线性分类学习方法从训练数据中学习分类模型。 核函数设$\mathbb{X}$是输入空间(欧氏空间$\mathbb{R}^{n}$的子集或离散集合),又设$\mathbb{H}$为特征空间(希尔伯特空间),如果存在一个从$\mathbb{X}$到$\mathbb{H}$的映射\Phi (x):\mathbb{X}\rightarrow \mathbb{H}使得对所有$x,z\in \mathbb{X}$,函数$K(x,z)$满足条件K(x,z)=\Phi (x)\cdot \Phi (z)则称$K(x,z)$为核函数,$\Phi (x)$为映射函数,式中$\Phi (x)\cdot \Phi (z)$为$\Phi (x)$和$\Phi (z)$的内积。 正定核函数一个函数要想成为正定核函数,必须满足他里面任何点的集合形成的Gram矩阵是半正定的。也就是说,对于任意的$x_i \in \chi , i=1,2,3…m$,$K(x_i,x_j)$对应的Gram矩阵$K = \bigg[ K(x_i, x_j )\bigg]$是半正定矩阵,则$K(x,z)$是正定核函数。 常用核函数 线性核函数线性核函数(Linear Kernel)其实就是我们前两篇的线性可分SVM,表达式为:K(x, z) = x \cdot z也就是说,线性可分SVM我们可以和线性不可分SVM归为一类,区别仅仅在于线性可分SVM用的是线性核函数。 多项式核函数多项式核函数(Polynomial Kernel)是线性不可分SVM常用的核函数之一,表达式为:K(x, z) = (\gamma x \cdot z + r)^d其中,$γ,r,d$都需要自己调参定义。 高斯核函数高斯核函数(Gaussian Kernel),在SVM中也称为径向基核函数(Radial Basis Function,RBF),它是非线性分类SVM最主流的核函数。libsvm默认的核函数就是它。表达式为:K(x, z) = exp(-\gamma||x-z||^2)其中,$γ$大于0,需要自己调参定义。 Sigmoid核函数Sigmoid核函数(Sigmoid Kernel)也是线性不可分SVM常用的核函数之一,表达式为:K(x, z) = tanh(\gamma x \cdot z + r)其中,$γ,r$都需要自己调参定义。 算法输入:$m$个样本$(x_1,y_1), (x_2,y_2), …, (x_m,y_m),$,其中x为n维特征向量。y为二元输出,值为1,或者-1.输出:分离超平面的参数$w^∗$和$b^∗$和分类决策函数。算法过程如下: 选择适当的核函数$K(x,z)$和一个惩罚系数$C>0$, 构造约束优化问题\begin{align*} &\underbrace{min}_{\alpha} \;\; \frac{1}{2}\sum\limits_{i=1}^{m}\sum\limits_{j=1}^{m}\alpha_i\alpha_jy_iy_j(x_i \cdot x_j) - \sum\limits_{i=1}^{m} \alpha_i\\ s.t\;\;\;\; & \sum\limits_{i=1}^{m}\alpha_iy_i = 0\\ &0\leqslant \alpha _i\leqslant C \; i=1,2,...m \end{align*} 用SMO算法求出上式最小时对应的$α$向量的值$α^∗$向量 得到$w^{\ast } = \sum\limits_{i=1}^{m}\alpha_i^{\ast }y_i\phi(x_i)$,此处可以不直接显式的计算$w^∗$。 找出所有的S个支持向量,即满足$0 < \alpha_s < C$对应的样本$(x_s,y_s)$,通过 $y_s(\sum\limits_{i=1}^{S}\alpha_iy_iK(x_i,x_s)+b) = 1$,计算出每个支持向量$(x_s,y_s)$对应的$b_s^{\ast }$,计算出这些$b_s^{\ast } = y_s - \sum\limits_{i=1}^{S}\alpha_iy_iK(x_i,x_s)$. 所有的$b_s^{\ast }$对应的平均值即为最终的$b^{*} = \frac{1}{S}\sum\limits_{i=1}^{S}b_s^{\ast }$ 这样最终的分类超平面为:$\sum\limits_{i=1}^{m}\alpha_i^{\ast }y_iK(x, x_i)+ b^{\ast } = 0$,最终的分类决策函数为: f(x) = sign(\sum\limits_{i=1}^{m}\alpha_i^{\ast }y_iK(x, x_i)+ b^{\ast })SMO算法支持向量机原理(一) 线性支持向量机支持向量机原理(二) 线性支持向量机的软间隔最大化模型支持向量机原理(三) 线性不可分支持向量机与核函数支持向量机原理(四) SMO算法原理]]></content>
<categories>
<category>人工智能</category>
<category>机器学习</category>
</categories>
<tags>
<tag>模型</tag>
<tag>线性分类器</tag>
</tags>
</entry>
<entry>
<title><![CDATA[神经元-感知器梯度下降]]></title>
<url>%2F2017%2F09%2F04%2F%E4%BA%BA%E5%B7%A5%E6%99%BA%E8%83%BD%2F%E6%B7%B1%E5%BA%A6%E5%AD%A6%E4%B9%A0%2F%E6%84%9F%E7%9F%A5%E5%99%A8%E6%A2%AF%E5%BA%A6%E4%B8%8B%E9%99%8D%2F</url>
<content type="text"><![CDATA[感知器-神经元 输入权值 一个感知器可以接收多个输$(x_1, x_2,…,x_n\mid x_i\in R)$(特征),每个输入上有一个权值$w_i\in R$。此外还有一个偏置项$b$,就是上图中的$w_0$ 激活函数 感知器的激活函数可以有很多选择,比如我们可以选择下面这个阶跃函数$f$来作为激活函数:f(z)=\left\{\begin{matrix} 1&\: \, z>0\\ 0&\: \,otherwise \end{matrix}\right. 输出 感知器的输出由下面这个公式来计算y=f(\mathrm{w}\bullet\mathrm{x}+b)\qquad偏置项$b$,就是上图中的$w_0$y=f(\mathrm{w}\bullet\mathrm{x})线性单元和梯度下降当感知器为线性函数时候y=\mathrm{w}\bullet\mathrm{x}+b偏置项$b$,就是上图中的$w_0$,$y=\mathrm{w}\bullet\mathrm{x}$其优化如下所示梯度下降法在监督学习下,对于一个样本,我们知道它的特征$x$,以及标记$y$。同时,我们还可以根据模型 $y=\mathrm{w}\bullet\mathrm{x}+b$ 计算得到输出 $\bar{y}$。注意这里面我们用$y$表示训练样本里面的标记,也就是实际值;用带上划线的 $\bar{y}$ 表示模型计算的出来的预测值。我们当然希望模型计算出来的和越接近越好。则损失函数为loss = \frac{1}{2}\sum_{i=1}^{n}(y^{(i)}-\bar{y}^{(i)})^2 \frac{\partial loss}{\partial w}=\sum_{i=1}^{n}(y^{(i)}-\bar{y}^{(i)})\mathrm{x}^{(i)}\mathrm{w}_{new}=\mathrm{w}_{old}+\eta\sum_{i=1}^{n}(y^{(i)}-\bar{y}^{(i)})\mathrm{x}^{(i)}\begin{bmatrix} w_0 \\ w_1 \\ w_2 \\ ... \\ w_m \\ \end{bmatrix}_{new}= \begin{bmatrix} w_0 \\ w_1 \\ w_2 \\ ... \\ w_m \\ \end{bmatrix}_{old}+\eta\sum_{i=1}^{n}(y^{(i)}-\bar{y}^{(i)}) \begin{bmatrix} 1 \\ x_1^{(i)} \\ x_2^{(i)} \\ ... \\ x_m^{(i)} \\ \end{bmatrix}]]></content>
<categories>
<category>人工智能</category>
<category>深度学习</category>
</categories>
<tags>
<tag>凸优化</tag>
<tag>模型</tag>
<tag>感知器</tag>
</tags>
</entry>
<entry>
<title><![CDATA[牛顿法和拟牛顿法]]></title>
<url>%2F2017%2F09%2F03%2F%E4%BA%BA%E5%B7%A5%E6%99%BA%E8%83%BD%2F%E4%BC%98%E5%8C%96%E7%AE%97%E6%B3%95%2F%E7%89%9B%E9%A1%BF%E6%B3%95%E5%92%8C%E6%8B%9F%E7%89%9B%E9%A1%BF%E6%B3%95%2F</url>
<content type="text"><![CDATA[牛顿法(Newton method)和拟牛顿法(quasi Newton method)也是求解无约束最优化问题的常用方法。方法使用函数 $f(x)$的一二阶导数。具有收敛速度快、迭代次数少、计算量大的特点。 问题描述考虑如下无约束极小化问题: \min_{x\in R^n} \; f(x)最优点记为$x^\ast$ 牛顿法 输入:目标函数$f(x)$,梯度$g(x)=\bigtriangledown f(x)$,海森矩阵$H(x)$,精度要求$\varepsilon $ 输出:$f(x)$的极小点$x^\ast$ 确定初始点$x_0$,置$k=0$。 计算 $g_k=g(x^{(k)})$ 若$\left | g_k \right |<\varepsilon $,则停止计算,得近似解$x^\ast=x^{(k)}$ 计算$H_k=H(x^{(k)})$ 置$x^{(k+1)}=x^{(k)}-H_k^{-1}g_k$ 置$k=k+1$,转第二步骤 其中$g_k=\bigtriangledown f(x^{(k)})$,$H_k=\bigtriangledown^2 f(x^{(k)})=\left [\frac{\partial^2 f}{\partial x_i\partial x_j} \right ]_{n\times n}$。$H_{k}^{-1}$为$x^{(k)}$的Hessian的逆矩阵, g(f)=\bigtriangledown f=\begin{bmatrix} \frac{\partial f}{\partial x_1} \\ \frac{\partial f}{\partial x_2} \\ \vdots \\ \frac{\partial f}{\partial x_n} \\ \end{bmatrix} \; \; \; \; \; H(f)= \bigtriangledown^2 f= \begin{bmatrix} \frac{\partial^2 f}{\partial x^2_1} & \frac{\partial^2 f}{\partial x_1 \partial x_2} \cdots & \frac{\partial^2 f}{\partial x_1 \partial x_n} \\\ \frac{\partial^2 f}{\partial x_2 \partial x_1} & \frac{\partial^2 f}{\partial x^2_2} \cdots & \frac{\partial^2 f}{\partial x_2 \partial x_n} \\\ \vdots & \ddots & \vdots \\\ \frac{\partial^2 f}{\partial x_n \partial x_1} & \frac{\partial^2 f}{\partial x_n \partial x_2} \cdots & \frac{\partial^2 f}{\partial x^2_n} \end{bmatrix}代数推导假设函数 $f(x)$ 二次可微,则在当前第$k$次迭代点 $x^{(k)}$ 对 $f(x)$ 进行二阶泰勒展开 f \left ( x \right ) \approx f \left ( x^{(k)} \right )+{f}'\left ( x^{(k)} \right )\left ( x-x^{(k)} \right )+\frac{1}{2}{f}''\left ( x^{(k)} \right )\left ( x-x^{(k)} \right )^2求函数 $f(x)$ 极值则可以转化为对 $f(x)$ 求导并令其为0, \frac{\partial f}{\partial x} \approx f'\left ( x^{(k)} \right )+{f}''\left ( x^{(k)} \right )\left ( x-x^{(k)} \right )=0得到 \begin{align*} x&=x^{(k)}-\frac{f'\left ( x^{(k)} \right )}{f''\left ( x^{(k)} \right )} \\ &= x^{(k)}-H_{k}^{-1}g_k \\ x^{(k+1)} &= x^{(k)}-H_{k}^{-1}g_k \end{align*}阻尼牛顿法原始牛顿法由于迭代公式中没有步长因子,而是固定步长,有时会使迭代超过极值点,即$f(x^{(k+1)})>f(x^k)$,在牛顿方向上附加了步长因子,每次调整时会在搜索空间,在该方向找到最优步长,然后调整 \lambda _k=arg\; \underset{\lambda \in \mathbb{R}}{min}\; f(x_k+\lambda H_k^{-1}g_k)\;\;\;\;\;\;\; \tag 2 输入:目标函数$f(x)$,梯度$g(x)=\bigtriangledown f(x)$,海森矩阵$H(x)$,精度要求$\varepsilon $ 输出:$f(x)$的极小点$x^\ast$ 确定初始点$x_0$,置$k=0$。 计算 $g_k=g(x^{(k)})$ 若$\left | g_k \right |<\varepsilon $,则停止计算,得近似解$x^\ast=x^{(k)}$ 计算$H_k=H(x^{(k)})$ 利用$(2)$式子得到步长$\lambda_k$置$x^{(k+1)}=x^{(k)}-\lambda_k H_k^{-1}g_k$ 置$k=k+1$,转第二步骤 拟牛顿算法拟牛顿思路 因为Hessian矩阵的计算量大而且无法保持正定所以采用拟牛顿算法。拟牛顿算法的基本思想是不用二阶偏导数而构造出可以近似Hessian矩阵(或者Hessian矩阵的逆矩阵)的正定对称矩阵,在拟牛顿的条件下优化目标函数 拟牛顿条件\begin{align*} \frac{\partial f}{\partial x} &\approx f'\left ( x^{(k)} \right )+{f}''\left ( x^{(k)} \right )\left ( x-x^{(k)} \right )\\ f'\left ( x^{(k+1)} \right ) &\approx f'\left ( x^{(k)} \right )+{f}''\left ( x^{(k)} \right )\left ( x^{(k+1)}-x^{(k)} \right ) \\ g_{k+1}-g_k &\approx H_k\left ( x^{(k+1)}-x^{(k)} \right ) \end{align*} 拟牛顿法对$H_k$或$H_k^{-1}$取近似值,可减少计算量。记$B\approx H$,$D\approx H^{-1}$,$y_k=g_{k+1}-g_k$,$s_k=x_{k+1}-x_k$。根据拟牛顿条件,可得近似公式: \color{red}{B_{k+1}=\frac{y_k}{s_k} \;\;\;\;\;\;\;\;D_{k+1}=\frac{s_k}{y_k}}DFP算法DFP算法采用的是$D$,但并不直接计算$D$,而是计算每一步DD的增量$ΔD$来间接的求出$D$。这也是很多优化算法的做法,因为一般上一步的中间结果对下一步的计算仍有价值,若直接抛弃重新计算耗时耗力耗内存,重新发明了轮子。 D_{k+1}=D_k+\Delta D_k$D_0$通常取单位矩阵I,关键导出每一步的$ΔD_k$。通过一系列艰苦而又卓绝的推导计算假设取便,最终的导出结果为: \color{red}{\begin{align*} D_{k+1}&=D_k+\Delta D_k\; \; \; \; k=0,1,2,\cdots \\ \Delta D_k &=\frac{s_ks_k^T}{s_k^Ty_k} -\frac{D_ky_ky_k^TD_k}{y_k^TD_ky_k} \end{align*}}一般来说,在进行中间增量计算时,都要经过这一步艰苦而又卓绝的推导计算。 BFGS算法BFGS算法与DFP算法类似,只是采用的BB来近似HH。最终的公式为: \color{red}{\begin{align*} \Delta B_k &=\frac{y_ky_k^T}{s_k^Ty_k} -\frac{B_ks_ks_k^TB_k}{s_k^TB_ks_k} \end{align*}}L-BFGSL-BFGS算法对BFGS算法进行改进,不再存储矩阵$D_k$,因为$D_k$有时候比较大,计算机的肚子盛不下。但是我们用到$D_k$的时候怎么办呢?答案是根据公式求出来。 从上面的算法推导可知,$D_k$只跟$D_0$和序列$\{s_k\}$和$\{y_k\}$有关。即我们知道了后者,即可以求得前者。进一步近似,我们只需要序列$\{s_k\}$和$\{y_k\}$的最近$m$个值即可。这样说来,我们的计算机内存中只需要存储这两个序列即可,瞬间卸掉了很多东西,正是春风得意马蹄轻。当然,这样cpu的计算量也会相应的增加,这是可以接受的,马,也是可以接受的。 最终的递推关系为 D_{k+1}=V^T_kD_kV_k+\rho_k s_ks^T_k其中 \rho_k=\frac{1}{y^T_ks_k},V_k=I-\rho_ky_ks^T_k]]></content>
<categories>
<category>人工智能</category>
<category>优化算法</category>
</categories>
<tags>
<tag>优化</tag>
<tag>凸优化</tag>
</tags>
</entry>
<entry>
<title><![CDATA[拉格朗日]]></title>
<url>%2F2017%2F09%2F02%2F%E4%BA%BA%E5%B7%A5%E6%99%BA%E8%83%BD%2F%E4%BC%98%E5%8C%96%E7%AE%97%E6%B3%95%2F%E6%8B%89%E6%A0%BC%E6%9C%97%E6%97%A5%2F</url>
<content type="text"><![CDATA[拉格朗日对偶性是解决带约束的最优化问题的方法,在实际应用中,通过拉格朗日对偶原理将原始问题转换成对偶问题,将原来不容易解决的问题转化为一个容易解决的问题,如支持向量机,最大熵模型。 原始问题假设$f(x),c_i(x),h_j(x)$是定义在$\mathbb{R}^{n}$上的连续可微函数。我们需要求解约束最优化问题: \underset{x\in \mathbb{R}^n}{min} f(x)\begin{align} \mathbb{s.t.}\quad &c_i(x) \le 0,\quad i=1,2,\cdots,k\\ &h_j(x)=0,\quad j=1,2,\cdots,l \end{align}广义拉格朗日函数为了求解原始问题,我们首先引入广义拉格朗日函数(generalized Lagrange function): L(x,\alpha,\beta)=f(x)+\sum_{i=1}^k \alpha_ic_i(x) + \sum_{j=1}^l\beta_jh_j(x) \tag{4}其中,$x=(x_1,x_2,\cdots,x_n)^T \in \mathbb{R}^n$,$\alpha_i$和$\beta_j$是拉格朗日乘子,特别要求$\alpha_i\geqslant 0$ 原始问题的等价转换:极小极大问题如果把$L(x,\alpha,\beta)$看作是$\alpha、\beta$的函数,求其最大值,即\theta_p(x)=\max_{\alpha,\beta:\alpha_i\ge0}L(x,\alpha,\beta) \tag 5 确定$\alpha、\beta$使$L(x,\alpha,\beta)$取得最大值,(此过程中把$x$看做常量)下面通过$x$是否满足约束条件两方面来分析这个函数 如果$x$ 满足原始问题中约束,由(2)、(3)、(4)、(5)可知 $θ(x)=f(x)$。(少的两项一个是非正的,一个是0,要取最大值的话当然得令两者都为0 如果 $x$ 不满足原始问题中的约束,那么 $θ(x)=+∞$。若某个$i$使约束$c_i(x)>0$,则可令则可令$\alpha \rightarrow +∞$,若某个$j$使得$h_j(x)\neq 0,$,则可令$\beta_j h_j(x) \rightarrow +∞$,而将其余各$\alpha _i、\beta_j$均取为0。 综上: \theta_p(x)=\left\{\begin{matrix} f(x),&x 满足原始问题约束\\ +\infty,&其他 \end{matrix}\right.求解原问题的最小值与原始问题等价 \color{red}{\underset{x\in \mathbb{R}^n}{min}\; \theta_p(x)= \underset{x\in \mathbb{R}^n}{min}\; f(x)= \underset{x\in \mathbb{R}^n}{min}\;\max_{\alpha,\beta:\alpha_i\ge0}L(x,\alpha,\beta) }原始问题和对偶问题的关系 弱对偶关系:弱对偶关系一定存在,若原始问题和对偶问题都有最优值,则\color{red}{\underset{\alpha,\beta:\alpha_i\ge0}{max}\; \underset{x\in\mathbb{R}^{n}}{min}\; L(x,\alpha,\beta) \leqslant \underset{x\in \mathbb{R}^n}{min}\;\max_{\alpha,\beta:\alpha_i\ge0}L(x,\alpha,\beta) ={min}\; f(x)} 强对偶关系设$x^\ast$和$a^\ast$,$β^\ast$分别是原始问题$\underset{x\in \mathbb{R}^n}{min}\;\underset{\alpha,\beta:\alpha_i\ge0}{max}\; L(x,\alpha,\beta)$和对偶问题$\underset{\alpha,\beta:\alpha_i\ge0}{max}\; \underset{x\in\mathbb{R}^{n}}{min}\; L(x,\alpha,\beta)$的可行解,并且\underset{\alpha,\beta:\alpha_i\ge0}{max}\; \underset{x\in\mathbb{R}^{n}}{min}\; L(x,\alpha,\beta) = \underset{x\in \mathbb{R}^n}{min}\;\max_{\alpha,\beta:\alpha_i\ge0}L(x,\alpha,\beta) ={min}\; f(x)\color{red}{\underset{x\in\mathbb{R}^{n}}{min}\; L(x,\alpha^\ast ,\beta^\ast ) = \max_{\alpha,\beta:\alpha_i\ge0}L(x^\ast ,\alpha,\beta)={min}\; f(x^\ast )}则$x^\ast$和$a^\ast$,$β^\ast$分别是原始问题和对偶问题的最优解。 弱对偶关系若原始问题和对偶问题都有最优值,则 \underset{x\in\mathbb{R}^{n}}{min}\; L(x,\alpha,\beta) \leqslant L(x,\alpha,\beta) \leqslant \max_{\alpha,\beta:\alpha_i\ge0}L(x,\alpha,\beta)\color{red}{\underset{\alpha,\beta:\alpha_i\ge0}{max}\; \underset{x\in\mathbb{R}^{n}}{min}\; L(x,\alpha,\beta) \leqslant \underset{x\in \mathbb{R}^n}{min}\;\max_{\alpha,\beta:\alpha_i\ge0}L(x,\alpha,\beta) ={min}\; f(x)}推论:设$x^\ast$和$a^\ast$,$β^\ast$分别是原始问题$\underset{x\in \mathbb{R}^n}{min}\;\underset{\alpha,\beta:\alpha_i\ge0}{max}\; L(x,\alpha,\beta) $和对偶问题$\underset{\alpha,\beta:\alpha_i\ge0}{max}\; \underset{x\in\mathbb{R}^{n}}{min}\; L(x,\alpha,\beta) $的可行解,并且$\underset{x\in \mathbb{R}^n}{min}\;\underset{\alpha,\beta:\alpha_i\ge0}{max}L(x,\alpha,\beta) =\underset{\alpha,\beta:\alpha_i\ge0}{max}\; \underset{x\in\mathbb{R}^{n}}{min}\; L(x,\alpha,\beta) $,则$x^\ast$和$a^\ast$,$β^\ast$分别是原始问题和对偶问题的最优解。 强对偶关系 定理1:判断强对偶关系假设函数$f(x)$和$c_i(x)$是凸函数,$h_j(x)$是仿射函数,并且不等式约束$c_i(x)$是严格可行的,即存在$x$,对所有$i$有$c_i(x)<0$,则存在$x^\ast$和$a^\ast,β^\ast$分别是原始问题和对偶问题的解,并且满足 \color{red}{\underset{\alpha,\beta:\alpha_i\ge0}{max}\; \underset{x\in\mathbb{R}^{n}}{min}\; L(x,\alpha^\ast ,\beta^\ast ) =L(x^\ast,\alpha^\ast ,\beta^\ast )= \underset{x\in \mathbb{R}^n}{min}\;\max_{\alpha,\beta:\alpha_i\ge0}L(x^\ast ,\alpha,\beta) ={min}\; f(x^\ast )} 定理2:KKT条件(求最优解)假设函数$f(x)$和$c_i(x)$是凸函数,$h_j(x)$是仿射函数,并且不等式约束$c_i(x)$是严格可行的,则$x^\ast$和$a^\ast,β^\ast$分别是原始问题和对偶问题的解的充分必要条件是$x^\ast,a^\ast,β^\ast$满足下面的Karush-Kuhn-Tucker(KKT)条件:(判断极值、其余项为0) \begin{align*} \nabla_xL(x^\ast,\alpha^\ast,\beta^\ast)&=0\\ \nabla_{\alpha}L(x^\ast,\alpha^\ast,\beta^\ast) &=0 \\ \nabla_{\beta}L(x^\ast,\alpha^\ast,\beta^\ast)&=0 \end{align*}\begin{align*} \alpha_i^*c_i(\boldsymbol{x}^*)&=0,\quad i=1,2,\cdots,k\\ c_i(\boldsymbol{x}^*)&\le0,\quad i=1,2,\cdots,k \\ \alpha_i^*&\ge0,\quad i=1,2,\cdots,k \\ h_j(\boldsymbol{x}^*)&=0,\quad j=1,2,\cdots,l \end{align*}当原问题是凸优化问题时,满足KKT条件的点也是原、对偶问题的最优解。 仿射函数f(x)=A\cdot x+b仿射函数就是一个线性函数,其输入是$n$ 维向量,参数 $A$ 可以是常数,也可以是 $m×n$ 的矩阵,$b$可以是常数,也可以是 $m$ 维的列向量,输出是一个 $m $维的列向量。在几何上,仿射函数是一个线性空间到另一个线性空间的变换。]]></content>
<categories>
<category>人工智能</category>
<category>优化算法</category>
</categories>
<tags>
<tag>优化</tag>
<tag>凸优化</tag>
</tags>
</entry>
<entry>
<title><![CDATA[核函数]]></title>
<url>%2F2017%2F09%2F02%2F%E6%95%B0%E5%AD%A6%E5%9F%BA%E7%A1%80%2F%E9%AB%98%E6%95%B0%2F%E6%A0%B8%E5%87%BD%E6%95%B0%2F</url>
<content type="text"><![CDATA[之前分析的感知机、主成分分析(Principle component analysis, PCA)包括后面看的支撑向量机(Support vector machines, SVM),都有用到核函数。核函数是将信号映射到高维,而PCA一般用来降维。这里简单梳理一下核函数的知识: 核函数基本概念定义设$\chi $是输入空间(欧氏空间$\mathbb{R}^{n}$的子集或离散集合),又设$\mathbb{R}^{H}$为特征空间(希尔伯特空间),如果存在一个从$\chi $到$\mathbb{R}^{H}$的映射\phi (x):\chi \rightarrow \mathbb{H}使得对所有$x,z\in \chi $,函数$K(x,z)$满足条件K(x,z)=\phi (x) \cdot \phi (z)则称$K(x,z)$为核函数,$\phi (x)$为映射函数,式中$\phi (x) \cdot \phi (z)$为$\phi (x)$和$\phi (z)$的内积 核函数:是映射关系$\phi (x)$的内积,映射函数本身仅仅是一种映射关系,并没有增加维度的特性,不过可以利用核函数的特性,构造可以增加维度的核函数,这通常是我们希望的。 核函数的作用 聚类、分类 二维映射到三维,区分就更容易了,这是聚类、分类常用核函数的原因。为什么PCA这样一个降维算法也用核函数呢? 降维 左图为原数据,右图为映射到三维的数据,可以看出:同样是降到1维,先通过Kernel映射到(Kernel是映射的内积,不要弄乱了)三维,再投影到1维,就容易分离开,这就是Kernel在PCA降维中的应用,本质还是对原有数据增加维度。 核函数为什么可以映射到高维为什么实现数据映射到高维 设原空间数据点$a_1=(x_1,x_2);a_2=(x_1’,x_2’)$; 设高维空间的数据点为$A_1 =\phi (a_1)=(z_1,z_2,z_3);A_2=\phi (a_1)=(z_1’,z_2’,z_3’)$ $\left \langle a_1,a_2 \right \rangle$为两点之间的内积$\left \langle a_1,a_2 \right \rangle = \left \langle (x_1,x_2),(x_1’,x_2’) \right \rangle =x_1x_1’+x_2x_2’$ $<\phi (a_1),\phi (a_2)>=<\phi (x_1,x_2),\phi (x_1’,x_2’)>=<(z_1,z_2,z_3),(z_1’,z_2’,z_3’)>=<(x_1^2,\sqrt{2}x_1x_2,x_2^2),({x}_1’^2,\sqrt{2}x_1’x_2’,{x}_2’^2)>=x_1^2x_1’^2+2x_1x_2x_1’x_2’+x_2^2x_2’^2=(x_1x_1’+x_2x_2’)^2=(<a_1,a_2’>)^2=k(a_1,a_2)$ 为什么不用映射函数$\phi (x)$,而用他们的内积形式$K(x,z)$,即Kernel函数?因为$(x,z)$一起出现的时候,$K(x,z)=\phi (x) \cdot \phi (z)$有许多固定的形式可以调用,而不必求解或者关心$\phi (x)$的具体形式,这大大简化了求解。 核函数的用法 两点之间的距离 \begin{align*} \left \| \phi (x)- \phi (x')\right \|^2 &=(\phi (x)- \phi (x'))^T(\phi (x)- \phi (x'))\\ &=\phi (x)^T\phi (x)-2\phi (x)^T\phi (x')+\phi (x')^T\phi (x')\\ &=-2]]></content>
<categories>
<category>数学基础</category>
<category>高数</category>
</categories>
<tags>
<tag>模型</tag>
<tag>高数</tag>
</tags>
</entry>
<entry>
<title><![CDATA[C++重载运算符和重载函数]]></title>
<url>%2F2017%2F08%2F29%2F%E7%BC%96%E7%A8%8B%E8%AF%AD%E8%A8%80%2Fc%E8%AF%AD%E8%A8%80%2FC-%E9%87%8D%E8%BD%BD%E8%BF%90%E7%AE%97%E7%AC%A6%E5%92%8C%E9%87%8D%E8%BD%BD%E5%87%BD%E6%95%B0%2F</url>
<content type="text"><![CDATA[C++ 允许在同一作用域中的某个函数和运算符指定多个定义,分别称为函数重载和运算符重载。 重载声明是指一个与之前已经在该作用域内声明过的函数或方法具有相同名称的声明,但是它们的参数列表和定义(实现)不相同。 当您调用一个重载函数或重载运算符时,编译器通过把您所使用的参数类型与定义中的参数类型进行比较,决定选用最合适的定义。选择最合适的重载函数或重载运算符的过程,称为重载决策。 C++ 中的函数重载在同一个作用域内,可以声明几个功能类似的同名函数,但是这些同名函数的形式参数(指参数的个数、类型或者顺序)必须不同。您不能仅通过返回类型的不同来重载函数。123456789101112131415161718192021222324252627282930313233#include <iostream>using namespace std;class printData{ public: void print(int i) { cout << "整数为: " << i << endl; } void print(double f) { cout << "浮点数为: " << f << endl; } void print(char c[]) { cout << "字符串为: " << c << endl; }};int main(void){ printData pd; // 输出整数 pd.print(5); // 输出浮点数 pd.print(500.263); // 输出字符串 char c[] = "Hello C++"; pd.print(c); return 0;} C++ 中的运算符重载重载的运算符是带有特殊名称的函数,函数名是由关键字 operator 和其后要重载的运算符符号构成的。与其他函数一样,重载运算符有一个返回类型和一个参数列表。 一元运算符12345678910111213141516171819202122232425262728293031323334353637383940414243444546#include <iostream>using namespace std;class Distance{private: int feet; // 0 到无穷 int inches; // 0 到 12public: // 所需的构造函数 Distance(){ feet = 0; inches = 0; } Distance(int f, int i){ feet = f; inches = i; } // 显示距离的方法 void displayDistance() { cout << "F: " << feet << " I:" << inches << endl; } // 重载负运算符( - ) Distance operator- () { feet = -feet; inches = -inches; return Distance(feet, inches); }};int main(){ Distance D1(11, 10), D2(-5, 11); D1.displayDistance(); D2.displayDistance(); -D1; // 取相反数 D1.displayDistance(); // 距离 D1 -D2; // 取相反数 D2.displayDistance(); // 距离 D2 return 0;} 输出1234F: 11 I:10F: -5 I:11F: -11 I:-10F: 5 I:-11 二元运算符1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374#include <iostream>using namespace std;class Box{ public: double getVolume(void) { return length * breadth * height; } void setLength( double len ) { length = len; } void setBreadth( double bre ) { breadth = bre; } void setHeight( double hei ) { height = hei; } // 重载 + 运算符,用于把两个 Box 对象相加 Box operator+(const Box& b) { Box box; box.length = this->length + b.length; box.breadth = this->breadth + b.breadth; box.height = this->height + b.height; return box; } private: double length; // 长度 double breadth; // 宽度 double height; // 高度};// 程序的主函数int main( ){ Box Box1; // 声明 Box1,类型为 Box Box Box2; // 声明 Box2,类型为 Box Box Box3; // 声明 Box3,类型为 Box double volume = 0.0; // 把体积存储在该变量中 // Box1 详述 Box1.setLength(6.0); Box1.setBreadth(7.0); Box1.setHeight(5.0); // Box2 详述 Box2.setLength(12.0); Box2.setBreadth(13.0); Box2.setHeight(10.0); // Box1 的体积 volume = Box1.getVolume(); cout << "Volume of Box1 : " << volume <<endl; // Box2 的体积 volume = Box2.getVolume(); cout << "Volume of Box2 : " << volume <<endl; // 把两个对象相加,得到 Box3 Box3 = Box1 + Box2; // Box3 的体积 volume = Box3.getVolume(); cout << "Volume of Box3 : " << volume <<endl; return 0;} 关系运算符重载12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758#include <iostream>using namespace std;class Distance{ private: int feet; // 0 到无穷 int inches; // 0 到 12 public: // 所需的构造函数 Distance(){ feet = 0; inches = 0; } Distance(int f, int i){ feet = f; inches = i; } // 显示距离的方法 void displayDistance() { cout << "F: " << feet << " I:" << inches <<endl; } // 重载负运算符( - ) Distance operator- () { feet = -feet; inches = -inches; return Distance(feet, inches); } // 重载小于运算符( < ) bool operator <(const Distance& d) { if(feet < d.feet) { return true; } if(feet == d.feet && inches < d.inches) { return true; } return false; }};int main(){ Distance D1(11, 10), D2(5, 11); if( D1 < D2 ) { cout << "D1 is less than D2 " << endl; } else { cout << "D2 is less than D1 " << endl; } return 0;} C++ 输入/输出运算符重载12345678910111213141516171819202122232425262728293031323334353637383940414243#include <iostream>using namespace std;class Distance{private: int feet; // 0 到无穷 int inches; // 0 到 12public: // 所需的构造函数 Distance(){ feet = 0; inches = 0; } Distance(int f, int i){ feet = f; inches = i; } friend ostream &operator<<(ostream &output, const Distance &D) { output << "F : " << D.feet << " I : " << D.inches; return output; } friend istream &operator>>(istream &input, Distance &D) { input >> D.feet >> D.inches; return input; }};int main(){ Distance D1(11, 10), D2(5, 11), D3; cout << "Enter the value of object : " << endl; cin >> D3; cout << "First Distance : " << D1 << endl; cout << "Second Distance :" << D2 << endl; cout << "Third Distance :" << D3 << endl; return 0;} 可重载运算符 类别 运算符 双目算术运算符 + (加),-(减),*(乘),/(除),% (取模) 关系运算符 ==(等于),!= (不等于),< (小于),> (大于>,<=(小于等于),>=(大于等于) 逻辑运算符 ||(逻辑或),&&(逻辑与),|(逻辑非) 单目运算符 + (正),-(负),*(指针),&(取地址) 自增自减运算符 ++(自增),—(自减) 位运算符 | (按位或),& (按位与),~(按位取反),^(按位异或),,<< (左移),>>(右移) 赋值运算符 =, +=, -=, *=, /= , % = , &=, |=, ^=, <<=, >>= 空间申请与释放 new, delete, new[ ] , delete[] 其他运算符 ()(函数调用),->(成员访问),,(逗号), 参考]]></content>
<categories>
<category>编程语言</category>
<category>C++</category>
</categories>
<tags>
<tag>c语言</tag>
<tag>指针</tag>
</tags>
</entry>
<entry>
<title><![CDATA[C++继承]]></title>
<url>%2F2017%2F08%2F29%2F%E7%BC%96%E7%A8%8B%E8%AF%AD%E8%A8%80%2Fc%E8%AF%AD%E8%A8%80%2FC-%E7%BB%A7%E6%89%BF%2F</url>
<content type="text"><![CDATA[面向对象程序设计中最重要的一个概念是继承。继承允许我们依据另一个类来定义一个类,这使得创建和维护一个应用程序变得更容易。这样做,也达到了重用代码功能和提高执行时间的效果。 当创建一个类时,您不需要重新编写新的数据成员和成员函数,只需指定新建的类继承了一个已有的类的成员即可。这个已有的类称为基类,新建的类称为派生类。 父类&子类1class derived-class: access-specifier base-class 其中,访问修饰符 access-specifier 是 public、protected 或 private 其中的一个,base-class 是之前定义过的某个类的名称。如果未使用访问修饰符 access-specifier,则默认为 private。 继承模式 描述 public 继承 基类 public 成员,protected 成员,private 成员的访问属性在派生类中分别变成:public, protected, private protected 继承 基类 public 成员,protected 成员,private 成员的访问属性在派生类中分别变成:protected, protected, private private 继承 基类 public 成员,protected 成员,private 成员的访问属性在派生类中分别变成:private, private, private 1234567891011121314151617181920212223242526272829303132333435363738394041using namespace std;// 基类class Shape{ public: void setWidth(int w) { width = w; } void setHeight(int h) { height = h; } protected: int width; int height;};// 派生类class Rectangle: public Shape{ public: int getArea() { return (width * height); }};int main(void){ Rectangle Rect; Rect.setWidth(5); Rect.setHeight(7); // 输出对象的面积 cout << "Total area: " << Rect.getArea() << endl; return 0;} 输出1Total area: 35 访问控制和继承派生类可以访问基类中所有的非私有成员。因此基类成员如果不想被派生类的成员函数访问,则应在基类中声明为 private。我们可以根据访问权限总结出不同的访问类型,如下所示: 访问 public protected private 同一个类 yes yes yes 派生类 yes yes no 外部的类 yes no no 一个派生类继承了所有的基类方法,但下列情况除外: 基类的构造函数、析构函数和拷贝构造函数。 基类的重载运算符。 基类的友元函数。 多继承多继承即一个子类可以有多个父类,它继承了多个父类的特性。C++ 类可以从多个类继承成员,语法如下: 1234class <派生类名>:<继承方式1><基类名1>,<继承方式2><基类名2>,…{<派生类类体>}; 其中,访问修饰符继承方式是 public、protected 或 private 其中的一个,用来修饰每个基类,各个基类之间用逗号分隔,如上所示。现在让我们一起看看下面的实例: 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859#include <iostream>using namespace std;// 基类 Shapeclass Shape{ public: void setWidth(int w) { width = w; } void setHeight(int h) { height = h; } protected: int width; int height;};// 基类 PaintCostclass PaintCost{ public: int getCost(int area) { return area * 70; }};// 派生类class Rectangle: public Shape, public PaintCost{ public: int getArea() { return (width * height); }};int main(void){ Rectangle Rect; int area; Rect.setWidth(5); Rect.setHeight(7); area = Rect.getArea(); // 输出对象的面积 cout << "Total area: " << Rect.getArea() << endl; // 输出总花费 cout << "Total paint cost: $" << Rect.getCost(area) << endl; return 0;}]]></content>
<categories>
<category>编程语言</category>
<category>C++</category>
</categories>
<tags>
<tag>c语言</tag>
<tag>继承</tag>
</tags>
</entry>
<entry>
<title><![CDATA[C++类和对象]]></title>
<url>%2F2017%2F08%2F29%2F%E7%BC%96%E7%A8%8B%E8%AF%AD%E8%A8%80%2Fc%E8%AF%AD%E8%A8%80%2FC-%E7%B1%BB%E5%92%8C%E5%AF%B9%E8%B1%A1%2F</url>
<content type="text"><![CDATA[C++ 类定义类由内部成员(属性与方法)组成,属性指变量,方法指行为。123456789101112131415161718192021222324252627282930313233343536373839404142434445464748#include<iostream>using namespace std;#include <iostream>using namespace std;class Box{//属性public: double length; // 长度 double breadth; // 宽度 double height; // 高度 double volume() { return height * length * breadth; }};//方法int main(){ Box Box1; // 声明 Box1,类型为 Box Box Box2; // 声明 Box2,类型为 Box double volume = 0.0; // 用于存储体积 // box 1 详述 Box1.height = 5.0; Box1.length = 6.0; Box1.breadth = 7.0; // box 2 详述 Box2.height = 10.0; Box2.length = 12.0; Box2.breadth = 13.0; // box 1 的体积 volume = Box1.volume(); cout << "Box1 的体积:" << volume << endl; // box 2 的体积 volume = Box2.volume(); cout << "Box2 的体积:" << volume << endl; return 0;} C++ 类访问修饰符数据封装是面向对象编程的一个重要特点,它防止函数直接访问类类型的内部成员(属性和方法)。 修饰符 描述 public 公有成员在程序中类的外部是可访问的。您可以不使用任何成员函数来设置和获取公有变量的值。 private 私有成员变量或函数在类的外部是不可访问的,甚至是不可查看的。只有类和友元函数可以访问私有成员。默认情况下,类的所有成员都是私有的。 protected 保护成员变量或函数与私有成员十分相似,但有一点不同,保护成员在派生类(即子类)中是可访问的。 123456789101112131415class Base { public: // 公有成员 protected: // 受保护成员 private: // 私有成员}; 公有(public)成员12345678910111213141516171819202122232425262728293031323334353637#include <iostream>using namespace std;class Line{ public: double length; void setLength( double len ); double getLength( void );};// 成员函数定义double Line::getLength(void){ return length ;}void Line::setLength( double len ){ length = len;}// 程序的主函数int main( ){ Line line; // 设置长度 line.setLength(6.0); cout << "Length of line : " << line.getLength() <<endl; // 不使用成员函数设置长度 line.length = 10.0; // OK: 因为 length 是公有的 cout << "Length of line : " << line.length <<endl; return 0;} 私有(private)成员123456789101112131415161718192021222324252627282930313233343536373839404142#include <iostream>using namespace std;class Box{ public: double length; void setWidth( double wid ); double getWidth( void ); private: double width;};// 成员函数定义double Box::getWidth(void){ return width ;}void Box::setWidth( double wid ){ width = wid;}// 程序的主函数int main(){ Box box; // 不使用成员函数设置长度 box.length = 10.0; // OK: 因为 length 是公有的 cout << "Length of box : " << box.length <<endl; // 不使用成员函数设置宽度 // box.width = 10.0; // Error: 因为 width 是私有的 box.setWidth(10.0); // 使用成员函数设置宽度 cout << "Width of box : " << box.getWidth() <<endl; return 0;} 保护(protected)成员保护成员变量或函数与私有成员十分相似,但有一点不同,保护成员在派生类(即子类)中是可访问的。 在下一个章节中,您将学习到派生类和继承的知识。现在您可以看到下面的实例中,我们从父类 Box 派生了一个子类 smallBox。下面的实例与前面的实例类似,在这里 width 成员可被派生类 smallBox 的任何成员函数访问。 1234567891011121314151617181920212223242526272829303132333435363738#include <iostream>using namespace std;class Box{ protected: double width;};class SmallBox:Box // SmallBox 是派生类{ public: void setSmallWidth( double wid ); double getSmallWidth( void );};// 子类的成员函数double SmallBox::getSmallWidth(void){ return width ;}void SmallBox::setSmallWidth( double wid ){ width = wid;}// 程序的主函数int main( ){ SmallBox box; // 使用成员函数设置宽度 box.setSmallWidth(5.0); cout << "Width of box : "<< box.getSmallWidth() << endl; return 0;} 继承的修饰符有public, protected, private三种继承方式,它们相应地改变了基类成员的访问属性。但无论哪种继承方式,上面两点都没有改变 private 成员只能被本类成员(类内)和友元访问,不能被派生类访问; protected 成员可以被派生类访问。 继承模式 描述 public 继承 基类 public 成员,protected 成员,private 成员的访问属性在派生类中分别变成:public, protected, private protected 继承 基类 public 成员,protected 成员,private 成员的访问属性在派生类中分别变成:protected, protected, private private 继承 基类 public 成员,protected 成员,private 成员的访问属性在派生类中分别变成:private, private, private public 继承12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849#include<iostream>#include<assert.h>using namespace std;class A{public: int a; A(){ a1 = 1; a2 = 2; a3 = 3; a = 4; } void fun(){ cout << a << endl; //正确 cout << a1 << endl; //正确 cout << a2 << endl; //正确 cout << a3 << endl; //正确 }public: int a1;protected: int a2;private: int a3;};class B : public A{public: int a; B(int i){ A(); a = i; } void fun(){ cout << a << endl; //正确,public成员 cout << a1 << endl; //正确,基类的public成员,在派生类中仍是public成员。 cout << a2 << endl; //正确,基类的protected成员,在派生类中仍是protected可以被派生类访问。 cout << a3 << endl; //错误,基类的private成员不能被派生类访问。 }};int main(){ B b(10); cout << b.a << endl; cout << b.a1 << endl; //正确 cout << b.a2 << endl; //错误,类外不能访问protected成员 cout << b.a3 << endl; //错误,类外不能访问private成员 system("pause"); return 0;} protected 继承123456789101112131415161718192021222324252627282930313233343536373839404142434445464748#include<iostream>#include<assert.h>using namespace std;class A{public: int a; A(){ a1 = 1; a2 = 2; a3 = 3; a = 4; } void fun(){ cout << a << endl; //正确 cout << a1 << endl; //正确 cout << a2 << endl; //正确 cout << a3 << endl; //正确 }public: int a1;protected: int a2;private: int a3;};class B : protected A{public: int a; B(int i){ A(); a = i; } void fun(){ cout << a << endl; //正确,public成员。 cout << a1 << endl; //正确,基类的public成员,在派生类中变成了protected,可以被派生类访问。 cout << a2 << endl; //正确,基类的protected成员,在派生类中还是protected,可以被派生类访问。 cout << a3 << endl; //错误,基类的private成员不能被派生类访问。 }};int main(){ B b(10); cout << b.a << endl; //正确。public成员 cout << b.a1 << endl; //错误,protected成员不能在类外访问。 cout << b.a2 << endl; //错误,protected成员不能在类外访问。 cout << b.a3 << endl; //错误,private成员不能在类外访问。 system("pause"); return 0;} private 继承123456789101112131415161718192021222324252627282930313233343536373839404142434445464748#include<iostream>#include<assert.h>using namespace std;class A{public: int a; A(){ a1 = 1; a2 = 2; a3 = 3; a = 4; } void fun(){ cout << a << endl; //正确 cout << a1 << endl; //正确 cout << a2 << endl; //正确 cout << a3 << endl; //正确 }public: int a1;protected: int a2;private: int a3;};class B : private A{public: int a; B(int i){ A(); a = i; } void fun(){ cout << a << endl; //正确,public成员。 cout << a1 << endl; //正确,基类public成员,在派生类中变成了private,可以被派生类访问。 cout << a2 << endl; //正确,基类的protected成员,在派生类中变成了private,可以被派生类访问。 cout << a3 << endl; //错误,基类的private成员不能被派生类访问。 }};int main(){ B b(10); cout << b.a << endl; //正确。public成员 cout << b.a1 << endl; //错误,private成员不能在类外访问。 cout << b.a2 << endl; //错误, private成员不能在类外访问。 cout << b.a3 << endl; //错误,private成员不能在类外访问。 system("pause"); return 0;} 构造函数类的构造函数是类的一种特殊的成员函数,它会在每次创建类的新对象时执行。 构造函数的名称与类的名称是完全相同的,并且不会返回任何类型,也不会返回 void。构造函数可用于为某些成员变量设置初始值。 1234567891011121314151617181920212223242526272829303132333435363738394041#include <iostream>using namespace std;class Line{ public: void setLength( double len ); double getLength( void ); Line(); // 这是构造函数 private: double length;};// 成员函数定义,包括构造函数Line::Line(void){ cout << "Object is being created" << endl;}void Line::setLength( double len ){ length = len;}double Line::getLength( void ){ return length;}// 程序的主函数int main( ){ Line line; // 设置长度 line.setLength(6.0); cout << "Length of line : " << line.getLength() <<endl; return 0;} 带参数的构造函数默认的构造函数没有任何参数,但如果需要,构造函数也可以带有参数。这样在创建对象时就会给对象赋初始值,如下面的例子所示:1234567891011121314151617181920212223242526272829303132333435363738394041424344#include <iostream>using namespace std;class Line{ public: void setLength( double len ); double getLength( void ); Line(double len); // 这是构造函数 private: double length;};// 成员函数定义,包括构造函数Line::Line( double len){ cout << "Object is being created, length = " << len << endl; length = len;}void Line::setLength( double len ){ length = len;}double Line::getLength( void ){ return length;}// 程序的主函数int main( ){ Line line(10.0); // 获取默认设置的长度 cout << "Length of line : " << line.getLength() <<endl; // 再次设置长度 line.setLength(6.0); cout << "Length of line : " << line.getLength() <<endl; return 0;} 类的析构函数类的析构函数是类的一种特殊的成员函数,它会在每次删除所创建的对象时执行。 析构函数的名称与类的名称是完全相同的,只是在前面加了个波浪号(~)作为前缀,它不会返回任何值,也不能带有任何参数。析构函数有助于在跳出程序(比如关闭文件、释放内存等)前释放资源。 12345678910111213141516171819202122232425262728293031323334353637383940414243444546#include <iostream>using namespace std;class Line{public: void setLength(double len); double getLength(void); Line(); // 这是构造函数声明 ~Line(); // 这是析构函数声明private: double length;};// 成员函数定义,包括构造函数Line::Line(void){ cout << "Object is being created" << endl;}Line::~Line(void){ cout << "Object is being deleted" << endl;}void Line::setLength(double len){ length = len;}double Line::getLength(void){ return length;}// 程序的主函数int main(){ Line line; // 设置长度 line.setLength(6.0); cout << "Length of line : " << line.getLength() << endl; return 0; //结束后,对象销毁,此时返回析构函数} C++ 拷贝构造函数拷贝构造函数是一种特殊的构造函数,它在创建对象时,是使用同一类中之前创建的对象来初始化新创建的对象。拷贝构造函数通常用于: 通过使用另一个同类型的对象来初始化新创建的对象。 复制对象把它作为参数传递给函数。 复制对象,并从函数返回这个对象 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859#include <iostream>using namespace std;class Line{ public: int getLength( void ); Line( int len ); // 简单的构造函数 Line( const Line &obj); // 拷贝构造函数 ~Line(); // 析构函数 private: int *ptr;};// 成员函数定义,包括构造函数Line::Line(int len){ cout << "调用构造函数" << endl; // 为指针分配内存 ptr = new int; *ptr = len;}Line::Line(const Line &obj){ cout << "调用拷贝构造函数并为指针 ptr 分配内存" << endl; ptr = new int; *ptr = *obj.ptr; // 拷贝值}Line::~Line(void){ cout << "释放内存" << endl; delete ptr;}int Line::getLength( void ){ return *ptr;}void display(Line obj){ cout << "line 大小 : " << obj.getLength() <<endl;}// 程序的主函数int main( ){ Line line1(10); Line line2 = line1; // 这里也调用了拷贝构造函数 display(line1); display(line2); return 0;} C++ 友元函数类的友元函数是定义在类外部,但有权访问类的所有私有(private)成员和保护(protected)成员。尽管友元函数的原型有在类的定义中出现过,但是友元函数并不是成员函数。 友元可以是一个函数,该函数被称为友元函数;友元也可以是一个类,该类被称为友元类,在这种情况下,整个类及其所有成员都是友元。如果要声明函数为一个类的友元,需要在类定义中该函数原型前使用关键字 friend,如下所示:1234567891011121314151617181920212223242526272829303132333435363738#include <iostream>using namespace std;class Box{ double width;public: friend void printWidth( Box box ); void setWidth( double wid );};// 成员函数定义void Box::setWidth( double wid ){ width = wid;}// 请注意:printWidth() 不是任何类的成员函数void printWidth( Box box ){ /* 因为 printWidth() 是 Box 的友元,它可以直接访问该类的任何成员 */ cout << "Width of box : " << box.width <<endl;}// 程序的主函数int main( ){ Box box; // 使用成员函数设置宽度 box.setWidth(10.0); // 使用友元函数输出宽度 printWidth( box ); return 0;} C++内联函数调用函数比求解等价表达式要慢得多。在大多数的机器上,调用函数都要做很多工作:调用前要先保存寄存器,并在返回时恢复,复制实参,程序还必须转向一个新位置执行C++中支持内联函数,其目的是为了提高函数的执行效率,用关键字 inline 放在函数定义(注意是定义而非声明,下文继续讲到)的前面即可将函数指定为内联函数。内联函数是通常与类一起使用。如果一个函数是内联的,那么在编译时,编译器会把该函数的代码副本放置在每个调用该函数的地方。对内联函数进行任何修改,都需要重新编译函数的所有客户端,因为编译器需要重新更换一次所有的代码,否则将会继续使用旧的函数。如果想把一个函数定义为内联函数,则需要在函数名前面放置关键字 inline,在调用函数之前需要对函数进行定义。 123456789101112131415161718#include <iostream>using namespace std;inline int Max(int x, int y){ return (x > y)? x : y;}// 程序的主函数int main( ){ cout << "Max (20,10): " << Max(20,10) << endl; cout << "Max (0,200): " << Max(0,200) << endl; cout << "Max (100,1010): " << Max(100,1010) << endl; return 0;} C++ this 指针在 C++ 中,每一个对象都能通过 this 指针来访问自己的地址。this 指针是所有成员函数的隐含参数。因此,在成员函数内部,它可以用来指向调用对象。 友元函数没有 this 指针,因为友元不是类的成员。只有成员函数才有 this 指针。1234567891011121314151617181920212223242526272829303132333435363738394041424344#include <iostream>using namespace std;class Box{ public: // 构造函数定义 Box(double l=2.0, double b=2.0, double h=2.0) { cout <<"Constructor called." << endl; length = l; breadth = b; height = h; } double Volume() { return length * breadth * height; } int compare(Box box) { return this->Volume() > box.Volume(); } private: double length; // Length of a box double breadth; // Breadth of a box double height; // Height of a box};int main(void){ Box Box1(3.3, 1.2, 1.5); // Declare box1 Box Box2(8.5, 6.0, 2.0); // Declare box2 if(Box1.compare(Box2)) { cout << "Box2 is smaller than Box1" <<endl; } else { cout << "Box2 is equal to or larger than Box1" <<endl; } return 0;} C++ 指向类的指针一个指向 C++ 类的指针与指向结构的指针类似,访问指向类的指针的成员,需要使用成员访问运算符 ->,就像访问指向结构的指针一样。123456789101112131415161718192021222324252627282930313233343536373839404142434445#include <iostream>using namespace std;class Box{ public: // 构造函数定义 Box(double l=2.0, double b=2.0, double h=2.0) { cout <<"Constructor called." << endl; length = l; breadth = b; height = h; } double Volume() { return length * breadth * height; } private: double length; // Length of a box double breadth; // Breadth of a box double height; // Height of a box};int main(void){ Box Box1(3.3, 1.2, 1.5); // Declare box1 Box Box2(8.5, 6.0, 2.0); // Declare box2 Box *ptrBox; // Declare pointer to a class. // 保存第一个对象的地址 ptrBox = &Box1; // 现在尝试使用成员访问运算符来访问成员 cout << "Volume of Box1: " << ptrBox->Volume() << endl; // 保存第二个对象的地址 ptrBox = &Box2; // 现在尝试使用成员访问运算符来访问成员 cout << "Volume of Box2: " << ptrBox->Volume() << endl; return 0;} C++类的静态成员我们可以使用 static 关键字来把类成员定义为静态的。当我们声明类的成员为静态时,这意味着无论创建多少个类的对象,静态成员都只有一个副本。 静态成员在类的所有对象中是共享的。如果不存在其他的初始化语句,在创建第一个对象时,所有的静态数据都会被初始化为零。我们不能把静态成员的初始化放置在类的定义中,但是可以在类的外部通过使用范围解析运算符 :: 来重新声明静态变量从而对它进行初始化,如下面的实例所示。 1234567891011121314151617181920212223242526272829303132333435363738394041#include <iostream>using namespace std;class Box{ public: static int objectCount; // 构造函数定义 Box(double l=2.0, double b=2.0, double h=2.0) { cout <<"Constructor called." << endl; length = l; breadth = b; height = h; // 每次创建对象时增加 1 objectCount++; } double Volume() { return length * breadth * height; } private: double length; // 长度 double breadth; // 宽度 double height; // 高度};// 初始化类 Box 的静态成员int Box::objectCount = 0;int main(void){ Box Box1(3.3, 1.2, 1.5); // 声明 box1 Box Box2(8.5, 6.0, 2.0); // 声明 box2 // 输出对象的总数 cout << "Total objects: " << Box::objectCount << endl; return 0;} 输出123Constructor called.Constructor called.Total objects: 2 静态成员函数 如果把函数成员声明为静态的,就可以把函数与类的任何特定对象独立开来。静态成员函数即使在类对象不存在的情况下也能被调用,静态函数只要使用类名加范围解析运算符 :: 就可以访问。 静态成员函数只能访问静态成员数据、其他静态成员函数和类外部的其他函数。 静态成员函数有一个类范围,他们不能访问类的 this 指针。您可以使用静态成员函数来判断类的某些对象是否已被创建。 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849#include <iostream>using namespace std;class Box{ public: static int objectCount; // 构造函数定义 Box(double l=2.0, double b=2.0, double h=2.0) { cout <<"Constructor called." << endl; length = l; breadth = b; height = h; // 每次创建对象时增加 1 objectCount++; } double Volume() { return length * breadth * height; } static int getCount() { return objectCount; } private: double length; // 长度 double breadth; // 宽度 double height; // 高度};// 初始化类 Box 的静态成员int Box::objectCount = 0;int main(void){ // 在创建对象之前输出对象的总数 cout << "Inital Stage Count: " << Box::getCount() << endl; Box Box1(3.3, 1.2, 1.5); // 声明 box1 Box Box2(8.5, 6.0, 2.0); // 声明 box2 // 在创建对象之后输出对象的总数 cout << "Final Stage Count: " << Box::getCount() << endl; return 0;} 输出1234Inital Stage Count: 0Constructor called.Constructor called.Final Stage Count: 2]]></content>
<categories>
<category>编程语言</category>
<category>C++</category>
</categories>
<tags>
<tag>c语言</tag>
<tag>类和对象</tag>
</tags>
</entry>
<entry>
<title><![CDATA[C++结构体]]></title>
<url>%2F2017%2F08%2F29%2F%E7%BC%96%E7%A8%8B%E8%AF%AD%E8%A8%80%2Fc%E8%AF%AD%E8%A8%80%2FC-%E7%BB%93%E6%9E%84%E4%BD%93%2F</url>
<content type="text"><![CDATA[结构体声明1234567891011121314struct stu{ char *name; //姓名 int num; //学号 int age; //年龄 char group; //所在学习小组 float score; //成绩} stu1, stu2; //stu1与stu2为结构体变量//给结构体成员赋值 stu1.name = "Tom"; stu1.num = 12; stu1.age = 18; stu1.group = 'A'; stu1.score = 136.5; 结构体数组1234567891011121314151617181920212223242526272829#include <stdio.h>struct{ char *name; //姓名 int num; //学号 int age; //年龄 char group; //所在小组 float score; //成绩}class1[] = { { "Li ping", 5, 18, 'C', 145.0 }, { "Zhang ping", 4, 19, 'A', 130.5 }, { "He fang", 1, 18, 'A', 148.5 }, { "Cheng ling", 2, 17, 'F', 139.0 }, { "Wang ming", 3, 17, 'B', 144.5 }};int main(){ int i, num_140 = 0; float sum = 0; for (i = 0; i<5; i++){ sum += class1[i].score; if (class1[i].score < 140) num_140++; } printf("sum=%.2f\naverage=%.2f\nnum_140=%d\n", sum, sum / 5, num_140); for (i = 0; i<5; i++){ cout << "name:" << class1[i].name << endl; } return 0;} 输出12345678sum=707.50average=141.50num_140=2name:Li pingname:Zhang pingname:He fangname:Cheng lingname:Wang ming 结构体枚举数据类型1enum week{ Mon, Tues, Wed, Thurs, Fri, Sat, Sun }; //枚举值默认从 0 开始,往后逐个加 1(递增);也就是说,week 中的 Mon、Tues ...... Sun 对应的值分别为 0、1 ...... 6。 结构体作为函数参数123456789101112131415struct Books{ char title[50]; char author[50]; char subject[100]; int book_id;};void printBook( struct Books book ){ printf( "Book title : %s\n", book.title); printf( "Book author : %s\n", book.author); printf( "Book subject : %s\n", book.subject); printf( "Book book_id : %d\n", book.book_id);} 指向结构的指针为了使用指向该结构的指针访问结构的成员,您必须使用 -> 运算符123struct Books *struct_pointer;struct_pointer = &Book1;struct_pointer->title; //为了使用指向该结构的指针访问结构的成员,您必须使用 -> 运算符]]></content>
<categories>
<category>编程语言</category>
<category>C++</category>
</categories>
<tags>
<tag>c语言</tag>
<tag>结构体</tag>
</tags>
</entry>
<entry>
<title><![CDATA[C++引用]]></title>
<url>%2F2017%2F08%2F29%2F%E7%BC%96%E7%A8%8B%E8%AF%AD%E8%A8%80%2Fc%E8%AF%AD%E8%A8%80%2FC-%E5%BC%95%E7%94%A8%2F</url>
<content type="text"><![CDATA[引用变量是一个别名,也就是说,它是某个已存在变量的另一个名字。一旦把引用初始化为某个变量,就可以使用该引用名称或变量名称来指向变量。 C++ 引用 vs 指针引用很容易与指针混淆,它们之间有三个主要的不同: 不存在空引用。引用必须连接到一块合法的内存。 一旦引用被初始化为一个对象,就不能被指向到另一个对象。指针可以在任何时候指向到另一个对象。 引用必须在创建时被初始化。指针可以在任何时间被初始化。 C++ 中创建引用试想变量名称是变量附属在内存位置中的标签,您可以把引用当成是变量附属在内存位置中的第二个标签。因此,您可以通过原始变量名称或引用来访问变量的内容。例如: 123456789101112131415161718192021222324#include <iostream>using namespace std;int main (){ // 声明简单的变量 int i; double d; // 声明引用变量 int& r = i; double& s = d; i = 5; cout << "Value of i : " << i << endl; cout << "Value of i reference : " << r << endl; d = 11.7; cout << "Value of d : " << d << endl; cout << "Value of d reference : " << s << endl; return 0;} 输出1234Value of i : 5Value of i reference : 5Value of d : 11.7Value of d reference : 11.7]]></content>
<categories>
<category>编程语言</category>
<category>C++</category>
</categories>
<tags>
<tag>c语言</tag>
<tag>引用</tag>
</tags>
</entry>
<entry>
<title><![CDATA[C++指针]]></title>
<url>%2F2017%2F08%2F29%2F%E7%BC%96%E7%A8%8B%E8%AF%AD%E8%A8%80%2Fc%E8%AF%AD%E8%A8%80%2FC-%E6%8C%87%E9%92%88%2F</url>
<content type="text"><![CDATA[概括12345int *p //声明指针变量int i //声明变量int temp //声明变量tempp=&i //&为取地址符,获取变量i的地址temp = *p //获取地址p的值 指针的声明12345int *ip; /* 一个整型的指针 */double *dp; /* 一个 double 型的指针 */float *fp; /* 一个浮点型的指针 */char *ch; /* 一个字符型的指针 */int *ptr = NULL; /* 声明空的指针 */ 指针的算术运算指针是一个用数值表示的地址。因此,您可以对指针执行算术运算。可以对指针进行四种算术运算:++、—、+、-。假设 ptr 是一个指向地址 1000 的整型指针,是一个 32 位的整数,让我们对该指针执行下列的算术运算:1234567891011121314151617181920#include <iostream>using namespace std;int main(){ // int *ptr; int a; ptr = &a; // 输出返回值 cout << "&a是:" << &a << endl; cout << "ptr:" << ptr << endl; cout << "ptr++:" << ptr++ << endl; cout << "ptr:" << ptr << endl; cout << "++ptr:" << ++ptr << endl; return 0;} 在执行完上述的运算之后,ptr 将指向位置 1004,因为 ptr 每增加一次,它都将指向下一个整数位置,即当前位置往后移 4 个字节。这个运算会在不影响内存位置中实际值的情况下,移动指针到下一个内存位置。如果 ptr 指向一个地址为 1000 的字符,上面的运算会导致指针指向位置 1001,因为下一个字符位置是在 1001。12345&a是:007FFDB0ptr:007FFDB0ptr++:007FFDB0ptr:007FFDB4++ptr:007FFDB8 指针数组一个数组,数组的数据类型为指针 示例一12345678910111213141516171819202122232425262728293031#include <stdio.h>const int MAX = 3;int main(){ int var[] = { 10, 100, 200 }; //数组 int i, *ptr[MAX]; //指针数组 for (i = 0; i < MAX; i++) { ptr[i] = &var[i]; /* 将var元素的地址赋值给对应的ptr */ } for (i = 0; i < MAX; i++) { printf("Value of *ptr[%d] = %d\n", i, *ptr[i]); //打印*ptr的值,ptr存储的地址所指向的值 } for (i = 0; i < MAX; i++) { printf("Value of var[%d] = %d\n", i, var[i]); //打印var数组的值 } for (i = 0; i < MAX; i++) { printf("Value of ptr[%d] = %d\n", i, ptr[i]); //打印ptr的值,ptr存储的地址 } return 0;} 输出123456789Value of *ptr[0] = 10Value of *ptr[1] = 100Value of *ptr[2] = 200Value of var[0] = 10Value of var[1] = 100Value of var[2] = 200Value of ptr[0] = 19921128Value of ptr[1] = 19921132Value of ptr[2] = 19921136 示例二1234567891011121314151617181920#include <stdio.h>const int MAX = 4;int main (){ char *names[] = { "Zara Ali", "Hina Ali", "Nuha Ali", "Sara Ali", }; int i = 0; for ( i = 0; i < MAX; i++) { printf("Value of names[%d] = %s\n", i, names[i] ); } return 0;} 输出1234Value of names[0] = Zara AliValue of names[1] = Hina AliValue of names[2] = Nuha AliValue of names[3] = Sara Ali 指针的指针——二级指针二级指针作为函数参数的作用:在函数外部定义一个指针p,在函数内给指针赋值,函数结束后对指针p生效,那么我们就需要二级指针。 1234567891011121314151617181920212223#include<iostream>using namespace std;int a = 10;int b = 100;int *q;void func(int *p){ cout << "func:&p(p的地址)=" << &p << ",p(p指向的地址)=" << p << endl; //note:3 p = &b; cout << "func:&p(p的地址)=" << &p << ",p(p指向的地址)=" << p << endl; //note:4}int main(){ cout << "&a(a的地址)=" << &a << ",&b(b的地址)=" << &b << ",&q(q的地址)=" << &q << endl; //note:1 q = &a; //q指向的地址为&a ,q=&a;q 指向地址的取值 即a; &q为指针q的取值 cout << "*q(指向地址的取值)=" << *q << ",q(指向的地址)=" << q << ",&q(q的地址)=" << &q << endl; //note:2 func(q); //p与q指向同一个地址,但是p与q为不同的指针,所以p=q但是&p不封于&q cout << "*q=" << *q << ",q=" << q << ",&q=" << &q << endl; //note:5 system("pause"); return 0;} 输出12345&a(a的地址)=00C3F000,&b(b的地址)=00C3F004,&q(q的地址)=00C3F364*q(指向地址的取值)=10,q(指向的地址)=00C3F000,&q(q的地址)=00C3F364func:&p(p的地址)=008FFC80,p(p指向的地址)=00C3F000func:&p(p的地址)=008FFC80,p(p指向的地址)=00C3F004*q=10,q=00C3F000,&q=00C3F364 传递指针给函数12345678910111213141516171819202122232425262728293031323334#include <stdio.h>/* 函数声明 */double getAverage(int *arr, int size);int main (){ /* 带有 5 个元素的整型数组 */ int balance[5] = {1000, 2, 3, 17, 50}; double avg; /* 传递一个指向数组的指针作为参数 */ avg = getAverage( balance, 5 ) ; /* 输出返回值 */ printf("Average value is: %f\n", avg ); return 0;}double getAverage(int *arr, int size){ int i, sum = 0; double avg; for (i = 0; i < size; ++i) { sum += arr[i]; } avg = (double)sum / size; return avg;} 从函数返回指针函数返回数组123456789101112131415161718192021222324252627282930313233343536#include <stdio.h>#include <time.h>#include <stdlib.h>/* 要生成和返回随机数的函数 */int * getRandom( ){ static int r[10]; int i; /* 设置种子 */ srand( (unsigned)time( NULL ) ); for ( i = 0; i < 10; ++i) { r[i] = rand(); printf("%d\n", r[i] ); } return r;}/* 要调用上面定义函数的主函数 */int main (){ /* 一个指向整数的指针 */ int *p; int i; p = getRandom(); for ( i = 0; i < 10; i++ ) { printf("*(p + [%d]) : %d\n", i, *(p + i) ); } return 0;} 输出123456789101112131415161718192015231980531187214107110830097843049495914213012769309710841232504841069321401604461820149169022*(p + [0]) : 1523198053*(p + [1]) : 1187214107*(p + [2]) : 1108300978*(p + [3]) : 430494959*(p + [4]) : 1421301276*(p + [5]) : 930971084*(p + [6]) : 123250484*(p + [7]) : 106932140*(p + [8]) : 1604461820*(p + [9]) : 149169022 函数指针通常我们说的指针变量是指向一个整型、字符型或数组等变量,而函数指针是指向函数。12345678910111213141516171819202122232425262728293031323334353637383940414243#include<iostream>using namespace std;/* 要生成和返回随机数的函数 */int *getRandom(){ static int r[10]; int i; /* 设置种子 */ srand((unsigned)time(NULL)); for (i = 0; i < 10; ++i) { r[i] = rand(); printf("%d\n", r[i]); } return r;}#include <stdio.h>int max(int x, int y){ return x > y ? x : y; //如果x>y,返回x否则返回y}int main(void){ /* p 是函数指针 */ int(*p)(int, int) = &max; // &可以省略 int a, b, c, d; cout << "请输入三个数字:" <<endl; cin >> a >> b >> c; /* 与直接调用函数等价,d = max(max(a, b), c) */ d = p(p(a, b), c); printf("最大的数字是: %d\n", d); return 0;} 回调函数函数指针作为某个函数的参数,函数指针变量可以作为某个函数的参数来使用的,回调函数就是一个通过函数指针调用的函数。简单讲:回调函数是由别人的函数执行时调用你实现的函数。1234567891011121314151617181920212223242526#include<iostream>using namespace std;// 回调函数void populate_array(int *array, size_t arraySize, int(*getNextValue)(void)){ for (size_t i = 0; i<arraySize; i++) array[i] = getNextValue();}// 获取随机值int getNextRandomValue(void){ return rand();}int main(void){ int myarray[10]; populate_array(myarray, 10, getNextRandomValue); for (int i = 0; i < 10; i++) { cout << myarray[i] << endl; } return 0;}]]></content>
<categories>
<category>编程语言</category>
<category>C++</category>
</categories>
<tags>
<tag>c语言</tag>
<tag>指针</tag>
</tags>
</entry>
<entry>
<title><![CDATA[C++数组]]></title>
<url>%2F2017%2F08%2F29%2F%E7%BC%96%E7%A8%8B%E8%AF%AD%E8%A8%80%2Fc%E8%AF%AD%E8%A8%80%2FC-%E6%95%B0%E7%BB%84%2F</url>
<content type="text"><![CDATA[数组123type arrayName [ arraySize ];double balance[5] = {1000.0, 2.0, 3.4, 7.0, 50.0}; 多维数组12345678type name[size1][size2]...[sizeN];//二维数组int a[3][4] = { {0, 1, 2, 3} , /* 初始化索引号为 0 的行 */ {4, 5, 6, 7} , /* 初始化索引号为 1 的行 */ {8, 9, 10, 11} /* 初始化索引号为 2 的行 */}; 指向数组的指针数组名是一个指向数组中第一个元素的常量指针。balance 是一个指向 &balance[0] 的指针,即数组 balance 的第一个元素的地址。因此,下面的程序片段把 p 赋值为 balance 的第一个元素的地址:1234double *p;double balance[10];p = balance; 使用数组名作为常量指针是合法的,反之亦然。因此,*(balance + 4) 是一种访问 balance[4] 数据的合法方式。 传递数组给函数方式 1形式参数是一个指针:123456void myFunction(int *param){...} 方式 2形式参数是一个已定义大小的数组:123456void myFunction(int param[10]){...} 方式 3形式参数是一个未定义大小的数组:123456void myFunction(int param[]){...} 从函数返回数组C++ 不允许返回一个完整的数组作为函数的参数。但是,您可以通过指定不带索引的数组名来返回一个指向数组的指针。 如果您想要从函数返回一个一维数组,您必须声明一个返回指针的函数,如下123456int * myFunction(){...} 实例12345678910111213141516171819202122232425262728293031323334353637#include <iostream>#include <cstdlib>#include <ctime>using namespace std;// 要生成和返回随机数的函数int * getRandom( ){ static int r[10]; // 设置种子 srand( (unsigned)time( NULL ) ); for (int i = 0; i < 10; ++i) { r[i] = rand(); cout << r[i] << endl; } return r;}// 要调用上面定义函数的主函数int main (){ // 一个指向整数的指针 int *p; p = getRandom(); for ( int i = 0; i < 10; i++ ) { cout << "*(p + " << i << ") : "; cout << *(p + i) << endl; } return 0;}]]></content>
<categories>
<category>编程语言</category>
<category>C++</category>
</categories>
<tags>
<tag>c语言</tag>
<tag>变量作用域</tag>
</tags>
</entry>
<entry>
<title><![CDATA[C++数字]]></title>
<url>%2F2017%2F08%2F29%2F%E7%BC%96%E7%A8%8B%E8%AF%AD%E8%A8%80%2Fc%E8%AF%AD%E8%A8%80%2FC-%E6%95%B0%E5%AD%97%2F</url>
<content type="text"><![CDATA[数学运算为了利用这些函数,您需要引用数学头文件 。 序号 函数 & 描述 1 double cos(double);该函数返回弧度角(double 型)的余弦。 2 double sin(double);该函数返回弧度角(double 型)的正弦。 3 double tan(double);该函数返回弧度角(double 型)的正切。 4 double log(double);该函数返回参数的自然对数。 5 double pow(double, double);假设第一个参数为 x,第二个参数为 y,则该函数返回 x 的 y 次方。 6 double hypot(double, double);该函数返回两个参数的平方总和的平方根,也就是说,参数为一个直角三角形的两个直角边,函数会返回斜边的长度。 7 double sqrt(double);该函数返回参数的平方根。 8 int abs(int);该函数返回整数的绝对值。 9 double fabs(double);该函数返回任意一个十进制数的绝对值。 10 double floor(double);该函数返回一个小于或等于传入参数的最大整数。 C++ 随机数在许多情况下,需要生成随机数。关于随机数生成器,有两个相关的函数。一个是 rand(),该函数只返回一个伪随机数。生成随机数之前必须先调用 srand() 函数 1234567891011121314151617181920212223#include <iostream>#include <ctime>#include <cstdlib>using namespace std;int main (){ int i,j; // 设置种子 srand( (unsigned)time( NULL ) ); /* 生成 10 个随机数 */ for( i = 0; i < 10; i++ ) { // 生成实际的随机数 j= rand(); cout <<"随机数: " << j << endl; } return 0;}]]></content>
<categories>
<category>编程语言</category>
<category>C++</category>
</categories>
<tags>
<tag>c语言</tag>
<tag>数字</tag>
</tags>
</entry>
<entry>
<title><![CDATA[C++函数]]></title>
<url>%2F2017%2F08%2F29%2F%E7%BC%96%E7%A8%8B%E8%AF%AD%E8%A8%80%2Fc%E8%AF%AD%E8%A8%80%2FC-%E5%87%BD%E6%95%B0%2F</url>
<content type="text"><![CDATA[1234return_type function_name( parameter list ){ body of the function} 返回类型:一个函数可以返回一个值。return_type 是函数返回的值的数据类型。有些函数执行所需的操作而不返回值,在这种情况下,return_type 是关键字 void。 函数名称:这是函数的实际名称。函数名和参数列表一起构成了函数签名。 参数:参数就像是占位符。当函数被调用时,您向参数传递一个值,这个值被称为实际参数。参数列表包括函数参数的类型、顺序、数量。参数是可选的,也就是说,函数可能不包含参数。 函数主体:函数主体包含一组定义函数执行任务的语句 函数参数 调用类型 描述 传值调用 该方法把参数的实际值复制给函数的形式参数。在这种情况下,修改函数内的形式参数对实际参数没有影响。 指针调用 该方法把参数的地址复制给形式参数。在函数内,该地址用于访问调用中要用到的实际参数。这意味着,修改形式参数会影响实际参数。 引用调用 该方法把参数的引用复制给形式参数。在函数内,该引用用于访问调用中要用到的实际参数。这意味着,修改形式参数会影响实际参数。 传值调用1234567891011121314151617181920212223242526272829303132333435#include <iostream>using namespace std;// 函数声明void swap(int x, int y);int main (){ // 局部变量声明 int a = 100; int b = 200; cout << "交换前,a 的值:" << a << endl; cout << "交换前,b 的值:" << b << endl; // 调用函数来交换值 swap(a, b); cout << "交换后,a 的值:" << a << endl; cout << "交换后,b 的值:" << b << endl; return 0;}// 函数定义void swap(int x, int y){ int temp; temp = x; /* 保存 x 的值 */ x = y; /* 把 y 赋值给 x */ y = temp; /* 把 x 赋值给 y */ return;} 指针调用12345678910111213141516171819202122232425262728293031323334353637#include <iostream>using namespace std;// 函数声明void swap(int *x, int *y);int main (){ // 局部变量声明 int a = 100; int b = 200; cout << "交换前,a 的值:" << a << endl; cout << "交换前,b 的值:" << b << endl; /* 调用函数来交换值 * &a 表示指向 a 的指针,即变量 a 的地址 * &b 表示指向 b 的指针,即变量 b 的地址 */ swap(&a, &b); cout << "交换后,a 的值:" << a << endl; cout << "交换后,b 的值:" << b << endl; return 0;}// 函数定义void swap(int *x, int *y){ int temp; temp = *x; /* 保存地址 x 的值 */ *x = *y; /* 把 y 赋值给 x */ *y = temp; /* 把 x 赋值给 y */ return;} 引用调用向函数传递参数的引用调用方法,把引用的地址复制给形式参数。在函数内,该引用用于访问调用中要用到的实际参数。这意味着,修改形式参数会影响实际参数。12345678910111213141516171819202122232425262728293031323334#include <iostream>using namespace std;// 函数声明void swap(int &x, int &y);int main (){ // 局部变量声明 int a = 100; int b = 200; cout << "交换前,a 的值:" << a << endl; cout << "交换前,b 的值:" << b << endl; /* 调用函数来交换值 */ swap(a, b); cout << "交换后,a 的值:" << a << endl; cout << "交换后,b 的值:" << b << endl; return 0;}// 函数定义void swap(int &x, int &y){ int temp; temp = x; /* 保存地址 x 的值 */ x = y; /* 把 y 赋值给 x */ y = temp; /* 把 x 赋值给 y */ return;} 参数的默认值当您定义一个函数,您可以为参数列表中后边的每一个参数指定默认值。当调用函数时,如果实际参数的值留空,则使用这个默认值1234567891011121314151617181920212223242526272829#include <iostream>using namespace std;int sum(int a, int b=20){ int result; result = a + b; return (result);}int main (){ // 局部变量声明 int a = 100; int b = 200; int result; // 调用函数来添加值 result = sum(a, b); cout << "Total value is :" << result << endl; // 再次调用函数 result = sum(a); cout << "Total value is :" << result << endl; return 0;} Lambda 函数与表达式1[capture](parameters)->return-type{body}]]></content>
<categories>
<category>编程语言</category>
<category>C++</category>
</categories>
<tags>
<tag>c语言</tag>
<tag>变量作用域</tag>
</tags>
</entry>
<entry>
<title><![CDATA[C++循环语句与条件语句]]></title>
<url>%2F2017%2F08%2F29%2F%E7%BC%96%E7%A8%8B%E8%AF%AD%E8%A8%80%2Fc%E8%AF%AD%E8%A8%80%2FC-%E5%BE%AA%E7%8E%AF%E8%AF%AD%E5%8F%A5%E4%B8%8E%E6%9D%A1%E4%BB%B6%E8%AF%AD%E5%8F%A5%2F</url>
<content type="text"><![CDATA[循环语句 循环类型 描述 while 循环 当给定条件为真时,重复语句或语句组。它会在执行循环主体之前测试条件。 for 循环 多次执行一个语句序列,简化管理循环变量的代码。 do…while 循环 除了它是在循环主体结尾测试条件外,其他与 while 语句类似。 嵌套循环 您可以在 while、for 或 do..while 循环内使用一个或多个循环。 循环控制语句 控制语句 描述 break 语句 终止 loop 或 switch 语句,程序流将继续执行紧接着 loop 或 switch 的下一条语句。 continue 语句 引起循环跳过主体的剩余部分,立即重新开始测试条件。 goto 语句 将控制转移到被标记的语句。但是不建议在程序中使用 goto 语句。 判断语句 语句 描述 if 语句 一个 if 语句 由一个布尔表达式后跟一个或多个语句组成。 if…else 语句 一个 if 语句 后可跟一个可选的 else 语句,else 语句在布尔表达式为假时执行。 嵌套 if 语句 您可以在一个 if 或 else if 语句内使用另一个 if 或 else if 语句。 switch 语句 一个 switch 语句允许测试一个变量等于多个值时的情况。 嵌套 switch 语句 您可以在一个 switch 语句内使用另一个 switch 语句。 ? : 运算符1Exp1 ? Exp2 : Exp3; Exp1为真,则计算Exp2 Exp1为假,则计算Exp3]]></content>
<categories>
<category>编程语言</category>
<category>C++</category>
</categories>
<tags>
<tag>c语言</tag>
<tag>循环语句</tag>
<tag>条件语句</tag>
</tags>
</entry>
<entry>
<title><![CDATA[C++运算符]]></title>
<url>%2F2017%2F08%2F29%2F%E7%BC%96%E7%A8%8B%E8%AF%AD%E8%A8%80%2Fc%E8%AF%AD%E8%A8%80%2FC-%E8%BF%90%E7%AE%97%E7%AC%A6%2F</url>
<content type="text"><![CDATA[运算符 描述 实例 + 把两个操作数相加 A + B 将得到 30 - 从第一个操作数中减去第二个操作数 A - B 将得到 -10 * 把两个操作数相乘 A * B 将得到 200 / 分子除以分母 B / A 将得到 2 % 取模运算符,整除后的余数 B % A 将得到 0 ++ 自增运算符,整数值增加 1 A++ 将得到 11 — 自减运算符,整数值减少 1 A— 将得到 9 == 检查两个操作数的值是否相等,如果相等则条件为真。 (A == B) 不为真。 != 检查两个操作数的值是否相等,如果不相等则条件为真。 (A != B) 为真。 > 检查左操作数的值是否大于右操作数的值,如果是则条件为真。 (A > B) 不为真。 < 检查左操作数的值是否小于右操作数的值,如果是则条件为真。 (A < B) 为真。 >= 检查左操作数的值是否大于或等于右操作数的值,如果是则条件为真。 (A >= B) 不为真。 <= 检查左操作数的值是否小于或等于右操作数的值,如果是则条件为真。 (A <= B) 为真。 && 称为逻辑与运算符。如果两个操作数都非零,则条件为真。 (A && B) 为假。 || 称为逻辑或运算符。如果两个操作数中有任意一个非零,则条件为真。 (A || B) 为真。 ! 称为逻辑非运算符。用来逆转操作数的逻辑状态。如果条件为真则逻辑非运算符将使其为假。 !(A && B) 为真。 & 如果同时存在于两个操作数中,二进制 AND 运算符复制一位到结果中。 (A & B) 将得到 12,即为 0000 1100 | 如果存在于任一操作数中,二进制 OR 运算符复制一位到结果中。 (A | B) 将得到 61,即为 0011 1101 ^ 如果存在于其中一个操作数中但不同时存在于两个操作数中,二进制异或运算符复制一位到结果中。 (A ^ B) 将得到 49,即为 0011 0001 ~ 二进制补码运算符是一元运算符,具有”翻转”位效果,即0变成1,1变成0。 (~A ) 将得到 -61,即为 1100 0011,一个有符号二进制数的补码形式。 << 二进制左移运算符。左操作数的值向左移动右操作数指定的位数。 A << 2 将得到 240,即为 1111 0000 >> 二进制右移运算符。左操作数的值向右移动右操作数指定的位数。 A >> 2 将得到 15,即为 0000 1111 = 简单的赋值运算符,把右边操作数的值赋给左边操作数 C = A + B 将把 A + B 的值赋给 C += 加且赋值运算符,把右边操作数加上左边操作数的结果赋值给左边操作数 C += A 相当于 C = C + A -= 减且赋值运算符,把左边操作数减去右边操作数的结果赋值给左边操作数 C -= A 相当于 C = C - A *= 乘且赋值运算符,把右边操作数乘以左边操作数的结果赋值给左边操作数 C = A 相当于 C = C A /= 除且赋值运算符,把左边操作数除以右边操作数的结果赋值给左边操作数 C /= A 相当于 C = C / A %= 求模且赋值运算符,求两个操作数的模赋值给左边操作数 C %= A 相当于 C = C % A <<= 左移且赋值运算符 C <<= 2 等同于 C = C << 2 >>= 右移且赋值运算符 C >>= 2 等同于 C = C >> 2 &= 按位与且赋值运算符 C &= 2 等同于 C = C & 2 ^= 按位异或且赋值运算符 C ^= 2 等同于 C = C ^ 2 |= 按位或且赋值运算符 C |= 2 等同于 C = C | 2 优先级 类别 运算符 结合性 后缀 () [] -> . ++ - - 从左到右 一元 + - ! ~ ++ - - (type)* & sizeof 从右到左 乘除 * / % 从左到右 加减 + - 从左到右 移位 << >> 从左到右 关系 < <= > >= 从左到右 相等 == != 从左到右 位与 AND & 从左到右 位异或 XOR ^ 从左到右 位或 OR | 从左到右 逻辑与 AND && 从左到右 逻辑或 OR || 从左到右 条件 ?: 从右到左 赋值 = += -= *= /= %=>>= <<= &= ^= |= 从右到左 逗号 , 从左到右]]></content>
<categories>
<category>编程语言</category>
<category>C++</category>
</categories>
<tags>
<tag>c语言</tag>
<tag>数据类型</tag>
</tags>
</entry>
<entry>
<title><![CDATA[C++变量作用域]]></title>
<url>%2F2017%2F08%2F29%2F%E7%BC%96%E7%A8%8B%E8%AF%AD%E8%A8%80%2Fc%E8%AF%AD%E8%A8%80%2FC-%E5%8F%98%E9%87%8F%E4%BD%9C%E7%94%A8%E5%9F%9F%2F</url>
<content type="text"><![CDATA[局部变量在函数或一个代码块内部声明的变量,称为局部变量。它们只能被函数内部或者代码块内部的语句使用。下面的实例使用了局部变量: 123456789101112131415161718#include <iostream>using namespace std;int main (){ // 局部变量声明 int a, b; int c; // 实际初始化 a = 10; b = 20; c = a + b; cout << c; return 0;} 全局变量在所有函数外部定义的变量(通常是在程序的头部),称为全局变量。全局变量的值在程序的整个生命周期内都是有效的。 全局变量可以被任何函数访问。也就是说,全局变量一旦声明,在整个程序中都是可用的。下面的实例使用了全局变量和局部变量: 1234567891011121314151617181920#include <iostream>using namespace std;// 全局变量声明int g;int main (){ // 局部变量声明 int a, b; // 实际初始化 a = 10; b = 20; g = a + b; cout << g; return 0;} 在程序中,局部变量和全局变量的名称可以相同,但是在函数内,局部变量的值会覆盖全局变量的值。下面是一个实例: 123456789101112131415#include <iostream>using namespace std;// 全局变量声明int g = 20;int main (){ // 局部变量声明 int g = 10; cout << g; return 0;} 当上面的代码被编译和执行时,它会产生下列结果:110]]></content>
<categories>
<category>编程语言</category>
<category>C++</category>
</categories>
<tags>
<tag>c语言</tag>
<tag>变量作用域</tag>
</tags>
</entry>
<entry>
<title><![CDATA[C++数据类型]]></title>
<url>%2F2017%2F08%2F29%2F%E7%BC%96%E7%A8%8B%E8%AF%AD%E8%A8%80%2Fc%E8%AF%AD%E8%A8%80%2FC-%E6%95%B0%E6%8D%AE%E7%B1%BB%E5%9E%8B%2F</url>
<content type="text"><![CDATA[数据类型 初始值 数据类型 初始化默认值 int 0 char ‘\0’ float 0 double 0 pointer NULL typedef 声明例如,下面的语句会告诉编译器,feet 是 int 的另一个名称:1typedef int feet; 枚举类型枚举类型(enumeration)是C++中的一种派生数据类型,它是由用户定义的若干枚举常量的集合。 如果一个变量只有几种可能的值,可以定义为枚举(enumeration)类型。所谓”枚举”是指将变量的值一一列举出来,变量的值只能在列举出来的值的范围内。 创建枚举,需要使用关键字 enum。枚举类型的一般形式为:123456enum 枚举名{ 标识符[=整型常数], 标识符[=整型常数],... 标识符[=整型常数]} 枚举变量; 如果枚举没有初始化, 即省掉”=整型常数”时, 则从第一个标识符开始。 例如,下面的代码定义了一个颜色枚举,变量 c 的类型为 color。最后,c 被赋值为 “blue”。12enum color { red, green, blue } c;c = blue; 默认情况下,第一个名称的值为 0,第二个名称的值为 1,第三个名称的值为 2,以此类推。但是,您也可以给名称赋予一个特殊的值,只需要添加一个初始值即可。例如,在下面的枚举中,green 的值为 5。1enum color { red, green=5, blue }; 在这里,blue 的值为 6,因为默认情况下,每个名称都会比它前面一个名称大 1,但 red 的值依然为 0。 C++ 中的左值(Lvalues)和右值(Rvalues)C++ 中有两种类型的表达式: 左值(lvalue):指向内存位置的表达式被称为左值(lvalue)表达式。左值可以出现在赋值号的左边或右边。 右值(rvalue):术语右值(rvalue)指的是存储在内存中某些地址的数值。右值是不能对其进行赋值的表达式,也就是说,右值可以出现在赋值号的右边,但不能出现在赋值号的左边。 变量是左值,因此可以出现在赋值号的左边。数值型的字面值是右值,因此不能被赋值,不能出现在赋值号的左边。下面是一个有效的语句:···C++int g = 20;···]]></content>
<categories>
<category>编程语言</category>
<category>C++</category>
</categories>
<tags>
<tag>c语言</tag>
<tag>数据类型</tag>
</tags>
</entry>
<entry>
<title><![CDATA[C++关键字]]></title>
<url>%2F2017%2F08%2F29%2F%E7%BC%96%E7%A8%8B%E8%AF%AD%E8%A8%80%2Fc%E8%AF%AD%E8%A8%80%2FC-%E5%85%B3%E9%94%AE%E5%AD%97%2F</url>
<content type="text"><![CDATA[关键字 asm else new this auto enum operator throw bool explicit private true break export protected try case extern public typedef catch false register typeid char float reinterpret_cast typename class for return union const friend short unsigned const_cast goto signed using continue if sizeof virtual default inline static void delete int static_cast volatile do long struct wchar_t double mutable switch while dynamic_cast namespace template]]></content>
<categories>
<category>编程语言</category>
<category>C++</category>
</categories>
<tags>
<tag>c语言</tag>
<tag>关键字</tag>
</tags>
</entry>
<entry>
<title><![CDATA[C++环境配置]]></title>
<url>%2F2017%2F08%2F29%2F%E7%BC%96%E7%A8%8B%E8%AF%AD%E8%A8%80%2Fc%E8%AF%AD%E8%A8%80%2FC-%E7%8E%AF%E5%A2%83%E9%85%8D%E7%BD%AE%2F</url>
<content type="text"><![CDATA[使用 Visual Studio (Graphical Interface) 编译1、下载及安装 Visual Studio Community 2015。2、打开 Visual Studio Community3、点击 File -> New -> Project 4、左侧列表选择 Templates -> Visual C++ -> Win32 Console Application,并设置项目名为 MyFirstProgram。 5、点击 OK。6、在以下窗口中点击 Next 7、在弹出的窗口中选择 Empty project 选项后,点击 Finish 按钮:8、右击文件夹 Source File 并点击 Add —> New Item… : 9、选择 C++ File 然后设置文件名为 main.cpp,然后点击 Add: 10、拷贝以下代码到 main.cpp 中:1234567#include <iostream>int main(){ std::cout << "Hello World!\n"; return 0;} 界面如下所示: 11、点击菜单上的 Debug -> Start Without Debugging (或按下 ctrl + F5) : 12、完成以上操作后,你可以看到以下输出]]></content>
<categories>
<category>编程语言</category>
<category>C++</category>
</categories>
<tags>
<tag>环境搭建</tag>
<tag>c语言</tag>
</tags>
</entry>
<entry>
<title><![CDATA[循环链表]]></title>
<url>%2F2017%2F08%2F28%2F%E6%95%B0%E6%8D%AE%E7%BB%93%E6%9E%84%2F%E5%BE%AA%E7%8E%AF%E9%93%BE%E8%A1%A8%2F</url>
<content type="text"><![CDATA[定义将单链表中终端结点的指针端有空指针改为指向头结点,就使整个单链表形成一个环,这种头尾相接的单链表成为单循环链表,简称单链表(circle linked list) 但是循环链表进过改造,不用头指针,而是用指向终端结点的尾指针来表示循环链表,这样查找开始结点和终端结点都很方便。 代码123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196#include <stdio.h>#include <stdlib.h>/*链式存储结构的定义*/typedef struct CLinkList{ int data; struct CLinkList * next;}node;/*** 1.初始化循环链表*/void ds_init(node **pNode){ int item; node * temp; node * target; printf("输入结点的值,输入0完成初始化\n"); while (1) { scanf("%d", &item); fflush(stdin); // 清空输入缓存区 if (item == 0) return; if ((*pNode) == NULL) { // 链表中只有一个结点 *pNode = (node *)malloc(sizeof(struct CLinkList)); if (!(*pNode)) exit(0); (*pNode)->data = item; (*pNode)->next = *pNode; }else{ //找到next指向第一个结点的结点 for (target = (*pNode); target->next != (*pNode); target = target->next); // 生成一个新的结点 temp = (node *)malloc(sizeof(struct CLinkList)); if (!temp) exit(0); temp->data = item; temp->next = *pNode; target->next = temp; } }}/*** 2.插入结点* @param pNode 链表的第一个结点* @param i 插入的位置*/void ds_insert(node **pNode, int i){ node * temp; node * target; node * p; int item; int j = 1; printf("输入要插入加点的值:"); scanf("%d", &item); if (i == 1) { // 插入到第一个位置 // 新插入的结点作为第一个结点 temp = (node *)malloc(sizeof(struct CLinkList)); if (!temp) exit(0); temp->data = item; //找到最后一个结点 for (target = (*pNode); target->next != (*pNode); target = target->next); temp->next = (*pNode); target->next = temp; *pNode = temp; }else{ // 插入到其他位置 target = *pNode; for (; j<(i-1); j++) { target = target->next; } temp = (node *)malloc(sizeof(struct CLinkList)); if (!temp) exit(0); temp->data = item; p = target->next; target->next = temp; temp->next = p; }}/*** 3.删除结点* @param pNode 链表的第一个结点* @param i 删除的位置*/void ds_delete(node **pNode, int i){ node * target; node * temp; int j = 1; if (i ==1) { // 删除的是第一个结点 // 找到最后一个结点 for (target = *pNode; target->next != *pNode; target = target->next); temp = *pNode; *pNode = (*pNode)->next; target->next = *pNode; free(temp); }else{ // 删除其他结点 target = *pNode; for (; j<i-1 ; j++) { target = target->next; } temp = target->next; target->next = temp->next; free(temp); }}/*** 4.返回结点所在位置* @param pNode 链表的第一个结点* @param elem 结点所在位置*/int ds_search(node *pNode, int elem){ node * target; int i = 1; for (target = pNode; target->data != elem && target->next != pNode; i++) { target = target->next; } if (target->next == pNode) return 0; // 表中不存在该元素 else return i;}/*** 5.遍历*/void ds_traverse(node *pNode){ node * temp; temp = pNode; printf("*************链表中的元素**********\n"); do { printf("%4d ", temp->data); } while ((temp = temp->next) != pNode); printf("\n");}/*** 5.合并链表*/LinkList Connect(LinkList A, LinkList B){ LinkList p = A->next; // 保存A表的头结点 A->next = B->next->next; // 将B表的开始结点链接到A表尾部 free(B); // 释放B表的头结点 B->next = p; return B; // 返回新循环链表的尾指针}]]></content>
<categories>
<category>数据结构</category>
</categories>
<tags>
<tag>链表</tag>
<tag>数据结构</tag>
<tag>c语言</tag>
</tags>
</entry>
<entry>
<title><![CDATA[单链表]]></title>
<url>%2F2017%2F08%2F28%2F%E6%95%B0%E6%8D%AE%E7%BB%93%E6%9E%84%2F%E5%8D%95%E9%93%BE%E8%A1%A8%2F</url>
<content type="text"><![CDATA[定义数据域:存储数据元素信息的域,指针域:存储直接后继位置的域。指针域中存储的信息成为指针或链。这两部分信息组成数据元素成为存储映像,成为结点(Node)。数据域|指针域-|-data|next 头结点与头指针 头结点头结点是加在单链表之前附设的一个头结点。头结点的数据域一般不存储任何信息,也可以存放一些关于线性表的长度的附加信息。-头指针头指针是指链表指向第一个结点的指针,若链表有头结点,则是指向头结点的指针。 代码123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128#include<iostream>#include<stdio.h>#include<math.h>#define NULL 0typedef int ElemType;void Error(char *s){ cout<<s<<endl; exit(1);}typedef struct LNode{ ElemType data; struct LNode *next;}LNode;typedef LNode *Linklist; //头指针void Error(char *s) //错误指示{ std::cout << s << endl; exit(1);}//创建链表,生成一个新节点作为头结点,并设其指针域为空Void InitList(Linklist &L){ L=new LNode; L->next=NULL;}//销毁链表,从链表的头结点开始依次释放表中每一个结点所占用的存储空间Void DestroyList(Linklist L){ while(L){ LNode *p=L; L=L->next; delete p; }}//设置单链表为空表,并释放所有结点空间void ClearList(Linklist &L){ LNode *p = L->next; L->next=NULL; L.length=0; while (p) { Linklist *q=p; p = p->next; delete q; }}//求链表长度int ListLength(Linklist L){ int length=0; LNode *p = L; while(p->next){ length++; p=p->next; } return length;}//返回链表中第i个结点数据域的值,1<=i<=表长;若i值不合法,则给出错误信息ElemType GetElem_L(Linklist L, int i){ int count = 1; LNode *p = L->next; while (p && (count < i)) { p = p->next; count++; } if (!(p->next)||count>i) Error("position Error") ; else return p->data;}//查找链表中第一个数据域值和x相等的结点LNode *LocateElem_L(Linklist L, ElemType x){ LNode *p =L->next; //将指针头传给指针p int j = 1; while (p && (p->data != x)) //向后扫描查找 { p = p->next; } return p;}//向链表中第i个位置插入元素x ,1<=x<=表长+1//若插入位置不合理则给出相应信息void ListInsert(Linklist &L, int i, int stu){ LNode *p =L; //将指针头传给指针p int j = 0; while (p && (j < i - 1)) //向后扫描查找 { p = p->next; j++; } if (!p || (j > i - 1)) //输入参数不对,给出错误提示,并跳出 Error("Postion Eeeor!"); else { LNode *s = new LNode; s->next = p->next; p->next = s; }}//删除链表L中第i个结点数据域值,并用返回,1<=x<=表长+1//若删除位置不合理则给出相应信息ElemType ListDelete_L(Linklist &L, int i){ LNode *p = L; //将指针头传给指针p int j = 0; while ((p ->next) && (j < i - 1)) { p = p->next; j++; } if (!(p->next) || (j > i - 1)) Error("Postion Eeeor!"); // 输入参数不对,给出错误提示,并跳出 else { LNode *q = p->next; ElemType x= p->data; p->next = q->next; delete q; return x; }} 单链表正标操作整表创建头插法12345678910111213141516171819202122/*** 头插法*/void CreatListHead(LinkList *L, int n){ LinkList p; int i; srand(time(0)); // 初始化随机数种子 *L = (LinkList)malloc(sizeof(Node)); (*L)->next = NULL; for (i = 0; i < n; i++) { p = (LinkList)malloc(sizeof(Node)); // 生成新节点 p ->data = rand()%100 + 1; p->next = (*L)->next; (*L)->next = p; }} 尾插法1234567891011121314151617181920212223/*** 尾插法*/void CreatListTail(LinkList *L, int n){ LinkList p, r; int i; srand(time(0)); *L = (LinkList)malloc(sizeof(Node)); r = *L; for (i = 0; i < n; i++) { p = (Node *)malloc(sizeof(Node)); p->data = rand()%100+1; r->next = p; r = p; } r->next = NULL;} 整表删除12345678910111213141516Status ClearList(LinkList *L){ LinkList p,q; p = (*L)->next; while (p) { q = p->next; free(p); p = q; } (*L)->next = NULL; return OK;}]]></content>
<categories>
<category>数据结构</category>
</categories>
<tags>
<tag>链表</tag>
<tag>数据结构</tag>
<tag>c语言</tag>
</tags>
</entry>
<entry>
<title><![CDATA[顺序表]]></title>
<url>%2F2017%2F08%2F28%2F%E6%95%B0%E6%8D%AE%E7%BB%93%E6%9E%84%2F%E9%A1%BA%E5%BA%8F%E8%A1%A8%2F</url>
<content type="text"><![CDATA[定义顺序存储结构是指用一段地址连续的存储单元依次存储线性表的数据元素。通常都用数组来描述数据结构中的顺序存储结构。 代码数据结构123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187# include <stdio.h># include <stdlib.h># define LIST_INIT_SIZE 100# define OK 1# define ERROR -1typedef int ElemType;typedef int Status;typedef struct { ElemType elem[LIST_INIT_SIZE]; //顺序表大小,可根据实际需要而定 int length; //线性表长度 int listsize; //数组长度(以ElemType为单位)} SqList; //由于c语言中数组的下标从0开始,因此线性表的第一个元素a1和最后一个元素a0分别存储在L.elem[0]和L.elem[L.length-1]Status InitList_Sq(SqList & L);Status ListInsert_Sq(SqList & L, int i, ElemType e);Status ListDelete_Sq(SqList & L, int i, ElemType & e);int LocateElem_Sq(SqList L, ElemType e, Status (* compare)(ElemType, ElemType));void MergeList_Sq(SqList La, SqList Lb, SqList &Lc);Status ShowList_Sq(SqList & L);Status compare(ElemType a, ElemType b);/*******************Status InitList(SqList &L)功能:初始化顺序线性表参数: SqList &L:顺序线性表L返回值: Status类型:OK为执行正确,ERROR为出错*******************/Status InitList_Sq(SqList & L) //&L 对线性表L的引用{ //算法2.3 List_Size L.elem = new ElemType[LIST_INIT_SIZE]; //elem 数组的名称是一个指针,(ElemType *)返回一个指针 if(!L.elem) { exit(OVERFLOW); } L.length = 0; L.listsize = LIST_INIT_SIZE; return OK;}/*******************Void DestroyList_Sq(SqList)功能:销毁顺序表:*******************/void DestroyList_Sq(SqList) {//释放顺序表L所占用的存储空间 delete []L.elem; L.length=0; L.listsize=0;}/*******************Status ListEmpty(SqList L)功能: 检测是否为空表限制: 初始条件:顺序线性表L已存在参数: SqList L:顺序线性表L返回值: Status类型:TRUE表示L为空表,否则返回FALSE*******************/Status ListEmpty(SqList L){ if(L.length == 0) return TRUE; else return FALSE;}/*******************Status ClearList(SqList *L)功能: 将L重置为空表限制: 初始条件:顺序线性表L已存在参数: SqList &L:顺序线性表L返回值: Status类型:OK表示执行正确*******************/Status ClearList(SqList &L) L->length=0; return OK;}/*******************int ListLength(SqList L)功能: 获取L中数据元素个数限制: 初始条件:顺序线性表L已存在参数: SqList L:顺序线性表L返回值: int类型:返回L中数据元素个数*******************/int ListLength(SqList L) { return L.length;}/*******************Status GetElem(SqList L,int i,ElemType *e)功能: 用e返回L中第i个数据元素的值(注意i是指位置)限制: 初始条件:顺序线性表L已存在,1≤i≤ListLength(L)参数: SqList L:顺序线性表L int i:要取出元素的位置 ElemType *e:所取出的元素返回值: Status类型:OK表示执行正确,ERROR为出错*******************/Status GetElem(SqList L,int i,ElemType *e){ if(L.length==0 || i<1 || i>L.length) return ERROR; *e=L.elem[i-1]; return ok;}/*******************Status ListInsert(SqList &L,int i,ElemType x)功能: 在L中第i个位置前插入新的数据元素x限制: 初始条件:顺序线性表L已存在,,1≤i≤ListLength(L)参数: SqList &L:顺序线性表L int i:位置i ElemType x:新的数据元素x返回值: Status类型:OK表示执行正确,ERROR为出错*******************/Status ListInsert_Sq(SqList & L, int i, ElemType x){ //算法2.4 //在顺序线性表L中第i个元素之前插入x if(i < 1 || i > L.length + 1) return ERROR; if(L.length >= L.listsize) return ERROR; ElemType *q = & (L.elem[i - 1]); for(ElemType *p = & (L.elem[L.length - 1]); p >= q; --p) { *(p + 1) = *p; //元素后移 } *q = x; ++L.length; //长度加1 return OK;}/*******************Status ListDelete(SqList —L,int i,ElemType e)功能: 删除L的第i个数据元素限制: 初始条件:顺序线性表L已存在,,1≤i≤ListLength(L)参数: SqList —L:顺序线性表L int i:位置i返回值: Status类型:OK表示执行正确,ERROR为出错*******************/Status ListDelete_Sq(SqList &L, int i){ //算法2.5 if(i < 1 || i > L.length) return ERROR; //输出错误 ElemType *p = & (L.elem[i - 1]); ElemType *q = L.elem + L.length - 1; for(++p; p <= q; ++p) { * (p - 1) = * p; //元素前移 } --L.length; return OK;}/*******************int LocateElem_Sq(SqList L,ElemType x)功能: 找出L中第1个与x满足相等关系的数据元素的位序限制: 初始条件:顺序线性表L已存在参数: SqList L:顺序线性表L ElemType x:元素x返回值: int类型:返回L中第1个与e满足相等关系的数据元素的位序;若这样的数据元素不存在,则返回值为0 *******************/int LocateElem_Sq(SqList L, ElemType x){ //算法2.6 //指向函数的指针的使用 int i = 1; ElemType * p = L.elem; while((i <= L.length )&& (*p++ != x)) ++i; if(i <= L.length) { return i; } else { return 0;} 测试代码12345678910111213141516171819202122232425262728int main(void){ //测试函数 SqList L; InitList_Sq(L); for(int i = 10; i > 0; i--) { ListInsert_Sq(L, 1, i); } ShowList_Sq(L); //print L int e; ListDelete_Sq(L, 4, e); ShowList_Sq(L); //print L printf("%d\n", LocateElem_Sq(L, 3, * compare)); //print location of 3 //函数做参数如何传值呢?? SqList Lb; InitList_Sq(Lb); for(int i = 10; i > 0; i--) { ListInsert_Sq(Lb, 1, 2 * i); } ShowList_Sq(Lb); //print Lb SqList Lc; InitList_Sq(Lc); ShowList_Sq(Lc); //print Lc MergeList_Sq(L, Lb, Lc); ShowList_Sq(Lc); //print Lc return 0;} 缺点线性表的顺序存储结构,最大的缺点就是插入和删除时需要移动大量的元素,这显然需要耗费时间。导致这个问题的原因是在于相邻元素的存储位置具有邻居关系,它们在内存中的位置是紧挨着的,中间没有间隙,当然无法快速插入和删除。]]></content>
<categories>
<category>数据结构</category>
</categories>
<tags>
<tag>链表</tag>
<tag>数据结构</tag>
<tag>c语言</tag>
</tags>
</entry>
<entry>
<title><![CDATA[数据结构目录]]></title>
<url>%2F2017%2F08%2F28%2F%E6%95%B0%E6%8D%AE%E7%BB%93%E6%9E%84%2F%E6%95%B0%E6%8D%AE%E7%BB%93%E6%9E%84%E7%9B%AE%E5%BD%95%2F</url>
<content type="text"><![CDATA[一、绪论二、抽象数据类型ADT12345ADT 抽象数据类型名称{ 数据对象(Data):<数据对象的定义> 数据关系 :<数据关系的定义> 基本操作(Operation):<基本操作的定义>}ADT抽象数据类型名称 线性表12345678910111213141516171819202122232425262728293031323334353637ADT 线性表(List)Data 线性表的数据对象集合为{a1,a2,…,an},每个元素的类型均为DataType。 其中,除第一个元素a1外,每一个元素有且只有一个直接前驱元素,除了最后一个元素an外,每一个元素有且只有一个直接后继元素。 数据元素之间的关系是一对一的关系。Operation InitList(): 初始化操作,建立一个空的线性表L。 DestroyList():线性表已经存在,释放该线性表所占用的存储空间 ClearList(): 线性表已经存在,重置线性表为空表,将线性表清空。 ListLength(): 线性表已经存在返回线性表L的元素个数。 GetElem(): 求线性表L中的第i个位置元素值。 初始条件:线性表已存在 输入参数:元素序号i,1<=i<=ListLength(L) 实现功能:取线性表中序号为i的元素值 输出数据:如果序号不合理,则给出错误信息;否则返回序号为i的元素序号值 操作结果:线性表不变 LocateElem(): 在线性表L中查找与给定值x相等的元素,如果查找成功,返回线性表中第1个值等于x的元素序号;否则返回0。 初始条件:线性表已存在 输入参数:元素x 实现功能:查找线性表中第1个值等于x的元素 输出数据:如果查找成功,返回线性表中第1个值等于x的元素序号;否则返回0 操作结果:线性表不变 ListInsert(): 在线性表L中第i个位置插入新元素e。 初始条件:线性表已存在 输入参数:插入位置i,1<=i<=ListLength(L)+1,ListLength(L)表示插入前的表长;待插入元素x 实现功能:插入x到线性表的第i个位置上 输出数据:如果插入不成功,则给出错误信息 操作结果:当插入成功时,线性表中增加了一个元素x,且表长增1 ListDelete(): 删除线性表L中第i个位置元素,并用e返回其值。 初始条件:线性表已存在 输入参数:删除位置i,1<=i<=ListLength(L)+1,ListLength(L)表示插入前的表长 实现功能:删除线性表中的第i个元素 输出数据:如果删除不成功,则给出错误信息。否则返回第i个元素值 操作结果:当删除成功时,线性表中减少了一个元素,且表长减1endADT 顺序表单链表静态链表.md 没有审核代码循环链表双向链表.md 没有审核代码 特殊线性表-栈、队列和串栈:后进先出123456789101112131415161718192021ADT Stack{ Data: 栈中元素具有相同类型及后进先出特性,相邻元素具有前驱和后继关系 Operation: InitStack():初始化栈,构造一个空栈 DestroyStack():销毁栈,释放栈所占用的存储空间 StackLength():求栈的长度 GetTop(): 实现功能:读取当前栈顶元素 输出数据:如果栈空,则给出错误信息;否则返回当前栈顶元素值 Push(): 输入参数:待插入元素 实现功能:插入元素到栈顶 输出数据:如果插入不成功,则给出错误信息 操作结果:当插入成功时,栈顶增加了一个元素 Pop(): 输入参数:无 实现功能:删除栈顶元素 输出数据:如果栈为空,则给出错误信息;否则返回栈顶元素 操作结果:当删除成功时,栈顶减少了一个元素} 栈的顺序储结构栈.md栈的链式存储结构.md 队列:先进先出123456789101112131415161718192021ADT Queue{ Data: 队列中元素具有相同类型及先进先出特性,相邻元素具有前驱和后继关系 Operation: InitQueue():初始化队列,构造一个空队列 DestroyQueue():销毁队列,释放队列所占用的存储空间 QueueLength():求队列的长度 GetHead(): 实现功能:读取当前队列头元素 输出数据:如果队列空,则给出错误信息;否则返回当前队列头元素值 EnQueue(): 输入参数:待插入元素 实现功能:插入元素到队列尾 输出数据:如果插入不成功,则给出错误信息 操作结果:当插入成功时,队列尾增加了一个元素 DeQueue(): 输入参数:无 实现功能:删除队列头元素 输出数据:如果队列为空,则给出错误信息;否则返回队列头元素 操作结果:当删除成功时,队列尾减少了一个元素} 队列的顺序存储结构.md队列链式存储结构.md 串123456789101112131415161718ADT 串(string){ Data 串中元素仅由一个字符组成,相邻元素具有前驱和后继关系. Operation StrAssign(T,*chars):生成一个其值等于字符常量chars的串T. StrCopy(T,S):串S存在,由S复制得到T. ClearString(S):串S存在,将串清空. StringEmpty(S): 若串为空,返回true,否则返回false. StrLentgth(S) :返回串S的元素个数,即串的长度. StrCompare(S,L):比较S和T,若S>T,返回>0,S==T返回0, S<T返回<0 SubString(Sub, S, pos, len): 串S存在,1<=pos<=StrLentgth(S),且 0<=len<=StrLentgth(S)-pos+1,用Sub返回串S的第pos个起,长度为len的子串. Index(S,T,pos) Replace(S,T,V)串S,T,V存在,T是非空串,用V替换S中出现的所有与T相等的不重叠的子串. StrInsert(S,T,pos): 在串S的第pos个字符之前插入串T. StrDelete(S,pos,len):从串S中删除第pos个字符起的长度为len的子串.}endADT 串的顺序存储结构.md 数组和广义表数组:由一组类型相同、下标不同的变量构成。根据数组中存储的数据元素之间的逻辑关系,可以将数组分为 : 一维数组、二维数组、…、n维数组。12345678910111213141516171819202122232425ADT Array {Data: 相同类型元素有序集合,每个元素受n(n>=1)个线性关系的约束并由一组下标唯一标识Operation: InitArray(): //构造一个空数组,数组的维数和各维的长度 DestroyArray()://销毁数组,释放数组所占用的存储空间 GetValue(A,&e,index1,…,indexn) //求值函数,求某个下标元素的值 初始条件:A是维数组,e为元素变量,随后是n个下标值. 操作结果:若各下标不超界,则e赋值为所指定的A的元素值,并返回OK. Assign(&A,e,index1,…,indexn)//赋值函数,给下具体的下标的元素赋值 初始条件:A是n维数组,e为元素变量,随后是n个下标值. 操作结果:若下标不超界,则将e的值赋给所指定的A的元素,并返回OK.}ADT Array 树12345678910111213141516171819202122ADT Tree{ Data: 树 Operation: InitTree(&T):构造空树 DestroyTree(&T):销毁树 CreateTree(&T,dfinition): 初始条件:defination给出树T的定义 操作结果:按defination构造树T ClearTree(&T):将树清为空树 TreeEmpty(T):若树为空树,则返回TRUE,否则FALSE TreeDepth(T):返回树的深度 Root(T):返回T的根 Value(T,node):树T存在,node是T中的某一个结点,返回node的值 Assign(T,node,value):树T存在,node是T中的某一个结点,结点node赋值为value Parent(T,node):若node是T的非根结点,返回它的双亲,否则函数值为空 LeftChild(T,node):若node是T的非叶子结点,返回它的最左孩子,否则函数值为空 RightSiblingChild(T,node):若node右兄弟,返回它的右兄弟,否则函数值为空 InsertChild(&T,&p,i,c):树T存在,p指向是T中某一个结点,插入c为T中p所指节点的第i颗子树 DeleteChild(&T,&p,i,c):树T存在,p指向是T中某一个结点,删除T中p所指节点的第i颗子树 TraveseTree():遍历树}ADT Tree 树与森林.md树的存储结构.md java版二叉树.md线索二叉树.md哈赫夫曼树.md 图12345678910111213141516171819ADT 图(Graph)Data 顶点的有穷非空集合和边的集合。Operation CreateGraph(*G, V, VR): 按照顶点集V和边弧集VR的定义构造图G。 DestroyGraph(*G): 图G存在则销毁。 LocateVex(G, u): 若图G中存在顶点u,则返回图中的位置。 GetVex(G, v): 返回图G中顶点v的值。 PutVex(G, v, value): 将图G中顶点v赋值value。 FirstAdjVex(G, *v): 返回顶点v的一个邻接顶点,若顶点在G中无邻接顶点返回空。 NextAdjVex(G, v, *w): 返回顶点v相对于顶点w的下一个邻接顶点, 若w是v的最后一个邻接点则返回“空”。 InsertVex(*G, v): 在图G中增添新顶点v。 DeleteVex(*G, v): 删除图G中顶点v及其相关的弧。 InsertArc(*G, v, w): 在图G中增添弧<v,w>,若G是无向图,还需要增添对称弧<w,v>。 DeleteArc(*G, v, w): 在图G中删除弧<v,w>,若G是无向图,则还删除对称弧<w,v>。 DFSTraverse(G): 对图G中进行深度优先遍历,在遍历过程对每个顶点调用。 HFSTraverse(G): 对图G中进行广度优先遍历,在遍历过程对每个顶点调用。endADT 图的术语与定义.md图的抽象结构与存储结构.md(无向图采用多重邻接表,有向图采用十字链表)图的遍历.md最小生成树.md最短路径问题.md拓扑排序.md关键路径.md 区别.md查找 静态查找(Static Search Table):只作查找操作的查找表。主要操作有: 查询某个“特定的”数据元素是否在查找表中。 检索某个“特定的”数据元素和各种属性。线性表的查找.md 动态查找表(Dynamic Search Table):在查找过程中同时插入查找表中不存在的数据元素,或者从查找表中删除已经存在的某个特定的数据元素。主要操作有两个: 查找时插入数据元素; 查找时删除数据元素。查找树.md平衡二叉树.md2-3树.mdB树红黑树 哈希表散列查找.md 排序 排序基础代码123456789101112131415161718192021#include <stdio.h>#include <stdlib.h>#define MAXSIZE 100#define TRUE 1#define FALSE 0typedef struct { int r[MAXSIZE + 1]; // 用于存储要排序的数组, r[0]用作哨兵或者临时变量 int length; // 用于记录顺序表的长度}SqList;/*** 交换数组r中下标i和j的值*/void swap(SqList *L, int i, int j){ int temp = L->r[i]; L->r[i] = L->r[j]; L->r[j] = temp;} 插入排序.md (直接插入排序,希尔排序。希尔排序根据直接插入排序而来)选择排序.md交换排序.md归并排序.md(从0开始) 排序代码集合(从0开始).md 性能比较 排序方法 平均情况 最好情况 最坏情况 辅助空间 稳定性 冒泡排序 $O(n^2)$ $O(n)$ $O(n^2)$ $O(1)$ 稳定 简单选择排序 $O(n^2)$ $O(n^2)$ $O(n^2)$ $O(1)$ 稳定 直接插入排序 $O(n^2)$ $O(n)$ $O(n^2)$ $O(1)$ 稳定 希尔排序 $O(nlogn)~O(n^2)$ $O(n^{1.3})$ $O(n^2)$ $O(1)$ 不稳定 堆排序 $O(nlogn)$ $O(nlogn)$ $O(nlogn)$ $O(1)$ 不稳定 归并排序 $O(nlogn)$ $O(nlogn)$ $O(nlogn)$ $O(n)$ 稳定 快速排序 $O(nlogn)$ $O(nlogn)$ $O(n^2)$ $O(nlogn)~O(n)$ 不稳定 附录图-邻接矩阵代码.md邻接表代码.md邻接矩阵与邻接表深度遍历.md邻接矩阵与邻接表的广度优先遍历算法.md普里姆(Prim)算法代码.md克鲁斯卡尔(Kruskal)算法代码.md迪杰斯特拉(Dijkstra)算法代码.md]]></content>
<categories>
<category>数据结构</category>
</categories>
<tags>
<tag>目录</tag>
</tags>
</entry>
<entry>
<title><![CDATA[随机森林]]></title>
<url>%2F2017%2F08%2F26%2F%E4%BA%BA%E5%B7%A5%E6%99%BA%E8%83%BD%2F%E6%9C%BA%E5%99%A8%E5%AD%A6%E4%B9%A0%2F%E9%9A%8F%E6%9C%BA%E6%A3%AE%E6%9E%97%2F</url>
<content type="text"><![CDATA[Bagging(套袋法)bagging的算法过程如下: 从原始样本集中使用Bootstraping方法随机抽取n个训练样本,共进行k轮抽取,得到k个训练集。(k个训练集之间相互独立,元素可以有重复) 对于k个训练集,我们训练k个模型(这k个模型可以根据具体问题而定,比如决策树,knn等) 对于分类问题:由投票表决产生分类结果;对于回归问题:由k个模型预测结果的均值作为最后预测结果。(所有模型的重要性相同) 随机森林鉴于决策树容易过拟合的缺点,随机森林采用多个决策树的投票机制来改善决策树,其中每棵树都和其他树略有不同,随机森林背后的思想是,每棵树的预测可能都相对较好,但可能对部分数据过拟合。如果构造很多树,并且每棵树的预测都很好,但都以不同的方式过拟合,那么我们可以对这些树的结果取平均值来降低过拟合。既能减少过拟合又能保证树的预测能力,这可以在数学上严格证明。 从原始训练集中使用Bootstraping方法随机有放回采样选出m个样本,共进行n_tree次采样,生成n_tree个训练集 对于n_tree个训练集,我们分别训练n_tree个决策树模型 对于单个决策树模型,假设训练样本特征的个数为n,那么每次分裂时根据信息增益/信息增益比/基尼指数选择最好的特征进行分裂 每棵树都一直这样分裂下去,直到该节点的所有训练样例都属于同一类。在决策树的分裂过程中不需要剪枝 将生成的多棵决策树组成随机森林。对于分类问题,按多棵树分类器投票决定最终分类结果;对于回归问题,由多棵树预测值的均值决定最终预测结果]]></content>
<categories>
<category>人工智能</category>
<category>机器学习</category>
</categories>
<tags>
<tag>模型</tag>
<tag>树模型</tag>
<tag>集成学习</tag>
</tags>
</entry>
<entry>
<title><![CDATA[梯度类方法与对偶算法]]></title>
<url>%2F2017%2F08%2F18%2F%E4%BA%BA%E5%B7%A5%E6%99%BA%E8%83%BD%2F%E4%BC%98%E5%8C%96%E7%AE%97%E6%B3%95%2F%E6%A2%AF%E5%BA%A6%E7%B1%BB%E6%96%B9%E6%B3%95%E4%B8%8E%E5%AF%B9%E5%81%B6%E7%AE%97%E6%B3%95%2F</url>
<content type="text"><![CDATA[梯度类方法梯度类方法是无约束优化中非常常用的方法,其依据的最根本的事实就是梯度的负方向是函数值下降最快的方向。但是常用的 gradient descent 必须要求函数的连续可导,而对于某些连续不可导的问题(如lasso regression),gradient descent 无能为力,这是需要用到subgradient descent和proximal gradient descent. gradient descent梯度下降法的迭代公式为 x^{(k)} = x^{(k-1)} - t_k\nabla f(x^{(k-1)} )上式中上标 $(k)$ 表示第 $k$ 次迭代, 而 $t_k$ 表示步长,$\nabla f(x^{(k-1)})$表示在点 $x^{(k-1)}$ 的梯度。 这里对于梯度下降主要讨论其步长选择的问题, 最简单直接的方式是固定每次的步长为一个恒定值,但是如果步长过大或过小时,可能会导致结果难以收敛或者收敛速度很慢。因此提出了可变长步长的方法,可变长步长的方法指的是根据每次迭代依照一定的规则改变步长,下面介绍两种:backtracking line search 和 exact line serach。 backtracking line searchbacktracking line search 需要先选择两个固定的参数 $α,β$ , 要求 $0<β<1,0<α<1/2$每次迭代的时候,假如下式成立 f(x - t\nabla f(x)) > f(x) - \alpha t||\nabla f(x)||_2^2则改变步长为 $t=βt$, 否则步长不变。 这种方法的思想是当步长过大的时候(即跨过了最优点),减小步长,否则保持步长不变,如下式是一个简单的例子 exact line serachexact line serach 则是得到先计算出梯度 $\nabla f(x^{(k-1)} )$,然后代入下面的函数中,此时只有步长 $t_k$ 是未知,因此可对 $t_k$ 进行求导并令其为0,求得的 $t_k$ 即为当前的最优的步长,因为这个步长令当前迭代下降的距离最大。 f(x^{(k-1)} - t_k\nabla f(x^{(k-1)} ))这种方法也被称为最速下降法。 对偶类算法拉格朗日拉格朗日对偶性是解决带约束的最优化问题的方法,在实际应用中,通过拉格朗日对偶原理将原始问题转换成对偶问题,将原来不容易解决的问题转化为一个容易解决的问题,如支持向量机。 拉格朗日函数假设 $f(x),c_i(x),h_j(x)$ 是定义在 $\mathbb{R}^{n}$ 上的连续可微函数。我们需要求解约束最优化问题: \underset{x\in \mathbb{R}^n}{min} \; f(x) \tag 1\begin{align} \mathbb{s.t.}\quad &c_i(x) \le 0,\quad i=1,2,\cdots,k \tag 2\\ &h_j(x)=0,\quad j=1,2,\cdots,l \tag 3 \end{align}为了求解原始问题,我们首先引入广义拉格朗日函数(generalized Lagrange function): L(x,\alpha,\beta)=f(x)+\sum_{i=1}^k \alpha_ic_i(x) + \sum_{j=1}^l\beta_jh_j(x) \tag{4}其中,$x=(x_1,x_2,\cdots,x_n)^T \in \mathbb{R}^n$,$\alpha_i$和$\beta_j$是拉格朗日乘子,特别要求$\alpha_i\geqslant 0$ 极小极大问题如果把$L(x,\alpha,\beta)$看作是$\alpha、\beta$的函数,求其最大值,即 \theta_p(x)=\max_{\alpha,\beta:\alpha_i\ge0}L(x,\alpha,\beta) \tag 5确定$\alpha、\beta$使$L(x,\alpha,\beta)$取得最大值,(此过程中把$x$看做常量)下面通过$x$是否满足约束条件两方面来分析这个函数 如果 $x$ 满足原始问题中约束,由(2)、(3)、(4)、(5)可知 $θ(x)=f(x)$。(少的两项一个是非正的,一个是0,要取最大值的话当然得令两者都为0 如果$x$不满足原始问题中的约束,那么$θ(x)=+∞$。若某个$i$使约束$c_i(x)>0$,则可令则可令$\alpha \rightarrow +∞$,若某个$j$使得$h_j(x)\neq 0,$,则可令$\beta_j h_j(x) \rightarrow +∞$,而将其余各$\alpha _i、\beta_j$均取为0。 综上: \theta_p(x)=\left\{\begin{matrix} f(x),&x 满足原始问题约束\\ +\infty,&其他 \end{matrix}\right.求解原问题的最小值 \underset{x\in \mathbb{R}^n}{min}; \theta_p(x)= \underset{x\in \mathbb{R}^n}{min}\; f(x)=\underset{x\in \mathbb{R}^n}{min}\;\max_{\alpha,\beta:\alpha_i\ge0}L(x,\alpha,\beta)极大极小问题(对偶问题)\theta _D(\alpha ,\beta )=\underset{x\in\mathbb{R}^{n}}{min}\; L(x,\alpha,\beta)\max_{\alpha,\beta:\alpha_i\ge0}\theta _D(\alpha ,\beta )=\underset{\alpha,\beta:\alpha_i\ge0}{max}\; \underset{x\in\mathbb{R}^{n}}{min}\; L(x,\alpha,\beta)可以将广义拉格朗日函数的极大极小问题表示为约束最优化问题: \begin{align*} &\underset{\alpha,\beta}{max}\; \min_{x\in\mathbb{R}^{n}}L(x,\alpha,\beta) \\ \mathbb{s.t.}&\quad\alpha_i\ge0,\quad i=1,2,\cdots,k \end{align*}原始问题和对偶问题的关系定理1若原始问题和对偶问题都有最优值,则 \underset{x\in\mathbb{R}^{n}}{min}\; L(x,\alpha,\beta) \leqslant L(x,\alpha,\beta) \leqslant \max_{\alpha,\beta:\alpha_i\ge0}L(x,\alpha,\beta)\underset{\alpha,\beta:\alpha_i\ge0}{max}\; \underset{x\in\mathbb{R}^{n}}{min}\; L(x,\alpha,\beta) \leqslant \underset{x\in \mathbb{R}^n}{min}\;\max_{\alpha,\beta:\alpha_i\ge0}L(x,\alpha,\beta) ={min}\; f(x)推论:设 $x^{\ast}$ 和 $a^{\ast},β^{\ast}$ 分别是原始问题 $\underset{x\in \mathbb{R}^n}{min}\;\max_{\alpha,\beta:\alpha_i\ge0}L(x,\alpha,\beta) $ 和对偶问题 $\underset{\alpha,\beta:\alpha_i\ge0}{max}\; \underset{x\in\mathbb{R}^{n}}{min}\; L(x,\alpha,\beta) $ 的可行解,并且 $\underset{x\in \mathbb{R}^n}{min}\;\underset{\alpha,\beta:\alpha_i\ge0}{max}L(x,\alpha,\beta) =\underset{\alpha,\beta:\alpha_i\ge0}{max}\; \underset{x\in\mathbb{R}^{n}}{min}\; L(x,\alpha,\beta)$,则 $x^{\ast}$和 $a^{\ast}$,$β^{\ast}$ 分别是原始问题和对偶问题的最优解。 定理2:KKT条件(原始问题与对偶问题的解相等的条件)假设函数$f(x)$和$c_i(x)$是凸函数,$h_j(x)$是仿射函数,并且不等式约束$c_i(x)$是严格可行的,则$x^{\ast}$和$a^{\ast},β^{\ast}$分别是原始问题和对偶问题的解的充分必要条件是$x^{\ast},a^{\ast},β^{\ast}$满足下面的Karush-Kuhn-Tucker(KKT)条件:(判断极值、其余项为0) \begin{align*} \nabla_xL(x^*,\alpha^*,\beta^*)&=0\\ \nabla_{\alpha}L(x^*,\alpha^*,\beta^*) &=0 \\ \nabla_{\beta}L(x^*,\alpha^*,\beta^*)&=0 \end{align*}\begin{align*} \alpha_i^*c_i(\boldsymbol{x}^*)&=0,\quad i=1,2,\cdots,k\\ c_i(\boldsymbol{x}^*)&\le0,\quad i=1,2,\cdots,k \\ \alpha_i^*&\ge0,\quad i=1,2,\cdots,k \\ h_j(\boldsymbol{x}^*)&=0,\quad j=1,2,\cdots,l \end{align*} 仿射函数f(x)=A\cdot x+b仿射函数就是一个线性函数,其输入是$n$ 维向量,参数 $A$ 可以是常数,也可以是 $m×n$ 的矩阵,$b$ 可以是常数,也可以是 $m$ 维的列向量,输出是一个 $m $维的列向量。在几何上,仿射函数是一个线性空间到另一个线性空间的变换。]]></content>
<categories>
<category>人工智能</category>
<category>优化算法</category>
</categories>
<tags>
<tag>优化</tag>
</tags>
</entry>
<entry>
<title><![CDATA[凸集,凸函数和凸优化]]></title>
<url>%2F2017%2F08%2F18%2F%E6%95%B0%E5%AD%A6%E5%9F%BA%E7%A1%80%2F%E9%AB%98%E6%95%B0%2F%E5%87%B8%E9%9B%86%EF%BC%8C%E5%87%B8%E5%87%BD%E6%95%B0%E5%92%8C%E5%87%B8%E4%BC%98%E5%8C%96%2F</url>
<content type="text"><![CDATA[凸优化也可以解释为目标函数 $f(x)$ 为凸函数而起约束围成的可行域为一个凸集。 凸集对于集合 $K$ ,$\forall x_1,x_2 \in K$,若 $\alpha x_1 + (1-\alpha)x_2 \in K$,其中$α∈[0,1])$,则 $K$ 为凸集,即集合中任意两点的连线均在凸集中,如在下图所示 有时候需要对某个凸集进行放缩转换等操作,对凸集进行以下操作后,得到的集合依然是凸集 凸集的重叠(intersection)部分任然为凸集 若 $C$ 为凸集,则aC+b = \lbrace ax+b , x \in C, \forall a, b\rbrace也为凸集 对于函数 $f(x)=Ax+b$, 若 $C$ 为凸集,则下面得到的转换也为凸集,注意这里的 $A$ 是矩阵f(C) = \lbrace f(x):x\in C\rbrace而当 $D$ 是一个凸集的时候,下面得到的转换也是凸集f^{-1}(D) = \lbrace x: f(x)\in D\rbrace这两个转换互为逆反关系 常见的凸集有下面这些(下式中 $a,x,b$ 均为向量, $A$ 为矩阵) 点(point)、线(line)、面(plane) norm ball: $\{x:||x||≤r\}$ hyperplane: $\{x:a^Tx=b\}$ halfspace: $\{x:a^Tx≤b\}$ affine space: $\{x:Ax=b\}$ polyhedron: $\{x:Ax<b\}$ ![polyheron的图像](## 凸函数的性质 凸函数有几个非常重要的性质,对于一个凸函数 $f$, 其重要性质 一阶特性(First-order characterization):f(y) \ge f(x) + \nabla f(x)(y - x) 二阶特性(Second-order characterization):函数的$∇^2f(x)$是半正定的。 Jensen不等式(Jensen’s inequality):f(E(x))≤E(f(x))这里的 $E$ 表示的是期望,这是从凸函数拓展到概率论的一个推论,这里不详细展开。 sublevel sets,即集合 $\{x:f(x)≤t\}$ 是一个凸集/凸集,凸函数和凸优化-a587ad27.png) 凸函数凸函数的定义如下 设 $f(x)$ 为定义在 $n$ 维欧氏空间中某个凸集 $S$ 上的函数,若对于任何实数 $α(0<α<1)$ 以及 $S$ 中的任意不同两点 $x$ 和 $y$,均有 f(\alpha x^{(1)}+ (1-\alpha)x^{(2)}) \le \alpha f(x^{(1)}) + (1-\alpha)f(x^{(2)})则称 $f(x)$ 为定义在凸集 $S$ 上的凸函数。假如上面不等式中的 $≤$ 改为 $<$, 则称其为严格凸函数。 判断凸函数根据凸函数的定义来判断一个函数是否为凸函数往往比较困难,这里分别通过一阶条件和二阶条件判断凸函数。 一阶条件设 $f(x)$ 在凸集 $S$上有一阶连续偏导数,则 $f(x)$ 为 $S$ 上的凸函数的充要条件为:对于 任意不同两点 $x^{(1)}$和 $x^{(2)}$,均有 f(x^{(2)}) \ge f(x^{(1)}) + \nabla f(x^{(1)})^T(x^{(2)} - x^{(1)})二阶条件设 $f(x)$ 在凸集 $S$上有二阶连续偏导数,则 $f(x)$ 为 $S$上的凸函数的充要条件为:$f(x)$ 的海塞矩阵 $∇^2f(x)$在 $S$ 上处处半正定(为凹函数的充要条件为处处半负定)。注意:假如海塞矩阵 $∇^2f(x)$在 $S$ 上处处正定,则 $f(x)$ 为严格凸函数,但是反过来不成立。 顺序主子式的定义 海塞矩阵判断凸函数例子 凸函数的性质凸函数有几个非常重要的性质,对于一个凸函数 $f$, 其重要性质 一阶特性(First-order characterization):f(y) \ge f(x) + \nabla f(x)(y - x) 二阶特性(Second-order characterization):\nabla^2f(x) \succeq 0这里的 $⪰0$ 表示 Hessian 矩阵是半正定的。 Jensen不等式(Jensen’s inequality):f(E(x))≤E(f(x))这里的 $E$ 表示的是期望,这是从凸函数拓展到概率论的一个推论,这里不详细展开。 sublevel sets,即集合 $\{x:f(x)≤t\}$ 是一个凸集 常见的凸函数有下面这些 仿射函数( Affine function ): $a^Tx+b$ 二次函数( quadratic function),注意这里的 $Q$ 必须为半正定矩阵: $\frac{1}{2}x^TQx + b^Tx+c(Q \succeq 0)$ 最小平方误差( Least squares loss ): $||y-Ax||_2^2$ (总是凸的,因为 $A^TA$ 总是半正定的) 示性函数(Indicator function):I_C(X) = \begin{cases} 0&x \in C\\ \infty & x \notin C\end{cases} max function: $f(x) = max \lbrace x_1,…x_n \rbrace$ 范数(Norm):范数分为向量范数和矩阵范数,任意范数均为凸的,各种范数的定义如下 向量范数0范数:$||x||_0$ 向量中非零元素的个数1范数: $||x||_1 = \sum_{i=1}^n |x_i|$$p$ 范数:$||x||_p = (\sum_{i=1}^nx_i^p)^{1/p}~~(p > 1)$无穷范数: $||x||_{\infty} = max_{i=1,…n} |x_i|$ 矩阵范数核(nuclear)范数: $||X||_{tr} = \sum_{i=1}^{r}\sigma_i(X)$ , ($\sigma_i(X)$是矩阵分解后的奇异值,核范数即为矩阵所有奇异值之和)谱(spectral)范数:$||X||_{op} = max_{i=1,…r}\sigma_i(X)$, 即为最大的奇异值 凸优化对于下面的优化问题 \begin{align*} &\min_x\quad f(x)\\ &\begin{array}\\ s.t.&g_i(x) \le 0,~i=1,\ldots,m\\ &h_j(x)=0,~j=1,\ldots,r \end{array} \end{align*}当 $f(x),gi(x$ 均为凸函数, 而 $h_j(x)$ 为仿射函数(affine function)时,该优化称为凸优化,注意上面的 min 以及约束条件的符号均要符合规定。凸优化也可以解释为目标函数 $f(x)$ 为凸函数而起约束围成的可行域为一个凸集。 常见的一些凸优化问题常见的一些凸优化问题有:线性规划(linear programs),二次规划(quadratic programs),半正定规划(semidefinite programs),且 $LP∈QP∈SDP$, 即后者是包含前者的关系。 线性规划问题一般原型如下($c$为向量,$D,A$为矩阵) \begin{align*} &\min_x\quad c^Tx\\ &\begin{array}\\ s.t.&Dx \le d\\ &Ax=b \end{array} \end{align*} 二次规划问题一般原型如下(要求矩阵 $Q$ 半正定) \begin{align*} &\min_x\quad \frac{1}{2}x^TQx+c^Tx\\ &\begin{array}\\ s.t.&Dx \le d\\ &Ax=b \end{array} \end{align*} 而半正定规划问题一般原型如下(X 在这里表示矩阵) \begin{align*} &\min_X\quad CX\\ &\begin{array}\\ s.t.&A_iX \le b_i, i=1,…m\\ &X \succeq 0 \end{array} \end{align*}]]></content>
<categories>
<category>数学基础</category>
<category>高数</category>
</categories>
<tags>
<tag>凸优化</tag>
<tag>高数</tag>
</tags>
</entry>
<entry>
<title><![CDATA[矩阵的基础概念]]></title>
<url>%2F2017%2F08%2F18%2F%E6%95%B0%E5%AD%A6%E5%9F%BA%E7%A1%80%2F%E7%BA%BF%E6%80%A7%E4%BB%A3%E6%95%B0%2F%E7%9F%A9%E9%98%B5%E7%9A%84%E5%9F%BA%E7%A1%80%E6%A6%82%E5%BF%B5%2F</url>
<content type="text"><![CDATA[零向量变换后落在原点的向量的集合被称为矩阵的‘零空间’或者‘核’。 零向量一定在列空间中 对于一个满秩变换来说,唯一能在变换后落在原点的就是零向量自身 对于一个非满秩的矩阵来说,它将空间压缩到一个更低的维度上,变换后的已给向量落在零向量上,而“零空间”正是这些向量所构成的空间 对角矩阵在方阵中,对角线(从左上到右下)上的值称为对角元素。非对角元素全部为0的矩阵称为对角矩阵。对角矩阵表示的映射是沿着坐标轴伸缩,其中对角元素就是各坐标轴伸缩的倍率 矩阵的秩矩阵的秩,为变换后的空间的维数 单位矩阵方阵中,如果除了对角线(从左上到右下)上的元素为1,其余元素都为0,则该矩阵称为单位矩阵,记为 $I$ 。 $I_{n}$ 表示 $n$ 阶单位矩阵。单位矩阵表示的映射是“什么都不做”的映射。 I_{n}=\begin{bmatrix} 1 & 0 & \cdots & 0\\ 0 & 1 & \cdots & 0\\ \vdots & \vdots & \ddots & \vdots \\ 0 & 0 & \cdots & 1 \end{bmatrix}奇异矩阵行列式为零的矩阵 逆矩阵AA^{-1}=A^{-1}A=I零矩阵所有元素都为0的矩阵称为零矩阵,记为 $O$ 。零矩阵表示的映射是将所有的点都映射到原点的映射 行列式线性变换的行列式即线性变换改变面积的比例。 det(M_1M_2) = det(M_1)det(M_2) 检验一个矩阵的行列式是否为0,就能了解这个矩阵所代表的变换是否将空间压缩到更小的维度上 在三维空间下,行列式可以简单看作这个平行六面体的体积,行列式为0则意味着整个空间被压缩为零体积的东西,也就是一个平面或者一条直线,或者更极端情况下的一个点 特征分解如果说一个向量 $v$ 是方阵 $A$ 的特征向量,将一定可以表示成下面的形式: Av=\lambda v$\lambda$ 为特征向量 $v$ 对应的特征值。特征值分解是将一个矩阵分解为如下形式: A=Q\Sigma Q^{-1}其中, $Q$ 是这个矩阵 $A$ 的特征向量组成的矩阵, $\Sigma$ 是一个对角矩阵,每一个对角线元素就是一个特征值,里面的特征值是由大到小排列的,这些特征值所对应的特征向量就是描述这个矩阵变化方向(从主要的变化到次要的变化排列)。也就是说矩阵A的信息可以由其特征值和特征向量表示。 对于矩阵为高维的情况下,那么这个矩阵就是高维空间下的一个线性变换。可以想象,这个变换也同样有很多的变换方向,我们通过特征值分解得到的前N个特征向量,那么就对应了这个矩阵最主要的N个变化方向。我们利用这前N个变化方向,就可以近似这个矩阵(变换)。 总结一下,特征值分解可以得到特征值与特征向量,特征值表示的是这个特征到底有多重要,而特征向量表示这个特征是什么。不过,特征值分解也有很多的局限,比如说变换的矩阵必须是方阵。 奇异值分解特征值分解是一个提取矩阵特征很不错的方法,但是它只是对方阵而言的,在现实的世界中,我们看到的大部分矩阵都不是方阵,比如说有N个学生,每个学生有M科成绩,这样形成的一个 $N \ast M $的矩阵就不可能是方阵,我们怎样才能描述这样普通的矩阵呢的重要特征呢?奇异值分解可以用来干这个事情,奇异值分解是一个能适用于任意的矩阵的一种分解的方法: 分解形式:假设A是一个 $M*N$ 的矩阵,那么得到的U是一个$M\ast M$ 的方阵(称为左奇异向量),$Σ$ 是一个 $M\ast N$ 的矩阵(除了对角线的元素都是0,对角线上的元素称为奇异值),$V^T$ ($V$的转置)是一个 $N\ast N$ 的矩阵(称为右奇异向量)。 LU分解给定矩阵 $A$,将$A$表示成下三角矩阵L和上三角矩阵U的乘积,称为LU分解]]></content>
<categories>
<category>数学基础</category>
<category>线性代数</category>
</categories>
<tags>
<tag>线性代数</tag>
<tag>矩阵</tag>
</tags>
</entry>
<entry>
<title><![CDATA[线性方程求解]]></title>
<url>%2F2017%2F08%2F18%2F%E6%95%B0%E5%AD%A6%E5%9F%BA%E7%A1%80%2F%E7%BA%BF%E6%80%A7%E4%BB%A3%E6%95%B0%2F%E7%BA%BF%E6%80%A7%E6%96%B9%E7%A8%8B%E6%B1%82%E8%A7%A3%2F</url>
<content type="text"><![CDATA[\begin{align*} 2x+5y+3z &= -3\\ 4x+8z &= 0\\ x+3y &= 2 \end{align*}\Leftrightarrow \overset{A}{\overbrace{\begin{bmatrix} 2 & 5 & 3\\ 4 & 0 & 8\\ 1 & 3 & 0 \end{bmatrix}}}\overset{\overrightarrow{x}}{\overbrace{\begin{bmatrix} x\\ y\\ z \end{bmatrix}}}=\overset{\overrightarrow{v}}{\overbrace{\begin{bmatrix} -3\\ 0\\ 2 \end{bmatrix}}}A\overrightarrow{x}=\overrightarrow{v}可以看做向量 $\overrightarrow{x}$ 经过空间变换后变为向量 $\overrightarrow{v}$ ,矩阵 $A$ 的变换过程有两种情况 空间变换之后,空间没有发生压缩。即$det(A)\neq0$ ,矩阵 $A$ 满秩 空间变换之后,空间发生压缩,即$det(A)=0$ ,矩阵 $A$ 不是满秩 空间变换之后,空间没有发生压缩空间变换之后,空间没有发生压缩。即 $det(A)\neq0$ ,矩阵 $A$ 满秩。 \begin{align*} A\overrightarrow{x}&=\overrightarrow{v} \\ \overrightarrow{x} &= A^{-1}\overrightarrow{v} \end{align*} 空间变换之后,空间发生压缩空间变换之后,空间发生压缩,即 $det(A)=0$ ,矩阵A不是满秩。此时你不能通过矩阵的逆变换将空间从一个低维状态复原到高维状态(即原空间)。 此时方程的解如下,当 $\overrightarrow{v}$ 在压缩的平面上方程有解,此时方程组可能有无穷个解;当 $\overrightarrow{v}$ 不在压缩的平面上方程无解]]></content>
<categories>
<category>数学基础</category>
<category>线性代数</category>
</categories>
<tags>
<tag>线性代数</tag>
<tag>矩阵</tag>
<tag>向量</tag>
</tags>
</entry>
<entry>
<title><![CDATA[特征值与特征向量]]></title>
<url>%2F2017%2F08%2F18%2F%E6%95%B0%E5%AD%A6%E5%9F%BA%E7%A1%80%2F%E7%BA%BF%E6%80%A7%E4%BB%A3%E6%95%B0%2F%E7%89%B9%E5%BE%81%E5%80%BC%E4%B8%8E%E7%89%B9%E5%BE%81%E5%90%91%E9%87%8F%2F</url>
<content type="text"><![CDATA[定义特征向量矩阵在空间变换过程中,没有发生旋转的向量。因此特征向量可以看过空间变换的旋转轴。 特征值特征向量在变换中拉伸或压缩比例的因子 计算$\vec{v}$为特征向量,$\lambda $为特征值。求解如下所示: \begin{align*} A\vec{v}&=\lambda \vec{v} \\ A\vec{v}- \lambda \vec{v} &= \vec{0}\\ (A- \lambda I)\vec{v} &= \vec{0} \end{align*}存在一个非零向量 $\vec{v}$ 使得 $(A- \lambda I)\vec{v} = \vec{0}$ 则 det(A- \lambda I)=0求出$\lambda $即特征值,带入特征值$\lambda $解得特征向量$\vec{v}$。 对角矩阵对角矩阵的所有基向量都是特征向量 \begin{bmatrix} -5 & 0 & 0 & 0\\ 0 & -2 & 0 & 0\\ 0 & 0 & -4 & 0\\ 0 & 0 & 0 & 4 \end{bmatrix}对角矩阵的优势 对角矩阵的使用求出特征向量,利用基变换将原空间变换为以特征向量为基的空间,再利用基变换将以特征向量为基的空间变换为原空间]]></content>
<categories>
<category>数学基础</category>
<category>线性代数</category>
</categories>
<tags>
<tag>线性代数</tag>
<tag>向量</tag>
</tags>
</entry>
<entry>
<title><![CDATA[正定二次型和正定矩阵]]></title>
<url>%2F2017%2F08%2F18%2F%E6%95%B0%E5%AD%A6%E5%9F%BA%E7%A1%80%2F%E7%BA%BF%E6%80%A7%E4%BB%A3%E6%95%B0%2F%E6%AD%A3%E5%AE%9A%E4%BA%8C%E6%AC%A1%E5%9E%8B%E5%92%8C%E6%AD%A3%E5%AE%9A%E7%9F%A9%E9%98%B5%2F</url>
<content type="text"><![CDATA[二次型矩阵对于一个方阵$A∈ R^{n×n}$和一个向量$x∈ R^n$,标量$x^TAx$被称作一个二次型。显式地写出来, x^TAx=\sum_{i=1}^{n}x_i(Ax)_i=\sum_{i=1}^{n}x_i\left ( \sum_{j=1}^{n}A_{ij}x_j \right )=\sum_{i=1}^{n}\sum_{j=1}^{n}A_{ij}x_ix_j上式实际为一个二次多项式例如: \left [ x_1 x_2 \right ] \begin{bmatrix} 1 & 3\\ 2 & 4 \end{bmatrix}\begin{bmatrix} x_1\\ x_2 \end{bmatrix}=[x_1,x_2] \begin{bmatrix} x_1+3x_2\\ 2x_1+4x_2 \end{bmatrix}=x_1^2+5x_1x_2+4x_2^2 分类对于任一个n元实二次型 $f(x_1,x_2,\cdots ,x_n)=X^TAX$,作为一个 $n$ 元二次齐次多项式,我们往往需要考虑它的取值问题,显然当 $x_1=x_2=\cdots=x_n=0$ 时,二次型 $f$ 的值为 $f(0,0,\cdots,0)=0$ ,下面我们根据当 $x_1,x_2,\cdots,x_n$ 取不全为零的 $n$ 个数时,即当 $X$ 为任一个非零列向量时,取值的不同情况,给出以下定义: 定义2.1:设有n元实二次型 $f(x_1,x_2,\cdots ,x_n)=X^TAX$。 如果对于任何非零列向量 $X$ ,都有 $X^TAX>0$,则称为正定二次型,称对称矩阵 $A$ 为正定矩阵。 如果对于任何非零列向量 $X$ ,都有 $X^TAX\geqslant 0$,则称为半正定二次型,称 $A$ 为半正定矩阵。 如果对于任何非零列向量 $X$ ,都有 $X^TAX<0$,则称为负定二次型,称 $A$ 为负定矩阵。 如果对于任何非零列量 $X$ ,都有 $X^TAX\leqslant 0$,则称为半负定二次型,称 $A$ 为半负定矩阵。 其它的实二次型称为不定二次型,其矩阵称为不定矩阵。 例子 正定矩阵性质 正定矩阵的行列式恒为正; 两个正定矩阵的和是正定矩阵; 正实数与正定矩阵的乘积是正定矩阵。正定等价命题 $A$是正定矩阵; $A$的一切顺序主子式均为正; $A$的一切主子式均为正; $A$的特征值均为正; 存在实可逆矩阵$C$,使$A=C’C$; 存在秩为$n$的$m×n$实矩阵$B$,使$A=B’B$; 存在主对角线元素全为正的实三角矩阵$R$,使$A=R’R$ 正定矩阵判别方法 求出A的所有特征值。若A的特征值均为正数,则A是正定的;若A的特征值均为负数,则A为负定的。 计算A的各阶顺序主子式。若A的各阶顺序主子式均大于零,则A是正定的;若A的各阶顺序主子式中,奇数阶主子式为负,偶数阶为正,则A为负定的。 半正定矩阵性质 半正定矩阵的行列式是非负的; 两个半正定矩阵的和是半正定的; 非负实数与半正定矩阵的数乘矩阵是半正定的。半正定等价命题 $A$是是半正定的; $A$的一切顺序主子式均为非负的; $A$的一切主子式均为非负的; $A$的特征值均为正; 存在$n$阶实矩阵$C$,使$A=C’C$; 存在秩为$r$的$r×n$实矩阵$B$,使$A=B’B$]]></content>
<categories>
<category>数学基础</category>
<category>线性代数</category>
</categories>
<tags>
<tag>线性代数</tag>
<tag>矩阵</tag>
</tags>
</entry>
<entry>
<title><![CDATA[行列式]]></title>
<url>%2F2017%2F08%2F18%2F%E6%95%B0%E5%AD%A6%E5%9F%BA%E7%A1%80%2F%E7%BA%BF%E6%80%A7%E4%BB%A3%E6%95%B0%2F%E8%A1%8C%E5%88%97%E5%BC%8F%2F</url>
<content type="text"><![CDATA[行列式行列式在数学中,是一个函数,其定义域为det的矩阵A,取值为一个标量,写作$det(A)$或 $| A |$。对于行列式的几何描述如下所示: 行列式求得是矩阵的面积。 行列式等于0时候,说明矩阵的空间操作把空间压缩为低维空间 行列式为负数的时候,说明空间发生了翻转 方阵特征值之积等于行列式值也可以如下这样理解 行列式的计算]]></content>
<categories>
<category>数学基础</category>
<category>线性代数</category>