本文將從兩個(gè)案例 MNIST手寫數(shù)字識(shí)別、狗的品種識(shí)別 入手,讓童鞋們從實(shí)戰(zhàn)角度快速入門深度學(xué)習(xí)的分類部分!
MNIST手寫數(shù)字識(shí)別
TensorFlow搭建MLP
import numpy as np
import tensorflow as tf
import matplotlib.pyplot as plt
# 下載數(shù)據(jù)集
from tensorflow.examples.tutorials.mnist import input_data
mnist = input_data.read_data_sets("MNIST_data/", one_hot=True)
print("訓(xùn)練集圖像大小:{}".format(mnist.train.images.shape))
print("訓(xùn)練集標(biāo)簽大?。簕}".format(mnist.train.labels.shape))
print("驗(yàn)證集圖像大小:{}".format(mnist.validation.images.shape))
print("驗(yàn)證集標(biāo)簽大?。簕}".format(mnist.validation.labels.shape))
print("測(cè)試集圖像大?。簕}".format(mnist.test.images.shape))
print("測(cè)試集標(biāo)簽大小:{}".format(mnist.test.labels.shape))
# 為了便于讀取,我們把數(shù)據(jù)集先各自使用一個(gè)變量指向它們
x_train, y_train = mnist.train.images, mnist.train.labels
x_valid, y_valid = mnist.validation.images, mnist.validation.labels
x_test, y_test = mnist.test.images, mnist.test.labels
# 繪制和顯示前5個(gè)訓(xùn)練集的圖像
fig = plt.figure(figsize=(10, 10))
for i in range(5):
ax = fig.add_subplot(1, 5, i+1, xticks=[], yticks=[])
ax.imshow(np.reshape(x_train[i:i+1], (28, 28)), cmap='gray')
# 繪制和顯示前(2*12)之后的五個(gè)訓(xùn)練集的圖像
fig = plt.figure(figsize=(10, 10))
for i in range(5):
ax = fig.add_subplot(1, 5, i+1, xticks=[], yticks=[])
ax.imshow(np.reshape(x_train[i+2*12:i+1+2*12], (28, 28)), cmap='gray')
# 定義可視化圖像的函數(shù),傳入一個(gè)圖像向量和figure對(duì)象
def visualize_input(img, ax):
# 繪制并輸出圖像
ax.imshow(img, cmap='gray')
# 對(duì)于該圖像的寬和高,我們輸出它們的具體的數(shù)值,
# 以便于我們更清晰的知道計(jì)算機(jī)是如何看待一張圖像的
width, height = img.shape
# 將圖像中的具體數(shù)值轉(zhuǎn)換成0-1之間的值
thresh = img.max()/2.5
# 遍歷行
for x in range(width):
# 遍歷列
for y in range(height):
# 將圖像的數(shù)值在它對(duì)應(yīng)的位置上標(biāo)出,且水平垂直居中
ax.annotate(str(round(img[x][y],2)), xy=(y,x),
horizontalalignment='center',
verticalalignment='center',
color='white' if img[x][y]<thresh else 'black')
fig = plt.figure(figsize=(10, 10))
ax = fig.add_subplot(111)
# 假設(shè)我們就取出下標(biāo)為5的樣本來(lái)作為例子
visualize_input(np.reshape(x_train[5:6], (28, 28)), ax)
import math
#模型搭建和訓(xùn)練
# 參數(shù)準(zhǔn)備
img_size = 28 * 28
num_classes = 10
learning_rate = 0.1
epochs = 100
batch_size = 128
# 創(chuàng)建模型
# x表示輸入,創(chuàng)建輸入占位符,該占位符會(huì)在訓(xùn)練時(shí),會(huì)對(duì)每次迭代的數(shù)據(jù)進(jìn)行填充上
x = tf.placeholder(tf.float32, [None, img_size])
# W表示weight,創(chuàng)建權(quán)重,初始化時(shí)都是為0,它的大小是(圖像的向量大小,圖像的總類別)
W = tf.Variable(tf.zeros([img_size, num_classes]))
# b表示bias,創(chuàng)建偏移項(xiàng)
b = tf.Variable(tf.zeros([num_classes]))
# y表示計(jì)算輸出結(jié)果,softmax表示激活函數(shù)是多類別分類的輸出
# 感知器的計(jì)算公式就是:(x * W) + b
y = tf.nn.softmax(tf.matmul(x, W) + b)
# 定義輸出預(yù)測(cè)占位符y_
y_ = tf.placeholder(tf.float32, [None, 10])
valid_feed_dict = { x: x_valid, y_: y_valid }
test_feed_dict = { x: x_test, y_: y_test }
# 通過(guò)激活函數(shù)softmax的交叉熵來(lái)定義損失函數(shù)
cost = tf.reduce_mean(tf.nn.softmax_cross_entropy_with_logits(labels=y_, logits=y))
# 定義梯度下降優(yōu)化器
optimizer = tf.train.GradientDescentOptimizer(learning_rate).minimize(cost)
# 比較正確的預(yù)測(cè)結(jié)果
correct_prediction = tf.equal(tf.argmax(y, 1), tf.argmax(y_, 1))
# 計(jì)算預(yù)測(cè)準(zhǔn)確率
accuracy = tf.reduce_mean(tf.cast(correct_prediction, tf.float32))
iteration = 0
# 定義訓(xùn)練時(shí)的檢查點(diǎn)
saver = tf.train.Saver()
# 創(chuàng)建一個(gè)TensorFlow的會(huì)話
with tf.Session() as sess:
# 初始化全局變量
sess.run(tf.global_variables_initializer())
# 根據(jù)每批次訓(xùn)練128個(gè)樣本,計(jì)算出一共需要迭代多少次
batch_count = int(math.ceil(mnist.train.labels.shape[0] / 128.0))
# 開(kāi)始迭代訓(xùn)練樣本
for e in range(epochs):
# 每個(gè)樣本都需要在TensorFlow的會(huì)話里進(jìn)行運(yùn)算,訓(xùn)練
for batch_i in range(batch_count):
# 樣本的索引,間隔是128個(gè)
batch_start = batch_i * batch_size
# 取出圖像樣本
batch_x = mnist.train.images[batch_start:batch_start+batch_size]
# 取出圖像對(duì)應(yīng)的標(biāo)簽
batch_y = mnist.train.labels[batch_start:batch_start+batch_size]
# 訓(xùn)練模型
loss, _ = sess.run([cost, optimizer], feed_dict={x: batch_x, y_: batch_y})
# 每20個(gè)批次時(shí)輸出一次訓(xùn)練損失等日志信息
if batch_i % 20 == 0:
print("Epoch: {}/{}".format(e+1, epochs),
"Iteration: {}".format(iteration),
"Training loss: {:.5f}".format(loss))
iteration += 1
# 每128個(gè)樣本時(shí),驗(yàn)證一下訓(xùn)練的效果如何,并輸出日志信息
if iteration % batch_size == 0:
valid_acc = sess.run(accuracy, feed_dict=valid_feed_dict)
print("Epoch: {}/{}".format(e, epochs),
"Iteration: {}".format(iteration),
"Validation Accuracy: {:.5f}".format(valid_acc))
# 保存訓(xùn)練模型的檢查點(diǎn)
saver.save(sess, "checkpoints/mnist_mlp_tf.ckpt")
# 預(yù)測(cè)測(cè)試數(shù)據(jù)集精確度
saver = tf.train.Saver()
with tf.Session() as sess:
# 從訓(xùn)練模型的檢查點(diǎn)恢復(fù)
saver.restore(sess, tf.train.latest_checkpoint('checkpoints'))
# 預(yù)測(cè)測(cè)試集精確度
test_acc = sess.run(accuracy, feed_dict=test_feed_dict)
print("test accuracy: {:.5f}".format(test_acc))
TensorFlow搭建CNN
import tensorflow as tf
from tensorflow.examples.tutorials.mnist import input_data
# 下載并加載數(shù)據(jù)集
mnist = input_data.read_data_sets('MNIST_data/', one_hot=True)
# 為了便于讀取,我們把數(shù)據(jù)集先各自使用一個(gè)變量指向它們
x_train, y_train = mnist.train.images, mnist.train.labels
x_valid, y_valid = mnist.validation.images, mnist.validation.labels
x_test, y_test = mnist.test.images, mnist.test.labels
print("訓(xùn)練集圖像大?。簕}".format(x_train.shape))
print("訓(xùn)練集標(biāo)簽大?。簕}".format(y_train.shape))
print("驗(yàn)證集圖像大小:{}".format(x_valid.shape))
print("驗(yàn)證集標(biāo)簽大?。簕}".format(y_valid.shape))
print("測(cè)試集圖像大?。簕}".format(x_test.shape))
print("測(cè)試集標(biāo)簽大?。簕}".format(y_test.shape))
# 參數(shù)準(zhǔn)備
img_size = 28 * 28
num_classes = 10
learning_rate = 1e-4
epochs = 10
batch_size = 50
# 定義輸入占位符
x = tf.placeholder(tf.float32, shape=[None, img_size])
x_shaped = tf.reshape(x, [-1, 28, 28, 1])
# 定義輸出占位符
y = tf.placeholder(tf.float32, shape=[None, num_classes])
# 定義卷積函數(shù)
def create_conv2d(input_data, num_input_channels, num_filters, filter_shape, pool_shape, name):
# 卷積的過(guò)濾器大小結(jié)構(gòu)是[filter_height, filter_width, in_channels, out_channels]
conv_filter_shape = [filter_shape[0], filter_shape[1], num_input_channels, num_filters]
# 定義權(quán)重Tensor變量,初始化時(shí)是截?cái)嗾龖B(tài)分布,標(biāo)準(zhǔn)差是0.03
weights = tf.Variable(tf.truncated_normal(conv_filter_shape, stddev=0.03), name=name+"_W")
# 定義偏移項(xiàng)Tensor變量,初始化時(shí)是截?cái)嗾龖B(tài)分布
bias = tf.Variable(tf.truncated_normal([num_filters]), name=name+"_b")
# 定義卷積層
out_layer = tf.nn.conv2d(input_data, weights, (1, 1, 1, 1), padding="SAME")
out_layer += bias
# 通過(guò)激活函數(shù)ReLU來(lái)計(jì)算輸出
out_layer = tf.nn.relu(out_layer)
# 添加最大池化層
out_layer = tf.nn.max_pool(out_layer, ksize=(1, pool_shape[0], pool_shape[1], 1), strides=(1, 2, 2, 1), padding="SAME")
return out_layer
# 添加第一層卷積層
layer1 = create_conv2d(x_shaped, 1, 32, (5, 5), (2, 2), name="layer1")
# 添加第二層卷積層
layer2 = create_conv2d(layer1, 32, 64, (5, 5), (2, 2), name="layer2")
# 添加扁平化層
flattened = tf.reshape(layer2, (-1, 7 * 7 * 64))
# 添加全連接層
wd1 = tf.Variable(tf.truncated_normal((7 * 7 * 64, 1000), stddev=0.03), name="wd1")
bd1 = tf.Variable(tf.truncated_normal([1000], stddev=0.01), name="bd1")
dense_layer1 = tf.add(tf.matmul(flattened, wd1), bd1)
dense_layer1 = tf.nn.relu(dense_layer1)
# 添加輸出全連接層
wd2 = tf.Variable(tf.truncated_normal((1000, num_classes), stddev=0.03), name="wd2")
bd2 = tf.Variable(tf.truncated_normal([num_classes], stddev=0.01), name="bd2")
dense_layer2 = tf.add(tf.matmul(dense_layer1, wd2), bd2)
# 添加激活函數(shù)的softmax輸出層
y_ = tf.nn.softmax(dense_layer2)
# 通過(guò)softmax交叉熵定義計(jì)算損失值
cost = tf.reduce_mean(tf.nn.softmax_cross_entropy_with_logits(logits=y_, labels=y))
# 定義優(yōu)化器是Adam
optimizer = tf.train.AdamOptimizer(learning_rate=learning_rate).minimize(cost)
# 定義預(yù)測(cè)結(jié)果的比較
correct_prediction = tf.equal(tf.argmax(y, 1), tf.argmax(y_, 1))
# 定義預(yù)測(cè)的精確度
accuracy = tf.reduce_mean(tf.cast(correct_prediction, tf.float32))
iteration = 0
import math
# 定義要保存訓(xùn)練模型的變量
saver = tf.train.Saver()
# 創(chuàng)建TensorFlow會(huì)話
with tf.Session() as sess:
# 初始化TensorFlow的全局變量
sess.run(tf.global_variables_initializer())
# 計(jì)算所有的訓(xùn)練集需要被訓(xùn)練多少次,當(dāng)每批次是batch_size個(gè)時(shí)
batch_count = int(math.ceil(x_train.shape[0] / float(batch_size)))
# 要迭代epochs次訓(xùn)練
for e in range(epochs):
# 對(duì)每張圖像進(jìn)行訓(xùn)練
for batch_i in range(batch_count):
# 每次取出batch_size張圖像
batch_x, batch_y = mnist.train.next_batch(batch_size=batch_size)
# 訓(xùn)練模型
_, loss = sess.run([optimizer, cost], feed_dict={x: batch_x, y: batch_y})
# 每訓(xùn)練20次圖像時(shí)打印一次日志信息,也就是20次乘以batch_size個(gè)圖像已經(jīng)被訓(xùn)練了
if batch_i % 20 == 0:
print("Epoch: {}/{}".format(e+1, epochs),
"Iteration: {}".format(iteration),
"Training loss: {:.5f}".format(loss))
iteration += 1
# 每迭代一次時(shí),做一次驗(yàn)證,并打印日志信息
if iteration % batch_size == 0:
valid_acc = sess.run(accuracy, feed_dict={x: x_valid, y: y_valid})
print("Epoch: {}/{}".format(e, epochs),
"Iteration: {}".format(iteration),
"Validation Accuracy: {:.5f}".format(valid_acc))
# 保存模型的檢查點(diǎn)
saver.save(sess, "checkpoints/mnist_cnn_tf.ckpt")
# 預(yù)測(cè)測(cè)試數(shù)據(jù)集
saver = tf.train.Saver()
with tf.Session() as sess:
# 從TensorFlow會(huì)話中恢復(fù)之前保存的模型檢查點(diǎn)
saver.restore(sess, tf.train.latest_checkpoint('checkpoints/'))
# 通過(guò)測(cè)試集預(yù)測(cè)精確度
test_acc = sess.run(accuracy, feed_dict={x: x_test, y: y_test})
print("test accuracy: {:.5f}".format(test_acc))
Keras搭建MLP
import keras
from keras.datasets import mnist
from keras.models import Sequential
from keras.layers import Dense, Dropout, Activation
from keras.optimizers import RMSprop
# 參數(shù)準(zhǔn)備
batch_size = 128
num_classes = 10
epochs = 20
img_size = 28 * 28
# 下載并讀取MNIST數(shù)據(jù)集數(shù)據(jù)
(x_train, y_train), (x_test, y_test) = mnist.load_data()
# 分割驗(yàn)證集數(shù)據(jù)
valid_len = 5000
x_len = x_train.shape[0]
train_len = x_len-valid_len
# 驗(yàn)證集數(shù)據(jù)
x_valid = x_train[train_len:]
y_valid = y_train[train_len:]
# 訓(xùn)練集數(shù)據(jù)
x_train = x_train[:train_len]
y_train = y_train[:train_len]
# 將訓(xùn)練集、驗(yàn)證集和測(cè)試集數(shù)據(jù)進(jìn)行圖像向量轉(zhuǎn)換
x_train = x_train.reshape(x_train.shape[0], img_size)
x_valid = x_valid.reshape(x_valid.shape[0], img_size)
x_test = x_test.reshape(x_test.shape[0], img_size)
# 將訓(xùn)練集、驗(yàn)證集和測(cè)試集數(shù)據(jù)都轉(zhuǎn)換成float32類型
x_train = x_train.astype('float32')
x_valid = x_valid.astype('float32')
x_test = x_test.astype('float32')
# 將訓(xùn)練集、驗(yàn)證集和測(cè)試集數(shù)據(jù)都轉(zhuǎn)換成0到1之間的數(shù)值,就是歸一化處理
x_train /= 255
x_valid /= 255
x_test /= 255
# 通過(guò)to_categorical()函數(shù)將訓(xùn)練集標(biāo)簽、驗(yàn)證集標(biāo)簽和測(cè)試集標(biāo)簽獨(dú)熱編碼(one-hot encoding)
y_train = keras.utils.to_categorical(y_train, num_classes)
y_valid = keras.utils.to_categorical(y_valid, num_classes)
y_test = keras.utils.to_categorical(y_test, num_classes)
# 創(chuàng)建模型
model = Sequential()
model.add(Dense(512, activation='relu', input_shape=(img_size,)))
model.add(Dropout(0.2))
model.add(Dense(512, activation='relu'))
model.add(Dropout(0.2))
model.add(Dense(num_classes, activation='softmax'))
# 模型架構(gòu)預(yù)覽
model.summary()
# 編譯模型
model.compile(loss='categorical_crossentropy', optimizer=RMSprop(), metrics=['accuracy'])
# 訓(xùn)練模型
model.fit(x_train, y_train, epochs=epochs, batch_size=batch_size, verbose=1, validation_data=(x_valid, y_valid))
# 評(píng)估模型
score = model.evaluate(x_test, y_test, verbose=0)
print('Test accuracy:{}, Test loss: {}, {}'.format(score[1], score[0], score))
#模型預(yù)測(cè),畫圖
import matplotlib.pyplot as plt
import numpy as np
x_img = x_test[7:8]
# 預(yù)測(cè)單張圖像的概率
prediction = model.predict(x_img)
x_coordinates = np.arange(prediction.shape[1])
plt.bar(x_coordinates, prediction[0][:])
plt.xticks(x_coordinates, np.arange(10))
plt.show()
Keras搭建CNN
import numpy as np
import keras
from keras.datasets import mnist
from keras.models import Sequential
from keras.layers import Dense, Dropout, Flatten, Conv2D, MaxPooling2D
from keras import utils
# 參數(shù)準(zhǔn)備
batch_size = 128
epochs = 15
num_classes = 10
img_width = 28
img_height = 28
img_channels = 1
# 下載并讀取MNIST數(shù)據(jù)集數(shù)據(jù)
(x_train, y_train), (x_test, y_test) = mnist.load_data()
# 分割驗(yàn)證集數(shù)據(jù)
valid_len = 5000
x_len = x_train.shape[0]
train_len = x_len-valid_len
# 驗(yàn)證集數(shù)據(jù)
x_valid = x_train[train_len:]
y_valid = y_train[train_len:]
# 訓(xùn)練集數(shù)據(jù)
x_train = x_train[:train_len]
y_train = y_train[:train_len]
# 將訓(xùn)練集、驗(yàn)證集和測(cè)試集數(shù)據(jù)進(jìn)行圖像轉(zhuǎn)換,
# 圖像的形狀大小是 [batch, height, width, channels]
x_train = x_train.reshape(x_train.shape[0], img_height, img_width, img_channels)
x_valid = x_valid.reshape(x_valid.shape[0], img_height, img_width, img_channels)
x_test = x_test.reshape(x_test.shape[0], img_height, img_width, img_channels)
# 將訓(xùn)練集、驗(yàn)證集和測(cè)試集數(shù)據(jù)都轉(zhuǎn)換成float32類型
x_train = x_train.astype(np.float32)
x_valid = x_valid.astype(np.float32)
x_test = x_test.astype(np.float32)
# 將訓(xùn)練集、驗(yàn)證集和測(cè)試集數(shù)據(jù)都轉(zhuǎn)換成0到1之間的數(shù)值,就是歸一化處理
x_train /= 255
x_valid /= 255
x_test /= 255
# 通過(guò)to_categorical()函數(shù)將訓(xùn)練集標(biāo)簽、驗(yàn)證集標(biāo)簽和測(cè)試集標(biāo)簽獨(dú)熱編碼(one-hot encoding)
y_train = keras.utils.to_categorical(y_train, num_classes)
y_valid = keras.utils.to_categorical(y_valid, num_classes)
y_test = keras.utils.to_categorical(y_test, num_classes)
# 創(chuàng)建模型
model = Sequential()
model.add(Conv2D(filters=32, kernel_size=(3, 3), activation='relu', input_shape=(img_width, img_height, img_channels)))
model.add(Conv2D(filters=64, kernel_size=(3, 3), activation='relu'))
model.add(MaxPooling2D(pool_size=(2, 2)))
model.add(Dropout(0.25))
model.add(Flatten())
model.add(Dense(128, activation='relu'))
model.add(Dropout(0.5))
model.add(Dense(num_classes, activation='softmax'))
# 模型架構(gòu)預(yù)覽
model.summary()
# 編譯模型
model.compile(loss=keras.losses.categorical_crossentropy,
optimizer=keras.optimizers.Adadelta(), metrics=['accuracy'])
# 訓(xùn)練模型
model.fit(x_train, y_train, batch_size=batch_size, epochs=epochs,
verbose=1, validation_data=(x_valid, y_valid))
# 評(píng)估模型
score = model.evaluate(x_test, y_test, verbose=0)
print("Test Loss: {:.5f}, Test Accuracy: {:.5f}".format(score[0], score[1]))
# 單張圖像預(yù)測(cè)
import matplotlib.pyplot as plt
# 取出第一張圖像
x_img = x_test[0:1]
# 通過(guò)模型預(yù)測(cè)
prediction = model.predict(x_img)
# 繪制圖展示
x_coordinate = np.arange(prediction.shape[1])
plt.bar(x_coordinate, prediction[0][:])
plt.xticks(x_coordinate, np.arange(10))
plt.show()
print("預(yù)測(cè)的圖中的數(shù)字是{}。".format(y_test[0:1]))
狗的品種識(shí)別
狗狗圖片數(shù)據(jù):
鏈接:https://pan.baidu.com/s/1cEgg2aqXvAvI58M8EAS9CQ 密碼:8ahu
Keras搭建CNN
from sklearn.datasets import load_files
from sklearn.model_selection import train_test_split
from keras.utils import np_utils
from keras.preprocessing import image
import numpy as np
from glob import glob
import matplotlib.pyplot as plt
from matplotlib import image
import tqdm
# 共有120種狗狗的品種
num_classes = 120
# 定義加載數(shù)據(jù)集的函數(shù)
def load_dataset(path):
# 通過(guò)sklearn提供的load_files()方法加載文件
# 返回一個(gè)類字典對(duì)象,包含文件相對(duì)路徑和文件所屬編號(hào)
data = load_files(path)
# 將文件路徑轉(zhuǎn)變成NumPy對(duì)象
dog_files = np.array(data['filenames'])
# 狗狗的每張圖片都按照順序排成列表
raw_targets = np.array(data['target'])
# 通過(guò)to_categorical()方法將文件所屬編號(hào)轉(zhuǎn)換成二進(jìn)制類別矩陣(就是one-hot encoding)
dog_targets = np_utils.to_categorical(raw_targets, num_classes)
# 返回所有圖片文件路徑,圖片文件編號(hào)和圖片文件的二進(jìn)制類別矩陣
return dog_files, raw_targets, dog_targets
# 加載數(shù)據(jù)集
dog_filepaths, dog_raw_targets, dog_targets = load_dataset('Images/')
# 加載狗狗的品種名稱列表
# glob是一個(gè)文件操作相關(guān)的模塊,通過(guò)指定的匹配模式,返回相應(yīng)的文件或文件夾路徑
# 這里的操作就是返回Images目錄下的所有文件夾
# 最后通過(guò)列表推導(dǎo)式遍歷每個(gè)文件路徑字符串,并截取狗狗類別名稱那段字符串
dogpath_prefix_len = len('Images/n02085620-')
dog_names = [item[dogpath_prefix_len:] for item in sorted(glob("Images/*"))]
print('狗狗的品種有{}種。'.format(len(dog_names)))
print('狗狗的圖片一共有{}張。n'.format(len(dog_filepaths)))
# 為了訓(xùn)練更快些,也考慮到一些讀者的本地機(jī)器性能不高,我們就用前9000張狗狗的圖片吧
# 如果讀者的機(jī)器性能還不錯(cuò),那就注釋這兩行,直接訓(xùn)練所有的圖片數(shù)據(jù)
dog_filepaths = dog_filepaths[:9000]
dog_targets = dog_targets[:9000]
# 分割訓(xùn)練數(shù)據(jù)集和測(cè)試數(shù)據(jù)集
X_train, X_test, y_train, y_test = train_test_split(dog_filepaths, dog_targets, test_size=0.2)
# 將測(cè)試集數(shù)據(jù)分割一半給驗(yàn)證集
half_test_count = int(len(X_test) / 2)
X_valid = X_test[:half_test_count]
y_valid = y_test[:half_test_count]
X_test = X_test[half_test_count:]
y_test = y_test[half_test_count:]
print("X_train.shape={}, y_train.shape={}.".format(X_train.shape, y_train.shape))
print("X_valid.shape={}, y_valid.shape={}.".format(X_valid.shape, y_valid.shape))
print("X_test.shape={}, y_test.shape={}.".format(X_test.shape, y_test.shape))
# 設(shè)置matplotlib在繪圖時(shí)的默認(rèn)樣式
plt.style.use('default')
# 查看隨機(jī)9張狗狗的圖像
def draw_random_9_dog_images():
# 創(chuàng)建9個(gè)繪圖對(duì)象,3行3列
fig, axes = plt.subplots(nrows=3, ncols=3)
# 設(shè)置繪圖的總?cè)萜鞔笮?/span>
fig.set_size_inches(10, 9)
# 隨機(jī)選擇9個(gè)數(shù),也就是9個(gè)品種的狗(可能重復(fù),且每次都不一樣)
random_9_nums = np.random.choice(len(X_train), 9)
# 從訓(xùn)練集中選出9張圖
random_9_imgs = X_train[random_9_nums]
print(random_9_imgs)
# 根據(jù)這隨機(jī)的9張圖片路徑,截取取得相應(yīng)的狗狗品種名稱
imgname_list = []
for imgpath in random_9_imgs:
imgname = imgpath[dogpath_prefix_len:]
imgname = imgname[:imgname.find('/')]
imgname_list.append(imgname)
index = 0
for row_index in range(3): # 行
for col_index in range(3): # 列
# 讀取圖片的數(shù)值內(nèi)容
img = image.imread(random_9_imgs[index])
# 獲取繪圖Axes對(duì)象,根據(jù)[行索引, 列索引]
ax = axes[row_index, col_index]
# 在Axes對(duì)象上顯示圖像
ax.imshow(img)
# 在繪圖對(duì)象上設(shè)置狗狗品種名稱
ax.set_xlabel(imgname_list[index])
# 索引加1
index += 1
draw_random_9_dog_images()
# 對(duì)數(shù)據(jù)集進(jìn)行遍歷,讀取每張圖片,并獲取它的大小,
# 最后返回的圖片shape存儲(chǔ)在變量dogs_shape_list列表里
dogs_shape_list = []
for filepath in dog_filepaths:
shape = image.imread(filepath).shape
if len(shape) == 3:
dogs_shape_list.append(shape)
dogs_shapes = np.asarray(dogs_shape_list)
print("總共{}張。".format(len(dogs_shapes)))
print("隨機(jī)抽取三張圖片的維度是{}。".format(dogs_shapes[np.random.choice(len(dogs_shapes), 3)]))
dogs_mean_width = np.mean(dogs_shapes[:,0])
dogs_mean_height = np.mean(dogs_shapes[:,1])
print("狗狗的圖片的平均寬:{:.1f} * 平均高:{:.1f}。".format(dogs_mean_width, dogs_mean_height))
# 定義一個(gè)函數(shù),將每張圖片都轉(zhuǎn)換成標(biāo)準(zhǔn)大小(1, 224, 224, 3)
def path_to_tensor(img_path):
# 加載圖片
# 圖片對(duì)象的加載用的是PIL庫(kù),通過(guò)load_img()方法返回的就是一個(gè)PIL對(duì)象
img = image.load_img(img_path, target_size=(224, 224, 3))
# 將PIL圖片對(duì)象類型轉(zhuǎn)化為格式(224, 224, 3)的3維張量
x = image.img_to_array(img)
# 將3維張量轉(zhuǎn)化格式為(1, 224, 224, 3)的4維張量并返回
return np.expand_dims(x, axis=0)
# 定義一個(gè)函數(shù),將數(shù)組里的所有路徑的圖片都轉(zhuǎn)換成圖像數(shù)值類型并返回
def paths_to_tensor(img_paths):
# tqdm模塊表示使用進(jìn)度條顯示,傳入一個(gè)所有圖片的數(shù)組對(duì)象
# 將所有圖片的對(duì)象一個(gè)個(gè)都轉(zhuǎn)換成numpy數(shù)值對(duì)象張量后,并返回成數(shù)組
list_of_tensors = [path_to_tensor(img_path) for img_path in tqdm(img_paths)]
# 將對(duì)象垂直堆砌排序擺放
return np.vstack(list_of_tensors)
from PIL import ImageFile
# 為了防止PIL讀取圖片對(duì)象時(shí)出現(xiàn)IO錯(cuò)誤,則設(shè)置截?cái)鄨D片為True
ImageFile.LOAD_TRUNCATED_IMAGES = True
# 將所有圖片都轉(zhuǎn)換成標(biāo)準(zhǔn)大小的數(shù)值圖像對(duì)象,然后除以255,進(jìn)行歸一化處理
# RGB的顏色值,最大為255,最小為0
# 對(duì)訓(xùn)練集數(shù)據(jù)進(jìn)行處理
train_tensors = paths_to_tensor(X_train).astype(np.float32) / 255
# 對(duì)驗(yàn)證集數(shù)據(jù)進(jìn)行處理
valid_tensors = paths_to_tensor(X_valid).astype(np.float32) / 255
# 對(duì)測(cè)試集數(shù)據(jù)進(jìn)行處理
test_tensors = paths_to_tensor(X_test).astype(np.float32) / 255
from keras.layers import Conv2D, MaxPooling2D, GlobalAveragePooling2D
from keras.layers import Dropout, Flatten, Dense
from keras.models import Sequential
# 創(chuàng)建Sequential模型
model = Sequential()
# 創(chuàng)建輸入層,輸入層必須傳入input_shape參數(shù)以表示圖像大小,深度是16
model.add(Conv2D(filters=16, kernel_size=(2, 2), strides=(1, 1), padding='same',
activation='relu', input_shape=train_tensors.shape[1:]))
# 添加最大池化層,大小為2x2,有效范圍默認(rèn)是valid,就是說(shuō),不夠2x2的大小的空間數(shù)據(jù)就丟棄了
model.add(MaxPooling2D(pool_size=(2, 2)))
# 添加Dropout層,每次丟棄20%的網(wǎng)絡(luò)節(jié)點(diǎn),防止過(guò)擬合
model.add(Dropout(0.2))
# 添加卷積層,深度是32,內(nèi)核大小是2x2,跨步是1x1,有效范圍是same則表示不夠數(shù)據(jù)范圍的就用0填充
model.add(Conv2D(filters=32, kernel_size=(2, 2), strides=(1, 1), padding='same', activation='relu'))
# 添加最大池化層,大小為2x2,有效范圍默認(rèn)是valid,就是說(shuō),不夠2x2的大小的空間數(shù)據(jù)就丟棄了
model.add(MaxPooling2D(pool_size=(2, 2)))
# 添加Dropout層,每次丟棄20%的網(wǎng)絡(luò)節(jié)點(diǎn),防止過(guò)擬合
model.add(Dropout(0.2))
# 添加卷積層,深度是64
model.add(Conv2D(filters=64, kernel_size=(2, 2), strides=(1, 1), padding='same', activation='relu'))
model.add(MaxPooling2D(pool_size=(2, 2)))
model.add(Dropout(0.2))
# 添加全局平均池化層
model.add(GlobalAveragePooling2D())
# 添加Dropout,每次丟棄50%
model.add(Dropout(0.5))
# 添加輸出層,120個(gè)類別輸出
model.add(Dense(num_classes, activation="softmax"))
# 打印輸出網(wǎng)絡(luò)模型架構(gòu)
model.summary()
# 編譯模型
model.compile(optimizer='rmsprop', loss='categorical_crossentropy', metrics=['accuracy'])
from keras.callbacks import ModelCheckpoint
epochs = 20
checkpointer = ModelCheckpoint(filepath='saved_models/weights.best.from_scratch.hdf5',
verbose=1,
save_best_only=True)
model.fit(train_tensors,
y_train,
validation_data=(valid_tensors, y_valid),
epochs=epochs,
batch_size=20,
callbacks=[checkpointer],
verbose=1)
## 加載具有最好驗(yàn)證權(quán)重的模型
model.load_weights('saved_models/weights.best.from_scratch.hdf5')
# 獲取測(cè)試數(shù)據(jù)集中每一個(gè)圖像所預(yù)測(cè)的狗品種的index
dog_breed_predictions = [np.argmax(model.predict(np.expand_dims(tensor, axis=0))) for tensor in test_tensors]
# 測(cè)試準(zhǔn)確率
test_accuracy = 100*np.sum(np.array(dog_breed_predictions)==np.argmax(y_test, axis=1))/len(dog_breed_predictions)
print('Test Accuracy: {:.4f}'.format(test_accuracy))
#結(jié)果發(fā)現(xiàn)準(zhǔn)確率很低很低,這是我們需要遷移學(xué)習(xí)
遷移學(xué)習(xí)(InceptionV3)
# 導(dǎo)入InceptionV3預(yù)訓(xùn)練模型和數(shù)據(jù)處理模塊
from keras.applications.inception_v3 import InceptionV3, preprocess_input, decode_predictions
# 導(dǎo)入構(gòu)建Keras的Model所需模塊
from keras.models import Model
from keras.layers import Dense, GlobalAveragePooling2D, Dropout
from keras.preprocessing import image
from keras.optimizers import SGD
from keras.callbacks import ModelCheckpoint
# 導(dǎo)入圖片數(shù)據(jù)增強(qiáng)生成器
from keras.preprocessing.image import ImageDataGenerator
import matplotlib.pyplot as plt
class InceptionV3Retrained:
"""
定義一個(gè)類,用來(lái)在預(yù)訓(xùn)練模型上去訓(xùn)練新的數(shù)據(jù)
"""
def add_new_last_layers(self, base_model, num_classes):
"""
添加新的全連接層
"""
# 添加一個(gè)全局空間平均池化層
x = base_model.output
x = GlobalAveragePooling2D()(x)
# 添加1024個(gè)全連接層
x = Dense(1024, activation='relu')(x)
# 添加全連接輸出層,有num_classes個(gè)類別輸出,使用softmax多類別分類激活函數(shù)
predictions = Dense(num_classes, activation='softmax')(x)
# 通過(guò)上面定義的base_model對(duì)象和它的輸出層
# 我們自定義創(chuàng)建一個(gè)新的Keras的Model模型對(duì)象
model = Model(input=base_model.input, output=predictions)
return model
def freeze_previous_layers(self, model, base_model):
"""
凍結(jié)預(yù)訓(xùn)練模型之前的層
"""
# 凍結(jié)InceptionV3模型的所有卷積層,因?yàn)槲覀冞w移學(xué)習(xí)就是對(duì)頂部的幾個(gè)層進(jìn)行訓(xùn)練
for layer in base_model.layers:
layer.trainable = False
# 編譯模型
# 優(yōu)化器rmsprop,參數(shù)使用默認(rèn)值即可
# 分類交叉熵使用多類別的
model.compile(optimizer='rmsprop', loss='categorical_crossentropy', metrics=['accuracy'])
def fine_tune_model(self, model):
"""
微調(diào)模型
"""
# 我們凍結(jié)模型的前面172層,然后把剩下的層數(shù)都解凍
for layer in model.layers[:172]:
layer.trainable = False
for layer in model.layers[172:]:
layer.trainable = True
# 再編譯模型
# 優(yōu)化器使用隨機(jī)梯度下降,學(xué)習(xí)率我們調(diào)小點(diǎn)0.0001
# 分類交叉熵依舊使用多類別的
model.compile(optimizer=SGD(lr=0.0001, momentum=0.9), loss='categorical_crossentropy', metrics=['accuracy'])
def plot_training(self, history):
"""
繪制訓(xùn)練模型時(shí)的損失值和精確度
"""
# 取出訓(xùn)練時(shí)的精確度
acc = history.history['acc']
# 取出驗(yàn)證時(shí)的精確度
val_acc = history.history['val_acc']
# 取出訓(xùn)練時(shí)的損失值
loss = history.history['loss']
# 取出驗(yàn)證時(shí)的損失值
val_loss = history.history['val_loss']
# 根據(jù)精確度的個(gè)數(shù),就可以得知訓(xùn)練了多少次
epochs = range(len(acc))
# 繪制訓(xùn)練精確度和驗(yàn)證精確度
plt.plot(epochs, acc, 'r.')
plt.plot(epochs, val_acc, 'r')
plt.title('Training and validation accuracy')
# 繪制訓(xùn)練損失和驗(yàn)證損失
plt.figure()
plt.plot(epochs, loss, 'r.')
plt.plot(epochs, val_loss, 'r-')
plt.title('Training and validation loss')
plt.show()
def train(self, num_classes, batch_size, epochs):
"""
訓(xùn)練模型
"""
# 定義訓(xùn)練數(shù)據(jù)增強(qiáng)生成器
# 參數(shù)preprocessing_function表示每次輸入都進(jìn)行預(yù)處理
# 參數(shù)rotation_range表示圖像隨機(jī)旋轉(zhuǎn)的度數(shù)范圍
# 參數(shù)width_shift_range表示圖像的寬度可移動(dòng)范圍
# 參數(shù)height_shift_range表示圖像的高度可移動(dòng)范圍
# 參數(shù)shear_range表示逆時(shí)針?lè)较蚣羟薪嵌?/span>
# 參數(shù)zoom_range表示隨機(jī)縮放的角度值
# 參數(shù)horizontal_flip表示是否水平翻轉(zhuǎn)
train_datagen = ImageDataGenerator(
preprocessing_function=preprocess_input,
rotation_range=20,
width_shift_range=0.2,
height_shift_range=0.2,
shear_range=0.2,
zoom_range=0.2,
horizontal_flip=True
)
# 定義驗(yàn)證數(shù)據(jù)增強(qiáng)生成器
valid_datagen = ImageDataGenerator(
preprocessing_function=preprocess_input,
rotation_range=20,
width_shift_range=0.2,
height_shift_range=0.2,
shear_range=0.2,
zoom_range=0.2,
horizontal_flip=True
)
# 訓(xùn)練數(shù)據(jù)增強(qiáng)
train_generator = train_datagen.flow(train_tensors, y_train, batch_size=batch_size)
# 驗(yàn)證數(shù)據(jù)增強(qiáng)
validation_generator = valid_datagen.flow(valid_tensors, y_valid, batch_size=batch_size)
# 初始化InceptionV3模型
# include_top=False表示初始化模型時(shí)不包含InceptionV3網(wǎng)絡(luò)結(jié)構(gòu)層中的最后的全連接層
base_model = InceptionV3(weights='imagenet', include_top=False)
# 添加新的全連接層
model = self.add_new_last_layers(base_model, num_classes)
# 凍結(jié)剛創(chuàng)建的InceptionV3的模型的所有卷積層
self.freeze_previous_layers(model, base_model)
# 定義模型檢查點(diǎn),只保存最佳的
checkpointer = ModelCheckpoint(filepath='inception_v3.dogs.133.best.weights.h5',
verbose=1,
save_best_only=True)
print("首次訓(xùn)練模型")
# 在新數(shù)據(jù)集上訓(xùn)練模型
history_tl = model.fit_generator(train_generator,
steps_per_epoch=train_tensors.shape[0] / batch_size,
validation_steps=valid_tensors.shape[0] / batch_size,
epochs=epochs,
verbose=1,
callbacks=[checkpointer],
validation_data=validation_generator)
# 微調(diào)模型
self.fine_tune_model(model)
print("微調(diào)模型后,再次訓(xùn)練模型")
# 我們?cè)俅斡?xùn)練模型
history_ft = model.fit_generator(train_generator,
steps_per_epoch=train_tensors.shape[0] / batch_size,
validation_steps=valid_tensors.shape[0] / batch_size,
epochs=epochs,
verbose=1,
callbacks=[checkpointer],
validation_data=validation_generator)
# 繪制模型的損失值和精確度
self.plot_training(history_ft)
# 每批次大小是128
batch_size = 128
# 訓(xùn)練5個(gè)批次
epochs = 5
incepV3_model = InceptionV3Retrained()
incepV3_model.train(num_classes, batch_size, epochs)
# 測(cè)試模型的精確度
# 創(chuàng)建一個(gè)不帶全連接層的InceptionV3模型
test_model = InceptionV3(weights='imagenet', include_top=False, input_shape=test_tensors.shape[1:])
# 添加全連接層輸出層
incepV3_model = InceptionV3Retrained()
trained_model = incepV3_model.add_new_last_layers(test_model, num_classes)
# 加載剛才訓(xùn)練的權(quán)重到模型中
trained_model.load_weights("inception_v3.dogs.133.best.weights.h5")
# 編譯模型
trained_model.compile(loss='categorical_crossentropy', optimizer='sgd', metrics=['accuracy'])
# 通過(guò)summary()方法,可以看到完整的InceptionV3的神經(jīng)網(wǎng)絡(luò)模型架構(gòu)
# trained_model.summary()
# 評(píng)估模型
score = trained_model.evaluate(test_tensors, y_test, verbose=1)
print("Test {}: {:.2f}. Test {}: {:.2f}.".format(trained_model.metrics_names[0],
score[0]*100,
trained_model.metrics_names[1],
score[1]*100))
# 預(yù)測(cè)狗狗品種
def predict_dog_breed(model, img_path):
# 加載圖像
x = load_img(img_path)
# 圖片預(yù)處理
x = preprocess_input(x)
# 模型預(yù)測(cè)
predictions = model.predict(x)
# 取出預(yù)測(cè)數(shù)值
prediction_list = predictions[0]
# 取出最大值索引和最大值
def get_max_arg_value(prediction_list):
arg_max = np.argmax(prediction_list)
max_val = prediction_list[arg_max]
preds = np.delete(prediction_list, arg_max)
return preds, arg_max, max_val
# 取出前3個(gè)預(yù)測(cè)值的最大值索引和最大值
def get_list_of_max_arg_value(prediction_list):
preds, argmax1, max1val = get_max_arg_value(prediction_list)
preds, argmax2, max2val = get_max_arg_value(preds)
preds, argmax3, max3val = get_max_arg_value(preds)
top_3_argmax = np.array([argmax1, argmax2, argmax3])
top_3_max_val = np.array([max1val, max2val, max3val])
return top_3_argmax, top_3_max_val
top_3_argmax, top_3_max_val = get_list_of_max_arg_value(prediction_list)
dog_titles = [dog_names[index] for index in top_3_argmax]
print('前3個(gè)最大值: {}'.format(top_3_max_val))
# # 如果希望顯示直方圖,可以取消注釋這三行代碼
# plt.barh(np.arange(3), top_3_max_val)
# plt.yticks(np.arange(3), dog_titles)
# plt.show()
# 創(chuàng)建繪圖對(duì)象
fig, ax = plt.subplots()
# 設(shè)置繪圖的總?cè)萜鞔笮?/span>
fig.set_size_inches(5, 5)
# 將最大值乘以100就是百分比
top_3_max_val *= 100
# 拼接前三個(gè)最大值的字符串
dog_title = "{}: {:.2f}%n".format(dog_titles[0], top_3_max_val[0]) +
"{}: {:.2f}%n".format(dog_titles[1], top_3_max_val[1]) +
"{}: {:.2f}%n".format(dog_titles[2], top_3_max_val[2])
# 在繪圖的右上角顯示加上識(shí)別的值字符串
ax.text(1.01, 0.8,
dog_title,
horizontalalignment='left',
verticalalignment='bottom',
transform=ax.transAxes)
# 讀取圖片的數(shù)值內(nèi)容
img = matplotlib.image.imread(img_path)
# 在Axes對(duì)象上顯示圖像
ax.imshow(img)
閱讀全文