参考资料:
- https://github.com/keras-team/keras/blob/eb97bc385599dec8182963fe263bd958b9ab0057/keras/models.py
- https://github.com/xingkongliang/Keras-Tutorials
- Keras学习资料大全,这是fchollet的一个仓库
- Keras官方扩展库,能找到许多没写进Keras但是会用得着的Layer,Model,Objectives
- keras进行图像预处理源码
- UCF课程:高级计算机视觉(Keras) by Mubarak Shah
用keras训练多标签数据
- Multi_Label_Classification_Keras
通常用keras做分类任务的时候,一张图像往往只对应着一种类别,但是在实际的问题中,可能你需要预测出一张图像的多种属性。例如在pyimagesearch的《multi-label-classification-with-keras》这篇文章中提出了一个衣服数据集,整个数据集有两种属性,一种是颜色(blue, red, black),另一种是衣服的类型(dress, jeans, shirt) 。如假设one-hot-vector编码顺序是(blue, red, black, dress, jeans, shirt)则black jeans的 label就是[0,0,1,0,1,0]。
keras multi label dataset
那么面对这样的多标签任务如何使用keras进行CNN模型的搭建与训练呢?
首先我们搭建一个单输入(一张图像)多输出(图像的多个属性,比如衣服的颜色,类型)的CNN。
代码语言:javascript复制def GenModel(img_width = 512, img_height = 512 , model_name = 'AlexNet'):
# 指定输入图像大小
image_input_shape = (img_width, img_height, 3)
# 定义网络(AlexNet)
if model_name == 'AlexNet':
print('n--------Start build model ', model_name, '--------n')
# 定义网络输入
image_input = Input(shape=image_input_shape, name='image_input')
# 定义主卷积(图像)网络卷积->激活->池化
conv_image = Conv2D(96, (11, 11), strides = (4, 4), padding = 'valid', activation = 'relu')(image_input)
conv_image = MaxPooling2D(pool_size = (3, 3), strides = (2, 2))(conv_image)
conv_image = Conv2D(256, (5, 5), strides = (1, 1), padding = 'same', activation = 'relu')(conv_image)
conv_image = MaxPooling2D(pool_size = (3, 3), strides = (2, 2))(conv_image)
conv_image = Conv2D(384, (3,3), strides = (1, 1), padding = 'same', activation = 'relu')(conv_image)
conv_image = Conv2D(384, (3,3), strides = (1, 1), padding = 'same', activation = 'relu')(conv_image)
conv_image = Conv2D(384, (3,3), strides = (1, 1), padding = 'same', activation = 'relu')(conv_image)
conv_image = MaxPooling2D(pool_size = (3, 3), strides = (2, 2))(conv_image)
conv_image = Flatten()(conv_image)
# 定义2个输出,分别用于预测衣服的颜色和类型
out_color = Dense(4096, activation='relu',)(conv_image)
out_color = Dense(512, activation='relu',)(out_color)
out_color = Dense(3, activation='sigmoid', name='out_age')(out_color)
out_type = Dense(4096, activation='relu',)(conv_image)
out_type = Dense(512, activation='relu',)(out_sex)
out_type = Dense(3, activation='sigmoid', name='out_sex')(out_sex)
# 定义整个单输入,2输出的模型
model=Model(inputs = image_input, outputs = [out_color, out_type])
return model
然后对模型进行编译
代码语言:javascript复制# 编译模型,定义损失函数
opt=Adadelta()
# opt=SGD()
print('n-------- optimizer: %s --------n'%(opt.__class__.__name__) )
model.compile( optimizer = opt,
loss = {'out_color': 'categorical_crossentropy', 'out_type': 'categorical_crossentropy'},
loss_weights = {'out_color': out_color_weight, 'out_type': out_type_weight, metrics = ['accuracy']) # 这里loss_weights需要自己手动设定下
最后将数据集载入模型进行训练和预测
代码语言:javascript复制# grab the image paths and randomly shuffle them
print("[INFO] loading images...")
imagePaths = sorted(list(paths.list_images(args["dataset"])))
random.seed(42)
random.shuffle(imagePaths)
# initialize the data and labels
data = []
labels = []
# loop over the input images
for imagePath in imagePaths:
# load the image, pre-process it, and store it in the data list
image = cv2.imread(imagePath)
image = cv2.resize(image, (IMAGE_DIMS[1], IMAGE_DIMS[0]))
image = img_to_array(image)
data.append(image)
# extract set of class labels from the image path and update the
# labels list
l = label = imagePath.split(os.path.sep)[-2].split("_")
labels.append(l)
# scale the raw pixel intensities to the range [0, 1]
data = np.array(data, dtype="float") / 255.0
labels = np.array(labels)
print("[INFO] data matrix: {} images ({:.2f}MB)".format(
len(imagePaths), data.nbytes / (1024 * 1000.0)))
print(labels)
# binarize the labels using scikit-learn's special multi-label
# binarizer implementation
print("[INFO] class labels:")
mlb = MultiLabelBinarizer()
labels = mlb.fit_transform(labels)
print(labels)
# loop over each of the possible class labels and show them
for (i, label) in enumerate(mlb.classes_):
print("{}. {}".format(i 1, label))
# partition the data into training and testing splits using 80% of
# the data for training and the remaining 20% for testing
(trainX, testX, trainY, testY) = train_test_split(data,
labels, test_size=0.2, random_state=42)
# construct the image generator for data augmentation
aug = ImageDataGenerator(rotation_range=25, width_shift_range=0.1,
height_shift_range=0.1, shear_range=0.2, zoom_range=0.2,
horizontal_flip=True, fill_mode="nearest")
# train the network
print("[INFO] training network...")
H = model.fit_generator(
aug.flow(trainX, trainY, batch_size=BS),
validation_data=(testX, testY),
steps_per_epoch=len(trainX) // BS,
epochs=EPOCHS, verbose=1)
输出优化器的名字
代码语言:javascript复制from keras.optimizers import RMSprop, Adam, Adadelta
opt=Adam()
opt_name=opt.__class__.__name__
print(opt_name)
使用回调函数
- https://keras.io/callbacks/
- http://blog.csdn.net/tsyccnh/article/details/78865167
- https://www.spaces.ac.cn/archives/5765
model的.fit方法有一个参数是callbacks,这个参数可以传入一些其他待执行的函数,在训练过程中,每一个epoch会调用一次列表中的callbacks 在下面这个例子中设置monitor='val_acc'来保存训练过程中验证集准确率最高的模型
代码语言:javascript复制checkpoint = ModelCheckpoint(filepath='./best_model.weights',
monitor='val_acc',
verbose=1,
save_best_only=True)
model.fit(x_train,
y_train,
epochs=10,
validation_data=(x_test, y_test),
callbacks=[checkpoint])
训练过程中保存文件
深度学习有可能需要跑很长时间,如果中间断了(特别是在竞价式实例上跑的时候)就要亲命了。本章关于在训练时中途保存模型。
- https://yq.aliyun.com/articles/599528
- https://keras.io/zh/callbacks/#_4
# checkpoint
# https://keras.io/zh/callbacks/
# 如果验证损失下降, 那么在每个训练轮之后保存模型
ArgName=',epo=' str(epoch) ',bsize=' str(batch_size) ',lr=' str(LearningRate) ',DropRate=' str(DropoutRate)
FileNamePy=os.path.basename(__file__).split('.')[-2]
checkpoint_filepath = FileNamePy ArgName
checkpointer_val_best = ModelCheckpoint(filepath=checkpoint_filepath, monitor='val_acc',
verbose=1, save_best_only=True, mode='max', save_weights_only=True)
callbacks_list = [checkpointer_val_best]
hist=model.fit_generator(
train_generator,
steps_per_epoch=nb_train_samples,
epochs=epoch,
validation_data=validation_generator,
validation_steps=nb_validation_samples,
callbacks = callbacks_list)
这种方法虽然简单,但是有一个明显的缺点,就是里边的指标是由compile的metrics来确定的,而Keres中自定义一个metric,需要写成张量运算才行,也就是说如果你期望的指标并不能写成张量运算(比如bleu等指标),那么就没法写成一个metric函数了,也就不能用这个方案了。 by:苏剑林
苏神提供了一个方案:自己写回调器
代码语言:javascript复制from keras.callbacks import Callback
def evaluate(): # 评测函数
pred = model.predict(x_test)
return np.mean(pred.argmax(axis=1) == y_test) # 爱算啥就算啥
# 定义Callback器,计算验证集的acc,并保存最优模型
class Evaluate(Callback):
def __init__(self):
self.accs = []
self.highest = 0.
def on_epoch_end(self, epoch, logs=None):
acc = evaluate()
self.accs.append(acc)
if acc >= self.highest: # 保存最优模型权重
self.highest = acc
model.save_weights('best_model.weights')
# 爱运行什么就运行什么
print 'acc: %s, highest: %s' % (acc, self.highest)
evaluator = Evaluate()
model.fit(x_train,
y_train,
epochs=10,
callbacks=[evaluator])
训练过程中还有可能对超参数进行微调,比如最常见的一个需求是根据epoch来调整学习率,这可以简单地通过LearningRateScheduler来实现,它也属于回调器之一。这个方案也是苏神的~
代码语言:javascript复制from keras.callbacks import LearningRateScheduler
def lr_schedule(epoch):
# 根据epoch返回不同的学习率
if epoch < 50:
lr = 1e-2
elif epoch < 80:
lr = 1e-3
else:
lr = 1e-4
return lr
lr_scheduler = LearningRateScheduler(lr_schedule)
model.fit(x_train,
y_train,
epochs=10,
callbacks=[evaluator, lr_scheduler])
更多例子可以看苏神博客:https://www.spaces.ac.cn/
在keras中多种数据读取的方法
- FancyKeras-数据的输入(传统)
- FancyKeras-数据的输入(花式)
自定义loss函数
Keras中自定义复杂的loss函数
使用Lambda层让你的keras网络更加灵活
- https://keras.io/layers/writing-your-own-keras-layers/
- https://keras-cn.readthedocs.io/en/latest/layers/core_layer/#lambda
- Lambda层
- Keras 自定义层
keras的Lambda层的导入和函数原型:
from keras.layers.core import Lambda
keras.layers.core.Lambda(function, output_shape=None, mask=None, arguments=None)
参数的含义:
function: 要实现的函数,该函数仅接受一个变量,即上一层的输出
output_shape: 函数应该返回值的shape,可以是一个tuple,也可以是一个根据输入shape
mask: 掩膜
arguments: 可选参数,字典,用来记录向函数中传递的其他关键字参数
例子:
代码语言:javascript复制# add a x -> x^2 layer
model.add(Lambda(lambda x: x ** 2))
代码语言:javascript复制# add a layer that returns the concatenation
# of the positive part of the input and
# the opposite of the negative part
def antirectifier(x):
x -= K.mean(x, axis=1, keepdims=True)
x = K.l2_normalize(x, axis=1)
pos = K.relu(x)
neg = K.relu(-x)
return K.concatenate([pos, neg], axis=1)
def antirectifier_output_shape(input_shape):
shape = list(input_shape)
assert len(shape) == 2 # only valid for 2D tensors
shape[-1] *= 2
return tuple(shape)
model.add(Lambda(antirectifier,
output_shape=antirectifier_output_shape))
代码语言:javascript复制# 对于简单的定制操作,可以通过使用layers.core.Lambda层来完成。
# 该方法的适用情况:仅对流经该层的数据做个变换,而这个变换本身没有需要学习的参数
# 切片后再分别进行embedding和average pooling
import numpy as np
from keras.models import Sequential
from keras.layers import Dense, Activation,Reshape
from keras.layers import merge
from keras.utils import plot_model
from keras.layers import *
from keras.models import Model
def get_slice(x, index):
return x[:, index]
keep_num = 3
field_lens = 90
input_field = Input(shape=(keep_num, field_lens))
avg_pools = []
for n in range(keep_num):
block = Lambda(get_slice,output_shape=(1,field_lens),arguments={'index':n})(input_field)
x_emb = Embedding(input_dim=100, output_dim=200, input_length=field_lens)(block)
x_avg = GlobalAveragePooling1D()(x_emb)
avg_pools.append(x_avg)
output = concatenate([p for p in avg_pools])
model = Model(input_field, output)
plot_model(model, to_file='model/lambda.png',show_shapes=True)
plt.figure(figsize=(21, 12))
im = plt.imread('model/lambda.png')
plt.imshow(im)
代码语言:javascript复制# 对于具有可训练权重的定制层,需要自己来实现。
from keras import backend as K
from keras.engine.topology import Layer
import numpy as np
class MyLayer(Layer):
def __init__(self, output_dim, **kwargs):
self.output_dim = output_dim
super(MyLayer, self).__init__(**kwargs)
def build(self, input_shape):
# Create a trainable weight variable for this layer.
self.kernel = self.add_weight(name='kernel',
shape=(input_shape[1], self.output_dim),
initializer='uniform',
trainable=True)
super(MyLayer, self).build(input_shape) # Be sure to call this somewhere!
def call(self, x):
return K.dot(x, self.kernel)
def compute_output_shape(self, input_shape):
return (input_shape[0], self.output_dim)
总结一下,keras的Lambda层就是一个层,允许用户自定义对上层输入数据的操作,自定义操作通过keras.layers.core.Lambda的function进行