参考链接
关于tensorflow的一些参数啥的,建议大家可以去官网看看,很多细节还是可以参考和修改的。
Tensorflow2.0开始,开始全面配合keras,包括模型的搭建,还是模型的训练都是用keras,不过是更加鼓励的tf.keras,包括模型的搭建等等都是tf.keras. 借着贵司的项目,我将使用tensorflow进行数据的预处理,这里没有用keras的imagegenerator,因为预处理的其中一种操作里面是没有的,而且用tf的数据处理的方式会更多。然后我们全部import的是tf.keras.
关于tensorflow2.0开始,关于keras和tensorflow,keras的关系可以参考这篇文章咯 :
tensorflow2.0开始,用keras还是tf.keras
整个过程分成多步:
1.数据预处理部分
2.模型生成部分
3.训练部分(优化器等超参数的设置,这里涉及到2.0开始的多GPU的运行)
1.数据预处理部分:主要使用了
tf.io.read_file(filename) (这里是读取文件,任何文件都会被读取,所以注意读取到非图像文件带来的错误)
tf.image这里面有很多的预处理方法,详细可以参考我的另一篇文章 参考文章
tf.constant 生成参数,比如生成标签等
tf.reshape(total_label,(-1, 1)) #保持维度一致哦
train_dataset = tf.data.Dataset.from_tensor_slices((total_train, total_label))
train_dataset = train_dataset.map(_image_process)
train_dataset = train_dataset.shuffle(buffer_size=40000)
train_dataset = train_dataset.batch(batch_size)
关于tf.data.Dataset.from_tensor_slices 参考链接
预处理的部分主要包括了:预处理操作,标签的生成,标签和数据的合成,合成数据的使用等等,还有就是验证集合的生成,还有shuffle的使用以及batchsize的使用。修改前
def _image_process(filename,label):
image_string = tf.io.read_file(filename)
image_decoded = tf.image.decode_jpeg(image_string)
image_resized = tf.image.resize(image_decoded, [int(360*224/640), 224],method=tf.image.ResizeMethod.BILINEAR) / 255.0
image_padding = tf.image.pad_to_bounding_box(image_resized, offset_height=49, offset_width=0, target_height=224,target_width=224)
image_rotated = tf.image.rot90(image_padding)
image_horizontal = tf.image.flip_left_right(image_rotated)
image_vertical = tf.image.flip_up_down(image_horizontal)
image_brightness = tf.image.random_brightness(image_vertical, max_delta=0.4)
final_img = tf.expand_dims(image_brightness, 0)
return image_brightness, label
def data_generator(train_dir, valid_dir, batch_size):
train_covers_dir = train_dir + '/covers/'
train_nocovers_dir = train_dir + '/nocovers/'
valid_covers_dir = valid_dir + '/covers/'
valid_nocovers_dir = valid_dir + '/nocovers/'
train_covers_filename = tf.constant([train_covers_dir + filename for filename in os.listdir(train_covers_dir) if filename.endswith('.jpg')])
train_nocovers_filename = tf.constant([train_nocovers_dir + filename for filename in os.listdir(train_nocovers_dir) if filename.endswith('.jpg')])
total_train = tf.concat([train_covers_filename,train_nocovers_filename],axis=-1)
total_label = tf.concat([tf.zeros(train_covers_filename.shape, dtype=tf.int32),tf.ones(train_nocovers_filename.shape,dtype=tf.int32)],axis=-1)
total_label = tf.reshape(total_label,(-1, 1)) #保持维度一致哦
train_dataset = tf.data.Dataset.from_tensor_slices((total_train, total_label))
train_dataset = train_dataset.map(_image_process)
train_dataset = train_dataset.shuffle(buffer_size=40000)
train_dataset = train_dataset.batch(batch_size)
valid_covers_filename = tf.constant([valid_covers_dir + filename for filename in os.listdir(valid_covers_dir) if filename.endswith('.jpg')])
valid_nocovers_filename = tf.constant([valid_nocovers_dir + filename for filename in os.listdir(valid_nocovers_dir) if filename.endswith('.jpg')])
total_valid = tf.concat([valid_covers_filename,valid_nocovers_filename],axis=-1)
total_valid_label = tf.concat([tf.zeros(valid_covers_filename.shape, dtype=tf.int32),tf.ones(valid_nocovers_filename.shape,dtype=tf.int32)],axis=-1)
total_valid_label = tf.reshape(total_valid_label,(-1, 1))
valid_dataset = tf.data.Dataset.from_tensor_slices((total_valid, total_valid_label))
valid_dataset = valid_dataset.map(_image_process)
valid_dataset = valid_dataset.shuffle(buffer_size=10000)`在这里插入代码片`
valid_dataset = valid_dataset.batch(test_batch_size)
return train_dataset, valid_dataset, total_train.shape[0], total_valid.shape[0]
上面部分是原版的,但是这里面会存在写问题,我就提出一些问题然后针对问题进行修改,给出修改的细节。
第一个是预处理部分:有些是要进行随机的,如果不是随机就是固定的改变,这里需要注意下。比如翻转部分就是应该要随机的。不然的话固定下来那么就是固定操作了,我这里是padding的操作需要固定其他的都是随机,只是为了增加数据的丰富。而且要注意的是,我训练的时候会随机,但是我的验证部分是没有随机的。因为那就是实打实的测试。通过下面的修改我的验证部分和我的训练部分的情况就基本上一致了。不然的话就会出现训练和测试所表现不统一的情况。
下面就是修改后了:
def _train_image_process(filename,label):
image_string = tf.io.read_file(filename)
image_decoded = tf.image.decode_jpeg(image_string)
image_resized = tf.image.resize(image_decoded, [int(360*224/640), 224],method=tf.image.ResizeMethod.BILINEAR) / 255.0
image_padding = tf.image.pad_to_bounding_box(image_resized, offset_height=49, offset_width=0, target_height=224,target_width=224)
image_horizontal = tf.image.random_flip_up_down(image_padding)
image_vertical = tf.image.random_flip_up_down(image_horizontal)
image_brightness = tf.image.random_brightness(image_vertical, max_delta=0.4)
final_img = tf.expand_dims(image_brightness, 0)
return image_brightness, label
def _valid_image_process(filename,label):
image_string = tf.io.read_file(filename)
image_decoded = tf.image.decode_jpeg(image_string)
image_resized = tf.image.resize(image_decoded, [int(360*224/640), 224],method=tf.image.ResizeMethod.BILINEAR) / 255.0
image_padding = tf.image.pad_to_bounding_box(image_resized, offset_height=49, offset_width=0, target_height=224,target_width=224)
return image_padding, label
第二部分就是tf.data的三个东西repeat(), shuffle(),还有batch()
非常好的参考链接~
参考链接2
参考链接3
1.batch很好理解,就是batch size。注意在一个epoch中最后一个batch大小可能小于等于batch size
2.dataset.repeat就是俗称epoch,但在tf中与dataset.shuffle的使用顺序可能会导致个epoch的混合
3.dataset.shuffle就是说维持一个buffer size 大小的 shuffle buffer,图中所需的每个样本从shuffle buffer中获取,取得一个样本后,就从源数据集中加入一个样本到shuffle buffer中。
是先repeat还是先shuffle呢?
1.The order is important. A .shuffle() before a .repeat() would shuffle items across epoch boundaries (some items will have seen twice
before others are seen at all). A .shuffle() after a .batch() would
shuffle the order of the batches, but not shuffle the items across
batches.2.We can use a buffer_size with the same size as the dataset for a full shuffle. Large values provide better randomization but use more
memory.3.The shuffle buffer is filled before any elements are pulled from it.
So a large buffer_size may cause a delay when your Dataset is
starting.4.The shuffled dataset doesn’t report the end of a dataset until the
shuffle-buffer is completely empty. The Dataset is restarted by
.repeat(), causing another wait for the shuffle-buffer to be filled.
所以我们首先会让我们的buffersize等于我们的数据集合的大小,然后让repeat在我们的shuffle的后面,这样每次的epoch就不会存在重复的数据了。但是这样的操作带来的就是每次的shuffle都会让训练的时间变长了。因为shuffle是需要耗费时间的
2.模型生成部分:
-要注意input_tensor的形状的设置,会直接涉及到你最后测试的时候可以输入的形状大小。
-这里使用的tf.keras来加载与训练的模型,然后提取某一层,然后有输入的部分,最终就是用
-代码的model是来自 :from tensorflow.keras import Model
x = base_model.get_layer('top_activation').output
x = GlobalAveragePooling2D()(x)
x = Dropout(0.5)(x)
predictions = Dense(units=1, activation='sigmoid')(x)
model = Model(inputs=base_model.input, outputs=predictions)
–训练部分(模型的优化器的选择,模型的损失函数的选择,如何使用多GPU的训练)要注意从2.0开始mul.model.train好像是这样写吧,在多GPU训练中不太能生效,所以用了下面的新的方法:
logging = TensorBoard(log_dir = log_dir)
best_val_loss_callback = ModelCheckpoint(save_path_weight, monitor='val_loss', verbose=0, save_best_only=True, save_weights_only=False, mode='auto', period=1)
reduce_lr = ReduceLROnPlateau(monitor='val_loss', factor=0.1, patience=10, verbose=1)
early_stopping = EarlyStopping(monitor='val_loss', min_delta=0, patience=10, verbose=1)
if args.gpu_num > 1:
strategy = tf.distribute.MirroredStrategy()
with strategy.scope():
model = models.init_model(args.Backbone,input_tensor)
model.compile(optimizer=Optimizers.init_optimizers(),loss=tf.keras.losses.binary_crossentropy,metrics=['accuracy'])
else:
print('planing go to use the cpu')
model = models.init_model(args.Backbone,input_tensor)
model.compile(optimizer=Optimizers.init_optimizers(), loss=tf.keras.losses.binary_crossentropy,metrics=['accuracy'])
model.fit(
train_generator,
epochs=args.max_epoch,
steps_per_epoch=max(1,train_len//train_batch_size),
validation_data=valid_generator,
workers=5,
validation_steps=max(1,valid_len//test_batch_size),
callbacks=[logging, reduce_lr , best_val_loss_callback, reduce_lr, early_stopping]
)
model.save(save_path_weight)
流水线加速你的代码反正快巨多咯~~ 而且成功的加快了你的GPU使用率
Tensorflow如何加速你的GPU