几乎每个介绍Keras"入门"的指南,都会用keras内置的数据集来做例子。往往,我们跟着“指南”学完了教程,仍然无法使用自己的图片数据集来训练自己的模型。所以有了这篇博客Keras Tutorial: How to get started with Keras, Deep Learning, and Python。
对于大数据集,可以参考以下博客:
- https://medium.com/datadriveninvestor/keras-training-on-large-datasets-3e9d9dbc09d4
- https://github.com/keras-team/keras/issues/107#issuecomment-100585794
我的实现
自定义数据生成器
#! -*- coding: utf-8 -*-
from skimage.io import imread
from skimage.transform import resize
from tensorflow import keras as k
from PIL import Image
import numpy as np
import random
import math
class MyGenerator(k.utils.Sequence):
def __init__(self, filename, batch_size, shuffle, size):
self.filename = filename
self.batch_size = batch_size
self.shuffle = shuffle
self.size = size
self._init()
def _init(self):
self.items = []
for line in open(self.filename):
name, label = line.strip("\n").split()
self.items.append((name, int(label)))
if self.shuffle:
random.shuffle(self.items)
if isinstance(self.size, int):
self.size = (self.size, self.size)
elif isinstance(self.size, tuple) or isinstance(self.size, list):
assert len(self.size) == 2
@staticmethod
def parse_fn(filename):
image = Image.open(filename)
image = image.convert("RGB")
image = np.array(image)
return image
def __len__(self):
return math.ceil(len(self.items) / float(self.batch_size))
def __getitem__(self, idx):
item = self.items[idx * self.batch_size: (idx + 1) * self.batch_size]
batch_x, batch_y = zip(*item)
# return np.array([resize(imread(filename), self.size) for filename in batch_x]), np.array(batch_y)
return np.array([self.parse_fn(filename) for filename in batch_x]), np.array(batch_y)
def on_epoch_end(self):
"""在每个epoch结束时,调用该函数"""
print(" Epoch_end and reshuffle")
random.shuffle(self.items)
if __name__ == '__main__':
gen = MyGenerator("data/fer2013_train.txt", 64, shuffle=True, size=48)
x, y = next(iter(gen))
print(x.shape)
print(y.shape)
xxx.txt内容如下:
/home/zwx/Documents/Data/fer2013/train/0/24441.jpg 0
/home/zwx/Documents/Data/fer2013/train/0/24383.jpg 0
/home/zwx/Documents/Data/fer2013/train/0/22685.jpg 0
/home/zwx/Documents/Data/fer2013/train/0/10511.jpg 0
/home/zwx/Documents/Data/fer2013/train/0/05162.jpg 0
/home/zwx/Documents/Data/fer2013/train/0/18565.jpg 0
/home/zwx/Documents/Data/fer2013/train/0/18715.jpg 0
/home/zwx/Documents/Data/fer2013/train/0/05432.jpg 0
/home/zwx/Documents/Data/fer2013/train/0/11153.jpg 0
/home/zwx/Documents/Data/fer2013/train/0/20802.jpg 0
/home/zwx/Documents/Data/fer2013/train/0/25642.jpg 0
/home/zwx/Documents/Data/fer2013/train/0/19743.jpg 0
/home/zwx/Documents/Data/fer2013/train/0/22733.jpg 0
/home/zwx/Documents/Data/fer2013/train/0/28511.jpg 0
/home/zwx/Documents/Data/fer2013/train/0/28526.jpg 0
/home/zwx/Documents/Data/fer2013/train/0/25897.jpg 0
/home/zwx/Documents/Data/fer2013/train/0/18176.jpg 0
/home/zwx/Documents/Data/fer2013/train/0/12294.jpg 0
/home/zwx/Documents/Data/fer2013/train/0/26549.jpg 0
/home/zwx/Documents/Data/fer2013/train/0/28279.jpg 0
/home/zwx/Documents/Data/fer2013/train/0/25928.jpg 0
/home/zwx/Documents/Data/fer2013/train/0/01123.jpg 0
/home/zwx/Documents/Data/fer2013/train/0/21576.jpg 0
/home/zwx/Documents/Data/fer2013/train/0/06442.jpg 0
....
自定义数据生成器+数据增强
#! -*- coding: utf-8 -*-
from skimage.io import imread
from skimage.transform import resize
from tensorflow import keras as k
from PIL import Image
import numpy as np
import random
import math
class MyGenerator(k.utils.Sequence):
def __init__(self, filename, batch_size, shuffle, size, aug=None):
self.filename = filename
self.batch_size = batch_size
self.shuffle = shuffle
self.size = size
self.aug = aug
self._init()
def _init(self):
self.items = []
for line in open(self.filename):
name, label = line.strip("\n").split()
self.items.append((name, int(label)))
if self.shuffle:
random.shuffle(self.items)
if isinstance(self.size, int):
self.size = (self.size, self.size)
elif isinstance(self.size, tuple) or isinstance(self.size, list):
assert len(self.size) == 2
def parse_fn(self, filename):
image = Image.open(filename)
image = image.resize(self.size)
image = image.convert("RGB")
image = np.array(image)
return image
def __len__(self):
return math.ceil(len(self.items) / float(self.batch_size))
def __getitem__(self, idx):
item = self.items[idx * self.batch_size: (idx + 1) * self.batch_size]
batch_x, batch_y = zip(*item)
# return np.array([resize(imread(filename), self.size) for filename in batch_x]), np.array(batch_y)
batch_x = np.array([self.parse_fn(filename) for filename in batch_x])
batch_y = np.array(batch_y)
if aug:
batch_x, batch_y = next(aug.flow(batch_x, batch_y))
return batch_x, batch_y
def on_epoch_end(self):
"""在每个epoch结束时,调用该函数"""
print(" Epoch_end and reshuffle")
random.shuffle(self.items)
if __name__ == '__main__':
# Data Generator
aug = k.preprocessing.image.ImageDataGenerator(rotation_range=20,
zoom_range=0.15,
width_shift_range=0.2,
height_shift_range=0.2,
shear_range=0.15,
horizontal_flip=True,
fill_mode="nearest")
gen = MyGenerator("data/fer2013_train.txt", 64, shuffle=True, size=224)
x, y = next(iter(gen))
print(x.shape)
print(y.shape)
训练模型代码如下
#! -*- coding: utf-8 -*-
from tensorflow import keras as k
from dataset import MyGenerator
TRAIN_FILE = "data/fer2013_train.txt"
VAL_FILE = "data/fer2013_val.txt"
TEST_FILE = "data/fer2013_test.txt"
IMAGE_SIZE = 48
IMAGE_SHAPE = (48, 48, 3)
def main():
train_gen = MyGenerator(TRAIN_FILE, 64, shuffle=True, size=IMAGE_SIZE)
val_gen = MyGenerator(VAL_FILE, 128, shuffle=False, size=IMAGE_SIZE)
# test_gen = MyGenerator(TEST_FILE, 128, shuffle=False, size=IMAGE_SIZE)
# 函数式API定义模型
inputs = k.Input(shape=(48, 48, 3))
x = k.layers.Conv2D(32, 5, 2, "SAME", activation="relu", name="conv1")(inputs)
x = k.layers.BatchNormalization(momentum=0.9)(x)
print(x.shape)
x = k.layers.MaxPool2D(2, 2, "SAME", name="pool1")(x)
print(x.shape)
x = k.layers.Conv2D(64, 3, 1, "SAME", activation="relu", name="conv2")(x)
x = k.layers.BatchNormalization(momentum=0.9)(x)
print(x.shape)
x = k.layers.MaxPool2D(2, 2, "SAME", name="pool2")(x)
print(x.shape)
x = k.layers.BatchNormalization(momentum=0.9)(x)
x = k.layers.Conv2D(128, 3, 1, "SAME", activation="relu", name="conv3")(x)
print(x.shape)
x = k.layers.MaxPool2D(2, 2, "SAME", name="pool3")(x)
print(x.shape)
x = k.layers.Flatten(name="flatten")(x)
print(x.shape)
x = k.layers.Dense(1024, activation="relu", name="fc1")(x)
print(x.shape)
x = k.layers.Dense(7, activation="softmax", name="output")(x)
print(x.shape)
model = k.Model(inputs, x)
model.compile(optimizer=k.optimizers.Adam(0.001),
loss=k.losses.sparse_categorical_crossentropy,
metrics=["accuracy"])
model.fit_generator(generator=train_gen,
steps_per_epoch=len(train_gen),
epochs=100,
validation_data=val_gen,
validation_steps=len(val_gen),
use_multiprocessing=True,
max_queue_size=32)
if __name__ == '__main__':
main()
更新——20190516
快速训练模型,只需要下面的几十行代码
#! -*- coding: utf-8 -*-
from tensorflow import keras as k
# from skimage.io import imread
# from skimage.transform import resize
from PIL import Image
import tensorflow as tf
import numpy as np
import random
import math
import os
import shutil
class Generator(k.utils.Sequence):
def __init__(self, filename, batch_size, shuffle, image_size):
self.filename = filename
self.batch_size = batch_size
self.shuffle = shuffle
self.image_size = image_size
self._init()
def _init(self):
self.items = []
for line in open(self.filename):
name, label = line.strip("\n").split()
self.items.append((name, label))
if self.shuffle:
random.shuffle(self.items)
def _parse_fn(self, filename):
image = Image.open(filename)
image = image.resize(self.image_size)
image = np.array(image)
return image
def __len__(self):
return math.ceil(len(self.items) / float(self.batch_size))
def __getitem__(self, idx):
print(" ----", idx)
batch = self.items[idx * self.batch_size: (idx + 1) * self.batch_size]
names, labels = zip(*batch)
# x_batch = np.array([resize(imread(filename), self.image_size) for filename in names]) # 效率低
x_batch = np.array([self._parse_fn(filename) for filename in names])
return x_batch, np.array(labels)
def mkdir(dir_name, delete):
if os.path.exists(dir_name):
if delete:
shutil.rmtree(dir_name)
os.makedirs(dir_name)
else:
os.makedirs(dir_name)
TRAIN_FILE = "data/train_precision.txt"
VAL_FILE = "data/test_precision.txt"
CHECKPOIN_DIR = "../experiments/spam_normal_v1/checkpoints/weights.{epoch:02d}-{val_loss:.2f}.hdf5"
LOG_DIR = "../experiments/spam_normal_v1/logs/"
mkdir(CHECKPOIN_DIR, delete=False)
mkdir(LOG_DIR, delete=True)
# 全局变量
IMAGE_SIZE = (224, 224)
IMAGE_SHAPE = (224, 224, 3)
# GPU资源配置
gpu_config = tf.GPUOptions(
allow_growth=True,
per_process_gpu_memory_fraction=0.95,
)
gpu_config = tf.ConfigProto(
log_device_placement=False,
allow_soft_placement=True,
gpu_options=gpu_config,
)
def main():
# 配置GPU
k.backend.set_session(tf.Session(config=gpu_config))
# 数据生成器
train_gen = Generator(TRAIN_FILE, 64, True, IMAGE_SIZE)
val_gen = Generator(VAL_FILE, 128, False, IMAGE_SIZE)
# 定义模型
base_model = k.applications.InceptionResNetV2(include_top=False, pooling="avg", input_shape=IMAGE_SHAPE)
model = k.Sequential([
base_model,
k.layers.Dense(2, activation="softmax")
])
# 编译模型
model.compile(
optimizer=k.optimizers.Adam(0.001),
loss=k.losses.sparse_categorical_crossentropy,
metrics=["accuracy"]
)
# 回调函数
callbacks = [
k.callbacks.EarlyStopping(monitor="val_loss", patience=5),
k.callbacks.TensorBoard(LOG_DIR, write_images=True),
k.callbacks.ModelCheckpoint(CHECKPOIN_DIR, monitor="val_loss", save_best_only=True, save_weights_only=False),
]
# 训练模型
model.fit_generator(train_gen,
epochs=1000,
steps_per_epoch=len(train_gen),
validation_data=val_gen,
validation_steps=len(val_gen),
max_queue_size=10,
workers=8,
use_multiprocessing=True,
callbacks=callbacks)
if __name__ == '__main__':
main()
部署
https://www.pyimagesearch.com/2018/01/29/scalable-keras-deep-learning-rest-api/
run_keras_server.py
#! -*- coding: utf-8 -*-
from tensorflow import keras as k
from PIL import Image
import numpy as np
import base64
import flask
import sys
import io
app = flask.Flask(__name__)
model = None
def load_model():
global model
model = k.applications.ResNet50(weights="imagenet")
def prepare_image(image, target):
if image.mode != 'RGB':
image = image.convert("RGB")
image = image.resize(target)
image = k.preprocessing.image.img_to_array(image)
image = np.expand_dims(image, axis=0)
image = k.applications.resnet50.preprocess_input(image)
return image
def base64_encode_image(image):
# base64 encode the input NumPy array
return base64.b64decode(image).decode("utf-8")
def base64_decode_image(image, dtype, shape):
# if this is Python 3, we need the extra step of encoding the
# serialized NumPy string as a byte object
if sys.version_info.major == 3:
image = bytes(image, encoding="utf-8")
# convert the string to a NumPy array using the supplied data
# type and target shape
image = np.frombuffer(base64.decodebytes(image), dtype=dtype)
image = image.reshape(shape)
return image
@app.route("/predict", methods=["POST"])
def predict():
# initialize the data dictionary that will be returned from the
# view
data = {"success": False}
# ensure an image was properly uploaded to our endpoint
if flask.request.method == "POST":
if flask.request.files.get("image"):
# read the image in PIL format
image = flask.request.files["image"].read()
image = Image.open(io.BytesIO(image))
# preprocess the image and prepare it for classification
image = prepare_image(image, target=(224, 224))
# classify the input image and then initialize the list
# of predictions to return to the client
preds = model.predict(image)
results = k.applications.resnet50.decode_predictions(preds)
data["predictions"] = []
# loop over the results and add them to the list of
# returned predictions
for (imagenetID, label, prob) in results[0]:
r = {"label": label, "probability": float(prob)}
data["predictions"].append(r)
# indicate that the request was a success
data["success"] = True
# return the data dictionary as a JSON response
return flask.jsonify(data)
if __name__ == '__main__':
print(("* Loading Keras model and Flask starting server..."
"please wait until server has fully started"))
load_model()
app.run(debug=False, threaded=False)
调用服务
curl -X POST -F image=@dog.jpg 'http://127.0.0.1:5000/predict'
结果
{
"predictions": [{
"label": "beagle",
"probability": 0.987775444984436
}, {
"label": "pot",
"probability": 0.0020967808086425066
}, {
"label": "Cardigan",
"probability": 0.001351703773252666
}, {
"label": "Walker_hound",
"probability": 0.0012711131712421775
}, {
"label": "Brittany_spaniel",
"probability": 0.0010085132671520114
}],
"success": true
}