forked from WZMIAOMIAO/deep-learning-for-image-processing
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
wz
authored and
wz
committed
May 28, 2020
1 parent
38f165a
commit 738f31a
Showing
6 changed files
with
225 additions
and
112 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -9,4 +9,5 @@ flower_data | |
*.ckpt.* | ||
*.ckpt | ||
*.config | ||
data | ||
checkpoint | ||
data |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
{ | ||
"0": "daisy", | ||
"1": "dandelion", | ||
"2": "roses", | ||
"3": "sunflowers", | ||
"4": "tulips" | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,114 @@ | ||
from tensorflow.keras.preprocessing.image import ImageDataGenerator | ||
import matplotlib.pyplot as plt | ||
from model import MobileNetV2 | ||
import tensorflow as tf | ||
import json | ||
import os | ||
import math | ||
import numpy as np | ||
|
||
|
||
class ConfusionMatrix(object): | ||
""" | ||
注意,如果显示的图像不全,是matplotlib版本问题 | ||
本例程使用matplotlib-3.2.1(windows and ubuntu)绘制正常 | ||
""" | ||
def __init__(self, num_classes: int, labels: list): | ||
self.matrix = np.zeros((num_classes, num_classes)) | ||
self.num_classes = num_classes | ||
self.labels = labels | ||
|
||
def update(self, preds, labels): | ||
for p, t in zip(preds, labels): | ||
self.matrix[t, p] += 1 | ||
|
||
def plot(self, normalize=False): | ||
matrix = self.matrix | ||
plot_title = 'Confusion matrix' | ||
if normalize: | ||
plot_title = 'Normalized Confusion matrix' | ||
# 去除axis=1纬度,即求每行元素的和 | ||
matrix = matrix / matrix.sum(axis=1)[:, np.newaxis] | ||
print(matrix) | ||
plt.imshow(matrix, cmap=plt.cm.Blues) | ||
|
||
# 设置x轴坐标label | ||
plt.xticks(range(self.num_classes), self.labels, rotation=45) | ||
# 设置y轴坐标label | ||
plt.yticks(range(self.num_classes), self.labels) | ||
# 显示colorbar | ||
plt.colorbar() | ||
plt.xlabel('Predicted Labels') | ||
plt.ylabel('True Labels') | ||
plt.title(plot_title) | ||
|
||
# 在图中标注数量/概率信息 | ||
thresh = matrix.max() / 2 | ||
for x in range(self.num_classes): | ||
for y in range(self.num_classes): | ||
# 注意这里的matrix[y, x]不是matrix[x, y] | ||
info = round(matrix[y, x], 2) if normalize else int(matrix[y, x]) | ||
plt.text(x, y, info, | ||
verticalalignment='center', | ||
horizontalalignment='center', | ||
color="white" if info > thresh else "black") | ||
plt.tight_layout() | ||
plt.show() | ||
|
||
|
||
if __name__ == '__main__': | ||
data_root = os.path.abspath(os.path.join(os.getcwd(), "../..")) # get data root path | ||
image_path = data_root + "/data_set/flower_data/" # flower data set path | ||
train_dir = image_path + "train" | ||
validation_dir = image_path + "val" | ||
|
||
im_height = 224 | ||
im_width = 224 | ||
batch_size = 16 | ||
|
||
|
||
def pre_function(img): | ||
# img = im.open('test.jpg') | ||
# img = np.array(img).astype(np.float32) | ||
img = img / 255. | ||
img = (img - 0.5) * 2.0 | ||
return img | ||
|
||
|
||
# data generator with data augmentation | ||
validation_image_generator = ImageDataGenerator(preprocessing_function=pre_function) | ||
|
||
val_data_gen = validation_image_generator.flow_from_directory(directory=validation_dir, | ||
batch_size=batch_size, | ||
shuffle=False, | ||
target_size=(im_height, im_width), | ||
class_mode='categorical') | ||
# img, _ = next(train_data_gen) | ||
total_val = val_data_gen.n | ||
|
||
model = MobileNetV2(num_classes=5) | ||
# feature.build((None, 224, 224, 3)) # when using subclass model | ||
model.load_weights('myMobileNet.ckpt') | ||
|
||
# read class_indict | ||
try: | ||
json_file = open('./class_indices.json', 'r') | ||
class_indict = json.load(json_file) | ||
except Exception as e: | ||
print(e) | ||
exit(-1) | ||
|
||
labels = [label for _, label in class_indict.items()] | ||
confusion = ConfusionMatrix(num_classes=5, labels=labels) | ||
|
||
# validate | ||
for step in range(math.ceil(total_val / batch_size)): | ||
val_images, val_labels = next(val_data_gen) | ||
print(len(val_labels)) | ||
results = model.predict_on_batch(val_images) | ||
results = tf.keras.layers.Softmax()(results).numpy() | ||
results = np.argmax(results, axis=-1) | ||
labels = np.argmax(val_labels, axis=-1) | ||
confusion.update(results, labels) | ||
confusion.plot() | ||
confusion.plot(True) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,98 @@ | ||
from tensorflow.keras import layers, Model, Sequential | ||
|
||
|
||
def _make_divisible(ch, divisor=8, min_ch=None): | ||
""" | ||
This function is taken from the original tf repo. | ||
It ensures that all layers have a channel number that is divisible by 8 | ||
It can be seen here: | ||
https://github.com/tensorflow/models/blob/master/research/slim/nets/mobilenet/mobilenet.py | ||
""" | ||
if min_ch is None: | ||
min_ch = divisor | ||
new_ch = max(min_ch, int(ch + divisor / 2) // divisor * divisor) | ||
# Make sure that round down does not go down by more than 10%. | ||
if new_ch < 0.9 * ch: | ||
new_ch += divisor | ||
return new_ch | ||
|
||
|
||
class ConvBNReLU(layers.Layer): | ||
def __init__(self, out_channel, kernel_size=3, stride=1, **kwargs): | ||
super(ConvBNReLU, self).__init__(**kwargs) | ||
self.conv = layers.Conv2D(filters=out_channel, kernel_size=kernel_size, | ||
strides=stride, padding='SAME', use_bias=False, name='Conv2d') | ||
self.bn = layers.BatchNormalization(momentum=0.9, epsilon=1e-5, name='BatchNorm') | ||
self.activation = layers.ReLU(max_value=6.0) | ||
|
||
def call(self, inputs, training=False, **kwargs): | ||
x = self.conv(inputs) | ||
x = self.bn(x, training=training) | ||
x = self.activation(x) | ||
return x | ||
|
||
|
||
class InvertedResidual(layers.Layer): | ||
def __init__(self, in_channel, out_channel, stride, expand_ratio, **kwargs): | ||
super(InvertedResidual, self).__init__(**kwargs) | ||
self.hidden_channel = in_channel * expand_ratio | ||
self.use_shortcut = stride == 1 and in_channel == out_channel | ||
|
||
layer_list = [] | ||
if expand_ratio != 1: | ||
# 1x1 pointwise conv | ||
layer_list.append(ConvBNReLU(out_channel=self.hidden_channel, kernel_size=1, name='expand')) | ||
layer_list.extend([ | ||
# 3x3 depthwise conv | ||
layers.DepthwiseConv2D(kernel_size=3, padding='SAME', strides=stride, | ||
use_bias=False, name='depthwise'), | ||
layers.BatchNormalization(momentum=0.9, epsilon=1e-5, name='depthwise/BatchNorm'), | ||
layers.ReLU(max_value=6.0), | ||
# 1x1 pointwise conv(linear) | ||
layers.Conv2D(filters=out_channel, kernel_size=1, strides=1, | ||
padding='SAME', use_bias=False, name='project'), | ||
layers.BatchNormalization(momentum=0.9, epsilon=1e-5, name='project/BatchNorm') | ||
]) | ||
self.main_branch = Sequential(layer_list, name='expanded_conv') | ||
|
||
def call(self, inputs, **kwargs): | ||
if self.use_shortcut: | ||
return inputs + self.main_branch(inputs) | ||
else: | ||
return self.main_branch(inputs) | ||
|
||
|
||
def MobileNetV2(im_height=224, im_width=224, num_classes=1000, alpha=1.0, round_nearest=8): | ||
block = InvertedResidual | ||
input_channel = _make_divisible(32 * alpha, round_nearest) | ||
last_channel = _make_divisible(1280 * alpha, round_nearest) | ||
inverted_residual_setting = [ | ||
# t, c, n, s | ||
[1, 16, 1, 1], | ||
[6, 24, 2, 2], | ||
[6, 32, 3, 2], | ||
[6, 64, 4, 2], | ||
[6, 96, 3, 1], | ||
[6, 160, 3, 2], | ||
[6, 320, 1, 1], | ||
] | ||
|
||
input_image = layers.Input(shape=(im_height, im_width, 3), dtype='float32') | ||
# conv1 | ||
x = ConvBNReLU(input_channel, stride=2, name='Conv')(input_image) | ||
# building inverted residual residual blockes | ||
for t, c, n, s in inverted_residual_setting: | ||
output_channel = _make_divisible(c * alpha, round_nearest) | ||
for i in range(n): | ||
stride = s if i == 0 else 1 | ||
x = block(x.shape[-1], output_channel, stride, expand_ratio=t)(x) | ||
# building last several layers | ||
x = ConvBNReLU(last_channel, kernel_size=1, name='Conv_1')(x) | ||
|
||
# building classifier | ||
x = layers.GlobalAveragePooling2D()(x) # pool + flatten | ||
x = layers.Dropout(0.2)(x) | ||
output = layers.Dense(num_classes, name='Logits')(x) | ||
|
||
model = Model(inputs=input_image, outputs=output) | ||
return model |