Skip to content

Commit

Permalink
add confusionmatrix
Browse files Browse the repository at this point in the history
  • Loading branch information
wz authored and wz committed May 28, 2020
1 parent 38f165a commit 738f31a
Show file tree
Hide file tree
Showing 6 changed files with 225 additions and 112 deletions.
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -9,4 +9,5 @@ flower_data
*.ckpt.*
*.ckpt
*.config
data
checkpoint
data
6 changes: 4 additions & 2 deletions pytorch_classification/ConfusionMatrix/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
class ConfusionMatrix(object):
"""
注意,如果显示的图像不全,是matplotlib版本问题
本例程使用matplotlib-3.2.1绘制正常
本例程使用matplotlib-3.2.1(windows and ubuntu)绘制正常
"""
def __init__(self, num_classes: int, labels: list):
self.matrix = np.zeros((num_classes, num_classes))
Expand Down Expand Up @@ -78,7 +78,7 @@ def plot(self, normalize=False):
net = MobileNetV2(num_classes=5)
# load pretrain weights
model_weight_path = "./MobileNetV2.pth"
net.load_state_dict(torch.load(model_weight_path))
net.load_state_dict(torch.load(model_weight_path, map_location=device))
net.to(device)

# read class_indict
Expand All @@ -99,4 +99,6 @@ def plot(self, normalize=False):
outputs = torch.softmax(outputs, dim=1)
outputs = torch.argmax(outputs, dim=1)
confusion.update(outputs.numpy(), val_labels.numpy())
confusion.plot()
confusion.plot(True)

109 changes: 0 additions & 109 deletions pytorch_classification/ConfusionMatrix/train.py

This file was deleted.

7 changes: 7 additions & 0 deletions tensorflow_classification/ConfusionMatrix/class_indices.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"0": "daisy",
"1": "dandelion",
"2": "roses",
"3": "sunflowers",
"4": "tulips"
}
114 changes: 114 additions & 0 deletions tensorflow_classification/ConfusionMatrix/main.py
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)
98 changes: 98 additions & 0 deletions tensorflow_classification/ConfusionMatrix/model.py
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

0 comments on commit 738f31a

Please sign in to comment.