應用 [TensorFlow 2.0 + Keras] 建構 CNN深度學習網路 執行 Fashion-MNIST 圖片分類

動機:因應近來 TensorFlow 釋出 2.0,想要運用 [TensorFlow 2.0 + Keras] 來構建 CNN 深度學習網路,並輸入 Fashion MNIST 公開資料集來做 圖片分類(Image Classification),要如何做呢?!

準備環境
1.OS: Ubuntu 18.04.3 LTS
2.GPU: GeForce GTX 1080 x 2
3.CUDA: 10.1
4.Python 3.7.4
5.Jupyter-notebook 6.0.1

操作步驟
全程使用 Jupyter-Notebook 操作畫面(第七版),請參閱連結網頁... http://dmfamily.ddns.net:8000/classification7.html
程式分享於... https://github.com/dvsseed/tf2_keras_cnn_jupyter/blob/master/classification7.ipynb
另外,筆者將上述網頁內容(第四版, 內容不完整)摘錄至本文尾...

心得
1.本次操作係以 [Dataset Model Analysis] 三階段製作,使用 Jupter Notebook 來直觀程式語法及執行結果,十分方便自學及教學,感謝背後許多辛苦付出的人士!!
2.有關『三階段』,可以區分:問題方法結論(結果)
2.1.問題實際的問題為何?!
2.2.方法示意圖指令 (加註解),目的為何?!
2.3.結論:說明清楚並呼應問題
2.4.要前後一致,指令要正確、版本一致
3.有關『三階段』,可以區分:DatasetModelAnalysis
3.1.Dataset:要有基本描述資料探索資料屬性、秀圖(指令→結果)...
3.2.Model建立模型(有幾層)、參數標註清楚、如何調整(指令)、效果如何...
3.3.Analysis分析Result、觀察優缺點(指令)
3.4.流程圖、圖(視覺化)
4.有關 Blogger 無法秀出 PDF內容及嵌入 HTML程式碼內容,雖本文 tf2.0 程式已多次改版,但筆者已賴得做 html tag 轉換,故 文尾網頁內容 未適時更新,特向各位讀者致歉!!


p.s. 有關本次教學檔案(三個程式檔),筆者分享於 https://github.com/dvsseed/tf2_keras_cnn_jupyter


參攷
1. TensorFlow, https://www.tensorflow.org/
2. Keras, https://keras.io/
3. Python, https://www.python.org/
4. Jupyter Notebook, https://jupyter.org/


X                           X                           X                           X                           X


Image Classification: Fashion MNIST with TensorFlow 2 + Keras and Deep Learning -- CNN(Convolutional Neural Network)

The TensorFlow 2.0.0 Tutorials and Courses by Davis on 2019-10-30

Ref: https://www.tensorflow.org/tutorials/keras/classification


ubuntu.jpeg

In [ ]:
# 檢查 python version
!python3 -V
In [1]:
# 更新 The Python Package Installer — pip
# !pip3 install --user --upgrade pip
# !pip3 install --user -U tensorflow-gpu
# !pip3 install --user -U matplotlib
# !pip3 install --user -U jupyter
# !pip3 install --user -U numpy
!pip3 -V
pip 19.3.1 from /home/d1075102/.local/lib/python3.7/site-packages/pip (python 3.7)

Outline

flow1.png


載入 python packages

import.png
In [2]:
from __future__ import (absolute_import, division, print_function, unicode_literals)  # 參攷[1]

# TensorFlow and tf.keras
import tensorflow as tf          # 參攷[2]
from tensorflow import keras     # 參攷[3]

print(tf.__version__)            # 查詢 TensorFlow版本
print(tf.version.VERSION)
print(keras.__version__)         # 查詢 keras 版本

# Helper libraries
import numpy as np               # 陣列運算的數學函數函式庫, 參攷[4]
import matplotlib.pyplot as plt  # 繪圖庫, 參攷[5]

import Ipynb_importer  # 參攷[6]
import tf2tools

# 將matplotlib的圖表直接嵌入到Notebook之中, 參攷[7]
%matplotlib inline
2.0.0
2.0.0
2.2.4-tf
importing Jupyter notebook from tf2tools.ipynb

Dataset

Dataset

Back

Fashion MNIST dataset 簡介

  • Zalando(德國的時尚科技公司)旗下的研究部門提供(參攷[1])
  • 涵蓋 10 種類別(categories)、共 70,000 個不同商品的正面圖片(images)
  • 訓練集/測試集60,000 / 10,000 的數據劃分(參攷[2])
  • Fashion-MNIST的大小(size):28x28 pixels灰階(grayscale)圖片、深度為 8 bits(灰階影像畫素:0 - 255, 0:代表黑色、255:代表白色) dataset.png

參攷

  1. https://research.zalando.com/welcome/mission/research-projects/fashion-mnist/
  2. https://github.com/zalandoresearch/fashion-mnist
Input Process Output
'T-shirt/top', 'Trouser', 'Pullover', 'Dress', 'Coat', 'Sandal', 'Shirt', 'Sneaker', 'Bag', 'Ankle boot'   [ ]   class_names  
In [3]:
# 利用 List(串列), 建立 10 種類別, 參攷
class_names = ['T-shirt/top',  # 0: index
               'Trouser',      # 1
               'Pullover',     # 2
               'Dress',        # 3
               'Coat',         # 4
               'Sandal',       # 5
               'Shirt',        # 6
               'Sneaker',      # 7
               'Bag',          # 8
               'Ankle boot']   # 9
Input Process Output
class_names   print(len( ))   10  
In [4]:
# 列出類別總數
print(len(class_names))
10
Input Process Output
9   class_names[ ]   'Ankle boot'  
In [5]:
# 列出第 10 個(輸入: 數字), index 從 0 起算
class_names[9]
Out[5]:
'Ankle boot'
In [6]:
# 使用 "關鍵字" 來搜尋
keyword = 'Ankle boot'

# using filter()
# to find indices
res_list = list(filter(lambda x: class_names[x] == keyword, range(len(class_names))))  # 參攷

# printing resultant
print("[" + keyword + "] is " + str(res_list[0]))
[Ankle boot] is 9
Input Process Output
class_names     ['T-shirt/top', 'Trouser', 'Pullover', 'Dress', 'Coat', 'Sandal', 'Shirt', 'Sneaker', 'Bag', 'Ankle boot']  
In [7]:
# 列出所有類別名稱
class_names
Out[7]:
['T-shirt/top',
 'Trouser',
 'Pullover',
 'Dress',
 'Coat',
 'Sandal',
 'Shirt',
 'Sneaker',
 'Bag',
 'Ankle boot']

fashion-mnist.jpeg
Input Process Output
fashion_mnist   .load_data()   (train_images, train_labels), (test_images, test_labels)  
In [8]:
# 下載 Fashion-MNIST dataset
fashion_mnist = keras.datasets.fashion_mnist

# 載入 Fashion-MNIST
(train_images, train_labels), (test_images, test_labels) = fashion_mnist.load_data()

train_images = [圖片a, 圖片b, 圖片c ...]

train_labels = [數字a, 數字b, 數字c ...]

  1. 提取資料(Extract):將資料從容器(硬碟、雲端等)提取
  2. 轉換資料(Transform):將資料轉換為模型可讀取的資料,同時進行數據清理等預處理
  3. 裝載資料(Load):將處理好的資料裝載至加速器(GPU)
In [9]:
# 秀出 Fashion-MNIST 檔案下載的位置
!ls ~/.keras/datasets/fashion-mnist/*ubyte.gz

#!dir %userprofile%\.keras\datasets\fashion-mnist  # for windows
/home/d1075102/.keras/datasets/fashion-mnist/t10k-images-idx3-ubyte.gz
/home/d1075102/.keras/datasets/fashion-mnist/t10k-labels-idx1-ubyte.gz
/home/d1075102/.keras/datasets/fashion-mnist/train-images-idx3-ubyte.gz
/home/d1075102/.keras/datasets/fashion-mnist/train-labels-idx1-ubyte.gz

參攷: https://support.anaconda.com/hc/en-us/articles/360023858254-Executing-Terminal-Commands-in-Jupyter-Notebooks

  • train-images-idx3-ubyte.gz 60,000 張訓練集圖片
  • train-labels-idx1-ubyte.gz 60,000 張訓練集圖片對應的標籤
  • t10k-images-idx3-ubyte.gz 10,000 張測試集圖片
  • t10k-labels-idx1-ubyte.gz 10,000 張測試集圖片對應的標籤

THE MNIST DATABASE

sites: http://yann.lecun.com/exdb/mnist/

二進位文件的儲存格式

mnist_ubyte.png
讀取(open)訓練集圖像檔案
[:4] magic number
[4:8] number of images
[8:12] number of rows
[12:16] number of columns

擷取第1張圖片的像素
[16: 16+784] pixel

利用numpy和cv2轉換格式
並保存(cv2.imwrite)成.jpg格式的圖片
boot.png
In [10]:
# 以訓練集圖像文件 train-images-idx3-ubyte 為例
# 使用open()函數打開文件,並使用read()方法將所有的文件數據讀入到一個字串中
with open('/home/d1075102/.keras/datasets/fashion-mnist/train-images-idx3-ubyte', 'rb') as f:
    trfile = f.read()  # file是str類型,其中的每個元素是存儲的1個byte的內容

magic_number = trfile[:4]           # 查看前4個bytes,即magic number的內容,是否是2051
print(magic_number)                 # Binary
print(magic_number.hex())           # Hexadecimal, hex
print(int(magic_number.hex(), 16))  # 轉 Decimal, dec
print()  ##

num_images = int(trfile[4:8].hex(), 16)  # 查看圖像數量
print('Number of images: {}'.format(num_images))  # 參攷[1]
h_image = int(trfile[8:12].hex(), 16)    # 圖像高度
print('Height of image: {}'.format(h_image))
w_image = int(trfile[12:16].hex(), 16)   # 圖像寬度
print('Width of image: {}'.format(w_image))
print()  ##

# 擷取第1張圖片的像素
image1 = [item for item in trfile[16 : 16 + 784]]
print('Pixel of image: {}'.format(len(image1)))

import cv2
# 利用numpy和cv2轉換其格式,並保存成.jpg格式的圖片
image1_np = np.array(image1, dtype=np.uint8).reshape(28, 28, 1)
# 圖片大小: nH高×nW寬×nC通道數(channel), 彩色圖片的nC數為3(三原色RGB)、灰階圖片nC數則為1, 參攷[2]
print('Shape of image: {}'.format(image1_np.shape))

cv2.imwrite('image1.jpg', image1_np)  # 參攷[3]
b'\x00\x00\x08\x03'
00000803
2051

Number of images: 60000
Height of image: 28
Width of image: 28

Pixel of image: 784
Shape of image: (28, 28, 1)
Out[10]:
True
In [ ]:
# 秀出 上述解壓後之圖片 image1.jpg
# import matplotlib.pyplot as plt    # plt 用於顯示圖片
import matplotlib.image as mpimg     # mpimg 用於讀取圖片, 參攷

boot = mpimg.imread('image1.jpg')    # 讀取 .jpg 成為 numpy.ndarray
print(boot.shape)  # (28, 28)
print(type(boot))

tf2tools.plot_image(boot)
Don't do this cell
In [ ]:
''' ***請勿執行***請勿執行***請勿執行*** '''
# 將MNIST數據集保存成.jpg圖片格式: ubyte=>jpg
''' 將二進制格式的MNIST數據集轉成.jpg圖片格式並保存,圖片標籤包含在圖片檔名中 '''
import numpy as np
import cv2
import os
import codecs

def save_mnist_to_jpg(mnist_image_file, mnist_label_file, save_dir):
    if 'train' in os.path.basename(mnist_image_file):  # 參攷[1]
        num_file = 60000
        prefix = 'train'
    else:
        num_file = 10000
        prefix = 'test'

    with open(mnist_image_file, 'rb') as f1:  # 參攷[2]
        image_file = f1.read()
    with open(mnist_label_file, 'rb') as f2:
        label_file = f2.read()

    image_file = image_file[16:]
    label_file = label_file[8:]

    for i in range(num_file):
        label = label_file[i]
        image_list = [item for item in image_file[i * 784 : i * 784 + 784]]
        image_np = np.array(image_list, dtype=np.uint8).reshape(28, 28, 1)
        save_name = os.path.join(save_dir, '{}_{}_{}.jpg'.format(prefix, i, label))  # 參攷[3]
        cv2.imwrite(save_name, image_np)
        print('{} ==> {}_{}_{}.jpg'.format(i, prefix, i, label))

train_image_file = '/home/d1075102/.keras/datasets/fashion-mnist/train-images-idx3-ubyte'
train_label_file = '/home/d1075102/.keras/datasets/fashion-mnist/train-labels-idx1-ubyte'
test_image_file = '/home/d1075102/.keras/datasets/fashion-mnist/t10k-images-idx3-ubyte'
test_label_file = '/home/d1075102/.keras/datasets/fashion-mnist/t10k-labels-idx1-ubyte'

save_train_dir = '/home/d1075102/.keras/datasets/fashion-mnist/train_images/'
save_test_dir ='/home/d1075102/.keras/datasets/fashion-mnist/test_images/'

if not os.path.exists(save_train_dir):  # 參攷[4]
    os.makedirs(save_train_dir)
if not os.path.exists(save_test_dir):
    os.makedirs(save_test_dir)

save_mnist_to_jpg(train_image_file, train_label_file, save_train_dir)
save_mnist_to_jpg(test_image_file, test_label_file, save_test_dir)

Training set

  • training set = 60,000 images
  • each image represented as 28 x 28 pixels
Input Process Output
train_images.shape   print( )   (60000, 28, 28)  
In [11]:
# 秀出 張量tensor: (張數, 圖高, 圖寬)
print(train_images.shape)  # shape函數表示張量的形狀, 參攷[1]
print(train_images.dtype)  # data type, 參攷[2]
(60000, 28, 28)
uint8
Input Process Output
len(train_labels)   print( )   60000  
In [12]:
# 秀出 資料量
print(len(train_labels))  # 參攷[1]
print(train_labels.size)  # 參攷[2]
60000
60000
Input Process Output
train_labels   type( )   numpy.ndarray  
In [13]:
# 秀出 屬性
type(train_labels)
# An array object represents a multidimensional, homogeneous array of fixed-size items.
# ndarray資料結構: 只容許一種資料類型,
#                 如果同時儲存有數值,布林值,會被自動轉換為數值,
#                 如果同時儲存有數值,布林值與文字,會被自動轉換為文字
Out[13]:
numpy.ndarray
In [14]:
# 秀出 資料量
len(train_labels)
Out[14]:
60000
In [15]:
# 秀出 label 內容, Each label is an integer between 0 and 9:
train_labels
Out[15]:
array([9, 0, 0, ..., 3, 0, 5], dtype=uint8)

Test set

  • test set = 10,000 images
  • each image represented as 28 x 28 pixels
In [16]:
# 秀出 張量tensor: (張數, 圖高, 圖寬)
test_images.shape  # shape函數表示張量的形狀
Out[16]:
(10000, 28, 28)
In [17]:
# 秀出 資料量
len(test_labels)
Out[17]:
10000
In [18]:
# 秀出 28x28 維的 灰階畫素位元值: 0-255
print(train_images[0])
[[  0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0
    0   0   0   0   0   0   0   0   0   0]
 [  0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0
    0   0   0   0   0   0   0   0   0   0]
 [  0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0
    0   0   0   0   0   0   0   0   0   0]
 [  0   0   0   0   0   0   0   0   0   0   0   0   1   0   0  13  73   0
    0   1   4   0   0   0   0   1   1   0]
 [  0   0   0   0   0   0   0   0   0   0   0   0   3   0  36 136 127  62
   54   0   0   0   1   3   4   0   0   3]
 [  0   0   0   0   0   0   0   0   0   0   0   0   6   0 102 204 176 134
  144 123  23   0   0   0   0  12  10   0]
 [  0   0   0   0   0   0   0   0   0   0   0   0   0   0 155 236 207 178
  107 156 161 109  64  23  77 130  72  15]
 [  0   0   0   0   0   0   0   0   0   0   0   1   0  69 207 223 218 216
  216 163 127 121 122 146 141  88 172  66]
 [  0   0   0   0   0   0   0   0   0   1   1   1   0 200 232 232 233 229
  223 223 215 213 164 127 123 196 229   0]
 [  0   0   0   0   0   0   0   0   0   0   0   0   0 183 225 216 223 228
  235 227 224 222 224 221 223 245 173   0]
 [  0   0   0   0   0   0   0   0   0   0   0   0   0 193 228 218 213 198
  180 212 210 211 213 223 220 243 202   0]
 [  0   0   0   0   0   0   0   0   0   1   3   0  12 219 220 212 218 192
  169 227 208 218 224 212 226 197 209  52]
 [  0   0   0   0   0   0   0   0   0   0   6   0  99 244 222 220 218 203
  198 221 215 213 222 220 245 119 167  56]
 [  0   0   0   0   0   0   0   0   0   4   0   0  55 236 228 230 228 240
  232 213 218 223 234 217 217 209  92   0]
 [  0   0   1   4   6   7   2   0   0   0   0   0 237 226 217 223 222 219
  222 221 216 223 229 215 218 255  77   0]
 [  0   3   0   0   0   0   0   0   0  62 145 204 228 207 213 221 218 208
  211 218 224 223 219 215 224 244 159   0]
 [  0   0   0   0  18  44  82 107 189 228 220 222 217 226 200 205 211 230
  224 234 176 188 250 248 233 238 215   0]
 [  0  57 187 208 224 221 224 208 204 214 208 209 200 159 245 193 206 223
  255 255 221 234 221 211 220 232 246   0]
 [  3 202 228 224 221 211 211 214 205 205 205 220 240  80 150 255 229 221
  188 154 191 210 204 209 222 228 225   0]
 [ 98 233 198 210 222 229 229 234 249 220 194 215 217 241  65  73 106 117
  168 219 221 215 217 223 223 224 229  29]
 [ 75 204 212 204 193 205 211 225 216 185 197 206 198 213 240 195 227 245
  239 223 218 212 209 222 220 221 230  67]
 [ 48 203 183 194 213 197 185 190 194 192 202 214 219 221 220 236 225 216
  199 206 186 181 177 172 181 205 206 115]
 [  0 122 219 193 179 171 183 196 204 210 213 207 211 210 200 196 194 191
  195 191 198 192 176 156 167 177 210  92]
 [  0   0  74 189 212 191 175 172 175 181 185 188 189 188 193 198 204 209
  210 210 211 188 188 194 192 216 170   0]
 [  2   0   0   0  66 200 222 237 239 242 246 243 244 221 220 193 191 179
  182 182 181 176 166 168  99  58   0   0]
 [  0   0   0   0   0   0   0  40  61  44  72  41  35   0   0   0   0   0
    0   0   0   0   0   0   0   0   0   0]
 [  0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0
    0   0   0   0   0   0   0   0   0   0]
 [  0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0
    0   0   0   0   0   0   0   0   0   0]]
boot_excel.png
In [19]:
# 秀出 第一張訓練圖像
img = train_images[0]
print(img.shape)  # tensor: (圖高height, 圖寬width)

tf2tools.plot_image(img)
(28, 28)
In [ ]:
# 畫一張全黑的圖片, 0代表著黑色,255代表白色
# np.zeros 產生給定維度 用0填充 的陣列(數組)
black_zeros_array = np.zeros((28, 28), dtype='float')

# print(black_zeros_array)

tf2tools.plot_image(black_zeros_array)

Preprocess the data

將 (上面的圖片畫素值 / 255):除 255 之後,本來 [0-255] 的值就會變成 [0-1],但數值的分佈比例還是一樣的,在機器學習中,通常用正規化,把值變成 [0-1],方便訓練及提高準確率
In [20]:
# normalizing
train_images = train_images / 255.  # dtype=float64

test_images = test_images / 255.0

print(train_images.dtype)
float64
In [21]:
train_images[0]
Out[21]:
array([[0.        , 0.        , 0.        , 0.        , 0.        ,
        0.        , 0.        , 0.        , 0.        , 0.        ,
        0.        , 0.        , 0.        , 0.        , 0.        ,
        0.        , 0.        , 0.        , 0.        , 0.        ,
        0.        , 0.        , 0.        , 0.        , 0.        ,
        0.        , 0.        , 0.        ],
       [0.        , 0.        , 0.        , 0.        , 0.        ,
        0.        , 0.        , 0.        , 0.        , 0.        ,
        0.        , 0.        , 0.        , 0.        , 0.        ,
        0.        , 0.        , 0.        , 0.        , 0.        ,
        0.        , 0.        , 0.        , 0.        , 0.        ,
        0.        , 0.        , 0.        ],
       [0.        , 0.        , 0.        , 0.        , 0.        ,
        0.        , 0.        , 0.        , 0.        , 0.        ,
        0.        , 0.        , 0.        , 0.        , 0.        ,
        0.        , 0.        , 0.        , 0.        , 0.        ,
        0.        , 0.        , 0.        , 0.        , 0.        ,
        0.        , 0.        , 0.        ],
       [0.        , 0.        , 0.        , 0.        , 0.        ,
        0.        , 0.        , 0.        , 0.        , 0.        ,
        0.        , 0.        , 0.00392157, 0.        , 0.        ,
        0.05098039, 0.28627451, 0.        , 0.        , 0.00392157,
        0.01568627, 0.        , 0.        , 0.        , 0.        ,
        0.00392157, 0.00392157, 0.        ],
       [0.        , 0.        , 0.        , 0.        , 0.        ,
        0.        , 0.        , 0.        , 0.        , 0.        ,
        0.        , 0.        , 0.01176471, 0.        , 0.14117647,
        0.53333333, 0.49803922, 0.24313725, 0.21176471, 0.        ,
        0.        , 0.        , 0.00392157, 0.01176471, 0.01568627,
        0.        , 0.        , 0.01176471],
       [0.        , 0.        , 0.        , 0.        , 0.        ,
        0.        , 0.        , 0.        , 0.        , 0.        ,
        0.        , 0.        , 0.02352941, 0.        , 0.4       ,
        0.8       , 0.69019608, 0.5254902 , 0.56470588, 0.48235294,
        0.09019608, 0.        , 0.        , 0.        , 0.        ,
        0.04705882, 0.03921569, 0.        ],
       [0.        , 0.        , 0.        , 0.        , 0.        ,
        0.        , 0.        , 0.        , 0.        , 0.        ,
        0.        , 0.        , 0.        , 0.        , 0.60784314,
        0.9254902 , 0.81176471, 0.69803922, 0.41960784, 0.61176471,
        0.63137255, 0.42745098, 0.25098039, 0.09019608, 0.30196078,
        0.50980392, 0.28235294, 0.05882353],
       [0.        , 0.        , 0.        , 0.        , 0.        ,
        0.        , 0.        , 0.        , 0.        , 0.        ,
        0.        , 0.00392157, 0.        , 0.27058824, 0.81176471,
        0.8745098 , 0.85490196, 0.84705882, 0.84705882, 0.63921569,
        0.49803922, 0.4745098 , 0.47843137, 0.57254902, 0.55294118,
        0.34509804, 0.6745098 , 0.25882353],
       [0.        , 0.        , 0.        , 0.        , 0.        ,
        0.        , 0.        , 0.        , 0.        , 0.00392157,
        0.00392157, 0.00392157, 0.        , 0.78431373, 0.90980392,
        0.90980392, 0.91372549, 0.89803922, 0.8745098 , 0.8745098 ,
        0.84313725, 0.83529412, 0.64313725, 0.49803922, 0.48235294,
        0.76862745, 0.89803922, 0.        ],
       [0.        , 0.        , 0.        , 0.        , 0.        ,
        0.        , 0.        , 0.        , 0.        , 0.        ,
        0.        , 0.        , 0.        , 0.71764706, 0.88235294,
        0.84705882, 0.8745098 , 0.89411765, 0.92156863, 0.89019608,
        0.87843137, 0.87058824, 0.87843137, 0.86666667, 0.8745098 ,
        0.96078431, 0.67843137, 0.        ],
       [0.        , 0.        , 0.        , 0.        , 0.        ,
        0.        , 0.        , 0.        , 0.        , 0.        ,
        0.        , 0.        , 0.        , 0.75686275, 0.89411765,
        0.85490196, 0.83529412, 0.77647059, 0.70588235, 0.83137255,
        0.82352941, 0.82745098, 0.83529412, 0.8745098 , 0.8627451 ,
        0.95294118, 0.79215686, 0.        ],
       [0.        , 0.        , 0.        , 0.        , 0.        ,
        0.        , 0.        , 0.        , 0.        , 0.00392157,
        0.01176471, 0.        , 0.04705882, 0.85882353, 0.8627451 ,
        0.83137255, 0.85490196, 0.75294118, 0.6627451 , 0.89019608,
        0.81568627, 0.85490196, 0.87843137, 0.83137255, 0.88627451,
        0.77254902, 0.81960784, 0.20392157],
       [0.        , 0.        , 0.        , 0.        , 0.        ,
        0.        , 0.        , 0.        , 0.        , 0.        ,
        0.02352941, 0.        , 0.38823529, 0.95686275, 0.87058824,
        0.8627451 , 0.85490196, 0.79607843, 0.77647059, 0.86666667,
        0.84313725, 0.83529412, 0.87058824, 0.8627451 , 0.96078431,
        0.46666667, 0.65490196, 0.21960784],
       [0.        , 0.        , 0.        , 0.        , 0.        ,
        0.        , 0.        , 0.        , 0.        , 0.01568627,
        0.        , 0.        , 0.21568627, 0.9254902 , 0.89411765,
        0.90196078, 0.89411765, 0.94117647, 0.90980392, 0.83529412,
        0.85490196, 0.8745098 , 0.91764706, 0.85098039, 0.85098039,
        0.81960784, 0.36078431, 0.        ],
       [0.        , 0.        , 0.00392157, 0.01568627, 0.02352941,
        0.02745098, 0.00784314, 0.        , 0.        , 0.        ,
        0.        , 0.        , 0.92941176, 0.88627451, 0.85098039,
        0.8745098 , 0.87058824, 0.85882353, 0.87058824, 0.86666667,
        0.84705882, 0.8745098 , 0.89803922, 0.84313725, 0.85490196,
        1.        , 0.30196078, 0.        ],
       [0.        , 0.01176471, 0.        , 0.        , 0.        ,
        0.        , 0.        , 0.        , 0.        , 0.24313725,
        0.56862745, 0.8       , 0.89411765, 0.81176471, 0.83529412,
        0.86666667, 0.85490196, 0.81568627, 0.82745098, 0.85490196,
        0.87843137, 0.8745098 , 0.85882353, 0.84313725, 0.87843137,
        0.95686275, 0.62352941, 0.        ],
       [0.        , 0.        , 0.        , 0.        , 0.07058824,
        0.17254902, 0.32156863, 0.41960784, 0.74117647, 0.89411765,
        0.8627451 , 0.87058824, 0.85098039, 0.88627451, 0.78431373,
        0.80392157, 0.82745098, 0.90196078, 0.87843137, 0.91764706,
        0.69019608, 0.7372549 , 0.98039216, 0.97254902, 0.91372549,
        0.93333333, 0.84313725, 0.        ],
       [0.        , 0.22352941, 0.73333333, 0.81568627, 0.87843137,
        0.86666667, 0.87843137, 0.81568627, 0.8       , 0.83921569,
        0.81568627, 0.81960784, 0.78431373, 0.62352941, 0.96078431,
        0.75686275, 0.80784314, 0.8745098 , 1.        , 1.        ,
        0.86666667, 0.91764706, 0.86666667, 0.82745098, 0.8627451 ,
        0.90980392, 0.96470588, 0.        ],
       [0.01176471, 0.79215686, 0.89411765, 0.87843137, 0.86666667,
        0.82745098, 0.82745098, 0.83921569, 0.80392157, 0.80392157,
        0.80392157, 0.8627451 , 0.94117647, 0.31372549, 0.58823529,
        1.        , 0.89803922, 0.86666667, 0.7372549 , 0.60392157,
        0.74901961, 0.82352941, 0.8       , 0.81960784, 0.87058824,
        0.89411765, 0.88235294, 0.        ],
       [0.38431373, 0.91372549, 0.77647059, 0.82352941, 0.87058824,
        0.89803922, 0.89803922, 0.91764706, 0.97647059, 0.8627451 ,
        0.76078431, 0.84313725, 0.85098039, 0.94509804, 0.25490196,
        0.28627451, 0.41568627, 0.45882353, 0.65882353, 0.85882353,
        0.86666667, 0.84313725, 0.85098039, 0.8745098 , 0.8745098 ,
        0.87843137, 0.89803922, 0.11372549],
       [0.29411765, 0.8       , 0.83137255, 0.8       , 0.75686275,
        0.80392157, 0.82745098, 0.88235294, 0.84705882, 0.7254902 ,
        0.77254902, 0.80784314, 0.77647059, 0.83529412, 0.94117647,
        0.76470588, 0.89019608, 0.96078431, 0.9372549 , 0.8745098 ,
        0.85490196, 0.83137255, 0.81960784, 0.87058824, 0.8627451 ,
        0.86666667, 0.90196078, 0.2627451 ],
       [0.18823529, 0.79607843, 0.71764706, 0.76078431, 0.83529412,
        0.77254902, 0.7254902 , 0.74509804, 0.76078431, 0.75294118,
        0.79215686, 0.83921569, 0.85882353, 0.86666667, 0.8627451 ,
        0.9254902 , 0.88235294, 0.84705882, 0.78039216, 0.80784314,
        0.72941176, 0.70980392, 0.69411765, 0.6745098 , 0.70980392,
        0.80392157, 0.80784314, 0.45098039],
       [0.        , 0.47843137, 0.85882353, 0.75686275, 0.70196078,
        0.67058824, 0.71764706, 0.76862745, 0.8       , 0.82352941,
        0.83529412, 0.81176471, 0.82745098, 0.82352941, 0.78431373,
        0.76862745, 0.76078431, 0.74901961, 0.76470588, 0.74901961,
        0.77647059, 0.75294118, 0.69019608, 0.61176471, 0.65490196,
        0.69411765, 0.82352941, 0.36078431],
       [0.        , 0.        , 0.29019608, 0.74117647, 0.83137255,
        0.74901961, 0.68627451, 0.6745098 , 0.68627451, 0.70980392,
        0.7254902 , 0.7372549 , 0.74117647, 0.7372549 , 0.75686275,
        0.77647059, 0.8       , 0.81960784, 0.82352941, 0.82352941,
        0.82745098, 0.7372549 , 0.7372549 , 0.76078431, 0.75294118,
        0.84705882, 0.66666667, 0.        ],
       [0.00784314, 0.        , 0.        , 0.        , 0.25882353,
        0.78431373, 0.87058824, 0.92941176, 0.9372549 , 0.94901961,
        0.96470588, 0.95294118, 0.95686275, 0.86666667, 0.8627451 ,
        0.75686275, 0.74901961, 0.70196078, 0.71372549, 0.71372549,
        0.70980392, 0.69019608, 0.65098039, 0.65882353, 0.38823529,
        0.22745098, 0.        , 0.        ],
       [0.        , 0.        , 0.        , 0.        , 0.        ,
        0.        , 0.        , 0.15686275, 0.23921569, 0.17254902,
        0.28235294, 0.16078431, 0.1372549 , 0.        , 0.        ,
        0.        , 0.        , 0.        , 0.        , 0.        ,
        0.        , 0.        , 0.        , 0.        , 0.        ,
        0.        , 0.        , 0.        ],
       [0.        , 0.        , 0.        , 0.        , 0.        ,
        0.        , 0.        , 0.        , 0.        , 0.        ,
        0.        , 0.        , 0.        , 0.        , 0.        ,
        0.        , 0.        , 0.        , 0.        , 0.        ,
        0.        , 0.        , 0.        , 0.        , 0.        ,
        0.        , 0.        , 0.        ],
       [0.        , 0.        , 0.        , 0.        , 0.        ,
        0.        , 0.        , 0.        , 0.        , 0.        ,
        0.        , 0.        , 0.        , 0.        , 0.        ,
        0.        , 0.        , 0.        , 0.        , 0.        ,
        0.        , 0.        , 0.        , 0.        , 0.        ,
        0.        , 0.        , 0.        ]])
In [22]:
# 秀出多張訓練集圖片(含label)
tf2tools.plot_images(train_images, train_labels)
In [ ]:
''' ***請勿執行***請勿執行***請勿執行*** '''
# Show Matplotlib colormaps
from pylab import *
from numpy import outer

rc('text', usetex=False)
a = outer(arange(0, 1, 0.01), ones(10))
plt.figure(figsize=(10, 5))
subplots_adjust(top=0.8, bottom=0.05, left=0.01, right=0.99)
maps=[m for m in cm.datad if not m.endswith("_r")]
maps.sort()
l = len(maps) + 1

for i, m in enumerate(maps):
    plt.subplot(1, l, i + 1)
    plt.axis("off")
    plt.imshow(a, aspect='auto', cmap=get_cmap(m), origin="lower")
    plt.title(m, rotation=90, fontsize=10)

# plt.savefig("colormaps.png", dpi=100, facecolor='gray')  # 保存為png格式
plt.show()

Keras ImageDataGenerator and Data Augmentation

使用keras中的方法對圖像進行增強,例如:旋轉、裁剪、灰階化、平移、仿射變換...等

參攷: https://keras.io/preprocessing/image/

In [23]:
print(train_images.shape)  # shape函數表示張量的形狀: (張數, 圖高, 圖寬, 通道數)

# reshape the images to 4D tensors
# train_data = np.asarray(train_images, dtype=float)
train_data = np.expand_dims(train_images, axis=-1)  # 增加一維,即(張數, 圖高, 圖寬, 通道數), 灰階=1, 參攷
print(train_data.shape)  # tensor: (張數, 圖高, 圖寬, 通道數)

# 秀出 100張測試圖片
tf2tools.plot_Images(train_data[0:100],
                     labels=train_labels[0:100],
                     rows=10,
                     figsize=(15, 15))
(60000, 28, 28)
(60000, 28, 28, 1)
In [24]:
# 參攷 https://www.novatec-gmbh.de/en/blog/keras-data-augmentation-for-cnn/
# 秀出一張圖片
img = train_data[0]
print(img.shape)                      # shape函數表示張量的形狀: (圖高, 圖寬, 通道數)
img1 = img.reshape((1,) + img.shape)  # reshape函數重新定義了原張量的維度, 即(張數, 圖高, 圖寬, 通道數)
print(img1.shape)

tf2tools.plot_Images(img1, figsize=(3, 3))
(28, 28, 1)
(1, 28, 28, 1)

Shift

random_shift allows you to randomly shift by a given fraction of the imagesize in each direction. Here we specify wrg=0.1 and hrg=0.2. That means that we shift up to 0.2 x imagesize (0.2 x 28 = 5.6) pixel up or down and up to 0.1 x imagesize (0.1 x 28 = 2.8) pixel left or right.
In all transformer functions you can specify row_axis, col_axis and channel_axis according to the array of images you pass into the function.
Input Process Output
img   keras.preprocessing.image.random_shift( )   img_shifted  
In [26]:
# 秀出5張 shift 圖片
img_shifted = [ keras.preprocessing.image.random_shift(
    img,                   # 輸入張量,必須是3維: (圖高, 圖寬, 通道數)
    wrg=0.1,               # width shift range, 寬度偏移範圍,作為寬度的浮動部分
    hrg=0.2,               # height shift range, 高度移動範圍,作為高度的浮動部分
    row_axis=0,            # 輸入張量中的行的軸索引
    col_axis=1,            # 輸入張量中的列的軸索引
    channel_axis=2,        # 輸入張量中的通道的軸的索引
    fill_mode='constant',  # 輸入的邊界之外的點根據給定的模式{常數}中的一個填充
    cval=0.                # 如果mode='constant',則用於輸入邊界以外的點的值
) for _ in range(5) ]

tf2tools.plot_Images(img_shifted, figsize=(7, 7))

Rotation

With the random_rotation transformer we can rotate the image randomly by up to x degrees clockwise or counterclockwise. Here we specify a maximum rotation of 20 degrees.
Input Process Output
img   keras.preprocessing.image.random_rotation( )   img_rotated  
In [27]:
# 秀出5張 rotate 圖片
img_rotated = [ keras.preprocessing.image.random_rotation(
    img,                   # 輸入張量,必須是3維
    rg=20,                 # 旋轉範圍,單位角度
    row_axis=0,            # 輸入張量中的行的軸索引
    col_axis=1,            # 輸入張量中的列的軸索引
    channel_axis=2,        # 輸入張量中的通道的軸的索引
    fill_mode='constant',  # 輸入的邊界之外的點根據給定的模式{常數}中的一個填充
    cval=0.                # 如果mode='constant',則用於輸入邊界以外的點的值
) for _ in range(5) ]

tf2tools.plot_Images(img_rotated, figsize=(7, 7))

Shear

The random_shear functions shears an image with a random shearing angle that is calculated from the given `intensity'.
Note that shearing is different from just rotation since it deforms the given image by multiplying it with the following transformation matrix shear.png
Input Process Output
img   keras.preprocessing.image.random_shear( )   img_sheared  
In [28]:
# 秀出5張 shear 圖片
img_sheared = [ keras.preprocessing.image.random_shear(
    img,
    intensity=0.4,    # 角度轉換強度
    row_axis=0,
    col_axis=1,
    channel_axis=2,
    fill_mode='constant',
    cval=0.
) for _ in range(5) ]

tf2tools.plot_Images(img_sheared, figsize=(7, 7))

Zoom

random_zoom zooms in and out of an image. But it don’t use the same zooming factor for horizontal and vertical zoom but two independently random values. We can specify a minimum (here 0.7) and a maximum value (here 1.3) for the zoom. A value bigger than 1.0 zooms in, thus making the object in the image bigger. A value smaller than 1.0 zooms out.
Input Process Output
img   keras.preprocessing.image.random_zoom( )   img_zoomed  
In [29]:
# 秀出5張 zoom 圖片
img_zoomed = [ keras.preprocessing.image.random_zoom(
    img,
    zoom_range=(0.7, 1.3),   # 浮動的元組;縮放範圍的寬度和高度
    row_axis=0,
    col_axis=1,
    channel_axis=2,
    fill_mode='constant',
    cval=0.
) for _ in range(5) ]

tf2tools.plot_Images(img_zoomed, figsize=(7, 7))

ImageDataGenerator

Now we combine every transformation that we just did in one ImageDataGenerator. It is also possibly to allow a flip of the image either horizontally or vertically. For now we disallow that option.
When we start the ImageDataGenerator it runs in an endless loop. But since we just want a few example we let it run in a for loop and break out of it when we have collected enough examples.
This allows us to create 100 images from just one image.
In [30]:
# 資料增強增加訓練樣本
datagen = keras.preprocessing.image.ImageDataGenerator(  # 影像預處理模組
        rotation_range=20,       # 角度值,0~180,影像旋轉
        width_shift_range=0.1,   # 水平平移,相對總寬度的比例
        height_shift_range=0.1,  # 垂直平移,相對總高度的比例
        shear_range=0.5,         # 剪切強度(以弧度逆時針方向剪切角度)
        zoom_range=(0.9, 1.1),   # 隨機縮放範圍
        horizontal_flip=False,   # 隨機水平翻轉
        vertical_flip=False,     # 隨機垂直翻轉
        fill_mode='constant',    # 填充新建立畫素的方法
        cval=0                   # 用於邊界之外的點的值
)

batches = 0
max_batches = 100
img_gen = []

# 產生隨機變換後圖像批量,迴圈是無限產生,需要手動指定終止條件
for x_batch in datagen.flow(img.reshape((1,) + img.shape), batch_size=max_batches):
    img_gen.append(x_batch[0])
    batches += 1
    if batches >= max_batches:
        # generator loops indefinetly
        break
        
tf2tools.plot_Images(img_gen, rows=10, figsize=(10, 10))

將廠商給的圖片檔轉換成Dataset(EagerTensor)
  1. 提取資料(Extract):將資料從容器(硬碟、雲端等)提取
  2. 轉換資料(Transform):將資料轉換為模型可讀取的資料,同時進行數據清理等預處理
  3. 裝載資料(Load):將處理好的資料裝載至加速器(GPU)
In [ ]:
# !pip3 install --user -U pathlib
# !pip3 install --user -U random2
In [31]:
import pathlib
import random
import os
import cv2

AUTOTUNE = tf.data.experimental.AUTOTUNE  # 使用自動調節管道找到 prefetching 的最佳參數
batch_size = 100  # 批次
num_classes = 4   # 類別

# --- 提取資料
# 印出目前工作目錄
currentDirectory = os.getcwd()
# print(type(currentDirectory))
data_root_orig = currentDirectory + "/liBattery"
# 抓取檔案
data_root = pathlib.Path(data_root_orig)
all_image_paths = list(data_root.glob('*/*'))
all_image_paths = [str(path) for path in all_image_paths]
# 將檔案打亂
random.shuffle(all_image_paths)
# print(len(all_image_paths))

# 將資料集分成 訓練:80% 及 測試:20%
r80 = (int)(len(all_image_paths) * 0.8)
train_image_paths = all_image_paths[:r80]      # 80%
test_image_paths = all_image_paths[r80:]       # 20%

train_image_count = len(train_image_paths)
test_image_count = len(test_image_paths)
# print(train_image_count)
# print(test_image_count)

# 列出目錄名稱=>標籤
label_names = sorted(dirs.name for dirs in data_root.glob('*/') if dirs.is_dir())
# print(label_names)

# 為每個標籤編流水號index
label_to_index = dict((name, index) for index, name in enumerate(label_names))
print(label_to_index)
print()

# 創建一個list,包含每個文件的標籤索引
all_image_labels = [label_to_index[pathlib.Path(path).parent.name] for path in all_image_paths]
train_image_labels = all_image_labels[:r80]      # 80%
test_image_labels = all_image_labels[r80:]       # 20%
print('All examples(100%): ', len(all_image_labels))
print('Train examples(80%):', len(train_image_labels))
print('Test examples(20%):  ', len(test_image_labels))

# 秀出一張圖
tf2tools.show_img(train_image_paths[0], train_image_labels[0], label_names)

img = cv2.imread(train_image_paths[0])
print('Original image:', img.shape)
print()

# --- 轉換資料
# 使用 from_tensor_slices 方法將圖像數組切片,得到一個圖像數據集
train_path_ds = tf.data.Dataset.from_tensor_slices(train_image_paths)
test_path_ds = tf.data.Dataset.from_tensor_slices(test_image_paths)

# 創建一個新的數據集,通過在路徑數據集上映射 preprocess_image 來動態加載和格式化圖片
train_image_ds = train_path_ds.map(tf2tools.load_and_preprocess_image, num_parallel_calls=AUTOTUNE)
test_image_ds = test_path_ds.map(tf2tools.load_and_preprocess_image, num_parallel_calls=AUTOTUNE)

# 使用 from_tensor_slices 方法創建一個標籤數據集
train_label_ds = tf.data.Dataset.from_tensor_slices(tf.cast(train_image_labels, tf.int64))
test_label_ds = tf.data.Dataset.from_tensor_slices(tf.cast(test_image_labels, tf.int64))

# 由於這些數據集順序相同,可將它們打包在一起得到 (圖片,標籤) 數據集
train_image_label_ds = tf.data.Dataset.zip((train_image_ds, train_label_ds))
test_image_label_ds = tf.data.Dataset.zip((test_image_ds, test_label_ds))

# 將這對數組切片
train_ds = tf.data.Dataset.from_tensor_slices((train_image_paths, train_image_labels))
test_ds = tf.data.Dataset.from_tensor_slices((test_image_paths, test_image_labels))

train_image_label_ds = train_ds.map(tf2tools.load_and_preprocess_from_path_label)
test_image_label_ds = test_ds.map(tf2tools.load_and_preprocess_from_path_label)

# 設置一個和數據集大小一致的 shuffle buffer size(隨機緩衝區大小)以保證數據
train_ds = train_image_label_ds.shuffle(buffer_size=train_image_count)
train_ds = train_ds.batch(batch_size)

test_ds = test_image_label_ds.batch(batch_size)

train_ds = train_ds.prefetch(buffer_size=AUTOTUNE)
test_ds = test_ds.prefetch(buffer_size=AUTOTUNE)

train_image_batch, train_label_batch = next(iter(train_ds))
test_image_batch, test_label_batch = next(iter(test_ds))

train_label_batch = keras.utils.to_categorical(train_label_batch, num_classes)
test_label_batch = keras.utils.to_categorical(test_label_batch, num_classes)

print('Train dataset:', train_image_batch.shape)
print('Train type:', type(train_image_batch))
print('Test dataset: ', test_image_batch.shape)
{'bottom_NG': 0, 'bottom_OK': 1, 'top_NG': 2, 'top_OK': 3}

All examples(100%):  20
Train examples(80%): 16
Test examples(20%):   4
Original image: (1000, 1000, 3)

Train dataset: (16, 256, 256, 1)
Train type: <class 'tensorflow.python.framework.ops.EagerTensor'>
Test dataset:  (4, 256, 256, 1)

Model

Model

Back

MLP 架構圖 (對照組)

image_preview.png

CNN架構圖

cnn.png

Set up the layers

The basic building block of a neural network is the layer. Layers extract representations from the data fed into them. Hopefully, these representations are meaningful for the problem at hand.
Most of deep learning consists of chaining together simple layers. Most layers, such as tf.keras.layers.Dense, have parameters that are learned during training.
Keras 模型構建包括5個步驟
  1. 定義(define)
  2. 編譯(compile)
  3. 訓練(fit)
  4. 評估(evaluate)
  5. 預測(prediction) keras.png
Don't do this cell
In [ ]:
''' 此為[對照組]***不要執行***不要執行***不要執行*** '''
# MLP序列
train_shape = train_images.shape[1:]  # 擷取訓練圖像的形狀
print(train_images.shape)  # 即(張數, 圖高, 圖寬)
print(train_shape)         # 即(圖高, 圖寬)

num_classes = len(class_names)  # 擷取類別總數

# 定義一個簡單的序列(sequential)模型,多個網路層的線性堆疊,添加相應層次結構
def create_model():
    model = keras.Sequential([  # 參攷[1]
        keras.layers.Flatten(input_shape=train_shape, 
                             name='flatten'),  # 將輸入展平,不影響批量大小,input_shape:輸入數據的形狀, 參攷[2]
        # 在第一層之後,就不再需要指定輸入的尺寸
        keras.layers.Dense(units=512, 
                           activation=tf.nn.relu, 
                           kernel_initializer='normal',
                           bias_initializer='zeros',
                           name='fc1'),  # 全連接層,units:神經元數量, activation:指定激活函數, 參攷[3]
        keras.layers.Dropout(rate=0.2, name='dropout1'),  # 防止過擬合 overfitting, 參攷 [4]
        keras.layers.Dense(units=256, activation=tf.nn.relu, name='fc2'),
        keras.layers.Dropout(rate=0.2, name='dropout2'),
        keras.layers.Dense(units=num_classes, 
                           activation=tf.nn.softmax, 
                           name='output')  # 參攷 [6]
    ])

    # Compile the model
    model.compile(optimizer='adam',  # Optimizer, 基於數據及損失函數來更新模型, 參攷[7, 8]
                  loss='sparse_categorical_crossentropy',  # Loss function, 希望轉向正確的方向並最小化此函數, 參攷[9]
                  metrics=['accuracy'])  # 用於監視訓練和測試步驟, 參攷[10]

    return model
Do this cell
Input Process Output
train_shape   keras.Sequential( )...keras.layers.Conv2D( )...keras.layers.MaxPool2D( )...keras.layers.Dropout( )...keras.layers.Flatten( )...keras.layers.Dense( )...   model  
In [32]:
# CNN序列
train_shape = (train_images.shape[1:] + (1,))  # 擷取訓練圖像的形狀
print(train_images.shape)  # 即(張數, 圖高, 圖寬)
print(train_shape)         # 即(圖高, 圖寬)

num_classes = len(class_names)  # 擷取類別總數

# del model  # 刪除模型

# 定義一個簡單的序列(sequential)模型,多個網路層的線性堆疊,添加相應層次結構
def cnn_model():
    model = keras.Sequential([  # 參攷[1]
        keras.layers.Conv2D(filters=128, 
                            kernel_size=3, 
                            strides=1, 
                            padding='same', 
                            activation=tf.nn.relu, 
                            input_shape=train_shape, 
                            name='conv1'),  # input_shape:輸入數據的形狀, 參攷[2]
        # keras.layers.BatchNormalization(name='bn1'),  # 參攷[11]
        keras.layers.MaxPool2D(pool_size=2, 
                               strides=2, 
                               padding='same', 
                               name='maxpool1'),  # 參攷[12]
        # keras.layers.Dropout(rate=0.1, name='dropoutc1'),  # 防止過擬合 overfitting, 參攷 [4]

        keras.layers.Conv2D(filters=256, 
                            kernel_size=3, 
                            strides=1, 
                            padding='same', 
                            activation=tf.nn.relu, 
                            name='conv2'),
        # keras.layers.BatchNormalization(name='bn2'),
        keras.layers.MaxPool2D(pool_size=2, 
                               strides=2, 
                               padding='same', 
                               name='maxpool2'),
        # keras.layers.Dropout(rate=0.1, name='dropoutc2'),

        keras.layers.Conv2D(filters=512, 
                            kernel_size=3, 
                            strides=1, 
                            padding='same', 
                            activation=tf.nn.relu, 
                            name='conv3'),
        # keras.layers.BatchNormalization(name='bn3'),
        keras.layers.MaxPool2D(pool_size=2, 
                               strides=2, 
                               padding='same', 
                               name='maxpool3'),
        keras.layers.Dropout(rate=0.1, name='dropoutc3'),

        keras.layers.Flatten(name='flatten'),  # 將輸入展平,不影響批量大小

        keras.layers.Dense(units=512,
                           activation=tf.nn.relu, 
                           kernel_initializer='orthogonal',  # 正交初始化權重
                           bias_initializer=tf.keras.initializers.constant(0.1),    # 初始化偏置向量
                           name='fc1'),  # 全連接層,units:神經元數量, activation:指定激活函數, 參攷[3]
        # keras.layers.Dropout(rate=0.1, name='dropoutf1'),  # 防止過擬合 overfitting

        keras.layers.Dense(units=256, 
                           activation=tf.nn.relu, 
                           kernel_initializer='orthogonal',
                           bias_initializer=tf.keras.initializers.constant(0.1),
                           name='fc2'),
        # keras.layers.Dropout(rate=0.1, name='dropoutf2'),

        keras.layers.Dense(units=128, 
                           activation=tf.nn.relu, 
                           kernel_initializer='orthogonal',
                           bias_initializer=tf.keras.initializers.constant(0.1),
                           name='fc3'),
        keras.layers.Dropout(rate=0.1, name='dropoutf3'),
        
        keras.layers.Dense(units=num_classes, 
                           activation=tf.nn.softmax, 
                           name='output')
    ])

    model.trainable = True

    # Compile the model
    model.compile(optimizer='adam',      # Optimizer, 基於數據及損失函數來更新模型, 參攷[7, 8]
                  loss='sparse_categorical_crossentropy',  # Loss function, 希望轉向正確的方向並最小化此函數, 參攷[9]
                  metrics=['accuracy'])  # 用於監視訓練和測試步驟, 參攷[10]

    return model
(60000, 28, 28)
(28, 28, 1)
In [33]:
# 創建一個基本的模型實例
model = cnn_model()
The first layer in this network, tf.keras.layers.Flatten, transforms the format of the images from a two-dimensional array (of 28 by 28 pixels) to a one-dimensional array (of 28 * 28 = 784 pixels). Think of this layer as unstacking rows of pixels in the image and lining them up. This layer has no parameters to learn; it only reformats the data.
After the pixels are flattened, the network consists of a sequence of two tf.keras.layers.Dense layers. These are densely connected, or fully connected, neural layers. The first Dense layer has 128 nodes (or neurons). The second (and last) layer is a 10-node softmax layer that returns an array of 10 probability scores that sum to 1. Each node contains a score that indicates the probability that the current image belongs to one of the 10 classes.

Compile the model

Before the model is ready for training, it needs a few more settings. These are added during the model's compile step:
  • Loss function —This measures how accurate the model is during training. You want to minimize this function to "steer" the model in the right direction.
  • Optimizer —This is how the model is updated based on the data it sees and its loss function.
  • Metrics —Used to monitor the training and testing steps. The following example uses accuracy, the fraction of the images that are correctly classified.
In [34]:
# 顯示模型的結構
model.summary()    # 參攷[1]
len(model.layers)  # 參攷[2]
Model: "sequential"
_________________________________________________________________
Layer (type)                 Output Shape              Param #   
=================================================================
conv1 (Conv2D)               (None, 28, 28, 128)       1280      
_________________________________________________________________
maxpool1 (MaxPooling2D)      (None, 14, 14, 128)       0         
_________________________________________________________________
conv2 (Conv2D)               (None, 14, 14, 256)       295168    
_________________________________________________________________
maxpool2 (MaxPooling2D)      (None, 7, 7, 256)         0         
_________________________________________________________________
conv3 (Conv2D)               (None, 7, 7, 512)         1180160   
_________________________________________________________________
maxpool3 (MaxPooling2D)      (None, 4, 4, 512)         0         
_________________________________________________________________
dropoutc3 (Dropout)          (None, 4, 4, 512)         0         
_________________________________________________________________
flatten (Flatten)            (None, 8192)              0         
_________________________________________________________________
fc1 (Dense)                  (None, 512)               4194816   
_________________________________________________________________
fc2 (Dense)                  (None, 256)               131328    
_________________________________________________________________
fc3 (Dense)                  (None, 128)               32896     
_________________________________________________________________
dropoutf3 (Dropout)          (None, 128)               0         
_________________________________________________________________
output (Dense)               (None, 10)                1290      
=================================================================
Total params: 5,836,938
Trainable params: 5,836,938
Non-trainable params: 0
_________________________________________________________________
Out[34]:
13

Train the model

Training the neural network model requires the following steps:
  1. Feed the training data to the model. In this example, the training data is in the train_images and train_labels arrays.
  2. The model learns to associate images and labels.
  3. You ask the model to make predictions about a test set—in this example, the test_images array. Verify that the predictions match the labels from the test_labels array.
To start training, call the model.fit method—so called because it "fits" the model to the training data:

在訓練期間保存模型(以 checkpoints 形式保存)

您可以使用訓練好的模型而無需從頭開始重新訓練,或在您打斷的地方開始訓練,以防止訓練過程沒有保存。
tf.keras.callbacks.ModelCheckpoint 允許在訓練的過程中和結束時回調保存的模型。

Checkpoint 回調用法

創建一個只在訓練期間保存權重的 tf.keras.callbacks.ModelCheckpoint 回調:
In [35]:
# 資料處理reshape the images to 4D tensors
train_cnn_images = np.expand_dims(train_images, axis=-1)  # 增加一維,灰階=1, (張數, 圖高, 圖寬, 通道數)
test_cnn_images = np.expand_dims(test_images, axis=-1)    # 同上
print(train_cnn_images.shape)
print(test_cnn_images.shape)
print()

import os

checkpoint_dir = "training_1"
# 若目錄不存在,則新建
if not os.path.exists(checkpoint_dir):
    os.makedirs(checkpoint_dir)

checkpoint_path = "training_1/cp.ckpt"
checkpoint_dir = os.path.dirname(checkpoint_path)

# 創建一個保存模型權重的callback
cp_callback = keras.callbacks.ModelCheckpoint(filepath=checkpoint_path,
                                              save_weights_only=True,  # save_best_only=True
                                              verbose=1)

# 使用新的回調訓練模型, epochs=10, 輸出至 history, 建議使用 GPU
history = model.fit(x=train_cnn_images,
                    y=train_labels,
                    epochs=10,
                    validation_data=(test_cnn_images, test_labels),
                    callbacks=[cp_callback],
                    use_multiprocessing=True)  # 通過回調訓練, 參攷

# 可能會生成與保存優化程序狀態相關的警告, 是防止過時使用=>可以忽略
(60000, 28, 28, 1)
(10000, 28, 28, 1)

Train on 60000 samples, validate on 10000 samples
Epoch 1/10
59712/60000 [============================>.] - ETA: 0s - loss: 0.4031 - accuracy: 0.8521
Epoch 00001: saving model to training_1/cp.ckpt
60000/60000 [==============================] - 13s 210us/sample - loss: 0.4028 - accuracy: 0.8522 - val_loss: 0.3198 - val_accuracy: 0.8892
Epoch 2/10
59776/60000 [============================>.] - ETA: 0s - loss: 0.2583 - accuracy: 0.9055
Epoch 00002: saving model to training_1/cp.ckpt
60000/60000 [==============================] - 11s 187us/sample - loss: 0.2584 - accuracy: 0.9055 - val_loss: 0.2753 - val_accuracy: 0.9047
Epoch 3/10
59808/60000 [============================>.] - ETA: 0s - loss: 0.2154 - accuracy: 0.9208
Epoch 00003: saving model to training_1/cp.ckpt
60000/60000 [==============================] - 11s 187us/sample - loss: 0.2153 - accuracy: 0.9209 - val_loss: 0.2842 - val_accuracy: 0.9016
Epoch 4/10
59936/60000 [============================>.] - ETA: 0s - loss: 0.1853 - accuracy: 0.9313
Epoch 00004: saving model to training_1/cp.ckpt
60000/60000 [==============================] - 11s 187us/sample - loss: 0.1853 - accuracy: 0.9312 - val_loss: 0.2485 - val_accuracy: 0.9149
Epoch 5/10
59744/60000 [============================>.] - ETA: 0s - loss: 0.1590 - accuracy: 0.9404
Epoch 00005: saving model to training_1/cp.ckpt
60000/60000 [==============================] - 11s 187us/sample - loss: 0.1590 - accuracy: 0.9404 - val_loss: 0.2578 - val_accuracy: 0.9165
Epoch 6/10
59872/60000 [============================>.] - ETA: 0s - loss: 0.1389 - accuracy: 0.9494
Epoch 00006: saving model to training_1/cp.ckpt
60000/60000 [==============================] - 11s 186us/sample - loss: 0.1389 - accuracy: 0.9494 - val_loss: 0.2788 - val_accuracy: 0.9076
Epoch 7/10
59872/60000 [============================>.] - ETA: 0s - loss: 0.1225 - accuracy: 0.9540
Epoch 00007: saving model to training_1/cp.ckpt
60000/60000 [==============================] - 11s 187us/sample - loss: 0.1225 - accuracy: 0.9539 - val_loss: 0.2360 - val_accuracy: 0.9251
Epoch 8/10
59936/60000 [============================>.] - ETA: 0s - loss: 0.1058 - accuracy: 0.9605
Epoch 00008: saving model to training_1/cp.ckpt
60000/60000 [==============================] - 11s 186us/sample - loss: 0.1059 - accuracy: 0.9605 - val_loss: 0.2694 - val_accuracy: 0.9170
Epoch 9/10
59776/60000 [============================>.] - ETA: 0s - loss: 0.0981 - accuracy: 0.9642
Epoch 00009: saving model to training_1/cp.ckpt
60000/60000 [==============================] - 11s 187us/sample - loss: 0.0982 - accuracy: 0.9642 - val_loss: 0.2809 - val_accuracy: 0.9255
Epoch 10/10
59872/60000 [============================>.] - ETA: 0s - loss: 0.0806 - accuracy: 0.9694
Epoch 00010: saving model to training_1/cp.ckpt
60000/60000 [==============================] - 11s 188us/sample - loss: 0.0806 - accuracy: 0.9694 - val_loss: 0.3112 - val_accuracy: 0.9210
In [36]:
# 秀出 checkpoint 檔案
# checkpoint_dir = "training_1"
!ls {checkpoint_dir}

# import os
# 刪除目錄
# os.system("rm -rf training_1")
checkpoint       cp.ckpt.data-00001-of-00002
cp.ckpt.data-00000-of-00002  cp.ckpt.index

上述代碼將權重存儲到 checkpoint -- 格式化文件的集合中,這些文件僅包含二進位格式的訓練權重。

Checkpoints 包含:

  • 一個或多個包含模型權重的分片
  • 索引文件,指示哪些權重存儲在哪個分片中。
創建一個新的未經訓練的模型
僅恢復模型的權重時,必須具有與原始模型具有相同網路結構的模型。
由於模型具有相同的結構,可以共享權重,儘管它是模型的不同實例。
現在重建一個新的未經訓練的模型,並在測試集上進行評估
未經訓練的模型將在機會水平(chance levels)上執行(準確度約為10%)
Input Process Output
x=test_cnn_images, y=test_labels   model.evaluate( )   loss, acc  
In [37]:
# 創建一個基本模型實例
model = cnn_model()

# 評估模型
loss, acc = model.evaluate(x=test_cnn_images, 
                           y=test_labels, 
                           verbose=2, 
                           use_multiprocessing=True)  # 參攷

print("Untrained model, accuracy: {:5.2f}%".format(100 * acc))
10000/1 - 1s - loss: 2.2773 - accuracy: 0.1000
Untrained model, accuracy: 10.00%

然後從 checkpoint 加載權重並重新評估(準確度約為92%)

In [38]:
# 加載權重
model.load_weights(checkpoint_path)  # 參攷

# 重新評估模型
loss, acc = model.evaluate(x=test_cnn_images, 
                           y=test_labels, 
                           verbose=2,
                           use_multiprocessing=True)

print("Restored model, accuracy: {:5.2f}%".format(100 * acc))
10000/1 - 1s - loss: 0.1964 - accuracy: 0.9210
Restored model, accuracy: 92.10%

如果要進行測試,請重置模型並加載最新的 checkpoint(準確度約為92%)

In [39]:
# 刪除模型
del model

# 創建一個新的模型實例
model = cnn_model()

# 加載之前保存的權重
model.load_weights(checkpoint_path)

# 重新評估模型
loss, acc = model.evaluate(x=test_cnn_images, 
                           y=test_labels, 
                           verbose=2, 
                           use_multiprocessing=True)
print("Restored model, accuracy: {:5.2f}%".format(100 * acc))
WARNING: Logging before flag parsing goes to stderr.
W1031 20:01:36.365928 139822885283648 util.py:144] Unresolved object in checkpoint: (root).optimizer.iter
W1031 20:01:36.366564 139822885283648 util.py:144] Unresolved object in checkpoint: (root).optimizer.beta_1
W1031 20:01:36.366942 139822885283648 util.py:144] Unresolved object in checkpoint: (root).optimizer.beta_2
W1031 20:01:36.367305 139822885283648 util.py:144] Unresolved object in checkpoint: (root).optimizer.decay
W1031 20:01:36.367662 139822885283648 util.py:144] Unresolved object in checkpoint: (root).optimizer.learning_rate
W1031 20:01:36.368025 139822885283648 util.py:152] A checkpoint was restored (e.g. tf.train.Checkpoint.restore or tf.keras.Model.load_weights) but not all checkpointed values were used. See above for specific issues. Use expect_partial() on the load status object, e.g. tf.train.Checkpoint.restore(...).expect_partial(), to silence these warnings, or use assert_consumed() to make the check explicit. See https://www.tensorflow.org/alpha/guide/checkpoints#loading_mechanics for details.
10000/1 - 1s - loss: 0.1964 - accuracy: 0.9210
Restored model, accuracy: 92.10%

手動保存權重

如何將權重加載到模型中。使用 Model.save_weights 方法手動保存它們。
默認情況下, tf.kerassave_weights 特別使用 TensorFlow checkpoints 格式 .ckpt 附檔名
和 (保存在 "HDF5" 附檔名為 .h5 保存並序列化模型)
In [40]:
# 保存權重
model.save_weights('./checkpoints/my_checkpoint')  # 參攷

# 創建模型實例
model = cnn_model()

# Restore the weights
model.load_weights('./checkpoints/my_checkpoint')  # 參攷

# Evaluate the model
loss, acc = model.evaluate(x=test_cnn_images, 
                           y=test_labels, 
                           verbose=2, 
                           use_multiprocessing=True)

print("Restored model, accuracy: {:5.2f}%".format(100 * acc))
10000/1 - 1s - loss: 0.1964 - accuracy: 0.9210
Restored model, accuracy: 92.10%
In [41]:
# 秀出 checkpoints 檔案
!ls {"checkpoints"}
checkpoint      my_checkpoint.data-00001-of-00002
my_checkpoint.data-00000-of-00002  my_checkpoint.index

保存整個模型

模型和優化器可以保存到包含其狀態(權重和變量)和模型參數的文件中。
這可以導出模型,以便在不訪問原始 python 代碼的情況下使用它。
而且可以通過恢復優化器狀態的方式,從中斷的位置恢復訓練。
保存完整模型會非常有用—可以在 TensorFlow.js (HDF5, Saved Model) 加載它們,然後在 web 瀏覽器中訓練和運行它們,
或者使用 TensorFlow Lite 將它們轉換為在移動設備上運行(HDF5, Saved Model)

將模型保存為HDF5文件

Keras 可以使用 HDF5 標準提供基本保存格式。可以將保存的模型視為單個二進位blob

參攷: https://tensorflow.google.cn/federated/api_docs/python/tff/simulation/HDF5ClientData?hl=en

In [42]:
# !pip3 install h5py
# 創建一個新的模型實例
model = cnn_model()

# 訓練模型, epochs=5
model.fit(x=train_cnn_images, 
          y=train_labels, 
          epochs=5, 
          use_multiprocessing=True)

# 將整個模型保存為HDF5文件
model.save('my_model.h5')
Train on 60000 samples
Epoch 1/5
W1031 20:03:09.754965 139822885283648 util.py:144] Unresolved object in checkpoint: (root).optimizer.iter
W1031 20:03:09.755493 139822885283648 util.py:144] Unresolved object in checkpoint: (root).optimizer.beta_1
W1031 20:03:09.755901 139822885283648 util.py:144] Unresolved object in checkpoint: (root).optimizer.beta_2
W1031 20:03:09.756328 139822885283648 util.py:144] Unresolved object in checkpoint: (root).optimizer.decay
W1031 20:03:09.756745 139822885283648 util.py:144] Unresolved object in checkpoint: (root).optimizer.learning_rate
W1031 20:03:09.757166 139822885283648 util.py:152] A checkpoint was restored (e.g. tf.train.Checkpoint.restore or tf.keras.Model.load_weights) but not all checkpointed values were used. See above for specific issues. Use expect_partial() on the load status object, e.g. tf.train.Checkpoint.restore(...).expect_partial(), to silence these warnings, or use assert_consumed() to make the check explicit. See https://www.tensorflow.org/alpha/guide/checkpoints#loading_mechanics for details.
60000/60000 [==============================] - 11s 180us/sample - loss: 0.4158 - accuracy: 0.8487
Epoch 2/5
60000/60000 [==============================] - 10s 173us/sample - loss: 0.2665 - accuracy: 0.9034
Epoch 3/5
60000/60000 [==============================] - 10s 174us/sample - loss: 0.2224 - accuracy: 0.9192
Epoch 4/5
60000/60000 [==============================] - 10s 173us/sample - loss: 0.1890 - accuracy: 0.9307
Epoch 5/5
60000/60000 [==============================] - 10s 174us/sample - loss: 0.1637 - accuracy: 0.9401
In [43]:
# del new_model  # 刪除現有模型

# 重新創建完全相同的模型,包括其權重和優化程序
new_model = keras.models.load_model('my_model.h5')  # 參攷

# 顯示網路結構
new_model.summary()
Model: "sequential_4"
_________________________________________________________________
Layer (type)                 Output Shape              Param #   
=================================================================
conv1 (Conv2D)               (None, 28, 28, 128)       1280      
_________________________________________________________________
maxpool1 (MaxPooling2D)      (None, 14, 14, 128)       0         
_________________________________________________________________
conv2 (Conv2D)               (None, 14, 14, 256)       295168    
_________________________________________________________________
maxpool2 (MaxPooling2D)      (None, 7, 7, 256)         0         
_________________________________________________________________
conv3 (Conv2D)               (None, 7, 7, 512)         1180160   
_________________________________________________________________
maxpool3 (MaxPooling2D)      (None, 4, 4, 512)         0         
_________________________________________________________________
dropoutc3 (Dropout)          (None, 4, 4, 512)         0         
_________________________________________________________________
flatten (Flatten)            (None, 8192)              0         
_________________________________________________________________
fc1 (Dense)                  (None, 512)               4194816   
_________________________________________________________________
fc2 (Dense)                  (None, 256)               131328    
_________________________________________________________________
fc3 (Dense)                  (None, 128)               32896     
_________________________________________________________________
dropoutf3 (Dropout)          (None, 128)               0         
_________________________________________________________________
output (Dense)               (None, 10)                1290      
=================================================================
Total params: 5,836,938
Trainable params: 5,836,938
Non-trainable params: 0
_________________________________________________________________
In [44]:
# 檢查準確率(accuracy)
loss, acc = new_model.evaluate(x=test_cnn_images, 
                               y=test_labels, 
                               verbose=2, 
                               use_multiprocessing=True)

print("Restored model, accuracy: {:5.2f}%".format(100 * acc))
10000/1 - 1s - loss: 0.2788 - accuracy: 0.9191
Restored model, accuracy: 91.91%

從 HDF5文件 顯示各層次名稱及權重等

In [45]:
# del loaded_model  # 刪除現有模型

# 加載模型並對模型進行調整
loaded_model = keras.models.load_model('my_model.h5')
# 修改 model's name
loaded_model._name = 'cnn_model'
loaded_model.summary()
print('Original layers:', len(loaded_model.layers))
print()

# 列印各層名字,權重的形狀
for layer in loaded_model.layers:  # 參攷
    for weight in layer.weights:
        print(layer.name, weight.name, weight.shape)
print()

# 列印單層
layer_name = 'conv1'
print(loaded_model.get_layer(layer_name).name)
# print(loaded_model.get_layer('conv1').get_weights())
print()
# 列印最後一層
print(loaded_model.layers[-1].input)
print(loaded_model.layers[-1].output)
print()

loaded_model._layers.pop(1)   # 刪除第一層
loaded_model._layers.pop(-1)  # 刪除最後一層
loaded_model.summary()
print('After layers:', len(loaded_model.layers))
W1031 20:05:37.696732 139822885283648 training.py:2090] Discrepancy between trainable weights and collected trainable weights, did you set `model.trainable` without calling `model.compile` after ?
Model: "cnn_model"
_________________________________________________________________
Layer (type)                 Output Shape              Param #   
=================================================================
conv1 (Conv2D)               (None, 28, 28, 128)       1280      
_________________________________________________________________
maxpool1 (MaxPooling2D)      (None, 14, 14, 128)       0         
_________________________________________________________________
conv2 (Conv2D)               (None, 14, 14, 256)       295168    
_________________________________________________________________
maxpool2 (MaxPooling2D)      (None, 7, 7, 256)         0         
_________________________________________________________________
conv3 (Conv2D)               (None, 7, 7, 512)         1180160   
_________________________________________________________________
maxpool3 (MaxPooling2D)      (None, 4, 4, 512)         0         
_________________________________________________________________
dropoutc3 (Dropout)          (None, 4, 4, 512)         0         
_________________________________________________________________
flatten (Flatten)            (None, 8192)              0         
_________________________________________________________________
fc1 (Dense)                  (None, 512)               4194816   
_________________________________________________________________
fc2 (Dense)                  (None, 256)               131328    
_________________________________________________________________
fc3 (Dense)                  (None, 128)               32896     
_________________________________________________________________
dropoutf3 (Dropout)          (None, 128)               0         
_________________________________________________________________
output (Dense)               (None, 10)                1290      
=================================================================
Total params: 5,836,938
Trainable params: 5,836,938
Non-trainable params: 0
_________________________________________________________________
Original layers: 13

conv1 conv1_6/kernel:0 (3, 3, 1, 128)
conv1 conv1_6/bias:0 (128,)
conv2 conv2_6/kernel:0 (3, 3, 128, 256)
conv2 conv2_6/bias:0 (256,)
conv3 conv3_6/kernel:0 (3, 3, 256, 512)
conv3 conv3_6/bias:0 (512,)
fc1 fc1_6/kernel:0 (8192, 512)
fc1 fc1_6/bias:0 (512,)
fc2 fc2_6/kernel:0 (512, 256)
fc2 fc2_6/bias:0 (256,)
fc3 fc3_6/kernel:0 (256, 128)
fc3 fc3_6/bias:0 (128,)
output output_6/kernel:0 (128, 10)
output output_6/bias:0 (10,)

conv1

Tensor("dropoutf3_6/Identity:0", shape=(None, 128), dtype=float32)
Tensor("output_6/Identity:0", shape=(None, 10), dtype=float32)

Model: "cnn_model"
_________________________________________________________________
Layer (type)                 Output Shape              Param #   
=================================================================
maxpool1 (MaxPooling2D)      (None, 14, 14, 128)       0         
_________________________________________________________________
conv2 (Conv2D)               (None, 14, 14, 256)       295168    
_________________________________________________________________
maxpool2 (MaxPooling2D)      (None, 7, 7, 256)         0         
_________________________________________________________________
conv3 (Conv2D)               (None, 7, 7, 512)         1180160   
_________________________________________________________________
maxpool3 (MaxPooling2D)      (None, 4, 4, 512)         0         
_________________________________________________________________
dropoutc3 (Dropout)          (None, 4, 4, 512)         0         
_________________________________________________________________
flatten (Flatten)            (None, 8192)              0         
_________________________________________________________________
fc1 (Dense)                  (None, 512)               4194816   
_________________________________________________________________
fc2 (Dense)                  (None, 256)               131328    
_________________________________________________________________
fc3 (Dense)                  (None, 128)               32896     
_________________________________________________________________
dropoutf3 (Dropout)          (None, 128)               0         
=================================================================
Total params: 5,836,938
Trainable params: 5,836,938
Non-trainable params: 0
_________________________________________________________________
After layers: 11
In [46]:
# Exports the Trackable object obj to SavedModel format
tf.saved_model.save(model, 'saved_model/')  # 參攷[1-3]
W1031 20:05:49.301464 139822885283648 deprecation.py:506] From /home/d1075102/.local/lib/python3.7/site-packages/tensorflow_core/python/ops/resource_variable_ops.py:1781: calling BaseResourceVariable.__init__ (from tensorflow.python.ops.resource_variable_ops) with constraint is deprecated and will be removed in a future version.
Instructions for updating:
If using Keras pass *_constraint arguments to layers.
tf2-architektur.png
In [47]:
# 秀出 saved_model 檔案
!ls {'saved_model'}

tf2tools.get_size('saved_model')

# 刪除目錄
# os.system("rm -rf saved_model")
assets saved_model.pb variables
saved_model

Model size: 215.186 KB
Variables size: 68405.11 KB
Total Size: 68620.296 KB

Evaluate accuracy

Next, compare how the model performs on the test dataset:
In [48]:
# 評估
test_loss, test_acc = model.evaluate(x=test_cnn_images, 
                                     y=test_labels, 
                                     verbose=2, 
                                     use_multiprocessing=True)  # 參攷

print('\nTest accuracy:', test_acc, '\tLoss:', test_loss)
10000/1 - 1s - loss: 0.2788 - accuracy: 0.9191

Test accuracy: 0.9191  Loss: 0.26531814611852167

Make predictions

With the model trained, you can use it to make predictions about some images.
It turns out that the accuracy on the test dataset is a little less than the accuracy on the training dataset. This gap between training accuracy and test accuracy represents overfitting. Overfitting is when a machine learning model performs worse on new, previously unseen inputs than on the training data.
Input Process Output
x=test_cnn_images   model.predict( )   predictions  
In [49]:
# 預測
predictions = model.predict(x=test_cnn_images, use_multiprocessing=True)  # 參攷
Here, the model has predicted the label for each image in the testing set. Let's take a look at the first prediction:
Input Process Output
0   predictions[ ]   [1.9726464e-19, 5.6842889e-20, 1.3350342e-19, ..., 9.9999952e-01]  
In [50]:
# 秀出 第一個預測結果
predictions[0]
Out[50]:
array([1.9726464e-19, 5.6842889e-20, 1.3350342e-19, 4.4257174e-18,
       1.9532888e-20, 3.6374831e-07, 6.2868125e-16, 1.5726280e-07,
       1.3409160e-16, 9.9999952e-01], dtype=float32)
A prediction is an array of 10 numbers. They represent the model's "confidence" that the image corresponds to each of the 10 different articles of clothing. You can see which label has the highest confidence value:
Input Process Output
predictions[0]   np.argmax(predictions[ ])   9  
In [51]:
# 返回最大值索引
np.argmax(predictions[0])  # 參攷
Out[51]:
9
So, the model is most confident that this image is an ankle boot, or class_names[9]. Examining the test label shows that this classification is correct:
In [52]:
# 秀出 相對位置的 標籤(類別編號)
test_labels[0]
Out[52]:
9
In [53]:
# 判斷 預測結果 是否相等於 標籤(答案)
np.argmax(predictions[0]) == test_labels[0]
Out[53]:
True
In [54]:
# 秀出 類別名稱
class_names[np.argmax(predictions[0])]
Out[54]:
'Ankle boot'
In [55]:
# 預測結果視覺化
# Plot the first X test images, their predicted labels, and the true labels.
# Color correct predictions in blue and incorrect predictions in red.
tf2tools.plot_predict(predictions, test_labels, test_images)

CPP call TensorFlow...2.0改版中

參攷: https://www.tensorflow.org/api_docs/cc


Analysis

Analysis

Back
analysis.png

顯示訓練歷史記錄的trend圖表

In [56]:
# list all data in history
print(history.history.keys())  # 參攷

# 顯示準確率圖表summarize history for accuracy
key = 'accuracy'
tf2tools.history_plot(history, key)

# 顯示損失函數誤差值圖表summarize history for loss
key = 'loss'
tf2tools.history_plot(history, key)
dict_keys(['loss', 'accuracy', 'val_loss', 'val_accuracy'])

顯示混淆矩陣(confusion matrix)

In [57]:
import seaborn as sns  # 參攷[1]
from sklearn.metrics import confusion_matrix  # 參攷[2]

# predict
y_pred = model.predict_classes(x=test_cnn_images)  # 參攷[3]
# print(y_pred)

# 混淆矩陣:真實值與預測值的對比
# 對角線上的值表示預測正確的數量/比例;非對角線元素是預測錯誤的部分
con_mat = confusion_matrix(test_labels, y_pred)

con_mat_norm = con_mat.astype('float') / con_mat.sum(axis=1)[:, np.newaxis]  # 歸一化
con_mat_norm = np.around(con_mat_norm, decimals=2)

# plot
figure = plt.figure(figsize=(8, 8))
sns.heatmap(con_mat_norm, annot=True, cmap='Blues')

plt.ylim(0, 10)
plt.xlabel('Predicted labels')
plt.ylabel('True labels')
plt.show()

class_names[6]
Out[57]:
'Shirt'

找出預測錯誤的資料

In [58]:
# 用訓練好的 Model 進行 辨識物體圖片
prediction = model.predict_classes(x=test_cnn_images)
# 顯示 測試圖片集 全部的預測結果
print(prediction)
# 顯示 圖片集 第 340~360 筆的預測結果
print(prediction[340:360])

# 透過 DataFrame 對照表清單, 來顯示 Label 真實數值, 與 AI 預設結果
import pandas as pd  # 參攷[1]

# Pandas.DataFrame 的資料集需要一維陣列, 而 test_labels 本身是二維陣列, 需透過 reshape 轉換為一維陣列
test_labels_onearr = test_labels.reshape(len(test_labels))
# print(test_labels_onearr.shape)
print()

checkList = pd.DataFrame(data={  # 參攷[2]
                         'label': test_labels_onearr,  # Column1 名稱: 欄位值集合 (Label 真實數值)
                         'prediction': prediction      # Column2 名稱: 欄位值集合 (CNN 預測結果)
                         })

# 顯示對照表前 10 筆結果
print(checkList[:10])
print()

# 列出對照表中, prediction 欄位值 不等於 label 欄位值的資料
checkList[checkList.prediction != checkList.label]
[9 2 1 ... 8 1 5]
[4 0 3 1 6 1 9 4 9 9 1 7 8 3 0 0 2 4 8 0]

   label  prediction
0      9           9
1      2           2
2      1           1
3      1           1
4      6           6
5      1           1
6      4           4
7      6           6
8      5           5
9      7           7

Out[58]:

label prediction
12 7 8
17 4 6
23 9 5
25 4 2
40 6 0
... ... ...
9964 9 5
9969 8 6
9977 6 0
9985 2 3
9991 6 2
809 rows × 2 columns
In [59]:
# demonstration of calculating metrics for a neural network model using sklearn(Scikit-learn)
# Model evaluation: quantifying the quality of predictions, 參攷[1]
# from sklearn.datasets import make_circles
from sklearn.metrics import accuracy_score     # 參攷[2]
from sklearn.metrics import precision_score    # 參攷[3]
from sklearn.metrics import recall_score       # 參攷[4]
from sklearn.metrics import f1_score           # 參攷[5]
from sklearn.metrics import cohen_kappa_score  # 參攷[6]
from sklearn.metrics import roc_auc_score      # 參攷[7]
from sklearn.metrics import confusion_matrix   # 參攷[8]

# predict probabilities for test set
y_probs = model.predict(x=test_cnn_images, verbose=0, use_multiprocessing=True)  # 參攷[8]
# predict crisp classes for test set
y_classes = model.predict_classes(x=test_cnn_images, verbose=0)  # 參攷[9]
# reduce to 1d array
# y_probs = y_probs[:, 0]
# y_classes = y_classes[:, 0]

# accuracy: (tp + tn) / (p + n)
accuracy = accuracy_score(test_labels, y_classes)
print('Accuracy: %f' % accuracy)
# precision tp / (tp + fp)
precision = precision_score(test_labels, y_classes, average='weighted')
print('Precision: %f' % precision)
# recall: tp / (tp + fn)
recall = recall_score(test_labels, y_classes, average='weighted')
print('Recall: %f' % recall)
print()

# f1: 2 tp / (2 tp + fp + fn)
f1 = f1_score(test_labels, y_classes, average='weighted')
print('F1 score: %f' % f1)
print()

# kappa
kappa = cohen_kappa_score(test_labels, y_classes)
print('Cohens kappa: %f' % kappa)
# ROC AUC
# auc = roc_auc_score(test_labels, y_probs)
# print('ROC AUC: %f' % auc)
# confusion matrix
print()

matrix = confusion_matrix(test_labels, y_classes)
print(matrix)
Accuracy: 0.919100
Precision: 0.919151
Recall: 0.919100

F1 score: 0.918276

Cohens kappa: 0.910111

[[900   0  24  12   0   0  60   0   4   0]
 [  2 974   0  21   1   0   1   0   1   0]
 [ 12   1 907  10  31   0  39   0   0   0]
 [  7   1   6 962   7   0  17   0   0   0]
 [  0   1  77  40 838   0  44   0   0   0]
 [  0   0   0   0   0 984   0  14   1   1]
 [138   0  59  27  64   0 708   0   4   0]
 [  0   0   0   0   0   4   0 985   2   9]
 [  2   0   1   4   2   1   3   1 986   0]
 [  0   0   1   0   0   8   0  44   0 947]]

The end.
In [ ]:
 

留言