kaggle花分类比赛91.168%

之前一直都没注意显存,也没注意数据格式,直到跑模型的时候电脑直接崩了,因为排队用TPU,感觉人多,就直接在自己电脑上跑,我自己是有一张8G的4070,没想到啊,光是读取数据,就占用了6G

历次成绩

这个是用分布式gpu跑的,kaggle给配了两张16G显存的卡,TPU我前面56个人,人太多了,分辨率本身有影响,我使用192×192

这里使用512×512的分辨率,效果明显提高了,Tan和Le,2019年论文,迁移u学习会随着分辨率提高而变得更好,分辨率太小容易过拟合

试着把batchnomalization层也去掉,跑最后一次,看keras官方文档说从头开始训练很难找到合适的正则化,直接放弃,效果确实提高了

导入包和读取数据

导入包

import re
import os
import random
import numpy as np
import pandas as pd
import tensorflow as tf
import matplotlib.pyplot as plt
import math

读取数据

AUTO可以优化数据加载和预处理过程,之前都不知道有这么个东西,看着自己GPU利用率跳上跳下,很多时候利用率只有40%,后面还有一个map我看其他博主说,也是用来提高利用率的,原理没仔细看,直接拿来用了,因为是本地来跑代码,就选了最小尺寸,我4070和TPU跑的时候速度差不多,但我显存不够,之前电脑一直卡死,在选择卷积基的时候,因为只有一张卡,batchsize也不敢设太高,在TPU上因为有128G,能设成128,选择512尺寸的图片。

AUTO = tf.data.AUTOTUNE
BATCH_SIZE = 16
EPOCHS = 20
IMAGE_SIZE = [192, 192]
CLASSES = ['pink primrose',    'hard-leaved pocket orchid', 'canterbury bells', 'sweet pea',     'wild geranium',     'tiger lily',           'moon orchid',              'bird of paradise', 'monkshood',        'globe thistle',         # 00 - 09
           'snapdragon',       "colt's foot",               'king protea',      'spear thistle', 'yellow iris',       'globe-flower',         'purple coneflower',        'peruvian lily',    'balloon flower',   'giant white arum lily', # 10 - 19
           'fire lily',        'pincushion flower',         'fritillary',       'red ginger',    'grape hyacinth',    'corn poppy',           'prince of wales feathers', 'stemless gentian', 'artichoke',        'sweet william',         # 20 - 29
           'carnation',        'garden phlox',              'love in the mist', 'cosmos',        'alpine sea holly',  'ruby-lipped cattleya', 'cape flower',              'great masterwort', 'siam tulip',       'lenten rose',           # 30 - 39
           'barberton daisy',  'daffodil',                  'sword lily',       'poinsettia',    'bolero deep blue',  'wallflower',           'marigold',                 'buttercup',        'daisy',            'common dandelion',      # 40 - 49
           'petunia',          'wild pansy',                'primula',          'sunflower',     'lilac hibiscus',    'bishop of llandaff',   'gaura',                    'geranium',         'orange dahlia',    'pink-yellow dahlia',    # 50 - 59
           'cautleya spicata', 'japanese anemone',          'black-eyed susan', 'silverbush',    'californian poppy', 'osteospermum',         'spring crocus',            'iris',             'windflower',       'tree poppy',            # 60 - 69
           'gazania',          'azalea',                    'water lily',       'rose',          'thorn apple',       'morning glory',        'passion flower',           'lotus',            'toad lily',        'anthurium',             # 70 - 79
           'frangipani',       'clematis',                  'hibiscus',         'columbine',     'desert-rose',       'tree mallow',          'magnolia',                 'cyclamen ',        'watercress',       'canna lily',            # 80 - 89
           'hippeastrum ',     'bee balm',                  'pink quill',       'foxglove',      'bougainvillea',     'camellia',             'mallow',                   'mexican petunia',  'bromelia',         'blanket flower',        # 90 - 99
           'trumpet creeper',  'blackberry lily',           'common tulip',     'wild rose']
GCS_PATH = '/home/linjiongji/下载/tpu-getting-started/tfrecords-jpeg-192x192'
TRAINING_FILENAMES = tf.io.gfile.glob(GCS_PATH + '/train/*.tfrec')
VALIDATION_FILENAMES = tf.io.gfile.glob(GCS_PATH + '/val/*.tfrec')
TEST_FILENAMES = tf.io.gfile.glob(GCS_PATH + '/test/*.tfrec')

设置用来读取的函数和展示图片的函数

用于读取数据的函数

这些函数用来读取数据,用于后面训练和调参

def decode_image(image_data):
    image = tf.image.decode_jpeg(image_data, channels=3)
    image = tf.cast(image, tf.float32) / 255.0
    image = tf.reshape(image, [*IMAGE_SIZE, 3])
    return image
def read_labeled_tfrecord(example):
    LABELED_TFREC_FORMAT = {
        "image": tf.io.FixedLenFeature([], tf.string),
        "class": tf.io.FixedLenFeature([], tf.int64),
    }
    example = tf.io.parse_single_example(example, LABELED_TFREC_FORMAT)
    image = decode_image(example['image'])
    label = tf.cast(example['class'], tf.int32)
    return image, label
def read_unlabeled_tfrecord(example):
    UNLABELED_TFREC_FORMAT = {
        "image": tf.io.FixedLenFeature([], tf.string),
        "id": tf.io.FixedLenFeature([], tf.string),
    }
    example = tf.io.parse_single_example(example, UNLABELED_TFREC_FORMAT)
    image = decode_image(example['image'])
    idnum = example['id']
    return image, idnum
def load_dataset(filenames, labeled=True, ordered=False):
    ignore_order = tf.data.Options()
    if not ordered:
        ignore_order.experimental_deterministic = False
    dataset = tf.data.TFRecordDataset(filenames, num_parallel_reads=AUTO)
    dataset = dataset.with_options(ignore_order)
    dataset = dataset.map(read_labeled_tfrecord if labeled else read_unlabeled_tfrecord, num_parallel_calls=AUTO)
    return dataset
IMAGE_HEIGHT=192
IMAGE_WIDTH=192
def get_training_dataset():
    dataset = load_dataset(TRAINING_FILENAMES, labeled=True)
    dataset = dataset.map(data_augment, num_parallel_calls=AUTO)
    dataset = dataset.repeat() # the training dataset must repeat for several epochs
    dataset = dataset.shuffle(2048)
    dataset = dataset.batch(BATCH_SIZE)
    dataset = dataset.prefetch(AUTO) # prefetch next batch while training (autotune prefetch buffer size)
    return dataset
def get_validation_dataset(ordered=False):
    dataset = load_dataset(VALIDATION_FILENAMES, labeled=True, ordered=ordered)
    dataset = dataset.batch(BATCH_SIZE)
    dataset = dataset.cache()
    dataset = dataset.prefetch(AUTO) # prefetch next batch while training (autotune prefetch buffer size)
    return dataset
def get_train_valid_datasets():
    dataset = load_dataset(TRAINING_FILENAMES + VALIDATION_FILENAMES, labeled=True)
    dataset = dataset.map(data_augment, num_parallel_calls=AUTO)
    dataset = dataset.repeat() # the training dataset must repeat for several epochs
    dataset = dataset.shuffle(2048)
    dataset = dataset.batch(BATCH_SIZE)
    dataset = dataset.prefetch(AUTO) # prefetch next batch while training (autotune prefetch buffer size)
    return dataset

def get_test_dataset(ordered=False):
    dataset = load_dataset(TEST_FILENAMES, labeled=False, ordered=ordered)
    dataset = dataset.batch(BATCH_SIZE)
    dataset = dataset.prefetch(AUTO) # prefetch next batch while training (autotune prefetch buffer size)
    return dataset
def count_data_items(filenames):
    n = [int(re.compile(r"-([0-9]*)\.").search(filename).group(1)) for filename in filenames]
    return np.sum(n)
NUM_TRAINING_IMAGES = count_data_items(TRAINING_FILENAMES)
NUM_VALIDATION_IMAGES = count_data_items(VALIDATION_FILENAMES)
NUM_TEST_IMAGES = count_data_items(TEST_FILENAMES)
print('Dataset: {} training images, {} validation images, {} unlabeled test images'.format(NUM_TRAINING_IMAGES, NUM_VALIDATION_IMAGES, NUM_TEST_IMAGES))

数据增强

这里我做了数据增强,进行了翻转,对比度,亮度,缩放方面的数据增强

def data_augment(image, label):
    image = tf.image.random_flip_left_right(image)
    image = tf.image.random_brightness(image, max_delta=0.2)
    image = tf.image.random_contrast(image, lower=0.5, upper=1.5)
    image = tf.image.resize(image, [IMAGE_HEIGHT, IMAGE_WIDTH])
    return image, label

用于绘图的函数

# 绘图
from matplotlib import pyplot as plt
def batch_to_numpy_images_and_labels(data):
    images, labels = data
    numpy_images = images.numpy()
    numpy_labels = labels.numpy()
    if numpy_labels.dtype == object:  # binary string in this case,
        # these are image ID strings
        numpy_labels = [None for _ in enumerate(numpy_images)]
    # If no labels, only image IDs, return None for labels (this is
    # the case for test data)
    return numpy_images, numpy_labels
def title_from_label_and_target(label, correct_label):
    if correct_label is None:
        return CLASSES[label], True
    correct = (label == correct_label)
    return "{} [{}{}{}]".format(CLASSES[label], 'OK' if correct else 'NO', u"\u2192" if not correct else '',
                                CLASSES[correct_label] if not correct else ''), correct


def display_one_flower(image, title, subplot, red=False, titlesize=16):
    plt.subplot(*subplot)
    plt.axis('off')
    plt.imshow(image)
    if len(title) > 0:
        plt.title(title, fontsize=int(titlesize) if not red else int(titlesize / 1.2), color='red' if red else 'black',
                  fontdict={'verticalalignment': 'center'}, pad=int(titlesize / 1.5))
    return (subplot[0], subplot[1], subplot[2] + 1)


def display_batch_of_images(databatch, predictions=None):
    images, labels = batch_to_numpy_images_and_labels(databatch)
    if labels is None:
        labels = [None for _ in enumerate(images)]
    rows = int(math.sqrt(len(images)))
    cols = len(images) // rows
    FIGSIZE = 13.0
    SPACING = 0.1
    subplot = (rows, cols, 1)
    if rows < cols:
        plt.figure(figsize=(FIGSIZE, FIGSIZE / cols * rows))
    else:
        plt.figure(figsize=(FIGSIZE / rows * cols, FIGSIZE))

    # display
    for i, (image, label) in enumerate(zip(images[:rows * cols], labels[:rows * cols])):
        title = '' if label is None else CLASSES[label]
        correct = True
        if predictions is not None:
            title, correct = title_from_label_and_target(predictions[i], label)
        dynamic_titlesize = FIGSIZE * SPACING / max(rows,
                                                    cols) * 40 + 3  # magic formula tested to work from 1x1 to 10x10 images
        subplot = display_one_flower(image, title, subplot, not correct, titlesize=dynamic_titlesize)

    # layout
    plt.tight_layout()
    if label is None and predictions is None:
        plt.subplots_adjust(wspace=0, hspace=0)
    else:
        plt.subplots_adjust(wspace=SPACING, hspace=SPACING)
    plt.show()

这个用来看花的,没什么用就是了,毕竟101种花,下面这段代码就是看花用的,花长下面这样,对于分析没多大帮助,他这里这个数据说明中有说花的数据是不平衡的,有的花甚至只有一张图,不知道有没有人对这里进行一个加权。

ds_iter = iter(get_training_dataset().unbatch().batch(20))
one_batch = next(ds_iter)
display_batch_of_images(one_batch)

自定义callback

下面这个使用来调整训练过程的learning_rate,可以用这种自定义的callback,Keras官方文档里面有教

def lrfn(epoch):
    LR_START = 0.00001
    LR_MAX = 0.00005 * strategy.num_replicas_in_sync
    LR_MIN = 0.00001
    LR_RAMPUP_EPOCHS = 5
    LR_SUSTAIN_EPOCHS = 0
    LR_EXP_DECAY = .8

    if epoch < LR_RAMPUP_EPOCHS:
        lr = (LR_MAX - LR_START) / LR_RAMPUP_EPOCHS * epoch + LR_START
    elif epoch < LR_RAMPUP_EPOCHS + LR_SUSTAIN_EPOCHS:
        lr = LR_MAX
    else:
        lr = (LR_MAX - LR_MIN) * LR_EXP_DECAY**(epoch - LR_RAMPUP_EPOCHS - LR_SUSTAIN_EPOCHS) + LR_MIN
    return lr
lr_schedule = tf.keras.callbacks.LearningRateScheduler(lrfn, verbose=1)

选择合适的卷积基

这里我看别的博主说Res系列效果不错,总共6个模型,这里要重头开始训练,不能冻结卷积基,冻结后效果特别差,10%出头的准确率。由于我显卡只有8G,我一次跑4个模型,20个轮次。

下面是代码,你可以自己改,主要就用来调参。

#选择适合的基
STEPS_PER_EPOCH = NUM_TRAINING_IMAGES // BATCH_SIZE
TL_Models =[
    tf.keras.applications.ResNet101(weights='imagenet',include_top=False ,input_shape=[*IMAGE_SIZE, 3]),
    tf.keras.applications.ResNet101V2(weights='imagenet',include_top=False ,input_shape=[*IMAGE_SIZE, 3]),
    tf.keras.applications.ResNet152(weights='imagenet',include_top=False ,input_shape=[*IMAGE_SIZE, 3]),
    tf.keras.applications.ResNet152V2(weights='imagenet',include_top=False ,input_shape=[*IMAGE_SIZE, 3]),
]
TL_Models_NAMES = ['ResNet101','ResNet101V2','ResNet152','ResNet152V2']
#训练模型
batch_size=BATCH_SIZE
from keras.models import Sequential
import tensorflow as tf
#端到端训练
#选择Res系列,但是显存不够,只能跑4个,多了电脑卡死,还有两个,但是验证集准确率都比较低,整个系列是6个
HISTORIES = []
for i in TL_Models:
    model = tf.keras.Sequential([
        i,
        tf.keras.layers.BatchNormalization(),
        tf.keras.layers.GlobalAveragePooling2D(),
        tf.keras.layers.BatchNormalization(),
        tf.keras.layers.Dense(len(CLASSES), activation='softmax')])
    model.compile(optimizer='adam', loss='sparse_categorical_crossentropy', metrics=['sparse_categorical_accuracy'])
    history = model.fit(get_training_dataset(), steps_per_epoch=STEPS_PER_EPOCH, epochs=20, callbacks=[lr_schedule],validation_data=get_validation_dataset())
    HISTORIES.append(history.history)
#绘图选择卷积基
styles=[':','-.','--','-',':','-.','--','-',':','-.','--','-']
plt.figure(figsize=(10, 8))
plt.subplot(2, 1, 1)
for i in range(len(HISTORIES)):
    plt.plot(HISTORIES[i]['val_sparse_categorical_accuracy'], linestyle=styles[i])
plt.legend(TL_Models_NAMES, loc='upper left')
plt.title('Validation Accuracy')
plt.xlabel('Epoch')
plt.ylabel('Accuracy')
plt.subplot(2, 1, 2)
for i in range(len(HISTORIES)):
    plt.plot(HISTORIES[i]['val_loss'], linestyle=styles[i])
plt.legend(TL_Models_NAMES, loc='upper left')
plt.title('Validation Loss')
plt.xlabel('Epoch')
plt.ylabel('Loss')
plt.show()

上面这一段跑完结果如下,看的出来ResNet101效果最好,还差两个,也跑一下。

#再把剩余两个RES系列也跑一下
STEPS_PER_EPOCH = NUM_TRAINING_IMAGES // BATCH_SIZE
TL_Models =[
    tf.keras.applications.ResNet101(weights='imagenet',include_top=False ,input_shape=[*IMAGE_SIZE, 3]),
    tf.keras.applications.ResNet50(weights='imagenet',include_top=False ,input_shape=[*IMAGE_SIZE, 3]),
    tf.keras.applications.ResNet50V2(weights='imagenet',include_top=False ,input_shape=[*IMAGE_SIZE, 3]),
]
TL_Models_NAMES = ['ResNet101','ResNet50','ResNet50V2']
#训练模型
batch_size=BATCH_SIZE
from keras.models import Sequential
import tensorflow as tf
HISTORIES = []
for i in TL_Models:
    model = tf.keras.Sequential([
        i,
        tf.keras.layers.BatchNormalization(),
        tf.keras.layers.GlobalAveragePooling2D(),
        tf.keras.layers.BatchNormalization(),
        tf.keras.layers.Dense(len(CLASSES), activation='softmax')])
    model.compile(optimizer='adam', loss='sparse_categorical_crossentropy', metrics=['sparse_categorical_accuracy'])
    history = model.fit(get_training_dataset(), steps_per_epoch=STEPS_PER_EPOCH, epochs=10, callbacks=[lr_schedule],validation_data=get_validation_dataset())
    HISTORIES.append(history.history)
#绘图选择卷积基
styles=[':','-.','--','-',':','-.','--','-',':','-.','--','-']
plt.figure(figsize=(10, 8))
plt.subplot(2, 1, 1)
for i in range(len(HISTORIES)):
    plt.plot(HISTORIES[i]['val_sparse_categorical_accuracy'], linestyle=styles[i])
plt.legend(TL_Models_NAMES, loc='upper left')
plt.title('Validation Accuracy')
plt.xlabel('Epoch')
plt.ylabel('Accuracy')
plt.subplot(2, 1, 2)
for i in range(len(HISTORIES)):
    plt.plot(HISTORIES[i]['val_loss'], linestyle=styles[i])
plt.legend(TL_Models_NAMES, loc='upper left')
plt.title('Validation Loss')
plt.xlabel('Epoch')
plt.ylabel('Loss')
plt.show()

这一段跑出来也是101最好,所以最后就选择101了

选择dropout_rate

我还调整了一下dropout

#选择适合的droprate
batch_size=BATCH_SIZE
from keras.models import Sequential
HISTORIES = []
rate = [0.2,0.3,0.4,0.5,0.6,0.7]
for i in rate:
     model = tf.keras.Sequential([
        tf.keras.applications.ResNet101(weights='imagenet',include_top=False ,input_shape=[*IMAGE_SIZE, 3]),
        tf.keras.layers.BatchNormalization(),
        tf.keras.layers.GlobalAveragePooling2D(),
        tf.keras.layers.BatchNormalization(),
        tf.keras.layers.Dropout(i),
        tf.keras.layers.Dense(len(CLASSES), activation='softmax')
    ])
     model.compile(optimizer='adam',loss = 'sparse_categorical_crossentropy',metrics=['sparse_categorical_accuracy'])
     history = model.fit(get_training_dataset(), steps_per_epoch=STEPS_PER_EPOCH,epochs=10,callbacks=[lr_schedule],validation_data=get_validation_dataset())
     HISTORIES.append(history.history)
#进行绘图选择dropout_rate
styles=[':','-.','--','-',':','-.','--','-',':','-.','--','-']
plt.figure(figsize=(10, 8))
plt.subplot(2, 1, 1)
names = ["2", "3", "4",'5','6','7']
for i in range(len(HISTORIES)):
    plt.plot(HISTORIES[i]['val_sparse_categorical_accuracy'], linestyle=styles[i])
plt.legend(names, loc='upper left')
plt.title('Validation Accuracy')
plt.xlabel('Epoch')
plt.ylabel('Accuracy')
plt.subplot(2, 1, 2)
for i in range(len(HISTORIES)):
    plt.plot(HISTORIES[i]['val_loss'], linestyle=styles[i])
plt.legend(names, loc='upper left')
plt.title('Validation Loss')
plt.xlabel('Epoch')
plt.ylabel('Loss')
plt.show()

这里跑出来结果如下,最后选0.2,但是之后又重新试了一下,好像不加更好

这之后重新跑了一下,好像不加更好

训练最终模型并提交

这里得到网站提交完整的notebook,我写完的时候tpu还没轮到我,我还没提交,我最终提交时吧论次改成25

#训练最终模型
model = tf.keras.Sequential([
        tf.keras.applications.ResNet101(weights='imagenet',include_top=False ,input_shape=[*IMAGE_SIZE, 3]),
        tf.keras.layers.BatchNormalization(),
        tf.keras.layers.GlobalAveragePooling2D(),
        tf.keras.layers.BatchNormalization(),
        tf.keras.layers.Dense(len(CLASSES), activation='softmax')])
model.compile(optimizer='adam',loss = 'sparse_categorical_crossentropy',metrics=['sparse_categorical_accuracy'],)
history = model.fit(get_train_valid_datasets(), steps_per_epoch=STEPS_PER_EPOCH,epochs=20, callbacks=[lr_schedule])
#在测试集上运行并保存结果
model.save("flower_model.keras")
model = tf.keras.models.load_model("flower_model.keras")
test_ds = get_test_dataset(ordered=True)
test_images_ds = test_ds.map(lambda image, idnum: image)
probabilities = model.predict(test_images_ds)
predictions = np.argmax(probabilities, axis=-1)
test_ids_ds = test_ds.map(lambda image, idnum: idnum).unbatch()
test_ids = next(iter(test_ids_ds.batch(NUM_TEST_IMAGES))).numpy().astype('U') # all in one batch
np.savetxt('submission.csv', np.rec.fromarrays([test_ids, predictions]), fmt=['%s', '%d'], delimiter=',', header='id,label', comments='')

网站提交用的代码

这里自己改改就行了,把一些不必要的省掉,参数都调好了,因为跑的话要花时间,去看别人写的,也很简略,看不到什么思路,也没什么地方能改,基本大家做的都差不多

推荐阅读

下面这篇提高GPU利用率说的挺清楚

GPU利用率低常见原因分析及优化方式_gpu占用率低怎么办-CSDN博客文章浏览阅读1.1w次,点赞6次,收藏30次。本文的 GPU 利用率主要指 GPU 在时间片上的利用率。GPU 任务会交替的使用 CPU 和 GPU 进行计算,当 CPU 计算成为瓶颈时,就会出现 GPU 等待的问题,GPU 空跑那利用率就低了。那么优化的方向就是缩短一切使用 CPU 计算环节的耗时。..._gpu占用率低怎么办https://blog.csdn.net/u012863603/article/details/126380563?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522171850624516800178510091%2522%252C%2522scm%2522%253A%252220140713.130102334..%2522%257D&request_id=171850624516800178510091&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2~all~sobaiduend~default-1-126380563-null-null.142^v100^pc_search_result_base4&utm_term=%E6%8F%90%E9%AB%98GPU%E5%8D%A0%E7%94%A8%E7%8E%87&spm=1018.2226.3001.4187

下面这个是其他系列的模型,可以尝试其他系列,但效果最好,大家都说是Res系列

Keras Applicationsicon-default.png?t=N7T8https://keras.io/api/applications/

  • 14
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值