目录
2. Pseudo-Label Method for Deep Neural Networks(深度神经网络的伪标记法)
2.1. Deep Neural Networks(深度神经网络)
2.2. Denoising Auto-Encoder(去噪自编码器)
3. Why could Pseudo-Label work?(为什么伪标签能起作用)
3.1. Low-Density Separation between Classes(类间的低密度分离)
3.2. Entropy Regularization(熵正则化)
3.3. Training with Pseudo-Label as Entropy Regularization(用伪标签作为熵正则化的训练)
4.1. Handwritten Digit Recognition (MNIST)(手写数字识别)
3.1 Comparison to other methods on SVHN and CIFAR-10(在SVHN和CIFAR-10数据集上与其他方法的比较)
3.2 SVHN with extra unlabeled data(实验SVHN中额外的无标签数据)
3.3 Analysis of the training curves(训练曲线分析)
本周完成的计划
- 读论文《Pseudo-Label : The Simple and Efficient Semi-Supervised Learning Method for Deep Neural Networks》
- 读论文《Mean teachers: Weight-averaged consistency targets improve semi-supervised》
- 跑模型:DeepLabv3+,Deeplab系列网络是由Google所提出,V2以后主要是引入了atrous spatial pyramid pooling(ASPP),利用不同膨胀因子的空洞卷积融合多尺度信息。其实就是利用空洞卷积(带孔卷积)用不同的rate来对图像特征进行操作的。
论文阅读1
Pseudo-Label : The Simple and Efficient Semi-Supervised Learning Method for Deep Neural Networks(伪标签:一种简单高效的深度神经网络半监督学习方法)
Abstract(摘要)
提出了一种简单有效的深度神经网络半监督学习方法。基本上,所提出的网络是以有监督的方式同时(simultaneously)使用标记和未标记的数据来训练的。对于未标记的数据,只需选取预测概率最大的类别,就可以使用伪标签(Pseudo-Labels),就好像它们是真标签一样。这实际上等同于熵正则化(Entropy Regularization)。它支持类之间的低密度分离(low-density separation),这是半监督学习通常假定的先验条件。在MNIST手写体数字数据集上,利用去噪自动编码器和Dropout,这种简单的方法在标签数据非常少的情况下优于传统的半监督学习方法。
1. Introduction(介绍)
在本文中,我们提出了一种以半监督方式训练神经网络的更简单的方法。对于未标记的数据,只需选取每次权重更新具有最大预测概率的类,就可以使用伪标签,就好像它们是真标签一样。该方法原则上可以结合几乎所有的神经网络模型和训练方法。这种方法实际上等同于熵正则化,类概率的条件熵可以用于类重叠的度量。通过最小化未标记数据的熵,可以减少类概率分布的重叠。它支持类之间的低密度分离,这是半监督学习通常假设的先验条件。
在著名的MNIST数据集上的实验表明,该方法具有最好的(state-of-the-art)性能。
2. Pseudo-Label Method for Deep Neural Networks(深度神经网络的伪标记法)
2.1. Deep Neural Networks(深度神经网络)
伪标签是一种以半监督方式训练深度神经网络的方法。在本文中,我们将考虑具有M层隐藏单元的多层神经网络:
是第k层的非线性激活函数,例如Sigmoid,整个网络可以通过最小化有监督损失函数来训练。
其中C是标签的个数,x是输入向量,我们可以选择Cross Entropyy作为损失函数:
2.2. Denoising Auto-Encoder(去噪自编码器)
去噪自编码器是一种无监督学习算法,它是基于这样的想法的:使学习到的表示(learned representations)对即使是部分破坏的输入也具有鲁棒性(Vincent等人,2008年)。这种方法可以用来训练自动编码器,并且可以将这些DAE堆叠起来以初始化深度神经网络。
是第j个部分损坏的输入值,
是第j个输入值的重构。自动编码器训练在于最小化在
和
之间的重建误差。对于二进制输入值,重建误差的常见选择是交叉熵:
我们在无监督的预训阶段使用DAE,以概率0.5用于masking noise 损坏(corruption)。
2.3. Dropout
辍学是一种可以应用于深度神经网络的监督学习的技术(Hinton等人,2012年)。在每个示例的网络激活上,以0.5的概率随机省略(失活)隐藏单元。有时,20%的可见单元丢失也是有帮助的。
通过该技术可以减少过拟合,以防止对训练数据的隐藏表示进行复杂的共适应(co-adaptations),因为在每次权重更新中,我们通过省略一半隐藏单元来训练不同的子模型。
2.4. Pseudo-Label(伪标签)
未标记数据的伪标签目标类,就像它们是真标签一样。我们只挑选对每个未标记样本具有最大预测概率的类别。
标记数据和未标记数据同时以有监督的方式训练预先训练的网络(pre-trained network)。对于未标记的数据,每次权值更新重新计算的伪标签被用于相同的监督学习任务的损失函数。由于有标签数据和无标签数据的总数有很大不同,并且它们之间的训练平衡对网络性能非常重要,因此总体损失函数为:
n是有标签数据的的小批次数,n'是无标签数据的,ymi是有标签数据的label,fmi是有标签数据的模型的预测值,y'mi是伪标签,f 'mi是无标签数据模型的预测值,α(t)是平衡它们的系数。
α(t)的合理调度对网络性能至关重要。如果α(t)太高,即使对于已标记的数据,也会干扰训练。而如果α(t)太小,我们就不能利用未标记数据的好处。此外,α(t)缓慢增加的确定性退火过程有望帮助优化过程避免较差的局部极小值,从而使未标记数据的伪标签尽可能类似于真实标签。
3. Why could Pseudo-Label work?(为什么伪标签能起作用)
3.1. Low-Density Separation between Classes(类间的低密度分离)
半监督学习的目标是利用未标记数据提高泛化性能(generalization performance )。聚类假设指出,决策边界应该位于低密度区域,以提高泛化性能。半监督嵌入(Weston et al.,2008)使用基于嵌入的正则化来提高深度神经网络的泛化性能。由于数据样本的邻域通过嵌入惩罚项与样本具有相似的激活,高密度区域中的数据样本更有可能具有相同的标签。
3.2. Entropy Regularization(熵正则化)
熵正则化(Grandvalet et al.,2006)是一种在最大后验估计框架下受益于未标记数据的方法。该方案通过最小化未标记数据的类概率的条件熵,支持类之间的低密度分离,而无需对密度进行任何建模。
熵是类重叠的度量。随着类重叠的减少,决策边界上的数据点密度变得更低。
通过最大化已标记数据(第一项)的条件对数似然和最小化未标记数据(第二项)的熵,利用未标记数据可以获得更好的泛化性能。
3.3. Training with Pseudo-Label as Entropy Regularization(用伪标签作为熵正则化的训练)
图1示出了t-SNE降维的 MNIST测试数据(未包括在未标记数据中)的网络输出的2D嵌入结果。神经网络用600个带标签的数据训练,有或没有60000个未标记的数据和伪标签。虽然在这两种情况下训练误差为零,但通过使用未标记数据和伪标签进行训练,测试数据的网络输出更加浓缩到1 of K码附近,换言之,熵被最小化。
4. Experiments(实验)
4.1. Handwritten Digit Recognition (MNIST)(手写数字识别)
可以看到加入了伪标签和去噪自编码器是还原最好的效果的。
5. Conclusion(结论)
在这项工作中,我们展示了一种简单有效的神经网络半监督学习方法。该方法在不需要复杂的训练方案和计算代价较高的相似矩阵的情况下,具有最好的性能。
论文阅读2
Mean teachers are better role models: Weight-averaged consistency targets improve semi-supervised deep learning results(Mean teachers 模型是更好的角色模型:加权平均一致性目标改善了半监督深度学习结果)
Abstract(摘要)
最近提出的Temporal Ensembling模型在几个半监督学习基准中取得了最先进的结果。它维护每个训练样本的标签预测的指数移动平均值,并惩罚与该目标不一致的预测。然而,因为目标在每个Epoch只改变一次,所以在学习大型数据集时,Temporal Ensembling变得很笨拙。为了克服这个问题,我们提出了Mean Teacher模型,一种平均模型权重而不是标签预测的方法。作为一个额外的好处,Means Teacher提高了测试的准确性,并且能够使用比Temporal Ensembling更少的标签进行训练。
1. Introduction(介绍)
深度学习在图像和语音识别等领域取得了巨大的成功。为了学习有用的抽象(abstractions),深度学习模型需要大量参数,从而使它们容易过度拟合。此外,手动将高质量的标签添加到训练数据通常是昂贵的。因此,在半监督学习中,需要使用正则化方法来有效地利用未标记数据来减少过拟合。
至少有两种方法可以提高目标质量。一种方法是仔细选择表示的扰动,而不是仅仅施加加性或乘性噪声。另一种方法是仔细选择教师模式,而不是勉强复制学生模式。因此,我们的目标是从学生模型中形成一个更好的教师模型,而不需要额外的培训。
2. Mean Teacher(平均教师)
为了克服Temporal Ensembling的局限性,我们提出平均模型权重而不是预测。由于教师模型是连续学生模型的平均值,我们将其称为均值教师方法(图2)。
图2:平均教师方法。该图描述了一个带有单个标签样本的训练批次。学生模型和教师模型都在其计算范围内评估应用噪声(η,η0)的输入。使用分类成本(classification cost )将学生模型的Softmax输出与One-Hot Label进行比较,并使用一致性成本将其与教师输出进行比较。在用梯度下降更新学生模型的权重之后,教师模型的权重被更新为学生权重的指数移动平均值。这两个模型的输出都可以用于预测,但在培训结束时,教师的预测更有可能是正确的。具有未标记样本的训练步骤将是类似的,只是不会应用分类成本(classification cost )。
教师模型使用学生模型的EMA权重,而不是与学生模型共享权重。现在,它可以在每一步而不是每一个时代之后收集信息。此外,由于权重平均值改善了所有层的输出,而不仅仅是顶部输出,因此目标模型具有更好的中间表示。这些方面导致了超过Temporal Ensembling模型的两个实际优势:第一,更准确的目标标签导致学生和教师模型之间更快的反馈循环,从而产生更好的测试准确性。其次,该方法适用于大型数据集和在线学习。
更形式化地,我们将一致性成本定义为学生模型的预测和教师模型的预测之间的期望距离。
Π模型、Temporal Ensembling和平均教师之间的区别在于教师预测是如何产生的,Π模型使用θ'=θ,Temporal Ensembling使用连续预测的加权平均值来近似,我们将
定义为在训练步长为t时,连续θ权重的EMA值。
3. Experiments(实验)
为了验证我们的假设,我们首先在TensorFlow中复制了Π模型作为基线。然后,我们修改了基线模型,以使用加权平均一致性目标。该模型结构是一个13层卷积神经网络(ConvNet),具有三种类型的噪声:输入图像的随机平移和水平翻转、输入层上的高斯噪声和网络内应用的(Dropout)丢弃噪声。
3.1 Comparison to other methods on SVHN and CIFAR-10(在SVHN和CIFAR-10数据集上与其他方法的比较)
我们使用街景房屋编号和CIFAR-10基准进行了实验,这两个数据集都包含属于十个不同类别的32x32像素RGB图像。
表1和表2将结果与最近最先进的方法进行了比较。比较中的所有方法都使用类似的13层ConvNet架构。相比于Π模型和Temporal Ensembling我们的Mean Teacher提高了在半监督SVHN任务上的测试准确率。Mean Teacher也提高了CIFAR-10的成绩,超过了我们的基线Π模型。
最近出版的Miyato等人的虚拟对抗训练(Virtual Adversarial Training)版本在1000个标签的SVHN和4000个标签的CIFAR-10上的表现甚至比Mean Teacher更好。正如在引言中所讨论的,VAT和Mean Teacher是互补的方法。它们的结合可能会比单独使用它们中的任何一个产生更好的准确性,但这一研究(investigation)超出了本文的讨论范围。
3.2 SVHN with extra unlabeled data(实验SVHN中额外的无标签数据)
如上所述,我们提出的Mean Teacher模型能够很好地适应大数据集和在线学习,SVHN和CIFAR-10的实验结果表明,该算法有效地利用了未标注样本。因此,我们想测试我们是否已经达到了我们方法的极限。表3显示了结果:
3.3 Analysis of the training curves(训练曲线分析)
图3:平滑后的平均教师分类成本(classification cost )(上)和分类错误(classification error)(下),以及我们在前100000个训练步骤中基于SVHN的基线Π模型。
可以从图中看出,教师(蓝色曲线)通过一致性损失(consistency cost)改善学生(橙色),学生通过指数移动平均(exponential moving averaging)改善教师,这似乎是一个良性的反馈循环。如果脱离这个反馈周期,学习速度就会变慢,模型就会更早地开始过度拟合(深灰色和浅灰色)。
4.Conclusion(结论)
在本文中,我们提出了Mean Teacher,一种平均模型权重的方法,以形成一个目标生成型教师模型。与Temporal Ensembling不同的是,Mean Teacher使用的是大数据集和在线学习。实验表明,该算法提高了训练网络的学习速度和分类精度。此外,它还可以很好地扩展到最先进的架构和大图像尺寸。
DeepLabv3+网络代码实现
import os, cv2
import numpy as np
import pandas as pd
import random, tqdm
import seaborn as sns
import matplotlib.pyplot as plt
%matplotlib inline
import warnings
warnings.filterwarnings("ignore")
import torch
import torch.nn as nn
from torch.utils.data import DataLoader
import albumentations as album
# !pip install -q -U segmentation-models-pytorch albumentations > /dev/null
import segmentation_models_pytorch as smp
# Defining train / val / test directories
DATA_DIR = '../input/massachusetts-buildings-dataset/tiff/'
x_train_dir = os.path.join(DATA_DIR, 'train')
y_train_dir = os.path.join(DATA_DIR, 'train_labels')
x_valid_dir = os.path.join(DATA_DIR, 'val')
y_valid_dir = os.path.join(DATA_DIR, 'val_labels')
x_test_dir = os.path.join(DATA_DIR, 'test')
y_test_dir = os.path.join(DATA_DIR, 'test_labels')
class_dict = pd.read_csv("../input/massachusetts-buildings-dataset/label_class_dict.csv")
# Get class names
class_names = class_dict['name'].tolist()
# Get class RGB values
class_rgb_values = class_dict[['r','g','b']].values.tolist()
print('All dataset classes and their corresponding RGB values in labels:')
print('Class Names: ', class_names)
print('Class RGB values: ', class_rgb_values)
# Shortlist specific classes to segment
# Useful to shortlist specific classes in datasets with large number of classes
select_classes = ['background', 'building']
# Get RGB values of required classes
select_class_indices = [class_names.index(cls.lower()) for cls in select_classes]
select_class_rgb_values = np.array(class_rgb_values)[select_class_indices]
print('Selected classes and their corresponding RGB values in labels:')
print('Class Names: ', class_names)
print('Class RGB values: ', class_rgb_values)
# Helper functions for viz. & one-hot encoding/decoding
# helper function for data visualization
def visualize(**images):
"""
Plot images in one row
"""
n_images = len(images)
plt.figure(figsize=(20,8))
for idx, (name, image) in enumerate(images.items()):
plt.subplot(1, n_images, idx + 1)
plt.xticks([]);
plt.yticks([])
# get title from the parameter names
plt.title(name.replace('_',' ').title(), fontsize=20)
plt.imshow(image)
plt.show()
# Perform one hot encoding on label
def one_hot_encode(label, label_values):
"""
Convert a segmentation image label array to one-hot format
by replacing each pixel value with a vector of length num_classes
# Arguments
label: The 2D array segmentation image label
label_values
# Returns
A 2D array with the same width and hieght as the input, but
with a depth size of num_classes
"""
semantic_map = []
for colour in label_values:
equality = np.equal(label, colour)
class_map = np.all(equality, axis = -1)
semantic_map.append(class_map)
semantic_map = np.stack(semantic_map, axis=-1)
return semantic_map
# Perform reverse one-hot-encoding on labels / preds
def reverse_one_hot(image):
"""
Transform a 2D array in one-hot format (depth is num_classes),
to a 2D array with only 1 channel, where each pixel value is
the classified class key.
# Arguments
image: The one-hot format image
# Returns
A 2D array with the same width and hieght as the input, but
with a depth size of 1, where each pixel value is the classified
class key.
"""
x = np.argmax(image, axis = -1)
return x
# Perform colour coding on the reverse-one-hot outputs
def colour_code_segmentation(image, label_values):
"""
Given a 1-channel array of class keys, colour code the segmentation results.
# Arguments
image: single channel array where each value represents the class key.
label_values
# Returns
Colour coded image for segmentation visualization
"""
colour_codes = np.array(label_values)
x = colour_codes[image.astype(int)]
return x
class BuildingsDataset(torch.utils.data.Dataset):
"""Massachusetts Buildings Dataset. Read images, apply augmentation and preprocessing transformations.
Args:
images_dir (str): path to images folder
masks_dir (str): path to segmentation masks folder
class_rgb_values (list): RGB values of select classes to extract from segmentation mask
augmentation (albumentations.Compose): data transfromation pipeline
(e.g. flip, scale, etc.)
preprocessing (albumentations.Compose): data preprocessing
(e.g. noralization, shape manipulation, etc.)
"""
def __init__(
self,
images_dir,
masks_dir,
class_rgb_values=None,
augmentation=None,
preprocessing=None,
):
self.image_paths = [os.path.join(images_dir, image_id) for image_id in sorted(os.listdir(images_dir))]
self.mask_paths = [os.path.join(masks_dir, image_id) for image_id in sorted(os.listdir(masks_dir))]
self.class_rgb_values = class_rgb_values
self.augmentation = augmentation
self.preprocessing = preprocessing
def __getitem__(self, i):
# read images and masks
image = cv2.cvtColor(cv2.imread(self.image_paths[i]), cv2.COLOR_BGR2RGB)
mask = cv2.cvtColor(cv2.imread(self.mask_paths[i]), cv2.COLOR_BGR2RGB)
# one-hot-encode the mask
mask = one_hot_encode(mask, self.class_rgb_values).astype('float')
# apply augmentations
if self.augmentation:
sample = self.augmentation(image=image, mask=mask)
image, mask = sample['image'], sample['mask']
# apply preprocessing
if self.preprocessing:
sample = self.preprocessing(image=image, mask=mask)
image, mask = sample['image'], sample['mask']
return image, mask
def __len__(self):
# return length of
return len(self.image_paths)
# Visualize Sample Image and Mask
dataset = BuildingsDataset(x_train_dir, y_train_dir, class_rgb_values=select_class_rgb_values)
random_idx = random.randint(0, len(dataset)-1)
image, mask = dataset[2]
visualize(
original_image = image,
ground_truth_mask = colour_code_segmentation(reverse_one_hot(mask), select_class_rgb_values),
one_hot_encoded_mask = reverse_one_hot(mask)
)
# Defining Augmentations
def get_training_augmentation():
train_transform = [
album.RandomCrop(height=256, width=256, always_apply=True),
album.OneOf(
[
album.HorizontalFlip(p=1),
album.VerticalFlip(p=1),
album.RandomRotate90(p=1),
],
p=0.75,
),
]
return album.Compose(train_transform)
def get_validation_augmentation():
# Add sufficient padding to ensure image is divisible by 32
test_transform = [
album.PadIfNeeded(min_height=1536, min_width=1536, always_apply=True, border_mode=0),
]
return album.Compose(test_transform)
def to_tensor(x, **kwargs):
return x.transpose(2, 0, 1).astype('float32')
def get_preprocessing(preprocessing_fn=None):
"""Construct preprocessing transform
Args:
preprocessing_fn (callable): data normalization function
(can be specific for each pretrained neural network)
Return:
transform: albumentations.Compose
"""
_transform = []
if preprocessing_fn:
_transform.append(album.Lambda(image=preprocessing_fn))
_transform.append(album.Lambda(image=to_tensor, mask=to_tensor))
return album.Compose(_transform)
# Visualize Augmented Images & Masks
augmented_dataset = BuildingsDataset(
x_train_dir, y_train_dir,
augmentation=get_training_augmentation(),
class_rgb_values=select_class_rgb_values,
)
random_idx = random.randint(0, len(augmented_dataset)-1)
# Different augmentations on a random image/mask pair (256*256 crop)
for i in range(3):
image, mask = augmented_dataset[random_idx]
visualize(
original_image = image,
ground_truth_mask = colour_code_segmentation(reverse_one_hot(mask), select_class_rgb_values),
one_hot_encoded_mask = reverse_one_hot(mask)
)
# Model Definition
ENCODER = 'resnet101'
ENCODER_WEIGHTS = 'imagenet'
CLASSES = class_names
ACTIVATION = 'sigmoid' # could be None for logits or 'softmax2d' for multiclass segmentation
# create segmentation model with pretrained encoder
model = smp.DeepLabV3Plus(
encoder_name=ENCODER,
encoder_weights=ENCODER_WEIGHTS,
classes=len(CLASSES),
activation=ACTIVATION,
)
preprocessing_fn = smp.encoders.get_preprocessing_fn(ENCODER, ENCODER_WEIGHTS)
# Get train and val dataset instances
train_dataset = BuildingsDataset(
x_train_dir, y_train_dir,
augmentation=get_training_augmentation(),
preprocessing=get_preprocessing(preprocessing_fn),
class_rgb_values=select_class_rgb_values,
)
valid_dataset = BuildingsDataset(
x_valid_dir, y_valid_dir,
augmentation=get_validation_augmentation(),
preprocessing=get_preprocessing(preprocessing_fn),
class_rgb_values=select_class_rgb_values,
)
# Get train and val data loaders
train_loader = DataLoader(train_dataset, batch_size=16, shuffle=True, num_workers=5)
valid_loader = DataLoader(valid_dataset, batch_size=1, shuffle=False, num_workers=2)
# Set Hyperparams
# Set flag to train the model or not. If set to 'False', only prediction is performed (using an older model checkpoint)
TRAINING = True
# Set num of epochs
EPOCHS = 80
# Set device: `cuda` or `cpu`
DEVICE = torch.device("cuda" if torch.cuda.is_available() else "cpu")
# define loss function
loss = smp.utils.losses.DiceLoss()
# define metrics
metrics = [
smp.utils.metrics.IoU(threshold=0.5),
]
# define optimizer
optimizer = torch.optim.Adam([
dict(params=model.parameters(), lr=0.0001),
])
# define learning rate scheduler (not used in this NB)
lr_scheduler = torch.optim.lr_scheduler.CosineAnnealingWarmRestarts(
optimizer, T_0=1, T_mult=2, eta_min=5e-5,
)
# load best saved model checkpoint from previous commit (if present)
if os.path.exists('../input/deeplabv3-efficientnetb4-frontend-using-pytorch/best_model.pth'):
model = torch.load('../input/deeplabv3-efficientnetb4-frontend-using-pytorch/best_model.pth', map_location=DEVICE)
train_epoch = smp.utils.train.TrainEpoch(
model,
loss=loss,
metrics=metrics,
optimizer=optimizer,
device=DEVICE,
verbose=True,
)
valid_epoch = smp.utils.train.ValidEpoch(
model,
loss=loss,
metrics=metrics,
device=DEVICE,
verbose=True,
)
# Training DeepLabV3+
if TRAINING:
best_iou_score = 0.0
train_logs_list, valid_logs_list = [], []
for i in range(0, EPOCHS):
# Perform training & validation
print('\nEpoch: {}'.format(i))
train_logs = train_epoch.run(train_loader)
valid_logs = valid_epoch.run(valid_loader)
train_logs_list.append(train_logs)
valid_logs_list.append(valid_logs)
# Save model if a better val IoU score is obtained
if best_iou_score < valid_logs['iou_score']:
best_iou_score = valid_logs['iou_score']
torch.save(model, './best_model.pth')
print('Model saved!')
# Prediction on Test Data
# load best saved model checkpoint from the current run
if os.path.exists('./best_model.pth'):
best_model = torch.load('./best_model.pth', map_location=DEVICE)
print('Loaded DeepLabV3+ model from this run.')
# load best saved model checkpoint from previous commit (if present)
elif os.path.exists('../input//deeplabv3-efficientnetb4-frontend-using-pytorch/best_model.pth'):
best_model = torch.load('../input//deeplabv3-efficientnetb4-frontend-using-pytorch/best_model.pth', map_location=DEVICE)
print('Loaded DeepLabV3+ model from a previous commit.')
# create test dataloader (with preprocessing operation: to_tensor(...))
test_dataset = BuildingsDataset(
x_test_dir,
y_test_dir,
augmentation=get_validation_augmentation(),
preprocessing=get_preprocessing(preprocessing_fn),
class_rgb_values=select_class_rgb_values,
)
test_dataloader = DataLoader(test_dataset)
# test dataset for visualization (without preprocessing transformations)
test_dataset_vis = BuildingsDataset(
x_test_dir, y_test_dir,
augmentation=get_validation_augmentation(),
class_rgb_values=select_class_rgb_values,
)
# get a random test image/mask index
random_idx = random.randint(0, len(test_dataset_vis)-1)
image, mask = test_dataset_vis[random_idx]
visualize(
original_image = image,
ground_truth_mask = colour_code_segmentation(reverse_one_hot(mask), select_class_rgb_values),
one_hot_encoded_mask = reverse_one_hot(mask)
)
# Center crop padded image / mask to original image dims
def crop_image(image, target_image_dims=[1500,1500,3]):
target_size = target_image_dims[0]
image_size = len(image)
padding = (image_size - target_size) // 2
return image[
padding:image_size - padding,
padding:image_size - padding,
:,
]
sample_preds_folder = 'sample_predictions/'
if not os.path.exists(sample_preds_folder):
os.makedirs(sample_preds_folder)
for idx in range(len(test_dataset)):
image, gt_mask = test_dataset[idx]
image_vis = crop_image(test_dataset_vis[idx][0].astype('uint8'))
x_tensor = torch.from_numpy(image).to(DEVICE).unsqueeze(0)
# Predict test image
pred_mask = best_model(x_tensor)
pred_mask = pred_mask.detach().squeeze().cpu().numpy()
# Convert pred_mask from `CHW` format to `HWC` format
pred_mask = np.transpose(pred_mask,(1,2,0))
# Get prediction channel corresponding to building
pred_building_heatmap = pred_mask[:,:,select_classes.index('building')]
pred_mask = crop_image(colour_code_segmentation(reverse_one_hot(pred_mask), select_class_rgb_values))
# Convert gt_mask from `CHW` format to `HWC` format
gt_mask = np.transpose(gt_mask,(1,2,0))
gt_mask = crop_image(colour_code_segmentation(reverse_one_hot(gt_mask), select_class_rgb_values))
cv2.imwrite(os.path.join(sample_preds_folder, f"sample_pred_{idx}.png"), np.hstack([image_vis, gt_mask, pred_mask])[:,:,::-1])
visualize(
original_image = image_vis,
ground_truth_mask = gt_mask,
predicted_mask = pred_mask,
predicted_building_heatmap = pred_building_heatmap
)
# Model Evaluation on Test Dataset
test_epoch = smp.utils.train.ValidEpoch(
model,
loss=loss,
metrics=metrics,
device=DEVICE,
verbose=True,
)
valid_logs = test_epoch.run(test_dataloader)
print("Evaluation on Test Data: ")
print(f"Mean IoU Score: {valid_logs['iou_score']:.4f}")
print(f"Mean Dice Loss: {valid_logs['dice_loss']:.4f}")
# Plot Dice Loss & IoU Metric for Train vs. Val
train_logs_df = pd.DataFrame(train_logs_list)
valid_logs_df = pd.DataFrame(valid_logs_list)
train_logs_df.T
plt.figure(figsize=(20,8))
plt.plot(train_logs_df.index.tolist(), train_logs_df.iou_score.tolist(), lw=3, label = 'Train')
plt.plot(valid_logs_df.index.tolist(), valid_logs_df.iou_score.tolist(), lw=3, label = 'Valid')
plt.xlabel('Epochs', fontsize=20)
plt.ylabel('IoU Score', fontsize=20)
plt.title('IoU Score Plot', fontsize=20)
plt.legend(loc='best', fontsize=16)
plt.grid()
plt.savefig('iou_score_plot.png')
plt.show()
plt.figure(figsize=(20,8))
plt.plot(train_logs_df.index.tolist(), train_logs_df.dice_loss.tolist(), lw=3, label = 'Train')
plt.plot(valid_logs_df.index.tolist(), valid_logs_df.dice_loss.tolist(), lw=3, label = 'Valid')
plt.xlabel('Epochs', fontsize=20)
plt.ylabel('Dice Loss', fontsize=20)
plt.title('Dice Loss Plot', fontsize=20)
plt.legend(loc='best', fontsize=16)
plt.grid()
plt.savefig('dice_loss_plot.png')
plt.show()
原始数据集和标签可视化展示
训练80个epochs,模型的损失和分割评分
使用测试集进行测试和可视化结果
本周工作总结
1.第一篇论文Pseudo-Label ,主要思想是通过熵正则化与伪标签具有相同的作用效果,可以利用未标签数据的分布的重叠程度的信息,进行半监督学习。
2.第二篇论文Mean Teacher,是将另外两篇半监督学习的模型和方法结合起来了(П-model和 Temporal ensembling),主要思想是有student model和teacher model这两个模型,teacher的参数由student计算指数移动平均值得到,主要是保证student model的预测结果和teacher model的预测结果尽量的相似,因为teacher model的参数是根据student model的移动平均得到,所以对于任何新来的数据,预测结果都不应该有太大的抖动,如果模型是正确的,那么前后两个模型的预测标签应该是接近的,并且变化比较小,那么使模型向两个模型预测结果接近的方向移动,就是向着groud truth model的方向移动。
3.在语义分割任务中,spatial pyramid pooling module(SPP)可以捕获更多尺度信息,encoder-decoder结构可以更好恢复物体的边缘信息,DeepLabv3+网络将上面两个有点都用到来构建网络。