1. 程式人生 > >Tensorflow視訊學習總結文件

Tensorflow視訊學習總結文件

      Tensorflow學習中,在學習了基礎知識和建立簡單模型後,發現可以利用已有函式retrain.py直接採取一個Inception V3架構模型訓練ImageNet影象和訓練新的頂層這個運用可以不需要基礎就運用起來,如果想認識retrain.py怎麼運作的,可以看原始碼或者文章:https://blog.csdn.net/daydayup_668819/article/details/68060483 

       首先可以看一下Inception的作用,推薦文章:https://blog.csdn.net/u010402786/article/details/52433324

,接下來分三個部分進行說明


一.Gpu版本tensorflow

        如果不用gpu運算的話,這一步也可以省略。

        首先安裝CUDA,網址如下:

https://developer.nvidia.com/cuda-downloads?target_os=Windows&target_arch=x86_64&target_version=81&target_type=exelocal

 

 

      安裝有分電腦系統的,而且得與

python的tensorflow相匹配,不匹配的話執行會有錯誤版本提示。我用的是windows下的,linux下安裝這個驅動似乎挺困難,不太建議。下載後得到安裝包,雙擊安裝,然後把CUDA安裝目錄下的bin和lib\x64新增到Path環境變數中。預設安裝目錄是在C:/Program Files/NVIDIA GPU Computing Toolkit/CUDA,

      接下來安裝cuDNN,對深度學習計算加速用的,網址為https://developer.nvidia.com/rdp/cudnn-download ,這個需要先註冊才能下載。

   

      cuDNN的版本需要與CUDA版本一致。下載後解壓,把壓縮包中bin,include,lib中的檔案分別拷貝到C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\v9.0 對應目錄下,把C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\v9.0\extras\CUPTI\libx64\cupti64拷貝到C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\v9.0\bin下。

        然後安裝tensorflow-gpu, pip uninstall tensorflow, pip install tensorflow-gpu。

 

二.利用已經建立好的模型進行微調得到自己的圖片分類

        使用這個微調技術只改變分類模型最後一層,前面的卷積層和池化層都固化了,訓練速度快,迭代的週期少,需要使用的圖片的資料量比較少。首先得下載tensorflow的檔案,連結為https://github.com/tensorflow/tensorflow我解壓完是tensorflow-master檔案,需要用到其中tensorflow-master\tensorflow\examples\image_retraining下的retrain.py檔案,最好新創一個資料夾建立批處理檔案retrain.bat,程式碼如下:

python3 D:/Tensorflow/tensorflow-master/tensorflow/examples/image_retraining\retrain.py ^
--bottleneck_dir bottleneck ^
--how_many_training_steps 10000 ^
--model_dir D:/Tensorflow/inception_model/ ^
--output_graph output_graph.pb ^
--output_labels output_labels.txt ^
--image_dir data/train/
pause

       其中python3根據情況得改成python,後面的路徑為retrain.py 的路徑,bottleneck檔案指對每張圖片計算一個值出來,那個值是使用已有模型計算的,5000為訓練次數,預設似乎是4000,但訓練到後面次數再多準確率也無法提升的了。需要自己先建立好bottleneck檔案,自動建立好一個pb圖檔案和一個txt檔案作為標籤檔案,data/train/中存放要訓練的圖片,如下建立:


       每個資料夾是一種分類,每種分類的圖片不能太少,至少也要30張左右,然後直接開retrain.bat執行即可,每訓練十次會打印出當前準確率和準確率的可信度。程式自動建立兩個檔案,一個pb檔案和一個txt檔案。


      之後便可以直接在程式碼中呼叫檔案進行分類即可。程式碼如下:

import tensorflow as tf
import os
import numpy as np
import re
from PIL import Image
import matplotlib.pyplot as plt
import pandas as pd
import pylab

'''
  對已經訓練好的模型進行微調
  得到的檔案進行呼叫
'''

save_path = 'C:/Users/lin/Desktop'

lines = tf.gfile.GFile('D:/Tensorflow/retrain/output_labels.txt').readlines()
uid_to_human = {}
#一行一行讀取
for uid,line in enumerate(lines):
    line = line.strip('\n')
    uid_to_human[uid] = line

def id_to_string(node_id):
    if node_id not in uid_to_human:
        return ''
    return uid_to_human[node_id]

#建立一個圖來存放google訓練好的模型
with tf.gfile.FastGFile('D:/Tensorflow/retrain/output_graph.pb', 'rb') as f:
    graph_def = tf.GraphDef()
    graph_def.ParseFromString(f.read())
    tf.import_graph_def(graph_def, name='')

with tf.Session() as sess:
    softmax_tensor = sess.graph.get_tensor_by_name('final_result:0')
    #遍歷目錄
    i = 1000
    for root,dirs,files in os.walk('D:/Tensorflow/retrain/image'):
        for file in files:
            # 載入圖片
            image_data = tf.gfile.FastGFile(os.path.join(root, file), 'rb').read() #讀,read()必下,以rb開啟,因為預設是utf-8解碼圖片,改為rb就好
            predictions = sess.run(softmax_tensor,{'DecodeJpeg/contents:0':image_data})#圖片格式jpg
            predictions = np.squeeze(predictions) #吧結果轉為1維資料

            #列印圖片路徑和名稱
            image_path = os.path.join(root, file)
            print(image_path)
            #排序
            top_k = predictions.argsort()[-5:][::-1] #列印後面五個,即概率最大的
            # for node_id in top_k:
            #     #獲取分類名稱
            #     humam_string = id_to_string(node_id)
            #     #獲取該分類的置信度
            #     # print(node_id)
            #     score = predictions[node_id]
            #     print('%s (score = %.5f)' % (humam_string, score))
            # print()
            # 顯示圖片
            img = Image.open(image_path)
            # img = np.array(img)
            # plt.imshow(img, cmap = 'gray')
            # plt.axis('off')
            # plt.show()
            paths = '%s/%s.jpg' % (top_k[0], i)
            i += 1
            img.save(os.path.join(save_path,paths))


三.從頭開始訓練新的模型

       從頭訓練,迭代週期長,需要資料量大,可能需要幾十萬到幾百萬張圖片進行訓練,才能有個好的結果。圖片太少會有嚴重的過擬合現象。首先,https://github.com/tensorflow/models 下載,其中有個slim模型,可以將其放在D:/Tensorflow/ 下,要從頭訓練圖片模型的話,首先要對圖片進行處理,將圖片轉化為tfrecord格式,底層為protobuf格式,後面需要呼叫到這些檔案進行訓練、生成tfrecord檔案如下:

import tensorflow as tf
import os
import random
import math
import sys

#驗證集數量
_NUM_TEST = 50
#隨機種子
_RANDOM_SEED = 0
#資料塊
_NUM_SHARDS = 5
#資料集路徑
DATASET_DIR = 'D:/Tensorflow/slim/images/'
#標籤檔名字
LABELS_FILENAME = 'D:/Tensorflow/slim/images/labels.txt'

#protobuf資料儲存的格式,谷歌開源,速度快,圖片資料先轉為protobuf格式
#定義tfrecord檔案的路徑加名字
def _get_dataset_filename(dataset_dir, split_name, shard_id):
    output_filename = 'image_%s_%05d-of-%05d.tfrecord' % (split_name,shard_id, _NUM_SHARDS)
    return os.path.join(dataset_dir, output_filename)

#判斷tfrecord檔案是否存在
def _dataset_exists(dataset_dir):
    for split_name in ['train', 'test']:
        for shard_id in range(_NUM_SHARDS):
            #定義tfrecord檔案的路徑+名字
            output_filename = _get_dataset_filename(dataset_dir, split_name,shard_id)
        if not tf.gfile.Exists(output_filename):
            return False
        return True

#獲取所有檔案以及分類
def _get_filename_and_classes(dataset_dir):
    #資料目錄
    directories = []
    #分類名稱
    class_names = []
    for filename in os.listdir(dataset_dir):
        #合併檔案路徑
        path = os.path.join(dataset_dir,filename)
        #判斷該路徑是否為目錄
        if os.path.isdir(path):
            #加入資料目錄
            directories.append(path)
            #加入類別名稱
            class_names.append(filename)

    photo_filenames = []
    print(directories)
    #迴圈每個分類的資料夾
    for directory in directories:
        print(directory)
        for filename in os.listdir(directory):
            path = os.path.join(directory,filename)
            #把圖片加入圖片列表
            photo_filenames.append(path)

    return photo_filenames, class_names

def int64_feature(values):
    if not isinstance(values, (tuple, list)):
        values = [values]
    return tf.train.Feature(int64_list=tf.train.Int64List(value=values))

def bytes_feature(values):
    return tf.train.Feature(bytes_list=tf.train.BytesList(value=[values]))

def image_to_tfexample(image_data,image_format,class_id):
    #Abstract base class for protocol message.
    return tf.train.Example(features=tf.train.Features(feature={
        'image/encoded':bytes_feature(image_data),
        'image/format':bytes_feature(image_format),
        'image/class/label':int64_feature(class_id),
    }))

def write_label_file(labels_to_class_names, dataset_dir,filename=LABELS_FILENAME):
    labels_filename = os.path.join(dataset_dir,filename)
    with tf.gfile.Open(labels_filename, 'w') as f:
        for label in labels_to_class_names:
            class_name = labels_to_class_names[label]
            f.write('%d:%s\n' % (label, class_name))

#把資料轉為TFRecord格式
def _convert_dataset(split_name, filenames, class_names_to_ids, dataset_dir):
    assert split_name in ['train', 'test']
    #計算每個資料塊有多少個數據
    #資料量大才用切分成多個tfrecord檔案
    num_per_shard = int(len(filenames) / _NUM_SHARDS)
    with tf.Graph().as_default():
        with tf.Session() as sess:
            for shard_id in range(_NUM_SHARDS):
                #定義tfrecord檔案的路徑+名字
                output_filename = _get_dataset_filename(dataset_dir,split_name,shard_id)
                with tf.python_io.TFRecordWriter(output_filename) as tfrecord_writer:  #固定套路
                    #每一個數據塊開始的位置
                    start_ndx = shard_id * num_per_shard
                    #每一個數據塊最後的位置
                    end_ndx = min((shard_id+1) * num_per_shard, len(filenames))
                    for i in range(start_ndx, end_ndx):
                        try:
                            sys.stdout.write('\r>>Converting image %d/%d shard %d'% (i+1,len(filenames),shard_id))
                            sys.stdout.flush()
                            #讀取圖片
                            image_data = tf.gfile.FastGFile(filenames[i],'rb').read()
                            #獲得圖片的類別名稱
                            class_name = os.path.basename(os.path.dirname(filenames[i]))
                            #找到類別對應的id
                            class_id = class_names_to_ids[class_name]
                            #生成tfrecord檔案
                            example = image_to_tfexample(image_data,b'.jpg',class_id)
                            tfrecord_writer.write(example.SerializeToString())
                        except IOError as e:
                            print('Could not read: ',filenames[i])
                            print("Error: ",e)
                            print("Skip it\n")
    sys.stdout.write('\n')
    sys.stdout.flush()
if __name__ == '__main__':
    #判斷tfrecode檔案是否存在
    if _dataset_exists(DATASET_DIR):
        print('tfrecord檔案以存在')
    else:
        #獲得圖以及分類
        photo_filenames, class_names = _get_filename_and_classes(DATASET_DIR)
        #把分類轉為字典模式,類似於{'house':3, 'flower':1}
        class_names_to_ids = dict(zip(class_names,range(len(class_names))))

        #吧資料切分為訓練集和測試集
        random.seed(_RANDOM_SEED)
        random.shuffle(photo_filenames) #打亂
        training_filenames = photo_filenames[_NUM_TEST:]
        testing_filenames = photo_filenames[:_NUM_TEST] #0-500的存在測試集裡

        #資料轉換
        _convert_dataset('train',training_filenames,class_names_to_ids,DATASET_DIR)
        _convert_dataset('test',testing_filenames,class_names_to_ids,DATASET_DIR)

        #輸出labels檔案
        labels_to_class_names = dict(zip(range(len(class_names)),class_names))
        write_label_file(labels_to_class_names, DATASET_DIR)

       接下來首先看到slim/datasets中dataset_factory.py,在其datasets_map中新增”’myimages : myimages,’”,在其前新增import myimages ,接下來新建myimages.py,程式碼如下,根據其他檔案修改:

# Copyright 2016 The TensorFlow Authors. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# ==============================================================================
"""Provides data for the flowers dataset.

The dataset scripts used to create the dataset can be found at:
tensorflow/models/research/slim/datasets/download_and_convert_flowers.py
"""

from __future__ import absolute_import
from __future__ import division
from __future__ import print_function

import os
import tensorflow as tf

from datasets import dataset_utils

slim = tf.contrib.slim

_FILE_PATTERN = 'image_%s_*.tfrecord'

SPLITS_TO_SIZES = {'train': 250, 'validation': 50} #可根據實際修改

_NUM_CLASSES = 10 #多少分類,可根據實際修改

_ITEMS_TO_DESCRIPTIONS = {
    'image': 'A color image of varying size.',
    'label': 'A single integer between 0 and 4',
}


def get_split(split_name, dataset_dir, file_pattern=None, reader=None):
  """Gets a dataset tuple with instructions for reading flowers.

  Args:
    split_name: A train/validation split name.
    dataset_dir: The base directory of the dataset sources.
    file_pattern: The file pattern to use when matching the dataset sources.
      It is assumed that the pattern contains a '%s' string so that the split
      name can be inserted.
    reader: The TensorFlow reader type.

  Returns:
    A `Dataset` namedtuple.

  Raises:
    ValueError: if `split_name` is not a valid train/validation split.
  """
  if split_name not in SPLITS_TO_SIZES:
    raise ValueError('split name %s was not recognized.' % split_name)

  if not file_pattern:
    file_pattern = _FILE_PATTERN
  file_pattern = os.path.join(dataset_dir, file_pattern % split_name)

  # Allowing None in the signature so that dataset_factory can use the default.
  if reader is None:
    reader = tf.TFRecordReader

  keys_to_features = {
      'image/encoded': tf.FixedLenFeature((), tf.string, default_value=''),
      'image/format': tf.FixedLenFeature((), tf.string, default_value='png'),
      '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)

  labels_to_names = None
  if dataset_utils.has_labels(dataset_dir):
    labels_to_names = dataset_utils.read_label_file(dataset_dir)

  return slim.dataset.Dataset(
      data_sources=file_pattern,
      reader=reader,
      decoder=decoder,
      num_samples=SPLITS_TO_SIZES[split_name],
      items_to_descriptions=_ITEMS_TO_DESCRIPTIONS,
      num_classes=_NUM_CLASSES,
      labels_to_names=labels_to_names)

       接下來在slim資料夾中建立批處理檔案train.bat,如下:

python3 D:/Tensorflow/slim/train_image_classifier.py ^
--train_dir=D:/Tensorflow/slim/model ^
--dataset_name=myimages ^
--dataset_split_name=train ^
--dataset_dir=D:/Tensorflow/slim/images ^
--batch_size=10^
--max_number_of_steps=1000 ^
--model_name=inception_v3 ^
pause

       其中第一行執行slim資料夾中的train_image_classifier.py檔案,train_dir為模型儲存位置,先定義好model資料夾,dataset_name傳入檔名,batch_size為批次大小,預設值32,需根據gpu記憶體修改,max_number_of_steps需要給個最大訓練步數,否則會一直訓練。以上引數在train_image_classifier.py中均有描述,可以原始碼看看。

       這個運算需要花費很長時間,僅建議有很大資料,有需要且條件好的去嘗試。