Tensorflow2.1实现CNN神经网络的工程案例

原创 2020-02-12 23:23  阅读 194 次 评论 0 条

本过程案例实现图片的分类,用户通过网页上传图片,由神经网络识别后返回识别结果。

工程涉及代码主要有:main.py,buildmodel.py,config.ini以及网页模板,其中main.py是主文件,buildmodel.py是模型建构;

工程涉及的文件夹主要有:train_data/是训练图片文件夹,test_data/是测试图片文件夹,model_dir/存放模型文件,static/存放Flask静态文件,templates/存放网页模板文件;

数据主要是采集的图片文件,已经经过缩放、剪裁等标准化处理;

神经网络识别图像实现主要采用Tensorflow2.1,网页服务采用Flask, 代码如下:

1.main.py

#导入所有依赖包
import flask
import os,sys
import pickle
import codecs
import chardet
import werkzeug
import buildmodel
import configparser
import numpy as np
import tensorflow as tf
from PIL import Image
 
#涉及文件有主文件main.py,模型文件buildmodel.py,网页文件app.py,配置文件config.ini以及网页模板
#train_data/是训练文件夹,test_data/是测试文件夹,model_dir/存放模型文件
#static/存放Flask静态文件,templates/存放模板文件
 
#读取配置文件参数  
def get_config(config_file='config.ini'):
    #识别文件编码
    f = open(config_file,'rb')
    fencoding=chardet.detect(f.readline())
    f.close()
    #读取配置文件
    parser=configparser.ConfigParser()
    parser.read(config_file, encoding=fencoding['encoding'])
    #分别获取整形、浮点和字符串配置参数
    _conf_ints = [(key, int(value)) for key, value in parser.items('ints')]
    _conf_floats = [(key, np.float32(value)) for key, value in parser.items('floats')]
    _conf_strings = [(key, str(value)) for key, value in parser.items('strings')]
    #以字典形式返回配置参数
    return dict(_conf_ints + _conf_floats + _conf_strings)
 
#初始化一个字典,用于存放从配置文件中获取的配置参数
gConfig = {}
#使用get_config方法从配置文件中获取配置参数
gConfig = get_config(config_file='config.ini')
 
#创建一个flask wen应用,名称为imgClassifierWeb
app = flask.Flask("imgClassifierWeb")
 
#增加homepage路由,用来上传图像文件
def homepage_upload():
    return flask.render_template(template_name_or_list="upload_image.html")
app.add_url_rule(rule="/", endpoint="homepage", view_func=homepage_upload)
 
#增加upload路由,使用POST方法,用于图像的上传
def upload_image():
    global secure_filename
    if flask.request.method == "POST":  # 设置request的模式为POST
        img_file = flask.request.files["image_file"]  # 获取需要分类的图片
        secure_filename = werkzeug.secure_filename(img_file.filename)  # 生成一个没有乱码的文件名
        img_path = os.path.join(app.root_path, "predict_img/"+secure_filename)  # 获取图片的保存路径
        img_file.save(img_path)  # 将图片保存在应用的根目录下
        print("图片上传成功.")
 
        return flask.redirect(flask.url_for(endpoint="predict"))
    return "图片上传失败"
app.add_url_rule(rule="/upload/", endpoint="upload", view_func=upload_image, methods=["POST"])
 
 
#增加predict路由,预测上传图片的分类
def CNN_predict():
    global secure_filename
    #使用PIL中 的Image打开文件并获取图像文件中的信息
    img = Image.open(os.path.join(app.root_path, 'predict_img/'+secure_filename))
    #将图像文件的格式转换为RGB
    img = img.convert("RGB")
    #分别获取r,g,b三元组的像素数据并进行拼接
    r, g, b = img.split()
    r_arr = np.array(r)
    g_arr = np.array(g)
    b_arr = np.array(b)
    img = np.concatenate((r_arr, g_arr, b_arr))
    #将拼接得到的数据按照模型输入维度需要转换为(32,32,3),并对数据进行归一化
    image = img.reshape([1, 32, 32, 3])/255
    #调用execute中的predict方法进行预测
    predicted_class = buildmodel.predict(image)
    #将预测结果返回并使用模板进行页面渲染
    return flask.render_template(template_name_or_list="prediction_result.html",
                             predicted_class=predicted_class)
app.add_url_rule(rule="/predict/", endpoint="predict", view_func=CNN_predict)
 
if __name__ == "__main__":
    app.run()

2.buildmodel.py

#导入所有依赖包
import flask
import os,sys
import pickle
import codecs
import chardet
import werkzeug
import buildmodel
import configparser
import numpy as np
import tensorflow as tf
from PIL import Image
 
#读取配置文件参数  
def get_config(config_file='config.ini'):
    #识别文件编码
    f = open(config_file,'rb')
    fencoding=chardet.detect(f.readline())
    f.close()
    #读取配置文件
    parser=configparser.ConfigParser()
    parser.read(config_file, encoding=fencoding['encoding'])
    #分别获取整形、浮点和字符串配置参数
    _conf_ints = [(key, int(value)) for key, value in parser.items('ints')]
    _conf_floats = [(key, np.float32(value)) for key, value in parser.items('floats')]
    _conf_strings = [(key, str(value)) for key, value in parser.items('strings')]
    #以字典形式返回配置参数
    return dict(_conf_ints + _conf_floats + _conf_strings)
 
#设计和编译神经网络模型
class cnnModel(object):
    #类参数初始化
    def __init__(self,rate):
        #定义神经元Dropout比例
        self.rate=rate
    #使用tf.keras.Sequential定义网络模型
    def createModel(self):
        model = tf.keras.Sequential()
 
        #定义第一个二维卷积层,输出数据维度64,卷积核为3*3
        model.add(tf.keras.layers.Conv2D(64, (3,3), kernel_initializer='he_normal', strides=1, activation='relu', padding='same',
                                    input_shape=(32,32,3),name="conv1"))
        #定义第一个二维池化层,使用最大值池化,池化维度2*2
        model.add(tf.keras.layers.MaxPool2D((2, 2), strides=2, padding='valid', name="pool1"))
        #添加第一个批量池化层BatchNormalization
        model.add(tf.keras.layers.BatchNormalization())
 
        #定义第二个二维卷积层,输出数据维度128,卷积核为3*3
        model.add(tf.keras.layers.Conv2D(128, (3,3), kernel_initializer='he_normal', strides=1, activation='relu', padding='same',
                                   name="conv2"))
        #定义第二个二维池化层,使用最大值池化,池化维度2*2
        model.add(tf.keras.layers.MaxPool2D((2, 2), strides=2, padding='valid', name="pool2"))
        #添加第二个批量池化层BatchNormalization
        model.add(tf.keras.layers.BatchNormalization())
 
        #定义第三个二维卷积层,输出数据维度256,卷积核为3*3
        model.add(tf.keras.layers.Conv2D(256, 3, kernel_initializer='he_normal', strides=1, activation='relu', padding='same',
                                   name="conv3"))
        #定义第三个二维池化层,使用最大值池化,池化维度2*2
        model.add(tf.keras.layers.MaxPool2D((2, 2), strides=2, padding='valid', name="pool3"))
        #添加第三个批量池化层BatchNormalization
        model.add(tf.keras.layers.BatchNormalization())
 
        #添加Flatte层将数据压平成一个维度
        model.add(tf.keras.layers.Flatten(name="flatten"))
        #添加Dropout层,防止过拟合
        model.add(tf.keras.layers.Dropout(self.rate))
        #添加第一个全连接层
        model.add(tf.keras.layers.Dense(128, activation='relu',kernel_initializer='he_normal'))
        #添加Dropout层,防止过拟合
        model.add(tf.keras.layers.Dropout(self.rate))
        #添加第二个全连接层,输出数据的维度10,即10个分类
        model.add(tf.keras.layers.Dense(10, activation='softmax',kernel_initializer='he_normal'))
        #神经网络模型设计结束,开始编译,生成模型
        model.compile(loss='categorical_crossentropy', optimizer='adam', metrics=['accuracy']) 
        return model
 
#读取训练数据文件
def unpickle_patch(file):
    patch_bin_file = open(file, 'rb')
    patch_dict = pickle.load(patch_bin_file, encoding='bytes')
    return patch_dict
 
#定义数据读取函数,完成数据读取、格式转换等操作
def read_data(dataset_path, im_dim, num_channels,num_files,images_per_file):
        #获取文件夹中的文件名
        files_names = os.listdir(dataset_path)
        #创建空白多维数组,存放图像二进制数据
        dataset_array = np.zeros(shape=(num_files * images_per_file, im_dim, im_dim, num_channels))
        #创建空白数组,存放图像标注信息
        dataset_labels = np.zeros(shape=(num_files * images_per_file), dtype=np.uint8)
        index = 0     
        #从训练集数据中读取二进制数据并转换成32*32*3
        for file_name in files_names:
            if file_name[0:len(file_name)-1] == "data_batch_":
                print("正在处理数据 : ", file_name)
                data_dict = unpickle_patch(dataset_path + file_name)
                images_data = data_dict[b"data"]
                images_data_reshaped = np.reshape(images_data, newshape=(len(images_data), im_dim, im_dim, num_channels))
                dataset_array[index * images_per_file:(index + 1) * images_per_file, :, :, :] = images_data_reshaped
                dataset_labels[index * images_per_file:(index + 1) * images_per_file] = data_dict[b"labels"]
                index = index + 1
        return dataset_array, dataset_labels  # 返回数据
 
def create_model():
    #判断是否存在预训练好的模型
    if 'pretrained_model'in gConfig:
        model=tf.keras.models.load_model(gConfig['pretrained_model'])
        return model
    ckpt=tf.io.gfile.listdir(gConfig['working_directory'])
 
    #如果存在训练中的模型文件则加载并继续训练,否则新建模型
    if  ckpt:
        model_file=os.path.join(gConfig['working_directory'], ckpt[-1])
        print("Reading model parameters from %s" % model_file)
        model=tf.keras.models.load_model(model_file)
        return model
    else:
        model=cnnModel(gConfig['keeps'])
        model=model.createModel()
        return model
 
#定义训练函数
def train():
     #实例化一个神经网络模型
     model=create_model()
     print(model.summary())
     #开始进行模型训练
     model.fit(dataset_array,dataset_labels,verbose=1,epochs=gConfig['epochs'],validation_data=(test_array,test_labels))
 
     #保存训练好的模型
     filename='cnn_model.h5'
     checkpoint_path = os.path.join(gConfig['working_directory'], filename)
     model.save(checkpoint_path)
     #sys.stdout.flush()
 
#定义预测函数
def predict(data):
    file = gConfig['dataset_path'] + "batches.meta"
    patch_bin_file = open(file, 'rb') 
    label_names_dict = pickle.load(patch_bin_file)["label_names"]
    #获取最新的模型文件路径 
    ckpt=os.listdir(gConfig['working_directory'])
    checkpoint_path=os.path.join(gConfig['working_directory'],'cnn_model.h5')
    #加载模型文件
    model=tf.keras.models.load_model(checkpoint_path)
    #对数据进行验证
    predicton=model.predict(data)
    index=tf.math.argmax(predicton[0]).numpy()
    #返回预测的分类名称
    return label_names_dict[index]
 
#读取配置文件参数
gConfig=get_config(config_file="config.ini") 
dataset_array, dataset_labels = read_data(dataset_path=gConfig['dataset_path'], im_dim=gConfig['im_dim'],
   num_channels=gConfig['num_channels'],num_files=5,images_per_file=gConfig['images_per_file'])
test_array, test_labels = read_data(dataset_path=gConfig['test_path'], im_dim=gConfig['im_dim'],
   num_channels=gConfig['num_channels'],num_files=1,images_per_file=gConfig['images_per_file'])
 
#对训练输入数据进行归一化处理
dataset_array=dataset_array.astype('float32')/255
test_array=test_array.astype('float32')/255
 
#对标注数据进行one-hot编码
dataset_labels=tf.keras.utils.to_categorical(dataset_labels,10)
test_labels=tf.keras.utils.to_categorical(test_labels,10)
 
if __name__=='__main__':
 
    if gConfig['mode']=='train':
        train()
 
    elif gConfig['mode']=='serve':
        print('请使用:python3 main.py')

3.config.ini

[strings]
# Mode : train, test, serve
mode = train
#配置模型文件的存储路径
working_directory = model_dir
#配置训练文件的路径
dataset_path=train_data/
#配置测试文件的路径
test_path=test_data/
 
[ints]
#运行测试集之前的步骤数
steps_per_checkpoint = 2
#F分类图像的种类
num_dataset_classes=10
#训练数据的总数
dataset_size=50000
#图像输入的尺寸
im_dim=32
#图像的通道数
num_channels = 3
#训练文件的数量
num_files=6
#每个训练文件中的图像数量
images_per_file=10000
#训练周期
epochs=2
 
[floats]
#dropout神经元失效的概率
keeps=0.5

4.网页模板等文件

下载:templates

参考文献:赵英俊.走向TensorFlow 2.0

本文地址:http://51blog.com/?p=8728
关注我们:请关注一下我们的微信公众号:扫描二维码广东高校数据家园_51博客的公众号,公众号:数博联盟
版权声明:本文为原创文章,版权归 jnussl 所有,欢迎分享本文,转载请保留出处!

发表评论


表情