深度有趣 | 09 Inception-v3圖片分類

簡介

Inception-v3是由Google提出,用於實現ImageNet大規模視覺識別任務(ImageNet Large Visual Recognition Challenge)的一種神經網絡

Inception模型結構

Inception-v3反覆使用了Inception Block,涉及大量的卷積和池化,而ImageNet包括1400多萬張圖片,類別數超過1000

因此手動在ImageNet上訓練Inception-v3,需要耗費大量的資源和時間

這裏我們選擇加載pre-trained的Inception-v3模型,來完成一些圖片分類任務

準備

預訓練好的模型共包括三個部分

  • classify_image_graph_def.pb:Inception-v3模型結構和參數
  • imagenet_2012_challenge_label_map_proto.pbtxt:從類別編號到類別字符串的對應關係
  • imagenet_synset_to_human_label_map.txt:從類別字符串到類別名的對應關係

例如,169對應n02510455,對應giant panda, panda, panda bear, coon bear, Ailuropoda melanoleuca

圖片分類

加載庫

# -*- coding: utf-8 -*-

import tensorflow as tf
import numpy as np

整理兩個映射文件,得到從類別編號到類別名的對應關係

uid_to_human = {}
for line in tf.gfile.GFile('imagenet_synset_to_human_label_map.txt').readlines():
	items = line.strip().split('\t')
	uid_to_human[items[0]] = items[1]

node_id_to_uid = {}
for line in tf.gfile.GFile('imagenet_2012_challenge_label_map_proto.pbtxt').readlines():
	if line.startswith('  target_class:'):
		target_class = int(line.split(': ')[1])
	if line.startswith('  target_class_string:'):
		target_class_string = line.split(': ')[1].strip('\n').strip('\"')
		node_id_to_uid[target_class] = target_class_string

node_id_to_name = {}
for key, value in node_id_to_uid.items():
	node_id_to_name[key] = uid_to_human[value]

加載模型

def create_graph():
	with tf.gfile.FastGFile('classify_image_graph_def.pb', 'rb') as f:
		graph_def = tf.GraphDef()
		graph_def.ParseFromString(f.read())
		_ = tf.import_graph_def(graph_def, name='')

定義一個分類圖片的函數

def classify_image(image, top_k=1):
	image_data = tf.gfile.FastGFile(image, 'rb').read()

	create_graph()

	with tf.Session() as sess:
		# 'softmax:0': A tensor containing the normalized prediction across 1000 labels
		# 'pool_3:0': A tensor containing the next-to-last layer containing 2048 float description of the image
		# 'DecodeJpeg/contents:0': A tensor containing a string providing JPEG encoding of the image
		softmax_tensor = sess.graph.get_tensor_by_name('softmax:0')
		predictions = sess.run(softmax_tensor, feed_dict={'DecodeJpeg/contents:0': image_data})
		predictions = np.squeeze(predictions)

		top_k = predictions.argsort()[-top_k:]
		for node_id in top_k:
			human_string = node_id_to_name[node_id]
			score = predictions[node_id]
			print('%s (score = %.5f)' % (human_string, score))

調用函數進行圖片分類,指定參數top_k可以返回最可能的多種分類結果

classify_image('test1.png')

分類結果如下

  • test1:giant panda, panda, panda bear, coon bear, Ailuropoda melanoleuca (score = 0.89107)
  • test2:Pekinese, Pekingese, Peke (score = 0.90348)
  • test3:Samoyed, Samoyede (score = 0.92054)

定製分類任務

Inception-v3是針對ImageNet圖片分類任務設計的,因此最後一層全連接層的神經元個數和分類標籤個數相同

如果需要定製分類任務,只需要使用自己的標註數據,然後替換掉最後一層全連接層即可

最後一層全連接層的神經元個數等於定製分類任務的標籤個數,模型只訓練最後一層的參數,其他參數保持不變

保留了Inception-v3對於圖像的理解和抽象能力,同時滿足定製的分類任務,屬於遷移學習的一種典型應用場景

TensorFlow官方提供瞭如何在Inception-v3上進行遷移學習的教程

https://www.tensorflow.org/tutorials/image_retraining

所使用的數據包括五種花的拍攝圖片

  • daisy:雛菊
  • dandelion:蒲公英
  • roses:玫瑰
  • sunflowers:向日葵
  • tulips:鬱金香

去掉最後一層全連接層後,對於一張圖片輸入,模型輸出的表示稱作Bottleneck

事先計算好全部圖片的Bottleneck並緩存下來,可以節省很多訓練時間,因爲後續只需計算和學習Bottleneck到輸出標籤之間的隱層即可

TensorFlow官方提供了重訓練的代碼

https://github.com/tensorflow/tensorflow/blob/master/tensorflow/examples/image_retraining/retrain.py

在命令行中使用,一些可選的命令行參數包括

  • --image_dir:訓練圖片目錄
  • --output_graph:模型保存目錄
  • --output_labels:模型標籤保存目錄
  • --summaries_dir:模型日誌保存目錄
  • --how_many_training_steps:訓練迭代次數,默認爲4000
  • --learning_rate:學習率,默認爲0.01
  • --testing_percentage:測試集比例,默認爲10%
  • --validation_percentage:校驗集比例,默認爲10%
  • --eval_step_interval:模型評估頻率,默認10次迭代評估一次
  • --train_batch_size:訓練批大小,默認爲100
  • --print_misclassified_test_images:是否輸出所有錯誤分類的測試集圖片,默認爲False
  • --model_dir:Inception-v3模型路徑
  • --bottleneck_dir:Bottleneck緩存目錄
  • --final_tensor_name:新增的最後一層全連接層的名稱,默認爲final_result
  • --flip_left_right:是否隨機將一半的圖片水平翻轉,默認爲False
  • --random_crop:隨機裁剪的比例,默認爲0即不裁剪
  • --random_scale:隨機放大的比例,默認爲0即不放大
  • --random_brightness:隨機增亮的比例,默認爲0即不增亮
  • --architecture:遷移的模型,默認爲inception_v3,準確率最高但訓練時間較長,還可以選'mobilenet_<parameter size>_<input_size>[_quantized]',例如mobilenet_1.0_224mobilenet_0.25_128_quantized

跑一下代碼

python retrain.py --image_dir flower_photos --output_graph output_graph.pb --output_labels output_labels.txt --summaries_dir summaries_dir --model_dir .. --bottleneck_dir bottleneck_dir

此處對於視頻中內容的勘誤

  • --output_graph之後的output_graph改爲output_graph.pb
  • --output_labels之後的output_labels改爲output_labels.txt

在校驗集、測試集上的分類準確率分別爲91%、91.2%

在我的筆記本上一共花了55分鐘,其中44分鐘花在了Bottleneck緩存上,但如果不緩存的話,訓練過程中每次迭代都必須重複計算一遍

summaries_dir目錄下的訓練日誌可用於TensorBorad可視化

tensorboard --logdir summaries_dir

然後在瀏覽器中訪問http://localhost:6006,即可看到可視化的效果,包括SCALARSGRAPHSDISTRIBUTIONSHISTOGRAMS四個頁面

TensorBoard可視化

如果需要完成其他圖片分類任務,整理相應的標註圖片,並以標籤名作爲子文件夾名稱即可

如果要使用訓練好的模型,參照以下代碼即可

  • output_labels.txt:分類類別文件路徑
  • output_graph.pb:訓練好的模型路徑
  • read_image():讀取圖片的函數
  • input_operation:圖片輸入對應的operation
  • output_operation:分類輸出對應的operation
  • test.jpg:待分類的圖片路徑
# -*- coding: utf-8 -*-

import tensorflow as tf
import numpy as np

labels = []
for line in tf.gfile.GFile('output_labels.txt').readlines():
	labels.append(line.strip())

def create_graph():
	graph = tf.Graph()
	graph_def = tf.GraphDef()
	with open('output_graph.pb', 'rb') as f:
		graph_def.ParseFromString(f.read())
	with graph.as_default():
		tf.import_graph_def(graph_def)
	return graph

def read_image(path, height=299, width=299, mean=128, std=128):
	file_reader = tf.read_file(path, 'file_reader')
	if path.endswith('.png'):
		image_reader = tf.image.decode_png(file_reader, channels=3, name='png_reader')
	elif path.endswith('.gif'):
		image_reader = tf.squeeze(tf.image.decode_gif(file_reader, name='gif_reader'))
	elif path.endswith('.bmp'):
		image_reader = tf.image.decode_bmp(file_reader, name='bmp_reader')
	else:
		image_reader = tf.image.decode_jpeg(file_reader, channels=3, name='jpeg_reader')
	image_np = tf.cast(image_reader, tf.float32)
	image_np = tf.expand_dims(image_np, 0)
	image_np = tf.image.resize_bilinear(image_np, [height, width])
	image_np = tf.divide(tf.subtract(image_np, [mean]), [std])
	sess = tf.Session()
	image_data = sess.run(image_np)
	return image_data

def classify_image(image, top_k=1):
	image_data = read_image(image)

	graph = create_graph()

	with tf.Session(graph=graph) as sess:
		input_operation = sess.graph.get_operation_by_name('import/Mul')
		output_operation = sess.graph.get_operation_by_name('import/final_result')
		predictions = sess.run(output_operation.outputs[0], feed_dict={input_operation.outputs[0]: image_data})
		predictions = np.squeeze(predictions)

		top_k = predictions.argsort()[-top_k:]
		for i in top_k:
			print('%s (score = %.5f)' % (labels[i], predictions[i]))

classify_image('test.jpg')

參考

視頻講解課程

深度有趣(一)

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章