使用Keras搭建一个CNN处理MNIST数据

代码结构

这里主要是参考了github上面一个u-net的程序的结构。链接
在项目中,程序员将整个神经网络分成了网络结构和训练两个类,并定义了一些函数来完成类似混淆矩阵生成这样的操作。
在这里,也是模仿了他的写法,这样会显得清晰一些。

代码

这里使用Keras库,采用的是Functional API的搭建网络方式:


#!/usr/bin/env python2
# -*- coding: utf-8 -*-
"""
Created on Thu Jun 15 10:03:52 2017

a simple cnn classifier for mnist data using Functional API


@author: huijian
"""
from __future__ import print_function

import numpy as np
# set the seed for reproducibility
np.random.seed(1234)

from sklearn.metrics import confusion_matrix
import matplotlib.pyplot as plt

# the libraries of keras
from keras.datasets import mnist
from keras.models import Sequential, Model
from keras.models import model_from_json
from keras.layers import Input
from keras.layers import Dense, Dropout, Activation, Flatten
from keras.layers import Conv2D, MaxPooling2D
from keras.utils import np_utils

from keras import backend as K
K.set_image_dim_ordering("tf")
"""
for a picture of a form (128,128,3) (img_row, img_col, channels)
th: (3,128,128) (channels,img_row,img_col)
tf: (128,128,3) (img_row, img_col, channels)
"""

def create_conv(img_shape, num_classes):
    """
    param:
    img_shape: a 1-D tensor, [img_row, img_col, channels]
    """
    inputs = Input(shape=(img_shape[0], img_shape[1], img_shape[2]))
    conv1 = Conv2D(filters = 30, kernel_size = [5,5],strides=[1,1],
                   padding = "same", activation = "relu",
                   kernel_initializer = "glorot_uniform", bias_initializer = "zeros")(inputs)
    pool1 = MaxPooling2D(pool_size=[2,2], strides =[2,2])(conv1)
    conv2 = Conv2D(filters = 15, kernel_size = [3,3],strides=[1,1],
                   padding = "same", activation = "relu",
                   kernel_initializer = "glorot_uniform", bias_initializer = "zeros")(pool1)
    pool2 = MaxPooling2D(pool_size=[2,2],strides = [2,2])(conv2)
    dropout1 = Dropout(rate=0.5)(pool2)
    flat1 = Flatten()(dropout1)
    dense1 = Dense(units=128, activation="relu")(flat1)
    dense2 = Dense(units=50, activation ="relu")(dense1)
    outputs = Dense(num_classes, activation = "softmax")(dense2)

    model = Model(outputs = outputs, inputs = inputs)
    return model

def get_data():
    (x_train, y_train), (x_test, y_test) = mnist.load_data()
    # reshape the data
    # 0-255
    x_train = x_train.reshape(x_train.shape[0],28,28,1).astype(np.float32)
    x_train = x_train/255.0
    x_test = x_test.reshape(x_test.shape[0],28,28,1).astype(np.float32)
    x_test = x_test/255.0

    y_train = np_utils.to_categorical(y_train)
    y_test = np_utils.to_categorical(y_test)
    return (x_train,y_train),(x_test,y_test)


class CNN(object):
    def __init__(self,img_shape, num_classes):
        """
        param:
        img_shape: a 1-D tensor, [img_row, img_col, channels]
        """
        self.img_shape = img_shape
        self.num_classes = num_classes
        self.inference()
    def inference(self):
        self.model = create_conv(self.img_shape, self.num_classes)
        return
    def save(self):
        # save the model
        json_string = self.model.to_json()
        open("my_model_architecture.json","w").write(json_string)
        # save the weights
        self.model.save_weights("my_model_weights.h5")
        return
    def restore(self):
        # restore the model
        self.model = model_from_json(open("my_model_architecture.json").read())
        # restore the model
        self.model.load_weights("my_model_weights.h5")
    def predict(self, x):
        # we can use predict, too, which may require a parameter of batch_size
        return self.model.predict_on_batch(x)

class Train(object):
    def __init__(self, net, loss="categorical_crossentropy", optimizer="adam", metrics=["accuracy"]):
        """
        param:
        net: an instance of model, which is not complied
        """
        net.model.compile(loss=loss,optimizer=optimizer,metrics=metrics)
        self.net = net

    def train(self, batch_size, epochs):
        # get_data
        (x_train,y_train),(x_test,y_test) = get_data()
        # here we designate the test data set as the validation set
        x_validation = x_test
        y_validation = y_test
        assert (x_train.shape[0] % batch_size == 0) and (x_test.shape[0] % batch_size == 0)
        # training
        for step in range(epochs):
            for itera in range((x_train.shape[0]/batch_size)):
                sta = itera * batch_size
                end = (itera+1) * batch_size
                # here we can use model.fit if the data can be stored in the raw
                info = self.net.model.train_on_batch(x = x_train[sta:end,:,:,:], y = y_train[sta:end,:])
                print("It's epoch:{epoch:>2}, loss value:{loss_value:.5f}, accuracy:{accuracy:.5f}".format(
                    epoch = step, loss_value = info[0], accuracy = info[1]))
            # check at every epoch
            info = self.net.model.test_on_batch(x = x_validation, y = y_validation)
            print("Validate the model:{epoch:>2}, loss value:{loss_value:.5f}, accuracy:{accuracy:.5f}".format(
                epoch=step, loss_value=info[0], accuracy=info[1]))
            # save the model at every epoch
            self.net.save()

        # test
        values = self.net.model.evaluate(x_test,y_test,batch_size)
        return values

def print_confusion_matrix(net):

    (x_train, y_train), (x_test, y_test) = get_data()
    y_pred = np.argmax(net.model.predict(x_test),axis=1)
    y_true = np.argmax(y_test, axis = 1)

    cm = confusion_matrix(y_true = y_true, y_pred = y_pred)
    plt.imshow(cm, interpolation="nearest", cmap=plt.cm.Blues)

    # make various adjustments to the plot
    plt.tight_layout()
    plt.colorbar()
    tick_marks = np.arange(net.num_classes)
    plt.xticks(tick_marks, range(net.num_classes))
    plt.yticks(tick_marks, range(net.num_classes))
    plt.xlabel("Predicted")
    plt.ylabel(("True"))
    plt.show()

if __name__=="__main__":

    img_shape = [28, 28, 1]
    num_classes = 10
    cnn = CNN(img_shape, num_classes)

    (x_train, y_train), (x_test, y_test) = get_data()
    print("Data set information:[1] train data shape and dtype is x:{x_train_shape} y:{y_train_shape} x_dtype:{x_train_dtype} y_dtype:{y_train_dtype}".format(
        x_train_shape=x_train.shape, y_train_shape=y_train.shape, x_train_dtype = x_train.dtype, y_train_dtype = y_train.dtype))
    print("Data set information:[2] test data shape and dtype is is x:{x_test_shape} y:{y_test_shape} x_dtype:{x_test_dtype} y_dtype:{y_test_dtype}".format(
        x_test_shape=x_test.shape, y_test_shape=y_test.shape, x_test_dtype = x_test.dtype, y_test_dtype = y_test.dtype))

    # restore
    # cnn.restore()
    # how to adjust the learning rate or how to initialize the weights?
    # train = Train(net = cnn,loss = "categorical_crossentropy",optimizer="adam", metrics=["accuracy"])
    # values = cnn.model.evaluate(x_test,y_test,batch_size = 100)
    # or train
    train = Train(net = cnn,loss = "categorical_crossentropy",optimizer="adam", metrics=["accuracy"])
    # print the confusion_matrix before training
    print_confusion_matrix(net = train.net)
    values = train.train(batch_size = 100, epochs = 1)

    print("Test data set: loss value:{loss_value:.5f} accuracy:{accuracy:.5f}".format(
            loss_value=values[0], accuracy=values[1]))
    # print the confusion_matrix after traininng
    print_confusion_matrix(net = train.net)  

References:
1. Generic U-Net Tensordflow implementation for segmentation
2. Keras 实现CNN进行手写字符识别
3. Keras Document

你可能感兴趣的:(Python)