Tensorflow-Inception-图像识别案例-整理翻译

人类大脑可以轻而易举的识别图像中的狮子和豹子,识别各种符号和文字,识别人脸,等等,但这些对于计算机来说就很难。因为我们的大脑尤其擅长图像识别。

题外,其实也并非完全如此,对于陌生的内容,我们并不能做的太好,比如你可能看过太多的日文字幕,但再看到屏幕上的日文符号的时候,你还记得它是否在《名侦探柯南》中出现过吗?同样,我们对于华人明星的脸几乎从不会搞混谁是谁,但对于欧美明不太知名的明星恐怕就经常分不清哪个是哪个。人类也经常被各种视觉错觉所欺骗,比如经常去看的3D电影,一块幕布竟然被我们看出了立体感,不觉得奇怪吗?这里面一定隐藏着什么一些未被重视的算法。

在过去的几年,计算机科学家在图像识别上取得了重大突破,尤其是CNN卷积神经网络的使用,计算机已经能够解决很多有难度的图像识别任务,甚至某些领域比人做的还好。

计算机视觉算法已经攻克了学院派的ImageNet测试标准,成功的模型不断进步: QuocNet, AlexNet, Inception (GoogLeNet), BN-Inception-v2,Google谷歌内部和外部的科学家正在探讨下一代图像识别模型,Inception-v3

Inception-v3是基于2012年以来ImageNet图片挑战赛数据训练得到的,这个挑战赛是计算机视觉的标杆性的比赛,需要识别1000种分类,比如斑马、斑点狗和洗碗机等,比如下面是AlexNet模型识别的一些分类:


螨虫,货船,踏板摩托,美洲豹;粉色条表示了最大可能的分类,都获得了正确的结果

为了对比模型,我们检查模型预测失败率最高的5个预测,AlexNet在2012年top5错误率是15.3%,Inception是6.67%,Inception-v2是4.9,Inception-v3是3.46%。而人类的top-5错误率是5.1%。

本篇教程将介绍如何使用Inception-v3识别1000种分类,也会介绍如何从这个模型种提取更高特征,以便于用其他识别任务。


准备工作

首先从百度云盘下载相关文件(密码:q3kf)

其中包含了saved_model和imgs两个文件夹

  • saved_model,这里是谷歌Inception项目基于众多图片训练好的模型,以及分类名称对照表数据(label_map_proto.pbtxt,nameid2name.txt)。这里是已保存的模型,而不是checkpoint文件。
  • imgs,只是一张将被识别的熊猫图片。

这次我将tensorflow升级到了1.7,推荐通过下面命令行升级

pip3 install --upgrade pip
pip3 install --upgrade tensorflow

代码结构

import os
import re #regular expressions正则表达式操作模块
import numpy as np
import tensorflow as tf

#准备目录
dir_path = os.path.dirname(os.path.realpath(__file__))
model_dir=os.path.join(dir_path,'saved_model')  
image_path=os.path.join(dir_path, 'imgs/panda.jpg')
proto_path=os.path.join(dir_path, 'saved_model/label_map_proto.pbtxt')
name_path=os.path.join(dir_path, 'saved_model/nameid2name.txt')
model_path=os.path.join(model_dir, 'classify_image_graph_def.pb')

#我们的主要代码将添加在这里

#入口函数
def main(_):    
    #在这里运行测试


#模块或应用
if __name__ == '__main__':
    tf.app.run()

加载分类名称对照表函数

首先看一下两个文件的内容:

分类类别编号(target_class即nodeid)和名称编号( target_class_string即nameid)的对应关系label_map_proto.pbtxt:

# -*- protobuffer -*-
# LabelMap from ImageNet 2012 full data set UID to int32 target class.
entry {
  target_class: 449
  target_class_string: "n01440764"
}
entry {
  target_class: 450
  target_class_string: "n01443537"
}

名称编号(n开头)和名称的对应关系nameid2name.txt:

n00004475   organism, being
n00005787   benthos
n00006024   heterotroph
n00006484   cell
n00007846   person, individual, someone, somebody, mortal, soul

我们向代码添加获取分类的函数get_class,并喂它创建两个方法:

  • load,用来读取两个文件,逐行读取,然后拆分,重组成为两个字典{nodeid:nameid}和{nameid:name},再把两个字典合并成为{nodeid:name}
  • get_name_by_nodeid,根据上面的字典,通过nodeid获取name。
    并在init里面自动使用load()方法读取。

在main里面运行并测试,增加和修改的部分代码如下:

#用来把分类节点nodeid转为物体名称字符串的函数
class get_class():
    def __init__(self):
        self.class_list=self.load() #载入,生成{nodeid:name}字典
       
    #从文本载入名称id映射数据
    def load(self):
        #读取nameid和name的对应关系
        nameid2name = {} #字典{nid:name}
        lines1 = tf.gfile.GFile(name_path).readlines()
        p = re.compile(r'[n\d]*[ \S,]*') #定义正则匹配方式
        for line in lines1:
            #findall将n00004475  organism, being分解成['n00004475','organism, being']
            parsed_items = p.findall(line) 
            nid = parsed_items[0]
            name = parsed_items[2]
            nameid2name[nid] = name
            
        #读取nodeid和nameid的对应关系  
        nodeid2nameid = {}
        lines2 = tf.gfile.GFile(proto_path).readlines()
        for line in lines2:
            #参考数据格式entry {
            #  target_class: 449
            #  target_class_string: "n01440764"
            #}
            if line.startswith('  target_class:'):
                target_class = int(line.split(': ')[1])
            if line.startswith('  target_class_string:'):
                target_class_string = line.split(': ')[1]
                nodeid2nameid[target_class] = target_class_string[1:-2]
                
        #合并成nodeid和name的对应关系
        nodeid2name = {}
        for key, val in nodeid2nameid.items():
            if val not in nameid2name:
                tf.logging.fatal('对应失败: %s', val)
            name = nameid2name[val]
            nodeid2name[key] = name
                
        print(nodeid2name) #这里打印结果!
        return nodeid2name
    
    #根据nodeid获取分类名
    def get_name_by_nodeid(self, nodeid):
        if nodeid not in self.class_list:
            return ''
        return self.class_list[nodeid]                

#入口函数
def main(_):    
    getclass = get_class()

运行将会打印出结果,编号对应了名称。

{449: 'tench, Tinca tinca', 450: 'goldfish, Carassius auratus', 442: 'great white shark, white shark, man-eater, man-eating shark, Carcharodon carcharias', 443: 'tiger shark,...

这个操作其实没什么意义,但是如果没有这个450到'goldfish, Carassius auratus'(金鱼)的转换,那么稍后预测出我们的图片是565,我们也没法知道这是什么鬼。


读取已保存的模型

我们使用tf.gfile来读取保存的pd文件model_path=os.path.join(model_dir, 'classify_image_graph_def.pb'),把内容恢复到tf.GraphDeftensorflow的计算图中。

下面是增加和修改部分的代码,运行后没有输出结果。

#从保存的模型读取graph
def create_graph():
    with tf.gfile.FastGFile(model_path, 'rb') as f:
        graph_def = tf.GraphDef()
        graph_def.ParseFromString(f.read())
        _ = tf.import_graph_def(graph_def, name='')

 #入口函数
def main(_):    
    create_graph()

对图片进行预测的函数

签名create_graph载入已保存模型创建了图,我们只要从graph中找出最终输出的张量softmax,然后利用feed_dict重新喂食我们的图片,就能run出预测结果。
当然别忘了用我们辛苦编写的get_class来把预测的nodeid改为可以读懂的名称再打印出来。

增加和修改部分的代码如下:

#执行预测
def predict_image():
    image_data = tf.gfile.FastGFile(image_path, 'rb').read() #读取图片
    create_graph() #创建计算图

    with tf.Session() as sess:
        softmax_tensor = sess.graph.get_tensor_by_name('softmax:0') #从计算图中提取张量
        predictions = sess.run(softmax_tensor,{'DecodeJpeg/contents:0': image_data}) #输入feed_dict进行运算
        predictions = np.squeeze(predictions) #去掉冗余的1维形状,比如把张量形状从(1,3,1)变为(3)

        #输出打印
        getclass = get_class()
        top5 = predictions.argsort()[-5:][::-1]
        
        print('\n预测结果是:')
        for node_id in top5:
            name_string = getclass.get_name_by_nodeid(node_id)
            score = predictions[node_id]
            print('%s (score = %.5f)' % (name_string, score))
        

#入口函数
def main(_):    
    predict_image()

这次运行就能预测大熊猫(giant panda)了!输出类似下面的结果,0.89107表示这个图片有89.107%的可能是大熊猫:

预测结果是:
giant panda, panda, panda bear, coon bear, Ailuropoda  (score = 0.89107)
indri, indris, Indri indri, Indri brevicaudatus (score = 0.00779)
lesser panda, red panda, panda, bear cat, cat bear, Ailurus fulgens (score = 0.00296)
custard apple (score = 0.00147)
earthstar (score = 0.00117)

结语

使用谷歌Inception项目训练好的模型来预测图片,其实关键代码很简单,就是载入模型,重新生成计算图,进行预测。但大篇幅的代码都是get_class来转换文字的。
下面是全部代码:

import os
import re #regular expressions正则表达式操作模块
import numpy as np
import tensorflow as tf

#准备目录
dir_path = os.path.dirname(os.path.realpath(__file__))
model_dir=os.path.join(dir_path,'saved_model')  
image_path=os.path.join(dir_path, 'imgs/panda.jpg')
proto_path=os.path.join(dir_path, 'saved_model/label_map_proto.pbtxt')
name_path=os.path.join(dir_path, 'saved_model/nameid2name.txt')
model_path=os.path.join(model_dir, 'classify_image_graph_def.pb')

#用来把分类节点nodeid转为物体名称字符串的函数
class get_class():
    def __init__(self):
        self.class_list=self.load() #载入,生成{nodeid:name}字典
       
    #从文本载入名称id映射数据
    def load(self):
        #读取nameid和name的对应关系
        nameid2name = {} #字典{nid:name}
        lines1 = tf.gfile.GFile(name_path).readlines()
        p = re.compile(r'[n\d]*[ \S,]*') #定义正则匹配方式
        for line in lines1:
            #findall将n00004475  organism, being分解成['n00004475','organism, being']
            parsed_items = p.findall(line) 
            nid = parsed_items[0]
            name = parsed_items[2]
            nameid2name[nid] = name
            
        #读取nodeid和nameid的对应关系  
        nodeid2nameid = {}
        lines2 = tf.gfile.GFile(proto_path).readlines()
        for line in lines2:
            #参考数据格式entry {
            #  target_class: 449
            #  target_class_string: "n01440764"
            #}
            if line.startswith('  target_class:'):
                target_class = int(line.split(': ')[1])
            if line.startswith('  target_class_string:'):
                target_class_string = line.split(': ')[1]
                nodeid2nameid[target_class] = target_class_string[1:-2]
                
        #合并成nodeid和name的对应关系
        nodeid2name = {}
        for key, val in nodeid2nameid.items():
            if val not in nameid2name:
                tf.logging.fatal('对应失败: %s', val)
            name = nameid2name[val]
            nodeid2name[key] = name
                
        return nodeid2name
    
    #根据nodeid获取分类名
    def get_name_by_nodeid(self, nodeid):
        if nodeid not in self.class_list:
            return ''
        return self.class_list[nodeid]                

#从保存的模型读取graph
def create_graph():
    with tf.gfile.FastGFile(model_path, 'rb') as f:
        graph_def = tf.GraphDef()
        graph_def.ParseFromString(f.read())
        _ = tf.import_graph_def(graph_def, name='')
        
#执行预测
def predict_image():
    image_data = tf.gfile.FastGFile(image_path, 'rb').read() #读取图片
    create_graph() #创建计算图

    with tf.Session() as sess:
        softmax_tensor = sess.graph.get_tensor_by_name('softmax:0') #从计算图中提取张量
        predictions = sess.run(softmax_tensor,{'DecodeJpeg/contents:0': image_data}) #输入feed_dict进行运算
        predictions = np.squeeze(predictions) #去掉冗余的1维形状,比如把张量形状从(1,3,1)变为(3)

        #输出打印
        getclass = get_class()
        top5 = predictions.argsort()[-5:][::-1]
        
        print('\n预测结果是:')
        for node_id in top5:
            name_string = getclass.get_name_by_nodeid(node_id)
            score = predictions[node_id]
            print('%s (score = %.5f)' % (name_string, score))
        
#入口函数
def main(_):    
    predict_image()

#模块或应用
if __name__ == '__main__':
    tf.app.run()

探索人工智能的新边界

如果您发现文章错误,请不吝留言指正;
如果您觉得有用,请点喜欢;
如果您觉得很有用,感谢转发~


END

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,761评论 5 460
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,953评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,998评论 0 320
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,248评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,130评论 4 356
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,145评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,550评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,236评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,510评论 1 291
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,601评论 2 310
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,376评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,247评论 3 313
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,613评论 3 299
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,911评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,191评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,532评论 2 342
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,739评论 2 335

推荐阅读更多精彩内容