Notation used on this pagesymbols, meanings, shapes
    01

    The problem: a model trained on some places has to work in a new one.

    A detector that does not travel

    Brick kilns are a large source of air pollution across the Indo-Gangetic plain, and they are hard to count on the ground. Satellite images make it possible to find them from above, but a detector learns from whichever regions someone had time to label. This section shows what changes when that detector is moved to a new region, names the problem, and sets out how the MixStyle paper measures progress on it.

    Labelled. Kilns with hand-validated boxes in western Uttar Pradesh. A detector is trained here.

    Not labelled. We now want the kilns in Bihar, about 750 km east.

    Will a detector trained on the left find the kilns on the right?

    15–25%

    drop in detection mAP

    SentinelKilnDB's benchmark trains detectors on some regions and tests them on distant regions they never saw.

    Detection accuracy drops by 15 to 25% in mAP. The kilns are the same kind of object; only the place changed.

    SentinelKilnDB benchmark, out-of-region task (Mondal et al., NeurIPS 2025).

    The kilns have the same shape everywhere. Their colour, and everything around them, changes.

    Sentinel-2 tiles, 128 × 128 pixels (1.28 km across), with kiln boxes from SentinelKilnDB (Mondal et al., NeurIPS 2025). Box colours mark the three kiln types.

    regionaverage colourmean R, G, Bspread R, G, B
    Colour, brightness and contrast change from place to place. Call this the style of an image. The kilns, fields and roads are its content.

    “Style” here is deliberately loose: the colour cast, brightness, contrast and texture of the whole image. A region can also differ in content (a different kiln design, a different ground resolution); MixStyle is not meant for that kind of change, and Section 11 comes back to it.

    What ordinary training assumes training (filled) and test (rings): one cloud What happens in a new region the test cloud has moved: domain shift

    Each source of images, such as a region, a camera, a season or a drawing style, is a domain.

    1 Same domain

    Test images come from the regions we trained on. Ordinary supervised learning.

    2 Domain adaptation

    We have unlabelled images from the new region while training, and adapt to them.

    3 Domain generalization

    We have nothing from the new region until test time. Train on several source domains; work on an unseen one.

    MixStyle is a domain generalization method. It never sees the target domain.

    PhotoArt paintingCartoonSketchtest on

    Report accuracy on each held-out domain, and the average of the four.

    The goal in one line: learn from several source domains a model that still works on a domain it has never seen. The next section looks at the obvious ways to get there and why the paper wanted something else.

    02

    Before MixStyle: the obvious ways to add variety, and what the paper found when it tried them.

    Why the usual fixes fall short

    Section 2

    More variety, fewer labels

    A new region changes how every image looks. The kilns stay the same kind of object.

    How can training see looks it was never given, without labelling another region?

    Map of India with yellow dots marking fixed chimney bull's trench kilns, dense across the Indo-Gangetic plain
    One kiln type (FCBK) across India, from SentinelKilnDB.

    62,671

    kilns, each checked by hand (208 hours of validation), across India, Bangladesh, Pakistan and Afghanistan

    More labelled regions is the most reliable fix.

    It is also the slowest. There is always another district, season or satellite.

    Each version is our guess about how a new region might look.

    A real region changes soil, crops, water, haze and roofs together, in ways we did not choose.

    +
    →
    $$\text{image} = \flam{\lambda}\,x_i + (1-\flam{\lambda})\,x_j \qquad \text{label} = \flam{\lambda}\,y_i + (1-\flam{\lambda})\,y_j$$

    λ is the blending weight, 0.5 here. xi, xj are the two images and yi, yj their labels.

    The blend shows two scenes at once. It is a double exposure, and no region looks like that.

    On PACS, none of these general-purpose tricks beats the plain network on the unseen domain.

    1No new labels

    Use only the regions we already labelled.

    2Looks nobody hand-picked

    Training sees styles that none of the source regions had, without us listing them.

    3Content and label untouched

    The kilns, fields and roads stay where they are, and the label stays the image's own.

    MixStyle does all three inside the network. First: where does the look of an image live inside a CNN?

    Every method in the paper's comparison uses the same ResNet-18, the same training schedule, and each baseline's recommended settings. The plain network averages 79.5% on PACS; Mixup 78.5%, CutMix 76.8%, Cutout 78.3%, DropBlock 79.2%, Manifold Mixup 76.2%. Mixup without label mixing (and with a uniform weight) reaches 77.3%, so taking the label mixing out does not rescue it.

    03

    A short refresher: what one convolution layer hands to the next, and the tensor MixStyle works on.

    What a CNN layer produces

    Section 3

    Inside the network

    We want to change how an image looks and keep what it shows.

    What does a CNN layer pass to the next layer, and where in it is the look?

    ∗
    →

    A layer with C filters returns C maps. Each map says where its pattern appears, and how strongly.

    one image: C × H × W a batch: B × C × H × W H × W image b, channel c

    ResNet-18 after its first stage, for 16 images of 224 × 224: 16 × 64 × 56 × 56.

    Fix one image and one channel: you are left with one H × W map. MixStyle works on these maps.

    import torch
    import torchvision
    
    net = torchvision.models.resnet18()
    x = torch.randn(16, 3, 224, 224)                  # 16 RGB images
    h = net.maxpool(net.relu(net.bn1(net.conv1(x))))  # the stem
    h = net.layer1(h)                                 # first residual stage
    print(h.shape)                                    # torch.Size([16, 64, 56, 56])

    h[b, c] is one 56 × 56 map: image b, channel c. Dimensions 2 and 3 are height and width.

    A channel is one of the C outputs of a layer. For one image, that channel's H × W grid of values is its feature map. The tensor x of shape B × C × H × W therefore holds B × C feature maps, and the next section summarizes each of them with two numbers.

    04

    Summarize one feature map with two numbers, then take them away and see what is left.

    Two numbers per channel

    Section 4

    Where a map is, and how strongly it fires

    A CNN layer hands on C maps per image. Each map says where one pattern appears.

    Can we separate where a channel fires from how strongly it fires?

    $$\fmu{\mu} = \frac{1}{HW}\sum_{h=1}^{H}\sum_{w=1}^{W} \fx{x}_{h,w}$$

    $\fmu{\mu} = \tfrac{1}{4}\,(0 + 0 + 2 + 2) = \fmu{1}$

    μ
    the mean: the average value in this map
    xh,w
    the value at row h, column w
    H W
    the number of positions: 2 × 2 = 4 here
    $$\fsig{\sigma} = \sqrt{\frac{1}{HW}\sum_{h,w} \big(\fx{x}_{h,w}-\fmu{\mu}\big)^2}$$

    $\fsig{\sigma} = \sqrt{\tfrac{1}{4}\big(1 + 1 + 1 + 1\big)} = \fsig{1}$

    σ
    the standard deviation: how far a typical value sits from μ
    xh,w − μ
    each value's distance from the mean: −1, −1, +1, +1

    $\fx{x_2} = 2\,\fx{x_1} + 3$: stretched by 2 and lifted by 3. Same arrangement, different level and spread.

    $$\fx{\hat{x}} = \frac{\fx{x}-\fmu{\mu}}{\fsig{\sigma}}$$

    Both normalized maps are identical. Removing μ and σ removed everything that differed; the arrangement is all that is left.

    $$\fx{x} = \fsig{\sigma}\,\fx{\hat{x}} + \fmu{\mu}$$

    The pattern x̂

    where the channel fires: the content

    μ and σ

    how strongly, and around what level: the look

    x = torch.tensor([[[[0., 0.], [2., 2.]]],
                      [[[3., 3.], [7., 7.]]]])               # (B, C, H, W) = (2, 1, 2, 2)
    mu = x.mean(dim=(2, 3), keepdim=True)                     # [1, 5], shape (2, 1, 1, 1)
    sig = x.std(dim=(2, 3), keepdim=True, unbiased=False)     # [1, 2]
    x_hat = (x - mu) / sig                                    # both [[-1, -1], [1, 1]]

    dim=(2, 3) averages over height and width only: one μ and one σ per image and per channel.

    For 16 images and 64 channels: 16 × 64 means and 16 × 64 standard deviations.

    Nothing here depends on the maps being 2 × 2. For any channel of any image, μ and σ are computed over its H × W positions, and the normalized map has mean 0 and standard deviation 1. When two maps differ only by a stretch and a shift, as they did here, their normalized versions are identical. Real maps from different regions also differ in their patterns, so their normalized maps differ too; what normalization guarantees is that μ and σ no longer do.

    05

    Evidence that μ and σ carry the look of an image: first on pixels, then from style transfer, then from the paper.

    Why those numbers are style

    Section 5

    Is the look really in μ and σ?

    Every channel of every image is a pattern plus two numbers, μ and σ.

    If we give one image another image's μ and σ, what changes and what stays?

    +
    →

    Same fields, roads and kilns as west UP. Bihar's colours, brightness and contrast.

    content (keep its pattern)

    style (take its μ and σ)

    regionbrightness μσleft-right edges μσgreen − red μσ

    A channel's μ says how much of its pattern an image has; its σ says how unevenly. Both differ from region to region. Bold marks the largest value in each column, grey the smallest.

    $$\operatorname{IN}(\fx{x}) = \gamma\,\frac{\fx{x}-\fmu{\mu}(\fx{x})}{\fsig{\sigma}(\fx{x})} + \beta$$
    μ(x), σ(x)
    this image's own mean and spread, per channel
    γ, β
    a learned scale and shift per channel, the same for every image

    Huang and Belongie's reading: normalizing each image with its own μ and σ removes its style.

    $$\operatorname{AdaIN}(\fx{x}, \fpart{y}) = \fsig{\sigma}(\fpart{y})\,\frac{\fx{x}-\fmu{\mu}(\fx{x})}{\fsig{\sigma}(\fx{x})} + \fmu{\mu}(\fpart{y})$$
    content x style y encoder AdaINx's pattern, y's μ and σ decoder stylized MixStyle drops this
    A sketch of the paper's Figure 1 (illustrative points)

    ResNet-18 trained on PACS. For each image, the μ and σ of every channel after the first residual stage, projected to 2-D.

    The four domains land in four separate groups.

    Early-layer statistics tell you which domain an image came from. MixStyle will blur this signal.

    The pixel swap, style transfer and the paper's plot all agree. On pixels, swapping per-channel μ and σ swaps colour and contrast and leaves the layout alone. In style transfer, instance normalization removes style and AdaIN inserts another image's style using nothing but channel means and spreads. In the paper's own network, early-layer statistics identify the domain. The next section uses this directly: if statistics carry style, then new statistics give new styles.

    06

    The mechanism itself, one pair of maps at a time, with every number worked out.

    MixStyle on one pair

    Section 6

    Blend the numbers instead of swapping them

    Giving an image another image's μ and σ gives it that image's look.

    What if we blend the two sets of statistics instead, by a random amount, every time we train?

    our map x label: ours normalize pattern x̂ μ(x), σ(x) partner x̃ same batch μ(x̃), σ(x̃) blend with λ λ · ours + (1 − λ) · theirs μmix, σmix our content, a new look label: still ours

    From the partner we take only its two numbers, μ(x̃) = 5 and σ(x̃) = 2.

    λ = 0 partner'sstatistics λ = 1 our ownstatistics λ = 0.5 halfway: our example

    λ is the share of our own statistics that we keep. The rest comes from the partner.

    $$\fmix{\mu_{\text{mix}}} = \flam{\lambda}\,\fmu{\mu}(\fx{x}) + (1-\flam{\lambda})\,\fmu{\mu}(\fpart{\tilde{x}})$$

    $\fmix{\mu_{\text{mix}}} = \flam{0.5}\cdot\fmu{1} + \flam{0.5}\cdot\fmu{5} = \fmix{3}$

    $$\fmix{\sigma_{\text{mix}}} = \flam{\lambda}\,\fsig{\sigma}(\fx{x}) + (1-\flam{\lambda})\,\fsig{\sigma}(\fpart{\tilde{x}})$$

    $\fmix{\sigma_{\text{mix}}} = \flam{0.5}\cdot\fsig{1} + \flam{0.5}\cdot\fsig{2} = \fmix{1.5}$

    λ
    the share of our own statistics we keep
    μ(x), σ(x)
    our map's mean and spread; x̃ marks the partner's
    μmix, σmix
    the mixed statistics: a look that neither image had
    $$\text{output} = \fmix{\sigma_{\text{mix}}}\,\fx{\hat{x}} + \fmix{\mu_{\text{mix}}}$$
    • ✓mean 3 = μmix, spread 1.5 = σmix
    • ✓normalized: [−1, −1; 1, 1], our pattern, and our label

    MixStyle: our pattern, mixed look

    Mixup: the two maps averaged

    $$\operatorname{MixStyle}(\fx{x}) = \fmix{\sigma_{\text{mix}}}\,\frac{\fx{x}-\fmu{\mu}(\fx{x})}{\fsig{\sigma}(\fx{x})} + \fmix{\mu_{\text{mix}}}$$
    (x − μ(x)) / σ(x)
    our pattern, with our own mean and spread removed
    σmix, μmix
    the blended look: λ · ours + (1 − λ) · the partner's

    The paper writes γmix for σmix and βmix for μmix (its equations 5 to 7). Nothing in this line is learned.

    partner

    Each channel keeps its pattern. Its μ and σ move towards the partner's.

    λ = 1: nothing changes

    λ = 0: a full swap (AdaIN)

    On PACS, blending averages 82.8%; always swapping, 82.1% (the paper's Table 4).

    x = torch.tensor([[[[0., 0.], [2., 2.]]],               # our map, x[0]
                      [[[7., 3.], [7., 3.]]]])              # its partner, x[1]
    mu = x.mean(dim=(2, 3), keepdim=True)                    # [1, 5]
    sig = x.std(dim=(2, 3), keepdim=True, unbiased=False)    # [1, 2]
    x_hat = (x - mu) / sig                                   # the two patterns
    lam = 0.5
    mu_mix = lam * mu[0] + (1 - lam) * mu[1]       # 0.5 * 1 + 0.5 * 5 = 3
    sig_mix = lam * sig[0] + (1 - lam) * sig[1]    # 0.5 * 1 + 0.5 * 2 = 1.5
    out = x_hat[0] * sig_mix + mu_mix              # [[[1.5, 1.5], [4.5, 4.5]]]

    mu[1] and sig[1] are the partner's statistics; x_hat[0] is our pattern.

    For one pair, MixStyle is five small steps: compute our statistics, normalize, compute the partner's statistics, blend the two sets with λ, and re-apply the blend to our pattern. The label never enters. With λ = 1 it does nothing; with λ = 0 it is AdaIN with the partner as the style image. The next section scales this up from one pair to a mini-batch, where every image needs a partner and its own λ.

    07

    From one pair to a mini-batch: who is each image's partner, and how λ is drawn.

    A whole batch

    Section 7

    Every image needs a partner

    For one pair: normalize, blend the statistics with λ, put them back.

    In a mini-batch, where does each image's partner come from, and who picks λ?

    perm = torch.randperm(x.size(0))    # a random order of 0, 1, ..., B-1
    mu2, sig2 = mu[perm], sig[perm]      # each image's partner statistics

    perm = torch.arange(B - 1, -1, -1)          # [5, 4, 3, 2, 1, 0]
    perm_j, perm_i = perm.chunk(2)              # [5, 4, 3] and [2, 1, 0]
    perm_j = perm_j[torch.randperm(B // 2)]     # for example [4, 3, 5]
    perm_i = perm_i[torch.randperm(B // 2)]     # for example [0, 2, 1]
    perm = torch.cat([perm_j, perm_i], 0)       # [4, 3, 5, 0, 2, 1]

    Images 0, 1, 2 (first region) get partners 4, 3, 5 (second region). Images 3, 4, 5 get partners 0, 2, 1.

    Mixing inside one domain helps a little. Mixing across domains helps most.

    α

    One λ per image, shared by all its channels: shape B × 1 × 1 × 1. With the paper's α = 0.1, most images get a small nudge or a near-swap; the λ = 0.5 of Section 6 is rare.

    Made-up 2 × 2 maps, small enough to check by hand.

    imageregionpartner perm[i]partner's regionλkeeps

    Image 1 keeps only 10% of its own statistics. Image 3 keeps all of them.

    imageλown μpartner μμmixown σpartner σσmix

    image 1, channel 0: $\fmix{\mu_{\text{mix}}} = \flam{0.1}\cdot\fmu{1} + \flam{0.9}\cdot\fmu{6} = \fmix{5.5}$, $\;\fmix{\sigma_{\text{mix}}} = \flam{0.1}\cdot\fsig{1} + \flam{0.9}\cdot\fsig{2} = \fmix{1.9}$

    mu = x.mean(dim=(2, 3), keepdim=True)                         # (B, C, 1, 1)
    sig = (x.var(dim=(2, 3), keepdim=True) + 1e-6).sqrt()         # (B, C, 1, 1)
    perm = torch.randperm(x.size(0))                              # partners
    lam = torch.distributions.Beta(0.1, 0.1).sample((x.size(0), 1, 1, 1))
    mu_mix = lam * mu + (1 - lam) * mu[perm]                      # (B, C, 1, 1)
    out = (x - mu) / sig * (lam * sig + (1 - lam) * sig[perm]) + mu_mix

    The variance line follows the released code: x.var divides by H·W − 1 and adds a small ε before the square root. Section 8 explains both.

    In a batch, partners come from shuffling the batch itself: at random, or across two regions when region labels are available. Each image gets its own λ from Beta(α, α), usually close to 0 or 1 when α = 0.1, and shares it across all its channels. The trace shows the consequence: with λ near 1 an image barely changes; with λ near 0 it takes on nearly all of its partner's statistics while keeping its own pattern.

    08

    When MixStyle runs, what backpropagation sees, and the complete layer.

    The rest of the recipe

    Section 8

    Four details that finish the layer

    Every image in the batch gets a partner and a λ, and its statistics are blended.

    How often does this happen, what happens at test time, and what does backpropagation see?

    def forward(self, x):
        if random.random() > self.p:    # with probability 1 - p
            return x                    # pass the batch through unchanged

    One coin per layer per batch. The paper gives no reason for p = 0.5; one plausible one is that unmixed batches keep the network used to real statistics.

    model.train()

    early CNN layers
    ↓
    MixStyle may act
    ↓
    rest of the network

    model.eval()

    early CNN layers
    ↓
    identity: does nothing
    ↓
    rest of the network

    At test time MixStyle does nothing. The deployed model is an ordinary CNN, and it never needs an image from the new region.

    x μ(x), σ(x) detach (x − μ) / σ μmix, σmix × σmix + μmix rest → loss gradient flows back through the pattern only
    mu, sig = mu.detach(), sig.detach()    # statistics become constants for autograd

    detached, as in the paper

    not detached

    Toy loss: the top-left output. Detached, its gradient reaches only the cell below it, times σmix / σ(x) = 1.5, and none reaches the partner.

    import random, torch, torch.nn as nn
    
    class MixStyle(nn.Module):
        def __init__(self, p=0.5, alpha=0.1, eps=1e-6):
            super().__init__()
            self.p, self.eps = p, eps
            self.beta = torch.distributions.Beta(alpha, alpha)
    
        def forward(self, x):
            if not self.training or random.random() > self.p:
                return x                                        # test time, or not this batch
            B = x.size(0)
            mu = x.mean(dim=[2, 3], keepdim=True)
            sig = (x.var(dim=[2, 3], keepdim=True) + self.eps).sqrt()
            mu, sig = mu.detach(), sig.detach()
            lmda = self.beta.sample((B, 1, 1, 1)).to(x.device)  # one lambda per image
            perm = torch.randperm(B)                            # partners
            mu_mix = mu * lmda + mu[perm] * (1 - lmda)
            sig_mix = sig * lmda + sig[perm] * (1 - lmda)
            return (x - mu) / sig * sig_mix + mu_mix
    layer = MixStyle(p=0.5, alpha=0.1)
    print(sum(q.numel() for q in layer.parameters()))    # 0
    settingpaper's choicewhat it controls
    p0.5how often a batch is mixed
    alpha0.1how extreme λ tends to be
    mixrandom, or cross-domain with labelswho the partners are
    placementafter the early residual stageswhich features get mixed (Section 9)
    eps1e-6guards against a flat map with σ = 0
    • x.var divides by H·W − 1; the paper's equation divides by H·W. Every σ changes by the same factor ( for 56 × 56), and it cancels in the output.
    • eps only matters for a flat channel, such as one that ReLU set entirely to 0.
    • Cross-domain mode assumes each batch is two equal halves from two domains, in that order. The data loader must build batches that way.
    • With several MixStyle layers, each draws its own shuffle. The paper found this slightly better than one shared order: 82.8% versus 82.4% on PACS.

    MixStyle acts on about half of the training batches, never at test time, and adds no parameters. Its statistics are detached, so each image's gradient passes straight back to that image's own features, scaled by σmix / σ(x), and none leaks into its partner. The remaining choice with the largest effect is placement, which the next section takes up.

    09

    MixStyle can follow any convolutional block. The paper's ablation shows where it helps and where it hurts.

    Where to put it

    Section 9

    Which layers should be mixed?

    MixStyle is a weightless layer that fits after any convolutional block.

    After which blocks does it help, and is there a block where it hurts?

    The candidate places are after res1, res2, res3 and res4.

    Early stages: gains. Anything that includes the last stage: worse than no MixStyle at all.

    Best here: res12. The last stage hurts again, badly.

    res4 output: 512 maps of 7 × 7 average pool 512 channel means = μ(x) linear class scores dog, horse, house, …

    After the last stage, μ is the class evidence. Mixing it mixes the evidence of two images and keeps one image's label.

    net = torchvision.models.resnet18()
    h = torch.randn(4, 512, 7, 7)                                      # a res4 output
    torch.allclose(net.avgpool(h).flatten(1), h.mean(dim=(2, 3)))      # True
    res1 to res3: grouped by domain (shade)
    res4: grouped by class (shape)

    Sketches of the pattern in the paper's Figure 4 (illustrative points). Shade marks the domain; shape marks the class.

    class ResNetMixStyle(nn.Module):
        def __init__(self, num_classes):
            super().__init__()
            self.net = torchvision.models.resnet18(num_classes=num_classes)
            self.ms = MixStyle(p=0.5, alpha=0.1)
    
        def forward(self, x):
            n = self.net
            x = n.maxpool(n.relu(n.bn1(n.conv1(x))))
            x = self.ms(n.layer1(x))      # after res1
            x = self.ms(n.layer2(x))      # after res2
            x = self.ms(n.layer3(x))      # after res3
            x = n.layer4(x)               # no MixStyle here
            return n.fc(n.avgpool(x).flatten(1))

    Early channels respond to colours, edges and textures, and their statistics describe the look of the image, so mixing them changes style. The last stage feeds global average pooling, whose output is the channel means themselves; there the statistics are the evidence the classifier uses, and mixing them is label noise. Between those two ends, the best number of early stages depends on the task: res123 on PACS, res12 on person re-identification.

    10

    The paper's results on image classification, person re-identification and reinforcement learning, read carefully.

    What the paper found

    Section 10

    Does it work?

    MixStyle after the early stages, p = 0.5, α = 0.1, partners from the same batch.

    How much does it help on domains the network never saw, and on which kinds of problem?

    ResNet-18+ MixStyle, random shuffle+ MixStyle, domain labels

    Average: 79.5% → 82.8% without domain labels, 83.7% with them. The largest gains are on art paintings and sketches.

    modelaccuracy on the training domains' validation images

    On PACS there is no trade-off: accuracy on the seen domains stays the same while accuracy on the unseen one rises.

    modelMarket→Duke mAPR1Duke→Market mAPR1

    MixStyle helps both networks in both directions. RandomErase, a standard re-identification trick, hurts.

    The task

    An agent learns a platform game on 500 levels and is tested on new, harder levels with unseen visual layouts.

    The setup

    Random-shuffle MixStyle after the first two convolutional sequences of the policy network. No domain labels.

    The result

    Better test performance than a strong baseline, a further gain on top of IBAC-SNI, and less variation between runs.

    Reported as curves in the paper's Figure 3.

    Digits-DG

    Office-Home

    Uneven gains: large on some domains (Clipart, MNIST-M), none on others (Artistic). L2A-OT, with its image generator, is slightly ahead on both.

    Across three kinds of problem MixStyle improves generalization to unseen domains, and on the seen PACS domains it costs nothing. It needs no extra network. The gains are uneven: in PACS they were largest on art paintings and sketches, in Office-Home only Clipart improved, and the generator-based L2A-OT stayed slightly ahead on Digits-DG and Office-Home. All numbers here are the paper's; none were rerun for this lesson.

    11

    How MixStyle would enter a kiln detector, how to test it fairly, and what it cannot fix. This section is a design; nothing here was trained.

    Back to kilns

    Section 11

    Back to kilns

    MixStyle mixes early-layer statistics across training images. It helps most when domains differ in style.

    How would we use it in a kiln detector, and how would we know whether it helped?

    Same boxes, same labels, same loss. MixStyle only changes what the later layers see during training.

    Pixel-level illustration on the west UP tile. Inside the detector the same operation acts on early feature maps.

    import random
    
    
    def two_region_batches(index_by_region, batch_size):
        while True:
            a, b = random.sample(list(index_by_region), 2)          # two different regions
            half = batch_size // 2
            yield (random.sample(index_by_region[a], half)          # first half: region a
                   + random.sample(index_by_region[b], half))       # second half: region b

    index_by_region maps a region name to the dataset indices of its tiles, for example {'pk': [...], 'br': [...]}. The generator never ends; the training loop stops after its planned number of steps.

    One region held out at a time, several seeds, plus the in-region score to check nothing was lost.

    Likely to help: differences in look

    • soil and crop colour
    • haze, season and lighting
    • sensor or processing differences in brightness and contrast

    Unlikely to help: differences in content

    • kiln designs the training regions lacked
    • a different ground resolution, so kilns change size
    • different labelling rules between regions

    Look at the errors on the new region first. MixStyle addresses a shift in how things look. A shift in what is there needs new data.

    For a kiln detector, MixStyle is a small change: a weightless layer after the early backbone stages, optionally a batch sampler that pairs regions, and nothing at test time. Whether it helps is an empirical question, answered by holding regions out one at a time and comparing against ordinary colour augmentation. It is aimed at shifts in look; a new kiln design or a new image resolution needs data or modelling changes that no statistics mixing can supply.

    12

    The whole method on one page, tests for your own implementation, and questions to check understanding.

    Summary and self-check

    1. Take an early feature map x of shape B × C × H × W.
    2. For every image and channel, compute μ and σ over H × W, and detach them.
    3. Give every image a partner by shuffling the batch: at random, or across two domains.
    4. Draw one λ per image from Beta(0.1, 0.1) and blend: μmix, σmix.
    5. Output σmix · (x − μ) / σ + μmix: the image's own pattern in a blended look. The label is unchanged.
    6. Only in training, on half the batches, after the early stages. Never after the last one.
    torch.manual_seed(0)
    random.seed(0)
    x = torch.randn(8, 16, 7, 7, requires_grad=True)
    ms = MixStyle(p=1.0)                                   # always mixes in training
    y = ms(x)
    assert y.shape == x.shape                              # shape unchanged
    assert not torch.allclose(y, x)                        # training: it changes x
    y.sum().backward()
    assert x.grad is not None                              # gradients reach x
    ms.eval()
    assert torch.equal(ms(x), x)                           # identity at test time
    assert sum(q.numel() for q in ms.parameters()) == 0    # nothing to learn

    Run after the Section 8 class, which imports random and torch.

    A batch holds a single image. What does MixStyle do to it?

    Its only possible partner is itself, so the mixed statistics equal its own and the output equals the input, up to rounding.

    Every image draws λ = 0. Which earlier method is this?

    AdaIN with the partner as the style image: a full swap of statistics. The paper calls it replacing; it scored 82.1% on PACS against 82.8% for mixing.

    Does MixStyle change the label or the loss?

    No. Each image keeps its own label, and the loss is the usual one. Only the features the later layers see are changed.

    Why not put MixStyle after the last residual stage?

    Global average pooling passes the channel means straight to the classifier. Mixing them mixes two images' class evidence while keeping one label. On PACS, adding res4 dropped accuracy below the plain network.

    Your batches hold 32 Punjab tiles followed by 32 Bihar tiles. Which mix mode needs that batch layout, and what does it guarantee?

    Cross-domain mode. It swaps the two halves and shuffles inside each, so every Punjab tile gets a Bihar partner and every Bihar tile a Punjab partner.

    What does MixStyle cost when the detector is deployed?

    Nothing. In evaluation mode it returns its input, so the deployed network is the plain one.

    A channel is all zeros after ReLU. What goes wrong without the ε?

    Its σ is 0, so (x − μ) / σ divides zero by zero and gives NaN. The ε under the square root keeps σ positive.

    The idea in two sentences. The observation: the mean and standard deviation of each early feature channel carry an image's style, and they separate domains. The move: during training, blend those statistics between images from different domains, so later layers see looks that no source domain had, while each image keeps its content and its label. The layer has no parameters, costs nothing at test time, and belongs after the early stages only.