华为云深度学习kaggle猫狗识别

使用华为云深度学习服务完成kaggle猫狗识别竞赛

参考:

  1. kaggle猫狗竞赛kernel第一名的代码
  2. Tensorflow官网代码
  3. 华为云DLS服务github代码

1. 环境配置与数据集处理

  • 首先我们需要从kaggle上面找到猫狗竞赛的页面,下载数据集压缩文件all.zip,大概853MB,解压两次后可以得到两个文件夹test和train,以及一个sample_submission.csv文件,test里面有12500张没有标签的测试图片,train中有带标签的25000张图片,猫狗各12500张,且按照顺序排好了。
  • 在桶的目录下创建code、log、model、train、test五个文件夹。
  • 使用华为云OBS客户端上传之前解压的图片,我是将解压后的图片上传的,所以需要一定的时间,文件位置为桶的目录下创建的train和test文件夹。
  • 创建开发环境,添加jupyter notebook

2. 代码-库的导入

from __future__ import division, absolute_import, print_function
import os, cv2, random
import pandas as pd
import tensorflow as tf
import numpy as np
import moxing as mox
from tensorflow import keras
from tensorflow.python.keras import layers

我使用tensorflow作为框架实现的,以及最后使用pandas保存结果,moxing用于读取文件,cv2用于图片resize。

3. 代码-定义常量以及运行时参数

# 使用tf.app.flags定义参数,运行时可以指定
tf.app.flags.DEFINE_string('data_url', 's3://dls-dogcat/input/', 'Dataset')
tf.app.flags.DEFINE_string('train_url', 's3://dls-dogcat/model/', 'Model_dir')
tf.app.flags.DEFINE_string('log_url', 's3://dls-dogcat/log/', 'Log_dir')

flags = tf.app.flags.FLAGS
TRAIN_DIR = os.path.join(flags.data_url, 'train/')
TEST_DIR = os.path.join(flags.data_url, 'test/')
MODEL_DIR = flags.train_url
LOG_DIR = flags.log_url

# 注释掉的部分用于测试
# TRAIN_DIR = 's3://dls-dogcat/input/train/'
# TEST_DIR = 's3://dls-dogcat/input/test/'
# MODEL_DIR = 's3://dls-dogcat/model/'
# LOG_DIR = 's3://dls-dogcat/log/'

# 这里获取文件夹下所有文件名称使用mox的函数,或者使用tf.gfile,不能使用os,测试数据需要按照名称顺序排列,
# 我在ubuntu下测试时发现文件名的排列规则是按照字符顺序,所以这里使用循环可以避免这个问题
train_image_url = [TRAIN_DIR + i for i in mox.file.list_directory(TRAIN_DIR)]
test_image_url = []
for i in range(1, 12501):
    path = TEST_DIR + str(i) + '.jpg'
    test_image_url.append(path)
print('Train images : {}, Test images : {}'.format(len(train_image_url), len(test_image_url)))



# 图片压缩为64*64大小,彩色图片3或灰度图片1
IMAGE_SIZE = 64
CHANNELS = 3

# batch大小16,epoch为30,但是一般不会跑完30轮,因为后面我设置了earlystop
BATCH_SIZE = 16
EPOCH = 30

# STEPS_PER_EPOCH后面实际没有使用,打乱训练集顺序
STEPS_PER_EPOCH = len(train_image_url) // BATCH_SIZE
random.shuffle(train_image_url)

# 使用mox获取文件夹下所有文件名
train_images = [TRAIN_DIR + i for i in mox.file.list_directory(TRAIN_DIR)]
test_images = [TEST_DIR + i for i in mox.file.list_directory(TEST_DIR)]

STEPS_PER_EPOCH = len(train_images) // BATCH_SIZE

# 数据集打乱
random.shuffle(train_images)

# 定义读取图片的函数,并且resize大小,设置为灰度图片或彩色,与上面CHANNELS对应即可
def read_image(file_path):
    img = cv2.imdecode(np.fromstring(mox.file.read(file_path, binary=True), np.uint8), cv2.IMREAD_COLOR)
    return cv2.resize(img, (IMAGE_SIZE, IMAGE_SIZE), interpolation=cv2.INTER_CUBIC)

# 将图片数据存入array中,同时除以255进行归一化
def prep_data(images):
    count = len(images)
    data = np.ndarray((count, IMAGE_SIZE, IMAGE_SIZE, CHANNELS), dtype=np.float32)
    for i, image_file in enumerate(images):
        image = read_image(image_file)
        data[i] = image / 255.0
        if i%1000 == 0: print('Processed {} of {}'.format(i, count))
    return data

train_set = prep_data(train_image_url)
test_set = prep_data(test_image_url)
print('Train_set shape: {}, Test_set shape: {}'.format(train_set.shape, test_set.shape))

# 获取训练集对应的label,就是将文件路径切分,找出是否包含dog或者cat,若是dog则写入1,否则0
train_labels = np.ndarray((len(train_image_url), 1), np.int32)
for i, url in enumerate(train_image_url):
    tag = url.split('.')[-3].split('/')[-1]
    if tag == 'dog':
        train_labels[i] = 1
    elif tag == 'cat':
        train_labels[i] = 0
        
print('Train_labels shape: {}'.format(train_labels.shape))

4. 代码-CNN构建

# 添加一些参数,便于调整神经网络,包括units,ksize,strides
f1, f2, f3, f4 = 32, 64, 128, 256
k1, k2, k3, k4 = 5, 5, 5, 5
s1, s2, s3, s4 = 2, 2, 2, 2
c1, c2, c3, c4 = 2, 2, 2, 2
fc1, fc2, fc3, fc4 = 512, 256, 128, 128

# 有三层(卷积层+最大池化+Dropout),然后Flatten层,再是全连接层,再是输出层
# 前面三层使用relu激活,drop概率0.5,全连接层drop概率0.5,最后输出层使用sigmoid激活
# 损失函数binary_crossentropy,优化器rmsprop
# 可优化部分:
# 1.增大内存,受限于内存,总参数在大概600000以上时,32GB内存会不够用,增加内存可以提升CNN的复杂度,也可以使用128大小的图片;
# 2.使用model_to_estimator将model转为estimator,然后使用GPU训练,可以提升训练速度;
# 3.使用vgg16取代前三层卷积层,可以实现更好的效果。

def cnn_model():
    model = keras.Sequential([
        layers.Conv2D(f1, k1, c1, padding='SAME', activation=tf.nn.relu, input_shape=(IMAGE_SIZE, IMAGE_SIZE, CHANNELS)),
        layers.MaxPool2D(strides=s1, padding='SAME'),
        layers.Dropout(0.5),
        
        layers.Conv2D(f2, k2, c2, padding='SAME', activation=tf.nn.relu),
        layers.MaxPool2D(strides=s2, padding='SAME'),
        layers.Dropout(0.5),
        
        layers.Conv2D(f3, k3, c3, padding='SAME', activation=tf.nn.relu),
        layers.MaxPool2D(strides=s3, padding='SAME'),
        layers.Dropout(0.5),
        
#         layers.Conv2D(f4, k4, c4, padding='SAME', activation=tf.nn.relu),
#         layers.MaxPool2D(strides=s4, padding='SAME'),
#         layers.Dropout(0.5),
        
        layers.Flatten(),
        layers.Dense(fc1, activation=tf.nn.relu),
        layers.Dropout(0.5),
        layers.Dense(fc2, activation=tf.nn.relu),
        layers.Dropout(0.5),
#         layers.Dense(fc3, activation=tf.nn.relu),
#         layers.Dropout(0.5),
#         layers.Dense(fc4, activation=tf.nn.relu),
#         layers.Dropout(0.5),
        
        layers.Dense(1, activation=tf.nn.sigmoid)
    ])
    model.compile(loss='binary_crossentropy',
              optimizer=tf.keras.optimizers.RMSprop(lr=1e-4),
              metrics=['accuracy'])
    return model

model = cnn_model()
model.summary()

图像处理使用卷积,为了避免过拟合使用了dropout,最后输出层为一个unit,因为是二分类,激活函数使用sigmoid,
所以使用binary_crossentropy为损失函数。

5. 代码-训练模型

# 设置PrintLoss callback在训练过程中打印损失和准确率
# 设置early_stop避免过拟合
# 设置tensorboard callback记录训练过程中loss、acc、val_loss、val_acc
# 设置checkpoint_callback保存训练过程中的模型
# 由于cp_callback、tb_callback不能直接写入,所以在华为云上实际运行时没有使用,如果跑在自己的机器上可以使用
early_stop = tf.keras.callbacks.EarlyStopping(monitor='val_loss', 
                                              patience=3, 
                                              mode='auto')

checkpoint_path = MODEL_DIR + "cp-{epoch:04d}.ckpt"
cp_callback = tf.keras.callbacks.ModelCheckpoint(checkpoint_path, 
                                                 verbose=1, 
                                                 save_weights_only=True, 
                                                 period=5)

tb_callback = tf.keras.callbacks.TensorBoard(log_dir=LOG_DIR, batch_size=BATCH_SIZE)

class PrintLoss(keras.callbacks.Callback):
    def on_epoch_end(self, epoch, logs):
        print('Epoch: {:03d} - loss: {:.5f} - acc: {:.5f} - val_loss: {:.5f} - val_acc: {:.5f}'.format(epoch + 1, 
                                                                                                       logs['loss'], 
                                                                                                       logs['acc'], 
                                                                                                       logs['val_loss'], 
                                                                                                       logs['val_acc']))

# 设置validation_split获取验证集损失
history = model.fit(train_set, 
                    train_labels, 
                    epochs=EPOCH, 
                    batch_size=BATCH_SIZE, 
                    validation_split=0.2, 
                    verbose=0, 
                    callbacks=[early_stop, cp_callback, tb_callback, PrintLoss()])

# 随便测试一下模型对训练集前3000图片的准确率
model.evaluate(train_set[:3000], train_labels[:3000])

6. 代码-预测与写入

# 调用predict进行预测
predictions = model.predict(test_set)

import pandas as pd
# path = os.path.join(flags.log_dir, 'submission.csv')
path = 's3://dls-dogcat/log/submission.csv'
counter = range(1, len(predictions) + 1)
df = pd.DataFrame({'id': counter, 'label': predictions})
# df['label'] = df['label'].map(lambda x: x.lstrip('[').rstrip(']')).astype('float32')
# df.reindex(index=list(range(1, df.shape[0])))
result = df.to_csv(path_or_buf=None, index=None)
with tf.gfile.Open(path, 'w') as f:
    f.write(result)
print('write success')

submission格式有要求,columns为id和label,label列下的数据格式要对,以及向桶中写数据的方式,参考华为云项目在github中的代码

7. kaggle分数

在这里插入图片描述

使用华为云深度学习服务完成kaggle猫狗识别竞赛-进阶版

进阶版主要使用了moxing和tensorflow框架,好处在于可以直接使用华为云GPU,非常迅速即可调试,坏处在于需要对自己的代码进行修改以适配框架

1. 数据处理

将图片数据存储为tfrecord,我对图片进行了resize处理,可以产生image size 128和64两种tfrecord,同时划分了train、eval数据集

代码参考华为云dls-example的使用moxing玩冰山

# 前面的部分代码和keras模式基本相同,将图片数据处理为numpy数组,不同的地方在于这个numpy数组我reshape为二维的了
# coding=utf-8
from __future__ import division, absolute_import, print_function
import os, cv2, random
import pandas as pd
import tensorflow as tf
import numpy as np
import moxing as mox
import matplotlib.pyplot as plt
import moxing.tensorflow as mtf
from sklearn.model_selection import train_test_split
from tensorflow import keras
from tensorflow.python.keras import layers
slim = tf.contrib.slim

print('load lib success', tf.VERSION)

TRAIN_DIR = 's3://dls-dogcat/input/train/'
TEST_DIR = 's3://dls-dogcat/input/test/'
OUTPUT_DIR = 's3://dls-dogcat/input/128'
TRAIN64 = 'train64.tfrecord'
EVAL64 = 'eval64.tfrecord'
TEST64 = 'test64.tfrecord'

TRAIN128 = 'train128.tfrecord'
EVAL128 = 'eval128.tfrecord'
TEST128 = 'test128.tfrecord'

train_image_url = [TRAIN_DIR + i for i in mox.file.list_directory(TRAIN_DIR)]

IMAGE_SIZE = 64
CHANNELS = 3

random.shuffle(train_image_url)

TRAIN_SIZE = len(train_image_url)
TEST_SIZE = len(tf.gfile.ListDirectory(TEST_DIR))
test_image_url = []
for i in range(1, TEST_SIZE + 1):
    path = TEST_DIR + str(i) + '.jpg'
    test_image_url.append(path)
print('Train images : {}, Test images : {}'.format(TRAIN_SIZE, TEST_SIZE))


def read_image(file_path):
    img = cv2.imdecode(np.fromstring(mox.file.read(file_path, binary=True), np.uint8), cv2.IMREAD_COLOR)
    return cv2.resize(img, (IMAGE_SIZE, IMAGE_SIZE), interpolation=cv2.INTER_CUBIC)

def prep_data(images):
    count = len(images)
    data = np.ndarray((count, IMAGE_SIZE * IMAGE_SIZE * CHANNELS), dtype=np.float32) # data也要改变对应下面
    for i, image_file in enumerate(images):
        image = read_image(image_file)
        image = image.reshape(-1) # 为了使用tfrecord的floatlist,我reshape了一下,其实可以使用byteslist,就不用reshape
        data[i] = image / 255.0
        if i%1000 == 0: print('Processed {} of {}'.format(i, count))
    return data

train_set = prep_data(train_image_url)
test_set = prep_data(test_image_url)
print('Train_set shape: {}, Test_set shape: {}'.format(train_set.shape, test_set.shape))


train_labels = np.ndarray((len(train_image_url), 1), np.int64)
for i, url in enumerate(train_image_url):
    tag = url.split('.')[-3].split('/')[-1]
    if tag == 'dog':
        train_labels[i] = 1
    elif tag == 'cat':
        train_labels[i] = 0
# 在这里划分数据集,80%train, 20%eval
print('Train_labels shape: {}'.format(train_labels.shape))
train_imgs, eval_imgs, train_labels, eval_labels = train_test_split(train_set, train_labels, test_size=0.2)
print(train_imgs.shape, eval_imgs.shape, train_labels.shape, eval_labels.shape)

# 将train和eval的numpy数组写入tfrecord的函数,这里需要定义Features,一个是image保存图片数组,一个是label保存标签(1、0)
# FloatList要求数组为一维的,所以上面我reshape了,如果使用byteslist就不用
def convert_and_encode_to_tfrecord(num_samples, images, labels, output_file):
    output_file = os.path.join(OUTPUT_DIR, output_file)
    with tf.python_io.TFRecordWriter(output_file) as tfrecord_writer:
        for i in range(num_samples):
            example = tf.train.Example(features=tf.train.Features(feature={
                'image': tf.train.Feature(float_list=tf.train.FloatList(value=images[i])),
                'label': tf.train.Feature(int64_list=tf.train.Int64List(value=labels[i]))
            }))
            tfrecord_writer.write(example.SerializeToString())
# 此处执行完毕会产生train64.tfrecord和eval64.tfrecord两个文件,train0.916GB,eval234MB,tfrecord比源文件大是很正常的
convert_and_encode_to_tfrecord(train_imgs.shape[0], train_imgs, train_labels, TRAIN64)
convert_and_encode_to_tfrecord(eval_imgs.shape[0], eval_imgs, eval_labels, EVAL64)


# 同理,这里将test写入tfrecord,但是test没有label,所以只需要一个feature
def convert_test_and_encode_to_tfrecord(num_samples, images, output_file):
    output_file = os.path.join(OUTPUT_DIR, output_file)
    with tf.python_io.TFRecordWriter(output_file) as tfrecord_writer:
        for i in range(num_samples):
            example = tf.train.Example(features=tf.train.Features(feature={
                'image': tf.train.Feature(float_list=tf.train.FloatList(value=images[i]))
            }))
            tfrecord_writer.write(example.SerializeToString())
# 大概586MB
convert_test_and_encode_to_tfrecord(test_set.shape[0], test_set, TEST64)

# 读取tfrecord的数据并显示出来,就可以验证是否正确的存入了数据,使用mox的get_tfrecord,参数照着改就完事了
# 这个方法不能与下面读取test一起使用,因为使用了session
def read_and_decode_tfrecord(dataset_dir, file_pattern, num_samples):
  keys_to_features = {
    'image': tf.FixedLenFeature((IMAGE_SIZE * IMAGE_SIZE * CHANNELS,), tf.float32, default_value=None),
    'label': tf.FixedLenFeature([1], tf.int64, default_value=None),
  }
  # 这里可以实现对一维数组的reshape,我们将数据恢复为彩色图片的三维形式
  items_to_handlers = {
    'image': slim.tfexample_decoder.Tensor('image', shape=[IMAGE_SIZE, IMAGE_SIZE, CHANNELS]),
    'label': slim.tfexample_decoder.Tensor('label', shape=[]),
  }
  dataset = mtf.get_tfrecord(dataset_dir=dataset_dir,
                             file_pattern=file_pattern,
                             num_samples=num_samples,
                             keys_to_features=keys_to_features,
                             items_to_handlers=items_to_handlers,
                             shuffle=False,
                             num_epochs=1)
  image, label = dataset.get(['image', 'label'])
  sv = tf.train.Supervisor()
  # 这里可以显示图片,20张彩色图片
  with sv.managed_session() as sess:
    plt.figure()
    for i in range(20):
      subp = plt.subplot(4, 5, i + 1)
      plt.subplots_adjust(hspace=1)
      subp.imshow(sess.run(image))
      label_eval = sess.run(label)
      subp.set_title('label=%s' % (label_eval))
    plt.show()

read_and_decode_tfrecord(dataset_dir=OUTPUT_DIR,
                         file_pattern='eval*.tfrecord',
                         num_samples=5000)



def read_test_and_decode_tfrecord(dataset_dir, file_pattern, num_samples):
  keys_to_features = {
    'image': tf.FixedLenFeature((IMAGE_SIZE * IMAGE_SIZE * CHANNELS,), tf.float32, default_value=None)
  }
  items_to_handlers = {
    'image': slim.tfexample_decoder.Tensor('image', shape=[IMAGE_SIZE, IMAGE_SIZE, CHANNELS])
  }
  dataset = mtf.get_tfrecord(dataset_dir=dataset_dir,
                             file_pattern=file_pattern,
                             num_samples=num_samples,
                             keys_to_features=keys_to_features,
                             items_to_handlers=items_to_handlers,
                             shuffle=False,
                             num_epochs=1)
  image = dataset.get(['image'])
  sv = tf.train.Supervisor()
  with sv.managed_session() as sess:
    plt.figure()
    for i in range(20):
      subp = plt.subplot(4, 5, i + 1)
      plt.subplots_adjust(hspace=1)
      subp.imshow(sess.run(image[0])) # 注意这个位置,test读出来的形式不一样,需要取image[0]才是正确的图片信息,此处有伏笔
    plt.show()

read_test_and_decode_tfrecord(dataset_dir=OUTPUT_DIR,
                         file_pattern='test*.tfrecord',
                         num_samples=12500)

2. 代码

经过上面的数据处理会得到三个tfrecord,分别是train、eval、test,可以修改参数得到128大小的tfrecord,文件大小为train3.66GB,eval0.915GB,test2.28GB

# 前面部分不必多说,是一些固定的库以及参数
from __future__ import absolute_import, division, print_function 
import os 
os.environ.pop('http_proxy', None) 
import math 
import numpy as np 
import pandas as pd 
import tensorflow as tf 
import moxing.tensorflow as mox 
from tensorflow.python.keras import layers

slim = tf.contrib.slim 

NUM_SAMPLES_TRAIN = 20000 
NUM_SAMPLES_EVAL = 5000 
NUM_SAMPLES_TEST = 12500 
# image_size对应之前存入tfrecord的图片大小
IMAGE_SIZE = 128
CHANNELS = 3

# 定义flags参数,这些参数将在创建作业时指定,有batch_size,data_url数据集路径,log_url日志以及checkpoint的路径、csv文件存储路径,is_training决定训练还是预测
tf.flags.DEFINE_integer('batch_size', 16, 'Mini-batch size') 
tf.flags.DEFINE_string('data_url', 's3://zxy/model/zzy', 'Dir of dataset') 
tf.flags.DEFINE_string('log_dir', 's3://zxy/model/zzy/log', 'Dir of log') 
tf.flags.DEFINE_boolean('is_training', True, 'True for train. False for eval and predict.') 
flags = tf.flags.FLAGS 

# 这个地方加上去可以避免因为obs传数据不稳定造成的失败,本质上是将桶内的数据拷贝到cache中
import atexit
import logging
_data_url = flags.data_url
_log_dir = flags.log_dir
if not mox.file.is_directory(_log_dir):
  mox.file.make_dirs(_log_dir)
mox.file.make_dirs('/cache/data_url')
mox.file.make_dirs('/cache/log_dir')
mox.file.copy_parallel(_data_url, '/cache/data_url')
mox.file.copy_parallel(_log_dir, '/cache/log_dir')
flags.data_url = '/cache/data_url'
flags.log_dir = '/cache/log_dir'
atexit.register(lambda: mox.file.copy_parallel('/cache/log_dir', _log_dir))
logger = logging.getLogger()
while logger.handlers:
  logger.handlers.pop()

# 这个部分实现了查询当前环境下的GPU数量,并据此定义steps,后面要用
num_gpus = mox.get_flag('num_gpus') 
# if using distributed, the number of workers is related to the number of machines.
num_workers = len(mox.get_flag('worker_hosts').split(',')) 
steps_per_epoch = int(math.ceil(float(NUM_SAMPLES_TRAIN) / (flags.batch_size * num_gpus * num_workers))) 
submission = pd.DataFrame(columns=['label']) 

# 定义输入函数,返回image、label,使用方式类似上面显示tfrecord的图片
def input_fn(run_mode, **kwargs): 
  # Train
  if run_mode == mox.ModeKeys.TRAIN: 
    num_samples = NUM_SAMPLES_TRAIN 
    num_epochs = None 
    shuffle = True 
    file_pattern = 'train*.tfrecord' 
  # Eval or Test
  else: 
    num_epochs = 1 
    shuffle = False 
    if run_mode == mox.ModeKeys.EVAL: 
      num_samples = NUM_SAMPLES_EVAL 
      file_pattern = 'eval*.tfrecord' 
    else: 
      num_samples = NUM_SAMPLES_TEST 
      file_pattern = 'test*.tfrecord' 
  keys_to_features = { 
    'image': tf.FixedLenFeature((IMAGE_SIZE * IMAGE_SIZE * CHANNELS,), tf.float32, default_value=None)
  } 
  items_to_handlers = { 
    'image': slim.tfexample_decoder.Tensor('image', shape=[IMAGE_SIZE, IMAGE_SIZE, CHANNELS])
  } 
  if run_mode != mox.ModeKeys.PREDICT: 
    keys_to_features['label'] = tf.FixedLenFeature([1], tf.int64, default_value=None) 
    items_to_handlers['label'] = slim.tfexample_decoder.Tensor('label', shape=[]) 
    
  # returns an instance of 'DatasetDataProvider' 
  # defined in 'tensorflow/contrib/slim/python/data/dataset_data_provider.py'
  dataset = mox.get_tfrecord(dataset_dir=flags.data_url, 
                             file_pattern=file_pattern, 
                             num_samples=num_samples, 
                             keys_to_features=keys_to_features, 
                             items_to_handlers=items_to_handlers, 
                             num_epochs=num_epochs, 
                             shuffle=shuffle) 
  # 如果运行的是predict,返回image,如果是train或eval,返回image和label
  if run_mode == mox.ModeKeys.PREDICT: 
    image = dataset.get(['image']) 
    # Non-DMA safe string cannot tensor may not be copied to a GPU. 
    # So we encode string to a list of integer. 
    return image
  else: 
    image, label = dataset.get(['image', 'label']) 
    return image, label
  # Data augementation(Only using in training data) 
#   if run_mode == mox.ModeKeys.TRAIN: 
#     image = tf.image.random_flip_left_right(image) 
#     image = tf.image.random_flip_up_down(image) 
#     image = tf.image.rot90(image, k=tf.random_uniform(shape=(), maxval=3, minval=0, dtype=tf.int32)) 
  

# 定义CNN模型,不多说,和keras模式差不多
f1, f2, f3, f4 = 32, 64, 128, 256
k1, k2, k3, k4 = 5, 5, 5, 5
s1, s2, s3, s4 = 2, 2, 2, 2
c1, c2, c3, c4 = 2, 2, 2, 2
fc1, fc2, fc3, fc4 = 256, 128, 64, 128

# Classification Model
def model_v1(images, run_mode): 
  is_training = (run_mode == mox.ModeKeys.TRAIN) 
  # Conv Layer 1 
  x = layers.Conv2D(f1, k1, c1, padding='SAME', activation=tf.nn.relu, input_shape=(IMAGE_SIZE, IMAGE_SIZE, CHANNELS))(images) 
  x = layers.MaxPool2D(strides=s1, padding='SAME')(x) 
  x = layers.Dropout(0.5)(x, training=is_training) 
  # Conv Layer 2 
  x = layers.Conv2D(f2, k2, c2, padding='SAME', activation=tf.nn.relu)(x) 
  x = layers.MaxPool2D(strides=s2, padding='SAME')(x) 
  x = layers.Dropout(0.5)(x, training=is_training)
  # Conv Layer 3 
  x = layers.Conv2D(f3, k3, c3, padding='SAME', activation=tf.nn.relu)(x) 
  x = layers.MaxPool2D(strides=s3, padding='SAME')(x) 
  x = layers.Dropout(0.5)(x, training=is_training)
  # Conv Layer 4 
  x = layers.Conv2D(f4, k4, c4, padding='SAME', activation=tf.nn.relu)(x) 
  x = layers.MaxPool2D(strides=s4, padding='SAME')(x) 
  x = layers.Dropout(0.5)(x, training=is_training)
  # Flatten the data for upcoming dense layers 
  x = layers.Flatten()(x) 
  # Dense Layers 
  x = layers.Dense(fc1, activation=tf.nn.relu)(x) 
  x = layers.Dropout(0.2)(x, training=is_training) 
  # Dense Layer 2 
  x = layers.Dense(fc2, activation=tf.nn.relu)(x) 
  x = layers.Dropout(0.2)(x, training=is_training) 
  # Sigmoid Layer 
  logits =layers.Dense(2)(x) 
  return logits 

# 定义model函数,这里是核心部分,从input_fn得到的数据在这里与model_fn发生反应,
# 通过mox.ModelSpec打印信息或者输出信息,与keras不同的是,这里使用softmax以及one-hot编码,sigmoid应该也是可以的
def model_fn(inputs, run_mode, **kwargs): 
  # In train or eval, id_or_labels represents labels. In predict, id_or_labels represents id. 
  if run_mode == mox.ModeKeys.PREDICT:
    images = inputs[0]
  else:
    images, labels = inputs
  # Reshape angles from [batch_size] to [batch_size, 1] 
  # Apply your version of model 
  logits = model_v1(images, run_mode) 
  if run_mode == mox.ModeKeys.PREDICT: 
    logits = tf.nn.softmax(logits) 
    model_spec = mox.ModelSpec(output_info={'logits': logits}) 
  else: 
    labels_one_hot = slim.one_hot_encoding(labels, 2) 
    loss = tf.losses.softmax_cross_entropy( 
      logits=logits, onehot_labels=labels_one_hot) 
    model_spec = mox.ModelSpec(loss=loss, log_info={'loss': loss}) 
  return model_spec 

# 这里对应model_fn的output_info,将logits[1]即dog的概率保存到DataFrame中,之后写入
def output_fn(outputs): 
  global submission 
  for output in outputs: 
    for logits in output['logits']: 
      # Get the probability of label==1 
      label = logits[1] 
      df = pd.DataFrame([label], columns=['label']) 
      submission = submission.append(df) 
      
# 主函数,根据创建作业时指定的is_training决定运行哪个方法,True对应训练,False对应eval和test      
if __name__ == '__main__': 
  # In training mode, using max_number_of_steps to control the epochs
  # Else, only run one epoch in eval and test mode.
  if flags.is_training: 
    mox.run(input_fn=input_fn, 
            model_fn=model_fn, 
            optimizer_fn=mox.get_optimizer_fn(name='adam', learning_rate=0.0001), 
            run_mode=mox.ModeKeys.TRAIN, 
            batch_size=flags.batch_size, 
            log_dir=flags.log_dir, 
            max_number_of_steps=steps_per_epoch * 150, # 这里与训练次数相关,自行修改
            log_every_n_steps=20, 
            save_summary_steps=50, 
            save_model_secs=120) 
  else: 
    mox.run(input_fn=input_fn, 
            model_fn=model_fn, 
            run_mode=mox.ModeKeys.EVAL, 
            batch_size=5, 
            log_every_n_steps=1, 
            max_number_of_steps=int(NUM_SAMPLES_EVAL / 5), 
            checkpoint_path=flags.log_dir) 
    mox.run(input_fn=input_fn, 
            output_fn=output_fn, 
            model_fn=model_fn, 
            run_mode=mox.ModeKeys.PREDICT, 
            batch_size=20,      # 这个地方很关键,必须为test size能整除的数,12500/20,ok,否则会丢失一部分测试数据
            max_number_of_steps=int(NUM_SAMPLES_TEST / 20),  # 对应上面
            log_every_n_steps=50, 
            output_every_n_steps=1,   # 这个地方必须为1,否则数据无法存入DataFrame中
            checkpoint_path=flags.log_dir) 
    # Write results to file. tf.gfile allow writing file to EBS/s3 
    submission_file = os.path.join(flags.log_dir, 'submission.csv') 
    print('start to write!!!!!')
    print('info: ', submission.label.values[:10])
    result = submission.to_csv(path_or_buf=None, index=False) 
    with tf.gfile.Open(submission_file, 'w') as f: 
      f.write(result)
    print('Success!!!')

经过上面的处理,并不能得到直接可以上传到kaggle的数据,因为我偷懒了,这个csv文件没有id列,所以最后你还需要再加工一下,很简单

结果及分析

使用image size为64的情况

Loss:

在这里插入图片描述

在这里插入图片描述
Score:

在这里插入图片描述

使用image size为128的情况

Loss:

在这里插入图片描述

在这里插入图片描述

Score:

在这里插入图片描述

  1. 在CNN不变的情况下,128大小的训练可以达到更低的损失,但是似乎结果并不比64好,而且与keras对比,似乎这个训练过程中有过拟合的问题,减少max_number_of_steps应该能得到更好的效果;
  2. 使用GPU的速度可快多了,最初的keras使用CPU跑了10小时以上,使用4个GPU可以在几十分钟内跑完更多轮的数据,效果相当的好。

总结

  1. 进行图片识别时基本的方式是使用卷积神经网络,所以使用基于tensorflow的keras可以迅速搭建一个卷积神经网络。
  2. 使用CNN的过程中可能会遇到内存不够的情况,一方面可以考虑优化CNN结构,另一方面可以考虑使用vgg16等已经训练好的模型加上自己的模型进行调试。
  3. 使用华为云进行开发的过程中会遇到一些问题,比如读取图片的方式被限定,tensorflow1.8版本model使用dataset会报错,文件写入的方式被限定,notebook意外断开连接等等。
  4. 使用华为云很方便,基于一个云平台基本可以完成所有的机器学习开发,而且提供了不少的学习工具,还是很实用的。
  5. 希望华为云可以提供更加直接的云计算平台使用方式,就我所想的应该是提供一个可以ssh的服务器,这样代码迁移更加方便。
  • 2
    点赞
  • 25
    收藏
    觉得还不错? 一键收藏
  • 6
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值