本文咱們將使用tf.keras構建一個卷積神經網絡,用於識別森林衛星圖。tf.keras是Tensorflow的高階API,具備模塊性,易擴展性,相比Tensorflow的Low-level API能夠更快速的實現模型。Pytorch也是至關不錯的框架,感興趣的讀者能夠查看官方文檔。伴隨Tensorflow對Keras的支持,目前Keras功能已十分強大,好比對TPU,多GPU的分佈式策略支持等。python
下載連接(文件較大)git
圖片數據集包含4萬張照片,每張照片包含兩種標籤:github
下載連接算法
CSV文件包含「圖片名字」,「天氣標籤」,「地面標籤」docker
咱們但願訓練的模型能夠準確的預測新衛星圖片的上述標籤。咱們的模型將會有天氣,地面的兩個輸出,兩種不一樣的損失函數。訓練完成後,咱們會導出模型使用Tensorflow Serving部署。編程
import tensorflow as tf IMG_SIZE=128 img_input=tf.keras.Input(shape=(IMG_SIZE,IMG_SIZE,3),name='input_layer') conv_1_32=tf.keras.layers.Conv2D( filters=32, # 卷積核爲奇數:1,奇數具備中心點 2,圖像兩邊能夠對稱padding kernel_size=3, padding='same', # 激活函數在輸入爲負值時激活值爲0,此時神經元沒法學習,LeakyReLU在輸入爲負值時不爲0(但值很小) activation='relu' )(img_input) pool_1_2=tf.keras.layers.MaxPooling2D( # 默認過濾器大小和步長(2,2) padding='same' )(conv_1_32) conv_2_32=tf.keras.layers.Conv2D( filters=32, kernel_size=3, padding='same', activation='relu' )(pool_1_2) pool_2_2=tf.keras.layers.MaxPooling2D( padding='same' )(conv_2_32) # 將輸出展開 conv_flat=tf.keras.layers.Flatten()(pool_2_2) fc_1_128=tf.keras.layers.Dense( units=128, activation='relu' )(conv_flat) # 僅訓練時設置 fc_1_drop=tf.keras.layers.Dropout( rate=0.2 )(fc_1_128) fc_2_128=tf.keras.layers.Dense( units=128, activation='relu' )(fc_1_drop) fc_2_drop=tf.keras.layers.Dropout( rate=0.2 )(fc_2_128) # 天氣標籤輸出 weather_output=tf.keras.layers.Dense( units=4, activation='softmax', name='weather' )(fc_2_drop) # 地面標籤輸出 ground_outpout=tf.keras.layers.Dense( units=13, # 對於類別大於2的分類問題,若是類別互斥使用softmax,反之使用sigmoid activation='sigmoid', name='ground' )(fc_2_drop) model=tf.keras.Model( inputs=img_input, outputs=[weather_output,ground_outpout] )
各層參數數量json
模型配置:api
這裏有必要簡單介紹下Adam算法:數組
model.compile( # 也可嘗試下帶動量的SGD # Adam 默認參數值:lr=0.001, beta_1=0.9, beta_2=0.999, epsilon=1e-08, decay=0.0. optimizer='adam', loss={ # 注意這裏損失函數對應的激活函數 "weather":'categorical_crossentropy', 'ground':'binary_crossentropy' } )
import ast import numpy as np import math import os import random import pandas as pd from tensorflow.keras.preprocessing.image import img_to_array from tensorflow.keras.preprocessing.image import load_img def load_image(img_path,img_size): # /255 將像素值由 0-255 轉爲 0-1 區間 return img_to_array(load_img(img_path,target_size=(img_size,img_size)))/255. class KagglePlanetSequence(tf.keras.utils.Sequence): def __init__(self,df_path,data_path,img_size,batch_size,mode='train'): self.df=pd.read_csv(df_path) self.img_size=img_size self.batch_size=batch_size self.mode=mode # ast.literal_eval(x) 功能同 eval,如:"[1,2,3]"轉爲[1,2,3],但增長了非法字符處理 self.w_lables=self.df['weather_labels'].apply(lambda x:ast.literal_eval(x)).tolist() self.g_lables=self.df['ground_labels'].apply(lambda x:ast.literal_eval(x)).tolist() self.imges_list=self.df['image_name'].apply(lambda x:os.path.join(data_path,x+'.jpg')).tolist() def __len__(self): # math.ceil 向上取整,返回:大於或等於輸入數值 # 計算每一個epoch內訓練步數 return int(math.ceil(len(self.df)/float(self.batch_size))) # 打亂數據 def on_epoch_end(self): self.indexes=range(len(self.imges_list)) if self.mode == 'train': self.indexes=random.sample(self.indexes,k=len(self.indexes)) # 如下較簡單,別把區間算錯就好 def get_batch_labels(self,idx): return [ self.w_lables[idx*self.batch_size:(idx+1)*self.batch_size], self.g_lables[idx*self.batch_size:(idx+1)*self.batch_size] ] def get_batch_feature(self,idx): batch_images=self.imges_list[ idx*self.batch_size:(idx+1)*self.batch_size ] return np.array([load_image(img,self.img_size) for img in batch_images]) def __getitem__(self, idx): batch_x=self.get_batch_feature(idx) batch_y=self.get_batch_labels(idx) return batch_x,batch_y seq=KagglePlanetSequence('./KagglePlaneMCML.csv','./data/train/', img_size=IMG_SIZE,batch_size=32)
在訓練期間經過添加callbacks,能夠實現「保存模型」,‘提早中止訓練’等功能。本次,咱們使用ModelCheckPoint在每迭代一次訓練集後保存模型。網絡
callbacks=[ # .h5 保存參數和圖 # 1爲輸出進度條記錄,2爲每一個epoch輸出一行記錄 tf.keras.callbacks.ModelCheckpoint('./models.h5',verbose=1) ] # fit_generator分批次產生數據,能夠節約內存 model.fit_generator( generator=seq, verbose=1, epochs=1, # 使用基於進程的線程 use_multiprocessing=True, # 進程數量 workers=4, callbacks=callbacks )
讀取已保存的模型,用於訓練
anther_model=tf.keras.models.load_model('./model.h5') anther_model.fit_generator(generator=seq,verbose=1,epochs=1)
test_sq=KagglePlanetSequence( './KagglePlaneMCML.csv', './data/train/', img_size=IMG_SIZE, batch_size=32, mode='test' ) predictons=model.predict_generator( generator=test_sq,verbose=1 )
TFRecord文件中的數據是經過tf.train.Example Protocol Buffer格式存儲,其中包含一個從屬性名稱到取值的字典,屬性的取值能夠爲」BytesList「,」FloatList「或者」Int64List「。此外,TFRecord的值能夠做爲Cloud MLEngine的輸入。
咱們首先將圖片和標籤保存爲TFRecord文件
def _bytes_feature(value): return tf.train.Feature(bytes_list=tf.train.BytesList(value=[value])) tf_records_filename='./data/KajgglePlaneTFRecord_{}'.format(IMG_SIZE) writer=tf.python_io.TFRecordWriter(tf_records_filename) # 獲取對應數據 df_train={} img_list=[os.path.join('./data/train',v+'.jpg') for v in df_train['image_name'].tolist()] w_lables_arr=np.array([ast.literal_eval(l) for l in df_train['weather_labels']]) g_lables_arr=np.array([ast.literal_eval(l) for l in df_train['ground_labels']]) # 文件寫入 for i in range(len(df_train)): w_labels=w_lables_arr[i] g_lables=g_lables_arr[i] img=np.array([load_image(img_list[i],IMG_SIZE)]) example=tf.train.Example( features=tf.train.Feature( # 讀取的時候使用該key feattures={ 'image':_bytes_feature(img.tostring()), 'weather_labels':_bytes_feature(w_lables_arr.tostring()), 'ground_lables':_bytes_feature(g_lables_arr.tostring()) } ) ) writer.write(example.SerizlizeToString()) writer.close()
DataSet讀取TFRecord文件
」「」 提供兩種解析方法,一種是tf.FixedLenFeature,解析結果爲tensor,另外一種是tf.VarLenFeature獲得的結果是SparseTensor,用於處理稀疏數據。固然,讀取數據和寫入數據的格式要一致。 「」「 featdef={ 'image':tf.FixedLenFeature(shape=[],dtype=tf.string), 'weather_lables':tf.FixedLenFeature(shape=[],dtype=tf.string), 'ground_labels':tf.FixedLenFeature(shape=[],dtype=tf.string) } def _parse_record(tfre_file,clip=False): file=tf.parse_single_example(tfre_file,features=featdef) # tf.decode_raw 將字符串解析成圖像對應的像素數組 img=tf.reshape(tf.decode_raw(file['image'],tf.float32),shape=(IMG_SIZE,IMG_SIZE,3)) weather=tf.decode_raw(file['weather_lables'],tf.float32) ground=tf.decode_raw(file['ground_lables'],tf.float32) return img,weather,ground ds_train=tf.data.TFRecordDataset(filenames='./data/KanglePlaneTFRecord_{}'.format(IMG_SIZE),map=_parse_record) ds_train=ds_train.shuffle(buffer_size=1000).batch(32)
模型訓練
model=tf.keras.Model(inputs=img_input,outputs=[weather_output,ground_outpout]) model.compile( optimizer='adam', loss={ 'weather':'categorical_crossentropy', 'ground':'binary_crossentropy' } ) # history.history:loss values and metrics values # 經過history_rest.history能夠獲取loss value metrics value等信息 history_rest=model.fit(ds_train,steps_per_epoch=100,epochs=1)
Tensorflow Serving框架
如需瞭解更多有關內容,請查看官網介紹。
使用TensorFlow Serving 部署咱們只能使用SaveModel方法。
# 模型導出,官方推薦使用save_model # 若是須要更多自定義功能請使用SavedModelBuilder # 返回訓練模式/測試模式的flag # learning_phase: 0 train model 1 test model tf.keras.backend.set_learning_phase(1) model=tf.keras.models.load_model('./model.h5') export_path='./PlaneModel/1' with tf.keras.backend.get_session() as sess: tf.saved_model.simple_save( session=sess, export_dir=export_path, inputs={ 'input_image':model.input }, outputs={ t.name:t for t in model.outputs } )
模型保存後的文件結構:
$ tree . └── 1 ├── saved_model.pb └── variables ├── variables.data-00000-of-00001 └── variables.index
從新訓練的模型能夠放到」PlanetModel/2「文件夾內,此時TensorFlow Serving會自動更新。
1.安裝Docker
2.安裝Serving image
docker pull tensorflow/serving
3,運行Tensorflow Serving
tensorflow_model_server --model_base_path=$(pwd) --rest_api_port=9000 --model_name=PlanetModel
4,模型預測
{ # 如多人開發,最好自定義 "signature_name": <string>, # 只可包含如下任意一項 "instances": <value>|<(nested)list>|<list-of-objects> "inputs": <value>|<(nested)list>|<object> }
import requests import json # 不要忘了歸一化處理 image = img_to_array(load_img('./data/train/train_10001.jpg', target_size=(128,128))) / 255. payload={ 'instances':[{'input_images':image.tolist()}] } result=requests.post('http://localhost:9000/v1/models/PlanetModel:predict',json=payload) json.load(result.content)
5,預測結果
{ "predictions": <value>|<(nested)list>|<list-of-objects> }
{u'predictions': [ {u'ground_2/Sigmoid:0': [ 0.153237, 0.000527727, 0.00555856, 0.00542973, 0.00105254, 0.000256282, 0.103614, 0.0325185, 0.998204, 0.072204, 0.00745501, 0.00326175, 0.0942268], u'weather_2/Softmax:0': [ 0.963947, 0.000207846, 0.00113924, 0.0347063] }]}
本次項目只是簡單的案例,主要是爲了熟悉相關模塊的使用,項目自己還有不少須要優化的地方。keras清晰,友好能夠快速實現你的想法,還能夠結和「Eager Execution」 「Estimator」使用。雖然keras優勢不少,但因爲它高度封裝,因此想進一步瞭解Tensorflow的朋友,掌握Low-level API仍是頗有必要的。
本文實現參考Stijn Decubber的文章,歡迎關注他的博客。