本文咱们将使用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的文章,欢迎关注他的博客。