diff --git a/lessonFive/to_jpg.py b/lessonFive/to_jpg.py new file mode 100644 index 0000000..3af140a --- /dev/null +++ b/lessonFive/to_jpg.py @@ -0,0 +1,76 @@ +#coding=utf-8 +import cv2 +import numpy as np +import os +import pickle + + +#文件夹名 +str_2 = 'train_data' +str_1 = 'test_data' + +#判断文件夹是否存在,不存在的话创建文件夹 +if os.path.exists(str_1) == False: + os.mkdir(str_1) +if os.path.exists(str_2) == False: + os.mkdir(str_2) + +# 解压缩,返回解压后的字典,f,encoding='bytes' +def unpickle(file): + + fo = open(file, 'rb') + dict = pickle.load(fo, encoding='bytes') + fo.close() + return dict + +def cifar_jpg(dir_file): +# 生成训练集图片,如果需要png格式,只需要改图片后缀名即可。 + for j in range(1, 6): + dataName = dir_file + '/' + "data_batch_" + str(j) # 读取当前目录下的data_batch12345文件,dataName其实也是data_batch文件的路径,本文和脚本文件在同一目录下。 + Xtr = unpickle(dataName) + print(Xtr) + print(dataName + " is loading...") + + for i in range(0, 10000): + img = np.reshape(Xtr[b'data'][i], (3, 32, 32)) + img = img.transpose(1, 2, 0) # 读取image + picName = 'train_data/' + str(Xtr[b'labels'][i]) + '_' + str(i + (j - 1) * 10000) + '.jpg' # Xtr['labels']为图片的标签,值范围0-9,本文中,train文件夹需要存在,并与脚本文件在同一目录下。 + cv2.imwrite(picName, img) + print(dataName + " loaded.") + + print("test_batch is loading...") + +# 生成测试集图片 + testName = dir_file + '/' + 'data_batch_6' + testXtr = unpickle(testName) + for i in range(0, 10000): + img = np.reshape(testXtr[b'data'][i], (3, 32, 32)) + img = img.transpose(1, 2, 0) + picName = 'test_data/' + str(testXtr[b'labels'][i]) + '_' + str(i) + '.jpg' + cv2.imwrite(picName, img) + print("test_batch loaded.") + return + +#标签与名字的对应关系 +def label_name(): + label_name_dict ={ + 'airplane': "0", + 'automobile': "1", + 'bird': "2", + 'cat': "3", + 'deer': "4", + 'dog': "5", + 'frog': "6", + 'horse': "7", + 'ship': "8", + 'truck': "9" + } + return label_name_dict + +if __name__ == '__main__': + dir_file = 'train_data' + #cifar_jpg(dir_file) +try: + cifar_jpg(dir_file) +except: + print('函数报错') diff --git a/lessonOne/imgClassifierWeb/cnnModel.py b/lessonOne/imgClassifierWeb/cnnModel.py index 8cac31f..1196aaf 100644 --- a/lessonOne/imgClassifierWeb/cnnModel.py +++ b/lessonOne/imgClassifierWeb/cnnModel.py @@ -106,7 +106,7 @@ def fc_layer(flattened_layer, num_inputs, num_outputs): fc_resultl = tf.matmul(flattened_layer, fc_weights) return fc_resultl batch_size=gConfig['percent']*gConfig['dataset_size']/100 - self.data_tensor=tf.placeholder(tf.float32,shape=tf.placeholder(tf.float32,shape=[batch_size,gConfig['im_dim'], gConfig['im_dim'],gConfig['num_channels']],name='data_tensor')) + self.data_tensor=tf.placeholder(tf.float32,shape=[batch_size,gConfig['im_dim'], gConfig['im_dim'],gConfig['num_channels']],name='data_tensor') self.label_tensor=tf.placeholder(tf.int32,shape=[batch_size],name='label_tensor') keep_prop=tf.Variable(initial_value=0.5,name="keep_prop") self.fc_result=create_CNN(input_data=self.data_tensor,num_classes=gConfig['num_dataset_classes'],keep_prop=gConfig['keeps']) diff --git a/lessonSix/vgg16/app.py b/lessonSix/vgg16/app.py new file mode 100644 index 0000000..7d8a266 --- /dev/null +++ b/lessonSix/vgg16/app.py @@ -0,0 +1,130 @@ +import flask +import werkzeug +import os +import scipy.misc +import tensorflow as tf +import getConfig +import execute + +gConfig = {} +gConfig = getConfig.get_config(config_file='config.ini') + +# Creating a new Flask Web application. It accepts the package name. +app = flask.Flask("imgClassifierWeb") + + +def CNN_predict(): + global sess + global model + global graph + + """ + global: + """ + global secure_filename + # 从本地目录读取需要分类的图片 + img = scipy.misc.imread(os.path.join(app.root_path, secure_filename)) + + """ + 校验图片格式 + """ + if (img.ndim) == 3: + """ + 是否为32*32 + """ + if img.shape[0] == img.shape[1] and img.shape[0] == 32: + """ + 是否为3通道,GRB + """ + if img.shape[-1] == 3: + + predicted_class = execute.predict_line(sess, model, img, graph) + """ + 将返回的结果用页面模板给渲染出来 + """ + return flask.render_template(template_name_or_list="prediction_result.html", + predicted_class=predicted_class) + else: + """ 如果检测出图片格式不符合要求,则返回错误并返回上传图片的格式""" + return flask.render_template(template_name_or_list="error.html", img_shape=img.shape) + else: + """ 如果检测出图片格式不符合要求,则返回错误并返回上传图片的格式""" + return flask.render_template(template_name_or_list="error.html", img_shape=img.shape) + return "遇到非图片格式的未知错误,请联系技术人员解决" + + +""" +flask路由系统: +1、使用flask.Flask.route() 修饰器。 +2、使用flask.Flask.add_url_rule()函数。 +3、直接访问基于werkzeug路由系统的flask.Flask.url_map. +参考知识链接:https://www.jianshu.com/p/e69016bd8f08 +1、@app.route('/index.html') + def index(): + return "Hello World!" +2、def index(): + return "Hello World!" + index = app.route('/index.html')(index) +app.add_url_rule:app.add_url_rule(rule,endpoint,view_func) +关于rule、ednpoint、view_func以及函数注册路由的原理可以参考:https://www.cnblogs.com/eric-nirnava/p/endpoint.html +""" +app.add_url_rule(rule="/predict/", endpoint="predict", view_func=CNN_predict) +""" +知识点: +flask.request属性 +form: +一个从POST和PUT请求解析的 MultiDict(一键多值字典)。 +args: +MultiDict,要操作 URL (如 ?key=value )中提交的参数可以使用 args 属性: +searchword = request.args.get('key', '') +values: +CombinedMultiDict,内容是form和args。 +可以使用values替代form和args。 +cookies: +顾名思义,请求的cookies,类型是dict。 +stream: +在可知的mimetype下,如果进来的表单数据无法解码,会没有任何改动的保存到这个·stream·以供使用。很多时候,当请求的数据转换为string时,使用data是最好的方式。这个stream只返回数据一次。 +headers: +请求头,字典类型。 +data: +包含了请求的数据,并转换为字符串,除非是一个Flask无法处理的mimetype。 +files: +MultiDict,带有通过POST或PUT请求上传的文件。 +method: +请求方法,比如POST、GET +知识点参考链接:https://blog.csdn.net/yannanxiu/article/details/53116652 +werkzeug +""" + + +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, 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"]) + + +def redirect_upload(): + return flask.render_template(template_name_or_list="upload_image.html") + + +""" +""" +app.add_url_rule(rule="/", endpoint="homepage", view_func=redirect_upload) +sess = tf.Session() +sess, model, graph = execute.init_session(sess, conf='config.ini') +if __name__ == "__main__": + app.run(host="localhost", port=7777, debug=False) diff --git a/lessonSix/vgg16/config.ini b/lessonSix/vgg16/config.ini new file mode 100644 index 0000000..a0c48d2 --- /dev/null +++ b/lessonSix/vgg16/config.ini @@ -0,0 +1,24 @@ +[strings] +# Mode : train, test, serve +mode = train +working_directory = model/ +dataset_path=/Users/zhaoyingjun/Learning/TensorFlow_code/lessonOne/imgClassifierWeb/train_data/ +dataset_test=/Users/zhaoyingjun/Learning/TensorFlow_code/lessonOne/imgClassifierWeb/test_data/ + +[ints] +steps_per_checkpoint = 10 +num_dataset_classes=10 +dataset_size=50000 +im_dim=32 +num_channels = 3 +num_files=5 +images_per_file=10000 +max_gradient_norm=5 + +[floats] +learning_rate = 0.01 +keeps=0.5 +learning_rate_decay_factor = 0.9 +percent=1 +end_learning_rate=0.0 + diff --git a/lessonSix/vgg16/execute.py b/lessonSix/vgg16/execute.py new file mode 100644 index 0000000..63849a4 --- /dev/null +++ b/lessonSix/vgg16/execute.py @@ -0,0 +1,190 @@ +import tensorflow as tf +import numpy as np +import vggModel +import os +import pickle +import time +import getConfig +import sys +import random +gConfig = {} + +def read_data(dataset_path, im_dim, num_channels,num_files,images_per_file): + files_names = os.listdir(dataset_path) + print(files_names) + # 获取训练集中训练文件的名称 + """ + 在CIFAR10中已经为我们标注和准备好了数据,一时找不到合适的高质量的标注训练集,我们就是使用CIFAR10的来作为我们的训练集。 + 在训练集中一共有50000个训练样本,放到5个二进制文件中心,每个样本有3072个像素点,是32*3维度的 + """ + #创建空的多维数组用于存放图片二进制数据 + 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 + #从训练集中读取二进制数据并将其维度转换成224*224*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"] + # 格式转换为224x224x3 shape. + print(images_data.shape) + 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 unpickle_patch(file): + #打开文件,读取二进制文件,返回读取到的数据 + patch_bin_file = open(file, 'rb') + patch_dict = pickle.load(patch_bin_file, encoding='bytes')#Loading the details of the binary file into a dictionary. + return patch_dict + +def create_model(session,forward_only): + + #用vggModel实例化一个对象model + model=vggModel.vggModel(gConfig['percent'],gConfig['learning_rate'],gConfig['learning_rate_decay_factor']) + if 'pretrained_model'in gConfig: + model.saver.restore(session,gConfig['pretrained_model']) + return model + ckpt=tf.train.get_checkpoint_state(gConfig['working_directory']) + #判断是否已经有Model文件存在,如果model文件存在则加载原来的model并在原来的moldel继续训练,如果不存在则新建model相关文件 + if ckpt and ckpt.model_checkpoint_path: + print("Reading model parameters from %s" % ckpt.model_checkpoint_path) + model.saver.restore(session, ckpt.model_checkpoint_path) + session.run(tf.global_variables_initializer()) + graph = tf.get_default_graph() + + return model,graph + + else: + print("Created model with fresh parameters.") + session.run(tf.global_variables_initializer()) + graph = tf.get_default_graph() + return model,graph +#获取批量处理数据,考虑到配置不同,如果没有GPU建议将percent调小一点,即将训练集调小 +def get_batch(data,labels,percent): + num_elements = np.uint32(percent * data.shape[0] / 100) + #之所以没有采用tf自带的tf.train.batch是因为那个接口对内存的消耗太大了,我们采用这种随机取index的方法简单又实用。 + n=np.random.randint(len(labels),size=num_elements) + shuffle_datas=[] + shuffled_labels=[] + for i in n: + shuffle_datas.append(data[i,:,:,:]) + shuffled_labels.append(labels[i]) + return shuffle_datas,shuffled_labels + +#定义训练函数 +def train(): + """使用BFC内存管理管理算法,tf.ConfigProto()用于GPU的管理,可以控制GPU的使用率 + #allow growth + config = tf.ConfigProto() + config.gpu_options.allow_growth = True + # per_process_gpu_memory_fraction + gpu_options=tf.GPUOptions(per_process_gpu_memory_fraction=0.7) + config=tf.ConfigProto(gpu_options=gpu_options) + 关于BFC算法: + 将内存分块管理,按块进行空间分配和释放。 + 通过split操作将大内存块分解成用户需要的小内存块。 + 通过merge操作合并小的内存块,做到内存碎片回收 + 通过bin这个抽象数据结构实现对空闲块高效管理。 + """ + + config = tf.ConfigProto() + config.gpu_options.allocator_type = 'BFC' + + #读取训练集的数据到内存中 + dataset_array, dataset_labels = read_data(dataset_path=gConfig['dataset_path'], im_dim=gConfig['im_dim'], + num_channels=gConfig['num_channels'],num_files=gConfig['num_files'],images_per_file=gConfig['images_per_file']) + #打印训练数据的维度 + print("Size of data : ", dataset_array.shape) + #读取测试数据到内存中 + dataset_array_test, dataset_labels_test = read_data(dataset_path=gConfig['dataset_test'], im_dim=gConfig['im_dim'], num_channels=gConfig['num_channels'],num_files=1,images_per_file=gConfig['images_per_file']) + #打印测试数据的维度 + print("Size of data : ", dataset_array_test.shape) + + with tf.Session(config=config) as sess: + model,_=create_model(sess,False) + + step_time, accuracy = 0.0, 0.0 + current_step = 0 + previous_correct = [] + # 开始训练循环,这里没有设置结束条件,当学习率下降到为0时停止 + while model.learning_rate.eval()>gConfig['end_learning_rate']: + + shuffled_datas, shuffled_labels = get_batch(data=dataset_array, labels=dataset_labels, + percent=gConfig['percent']) + shuffled_datas_test, shuffled_labels_test = get_batch(data=dataset_array_test, labels=dataset_labels_test, + percent=gConfig['percent']*5) + + start_time = time.time() + step_correct=model.step(sess,shuffled_datas,shuffled_labels,False) + step_time += (time.time() - start_time) / gConfig['steps_per_checkpoint'] + accuracy += step_correct / gConfig['steps_per_checkpoint'] + current_step += 1 + + # 达到一个训练模型保存点后,将模型保存下来,并打印出这个保存点的平均准确率 + if current_step % gConfig['steps_per_checkpoint'] == 0: + #如果超过三次预测正确率没有升高则改变学习率 + if len(previous_correct) > 2 and accuracy == min(previous_correct[-3:]): + sess.run(model.learning_rate_decay_op) + previous_correct.append(accuracy) + checkpoint_path = os.path.join(gConfig['working_directory'], "cnn.ckpt") + #saver=tf.train.Saver() + model.saver.save(sess, checkpoint_path,global_step=model.global_step) + + + #以下为增加模型在测试集上的准确率测试 + graph = tf.get_default_graph() + #以下获取默认计算图的参数 + softmax_propabilities = graph.get_tensor_by_name(name="softmax_probs:0") + softmax_predictions = tf.argmax(softmax_propabilities, axis=1) + data_tensor = graph.get_tensor_by_name(name="data_tensor:0") + label_tensor = graph.get_tensor_by_name(name="label_tensor:0") + keep_prop = graph.get_tensor_by_name(name="keep_prop:0") + + feed_dict_testing = {data_tensor: shuffled_datas_test, + label_tensor: shuffled_labels_test, + keep_prop: 1.0} + + softmax_propabilities_, softmax_predictions_ = sess.run([softmax_propabilities, softmax_predictions], + feed_dict=feed_dict_testing) + + correct = np.array(np.where(softmax_predictions_ == shuffled_labels_test)) + correct = correct.size + #打印出模型在训练集上的准确率 + print("在", str(gConfig['percent'] *gConfig['dataset_size']/100),"个训练集上训练的准确率", ' : ', accuracy) + print("学习率 %.4f 每步耗时 %.2f " % ( model.learning_rate.eval(),step_time)) + step_time, accuracy = 0.0,0.0 + + #打印出模型在测试集上的准确率 + print("模型在测试集上的准确率为 : ", correct/(gConfig['percent']*gConfig['dataset_size']/100)) + + sys.stdout.flush() + +def init_session(sess,conf='config.ini'): + global gConfig + gConfig=getConfig.get_config(conf) + model,graph=create_model(sess,True) + return sess, model,graph + +def predict_line(sess,model,img,graph): + predict_name=model.step(sess,img,img,graph,True) + return predict_name + +if __name__=='__main__': + if len(sys.argv) - 1: + gConfig = getConfig(sys.argv[1]) + else: + # get configuration from config.ini + gConfig = getConfig.get_config() + if gConfig['mode']=='train': + train() + elif gConfig['mode']=='server': + print('Sever Usage:python3 app.py') \ No newline at end of file diff --git a/lessonSix/vgg16/getConfig.py b/lessonSix/vgg16/getConfig.py new file mode 100644 index 0000000..4421059 --- /dev/null +++ b/lessonSix/vgg16/getConfig.py @@ -0,0 +1,9 @@ +import configparser +def get_config(config_file='config.ini'): + parser=configparser.ConfigParser() + parser.read(config_file) + # get the ints, floats and strings + _conf_ints = [(key, int(value)) for key, value in parser.items('ints')] + _conf_floats = [(key, float(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) \ No newline at end of file diff --git a/lessonSix/vgg16/predict_img/airplane.png b/lessonSix/vgg16/predict_img/airplane.png new file mode 100755 index 0000000..8916132 Binary files /dev/null and b/lessonSix/vgg16/predict_img/airplane.png differ diff --git a/lessonSix/vgg16/predict_img/deer.png b/lessonSix/vgg16/predict_img/deer.png new file mode 100755 index 0000000..1e3af0f Binary files /dev/null and b/lessonSix/vgg16/predict_img/deer.png differ diff --git a/lessonSix/vgg16/predict_img/dog.png b/lessonSix/vgg16/predict_img/dog.png new file mode 100755 index 0000000..b234d55 Binary files /dev/null and b/lessonSix/vgg16/predict_img/dog.png differ diff --git a/lessonSix/vgg16/predict_img/im.png b/lessonSix/vgg16/predict_img/im.png new file mode 100755 index 0000000..0df1a2e Binary files /dev/null and b/lessonSix/vgg16/predict_img/im.png differ diff --git a/lessonSix/vgg16/predict_img/truck.png b/lessonSix/vgg16/predict_img/truck.png new file mode 100755 index 0000000..0df1a2e Binary files /dev/null and b/lessonSix/vgg16/predict_img/truck.png differ diff --git a/lessonSix/vgg16/static/project_styles.css b/lessonSix/vgg16/static/project_styles.css new file mode 100755 index 0000000..c109253 --- /dev/null +++ b/lessonSix/vgg16/static/project_styles.css @@ -0,0 +1,27 @@ +body{ +background-color: #c4d5f2; +} + +.error{ +font-size:60px; +} + +.error-msg{ +font-size:50px; +color: red; +} + +span{ +font-size: 40px; +color: red; +} + +h3{ +font-size: 30px; +color: blue; +} + +input{ +font-size: 30px; +color: green; +} diff --git a/lessonSix/vgg16/static/result.js b/lessonSix/vgg16/static/result.js new file mode 100755 index 0000000..6e2af05 --- /dev/null +++ b/lessonSix/vgg16/static/result.js @@ -0,0 +1,3 @@ +function show_alert(predicted_class){ +alert("图片类别预测结果为:"+predicted_class+"。") +} diff --git a/lessonSix/vgg16/templates/error.html b/lessonSix/vgg16/templates/error.html new file mode 100755 index 0000000..69b28e9 --- /dev/null +++ b/lessonSix/vgg16/templates/error.html @@ -0,0 +1,15 @@ + + +
+ + +