-
Notifications
You must be signed in to change notification settings - Fork 0
/
atom.xml
545 lines (318 loc) · 224 KB
/
atom.xml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
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
<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
<title>hushhw × Wiki</title>
<link href="/atom.xml" rel="self"/>
<link href="https://wiki.hushhw.cn/"/>
<updated>2021-02-03T06:56:56.832Z</updated>
<id>https://wiki.hushhw.cn/</id>
<author>
<name>hushhw</name>
</author>
<generator uri="http://hexo.io/">Hexo</generator>
<entry>
<title>iOS 砸壳总结</title>
<link href="https://wiki.hushhw.cn/posts/125bcd75.html"/>
<id>https://wiki.hushhw.cn/posts/125bcd75.html</id>
<published>2020-01-16T10:15:09.000Z</published>
<updated>2021-02-03T06:56:56.832Z</updated>
<content type="html"><![CDATA[<h2 id="dumpdecrypted-砸壳"><a href="#dumpdecrypted-砸壳" class="headerlink" title="dumpdecrypted 砸壳"></a>dumpdecrypted 砸壳</h2><h3 id="1-应用场景"><a href="#1-应用场景" class="headerlink" title="1. 应用场景"></a>1. 应用场景</h3><p>应用上传 appstore 后,苹果会对应用的代码部分进行加密,当应用运行的时候才会动态解密,所以市面上的应用即使我们可以拿到 ipa 包也无法对二进制程序进行分析,这时可以使用 dumpdecrypted 时dump 内存中解密后的代码。</p><h3 id="2-环境配置"><a href="#2-环境配置" class="headerlink" title="2. 环境配置"></a>2. 环境配置</h3><div class="table-container"><table><thead><tr><th style="text-align:center">手机</th><th>越狱设备(iOS11以下越狱比较好做)</th></tr></thead><tbody><tr><td style="text-align:center">Cydia 依赖</td><td>安装 cycript</td></tr><tr><td style="text-align:center">PC 端</td><td>正常mac环境 + ssh client</td></tr><tr><td style="text-align:center">PC 端依赖</td><td>Classdump下载 + xcode</td></tr></tbody></table></div><h3 id="3-砸壳步骤"><a href="#3-砸壳步骤" class="headerlink" title="3. 砸壳步骤"></a>3. 砸壳步骤</h3><ol><li><p>打开一个 mac 终端窗口用于生成 <code>dumpdecrypted.dylib</code> 动态库文件(砸壳的锤子)</p><ol><li>终端命令:git clone <a href="https://github.com/stefanesser/dumpdecrypted.git" target="_blank" rel="noopener">https://github.com/stefanesser/dumpdecrypted.git</a></li><li>cd dumpdecrypted 文件目录下执行命令 <code>make</code> 既可。</li></ol></li><li><p>ssh 连接手机</p><ol><li>方法一:iOS 8 和 iOS 9 的越狱设备在手机上安装 OpenSSH,在 Wifi 设置中当前连接 Wi-Fi 获取到 IP 地址,如:10.28.173.46。再打开一个 mac 终端窗口用于连接手机,输入:ssh [email protected],回车后输入”yes”,再回车输入密码”qwert”(默认密码为“alpine”)。</li><li>方法二:通过 USB 登录。打开一个 mac 终端安装 libimobiledevice,输入:brew install libimobiledevice,安装完成后使用里面提供的工具 iproxy 把本地端口 2222 映射到设备的 TCP 端口 22,执行命令:iproxy 2222 22,当看到 waiting for connection 时,另开一个 Mac 终端窗口,执行 ssh root@localhost -p 2222 来连接手机,后面同样输入密码等操作。</li></ol></li><li><p>查找要反编译的 app 的路径,在连接到手机的 mac 终端窗口进行下面操作:</p><ol><li><p>ps -e 获取手机中当前运行的进程</p></li><li><p>抓取手机上运行的 APP 进程:</p><ol><li>直接执行 ps -A | grep mobile 命令,可自动抓取手机上运行的 APP进程</li><li>或者知道应用的名称,直接执行 ps -e | grep PostalSavingsBankOfChina (这里以邮政银行为例)来抓取该应用进程。若不知道应用名称,可以在所有进程中找到带有 <code>bundle</code> 路径的进程,该进程就是我们正在打开的应用,路径最后即该应用的名称。</li></ol></li><li><p>使用 cycript 找出反编译 APP 的 Documents 目录路径:</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">cycript -p PostalSavingsBankOfChina </span><br><span class="line">cy# directory = NSHomeDirectory()</span><br></pre></td></tr></table></figure><p>这样就得到了 Data 目录,在后面加上 /Documents 即可。</p></li><li><p>拷贝 dumpdecrypted.dylib 到 iPhone 该应用的 Documents 文件夹下:</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">scp /users/bupt/test1127/dumpdecrypted/dumpdecrypted.dylib [email protected]:/var/mobile/Containers/Data/Application/BD1E0C3F-6C65-4A2B-B881-2BBA9E3B3594/Documents</span><br></pre></td></tr></table></figure></li><li><p>砸壳,打开连接到 iPhone 的终端窗口,cd 到 Data 路径的 Document 目录前面放 dumpdecrypted.dylib 文件夹下,执行命令:</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">DYLD_INSERT_LIBRARIES=dumpdecrypted.dylib /var/containers/Bundle/Application/AB511F0D-D3D4-46A6-86BD-DFB017F07A87/PostalSavingsBankOfChina.app/PostalSavingsBankOfChina</span><br></pre></td></tr></table></figure><p>砸壳成功后,用 ls 命令查看 PostalSavingsBankOfChina.decrypted 文件即我们要破解的文件。</p></li><li><p>拷贝生成的 decryption 文件到电脑</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">scp -r [email protected]:/var/mobile/Containers/Data/Application/BD1E0C3F-6C65-4A2B-B881-2BBA9E3B3594/Documents/PostalSavingsBankOfChina.decrypted /Users/bupt/Desktop/PostalSavingsBankOfChina</span><br></pre></td></tr></table></figure></li></ol></li></ol><h2 id="frida-ios-dump-砸壳使用总结"><a href="#frida-ios-dump-砸壳使用总结" class="headerlink" title="frida-ios-dump 砸壳使用总结"></a>frida-ios-dump 砸壳使用总结</h2><p> 该工具基于 frida 提供的强大功能通过注入 js 实现内存 dump 然后通过 python 自动拷贝到电脑生成 ipa 文件。</p><h3 id="1-越狱手机配置"><a href="#1-越狱手机配置" class="headerlink" title="1. 越狱手机配置"></a>1. 越狱手机配置</h3><p> 1、打开<code>cydia</code>添加源:<a href="http://build.frida.re" target="_blank" rel="noopener">http://build.frida.re</a> 并在搜索中下载安装<code>frida</code>。</p><p> 2、安装完成后在<code>Mac</code>端执行<code>frida-ps -U</code>查看是否可以工作。</p><h3 id="2-Mac-配置"><a href="#2-Mac-配置" class="headerlink" title="2. Mac 配置"></a>2. Mac 配置</h3><p> 安装 <code>frida</code>:</p> <figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">sudo pip install frida –-upgrade –-ignore-installed six</span><br></pre></td></tr></table></figure><p> 克隆项目<a href="https://github.com/AloneMonkey/frida-ios-dump" target="_blank" rel="noopener">frida-ios-dump</a>:</p> <figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">git clone https://github.com/AloneMonkey/frida-ios-dump</span><br></pre></td></tr></table></figure><p> cd 到 <code>frida-ios-dump</code> 文件夹下,安装脚本依赖环境,执行:</p> <figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">sudo pip install -r requirements.txt --upgrade</span><br></pre></td></tr></table></figure><p> 修改 <code>dump.py</code> 配置:</p> <figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">User = <span class="string">'root'</span></span><br><span class="line">Password = <span class="string">'qwert'</span> <span class="comment">#我们修改了ssh默认密码,故需要修改</span></span><br><span class="line">Host = <span class="string">'localhost'</span></span><br><span class="line">Port = <span class="number">2222</span></span><br></pre></td></tr></table></figure><h3 id="3-使用"><a href="#3-使用" class="headerlink" title="3. 使用"></a>3. 使用</h3><p> 默认采用 USB 方式连接 ssh,将<code>22</code>映射到电脑上的<code>2222端口</code>:</p> <figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">iproxy 2222 22</span><br></pre></td></tr></table></figure><p> 到 frida-ios-dump 文件夹下执行<code>dump.py -l</code>,查看安装的应用的名字和bundle id:</p> <figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">python dump.py -l</span><br></pre></td></tr></table></figure><p> 执行<code>dump.py 应用名或包名</code>,即可砸壳生成 ipa 文件在当前目录下。例如:</p> <figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">python dump.py 个人所得税</span><br></pre></td></tr></table></figure> <figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">python dump.py cn.gov.tax.its</span><br></pre></td></tr></table></figure><p> 生成的 ipa 文件需解压后打开包内容查看同名文件,该文件仅为砸壳成功的 arm64 位。</p>]]></content>
<summary type="html">
本文总结iOS中应用逆向砸壳的一些方法,包括dumpdecrypted砸壳方法和支持版本更高的frida-ios-dump 砸壳方法。
</summary>
<category term="移动安全" scheme="https://wiki.hushhw.cn/categories/%E7%A7%BB%E5%8A%A8%E5%AE%89%E5%85%A8/"/>
<category term="iOS安全" scheme="https://wiki.hushhw.cn/categories/%E7%A7%BB%E5%8A%A8%E5%AE%89%E5%85%A8/iOS%E5%AE%89%E5%85%A8/"/>
<category term="iOS逆向" scheme="https://wiki.hushhw.cn/tags/iOS%E9%80%86%E5%90%91/"/>
<category term="移动安全" scheme="https://wiki.hushhw.cn/tags/%E7%A7%BB%E5%8A%A8%E5%AE%89%E5%85%A8/"/>
<category term="iOS安全" scheme="https://wiki.hushhw.cn/tags/iOS%E5%AE%89%E5%85%A8/"/>
</entry>
<entry>
<title>常见 APK 恶意行为的代码特征总结</title>
<link href="https://wiki.hushhw.cn/posts/5b0e059c.html"/>
<id>https://wiki.hushhw.cn/posts/5b0e059c.html</id>
<published>2020-01-08T19:31:51.000Z</published>
<updated>2021-02-03T06:56:56.832Z</updated>
<content type="html"><![CDATA[<h3 id="隐藏应用图标"><a href="#隐藏应用图标" class="headerlink" title="隐藏应用图标"></a>隐藏应用图标</h3><p>扣费类应用:VT-Ubsod- 0e5b5fa86172554a3b9c5ebea668a39c649bf8c6badd4e12e1829f59993e1690</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">this</span>.a.getPackageManager().setComponentEnabledSetting(<span class="keyword">new</span> ComponentName(<span class="string">"mob.wu.hece.fofocuwera"</span>, <span class="string">"mob.wu.hece.fofocuwera.vawogavovix"</span>), <span class="number">2</span>, <span class="number">1</span>);</span><br></pre></td></tr></table></figure><p>使用到的敏感 API 为:</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">abstract</span> <span class="keyword">void</span> <span class="title">setComponentEnabledSetting</span> <span class="params">(ComponentName componentName, <span class="keyword">int</span> newState, <span class="keyword">int</span> flags)</span></span></span><br></pre></td></tr></table></figure><ul><li>componentName:组件名称 </li><li>newState:组件新的状态,可以设置三个值,分别是如下: <ul><li>默认状态:COMPONENT_ENABLED_STATE_DEFAULT </li><li><strong>显示应用图标</strong>:COMPONENT_ENABLED_STATE_ENABLED </li><li><strong>隐藏应用图标</strong>:COMPONENT_ENABLED_STATE_DISABLED </li></ul></li><li>flags:行为标签,值可以是DONT_KILL_APP或者0。 0说明杀死包含该组件的app</li></ul><h3 id="设备是否-root"><a href="#设备是否-root" class="headerlink" title="设备是否 root"></a>设备是否 root</h3><p>扣费类应用:VT-Ubsod- 0e5b5fa86172554a3b9c5ebea668a39c649bf8c6badd4e12e1829f59993e1690</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 方法一</span></span><br><span class="line"><span class="keyword">if</span>(v2_1 == <span class="number">0</span>) {</span><br><span class="line"> String[] v3 = <span class="keyword">new</span> String[v6];</span><br><span class="line"> v3[<span class="number">0</span>] = <span class="string">"/system/app/Superuser.apk"</span>;</span><br><span class="line"> v3[<span class="number">1</span>] = <span class="string">"/sbin/su"</span>;</span><br><span class="line"> v3[<span class="number">2</span>] = <span class="string">"/system/bin/su"</span>;</span><br><span class="line"> v3[<span class="number">3</span>] = <span class="string">"/system/xbin/su"</span>;</span><br><span class="line"> v3[<span class="number">4</span>] = <span class="string">"/data/local/xbin/su"</span>;</span><br><span class="line"> v3[<span class="number">5</span>] = <span class="string">"/data/local/bin/su"</span>;</span><br><span class="line"> v3[<span class="number">6</span>] = <span class="string">"/system/sd/xbin/su"</span>;</span><br><span class="line"> v3[<span class="number">7</span>] = <span class="string">"/system/bin/failsafe/su"</span>;</span><br><span class="line"> v3[<span class="number">8</span>] = <span class="string">"/data/local/su"</span>;</span><br><span class="line"> v3[<span class="number">9</span>] = <span class="string">"/su/bin/su"</span>;</span><br><span class="line"> v2_1 = <span class="number">0</span>;</span><br><span class="line">}</span><br><span class="line"><span class="comment">// 方法二</span></span><br><span class="line"><span class="keyword">try</span> {</span><br><span class="line"> v2 = Runtime.getRuntime().exec(<span class="keyword">new</span> String[]{<span class="string">"/system/xbin/which"</span>, <span class="string">"su"</span>}); </span><br><span class="line">}</span><br></pre></td></tr></table></figure><h4 id="1-检查是否存在Superuser-apk"><a href="#1-检查是否存在Superuser-apk" class="headerlink" title="1. 检查是否存在Superuser.apk"></a>1. 检查是否存在Superuser.apk</h4><p>Superuser.apk 是一个被广泛使用的用来 root 安卓设备的软件,所以可以检查这个 app 是否存在。</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">boolean</span> <span class="title">checkSuperuserApk</span><span class="params">()</span></span>{</span><br><span class="line"> <span class="keyword">try</span> {</span><br><span class="line"> File file = <span class="keyword">new</span> File(<span class="string">"/system/app/Superuser.apk"</span>);</span><br><span class="line"> <span class="keyword">if</span> (file.exists()) {</span><br><span class="line"> Log.i(LOG_TAG,<span class="string">"/system/app/Superuser.apk exist"</span>);</span><br><span class="line"> <span class="keyword">return</span> <span class="keyword">true</span>;</span><br><span class="line"> }</span><br><span class="line"> } <span class="keyword">catch</span> (Exception e) { }</span><br><span class="line"> <span class="keyword">return</span> <span class="keyword">false</span>;</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h4 id="2-检查-su-命令"><a href="#2-检查-su-命令" class="headerlink" title="2. 检查 su 命令"></a>2. 检查 su 命令</h4><p>su 是 Linux 下切换用户的命令,在使用时不带参数,就是切换到超级用户。通常我们获取 root 权限,就是使用su命令来实现的,所以可以检查这个命令是否存在。</p><ul><li>检测在常用目录下是否存在 su,那么就有可能漏过不常用的目录。</li><li>使用 which 命令查看是否存在 su,which 是 linux 下的一个命令,可以在系统 PATH 变量指定的路径中搜索某个系统命令的位置并且返回第一个搜索结果。</li><li>执行su,看能否获取到root权限,但是在已经root的设备上,会弹出提示框,请求给app开启root权限。这个提示不太友好,可能用户会不喜欢。</li></ul><h3 id="收集、泄漏手机信息"><a href="#收集、泄漏手机信息" class="headerlink" title="收集、泄漏手机信息"></a>收集、泄漏手机信息</h3><p>扣费类应用:VT-Ubsod- 0e5b5fa86172554a3b9c5ebea668a39c649bf8c6badd4e12e1829f59993e1690</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">static</span> String <span class="title">a</span><span class="params">(Activity arg4)</span> </span>{</span><br><span class="line">String v0_2;</span><br><span class="line">Object v0 = arg4.getSystemService(<span class="string">"phone"</span>);</span><br><span class="line"> <span class="keyword">try</span> {</span><br><span class="line">JSONObject v1 = <span class="keyword">new</span> JSONObject();</span><br><span class="line"> v1.put(<span class="string">"STREAM"</span>, <span class="string">"myandroid"</span>);</span><br><span class="line"> v1.put(<span class="string">"VERSION"</span>, Build$VERSION.SDK_INT);</span><br><span class="line"> <span class="keyword">if</span>(BaweqCenejaxe_关闭WIFI并打开移动数据网络.b != <span class="keyword">null</span>) {</span><br><span class="line"> v1.put(<span class="string">"ANDROID_ID"</span>, BaweqCenejaxe_关闭WIFI并打开移动数据网络.b);</span><br><span class="line"> }</span><br><span class="line"><span class="keyword">if</span>(arg4.getString(<span class="number">2131099669</span>) != <span class="keyword">null</span>) {</span><br><span class="line">v1.put(<span class="string">"APP_NAME"</span>, arg4.getString(<span class="number">2131099669</span>));</span><br><span class="line"> }</span><br><span class="line"><span class="keyword">if</span>(Build.MODEL != <span class="keyword">null</span>) {</span><br><span class="line">v1.put(<span class="string">"MODEL"</span>, Build.MODEL);</span><br><span class="line"> }</span><br><span class="line"><span class="keyword">if</span>(Build$VERSION.SDK_INT < <span class="number">23</span> && ((TelephonyManager)v0).getDeviceId() != <span class="keyword">null</span>) {</span><br><span class="line">v1.put(<span class="string">"IMEI"</span>, ((TelephonyManager)v0).getDeviceId());</span><br><span class="line"> </span><br><span class="line">v0_2 = v1.toString();</span><br><span class="line"> }</span><br></pre></td></tr></table></figure><p>这类行为并不一定恶意行为,因为某些正常应用也会进行收集。</p><p>要获取手机硬件信息,首先获得 SystemServer</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">context.getSystemServer(<span class="string">"phone"</span>);</span><br></pre></td></tr></table></figure><p>然后利用 server 获取信息,重点在 SystemServer 以及一些固定 API,如 getDeviceId 等等。</p><h3 id="申请设备管理器权限"><a href="#申请设备管理器权限" class="headerlink" title="申请设备管理器权限"></a>申请设备管理器权限</h3><p>拦截马类:0A2CA97D070A04AECB6EC9B1DA5CD987.apk</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@TargetApi</span>(value=<span class="number">8</span>) <span class="function"><span class="keyword">private</span> <span class="keyword">void</span> <span class="title">a</span><span class="params">()</span> </span>{</span><br><span class="line"> Object v0 = <span class="keyword">this</span>.getSystemService(<span class="string">"device_policy"</span>); <span class="comment">// 获取安全管理服务</span></span><br><span class="line"> ComponentName v1 = <span class="keyword">new</span> ComponentName(((Context)<span class="keyword">this</span>), PAReceiver<span class="class">.<span class="keyword">class</span>)</span>;</span><br><span class="line"> <span class="keyword">if</span>(!((DevicePolicyManager)v0).isAdminActive(v1)) { <span class="comment">// 若设备不具备设备管理权限,执行</span></span><br><span class="line">Intent v0_1 = <span class="keyword">new</span> Intent(<span class="string">"android.app.action.ADD_DEVICE_ADMIN"</span>);</span><br><span class="line"> v0_1.putExtra(<span class="string">"android.app.extra.DEVICE_ADMIN"</span>, ((Parcelable)v1));</span><br><span class="line"> v0_1.putExtra(<span class="string">"android.app.extra.ADD_EXPLANATION"</span>, <span class="keyword">this</span>.getResources().getString(<span class="number">2131034113</span>));</span><br><span class="line"> <span class="keyword">this</span>.startActivityForResult(v0_1, <span class="number">1</span>); <span class="comment">// 添加隐式意图,获取管理员权限</span></span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h3 id="发送短信"><a href="#发送短信" class="headerlink" title="发送短信"></a>发送短信</h3><figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag"><<span class="name">uses-permission</span> <span class="attr">android:name</span>=<span class="string">"android.permission.SEND_SMS"</span> /></span></span><br></pre></td></tr></table></figure><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line">PendingIntent localPendingIntent1 = PendingIntent.getBroadcast(<span class="keyword">this</span>.ctx, <span class="number">0</span>, <span class="keyword">new</span> Intent(<span class="string">"SMS_SENT"</span>), <span class="number">0</span>);</span><br><span class="line">PendingIntent localPendingIntent2 = PendingIntent.getBroadcast(<span class="keyword">this</span>.ctx, <span class="number">0</span>, <span class="keyword">new</span> Intent(<span class="string">"SMS_DELIVERED"</span>), <span class="number">0</span>);</span><br><span class="line">SmsManager localSmsManager = SmsManager.getDefault();</span><br><span class="line"><span class="keyword">try</span> {</span><br><span class="line"> localSmsManager.sendTextMessage(paramString1, <span class="keyword">null</span>, paramString2, localPendingIntent1, localPendingIntent2);</span><br><span class="line"> <span class="keyword">return</span>;</span><br><span class="line">}</span><br><span class="line"><span class="keyword">catch</span> (Exception paramString) {</span><br><span class="line"> paramString.printStackTrace();</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h3 id="读取短信"><a href="#读取短信" class="headerlink" title="读取短信"></a>读取短信</h3><p>申请权限:</p><figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag"><<span class="name">uses-permission</span> <span class="attr">android:name</span>=<span class="string">"android.permission.SEND_SMS"</span> /></span></span><br><span class="line"><span class="tag"><<span class="name">uses-permission</span> <span class="attr">android:name</span>=<span class="string">"android.permission.RECEIVE_SMS"</span> /></span></span><br></pre></td></tr></table></figure><p>通过短信数据库获取短信内容,注册 SMS 监视器,监听短信数据库的变化,添加删除修改,变化的时候会回调 <strong>onChange</strong> 方法,提取刚刚变化的那条短信的内容。</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">v0.registerContentObserver(Uri.parse(<span class="string">"content://sms"</span>), <span class="keyword">true</span>, <span class="keyword">this</span>.f); <span class="comment">// 注册SMS监视器</span></span><br></pre></td></tr></table></figure><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">public</span> List <span class="title">a</span><span class="params">()</span> </span>{</span><br><span class="line"> ArrayList v6 = <span class="keyword">new</span> ArrayList();</span><br><span class="line"> Cursor v0 = <span class="keyword">this</span>.a.getContentResolver().query(Uri.parse(<span class="string">"content://sms/"</span>), <span class="keyword">new</span> String[]{<span class="string">"_id"</span>, <span class="string">"address"</span>, <span class="string">"body"</span>, <span class="string">"date"</span>, <span class="string">"type"</span>}, <span class="keyword">null</span>, <span class="keyword">null</span>, <span class="string">" date desc "</span>);</span><br><span class="line"> <span class="keyword">while</span>(v0.moveToNext()) {</span><br><span class="line"> e v1 = <span class="keyword">new</span> e();</span><br><span class="line"> String v2 = v0.getString(<span class="number">0</span>);</span><br><span class="line"> String v3 = v0.getString(<span class="number">1</span>);</span><br><span class="line"> String v4 = v0.getString(<span class="number">2</span>);</span><br><span class="line"> <span class="keyword">long</span> v8 = v0.getLong(<span class="number">3</span>);</span><br><span class="line"> String v5 = v0.getString(<span class="number">4</span>);</span><br><span class="line"> String v7 = <span class="keyword">new</span> SimpleDateFormat(<span class="string">"yyyy-MM-dd hh:mm:ss"</span>).format(<span class="keyword">new</span> Date(v8));</span><br><span class="line"> v1.a(v2);</span><br><span class="line"> v1.c(v3);</span><br><span class="line"> v1.b(v4);</span><br><span class="line"> v1.d(v7);</span><br><span class="line"> v1.e(v5);</span><br><span class="line"> ((List)v6).add(v1);</span><br><span class="line"> }</span><br></pre></td></tr></table></figure><h3 id="拦截短信"><a href="#拦截短信" class="headerlink" title="拦截短信"></a>拦截短信</h3><figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag"><<span class="name">receiver</span> <span class="attr">android:name</span>=<span class="string">"com.t20.receiver.SmsReceiver"</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">intent-filter</span> <span class="attr">android:priority</span>=<span class="string">"1000"</span> ></span></span><br><span class="line"> <span class="comment"><!-- 有序广播设置优先级:priority表示优先级(0-1000),默认是500 ,1000的优先级最高--></span></span><br><span class="line"> <span class="tag"><<span class="name">action</span> <span class="attr">android:name</span>=<span class="string">"android.provider.Telephony.SMS_RECEIVED"</span>/></span></span><br><span class="line"> <span class="tag"></<span class="name">intent-filter</span>></span></span><br><span class="line"><span class="tag"></<span class="name">receiver</span>></span></span><br></pre></td></tr></table></figure><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">package</span> com.t20.receiver;</span><br><span class="line"> </span><br><span class="line"><span class="keyword">import</span> android.content.BroadcastReceiver;</span><br><span class="line"><span class="keyword">import</span> android.content.Context;</span><br><span class="line"><span class="keyword">import</span> android.content.Intent;</span><br><span class="line"><span class="keyword">import</span> android.os.Bundle;</span><br><span class="line"><span class="keyword">import</span> android.telephony.SmsMessage;</span><br><span class="line"> </span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">SmsReceiver</span> <span class="keyword">extends</span> <span class="title">BroadcastReceiver</span> </span>{</span><br><span class="line"> </span><br><span class="line"><span class="meta">@Override</span></span><br><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">onReceive</span><span class="params">(Context context, Intent intent)</span> </span>{</span><br><span class="line"><span class="comment">// TODO Auto-generated method stub</span></span><br><span class="line"><span class="comment">//1、接收短信协议</span></span><br><span class="line">Bundle bundle= intent.getExtras(); <span class="comment">//Bundle表示MAP套装(键值对)</span></span><br><span class="line"><span class="comment">//2、通过Bundle取值</span></span><br><span class="line">Object[] objs= (Object[]) bundle.get(<span class="string">"pdus"</span>);</span><br><span class="line"><span class="keyword">for</span> (Object obj : objs) {</span><br><span class="line"> <span class="comment">//3、获取短信对象</span></span><br><span class="line"> SmsMessage sms=SmsMessage.createFromPdu((<span class="keyword">byte</span>[])obj);</span><br><span class="line"> System.out.println(<span class="string">"短信联系人:"</span>+sms.getOriginatingAddress());</span><br><span class="line"> System.out.println(<span class="string">"短信内容:"</span>+sms.getDisplayMessageBody());</span><br><span class="line"> <span class="comment">//4、短信拦截(收到短信时,系统会发一个有序广播,默认优先级是500,我们可以设置短信窃听器的广播优先级为1000)</span></span><br><span class="line"> <span class="keyword">if</span>(sms.getOriginatingAddress().equals(<span class="string">"110"</span>)){</span><br><span class="line"> abortBroadcast();<span class="comment">//终止广播</span></span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line">}</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h3 id="设置为默认短信应用"><a href="#设置为默认短信应用" class="headerlink" title="设置为默认短信应用"></a>设置为默认短信应用</h3><p>通过 Telephony.Sms.getDefaultSmsPackage()方法来判断自己的应用是否为Default SMS app。</p><h3 id="删除短信"><a href="#删除短信" class="headerlink" title="删除短信"></a>删除短信</h3><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">getContentResolver().delete(Uri.parse(<span class="string">"content://sms/sms_id"</span>), <span class="keyword">null</span>, <span class="keyword">null</span>);</span><br></pre></td></tr></table></figure><h3 id="设置锁屏密码"><a href="#设置锁屏密码" class="headerlink" title="设置锁屏密码"></a>设置锁屏密码</h3><p>继承 DeviceAdminReceiver,采用了系统的设备管理器来获取权限进而修改密码,使用 resetPassword 设置密码</p><h3 id="屏蔽-back-键"><a href="#屏蔽-back-键" class="headerlink" title="屏蔽 back 键"></a>屏蔽 back 键</h3><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">boolean</span> <span class="title">onKeyDown</span><span class="params">(<span class="keyword">int</span> arg2, KeyEvent arg3)</span> </span>{</span><br><span class="line"> <span class="keyword">return</span> <span class="number">1</span>;</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h3 id="参考"><a href="#参考" class="headerlink" title="参考"></a>参考</h3><blockquote><p><a href="https://blog.csdn.net/ydt_lwj/article/details/9419239" target="_blank" rel="noopener">Android 如何隐藏应用程序的图标</a></p><p><a href="https://blog.csdn.net/lintax/article/details/70988565" target="_blank" rel="noopener">Android root检测方法小结</a></p></blockquote>]]></content>
<summary type="html">
本文总结Android常见APK中的恶意行为的代码特征,用于在进行静态分析时能更加迅速的定位到恶意行为。
</summary>
<category term="移动安全" scheme="https://wiki.hushhw.cn/categories/%E7%A7%BB%E5%8A%A8%E5%AE%89%E5%85%A8/"/>
<category term="Android安全" scheme="https://wiki.hushhw.cn/categories/%E7%A7%BB%E5%8A%A8%E5%AE%89%E5%85%A8/Android%E5%AE%89%E5%85%A8/"/>
<category term="移动安全" scheme="https://wiki.hushhw.cn/tags/%E7%A7%BB%E5%8A%A8%E5%AE%89%E5%85%A8/"/>
<category term="恶意软件" scheme="https://wiki.hushhw.cn/tags/%E6%81%B6%E6%84%8F%E8%BD%AF%E4%BB%B6/"/>
<category term="Android安全" scheme="https://wiki.hushhw.cn/tags/Android%E5%AE%89%E5%85%A8/"/>
</entry>
<entry>
<title>机器学习中评估分类器性能</title>
<link href="https://wiki.hushhw.cn/posts/f2b20d45.html"/>
<id>https://wiki.hushhw.cn/posts/f2b20d45.html</id>
<published>2019-10-21T17:33:21.000Z</published>
<updated>2021-02-03T06:56:56.832Z</updated>
<content type="html"><![CDATA[<h2 id="混淆矩阵(Confusion-Matrix)"><a href="#混淆矩阵(Confusion-Matrix)" class="headerlink" title="混淆矩阵(Confusion Matrix)"></a>混淆矩阵(Confusion Matrix)</h2><p>在机器学习领域和统计分类问题中,<strong>混淆矩阵</strong>(<strong>confusion matrix</strong>)是可视化工具,特别用于监督学习,在无监督学习一般叫做匹配矩阵。矩阵的每一列代表一个类的实例预测,而每一行表示一个实际的类的实例。之所以如此命名,是因为通过这个矩阵可以方便地看出机器是否将两个不同的类混淆了(比如说把一个类错当成了另一个)。<sup id="fnref:1"><a href="#fn:1" rel="footnote">1</a></sup></p><p>对于二分类问题,混淆矩阵为一个 2*2 的表,行代表真实值,列代表预测值,见下表:</p><div class="table-container"><table><thead><tr><th style="text-align:center">真实 \ 预测</th><th style="text-align:center">0</th><th style="text-align:center">1</th></tr></thead><tbody><tr><td style="text-align:center"><strong>0</strong></td><td style="text-align:center">预测 negative 正确(<strong>TN</strong>)</td><td style="text-align:center">预测 positive 错误(<strong>FP</strong>)</td></tr><tr><td style="text-align:center"><strong>1</strong></td><td style="text-align:center">预测 negative 错误(<strong>FN</strong>)</td><td style="text-align:center">预测 positive 正确(<strong>TP</strong>)</td></tr></tbody></table></div><p>以「患癌症问题」举例,上表中 <code>0</code> 代表未得癌症,<code>1</code> 代表得了癌症,行代表患癌症的真实值,列代表患癌症的预测值,那么,<code>TN</code> 就代表着真实情况没有得癌症且预测没有得癌症正确,同样 <code>FN</code> 代表真实情况得了癌症但预测其未得癌症。</p><p> </p><h2 id="精准率和召回率"><a href="#精准率和召回率" class="headerlink" title="精准率和召回率"></a>精准率和召回率</h2><p><strong>精准率(precision)</strong>:在所有预测值为 <code>1</code> 的情况下,实际也正确的概率。例如在癌症问题中表示预测患癌症成功的概率。公式如下:</p><script type="math/tex; mode=display">precision = \frac{TP}{TP+FP}</script><p><strong>召回率(recall)</strong>:在所有真实值为 <code>1</code> 的情况下,预测正确的概率。例如在癌症问题中表示患癌症的人群中成功预测的概率。公式如下:</p><script type="math/tex; mode=display">recall = \frac{TP}{TP+FN}</script><p>下面代码实现:</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> numpy <span class="keyword">as</span> np</span><br><span class="line"><span class="keyword">from</span> sklearn <span class="keyword">import</span> datasets</span><br><span class="line"></span><br><span class="line"><span class="comment"># 引入手写识别数据集</span></span><br><span class="line">digits = datasets.load_digits()</span><br><span class="line">X = digits.data</span><br><span class="line">y = digits.target.copy()</span><br><span class="line"></span><br><span class="line"><span class="comment"># 为了让数据集变成二分类问题,做如下处理</span></span><br><span class="line">y[digits.target == <span class="number">9</span>] = <span class="number">1</span></span><br><span class="line">y[digits.target != <span class="number">9</span>] = <span class="number">0</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">from</span> sklearn.model_selection <span class="keyword">import</span> train_test_split</span><br><span class="line">X_train, X_test, y_train, y_test = train_test_split(X, y, random_state=<span class="number">42</span>)</span><br><span class="line"></span><br><span class="line"><span class="keyword">from</span> sklearn.linear_model <span class="keyword">import</span> LogisticRegression</span><br><span class="line">log_reg = LogisticRegression()</span><br><span class="line">log_reg.fit(X_train, y_train)</span><br><span class="line">log_reg.score(X_test, y_test)</span><br><span class="line"><span class="comment"># 0.97555555555555551</span></span><br><span class="line"></span><br><span class="line">y_log_predict = log_reg.predict(X_test)</span><br><span class="line"></span><br><span class="line"><span class="comment"># TN</span></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">TN</span><span class="params">(y_true, y_predict)</span>:</span></span><br><span class="line"> <span class="keyword">assert</span> len(y_true) == len(y_predict)</span><br><span class="line"> <span class="keyword">return</span> np.sum((y_true == <span class="number">0</span>) & (y_predict == <span class="number">0</span>))</span><br><span class="line">TN(y_test, y_log_predict)</span><br><span class="line"><span class="comment"># 397</span></span><br><span class="line"><span class="comment"># FP</span></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">FP</span><span class="params">(y_true, y_predict)</span>:</span></span><br><span class="line"> <span class="keyword">assert</span> len(y_true) == len(y_predict)</span><br><span class="line"> <span class="keyword">return</span> np.sum((y_true == <span class="number">0</span>) & (y_predict == <span class="number">1</span>))</span><br><span class="line">FP(y_test, y_log_predict)</span><br><span class="line"><span class="comment"># 5</span></span><br><span class="line"><span class="comment"># FN</span></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">FN</span><span class="params">(y_true, y_predict)</span>:</span></span><br><span class="line"> <span class="keyword">assert</span> len(y_true) == len(y_predict)</span><br><span class="line"> <span class="keyword">return</span> np.sum((y_true == <span class="number">1</span>) & (y_predict == <span class="number">0</span>))</span><br><span class="line">FN(y_test, y_log_predict)</span><br><span class="line"><span class="comment"># 6</span></span><br><span class="line"><span class="comment"># TP</span></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">TP</span><span class="params">(y_true, y_predict)</span>:</span></span><br><span class="line"> <span class="keyword">assert</span> len(y_true) == len(y_predict)</span><br><span class="line"> <span class="keyword">return</span> np.sum((y_true == <span class="number">1</span>) & (y_predict == <span class="number">1</span>))</span><br><span class="line">TP(y_test, y_log_predict)</span><br><span class="line"><span class="comment">#42</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 混淆矩阵</span></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">confusion_matrix</span><span class="params">(y_true, y_predict)</span>:</span></span><br><span class="line"> <span class="keyword">return</span> np.array([</span><br><span class="line"> [TN(y_true, y_predict), FP(y_true, y_predict)],</span><br><span class="line"> [FN(y_true, y_predict), TP(y_true, y_predict)]</span><br><span class="line"> ])</span><br><span class="line">confusion_matrix(y_test, y_log_predict)</span><br><span class="line"><span class="comment">#array([[397, 5],</span></span><br><span class="line"><span class="comment"># [ 6, 42]])</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 精准率</span></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">precision_score</span><span class="params">(y_true, y_predict)</span>:</span></span><br><span class="line"> tp = TP(y_true, y_predict)</span><br><span class="line"> fp = FP(y_true, y_predict)</span><br><span class="line"> <span class="keyword">try</span>:</span><br><span class="line"> <span class="keyword">return</span> tp / (tp + fp)</span><br><span class="line"> <span class="keyword">except</span>:</span><br><span class="line"> <span class="keyword">return</span> <span class="number">0.0</span> </span><br><span class="line">precision_score(y_test, y_log_predict)</span><br><span class="line"><span class="comment"># 0.8936170212765957</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 召回率</span></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">recall_score</span><span class="params">(y_true, y_predict)</span>:</span></span><br><span class="line"> tp = TP(y_true, y_predict)</span><br><span class="line"> fn = FN(y_true, y_predict)</span><br><span class="line"> <span class="keyword">try</span>:</span><br><span class="line"> <span class="keyword">return</span> tp / (tp + fn)</span><br><span class="line"> <span class="keyword">except</span>:</span><br><span class="line"> <span class="keyword">return</span> <span class="number">0.0</span></span><br><span class="line">recall_score(y_test, y_log_predict)</span><br><span class="line"><span class="comment"># 0.875</span></span><br></pre></td></tr></table></figure><p>在 <code>Scikit-learn</code> 中实现混淆矩阵、精准率和召回率</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">from</span> sklearn.metrics <span class="keyword">import</span> confusion_matrix</span><br><span class="line">confusion_matrix(y_test, y_log_predict)</span><br><span class="line"></span><br><span class="line"><span class="keyword">from</span> sklearn.metrics <span class="keyword">import</span> precision_score</span><br><span class="line">precision_score(y_test, y_log_predict)</span><br><span class="line"></span><br><span class="line"><span class="keyword">from</span> sklearn.metrics <span class="keyword">import</span> recall_score</span><br><span class="line">recall_score(y_test, y_log_predict)</span><br></pre></td></tr></table></figure><p> </p><h2 id="F1-Score"><a href="#F1-Score" class="headerlink" title="F1 Score"></a>F1 Score</h2><p>对于精准率和召回率,在不同的场景有不同的侧重点。</p><p>在股票预测问题中,设股票增长为 <code>1</code>,我们更加关注精准率,即我们预测股票增长情况下预测正确的概率,而对于回归率我们并不太关心,因为召回率代表着实际会增长的股票我们预测到会增长的股票的概率,而增长的股票有很多,我们只是漏掉了部分会增长的股票而已,我们并没有什么损失;在病人诊断问题中,我们就更加关注召回率,即病人已经得病了能够诊断出其患病的概率,显然这时候召回率越高越好,能够不漏掉任何一个患病的病人,而精准率低一些并没有关系,即有一些人没有病被预测为有病,再继续做检查确诊就行了,不会造成巨大危害。</p><p>如果要同时关注这两个指标,就需要引入一个新的指标——<strong>F1 Score</strong></p><p>F1 Score 是精准率和召回率的调和平均值</p><script type="math/tex; mode=display">F_1 = \frac2{\frac1{precision}+\frac1{recall}} = \frac{2\times precision\times recall}{precision + recall}</script><p>下面尝试不同的精准率和召回率下,F1 Score 的值变化</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">f1_score</span><span class="params">(precision, recall)</span>:</span></span><br><span class="line"> <span class="keyword">try</span>:</span><br><span class="line"> <span class="keyword">return</span> <span class="number">2</span> * precision * recall / (precision + recall)</span><br><span class="line"> <span class="keyword">except</span>:</span><br><span class="line"> <span class="keyword">return</span> <span class="number">0.0</span></span><br><span class="line"></span><br><span class="line">precision = <span class="number">0.5</span></span><br><span class="line">recall = <span class="number">0.5</span></span><br><span class="line">f1_score(precision, recall)</span><br><span class="line"><span class="comment"># 0.5</span></span><br><span class="line"></span><br><span class="line">precision = <span class="number">0.1</span></span><br><span class="line">recall = <span class="number">0.9</span></span><br><span class="line">f1_score(precision, recall)</span><br><span class="line"><span class="comment"># 0.18000000000000002</span></span><br><span class="line"></span><br><span class="line">precision = <span class="number">0.0</span></span><br><span class="line">recall = <span class="number">1.0</span></span><br><span class="line">f1_score(precision, recall)</span><br><span class="line"><span class="comment"># 0.0</span></span><br></pre></td></tr></table></figure><p>在前面的例子中我们测得了精准率和召回率,现在使用 <code>Scikit-learn</code> 计算 f1_score:</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">from</span> sklearn.metrics <span class="keyword">import</span> confusion_matrix</span><br><span class="line">confusion_matrix(y_test, y_log_predict)</span><br><span class="line"><span class="comment">#array([[403, 2],</span></span><br><span class="line"><span class="comment"># [ 9, 36]])</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">from</span> sklearn.metrics <span class="keyword">import</span> precision_score</span><br><span class="line">precision_score(y_test, y_log_predict)</span><br><span class="line"><span class="comment"># 0.94736842105263153</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">from</span> sklearn.metrics <span class="keyword">import</span> recall_score</span><br><span class="line">recall_score(y_test, y_log_predict)</span><br><span class="line"><span class="comment"># 0.80000000000000004</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">from</span> sklearn.metrics <span class="keyword">import</span> f1_score</span><br><span class="line">f1_score(y_test, y_log_predict)</span><br><span class="line"><span class="comment"># 0.8842105263157894</span></span><br></pre></td></tr></table></figure><p>F1 Score 对那些具有相近的精准率和召回率的分类器更为有利。</p><p> </p><h2 id="Precision-recall-的平衡"><a href="#Precision-recall-的平衡" class="headerlink" title="Precision-recall 的平衡"></a>Precision-recall 的平衡</h2><p>下图中用竖线代表阈值,划分左右两边分别为预测为 0 和 1,五角星代表实际值为 1,圆代表 0。</p><p><img src="https://photo.hushhw.cn/20191021200918.png" alt></p><p>当阈值为 0 ,小于 0,大于 0 时分别不同的精准率和召回率,由此可知鱼与熊掌不可兼得。</p><p><code>Scikit-learn</code> 不允许直接设置阈值,但可以访问它用于预测的决策分数。不是调用分类器的 predict() 方法,而是调用 decision_function() 方法,这个方法返回每个实例的分数,然后就可以根据这些分数,使用任意阈值进行预测了:</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br></pre></td><td class="code"><pre><span class="line">decision_scores = log_reg.decision_function(X_test)</span><br><span class="line"></span><br><span class="line">np.min(decision_scores)</span><br><span class="line"><span class="comment"># -61.02813630853092</span></span><br><span class="line">np.max(decision_scores)</span><br><span class="line"><span class="comment"># 17.504275181503946</span></span><br><span class="line"></span><br><span class="line">y_predict_2 = np.array(decision_scores >= <span class="number">5</span>, dtype=<span class="string">'int'</span>)</span><br><span class="line">confusion_matrix(y_test, y_predict_2)</span><br><span class="line"><span class="comment">#array([[402, 0],</span></span><br><span class="line"><span class="comment"># [ 20, 28]], dtype=int64)</span></span><br><span class="line">precision_score(y_test, y_predict_2)</span><br><span class="line"><span class="comment"># 1.0</span></span><br><span class="line">recall_score(y_test, y_predict_2)</span><br><span class="line"><span class="comment"># 0.5833333333333334</span></span><br><span class="line"></span><br><span class="line">y_predict_3 = np.array(decision_scores >= <span class="number">-5</span>, dtype=<span class="string">'int'</span>)</span><br><span class="line">confusion_matrix(y_test, y_predict_3)</span><br><span class="line"><span class="comment">#array([[379, 23],</span></span><br><span class="line"><span class="comment"># [ 2, 46]], dtype=int64)</span></span><br><span class="line">precision_score(y_test, y_predict_3)</span><br><span class="line"><span class="comment"># 0.6666666666666666</span></span><br><span class="line">recall_score(y_test, y_predict_3)</span><br><span class="line"><span class="comment"># 0.9583333333333334</span></span><br></pre></td></tr></table></figure><p>下面用 for 循环拿到所有的阈值,绘制成图:</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line">precisions = []</span><br><span class="line">recalls = []</span><br><span class="line">thresholds = np.arange(np.min(decision_scores), np.max(decision_scores), <span class="number">0.1</span>)</span><br><span class="line"><span class="keyword">for</span> threshold <span class="keyword">in</span> thresholds:</span><br><span class="line"> y_predict = np.array(decision_scores >= threshold, dtype = <span class="string">'int'</span>)</span><br><span class="line"> precisions.append(precision_score(y_test, y_predict))</span><br><span class="line"> recalls.append(recall_score(y_test, y_predict))</span><br><span class="line"> </span><br><span class="line">plt.plot(thresholds, precisions)</span><br><span class="line">plt.plot(thresholds, recalls)</span><br><span class="line">plt.show()</span><br></pre></td></tr></table></figure><p><img src="https://photo.hushhw.cn/20191021205050.png" alt></p><p> </p><h2 id="Precision-Recall-曲线"><a href="#Precision-Recall-曲线" class="headerlink" title="Precision-Recall 曲线"></a>Precision-Recall 曲线</h2><p>将 Precision 和 Recall 分别放在坐标轴的 x 和 y 轴上,可以清晰的观察到两者的关系:</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">plt.plot(precisions, recalls)</span><br><span class="line">plt.show()</span><br></pre></td></tr></table></figure><p><img src="https://photo.hushhw.cn/20191021205339.png" alt></p><p>在 <code>Scikit-learn</code> 中可以直接调用 <code>precision_recall_curve</code> 方法来得到相应参数:</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">from</span> sklearn.metrics <span class="keyword">import</span> precision_recall_curve</span><br><span class="line">precisions, recalls, thresholds = precision_recall_curve(y_test, decision_scores)</span><br></pre></td></tr></table></figure><p>返回了三个参数,分别是 precisions、recalls 和 thresholds(阈值),下面看看这几个参数的元素个数:<br><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">precisions.shape</span><br><span class="line"><span class="comment"># (93,)</span></span><br><span class="line">recalls.shape</span><br><span class="line"><span class="comment"># (93,)</span></span><br><span class="line">thresholds.shape</span><br><span class="line"><span class="comment"># (92,)</span></span><br></pre></td></tr></table></figure></p><p>翻阅官方文档<sup id="fnref:2"><a href="#fn:2" rel="footnote">2</a></sup>可以知道最后一个精准率或回归率的值默认为 1 或 0,且没有 threshold。</p><p>最后用 <code>matplotlib</code> 绘制即可:</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">plt.plot(thresholds, precisons[:<span class="number">-1</span>])</span><br><span class="line">plt.plot(thresholds, recalls[:<span class="number">-1</span>])</span><br><span class="line">plt.show()</span><br></pre></td></tr></table></figure><p><img src="https://photo.hushhw.cn/20191021210859.png" alt></p><p>若绘制前面那种 precison 与 recall 的坐标系:</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">plt.plot(precisions, recalls)</span><br><span class="line">plt.show()</span><br></pre></td></tr></table></figure><p><img src="https://photo.hushhw.cn/20191021211010.png" alt></p><p>对比前面的图会发现这里只是前面的中间部分,是因为 precision_recall_curve 选择了它认为最重要的数据。</p><p> </p><h2 id="ROC-曲线"><a href="#ROC-曲线" class="headerlink" title="ROC 曲线"></a>ROC 曲线</h2><p>ROC 曲线经常与二元分类器一起使用,它与 precison-recall 曲线非常相似,但绘制的不是精准率和召回率,而是真正类率(<strong>TPR</strong>)与假正类率(<strong>FPR</strong>)。</p><p>回到前面的混淆矩阵二分类问题上,其实 TPR = recall,二者是一样的含义,而 FPR 表示在真实值为 0 的情况下,预测 为 1 的概率,公式:</p><script type="math/tex; mode=display">FPR = \frac{FP}{TN+FP}</script><p>TPR 和 FPR 二者的关系如下图,同大同小:</p><p><img src="https://photo.hushhw.cn/20191021213307.png" alt></p><p>使用 <code>Scikit-learn</code> 中的 <code>roc_curve</code> 方法可以得到想要的参数:</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">from</span> sklearn.metrics <span class="keyword">import</span> roc_curve</span><br><span class="line">fprs, tprs, thresholds = roc_curve(y_test, decision_scores)</span><br></pre></td></tr></table></figure><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">plt.plot(fprs, tprs)</span><br><span class="line">plt.show()</span><br></pre></td></tr></table></figure><p>绘制结果:</p><p><img src="https://photo.hushhw.cn/20191021213534.png" alt></p><p>ROC 曲线一般用来比较两个模型孰优孰劣,其<strong>曲线下面积(AUC)</strong>是非常重要的参数,完美的 ROC AUC 等于 1,而纯随机分类器的 ROC AUC 等于 0.5。</p><p>下面是 <code>Scikit-learn</code> 中提供的方法:</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">from</span> sklearn.metrics <span class="keyword">import</span> roc_auc_score</span><br><span class="line">roc_auc_score(y_test, decision_scores)</span><br><span class="line"><span class="comment"># 0.98304526748971188</span></span><br></pre></td></tr></table></figure><p> </p><p>完整代码:<a href="https://github.com/hushhw/Machine_Learning_Algorithms_Note/blob/master/Classification-Performance-Measures.ipynb" target="_blank" rel="noopener"><strong>Classification-Performance-Measures.ipynb</strong></a></p><div id="footnotes"><hr><div id="footnotelist"><ol style="list-style:none; padding-left: 0;"><li id="fn:1"><span style="display: inline-block; vertical-align: top; padding-right: 10px;">1.</span><span style="display: inline-block; vertical-align: top;"><a href="https://zh.wikipedia.org/wiki/%E6%B7%B7%E6%B7%86%E7%9F%A9%E9%98%B5" target="_blank" rel="noopener">维基百科</a></span><a href="#fnref:1" rev="footnote"> ↩</a></li><li id="fn:2"><span style="display: inline-block; vertical-align: top; padding-right: 10px;">2.</span><span style="display: inline-block; vertical-align: top;"><a href="https://scikit-learn.org/stable/modules/generated/sklearn.metrics.precision_recall_curve.html#sklearn.metrics.precision_recall_curve" target="_blank" rel="noopener">precision_recall_curve</a></span><a href="#fnref:2" rev="footnote"> ↩</a></li></ol></div></div>]]></content>
<summary type="html">
<h2 id="混淆矩阵(Confusion-Matrix)"><a href="#混淆矩阵(Confusion-Matrix)" class="headerlink" title="混淆矩阵(Confusion Matrix)"></a>混淆矩阵(Confusion Matri
</summary>
<category term="笔记整理" scheme="https://wiki.hushhw.cn/categories/%E7%AC%94%E8%AE%B0%E6%95%B4%E7%90%86/"/>
<category term="机器学习" scheme="https://wiki.hushhw.cn/categories/%E7%AC%94%E8%AE%B0%E6%95%B4%E7%90%86/%E6%9C%BA%E5%99%A8%E5%AD%A6%E4%B9%A0/"/>
<category term="Machine Learning" scheme="https://wiki.hushhw.cn/tags/Machine-Learning/"/>
</entry>
<entry>
<title>使用virtualenv创建隔离环境</title>
<link href="https://wiki.hushhw.cn/posts/6adc5586.html"/>
<id>https://wiki.hushhw.cn/posts/6adc5586.html</id>
<published>2019-10-20T15:05:18.000Z</published>
<updated>2021-02-03T06:56:56.832Z</updated>
<content type="html"><![CDATA[<p>如果你希望在一个隔离的环境里工作(强烈推荐,这样你可以在库版本不冲突的情况下处理不同的项目),可以通过运行以下 pip 命令来安装 <code>virtualenv</code>:</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">$ pip3 install --user --upgrade virtualenv</span><br><span class="line">Collecting virtualenv</span><br><span class="line">[...]</span><br><span class="line">Successfully installed virtualenv</span><br></pre></td></tr></table></figure><p>输入以下命令创建一个隔离的 Python 环境:</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">$ cd $ML_PATH</span><br><span class="line">$ virtualenv env</span><br><span class="line">Using base prefix '[...]'</span><br><span class="line">New Python executable in [...]/ml/env/bin/python3.5</span><br><span class="line">Also creating executable in [...]/ml/env/bin/python</span><br><span class="line">Installing setuptools, pip, wheel...done.</span><br></pre></td></tr></table></figure><p>现在开始,每当要激活这个环境时,只需要打开终端并输入:</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">$ cd $ML_PATH</span><br><span class="line">$ source env/bin/activate</span><br></pre></td></tr></table></figure><p>当这个环境处于激活状态时,你使用 pip 安装的任何软件包都将被安装在这个隔离的环境中,Python 只拥有这些包的访问权限(如果你还希望访问系统站点的软件包,则需要使用 <code>virtualenv</code> 的 <code>`--system-site-packages</code> 命令选项)。更多详情可以参考 <a href="https://virtualenv.pypa.io/en/latest/" target="_blank" rel="noopener">virtualenv文档</a>。</p>]]></content>
<summary type="html">
如果你希望在一个隔离的环境里工作(强烈推荐,这样你可以在库版本不冲突的情况下处理不同的项目),可以通过运行以下 pip 命令来安装 `virtualenv`
</summary>
<category term="编程开发" scheme="https://wiki.hushhw.cn/categories/%E7%BC%96%E7%A8%8B%E5%BC%80%E5%8F%91/"/>
<category term="环境配置" scheme="https://wiki.hushhw.cn/categories/%E7%BC%96%E7%A8%8B%E5%BC%80%E5%8F%91/%E7%8E%AF%E5%A2%83%E9%85%8D%E7%BD%AE/"/>
<category term="Python" scheme="https://wiki.hushhw.cn/categories/%E7%BC%96%E7%A8%8B%E5%BC%80%E5%8F%91/%E7%8E%AF%E5%A2%83%E9%85%8D%E7%BD%AE/Python/"/>
<category term="Python" scheme="https://wiki.hushhw.cn/tags/Python/"/>
<category term="virtualenv" scheme="https://wiki.hushhw.cn/tags/virtualenv/"/>
</entry>
<entry>
<title>HTTPS协议 & TLS协议</title>
<link href="https://wiki.hushhw.cn/posts/d30e9e5e.html"/>
<id>https://wiki.hushhw.cn/posts/d30e9e5e.html</id>
<published>2019-10-11T19:22:47.000Z</published>
<updated>2021-02-03T06:56:56.832Z</updated>
<content type="html"><![CDATA[<h2 id="1-HTTPS协议"><a href="#1-HTTPS协议" class="headerlink" title="1. HTTPS协议"></a>1. HTTPS协议</h2><h3 id="1-1-HTTP-和-HTTPS-协议的区别"><a href="#1-1-HTTP-和-HTTPS-协议的区别" class="headerlink" title="1.1 HTTP 和 HTTPS 协议的区别"></a>1.1 HTTP 和 HTTPS 协议的区别</h3><p>HTTP 协议存在一定的缺点:</p><ul><li>通信使用明文,不提供任何方式的数据加密,内容可能被窃听(重要密码泄露)</li><li>不验证通信方身份,有可能遭遇伪装(跨站点请求伪造)</li><li>无法证明报文的完整性,有可能已遭篡改(运营商劫持)</li></ul><p>为了解决 HTTP 存在的这些漏洞使数据传输更加安全,引入了另一种协议 HTTPS,HTTPS 协议是在 HTTP 协议的基础上加入了加密处理和认证机制和完整性保护,只是 HTTP 通信接口部分用 SSL/TLS 协议代替而已,简单讲就是 HTTP 的安全版,通常 HTTP 直接和 TCP 通信,当使用 SSL 时,就演变成先与 SSL 通信,再由 SSL 和 TCP 通信。</p><p><img src="https://photo.hushhw.cn/20190923193436.png" alt></p><p>HTTP 和 HTTPS 的区别:</p><ul><li>HTTPS 协议需要到 CA 申请证书,一般免费证书较少,因而需要一定费用;</li><li>HTTP 是超文本传输协议,信息是明文传输,HTTPS 则是安全性的 SSL 加密传输协议;</li><li>HTTP 和 HTTPS 使用的是完全不同的连接方式,用的端口不一样,前者是 80,后者是 443;</li><li><p>HTTP 的连接很简单,是无状态的,HTTPS 协议是由 SSL + HTTP 协议构建的可进行加密传输、身份认证的网络协议,比 HTTP 协议安全。</p><p> </p></li></ul><h3 id="1-2-HTTPS-的工作原理"><a href="#1-2-HTTPS-的工作原理" class="headerlink" title="1.2 HTTPS 的工作原理"></a>1.2 HTTPS 的工作原理</h3><ul><li><p>客户用 HTTPS 的 URL 访问 Web 服务器,要求与 Web 服务器建立 SSL 连接;</p></li><li><p>Web 服务器收到客户端请求后,会将网站的证书信息(证书中包含公钥)传送一份给客户端;</p></li><li><p>客户端的浏览器与 Web 服务器开始协商 SSL 连接的安全等级,也就是信息加密的等级;</p></li><li><p>客户端的浏览器根据双方同意的安全等级,建立会话密钥,然后利用网站的公钥将会话密钥加密,并传送给网站;</p></li><li><p>Web 服务器利用自己的私钥解密出会话密钥;</p></li><li><p>Web 服务器利用会话密钥加密与客户端之间的通信;</p><p> </p></li></ul><h2 id="2-TLS-协议"><a href="#2-TLS-协议" class="headerlink" title="2. TLS 协议"></a>2. TLS 协议</h2><blockquote><p>SSL/TLS 是保护计算机网络通讯安全的一类加密协议,它们在传输层上给原先非安全的应用层协议提供加密保护,如非安全的 HTTP 协议即可被 SSL/TLS 保护形成安全的 HTTPS 协议。</p><p>TLS 可以理解为 SSL(Secure Socket Layer)安全套接字层的后续版本,TLS 协议是继承了 SSL 协议并写入 RFC 标准化后的产物。</p></blockquote><p><strong>SSL (Secure Socket Layer)安全套接字层协议</strong></p><ul><li><p>SSL通过互相认证、使用数字签名确保完整性、使用加密确保私密性,以实现客户端和服务器之间的安全通讯。</p></li><li><p>分为SSL记录协议和SSL握手协议。</p></li></ul><p><strong>TLS(Transport Layer Security)传输层安全协议</strong></p><ul><li>用于两个应用程序之间提供保密性和数据完整性。</li><li><p>分为TLS记录协议和TLS握手协议。 </p><p> </p></li></ul><h3 id="2-1-TLS-通信握手过程"><a href="#2-1-TLS-通信握手过程" class="headerlink" title="2.1 TLS 通信握手过程"></a>2.1 TLS 通信握手过程</h3><ul><li>客户端给出协议版本号、一个客户端生成的随机数(Client random),以及客户端支持的加密方法列表;</li><li>服务器从算法列表中选择一种加密算法确认双方使用的加密方法,并给出包含服务器公钥的数字证书、以及一个服务器生成的随机数(Server random);</li><li>客户端确认数字证书有效,然后生成一个新的随机数(Premaster secret),并使用数字证书中的公钥加密这个随机数,将加密后的信息发给服务器;</li><li>服务器使用自己的私钥,获取客户端发来的随机数(即Premaster secret);</li><li>客户端和服务器根据约定的加密方法,使用前面的三个随机数,生成「对话密钥」(session key),用来加密接下来的整个对话过程。</li></ul><p><img src="https://photo.hushhw.cn/20190924150109.png" alt></p><p>握手阶段有三点需要注意:</p><ul><li>「对话密钥」由前面提到的三个随机数一起组成。</li><li>握手之后的对话使用「对话密钥」加密,属于对称加密;服务器的公钥和私钥只用于加密和解密「对话密钥」,为非对称加密。</li><li>服务器的公钥放在服务器的数字证书之中,只要证书是可信的公钥就是可信的,保证了公钥不被篡改。</li><li>每一次对话(session),客户端和服务器端都生成一个「对话密钥」(session key),用它来加密信息。由于”对话密钥”是对称加密,所以运算速度非常快,而服务器公钥只用于加密”对话密钥”本身,这样就减少了加密运算的消耗时间。</li></ul><p> </p><h2 id="参考"><a href="#参考" class="headerlink" title="参考"></a>参考</h2><blockquote><p><a href="https://www.cnblogs.com/jesse131/p/9080925.html" target="_blank" rel="noopener">我是这样理解HTTP和HTTPS区别的</a></p><p><a href="https://www.freebuf.com/articles/network/116497.html" target="_blank" rel="noopener">传输层安全协议抓包分析之SSL/TLS</a></p><p><a href="http://www.ruanyifeng.com/blog/2014/09/illustration-ssl.html" target="_blank" rel="noopener">图解SSL/TLS协议</a></p><p><a href="https://www.cnblogs.com/snowater/p/7804889.html" target="_blank" rel="noopener">HTTPS协议、TLS协议、证书认证过程解析</a> </p></blockquote>]]></content>
<summary type="html">
为了解决 HTTP 存在的这些漏洞使数据传输更加安全,引入了另一种协议 HTTPS,HTTPS 协议是在 HTTP 协议的基础上加入了加密处理和认证机制和完整性保护,只是 HTTP 通信接口部分用 SSL/TLS 协议代替而已,简单讲就是 HTTP 的安全版,通常 HTTP 直接和 TCP 通信,当使用 SSL 时,就演变成先与 SSL 通信,再由 SSL 和 TCP 通信。
</summary>
<category term="笔记整理" scheme="https://wiki.hushhw.cn/categories/%E7%AC%94%E8%AE%B0%E6%95%B4%E7%90%86/"/>
<category term="流量分析" scheme="https://wiki.hushhw.cn/categories/%E7%AC%94%E8%AE%B0%E6%95%B4%E7%90%86/%E6%B5%81%E9%87%8F%E5%88%86%E6%9E%90/"/>
<category term="TLS" scheme="https://wiki.hushhw.cn/tags/TLS/"/>
<category term="Encrypted Traffic Analytics" scheme="https://wiki.hushhw.cn/tags/Encrypted-Traffic-Analytics/"/>
</entry>
<entry>
<title>吴恩达《机器学习》笔记(九)——支持向量机SVM</title>
<link href="https://wiki.hushhw.cn/posts/f95480d6.html"/>
<id>https://wiki.hushhw.cn/posts/f95480d6.html</id>
<published>2019-08-23T12:32:50.000Z</published>
<updated>2021-02-03T06:56:56.832Z</updated>
<content type="html"><![CDATA[<h2 id="Support-Vector-Machines"><a href="#Support-Vector-Machines" class="headerlink" title="Support Vector Machines"></a>Support Vector Machines</h2><h3 id="12-1-Optimization-Objective"><a href="#12-1-Optimization-Objective" class="headerlink" title="12.1. Optimization Objective"></a>12.1. Optimization Objective</h3><p><strong>支持向量机(Support Vector Machines,SVM)</strong>在学习复杂的非线性方程时提供了一种更为清晰更为强大的方式,属于监督学习算法。</p><p>为了描述 SVM,我们从 logistic 回归开始,下图是回顾 logistic 回归的假设函数的一些特性:</p><p><img src="https://photo.hushhw.cn/20190906130133.png" alt></p><p>再看看 logistic 回归的代价函数:</p><script type="math/tex; mode=display">\begin{align}J(\theta) &= \frac{1}{m}\sum^{m}_{i=1}\left[-y^{(i)}log\ h_{\theta}(x^{(i)}) - (1-y^{(i)})log(1-h_{\theta}(x^{(i)}))\right] \\&= \frac{1}{m}\sum^{m}_{i=1}\left[-y^{(i)}log\left( \frac1{1+e^{-\theta^Tx^{(i)}}}\right) - (1-y^{(i)})log\left(1-\frac1{1+e^{-\theta^Tx^{(i)}}}\right)\right]\end{align}</script><p>令 $z=\theta^Tx$ 后画出两条光滑的 sigmoid 函数曲线如下图:</p><p><img src="https://photo.hushhw.cn/20190906132048.png" alt></p><p>用两条粉色线段代替曲线,这被称为 hinge loss 函数。</p><ul><li><p>当 y = 1 时,命名为 $cost_1(z)$ ,当 z 大于 1 时为零,小于 1 时斜率可以不用考虑。</p></li><li><p>当 y = 0 时,命名为 $cost_0(z)$ ,当 z 小于 -1 时为零,大于 -1 时斜率可以不用考虑。</p></li></ul><p>logistic 回归正则化后的待见函数为:</p><script type="math/tex; mode=display">J(\theta) = \frac{1}{m}\sum^{m}_{i=1}\left[y^{(i)}\left(-log\ h_{\theta}(x^{(i)})\right) + (1-y^{(i)})\left(-log(1-h_{\theta}(x^{(i)}))\right)\right] + \frac{\lambda}{2m}\sum^n_{j=1}\theta^2_j</script><p>用命名后的线段代替 logistic 回归代价函数的第一项和第二项得到:</p><script type="math/tex; mode=display">J(\theta) = \frac{1}{m}\sum^{m}_{i=1}\left[y^{(i)}cost_1(\theta^Tx^{(i)}) + (1-y^{(i)})cost_0(\theta^Tx^{(i)})\right] + \frac{\lambda}{2m}\sum^n_{j=1}\theta^2_j</script><p>现在我们考虑去掉 1/m 这一项,去掉后依然可以得到同样的 𝜃 最优值,因为 1/m 仅是一个常量。我们做一下变形,乘以一个 m/λ:</p><script type="math/tex; mode=display">J(\theta) = \frac{1}{\lambda}\sum^{m}_{i=1}\left[y^{(i)}cost_1(\theta^Tx^{(i)}) + (1-y^{(i)})cost_0(\theta^Tx^{(i)})\right] + \frac12\sum^n_{j=1}\theta^2_j</script><p>最后我们记 C=1/λ,得到:</p><script type="math/tex; mode=display">J(\theta) = C\sum^{m}_{i=1}\left[y^{(i)}cost_1(\theta^Tx^{(i)}) + (1-y^{(i)})cost_0(\theta^Tx^{(i)})\right] + \frac12\sum^n_{j=1}\theta^2_j</script><p>这就是通常使用的 SVM 代价函数,这个系数 C 本质上和 λ 一样,都是改变普通代价函数项和正则项的权重关系。也就是说,如果我们想要加强正则化强度来处理过拟合,那么减小 C;如果想要减少正则化强度来处理欠拟合,那么增大 C。</p><p>最后,有别于 logistic 回归的一点是 SVM 算法的假设函数并不代表 y = 0 或 1 的概率,而只是输出 0 或 1:</p><script type="math/tex; mode=display">h_{\theta}(x)= \begin{cases}1 \quad if\: \theta^Tx\geq0 \\0 \quad otherwise\end{cases}</script><p> </p><h3 id="12-2-Large-Margin-Intuition"><a href="#12-2-Large-Margin-Intuition" class="headerlink" title="12.2. Large Margin Intuition"></a>12.2. Large Margin Intuition</h3><p>把 SVM 算法当作<strong>大间距分类器(Large Margin Classifier)</strong>来理解是比较直观的理解方式。</p><p>下图回顾了 SVM 算法的最小化代价函数的结论:</p><p><img src="https://photo.hushhw.cn/20190906144938.png" alt></p><p>如果代价函数中的 C 被设置的非常非常大,想要是的代价函数最小,就必须让代价函数项为 0 ,即:</p><script type="math/tex; mode=display">\begin{align}J(\theta) &= C\cdot0 + \frac12\sum^n_{j=1}\theta^2_j \\when: \quad&\theta^Tx^{(i)}\geq 1 \quad if \:y^{(i)}=1 \\&\theta^Tx^{(i)}\leq -1 \quad if \:y^{(i)}=0 \\\end{align}</script><p><img src="https://photo.hushhw.cn/20190906150428.png" alt></p><p>在上图的决策边界选择中,黑线决策边界的选择是符合 SVM 算法要求的,黑线有与正负数据集更大的距离,这个距离叫做支持向量机的<strong>间距(margin)</strong>,因此支持向量机有时被称为<strong>大间距分类器(Large Margin Classifier)</strong>。在 SVM 算法中,决策边界的性质是尽可能地远离正数据集与负数据集。</p><p>事实上,支持向量机 SVM 现在比大间距分类器要成熟得多,使用大间距分类器的学习算法容易受到异常点(outlier)的影响,如下图中会因为一个异常点导致决策边界发生很大的偏移。</p><p><img src="https://photo.hushhw.cn/20190906151419.png" alt></p><p>如果想要使得这些异常点不过分影响决策边界,这时应该减小 C 的数值。</p>]]></content>
<summary type="html">
<h2 id="Support-Vector-Machines"><a href="#Support-Vector-Machines" class="headerlink" title="Support Vector Machines"></a>Support Vector Ma
</summary>
<category term="笔记整理" scheme="https://wiki.hushhw.cn/categories/%E7%AC%94%E8%AE%B0%E6%95%B4%E7%90%86/"/>
<category term="机器学习" scheme="https://wiki.hushhw.cn/categories/%E7%AC%94%E8%AE%B0%E6%95%B4%E7%90%86/%E6%9C%BA%E5%99%A8%E5%AD%A6%E4%B9%A0/"/>
<category term="Machine Learning" scheme="https://wiki.hushhw.cn/tags/Machine-Learning/"/>
</entry>
<entry>
<title>吴恩达《机器学习》笔记(七)——神经网络的学习</title>
<link href="https://wiki.hushhw.cn/posts/18a3eba2.html"/>
<id>https://wiki.hushhw.cn/posts/18a3eba2.html</id>
<published>2019-08-13T17:14:21.000Z</published>
<updated>2021-02-03T06:56:56.832Z</updated>
<content type="html"><![CDATA[<h2 id="Neural-Networks:-Learning"><a href="#Neural-Networks:-Learning" class="headerlink" title="Neural Networks: Learning"></a>Neural Networks: Learning</h2><h3 id="9-1-Cost-Function"><a href="#9-1-Cost-Function" class="headerlink" title="9.1. Cost Function"></a>9.1. Cost Function</h3><p>首先引入一些标记:</p><ul><li>$L$:神经网络的总层数</li><li>$s_l$:第 L 层的单元数量(不含偏置单元)</li><li>$K$:输出单元的数量</li><li>$h_{\Theta}(x)_k$:假设函数中的第 k 个输出</li></ul><p>下图中分别为单类和多类分类的表示形式:</p><p><img src="https://photo.hushhw.cn/20190905174159.png" alt></p><p>在 logistic 回归问题中我们的代价函数是:</p><script type="math/tex; mode=display">J(\theta) = -\frac{1}{m}\sum^{m}_{i=1}\left[y^{(i)}log\ h_{\theta}(x^{(i)}) + (1-y^{(i)})log(1-h_{\theta}(x^{(i)}))\right] + \frac{λ}{2m}\sum^n_{j=1}\theta^2_j</script><p>而神经网络中的代价函数是 logistic 回归代价函数的推广,在 logistic 回归中只有一个输出变量且只有一个因变量 y,而在神经网络中却有很多输出变量,其代价函数要复杂一些,如下:</p><script type="math/tex; mode=display">\begin{align}J(\theta) &= -\frac{1}{m}\left[\sum^{m}_{i=1}\sum^k_{k=1}y_k^{(i)}log\left( h_{\Theta}(x^{(i)})\right)_k + (1-y_k^{(i)})log\left(1-h_{\Theta}(x^{(i)})\right)_k\right] \\&+ \frac{λ}{2m}\sum^{L-1}_{l=1}\sum^{s_l}_{i=1}\sum^{s_{l+1}}_{j=1}(\Theta^{(l)}_{j,i})^2\end{align}</script><h3 id="9-2-Backpropagation-Algorithm"><a href="#9-2-Backpropagation-Algorithm" class="headerlink" title="9.2. Backpropagation Algorithm"></a>9.2. Backpropagation Algorithm</h3><p>待补充。。。</p>]]></content>
<summary type="html">
<h2 id="Neural-Networks:-Learning"><a href="#Neural-Networks:-Learning" class="headerlink" title="Neural Networks: Learning"></a>Neural Netw
</summary>
<category term="笔记整理" scheme="https://wiki.hushhw.cn/categories/%E7%AC%94%E8%AE%B0%E6%95%B4%E7%90%86/"/>
<category term="机器学习" scheme="https://wiki.hushhw.cn/categories/%E7%AC%94%E8%AE%B0%E6%95%B4%E7%90%86/%E6%9C%BA%E5%99%A8%E5%AD%A6%E4%B9%A0/"/>
<category term="Machine Learning" scheme="https://wiki.hushhw.cn/tags/Machine-Learning/"/>
</entry>
<entry>
<title>吴恩达《机器学习》笔记(六)——神经网络的表述</title>
<link href="https://wiki.hushhw.cn/posts/e75b0cdb.html"/>
<id>https://wiki.hushhw.cn/posts/e75b0cdb.html</id>
<published>2019-08-11T14:53:52.000Z</published>
<updated>2021-02-03T06:56:56.832Z</updated>
<content type="html"><![CDATA[<blockquote><p>本节表述了神经网络在机器学习中的应用及表达形式,并做了简单的举例说明。</p></blockquote><h2 id="Neural-Networks-Representation"><a href="#Neural-Networks-Representation" class="headerlink" title="Neural Networks: Representation"></a>Neural Networks: Representation</h2><h3 id="8-1-Non-linear-Hypotheses"><a href="#8-1-Non-linear-Hypotheses" class="headerlink" title="8.1. Non-linear Hypotheses"></a>8.1. Non-linear Hypotheses</h3><p>我们前面学到的线性回归和逻辑回归在面临有很多特征时,计算的负荷都会非常大。</p><p>使用非线性的多项式项能够帮助我们建立更好的分类模型。假设我们有非常多的特征,例如大于 100 个变量,我们希望用这 100 个特征来构建一个非线性的多项式模型,结果将是数据非常惊人的特征组合,即便我们只采用两两特征的组合($x<em>1x_2 + x_1x_3 + x_1x_4 + \cdots + x_2x_3 + x_2x_4 + \cdots + x</em>{99}x_{100}$),这样也有接近 5000 个组合而成的特征,这对于一般的逻辑回归来说需要计算的特征太多了。</p><p>所以,我们需要引入神经网络。</p><p> </p><h3 id="8-2-Neurons-and-the-Brain"><a href="#8-2-Neurons-and-the-Brain" class="headerlink" title="8.2. Neurons and the Brain"></a>8.2. Neurons and the Brain</h3><p>神经网络是计算量有些偏大的算法,大概由于近些年计算机的运行速度变快,才足以真正运行起大<br>规模的神经网络。正是由于这个原因和其他一些我们后面会讨论到的技术因素,如今的神经网络对于许多应用来说是最先进的技术。</p><p> </p><h3 id="8-3-Model-Representation"><a href="#8-3-Model-Representation" class="headerlink" title="8.3. Model Representation"></a>8.3. Model Representation</h3><h4 id="8-3-1-简单表达"><a href="#8-3-1-简单表达" class="headerlink" title="8.3.1. 简单表达"></a>8.3.1. 简单表达</h4><p>为了构建神经网络模型,我们需要首先思考大脑中的神经网络是怎样的?</p><p>每一个神经元都可以被认为是一个<strong>处理单元/神经核(processing unit/Nucleus)</strong>,它含有许多<strong>输入/树突(input/Dendrite)</strong>,并且有一个<strong>输出/轴突(output/Axon)</strong>。神经网络是大量神经元相互链接并通过电脉冲来交流的一个网络。 </p><p><img src="https://photo.hushhw.cn/20190811162118.png" alt></p><p>神经网络模型建立在很多神经元之上,每一个神经元又是一个个学习模型。这些神经元采纳一些特征作为输出,并且根据本身的模型提供一个输出。下面是一个以 logistic 回归模型作为自身学习模型的神经元示例:</p><p><img src="https://photo.hushhw.cn/20190904173829.png" alt></p><p>其中,在神经网络中,参数(parameter)又可被称为<strong>权重(weight)</strong>;$x_0=1$ 称为<strong>偏置单元(bias unit)</strong>,是否写出根据情况来定;红色的圈代表神经元,又称<strong>激活单元(activation unit)</strong>;g(z) 称为<strong>激活函数(activation function)</strong>,一般指「由……驱动的神经元计算函数 g(z)」,上面就是逻辑激活函数。</p><p>我们推演到多个神经元的情况:</p><p><img src="https://photo.hushhw.cn/20190904175859.png" alt></p><p>其中,第一层是输入层(Input layer),第三层是输出层(Output Layer),而中间数据处理的一层为隐藏层(Hidden Layers),有输入层加权组合后重新映射而成,每一层都可以加上偏置单元。</p><p>其中一些符号标记:</p><script type="math/tex; mode=display">\begin{align}a_i^{(j)} &= 第j层的第i个激活单元 \\\Theta^{(j)} &= 控制从第j层到j+1层的映射函数的权重矩阵\end{align}</script><p>其中,每一个激活结点都可以用 sigmoid 激活函数表示:</p><p><img src="https://photo.hushhw.cn/20190904191827.png" alt></p><p>由矩阵乘法可知:因此如果要从含有 $s<em>j$ 个单元的第 j 层映射到含有 $s_j+1$ 个单元的第 j+1 层,那么权重矩阵 Θ(j) 的尺寸为 $s</em>{j+1}×(s_j+1)$,其中的 +1是因为要考虑偏置单元。</p><p>这种从左到右的算法称为<strong>前向传播算法(Forward propagation)</strong>。</p><h4 id="8-3-2-向量形式表达"><a href="#8-3-2-向量形式表达" class="headerlink" title="8.3.2. 向量形式表达"></a>8.3.2. 向量形式表达</h4><p>下面利用向量形式来表达该算法,以上面神经网络为例,试着计算第二层的值:</p><script type="math/tex; mode=display">x=\left[ \begin{matrix} x_0 \\x_1 \\ x_2 \\x_3 \end{matrix} \right]、 z^{(2)} = \left[ \begin{matrix} z_1^{(2)} \\ z_2^{(2)} \\ z_3^{(2)}\end{matrix} \right]</script><p>其中,$z^{(2)} = \Theta^{(1)}x,a^{(2)} = g(z^{(2)})$。</p><p>进而,我们得到了参数 z 计算的表达式,计算第 j-1 层到第 j 层映射 g(z) 的参数 z:</p><script type="math/tex; mode=display">z^{(j)} = \Theta^{(j-1)}a^{(j-1)}</script><p>接着,用同样的方法计算下一层的信息:</p><script type="math/tex; mode=display">z^{(j+1)} = \Theta^{(j)}a^{(j)}</script><p>这样上例中 $h_{\theta}(x)=a^{(3)}=g(z^{(3)})$。</p><h4 id="8-3-3-例子"><a href="#8-3-3-例子" class="headerlink" title="8.3.3. 例子"></a>8.3.3. 例子</h4><p>从本质上讲,神经网络能够通过学习得出其自身的一系列特征。在神经网络中,原始特征只是输入层,在我们上面三层的神经网络例子中,第三层也就是输出层做出的预测利用的是第二层的特征,而非输入层中的原始特征,我们可以认为第二层中的特征是神经网络通过学习后自己得出的一系列用于预测输出变量的新特征。 </p><p>神经网络中,单层神经元(无中间层)的计算可用来表示逻辑运算,比如逻辑与(AND)、逻辑或(OR)。 </p><p><img src="https://photo.hushhw.cn/20190905075722.png" alt></p><p><img src="https://photo.hushhw.cn/20190905075911.png" alt></p><p>当输入特征为布尔值时,我们可以用一个单一的激活层作为二元逻辑运算符(binary logical operators),为了表示不同的运算符,只需要修改不同的权重即可。</p><p> </p><h3 id="8-7-Multicalss-Classification"><a href="#8-7-Multicalss-Classification" class="headerlink" title="8.7. Multicalss Classification"></a>8.7. Multicalss Classification</h3><p>当我们有不止两种分类时,可通过神经网络算法来输出不同的结果进行判断,如第一个值是 1 或 0 来预测是否是行人,第二个值是否是 1 或 0 来预测是否是汽车等。</p><p><img src="https://photo.hushhw.cn/20190905171102.png" alt></p><p> </p><h2 id="参考"><a href="#参考" class="headerlink" title="参考"></a>参考</h2><blockquote><p><a href="https://www.zhouyongyi.com/andrew-ng-machine-learning-notes-6/" target="_blank" rel="noopener">驿舟小站</a></p><p><a href="https://github.com/fengdu78/Coursera-ML-AndrewNg-Notes" target="_blank" rel="noopener">Coursera-ML-AndrewNg-Notes</a></p><p><a href="https://www.coursera.org/course/ml" target="_blank" rel="noopener">斯坦福大学 2014 机器学习</a></p></blockquote>]]></content>
<summary type="html">
本节表述了神经网络在机器学习中的应用及表达形式,并做了简单的举例说明。
</summary>
<category term="笔记整理" scheme="https://wiki.hushhw.cn/categories/%E7%AC%94%E8%AE%B0%E6%95%B4%E7%90%86/"/>
<category term="机器学习" scheme="https://wiki.hushhw.cn/categories/%E7%AC%94%E8%AE%B0%E6%95%B4%E7%90%86/%E6%9C%BA%E5%99%A8%E5%AD%A6%E4%B9%A0/"/>
<category term="Machine Learning" scheme="https://wiki.hushhw.cn/tags/Machine-Learning/"/>
</entry>
<entry>
<title>吴恩达《机器学习》笔记(五)——正则化</title>
<link href="https://wiki.hushhw.cn/posts/76f89b87.html"/>
<id>https://wiki.hushhw.cn/posts/76f89b87.html</id>
<published>2019-08-01T16:54:09.000Z</published>
<updated>2021-02-03T06:56:56.832Z</updated>
<content type="html"><![CDATA[<blockquote><p>评价一个模型拟合度是否优良的参考之一是它与实际数据集的偏差程度,我们用代价函数来定量,一般代价函数越小越好,但是当将它们应用在某些特定的机器学习应用时,会遇到<strong>过拟合(over-fitting)</strong>的问题。这时就需要采取一些措施,正则化就是其中一种方式。</p></blockquote><h2 id="Regularization"><a href="#Regularization" class="headerlink" title="Regularization"></a>Regularization</h2><h3 id="7-1-Overfitting"><a href="#7-1-Overfitting" class="headerlink" title="7.1. Overfitting"></a>7.1. Overfitting</h3><p>机器学习训练的目的是为了让模型更好的拟合实际情况,从而指导我们进行预测。评价一个模型拟合度是否优良的参考之一是它与实际数据集的偏差程度,我们用代价函数来定量,一般代价函数越小越好,但是当将它们应用在某些特定的机器学习应用时,会遇到<strong>过拟合(over-fitting)</strong>的问题。</p><p>下图是一个回归问题的例子:</p><p>第一个模型是一个线性模型,欠拟合,不能很好地适应我们的训练集;第三个模型是一个四次方的模型,过于强调拟合原始数据,而丢失了算法的本质:预测新数据。我们可以看出,若给出一个新的值使之预测,它将表现的很差,是过拟合,虽然能非常好地适应我们的训练集但在新输入变量进行预测时可能会效果不好;而中间的模型似乎最合适。 </p><p><img src="https://photo.hushhw.cn/20190805144528.png" alt></p><p>下图是一个分类问题的例子:</p><p>以多项式理解,𝑥 的次数越高,拟合的越好,但相应的预测的能力就可能变差。 </p><p><img src="https://photo.hushhw.cn/20190805144934.png" alt></p><p>由上述分析,我们可以由两种解决过拟合的方案:</p><ul><li>丢弃一些不能帮助我们正确预测的特征:可以手动选择保留哪些特征,或者使用一些模型选择的算法来帮忙</li><li>正则化:保留所有的特征变量,但是减少量级或参数 $\theta_j$ 的大小</li></ul><p> </p><h3 id="7-2-Cost-Function"><a href="#7-2-Cost-Function" class="headerlink" title="7.2. Cost Function"></a>7.2. Cost Function</h3><p>为了避免过拟合的问题,这里介绍前面提到的正则化的方法。</p><p>上面的回归问题中我们知道,正是那些高次项导致了过拟合的问题,所以如果我们能让这些高次项的系数接近于 0 的话,我们就能很好的拟合了。</p><p>所以,这里我们通过降低三次项和四次项的权重,即令 $𝜃_3$ 和 $𝜃_4$ 的值很小,使得拟合模型在大方向上是「二次多项式」在发挥作用。我们要做的便是修改代价函数,在其中 $𝜃_3$ 和 $𝜃_4$ 设置一点惩罚(penalize)。这样做的话,我们在尝试最小化代价时也需要将这个惩罚纳入考虑中,并最终导致选择较小一些的 $𝜃_3$ 和 $𝜃_4$ 。 </p><p><img src="https://photo.hushhw.cn/20190805152238.png" alt></p><p>通过这样的代价函数选择出的 $𝜃_3$ 和 $𝜃_4$ 对预测结果的影响就比之前要小许多。假如我们有非常多的特征,我们并不知道其中哪些特征我们要惩罚,我们将对所有的特征进行惩罚,并且让代价函数最优化的软件来选择这些惩罚的程度。这样的结果是得到了一个较为简单的能防止过拟合问题的假设:</p><script type="math/tex; mode=display">J(\theta) = \frac{1}{2m}\left[ \sum^{m}_{i=1}(h_{\theta}(x^{(i)}-y^{(i)})^2 + \lambda\sum^n_{j=1}\theta^2_j\right]</script><p>其中 𝜆 又称为<strong>正则化参数(Regularization Parameter)</strong>。 根据惯例,我们不对 $𝜃_0$ 进行惩罚。</p><p>对 𝜆 的取值要合理,如果 𝜆 过大的话,为了使得代价函数尽可能小,所有的 𝜃 值(不包括 $𝜃_0$)就都会在一定程度上减小,我们只能得到一条平行于 𝑥 轴的直线,这样会导致<strong>欠拟合(underfitting)</strong>。</p><p> </p><h3 id="7-3-Regularized-Linear-Regression"><a href="#7-3-Regularized-Linear-Regression" class="headerlink" title="7.3. Regularized Linear Regression"></a>7.3. Regularized Linear Regression</h3><p>对于线性回归的求解,我们之前推到了两种学习算法:</p><ul><li>梯度下降</li><li>正规方程</li></ul><p>正则化线性回归的代价函数为:</p><script type="math/tex; mode=display">J(\theta) = \frac{1}{2m}\left[ \sum^{m}_{i=1}(h_{\theta}(x^{(i)}-y^{(i)})^2 + \lambda\sum^n_{j=1}\theta^2_j\right]</script><p>如果我们要使用梯度下降法令这个代价函数最小化,其正则化迭代式如下:</p><script type="math/tex; mode=display">\begin{align}Repeat \{ \\θ_0 &:= θ_0-\alpha\frac1m\sum^m_{i=1}(h_θ(x^{(i)})-y^{(i)})x^{(i)}_0 \\θ_j &:= θ_j-\alpha \left[ \left( \frac1m\sum^m_{i=1}(h_θ(x^{(i)})-y^{(i)})x^{(i)}_j\right) + \frac{\lambda}{m}θ_j \right] \ j\in\{1,2,\cdots,n\}\\\}\end{align}</script><p>将 $θ_0$ 单独分开的原因上面提到了,其对应的特征量是 1,约定俗成不必正则化。上式可在整理为:</p><script type="math/tex; mode=display">θ_j := θ_j(1-\alpha\frac{\lambda}{m})-\alpha\frac1m\sum^m_{i=1}(h_θ(x^{(i)})-y^{(i)})x^{(i)}_j</script><p>在这个形式下,我们可以理解得到,每一次迭代都会多乘一个小于一的系数 $1–α\fracλm$,进而缩小。</p><p> </p><p>如果我们要使用正规方程来求解正则化线性回归模型,方法如下:</p><p><img src="https://photo.hushhw.cn/20190805165041.png" alt></p><script type="math/tex; mode=display">\theta = (X^TX + \lambda L)^{-1}X^Ty \\where \ L = \left[\begin{matrix}0 \qquad\qquad\qquad\qquad \\\qquad 1 \qquad\qquad\qquad\\\qquad\qquad1 \qquad\qquad \\\qquad\qquad\qquad\ddots \qquad \\\qquad\qquad\qquad\qquad 1\end{matrix}\right]</script><pre><code> 其中 L 为(n+1)*(n+1)。</code></pre><p> </p><h3 id="7-4-Regularized-Logistic-Regression"><a href="#7-4-Regularized-Logistic-Regression" class="headerlink" title="7.4. Regularized Logistic Regression"></a>7.4. Regularized Logistic Regression</h3><p><img src="https://photo.hushhw.cn/20190811143857.png" alt></p><p>对于 logistic 回归模型,如果你得模型中有很多的特征,有无关紧要的多项式,这些大量的特征就会造成过拟合现象。</p><p>logistic 回归模型正则化后的代价函数为:</p><script type="math/tex; mode=display">J(\theta) = -\frac{1}{m}\sum^{m}_{i=1}\left[y^{(i)}log\ h_{\theta}(x^{(i)}) + (1-y^{(i)})log(1-h_{\theta}(x^{(i)}))\right] + \frac{λ}{2m}\sum^n_{j=1}\theta^2_j</script><p>如果使用梯度下降算法最小化代价函数的话,其正则化迭代式为:</p><script type="math/tex; mode=display">\begin{align}Repeat \{ \\θ_0 &:= θ_0-\alpha\frac1m\sum^m_{i=1}(h_θ(x^{(i)})-y^{(i)})x^{(i)}_0 \\θ_j &:= θ_j-\alpha \left[ \left( \frac1m\sum^m_{i=1}(h_θ(x^{(i)})-y^{(i)})x^{(i)}_j\right) + \frac{\lambda}{m}θ_j \right] \ j\in\{1,2,\cdots,n\}\\\}\end{align}</script><p>看上去和线性回归一样,但是因为 $h_{\theta}(x) = g({\theta}^TX)$,所以与线性回归不同。</p><p> </p><h2 id="专业名词整理"><a href="#专业名词整理" class="headerlink" title="专业名词整理"></a>专业名词整理</h2><ul><li><code>overfitting</code>:过拟合、<code>underfitting</code>:欠拟合</li></ul><p> </p><h2 id="参考"><a href="#参考" class="headerlink" title="参考"></a>参考</h2><blockquote><p><a href="https://www.zhouyongyi.com/andrew-ng-machine-learning-notes-5/" target="_blank" rel="noopener">驿舟小站</a></p><p><a href="https://github.com/fengdu78/Coursera-ML-AndrewNg-Notes" target="_blank" rel="noopener">Coursera-ML-AndrewNg-Notes</a></p><p><a href="https://www.coursera.org/course/ml" target="_blank" rel="noopener">斯坦福大学 2014 机器学习</a></p></blockquote>]]></content>
<summary type="html">
评价一个模型拟合度是否优良的参考之一是它与实际数据集的偏差程度,我们用代价函数来定量,一般代价函数越小越好,但是当将它们应用在某些特定的机器学习应用时,会遇到过拟合(over-fitting)的问题。这时就需要采取一些措施,正则化就是其中一种方式。
</summary>
<category term="笔记整理" scheme="https://wiki.hushhw.cn/categories/%E7%AC%94%E8%AE%B0%E6%95%B4%E7%90%86/"/>
<category term="机器学习" scheme="https://wiki.hushhw.cn/categories/%E7%AC%94%E8%AE%B0%E6%95%B4%E7%90%86/%E6%9C%BA%E5%99%A8%E5%AD%A6%E4%B9%A0/"/>
<category term="Machine Learning" scheme="https://wiki.hushhw.cn/tags/Machine-Learning/"/>
</entry>
<entry>
<title>Google Hacking 语法使用总结</title>
<link href="https://wiki.hushhw.cn/posts/4884afb4.html"/>
<id>https://wiki.hushhw.cn/posts/4884afb4.html</id>
<published>2019-07-28T18:24:35.000Z</published>
<updated>2021-02-03T06:56:56.836Z</updated>
<content type="html"><![CDATA[<h2 id="起因"><a href="#起因" class="headerlink" title="起因"></a>起因</h2><p>虽然一直知道 Google Hacking 功能的强大,平常也会用到一些语法,但是终究用的不熟练,或者没有充分发挥它的功能,所以写下这篇文章总结一下。</p><h2 id="Google-Hacking-常用语法"><a href="#Google-Hacking-常用语法" class="headerlink" title="Google Hacking 常用语法"></a>Google Hacking 常用语法</h2><ul><li><strong>intext</strong>:把网页中的正文内容中的某个字符作为搜索条件</li><li><strong>intitle</strong>:把网页标题中的某些字符作为搜索条件</li><li><strong>cache</strong>:搜索搜索引擎里关于某些内容的缓存</li><li><strong>filetype</strong>:指定一个格式类型的文件作为搜索对象</li><li><strong>inurl</strong>:搜索包含指定字符的 URL</li><li><strong>site</strong>:在指定的站点搜索相关内容</li></ul><h2 id="Google-Hacking-其它语法"><a href="#Google-Hacking-其它语法" class="headerlink" title="Google Hacking 其它语法"></a>Google Hacking 其它语法</h2><ul><li><strong>引号「””</strong>」:把关键字打上引号后把引号部分作为整体来搜索</li><li><strong>or</strong>:同时搜索两个或更多的关键字</li><li><strong>link</strong>:搜索某个网站的链接<br>这些语法可叠加使用。</li></ul>]]></content>
<summary type="html">
虽然一直知道 Google Hacking 功能的强大,平常也会用到一些语法,但是终究用的不熟练,或者没有充分发挥它的功能,所以写下这篇文章总结一下。
</summary>
<category term="日常技巧" scheme="https://wiki.hushhw.cn/categories/%E6%97%A5%E5%B8%B8%E6%8A%80%E5%B7%A7/"/>
<category term="搜索技巧" scheme="https://wiki.hushhw.cn/tags/%E6%90%9C%E7%B4%A2%E6%8A%80%E5%B7%A7/"/>
</entry>
<entry>
<title>吴恩达《机器学习》笔记(四)——Logistic回归</title>
<link href="https://wiki.hushhw.cn/posts/f72c23ef.html"/>
<id>https://wiki.hushhw.cn/posts/f72c23ef.html</id>
<published>2019-07-21T14:50:05.000Z</published>
<updated>2021-02-03T06:56:56.832Z</updated>
<content type="html"><![CDATA[<blockquote><p>本文内容介绍「logistic 回归」解决分类问题。</p></blockquote><h2 id="Logistic-Regression"><a href="#Logistic-Regression" class="headerlink" title="Logistic Regression"></a>Logistic Regression</h2><h3 id="6-1-Classification"><a href="#6-1-Classification" class="headerlink" title="6.1. Classification"></a>6.1. Classification</h3><p>与回归问题不同的是,分类问题中输出 y 不是连续的值,只能是 0 或者 1。并且 0 一般用来代表负向类(negative class),1 代表正向类(positive class),当然也可以任意指定。</p><p>如果我们要用线性回归算法来解决一个分类问题,对于分类, 𝑦 取值为 0 或者 1,那么假设函数的输出值可能远大于 1,或者远小于 0,即使所有训练样本的标签 𝑦 都等于 0 或 1。尽管我们知道标签应该取值 0 或者 1,但是如果算法得到的值远大于 1 或者远小于 0 的话,就会感觉很奇怪。所以我们在接下来的要研究的算法就叫做 <strong>Logistic 回归</strong>,这个算法的性质是:它的输出值永远在 0 到 1 之间。 </p><p> </p><h3 id="6-2-Hypothesis-Representation"><a href="#6-2-Hypothesis-Representation" class="headerlink" title="6.2 Hypothesis Representation"></a>6.2 Hypothesis Representation</h3><p>在分类问题中,要用什么样的函数来表示我们的假设。希望我们的分类器的输出值在 0 和 1 之间,因此,我们希望想出一个满足某个性质的假设函数,这个性质是它的预测值要在 0 和 1 之间,于是我们引入了一个新的模型——逻辑回归(Logistic Regression),该模型的输出变量范围式中在 0 和 1 之间。</p><p>logistic 回归模型的假设是:$h_{\theta}(x) = g(\theta^TX)$,其中 X 代表特征向量,g 代表 logistic 函数(logistic function)是一个常用的逻辑函数为 <strong>sigmoid 函数(Sigmoid function)</strong>,公式为:$g(z)= \frac1{1+e^{-z}} $,该函数图像如下图:</p><p> <img src="https://photo.hushhw.cn/20190721154842.png" alt></p><p>下面我们来理解一下 logistic 回归模型的假设:</p><script type="math/tex; mode=display">h_{\theta}(x) = \frac1{1+e^{-\theta^Tx}}</script><p>其中,h 表示对于给定的输入变量,根据选择的参数计算输出变量为 1 的可能性(estimated probablity),即 $ℎ_𝜃(𝑥) = 𝑃(𝑦 = 1|𝑥;𝜃)$。</p><p>例如,如果对于给定的 𝑥,通过已经确定的参数计算得出 $ℎ_𝜃(𝑥) = 0.7$,则表示有 70% 的几率 𝑦 为正向类,相应地 𝑦 为负向类的几率为 1-0.7=0.3。 </p><p> </p><h3 id="6-3-Decision-Boundary"><a href="#6-3-Decision-Boundary" class="headerlink" title="6.3. Decision Boundary"></a>6.3. Decision Boundary</h3><p>这一节讨论<strong>决策边界(Decision Boundary)</strong>问题,根据上面 sigmoid 函数的图像我们知道,当 $z\geq 0$ 时 $g(z) \geq 0.5$,反之小于 0.5,对应的 logistic 回归模型的假设得到:</p><script type="math/tex; mode=display">\theta^Tx \geq 0\ 时,预测\ y = 1\\\theta^Tx < 0 \ 时,预测\ y=0</script><p>假设有一个模型其假设函数为:$h_{\theta}(x) = g(\theta_0 + \theta_1x_1 + \theta_2x_2)$,并且假设我们已经得到了参数 𝜃 的转置向量为 [-3 1 1]。则当 $-3 + x_1 + x_2 \geq 0$,即 $x_1 + x_2 \geq 3$ 时,模型将预测 y = 1。我们绘制出这条直线 $x_1 + x_2 = 3$,这条线便是模型的分界线,即<strong>决策边界</strong>。</p><p><img src="https://photo.hushhw.cn/20190721162039.png" alt></p><p>更进一步,若假设函数是非线性的,$h_θ=g(θ_0+θ_1x_1+θ_2x_2+θ_3x^2_1+θ_4x^2_2)$,当我们通过算法得出 θ 后,画出图形如下,此时决策边界不一定是直线了:</p><p><img src="https://photo.hushhw.cn/20190721162441.png" alt></p><p> </p><h3 id="6-4-Cost-Function"><a href="#6-4-Cost-Function" class="headerlink" title="6.4. Cost Function"></a>6.4. Cost Function</h3><p>如何拟合 logistic 回归模型的参数 θ 呢?我们定义用来拟合参数的优化目标或者叫代价函数(cost function)。</p><p>在线性回归模型中,我们定义的代价函数是所有模型误差的平方和,即代价函数为:</p><script type="math/tex; mode=display">J(\theta) = \frac{1}{2m}\sum^m_{i=1}(h^{(i)}_{\theta}-y^{(i)})^2</script><p>它的结构形式可以写成:</p><script type="math/tex; mode=display">J(\theta) = \frac{1}{m}\sum^m_{i=1}\frac12(h^{(i)}_{\theta}-y^{(i)})^2 \\=\frac{1}{m}\sum^m_{i=1}Cost(h_{\theta}(x^{(i)}),y)</script><p>理论上来说,我们也可以对逻辑回归模型沿用这个定义,但是问题在于当我们将 $h_{\theta}(x) = \frac1{1+e^{-\theta^Tx}}$ 带入到这样定义了的代价函数中时,我们得到的代价函数将是一个<strong>非凸函数(non-convex)</strong>。 </p><p><img src="https://photo.hushhw.cn/20190731144737.png" alt></p><p>这意味着我们的代价函数有许多局部最小值,这将影响梯度下降算法寻找全局最小值。</p><p>这里 Andrew Ng 不加证明的重新给出了代价函数:</p><script type="math/tex; mode=display">Cost(h_{\theta}(x),y) = \begin{cases}\begin{align}-log(h_{\theta}(x)) \qquad if\quad y=1 \\-log(1-h_{\theta}(x)) \qquad if\quad y=0\end{align}\end{cases}</script><p>$h_{\theta}(x)$ 与代价函数之间的关系如下图:</p><p><img src="https://photo.hushhw.cn/20190731150435.png" alt></p><p><img src="https://photo.hushhw.cn/20190731150528.png" alt></p><p>这样构建的代价函数的特点是:</p><ul><li><p>当实际的 y = 1 时,$h_{\theta}(x)$ 为 1 时误差为 0,不为 1 时误差随着趋于 0 而变大;</p></li><li><p>当实际的 y = 0 时,$h_{\theta}(x)$ 为 0 时误差为 0,不为 0 时误差随着趋于 1 而变大。</p></li></ul><p>由于 y 只能取 0 或 1,可以把这个式子整合成:</p><script type="math/tex; mode=display">Cost(h_{\theta}(x), y) = -ylog(h_{\theta}(x)) - (1-y)log(1-h_{\theta}(x))</script><p>即 logistic 回归的代价函数为:</p><script type="math/tex; mode=display">\begin{align}J(\theta) &= \frac{1}{m}\sum^{m}_{i=1}Cost(h_{\theta}(x^{(i)}),y^{(i)}) \\&= -\frac{1}{m}\sum^{m}_{i=1}\left[y^{(i)}log\ h_{\theta}(x^{(i)}) + (1-y^{(i)})log(1-h_{\theta}(x^{(i)}))\right]\end{align}</script><p> </p><h3 id="6-5-Gradient-Descent"><a href="#6-5-Gradient-Descent" class="headerlink" title="6.5. Gradient Descent"></a>6.5. Gradient Descent</h3><p>在得到这样一个代价函数后,我们便可以用<strong>梯度下降算法</strong>来求得使代价函数最小的参数了,即最小化 J(𝜃)。</p><p>梯度下降算法前面我们已经用过,用循环运算表达的形式如下:</p><script type="math/tex; mode=display">\theta_j:=\theta_j-\alpha\frac{∂}{∂\theta_j}J(\theta)</script><p>用上面的式子来不断更新所有的 𝜃 值,如果将 J(θ) 的偏导数带入后,我们得到:</p><script type="math/tex; mode=display">\theta_j:=\theta_j-\alpha\sum^{m}_{i=1}(h_{\theta}(x^{(i)})-y^{(i)})x^{(i)}_j</script><p>现在,如果你把这个更新规则和我们之前用在线性回归上的进行比较的话,你会惊讶地发现,这个式子正是我们用来做线性回归梯度下降的。 但是线性回归和 logistic 回归并不一样,因为两者的假设函数 $h_{\theta}(x)$ 不一样。</p><hr><p><strong>代价函数偏导数的过程</strong></p><p>首先计算 sigmoid 函数的导数:</p><script type="math/tex; mode=display">\begin{split}σ(x)^{'} &=(\frac{1}{1+e^{-x}})^{'} =\frac{e^{-x}}{(1+e^{-x})^2}=(\frac{1}{1+e^{-x}})(\frac{e^{-x}}{1+e^{-x}})=σ(x)(\frac{1+e^{-x}-1}{1+e^{-x}}) \\&= σ(x)(1-σ(x))\end{split}</script><p>再来对代价函数求偏导:</p><script type="math/tex; mode=display">\begin{align}\frac{∂}{∂θ_j}J(θ) &= \frac{∂}{∂θ_j}\frac{-1}{m}\sum^{m}_{i=1}\left[y^{(i)}log\ h_{\theta}(x^{(i)}) + (1-y^{(i)})log(1-h_{\theta}(x^{(i)}))\right] \\&= -\frac1m\sum^{m}_{i=1}\left[y^{(i)}\frac{∂}{∂θ_j}log\ h_{\theta}(x^{(i)}) + (1-y^{(i)})\frac{∂}{∂θ_j}log(1-h_{\theta}(x^{(i)}))\right] \\&= -\frac1m\sum^{m}_{i=1}\left[y^{(i)}\frac{\frac{∂}{∂θ_j}h_{\theta}(x^{(i)})}{h_{\theta}(x^{(i)})} + (1-y^{(i)})\frac{\frac{∂}{∂θ_j}(1-h_{\theta}(x^{(i)}))}{1-h_{\theta}(x^{(i)})}\right] \\&= -\frac1m\sum^{m}_{i=1}\left[y^{(i)}\frac{\frac{∂}{∂θ_j}σ({\theta}^Tx^{(i)})}{σ({\theta}^Tx^{(i)})} + (1-y^{(i)})\frac{\frac{∂}{∂θ_j}(1-σ({\theta}^Tx^{(i)}))}{1-σ({\theta}^Tx^{(i)})}\right] \\&= -\frac1m\sum^{m}_{i=1}\left[y^{(i)}\frac{σ(1-σ)\frac{∂}{∂θ_j}({\theta}^Tx^{(i)})}{σ} + (1-y^{(i)})\frac{σ(1-σ)\frac{∂}{∂θ_j}(1-{\theta}^Tx^{(i)})}{1-σ}\right] \\&= -\frac1m\sum^{m}_{i=1}\left[y^{(i)}(1-σ)x^{(i)}_j - (1-y^{(i)})σx^{(i)}_j\right] \\&= -\frac1m\sum^{m}_{i=1}\left[y^{(i)}(1-h_{\theta}(x^{(i)})) - (1-y^{(i)})h_{\theta}(x^{(i)})\right]x^{(i)}_j \\&= -\frac1m\sum^{m}_{i=1}\left[y^{(i)}-y^{(i)}h_{\theta}(x^{(i)}) - h_{\theta}(x^{(i)})+y^{(i)}h_{\theta}(x^{(i)})\right]x^{(i)}_j \\&= -\frac1m\sum^{m}_{i=1}\left[y^{(i)} - h_{\theta}(x^{(i)})\right]x^{(i)}_j \\&= \frac1m\sum^{m}_{i=1}\left[h_{\theta}(x^{(i)})-y^{(i)}\right]x^{(i)}_j \\\end{align}</script><p> </p><h3 id="6-6-Advanced-Optimization"><a href="#6-6-Advanced-Optimization" class="headerlink" title="6.6. Advanced Optimization"></a>6.6. Advanced Optimization</h3><p>上面我们讨论了用梯度下降的方法最小化逻辑回归中代价函数 𝐽(𝜃)。这一节介绍一些一些高级优化算法和一些高级的优化概念,利用这些方法我们就能够使通过梯度下降进行 logistic 回归的速度大大提高,而这也将使算法更加适合解决大型的机器学习问题。</p><p> 如果我们能用这些方法来计算代价函数 𝐽(𝜃) 和偏导数项的话,那么这些算法就是为我们优化代价函数的不同方法,<strong>共轭梯度法</strong>、<strong>BFGS(变尺度法)</strong>和 <strong>L-BFGS(限制变尺度法)</strong>就是其中一些更高级的优化算法,它们需要有一种方法来计算 𝐽(𝜃) 以及需要一种方法计算导数项,然后使用比梯度下降更复杂的算法来最小化代价函数。</p><p>他们相对比较复杂,优势主要有两点</p><ul><li>不需要选择学习率α,算法会自适应</li><li>经常比梯度下降算法快</li></ul><p> </p><h3 id="6-7-Multi-class-classification-One-vs-all"><a href="#6-7-Multi-class-classification-One-vs-all" class="headerlink" title="6.7. Multi-class classification: One-vs-all"></a>6.7. Multi-class classification: One-vs-all</h3><p>前面我们讨论的分类问题都是<strong>二元分类(Binary classification)</strong>,这一节介绍使用 logistic 回归来解决<strong>多类别分类(Multi-class classfication)</strong>问题,具体来说讨论「一对多」的分类问题。</p><p><img src="https://photo.hushhw.cn/20190801162235.png" alt></p><p>解决多类别分类问题的思路是将其分成多个二元分类问题。</p><p><img src="https://photo.hushhw.cn/20190801162837.png" alt></p><p> </p><h2 id="专业名词整理"><a href="#专业名词整理" class="headerlink" title="专业名词整理"></a>专业名词整理</h2><ul><li><code>dependent variable</code>:因变量</li><li><code>convex function</code>:凸函数、<code>non-convex function</code>:非凸函数</li><li><code>maximum likelihood estimation</code>:极大似然估计</li><li><code>binary classification</code>:二元分类、<code>multi-class classfication</code>:多类别分类</li></ul><p> </p><h2 id="参考"><a href="#参考" class="headerlink" title="参考"></a>参考</h2><blockquote><p><a href="https://www.zhouyongyi.com/andrew-ng-machine-learning-notes-4/" target="_blank" rel="noopener">驿舟小站</a></p><p><a href="https://github.com/fengdu78/Coursera-ML-AndrewNg-Notes" target="_blank" rel="noopener">Coursera-ML-AndrewNg-Notes</a></p><p><a href="https://www.coursera.org/course/ml" target="_blank" rel="noopener">斯坦福大学 2014 机器学习</a></p></blockquote>]]></content>
<summary type="html">
本文内容介绍「logistic 回归」解决分类问题。
</summary>
<category term="笔记整理" scheme="https://wiki.hushhw.cn/categories/%E7%AC%94%E8%AE%B0%E6%95%B4%E7%90%86/"/>
<category term="机器学习" scheme="https://wiki.hushhw.cn/categories/%E7%AC%94%E8%AE%B0%E6%95%B4%E7%90%86/%E6%9C%BA%E5%99%A8%E5%AD%A6%E4%B9%A0/"/>
<category term="Machine Learning" scheme="https://wiki.hushhw.cn/tags/Machine-Learning/"/>
</entry>
<entry>
<title>吴恩达《机器学习》笔记(三)——多变量线性回归</title>
<link href="https://wiki.hushhw.cn/posts/92253a1a.html"/>
<id>https://wiki.hushhw.cn/posts/92253a1a.html</id>
<published>2019-07-16T16:49:45.000Z</published>
<updated>2021-02-03T06:56:56.836Z</updated>
<content type="html"><![CDATA[<blockquote><p>本文内容介绍「多变量线性回归」问题。</p><p>首先引入一个多维特征模型,利用「多变量梯度下降」来迭代求出代价函数最小值。</p><p>之后介绍了「多项式回归」问题,以及「正规方程」,并把正规方程和梯度下降问题做了比较。</p></blockquote><h2 id="Linear-Algebra-Review"><a href="#Linear-Algebra-Review" class="headerlink" title="Linear Algebra Review"></a>Linear Algebra Review</h2><p>这部分线性代数的只是非常基础,仅仅整理了一些专业名词的英文去熟悉学习。</p><p> </p><h2 id="Linear-Regression-with-Multiple-Variables"><a href="#Linear-Regression-with-Multiple-Variables" class="headerlink" title="Linear Regression with Multiple Variables"></a>Linear Regression with Multiple Variables</h2><h3 id="4-1-Multiple-Features"><a href="#4-1-Multiple-Features" class="headerlink" title="4.1. Multiple Features"></a>4.1. Multiple Features</h3><p>前面我们讨论了单变量特征(single variable)的回归模型(影响房屋价格的因素仅有面积大小),现在我们对房价模型增加更多的特征 $(x_1,x_2,…,x_n)$,构成一个多变量的模型。</p><p>下面这个例子为四个特征的模型,其中一些参数的说明如图:</p><p><img src="https://photo.hushhw.cn/20190716173040.png" alt></p><p>支持多变量的假设 ℎ 表示为:</p><script type="math/tex; mode=display">ℎ_{\theta}(𝑥) = \theta_0 + \theta_1x_1 + \theta_2x_2+...+\theta_𝑛x_𝑛</script><p>这个公式中有 𝑛 + 1 个参数和 𝑛 个变量,为了使得公式能够简化一些,引入 $x_0 = 1$,则公式转化为:</p><script type="math/tex; mode=display">ℎ_{\theta}(𝑥) = \theta_0x_0 + \theta_1x_1 + \theta_2x_2+...+\theta_𝑛x_𝑛</script><p>改写成矩阵的形式:</p><script type="math/tex; mode=display">h_{\theta}(x) = \left[\begin{matrix}\theta_0 & \theta_1 & \theta_2 \cdots & \theta_n \end{matrix}\right]\left[\begin{matrix}x_0 \\ x_1 \\x_2 \\ \vdots\\ x_n\end{matrix}\right] = \theta^TX</script><p>此时模型中的参数是一个 𝑛 + 1 维的向量,任何一个训练实例也都是 𝑛 + 1 维的向量,相乘就相当于参数向量的转置乘以实例向量,因此公式可以简化为:</p><script type="math/tex; mode=display">h_{\theta}(x) = \theta^TX</script><p> </p><h3 id="4-2-Gradient-Descent-for-Multiple-Variables"><a href="#4-2-Gradient-Descent-for-Multiple-Variables" class="headerlink" title="4.2. Gradient Descent for Multiple Variables"></a>4.2. Gradient Descent for Multiple Variables</h3><p>与单变量线性回归类似,在多变量线性回归中,我们也构建一个代价函数(cost function),则这个代价函数是所有建模误差的平方和,即:</p><script type="math/tex; mode=display">J(\theta_0,\theta_1,\cdots,\theta_n)=\frac{1}{2m}\sum^m_{i=1}(h_{\theta}(x^{(i)})-y^{(i)})^2</script><p>我们可以简写成 $J(\theta)$ ,其中:$h_{\theta}(x) = \theta^TX = \theta_0 + \theta_1x_1 + \theta_2x_2 + \cdots + \theta_nx_n$</p><p>我们的目标和单变量线性回归问题一样,要找到使得代价函数最小的系列参数,使用梯度下降的方法来求,就是要不断的更新每一个 $\theta_j$ 参数,通过 $\theta_j$ 减去 $\alpha$ 乘以导数项,具体过程如下图,分别是单特征和有多个特征的模型的情况。</p><p><img src="https://photo.hushhw.cn/20190716183159.png" alt></p><p> </p><h3 id="4-3-Feature-Scaling"><a href="#4-3-Feature-Scaling" class="headerlink" title="4.3. Feature Scaling"></a>4.3. Feature Scaling</h3><p>机器学习中我们使用参数的目标是为了通过数据集改善每一个特征量 x 对应的权重 θ,最终得到拟合度高的假设方程。这时就出现了一种情况,如果某个特征量的数量级远大于其它特征量,就会导致该特征在学习算法中占主导位置。所以,我们需要通过预处理的方式让初始的特征量具有同等的地位,才能让机器学习算法更快地学习得到他们的权重。</p><p>使用这里「房价预估」的例子,假设有两个特征影响房价,分别是「面积」和「房间数」,很显然这两个数据差别是非常大的,在没有进行单位统一之前,由于变量的单位相差很大,导致了椭圆型的梯度轮廓;缩放之后将变量变成同一单位,产生了圆形轮廓。</p><p><img src="https://photo.hushhw.cn/20190721103703.png" alt></p><p><img src="https://photo.hushhw.cn/20190721103741.png" alt="图片来源:TingMind.cn"></p><p>由于梯度下降是按切线方向下降,所以导致了系统在椭圆轮廓不停迂回地寻找最优解,而圆形轮廓就能轻松找到。</p><p>特征缩放的方法有很多,比如使用 $x_n = \frac{x_n-\mu}{max-min}$ 的方式,将数据缩放为 [-1, 1] 的范围,例如房屋面积范围在 [100, 2000],平均值为 1000,就可以将均值 1000 以及最大最小值差带入。</p><p>更一般的方法,可以使用<strong>均值归一化(mean normalization)</strong>使得数据集的平均值为零:$x_n = \frac{x_n-\mu_n}{s_n}$,其中 𝜇 是平均值,𝑠 是标准差。 </p><blockquote><p>埋坑:这里需要去理解特征标准化和归一化的区别,有待深入学习。</p></blockquote><p> </p><h3 id="4-4-Learning-Rate"><a href="#4-4-Learning-Rate" class="headerlink" title="4.4. Learning Rate"></a>4.4. Learning Rate</h3><p>梯度下降算法收敛所需要的迭代次数根据模型的不同而不同,我们不能提前预知,我们可以绘制迭代次数和代价函数的图表来观测算法在何时趋于收敛。 也有一些自动测试是否收敛的方法,例如将代价函数的变化值与某个阀值(例如 0.001)进行比较。</p><p>梯度下降算法的每次迭代受到学习率的影响,如果学习率 𝑎 过小,则达到收敛所需的迭代次数会非常高;如果学习率 𝑎 过大,每次迭代可能不会减小代价函数,可能会越过局部最小值导致无法收敛。</p><p> </p><h3 id="4-5-Polynomial-Regression"><a href="#4-5-Polynomial-Regression" class="headerlink" title="4.5. Polynomial Regression"></a>4.5. Polynomial Regression</h3><p>前面学习了线性回归,但是线性回归并不适用于所有数据,有时我们需要曲线来适应我们的数据,这一节继续学习<strong>多项式回归(Polynomial Regression)</strong>,比如一个三次方模型:$h_{\theta}(x) = \theta_0+\theta_1x_1+\theta_2x^2_2+\theta_3x_3^3$,下图是一个一元多项式回归的例子。</p><p><img src="https://photo.hushhw.cn/20190721123805.png" alt></p><p>如果我们采用多项式回归模型,在运行梯度下降算法前,特征缩放非常有必要。 </p><p> </p><h3 id="4-6-Normal-Equation"><a href="#4-6-Normal-Equation" class="headerlink" title="4.6. Normal Equation"></a>4.6. Normal Equation</h3><p>在前面我们学习了使用梯度下降法来计算参数最优解,其过程是对代价函数相对于每个参数求偏导数,通过迭代算法一步一步进行同步更新,直到收敛到全局最小值,从而得到最优参数值。但是对于某些线性回归问题,<strong>正规方程(Normal Equation)</strong>方法是更好的解决方案,正规方程则是通过数学方法一次性求得最优解。</p><p>下面这个例子中,我们有 4 个训练样本,我们构建一个矩阵 X 包含训练样本的所有特征变量,构成一个 m*(n+1) 维矩阵,所有的预测值放入向量 y,构成一个 m 维向量。代价函数最小化的 $\theta = (X^TX)^{-1}X^Ty$。</p><p><img src="https://photo.hushhw.cn/20190721130118.png" alt></p><p>对于更一般的情况,假设有 m 组训练样本分别为 $(x^{(1)},y^{(1)},\cdots,x^{(m)},y^{(m)})$,每组训练样本有 n 个特征值。其中,</p><script type="math/tex; mode=display">x^{(i)} = \left[ \begin{matrix} x_0^{(i)}\\x_1^{(i)}\\x_2^{(i)}\\\vdots\\x_n^{(i)} \end{matrix}\right]\in R^{n+1},对应的 X = \left[ \begin{matrix} \cdots {x^{(1)}}^T \cdots \\ \cdots {x^{(2)}}^T \cdots \\ \vdots \\ \cdots {x^{(m)}}^T \cdots \\ \end{matrix}\right]</script><p>对于那些不可逆的矩阵(通常是因为特征之间不独立,如同时包含英尺为单位的尺寸和米为单位的尺寸两个特征,也有可能是特征数量大于训练集的数量),正规方程方法是不能用的。 </p><p>梯度下降与正规方程比较:</p><div class="table-container"><table><thead><tr><th style="text-align:center">梯度下降</th><th style="text-align:center">正规方程</th></tr></thead><tbody><tr><td style="text-align:center">需要选择学习率𝛼</td><td style="text-align:center">不需要</td></tr><tr><td style="text-align:center">需要多次迭代</td><td style="text-align:center">一次运算得出</td></tr><tr><td style="text-align:center">当特征数量 𝑛 大时也能较好适用</td><td style="text-align:center">需要计算 ${(X^TX)}^{-1}$ 如果特征数量 𝑛 较大则运算代价大,因为矩阵逆的计算时间复杂度 为 $𝑂(𝑛^3)$,通常来说当 𝑛 小于 10000 时还是可以接受的</td></tr><tr><td style="text-align:center">适用于各种类型的模型</td><td style="text-align:center">只适用于线性模型,不适合逻辑回归模型等其他模型</td></tr></tbody></table></div><p>只要特征变量的数目并不大,标准方程是一个很好的计算参数𝜃的替代方法。具体地说,只要特征变量数量小于一万,我通常使用标准方程法,而不使用梯度下降法。<br>随着我们要讲的学习算法越来越复杂,例如,当我们讲到分类算法,像逻辑回归算法,我们会看到,实际上对于那些算法,并不能使用标准方程法。对于那些更复杂的学习算法,我们将不得不仍然使用梯度下降法。因此,梯度下降法是一个非常有用的算法,可以用在有大量特征变量的线性回归问题。</p><p> </p><h2 id="专业名词整理"><a href="#专业名词整理" class="headerlink" title="专业名词整理"></a>专业名词整理</h2><ul><li><code>linear algebra</code>:线性代数</li><li><code>matrix(matrices)</code>:矩阵</li><li><code>vector</code>:向量</li><li><code>two dimensional array</code>:二维数组</li><li><code>multiplication</code>:乘法</li><li><code>inverse</code>:逆、<code>transpose</code>:转置</li><li><code>superscript</code>:上标、<code>subscript</code>:下标</li><li><code>feature scaling</code>:特征放缩</li><li><code>standard deviation</code>:标准差</li><li><code>average value</code>:平均值</li><li><code>polynomial</code>:多项式</li><li><code>normal equation</code>:正规方程</li></ul><h2 id="参考"><a href="#参考" class="headerlink" title="参考"></a>参考</h2><blockquote><p><a href="https://www.tinymind.cn/articles/1217" target="_blank" rel="noopener">数据特征 标准化和归一化你了解多少?</a></p><p><a href="https://www.zhouyongyi.com/andrew-ng-machine-learning-notes-3/" target="_blank" rel="noopener">驿舟小站</a></p><p><a href="https://github.com/fengdu78/Coursera-ML-AndrewNg-Notes" target="_blank" rel="noopener">Coursera-ML-AndrewNg-Notes</a></p><p><a href="https://www.coursera.org/course/ml" target="_blank" rel="noopener">斯坦福大学 2014 机器学习</a></p></blockquote>]]></content>
<summary type="html">
本文内容介绍「多变量线性回归」问题。首先引入一个多维特征模型,利用「多变量梯度下降」来迭代求出代价函数最小值。之后介绍了「多项式回归」问题,以及「正规方程」,并把正规方程和梯度下降问题做了比较。
</summary>
<category term="笔记整理" scheme="https://wiki.hushhw.cn/categories/%E7%AC%94%E8%AE%B0%E6%95%B4%E7%90%86/"/>
<category term="机器学习" scheme="https://wiki.hushhw.cn/categories/%E7%AC%94%E8%AE%B0%E6%95%B4%E7%90%86/%E6%9C%BA%E5%99%A8%E5%AD%A6%E4%B9%A0/"/>
<category term="Machine Learning" scheme="https://wiki.hushhw.cn/tags/Machine-Learning/"/>
</entry>
<entry>
<title>吴恩达《机器学习》笔记(二)——单变量线性回归</title>
<link href="https://wiki.hushhw.cn/posts/df3b9948.html"/>
<id>https://wiki.hushhw.cn/posts/df3b9948.html</id>
<published>2019-07-16T11:49:49.000Z</published>
<updated>2021-02-03T06:56:56.836Z</updated>
<content type="html"><![CDATA[<blockquote><p>本文内容主要介绍「单变量线性回归」的问题。</p><p>借助一个单变量线性回归模型,求得它的「代价函数」,并利用「梯度下降」的方法来最小化代价函数,从而达到训练模型的目的。</p></blockquote><h2 id="Linear-Regression-with-One-Variable"><a href="#Linear-Regression-with-One-Variable" class="headerlink" title="Linear Regression with One Variable"></a>Linear Regression with One Variable</h2><h3 id="2-1-Univariate-linear-regression"><a href="#2-1-Univariate-linear-regression" class="headerlink" title="2.1 Univariate linear regression"></a>2.1 Univariate linear regression</h3><ul><li>$𝑚$ 代表训练集中实例的数量 </li><li>$𝑥$ 代表特征/输入变量 </li><li>$𝑦$ 代表目标变量/输出变量 </li><li>$(𝑥,𝑦)$ 代表训练集中的实例 </li><li>$(𝑥^{(𝑖)},𝑦^{(𝑖)})$ 代表第𝑖 个观察实例 </li><li>$ℎ$ 代表学习算法的解决方案或函数也称为假设(<strong>hypothesis</strong>) </li></ul><p>这里以房屋价格预测为例,我们的学习算法中,我么需要通过学习得到一个假设 h,h 表示一个函数,输入房屋尺寸大小 x,从而得到输出房屋的价格 y,因此 h 是一个从 x 到 y 的函数映射。<br><img src="https://photo.hushhw.cn/20190716121509.png" alt></p><p>这里我们采用 $h_{\theta}(x) = \theta_0 + \theta_1x$ 来表达 h,因为只含有一个特征/输入变量,因此被称为<strong>单变量线性回归问题(Univariate linear regression)</strong>。</p><p> </p><h3 id="2-2-Cost-function"><a href="#2-2-Cost-function" class="headerlink" title="2.2 Cost function"></a>2.2 Cost function</h3><p>我们前面得到了预测的函数是一个线性函数:$h_{\theta}(x) = \theta_0 + \theta_1x$,下面我们需要选择合适的参数(parameters)$\theta_0$ 和 $\theta_1$ 来使得我们的预测更加准确,模型所预测的值和训练集中实际值之间的差距就是<strong>建模误差(modeling error)</strong>。</p><p>一般而言,我们通过调整 θ,使得所有训练集数据与其拟合数据的差的平方和更小,即认为得到了拟合度更好的函数。从而我们得到<strong>代价函数(cost function)</strong>:</p><script type="math/tex; mode=display">J(\theta_0,\theta_1) = \frac{1}{2m}\sum^m_{i=1}(h_{\theta}(x^{(i)})-y^{(i)})^2</script><p>这里的$\frac{1}{2}$ 是为了方便后面的运算而加上的。</p><p>代价函数又被称为<strong>平方误差函数</strong>,有时也被称为<strong>平方误差代价函数</strong>。求误差的平方在大多数问题,特别是回归问题都是一个合理的选择。</p><p>回顾一下目前为止我们的思路:</p><p><img src="https://photo.hushhw.cn/20190716133921.png" alt></p><p> </p><h3 id="2-3-Gradient-Descent"><a href="#2-3-Gradient-Descent" class="headerlink" title="2.3 Gradient Descent"></a>2.3 Gradient Descent</h3><p>本节来解决如何求 $minimize J(\theta_0, \theta_1)$ 的问题。</p><p><strong>梯度下降(Gradient Descent)</strong> 是一个用来求函数最小值的算法,我们将使用梯度下降算法求出代价函数最小值。</p><p>梯度下降背后的思想是:开始时我们随机选择一个参数的组合 $(𝜃<em>0,𝜃_1,…,𝜃</em>𝑛)$,计算代价函数,然后我们寻找下一个能让代价函数值下降最多的参数组合。我们持续这么做直到到到一个局部最小值(local minimum),因为我们并没有尝试完所有的参数组合,所以不能确定我们得到的局部最小值是否便是全局最小值(global minimum),选择不同的初始参数组合,可能会找到不同的局部最小值。 </p><p><img src="https://photo.hushhw.cn/20190716135043.png" alt></p><p><img src="https://photo.hushhw.cn/20190716135116.png" alt></p><p>上面是一个非线性函数的代价函数,我们可以看到,选取不同的初始值 θ,可能会使得迭代的代价函数最后进入不同的极小值点。</p><p>梯度下降算法公式:</p><p><img src="https://photo.hushhw.cn/20190716135452.png" alt></p><p>其中,</p><ul><li>$\alpha$ 是<strong>学习率(learning rate)</strong>,它决定了我们沿着能让代价函数下降程度最大的方向向下迈出的步子有多大。$\alpha$ 不能太大也不能太小,太小了会使得移动的速率很慢,需要很多步才能到达全局最低点。太大了可能会直接越过了最低点,甚至可能无法收敛,甚至发散。</li><li>:= 表示赋值(assignment)</li></ul><p>对于单变量线性回归的梯度函数而言,其代价函数 J 关于参数 θ的图像如下,只有一个极值以及最值:</p><p><img src="https://photo.hushhw.cn/20190716141637.png" alt></p><p>我们让模型再简化一下,取J(θ)=θx,其代价函数关于θ的图像如下。我们通过观察他的迭代过程,有助于理解梯度下降算法:</p><p><img src="https://photo.hushhw.cn/20190716141721.png" alt></p><p>可以看到:</p><ul><li>当 θ 大于最小值时,导数为正,那么迭代公式 $\theta := \theta - \alpha\frac{∂}{∂{\theta}}J(\theta)$ 里,θ 减去一个正数,向左往最小值逼近;</li><li>当θ小于最小值时,导数为负,那么迭代公式 $\theta := \theta - \alpha\frac{∂}{∂{\theta}}J(\theta)$ 里,θ 减去一个负数,向右往最小值逼近。</li></ul><p> </p><h3 id="2-4-Gradient-Descent-For-Linear-Regression"><a href="#2-4-Gradient-Descent-For-Linear-Regression" class="headerlink" title="2.4 Gradient Descent For Linear Regression"></a>2.4 Gradient Descent For Linear Regression</h3><p>梯度下降(Gradient Descent)是很常用的算法,这一节我们将用到此算法,并将其应用于具体的拟合直线的线性回归算法里。 </p><p><img src="https://photo.hushhw.cn/20190716165528.png" alt></p><p> </p><h2 id="专业名词整理"><a href="#专业名词整理" class="headerlink" title="专业名词整理"></a>专业名词整理</h2><ul><li><code>hypothesis</code>:假设</li><li><code>parameters</code>:参数</li><li><code>Gradient Descent</code>:梯度下降</li><li><code>Univariate linear regression</code>:单变量线性回归</li><li><code>cost function</code>:代价函数</li><li><code>local minimun</code>:局部最小值</li><li><code>learning rate</code>:学习率</li><li><code>iterative algorithm</code>:迭代算法</li><li><code>derivative term</code>:导数项、<code>partial derivative</code>:偏导数</li><li><code>equation</code>:方程式</li><li><code>positive slope</code>:正斜率、<code>negative slope</code>:负斜率</li></ul><h2 id="参考"><a href="#参考" class="headerlink" title="参考"></a>参考</h2><blockquote><p><a href="https://www.zhouyongyi.com/andrew-ng-machine-learning-notes-2/" target="_blank" rel="noopener">驿舟小站</a></p><p><a href="https://github.com/fengdu78/Coursera-ML-AndrewNg-Notes" target="_blank" rel="noopener">Coursera-ML-AndrewNg-Notes</a></p><p><a href="https://www.coursera.org/course/ml" target="_blank" rel="noopener">斯坦福大学 2014 机器学习</a> </p><p><a href="https://wei2624.github.io/MachineLearning/sv_discriminative_model/" target="_blank" rel="noopener">Discriminative Algorithm</a> </p></blockquote>]]></content>
<summary type="html">
本文内容主要介绍「单变量线性回归」的问题。借助一个单变量线性回归模型,求得它的「代价函数」,并利用「梯度下降」的方法来最小化代价函数。
</summary>
<category term="笔记整理" scheme="https://wiki.hushhw.cn/categories/%E7%AC%94%E8%AE%B0%E6%95%B4%E7%90%86/"/>
<category term="机器学习" scheme="https://wiki.hushhw.cn/categories/%E7%AC%94%E8%AE%B0%E6%95%B4%E7%90%86/%E6%9C%BA%E5%99%A8%E5%AD%A6%E4%B9%A0/"/>
<category term="Machine Learning" scheme="https://wiki.hushhw.cn/tags/Machine-Learning/"/>
</entry>
<entry>
<title>吴恩达《机器学习》笔记(一)——介绍</title>
<link href="https://wiki.hushhw.cn/posts/ef2d7590.html"/>
<id>https://wiki.hushhw.cn/posts/ef2d7590.html</id>
<published>2019-07-16T09:30:01.000Z</published>
<updated>2021-02-03T06:56:56.836Z</updated>
<content type="html"><![CDATA[<blockquote><p> 本系列学习笔记用来记录我学习吴恩达教授的《机器学习》课程,课程版本为在 Coursera 版,在 B 站、网易云课堂都可以找到相应的视频资源,配合 <a href="https://github.com/fengdu78" target="_blank" rel="noopener">@fengdu78</a> 整理的笔记「<a href="https://github.com/fengdu78/Coursera-ML-AndrewNg-Notes" target="_blank" rel="noopener"><strong>Coursera-ML-AndrewNg-Notes</strong></a>」来学习。</p><p> 限于本人是机器学习的初学者,在笔记过程中难免有思考不周之处,读者请自行查阅吴恩达教授课程内容。</p><p>本文内容包括对「机器学习」定义的阐述,及「监督学习」和「非监督学习」的基本理解。</p></blockquote><h2 id="Introduction"><a href="#Introduction" class="headerlink" title="Introduction"></a>Introduction</h2><h3 id="1-1-What-is-Machine-Learning"><a href="#1-1-What-is-Machine-Learning" class="headerlink" title="1.1 What is Machine Learning"></a>1.1 What is Machine Learning</h3><p>什么是机器学习?即使是在机器学习的专业人士中,也不存在一个被广泛认可的定义来准确定义机器学习。</p><p><strong>1. Arthur Samue 提出的定义:</strong></p><blockquote><p>“The field of study that gives computers the ability to learn without being explicitly programmed.”</p></blockquote><p>在进行特定编程的情况下,给予计算机学习能力的领域。</p><p><strong>2. Tom Mitchell 提出的定义:</strong></p><blockquote><p>“A computer program is said to learn from experience E with respect to some class of tasks T and performance measure P, if its performance at tasks in T, as measured by P, improves with experience E.”</p></blockquote><p>一个好的学习问题定义如下,一个程序被认为能从经验 E 中学习,解决任务 T,达到性能度量值 P,当且仅当,有了经验 E 后,经过 P 评判,程序在处理 T 时的性能有所提升。</p><p> </p><h3 id="1-2-Supervised-Learning"><a href="#1-2-Supervised-Learning" class="headerlink" title="1.2 Supervised Learning"></a>1.2 Supervised Learning</h3><blockquote><p>In supervised learning, we are given a data set and already know what our correct output should look like, having the idea that there is a relationship between the input and the output.</p></blockquote><p>在监督学习中,我们已经得到了一个数据集,并且数据集中的每一个样本都是“正确答案”,再根据这些样本来做出预测。</p><p>监督学习的问题被分为<strong>“回归(regression)</strong>和<strong>“分类(classification)”</strong>两类。</p><ul><li>在回归问题中,我们尝试预测出<strong>连续的</strong>输出。</li><li>在分类问题中,我们尝试预测出<strong>离散的</strong>输出。</li></ul><p>在这种监督学习模式下,我们有输入模块叫<strong>特征(features)</strong>,和输出模块叫<strong>目标(target)</strong>。学习的目的是基于给定的输入和对应的标签训练模型,然后用训练好的模型对给定的新输入来预测输出。</p><p>为此,我们收集一个训练<strong>数据集(training set)</strong>,在这个数据集中,我们有许多成对的<strong>训练样本</strong>,每对样本包含<strong>特征向量(feature vector)</strong>作为输入(用符号 X 表示所有的特征向量)及其相应的<strong>目标(output)</strong>作为输出(用符号 Y 表示所有的目标值)。 由于每一个输入都有来自事实对应的标签,我们将这种学习称为监督学习(supervised learning),同时将训练好的模型称为<strong>假设(hypothesis)</strong>。 </p><p> </p><h3 id="1-3-Unsupervised-Learning"><a href="#1-3-Unsupervised-Learning" class="headerlink" title="1.3 Unsupervised Learning"></a>1.3 Unsupervised Learning</h3><blockquote><p>Unsupervised learning, allows us to approach problems with little or no idea what our results should look like. We can derive structure from data where we don’t necessarily know the effect of the variables.</p></blockquote><p>无监督学习中,我们对对已知的数据集不知道如何处理,未被告知每一个数据点是什么。我们可以在不知道变量的具体影响的情况下,从数据中提取出结构(structure)。</p><p>我们可以根据数据中的变量关系对数据进行<strong>聚类(clustering)</strong>,来提取出数据的结构。</p><p><img src="https://photo.hushhw.cn/20190716112626.png" alt></p><p>聚类应用的例子,如在谷歌新闻中,将同一主题的新闻事件聚类在一起显示。我们没有提前告知算法一些信息,算法自动地聚类,我们没有算法正确答案来回应数据集中的数据。</p><h2 id="专业名词整理"><a href="#专业名词整理" class="headerlink" title="专业名词整理"></a>专业名词整理</h2><ul><li><code>supervised learning</code>:监督学习</li><li><code>regresssion</code>:回归</li><li><code>classification</code>:分类</li><li><code>horizontal axis</code>:横轴、<code>vertical axis</code>:纵轴</li><li><code>quadratic function</code>:二次函数、<code>cubic function</code>:三次函数</li><li><code>discrete value</code>:离散值、<code>continuous value</code>:连续值</li><li><code>training set</code>:训练集、<code>data set</code>:数据集</li><li><code>unsupervised learning</code>:非监督学习</li><li><code>cluster</code>:簇、<code>clustering algorithm</code>:聚类算法</li></ul><h2 id="参考"><a href="#参考" class="headerlink" title="参考"></a>参考</h2><blockquote><p><a href="https://www.zhouyongyi.com/andrew-ng-machine-learning-notes-1/" target="_blank" rel="noopener">驿舟小站</a></p><p><a href="https://github.com/fengdu78/Coursera-ML-AndrewNg-Notes" target="_blank" rel="noopener">Coursera-ML-AndrewNg-Notes</a></p><p><a href="https://www.coursera.org/course/ml" target="_blank" rel="noopener">斯坦福大学 2014 机器学习</a></p></blockquote>]]></content>
<summary type="html">
本系列学习笔记用来记录我学习吴恩达教授的《机器学习》课程。本文内容包括对「机器学习」定义的阐述,及「监督学习」和「非监督学习」的基本理解。
</summary>
<category term="笔记整理" scheme="https://wiki.hushhw.cn/categories/%E7%AC%94%E8%AE%B0%E6%95%B4%E7%90%86/"/>
<category term="机器学习" scheme="https://wiki.hushhw.cn/categories/%E7%AC%94%E8%AE%B0%E6%95%B4%E7%90%86/%E6%9C%BA%E5%99%A8%E5%AD%A6%E4%B9%A0/"/>
<category term="Machine Learning" scheme="https://wiki.hushhw.cn/tags/Machine-Learning/"/>
</entry>
<entry>
<title>《图解密码技术》学习笔记之密码(二)</title>
<link href="https://wiki.hushhw.cn/posts/feaace02.html"/>
<id>https://wiki.hushhw.cn/posts/feaace02.html</id>
<published>2019-05-07T21:00:27.000Z</published>
<updated>2021-02-03T06:56:56.836Z</updated>
<content type="html"><![CDATA[<blockquote><p>本文接『<a href="https://wiki.hushhw.cn/posts/418f8d38.html">《图解密码技术》学习笔记之密码(一)</a>』继续补充密码部分的基础知识。</p><p>内容概述:</p><ul><li>公钥密码</li><li>混合密码系统</li></ul></blockquote><a id="more"></a><h2 id="5-公钥密码"><a href="#5-公钥密码" class="headerlink" title="5. 公钥密码"></a>5. 公钥密码</h2><h3 id="5-1-密钥配送问题"><a href="#5-1-密钥配送问题" class="headerlink" title="5.1 密钥配送问题"></a>5.1 密钥配送问题</h3><p>在对称密码中,由于加密和解密的密钥是相同的,因此必须像接收者配送密钥。用于解密的密钥必须配送给接收者,这一问题称为密钥配送问题。</p><p>解决密钥配送问题的方法:</p><ul><li>通过事先共享密钥来解决:事先用安全的方式将密钥交给对方。</li><li>通过密钥分配中心来解决:有一个密钥分配中心生成通信密钥,每个人只要和密钥分配中心实现共享密钥就可以了。</li><li>通过 Diffie-Hellman 密钥交换来解决:发送者和接收者之间相互传递信息,利用这些信息各自生成相同的密钥。</li><li>通过公钥密码来解决</li></ul><h3 id="5-2-公钥密码"><a href="#5-2-公钥密码" class="headerlink" title="5.2 公钥密码"></a>5.2 公钥密码</h3><p><strong>公钥密码</strong>(public-key cryptography)(又称<strong>非对称密码</strong>)中,密钥分为加密密钥和解密密钥两种。发送者用加密密钥对消息进行加密,接收者用解密密钥对密文进行解密。两者的区别:</p><ul><li>发送者只需要加密密钥</li><li>接收者只需要解密密钥</li><li>解密密钥不可以被窃听者获取</li><li>加密密钥被窃听者获取也没问题</li></ul><p>解密密钥一开始就是由接收者自己保管,因此只要将加密密钥发给发送者就可以解决密钥配送问题了,而根本不需要配送解密密钥。</p><p><strong>公钥</strong>(public key):加密密钥可以被任意公开,被称为公钥。</p><p><strong>私钥</strong>(private key):解密密钥是绝对不能公开的,称为私钥。</p><p>公钥和私钥一一对应,一对公钥和私钥统称为密钥对(key pair)。由公钥进行加密的密文,必须使用与该公钥配对的私钥才能够解密。</p><p>公钥密码解决了密码配送问题,但我们还需要判断所得到的公钥是否正确合法,并且其处理速度只有对称密码的几百分之一。</p><h3 id="5-3-RSA"><a href="#5-3-RSA" class="headerlink" title="5.3 RSA"></a>5.3 RSA</h3><p><strong>RSA</strong> 是一种公钥密码算法,名字来自三位开发者 Ron Rivest、Adi Shamir 和 Leonard Adleman。RSA 可以被用于公钥密码和数字签名。</p><p>RSA 加密公式:$密文 = 明文^EmodN$ ,即 RSA 的密文是对代表明文的数字的 E 次方求 mod N 的结果,这里的 E(Encryption,加密) 和 N(number,数字) 的组合就是公钥。</p><p>RSA 解密公式:$明文 = 密文^D modN$ ,即 RSA 的明文是对代表密文的数字的 D 次方求 mod N 的结果,这里d的 D(Decryption,解密)和 N 的组合就是密钥。</p><p><img src="https://photo.hushhw.cn/images/wKioL1hemhCT2YyuAAB521Eadok617.png" alt></p><p>RSA 密码对的生成步骤即求 N、L(一个中间数字)、E 和 D 的过程,如下图:</p><p><img src="https://photo.hushhw.cn/images/20190113152536907.png" alt></p><h3 id="5-4-其他公钥密码"><a href="#5-4-其他公钥密码" class="headerlink" title="5.4 其他公钥密码"></a>5.4 其他公钥密码</h3><p><strong>ElGamal 方式</strong>:由 Taher ElGamal 设计的公钥算法。RSA 利用了质因数分解的困难度,而 ElGamal 方式则利用了 mod N 下求离散对数的困难度。其缺点是经过加密的密文长度会变成明文的两倍。</p><p><strong>Rabin 方式</strong>:由 M.O.Rabin 设计的公钥算法。Rabin 方式利用了 mod N 下求平方根的困难度。</p><p><strong>椭圆曲线密码</strong>(Elliptic Curve Cryptography,ECC):通过将椭圆曲线上的特定点进行特殊的乘法运算来实现,它利用了乘法运算的逆运算非常困难这一特性。</p><p> </p><h2 id="6-混合密码系统"><a href="#6-混合密码系统" class="headerlink" title="6. 混合密码系统"></a>6. 混合密码系统</h2><p>对称密码无法解决密钥配送问题,公钥密码的处理速度远远低于对称密码并且难以抵御中间人攻击。</p><p><strong>混合密码系统</strong>(hybrid cryptosystem)是将对称密码和公钥密码的优势相结合的方法。一般情况下,将两种不同的方式组合的做法就称为混合(hybrid)。</p><p>混合密码系统的本质是将消息通过对称密码来加密,将加密消息时使用的密钥通过公钥密码来加密。</p><p>由于对称密码的密钥一般比消息本身要短,因此公钥密码速度慢的问题就可以忽略了。</p><p>混合密码系统的组成机制:</p><ul><li><p>用对称密码加密消息</p></li><li><p>通过伪随机数生成器生成对称密码加密中使用的会话密钥</p></li><li><p>用公钥密码加密会话密钥</p></li><li><p>从混合密码系统外部赋予公钥密码加密时使用的密钥</p></li></ul><p>混合密码系统运用了伪随机数生成器、对称密码和公钥密钥这三种密码技术,正是通过这三种密码技术的结合,才创造出了一种兼具对称密码和公钥密码优点的密码方式。</p><p>混合密码系统的加密过程如下:</p><p><img src="https://photo.hushhw.cn/images/101_22.png" alt></p><p>混合密码系统的解密过程如下:</p><p><img src="https://photo.hushhw.cn/images/101_21.png" alt></p><p>怎样才是高强度的混合密码系统:</p><ul><li><p>伪随机数生成器:采用算法较佳</p></li><li><p>对称密码:使用高强度</p></li><li><p>公钥密码:高强度</p></li></ul><p>密码长度的平衡:对称密码和公钥密码的密钥长度必须具备同等的强度。然而,考虑到长期运用的情况,公钥密码的强度应该要高于对称密码,因为对称密码的会话密钥被破译只会影响本次通信的内容,而公钥密码一旦被破译,从过去到未来的(用相同公钥加密的)所有通信内容就能够被破译了。</p>]]></content>
<summary type="html">
<blockquote>
<p>本文接『<a href="https://wiki.hushhw.cn/posts/418f8d38.html">《图解密码技术》学习笔记之密码(一)</a>』继续补充密码部分的基础知识。</p>
<p>内容概述:</p>
<ul>
<li>公钥密码</li>
<li>混合密码系统</li>
</ul>
</blockquote>
</summary>
<category term="笔记整理" scheme="https://wiki.hushhw.cn/categories/%E7%AC%94%E8%AE%B0%E6%95%B4%E7%90%86/"/>
<category term="密码学" scheme="https://wiki.hushhw.cn/categories/%E7%AC%94%E8%AE%B0%E6%95%B4%E7%90%86/%E5%AF%86%E7%A0%81%E5%AD%A6/"/>
<category term="密码学" scheme="https://wiki.hushhw.cn/tags/%E5%AF%86%E7%A0%81%E5%AD%A6/"/>
<category term="学习笔记" scheme="https://wiki.hushhw.cn/tags/%E5%AD%A6%E4%B9%A0%E7%AC%94%E8%AE%B0/"/>
</entry>
<entry>
<title>虚拟机 VMware 中安装 Ubuntu 操作系统</title>
<link href="https://wiki.hushhw.cn/posts/6fbc30d9.html"/>
<id>https://wiki.hushhw.cn/posts/6fbc30d9.html</id>
<published>2019-04-25T14:54:43.000Z</published>
<updated>2021-02-03T06:56:56.836Z</updated>
<content type="html"><![CDATA[<blockquote><p>大三下学期在上的「操作系统」课程的时候就想装个虚拟机来学习一下 Linux,时到现在有空折腾了才开始做这件事情,但总算是开始做了。</p><p>折腾在虚拟机中安装 Ubuntu 操作系统花了我一天时间,写下这篇文章来记录这个过程,留作以后再学习排坑。</p></blockquote><p><img src="https://photo.hushhw.cn/images/Snipaste_2019-04-25_15-04-07.png" alt></p><h2 id="1-安装前的准备和基本安装"><a href="#1-安装前的准备和基本安装" class="headerlink" title="1. 安装前的准备和基本安装"></a>1. 安装前的准备和基本安装</h2><h3 id="1-1-虚拟机-VMware-下载"><a href="#1-1-虚拟机-VMware-下载" class="headerlink" title="1.1 虚拟机 VMware 下载"></a>1.1 虚拟机 <strong>VMware</strong> 下载</h3><p>可以自行去官网下载然后找一下激活密钥,我这里存了一份找到了一个 <code>15.0.0</code> 版本可供 <i class="fa fa-download fa"></i> <a href="https://pan.baidu.com/s/1QbcYGlaX_bTLqQiYN571eQ" target="_blank" rel="noopener">百度网盘</a> 下载,提取码:<code>t7ys</code> 。</p><h3 id="1-2-操作系统-Ubuntu-下载"><a href="#1-2-操作系统-Ubuntu-下载" class="headerlink" title="1.2 操作系统 Ubuntu 下载"></a>1.2 操作系统 <strong>Ubuntu</strong> 下载</h3><p>可到 <a href="http://mirrors.ustc.edu.cn/" target="_blank" rel="noopener">中科大镜像源</a> 下载你需要的版本,如下图所示:</p><p><img src="https://photo.hushhw.cn/images/Snipaste_2019-04-25_15-26-58.png" alt></p><p>我选择了最新的发行版本 <code>19.04</code> 的映像文件来安装,配合 VMware 15 没有出现兼容问题,之前电脑上装的 VMware 8 版本太低,安装过程中报错。</p><h3 id="1-3-电脑开启虚拟化技术"><a href="#1-3-电脑开启虚拟化技术" class="headerlink" title="1.3 电脑开启虚拟化技术"></a>1.3 电脑开启虚拟化技术</h3><p>各电脑厂商可能不太一样,但基本都是进入BIOS下开启虚拟化技术,可以自行 Google 操作。</p><h3 id="1-4-基本安装"><a href="#1-4-基本安装" class="headerlink" title="1.4 基本安装"></a>1.4 基本安装</h3><p>有了映像文件之后,只需要按照提示一步一步操作即可安装 Ubuntu,如果有什么问题可以在网上找一下别人的安装教材看看,我在 B 站看了一位大佬做了简单的安装过程视频,视频地址:<a href="https://www.bilibili.com/video/av24804060" target="_blank" rel="noopener">VMware 安装Ubuntu 18.04 LTS</a> 。</p><p> </p><h2 id="2-遇到的一些问题及解决方案"><a href="#2-遇到的一些问题及解决方案" class="headerlink" title="2. 遇到的一些问题及解决方案"></a>2. 遇到的一些问题及解决方案</h2><p>以下很多操作都可以用命令行来解决问题,也可以用图形界面来操作。</p><h3 id="2-1-显示屏幕太小解决办法"><a href="#2-1-显示屏幕太小解决办法" class="headerlink" title="2.1 显示屏幕太小解决办法"></a>2.1 显示屏幕太小解决办法</h3><p>使用 VMware 安装的 ubuntu 虚拟机的显示屏幕太小,可以通过在 VMware 里安装「VMware Tool」插件解决。 具体操作过程可以参考这篇文章 <a href="https://blog.csdn.net/dcrmg/article/details/74090307" target="_blank" rel="noopener">VMware虚拟机ubuntu显示屏幕太小解决办法</a> 。我安装是通过 VMware 15 虚拟机安装完成,所以步骤和文章中有一些不同,但是大致是一样的原理。</p><h3 id="2-2-设置语言环境"><a href="#2-2-设置语言环境" class="headerlink" title="2.2 设置语言环境"></a>2.2 设置语言环境</h3><p>参考文章:<a href="https://blog.csdn.net/qq_19339041/article/details/80058575" target="_blank" rel="noopener">更改系统语言为简体中文</a> </p><p>如果你在语言环境下载包的过程中非常慢,可以考虑先把下面的「设置服务器镜像源」先改了再说。</p><p>如果你发现你得 Ubuntu 似乎没有网络,那么请在 VMware 设置当前虚拟机的网络适配器,具体可以网上找一找原因。</p><p>安装完成后,建议保留旧的名称。在terminal中有中文,后期开发使用可能会有问题。</p><h3 id="2-3-设置服务器镜像源"><a href="#2-3-设置服务器镜像源" class="headerlink" title="2.3 设置服务器镜像源"></a>2.3 设置服务器镜像源</h3><p>Ubuntu 中大部分软件的安装/更新都是利用 <code>apt</code> 命令,从 Ubuntu 的服务器直接安装的,Ubuntu 官方服务器在国外,为了提高软件安装更新速度,Ubuntu 提供了选择最佳服务器的功能,可以帮助我们方便的找到一个速度最快的镜像服务器。</p><p>旧版本中在系统设置中可以找到 <code>软件和更新</code> 这个选项,但是我在 <code>19.0.4</code> 这个版本中并没有找到,所以直接找到文件位置 <code>etc/apt/sources.list</code>,打开文件后选择下载源为 <code>其他站点</code>,之后点击 <code>选择最佳服务器</code> ,退出来时弹框点击<code>重新载入</code>。</p><h3 id="2-4-修改时区和更新时间"><a href="#2-4-修改时区和更新时间" class="headerlink" title="2.4 修改时区和更新时间"></a>2.4 修改时区和更新时间</h3><p>参考文章:<a href="https://blog.csdn.net/zhengchaooo/article/details/79500032" target="_blank" rel="noopener">Ubuntu 修改时区和更新时间</a> </p><p> </p><h2 id="3-apt-终端命令"><a href="#3-apt-终端命令" class="headerlink" title="3. apt 终端命令"></a>3. apt 终端命令</h2><p><code>apt</code> 是 <code>Adavanced Packaging Tool</code>,是 Ubuntu 下的安装包管理工具。大部分软件的安装/更新都是利用 <code>apt</code> 命令,直接在终端中输入 <code>apt</code> 即可以查阅命令的帮助信息。</p><p>常用命令:</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"># 1. 安装软件</span><br><span class="line">$ sudo apt install 软件名</span><br><span class="line"></span><br><span class="line"># 2. 卸载软件</span><br><span class="line">$ sudo apt remove 软件名</span><br><span class="line"></span><br><span class="line"># 3. 更新可用软件包列表</span><br><span class="line">$ sudo apt update</span><br><span class="line"></span><br><span class="line"># 4. 更新已安装的包</span><br><span class="line">$ sudo apt upgrade</span><br></pre></td></tr></table></figure><p><code>apt</code> 安装命令并不需要记忆,如果在终端中输入的软件没有安装,系统会提示 apt 命令的使用格式。</p><p>例如 python 双版本的安装:</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">$ sudo apt install ipython</span><br><span class="line">$ sudo apt install ipython3</span><br><span class="line">$ sudo apt install python-pip</span><br><span class="line">$ sudo apt install python3-pip</span><br></pre></td></tr></table></figure><p> </p><h2 id="4-deb-安装格式"><a href="#4-deb-安装格式" class="headerlink" title="4. deb 安装格式"></a>4. deb 安装格式</h2><p><code>deb</code> 是 Debian Linux 的安装格式,在 ubuntu 中同样可以使用。要安装 deb 安装包,需要使用 <code>dpkg</code> 这个终端命令,格式如下:</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">sudo dpkg -i <package.deb></span><br></pre></td></tr></table></figure><h3 id="4-1-谷歌浏览器"><a href="#4-1-谷歌浏览器" class="headerlink" title="4.1 谷歌浏览器"></a>4.1 谷歌浏览器</h3><p>从 chrome 官网下载到 .deb 安装文件,拷贝到 ubuntu 系统中,然后到该文件夹下执行:</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">$ sudo apt install libappindicator1 libindicator7</span><br><span class="line">$ sudo dpkg -i google-chrome-stable_current_amd64.deb</span><br><span class="line">$ sudo apt -f install</span><br></pre></td></tr></table></figure><h3 id="4-2-搜狗输入法"><a href="#4-2-搜狗输入法" class="headerlink" title="4.2 搜狗输入法"></a>4.2 搜狗输入法</h3><p><code>fcitx</code> 被称为小企鹅输入法,是一个以 GPL 方式发布的输入法平台,可以通过安装引擎支持多种输入法,首先安装 Fcitx 输入法框架:</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">$ sudo apt install fcitx</span><br></pre></td></tr></table></figure><p>之后到搜狗输入法官网下载 Linux 版本,拷贝到 ubuntu 系统中,在对应文件夹下执行:</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">$ sudo dpkg -i sogoupinyin_2.2.0.0108_amd64.deb</span><br><span class="line">$ sudo apt -f install</span><br></pre></td></tr></table></figure><p>其实前面忘了装 fcitx 这里命令行会提示里让你修复安装的。</p><p>安装过后,进入设置语言安装界面,设置键盘输入系统为 fcitx,重启虚拟机后右上角就有了键盘标志。更多细节可以参考:<a href="https://blog.csdn.net/lupengCSDN/article/details/80279177" target="_blank" rel="noopener">Ubuntu18.04下安装搜狗输入法</a> 。</p>]]></content>
<summary type="html">
大三下学期在上的「操作系统」课程的时候就想装个虚拟机来学习一下 Linux,时到现在有空折腾了才开始做这件事情,但总算是开始做了。折腾在虚拟机中安装 Ubuntu 操作系统花了我一天时间,写下这篇文章来记录这个过程,留作以后再学习排坑。
</summary>
<category term="编程开发" scheme="https://wiki.hushhw.cn/categories/%E7%BC%96%E7%A8%8B%E5%BC%80%E5%8F%91/"/>
<category term="环境配置" scheme="https://wiki.hushhw.cn/categories/%E7%BC%96%E7%A8%8B%E5%BC%80%E5%8F%91/%E7%8E%AF%E5%A2%83%E9%85%8D%E7%BD%AE/"/>
<category term="Linux" scheme="https://wiki.hushhw.cn/categories/%E7%BC%96%E7%A8%8B%E5%BC%80%E5%8F%91/%E7%8E%AF%E5%A2%83%E9%85%8D%E7%BD%AE/Linux/"/>
<category term="Ubuntu" scheme="https://wiki.hushhw.cn/tags/Ubuntu/"/>
<category term="Linux" scheme="https://wiki.hushhw.cn/tags/Linux/"/>
</entry>
<entry>
<title>《图解密码技术》学习笔记之密码(一)</title>
<link href="https://wiki.hushhw.cn/posts/418f8d38.html"/>
<id>https://wiki.hushhw.cn/posts/418f8d38.html</id>
<published>2019-04-21T11:20:55.000Z</published>
<updated>2021-02-03T06:56:56.836Z</updated>
<content type="html"><![CDATA[<blockquote><p>本文整理「密码」这一部分的基础知识点。</p><p>内容概述:</p><ul><li>经典密码</li><li>对称密码</li><li>分组密码</li></ul></blockquote><a id="more"></a><h2 id="1-环游密码世界-概述"><a href="#1-环游密码世界-概述" class="headerlink" title="1. 环游密码世界(概述)"></a>1. 环游密码世界(概述)</h2><h3 id="1-1-基础概念"><a href="#1-1-基础概念" class="headerlink" title="1.1 基础概念"></a>1.1 基础概念</h3><ul><li>发送者、接收者和窃听者</li><li>加密与解密</li><li>破译</li><li>密码算法、密钥</li></ul><h3 id="1-2-密码学家的工具箱"><a href="#1-2-密码学家的工具箱" class="headerlink" title="1.2 密码学家的工具箱"></a>1.2 密码学家的工具箱</h3><ul><li>对称密码: 在加密和解密,使用同一个密钥的方式。</li><li>公钥密码/非对称密码: 是指在加密和解密时使用不同密钥的方式。</li><li>单向散列函数/哈希值/密码校验/消息摘要:所保证的并不是机密性,而是完整性。即可以检测出数据是否被篡改过。</li><li>消息认证码:不仅能够保证完整性,而且能够提供认证机制。即不但能确认消息是否被篡改过,而且能够确认消息是否来自所期待的通信对象。</li><li>数字签名:是一种能够保证完整性,提供认证,并防止否认的密码技术。</li><li>伪随机生成器:是一种能够模拟生产随机数列的算法,承担着密钥生成的重要职责。如果生成随机数的算法不好,窃听者就能推测出密钥,从而带来通信机密性下降的风险。</li></ul><p><img src="https://photo.hushhw.cn/images/cryptopraphy1.png" alt></p><h3 id="1-3-密码与信息安全常识"><a href="#1-3-密码与信息安全常识" class="headerlink" title="1.3 密码与信息安全常识"></a>1.3 密码与信息安全常识</h3><ul><li>不要使用保密的密码算法</li><li>使用低强度的密码比不进行任何加密更危险</li><li>任何密码总有一天都会被破解</li><li>密码只是信息安全的一部分</li></ul><p> </p><h2 id="2-经典密码"><a href="#2-经典密码" class="headerlink" title="2. 经典密码"></a>2. 经典密码</h2><h3 id="2-1-凯撒密码(Caesar-cipher)"><a href="#2-1-凯撒密码(Caesar-cipher)" class="headerlink" title="2.1 凯撒密码(Caesar cipher)"></a>2.1 凯撒密码(Caesar cipher)</h3><p><strong>凯撒密码</strong>是通过将明文中所使用的字母表按照一定的字数「平移」来进行加密。例如要保密的信息为 <code>yoshiko</code> ,按照平移 3 格加密后的数据为 <code>BRVKLNR</code> 。</p><p>解密的过程只需要反向平移相应位数就可以解密了。</p><p>破解凯撒密码的方法是<strong>暴力破解</strong>(穷举搜索),把所有可能的密钥全部尝试一遍。</p><h3 id="2-2-简单替换密码"><a href="#2-2-简单替换密码" class="headerlink" title="2.2 简单替换密码"></a>2.2 简单替换密码</h3><p><strong>简单替换密码</strong>是将明文中所使用的字母表替换为另一套字母表的密码。</p><p>解密的过程就是使用加密时所使用的替换表进行反向替换,所以发送者和接收者必须事先同时拥有该替换表,而这份替换表也就相当于简单替换密码的密钥。</p><p>破解简单替换密码很难通过暴力破解来破译,因为简单替换密码的<strong>密钥空间</strong>足够大,以 26 位字母为例,计算出来的密钥总数为 26 的阶乘,数据相当庞大,所以要采用<strong>频率分析</strong>的密码破译方法来破解简单替换密码。所谓了频率分析就是统计替换表中每个字母出现的频率,然后与英文中使用最高频的一些字母对应分析。</p><h3 id="2-3-Enigma密码机"><a href="#2-3-Enigma密码机" class="headerlink" title="2.3 Enigma密码机"></a>2.3 Enigma密码机</h3><p>Enigma 是一种有键盘、齿轮、电池和灯泡所组成的机器,通过这台机器可以完成加密和解密两种操作。</p><p> </p><h2 id="3-对称密码"><a href="#3-对称密码" class="headerlink" title="3. 对称密码"></a>3. 对称密码</h2><p>对称密码又称共享密钥密码,用相同的密钥进行加密和解密。</p><h3 id="3-1-一次性密码本"><a href="#3-1-一次性密码本" class="headerlink" title="3.1 一次性密码本"></a>3.1 一次性密码本</h3><p><strong>一次性密码本(One-time pad),又称维纳密码(Vernam cipher)</strong>:将明文与一串随机的比特序列进行异或(XOR)运算,解密就是加密的反向运算,即用密文和密钥进行异或运算得到明文。</p><p>一次性密码本是无条件安全的,在理论上是无法破译的,因为就算我们用暴力方式侥幸得到了密钥,我们也无法确定异或出来的结果是否就是正确的明文。</p><p>一次性密码本之所以没有被使用是因为发送方必须给接收方发送密文和密钥,既然有一种方法可以将密钥安全的发送出去,那岂不是也可以用同样的方法来安全的发送明文?</p><h3 id="3-2-DES"><a href="#3-2-DES" class="headerlink" title="3.2 DES"></a>3.2 DES</h3><p>DES (Data Encryption Standard) 即数据加密标准,是 1977 年美国联邦信息处理标准中所采用的一种对称密码。一直以来被美国以及其他国家的政府和银行等广泛使用,但是随着计算机的进步,现在的DES已经能够被暴力破解,强度大不如前了,现在我们<strong>不应该再使用 DES 了</strong>。</p><p>DES 是一种将 64 比特的明文加密成 64 比特的密文的对称密码算法,他的密钥长度是 56 比特(每隔 7 比特会设置一个用于错误检查的比特)。DES 以 64 比特的明文(比特序列)为一个单位进行加密,这个 64 比特的单位称为分组,<strong>DES 是分组密码的一种</strong>。DES 每次只能加密 64 比特的数据,如果要加密的明文比较长,就需要对 DES 加密进行迭代(反复),而迭代的具体方式称为<a href="#4-分组密码的模式">模式</a>(mode)。</p><p>DES 的基本结构是由 Horst Feistel 设计的,因此也成为 Feistel 网络、Feistel 结构或者 Feistel 密码。在Feistel 网络中,加密的各个步骤称为轮(round),整个加密过程就是进行若干次轮的循环。<strong>DES 是一种 16 轮循环的 Feistel 网络</strong>。</p><p>Feistel 网络一轮的具体计算步骤:</p><ul><li>将输入的数据等分为左右两部分。</li><li>将输入的右侧直接发送到输出的右侧。</li><li>将输入的右侧发送到轮函数。</li><li>轮函数根据右侧数据和子密钥,计算出一串看上去是随机的比特序列。</li><li>将上一步得到的比特序列与左侧数据进行 XOR 运算,并将结果作为加密后的左侧。</li></ul><p>这样一来「右侧」根本没有被加密,因此我们需要用不同的子密钥的一轮的处理重复若干次,并在每两轮处理之间将左侧和右侧的数据对调。</p><p><img src="https://photo.hushhw.cn/images/32421175-4b12736e-c25b-11e7-8c60-85242d72b9b3.png" alt></p><p>Feistel 网络的性质:</p><ul><li>Feistel 网络的论述可以任意增加。</li><li>加密时无论使用任何函数作为轮函数都可以正确解密。</li><li>加密和解密可以用完全相同的结构来实现。</li></ul><p>综上所述,无论是任何轮数、任何轮函数,Feistel 网络都可以用相同的结构实现加密和解密,且加密的结果必定能够正确解密。</p><h3 id="3-3-三重-DES"><a href="#3-3-三重-DES" class="headerlink" title="3.3 三重 DES"></a>3.3 三重 DES</h3><p>现在 DES 已经可以在现实的时间内被暴力破解,<strong>三重 DES</strong>(triple-DES)出于这个目的被开发出来的,但是处理速度不高,而且在安全性方面也逐渐显现出一些问题,也不推荐使用。</p><p>三重 DES 的加密机制如图:</p><p><img src="https://photo.hushhw.cn/images/wKiom1hStybR6nRgAABzM1_8bWA892.png" alt></p><p>明文经过三次 DES 处理才能变成最后的密文,由于 DES 密钥的长度实质上是 56 比特,因此三重 DES 的密钥长度就是 56*3=168 比特。</p><p>三重 DES 并不是进行三次加密,而是「加密—<strong>解密</strong>—加密」的过程,当三重 DES 中所有的密钥都是相同时,三重 DES 也就等同于普通的 DES 了。这是因为在前两步加密—解密之后,得到的就是最初的明文。因此,以前用 DES 加密的密文,就可以通过这种方式用三重 DES 来进行解密。也就是说,<strong>三重 DES 对 DES 具备向下兼容性</strong>。</p><p>三重 DES 的解密过程和加密正好相反,是以密钥 3、密钥 2、密钥 1 的顺序执行解密—加密—解密的操作:</p><p><img src="https://photo.hushhw.cn/images/wKioL1hSupqweE1JAACDK9OtHAc865.png" alt></p><h3 id="3-4-AES(Rijndael)"><a href="#3-4-AES(Rijndael)" class="headerlink" title="3.4 AES(Rijndael)"></a>3.4 AES(Rijndael)</h3><p>AES(Advanced Encryption Standard)是取代其前任标准(DES)而称为新标准的一种对称密码算法。在 2000 年从众多候选算法中选出了一种名为 <strong>Rijndael</strong> 的对称密码算法,并将其确定为 AES。Rijndael 是由比利时密码学家 Joan Daemen 和 Vincent Rijmen 设计的分组密码算法。</p><p>Rijndael 和 AES 并不是一点区别都没有,Rijndael 的分组长度和密钥长度可以分别以 32 比特为单位在 128 比特到 256 比特的范围内进行选择。不过在 AES 的规格中,分组长度固定为 128 比特,密钥长度只有 128、192 和 256 比特三种。</p><p>Rijndael 并不像 DES 使用 Feistel 网络作为基本结构,而是使用了 <strong>SPN结构</strong>。和 DES 一样的是 Rijndael 算法也是由多个轮构成的,其中每一轮分为 SubBytes、ShiftRows、MixColumns 和 AddRoundKey 共 4 个步骤。</p><p>Rijndael 的输入分组为 128 比特,也就是 16 字节,一次进行四个步骤的操作:</p><ul><li><strong>SubBytes</strong>:逐个字节地对 16 字节的输入数据进行处理,通过一个非线性的替换函数,用查找表的方式把每个字节替换成对应的字节。</li><li><strong>ShiftRows</strong>:将以 4 字节为单位的行按照一定的规则向左平移,且每一行平移的字节数是不同的。</li><li><strong>MixColumns</strong>:对一个 4 字节的值进行比特运算,将其变为另外一个 4 字节值。</li><li><strong>AddRoundKey</strong>:将 MixColumns 的输出与轮密钥进行 XOR。</li></ul><p>实际上,在 Rijndael 中需要重复进行 10-14 轮计算。</p><p><img src="https://photo.hushhw.cn/images/wKiom1hUEnfhQmtBAAGbstlZhT8469.png" alt></p><p>通过上面的结构我们可以发现输入的所有比特在议论中都会被加密。和每一轮都只加密一半输入的比特的 Feistel 网络相比,这种方式的优势在于加密所需要的轮数更少。此外,这种方式还有一个优势,即每轮步骤中可以分别以字节、行和列为单位进行并行计算。</p><p>下图为解密的过程,除了 AddRoundKey 是一样的,其余三个步骤都是逆运算。</p><p><img src="https://photo.hushhw.cn/images/wKioL1hUEqjAOdloAAGfTs-rFkY687.png" alt></p><h3 id="3-5-该选择哪种对称密码"><a href="#3-5-该选择哪种对称密码" class="headerlink" title="3.5 该选择哪种对称密码"></a>3.5 该选择哪种对称密码</h3><p>今后最好不要将 DES 用于新的较高的安全用途,因为随着计算机技术的进步,现在用暴力破解法已经能够在现实的时间内完成对 DES的 破译。但是,在某些情况下也需要保持与旧版本软件的兼容性,出于兼容性的因素三重 DES 在今后还会使用一段时间,但会逐步被 AES 所取代。今后大家应该使用的算法是 AES(Rijndael),因为它安全、快速,而且能够在各种平台上工作。</p><p> </p><h2 id="4-分组密码的模式"><a href="#4-分组密码的模式" class="headerlink" title="4. 分组密码的模式"></a>4. 分组密码的模式</h2><p>前面介绍的 DES 和 AES 都属于分组密码,分组密码算法只能加密固定长度的分组,但是我们需要加密的明文长度可能会超过分组密码的分组长度,这时就需要对分组密码算法进行迭代,以便将一段很长的明文全部加密。而迭代的方法就称为分组密码的<strong>模式</strong>。</p><ul><li><p>分组密码的主要模式有:</p></li><li><p>ECB 模式:Electronic CodeBook mode(电子密码本模式)</p></li><li>CBC 模式:Cipher Block Chaining mode(密码分组链接模式)</li><li>CFB 模式:Cipher FeedBack mode(密码反馈模式)</li><li>OFB 模式:Output FeedBack mode(输出反馈模式)</li><li>CTR 模式:CounTeR mode(计数器模式)</li></ul><h3 id="4-1-分组密码与流密码"><a href="#4-1-分组密码与流密码" class="headerlink" title="4.1 分组密码与流密码"></a>4.1 分组密码与流密码</h3><p>密码算法可以分为分组密码和流密码两种。</p><p><strong>分组密码</strong>(block cipher)是每次只能处理特定长度的一块数据的一类密码算法,这里的「一块」就称为<strong>分组</strong>(block)。此外,一个分组的比特数就称为<strong>分组长度</strong>(block length)。例如 DES 和 3DES 分组长度都是 64 比特,AES 的分组长度为 128 比特。</p><p><strong>流密码</strong>(stream cipher)是对数据流进行连续处理的一类密码算法。流密码中一般以 1 比特、8 比特或 32 比特等为单位进行加密和解密。</p><p>分组密码处理完一个分组就结束了,因此不需要通过内部状态来记录加密的进度;相对地,流密码是对一串数据流进行连续处理,因此需要保持内部状态。前面提到的一次性密码本属于流密码,而 DES、3DES、AES(Rijndael)等大多数对称密码算法都属于分组密码。</p><h3 id="4-2-明文分组与密文分组"><a href="#4-2-明文分组与密文分组" class="headerlink" title="4.2 明文分组与密文分组"></a>4.2 明文分组与密文分组</h3><p><strong>明文分组</strong>是指分组密码算法中作为加密对象的明文。明文分组的长度与分组密码算法的分组长度是相等的。</p><p><strong>密文分组</strong>是指使用分组密码算法将明文分组加密之后所生成的密文。</p><h3 id="4-3-ECB-模式"><a href="#4-3-ECB-模式" class="headerlink" title="4.3 ECB 模式"></a>4.3 ECB 模式</h3><p><strong>ECB 模式</strong>的全称是 Electronic CodeBook 模式。在 ECB 模式中,将明文分组加密之后的结果将直接成为密文分组。</p><p><img src="https://photo.hushhw.cn/images/wKiom1hWoxLzFmWBAAE0Ephoto09FU260.png" alt></p><p><img src="https://photo.hushhw.cn/images/wKiom1hWoxKAoMorAAGBgBrThDY944.png" alt></p><p>使用 ECB 模式加密时,相同的分组会被转换为相同的密文分组,也就是说,我们可以将其理解为是一个巨大的「明文分组-密文分组」的对应表,因此ECB模式也称为<strong>电子密码本模式</strong>。</p><p>ECB 模式是所有模式中最简单的一种,其明文分组和密文分组是一一对应的关系,因此,如果明文中存在多个相同的明文分组,则这些明文分组最终都会被转换为相同的密文分组。这样一来,只要观察一下密文,就可以知道明文中存在怎样的重复组合,并可以以此为线索来破译密码,因此 ECB 模式存在一定的风险。</p><p>由于 ECB 模式中每个明文分组都各自独立地进行加密和解密,攻击者只需要改变密文分组的顺序,就能操控明文。由于密文分组的顺序被改变了,因此响应的明文分组的顺序也会被改变。也就是说,<strong>攻击者无需破译密码就能够操作明文</strong>。</p><h3 id="4-4-CBC-模式"><a href="#4-4-CBC-模式" class="headerlink" title="4.4 CBC 模式"></a>4.4 CBC 模式</h3><p><strong>CBC 模式</strong>的全称是 Cipher Block Chaining 模式(密文分组链接模式)。在 CBC 模式中,首先将明文分组与前一个密文分组进行 XOR 运算,然后再进行加密。</p><p><img src="https://photo.hushhw.cn/images/wKioL1hWtIrS8N80AAFwPPExW0E664.png" alt></p><p><strong>初始化向量 IV</strong> 为事先准备的一个长度为一个分组的比特序列,用来代替不存在的「前一个密文分组」。每次加密都会随机产生一个不同的比特序列来作为初始化向量。</p><p>对比 CBC 和ECB 模式发现,ECB 模式只进行了加密,而 CBC 模式则在加密之前进行了一次 XOR。因此,即便明文分组 1 和明文分组 2 的值相等,密文分组 1 和 2 也不一定相等,这样 CBC 模式就解决了 ECB 模式存在的缺陷了。</p><p>在 CBC 模式加密过程中,我们无法单独对一个中间的明文分组进行加密。例如,如果要生成密文分组 3 ,就至少需要凑齐明文分组 1、2、3 才行。</p><p>在 CBC 模式解密过程中,如果某一个密文分组因硬盘故障等原因损坏了,在这种情况下,只要密文分组的长度没有发生变化,则解密时最多只会有 2 个分组收到数据损坏影响;如果是密文分组中有一些比特因通信错误导致没有收到某些比特等原因缺失,哪怕只是缺失 1 比特,导致了密文分组的长度发生变化,缺失比特的位置之后的密文分组也就全部无法解密了。</p><p>通过修改密文来操纵解密后的明文,攻击者可以对初始化向量中的任意比特进行反转,则明文分组中的相应比特也会反转;攻击者如果对密文分组进行同样的攻击就非常困难,如修改了密文分组 1 中的某个比特,则明文分组 2 相应比特会反转,而这 1 比特的变化会对解密后的明文分组 1 中的多个比特造成影响,也就是说,只让明文分组 1 中特定比特发生变化是很困难的。</p><h3 id="4-5-CFB-模式"><a href="#4-5-CFB-模式" class="headerlink" title="4.5 CFB 模式"></a>4.5 CFB 模式</h3><p><strong>CFB 模式</strong>的全称是 Cipher FeedBack 模式(密文反馈模式)。在 CFB 模式中,前一个密文分组会被送回到密码算法的输入端。所谓反馈,这里指的是返回输入端的意思。</p><p><img src="https://photo.hushhw.cn/images/wKiom1hX40zh5Y4OAADND_4IQ9c584.png" alt></p><p><img src="https://photo.hushhw.cn/images/wKioL1hX403CntLOAAD4l1Gk2iw770.png" alt></p><p>在 ECB 和 CBC 模式中,明文分组都是通过密码算法进行加密的,然而,在 CFB 模式中,明文分组并没有通过密码来进行直接加密。</p><p><img src="https://photo.hushhw.cn/images/wKioL1hX5YmAzmH1AACUL8pDza0478.png" alt></p><p>CFB 模式与一次性密码本(流密码)非常相似,一次性密码本是通过将明文与随机比特序列进行 XOR 运算来生成密文,而 CFB 模式则通过将明文分组与密码算法的输出进行 XOR 运算来生成密文分组。在 CFB 模式中,密码算法的输出相当于一次性密码本中的随机比特序列。由于密码算法的输出是通过计算得到的,并不是真正的随机数,因此 CFB 模式并不具备理论上不可破译的性质。</p><p>在 CFB模式中,明文数据可以被逐比特加密,因此我们可以将 CFB 模式看作是一种使用分组密码来实现流密码的方式。</p><p>对 CFB 模式可以实施<strong>重放攻击</strong>。</p><h3 id="4-6-OFB-模式"><a href="#4-6-OFB-模式" class="headerlink" title="4.6 OFB 模式"></a>4.6 OFB 模式</h3><p><strong>OFB 模式</strong>的全称是 Output-Feedback 模式(输出反馈模式)。在 OFB 模式中,密码算法的输出会反馈到密码算法的输入中。</p><p><img src="https://photo.hushhw.cn/images/wKioL1hX5pjBv-FQAAEV8EaziXw327.png" alt></p><p>OFB 模式和 CFB 模式的区别仅仅在于密码算法的输入。CFB 模式中密码算法的输入是前一个密文分组,也就是将密文分组反馈到密码算法中,相反地,在 OFB 模式中密码算法的输入则是密码算法的前一个输出,也就是将输出反馈到密码算法中。</p><p>由于 CFB 模式中要对密文分组进行反馈,因此必须从第一个明文分组开始按顺序进行加密,相反地,在 OFB 模式中,XOR 所需要的比特序列(密钥流)可以事先通过密码算法生成,和明文分组无关,只要提前准备好所需的密钥流,则在实际从明文生成密文的过程中,就完全不需要动用密码算法了,只要将明文与密钥流进行 XOR 即可。生成密钥流的操作和进行 XOR 运算的操作是可以并行的,可以快速完成加密。</p><h3 id="4-7-CTR-模式"><a href="#4-7-CTR-模式" class="headerlink" title="4.7 CTR 模式"></a>4.7 CTR 模式</h3><p><strong>CTR 模式</strong>全称是 CounTeR 模式(计数器模式)。CTR 模式是一种通将逐次累加的计数器进行加密来生成密钥流的流密码。</p><p>CTR 模式中,每个分组对应一个逐次累加的计数器,并通过对计数器进行加密来生成密钥流。也就是说,最终的密文是通过将计数器加密得到的比特序列,与明文分组进行 XOR 而得到的。</p><p><img src="https://photo.hushhw.cn/images/wKiom1hX53Hwe-G8AADtc_bAjrE686.png" alt></p><p><img src="https://photo.hushhw.cn/images/wKioL1hX53GzfsX1AAEeB2NJuuk558.png" alt></p><p>CTR 模式和 OFB 模式一样,都属于流密码。加密和解密使用了完全相同的结构,因此在程序实现上比较容易。CTR 模式中可以任意顺序对分组进行加密和解密,因此在加密和解密时需要用到的「计数器」的值可以直接计算出来,这一性质是 OFB 模式所不具备的。能够以任意顺序处理分组,就意味着能够实现并行计算,在支持并行计算的系统中,CTR 模式的速度是非常快的。</p><h3 id="4-8-该选者哪种模式"><a href="#4-8-该选者哪种模式" class="headerlink" title="4.8 该选者哪种模式"></a>4.8 该选者哪种模式</h3><p><img src="https://photo.hushhw.cn/images/wKioL1hX58zS9WOIAAHYrp0dqCI726.png" alt></p><p><img src="https://photo.hushhw.cn/images/wKiom1hX582S9fKIAAIT3re_2vo030.png" alt></p><p> </p>]]></content>
<summary type="html">
<blockquote>
<p>本文整理「密码」这一部分的基础知识点。</p>
<p>内容概述:</p>
<ul>
<li>经典密码</li>
<li>对称密码</li>
<li>分组密码</li>
</ul>
</blockquote>
</summary>
<category term="笔记整理" scheme="https://wiki.hushhw.cn/categories/%E7%AC%94%E8%AE%B0%E6%95%B4%E7%90%86/"/>
<category term="密码学" scheme="https://wiki.hushhw.cn/categories/%E7%AC%94%E8%AE%B0%E6%95%B4%E7%90%86/%E5%AF%86%E7%A0%81%E5%AD%A6/"/>
<category term="密码学" scheme="https://wiki.hushhw.cn/tags/%E5%AF%86%E7%A0%81%E5%AD%A6/"/>
<category term="学习笔记" scheme="https://wiki.hushhw.cn/tags/%E5%AD%A6%E4%B9%A0%E7%AC%94%E8%AE%B0/"/>
</entry>
<entry>
<title>《图解密码技术》学习笔记目录</title>
<link href="https://wiki.hushhw.cn/posts/83650eb4.html"/>
<id>https://wiki.hushhw.cn/posts/83650eb4.html</id>
<published>2019-04-21T10:31:20.000Z</published>
<updated>2021-02-03T06:56:56.836Z</updated>
<content type="html"><![CDATA[<blockquote><p>入门学习「密码学」,在图书馆找了一圈,锁定了这本《图解密码技术》,非常适合入门来捋一遍密码技术中的各种名词,大体上有个概念。</p><p>本文用于学习过程中梳理脉络,整理后面相关学习笔记链接。</p></blockquote><p>『<a href="https://wiki.hushhw.cn/posts/418f8d38.html">《图解密码技术》学习笔记之密码</a>』</p><h2 id="脉络"><a href="#脉络" class="headerlink" title="脉络"></a>脉络</h2><ul><li>密码<ul><li>经典密码</li><li>对称密码</li><li>分组密码</li><li>公钥密码</li><li>混合密码系统</li></ul></li><li>认证<ul><li>单向散列函数</li><li>消息认证码</li><li>数字签名</li><li>证书</li></ul></li><li>密钥、随机数与应用技术<ul><li>密钥</li><li>随机数</li><li>PGP</li><li>SSL/TLS</li><li>密码技术与现实社会</li></ul></li></ul><blockquote><p>内容整理自:</p><p>[日]结城浩(著)、周自恒(译)<strong>《图解密码技术》</strong>(第三版)</p></blockquote>]]></content>
<summary type="html">
入门学习「密码学」,在图书馆找了一圈,锁定了这本《图解密码技术》,非常适合入门来捋一遍密码技术中的各种名词,大体上有个概念。
</summary>
<category term="笔记整理" scheme="https://wiki.hushhw.cn/categories/%E7%AC%94%E8%AE%B0%E6%95%B4%E7%90%86/"/>
<category term="密码学" scheme="https://wiki.hushhw.cn/categories/%E7%AC%94%E8%AE%B0%E6%95%B4%E7%90%86/%E5%AF%86%E7%A0%81%E5%AD%A6/"/>
<category term="密码学" scheme="https://wiki.hushhw.cn/tags/%E5%AF%86%E7%A0%81%E5%AD%A6/"/>
<category term="学习笔记" scheme="https://wiki.hushhw.cn/tags/%E5%AD%A6%E4%B9%A0%E7%AC%94%E8%AE%B0/"/>
</entry>
<entry>
<title>Three.js 入坑笔记</title>
<link href="https://wiki.hushhw.cn/posts/ae8c05d6.html"/>
<id>https://wiki.hushhw.cn/posts/ae8c05d6.html</id>
<published>2019-04-03T15:09:57.000Z</published>
<updated>2021-02-03T06:56:56.836Z</updated>
<content type="html"><![CDATA[<blockquote><p>关于一些函数的参数用一次忘一次,参考系这些东西每次都到用的时候想半天,所以还是写一篇笔记来记录,方便查阅,不至于每次都去 Google 浪费时间。</p></blockquote><h2 id="1-坐标系"><a href="#1-坐标系" class="headerlink" title="1. 坐标系"></a>1. 坐标系</h2><p><code>three.js</code> 中使用的是右手坐标系,X 轴水平向右,Y 轴垂直向上,Z 轴的方向就是屏幕由里往外的方向:</p><p><img src="https://photo.hushhw.cn/images/Snipaste_2019-04-03_15-37-08.png" alt></p><h2 id="2-照相机"><a href="#2-照相机" class="headerlink" title="2. 照相机"></a>2. 照相机</h2><h3 id="2-1-什么是照相机?"><a href="#2-1-什么是照相机?" class="headerlink" title="2.1 什么是照相机?"></a>2.1 什么是照相机?</h3><p>我们使用的 <code>Three.js</code> 创建的场景是三维的,而通常情况下显示器是二维的,那么三维的场景怎么在二维的显示器上显示呢?照相机就是一个抽象,它定义了三维空间到二维屏幕投影的方式,用「照相机」这样一个类比,可以使我们直观地理解这一投影方式。</p><p>而针对<code>投影方式</code>的不同,照相机又分为<code>正交投影照相机</code>与<code>透视投影照相机</code>。我们需要为自己的程序选择合适的照相机。</p><h3 id="2-2-正交投影和透视投影"><a href="#2-2-正交投影和透视投影" class="headerlink" title="2.2 正交投影和透视投影"></a>2.2 正交投影和透视投影</h3><p>举个简单的例子来说明正交投影与透视投影照相机的区别。使用<code>透视投影照相</code>机获得的结果是<code>类似人眼在真实世界中看到的有“近大远小”</code>的效果(如下图中的 (a));而使用<code>正交投影照相机</code>获得的结果就像我们在数学几何学课上老师教我们画的效果,对于<code>三维空间内平行的线</code>,投影到<code>二维空间中也一定是平行的</code>(如下图中的 (b))。</p><p><img src="https://photo.hushhw.cn/images/Snipaste_2019-04-03_15-23-54.png" alt="image"></p><p>一般说来,对于<code>制图、建模软</code>通常使<code>正交投影</code>,这样不会因为投影而改变物体比例;而<code>对于其他大多数应用</code>,通常使用<code>透视投影</code>,因为这更接近人眼的观察效果。当然,照相机的选择并没有对错之分,你可以更具应用的特性,选择一个效果更佳的照相机。</p><h3 id="2-3-正交投影照相机"><a href="#2-3-正交投影照相机" class="headerlink" title="2.3 正交投影照相机"></a>2.3 正交投影照相机</h3><h4 id="2-3-1-参数介绍"><a href="#2-3-1-参数介绍" class="headerlink" title="2.3.1 参数介绍"></a>2.3.1 参数介绍</h4><blockquote><p>正交投影照相机(<code>Orthographic Camera</code>)</p></blockquote><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">THREE.OrthographicCamera(left, right, top, bottom, near, far)</span><br></pre></td></tr></table></figure><p>这六个参数分别代表正交投影照相机拍摄到的空间的六个面的位置,这六个面围成一个长方体,我们称其<code>视景体(Frustum)</code>。只有在视景体内部(下图中的灰色部分)的物体才可能显示在屏幕上,而视景体外的物体会在显示之前被裁减掉。</p><p><img src="https://photo.hushhw.cn/images/Snipaste_2019-04-03_15-25-27.png" alt="image"></p><p>为了保持照相机的横竖比例,需要保证<code>(right - left)与(top - bottom)</code>的比例与<code>Canvas宽度与高度的比例(800/600)</code>一致。</p><p><code>near与far</code>都是指到照相机位置在深度平面的位置,而照相机不应该拍摄到其后方的物体,因此这两个值应该均为<code>正值</code>。为了保证场景中的物体不会因为太近或太远而被照相机忽略,一般<code>near的值设置得较小</code>,<code>far的值设置得较大</code>,具体值视场景中物体的位置等决定。</p><h4 id="2-3-2-示例代码"><a href="#2-3-2-示例代码" class="headerlink" title="2.3.2 示例代码"></a>2.3.2 示例代码</h4><p>下面我们通过一个具体的例子来了解正交投影照相机的设置</p><p><strong>基本设置</strong></p><p>设置照相机:</p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> camera = <span class="keyword">new</span> THREE.OrthographicCamera(<span class="number">-2</span>, <span class="number">2</span>, <span class="number">1.5</span>, <span class="number">-1.5</span>, <span class="number">1</span>, <span class="number">10</span>);</span><br><span class="line">camera.poaition.set(<span class="number">0</span>,<span class="number">0</span>,<span class="number">5</span>);</span><br><span class="line">scene.add(camera);</span><br></pre></td></tr></table></figure><p>在原点处创建一个边长为1的正方体,为了和透视效果做对比,这里我们使用<code>wireframe</code>而不是实心的材质,以便看到正方体后方的边:</p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> cube = <span class="keyword">new</span> THREE.Mesh(<span class="keyword">new</span> THREE.CubeGeometry(<span class="number">1</span>, <span class="number">1</span>, <span class="number">1</span>), </span><br><span class="line"> <span class="keyword">new</span> THREE.MeshBasicMaterial({</span><br><span class="line"> color: <span class="number">0xff0000</span>,</span><br><span class="line"> wireframe: <span class="literal">true</span></span><br><span class="line"> })</span><br><span class="line">);</span><br><span class="line">scene.add(cube);</span><br></pre></td></tr></table></figure><ul><li>效果图:</li></ul><p><img src="https://photo.hushhw.cn/images/Snipaste_2019-04-03_15-29-55.png" alt></p><p>我们看到正交投影的结果是一个正方形,后面的边与前面完全重合了,这也就是正交投影与透视投影的区别所在。</p><p><strong>长宽比例</strong></p><p>这里,我们的Canvas宽度是 800px,高度是 600px,照相机水平方向距离 4,垂直方向距离 3,因此长宽比例保持不变。为了试验长宽比例变化时的效果,我们将照相机水平方向的距离减小为 2 (right-left = 2):</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">var camera = new THREE.OrthographicCamera(-1, 1, 1.5, -1.5, 1, 10);</span><br></pre></td></tr></table></figure><p>效果图(此时水平方向的距离就被拉长了):</p><p><img src="https://photo.hushhw.cn/images/Snipaste_2019-04-03_15-31-42.png" alt></p><h3 id="2-4-透视投影照相机"><a href="#2-4-透视投影照相机" class="headerlink" title="2.4 透视投影照相机"></a>2.4 透视投影照相机</h3><h4 id="2-4-1-参数介绍"><a href="#2-4-1-参数介绍" class="headerlink" title="2.4.1 参数介绍"></a>2.4.1 参数介绍</h4><blockquote><p>透视投影照相机(<code>Perspective Camera</code>)</p></blockquote><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">THREE.PerspectiveCamera(fov, aspect, near, far)</span><br></pre></td></tr></table></figure><p>让我们通过一张透视照相机投影的图来了解这些参数。</p><p><img src="https://photo.hushhw.cn/images/Snipaste_2019-04-03_15-39-39.png" alt></p><ul><li><p>透视图中,灰色的部分是<code>视景体</code>,是可能被渲染的物体所在的区域。<code>fov</code>是视景体竖直方向上的<code>张角</code>(是角度制而非弧度制),如侧视图所示。</p></li><li><p><code>aspect</code>等于<code>width / height</code>,是照相机水平方向和竖直方向长度的比值,通常设为Canvas的<code>横纵比例</code>。</p></li><li><p><code>near</code>和<code>far</code>分别是照相机到视景体 最近、最远的距离,均为正值,且far应大于near。</p></li></ul><h4 id="2-4-2-示例代码"><a href="#2-4-2-示例代码" class="headerlink" title="2.4.2 示例代码"></a>2.4.2 示例代码</h4><p>下面我们通过一个例子来学习透视投影照相机</p><p><strong>基本设置</strong></p><p>设置透视投影照相机,这里Canvas长<code>800px</code>,宽<code>600px</code>,所以<code>aspect</code>设为<code>800 / 600</code>:</p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> camera = <span class="keyword">new</span> THREE.PerspectiveCamera(<span class="number">45</span>, <span class="number">800</span> / <span class="number">600</span>, <span class="number">1</span>, <span class="number">10</span>);</span><br><span class="line">camera.position.set(<span class="number">0</span>, <span class="number">0</span>, <span class="number">5</span>);</span><br><span class="line">scene.add(camera);</span><br></pre></td></tr></table></figure><p>设置一个在原点处的边长为1的正方体:</p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> cube = <span class="keyword">new</span> THREE.Mesh(<span class="keyword">new</span> THREE.CubeGeometry(<span class="number">1</span>, <span class="number">1</span>, <span class="number">1</span>),</span><br><span class="line"> <span class="keyword">new</span> THREE.MeshBasicMaterial({</span><br><span class="line"> color: <span class="number">0xff0000</span>,</span><br><span class="line"> wireframe: <span class="literal">true</span></span><br><span class="line"> })</span><br><span class="line">);</span><br><span class="line">scene.add(cube);</span><br></pre></td></tr></table></figure><p>效果图:</p><p><img src="https://photo.hushhw.cn/images/Snipaste_2019-04-03_15-41-29.png" alt></p><p>对比正交透视照相机下正方形的效果,透视投影可以看到<code>全部的12条边</code>,而且有<code>近大远小</code>的效果,这也就是与正交投影的区别。</p><p><strong>竖直张角</strong></p><ul><li>接下来,我们来看下<code>fov</code>的改变对渲染效果的影响。我们将原来的<code>45改为60</code>:</li></ul><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> camera = <span class="keyword">new</span> THREE.PerspectiveCamera(<span class="number">60</span>, <span class="number">800</span> / <span class="number">600</span>, <span class="number">1</span>, <span class="number">10</span>);</span><br><span class="line">camera.position.set(<span class="number">0</span>, <span class="number">0</span>, <span class="number">5</span>);</span><br><span class="line">scene.add(camera);</span><br></pre></td></tr></table></figure><ul><li>效果图:</li></ul><p><img src="https://photo.hushhw.cn/images/Snipaste_2019-04-03_15-42-39.png" alt></p><p>为什么正方体显得更小了呢?我们从下面的侧视图来看,虽然正方体的实际大小并未改变,但是将照相机的<code>竖直张角</code>设置得<code>更大</code>时,<code>视景体变大了</code>,因而<code>正方体</code>相对于<code>整个视景体</code>的大小就<code>变小</code>了,看起来正方形就显得变小了。</p><p><img src="https://photo.hushhw.cn/images/Snipaste_2019-04-03_15-43-15.png" alt></p><p>注意,<code>改变fov</code>并<code>不会</code>引<code>起画面横竖比例</code>的变化,而<code>改变aspect</code>则<code>会</code>改变横竖比例。</p><h2 id="参考文档整理"><a href="#参考文档整理" class="headerlink" title="参考文档整理"></a>参考文档整理</h2><ul><li><p><a href="https://techbrood.com/threejs/docs/" target="_blank" rel="noopener">TechbrooD 对 Three.js 在线文档的汉化</a> </p></li><li><p><a href="http://test.domojyun.net/MEMO/3D/threejs.html" target="_blank" rel="noopener">3D 網站開發入門筆記_Three.js 入門</a> </p></li></ul><blockquote><p>本文整理自</p><p>开源书籍:《Three.js 入门指南》</p></blockquote>]]></content>
<summary type="html">
关于一些函数的参数用一次忘一次,参考系这些东西每次都到用的时候想半天,所以还是写一篇笔记来记录,方便查阅,不至于每次都去 Google 浪费时间。
</summary>
<category term="笔记整理" scheme="https://wiki.hushhw.cn/categories/%E7%AC%94%E8%AE%B0%E6%95%B4%E7%90%86/"/>
<category term="WebGL" scheme="https://wiki.hushhw.cn/categories/%E7%AC%94%E8%AE%B0%E6%95%B4%E7%90%86/WebGL/"/>
<category term="ThreeJS" scheme="https://wiki.hushhw.cn/tags/ThreeJS/"/>
</entry>
<entry>
<title>二叉树遍历题型汇总</title>
<link href="https://wiki.hushhw.cn/posts/4884afb4.html"/>
<id>https://wiki.hushhw.cn/posts/4884afb4.html</id>
<published>2019-03-28T18:24:35.000Z</published>
<updated>2021-02-03T06:56:56.836Z</updated>
<content type="html"><![CDATA[<blockquote><p>总结二叉树遍历相关题目,对这些基础题型总结经验。</p></blockquote><p> </p><h3 id="1020-Tree-Traversals"><a href="#1020-Tree-Traversals" class="headerlink" title="1020 Tree Traversals"></a>1020 Tree Traversals</h3><p>题目链接:<a href="https://pintia.cn/problem-sets/994805342720868352/problems/994805485033603072" target="_blank" rel="noopener">1020 Tree Traversals (25 分)</a></p><p>给定一棵二叉树的后序遍历和中序遍历,请你输出其层序遍历的序列。这里假设键值都是互不相等的正整数。</p><blockquote><p>Sample Input:<br>7<br>2 3 1 5 7 6 4<br>1 2 3 4 5 6 7<br>Sample Output:<br>4 1 6 3 5 7 2</p></blockquote><figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string"><bits/stdc++.h></span></span></span><br><span class="line"><span class="keyword">using</span> <span class="keyword">namespace</span> <span class="built_in">std</span>;</span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">struct</span> <span class="title">node</span> {</span></span><br><span class="line"> <span class="keyword">int</span> index, value;</span><br><span class="line">};</span><br><span class="line"><span class="function"><span class="keyword">bool</span> <span class="title">cmp</span><span class="params">(node a, node b)</span> </span>{</span><br><span class="line"> <span class="keyword">return</span> a.index < b.index;</span><br><span class="line">}</span><br><span class="line"><span class="built_in">vector</span><<span class="keyword">int</span>> post, in;</span><br><span class="line"><span class="built_in">vector</span><node> ans;</span><br><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">pre</span><span class="params">(<span class="keyword">int</span> root, <span class="keyword">int</span> start, <span class="keyword">int</span> end, <span class="keyword">int</span> index)</span> </span>{</span><br><span class="line"> <span class="keyword">if</span> (start > end) <span class="keyword">return</span>;</span><br><span class="line"> <span class="keyword">int</span> i = start;</span><br><span class="line"> <span class="keyword">while</span> (i < end && in[i] != post[root]) i++;</span><br><span class="line"> ans.push_back({index, post[root]});</span><br><span class="line"> pre(root - <span class="number">1</span> - end + i, start, i - <span class="number">1</span>, <span class="number">2</span> * index + <span class="number">1</span>);</span><br><span class="line"> pre(root - <span class="number">1</span>, i + <span class="number">1</span>, end, <span class="number">2</span> * index + <span class="number">2</span>);</span><br><span class="line">}</span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">main</span><span class="params">()</span> </span>{</span><br><span class="line"> <span class="keyword">int</span> n;</span><br><span class="line"> <span class="built_in">scanf</span>(<span class="string">"%d"</span>, &n);</span><br><span class="line"> post.resize(n);</span><br><span class="line"> in.resize(n);</span><br><span class="line"> <span class="keyword">for</span> (<span class="keyword">int</span> i = <span class="number">0</span>; i < n; i++) <span class="built_in">scanf</span>(<span class="string">"%d"</span>, &post[i]);</span><br><span class="line"> <span class="keyword">for</span> (<span class="keyword">int</span> i = <span class="number">0</span>; i < n; i++) <span class="built_in">scanf</span>(<span class="string">"%d"</span>, &in[i]);</span><br><span class="line"> pre(n - <span class="number">1</span>, <span class="number">0</span>, n - <span class="number">1</span>, <span class="number">0</span>);</span><br><span class="line"> sort(ans.begin(), ans.end(), cmp);</span><br><span class="line"> <span class="keyword">for</span> (<span class="keyword">int</span> i = <span class="number">0</span>; i < ans.size(); i++) {</span><br><span class="line"> <span class="keyword">if</span> (i != <span class="number">0</span>) <span class="built_in">cout</span> << <span class="string">" "</span>;</span><br><span class="line"> <span class="built_in">cout</span> << ans[i].value;</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p> </p><h3 id="1138-Postorder-Traversal"><a href="#1138-Postorder-Traversal" class="headerlink" title="1138 Postorder Traversal"></a>1138 Postorder Traversal</h3><p>题目链接:<a href="https://pintia.cn/problem-sets/994805342720868352/problems/994805345078067200" target="_blank" rel="noopener">1138 Postorder Traversal</a></p><p>给定先序和中序,求后序第一个数字</p><blockquote><p>Sample Input:<br>7<br>1 2 3 4 5 6 7<br>2 3 1 5 4 7 6<br>Sample Output:<br>3</p></blockquote><figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string"><bits/stdc++.h></span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">define</span> For(i, m, n) for (int i = m; i < n; i++)</span></span><br><span class="line"><span class="keyword">using</span> <span class="keyword">namespace</span> <span class="built_in">std</span>;</span><br><span class="line"></span><br><span class="line"><span class="built_in">vector</span><<span class="keyword">int</span>> pre, in;</span><br><span class="line"><span class="keyword">bool</span> flag;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">post</span><span class="params">(<span class="keyword">int</span> root, <span class="keyword">int</span> start, <span class="keyword">int</span> end)</span></span>{</span><br><span class="line"> <span class="keyword">if</span>(start > end || flag) <span class="keyword">return</span>;</span><br><span class="line"> <span class="keyword">int</span> i = start;</span><br><span class="line"> <span class="keyword">while</span>(in[i]!=pre[root]) i++;</span><br><span class="line"> post(root+<span class="number">1</span>, start, i - <span class="number">1</span>);</span><br><span class="line"> post(root+i-start+<span class="number">1</span>, i + <span class="number">1</span>, end);</span><br><span class="line"> <span class="comment">//printf("%d ", in[i]);</span></span><br><span class="line"> <span class="keyword">if</span>(!flag){</span><br><span class="line"> <span class="built_in">printf</span>(<span class="string">"%d"</span>, in[i]);</span><br><span class="line"> flag = <span class="literal">true</span>;</span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">main</span><span class="params">()</span></span>{</span><br><span class="line"> <span class="keyword">int</span> n;</span><br><span class="line"> <span class="keyword">while</span>(<span class="built_in">scanf</span>(<span class="string">"%d"</span>,&n)!=EOF){</span><br><span class="line"> flag = <span class="literal">false</span>; <span class="comment">//用于判断是否已经找到第一个数字</span></span><br><span class="line"> pre.resize(n);</span><br><span class="line"> in.resize(n);</span><br><span class="line"> For(i, <span class="number">0</span>, n) <span class="built_in">scanf</span>(<span class="string">"%d"</span>, &pre[i]);</span><br><span class="line"> For(i, <span class="number">0</span>, n) <span class="built_in">scanf</span>(<span class="string">"%d"</span>, &in[i]);</span><br><span class="line"> post(<span class="number">0</span>, <span class="number">0</span>, n - <span class="number">1</span>);</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p> </p><h3 id="1119-Pre-and-Post-order-Traversals"><a href="#1119-Pre-and-Post-order-Traversals" class="headerlink" title="1119 Pre- and Post-order Traversals"></a>1119 Pre- and Post-order Traversals</h3><p>题目链接:<a href="https://pintia.cn/problem-sets/994805342720868352/problems/994805353470869504" target="_blank" rel="noopener">1119 Pre- and Post-order Traversals (30 分)</a> </p><p>给出一棵树的结点个数 n,以及它的前序遍历和后序遍历,输出它的中序遍历,如果中序遍历不唯一就输出No,且输出其中一个中序即可,如果中序遍历唯一就输出Yes,并输出它的中序。</p><blockquote><p>Sample Input 1:<br>7<br>1 2 3 4 6 7 5<br>2 6 7 4 5 3 1<br>Sample Output 1:<br>Yes<br>2 1 6 4 7 3 5<br>Sample Input 2:<br>4<br>1 2 3 4<br>2 4 3 1<br>Sample Output 2:<br>No<br>2 1 3 4</p></blockquote><figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string"><bits/stdc++.h></span></span></span><br><span class="line"><span class="keyword">using</span> <span class="keyword">namespace</span> <span class="built_in">std</span>;</span><br><span class="line"></span><br><span class="line"><span class="built_in">vector</span><<span class="keyword">int</span>> in, pre, post;</span><br><span class="line"><span class="keyword">bool</span> uniq = <span class="literal">true</span>;</span><br><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">getIn</span><span class="params">(<span class="keyword">int</span> preLeft, <span class="keyword">int</span> preRight, <span class="keyword">int</span> postLeft, <span class="keyword">int</span> postRight)</span> </span>{</span><br><span class="line"> <span class="keyword">if</span>(preLeft == preRight) {</span><br><span class="line"> in.push_back(pre[preLeft]);</span><br><span class="line"> <span class="keyword">return</span>;</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">if</span> (pre[preLeft] == post[postRight]) {</span><br><span class="line"> <span class="keyword">int</span> i = preLeft + <span class="number">1</span>;</span><br><span class="line"> <span class="keyword">while</span> (i <= preRight && pre[i] != post[postRight<span class="number">-1</span>]) i++;</span><br><span class="line"> <span class="keyword">if</span> (i - preLeft > <span class="number">1</span>)</span><br><span class="line"> getIn(preLeft + <span class="number">1</span>, i - <span class="number">1</span>, postLeft, postLeft + (i - preLeft - <span class="number">1</span>) - <span class="number">1</span>);</span><br><span class="line"> <span class="keyword">else</span></span><br><span class="line"> uniq = <span class="literal">false</span>;</span><br><span class="line"> in.push_back(post[postRight]);</span><br><span class="line"> getIn(i, preRight, postLeft + (i - preLeft - <span class="number">1</span>), postRight - <span class="number">1</span>);</span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">main</span><span class="params">()</span> </span>{</span><br><span class="line"> <span class="keyword">int</span> n;</span><br><span class="line"> <span class="built_in">scanf</span>(<span class="string">"%d"</span>, &n);</span><br><span class="line"> pre.resize(n), post.resize(n);</span><br><span class="line"> <span class="keyword">for</span> (<span class="keyword">int</span> i = <span class="number">0</span>; i < n; i++) <span class="built_in">scanf</span>(<span class="string">"%d"</span>, &pre[i]);</span><br><span class="line"> <span class="keyword">for</span> (<span class="keyword">int</span> i = <span class="number">0</span>; i < n; i++) <span class="built_in">scanf</span>(<span class="string">"%d"</span>, &post[i]);</span><br><span class="line"> getIn(<span class="number">0</span>, n<span class="number">-1</span>, <span class="number">0</span>, n<span class="number">-1</span>);</span><br><span class="line"> <span class="built_in">printf</span>(<span class="string">"%s\n%d"</span>, uniq == <span class="literal">true</span> ? <span class="string">"Yes"</span> : <span class="string">"No"</span>, in[<span class="number">0</span>]);</span><br><span class="line"> <span class="keyword">for</span> (<span class="keyword">int</span> i = <span class="number">1</span>; i < in.size(); i++)</span><br><span class="line"> <span class="built_in">printf</span>(<span class="string">" %d"</span>, in[i]);</span><br><span class="line"> <span class="built_in">printf</span>(<span class="string">"\n"</span>);</span><br><span class="line"> <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">}</span><br></pre></td></tr></table></figure>]]></content>
<summary type="html">
总结二叉树遍历相关题目,对这些基础题型总结经验。
</summary>
<category term="编程开发" scheme="https://wiki.hushhw.cn/categories/%E7%BC%96%E7%A8%8B%E5%BC%80%E5%8F%91/"/>
<category term="OJ刷题" scheme="https://wiki.hushhw.cn/categories/%E7%BC%96%E7%A8%8B%E5%BC%80%E5%8F%91/OJ%E5%88%B7%E9%A2%98/"/>
<category term="OJ" scheme="https://wiki.hushhw.cn/tags/OJ/"/>
</entry>
</feed>