赞
踩
有一组照片,每个文件夹的名称为具体的年龄,里面放的是该年纪的人物图片。
微调 TF-Hub 库,让模型学习这些样本,找到其中的规律,可以根据具体人物的图片来评估人物的年龄。
即便是通过人眼来观察他人的外表,也不能准确判断出被观察人的性别和年纪。所以在应用中,模型的准确度应该与用人眼的估计值来比对,并不能与被测目标的真实值来比对。
本实例所用的样本来自于 IMDB-WIKI 数据集。IMDB-WIKI 数据集中包含与年龄匹配应的人物图片。
因为该数据集相对粗糙(有些年纪对应的图片特别少),所以需要在该数据集的基础上做一些简单的调整:
整理后的图片一共有 105500 张。
读者可以直接使用本书配套的数据集,将该数据集(IMBD-WIKI 文件夹)放到当前代码的本地同级文件夹下即可使用。
安装 TF-Hub 库后,可以按照以下步骤进行操作。
在 GitHub 网站中找到 TF-Hub 库中所提供的模型及下载地址,具体网址如下(国内可能访问不了,请读者自行想办法): https://tfhub.dev/
打开该网页后,可以看到在列表中有很多模型及下载链接,如图 1 所示。
图 1 预训练模型列表
在图 1 可以分为 3 部分,具体如下:
因为本例需要图像方面的预训练模型,所以重点介绍左侧分类目录中 image 下的内容。在 image 分类下方还有 4 个子菜单,具体含义如下:
在图 1 中的搜索框里输入“mobilenet”并按 Enter 键,即可显示出与 MobileNet 相关的模型,如图 2 所示。
图 2 搜索 MobileNet 预训练模型
在图 2 右侧的列表部分,可以找到 MobileNet 模型。以 MobileNet_v2_100_224 模型为例(图 2 右侧列表中的最下方 2 行),该模型有两个版本:classification 与 feature_vector。
单击图 2 右侧列表中的最后下面一行,进入 MobileNet_v2_100_224 模型 classification 版本的详细说明页面,如图 3 所示。
图 3 NASNet_Mobile 模型 feature_vector 版本的详细说明页
在如图 3 所示的页面中,可以看到该网页介绍了 MobileNet_v2_100_224 模型的来源、训练、使用、微调,以及历史日志等方面的内容。在页面的右上角有一个“Copy URL”按钮,该按钮可以复制模型的下载,方便下载使用。
下载 TF-Hub 库中的模型方法有两种:自动下载和手动下载。
以 MobileNet_v2_100_224(简称 MobileNet_V2)模型的 classification 版本为例,手动下载的步骤如下。
(1)单击 5-8 中的“Copy URL”按钮,所得到的 URL 地址如下:
https://tfhub.dev/google/imagenet/mobilenet_v2_100_224/feature_vector/2
(2)将其改成正常下载的地址。具体如下:
https://storage.googleapis.com/tfhub-modules/google/imagenet/mobilenet_v2_100_224/feature_vector/2.tar.gz
(3)用下载工具按照(2)中的地址进行下载。
为了验证 TF-Hub 库中的模型效果,本小节将使用与第 3 章类似的代码:将 3 张图片输入 MobileNet_V2 模型的 classification 版本中,观察其输出结果。
编写代码载入 MobileNet_V2 模型,具体代码如下:
代码 1 测试 TF-Hub 库中的 NASNet_Mobile 模型
- from PIL import Image
- from matplotlib import pyplot as plt
- import numpy as np
- import tensorflow as tf
- import tensorflow_hub as hub
-
- with open('中文标签.csv','r+') as f: #打开文件
- labels =list( map(lambda x:x.replace(',',' '),list(f)) )
- print(len(labels),type(labels),labels[:5]) #显示输出中文标签
-
- sample_images = ['hy.jpg', 'ps.jpg','72.jpg'] #定义待测试图片路径
-
- #加载分类模型
- module_spec = hub.load_module_spec("https://tfhub.dev/google/imagenet/mobilenet_v2_100_224/classification/2")
- #获得模型的输入图片尺寸
- height, width = hub.get_expected_image_size(module_spec)
-
- input_imgs = tf.placeholder(tf.float32, [None, height,width,3])#定义占位符
- images = 2 *( input_imgs / 255.0)-1.0 #归一化图片
-
- module = hub.Module(module_spec) #将模型载入张量图
-
- logits = module(images) #获得输出张量,其形状为 [batch_size, num_classes]
-
- y = tf.argmax(logits,axis = 1) #获得结果的输出节点
- with tf.Session() as sess:
- sess.run(tf.global_variables_initializer())
- sess.run(tf.tables_initializer())
-
- def preimg(img): #定义图片预处理函数
- return np.asarray(img.resize((height, width)),
- dtype=np.float32).reshape(height, width,3)
-
- #获得原始图片与预处理图片
- batchImg = [ preimg( Image.open(imgfilename) ) for imgfilename in sample_images ]
- orgImg = [ Image.open(imgfilename) for imgfilename in sample_images ]
-
- #将样本输入模型
- yv,img_norm = sess.run([y,images], feed_dict={input_imgs: batchImg})
- print(yv,np.shape(yv)) #显示输出结果
- def showresult(yy,img_norm,img_org): #定义显示图片函数
- plt.figure()
- p1 = plt.subplot(121)
- p2 = plt.subplot(122)
- p1.imshow(img_org) #显示图片
- p1.axis('off')
- p1.set_title("organization image")
-
- p2.imshow((img_norm * 255).astype(np.uint8)) #显示图片
- p2.axis('off')
- p2.set_title("input image")
- plt.show()
-
- print(yy,labels[yy])
-
- for yy,img1,img2 in zip(yv,batchImg,orgImg): #显示每条结果及图片
- showresult(yy,img1,img2)
在代码第 14 行,用 TF-Hub 库中的 load_module_spec 函数加载 MobileNet_V2 模型。该步骤是通过将 TF-Hub 库中的模型链接(Module URL=“ https://tfhub.dev /google/imagenet/mobilenet_v2_100_224/classification/2”)传入函数 load_module_spec 中来完成的。
在链接里可以找到该模型文件的名字:mobilenet_v2_100_224。TF-Hub 库中的命名都非常规范,从名字上便可了解该模型的相关信息:
得到模型之后,便将模型文件载入图中(见代码第 21 行),并获得输出张量(见代码第 23 行),然后通过会话(session)完成模型的输出结果。
运行代码后,显示以下结果:
在显示的结果中,可以分为两部分内容:
TFHUB_CACHE_DIR=./my_module_cache
提示 1: 如果由于网络原因导致模型无法下载成功,还可以将本书的配套模型资源复制到当前代码同级目录下,并传入当前模型文件的路径。具体操作是,将代码第 14 行换为以下代码:
module_spec = hub.load_module_spec("mobilenet_v2_100_224")
提示 2:在最后一条的 INFO 信息之后便是模型的预测结果。
如果感觉输出的 INFO 内容太多,则可以在代码的最前面加上“tf.logging.set_verbosity (tf.logging.ERROR)”来关闭 info 信息输出。
在 TF-Hub 库的 GitHub 网站上提供了微调模型的代码文件,运行该代码可以直接微调现有模型。该文件的地址如下:
https://github.com/tensorflow/hub/raw/master/examples/image_retraining/retrain.py
将代码文件下载后,直接用命令行的方式运行,便可以对模型进行微调。
当前代码存在一个隐含的 BUG:在某一类的数据样本相对较少的情况下,运行时会产生错误。需要将其修改后才可以正常运行。
在“ retrain.py ”代码文件中的函数 get_random_cached_bottlenecks 里添加代码(见代码第 2 行,书中第 477 行),当程序在产生错误时,让其再去执行一次随机选取类别的操作(见代码第 15~25 行,书中第 515~525 行)。具体代码如下:
代码 retrain(片段)
- …
- def get_random_cached_bottlenecks(sess, image_lists, how_many, category,
- bottleneck_dir, image_dir, jpeg_data_tensor,
- decoded_image_tensor, resized_input_tensor,
- bottleneck_tensor, module_name):
- ……
- class_count = len(image_lists.keys())
- bottlenecks = []
- ground_truths = []
- filenames = []
- if how_many >= 0:
- # Retrieve a random sample of bottlenecks.
- for unused_i in range(how_many):
-
- IsErr = True #添加检测异常标志
- while IsErr==True: #如果出现异常就再运行一次
- try:
- label_index = random.randrange(class_count)
- label_name = list(image_lists.keys())[label_index]
- image_index = random.randrange(MAX_NUM_IMAGES_PER_CLASS + 1)
- image_name = get_image_path(image_lists, label_name, image_index,
- image_dir, category)
- IsErr = False #没有异常
- except ZeroDivisionError:
- continue #出现异常,再运行一次
- …
将代码文件“ retrain.py ”与 5.5.1 小节准备的样本数据、5.5.2 小节下载的 MobileNet_V2 模型文件一起放到当前代码的同级目录下。在命令行窗口中输入以下命令:
python retrain.py --image_dir ./IMBD-WIKI --tfhub_module mobilenet_v2_100_224_feature_vector
也可以输入以下命令,直接从网上下载 MobileNet_V2 模型,并进行微调。
python retrain.py --image_dir ./IMBD-WIKI --tfhub_module https://tfhub.dev/google/imagenet/mobilenet_v2_100_224/feature_vector/2
程序运行之后,会显示如图 5 所示界面。
图 5 微调 MobileNet_V2 模型结束
从图 5 中可以看到,生成的模型被放在默认路径下(根目录下的 tmp 文件夹里)。来到该路径下(作者本地的路径是“G:\tmp”),可以看到微调模型程序所生成的文件,如图 6 所示。
图 6 微调 MobileNet_V2 模型后生成的文件
在图 6 中可以看到有两个文件夹。
其他的文件是训练后生成的模型。每个模型文件的具体意义在第 6 章会有介绍。
提示:
本实例只是一个例子,重点在演示 TF-Hub 的使用。因为实例中所使用的数据集质量较低,所以训练效果并不是太理想。读者可以按照本实例的方法使用更优质的数据集训练出更好的模型。
代码文件“ retrain.py ”是一个很强大的训练脚本。在使用时,还可以通过修改参数实现更多的配置。
本实例只演示了部分参数的使用,其他的参数都用默认值,例如:迭代训练 4000 次,学习率为 0.01,批次大小为 100,训练集占比为 80%,测试集与验证集各占比 10% 等。
可以通过以下命令获得该脚本的全部参数说明。
python retrain.py -h
用代码文件“ retrain.py ”微调后的模型是以扩展名为“pb”的文件存在的(在图 6 中,第 2 行的左数第 1 个)。该模型文件属于冻结图文件。冻结图的知识在第 13 章会详细讲解。
将冻结图格式的模型载入内存,便可以人评估物的年纪。
冻结图文件中只有模型的具体参数。如果想使用它,则还需要知道与模型文件对应的输入和输出节点。
这两个节点都可以在代码文件“ retrain.py ”中找到。以输入节点为例,具体代码如下:
代码 retrain(片断)
- …
- def create_module_graph(module_spec):
- ……
- height, width = hub.get_expected_image_size(module_spec)
- with tf.Graph().as_default() as graph:
- resized_input_tensor = tf.placeholder(tf.float32, [None, height, width, 3])
- m = hub.Module(module_spec)
- bottleneck_tensor = m(resized_input_tensor)
- wants_quantization = any(node.op in FAKE_QUANT_OPS
- for node in graph.as_graph_def().node)
- return graph, bottleneck_tensor, resized_input_tensor, wants_quantization
- …
从代码文件“ retrain.py ”的第 6 行(书中第 305 行)代码可以看到,输入节点的张量是一个占位符——placeholder。
提示:
直接使用 print( placeholder.name ) 和 print(final_result.name) 两行代码即可将输入节点和输出节点的名称打印出来。
将输入节点和输出节点的名称记下来,填入代码文件“5-6 用微调后的 mobilenet_v2 模型评估人物的年龄.py”中,便可以实现模型的使用。
更多有关张量的介绍可以参考《深度学习之 TensorFlow——入门、原理与进阶实战》的 4.4.2 小节。
将本书的配套图片样例文件“22.jpg”和“tt2t.jpg”放到代码的同级目录下,用于测试模型。同时把生成的模型文件夹“tmp”也复制到本地代码的同级目录下。
这部分代码可以分为 3 部分。
完整的代码如下:
代码 2 用模型评估人物的年龄
- from PIL import Image
- from matplotlib import pyplot as plt
- import numpy as np
- import tensorflow as tf
-
- from sklearn.utils import shuffle
- import os
-
- def load_sample(sample_dir,shuffleflag = True):
- '''递归读取文件。只支持一级。返回文件名、数值标签、数值对应的标签名'''
- print ('loading sample dataset..')
- lfilenames = []
- labelsnames = []
- for (dirpath, dirnames, filenames) in os.walk(sample_dir):
- for filename in filenames: #遍历所有文件名
- #print(dirnames)
- filename_path = os.sep.join([dirpath, filename])
- lfilenames.append(filename_path) #添加文件名
- labelsnames.append( dirpath.split('\\')[-1] )#添加文件名对应的标签
-
- lab= list(sorted(set(labelsnames))) #生成标签名称列表
- labdict=dict( zip( lab ,list(range(len(lab))) )) #生成字典
-
- labels = [labdict[i] for i in labelsnames]
- if shuffleflag == True:
- return shuffle(np.asarray( lfilenames),np.asarray( labels)),np.asarray(lab)
- else:
- return (np.asarray( lfilenames),np.asarray( labels)),np.asarray(lab)
-
- #载入标签
- data_dir = 'IMBD-WIKI\\' #定义文件的路径
- _,labels = load_sample(data_dir,False) #载入文件的名称与标签
- print(labels) #输出 load_sample 返回的标签字符串
-
- sample_images = ['22.jpg', 'tt2t.jpg'] #定义待测试图片的路径
-
- tf.logging.set_verbosity(tf.logging.ERROR)
- tf.reset_default_graph()
- #分类模型
- thissavedir= 'tmp'
- PATH_TO_CKPT = thissavedir +'/output_graph.pb'
- od_graph_def = tf.GraphDef()
- with tf.gfile.GFile(PATH_TO_CKPT, 'rb') as fid:
- serialized_graph = fid.read()
- od_graph_def.ParseFromString(serialized_graph)
- tf.import_graph_def(od_graph_def, name='')
-
- fenlei_graph = tf.get_default_graph()
-
- height,width = 224,224
-
- with tf.Session(graph=fenlei_graph) as sess:
- result = fenlei_graph.get_tensor_by_name('final_result:0')
- input_imgs = fenlei_graph.get_tensor_by_name('Placeholder:0')
- y = tf.argmax(result,axis = 1)
-
- def preimg(img): #定义图片的预处理函数
- reimg = np.asarray(img.resize((height, width)),
- dtype=np.float32).reshape(height, width,3)
- normimg = 2 *( reimg / 255.0)-1.0
- return normimg
-
- #获得原始图片与预处理图片
- batchImg = [ preimg( Image.open(imgfilename) ) for imgfilename in sample_images ]
- orgImg = [ Image.open(imgfilename) for imgfilename in sample_images ]
-
- yv = sess.run(y, feed_dict={input_imgs: batchImg}) #输入模型
- print(yv)
-
- print(yv,np.shape(yv)) #显示输出结果
- def showresult(yy,img_norm,img_org): #定义显示图片的函数
- plt.figure()
- p1 = plt.subplot(121)
- p2 = plt.subplot(122)
- p1.imshow(img_org) #显示图片
- p1.axis('off')
- p1.set_title("organization image")
-
- img = ((img_norm+1)/2)*255
- p2.imshow( np.asarray(img,np.uint8) ) #显示图片
- p2.axis('off')
- p2.set_title("input image")
-
- plt.show()
-
- print(" 索引:",yy,","," 年纪:",labels[yy])
-
- for yy,img1,img2 in zip(yv,batchImg,orgImg): #显示每条结果及图片
- showresult(yy,img1,img2)
代码第 41 行,指定了要加载的模型动态图文件。
代码第 53 行,指定了与模型文件对应的输入节点“final_result:0”。
代码第 54 行,指定了与模型文件对应的输出节点“Placeholder:0”。
代码运行后显示以下结果:
输出结果可以分为两部分:
在第 2 部分中,每张图片的下面都会显示这个图片的评估结果,其中包括:在模型中的标签索引、该索引对应的标签名称。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。