-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathsearch.xml
287 lines (136 loc) · 421 KB
/
search.xml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
<?xml version="1.0" encoding="utf-8"?>
<search>
<entry>
<title>ViT实现</title>
<link href="/2024/01/13/ViT%E5%AE%9E%E7%8E%B0/"/>
<url>/2024/01/13/ViT%E5%AE%9E%E7%8E%B0/</url>
<content type="html"><</p><p>这一动作是对标Transformer中Word Embedding的,但是是把一张图像分成多个Embedding。Patch to Embedding的过程是可学习的。</p><p>具体理解可以参考<a href="https://blog.csdn.net/lzzzzzzm/article/details/122902777%E8%BF%99%E7%AF%87%E6%96%87%E7%AB%A0%EF%BC%8C%E6%8E%A5%E4%B8%8B%E6%9D%A5%E5%8F%AA%E8%AF%B4%E4%BB%A3%E7%A0%81%E5%AE%9E%E7%8E%B0%E3%80%82">https://blog.csdn.net/lzzzzzzm/article/details/122902777这篇文章,接下来只说代码实现。</a></p><p>在Patch to Embedding中,一般会分为两种实现方法。</p><p><strong>其一</strong>,分块+权重矩阵得到Embedding的结果</p><p>具体思路如下:</p><figure class="highlight python"><table><tr><td class="code"><pre><span class="line"><span class="comment"># 每个patch会被映射为一个embedding</span></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">image2emb_naive</span>(<span class="params">image, patch_size, weight</span>):</span><br><span class="line"> <span class="comment"># image shape : [batch_size,channels,height,width]</span></span><br><span class="line"> patch = F.unfold(image, kernel_size=patch_size, stride=patch_size).transpose(-<span class="number">1</span>, -<span class="number">2</span>)</span><br><span class="line"> patch_embedding = patch @ weight</span><br><span class="line"> <span class="keyword">return</span> patch_embedding</span><br><span class="line"></span><br><span class="line">bs, input_channel, image_h, image_w = <span class="number">1</span>, <span class="number">3</span>, <span class="number">8</span>, <span class="number">8</span></span><br><span class="line">image = torch.randn((bs, input_channel, image_h, image_w))</span><br><span class="line">patch_size = <span class="number">4</span></span><br><span class="line">model_dim = <span class="number">8</span></span><br><span class="line">patch_depth = patch_size * patch_size * input_channel</span><br><span class="line"><span class="comment"># model_dim是输出通道数目,patch_depth是卷积核面积*输入通道数</span></span><br><span class="line">weight = torch.randn(patch_depth, model_dim)</span><br><span class="line"><span class="comment"># 分块方法得到embedding</span></span><br><span class="line">patch_embedding_naive = image2emb_naive(image, patch_size, weight)</span><br></pre></td></tr></table></figure><p>运用unfold进行分块,为什么最后要交换呢?</p><p>单纯unfold得到patch的shape会是<code>(batch_size,patch_size*patch_size*channels,patch_nums)</code>,对图像分块,其实没有了channels的维度,只会有像素数量,交换后可以与权重矩阵做进一步的乘法。</p><p><strong>其二</strong>,卷积方法得到Embedding的结果。目前使用的就是这种</p><p>具体思路如下:</p><figure class="highlight python"><table><tr><td class="code"><pre><span class="line"><span class="keyword">def</span> <span class="title function_">image2emb_conv</span>(<span class="params">image, kernel, stride</span>):</span><br><span class="line"> <span class="comment"># conv_output shape = [batch_size,model_dim,output_height,output_width],最后把output_height和output_width使用flatten拉伸</span></span><br><span class="line"> conv_output = F.conv2d(image, kernel, stride=stride) </span><br><span class="line"> <span class="built_in">print</span>(conv_output.shape) <span class="comment"># shape[1,8,2,2]</span></span><br><span class="line"> bs, oc, oh, ow = conv_output.shape</span><br><span class="line"> patch_embedding = conv_output.reshape((bs, oc, oh * ow)).transpose(-<span class="number">1</span>, -<span class="number">2</span>)</span><br><span class="line"> <span class="keyword">return</span> patch_embedding</span><br><span class="line"></span><br><span class="line">bs, input_channel, image_h, image_w = <span class="number">1</span>, <span class="number">3</span>, <span class="number">8</span>, <span class="number">8</span></span><br><span class="line">image = torch.randn((bs, input_channel, image_h, image_w))</span><br><span class="line">patch_size = <span class="number">4</span></span><br><span class="line">model_dim = <span class="number">8</span></span><br><span class="line">patch_depth = patch_size * patch_size * input_channel</span><br><span class="line"><span class="comment"># model_dim是输出通道数目,patch_depth是卷积核面积*输入通道数</span></span><br><span class="line">weight = torch.randn(patch_depth, model_dim)</span><br><span class="line"></span><br><span class="line"><span class="comment"># kernel shape = [model_dim,input_channel,kernel_height,kernel_width]</span></span><br><span class="line">kernel = weight.transpose(<span class="number">0</span>, <span class="number">1</span>).reshape((-<span class="number">1</span>, input_channel, patch_size, patch_size)) </span><br><span class="line"><span class="comment"># 得到kernel的shape应该是[8,3,4,4],image shape为[1,3,8,8] stride=4</span></span><br><span class="line">patch_embedding_conv = image2emb_conv(image, kernel=kernel, stride=patch_size)</span><br></pre></td></tr></table></figure><p>总结:从Patch to Embedding的过程能够知道,一张图像分块送入ViT中有一个前提条件,即image的长宽必须能除尽patch_size,在ViT的代码中,长和宽可以使用一个元组来说明,这时image的长必须除尽元组中代表的长,而image的宽必须除尽元组中代表的宽;另外,model_dim即输入到ViT中的Embedding的维数,个人理解model_dim的维数越多,能代表一个patch块中的信息量越大;为了更好的任务效果,这个映射过程在代码实现中实际上是可学习的。</p><h1 id="Cls-token-amp-Positional-Encoding"><a href="#Cls-token-amp-Positional-Encoding" class="headerlink" title="Cls token & Positional Encoding"></a>Cls token & Positional Encoding</h1><p>人为地添加一个与Patch Embedding相同维度的Embedding,该Embedding用于最终分类。</p><p>Positional Encoding用于标注patch的位置,具体需要额外了解。</p><p>参考文章:<a href="https://blog.csdn.net/chumingqian/article/details/124660657">https://blog.csdn.net/chumingqian/article/details/124660657</a></p>]]></content>
<tags>
<tag> 科研 </tag>
</tags>
</entry>
<entry>
<title>计网第三章</title>
<link href="/2024/01/13/%E8%AE%A1%E7%BD%91%E7%AC%AC%E4%B8%89%E7%AB%A0/"/>
<url>/2024/01/13/%E8%AE%A1%E7%BD%91%E7%AC%AC%E4%B8%89%E7%AB%A0/</url>
<content type="html"><![CDATA[<h1 id="数据链路层"><a href="#数据链路层" class="headerlink" title="数据链路层"></a>数据链路层</h1><p>数据链路层使用的信道主要有:</p><ul><li>点对点信道,使用PPP协议</li><li>广播信道,使用CSMA/CD协议</li></ul><p>数据链路层研究的是在同一个局域网中,分组怎样从一台主机传送到另一台主机而<strong>不经过路由器转发</strong>。局域网属于数据链路层的范围。</p><p>数据链路层只管点到点运输,不同种类网络的数据链路层采用不同的数据链路层协议。</p><h2 id="3-1-数据链路层的几个共同问题"><a href="#3-1-数据链路层的几个共同问题" class="headerlink" title="3.1 数据链路层的几个共同问题"></a>3.1 数据链路层的几个共同问题</h2><h3 id="3-1-1-数据链路和帧"><a href="#3-1-1-数据链路和帧" class="headerlink" title="3.1.1 数据链路和帧"></a>3.1.1 数据链路和帧</h3><p>链路是一条无源的无源的点到点的物理线路段,中间没有任何其他的交换节点。一条链路只是<strong>一条通路的一个组成部分</strong>。数据链路除了物理线路外,还必须有通信协议来控制这些数据的传输。若把实现这些协议的硬件和软件加到链路上,就构成了数据链路。<strong>即数据链路和链路的区别是数据链路还有协议</strong></p><p>数据链路层把网络层交下来的IP数据报构成帧发送到链路上并且把收到的帧中的数据取出并上交给网络层。——封装成帧</p><h3 id="3-1-2-三个基本问题"><a href="#3-1-2-三个基本问题" class="headerlink" title="3.1.2 三个基本问题"></a>3.1.2 三个基本问题</h3><h4 id="数据链路层的主要功能"><a href="#数据链路层的主要功能" class="headerlink" title="数据链路层的主要功能"></a>数据链路层的主要功能</h4><ul><li>链路管理</li><li><strong>帧定界</strong></li><li>流量控制</li><li><strong>差错控制</strong></li><li>将数据和控制信息区分开</li><li><strong>透明传输</strong></li><li>寻址</li></ul><p>其中任何数据链路层都要解决的<strong>三个问题</strong>:</p><ul><li><strong>封装成帧</strong></li><li><strong>透明传输</strong></li><li><strong>差错控制</strong></li></ul><ol><li><h4 id="封装成帧"><a href="#封装成帧" class="headerlink" title="封装成帧"></a>封装成帧</h4></li></ol><p>封装成帧就是在一段数据的前后分别添加首部和尾部,就构成了一个帧,确定了<strong>帧的定界</strong>。也就是在收到的网络层的IP数据报的前后添加首尾部。<strong>首尾部的一个重要作用就是帧定界。</strong></p><p> 当数据是由可打印的ASCII码组成的文本文件时,帧定界可以使用特殊的帧定界符(敲不出来)。其中SOH(start)作为首部,表示帧的开始,而EOT(end)作为尾部,表示帧的结束。</p><ol start="2"><li><h4 id="透明传输"><a href="#透明传输" class="headerlink" title="透明传输"></a>透明传输</h4></li></ol><p>透明传输就是说需要<strong>使数据传输无障碍</strong>,如果出现了和帧定界符一样的比特组合,就要想方法解决。</p><p>如果说数据部分是文本文件组成的(即非特殊字符),就不会导致数据部分出现定界符。</p><p>而如果数据部分是<strong>非ASCII码</strong>的文本文件(图像、程序)时,如果数据中恰好出现和SOH或EOT一样的字节,链路层就会错误找到帧边界,而把剩下的部分丢弃。</p><p><strong>解决透明传输问题的方法是:</strong>字节填充(也即字符填充)</p><p>发送端的数据链路层在数据中出现控制字符“SOH”或<br>“EOT”的前面插入一个<strong>转义字符“ESC”</strong> (其十六进制编码是 1B)。</p><ol start="3"><li><h4 id="差错检测"><a href="#差错检测" class="headerlink" title="差错检测"></a>差错检测</h4></li></ol><p><strong>比特差错</strong>:比特在传输过程中,1可能变成0,0也可能变成1</p><p><strong>误码率</strong>:一段时间内传输错误的比特占所传输比特总数的比率</p><h5 id="循环冗余检验CRC:"><a href="#循环冗余检验CRC:" class="headerlink" title="循环冗余检验CRC:"></a>循环冗余检验CRC:</h5><p>(1)在发送端先把数据划分为组,每组<strong>K个比特</strong></p><p>(2)假设待传送的一组数据 M = 101001(现在 k =<br>6)。我们在 M 的后面再添加供差错检测用的 n<br>位冗余码一起发送。</p><h5 id="计算冗余码:"><a href="#计算冗余码:" class="headerlink" title="计算冗余码:"></a><strong>计算冗余码:</strong></h5><p>(1)用二进制的模 2 运算进行 2^n乘 M 的运算,这相当于<strong>在 M 后面添加 n 个 0</strong>。</p><p>(2)得到的 (k + n) 位的数除以事先选定好的长度为<br>(n + 1) 位的除数 P,得出商是 Q 而余数是 R,余数 R 比除数 P 少 1 位,即 R 是 n 位。</p><p>(3)将余数 R 作为冗余码拼接在数据 M 后面发送出去。</p><p>在这里进行的模2运算就是指亦或运算,相同为0,不同为1</p><img src="/2024/01/13/%E8%AE%A1%E7%BD%91%E7%AC%AC%E4%B8%89%E7%AB%A0/计算冗余码示例.jpg" alt="计算冗余码示例" style="zoom:50%;"><p><strong>帧检验序列FCS</strong>:即添加的冗余码。</p><p>循环冗余检验CRC和帧检验序列FCS不一样,CRC 是一种常用的检错方法,而 FCS 是添加在数据后面的冗余码。</p><p>FCS 可以用 CRC 这种方法得出,但 CRC 并非用来<br>获得 FCS 的唯一方法。</p><p><strong>也即它们的区别是CRC能得到冗余码FCS,但是冗余码不止能由CRC得到。</strong></p><h5 id="用生成多项式表示除数P:"><a href="#用生成多项式表示除数P:" class="headerlink" title="用生成多项式表示除数P:"></a>用生成多项式表示除数P:</h5><img src="/2024/01/13/%E8%AE%A1%E7%BD%91%E7%AC%AC%E4%B8%89%E7%AB%A0/计算冗余码示例.jpg" alt="计算冗余码示例" style="zoom:50%;"><p><strong>判断接收的数据是否出错</strong>:将收到的每一个帧都模2除以同样的除数P,然后检查得到的余数R,这里的P和添加冗余码的P是一样的,即P是发送和接收双方协同规定的。只要判断<strong>R≠0,就丢弃</strong>。但这种检测方法<strong>并不能确定究竟是哪一个或哪几个比特出现了差错</strong>。</p><p>循环冗余检验CRC差错检测技术只能做到无差错接收,也就是说凡是接收端数据链路层接受的帧都没有传输差错。</p><p><strong>无比特差错与无传输差错</strong>是不同的概念,无比特差错是指接收到的数据不会有比特差错,无传输差错也可以出现帧丢失、重复、失序等情况。在数据链路层使用 CRC 检验,能够实现无比特差错的传输,但这还不是可靠传输。</p><h2 id="3-2-点对点协议PPP"><a href="#3-2-点对点协议PPP" class="headerlink" title="3.2 点对点协议PPP"></a>3.2 点对点协议PPP</h2><h3 id="3-2-1-PPP协议的特点"><a href="#3-2-1-PPP协议的特点" class="headerlink" title="3.2.1 PPP协议的特点"></a>3.2.1 PPP协议的特点</h3><p><strong>用户使用拨号电话线接入互联网时</strong>, 用户计算机和 ISP 进行通信时所使用的数据链路层协议就是 PPP 协议。</p><ol><li><h4 id="PPP协议满足的需求"><a href="#PPP协议满足的需求" class="headerlink" title="PPP协议满足的需求"></a>PPP协议满足的需求</h4></li></ol><ul><li><strong>简单——首要要求</strong></li><li><strong>封装成帧</strong></li><li><strong>透明性</strong></li><li><strong>多种网络层协议</strong></li><li>多种类型链路</li><li><strong>差错检测</strong></li><li>检测连接状态</li><li>最大传送单元</li><li>网络层地址协商</li><li>数据压缩协商</li></ul><ol start="2"><li><h4 id="PPP协议不需要的功能"><a href="#PPP协议不需要的功能" class="headerlink" title="PPP协议不需要的功能"></a>PPP协议不需要的功能</h4></li></ol><ul><li><strong>纠错:不提供使用序号和确认的可靠传输</strong></li></ul><p>PPP 协议之所以不使用序号和确认机制是出于以下的考虑:在数据链路层出现差错的概率不大时,使用比较简单的 PPP 协议较为合理;在因特网环境下,PPP 的信息字段放入的数据是 IP<br>数据报。<strong>数据链路层的可靠传输并不能够保证网络层的传输也是可靠的</strong>;帧检验序列 FCS 字段可保证无差错接受。</p><ul><li>流量控制</li><li>序号</li><li>多点线路</li><li>半双工或单工链路</li></ul><ol start="3"><li><h4 id="PPP协议的组成(简单了解)"><a href="#PPP协议的组成(简单了解)" class="headerlink" title="PPP协议的组成(简单了解)"></a>PPP协议的组成(简单了解)</h4></li></ol><p>PPP协议有三个组成部分:</p><p>(1)一个将IP数据报封装到串行链路的方法</p><p>(2)网络控制协议NCP</p><p>(3)链路控制协议LCP</p><p>PPP的两个不同子层:</p><ul><li><p>NCP:一组协议,每一个协议支持不同的网络层协议,如IP、OSI的网络层、AppleTalk等。</p></li><li><p>LCP:用来<strong>建立、配置和测试数据链路连接</strong></p></li></ul><p>PPP会话的建立:</p><p>1.链路建立 2.<strong>验证阶段(可选)</strong> 3.网络层协议连接</p><h3 id="3-2-2-PPP协议的帧格式"><a href="#3-2-2-PPP协议的帧格式" class="headerlink" title="3.2.2 PPP协议的帧格式"></a>3.2.2 PPP协议的帧格式</h3><p>PPP帧的首部的第一个字段和尾部的第二个字段都是标志字段(0x7E),表示一个帧的开始或结束,即定界符。</p><p>PPP是面向字节的,<strong>所有的PPP帧的长度都是整数字节</strong>。</p><img src="/2024/01/13/%E8%AE%A1%E7%BD%91%E7%AC%AC%E4%B8%89%E7%AB%A0/PPP协议帧格式.jpg" alt="PPP协议帧格式" style="zoom:50%;"><h4 id="PPP中的透明传输问题"><a href="#PPP中的透明传输问题" class="headerlink" title="PPP中的透明传输问题"></a>PPP中的透明传输问题</h4><p>信息字段出现和标志字段一样的比特组合时,必须采取一些措施使这种形式上和标志字段一样的比特组合不出现在信息字段中。</p><h5 id="1-当PPP用在异步传输时,使用字符填充法"><a href="#1-当PPP用在异步传输时,使用字符填充法" class="headerlink" title="1.当PPP用在异步传输时,使用字符填充法"></a>1.当PPP用在异步传输时,使用字符填充法</h5><p>把<strong>转义字符</strong>定义为0x7D,将信息字段中出现的每一个 0x7E 字节转变成为<br>2 字节序列 (0x7D, 0x5E);若信息字段中出现一个 0x7D 的字节, 则将其转变成为 2 字节序列 (0x7D, 0x5D)</p><h5 id="2-当PPP用在SONET-x2F-SDH链路时,同步传输,使用零比特填充"><a href="#2-当PPP用在SONET-x2F-SDH链路时,同步传输,使用零比特填充" class="headerlink" title="2.当PPP用在SONET/SDH链路时,同步传输,使用零比特填充"></a>2.当PPP用在SONET/SDH链路时,同步传输,使用零比特填充</h5><p>在发送端,只要发现有5个连续1,即立即填入一个0;接收端对帧中的比特流进行扫描,每当发现5个连续1时,就把这5个连续1后的一个0删除。</p><h2 id="3-3-使用广播信道的数据链路层"><a href="#3-3-使用广播信道的数据链路层" class="headerlink" title="3.3 使用广播信道的数据链路层"></a>3.3 使用广播信道的数据链路层</h2><p>局域网最主要的特点是:网络为一个单位所拥有,地理范围和站点数目均有限。</p><p>局域网的拓扑结构为:</p><img src="/2024/01/13/%E8%AE%A1%E7%BD%91%E7%AC%AC%E4%B8%89%E7%AB%A0/局域网拓扑结构.jpg" alt="局域网拓扑结构" style="zoom:50%;"><p>(在这里,局域网等同以太网)</p><p><strong>以太网的两个标准</strong>:DIX Ethernet V2是世界上第一个局域网产品的规约;IEEE 802.3是第一个IEEE的以太网标准。</p><p>局域网的数据链路层的两个子层是:</p><ul><li><strong>逻辑链路控制</strong>LLC子层</li><li><strong>媒体接入控制</strong>MAC子层</li><li>与<strong>接入到传输媒体有关</strong>的内容都放在MAC子层,而LLC子层则与传输媒体无关。</li></ul><img src="/2024/01/13/%E8%AE%A1%E7%BD%91%E7%AC%AC%E4%B8%89%E7%AB%A0/对LLC子层透明.jpg" alt="对LLC子层透明" style="zoom:67%;"><p>因为LLC子层在上面而MAC子层在下面,所以对于LLC层来说是透明的。</p><p><strong>网卡</strong>是实现以太网协议的硬件。</p><p><strong>以太网采取了两种重要措施:</strong></p><p>(1)采用较为灵活的<strong>无连接的工作方式</strong>,提供<strong>不可靠</strong>的交付</p><ul><li>不必先建立连接就可以直接发送数据</li><li>对发送的数据帧不进行编号,也不要求对方发回确认</li><li><strong>这样做的理由是局域网信道的质量很好,因信道质量产生差错的概率是很小的。</strong></li></ul><p>(2)以太网发送的数据都<strong>使用曼彻斯特编码</strong></p><img src="/2024/01/13/%E8%AE%A1%E7%BD%91%E7%AC%AC%E4%B8%89%E7%AB%A0/曼彻斯特编码.jpg" alt="曼彻斯特编码" style="zoom:50%;"><p>曼彻斯特编码是“<strong>低平到高平代表0,高平到低平代表1</strong>”。曼彻斯特编码是<strong>两个码元即两个比特代表我们看到的一个比特</strong>,所以一个数据位由两个曼彻斯特码元组成。</p><p><strong>两种媒体共享技术的方法:</strong></p><p>(1)<strong>静态划分信道</strong></p><ul><li>频分复用</li><li>时分复用</li><li>波分复用</li><li>码分复用</li></ul><p>静态分配的特点:预先分配给用户;不同用户使用情况不同;不发生信道冲突</p><p>静态分配的<strong>缺陷</strong>:<strong>资源分配不合理</strong>,不满足用户对资源占用的不同需求;有<strong>资源浪费</strong>,效率低;信道N等分,<strong>延迟时间增大N倍</strong>。</p><p>静态分配的<strong>应用</strong>:适于<strong>用户数量少</strong>且<strong>用户数目固定</strong>的情况;适于<strong>通信量大</strong>且<strong>流量稳定</strong>的情况;<strong>不适用于突发性业务的情况</strong>。</p><p>(2)<strong>动态媒体接入控制(多点接入)</strong></p><ul><li>随机接入:站点之间<strong>争用信道</strong>,可能出现站点之间的冲突。</li><li>受控接入:站点被分配占用信道,<strong>无冲突</strong>。</li></ul><p>动态分配的<strong>特点</strong>:信道是开放的;没有预分配。</p><p>通过多点接入协议<strong>动态分配</strong>信道资源,提高信道利用率。</p><h3 id="3-3-2-CSMA-x2F-CD协议:先听后发、变发边听"><a href="#3-3-2-CSMA-x2F-CD协议:先听后发、变发边听" class="headerlink" title="3.3.2 CSMA/CD协议:先听后发、变发边听"></a>3.3.2 CSMA/CD协议:先听后发、变发边听</h3><ul><li>CSMA/CD的含义:<strong>载波监听多点接入/碰撞检测</strong></li><li>多点接入:是<strong>总线型网络</strong>,表示<strong>许多计算机以多点接入的方式连接在一根总线上</strong>。</li><li>载波监听:每一个站在<strong>发送数据之前</strong>先要检测一下总线上是否有其他计算机在发送数据,如果有,则暂时不要发送数据,以免发生碰撞。</li><li>碰撞检测:计算机<strong>边发送数据边检测</strong>信道上的信号电压大小。每一个正在发送数据的站,一旦发现总线上出现了碰撞,就要<strong>立即停止发送</strong>,免得继续浪费网络资源,然后<strong>等待一段随机时间</strong>后再次发送。</li></ul><img src="/2024/01/13/%E8%AE%A1%E7%BD%91%E7%AC%AC%E4%B8%89%E7%AB%A0/信号传播延时对载波监听的影响.jpg" alt="信号传播延时对载波监听的影响" style="zoom:50%;"><p>最迟需要经过两倍的总线端到端的传播时延(2t),或总线的端到端往返传播时延才能知道自己发送的数据和其他站发送的数据有无发生碰撞。将这个2t认为是最远的两个端点来回的时间。</p><p><strong>CSMA/CD的重要特性:</strong></p><ul><li>CSMA/CD协议的以太网不能进行全双工通信而<strong>只能进行双向交替通信(半双工通信)</strong>。</li><li>每个站在发送数据之后的小短时间内,存在着遭遇碰撞的可能性。</li><li>这种<strong>发送的不确定性</strong>使整个以太网的平均通信量远小于以太网的最高数据率。</li></ul><p><strong>争用期:</strong></p><p>最先发送数据帧的站,在发送数据帧后至多经过时间<strong>2t(端到端往返时延)</strong>就可知道发送的数据帧是否遭受了碰撞。以太网的端到端往返时延2t称为<strong>争用期</strong>,或<strong>碰撞窗口</strong>。2t是最远的两端的往返时间。</p><p><strong>二进制指数类型退避算法</strong></p><ul><li>发生碰撞的站在停止发送数据后,不是等待信道变为空闲后就立即再发送数据,而是<strong>退避一个随机的时间</strong>。</li><li>基本退避时间取为争用期2t,具体的<strong>争用期时间是51.2us,对于10Mbit/s以太网,在争用期内可以发送512比特</strong>,即64字节,因此也可以说<strong>争用期是512比特时间</strong>,所以<strong>时间与数据率密切相关</strong>,可以<strong>直接使用比特作为争用期的单位。争用期是512比特,即争用期是发送512比特所需的时间。</strong>这里应该注意的是,如果对于100Mbit/s的以太网,争用期为5.12us,这就是以发送比特数为争用期单位。</li><li>从整数【0,1,…,2的k次方-1】中随机取一个数,记为r。<strong>重传时延就是r倍的基本退避时间</strong>。</li><li>k=min[重传次数,10]。当k≤10时,参数k等于重传次数。当重传达16次仍不能成功就丢弃该帧。</li></ul><p><strong>最短有效帧长:</strong></p><ul><li><p>如果发生冲突,就一定是在发送的前 64 字节之内。</p></li><li><p>由于一检测到冲突就立即中止发送,这时已经发送出去的数据一定小于 64 字节。</p></li><li><p>以太网规定了最短有效帧长为 64 字节,凡长度小于 64 字节的帧都是由于冲突而异常中止的无效帧。</p></li></ul><p><strong>强化碰撞:</strong></p><p>当发送数据的站一旦发现发生碰撞,则立即停止发送数据,并再继续发送若干比特的<strong>人为干扰信号</strong>,让所有用户都知道现在发生了碰撞。</p><p><strong>CSMA/CD协议要点:</strong></p><p>(1)<strong>准备发送。</strong>从网络层获得分组,加上以太网的首尾部,组成以太网帧。<strong>在发送之前,必须先检测信道。</strong></p><p>(2)<strong>检测信道。</strong>若检测到信道忙,则继续不停地检测,一直等待信道转为空闲。若检测到<strong>信道空闲</strong>,并在 <strong>96 比特时间内信道保持空闲</strong>(保证了帧间最小间隔),就发送这个帧。</p><p>(3)<strong>检查碰撞。</strong>在发送过程中仍不停地检测信道,即网络适配器要<strong>边发送边监听</strong>。这里只有<strong>两种可能性</strong>:</p><ul><li>发送成功:在争用期内一直未检测到碰撞。这个帧肯定能够发送成功。发送完毕后,其他什么也不做。然后回到 (1)。</li><li>发送失败:在争用期内检测到碰撞。这时立即停止发送数据,并按规定发送人为干扰信号。适配器接着就执行指数退避算法, 等待 r 倍 512 比特时间后,返回到步骤 (2),继续检测信道。但若重传达 16 次仍不能成功,则停止重传而向上报错。</li></ul><p><strong>帧间最小间隔:</strong></p><p>一个站在检测到总线开始空闲后,还要等待9.6us才能再次发送数据;这样组都是为了使刚刚收到数据帧地站地接收缓存来得及清理,做好接受下一帧的准备。</p><h3 id="3-3-3-使用集线器的星形拓扑"><a href="#3-3-3-使用集线器的星形拓扑" class="headerlink" title="3.3.3 使用集线器的星形拓扑"></a>3.3.3 使用集线器的星形拓扑</h3><ul><li>传统以太网最初是使用<strong>粗同轴电缆</strong>,后来演进到使用比较便宜的<strong>细同轴电缆</strong>,最后发展为使用更便宜和更灵活的<strong>双绞线</strong>。</li><li>采用双绞线的以太网采用星形拓扑,在星形的中心则增加了一种可靠性非常高的设备,叫做<strong>集线器</strong> (hub)。</li></ul><img src="/2024/01/13/%E8%AE%A1%E7%BD%91%E7%AC%AC%E4%B8%89%E7%AB%A0/采用集线器的双绞线以太网.jpg" alt="采用集线器的双绞线以太网" style="zoom:50%;"><p><strong>星形以太网10BASE-T</strong></p><p>1990年 IEEE 制定出星形以太网 10BASE-T 的标准 802.3i,<strong>其中10代表数据率为10Mbit/s,BASE代表基带信号,T代表双绞线</strong>。双绞线的两端使用 <strong>RJ-45 插头</strong>。主机到<strong>交换机或集线器</strong>间都是用10BASE-T标准,每站间<strong>长为100m</strong>。</p><ul><li>10BASE-5 粗缆,距离500m;10BASE-2,细缆,距离200m;</li></ul><p><strong>10BASE-T以太网在局域网中的统治地位</strong>:</p><ul><li>这种 10 Mbit/s 速率的无屏蔽双绞线星形网的出现,既<strong>降低了成本,又提高了可靠性</strong>。 具有很高的性价比。</li><li><strong>10BASE-T 双绞线以太网的出现,是局域网发展史上的一个非常重要的里程碑</strong>,它为以太网在局域网中的统治地位奠定了牢固的基础。</li></ul><p><strong>集线器的一些特点:</strong></p><p>(1)集线器是使用电子器件来模拟实际电缆线的工作, 因此整个系统仍然像一个传统的以太网(总线型)那样运行。</p><p>(2)使用集线器的以太网在<strong>逻辑上仍是一个总线网</strong>,各<strong>工作站使用的还是 CSMA/CD 协议</strong>,并<strong>共享逻辑上的总线</strong>。</p><p>(3)集线器很像一个多接口的转发器,<strong>集线器工作在物理层</strong>。</p><p>(4)集线器采用了专门的芯片,进行自适应串音回波抵消,减少了近端串音。</p><h3 id="3-3-4-以太网的信道利用率"><a href="#3-3-4-以太网的信道利用率" class="headerlink" title="3.3.4 以太网的信道利用率"></a>3.3.4 以太网的信道利用率</h3><ul><li>发生碰撞,信道资源被浪费,当扣除碰撞造成的信道损失后,<strong>以太网总的信道利用率并不能达到100%</strong>。</li><li>设帧长为 L (bit),数据发送速率为 C (bit/s),则帧的发送时间为 <strong>T0 = L/C (s)</strong></li></ul><img src="/2024/01/13/%E8%AE%A1%E7%BD%91%E7%AC%AC%E4%B8%89%E7%AB%A0/以太网信道被占用的情况.jpg" alt="以太网信道被占用的情况" style="zoom:50%;"><p>成功发送一个帧需要占用信道的时间是T0+t,因为当一个站发送完最后一个比特时,这个比特还要在以太网上传播。</p><p><strong>参数α与利用率:</strong></p><ul><li>要提高以太网信道利用率,就必须减小t与T0之比。</li><li>以太网中定义了参数α,它是以太网单程端到端时延t与帧的发送时间T0之比:α=t/T0。</li><li>α →0,表示一发生碰撞就立即可以检测出来,并立即停止发送,因而信道利用率很高。</li><li>α 越大,表明争用期所占的比例增大,每发生一次碰撞就浪费许多信道资源,使得信道利用率明显降低。</li></ul><p>为了提高利用率,以太网的参数α的值应当尽可能小些。<strong>故当数据率一定时,以太网的连线的长度受到限制,否则t的数值会太大;以太网的帧长不能太短,否则T0的值会太小,使α值太大。</strong></p><p><strong>信道利用率的最大值Smax:</strong></p><p><strong>理想化</strong>情况下,以太网上的各站发送数据都不会产生碰撞(这显然已经不是 CSMA/CD,而是需要使用一种特殊的调度方法),即总线一旦空闲就有某一个站立即发送数据。</p><p>发送一帧占用线路的时间是 T0 + t,而帧本身的发送时间是 T0 。于是我们可计算出理想情况下的极限信道利用率Smax 为:1/(1+α)。这说明,只有当参数 a 远小于 1 才能得到尽可能高的极限信道利用率。</p><p><strong>所以使用集线器的以太网利用率很低。</strong></p><h3 id="3-3-5-以太网的MAC层"><a href="#3-3-5-以太网的MAC层" class="headerlink" title="3.3.5 以太网的MAC层"></a>3.3.5 以太网的MAC层</h3><h4 id="1-MAC层的硬件地址"><a href="#1-MAC层的硬件地址" class="headerlink" title="1.MAC层的硬件地址"></a>1.MAC层的硬件地址</h4><p>局域网中,<strong>硬件地址</strong>又称为<strong>物理地址</strong>,或<strong>MAC地址</strong>。如果主机<strong>有多个接口(即适配器),这个主机就有多个MAC地址,MAC地址是标识接口的。</strong></p><p><strong>48位的MAC地址</strong></p><p>IEEE的注册管理机构RA负责向厂家分配地址字段6个字节中的<strong>前三个字节</strong>(即高位24位),称为<strong>组织唯一标识符</strong>。</p><p>地址字段6个字节中的后三个字节(即<strong>低位24位</strong>)由厂家自行指派,称为<strong>扩展唯一标识符</strong>,<strong>必须保证生产出的适配器没有重复地址</strong>。</p><img src="/2024/01/13/%E8%AE%A1%E7%BD%91%E7%AC%AC%E4%B8%89%E7%AB%A0/48位的MAC地址.jpg" alt="48位的MAC地址" style="zoom:50%;"><p>一个地址块可以生成 2的24次方个不同的地址。这种 48 位地址称为 MAC-48,它的通用名称是 EUI-48。</p><p>生产适配器时,6 字节的 MAC 地址已被固化在适配器的 ROM,因此,MAC 地址也叫作<strong>硬件地址</strong> (hardware address)或<strong>物理地址</strong>。</p><p>“MAC地址”实际上就是适配器地址或适配器标识符 EUI-48。</p><p><strong>单站地址,组地址,广播地址:</strong></p><p>IEEE规定地址字段的第一字节的最低位为I/G位。当<strong>I/G位=0</strong>时,地址字段表示一个<strong>单站地址</strong>。当<strong>I/G位=1</strong>时,表示<strong>组地址</strong>,用来进行多播。当I/G位分别为0和1时,一个地址块可分别生成2的24次方个单站地址和2的24次方个组地址。</p><p>所有 48 位都为 1 时,为<strong>广播地址</strong>。只能作为目的地址使用。</p><p>全球管理与本地管理:</p><p>IEEE 把地址字段第一字节的最低第 2 位规定为G/L位,当 G/L位=0 时,是全球管理,当 G/L位=1 时, 是本地管理。</p><p>适配器检查MAC地址:</p><p>适配器的过滤功能:适配器从网络上每收到一个MAC帧就首先用硬件检查MAC帧中的MAC地址。如果是<strong>发往本站的帧</strong>则收下,然后再进行其他处理。否则就将此帧丢弃,不再进行其他的处理。</p><p><strong>”发往本站的帧“,包括以下三种:</strong></p><ul><li><p><strong>单播帧(一对一)</strong></p></li><li><p><strong>广播帧(一对全体)</strong></p></li><li><p><strong>多播帧(一对多)</strong></p></li></ul><p>所有适配器都至少能识别前两种帧,<strong>即能识别单播地址和广播地址</strong>。有的适配器可用编程方法识别多播地址。<strong>只有目的地址才能使用广播地址和多播地址。</strong></p><p>以<strong>混杂方式</strong>工作的以太网适配器只要听到有帧在以太网上传输就都接收下来。</p><h4 id="2-MAC帧的格式"><a href="#2-MAC帧的格式" class="headerlink" title="2.MAC帧的格式"></a>2.MAC帧的格式</h4><p>常用的以太网MAC帧格式有两种标准:</p><ul><li><p><strong>DIX Ethernet V2 标准</strong></p></li><li><p><strong>IEEE 的 802.3 标准</strong></p></li></ul><p>最常用的MAC帧是<strong>以太网V2的格式</strong></p><img src="/2024/01/13/%E8%AE%A1%E7%BD%91%E7%AC%AC%E4%B8%89%E7%AB%A0/数据字段.jpg" alt="数据字段" style="zoom:50%;"><p>数据字段的长度在46~1500字节之间(46字节是这样得出:最小长度64字节减去18字节的首部和尾部就得出数据字段的最小长度)。如果数据字段的长度小于46字节,应在数据字段的后面加入整数字节的填充字段,保证以太网的MAC帧长不小于64字节。</p><p><strong>无效的MAC帧:</strong></p><ul><li><p><strong>帧的长度不是整数个字节</strong></p></li><li><p><strong>用收到的帧检验序列 FCS 查出有差错</strong></p></li><li><p><strong>数据字段的长度不在 46 ~ 1500 字节之间</strong></p></li><li><p><strong>有效的 MAC 帧长度为 64 ~ 1518 字节之间</strong></p></li></ul><p>对于检查出的无效 MAC 帧就简单地丢弃。以太网不负责重传丢弃的帧。</p><p><strong>IEEE 802.3MAC帧格式</strong></p><p>与以太网V2 MAC 帧格式相似,区别在于:</p><p>(1) IEEE 802.3 规定的 MAC 帧的第三个字段是“<strong>长度 / 类型</strong>”。</p><p>当这个字段值大于 0x0600 时(相当于十进制的1536),就<strong>表示“类型”</strong>。这样的帧和以太网V2 MAC 帧完全一样。</p><p>当这个字段值小于 0x0600 时才<strong>表示“长度”</strong>。</p><p>(2) 当“长度/类型”字段值小于 0x0600 时,数据字段必须装入上面的逻辑链路控制 LLC 子层的 LLC 帧。</p><h2 id="3-4-扩展的以太网"><a href="#3-4-扩展的以太网" class="headerlink" title="3.4 扩展的以太网"></a>3.4 扩展的以太网</h2><h3 id="3-4-1-在物理层扩展以太网(主要是集线器)"><a href="#3-4-1-在物理层扩展以太网(主要是集线器)" class="headerlink" title="3.4.1 在物理层扩展以太网(主要是集线器)"></a>3.4.1 在物理层扩展以太网(主要是集线器)</h3><p><strong>1.使用光纤扩展</strong></p><p>主机使用光纤(通常是一对光纤)和一对光纤调制解调器连接到集线器。很容易使主机和几公里以外的集线器相连接。</p><p><strong>2.使用集线器扩展</strong></p><p>使用多个集线器可连成更大的、多级星形结构的以太网。</p><img src="/2024/01/13/%E8%AE%A1%E7%BD%91%E7%AC%AC%E4%B8%89%E7%AB%A0/集线器扩展以太网.jpg" alt="集线器扩展以太网" style="zoom:50%;"><p><strong>这样做的好处为:</strong></p><ul><li><p><strong>使原来属于不同碰撞域的以太网上的计算机能够进行跨碰撞域的通信。</strong></p></li><li><p><strong>扩大了以太网覆盖的地理范围。</strong></p></li></ul><p><strong>缺点</strong></p><ul><li>碰撞域增大了,但总的吞吐量并未提高。</li><li>如果不同的碰撞域使用不同的数据率,那么就不能用集线器将它们互连起来。</li></ul><h3 id="3-4-2-在数据链路层扩展以太网"><a href="#3-4-2-在数据链路层扩展以太网" class="headerlink" title="3.4.2 在数据链路层扩展以太网"></a>3.4.2 在数据链路层扩展以太网</h3><p>早期使用<strong>网桥</strong>,现在使用以太网<strong>交换机</strong>。</p><p>网桥工作在数据链路层,<strong>根据MAC帧的目的地址对收到的帧进行转发和过滤</strong>。</p><p><strong>交换式集线器</strong>称为<strong>以太网交换机</strong>或<strong>第二层交换机</strong>,工作在数据链路层。</p><h4 id="1-以太网交换机的特点"><a href="#1-以太网交换机的特点" class="headerlink" title="1.以太网交换机的特点"></a>1.以太网交换机的特点</h4><ul><li>以太网交换机实质上是一个<strong>多接口的网桥</strong>。</li><li>每个接口都直接与一个单台主机或另一个以太网交换机相连,并且一般都工作在<strong>全双工方式</strong>。</li><li>以太网交换机<strong>具有并行性</strong>,能够同时连同多接口,使多对主机同时通信。</li><li><strong>相互通信的主机都是独占传输媒体,无碰撞地传输数据。</strong></li><li>以太网交换机是一种<strong>即插即用</strong>设备,其内部的<strong>帧交换表</strong>(又称为<strong>地址表</strong>)是通过<strong>自学习算法</strong>自动地逐渐建立起来的。</li><li><strong>用户独享带宽,增加了总容量</strong>。<ul><li>对于普通 10 Mbit/s 的<strong>共享式以太网</strong>(集线器),若共有 N 个用户,则每个用户占有的平均带宽只有总带宽 (10 Mbit/s)的 N 分之一。</li><li>使用以太网交换机时,虽然在每个接口到主机的带宽还是10Mbit/s,但由于一个用户在通信时是<strong>独占而不是和其他网络用户共享传输媒体的带宽</strong>,因此对于拥有 N 个接口的交换机的总容量为 N×10 Mbit/s。</li></ul></li></ul><img src="/2024/01/13/%E8%AE%A1%E7%BD%91%E7%AC%AC%E4%B8%89%E7%AB%A0/集线器和交换机的比较.jpg" alt="集线器和交换机的比较" style="zoom:50%;"><p><strong>以太网交换机的交换方式:</strong></p><p><strong>1.直通方式:</strong>接收数据帧的同时就<strong>立即按数据帧的目的 MAC 地址</strong>决定该帧的转发接口,因而提高了帧的转发速度;<strong>缺点</strong>是它不检查差错就直接将帧转发出去,因此有可能也将一些无效帧转发给其他的站。</p><p><strong>2.存储转发方式:</strong>把整个数据帧先缓存,检查是否出错,如无错才送往目的端口。有差错控制;交换时延较长。</p><h4 id="2-以太网交换机的自学功能(看P102)"><a href="#2-以太网交换机的自学功能(看P102)" class="headerlink" title="2.以太网交换机的自学功能(看P102)"></a>2.以太网交换机的自学功能(看P102)</h4><ul><li>最初开机时MAC地址表是空的。</li><li>交换机接收到帧后,先查找地址表,没有目的地址,就将源地址和发送端口写入交换表中,并向除了端口1以外的所有端口广播这个帧。</li></ul><p><strong>整个过程大概就是说一开始如果没有找到目的地址就要向除了发出端口的所有端口都转发,然后要把源地址写入,后续目的地址是有了,但是每次发出去都还是要写入源地址。</strong></p><p>交换表中每个条目都设有一定的<strong>有效时间</strong>。若两主机在该时间内未通信,该条目将<strong>自动被删除</strong>。</p><p>Mac地址表空间有限,一旦地址表满,就会<strong>洪泛所有到新Mac地址的帧</strong>,直到现存地址条目老化为止。</p><p>交换机的内存有限,如果<strong>发送大量错误MAC地址的数据帧对交换机进行攻击,造成内存溢出。</strong></p><h4 id="生成树协议"><a href="#生成树协议" class="headerlink" title="生成树协议"></a>生成树协议</h4><p><strong>冗余拓扑</strong>消除了由于单点故障所引致的网络不通问题。冗余拓扑却带来了<strong>广播风暴、重复帧和MAC地址表不稳定</strong>的问题。</p><p><strong>生成树协议STP的要点是</strong>:<strong>不改变网络的实际拓扑,但在逻辑上则切断某些链路,通过将某些端口置于阻塞状态,使得从一台主机到所有其他主机的路径是无环路的树状结构,从而消除了兜圈子现象。</strong></p><h4 id="3-从总线以太网到星形以太网"><a href="#3-从总线以太网到星形以太网" class="headerlink" title="3.从总线以太网到星形以太网"></a>3.从总线以太网到星形以太网</h4><p>总线以太网使用 CSMA/CD 协议,以半双工方式工作。以太网交换机不使用共享总线,没有碰撞问题, 因此不使用 CSMA/CD 协议,而是以全双工方式工作。<strong>但仍然采用以太网的帧结构</strong>。</p><h3 id="3-4-3-虚拟局域网"><a href="#3-4-3-虚拟局域网" class="headerlink" title="3.4.3 虚拟局域网"></a>3.4.3 虚拟局域网</h3><p>虚拟局域网( VLAN ):它是将局域网从逻辑上划分为一个个的网段,从而实现虚拟工作组的一种交换技术。</p><ul><li>利用以太网交换机可以很方便地实现虚拟局域网 VLAN (Virtual LAN)。</li><li><strong>虚拟局域网 VLAN</strong> 是由一些局域网网段构成的<strong>与物理位置无关的逻辑组</strong>,而这些网段具有某些共同的需求。每一个 VLAN 的帧都有一个明确的标识符,指明发送这个帧的计算机是属于哪一个 VLAN。</li><li><strong>虚拟局域网其实只是局域网给用户提供的一种服务, 而并不是一种新型局域网。</strong></li><li>由于虚拟局域网是用户和网络资源的逻辑组合,因此可按照需要将有关设备和资源非常方便地重新组合,使用户从不同的服务器或数据库中存取所需的资源。</li></ul><p>一个VLAN = 一个广播域 = 逻辑网段 (子网)。在同一个网段中前3个数必须相同,否则不可通信,但这种不可通信是逻辑上的,可以通过IP更改而改变,而VLAN是物理上的。</p><p>虚拟局域网协议允许在以太网的帧格式中插入 一个4字节的标识符,称为 <strong>VLAN 标记 (tag)<strong>, 用来指明发送该帧的计算机属于哪一个虚拟局域网。插入 VLAN 标记得出的帧称为 <strong>802.1Q 帧</strong>或</strong>带标记的以太网帧</strong>。</p>]]></content>
<tags>
<tag> 本科课内 </tag>
</tags>
</entry>
<entry>
<title>一点点CV知识</title>
<link href="/2023/01/18/MicroExpression/"/>
<url>/2023/01/18/MicroExpression/</url>
<content type="html"><![CDATA[<p><strong>K折交叉验证</strong>:每轮都将训练集分为训练集+验证集两部分,K-1折作为训练集而1折作为测试集。</p><p>它是一种用于<strong>评估模型</strong>的方法,一般<strong>数据集小</strong>,选择的<strong>测试集样本不足</strong>可能会导致评估结果不稳定,而使用这种方法可以使测试集样本较多,使结果稳定。并且将数据都使用了一遍作为训练和测试,就具有说服力。</p><p>下面是10折交叉验证的原理图:将数据集分为十折,每轮取其中九折作为训练集,剩下一折作为验证集。每轮的验证集需要记录结果最后返回平均值作为精度,从而对模型进行评估。</p><p><img src="/2023/01/18/MicroExpression/%E4%BA%A4%E5%8F%89%E9%AA%8C%E8%AF%81.png" alt="交叉验证"></p><p>它的代码实现方法是在每一个Epoch中使用交叉验证,并且将这一轮的十折交叉后的<strong>eva的平均值作为返回的Acc</strong></p><figure class="highlight python"><table><tr><td class="code"><pre><span class="line"><span class="comment"># shuffle是代表每一折的数据被随机打乱,如果为False,就是顺序性的每一轮选不一样的数据作为验证集。</span></span><br><span class="line">kf = kFold(n_splits=<span class="number">5</span>, shuffle=<span class="literal">True</span>, random_state=<span class="number">42</span>)</span><br><span class="line">epoch <span class="keyword">in</span> <span class="built_in">range</span>(<span class="number">1</span>, EPOCHS+<span class="number">1</span>):</span><br><span class="line"> <span class="keyword">for</span> fold, (train_indexes, val_indexes) <span class="keyword">in</span> <span class="built_in">enumerate</span>(kf.split(dataset)):</span><br><span class="line"> train_dataset = torch.utils.data.Subset(dataset, train_indexes)</span><br><span class="line"> val_dataset = torch.utils.data.Subset(dataset, val_indexes)</span><br><span class="line"> train_loader = DataLoader(train_dataset, batch_size=BATCH_SIZE, drop_last=<span class="literal">True</span>)</span><br><span class="line"> val_loader = DataLoader(val_dataset, batch_size=BATCH_SIZE, shuffle=<span class="literal">False</span>)</span><br></pre></td></tr></table></figure><p>核心部分代码如上所示:需要解释的是kFold实质只是每轮将dataset的图像顺序编号找到并且提取出来。</p><p>在一份<strong>train代码中</strong>,对于数据需要做预处理以及增强操作。</p><p>常用的数据增强方法为角度变化、明暗度变化,当然明暗度变化也可以作为预处理中的部分看待,看用于什么地方。</p><p><code>torchvision.transfoms</code>可以使用Compose对数据进行一系列的变换和处理。方法是transforms.Compose(list<transforms>)</transforms></p><p>对于图像的输入处理可以使用**transforms.Resize()*<em>函数。如果是缩放短边,并保持长宽比不变的话就用Resize(224);如果需要把长短边都变成224</em>224就需要写Resize([224,224])。</p><p>用于对输入的数据进行数据增强和预处理的方法有:</p><ul><li>transforms中的中心裁剪使用CenterCrop(224)就会裁切成(224,224)的格式,而如果使用CenterCrop([h,w])就会切成(h,w)的格式;</li><li>transforms中的RandomHorizontalFlip()是水平翻转图像,默认的概率是p=0.5,这样可以对图片进行概率的翻转。这里我们需要知道:tran</li><li>transforms中的ToTensor作用是将PIL image或ndarray数据转化为tensor数据,并且使用将所有像素点除以255以归一化到[0,1]。注意:tensor数据维度为[C,H,W],而图像的np.array数据是[H,W,C],所以ToTensor还有作用就是reshape的作用。</li><li>Normalize操作是将tensor图片归一化,不支持PIL image格式,所以对于PIL格式的图片,Normalize操作必须要在Totensor之后。有了上面的ToTensor再进行transforms.Normalize(mean=[0.5,0.5,0.5],std=[0.5,0.5,0.5])就相当于把每张图像的每个像素点的值都缩小到[-1,1]之间了。</li><li>torchtoolbox.transform中的Cutout随机将图像中的某些部分遮挡掉,以减少模型对局部的依赖性。它的内部是随机选择一个趋于并将该区域内的像素值设置为0。它与torchvision.transforms中的RandomErasing是一样的效果。</li></ul><p>此外我们需要了解的是:</p><ol><li><p>将PIL图像转化为Numpy数组的方法为:</p><figure class="highlight python"><table><tr><td class="code"><pre><span class="line">img = Image.<span class="built_in">open</span>(img_path)</span><br><span class="line">imgArray = np.array(img)</span><br><span class="line"><span class="comment"># 得到的维度为(H,W,C)</span></span><br></pre></td></tr></table></figure></li><li><p>将Numpy转换为PIL的方法为:</p><figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">img = Image.fromarray(imgArray)</span><br><span class="line">img.show()</span><br></pre></td></tr></table></figure></li><li><p>Numpy转tensor也可以:</p><figure class="highlight python"><table><tr><td class="code"><pre><span class="line">img_tensor = torch.tensor(imgArray)</span><br></pre></td></tr></table></figure></li><li><p>tensor转Numpy:</p><figure class="highlight python"><table><tr><td class="code"><pre><span class="line">imgArray = img_tensor.numpy()</span><br><span class="line"><span class="comment">#或者</span></span><br><span class="line">imgArray = np.array(img_tensor)</span><br></pre></td></tr></table></figure></li><li><p>tensor转Image:</p><figure class="highlight python"><table><tr><td class="code"><pre><span class="line">to_pil = transforms.ToPILImage()</span><br><span class="line">img = to_pil(img_tensor)</span><br><span class="line">img.show()</span><br></pre></td></tr></table></figure></li><li><p>list转numpy</p><figure class="highlight python"><table><tr><td class="code"><pre><span class="line">array=numpy.array(<span class="built_in">list</span>)</span><br></pre></td></tr></table></figure></li><li><p>list转tensor</p><figure class="highlight python"><table><tr><td class="code"><pre><span class="line">tensor=torch.Tensor(<span class="built_in">list</span>)</span><br></pre></td></tr></table></figure></li><li><p>array转为list</p><figure class="highlight python"><table><tr><td class="code"><pre><span class="line"><span class="built_in">list</span>=numpy.tolist()</span><br></pre></td></tr></table></figure></li><li><p>tensor转list</p><figure class="highlight python"><table><tr><td class="code"><pre><span class="line"><span class="built_in">list</span>=tensor.numpy().tolist()</span><br></pre></td></tr></table></figure></li></ol><p>list,array,tensor,numpy的辨别</p><p>array和tensor用法一致,只能存储单一数据类型的元素。</p><ul><li>list(列表)可以存储不同类型的数据,但是整体不够高效。</li><li>array(数组)较为高效,python本身没有数组这个概念,但是可以通过numpy.array来实现</li><li>tensor(张量),与numpy的ndarray相互转换,唯一不同的是tensor可以在GPU上运行,支持自动求导,而numpy的ndarray只能在cpu上运行。</li></ul><p>torchvision.datasets.ImageFolder的作用是将一个文件夹下已经分好的每一个子文件夹分配一个label,子文件夹中的图像数据作为data,data对应了label。使用方法是dataset = datasets.ImageFolder(root,transforms)。</p><p>或者我们可以自己打标签:</p><p>DataLoader中的shuffle参数=True是指在每一个epoch里数据集会被打乱再按Batch_Size的数量来取数据,这样每个epoch里Batch_Size就不一样了。能够避免模型学习到类似排序等习惯性偏差,提高模型的泛化能力。</p>]]></content>
<tags>
<tag> 科研 </tag>
</tags>
</entry>
<entry>
<title>对CV的一些初步理解</title>
<link href="/2023/01/18/%E5%AF%B9CV%E7%9A%84%E4%B8%80%E4%BA%9B%E7%90%86%E8%A7%A3%E4%BB%A5%E5%8F%8A%E4%BB%8B%E7%BB%8D/"/>
<url>/2023/01/18/%E5%AF%B9CV%E7%9A%84%E4%B8%80%E4%BA%9B%E7%90%86%E8%A7%A3%E4%BB%A5%E5%8F%8A%E4%BB%8B%E7%BB%8D/</url>
<content type="html"><![CDATA[<p>计算机视觉(computer vision)<strong>四大基本任务</strong>为:分类(a)、定位,检测(b)、语义分割(c)、实例分割(d)。</p><img src="/2023/01/18/%E5%AF%B9CV%E7%9A%84%E4%B8%80%E4%BA%9B%E7%90%86%E8%A7%A3%E4%BB%A5%E5%8F%8A%E4%BB%8B%E7%BB%8D/四大基本任务.jpg" alt="四大基本任务" style="zoom: 50%;"><p>计算机视觉难点在于<strong>语义鸿沟</strong>(semantic gap):人类可以轻松地从图像中识别出目标,而计算机看到的图像只是一组0到255之间的整数。就像计算机如成年人一样下棋是相对容易的,但是让它有如一岁小孩般的感知和行动能力却是相当困难甚至是不可能的。</p><p>计算机任务的其他困难:<strong>拍摄视角变化、目标占据图像的比例变化、光照变化、背景融合、目标形变、遮挡</strong>等。</p><p><strong>卷积神经网络(CNN)</strong></p><p>多层感知机就是一系列全连接层组成,而卷积神经网络中除了全连接层之外,还有卷积层和池化层。</p><p>(1)卷积层</p><p>为什么使用卷积层?</p><p>输入图像维数很高,1000*1000大小的彩色图像对应于300w维特征,继续沿用多层感知机中的全连接层会<strong>让参数量特别大</strong>,会导致<strong>计算量大</strong>,而且<strong>大参数会有过拟合的风险</strong>。卷积是<strong>局部连接、共享参数版的全连接层</strong>,使参数大大降低。卷积中的参数就是卷积核(滤波器)。</p><p>所谓局部连接:如果是在全连接中,<strong>每个输出通过权重矩阵和所有的输入相连</strong>。而在视觉识别中,<strong>关键性的图像特征、边缘、角点等只占据了整张图像的小部分</strong>,图像中<strong>相距很远的两个像素之间有相互影响的可能性很小</strong>。因此,卷积层中,每个输出神经元在通道方向保持全连接(像感知机一样相加),而在空间方向上只和一小部分输入神经元相连(卷积核w,h)。</p><p>共享参数:如果一组权值可以在图像中某个区域提取出有效的表示,那么它们也能在图像的另外区域中提取出有效的表示,具体的表示即卷积核的移动。<strong>如果一个pattern出现在图像中的某个区域,那么它们也可以出现在图像中的其他任何区域。</strong>这里我解释为每个卷积核会学习一个特征,得到一张特征图,这样的话就是学习到一张图片中所有具有这种特征的区域。因此,卷积层不同空间位置的神经元共享权值,用于发现图像中不同空间位置的pattern,共享参数是深度学习一个重要的思想,<strong>其在减少网络参数的同时仍然能够保持很高的网络容量</strong>(能够表达复杂函数的能力),同时这种思想也使得模型在遇到新的数据时更具有鲁棒性和更高的泛化能力。卷积层在空间方向共享参数,而循环神经网络(RNN)在时间方向共享参数。</p><p>卷积层的作用:通过卷积,我们可以捕获图像的局部信息。通过多层卷积层堆叠,各层提取到特征逐渐由边缘、纹理、方向等低层级特征过渡到文字、车轮、人脸等高层级特征。</p><p>描述卷积的四个量: 一个卷积层的配置由如下四个量确定<strong>。</strong>1. <strong>滤波器个数</strong>。使用一个滤波器对输入进行卷积会得到一个二维的特征图(feature map)。我们可以用时使用多个滤波器对输入进行卷积,以得到多个特征图。2. <strong>感受野(receptive field)</strong> <em>F</em>,即滤波器空间局部连接大小<strong>。</strong>3. <strong>零填补(zero-padding)</strong> <em>P</em><strong>。</strong>随着卷积的进行,图像大小将缩小,图像边缘的信息将逐渐丢失。因此,在卷积前,我们在图像上下左右填补一些0,使得我们可以控制输出特征图的大小。4. <strong>步长(stride)</strong> <em>S</em><strong>。</strong>滤波器在输入每移动<em>S</em>个位置计算一个输出神经元。</p><p>卷积输入输出的大小关系:假设输入高和宽为<em>H</em>和<em>W</em>,输出高和宽为<em>H</em>‘和<em>W</em>‘, 则<em>H</em>‘=(<em>H</em>-<em>F</em>+2<em>P</em>)/<em>S</em>+1, <em>W</em>‘=(<em>W</em>-<em>F</em>+2<em>P</em>)/<em>S</em>+1. 当<em>S</em>=1时,通过设定<em>P</em>=(<em>F</em>-1)/2, 可以保证输入输出空间大小相同。例如,3*3的卷积需要填补一个像素使得输入输出空间大小不变。</p><p>应该使用多大的滤波器:尽量使用小的滤波器,如3×3卷积。通过堆叠多层3×3卷积,可以取得与大滤波器相同的感受野,例如三层3×3卷积等效于一层7×7卷积的感受野。但使用小滤波器有以下两点好处。1. <strong>更少的参数量</strong>。假设通道数为<em>D</em>,三层3×3卷积的参数量为3×(<em>D</em>×<em>D</em>×3×3)=27<em>D</em>^2, 而一层7×7卷积的参数量为<em>D</em>×<em>D</em>×7×7=49<em>D</em>^2。2. <strong>更多非线性。</strong>由于每层卷积层后都有非线性激活函数,三层3×3卷积一共经过三次非线性激活函数,而一层7×7卷积只经过一次。</p><p>1×1卷积:旨在对每个空间位置的<em>D</em>维向量做一个相同的线性变换。<strong>通常用于增加非线性,或降维</strong>,<strong>这相当于在通道数方向上进行了压缩</strong>。1×1卷积是减少网络计算量和参数的重要方式。</p><p>全连接层的卷积层等效:由于全连接层和卷积层都是做点乘,<strong>这两种操作可以相互等效</strong>。全连接层的卷积层等效只需要设定好卷积层的四个量:滤波器个数等于原全连接层输出神经元个数、感受野等于输入的空间大小、没有零填补、步长为1。</p><p>为什么要将全连接层等效为卷积层:全连接层只能处理固定大小的输入,而卷积层可以处理任意大小输入。假设训练图像大小是224×224,而当测试图像大小是256×256。如果不进行全连接层的卷积层等效,我们需要从测试图像中裁剪出多个224×224区域分别前馈网络。而进行卷积层等效后,我们只需要将256×256输入前馈网络一次,即可达到多次前馈224×224区域的效果。</p>]]></content>
<tags>
<tag> 科研 </tag>
</tags>
</entry>
<entry>
<title>熵权法</title>
<link href="/2023/01/18/%E7%86%B5%E6%9D%83%E6%B3%95/"/>
<url>/2023/01/18/%E7%86%B5%E6%9D%83%E6%B3%95/</url>
<content type="html"><![CDATA[<p>是做多对象、多指标的综合评价方法。</p><p>熵权法避免层次分析法主观性强的弊端,很大程度上避免人为因素的干扰。但是我们需要前提是几种指标的重要性差不多</p><p>基本过程:归一化、计算指标变异性、计算信息熵、计算权值。</p><p><strong>问题引入:</strong></p><img src="/2023/01/18/%E7%86%B5%E6%9D%83%E6%B3%95/引入的问题.png" alt="引入的问题" style="zoom:67%;"><p>利用一个不同于层次分析法的方法构造权重,根据A、B得分来评价它的权重,然后给出一个得分,<strong>归一化之后的值容易得到</strong>,<strong>权重不容易得到</strong>。</p><p><strong>熵权法的简介:</strong></p><ul><li>熵:熵的概念来源于热力学,物理意义是度量热力学系统的无序程度。<strong>熵与信息量成反比,熵值越小越好</strong>。</li><li>熵权法:熵权法是一种可以用于多对象、多指标的综合评价方法,其评价结果主要是依据客观资料,<strong>最大优点是几乎不受主观因素的影响</strong>,可以在很大程度上避免人为因素的干扰。</li></ul><p>我们可以选择<strong>正向权重</strong>和<strong>逆向权重</strong>两种,正向权重被除完后越大越好,逆向权重和熵值正相关,权重就越小越好,看我们如何构造了。</p><p><strong>熵权法的基本思路:</strong></p><p>权重大的话,提供的信息量也就越大(正向权重),指标变异性大(指标占的比重大),信息熵值就会小,熵值越小,权重越大。</p><img src="/2023/01/18/%E7%86%B5%E6%9D%83%E6%B3%95/熵权法的思路.png" alt="熵权法的思路" style="zoom:67%;"><p>所以我们要根据指标占的比重来确定客观权重。</p><p>首先进行归一化处理,将计量单位统一,将指标绝对值找一个标准化成相对值;</p><p><strong>归一化:</strong></p><img src="/2023/01/18/%E7%86%B5%E6%9D%83%E6%B3%95/归一化.png" alt="归一化" style="zoom: 67%;"><p>归一化就是将<strong>指标的绝对值转化为相对值</strong>,转成相对值就没有单位影响了。</p><p><strong>计算指标变异性:</strong></p><img src="/2023/01/18/%E7%86%B5%E6%9D%83%E6%B3%95/计算指标变异性.png" alt="计算指标变异性" style="zoom:67%;"><p><strong>计算信息熵:</strong></p><img src="/2023/01/18/%E7%86%B5%E6%9D%83%E6%B3%95/计算信息熵.png" alt="计算信息熵" style="zoom: 67%;"><p>信息熵得到之后就可以找权重了,n是指方案个数</p><p><strong>计算信息熵冗余度:</strong></p><img src="/2023/01/18/%E7%86%B5%E6%9D%83%E6%B3%95/计算信息熵冗余度.png" alt="计算信息熵冗余度" style="zoom:67%;"><p><strong>计算权重:</strong></p><img src="/2023/01/18/%E7%86%B5%E6%9D%83%E6%B3%95/计算权重.png" alt="计算权重" style="zoom:67%;"><p><strong>进行综合评价:</strong></p><img src="/2023/01/18/%E7%86%B5%E6%9D%83%E6%B3%95/进行综合评价.png" alt="进行综合评价" style="zoom:67%;"><p><strong>例题讲解:</strong></p><img src="/2023/01/18/%E7%86%B5%E6%9D%83%E6%B3%95/例题.png" alt="例题" style="zoom:67%;"><img src="/2023/01/18/%E7%86%B5%E6%9D%83%E6%B3%95/例题计算正向指标.png" alt="例题计算正向指标" style="zoom:67%;"><img src="/2023/01/18/%E7%86%B5%E6%9D%83%E6%B3%95/算比重.png" alt="算比重" style="zoom:67%;"><p><strong>就是按列归一化</strong></p><img src="/2023/01/18/%E7%86%B5%E6%9D%83%E6%B3%95/计算信息熵和信息熵冗余度.png" alt="计算信息熵和信息熵冗余度" style="zoom:67%;"><img src="/2023/01/18/%E7%86%B5%E6%9D%83%E6%B3%95/权重和结果.png" alt="权重和结果" style="zoom:67%;"><p><strong>总结:</strong></p><p>熵权法的优点:</p><ul><li>根据各项指标指标值的变异程度来确定指标权重,避免人为因素带来的偏差;</li><li>相对于主观赋值法,精度较高、客观性更强,能够更好的解释所得到的结果。</li></ul><p>缺点:</p><ul><li>忽略了指标本身重要程度,有时确定的指标权数会和预期的结果相差甚远,所以指标之间重要性是差不多的</li><li>不能减少评价指标的维数,往往会忽视决策者主观的意图,指标数是确定的</li><li>如果指标值的变动很小或突然地变大变小,熵权法用起来有局限</li></ul><p>使用环境:</p><ul><li>指标个数最好小于对象个数,比较好</li><li>可以用于任何评价问题中的确定指标权重</li><li>可用于剔除指标体系中对评价结果贡献不大的指标</li><li>可以用于任何需要确定权重的过程,也可以结合一些方法共同使用</li></ul><h1 id="北海讲解"><a href="#北海讲解" class="headerlink" title="北海讲解"></a>北海讲解</h1><h2 id="模型简介:"><a href="#模型简介:" class="headerlink" title="模型简介:"></a>模型简介:</h2><p><img src="/2023/01/18/%E7%86%B5%E6%9D%83%E6%B3%95/%E5%8C%97%E6%B5%B7%E4%BE%8B%E9%A2%98.png" alt="北海例题"></p><p>哪门科目最容易拉开差距,哪门科目最不容易拉开差距。例如化学就不容易拉开差距,数学容易拉开差距。<img src="/2023/01/18/%E7%86%B5%E6%9D%83%E6%B3%95/%E7%86%B5%E6%9D%83%E6%B3%95%5C%E7%86%B5%E6%9D%83%E6%B3%95%E8%A7%A3%E9%87%8A.png" alt="熵权法解释"></p><p><img src="/2023/01/18/%E7%86%B5%E6%9D%83%E6%B3%95/%E7%86%B5%E6%9D%83%E6%B3%95%E9%80%82%E7%94%A8%E6%83%85%E5%86%B5.png" alt="熵权法适用情况"></p><p>这里说的数据全面,缺少文献或主观依据,就是在说没有考虑哪个指标影响大哪个指标影响小这个意思</p>]]></content>
<tags>
<tag> 数学建模 </tag>
<tag> 评价类模型 </tag>
</tags>
</entry>
<entry>
<title>层次分析法AHP</title>
<link href="/2023/01/16/AHP%E5%B1%82%E6%AC%A1%E5%88%86%E6%9E%90%E6%B3%95/"/>
<url>/2023/01/16/AHP%E5%B1%82%E6%AC%A1%E5%88%86%E6%9E%90%E6%B3%95/</url>
<content type="html"><![CDATA[<p>首先,我需要说明的是,层次分析法相对于主成分分析、因子分析、熵权法等,主观性太强了,没有客观性,在评价类模型中历史悠久,相对也简单,如果不会其他模型,拿来混是没有问题的,或者说在一个题目我用两个评价模型,其中有一个是层次分析法,也没啥问题,但是单独拿来用,对于提高自己的奖项不是很有利,所以不建议用。但是因为有学习过,所以我还是把笔记做好,供SPSSPRO之层次分析法(简化版)使用。</p><p>综合评价例题引入:</p><img src="/2023/01/16/AHP%E5%B1%82%E6%AC%A1%E5%88%86%E6%9E%90%E6%B3%95/例题引入.png" alt="例题引入" style="zoom:80%;"><p>目标是要根据评价结果进行排名,这是一个<strong>评价类</strong>的问题。</p><p><strong>目标</strong>是选出第一名的UP,所以目标层即第一名;四条可量化的评价指标,就是<strong>准则层</strong>的四个准则;<strong>方案层</strong>是五个UP,即所有可能的结果。</p><p><img src="/2023/01/16/AHP%E5%B1%82%E6%AC%A1%E5%88%86%E6%9E%90%E6%B3%95/%E6%A8%A1%E5%9E%8B%E7%BB%93%E6%9E%84.png" alt="模型结构"></p><p>我们应该从下往上看,从方案层的方案个数,通过方案之间的共性得到准则层,通过准则层求出目标层。所有连线代表所有的方案都需要进行评价给出得分。</p><p><strong>如何根据四项指标对五位UP进行综合评价?</strong></p><p><img src="/2023/01/16/AHP%E5%B1%82%E6%AC%A1%E5%88%86%E6%9E%90%E6%B3%95/%E7%AE%80%E5%8D%95%E7%B2%97%E6%9A%B4%E6%B3%95.png" alt="简单粗暴法"></p><p>归一化处理是减少数量级的,要记得是在同一个数量级进行归一化,在这里就体现为对粉丝数、播放量、获赞数、稿件数分别进行归一化,不要弄错归一化是行还是列。</p><p><img src="/2023/01/16/AHP%E5%B1%82%E6%AC%A1%E5%88%86%E6%9E%90%E6%B3%95/%E9%97%AE%E9%A2%98%E5%88%86%E6%9E%90.png" alt="问题分析"></p><p>层次分析法的缺点就是主观性太强了,需要自己设计权重,并且需要科学合理设计权重,不能<strong>有逻辑上的错误</strong>就有如下的判断矩阵,判断矩阵的目的就是为了求出权重:</p><p><img src="/2023/01/16/AHP%E5%B1%82%E6%AC%A1%E5%88%86%E6%9E%90%E6%B3%95/%E5%88%A4%E6%96%AD%E7%9F%A9%E9%98%B5%E5%BC%95%E5%85%A5.png" alt="判断矩阵引入"></p><p>其中,目标层就是评出第一,准则层有四条,即粉丝数、播放数…</p><p>例子中粉丝数与稿件数相比为5:1,那么稿件数与粉丝数相比就是1:5,所以是倒数关系。</p><p>记住这个判断矩阵是根据我们的主观想法得到的。</p><p><img src="/2023/01/16/AHP%E5%B1%82%E6%AC%A1%E5%88%86%E6%9E%90%E6%B3%95/%E7%9F%9B%E7%9B%BE%E5%A4%84.png" alt="矛盾处"></p><p>在我们填写完整判断矩阵后,会出现图片中的不一致现象,这是逻辑上的矛盾。出现矛盾的原因是我们两两比较时并不会考虑其他因素的存在,忽略了其他因素和本我们要比较的两个因素之间的逻辑关系。</p><p><img src="/2023/01/16/AHP%E5%B1%82%E6%AC%A1%E5%88%86%E6%9E%90%E6%B3%95/%E4%B8%80%E8%87%B4%E6%80%A7%E6%A3%80%E9%AA%8C%E5%BC%95%E5%85%A5.png" alt="一致性检验引入"></p><p>如果我们设立的判断矩阵矩阵值满足理想情况的式子,我们就说这个矩阵是一致矩阵,一致矩阵是一种十分理想的情况,实际中我们并不要求矛盾一定是不出现的,而是出现矛盾的次数少。</p><p><img src="/2023/01/16/AHP%E5%B1%82%E6%AC%A1%E5%88%86%E6%9E%90%E6%B3%95/CR%E7%9A%84%E5%85%B7%E4%BD%93%E6%B1%82%E6%B3%95.png" alt="CR的具体求法"></p><p>我们实际操作中判断矩阵的一致性比例CR一定要<0.1,否则我们需要手动修改判断矩阵使得CR<0.1</p><p>一致性比例代表的意思就是说,我们手动设置的矩阵和一致矩阵的差异有多大。</p><p>指标数n对应的RI是固定的,需要通过查表得到。</p><p><img src="/2023/01/16/AHP%E5%B1%82%E6%AC%A1%E5%88%86%E6%9E%90%E6%B3%95/%E4%BF%AE%E6%94%B9%E5%88%A4%E6%96%AD%E7%9F%A9%E9%98%B5.png" alt="修改判断矩阵"></p><p>CR=0.042说明此时还存在微小矛盾,但矛盾已经不影响做题了</p><p><img src="/2023/01/16/AHP%E5%B1%82%E6%AC%A1%E5%88%86%E6%9E%90%E6%B3%95/%E6%B1%82%E6%9D%83%E9%87%8D.png" alt="求权重"></p><p>我们对判断矩阵进行按列归一化而不进行按行归一化,原因是按列归一化得到的才是某一指标相对的重要性,不明白的话,记住就好。</p><p>我们使用的是算术平均法求权重得到的结果A权重最大,说明A比其他的指标更重要,在判断矩阵中也能说明。</p><p><img src="/2023/01/16/AHP%E5%B1%82%E6%AC%A1%E5%88%86%E6%9E%90%E6%B3%95/%E7%BB%93%E6%9E%9C.png" alt="结果"></p><p><img src="/2023/01/16/AHP%E5%B1%82%E6%AC%A1%E5%88%86%E6%9E%90%E6%B3%95/%E5%B1%82%E6%AC%A1%E5%88%86%E6%9E%90%E6%B3%95%E6%A8%A1%E5%9E%8B.png" alt="层次分析法模型"></p><p>读完题发现是评价类问题,就考虑能不能用层次分析法,于是确定目标层、准则层、方案层,就可以进行一致性检验…</p><p><img src="/2023/01/16/AHP%E5%B1%82%E6%AC%A1%E5%88%86%E6%9E%90%E6%B3%95/%E9%A2%98%E7%9B%AE%E4%B8%AD%E6%9B%B4%E6%99%AE%E9%81%8D%E7%9A%84%E6%83%85%E5%86%B5.png" alt="题目中更普遍的情况"></p><p>如果准则层没有客观数据怎么办?也就是没有准则的具体数据,只有权重怎么办?</p><p>我们就要按照求准则层相对于目标层的判断矩阵的方法,同样求出方案层对于准则层的判断矩阵。我们求的是四个评价指标对于第一名这个评价结果的权重。现在求例如求影响力就是五位UP对影响力,也是一样的,我们两两比较构造判断矩阵,然后求出权重向量,每个指标都有一个权重向量,所以就会有一个5*4的矩阵,同样的方法。</p><p>还有一种情况就是准则层是两层而不是单层该怎么办?等竞赛遇到了再仔细研究。</p>]]></content>
<tags>
<tag> 数学建模 </tag>
<tag> 评价类模型 </tag>
<tag> 不建议使用 </tag>
</tags>
</entry>
<entry>
<title>因子分析</title>
<link href="/2023/01/16/%E5%9B%A0%E5%AD%90%E5%88%86%E6%9E%90/"/>
<url>/2023/01/16/%E5%9B%A0%E5%AD%90%E5%88%86%E6%9E%90/</url>
<content type="html"><![CDATA[<p><strong>公共因子:</strong></p><img src="/2023/01/16/%E5%9B%A0%E5%AD%90%E5%88%86%E6%9E%90/公共因子.png" alt="公共因子" style="zoom:67%;"><p>公共因子其实就是用<strong>一个共同的因素</strong>来刻画几个<strong>高度相关的变量</strong>,这些公共因子通常是无法观测的,是某一些可以观测的变量背后的一些公共因素,故称为潜变量。</p><p><img src="/2023/01/16/%E5%9B%A0%E5%AD%90%E5%88%86%E6%9E%90/因子分析目的.png" alt="因子分析目的" style="zoom:67%;">m是公共因子的个数,p是原始的变量个数,m通常小于p,这样可以达到降维的目的。它是通过一些公共因素去刻画原始变量的相关性的,跟主成分分析不一样,主成分分析的出发点在方差上面,而不是相关性上面。</p><p><img src="/2023/01/16/%E5%9B%A0%E5%AD%90%E5%88%86%E6%9E%90/%E4%B8%80%E4%BA%9B%E6%A6%82%E5%BF%B5.png" alt="一些概念"></p><p>特殊因子是公共因子搞不定的部分,是一些特殊的部分。系数决定了原始变量是怎么由公共因子决定的。</p><p>大多数的时候需要多个公共因子刻画,这就是正交因子模型。</p><img src="/2023/01/16/%E5%9B%A0%E5%AD%90%E5%88%86%E6%9E%90/正交因子模型.png" alt="正交因子模型" style="zoom:67%;"><p>减去均值是中心化过程。</p><p>我们假设公共因子矩阵的均值为0,协方差矩阵是一个单位阵,说明公共因子间具有正交性,也就是不相关,如果两个公共因子之间具有相关性,就可以把他们合并成为一个公共因子;假设特殊因子的均值为0,协方差矩阵是一个对角阵,所有特殊因子也应该是彼此独立的,也是因为这样才不会将它们合并为一个特殊因子。</p><img src="/2023/01/16/%E5%9B%A0%E5%AD%90%E5%88%86%E6%9E%90/假设.png" alt="假设" style="zoom:67%;"><img src="/2023/01/16/%E5%9B%A0%E5%AD%90%E5%88%86%E6%9E%90/载荷矩阵的理解.png" alt="载荷矩阵的理解" style="zoom:67%;"><p>就是说载荷就是协方差值。载荷矩阵刻画了公共因子到底怎么去解释原始变量的。</p><img src="/2023/01/16/%E5%9B%A0%E5%AD%90%E5%88%86%E6%9E%90/协方差矩阵的具体分解.png" alt="协方差矩阵的具体分解" style="zoom:67%;"><p><strong>载荷估计方法:</strong></p><p>主成分法:</p><img src="/2023/01/16/%E5%9B%A0%E5%AD%90%E5%88%86%E6%9E%90/载荷矩阵估计.png" alt="载荷矩阵估计" style="zoom:67%;"><p>主成分法的思想:使用特征值特征向量的思想,用谱分解的前m列代替L</p><img src="/2023/01/16/%E5%9B%A0%E5%AD%90%E5%88%86%E6%9E%90/说明.png" alt="说明" style="zoom:67%;"><img src="/2023/01/16/%E5%9B%A0%E5%AD%90%E5%88%86%E6%9E%90/主成分法.png" alt="主成分法" style="zoom:67%;"><p>上面的东西学着学着懵了,就开始换视频学习。</p><h1 id="山东财经大学讲解"><a href="#山东财经大学讲解" class="headerlink" title="山东财经大学讲解"></a>山东财经大学讲解</h1><h2 id="因子分析概述"><a href="#因子分析概述" class="headerlink" title="因子分析概述"></a>因子分析概述</h2><ul><li>消减变量个数会导致信息丢失和信息不完整等问题的产生,我们要在降维的同时尽可能多的保存信息。</li></ul><p>因子分析可以解决上面的问题,它以最少的信息丢失,将原始众多变量综合成较少的几个综合指标(因子),能够起到有效降维的目的。它是主成分分析的一种扩展和延申</p><p>因子分析的特点:</p><ul><li>因子个数远远少于原有变量的个数</li><li>因子能够反映原有变量的绝大部分信息</li><li>因子之间不存在线性关系</li><li>因子具有命名解释性</li></ul><h2 id="因子分析的数学模型和相关概念"><a href="#因子分析的数学模型和相关概念" class="headerlink" title="因子分析的数学模型和相关概念"></a>因子分析的数学模型和相关概念</h2><img src="/2023/01/16/%E5%9B%A0%E5%AD%90%E5%88%86%E6%9E%90/模型.png" alt="模型" style="zoom:67%;"><ul><li><p>这里和主成分分析的差异就体现出来了,主成分分析是把原来的多个变量合成,而这里因子分析是吧原来的变量分解成新的变量。</p></li><li><p>而每个变量的均值为0,我们就要对变量进行<strong>去中心化、标准化处理</strong>。</p></li><li><p>因子分析最终也就是要找到f1…fk</p></li><li><p>数学模型矩阵形式中的F称为<strong>公共因子</strong>,因为它出现在每个变量的线性表达式中,简称<strong>因子</strong>。因子可以理解为高维度空间中相互垂直的k个坐标轴;A称为<strong>因子载荷矩阵</strong>,aij称为<strong>因子载荷</strong>,是第i个原始变量在第j个因子上的负荷;e称为<strong>特殊因子</strong>,表示原始变量不能被因子解释的部分,其<strong>均值为0</strong>,相当于多元线性回归模型中的残差,也就是<strong>信息损失</strong>。</p></li><li><p>因子载荷:在因子不相关的前提下,因子载荷是第i个变量与第j个因子的<strong>相关系数,取值在-1到1之间</strong>。因子载荷越接近1,说明<strong>因子与变量的相关性</strong>越强;因子载荷的平方说明了<strong>因子对变量的重要性和程度</strong>,越接近于1越重要。</p></li><li><p>变量共同度:变量共同度也称为公共方差,第i个变量的共同都定义为因子负载矩阵中第i行元素的平方和,体现了<strong>因子全体对变量Xi的解释贡献程度,是评价Xi信息丢失程度的重要指标</strong>。也就是说因子全体能够保留多少数据。即<img src="/2023/01/16/%E5%9B%A0%E5%AD%90%E5%88%86%E6%9E%90/变量共同度.png" alt="变量共同度" style="zoom:67%;"></p></li><li><p>因子的方差贡献:因子方差贡献是因子载荷矩阵中第j列元素的平方和,反映了第j个因子对原有变量总方差的解释能力。该数值越高,说明相应因子的重要性越高。<img src="/2023/01/16/%E5%9B%A0%E5%AD%90%E5%88%86%E6%9E%90/因子方差贡献度.png" alt="因子方差贡献度" style="zoom:50%;"></p></li></ul><h2 id="因子分析的基本内容"><a href="#因子分析的基本内容" class="headerlink" title="因子分析的基本内容"></a>因子分析的基本内容</h2><p>因子分析的基本步骤:</p><ul><li>因子分析的前提条件;检验是否适合做因子分析</li><li>因子提取;</li><li>使因子更具有命名可解释性;怎么做使因子有更好的实际意义</li><li>计算各样本的因子得分。</li></ul><h3 id="因子分析的前提条件"><a href="#因子分析的前提条件" class="headerlink" title="因子分析的前提条件"></a>因子分析的前提条件</h3><p><strong>原始变量之间应存在较强的相关关系</strong>。</p><p>计算相关系数矩阵并进行统计检验。如果相关系数矩阵中的大部分相关系数小于0.3,那么这些变量不适合进行因子分析。</p><p>或者计算反映象相关矩阵:<img src="/2023/01/16/%E5%9B%A0%E5%AD%90%E5%88%86%E6%9E%90/反映象相关矩阵.png" alt="反映象相关矩阵" style="zoom: 50%;"></p><p><strong>最常用的</strong>就是Bartlett’s球度检验以及KMO检验</p><img src="/2023/01/16/%E5%9B%A0%E5%AD%90%E5%88%86%E6%9E%90/KMO和Bartlett's.png" alt="KMO和Bartlett's" style="zoom:67%;"><p>Bartlett’s 原假设是变量之间不相关,拒绝原假设的话就是说变量之间有较强的相关性。</p><p>如果不适合就换方法。</p><h3 id="因子提取和因子载荷矩阵的求解"><a href="#因子提取和因子载荷矩阵的求解" class="headerlink" title="因子提取和因子载荷矩阵的求解"></a>因子提取和因子载荷矩阵的求解</h3><img src="/2023/01/16/%E5%9B%A0%E5%AD%90%E5%88%86%E6%9E%90/因子提取和因子载荷矩阵的求解.png" alt="因子提取和因子载荷矩阵的求解" style="zoom:67%;"><img src="/2023/01/16/%E5%9B%A0%E5%AD%90%E5%88%86%E6%9E%90/主成分的要求.png" alt="主成分的要求" style="zoom:67%;"><img src="/2023/01/16/%E5%9B%A0%E5%AD%90%E5%88%86%E6%9E%90/主成分分析.png" alt="主成分分析" style="zoom:67%;"><img src="/2023/01/16/%E5%9B%A0%E5%AD%90%E5%88%86%E6%9E%90/载荷矩阵计算.png" alt="载荷矩阵计算" style="zoom:67%;"><img src="/2023/01/16/%E5%9B%A0%E5%AD%90%E5%88%86%E6%9E%90/选取因子.png" alt="选取因子" style="zoom:67%;"><img src="/2023/01/16/%E5%9B%A0%E5%AD%90%E5%88%86%E6%9E%90/确定因子个数.png" alt="确定因子个数" style="zoom:67%;"><img src="/2023/01/16/%E5%9B%A0%E5%AD%90%E5%88%86%E6%9E%90/累计方差贡献率求法.png" alt="累计方差贡献率求法" style="zoom:67%;"><h2 id="因子命名"><a href="#因子命名" class="headerlink" title="因子命名"></a>因子命名</h2><img src="/2023/01/16/%E5%9B%A0%E5%AD%90%E5%88%86%E6%9E%90/因子命名.png" alt="因子命名" style="zoom:67%;"><img src="/2023/01/16/%E5%9B%A0%E5%AD%90%E5%88%86%E6%9E%90/命名解释.png" alt="命名解释" style="zoom:67%;"><p>左图的因子无法明确代表某一类点,右图因子可以明确代表某一类点的共同之处</p><img src="/2023/01/16/%E5%9B%A0%E5%AD%90%E5%88%86%E6%9E%90/计算因子得分.png" alt="计算因子得分" style="zoom:67%;">]]></content>
<tags>
<tag> 数学建模 </tag>
<tag> 评价类模型 </tag>
</tags>
</entry>
<entry>
<title>深度学习入门</title>
<link href="/2023/01/14/%E6%B7%B1%E5%BA%A6%E5%AD%A6%E4%B9%A0%E5%85%A5%E9%97%A8/"/>
<url>/2023/01/14/%E6%B7%B1%E5%BA%A6%E5%AD%A6%E4%B9%A0%E5%85%A5%E9%97%A8/</url>
<content type="html"><![CDATA[<h1 id="python一些入门"><a href="#python一些入门" class="headerlink" title="python一些入门"></a>python一些入门</h1><h2 id="Numpy"><a href="#Numpy" class="headerlink" title="Numpy"></a>Numpy</h2><p>导入:<code>import numpy as np</code>,将numpy作为np导入</p><p>生成Numpy数组(numpy.ndarray):使用<code>np.array(ython)</code>方法。<code>np.array()</code>以列表为参数。它<strong>和一维数组是不同的</strong>。</p><figure class="highlight python"><table><tr><td class="code"><pre><span class="line">>>>x = np.array([<span class="number">1.0</span>,<span class="number">2.0</span>,<span class="number">3.0</span>])</span><br><span class="line">>>><span class="built_in">print</span>(x)</span><br><span class="line">[ <span class="number">1.</span> <span class="number">2.</span> <span class="number">3.</span>]</span><br><span class="line"><span class="meta">>>> </span><span class="built_in">type</span>(x)</span><br><span class="line"><<span class="keyword">class</span> <span class="string">'numpy.ndarray'</span>></span><br></pre></td></tr></table></figure><p>Numpy数组的算术运算:对应位置的运算,数组元素个数要相同。</p><p>Numpy生成二维数组:</p><figure class="highlight python"><table><tr><td class="code"><pre><span class="line"><span class="meta">>>> </span>A = np.array([[<span class="number">1</span>, <span class="number">2</span>], [<span class="number">3</span>, <span class="number">4</span>]])</span><br><span class="line"><span class="meta">>>> </span><span class="built_in">print</span>(A)</span><br><span class="line">[[<span class="number">1</span> <span class="number">2</span>]</span><br><span class="line">[<span class="number">3</span> <span class="number">4</span>]]</span><br><span class="line"><span class="meta">>>> </span>A.shape <span class="comment"># 查看矩阵A的形状</span></span><br><span class="line">(<span class="number">2</span>, <span class="number">2</span>)</span><br><span class="line"><span class="meta">>>> </span>A.dtype <span class="comment"># 查看矩阵元素的类型</span></span><br><span class="line">dtype(<span class="string">'int64'</span>)</span><br></pre></td></tr></table></figure><p>数学上将一维数组称为向量,将二维数组称为矩阵。可以将一般化之后的向量或矩阵等统称为张量(tensor)。本书基本上将二维数组称为“矩阵”,将三维数组及三维以上的数组称为“张量”或“多维数组”。</p><p>NumPy 有广播功能,所以<strong>不同形状的数组</strong>之间也可以顺利地进行运算。</p><figure class="highlight python"><table><tr><td class="code"><pre><span class="line"><span class="meta">>>> </span>A = np.array([[<span class="number">1</span>, <span class="number">2</span>], [<span class="number">3</span>, <span class="number">4</span>]])</span><br><span class="line"><span class="meta">>>> </span>B = np.array([<span class="number">10</span>, <span class="number">20</span>])</span><br><span class="line"><span class="meta">>>> </span>A * B</span><br><span class="line">array([[ <span class="number">10</span>, <span class="number">40</span>],</span><br><span class="line"> [ <span class="number">30</span>, <span class="number">80</span>]])</span><br></pre></td></tr></table></figure><p><code>flatten()</code>函数,可以将Numpy数组转化为一维数组</p><figure class="highlight python"><table><tr><td class="code"><pre><span class="line"><span class="meta">>>> </span>X = np.array([[<span class="number">51</span>, <span class="number">55</span>], [<span class="number">14</span>, <span class="number">19</span>], [<span class="number">0</span>, <span class="number">4</span>]])</span><br><span class="line"><span class="meta">>>> </span>X = X.flatten() <span class="comment"># 将 X 转换为一维数组</span></span><br><span class="line"><span class="meta">>>> </span><span class="built_in">print</span>(X)</span><br><span class="line">[<span class="number">51</span> <span class="number">55</span> <span class="number">14</span> <span class="number">19</span> <span class="number">0</span> <span class="number">4</span>]</span><br><span class="line"><span class="meta">>>> </span>X[np.array([<span class="number">0</span>, <span class="number">2</span>, <span class="number">4</span>])] <span class="comment"># 获取索引为 0、2、4 的元素</span></span><br><span class="line">array([<span class="number">51</span>, <span class="number">14</span>, <span class="number">0</span>])</span><br></pre></td></tr></table></figure><p>运用这个标记法,可以获取满足一定条件的元素。例如,要从 X 中抽出大于 15 的元素,可以写成如下形式:</p><figure class="highlight python"><table><tr><td class="code"><pre><span class="line"><span class="meta">>>> </span>X > <span class="number">15</span></span><br><span class="line">array([ <span class="literal">True</span>, <span class="literal">True</span>, <span class="literal">False</span>, <span class="literal">True</span>, <span class="literal">False</span>, <span class="literal">False</span>], dtype=<span class="built_in">bool</span>)</span><br><span class="line"><span class="meta">>>> </span>X[X><span class="number">15</span>]</span><br><span class="line">array([<span class="number">51</span>, <span class="number">55</span>, <span class="number">19</span>])</span><br></pre></td></tr></table></figure><p>对 NumPy 数组使用不等号运算符等(上例中是 X > 15), 结果会得到一个布尔型的数组。上例中就是使用这个布尔型数组取出了数组的各个元素(取出 True 对应的元素)。</p><h2 id="Matplotlib"><a href="#Matplotlib" class="headerlink" title="Matplotlib"></a>Matplotlib</h2><p>用于绘制图形和实现数据的可视化。可以使用 matplotlib 的 pyplot 模块绘制图形。</p><p>例如:</p><figure class="highlight python"><table><tr><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">import</span> matplotlib.pyplot <span class="keyword">as</span> plt</span><br><span class="line"><span class="comment"># 生成数据</span></span><br><span class="line">x = np.arange(<span class="number">0</span>, <span class="number">6</span>, <span class="number">0.1</span>) <span class="comment"># 以 0.1 为单位,生成 0 到 6 的数据</span></span><br><span class="line">y = np.sin(x)</span><br><span class="line"><span class="comment"># 绘制图形</span></span><br><span class="line">plt.plot(x, y)</span><br><span class="line">plt.show()</span><br></pre></td></tr></table></figure><p>这里使用 NumPy 的 arange 方法生成了 [0, 0.1, 0.2, …, 5.8, 5.9]的数据,将其设为 x。对 x 的各个元素,应用 NumPy 的 sin 函数 np.sin(),将 x、y 的数据传给 plt.plot 方法,然后绘制图形。最后,通过 plt.show() 显示图形。显示结果如下</p><img src="/2023/01/14/%E6%B7%B1%E5%BA%A6%E5%AD%A6%E4%B9%A0%E5%85%A5%E9%97%A8/Figure_1.png" alt="Figure_1" style="zoom:67%;"><p><strong>pyplot的功能:</strong></p><p>在刚才的 sin 函数的图形中,我们尝试追加 cos 函数的图形,并尝试使用<br>pyplot 的添加标题和 x 轴标签名等其他功能。</p><figure class="highlight python"><table><tr><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">import</span> matplotlib.pyplot <span class="keyword">as</span> plt</span><br><span class="line"><span class="comment"># 生成数据</span></span><br><span class="line">x = np.arange(<span class="number">0</span>, <span class="number">6</span>, <span class="number">0.1</span>) <span class="comment"># 以 0.1 为单位,生成 0 到 6 的数据</span></span><br><span class="line">y1 = np.sin(x)</span><br><span class="line">y2 = np.cos(x)</span><br><span class="line"><span class="comment"># 绘制图形</span></span><br><span class="line">plt.plot(x, y1, label=<span class="string">"sin"</span>)</span><br><span class="line">plt.plot(x, y2, linestyle = <span class="string">"--"</span>, label=<span class="string">"cos"</span>) <span class="comment"># 用虚线绘制</span></span><br><span class="line">plt.xlabel(<span class="string">"x"</span>) <span class="comment"># x 轴标签</span></span><br><span class="line">plt.ylabel(<span class="string">"y"</span>) <span class="comment"># y 轴标签</span></span><br><span class="line">plt.title(<span class="string">'sin & cos'</span>) <span class="comment"># 标题</span></span><br><span class="line">plt.legend() <span class="comment"># 图例</span></span><br><span class="line">plt.show() <span class="comment">#显示</span></span><br></pre></td></tr></table></figure><p>结果如下:</p><img src="/2023/01/14/%E6%B7%B1%E5%BA%A6%E5%AD%A6%E4%B9%A0%E5%85%A5%E9%97%A8/Figure_2.png" alt="Figure_2" style="zoom:72%;"><p><strong>显示图像:</strong></p><p>pyplot中还提供了用于显示图像的方法<code>imshow()</code>,另外可以使用matplotlib.image模块的<code>imread()</code>方法读入图像。</p><figure class="highlight python"><table><tr><td class="code"><pre><span class="line"><span class="keyword">import</span> matplotlib.pyplot <span class="keyword">as</span> plt</span><br><span class="line"><span class="keyword">from</span> matplotlib.image <span class="keyword">import</span> imread</span><br><span class="line"></span><br><span class="line">img = imread(<span class="string">'./img/dog.jpg'</span>) <span class="comment"># 读入图像(设定合适的路径!)</span></span><br><span class="line">plt.imshow(img)</span><br><span class="line">plt.show()</span><br></pre></td></tr></table></figure><p>结果如下:<img src="/2023/01/14/%E6%B7%B1%E5%BA%A6%E5%AD%A6%E4%B9%A0%E5%85%A5%E9%97%A8/Figure_3.png" alt="Figure_3"></p><h1 id="感知机"><a href="#感知机" class="headerlink" title="感知机"></a>感知机</h1><h2 id="感知机是什么?"><a href="#感知机是什么?" class="headerlink" title="感知机是什么?"></a>感知机是什么?</h2><p>感知机<strong>接收多个输入信号,输出一个信号</strong>。</p><p>像电流流过导线,向前方输送电子一样,感知机的信号也会形成流,<strong>向前方输送信息</strong>。</p><p>感知机的信号只有“流/不流”(1/0)两种取值。在本书中,0对应“不传递信号”,1对应“传递信号”。<img src="/2023/01/14/%E6%B7%B1%E5%BA%A6%E5%AD%A6%E4%B9%A0%E5%85%A5%E9%97%A8/Figure_4.png" alt="Figure_4"></p><p><img src="/2023/01/14/%E6%B7%B1%E5%BA%A6%E5%AD%A6%E4%B9%A0%E5%85%A5%E9%97%A8/%E6%84%9F%E7%9F%A5%E6%9C%BA%E8%BF%90%E8%A1%8C%E5%8E%9F%E7%90%86%E7%9A%84%E6%95%B0%E5%AD%A6%E5%85%AC%E5%BC%8F.png" alt="感知机运行原理的数学公式"></p><p>上图是一个接收<strong>两个输入信号</strong>的感知机的例子。<strong>x1、x2是输入信号</strong>,<strong>y是输出信号</strong>,<strong>w1、w2是权重</strong>(w是weight的首字母)。图中的<strong>⚪称为“神经元”或者“节点”</strong>。输入信号被送往神经元时,会被分别乘以固定的权重(w1x1、w2x2)。神经元会<strong>计算传送过来的信号的总和</strong>,只有当这个总和超过了<strong>某个界限值时</strong>,才会输出1。这也称为“<strong>神经元被激活</strong>”。这里将这个界限值称为<strong>阈值</strong>,用符号θ表示。感知机的多个输入信号都有各自固有的权重,这些权重发挥着控制各个信号的重要性的作用。也就是说,权重越大,对应该权重的信号的重要性就越高。</p><h2 id="简单逻辑电路"><a href="#简单逻辑电路" class="headerlink" title="简单逻辑电路"></a>简单逻辑电路</h2><p>机器学习的课题就是将这个决定参数值的工作交由计算机自动进行。学习是确定合适的参数的过程,而人要做的是思考感知机的构造模型,并把训练数据交给计算机。</p><h3 id="与门"><a href="#与门" class="headerlink" title="与门"></a>与门</h3><p>与门仅在两个输入均为1时输出1,其他时候输出0。</p><p>例如,当 (w1, w2, θ) = (0.5, 0.5, 0.7) 时,可以满足感知机的条件,设定这样的参数后,仅当x1和x2同时为1时,信号的加权总和才会超过给定的阈值θ。</p><h3 id="与非门和或门"><a href="#与非门和或门" class="headerlink" title="与非门和或门"></a>与非门和或门</h3><h4 id="与非门"><a href="#与非门" class="headerlink" title="与非门"></a>与非门</h4><p>与非门仅当x1和x2同时为1时输出0,其他时候则输出1。要表示与非门,可以用(w1, w2, θ) = (−0.5, −0.5, −0.7)这样的组合,实际上,只要把实现与门的参数值的符号取反,就可以实现与非门。</p><h4 id="或门"><a href="#或门" class="headerlink" title="或门"></a>或门</h4><p>或门是“只要有一个输入信号是1,输出就为1”的逻辑电路。</p><p>与门、与非门、或门的感知机构造是一样的。实际上,3个门电路只有参数的值(权重和阈值)不同。也就是说,相同构造的感知机,只需通过适当地调整参数的值,就可以像“变色龙演员”表演不同的角色一样,变身为与门、与非门、或门。</p><h2 id="感知机实现"><a href="#感知机实现" class="headerlink" title="感知机实现"></a>感知机实现</h2><h3 id="导入权重和偏置"><a href="#导入权重和偏置" class="headerlink" title="导入权重和偏置"></a>导入权重和偏置</h3><p>首先把上式的θ换成−b,于 是就可以用下式来表示感知机的行为,我们将感知机运行原理以下图重新描绘:</p><img src="/2023/01/14/%E6%B7%B1%E5%BA%A6%E5%AD%A6%E4%B9%A0%E5%85%A5%E9%97%A8/感知机运行数学原理.png" alt="感知机运行数学原理" style="zoom:67%;"><p>两式虽然有一个符号不同,但表达内容相同。此处,<strong>b称为偏置</strong>,<strong>w1和w2称为权重</strong>。</p><p>感知机会<strong>计算输入信号和权重的乘积</strong>,然后<strong>加上偏置</strong>,如果这个值<strong>大于0则输出1</strong>,<strong>否则输出0</strong>。</p><p>实现感知机:</p><figure class="highlight python"><table><tr><td class="code"><pre><span class="line"><span class="meta">>>> </span><span class="keyword">import</span> numpy <span class="keyword">as</span> np</span><br><span class="line"><span class="meta">>>> </span>x = np.array([<span class="number">0</span>, <span class="number">1</span>]) <span class="comment"># 输入</span></span><br><span class="line"><span class="meta">>>> </span>w = np.array([<span class="number">0.5</span>, <span class="number">0.5</span>]) <span class="comment"># 权重</span></span><br><span class="line"><span class="meta">>>> </span>b = -<span class="number">0.7</span> <span class="comment"># 偏置</span></span><br><span class="line"><span class="meta">>>> </span>w*x</span><br><span class="line">array([ <span class="number">0.</span> , <span class="number">0.5</span>])</span><br><span class="line"><span class="meta">>>> </span>np.<span class="built_in">sum</span>(w*x)</span><br><span class="line"><span class="number">0.5</span></span><br><span class="line"><span class="meta">>>> </span>np.<span class="built_in">sum</span>(w*x) + b</span><br><span class="line">-<span class="number">0.19999999999999996</span> <span class="comment"># 大约为-0.2(由浮点小数造成的运算误差)</span></span><br></pre></td></tr></table></figure><p>在NumPy数组的乘法运算中,当两个数组的元素个数相同时,各个元素分别相乘,因此w<em>x的结果就是它们的各个元素分别相乘([0, 1] * [0.5, 0.5] => [0, 0.5])。之后,np.sum(w</em>x)再计算相乘后的各个元素的总和。最后再把偏置加到这个加权总和上。</p><h3 id="使用权重和偏置的实现"><a href="#使用权重和偏置的实现" class="headerlink" title="使用权重和偏置的实现"></a>使用权重和偏置的实现</h3><p>实现与门如下:</p><figure class="highlight python"><table><tr><td class="code"><pre><span class="line"><span class="keyword">def</span> <span class="title function_">AND</span>(<span class="params">x1, x2</span>):</span><br><span class="line"> x = np.array([x1, x2])</span><br><span class="line"> w = np.array([<span class="number">0.5</span>, <span class="number">0.5</span>])</span><br><span class="line"> b = -<span class="number">0.7</span></span><br><span class="line"> tmp = np.<span class="built_in">sum</span>(w*x) + b</span><br><span class="line"> <span class="keyword">if</span> tmp <= <span class="number">0</span>:</span><br><span class="line"> <span class="keyword">return</span> <span class="number">0</span></span><br><span class="line"> <span class="keyword">else</span>:</span><br><span class="line"> <span class="keyword">return</span> <span class="number">1</span></span><br></pre></td></tr></table></figure><p>把−θ命名为偏置b,但是偏置和权重w1、w2的作用是不 一样的。具体地说,<strong>w1和w2是控制输入信号的重要性</strong>的参数,而<strong>偏置是调整神经元被激活的容易程度</strong>(输出信号为1的程度)的参数。</p><p>若b为 −0.1,则只要输入信号的加权总和超过0.1,神经元就会被激活。但是如果b为−20.0,则输入信号的加权总和必须超过20.0,神经元才会被激活。</p><h3 id="感知机的局限性"><a href="#感知机的局限性" class="headerlink" title="感知机的局限性"></a>感知机的局限性</h3><h4 id="异或门"><a href="#异或门" class="headerlink" title="异或门"></a>异或门</h4><p>仅当x1或x2中的一方为 1时,才会输出1。用感知机是无法实现这个异或门的</p><h4 id="线性与非线性"><a href="#线性与非线性" class="headerlink" title="线性与非线性"></a>线性与非线性</h4><p>感知机的局限性就在于它只能表示由一条直线分割的空间,弯曲的曲线无法用感知机表示。由曲线分割而成的空间称为<strong>非线性空间</strong>,由直线分割而成的空间称为<strong>线性空间</strong>。</p><h2 id="多层感知机"><a href="#多层感知机" class="headerlink" title="多层感知机"></a>多层感知机</h2><p>感知机的绝妙之处在于它可以“叠加层”(通过叠加层来表示异或门是本节的要点)。</p><h3 id="已有门电路的组合"><a href="#已有门电路的组合" class="headerlink" title="已有门电路的组合"></a>已有门电路的组合</h3><p>严格地讲,应该是“单层感知机无法表示异或门”或者“单层感知机无法分离非线性空间”。</p><p>异或门是一种多层结构的神经网络。</p><h1 id="神经网络"><a href="#神经网络" class="headerlink" title="神经网络"></a>神经网络</h1><p>神经网络的一个重要性质是它可以自动地从数据中学习到合适的权重参数。</p><h2 id="从感知机到神经网络"><a href="#从感知机到神经网络" class="headerlink" title="从感知机到神经网络"></a>从感知机到神经网络</h2><h3 id="神经网络的例子"><a href="#神经网络的例子" class="headerlink" title="神经网络的例子"></a>神经网络的例子</h3><img src="/2023/01/14/%E6%B7%B1%E5%BA%A6%E5%AD%A6%E4%B9%A0%E5%85%A5%E9%97%A8/神经网络例子.png" alt="神经网络例子" style="zoom: 50%;"><p>把最左边的一列称为<strong>输入层</strong>,最右边的一列称为<strong>输出层</strong>,中间的一列称为<strong>中间层</strong>。中间层有时也称为<strong>隐藏层</strong>,“隐藏”一词的意思是,隐藏层的神经元(和输入层、输出层不同)肉眼看不见。本书中把输入层到输出层依次称为第0层、第1层、第2层,第0层对应输入层,第1层对应中间层,第2层对应输出层。</p><h3 id="激活函数"><a href="#激活函数" class="headerlink" title="激活函数"></a>激活函数</h3><p>一个函数将输入信号的综合转换为输出信号,这种函数一般称作<strong>激活函数</strong>。如激活一词所示,激活函数的作用在于决定如何来激活输入信号的总和。</p><p>“多层感知机”是指神经网络,即使用 sigmoid 函数等平滑的激活函数的多层网络。</p><h2 id="激活函数-1"><a href="#激活函数-1" class="headerlink" title="激活函数"></a>激活函数</h2><p>前面的h(x)表示的激活函数以阈值为界,一旦输入超过阈值,就切换输出,这样的函数称为“<strong>阶跃函数</strong>”。因此,可以说感知机中使用了阶跃函数作为激活函数。</p><p>如果将激活函数从阶跃函数换成其他函数,就可以进入神经网络的世界了。</p><h3 id="sigmoid函数"><a href="#sigmoid函数" class="headerlink" title="sigmoid函数"></a>sigmoid函数</h3><img src="/2023/01/14/%E6%B7%B1%E5%BA%A6%E5%AD%A6%E4%B9%A0%E5%85%A5%E9%97%A8/sigmoid函数.png" alt="sigmoid函数" style="zoom:67%;"><p>神经网络中用sigmoid函数作为激活函数,进行信号的转换,转换后的信号被传送给下一个神经元。</p><p>感知机和神经网络的主要区别就在于这个激活函数。</p><p>机器学习的问题大致可以分为分类问题和回归问题,一般而言,回归预测问题用恒等函数,多元分类问题用softmax函数,二元分类问题使用sigmoid函数。</p>]]></content>
<tags>
<tag> 深度学习 </tag>
<tag> python </tag>
</tags>
</entry>
<entry>
<title>PyTorch</title>
<link href="/2023/01/09/pytorch/"/>
<url>/2023/01/09/pytorch/</url>
<content type="html"><![CDATA[<p>创建新项目的时候选择的编译器要选择Conda环境,如果手选Conda环境,要在Anaconda3的envs这个文件里选pytorch里面的python.exe就可以了。</p><h1 id="两个重要函数"><a href="#两个重要函数" class="headerlink" title="两个重要函数"></a>两个重要函数</h1><p>pytorch是一个package工具箱</p><ul><li><code>dir()</code>:打开,看见里面有什么东西。当使用dir()得到的参数是双下划线的时候,说明这些标识符是函数。</li><li><code>help()</code>:说明书,还有一种形式例如<code>Dataset??</code><ul><li>例如查看<code>is_available()</code>函数,我们就运行<code>help(torch.cuda.is_available)</code>,括号里只需要输入标识符即可</li></ul></li></ul><h1 id="加载数据"><a href="#加载数据" class="headerlink" title="加载数据"></a>加载数据</h1><p>如何读取数据涉及到两个类</p><ul><li>Dataset:提供一种方式去获取数据及其label。<ul><li>Dataset是一个抽象类,所有数据集都要继承它,所有子类都要实现<code>getitem</code>方法,选择性重写<code>len</code>方法。</li><li>如何获取每一个数据及其label?实现<code>getitem</code>方法。</li><li>告诉我们总共有多少的数据?重写<code>len</code>方法</li></ul></li><li>Dataloader:对我们要送进网络的数据进行打包,为网络提供不同的数据形式</li></ul><p><strong>import os</strong>:</p><ul><li><p><code>os.path.join(str1,str2...)</code>函数的参数可以放字符串,得到的字符串结果是把多个字符串用<code>\\</code>连接,因为单独的\会被当成转义字符,/并不是转义字符,例如:</p><figure class="highlight python"><table><tr><td class="code"><pre><span class="line">root_dir=<span class="string">"dataset/train"</span>label_dir=<span class="string">"ants"</span></span><br><span class="line">path=os.path.join(root_dir,label_dir)</span><br><span class="line">>>>path={<span class="built_in">str</span>}<span class="string">'dataset/train\\ants'</span></span><br></pre></td></tr></table></figure></li><li><p><code>os.listdir(path)</code>函数可以将路径path中的文件转化成列表形式,例如:</p><figure class="highlight python"><table><tr><td class="code"><pre><span class="line">img_path_list=os.listdir(path)</span><br><span class="line">>>>img_path_list={<span class="built_in">list</span>:<span class="number">124</span>}[...]</span><br></pre></td></tr></table></figure></li></ul><p><strong>from PIL import Image</strong>:</p><ul><li><code>Image.open(img_path)</code>此函数用于创建图片,返回<code>{JpegImageFile}</code>变量</li><li><code>img.show()</code>函数用于显示图片</li></ul><p>对目前所学的一个例子:</p><figure class="highlight python"><table><tr><td class="code"><pre><span class="line"><span class="keyword">from</span> torch.utils.data <span class="keyword">import</span> Dataset</span><br><span class="line"><span class="comment"># 从常用工具区的关于数据的 data 区 import Dataset</span></span><br><span class="line"><span class="keyword">from</span> PIL <span class="keyword">import</span> Image <span class="comment">#读取图片</span></span><br><span class="line"><span class="keyword">import</span> os <span class="comment">#operating system 关于系统的库</span></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">MyData</span>(<span class="title class_ inherited__">Dataset</span>):<span class="comment">#继承Dataset类</span></span><br><span class="line"> <span class="keyword">def</span> <span class="title function_">__init__</span>(<span class="params">self,root_dir,label_dir</span>):</span><br><span class="line"> self.root_dir=root_dir <span class="comment">#self相当于在类中创建了全局变量</span></span><br><span class="line"> self.label_dir=label_dir</span><br><span class="line"> self.path = os.path.join(self.root_dir, self.label_dir)</span><br><span class="line"> self.img_path = os.listdir(self.path)</span><br><span class="line"></span><br><span class="line"> <span class="keyword">def</span> <span class="title function_">__getitem__</span>(<span class="params">self, idx</span>):</span><br><span class="line"> img_name = self.img_path[idx]<span class="comment">#注意要加self</span></span><br><span class="line"> img_item_path = os.path.join(self.path,img_name)</span><br><span class="line"> img=Image.<span class="built_in">open</span>(img_item_path)</span><br><span class="line"> label = self.label_dir <span class="comment">#相对于本个例子而言的label</span></span><br><span class="line"> <span class="keyword">return</span> img, label</span><br><span class="line"> <span class="comment">#这里重写了getitem,返回img,label,就达到了我们利用下标获取每一个数据及其label的目的</span></span><br><span class="line"></span><br><span class="line"> <span class="keyword">def</span> <span class="title function_">__len__</span>(<span class="params">self</span>):</span><br><span class="line"> <span class="keyword">return</span> <span class="built_in">len</span>(self.img_path)<span class="comment">#返回列表长度</span></span><br><span class="line"></span><br><span class="line"></span><br><span class="line">root_dir = <span class="string">"dataset/train"</span></span><br><span class="line"></span><br><span class="line">ants_label_dir = <span class="string">"ants"</span></span><br><span class="line">ants_dataset = MyData(root_dir, ants_label_dir)</span><br><span class="line"></span><br><span class="line">bees_label_dir = <span class="string">"bees"</span></span><br><span class="line">bees_dataset = MyData(root_dir, bees_label_dir)</span><br><span class="line"></span><br><span class="line"><span class="built_in">print</span>(ants_dataset[<span class="number">0</span>])<span class="comment">#会输出img以及对应的label</span></span><br><span class="line">img,label = ants_dataset[<span class="number">0</span>]<span class="comment">#实现了geititem函数,有两个返回值</span></span><br><span class="line">img.show()//显示图片</span><br><span class="line">img,label = bees_dataset[<span class="number">1</span>]</span><br><span class="line">img.show()</span><br><span class="line"></span><br><span class="line"><span class="built_in">len</span>(ants_dataset)<span class="comment">#得到124</span></span><br><span class="line"><span class="built_in">len</span>(bees_dataset)<span class="comment">#得到121</span></span><br><span class="line">train_dataset = ants_dataset+bees_dataset<span class="comment">#这个数据集会变成ants_dataset和bees_dataset的拼接,达到仿造数据集,解决数据集不足问题</span></span><br><span class="line"><span class="built_in">len</span>(train_dataset)<span class="comment">#得到245</span></span><br><span class="line"></span><br><span class="line">img,label=train_dataset[<span class="number">123</span>]<span class="comment">#得到蚂蚁,蚂蚁是[0-123]</span></span><br><span class="line">img,label=train_dataset[<span class="number">124</span>]<span class="comment">#得到蜜蜂,蜜蜂是[124-244]</span></span><br></pre></td></tr></table></figure><p>作出数据集的另一种存储方式:</p><figure class="highlight python"><table><tr><td class="code"><pre><span class="line"><span class="comment">#把图片的label生成以图片名为文件名的txt文档</span></span><br><span class="line"><span class="keyword">import</span> os</span><br><span class="line"></span><br><span class="line">root_dir = <span class="string">'练手数据集/train'</span></span><br><span class="line">target_dir = <span class="string">'ants_image'</span></span><br><span class="line">img_path = os.listdir(os.path.join(root_dir, target_dir))</span><br><span class="line">label = target_dir.split(<span class="string">'_'</span>)[<span class="number">0</span>]</span><br><span class="line">out_dir = <span class="string">'ants_label'</span></span><br><span class="line"><span class="keyword">for</span> i <span class="keyword">in</span> img_path:</span><br><span class="line"> file_name = i.split(<span class="string">'.jpg'</span>)[<span class="number">0</span>]</span><br><span class="line"> <span class="keyword">with</span> <span class="built_in">open</span>(os.path.join(root_dir, out_dir,<span class="string">"{}.txt"</span>.<span class="built_in">format</span>(file_name)),<span class="string">'w'</span>) <span class="keyword">as</span> f:<span class="comment">#open会生成不存在的文件</span></span><br><span class="line"> f.write(label)</span><br></pre></td></tr></table></figure><h1 id="TensorBoard的使用"><a href="#TensorBoard的使用" class="headerlink" title="TensorBoard的使用"></a>TensorBoard的使用</h1><p>TensorBoard:演示transform的结果,在运行完方法后展示图像。例如loss图、训练结果output。</p><p>首先要<code>from torch.utils.tensorboard import SummaryWriter</code>。</p><h2 id="SummaryWriter类使用"><a href="#SummaryWriter类使用" class="headerlink" title="SummaryWriter类使用"></a>SummaryWriter类使用</h2><p><code>CTRL</code>+鼠标移至类可以查看类的用法。</p><ul><li><p>SummerWriter将条目直接写入 TensorBoard 要使用的<code>log_dir</code>中的事件文件,事件文件可以直接被TensorBoard解析。</p></li><li><p>初始化的时候需要输入<code>log_dir</code>文件夹的名称,不输入也可以,有默认位置。</p></li><li><p>初始化方式:</p><figure class="highlight python"><table><tr><td class="code"><pre><span class="line">writer = SummaryWriter()<span class="comment">#什么都不加,默认的事件文件存储的位置在runs/下</span></span><br><span class="line">writer = SummaryWriter(<span class="string">"my_experiment"</span>)<span class="comment">#给出初始化文件夹</span></span><br><span class="line">writer = SummaryWriter(comment=<span class="string">"LR_0.1_BATCH_16"</span>)<span class="comment">#设置相应参数</span></span><br></pre></td></tr></table></figure></li></ul><p>主要用到两个方法:</p><ul><li><code>writer.add_scalar(tag(string),scalar_value(float or string/blobname),global_step(int))</code><ul><li>参数<code>tag(string)</code>设置图表的title</li><li>参数<code>scalar_value(float or string/blobname)</code>设置图表y轴</li><li>参数<code>global_step(int)</code>对应x轴</li></ul></li></ul><p>打开事件文件,在pycharm的命令行,输入<code>tensorboard --logdir=事件文件所在文件夹名</code>。如果端口被占用了,可以自行添加端口,输入<code>tensorboard --logdir=事件文件所在文件夹名 --port=端口值 </code> </p><p>如果打开的tensorboard出现了重复在一张图上的情况,需要进行删除原来的事件文件,或者重新SummaryWriter一个<code>log_dir</code>文件</p><ul><li><code>writer.add_image(tag(string),img_tensor(torch.Tensor, numpy.array, or string/blobname),global_step(int))</code><ul><li>参数<code>tag(string)</code>,设置图像的title</li><li>参数<code>img_tensor(torch.Tensor, numpy.array, or string/blobname)</code>,设置图像</li><li>参数<code>global_step(int)</code>,设置训练步骤</li></ul></li></ul><p>可以利用Opencv读取图片,获得numpy型图片数据。</p><p>如果是用PIL数据转化成numpy数据,需要在add_image()中指定shape中每一个数字/维表示的含义。主要是通道数。</p><p>step可以理解为多张图片可以放在同一个title里面,通过滑块可以看到每一步的图片,如果想让多张图片单独显示,需要把title重新命名</p><p>上面的代码综合运用:</p><figure class="highlight python"><table><tr><td class="code"><pre><span class="line"><span class="keyword">from</span> torch.utils.tensorboard <span class="keyword">import</span> SummaryWriter</span><br><span class="line"><span class="keyword">from</span> PIL <span class="keyword">import</span> Image</span><br><span class="line"><span class="keyword">import</span> numpy <span class="keyword">as</span> np</span><br><span class="line"></span><br><span class="line">writer = SummaryWriter(<span class="string">"logs"</span>)<span class="comment">#存储文件夹</span></span><br><span class="line"><span class="comment"># y=3x</span></span><br><span class="line"><span class="keyword">for</span> i <span class="keyword">in</span> <span class="built_in">range</span>(<span class="number">100</span>): <span class="comment">#i从0-99</span></span><br><span class="line"> writer.add_scalar(<span class="string">"y=3x"</span>,<span class="number">3</span>*i,i)<span class="comment"># 添加标量</span></span><br><span class="line"></span><br><span class="line">img_path = <span class="string">"data/train/ants_image/0013035.jpg"</span></span><br><span class="line">img = Image.<span class="built_in">open</span>(img_path)</span><br><span class="line">img_array = np.array(img) <span class="comment"># 类型转换,这样得到的numpy.array的通道是在最后面的</span></span><br><span class="line"><span class="built_in">print</span>(<span class="built_in">type</span>(img_array))</span><br><span class="line"><span class="built_in">print</span>(img_array.shape) <span class="comment"># 结果是(512,768,3)</span></span><br><span class="line">writer.add_image(<span class="string">"train"</span>,img_array,<span class="number">1</span>,dataformats=<span class="string">'HWC'</span>)<span class="comment">#需要指定是HWC型,因为数据就是通道在最后面,不然会报错</span></span><br><span class="line"></span><br><span class="line">writer.close()</span><br></pre></td></tr></table></figure><h1 id="Transforms的使用"><a href="#Transforms的使用" class="headerlink" title="Transforms的使用"></a>Transforms的使用</h1><p>Transforms主要是用于对图片进行一些变换。将特定格式的图片经过工具之后输出我们想要的图片结果。</p><h2 id="Transforms的结构及用法"><a href="#Transforms的结构及用法" class="headerlink" title="Transforms的结构及用法"></a>Transforms的结构及用法</h2><p>Transforms.py工具箱具有的工具:</p><ul><li>Totensor类,Totensor就是把PIL Image或者numpy.ndarray类型转化为tensor类型的</li><li>resize类,裁剪</li><li>…</li></ul><h2 id="tensor数据类型"><a href="#tensor数据类型" class="headerlink" title="tensor数据类型"></a>tensor数据类型</h2><p>通过transforms.Totensor去解决两个问题:</p><ul><li><p>transforms该如何使用(python)?</p><figure class="highlight python"><table><tr><td class="code"><pre><span class="line">tensor_trans = transforms.ToTensor() <span class="comment">#返回totensor的对象,默认构造</span></span><br><span class="line">tensor_img = tensor_trans(img)<span class="comment">#将img转化为tensor类型的img</span></span><br></pre></td></tr></table></figure></li><li><p>Tensor数据类型相较于普通的数据类型或者图片数据类型有何区别?为什么需要tensor的数据类型?</p><p>Tensor数据类型包装了神经网络所需要的一些参数</p></li></ul><p>上述示例代码:</p><figure class="highlight python"><table><tr><td class="code"><pre><span class="line"><span class="keyword">from</span> torch.utils.tensorboard <span class="keyword">import</span> SummaryWriter</span><br><span class="line"><span class="keyword">from</span> torchvision <span class="keyword">import</span> transforms</span><br><span class="line"><span class="keyword">from</span> PIL <span class="keyword">import</span> Image</span><br><span class="line"><span class="keyword">import</span> cv2</span><br><span class="line"></span><br><span class="line">img_path = <span class="string">"data/train/ants_image/0013035.jpg"</span></span><br><span class="line">img = Image.<span class="built_in">open</span>(img_path)</span><br><span class="line"><span class="built_in">print</span>(img) <span class="comment"># 得到<PIL.JpegImagePlugin.JpegImageFile image mode=RGB size=768x512 at 0x1B1C3BB77F0></span></span><br><span class="line"></span><br><span class="line">tensor_trans = transforms.ToTensor() <span class="comment">#新建totensor的对象</span></span><br><span class="line">tensor_img = tensor_trans(img)<span class="comment">#调用call函数,调用类的名称时会自动运行该方法,将img转化为tensor类型的img</span></span><br><span class="line"><span class="built_in">print</span>(tensor_img)</span><br><span class="line"></span><br><span class="line">cv_img = cv2.imread(img_path) <span class="comment">#cv_img的数据类型就是numpy.ndarray</span></span><br><span class="line">tensor_cv_img = tensor_trans(cv_img)<span class="comment">#将cv_img转化为tensor类型的img</span></span><br><span class="line"><span class="built_in">print</span>(tensor_cv_img)</span><br><span class="line"></span><br><span class="line">writer = SummaryWriter(<span class="string">"logs"</span>)</span><br><span class="line">writer.add_image(<span class="string">"Tensor_img"</span>,tensor_img)</span><br><span class="line"></span><br><span class="line">writer.close()</span><br></pre></td></tr></table></figure><p>python中call函数的作用:call函数可以直接使用<code>对象名(参数)</code>这种方式去调用,这种方式就像Java重写的toString一样</p><h2 id="常见的Transform"><a href="#常见的Transform" class="headerlink" title="常见的Transform"></a>常见的Transform</h2><p>Compose类:把不同的transform结合在一起</p><p>ToTensor类:把PIL Image或者numpy.ndarray类型转化为tensor类型</p><p>ToPILImage类:把tensor类型或者ndarray类型转化为PIL Image类型</p><p>Normalize类:用平均值和标准差对tensor image(图像张量)进行归一化,归一到[-1,1]</p><p>Resize类:把图像缩放成指定的大小,如果给了元组序列,图像就会按元组序列的大小来重新调整,如果只是给了一个int型,图像就会以这个int型进行等比缩放,最短边的长度等于int值。输入为PIL Image,返回值还是PIL Image</p><p>RandomCrop类:随即裁剪,和Resize用法差不多,输入都是 PIL Image</p><p>使用方法总结:1. 关注输入和输出类型,输出可以直接print()看,也可以断点看 2.多看官方文档 3.关注方法需要的参数,没有设置默认值就是要输入的参数,找到它然后查看它的类型</p><p>上述代码实例:</p><figure class="highlight python"><table><tr><td class="code"><pre><span class="line"><span class="keyword">from</span> PIL <span class="keyword">import</span> Image</span><br><span class="line"><span class="keyword">from</span> torch.utils.tensorboard <span class="keyword">import</span> SummaryWriter</span><br><span class="line"><span class="keyword">from</span> torchvision <span class="keyword">import</span> transforms</span><br><span class="line"></span><br><span class="line">img = Image.<span class="built_in">open</span>(<span class="string">"data/train/ants_image/0013035.jpg"</span>)</span><br><span class="line"><span class="built_in">print</span>(img)</span><br><span class="line">writer = SummaryWriter(<span class="string">"logs"</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># Totensor</span></span><br><span class="line">trans_totensor = transforms.ToTensor()</span><br><span class="line">img_tenser = trans_totensor(img)</span><br><span class="line">writer.add_image(<span class="string">"ToTenser"</span>, img_tenser)</span><br><span class="line"></span><br><span class="line"><span class="comment"># Normalize</span></span><br><span class="line"><span class="comment"># 归一化计算公式:output[channel] = (input[channel] - mean[channel]) / std[channel]</span></span><br><span class="line"><span class="built_in">print</span>(img_tenser[<span class="number">0</span>][<span class="number">0</span>][<span class="number">0</span>])</span><br><span class="line">trans_norm = transforms.Normalize([<span class="number">0.5</span>,<span class="number">0.5</span>,<span class="number">0.5</span>],[<span class="number">0.5</span>,<span class="number">0.5</span>,<span class="number">0.5</span>])</span><br><span class="line">img_norm = trans_norm(img_tenser)</span><br><span class="line"><span class="built_in">print</span>(img_norm[<span class="number">0</span>][<span class="number">0</span>][<span class="number">0</span>])</span><br><span class="line">writer.add_image(<span class="string">"Normalize"</span>,img_norm)</span><br><span class="line"></span><br><span class="line"><span class="comment"># Resize</span></span><br><span class="line"><span class="built_in">print</span>(img.size) <span class="comment">#(768, 512)</span></span><br><span class="line">trans_resize = transforms.Resize((<span class="number">512</span>,<span class="number">512</span>))</span><br><span class="line"><span class="comment"># img是PIL类型,经过resize返回还是一个PIL类型</span></span><br><span class="line">img_resize = trans_resize(img)</span><br><span class="line"><span class="built_in">print</span>(img_resize) <span class="comment">#<PIL.Image.Image image mode=RGB size=512x512 at 0x13442E71430></span></span><br><span class="line"><span class="comment">#img_resize.show()</span></span><br><span class="line"><span class="comment"># PIL类型使用totensor转化为tensor对象</span></span><br><span class="line">img_resize = trans_totensor(img_resize)</span><br><span class="line">writer.add_image(<span class="string">"Resize"</span>,img_resize,<span class="number">0</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># Compose — resize的第二种用法</span></span><br><span class="line">trans_resize_2 = transforms.Resize(<span class="number">512</span>)</span><br><span class="line"><span class="comment"># PIL Image->PIL Image->tensor 要注意列表中前一个输出和后一个输入是不是相互匹配,因为是联系的</span></span><br><span class="line">trans_compose = transforms.Compose([trans_resize_2,trans_totensor])</span><br><span class="line">img_resize_2 = trans_compose(img)<span class="comment">#第一个transform需要的输入</span></span><br><span class="line">writer.add_image(<span class="string">"Resize"</span>,img_resize_2,<span class="number">1</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># RandomCrop</span></span><br><span class="line">trans_random = transforms.RandomCrop(<span class="number">512</span>)</span><br><span class="line">trans_compose_2 = transforms.Compose([trans_random,trans_totensor])</span><br><span class="line"><span class="keyword">for</span> i <span class="keyword">in</span> <span class="built_in">range</span>(<span class="number">10</span>):</span><br><span class="line"> img_crop = trans_compose_2(img)</span><br><span class="line"> writer.add_image(<span class="string">"RandomCrop"</span>,img_crop,i)</span><br><span class="line"></span><br><span class="line">writer.close()</span><br></pre></td></tr></table></figure><h1 id="将dataset和transform结合到一起"><a href="#将dataset和transform结合到一起" class="headerlink" title="将dataset和transform结合到一起"></a>将dataset和transform结合到一起</h1><p>dataset告诉我们数据集在什么样的位置和多少数据,给索引+数据</p><p>具体实例:</p><figure class="highlight python"><table><tr><td class="code"><pre><span class="line"><span class="keyword">import</span> torchvision</span><br><span class="line"><span class="keyword">from</span> PIL <span class="keyword">import</span> Image</span><br><span class="line"><span class="keyword">from</span> torch.utils.tensorboard <span class="keyword">import</span> SummaryWriter</span><br><span class="line"></span><br><span class="line">dataset_transform = torchvision.transforms.Compose([</span><br><span class="line"> torchvision.transforms.ToTensor()</span><br><span class="line">])</span><br><span class="line"><span class="comment"># 保存数据集的路径,训练集还是测试集,transform,下载到本地否,'.'表示当前目录,'..'表示上一级目录</span></span><br><span class="line">train_set = torchvision.datasets.CIFAR10(root=<span class="string">"./dataset"</span>,train=<span class="literal">True</span>,transform=dataset_transform,download=<span class="literal">True</span>)<span class="comment">#训练集</span></span><br><span class="line">test_set = torchvision.datasets.CIFAR10(root=<span class="string">"./dataset"</span>,train=<span class="literal">False</span>,transform=dataset_transform,download=<span class="literal">True</span>)<span class="comment">#测试集</span></span><br><span class="line"><span class="comment"># (<PIL.Image.Image image mode=RGB size=32x32 at 0x2784D869610>, 3),3是target</span></span><br><span class="line"><span class="comment"># 原始图片是PIL Image</span></span><br><span class="line"><span class="built_in">print</span>(test_set[<span class="number">0</span>])</span><br><span class="line"><span class="built_in">print</span>(test_set.classes)</span><br><span class="line"></span><br><span class="line">img, target = test_set[<span class="number">0</span>]</span><br><span class="line"><span class="built_in">print</span>(img)</span><br><span class="line"><span class="built_in">print</span>(target)</span><br><span class="line"><span class="built_in">print</span>(test_set.classes[target])</span><br><span class="line">img.show()</span><br><span class="line"></span><br><span class="line"><span class="built_in">print</span>(test_set[<span class="number">0</span>])</span><br><span class="line"></span><br><span class="line">writer = SummaryWriter(<span class="string">"logs"</span>)</span><br><span class="line"><span class="keyword">for</span> i <span class="keyword">in</span> <span class="built_in">range</span>(<span class="number">10</span>):</span><br><span class="line"> img,target = test_set[i]</span><br><span class="line"> writer.add_image(<span class="string">"test_set"</span>,img,i)</span><br><span class="line"></span><br><span class="line">writer.close()</span><br></pre></td></tr></table></figure><h1 id="dataloader"><a href="#dataloader" class="headerlink" title="dataloader"></a>dataloader</h1><p>dataloader就是从dataset中取数据,参数控制取多少怎么取</p><ul><li>batch_size这个参数是指每次取多少数据</li><li>suffle这个参数是打乱顺序</li><li>num_workers多线程,加载数据的时候采用单个线程还是多个线程,默认情况为0,采用主线程</li><li>drop_last是否舍去最后的余数</li></ul><p>代码实例:</p><figure class="highlight python"><table><tr><td class="code"><pre><span class="line"><span class="keyword">import</span> torchvision</span><br><span class="line"><span class="keyword">from</span> torch.utils.data <span class="keyword">import</span> DataLoader</span><br><span class="line"><span class="keyword">from</span> torch.utils.tensorboard <span class="keyword">import</span> SummaryWriter</span><br><span class="line"></span><br><span class="line"><span class="comment"># 准备的测试数据集</span></span><br><span class="line">test_data = torchvision.datasets.CIFAR10(<span class="string">"./dataset"</span>,train=<span class="literal">False</span>,transform=torchvision.transforms.ToTensor())</span><br><span class="line"></span><br><span class="line"><span class="comment">#取出四个img,四个target的形式分别打包返回</span></span><br><span class="line">test_loader = DataLoader(dataset=test_data,batch_size=<span class="number">64</span>,shuffle=<span class="literal">False</span>,num_workers=<span class="number">0</span>,drop_last=<span class="literal">False</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 测试数据集中第一张图片及target</span></span><br><span class="line">img,target = test_data[<span class="number">0</span>]</span><br><span class="line"><span class="built_in">print</span>(img.shape)</span><br><span class="line"><span class="built_in">print</span>(target)</span><br><span class="line"></span><br><span class="line">writer = SummaryWriter(<span class="string">"logs"</span>)</span><br><span class="line"><span class="keyword">for</span> epoch <span class="keyword">in</span> <span class="built_in">range</span>(<span class="number">2</span>):<span class="comment">#相当于一轮结束的洗牌</span></span><br><span class="line"> step = <span class="number">0</span></span><br><span class="line"> <span class="keyword">for</span> data <span class="keyword">in</span> test_loader:</span><br><span class="line"> imgs, targets = data</span><br><span class="line"> <span class="comment"># print(imgs.shape)</span></span><br><span class="line"> <span class="comment"># print(targets)</span></span><br><span class="line"> writer.add_images(<span class="string">"Epoch:{}"</span>.<span class="built_in">format</span>(epoch),imgs,step)</span><br><span class="line"> step = step+<span class="number">1</span></span><br><span class="line"></span><br><span class="line">writer.close()</span><br></pre></td></tr></table></figure><h1 id="网络基本骨架的搭建"><a href="#网络基本骨架的搭建" class="headerlink" title="网络基本骨架的搭建"></a>网络基本骨架的搭建</h1><p>关于神经网络的工具一般都在torch.nn<Neural network>里面</Neural></p><p>Containers—>神经网络的骨架、结构,常用Module模块,为所有神经网络提供一个基本的骨架,所搭建的神经网络必须从torch.nn.Module类继承而来。其中有个forward函数,它是call方法的实现,在所有继承了torch.nn.Module类的子类中都要重写,是前向传播,比较重要,类似于call方法直接调用类作为方法,它的作用是输入通过forward变为输出</p><p>Module的使用代码:</p><figure class="highlight python"><table><tr><td class="code"><pre><span class="line"><span class="keyword">import</span> torch</span><br><span class="line"><span class="keyword">from</span> torch <span class="keyword">import</span> nn</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">Model</span>(nn.Module):</span><br><span class="line"> <span class="keyword">def</span> <span class="title function_">__init__</span>(<span class="params">self</span>) -> <span class="literal">None</span>:</span><br><span class="line"> <span class="built_in">super</span>().__init__()</span><br><span class="line"> </span><br><span class="line"> <span class="comment">#这种方法也可以初始化,但是我不知道区别在哪</span></span><br><span class="line"> <span class="comment">#def __init__(self):</span></span><br><span class="line"> <span class="comment">#super(Model, self).__init__()</span></span><br><span class="line"></span><br><span class="line"> <span class="keyword">def</span> <span class="title function_">forward</span>(<span class="params">self,<span class="built_in">input</span></span>):</span><br><span class="line"> output = <span class="built_in">input</span> +<span class="number">1</span></span><br><span class="line"> <span class="keyword">return</span> output</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">model = Model()</span><br><span class="line">x = torch.tensor(<span class="number">1.0</span>)</span><br><span class="line"><span class="comment"># 把x放到神经网络中</span></span><br><span class="line">output = model(x)</span><br><span class="line"><span class="built_in">print</span>(output)</span><br></pre></td></tr></table></figure><p>Sequential的目的可以让神经网络代码压缩在一段代码中,过程连续。</p><p>sequential实例代码:</p><figure class="highlight python"><table><tr><td class="code"><pre><span class="line"><span class="keyword">import</span> torch</span><br><span class="line"><span class="keyword">from</span> torch <span class="keyword">import</span> nn</span><br><span class="line"><span class="keyword">from</span> torch.nn <span class="keyword">import</span> Conv2d, MaxPool2d, Flatten, Linear, Sequential</span><br><span class="line"><span class="keyword">from</span> torch.utils.tensorboard <span class="keyword">import</span> SummaryWriter</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">CNN</span>(nn.Module):</span><br><span class="line"> <span class="keyword">def</span> <span class="title function_">__init__</span>(<span class="params">self</span>):</span><br><span class="line"> <span class="built_in">super</span>(CNN, self).__init__()</span><br><span class="line"> <span class="comment"># self.conv1 = Conv2d(3,32,5,padding=2)</span></span><br><span class="line"> <span class="comment"># self.maxpool1 = MaxPool2d(2)</span></span><br><span class="line"> <span class="comment"># self.conv2 = Conv2d(32,32,5,padding=2)</span></span><br><span class="line"> <span class="comment"># self.maxpool2 = MaxPool2d(2)</span></span><br><span class="line"> <span class="comment"># self.conv3 = Conv2d(32,64,5,padding=2)</span></span><br><span class="line"> <span class="comment"># self.maxpool3 = MaxPool2d(2)</span></span><br><span class="line"> <span class="comment"># self.flatten = Flatten()</span></span><br><span class="line"> <span class="comment"># self.linear1 = Linear(1024,64)</span></span><br><span class="line"> <span class="comment"># self.linear2 = Linear(64,10)</span></span><br><span class="line"></span><br><span class="line"> self.module1 = Sequential(</span><br><span class="line"> Conv2d(<span class="number">3</span>, <span class="number">32</span>, <span class="number">5</span>, padding=<span class="number">2</span>),</span><br><span class="line"> MaxPool2d(<span class="number">2</span>),</span><br><span class="line"> Conv2d(<span class="number">32</span>, <span class="number">32</span>, <span class="number">5</span>, padding=<span class="number">2</span>),</span><br><span class="line"> MaxPool2d(<span class="number">2</span>),</span><br><span class="line"> Conv2d(<span class="number">32</span>, <span class="number">64</span>, <span class="number">5</span>, padding=<span class="number">2</span>),</span><br><span class="line"> MaxPool2d(<span class="number">2</span>),</span><br><span class="line"> Flatten(),</span><br><span class="line"> Linear(<span class="number">1024</span>, <span class="number">64</span>),</span><br><span class="line"> Linear(<span class="number">64</span>, <span class="number">10</span>)</span><br><span class="line"> )</span><br><span class="line"><span class="comment"># 检验网络参数是否正确的话,可以一步步写forward 然后运行查看output.shape</span></span><br><span class="line"> <span class="keyword">def</span> <span class="title function_">forward</span>(<span class="params">self,x</span>):</span><br><span class="line"> <span class="comment"># x = self.conv1(x)</span></span><br><span class="line"> <span class="comment"># x = self.maxpool1(x)</span></span><br><span class="line"> <span class="comment"># x = self.conv2(x)</span></span><br><span class="line"> <span class="comment"># x = self.maxpool2(x)</span></span><br><span class="line"> <span class="comment"># x = self.conv3(x)</span></span><br><span class="line"> <span class="comment"># x = self.maxpool3(x)</span></span><br><span class="line"> <span class="comment"># x = self.flatten(x)</span></span><br><span class="line"> <span class="comment"># x = self.linear1(x)</span></span><br><span class="line"> <span class="comment"># x = self.linear2(x)</span></span><br><span class="line"></span><br><span class="line"> x = self.module1(x)</span><br><span class="line"> <span class="keyword">return</span> x</span><br><span class="line"></span><br><span class="line">cnn = CNN()</span><br><span class="line"><span class="built_in">print</span>(cnn)</span><br><span class="line"></span><br><span class="line"><span class="built_in">input</span> = torch.ones((<span class="number">64</span>,<span class="number">3</span>,<span class="number">32</span>,<span class="number">32</span>))</span><br><span class="line">output = cnn(<span class="built_in">input</span>)</span><br><span class="line"><span class="built_in">print</span>(output.shape)</span><br><span class="line"></span><br><span class="line">writer = SummaryWriter(<span class="string">"logs"</span>)</span><br><span class="line">writer.add_graph(cnn,<span class="built_in">input</span>)</span><br><span class="line">writer.close()</span><br></pre></td></tr></table></figure><h1 id="Covolution-Layers—-gt-卷积层"><a href="#Covolution-Layers—-gt-卷积层" class="headerlink" title="Covolution Layers—>卷积层"></a>Covolution Layers—>卷积层</h1><p>卷积核随机初始化,网络训练的就是卷积核</p><p>卷积操作内部具体实现<实践用不到>:</p><figure class="highlight python"><table><tr><td class="code"><pre><span class="line"><span class="keyword">import</span> torch</span><br><span class="line"><span class="keyword">import</span> torch.nn.functional <span class="keyword">as</span> F</span><br><span class="line"><span class="comment"># 数据类型转换</span></span><br><span class="line"><span class="built_in">input</span> = torch.tensor([[<span class="number">1</span>,<span class="number">2</span>,<span class="number">0</span>,<span class="number">3</span>,<span class="number">1</span>],</span><br><span class="line"> [<span class="number">0</span>,<span class="number">1</span>,<span class="number">2</span>,<span class="number">3</span>,<span class="number">1</span>],</span><br><span class="line"> [<span class="number">1</span>,<span class="number">2</span>,<span class="number">1</span>,<span class="number">0</span>,<span class="number">0</span>],</span><br><span class="line"> [<span class="number">5</span>,<span class="number">2</span>,<span class="number">3</span>,<span class="number">1</span>,<span class="number">1</span>],</span><br><span class="line"> [<span class="number">2</span>,<span class="number">1</span>,<span class="number">0</span>,<span class="number">1</span>,<span class="number">1</span>]])</span><br><span class="line"></span><br><span class="line">kernel = torch.tensor([[<span class="number">1</span>,<span class="number">2</span>,<span class="number">1</span>],</span><br><span class="line"> [<span class="number">0</span>,<span class="number">1</span>,<span class="number">0</span>],</span><br><span class="line"> [<span class="number">2</span>,<span class="number">1</span>,<span class="number">0</span>]])</span><br><span class="line"><span class="comment">#调整尺寸,由于cov2d的input需要四位张量</span></span><br><span class="line"><span class="built_in">input</span> = torch.reshape(<span class="built_in">input</span>,(<span class="number">1</span>,<span class="number">1</span>,<span class="number">5</span>,<span class="number">5</span>))</span><br><span class="line">kernel = torch.reshape(kernel,(<span class="number">1</span>,<span class="number">1</span>,<span class="number">3</span>,<span class="number">3</span>))</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="built_in">print</span>(<span class="built_in">input</span>.shape)</span><br><span class="line"><span class="built_in">print</span>(kernel.shape)</span><br><span class="line"></span><br><span class="line">output = F.conv2d(<span class="built_in">input</span>,kernel,stride=<span class="number">1</span>)</span><br><span class="line"><span class="built_in">print</span>(output)</span><br><span class="line"></span><br><span class="line">output2 = F.conv2d(<span class="built_in">input</span>,kernel,stride=<span class="number">2</span>)</span><br><span class="line"><span class="built_in">print</span>(output2)</span><br><span class="line"></span><br><span class="line">output3 = F.conv2d(<span class="built_in">input</span>,kernel,stride=<span class="number">1</span>,padding=<span class="number">1</span>)</span><br><span class="line"><span class="built_in">print</span>(output3)</span><br></pre></td></tr></table></figure><p>代码示例:</p><figure class="highlight python"><table><tr><td class="code"><pre><span class="line"><span class="keyword">import</span> torchvision</span><br><span class="line"><span class="keyword">import</span> torch</span><br><span class="line"><span class="keyword">import</span> torch.nn</span><br><span class="line"><span class="keyword">from</span> torch <span class="keyword">import</span> nn</span><br><span class="line"><span class="keyword">from</span> torch.nn <span class="keyword">import</span> Conv2d</span><br><span class="line"><span class="keyword">from</span> torch.utils.data <span class="keyword">import</span> DataLoader</span><br><span class="line"><span class="keyword">from</span> torch.utils.tensorboard <span class="keyword">import</span> SummaryWriter</span><br><span class="line"></span><br><span class="line">dataset = torchvision.datasets.CIFAR10(<span class="string">"./dataset"</span>,train=<span class="literal">False</span>,transform=torchvision.transforms.ToTensor(),download=<span class="literal">True</span>)</span><br><span class="line"></span><br><span class="line">dataloader = DataLoader(dataset,batch_size=<span class="number">64</span>)</span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">NN</span>(nn.Module):</span><br><span class="line"> <span class="keyword">def</span> <span class="title function_">__init__</span>(<span class="params">self</span>):</span><br><span class="line"> <span class="built_in">super</span>(NN, self).__init__()</span><br><span class="line"> self.conv1 = Conv2d(in_channels=<span class="number">3</span>,out_channels=<span class="number">6</span>,kernel_size=<span class="number">3</span>,stride=<span class="number">1</span>,padding=<span class="number">0</span>)</span><br><span class="line"></span><br><span class="line"> <span class="keyword">def</span> <span class="title function_">forward</span>(<span class="params">self,x</span>):</span><br><span class="line"> x = self.conv1(x)</span><br><span class="line"> <span class="keyword">return</span> x</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">nn = NN()</span><br><span class="line"><span class="built_in">print</span>(nn)</span><br><span class="line">writer = SummaryWriter(<span class="string">"logs"</span>)</span><br><span class="line">step = <span class="number">0</span></span><br><span class="line"><span class="keyword">for</span> data <span class="keyword">in</span> dataloader:</span><br><span class="line"> imgs,targets = data</span><br><span class="line"> output = nn(imgs)</span><br><span class="line"> <span class="built_in">print</span>(imgs.shape)</span><br><span class="line"> <span class="built_in">print</span>(output.shape)</span><br><span class="line"> <span class="comment">#torch.Size([64, 3, 32, 32])</span></span><br><span class="line"> writer.add_images(<span class="string">"input"</span>,imgs,step)</span><br><span class="line"> <span class="comment">#torch.Size([64, 6, 30, 30]) 会报错,因为彩色图像为3个channel,6个channel不知道怎么显示</span></span><br><span class="line"></span><br><span class="line"> output = torch.reshape(output,(-<span class="number">1</span>,<span class="number">3</span>,<span class="number">30</span>,<span class="number">30</span>))<span class="comment">#设置-1,会根据数据自动调整</span></span><br><span class="line"> writer.add_images(<span class="string">"output"</span>,output,step)</span><br><span class="line"> step = step + <span class="number">1</span></span><br></pre></td></tr></table></figure><h1 id="Pooling-Layers—-gt-池化层"><a href="#Pooling-Layers—-gt-池化层" class="headerlink" title="Pooling Layers—>池化层"></a>Pooling Layers—>池化层</h1><p>最大池化目的:保留输入的特征,同时把参数量减少</p><p>代码示例:</p><figure class="highlight python"><table><tr><td class="code"><pre><span class="line"><span class="keyword">import</span> torch</span><br><span class="line"><span class="keyword">import</span> torchvision.datasets</span><br><span class="line"><span class="keyword">from</span> torch <span class="keyword">import</span> nn</span><br><span class="line"><span class="keyword">from</span> torch.nn <span class="keyword">import</span> MaxPool2d</span><br><span class="line"><span class="keyword">from</span> torch.utils.data <span class="keyword">import</span> DataLoader</span><br><span class="line"><span class="keyword">from</span> torch.utils.tensorboard <span class="keyword">import</span> SummaryWriter</span><br><span class="line"></span><br><span class="line">dataset = torchvision.datasets.CIFAR10(<span class="string">"./dataset"</span>,train=<span class="literal">False</span>,download=<span class="literal">True</span>,transform=torchvision.transforms.ToTensor())</span><br><span class="line"></span><br><span class="line">dataloader = DataLoader(dataset, batch_size=<span class="number">64</span>)</span><br><span class="line"></span><br><span class="line"><span class="built_in">input</span> = torch.tensor([[<span class="number">1</span>,<span class="number">2</span>,<span class="number">0</span>,<span class="number">3</span>,<span class="number">1</span>],</span><br><span class="line"> [<span class="number">0</span>,<span class="number">1</span>,<span class="number">2</span>,<span class="number">3</span>,<span class="number">1</span>],</span><br><span class="line"> [<span class="number">1</span>,<span class="number">2</span>,<span class="number">1</span>,<span class="number">0</span>,<span class="number">0</span>],</span><br><span class="line"> [<span class="number">5</span>,<span class="number">2</span>,<span class="number">3</span>,<span class="number">1</span>,<span class="number">1</span>],</span><br><span class="line"> [<span class="number">2</span>,<span class="number">1</span>,<span class="number">0</span>,<span class="number">1</span>,<span class="number">1</span>]],dtype=torch.float32)<span class="comment">#其中数字转化为浮点数,编程浮点数的tensor类型</span></span><br><span class="line"></span><br><span class="line"><span class="built_in">input</span> = torch.reshape(<span class="built_in">input</span>,(-<span class="number">1</span>,<span class="number">1</span>,<span class="number">5</span>,<span class="number">5</span>))</span><br><span class="line"><span class="built_in">print</span>(<span class="built_in">input</span>.shape)</span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">maxpool</span>(nn.Module):</span><br><span class="line"> <span class="keyword">def</span> <span class="title function_">__init__</span>(<span class="params">self</span>):</span><br><span class="line"> <span class="built_in">super</span>(maxpool, self).__init__()</span><br><span class="line"> self.maxpool1 = MaxPool2d(kernel_size=<span class="number">3</span>,ceil_mode=<span class="literal">True</span>)</span><br><span class="line"></span><br><span class="line"> <span class="keyword">def</span> <span class="title function_">forward</span>(<span class="params">self,<span class="built_in">input</span></span>):</span><br><span class="line"> output = self.maxpool1(<span class="built_in">input</span>)</span><br><span class="line"> <span class="keyword">return</span> output</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">mp = maxpool()</span><br><span class="line"><span class="comment"># output = mp(input)</span></span><br><span class="line"><span class="comment"># print(output)</span></span><br><span class="line">writer = SummaryWriter(<span class="string">"logs"</span>)</span><br><span class="line">step = <span class="number">0</span></span><br><span class="line"><span class="keyword">for</span> data <span class="keyword">in</span> dataloader:</span><br><span class="line"> imgs,targets = data</span><br><span class="line"> writer.add_images(<span class="string">"input"</span>,imgs,step)</span><br><span class="line"> output = mp(imgs)<span class="comment">#得到的图像依然是三维的</span></span><br><span class="line"> writer.add_images(<span class="string">"output"</span>,output,step)</span><br><span class="line"> step=step+<span class="number">1</span></span><br><span class="line"></span><br><span class="line">writer.close()</span><br></pre></td></tr></table></figure><h1 id="非线性激活"><a href="#非线性激活" class="headerlink" title="非线性激活"></a>非线性激活</h1><p>RELU,当input>0时,赋值为input原来值,当input<0时,赋值为0,输入只有N—>batch_size;Sigmoid,也只是输入N。</p><p>ReLU的实例代码:</p><figure class="highlight python"><table><tr><td class="code"><pre><span class="line"><span class="keyword">import</span> torch</span><br><span class="line"><span class="keyword">from</span> torch <span class="keyword">import</span> nn</span><br><span class="line"><span class="keyword">from</span> torch.nn <span class="keyword">import</span> ReLU</span><br><span class="line"></span><br><span class="line"><span class="built_in">input</span> = torch.tensor([[<span class="number">1</span>,-<span class="number">0.5</span>],</span><br><span class="line"> [-<span class="number">1</span>,<span class="number">3</span>]])</span><br><span class="line"><span class="built_in">input</span> = torch.reshape(<span class="built_in">input</span>,(-<span class="number">1</span>,<span class="number">1</span>,<span class="number">2</span>,<span class="number">2</span>))</span><br><span class="line"><span class="built_in">print</span>(<span class="built_in">input</span>.shape)</span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">RELU</span>(nn.Module):</span><br><span class="line"> <span class="keyword">def</span> <span class="title function_">__init__</span>(<span class="params">self</span>):</span><br><span class="line"> <span class="built_in">super</span>(RELU, self).__init__()</span><br><span class="line"> self.relu = ReLU()<span class="comment">#inplace这个参数如果是True就在原值上进行改变,如果为False原值不变,返回改变后的值</span></span><br><span class="line"></span><br><span class="line"> <span class="keyword">def</span> <span class="title function_">forward</span>(<span class="params">self,<span class="built_in">input</span></span>):</span><br><span class="line"> output = self.relu(<span class="built_in">input</span>)</span><br><span class="line"> <span class="keyword">return</span> output</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">relu = RELU()</span><br><span class="line">output = relu(<span class="built_in">input</span>)</span><br><span class="line"><span class="built_in">print</span>(output)</span><br></pre></td></tr></table></figure><p>Sigmoid的实例代码:</p><figure class="highlight python"><table><tr><td class="code"><pre><span class="line"><span class="keyword">import</span> torch</span><br><span class="line"><span class="keyword">import</span> torchvision.datasets</span><br><span class="line"><span class="keyword">from</span> torch <span class="keyword">import</span> nn</span><br><span class="line"><span class="keyword">from</span> torch.nn <span class="keyword">import</span> ReLU, Sigmoid</span><br><span class="line"><span class="keyword">from</span> torch.utils.data <span class="keyword">import</span> DataLoader</span><br><span class="line"><span class="keyword">from</span> torch.utils.tensorboard <span class="keyword">import</span> SummaryWriter</span><br><span class="line"><span class="keyword">class</span> <span class="title class_">SigMoid</span>(nn.Module):</span><br><span class="line"> <span class="keyword">def</span> <span class="title function_">__init__</span>(<span class="params">self</span>):</span><br><span class="line"> <span class="built_in">super</span>(SigMoid, self).__init__()</span><br><span class="line"> self.sigmoid = Sigmoid()</span><br><span class="line"></span><br><span class="line"> <span class="keyword">def</span> <span class="title function_">forward</span>(<span class="params">self,<span class="built_in">input</span></span>):</span><br><span class="line"> output = self.sigmoid(<span class="built_in">input</span>)</span><br><span class="line"> <span class="keyword">return</span> output</span><br><span class="line"></span><br><span class="line">sigmoid = SigMoid()</span><br><span class="line"></span><br><span class="line">dataset = torchvision.datasets.CIFAR10(<span class="string">"./dataset"</span>,train=<span class="literal">False</span>,download=<span class="literal">True</span>,transform=torchvision.transforms.ToTensor())</span><br><span class="line">dataloader = DataLoader(dataset,batch_size = <span class="number">64</span>)</span><br><span class="line">writer = SummaryWriter(<span class="string">"logs"</span>)</span><br><span class="line">step = <span class="number">0</span></span><br><span class="line"><span class="keyword">for</span> data <span class="keyword">in</span> dataloader:</span><br><span class="line"> imgs,targets = data</span><br><span class="line"> writer.add_images(<span class="string">"input"</span>,imgs,step)</span><br><span class="line"> output=sigmoid(imgs)</span><br><span class="line"> writer.add_images(<span class="string">"output"</span>,output,step)</span><br><span class="line"> step=step+<span class="number">1</span></span><br><span class="line"></span><br><span class="line">writer.close()</span><br></pre></td></tr></table></figure><p>Padding Layers—>填充层,用的不多,在卷积里能达到同样的效果</p><p>Normalization Layers—>标准化层,用的不多</p><h1 id="Linear-Layers—-gt-全连接层"><a href="#Linear-Layers—-gt-全连接层" class="headerlink" title="Linear Layers—>全连接层"></a>Linear Layers—>全连接层</h1><p>Linear代码示例:</p><figure class="highlight python"><table><tr><td class="code"><pre><span class="line"><span class="keyword">import</span> torch</span><br><span class="line"><span class="keyword">import</span> torchvision</span><br><span class="line"><span class="keyword">from</span> torch <span class="keyword">import</span> nn</span><br><span class="line"><span class="keyword">from</span> torch.nn <span class="keyword">import</span> Linear</span><br><span class="line"><span class="keyword">from</span> torch.utils.data <span class="keyword">import</span> DataLoader</span><br><span class="line"></span><br><span class="line">dataset =torchvision.datasets.CIFAR10(<span class="string">"./dataset"</span>,train=<span class="literal">False</span>,transform=torchvision.transforms.ToTensor())</span><br><span class="line">dataloader= DataLoader(dataset,batch_size=<span class="number">64</span>)</span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">LINEAR</span>(nn.Module):</span><br><span class="line"> <span class="keyword">def</span> <span class="title function_">__init__</span>(<span class="params">self</span>):</span><br><span class="line"> <span class="built_in">super</span>(LINEAR, self).__init__()</span><br><span class="line"> self.linear = Linear(<span class="number">196608</span>,<span class="number">10</span>)</span><br><span class="line"></span><br><span class="line"> <span class="keyword">def</span> <span class="title function_">forward</span>(<span class="params">self,<span class="built_in">input</span></span>):</span><br><span class="line"> output = self.linear(<span class="built_in">input</span>)</span><br><span class="line"> <span class="keyword">return</span> output</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">linear = LINEAR()</span><br><span class="line"><span class="keyword">for</span> data <span class="keyword">in</span> dataloader:</span><br><span class="line"> imgs,target = data</span><br><span class="line"> <span class="built_in">print</span>(imgs.shape)</span><br><span class="line"> <span class="comment"># output = torch.reshape(imgs,(1,1,1,-1))</span></span><br><span class="line"> output = torch.flatten(imgs)<span class="comment">#扁平化处理</span></span><br><span class="line"> <span class="built_in">print</span>(output.shape)</span><br><span class="line"> output = linear(output)</span><br><span class="line"> <span class="built_in">print</span>(output.shape)</span><br></pre></td></tr></table></figure><p>Loss Function:loss就是输出值与实际的误差值,越小越好,是神经网络训练的依据。可以计算实际输出和目标之间的差距,为我们更新输出提供一定的依据(反向传播)。</p><ul><li>CrossEntropyLoss,交叉熵,分类问题中有C个类别。</li></ul><p>各种loss函数的使用</p><figure class="highlight python"><table><tr><td class="code"><pre><span class="line"><span class="keyword">import</span> torch</span><br><span class="line"><span class="keyword">from</span> torch <span class="keyword">import</span> nn</span><br><span class="line"><span class="keyword">from</span> torch.nn <span class="keyword">import</span> L1Loss, MSELoss</span><br><span class="line"></span><br><span class="line"><span class="comment"># 实际默认的数据就是float,这是因为我们创建tensor的时候没有加小数</span></span><br><span class="line">inputs = torch.tensor([<span class="number">1</span>,<span class="number">2</span>,<span class="number">3</span>],dtype=torch.float32)</span><br><span class="line">targets = torch.tensor([<span class="number">1</span>,<span class="number">2</span>,<span class="number">5</span>],dtype=torch.float32)</span><br><span class="line"></span><br><span class="line">inputs = torch.reshape(inputs,(<span class="number">1</span>,<span class="number">1</span>,<span class="number">1</span>,<span class="number">3</span>))</span><br><span class="line">targets = torch.reshape(targets,(<span class="number">1</span>,<span class="number">1</span>,<span class="number">1</span>,<span class="number">3</span>))</span><br><span class="line"></span><br><span class="line">loss = L1Loss()<span class="comment">#默认是取平均差值,可以选择取加值</span></span><br><span class="line">result = loss(inputs,targets)</span><br><span class="line"><span class="built_in">print</span>(result)<span class="comment">#tensor(0.6667)</span></span><br><span class="line"></span><br><span class="line">loss_mse = MSELoss()<span class="comment">#取平均平方差</span></span><br><span class="line">result_mse = loss_mse(inputs,targets)</span><br><span class="line"><span class="built_in">print</span>(result_mse)<span class="comment">#tensor(1.3333)</span></span><br><span class="line"></span><br><span class="line">x = torch.tensor([<span class="number">0.1</span>,<span class="number">0.2</span>,<span class="number">0.3</span>])</span><br><span class="line"><span class="built_in">print</span>(x.shape)</span><br><span class="line">y = torch.tensor([<span class="number">1</span>])</span><br><span class="line">x = torch.reshape(x,(<span class="number">1</span>,<span class="number">3</span>))<span class="comment">#一张图,三个分类</span></span><br><span class="line"><span class="built_in">print</span>(x.shape)</span><br><span class="line">loss_cross = nn.CrossEntropyLoss()</span><br><span class="line">result_cross = loss_cross(x,y)</span><br><span class="line"><span class="built_in">print</span>(result_cross)</span><br></pre></td></tr></table></figure><figure class="highlight python"><table><tr><td class="code"><pre><span class="line"><span class="keyword">import</span> torch</span><br><span class="line"><span class="keyword">import</span> torchvision</span><br><span class="line"><span class="keyword">from</span> torch <span class="keyword">import</span> nn</span><br><span class="line"><span class="keyword">from</span> torch.nn <span class="keyword">import</span> Conv2d, MaxPool2d, Flatten, Linear, Sequential</span><br><span class="line"><span class="keyword">from</span> torch.utils.data <span class="keyword">import</span> DataLoader</span><br><span class="line"></span><br><span class="line">dataset = torchvision.datasets.CIFAR10(<span class="string">"./dataset"</span>,train = <span class="literal">False</span>,transform=torchvision.transforms.ToTensor(),download=<span class="literal">True</span>)</span><br><span class="line">dataloader = DataLoader(dataset,batch_size=<span class="number">1</span>)</span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">CNN</span>(nn.Module):</span><br><span class="line"> <span class="keyword">def</span> <span class="title function_">__init__</span>(<span class="params">self</span>):</span><br><span class="line"> <span class="built_in">super</span>(CNN, self).__init__()</span><br><span class="line"> self.module1 = Sequential(</span><br><span class="line"> Conv2d(<span class="number">3</span>, <span class="number">32</span>, <span class="number">5</span>, padding=<span class="number">2</span>),</span><br><span class="line"> MaxPool2d(<span class="number">2</span>),</span><br><span class="line"> Conv2d(<span class="number">32</span>, <span class="number">32</span>, <span class="number">5</span>, padding=<span class="number">2</span>),</span><br><span class="line"> MaxPool2d(<span class="number">2</span>),</span><br><span class="line"> Conv2d(<span class="number">32</span>, <span class="number">64</span>, <span class="number">5</span>, padding=<span class="number">2</span>),</span><br><span class="line"> MaxPool2d(<span class="number">2</span>),</span><br><span class="line"> Flatten(),</span><br><span class="line"> Linear(<span class="number">1024</span>, <span class="number">64</span>),</span><br><span class="line"> Linear(<span class="number">64</span>, <span class="number">10</span>)</span><br><span class="line"> )</span><br><span class="line"></span><br><span class="line"> <span class="keyword">def</span> <span class="title function_">forward</span>(<span class="params">self,x</span>):</span><br><span class="line"> x = self.module1(x)</span><br><span class="line"> <span class="keyword">return</span> x</span><br><span class="line"></span><br><span class="line">loss = nn.CrossEntropyLoss()</span><br><span class="line">cnn = CNN()</span><br><span class="line"><span class="keyword">for</span> data <span class="keyword">in</span> dataloader:</span><br><span class="line"> <span class="comment">#看outputs和targets长什么样,看选择什么样的损失函数</span></span><br><span class="line"> imgs,targets = data</span><br><span class="line"> output = cnn(imgs)</span><br><span class="line"> <span class="comment"># print(output)</span></span><br><span class="line"> <span class="comment"># print(targets)</span></span><br><span class="line"> <span class="comment"># 反向传播,求导,需要选择合适的优化器</span></span><br><span class="line"> result_loss = loss(output,targets)</span><br><span class="line"> <span class="comment"># print(result_loss)</span></span><br><span class="line"> <span class="comment">#得到反向传播要更新参数对应的梯度</span></span><br><span class="line"> result_loss.backward()</span><br></pre></td></tr></table></figure><p>加上参数调优之后的整个网络:</p><figure class="highlight python"><table><tr><td class="code"><pre><span class="line"><span class="keyword">import</span> torch</span><br><span class="line"><span class="keyword">import</span> torchvision</span><br><span class="line"><span class="keyword">from</span> torch <span class="keyword">import</span> nn</span><br><span class="line"><span class="keyword">from</span> torch.nn <span class="keyword">import</span> Conv2d, MaxPool2d, Flatten, Linear, Sequential</span><br><span class="line"><span class="keyword">from</span> torch.utils.data <span class="keyword">import</span> DataLoader</span><br><span class="line"></span><br><span class="line">dataset = torchvision.datasets.CIFAR10(<span class="string">"./dataset"</span>,train = <span class="literal">False</span>,transform=torchvision.transforms.ToTensor(),download=<span class="literal">True</span>)</span><br><span class="line">dataloader = DataLoader(dataset,batch_size=<span class="number">1</span>)</span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">CNN</span>(nn.Module):</span><br><span class="line"> <span class="keyword">def</span> <span class="title function_">__init__</span>(<span class="params">self</span>):</span><br><span class="line"> <span class="built_in">super</span>(CNN, self).__init__()</span><br><span class="line"> self.module1 = Sequential(</span><br><span class="line"> Conv2d(<span class="number">3</span>, <span class="number">32</span>, <span class="number">5</span>, padding=<span class="number">2</span>),</span><br><span class="line"> MaxPool2d(<span class="number">2</span>),</span><br><span class="line"> Conv2d(<span class="number">32</span>, <span class="number">32</span>, <span class="number">5</span>, padding=<span class="number">2</span>),</span><br><span class="line"> MaxPool2d(<span class="number">2</span>),</span><br><span class="line"> Conv2d(<span class="number">32</span>, <span class="number">64</span>, <span class="number">5</span>, padding=<span class="number">2</span>),</span><br><span class="line"> MaxPool2d(<span class="number">2</span>),</span><br><span class="line"> Flatten(),</span><br><span class="line"> Linear(<span class="number">1024</span>, <span class="number">64</span>),</span><br><span class="line"> Linear(<span class="number">64</span>, <span class="number">10</span>)</span><br><span class="line"> )</span><br><span class="line"></span><br><span class="line"> <span class="keyword">def</span> <span class="title function_">forward</span>(<span class="params">self,x</span>):</span><br><span class="line"> x = self.module1(x)</span><br><span class="line"> <span class="keyword">return</span> x</span><br><span class="line"></span><br><span class="line"><span class="comment"># 交叉熵损失函数</span></span><br><span class="line">loss = nn.CrossEntropyLoss()</span><br><span class="line">cnn = CNN()</span><br><span class="line"><span class="comment"># 优化器,第一个参数是模型的参数</span></span><br><span class="line">optim = torch.optim.SGD(cnn.parameters(),lr= <span class="number">0.01</span>)</span><br><span class="line"><span class="keyword">for</span> epoch <span class="keyword">in</span> <span class="built_in">range</span>(<span class="number">20</span>):</span><br><span class="line"> running_loss = <span class="number">0.0</span></span><br><span class="line"> <span class="keyword">for</span> data <span class="keyword">in</span> dataloader:</span><br><span class="line"> <span class="comment"># 看outputs和targets长什么样,看选择什么样的损失函数</span></span><br><span class="line"> imgs,targets = data</span><br><span class="line"> output = cnn(imgs)</span><br><span class="line"> <span class="comment"># 计算loss</span></span><br><span class="line"> result_loss = loss(output,targets)</span><br><span class="line"> <span class="comment"># 把上一个循环中每个参数对应的梯度清零</span></span><br><span class="line"> optim.zero_grad()</span><br><span class="line"> <span class="comment"># 反向传播,计算更新参数对应的梯度</span></span><br><span class="line"> result_loss.backward()</span><br><span class="line"> <span class="comment"># 参数调优</span></span><br><span class="line"> optim.step()</span><br><span class="line"> running_loss = running_loss + result_loss</span><br><span class="line"></span><br><span class="line"> <span class="built_in">print</span>(running_loss)</span><br></pre></td></tr></table></figure><h1 id="使用网络模型调参"><a href="#使用网络模型调参" class="headerlink" title="使用网络模型调参"></a>使用网络模型调参</h1><figure class="highlight python"><table><tr><td class="code"><pre><span class="line"><span class="keyword">import</span> torchvision</span><br><span class="line"><span class="keyword">from</span> torch <span class="keyword">import</span> nn</span><br><span class="line"></span><br><span class="line"><span class="comment"># train_data = torchvision.datasets.ImageNet("./dataset_ImageNet",split='train',transform=torchvision.transforms.ToTensor())</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 当为false的时候,加载网络模型——>默认参数,可以将pretrained=False改为weight=none;为True的时候需要从网络中下载每一个参数pretrained=True改成weight=default</span></span><br><span class="line">vgg16_false = torchvision.models.vgg16(pretrained=<span class="literal">False</span>)</span><br><span class="line">vgg16_true = torchvision.models.vgg16(pretrained=<span class="literal">True</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># print(vgg16_true)</span></span><br><span class="line"></span><br><span class="line">train_data = torchvision.datasets.CIFAR10(<span class="string">"./dataset"</span>,train=<span class="literal">True</span>,transform=torchvision.transforms.ToTensor(),download=<span class="literal">True</span>)</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="comment"># 对VGG-16 True进行调参修改,加到最后</span></span><br><span class="line">vgg16_true.add_module(<span class="string">'add_linear'</span>,nn.Linear(<span class="number">1000</span>,<span class="number">10</span>))</span><br><span class="line"><span class="comment"># print(vgg16_true)</span></span><br><span class="line"><span class="comment">#加到相应标签下</span></span><br><span class="line">vgg16_true.classifier.add_module(<span class="string">'add_linear'</span>,nn.Linear(<span class="number">1000</span>,<span class="number">10</span>))</span><br><span class="line"><span class="comment"># print(vgg16_true)</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># print(vgg16_false)</span></span><br><span class="line"><span class="comment">#对某一个步骤进行修改</span></span><br><span class="line">vgg16_false.classifier[<span class="number">6</span>] =nn.Linear(<span class="number">4096</span>,<span class="number">10</span>)</span><br><span class="line"><span class="built_in">print</span>(vgg16_false)</span><br></pre></td></tr></table></figure><h1 id="网络模型的保存与读取"><a href="#网络模型的保存与读取" class="headerlink" title="网络模型的保存与读取"></a>网络模型的保存与读取</h1><figure class="highlight python"><table><tr><td class="code"><pre><span class="line"><span class="keyword">import</span> torch</span><br><span class="line"><span class="keyword">import</span> torchvision</span><br><span class="line"><span class="keyword">from</span> torch <span class="keyword">import</span> nn</span><br><span class="line"></span><br><span class="line">vgg16 = torchvision.models.vgg16(pretrained=<span class="literal">False</span>)</span><br><span class="line"><span class="comment"># 保存方式1,保存网络模型的结构和参数</span></span><br><span class="line"><span class="comment"># 在别的文件使用方法 model = torch.load("vgg16_method1.pth")</span></span><br><span class="line">torch.save(vgg16,<span class="string">"vgg16_method1.pth"</span>)</span><br><span class="line"><span class="comment"># 方式一陷阱</span></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">cnn</span>(nn.Module):</span><br><span class="line"> <span class="keyword">def</span> <span class="title function_">__init__</span>(<span class="params">self</span>):</span><br><span class="line"> <span class="built_in">super</span>(cnn, self).__init__()</span><br><span class="line"> self.conv1 = nn.Conv2d(<span class="number">3</span>,<span class="number">64</span>,kernel_size=<span class="number">3</span>)</span><br><span class="line"></span><br><span class="line"> <span class="keyword">def</span> <span class="title function_">forward</span>(<span class="params">self,x</span>):</span><br><span class="line"> x = self.conv1(x)</span><br><span class="line"> <span class="keyword">return</span> x</span><br><span class="line"></span><br><span class="line">Cnn = cnn()</span><br><span class="line">torch.save(cnn,<span class="string">"Cnn_method1.pth"</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 加载会报错,需要将模型的定义一并放到引用文件中,或者from model_save import *也可以</span></span><br><span class="line">model = torch.load(<span class="string">"Cnn_method1.pth"</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 保存方式2(推荐)</span></span><br><span class="line"><span class="comment"># 将vgg16的参数保存成字典型</span></span><br><span class="line"><span class="comment"># 在别的文件中使用方法 model = torch.load("vgg16_method2.pth")</span></span><br><span class="line">torch.save(vgg16.state_dict(),<span class="string">"vgg16_method2.pth"</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 使用方法</span></span><br><span class="line">vgg16 = torchvision.models.vgg16(pretrained=<span class="literal">False</span>)</span><br><span class="line">vgg16.load_state_dict(torch.load(<span class="string">"vgg16_method2.pth"</span>))</span><br></pre></td></tr></table></figure><h1 id="完整的模型训练套路"><a href="#完整的模型训练套路" class="headerlink" title="完整的模型训练套路"></a>完整的模型训练套路</h1><figure class="highlight python"><table><tr><td class="code"><pre><span class="line"><span class="keyword">import</span> torchvision</span><br><span class="line"><span class="keyword">from</span> torch.utils.data <span class="keyword">import</span> DataLoader</span><br><span class="line"><span class="keyword">from</span> torch.utils.tensorboard <span class="keyword">import</span> SummaryWriter</span><br><span class="line"></span><br><span class="line"><span class="keyword">from</span> model <span class="keyword">import</span> *</span><br><span class="line"></span><br><span class="line"><span class="comment"># 准备数据集</span></span><br><span class="line">train_data = torchvision.datasets.CIFAR10(<span class="string">"./dataset"</span>,train=<span class="literal">True</span>,transform=torchvision.transforms.ToTensor(),download=<span class="literal">True</span>)</span><br><span class="line"></span><br><span class="line">test_data = torchvision.datasets.CIFAR10(<span class="string">"./dataset"</span>,train=<span class="literal">False</span>,transform=torchvision.transforms.ToTensor(),download=<span class="literal">True</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># length 长度</span></span><br><span class="line">train_data_size = <span class="built_in">len</span>(train_data)</span><br><span class="line">test_data_size = <span class="built_in">len</span>(test_data)</span><br><span class="line"><span class="comment"># 字符串格式化写法</span></span><br><span class="line"><span class="built_in">print</span>(<span class="string">"训练数据集的长度为:{}"</span>.<span class="built_in">format</span>(train_data_size))</span><br><span class="line"><span class="built_in">print</span>(<span class="string">"测试数据集的长度为:{}"</span>.<span class="built_in">format</span>(test_data_size))</span><br><span class="line"></span><br><span class="line"><span class="comment"># 利用DataLoader加载数据集</span></span><br><span class="line">train_dataloader = DataLoader(train_data,batch_size=<span class="number">64</span>)</span><br><span class="line">test_dataloader = DataLoader(test_data,batch_size=<span class="number">64</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 创建网络模型</span></span><br><span class="line">cnn = CNN()</span><br><span class="line"></span><br><span class="line"><span class="comment"># 创建损失函数</span></span><br><span class="line">loss_fn = nn.CrossEntropyLoss()</span><br><span class="line"></span><br><span class="line"><span class="comment"># 定义优化器</span></span><br><span class="line">learning_rate = <span class="number">1e-2</span></span><br><span class="line">optimizer = torch.optim.SGD(cnn.parameters(),lr=learning_rate)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 设置训练网络的一些参数</span></span><br><span class="line"><span class="comment"># 记录训练的次数</span></span><br><span class="line">total_train_step = <span class="number">0</span></span><br><span class="line"><span class="comment"># 记录测试的次数</span></span><br><span class="line">total_test_step = <span class="number">0</span></span><br><span class="line"><span class="comment"># 训练的轮数</span></span><br><span class="line">epoch = <span class="number">10</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 添加tensorboard</span></span><br><span class="line">writer = SummaryWriter(<span class="string">"logs"</span>)</span><br><span class="line"></span><br><span class="line"><span class="keyword">for</span> i <span class="keyword">in</span> <span class="built_in">range</span>(epoch):</span><br><span class="line"> <span class="built_in">print</span>(<span class="string">"---------第{}轮训练开始---------"</span>.<span class="built_in">format</span>(i+<span class="number">1</span>))</span><br><span class="line"> <span class="comment"># 训练步骤开始</span></span><br><span class="line"> <span class="comment"># 设置成训练模式,与测试模式一样,只有当网络中有Dropout层、BatchNorm层等的时候才发挥作用</span></span><br><span class="line"> cnn.train()</span><br><span class="line"> <span class="keyword">for</span> data <span class="keyword">in</span> train_dataloader:</span><br><span class="line"> imgs,targets = data</span><br><span class="line"> outputs = cnn(imgs)</span><br><span class="line"> loss = loss_fn(outputs,targets)</span><br><span class="line"> <span class="comment"># 优化器优化模型</span></span><br><span class="line"> <span class="comment"># 梯度清零</span></span><br><span class="line"> optimizer.zero_grad()</span><br><span class="line"> <span class="comment"># 反向传播</span></span><br><span class="line"> loss.backward()</span><br><span class="line"> <span class="comment"># 参数优化</span></span><br><span class="line"> optimizer.step()</span><br><span class="line"> total_train_step=total_train_step+<span class="number">1</span></span><br><span class="line"> <span class="comment"># 加上item()会将tensor值转化为真实的数字,都可以</span></span><br><span class="line"> <span class="keyword">if</span> total_train_step % <span class="number">100</span> == <span class="number">0</span>:</span><br><span class="line"> <span class="built_in">print</span>(<span class="string">"训练次数:{},Loss:{}"</span>.<span class="built_in">format</span>(total_train_step,loss.item()))</span><br><span class="line"> writer.add_scalar(<span class="string">"train_loss"</span>,loss.item(),total_train_step)</span><br><span class="line"></span><br><span class="line"> <span class="comment"># 测试步骤开始</span></span><br><span class="line"> <span class="comment"># 设置成测试模式</span></span><br><span class="line"> cnn.<span class="built_in">eval</span>()</span><br><span class="line"> total_test_loss = <span class="number">0</span></span><br><span class="line"> <span class="comment"># 整体正确的个数</span></span><br><span class="line"> total_accuracy = <span class="number">0</span></span><br><span class="line"> <span class="comment"># 没有梯度,方便调优</span></span><br><span class="line"> <span class="keyword">with</span> torch.no_grad():</span><br><span class="line"> <span class="keyword">for</span> data <span class="keyword">in</span> test_dataloader:</span><br><span class="line"> imgs,targets = data</span><br><span class="line"> outputs = cnn(imgs)</span><br><span class="line"> loss = loss_fn(outputs,targets)</span><br><span class="line"> total_test_loss = total_test_loss+loss</span><br><span class="line"> accuracy = (outputs.argmax(<span class="number">1</span>) == targets).<span class="built_in">sum</span>()</span><br><span class="line"> total_accuracy = total_accuracy + accuracy</span><br><span class="line"> <span class="built_in">print</span>(<span class="string">"整体测试集上的Loss:{}"</span>.<span class="built_in">format</span>(total_test_loss))</span><br><span class="line"> <span class="built_in">print</span>(<span class="string">"整体测试集上的正确率:{}"</span>.<span class="built_in">format</span>(total_accuracy/test_data_size))</span><br><span class="line"> writer.add_scalar(<span class="string">"test_accuracy"</span>,total_accuracy/test_data_size,total_test_step)</span><br><span class="line"> writer.add_scalar(<span class="string">"test_loss"</span>,total_test_loss,total_test_step)</span><br><span class="line"> total_test_step = total_test_step+<span class="number">1</span></span><br><span class="line"></span><br><span class="line"> <span class="comment"># torch.save(cnn,"cnn_{}.pth".format(i))</span></span><br><span class="line"> <span class="comment"># print("模型已保存")</span></span><br><span class="line"> </span><br><span class="line"></span><br><span class="line">writer.close()</span><br></pre></td></tr></table></figure><p>上述的准确率计算的说明:</p><figure class="highlight python"><table><tr><td class="code"><pre><span class="line"><span class="keyword">import</span> torch</span><br><span class="line"></span><br><span class="line">outputs = torch.tensor([[<span class="number">0.1</span>,<span class="number">0.2</span>],</span><br><span class="line"> [<span class="number">0.3</span>,<span class="number">0.4</span>]])</span><br><span class="line"><span class="comment"># 参数为1的话从左往右看,0的话从上往下看</span></span><br><span class="line"><span class="built_in">print</span>(outputs.argmax(<span class="number">1</span>))</span><br><span class="line"><span class="comment"># 看到预测的分类是哪个</span></span><br><span class="line">preds = outputs.argmax(<span class="number">1</span>)</span><br><span class="line"><span class="comment"># 实际的分类是哪个</span></span><br><span class="line">targets = torch.tensor([<span class="number">0</span>,<span class="number">1</span>])</span><br><span class="line"><span class="comment"># 计算出对应位置相等的个数</span></span><br><span class="line"><span class="built_in">print</span>((preds == targets).<span class="built_in">sum</span>())</span><br></pre></td></tr></table></figure><p>代码的CNN模型<未调参>:</p><figure class="highlight python"><table><tr><td class="code"><pre><span class="line"><span class="keyword">import</span> torch</span><br><span class="line"><span class="keyword">from</span> torch <span class="keyword">import</span> nn</span><br><span class="line"></span><br><span class="line"><span class="comment"># 搭建神经网络</span></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">CNN</span>(nn.Module):</span><br><span class="line"> <span class="keyword">def</span> <span class="title function_">__init__</span>(<span class="params">self</span>):</span><br><span class="line"> <span class="built_in">super</span>(CNN, self).__init__()</span><br><span class="line"> self.model=nn.Sequential(</span><br><span class="line"> nn.Conv2d(<span class="number">3</span>,<span class="number">32</span>,<span class="number">5</span>,<span class="number">1</span>,<span class="number">2</span>),</span><br><span class="line"> nn.MaxPool2d(<span class="number">2</span>),</span><br><span class="line"> nn.Conv2d(<span class="number">32</span>,<span class="number">32</span>,<span class="number">5</span>,<span class="number">1</span>,<span class="number">2</span>),</span><br><span class="line"> nn.MaxPool2d(<span class="number">2</span>),</span><br><span class="line"> nn.Conv2d(<span class="number">32</span>,<span class="number">64</span>,<span class="number">5</span>,<span class="number">1</span>,<span class="number">2</span>),</span><br><span class="line"> nn.MaxPool2d(<span class="number">2</span>),</span><br><span class="line"> nn.Flatten(),</span><br><span class="line"> nn.Linear(<span class="number">1024</span>,<span class="number">64</span>),</span><br><span class="line"> nn.Linear(<span class="number">64</span>,<span class="number">10</span>)</span><br><span class="line"> )</span><br><span class="line"></span><br><span class="line"> <span class="keyword">def</span> <span class="title function_">forward</span>(<span class="params">self,x</span>):</span><br><span class="line"> x = self.model(x)</span><br><span class="line"> <span class="keyword">return</span> x</span><br><span class="line"></span><br><span class="line"><span class="comment"># 主函数用来debug,查看参数等</span></span><br><span class="line"><span class="comment"># if __name__ == '__main__':</span></span><br><span class="line"><span class="comment"># cnn =CNN()</span></span><br><span class="line"><span class="comment"># input = torch.ones(64,3,32,32)</span></span><br><span class="line"><span class="comment"># output = cnn(input)</span></span><br><span class="line"><span class="comment"># print(output.shape)</span></span><br></pre></td></tr></table></figure><h1 id="如何使用GPU来训练"><a href="#如何使用GPU来训练" class="headerlink" title="如何使用GPU来训练"></a>如何使用GPU来训练</h1><p>第一种方式:需要网络模型、数据(输入,输出)、损失函数,调用<code>.cuda()</code>返回即可</p><figure class="highlight python"><table><tr><td class="code"><pre><span class="line"><span class="keyword">import</span> torch</span><br><span class="line"><span class="keyword">import</span> torchvision</span><br><span class="line"><span class="keyword">from</span> torch <span class="keyword">import</span> nn</span><br><span class="line"><span class="keyword">from</span> torch.utils.data <span class="keyword">import</span> DataLoader</span><br><span class="line"><span class="keyword">from</span> torch.utils.tensorboard <span class="keyword">import</span> SummaryWriter</span><br><span class="line"><span class="comment"># from model import *</span></span><br><span class="line"><span class="keyword">import</span> time</span><br><span class="line"><span class="comment"># 准备数据集,数据集没有cuda方法</span></span><br><span class="line">train_data = torchvision.datasets.CIFAR10(<span class="string">"./dataset"</span>,train=<span class="literal">True</span>,transform=torchvision.transforms.ToTensor(),download=<span class="literal">True</span>)</span><br><span class="line"></span><br><span class="line">test_data = torchvision.datasets.CIFAR10(<span class="string">"./dataset"</span>,train=<span class="literal">False</span>,transform=torchvision.transforms.ToTensor(),download=<span class="literal">True</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># length 长度</span></span><br><span class="line">train_data_size = <span class="built_in">len</span>(train_data)</span><br><span class="line">test_data_size = <span class="built_in">len</span>(test_data)</span><br><span class="line"><span class="comment"># 字符串格式化写法</span></span><br><span class="line"><span class="built_in">print</span>(<span class="string">"训练数据集的长度为:{}"</span>.<span class="built_in">format</span>(train_data_size))</span><br><span class="line"><span class="built_in">print</span>(<span class="string">"训练数据集的长度为:{}"</span>.<span class="built_in">format</span>(test_data_size))</span><br><span class="line"></span><br><span class="line"><span class="comment"># 利用DataLoader加载数据集</span></span><br><span class="line">train_dataloader = DataLoader(train_data,batch_size=<span class="number">64</span>)</span><br><span class="line">test_dataloader = DataLoader(test_data,batch_size=<span class="number">64</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 创建网络模型</span></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">CNN</span>(nn.Module):</span><br><span class="line"> <span class="keyword">def</span> <span class="title function_">__init__</span>(<span class="params">self</span>):</span><br><span class="line"> <span class="built_in">super</span>(CNN, self).__init__()</span><br><span class="line"> self.model=nn.Sequential(</span><br><span class="line"> nn.Conv2d(<span class="number">3</span>,<span class="number">32</span>,<span class="number">5</span>,<span class="number">1</span>,<span class="number">2</span>),</span><br><span class="line"> nn.MaxPool2d(<span class="number">2</span>),</span><br><span class="line"> nn.Conv2d(<span class="number">32</span>,<span class="number">32</span>,<span class="number">5</span>,<span class="number">1</span>,<span class="number">2</span>),</span><br><span class="line"> nn.MaxPool2d(<span class="number">2</span>),</span><br><span class="line"> nn.Conv2d(<span class="number">32</span>,<span class="number">64</span>,<span class="number">5</span>,<span class="number">1</span>,<span class="number">2</span>),</span><br><span class="line"> nn.MaxPool2d(<span class="number">2</span>),</span><br><span class="line"> nn.Flatten(),</span><br><span class="line"> nn.Linear(<span class="number">1024</span>,<span class="number">64</span>),</span><br><span class="line"> nn.Linear(<span class="number">64</span>,<span class="number">10</span>)</span><br><span class="line"> )</span><br><span class="line"></span><br><span class="line"> <span class="keyword">def</span> <span class="title function_">forward</span>(<span class="params">self,x</span>):</span><br><span class="line"> x = self.model(x)</span><br><span class="line"> <span class="keyword">return</span> x</span><br><span class="line"></span><br><span class="line">cnn = CNN()</span><br><span class="line"><span class="comment"># 网络模型转移到cuda上面,判断cuda是否可用</span></span><br><span class="line"><span class="keyword">if</span> torch.cuda.is_available():</span><br><span class="line"> cnn = cnn.cuda()</span><br><span class="line"></span><br><span class="line"><span class="comment"># 创建损失函数</span></span><br><span class="line">loss_fn = nn.CrossEntropyLoss()</span><br><span class="line"><span class="comment"># 损失函数的cuda</span></span><br><span class="line"><span class="keyword">if</span> torch.cuda.is_available():</span><br><span class="line"> loss_fn = loss_fn.cuda()</span><br><span class="line"></span><br><span class="line"><span class="comment"># 定义优化器</span></span><br><span class="line">learning_rate = <span class="number">1e-2</span></span><br><span class="line">optimizer = torch.optim.SGD(cnn.parameters(),lr=learning_rate)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 设置训练网络的一些参数</span></span><br><span class="line"><span class="comment"># 记录训练的次数</span></span><br><span class="line">total_train_step = <span class="number">0</span></span><br><span class="line"><span class="comment"># 记录测试的次数</span></span><br><span class="line">total_test_step = <span class="number">0</span></span><br><span class="line"><span class="comment"># 训练的轮数</span></span><br><span class="line">epoch = <span class="number">10</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 添加tensorboard</span></span><br><span class="line">writer = SummaryWriter(<span class="string">"logs"</span>)</span><br><span class="line"></span><br><span class="line">start_time = time.time()</span><br><span class="line"><span class="keyword">for</span> i <span class="keyword">in</span> <span class="built_in">range</span>(epoch):</span><br><span class="line"> <span class="built_in">print</span>(<span class="string">"---------第{}轮训练开始---------"</span>.<span class="built_in">format</span>(i+<span class="number">1</span>))</span><br><span class="line"> <span class="comment"># 训练步骤开始</span></span><br><span class="line"> <span class="comment"># 设置成训练模式,与测试模式一样,只有当网络中有Dropout层、BatchNorm层等的时候才发挥作用</span></span><br><span class="line"> cnn.train()</span><br><span class="line"> <span class="keyword">for</span> data <span class="keyword">in</span> train_dataloader:</span><br><span class="line"> imgs,targets = data</span><br><span class="line"> <span class="comment"># 对输入数据进行cuda</span></span><br><span class="line"> <span class="keyword">if</span> torch.cuda.is_available():</span><br><span class="line"> imgs = imgs.cuda()</span><br><span class="line"> targets = targets.cuda()</span><br><span class="line"> outputs = cnn(imgs)</span><br><span class="line"> loss = loss_fn(outputs,targets)</span><br><span class="line"> <span class="comment"># 优化器优化模型</span></span><br><span class="line"> <span class="comment"># 梯度清零</span></span><br><span class="line"> optimizer.zero_grad()</span><br><span class="line"> <span class="comment"># 反向传播</span></span><br><span class="line"> loss.backward()</span><br><span class="line"> <span class="comment"># 参数优化</span></span><br><span class="line"> optimizer.step()</span><br><span class="line"> total_train_step = total_train_step+<span class="number">1</span></span><br><span class="line"> <span class="comment"># 加上item()会将tensor值转化为真实的数字,都可以</span></span><br><span class="line"> <span class="keyword">if</span> total_train_step % <span class="number">100</span> == <span class="number">0</span>:</span><br><span class="line"> end_time = time.time()</span><br><span class="line"> <span class="built_in">print</span>(<span class="string">"耗时:{}"</span>.<span class="built_in">format</span>(end_time-start_time))</span><br><span class="line"> <span class="built_in">print</span>(<span class="string">"训练次数:{},Loss:{}"</span>.<span class="built_in">format</span>(total_train_step,loss.item()))</span><br><span class="line"> writer.add_scalar(<span class="string">"train_loss"</span>,loss.item(),total_train_step)</span><br><span class="line"></span><br><span class="line"> <span class="comment"># 测试步骤开始</span></span><br><span class="line"> <span class="comment"># 设置成测试模式</span></span><br><span class="line"> cnn.<span class="built_in">eval</span>()</span><br><span class="line"> total_test_loss = <span class="number">0</span></span><br><span class="line"> <span class="comment"># 整体正确的个数</span></span><br><span class="line"> total_accuracy = <span class="number">0</span></span><br><span class="line"> <span class="comment"># 没有梯度,方便调优</span></span><br><span class="line"> <span class="keyword">with</span> torch.no_grad():</span><br><span class="line"> <span class="keyword">for</span> data <span class="keyword">in</span> test_dataloader:</span><br><span class="line"> imgs,targets = data</span><br><span class="line"> <span class="keyword">if</span> torch.cuda.is_available():</span><br><span class="line"> imgs = imgs.cuda()</span><br><span class="line"> targets = targets.cuda()</span><br><span class="line"> outputs = cnn(imgs)</span><br><span class="line"> loss = loss_fn(outputs,targets)</span><br><span class="line"> total_test_loss = total_test_loss+loss</span><br><span class="line"> accuracy = (outputs.argmax(<span class="number">1</span>) == targets).<span class="built_in">sum</span>()</span><br><span class="line"> total_accuracy = total_accuracy + accuracy</span><br><span class="line"> <span class="built_in">print</span>(<span class="string">"整体测试集上的Loss:{}"</span>.<span class="built_in">format</span>(total_test_loss))</span><br><span class="line"> <span class="built_in">print</span>(<span class="string">"整体测试集上的正确率:{}"</span>.<span class="built_in">format</span>(total_accuracy/test_data_size))</span><br><span class="line"> writer.add_scalar(<span class="string">"test_accuracy"</span>,total_accuracy/test_data_size,total_test_step)</span><br><span class="line"> writer.add_scalar(<span class="string">"test_loss"</span>,total_test_loss,total_test_step)</span><br><span class="line"> total_test_step = total_test_step+<span class="number">1</span></span><br><span class="line"></span><br><span class="line"> <span class="comment"># torch.save(cnn,"cnn_{}.pth".format(i))</span></span><br><span class="line"> <span class="comment"># print("模型已保存")</span></span><br><span class="line">writer.close()</span><br></pre></td></tr></table></figure><p>第二种GPU训练方式:需要网络模型、数据(输入,输出)、损失函数,调用<code>.to(device)</code>,这个<code>divcie = torch.device("cpu")</code>也可以是<code>device = torch.device("cuda")</code>,电脑有多个显卡的话,可以指定<code>device = torch.device("cuda:n")</code></p><figure class="highlight python"><table><tr><td class="code"><pre><span class="line"><span class="keyword">import</span> torch</span><br><span class="line"><span class="keyword">import</span> torchvision</span><br><span class="line"><span class="keyword">from</span> torch <span class="keyword">import</span> nn</span><br><span class="line"><span class="keyword">from</span> torch.utils.data <span class="keyword">import</span> DataLoader</span><br><span class="line"><span class="keyword">from</span> torch.utils.tensorboard <span class="keyword">import</span> SummaryWriter</span><br><span class="line"><span class="comment"># from model import *</span></span><br><span class="line"><span class="keyword">import</span> time</span><br><span class="line"><span class="comment"># 定义训练设备,如果cuda可用,采用GPU,不可用的话采用CPU</span></span><br><span class="line">device = torch.device(<span class="string">"cuda"</span> <span class="keyword">if</span> torch.cuda.is_avaliable() <span class="keyword">else</span> <span class="string">"cpu"</span>)</span><br><span class="line">train_data = torchvision.datasets.CIFAR10(<span class="string">"./dataset"</span>,train=<span class="literal">True</span>,transform=torchvision.transforms.ToTensor(),download=<span class="literal">True</span>)</span><br><span class="line"></span><br><span class="line">test_data = torchvision.datasets.CIFAR10(<span class="string">"./dataset"</span>,train=<span class="literal">False</span>,transform=torchvision.transforms.ToTensor(),download=<span class="literal">True</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># length 长度</span></span><br><span class="line">train_data_size = <span class="built_in">len</span>(train_data)</span><br><span class="line">test_data_size = <span class="built_in">len</span>(test_data)</span><br><span class="line"><span class="comment"># 字符串格式化写法</span></span><br><span class="line"><span class="built_in">print</span>(<span class="string">"训练数据集的长度为:{}"</span>.<span class="built_in">format</span>(train_data_size))</span><br><span class="line"><span class="built_in">print</span>(<span class="string">"训练数据集的长度为:{}"</span>.<span class="built_in">format</span>(test_data_size))</span><br><span class="line"></span><br><span class="line"><span class="comment"># 利用DataLoader加载数据集</span></span><br><span class="line">train_dataloader = DataLoader(train_data,batch_size=<span class="number">64</span>)</span><br><span class="line">test_dataloader = DataLoader(test_data,batch_size=<span class="number">64</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 创建网络模型</span></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">CNN</span>(nn.Module):</span><br><span class="line"> <span class="keyword">def</span> <span class="title function_">__init__</span>(<span class="params">self</span>):</span><br><span class="line"> <span class="built_in">super</span>(CNN, self).__init__()</span><br><span class="line"> self.model=nn.Sequential(</span><br><span class="line"> nn.Conv2d(<span class="number">3</span>,<span class="number">32</span>,<span class="number">5</span>,<span class="number">1</span>,<span class="number">2</span>),</span><br><span class="line"> nn.MaxPool2d(<span class="number">2</span>),</span><br><span class="line"> nn.Conv2d(<span class="number">32</span>,<span class="number">32</span>,<span class="number">5</span>,<span class="number">1</span>,<span class="number">2</span>),</span><br><span class="line"> nn.MaxPool2d(<span class="number">2</span>),</span><br><span class="line"> nn.Conv2d(<span class="number">32</span>,<span class="number">64</span>,<span class="number">5</span>,<span class="number">1</span>,<span class="number">2</span>),</span><br><span class="line"> nn.MaxPool2d(<span class="number">2</span>),</span><br><span class="line"> nn.Flatten(),</span><br><span class="line"> nn.Linear(<span class="number">1024</span>,<span class="number">64</span>),</span><br><span class="line"> nn.Linear(<span class="number">64</span>,<span class="number">10</span>)</span><br><span class="line"> )</span><br><span class="line"></span><br><span class="line"> <span class="keyword">def</span> <span class="title function_">forward</span>(<span class="params">self,x</span>):</span><br><span class="line"> x = self.model(x)</span><br><span class="line"> <span class="keyword">return</span> x</span><br><span class="line"></span><br><span class="line">cnn = CNN()</span><br><span class="line"><span class="comment"># 网络模型转移到cuda上面,判断cuda是否可用</span></span><br><span class="line"></span><br><span class="line">cnn = cnn.cuda()</span><br><span class="line">cnn = cnn.to(device) <span class="comment"># 也可以不用赋值,直接cnn.to(device)</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 创建损失函数</span></span><br><span class="line">loss_fn = nn.CrossEntropyLoss()</span><br><span class="line"><span class="comment"># 损失函数的cuda</span></span><br><span class="line"></span><br><span class="line">loss_fn = loss_fn.cuda()</span><br><span class="line">loss_fn = loss_fn.to(device)</span><br><span class="line"><span class="comment"># 定义优化器</span></span><br><span class="line">learning_rate = <span class="number">1e-2</span></span><br><span class="line">optimizer = torch.optim.SGD(cnn.parameters(),lr=learning_rate)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 设置训练网络的一些参数</span></span><br><span class="line"><span class="comment"># 记录训练的次数</span></span><br><span class="line">total_train_step = <span class="number">0</span></span><br><span class="line"><span class="comment"># 记录测试的次数</span></span><br><span class="line">total_test_step = <span class="number">0</span></span><br><span class="line"><span class="comment"># 训练的轮数</span></span><br><span class="line">epoch = <span class="number">10</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 添加tensorboard</span></span><br><span class="line">writer = SummaryWriter(<span class="string">"logs"</span>)</span><br><span class="line"></span><br><span class="line">start_time = time.time()</span><br><span class="line"><span class="keyword">for</span> i <span class="keyword">in</span> <span class="built_in">range</span>(epoch):</span><br><span class="line"> <span class="built_in">print</span>(<span class="string">"---------第{}轮训练开始---------"</span>.<span class="built_in">format</span>(i+<span class="number">1</span>))</span><br><span class="line"> <span class="comment"># 训练步骤开始</span></span><br><span class="line"> <span class="comment"># 设置成训练模式,与测试模式一样,只有当网络中有Dropout层、BatchNorm层等的时候才发挥作用</span></span><br><span class="line"> cnn.train()</span><br><span class="line"> <span class="keyword">for</span> data <span class="keyword">in</span> train_dataloader:</span><br><span class="line"> imgs,targets = data</span><br><span class="line"> <span class="comment"># 对输入数据调用硬件</span></span><br><span class="line"> imgs = imgs.to(device)</span><br><span class="line"> targets = targets.to(device)</span><br><span class="line"> outputs = cnn(imgs)</span><br><span class="line"> loss = loss_fn(outputs,targets)</span><br><span class="line"> <span class="comment"># 优化器优化模型</span></span><br><span class="line"> <span class="comment"># 梯度清零</span></span><br><span class="line"> optimizer.zero_grad()</span><br><span class="line"> <span class="comment"># 反向传播</span></span><br><span class="line"> loss.backward()</span><br><span class="line"> <span class="comment"># 参数优化</span></span><br><span class="line"> optimizer.step()</span><br><span class="line"> total_train_step = total_train_step+<span class="number">1</span></span><br><span class="line"> <span class="comment"># 加上item()会将tensor值转化为真实的数字,都可以</span></span><br><span class="line"> <span class="keyword">if</span> total_train_step % <span class="number">100</span> == <span class="number">0</span>:</span><br><span class="line"> end_time = time.time()</span><br><span class="line"> <span class="built_in">print</span>(<span class="string">"耗时:{}"</span>.<span class="built_in">format</span>(end_time-start_time))</span><br><span class="line"> <span class="built_in">print</span>(<span class="string">"训练次数:{},Loss:{}"</span>.<span class="built_in">format</span>(total_train_step,loss.item()))</span><br><span class="line"> writer.add_scalar(<span class="string">"train_loss"</span>,loss.item(),total_train_step)</span><br><span class="line"></span><br><span class="line"> <span class="comment"># 测试步骤开始</span></span><br><span class="line"> <span class="comment"># 设置成测试模式</span></span><br><span class="line"> cnn.<span class="built_in">eval</span>()</span><br><span class="line"> total_test_loss = <span class="number">0</span></span><br><span class="line"> <span class="comment"># 整体正确的个数</span></span><br><span class="line"> total_accuracy = <span class="number">0</span></span><br><span class="line"> <span class="comment"># 没有梯度,方便调优</span></span><br><span class="line"> <span class="keyword">with</span> torch.no_grad():</span><br><span class="line"> <span class="keyword">for</span> data <span class="keyword">in</span> test_dataloader:</span><br><span class="line"> imgs,targets = data</span><br><span class="line"> imgs = imgs.to(device)</span><br><span class="line"> targets = targets.to(device)</span><br><span class="line"> outputs = cnn(imgs)</span><br><span class="line"> loss = loss_fn(outputs,targets)</span><br><span class="line"> total_test_loss = total_test_loss+loss</span><br><span class="line"> accuracy = (outputs.argmax(<span class="number">1</span>) == targets).<span class="built_in">sum</span>()</span><br><span class="line"> total_accuracy = total_accuracy + accuracy</span><br><span class="line"> <span class="built_in">print</span>(<span class="string">"整体测试集上的Loss:{}"</span>.<span class="built_in">format</span>(total_test_loss))</span><br><span class="line"> <span class="built_in">print</span>(<span class="string">"整体测试集上的正确率:{}"</span>.<span class="built_in">format</span>(total_accuracy/test_data_size))</span><br><span class="line"> writer.add_scalar(<span class="string">"test_accuracy"</span>,total_accuracy/test_data_size,total_test_step)</span><br><span class="line"> writer.add_scalar(<span class="string">"test_loss"</span>,total_test_loss,total_test_step)</span><br><span class="line"> total_test_step = total_test_step+<span class="number">1</span></span><br><span class="line"></span><br><span class="line"> <span class="comment"># torch.save(cnn,"cnn_{}.pth".format(i))</span></span><br><span class="line"> <span class="comment"># print("模型已保存")</span></span><br><span class="line">writer.close()</span><br></pre></td></tr></table></figure><h1 id="完整的模型验证(测试,demo)套路"><a href="#完整的模型验证(测试,demo)套路" class="headerlink" title="完整的模型验证(测试,demo)套路"></a>完整的模型验证(测试,demo)套路</h1><p>核心是利用已经训练好的模型,然后给它提供输入。</p><p>例如:</p><figure class="highlight python"><table><tr><td class="code"><pre><span class="line"><span class="keyword">import</span> torch</span><br><span class="line"><span class="keyword">import</span> torchvision</span><br><span class="line"><span class="keyword">from</span> PIL <span class="keyword">import</span> Image</span><br><span class="line"><span class="keyword">from</span> torch <span class="keyword">import</span> nn</span><br><span class="line"></span><br><span class="line">image_path = <span class="string">"./img/dog.jpg"</span></span><br><span class="line">image = Image.<span class="built_in">open</span>(image_path)</span><br><span class="line"><span class="comment"># 因为png格式是4个通道,除了RGB三通道外,还有一个透明度通道</span></span><br><span class="line"><span class="comment"># 调用image = image.convert('RGB'),保留颜色通道,加上这一步之后,不管是png还是jpg图片都可以运行</span></span><br><span class="line">image = image.convert(<span class="string">'RGB'</span>)</span><br><span class="line">transform = torchvision.transforms.Compose([torchvision.transforms.Resize((<span class="number">32</span>,<span class="number">32</span>)),</span><br><span class="line"> torchvision.transforms.ToTensor()])</span><br><span class="line">image = transform(image)</span><br><span class="line"><span class="built_in">print</span>(image.shape)</span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">CNN</span>(nn.Module):</span><br><span class="line"> <span class="keyword">def</span> <span class="title function_">__init__</span>(<span class="params">self</span>):</span><br><span class="line"> <span class="built_in">super</span>(CNN, self).__init__()</span><br><span class="line"> self.model=nn.Sequential(</span><br><span class="line"> nn.Conv2d(<span class="number">3</span>,<span class="number">32</span>,<span class="number">5</span>,<span class="number">1</span>,<span class="number">2</span>),</span><br><span class="line"> nn.MaxPool2d(<span class="number">2</span>),</span><br><span class="line"> nn.Conv2d(<span class="number">32</span>,<span class="number">32</span>,<span class="number">5</span>,<span class="number">1</span>,<span class="number">2</span>),</span><br><span class="line"> nn.MaxPool2d(<span class="number">2</span>),</span><br><span class="line"> nn.Conv2d(<span class="number">32</span>,<span class="number">64</span>,<span class="number">5</span>,<span class="number">1</span>,<span class="number">2</span>),</span><br><span class="line"> nn.MaxPool2d(<span class="number">2</span>),</span><br><span class="line"> nn.Flatten(),</span><br><span class="line"> nn.Linear(<span class="number">1024</span>,<span class="number">64</span>),</span><br><span class="line"> nn.Linear(<span class="number">64</span>,<span class="number">10</span>)</span><br><span class="line"> )</span><br><span class="line"></span><br><span class="line"> <span class="keyword">def</span> <span class="title function_">forward</span>(<span class="params">self,x</span>):</span><br><span class="line"> x = self.model(x)</span><br><span class="line"> <span class="keyword">return</span> x</span><br><span class="line"></span><br><span class="line"><span class="comment"># 因为train文件里面我没有保存模型,所以这里我是没法运行的,要先运行train文件</span></span><br><span class="line"><span class="comment"># 采用GPU训练的模型,在CPU加载的过程中,要映射到CPU上</span></span><br><span class="line">model = torch.load(<span class="string">"cnn_9.pth"</span>,map_location=torch.device(<span class="string">'cpu'</span>))</span><br><span class="line"><span class="comment"># model的输入要是4维的</span></span><br><span class="line">torch.reshape(image,(<span class="number">1</span>,<span class="number">3</span>,<span class="number">32</span>,<span class="number">32</span>))</span><br><span class="line"><span class="comment"># 记得写上</span></span><br><span class="line">model.<span class="built_in">eval</span>()</span><br><span class="line"><span class="keyword">with</span> torch.no_grad():</span><br><span class="line"> output = model(image)</span><br><span class="line"></span><br><span class="line"><span class="built_in">print</span>(output.argmax(<span class="number">1</span>))</span><br></pre></td></tr></table></figure>]]></content>
<tags>
<tag> 深度学习 </tag>
<tag> 框架 </tag>
</tags>
</entry>
<entry>
<title>python</title>
<link href="/2023/01/08/python/"/>
<url>/2023/01/08/python/</url>
<content type="html"><![CDATA[<h1 id="基础"><a href="#基础" class="headerlink" title="基础"></a>基础</h1><p>首先记录pycharm的一些快捷键:</p><ul><li><code>shift</code>+<code>F10</code>:运行py文件</li><li><code>CTRL</code>:查看类或函数的使用方法</li><li><code>CTRL</code>+<code>/</code>进行注释</li><li><code>CTRL</code>+<code>P</code>查看当前函数所需要的参数</li><li><code>CTRL</code>+<code>D</code>复制上一行的代码</li><li><code>CTRL</code>+<code>C</code>退出程序</li></ul><h2 id="python的IO"><a href="#python的IO" class="headerlink" title="python的IO"></a>python的IO</h2><ul><li>输出:<code>print()</code>,例如<code>print('hello world')</code>,<code>print()</code>可以接受多个字符串,字符串之间用<code>,</code>隔开,就可以将字符串连成一串输出。<code>print()</code>会依次打印各个字符串,遇到<code>,</code>就会输出一个空格。</li><li>输入:<code>input()</code>,例如<code>name=input()</code>。<code>input()</code>有一个字符串参数,例如<code>name = input('please enter your name: ')</code>,input的返回值类型是str,会首先输出字符串参数的内容。</li></ul><h2 id="python的数据类型"><a href="#python的数据类型" class="headerlink" title="python的数据类型"></a>python的数据类型</h2><ul><li><p>整数:为了清楚,可以在数字中间以<code>_</code>分隔,例如<code>10_000_000_000</code>和<code>10000000000</code>是一样的。</p></li><li><p>字符串:以<code>'</code>或<code>"</code>括起来的任意文本。如果<code>'</code>括起来内部还有<code>'</code>这种的话,就要加转义字符<code>\</code>。python可以用<code>r''</code>表示<code>''</code>内部的字符串不转义。</p></li><li><p>布尔值:只有<code>True</code>和<code>False</code>两种值。可以直接用<code>True</code>和<code>False</code>表示布尔值,也可以通过布尔运算计算出来。布尔值可以用<code>and</code>、<code>or</code>和<code>not</code>运算。</p></li><li><p>空值:空值用<code>None</code>表示,不能理解为<code>0</code>,<code>0</code>是有意义的,而<code>None</code>是一个特殊的空值。</p></li><li><p><strong>变量:python是动态语言,可以把任意数据类型赋值给变量,同一个变量可以反复赋值,而且可以是不同类型的变量。</strong></p></li><li><p>常量:常用大写变量名表示常量,但是这个常量仍然是一个变量,因为python没有任何机制能够保证常量是常量,只是我们自己心里的一个默认罢了。</p></li><li><p><code>list</code>列表:<code>list</code>是一种有序的集合,可以随时添加和删除其中的元素。例如:</p><figure class="highlight python"><table><tr><td class="code"><pre><span class="line"><span class="meta">>>> </span>classmates = [<span class="string">'Michael'</span>, <span class="string">'Bob'</span>, <span class="string">'Tracy'</span>]</span><br><span class="line"><span class="meta">>>> </span>classmates</span><br><span class="line">[<span class="string">'Michael'</span>, <span class="string">'Bob'</span>, <span class="string">'Tracy'</span>]</span><br></pre></td></tr></table></figure><ul><li><p>变量<code>classmates</code>就是一个list。用<code>len()</code>函数可以获得list元素的个数。</p><figure class="highlight python"><table><tr><td class="code"><pre><span class="line"><span class="meta">>>> </span><span class="built_in">len</span>(classmates)</span><br><span class="line"><span class="number">3</span></span><br></pre></td></tr></table></figure></li><li><p>用索引来访问list中每一个位置的元素,记得索引是从<code>0</code>开始的,最后一个元素的索引是<code>len(classmates) - 1</code>。如果要取最后一个元素,除了计算索引位置外,还可以用<code>-1</code>做索引,直接获取最后一个元素,以此类推,可以获取倒数第2个、倒数第3个。当索引超出了范围时,Python会报一个<code>IndexError</code>错误。</p><figure class="highlight python"><table><tr><td class="code"><pre><span class="line"><span class="meta">>>> </span>classmates[<span class="number">0</span>]</span><br><span class="line"><span class="string">'Michael'</span></span><br><span class="line"><span class="meta">>>> </span>classmates[<span class="number">1</span>]</span><br><span class="line"><span class="string">'Bob'</span></span><br><span class="line"><span class="meta">>>> </span>classmates[<span class="number">2</span>]</span><br><span class="line"><span class="string">'Tracy'</span></span><br><span class="line"><span class="meta">>>> </span>classmates[<span class="number">3</span>]</span><br><span class="line">Traceback (most recent call last):</span><br><span class="line"> File <span class="string">"<stdin>"</span>, line <span class="number">1</span>, <span class="keyword">in</span> <module></span><br><span class="line">IndexError: <span class="built_in">list</span> index out of <span class="built_in">range</span></span><br><span class="line"><span class="meta">>>> </span>classmates[-<span class="number">1</span>]</span><br><span class="line"><span class="string">'Tracy'</span></span><br><span class="line"><span class="meta">>>> </span>classmates[-<span class="number">2</span>]</span><br><span class="line"><span class="string">'Bob'</span></span><br><span class="line"><span class="meta">>>> </span>classmates[-<span class="number">3</span>]</span><br><span class="line"><span class="string">'Michael'</span></span><br><span class="line"><span class="meta">>>> </span>classmates[-<span class="number">4</span>]</span><br><span class="line">Traceback (most recent call last):</span><br><span class="line"> File <span class="string">"<stdin>"</span>, line <span class="number">1</span>, <span class="keyword">in</span> <module></span><br><span class="line">IndexError: <span class="built_in">list</span> index out of <span class="built_in">range</span></span><br></pre></td></tr></table></figure></li><li><p>list是一个可变的有序表,所以,可以往list中追加元素到末尾。</p><figure class="highlight python"><table><tr><td class="code"><pre><span class="line"><span class="meta">>>> </span>classmates.append(<span class="string">'Adam'</span>)</span><br><span class="line"><span class="meta">>>> </span>classmates</span><br><span class="line">[<span class="string">'Michael'</span>, <span class="string">'Bob'</span>, <span class="string">'Tracy'</span>, <span class="string">'Adam'</span>]</span><br></pre></td></tr></table></figure></li><li><p>也可以把元素插入到指定的位置,比如索引号为<code>1</code>的位置。</p><figure class="highlight python"><table><tr><td class="code"><pre><span class="line"><span class="meta">>>> </span>classmates.insert(<span class="number">1</span>, <span class="string">'Jack'</span>)</span><br><span class="line"><span class="meta">>>> </span>classmates</span><br><span class="line">[<span class="string">'Michael'</span>, <span class="string">'Jack'</span>, <span class="string">'Bob'</span>, <span class="string">'Tracy'</span>, <span class="string">'Adam'</span>]</span><br></pre></td></tr></table></figure></li><li><p>要删除list末尾的元素,用<code>pop()</code>方法。</p><figure class="highlight python"><table><tr><td class="code"><pre><span class="line"><span class="meta">>>> </span>classmates.pop()</span><br><span class="line"><span class="string">'Adam'</span></span><br><span class="line"><span class="meta">>>> </span>classmates</span><br><span class="line">[<span class="string">'Michael'</span>, <span class="string">'Jack'</span>, <span class="string">'Bob'</span>, <span class="string">'Tracy'</span>]</span><br></pre></td></tr></table></figure></li><li><p>要删除指定位置的元素,用<code>pop(i)</code>方法,其中<code>i</code>是索引位置。</p><figure class="highlight python"><table><tr><td class="code"><pre><span class="line"><span class="meta">>>> </span>classmates.pop(<span class="number">1</span>)</span><br><span class="line"><span class="string">'Jack'</span></span><br><span class="line"><span class="meta">>>> </span>classmates</span><br><span class="line">[<span class="string">'Michael'</span>, <span class="string">'Bob'</span>, <span class="string">'Tracy'</span>]</span><br></pre></td></tr></table></figure></li><li><p>要把某个元素替换成别的元素,可以直接赋值给对应的索引位置。</p><figure class="highlight python"><table><tr><td class="code"><pre><span class="line"><span class="meta">>>> </span>classmates[<span class="number">1</span>] = <span class="string">'Sarah'</span></span><br><span class="line"><span class="meta">>>> </span>classmates</span><br><span class="line">[<span class="string">'Michael'</span>, <span class="string">'Sarah'</span>, <span class="string">'Tracy'</span>]</span><br></pre></td></tr></table></figure></li><li><p>list里面的元素的数据类型也可以不同。</p><figure class="highlight python"><table><tr><td class="code"><pre><span class="line"><span class="meta">>>> </span>L = [<span class="string">'Apple'</span>, <span class="number">123</span>, <span class="literal">True</span>]</span><br></pre></td></tr></table></figure></li><li><p>list元素也可以是另一个list。</p><figure class="highlight python"><table><tr><td class="code"><pre><span class="line"><span class="meta">>>> </span>s = [<span class="string">'python'</span>, <span class="string">'java'</span>, [<span class="string">'asp'</span>, <span class="string">'php'</span>], <span class="string">'scheme'</span>]</span><br><span class="line"><span class="meta">>>> </span><span class="built_in">len</span>(s)</span><br><span class="line"><span class="number">4</span></span><br></pre></td></tr></table></figure><p>要注意<code>s</code>只有4个元素,其中<code>s[2]</code>又是一个list,如果拆开写就更容易理解了:</p><figure class="highlight python"><table><tr><td class="code"><pre><span class="line"><span class="meta">>>> </span>p = [<span class="string">'asp'</span>, <span class="string">'php'</span>]</span><br><span class="line"><span class="meta">>>> </span>s = [<span class="string">'python'</span>, <span class="string">'java'</span>, p, <span class="string">'scheme'</span>]</span><br></pre></td></tr></table></figure><p>要拿到<code>'php'</code>可以写<code>p[1]</code>或者<code>s[2][1]</code>,因此<code>s</code>可以看成是一个二维数组。</p></li><li><p>如果一个list中一个元素也没有,就是一个空的list,它的长度为0。</p><figure class="highlight python"><table><tr><td class="code"><pre><span class="line"><span class="meta">>>> </span>L = []</span><br><span class="line"><span class="meta">>>> </span><span class="built_in">len</span>(L)</span><br><span class="line"><span class="number">0</span></span><br></pre></td></tr></table></figure></li></ul></li><li><p>tuple:另一种有序列表叫元组:tuple。tuple和list非常类似,但是tuple一旦初始化就不能修改。</p><ul><li><pre><code class="python">>>> classmates = ('Michael', 'Bob', 'Tracy')<figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line"></span><br><span class="line"> classmates这个tuple不能变了,它也没有append(),insert()这样的方法。其他获取元素的方法和list是一样的,你可以正常地使用`classmates[0]`,`classmates[-1]`,但不能赋值成另外的元素。</span><br><span class="line"></span><br><span class="line"> </span><br><span class="line"></span><br><span class="line">- 当你定义一个tuple时,在定义的时候,tuple的元素就必须被确定下来。</span><br><span class="line"></span><br><span class="line"> ```python</span><br><span class="line"> >>> t = (1, 2)</span><br><span class="line"> >>> t</span><br><span class="line"> (1, 2)</span><br></pre></td></tr></table></figure></code></pre></li><li><p>如果要定义一个空的tuple,可以写成<code>()</code></p><figure class="highlight python"><table><tr><td class="code"><pre><span class="line"><span class="meta">>>> </span>t = ()</span><br><span class="line"><span class="meta">>>> </span>t</span><br><span class="line">()</span><br></pre></td></tr></table></figure></li><li><p>但是,要定义一个只有1个元素的tuple,如果这么定义:</p><figure class="highlight python"><table><tr><td class="code"><pre><span class="line"><span class="meta">>>> </span>t = (<span class="number">1</span>)</span><br><span class="line"><span class="meta">>>> </span>t</span><br><span class="line"><span class="number">1</span></span><br></pre></td></tr></table></figure></li><li><p>定义的不是tuple,是<code>1</code>这个数!这是因为括号<code>()</code>既可以表示tuple,又可以表示数学公式中的小括号,这就产生了歧义,因此,Python规定,这种情况下,按小括号进行计算,计算结果自然是<code>1</code>。所以,只有1个元素的tuple定义时必须加一个逗号<code>,</code>,来消除歧义:</p><figure class="highlight python"><table><tr><td class="code"><pre><span class="line"><span class="meta">>>> </span>t = (<span class="number">1</span>,)</span><br><span class="line"><span class="meta">>>> </span>t</span><br><span class="line">(<span class="number">1</span>,)</span><br></pre></td></tr></table></figure><p>Python在显示只有1个元素的tuple时,也会加一个逗号<code>,</code>。</p></li><li><p>一个”可变的“tuple:</p><figure class="highlight python"><table><tr><td class="code"><pre><span class="line"><span class="meta">>>> </span>t = (<span class="string">'a'</span>, <span class="string">'b'</span>, [<span class="string">'A'</span>, <span class="string">'B'</span>])</span><br><span class="line"><span class="meta">>>> </span>t[<span class="number">2</span>][<span class="number">0</span>] = <span class="string">'X'</span></span><br><span class="line"><span class="meta">>>> </span>t[<span class="number">2</span>][<span class="number">1</span>] = <span class="string">'Y'</span></span><br><span class="line"><span class="meta">>>> </span>t</span><br><span class="line">(<span class="string">'a'</span>, <span class="string">'b'</span>, [<span class="string">'X'</span>, <span class="string">'Y'</span>])</span><br></pre></td></tr></table></figure></li><li><p>这个tuple定义的时候有3个元素,分别是<code>'a'</code>,<code>'b'</code>和一个list。</p></li></ul></li></ul><img src="/2023/01/08/python/tuple1.png" alt="tuple1" style="zoom:67%;"><p>当我们把list的元素<code>'A'</code>和<code>'B'</code>修改为<code>'X'</code>和<code>'Y'</code>后,tuple变为:</p><p><img src="/2023/01/08/python/tuple2.png" alt="tuple2" style="zoom:67%;"></p><p>表面上看,tuple的元素确实变了,但其实变的不是tuple的元素,而是list的元素。tuple一开始指向的list并没有改成别的list,所以,tuple所谓的“不变”是说,tuple的每个元素,指向永远不变。即向<code>'a'</code>,就不能改成指向<code>'b'</code>,指向一个list,就不能改成指向其他对象,但指向的这个list本身是可变的。</p><p><strong>Python的整数没有大小限制,Python的浮点数也没有大小限制,但是超出一定范围就直接表示为<code>inf</code>(无限大)。</strong></p><ul><li>类型转换例如:int(str)</li></ul><h2 id="python的计算"><a href="#python的计算" class="headerlink" title="python的计算"></a>python的计算</h2><ul><li>一种除法是<code>/</code>,<code>/</code>计算结果是浮点数。</li><li>还有一种除法是<code>//</code>,两个整数的除法仍然是整数。</li><li>取余<code>%</code></li></ul><h2 id="字符编码"><a href="#字符编码" class="headerlink" title="字符编码"></a>字符编码</h2><p>python的字符串以Unicode编码,对于单个字符编码,<code>ord()</code>函数获取字符的整数表示,<code>chr()</code>函数把编码转换为对应的字符。python对<code>bytes</code>类型的数据用带b前缀的单引号或双引号表示,例如<code>x=b'ABC'</code>。以Unicode表示的<code>str</code>通过<code>encode()</code>方法可以编码为指定的<code>bytes</code>。要把<code>bytes</code>变为<code>str</code>,就需要用<code>decode()</code>方法。要计算<code>str</code>包含多少个字符,可以用<code>len()</code>函数,<code>len()</code>函数计算的是<code>str</code>的字符数,如果换成<code>bytes</code>,<code>len()</code>函数就计算字节数。</p><h2 id="格式化字符串"><a href="#格式化字符串" class="headerlink" title="格式化字符串"></a>格式化字符串</h2><p>python使用<code>%</code>格式化字符串,有几个<code>%?</code>占位符,后面就跟着几个变量或者值,如果只有一个<code>%?</code>,括号可以省略。例如:</p><figure class="highlight python"><table><tr><td class="code"><pre><span class="line"><span class="string">'Hello, %s'</span> % <span class="string">'world'</span></span><br><span class="line"><span class="string">'Hi, %s, you have $%d.'</span> % (<span class="string">'Michael'</span>, <span class="number">1000000</span>)</span><br></pre></td></tr></table></figure><p>常见占位符:</p><table><thead><tr><th>占位符</th><th>替换内容</th></tr></thead><tbody><tr><td>%d</td><td>整数</td></tr><tr><td>%f</td><td>浮点数</td></tr><tr><td>%s</td><td>字符串</td></tr><tr><td>%x</td><td>十六进制整数</td></tr></tbody></table><p>如果你不太确定应该用什么,<code>%s</code>永远起作用,它会把任何数据类型转换为字符串。有些时候,字符串里面的<code>%</code>是一个普通字符怎么办?这个时候就需要转义,用<code>%%</code>来表示一个<code>%</code>。</p><p>另一种格式化字符串的方法是使用字符串的<code>format()</code>方法,它会用传入的参数依次替换字符串内的占位符<code>{0}</code>、<code>{1}</code>……,不过这种方式写起来比%要麻烦得多:</p><figure class="highlight python"><table><tr><td class="code"><pre><span class="line"><span class="string">'Hello, {0}, 成绩提升了 {1:.1f}%'</span>.<span class="built_in">format</span>(<span class="string">'小明'</span>, <span class="number">17.125</span>)</span><br><span class="line"><span class="string">'Hello, 小明, 成绩提升了 17.1%'</span></span><br></pre></td></tr></table></figure><p>最后一种格式化字符串的方法是使用以<code>f</code>开头的字符串,称之为<code>f-string</code>,它和普通字符串不同之处在于,字符串如果包含<code>{xxx}</code>,就会以对应的变量替换:</p><figure class="highlight python"><table><tr><td class="code"><pre><span class="line"><span class="meta">>>> </span>r = <span class="number">2.5</span></span><br><span class="line"><span class="meta">>>> </span>s = <span class="number">3.14</span> * r ** <span class="number">2</span></span><br><span class="line"><span class="meta">>>> </span><span class="built_in">print</span>(<span class="string">f'The area of a circle with radius <span class="subst">{r}</span> is <span class="subst">{s:<span class="number">.2</span>f}</span>'</span>)</span><br><span class="line">The area of a circle <span class="keyword">with</span> radius <span class="number">2.5</span> <span class="keyword">is</span> <span class="number">19.62</span></span><br></pre></td></tr></table></figure><p>上述代码中,<code>{r}</code>被变量<code>r</code>的值替换,<code>{s:.2f}</code>被变量<code>s</code>的值替换,并且<code>:</code>后面的<code>.2f</code>指定了格式化参数(即保留两位小数),因此,<code>{s:.2f}</code>的替换结果是<code>19.62</code>。</p><h2 id="If相关语句"><a href="#If相关语句" class="headerlink" title="If相关语句"></a>If相关语句</h2><ul><li><p>python中的if语句通过缩进表明后面的语句是同一个代码块的,这点和其他语言不同,并且要在条件之后加上一个冒号。例如:</p><figure class="highlight python"><table><tr><td class="code"><pre><span class="line">age = <span class="number">3</span></span><br><span class="line"><span class="keyword">if</span> age >= <span class="number">18</span>:</span><br><span class="line"> <span class="built_in">print</span>(<span class="string">'your age is'</span>, age)</span><br><span class="line"> <span class="built_in">print</span>(<span class="string">'adult'</span>)</span><br><span class="line"><span class="keyword">else</span>:</span><br><span class="line"> <span class="built_in">print</span>(<span class="string">'your age is'</span>, age)</span><br><span class="line"> <span class="built_in">print</span>(<span class="string">'teenager'</span>)</span><br></pre></td></tr></table></figure></li><li><p>python中的else-if语句写法如,这里elif就是else if的缩写:</p><figure class="highlight python"><table><tr><td class="code"><pre><span class="line">age = <span class="number">3</span></span><br><span class="line"><span class="keyword">if</span> age >= <span class="number">18</span>:</span><br><span class="line"> <span class="built_in">print</span>(<span class="string">'adult'</span>)</span><br><span class="line"><span class="keyword">elif</span> age >= <span class="number">6</span>:</span><br><span class="line"> <span class="built_in">print</span>(<span class="string">'teenager'</span>)</span><br><span class="line"><span class="keyword">else</span>:</span><br><span class="line"> <span class="built_in">print</span>(<span class="string">'kid'</span>)</span><br></pre></td></tr></table></figure></li></ul><h2 id="循环语句"><a href="#循环语句" class="headerlink" title="循环语句"></a>循环语句</h2><p>python循环有两种。</p><ul><li><p>for…in循环,依次把list或tuple中的每个元素迭代出来.</p><figure class="highlight python"><table><tr><td class="code"><pre><span class="line">names = [<span class="string">'Michael'</span>, <span class="string">'Bob'</span>, <span class="string">'Tracy'</span>]</span><br><span class="line"><span class="keyword">for</span> name <span class="keyword">in</span> names:</span><br><span class="line"> <span class="built_in">print</span>(name)</span><br></pre></td></tr></table></figure><p><code>for x in...</code>循环就是把每个元素代入变量x,然后执行缩进块的语句,如下例子较为麻烦。</p><figure class="highlight python"><table><tr><td class="code"><pre><span class="line"><span class="built_in">sum</span> = <span class="number">0</span></span><br><span class="line"><span class="keyword">for</span> x <span class="keyword">in</span> [<span class="number">1</span>, <span class="number">2</span>, <span class="number">3</span>, <span class="number">4</span>, <span class="number">5</span>, <span class="number">6</span>, <span class="number">7</span>, <span class="number">8</span>, <span class="number">9</span>, <span class="number">10</span>]:</span><br><span class="line"> <span class="built_in">sum</span> = <span class="built_in">sum</span> + x</span><br><span class="line"><span class="built_in">print</span>(<span class="built_in">sum</span>)</span><br></pre></td></tr></table></figure><p>python有<code>range()</code>函数,可以生成整数序列,再通过<code>list()</code>函数可以转换为list</p><figure class="highlight python"><table><tr><td class="code"><pre><span class="line"><span class="built_in">sum</span> = <span class="number">0</span></span><br><span class="line"><span class="keyword">for</span> x <span class="keyword">in</span> <span class="built_in">range</span>(<span class="number">101</span>): <span class="comment">#这是从0-100,也可以写成list(range(101)),一种是列表,一种是序列</span></span><br><span class="line"> <span class="built_in">sum</span> = <span class="built_in">sum</span> + x</span><br><span class="line"><span class="built_in">print</span>(<span class="built_in">sum</span>)</span><br></pre></td></tr></table></figure></li><li><p>while循环</p><p>例如,计算100以内奇数之和:</p><figure class="highlight python"><table><tr><td class="code"><pre><span class="line"><span class="built_in">sum</span> = <span class="number">0</span></span><br><span class="line">n = <span class="number">99</span></span><br><span class="line"><span class="keyword">while</span> n > <span class="number">0</span>:</span><br><span class="line"> <span class="built_in">sum</span> = <span class="built_in">sum</span> + n</span><br><span class="line"> n = n - <span class="number">2</span></span><br><span class="line"><span class="built_in">print</span>(<span class="built_in">sum</span>)</span><br></pre></td></tr></table></figure></li><li><p>break语句,提前结束循环,用法一致</p><figure class="highlight python"><table><tr><td class="code"><pre><span class="line">n = <span class="number">1</span></span><br><span class="line"><span class="keyword">while</span> n <= <span class="number">100</span>:</span><br><span class="line"> <span class="keyword">if</span> n > <span class="number">10</span>: <span class="comment"># 当n = 11时,条件满足,执行break语句</span></span><br><span class="line"> <span class="keyword">break</span> <span class="comment"># break语句会结束当前循环</span></span><br><span class="line"> <span class="built_in">print</span>(n)</span><br><span class="line"> n = n + <span class="number">1</span></span><br><span class="line"><span class="built_in">print</span>(<span class="string">'END'</span>)</span><br></pre></td></tr></table></figure></li><li><p>continue语句,用法与其他语言一致</p><figure class="highlight python"><table><tr><td class="code"><pre><span class="line">n = <span class="number">0</span></span><br><span class="line"><span class="keyword">while</span> n < <span class="number">10</span>:</span><br><span class="line"> n = n + <span class="number">1</span></span><br><span class="line"> <span class="keyword">if</span> n % <span class="number">2</span> == <span class="number">0</span>: <span class="comment"># 如果n是偶数,执行continue语句</span></span><br><span class="line"> <span class="keyword">continue</span> <span class="comment"># continue语句会直接继续下一轮循环,后续的print()语句不会执行</span></span><br><span class="line"> <span class="built_in">print</span>(n)</span><br></pre></td></tr></table></figure></li></ul><h2 id="dict和set"><a href="#dict和set" class="headerlink" title="dict和set"></a>dict和set</h2><p>字典dict其实就是map,是key-value的映射,特点是查找速度快。dict的key是不可变对象如字符串、整数,list是可变的</p><p>例子:</p><figure class="highlight python"><table><tr><td class="code"><pre><span class="line"><span class="comment"># 初始化,将数据放入dict</span></span><br><span class="line"><span class="meta">>>> </span>d = {<span class="string">'Michael'</span>: <span class="number">95</span>, <span class="string">'Bob'</span>: <span class="number">75</span>, <span class="string">'Tracy'</span>: <span class="number">85</span>}</span><br><span class="line"><span class="meta">>>> </span>d[<span class="string">'Michael'</span>]</span><br><span class="line"><span class="number">95</span></span><br><span class="line"><span class="comment"># 通过key放入数据,一个key只能对应一个value</span></span><br><span class="line"><span class="meta">>>> </span>d[<span class="string">'Adam'</span>] = <span class="number">67</span></span><br><span class="line"><span class="meta">>>> </span>d[<span class="string">'Adam'</span>]</span><br><span class="line"><span class="number">67</span></span><br></pre></td></tr></table></figure><p>判断key是否存在</p><ul><li><p>通过<code>in</code>判断</p><figure class="highlight python"><table><tr><td class="code"><pre><span class="line"><span class="meta">>>> </span><span class="string">'Thomas'</span> <span class="keyword">in</span> d</span><br><span class="line"><span class="literal">False</span></span><br></pre></td></tr></table></figure></li><li><p>通过dict提供的<code>get()</code>方法,如果key不存在,可以返回<code>None</code>,或者自己指定的value:</p><figure class="highlight python"><table><tr><td class="code"><pre><span class="line"><span class="meta">>>> </span>d.get(<span class="string">'Thomas'</span>)</span><br><span class="line"><span class="meta">>>> </span>d.get(<span class="string">'Thomas'</span>, -<span class="number">1</span>) <span class="comment"># 指定不存在的话value=-1</span></span><br><span class="line">-<span class="number">1</span></span><br></pre></td></tr></table></figure></li></ul><p>删除key,用pop(key)方法,对应的value也会从dict中删除</p><figure class="highlight python"><table><tr><td class="code"><pre><span class="line"><span class="meta">>>> </span>d.pop(<span class="string">'Bob'</span>)</span><br><span class="line"><span class="number">75</span></span><br><span class="line"><span class="meta">>>> </span>d</span><br><span class="line">{<span class="string">'Michael'</span>: <span class="number">95</span>, <span class="string">'Tracy'</span>: <span class="number">85</span>}</span><br></pre></td></tr></table></figure><p>set也是一组key的集合,但是不存储value,key不能重复,同样不能放入可变对象。</p><p>创建一个set,需要提供list作为输入集合。</p><figure class="highlight python"><table><tr><td class="code"><pre><span class="line"><span class="meta">>>> </span>s = <span class="built_in">set</span>([<span class="number">1</span>, <span class="number">2</span>, <span class="number">3</span>])</span><br><span class="line"><span class="meta">>>> </span>s</span><br><span class="line">{<span class="number">1</span>, <span class="number">2</span>, <span class="number">3</span>}</span><br><span class="line"></span><br><span class="line"><span class="meta">>>> </span>s = <span class="built_in">set</span>([<span class="number">1</span>, <span class="number">1</span>, <span class="number">2</span>, <span class="number">2</span>, <span class="number">3</span>, <span class="number">3</span>])</span><br><span class="line"><span class="meta">>>> </span>s</span><br><span class="line">{<span class="number">1</span>, <span class="number">2</span>, <span class="number">3</span>} <span class="comment">#重复元素会自动被过滤</span></span><br></pre></td></tr></table></figure><p>传入的参数<code>[1, 2, 3]</code>是一个list,而显示的<code>{1, 2, 3}</code>只是告诉你这个set内部有1,2,3这3个元素,显示的顺序也不表示set是有序的。</p><p>通过<code>add(key)</code>方法可以添加元素到set中,可以重复添加,但不会有效果;通过<code>remove(key)</code>方法可以删除元素;set可以看成数学意义上的无序和无重复元素的集合,因此,两个set可以做数学意义上的交集、并集等操作</p><figure class="highlight python"><table><tr><td class="code"><pre><span class="line"><span class="meta">>>> </span>s.add(<span class="number">4</span>)</span><br><span class="line"><span class="meta">>>> </span>s</span><br><span class="line">{<span class="number">1</span>, <span class="number">2</span>, <span class="number">3</span>, <span class="number">4</span>}</span><br><span class="line"><span class="meta">>>> </span>s.add(<span class="number">4</span>)</span><br><span class="line"><span class="meta">>>> </span>s</span><br><span class="line">{<span class="number">1</span>, <span class="number">2</span>, <span class="number">3</span>, <span class="number">4</span>}</span><br><span class="line"><span class="meta">>>> </span>s.remove(<span class="number">4</span>)</span><br><span class="line"><span class="meta">>>> </span>s</span><br><span class="line">{<span class="number">1</span>, <span class="number">2</span>, <span class="number">3</span>}</span><br><span class="line"><span class="meta">>>> </span>s1 = <span class="built_in">set</span>([<span class="number">1</span>, <span class="number">2</span>, <span class="number">3</span>])</span><br><span class="line"><span class="meta">>>> </span>s2 = <span class="built_in">set</span>([<span class="number">2</span>, <span class="number">3</span>, <span class="number">4</span>])</span><br><span class="line"><span class="meta">>>> </span>s1 & s2</span><br><span class="line">{<span class="number">2</span>, <span class="number">3</span>}</span><br><span class="line"><span class="meta">>>> </span>s1 | s2</span><br><span class="line">{<span class="number">1</span>, <span class="number">2</span>, <span class="number">3</span>, <span class="number">4</span>}</span><br></pre></td></tr></table></figure><h1 id="函数"><a href="#函数" class="headerlink" title="函数"></a>函数</h1><h2 id="调用函数"><a href="#调用函数" class="headerlink" title="调用函数"></a>调用函数</h2><p><code>help(函数名)</code>可以查看函数信息</p><p>abs函数是绝对值函数,max函数是最大值函数,它们都是内置函数。若传入的参数书量不对会报TypeError错误,如果传入的参数数量是对的,但是参数类型错误,也会报TypeError错误。</p><p>在python中数据类型转换是一种函数,可以直接调用,它是内置的,例如:</p><figure class="highlight python"><table><tr><td class="code"><pre><span class="line"><span class="meta">>>> </span><span class="built_in">int</span>(<span class="string">'123'</span>)</span><br><span class="line"><span class="number">123</span></span><br><span class="line"><span class="meta">>>> </span><span class="built_in">int</span>(<span class="number">12.34</span>)</span><br><span class="line"><span class="number">12</span></span><br><span class="line"><span class="meta">>>> </span><span class="built_in">float</span>(<span class="string">'12.34'</span>)</span><br><span class="line"><span class="number">12.34</span></span><br><span class="line"><span class="meta">>>> </span><span class="built_in">str</span>(<span class="number">1.23</span>)</span><br><span class="line"><span class="string">'1.23'</span></span><br><span class="line"><span class="meta">>>> </span><span class="built_in">str</span>(<span class="number">100</span>)</span><br><span class="line"><span class="string">'100'</span></span><br><span class="line"><span class="meta">>>> </span><span class="built_in">bool</span>(<span class="number">1</span>)</span><br><span class="line"><span class="literal">True</span></span><br><span class="line"><span class="meta">>>> </span><span class="built_in">bool</span>(<span class="string">''</span>)</span><br><span class="line"><span class="literal">False</span></span><br></pre></td></tr></table></figure><p>函数名其实就是指向一个函数对象的引用,完全可以把函数名赋给一个变量,相当于给这个函数起了一个“别名”,例如:</p><figure class="highlight python"><table><tr><td class="code"><pre><span class="line"><span class="meta">>>> </span>a = <span class="built_in">abs</span> <span class="comment"># 变量a指向abs函数</span></span><br><span class="line"><span class="meta">>>> </span>a(-<span class="number">1</span>) <span class="comment"># 所以也可以通过a调用abs函数</span></span><br><span class="line"><span class="number">1</span></span><br></pre></td></tr></table></figure><h2 id="定义函数"><a href="#定义函数" class="headerlink" title="定义函数"></a>定义函数</h2><p>python中定义函数需要使用<code>def语句</code>,依次写出函数名、括号、括号中的参数和<code>冒号:</code>,然后在缩进块中编写函数体,函数的返回值用return语句返回。</p><p>函数一旦执行到return,就执行完毕了,将结果返回。没有return语句,函数执行完毕后也会返回结果,只是结果为<code>None</code>。<code>return None</code>可以简写为return。</p><p>导入函数,可以举例用<code>from abstest import my_abs</code>来导入<code>my_abs()</code>函数,注意<code>abstest</code>是文件名。</p><p>空函数:如果想定义一个什么事也不做的空函数,可以用pass语句。<code>pass</code>可以用来作为占位符,比如现在还没想好怎么写函数的代码,就可以先放一个<code>pass</code>,让代码能运行起来。pass还可以用在其他语句里,比如:</p><figure class="highlight python"><table><tr><td class="code"><pre><span class="line"><span class="keyword">if</span> age >= <span class="number">18</span>:</span><br><span class="line"> <span class="keyword">pass</span></span><br></pre></td></tr></table></figure><p>对于函数的传入参数检查,可以使用内置函数isinstance实现:</p><figure class="highlight python"><table><tr><td class="code"><pre><span class="line"><span class="keyword">def</span> <span class="title function_">my_abs</span>(<span class="params">x</span>):</span><br><span class="line"> <span class="keyword">if</span> <span class="keyword">not</span> <span class="built_in">isinstance</span>(x, (<span class="built_in">int</span>, <span class="built_in">float</span>)):</span><br><span class="line"> <span class="keyword">raise</span> TypeError(<span class="string">'bad operand type'</span>)</span><br><span class="line"> <span class="keyword">if</span> x >= <span class="number">0</span>:</span><br><span class="line"> <span class="keyword">return</span> x</span><br><span class="line"> <span class="keyword">else</span>:</span><br><span class="line"> <span class="keyword">return</span> -x</span><br></pre></td></tr></table></figure><p>python中的函数可以返回多个值:</p><figure class="highlight python"><table><tr><td class="code"><pre><span class="line"><span class="keyword">import</span> math</span><br><span class="line"><span class="keyword">def</span> <span class="title function_">move</span>(<span class="params">x, y, step, angle=<span class="number">0</span></span>):</span><br><span class="line"> nx = x + step * math.cos(angle)</span><br><span class="line"> ny = y - step * math.sin(angle)</span><br><span class="line"> <span class="keyword">return</span> nx, ny</span><br><span class="line"></span><br><span class="line"><span class="meta">>>> </span>x, y = move(<span class="number">100</span>, <span class="number">100</span>, <span class="number">60</span>, math.pi / <span class="number">6</span>)</span><br><span class="line"><span class="meta">>>> </span><span class="built_in">print</span>(x, y)</span><br><span class="line"><span class="number">151.96152422706632</span> <span class="number">70.0</span></span><br></pre></td></tr></table></figure><p><code>import math</code>语句表示导入<code>math</code>包,并允许后续代码引用<code>math</code>包里的<code>sin</code>、<code>cos</code>等函数。但是其实Python函数的返回值仍然是单一值,是一个tuple,但是,在语法上,返回一个tuple可以省略括号,而多个变量可以同时接收一个tuple,按位置赋给对应的值,所以,Python的函数返回多值其实就是返回一个tuple,但写起来更方便。</p><h2 id="函数的参数"><a href="#函数的参数" class="headerlink" title="函数的参数"></a>函数的参数</h2><h3 id="位置参数"><a href="#位置参数" class="headerlink" title="位置参数"></a>位置参数</h3><p>就是直接给的参数,调用函数时,传入的值按照位置顺序依次赋值给位置参数。</p><h3 id="默认参数"><a href="#默认参数" class="headerlink" title="默认参数"></a>默认参数</h3><p>可以将参数在定义函数时默认值。设置默认参数时需要注意1.一是必选参数在前,默认参数在后 2.当函数有多个参数时,把变化大的参数放前面,变化小的参数放后面。变化小的参数就可以作为默认参数。调用的时候,既可以按顺序提供默认参数,也可以不按顺序提供部分默认参数。当不按顺序提供部分默认参数时,需要把参数名写上。</p><p>默认参数有坑:</p><figure class="highlight python"><table><tr><td class="code"><pre><span class="line"><span class="keyword">def</span> <span class="title function_">add_end</span>(<span class="params">L=[]</span>):</span><br><span class="line"> L.append(<span class="string">'END'</span>)</span><br><span class="line"> <span class="keyword">return</span> L</span><br><span class="line"></span><br><span class="line"><span class="meta">>>> </span>add_end([<span class="number">1</span>, <span class="number">2</span>, <span class="number">3</span>])</span><br><span class="line">[<span class="number">1</span>, <span class="number">2</span>, <span class="number">3</span>, <span class="string">'END'</span>]</span><br><span class="line"><span class="meta">>>> </span>add_end([<span class="string">'x'</span>, <span class="string">'y'</span>, <span class="string">'z'</span>])</span><br><span class="line">[<span class="string">'x'</span>, <span class="string">'y'</span>, <span class="string">'z'</span>, <span class="string">'END'</span>]</span><br><span class="line"></span><br><span class="line"><span class="meta">>>> </span>add_end()</span><br><span class="line">[<span class="string">'END'</span>]</span><br><span class="line"><span class="meta">>>> </span>add_end()</span><br><span class="line">[<span class="string">'END'</span>, <span class="string">'END'</span>]</span><br><span class="line"><span class="meta">>>> </span>add_end()</span><br><span class="line">[<span class="string">'END'</span>, <span class="string">'END'</span>, <span class="string">'END'</span>]</span><br></pre></td></tr></table></figure><p>Python函数在定义的时候,默认参数<code>L</code>的值就被计算出来了,即<code>[]</code>,因为默认参数<code>L</code>也是一个变量,它指向对象<code>[]</code>,每次调用该函数,如果改变了<code>L</code>的内容,则下次调用时,默认参数的内容就变了,不再是函数定义时的<code>[]</code>了。<strong>定义默认参数要牢记一点:默认参数必须指向不变对象!</strong></p><h3 id="可变参数"><a href="#可变参数" class="headerlink" title="可变参数"></a>可变参数</h3><p>定义可变参数和定义一个list或tuple参数相比,仅仅在参数前面加了一个<code>*</code>号。在函数内部,参数<code>numbers</code>接收到的是一个tuple,因此,函数代码完全不变。但是,调用该函数时,可以传入任意个参数,包括0个参数:</p><figure class="highlight python"><table><tr><td class="code"><pre><span class="line"><span class="keyword">def</span> <span class="title function_">calc</span>(<span class="params">*numbers</span>):</span><br><span class="line"> <span class="built_in">sum</span> = <span class="number">0</span></span><br><span class="line"> <span class="keyword">for</span> n <span class="keyword">in</span> numbers:</span><br><span class="line"> <span class="built_in">sum</span> = <span class="built_in">sum</span> + n * n</span><br><span class="line"> <span class="keyword">return</span> <span class="built_in">sum</span></span><br><span class="line"><span class="meta">>>> </span>calc(<span class="number">1</span>, <span class="number">2</span>)</span><br><span class="line"><span class="number">5</span></span><br><span class="line"><span class="meta">>>> </span>calc()</span><br><span class="line"><span class="number">0</span></span><br></pre></td></tr></table></figure><p>Python允许你在list或tuple前面加一个<code>*</code>号,把list或tuple的元素变成可变参数传进去:</p><figure class="highlight python"><table><tr><td class="code"><pre><span class="line"><span class="meta">>>> </span>nums = [<span class="number">1</span>, <span class="number">2</span>, <span class="number">3</span>]</span><br><span class="line"><span class="meta">>>> </span>calc(*nums)</span><br><span class="line"><span class="number">14</span></span><br></pre></td></tr></table></figure><p><code>*nums</code>表示把<code>nums</code>这个list的所有元素作为可变参数传进去。这种写法相当有用,而且很常见。</p><h3 id="关键字参数"><a href="#关键字参数" class="headerlink" title="关键字参数"></a>关键字参数</h3><p>可变参数允许你传入0个或任意个参数,这些可变参数在函数调用时自动组装为一个tuple。而关键字参数允许你传入0个或任意个含参数名的参数,这些关键字参数在函数内部自动组装为一个dict。</p><figure class="highlight python"><table><tr><td class="code"><pre><span class="line"><span class="keyword">def</span> <span class="title function_">person</span>(<span class="params">name, age, **kw</span>):</span><br><span class="line"> <span class="built_in">print</span>(<span class="string">'name:'</span>, name, <span class="string">'age:'</span>, age, <span class="string">'other:'</span>, kw)</span><br></pre></td></tr></table></figure><p>函数<code>person</code>除了必选参数<code>name</code>和<code>age</code>外,还接受关键字参数<code>kw</code>。在调用该函数时,可以只传入必选参数,也可以传入任意个数的关键字参数:</p><figure class="highlight python"><table><tr><td class="code"><pre><span class="line"><span class="meta">>>> </span>person(<span class="string">'Michael'</span>, <span class="number">30</span>)</span><br><span class="line">name: Michael age: <span class="number">30</span> other: {}</span><br><span class="line"> </span><br><span class="line"><span class="meta">>>> </span>person(<span class="string">'Bob'</span>, <span class="number">35</span>, city=<span class="string">'Beijing'</span>)</span><br><span class="line">name: Bob age: <span class="number">35</span> other: {<span class="string">'city'</span>: <span class="string">'Beijing'</span>}</span><br><span class="line"><span class="meta">>>> </span>person(<span class="string">'Adam'</span>, <span class="number">45</span>, gender=<span class="string">'M'</span>, job=<span class="string">'Engineer'</span>)</span><br><span class="line">name: Adam age: <span class="number">45</span> other: {<span class="string">'gender'</span>: <span class="string">'M'</span>, <span class="string">'job'</span>: <span class="string">'Engineer'</span>}</span><br></pre></td></tr></table></figure><p>和可变参数类似,可以将传入**dict:</p><figure class="highlight python"><table><tr><td class="code"><pre><span class="line"><span class="meta">>>> </span>extra = {<span class="string">'city'</span>: <span class="string">'Beijing'</span>, <span class="string">'job'</span>: <span class="string">'Engineer'</span>}</span><br><span class="line"><span class="meta">>>> </span>person(<span class="string">'Jack'</span>, <span class="number">24</span>, **extra)</span><br><span class="line">name: Jack age: <span class="number">24</span> other: {<span class="string">'city'</span>: <span class="string">'Beijing'</span>, <span class="string">'job'</span>: <span class="string">'Engineer'</span>}</span><br></pre></td></tr></table></figure><p><code>**extra</code>表示把<code>extra</code>这个dict的所有key-value用关键字参数传入到函数的<code>**kw</code>参数,<code>kw</code>将获得一个dict,注意<code>kw</code>获得的dict是<code>extra</code>的一份拷贝,对<code>kw</code>的改动不会影响到函数外的<code>extra</code>。</p><h3 id="命名关键字参数"><a href="#命名关键字参数" class="headerlink" title="命名关键字参数"></a>命名关键字参数</h3><p>如果要限制关键字参数的名字,就可以用命名关键字参数,例如,只接收<code>city</code>和<code>job</code>作为关键字参数。这种方式定义的函数如下:</p><figure class="highlight python"><table><tr><td class="code"><pre><span class="line"><span class="keyword">def</span> <span class="title function_">person</span>(<span class="params">name, age, *, city, job</span>):</span><br><span class="line"> <span class="built_in">print</span>(name, age, city, job)</span><br></pre></td></tr></table></figure><p>和关键字参数<code>**kw</code>不同,命名关键字参数需要一个特殊分隔符<code>*</code>,<code>*</code>后面的参数被视为命名关键字参数。调用方式如下:</p><figure class="highlight python"><table><tr><td class="code"><pre><span class="line"><span class="meta">>>> </span>person(<span class="string">'Jack'</span>, <span class="number">24</span>, city=<span class="string">'Beijing'</span>, job=<span class="string">'Engineer'</span>)</span><br><span class="line">Jack <span class="number">24</span> Beijing Engineer</span><br></pre></td></tr></table></figure><p>如果函数定义中已经有了一个可变参数,后面跟着的命名关键字参数就不再需要一个特殊分隔符<code>*</code>了:</p><figure class="highlight python"><table><tr><td class="code"><pre><span class="line"><span class="keyword">def</span> <span class="title function_">person</span>(<span class="params">name, age, *args, city, job</span>):</span><br><span class="line"> <span class="built_in">print</span>(name, age, args, city, job)</span><br></pre></td></tr></table></figure><p><strong>命名关键字参数必须传入参数名</strong>,这和位置参数不同。如果没有传入参数名,调用将报错。</p><p>命名关键字参数可以有缺省值,从而简化调用,由于命名关键字参数<code>city</code>具有默认值,调用时,可不传入<code>city</code>参数:</p><figure class="highlight python"><table><tr><td class="code"><pre><span class="line"><span class="keyword">def</span> <span class="title function_">person</span>(<span class="params">name, age, *, city=<span class="string">'Beijing'</span>, job</span>):</span><br><span class="line"> <span class="built_in">print</span>(name, age, city, job)</span><br><span class="line"></span><br><span class="line"><span class="meta">>>> </span>person(<span class="string">'Jack'</span>, <span class="number">24</span>, job=<span class="string">'Engineer'</span>)</span><br><span class="line">Jack <span class="number">24</span> Beijing Engineer</span><br></pre></td></tr></table></figure><p>使用命名关键字参数时,要特别注意,如果没有可变参数,就必须加一个<code>*</code>作为特殊分隔符。如果缺少<code>*</code>,Python解释器将无法识别位置参数和命名关键字参数</p><h3 id="参数组合"><a href="#参数组合" class="headerlink" title="参数组合"></a>参数组合</h3><p>在Python中定义函数,可以用必选参数、默认参数、可变参数、关键字参数和命名关键字参数,这<strong>5种参数都可以组合使用</strong>。但是请注意,<strong>参数定义的顺序必须是:必选参数、默认参数、可变参数、命名关键字参数和关键字参数</strong>。例如:</p><figure class="highlight python"><table><tr><td class="code"><pre><span class="line"><span class="keyword">def</span> <span class="title function_">f1</span>(<span class="params">a, b, c=<span class="number">0</span>, *args, **kw</span>):</span><br><span class="line"> <span class="built_in">print</span>(<span class="string">'a ='</span>, a, <span class="string">'b ='</span>, b, <span class="string">'c ='</span>, c, <span class="string">'args ='</span>, args, <span class="string">'kw ='</span>, kw)</span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">f2</span>(<span class="params">a, b, c=<span class="number">0</span>, *, d, **kw</span>):</span><br><span class="line"> <span class="built_in">print</span>(<span class="string">'a ='</span>, a, <span class="string">'b ='</span>, b, <span class="string">'c ='</span>, c, <span class="string">'d ='</span>, d, <span class="string">'kw ='</span>, kw)</span><br><span class="line"></span><br><span class="line"><span class="meta">>>> </span>f1(<span class="number">1</span>, <span class="number">2</span>)</span><br><span class="line">a = <span class="number">1</span> b = <span class="number">2</span> c = <span class="number">0</span> args = () kw = {}</span><br><span class="line"><span class="meta">>>> </span>f1(<span class="number">1</span>, <span class="number">2</span>, c=<span class="number">3</span>)</span><br><span class="line">a = <span class="number">1</span> b = <span class="number">2</span> c = <span class="number">3</span> args = () kw = {}</span><br><span class="line"><span class="meta">>>> </span>f1(<span class="number">1</span>, <span class="number">2</span>, <span class="number">3</span>, <span class="string">'a'</span>, <span class="string">'b'</span>)</span><br><span class="line">a = <span class="number">1</span> b = <span class="number">2</span> c = <span class="number">3</span> args = (<span class="string">'a'</span>, <span class="string">'b'</span>) kw = {}</span><br><span class="line"><span class="meta">>>> </span>f1(<span class="number">1</span>, <span class="number">2</span>, <span class="number">3</span>, <span class="string">'a'</span>, <span class="string">'b'</span>, x=<span class="number">99</span>)</span><br><span class="line">a = <span class="number">1</span> b = <span class="number">2</span> c = <span class="number">3</span> args = (<span class="string">'a'</span>, <span class="string">'b'</span>) kw = {<span class="string">'x'</span>: <span class="number">99</span>}</span><br><span class="line"><span class="meta">>>> </span>f2(<span class="number">1</span>, <span class="number">2</span>, d=<span class="number">99</span>, ext=<span class="literal">None</span>)</span><br><span class="line">a = <span class="number">1</span> b = <span class="number">2</span> c = <span class="number">0</span> d = <span class="number">99</span> kw = {<span class="string">'ext'</span>: <span class="literal">None</span>}</span><br></pre></td></tr></table></figure><p>通过一个tuple和dict,你也可以调用上述函数:</p><figure class="highlight python"><table><tr><td class="code"><pre><span class="line"><span class="meta">>>> </span>args = (<span class="number">1</span>, <span class="number">2</span>, <span class="number">3</span>, <span class="number">4</span>)</span><br><span class="line"><span class="meta">>>> </span>kw = {<span class="string">'d'</span>: <span class="number">99</span>, <span class="string">'x'</span>: <span class="string">'#'</span>}</span><br><span class="line"><span class="meta">>>> </span>f1(*args, **kw)</span><br><span class="line">a = <span class="number">1</span> b = <span class="number">2</span> c = <span class="number">3</span> args = (<span class="number">4</span>,) kw = {<span class="string">'d'</span>: <span class="number">99</span>, <span class="string">'x'</span>: <span class="string">'#'</span>}</span><br><span class="line"><span class="meta">>>> </span>args = (<span class="number">1</span>, <span class="number">2</span>, <span class="number">3</span>)</span><br><span class="line"><span class="meta">>>> </span>kw = {<span class="string">'d'</span>: <span class="number">88</span>, <span class="string">'x'</span>: <span class="string">'#'</span>}</span><br><span class="line"><span class="meta">>>> </span>f2(*args, **kw)</span><br><span class="line">a = <span class="number">1</span> b = <span class="number">2</span> c = <span class="number">3</span> d = <span class="number">88</span> kw = {<span class="string">'x'</span>: <span class="string">'#'</span>}</span><br></pre></td></tr></table></figure><h1 id="高级特性"><a href="#高级特性" class="headerlink" title="高级特性"></a>高级特性</h1><h2 id="切片"><a href="#切片" class="headerlink" title="切片"></a>切片</h2><p>Python提供了切片(Slice)操作符,能大大简化取一个list或tuple的部分元素这种操作</p><p>取前3个元素,用一行代码就可以完成切片:</p><figure class="highlight python"><table><tr><td class="code"><pre><span class="line"><span class="meta">>>> </span>L = [<span class="string">'Michael'</span>, <span class="string">'Sarah'</span>, <span class="string">'Tracy'</span>, <span class="string">'Bob'</span>, <span class="string">'Jack'</span>]</span><br><span class="line"><span class="meta">>>> </span>L[<span class="number">0</span>:<span class="number">3</span>]</span><br><span class="line">[<span class="string">'Michael'</span>, <span class="string">'Sarah'</span>, <span class="string">'Tracy'</span>]</span><br></pre></td></tr></table></figure><p><code>L[0:3]</code>表示,从索引<code>0</code>开始取,直到索引<code>3</code>为止,但不包括索引<code>3</code>。即索引<code>0</code>,<code>1</code>,<code>2</code>,正好是3个元素。</p><p>如果第一个索引是<code>0</code>,还可以省略:</p><figure class="highlight python"><table><tr><td class="code"><pre><span class="line"><span class="meta">>>> </span>L[:<span class="number">3</span>]</span><br><span class="line">[<span class="string">'Michael'</span>, <span class="string">'Sarah'</span>, <span class="string">'Tracy'</span>]</span><br></pre></td></tr></table></figure><p>支持倒数切片:</p><figure class="highlight python"><table><tr><td class="code"><pre><span class="line"><span class="meta">>>> </span>L[-<span class="number">2</span>:]</span><br><span class="line">[<span class="string">'Bob'</span>, <span class="string">'Jack'</span>]</span><br><span class="line"><span class="meta">>>> </span>L[-<span class="number">2</span>:-<span class="number">1</span>]</span><br><span class="line">[<span class="string">'Bob'</span>]</span><br></pre></td></tr></table></figure><p>实现前10个数,每两个取一个:</p><figure class="highlight python"><table><tr><td class="code"><pre><span class="line"><span class="meta">>>> </span>L[:<span class="number">10</span>:<span class="number">2</span>]</span><br><span class="line">[<span class="number">0</span>, <span class="number">2</span>, <span class="number">4</span>, <span class="number">6</span>, <span class="number">8</span>]</span><br></pre></td></tr></table></figure><p>所有数,每5个取一个:</p><figure class="highlight python"><table><tr><td class="code"><pre><span class="line"><span class="meta">>>> </span>L[::<span class="number">5</span>]</span><br><span class="line">[<span class="number">0</span>, <span class="number">5</span>, <span class="number">10</span>, <span class="number">15</span>, <span class="number">20</span>, <span class="number">25</span>, <span class="number">30</span>, <span class="number">35</span>, <span class="number">40</span>, <span class="number">45</span>, <span class="number">50</span>, <span class="number">55</span>, <span class="number">60</span>, <span class="number">65</span>, <span class="number">70</span>, <span class="number">75</span>, <span class="number">80</span>, <span class="number">85</span>, <span class="number">90</span>, <span class="number">95</span>]</span><br></pre></td></tr></table></figure><p>甚至什么都不写,只写<code>[:]</code>就可以原样复制一个list:</p><figure class="highlight python"><table><tr><td class="code"><pre><span class="line"><span class="meta">>>> </span>L[:]</span><br><span class="line">[<span class="number">0</span>, <span class="number">1</span>, <span class="number">2</span>, <span class="number">3</span>, ..., <span class="number">99</span>]</span><br></pre></td></tr></table></figure><p>tuple也是一种list,唯一区别是tuple不可变。因此,tuple也可以用切片操作,只是操作的结果仍是tuple:</p><figure class="highlight python"><table><tr><td class="code"><pre><span class="line"><span class="meta">>>> </span>(<span class="number">0</span>, <span class="number">1</span>, <span class="number">2</span>, <span class="number">3</span>, <span class="number">4</span>, <span class="number">5</span>)[:<span class="number">3</span>]</span><br><span class="line">(<span class="number">0</span>, <span class="number">1</span>, <span class="number">2</span>)</span><br></pre></td></tr></table></figure><p>字符串<code>'xxx'</code>也可以看成是一种list,每个元素就是一个字符。因此,字符串也可以用切片操作,只是操作结果仍是字符串,Python没有针对字符串的截取函数,只需要切片一个操作就可以完成:</p><figure class="highlight python"><table><tr><td class="code"><pre><span class="line"><span class="meta">>>> </span><span class="string">'ABCDEFG'</span>[:<span class="number">3</span>]</span><br><span class="line"><span class="string">'ABC'</span></span><br><span class="line"><span class="meta">>>> </span><span class="string">'ABCDEFG'</span>[::<span class="number">2</span>]</span><br><span class="line"><span class="string">'ACEG'</span></span><br></pre></td></tr></table></figure><h2 id="迭代"><a href="#迭代" class="headerlink" title="迭代"></a>迭代</h2><p>Python的<code>for</code>循环不仅可以用在<code>list</code>或<code>tuple</code>上,还可以作用在其他可迭代对象上。</p><p><code>list</code>这种数据类型虽然有下标,但很多其他数据类型是没有下标的,但是,只要是可迭代对象,无论有无下标,都可以迭代,比如<code>dict</code>就可以迭代:</p><figure class="highlight python"><table><tr><td class="code"><pre><span class="line"><span class="meta">>>> </span>d = {<span class="string">'a'</span>: <span class="number">1</span>, <span class="string">'b'</span>: <span class="number">2</span>, <span class="string">'c'</span>: <span class="number">3</span>}</span><br><span class="line"><span class="meta">>>> </span><span class="keyword">for</span> key <span class="keyword">in</span> d:</span><br><span class="line"><span class="meta">... </span> <span class="built_in">print</span>(key)</span><br><span class="line">...</span><br><span class="line">a</span><br><span class="line">c</span><br><span class="line">b</span><br></pre></td></tr></table></figure><p>因为<code>dict</code>的存储不是按照<code>list</code>的方式顺序排列,所以,迭代出的结果顺序很可能不一样。默认情况下,<code>dict</code>迭代的是key。如果要迭代value,可以用<code>for value in d.values()</code>,如果要同时迭代key和value,可以用<code>for k, v in d.items()</code>。</p><p>由于字符串也是可迭代对象,因此,也可以作用于<code>for</code>循环:</p><figure class="highlight python"><table><tr><td class="code"><pre><span class="line"><span class="meta">>>> </span><span class="keyword">for</span> ch <span class="keyword">in</span> <span class="string">'ABC'</span>:</span><br><span class="line"><span class="meta">... </span> <span class="built_in">print</span>(ch)</span><br><span class="line">...</span><br><span class="line">A</span><br><span class="line">B</span><br><span class="line">C</span><br></pre></td></tr></table></figure><p>Python内置的<code>enumerate</code>函数可以把一个<code>list</code>变成索引-元素对,这样就可以在<code>for</code>循环中同时迭代索引和元素本身:</p><figure class="highlight python"><table><tr><td class="code"><pre><span class="line"><span class="meta">>>> </span><span class="keyword">for</span> i, value <span class="keyword">in</span> <span class="built_in">enumerate</span>([<span class="string">'A'</span>, <span class="string">'B'</span>, <span class="string">'C'</span>]):</span><br><span class="line"><span class="meta">... </span> <span class="built_in">print</span>(i, value)</span><br><span class="line">...</span><br><span class="line"><span class="number">0</span> A</span><br><span class="line"><span class="number">1</span> B</span><br><span class="line"><span class="number">2</span> C</span><br></pre></td></tr></table></figure><p>上面的<code>for</code>循环里,同时引用了两个变量,在Python里是很常见的,比如下面的代码:</p><figure class="highlight python"><table><tr><td class="code"><pre><span class="line"><span class="meta">>>> </span><span class="keyword">for</span> x, y <span class="keyword">in</span> [(<span class="number">1</span>, <span class="number">1</span>), (<span class="number">2</span>, <span class="number">4</span>), (<span class="number">3</span>, <span class="number">9</span>)]:</span><br><span class="line"><span class="meta">... </span> <span class="built_in">print</span>(x, y)</span><br><span class="line">...</span><br><span class="line"><span class="number">1</span> <span class="number">1</span></span><br><span class="line"><span class="number">2</span> <span class="number">4</span></span><br><span class="line"><span class="number">3</span> <span class="number">9</span></span><br></pre></td></tr></table></figure><h2 id="列表生成式"><a href="#列表生成式" class="headerlink" title="列表生成式"></a>列表生成式</h2><p>要生成list <code>[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]</code>可以用<code>list(range(1, 11))</code>:</p><figure class="highlight python"><table><tr><td class="code"><pre><span class="line"><span class="meta">>>> </span><span class="built_in">list</span>(<span class="built_in">range</span>(<span class="number">1</span>, <span class="number">11</span>))</span><br><span class="line">[<span class="number">1</span>, <span class="number">2</span>, <span class="number">3</span>, <span class="number">4</span>, <span class="number">5</span>, <span class="number">6</span>, <span class="number">7</span>, <span class="number">8</span>, <span class="number">9</span>, <span class="number">10</span>]</span><br></pre></td></tr></table></figure><p>但如果要生成<code>[1x1, 2x2, 3x3, ..., 10x10]</code>怎么做?列表生成式可以用一行语句代替循环生成上面的list:</p><figure class="highlight python"><table><tr><td class="code"><pre><span class="line"><span class="meta">>>> </span>[x * x <span class="keyword">for</span> x <span class="keyword">in</span> <span class="built_in">range</span>(<span class="number">1</span>, <span class="number">11</span>)]</span><br><span class="line">[<span class="number">1</span>, <span class="number">4</span>, <span class="number">9</span>, <span class="number">16</span>, <span class="number">25</span>, <span class="number">36</span>, <span class="number">49</span>, <span class="number">64</span>, <span class="number">81</span>, <span class="number">100</span>]</span><br></pre></td></tr></table></figure><p>写列表生成式时,把要生成的元素<code>x * x</code>放到前面,后面跟<code>for</code>循环,就可以把list创建出来,十分有用。</p><p>for循环后面还可以加上if判断,这样我们就可以筛选出仅偶数的平方:</p><figure class="highlight python"><table><tr><td class="code"><pre><span class="line"><span class="meta">>>> </span>[x * x <span class="keyword">for</span> x <span class="keyword">in</span> <span class="built_in">range</span>(<span class="number">1</span>, <span class="number">11</span>) <span class="keyword">if</span> x % <span class="number">2</span> == <span class="number">0</span>]</span><br><span class="line">[<span class="number">4</span>, <span class="number">16</span>, <span class="number">36</span>, <span class="number">64</span>, <span class="number">100</span>]</span><br></pre></td></tr></table></figure><p>还可以使用两层循环,可以生成全排列:</p><figure class="highlight python"><table><tr><td class="code"><pre><span class="line"><span class="meta">>>> </span>[m + n <span class="keyword">for</span> m <span class="keyword">in</span> <span class="string">'ABC'</span> <span class="keyword">for</span> n <span class="keyword">in</span> <span class="string">'XYZ'</span>]</span><br><span class="line">[<span class="string">'AX'</span>, <span class="string">'AY'</span>, <span class="string">'AZ'</span>, <span class="string">'BX'</span>, <span class="string">'BY'</span>, <span class="string">'BZ'</span>, <span class="string">'CX'</span>, <span class="string">'CY'</span>, <span class="string">'CZ'</span>]</span><br></pre></td></tr></table></figure><p><code>for</code>循环其实可以同时使用两个甚至多个变量,比如<code>dict</code>的<code>items()</code>可以同时迭代key和value:</p><figure class="highlight python"><table><tr><td class="code"><pre><span class="line"><span class="meta">>>> </span>d = {<span class="string">'x'</span>: <span class="string">'A'</span>, <span class="string">'y'</span>: <span class="string">'B'</span>, <span class="string">'z'</span>: <span class="string">'C'</span> }</span><br><span class="line"><span class="meta">>>> </span><span class="keyword">for</span> k, v <span class="keyword">in</span> d.items():</span><br><span class="line"><span class="meta">... </span> <span class="built_in">print</span>(k, <span class="string">'='</span>, v)</span><br><span class="line">...</span><br><span class="line">y = B</span><br><span class="line">x = A</span><br><span class="line">z = C</span><br></pre></td></tr></table></figure><p>因此,列表生成式也可以使用两个变量来生成list:</p><figure class="highlight python"><table><tr><td class="code"><pre><span class="line"><span class="meta">>>> </span>d = {<span class="string">'x'</span>: <span class="string">'A'</span>, <span class="string">'y'</span>: <span class="string">'B'</span>, <span class="string">'z'</span>: <span class="string">'C'</span> }</span><br><span class="line"><span class="meta">>>> </span>[k + <span class="string">'='</span> + v <span class="keyword">for</span> k, v <span class="keyword">in</span> d.items()]</span><br><span class="line">[<span class="string">'y=B'</span>, <span class="string">'x=A'</span>, <span class="string">'z=C'</span>]</span><br></pre></td></tr></table></figure><p>最后把一个list中所有的字符串变成小写:</p><figure class="highlight python"><table><tr><td class="code"><pre><span class="line"><span class="meta">>>> </span>L = [<span class="string">'Hello'</span>, <span class="string">'World'</span>, <span class="string">'IBM'</span>, <span class="string">'Apple'</span>]</span><br><span class="line"><span class="meta">>>> </span>[s.lower() <span class="keyword">for</span> s <span class="keyword">in</span> L]</span><br><span class="line">[<span class="string">'hello'</span>, <span class="string">'world'</span>, <span class="string">'ibm'</span>, <span class="string">'apple'</span>]</span><br></pre></td></tr></table></figure><p>以下代码正常输出偶数:</p><figure class="highlight python"><table><tr><td class="code"><pre><span class="line"><span class="meta">>>> </span>[x <span class="keyword">for</span> x <span class="keyword">in</span> <span class="built_in">range</span>(<span class="number">1</span>, <span class="number">11</span>) <span class="keyword">if</span> x % <span class="number">2</span> == <span class="number">0</span>]</span><br><span class="line">[<span class="number">2</span>, <span class="number">4</span>, <span class="number">6</span>, <span class="number">8</span>, <span class="number">10</span>]</span><br></pre></td></tr></table></figure><p>但是,我们不能在最后的<code>if</code>加上<code>else</code>,这是因为跟在<code>for</code>后面的<code>if</code>是一个筛选条件,不能带<code>else</code>,否则如何筛选?</p><p>把<code>if</code>写在<code>for</code>前面必须加<code>else</code>,否则报错:</p><figure class="highlight python"><table><tr><td class="code"><pre><span class="line"><span class="meta">>>> </span>[x <span class="keyword">if</span> x % <span class="number">2</span> == <span class="number">0</span> <span class="keyword">for</span> x <span class="keyword">in</span> <span class="built_in">range</span>(<span class="number">1</span>, <span class="number">11</span>)]</span><br><span class="line"> File <span class="string">"<stdin>"</span>, line <span class="number">1</span></span><br><span class="line"> [x <span class="keyword">if</span> x % <span class="number">2</span> == <span class="number">0</span> <span class="keyword">for</span> x <span class="keyword">in</span> <span class="built_in">range</span>(<span class="number">1</span>, <span class="number">11</span>)]</span><br><span class="line"> ^</span><br><span class="line">SyntaxError: invalid syntax</span><br></pre></td></tr></table></figure><p>这是因为<code>for</code>前面的部分是一个表达式,它必须根据<code>x</code>计算出一个结果。因此,考察表达式:<code>x if x % 2 == 0</code>,它无法根据<code>x</code>计算出结果,因为缺少<code>else</code>,必须加上<code>else</code>:</p><figure class="highlight python"><table><tr><td class="code"><pre><span class="line"><span class="meta">>>> </span>[x <span class="keyword">if</span> x % <span class="number">2</span> == <span class="number">0</span> <span class="keyword">else</span> -x <span class="keyword">for</span> x <span class="keyword">in</span> <span class="built_in">range</span>(<span class="number">1</span>, <span class="number">11</span>)]</span><br><span class="line">[-<span class="number">1</span>, <span class="number">2</span>, -<span class="number">3</span>, <span class="number">4</span>, -<span class="number">5</span>, <span class="number">6</span>, -<span class="number">7</span>, <span class="number">8</span>, -<span class="number">9</span>, <span class="number">10</span>]</span><br></pre></td></tr></table></figure><p>上述<code>for</code>前面的表达式<code>x if x % 2 == 0 else -x</code>才能根据<code>x</code>计算出确定的结果。</p><p>可见,在一个列表生成式中,<code>for</code>前面的<code>if ... else</code>是表达式,而<code>for</code>后面的<code>if</code>是过滤条件,不能带<code>else</code>。</p><h2 id="生成器"><a href="#生成器" class="headerlink" title="生成器"></a>生成器</h2><p>一边循环一边计算的机制,称为生成器:generator。这样就不必创建完整的list,从而节省大量的空间。</p><p>要创建一个generator,有很多种方法。第一种方法很简单,只要把一个列表生成式的<code>[]</code>改成<code>()</code>,就创建了一个generator:</p><figure class="highlight python"><table><tr><td class="code"><pre><span class="line"><span class="meta">>>> </span>L = [x * x <span class="keyword">for</span> x <span class="keyword">in</span> <span class="built_in">range</span>(<span class="number">10</span>)]</span><br><span class="line"><span class="meta">>>> </span>L</span><br><span class="line">[<span class="number">0</span>, <span class="number">1</span>, <span class="number">4</span>, <span class="number">9</span>, <span class="number">16</span>, <span class="number">25</span>, <span class="number">36</span>, <span class="number">49</span>, <span class="number">64</span>, <span class="number">81</span>]</span><br><span class="line"><span class="meta">>>> </span>g = (x * x <span class="keyword">for</span> x <span class="keyword">in</span> <span class="built_in">range</span>(<span class="number">10</span>))</span><br><span class="line"><span class="meta">>>> </span>g</span><br><span class="line"><generator <span class="built_in">object</span> <genexpr> at <span class="number">0x1022ef630</span>></span><br></pre></td></tr></table></figure><p>创建<code>L</code>和<code>g</code>的区别仅在于最外层的<code>[]</code>和<code>()</code>,<code>L</code>是一个list,而<code>g</code>是一个generator。</p><p>如果要一个一个打印出来,可以通过<code>next()</code>函数获得generator的下一个返回值。当然,上面这种不断调用<code>next(g)</code>实在是太变态了,正确的方法是使用<code>for</code>循环,因为generator也是可迭代对象:</p><figure class="highlight python"><table><tr><td class="code"><pre><span class="line"><span class="meta">>>> </span>g = (x * x <span class="keyword">for</span> x <span class="keyword">in</span> <span class="built_in">range</span>(<span class="number">10</span>))</span><br><span class="line"><span class="meta">>>> </span><span class="keyword">for</span> n <span class="keyword">in</span> g:</span><br><span class="line"><span class="meta">... </span> <span class="built_in">print</span>(n)</span><br><span class="line"><span class="meta">... </span></span><br><span class="line"><span class="number">0</span></span><br><span class="line"><span class="number">1</span></span><br><span class="line"><span class="number">4</span></span><br><span class="line"><span class="number">9</span></span><br><span class="line"><span class="number">16</span></span><br><span class="line"><span class="number">25</span></span><br><span class="line"><span class="number">36</span></span><br><span class="line"><span class="number">49</span></span><br><span class="line"><span class="number">64</span></span><br><span class="line"><span class="number">81</span></span><br></pre></td></tr></table></figure><p>所以,我们创建了一个generator后,基本上永远不会调用<code>next()</code>,而是通过<code>for</code>循环来迭代它</p><h2 id="迭代器"><a href="#迭代器" class="headerlink" title="迭代器"></a>迭代器</h2><p>可以使用<code>isinstance()</code>判断一个对象是否是可迭代对象:</p><figure class="highlight python"><table><tr><td class="code"><pre><span class="line"><span class="meta">>>> </span><span class="keyword">from</span> collections.abc <span class="keyword">import</span> Iterable</span><br><span class="line"><span class="meta">>>> </span><span class="built_in">isinstance</span>([], Iterable)</span><br><span class="line"><span class="literal">True</span></span><br><span class="line"><span class="meta">>>> </span><span class="built_in">isinstance</span>({}, Iterable)</span><br><span class="line"><span class="literal">True</span></span><br><span class="line"><span class="meta">>>> </span><span class="built_in">isinstance</span>(<span class="string">'abc'</span>, Iterable)</span><br><span class="line"><span class="literal">True</span></span><br><span class="line"><span class="meta">>>> </span><span class="built_in">isinstance</span>((x <span class="keyword">for</span> x <span class="keyword">in</span> <span class="built_in">range</span>(<span class="number">10</span>)), Iterable)</span><br><span class="line"><span class="literal">True</span></span><br><span class="line"><span class="meta">>>> </span><span class="built_in">isinstance</span>(<span class="number">100</span>, Iterable)</span><br><span class="line"><span class="literal">False</span></span><br></pre></td></tr></table></figure><p>生成器不但可以作用于<code>for</code>循环,还可以被<code>next()</code>函数不断调用并返回下一个值。可以被<code>next()</code>函数调用并不断返回下一个值的对象称为迭代器:<code>Iterator</code>。</p><p>可以使用<code>isinstance()</code>判断一个对象是否是<code>Iterator</code>对象:</p><figure class="highlight python"><table><tr><td class="code"><pre><span class="line"><span class="meta">>>> </span><span class="keyword">from</span> collections.abc <span class="keyword">import</span> Iterator</span><br><span class="line"><span class="meta">>>> </span><span class="built_in">isinstance</span>((x <span class="keyword">for</span> x <span class="keyword">in</span> <span class="built_in">range</span>(<span class="number">10</span>)), Iterator)</span><br><span class="line"><span class="literal">True</span></span><br><span class="line"><span class="meta">>>> </span><span class="built_in">isinstance</span>([], Iterator)</span><br><span class="line"><span class="literal">False</span></span><br><span class="line"><span class="meta">>>> </span><span class="built_in">isinstance</span>({}, Iterator)</span><br><span class="line"><span class="literal">False</span></span><br><span class="line"><span class="meta">>>> </span><span class="built_in">isinstance</span>(<span class="string">'abc'</span>, Iterator)</span><br><span class="line"><span class="literal">False</span></span><br></pre></td></tr></table></figure><p>把<code>list</code>、<code>dict</code>、<code>str</code>等<code>Iterable</code>变成<code>Iterator</code>可以使用<code>iter()</code>函数:</p><figure class="highlight python"><table><tr><td class="code"><pre><span class="line"><span class="meta">>>> </span><span class="built_in">isinstance</span>(<span class="built_in">iter</span>([]), Iterator)</span><br><span class="line"><span class="literal">True</span></span><br><span class="line"><span class="meta">>>> </span><span class="built_in">isinstance</span>(<span class="built_in">iter</span>(<span class="string">'abc'</span>), Iterator)</span><br><span class="line"><span class="literal">True</span></span><br></pre></td></tr></table></figure><h1 id="函数式编程"><a href="#函数式编程" class="headerlink" title="函数式编程"></a>函数式编程</h1><p>变量可以指向函数,可以把函数本身赋值给变量。一个变量指向了一个函数,可通过该变量来调用这个函数。</p><figure class="highlight python"><table><tr><td class="code"><pre><span class="line"><span class="meta">>>> </span>f = <span class="built_in">abs</span></span><br><span class="line"><span class="meta">>>> </span>f</span><br><span class="line"><built-<span class="keyword">in</span> function <span class="built_in">abs</span>></span><br><span class="line"><span class="meta">>>> </span>f(-<span class="number">10</span>)</span><br><span class="line"><span class="number">10</span></span><br></pre></td></tr></table></figure><p>变量<code>f</code>现在已经指向了<code>abs</code>函数本身。直接调用<code>abs()</code>函数和调用变量<code>f()</code>完全相同。</p><h2 id="高阶函数"><a href="#高阶函数" class="headerlink" title="高阶函数"></a>高阶函数</h2><h3 id="函数名也是变量"><a href="#函数名也是变量" class="headerlink" title="函数名也是变量"></a>函数名也是变量</h3><p>函数名其实就是指向函数的变量。对于<code>abs()</code>这个函数,完全可以把函数名<code>abs</code>看成变量,它指向一个可以计算绝对值的函数。</p><p>如果把<code>abs</code>指向其他对象,会有什么情况发生?</p><figure class="highlight python"><table><tr><td class="code"><pre><span class="line"><span class="meta">>>> </span><span class="built_in">abs</span> = <span class="number">10</span></span><br><span class="line"><span class="meta">>>> </span><span class="built_in">abs</span>(-<span class="number">10</span>)</span><br><span class="line">Traceback (most recent call last):</span><br><span class="line"> File <span class="string">"<stdin>"</span>, line <span class="number">1</span>, <span class="keyword">in</span> <module></span><br><span class="line">TypeError: <span class="string">'int'</span> <span class="built_in">object</span> <span class="keyword">is</span> <span class="keyword">not</span> <span class="built_in">callable</span></span><br></pre></td></tr></table></figure><p>把<code>abs</code>指向<code>10</code>后,就无法通过<code>abs(-10)</code>调用该函数了!因为<code>abs</code>这个变量已经不指向求绝对值函数而是指向一个整数<code>10</code>。</p><h3 id="传入函数"><a href="#传入函数" class="headerlink" title="传入函数"></a>传入函数</h3><p>既然变量可以指向函数,函数的参数能接收变量,那么一个函数就可以接收另一个函数作为参数,这种函数就称之为高阶函数。</p><p>一个最简单的高阶函数:</p><figure class="highlight python"><table><tr><td class="code"><pre><span class="line"><span class="keyword">def</span> <span class="title function_">add</span>(<span class="params">x, y, f</span>):</span><br><span class="line"> <span class="keyword">return</span> f(x) + f(y)</span><br></pre></td></tr></table></figure><p>编写高阶函数,就是让函数的参数能够接收别的函数。</p><h3 id="map-amp-reduce"><a href="#map-amp-reduce" class="headerlink" title="map&reduce"></a>map&reduce</h3><h4 id="map"><a href="#map" class="headerlink" title="map"></a>map</h4><p><code>map()</code>函数接收两个参数,一个是函数,一个是<code>Iterable</code>,<code>map</code>将传入的函数依次作用到序列的每个元素,并把结果作为新的<code>Iterator</code>返回。</p><p>举例说明,比如我们有一个函数f(x)=x2,要把这个函数作用在一个list <code>[1, 2, 3, 4, 5, 6, 7, 8, 9]</code>上,就可以用<code>map()</code>实现如下:</p><figure class="highlight python"><table><tr><td class="code"><pre><span class="line"><span class="meta">>>> </span><span class="keyword">def</span> <span class="title function_">f</span>(<span class="params">x</span>):</span><br><span class="line"><span class="meta">... </span> <span class="keyword">return</span> x * x</span><br><span class="line">...</span><br><span class="line"><span class="meta">>>> </span>r = <span class="built_in">map</span>(f, [<span class="number">1</span>, <span class="number">2</span>, <span class="number">3</span>, <span class="number">4</span>, <span class="number">5</span>, <span class="number">6</span>, <span class="number">7</span>, <span class="number">8</span>, <span class="number">9</span>])</span><br><span class="line"><span class="meta">>>> </span><span class="built_in">list</span>(r)</span><br><span class="line">[<span class="number">1</span>, <span class="number">4</span>, <span class="number">9</span>, <span class="number">16</span>, <span class="number">25</span>, <span class="number">36</span>, <span class="number">49</span>, <span class="number">64</span>, <span class="number">81</span>]</span><br></pre></td></tr></table></figure><p><code>map()</code>传入的第一个参数是<code>f</code>,即函数对象本身。由于结果<code>r</code>是一个<code>Iterator</code>,<code>Iterator</code>是惰性序列,因此通过<code>list()</code>函数让它把整个序列都计算出来并返回一个list。</p><p>我们不但可以计算简单的f(x)=x2,还可以计算任意复杂的函数,比如,把这个list所有数字转为字符串:</p><figure class="highlight python"><table><tr><td class="code"><pre><span class="line"><span class="meta">>>> </span><span class="built_in">list</span>(<span class="built_in">map</span>(<span class="built_in">str</span>, [<span class="number">1</span>, <span class="number">2</span>, <span class="number">3</span>, <span class="number">4</span>, <span class="number">5</span>, <span class="number">6</span>, <span class="number">7</span>, <span class="number">8</span>, <span class="number">9</span>]))</span><br><span class="line">[<span class="string">'1'</span>, <span class="string">'2'</span>, <span class="string">'3'</span>, <span class="string">'4'</span>, <span class="string">'5'</span>, <span class="string">'6'</span>, <span class="string">'7'</span>, <span class="string">'8'</span>, <span class="string">'9'</span>]</span><br></pre></td></tr></table></figure><h4 id="reduce"><a href="#reduce" class="headerlink" title="reduce"></a>reduce</h4><p><code>reduce</code>把一个函数作用在一个序列<code>[x1, x2, x3, ...]</code>上,这个函数必须接收两个参数,<code>reduce</code>把结果继续和序列的下一个元素做累积计算,其效果就是:</p><figure class="highlight python"><table><tr><td class="code"><pre><span class="line">reduce(f, [x1, x2, x3, x4]) = f(f(f(x1, x2), x3), x4)</span><br></pre></td></tr></table></figure><p>比方说对一个序列求和,就可以用<code>reduce</code>实现:</p><figure class="highlight python"><table><tr><td class="code"><pre><span class="line"><span class="meta">>>> </span><span class="keyword">from</span> functools <span class="keyword">import</span> reduce</span><br><span class="line"><span class="meta">>>> </span><span class="keyword">def</span> <span class="title function_">add</span>(<span class="params">x, y</span>):</span><br><span class="line"><span class="meta">... </span> <span class="keyword">return</span> x + y</span><br><span class="line">...</span><br><span class="line"><span class="meta">>>> </span>reduce(add, [<span class="number">1</span>, <span class="number">3</span>, <span class="number">5</span>, <span class="number">7</span>, <span class="number">9</span>])</span><br><span class="line"><span class="number">25</span></span><br></pre></td></tr></table></figure><figure class="highlight python"><table><tr><td class="code"><pre><span class="line"><span class="keyword">from</span> functools <span class="keyword">import</span> reduce</span><br><span class="line"></span><br><span class="line">DIGITS = {<span class="string">'0'</span>: <span class="number">0</span>, <span class="string">'1'</span>: <span class="number">1</span>, <span class="string">'2'</span>: <span class="number">2</span>, <span class="string">'3'</span>: <span class="number">3</span>, <span class="string">'4'</span>: <span class="number">4</span>, <span class="string">'5'</span>: <span class="number">5</span>, <span class="string">'6'</span>: <span class="number">6</span>, <span class="string">'7'</span>: <span class="number">7</span>, <span class="string">'8'</span>: <span class="number">8</span>, <span class="string">'9'</span>: <span class="number">9</span>}</span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">str2int</span>(<span class="params">s</span>):</span><br><span class="line"> <span class="keyword">def</span> <span class="title function_">fn</span>(<span class="params">x, y</span>):</span><br><span class="line"> <span class="keyword">return</span> x * <span class="number">10</span> + y</span><br><span class="line"> <span class="keyword">def</span> <span class="title function_">char2num</span>(<span class="params">s</span>):</span><br><span class="line"> <span class="keyword">return</span> DIGITS[s]</span><br><span class="line"> <span class="keyword">return</span> reduce(fn, <span class="built_in">map</span>(char2num, s))</span><br><span class="line"><span class="meta">>>> </span>str2int(<span class="string">'13579'</span>)</span><br><span class="line"><span class="number">13579</span></span><br></pre></td></tr></table></figure><p>这个是map和reduce的运用+函数内定义函数这个用法</p><h3 id="filter"><a href="#filter" class="headerlink" title="filter"></a>filter</h3><p>和<code>map()</code>类似,<code>filter()</code>也接收一个函数和一个序列。和<code>map()</code>不同的是,<code>filter()</code>把传入的函数依次作用于每个元素,然后根据返回值是<code>True</code>还是<code>False</code>决定保留还是丢弃该元素。</p><p>例如,在一个list中,删掉偶数,只保留奇数,可以这么写:</p><figure class="highlight python"><table><tr><td class="code"><pre><span class="line"><span class="keyword">def</span> <span class="title function_">is_odd</span>(<span class="params">n</span>):</span><br><span class="line"> <span class="keyword">return</span> n % <span class="number">2</span> == <span class="number">1</span></span><br><span class="line"></span><br><span class="line"><span class="built_in">list</span>(<span class="built_in">filter</span>(is_odd, [<span class="number">1</span>, <span class="number">2</span>, <span class="number">4</span>, <span class="number">5</span>, <span class="number">6</span>, <span class="number">9</span>, <span class="number">10</span>, <span class="number">15</span>]))</span><br><span class="line"><span class="comment"># 结果: [1, 5, 9, 15]</span></span><br></pre></td></tr></table></figure><p><code>filter()</code>函数返回的是一个<code>Iterator</code>,也就是一个惰性序列,所以要强迫<code>filter()</code>完成计算结果,需要用<code>list()</code>函数获得所有结果并返回list。</p><h3 id="sorted"><a href="#sorted" class="headerlink" title="sorted"></a>sorted</h3><p>Python内置的<code>sorted()</code>函数就可以对list进行排序:</p><figure class="highlight python"><table><tr><td class="code"><pre><span class="line"><span class="meta">>>> </span><span class="built_in">sorted</span>([<span class="number">36</span>, <span class="number">5</span>, -<span class="number">12</span>, <span class="number">9</span>, -<span class="number">21</span>])</span><br><span class="line">[-<span class="number">21</span>, -<span class="number">12</span>, <span class="number">5</span>, <span class="number">9</span>, <span class="number">36</span>]</span><br></pre></td></tr></table></figure><p>此外,<code>sorted()</code>函数也是一个高阶函数,它还可以接收一个<code>key</code>函数来实现自定义的排序,例如按绝对值大小排序:</p><figure class="highlight python"><table><tr><td class="code"><pre><span class="line"><span class="meta">>>> </span><span class="built_in">sorted</span>([<span class="number">36</span>, <span class="number">5</span>, -<span class="number">12</span>, <span class="number">9</span>, -<span class="number">21</span>], key=<span class="built_in">abs</span>)</span><br><span class="line">[<span class="number">5</span>, <span class="number">9</span>, -<span class="number">12</span>, -<span class="number">21</span>, <span class="number">36</span>]</span><br></pre></td></tr></table></figure><p>key指定的函数将作用于list的每一个元素上,并根据key函数返回的结果进行排序。对比原始的list和经过<code>key=abs</code>处理过的list:</p><figure class="highlight python"><table><tr><td class="code"><pre><span class="line"><span class="built_in">list</span> = [<span class="number">36</span>, <span class="number">5</span>, -<span class="number">12</span>, <span class="number">9</span>, -<span class="number">21</span>]</span><br><span class="line"></span><br><span class="line">keys = [<span class="number">36</span>, <span class="number">5</span>, <span class="number">12</span>, <span class="number">9</span>, <span class="number">21</span>]</span><br></pre></td></tr></table></figure><p>然后<code>sorted()</code>函数按照keys进行排序,并按照对应关系返回list相应的元素:</p><figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">keys排序结果 => [5, 9, 12, 21, 36]</span><br><span class="line"> | | | | |</span><br><span class="line">最终结果 => [5, 9, -12, -21, 36]</span><br></pre></td></tr></table></figure><p>要进行反向排序,不必改动key函数,可以传入第三个参数<code>reverse=True</code></p><figure class="highlight python"><table><tr><td class="code"><pre><span class="line"><span class="meta">>>> </span><span class="built_in">sorted</span>([<span class="number">36</span>, <span class="number">5</span>, -<span class="number">12</span>, <span class="number">9</span>, -<span class="number">21</span>], key=<span class="built_in">abs</span>,reverse=<span class="literal">True</span>)</span><br><span class="line">[<span class="number">36</span>, -<span class="number">21</span>, -<span class="number">12</span>, <span class="number">9</span>, <span class="number">5</span>]</span><br></pre></td></tr></table></figure><h2 id="返回函数"><a href="#返回函数" class="headerlink" title="返回函数"></a>返回函数</h2><p>如果不需要立刻求和,而是在后面的代码中,根据需要再计算。可以不返回求和的结果,而是返回求和的函数:</p><figure class="highlight python"><table><tr><td class="code"><pre><span class="line"><span class="keyword">def</span> <span class="title function_">lazy_sum</span>(<span class="params">*args</span>):</span><br><span class="line"> <span class="keyword">def</span> <span class="title function_">sum</span>():</span><br><span class="line"> ax = <span class="number">0</span></span><br><span class="line"> <span class="keyword">for</span> n <span class="keyword">in</span> args:</span><br><span class="line"> ax = ax + n</span><br><span class="line"> <span class="keyword">return</span> ax</span><br><span class="line"> <span class="keyword">return</span> <span class="built_in">sum</span></span><br></pre></td></tr></table></figure><p>当我们调用<code>lazy_sum()</code>时,返回的并不是求和结果,而是求和函数:</p><figure class="highlight python"><table><tr><td class="code"><pre><span class="line"><span class="meta">>>> </span>f = lazy_sum(<span class="number">1</span>, <span class="number">3</span>, <span class="number">5</span>, <span class="number">7</span>, <span class="number">9</span>)</span><br><span class="line"><span class="meta">>>> </span>f</span><br><span class="line"><function lazy_sum.<<span class="built_in">locals</span>>.<span class="built_in">sum</span> at <span class="number">0x101c6ed90</span>></span><br></pre></td></tr></table></figure><p>调用函数<code>f</code>时,才真正计算求和的结果:</p><figure class="highlight python"><table><tr><td class="code"><pre><span class="line"><span class="meta">>>> </span>f()</span><br><span class="line"><span class="number">25</span></span><br></pre></td></tr></table></figure><p>我们在函数<code>lazy_sum</code>中又定义了函数<code>sum</code>,并且,内部函数<code>sum</code>可以引用外部函数<code>lazy_sum</code>的参数和局部变量,当<code>lazy_sum</code>返回函数<code>sum</code>时,相关参数和变量都保存在返回的函数中,这种称为“闭包(Closure)”的程序结构拥有极大的威力。</p><p>当我们调用<code>lazy_sum()</code>时,每次调用都会返回一个新的函数,即使传入相同的参数:</p><figure class="highlight python"><table><tr><td class="code"><pre><span class="line"><span class="meta">>>> </span>f1 = lazy_sum(<span class="number">1</span>, <span class="number">3</span>, <span class="number">5</span>, <span class="number">7</span>, <span class="number">9</span>)</span><br><span class="line"><span class="meta">>>> </span>f2 = lazy_sum(<span class="number">1</span>, <span class="number">3</span>, <span class="number">5</span>, <span class="number">7</span>, <span class="number">9</span>)</span><br><span class="line"><span class="meta">>>> </span>f1==f2</span><br><span class="line"><span class="literal">False</span></span><br></pre></td></tr></table></figure><p><code>f1()</code>和<code>f2()</code>的调用结果互不影响。</p><p>返回闭包时牢记一点:返回函数不要引用任何循环变量,或者后续会发生变化的变量。</p><h2 id="匿名函数"><a href="#匿名函数" class="headerlink" title="匿名函数"></a>匿名函数</h2><p>在Python中,对匿名函数提供了有限支持。还是以<code>map()</code>函数为例,计算f(x)=x2时,除了定义一个<code>f(x)</code>的函数外,还可以直接传入匿名函数:</p><figure class="highlight python"><table><tr><td class="code"><pre><span class="line"><span class="meta">>>> </span><span class="built_in">list</span>(<span class="built_in">map</span>(<span class="keyword">lambda</span> x: x * x, [<span class="number">1</span>, <span class="number">2</span>, <span class="number">3</span>, <span class="number">4</span>, <span class="number">5</span>, <span class="number">6</span>, <span class="number">7</span>, <span class="number">8</span>, <span class="number">9</span>]))</span><br><span class="line">[<span class="number">1</span>, <span class="number">4</span>, <span class="number">9</span>, <span class="number">16</span>, <span class="number">25</span>, <span class="number">36</span>, <span class="number">49</span>, <span class="number">64</span>, <span class="number">81</span>]</span><br></pre></td></tr></table></figure><p>通过对比可以看出,匿名函数<code>lambda x: x * x</code>实际上就是:</p><figure class="highlight python"><table><tr><td class="code"><pre><span class="line"><span class="keyword">def</span> <span class="title function_">f</span>(<span class="params">x</span>):</span><br><span class="line"> <span class="keyword">return</span> x * x</span><br></pre></td></tr></table></figure><p>关键字<code>lambda</code>表示匿名函数,冒号前面的<code>x</code>表示函数参数。匿名函数有个限制,就是只能有一个表达式,不用写<code>return</code>,返回值就是该表达式的结果。</p><p>匿名函数也是一个函数对象,也可以把匿名函数赋值给一个变量,再利用变量来调用该函数:</p><figure class="highlight python"><table><tr><td class="code"><pre><span class="line"><span class="meta">>>> </span>f = <span class="keyword">lambda</span> x: x * x</span><br><span class="line"><span class="meta">>>> </span>f</span><br><span class="line"><function <<span class="keyword">lambda</span>> at <span class="number">0x101c6ef28</span>></span><br><span class="line"><span class="meta">>>> </span>f(<span class="number">5</span>)</span><br><span class="line"><span class="number">25</span></span><br></pre></td></tr></table></figure><p>同样,也可以把匿名函数作为返回值返回,比如:</p><figure class="highlight python"><table><tr><td class="code"><pre><span class="line"><span class="keyword">def</span> <span class="title function_">build</span>(<span class="params">x, y</span>):</span><br><span class="line"> <span class="keyword">return</span> <span class="keyword">lambda</span>: x * x + y * y</span><br></pre></td></tr></table></figure><h1 id="模块"><a href="#模块" class="headerlink" title="模块"></a>模块</h1><h2 id="模块使用"><a href="#模块使用" class="headerlink" title="模块使用"></a>模块使用</h2><figure class="highlight python"><table><tr><td class="code"><pre><span class="line"><span class="keyword">if</span> __name__==<span class="string">'__main__'</span>:</span><br></pre></td></tr></table></figure><p>相当于main函数,只有模块内部才能用,其他的模块是没法调用这个的</p><h2 id="作用域"><a href="#作用域" class="headerlink" title="作用域"></a>作用域</h2><p>正常的函数和变量名是公开的(public),可以被直接引用,比如:<code>abc</code>,<code>x123</code>,<code>PI</code>等;类似<code>__xxx__</code>这样的变量是特殊变量,可以被直接引用,但是有特殊用途,<code>__name__</code>就是特殊变量。类似<code>_xxx</code>和<code>__xxx</code>这样的函数或变量就是非公开的(private),不应该被直接引用,比如<code>_abc</code>,<code>__abc</code>等;</p><h1 id="面向对象编程"><a href="#面向对象编程" class="headerlink" title="面向对象编程"></a>面向对象编程</h1><h2 id="类和实例"><a href="#类和实例" class="headerlink" title="类和实例"></a>类和实例</h2><figure class="highlight python"><table><tr><td class="code"><pre><span class="line"><span class="keyword">class</span> <span class="title class_">Student</span>(<span class="title class_ inherited__">object</span>):</span><br><span class="line"> <span class="keyword">pass</span></span><br></pre></td></tr></table></figure><p><code>(object)</code>,表示该类是从哪个类继承下来的,如果没有合适的继承类,就使用<code>object</code>类,这是所有类最终都会继承的类。</p><p>可以自由地给一个实例变量绑定属性,比如,给实例<code>bart</code>绑定一个<code>name</code>属性:</p><figure class="highlight python"><table><tr><td class="code"><pre><span class="line"><span class="meta">>>> </span>bart = Student()</span><br><span class="line"><span class="meta">>>> </span>bart.name = <span class="string">'Bart Simpson'</span></span><br><span class="line"><span class="meta">>>> </span>bart.name</span><br><span class="line"><span class="string">'Bart Simpson'</span></span><br></pre></td></tr></table></figure><p>可以在创建实例的时候,把一些我们认为必须绑定的属性强制填写进去。通过定义一个特殊的<code>__init__</code>方法,在创建实例的时候,就把<code>name</code>,<code>score</code>等属性绑上去:</p><figure class="highlight python"><table><tr><td class="code"><pre><span class="line"><span class="keyword">class</span> <span class="title class_">Student</span>(<span class="title class_ inherited__">object</span>):</span><br><span class="line"></span><br><span class="line"> <span class="keyword">def</span> <span class="title function_">__init__</span>(<span class="params">self, name, score</span>):</span><br><span class="line"> self.name = name</span><br><span class="line"> self.score = score</span><br></pre></td></tr></table></figure><p>方法<code>__init__</code>前后分别有两个下划线。<code>__init__</code>方法的第一个参数永远是<code>self</code>,表示创建的实例本身,因此,在<code>__init__</code>方法内部,就可以把各种属性绑定到<code>self</code>,因为<code>self</code>就指向创建的实例本身,相当于<code>this</code>。有了<code>__init__</code>方法,在创建实例的时候,就不能传入空的参数了,必须传入与<code>__init__</code>方法匹配的参数,但<code>self</code>不需要传,Python解释器自己会把实例变量传进去。</p><p>和普通的函数相比,在类中定义的函数只有一点不同,就是第一个参数永远是实例变量<code>self</code>,并且,调用时,不用传递该参数。要调用一个方法,只需要在实例变量上直接调用,除了<code>self</code>不用传递,其他参数正常传入。</p><h2 id="访问限制"><a href="#访问限制" class="headerlink" title="访问限制"></a>访问限制</h2><p>如果要让内部属性不被外部访问,可以把属性的名称前加上两个下划线<code>__</code>,在Python中,实例的变量名如果以<code>__</code>开头,就变成了一个私有变量(private),只有内部可以访问,外部不能访问。</p><p>需要注意的是,在Python中,变量名类似<code>__xxx__</code>的,也就是以双下划线开头,并且以双下划线结尾的,是特殊变量,特殊变量是可以直接访问的,不是private变量,所以,不能用<code>__name__</code>、<code>__score__</code>这样的变量名。</p><p>以一个下划线开头的实例变量名,比如<code>_name</code>,这样的实例变量外部是可以访问的,但是,按照约定俗成的规定,当你看到这样的变量时,意思就是,“虽然我可以被访问,但是,请把我视为私有变量,不要随意访问”。</p><p>注意:</p><figure class="highlight python"><table><tr><td class="code"><pre><span class="line"><span class="meta">>>> </span>bart = Student(<span class="string">'Bart Simpson'</span>, <span class="number">59</span>)</span><br><span class="line"><span class="meta">>>> </span>bart.get_name()</span><br><span class="line"><span class="string">'Bart Simpson'</span></span><br><span class="line"><span class="meta">>>> </span>bart.__name = <span class="string">'New Name'</span> <span class="comment"># 设置__name变量!</span></span><br><span class="line"><span class="meta">>>> </span>bart.__name</span><br><span class="line"><span class="string">'New Name'</span></span><br></pre></td></tr></table></figure><p>表面上看,外部代码“成功”地设置了<code>__name</code>变量,但实际上这个<code>__name</code>变量和class内部的<code>__name</code>变量不是一个变量!内部的<code>__name</code>变量已经被Python解释器自动改成了<code>_Student__name</code>,而外部代码给<code>bart</code>新增了一个<code>__name</code>变量。</p><h2 id="继承和多态"><a href="#继承和多态" class="headerlink" title="继承和多态"></a>继承和多态</h2><p>判断一个变量是否是某个类型可以用<code>isinstance()</code>判断:</p><figure class="highlight python"><table><tr><td class="code"><pre><span class="line">a = <span class="built_in">list</span>() <span class="comment"># a是list类型</span></span><br><span class="line">b = Animal() <span class="comment"># b是Animal类型</span></span><br><span class="line">c = Dog() <span class="comment"># c是Dog类型</span></span><br><span class="line"></span><br><span class="line"><span class="meta">>>> </span><span class="built_in">isinstance</span>(a, <span class="built_in">list</span>)</span><br><span class="line"><span class="literal">True</span></span><br><span class="line"><span class="meta">>>> </span><span class="built_in">isinstance</span>(b, Animal)</span><br><span class="line"><span class="literal">True</span></span><br><span class="line"><span class="meta">>>> </span><span class="built_in">isinstance</span>(c, Dog)</span><br><span class="line"><span class="literal">True</span></span><br></pre></td></tr></table></figure><p>在继承关系中,如果一个实例的数据类型是某个子类,那它的数据类型也可以被看做是父类。但是,反过来就不行</p><p>对于静态语言(例如Java)来说,如果需要传入<code>Animal</code>类型,则传入的对象必须是<code>Animal</code>类型或者它的子类,否则,将无法调用<code>run()</code>方法。</p><p>对于Python这样的动态语言来说,则不一定需要传入<code>Animal</code>类型。我们只需要保证传入的对象有一个<code>run()</code>方法就可以了:</p><figure class="highlight python"><table><tr><td class="code"><pre><span class="line"><span class="keyword">class</span> <span class="title class_">Timer</span>(<span class="title class_ inherited__">object</span>):</span><br><span class="line"> <span class="keyword">def</span> <span class="title function_">run</span>(<span class="params">self</span>):</span><br><span class="line"> <span class="built_in">print</span>(<span class="string">'Start...'</span>)</span><br></pre></td></tr></table></figure><h2 id="获取对象信息"><a href="#获取对象信息" class="headerlink" title="获取对象信息"></a>获取对象信息</h2><h3 id="type"><a href="#type" class="headerlink" title="type()"></a>type()</h3><p>type()返回对应的class类型。</p><p>基本类型可以用type()来判断:</p><figure class="highlight python"><table><tr><td class="code"><pre><span class="line"><span class="meta">>>> </span><span class="built_in">type</span>(<span class="number">123</span>)</span><br><span class="line"><<span class="keyword">class</span> <span class="string">'int'</span>></span><br><span class="line"><span class="meta">>>> </span><span class="built_in">type</span>(<span class="string">'str'</span>)</span><br><span class="line"><<span class="keyword">class</span> <span class="string">'str'</span>></span><br><span class="line"><span class="meta">>>> </span><span class="built_in">type</span>(<span class="literal">None</span>)</span><br><span class="line"><<span class="built_in">type</span>(<span class="literal">None</span>) <span class="string">'NoneType'</span>></span><br></pre></td></tr></table></figure><p>指向函数或类的变量也用type()判断类型:</p><figure class="highlight python"><table><tr><td class="code"><pre><span class="line"><span class="meta">>>> </span><span class="built_in">type</span>(<span class="built_in">abs</span>)</span><br><span class="line"><<span class="keyword">class</span> <span class="string">'builtin_function_or_method'</span>></span><br><span class="line"><span class="meta">>>> </span><span class="built_in">type</span>(a)</span><br><span class="line"><<span class="keyword">class</span> <span class="string">'__main__.Animal'</span>></span><br></pre></td></tr></table></figure><p>如果在if中判断两个变量type基本类型是否相同,采用如下形式:</p><figure class="highlight python"><table><tr><td class="code"><pre><span class="line"><span class="meta">>>> </span><span class="built_in">type</span>(<span class="number">123</span>)==<span class="built_in">type</span>(<span class="number">456</span>)</span><br><span class="line"><span class="literal">True</span></span><br><span class="line"><span class="meta">>>> </span><span class="built_in">type</span>(<span class="number">123</span>)==<span class="built_in">int</span></span><br><span class="line"><span class="literal">True</span></span><br><span class="line"><span class="meta">>>> </span><span class="built_in">type</span>(<span class="string">'abc'</span>)==<span class="built_in">type</span>(<span class="string">'123'</span>)</span><br><span class="line"><span class="literal">True</span></span><br><span class="line"><span class="meta">>>> </span><span class="built_in">type</span>(<span class="string">'abc'</span>)==<span class="built_in">str</span></span><br><span class="line"><span class="literal">True</span></span><br><span class="line"><span class="meta">>>> </span><span class="built_in">type</span>(<span class="string">'abc'</span>)==<span class="built_in">type</span>(<span class="number">123</span>)</span><br><span class="line"><span class="literal">False</span></span><br></pre></td></tr></table></figure><p>判断一个对象是否是函数,就要使用<code>types</code>模块中的常量:</p><figure class="highlight python"><table><tr><td class="code"><pre><span class="line"><span class="meta">>>> </span><span class="keyword">import</span> types</span><br><span class="line"><span class="meta">>>> </span><span class="keyword">def</span> <span class="title function_">fn</span>():</span><br><span class="line"><span class="meta">... </span> <span class="keyword">pass</span></span><br><span class="line">...</span><br><span class="line"><span class="meta">>>> </span><span class="built_in">type</span>(fn)==types.FunctionType</span><br><span class="line"><span class="literal">True</span></span><br><span class="line"><span class="meta">>>> </span><span class="built_in">type</span>(<span class="built_in">abs</span>)==types.BuiltinFunctionType</span><br><span class="line"><span class="literal">True</span></span><br><span class="line"><span class="meta">>>> </span><span class="built_in">type</span>(<span class="keyword">lambda</span> x: x)==types.LambdaType</span><br><span class="line"><span class="literal">True</span></span><br><span class="line"><span class="meta">>>> </span><span class="built_in">type</span>((x <span class="keyword">for</span> x <span class="keyword">in</span> <span class="built_in">range</span>(<span class="number">10</span>)))==types.GeneratorType</span><br><span class="line"><span class="literal">True</span></span><br></pre></td></tr></table></figure><h3 id="isinstance"><a href="#isinstance" class="headerlink" title="isinstance()"></a>isinstance()</h3><p>对于class我们使用<code>isintance()</code>函数。<code>isinstance()</code>判断的是一个对象是否是该类型本身,或者位于该类型的父继承链上。总是优先使用isinstance()判断类型,可以将指定类型及其子类“一网打尽”。</p><p>如果继承关系是:</p><figure class="highlight python"><table><tr><td class="code"><pre><span class="line"><span class="built_in">object</span> -> Animal -> Dog -> Husky</span><br><span class="line"><span class="meta">>>> </span>a = Animal()</span><br><span class="line"><span class="meta">>>> </span>d = Dog()</span><br><span class="line"><span class="meta">>>> </span>h = Husky()</span><br></pre></td></tr></table></figure><p>那么</p><figure class="highlight python"><table><tr><td class="code"><pre><span class="line"><span class="meta">>>> </span><span class="built_in">isinstance</span>(h, Husky)</span><br><span class="line"><span class="literal">True</span></span><br><span class="line"><span class="meta">>>> </span><span class="built_in">isinstance</span>(h, Dog)</span><br><span class="line"><span class="literal">True</span></span><br><span class="line"><span class="meta">>>> </span><span class="built_in">isinstance</span>(h, Animal)</span><br><span class="line"><span class="literal">True</span></span><br></pre></td></tr></table></figure><p>能用<code>type()</code>判断的基本类型也可以用<code>isinstance()</code>判断:</p><figure class="highlight python"><table><tr><td class="code"><pre><span class="line"><span class="meta">>>> </span><span class="built_in">isinstance</span>(<span class="string">'a'</span>, <span class="built_in">str</span>)</span><br><span class="line"><span class="literal">True</span></span><br><span class="line"><span class="meta">>>> </span><span class="built_in">isinstance</span>(<span class="number">123</span>, <span class="built_in">int</span>)</span><br><span class="line"><span class="literal">True</span></span><br><span class="line"><span class="meta">>>> </span><span class="built_in">isinstance</span>(<span class="string">b'a'</span>, <span class="built_in">bytes</span>)</span><br><span class="line"><span class="literal">True</span></span><br></pre></td></tr></table></figure><p>并且可以判断一个变量是否是某些类型中的一种,例如判断是否是list或者tuple</p><figure class="highlight python"><table><tr><td class="code"><pre><span class="line"><span class="meta">>>> </span><span class="built_in">isinstance</span>([<span class="number">1</span>, <span class="number">2</span>, <span class="number">3</span>], (<span class="built_in">list</span>, <span class="built_in">tuple</span>))</span><br><span class="line"><span class="literal">True</span></span><br><span class="line"><span class="meta">>>> </span><span class="built_in">isinstance</span>((<span class="number">1</span>, <span class="number">2</span>, <span class="number">3</span>), (<span class="built_in">list</span>, <span class="built_in">tuple</span>))</span><br><span class="line"><span class="literal">True</span></span><br></pre></td></tr></table></figure><h3 id="dir"><a href="#dir" class="headerlink" title="dir()"></a>dir()</h3><p>获取一个对象所有属性和方法,使用<code>dir()</code>函数,返回一个包含字符串的list,比如获得一个str对象的所有属性和方法:</p><figure class="highlight python"><table><tr><td class="code"><pre><span class="line"><span class="meta">>>> </span><span class="built_in">dir</span>(<span class="string">'ABC'</span>)</span><br><span class="line">[<span class="string">'__add__'</span>, <span class="string">'__class__'</span>,..., <span class="string">'__subclasshook__'</span>, <span class="string">'capitalize'</span>, <span class="string">'casefold'</span>,..., <span class="string">'zfill'</span>]</span><br></pre></td></tr></table></figure><p>类似<code>__xxx__</code>的属性和方法在Python中都是有特殊用途的,比如<code>__len__</code>方法返回长度。在Python中,如果你调用<code>len()</code>函数试图获取一个对象的长度,实际上,在<code>len()</code>函数内部,它自动去调用该对象的<code>__len__()</code>方法,所以,下面的代码是等价的:</p><figure class="highlight python"><table><tr><td class="code"><pre><span class="line"><span class="meta">>>> </span><span class="built_in">len</span>(<span class="string">'ABC'</span>)</span><br><span class="line"><span class="number">3</span></span><br><span class="line"><span class="meta">>>> </span><span class="string">'ABC'</span>.__len__()</span><br><span class="line"><span class="number">3</span></span><br></pre></td></tr></table></figure><p>仅仅把属性和方法列出来是不够的,配合<code>getattr()</code>、<code>setattr()</code>以及<code>hasattr()</code>,我们可以直接操作一个对象的状态:</p><figure class="highlight python"><table><tr><td class="code"><pre><span class="line"><span class="meta">>>> </span><span class="keyword">class</span> <span class="title class_">MyObject</span>(<span class="title class_ inherited__">object</span>):</span><br><span class="line"><span class="meta">... </span> <span class="keyword">def</span> <span class="title function_">__init__</span>(<span class="params">self</span>):</span><br><span class="line"><span class="meta">... </span> self.x = <span class="number">9</span></span><br><span class="line"><span class="meta">... </span> <span class="keyword">def</span> <span class="title function_">power</span>(<span class="params">self</span>):</span><br><span class="line"><span class="meta">... </span> <span class="keyword">return</span> self.x * self.x</span><br><span class="line">...</span><br><span class="line"><span class="meta">>>> </span>obj = MyObject()</span><br></pre></td></tr></table></figure><p>可以测试该对象的属性:</p><figure class="highlight python"><table><tr><td class="code"><pre><span class="line"><span class="meta">>>> </span><span class="built_in">hasattr</span>(obj, <span class="string">'x'</span>) <span class="comment"># 有属性'x'吗?</span></span><br><span class="line"><span class="literal">True</span></span><br><span class="line"><span class="meta">>>> </span>obj.x</span><br><span class="line"><span class="number">9</span></span><br><span class="line"><span class="meta">>>> </span><span class="built_in">hasattr</span>(obj, <span class="string">'y'</span>) <span class="comment"># 有属性'y'吗?</span></span><br><span class="line"><span class="literal">False</span></span><br><span class="line"><span class="meta">>>> </span><span class="built_in">setattr</span>(obj, <span class="string">'y'</span>, <span class="number">19</span>) <span class="comment"># 设置一个属性'y'</span></span><br><span class="line"><span class="meta">>>> </span><span class="built_in">hasattr</span>(obj, <span class="string">'y'</span>) <span class="comment"># 有属性'y'吗?</span></span><br><span class="line"><span class="literal">True</span></span><br><span class="line"><span class="meta">>>> </span><span class="built_in">getattr</span>(obj, <span class="string">'y'</span>) <span class="comment"># 获取属性'y'</span></span><br><span class="line"><span class="number">19</span></span><br><span class="line"><span class="meta">>>> </span>obj.y <span class="comment"># 获取属性'y'</span></span><br><span class="line"><span class="number">19</span></span><br></pre></td></tr></table></figure><p>当获取不存在的属性时会抛出错误:</p><figure class="highlight python"><table><tr><td class="code"><pre><span class="line"><span class="meta">>>> </span><span class="built_in">getattr</span>(obj, <span class="string">'z'</span>) <span class="comment"># 获取属性'z'</span></span><br><span class="line">Traceback (most recent call last):</span><br><span class="line"> File <span class="string">"<stdin>"</span>, line <span class="number">1</span>, <span class="keyword">in</span> <module></span><br><span class="line">AttributeError: <span class="string">'MyObject'</span> <span class="built_in">object</span> has no attribute <span class="string">'z'</span></span><br></pre></td></tr></table></figure><p>可以传入一个default参数,如果属性不存在,就返回默认值:</p><figure class="highlight python"><table><tr><td class="code"><pre><span class="line"><span class="meta">>>> </span><span class="built_in">getattr</span>(obj, <span class="string">'z'</span>, <span class="number">404</span>) <span class="comment"># 获取属性'z',如果不存在,返回默认值404</span></span><br><span class="line"><span class="number">404</span></span><br></pre></td></tr></table></figure><p>以下是获得对象方法的例子:</p><figure class="highlight python"><table><tr><td class="code"><pre><span class="line"><span class="meta">>>> </span><span class="built_in">hasattr</span>(obj, <span class="string">'power'</span>) <span class="comment"># 有属性'power'吗?</span></span><br><span class="line"><span class="literal">True</span></span><br><span class="line"><span class="meta">>>> </span><span class="built_in">getattr</span>(obj, <span class="string">'power'</span>) <span class="comment"># 获取属性'power'</span></span><br><span class="line"><bound method MyObject.power of <__main__.MyObject <span class="built_in">object</span> at <span class="number">0x10077a6a0</span>>></span><br><span class="line"><span class="meta">>>> </span>fn = <span class="built_in">getattr</span>(obj, <span class="string">'power'</span>) <span class="comment"># 获取属性'power'并赋值到变量fn</span></span><br><span class="line"><span class="meta">>>> </span>fn <span class="comment"># fn指向obj.power</span></span><br><span class="line"><bound method MyObject.power of <__main__.MyObject <span class="built_in">object</span> at <span class="number">0x10077a6a0</span>>></span><br><span class="line"><span class="meta">>>> </span>fn() <span class="comment"># 调用fn()与调用obj.power()是一样的</span></span><br><span class="line"><span class="number">81</span></span><br></pre></td></tr></table></figure><p>一个例子如下:</p><figure class="highlight python"><table><tr><td class="code"><pre><span class="line"><span class="keyword">def</span> <span class="title function_">readImage</span>(<span class="params">fp</span>):</span><br><span class="line"> <span class="keyword">if</span> <span class="built_in">hasattr</span>(fp, <span class="string">'read'</span>):</span><br><span class="line"> <span class="keyword">return</span> readData(fp)</span><br><span class="line"> <span class="keyword">return</span> <span class="literal">None</span></span><br></pre></td></tr></table></figure><p>假设我们希望从文件流fp中读取图像,我们首先要判断该fp对象是否存在read方法,如果存在,则该对象是一个流,如果不存在,则无法读取。<code>hasattr()</code>就派上了用场。</p><p>在Python这类动态语言中,根据鸭子类型,有<code>read()</code>方法,不代表该fp对象就是一个文件流,它也可能是网络流,也可能是内存中的一个字节流,但只要<code>read()</code>方法返回的是有效的图像数据,就不影响读取图像的功能。</p><h3 id="实例属性和类属性"><a href="#实例属性和类属性" class="headerlink" title="实例属性和类属性"></a>实例属性和类属性</h3><p>根据类创建的实例可以任意绑定属性,给实例绑定属性的方法是通过实例变量,或者通过<code>self</code>变量,例如:</p><figure class="highlight python"><table><tr><td class="code"><pre><span class="line"><span class="keyword">class</span> <span class="title class_">Student</span>(<span class="title class_ inherited__">object</span>):</span><br><span class="line"> <span class="keyword">def</span> <span class="title function_">__init__</span>(<span class="params">self, name</span>):</span><br><span class="line"> self.name = name</span><br><span class="line"></span><br><span class="line">s = Student(<span class="string">'Bob'</span>)</span><br><span class="line">s.score = <span class="number">90</span></span><br></pre></td></tr></table></figure><p>如果student类本身需要绑定一个属性,可以直接在class中定义属性,这种属性是类属性,归student类所有:</p><figure class="highlight python"><table><tr><td class="code"><pre><span class="line"><span class="keyword">class</span> <span class="title class_">Student</span>(<span class="title class_ inherited__">object</span>):</span><br><span class="line"> name = <span class="string">'Student'</span></span><br></pre></td></tr></table></figure><p>当我们定义了一个类属性后,这个属性虽然归类所有,但类的所有实例都可以访问到。</p><figure class="highlight python"><table><tr><td class="code"><pre><span class="line"><span class="meta">>>> </span><span class="keyword">class</span> <span class="title class_">Student</span>(<span class="title class_ inherited__">object</span>):</span><br><span class="line"><span class="meta">... </span> name = <span class="string">'Student'</span></span><br><span class="line">...</span><br><span class="line"><span class="meta">>>> </span>s = Student() <span class="comment"># 创建实例s</span></span><br><span class="line"><span class="meta">>>> </span><span class="built_in">print</span>(s.name) <span class="comment"># 打印name属性,因为实例并没有name属性,所以会继续查找class的name属性</span></span><br><span class="line">Student</span><br><span class="line"><span class="meta">>>> </span><span class="built_in">print</span>(Student.name) <span class="comment"># 打印类的name属性</span></span><br><span class="line">Student</span><br><span class="line"><span class="meta">>>> </span>s.name = <span class="string">'Michael'</span> <span class="comment"># 给实例绑定name属性</span></span><br><span class="line"><span class="meta">>>> </span><span class="built_in">print</span>(s.name) <span class="comment"># 由于实例属性优先级比类属性高,因此,它会屏蔽掉类的name属性</span></span><br><span class="line">Michael</span><br><span class="line"><span class="meta">>>> </span><span class="built_in">print</span>(Student.name) <span class="comment"># 但是类属性并未消失,用Student.name仍然可以访问</span></span><br><span class="line">Student</span><br><span class="line"><span class="meta">>>> </span><span class="keyword">del</span> s.name <span class="comment"># 如果删除实例的name属性</span></span><br><span class="line"><span class="meta">>>> </span><span class="built_in">print</span>(s.name) <span class="comment"># 再次调用s.name,由于实例的name属性没有找到,类的name属性就显示出来了</span></span><br><span class="line">Student</span><br></pre></td></tr></table></figure><p>不要对实例属性和类属性使用相同的名字,因为相同名称的实例属性将屏蔽掉类属性,但是当你删除实例属性后,再使用相同的名称,访问到的将是类属性。</p><h1 id="面向对象高级编程"><a href="#面向对象高级编程" class="headerlink" title="面向对象高级编程"></a>面向对象高级编程</h1><h2 id="使用-slots"><a href="#使用-slots" class="headerlink" title="使用__slots__"></a>使用<code>__slots__</code></h2><p>当我们创建了一个实例后,我们可以给这个实例绑定任何属性和方法。</p><figure class="highlight python"><table><tr><td class="code"><pre><span class="line"><span class="meta">>>> </span><span class="keyword">def</span> <span class="title function_">set_age</span>(<span class="params">self, age</span>): <span class="comment"># 定义一个函数作为实例方法</span></span><br><span class="line"><span class="meta">... </span> self.age = age</span><br><span class="line">...</span><br><span class="line"><span class="meta">>>> </span><span class="keyword">from</span> types <span class="keyword">import</span> MethodType</span><br><span class="line"><span class="meta">>>> </span>s.set_age = MethodType(set_age, s) <span class="comment"># 给实例绑定一个方法</span></span><br><span class="line"><span class="meta">>>> </span>s.set_age(<span class="number">25</span>) <span class="comment"># 调用实例方法</span></span><br><span class="line"><span class="meta">>>> </span>s.age <span class="comment"># 测试结果</span></span><br><span class="line"><span class="number">25</span></span><br></pre></td></tr></table></figure><p>但是,给一个实例绑定的方法,对另一个实例是不起作用的。为了给所有实例都绑定方法,可以给class绑定方法,给class绑定方法后,所有实例均可调用:</p><figure class="highlight python"><table><tr><td class="code"><pre><span class="line"><span class="meta">>>> </span><span class="keyword">def</span> <span class="title function_">set_score</span>(<span class="params">self, score</span>):</span><br><span class="line"><span class="meta">... </span> self.score = score</span><br><span class="line">...</span><br><span class="line"><span class="meta">>>> </span>Student.set_score = set_score</span><br><span class="line"></span><br><span class="line"><span class="meta">>>> </span>s.set_score(<span class="number">100</span>)</span><br><span class="line"><span class="meta">>>> </span>s.score</span><br><span class="line"><span class="number">100</span></span><br><span class="line"><span class="meta">>>> </span>s2.set_score(<span class="number">99</span>)</span><br><span class="line"><span class="meta">>>> </span>s2.score</span><br><span class="line"><span class="number">99</span></span><br></pre></td></tr></table></figure><p>通常情况下,上面的<code>set_score</code>方法可以直接定义在class中,但动态绑定允许我们在程序运行的过程中动态给class加上功能。</p><p>如果想要限制实例的属性,比如只允许对student实例添加name和age属性,就要在定义class的时候定义一个特殊的<code>__slots__</code>变量,来限制该class实例能添加的属性:</p><figure class="highlight python"><table><tr><td class="code"><pre><span class="line"><span class="keyword">class</span> <span class="title class_">Student</span>(<span class="title class_ inherited__">object</span>):</span><br><span class="line"> __slots__ = (<span class="string">'name'</span>, <span class="string">'age'</span>) <span class="comment"># 用tuple定义允许绑定的属性名称</span></span><br><span class="line"></span><br><span class="line"><span class="meta">>>> </span>s = Student() <span class="comment"># 创建新的实例</span></span><br><span class="line"><span class="meta">>>> </span>s.name = <span class="string">'Michael'</span> <span class="comment"># 绑定属性'name'</span></span><br><span class="line"><span class="meta">>>> </span>s.age = <span class="number">25</span> <span class="comment"># 绑定属性'age'</span></span><br><span class="line"><span class="meta">>>> </span>s.score = <span class="number">99</span> <span class="comment"># 绑定属性'score'</span></span><br><span class="line">Traceback (most recent call last):</span><br><span class="line"> File <span class="string">"<stdin>"</span>, line <span class="number">1</span>, <span class="keyword">in</span> <module></span><br><span class="line">AttributeError: <span class="string">'Student'</span> <span class="built_in">object</span> has no attribute <span class="string">'score'</span></span><br></pre></td></tr></table></figure><p>由于<code>'score'</code>没有被放到<code>__slots__</code>中,所以不能绑定<code>score</code>属性,试图绑定<code>score</code>将得到<code>AttributeError</code>的错误。使用<code>__slots__</code>要注意,<code>__slots__</code>定义的属性仅对当前类实例起作用,对继承的子类是不起作用的:</p><figure class="highlight python"><table><tr><td class="code"><pre><span class="line"><span class="meta">>>> </span><span class="keyword">class</span> <span class="title class_">GraduateStudent</span>(<span class="title class_ inherited__">Student</span>):</span><br><span class="line"><span class="meta">... </span> <span class="keyword">pass</span></span><br><span class="line">...</span><br><span class="line"><span class="meta">>>> </span>g = GraduateStudent()</span><br><span class="line"><span class="meta">>>> </span>g.score = <span class="number">9999</span></span><br></pre></td></tr></table></figure><p>除非在子类中也定义<code>__slots__</code>,这样,子类实例允许定义的属性就是自身的<code>__slots__</code>加上父类的<code>__slots__</code>。</p>]]></content>
<tags>
<tag> 语言 </tag>
<tag> python </tag>
</tags>
</entry>
<entry>
<title>PCA主成分分析</title>
<link href="/2023/01/05/PCA%E4%B8%BB%E6%88%90%E5%88%86%E5%88%86%E6%9E%90/"/>
<url>/2023/01/05/PCA%E4%B8%BB%E6%88%90%E5%88%86%E5%88%86%E6%9E%90/</url>
<content type="html"><![CDATA[<p>PCA就是<strong>降维</strong>保存信息,例如将二维数据保留为一维数据。就是找到一个新的坐标系,这个坐标系的原点落在数据中心,坐标系的方向是往数据分布的方向走。我们需要保存的是新坐标系的原点、新坐标系的角度和新的坐标点。</p><p>PCA的目标就是要找到坐标系,使得保留了某些维度的时候,<strong>信息损失是最小的(信息保留最多)<strong>,比如说投影在某个轴上面,</strong>数据分布是最分散的</strong>。我们要找到数据分布最分散的方向(方差最大),作为主成分(坐标轴)。新坐标系的第一个维度称为主成分一,第二个维度称为主成分二,如果我们找到数据在主成分一上面的投影分布方差是最大的时候,那么说明主成分一它能够保留最多的信息,在这个时候的坐标系就是最好的坐标系。</p><p><img src="/2023/01/05/PCA%E4%B8%BB%E6%88%90%E5%88%86%E5%88%86%E6%9E%90/%E5%9D%90%E6%A0%87%E8%BD%B4%E7%A4%BA%E6%84%8F%E5%9B%BE.png" alt="坐标轴示意图"></p><p>如何实现PCA?</p><p>1.首先是要去中心化,就是把坐标原点放在数据的中心。方法就是把每一个值减去全部值的平均值。移动数据并不会改变数据点彼此之间的相对位置。</p><p>2.然后就是找坐标系(找到方差最大的方向) </p><p>我们该如何找到方差最大的方向呢?</p><p><img src="/2023/01/05/PCA%E4%B8%BB%E6%88%90%E5%88%86%E5%88%86%E6%9E%90/%E6%95%B0%E6%8D%AE%E7%BA%BF%E6%80%A7%E5%8F%98%E6%8D%A2.png" alt="数据线性变换"></p><p>如果只是数据对角矩阵。用待变换矩阵左乘它就会得到<strong>伸缩</strong>之后的坐标,如图所示就是将x轴拉伸2倍长。原来是单位矩阵左乘,那么是让坐标不变,现在相应的扩大几倍。</p><p><img src="/2023/01/05/PCA%E4%B8%BB%E6%88%90%E5%88%86%E5%88%86%E6%9E%90/%E6%95%B0%E6%8D%AE%E7%BA%BF%E6%80%A7%E5%8F%98%E6%8D%A22.png" alt="数据线性变换2"></p><p>在矩阵运算上面可以想象到,这是逆时针旋转相应度数</p><p><img src="/2023/01/05/PCA%E4%B8%BB%E6%88%90%E5%88%86%E5%88%86%E6%9E%90/%E8%BD%AC%E6%8D%A2.png" alt="转换"></p><p><strong>我们手上的数据就是我们要降维的数据</strong>。可以通过白数据左乘S再左乘R得到。</p><p>拉伸和旋转变换有什么作用?</p><p>拉伸决定了<strong>方差最大的方向</strong>是横或者纵向。旋转决定了方差最大的方向的角度是多大。<strong>所以我们要求的就是R矩阵</strong>。</p><p><img src="/2023/01/05/PCA%E4%B8%BB%E6%88%90%E5%88%86%E5%88%86%E6%9E%90/%E8%BD%AC%E6%8D%A22.png" alt="转换2"></p><p><strong>我们手上的数据</strong>D’同样也可以转换回白数据。可以先乘一个R的逆矩阵,它本来是逆时针旋转,取逆就是再顺时针旋转同样的度数。再拉伸,压缩回拉伸的倒数。</p><p>怎么求R?<strong>协方差矩阵的特征向量就是R</strong>。</p><p>何为协方差矩阵?</p><p><img src="/2023/01/05/PCA%E4%B8%BB%E6%88%90%E5%88%86%E5%88%86%E6%9E%90/%E4%BB%80%E4%B9%88%E6%98%AF%E5%8D%8F%E6%96%B9%E5%B7%AE.png" alt="什么是协方差"></p><p><img src="/2023/01/05/PCA%E4%B8%BB%E6%88%90%E5%88%86%E5%88%86%E6%9E%90/%E5%8D%8F%E6%96%B9%E5%B7%AE%E7%9F%A9%E9%98%B5.png" alt="协方差矩阵"></p><p>白数据x,y不相关,这个时候协方差为0,只剩下对角线,即x、y方差为1就是一个单位矩阵。数据正相关协方差大于0,数据负相关,协方差小于0。</p><p>将协方差的公式代入协方差矩阵得到:再提个1/(N-1)</p><p><img src="/2023/01/05/PCA%E4%B8%BB%E6%88%90%E5%88%86%E5%88%86%E6%9E%90/%E4%BB%A3%E5%85%A5%E5%8D%8F%E6%96%B9%E5%B7%AE%E7%9F%A9%E9%98%B5.png" alt="代入协方差矩阵"></p><p>这里的D是移至原点后的<strong>原始数据矩阵</strong></p><p>我的想法是C既拉伸又旋转?</p><p><img src="/2023/01/05/PCA%E4%B8%BB%E6%88%90%E5%88%86%E5%88%86%E6%9E%90/%E6%89%8B%E4%B8%8A%E6%95%B0%E6%8D%AE%E7%9A%84%E5%8D%8F%E6%96%B9%E5%B7%AE.png" alt="手上数据的协方差"></p><p><img src="/2023/01/05/PCA%E4%B8%BB%E6%88%90%E5%88%86%E5%88%86%E6%9E%90/%E5%8D%8F%E6%96%B9%E5%B7%AE%E7%9A%84%E7%89%B9%E5%BE%81%E5%90%91%E9%87%8F.png" alt="协方差的特征向量"></p><img src="/2023/01/05/PCA%E4%B8%BB%E6%88%90%E5%88%86%E5%88%86%E6%9E%90/R和L矩阵.png" alt="R和L矩阵" style="zoom: 80%;"><p>把特征值放到一起组成L矩阵,把特征向量v1和v2组合到一起成为R</p><p><img src="/2023/01/05/PCA%E4%B8%BB%E6%88%90%E5%88%86%E5%88%86%E6%9E%90/%E5%8D%8F%E6%96%B9%E5%B7%AE%E7%9A%84%E7%89%B9%E5%BE%81%E5%80%BC.png" alt="协方差的特征值"></p><p>旋转回来协方差是L,因为R逆是一个对角矩阵,旋转回来之后x方向和y方向不相关,x方向的方差是a²,y方向的方差是b²,同时又是协方差矩阵的特征值。</p><p><img src="/2023/01/05/PCA%E4%B8%BB%E6%88%90%E5%88%86%E5%88%86%E6%9E%90/%E5%A6%82%E4%BD%95%E6%B1%82%E8%A7%A3PCA.png" alt="如何求解PCA"></p><p>如何判断PCA拟合度的高低?我们可以通过量化数据在主成分上的投影长度和最小或者数据在投影点到中心点的距离和最大,通常我们选择后者,因为后者更方便。</p><p>计算每个主成分的差异率就是把主成分分别的特征值除以全部的特征值加起来,越大越好,代表信息越多</p><p>主成分分析的应用</p><p><img src="/2023/01/05/PCA%E4%B8%BB%E6%88%90%E5%88%86%E5%88%86%E6%9E%90/%E4%B8%A4%E7%BB%84%E6%95%B0%E6%8D%AE.png" alt="两组数据"></p><p><img src="/2023/01/05/PCA%E4%B8%BB%E6%88%90%E5%88%86%E5%88%86%E6%9E%90/%E6%A0%B7%E6%9C%AC%E7%82%B9%E5%8E%BB%E4%B8%AD%E5%BF%83%E5%8C%96.png" alt="样本点去中心化"></p><p><img src="/2023/01/05/PCA%E4%B8%BB%E6%88%90%E5%88%86%E5%88%86%E6%9E%90/%E7%BB%93%E6%9E%9C.png" alt="结果"></p><p><img src="/2023/01/05/PCA%E4%B8%BB%E6%88%90%E5%88%86%E5%88%86%E6%9E%90/%E7%BB%98%E5%9B%BE.png" alt="绘图"></p><p><img src="/2023/01/05/PCA%E4%B8%BB%E6%88%90%E5%88%86%E5%88%86%E6%9E%90/%E5%8E%9F%E6%9C%89%E6%95%B0%E6%8D%AE%E8%BD%AC%E5%8C%96%E4%B8%BA%E4%B8%80%E7%BB%B4%E6%95%B0%E6%8D%AE.png" alt="原有数据转化为一维数据"></p><p><img src="/2023/01/05/PCA%E4%B8%BB%E6%88%90%E5%88%86%E5%88%86%E6%9E%90/PCA%E6%AD%A5%E9%AA%A4.png" alt="PCA步骤"></p><p>主成分分析的本质就是向量换基。</p><p>主成分分析通过对投影距离方差的运用将降维问题转换成了求最值的问题。</p><p>主成分分为<strong>主成分分析和主成分评价</strong>两个方面,分析就是单纯的分析数据是否具有主成分和主成分效果如何,评价就是根据主成分运行的结果直接评价了。</p><p>在数模中<strong>主成分分析用于评价类的问题,即综合评估</strong></p><p>使用的<strong>注意要求</strong>是:指标之间的<strong>相关性比较高</strong>,一般需要对<strong>数据的相关性或者主成分分析的结果进行分析</strong>后,如果效果比较好,再使用主成分分析,如果效果不好,就不要使用主成分分析。</p><p>也就是说协方差越大,相关性越高,一般主成分分析相关性大部分变量协方差要大于0.3</p><p>主成分说白了就是在评价的时候有很多指标,因为指标太多了,并且各个指标之间相互有影响,为了消除指标之间的影响,单纯从数据的角度寻找各个指标具有公共特征,这些公共的特征就是主成分,也就是常说的第一主成分,第二主成分,第N主成分。具体的第一主成分第二主成分以及累计贡献率(要保证所有主成分累计对原始数据的贡献达到80%,即差异率超80%)是如何计算出的,可以不用掌握,且在比赛的时候也没必要写在论文上,只需要给出主要的结果即可。</p><p>主成分分析目的不是用来分类,而是用于综合评价</p>]]></content>
<tags>
<tag> 数学建模 </tag>
<tag> 评价类模型 </tag>
<tag> 线性代数 </tag>
</tags>
</entry>
<entry>
<title>Action</title>
<link href="/2022/12/30/Action/"/>
<url>/2022/12/30/Action/</url>
<content type="html"><![CDATA[<h2 id="上传一个文件到博客的步骤如下:"><a href="#上传一个文件到博客的步骤如下:" class="headerlink" title="上传一个文件到博客的步骤如下:"></a>上传一个文件到博客的步骤如下:</h2><h3 id="清理缓存"><a href="#清理缓存" class="headerlink" title="清理缓存"></a>清理缓存</h3><figure class="highlight bash"><table><tr><td class="code"><pre><span class="line">$ hexo clean</span><br></pre></td></tr></table></figure><h3 id="生成新的静态文件"><a href="#生成新的静态文件" class="headerlink" title="生成新的静态文件"></a>生成新的静态文件</h3><figure class="highlight bash"><table><tr><td class="code"><pre><span class="line">$ hexo g</span><br></pre></td></tr></table></figure><h3 id="本地预览博客"><a href="#本地预览博客" class="headerlink" title="本地预览博客"></a>本地预览博客</h3><figure class="highlight bash"><table><tr><td class="code"><pre><span class="line">$ hexo s</span><br></pre></td></tr></table></figure><h3 id="上传到github仓库,将文件部署到博客上"><a href="#上传到github仓库,将文件部署到博客上" class="headerlink" title="上传到github仓库,将文件部署到博客上"></a>上传到github仓库,将文件部署到博客上</h3><figure class="highlight bash"><table><tr><td class="code"><pre><span class="line">$ hexo d</span><br></pre></td></tr></table></figure><h2 id="其他的一些设置:"><a href="#其他的一些设置:" class="headerlink" title="其他的一些设置:"></a>其他的一些设置:</h2><h3 id="生成新的md文件"><a href="#生成新的md文件" class="headerlink" title="生成新的md文件"></a>生成新的md文件</h3><figure class="highlight bash"><table><tr><td class="code"><pre><span class="line">$ hexo n</span><br></pre></td></tr></table></figure><h3 id="创建新的md以及对应文件夹"><a href="#创建新的md以及对应文件夹" class="headerlink" title="创建新的md以及对应文件夹"></a>创建新的md以及对应文件夹</h3><figure class="highlight bash"><table><tr><td class="code"><pre><span class="line">$ hexo new post <span class="string">'FileName'</span></span><br></pre></td></tr></table></figure><h2 id="butterfly设置:"><a href="#butterfly设置:" class="headerlink" title="butterfly设置:"></a>butterfly设置:</h2><h3 id="创建一个新的标签页"><a href="#创建一个新的标签页" class="headerlink" title="创建一个新的标签页"></a>创建一个新的标签页</h3><figure class="highlight bash"><table><tr><td class="code"><pre><span class="line">$ hexo new page tags</span><br></pre></td></tr></table></figure><h3 id="创建一个分类页"><a href="#创建一个分类页" class="headerlink" title="创建一个分类页"></a>创建一个分类页</h3><figure class="highlight bash"><table><tr><td class="code"><pre><span class="line">$ hexo new page categories</span><br></pre></td></tr></table></figure><h3 id="创建友情链接"><a href="#创建友情链接" class="headerlink" title="创建友情链接"></a>创建友情链接</h3><figure class="highlight bash"><table><tr><td class="code"><pre><span class="line">$ hexo new page <span class="built_in">link</span></span><br></pre></td></tr></table></figure>]]></content>
<tags>
<tag> 技术操作 </tag>
</tags>
</entry>
<entry>
<title>CNN</title>
<link href="/2022/12/30/CNN/"/>
<url>/2022/12/30/CNN/</url>
<content type="html"><![CDATA[<p><strong>卷积神经网络用处在哪?</strong></p><p>卷积神经网络是用于特征提取的。</p><p>传统的神经网络权重参数矩阵大、训练时间多、过拟合风险高。</p><p>可用于检测追踪任务、分类和检索、超分辨率重构、无人驾驶、人脸识别等</p><img src="/2022/12/30/CNN/超分辨率.png" alt="超分辨率" style="zoom:50%;"><p><strong>卷积神经网络(CNN)与传统网络(NN)的区别:</strong></p><img src="/2022/12/30/CNN/卷积神经网络与传统网络的区别.png" alt="卷积神经网络与传统网络的区别" style="zoom: 80%;"><ul><li>NN输入的是像素点,而CNN输入的是一张原始图像(h×w×c),一个是一维,一个是三维。</li></ul><p><strong>卷积神经网络的整体架构:</strong></p><ul><li>输入层:输入一个图像数据</li><li>卷积层:尽可能多的提取特征</li><li>池化层:压缩、下采样特征</li><li>全连接层:通过一组权重参数,把输入层和输出层连在一起</li></ul><p><strong>卷积:</strong>卷积做了一件什么事?</p><img src="/2022/12/30/CNN/卷积做了一件什么事.png" alt="卷积做了一件什么事" style="zoom:60%;"><p>对不同的区域提取出不同的特征,将一张图像分成不同的部分,区别处理,进行图像分割。</p><p><strong>图像颜色通道</strong>:</p><p>R,G,B:要对三个颜色通道分别做计算,把三个通道卷积完的结果<strong>加</strong>在一起。对于每一个区域都要进行特征提取,得到最终的特征值。</p><img src="/2022/12/30/CNN/RGB特征提取.png" alt="RGB特征提取" style="zoom:67%;"><p>输入的图像维度c是多少,那么卷积核的维度c也应该是多少。</p><h3 id="卷积层"><a href="#卷积层" class="headerlink" title="卷积层"></a>卷积层</h3><p>卷积核就是:每多大区域选出一个特征,一个区域对应出一个特征值。 相当于权重,刚开始是随机初始化的,然后学习更新。即下图W0</p><p>所有的卷积网络都是用<strong>内积</strong>做计算,<strong>对应位置相乘</strong>,所有结果加一起就可以了。结果别忘了加一个偏置项。在每一个卷积层中的特征矩阵w,h应该是相同的。在不通的卷积层中w,h可以不同</p><p><img src="/2022/12/30/CNN/%E5%8D%B7%E7%A7%AF%E8%BF%87%E7%A8%8B.png" alt="卷积过程"></p><p><img src="/2022/12/30/CNN/%E5%8D%B7%E7%A7%AF%E8%BF%87%E7%A8%8B2.png" alt="卷积过程2"></p><p><strong>卷积层涉及的参数</strong>:</p><ul><li><p>滑动窗口步长:注意<strong>步长</strong>为2得到的最终结果才是3*3,步长小,提取特征比较细致,效率慢;步长大,提取特征比较粗糙,特征少。<strong>一般对于图像而言,我们选择步长为1就可以</strong>,但是对于文本数据和其他数据步长不确定。</p></li><li><p>卷积核尺寸:卷积核越小,特征提取越细致,一般来说选<strong>3*3</strong></p></li><li><p>边缘填充:在边界外再加几圈0,能够弥补一些边界信息利用不充分问题。最外层只是扩充,因为是0,所以对最终结果不会产生影响。一般添一圈</p></li><li><p>卷积核个数:在算的过程当中要得到多少个特征图就有多少个卷积核。</p></li></ul><p>特征图的个数:特征图的个数取决于你给了多少份的权重矩阵,选择不通的权重矩阵,得到的特征图个数结果不一样。</p><p>卷积神经网络不止可以做一次卷积,一次可以提取出粗略特征,再卷积一次可以提取出中间特征,最后再提取出高级特征,再拿出高级特征来做分类。做一次卷积是不够的,需要做多次。</p><p><strong>卷积结果计算公式:</strong></p><img src="/2022/12/30/CNN/卷积结果计算公式.png" alt="卷积结果计算公式" style="zoom:80%;"><img src="/2022/12/30/CNN/卷积结果计算例子.png" alt="卷积结果计算例子" style="zoom: 33%;"><p><strong>卷积参数共享:</strong></p><p> 对于图中的每个区域都选择同样的卷积核,卷积核这个权值矩阵是不变的</p><p><img src="/2022/12/30/CNN/%E5%8F%82%E6%95%B0%E5%85%B1%E4%BA%AB.png" alt="参数共享"></p><p>每次卷积完都要加一个RELU函数,即非线性变换</p><h3 id="池化层"><a href="#池化层" class="headerlink" title="池化层"></a>池化层</h3><p>在原始得到的特征上进行一个筛选,并不会改变特征图的个数。不涉及矩阵计算,只涉及筛选。</p><p><strong>最大池化</strong>:</p><p>选择不同区域,在每个区域中选择最大值,选择一个最大值说明这个特征比较重要。</p><img src="/2022/12/30/CNN/最大池化.png" alt="最大池化" style="zoom: 67%;"><p><strong>平均池化</strong>:</p><p>选择不同区域,在每个区域中求平均值。</p><p>我们<strong>选择最大池化</strong>,因为神经网络是个优胜劣汰的过程,我们选择最好的特征,不平均来把不好的特征拷进去。</p><p>两次卷积后一次池化,RELU是激活函数</p><p><img src="/2022/12/30/CNN/%E5%8D%B7%E7%A7%AF%E6%B1%A0%E5%8C%96.png" alt="卷积池化"></p><p>卷积和池化只是做特征提取的,到最后池化后会形成立体的特征图,对特征图进行分类,如何转化为分类的概率值?全连接层无法连三维的东西,我们需要将三维的特征图拉长形成特征向量,全连接层如果是五分类,池化层得到的特征图大小为<code>32*32*10</code>,那么得到的全连接层是[10240,5],相当于将10240个特征转化为我们预测的五个类别的概率值。所以在Pooling层和FC层之间还有一个拉长的操作(转换操作)</p><p><strong>什么才能称为一层</strong>?带参数计算的才能被称为一层。卷积层带,RELU层(激活层)不带参数计算,池化层不带参数计算,不更新参数之类的,全连接层也有权重参数矩阵,需要更新参数。</p><img src="/2022/12/30/CNN/特征图的变化.png" alt="特征图的变化" style="zoom:67%;"><p><strong>感受野:</strong></p><img src="/2022/12/30/CNN/感受野.png" alt="感受野" style="zoom:60%;"><p>我们希望感受野越大越好。</p><img src="/2022/12/30/CNN/感受野2.png" alt="感受野2" style="zoom:60%;">]]></content>
<tags>
<tag> 数学建模 </tag>
<tag> 深度学习 </tag>
</tags>
</entry>
<entry>
<title></title>
<link href="/2022/12/30/%E8%AE%A1%E7%BD%91/"/>
<url>/2022/12/30/%E8%AE%A1%E7%BD%91/</url>
<content type="html"><![CDATA[<h1 id="1-概述"><a href="#1-概述" class="headerlink" title="1 概述"></a>1 概述</h1><p>三类常见网络:<strong>电信网络、有线电视网络、计算机网络</strong>。</p><p>互联网的两个重要基本特点:<strong>连通性和共享</strong>。</p><h2 id="1-2-互联网概述"><a href="#1-2-互联网概述" class="headerlink" title="1.2 互联网概述"></a>1.2 互联网概述</h2><h3 id="1-2-1-网络的网络"><a href="#1-2-1-网络的网络" class="headerlink" title="1.2.1 网络的网络"></a>1.2.1 网络的网络</h3><p>计算机网络(网络)由若干节点和连接这些节点的链路组成。</p><ul><li>两台计算机连接也是计算机网络</li></ul><p>多个网络通过路由相互连接起来,构成了互联网,互联网是“网络的网络”。</p><p>与网络相连的计算机称为主机。</p><h2 id="1-3互联网的组成"><a href="#1-3互联网的组成" class="headerlink" title="1.3互联网的组成"></a>1.3互联网的组成</h2><p>1.边缘部分:由所有连接在互联网上的主机组成,这部分是用户直接使用的。</p><p>2.核心部分:由大量网络和连接这些网络的路由器组成,这部分是为边缘部分提供服务的(提供连通性和交换)。</p><h3 id="1-3-1-互联网的边缘部分"><a href="#1-3-1-互联网的边缘部分" class="headerlink" title="1.3.1 互联网的边缘部分"></a>1.3.1 互联网的边缘部分</h3><p>网络边缘的端系统之间的通信方式可以划为两大类:客户-服务器方式(C/S方式)和对等方式(P2P方式)</p><h4 id="1-客户-服务器方式(客户-x2F-服务器方式)"><a href="#1-客户-服务器方式(客户-x2F-服务器方式)" class="headerlink" title="1.客户-服务器方式(客户/服务器方式)"></a>1.客户-服务器方式(客户/服务器方式)</h4><ul><li><p>客户(client)和服务器(server)都是指通信中涉及的两个应用进程。</p></li><li><p>客户-服务器方式所描述的是进程之间服务和被服务的关系。</p></li><li><p>客户是服务请求方,服务器是服务提供方。</p></li><li><p>数据集中在服务器中。</p></li></ul><h4 id="2-对等连接方式"><a href="#2-对等连接方式" class="headerlink" title="2.对等连接方式"></a>2.对等连接方式</h4><ul><li><p>P2P并不区分请求方和提供方</p></li><li><p>本质上还是客户-服务器方式</p></li><li><p>每一台主机既是客户又是服务器</p></li></ul><p>QQ是P2P和C/S的结合:在线P2P,离线C/S</p><h3 id="1-3-2-互联网的核心部分"><a href="#1-3-2-互联网的核心部分" class="headerlink" title="1.3.2 互联网的核心部分"></a>1.3.2 互联网的核心部分</h3><p>网络核心部分起特殊作用的是路由器,路由器是实现分组交换的关键构建,其任务是转发收到的分组。</p><h4 id="1-电路交换的主要特点"><a href="#1-电路交换的主要特点" class="headerlink" title="1.电路交换的主要特点"></a>1.电路交换的主要特点</h4><ul><li>在通话的全部时间内,通话的两个客户始终占用端到端的通信资源</li><li>线路的传输效率低</li><li>整个报文的比特流连续地从源点直达终点,好像在一个管道中传送</li></ul><h4 id="2-分组交换的主要特点"><a href="#2-分组交换的主要特点" class="headerlink" title="2.分组交换的主要特点"></a>2.分组交换的主要特点</h4><ul><li>采用存储转发方式,把报文划分为几个分组后再进行传送</li><li>路由器是分组交换的工具,用于转发分组。</li><li>单个分组传送到相邻节点,存储下来后查找转发表,转发到下一节点</li></ul><h4 id="3-报文交换"><a href="#3-报文交换" class="headerlink" title="3.报文交换"></a>3.报文交换</h4><ul><li>报文交换也属于存储转发方式</li><li>整个报文先传送到相邻节点,全部存储下来后查找转发表,转发到下一个节点。</li></ul><h2 id="1-5-计算机网络的类别"><a href="#1-5-计算机网络的类别" class="headerlink" title="1.5 计算机网络的类别"></a>1.5 计算机网络的类别</h2><h3 id="1-5-2-几种不同类别的计算机网络"><a href="#1-5-2-几种不同类别的计算机网络" class="headerlink" title="1.5.2 几种不同类别的计算机网络"></a>1.5.2 几种不同类别的计算机网络</h3><h4 id="1-按照网络的作用范围进行分类"><a href="#1-按照网络的作用范围进行分类" class="headerlink" title="1. 按照网络的作用范围进行分类"></a>1. 按照网络的作用范围进行分类</h4><ul><li><p>广域网WAN(wide)。广域网的作用范围为几十到几千公里。</p></li><li><p>城域网MAN。城域网的作用范围一般是一个城市,作用距离约为5~50km。</p></li><li><p>局域网LAN(local)。局域网在地理上局限在较小的范围如1km左右。</p></li><li><p>个人区域网PAN(personal)。作用范围很小,大概在10m左右。</p></li><li><p>人体区域网BAN(body)。作用范围仅限于人体。</p></li></ul><h4 id="2-按照网络的使用者进行分类"><a href="#2-按照网络的使用者进行分类" class="headerlink" title="2.按照网络的使用者进行分类"></a>2.按照网络的使用者进行分类</h4><ul><li><p>公用网。所有愿意按电信公司规定缴纳费用的人都可以使用的网络。</p></li><li><p>专用网。为满足某单位的特殊业务工作需要而建造的网络,不向单位之外的人提供服务。</p></li></ul><h4 id="3-用来把用户接入到互联网的网络"><a href="#3-用来把用户接入到互联网的网络" class="headerlink" title="3.用来把用户接入到互联网的网络"></a>3.用来把用户接入到互联网的网络</h4><p>接入网。接入网只是起到让用户与互联网连接的桥梁作用。</p><h2 id="1-6计算机网络的性能"><a href="#1-6计算机网络的性能" class="headerlink" title="1.6计算机网络的性能"></a>1.6计算机网络的性能</h2><h3 id="1-6-1-计算机网络的性能指标"><a href="#1-6-1-计算机网络的性能指标" class="headerlink" title="1.6.1 计算机网络的性能指标"></a>1.6.1 计算机网络的性能指标</h3><ul><li>速率:数据的传送速率。</li><li>带宽:带宽用来表示网络中某通道传送数据的能力,网络带宽表示在单位时间内网络中的某信道所能通过的最高数据率。</li><li>吞吐量:吞吐量表示在单位时间内通过某个网络的实际数据量。</li><li>时延:时延指的是数据从网络的一端传送到另一端所需要的时间。包括发送时延、传播时延、处理时延、排队时延。发送时延也叫传输时延,是指主机或路由器发送数据帧所需要的时间,也就是从发送数据帧的第一个比特算起,到该帧的最后一个比特发送完毕所需的时间;传播时延是指电磁波在信道中传播一定的距离需要花费的时间;处理时延是指主机或路由器在收到分组时花费时间进行处理所需要的时间;排队时延是指分组经过路由器需要先在输入队列中排队等待处理所花费的时间。数据在网络中经历的总时延就是以上四种时延之和。</li><li>时延带宽积:传播时延和带宽相乘就是时延带宽积,代表从发送端发出但尚未到达接收端的比特数。</li><li>往返时间RTT:主机双向交互一次所需要的时间。</li><li>利用率:利用率分为信道利用率和网络利用率两种。信道利用率指某信道有百分之几的时间是被利用的;网络利用率则是全网络的信道利用率的加权平均值。</li></ul><h2 id="1-7-计算机网络体系结构"><a href="#1-7-计算机网络体系结构" class="headerlink" title="1.7 计算机网络体系结构"></a>1.7 计算机网络体系结构</h2><h3 id="1-7-2-协议与划分层次"><a href="#1-7-2-协议与划分层次" class="headerlink" title="1.7.2 协议与划分层次"></a>1.7.2 协议与划分层次</h3><p>协议是为进行网络中的数据交换而建立的规则、标准或约定。</p><p><strong>协议由三要素组成:</strong></p><ul><li>语法,即数据与控制信息的结构或格式。</li><li>语义,即需要发出何种控制信息,完成何种动作以及作出何种相应。</li><li>同步,即事件实现顺序的详细说明。</li></ul><p><strong>分层的好处?</strong></p><p>相互通信的计算机系统必须高度协调工作,这种协调是相当复杂的,“分层”可将庞大而复杂的问题,转化为若干较小的局部问题,而这些较小的局部问题就比较易于研究和处理。分层具有减小问题复杂程度、灵活性好、结构上可分割开、易于实现和维护、能促进标准化工作的优点。</p><p>计算机网络的各层及其协议的集合就是网络的体系结构。</p><h3 id="1-7-3-具有五层协议的体系结构"><a href="#1-7-3-具有五层协议的体系结构" class="headerlink" title="1.7.3 具有五层协议的体系结构"></a>1.7.3 具有五层协议的体系结构</h3><p>五层协议从下到上分为物理层、数据链路层、网络层、传输层、应用层。</p><ul><li>应用层通过应用进程间的交互来完成特定网络应用。提供各种网络应用服务,使用户防变进行网络通信和交互。</li><li>运输层负责向两台主机中进程之间的通信提供通用的数据传输服务。起到兜底保证运输的作用。</li><li>网络层将数据报封装成分组和包进行传送。在每个路由器上生成转发分组的转发表以及在路由器接收到分组时依据转发表中的路径把分组转发到下一个路由器。</li><li>数据链路层将网络层交下来的IP数据包组装成帧,并将接收到的帧提取出数据部分交给网络层。数据链路层需要检查收到的帧中是否有差错,并且需要进行纠错。</li><li>物理层要考虑用多大的电压代表0和1,以及接收方如何识别出发送方所发送的比特,还要确定连接电缆的插头应当有多少根引脚以及各引脚应如何连接。</li></ul><h3 id="1-7-4-实体、协议、服务和服务访问点"><a href="#1-7-4-实体、协议、服务和服务访问点" class="headerlink" title="1.7.4 实体、协议、服务和服务访问点"></a>1.7.4 实体、协议、服务和服务访问点</h3><p>实体:实体表示任何可以发送或接收信息的硬件或软件进程。在许多情况下,实体就是一个特定的软件模块。</p><p>协议:协议是控制两个对等实体进行通信的规则的集合。</p><p>服务:在协议的控制下,两个对等实体间的通信使得本层能够向上一层提供服务。要实现本层协议,还需要使用下面一层所提供的服务。</p><p><strong>协议和服务的区别?</strong></p><p>协议是控制两个对等实体进行通信的规则的集合,是水平的。服务是下层通过层间接口向上层提供的功能,是垂直的;在协议的控制下,两个对等实体间的通信使得本层能够向上一层提供服务,要实现本层协议,还需要使用下面一层所提供的服务。</p><h3 id="1-7-5-TCP-x2F-IP的体系结构"><a href="#1-7-5-TCP-x2F-IP的体系结构" class="headerlink" title="1.7.5 TCP/IP的体系结构"></a>1.7.5 TCP/IP的体系结构</h3><ul><li>TCP/IP体系结构有四层,包括:链路层、网际层、运输层、应用层。</li><li>路由器没有应用层和运输层,路由器既解封装又加封装。</li><li>网际层的可以为各式应用提供服务,用于各种网络上去。</li></ul><h1 id="2-物理层"><a href="#2-物理层" class="headerlink" title="2 物理层"></a>2 物理层</h1><p>物理层考虑的是怎样才能在连接各种计算机的传输媒体上传输数据比特流,而<strong>不是指具体的传输媒体</strong>。</p><p>物理层的作用是要尽可能地<strong>屏蔽掉传输媒体和通信手段的差异</strong>。即不论用什么接口都能通信。</p><p>用于物理层的协议也常称为物理层<strong>规程</strong>。</p><p>物理层的主要任务是<strong>与传输接口有关</strong>的一些特性:</p><ul><li><strong>机械特性</strong>:指明接口所用接线器的形状和尺寸、引脚数目和排列、固定和锁定装置等。平时常见的各种规格的接插件都有严格的标准化的规定。</li><li><strong>电气特性</strong>:指明在接口电缆的各条线上出现的电压的范围。</li><li><strong>功能特性</strong>:指明某条线上出现的某一电平的电压的意义。</li><li><strong>过程特性</strong>:指明对于不同功能的各种可能事件的出现顺序。</li></ul><p>数据在计算机内部采用并行运输的方式:多个比特同时传输。</p><p>数据在通信线路(传输媒体)上使用串行运输的方式:即逐个比特按照时间顺序传输。</p><h2 id="2-2-数据通信的基础知识"><a href="#2-2-数据通信的基础知识" class="headerlink" title="2.2 数据通信的基础知识"></a>2.2 数据通信的基础知识</h2><h3 id="2-2-1-数据通信系统的模型"><a href="#2-2-1-数据通信系统的模型" class="headerlink" title="2.2.1 数据通信系统的模型"></a>2.2.1 数据通信系统的模型</h3><p><img src="/2022/12/30/%E8%AE%A1%E7%BD%91/%E6%95%B0%E6%8D%AE%E9%80%9A%E4%BF%A1%E7%B3%BB%E7%BB%9F%E6%A8%A1%E5%9E%8B.jpg" alt="数据通信系统模型"></p><p>一个数据通信系统可划分为三大部分,即<strong>源系统</strong>(或发送端、发送方)、<strong>传输系统</strong>(或传输网络)和<strong>目的系统</strong>(或接收端、接收方)。又称为:信源、通信媒体、信宿。</p><p>在上图中:调制认为是把<strong>数字信号转换为模拟信号</strong>的过程,而解调是把<strong>模拟信号转换为数字信号</strong>的过程。</p><p>老师课堂解释的调制:把信号转化为<strong>适合在信道上传输的信号</strong>。</p><p>一些常用术语及解释:</p><table><thead><tr><th>术语</th><th>解释</th></tr></thead><tbody><tr><td>数据</td><td>运送消息的实体</td></tr><tr><td>信号</td><td>数据的电气的或电磁的表现</td></tr><tr><td>模拟信号</td><td>代表消息的参数的取值是连续的</td></tr><tr><td>数字信号</td><td>代表消息的参数的取值是离散的</td></tr><tr><td>码元</td><td>在使用时间域(或简称为时域)的波形表示数字信号时,代表不同离散数值的基本波形。</td></tr></tbody></table><img src="/2022/12/30/%E8%AE%A1%E7%BD%91/模拟信号.jpg" alt="模拟信号" style="zoom:67%;"><img src="/2022/12/30/%E8%AE%A1%E7%BD%91/数字信号.jpg" alt="数字信号" style="zoom:67%;"><img src="/2022/12/30/%E8%AE%A1%E7%BD%91/码元.jpg" alt="码元" style="zoom:67%;"><h3 id="2-2-2-有关信道的几个基本概念"><a href="#2-2-2-有关信道的几个基本概念" class="headerlink" title="2.2.2 有关信道的几个基本概念"></a>2.2.2 有关信道的几个基本概念</h3><table><thead><tr><th>概念</th><th>解释</th></tr></thead><tbody><tr><td>信道</td><td>一般用来表示向某一个方向传递信息的媒体</td></tr><tr><td>单向信道(单工信道)</td><td>只能有一个方向的通信而没有反方向的交互</td></tr><tr><td>双向交替通信(半双工通信)</td><td>通信的双方都可以发送信息,但不能双方同时发送(当然也就不能同时接收)</td></tr><tr><td>双向同时通信(全双工通信)</td><td>通信的双方可以同时发送和接收信息</td></tr></tbody></table><img src="/2022/12/30/%E8%AE%A1%E7%BD%91/三种通信方式.jpg" alt="三种通信方式" style="zoom:67%;"><p><strong>基带信号</strong>(即基本频带信号)——来自信源的信号。计算机输出的代表各种文字或图像文件的<strong>数据信号</strong>(数字信号)都属于基带信号。</p><p>调制分为两大类:</p><ul><li>基带调制:仅对基带信号的波形进行变换,使它能够与信道特征相适应。<strong>变换后的信号仍然是基带信号</strong>。把这种过程称为<strong>编码</strong>。把数字信号转换为另一种形式的数字信号。</li><li>带通调制:使用<strong>载波</strong>进行调制,把基带信号的频率范围搬移到较高的频段,并<strong>转换为模拟信号</strong>,这样就能够更好地在模拟信道中传输。</li></ul><p><strong>带通信号</strong>:经过载波调制后的信号。</p><p>常用的编码方式:</p><table><thead><tr><th>编码方法</th><th>表示方法</th></tr></thead><tbody><tr><td>不归零制</td><td>正电平代表1,负电平代表0</td></tr><tr><td>归零制</td><td>正脉冲代表1,负脉冲代表0</td></tr><tr><td>⭐曼彻斯特编码</td><td>高—>低代表1,低—>高代表0。但也可以反过来定义</td></tr><tr><td>差分曼彻斯特编码</td><td>前一位后半和后一位前半不同表示0。前一位后半和后一位前半相同表示1</td></tr></tbody></table><img src="/2022/12/30/%E8%AE%A1%E7%BD%91/常用编码方式.jpg" alt="常用编码方式" style="zoom:67%;"><p>基本的带通调制方法:</p><table><thead><tr><th>方法</th><th>含义</th></tr></thead><tbody><tr><td>调幅(AM)/幅移键控(ASK)</td><td>载波的振幅随基带数字信号而变化</td></tr><tr><td>调频(FM)/频移键控(FSK)</td><td>载波的频率随基带数字信号而变化</td></tr><tr><td>调相(PM)/相移键控(PSK)</td><td>载波的初始相位随基带数字信号而变化</td></tr></tbody></table><img src="/2022/12/30/%E8%AE%A1%E7%BD%91/基本带通调制方法.jpg" alt="基本带通调制方法" style="zoom:67%;"><h3 id="2-2-3-信道的极限容量"><a href="#2-2-3-信道的极限容量" class="headerlink" title="2.2.3 信道的极限容量"></a>2.2.3 信道的极限容量</h3><p>任何实际的信道都不是理想的,在传输信号时会产生各种<strong>失真</strong>以及带来多种干扰。</p><p>从概念上讲,限制码元在信道上的传输速率的因素有以下两个:</p><ul><li>信道能够通过的频率范围—>带宽</li><li>信噪比</li></ul><h4 id="(1)信道能够通过的频率范围"><a href="#(1)信道能够通过的频率范围" class="headerlink" title="(1)信道能够通过的频率范围"></a>(1)信道能够通过的频率范围</h4><img src="/2022/12/30/%E8%AE%A1%E7%BD%91/信道频率范围.jpg" alt="信道频率范围" style="zoom:67%;"><p>所谓<strong>带宽就是指能通过的频率范围</strong>。单位是Hz</p><h5 id="奈氏准则:"><a href="#奈氏准则:" class="headerlink" title="奈氏准则:"></a>奈氏准则:</h5><ul><li><strong>假定的理想条件下</strong>,为了避免码间串扰,<strong>码元的传输速率的上限值</strong>。</li><li>理想低通信道的最高码元传输速率 = <strong>2W</strong> (码元/秒=波特)</li><li><strong>W 是理想低通信道的带宽</strong>,单位为赫(Hz)。信道的<strong>频带越宽</strong>,也就是能够通过的信号高频分量越多,就可以用更高的速率传送码元而不出现码元串扰。</li></ul><h5 id="比特率和波特率的关系:"><a href="#比特率和波特率的关系:" class="headerlink" title="比特率和波特率的关系:"></a>比特率和波特率的关系:</h5><ul><li>比特率是指单位时间内所传输的二进制代码的有效位数,单位是比特/秒(bps)。</li><li>波特率是指每秒传送的波形的个数,单位是波特(baud)。</li><li>比特率和波特率之间的换算关系如下:<code>比特率 = 波特率 *log2N</code>,其中N为码元所表示的有效状态数。</li></ul><h4 id="(2)信噪比"><a href="#(2)信噪比" class="headerlink" title="(2)信噪比"></a>(2)信噪比</h4><ul><li><p>信噪比就是信号的平均功率和噪声的平均功率之比。常记为S/N,并用分贝(dB)作为度量单位。即:<code>S/N(dB) = 10log10(S/N)(dB)</code>,这里要注意,<strong>信噪比的意思是S/N</strong>,但是用分贝作度量单位的时候要把信噪比用公式<code>10log10(S/N)(dB)</code>得到。</p></li><li><p>带宽受限且有高斯白噪声干扰的信道的<strong>极限、无差错的信息传输速率C</strong>(香农公式)可表达为:<code>C = W log2(1+S/N) (bit/s)</code>。其中W 为信道的带宽(以 Hz 为单位);S 为信道内所传信号的平均功率;N 为信道内部的高斯噪声功率。</p></li></ul><p>香农公式表明:</p><ul><li>信道的带宽或信道中的信噪比越大,则信息的极限传输速率就越高。</li><li><strong>只要信息传输速率低于信道的极限信息传输速率,就一定可以找到某种办法来实现无差错的传输。</strong></li><li>若信道带宽 W 或信噪比 S/N 没有上限(当然实际信道<br>不可能是这样的),则信道的极限信息传输速率 C 也<br>就没有上限。</li><li>实际信道上能够达到的信息传输速率要比香农的极限传输速率低不少。</li></ul><h2 id="2-3-物理层下面的传输媒体"><a href="#2-3-物理层下面的传输媒体" class="headerlink" title="2.3 物理层下面的传输媒体"></a>2.3 物理层下面的传输媒体</h2><p>传输媒体也称为传输介质或传输媒介,<strong>它并不属于物理层</strong></p><p>传输媒体分为两大类,即导引型传输媒体和非导引型传输媒体。</p><ul><li>导引型传输媒体中,<strong>电磁波被导引沿着固体媒体</strong>(铜线或光纤)传播。</li><li><strong>非导引型传输媒体就是指自由空间</strong>。在非导引型传输媒体中,电磁波的传输称为无线传输。</li></ul><h3 id="2-3-1-导引型传输媒体"><a href="#2-3-1-导引型传输媒体" class="headerlink" title="2.3.1 导引型传输媒体"></a>2.3.1 导引型传输媒体</h3><h4 id="双绞线"><a href="#双绞线" class="headerlink" title="双绞线"></a>双绞线</h4><ul><li>最常用的传输媒体</li><li>模拟传输和数字传输都可以使用双绞线,通信距离一般为几到十几公里。</li><li><strong>屏蔽双绞线STP</strong>,带金属屏蔽层。</li><li>无屏蔽双绞线UTP</li><li>导线相互缠绕可以降低电磁干扰</li></ul><h4 id="同轴电缆"><a href="#同轴电缆" class="headerlink" title="同轴电缆"></a>同轴电缆</h4><ul><li>同轴电缆具有很好的抗干扰特性,被广泛用于传输较高速率的数据。</li><li>同轴电缆的带宽取决于电缆的质量。</li></ul><h4 id="光纤"><a href="#光纤" class="headerlink" title="光纤"></a>光纤</h4><ul><li><p>多模光纤:纤芯直径较大,可同时传输多条光线;使用发光二极管作为光源。</p></li><li><p>单模光纤:纤芯直径较小,一次仅能传输一条光线;使用激光作为光源。</p></li></ul><p>与单模光纤相比,多模光纤传输速率低,距离短(2km),整体的传输性能差,成本较低;一般用于建筑物内或地理位置相邻的环境,主要用于局域网。单模光纤通常用作楼间的连接或广域网连接。</p><p>光纤优点:</p><p>(1) 通信容量非常大。</p><p>(2) 传输损耗小,中继距离长。</p><p>(2) 抗雷电和电磁干扰性能好。</p><p>(3) 无串音干扰,保密性好。</p><p>(4) 体积小,重量轻。</p><h3 id="2-3-2-非导引型传输媒体"><a href="#2-3-2-非导引型传输媒体" class="headerlink" title="2.3.2 非导引型传输媒体"></a>2.3.2 非导引型传输媒体</h3><ul><li>将自由空间称为“非导引型传输媒体”</li></ul><h4 id="短波通信"><a href="#短波通信" class="headerlink" title="短波通信"></a>短波通信</h4><p>短波通信(即高频通信)主要是靠电离层的反射,但电离层的不稳定所产生的衰落现象和电离层反射所产生的多径效应,使短波信道的通信质量较差,传输速率低。</p><h4 id="微波通信"><a href="#微波通信" class="headerlink" title="微波通信"></a>微波通信</h4><p>微波在空间主要是直线传播。</p><p>传统微波通信有两种方式:</p><ul><li><p>地面微波中继通信</p></li><li><p>卫星通信</p></li></ul><h2 id="⭐2-4-信道复用技术"><a href="#⭐2-4-信道复用技术" class="headerlink" title="⭐2.4 信道复用技术"></a>⭐2.4 信道复用技术</h2><p>包括频分复用、时分复用、统计时分复用、波分复用、码分复用</p><p>复用允许用户使用一个<strong>共享</strong>信道进行通信,<strong>降低成本,提高利用率</strong>。</p><p><strong>多个码片代表一个比特。</strong></p><img src="/2022/12/30/%E8%AE%A1%E7%BD%91/复用示意图.jpg" alt="复用示意图" style="zoom:67%;"><h3 id="频分复用FDM"><a href="#频分复用FDM" class="headerlink" title="频分复用FDM"></a>频分复用FDM</h3><ul><li>将整个带宽分为多份,用户在分配到一定的频带后,在通信过程中自始至终都占用这个频带。</li><li>频分复用的所有用户在<strong>同样的时间占用不同的带宽资源</strong>(请注意,这里的“带宽”是频率带宽而不是数据的发送速率)。</li></ul><img src="/2022/12/30/%E8%AE%A1%E7%BD%91/频分复用.jpg" alt="频分复用" style="zoom:67%;"><h3 id="时分复用TDM"><a href="#时分复用TDM" class="headerlink" title="时分复用TDM"></a>时分复用TDM</h3><ul><li>时分复用则是将时间划分为一段段等长的时分复用帧(TDM 帧)。每一个时分复用的用户在每一个 TDM<br>帧中占用固定序号的时隙。</li><li>每一个用户所占用的时隙是周期性地出现(其周期就<br>是 TDM 帧的长度)。</li><li>TDM 信号也称为等时(isochronous)信号。</li><li>时分复用的所有用户是<strong>在不同的时间占用同样的频带宽度</strong>。</li></ul><img src="/2022/12/30/%E8%AE%A1%E7%BD%91/时分复用.jpg" alt="时分复用" style="zoom:67%;"><p>使用时分复用系统传送计算机数据时,由于<strong>计算机数据的突发性质</strong>,用户对分配到的子信道的利用率一般是不高的。</p><img src="/2022/12/30/%E8%AE%A1%E7%BD%91/时分复用的缺点.jpg" alt="时分复用的缺点" style="zoom:67%;"><h3 id="统计时分复用STDM"><a href="#统计时分复用STDM" class="headerlink" title="统计时分复用STDM"></a>统计时分复用STDM</h3><p>STDM 帧不是固定分配时隙,而是<strong>按需动态地分配时隙</strong>。因此统计时分复用<strong>可以提高线路的利用率</strong>。</p><img src="/2022/12/30/%E8%AE%A1%E7%BD%91/统计时分复用.jpg" alt="统计时分复用" style="zoom:67%;"><ul><li>在每个时隙中<strong>需要有用户的地址信息</strong>。</li><li>集中器<strong>能正常工作的前提是假定各用户都是间歇地工作</strong>。</li></ul><h3 id="波分复用WDM"><a href="#波分复用WDM" class="headerlink" title="波分复用WDM"></a>波分复用WDM</h3><img src="/2022/12/30/%E8%AE%A1%E7%BD%91/波分复用.jpg" alt="波分复用" style="zoom: 67%;"><h3 id="码分复用CDMA"><a href="#码分复用CDMA" class="headerlink" title="码分复用CDMA"></a>码分复用CDMA</h3><ul><li>码分复用是一种<strong>共享信道</strong>的方法。当码分复用信道为多个不同地址的用户所共享时,就称为<strong>码分多址</strong>CDMA。</li><li>各用户使用经过特殊挑选的<strong>不同码型</strong>,因此彼此不会造成干扰。</li><li>这种系统发送的信号<strong>有很强的抗干扰能力</strong>,其频谱类似于白噪声,不易被敌人发现。</li></ul><h4 id="码片序列"><a href="#码片序列" class="headerlink" title="码片序列"></a>码片序列</h4><p>每一个比特时间划分为 m 个短的间隔,称为<strong>码片</strong>。每个站被指派一个唯一的 m bit 码片序列。</p><p>将<strong>码片中的0记为-1</strong>,将<strong>码片中的1记为+1</strong></p><img src="/2022/12/30/%E8%AE%A1%E7%BD%91/码片序列举例.jpg" alt="码片序列举例" style="zoom:67%;"><h4 id="CDMA的重要特点"><a href="#CDMA的重要特点" class="headerlink" title="CDMA的重要特点"></a>CDMA的重要特点</h4><p>每个站分配的码片序列不仅<strong>必须各不相同</strong>,并且还必须<strong>互相正交</strong> (orthogonal)。</p><p>两个不同站的码片序列正交,就是向量 S 和T<br>的规格化内积 (inner product) 等于 0,<strong>注意要求平均</strong>。</p><img src="/2022/12/30/%E8%AE%A1%E7%BD%91/规格化内积.jpg" alt="规格化内积" style="zoom:67%;"><p>任何一个码片向量和该码片向量自己的规格化内积都是 1</p><img src="/2022/12/30/%E8%AE%A1%E7%BD%91/与自己的规格化内积.jpg" alt="与自己的规格化内积" style="zoom:67%;"><p>一个码片向量和该码片反码的向量的规格化内积值是 –1</p><p>当<strong>S站发送比特1时,在X站计算内积的结果是+1</strong>;当<strong>S站发送比特0时,内积的结果是-1</strong>。</p><p>所有站收到的都是叠加的信号。</p><img src="/2022/12/30/%E8%AE%A1%E7%BD%91/CDMA原理.jpg" alt="CDMA原理" style="zoom: 67%;"><p>当接收S站发送的信号时,就用S站的码片序列与收到的信号求规格化内积。S·Sx就是S站发送的数据比特,因为在计算规格化内积时,或者都是+1,或者都是-1</p><h2 id="2-5-数字传输系统"><a href="#2-5-数字传输系统" class="headerlink" title="2.5 数字传输系统"></a>2.5 数字传输系统</h2><p>与模拟通信相比,数字通信无论是在传输质量上还是经济上都有明显的优势。</p><p>目前,长途干线大都采用<strong>时分复用的脉码调制PCM</strong> 的<strong>数字传输</strong>方式。</p><p>脉码调制一般包括三个过程:<strong>采样、量化和编码</strong>。</p><img src="/2022/12/30/%E8%AE%A1%E7%BD%91/脉码调制过程.jpg" alt="脉码调制过程" style="zoom: 50%;"><p>旧的数字传输系统存在许多缺点:</p><ul><li><strong>速率标准不统一</strong></li><li><strong>不是同步传输</strong></li></ul><p><strong>同步光纤网 SONET</strong></p><ul><li>对电信信号称为第 1 级同步传送信号 STS-1</li><li>对光信号则称为第 1 级光载波 OC-1</li></ul><p><strong>同步数字系列 SDH</strong></p><p>一般可认为 SDH 与 SONET 是同义词。</p><p>其主要不同点是:SDH 的第 1 级同步传递模块,即 STM-1,相当于 SONET 体系中的 OC-3 速率。</p><img src="/2022/12/30/%E8%AE%A1%E7%BD%91/OCSTS级和STM级的对应关系.jpg" alt="OCSTS级和STM级的对应关系" style="zoom:50%;"><p>SONET / SDH 标准的意义</p><p>第一次真正<strong>实现了数字传输体制上的世界性标准</strong>。</p><h2 id="2-6-宽带接入技术"><a href="#2-6-宽带接入技术" class="headerlink" title="2.6 宽带接入技术"></a>2.6 宽带接入技术</h2><p>从宽带接入的媒体来看,可以划分为两大类:</p><ul><li><strong>有线宽带接入</strong></li><li>无线宽带接入</li></ul><h3 id="非对称数字用户线ADSL"><a href="#非对称数字用户线ADSL" class="headerlink" title="非对称数字用户线ADSL"></a>非对称数字用户线ADSL</h3><ul><li><p>用数字技术对现有的<strong>模拟电话用户线</strong>进行改造,使它能够承载宽带业务。</p></li><li><p>上行和下行带宽做成不对称的 ,其<strong>下行速率远大于上行速率</strong>。</p></li><li><p>采用非对称传输的原因:在网络应用中,用户的下载数据量要远大于上传的数据量。</p></li><li><p>ADSL 不能保证固定的数据率。对于质量很差的用户线甚至无法开通 ADSL</p></li></ul><h3 id="DMT技术"><a href="#DMT技术" class="headerlink" title="DMT技术"></a>DMT技术</h3><p>DMT 调制技术采用频分复用的方法,把 40 kHz<br>以上一直到 1.1 MHz 的高端频谱划分为许多的子信道,其中 25 个子信道用于上行信道,而<br>249 个子信道用于下行信道。</p><h3 id="第二代ADSL"><a href="#第二代ADSL" class="headerlink" title="第二代ADSL"></a>第二代ADSL</h3><ul><li>通过提高调制效率得到了更高的数据率</li><li>采用了无缝速率自适应技术 SRA (Seamless Rate Adaptation),<br>可在运营中不中断通信和不产生误码的情况下,自适应地调整数据率。</li></ul><h3 id="光纤同轴混合网(HFC网)"><a href="#光纤同轴混合网(HFC网)" class="headerlink" title="光纤同轴混合网(HFC网)"></a>光纤同轴混合网(HFC网)</h3><ul><li>FC (Hybrid Fiber Coax) 网是在目前覆盖面很广的有线电视网 CATV 的基础上开发的一种居民宽带接入网。</li><li>HFC 网具有双向传输功能,扩展了传输频带。</li></ul><h1 id="第三章-数据链路层"><a href="#第三章-数据链路层" class="headerlink" title="第三章 数据链路层"></a>第三章 数据链路层</h1><p><strong>数据链路</strong>(data link) 除了<strong>物理线路</strong>外,还必须有通信协议来控制这些数据的传输。若把<strong>实现这些协议的硬件和软件</strong>加到链路上,就构成了数据链路。</p><h2 id="3-1-数据链路层的主要功能"><a href="#3-1-数据链路层的主要功能" class="headerlink" title="3.1 数据链路层的主要功能"></a>3.1 数据链路层的主要功能</h2><ul><li>链路管理</li><li><strong>帧定界</strong></li><li>流量控制</li><li><strong>差错控制</strong></li><li>将数据和控制信息区分开</li><li><strong>透明传输</strong></li><li>寻址</li></ul><h3 id="3-1-2-三个基本问题"><a href="#3-1-2-三个基本问题" class="headerlink" title="3.1.2 三个基本问题"></a>3.1.2 三个基本问题</h3><p>数据链路层协议有许多种,但有三个基本问题则是共同的。这三个基本问题是:</p><ul><li><strong>封装成帧</strong></li><li><strong>透明传输</strong></li><li><strong>差错控制</strong></li></ul><h4 id="差错检测"><a href="#差错检测" class="headerlink" title="差错检测"></a>差错检测</h4><ul><li>帧尾<strong>加设冗余码</strong>,这<strong>不是多余的</strong>。</li><li>用二进制的模 2 运算进行 2的n次方乘 M 的运算,这<strong>相当于在 M 后面添加 n 个 0。直接加n个0就好</strong></li></ul><p>在数据后面添加的冗余码被称为<strong>帧检验序列</strong></p><p>用多项式表示除数P:即把X上的系数拿出来作为进制。</p><img src="/2022/12/30/%E8%AE%A1%E7%BD%91/多项式表示除数P.jpg" alt="多项式表示除数P" style="zoom:67%;"><p>进行CRC检测<strong>只能判断有没有差错,而不能判断是哪个比特出现了差错</strong>,它只能做到无差错接受,而<strong>不能保证可靠传输</strong>:即发送什么输出什么,因为可靠传输是保证帧不重复、丢包等,这需要传输层来实现可靠传输。</p><h4 id="数据链路层使用的信道"><a href="#数据链路层使用的信道" class="headerlink" title="数据链路层使用的信道"></a>数据链路层使用的信道</h4><ul><li><strong>点对点信道</strong>使用的协议相对简单,这种信道使用一对一的通信方式。</li><li><strong>广播信道</strong>,在一个广播里面,发送一条信息所有主机都能收到,由于共同主机使用这个信道,如果多台主机同时发送数据会产生冲突,所以过程复杂。</li></ul><h2 id="3-2-点对点协议PPP"><a href="#3-2-点对点协议PPP" class="headerlink" title="3.2 点对点协议PPP"></a>3.2 点对点协议PPP</h2><p>点对点协议PPP(Point-to-Point<br>Protocol)。</p><p><img src="/2022/12/30/%E8%AE%A1%E7%BD%91/PPP%E5%8D%8F%E8%AE%AE.jpg" alt="PPP协议"></p><h3 id="3-2-1-PPP-协议的特点"><a href="#3-2-1-PPP-协议的特点" class="headerlink" title="3.2.1 PPP 协议的特点"></a>3.2.1 PPP 协议的特点</h3><h4 id="1-PPP协议应满足的需求"><a href="#1-PPP协议应满足的需求" class="headerlink" title="1. PPP协议应满足的需求"></a>1. PPP协议应满足的需求</h4><ul><li>简单 —— <strong>这是首要的要求</strong>。</li><li><strong>封装成帧</strong> —— 必须规定特殊的字符作为帧定界符。</li><li><strong>透明性</strong> —— 必须保证数据传输的透明性。</li><li><strong>多种网络层协议</strong> —— 能够在同一条物理链路上同时支持多种网络层协议,可以支持TCP/IP,也可以OSI。</li><li>多种类型链路 —— 能够在多种类型的链路上运行。</li><li><strong>差错检测</strong> —— 能够对接收端收到的帧进行检测,并立即丢弃有差错的帧。</li></ul><h4 id="2-PPP协议不需要的功能"><a href="#2-PPP协议不需要的功能" class="headerlink" title="2. PPP协议不需要的功能"></a>2. PPP协议不需要的功能</h4><ul><li><strong>纠错:不提供使用序号和确认的可靠传输</strong></li></ul><p>PPP 协议之所以不使用序号和确认机制是出于以下的考虑:在数据链路层出现差错的概率不大时,使用比较简单的 PPP 协议较为合理;在因特网环境下,PPP 的信息字段放入的数据是 IP<br>数据报。<strong>数据链路层的可靠传输并不能够保证网络层的传输也是可靠的</strong>;帧检验序列 FCS 字段可保证无差错接受。</p><ul><li>流量控制</li><li>序号</li><li>多点线路</li><li>半双工或单工链路</li></ul><h4 id="3-PPP协议的组成(简单了解)"><a href="#3-PPP协议的组成(简单了解)" class="headerlink" title="3. PPP协议的组成(简单了解)"></a>3. PPP协议的组成(简单了解)</h4><p><strong>PPP的两个不同子层:</strong></p><ul><li>NCP (网络控制协议):一组协议,每一个协议支持不同的网络层协议</li><li>LCP (链路控制协议)</li></ul><p>PPP会话的建立:</p><p>1、链路建立<br>2、<strong>验证阶段(可选</strong>)<br>3、网络层协议连接</p><p>PPP协议的工作状态:</p><img src="/2022/12/30/%E8%AE%A1%E7%BD%91/PPP协议工作状态.jpg" alt="PPP协议工作状态" style="zoom:67%;"><h4 id="3-2-2-PPP协议的帧格式"><a href="#3-2-2-PPP协议的帧格式" class="headerlink" title="3.2.2 PPP协议的帧格式"></a>3.2.2 PPP协议的帧格式</h4><p><strong>解决透明传输问题的两种方案</strong>:</p><ul><li>字符填充:</li></ul><p>定义一种特殊的转义字符,出现7D的定界符转成特殊字符。</p><ul><li>零比特填充:</li></ul><p>PPP 协议用在 SONET/SDH 链路时,使用同步传输(一连串的比特连续传送)。这时 PPP 协议采用<strong>零比特填充</strong>方法来实现透明传输。</p><h2 id="3-3-使用广播信道的数据链路层"><a href="#3-3-使用广播信道的数据链路层" class="headerlink" title="3.3 使用广播信道的数据链路层"></a>3.3 使用广播信道的数据链路层</h2><h3 id="3-3-1-局域网的数据链路层"><a href="#3-3-1-局域网的数据链路层" class="headerlink" title="3.3.1 局域网的数据链路层"></a>3.3.1 局域网的数据链路层</h3><p>局域网的最主要<strong>特点</strong>:</p><p>范围比较小,数目有限;网络为一个单位所拥有;</p><p>局域网具有如下主要<strong>优点</strong>:</p><ul><li>具有广播功能,从一个站点可很方便地访问全网。局域网上的主机可共享连接在局域网上的各种硬件和软件资源。</li></ul><p>局域网的拓扑结构:</p><p><img src="/2022/12/30/%E8%AE%A1%E7%BD%91/%E5%B1%80%E5%9F%9F%E7%BD%91%E6%8B%93%E6%89%91%E7%BB%93%E6%9E%84.jpg" alt="局域网拓扑结构"></p><p>把局域网的数据链路层拆成两个子层:</p><ul><li><p>逻辑链路控制LLC子层:<strong>与传输媒体无关</strong>的内容放在这里,一般不考虑LLC子层</p></li><li><p>媒体接入控制MAC子层:<strong>与传输媒体有关</strong>的内容放在这里</p></li></ul><h4 id="适配器的作用"><a href="#适配器的作用" class="headerlink" title="适配器的作用"></a>适配器的作用</h4><p>网络接口板又称为通信适配器 (adapter) 或网络接口卡 NIC (Network Interface Card),或“网卡”。</p><p>适配器重要功能:</p><ul><li><strong>进行串行/并行转换</strong>。</li><li><strong>对数据进行缓存</strong>。</li><li><strong>在计算机的操作系统安装设备驱动程序</strong>。</li><li><strong>实现以太网协议</strong>。(以太网网卡)</li></ul><p>以太网采用两种重要措施:</p><ul><li>采用较为灵活的无连接的工作方式:不必先建立连接就可以直接发送数据;对发送的数据帧不进行编号,也不要求对方发回确认。 这提供的是不可靠的交付,差错的纠正由高层来决定</li><li>以太网的数据使用<strong>曼彻斯特编码</strong></li></ul><h3 id="媒体共享技术"><a href="#媒体共享技术" class="headerlink" title="媒体共享技术"></a>媒体共享技术</h3><p>静态划分信道:</p><ul><li>频分复用;</li><li>时分复用;</li><li>波分复用;</li><li>码分复用。</li></ul><p>信号提前划分给用户</p><p>静态分配的缺陷:</p><ul><li>资源分配不合理,不满足用户对资源占用的不同需求;</li><li>有资源浪费,效率低;</li><li>信道N等分,延迟时间增大N倍</li></ul><p> 静态分配的应用:</p><ul><li>适于用户数量少且用户数目固定的情况<br>;</li><li>适于通信量大且流量稳定的情况;</li><li>不适用于突发性业务的情况</li></ul><p>使用动态媒体接入控制(多点接入):</p><ul><li><p><strong>随机接入</strong>:站点争用信道,可能出现站点之间的冲突。谁争赢了给谁用,效率高</p></li><li><p>受控接入:站点被分配占用信道,<strong>无冲突</strong>。</p></li></ul><p><strong>不能够两个以上用户同时发送数据</strong>,信道是开放的,没有预分配,大大提高信道的利用率。</p><h3 id="3-3-2-CSMA-x2F-CD-协议"><a href="#3-3-2-CSMA-x2F-CD-协议" class="headerlink" title="3.3.2 CSMA/CD 协议"></a>3.3.2 CSMA/CD 协议</h3><p><strong>CSMA/CD 含义</strong>:<strong>载波监听多点接入 / 碰撞检测</strong></p><p>“多点接入”表示许多计算机以多点接入的方式连接在一根总线上。</p><p>“载波监听”是指每一个站在发送数据之前先要检测<br>一下总线上是否有其他计算机在发送数据,如果有,<br>则暂时不要发送数据,以免发生碰撞。</p><p>“碰撞检测”就是计算机边发送数据边检测信道上的信号电压大小。<strong>所谓“碰撞”就是发生了冲突。因此“碰撞检测”也</strong><br><strong>称为“冲突检测”。</strong>每一个正在发送数据的站,一旦发现总线上出现了碰撞,就要<strong>立即停止发送</strong>,免得继续浪费<br>网络资源,然后<strong>等待一段随机时间</strong>后再次发送</p><p>为什么进行碰撞检测?</p><p>由于电磁波在总线上的传播速率是有限的,当某个站监听到总线是空闲时,也可能总线并非真正是空闲的。</p><p><img src="/2022/12/30/%E8%AE%A1%E7%BD%91/%E4%BA%89%E7%94%A8%E6%9C%9F.jpg" alt="争用期"></p>]]></content>
</entry>
</search>