-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathsearch.xml
1994 lines (1758 loc) · 692 KB
/
search.xml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
<?xml version="1.0" encoding="utf-8"?>
<search>
<entry>
<title>Ubuntu编译OpenCV4.4.0+opencv_contrib(带CUDA)</title>
<url>/2021/06/03/Ubuntu%E7%BC%96%E8%AF%91OpenCV4-4-0-opencv-contrib-%E5%B8%A6CUDA/</url>
<content><![CDATA[<p>本文方法是用在Jetson Nano开发板上的,开发板提供的官方系统镜像中,安装的是OpenCV4.1.1,在调用TensorFlow2的模型时有问题,于是手动编译OpenCV4.4.0进行测试,发现可行。下面介绍OpenCV4.4.0的编译安装过程。</p>
<a id="more"></a>
<h3 id="1、环境准备"><a href="#1、环境准备" class="headerlink" title="1、环境准备"></a>1、环境准备</h3><ol>
<li>按照开发板入门教程把CUDA和cudnn配置好</li>
<li>确保安装了CMake</li>
<li>下载<a href="https://github.com/opencv/opencv/tree/4.4.0" target="_blank" rel="noopener">OpenCV4.4.0</a></li>
<li>下载对应版本的<a href="https://github.com/opencv/opencv_contrib/tree/4.4.0" target="_blank" rel="noopener">opencv_contrib</a></li>
</ol>
<h3 id="2、卸载旧版本OpenCV"><a href="#2、卸载旧版本OpenCV" class="headerlink" title="2、卸载旧版本OpenCV"></a>2、卸载旧版本OpenCV</h3><figure class="highlight shell"><table><tr><td class="code"><pre><span class="line">sudo apt-get purge libopencv*</span><br><span class="line">sudo apt autoremove</span><br><span class="line">sudo apt-get update</span><br></pre></td></tr></table></figure>
<h3 id="3、安装依赖项"><a href="#3、安装依赖项" class="headerlink" title="3、安装依赖项"></a>3、安装依赖项</h3><figure class="highlight shell"><table><tr><td class="code"><pre><span class="line">sudo apt-get install build-essential</span><br><span class="line">sudo apt-get install libglew-dev libtiff5-dev zlib1g-dev libjpeg-dev \ </span><br><span class="line"> libavcodec-dev libavformat-dev libavutil-dev libpostproc-dev \</span><br><span class="line"> libswscale-dev libeigen3-dev libtbb-dev libgtk2.0-dev pkg-config libpng-dev </span><br><span class="line">sudo apt-get install libatlas-base-dev gfortran</span><br><span class="line">sudo apt update</span><br><span class="line">sudo apt upgrade</span><br></pre></td></tr></table></figure>
<h3 id="4、编译安装"><a href="#4、编译安装" class="headerlink" title="4、编译安装"></a>4、编译安装</h3><ol>
<li><p>新建一个目录opencv4,把OpenCV4.4.0的源码解压到里面,然后再在opencv4中新建一个opencv_contrib目录,将opencv_contrib4.4.0的源码解压到里面</p>
</li>
<li><p>cd到yourpath/opencv4,运行以下命令:</p>
<figure class="highlight shell"><table><tr><td class="code"><pre><span class="line">mkdir build</span><br><span class="line">cd build</span><br><span class="line">cmake \</span><br><span class="line">-DCMAKE_BUILD_TYPE=Release \</span><br><span class="line">-DBUILD_PNG=OFF \</span><br><span class="line">-DBUILD_TIFF=OFF \</span><br><span class="line">-DBUILD_TBB=OFF \</span><br><span class="line">-DBUILD_JPEG=OFF \</span><br><span class="line">-DBUILD_JASPER=OFF \</span><br><span class="line">-DBUILD_ZLIB=OFF \</span><br><span class="line">-DBUILD_EXAMPLES=OFF \</span><br><span class="line">-DBUILD_opencv_java=OFF \</span><br><span class="line">-DBUILD_opencv_python2=OFF \</span><br><span class="line">-DBUILD_opencv_python3=ON \</span><br><span class="line">-DENABLE_PRECOMPILED_HEADERS=OFF \</span><br><span class="line">-DOPENCV_GENERATE_PKGCONFIG=ON \</span><br><span class="line">-DWITH_OPENCL=OFF \</span><br><span class="line">-DWITH_OPENMP=OFF \</span><br><span class="line">-DWITH_FFMPEG=ON \</span><br><span class="line">-DWITH_GSTREAMER=OFF \</span><br><span class="line">-DWITH_GSTREAMER_0_10=OFF \</span><br><span class="line">-DWITH_CUDA=ON \</span><br><span class="line">-DOPENCV_DNN_CUDA=ON \</span><br><span class="line">-DWITH_GTK=ON \</span><br><span class="line">-DWITH_VTK=OFF \</span><br><span class="line">-DWITH_TBB=ON \</span><br><span class="line">-DWITH_1394=OFF \</span><br><span class="line">-DWITH_OPENEXR=OFF \</span><br><span class="line">-DCUDA_TOOLKIT_ROOT_DIR=/usr/local/cuda-10.2 \</span><br><span class="line">-DCUDA_ARCH_BIN=5.3 \</span><br><span class="line">-DCUDA_ARCH_PTX="" \</span><br><span class="line">-DINSTALL_C_EXAMPLES=OFF \</span><br><span class="line">-DOPENCV_ENABLE_NONFREE=ON \</span><br><span class="line">-DINSTALL_TESTS=OFF \</span><br><span class="line">-DOPENCV_EXTRA_MODULES_PATH=../opencv_contrib/modules \</span><br><span class="line">-DCMAKE_INSTALL_PREFIX=/usr/local ..</span><br><span class="line">sudo make -j4</span><br><span class="line">sudo make install</span><br></pre></td></tr></table></figure>
<p>cmake的配置选项“-DOPENCV_GENERATE_PKGCONFIG=ON”是为了编译时生成opencv4.pc文件,这个文件可以把opencv库的各种信息告诉pkg-config,“-DWITH_CUDA=ON”和“-DOPENCV_DNN_CUDA=ON”是为了编译CUDA相关代码,之后就可以用dnn模块调用GPU了,“-DCMAKE_INSTALL_PREFIX=/usr/local”指定了库的安装路径前缀,之后库文件就会被安装在/usr/local/lib中,头文件被放在/usr/local/include/opencv4/opencv2/中,这里的“/usr/local”其实也是默认值。</p>
<p>编译过程比较长,耐心等待即可。</p>
<h3 id="5、配置系统环境变量"><a href="#5、配置系统环境变量" class="headerlink" title="5、配置系统环境变量"></a>5、配置系统环境变量</h3><ol>
<li><p>当操作系统加载动态库时,会首先去读取/etc/ld.so.conf文件,然后进入/etc/ld.so.conf.d目录下,读取里面所有的配置文件,这些配置文件内记载着库的路径,这样就可以找到动态库,因此需要将OpenCV库目录添加到/etc/ld.so.conf.d/opencv.conf:</p>
<figure class="highlight shell"><table><tr><td class="code"><pre><span class="line">sudo vim /etc/ld.so.conf.d/opencv.conf</span><br></pre></td></tr></table></figure>
<p>用上面的命令创建\打开<code>opencv.conf</code>后,在文件末尾添加:<code>/usr/local/lib</code></p>
<p>然后使用下面的命令使配置生效:</p>
<figure class="highlight shell"><table><tr><td class="code"><pre><span class="line">sudo ldconfig</span><br></pre></td></tr></table></figure>
</li>
</ol>
</li>
</ol>
<ol start="2">
<li><p>pkg-config 能通过库提供的 .pc 文件获得库的各种必要信息,包括版本信息、编译和连接需要的参数等。通过 pkg-config 提供的参数–cflags, –libs,将所需信息提取出来供编译和连接使用。当pkg-config运行时,会在其配置目录下查找.pc文件,找到对应的文件就读取它,pkg-config的默认路径是: /usr/lib/pkgconfig/和/usr/share/pkgconfig/,这里我们为它添加一个新的路径<code>/usr/local/lib/pkgconfig</code>,首先在/usr/local/lib/目录中新建pkgconfig目录:</p>
<figure class="highlight shell"><table><tr><td class="code"><pre><span class="line">cd /usr/local/lib</span><br><span class="line">mkdir pkgconfig</span><br></pre></td></tr></table></figure>
<p>然后把这个目录加入PKG_CONFIG_PATH ,首先打开文件:</p>
<figure class="highlight shell"><table><tr><td class="code"><pre><span class="line">sudo vim /etc/bash.bashrc</span><br></pre></td></tr></table></figure>
<p>在文件末尾加入:</p>
<figure class="highlight plain"><table><tr><td class="code"><pre><span class="line">PKG_CONFIG_PATH=$PKG_CONFIG_PATH:/usr/local/lib/pkgconfig </span><br><span class="line">export PKG_CONFIG_PATH</span><br></pre></td></tr></table></figure>
<p>保存退出后,更新系统环境变量:</p>
<figure class="highlight shell"><table><tr><td class="code"><pre><span class="line">source /etc/bash.bashrc</span><br></pre></td></tr></table></figure>
</li>
</ol>
<h3 id="6、验证"><a href="#6、验证" class="headerlink" title="6、验证"></a>6、验证</h3><p>在终端输入:</p>
<figure class="highlight shell"><table><tr><td class="code"><pre><span class="line">opencv_version</span><br></pre></td></tr></table></figure>
<p>应当出现OpenCV版本号。</p>
<p>在终端输入:</p>
<figure class="highlight shell"><table><tr><td class="code"><pre><span class="line">pkg-config opencv4 --modversion</span><br></pre></td></tr></table></figure>
<p>也应当出现OpenCV版本号。</p>
]]></content>
<categories>
<category>教程</category>
</categories>
<tags>
<tag>OpenCV</tag>
<tag>opencv_contrib</tag>
<tag>Linux</tag>
<tag>CUDA</tag>
</tags>
</entry>
<entry>
<title>Win10编译OpenCV4.4.0+opencv_contrib(带CUDA)</title>
<url>/2021/06/03/Win10%E7%BC%96%E8%AF%91OpenCV4-4-0-opencv-contrib-%E5%B8%A6CUDA/</url>
<content><">CUDA和cuDNN</a></li>
<li>安装<a href="https://cmake.org/download/" target="_blank" rel="noopener">CMake</a>(推荐3.17以上的版本)</li>
<li>下载[OpenCV4.4.0](<a href="https://github.com/opencv/opencv/tree/4.4.0" target="_blank" rel="noopener">GitHub - opencv/opencv at 4.4.0</a>)</li>
<li>下载对应版本的<a href="https://github.com/opencv/opencv_contrib/tree/4.4.0" target="_blank" rel="noopener">opencv_contrib</a></li>
</ol>
<h3 id="2、设置文件目录"><a href="#2、设置文件目录" class="headerlink" title="2、设置文件目录"></a>2、设置文件目录</h3><p>为了便于描述,笔者将按照自己的文件目录设置介绍后续过程,在此说明笔者的目录设置。</p>
<ol>
<li>在一个熟悉的目录中,新建一个文件夹opencv4,把opencv4.4.0的源码解压到此文件夹;</li>
<li>然后再在opencv4文件夹中新建一个opencv_contrib文件夹,把opencv_contrib4.4.0的源码解压到里面;</li>
<li>最后在opencv4文件夹中新建一个build文件夹。</li>
</ol>
<h3 id="3、用CMake生成VS2017工程文件"><a href="#3、用CMake生成VS2017工程文件" class="headerlink" title="3、用CMake生成VS2017工程文件"></a>3、用CMake生成VS2017工程文件</h3><h4 id="1-设置源码和构建路径"><a href="#1-设置源码和构建路径" class="headerlink" title="1.设置源码和构建路径"></a>1.设置源码和构建路径</h4><p>打开CMake,“Where is the source code:”选择yourpath\opencv4,“Where to build the binaries:”选择yourpath\opencv4\build</p>
<h4 id="2-配置构建选项"><a href="#2-配置构建选项" class="headerlink" title="2.配置构建选项"></a>2.配置构建选项</h4><ol>
<li>点击“Configure”配置“Specify the generator for this project”为VS2017,“Optional platform for generator”为x64,然后点“Finish”结束</li>
<li>之后CMake会进行初次配置,然后显示出很多可配置选项,接下来修改这些选项</li>
<li>勾选“WITH_CUDA”、“OPENCV_DNN_CUDA”、“BUILD_opencv_world”</li>
<li>搜索含“test”的选项,都不勾选,搜索含“python”的选项,都不勾选</li>
<li>找到“OPENCV_EXTRA_MODULES_PATH”,设置为yourpath\opencv4\opencv_contrib\modules</li>
<li>再次点击“Configure”,过程中会下载一些文件,可能会下载失败,可以参考<a href="https://blog.csdn.net/length85/article/details/103767475" target="_blank" rel="noopener">文章1</a>和<a href="https://blog.csdn.net/yapifeitu/article/details/106323853" target="_blank" rel="noopener">文章2</a>解决。</li>
</ol>
<h4 id="3-生成并打开VS工程"><a href="#3-生成并打开VS工程" class="headerlink" title="3.生成并打开VS工程"></a>3.生成并打开VS工程</h4><p>上一步完全没有错误后,依次点击“Generate”和“Open Project”即可打开生成的VS工程。</p>
<h3 id="4、编译"><a href="#4、编译" class="headerlink" title="4、编译"></a>4、编译</h3><ol>
<li>用VS2017打开上一步生成的工程以后,分别在Debug x64和Release x64模式下,生成整个解决方案,根据电脑配置的不同,每次编译预计会花费2~3个小时</li>
<li>编译完成后,分别在Debug x64和Release x64模式下,仅生成INSTALL项目</li>
</ol>
<h3 id="5、移动编译好的OpenCV到合适的目录"><a href="#5、移动编译好的OpenCV到合适的目录" class="headerlink" title="5、移动编译好的OpenCV到合适的目录"></a>5、移动编译好的OpenCV到合适的目录</h3><p>上一步正常编译完成后,应该没有任何编译失败的项目,在yourpath\opencv4\build\install文件夹下就是发布版OpenCV的build文件夹中的内容,可以把它们移动到C盘。例如C:\Program Files\opencv4\build,下面以此路径为例继续介绍。</p>
<h3 id="6、配置环境变量"><a href="#6、配置环境变量" class="headerlink" title="6、配置环境变量"></a>6、配置环境变量</h3><ol>
<li>新建系统环境变量OpenCV_DIR,值为C:\Program Files\opencv4\build</li>
<li>把C:\Program Files\opencv4\build\x64\vc15\bin添加到系统环境变量Path中</li>
</ol>
<h3 id="7、在VS2017中配置OpenCV"><a href="#7、在VS2017中配置OpenCV" class="headerlink" title="7、在VS2017中配置OpenCV"></a>7、在VS2017中配置OpenCV</h3><p>由于是用VS2017编译的OpenCV,在C:\Program Files\opencv4\build\x64\目录下只有vc15,只能用于配置VS2017</p>
<ol>
<li>用VS2017打开任意一个工程,打开“视图”->“属性管理器”,双击打开其中的“工程名”->”Debug | x64”->”Microsoft.Cpp.x64.user”</li>
<li>选择“通用属性”->”VC++目录”,在“包含目录”中添加C:\Program Files\opencv4\build\include和C:\Program Files\opencv4\build\include\opencv2,在“库目录”中添加C:\Program Files\opencv4\build\x64\vc15\lib</li>
<li>选择“通用属性”->”链接器”->“输入”,在“附加依赖项”中添加<code>opencv_world440d.lib</code>,其中的“440”表示版本为4.4.0,后面的“d”表示Debug版</li>
<li>Release版本的配置也是类似,只是“附加依赖项”中添加的是不带“d”后缀的<code>opencv_world440.lib</code></li>
</ol>
<p>至此就完成了OpenCV4.4.0编译和配置。</p>
]]></content>
<categories>
<category>教程</category>
</categories>
<tags>
<tag>Win10</tag>
<tag>OpenCV</tag>
<tag>opencv_contrib</tag>
<tag>CUDA</tag>
</tags>
</entry>
<entry>
<title>TensorFlow2学习笔记</title>
<url>/2021/04/10/TensorFlow2%E5%AD%A6%E4%B9%A0%E7%AC%94%E8%AE%B0/</url>
<content><![CDATA[<p>本笔记是笔者在学习网易云课堂上日月光华老师的<a href="https://study.163.com/course/introduction/1004573006.htm" target="_blank" rel="noopener">Tensorflow深度学习入门与实战课程</a>时记录整理而来的,记录过程中难免出现错误,敬请指正。</p>
<a id="more"></a>
<h3 id="安装"><a href="#安装" class="headerlink" title="安装"></a>安装</h3><p>安装Anaconda,创建新环境tf2436,Python版本为3.6,然后在环境内安装tensorflow</p>
<figure class="highlight python"><table><tr><td class="code"><pre><span class="line"><span class="comment">#在base环境创建新环境</span></span><br><span class="line">conda create -n tf2436 python=<span class="number">3.6</span></span><br><span class="line"></span><br><span class="line"><span class="comment">#进入tf2436环境运行pip命令</span></span><br><span class="line">pip install --upgrade pip</span><br><span class="line">pip install numpy pandas matplotlib sklearn notebook tensorflow-cpu==<span class="number">2.4</span><span class="number">.0</span> -i https://pypi.douban.com/simple/</span><br></pre></td></tr></table></figure>
<h3 id="tf-keras实现线性回归"><a href="#tf-keras实现线性回归" class="headerlink" title="tf.keras实现线性回归"></a>tf.keras实现线性回归</h3><figure class="highlight python"><table><tr><td class="code"><pre><span class="line"><span class="keyword">import</span> tensorflow <span class="keyword">as</span> tf</span><br><span class="line"><span class="keyword">import</span> pandas <span class="keyword">as</span> pd</span><br><span class="line"><span class="keyword">import</span> matplotlib.pyplot <span class="keyword">as</span> plt</span><br><span class="line">print(<span class="string">'TensorFlow Version: {}'</span>.format(tf.__version__))</span><br><span class="line"></span><br><span class="line"><span class="comment">#读取数据</span></span><br><span class="line">data=pd.read_csv(<span class="string">'./dataset/data.csv'</span>)</span><br><span class="line"><span class="comment">#绘制散点图</span></span><br><span class="line">plt.scatter(data.Education,data.Income)</span><br><span class="line"><span class="comment">#分配数据</span></span><br><span class="line">x=data.Education</span><br><span class="line">y=data.Income</span><br><span class="line"><span class="comment">#定义模型</span></span><br><span class="line">model=tf.keras.Sequential()</span><br><span class="line">model.add(tf.keras.layers.Dense(<span class="number">1</span>,input_shape=(<span class="number">1</span>,)))<span class="comment">#添加一个Dense层,输出维度为1,输入维度也是1</span></span><br><span class="line"><span class="comment">#查看模型参数情况</span></span><br><span class="line">model.summary()</span><br><span class="line"><span class="comment">#编译模型</span></span><br><span class="line">model.compile(optimizer=<span class="string">'adam'</span>,</span><br><span class="line"> loss=<span class="string">'mse'</span>)</span><br><span class="line"><span class="comment">#训练模型</span></span><br><span class="line">history=model.fit(x,y,epochs=<span class="number">5000</span>)</span><br><span class="line"><span class="comment">#用模型进行预测</span></span><br><span class="line">model.predict(x)</span><br><span class="line">model.predict(pd.Series([<span class="number">20</span>]))</span><br></pre></td></tr></table></figure>
<h3 id="多层感知器的代码实现"><a href="#多层感知器的代码实现" class="headerlink" title="多层感知器的代码实现"></a>多层感知器的代码实现</h3><figure class="highlight python"><table><tr><td class="code"><pre><span class="line"><span class="keyword">import</span> tensorflow <span class="keyword">as</span> tf</span><br><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> pandas <span class="keyword">as</span> pd</span><br><span class="line"><span class="keyword">import</span> matplotlib.pyplot <span class="keyword">as</span> plt</span><br><span class="line">%matplotlib inline <span class="comment">#jupyter魔术方法</span></span><br><span class="line"></span><br><span class="line">data=pd.read_csv(dataset/dataset.csv)</span><br><span class="line"><span class="comment">#查看数据表</span></span><br><span class="line">data.head() </span><br><span class="line"><span class="comment">#绘散点图估计数据之间的关联</span></span><br><span class="line">plt.scatter(data.TV,data.sales)</span><br><span class="line"><span class="comment">#特征值与预测值分配</span></span><br><span class="line">x.data.iloc[:,<span class="number">1</span>:<span class="number">-1</span>] <span class="comment">#所有行,第二列到倒数第二列</span></span><br><span class="line">y=data.iloc[:,<span class="number">-1</span>] <span class="comment">#所有行,最后一列</span></span><br><span class="line"><span class="comment">#定义模型</span></span><br><span class="line"><span class="comment">#Dense()的第一个参数表示输出维度的大小,也就是隐藏层神经元个数</span></span><br><span class="line">model=tf.keras.Sequential([</span><br><span class="line"> tf.keras.Dense(<span class="number">10</span>,input_shape=(<span class="number">3</span>,),activation=<span class="string">'relu'</span>),</span><br><span class="line"> tf.keras.Dense(<span class="number">1</span>)</span><br><span class="line"> ])</span><br><span class="line"><span class="comment">#查看模型参数情况</span></span><br><span class="line">model.summary()</span><br><span class="line"><span class="comment">#编译模型</span></span><br><span class="line">model.compile(optimizer=<span class="string">'adam'</span>,</span><br><span class="line"> loss=<span class="string">'mse'</span>)</span><br><span class="line"><span class="comment">#训练模型</span></span><br><span class="line">history=model.fit(x,y,epochs=<span class="number">100</span>)</span><br><span class="line"><span class="comment">#用模型进行预测</span></span><br><span class="line">test=data.iloc[:<span class="number">10</span>,<span class="number">1</span>:<span class="number">-1</span>]</span><br><span class="line">model.predict(test)</span><br></pre></td></tr></table></figure>
<h3 id="逻辑回归与交叉熵"><a href="#逻辑回归与交叉熵" class="headerlink" title="逻辑回归与交叉熵"></a>逻辑回归与交叉熵</h3><p>Keras中用binary_crossentropy来计算二元交叉熵损失</p>
<h3 id="逻辑回归的代码实现"><a href="#逻辑回归的代码实现" class="headerlink" title="逻辑回归的代码实现"></a>逻辑回归的代码实现</h3><figure class="highlight python"><table><tr><td class="code"><pre><span class="line"><span class="keyword">import</span> tensorflow <span class="keyword">as</span> tf</span><br><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> pandas <span class="keyword">as</span> pd</span><br><span class="line"><span class="keyword">import</span> matplotlib.pyplot <span class="keyword">as</span> plt</span><br><span class="line">%matplotlib inline <span class="comment">#jupyter魔术方法</span></span><br><span class="line"></span><br><span class="line"><span class="comment">#加载数据(无表头)</span></span><br><span class="line">data=pd.read_csv(dataset/dataset.csv,header=<span class="literal">None</span>)</span><br><span class="line">data.head() </span><br><span class="line"><span class="comment">#检查样本均衡性</span></span><br><span class="line">data.iloc[:,<span class="number">-1</span>].value_counts()</span><br><span class="line"><span class="comment">#特征值与预测值分配</span></span><br><span class="line">x.data.iloc[:,:<span class="number">-1</span>] <span class="comment">#所有行,第1列到倒数第二列</span></span><br><span class="line">y=data.iloc[:,<span class="number">-1</span>].replace(<span class="number">-1</span>,<span class="number">0</span>) <span class="comment">#所有行,最后一列,并替换(-1)为(0)</span></span><br><span class="line"><span class="comment">#定义模型</span></span><br><span class="line">model=tf.keras.Sequential()</span><br><span class="line">model.add(tf.keras.Dense(<span class="number">4</span>,input_shape=(<span class="number">15</span>,),activation=<span class="string">'relu'</span>))</span><br><span class="line">model.add(tf.keras.Dense(<span class="number">4</span>,activation=<span class="string">'relu'</span>))</span><br><span class="line">model.add(tf.keras.Dense(<span class="number">1</span>,activation=<span class="string">'sigmoid'</span>))</span><br><span class="line"><span class="comment">#查看模型参数情况</span></span><br><span class="line">model.summary()</span><br><span class="line"><span class="comment">#编译模型</span></span><br><span class="line"><span class="comment">#配置优化算法,损失函数,指标输出</span></span><br><span class="line">model.compile(optimizer=<span class="string">'adam'</span>,</span><br><span class="line"> loss=<span class="string">'binary_crossentropy'</span>,</span><br><span class="line"> metrics=[<span class="string">'acc'</span>])</span><br><span class="line"><span class="comment">#训练模型</span></span><br><span class="line">history=model.fit(x,y,epochs=<span class="number">100</span>)</span><br><span class="line"><span class="comment">#查看训练过程记录(字典类型)</span></span><br><span class="line">history.history.keys()</span><br><span class="line">plt.plot(history.epoch,history.history.get(<span class="string">'loss'</span>))</span><br><span class="line">history.history.keys()</span><br><span class="line">plt.plot(history.epoch,history.history.get(<span class="string">'acc'</span>))</span><br></pre></td></tr></table></figure>
<h3 id="Softmax多分类"><a href="#Softmax多分类" class="headerlink" title="Softmax多分类"></a>Softmax多分类</h3><p><strong>Softmax层的作用</strong>就是把神经网络的输出向量转换成由概率值构成的向量</p>
<p>对于多分类问题,在Keras中用<strong>categorical_crossentropy</strong>和<strong>sparse_categorical_crossentropy</strong>来计算Softmax交叉熵损失。</p>
<p>对于0,1,2…n这样的顺序编号型label,应使用<strong>sparse_categorical_crossentropy</strong>;</p>
<p>对于one-hot向量型label(独热编码),应使用<strong>categorical_crossentropy</strong>。</p>
<h4 id="加载Fashion-MNIST数据集"><a href="#加载Fashion-MNIST数据集" class="headerlink" title="加载Fashion MNIST数据集"></a>加载Fashion MNIST数据集</h4><figure class="highlight python"><table><tr><td class="code"><pre><span class="line"><span class="keyword">import</span> tensorflow <span class="keyword">as</span> tf</span><br><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> pandas <span class="keyword">as</span> pd</span><br><span class="line"><span class="keyword">import</span> matplotlib.pyplot <span class="keyword">as</span> plt</span><br><span class="line">%matplotlib inline <span class="comment">#jupyter魔术方法</span></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">(train_image,train_label),(test_image,test_label)=tf.keras.datasets.fashion_mnist.load_data()</span><br></pre></td></tr></table></figure>
<h3 id="Softmax多分类代码实现"><a href="#Softmax多分类代码实现" class="headerlink" title="Softmax多分类代码实现"></a>Softmax多分类代码实现</h3><figure class="highlight python"><table><tr><td class="code"><pre><span class="line"><span class="keyword">import</span> tensorflow <span class="keyword">as</span> tf</span><br><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> pandas <span class="keyword">as</span> pd</span><br><span class="line"><span class="keyword">import</span> matplotlib.pyplot <span class="keyword">as</span> plt</span><br><span class="line">%matplotlib inline <span class="comment">#jupyter魔术方法</span></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">(train_image,train_label),(test_image,test_label)=tf.keras.datasets.fashion_mnist.load_data()</span><br><span class="line"><span class="comment">#查看数据集</span></span><br><span class="line">train_image.shape</span><br><span class="line">train_label.shape</span><br><span class="line">test_image.shape</span><br><span class="line">test_label.shape</span><br><span class="line">plt.imshow(train_image[<span class="number">0</span>])</span><br><span class="line">train_image[<span class="number">0</span>]</span><br><span class="line">np.max(train_image[<span class="number">0</span>]) <span class="comment">#发现是255</span></span><br><span class="line">train_label[<span class="number">0</span>]</span><br><span class="line">train_label</span><br><span class="line"><span class="comment">#像素灰度值归一化</span></span><br><span class="line">train_image=train_image/<span class="number">255</span></span><br><span class="line">test_image=test_image/<span class="number">255</span></span><br><span class="line"><span class="comment">#定义模型</span></span><br><span class="line">model=tf.Sequential()</span><br><span class="line">model.add(tf.keras.layers.Flatten(input_shape=(<span class="number">28</span>,<span class="number">28</span>))) <span class="comment">#展平图片28*28为向量</span></span><br><span class="line">model.add(tf.keras.layers.Dense(<span class="number">128</span>,activation=<span class="string">'relu'</span>))</span><br><span class="line">model.add(tf.keras.layers.Dense(<span class="number">10</span>,activation=<span class="string">'softmax'</span>)) <span class="comment">#softmax是以激活函数的形式加上的,不是独立的layer</span></span><br><span class="line"><span class="comment">#查看模型参数情况</span></span><br><span class="line">model.summary()</span><br><span class="line"><span class="comment">#编译模型</span></span><br><span class="line"><span class="comment">#配置优化算法,损失函数,指标输出</span></span><br><span class="line">model.compile(optimizer=<span class="string">'adam'</span>,</span><br><span class="line"> loss=<span class="string">'sparse_categorical_crossentropy'</span>,</span><br><span class="line"> metrics=[<span class="string">'acc'</span>])</span><br><span class="line"><span class="comment">#训练模型</span></span><br><span class="line">history=model.fit(train_image,train_label,epochs=<span class="number">5</span>)</span><br><span class="line"><span class="comment">#在test集上评估模型性能</span></span><br><span class="line">model.evaluate(test_image,test_label)</span><br></pre></td></tr></table></figure>
<h3 id="独热编码与交叉熵损失"><a href="#独热编码与交叉熵损失" class="headerlink" title="独热编码与交叉熵损失"></a>独热编码与交叉熵损失</h3><p>one-hot向量型label</p>
<figure class="highlight python"><table><tr><td class="code"><pre><span class="line"><span class="keyword">import</span> tensorflow <span class="keyword">as</span> tf</span><br><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> pandas <span class="keyword">as</span> pd</span><br><span class="line"><span class="keyword">import</span> matplotlib.pyplot <span class="keyword">as</span> plt</span><br><span class="line">%matplotlib inline <span class="comment">#jupyter魔术方法</span></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">(train_image,train_label),(test_image,test_label)=tf.keras.datasets.fashion_mnist.load_data()</span><br><span class="line"></span><br><span class="line"><span class="comment">#将label转为one-hot向量</span></span><br><span class="line">train_label_onehot=tf.keras.utils.to_categorical(train_label)</span><br><span class="line">test_label_onehot=tf.keras.utils.to_categorical(test_label)</span><br><span class="line"><span class="comment">#查看新的label</span></span><br><span class="line">train_label_onehot</span><br><span class="line">train_label_onehot[<span class="number">0</span>]</span><br><span class="line">train_label_onehot[<span class="number">-1</span>]</span><br><span class="line"><span class="comment">#像素灰度值归一化</span></span><br><span class="line">train_image=train_image/<span class="number">255</span></span><br><span class="line">test_image=test_image/<span class="number">255</span></span><br><span class="line"><span class="comment">#定义模型</span></span><br><span class="line">model=tf.Sequential()</span><br><span class="line">model.add(tf.keras.layers.Flatten(input_shape=(<span class="number">28</span>,<span class="number">28</span>))) <span class="comment">#展平图片28*28为向量</span></span><br><span class="line">model.add(tf.keras.layers.Dense(<span class="number">128</span>,activation=<span class="string">'relu'</span>))</span><br><span class="line">model.add(tf.keras.layers.Dense(<span class="number">10</span>,activation=<span class="string">'softmax'</span>)) <span class="comment">#softmax是以激活函数的形式加上的,不是独立的layer</span></span><br><span class="line"><span class="comment">#查看模型参数情况</span></span><br><span class="line">model.summary()</span><br><span class="line"><span class="comment">#编译模型</span></span><br><span class="line"><span class="comment">#配置优化算法,损失函数,指标输出</span></span><br><span class="line">model.compile(optimizer=<span class="string">'adam'</span>,</span><br><span class="line"> loss=<span class="string">'categorical_crossentropy'</span>,</span><br><span class="line"> metrics=[<span class="string">'acc'</span>])</span><br><span class="line"><span class="comment">#训练模型</span></span><br><span class="line">history=model.fit(train_image,train_label_onehot,epochs=<span class="number">5</span>)</span><br><span class="line"><span class="comment">#在test集上评估模型性能</span></span><br><span class="line">model.evaluate(test_image,test_label_onehot)</span><br><span class="line"><span class="comment">#用模型进行预测</span></span><br><span class="line">predict=model.predict(test_image)</span><br><span class="line">predict[<span class="number">0</span>]</span><br><span class="line">np.argmax(predict[<span class="number">0</span>]) <span class="comment">#找到最大概率的索引即是预测分类结果</span></span><br></pre></td></tr></table></figure>
<h3 id="优化算法、学习速率与反向传播算法"><a href="#优化算法、学习速率与反向传播算法" class="headerlink" title="优化算法、学习速率与反向传播算法"></a>优化算法、学习速率与反向传播算法</h3><p>SGD:min-batch方法</p>
<p>RMSprop:适合序列问题,如文本分类、<strong>一维卷积</strong>,多用于循环神经网络(RNN)训练</p>
<p>Adam:可看作修正后的Momentum+RMSprop算法,对超参数的选择不敏感,学习率建议设为0.001</p>
<h4 id="通过设置超参数的方法配置优化算法"><a href="#通过设置超参数的方法配置优化算法" class="headerlink" title="通过设置超参数的方法配置优化算法"></a>通过设置超参数的方法配置优化算法</h4><p>如果仅指定算法名称,将采用默认参数,这里可以通过另一种方式设置超参数</p>
<figure class="highlight python"><table><tr><td class="code"><pre><span class="line"><span class="comment">#编译模型</span></span><br><span class="line"><span class="comment">#配置优化算法,损失函数,指标输出</span></span><br><span class="line">model.compile(optimizer=tf.keras.optimizers.Adam(learning_rate=<span class="number">0.01</span>),</span><br><span class="line"> loss=<span class="string">'categorical_crossentropy'</span>,</span><br><span class="line"> metrics=[<span class="string">'acc'</span>])</span><br></pre></td></tr></table></figure>
<h3 id="网络优化与超参数选择"><a href="#网络优化与超参数选择" class="headerlink" title="网络优化与超参数选择"></a>网络优化与超参数选择</h3><p>网络容量:与可训练参数的数量成正比</p>
<p>容量大的网络训练速度慢,难度大,易产生过拟合。</p>
<h4 id="如何提高网络的拟合能力"><a href="#如何提高网络的拟合能力" class="headerlink" title="如何提高网络的拟合能力"></a>如何提高网络的拟合能力</h4><ol>
<li>增大网络容量:增加层比单纯增加神经元个数更有效,而单层神经元个数又不能太小,否则会造成信息瓶颈,导致欠拟合</li>
</ol>
<h3 id="过拟合、Dropout、网络参数选择总原则"><a href="#过拟合、Dropout、网络参数选择总原则" class="headerlink" title="过拟合、Dropout、网络参数选择总原则"></a>过拟合、Dropout、网络参数选择总原则</h3><h4 id="在训练中添加验证环节,查看过拟合现象"><a href="#在训练中添加验证环节,查看过拟合现象" class="headerlink" title="在训练中添加验证环节,查看过拟合现象"></a>在训练中添加验证环节,查看过拟合现象</h4><figure class="highlight python"><table><tr><td class="code"><pre><span class="line"><span class="keyword">import</span> tensorflow <span class="keyword">as</span> tf</span><br><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> pandas <span class="keyword">as</span> pd</span><br><span class="line"><span class="keyword">import</span> matplotlib.pyplot <span class="keyword">as</span> plt</span><br><span class="line">%matplotlib inline <span class="comment">#jupyter魔术方法</span></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">(train_image,train_label),(test_image,test_label)=tf.keras.datasets.fashion_mnist.load_data()</span><br><span class="line"></span><br><span class="line"><span class="comment">#将label转为one-hot向量</span></span><br><span class="line">train_label_onehot=tf.keras.utils.to_categorical(train_label)</span><br><span class="line">test_label_onehot=tf.keras.utils.to_categorical(test_label)</span><br><span class="line"><span class="comment">#查看新的label</span></span><br><span class="line">train_label_onehot</span><br><span class="line">train_label_onehot[<span class="number">0</span>]</span><br><span class="line">train_label_onehot[<span class="number">-1</span>]</span><br><span class="line"><span class="comment">#像素灰度值归一化</span></span><br><span class="line">train_image=train_image/<span class="number">255</span></span><br><span class="line">test_image=test_image/<span class="number">255</span></span><br><span class="line"><span class="comment">#定义模型</span></span><br><span class="line">model=tf.Sequential()</span><br><span class="line">model.add(tf.keras.layers.Flatten(input_shape=(<span class="number">28</span>,<span class="number">28</span>))) <span class="comment">#展平图片28*28为向量</span></span><br><span class="line">model.add(tf.keras.layers.Dense(<span class="number">128</span>,activation=<span class="string">'relu'</span>))</span><br><span class="line">model.add(tf.keras.layers.Dense(<span class="number">10</span>,activation=<span class="string">'softmax'</span>)) <span class="comment">#softmax是以激活函数的形式加上的,不是独立的layer</span></span><br><span class="line"><span class="comment">#查看模型参数情况</span></span><br><span class="line">model.summary()</span><br><span class="line"><span class="comment">#编译模型</span></span><br><span class="line"><span class="comment">#配置优化算法,损失函数,指标输出</span></span><br><span class="line">model.compile(optimizer=<span class="string">'adam'</span>,</span><br><span class="line"> loss=<span class="string">'categorical_crossentropy'</span>,</span><br><span class="line"> metrics=[<span class="string">'acc'</span>])</span><br><span class="line"><span class="comment">#训练模型(添加验证环节)</span></span><br><span class="line">history=model.fit(train_image,train_label_onehot,</span><br><span class="line"> epochs=<span class="number">10</span>,</span><br><span class="line"> validation_data=(test_image,test_label_onehot) )</span><br><span class="line"><span class="comment">#查看训练过程记录(字典类型)</span></span><br><span class="line">history.history.keys()</span><br><span class="line">plt.plot(history.epoch,history.history.get(<span class="string">'loss'</span>),label=<span class="string">'loss'</span>)</span><br><span class="line">plt.plot(history.epoch,history.history.get(<span class="string">'val_loss'</span>),label=<span class="string">'val_loss'</span>)</span><br><span class="line">plt.legend()<span class="comment">#从这里看到训练集loss在不断减小,但验证集的loss先减小后增大,说明过拟合了</span></span><br><span class="line"></span><br><span class="line">plt.plot(history.epoch,history.history.get(<span class="string">'acc'</span>),label=<span class="string">'acc'</span>)</span><br><span class="line">plt.plot(history.epoch,history.history.get(<span class="string">'val_acc'</span>),label=<span class="string">'val_acc'</span>)</span><br><span class="line">plt.legend()<span class="comment">#可以看到训练集与验证集的正确率都在上上升,但验证集的正确率相对小得多,说明过拟合了</span></span><br></pre></td></tr></table></figure>
<p>过拟合:模型在训练集上表现很好,在测试集上表现较差</p>
<p>欠拟合:模型在训练集上表现不好,在测试集上表现也不好</p>
<h4 id="为什么Dropout可以缓解过拟合?"><a href="#为什么Dropout可以缓解过拟合?" class="headerlink" title="为什么Dropout可以缓解过拟合?"></a>为什么Dropout可以缓解过拟合?</h4><p>每次训练都只有一部分参数被更新,因此相当于训练了多个不同的网络,最后这些网络又共同组成了整体模型。</p>
<ol>
<li>有“训练多个不同模型取均值”的作用</li>
<li>减少神经元之间复杂的共适应关系</li>
<li>类似于生物学中的基因突变</li>
</ol>
<h4 id="参数选择总原则"><a href="#参数选择总原则" class="headerlink" title="参数选择总原则"></a>参数选择总原则</h4><p>首先要开发一个过拟合的模型,保证模型有足够的拟合能力:</p>
<ol>
<li>添加更过层,</li>
<li>增大每一层,</li>
<li>训练更多的epoch</li>
</ol>
<p>然后抑制过拟合:</p>
<ol>
<li>dropout</li>
<li>正则化</li>
<li>数据增强</li>
</ol>
<p>最后调节超参数:</p>
<ol>
<li>学习率</li>
<li>隐藏层单元数</li>
<li>训练轮数</li>
</ol>
<p>经典机器学习方法:</p>
<ol>
<li>特征工程</li>
<li>增加训练数据</li>
</ol>
<p>调参过程中要注意交叉验证,把数据划分为3块。</p>
<p>增大网络容量->抑制过拟合->增大网络容量->抑制过拟合…</p>
<h3 id="用Dropout抑制过拟合-代码实现"><a href="#用Dropout抑制过拟合-代码实现" class="headerlink" title="用Dropout抑制过拟合-代码实现"></a>用Dropout抑制过拟合-代码实现</h3><figure class="highlight python"><table><tr><td class="code"><pre><span class="line"><span class="keyword">import</span> tensorflow <span class="keyword">as</span> tf</span><br><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> pandas <span class="keyword">as</span> pd</span><br><span class="line"><span class="keyword">import</span> matplotlib.pyplot <span class="keyword">as</span> plt</span><br><span class="line">%matplotlib inline <span class="comment">#jupyter魔术方法</span></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">(train_image,train_label),(test_image,test_label)=tf.keras.datasets.fashion_mnist.load_data()</span><br><span class="line"></span><br><span class="line"><span class="comment">#将label转为one-hot向量</span></span><br><span class="line">train_label_onehot=tf.keras.utils.to_categorical(train_label)</span><br><span class="line">test_label_onehot=tf.keras.utils.to_categorical(test_label)</span><br><span class="line"><span class="comment">#查看新的label</span></span><br><span class="line">train_label_onehot</span><br><span class="line">train_label_onehot[<span class="number">0</span>]</span><br><span class="line">train_label_onehot[<span class="number">-1</span>]</span><br><span class="line"><span class="comment">#像素灰度值归一化</span></span><br><span class="line">train_image=train_image/<span class="number">255</span></span><br><span class="line">test_image=test_image/<span class="number">255</span></span><br><span class="line"><span class="comment">#定义模型</span></span><br><span class="line">model=tf.Sequential()</span><br><span class="line">model.add(tf.keras.layers.Flatten(input_shape=(<span class="number">28</span>,<span class="number">28</span>))) <span class="comment">#展平图片28*28为向量</span></span><br><span class="line">model.add(tf.keras.layers.Dense(<span class="number">128</span>,activation=<span class="string">'relu'</span>))</span><br><span class="line">model.add(tf.keras.layers.Dropout(<span class="number">0.5</span>))<span class="comment">#参数为input_units_drop_rate</span></span><br><span class="line">model.add(tf.keras.layers.Dense(<span class="number">128</span>,activation=<span class="string">'relu'</span>))</span><br><span class="line">model.add(tf.keras.layers.Dropout(<span class="number">0.5</span>))<span class="comment">#参数为input_units_drop_rate</span></span><br><span class="line">model.add(tf.keras.layers.Dense(<span class="number">128</span>,activation=<span class="string">'relu'</span>))</span><br><span class="line">model.add(tf.keras.layers.Dropout(<span class="number">0.5</span>))<span class="comment">#参数为input_units_drop_rate</span></span><br><span class="line">model.add(tf.keras.layers.Dense(<span class="number">10</span>,activation=<span class="string">'softmax'</span>)) <span class="comment">#softmax是以激活函数的形式加上的,不是独立的layer</span></span><br><span class="line"><span class="comment">#查看模型参数情况</span></span><br><span class="line">model.summary()</span><br><span class="line"><span class="comment">#编译模型</span></span><br><span class="line"><span class="comment">#配置优化算法,损失函数,指标输出</span></span><br><span class="line">model.compile(optimizer=<span class="string">'adam'</span>,</span><br><span class="line"> loss=<span class="string">'categorical_crossentropy'</span>,</span><br><span class="line"> metrics=[<span class="string">'acc'</span>])</span><br><span class="line"><span class="comment">#训练模型(添加验证环节)</span></span><br><span class="line">history=model.fit(train_image,train_label_onehot,</span><br><span class="line"> epochs=<span class="number">10</span>,</span><br><span class="line"> validation_data=(test_image,test_label_onehot) )</span><br><span class="line"><span class="comment">#查看训练过程记录(字典类型)</span></span><br><span class="line">history.history.keys()</span><br><span class="line">plt.plot(history.epoch,history.history.get(<span class="string">'loss'</span>),label=<span class="string">'loss'</span>)</span><br><span class="line">plt.plot(history.epoch,history.history.get(<span class="string">'val_loss'</span>),label=<span class="string">'val_loss'</span>)</span><br><span class="line">plt.legend()</span><br><span class="line"></span><br><span class="line">plt.plot(history.epoch,history.history.get(<span class="string">'acc'</span>),label=<span class="string">'acc'</span>)</span><br><span class="line">plt.plot(history.epoch,history.history.get(<span class="string">'val_acc'</span>),label=<span class="string">'val_acc'</span>)</span><br><span class="line">plt.legend()</span><br></pre></td></tr></table></figure>
<p>增大数据规模、减小网络规模和正则化也可以抑制过拟合。</p>
<p>其中正则化可以在设置层参数时配置,默认值是None,如</p>
<figure class="highlight"><table><tr><td class="code"><pre><span class="line">model.add(tf.keras.layers.Dense(128,activation='relu',kernel_regularizer=??))</span><br></pre></td></tr></table></figure>
<h3 id="函数式API与多输入多输出"><a href="#函数式API与多输入多输出" class="headerlink" title="函数式API与多输入多输出"></a>函数式API与多输入多输出</h3><h4 id="函数式API使模型定义更加灵活"><a href="#函数式API使模型定义更加灵活" class="headerlink" title="函数式API使模型定义更加灵活"></a>函数式API使模型定义更加灵活</h4><figure class="highlight python"><table><tr><td class="code"><pre><span class="line"><span class="keyword">import</span> tensorflow <span class="keyword">as</span> tf</span><br><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> pandas <span class="keyword">as</span> pd</span><br><span class="line"><span class="keyword">import</span> matplotlib.pyplot <span class="keyword">as</span> plt</span><br><span class="line">%matplotlib inline <span class="comment">#jupyter魔术方法</span></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">(train_image,train_label),(test_image,test_label)=tf.keras.datasets.fashion_mnist.load_data()</span><br><span class="line"><span class="comment">#像素灰度值归一化</span></span><br><span class="line">train_image=train_image/<span class="number">255.0</span></span><br><span class="line">test_image=test_image/<span class="number">255.0</span></span><br><span class="line"><span class="comment">#定义模型</span></span><br><span class="line">input=tf.keras.Input(shape=(<span class="number">28</span>,<span class="number">28</span>))</span><br><span class="line">x=tf.keras.layers.Flatten()(input)</span><br><span class="line">x=tf.keras.layers.Dense(<span class="number">32</span>,activation=<span class="string">'relu'</span>)(x)</span><br><span class="line">x=tf.keras.layers.Dropout(<span class="number">0.5</span>)(x)</span><br><span class="line">x=tf.keras.layers.Dense(<span class="number">64</span>,activation=<span class="string">'relu'</span>)(x)</span><br><span class="line">output=tf.keras.layers.Dense(<span class="number">10</span>,activation=<span class="string">'softmax'</span>)(x)</span><br><span class="line">model=tf.keras.Model(inputs=input,outputs=output)</span><br><span class="line"><span class="comment">#查看模型参数</span></span><br><span class="line">model.summary()</span><br><span class="line"><span class="comment">#编译模型</span></span><br><span class="line"><span class="comment">#配置优化算法,损失函数,指标输出</span></span><br><span class="line">model.compile(optimizer=<span class="string">'adam'</span>,</span><br><span class="line"> loss=<span class="string">'sparse_categorical_crossentropy'</span>,</span><br><span class="line"> metrics=[<span class="string">'acc'</span>])</span><br><span class="line"><span class="comment">#训练模型</span></span><br><span class="line">history=model.fit(train_image,train_label,epochs=<span class="number">5</span>)</span><br><span class="line"><span class="comment">#在test集上评估模型性能</span></span><br><span class="line">model.evaluate(test_image,test_label)</span><br></pre></td></tr></table></figure>
<h4 id="多输入多输出"><a href="#多输入多输出" class="headerlink" title="多输入多输出"></a>多输入多输出</h4><figure class="highlight python"><table><tr><td class="code"><pre><span class="line"><span class="keyword">import</span> tensorflow <span class="keyword">as</span> tf</span><br><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> pandas <span class="keyword">as</span> pd</span><br><span class="line"><span class="keyword">import</span> matplotlib.pyplot <span class="keyword">as</span> plt</span><br><span class="line">%matplotlib inline <span class="comment">#jupyter魔术方法</span></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">(train_image,train_label),(test_image,test_label)=tf.keras.datasets.fashion_mnist.load_data()</span><br><span class="line"><span class="comment">#像素灰度值归一化</span></span><br><span class="line">train_image=train_image/<span class="number">255.0</span></span><br><span class="line">test_image=test_image/<span class="number">255.0</span></span><br><span class="line"><span class="comment">#上面的数据集还需要进一步处理才能用于下面的多输入模型训练</span></span><br><span class="line"></span><br><span class="line"><span class="comment">#定义模型(多输入)</span></span><br><span class="line">input1=tf.keras.Input(shape=(<span class="number">28</span>,<span class="number">28</span>))</span><br><span class="line">input2=tf.keras.Input(shape=(<span class="number">28</span>,<span class="number">28</span>))</span><br><span class="line">x1=tf.keras.layers.Flatten()(input1)</span><br><span class="line">x2=tf.keras.layers.Flatten()(input2)</span><br><span class="line">x=tf.keras.layers.concatenate([x1,x2])</span><br><span class="line"></span><br><span class="line">x=tf.keras.layers.Dense(<span class="number">32</span>,activation=<span class="string">'relu'</span>)(x)</span><br><span class="line">output=tf.keras.layers.Dense(<span class="number">1</span>,activation=<span class="string">'sigmoid'</span>)(x)</span><br><span class="line">model=tf.keras.Model(inputs=[input1,input2],outputs=output)</span><br><span class="line"><span class="comment">#查看模型参数</span></span><br><span class="line">model.summary()</span><br><span class="line"><span class="comment">#编译模型</span></span><br><span class="line"><span class="comment">#配置优化算法,损失函数,指标输出</span></span><br><span class="line">model.compile(optimizer=<span class="string">'adam'</span>,</span><br><span class="line"> loss=<span class="string">'binary_crossentropy'</span>,</span><br><span class="line"> metrics=[<span class="string">'acc'</span>])</span><br><span class="line"><span class="comment">#训练模型</span></span><br><span class="line">history=model.fit(train_image1,train_image2,train_label,epochs=<span class="number">5</span>)</span><br><span class="line"><span class="comment">#在test集上评估模型性能</span></span><br><span class="line">model.evaluate(test_image,test_label)</span><br></pre></td></tr></table></figure>
<hr>
<h3 id="tf-data模块"><a href="#tf-data模块" class="headerlink" title="tf.data模块"></a>tf.data模块</h3><p>最重要的概念(类):tf.data.Dataset</p>
<p>Dataset的创建方法:</p>
<ol>
<li>Dataset.from_tensor_slices()</li>
<li>变换Dataset以创建新的Dataset</li>
</ol>
<figure class="highlight python"><table><tr><td class="code"><pre><span class="line"><span class="keyword">import</span> tensorflow <span class="keyword">as</span> tf</span><br><span class="line"></span><br><span class="line"><span class="comment">#要求Dataset得每个元素结构相同</span></span><br><span class="line"><span class="comment">#从一维列表创建Dataset</span></span><br><span class="line">dataset=tf.data.Dataset.from_tensor_slices([<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><br><span class="line"><span class="keyword">for</span> ele <span class="keyword">in</span> dataset:</span><br><span class="line"> print(ele) <span class="comment">#输出Tensor类型</span></span><br><span class="line"> print(ele.numpy()) <span class="comment">#输出numpy类型</span></span><br><span class="line"><span class="comment">#从二维列表创建Dataset</span></span><br><span class="line">dataset=tf.data.Dataset.from_tensor_slices([[<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><br><span class="line"><span class="keyword">for</span> ele <span class="keyword">in</span> dataset:</span><br><span class="line"> print(ele) <span class="comment">#输出Tensor类型</span></span><br><span class="line"> print(ele.numpy()) <span class="comment">#输出numpy类型</span></span><br><span class="line"><span class="comment">#从字典创建Dataset(比较特殊)</span></span><br><span class="line"><span class="comment">#会把每个列表中的每个值取出来,跟key一起当作一个Tensor</span></span><br><span class="line">dataset_dic=tf.data.Dataset.from_tensor_slices({</span><br><span class="line"> <span class="string">'a'</span>:[<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="string">'b'</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">'c'</span>:[<span class="number">12</span>,<span class="number">13</span>,<span class="number">14</span>,<span class="number">15</span>]})</span><br><span class="line"><span class="keyword">for</span> ele <span class="keyword">in</span> dataset_dic:<span class="comment">#</span></span><br><span class="line"> print(ele) <span class="comment">#输出Tensor类型(第一个tensor:a 1,b 6,c 12)</span></span><br></pre></td></tr></table></figure>
<h3 id="tf-data模块用法示例"><a href="#tf-data模块用法示例" class="headerlink" title="tf.data模块用法示例"></a>tf.data模块用法示例</h3><p>Dataset.from_tensor_slices()的输入也可以是numpy的array类型</p>
<figure class="highlight python"><table><tr><td class="code"><pre><span class="line"><span class="keyword">import</span> tensorflow <span class="keyword">as</span> tf</span><br><span class="line"><span class="keyword">import</span> numpy <span class="keyword">as</span> np</span><br><span class="line"></span><br><span class="line"><span class="comment">#从numpy一维列表创建Dataset</span></span><br><span class="line">dataset=tf.data.Dataset.from_tensor_slices(np.array([<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><br><span class="line"><span class="keyword">for</span> ele <span class="keyword">in</span> dataset:</span><br><span class="line"> print(ele)</span><br><span class="line"> print(ele.numpy())</span><br><span class="line"><span class="comment">#仅遍历前4个元素</span></span><br><span class="line"><span class="keyword">for</span> ele <span class="keyword">in</span> dataset.take(<span class="number">4</span>):</span><br><span class="line"> print(ele.numpy())</span><br><span class="line"><span class="comment">#打乱Dataset的元素顺序</span></span><br><span class="line">dataset=dataset.shuffle(<span class="number">5</span>) <span class="comment">#参数为打乱顺序的元素个数</span></span><br><span class="line"><span class="keyword">for</span> ele <span class="keyword">in</span> dataset:</span><br><span class="line"> print(ele.numpy())</span><br><span class="line"><span class="comment">#重复执行乱序并把每次的结果合并到一个Dataset</span></span><br><span class="line">dataset=dataset.shuffle(<span class="number">5</span>)</span><br><span class="line">dataset=dataset.repeat(count=<span class="number">3</span>)<span class="comment">#默认为none,表示无限重复下去</span></span><br><span class="line"><span class="keyword">for</span> ele <span class="keyword">in</span> dataset: <span class="comment">#因为包含3次乱序的结果,所以共有3x5个元素</span></span><br><span class="line"> print(ele.numpy())</span><br><span class="line"><span class="comment">#Dataset的batch功能</span></span><br><span class="line">dataset=dataset.shuffle(<span class="number">5</span>)</span><br><span class="line">dataset=dataset.repeat()</span><br><span class="line">datatset.batch(<span class="number">3</span>) <span class="comment">#遍历时每次取出3个元素</span></span><br><span class="line"><span class="keyword">for</span> ele <span class="keyword">in</span> dataset: </span><br><span class="line"> print(ele.numpy())</span><br><span class="line"> </span><br><span class="line"></span><br><span class="line"><span class="comment">#同时操作Dataset中的所有元素</span></span><br><span class="line"><span class="comment">#用某个函数对每个元素进行变换</span></span><br><span class="line">dataset=tf.data.Dataset.from_tensor_slices(np.array([<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><br><span class="line">dataset=dataset.map(tf.square) <span class="comment">#把每一个元素都经tf.square函数映射一下</span></span><br><span class="line"><span class="keyword">for</span> ele <span class="keyword">in</span> dataset:</span><br><span class="line"> print(ele.numpy())</span><br></pre></td></tr></table></figure>
<h3 id="tf-data输入实例(1)"><a href="#tf-data输入实例(1)" class="headerlink" title="tf.data输入实例(1)"></a>tf.data输入实例(1)</h3><figure class="highlight python"><table><tr><td class="code"><pre><span class="line"><span class="keyword">import</span> tensorflow <span class="keyword">as</span> tf</span><br><span class="line"></span><br><span class="line"><span class="comment">#从内置数据集加载数据</span></span><br><span class="line">(train_images,train_labels),(test_images,test_labels)=tf.keras.datasets.mnist.load_data()</span><br><span class="line">train_images=train_images/<span class="number">255</span></span><br><span class="line">test_images=test_images/<span class="number">255</span></span><br><span class="line"><span class="comment">#将img和label分别装进Dataset</span></span><br><span class="line">ds_train_img=tf.data.Dataset.from_tensor_slices(train_images)</span><br><span class="line">ds_train_lab=tf.data.Dataset.from_tensor_slices(train_labels)</span><br><span class="line"><span class="comment">#将img和label的Dataset关联起来</span></span><br><span class="line">ds_train=tf.data.Dataset.zip((ds_train_img,ds_train_lab))<span class="comment">#以元组的形式输入</span></span><br><span class="line"><span class="comment">#乱序+重复操作+设置batch_size</span></span><br><span class="line">ds_train=ds_train.shuffle(<span class="number">10000</span>).repeat().batch(<span class="number">64</span>)</span><br><span class="line"><span class="comment">#定义模型</span></span><br><span class="line">model=tf.keras.Sequential([</span><br><span class="line"> tf.keras.layers.Flatten(input_shape=(<span class="number">28</span>,<span class="number">28</span>)),</span><br><span class="line"> tf.keras.layers.Dense(<span class="number">128</span>,activation=<span class="string">'relu'</span>),</span><br><span class="line"> tf.keras.layers.Dense(<span class="number">10</span>,activation=<span class="string">'softmax'</span>)</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">model.compile(</span><br><span class="line"> optimizer=<span class="string">'adam'</span>,</span><br><span class="line"> loss=<span class="string">'sparse_categorical_crossentropy'</span>,</span><br><span class="line"> metrics=[<span class="string">'acc'</span>])</span><br><span class="line"><span class="comment">#训练模型</span></span><br><span class="line">steps_per_epochs=train_images.shape[<span class="number">0</span>]//<span class="number">64</span><span class="comment">#指定每轮的迭代次数:总量除以batch_size(整除)</span></span><br><span class="line">history=model.fit(ds_train,epochs=<span class="number">5</span>,steps_per_epochs=steps_per_epochs)</span><br></pre></td></tr></table></figure>
<h3 id="tf-data输入实例(2)"><a href="#tf-data输入实例(2)" class="headerlink" title="tf.data输入实例(2)"></a>tf.data输入实例(2)</h3><p>添加验证数据集,在训练时加入验证环节</p>
<figure class="highlight python"><table><tr><td class="code"><pre><span class="line"><span class="keyword">import</span> tensorflow <span class="keyword">as</span> tf</span><br><span class="line"></span><br><span class="line"><span class="comment">#从内置数据集加载数据</span></span><br><span class="line">(train_images,train_labels),(test_images,test_labels)=tf.keras.datasets.mnist.load_data()</span><br><span class="line">train_images=train_images/<span class="number">255</span></span><br><span class="line">test_images=test_images/<span class="number">255</span></span><br><span class="line"><span class="comment">#将img和label分别装进Dataset</span></span><br><span class="line">ds_train_img=tf.data.Dataset.from_tensor_slices(train_images)</span><br><span class="line">ds_train_lab=tf.data.Dataset.from_tensor_slices(train_labels)</span><br><span class="line"><span class="comment">#再将img和label的Dataset关联起来</span></span><br><span class="line">ds_train=tf.data.Dataset.zip((ds_train_img,ds_train_lab))<span class="comment">#以元组的形式输入</span></span><br><span class="line"><span class="comment"># 或者直接在建立Dataset时就关联好label</span></span><br><span class="line">ds_test=tf.data.Dataset.from_tensor_slices((test_images,test_labels))</span><br><span class="line"><span class="comment">#乱序+重复操作+设置batch_size</span></span><br><span class="line">ds_train=ds_train.shuffle(<span class="number">10000</span>).repeat().batch(<span class="number">64</span>)</span><br><span class="line">ds_test=ds_test.batch(<span class="number">64</span>)</span><br><span class="line"><span class="comment">#定义模型</span></span><br><span class="line">model=tf.keras.Sequential([</span><br><span class="line"> tf.keras.layers.Flatten(input_shape=(<span class="number">28</span>,<span class="number">28</span>)),</span><br><span class="line"> tf.keras.layers.Dense(<span class="number">128</span>,activation=<span class="string">'relu'</span>),</span><br><span class="line"> tf.keras.layers.Dense(<span class="number">10</span>,activation=<span class="string">'softmax'</span>)</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">model.compile(</span><br><span class="line"> optimizer=<span class="string">'adam'</span>,</span><br><span class="line"> loss=<span class="string">'sparse_categorical_crossentropy'</span>,</span><br><span class="line"> metrics=[<span class="string">'acc'</span>])</span><br><span class="line"><span class="comment">#训练模型</span></span><br><span class="line">steps_per_epochs=train_images.shape[<span class="number">0</span>]//<span class="number">64</span><span class="comment">#指定每轮的迭代次数:总量除以batch_size(整除)</span></span><br><span class="line">history=model.fit(ds_train,</span><br><span class="line"> epochs=<span class="number">5</span>,</span><br><span class="line"> steps_per_epochs=steps_per_epochs,</span><br><span class="line"> validation_data=ds_test,</span><br><span class="line"> validation_steps=<span class="number">10000</span>//<span class="number">64</span></span><br><span class="line"> )<span class="comment">#添加validation_data和validation_steps参数</span></span><br></pre></td></tr></table></figure>
<h3 id="认识卷积神经网络(1)"><a href="#认识卷积神经网络(1)" class="headerlink" title="认识卷积神经网络(1)"></a>认识卷积神经网络(1)</h3><p>解决了图像处理时参数爆炸问题</p>
<h3 id="认识卷积神经网络-卷积层与池化层"><a href="#认识卷积神经网络-卷积层与池化层" class="headerlink" title="认识卷积神经网络-卷积层与池化层"></a>认识卷积神经网络-卷积层与池化层</h3><p>ksize,stride,padding</p>
<h3 id="卷积神经网络整体架构"><a href="#卷积神经网络整体架构" class="headerlink" title="卷积神经网络整体架构"></a>卷积神经网络整体架构</h3><p>CNN提取特征的过程就是使特征图变厚变小的过程,最后变成1xn向量方便softmax分类器处理。</p>
<h3 id="CNN识别Fashionmnist数据集"><a href="#CNN识别Fashionmnist数据集" class="headerlink" title="CNN识别Fashionmnist数据集"></a>CNN识别Fashionmnist数据集</h3><p>可用kaggle免费使用GPU资源。</p>
<figure class="highlight python"><table><tr><td class="code"><pre><span class="line"><span class="keyword">from</span> tensorflow <span class="keyword">import</span> keras</span><br><span class="line"><span class="keyword">import</span> matplotlib.pyplot <span class="keyword">as</span> plt</span><br><span class="line"><span class="keyword">import</span> numpy <span class="keyword">as</span> np</span><br><span class="line"></span><br><span class="line"><span class="comment">#加载数据集</span></span><br><span class="line">fashion_mnist=keras.datasets.fashion_mnist</span><br><span class="line">(train_images,train_labels),(test_images,test_labels)=fashion_mnist.load_data()</span><br><span class="line"><span class="comment">#增加图像维度(n,h,w,c),也可用reshape方法实现</span></span><br><span class="line">train_images=np.expand_dims(train_images,<span class="number">-1</span>)</span><br><span class="line">test_images=np.expand_dims(test_images,<span class="number">-1</span>)<span class="comment">#此时图片shape变成(28,28,1)</span></span><br><span class="line"></span><br><span class="line"><span class="comment">#定义模型</span></span><br><span class="line">model=tf.keras.Sequential()</span><br><span class="line">model.add(tf.keras.layers.Conv2D(<span class="number">32</span>,(<span class="number">3</span>,<span class="number">3</span>),</span><br><span class="line"> input_shape=(<span class="number">28</span>,<span class="number">28</span>,<span class="number">1</span>),</span><br><span class="line"> activation=<span class="string">'relu'</span>,</span><br><span class="line"> padding=<span class="string">'same'</span>))</span><br><span class="line">model.output_shape<span class="comment">#查看输出形状</span></span><br><span class="line">model.add(tf.keras.layers.MaxPool2D())<span class="comment">#默认2x2</span></span><br><span class="line">model.output_shape<span class="comment">#查看输出形状</span></span><br><span class="line">model.add(tf.keras.layers.Conv2D(<span class="number">64</span>,(<span class="number">3</span>,<span class="number">3</span>),activation=<span class="string">'relu'</span>))<span class="comment">#每层卷积核个数按照2^n递增效果会比较好</span></span><br><span class="line">model.output_shape<span class="comment">#查看输出形状</span></span><br><span class="line">model.add(tf.keras.layers.GlobalAveragePooling2D())<span class="comment">#用全局池化实现特征图扁平化(不同于全连接层的全局卷积)</span></span><br><span class="line">model.add(tf.keras.layers.Dense(<span class="number">10</span>,activation=<span class="string">'softmax'</span>))<span class="comment">#对特征向量进行线性运算(如果上一层池化后的向量维度刚好等于分类数,也可以直接在上面设置激活函数为softmax吧?)</span></span><br><span class="line"><span class="comment">#查看模型</span></span><br><span class="line">model.summary()</span><br><span class="line">model.output_shape</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">model.compile(</span><br><span class="line"> optimizer=<span class="string">'adam'</span>,</span><br><span class="line"> loss=<span class="string">'sparse_categorical_crossentropy'</span>,</span><br><span class="line"> metrics=[<span class="string">'acc'</span>])</span><br><span class="line"><span class="comment">#训练模型</span></span><br><span class="line">history=model.fit(train_images,</span><br><span class="line"> train_labels</span><br><span class="line"> epochs=<span class="number">30</span>,</span><br><span class="line"> validation_data=(test_images,test_labels)</span><br><span class="line"> )<span class="comment">#添加validation_data和validation_steps参数</span></span><br><span class="line"><span class="comment">#查看训练结果</span></span><br><span class="line">history.history.keys()</span><br><span class="line">plt.plot(history.epoch,history.history.get(<span class="string">'acc'</span>),label=<span class="string">'acc'</span>)</span><br><span class="line">plt.plot(history.epoch,history.history.get(<span class="string">'val_acc'</span>),label=<span class="string">'val_acc'</span>)</span><br><span class="line"></span><br><span class="line">plt.plot(history.epoch,history.history.get(<span class="string">'loss'</span>),label=<span class="string">'loss'</span>)</span><br><span class="line">plt.plot(history.epoch,history.history.get(<span class="string">'val_loss'</span>),label=<span class="string">'val_loss'</span>)<span class="comment">#发现过拟合现象:训练集上表现好,测试集上表现差</span></span><br><span class="line"> <span class="comment">#同时也有欠拟合现象:在训练集上的表现也并不出色</span></span><br></pre></td></tr></table></figure>
<h3 id="CNN的优化"><a href="#CNN的优化" class="headerlink" title="CNN的优化"></a>CNN的优化</h3><p>上一个模型的拟合能力不足,因此需要增大模型复杂度,同时为了避免过拟合,需要添加dropout层,有个问题:<strong>dropout层控制的是哪些参数?</strong>是它前一层的参数吗?</p>
<figure class="highlight python"><table><tr><td class="code"><pre><span class="line"><span class="keyword">from</span> tensorflow <span class="keyword">import</span> keras</span><br><span class="line"><span class="keyword">import</span> matplotlib.pyplot <span class="keyword">as</span> plt</span><br><span class="line"><span class="keyword">import</span> numpy <span class="keyword">as</span> np</span><br><span class="line"></span><br><span class="line"><span class="comment">#加载数据集</span></span><br><span class="line">fashion_mnist=keras.datasets.fashion_mnist</span><br><span class="line">(train_images,train_labels),(test_images,test_labels)=fashion_mnist.load_data()</span><br><span class="line"><span class="comment">#增加图像维度(n,h,w,c),也可用reshape方法实现</span></span><br><span class="line">train_images=np.expand_dims(train_images,<span class="number">-1</span>)</span><br><span class="line">test_images=np.expand_dims(test_images,<span class="number">-1</span>)<span class="comment">#此时图片shape变成(28,28,1)</span></span><br><span class="line"></span><br><span class="line"><span class="comment">#定义模型(优化)</span></span><br><span class="line">model=tf.keras.Sequential()</span><br><span class="line">model.add(tf.keras.layers.Conv2D(<span class="number">64</span>,(<span class="number">3</span>,<span class="number">3</span>),</span><br><span class="line"> input_shape=(<span class="number">28</span>,<span class="number">28</span>,<span class="number">1</span>),</span><br><span class="line"> activation=<span class="string">'relu'</span>,</span><br><span class="line"> padding=<span class="string">'same'</span>))</span><br><span class="line">model.add(tf.keras.layers.Conv2D(<span class="number">64</span>,(<span class="number">3</span>,<span class="number">3</span>),</span><br><span class="line"> activation=<span class="string">'relu'</span>,</span><br><span class="line"> padding=<span class="string">'same'</span>))</span><br><span class="line">model.add(tf.keras.layers.MaxPool2D())<span class="comment">#默认2x2</span></span><br><span class="line">model.add(tf.keras.layers.Dropout(<span class="number">0.5</span>))</span><br><span class="line">model.add(tf.keras.layers.Conv2D(<span class="number">128</span>,(<span class="number">3</span>,<span class="number">3</span>),activation=<span class="string">'relu'</span>, padding=<span class="string">'same'</span>))<span class="comment">#每层卷积核个数按照2^n递增效果会比较好</span></span><br><span class="line">model.add(tf.keras.layers.Conv2D(<span class="number">128</span>,(<span class="number">3</span>,<span class="number">3</span>),activation=<span class="string">'relu'</span>, padding=<span class="string">'same'</span>))</span><br><span class="line">model.add(tf.keras.layers.MaxPool2D())<span class="comment">#默认2x2</span></span><br><span class="line">model.add(tf.keras.layers.Dropout(<span class="number">0.5</span>))</span><br><span class="line">model.add(tf.keras.layers.Conv2D(<span class="number">256</span>,(<span class="number">3</span>,<span class="number">3</span>),activation=<span class="string">'relu'</span>, padding=<span class="string">'same'</span>))</span><br><span class="line">model.add(tf.keras.layers.Conv2D(<span class="number">256</span>,(<span class="number">3</span>,<span class="number">3</span>),activation=<span class="string">'relu'</span>, padding=<span class="string">'same'</span>))</span><br><span class="line">model.add(tf.keras.layers.MaxPool2D())<span class="comment">#默认2x2</span></span><br><span class="line">model.add(tf.keras.layers.Dropout(<span class="number">0.5</span>))</span><br><span class="line">model.add(tf.keras.layers.Conv2D(<span class="number">512</span>,(<span class="number">3</span>,<span class="number">3</span>),activation=<span class="string">'relu'</span>, padding=<span class="string">'same'</span>))</span><br><span class="line">model.add(tf.keras.layers.Conv2D(<span class="number">512</span>,(<span class="number">3</span>,<span class="number">3</span>),activation=<span class="string">'relu'</span>, padding=<span class="string">'same'</span>))</span><br><span class="line">model.add(tf.keras.layers.Dropout(<span class="number">0.5</span>))</span><br><span class="line">model.add(tf.keras.layers.GlobalAveragePooling2D())<span class="comment">#用全局池化实现特征图扁平化(不同于全连接层的全局卷积)</span></span><br><span class="line">model.add(tf.keras.layers.Dense(<span class="number">256</span>,activation=<span class="string">'relu'</span>))</span><br><span class="line">model.add(tf.keras.layers.Dense(<span class="number">10</span>,activation=<span class="string">'softmax'</span>))<span class="comment">#对特征向量进行线性运算(如果上一层池化后的向量维度刚好等于分类数,也可以直接在上面设置激活函数为softmax吧?)</span></span><br><span class="line"></span><br><span class="line"><span class="comment">#查看模型</span></span><br><span class="line">model.summary()</span><br><span class="line">model.output_shape</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">model.compile(</span><br><span class="line"> optimizer=<span class="string">'adam'</span>,</span><br><span class="line"> loss=<span class="string">'sparse_categorical_crossentropy'</span>,</span><br><span class="line"> metrics=[<span class="string">'acc'</span>])</span><br><span class="line"><span class="comment">#训练模型</span></span><br><span class="line">history=model.fit(train_images,</span><br><span class="line"> train_labels</span><br><span class="line"> epochs=<span class="number">30</span>,</span><br><span class="line"> validation_data=(test_images,test_labels)</span><br><span class="line"> )<span class="comment">#添加validation_data和validation_steps参数</span></span><br><span class="line"><span class="comment">#查看训练结果</span></span><br><span class="line">history.history.keys()</span><br><span class="line">plt.plot(history.epoch,history.history.get(<span class="string">'acc'</span>),label=<span class="string">'acc'</span>)</span><br><span class="line">plt.plot(history.epoch,history.history.get(<span class="string">'val_acc'</span>),label=<span class="string">'val_acc'</span>)</span><br><span class="line"></span><br><span class="line">plt.plot(history.epoch,history.history.get(<span class="string">'loss'</span>),label=<span class="string">'loss'</span>)</span><br><span class="line">plt.plot(history.epoch,history.history.get(<span class="string">'val_loss'</span>),label=<span class="string">'val_loss'</span>)</span><br></pre></td></tr></table></figure>
<h3 id="基于CNN的卫星图像识别综合实例"><a href="#基于CNN的卫星图像识别综合实例" class="headerlink" title="基于CNN的卫星图像识别综合实例"></a>基于CNN的卫星图像识别综合实例</h3><p>一个二分类问题,识别卫星图像中是湖还是飞机</p>
<figure class="highlight python"><table><tr><td class="code"><pre><span class="line"><span class="keyword">import</span> tensorflow <span class="keyword">as</span> tf</span><br><span class="line"><span class="keyword">from</span> tensorflow <span class="keyword">import</span> keras</span><br><span class="line"><span class="keyword">import</span> matplotlib.pyplot <span class="keyword">as</span> plt</span><br><span class="line">%matplotlib inline</span><br><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> glob <span class="comment">#自带库,编写路径表达式获取文件</span></span><br><span class="line"><span class="keyword">import</span> random</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">all_image_path=glob.glob(<span class="string">'../datasets/*/*.jpg'</span>)<span class="comment">#字符串列表</span></span><br><span class="line">all_image_path[:<span class="number">5</span>]</span><br><span class="line">all_image_path[<span class="number">-5</span>:]</span><br><span class="line"><span class="comment">#乱序处理</span></span><br><span class="line">random.shuffle(all_image_path)</span><br><span class="line"><span class="comment">#打标签前的准备:以字典保存标签与索引的对应关系</span></span><br><span class="line">label_to_index={<span class="string">'airplane'</span>:<span class="number">0</span>,<span class="string">'lake'</span>:<span class="number">1</span>}</span><br><span class="line">index_to_label=dict((v,k) <span class="keyword">for</span> k,v <span class="keyword">in</span> label_to_index.items()) <span class="comment">#元组推导式+字典构造函数</span></span><br><span class="line"><span class="comment">#以'\\'分裂路径字符串,获得一个子字符串列表并取第二个子串,然后获得index</span></span><br><span class="line">all_labels=[label_to_index.get(img.split(<span class="string">'\\'</span>)[<span class="number">1</span>]) <span class="keyword">for</span> img <span class="keyword">in</span> all_image_path] </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"><span class="function"><span class="keyword">def</span> <span class="title">load_img</span><span class="params">(path)</span>:</span></span><br><span class="line"> <span class="comment">#读取图片(用tf.io.read_file()获取二进制格式)</span></span><br><span class="line"> img_raw=tf.io.read_file(path)</span><br><span class="line"> <span class="comment">#解码图片(用tf.image.decode_jpeg()获取tensor图片)</span></span><br><span class="line"> img_tensor=tf.image.decode_jpeg(img_raw,channels=<span class="number">3</span>)</span><br><span class="line"> <span class="comment">#约束图片尺寸(用tf.image.resize()获得统一尺寸)</span></span><br><span class="line"> img_tensor=tf.image.resize(img_tensor,[<span class="number">256</span>,<span class="number">256</span>])</span><br><span class="line"> <span class="comment">#转换数据类型</span></span><br><span class="line"> img_tensor=tf.cast(img_tensor,tf.float32)</span><br><span class="line"> <span class="comment">#数值归一化</span></span><br><span class="line"> img_tensor=img_tensor/<span class="number">255</span></span><br><span class="line"> <span class="keyword">return</span> img_tensor</span><br><span class="line"><span class="comment">#******随机选择一个图片路径来测试函数******</span></span><br><span class="line">i=random.choice(range(len(all_image_path)))</span><br><span class="line">img_path=all_image_path[i]</span><br><span class="line">label=all_labels[i]</span><br><span class="line">img_tensor=load_img(img_path)</span><br><span class="line">plt.title(index_to_label.get(label))</span><br><span class="line">plt.imshow(img_tensor.numpy())</span><br><span class="line"></span><br><span class="line"><span class="comment">########创建Dataset########</span></span><br><span class="line"><span class="comment">#将图片路径装进Dataset</span></span><br><span class="line">img_ds=tf.data.Dataset.from_tensor_slices(all_image_path)</span><br><span class="line"><span class="comment">#将Dataset中的每个路径,通过load_img函数映射为图片tensor</span></span><br><span class="line">img_ds=img_ds.map(load_img) <span class="comment">#Dataset的map方法</span></span><br><span class="line"><span class="comment">#将labels装进Dataset</span></span><br><span class="line">label_ds=tf.data.Dataset.from_tensor_slices(all_labels)</span><br><span class="line"><span class="comment">#******查看标签******</span></span><br><span class="line">label_ds <span class="comment">#由于label实际上是索引值,是标量,所以shapes:()</span></span><br><span class="line"><span class="keyword">for</span> la <span class="keyword">in</span> label_ds.take(<span class="number">10</span>):</span><br><span class="line"> print(index_to_label.get(la.numpy()))</span><br><span class="line"></span><br><span class="line"><span class="comment">########划分训练和测试Dataset########</span></span><br><span class="line"><span class="comment">#合并图片和标签Dataset</span></span><br><span class="line">img_label_ds=tf.data.Dataset.zip((img_ds,label_ds)) <span class="comment">#输入参数应为元组</span></span><br><span class="line"><span class="comment">#按比例划分</span></span><br><span class="line">image_count=len(all_image_path)</span><br><span class="line">test_count=int(image_count*<span class="number">0.2</span>)</span><br><span class="line">train_count=image_count-test_count</span><br><span class="line">train_ds=img_label_ds.skip(test_count)<span class="comment">#用Dataset的skip方法跳过前n个样本</span></span><br><span class="line">test_ds=img_label_ds.take(test_count)<span class="comment">#用Dataset的skip方法获取前n个样本</span></span><br><span class="line"><span class="comment">#设置train_ds在epoch中重复执行乱序</span></span><br><span class="line">BATCH_SIZE=<span class="number">16</span></span><br><span class="line">train_ds=train_ds.repeat().shuffle(<span class="number">100</span>).batch(BATCH_SIZE)</span><br><span class="line"><span class="comment">#设置test_ds的batch_size</span></span><br><span class="line">test_ds=test_ds.batch(BATCH_SIZE)</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">model=tf.keras.Sequential()</span><br><span class="line">model.add(tf.keras.layers.Conv2D(<span class="number">64</span>,(<span class="number">3</span>,<span class="number">3</span>),</span><br><span class="line"> input_shape=(<span class="number">256</span>,<span class="number">256</span>,<span class="number">3</span>),</span><br><span class="line"> activation=<span class="string">'relu'</span>))</span><br><span class="line">model.add(tf.keras.layers.Conv2D(<span class="number">64</span>,(<span class="number">3</span>,<span class="number">3</span>),</span><br><span class="line"> activation=<span class="string">'relu'</span>))</span><br><span class="line">model.add(tf.keras.layers.MaxPool2D())</span><br><span class="line">model.add(tf.keras.layers.Conv2D(<span class="number">128</span>,(<span class="number">3</span>,<span class="number">3</span>),</span><br><span class="line"> activation=<span class="string">'relu'</span>))</span><br><span class="line">model.add(tf.keras.layers.Conv2D(<span class="number">128</span>,(<span class="number">3</span>,<span class="number">3</span>),</span><br><span class="line"> activation=<span class="string">'relu'</span>))</span><br><span class="line">model.add(tf.keras.layers.MaxPool2D())</span><br><span class="line">model.add(tf.keras.layers.Conv2D(<span class="number">256</span>,(<span class="number">3</span>,<span class="number">3</span>),</span><br><span class="line"> activation=<span class="string">'relu'</span>))</span><br><span class="line">model.add(tf.keras.layers.Conv2D(<span class="number">256</span>,(<span class="number">3</span>,<span class="number">3</span>),</span><br><span class="line"> activation=<span class="string">'relu'</span>))</span><br><span class="line">model.add(tf.keras.layers.MaxPool2D())</span><br><span class="line">model.add(tf.keras.layers.Conv2D(<span class="number">512</span>,(<span class="number">3</span>,<span class="number">3</span>),</span><br><span class="line"> activation=<span class="string">'relu'</span>))</span><br><span class="line">model.add(tf.keras.layers.Conv2D(<span class="number">512</span>,(<span class="number">3</span>,<span class="number">3</span>),</span><br><span class="line"> activation=<span class="string">'relu'</span>))</span><br><span class="line">model.add(tf.keras.layers.MaxPool2D())</span><br><span class="line">model.add(tf.keras.layers.Conv2D(<span class="number">512</span>,(<span class="number">3</span>,<span class="number">3</span>),</span><br><span class="line"> activation=<span class="string">'relu'</span>))</span><br><span class="line">model.add(tf.keras.layers.Conv2D(<span class="number">512</span>,(<span class="number">3</span>,<span class="number">3</span>),</span><br><span class="line"> activation=<span class="string">'relu'</span>))</span><br><span class="line">model.add(tf.keras.layers.Conv2D(<span class="number">512</span>,(<span class="number">3</span>,<span class="number">3</span>),</span><br><span class="line"> activation=<span class="string">'relu'</span>))</span><br><span class="line"><span class="comment">#至此输出shape是:(batch, height, width, channels)</span></span><br><span class="line"><span class="comment">#接下来需要扁平化:最好用全局平均池化,比展平操作节省资源,也比全局卷积操作节省资源</span></span><br><span class="line">model.add(tf.keras.layers.GlobalAveragePooling2D())</span><br><span class="line">model.add(tf.keras.layers.Dense(<span class="number">1024</span>,activation=<span class="string">'relu'</span>))</span><br><span class="line">model.add(tf.keras.layers.Dense(<span class="number">256</span>,activation=<span class="string">'relu'</span>))</span><br><span class="line"><span class="comment">#二分类直接输出一个值用sigmoid激活函数处理即可,不过损失函数应该怎么写呢?二元交叉熵?</span></span><br><span class="line">model.add(tf.keras.layers.Dense(<span class="number">1</span>,activation=<span class="string">'sigmoid'</span>))</span><br><span class="line"><span class="comment">#******查看模型参数******</span></span><br><span class="line">model.summary</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">model.compile(optimizer=tf.keras.optimizers.Adam(<span class="number">0.0001</span>),</span><br><span class="line"> loss=tf.keras.losses.BinaryCrossentropy(),</span><br><span class="line"> metrics=[<span class="string">'acc'</span>])</span><br><span class="line"><span class="comment">#训练模型</span></span><br><span class="line">steps_per_epochs=train_count//BATCH_SIZE <span class="comment">#以复合Dataset训练时,制定了repeat,因此要指定每一轮的迭代步数,才能控制循环停止</span></span><br><span class="line">val_steps=test_count//BATCH_SIZE</span><br><span class="line">history=model.fit(train_ds,epochs=<span class="number">10</span>,</span><br><span class="line"> steps_per_epochs=steps_per_epoch,</span><br><span class="line"> validation_data=test_ds,</span><br><span class="line"> validation_steps=val_steps)</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">history.history.keys()</span><br><span class="line">plt.plot(history.epoch,histoy.history.get(<span class="string">'acc'</span>),label=<span class="string">'acc'</span>)</span><br><span class="line">plt.plot(history.epoch,histoy.history.get(<span class="string">'val_acc'</span>),label=<span class="string">'val_acc'</span>)</span><br><span class="line">plt.legend()</span><br><span class="line">plt.plot(history.epoch,histoy.history.get(<span class="string">'loss'</span>),label=<span class="string">'loss'</span>)</span><br><span class="line">plt.plot(history.epoch,histoy.history.get(<span class="string">'val_loss'</span>),label=<span class="string">'val_loss'</span>)</span><br><span class="line">plt.legend()</span><br><span class="line"><span class="comment">#观察到loss最后都在减小或不变了,acc都在增大或不变了,说明最终模型较好</span></span><br><span class="line"><span class="comment">#将模型封装到一个函数中实施预测</span></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">pre_img</span><span class="params">(path)</span>:</span></span><br><span class="line"> <span class="comment">#加载图片准备输入模型进行预测</span></span><br><span class="line"> img=load_img(path)</span><br><span class="line"> <span class="comment">#由于模型的输入shape是:(batch, height, width, channels),因此需要扩展单一图片的维度,扩展后变成(1,h,w,c)</span></span><br><span class="line"> img=tf.expand_dims(img,axis=<span class="number">0</span>) <span class="comment">#在最开始的位置扩展维度,扩展后变成(1,h,w,c)</span></span><br><span class="line"> <span class="comment">#预测</span></span><br><span class="line"> pred=model.predict(img) <span class="comment">#输出值是个概率值,而且位于二维数组中</span></span><br><span class="line"> print(index_to_label.get((pred><span class="number">0.5</span>).astype(<span class="string">'int'</span>)[<span class="number">0</span>][<span class="number">0</span>]))</span><br><span class="line"><span class="comment">#预测</span></span><br><span class="line">pth=<span class="string">'../test/001.jpg'</span></span><br><span class="line">pre_img(pth)</span><br></pre></td></tr></table></figure>
<h4 id="一些问题:"><a href="#一些问题:" class="headerlink" title="一些问题:"></a>一些问题:</h4><ol>
<li><p>解码图片代码img_tensor=tf.image.decode_jpeg(img_raw,channels=3)中,如果指定错误的channels,会发生什么?</p>
</li>
<li><p>训练集乱序参数shuffle(100)的含义是什么?是乱序操作的缓存?会把所有样本都乱序吗?还是只有某100个?train_ds=train_ds.repeat().shuffle(100).batch(BATCH_SIZE)</p>
</li>
<li><p>train_ds.repeat()到底做了什么?是一个开关吗?设置只要读取train_ds就会重复某些操作?重复哪些操作?</p>
</li>
<li><p>为什么模型预测的输出值是个二维数组?</p>
</li>
<li><p>如何以一个批次进行预测?</p>
</li>
</ol>
<h3 id="批标准化介绍"><a href="#批标准化介绍" class="headerlink" title="批标准化介绍"></a>批标准化介绍</h3><p>标准化也叫归一化,一般是将数据映射到指定范围,用于取出不同维度数据的量纲以及量纲单位。</p>
<p>数据标准化让模型看到的不同样本更加相似,有助于模型的学习和对新数据的泛化。</p>
<h4 id="常见的数据标准化方法"><a href="#常见的数据标准化方法" class="headerlink" title="常见的数据标准化方法"></a>常见的数据标准化方法</h4><p>标准化:将数据减去其均值使其中心(均值)为0,然后将数据除以其标准差使其标准差为1。</p>
<p>归一化:映射到[0,1]</p>
<p><strong>批标准化(Batch Normalization):</strong>不仅在将数据输入模型之前对数据做标准化,在网络的每一层之前都应该考虑数据标准化。这样即使在训练时均值和方差随时间发生变化,也能适应性地将数据标准化。</p>
<h4 id="为什么要做批标准化(BN)"><a href="#为什么要做批标准化(BN)" class="headerlink" title="为什么要做批标准化(BN)"></a>为什么要做批标准化(BN)</h4><p>BN是一种训练优化方法,能解决梯度消失和梯度爆炸问题。这里的“梯度”不是指损失的梯度,而是模型输出关于输入的梯度(导数)。</p>
<p>BN最直接的好处是可以加快收敛速度,此外它还具有正则化效果(抑制过拟合),提高模型的泛化能力,允许更高的学习率从而加速收敛。</p>
<p>BN有助于梯度传播,因此允许更深的网络,对于有些极深的网络,BN甚至是必需的。(除了BN,残差也可以起到类似的作用)</p>
<h4 id="Tensorflow中的批标准化"><a href="#Tensorflow中的批标准化" class="headerlink" title="Tensorflow中的批标准化"></a>Tensorflow中的批标准化</h4><p>BN层通常被加在卷积层或密集全连接层之后。</p>
<p>tf.keras.layers.Batchnormalization()</p>
<h3 id="批标准化的使用"><a href="#批标准化的使用" class="headerlink" title="批标准化的使用"></a>批标准化的使用</h3><h4 id="批标准化的实现过程"><a href="#批标准化的实现过程" class="headerlink" title="批标准化的实现过程"></a>批标准化的实现过程</h4><ol>
<li>求每个Batch数据的均值</li>
<li>求每个Batch数据的方差</li>
<li>对Batch数据进行标准化</li>
<li>训练参数γ,β</li>
<li>输出y通过参数γ,β的线性变换得到原来的数值</li>
</ol>
<p>在训练的正向传播中,<strong>不会改变当前输出?</strong>,只记录下γ和β。在反向传播的时候,根据求得的y与γ,β,通过链式求导方式,求出学习速率以至改变权值。</p>
<p>预测阶段使用的均值和方差,来自于整个训练集。训练的每个Batch的均值和方差都被记录下来,训练完毕时就可计算出整个训练集的均值和方差。</p>
<p><strong>Tensorflow的批标准化存在两种模式</strong>:训练模式和推理模式,由布尔参数training控制。</p>
<h4 id="批标准化的添加位置"><a href="#批标准化的添加位置" class="headerlink" title="批标准化的添加位置"></a>批标准化的添加位置</h4><p>论文中讲到一般把BN放在<strong>XX层之后,非线性激活函数之前</strong>,但<strong>实际上把BN层放在激活函数之后效果可能更好</strong>。也就是BN层必须紧邻激活函数,可以在前也可以在后。</p>
<h4 id="代码实现"><a href="#代码实现" class="headerlink" title="代码实现"></a>代码实现</h4><p>在卫星图片识别模型中添加BN层,为了在激活函数之前添加BN层,在添加普通层时不指定激活函数,而在添加BN层之后再手动添加专门的激活层。</p>
<figure class="highlight python"><table><tr><td class="code"><pre><span class="line"><span class="keyword">import</span> tensorflow <span class="keyword">as</span> tf</span><br><span class="line"><span class="keyword">from</span> tensorflow <span class="keyword">import</span> keras</span><br><span class="line"><span class="keyword">import</span> matplotlib.pyplot <span class="keyword">as</span> plt</span><br><span class="line">%matplotlib inline</span><br><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> glob <span class="comment">#自带库,编写路径表达式获取文件</span></span><br><span class="line"><span class="keyword">import</span> random</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">all_image_path=glob.glob(<span class="string">'../datasets/*/*.jpg'</span>)<span class="comment">#字符串列表</span></span><br><span class="line">all_image_path[:<span class="number">5</span>]</span><br><span class="line">all_image_path[<span class="number">-5</span>:]</span><br><span class="line"><span class="comment">#乱序处理</span></span><br><span class="line">random.shuffle(all_image_path)</span><br><span class="line"><span class="comment">#打标签前的准备:以字典保存标签与索引的对应关系</span></span><br><span class="line">label_to_index={<span class="string">'airplane'</span>:<span class="number">0</span>,<span class="string">'lake'</span>:<span class="number">1</span>}</span><br><span class="line">index_to_label=dict((v,k) <span class="keyword">for</span> k,v <span class="keyword">in</span> label_to_index.items())</span><br><span class="line"><span class="comment">#以'\\'分裂路径字符串,获得一个子字符串列表并取第二个子串,然后获得index</span></span><br><span class="line">all_labels=[label_to_index.get(img.split(<span class="string">'\\'</span>)[<span class="number">1</span>]) <span class="keyword">for</span> img <span class="keyword">in</span> all_image_path] </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"><span class="function"><span class="keyword">def</span> <span class="title">load_img</span><span class="params">(path)</span>:</span></span><br><span class="line"> <span class="comment">#读取图片(用tf.io.read_file()获取二进制格式)</span></span><br><span class="line"> img_raw=tf.io.read_file(path)</span><br><span class="line"> <span class="comment">#解码图片(用tf.image.decode_jpeg()获取tensor图片)</span></span><br><span class="line"> img_tensor=tf.image.decode_jpeg(img_raw,channels=<span class="number">3</span>)</span><br><span class="line"> <span class="comment">#约束图片尺寸(用tf.image.resize()获得统一尺寸)</span></span><br><span class="line"> img_tensor=tf.image.resize(img_tensor,[<span class="number">256</span>,<span class="number">256</span>])</span><br><span class="line"> <span class="comment">#转换数据类型</span></span><br><span class="line"> img_tensor=tf.cast(img_tensor,tf.float32)</span><br><span class="line"> <span class="comment">#数值归一化</span></span><br><span class="line"> img_tensor=img_tensor/<span class="number">255</span></span><br><span class="line"> <span class="keyword">return</span> img_tensor</span><br><span class="line"><span class="comment">#******随机选择一个图片路径来测试函数******</span></span><br><span class="line">i=random.choice(range(len(all_image_path)))</span><br><span class="line">img_path=all_image_path[i]</span><br><span class="line">label=all_labels[i]</span><br><span class="line">img_tensor=load_img(img_path)</span><br><span class="line">plt.title(index_to_label.get(label))</span><br><span class="line">plt.imshow(img_tensor.numpy())</span><br><span class="line"></span><br><span class="line"><span class="comment">########创建Dataset########</span></span><br><span class="line"><span class="comment">#将图片路径装进Dataset</span></span><br><span class="line">img_ds=tf.data.Dataset.from_tensor_slices(all_image_path)</span><br><span class="line"><span class="comment">#将Dataset中的每个路径,通过load_img函数映射为图片tensor</span></span><br><span class="line">img_ds=img_ds.map(load_img) <span class="comment">#Dataset的map方法</span></span><br><span class="line"><span class="comment">#将labels装进Dataset</span></span><br><span class="line">label_ds=tf.data.Dataset.from_tensor_slices(all_labels)</span><br><span class="line"><span class="comment">#******查看标签******</span></span><br><span class="line">label_ds <span class="comment">#由于label实际上是索引值,是标量,所以shapes:()</span></span><br><span class="line"><span class="keyword">for</span> la <span class="keyword">in</span> label_ds.take(<span class="number">10</span>):</span><br><span class="line"> print(index_to_label.get(la.numpy()))</span><br><span class="line"></span><br><span class="line"><span class="comment">########划分训练和测试Dataset########</span></span><br><span class="line"><span class="comment">#合并图片和标签Dataset</span></span><br><span class="line">img_label_ds=tf.data.Dataset.zip((img_ds,label_ds)) <span class="comment">#输入参数应为元组</span></span><br><span class="line"><span class="comment">#按比例划分</span></span><br><span class="line">image_count=len(all_image_path)</span><br><span class="line">test_count=int(image_count*<span class="number">0.2</span>)</span><br><span class="line">train_count=image_count-test_count</span><br><span class="line">train_ds=img_label_ds.skip(test_count)<span class="comment">#用Dataset的skip方法跳过前n个样本</span></span><br><span class="line">test_ds=img_label_ds.take(test_count)<span class="comment">#用Dataset的skip方法获取前n个样本</span></span><br><span class="line"><span class="comment">#设置train_ds在epoch中重复执行乱序</span></span><br><span class="line">BATCH_SIZE=<span class="number">16</span></span><br><span class="line">train_ds=train_ds.repeat().shuffle(<span class="number">100</span>).batch(BATCH_SIZE)</span><br><span class="line"><span class="comment">#设置test_ds的batch_size</span></span><br><span class="line">test_ds=test_ds.batch(BATCH_SIZE)</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">model=tf.keras.Sequential()</span><br><span class="line">model.add(tf.keras.layers.Conv2D(<span class="number">64</span>,(<span class="number">3</span>,<span class="number">3</span>),</span><br><span class="line"> input_shape=(<span class="number">256</span>,<span class="number">256</span>,<span class="number">3</span>)))</span><br><span class="line">model.add(tf.keras.layers.Batchnormalization())</span><br><span class="line">model.add(tf.keras.layers.Activation(<span class="string">'relu'</span>))</span><br><span class="line">model.add(tf.keras.layers.Conv2D(<span class="number">64</span>,(<span class="number">3</span>,<span class="number">3</span>)))</span><br><span class="line">model.add(tf.keras.layers.Batchnormalization())</span><br><span class="line">model.add(tf.keras.layers.Activation(<span class="string">'relu'</span>))</span><br><span class="line">model.add(tf.keras.layers.MaxPool2D())</span><br><span class="line">model.add(tf.keras.layers.Conv2D(<span class="number">128</span>,(<span class="number">3</span>,<span class="number">3</span>)))</span><br><span class="line">model.add(tf.keras.layers.Batchnormalization())</span><br><span class="line">model.add(tf.keras.layers.Activation(<span class="string">'relu'</span>))</span><br><span class="line">model.add(tf.keras.layers.Conv2D(<span class="number">128</span>,(<span class="number">3</span>,<span class="number">3</span>)))</span><br><span class="line">model.add(tf.keras.layers.Batchnormalization())</span><br><span class="line">model.add(tf.keras.layers.Activation(<span class="string">'relu'</span>))</span><br><span class="line">model.add(tf.keras.layers.MaxPool2D())</span><br><span class="line">model.add(tf.keras.layers.Conv2D(<span class="number">256</span>,(<span class="number">3</span>,<span class="number">3</span>)))</span><br><span class="line">model.add(tf.keras.layers.Batchnormalization())</span><br><span class="line">model.add(tf.keras.layers.Activation(<span class="string">'relu'</span>))</span><br><span class="line">model.add(tf.keras.layers.Conv2D(<span class="number">256</span>,(<span class="number">3</span>,<span class="number">3</span>)))</span><br><span class="line">model.add(tf.keras.layers.Batchnormalization())</span><br><span class="line">model.add(tf.keras.layers.Activation(<span class="string">'relu'</span>))</span><br><span class="line">model.add(tf.keras.layers.MaxPool2D())</span><br><span class="line">model.add(tf.keras.layers.Conv2D(<span class="number">512</span>,(<span class="number">3</span>,<span class="number">3</span>)))</span><br><span class="line">model.add(tf.keras.layers.Batchnormalization())</span><br><span class="line">model.add(tf.keras.layers.Activation(<span class="string">'relu'</span>))</span><br><span class="line">model.add(tf.keras.layers.Conv2D(<span class="number">512</span>,(<span class="number">3</span>,<span class="number">3</span>)))</span><br><span class="line">model.add(tf.keras.layers.Batchnormalization())</span><br><span class="line">model.add(tf.keras.layers.Activation(<span class="string">'relu'</span>))</span><br><span class="line">model.add(tf.keras.layers.MaxPool2D())</span><br><span class="line">model.add(tf.keras.layers.Conv2D(<span class="number">512</span>,(<span class="number">3</span>,<span class="number">3</span>)))</span><br><span class="line">model.add(tf.keras.layers.Batchnormalization())</span><br><span class="line">model.add(tf.keras.layers.Activation(<span class="string">'relu'</span>))</span><br><span class="line">model.add(tf.keras.layers.Conv2D(<span class="number">512</span>,(<span class="number">3</span>,<span class="number">3</span>)))</span><br><span class="line">model.add(tf.keras.layers.Batchnormalization())</span><br><span class="line">model.add(tf.keras.layers.Activation(<span class="string">'relu'</span>))</span><br><span class="line">model.add(tf.keras.layers.Conv2D(<span class="number">512</span>,(<span class="number">3</span>,<span class="number">3</span>)))</span><br><span class="line">model.add(tf.keras.layers.Batchnormalization())</span><br><span class="line">model.add(tf.keras.layers.Activation(<span class="string">'relu'</span>))</span><br><span class="line"><span class="comment">#至此输出shape是:(batch, height, width, channels)</span></span><br><span class="line"><span class="comment">#接下来需要扁平化:最好用全局平均池化,比展平操作节省资源,也比全局卷积操作节省资源</span></span><br><span class="line">model.add(tf.keras.layers.GlobalAveragePooling2D())</span><br><span class="line">model.add(tf.keras.layers.Dense(<span class="number">1024</span>))</span><br><span class="line">model.add(tf.keras.layers.Batchnormalization())</span><br><span class="line">model.add(tf.keras.layers.Activation(<span class="string">'relu'</span>))</span><br><span class="line">model.add(tf.keras.layers.Dense(<span class="number">256</span>))</span><br><span class="line">model.add(tf.keras.layers.Batchnormalization())</span><br><span class="line">model.add(tf.keras.layers.Activation(<span class="string">'relu'</span>))</span><br><span class="line"><span class="comment">#二分类直接输出一个值用sigmoid激活函数处理即可,不过损失函数应该怎么写呢?二元交叉熵?</span></span><br><span class="line">model.add(tf.keras.layers.Dense(<span class="number">1</span>,activation=<span class="string">'sigmoid'</span>))</span><br><span class="line"><span class="comment">#******查看模型参数******</span></span><br><span class="line">model.summary</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">model.compile(optimizer=tf.keras.optimizers.Adam(<span class="number">0.0001</span>),</span><br><span class="line"> loss=tf.keras.losses.BinaryCrossentropy(),</span><br><span class="line"> metrics=[<span class="string">'acc'</span>])</span><br><span class="line"><span class="comment">#训练模型</span></span><br><span class="line">steps_per_epochs=train_count//BATCH_SIZE <span class="comment">#以复合Dataset训练时,制定了repeat,因此要指定每一轮的迭代步数,才能控制循环停止</span></span><br><span class="line">val_steps=test_count//BATCH_SIZE</span><br><span class="line">history=model.fit(train_ds,epochs=<span class="number">10</span>,</span><br><span class="line"> steps_per_epochs=steps_per_epoch,</span><br><span class="line"> validation_data=test_ds,</span><br><span class="line"> validation_steps=val_steps)</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">history.history.keys()</span><br><span class="line">plt.plot(history.epoch,histoy.history.get(<span class="string">'acc'</span>),label=<span class="string">'acc'</span>)</span><br><span class="line">plt.plot(history.epoch,histoy.history.get(<span class="string">'val_acc'</span>),label=<span class="string">'val_acc'</span>)</span><br><span class="line">plt.legend()</span><br><span class="line">plt.plot(history.epoch,histoy.history.get(<span class="string">'loss'</span>),label=<span class="string">'loss'</span>)</span><br><span class="line">plt.plot(history.epoch,histoy.history.get(<span class="string">'val_loss'</span>),label=<span class="string">'val_loss'</span>)</span><br><span class="line">plt.legend()</span><br><span class="line"><span class="comment">#观察到loss最后都在减小或不变了,acc都在增大或不变了,说明最终模型较好</span></span><br><span class="line"><span class="comment">#将模型封装到一个函数中实施预测</span></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">pre_img</span><span class="params">(path)</span>:</span></span><br><span class="line"> <span class="comment">#加载图片准备输入模型进行预测</span></span><br><span class="line"> img=load_img(path)</span><br><span class="line"> <span class="comment">#由于模型的输入shape是:(batch, height, width, channels),因此需要扩展单一图片的维度,扩展后变成(1,h,w,c)</span></span><br><span class="line"> img=tf.expand_dims(img,axis=<span class="number">0</span>) <span class="comment">#在最开始的位置扩展维度,扩展后变成(1,h,w,c)</span></span><br><span class="line"> <span class="comment">#预测</span></span><br><span class="line"> pred=model.predict(img) <span class="comment">#输出值是个概率值,而且位于二维数组中</span></span><br><span class="line"> print(index_to_label.get((pred><span class="number">0.5</span>).astype(<span class="string">'int'</span>)[<span class="number">0</span>][<span class="number">0</span>]))</span><br><span class="line"><span class="comment">#预测</span></span><br><span class="line">pth=<span class="string">'../test/001.jpg'</span></span><br><span class="line">pre_img(pth)</span><br></pre></td></tr></table></figure>
<h3 id="200种鸟类图片分类实例"><a href="#200种鸟类图片分类实例" class="headerlink" title="200种鸟类图片分类实例"></a>200种鸟类图片分类实例</h3><p>通过修改上次识别卫星图片的代码实现</p>
<figure class="highlight python"><table><tr><td class="code"><pre><span class="line"><span class="keyword">import</span> tensorflow <span class="keyword">as</span> tf</span><br><span class="line"><span class="keyword">from</span> tensorflow <span class="keyword">import</span> keras</span><br><span class="line"><span class="keyword">import</span> matplotlib.pyplot <span class="keyword">as</span> plt</span><br><span class="line">%matplotlib inline</span><br><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> glob <span class="comment">#自带库,编写路径表达式获取文件</span></span><br><span class="line"><span class="keyword">import</span> random</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">all_image_path=glob.glob(<span class="string">'../datasets/birds/*/*.jpg'</span>)<span class="comment">#字符串列表</span></span><br><span class="line">all_image_path[:<span class="number">5</span>]</span><br><span class="line">all_image_path[<span class="number">-5</span>:]</span><br><span class="line"><span class="comment">#乱序处理(本次采用打标签以后再乱序的做法)</span></span><br><span class="line"><span class="comment">#random.shuffle(all_image_path)</span></span><br><span class="line"><span class="comment">#打标签</span></span><br><span class="line"><span class="comment">#以'\\'分裂路径字符串,获得一个子字符串列表并取第二个子串(文件夹名),然后,再用'.'分裂获得鸟类名称</span></span><br><span class="line">all_labels_name=[img.split(<span class="string">'\\'</span>)[<span class="number">1</span>].split(<span class="string">'.'</span>)[<span class="number">1</span>] <span class="keyword">for</span> img <span class="keyword">in</span> all_image_path] <span class="comment">#列表推导式</span></span><br><span class="line">label_names=np.unique(all_labels_name)<span class="comment">#获取所有名称(200个)</span></span><br><span class="line">label_to_index=dict((name,i) </span><br><span class="line"> <span class="keyword">for</span> i,name <span class="keyword">in</span> enumerate(label_names))<span class="comment">#enumerate函数可以获得(idx,val)列表</span></span><br><span class="line">index_to_label=dict((v,k) <span class="keyword">for</span> k,v <span class="keyword">in</span> label_to_index.items())</span><br><span class="line"><span class="comment">#通过字典映射获得每个图片对应的标签index</span></span><br><span class="line">all_labels=[label_to_index.get(name) <span class="keyword">for</span> name <span class="keyword">in</span> all_labels_name] </span><br><span class="line"><span class="comment">#乱序处理(基于一个随机索引列表乱序处理)</span></span><br><span class="line">np.random.seed(<span class="number">2021</span>)</span><br><span class="line">random_index=no.random.permutation(len(all_image_path))</span><br><span class="line">all_image_path=np.array(all_image_path)[random_index]</span><br><span class="line">all_labels=np.array(all_labels)[random_index]</span><br><span class="line"></span><br><span class="line"><span class="comment">########创建Dataset并读取图片########</span></span><br><span class="line"><span class="comment">#先按比例划分</span></span><br><span class="line">i=int(len(all_image_path)*<span class="number">0.8</span>)</span><br><span class="line">train_path=all_image_path[:i]</span><br><span class="line">train_labels=all_labels[:i]</span><br><span class="line">test_path=all_image_path[i:]</span><br><span class="line">test_labels=all_labels[i:]</span><br><span class="line"><span class="comment">#再创建Dataset</span></span><br><span class="line">train_ds=tf.data.Dataset.from_tensor_slices((train_path,train_labels))</span><br><span class="line">test_ds=tf.data.Dataset.from_tensor_slices((test_path,test_labels))</span><br><span class="line"><span class="comment">#定义一个函数,从成对的Dataset的元素中读取图片(处理上一步的Dataset的元素)</span></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">load_img</span><span class="params">(path,label)</span>:</span></span><br><span class="line"> <span class="comment">#读取图片(用tf.io.read_file()获取二进制格式)</span></span><br><span class="line"> img_raw=tf.io.read_file(path)</span><br><span class="line"> <span class="comment">#解码图片(用tf.image.decode_jpeg()获取tensor图片)</span></span><br><span class="line"> img_tensor=tf.image.decode_jpeg(img_raw,channels=<span class="number">3</span>)</span><br><span class="line"> <span class="comment">#约束图片尺寸(用tf.image.resize()获得统一尺寸)</span></span><br><span class="line"> img_tensor=tf.image.resize(img_tensor,[<span class="number">256</span>,<span class="number">256</span>])</span><br><span class="line"> <span class="comment">#转换数据类型</span></span><br><span class="line"> img_tensor=tf.cast(img_tensor,tf.float32)</span><br><span class="line"> <span class="comment">#数值归一化</span></span><br><span class="line"> img_tensor=img_tensor/<span class="number">255</span></span><br><span class="line"> <span class="keyword">return</span> img_tensor,label</span><br><span class="line"><span class="comment">#用上面的load_img函数映射Dataset的元素</span></span><br><span class="line">AUTOTUNE=tf.data.experimental.AUTOTUNE<span class="comment">#用于设置映射操作的线程数</span></span><br><span class="line">train_ds=train_ds.map(load_img,num_parallel_calls=AUTOTUNE)</span><br><span class="line"><span class="comment">#设置train_ds在epoch中重复执行乱序</span></span><br><span class="line">BATCH_SIZE=<span class="number">32</span></span><br><span class="line">train_ds=train_ds.repeat().shuffle(<span class="number">100</span>).batch(BATCH_SIZE)</span><br><span class="line"><span class="comment">#设置test_ds的batch_size</span></span><br><span class="line">test_ds=test_ds.batch(BATCH_SIZE)</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">model=tf.keras.Sequential()</span><br><span class="line">model.add(tf.keras.layers.Conv2D(<span class="number">64</span>,(<span class="number">3</span>,<span class="number">3</span>),</span><br><span class="line"> input_shape=(<span class="number">256</span>,<span class="number">256</span>,<span class="number">3</span>)))</span><br><span class="line">model.add(tf.keras.layers.Batchnormalization())</span><br><span class="line">model.add(tf.keras.layers.Activation(<span class="string">'relu'</span>))</span><br><span class="line">model.add(tf.keras.layers.Conv2D(<span class="number">64</span>,(<span class="number">3</span>,<span class="number">3</span>)))</span><br><span class="line">model.add(tf.keras.layers.Batchnormalization())</span><br><span class="line">model.add(tf.keras.layers.Activation(<span class="string">'relu'</span>))</span><br><span class="line">model.add(tf.keras.layers.MaxPool2D())</span><br><span class="line">model.add(tf.keras.layers.Conv2D(<span class="number">128</span>,(<span class="number">3</span>,<span class="number">3</span>)))</span><br><span class="line">model.add(tf.keras.layers.Batchnormalization())</span><br><span class="line">model.add(tf.keras.layers.Activation(<span class="string">'relu'</span>))</span><br><span class="line">model.add(tf.keras.layers.Conv2D(<span class="number">128</span>,(<span class="number">3</span>,<span class="number">3</span>)))</span><br><span class="line">model.add(tf.keras.layers.Batchnormalization())</span><br><span class="line">model.add(tf.keras.layers.Activation(<span class="string">'relu'</span>))</span><br><span class="line">model.add(tf.keras.layers.MaxPool2D())</span><br><span class="line">model.add(tf.keras.layers.Conv2D(<span class="number">256</span>,(<span class="number">3</span>,<span class="number">3</span>)))</span><br><span class="line">model.add(tf.keras.layers.Batchnormalization())</span><br><span class="line">model.add(tf.keras.layers.Activation(<span class="string">'relu'</span>))</span><br><span class="line">model.add(tf.keras.layers.Conv2D(<span class="number">256</span>,(<span class="number">3</span>,<span class="number">3</span>)))</span><br><span class="line">model.add(tf.keras.layers.Batchnormalization())</span><br><span class="line">model.add(tf.keras.layers.Activation(<span class="string">'relu'</span>))</span><br><span class="line">model.add(tf.keras.layers.MaxPool2D())</span><br><span class="line">model.add(tf.keras.layers.Conv2D(<span class="number">512</span>,(<span class="number">3</span>,<span class="number">3</span>)))</span><br><span class="line">model.add(tf.keras.layers.Batchnormalization())</span><br><span class="line">model.add(tf.keras.layers.Activation(<span class="string">'relu'</span>))</span><br><span class="line">model.add(tf.keras.layers.Conv2D(<span class="number">512</span>,(<span class="number">3</span>,<span class="number">3</span>)))</span><br><span class="line">model.add(tf.keras.layers.Batchnormalization())</span><br><span class="line">model.add(tf.keras.layers.Activation(<span class="string">'relu'</span>))</span><br><span class="line">model.add(tf.keras.layers.MaxPool2D())</span><br><span class="line">model.add(tf.keras.layers.Conv2D(<span class="number">512</span>,(<span class="number">3</span>,<span class="number">3</span>)))</span><br><span class="line">model.add(tf.keras.layers.Batchnormalization())</span><br><span class="line">model.add(tf.keras.layers.Activation(<span class="string">'relu'</span>))</span><br><span class="line">model.add(tf.keras.layers.Conv2D(<span class="number">512</span>,(<span class="number">3</span>,<span class="number">3</span>)))</span><br><span class="line">model.add(tf.keras.layers.Batchnormalization())</span><br><span class="line">model.add(tf.keras.layers.Activation(<span class="string">'relu'</span>))</span><br><span class="line">model.add(tf.keras.layers.Conv2D(<span class="number">512</span>,(<span class="number">3</span>,<span class="number">3</span>)))</span><br><span class="line">model.add(tf.keras.layers.Batchnormalization())</span><br><span class="line">model.add(tf.keras.layers.Activation(<span class="string">'relu'</span>))</span><br><span class="line"><span class="comment">#至此输出shape是:(batch, height, width, channels)</span></span><br><span class="line"><span class="comment">#接下来需要扁平化:最好用全局平均池化,比展平操作节省资源,也比全局卷积操作节省资源</span></span><br><span class="line">model.add(tf.keras.layers.GlobalAveragePooling2D())</span><br><span class="line">model.add(tf.keras.layers.Dense(<span class="number">1024</span>))</span><br><span class="line">model.add(tf.keras.layers.Batchnormalization())</span><br><span class="line">model.add(tf.keras.layers.Activation(<span class="string">'relu'</span>))</span><br><span class="line">model.add(tf.keras.layers.Dense(<span class="number">256</span>))</span><br><span class="line">model.add(tf.keras.layers.Batchnormalization())</span><br><span class="line">model.add(tf.keras.layers.Activation(<span class="string">'relu'</span>))</span><br><span class="line"><span class="comment">#多分类问题最后的Dense层也可以不指定activation='softmax',此时的输出为logits,可以把softmax操作交给损失函数处理,这时整个计算会更高效稳定。</span></span><br><span class="line">model.add(tf.keras.layers.Dense(<span class="number">200</span>))</span><br><span class="line"><span class="comment">#******查看模型参数******</span></span><br><span class="line">model.summary</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">model.compile(optimizer=tf.keras.optimizers.Adam(<span class="number">0.0001</span>),</span><br><span class="line"> loss=tf.keras.losses.SparseCategoricalCrossentropy(from_logits=<span class="literal">True</span>),<span class="comment">#因输出层没有指定activation='softmax'</span></span><br><span class="line"> metrics=[<span class="string">'acc'</span>])</span><br><span class="line"><span class="comment">#训练模型</span></span><br><span class="line">train_count=len(train_path)</span><br><span class="line">test_count=len(test_path)</span><br><span class="line">steps_per_epochs=train_count//BATCH_SIZE <span class="comment">#以复合Dataset训练时,制定了repeat,因此要指定每一轮的迭代步数,才能控制循环停止</span></span><br><span class="line">val_steps=test_count//BATCH_SIZE</span><br><span class="line">history=model.fit(train_ds,epochs=<span class="number">10</span>,</span><br><span class="line"> steps_per_epochs=steps_per_epoch,</span><br><span class="line"> validation_data=test_ds,</span><br><span class="line"> validation_steps=val_steps)</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">history.history.keys()</span><br><span class="line">plt.plot(history.epoch,history.history.get(<span class="string">'acc'</span>),label=<span class="string">'acc'</span>)</span><br><span class="line">plt.plot(history.epoch,history.history.get(<span class="string">'val_acc'</span>),label=<span class="string">'val_acc'</span>)</span><br><span class="line">plt.legend()</span><br><span class="line">plt.plot(history.epoch,history.history.get(<span class="string">'loss'</span>),label=<span class="string">'loss'</span>)</span><br><span class="line">plt.plot(history.epoch,history.history.get(<span class="string">'val_loss'</span>),label=<span class="string">'val_loss'</span>)</span><br><span class="line">plt.legend()</span><br><span class="line"><span class="comment">#观察训练曲线要关注:是否过拟合?是否训练次数不足?</span></span><br><span class="line"><span class="comment">#观察到loss最后都在减小或不变了,acc都在增大或不变了,说明最终模型较好</span></span><br><span class="line"></span><br><span class="line"><span class="comment">#定义一个普通的图片加载函数</span></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">load_img_new</span><span class="params">(path)</span>:</span></span><br><span class="line"> <span class="comment">#读取图片(用tf.io.read_file()获取二进制格式)</span></span><br><span class="line"> img_raw=tf.io.read_file(path)</span><br><span class="line"> <span class="comment">#解码图片(用tf.image.decode_jpeg()获取tensor图片)</span></span><br><span class="line"> img_tensor=tf.image.decode_jpeg(img_raw,channels=<span class="number">3</span>)</span><br><span class="line"> <span class="comment">#约束图片尺寸(用tf.image.resize()获得统一尺寸)</span></span><br><span class="line"> img_tensor=tf.image.resize(img_tensor,[<span class="number">256</span>,<span class="number">256</span>])</span><br><span class="line"> <span class="comment">#转换数据类型</span></span><br><span class="line"> img_tensor=tf.cast(img_tensor,tf.float32)</span><br><span class="line"> <span class="comment">#数值归一化</span></span><br><span class="line"> img_tensor=img_tensor/<span class="number">255</span></span><br><span class="line"> <span class="keyword">return</span> img_tensor</span><br><span class="line"><span class="comment">#将模型封装到一个函数中实施预测</span></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">pre_img</span><span class="params">(path)</span>:</span></span><br><span class="line"> <span class="comment">#加载图片准备输入模型进行预测</span></span><br><span class="line"> img=load_img_new(path)</span><br><span class="line"> <span class="comment">#由于模型的输入shape是:(batch, height, width, channels),因此需要扩展单一图片的维度,扩展后变成(1,h,w,c)</span></span><br><span class="line"> img=tf.expand_dims(img,axis=<span class="number">0</span>) <span class="comment">#在最开始的位置扩展维度,扩展后变成(1,h,w,c)</span></span><br><span class="line"> <span class="comment">#预测</span></span><br><span class="line"> pred=model.predict(img) <span class="comment">#输出值是个长度200的张量(ndarray)</span></span><br><span class="line"> name=index_to_label.get(np.argmax(pred))<span class="comment">#找到最大值的下标并查字典</span></span><br><span class="line"> print(name)</span><br><span class="line"><span class="comment">#预测</span></span><br><span class="line">pth=<span class="string">'../test/001.jpg'</span></span><br><span class="line">pre_img(pth)</span><br></pre></td></tr></table></figure>
<h4 id="一些问题:-1"><a href="#一些问题:-1" class="headerlink" title="一些问题:"></a>一些问题:</h4><ol>
<li>Dataset的map方法是立刻执行映射吗?也就是直接把数据集加载进内存了吗?估计不是,否则动辄上G的图片量一下子加载进内存岂不完蛋?难道它会记录下所有的map操作,然后等到程序使用当前元素或者其所在Batch时再执行map操作?</li>
<li>loss=tf.keras.losses.SparseCategoricalCrossentropy(from_logits=True)对于这种情况下的输出,是没有经过softmax函数处理的logits,也就不能够算是分布概率,所以从输出结果只能推断出分类结果,要想知道概率,还需要softmax处理?</li>
</ol>
<h3 id="tf-keras-序列问题-电影评论数据分类"><a href="#tf-keras-序列问题-电影评论数据分类" class="headerlink" title="tf.keras 序列问题-电影评论数据分类"></a>tf.keras 序列问题-电影评论数据分类</h3><p>先用一般的全连接神经网络处理,后期用LSTM网络处理。</p>
<p>文本处理一般有三种方式:密集向量(最好)、k-hot向量、RDF(编码+权重)</p>
<p>下面的程序将采用“<strong>把文本训练成密集向量</strong>”的方法,keras已经提供了相应的层Embedding。</p>
<figure class="highlight python"><table><tr><td class="code"><pre><span class="line"><span class="keyword">import</span> tensorflow <span class="keyword">as</span> tf</span><br><span class="line"><span class="keyword">from</span> tensorflow <span class="keyword">import</span> keras</span><br><span class="line"><span class="keyword">from</span> tensorflow.keras <span class="keyword">import</span> layers</span><br><span class="line"><span class="keyword">import</span> matplotlib.pyplot <span class="keyword">as</span> plt</span><br><span class="line">%matplotlib inline</span><br><span class="line"></span><br><span class="line"><span class="comment">#加载数据集</span></span><br><span class="line">data=keras.datasets.imdb</span><br><span class="line">max_word=<span class="number">10000</span></span><br><span class="line">(x_train,y_train),(x_test,y_test)=data.losd_data(num_words=max_word)<span class="comment">#评论数据是一个整数列表,每个整数代表一个单词,整数的最大值被限制为10000</span></span><br><span class="line"><span class="comment">#序列预处理:截断或者填充序列,以统一长度</span></span><br><span class="line">x_train=keras.preprocessing.sequence.pad_sequences(x_train,<span class="number">300</span>)</span><br><span class="line">x_test=keras.preprocessing.sequence.pad_sequences(x_test,<span class="number">300</span>)</span><br><span class="line"><span class="comment">#定义模型</span></span><br><span class="line">model=keras.models.Sequential()</span><br><span class="line">model.add(layers.Embedding(<span class="number">10000</span>,<span class="number">50</span>,input_length=<span class="number">300</span>))<span class="comment">#指明词表规模和目标向量维度(长度)</span></span><br><span class="line"><span class="comment">#此时的输出shape:(none,300,50)</span></span><br><span class="line"><span class="comment"># model.add(layers.Flatten()) #此时的输出shape:(none,15000)</span></span><br><span class="line">model.add(layers.GlobalAveragePooling1D())<span class="comment">#用一维全局平局池化代替Flatten更好(求300个数的均值),此时的输出shape:(none,50),模型参数规模更小</span></span><br><span class="line">model.add(layers.Dense(<span class="number">64</span>,activation=<span class="string">'relu'</span>))</span><br><span class="line">model.add(layers.Dropout(<span class="number">0.5</span>)) <span class="comment">#抑制过拟合</span></span><br><span class="line">model.add(layers.Dense(<span class="number">1</span>,activation=<span class="string">'sigmoid'</span>))</span><br><span class="line"><span class="comment">#查看模型参数</span></span><br><span class="line">model.summary()</span><br><span class="line"><span class="comment">#编译模型</span></span><br><span class="line">model.compile(optimizer=keras.optimizers.Adam(lr=<span class="number">0.001</span>),</span><br><span class="line"> loss=<span class="string">'binary_crossentropy'</span>,</span><br><span class="line"> metrics=[<span class="string">'acc'</span>])</span><br><span class="line"><span class="comment">#训练模型(直接指定batch_size而不是steps_per_epoch)</span></span><br><span class="line">history=model.fit(x_train,y_train,epochs=<span class="number">15</span>,batch_size=<span class="number">256</span>,</span><br><span class="line"> validation_data=(x_test,y_test))</span><br><span class="line"><span class="comment">#查看训练过程</span></span><br><span class="line">history.history.keys()</span><br><span class="line">plt.plot(history.epoch,history.history[<span class="string">'loss'</span>],<span class="string">'r'</span>)<span class="comment">#这次用方括号引用字典的val,用'r'指定曲线颜色</span></span><br><span class="line">plt.plot(history.epoch,history.history[<span class="string">'val_loss'</span>],<span class="string">'b--'</span>)<span class="comment">#'b--'表示蓝色虚线</span></span><br><span class="line">plt.plot(history.epoch,history.history[<span class="string">'acc'</span>],<span class="string">'r'</span>)</span><br><span class="line">plt.plot(history.epoch,history.history[<span class="string">'val_acc'</span>],<span class="string">'b--'</span>)</span><br><span class="line"><span class="comment">#发现过拟合了</span></span><br><span class="line"><span class="comment">#解决过拟合:dropout或者正则化</span></span><br><span class="line"><span class="comment">#添加model.add(layers.Dropout(0.5))</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">#以空格分裂文本,获取词表</span></span><br><span class="line">dict((word,str.split().index(word)) <span class="keyword">for</span> word <span class="keyword">in</span> str.split())<span class="comment">#元组推导式</span></span><br></pre></td></tr></table></figure>
<h3 id="Eager模式介绍"><a href="#Eager模式介绍" class="headerlink" title="Eager模式介绍"></a>Eager模式介绍</h3><p>TensoeFloe的Eager模式(2.x版本的默认模式)是一个命令式编程环境,通过它无需构建计算图就可以立即看到每一步操作产生的结果,调试模型十分方便。</p>
<p>Eager模式下,tf操作会立即执行,并将结果返回给Python,tf.Tensor对象引用具体值而不是计算图中节点的符号句柄。</p>
<p>tf.Tensor对象可以和Python对象和numpy数组方便地转换,自动转换或者用numpy()方法,或者用一般的类型转换函数。</p>
<h3 id="Eager模式代码演示和张量介绍"><a href="#Eager模式代码演示和张量介绍" class="headerlink" title="Eager模式代码演示和张量介绍"></a>Eager模式代码演示和张量介绍</h3><figure class="highlight python"><table><tr><td class="code"><pre><span class="line"><span class="keyword">import</span> tensorflow <span class="keyword">as</span> tf</span><br><span class="line">tf.executing_eagerly() <span class="comment">#查看是否处于eager模式</span></span><br><span class="line"><span class="comment">#矩阵相乘</span></span><br><span class="line">x=[[<span class="number">2</span>,]]</span><br><span class="line">m=tf.matmul(x,x)</span><br><span class="line">print(m)</span><br><span class="line"><span class="comment">#Tensor转numpy</span></span><br><span class="line">m.numpy()</span><br><span class="line"><span class="comment">#常量Tensor</span></span><br><span class="line">a=tf.constant([[<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="comment">#加法</span></span><br><span class="line">b=tf.add(a,<span class="number">1</span>)</span><br><span class="line"><span class="comment">#乘法</span></span><br><span class="line">c=tf.multiply(a,b)</span><br><span class="line"><span class="comment">#将普通数值转换为Tensor</span></span><br><span class="line">num=tf.convert_to_tensor(<span class="number">10</span>)</span><br><span class="line"><span class="comment">#Python控制流操作Tensor</span></span><br><span class="line"><span class="keyword">for</span> i <span class="keyword">in</span> range(num.numpy()):</span><br><span class="line"> i=tf.constant(i)</span><br><span class="line"> <span class="keyword">if</span> int(i%<span class="number">2</span>)==<span class="number">0</span>:</span><br><span class="line"> print(<span class="string">'even'</span>)</span><br><span class="line"> <span class="keyword">else</span>:</span><br><span class="line"> print(<span class="string">'odd'</span>)</span><br></pre></td></tr></table></figure>
<h3 id="变量与自动微分运算"><a href="#变量与自动微分运算" class="headerlink" title="变量与自动微分运算"></a>变量与自动微分运算</h3><figure class="highlight python"><table><tr><td class="code"><pre><span class="line"><span class="keyword">import</span> tensorflow <span class="keyword">as</span> tf</span><br><span class="line"></span><br><span class="line"><span class="comment">#创建一个tf变量</span></span><br><span class="line">v=tf.Variable(<span class="number">0.0</span>)</span><br><span class="line">v+<span class="number">1</span></span><br><span class="line"><span class="comment">#改变变量的值</span></span><br><span class="line">v.assign(<span class="number">5</span>) <span class="comment">#赋值</span></span><br><span class="line">v.assign_add(<span class="number">1</span>) <span class="comment">#加等操作</span></span><br><span class="line"><span class="comment">#读取变量的当前值</span></span><br><span class="line">v.read_value() <span class="comment">#用于保存网络(参数)</span></span><br><span class="line"></span><br><span class="line"><span class="comment">#自动微分计算</span></span><br><span class="line"><span class="comment">#对于tf变量</span></span><br><span class="line">w=tf.Variable([<span class="number">1.0</span>])</span><br><span class="line"><span class="keyword">with</span> tf.GradientTape() <span class="keyword">as</span> t: <span class="comment">#上下文关联器,跟踪记录tf变量的运算</span></span><br><span class="line"> loss=w*w</span><br><span class="line">grad=t.gradient(loss,w)<span class="comment">#求解loss对w的微分(不放在with里面吗?)</span></span><br><span class="line"><span class="comment">#对于tf常量,需要使用记录器的watch方法</span></span><br><span class="line">w=tf.constant(<span class="number">3.0</span>)</span><br><span class="line"><span class="keyword">with</span> tf.GradientTape() <span class="keyword">as</span> t: </span><br><span class="line"> t.watch(w) <span class="comment">#对于tf常量要加上这一步</span></span><br><span class="line"> loss=w*w</span><br><span class="line">grad=t.gradient(loss,w)</span><br><span class="line"><span class="comment">#以上例子在调用了t.gradient方法以后,记录资源会被立即释放,那么对于需要多次计算微分的情况,就需要增加记录器参数:</span></span><br><span class="line">w=tf.Variable([<span class="number">1.0</span>])</span><br><span class="line"><span class="keyword">with</span> tf.GradientTape(persistent=<span class="literal">True</span>) <span class="keyword">as</span> t: <span class="comment">#上下文关联器,跟踪记录tf变量的运算,且记录资源会一直存在</span></span><br><span class="line"> y=w*w</span><br><span class="line"> z=y*y</span><br><span class="line">dy_dw=t.gradient(y,w)</span><br><span class="line">dz_dw=t.gradient(z,w)</span><br></pre></td></tr></table></figure>
<h3 id="自动微分与自定义训练"><a href="#自动微分与自定义训练" class="headerlink" title="自动微分与自定义训练"></a>自动微分与自定义训练</h3><p>以mnist数据集为例</p>
<figure class="highlight python"><table><tr><td class="code"><pre><span class="line"><span class="keyword">import</span> tensorflow <span class="keyword">as</span> tf</span><br><span class="line"></span><br><span class="line"><span class="comment">#加载数据集</span></span><br><span class="line">(train_image,train_labels),_=tf.keras.datasets.mnist.load_data() <span class="comment">#不用测试集,因此用占位符'_'代替</span></span><br><span class="line"><span class="comment">#扩充维度(CNN要求图片具有h,w,c三个维度)</span></span><br><span class="line">train_image=tf.expand_dims(train_image,<span class="number">-1</span>)<span class="comment">#在末尾添加维度</span></span><br><span class="line"><span class="comment">#归一化,改变数据类型</span></span><br><span class="line">train_image=tf.cast(train_image/<span class="number">255</span>,tf.float32)</span><br><span class="line">train_labels=tf.cast(train_labels,tf.int64)</span><br><span class="line"><span class="comment">#创建Dataset</span></span><br><span class="line">dataset=tf.data.Dataset.from_tensor_slices((train_image,train_labels))</span><br><span class="line"><span class="comment">#设置Dataset</span></span><br><span class="line">dataset=dataset.shuffle(<span class="number">10000</span>).batch(<span class="number">32</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment">#定义模型</span></span><br><span class="line">model=tf.keras.Sequential([</span><br><span class="line"> tf.keras.layers.Conv2D(<span class="number">16</span>,[<span class="number">3</span>,<span class="number">3</span>],activation=<span class="string">'relu'</span>,input_shape=(<span class="literal">None</span>,<span class="literal">None</span>,<span class="number">1</span>)),<span class="comment">#不限制输入shape</span></span><br><span class="line"> tf.keras.layers.Conv2D(<span class="number">32</span>,[<span class="number">3</span>,<span class="number">3</span>],activation=<span class="string">'relu'</span>),</span><br><span class="line"> tf.keras.layers.GlobalMaxPooling2D(),</span><br><span class="line"> tf.keras.layers.Dense(<span class="number">10</span>)</span><br><span class="line">])</span><br><span class="line"><span class="comment">#定义优化器和损失函数</span></span><br><span class="line">optimizer=tf.keras.optimizers.Adam()</span><br><span class="line">loss_func=tf.keras.losses.SparseCatagoricalCrossentropy(From_logits=<span class="literal">True</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment">#**测试如何从dataset中取数据</span></span><br><span class="line">features,label=next(iter(dataset))</span><br><span class="line">pred=model(features) <span class="comment">#model可以直接调用,从而进行预测</span></span><br><span class="line">tf.argmax(pred,<span class="number">1</span>) <span class="comment">#在第一个维度上计算“最大值索引”</span></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"><span class="function"><span class="keyword">def</span> <span class="title">loss</span><span class="params">(model,x,y)</span>:</span></span><br><span class="line"> y_=model(x)</span><br><span class="line"> <span class="keyword">return</span> loss_func(y,y_)</span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">train_step</span><span class="params">(model,images,labels)</span>:</span></span><br><span class="line"> <span class="keyword">with</span> tf.GradientTape() <span class="keyword">as</span> t:</span><br><span class="line"> loss_step=loss(model,images,labels)</span><br><span class="line"> grads=t.gradient(loss_step,model.trainable_variables)</span><br><span class="line"> optimizer.apply_gradients(zip(grads,model.trainable_variables))</span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">train</span><span class="params">()</span>:</span></span><br><span class="line"> <span class="keyword">for</span> epoch <span class="keyword">in</span> range(<span class="number">10</span>):</span><br><span class="line"> <span class="keyword">for</span> (batch,(images,labels)) <span class="keyword">in</span> enumerate(dataset):</span><br><span class="line"> train_step(model,images,labels)</span><br><span class="line"> print(<span class="string">'Epoch {} is finished'</span>.format(epoch))</span><br><span class="line"> </span><br><span class="line"><span class="comment">#开始循环迭代训练模型</span></span><br><span class="line">train()</span><br></pre></td></tr></table></figure>
<h3 id="tf-keras-metrics汇总计算模块"><a href="#tf-keras-metrics汇总计算模块" class="headerlink" title="tf.keras.metrics汇总计算模块"></a>tf.keras.metrics汇总计算模块</h3><figure class="highlight python"><table><tr><td class="code"><pre><span class="line"><span class="comment">#...接上一节程序</span></span><br><span class="line"><span class="comment">#定义一个计算均值的对象</span></span><br><span class="line">m=tf.keras.metrics.Mean(<span class="string">'acc'</span>)</span><br><span class="line">m(<span class="number">10</span>)</span><br><span class="line">m(<span class="number">20</span>)</span><br><span class="line">m([<span class="number">30</span>,<span class="number">40</span>])</span><br><span class="line">m.result().numpy() <span class="comment">#返回m记录的所有值的均值25</span></span><br><span class="line">m.reset_states() <span class="comment">#重置对象状态,清除记录</span></span><br><span class="line"></span><br><span class="line"><span class="comment">#定义一个计算正确率的对象</span></span><br><span class="line">a=tf.keras.metrics.SparseCatagoricalAccuracy(<span class="string">'acc'</span>)</span><br><span class="line">a(lables,model(features)) <span class="comment">#预测并计算正确率</span></span><br></pre></td></tr></table></figure>
<h3 id="tf-keras-metrics汇总计算应用实例"><a href="#tf-keras-metrics汇总计算应用实例" class="headerlink" title="tf.keras.metrics汇总计算应用实例"></a>tf.keras.metrics汇总计算应用实例</h3><p>借用上上节代码</p>
<figure class="highlight python"><table><tr><td class="code"><pre><span class="line"><span class="keyword">import</span> tensorflow <span class="keyword">as</span> tf</span><br><span class="line"></span><br><span class="line"><span class="comment">#加载数据集</span></span><br><span class="line">(train_image,train_labels),(test_image,test_labels)=tf.keras.datasets.mnist.load_data() </span><br><span class="line"><span class="comment">#扩充维度(CNN要求图片具有h,w,c三个维度)</span></span><br><span class="line">train_image=tf.expand_dims(train_image,<span class="number">-1</span>)<span class="comment">#在末尾添加维度</span></span><br><span class="line">test_image=tf.expand_dims(test_image,<span class="number">-1</span>)</span><br><span class="line"><span class="comment">#归一化,改变数据类型</span></span><br><span class="line">train_image=tf.cast(train_image/<span class="number">255</span>,tf.float32)</span><br><span class="line">test_image=tf.cast(test_image/<span class="number">255</span>,tf.float32)</span><br><span class="line">train_labels=tf.cast(train_labels,tf.int64)</span><br><span class="line">test_labels=tf.cast(test_labels,tf.int64)</span><br><span class="line"><span class="comment">#创建Dataset</span></span><br><span class="line">dataset=tf.data.Dataset.from_tensor_slices((train_image,train_labels))</span><br><span class="line">test_dataset=tf.data.Dataset.from_tensor_slices((test_image,test_labels))</span><br><span class="line"><span class="comment">#设置Dataset</span></span><br><span class="line">dataset=dataset.shuffle(<span class="number">10000</span>).batch(<span class="number">32</span>)</span><br><span class="line">test_dataset=test_dataset.batch(<span class="number">32</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment">#定义模型</span></span><br><span class="line">model=tf.keras.Sequential([</span><br><span class="line"> tf.keras.layers.Conv2D(<span class="number">16</span>,[<span class="number">3</span>,<span class="number">3</span>],activation=<span class="string">'relu'</span>,input_shape=(<span class="literal">None</span>,<span class="literal">None</span>,<span class="number">1</span>)),<span class="comment">#不限制输入shape</span></span><br><span class="line"> tf.keras.layers.Conv2D(<span class="number">32</span>,[<span class="number">3</span>,<span class="number">3</span>],activation=<span class="string">'relu'</span>),</span><br><span class="line"> tf.keras.layers.GlobalMaxPooling2D(),</span><br><span class="line"> tf.keras.layers.Dense(<span class="number">10</span>)</span><br><span class="line">])</span><br><span class="line"><span class="comment">#定义优化器和损失函数</span></span><br><span class="line">optimizer=tf.keras.optimizers.Adam()</span><br><span class="line">loss_func=tf.keras.losses.SparseCatagoricalCrossentropy(From_logits=<span class="literal">True</span>)</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">train_loss=tf.keras.metrics.Mean(<span class="string">'train_loss'</span>)</span><br><span class="line">train_accuracy=tf.keras.metrics.SparseCatagoricalAccuracy(<span class="string">'train_accuracy'</span>)</span><br><span class="line">test_loss=tf.keras.metrics.Mean(<span class="string">'test_loss'</span>)</span><br><span class="line">test_accuracy=tf.keras.metrics.SparseCatagoricalAccuracy(<span class="string">'test_accuracy'</span>)</span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">train_step</span><span class="params">(model,images,labels)</span>:</span></span><br><span class="line"> <span class="keyword">with</span> tf.GradientTape() <span class="keyword">as</span> t:</span><br><span class="line"> pred=model(images)</span><br><span class="line"> loss_step=loss_func(labels,pred)</span><br><span class="line"> grads=t.gradient(loss_step,model.trainable_variables)</span><br><span class="line"> optimizer.apply_gradients(zip(grads,model.trainable_variables))</span><br><span class="line"> train_loss(loss_step) <span class="comment">#记录每次迭代的loss,用于计算当前epoch的平均loss</span></span><br><span class="line"> train_accuracy(labels,pred) <span class="comment">#记录预测值和标签值,用于计算当前epoch的正确率</span></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">test_step</span><span class="params">(model,images,labels)</span>:</span></span><br><span class="line"> pred=model(images)</span><br><span class="line"> loss_step=loss_func(labels,pred)</span><br><span class="line"> test_loss(loss_step) <span class="comment">#记录每次迭代的loss,用于计算当前epoch的平均loss</span></span><br><span class="line"> test_accuracy(labels,pred) <span class="comment">#记录预测值和标签值,用于计算当前epoch的正确率</span></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">train</span><span class="params">()</span>:</span></span><br><span class="line"> <span class="keyword">for</span> epoch <span class="keyword">in</span> range(<span class="number">10</span>):</span><br><span class="line"> <span class="keyword">for</span> (batch,(images,labels)) <span class="keyword">in</span> enumerate(dataset):</span><br><span class="line"> train_step(model,images,labels)</span><br><span class="line"> print(<span class="string">'Epoch {} loss is {},accuracy is {}'</span>.format(epoch,</span><br><span class="line"> train_loss.result(),</span><br><span class="line"> train_accuracy.result()))</span><br><span class="line"> train_loss.reset_states()</span><br><span class="line"> train_accuracy.reset_states()</span><br><span class="line"> </span><br><span class="line"> <span class="keyword">for</span> (batch,(images,labels)) <span class="keyword">in</span> enumerate(test_dataset):</span><br><span class="line"> test_step(model,images,labels)</span><br><span class="line"> print(<span class="string">'Epoch {} test_loss is {},test_accuracy is {}'</span>.format(epoch,</span><br><span class="line"> test_loss.result(),</span><br><span class="line"> test_accuracy.result()))</span><br><span class="line"> test_loss.reset_states()</span><br><span class="line"> test_accuracy.reset_states()</span><br><span class="line"> </span><br><span class="line"><span class="comment">#开始循环迭代训练模型</span></span><br><span class="line">train()</span><br></pre></td></tr></table></figure>
<h3 id="利用回调函数使用TensorBoard"><a href="#利用回调函数使用TensorBoard" class="headerlink" title="利用回调函数使用TensorBoard"></a>利用回调函数使用TensorBoard</h3><p>TB通过读取训练过程中的事件文件,可视化训练过程。</p>
<p>以上次的程序为例</p>
<figure class="highlight python"><table><tr><td class="code"><pre><span class="line"><span class="keyword">import</span> tensorflow <span class="keyword">as</span> tf</span><br><span class="line"><span class="keyword">import</span> datetime</span><br><span class="line"><span class="keyword">import</span> os</span><br><span class="line"></span><br><span class="line"><span class="comment">#加载数据集</span></span><br><span class="line">(train_image,train_labels),(test_image,test_labels)=tf.keras.datasets.mnist.load_data() </span><br><span class="line"><span class="comment">#扩充维度(CNN要求图片具有h,w,c三个维度)</span></span><br><span class="line">train_image=tf.expand_dims(train_image,<span class="number">-1</span>)<span class="comment">#在末尾添加维度</span></span><br><span class="line">test_image=tf.expand_dims(test_image,<span class="number">-1</span>)</span><br><span class="line"><span class="comment">#归一化,改变数据类型</span></span><br><span class="line">train_image=tf.cast(train_image/<span class="number">255</span>,tf.float32)</span><br><span class="line">test_image=tf.cast(test_image/<span class="number">255</span>,tf.float32)</span><br><span class="line">train_labels=tf.cast(train_labels,tf.int64)</span><br><span class="line">test_labels=tf.cast(test_labels,tf.int64)</span><br><span class="line"><span class="comment">#创建Dataset</span></span><br><span class="line">dataset=tf.data.Dataset.from_tensor_slices((train_image,train_labels))</span><br><span class="line">test_dataset=tf.data.Dataset.from_tensor_slices((test_image,test_labels))</span><br><span class="line"><span class="comment">#设置Dataset</span></span><br><span class="line">dataset=dataset.repeat().shuffle(<span class="number">10000</span>).batch(<span class="number">128</span>)</span><br><span class="line">test_dataset=test_dataset.batch(<span class="number">128</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment">#定义模型</span></span><br><span class="line">model=tf.keras.Sequential([</span><br><span class="line"> tf.keras.layers.Conv2D(<span class="number">16</span>,[<span class="number">3</span>,<span class="number">3</span>],activation=<span class="string">'relu'</span>,input_shape=(<span class="literal">None</span>,<span class="literal">None</span>,<span class="number">1</span>)),<span class="comment">#不限制输入shape</span></span><br><span class="line"> tf.keras.layers.Conv2D(<span class="number">32</span>,[<span class="number">3</span>,<span class="number">3</span>],activation=<span class="string">'relu'</span>),</span><br><span class="line"> tf.keras.layers.GlobalMaxPooling2D(),</span><br><span class="line"> tf.keras.layers.Dense(<span class="number">10</span>)</span><br><span class="line">])</span><br><span class="line"><span class="comment">#编译模型</span></span><br><span class="line">model.compile(optimizer=<span class="string">'adam'</span>,</span><br><span class="line"> loss=<span class="string">'sparse_categorical_crossentropy'</span>,</span><br><span class="line"> metrics=[<span class="string">'acc'</span>])</span><br><span class="line"><span class="comment">#添加tensorboard回调函数</span></span><br><span class="line">log_dir=os.path.join(<span class="string">'logs'</span>,</span><br><span class="line"> datetime.datetime.now().strftime(<span class="string">"%Y%m%d-%H%M%S"</span>))<span class="comment"># 生成路径字符串,用于保存事件文件?</span></span><br><span class="line">tensorboard_callback=tf.keras.callbacks.TensorBoard(log_dir,histogram_freq=<span class="number">1</span>)</span><br><span class="line"><span class="comment">#训练模型</span></span><br><span class="line">model.fit(dataset,</span><br><span class="line"> steps_per_epochs=<span class="number">60000</span>//<span class="number">128</span>,</span><br><span class="line"> validation_data=test_dataset,</span><br><span class="line"> validation_steps=<span class="number">10000</span>//<span class="number">128</span>,</span><br><span class="line"> callbacks=[tensorboard_callback])</span><br><span class="line"></span><br><span class="line"><span class="comment">#在notebook中启动tensorboard</span></span><br><span class="line">%load_ext tensorboard</span><br><span class="line">%matplotlib inline</span><br><span class="line">%tensorboard --logdir logs</span><br><span class="line"><span class="comment">#在命令行中启动tensorboard</span></span><br><span class="line"><span class="comment"># tensorboard --logdir d:\...\logs</span></span><br></pre></td></tr></table></figure>
<h3 id="自定义变量的TensorBoard可视化"><a href="#自定义变量的TensorBoard可视化" class="headerlink" title="自定义变量的TensorBoard可视化"></a>自定义变量的TensorBoard可视化</h3><figure class="highlight python"><table><tr><td class="code"><pre><span class="line"><span class="keyword">import</span> tensorflow <span class="keyword">as</span> tf</span><br><span class="line"><span class="keyword">import</span> datetime</span><br><span class="line"><span class="keyword">import</span> os</span><br><span class="line"></span><br><span class="line"><span class="comment">#加载数据集</span></span><br><span class="line">(train_image,train_labels),(test_image,test_labels)=tf.keras.datasets.mnist.load_data() </span><br><span class="line"><span class="comment">#扩充维度(CNN要求图片具有h,w,c三个维度)</span></span><br><span class="line">train_image=tf.expand_dims(train_image,<span class="number">-1</span>)<span class="comment">#在末尾添加维度</span></span><br><span class="line">test_image=tf.expand_dims(test_image,<span class="number">-1</span>)</span><br><span class="line"><span class="comment">#归一化,改变数据类型</span></span><br><span class="line">train_image=tf.cast(train_image/<span class="number">255</span>,tf.float32)</span><br><span class="line">test_image=tf.cast(test_image/<span class="number">255</span>,tf.float32)</span><br><span class="line">train_labels=tf.cast(train_labels,tf.int64)</span><br><span class="line">test_labels=tf.cast(test_labels,tf.int64)</span><br><span class="line"><span class="comment">#创建Dataset</span></span><br><span class="line">dataset=tf.data.Dataset.from_tensor_slices((train_image,train_labels))</span><br><span class="line">test_dataset=tf.data.Dataset.from_tensor_slices((test_image,test_labels))</span><br><span class="line"><span class="comment">#设置Dataset</span></span><br><span class="line">dataset=dataset.repeat().shuffle(<span class="number">10000</span>).batch(<span class="number">128</span>)</span><br><span class="line">test_dataset=test_dataset.batch(<span class="number">128</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment">#定义模型</span></span><br><span class="line">model=tf.keras.Sequential([</span><br><span class="line"> tf.keras.layers.Conv2D(<span class="number">16</span>,[<span class="number">3</span>,<span class="number">3</span>],activation=<span class="string">'relu'</span>,input_shape=(<span class="literal">None</span>,<span class="literal">None</span>,<span class="number">1</span>)),<span class="comment">#不限制输入shape</span></span><br><span class="line"> tf.keras.layers.Conv2D(<span class="number">32</span>,[<span class="number">3</span>,<span class="number">3</span>],activation=<span class="string">'relu'</span>),</span><br><span class="line"> tf.keras.layers.GlobalMaxPooling2D(),</span><br><span class="line"> tf.keras.layers.Dense(<span class="number">10</span>)</span><br><span class="line">])</span><br><span class="line"><span class="comment">#编译模型</span></span><br><span class="line">model.compile(optimizer=<span class="string">'adam'</span>,</span><br><span class="line"> loss=<span class="string">'sparse_categorical_crossentropy'</span>,</span><br><span class="line"> metrics=[<span class="string">'acc'</span>])</span><br><span class="line"><span class="comment">#添加tensorboard回调函数</span></span><br><span class="line">log_dir=os.path.join(<span class="string">'logs'</span>,</span><br><span class="line"> datetime.datetime.now().strftime(<span class="string">"%Y%m%d-%H%M%S"</span>))<span class="comment"># 生成路径字符串,用于保存事件文件?</span></span><br><span class="line">tensorboard_callback=tf.keras.callbacks.TensorBoard(log_dir,histogram_freq=<span class="number">1</span>)</span><br><span class="line"><span class="comment">#定义学习率控制函数,并创建一个LearningRateScheduler回调函数</span></span><br><span class="line">file_writer=tf.summary.create_file_writer(log_dir+<span class="string">'/lr'</span>) <span class="comment">#创建一个文件编写器,记录学习率的变化</span></span><br><span class="line">file_writer.set_as_default() <span class="comment">#设置为默认文件编写器</span></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">lr_sche</span><span class="params">(epoch)</span>:</span></span><br><span class="line"> learning_rate=<span class="number">0.2</span></span><br><span class="line"> <span class="keyword">if</span> epoch><span class="number">5</span>:</span><br><span class="line"> learning_rate=<span class="number">0.02</span></span><br><span class="line"> <span class="keyword">if</span> epoch><span class="number">10</span>:</span><br><span class="line"> learning_rate=<span class="number">0.01</span></span><br><span class="line"> <span class="keyword">if</span> epoch><span class="number">20</span>:</span><br><span class="line"> learning_rate=<span class="number">0.005</span></span><br><span class="line"> tf.summary.scalar(<span class="string">'learning_rate'</span>,data=learning_rate,step=epoch)<span class="comment">#使用文件编写器记录学习率变化</span></span><br><span class="line"> <span class="keyword">return</span> learning_rate</span><br><span class="line">lr_callback=tf.keras.callbacks.LearningRateScheduler(lr_sche)<span class="comment">#创建一个学习率调度器回调函数</span></span><br><span class="line"><span class="comment">#训练模型</span></span><br><span class="line">model.fit(dataset,</span><br><span class="line"> steps_per_epochs=<span class="number">60000</span>//<span class="number">128</span>,</span><br><span class="line"> validation_data=test_dataset,</span><br><span class="line"> validation_steps=<span class="number">10000</span>//<span class="number">128</span>,</span><br><span class="line"> callbacks=[tensorboard_callback,lr_callback])</span><br></pre></td></tr></table></figure>
<h3 id="自定义训练的TensorBoard可视化"><a href="#自定义训练的TensorBoard可视化" class="headerlink" title="自定义训练的TensorBoard可视化"></a>自定义训练的TensorBoard可视化</h3><figure class="highlight python"><table><tr><td class="code"><pre><span class="line"><span class="keyword">import</span> tensorflow <span class="keyword">as</span> tf</span><br><span class="line"></span><br><span class="line"><span class="comment">#加载数据集</span></span><br><span class="line">(train_image,train_labels),(test_image,test_labels)=tf.keras.datasets.mnist.load_data() </span><br><span class="line"><span class="comment">#扩充维度(CNN要求图片具有h,w,c三个维度)</span></span><br><span class="line">train_image=tf.expand_dims(train_image,<span class="number">-1</span>)<span class="comment">#在末尾添加维度</span></span><br><span class="line">test_image=tf.expand_dims(test_image,<span class="number">-1</span>)</span><br><span class="line"><span class="comment">#归一化,改变数据类型</span></span><br><span class="line">train_image=tf.cast(train_image/<span class="number">255</span>,tf.float32)</span><br><span class="line">test_image=tf.cast(test_image/<span class="number">255</span>,tf.float32)</span><br><span class="line">train_labels=tf.cast(train_labels,tf.int64)</span><br><span class="line">test_labels=tf.cast(test_labels,tf.int64)</span><br><span class="line"><span class="comment">#创建Dataset</span></span><br><span class="line">dataset=tf.data.Dataset.from_tensor_slices((train_image,train_labels))</span><br><span class="line">test_dataset=tf.data.Dataset.from_tensor_slices((test_image,test_labels))</span><br><span class="line"><span class="comment">#设置Dataset</span></span><br><span class="line">dataset=dataset.shuffle(<span class="number">10000</span>).batch(<span class="number">32</span>)</span><br><span class="line">test_dataset=test_dataset.batch(<span class="number">32</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment">#定义模型</span></span><br><span class="line">model=tf.keras.Sequential([</span><br><span class="line"> tf.keras.layers.Conv2D(<span class="number">16</span>,[<span class="number">3</span>,<span class="number">3</span>],activation=<span class="string">'relu'</span>,input_shape=(<span class="literal">None</span>,<span class="literal">None</span>,<span class="number">1</span>)),<span class="comment">#不限制输入shape</span></span><br><span class="line"> tf.keras.layers.Conv2D(<span class="number">32</span>,[<span class="number">3</span>,<span class="number">3</span>],activation=<span class="string">'relu'</span>),</span><br><span class="line"> tf.keras.layers.GlobalMaxPooling2D(),</span><br><span class="line"> tf.keras.layers.Dense(<span class="number">10</span>)</span><br><span class="line">])</span><br><span class="line"><span class="comment">#定义优化器和损失函数</span></span><br><span class="line">optimizer=tf.keras.optimizers.Adam()</span><br><span class="line">loss_func=tf.keras.losses.SparseCatagoricalCrossentropy(From_logits=<span class="literal">True</span>)</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">train_loss=tf.keras.metrics.Mean(<span class="string">'train_loss'</span>)</span><br><span class="line">train_accuracy=tf.keras.metrics.SparseCatagoricalAccuracy(<span class="string">'train_accuracy'</span>)</span><br><span class="line">test_loss=tf.keras.metrics.Mean(<span class="string">'test_loss'</span>)</span><br><span class="line">test_accuracy=tf.keras.metrics.SparseCatagoricalAccuracy(<span class="string">'test_accuracy'</span>)</span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">train_step</span><span class="params">(model,images,labels)</span>:</span></span><br><span class="line"> <span class="keyword">with</span> tf.GradientTape() <span class="keyword">as</span> t:</span><br><span class="line"> pred=model(images)</span><br><span class="line"> loss_step=loss_func(labels,pred)</span><br><span class="line"> grads=t.gradient(loss_step,model.trainable_variables)</span><br><span class="line"> optimizer.apply_gradients(zip(grads,model.trainable_variables))</span><br><span class="line"> train_loss(loss_step) <span class="comment">#记录每次迭代的loss,用于计算当前epoch的平均loss</span></span><br><span class="line"> train_accuracy(labels,pred) <span class="comment">#记录预测值和标签值,用于计算当前epoch的正确率</span></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">test_step</span><span class="params">(model,images,labels)</span>:</span></span><br><span class="line"> pred=model(images)</span><br><span class="line"> loss_step=loss_func(labels,pred)</span><br><span class="line"> test_loss(loss_step) <span class="comment">#记录每次迭代的loss,用于计算当前epoch的平均loss</span></span><br><span class="line"> test_accuracy(labels,pred) <span class="comment">#记录预测值和标签值,用于计算当前epoch的正确率</span></span><br><span class="line"></span><br><span class="line"><span class="comment">#创建文件编写器</span></span><br><span class="line">current_time=datetime.datetime.now().strftime(<span class="string">"%Y%m%d-%H%M%S"</span>) </span><br><span class="line">train_log_dir=<span class="string">'logs/gradient_tape'</span>+current_time+<span class="string">'/train'</span></span><br><span class="line">test_log_dir=<span class="string">'logs/gradient_tape'</span>+current_time+<span class="string">'/test'</span></span><br><span class="line">train_writer=tf.summary.create_file_writer(train_log_dir)</span><br><span class="line">test_writer=tf.summary.create_file_writer(test_log_dir)</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">train</span><span class="params">()</span>:</span></span><br><span class="line"> <span class="keyword">for</span> epoch <span class="keyword">in</span> range(<span class="number">10</span>):</span><br><span class="line"> <span class="keyword">for</span> (batch,(images,labels)) <span class="keyword">in</span> enumerate(dataset):</span><br><span class="line"> train_step(model,images,labels)</span><br><span class="line"> <span class="keyword">with</span> train_writer.as_default():</span><br><span class="line"> tf.summary.scalar(<span class="string">'loss'</span>,train_loss.result(),step=epoch)<span class="comment">#记录此epoch的train_loss到磁盘</span></span><br><span class="line"> tf.summary.scalar(<span class="string">'accuracy'</span>,train_accuracy.result(),step=epoch)<span class="comment">#记录此epoch的train_accuracy到磁盘</span></span><br><span class="line"> </span><br><span class="line"> print(<span class="string">'Epoch {} loss is {},accuracy is {}'</span>.format(epoch,</span><br><span class="line"> train_loss.result(),</span><br><span class="line"> train_accuracy.result()))</span><br><span class="line"> train_loss.reset_states()</span><br><span class="line"> train_accuracy.reset_states()</span><br><span class="line"> </span><br><span class="line"> <span class="keyword">for</span> (batch,(images,labels)) <span class="keyword">in</span> enumerate(test_dataset):</span><br><span class="line"> test_step(model,images,labels)</span><br><span class="line"> <span class="keyword">with</span> test_writer.as_default():</span><br><span class="line"> tf.summary.scalar(<span class="string">'loss'</span>,test_loss.result(),step=epoch)<span class="comment">#记录此epoch的test_loss到磁盘</span></span><br><span class="line"> tf.summary.scalar(<span class="string">'accuracy'</span>,test_accuracy.result(),step=epoch)<span class="comment">#记录此epoch的test_accuracy到磁盘</span></span><br><span class="line"> print(<span class="string">'Epoch {} test_loss is {},test_accuracy is {}'</span>.format(epoch,</span><br><span class="line"> test_loss.result(),</span><br><span class="line"> test_accuracy.result()))</span><br><span class="line"> test_loss.reset_states()</span><br><span class="line"> test_accuracy.reset_states()</span><br><span class="line"> </span><br><span class="line"><span class="comment">#开始循环迭代训练模型</span></span><br><span class="line">train()</span><br></pre></td></tr></table></figure>
<p>注意,<code>tf.summary.scalar('loss',train_loss.result(),step=epoch)</code>记录此epoch的train_loss到磁盘,里面第一个参数’loss’表示这一组数据的标签,如果另一个文件编写器在另一个文件夹也以相同的标签记录了一组值,那么这些数据会显示在同一张画布上,方便对比两条曲线的变化趋势。</p>
<h3 id="网络结构可视化"><a href="#网络结构可视化" class="headerlink" title="网络结构可视化"></a>网络结构可视化</h3><figure class="highlight python"><table><tr><td class="code"><pre><span class="line"><span class="comment">#网络结构可视化方法1</span></span><br><span class="line"><span class="keyword">import</span> pydot</span><br><span class="line">keras.utils.plot_model(model, to_file=<span class="string">'LeNet_model.png'</span>, show_shapes=<span class="literal">True</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment">#网络结构可视化方法2(不太行)</span></span><br><span class="line"><span class="keyword">from</span> tensorflow.python.ops <span class="keyword">import</span> summary_ops_v2 <span class="comment"># 需要引入这个模块</span></span><br><span class="line"></span><br><span class="line"><span class="meta">@tf.function # 需要使用tf.function</span></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">call</span><span class="params">( inputs)</span>:</span></span><br><span class="line"> <span class="keyword">return</span> model(inputs)</span><br><span class="line"><span class="comment"># 开始创建网络计算图</span></span><br><span class="line">graph_writer = tf.summary.create_file_writer(logdir=<span class="string">'./Logs'</span>)</span><br><span class="line"><span class="keyword">with</span> graph_writer.as_default():</span><br><span class="line"> graph=call.get_concrete_function(img).graph</span><br><span class="line"> summary_ops_v2.graph(graph.as_graph_def())</span><br><span class="line">graph_writer.close()</span><br><span class="line"></span><br><span class="line">%load_ext tensorboard</span><br><span class="line">%matplotlib inline</span><br><span class="line">%tensorboard --logdir Logs</span><br></pre></td></tr></table></figure>
<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">import</span> tensorflow <span class="keyword">as</span> tf</span><br><span class="line"><span class="keyword">from</span> tensorflow <span class="keyword">import</span> keras</span><br><span class="line"><span class="keyword">from</span> tensorflow.keras <span class="keyword">import</span> layers</span><br><span class="line"><span class="keyword">import</span> matplotlib.pyplot <span class="keyword">as</span> plt</span><br><span class="line">%matplotlib inline</span><br><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> glob</span><br><span class="line"><span class="keyword">import</span> os</span><br><span class="line"><span class="keyword">import</span> random</span><br><span class="line"></span><br><span class="line"><span class="comment">#获取数据集图片路径和标签</span></span><br><span class="line">train_image_path=glob.glob(<span class="string">'./dc/train/*/*.jpg'</span>)</span><br><span class="line"><span class="comment">#random.shuffle(train_image_path) #乱序处理</span></span><br><span class="line">train_image_label=[int(p.split(<span class="string">'\\'</span>).[<span class="number">1</span>]==<span class="string">'cats'</span>) <span class="keyword">for</span> p <span class="keyword">in</span> train_image_path]</span><br><span class="line"><span class="comment">#图片和标签预处理</span></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">load_preprosess_image</span><span class="params">(path,label)</span>:</span></span><br><span class="line"> img=tf.io.read_file(path)</span><br><span class="line"> img=tf.image.decode_jpeg(img,channels=<span class="number">3</span>)</span><br><span class="line"> img=tf.image.resize(img,[<span class="number">360</span>,<span class="number">360</span>])</span><br><span class="line"> img=tf.image.random_crop(img,[<span class="number">256</span>,<span class="number">256</span>,<span class="number">3</span>]) <span class="comment">#图像增强</span></span><br><span class="line"> img=tf.image.random_flip_left_right(img) <span class="comment">#图像增强</span></span><br><span class="line"> img=tf.image.random_flip_up_down(img) <span class="comment">#图像增强</span></span><br><span class="line"> img=tf.image.random_brightness(img,<span class="number">0.5</span>) <span class="comment">#图像增强</span></span><br><span class="line"> img=tf.image.random_contrast(img,<span class="number">0</span>,<span class="number">1</span>) <span class="comment">#图像增强</span></span><br><span class="line"> img=tf.cast(img,tf.float32)</span><br><span class="line"> img=img/<span class="number">255</span> <span class="comment">#tf.image.convert_image_dtype()函数会自动把非float型图片归一化</span></span><br><span class="line"> <span class="comment">#将标签列表转换一下:[1,2,3]----->[[1],[2],[3]]</span></span><br><span class="line"> label=tf.reshape(label,[<span class="number">1</span>]) <span class="comment">#reshape成一维数据,其实就是把一个普通整数转换成Tensor吧?</span></span><br><span class="line"> <span class="keyword">return</span> img,label <span class="comment">#这里如果label是普通整数就不行吗?</span></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">load_preprosess_test_image</span><span class="params">(path,label)</span>:</span></span><br><span class="line"> img=tf.io.read_file(path)</span><br><span class="line"> img=tf.image.decode_jpeg(img,channels=<span class="number">3</span>)</span><br><span class="line"> img=tf.image.resize(img,[<span class="number">256</span>,<span class="number">256</span>])</span><br><span class="line"> img=tf.cast(img,tf.float32)</span><br><span class="line"> img=img/<span class="number">255</span> <span class="comment">#tf.image.convert_image_dtype()函数会自动把非float型图片归一化</span></span><br><span class="line"> <span class="comment">#将标签列表转换一下:[1,2,3]----->[[1],[2],[3]]</span></span><br><span class="line"> label=tf.reshape(label,[<span class="number">1</span>]) <span class="comment">#reshape成一维数据,其实就是把一个普通整数转换成Tensor吧?</span></span><br><span class="line"> <span class="keyword">return</span> img,label <span class="comment">#这里如果label是普通整数就不行吗?</span></span><br><span class="line"><span class="comment">#创建Dataset</span></span><br><span class="line">train_ds=tf.data.Dataset.from_tensor_slices((train_image_path,train_image_label))</span><br><span class="line">AUTOTUNE=tf.data.experimental.AUTOTUNE</span><br><span class="line">train_ds=train_ds.map(load_preprosess_image,num_parallel_calls=AUTOTUNE)<span class="comment">#同时设置map所用资源</span></span><br><span class="line"><span class="comment">#设置Dataset</span></span><br><span class="line">BATCH_SIZE=<span class="number">32</span></span><br><span class="line">train_count=len(train_image_path)</span><br><span class="line">train_ds=train_ds.shuffle(train_count).batch(BATCH_SIZE) <span class="comment">#自定义训练不需要repeat</span></span><br><span class="line">train_ds=train_ds.prefetch(AUTOTUNE) <span class="comment">#设置后台预读取图片所分配的计算资源(自动)</span></span><br><span class="line"></span><br><span class="line"><span class="comment">#添加验证数据集</span></span><br><span class="line">test_image_path=glob.glob(<span class="string">'./dc/test/*/*.jpg'</span>)</span><br><span class="line">test_image_label=[int(p.split(<span class="string">'\\'</span>).[<span class="number">1</span>]==<span class="string">'cats'</span>) <span class="keyword">for</span> p <span class="keyword">in</span> test_image_path]</span><br><span class="line">test_ds=tf.data.Dataset.from_tensor_slices((test_image_path,test_image_label))</span><br><span class="line">test_ds=test_ds.map(load_preprosess_test_image,num_parallel_calls=AUTOTUNE)</span><br><span class="line">test_ds=test_ds.batch(BATCH_SIZE)</span><br><span class="line">test_ds=test_ds.prefetch(AUTOTUNE) </span><br><span class="line"></span><br><span class="line"><span class="comment">#定义模型(参考VGG)</span></span><br><span class="line">model=keras.Sequential([</span><br><span class="line"> layers.Conv2D(<span class="number">64</span>,(<span class="number">3</span>,<span class="number">3</span>),input_shape=(<span class="number">256</span>,<span class="number">256</span>,<span class="number">3</span>),padding=<span class="string">'same'</span>,activation=<span class="string">'relu'</span>),</span><br><span class="line"> layers.BatchNormalization(), <span class="comment">#优化-新增</span></span><br><span class="line"> layers.Conv2D(<span class="number">64</span>,(<span class="number">3</span>,<span class="number">3</span>),activation=<span class="string">'relu'</span>), <span class="comment">#优化-新增</span></span><br><span class="line"> layers.BatchNormalization(), <span class="comment">#优化-新增</span></span><br><span class="line"> layers.Conv2D(<span class="number">64</span>,(<span class="number">3</span>,<span class="number">3</span>),activation=<span class="string">'relu'</span>), <span class="comment">#优化-新增</span></span><br><span class="line"> layers.BatchNormalization(), <span class="comment">#优化-新增</span></span><br><span class="line"> layers.MaxPooling2D(),</span><br><span class="line"> layers.Conv2D(<span class="number">128</span>,(<span class="number">3</span>,<span class="number">3</span>),activation=<span class="string">'relu'</span>),</span><br><span class="line"> layers.BatchNormalization(), <span class="comment">#优化-新增</span></span><br><span class="line"> layers.Conv2D(<span class="number">128</span>,(<span class="number">3</span>,<span class="number">3</span>),activation=<span class="string">'relu'</span>), <span class="comment">#优化-新增</span></span><br><span class="line"> layers.BatchNormalization(), <span class="comment">#优化-新增</span></span><br><span class="line"> layers.Conv2D(<span class="number">128</span>,(<span class="number">3</span>,<span class="number">3</span>),activation=<span class="string">'relu'</span>), <span class="comment">#优化-新增</span></span><br><span class="line"> layers.BatchNormalization(), <span class="comment">#优化-新增</span></span><br><span class="line"> layers.MaxPooling2D(),</span><br><span class="line"> layers.Conv2D(<span class="number">256</span>,(<span class="number">3</span>,<span class="number">3</span>),activation=<span class="string">'relu'</span>),</span><br><span class="line"> layers.BatchNormalization(), <span class="comment">#优化-新增</span></span><br><span class="line"> layers.Conv2D(<span class="number">256</span>,(<span class="number">3</span>,<span class="number">3</span>),activation=<span class="string">'relu'</span>), <span class="comment">#优化-新增</span></span><br><span class="line"> layers.BatchNormalization(), <span class="comment">#优化-新增</span></span><br><span class="line"> layers.Conv2D(<span class="number">256</span>,(<span class="number">3</span>,<span class="number">3</span>),activation=<span class="string">'relu'</span>), <span class="comment">#优化-新增</span></span><br><span class="line"> layers.BatchNormalization(), <span class="comment">#优化-新增</span></span><br><span class="line"> layers.MaxPooling2D(),</span><br><span class="line"> layers.Conv2D(<span class="number">512</span>,(<span class="number">3</span>,<span class="number">3</span>),activation=<span class="string">'relu'</span>),</span><br><span class="line"> layers.BatchNormalization(), <span class="comment">#优化-新增</span></span><br><span class="line"> layers.Conv2D(<span class="number">512</span>,(<span class="number">3</span>,<span class="number">3</span>),activation=<span class="string">'relu'</span>), <span class="comment">#优化-新增</span></span><br><span class="line"> layers.BatchNormalization(), <span class="comment">#优化-新增</span></span><br><span class="line"> layers.Conv2D(<span class="number">512</span>,(<span class="number">3</span>,<span class="number">3</span>),activation=<span class="string">'relu'</span>), <span class="comment">#优化-新增</span></span><br><span class="line"> layers.BatchNormalization(), <span class="comment">#优化-新增</span></span><br><span class="line"> layers.MaxPooling2D(),</span><br><span class="line"> layers.GlobalAveragePooling2D(),</span><br><span class="line"> layers.Dense(<span class="number">1024</span>,activation=<span class="string">'relu'</span>),</span><br><span class="line"> layers.BatchNormalization(), <span class="comment">#优化-新增</span></span><br><span class="line"> layers.Dense(<span class="number">256</span>,activation=<span class="string">'relu'</span>),</span><br><span class="line"> layers.BatchNormalization(), <span class="comment">#优化-新增</span></span><br><span class="line"> layers.Dense(<span class="number">1</span>) <span class="comment">#不加sigmoid激活,输出为logits,小于0认为是0,大于0认为是1,损失函数对象应设置参数from_logits=True</span></span><br><span class="line">])</span><br><span class="line"><span class="comment">#**先测试一下未训练的模型</span></span><br><span class="line">imgs,labels=next(iter(train_ds))</span><br><span class="line">pred=model(imgs)</span><br><span class="line">np.array([p[<span class="number">0</span>].numpy() <span class="keyword">for</span> p <span class="keyword">in</span> tf.cast(pred><span class="number">0</span>,tf.float32)]) <span class="comment">#预测得到的标签值</span></span><br><span class="line">np.array([l[<span class="number">0</span>].numpy() <span class="keyword">for</span> l <span class="keyword">in</span> labels]) <span class="comment">#实际标签值</span></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">train_loss_avg=tf.keras.metrics.Mean(<span class="string">'train_loss'</span>)<span class="comment">#想不通这些字符串参数有什么用</span></span><br><span class="line">train_acc=tf.keras.metrics.Accuracy(<span class="string">'train_acc'</span>)<span class="comment">#这个直接可以不指定参数</span></span><br><span class="line">test_loss_avg=tf.keras.metrics.Mean(<span class="string">'test_loss'</span>)</span><br><span class="line">test_acc=tf.keras.metrics.Accuracy(<span class="string">'test_acc'</span>)</span><br><span class="line"><span class="comment">#定义损失函数和优化器</span></span><br><span class="line">ls=tf.keras.losses.BinaryCrossentropy(from_logits=<span class="literal">True</span>) <span class="comment">#损失函数(真实值,预测值)</span></span><br><span class="line">optimizer=tf.keras.optimizers.Adam()</span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">train_step</span><span class="params">(model,images,labels)</span>:</span></span><br><span class="line"> <span class="keyword">with</span> tf.GradientTape() <span class="keyword">as</span> t:</span><br><span class="line"> pred=model(images)</span><br><span class="line"> loss_step=ls(labels,pred)</span><br><span class="line"> grads=t.gradient(loss_step,model.trainable_variables)</span><br><span class="line"> optimizer.apply_gradients(zip(grads,model.trainable_variables))</span><br><span class="line"> train_loss_avg(loss_step)</span><br><span class="line"> train_acc(labels,tf.cast(pred><span class="number">0</span>,tf.int32)) <span class="comment">#记录结果,计算正确率</span></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">test_step</span><span class="params">(model,images,labels)</span>:</span></span><br><span class="line"> pred=model(images,training=<span class="literal">False</span>)</span><br><span class="line"> <span class="comment"># pred=model.predict(images) #等效于上一句?</span></span><br><span class="line"> loss_step=ls(labels,pred)</span><br><span class="line"> test_loss_avg(loss_step)</span><br><span class="line"> test_acc(labels,tf.cast(pred><span class="number">0</span>,tf.int32)) <span class="comment">#记录结果,计算正确率</span></span><br><span class="line"><span class="comment">#开始训练</span></span><br><span class="line">train_loss_results=[] <span class="comment">#记录每个epoch的loss</span></span><br><span class="line">train_acc_results=[] <span class="comment">#记录每个epoch的acc</span></span><br><span class="line">test_loss_results=[] <span class="comment">#记录每个epoch的test loss</span></span><br><span class="line">test_acc_results=[] <span class="comment">#记录每个epoch的test acc</span></span><br><span class="line"><span class="keyword">for</span> epoch <span class="keyword">in</span> range(<span class="number">30</span>):</span><br><span class="line"> <span class="keyword">for</span> imgs_,labels_ <span class="keyword">in</span> train_ds:</span><br><span class="line"> train_step(model,imgs_,labels_)</span><br><span class="line"> print(<span class="string">'.'</span>,end=<span class="string">''</span>) <span class="comment">#打印一个'.'且不换行</span></span><br><span class="line"> print()<span class="comment">#换行</span></span><br><span class="line"> train_loss_results.append(train_loss_avg.result()) <span class="comment">#记录每个epoch的loss</span></span><br><span class="line"> train_acc_results.append(train_acc.result()) <span class="comment">#记录每个epoch的acc</span></span><br><span class="line"> <span class="keyword">for</span> imgs_,labels_ <span class="keyword">in</span> test_ds:</span><br><span class="line"> test_step(model,imgs_,labels_)</span><br><span class="line"> test_loss_results.append(test_loss_avg.result()) <span class="comment">#记录每个epoch的test loss</span></span><br><span class="line"> test_acc_results.append(test_acc.result()) <span class="comment">#记录每个epoch的test acc</span></span><br><span class="line"> print(<span class="string">'Epoch {}: loss: {:.3f},acc: {:.3f}, test_loss: {:.3f}, test_acc: {:.3f}'</span>.format( <span class="comment">#格式化:保留三位小数</span></span><br><span class="line"> epoch+<span class="number">1</span>,</span><br><span class="line"> train_loss_avg.result(),</span><br><span class="line"> train_acc.result(),</span><br><span class="line"> test_loss_avg.result(),</span><br><span class="line"> test_acc.result()</span><br><span class="line"> ))</span><br><span class="line"> train_loss_avg.reset_states() <span class="comment">#恢复汇总计算对象的状态</span></span><br><span class="line"> train_acc.reset_states()</span><br><span class="line"> test_loss_avg.reset_states()</span><br><span class="line"> test_acc.reset_states()</span><br></pre></td></tr></table></figure>
<h4 id="一些问题:-2"><a href="#一些问题:-2" class="headerlink" title="一些问题:"></a>一些问题:</h4><ol>
<li><p><code>train_loss_avg=tf.keras.metrics.Mean('train_loss')</code>想不通这些字符串参数有什么用.</p>
</li>
<li><p><code>model(img)</code>和<code>model.predict(img)</code>有什么区别?一般前者用于自定义训练?后者用于前向推断?计算效率有差别吗?</p>
</li>
<li><p>是不是每次读取一个batch时Dataset都会重新从图片路径map到图片?</p>
</li>
</ol>
<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="comment">#在Dataset进行map操作时用到的预处理函数中添加图像增强操作即可</span></span><br><span class="line"><span class="comment">#直接修改上节代码中的预处理函数</span></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">load_preprosess_image</span><span class="params">(path,label)</span>:</span></span><br><span class="line"> img=tf.io.read_file(path)</span><br><span class="line"> img=tf.image.decode_jpeg(img,channels=<span class="number">3</span>)</span><br><span class="line"> img=tf.image.resize(img,[<span class="number">360</span>,<span class="number">360</span>])</span><br><span class="line"> img=tf.image.random_crop(img,[<span class="number">256</span>,<span class="number">256</span>,<span class="number">3</span>]) <span class="comment">#图像增强</span></span><br><span class="line"> img=tf.image.random_flip_left_right(img) <span class="comment">#图像增强</span></span><br><span class="line"> img=tf.image.random_flip_up_down(img) <span class="comment">#图像增强</span></span><br><span class="line"> img=tf.image.random_brightness(img,<span class="number">0.5</span>) <span class="comment">#图像增强</span></span><br><span class="line"> img=tf.image.random_contrast(img,<span class="number">0</span>,<span class="number">1</span>) <span class="comment">#图像增强</span></span><br><span class="line"> img=tf.cast(img,tf.float32)</span><br><span class="line"> img=img/<span class="number">255</span> <span class="comment">#tf.image.convert_image_dtype()函数会自动把非float型图片归一化</span></span><br><span class="line"> <span class="comment">#将标签列表转换一下:[1,2,3]----->[[1],[2],[3]]</span></span><br><span class="line"> label=tf.reshape(label,[<span class="number">1</span>]) <span class="comment">#reshape成一维数据,其实就是把一个普通整数转换成Tensor吧?</span></span><br><span class="line"> <span class="keyword">return</span> img,label <span class="comment">#这里如果label是普通整数就不行吗?</span></span><br></pre></td></tr></table></figure>
<h3 id="迁移学习网络架构"><a href="#迁移学习网络架构" class="headerlink" title="迁移学习网络架构"></a>迁移学习网络架构</h3><p>预训练的网络包含训练好的卷积基和分类器,迁移学习时,只要冻结预训练卷积基,然后用新任务的分类器代替预训练模型的分类器,再开始训练即可。</p>
<h3 id="迁移学习的代码实现"><a href="#迁移学习的代码实现" class="headerlink" title="迁移学习的代码实现"></a>迁移学习的代码实现</h3><p>用VGG预训练网络在猫狗数据集上进行迁移学习。</p>
<figure class="highlight python"><table><tr><td class="code"><pre><span class="line"><span class="keyword">import</span> tensorflow <span class="keyword">as</span> tf</span><br><span class="line"><span class="keyword">from</span> tensorflow <span class="keyword">import</span> keras</span><br><span class="line"><span class="keyword">from</span> tensorflow.keras <span class="keyword">import</span> layers</span><br><span class="line"><span class="keyword">import</span> matplotlib.pyplot <span class="keyword">as</span> plt</span><br><span class="line">%matplotlib inline</span><br><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> glob</span><br><span class="line"></span><br><span class="line"><span class="comment">#获取数据集图片路径和标签</span></span><br><span class="line">train_image_path=glob.glob(<span class="string">'./dc/train/*/*.jpg'</span>)</span><br><span class="line">train_image_label=[int(p.split(<span class="string">'\\'</span>).[<span class="number">1</span>]==<span class="string">'cats'</span>) <span class="keyword">for</span> p <span class="keyword">in</span> train_image_path]</span><br><span class="line"><span class="comment">#图片和标签预处理</span></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">load_preprosess_image</span><span class="params">(path,label)</span>:</span></span><br><span class="line"> img=tf.io.read_file(path)</span><br><span class="line"> img=tf.image.decode_jpeg(img,channels=<span class="number">3</span>)</span><br><span class="line"> img=tf.image.resize(img,[<span class="number">360</span>,<span class="number">360</span>])</span><br><span class="line"> img=tf.image.random_crop(img,[<span class="number">256</span>,<span class="number">256</span>,<span class="number">3</span>]) <span class="comment">#图像增强</span></span><br><span class="line"> img=tf.image.random_flip_left_right(img) <span class="comment">#图像增强</span></span><br><span class="line"> img=tf.image.random_flip_up_down(img) <span class="comment">#图像增强</span></span><br><span class="line"> img=tf.image.random_brightness(img,<span class="number">0.5</span>) <span class="comment">#图像增强</span></span><br><span class="line"> img=tf.image.random_contrast(img,<span class="number">0</span>,<span class="number">1</span>) <span class="comment">#图像增强</span></span><br><span class="line"> img=tf.cast(img,tf.float32)</span><br><span class="line"> img=img/<span class="number">255</span> <span class="comment">#tf.image.convert_image_dtype()函数会自动把非float型图片归一化</span></span><br><span class="line"> <span class="comment">#将标签列表转换一下:[1,2,3]----->[[1],[2],[3]]</span></span><br><span class="line"> label=tf.reshape(label,[<span class="number">1</span>]) <span class="comment">#reshape成一维数据,其实就是把一个普通整数转换成Tensor吧?</span></span><br><span class="line"> <span class="keyword">return</span> img,label <span class="comment">#这里如果label是普通整数就不行吗?</span></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">load_preprosess_test_image</span><span class="params">(path,label)</span>:</span></span><br><span class="line"> img=tf.io.read_file(path)</span><br><span class="line"> img=tf.image.decode_jpeg(img,channels=<span class="number">3</span>)</span><br><span class="line"> img=tf.image.resize(img,[<span class="number">256</span>,<span class="number">256</span>])</span><br><span class="line"> img=tf.cast(img,tf.float32)</span><br><span class="line"> img=img/<span class="number">255</span> <span class="comment">#tf.image.convert_image_dtype()函数会自动把非float型图片归一化</span></span><br><span class="line"> <span class="comment">#将标签列表转换一下:[1,2,3]----->[[1],[2],[3]]</span></span><br><span class="line"> label=tf.reshape(label,[<span class="number">1</span>]) <span class="comment">#reshape成一维数据,其实就是把一个普通整数转换成Tensor吧?</span></span><br><span class="line"> <span class="keyword">return</span> img,label <span class="comment">#这里如果label是普通整数就不行吗?</span></span><br><span class="line"><span class="comment">#创建Dataset</span></span><br><span class="line">train_ds=tf.data.Dataset.from_tensor_slices((train_image_path,train_image_label))</span><br><span class="line">AUTOTUNE=tf.data.experimental.AUTOTUNE</span><br><span class="line">train_ds=train_ds.map(load_preprosess_image,num_parallel_calls=AUTOTUNE)<span class="comment">#同时设置map所用资源</span></span><br><span class="line"><span class="comment">#设置Dataset</span></span><br><span class="line">BATCH_SIZE=<span class="number">32</span></span><br><span class="line">train_count=len(train_image_path)</span><br><span class="line">train_ds=train_ds.repeat(<span class="number">-1</span>).shuffle(train_count).batch(BATCH_SIZE) </span><br><span class="line">train_ds=train_ds.prefetch(AUTOTUNE) <span class="comment">#设置后台预读取图片所分配的计算资源(自动)</span></span><br><span class="line"></span><br><span class="line"><span class="comment">#添加验证数据集</span></span><br><span class="line">test_image_path=glob.glob(<span class="string">'./dc/test/*/*.jpg'</span>)</span><br><span class="line">test_image_label=[int(p.split(<span class="string">'\\'</span>).[<span class="number">1</span>]==<span class="string">'cats'</span>) <span class="keyword">for</span> p <span class="keyword">in</span> test_image_path]</span><br><span class="line">test_count=len(test_image_path)</span><br><span class="line">test_ds=tf.data.Dataset.from_tensor_slices((test_image_path,tset_image_label))</span><br><span class="line">test_ds=test_ds.map(load_preprosess_test_image,num_parallel_calls=AUTOTUNE)</span><br><span class="line">test_ds=test_ds.batch(BATCH_SIZE)</span><br><span class="line">test_ds=test_ds.prefetch(AUTOTUNE) </span><br><span class="line"></span><br><span class="line"><span class="comment">##定义模型(参考VGG)</span></span><br><span class="line"><span class="comment">#获取预训练的经典模型</span></span><br><span class="line">conv_base=keras.applications.VGG16(weights=<span class="string">'imagenet'</span>,include_top=<span class="literal">False</span>) <span class="comment">#指定权重来源和是否包含卷积基后面的分类器</span></span><br><span class="line">conv_base.trainable=<span class="literal">False</span> <span class="comment">#冻结卷积基的权重,冻结前后网络的可训练参数量是不同的</span></span><br><span class="line">conv_base.summary() <span class="comment">#查看卷积基参数</span></span><br><span class="line"><span class="comment">#在预训练卷积基的基础上定义自己的模型</span></span><br><span class="line">model=keras.Sequential()</span><br><span class="line">model.add(conv_base) <span class="comment">#首先添加预训练卷积基</span></span><br><span class="line">model.add(layers.GlobalAveragePooling2D())</span><br><span class="line">model.add(layers.Dense(<span class="number">1024</span>,activation=<span class="string">'relu'</span>))</span><br><span class="line">model.add(layers.BatchNormalization())</span><br><span class="line">model.add(layers.Dense(<span class="number">256</span>,activation=<span class="string">'relu'</span>))</span><br><span class="line">model.add(layers.BatchNormalization())</span><br><span class="line">model.add(layers.Dense(<span class="number">1</span>))</span><br><span class="line">model.summary() <span class="comment">#查看模型参数</span></span><br><span class="line"></span><br><span class="line"><span class="comment">#编译模型</span></span><br><span class="line">model.compile(optimizer=keras.optimizers.Adam(lr=<span class="number">0.0005</span>),</span><br><span class="line"> loss=<span class="string">'binary_crossentropy'</span>, <span class="comment">#输出层没有经过sigmoid激活,也可以用默认的二元交叉熵?</span></span><br><span class="line"> metrics=[<span class="string">'acc'</span>])</span><br><span class="line"><span class="comment">#训练模型</span></span><br><span class="line">history=model.fit(</span><br><span class="line"> train_ds,</span><br><span class="line"> steps_per_epochs=train_count//BATCH_SIZE,</span><br><span class="line"> epochs=<span class="number">15</span>,</span><br><span class="line"> validation_data=test_ds,</span><br><span class="line"> validation_steps=test_count//BATCH_SIZE</span><br><span class="line">)</span><br></pre></td></tr></table></figure>
<h4 id="一些问题:-3"><a href="#一些问题:-3" class="headerlink" title="一些问题:"></a>一些问题:</h4><ol>
<li>全局平均池化是近两年刚出的方法?比全局卷积和展平方法更高效。</li>
<li><code>loss='binary_crossentropy'</code>默认的二元交叉熵损失函数接受网络的logits输出吗?</li>
</ol>
<h3 id="预训练网络的使用——微调"><a href="#预训练网络的使用——微调" class="headerlink" title="预训练网络的使用——微调"></a>预训练网络的使用——微调</h3><p>卷积基在结构上可以分成两部分:底部卷积层和顶部卷积层。</p>
<p>微调指的是:冻结<strong>底部卷积层</strong>,训练新添加的分类器层和<strong>顶部卷积层</strong>。</p>
<p>“微调”调节的是<strong>基础模型中的高阶特征表示</strong>,使其适应于特定任务。</p>
<h4 id="微调的步骤:"><a href="#微调的步骤:" class="headerlink" title="微调的步骤:"></a>微调的步骤:</h4><ol>
<li>在预训练卷积基上添加自定义层</li>
<li>冻结卷积基</li>
<li>训练添加的自定义分类层</li>
<li>解冻卷积基顶部的一部分卷积层</li>
<li>联合训练解冻的卷积层和添加的自定义分类层</li>
</ol>
<figure class="highlight python"><table><tr><td class="code"><pre><span class="line"><span class="keyword">import</span> tensorflow <span class="keyword">as</span> tf</span><br><span class="line"><span class="keyword">from</span> tensorflow <span class="keyword">import</span> keras</span><br><span class="line"><span class="keyword">from</span> tensorflow.keras <span class="keyword">import</span> layers</span><br><span class="line"><span class="keyword">import</span> matplotlib.pyplot <span class="keyword">as</span> plt</span><br><span class="line">%matplotlib inline</span><br><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> glob</span><br><span class="line"></span><br><span class="line"><span class="comment">#获取数据集图片路径和标签</span></span><br><span class="line">train_image_path=glob.glob(<span class="string">'./dc/train/*/*.jpg'</span>)</span><br><span class="line">train_image_label=[int(p.split(<span class="string">'\\'</span>).[<span class="number">1</span>]==<span class="string">'cats'</span>) <span class="keyword">for</span> p <span class="keyword">in</span> train_image_path]</span><br><span class="line"><span class="comment">#图片和标签预处理</span></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">load_preprosess_image</span><span class="params">(path,label)</span>:</span></span><br><span class="line"> img=tf.io.read_file(path)</span><br><span class="line"> img=tf.image.decode_jpeg(img,channels=<span class="number">3</span>)</span><br><span class="line"> img=tf.image.resize(img,[<span class="number">360</span>,<span class="number">360</span>])</span><br><span class="line"> img=tf.image.random_crop(img,[<span class="number">256</span>,<span class="number">256</span>,<span class="number">3</span>]) <span class="comment">#图像增强</span></span><br><span class="line"> img=tf.image.random_flip_left_right(img) <span class="comment">#图像增强</span></span><br><span class="line"> img=tf.image.random_flip_up_down(img) <span class="comment">#图像增强</span></span><br><span class="line"> img=tf.image.random_brightness(img,<span class="number">0.5</span>) <span class="comment">#图像增强</span></span><br><span class="line"> img=tf.image.random_contrast(img,<span class="number">0</span>,<span class="number">1</span>) <span class="comment">#图像增强</span></span><br><span class="line"> img=tf.cast(img,tf.float32)</span><br><span class="line"> img=img/<span class="number">255</span> <span class="comment">#tf.image.convert_image_dtype()函数会自动把非float型图片归一化</span></span><br><span class="line"> <span class="comment">#将标签列表转换一下:[1,2,3]----->[[1],[2],[3]]</span></span><br><span class="line"> label=tf.reshape(label,[<span class="number">1</span>]) <span class="comment">#reshape成一维数据,其实就是把一个普通整数转换成Tensor吧?</span></span><br><span class="line"> <span class="keyword">return</span> img,label <span class="comment">#这里如果label是普通整数就不行吗?</span></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">load_preprosess_test_image</span><span class="params">(path,label)</span>:</span></span><br><span class="line"> img=tf.io.read_file(path)</span><br><span class="line"> img=tf.image.decode_jpeg(img,channels=<span class="number">3</span>)</span><br><span class="line"> img=tf.image.resize(img,[<span class="number">256</span>,<span class="number">256</span>])</span><br><span class="line"> img=tf.cast(img,tf.float32)</span><br><span class="line"> img=img/<span class="number">255</span> <span class="comment">#tf.image.convert_image_dtype()函数会自动把非float型图片归一化</span></span><br><span class="line"> <span class="comment">#将标签列表转换一下:[1,2,3]----->[[1],[2],[3]]</span></span><br><span class="line"> label=tf.reshape(label,[<span class="number">1</span>]) <span class="comment">#reshape成一维数据,其实就是把一个普通整数转换成Tensor吧?</span></span><br><span class="line"> <span class="keyword">return</span> img,label <span class="comment">#这里如果label是普通整数就不行吗?</span></span><br><span class="line"><span class="comment">#创建Dataset</span></span><br><span class="line">train_ds=tf.data.Dataset.from_tensor_slices((train_image_path,train_image_label))</span><br><span class="line">AUTOTUNE=tf.data.experimental.AUTOTUNE</span><br><span class="line">train_ds=train_ds.map(load_preprosess_image,num_parallel_calls=AUTOTUNE)<span class="comment">#同时设置map所用资源</span></span><br><span class="line"><span class="comment">#设置Dataset</span></span><br><span class="line">BATCH_SIZE=<span class="number">32</span></span><br><span class="line">train_count=len(train_image_path)</span><br><span class="line">train_ds=train_ds.repeat(<span class="number">-1</span>).shuffle(train_count).batch(BATCH_SIZE)</span><br><span class="line">train_ds=train_ds.prefetch(AUTOTUNE) <span class="comment">#设置后台预读取图片所分配的计算资源(自动)</span></span><br><span class="line"></span><br><span class="line"><span class="comment">#添加验证数据集</span></span><br><span class="line">test_image_path=glob.glob(<span class="string">'./dc/test/*/*.jpg'</span>)</span><br><span class="line">test_image_label=[int(p.split(<span class="string">'\\'</span>).[<span class="number">1</span>]==<span class="string">'cats'</span>) <span class="keyword">for</span> p <span class="keyword">in</span> test_image_path]</span><br><span class="line">test_count=len(test_image_path)</span><br><span class="line">test_ds=tf.data.Dataset.from_tensor_slices((test_image_path,tset_image_label))</span><br><span class="line">test_ds=test_ds.map(load_preprosess_test_image,num_parallel_calls=AUTOTUNE)</span><br><span class="line">test_ds=test_ds.batch(BATCH_SIZE)</span><br><span class="line">test_ds=test_ds.prefetch(AUTOTUNE) </span><br><span class="line"></span><br><span class="line"><span class="comment">##定义模型(参考VGG)</span></span><br><span class="line"><span class="comment">#获取预训练的经典模型</span></span><br><span class="line">conv_base=keras.applications.VGG16(weights=<span class="string">'imagenet'</span>,include_top=<span class="literal">False</span>) <span class="comment">#指定权重来源和是否包含卷积基后面的分类器</span></span><br><span class="line">conv_base.trainable=<span class="literal">False</span> <span class="comment">#冻结卷积基的权重,冻结前后网络的可训练参数量是不同的</span></span><br><span class="line">conv_base.summary() <span class="comment">#查看卷积基参数</span></span><br><span class="line"><span class="comment">#在预训练卷积基的基础上定义自己的模型</span></span><br><span class="line">model=keras.Sequential()</span><br><span class="line">model.add(conv_base) <span class="comment">#首先添加预训练卷积基</span></span><br><span class="line">model.add(layers.GlobalAveragePooling2D())</span><br><span class="line">model.add(layers.Dense(<span class="number">1024</span>,activation=<span class="string">'relu'</span>))</span><br><span class="line">model.add(layers.BatchNormalization())</span><br><span class="line">model.add(layers.Dense(<span class="number">256</span>,activation=<span class="string">'relu'</span>))</span><br><span class="line">model.add(layers.BatchNormalization())</span><br><span class="line">model.add(layers.Dense(<span class="number">1</span>))</span><br><span class="line">model.summary() <span class="comment">#查看模型参数</span></span><br><span class="line"></span><br><span class="line"><span class="comment">#编译模型</span></span><br><span class="line">model.compile(optimizer=keras.optimizers.Adam(lr=<span class="number">0.0005</span>),</span><br><span class="line"> loss=<span class="string">'binary_crossentropy'</span>, <span class="comment">#输出层没有经过sigmoid激活,也可以用默认的二元交叉熵?</span></span><br><span class="line"> metrics=[<span class="string">'acc'</span>])</span><br><span class="line"><span class="comment">#训练模型</span></span><br><span class="line">history=model.fit(</span><br><span class="line"> train_ds,</span><br><span class="line"> steps_per_epochs=train_count//BATCH_SIZE,</span><br><span class="line"> epochs=<span class="number">15</span>,</span><br><span class="line"> validation_data=test_ds,</span><br><span class="line"> validation_steps=test_count//BATCH_SIZE</span><br><span class="line">)</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">conv_base.trainable=<span class="literal">True</span></span><br><span class="line"><span class="comment">#再冻结至倒数第三层(逐层操作)</span></span><br><span class="line">len(conv_base.layers) <span class="comment">#查看卷积基包含的层数</span></span><br><span class="line">fine_tune_at=<span class="number">-3</span></span><br><span class="line"><span class="keyword">for</span> layer <span class="keyword">in</span> conv_base.layers[:fine_tune_at]:</span><br><span class="line"> layer.trainable=<span class="literal">False</span></span><br><span class="line"><span class="comment">#重新编译模型(调低学习率)</span></span><br><span class="line">model.compile(optimizer=keras.optimizers.Adam(lr=<span class="number">0.0005</span>/<span class="number">10</span>),</span><br><span class="line"> loss=<span class="string">'binary_crossentropy'</span>, <span class="comment">#输出层没有经过sigmoid激活,也可以用默认的二元交叉熵?</span></span><br><span class="line"> metrics=[<span class="string">'acc'</span>])</span><br><span class="line"><span class="comment">#再次训练模型</span></span><br><span class="line">initial_epochs=<span class="number">15</span></span><br><span class="line">fine_tune_epochs=<span class="number">10</span></span><br><span class="line">total_epochs=initial_epochs+fine_tune_epochs</span><br><span class="line">history=model.fit(</span><br><span class="line"> train_ds,</span><br><span class="line"> steps_per_epochs=train_count//BATCH_SIZE,</span><br><span class="line"> epochs=total_epochs, <span class="comment">#指定总轮数</span></span><br><span class="line"> initial_epochs=initial_epochs, <span class="comment">#指定已训练轮数(初始轮数)</span></span><br><span class="line"> validation_data=test_ds,</span><br><span class="line"> validation_steps=test_count//BATCH_SIZE</span><br><span class="line">)</span><br></pre></td></tr></table></figure>
<h4 id="一些问题:-4"><a href="#一些问题:-4" class="headerlink" title="一些问题:"></a>一些问题:</h4><ol>
<li><p>微调时直接迭代卷积基最后三层,设置他们的trainable属性为True也可以吧:</p>
<figure class="highlight python"><table><tr><td class="code"><pre><span class="line">fine_tune_at=<span class="number">-3</span></span><br><span class="line"><span class="keyword">for</span> layer <span class="keyword">in</span> conv_base.layers[fine_tune_at:]:</span><br><span class="line"> layer.trainable=<span class="literal">True</span></span><br></pre></td></tr></table></figure>
</li>
</ol>
<h3 id=""><a href="#" class="headerlink" title=""></a></h3><h3 id="常见预训练网络及其使用"><a href="#常见预训练网络及其使用" class="headerlink" title="常见预训练网络及其使用"></a>常见预训练网络及其使用</h3><p>Xception.VGG16,VGG19,ResNet50,InceptionV3,InceptionResNetV2,</p>
<p>MobileNet,MobileNetV2,DenseNet121,DenseNet169,DenseNet201,NASNetMobile,NASNetLarge等。</p>
<p>Xception只支持channels_last的维度顺序(h,w,c)默认输入图片尺寸为299x299x3,以Xception的使用为例:</p>
<figure class="highlight python"><table><tr><td class="code"><pre><span class="line"><span class="keyword">import</span> tensorflow <span class="keyword">as</span> tf</span><br><span class="line"><span class="keyword">from</span> tensorflow <span class="keyword">import</span> keras</span><br><span class="line"><span class="keyword">from</span> tensorflow.keras <span class="keyword">import</span> layers</span><br><span class="line"><span class="keyword">import</span> matplotlib.pyplot <span class="keyword">as</span> plt</span><br><span class="line">%matplotlib inline</span><br><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> glob</span><br><span class="line"></span><br><span class="line"><span class="comment">#获取数据集图片路径和标签</span></span><br><span class="line">train_image_path=glob.glob(<span class="string">'./dc/train/*/*.jpg'</span>)</span><br><span class="line">train_image_label=[int(p.split(<span class="string">'\\'</span>).[<span class="number">1</span>]==<span class="string">'cats'</span>) <span class="keyword">for</span> p <span class="keyword">in</span> train_image_path]</span><br><span class="line"><span class="comment">#图片和标签预处理</span></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">load_preprosess_image</span><span class="params">(path,label)</span>:</span></span><br><span class="line"> img=tf.io.read_file(path)</span><br><span class="line"> img=tf.image.decode_jpeg(img,channels=<span class="number">3</span>)</span><br><span class="line"> img=tf.image.resize(img,[<span class="number">360</span>,<span class="number">360</span>])</span><br><span class="line"> img=tf.image.random_crop(img,[<span class="number">256</span>,<span class="number">256</span>,<span class="number">3</span>]) <span class="comment">#图像增强</span></span><br><span class="line"> img=tf.image.random_flip_left_right(img) <span class="comment">#图像增强</span></span><br><span class="line"> img=tf.image.random_flip_up_down(img) <span class="comment">#图像增强</span></span><br><span class="line"> img=tf.image.random_brightness(img,<span class="number">0.5</span>) <span class="comment">#图像增强</span></span><br><span class="line"> img=tf.image.random_contrast(img,<span class="number">0</span>,<span class="number">1</span>) <span class="comment">#图像增强</span></span><br><span class="line"> img=tf.cast(img,tf.float32)</span><br><span class="line"> img=img/<span class="number">255</span> <span class="comment">#tf.image.convert_image_dtype()函数会自动把非float型图片归一化</span></span><br><span class="line"> <span class="comment">#将标签列表转换一下:[1,2,3]----->[[1],[2],[3]]</span></span><br><span class="line"> label=tf.reshape(label,[<span class="number">1</span>]) <span class="comment">#reshape成一维数据,其实就是把一个普通整数转换成Tensor吧?</span></span><br><span class="line"> <span class="keyword">return</span> img,label <span class="comment">#这里如果label是普通整数就不行吗?</span></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">load_preprosess_test_image</span><span class="params">(path,label)</span>:</span></span><br><span class="line"> img=tf.io.read_file(path)</span><br><span class="line"> img=tf.image.decode_jpeg(img,channels=<span class="number">3</span>)</span><br><span class="line"> img=tf.image.resize(img,[<span class="number">256</span>,<span class="number">256</span>])</span><br><span class="line"> img=tf.cast(img,tf.float32)</span><br><span class="line"> img=img/<span class="number">255</span> <span class="comment">#tf.image.convert_image_dtype()函数会自动把非float型图片归一化</span></span><br><span class="line"> <span class="comment">#将标签列表转换一下:[1,2,3]----->[[1],[2],[3]]</span></span><br><span class="line"> label=tf.reshape(label,[<span class="number">1</span>]) <span class="comment">#reshape成一维数据,其实就是把一个普通整数转换成Tensor吧?</span></span><br><span class="line"> <span class="keyword">return</span> img,label <span class="comment">#这里如果label是普通整数就不行吗?</span></span><br><span class="line"><span class="comment">#创建Dataset</span></span><br><span class="line">train_ds=tf.data.Dataset.from_tensor_slices((train_image_path,train_image_label))</span><br><span class="line">AUTOTUNE=tf.data.experimental.AUTOTUNE</span><br><span class="line">train_ds=train_ds.map(load_preprosess_image,num_parallel_calls=AUTOTUNE)<span class="comment">#同时设置map所用资源</span></span><br><span class="line"><span class="comment">#设置Dataset</span></span><br><span class="line">BATCH_SIZE=<span class="number">32</span></span><br><span class="line">train_count=len(train_image_path)</span><br><span class="line">train_ds=train_ds.repeat(<span class="number">-1</span>).shuffle(train_count).batch(BATCH_SIZE) </span><br><span class="line">train_ds=train_ds.prefetch(AUTOTUNE) <span class="comment">#设置后台预读取图片所分配的计算资源(自动)</span></span><br><span class="line"></span><br><span class="line"><span class="comment">#添加验证数据集</span></span><br><span class="line">test_image_path=glob.glob(<span class="string">'./dc/test/*/*.jpg'</span>)</span><br><span class="line">test_image_label=[int(p.split(<span class="string">'\\'</span>).[<span class="number">1</span>]==<span class="string">'cats'</span>) <span class="keyword">for</span> p <span class="keyword">in</span> test_image_path]</span><br><span class="line">test_count=len(test_image_path)</span><br><span class="line">test_ds=tf.data.Dataset.from_tensor_slices((test_image_path,tset_image_label))</span><br><span class="line">test_ds=test_ds.map(load_preprosess_test_image,num_parallel_calls=AUTOTUNE)</span><br><span class="line">test_ds=test_ds.batch(BATCH_SIZE)</span><br><span class="line">test_ds=test_ds.prefetch(AUTOTUNE) </span><br><span class="line"></span><br><span class="line"><span class="comment">##定义模型(基于Xception)</span></span><br><span class="line"><span class="comment">#获取预训练的经典模型</span></span><br><span class="line">conv_base=keras.applications.xception.Xception(weights=<span class="string">'imagenet'</span>, <span class="comment">#指定权重来源</span></span><br><span class="line"> include_top=<span class="literal">False</span>,<span class="comment">#指定是否包含卷积基后面的分类器</span></span><br><span class="line"> input_shape=(<span class="number">256</span>,<span class="number">256</span>,<span class="number">3</span>),<span class="comment">#指定输入尺寸</span></span><br><span class="line"> pooling=<span class="string">'avg'</span>) <span class="comment">#指定是否包含最后的全局平均池化层</span></span><br><span class="line">conv_base.trainable=<span class="literal">False</span> <span class="comment">#冻结卷积基的权重,冻结前后网络的可训练参数量是不同的</span></span><br><span class="line">conv_base.summary() <span class="comment">#查看卷积基参数</span></span><br><span class="line"><span class="comment">#在预训练卷积基的基础上定义自己的模型</span></span><br><span class="line">model=keras.Sequential()</span><br><span class="line">model.add(conv_base) <span class="comment">#首先添加预训练卷积基</span></span><br><span class="line"><span class="comment">#model.add(layers.GlobalAveragePooling2D())</span></span><br><span class="line">model.add(layers.Dense(<span class="number">1024</span>,activation=<span class="string">'relu'</span>))</span><br><span class="line">model.add(layers.BatchNormalization())</span><br><span class="line">model.add(layers.Dense(<span class="number">256</span>,activation=<span class="string">'relu'</span>))</span><br><span class="line">model.add(layers.BatchNormalization())</span><br><span class="line">model.add(layers.Dense(<span class="number">1</span>),activation=<span class="string">'sigmoid'</span>)</span><br><span class="line">model.summary() <span class="comment">#查看模型参数</span></span><br><span class="line"></span><br><span class="line"><span class="comment">#编译模型</span></span><br><span class="line">model.compile(optimizer=keras.optimizers.Adam(lr=<span class="number">0.0005</span>),</span><br><span class="line"> loss=<span class="string">'binary_crossentropy'</span>, <span class="comment">#输出层没有经过sigmoid激活,也可以用默认的二元交叉熵?</span></span><br><span class="line"> metrics=[<span class="string">'acc'</span>])</span><br><span class="line"><span class="comment">#训练模型</span></span><br><span class="line">history=model.fit(</span><br><span class="line"> train_ds,</span><br><span class="line"> steps_per_epochs=train_count//BATCH_SIZE,</span><br><span class="line"> epochs=<span class="number">5</span>,</span><br><span class="line"> validation_data=test_ds,</span><br><span class="line"> validation_steps=test_count//BATCH_SIZE</span><br><span class="line">)</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">conv_base.trainable=<span class="literal">True</span></span><br><span class="line"><span class="comment">#再冻结至倒数第33层(逐层操作)</span></span><br><span class="line">fine_tune_at=<span class="number">-33</span></span><br><span class="line"><span class="keyword">for</span> layer <span class="keyword">in</span> conv_base.layers[:fine_tune_at]:</span><br><span class="line"> layer.trainable=<span class="literal">False</span></span><br><span class="line"><span class="comment">#重新编译模型(调低学习率)</span></span><br><span class="line">model.compile(optimizer=keras.optimizers.Adam(lr=<span class="number">0.0005</span>/<span class="number">10</span>),</span><br><span class="line"> loss=<span class="string">'binary_crossentropy'</span>, <span class="comment">#输出层没有经过sigmoid激活,也可以用默认的二元交叉熵?</span></span><br><span class="line"> metrics=[<span class="string">'acc'</span>])</span><br><span class="line"><span class="comment">#再次训练模型</span></span><br><span class="line">initial_epochs=<span class="number">5</span></span><br><span class="line">fine_tune_epochs=<span class="number">5</span></span><br><span class="line">total_epochs=initial_epochs+fine_tune_epochs</span><br><span class="line">history=model.fit(</span><br><span class="line"> train_ds,</span><br><span class="line"> steps_per_epochs=train_count//BATCH_SIZE,</span><br><span class="line"> epochs=total_epochs, <span class="comment">#指定总轮数</span></span><br><span class="line"> initial_epochs=initial_epochs, <span class="comment">#指定已训练轮数(初始轮数)</span></span><br><span class="line"> validation_data=test_ds,</span><br><span class="line"> validation_steps=test_count//BATCH_SIZE</span><br><span class="line">)</span><br></pre></td></tr></table></figure>
<h3 id="多输出模型实例"><a href="#多输出模型实例" class="headerlink" title="多输出模型实例"></a>多输出模型实例</h3><p>所谓多输出,指的是同时输出目标的多个属性,比如衣服的形状和颜色。</p>
<figure class="highlight python"><table><tr><td class="code"><pre><span class="line"><span class="keyword">import</span> tensorflow <span class="keyword">as</span> tf</span><br><span class="line"><span class="keyword">from</span> tensorflow <span class="keyword">import</span> keras</span><br><span class="line"><span class="keyword">from</span> tensorflow.keras <span class="keyword">import</span> layers</span><br><span class="line"><span class="keyword">import</span> matplotlib.pyplot <span class="keyword">as</span> plt</span><br><span class="line">%matplotlib inline</span><br><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> pathlib <span class="comment">#这次没用glob</span></span><br><span class="line"><span class="keyword">import</span> random</span><br><span class="line"></span><br><span class="line"><span class="comment">##加载数据</span></span><br><span class="line">data_dir=<span class="string">'./dataset/moc'</span></span><br><span class="line">data_root=pathlib.Path(data_dir) <span class="comment">#获取data_dir的所有子目录</span></span><br><span class="line">all_image_paths=list(data_root.glob(<span class="string">'*/*'</span>)) <span class="comment">#获取所有图片路径(此时还不是字符串类型)</span></span><br><span class="line">image_count=len(all_image_paths)</span><br><span class="line">all_image_paths=[str(path) <span class="keyword">for</span> path <span class="keyword">in</span> all_image_paths] <span class="comment">#列表推导式:将图片路径转换为字符串类型</span></span><br><span class="line">random.shuffle(all_image_paths) <span class="comment">#乱序</span></span><br><span class="line"><span class="comment">#打标签</span></span><br><span class="line">lable_names=sorted(item.name <span class="keyword">for</span> item <span class="keyword">in</span> data_root.glob(<span class="string">'*/'</span>) <span class="keyword">if</span> item.is_dir())<span class="comment">#获取子文件夹名,即标签</span></span><br><span class="line">color_label_names=set(name.split(<span class="string">'_'</span>)[<span class="number">0</span>] <span class="keyword">for</span> name <span class="keyword">in</span> lable_names) <span class="comment">#获取颜色标签</span></span><br><span class="line">item_label_names=set(name.split(<span class="string">'_'</span>)[<span class="number">1</span>] <span class="keyword">for</span> name <span class="keyword">in</span> lable_names) <span class="comment">#获取类型标签</span></span><br><span class="line">color_label_to_index=dict((name,index) <span class="keyword">for</span> index,name <span class="keyword">in</span> enumerate(color_label_names)) <span class="comment">#生成索引字典</span></span><br><span class="line">item_label_to_index=dict((name,index) <span class="keyword">for</span> index,name <span class="keyword">in</span> enumerate(item_label_names))</span><br><span class="line">all_image_labels=[pathlib.Path(path).parent.name <span class="keyword">for</span> path <span class="keyword">in</span> all_image_paths]<span class="comment">#获取每个图片对应的标签(所在文件夹名称)</span></span><br><span class="line">color_labels=[color_label_to_index[label.split(<span class="string">'_'</span>)[<span class="number">0</span>]] <span class="keyword">for</span> label <span class="keyword">in</span> all_image_labels]<span class="comment">#取出颜色名并转换为编码值</span></span><br><span class="line">item_labels=[item_label_to_index[label.split(<span class="string">'_'</span>)[<span class="number">1</span>]] <span class="keyword">for</span> label <span class="keyword">in</span> all_image_labels]<span class="comment">#取出类型名并转换为编码值</span></span><br><span class="line"><span class="comment">#预处理函数</span></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">load_preprosess_image</span><span class="params">(path)</span>:</span></span><br><span class="line"> img=tf.io.read_file(path)</span><br><span class="line"> img=tf.image.decode_jpeg(img,channels=<span class="number">3</span>)</span><br><span class="line"> img=tf.image.resize(img,[<span class="number">224</span>,<span class="number">224</span>])</span><br><span class="line"> img=tf.cast(img,tf.float32)</span><br><span class="line"> img=img/<span class="number">255.0</span> <span class="comment">#归一化[0,1]</span></span><br><span class="line"> img=<span class="number">2</span>*img<span class="number">-1</span> <span class="comment">#映射到[-1,1]</span></span><br><span class="line"> <span class="keyword">return</span> img</span><br><span class="line"></span><br><span class="line"><span class="comment">#**测试,显示图片</span></span><br><span class="line">img_path=all_image_paths[<span class="number">0</span>]</span><br><span class="line">label=all_image_labels[<span class="number">0</span>]</span><br><span class="line">plt.imshow((load_preprosess_image(img_path)+<span class="number">1</span>)/<span class="number">2</span>)</span><br><span class="line">plt.grid(<span class="literal">False</span>)</span><br><span class="line">plt.xlabel(label)</span><br><span class="line">print()</span><br><span class="line"></span><br><span class="line"><span class="comment">#创建Dataset</span></span><br><span class="line">path_ds=tf.data.Dataset.from_tensor_slices(all_image_paths) <span class="comment">#图片</span></span><br><span class="line">AUTOTUNE=tf.data.experimental.AUTOTUNE</span><br><span class="line">image_ds=path_ds.map(load_preprosess_image,num_parallel_calls=AUTOTUNE)<span class="comment">#同时设置map所用资源</span></span><br><span class="line">label_ds=tf.data.Dataset.from_tensor_slices((color_labels,item_labels)) <span class="comment">#标签</span></span><br><span class="line"><span class="comment">#关联图片和标签Dataset</span></span><br><span class="line">image_label_ds=tf.data.Dataset.zip((image_ds,label_ds))</span><br><span class="line"><span class="comment">#划分数据集</span></span><br><span class="line">test_count=int(image_count*<span class="number">0.2</span>)</span><br><span class="line">train_count=image_count-test_count</span><br><span class="line">train_data=image_label_ds.skip(test_count)</span><br><span class="line">test_data=image_label_ds.take(test_count)</span><br><span class="line"><span class="comment">#设置Dataset</span></span><br><span class="line">BATCH_SIZE=<span class="number">32</span></span><br><span class="line">train_data=train_data.repeat(<span class="number">-1</span>).shuffle(buffer_size=train_count).batch(BATCH_SIZE)</span><br><span class="line">train_data=train_data.prefetch(AUTOTUNE) <span class="comment">#设置后台预读取图片所分配的计算资源(自动)</span></span><br><span class="line">test_data=test_data.batch(BATCH_SIZE)</span><br><span class="line"></span><br><span class="line"><span class="comment">##定义模型(函数式API方法)</span></span><br><span class="line">mobile_net=keras.applications.MobileNetV2(input_shape=(<span class="number">224</span>,<span class="number">224</span>,<span class="number">3</span>),</span><br><span class="line"> include_top=<span class="literal">False</span>)<span class="comment">#没有使用预训练权重</span></span><br><span class="line">inputs=keras.Input(shape=(<span class="number">224</span>,<span class="number">224</span>,<span class="number">3</span>))</span><br><span class="line">x=mobile_net(inputs)</span><br><span class="line">x=layers.GlobalAveragePooling2D()(x) <span class="comment">#先生成函数对象,再调用</span></span><br><span class="line">x.get_shape() <span class="comment">#**看看形状</span></span><br><span class="line">x1=layers.Dense(<span class="number">1024</span>,activation=<span class="string">'relu'</span>)(x)</span><br><span class="line">output_color=layers.Dense(len(color_label_names),</span><br><span class="line"> activation=<span class="string">'softmax'</span>,</span><br><span class="line"> name=<span class="string">'output_color'</span>)(x1)<span class="comment">#name参数用于后期指定loss_fn</span></span><br><span class="line">x2=layers.Dense(<span class="number">1024</span>,activation=<span class="string">'relu'</span>)(x)</span><br><span class="line">output_item=layers.Dense(len(color_label_names),</span><br><span class="line"> activation=<span class="string">'softmax'</span>,</span><br><span class="line"> name=<span class="string">'output_item'</span>)(x2)</span><br><span class="line">model=keras.Model(inputs=inputs,</span><br><span class="line"> outputs=[output_color,output_item])<span class="comment">#这里的label顺序要跟创建Dataset时的顺序一致吧</span></span><br><span class="line"><span class="comment">#查看模型参数</span></span><br><span class="line">model.summary()</span><br><span class="line"></span><br><span class="line"><span class="comment">##编译模型(多输出对应多个loss)</span></span><br><span class="line">mobel.compile(optimizer=keras.optimizers.Adam(learning_rate=<span class="number">0.0001</span>,</span><br><span class="line"> loss={<span class="string">'output_color'</span>:<span class="string">'sparse_categorical_crossentropy'</span>,</span><br><span class="line"> <span class="string">'output_item'</span>:<span class="string">'sparse_categorical_crossentropy'</span>} ,<span class="comment">#如果loss_fn都相同,也可直接用loss='spares_categorical_crossentropy'</span></span><br><span class="line"> metrics=[<span class="string">'acc'</span>]</span><br><span class="line"> )</span><br><span class="line"><span class="comment">#训练模型</span></span><br><span class="line">train_steps=train_count//BATCH_SIZE</span><br><span class="line">test_steps=test_count//BATCH_SIZE</span><br><span class="line">history=model.fit(train_data,</span><br><span class="line"> epochs=<span class="number">15</span>,</span><br><span class="line"> steps_per_epochs=train_steps,</span><br><span class="line"> validation_data=test_data,</span><br><span class="line"> validation_steps=test_steps)</span><br><span class="line"> </span><br><span class="line"><span class="comment">##评价模型(使用model的evaluate()方法)</span></span><br><span class="line"><span class="comment">#此处代码使用的数据集与上面代码不同,将图片与标签分开了!</span></span><br><span class="line">model.evaluate(test_image_array,[test_color_labels,tast_item_labels],</span><br><span class="line"> verbose=<span class="number">0</span>)</span><br><span class="line"><span class="comment">#使用模型预测</span></span><br><span class="line"><span class="comment">#此处代码使用的数据集与上面代码不同,没有定义index_to_color,index_to_item</span></span><br><span class="line">path=<span class="string">'一个图片的路径'</span></span><br><span class="line">img=load_preprosess_image(path)<span class="comment">#预处理,此时shape:(224,224,3)</span></span><br><span class="line">img=np.expand_dims(img,<span class="number">0</span>) <span class="comment">#在第0个维度扩张一个维度才能作为模型输入,此时shape:(1,224,224,3),此处也可以用tf.expand_dims(img,0)</span></span><br><span class="line">pred=model.predict(img) <span class="comment">#结果不是二维数据?</span></span><br><span class="line"><span class="comment">#pred=model(img,training=False) #也可以</span></span><br><span class="line">index_to_color={index:color <span class="keyword">for</span> color,index <span class="keyword">in</span> color_to_index.items()}</span><br><span class="line">index_to_item={index:item <span class="keyword">for</span> item,index <span class="keyword">in</span> item_to_index.items()} <span class="comment">#这是啥?字典推导式?</span></span><br><span class="line">pred_color=index_to_color[np.argmax(pred[<span class="number">0</span>][<span class="number">0</span>])] <span class="comment">#argmax函数的参数是?</span></span><br><span class="line">pred_item=index_to_item[np.argmax(pred[<span class="number">1</span>][<span class="number">0</span>])]</span><br></pre></td></tr></table></figure>
<h4 id="一些问题:-5"><a href="#一些问题:-5" class="headerlink" title="一些问题:"></a>一些问题:</h4><ol>
<li><p><code>tf.data.Dataset.zip((image_ds,label_ds))</code>就保证了标签Dataset和图片Dataset的一一对应?如果直接把图片和两种标签装进Dataset行不行?就像<code>tf.data.Dataset.from_tensor_slices((all_image_paths,color_labels,item_labels))</code>?这样不好做映射,而且不知道能不能把图片和标签明确分开,因为有三列数据。</p>
</li>
<li><p>为什么要把图片像素值映射到[-1,1]?是模型的要求?还是有什么好处?</p>
</li>
<li><p>Dataset的repeat方法到底是不是个开关?参数是重复的次数?默认是-1表示一直重复?</p>
</li>
<li><p><code>pred=model.predict(img) #结果不是二维数据?
index_to_color={index:color for color,index in color_to_index.items()}
index_to_item={index:item for item,index in item_to_index.items()} #这是啥?字典推导式?
pred_color=index_to_color[np.argmax(pred[0][0])] #argmax函数的参数是?
pred_item=index_to_item[np.argmax(pred[1][0])]</code>这里求最大值索引是怎么操作的?为什么还要指定第二维的下标?</p>
</li>
<li><p><code>pred=model(img,training=False)</code>中training设置为True的话,将使用当前Batch的均值和方差作为批标准化参数,设为False的话将采用整个训练集的均值和方差作为批标准化参数。</p>
</li>
</ol>
<h3 id="模型的保存"><a href="#模型的保存" class="headerlink" title="模型的保存"></a>模型的保存</h3><h4 id="保存整体模型"><a href="#保存整体模型" class="headerlink" title="保存整体模型"></a>保存整体模型</h4><p>把整个模型保存到一个文件,其中包含<strong>权重、模型配置(架构)和优化器配置</strong>。有了这样的一个文件,之后就可以从保存时的状态开始,继续训练,不需要原始代码。</p>
<p>在Keras中保存完整模型后,可以在TensorFlow.js中加载,然后在网络浏览器中训练和运行。</p>
<p>Keras使用HDF5标准提供基本的保存格式。</p>
<figure class="highlight python"><table><tr><td class="code"><pre><span class="line"><span class="keyword">import</span> tensorflow <span class="keyword">as</span> tf</span><br><span class="line"><span class="comment">#...假设模型已经训练好了...</span></span><br><span class="line"><span class="comment">#保存整个模型</span></span><br><span class="line">model.save(<span class="string">'./my_models/my_model.h5'</span>) <span class="comment">#指明保存路径即可</span></span><br><span class="line"><span class="comment">#加载模型</span></span><br><span class="line">new_model=tf.keras.models.load_model(<span class="string">'./my_models/my_model.h5'</span>)</span><br><span class="line"><span class="comment">#此后就可以像使用之前的模型一样进行训练、预测、评价了</span></span><br></pre></td></tr></table></figure>
<h4 id="仅保存模型架构"><a href="#仅保存模型架构" class="headerlink" title="仅保存模型架构"></a>仅保存模型架构</h4><p>仅保存模型结构,不保存权重和优化器配置,这时把网络结构保存为json文件即可。</p>
<figure class="highlight python"><table><tr><td class="code"><pre><span class="line"><span class="keyword">import</span> tensorflow <span class="keyword">as</span> tf</span><br><span class="line"><span class="comment">#...假设模型已经训练好了...</span></span><br><span class="line"><span class="comment">#仅保存模型架构</span></span><br><span class="line">json_config=model.to_json() <span class="comment">#json文件如何保存?此处的返回对象有相应方法?</span></span><br><span class="line"><span class="comment">#可以这样保存到磁盘吗?</span></span><br><span class="line"><span class="keyword">with</span> open(<span class="string">'./my_model.json'</span>) <span class="keyword">as</span> f:</span><br><span class="line"> f.write(json_config)</span><br><span class="line"><span class="comment">#恢复(重建)模型</span></span><br><span class="line">reinitialized_model=tf.keras.models.model_from_json(json_config)</span><br><span class="line"><span class="comment">#此时恢复获得的模型仅仅有定义,需要进行后续的编译和训练</span></span><br></pre></td></tr></table></figure>
<p>一些问题:</p>
<ol>
<li>如何将json内容保存到磁盘?</li>
</ol>
<h4 id="仅保存权重"><a href="#仅保存权重" class="headerlink" title="仅保存权重"></a>仅保存权重</h4><p>有时候只需要权重就够了,因为有源代码,不需要保存模型结构,这时可通过get_weights()获取权重值,并通过set_weights()设置权重值。</p>
<figure class="highlight python"><table><tr><td class="code"><pre><span class="line"><span class="keyword">import</span> tensorflow <span class="keyword">as</span> tf</span><br><span class="line"><span class="comment">#...假设模型已经训练好了...</span></span><br><span class="line"><span class="comment">#...假设模型reinitialized_model仅包含model的架构...</span></span><br><span class="line"><span class="comment">#取出模型权重</span></span><br><span class="line">weights=model.get_weights() </span><br><span class="line"><span class="comment">#加载取出的权重</span></span><br><span class="line">reinitialized_model.set_weights(weights)</span><br><span class="line"></span><br><span class="line"><span class="comment">#仅保存模型权重到磁盘</span></span><br><span class="line">model.save_weights(<span class="string">'./model_weights/my_weights.h5'</span>) <span class="comment">#保存到磁盘</span></span><br><span class="line"><span class="comment">#加载磁盘中的权重</span></span><br><span class="line">reinitialized_model.load_weights(<span class="string">'./model_weights/my_weights.h5'</span>)</span><br></pre></td></tr></table></figure>
<h4 id="用回调函数保存检查点"><a href="#用回调函数保存检查点" class="headerlink" title="用回调函数保存检查点"></a>用回调函数保存检查点</h4><p>在训练期间或者训练结束时自动保存检查点,这样就可以使用经过训练的模型,从上次暂停的状态继续训练,这么做能应对训练过程意外中断的状况。</p>
<p>用回调函数实现上述功能:tf.keras.callbacks.ModelCheckpoint()</p>
<p><strong>该回调函数保存的检查点只有一个?就是符合设定条件的那一个?提供的路径一定要包含文件名吗?还是说所谓的路径,其最后一级表示文件名前缀?</strong></p>
<figure class="highlight python"><table><tr><td class="code"><pre><span class="line"><span class="keyword">import</span> tensorflow <span class="keyword">as</span> tf</span><br><span class="line"></span><br><span class="line">checkpoint_path=<span class="string">'./cp/cp.cpkt'</span> <span class="comment">#这个文件名有固定格式吗?没有,会以cp.cpkt为前缀,自动加新的后缀</span></span><br><span class="line">cp_callback=tf.keras.callbacks.ModelCheckpoint(checkpoint_path,</span><br><span class="line"> save_weights_only=<span class="literal">True</span>) <span class="comment">#该函数对象有很多可设置参数,包括monitor='val_loss',verbose=0,save_best_only=False,save_weights_only=False,mode='auto',save_freq='epoch'</span></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">model.fit(train_image,</span><br><span class="line"> train_label,</span><br><span class="line"> epochs=<span class="number">3</span>,</span><br><span class="line"> callbacks=[cp_callback])</span><br><span class="line"><span class="comment">#加载保存的检查点中的权重</span></span><br><span class="line">model.load_weights(checkpoint_path) <span class="comment">#不需要指定哪一个检查点?其实只有一个检查点?</span></span><br></pre></td></tr></table></figure>
<h3 id="在自定义训练中保存检查点"><a href="#在自定义训练中保存检查点" class="headerlink" title="在自定义训练中保存检查点"></a>在自定义训练中保存检查点</h3><p>基于tf.train.Checkpoint类</p>
<figure class="highlight python"><table><tr><td class="code"><pre><span class="line"><span class="keyword">import</span> tensorflow <span class="keyword">as</span> tf</span><br><span class="line"><span class="keyword">import</span> os</span><br><span class="line"><span class="comment">#.......假设自定义训练的一系列准备均已完成.........</span></span><br><span class="line"></span><br><span class="line"><span class="comment">#定义检查点保存文件的前缀</span></span><br><span class="line">cp_dir=<span class="string">'./customtrain_cp'</span></span><br><span class="line">cp_prefix=os.path.join(cp_dir,<span class="string">'ckpt'</span>)</span><br><span class="line"><span class="comment">#初始化检查点对象</span></span><br><span class="line">checkpoint=tf.train.Checkpoint(optimizer=optimizer,</span><br><span class="line"> model=model) <span class="comment">#指定要保存的项目,optimizer是自定义训练时的优化器,这里要保存它在训练结束时的状态;model指的是定义的模型,包含了结构和权重</span></span><br><span class="line"></span><br><span class="line"><span class="comment">#在train()函数中加入检查点保存过程</span></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">train</span><span class="params">()</span>:</span></span><br><span class="line"> <span class="keyword">for</span> epoch <span class="keyword">in</span> range(<span class="number">10</span>):</span><br><span class="line"> <span class="keyword">for</span> (batch,(images,labels)) <span class="keyword">in</span> enumerate(dataset):</span><br><span class="line"> train_step(model,images,labels)</span><br><span class="line"> print(<span class="string">'Epoch {} loss is {},accuracy is {}'</span>.format(epoch,</span><br><span class="line"> train_loss.result(),</span><br><span class="line"> train_accuracy.result()))</span><br><span class="line"> train_loss.reset_states()</span><br><span class="line"> train_accuracy.reset_states()</span><br><span class="line"> </span><br><span class="line"> <span class="keyword">for</span> (batch,(images,labels)) <span class="keyword">in</span> enumerate(test_dataset):</span><br><span class="line"> test_step(model,images,labels)</span><br><span class="line"> print(<span class="string">'Epoch {} test_loss is {},test_accuracy is {}'</span>.format(epoch,</span><br><span class="line"> test_loss.result(),</span><br><span class="line"> test_accuracy.result()))</span><br><span class="line"> test_loss.reset_states()</span><br><span class="line"> test_accuracy.reset_states()</span><br><span class="line"> <span class="comment">#在epoch的结尾设置检查点进行信息保存</span></span><br><span class="line"> <span class="keyword">if</span> (epoch+<span class="number">1</span>)%<span class="number">2</span>==<span class="number">0</span>:</span><br><span class="line"> checkpoint.save(file_perfix=cp_prefix)</span><br><span class="line"></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">checkpoint.restore(tf.train.latest_checkpoint(cp_dir)) <span class="comment">#获取最新的检查点,并加载到检查点对象</span></span><br><span class="line"><span class="comment">#用恢复的模型预测</span></span><br><span class="line">pred=tf.argmax(model(train_image,training=<span class="literal">False</span>),axis=<span class="number">-1</span>).numpy() <span class="comment">#这里模型输出是二维数组,axis=-1表示在最后一个(第二个)维度上求最大值索引</span></span><br><span class="line">(pred==train_label).sum()/len(train_label) <span class="comment">#正确率</span></span><br></pre></td></tr></table></figure>
<h3 id="图像定位(略过)"><a href="#图像定位(略过)" class="headerlink" title="图像定位(略过)"></a>图像定位(略过)</h3><h3 id="自动图运算"><a href="#自动图运算" class="headerlink" title="自动图运算"></a>自动图运算</h3><p>@tf.function 可以适当提升性能</p>
<p>AutoGraph起到了类似编译器的作用,能通过更加自然的Python控制流轻松地构建带有条件或循环的计算图,而无需手动使用TensorFlow的API进行构建。</p>
<p>dan当定义了多个函数来实现不同的运算时,只需要再最后第哦啊用的函数上添加@tf.function 装饰器即可,说有的运算节点都会被编译。</p>
<figure class="highlight python"><table><tr><td class="code"><pre><span class="line"><span class="comment">#对自定义的预处理函数添加装饰</span></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">func1</span><span class="params">(path)</span>:</span></span><br><span class="line"> <span class="comment">#...</span></span><br><span class="line"> <span class="comment">#...</span></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">func2</span><span class="params">(img)</span>:</span></span><br><span class="line"> <span class="comment">#...</span></span><br><span class="line"> <span class="comment">#...</span></span><br><span class="line"></span><br><span class="line"><span class="meta">@tf.function </span></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">load_img</span><span class="params">(path)</span>:</span></span><br><span class="line"> img=func1(path)</span><br><span class="line"> img=func2(img)</span><br><span class="line"> <span class="keyword">return</span> img</span><br><span class="line"><span class="comment">#之后model.fit函数会自动进入自动图运算模式</span></span><br><span class="line"></span><br><span class="line"><span class="comment">#如果是自定义循环,只需在train_step函数前加装饰</span></span><br><span class="line"><span class="meta">@tf.function </span></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">train_step</span><span class="params">(img,label)</span>:</span></span><br><span class="line"> <span class="comment">#...</span></span><br><span class="line"> <span class="comment">#...</span></span><br></pre></td></tr></table></figure>
<h3 id="GPU的使用与分配"><a href="#GPU的使用与分配" class="headerlink" title="GPU的使用与分配"></a>GPU的使用与分配</h3><h4 id="获取当前主机上的运算设备列表"><a href="#获取当前主机上的运算设备列表" class="headerlink" title="获取当前主机上的运算设备列表"></a>获取当前主机上的运算设备列表</h4><figure class="highlight python"><table><tr><td class="code"><pre><span class="line"><span class="keyword">import</span> tensorflow <span class="keyword">as</span> tf</span><br><span class="line"></span><br><span class="line">gpus=tf.config.experimental.list_physical_devices(device_type=<span class="string">'GPU'</span>)<span class="comment">#列出所有GPU</span></span><br><span class="line">cpus=tf.config.experimental.list_physical_devices(device_type=<span class="string">'CPU'</span>)<span class="comment">#CPU</span></span><br><span class="line">tf.config.experimental.set_visible_devices(devices=gpus[<span class="number">0</span>:<span class="number">2</span>],device_type=<span class="string">'GPU'</span>)<span class="comment">#设置可见范围</span></span><br></pre></td></tr></table></figure>
<h4 id="设置显存使用策略"><a href="#设置显存使用策略" class="headerlink" title="设置显存使用策略"></a>设置显存使用策略</h4><ol>
<li>仅在需要时申请显存空间(动态申请)</li>
<li>限制为消耗固定大小的显存,超出会报错</li>
</ol>
<figure class="highlight python"><table><tr><td class="code"><pre><span class="line"><span class="keyword">import</span> tensorflow <span class="keyword">as</span> tf</span><br><span class="line"></span><br><span class="line">gpus=tf.config.experimental.list_physical_devices(device_type=<span class="string">'GPU'</span>) <span class="comment">#列出所有GPU</span></span><br><span class="line"><span class="keyword">for</span> gpu <span class="keyword">in</span> gpus:</span><br><span class="line"> tf.config.experimental.set_memory_growth(device=gpu,<span class="literal">True</span>) <span class="comment">#仅在需要时申请显存空间(动态申请)</span></span><br><span class="line">tf.config.experimental.set_virtual_device_configuration(gpus[<span class="number">0</span>],</span><br><span class="line"> [tf.config.experimental.VirtualDeviceConfiguration(memory_limit=<span class="number">1024</span>)]) <span class="comment">#限制为消耗固定大小的显存</span></span><br></pre></td></tr></table></figure>
<h3 id="图像语义分割(略过)"><a href="#图像语义分割(略过)" class="headerlink" title="图像语义分割(略过)"></a>图像语义分割(略过)</h3><h4 id="上采样方法"><a href="#上采样方法" class="headerlink" title="上采样方法"></a>上采样方法</h4><ol>
<li>插值</li>
<li>反池化</li>
<li>反卷积(转置卷积),最常用</li>
</ol>
<figure class="highlight python"><table><tr><td class="code"><pre><span class="line"><span class="comment">#反卷积实例(上采样为原来的2倍)</span></span><br><span class="line">x5=tf.keras.layers.Conv2DTranspose(<span class="number">128</span>,<span class="number">3</span>,</span><br><span class="line"> strides=<span class="number">2</span>,</span><br><span class="line"> padding=<span class="string">'same'</span>,</span><br><span class="line"> activation=<span class="string">'relu'</span>)(x4)</span><br></pre></td></tr></table></figure>
<h4 id="网络分支的实现"><a href="#网络分支的实现" class="headerlink" title="网络分支的实现"></a>网络分支的实现</h4><p>通过创建子模型获取网络中间层的输出。</p>
<p>要获取网络中某一层的输出,可以通过model.layers参数获得列表,再通过列表索引到具体的某一层;也可以通过名称找到某一个层,使用model.get_layer()方法。</p>
<p>对于一个层的输出,可以用my_layer.output属性。</p>
<figure class="highlight python"><table><tr><td class="code"><pre><span class="line"><span class="keyword">import</span> tensorflow <span class="keyword">as</span> tf</span><br><span class="line"></span><br><span class="line"><span class="comment">#假设已定义了一个骨干网络conv_base</span></span><br><span class="line"><span class="comment">#定义一个子模型,从骨干网络上引出分支</span></span><br><span class="line">layer_names=[<span class="string">'layer_name1'</span>,<span class="string">'layer_name2'</span>,<span class="string">'layer_name3'</span>,<span class="string">'layer_name4'</span>]</span><br><span class="line">layers_output=[conv_base.get_layer(name).output <span class="keyword">for</span> name <span class="keyword">in</span> layer_names]</span><br><span class="line">sub_multi_out_model=tf.keras.models.Model(imputs=conv_base.input,</span><br><span class="line"> outputs=layers_output)</span><br></pre></td></tr></table></figure>
<p>一些问题:</p>
<ol>
<li>一定要通过创建子模型的方式创建分支吗?不能直接通过函数式API在定义自己的模型时,用中间变量形成分支吗?应该是可以的,上述代码只是针对从预训练模型取得中间输出,因为预训练模型的结构已经定义好了,而自定义网络时,只能通过 “输入+子模型” 获得输出,无法获取子模型的中间输出,所以要要想获取子模型的中间输出,只好再定义一个子模型引出中间层输出。而如果是纯粹自定义的网络,就可以随时获得每一层的输出,只要用中间变量存储每层的输出就行了。</li>
</ol>
<h3 id="自定义层和自定义模型(暂时略过)"><a href="#自定义层和自定义模型(暂时略过)" class="headerlink" title="自定义层和自定义模型(暂时略过)"></a>自定义层和自定义模型(暂时略过)</h3><h3 id="UNET图像语义分割模型(暂时略过)"><a href="#UNET图像语义分割模型(暂时略过)" class="headerlink" title="UNET图像语义分割模型(暂时略过)"></a>UNET图像语义分割模型(暂时略过)</h3><h3 id="RNN循环神经网络简介"><a href="#RNN循环神经网络简介" class="headerlink" title="RNN循环神经网络简介"></a>RNN循环神经网络简介</h3><p>Keras支持RNN的各种变体:keras.layers.LSTM,keras.layers.GRU</p>
<h3 id="Keras-RNN-航空公司评论分类(正面or负面)"><a href="#Keras-RNN-航空公司评论分类(正面or负面)" class="headerlink" title="Keras-RNN-航空公司评论分类(正面or负面)"></a>Keras-RNN-航空公司评论分类(正面or负面)</h3><figure class="highlight python"><table><tr><td class="code"><pre><span class="line"><span class="keyword">import</span> tensorflow <span class="keyword">as</span> tf</span><br><span class="line"><span class="keyword">from</span> tensorflow <span class="keyword">import</span> keras</span><br><span class="line"><span class="keyword">from</span> tensorflow.keras <span class="keyword">import</span> layers</span><br><span class="line"><span class="keyword">import</span> matplotlib.pyplot <span class="keyword">as</span> plt</span><br><span class="line">%matplotlib inline</span><br><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> pandas <span class="keyword">as</span> pd</span><br><span class="line"><span class="keyword">import</span> re</span><br><span class="line"></span><br><span class="line"><span class="comment">#加载评论数据</span></span><br><span class="line">data=pd.read_csv(<span class="string">'Tweets.csv'</span>)</span><br><span class="line">data=data[[<span class="string">'airline_sentiment'</span>,<span class="string">'text'</span>]] <span class="comment">#取出其中两列</span></span><br><span class="line">data.airline_sentiment.unique() <span class="comment">#**查看有哪些评论性质</span></span><br><span class="line">data.airline_sentiment.value_counts() <span class="comment">#**查看每种性质的评论的数量</span></span><br><span class="line">data_p=data[data.airline_sentiment==<span class="string">'positive'</span>] <span class="comment">#取出满足特定条件的评论</span></span><br><span class="line">data_n=data[data.airline_sentiment==<span class="string">'negative'</span>]</span><br><span class="line">data_n=data_n.iloc[:len(data_p)] <span class="comment">#取出与data_p数量相同的负面评论</span></span><br><span class="line">data=pd.concat([data_n,data_p]) <span class="comment">#合并两组数据</span></span><br><span class="line"><span class="comment">##预处理</span></span><br><span class="line">data=data.sample(len(data)) <span class="comment">#乱序(用取样操作代替)</span></span><br><span class="line">data[<span class="string">'review'</span>]=(data.airline_sentiment==<span class="string">'positive'</span>).astype(<span class="string">'int'</span>) <span class="comment">#转换评论性质的标记(这里就是label)</span></span><br><span class="line"><span class="keyword">del</span> data[<span class="string">'airline_sentiment'</span>] <span class="comment">#删除原来的列(评论性质)</span></span><br><span class="line"><span class="comment">#取出匹配项,清除无关项,将评论转换为单词列表</span></span><br><span class="line">token=re.compile(<span class="string">'[A-Za-z]+|[!?,.()]'</span>) <span class="comment">#正则表达式(不止一个的字母 或者标点符号)</span></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">reg_text</span><span class="params">(text)</span>:</span> <span class="comment">#定义一个处理函数,找出一个字符串中的所有匹配项并小写化</span></span><br><span class="line"> new_text=token.findall(text) <span class="comment">#找到所有匹配项(按原来的顺序)</span></span><br><span class="line"> new_text=[word.lower() <span class="keyword">for</span> word <span class="keyword">in</span> new_text] <span class="comment">#转换为小写</span></span><br><span class="line"> <span class="keyword">return</span> new_text</span><br><span class="line">data.text[<span class="string">'text'</span>]=data.text.apply(reg_text) <span class="comment">#应用处理函数</span></span><br><span class="line"><span class="comment">#制作词表,将单词转换为索引值</span></span><br><span class="line">word_set=set()</span><br><span class="line"><span class="keyword">for</span> text <span class="keyword">in</span> data.text:</span><br><span class="line"> <span class="keyword">for</span> word <span class="keyword">in</span> text:</span><br><span class="line"> word_set.add(word)</span><br><span class="line"></span><br><span class="line"> word_list=list(word_set)</span><br><span class="line">word_index=dict((word,word_list.index(word)+<span class="number">1</span>) <span class="keyword">for</span> word <span class="keyword">in</span> word_list)</span><br><span class="line">data_ok=data.text.apply(<span class="keyword">lambda</span> x:[word_index.get(word,<span class="number">0</span>) <span class="keyword">for</span> word <span class="keyword">in</span> x]) <span class="comment">#转换为索引,查不到用0填充</span></span><br><span class="line"><span class="comment">#统一评论长度</span></span><br><span class="line">maxlen=max(len(x) <span class="keyword">for</span> x <span class="keyword">in</span> data_ok) <span class="comment">#最大评论长度</span></span><br><span class="line">max_word=len(word_set)+<span class="number">1</span> <span class="comment">#共有max_word个单词(含填充值0)</span></span><br><span class="line">data_ok=keras.preprocessing.sequence.pad_sequences(data_ok.values,maxlen) <span class="comment">#填充(这里就是模型输入)</span></span><br><span class="line"></span><br><span class="line"><span class="comment">##定义模型</span></span><br><span class="line">model=keras.Sequential()</span><br><span class="line">model.add(layers.Embedding(max_word,<span class="number">50</span>,input_length=maxlen)) <span class="comment">#Embedding:把文本映射为密集向量</span></span><br><span class="line">model.add(layers.LSTM(<span class="number">64</span>)) <span class="comment">#含有64个隐藏层单元</span></span><br><span class="line">model.add(layers.Dense(<span class="number">1</span>,activation=<span class="string">'sigmoid'</span>))</span><br><span class="line"><span class="comment">#查看模型参数</span></span><br><span class="line">model.summary()</span><br><span class="line"></span><br><span class="line"><span class="comment">#编译模型</span></span><br><span class="line">model.compile(optimizer=<span class="string">'adam'</span>,</span><br><span class="line"> loss=<span class="string">'binary_crossentropy'</span>,</span><br><span class="line"> metrics=[<span class="string">'acc'</span>])</span><br><span class="line"><span class="comment">#训练</span></span><br><span class="line">model.fit(data_ok,</span><br><span class="line"> data.review.values,</span><br><span class="line"> epochs=<span class="number">10</span>,</span><br><span class="line"> batch_size=<span class="number">128</span>, <span class="comment">#如果不是dataset应该指明的就是batch_size而不是每轮步数</span></span><br><span class="line"> validation_split=<span class="number">0.2</span>) <span class="comment">#从训练数据中切分20%作为测试数据</span></span><br></pre></td></tr></table></figure>
<h4 id="一些问题:-6"><a href="#一些问题:-6" class="headerlink" title="一些问题:"></a>一些问题:</h4><ol>
<li>为保证样本均衡性,可以规模最小的类别为准,数量超过这个规模的类别,去除多余的部分。</li>
</ol>
<h3 id="Keras-RNN-空气污染预测(暂时略过)"><a href="#Keras-RNN-空气污染预测(暂时略过)" class="headerlink" title="Keras-RNN-空气污染预测(暂时略过)"></a>Keras-RNN-空气污染预测(暂时略过)</h3><h3 id="一维卷积简介"><a href="#一维卷积简介" class="headerlink" title="一维卷积简介"></a>一维卷积简介</h3><p>对某些序列处理问题,一维卷积网络的效果可以媲美RNN,且计算代价通常小得多(更高效)。</p>
<p>一维卷积神经网络在音频生成和机器翻译领域取得了巨大成功。对于文本分类和时间序列预测等简单任务,小型的1D-CNN可以替代RNN,且速度更快。</p>
<p>一维卷积层可以识别序列中的<strong>局部模式</strong>,因为对每个序列段执行相同的输入变换,所以在序列中某个位置学到的模式稍后可以在其他位置被识别,这使得一维卷积网络具有<strong>平移不变性</strong>。</p>
<p>Keras中的一维卷积神经网络是Conv1D层,它接受的输入shape为(batch,time,features(channel))三维张量,并返回类似形状的三维张量。卷积窗口时时间轴上的一维窗口。</p>
<p>Keras中的一维池化是MaxPooling1D层。</p>
<p>一维卷积网络可以使用更大的卷积窗口,可以使用大小等于7、9、11、15的一维卷积窗口,相当于二维的3x3、5x5卷积核。</p>
<h3 id="一维卷积实例——文本分类(暂时略过)"><a href="#一维卷积实例——文本分类(暂时略过)" class="headerlink" title="一维卷积实例——文本分类(暂时略过)"></a>一维卷积实例——文本分类(暂时略过)</h3><h3 id="一维卷积实例——叶子分类(暂时略过)"><a href="#一维卷积实例——叶子分类(暂时略过)" class="headerlink" title="一维卷积实例——叶子分类(暂时略过)"></a>一维卷积实例——叶子分类(暂时略过)</h3><h3 id="一维卷积网络的优化(暂时略过)"><a href="#一维卷积网络的优化(暂时略过)" class="headerlink" title="一维卷积网络的优化(暂时略过)"></a>一维卷积网络的优化(暂时略过)</h3><p>增大深度、宽度、宽度递增、dropout、BN、引入残差等都可以优化网络。</p>
]]></content>
<categories>
<category>学习笔记</category>
</categories>
<tags>
<tag>深度学习</tag>
<tag>Win10</tag>
<tag>TensorFlow2</tag>
</tags>
</entry>
<entry>
<title>Win10平台训练Yolo-Fastest模型全流程</title>
<url>/2021/03/24/Win10%E5%B9%B3%E5%8F%B0%E8%AE%AD%E7%BB%83Yolo-Fastest%E6%A8%A1%E5%9E%8B%E5%85%A8%E6%B5%81%E7%A8%8B/</url>
<content><![CDATA[<h3 id="一、环境准备"><a href="#一、环境准备" class="headerlink" title="一、环境准备"></a>一、环境准备</h3><ol>
<li>安装vs2015</li>
<li>根据显卡驱动安装相应版本的<a href="https://developer.nvidia.com/cuda-downloads" target="_blank" rel="noopener">CUDA</a>和<a href="https://developer.nvidia.com/rdp/cudnn-download" target="_blank" rel="noopener">cuDNN</a></li>
<li>安装<a href="https://sourceforge.net/projects/opencvlibrary/files/4.4.0/opencv-4.4.0-vc14_vc15.exe/download" target="_blank" rel="noopener">OpenCV 4.4.0</a></li>
<li>安装<a href="https://cmake.org/download/" target="_blank" rel="noopener">CMake</a></li>
<li>安装<a href="https://www.anaconda.com/products/individual" target="_blank" rel="noopener">Anaconda</a></li>
</ol>
<a id="more"></a>
<p>以上所有安装均可轻易找到大量教程,此处不再赘述。</p>
<h3 id="二、搜集样本"><a href="#二、搜集样本" class="headerlink" title="二、搜集样本"></a>二、搜集样本</h3><p>运行这篇博客的Python脚本,可以快速搜集目标样本集,亲测可用:</p>
<p><a href="https://blog.csdn.net/qq_40774175/article/details/81273198" target="_blank" rel="noopener">https://blog.csdn.net/qq_40774175/article/details/81273198</a></p>
<h3 id="三、标注样本"><a href="#三、标注样本" class="headerlink" title="三、标注样本"></a>三、标注样本</h3><p>目标检测样本需要把待检测目标在图片中的位置和大小标注出来,采用<strong>LabelImg</strong>标注Yolo样本非常方便,下面介绍使用方法。</p>
<h4 id="1-用pip安装LabelImg"><a href="#1-用pip安装LabelImg" class="headerlink" title="1.用pip安装LabelImg"></a>1.用pip安装LabelImg</h4><p>安装Anaconda以后,在开始菜单打开Anaconda Prompt (Anaconda3)进入base环境,运行pip命令安装LabelImg:</p>
<figure class="highlight plain"><table><tr><td class="code"><pre><span class="line">pip install labelImg</span><br></pre></td></tr></table></figure>
<h4 id="2-启动LabelImg"><a href="#2-启动LabelImg" class="headerlink" title="2.启动LabelImg"></a>2.启动LabelImg</h4><p>安装好LabelImg之后,在命令行输入程序名回车即可启动:</p>
<figure class="highlight plain"><table><tr><td class="code"><pre><span class="line">LabelImg</span><br></pre></td></tr></table></figure>
<h4 id="3-设置样本类型"><a href="#3-设置样本类型" class="headerlink" title="3.设置样本类型"></a>3.设置样本类型</h4><p>在LabelImg的界面中,左边“<strong>Save</strong>”按钮下方的按钮表示样本类型,<strong>点击即可切换</strong>,将其切换为“<strong>YOLO</strong>”即可。</p>
<h4 id="4-设置路径"><a href="#4-设置路径" class="headerlink" title="4.设置路径"></a>4.设置路径</h4><p>在LabelImg的界面中,点击左侧的“<strong>Open Dir</strong>”按钮,将目录设置为事先准备好的目标图片文件夹;</p>
<p>再点击左侧的“<strong>Change Save Dir</strong>”按钮,指定一个输出目录,用于保存每张图片的标注信息文件。</p>
<h4 id="5-开始标注"><a href="#5-开始标注" class="headerlink" title="5.开始标注"></a>5.开始标注</h4><ol>
<li><p>设置好“<strong>Open Dir</strong>”后,会自动加载其中的图片,将输入法切换为英文,按下快捷键“W”,然后在图像区域按下鼠标左键拖动,就会出现矩形框;</p>
</li>
<li><p>框出目标后会提示输入目标名称(只需输入一次,以后会自动填充),如果有多个目标需要标注,就继续框选,然后输入目标名字即可;</p>
</li>
<li><p>待所有目标都被标注完毕后,点击界面左侧的“<strong>save</strong>”按钮,即可保存当前图片的标注信息,到这里第一张图片的标注工作就完成了;</p>
</li>
<li><p>之后点击界面左侧的“<strong>Next Image</strong>”按钮,即可开始下一张图片的标注,然后重复第2、3步操作即可;</p>
</li>
<li><p>重复第2、3、4步,直到指定目录下的所有图片标注完毕。</p>
</li>
</ol>
<h3 id="四、编译Yolo-Fastest"><a href="#四、编译Yolo-Fastest" class="headerlink" title="四、编译Yolo-Fastest"></a>四、编译Yolo-Fastest</h3><h4 id="1-下载Yolo-Fastest项目源码"><a href="#1-下载Yolo-Fastest项目源码" class="headerlink" title="1.下载Yolo-Fastest项目源码"></a>1.下载Yolo-Fastest项目源码</h4><p>github地址:<a href="https://github.com/dog-qiuqiu/Yolo-Fastest" target="_blank" rel="noopener">https://github.com/dog-qiuqiu/Yolo-Fastest</a></p>
<p>下载后将源码解压到本地熟悉的路径下,接下来以<strong>D:\Yolo-Fastest-master</strong>为例。</p>
<h4 id="2-配置Makefile"><a href="#2-配置Makefile" class="headerlink" title="2.配置Makefile"></a>2.配置Makefile</h4><p>在Yolo-Fastest根目录下找到Makefile,用记事本打开,可以看到前面这几项,保持作者的默认配置即可:</p>
<figure class="highlight plain"><table><tr><td class="code"><pre><span class="line">GPU=1 #启用GPU加速</span><br><span class="line">CUDNN=1 #启用cuDNN,支持v5-v7版本</span><br><span class="line">CUDNN_HALF=0</span><br><span class="line">OPENCV=1 #启用OpenCV,支持OpenCV 4.x/3.x/2.4.x</span><br></pre></td></tr></table></figure>
<h4 id="3-用CMake生成解决方案"><a href="#3-用CMake生成解决方案" class="headerlink" title="3.用CMake生成解决方案"></a>3.用CMake生成解决方案</h4><ol>
<li>打开CMake,将<code>source code</code>路径设置为<code>D:\Yolo-Fastest-master</code>,将目标生成路径也设置为<code>D:\Yolo-Fastest-master</code>;</li>
<li>然后点击“<strong>Configure</strong>”,设置VS版本为VS2015,目标平台为x64,然后确认;</li>
<li>若提示没有找到<strong>OPENCV_DIR</strong>,再选择<code>opencv.exe</code>解压缩后的<code>build</code>文件夹为<strong>OPENCV_DIR</strong>的路径即可,可以添加<strong>OPENCV_DIR</strong>环境变量,也可以直接在CMake上面的配置列表中设置;</li>
<li>然后依次点击“<strong>Generate</strong>”和“<strong>Open Project</strong>”按钮即可打开解决方案。</li>
</ol>
<p>如果上面的过程没有其他报错,就可以编译解决方案了。</p>
<h4 id="4-编译Darknet"><a href="#4-编译Darknet" class="headerlink" title="4.编译Darknet"></a>4.编译Darknet</h4><p>打开上一步生成的的解决方案后,生成整个解决方案,如果不能全部成功编译,可能是OpenCV,CUDA或者cuDNN等的安装不正确,或者版本不合适。</p>
<p>如果全部正常通过编译,在<code>D:\Yolo-Fastest-master</code>路径下会多出一个<code>Release</code>文件夹,将其中的<strong>darknet.dll</strong>和<strong>darknet.exe</strong>复制到<code>D:\Yolo-Fastest-master/build/darknet/x64</code>目录下。</p>
<h4 id="5-获取Yolo-Fastest的模型文件和权重文件"><a href="#5-获取Yolo-Fastest的模型文件和权重文件" class="headerlink" title="5.获取Yolo-Fastest的模型文件和权重文件"></a>5.获取Yolo-Fastest的模型文件和权重文件</h4><p>在<code>D:\Yolo-Fastest-master\Yolo-Fastest</code>目录下可以找到COCO或VOC版本的Yolo-Fastest与训练模型,任选一个即可,复制一下4个文件:</p>
<figure class="highlight plain"><table><tr><td class="code"><pre><span class="line">yolo-fastest.cfg</span><br><span class="line">yolo-fastest.weights</span><br><span class="line">yolo-fastest-xl.cfg</span><br><span class="line">yolo-fastest-xl.weights</span><br></pre></td></tr></table></figure>
<p>将上面复制的文件粘贴到<code>D:\Yolo-Fastest-master\build\darknet\x64\cfg</code>文件夹。</p>
<p>至此,可以用下面的批处理文件测试一下Yolo-Fastest预训练模型:</p>
<figure class="highlight plain"><table><tr><td class="code"><pre><span class="line">darknet detector test cfg\voc.data cfg\yolo-fastest.cfg cfg\yolo-fastest.weights data\person.jpg -i 1 -thresh 0.25 -out_filename data\person_output.jpg</span><br><span class="line">pause</span><br></pre></td></tr></table></figure>
<p>将上述命令保存在.txt文件中,然后改文件名后缀为.bat,将.bat文件复制到<code>D:\Yolo-Fastest-master\build\darknet\x64</code>文件夹,然后双击运行,如果能顺利检测,说明目前为止一切正常,已经成功编译了Yolo-Fastest。</p>
<h3 id="五、在目标数据集上训练Yolo-Fastest"><a href="#五、在目标数据集上训练Yolo-Fastest" class="headerlink" title="五、在目标数据集上训练Yolo-Fastest"></a>五、在目标数据集上训练Yolo-Fastest</h3><h4 id="1-配置训练所用数据集"><a href="#1-配置训练所用数据集" class="headerlink" title="1.配置训练所用数据集"></a>1.配置训练所用数据集</h4><p>数据集的准备包含5个部分,图片、图片对应的标注文件、trainlist.txt和testlist.txt、yourdataset.data、yourdataset.names。</p>
<ol>
<li><p>检查数据集:用LabelImg标注好的数据集包含一个标注信息文件夹,即用按钮“<strong>Change Save Dir</strong>”指定的文件夹,里面有一批与图片同名的.txt文件,记录着每张图片的标注信息,还有一个“classes.txt”文件,记录着所有目标类别;</p>
</li>
<li><p>准备trainlist.txt和testlist.txt文件:这两个文件分别记录训练集和测试集中每张图片的路径,可用如下批处理脚本获得:</p>
<figure class="highlight plain"><table><tr><td class="code"><pre><span class="line">dir /b/s/p/w *.jpg > list.txt</span><br><span class="line">pause</span><br><span class="line"></span><br><span class="line">#以下为注释内容,实际使用时应删除</span><br><span class="line">将上面的命令写入一个.txt文件,更改文件名后缀为.bat,然后将.bat文件复制到图片所在文件夹,双击运行即可获得该路径下所有jpg图片的路径,并保存在list.txt文件中。</span><br></pre></td></tr></table></figure>
</li>
<li><p>准备.names文件:将上一步中的“classes.txt”更名为“yoursataset.names”即可;</p>
</li>
<li><p>准备.data文件:新建一个.txt文件,更名为“yourdataset.data”,并写入以下内容:</p>
<figure class="highlight plain"><table><tr><td class="code"><pre><span class="line">classes = 1</span><br><span class="line">train = data/yourdataset/trainlist.txt</span><br><span class="line">valid = data/yourdataset/testlist.txt</span><br><span class="line">names = data/yourdataset/yourdataset.names</span><br><span class="line">backup = backup/</span><br><span class="line"></span><br><span class="line">#以下为注释内容,实际使用时应删除</span><br><span class="line">上面的选项应根据具体情况修改,其含义如下:</span><br><span class="line">classes:表示要识别的目标的类别数目,也就是.names文件中记录的类目数量;</span><br><span class="line">train:指明trainlist.txt文件所在路径</span><br><span class="line">valid:指明testlist.txt文件所在路径</span><br><span class="line">names:指明.names文件所在路径</span><br><span class="line">backup:指定训练结果保存路径,训练好的权重将保存在这个路径下</span><br></pre></td></tr></table></figure>
</li>
<li><p>整理目录结构:所有文件准备完毕后,建议以如下目录结构组织文件:</p>
<figure class="highlight plain"><table><tr><td class="code"><pre><span class="line">train_set(文件夹)</span><br><span class="line">test_set(文件夹)</span><br><span class="line">yourdataset.data</span><br><span class="line">yourdataset.names</span><br><span class="line">trainlist.txt</span><br><span class="line">testlist.txt</span><br><span class="line"></span><br><span class="line">#重要说明</span><br><span class="line">1.train_set和test_set是文件夹,分别用来放置训练集和测试集,里面应存放着图片文件和相应的标注信息文件;</span><br><span class="line">2.其余4个文件就是前几步获得的文件;</span><br><span class="line">3.所有文件名可以自定义,但要保证跟.data中的设置一致;</span><br><span class="line">4.可以将上述文件和文件夹单独存放到一个文件夹中,例如yourdataset</span><br></pre></td></tr></table></figure>
</li>
</ol>
<p>至此,数据集准备完毕,笔者将上述目录结构保存在<code>Yolo-Fastest\build\darknet\x64\data\yourdataset</code>下,接下来将按以此为例进行说明。</p>
<h4 id="2-配置模型文件yolo-fastest-cfg"><a href="#2-配置模型文件yolo-fastest-cfg" class="headerlink" title="2.配置模型文件yolo-fastest.cfg"></a>2.配置模型文件yolo-fastest.cfg</h4><p>打开之前复制到<code>D:\Yolo-Fastest-master\build\darknet\x64\cfg</code>目录下的模型结构配置文件<code>yolo-fastest.cfg</code>,可以看到整个模型的各项设置,需要自定义配置的主要有以下几处:</p>
<ol>
<li>搜索“[net]”,<strong>[net]</strong>字段下是各种训练参数,训练时一般设置<code>batch=64</code>,<code>subdivisions=16</code>,测试时一般设置<code>batch=1</code>,<code>subdivisions=1</code>,其他参数可以保持默认;</li>
<li>搜索“[yolo]”,将<span style="color:red"><strong>所有</strong></span><strong>[yolo]</strong>字段下的<code>classes</code>项设置为你的目标类别数,然后将<span style="color:red"><strong>每一个</strong></span><strong>[yolo]</strong>字段前<span style="color:red"><strong>紧邻的一个</strong></span><strong>[convolutional]</strong>字段中的<code>filters</code>项重置,计算方法为<code>filters=(classes+1)*3</code></li>
</ol>
<p>修改后保存即可。</p>
<h4 id="3-开始训练Yolo-Fastest"><a href="#3-开始训练Yolo-Fastest" class="headerlink" title="3.开始训练Yolo-Fastest"></a>3.开始训练Yolo-Fastest</h4><p>训练的思路是,先生成预训练模型(骨干网络),然后在其基础上拿自己的数据集进行训练,完成模型的迁移学习。</p>
<ol>
<li><p>生成预训练模型:在<code>D:\Yolo-Fastest-master\build\darknet\x64</code>目录下新建一文件夹<code>pretrained_model</code>,将如下命令写入一.bat文件中,复制文件到<code>D:\Yolo-Fastest-master\build\darknet\x64</code>下,双击运行即可生成预训练模型<code>yolo-fastest.conv.109</code>:</p>
<figure class="highlight plain"><table><tr><td class="code"><pre><span class="line">darknet partial cfg\yolo-fastest.cfg cfg\yolo-fastest.weights pretrained_model\yolo-fastest.conv.109 109</span><br><span class="line">pause</span><br></pre></td></tr></table></figure>
</li>
<li><p>开始训练:将以下命令写入一.bat文件,复制文件到<code>D:\Yolo-Fastest-master\build\darknet\x64</code>下,双击运行即可开始训练:</p>
<figure class="highlight plain"><table><tr><td class="code"><pre><span class="line">darknet detector train data\yourdataset\yourdataset.data cfg\yolo-fastest.cfg pretrained_model\yolo-fastest.conv.109 backup\</span><br><span class="line">pause</span><br></pre></td></tr></table></figure>
</li>
</ol>
<h4 id="4-测试训练好的Yolo-Fastest模型"><a href="#4-测试训练好的Yolo-Fastest模型" class="headerlink" title="4.测试训练好的Yolo-Fastest模型"></a>4.测试训练好的Yolo-Fastest模型</h4><p>将以下命令写入一.bat文件,复制文件到<code>D:\Yolo-Fastest-master\build\darknet\x64</code>下,双击运行即可开始测试,测试时根据提示输入待检测图片的路径,即可在<code>D:\Yolo-Fastest-master\build\darknet\x64</code>目录下生成检测结果图片:</p>
<figure class="highlight plain"><table><tr><td class="code"><pre><span class="line">darknet detector test data\yourdataset\yourdataset.data cfg\yolo-fastest.cfg backup\yolo-fastest_last.weights</span><br></pre></td></tr></table></figure>]]></content>
<categories>
<category>教程</category>
</categories>
<tags>
<tag>深度学习</tag>
<tag>Win10</tag>
<tag>Yolo</tag>
</tags>
</entry>
<entry>
<title>利用Matlab内置函数绘制Matlab风格的混淆矩阵图并计算F1值</title>
<url>/2021/01/13/%E5%88%A9%E7%94%A8Matlab%E5%86%85%E7%BD%AE%E5%87%BD%E6%95%B0%E7%BB%98%E5%88%B6Matlab%E9%A3%8E%E6%A0%BC%E7%9A%84%E6%B7%B7%E6%B7%86%E7%9F%A9%E9%98%B5%E5%9B%BE%E5%B9%B6%E8%AE%A1%E7%AE%97F1%E5%80%BC/</url>
<content><![CDATA[<h4 id="新建一个-m文件,复制以下代码即可定义函数PlotConfusion,直接按要求调用即可:"><a href="#新建一个-m文件,复制以下代码即可定义函数PlotConfusion,直接按要求调用即可:" class="headerlink" title="新建一个.m文件,复制以下代码即可定义函数PlotConfusion,直接按要求调用即可:"></a>新建一个.m文件,复制以下代码即可定义函数PlotConfusion,直接按要求调用即可:</h4><figure class="highlight matlab"><table><tr><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">F1</span>=<span class="title">PlotConfusion</span><span class="params">(confusion_matrix)</span></span></span><br><span class="line"><span class="comment">%输入混淆矩阵,绘制混淆矩阵图</span></span><br><span class="line"><span class="comment">%列的方向是真实值,行的方向是预测值</span></span><br><span class="line"><span class="comment">%或者说是x轴为真实标签,y轴是预测标签,原点在左上角</span></span><br><span class="line"><span class="comment">%返回值F1是每个类别的recall和precision的调和平均值,按行排列</span></span><br><span class="line"><span class="comment">%例如:f1=PlotConfusion([12,3;15,2]);</span></span><br><span class="line"></span><br><span class="line">[label_num,~]=<span class="built_in">size</span>(confusion_matrix);</span><br><span class="line">sum_lable=sum(confusion_matrix);</span><br><span class="line">sum_pre_lable=sum((confusion_matrix'));</span><br><span class="line">precision=<span class="built_in">zeros</span>(<span class="number">1</span>,label_num);</span><br><span class="line">recall=<span class="built_in">zeros</span>(<span class="number">1</span>,label_num);</span><br><span class="line">F1=<span class="built_in">zeros</span>(<span class="number">1</span>,label_num);</span><br><span class="line"><span class="comment">%构造plotconfusion函数的输入</span></span><br><span class="line">labels=[];</span><br><span class="line">predicted_labels=[];</span><br><span class="line"><span class="keyword">for</span> col=<span class="number">1</span>:label_num</span><br><span class="line"> <span class="keyword">for</span> row=<span class="number">1</span>:label_num</span><br><span class="line"> num=confusion_matrix(row,col);</span><br><span class="line"> <span class="comment">%计算recall和precision</span></span><br><span class="line"> <span class="keyword">if</span>(row==col)</span><br><span class="line"> recall(row)=num/sum_lable(row);</span><br><span class="line"> precision(row)=num/sum_pre_lable(row);</span><br><span class="line"> <span class="keyword">end</span></span><br><span class="line"> <span class="comment">%构造过程</span></span><br><span class="line"> temp=<span class="built_in">zeros</span>(label_num,num);</span><br><span class="line"> temp_pre=temp;</span><br><span class="line"> one_row=<span class="built_in">ones</span>(<span class="number">1</span>,num);</span><br><span class="line"> temp(col,:)=one_row;</span><br><span class="line"> temp_pre(row,:)=one_row;</span><br><span class="line"> labels=[labels,temp];</span><br><span class="line"> predicted_labels=[predicted_labels,temp_pre];</span><br><span class="line"> <span class="keyword">end</span></span><br><span class="line"><span class="keyword">end</span></span><br><span class="line"><span class="comment">%绘制混淆矩阵</span></span><br><span class="line">plotconfusion(labels,predicted_labels);</span><br><span class="line"></span><br><span class="line"><span class="comment">%计算各类F1指标</span></span><br><span class="line"><span class="keyword">for</span> <span class="built_in">i</span>=<span class="number">1</span>:label_num</span><br><span class="line"> F1(<span class="built_in">i</span>)=<span class="number">2</span>*recall(<span class="built_in">i</span>)*precision(<span class="built_in">i</span>)/(recall(<span class="built_in">i</span>)+precision(<span class="built_in">i</span>));</span><br><span class="line"><span class="keyword">end</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">end</span></span><br></pre></td></tr></table></figure>
]]></content>
<categories>
<category>教程</category>
</categories>
<tags>
<tag>Matlab</tag>
<tag>机器学习</tag>
</tags>
</entry>
<entry>
<title>Win10下为Caffe添加自定义层ConfusionLayer统计并输出混淆矩阵</title>
<url>/2021/01/12/win10%E4%B8%8B%E4%B8%BACaffe%E6%B7%BB%E5%8A%A0%E8%87%AA%E5%AE%9A%E4%B9%89%E5%B1%82ConfusionLayer%E7%BB%9F%E8%AE%A1%E5%B9%B6%E8%BE%93%E5%87%BA%E6%B7%B7%E6%B7%86%E7%9F%A9%E9%98%B5/</url>
<content><![CDATA[<p> 本文通过添加自定义层ConfusionLayer和修改solver.cpp以及添加Solver参数实现了在test阶段输出混淆矩阵。</p>
<a id="more"></a>
<h4 id="1-打开VS2013为libcaffe工程添加Confusion层的头文件:confusion-layer-hpp"><a href="#1-打开VS2013为libcaffe工程添加Confusion层的头文件:confusion-layer-hpp" class="headerlink" title="1.打开VS2013为libcaffe工程添加Confusion层的头文件:confusion_layer.hpp"></a>1.打开VS2013为libcaffe工程添加Confusion层的头文件:confusion_layer.hpp</h4><p>头文件confusion_layer.hpp是在accuracy_layer.hpp的基础上修改得到的,内容如下:</p>
<figure class="highlight c++"><table><tr><td class="code"><pre><span class="line"><span class="meta">#<span class="meta-keyword">ifndef</span> CAFFE_CONFUSION_LAYER_HPP_</span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">define</span> CAFFE_CONFUSION_LAYER_HPP_</span></span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string"><vector></span></span></span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">"caffe/blob.hpp"</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">"caffe/layer.hpp"</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">"caffe/proto/caffe.pb.h"</span></span></span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="keyword">namespace</span> caffe {</span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * @brief 计算混淆矩阵的各个元素</span></span><br><span class="line"><span class="comment"> * 当采用confusion层时,需要指定Sover参数topname_for_tfpn,使得测试时可以输出混淆矩阵</span></span><br><span class="line"><span class="comment"> * 该参数应与confusion层的top name一致,</span></span><br><span class="line"><span class="comment"> * 不设置该参数而采用confusion层,或者该参数与confusion的top name不一致,</span></span><br><span class="line"><span class="comment"> * 将不会输出预期的混淆矩阵,得到的结果是(混淆矩阵元素/迭代次数)</span></span><br><span class="line"><span class="comment"> * 为了得到准确的统计值,应保证测试迭代数*测试batch_size<=提供的测试样本数</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">template</span> <<span class="keyword">typename</span> Dtype></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">ConfusionLayer</span> :</span> <span class="keyword">public</span> Layer<Dtype> {</span><br><span class="line"> <span class="keyword">public</span>:</span><br><span class="line"></span><br><span class="line"> <span class="function"><span class="keyword">explicit</span> <span class="title">ConfusionLayer</span><span class="params">(<span class="keyword">const</span> LayerParameter& param)</span></span></span><br><span class="line"><span class="function"> : Layer<Dtype><span class="params">(param)</span> </span>{}</span><br><span class="line"> <span class="function"><span class="keyword">virtual</span> <span class="keyword">void</span> <span class="title">LayerSetUp</span><span class="params">(<span class="keyword">const</span> <span class="built_in">vector</span><Blob<Dtype>*>& bottom,</span></span></span><br><span class="line"><span class="function"><span class="params"> <span class="keyword">const</span> <span class="built_in">vector</span><Blob<Dtype>*>& top)</span></span>;</span><br><span class="line"> <span class="function"><span class="keyword">virtual</span> <span class="keyword">void</span> <span class="title">Reshape</span><span class="params">(<span class="keyword">const</span> <span class="built_in">vector</span><Blob<Dtype>*>& bottom,</span></span></span><br><span class="line"><span class="function"><span class="params"> <span class="keyword">const</span> <span class="built_in">vector</span><Blob<Dtype>*>& top)</span></span>;</span><br><span class="line"></span><br><span class="line"> <span class="function"><span class="keyword">virtual</span> <span class="keyword">inline</span> <span class="keyword">const</span> <span class="keyword">char</span>* <span class="title">type</span><span class="params">()</span> <span class="keyword">const</span> </span>{ <span class="keyword">return</span> <span class="string">"Confusion"</span>; }</span><br><span class="line"> <span class="function"><span class="keyword">virtual</span> <span class="keyword">inline</span> <span class="keyword">int</span> <span class="title">ExactNumBottomBlobs</span><span class="params">()</span> <span class="keyword">const</span> </span>{ <span class="keyword">return</span> <span class="number">2</span>; }</span><br><span class="line"></span><br><span class="line"> <span class="comment">// 只允许有一个top blob,包含混淆矩阵元素</span></span><br><span class="line"> <span class="function"><span class="keyword">virtual</span> <span class="keyword">inline</span> <span class="keyword">int</span> <span class="title">MinTopBlobs</span><span class="params">()</span> <span class="keyword">const</span> </span>{ <span class="keyword">return</span> <span class="number">1</span>; }</span><br><span class="line"> <span class="function"><span class="keyword">virtual</span> <span class="keyword">inline</span> <span class="keyword">int</span> <span class="title">MaxTopBlos</span><span class="params">()</span> <span class="keyword">const</span> </span>{ <span class="keyword">return</span> <span class="number">1</span>; }</span><br><span class="line"></span><br><span class="line"> <span class="keyword">protected</span>:</span><br><span class="line"> </span><br><span class="line"> <span class="function"><span class="keyword">virtual</span> <span class="keyword">void</span> <span class="title">Forward_cpu</span><span class="params">(<span class="keyword">const</span> <span class="built_in">vector</span><Blob<Dtype>*>& bottom,</span></span></span><br><span class="line"><span class="function"><span class="params"> <span class="keyword">const</span> <span class="built_in">vector</span><Blob<Dtype>*>& top)</span></span>;</span><br><span class="line"></span><br><span class="line"> <span class="function"><span class="keyword">virtual</span> <span class="keyword">void</span> <span class="title">Backward_cpu</span><span class="params">(<span class="keyword">const</span> <span class="built_in">vector</span><Blob<Dtype>*>& top,</span></span></span><br><span class="line"><span class="function"><span class="params"> <span class="keyword">const</span> <span class="built_in">vector</span><<span class="keyword">bool</span>>& propagate_down, <span class="keyword">const</span> <span class="built_in">vector</span><Blob<Dtype>*>& bottom)</span> </span>{</span><br><span class="line"> <span class="keyword">for</span> (<span class="keyword">int</span> i = <span class="number">0</span>; i < propagate_down.<span class="built_in">size</span>(); ++i) {</span><br><span class="line"> <span class="keyword">if</span> (propagate_down[i]) { NOT_IMPLEMENTED; }</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="keyword">int</span> label_axis_, outer_num_, inner_num_;</span><br><span class="line"></span><br><span class="line"> <span class="comment">/// Whether to ignore instances with a certain label.</span></span><br><span class="line"> <span class="keyword">bool</span> has_ignore_label_;<span class="comment">//从Accuracy层保留下来的参数</span></span><br><span class="line"> <span class="comment">/// The label indicating that an instance should be ignored.</span></span><br><span class="line"> <span class="keyword">int</span> ignore_label_;<span class="comment">//从Accuracy层保留下来的参数</span></span><br><span class="line"> <span class="comment">/// Keeps counts of the number of samples per class.</span></span><br><span class="line"> Blob<Dtype> nums_buffer_;<span class="comment">//从Accuracy层保留下来的参数</span></span><br><span class="line">};</span><br><span class="line"></span><br><span class="line">} <span class="comment">// namespace caffe</span></span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="meta-keyword">endif</span> <span class="comment">// CAFFE_ACCURACY_LAYER_HPP_</span></span></span><br></pre></td></tr></table></figure>
<h4 id="2-打开VS2013为libcaffe工程添加Confusion层的源文件:confusion-layer-cpp"><a href="#2-打开VS2013为libcaffe工程添加Confusion层的源文件:confusion-layer-cpp" class="headerlink" title="2.打开VS2013为libcaffe工程添加Confusion层的源文件:confusion_layer.cpp"></a>2.打开VS2013为libcaffe工程添加Confusion层的源文件:confusion_layer.cpp</h4><p>源文件confusion_layer.cpp是在accuracy_layer.cpp的基础上修改得到的,内容如下:</p>
<figure class="highlight c++"><table><tr><td class="code"><pre><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string"><functional></span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string"><utility></span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string"><vector></span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span><span class="meta-string"><algorithm></span></span></span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">"caffe/layers/confusion_layer.hpp"</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">"caffe/util/math_functions.hpp"</span></span></span><br><span class="line"></span><br><span class="line"><span class="keyword">namespace</span> caffe {</span><br><span class="line"></span><br><span class="line"><span class="keyword">template</span> <<span class="keyword">typename</span> Dtype></span><br><span class="line"><span class="keyword">void</span> ConfusionLayer<Dtype>::LayerSetUp(</span><br><span class="line"> <span class="keyword">const</span> <span class="built_in">vector</span><Blob<Dtype>*>& bottom, <span class="keyword">const</span> <span class="built_in">vector</span><Blob<Dtype>*>& top) {<span class="comment">//无需任何操作</span></span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">template</span> <<span class="keyword">typename</span> Dtype></span><br><span class="line"><span class="keyword">void</span> ConfusionLayer<Dtype>::Reshape(</span><br><span class="line"> <span class="keyword">const</span> <span class="built_in">vector</span><Blob<Dtype>*>& bottom, <span class="keyword">const</span> <span class="built_in">vector</span><Blob<Dtype>*>& top) {</span><br><span class="line"> label_axis_ =</span><br><span class="line"> bottom[<span class="number">0</span>]->CanonicalAxisIndex(<span class="keyword">this</span>->layer_param_.accuracy_param().axis());</span><br><span class="line"> outer_num_ = bottom[<span class="number">0</span>]->count(<span class="number">0</span>, label_axis_);</span><br><span class="line"> inner_num_ = bottom[<span class="number">0</span>]->count(label_axis_ + <span class="number">1</span>);</span><br><span class="line"> CHECK_EQ(outer_num_ * inner_num_, bottom[<span class="number">1</span>]->count())</span><br><span class="line"> << <span class="string">"Number of labels must match number of predictions; "</span></span><br><span class="line"> << <span class="string">"e.g., if label axis == 1 and prediction shape is (N, C, H, W), "</span></span><br><span class="line"> << <span class="string">"label count (number of labels) must be N*H*W, "</span></span><br><span class="line"> << <span class="string">"with integer values in {0, 1, ..., C-1}."</span>;</span><br><span class="line"> <span class="keyword">const</span> <span class="keyword">int</span> num_labels = bottom[<span class="number">0</span>]->shape(label_axis_);<span class="comment">///全连接层的top blob第二维度大小为output_num,即类别数</span></span><br><span class="line"> <span class="function"><span class="built_in">vector</span><<span class="keyword">int</span>> <span class="title">top_shape</span><span class="params">(<span class="number">1</span>, num_labels*num_labels)</span></span>; <span class="comment">//混淆矩阵只有一个维度,大小为类别数的平方</span></span><br><span class="line"> top[<span class="number">0</span>]->Reshape(top_shape);</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">template</span> <<span class="keyword">typename</span> Dtype></span><br><span class="line"><span class="keyword">void</span> ConfusionLayer<Dtype>::Forward_cpu(<span class="keyword">const</span> <span class="built_in">vector</span><Blob<Dtype>*>& bottom,</span><br><span class="line"> <span class="keyword">const</span> <span class="built_in">vector</span><Blob<Dtype>*>& top) {</span><br><span class="line"> <span class="keyword">const</span> Dtype* bottom_data = bottom[<span class="number">0</span>]->cpu_data();<span class="comment">//获取输入的数据和标签值</span></span><br><span class="line"> <span class="keyword">const</span> Dtype* bottom_label = bottom[<span class="number">1</span>]->cpu_data();</span><br><span class="line"> <span class="keyword">const</span> <span class="keyword">int</span> dim = bottom[<span class="number">0</span>]->count() / outer_num_;</span><br><span class="line"> <span class="keyword">const</span> <span class="keyword">int</span> num_labels = bottom[<span class="number">0</span>]->shape(label_axis_);<span class="comment">//类别数</span></span><br><span class="line"></span><br><span class="line"> <span class="comment">//每次迭代,应当首先对top blob的数据清零,保证从0开始累加</span></span><br><span class="line"> <span class="keyword">for</span> (<span class="keyword">int</span> i = <span class="number">0</span>; i < num_labels*num_labels; ++i){</span><br><span class="line"> top[<span class="number">0</span>]->mutable_cpu_data()[i] = <span class="number">0</span>;</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">for</span> (<span class="keyword">int</span> i = <span class="number">0</span>; i < outer_num_; ++i) {<span class="comment">//针对一个batch的所有样本的循环</span></span><br><span class="line"> <span class="keyword">for</span> (<span class="keyword">int</span> j = <span class="number">0</span>; j < inner_num_; ++j) {</span><br><span class="line"> <span class="keyword">const</span> <span class="keyword">int</span> label_value =</span><br><span class="line"> <span class="keyword">static_cast</span><<span class="keyword">int</span>>(bottom_label[i * inner_num_ + j]);</span><br><span class="line"> <span class="keyword">if</span> (has_ignore_label_ && label_value == ignore_label_) {</span><br><span class="line"> <span class="keyword">continue</span>;</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">if</span> (top.<span class="built_in">size</span>() > <span class="number">1</span>) ++nums_buffer_.mutable_cpu_data()[label_value];</span><br><span class="line"> DCHECK_GE(label_value, <span class="number">0</span>);</span><br><span class="line"> DCHECK_LT(label_value, num_labels);</span><br><span class="line"> <span class="comment">//开始检测当前样本的预测值和label值,并累加混淆矩阵相应的元素</span></span><br><span class="line"> <span class="built_in">std</span>::<span class="built_in">vector</span><Dtype> bottom_data_vec;<span class="comment">//存储全连接层的输出向量</span></span><br><span class="line"> <span class="keyword">for</span> (<span class="keyword">int</span> k = <span class="number">0</span>; k < num_labels; ++k) {</span><br><span class="line"> bottom_data_vec.push_back(bottom_data[i * dim + k * inner_num_ + j]);<span class="comment">//其实就是[i * dim + k ],因inner_num_=1</span></span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">auto</span> max_var = <span class="built_in">std</span>::max_element(bottom_data_vec.<span class="built_in">begin</span>(), bottom_data_vec.<span class="built_in">end</span>());<span class="comment">//求最大值所在位置</span></span><br><span class="line"> <span class="keyword">const</span> <span class="keyword">int</span> predicted_label_value = <span class="built_in">std</span>::distance(bottom_data_vec.<span class="built_in">begin</span>(), max_var);<span class="comment">//最大值的索引就是预测label值</span></span><br><span class="line"> (top[<span class="number">0</span>]->mutable_cpu_data()[num_labels*label_value + predicted_label_value])++;<span class="comment">//对应元素+1,即label->pre_label</span></span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line">INSTANTIATE_CLASS(ConfusionLayer);</span><br><span class="line">REGISTER_LAYER_CLASS(Confusion);</span><br><span class="line"></span><br><span class="line">} <span class="comment">// namespace caffe</span></span><br></pre></td></tr></table></figure>
<h4 id="3-修改caffe-proto定义新加的ConfusionLayer"><a href="#3-修改caffe-proto定义新加的ConfusionLayer" class="headerlink" title="3.修改caffe.proto定义新加的ConfusionLayer"></a>3.修改caffe.proto定义新加的ConfusionLayer</h4><p>在caffe的目录中找到caffe-master\src\caffe\proto\caffe.proto,打开后搜索关键字”message LayerParameter”,即定义Layer参数的地方,查看上一行的注释:</p>
<figure class="highlight c++"><table><tr><td class="code"><pre><span class="line"><span class="comment">// LayerParameter next available layer-specific ID: 151 (last added: anything )</span></span><br></pre></td></tr></table></figure>
<p>说明LayerParameter的下一个可用参数ID是151,那么就可以在下面的message LayerParameter里面加入下面的参数:</p>
<figure class="highlight c++"><table><tr><td class="code"><pre><span class="line"><span class="comment">//为Confusion层添加参数</span></span><br><span class="line">optional ConfusionParameter confusion_param = <span class="number">151</span>;</span><br></pre></td></tr></table></figure>
<p>为了方便下次添加参数,可以把上面注释中的可用参数ID加1改为152:</p>
<figure class="highlight c++"><table><tr><td class="code"><pre><span class="line"><span class="comment">// LayerParameter next available layer-specific ID: 152 (last added: ConfusionParameter)</span></span><br></pre></td></tr></table></figure>
<p>然后在caffe.proto文件末尾添加Confusion层的参数区域(尽管并没有参数):</p>
<figure class="highlight c++"><table><tr><td class="code"><pre><span class="line">message ConfusionParameter {</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<p>由于修改了proto文件,需要重新编译caffe.proto,具体方法将在下面介绍。</p>
<p>至此,<strong>添加自定义层</strong>的操作已完成。</p>
<h4 id="4-修改solver-cpp-hpp以对Confusion层输出的blob作特殊处理"><a href="#4-修改solver-cpp-hpp以对Confusion层输出的blob作特殊处理" class="headerlink" title="4.修改solver.cpp/hpp以对Confusion层输出的blob作特殊处理"></a>4.修改solver.cpp/hpp以对Confusion层输出的blob作特殊处理</h4><p>首先在solver.hpp中为class Solver增加成员函数TestOutPutTFPN,可以搜索成员函数Test,然后在它的下面声明TestOutPutTFPN:</p>
<figure class="highlight c++"><table><tr><td class="code"><pre><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">TestOutPutTFPN</span><span class="params">(<span class="keyword">const</span> <span class="keyword">int</span> test_net_id = <span class="number">0</span>)</span></span>;</span><br></pre></td></tr></table></figure>
<p>然后在原solver.cpp的基础上增加了成员函数TestOutPutTFPN的定义,并修改了成员函数TestAll,修改后的solver.cpp内容如下:</p>
<figure class="highlight c++"><table><tr><td class="code"><pre><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string"><cstdio></span></span></span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string"><map></span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string"><string></span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string"><vector></span></span></span><br><span class="line"></span><br><span class="line"><span class="comment">//#include "caffe/util/bbox_util.hpp"</span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">"caffe/solver.hpp"</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">"caffe/util/format.hpp"</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">"caffe/util/hdf5.hpp"</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">"caffe/util/io.hpp"</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">"caffe/util/upgrade_proto.hpp"</span></span></span><br><span class="line"></span><br><span class="line"><span class="keyword">namespace</span> caffe {</span><br><span class="line"></span><br><span class="line"><span class="keyword">template</span><<span class="keyword">typename</span> Dtype></span><br><span class="line"><span class="keyword">void</span> Solver<Dtype>::SetActionFunction(ActionCallback func) {</span><br><span class="line"> action_request_function_ = func;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">template</span><<span class="keyword">typename</span> Dtype></span><br><span class="line">SolverAction::Enum Solver<Dtype>::GetRequestedAction() {</span><br><span class="line"> <span class="keyword">if</span> (action_request_function_) {</span><br><span class="line"> <span class="comment">// If the external request function has been set, call it.</span></span><br><span class="line"> <span class="keyword">return</span> action_request_function_();</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">return</span> SolverAction::NONE;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">template</span> <<span class="keyword">typename</span> Dtype></span><br><span class="line">Solver<Dtype>::Solver(<span class="keyword">const</span> SolverParameter& param, <span class="keyword">const</span> Solver* root_solver)</span><br><span class="line"> : net_(), callbacks_(), root_solver_(root_solver),</span><br><span class="line"> requested_early_exit_(<span class="literal">false</span>) {</span><br><span class="line"> Init(param);</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">template</span> <<span class="keyword">typename</span> Dtype></span><br><span class="line">Solver<Dtype>::Solver(<span class="keyword">const</span> <span class="built_in">string</span>& param_file, <span class="keyword">const</span> Solver* root_solver)</span><br><span class="line"> : net_(), callbacks_(), root_solver_(root_solver),</span><br><span class="line"> requested_early_exit_(<span class="literal">false</span>) {</span><br><span class="line"> SolverParameter param;</span><br><span class="line"> ReadSolverParamsFromTextFileOrDie(param_file, &param);</span><br><span class="line"> Init(param);</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">template</span> <<span class="keyword">typename</span> Dtype></span><br><span class="line"><span class="keyword">void</span> Solver<Dtype>::Init(<span class="keyword">const</span> SolverParameter& param) {</span><br><span class="line"> CHECK(Caffe::root_solver() || root_solver_)</span><br><span class="line"> << <span class="string">"root_solver_ needs to be set for all non-root solvers"</span>;</span><br><span class="line"> LOG_IF(INFO, Caffe::root_solver()) << <span class="string">"Initializing solver from parameters: "</span></span><br><span class="line"> << <span class="built_in">std</span>::<span class="built_in">endl</span> << param.DebugString();</span><br><span class="line"> param_ = param;</span><br><span class="line"> CHECK_GE(param_.average_loss(), <span class="number">1</span>) << <span class="string">"average_loss should be non-negative."</span>;</span><br><span class="line"> CheckSnapshotWritePermissions();</span><br><span class="line"> <span class="keyword">if</span> (Caffe::root_solver() && param_.random_seed() >= <span class="number">0</span>) {</span><br><span class="line"> Caffe::set_random_seed(param_.random_seed());</span><br><span class="line"> }</span><br><span class="line"> <span class="comment">// Scaffolding code</span></span><br><span class="line"> InitTrainNet();</span><br><span class="line"> <span class="keyword">if</span> (Caffe::root_solver()) {</span><br><span class="line"> InitTestNets();</span><br><span class="line"> LOG(INFO) << <span class="string">"Solver scaffolding done."</span>;</span><br><span class="line"> }</span><br><span class="line"> iter_ = <span class="number">0</span>;</span><br><span class="line"> current_step_ = <span class="number">0</span>;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">template</span> <<span class="keyword">typename</span> Dtype></span><br><span class="line"><span class="keyword">void</span> Solver<Dtype>::InitTrainNet() {</span><br><span class="line"> <span class="keyword">const</span> <span class="keyword">int</span> num_train_nets = param_.has_net() + param_.has_net_param() +</span><br><span class="line"> param_.has_train_net() + param_.has_train_net_param();</span><br><span class="line"> <span class="keyword">const</span> <span class="built_in">string</span>& field_names = <span class="string">"net, net_param, train_net, train_net_param"</span>;</span><br><span class="line"> CHECK_GE(num_train_nets, <span class="number">1</span>) << <span class="string">"SolverParameter must specify a train net "</span></span><br><span class="line"> << <span class="string">"using one of these fields: "</span> << field_names;</span><br><span class="line"> CHECK_LE(num_train_nets, <span class="number">1</span>) << <span class="string">"SolverParameter must not contain more than "</span></span><br><span class="line"> << <span class="string">"one of these fields specifying a train_net: "</span> << field_names;</span><br><span class="line"> NetParameter net_param;</span><br><span class="line"> <span class="keyword">if</span> (param_.has_train_net_param()) {</span><br><span class="line"> LOG_IF(INFO, Caffe::root_solver())</span><br><span class="line"> << <span class="string">"Creating training net specified in train_net_param."</span>;</span><br><span class="line"> net_param.CopyFrom(param_.train_net_param());</span><br><span class="line"> } <span class="keyword">else</span> <span class="keyword">if</span> (param_.has_train_net()) {</span><br><span class="line"> LOG_IF(INFO, Caffe::root_solver())</span><br><span class="line"> << <span class="string">"Creating training net from train_net file: "</span> << param_.train_net();</span><br><span class="line"> ReadNetParamsFromTextFileOrDie(param_.train_net(), &net_param);</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">if</span> (param_.has_net_param()) {</span><br><span class="line"> LOG_IF(INFO, Caffe::root_solver())</span><br><span class="line"> << <span class="string">"Creating training net specified in net_param."</span>;</span><br><span class="line"> net_param.CopyFrom(param_.net_param());</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">if</span> (param_.has_net()) {</span><br><span class="line"> LOG_IF(INFO, Caffe::root_solver())</span><br><span class="line"> << <span class="string">"Creating training net from net file: "</span> << param_.net();</span><br><span class="line"> ReadNetParamsFromTextFileOrDie(param_.net(), &net_param);</span><br><span class="line"> }</span><br><span class="line"> <span class="comment">// Set the correct NetState. We start with the solver defaults (lowest</span></span><br><span class="line"> <span class="comment">// precedence); then, merge in any NetState specified by the net_param itself;</span></span><br><span class="line"> <span class="comment">// finally, merge in any NetState specified by the train_state (highest</span></span><br><span class="line"> <span class="comment">// precedence).</span></span><br><span class="line"> NetState net_state;</span><br><span class="line"> net_state.set_phase(TRAIN);</span><br><span class="line"> net_state.MergeFrom(net_param.state());</span><br><span class="line"> net_state.MergeFrom(param_.train_state());</span><br><span class="line"> net_param.mutable_state()->CopyFrom(net_state);</span><br><span class="line"> <span class="keyword">if</span> (Caffe::root_solver()) {</span><br><span class="line"> net_.reset(<span class="keyword">new</span> Net<Dtype>(net_param));</span><br><span class="line"> } <span class="keyword">else</span> {</span><br><span class="line"> net_.reset(<span class="keyword">new</span> Net<Dtype>(net_param, root_solver_->net_.<span class="built_in">get</span>()));</span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">template</span> <<span class="keyword">typename</span> Dtype></span><br><span class="line"><span class="keyword">void</span> Solver<Dtype>::InitTestNets() {</span><br><span class="line"> CHECK(Caffe::root_solver());</span><br><span class="line"> <span class="keyword">const</span> <span class="keyword">bool</span> has_net_param = param_.has_net_param();</span><br><span class="line"> <span class="keyword">const</span> <span class="keyword">bool</span> has_net_file = param_.has_net();</span><br><span class="line"> <span class="keyword">const</span> <span class="keyword">int</span> num_generic_nets = has_net_param + has_net_file;</span><br><span class="line"> CHECK_LE(num_generic_nets, <span class="number">1</span>)</span><br><span class="line"> << <span class="string">"Both net_param and net_file may not be specified."</span>;</span><br><span class="line"> <span class="keyword">const</span> <span class="keyword">int</span> num_test_net_params = param_.test_net_param_size();</span><br><span class="line"> <span class="keyword">const</span> <span class="keyword">int</span> num_test_net_files = param_.test_net_size();</span><br><span class="line"> <span class="keyword">const</span> <span class="keyword">int</span> num_test_nets = num_test_net_params + num_test_net_files;</span><br><span class="line"> <span class="keyword">if</span> (num_generic_nets) {</span><br><span class="line"> CHECK_GE(param_.test_iter_size(), num_test_nets)</span><br><span class="line"> << <span class="string">"test_iter must be specified for each test network."</span>;</span><br><span class="line"> } <span class="keyword">else</span> {</span><br><span class="line"> CHECK_EQ(param_.test_iter_size(), num_test_nets)</span><br><span class="line"> << <span class="string">"test_iter must be specified for each test network."</span>;</span><br><span class="line"> }</span><br><span class="line"> <span class="comment">// If we have a generic net (specified by net or net_param, rather than</span></span><br><span class="line"> <span class="comment">// test_net or test_net_param), we may have an unlimited number of actual</span></span><br><span class="line"> <span class="comment">// test networks -- the actual number is given by the number of remaining</span></span><br><span class="line"> <span class="comment">// test_iters after any test nets specified by test_net_param and/or test_net</span></span><br><span class="line"> <span class="comment">// are evaluated.</span></span><br><span class="line"> <span class="keyword">const</span> <span class="keyword">int</span> num_generic_net_instances = param_.test_iter_size() - num_test_nets;</span><br><span class="line"> <span class="keyword">const</span> <span class="keyword">int</span> num_test_net_instances = num_test_nets + num_generic_net_instances;</span><br><span class="line"> <span class="keyword">if</span> (param_.test_state_size()) {</span><br><span class="line"> CHECK_EQ(param_.test_state_size(), num_test_net_instances)</span><br><span class="line"> << <span class="string">"test_state must be unspecified or specified once per test net."</span>;</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">if</span> (num_test_net_instances) {</span><br><span class="line"> CHECK_GT(param_.test_interval(), <span class="number">0</span>);</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">int</span> test_net_id = <span class="number">0</span>;</span><br><span class="line"> <span class="function"><span class="built_in">vector</span><<span class="built_in">string</span>> <span class="title">sources</span><span class="params">(num_test_net_instances)</span></span>;</span><br><span class="line"> <span class="function"><span class="built_in">vector</span><NetParameter> <span class="title">net_params</span><span class="params">(num_test_net_instances)</span></span>;</span><br><span class="line"> <span class="keyword">for</span> (<span class="keyword">int</span> i = <span class="number">0</span>; i < num_test_net_params; ++i, ++test_net_id) {</span><br><span class="line"> sources[test_net_id] = <span class="string">"test_net_param"</span>;</span><br><span class="line"> net_params[test_net_id].CopyFrom(param_.test_net_param(i));</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">for</span> (<span class="keyword">int</span> i = <span class="number">0</span>; i < num_test_net_files; ++i, ++test_net_id) {</span><br><span class="line"> sources[test_net_id] = <span class="string">"test_net file: "</span> + param_.test_net(i);</span><br><span class="line"> ReadNetParamsFromTextFileOrDie(param_.test_net(i),</span><br><span class="line"> &net_params[test_net_id]);</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">const</span> <span class="keyword">int</span> remaining_test_nets = param_.test_iter_size() - test_net_id;</span><br><span class="line"> <span class="keyword">if</span> (has_net_param) {</span><br><span class="line"> <span class="keyword">for</span> (<span class="keyword">int</span> i = <span class="number">0</span>; i < remaining_test_nets; ++i, ++test_net_id) {</span><br><span class="line"> sources[test_net_id] = <span class="string">"net_param"</span>;</span><br><span class="line"> net_params[test_net_id].CopyFrom(param_.net_param());</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">if</span> (has_net_file) {</span><br><span class="line"> <span class="keyword">for</span> (<span class="keyword">int</span> i = <span class="number">0</span>; i < remaining_test_nets; ++i, ++test_net_id) {</span><br><span class="line"> sources[test_net_id] = <span class="string">"net file: "</span> + param_.net();</span><br><span class="line"> ReadNetParamsFromTextFileOrDie(param_.net(), &net_params[test_net_id]);</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> test_nets_.resize(num_test_net_instances);</span><br><span class="line"> <span class="keyword">for</span> (<span class="keyword">int</span> i = <span class="number">0</span>; i < num_test_net_instances; ++i) {</span><br><span class="line"> <span class="comment">// Set the correct NetState. We start with the solver defaults (lowest</span></span><br><span class="line"> <span class="comment">// precedence); then, merge in any NetState specified by the net_param</span></span><br><span class="line"> <span class="comment">// itself; finally, merge in any NetState specified by the test_state</span></span><br><span class="line"> <span class="comment">// (highest precedence).</span></span><br><span class="line"> NetState net_state;</span><br><span class="line"> net_state.set_phase(TEST);</span><br><span class="line"> net_state.MergeFrom(net_params[i].state());</span><br><span class="line"> <span class="keyword">if</span> (param_.test_state_size()) {</span><br><span class="line"> net_state.MergeFrom(param_.test_state(i));</span><br><span class="line"> }</span><br><span class="line"> net_params[i].mutable_state()->CopyFrom(net_state);</span><br><span class="line"> LOG(INFO)</span><br><span class="line"> << <span class="string">"Creating test net (#"</span> << i << <span class="string">") specified by "</span> << sources[i];</span><br><span class="line"> <span class="keyword">if</span> (Caffe::root_solver()) {</span><br><span class="line"> test_nets_[i].reset(<span class="keyword">new</span> Net<Dtype>(net_params[i]));</span><br><span class="line"> } <span class="keyword">else</span> {</span><br><span class="line"> test_nets_[i].reset(<span class="keyword">new</span> Net<Dtype>(net_params[i],</span><br><span class="line"> root_solver_->test_nets_[i].<span class="built_in">get</span>()));</span><br><span class="line"> }</span><br><span class="line"> test_nets_[i]->set_debug_info(param_.debug_info());</span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">template</span> <<span class="keyword">typename</span> Dtype></span><br><span class="line"><span class="keyword">void</span> Solver<Dtype>::Step(<span class="keyword">int</span> iters) {</span><br><span class="line"> <span class="keyword">const</span> <span class="keyword">int</span> start_iter = iter_;</span><br><span class="line"> <span class="keyword">const</span> <span class="keyword">int</span> stop_iter = iter_ + iters;</span><br><span class="line"> <span class="keyword">int</span> average_loss = <span class="keyword">this</span>->param_.average_loss();</span><br><span class="line"> losses_.<span class="built_in">clear</span>();</span><br><span class="line"> smoothed_loss_ = <span class="number">0</span>;</span><br><span class="line"></span><br><span class="line"> <span class="keyword">while</span> (iter_ < stop_iter) {<span class="comment">//优化主循环,持续到函数尾部</span></span><br><span class="line"> <span class="comment">// zero-init the params</span></span><br><span class="line"> net_->ClearParamDiffs();</span><br><span class="line"> <span class="keyword">if</span> (param_.test_interval() && iter_ % param_.test_interval() == <span class="number">0</span><span class="comment">//如果迭代数满足测试条件,就测试一轮</span></span><br><span class="line"> && (iter_ > <span class="number">0</span> || param_.test_initialization())</span><br><span class="line"> && Caffe::root_solver()) {</span><br><span class="line"> TestAll();</span><br><span class="line"> <span class="keyword">if</span> (requested_early_exit_) {</span><br><span class="line"> <span class="comment">// Break out of the while loop because stop was requested while testing.</span></span><br><span class="line"> <span class="keyword">break</span>;</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="keyword">for</span> (<span class="keyword">int</span> i = <span class="number">0</span>; i < callbacks_.<span class="built_in">size</span>(); ++i) {</span><br><span class="line"> callbacks_[i]->on_start();</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">const</span> <span class="keyword">bool</span> <span class="built_in">display</span> = param_.<span class="built_in">display</span>() && iter_ % param_.<span class="built_in">display</span>() == <span class="number">0</span>;<span class="comment">//判断是否到了display的时候,若display设为0,就不会显示</span></span><br><span class="line"> net_->set_debug_info(<span class="built_in">display</span> && param_.debug_info());</span><br><span class="line"> <span class="comment">// accumulate the loss and gradient</span></span><br><span class="line"> Dtype loss = <span class="number">0</span>;</span><br><span class="line"> <span class="keyword">for</span> (<span class="keyword">int</span> i = <span class="number">0</span>; i < param_.iter_size(); ++i) {</span><br><span class="line"> loss += net_->ForwardBackward();<span class="comment">//前向计算</span></span><br><span class="line"> }</span><br><span class="line"> loss /= param_.iter_size();</span><br><span class="line"> <span class="comment">// average the loss across iterations for smoothed reporting</span></span><br><span class="line"> UpdateSmoothedLoss(loss, start_iter, average_loss);</span><br><span class="line"> <span class="keyword">if</span> (<span class="built_in">display</span>) {</span><br><span class="line"> LOG_IF(INFO, Caffe::root_solver()) << <span class="string">"Iteration "</span> << iter_</span><br><span class="line"> << <span class="string">", loss = "</span> << smoothed_loss_;</span><br><span class="line"> <span class="keyword">const</span> <span class="built_in">vector</span><Blob<Dtype>*>& result = net_->output_blobs();</span><br><span class="line"> <span class="keyword">int</span> score_index = <span class="number">0</span>;</span><br><span class="line"> <span class="keyword">for</span> (<span class="keyword">int</span> j = <span class="number">0</span>; j < result.<span class="built_in">size</span>(); ++j) {</span><br><span class="line"> <span class="keyword">const</span> Dtype* result_vec = result[j]->cpu_data();</span><br><span class="line"> <span class="keyword">const</span> <span class="built_in">string</span>& output_name =</span><br><span class="line"> net_->blob_names()[net_->output_blob_indices()[j]];</span><br><span class="line"> <span class="keyword">const</span> Dtype loss_weight =</span><br><span class="line"> net_->blob_loss_weights()[net_->output_blob_indices()[j]];</span><br><span class="line"> <span class="keyword">for</span> (<span class="keyword">int</span> k = <span class="number">0</span>; k < result[j]->count(); ++k) {</span><br><span class="line"> <span class="built_in">ostringstream</span> loss_msg_stream;</span><br><span class="line"> <span class="keyword">if</span> (loss_weight) {</span><br><span class="line"> loss_msg_stream << <span class="string">" (* "</span> << loss_weight</span><br><span class="line"> << <span class="string">" = "</span> << loss_weight * result_vec[k] << <span class="string">" loss)"</span>;</span><br><span class="line"> }</span><br><span class="line"> LOG_IF(INFO, Caffe::root_solver()) << <span class="string">" Train net output #"</span></span><br><span class="line"> << score_index++ << <span class="string">": "</span> << output_name << <span class="string">" = "</span></span><br><span class="line"> << result_vec[k] << loss_msg_stream.str();</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">for</span> (<span class="keyword">int</span> i = <span class="number">0</span>; i < callbacks_.<span class="built_in">size</span>(); ++i) {</span><br><span class="line"> callbacks_[i]->on_gradients_ready();</span><br><span class="line"> }</span><br><span class="line"> ApplyUpdate();<span class="comment">//更新权重</span></span><br><span class="line"></span><br><span class="line"> <span class="comment">// Increment the internal iter_ counter -- its value should always indicate</span></span><br><span class="line"> <span class="comment">// the number of times the weights have been updated.</span></span><br><span class="line"> ++iter_;</span><br><span class="line"></span><br><span class="line"> SolverAction::Enum request = GetRequestedAction();</span><br><span class="line"></span><br><span class="line"> <span class="comment">// Save a snapshot if needed.</span></span><br><span class="line"> <span class="keyword">if</span> ((param_.snapshot()</span><br><span class="line"> && iter_ % param_.snapshot() == <span class="number">0</span></span><br><span class="line"> && Caffe::root_solver()) ||</span><br><span class="line"> (request == SolverAction::SNAPSHOT)) {</span><br><span class="line"> Snapshot();</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">if</span> (SolverAction::STOP == request) {</span><br><span class="line"> requested_early_exit_ = <span class="literal">true</span>;</span><br><span class="line"> <span class="comment">// Break out of training loop.</span></span><br><span class="line"> <span class="keyword">break</span>;</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">//Solve函数的定义</span></span><br><span class="line"><span class="keyword">template</span> <<span class="keyword">typename</span> Dtype></span><br><span class="line"><span class="keyword">void</span> Solver<Dtype>::Solve(<span class="keyword">const</span> <span class="keyword">char</span>* resume_file) {</span><br><span class="line"> CHECK(Caffe::root_solver());</span><br><span class="line"> LOG(INFO) << <span class="string">"Solving "</span> << net_->name();</span><br><span class="line"> LOG(INFO) << <span class="string">"Learning Rate Policy: "</span> << param_.lr_policy();</span><br><span class="line"></span><br><span class="line"> <span class="comment">// Initialize to false every time we start solving.</span></span><br><span class="line"> requested_early_exit_ = <span class="literal">false</span>;</span><br><span class="line"></span><br><span class="line"> <span class="keyword">if</span> (resume_file) {</span><br><span class="line"> LOG(INFO) << <span class="string">"Restoring previous solver status from "</span> << resume_file;</span><br><span class="line"> Restore(resume_file);</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="comment">// For a network that is trained by the solver, no bottom or top vecs</span></span><br><span class="line"> <span class="comment">// should be given, and we will just provide dummy vecs.</span></span><br><span class="line"> <span class="keyword">int</span> start_iter = iter_;</span><br><span class="line"> Step(param_.max_iter() - iter_);<span class="comment">//训练过程</span></span><br><span class="line"> <span class="comment">// If we haven't already, save a snapshot after optimization, unless</span></span><br><span class="line"> <span class="comment">// overridden by setting snapshot_after_train := false</span></span><br><span class="line"> <span class="keyword">if</span> (param_.snapshot_after_train()</span><br><span class="line"> && (!param_.snapshot() || iter_ % param_.snapshot() != <span class="number">0</span>)) {</span><br><span class="line"> Snapshot();</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">if</span> (requested_early_exit_) {</span><br><span class="line"> LOG(INFO) << <span class="string">"Optimization stopped early."</span>;</span><br><span class="line"> <span class="keyword">return</span>;</span><br><span class="line"> }</span><br><span class="line"> <span class="comment">// After the optimization is done, run an additional train and test pass to</span></span><br><span class="line"> <span class="comment">// display the train and test loss/outputs if appropriate (based on the</span></span><br><span class="line"> <span class="comment">// display and test_interval settings, respectively). Unlike in the rest of</span></span><br><span class="line"> <span class="comment">// training, for the train net we only run a forward pass as we've already</span></span><br><span class="line"> <span class="comment">// updated the parameters "max_iter" times -- this final pass is only done to</span></span><br><span class="line"> <span class="comment">// display the loss, which is computed in the forward pass.</span></span><br><span class="line"> <span class="keyword">if</span> (param_.<span class="built_in">display</span>() && iter_ % param_.<span class="built_in">display</span>() == <span class="number">0</span>) {</span><br><span class="line"> <span class="keyword">int</span> average_loss = <span class="keyword">this</span>->param_.average_loss();</span><br><span class="line"> Dtype loss;</span><br><span class="line"> net_->Forward(&loss);</span><br><span class="line"></span><br><span class="line"> UpdateSmoothedLoss(loss, start_iter, average_loss);</span><br><span class="line"></span><br><span class="line"> LOG(INFO) << <span class="string">"Iteration "</span> << iter_ << <span class="string">", loss = "</span> << smoothed_loss_;</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">if</span> (param_.test_interval() && iter_ % param_.test_interval() == <span class="number">0</span>) {</span><br><span class="line"> TestAll();</span><br><span class="line"> }</span><br><span class="line"> LOG(INFO) << <span class="string">"Optimization Done."</span>;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">template</span> <<span class="keyword">typename</span> Dtype></span><br><span class="line"><span class="keyword">void</span> Solver<Dtype>::TestAll() {</span><br><span class="line"> <span class="keyword">for</span> (<span class="keyword">int</span> test_net_id = <span class="number">0</span>;</span><br><span class="line"> test_net_id < test_nets_.<span class="built_in">size</span>() && !requested_early_exit_;</span><br><span class="line"> ++test_net_id) {</span><br><span class="line"> <span class="keyword">if</span> (param_.topname_for_tfpn() == <span class="string">""</span>){<span class="comment">//新增参数SolverPrameter:topnamefortfpn="",决定是否输出混淆矩阵</span></span><br><span class="line"> Test(test_net_id);</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">else</span>{</span><br><span class="line"> TestOutPutTFPN(test_net_id);</span><br><span class="line"> }</span><br><span class="line"> </span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">template</span> <<span class="keyword">typename</span> Dtype></span><br><span class="line"><span class="keyword">void</span> Solver<Dtype>::Test(<span class="keyword">const</span> <span class="keyword">int</span> test_net_id) {</span><br><span class="line"> CHECK(Caffe::root_solver());</span><br><span class="line"> LOG(INFO) << <span class="string">"Iteration "</span> << iter_</span><br><span class="line"> << <span class="string">", Testing net (#"</span> << test_net_id << <span class="string">")"</span>;</span><br><span class="line"> CHECK_NOTNULL(test_nets_[test_net_id].<span class="built_in">get</span>())-></span><br><span class="line"> ShareTrainedLayersWith(net_.<span class="built_in">get</span>());</span><br><span class="line"> <span class="built_in">vector</span><Dtype> test_score;<span class="comment">///</span></span><br><span class="line"> <span class="built_in">vector</span><<span class="keyword">int</span>> test_score_output_id;<span class="comment">///</span></span><br><span class="line"> <span class="keyword">const</span> <span class="built_in">shared_ptr</span><Net<Dtype> >& test_net = test_nets_[test_net_id];</span><br><span class="line"> Dtype loss = <span class="number">0</span>;</span><br><span class="line"> <span class="keyword">for</span> (<span class="keyword">int</span> i = <span class="number">0</span>; i < param_.test_iter(test_net_id); ++i) {</span><br><span class="line"> SolverAction::Enum request = GetRequestedAction();</span><br><span class="line"> <span class="comment">// Check to see if stoppage of testing/training has been requested.</span></span><br><span class="line"> <span class="keyword">while</span> (request != SolverAction::NONE) {</span><br><span class="line"> <span class="keyword">if</span> (SolverAction::SNAPSHOT == request) {</span><br><span class="line"> Snapshot();</span><br><span class="line"> } <span class="keyword">else</span> <span class="keyword">if</span> (SolverAction::STOP == request) {</span><br><span class="line"> requested_early_exit_ = <span class="literal">true</span>;</span><br><span class="line"> }</span><br><span class="line"> request = GetRequestedAction();</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">if</span> (requested_early_exit_) {</span><br><span class="line"> <span class="comment">// break out of test loop.</span></span><br><span class="line"> <span class="keyword">break</span>;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> Dtype iter_loss;</span><br><span class="line"> <span class="keyword">const</span> <span class="built_in">vector</span><Blob<Dtype>*>& result =<span class="comment">//Test函数是基于result blob来计算日志内容的,它能显示什么内容取决于能给它什么blob</span></span><br><span class="line"> test_net->Forward(&iter_loss); </span><br><span class="line"> <span class="keyword">if</span> (param_.test_compute_loss()) {</span><br><span class="line"> loss += iter_loss;</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">if</span> (i == <span class="number">0</span>) {<span class="comment">///对第1次迭代做特殊处理</span></span><br><span class="line"> <span class="keyword">for</span> (<span class="keyword">int</span> j = <span class="number">0</span>; j < result.<span class="built_in">size</span>(); ++j) {<span class="comment">//遍历每个结果blob</span></span><br><span class="line"> <span class="keyword">const</span> Dtype* result_vec = result[j]->cpu_data();<span class="comment">//获得blob的数据指针</span></span><br><span class="line"> <span class="keyword">for</span> (<span class="keyword">int</span> k = <span class="number">0</span>; k < result[j]->count(); ++k) {<span class="comment">//遍历blob中的每个元素</span></span><br><span class="line"> test_score.push_back(result_vec[k]);<span class="comment">//给blob的每个元素开个内存空间,并把它们存进去</span></span><br><span class="line"> test_score_output_id.push_back(j);<span class="comment">//记录每个blob元素所属的blob ID</span></span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> } <span class="keyword">else</span> {<span class="comment">///</span></span><br><span class="line"> <span class="keyword">int</span> idx = <span class="number">0</span>;<span class="comment">//上面为每个blob元素开辟的空间的索引</span></span><br><span class="line"> <span class="keyword">for</span> (<span class="keyword">int</span> j = <span class="number">0</span>; j < result.<span class="built_in">size</span>(); ++j) {<span class="comment">//遍历每个结果blob</span></span><br><span class="line"> <span class="keyword">const</span> Dtype* result_vec = result[j]->cpu_data();<span class="comment">//获得blob的数据指针</span></span><br><span class="line"> <span class="keyword">for</span> (<span class="keyword">int</span> k = <span class="number">0</span>; k < result[j]->count(); ++k) {<span class="comment">//遍历blob中的每个元素</span></span><br><span class="line"> test_score[idx++] += result_vec[k];<span class="comment">//累加每次迭代的所有blob元素的值</span></span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">if</span> (requested_early_exit_) {</span><br><span class="line"> LOG(INFO) << <span class="string">"Test interrupted."</span>;</span><br><span class="line"> <span class="keyword">return</span>;</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">if</span> (param_.test_compute_loss()) {</span><br><span class="line"> loss /= param_.test_iter(test_net_id);</span><br><span class="line"> LOG(INFO) << <span class="string">"Test loss: "</span> << loss;</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">for</span> (<span class="keyword">int</span> i = <span class="number">0</span>; i < test_score.<span class="built_in">size</span>(); ++i) {<span class="comment">///该循环持续到函数尾部,遍历所有的结果元素</span></span><br><span class="line"> <span class="keyword">const</span> <span class="keyword">int</span> output_blob_index = </span><br><span class="line"> test_net->output_blob_indices()[test_score_output_id[i]];</span><br><span class="line"> <span class="keyword">const</span> <span class="built_in">string</span>& output_name = test_net->blob_names()[output_blob_index];<span class="comment">//如果迭代次数*batch size超过测试样本量的话,某些样本的会被测试多次,其结果会被多次计入,得到的混淆矩阵不准确,</span></span><br><span class="line"> <span class="keyword">const</span> Dtype loss_weight = test_net->blob_loss_weights()[output_blob_index];<span class="comment">//为了保证准确,迭代次数*batch size不能超过所给测试样本量</span></span><br><span class="line"> <span class="built_in">ostringstream</span> loss_msg_stream; <span class="comment">//</span></span><br><span class="line"> <span class="keyword">const</span> Dtype mean_score = test_score[i] / param_.test_iter(test_net_id);<span class="comment">//计算关于迭代次数的平均值</span></span><br><span class="line"> <span class="keyword">if</span> (loss_weight) {</span><br><span class="line"> loss_msg_stream << <span class="string">" (* "</span> << loss_weight</span><br><span class="line"> << <span class="string">" = "</span> << loss_weight * mean_score << <span class="string">" loss)"</span>;</span><br><span class="line"> }</span><br><span class="line"> LOG(INFO) << <span class="string">" Test net output #"</span> << i << <span class="string">": "</span> << output_name << <span class="string">" = "</span></span><br><span class="line"> << mean_score << loss_msg_stream.str();</span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">template</span> <<span class="keyword">typename</span> Dtype></span><br><span class="line"><span class="keyword">void</span> Solver<Dtype>::TestOutPutTFPN(<span class="keyword">const</span> <span class="keyword">int</span> test_net_id){</span><br><span class="line"> CHECK(Caffe::root_solver());</span><br><span class="line"> LOG(INFO) << <span class="string">"Iteration "</span> << iter_</span><br><span class="line"> << <span class="string">", Testing net (#"</span> << test_net_id << <span class="string">")"</span>;</span><br><span class="line"> CHECK_NOTNULL(test_nets_[test_net_id].<span class="built_in">get</span>())-></span><br><span class="line"> ShareTrainedLayersWith(net_.<span class="built_in">get</span>());</span><br><span class="line"> <span class="built_in">vector</span><Dtype> test_score;<span class="comment">///</span></span><br><span class="line"> <span class="built_in">vector</span><<span class="keyword">int</span>> test_score_output_id;<span class="comment">///</span></span><br><span class="line"> <span class="keyword">int</span> class_num = <span class="number">0</span>;<span class="comment">//记录类别数</span></span><br><span class="line"> <span class="keyword">const</span> <span class="built_in">shared_ptr</span><Net<Dtype> >& test_net = test_nets_[test_net_id];</span><br><span class="line"> Dtype loss = <span class="number">0</span>;</span><br><span class="line"> <span class="comment">//记录结果</span></span><br><span class="line"> <span class="keyword">for</span> (<span class="keyword">int</span> i = <span class="number">0</span>; i < param_.test_iter(test_net_id); ++i) {<span class="comment">//这里是真正的测试迭代循环</span></span><br><span class="line"> SolverAction::Enum request = GetRequestedAction();</span><br><span class="line"> <span class="comment">// Check to see if stoppage of testing/training has been requested.</span></span><br><span class="line"> <span class="keyword">while</span> (request != SolverAction::NONE) {</span><br><span class="line"> <span class="keyword">if</span> (SolverAction::SNAPSHOT == request) {</span><br><span class="line"> Snapshot();</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">else</span> <span class="keyword">if</span> (SolverAction::STOP == request) {</span><br><span class="line"> requested_early_exit_ = <span class="literal">true</span>;</span><br><span class="line"> }</span><br><span class="line"> request = GetRequestedAction();</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">if</span> (requested_early_exit_) {</span><br><span class="line"> <span class="comment">// break out of test loop.</span></span><br><span class="line"> <span class="keyword">break</span>;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> Dtype iter_loss;</span><br><span class="line"> <span class="keyword">const</span> <span class="built_in">vector</span><Blob<Dtype>*>& result =<span class="comment">//Test函数是基于result blob来计算日志内容的,它能显示什么内容取决于能给它什么blob</span></span><br><span class="line"> test_net->Forward(&iter_loss);<span class="comment">//进行测试阶段的前向计算,获取测试net的输出(result)blob</span></span><br><span class="line"> <span class="keyword">if</span> (param_.test_compute_loss()) {</span><br><span class="line"> loss += iter_loss;</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">if</span> (i == <span class="number">0</span>) {<span class="comment">///对第1次迭代做特殊处理</span></span><br><span class="line"> <span class="keyword">for</span> (<span class="keyword">int</span> j = <span class="number">0</span>; j < result.<span class="built_in">size</span>(); ++j) {</span><br><span class="line"> <span class="comment">//这里增加一部分代码用于获取类别数</span></span><br><span class="line"> <span class="keyword">const</span> <span class="keyword">int</span> output_blob_index =</span><br><span class="line"> test_net->output_blob_indices()[j];<span class="comment">//检测第j个result blob是否来自confusion层</span></span><br><span class="line"> <span class="keyword">const</span> <span class="built_in">string</span>& output_name = test_net->blob_names()[output_blob_index]; </span><br><span class="line"> <span class="keyword">if</span> (output_name == param_.topname_for_tfpn()){<span class="comment">//proto参数字符串的类型就是string</span></span><br><span class="line"> <span class="keyword">double</span> class2 = result[j]->count();<span class="comment">//混淆矩阵元素个数,即类数的平方</span></span><br><span class="line"> class_num = <span class="built_in">sqrt</span>(class2);</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="keyword">const</span> Dtype* result_vec = result[j]->cpu_data();<span class="comment">//获得结果blob的数据指针</span></span><br><span class="line"> <span class="keyword">for</span> (<span class="keyword">int</span> k = <span class="number">0</span>; k < result[j]->count(); ++k) {<span class="comment">//遍历blob中的每个元素</span></span><br><span class="line"> test_score.push_back(result_vec[k]);<span class="comment">//给blob的每个元素开个内存空间,并把它们存进去</span></span><br><span class="line"> test_score_output_id.push_back(j);<span class="comment">//记录每个blob元素所属的blob ID</span></span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">else</span> {<span class="comment">///</span></span><br><span class="line"> <span class="keyword">int</span> idx = <span class="number">0</span>;<span class="comment">//上面为每个blob元素开辟的空间的索引</span></span><br><span class="line"> <span class="keyword">for</span> (<span class="keyword">int</span> j = <span class="number">0</span>; j < result.<span class="built_in">size</span>(); ++j) {<span class="comment">//遍历每个结果blob</span></span><br><span class="line"> <span class="keyword">const</span> Dtype* result_vec = result[j]->cpu_data();<span class="comment">//获得blob的数据指针</span></span><br><span class="line"> <span class="keyword">for</span> (<span class="keyword">int</span> k = <span class="number">0</span>; k < result[j]->count(); ++k) {<span class="comment">//遍历blob中的每个元素</span></span><br><span class="line"> test_score[idx++] += result_vec[k];<span class="comment">//累加每次迭代的所有blob元素的值</span></span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">if</span> (requested_early_exit_) {</span><br><span class="line"> LOG(INFO) << <span class="string">"Test interrupted."</span>;</span><br><span class="line"> <span class="keyword">return</span>;</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">if</span> (param_.test_compute_loss()) {</span><br><span class="line"> loss /= param_.test_iter(test_net_id);</span><br><span class="line"> LOG(INFO) << <span class="string">"Test loss: "</span> << loss;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="keyword">int</span> k = <span class="number">0</span>;<span class="comment">//记录混淆矩阵元素在数组中的索引</span></span><br><span class="line"> <span class="keyword">for</span> (<span class="keyword">int</span> i = <span class="number">0</span>; i < test_score.<span class="built_in">size</span>(); ++i) {<span class="comment">///该循环持续到函数尾部,遍历所有的结果元素</span></span><br><span class="line"> <span class="keyword">const</span> <span class="keyword">int</span> output_blob_index = </span><br><span class="line"> test_net->output_blob_indices()[test_score_output_id[i]];</span><br><span class="line"> <span class="keyword">const</span> <span class="built_in">string</span>& output_name = test_net->blob_names()[output_blob_index];<span class="comment">//如果迭代次数*batch size超过测试样本量的话,某些样本的会被测试多次,其结果会被多次计入,得到的混淆矩阵不准确,</span></span><br><span class="line"> <span class="comment">//筛选出存储混淆矩阵的blob的元素</span></span><br><span class="line"> <span class="keyword">if</span> (output_name == param_.topname_for_tfpn()){</span><br><span class="line"> <span class="keyword">int</span> num = test_score[i];<span class="comment">//混淆矩阵的元素</span></span><br><span class="line"> LOG(INFO) << <span class="string">" Test net output #"</span> << i << <span class="string">": "</span> </span><br><span class="line"> << output_name << <span class="string">": "</span><< k / class_num << <span class="string">"->"</span> << k%class_num</span><br><span class="line"> << <span class="string">" = "</span> << num;<span class="comment">//lable->f(x),实际标签->预测值</span></span><br><span class="line"> k++;</span><br><span class="line"> <span class="keyword">continue</span>;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="keyword">const</span> Dtype loss_weight = test_net->blob_loss_weights()[output_blob_index];<span class="comment">//为了保证准确,迭代次数*batch size不能超过所给测试样本量</span></span><br><span class="line"> <span class="built_in">ostringstream</span> loss_msg_stream; <span class="comment">//</span></span><br><span class="line"> <span class="keyword">const</span> Dtype mean_score = test_score[i] / param_.test_iter(test_net_id);<span class="comment">//计算关于迭代次数的平均值</span></span><br><span class="line"> <span class="keyword">if</span> (loss_weight) {</span><br><span class="line"> loss_msg_stream << <span class="string">" (* "</span> << loss_weight</span><br><span class="line"> << <span class="string">" = "</span> << loss_weight * mean_score << <span class="string">" loss)"</span>;</span><br><span class="line"> }</span><br><span class="line"> LOG(INFO) << <span class="string">" Test net output #"</span> << i << <span class="string">": "</span> << output_name << <span class="string">" = "</span></span><br><span class="line"> << mean_score << loss_msg_stream.str();</span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">template</span> <<span class="keyword">typename</span> Dtype></span><br><span class="line"><span class="keyword">void</span> Solver<Dtype>::Snapshot() {</span><br><span class="line"> CHECK(Caffe::root_solver());</span><br><span class="line"> <span class="built_in">string</span> model_filename;</span><br><span class="line"> <span class="keyword">switch</span> (param_.snapshot_format()) {</span><br><span class="line"> <span class="keyword">case</span> caffe::SolverParameter_SnapshotFormat_BINARYPROTO:</span><br><span class="line"> model_filename = SnapshotToBinaryProto();</span><br><span class="line"> <span class="keyword">break</span>;</span><br><span class="line"> <span class="keyword">case</span> caffe::SolverParameter_SnapshotFormat_HDF5:</span><br><span class="line"> model_filename = SnapshotToHDF5();</span><br><span class="line"> <span class="keyword">break</span>;</span><br><span class="line"> <span class="keyword">default</span>:</span><br><span class="line"> LOG(FATAL) << <span class="string">"Unsupported snapshot format."</span>;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> SnapshotSolverState(model_filename);</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">template</span> <<span class="keyword">typename</span> Dtype></span><br><span class="line"><span class="keyword">void</span> Solver<Dtype>::CheckSnapshotWritePermissions() {</span><br><span class="line"> <span class="keyword">if</span> (Caffe::root_solver() && param_.snapshot()) {</span><br><span class="line"> CHECK(param_.has_snapshot_prefix())</span><br><span class="line"> << <span class="string">"In solver params, snapshot is specified but snapshot_prefix is not"</span>;</span><br><span class="line"> <span class="built_in">string</span> probe_filename = SnapshotFilename(<span class="string">".tempfile"</span>);</span><br><span class="line"> <span class="function"><span class="built_in">std</span>::ofstream <span class="title">probe_ofs</span><span class="params">(probe_filename.c_str())</span></span>;</span><br><span class="line"> <span class="keyword">if</span> (probe_ofs.good()) {</span><br><span class="line"> probe_ofs.<span class="built_in">close</span>();</span><br><span class="line"> <span class="built_in">std</span>::<span class="built_in">remove</span>(probe_filename.c_str());</span><br><span class="line"> } <span class="keyword">else</span> {</span><br><span class="line"> LOG(FATAL) << <span class="string">"Cannot write to snapshot prefix '"</span></span><br><span class="line"> << param_.snapshot_prefix() << <span class="string">"'. Make sure "</span></span><br><span class="line"> << <span class="string">"that the directory exists and is writeable."</span>;</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">template</span> <<span class="keyword">typename</span> Dtype></span><br><span class="line"><span class="built_in">string</span> Solver<Dtype>::SnapshotFilename(<span class="keyword">const</span> <span class="built_in">string</span> extension) {</span><br><span class="line"> <span class="keyword">return</span> param_.snapshot_prefix() + <span class="string">"_iter_"</span> + caffe::format_int(iter_)</span><br><span class="line"> + extension;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">template</span> <<span class="keyword">typename</span> Dtype></span><br><span class="line"><span class="built_in">string</span> Solver<Dtype>::SnapshotToBinaryProto() {</span><br><span class="line"> <span class="built_in">string</span> model_filename = SnapshotFilename(<span class="string">".caffemodel"</span>);</span><br><span class="line"> LOG(INFO) << <span class="string">"Snapshotting to binary proto file "</span> << model_filename;</span><br><span class="line"> NetParameter net_param;</span><br><span class="line"> net_->ToProto(&net_param, param_.snapshot_diff());</span><br><span class="line"> WriteProtoToBinaryFile(net_param, model_filename);</span><br><span class="line"> <span class="keyword">return</span> model_filename;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">template</span> <<span class="keyword">typename</span> Dtype></span><br><span class="line"><span class="built_in">string</span> Solver<Dtype>::SnapshotToHDF5() {</span><br><span class="line"> <span class="built_in">string</span> model_filename = SnapshotFilename(<span class="string">".caffemodel.h5"</span>);</span><br><span class="line"> LOG(INFO) << <span class="string">"Snapshotting to HDF5 file "</span> << model_filename;</span><br><span class="line"> net_->ToHDF5(model_filename, param_.snapshot_diff());</span><br><span class="line"> <span class="keyword">return</span> model_filename;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">template</span> <<span class="keyword">typename</span> Dtype></span><br><span class="line"><span class="keyword">void</span> Solver<Dtype>::Restore(<span class="keyword">const</span> <span class="keyword">char</span>* state_file) {</span><br><span class="line"> CHECK(Caffe::root_solver());</span><br><span class="line"> <span class="function"><span class="built_in">string</span> <span class="title">state_filename</span><span class="params">(state_file)</span></span>;</span><br><span class="line"> <span class="keyword">if</span> (state_filename.<span class="built_in">size</span>() >= <span class="number">3</span> &&</span><br><span class="line"> state_filename.compare(state_filename.<span class="built_in">size</span>() - <span class="number">3</span>, <span class="number">3</span>, <span class="string">".h5"</span>) == <span class="number">0</span>) {</span><br><span class="line"> RestoreSolverStateFromHDF5(state_filename);</span><br><span class="line"> } <span class="keyword">else</span> {</span><br><span class="line"> RestoreSolverStateFromBinaryProto(state_filename);</span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">template</span> <<span class="keyword">typename</span> Dtype></span><br><span class="line"><span class="keyword">void</span> Solver<Dtype>::UpdateSmoothedLoss(Dtype loss, <span class="keyword">int</span> start_iter,</span><br><span class="line"> <span class="keyword">int</span> average_loss) {</span><br><span class="line"> <span class="keyword">if</span> (losses_.<span class="built_in">size</span>() < average_loss) {</span><br><span class="line"> losses_.push_back(loss);</span><br><span class="line"> <span class="keyword">int</span> <span class="built_in">size</span> = losses_.<span class="built_in">size</span>();</span><br><span class="line"> smoothed_loss_ = (smoothed_loss_ * (<span class="built_in">size</span> - <span class="number">1</span>) + loss) / <span class="built_in">size</span>;</span><br><span class="line"> } <span class="keyword">else</span> {</span><br><span class="line"> <span class="keyword">int</span> idx = (iter_ - start_iter) % average_loss;</span><br><span class="line"> smoothed_loss_ += (loss - losses_[idx]) / average_loss;</span><br><span class="line"> losses_[idx] = loss;</span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line">INSTANTIATE_CLASS(Solver);</span><br><span class="line"></span><br><span class="line">} <span class="comment">// namespace caffe</span></span><br></pre></td></tr></table></figure>
<h4 id="5-修改caffe-proto添加Solver参数topname-for-tfpn"><a href="#5-修改caffe-proto添加Solver参数topname-for-tfpn" class="headerlink" title="5.修改caffe.proto添加Solver参数topname_for_tfpn"></a>5.修改caffe.proto添加Solver参数topname_for_tfpn</h4><p>在caffe的目录中找到caffe-master\src\caffe\proto\caffe.proto,打开后搜索关键字”message SolverParameter”,即定义Solver参数的地方,查看上一行的注释:</p>
<figure class="highlight c++"><table><tr><td class="code"><pre><span class="line"><span class="comment">// SolverParameter next available ID: 41 (last added: anything)</span></span><br></pre></td></tr></table></figure>
<p>说明SolverParameter的下一个可用参数ID是41,那么就可以在下面的message SolverParameter里面加入参数topname_for_tfpn:</p>
<figure class="highlight c++"><table><tr><td class="code"><pre><span class="line"><span class="comment">//添加Solver.cpp中用于切换带有求混淆矩阵功能的Test函数的参数</span></span><br><span class="line"> optional <span class="built_in">string</span> topname_for_tfpn = <span class="number">41</span> [<span class="keyword">default</span> = <span class="string">""</span>];</span><br></pre></td></tr></table></figure>
<p>然后为了方便下次添加参数,可以把上面注释中的可用参数ID加1改为42:</p>
<figure class="highlight c++"><table><tr><td class="code"><pre><span class="line"><span class="comment">// SolverParameter next available ID: 42 (last added: topname_for_tfpn)</span></span><br></pre></td></tr></table></figure>
<h4 id="6-重新编译caffe-proto文件"><a href="#6-重新编译caffe-proto文件" class="headerlink" title="6.重新编译caffe.proto文件"></a>6.重新编译caffe.proto文件</h4><p>由于修改了caffe.proto,需要重新编译生成新的caffe.pb.cc和caffe.pb.h。</p>
<p>首先下载<a href="https://github.com/protocolbuffers/protobuf/releases/download/v2.6.1/protoc-2.6.1-win32.zip" target="_blank" rel="noopener">Protocol Buffers v2.6.1</a>,将解压得到的protoc.exe与caffe.proto放在同一文件夹中,然后再在其中创建一个bat文件,内容如下:</p>
<figure class="highlight plain"><table><tr><td class="code"><pre><span class="line">protoc.exe caffe.proto --cpp_out=.\</span><br><span class="line"></span><br><span class="line">pause</span><br></pre></td></tr></table></figure>
<p>保存后双击运行bat文件即可编译proto生成新的caffe.pb.cc和caffe.pb.h。</p>
<p>然后将分别将新的caffe.pb.h和caffe.pb.cc拷贝到caffe-master\include\caffe\proto和caffe-master\src\caffe\proto目录下替换原文件。</p>
<h4 id="7-使用方法"><a href="#7-使用方法" class="headerlink" title="7.使用方法"></a>7.使用方法</h4><p>做完上面的6步后,用VS2013重新编译Caffe项目,然后就可以为自己的网络配置Confusion层,分2步走:</p>
<ol>
<li><p>在自己的train_test.prototxt文件的末尾添加layer:</p>
<figure class="highlight plain"><table><tr><td class="code"><pre><span class="line">layer {</span><br><span class="line"> name: "confusion"</span><br><span class="line"> type: "Confusion"</span><br><span class="line"> bottom: "ip" #根据自己网络最后一层的top进行修改</span><br><span class="line"> bottom: "label"</span><br><span class="line"> top: "TFPN" #应与参数topname_for_tfpn一致</span><br><span class="line"> include {</span><br><span class="line"> phase: TEST</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
</li>
<li><p>在solver.prototxt文件中设置参数topname_for_tfpn:</p>
<figure class="highlight plain"><table><tr><td class="code"><pre><span class="line">topname_for_tfpn: "TFPN" #应与Confudion层的top名一致</span><br></pre></td></tr></table></figure>
</li>
</ol>
<p>之后就可以在训练日志中看到test阶段输出的混淆矩阵,以二分类为例:</p>
<figure class="highlight plain"><table><tr><td class="code"><pre><span class="line">I0112 16:33:26.690425 13844 solver.cpp:421] Iteration 1000, Testing net (#0)</span><br><span class="line">I0112 16:33:28.491104 13844 solver.cpp:505] Test net output #0: TFPN: 0->0 = 2892</span><br><span class="line">I0112 16:33:28.491104 13844 solver.cpp:505] Test net output #1: TFPN: 0->1 = 150</span><br><span class="line">I0112 16:33:28.491104 13844 solver.cpp:505] Test net output #2: TFPN: 1->0 = 325</span><br><span class="line">I0112 16:33:28.491104 13844 solver.cpp:505] Test net output #3: TFPN: 1->1 = 729</span><br><span class="line">I0112 16:33:28.491104 13844 solver.cpp:519] Test net output #4: accuracy = 0.884033</span><br><span class="line">I0112 16:33:28.492105 13844 solver.cpp:519] Test net output #5: loss = 0.321114 (* 1 = 0.321114 loss)</span><br></pre></td></tr></table></figure>
]]></content>
<categories>
<category>教程</category>
</categories>
<tags>
<tag>Caffe</tag>
<tag>深度学习</tag>
<tag>Win10</tag>
</tags>
</entry>
<entry>
<title>解决Qt5连接MySQL时报错:QSqlDatabase: QMYSQL driver not loaded</title>
<url>/2020/12/23/%E8%A7%A3%E5%86%B3Qt5%E8%BF%9E%E6%8E%A5MySQL%E6%97%B6%E6%8A%A5%E9%94%99%EF%BC%9AQSqlDatabase-QMYSQL-driver-not-loaded/</url>
<content><![CDATA[<h4 id="1-确保编程环境搭建正确"><a href="#1-确保编程环境搭建正确" class="headerlink" title="1.确保编程环境搭建正确"></a>1.确保编程环境搭建正确</h4><ol>
<li><p>安装Qt时记得勾选source(<strong>源码</strong>);</p>
</li>
<li><p>MySQL的位数需要与所用Qt开发套件的<strong>位数一致</strong>,如64bit的MySQL对应msvc2015_64。</p>
</li>
</ol>
<a id="more"></a>
<h4 id="2-检查所用Qt开发套件目录下是否存在MySQL插件"><a href="#2-检查所用Qt开发套件目录下是否存在MySQL插件" class="headerlink" title="2.检查所用Qt开发套件目录下是否存在MySQL插件"></a>2.检查所用Qt开发套件目录下是否存在MySQL插件</h4><ol>
<li><p>检查Qt安装目录,到所用开发套件的目录下查看是否有MySQL插件,以msvc2015_64为例,查看下面这个目录中是否存在qsqlmysql.dll和qsqlmysqld.dll。</p>
<figure class="highlight plain"><table><tr><td class="code"><pre><span class="line">C:\Qt\Qt5.12.4\5.12.4\msvc2015_64\plugins\sqldrivers</span><br></pre></td></tr></table></figure>
</li>
</ol>
<p>PS:如果Qt版本较高,大概率是不存在这两个dll文件的,就算存在,也需要确保它们跟你安装的MySQL版本相对应,这就需要根据Qt源码和已安装的MySQL版本编译出合适的qsqlmysql.dll和qsqlmysqld.dll。</p>
<h4 id="3-根据Qt源码中的mysql项目编译MySQL插件(基于Qt-Creator)"><a href="#3-根据Qt源码中的mysql项目编译MySQL插件(基于Qt-Creator)" class="headerlink" title="3.根据Qt源码中的mysql项目编译MySQL插件(基于Qt Creator)"></a>3.根据Qt源码中的mysql项目编译MySQL插件(基于Qt Creator)</h4><ol>
<li><p>如果安装Qt时勾选了源码,在Qt安装目录下可以找到mysql项目:</p>
<figure class="highlight plain"><table><tr><td class="code"><pre><span class="line">C:\Qt\Qt5.12.4\5.12.4\Src\qtbase\src\plugins\sqldrivers\mysql\mysql.pro</span><br></pre></td></tr></table></figure>
</li>
<li><p>双击mysql.pro用Qt Creator打开项目,用下面的代码覆盖mysql.pro文件中原来的代码:</p>
<figure class="highlight plain"><table><tr><td class="code"><pre><span class="line">TARGET = qsqlmysql</span><br><span class="line"></span><br><span class="line">HEADERS += $$PWD/qsql_mysql_p.h</span><br><span class="line">SOURCES += $$PWD/qsql_mysql.cpp $$PWD/main.cpp</span><br><span class="line"></span><br><span class="line">#这一句需要注释掉,不然报错说找不到mysql库</span><br><span class="line">#QMAKE_USE += mysql</span><br><span class="line"></span><br><span class="line">OTHER_FILES += mysql.json</span><br><span class="line"></span><br><span class="line">PLUGIN_CLASS_NAME = QMYSQLDriverPlugin</span><br><span class="line">include(../qsqldriverbase.pri)</span><br><span class="line"></span><br><span class="line">#下面的四项需要根据具体的情况自定义</span><br><span class="line"></span><br><span class="line">#前三项指定MySQL的库文件和头文件路径,可根据自己的安装路径替换.</span><br><span class="line">#或者可以用Qt自动生成代码:鼠标右键->添加库->外部库,在库文件选项后点击</span><br><span class="line">#“浏览”,找到MySQL安装路径下的lib目录,选中其中的libmysql.lib添加</span><br><span class="line">#进去,然后把下面的复选框全部去掉对勾,之后点击下一步,完成后就会自动添</span><br><span class="line">#加下面的三项</span><br><span class="line">win32: LIBS += -L$$PWD/'../../../../../../../../../Program Files/MySQL/MySQL Server 5.7/lib/' -llibmysql</span><br><span class="line">INCLUDEPATH += $$PWD/'../../../../../../../../../Program Files/MySQL/MySQL Server 5.7/include'</span><br><span class="line">DEPENDPATH += $$PWD/'../../../../../../../../../Program Files/MySQL/MySQL Server 5.7/include'</span><br><span class="line">#这一项是为了设置生成的MySQL插件的存放位置</span><br><span class="line">#如果不写这一项,默认存在Qt安装目录所在盘的根目录下的#\plugins\sqldrivers中</span><br><span class="line">DESTDIR = D:\DLL</span><br></pre></td></tr></table></figure>
</li>
<li><p>然后点击Qt Creator界面左侧的“项目”,确保Build & Run中选择的是正确的开发套件,例如本文用的就是msvc2015_64,也就是选中Desktop Qt 5.12.4 MSVC2015 64bit.</p>
</li>
<li><p>点击工具栏上的“构建”->“执行qmake”,成功之后再点击Qt Creator界面左下角的锤子按钮进行构建,如果成功构建,恭喜你!可以在设置的位置找到qsqlmysql.dll和qsqlmysqld.dll了。</p>
</li>
</ol>
<h4 id="4-添加MySQL插件"><a href="#4-添加MySQL插件" class="headerlink" title="4.添加MySQL插件"></a>4.添加MySQL插件</h4><ol>
<li><p>把上一步编译生成的qsqlmysql.dll和qsqlmysqld.dll拷贝到所用Qt开发套件的数据库插件目录下,仍以msvc2015_64为例,就是下面这个目录:</p>
<figure class="highlight plain"><table><tr><td class="code"><pre><span class="line">C:\Qt\Qt5.12.4\5.12.4\msvc2015_64\plugins\sqldrivers</span><br></pre></td></tr></table></figure>
</li>
</ol>
<h4 id="5-向Qt安装目录添加MySQL动态库文件"><a href="#5-向Qt安装目录添加MySQL动态库文件" class="headerlink" title="5.向Qt安装目录添加MySQL动态库文件"></a>5.向Qt安装目录添加MySQL动态库文件</h4><ol>
<li><p>仍以msvc2015_64开发套件为例,把MySQL安装目录下的lib文件夹下的libmysql.dll拷贝到Qt安装目录下的对应位置:</p>
<figure class="highlight plain"><table><tr><td class="code"><pre><span class="line">C:\Qt\Qt5.12.4\5.12.4\msvc2015_64\bin</span><br></pre></td></tr></table></figure>
</li>
</ol>
<h4 id="6-大功告成"><a href="#6-大功告成" class="headerlink" title="6.大功告成"></a>6.大功告成</h4>]]></content>
<categories>
<category>报错问题</category>
</categories>
<tags>
<tag>Qt5</tag>
<tag>MySQL</tag>
</tags>
</entry>
<entry>
<title>Win10环境下用draw-net.py绘制Caffe模型的网络结构图</title>
<url>/2020/10/26/Win10%E7%8E%AF%E5%A2%83%E4%B8%8B%E7%94%A8draw-net.py%E7%BB%98%E5%88%B6Caffe%E6%A8%A1%E5%9E%8B%E7%9A%84%E7%BD%91%E7%BB%9C%E7%BB%93%E6%9E%84%E5%9B%BE/</url>
<content><![CDATA[<h4 id="1-编译pycaffe项目"><a href="#1-编译pycaffe项目" class="headerlink" title="1.编译pycaffe项目"></a>1.编译pycaffe项目</h4><p>Caffe提供了Python接口,用Visual Studio 2013打开caffe-master\window目录下的Caffe.sln,修改文件CommonSettings.props的<strong>PythonSupport</strong>项为true,并在<strong>PythonDir</strong>项添加本机的Python安装路径,推荐用Python2,Python3编译时会报错,例如:</p>
<figure class="highlight plain"><table><tr><td class="code"><pre><span class="line">...</span><br><span class="line"><PythonSupport>true</PythonSupport></span><br><span class="line">...</span><br><span class="line"><PythonDir>C:\python27</PythonDir></span><br><span class="line">...</span><br></pre></td></tr></table></figure>
<p>然后生成pycaffe项目。</p>
<a id="more"></a>
<h4 id="2-为Python添加Caffe模块"><a href="#2-为Python添加Caffe模块" class="headerlink" title="2.为Python添加Caffe模块"></a>2.为Python添加Caffe模块</h4><p>上一步成功编译pycaffe后,在caffe-master\Build\x64\Release目录下可以找到pycaffe文件夹,把里面的caffe文件夹拷贝到Python安装目录下的Lib\site-packages中,然后用CMD打开Python并尝试导入caffe模块:</p>
<figure class="highlight python"><table><tr><td class="code"><pre><span class="line"><span class="keyword">import</span> caffe</span><br><span class="line"></span><br><span class="line"><span class="comment">#如果正常导入,说明添加成功,否则需要根据错误提示安装所需依赖包。</span></span><br><span class="line"><span class="comment">#所需依赖至少包括numpy、scikit-image、scipy、protobuf</span></span><br></pre></td></tr></table></figure>
<h4 id="3-安装绘图脚本所需依赖"><a href="#3-安装绘图脚本所需依赖" class="headerlink" title="3.安装绘图脚本所需依赖"></a>3.安装绘图脚本所需依赖</h4><p><strong>1.安装pydot包</strong></p>
<p>直接打开CMD输入以下命令用pip安装:<br> <figure class="highlight plain"><table><tr><td class="code"><pre><span class="line">python -m pip install pydot</span><br></pre></td></tr></table></figure></p>
<p><strong>2.安装Graphviz软件:</strong></p>
<ol>
<li><p>在<a href="https://www2.graphviz.org/Packages/stable/windows/10/cmake/Release/x64/" target="_blank" rel="noopener">Graphviz官网</a>下载安装包;</p>
</li>
<li><p>安装Graphviz并将安装目录下的\bin添加到系统环境变量Path;</p>
</li>
<li><p><strong>以管理员方式打开</strong>CMD运行以下命令完成配置:</p>
<figure class="highlight plain"><table><tr><td class="code"><pre><span class="line">dot -c</span><br></pre></td></tr></table></figure>
</li>
</ol>
<h4 id="4-用CMD调用Python执行绘图脚本draw-net-py"><a href="#4-用CMD调用Python执行绘图脚本draw-net-py" class="headerlink" title="4.用CMD调用Python执行绘图脚本draw_net.py"></a>4.用CMD调用Python执行绘图脚本draw_net.py</h4><p>draw_net.py位于caffe-master\python目录,可以把自己的网络模型配置文件train_test.prototxt拷贝到该目录,并<strong>在该目录运行</strong>CMD,然后执行以下命令:</p>
<figure class="highlight plain"><table><tr><td class="code"><pre><span class="line">python draw_net.py --rankdir LR train_test.prototxt 网络结构图.jpg</span><br><span class="line"></span><br><span class="line">#参数--rankdir LR 用来设置网络结构图的摆放方向,可选项有:</span><br><span class="line">#自左向右:LR</span><br><span class="line">#自右向左:RL</span><br><span class="line">#自下而上:BT</span><br><span class="line">#自上而下:TB</span><br></pre></td></tr></table></figure>
<p>就会在caffe-master\python目录下生成“网络结构图.jpg”。</p>
]]></content>
<categories>
<category>使用教程</category>
</categories>
<tags>
<tag>Caffe</tag>
<tag>深度学习</tag>
<tag>Win10</tag>
<tag>Python</tag>
<tag>可视化</tag>
</tags>
</entry>
<entry>
<title>Win10下用“命令行+Matlab”实现Caffe训练过程可视化</title>
<url>/2020/10/23/Win10%E4%B8%8B%E7%94%A8%E2%80%9C%E5%91%BD%E4%BB%A4%E8%A1%8C+Matlab%E2%80%9D%E5%AE%9E%E7%8E%B0Caffe%E8%AE%AD%E7%BB%83%E8%BF%87%E7%A8%8B%E5%8F%AF%E8%A7%86%E5%8C%96/</url>
<content><![CDATA[<h4 id="1-训练模型并获取训练日志"><a href="#1-训练模型并获取训练日志" class="headerlink" title="1.训练模型并获取训练日志"></a>1.训练模型并获取训练日志</h4><p>用CMD调用Caffe进行模型训练并将日志重定向到txt文件,命令格式如下:</p>
<a id="more"></a>
<figure class="highlight plain"><table><tr><td class="code"><pre><span class="line">#命令格式:</span><br><span class="line">command 2> logfile_path & type logfile_path</span><br><span class="line"></span><br><span class="line">#示例:</span><br><span class="line">Build\x64\Release\caffe.exe train --solver=examples\mnist\lenet_solver_adam.prototxt 2> examples\mnist\train_log.txt & type examples\mnist\train_log.txt</span><br><span class="line">#回车执行命令,训练完成后,CMD窗口的训练日志会被保存到examples\mnist\train_log.txt中。</span><br></pre></td></tr></table></figure>
<h4 id="2-解析日志文件并绘制loss和accuracy曲线"><a href="#2-解析日志文件并绘制loss和accuracy曲线" class="headerlink" title="2.解析日志文件并绘制loss和accuracy曲线"></a>2.解析日志文件并绘制loss和accuracy曲线</h4><p>将上一步得到的日志文件train_log.txt的完整路径输入下面Matlab脚本定义的PlotLossAndAccuracy()函数,即可解析并绘制loss和accuracy曲线</p>
<figure class="highlight matlab"><table><tr><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">result</span>=<span class="title">PlotLossAndAccuracy</span><span class="params">(kLogName)</span></span></span><br><span class="line"><span class="comment">%输入caffe模型训练日志的路径,绘制loss和accuracy曲线</span></span><br><span class="line"><span class="comment">%返回值result=[accuracy,testloss,trainloss]</span></span><br><span class="line"><span class="comment">%示例:</span></span><br><span class="line"><span class="comment">%PlotLossAndAccuracy('E:\caffe-window\新建文本文档.txt')</span></span><br><span class="line"></span><br><span class="line"><span class="comment">%初始化变量</span></span><br><span class="line">ind=strfind(kLogName,<span class="string">'\');%找到所有'</span>\<span class="string">'</span></span><br><span class="line"><span class="string">ind1=ind(end)+1;</span></span><br><span class="line"><span class="string">ind=strfind(kLogName,'</span>_train_log');</span><br><span class="line">ind2=ind<span class="number">-1</span>;</span><br><span class="line">str=kLogName(ind1:ind2);</span><br><span class="line">title_str=strrep(str,<span class="string">'_'</span>,<span class="string">'-'</span>);<span class="comment">%替换字符串</span></span><br><span class="line">fid = fopen(kLogName, <span class="string">'r'</span>);</span><br><span class="line">test_data=[];<span class="comment">%记录测试数据,第一行为迭代次数,第二行为accuracy,第三行为loss</span></span><br><span class="line">train_data=[];<span class="comment">%记录训练数据,第一行为迭代次数,第二行为loss</span></span><br><span class="line"></span><br><span class="line">fline = fgetl(fid);</span><br><span class="line"><span class="keyword">while</span> ischar(fline)</span><br><span class="line"> <span class="comment">%检测当前行所包含的信息</span></span><br><span class="line"> ind=strfind(fline,<span class="string">', Testing net (#0)'</span>);</span><br><span class="line"> <span class="keyword">if</span> ind <span class="comment">%检测是否为测试迭代</span></span><br><span class="line"> ind1=strfind(fline,<span class="string">'Iteration'</span>);</span><br><span class="line"> num=str2double(fline(ind1+<span class="number">10</span>:ind<span class="number">-1</span>));</span><br><span class="line"> fline = fgetl(fid);<span class="comment">%读取下一行</span></span><br><span class="line"> ind=strfind(fline,<span class="string">'accuracy ='</span>);</span><br><span class="line"> <span class="keyword">while</span> <span class="built_in">isempty</span>(ind) <span class="comment">%确保下次一定读取到accuracy</span></span><br><span class="line"> fline = fgetl(fid);</span><br><span class="line"> ind=strfind(fline,<span class="string">'accuracy ='</span>);</span><br><span class="line"> <span class="keyword">end</span></span><br><span class="line"> accuracy=str2double(fline(ind+<span class="number">11</span>:<span class="keyword">end</span>));</span><br><span class="line"> fline = fgetl(fid);<span class="comment">%读取下一行</span></span><br><span class="line"> ind=strfind(fline,<span class="string">'loss ='</span>);</span><br><span class="line"> ind1=strfind(fline,<span class="string">'(*'</span>);</span><br><span class="line"> loss=str2double(fline(ind+<span class="number">7</span>:ind1<span class="number">-2</span>));</span><br><span class="line"> test_data=[test_data,[num;accuracy;loss]];</span><br><span class="line"> <span class="keyword">else</span></span><br><span class="line"> ind=strfind(fline,<span class="string">', loss = '</span>);<span class="comment">%如果不是测试迭代,检测是否为训练迭代</span></span><br><span class="line"> <span class="keyword">if</span> ind</span><br><span class="line"> ind1=strfind(fline,<span class="string">'Iteration'</span>);</span><br><span class="line"> loss=str2double(fline(ind+<span class="number">9</span>:<span class="keyword">end</span>));</span><br><span class="line"> num=str2double(fline(ind1+<span class="number">10</span>:ind<span class="number">-1</span>));</span><br><span class="line"> train_data=[train_data,[num;loss]];</span><br><span class="line"> <span class="keyword">end</span></span><br><span class="line"> <span class="keyword">end</span></span><br><span class="line"> <span class="comment">%读取下一行</span></span><br><span class="line"> fline = fgetl(fid);</span><br><span class="line"><span class="keyword">end</span></span><br><span class="line">fclose(fid);</span><br><span class="line"></span><br><span class="line"><span class="comment">%打印最后一次迭代的模型信息</span></span><br><span class="line">result=[test_data(<span class="number">2</span>,<span class="keyword">end</span>),test_data(<span class="number">3</span>,<span class="keyword">end</span>),train_data(<span class="number">2</span>,<span class="keyword">end</span>)];</span><br><span class="line"><span class="built_in">disp</span>([<span class="string">'test-accuracy = '</span>,num2str(result(<span class="number">1</span>))]);</span><br><span class="line"><span class="built_in">disp</span>([<span class="string">'test-loss = '</span>,num2str(result(<span class="number">2</span>))]);</span><br><span class="line"><span class="built_in">disp</span>([<span class="string">'train-loss = '</span>,num2str(result(<span class="number">3</span>))]);</span><br><span class="line"></span><br><span class="line"><span class="comment">%绘制loss,accuracy曲线</span></span><br><span class="line"><span class="comment">% figure(1)</span></span><br><span class="line"><span class="comment">% clf</span></span><br><span class="line"><span class="built_in">figure</span></span><br><span class="line"><span class="built_in">plot</span>(test_data(<span class="number">1</span>,:),test_data(<span class="number">2</span>,:),<span class="string">'-og'</span>,test_data(<span class="number">1</span>,:),test_data(<span class="number">3</span>,:),<span class="string">'-or'</span>,<span class="string">'LineWidth'</span>,<span class="number">2</span>)</span><br><span class="line"><span class="built_in">hold</span> on</span><br><span class="line"><span class="built_in">plot</span>(train_data(<span class="number">1</span>,:),train_data(<span class="number">2</span>,:),<span class="string">'-ob'</span>,<span class="string">'LineWidth'</span>,<span class="number">2</span>)</span><br><span class="line">legend1=<span class="built_in">legend</span>(<span class="string">'test-accuracy'</span>,<span class="string">'test-loss'</span>,<span class="string">'train-loss'</span>);</span><br><span class="line">set(legend1,<span class="string">'FontWeight'</span>,<span class="string">'bold'</span>,<span class="string">'FontSize'</span>,<span class="number">9</span>);</span><br><span class="line">xlabel(<span class="string">'Iterations'</span>,<span class="string">'FontWeight'</span>,<span class="string">'bold'</span>,<span class="string">'FontSize'</span>,<span class="number">12</span>)</span><br><span class="line">ylabel(<span class="string">'loss and accuracy'</span>,<span class="string">'FontWeight'</span>,<span class="string">'bold'</span>,<span class="string">'FontSize'</span>,<span class="number">12</span>)</span><br><span class="line">title(title_str,<span class="string">'FontWeight'</span>,<span class="string">'bold'</span>,<span class="string">'FontSize'</span>,<span class="number">12</span>)</span><br><span class="line">set(gca,<span class="string">'FontWeight'</span>,<span class="string">'bold'</span>,<span class="string">'FontSize'</span>,<span class="number">11</span> );</span><br><span class="line"></span><br><span class="line"><span class="keyword">end</span></span><br></pre></td></tr></table></figure>
]]></content>
<categories>
<category>工具</category>
</categories>
<tags>
<tag>Caffe</tag>
<tag>深度学习</tag>
<tag>Win10</tag>
<tag>命令行</tag>
<tag>Matlab</tag>
</tags>
</entry>
<entry>
<title>Win10环境下以命令行方式调用Caffe</title>
<url>/2020/10/16/Win10%E7%8E%AF%E5%A2%83%E4%B8%8B%E4%BB%A5%E5%91%BD%E4%BB%A4%E8%A1%8C%E6%96%B9%E5%BC%8F%E8%B0%83%E7%94%A8Caffe/</url>
<content><![CDATA[<p>调用Caffe的命令行格式:</p>
<figure class="highlight plain"><table><tr><td class="code"><pre><span class="line">caffe <command> <arg></span><br><span class="line"></span><br><span class="line">#以下为注释内容,介绍命令和参数的可选项</span><br><span class="line">#command:</span><br><span class="line"> train</span><br><span class="line"> test</span><br><span class="line"> time</span><br><span class="line"> device_query</span><br><span class="line">#args:</span><br><span class="line"> -solver #指定训练配置文件solver.prototxt</span><br><span class="line"> -model #指定模型配置文件train_test.prototxt</span><br><span class="line"> -weights #指定模型权重,即一个.caffemodel文件</span><br><span class="line"> -iterations #指定迭代次数,一般用于test和time命令</span><br><span class="line"> -gpu #指定运行模型的gpu id</span><br><span class="line"> -snapshot #指定用于恢复训练的快照,即一个.solverstate文件</span><br><span class="line"> -sighup_effect #指定程序被挂起时采取的操作,默认为snapshot,还可以设为stop或none</span><br><span class="line"> -sigint_effct #指定程序被键盘终止(ctrl+c)时采取的操作,默认为snapshot,还可以设为stop或none</span><br></pre></td></tr></table></figure>
<a id="more"></a>
<p>下面是用批处理文件(.bat)运行上述命令的示例。</p>
<h4 id="1-train命令示例"><a href="#1-train命令示例" class="headerlink" title="1. train命令示例"></a>1. train命令示例</h4><figure class="highlight plain"><table><tr><td class="code"><pre><span class="line">Build\x64\Release\caffe.exe train --solver=my_caffe_project\mynet\solver.prototxt </span><br><span class="line"></span><br><span class="line">pause </span><br><span class="line">#这里没有使用其他参数,因为一般都在solver.prototxt文件中设置好了</span><br></pre></td></tr></table></figure>
<h4 id="2-test命令示例"><a href="#2-test命令示例" class="headerlink" title="2. test命令示例"></a>2. test命令示例</h4><figure class="highlight plain"><table><tr><td class="code"><pre><span class="line">Build\x64\Release\caffe.exe test --model my_caffe_project\mynet\train_test.prototxt --weights my_caffe_project\mynet\mynet_iter_5000.caffemodel --gpu 0 --iterations 100</span><br><span class="line"></span><br><span class="line">pause </span><br><span class="line">#本次测试指定了:模型+权重+迭代次数+运行设备</span><br><span class="line">#模型、权重是必选项,迭代次数默认为50,运行设备默认是CPU</span><br></pre></td></tr></table></figure>
<h4 id="3-time命令示例"><a href="#3-time命令示例" class="headerlink" title="3. time命令示例"></a>3. time命令示例</h4><figure class="highlight plain"><table><tr><td class="code"><pre><span class="line">Build\x64\Release\caffe.exe time --model my_caffe_project\mynet\train_test.prototxt --weights my_caffe_project\mynet\mynet_iter_5000.caffemodel --iterations 10 --gpu=0</span><br><span class="line"></span><br><span class="line">pause </span><br><span class="line">#本次时间测量指定了:模型+权重+迭代次数+运行设备</span><br><span class="line">#仅模型是必选项</span><br></pre></td></tr></table></figure>
<h4 id="4-device-query命令示例"><a href="#4-device-query命令示例" class="headerlink" title="4. device_query命令示例"></a>4. device_query命令示例</h4><figure class="highlight plain"><table><tr><td class="code"><pre><span class="line">Build\x64\Release\caffe.exe device_query --gpu 0</span><br><span class="line"></span><br><span class="line">pause</span><br></pre></td></tr></table></figure>
<h4 id="PS:"><a href="#PS:" class="headerlink" title="PS:"></a>PS:</h4><p>在批处理文件中用命令行调用Caffe时,程序、命令、参数之间可以用任意数量的空格隔开,参数名需要前缀1或2个”-“,参数名与参数值之间可以用任意数量的空格隔开,也可以用”=”连接。</p>
]]></content>
<categories>
<category>使用教程</category>
</categories>
<tags>
<tag>Caffe</tag>
<tag>深度学习</tag>
<tag>Win10</tag>
<tag>命令行</tag>
</tags>
</entry>
<entry>
<title>Win10环境下用Caffe训练CNN的一般步骤</title>
<url>/2020/10/16/Win10%E7%8E%AF%E5%A2%83%E4%B8%8B%E7%94%A8Caffe%E8%AE%AD%E7%BB%83CNN%E7%9A%84%E4%B8%80%E8%88%AC%E6%AD%A5%E9%AA%A4/</url>
<content><![CDATA[<p>首先确保已经用VS2013打开Caffe.sln并编译好了libcaffe、caffe和convert_imageset这三个项目。</p>
<h4 id="一、数据准备"><a href="#一、数据准备" class="headerlink" title="一、数据准备"></a>一、数据准备</h4><p>1.先准备一个文件夹存放所有训练用的图片文件,再准备一个trainlist.txt文件,包含图片名及其对应的标签,格式如下:</p>
<a id="more"></a>
<figure class="highlight plain"><table><tr><td class="code"><pre><span class="line">imagename1.jpg 0</span><br><span class="line">imagename2.jpg 1</span><br><span class="line">imagename3.jpg 1</span><br><span class="line">...</span><br></pre></td></tr></table></figure>
<p>这里提供一个简陋的Python脚本用于划分数据集和生成相应的list.txt文件,不保证样本数目均衡:</p>
<figure class="highlight python"><table><tr><td class="code"><pre><span class="line"><span class="keyword">import</span> os</span><br><span class="line"><span class="keyword">import</span> shutil</span><br><span class="line"><span class="keyword">import</span> random</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">CreateFileList</span><span class="params">(images_path, txt_save_path)</span>:</span></span><br><span class="line"> <span class="comment">#创建list.txt文件</span></span><br><span class="line"> <span class="keyword">with</span> open(txt_save_path,<span class="string">"w"</span>) <span class="keyword">as</span> fw:</span><br><span class="line"> <span class="comment">#查看图片目录下的文件</span></span><br><span class="line"> image_names = os.listdir(images_path)</span><br><span class="line"> <span class="comment">#遍历所有文件名</span></span><br><span class="line"> <span class="keyword">for</span> image_name <span class="keyword">in</span> image_names:</span><br><span class="line"> label=image_name[:image_name.find(<span class="string">'_'</span>)]</span><br><span class="line"> fw.write(image_name + <span class="string">' '</span>+label+<span class="string">'\n'</span>)</span><br><span class="line"> <span class="comment">#打印成功信息</span></span><br><span class="line"> <span class="keyword">print</span> (<span class="string">"生成list.txt文件成功!"</span>)</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">DivideDatasets</span><span class="params">(root_path)</span>:</span></span><br><span class="line"> <span class="string">"""</span></span><br><span class="line"><span class="string"> 通过指定图片集所在路径root_path,按比例(6:2:2)划分训练、验证和测试数据集,以及对应的trainlist.txt文件.</span></span><br><span class="line"><span class="string"> 要求:root_path路径下【只有图片】,且图片名以"label_"开头,例如1_猫咪.jpg,0_狗子.jpg</span></span><br><span class="line"><span class="string"> """</span></span><br><span class="line"> <span class="comment">#获得数据集信息并初始化参数</span></span><br><span class="line"> image_names = os.listdir(root_path)</span><br><span class="line"> all_num=len(image_names)</span><br><span class="line"> train_num=int(<span class="number">3</span>*all_num/<span class="number">5</span>) <span class="comment">#6/10</span></span><br><span class="line"> verify_num=int(all_num/<span class="number">5</span>) <span class="comment">#2/10</span></span><br><span class="line"> end_of_verify=train_num+verify_num</span><br><span class="line"> num=<span class="number">1</span></span><br><span class="line"> <span class="comment"># 随机打乱文件名顺序</span></span><br><span class="line"> random.shuffle(image_names)</span><br><span class="line"> <span class="comment">#创建所需目录</span></span><br><span class="line"> train_path=root_path+<span class="string">'train\\'</span></span><br><span class="line"> verify_path=root_path+<span class="string">'verify\\'</span></span><br><span class="line"> test_path=root_path+<span class="string">'test\\'</span></span><br><span class="line"> <span class="keyword">if</span> <span class="keyword">not</span> os.path.exists(train_path):</span><br><span class="line"> os.makedirs(train_path)</span><br><span class="line"> <span class="keyword">if</span> <span class="keyword">not</span> os.path.exists(verify_path):</span><br><span class="line"> os.makedirs(verify_path)</span><br><span class="line"> <span class="keyword">if</span> <span class="keyword">not</span> os.path.exists(test_path):</span><br><span class="line"> os.makedirs(test_path)</span><br><span class="line"> <span class="comment"># 移动文件到目标文件夹</span></span><br><span class="line"> <span class="keyword">for</span> image_name <span class="keyword">in</span> image_names:</span><br><span class="line"> <span class="keyword">if</span> num<=train_num:</span><br><span class="line"> shutil.move(root_path+image_name,train_path+image_name)</span><br><span class="line"> <span class="keyword">elif</span> num<=end_of_verify:</span><br><span class="line"> shutil.move(root_path+image_name,verify_path+image_name)</span><br><span class="line"> <span class="keyword">else</span>:</span><br><span class="line"> shutil.move(root_path+image_name,test_path+image_name)</span><br><span class="line"> num+=<span class="number">1</span></span><br><span class="line"> <span class="comment"># 生成各数据集的list.txt文件</span></span><br><span class="line"> CreateFileList(train_path,root_path+<span class="string">'trainlist.txt'</span>)</span><br><span class="line"> CreateFileList(verify_path,root_path+<span class="string">'verifylist.txt'</span>)</span><br><span class="line"> CreateFileList(test_path,root_path+<span class="string">'testlist.txt'</span>)</span><br><span class="line"> print(<span class="string">"数据划分成功!"</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment">#定义图片集根目录</span></span><br><span class="line">root_path=<span class="string">'D:\\caffe-window\\caffe-master\\my_caffe_project\\data\\imageset\\'</span></span><br><span class="line"><span class="comment">#调用划分函数</span></span><br><span class="line">DivideDatasets(root_path)</span><br></pre></td></tr></table></figure>
<p>2.然后调用编译好的convert_imageset.exe将图片数据集转换为适合Caffe快速读取的lmdb格式,具体做法是在caffe-master文件夹里新建批处理文件my_data_convert.bat,以灰度图为例,内容如下:</p>
<figure class="highlight plain"><table><tr><td class="code"><pre><span class="line">Build\x64\Release\convert_imageset --gray=true --shuffle --backend=lmdb my_caffe_project\data\imageset\train\ my_caffe_project\data\imageset\trainlist.txt my_caffe_project\data\LMDB\trainset</span><br><span class="line"></span><br><span class="line">Build\x64\Release\convert_imageset --gray=true --shuffle --backend=lmdb my_caffe_project\data\imageset\verify\ my_caffe_project\data\imageset\verifylist.txt my_caffe_project\data\LMDB\verifyset</span><br><span class="line"></span><br><span class="line">Build\x64\Release\convert_imageset --gray=true --shuffle --backend=lmdb my_caffe_project\data\imageset\test\ my_caffe_project\data\imageset\testlist.txt my_caffe_project\data\LMDB\testset</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">Pause</span><br><span class="line">#注意上述Build和my_caffe_project都是caffe-master的子目录。</span><br></pre></td></tr></table></figure>
<h4 id="二、修改定义好网络配置文件train-test-prototxt和训练参数配置文件solver-prototxt"><a href="#二、修改定义好网络配置文件train-test-prototxt和训练参数配置文件solver-prototxt" class="headerlink" title="二、修改定义好网络配置文件train_test.prototxt和训练参数配置文件solver.prototxt"></a>二、修改定义好网络配置文件train_test.prototxt和训练参数配置文件solver.prototxt</h4><p>假设两个配置文件都在目录caffe-master\my_caffe_project\mynet\下,网络结构和训练参数都设置好之后,只需修改train_test.prototxt中数据层的训练集和测试集lmdb文件所在路径,修改为我们在上一步生成的lmdb文件所在路径:</p>
<figure class="highlight plain"><table><tr><td class="code"><pre><span class="line"> ...</span><br><span class="line"> data_param {</span><br><span class="line"> source: "my_caffe_project/data/LMDB/imageset_train_lmdb"</span><br><span class="line"> batch_size: 64</span><br><span class="line"> backend: LMDB</span><br><span class="line"> ...</span><br><span class="line"> data_param {</span><br><span class="line"> source: "my_caffe_project/data/LMDB/imageset_test_lmdb"</span><br><span class="line"> batch_size: 100</span><br><span class="line"> backend: LMDB</span><br><span class="line">#注意:路径中要用'/'分隔。</span><br></pre></td></tr></table></figure>
<p>然后修改solver.prototxt中的net参数和snapshot_prefix参数,指定网络配置文件的路径和网络快照保存位置:</p>
<figure class="highlight plain"><table><tr><td class="code"><pre><span class="line">net: "my_caffe_project/mynet/train_test.prototxt"</span><br><span class="line">...</span><br><span class="line">snapshot_prefix: "my_caffe_project/mynet/mynet"</span><br><span class="line">#最后一个'mynet'表示网络快照文件的前缀</span><br><span class="line">#注意:路径中要用'/'分隔。</span><br></pre></td></tr></table></figure>
<h4 id="三、调用caffe-exe训练网络"><a href="#三、调用caffe-exe训练网络" class="headerlink" title="三、调用caffe.exe训练网络"></a>三、调用caffe.exe训练网络</h4><p>在caffe-master文件夹下新建批处理文件mynet_train_run.bat文件,编辑如下命令:</p>
<figure class="highlight plain"><table><tr><td class="code"><pre><span class="line">Build\x64\Release\caffe.exe train --solver=my_caffe_project\mynet\solver.prototxt --gpu=0</span><br><span class="line"></span><br><span class="line">pause</span><br></pre></td></tr></table></figure>
]]></content>
<categories>
<category>使用教程</category>
</categories>
<tags>
<tag>Caffe</tag>
<tag>深度学习</tag>
<tag>Win10</tag>
</tags>
</entry>
<entry>
<title>Caffe安装(Win10+GPU)</title>
<url>/2020/10/02/Caffe%E5%AE%89%E8%A3%85-Win10-GPU/</url>
<content><![CDATA[<h3 id="1、安装CUDA"><a href="#1、安装CUDA" class="headerlink" title="1、安装CUDA"></a>1、安装CUDA</h3><ol>
<li><p>安装CUDA前需要先安装好Visual Studio 2013;</p>
</li>
<li><p>通过控制面板->硬件和声音->NVIDIA控制面板->帮助->系统信息->组件->3D设置->NVCUDA.DLL对应的产品名称,查看本机对应的CUDA版本,然后下载对应版本的CUDA;</p>
</li>
</ol>
<a id="more"></a>
<ol start="3">
<li>下载地址:<a href="https://developer.nvidia.com/cuda-downloads" target="_blank" rel="noopener">https://developer.nvidia.com/cuda-downloads</a></li>
<li>确保安装了Visual Studio 2013后,再安装CUDA。</li>
<li>安装完成后,把CUDA的安装目录(比如我的就是C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\v8.0)下的bin、lib\x64、libnvvp、include和extras\CUPTI\lib64目录添加到系统环境变量Path中。</li>
</ol>
<h3 id="2、安装cuDNN"><a href="#2、安装cuDNN" class="headerlink" title="2、安装cuDNN"></a>2、安装cuDNN</h3><ol>
<li>根据CUDA的版本下载对应版本的的cuDNN(需要注册NVIDIA开发者账户);</li>
<li>下载地址:<a href="https://developer.nvidia.com/rdp/cudnn-download" target="_blank" rel="noopener">https://developer.nvidia.com/rdp/cudnn-download</a></li>
<li>下载的cuDNN版本除了需要跟CUDA版本对应,还需要注意后面安装的Caffe是否支持,查看方法:看Caffe的编译配置文件CommonSettings.props中的<CuDnnPath>项上面一行的说明,例如我的就是 <!-- CuDNN 4 and 5 are supported -->,表明支持cuDNN v4和v5;</li>
<li>将下载的压缩包解压,把里面的bin,include,lib文件夹拷贝到CUDA的安装目录;</li>
<li>CUDA的安装目录下的<strong>extras\CUPTI\libx64</strong>里面的cupti64_80.dll复制到CUDA的安装目录下<strong>bin</strong>中。</li>
</ol>
<h3 id="3、安装Caffe"><a href="#3、安装Caffe" class="headerlink" title="3、安装Caffe"></a>3、安装Caffe</h3><ol>
<li><p>下载地址:<a href="https://github.com/Microsoft/caffe" target="_blank" rel="noopener">https://github.com/Microsoft/caffe</a></p>
</li>
<li><p>在磁盘任意位置新建文件夹caffe-window,将下载的压缩包解压到caffe-window,把cuDNN压缩包中的cuda文件夹也复制到caffe-window;</p>
</li>
<li><p>进入caffe-master\windows,复制CommonSettings.props.example文件,并重命名为CommonSettings.props;</p>
</li>
<li><p>用Visual Studio 2013打开caffe-master\windows中的Caffe.sln,如果libcaffe和testall加载出现问题,就打开CommonSettings.props修改其中<CudaVersion>为本机安装的版本,再重新打开Caffe.sln;</p>
</li>
<li><p>设置libcaffe为启动项目;</p>
</li>
<li><p>右键项目libcaffe,定位到属性->-C/C++->视警告为错误,把这一项改为“否”;或者也可以把CommonSettings.props文件中的TreatWarningAsError项设为false:</p>
<p><TreatWarningAsError>false</TreatWarningAsError></p>
</li>
<li><p>在CommonSettings.props文件的<CuDnnPath>项添加cuDNN路径,即<CuDnnPath>D:\caffe-window</CuDnnPath></p>
</li>
<li><p>确保编译方式是release,平台是x64,Ctrl+F5开始编译libcaffe;</p>
<p>ps1:如遇到错误:nuget 基础连接已经关闭:发送时发生错误,新建txt将下面的代码复制进去,保存为.reg文件,双击文件修改注册表即可解决。</p>
<figure class="highlight plain"><table><tr><td class="code"><pre><span class="line">Windows Registry Editor Version 5.00</span><br><span class="line"> </span><br><span class="line">[HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\.NETFramework\v4.0.30319]</span><br><span class="line">"SchUseStrongCrypto"=dword:00000001</span><br><span class="line"> </span><br><span class="line">[HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node\Microsoft\.NETFramework\v4.0.30319]</span><br><span class="line">"SchUseStrongCrypto"=dword:00000001</span><br></pre></td></tr></table></figure>
<p>ps2:如果出现了error MSB3721错误,问题可能来自于Caffe.sln中的cudnn.hpp,这个文件第114行的cudnnSetConvolution2dDescriptor函数里面缺参数,在这个函数参数里添加一个CUDNN_DATA_DOUBLE编译就过了;</p>
<p>ps3:error MSB3721错误也可能是因为CommonSettings.props中的这一句:</p>
<figure class="highlight plain"><table><tr><td class="code"><pre><span class="line"><CudaArchitecture>compute_35,sm_35;compute_52,sm_52</CudaArchitecture></span><br></pre></td></tr></table></figure>
<p>把里面的的35,52,换成小一点的值,比如20,更详细的参考:</p>
<p><a href="https://blog.csdn.net/weixin_42370246/article/details/104283021?utm_medium=distribute.pc_relevant_t0.none-task-blog-BlogCommendFromMachineLearnPai2-1.nonecase&depth_1-utm_source=distribute.pc_relevant_t0.none-task-blog-BlogCommendFromMachineLearnPai2-1.nonecase" target="_blank" rel="noopener">https://blog.csdn.net/weixin_42370246/article/details/104283021?utm_medium=distribute.pc_relevant_t0.none-task-blog-BlogCommendFromMachineLearnPai2-1.nonecase&depth_1-utm_source=distribute.pc_relevant_t0.none-task-blog-BlogCommendFromMachineLearnPai2-1.nonecase</a></p>
</li>
<li><p>项目生成成功后,caffe-window中会多出一个NugetPackages文件夹,里面是自动下载的一些依赖库,caffe-master中也会多出一个Build文件夹,以后编译成功和运行需要的文件都在Build\x64\Release中;</p>
</li>
<li><p>重复第6、7步编译Caffe,编译其他项目也是一样。</p>
<p>ps:这里编译可以对Caffe项目点击右键->仅用于项目->仅生成Caffe。如果点击生成,会重新生成一次libcaffe,这时也不要取消,如果取消,可能会出现错误:无法打开输入文件“libcaffe.lib”,这个只要重新生成libcaffe项目即可解决。</p>
</li>
</ol>
<h3 id="4、测试"><a href="#4、测试" class="headerlink" title="4、测试"></a>4、测试</h3><ol>
<li><p>按照上面说的方法编译convert_mnist_data项目;</p>
</li>
<li><p>下载mnist数据集:<a href="http://yann.lecun.com/exdb/mnist/" target="_blank" rel="noopener">http://yann.lecun.com/exdb/mnist/</a></p>
</li>
<li><p>把数据集解压到caffe-window\caffe-master\data\mnist</p>
</li>
<li><p>在caffe-master中新建mnist_data_convert.bat文件用来转换mnist数据,在该文件中添加以下命令:</p>
<figure class="highlight plain"><table><tr><td class="code"><pre><span class="line">Build\x64\Release\convert_mnist_data --backend=lmdb data\mnist\train-images-idx3-ubyte\train-images.idx3-ubyte data\mnist\train-labels-idx1-ubyte\train-labels.idx1-ubyte examples\mnist\mnist_train_lmdb</span><br><span class="line"></span><br><span class="line">Build\x64\Release\convert_mnist_data --backend=lmdb data\mnist\t10k-images-idx3-ubyte\t10k-images.idx3-ubyte data\mnist\t10k-labels-idx1-ubyte\t10k-labels.idx1-ubyte examples\mnist\mnist_test_lmdb</span><br><span class="line"></span><br><span class="line">Pause</span><br></pre></td></tr></table></figure>
<p>保存bat文件后双击运行,就可以把mnist数据集转换成caffe能够读取的形式;</p>
<p>ps:注意命令中的路径要跟自己的匹配,如果出现错误Check failed: _mkdir(source.c_str()) == 0 (-1 vs. 0),可能是因为caffe-master\examples\mnist\下已经存在了mnist_train_lmdb和mnist_test_lmdb文件夹,把它们删除再运行即可解决。</p>
</li>
<li><p>在caffe-master中新建mnist_test_run.bat.bat文件,内容如下:</p>
<figure class="highlight plain"><table><tr><td class="code"><pre><span class="line">Build\x64\Release\caffe.exe train --solver=examples\mnist\lenet_solver_adam.prototxt</span><br><span class="line"></span><br><span class="line">pause</span><br></pre></td></tr></table></figure>
<p>双击运行mnist_test_run.bat.bat,如果成功运行,说明caffe安装成功。</p>
<p>ps:如果遇到错误Check failed: status == CUDNN_STATUS_SUCCESS (6 vs. 0),说明本机<a href="https://developer.nvidia.com/cuda-gpus" target="_blank" rel="noopener">GPU的加速性能</a>不够,cuDNN只支持CUDA Capability 3.0以上的GPU加速,这时回到Visual Studio 2013,修改CommonSettings.props文件中的<UseCuDNN>true</UseCuDNN>为<UseCuDNN>false</UseCuDNN>,即关闭cuDNN加速,然后重新编译项目libcaffe、Caffe、convert_mnist_data,再次双击运行mnist_test_run.bat.bat。</p>
</li>
</ol>
]]></content>
<categories>
<category>安装教程</category>
</categories>
<tags>
<tag>Caffe</tag>
<tag>深度学习</tag>
<tag>Win10</tag>
</tags>
</entry>
<entry>
<title>Win10下生成ssh公钥</title>
<url>/2020/09/01/Win10%E4%B8%8B%E7%94%9F%E6%88%90ssh%E5%85%AC%E9%92%A5/</url>
<content><![CDATA[<h5 id="1-打开CMD-输入以下命令"><a href="#1-打开CMD-输入以下命令" class="headerlink" title="1.打开CMD,输入以下命令"></a>1.打开CMD,输入以下命令</h5><figure class="highlight plain"><table><tr><td class="code"><pre><span class="line">ssh-keygen -t rsa -C "填一个邮箱"</span><br></pre></td></tr></table></figure>
<h5 id="2-可以连续回车不设置密码,生成密钥文件"><a href="#2-可以连续回车不设置密码,生成密钥文件" class="headerlink" title="2.可以连续回车不设置密码,生成密钥文件"></a>2.可以连续回车不设置密码,生成密钥文件</h5><h5 id="3-打开路径”C-username-ssh”中的-pub文件全选复制ssh公钥"><a href="#3-打开路径”C-username-ssh”中的-pub文件全选复制ssh公钥" class="headerlink" title="3.打开路径”C:/username/.ssh”中的.pub文件全选复制ssh公钥"></a>3.打开路径”C:/username/.ssh”中的.pub文件全选复制ssh公钥</h5><h5 id="4-在coding或github等个人账户设置处添加ssh公钥"><a href="#4-在coding或github等个人账户设置处添加ssh公钥" class="headerlink" title="4.在coding或github等个人账户设置处添加ssh公钥"></a>4.在coding或github等个人账户设置处添加ssh公钥</h5><h5 id="5-回到CMD用下面的命令测试不同平台的连接"><a href="#5-回到CMD用下面的命令测试不同平台的连接" class="headerlink" title="5.回到CMD用下面的命令测试不同平台的连接"></a>5.回到CMD用下面的命令测试不同平台的连接</h5><figure class="highlight plain"><table><tr><td class="code"><pre><span class="line">ssh -T [email protected]</span><br><span class="line">ssh -T [email protected]</span><br><span class="line">ssh -T [email protected]</span><br></pre></td></tr></table></figure>
<p>ps:回车后有询问,键入yes回车,提示成功即可。</p>
]]></content>
<categories>
<category>技巧</category>
</categories>
<tags>
<tag>Win10</tag>
<tag>ssh</tag>
</tags>
</entry>
<entry>
<title>Matlab笔记</title>
<url>/2020/08/31/Matlab%E7%AC%94%E8%AE%B0/</url>
<content><![CDATA[<p>【元胞数组cell】对于元胞数组a,a(i,j)表示对元胞数组a的第i行第j列单元(cell)的直接引用,返回结果是cell类型,而a{i,j}表示对元胞数组a的第i行第j列单元(cell)内存储的变量的引用,返回结果的类型与单元内具体变量的类型一致。<br>【关键字end】end表示矩阵某一维度的末尾下标。对于向量相当于length()函数的返回值。<br>【求矩阵的最大元素】A(:)将二维矩阵A的每一个列向量沿纵向从上到下依次排列构成一个新的列向量B,<br>再调用max(B)即可求得A的最大元素<br>更多【特殊图形绘制函数】参见《matiab图像处理超级学习手册》一书的第三章。</p>
<a id="more"></a>
<p>【imshow函数】不会显示double类型灰度图像?<br>【quiver函数】用于绘制向量图quiver(X,Y,DX,DY),X,Y为栅格坐标,DX,DY是Z(X,Y)在点(X,Y)处的梯度。<br>【pie函数】pie(X)是用X里的数据绘制一张饼图,X里的每一个元素被表示为饼图的一张切片。<br>pie(X,explode)表示分离饼图中的某一切片,<br>分离哪一个就用一个单位向量标出哪一个数据,如[0 0 1 0]表示把有4部分的饼图中的第三块分离。<br>【scatter函数】利用scatter(X,Y,S,C)在向量X、Y定义的位置绘制彩色的圆圈(X、Y必须大小相同),<br>S定义了每个符号的大小,C定义了每个标记的颜色<br>【compass函数】可对复数数组作图<br>【feather函数】将每一个数据视为复数,并以箭线画出,feather(z),其中z为复数组成的数组(向量)。<br>【fill函数】将数据点视为多边形顶点,并将此多边形涂上颜色,用法类似plot<br>【stem函数】可绘制针状图,常用来绘制数位信号,用法类似plot<br>【stairs函数】可以绘制阶梯图,用法类似plot<br>【rose函数】将数据集元素的大小视为角度,将相同大小的数据元素的个数视为距离,绘制极坐标图像<br>【hist函数】用于显示数据的分布情况,hist(y,x),具体用法不明<br>【fplot函数】绘图比较精准,能在剧烈变化处进行相对密集的取样<br>【errorbar函数】用于绘制误差图,errorbar(x,y,e),e表示误差大小<br>【area函数】用于绘制区域图<br>【rand函数】用于生成0-1之间的伪随机数,用法同ones(),zero()等:rand(m,n)或rand([m n]),返回一个mxn的随机数矩阵,rand(n)返回nxn的随机数矩阵,rand()返回一个随机数<br>【who和whos命令】用于查看工作区变量的信息,后加变量名可以查看特定变量的信息<br>【cat(dim,A,B,…)】cat函数在指定的维度dim上拼接数组,可以作为提升维度的工具。<br>【函数定义与调用】如果函数没有输入参数,那么无论是定义还是调用该函数时,后面的空圆括号都是可有可无的,为表明它的函数身份,最好把括号带上。<br>【换行符】newline既可以作为单引号类型字符串的换行符,也可以作为双引号类型字符串的换行符。<br>newline是无输入参数的函数,就像figure一样,但matlab中这一类函数名后面可以不加括号。<br>不知道这算不算关键字,所谓关键字就是已经被系统占用的名字吧?<br>【关于字符串类型】matlab中单引号和双引号都可以表示字符串,但有区别。<br>单引号下的字符串更像是char数组,内含的每一个字符都直接对应字符编码,<br>也就是每个元素实际都是数字类型的,所以“+”操作符对其的作用效果是存储着编码的数组相加。<br>注意:num2str()函数返回的就是单引号类型的字符串。<br>双引号下的字符串才属于真正意义上的字符串类型,一对双引号就代表一个字符串元素,“+”操作符对其的作用效果是字符串拼接。<br>所以单引号、双引号不能混用,会造成不可预知后果。<br>用方括号“拼接”单引号、双引号字符串时,存在几种情况:<br>1.全是单引号类型:相当于数字类型的数组的合并(拼接),得到的结果还是单引号类型字符串(如果元素仍旧是字符编码的话,会被解释成字符,否则就是数字)<br>2.全是双引号类型:相当于字符串类型的数组的合并,得到的结果不是一个字符串,而是一个字符串数组,所以并没有实现字符串拼接,只是把每个字符串存在数组里了。<br>3.既有单引号类型,也有双引号类型,这时会把单引号类型转化为双引号类型的字符串,然后按情况2进行处理。<br>所以用[,,]这种方式对字符串进行合并,只针对单引号类型字符串,原理就是对数字类型数组进行嵌套|合并|拼接。<br>至于双引号类型的字符串,只能通过“+”操作符进行拼接,因为他们都只是单个元素,并不是数组。<br>【单、双引号字符串作为函数参数时的问题】<br>单引号字符串和双引号字符串都可以作为字符串参数传给函数,函数不管它是哪种引号引起来的,统统接收并产生预期效果。<br>但是如果把双引号字符串数组传给函数,就会出现奇怪的结果,<br>因为函数接受的是一个字符串参数,这个参数要么应该是一个单引号类型字符串(只能用[,]拼接),要么应该是一个双引号字符串(只能用”+”拼接)<br>如果传过去的是个双引号字符串数组,就出现了类型不匹配的问题,<br>根据测试结果推测,这时候matlab会把数组中的字符串元素拼接起来,但以换行符作为间隔,也就是所谓奇怪的结果。<br>【直接定义矩阵/数组/向量】时,可以在方括号内进行用圆括号进行分组:<br>tspan = [(0: 20);(1:21)];<br>这种情况下也可以丢掉圆括号,对于i=[1:10]这样定义的向量,方括号是多余的。<br>【ode45的简单示例】:<br>%函数m文件内容:<br>function dydt = vanderpoldemo(t,y,Mu) %多了个常量Mu,这个函数不能直接交给ode45求解器<br>dydt = [y(2); Mu<em>(1-y(1)^2)</em>y(2)-y(1)];%这里【必须用列向量】保存微分方程组,因为要为ode45所用,这个函数必须返回列向量<br>%脚本m文件内容:<br>tspan = [0, 20];%自变量范围,这里只指定了范围并没有给中间的自变量取值,似乎是因为ode45可以自动调节步长。这里也可以指定自变量的一系列取值(大于两个元素),但结果不平滑,误差较大<br>y0 = [2; 0];%因变量初值,这里tspan和y0定义成行向量或列向量均可,只要是一维的就行<br>Mu = 1;<br>ode = @(t,y) vanderpoldemo(t,y,Mu);%指定常量Mu,生成新的函数,只接受t,y两个变量,并用变量ode表示这个加了@的新函数<br>[t,y] = ode45(ode, tspan, y0);%ode可以不带@是因为ode本就是个”@函数名”类型的参数,如果是直接调用m文件中的函数,要加@<br>% Plot of the solution<br>plot(t,y(:,1))%ode45返回(输出)的y的【各列】分别是各因变量在自变量范围内的取值,t也是列向量<br>xlabel(‘t’)<br>ylabel(‘solution y’)<br>title(‘van der Pol Equation, \mu = 1’)</p>
<p>【求矩阵的特征值和特征向量】:[X,B]=eig(A) ;其中B的对角线元素是特征值,X的列是相应的特征向量<br>【求矩阵的逆】:inv(A);<br>【将坐标轴按比例显示 】用DataAspectRatio属性定义比例即可<br>举例:ezplot(@sin)<br>set(gca,’DataAspectRatio’,[2 1 1])%数组中三个值分别代表x、y、z轴的比例,如果想等比例显示,设为[1 1 1]即可,效果等价于axis equal<br>【三维绘图】介绍3类(plot3/mesh/surf)7种三维图像绘制的方法:</p>
<p>plot3 三维曲线图;</p>
<p>mesh 三维网格图;</p>
<p>meshc 除了生成网格图外,还在xy平面生成曲面的等高线;</p>
<p>meshz 除了生成网格图外,还在曲线下面加上个矩形垂帘;</p>
<p>surf 三维着色曲面图;</p>
<p>surfc 同时画出三维着色曲面图与等高线;</p>
<p>surfl 带光照的三维着色曲面图。<br>【坐标转换】可以用pol2cart将柱坐标转换为笛卡尔坐标:[x,y,z] = pol2cart(theta,rho,z)<br>用sph2cart将球面坐标转换为笛卡尔坐标<br>使用subplot()创建多个画布时,按住shift键同时使用数据游标,可以同时标示多个数据点。<br>字符串拼接要使用向量形式,整个个向量代表一个字符串输入,向量元素为被拼接字符串,例如:title([‘字符串1’,’字符串2’])或者legend([‘字符串1’,’字符串2’],[‘字符串3’,’字符串4’])<br>【取整函数】:截尾取整:fix(x);高斯取整(不超过x的最大整数):floor(x);大于x的最小整数:ceil(x);四舍五入取整:round(x);<br>subs()替换函数,可以在符号变量、标量和字符串之间自由替换。<br>连接字符串:把字符串当成矩阵元素,按照矩阵的组合将字符串连接起来,成为新的“矩阵”。<br>终止当前计算:<br>第一种解决方法:同时按住快捷键Ctrl-C,这样能够终止死循环,如下图所示<br>这种方法并不是都有效,因为某些程序占据内存过高,不容易退出,这是我们采取第二种方法。<br>第二种解决方法:关闭MATLAB软件,这种方法的缺点是不能保存MATLAB的中间结果<br>第三解决方法:强制关闭MATLAB软件,进入任务管理器(同时按住Ctrl+Alt+Delete),关闭MATLAB,如下图所示,这种方法的缺点是不能保存MATLAB的中间结果<br>对于稍微复杂的问题,还可以写在一个函数里,对于复杂问题,如果写在一个函数里,定义的所有变量处于同一个域里,容易发生混淆,如果分为多个函数,变量定义就比较简单。<br>for循环还可以:</p>
<blockquote>
<blockquote>
<p>a=[1,5,9,4,8]<br>for i=a<br>循环体<br>end<br>也就是for后面的条件实际上是赋给i一系列值,这些值构成一个向量,这个向量可以现场定义,也可以取自已知变量。<br>fprintf(‘string%g%g%g\n’,x,y,n)表示打印出string和x,y,n的值,类似于disp(),但需要后缀\n换行。<br>&&前后的两个量是标量还是矩阵?&&只能用标量<br>【回调函数之间的数据传输方法2】:<br>用gui中已有对象的‘UserData’属性传输,但只能传输一个数据,类型不限:set(handles.Tag,’UserData’,Value)<br>【回调函数之间的数据传输方法1】:<br>将变量a存入handles中:<br>handles.a=a;%a、b数据类型不限<br>handles.b=b;<br>…..<br>guidata(hObject,handles);<br>要获得带变量值,可以使用:<br>m=handles.a;<br>n=handles.b;<br>编辑器中使用Tab键进行函数提示。<br>gui设计鼠标悬停提示符:在控件属性里的tooltipString项填写提示字符串即可。<br>【多返回值函数】:<br>Function [a,b,c]=fun(x,y)<br>a=eq;<br>b=eq1;<br>c=eq;<br>end<br>调用时:>>[m,n,p]=fun(x,y)<br>同时给多个变量赋值:[a,b,c]=deal(91,109,91)<br>【关于纵坐标标签】ylabel({‘错’,’误’,’率’,’\zeta^2’},’Rotation’,0) ;可旋转标签文字方向<br>clf; 用来清除图形的命令。一般在画图之前用。它会清除当前图窗的所有内容<br>假设一个场景:你原来打开的matlab里面,有一个图形,现在,你要画一个新的图形,如果你手动关闭这个原有图形,也不用clf命令清楚图形,直接画上去,那么原来的图形和你要画的图形就会重叠在一起。会造成干扰。<br>类似的命令还有很多:<br>clear; 清除原有变量<br>clc; 清楚命令窗口的内容s<br>demo; 查看帮助<br>help 查看帮助<br>quit 退出matlab<br>figure 新建图形窗口<br>【批量改变量名】:找到变量第一次出现的位置,修改后会有提示,同时按shift和Enter键即可。<br>plot()绘制两点默认将得到连接两点的线段。<br>积分函数:int(积分表达式,积分变量,积分下限,积分上限),可以在积分表达式处进行嵌套。<br>微分函数:diff(函数,微分变量)<br>查看变量:who或者whos<br>清除已定义变量:clear [全部清除] 或 clear x y z [清除特定变量]<br>分号[;]表示不显示;之前表达式的结果<br>省略号[…]回车后,MATLAB 会把光标移到下一行等待用户更进一步的输入,表示换行,不计算,用于冗长表达式<br>改变小数点位数: 输出小数点后4位:format short ; 输出小数点后16位format long ;财务计算:format bank [输出小数点后两位]<br>科学记数法:format short e [短指数] 或format long e [长指数]<br>以比例式显示结果:format rat<br>右除和左除:被除数 / 除数 ;除数 \ 被除数 。都表示除法,只是被除数的位置规定不同。<br>【基本数学定义式】:<br>Π :pi ;<br>e^a : exp(a)<br>平方根公式:sqrt(9)=3<br>x的自然对数ln(x) :log(x)<br>x以10为底的对数:log10(x)<br>MATLAB 还带有基本三角函数及反三角函数,默认以<弧度>为参数,以小写标准形式输入即可: cos(pi/4) , sin(pi/4) , tan(pi/4) …<br>要使用反三角函数,在三角函数名前加 a: acos(pi/4) , asin(pi/4) , atan(pi/4)…<br>在 MATLAB 中输入 复数很容易,默认就把 i 当为负一的平方根。<br>另外,在 MATLAB 没有必要在 i 前面添加空格或乘号(*),例如 a = 2 + 3i 。<br>【修正输入】<br>有时候我们输入表达式时会带有错误,当你按 ENTER 回车后才意识到,<br>这时没必须重 新输入整行,只需使用方向键向上移动,<br>修正错误,然后按回车重新输入,MATLAB 会修正输出。<br>【文件基础 】<br>要保存在命令窗口中输入的变量和表达 式以便以后使用,那么按照下面的步骤来操作 :</p>
<ol>
<li>点击“文件(File)”下接菜单 </li>
<li>选择“保存工作区为(Save Workspace As…)” </li>
<li>输入文件名 </li>
<li>点击“保存(Save)”按钮 </li>
</ol>
</blockquote>
</blockquote>
<p>某行以%开始,表示这一行是注释(Comment)。<br>数组采用方括号[ ]表示,元 素之间采用冒号(:)或分号(;)隔开,例如:x = [1:2:3:4];<br>保存工作区(.mat文件)可以保存变量极其值,创建并保存脚本(.m文件)可以创建和保存以后想要使用的命令,函数,数据。<br>列向量(数组)用方括号加分号分隔符表示,如:temps = [32;50;65;70;85] ;行向量用方括号加空格或逗号分隔符表示,如,temps = [32,50,65,70,85]<br>退出程序命令:quit</p>
<p>第二章:</p>
<p>用单引号(’)代表转置操作,如: a = [2; 1; 4]; y = a’ >>y = 2 1 4<br>两个向量进行相加或相减,要实现此操作,两个向量之 间必须类型相同,长度相同。<br>向量可以进行合并,只要将已定义的多个行向量(或列向量)作为元素,定义新的行向量即可,如 :R = [12, 11, 9]; S = [1, 4]; >> T = [R, S]<br>有时需要创建带有等差元素的向量,差值为 q 为一个实数。创建一个首元素为 xi,末元 素为 xe的向量 x 的语法如下: x = [xi : q: xe]<br>注意在 MATLAB 中向量的乘方必须在幂运算符前(^)前加上句号(.),如 果我们只是输入>> y = x^2,MATLAB 会给出错误信息。 x = [0:0.1:1] ; y = x .^ 2 </p>
<p>我们也可以采用 linspace 命令创建行向量,这向量含有 a 到 b 之间间隔相等(等差)的 n 个元素。<br>linspace(a, b)创建了 a、b 之间含有 100 个等差元素的向量,而 linspace(a, b, n)创 建了 a、b 之间含有 n 个等差元素的向量。<br>不管是哪种形式,MATLAB 自动确定元素之间的 增量。 </p>
<p>MATLAB 还允许创建 n 个对数值相隔相同的行向量,使用的格式为 logspace(a, b, n) 这创建了 10^a和 10^b之间 n 个对数值等差的向量。例如: </p>
<blockquote>
<blockquote>
<p>logspace(1, 2, 5) ans = 10.0000 17.7828 31.6228 56.2341 100.0000<br> 另一个例子:<br>logspace(-1, 1, 6) ans = 0.1000 0.2512 0.6310 1.5849 3.9811 10.0000 </p>
</blockquote>
</blockquote>
<p>命令 length(X) 返回向量中包含元素的个数,例如: >> A = [2;3;3;4;5]; >> length(A) ans = 5<br>length 命令既可以应用到行向量和列向量,也能应用到矩阵,但求的是列的数量,相当于把矩阵当成行向量。</p>
<p>使用 max 或 min 命令我们还可以找出向量中数值大和小的元素。例如: >> A = [8 4 4 1 7 11 2 0]; >> max(A) ans = 11 >> min(A) ans = 0 </p>
<p>向量的数量积(点乘){严格来说这不是向量的数量积,少了加法操作},使用数组乘法(.<em>)来完成。首先 我们定义一个向量。 注意(.</em>)运算的两个向量应该是同类型的(数组乘法嘛)</p>
<blockquote>
<blockquote>
<p>J = [0; 3; 4];<br> 现在我们就可以做数组相乘了:<br>J.*J ans = 0 9 16 (这是个列向量)<br>需要注意的是,这里的“数量积”得到的并不是一个数量(数学上,两个向量对应各元素相乘后相加所得的值),而是一个新的列向量,每个元素是相乘的两个向量的对应元素之积。<br>向量的模是一定是实数。<br>向量元素的总和可以使用 sum 操作符: </p>
</blockquote>
</blockquote>
<blockquote>
<blockquote>
<p>a = sum(J.*J) a = 25 </p>
</blockquote>
</blockquote>
<p>【数学重点】含有复数的向量求模时,是对原向量与其共轭复数向量的数量积求开根。<br>使用 conj 命令计算向量的共轭复数向量:</p>
<blockquote>
<blockquote>
<p>u = [i; 1+2i; 4];<br> v = conj(u)<br> v = 0 - 1.0000i 1.0000 - 2.0000i 4.0000 (这是一个列向量)<br>注意使用conj()命令求共轭复数向量不改变原来向量的行列性质,使用转置操作符也可以求复数向量的共轭复数向量,但这个向量跟原向量的行列性质相反(转置嘛)。</p>
</blockquote>
</blockquote>
<p>所以求向量的模的通用公式为:<br>sqrt(a*(a’)),其中a为行向量。而更为通用的是sqrt(dot(J,J))<br>此时不论a是否为复数向量,都可以求得向量的模。</p>
<p>可以使用 abs 命令返回向量的绝对值——即在原位置返回向量中每个元素的绝对 值。例如: </p>
<blockquote>