python-用Tensorflow構建卷積神經網絡圖像分類

 環境:python3.6版本 + TensorFlow 1.6版本

import os
from PIL import Image
import numpy as np
import tensorflow as tf


data_dir = r'E://data/data'  # 數據文件夾
train = True  # 訓練還是測試
model_path = "model/image_model"  # 模型文件路徑


# 從文件夾讀取圖片和標籤到numpy數組中
# 標籤信息在文件名中,例如1_40.jpg表示該圖片的標籤爲1
def read_data(data_dir):
    datas = []
    labels = []
    fpaths = []
    for fname in os.listdir(data_dir):
        fpath = os.path.join(data_dir, fname)
        data = np.array(Image.open(fpath))/255.0   # pillow讀取的圖像像素值在0-255之間,需要歸一化
        label = int(fname.split("_")[0])
        fpaths.append(fpath)  # 在讀取圖像數據、Label信息的同時,記錄圖像的路徑
        datas.append(data)
        labels.append(label)
    datas = np.array(datas)
    labels = np.array(labels)
    print("shape of datas: {}\tshape of labels: {}".format(datas.shape, labels.shape))
    return fpaths, datas, labels


fpaths, datas, labels = read_data(data_dir)
# 'shape of datas: (150, 32, 32, 3)	shape of labels: (150,)'
# 計算有多少類圖片
num_classes = len(set(labels))

# Placeholder:TensorFlow中的佔位符,用於傳入外部數據。
#   dtype:數據類型;shape:數據的維度。默認爲None,表示沒有限制;name:名稱;返回類型:Tensor
# Tensorflow 1.x 版本提供placeholder,而 2.0版本暫時沒有這個模塊。
datas_placeholder = tf.placeholder(tf.float32, [None, 32, 32, 3])
labels_placeholder = tf.placeholder(tf.int32, [None])
dropout_placeholdr = tf.placeholder(tf.float32)  # 存放DropOut參數的容器,訓練時爲0.25,測試時爲0



tf.layers.conv2d (inputs,  # 必需,即需要進行操作的輸入數據。
                  filters,  # 必需,是一個數字,代表了輸出通道的個數,即 output_channels
                  kernel_size,  # 必需,卷積核大小,必須是一個數字(高和寬都是此數字)或者長度爲 2 的列表(分別代表高、寬)。
                  strides=(1, 1),  # 可選,默認爲 (1, 1),卷積步長,必須是一個數字(高和寬都是此數字)或者長度爲 2 的列表(分別代表高、寬)
                  padding='valid',  # 可選,默認爲 valid,padding 的模式,有 valid 和 same 兩種,大小寫不區分。
                  data_format='channels_last',
                  # 可選,默認 channels_last,分爲 channels_last 和 channels_first 兩種模式,代表了輸入數據的維度類型,
                  # 如果是 channels_last, 那麼輸入數據的 shape 爲 (batch, height, width, channels),
                  # 如果是 channels_first,那麼輸入數據的 shape 爲 (batch, channels, height, width)。
                  dilation_rate=(1, 1),  # 可選,默認爲 (1, 1),卷積的擴張率,如當擴張率爲2時,卷積核內部就會有邊距,3×3 的卷積核就會變成 5×5。
                  activation=None,  # 可選,默認爲 None,如果爲 None 則是線性激活。
                  use_bias=True,  # 可選,默認爲 True,是否使用偏置。
                  kernel_initializer=None,  # 可選,默認爲 None,即權重的初始化方法,如果爲 None,則使用默認的 Xavier 初始化方法。
                  bias_initializer=None,  # 可選,默認爲零值初始化,即偏置的初始化方法。
                  kernel_regularizer=None,  # 可選,默認爲 None,施加在權重上的正則項。
                  bias_regularizer=None,  # 可選,默認爲 None,施加在偏置上的正則項。
                  activity_regularizer=None,  # 可選,默認爲 None,施加在輸出上的正則項。
                  kernel_constraint=None,  # 可選,默認爲 None,施加在權重上的約束項。
                  bias_constraint=None,  # 可選,默認爲 None,施加在偏置上的約束項。
                  trainable=True,  # 可選,默認爲 True,布爾類型,如果爲 True,則將變量添加到 GraphKeys.TRAINABLE_VARIABLES 中。
                  name=None,  # 可選,默認爲 None,卷積層的名稱。
                  reuse=None  # 可選,默認爲 None,布爾類型,如果爲 True,那麼如果 name 相同時,會重複利用。
                  )


# 定義卷積層, 20個卷積核, 卷積核大小爲5,用Relu激活
conv0 = tf.layers.conv2d(inputs=datas_placeholder, filters=20, kernel_size=5, activation=tf.nn.relu)
# 定義max-pooling層,pooling窗口爲2x2,步長爲2x2
pool0 = tf.layers.max_pooling2d(conv0, [2, 2], [2, 2])

# 定義卷積層, 40個卷積核, 卷積核大小爲4,用Relu激活
conv1 = tf.layers.conv2d(pool0, 40, 4, activation=tf.nn.relu)
# 定義max-pooling層,pooling窗口爲2x2,步長爲2x2
pool1 = tf.layers.max_pooling2d(conv1, [2, 2], [2, 2])

# 將3維特徵轉換爲1維向量
flatten = tf.layers.flatten(pool1)

# 全連接層,轉換爲長度爲100的特徵向量
fc = tf.layers.dense(flatten, 400, activation=tf.nn.relu)

# 加上DropOut,防止過擬合
dropout_fc = tf.layers.dropout(fc, dropout_placeholdr)

# 未激活的輸出層
logits = tf.layers.dense(dropout_fc, num_classes)

predicted_labels = tf.arg_max(logits, 1)


# 利用交叉熵定義損失
losses = tf.nn.softmax_cross_entropy_with_logits(
    labels=tf.one_hot(labels_placeholder, num_classes),
    logits=logits
)
# 平均損失
mean_loss = tf.reduce_mean(losses)

# 定義優化器,指定要優化的損失函數
optimizer = tf.train.AdamOptimizer(learning_rate=1e-2).minimize(losses)


# 用於保存和載入模型
saver = tf.train.Saver()

with tf.Session() as sess:

    if train:
        print("訓練模式")
        # 如果是訓練,初始化參數
        sess.run(tf.global_variables_initializer())
        # 定義輸入和Label以填充容器,訓練時dropout爲0.25
        train_feed_dict = {
            datas_placeholder: datas,
            labels_placeholder: labels,
            dropout_placeholdr: 0.25
        }
        for step in range(150):
            _, mean_loss_val = sess.run([optimizer, mean_loss], feed_dict=train_feed_dict)

            if step % 10 == 0:
                print("step = {}\tmean loss = {}".format(step, mean_loss_val))
        saver.save(sess, model_path)
        print("訓練結束,保存模型到{}".format(model_path))
    else:
        print("測試模式")
        # 如果是測試,載入參數
        saver.restore(sess, model_path)
        print("從{}載入模型".format(model_path))
        # label和名稱的對照關係
        label_name_dict = {
            0: "飛機",
            1: "汽車",
            2: "鳥"
        }
        # 定義輸入和Label以填充容器,測試時dropout爲0
        test_feed_dict = {
            datas_placeholder: datas,
            labels_placeholder: labels,
            dropout_placeholdr: 0
        }
        predicted_labels_val = sess.run(predicted_labels, feed_dict=test_feed_dict)
        # 真實label與模型預測label
        for fpath, real_label, predicted_label in zip(fpaths, labels, predicted_labels_val):
            # 將label id轉換爲label名
            real_label_name = label_name_dict[real_label]
            predicted_label_name = label_name_dict[predicted_label]
            print("{}\t{} => {}".format(fpath, real_label_name, predicted_label_name))











 

發佈了98 篇原創文章 · 獲贊 165 · 訪問量 163萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章