轉(zhuǎn)自:
https://www.cnblogs.com/andre-ma/p/8458172.html
【寫在前面】
用Tensorflow(TF)已實(shí)現(xiàn)好的卷積神經(jīng)網(wǎng)絡(luò)(CNN)模型來訓(xùn)練自己的數(shù)據(jù)集潮秘,驗(yàn)證目前較成熟模型在不同數(shù)據(jù)集上的準(zhǔn)確度,如Inception_V3, VGG16,Inception_resnet_v2等模型勺疼。本文驗(yàn)證Inception_resnet_v2基于菜場實(shí)拍數(shù)據(jù)的準(zhǔn)確性校赤,測試數(shù)據(jù)為芹菜讽膏、雞毛菜房揭、青菜栋豫,各類別樣本約600張箭阶,多個(gè)菜場拍攝虚茶,不同數(shù)據(jù)源戈鲁。
補(bǔ)充:自己當(dāng)初的計(jì)劃是用別人預(yù)訓(xùn)練好的模型來再訓(xùn)練自己的數(shù)據(jù)集已使可以完成新的分類任務(wù),但必須要修改代碼改網(wǎng)絡(luò)結(jié)構(gòu)嘹叫,并使用遷移學(xué)習(xí)(Fine-tune)
本文記錄了其間的工作過程 婆殿,? 相信也會(huì)有一些幫助的? : )
測試環(huán)境:Centos7.3-64位? python3.5.4(Anaconda)?
?目錄
一.準(zhǔn)備
1.安裝python
2.安裝tensorflow?
3.下載TF-slim圖像庫
4.準(zhǔn)備數(shù)據(jù)
5.下載模型
二.訓(xùn)練
1.讀入數(shù)據(jù)
2.構(gòu)建模型
3.開始訓(xùn)練
4.執(zhí)行腳本,訓(xùn)練自己的數(shù)據(jù)
5.可視化log
【問題】 tensorboard版本已更新罩扇,找不到對應(yīng)包
三.驗(yàn)證
四.測試
一.準(zhǔn)備
1.安裝python
推薦Anaconda婆芦,可創(chuàng)建虛擬環(huán)境,用conda命令易實(shí)現(xiàn)虛擬環(huán)境管理喂饥、包管理寞缝,安裝包時(shí)會(huì)查出所有依賴包并一共一鍵安裝, 鏈接:https://www.anaconda.com/download/
2.安裝tensorflow?
進(jìn)入當(dāng)下Anaconda的運(yùn)行環(huán)境仰泻,我安裝的是python2.7版荆陆,并創(chuàng)建3.5虛擬環(huán)境
conda create -n py35 python=3.5? ? 【py35是虛擬環(huán)境的名稱; 輸入y 安裝】
source activate py35? 【激活py35環(huán)境】
conda install tensorflow? 【安裝tensorflow-cpu版,有GPU可安裝cpu版】
3.下載TF-slim代碼庫
cd? $WORKSPACE? 【目錄跳轉(zhuǎn)到自己的工作目錄下】
git clone https://github.com/tensorflow/models/
4.準(zhǔn)備數(shù)據(jù)
對所有訓(xùn)練樣本按不同樣本類別存在不同文件夾下
zsy_train|---jimaocai
|---0.jpg
|---? ...|---qc|---qingcai
下面的代碼是為了生成list.txt 集侯, 把不同文件夾下的圖片和 數(shù)字label對應(yīng)起來
1import os 2class_names_to_ids = {'jimaocai': 0,'qc': 1,'qingcai': 2} 3data_dir ='flower_photos/' 4output_path ='list.txt' 5fd = open(output_path,'w') 6forclass_namein class_names_to_ids.keys(): 7images_list = os.listdir(data_dir + class_name) 8forimage_namein images_list: 9fd.write('{}/{} {}\n'.format(class_name, image_name, class_names_to_ids[class_name]))10fd.close()
為了方便后期查看label標(biāo)簽被啼,也可定義labels.txt
jimaocai
qc
qingcai
隨機(jī)生成訓(xùn)練集和驗(yàn)證集(在總量中隨機(jī)選取350個(gè)樣本作為驗(yàn)證集)
1import random 2_NUM_VALIDATION = 350 3_RANDOM_SEED = 0 4list_path ='list.txt' 5train_list_path ='list_train.txt' 6val_list_path ='list_val.txt' 7fd = open(list_path) 8lines = fd.readlines() 9fd.close()10random.seed(_RANDOM_SEED)11random.shuffle(lines)12fd = open(train_list_path,'w')13forlinein lines[_NUM_VALIDATION:]:14? ? fd.write(line)15fd.close()16fd = open(val_list_path,'w')17forlinein lines[:_NUM_VALIDATION]:18? ? fd.write(line)19fd.close()
生成TFRecord數(shù)據(jù)
import sys# sys.path.insert(0, '../models/slim/')? models-master researchsys.path.insert(0,'./models/research/slim/')#把后面的路徑插入到系統(tǒng)路徑中 idx=0fromdatasetsimport dataset_utilsimport mathimport osimport tensorflow as tf#? 根據(jù)list路徑? 把數(shù)據(jù)轉(zhuǎn)化為TFRecord# def convert_dataset(list_path, data_dir, output_dir, _NUM_SHARDS=5):? defconvert_dataset(list_path, data_dir, output_dir, _NUM_SHARDS=3):? ? ?
? ? fd = open(list_path)
? ? lines = [line.split()forlinein fd]
? ? fd.close()
? ? num_per_shard = int(math.ceil(len(lines) / float(_NUM_SHARDS)))
? ? with tf.Graph().as_default():
? ? ? ? decode_jpeg_data = tf.placeholder(dtype=tf.string)
? ? ? ? decode_jpeg = tf.image.decode_jpeg(decode_jpeg_data, channels=3)
? ? ? ? with tf.Session('') as sess:
? ? ? ? ? ? forshard_idin range(_NUM_SHARDS):
? ? ? ? ? ? ? ? output_path = os.path.join(output_dir,#? ? ? ? ? ? ? ? ? ? 'data_{:05}-of-{:05}.tfrecord'.format(shard_id, _NUM_SHARDS))'data_{:03}-of-{:03}.tfrecord'.format(shard_id, _NUM_SHARDS))
? ? ? ? ? ? ? ? tfrecord_writer = tf.python_io.TFRecordWriter(output_path)
? ? ? ? ? ? ? ? start_ndx = shard_id * num_per_shard
? ? ? ? ? ? ? ? end_ndx = min((shard_id + 1) * num_per_shard, len(lines))
? ? ? ? ? ? ? ? foriin range(start_ndx, end_ndx):
? ? ? ? ? ? ? ? ? ? sys.stdout.write('\r>> Converting image {}/{} shard {}'.format(
? ? ? ? ? ? ? ? ? ? ? ? i + 1, len(lines), shard_id))
? ? ? ? ? ? ? ? ? ? sys.stdout.flush()
? ? ? ? ? ? ? ? ? ? image_data = tf.gfile.FastGFile(os.path.join(data_dir, lines[i][0]),'rb').read()
? ? ? ? ? ? ? ? ? ? image = sess.run(decode_jpeg, feed_dict={decode_jpeg_data: image_data})
? ? ? ? ? ? ? ? ? ? height, width = image.shape[0], image.shape[1]
? ? ? ? ? ? ? ? ? ? example = dataset_utils.image_to_tfexample(
? ? ? ? ? ? ? ? ? ? ? ? image_data, b'jpg', height, width, int(lines[i][1]))
? ? ? ? ? ? ? ? ? ? tfrecord_writer.write(example.SerializeToString())
? ? ? ? ? ? ? ? tfrecord_writer.close()
? ? sys.stdout.write('\n')
? ? sys.stdout.flush()
os.system('mkdir -p train')
convert_dataset('list_train.txt','zsy_train','train/')
os.system('mkdir -p val')
convert_dataset('list_val.txt','zsy_train','val/')
得到的文件夾結(jié)構(gòu)如下
WORKSPACE
├── zsy_train
├── labels.txt
├── list_train.txt
├── list.txt
├── list_val.txt
├── train
│? ├── data_000-of-003.tfrecord
│? ├── ...
│? └── data_002-of-003.tfrecord
└── val
? ? ├── data_000-of-003.tfrecord
? ? ├── ...
? ? └── data_002-of-003.tfrecord
5.下載模型
官方提供了預(yù)訓(xùn)練,這里以Inception-ResNet-v2以例
cd $WORKSPACE/checkpointswgethttp://download.tensorflow.org/models/inception_resnet_v2_2016_08_30.tar.gztarzxf inception_resnet_v2_2016_08_30.tar.gz
二.訓(xùn)練
1.讀入數(shù)據(jù)
讀入自己的數(shù)據(jù)棠枉,需要把下面代碼寫入models/slim/datasets/dataset_classification.py
import osimport tensorflow as tf
slim = tf.contrib.slimdefget_dataset(dataset_dir, num_samples, num_classes, labels_to_names_path=None, file_pattern='*.tfrecord'):
? ? file_pattern = os.path.join(dataset_dir, file_pattern)
? ? keys_to_features = {
? ? ? ? 'image/encoded': tf.FixedLenFeature((), tf.string, default_value=''),
? ? ? ? 'image/format': tf.FixedLenFeature((), tf.string, default_value='jpg'),
? ? ? ? 'image/class/label': tf.FixedLenFeature(
? ? ? ? ? ? [], tf.int64, default_value=tf.zeros([], dtype=tf.int64)),
? ? }
? ? items_to_handlers = {
? ? ? ? 'image': slim.tfexample_decoder.Image(),
? ? ? ? 'label': slim.tfexample_decoder.Tensor('image/class/label'),
? ? }
? ? decoder = slim.tfexample_decoder.TFExampleDecoder(keys_to_features, items_to_handlers)
? ? items_to_descriptions = {
? ? ? ? 'image':'A color image of varying size.',
? ? ? ? 'label':'A single integer between 0 and '+ str(num_classes - 1),
? ? }
? ? labels_to_names = None
? ? iflabels_to_names_pathisnot None:
? ? ? ? fd = open(labels_to_names_path)
? ? ? ? labels_to_names = {i : line.strip()fori, linein enumerate(fd)}
? ? ? ? fd.close()
? ? return slim.dataset.Dataset(
? ? ? ? ? ? data_sources=file_pattern,
? ? ? ? ? ? reader=tf.TFRecordReader,
? ? ? ? ? ? decoder=decoder,
? ? ? ? ? ? num_samples=num_samples,
? ? ? ? ? ? items_to_descriptions=items_to_descriptions,
? ? ? ? ? ? num_classes=num_classes,
? ? ? ? ? ? labels_to_names=labels_to_names)
2.構(gòu)建模型
構(gòu)建模型取決于個(gè)人欲構(gòu)建什么樣的模型浓体,官方都有對應(yīng)模型的下載鏈接,只需把對應(yīng)下載(下載鏈接:https://github.com/tensorflow/models/tree/master/research/slim)好的模型解壓放入到checkpoints中即可
3.開始訓(xùn)練
由于是用已有模型訓(xùn)練自己的數(shù)據(jù)集辈讶,故需對原工程代碼做適當(dāng)調(diào)整命浴。
把
fromdatasetsimportdataset_factory
改為:
fromdatasetsimportdataset_classification
把
dataset = dataset_factory.get_dataset(
? ? FLAGS.dataset_name, FLAGS.dataset_split_name, FLAGS.dataset_dir)
改為:
dataset = dataset_classification.get_dataset(
? ? FLAGS.dataset_dir, FLAGS.num_samples, FLAGS.num_classes, FLAGS.labels_to_names_path)
在
tf.app.flags.DEFINE_string(
? ? 'dataset_dir', None,'The directory where the dataset files are stored.')
后加入:
tf.app.flags.DEFINE_integer(
? ? 'num_samples', 1781,'Number of samples.')
tf.app.flags.DEFINE_integer(
? ? 'num_classes', 3,'Number of classes.')
tf.app.flags.DEFINE_string(
? ? 'labels_to_names_path', None,'Label names file path.')
?4.執(zhí)行腳本,訓(xùn)練自己的數(shù)據(jù)
cd $WORKSPACE/models/slim? ? #跳轉(zhuǎn)到工作環(huán)境目錄
python train_image_classifier.py \? ? #運(yùn)行腳本贱除,后面跟的系統(tǒng)參數(shù)
? ? --train_dir=/root/workspace_mrt/model_lab/train_logs \? #train_log目錄生闲,當(dāng)模型訓(xùn)練時(shí),可用tensorboard命令指定該目錄月幌,動(dòng)態(tài)監(jiān)測
? ? --dataset_dir=../../../train \? ? #訓(xùn)練數(shù)據(jù)集? 里面是轉(zhuǎn)換好的TFRecord格式
? ? --num_samples=1781 \? ? #訓(xùn)練樣本數(shù)碍讯,即值train_set中的總樣本數(shù),不包括valid中隨機(jī)抽取350個(gè)樣本
? ? --num_classes=3 \ #樣本類別數(shù)
? ? --labels_to_names_path=../../../labels.txt \?
? ? --model_name=inception_resnet_v2 \
? ? --checkpoint_path=../../../checkpoints/inception_resnet_v2_2016_08_30.ckpt \? ? #指定模型位置
? ? --checkpoint_exclude_scopes=InceptionResnetV2/Logits,InceptionResnetV2/AuxLogits \
? ? --trainable_scopes=InceptionResnetV2/Logits,InceptionResnetV2/AuxLogits \
? ? --clone_on_cpu=True #cpu訓(xùn)練必須加上該參數(shù)
#fine-tune要把 --checkpoint_path,--checkpoint_exclude_scopes扯躺,--trainable_scopes 加上
5.可視化log
為了可視化訓(xùn)練時(shí)的loss或其他指標(biāo)捉兴,可用tensorboard,如下命令
tensorboard --logdir=${TRAIN_DIR}
在本教程中录语,對應(yīng)執(zhí)行下面命令
tensorboard --logdir=/root/workspace_mrt/model_lab/train_logs
【問題】 tensorboard版本已更新倍啥,找不到對應(yīng)包
當(dāng)執(zhí)行
tensorboard --logdir=/root/workspace_mrt/model_lab/train_logs
時(shí),得到如下錯(cuò)誤
ImportError: No module named'tensorflow.tensorboard.tensorboard'
究其原因澎埠,是因?yàn)樵趖ensorflow更新時(shí)虽缕,包的位置和所屬關(guān)系改變了。執(zhí)行以下代碼失暂,可解決該問題彼宠。
cd /root/anaconda2/envs/py35/bin? ? #跳轉(zhuǎn)到對應(yīng)python環(huán)境的bin目錄下鳄虱,修改tensorboard執(zhí)行腳本代碼弟塞,使之適應(yīng)當(dāng)前版本
vim tensorboard
把
import tensorflow.tensorboard.tensorboard
修改為:
import tensorboard.main
把
sys.exit(tensorflow.tensorboard.tensorboard.main())
修改為:sys.exit(tensorboard.main.main())
wq保存凭峡,退出,重新執(zhí)行
tensorboard --logdir=/root/workspace_mrt/model_lab/train_logs
命令决记,無報(bào)錯(cuò)摧冀。根據(jù)日志提示,進(jìn)入ip:6006進(jìn)入tensorboard界面系宫。
三.驗(yàn)證
使用自己的數(shù)據(jù)集索昂,需修改models/slim/eval_image_classifier.py
把
from?datasets?import?dataset_factory
改為:
from?datasets?import?dataset_classification
把
dataset = dataset_factory.get_dataset( FLAGS.dataset_name, FLAGS.dataset_split_name, FLAGS.dataset_dir)
改為:
dataset = dataset_classification.get_dataset(
? ? FLAGS.dataset_dir, FLAGS.num_samples, FLAGS.num_classes, FLAGS.labels_to_names_path)
在
tf.app.flags.DEFINE_string(
? ? 'dataset_dir', None,'The directory where the dataset files are stored.')
后加入
tf.app.flags.DEFINE_integer(
? ? 'num_samples', 350,'Number of samples.')
tf.app.flags.DEFINE_integer(
? ? 'num_classes', 3,'Number of classes.')
tf.app.flags.DEFINE_string(
? ? 'labels_to_names_path', None,'Label names file path.')
驗(yàn)證時(shí)執(zhí)行以下命令即可:
python eval_image_classifier.py \
? ? --checkpoint_path=../../../checkpoints/inception_resnet_v2_2016_08_30.ckpt \
? ? --eval_dir=/root/workspace_mrt/model_lab/eval_logs \
? ? --dataset_dir=../../../val \
? ? --num_samples=350 \
? ? --num_classes=3 \
? ? --model_name=inception_resnet_v2
可以一邊訓(xùn)練一邊驗(yàn)證,注意使用其它的GPU或合理分配顯存扩借。
同樣也可以可視化log椒惨,如果已經(jīng)在可視化訓(xùn)練的log則建議使用其它端口,如:
tensorboard --logdir ../../../eval_logs/ --port 6007
四.測試
參考models/slim/eval_image_classifier.py潮罪,可編寫批量讀取圖片用模型進(jìn)行推導(dǎo)的腳本models/slim/test_image_classifier.py
from__future__import absolute_importfrom__future__import divisionfrom__future__import print_functionimport osimport jsonimport mathimport timeimport numpy as npimport tensorflow as tffromnetsimport nets_factoryfrompreprocessingimport preprocessing_factory
slim = tf.contrib.slim
tf.app.flags.DEFINE_string(
? ? 'master','','The address of the TensorFlow master to use.')
tf.app.flags.DEFINE_string(
? ? 'checkpoint_path', None,
? ? 'The directory where the model was written to or an absolute path to a ''checkpoint file.')
tf.app.flags.DEFINE_string(
? ? 'test_list','','Test image list.')
tf.app.flags.DEFINE_string(
? ? 'test_dir','.','Test image directory.')
tf.app.flags.DEFINE_integer(
? ? 'batch_size', 16,'Batch size.')
tf.app.flags.DEFINE_integer(
? ? 'num_classes', 3,'Number of classes.')
tf.app.flags.DEFINE_integer(
? ? 'labels_offset', 0,
? ? 'An offset for the labels in the dataset. This flag is primarily used to ''evaluate the VGG and ResNet architectures which do not use a background ''class for the ImageNet dataset.')
tf.app.flags.DEFINE_string(
? ? 'model_name','inception_resnet_v2','The name of the architecture to evaluate.')
tf.app.flags.DEFINE_string(
? ? 'preprocessing_name', None,'The name of the preprocessing to use. If left ''as `None`, then the model_name flag is used.')
tf.app.flags.DEFINE_integer(
? ? 'test_image_size', None,'Eval image size')
FLAGS = tf.app.flags.FLAGSdef main(_):
? ? ifnot FLAGS.test_list:
? ? ? ? raiseValueError('You must supply the test list with --test_list')
? ? tf.logging.set_verbosity(tf.logging.INFO)
? ? with tf.Graph().as_default():
? ? ? ? tf_global_step = slim.get_or_create_global_step()
? ? ? ? ##################### Select the model #####################network_fn = nets_factory.get_network_fn(
? ? ? ? ? ? FLAGS.model_name,
? ? ? ? ? ? num_classes=(FLAGS.num_classes - FLAGS.labels_offset),
? ? ? ? ? ? is_training=False)
? ? ? ? ###################################### Select the preprocessing function ######################################preprocessing_name = FLAGS.preprocessing_nameor FLAGS.model_name
? ? ? ? image_preprocessing_fn = preprocessing_factory.get_preprocessing(
? ? ? ? ? ? preprocessing_name,
? ? ? ? ? ? is_training=False)
? ? ? ? test_image_size = FLAGS.test_image_sizeor network_fn.default_image_size
? ? ? ? if tf.gfile.IsDirectory(FLAGS.checkpoint_path):
? ? ? ? ? ? checkpoint_path = tf.train.latest_checkpoint(FLAGS.checkpoint_path)
? ? ? ? else:
? ? ? ? ? ? checkpoint_path = FLAGS.checkpoint_path
? ? ? ? batch_size = FLAGS.batch_size
? ? ? ? tensor_input = tf.placeholder(tf.float32, [None, test_image_size, test_image_size, 3])
? ? ? ? logits, _ = network_fn(tensor_input)
? ? ? ? logits = tf.nn.top_k(logits, 5)
? ? ? ? config = tf.ConfigProto()
? ? ? ? config.gpu_options.allow_growth = True
? ? ? ? test_ids = [line.strip()forlinein open(FLAGS.test_list)]
? ? ? ? tot = len(test_ids)
? ? ? ? results = list()
? ? ? ? with tf.Session(config=config) as sess:
? ? ? ? ? ? sess.run(tf.global_variables_initializer())
? ? ? ? ? ? saver = tf.train.Saver()
? ? ? ? ? ? saver.restore(sess, checkpoint_path)
? ? ? ? ? ? time_start = time.time()
? ? ? ? ? ? foridxin range(0, tot, batch_size):
? ? ? ? ? ? ? ? images = list()
? ? ? ? ? ? ? ? idx_end = min(tot, idx + batch_size)
? ? ? ? ? ? ? ? print(idx)
? ? ? ? ? ? ? ? foriin range(idx, idx_end):
? ? ? ? ? ? ? ? ? ? image_id = test_ids[i]
? ? ? ? ? ? ? ? ? ? test_path = os.path.join(FLAGS.test_dir, image_id)
? ? ? ? ? ? ? ? ? ? image = open(test_path,'rb').read()
? ? ? ? ? ? ? ? ? ? image = tf.image.decode_jpeg(image, channels=3)
? ? ? ? ? ? ? ? ? ? processed_image = image_preprocessing_fn(image, test_image_size, test_image_size)
? ? ? ? ? ? ? ? ? ? processed_image = sess.run(processed_image)
? ? ? ? ? ? ? ? ? ? images.append(processed_image)
? ? ? ? ? ? ? ? images = np.array(images)
? ? ? ? ? ? ? ? predictions = sess.run(logits, feed_dict = {tensor_input : images}).indices
? ? ? ? ? ? ? ? foriin range(idx, idx_end):
? ? ? ? ? ? ? ? ? ? print('{} {}'.format(image_id, predictions[i - idx].tolist())
? ? ? ? ? ? time_total = time.time() - time_start
? ? ? ? ? ? print('total time: {}, total images: {}, average time: {}'.format(
? ? ? ? ? ? ? ? time_total, len(test_ids), time_total / len(test_ids)))if__name__=='__main__':
? ? tf.app.run()
測試時(shí)執(zhí)行以下命令即可:
CUDA_VISIBLE_DEVICES="0" python test_image_classifier.py \
? ? --checkpoint_path=../../../train_logs/ \
? ? --test_list=../../../list_val.txt \
? ? --test_dir=../../../val \
? ? --batch_size=16 \
? ? --num_classes=3 \
? ? --model_name=inception_resnet_v2
【參考】
https://github.com/tensorflow/models/tree/master/research/slim
【總結(jié)】
1.使用Tensorflow預(yù)訓(xùn)練模型(TF-slim)可以快速地測試已有成熟模型在不同數(shù)據(jù)集上的效果康谆,且有利于數(shù)據(jù)代碼架構(gòu)和核心代碼
2.若要自己實(shí)現(xiàn)已有成熟的網(wǎng)絡(luò)模型,包括數(shù)據(jù)讀取嫉到、訓(xùn)練沃暗、驗(yàn)證、測試何恶,調(diào)優(yōu)孽锥,則可用Tensorflow底層API或基于TF的高級API(TensorLayer? TFLearn? TF-slim)實(shí)現(xiàn)。從目前了解情況看细层,TensorLayer更好用惜辑,在DeepLearning中遷移學(xué)習(xí)更是如此
3.TensorFlow是一個(gè)非常龐大的架構(gòu),新手學(xué)習(xí)成本太高且難以摸清方向疫赎,故此借助下載已有models韵丑,調(diào)整參數(shù)和少量代碼有助于進(jìn)一步認(rèn)識TensorFlow的實(shí)現(xiàn)原理,若想要更深入研究虚缎,手寫CNN撵彻,看源碼等都是不可避免的
4.當(dāng)多看TensorFlow源碼,遷移學(xué)習(xí)的一大利器实牡!
最后附上自己的命令:
記得修改test_image_classifier.py修改topK