<jupyter_start><jupyter_text>Compare Network Architectures Now that we have a reasonable baseline for our EasyDeepFakes dataset, let's try to improve performance. For starters, let's just compare how a variety of networks perform on this dataset. We will try: - ResNet - XResNet - EfficientNet - MesoNet - XceptionNet<jupyter_code>from fastai.core import *
from fastai.vision import *
path = Path('../data/EasyDeepFakes')
src = ImageList.from_folder(path).split_by_folder(train='train', valid='val')
def get_data(bs,size):
    data = (src.label_from_re('([A-Z]+).png$')
           .transform(get_transforms(max_warp=0, max_zoom=1), size=size)
           .databunch(bs=bs).normalize(imagenet_stats))
    return data
bs, sz = 32, 256
data = get_data(bs, sz)
data.show_batch(rows=4, figsize=(10,7))<jupyter_output><empty_output><jupyter_text>ResNet The ResNet architecture is one of the most common and trusted baseline architectures. We will use it to establish a reasonable baseline for performance and compare our other networks to it.  ResNet18<jupyter_code>from fastai.vision.models import resnet18
learner = cnn_learner(data, resnet18, metrics=[accuracy])
learner.lr_find()
learner.recorder.plot()
# Train only the head of the network
learner.fit_one_cycle(5, 1e-3)
# Unfreeze other layers and train the entire network
learner.unfreeze()
learner.fit_one_cycle(20, max_lr=slice(1e-5, 1e-3))<jupyter_output><empty_output><jupyter_text>ResNet18 gets a final accuracy of **85.6%** and a peak accuracy of **86.8%**  ResNet34<jupyter_code>from fastai.vision.models import resnet34
learner = cnn_learner(data, resnet34, metrics=[accuracy])
learner.lr_find()
learner.recorder.plot()
# Train only the head of the network
learner.fit_one_cycle(5, 1e-3)
# Unfreeze other layers and train the entire network
learner.unfreeze()
learner.fit_one_cycle(20, max_lr=slice(1e-5, 1e-3))<jupyter_output><empty_output><jupyter_text>ResNet34 has a final accuracy of **87.6%** and a peak accuracy of **89.1%**  ResNet50<jupyter_code>from fastai.vision.models import resnet50
learner = cnn_learner(data, resnet50, metrics=[accuracy])
learner.lr_find()
learner.recorder.plot()
# Train only the head of the network
learner.fit_one_cycle(5, 5e-3)
# Unfreeze other layers and train the entire network
learner.unfreeze()
learner.fit_one_cycle(20, max_lr=slice(1e-5, 1e-3))<jupyter_output><empty_output><jupyter_text>ResNet50 has a final accuracy of **91.1%** and a peak accuracy of **91.1%**.  XResNet `xresnet` is modified resnet architecture developed by fast.ai in according with the paper [Bag of Tricks for Image Classification with Convolutional Neural Networks](https://arxiv.org/abs/1812.01187).Notably the initial 7x7 conv is replaced by a series of 3x3 convolutions. I believe they have also changed some of the 1x1 convolutions in the bottleneck layers.**NOTE:** In fastai v1, there is no pretrained model for xresnet.  XResNet18<jupyter_code>from fastai.vision.models import xresnet18
learner = cnn_learner(data, xresnet18, metrics=[accuracy], pretrained=False)
learner.lr_find()
learner.recorder.plot()
# Train only the head of the network
learner.fit_one_cycle(5, 1e-3)
# Unfreeze other layers and train the entire network
learner.unfreeze()
learner.fit_one_cycle(20, max_lr=slice(1e-5, 1e-3))<jupyter_output><empty_output><jupyter_text>(Non-pretrained) XResNet18 gets a final accuracy of **65.0%** and a peak accuracy of **68.4%**.  XResNet34<jupyter_code>from fastai.vision.models import xresnet34
learner = cnn_learner(data, xresnet34, metrics=[accuracy], pretrained=False)
learner.lr_find()
learner.recorder.plot()
# Train only the head of the network
learner.fit_one_cycle(5, 1e-3)
# Unfreeze other layers and train the entire network
learner.unfreeze()
learner.fit_one_cycle(20, max_lr=slice(1e-5, 1e-3))<jupyter_output><empty_output><jupyter_text>`xresnet34` has a final accuracy of **68.9%** and a peak accuracy of **73.4%**.  XResNet50<jupyter_code>from fastai.vision.models import xresnet50
learner = cnn_learner(data, xresnet50, metrics=[accuracy], pretrained=False)
learner.lr_find()
learner.recorder.plot()
# Train only the head of the network
learner.fit_one_cycle(5, 1e-3)
# Unfreeze other layers and train the entire network
learner.unfreeze()
learner.fit_one_cycle(20, max_lr=slice(1e-5, 1e-3))<jupyter_output><empty_output><jupyter_text>`xresnet50` has a final accuracy of **71.1%** and a peak accuracy of **72.7%**.  EfficientNet EfficientNet is an architecture released by Google with the intention of reducing the number of parameters while maintaining good performance. There are 8 versions of Efficient with increasing capacity from `efficientb0` to `efficientnetb7`.I haven't figured out how to set up layer groups so I'm unable to do discriminitive learning with EfficientNet, but we'll give it a shot anyways.<jupyter_code># !pip install efficientnet-pytorch
from efficientnet_pytorch import EfficientNet<jupyter_output><empty_output><jupyter_text>EfficientNetB0<jupyter_code>model = EfficientNet.from_pretrained('efficientnet-b0', num_classes=data.c)
learner = Learner(data, model, metrics=[accuracy])

learner.lr_find()
learner.recorder.plot()
# Train only the head of the network
learner.fit_one_cycle(5, 1e-3)
# Unfreeze other layers and train the entire network
learner.unfreeze()

#NOTE: Not using discriminitive learning rates!
learner.fit_one_cycle(20, max_lr=1e-4)<jupyter_output><empty_output><jupyter_text>`efficientnetb0` has a final accuracy of **91.1%** and a peak accuracy of **92.7%**.  EfficientNetB1<jupyter_code>model = EfficientNet.from_pretrained('efficientnet-b1', num_classes=data.c)
learner = Learner(data, model, metrics=[accuracy])

learner.lr_find()
learner.recorder.plot()
# Train only the head of the network
learner.fit_one_cycle(5, 1e-3)
# Unfreeze other layers and train the entire network
learner.unfreeze()

#NOTE: Not using discriminitive learning rates!
learner.fit_one_cycle(20, max_lr=1e-4)<jupyter_output><empty_output><jupyter_text>`efficientnetb1` hsa an final accuracy of **91.9%** and a peak accuracy of **93.7%**  EfficientNetB2<jupyter_code>model = EfficientNet.from_pretrained('efficientnet-b2', num_classes=data.c)
learner = Learner(data, model, metrics=[accuracy])

learner.lr_find()
learner.recorder.plot()
# Train only the head of the network
learner.fit_one_cycle(5, 1e-3)
# Unfreeze other layers and train the entire network
learner.unfreeze()

#NOTE: Not using discriminitive learning rates!
learner.fit_one_cycle(20, max_lr=1e-4)<jupyter_output><empty_output><jupyter_text>`efficientnetb2` get an final accuracy of **89.4%** and a peak accuracy of **90.1%**.  MesoNet MesoNet was developed to detect deep fakes in the [MesoNet: a Compact Facial Video Forgery Detection Network](https://arxiv.org/abs/1809.00888) paper.Like EfficientNet, I'm unsure how to build layer groups, so we will not use discriminitive fine tuning on this network.<jupyter_code>#export
# By <NAME>.
# Paper implementation does not use Adaptive Average Pooling. To get the exact same implementation, 
# comment the avg_pool and uncomment the final max_pool layer.
class MesoNet(nn.Module):
    def __init__(self):
        super().__init__()
          
        self.conv1 = nn.Conv2d(3, 8, 3, 1,1) # 8 x 256 x 256
        self.bn1 = nn.BatchNorm2d(8)
        self.conv2 = nn.Conv2d(8, 8, 5, 1,2) # 8 x 128 x 128
        self.bn2 = nn.BatchNorm2d(8)
        self.conv3 = nn.Conv2d(8, 16, 5, 1,2) # 8 x 64 x 64
        self.bn3 = nn.BatchNorm2d(16)
        self.conv4 = nn.Conv2d(16,16,5,1,2) # 8 x 32 x 32
        self.bn4 = nn.BatchNorm2d(16)
        self.avg_pool = nn.AdaptiveAvgPool2d((8))
        self.fc1 = nn.Linear(1024, 16)
        self.fc2 = nn.Linear(16, 2)
    
    def forward(self, x):

        x = F.relu(self.conv1(x))
        x = self.bn1(x)
        x = F.max_pool2d(x, 2, 2)
        
        x = F.relu(self.conv2(x))
        x = self.bn2(x)
        x = F.max_pool2d(x, 2, 2)
        
        x = F.relu(self.conv3(x))  
        x = self.bn3(x)
        x = F.max_pool2d(x, 2, 2)
        
        x = F.relu(self.conv4(x))
        x = self.bn4(x)
        #x = F.max_pool2d(x, 4, 4)
        
        x = self.avg_pool(x)

        x = x.reshape(x.shape[0], -1)

        x = F.dropout(x, 0.5)
        x = F.relu(self.fc1(x))
        x = F.dropout(x,0.5)
        x = self.fc2(x)
        return x
model = MesoNet()
learner = Learner(data, model, metrics=[accuracy])

learner.lr_find()
learner.recorder.plot()
# Train only the head of the network
learner.fit_one_cycle(5, 1e-3)
# Unfreeze other layers and train the entire network
learner.unfreeze()

#NOTE: Not using discriminitive learning rates!
learner.fit_one_cycle(20, max_lr=1e-4)<jupyter_output><empty_output><jupyter_text>`mesonet` has a final accuracy of **65.6%** and a peak accuracy of **67.6%**.  XceptionNet [XceptionNet](https://arxiv.org/abs/1610.02357) was developed to be a more performant version of Google's InceptionNet. They replace Inception modules with depthwise separable convolution modules.It was also the best performing model used in [FaceForensics++: Learning to Detect Manipulated Facial Images](https://arxiv.org/abs/1901.08971).<jupyter_code>## xception.py
"""
Ported to pytorch thanks to [tstandley](https://github.com/tstandley/Xception-PyTorch)
@author: tstandley
Adapted by cadene
Creates an Xception Model as defined in:
Francois Chollet
Xception: Deep Learning with Depthwise Separable Convolutions
https://arxiv.org/pdf/1610.02357.pdf
This weights ported from the Keras implementation. Achieves the following performance on the validation set:
Loss:0.9173 Prec@1:78.892 Prec@5:94.292
REMEMBER to set your image size to 3x299x299 for both test and validation
normalize = transforms.Normalize(mean=[0.5, 0.5, 0.5],
                                  std=[0.5, 0.5, 0.5])
The resize parameter of the validation transform should be 333, and make sure to center crop at 299x299
"""
import math
import torch
import torch.nn as nn
import torch.nn.functional as F
import torch.utils.model_zoo as model_zoo
from torch.nn import init

pretrained_settings = {
    'xception': {
        'imagenet': {
            'url': 'http://data.lip6.fr/cadene/pretrainedmodels/xception-b5690688.pth',
            'input_space': 'RGB',
            'input_size': [3, 299, 299],
            'input_range': [0, 1],
            'mean': [0.5, 0.5, 0.5],
            'std': [0.5, 0.5, 0.5],
            'num_classes': 1000,
            'scale': 0.8975 # The resize parameter of the validation transform should be 333, and make sure to center crop at 299x299
        }
    }
}


class SeparableConv2d(nn.Module):
    def __init__(self,in_channels,out_channels,kernel_size=1,stride=1,padding=0,dilation=1,bias=False):
        super(SeparableConv2d,self).__init__()

        self.conv1 = nn.Conv2d(in_channels,in_channels,kernel_size,stride,padding,dilation,groups=in_channels,bias=bias)
        self.pointwise = nn.Conv2d(in_channels,out_channels,1,1,0,1,1,bias=bias)

    def forward(self,x):
        x = self.conv1(x)
        x = self.pointwise(x)
        return x


class Block(nn.Module):
    def __init__(self,in_filters,out_filters,reps,strides=1,start_with_relu=True,grow_first=True):
        super(Block, self).__init__()

        if out_filters != in_filters or strides!=1:
            self.skip = nn.Conv2d(in_filters,out_filters,1,stride=strides, bias=False)
            self.skipbn = nn.BatchNorm2d(out_filters)
        else:
            self.skip=None

        self.relu = nn.ReLU(inplace=True)
        rep=[]

        filters=in_filters
        if grow_first:
            rep.append(self.relu)
            rep.append(SeparableConv2d(in_filters,out_filters,3,stride=1,padding=1,bias=False))
            rep.append(nn.BatchNorm2d(out_filters))
            filters = out_filters

        for i in range(reps-1):
            rep.append(self.relu)
            rep.append(SeparableConv2d(filters,filters,3,stride=1,padding=1,bias=False))
            rep.append(nn.BatchNorm2d(filters))

        if not grow_first:
            rep.append(self.relu)
            rep.append(SeparableConv2d(in_filters,out_filters,3,stride=1,padding=1,bias=False))
            rep.append(nn.BatchNorm2d(out_filters))

        if not start_with_relu:
            rep = rep[1:]
        else:
            rep[0] = nn.ReLU(inplace=False)

        if strides != 1:
            rep.append(nn.MaxPool2d(3,strides,1))
        self.rep = nn.Sequential(*rep)

    def forward(self,inp):
        x = self.rep(inp)

        if self.skip is not None:
            skip = self.skip(inp)
            skip = self.skipbn(skip)
        else:
            skip = inp

        x+=skip
        return x


class Xception(nn.Module):
    """
    Xception optimized for the ImageNet dataset, as specified in
    https://arxiv.org/pdf/1610.02357.pdf
    """
    def __init__(self, num_classes=1000):
        """ Constructor
        Args:
            num_classes: number of classes
        """
        super(Xception, self).__init__()
        self.num_classes = num_classes

        self.conv1 = nn.Conv2d(3, 32, 3,2, 0, bias=False)
        self.bn1 = nn.BatchNorm2d(32)
        self.relu = nn.ReLU(inplace=True)

        self.conv2 = nn.Conv2d(32,64,3,bias=False)
        self.bn2 = nn.BatchNorm2d(64)
        #do relu here

        self.block1=Block(64,128,2,2,start_with_relu=False,grow_first=True)
        self.block2=Block(128,256,2,2,start_with_relu=True,grow_first=True)
        self.block3=Block(256,728,2,2,start_with_relu=True,grow_first=True)

        self.block4=Block(728,728,3,1,start_with_relu=True,grow_first=True)
        self.block5=Block(728,728,3,1,start_with_relu=True,grow_first=True)
        self.block6=Block(728,728,3,1,start_with_relu=True,grow_first=True)
        self.block7=Block(728,728,3,1,start_with_relu=True,grow_first=True)

        self.block8=Block(728,728,3,1,start_with_relu=True,grow_first=True)
        self.block9=Block(728,728,3,1,start_with_relu=True,grow_first=True)
        self.block10=Block(728,728,3,1,start_with_relu=True,grow_first=True)
        self.block11=Block(728,728,3,1,start_with_relu=True,grow_first=True)

        self.block12=Block(728,1024,2,2,start_with_relu=True,grow_first=False)

        self.conv3 = SeparableConv2d(1024,1536,3,1,1)
        self.bn3 = nn.BatchNorm2d(1536)

        #do relu here
        self.conv4 = SeparableConv2d(1536,2048,3,1,1)
        self.bn4 = nn.BatchNorm2d(2048)

        self.fc = nn.Linear(2048, num_classes)

        # #------- init weights --------
        # for m in self.modules():
        #     if isinstance(m, nn.Conv2d):
        #         n = m.kernel_size[0] * m.kernel_size[1] * m.out_channels
        #         m.weight.data.normal_(0, math.sqrt(2. / n))
        #     elif isinstance(m, nn.BatchNorm2d):
        #         m.weight.data.fill_(1)
        #         m.bias.data.zero_()
        # #-----------------------------

    def features(self, input):
        x = self.conv1(input)
        x = self.bn1(x)
        x = self.relu(x)

        x = self.conv2(x)
        x = self.bn2(x)
        x = self.relu(x)

        x = self.block1(x)
        x = self.block2(x)
        x = self.block3(x)
        x = self.block4(x)
        x = self.block5(x)
        x = self.block6(x)
        x = self.block7(x)
        x = self.block8(x)
        x = self.block9(x)
        x = self.block10(x)
        x = self.block11(x)
        x = self.block12(x)

        x = self.conv3(x)
        x = self.bn3(x)
        x = self.relu(x)

        x = self.conv4(x)
        x = self.bn4(x)
        return x

    def logits(self, features):
        x = self.relu(features)

        x = F.adaptive_avg_pool2d(x, (1, 1))
        x = x.view(x.size(0), -1)
        x = self.last_linear(x)
        return x

    def forward(self, input):
        x = self.features(input)
        x = self.logits(x)
        return x


def xception(num_classes=1000, pretrained='imagenet'):
    model = Xception(num_classes=num_classes)
    if pretrained:
        settings = pretrained_settings['xception'][pretrained]
        assert num_classes == settings['num_classes'], \
            "num_classes should be {}, but is {}".format(settings['num_classes'], num_classes)

        model = Xception(num_classes=num_classes)
        model.load_state_dict(model_zoo.load_url(settings['url']))

        model.input_space = settings['input_space']
        model.input_size = settings['input_size']
        model.input_range = settings['input_range']
        model.mean = settings['mean']
        model.std = settings['std']

    # TODO: ugly
    model.last_linear = model.fc
    del model.fc
    return model
XCEPTION_MODEL = 'xception/xception-b5690688.pth'

def return_pytorch04_xception(pretrained=True):
    # Raises warning "src not broadcastable to dst" but thats fine
    model = xception(pretrained=False)
    if pretrained:
        # Load model in torch 0.4+
        model.fc = model.last_linear
        del model.last_linear
        state_dict = torch.load(
            XCEPTION_MODEL)
        for name, weights in state_dict.items():
            if 'pointwise' in name:
                state_dict[name] = weights.unsqueeze(-1).unsqueeze(-1)
        model.load_state_dict(state_dict)
        model.last_linear = model.fc
        del model.fc
    return model
model = return_pytorch04_xception()
model.last_linear = torch.nn.Linear(in_features=2048, out_features=2, bias=True)
learner = Learner(data, model, metrics=[accuracy])

learner.lr_find()
learner.recorder.plot()
# Train only the head of the network
learner.fit_one_cycle(5, 1e-3)
# Unfreeze other layers and train the entire network
learner.unfreeze()

#NOTE: Not using discriminitive learning rates!
learner.fit_one_cycle(20, max_lr=1e-4)<jupyter_output><empty_output>
{'fraction_non_alphanumeric': 0.11872711912137426, 'fraction_numerical': 0.043030132357082515, 'mean_word_length': 3.4423317488116085, 'pattern_counts': {'":': 0, '<': 54, '<?xml version=': 0, '>': 54, 'https://': 7, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 1, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'chain_length': '13', 'dir': 'jupyter-structured-clean-dedup', 'id': '9138', 'license': 'Apache-2.0', 'n_tokens_mistral': 6607, 'n_tokens_neox': 6177, 'n_words': 1417, 'path': 'face_detection/08_CompareNetworks.ipynb', 'repo_name': 'qAp/kgl_deepfake', 'size': '17762'}